Profiler

Struct Profiler 

Source
pub struct Profiler { /* private fields */ }
Expand description

Rust profiler based on async-profiler.

Spawning a profiler can be done either in an attached (controllable) mode, which allows for stopping the profiler (and, in fact, stops it when the relevant handle is dropped), or in detached mode, in which the profiler keeps running forever. Applications that can shut down the profiler at run-time, for example applications that support reconfiguration of a running profiler, generally want to use controllable mode. Other applications (most of them) should use detached mode.

In addition, the profiler can either be spawned into the current Tokio runtime, or into a new one. Normally, applications should spawn the profiler into their own Tokio runtime, but applications that don’t have a default Tokio runtime should spawn it into a different one

This leaves 4 functions:

  1. Self::spawn - detached, same runtime
  2. Self::spawn_thread_to_runtime - detached, different runtime
  3. Self::spawn_controllable - controllable, same runtime
  4. Self::spawn_controllable_thread_to_runtime - controllable, different runtime

In addition, there’s a helper function that just spawns the profiler to a new runtime in a new thread, for applications that don’t have a Tokio runtime and don’t need complex control:

  1. Self::spawn_thread - detached, new runtime in a new thread

Implementations§

Source§

impl Profiler

Source

pub fn spawn(self) -> Result<JoinHandle<()>, SpawnError>

Start profiling. The profiler will run in a tokio task at the configured interval.

This is the same as calling Profiler::spawn_controllable followed by RunningProfiler::detach, except it returns a JoinHandle.

The returned JoinHandle can be used to detect if the profiler has exited due to a fatal error.

This function will fail if it is unable to start async-profiler, for example if it can’t find or load libasyncProfiler.so.

§Tokio Runtime

This function must be run within a Tokio runtime, otherwise it will panic. If your application does not have a main Tokio runtime, see Profiler::spawn_thread.

§Example

This example uses ProfilerBuilder::with_local_reporter which reports the profiles to a directory. It works with any other Reporter using ProfilerBuilder::with_reporter.

let profiler = ProfilerBuilder::default()
   .with_local_reporter("/tmp/profiles")
   .build();
profiler.spawn()?;
Source

pub fn spawn_thread_to_runtime( self, runtime: Runtime, spawn_fn: impl FnOnce(Box<dyn FnOnce() + Send>) -> JoinHandle<()>, ) -> Result<(), SpawnError>

Like Self::spawn, but instead of spawning within the current Tokio runtime, spawns within a set Tokio runtime and then runs a thread that calls block_on on that runtime.

If your configuration is standard, use Profiler::spawn_thread.

If you want to be able to stop the resulting profiler, use Profiler::spawn_controllable_thread_to_runtime.

spawn_fn should be std::thread::spawn, or some function that behaves like it (to allow for configuring thread properties, for example thread names).

This is to be used when your program does not have a “main” Tokio runtime already set up.

§Example

This example uses ProfilerBuilder::with_local_reporter which reports the profiles to a directory. It works with any other Reporter using ProfilerBuilder::with_reporter.

let rt = tokio::runtime::Builder::new_current_thread()
    .enable_all()
    .build()?;
let profiler = ProfilerBuilder::default()
   .with_local_reporter("/tmp/profiles")
   .build();

profiler.spawn_thread_to_runtime(
    rt,
    |t| {
        std::thread::Builder::new()
            .name("asprof-agent".to_owned())
            .spawn(t)
            .expect("thread name contains nuls")
    }
)?;
Source

pub fn spawn_thread(self) -> Result<(), SpawnThreadError>

Like Self::spawn, but instead of spawning within the current Tokio runtime, spawns within a new Tokio runtime and then runs a thread that calls block_on on that runtime, setting up the runtime by itself.

If your configuration is less standard, use Profiler::spawn_thread_to_runtime. Calling Profiler::spawn_thread is equivalent to calling Profiler::spawn_thread_to_runtime with the following:

  1. a current thread runtime with background worker threads (these exist for blocking IO) named “asprof-worker”
  2. a controller thread (the “main” thread of the runtime) named “asprof-agent”

If you want to be able to stop the resulting profiler, use Profiler::spawn_controllable_thread_to_runtime.

This is to be used when your program does not have a “main” Tokio runtime already set up.

§Example

This example uses ProfilerBuilder::with_local_reporter which reports the profiles to a directory. It works with any other Reporter using ProfilerBuilder::with_reporter.

let profiler = ProfilerBuilder::default()
   .with_local_reporter("/tmp/profiles")
   .build();

profiler.spawn_thread()?;
Source

pub fn spawn_controllable(self) -> Result<RunningProfiler, SpawnError>

Like Self::spawn, but returns a RunningProfiler that allows for controlling (currently only stopping) the profiler.

This allows for changing the configuration of the profiler at runtime, by stopping it and then starting a new Profiler with a new configuration. It also allows for stopping profiling in case the profiler is suspected to cause operational issues.

Dropping the returned RunningProfiler will cause the profiler to quit, so if your application doen’t need to change the profiler’s configuration at runtime, it will be easier to use Profiler::spawn.

This function will fail if it is unable to start async-profiler, for example if it can’t find or load libasyncProfiler.so.

§Tokio Runtime

This function must be run within a Tokio runtime, otherwise it will panic. If your application does not have a main Tokio runtime, see Profiler::spawn_controllable_thread_to_runtime.

§Example

This example uses ProfilerBuilder::with_local_reporter which reports the profiles to a directory. It works with any other Reporter using ProfilerBuilder::with_reporter.

let profiler = ProfilerBuilder::default()
   .with_local_reporter("/tmp/profiles")
   .build();

let profiler = profiler.spawn_controllable()?;

// [insert your signaling/monitoring mechanism to have a request to disable
// profiling in case of a problem]
let got_request_to_disable_profiling = async move {
    // ...
};
// spawn a task that will disable profiling if requested
tokio::task::spawn(async move {
    if got_request_to_disable_profiling.await {
        profiler.stop().await;
    }
});
Source

pub fn spawn_controllable_thread_to_runtime( self, runtime: Runtime, spawn_fn: impl FnOnce(Box<dyn FnOnce() + Send>) -> JoinHandle<()>, ) -> Result<RunningProfilerThread, SpawnError>

Like Self::spawn_controllable, but instead of spawning within the current Tokio runtime, spawns within a set Tokio runtime and then runs a thread that calls block_on on that runtime.

spawn_fn should be std::thread::spawn, or some function that behaves like it (to allow for configuring thread properties, for example thread names).

This is to be used when your program does not have a “main” Tokio runtime already set up.

§Example

This example uses ProfilerBuilder::with_local_reporter which reports the profiles to a directory. It works with any other Reporter using ProfilerBuilder::with_reporter.

let rt = tokio::runtime::Builder::new_current_thread()
    .enable_all()
    .build()?;
let profiler = ProfilerBuilder::default()
   .with_local_reporter("/tmp/profiles")
   .build();

let profiler = profiler.spawn_controllable_thread_to_runtime(
    rt,
    |t| {
        std::thread::Builder::new()
            .name("asprof-agent".to_owned())
            .spawn(t)
            .expect("thread name contains nuls")
    }
)?;

// spawn a task that will disable profiling if requested
std::thread::spawn(move || {
    if got_request_to_disable_profiling() {
        profiler.stop();
    }
});

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<Unshared, Shared> IntoShared<Shared> for Unshared
where Shared: FromUnshared<Unshared>,

Source§

fn into_shared(self) -> Shared

Creates a shared type from an unshared type.
Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more