pprof-rs

repository·master·Indexed 23 days ago

https://github.com/tikv/pprof-rs

A CPU profiler for Rust programs (version 0.15.0) that enables the generation of stack traces, flamegraphs, and protobuf-compatible profiles. It features a ProfilerGuard for RAII-based profiling, support for Google pprof protobuf format, and integration with Criterion benchmarks. The library supports custom frame post-processing, C++ demangling, and frame pointer unwinding for robust profiling on Rust 1.74 or higher.

Tokens
3.3K
Snippets
11
Records
22
Agent score
81%

What's inside pprof-rs

  1. Customize reports with a Frame Post Processor

    master

    The frame_post_processor allows you to modify raw statistic data before the report is generated. This is useful for grouping symbols, renaming threads (e.g., using Regex to simplify thread names), or demangling specific symbols. You pass a closure that accepts and modifies &mut pprof::Frames to the guard.frames_post_processor() method.

    fn frames_post_processor() -> impl Fn(&mut pprof::Frames) {
        let thread_rename = [
            (Regex::new(r"^grpc-server-\d*$").unwrap(), "grpc-server"),
            // ... other mappings
        ];
    
        move |frames| {
            for (regex, name) in thread_rename.iter() {
                if regex.is_match(&frames.thread_name) {
                    frames.thread_name = name.to_string();
                }
            }
        }
    }
    
    if let Ok(report) = guard.frames_post_processor(frames_post_processor()).report().build() {
        let file = File::create("flamegraph.svg").unwrap();
        report.flamegraph(file).unwrap();
    }
  2. Start and stop profiling with ProfilerGuard

    master

    To profile a Rust program, use pprof::ProfilerGuardBuilder to create a guard. Profiling begins immediately upon creation and continues until the guard is dropped. You can configure the sampling frequency and blocklist specific libraries (like libc or pthread) to reduce noise in your reports.

    let guard = pprof::ProfilerGuardBuilder::default()
        .frequency(1000)
        .blocklist(&["libc", "libgcc", "pthread", "vdso"])
        .build()
        .unwrap();
    // Profiling happens here...
    // When `guard` is dropped, profiling stops.
  3. Enable frame pointer unwinding for more robust profiling

    master

    pprof-rs supports unwinding via frame pointers, which avoids the need for libunwind. However, the standard library shipped with the Rust compiler does not always have correct frame pointers in every function. To use this method effectively, you must build the standard library from source using the nightly toolchain with the following command:

    cargo +nightly -Z build-std

    Warning: Since stack boundaries cannot be verified inside the signal handler, an incorrect frame pointer value may cause the program to panic.

  4. Integrate pprof-rs with Criterion benchmarks

    master

    If the criterion feature is enabled, you can use PProfProfiler as a custom profiler within a criterion benchmark group. This automatically generates flamegraphs or protobuf files in the target/criterion/<bench_name>/profile/ directory.

    use pprof::criterion::{PProfProfiler, Output};
    
    criterion_group!{
        name = benches;
        config = Criterion::default().with_profiler(PProfProfiler::new(100, Output::Flamegraph(None)));
        targets = bench
    }
    criterion_main!(benches);

    To run the example provided in the repo:

    cargo run --example criterion --release --features="flamegraph criterion" -- --bench --profile-time 5
  5. Generate Flamegraphs

    master

    If the flamegraph feature is enabled, you can generate SVG flamegraphs from a Report. You can use the default flamegraph method or flamegraph_with_options to customize settings like image_width.

    // Default flamegraph
    if let Ok(report) = guard.report().build() {
        let file = File::create("flamegraph.svg").unwrap();
        report.flamegraph(file).unwrap();
    };
    
    // Custom flamegraph options
    if let Ok(report) = guard.report().build() {
        let file = File::create("flamegraph.svg").unwrap();
        let mut options = pprof::flamegraph::Options::default();
        options.image_width = Some(2500);
        report.flamegraph_with_options(file, &mut options).unwrap();
    };
  6. Export to Google pprof protobuf format

    master

    By enabling the protobuf or protobuf-codec features, you can export profiles in the profile.proto format compatible with the Google pprof tool. This allows you to use the pprof CLI to visualize results (e.g., via SVG).

    match guard.report().build() {
        Ok(report) => {
            let mut file = File::create("profile.pb").unwrap();
            let profile = report.pprof().unwrap();
    
            let mut content = Vec::new;
            profile.encode(&mut content).unwrap();
            file.write_all(&content).unwrap();
    
            println!("report: {}", &report);
        }
        Err(_) => {}
    };

    Then use the CLI:

    ~/go/bin/pprof -svg profile.pb
  7. Understand the Report and UnresolvedReport structures

    master

    The library provides two primary structures for representing profiling results:

    1. Report: A symbolized representation. The data field is a HashMap<Frames, isize>, where the key is a backtrace of Frames and the value is the occurrence count.
    2. UnresolvedReport: An unsymbolicated representation. The data field is a HashMap<UnresolvedFrames, isize>, used when symbol information is unavailable.

    Both structures include a timing field of type ReportTiming which contains the collection frequency, start time, and duration of the profiling session.

  8. Start a profiling session with ProfilerGuardBuilder

    master

    To start a CPU profiling session, use ProfilerGuardBuilder to configure and build a ProfilerGuard. The ProfilerGuard uses the RAII pattern: profiling starts when it is created and automatically stops when the guard is dropped.

    By default, the sampling frequency is 99 Hz. You can customize the frequency, use an alternate signal stack (useful in environments with small stacks like Go), or blocklist specific shared libraries to avoid profiling them.

  9. Avoid deadlocks and signal safety issues with ProfilerGuardBuilder blocklist

    master

    Because pprof-rs uses backtrace-rs (which relies on libunwind from libgcc), it is susceptible to deadlocks if a profiling tick occurs while the program is propagating an exception. To resolve this, you should use a ProfilerGuardBuilder with a blocklist containing system libraries like libc, libgcc, pthread, and vdso. Adding vdso is specifically recommended for certain distributions (e.g., Ubuntu 18.04) where DWARF information in vdso may be incorrect.

    let guard = pprof::ProfilerGuardBuilder::default().frequency(1000).blocklist(&["libc", "libgcc", "pthread", "vdso"]).build().unwrap();
  10. Generate a stack counter report

    master

    Once you have a ProfilerGuard, you can generate a Report. Implementing Debug for Report allows you to print a human-readable stack counter report directly to the console.

    if let Ok(report) = guard.report().build() {
        println!("report: {:?}", &report);
    };
  11. Available pprof-rs features

    master

    The following features can be enabled in your Cargo.toml to extend pprof-rs functionality:

    • cpp: Enables C++ demangling.
    • flamegraph: Enables generating flamegraph reports.
    • prost-codec: Enables pprof protobuf format via the prost crate.
    • protobuf-codec: Enables pprof protobuf format via the protobuf crate.
    • frame-pointer: Uses frame pointers for backtraces (requires nightly Rust).