Coder Social home page Coder Social logo

pprof-rs's Introduction

pprof

pprof is a cpu profiler that can be easily integrated into a rust program.

Actions Status Crates.io Dependency Status FOSSA Status

Usage

First, get a guard to start profiling. Profiling will continue until this guard was dropped.

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

During the profiling time, you can get a report with the guard.

if let Ok(report) = guard.report().build() {
    println!("report: {:?}", &report);
};

Debug was implemented for Report. It will print a human-readable stack counter report. Here is an example:

FRAME: backtrace::backtrace::trace::h3e91a3123a3049a5 -> FRAME: pprof::profiler::perf_signal_handler::h7b995c4ab2e66493 -> FRAME: Unknown -> FRAME: prime_number::is_prime_number::h70653a2633b88023 -> FRAME: prime_number::main::h47f1058543990c8b -> FRAME: std::rt::lang_start::{{closure}}::h4262e250f8024b06 -> FRAME: std::rt::lang_start_internal::{{closure}}::h812f70926ebbddd0 -> std::panicking::try::do_call::h3210e2ce6a68897b -> FRAME: __rust_maybe_catch_panic -> FRAME: std::panicking::try::h28c2e2ec1c3871ce -> std::panic::catch_unwind::h05e542185e35aabf -> std::rt::lang_start_internal::hd7efcfd33686f472 -> FRAME: main -> FRAME: __libc_start_main -> FRAME: _start -> FRAME: Unknown -> THREAD: prime_number 1217
FRAME: backtrace::backtrace::trace::h3e91a3123a3049a5 -> FRAME: pprof::profiler::perf_signal_handler::h7b995c4ab2e66493 -> FRAME: Unknown -> FRAME: alloc::alloc::box_free::h82cea48ed688e081 -> FRAME: prime_number::main::h47f1058543990c8b -> FRAME: std::rt::lang_start::{{closure}}::h4262e250f8024b06 -> FRAME: std::rt::lang_start_internal::{{closure}}::h812f70926ebbddd0 -> std::panicking::try::do_call::h3210e2ce6a68897b -> FRAME: __rust_maybe_catch_panic -> FRAME: std::panicking::try::h28c2e2ec1c3871ce -> std::panic::catch_unwind::h05e542185e35aabf -> std::rt::lang_start_internal::hd7efcfd33686f472 -> FRAME: main -> FRAME: __libc_start_main -> FRAME: _start -> FRAME: Unknown -> THREAD: prime_number 1
FRAME: backtrace::backtrace::trace::h3e91a3123a3049a5 -> FRAME: pprof::profiler::perf_signal_handler::h7b995c4ab2e66493 -> FRAME: Unknown -> FRAME: prime_number::main::h47f1058543990c8b -> FRAME: std::rt::lang_start::{{closure}}::h4262e250f8024b06 -> FRAME: std::rt::lang_start_internal::{{closure}}::h812f70926ebbddd0 -> std::panicking::try::do_call::h3210e2ce6a68897b -> FRAME: __rust_maybe_catch_panic -> FRAME: std::panicking::try::h28c2e2ec1c3871ce -> std::panic::catch_unwind::h05e542185e35aabf -> std::rt::lang_start_internal::hd7efcfd33686f472 -> FRAME: main -> FRAME: __libc_start_main -> FRAME: _start -> FRAME: Unknown -> THREAD: prime_number 1

Features

  • cpp enables the cpp demangle.
  • flamegraph enables the flamegraph report format.
  • prost-codec enables the pprof protobuf report format through prost.
  • protobuf-codec enables the pprof protobuf report format through protobuf crate.
  • frame-pointer gets the backtrace through frame pointer. only available for nightly

Flamegraph

pprof = { version = "0.13", features = ["flamegraph"] }

If flamegraph feature is enabled, you can generate flamegraph from the report. Report struct has a method flamegraph which can generate flamegraph and write it into a Write.

if let Ok(report) = guard.report().build() {
    let file = File::create("flamegraph.svg").unwrap();
    report.flamegraph(file).unwrap();
};

Additionally, custom flamegraph options can be specified.

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();
};

Here is an example of generated flamegraph:

flamegraph

Frame Post Processor

Before the report was generated, frame_post_processor was provided as an interface to modify raw statistic data. If you want to group several symbols/thread or demangle for some symbols, this feature will benefit you.

For example:

fn frames_post_processor() -> impl Fn(&mut pprof::Frames) {
    let thread_rename = [
        (Regex::new(r"^grpc-server-\d*$").unwrap(), "grpc-server"),
        (Regex::new(r"^cop-high\d*$").unwrap(), "cop-high"),
        (Regex::new(r"^cop-normal\d*$").unwrap(), "cop-normal"),
        (Regex::new(r"^cop-low\d*$").unwrap(), "cop-low"),
        (Regex::new(r"^raftstore-\d*$").unwrap(), "raftstore"),
        (Regex::new(r"^raftstore-\d*-\d*$").unwrap(), "raftstore"),
        (Regex::new(r"^sst-importer\d*$").unwrap(), "sst-importer"),
        (
            Regex::new(r"^store-read-low\d*$").unwrap(),
            "store-read-low",
        ),
        (Regex::new(r"^rocksdb:bg\d*$").unwrap(), "rocksdb:bg"),
        (Regex::new(r"^rocksdb:low\d*$").unwrap(), "rocksdb:low"),
        (Regex::new(r"^rocksdb:high\d*$").unwrap(), "rocksdb:high"),
        (Regex::new(r"^snap sender\d*$").unwrap(), "snap-sender"),
        (Regex::new(r"^snap-sender\d*$").unwrap(), "snap-sender"),
        (Regex::new(r"^apply-\d*$").unwrap(), "apply"),
        (Regex::new(r"^future-poller-\d*$").unwrap(), "future-poller"),
    ];

    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();
}

Use with pprof

With protobuf feature enabled, pprof-rs can also output profile.proto format.

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 you can use pprof command with profile.pb. For example:

~/go/bin/pprof -svg profile.pb

Then pprof will generate a svg file according to the profile.

tree

Integrate with criterion

With criterion feature enabled, a criterion custom profiler is provided in pprof-rs.

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);

After running the benchmark, you can find the flamegraph at target/criterion/<name-of-benchmark>/profile/flamegraph.svg. protobuf output is also available with the Output::Protobuf option; these end up at target/criterion/<name-of-benchmark>/profile.pb.

For more details, you can check the examples/criterion.rs, and the profiling document of criterion. For a quick start, you can run this example with cargo run --example criterion --release --features="flamegraph criterion" -- --bench --profile-time 5

Why not ...

There have been tons of profilers, why we create a new one? Here we make a comparison between pprof-rs and other popular profilers to help you choose the best fit one.

gperftools

gperftools is also an integrated profiler. There is also a wrapper for gperftools in rust called cpuprofiler which makes it programmable for a rust program.

Pros

  1. pprof-rs has a modern build system and can be integrated into a rust program easily while compiling gperftools statically is buggy.
  2. pprof-rs has a native rust interface while gperftools's wrapper is just a wrapper.
  3. Programming with rust guarantees thread safety natively.

Cons

  1. gperftools is a collection of performance analysis tools which contains cpu profiler, heap profiler... pprof-rs focuses on cpu profiler now.

perf

perf is a performance analyzing tool in Linux.

Pros

  1. You don't need to start another process to perf with pprof-rs.
  2. pprof-rs can be easily integrated with rust program which means you don't need to install any other programs.
  3. pprof-rs has a modern programmable interface to hack with
  4. pprof-rs theoretically supports all POSIX systems and can easily support more systems in the future.

Cons

  1. perf is much more feature-rich than pprof-rs.
  2. perf is highly integrated with Linux.

Implementation

When profiling was started, setitimer system call was used to set up a timer which will send a SIGPROF to this program every constant interval.

When receiving a SIGPROF signal, the signal handler will capture a backtrace and increase the count of it. After a while, the profiler can get every possible backtrace and their count. Finally, we can generate a report with profiler data.

However, the real world is full of thorns. There are many worths of note parts in the implementation.

Backtrace

Unfortunately, there is no 100% robust stack tracing method. Some related researches have been done by gperftools. pprof-rs uses backtrace-rs which finally uses libunwind provided by libgcc

WARN: as described in former gperftools documents, libunwind provided by libgcc is not signal safe.

libgcc's unwind method is not safe to use from signal handlers. One particular cause of deadlock is when profiling tick happens when program is propagating thrown exception.

This can be resolved by adding a blocklist:

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

The vdso should also be added to the blocklist, because in some distribution (e.g. ubuntu 18.04), the dwarf information in vdso is incorrect.

Frame Pointer

The pprof-rs also supports unwinding through frame pointer, without the need to use libunwind. However, the standard library shipped with the rust compiler does not have the correct frame pointer in every function, so you need to use cargo +nightly -Z build-std to build the standard library from source.

As we cannot get the stack boundaries inside the signal handler, it's also not possible to ensure the safety. If the frame pointer was set to a wrong value, the program will panic.

Signal Safety

Signal safety is hard to guarantee. But it's not that hard.

First, we have to avoid deadlock. When profiler samples or reports, it will get a global lock on the profiler. Particularly, deadlock happenswhen the running program is getting a report from the profiler (which will hold the lock), at the same time, a SIGPROF signal is triggered and the profiler wants to sample (which will also hold the lock). So we don't wait for the lock in signal handler, instead we try_lock in the signal handler. If the global lock cannot be gotten, the profiler will give up directly.

Then, signal safety POSIX function is quite limited as listed here. The most bothering issue is that we cannot use malloc in signal handler. So we can only use pre-allocated memory in profiler. The simplest way is write every sample serially into a file. We optimized it with a fix-sized hashmap that has a fixed number of buckets and every bucket is an array with a fixed number of items. If the hashmap is full, we pop out the item with minimum count and write it into a temporary file.

Unit tests have been added to guarantee there is no malloc in sample functions.

futex is also not safe to use in signal handler. So we use a spin lock to avoid usage of futex.

TODO

  1. Restore the original SIGPROF handler after stopping the profiler.

Minimum Supported Rust Version

Rust 1.64 or higher.

Minimum supported Rust version can be changed in the future, but it will be done with a minor version bump.

License

FOSSA Status

pprof-rs's People

Contributors

alamb avatar bnjjj avatar busyjay avatar dependabot[bot] avatar flxo avatar fossabot avatar free avatar funbringer avatar gen-xu avatar hexilee avatar hoverbear avatar kadiwa4 avatar kennytm avatar koushiro avatar mornyx avatar noahols avatar omarabid avatar pseitz avatar rrbutani avatar rtzoeller avatar rustin170506 avatar sticnarf avatar umanwizard avatar victoryang00 avatar viglia avatar xhebox avatar xiangzhai avatar xuanwo avatar yangkeao avatar zhaixiaojuan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pprof-rs's Issues

Panic on aarch64

tikv/tikv#10658

TiKV on HUAWEI,Kunpeng 920 failed to profile and got an panic.

#0  0x0000fffd7b6aceb4 in ?? () from /lib64/libgcc_s.so.1
#1  0x0000fffd7b6ae534 in _Unwind_Backtrace () from /lib64/libgcc_s.so.1
#2  0x0000aaac01eedb58 in backtrace::backtrace::libunwind::trace (cb=...) at /root/.cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.37/src/backtrace/libunwind.rs:88
#3  backtrace::backtrace::trace_unsynchronized (cb=...) at /root/.cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.37/src/backtrace/mod.rs:66
#4  pprof::profiler::perf_signal_handler (_signal=<optimized out>) at /root/.cargo/registry/src/github.com-1ecc6299db9ec823/pprof-0.4.2/src/profiler.rs:128
#5  <signal handler called>

Try to use other lock

gperftools use spinlock in profiler. Check whether spinlock will resolve "deadlock" problem which occurred when too high frequency was adapted.

undefined reference to `__malloc_hook'

run cargo test --all-features on a platform with glibc 2.34 (such as Fedora 35)

some error message

  = note: /usr/bin/ld: $HOME/pprof-rs/target/debug/deps/pprof-b3a0e5c36a90b5b6.3fe154bcu86215js.rcgu.o: in function `pprof::collector::malloc_free_test::malloc_hook':
          $HOME/pprof-rs/src/collector.rs:385: undefined reference to `__malloc_hook'
          /usr/bin/ld: $HOME/pprof-rs/src/collector.rs:394: undefined reference to `__malloc_hook'
          /usr/bin/ld: $HOME/pprof-rs/target/debug/deps/pprof-b3a0e5c36a90b5b6.3fe154bcu86215js.rcgu.o: in function `pprof::collector::malloc_free_test::malloc_free':
          $HOME/pprof-rs/src/collector.rs:406: undefined reference to `__malloc_hook'
          /usr/bin/ld: $HOME/pprof-rs/src/collector.rs:415: undefined reference to `__malloc_hook'
          /usr/bin/ld: $HOME/pprof-rs/target/debug/deps/pprof-b3a0e5c36a90b5b6.51fjuio59woik1f1.rcgu.o: in function `pprof::profiler::tests::malloc_hook':
          $HOME/pprof-rs/src/profiler.rs:377: undefined reference to `__malloc_hook'
          /usr/bin/ld: $HOME/pprof-rs/target/debug/deps/pprof-b3a0e5c36a90b5b6.51fjuio59woik1f1.rcgu.o:$HOME/pprof-rs/src/profiler.rs:386: more undefined references to `__malloc_hook' follow
          collect2: error: ld returned 1 exit status

tips

* The deprecated memory allocation hooks __malloc_hook, __realloc_hook,
  __memalign_hook and __free_hook are now removed from the API.  Compatibility
  symbols are present to support legacy programs but new applications can no
  longer link to these symbols.  These hooks no longer have any effect on glibc
  functionality.  The malloc debugging DSO libc_malloc_debug.so currently
  supports hooks and can be preloaded to get this functionality back for older
  programs.  However this is a transitional measure and may be removed in a
  future release of the GNU C Library.  Users may port away from these hooks by
  writing and preloading their own malloc interposition library.

Test under musl failed for pthread_getname_np not found

It's pretty wired. I run nm libc.a |grep pthread|grep getname in the alpine, and got:

0000000000000000 T pthread_getname_np

But I came across the same issue you faced. I will approve and merge this PR to make it work under musl (and I'll investigate further why it cannot link with this symbol)

Originally posted by @YangKeao in #140 (comment)

Document the MSRV policy

Currently, this repo has no documented MSRV policy, which can lead to miscommunications about how downstream users can depend on it, and how it is altered by the crate maintainers.

The motivating example is #91, which migrated pprof to Rust 2021 edition. This removed support for Rust versions prior to 1.56 (which is the first version supporting edition = "2021"). However, it was released as a point release (0.6.2), which meant that any downstream users with pprof = "0.6" in their Cargo.toml would automatically update. If those users were enforcing their own MSRV prior to 1.56 (as in my case, I am), it resulted in unprompted inconsistent compilation breakage (depending on when the user last updated their Cargo.lock).

I note that the GitHub Actions workflow for pprof tests it against the current stable Rust version. If "only supports latest stable" is the intended MSRV policy, it would be helpful to state this in the README, so that users of pprof know that they need to handle it separately from their own MSRVs.

Alternatively, I would recommend an MSRV policy of a similar style used in the RustCrypto ecosystem, which makes it easier for downstream users to prevent automatic upgrades breaking their workflows:

Minimum Supported Rust Version

Rust 1.56 or higher.

Minimum supported Rust version can be changed in the future, but it will be
done with a minor version bump.

RUSTSEC-2019-0031: spin is no longer actively maintained

spin is no longer actively maintained

Details
Status unmaintained
Package spin
Version 0.5.2
URL mvdnes/spin-rs@7516c80
Date 2019-11-21

The author of the spin crate does not have time or interest to maintain it.

Consider the following alternatives (both of which support no_std):

See advisory page for additional details.

I came across this issue in gitui: extrawurst/gitui#172

Run build.rs failed when compile

The build.rs of pprof-rs ran failed since v0.8.x

Caused by:
  process didn't exit successfully: `/home/gao/dev/axon/target/debug/build/prost-build-656a0dbcb9c07db5/build-script-build` (exit status: 101)
  --- stdout
  cargo:rerun-if-changed=/home/gao/.cargo/registry/src/github.com-1ecc6299db9ec823/prost-build-0.10.4/third-party/protobuf/cmake
  CMAKE_TOOLCHAIN_FILE_x86_64-unknown-linux-gnu = None
  CMAKE_TOOLCHAIN_FILE_x86_64_unknown_linux_gnu = None
  HOST_CMAKE_TOOLCHAIN_FILE = None
  CMAKE_TOOLCHAIN_FILE = None
  CMAKE_GENERATOR_x86_64-unknown-linux-gnu = None
  CMAKE_GENERATOR_x86_64_unknown_linux_gnu = None
  HOST_CMAKE_GENERATOR = None
  CMAKE_GENERATOR = None
  CMAKE_PREFIX_PATH_x86_64-unknown-linux-gnu = None
  CMAKE_PREFIX_PATH_x86_64_unknown_linux_gnu = None
  HOST_CMAKE_PREFIX_PATH = None
  CMAKE_PREFIX_PATH = None
  CMAKE_x86_64-unknown-linux-gnu = None
  CMAKE_x86_64_unknown_linux_gnu = None
  HOST_CMAKE = None
  CMAKE = None
  running: "cmake" "/home/gao/.cargo/registry/src/github.com-1ecc6299db9ec823/prost-build-0.10.4/third-party/protobuf/cmake" "-Dprotobuf_BUILD_TESTS=OFF" "-DCMAKE_INSTALL_PREFIX=/home/gao/dev/axon/target/debug/build/prost-build-d4932c38416f1edb/out" "-DCMAKE_C_FLAGS= -ffunction-sections -fdata-sections -fPIC -m64" "-DCMAKE_C_COMPILER=/usr/bin/cc" "-DCMAKE_CXX_FLAGS= -ffunction-sections -fdata-sections -fPIC -m64" "-DCMAKE_CXX_COMPILER=/usr/bin/c++" "-DCMAKE_ASM_FLAGS= -ffunction-sections -fdata-sections -fPIC -m64" "-DCMAKE_ASM_COMPILER=/usr/bin/cc" "-DCMAKE_BUILD_TYPE=Debug"
  -- 
  -- 3.19.4.0
  -- Configuring done
  -- Generating done
  -- Build files have been written to: /home/gao/dev/axon/target/debug/build/prost-build-d4932c38416f1edb/out/build
  running: "cmake" "--build" "." "--target" "install" "--config" "Debug" "--parallel" "24"

  --- stderr
  Unknown argument --parallel
  Unknown argument 24
  Usage: cmake --build <dir> [options] [-- [native-options]]
  Options:
    <dir>          = Project binary directory to be built.
    --target <tgt> = Build <tgt> instead of default targets.
                     May only be specified once.
    --config <cfg> = For multi-configuration tools, choose <cfg>.
    --clean-first  = Build target 'clean' first, then build.
                     (To clean only, use --target 'clean'.)
    --use-stderr   = Ignored.  Behavior is default in CMake >= 3.0.
    --             = Pass remaining options to the native tool.
  thread 'main' panicked at '
  command did not execute successfully, got: exit status: 1

  build script failed, must exit now', /home/gao/.cargo/registry/src/github.com-1ecc6299db9ec823/cmake-0.1.48/src/lib.rs:975:5
  note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Error: Could not find protos in pprof

Env

$ cargo --version
cargo 1.58.0 (f01b232bc 2022-01-19)

I create a new project with cargo new test command. And replace the test/src/main.rs with examples/profile_proto.rs. Then add the pprof = "0.6.2" to Cargo.toml like this:

[dependencies]
pprof = "0.6.2"

But I got the result after run the test project:

$ cargo run
   Compiling learn v0.1.0 (/home/echo/workspace/rust/test)
error[E0432]: unresolved import `pprof::protos`
 --> src/main.rs:4:12
  |
4 | use pprof::protos::Message;
  |            ^^^^^^ could not find `protos` in `pprof`

error[E0599]: no method named `pprof` found for struct `Report` in the current scope
   --> src/main.rs:108:34
    |
108 |             let profile = report.pprof().unwrap();
    |                                  ^^^^^ method not found in `Report`

Some errors have detailed explanations: E0432, E0599.
For more information about an error, try `rustc --explain E0432`.
error: could not compile `learn` due to 2 previous errors

What should I do to solve this problem?

is there any plan to support heap alloc analysis?

thanks to pprof-rs i could analyze my programโ€™s performance easily!
is there any plan to support heap alloc analysis officially in proto format? or is there any guidance to implement that by my self?

Documentation: Supported platforms

Hi there!

The initiative seems amazing!

I wanted to use it on Windows but did not manage. I read through issue #9 and wanted to ask if you could mention supported platforms in the README somewhere. I know that profiling in Windows is usually much harder than in Linux, so as long as this is not sorted out, I was expecting this notice.

Best!

Signal handler is unsafe

In perf_signal_handler, backtrace::trace_unsychronized is called. This will not produce any bugs if the user is just using pprof-rs, since a lock is taken, so the main body of perf_signal_handler cannot be executed more than once at a time.

However, if the user is calling backtrace::trace from any other part of the code at the same time, this will result in UB.

I suspect (but I'm not sure) that this is why we are seeing deadlocks in https://github.com/MaterializeInc/materialize when using both jemalloc heap profiling and pprof-rs profiling at the same time.

examples profile_proto.rs Can't work

error[E0599]: no method named pprof found for struct pprof::Report in the current scope
--> examples/profile_proto.rs:103:30
|
103 | let profile = report.pprof().unwrap();
| ^^^^^ method not found in pprof::Report

error[E0599]: no method named pprof found for struct pprof::Report in the current scope
--> examples/profile_proto.rs:104:30
|
104 | let profile = report.pprof().unwrap();
| ^^^^^ method not found in pprof::Report

For more information about this error, try rustc --explain E0599.
error: could not compile pprof due to 2 previous errors

  • The terminal process "cargo 'run', '--package', 'pprof', '--example', 'profile_proto', '--features', 'protobuf'" terminated with exit code: 101.
  • Terminal will be reused by tasks, press any key to close it.

Make clippy happy

Clippy is not happy for a lot of reasons:

error: this import is redundant
 --> examples/prime_number.rs:3:1
  |
3 | use pprof;
  | ^^^^^^^^^^ help: remove it entirely
  |
  = note: `-D clippy::single-component-path-imports` implied by `-D warnings`
  = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#single_component_path_imports

error: this import is redundant
 --> examples/malloc_hook.rs:5:1
  |
5 | use pprof;
  | ^^^^^^^^^^ help: remove it entirely
  |
  = note: `-D clippy::single-component-path-imports` implied by `-D warnings`
  = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#single_component_path_imports

error: this import is redundant
 --> examples/flamegraph.rs:3:1
  |
3 | use pprof;
  | ^^^^^^^^^^ help: remove it entirely
  |
  = note: `-D clippy::single-component-path-imports` implied by `-D warnings`
  = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#single_component_path_imports

Let's address all of them and make clippy happy.

pprof 0.9.1: Databend cannot be built on musl targets

= note: /usr/local/bin/../lib/gcc/x86_64-linux-musl/9.2.0/../../../../x86_64-linux-musl/bin/ld: /workspace/target/x86_64-unknown-linux-musl/release/deps/databend_meta-3240ad175f7a77fc.pprof-d7659ef0c4e51494.pprof.f6a0a7fc-cgu.0.rcgu.o.rcgu.o: in function `pprof::profiler::write_thread_name':
          /opt/rust/cargo/registry/src/[github.com](http://github.com/)-1ecc6299db9ec823/pprof-0.9.1/src/[profiler.rs:193](http://profiler.rs:193/): undefined reference to `pthread_getname_np'
          collect2: error: ld returned 1 exit status

https://github.com/datafuselabs/databend/runs/7005883205?check_suite_focus=true#step:7:1134

Related:

Using `pprof` with `criterion` benchmarks that have ids with function names or parameters fails with a file system error

To reproduce:

#[macro_use]
extern crate criterion;
use criterion::{Criterion, BenchmarkId};

use pprof::criterion::{Output, PProfProfiler};

fn bench(c: &mut Criterion) {
    let mut group = c.benchmark_group("foo");

    group.bench_function("bar", |b| b.iter(|| ()));
    group.bench_with_input(
        BenchmarkId::new("baz", "quux"),
        &(),
        |b, _| b.iter(|| ()),
    );
}

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

In the above, pprof will panic here when creating the flamegraph for bar and bar because it tries to create files named foo/bar.svg and foo/bar/quux.svg for each (respectively).

Since / usually isn't allowed in file names, this fails.


The /s are coming from criterion; specifically this is how BenchmarkIds are turned into the benchmark_id that's passed to Profiler implementations here.

This doesn't affect the example in this repo because it doesn't specify a benchmark function id or a value (no benchmark group); it just calls bench_function which ultimately creates an empty BenchmarkId and goes down this codepath which doesn't introduce any slashes.

Any time a benchmark group is used (without or without the parameter being present as it is for baz in the above), PProfProfiler will hit this error.


I think the easiest fix is to just give the generated flamegraph/protobuf files a static name and ignore the benchmark_id that criterion provides. This is okay to do because the directory (benchmark_dir) given is already unique to the specific benchmark that's being run.

Another option is to escape/replace the slashes.

use function address rather than symbol for frame hash/eq

As I have tested, function address reported by backtrace-rs is not faithful. The same functions are reported with several different addresses (which differs only one or two bytes). Find the reason and try to get a faithful function address (and use them to speed up the data collector.)

support musl

Related with #27 #32

pprof-rs cannot be compiled and work well with musl ๐Ÿ˜ฟ . Find the reason and fix them ๐Ÿ˜ธ

Trace/BPT trap on Mac

This code:

use pprof::ProfilerGuard;
static mut GUARD: Option<ProfilerGuard<'static>> = None;
static mut BLACKLIST: Vec<String> = Vec::new();
pub fn start_profilingx(blacklist: &[String]) {
    let frequency = 1000;
    unsafe {
        BLACKLIST = blacklist.to_vec();
        GUARD = Some(pprof::ProfilerGuard::new(frequency).unwrap());
    }
}
pub fn profiling_blacklistx() -> Vec<String> {
    vec!["woof".to_string()]
}
fn main() {
    start_profilingx(&profiling_blacklistx());
    let mut mc = 0;
    for i in 0..10_000 {
        for j in 0..10_000 {
            let n = i * j;
            mc = (mc + n) % 999;
        }
    }
    eprintln!("mc = {}", mc);
}

yields Trace/BPT trap when I run it, at least most of the time. Other similar code does this most of the time.

It is exceptionally finicky. For example, changing vec!["woof".to_string()] to vec![] eliminates the problem.

This is using master on pprof, but also occurs in 0.6.

I believe that the problem may be restricted to particular Mac versions. I have:

rustc 1.60.0
OSX 12.4
chip = apple M1 Pro

[profile.dev]
debug = 1
opt-level = 3
split-debuginfo = "unpacked"

Consider reexport prost::Message

In the example of pprof feature, encode from prost::Message was used to serialize protobuf data. It is recommended to reexport this trait to avoid potential version conflict.

No data from the proto profile?

Thus far unable to get a nice small example. What I do have is at https://github.com/jabley/monkeyinterpreter/tree/feature/pprof

In jabley/monkeyinterpreter@fabd13c, I tried to add pprof-rs to see what it showed me. The current answer is: very little, and I'm not sure what I'm doing wrong.

I'm very new to Rust. That codebase is my first attempt to write something in Rust. I've added pprof-rs and when I run the binary, I don't get a lot of data.

> cargo run --bin vm-flamegraph --release -- -p profile.pb
        Finished release [optimized + debuginfo] target(s) in 0.38s
         Running `target/release/vm-flamegraph -p profile.pb`
    Result: 9227465
    report: THREAD: ThreadId(4437034432) 2763

That takes about 25 seconds to run on my hardware.

I was expecting to see more in the report which is dumped out by:

guard.and_then(|guard| -> Option<()> {
        if let Ok(report) = guard.report().build() {
            let mut file = File::create(matches.value_of("cpuprofile").unwrap()).unwrap();
            let profile = report.pprof().unwrap();

            let mut content = Vec::new();
            profile.encode(&mut content).unwrap();
            file.write_all(&content).unwrap();

            println!("report: {}", &report);
        };

        None
    });

I've tried with both debug and release builds and get the same output. This is on MacOS using cargo 1.44.1 (88ba85757 2020-06-11) and rustc 1.44.1 (c7087fe00 2020-06-17)

When I then try viewing the protobuf file using go tool pprof, there isn't the expected amount of data and call-stacks that I was hoping for.

What am I missing? Very happy to be pointed at how to get more information which would help you understand this.

Aside

I also occasionally get "Illegal instruction: 4". This could definitely be a bug in my code, but that string isn't in my code, and I'm not sure where it's coming from.

> cargo run --bin vm-flamegraph --release -- -p profile.pb 
    Finished release [optimized + debuginfo] target(s) in 0.24s
     Running `target/release/vm-flamegraph -p profile.pb`
Illegal instruction: 4
> echo $?
132

I'd never seen this behaviour until I added pprof-rs. Could just be I'm lucky like that!

No flamegraph generated with criterion and flamegraph feature

I tried to use pprof with criterion to generate a flamegraph and I'm not sure what I'm missing as it seems to match the example functionally but no SVG is generated. Below is the entire benchmark source but it can also be found here https://github.com/xd009642/yin/blob/pprof_test/benches/yin_benchmark.rs

Also not sure if there's some way to turn on some form of logging for pprof but it could be useful for something just to help diagnose if things like that are a user problem or a pprof problem. I didn't see any mentioned and tried a quick RUST_LOG change which didn' do anything but if anyone has any tips in that regard I'd be interested ๐Ÿ‘€

use criterion::{black_box, criterion_group, criterion_main, Criterion};
use yin::Yin;

use pprof::criterion::{Output, PProfProfiler};

pub fn norm_sine_benchmark(c: &mut Criterion) {
    let sample = {
        use dasp::{signal, Signal};
        let mut signal = signal::rate(44100.0).const_hz(100.0).sine();
        let sample: Vec<f64> = (0..44100).map(|_| signal.next()).collect();
        sample
    };
    let yin = Yin::init(0.1, 40.0, 200.0, 1000);
    c.bench_function("1000 sr, 100.0 freq", |b| {
        b.iter(|| yin.estimate_freq(black_box(&sample)))
    });
}

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

criterion_main!(benches);

Can't see full method names in flamegraph

In order to make sense of the kind of data that a profiler produces, some sort of visualization such as a flamegraph is essentially mandatory. Yet, when I generate a flamegraph from this tool, all the method names state with the module path, which makes seeing which method it is impossible, which is blocking me from accessing any useful data.

cross compiling failed

Hi there,
I tried to cross-compile rummqtt and got the following output.

roker/target/armv7-unknown-linux-gnueabihf/release/deps/libthiserror-eef2e33c746f6e56.rmeta --cap-lints allow`
error[E0308]: mismatched types
   --> /home/xx/.cargo/registry/src/github.com-1ecc6299db9ec823/pprof-0.4.1/src/profiler.rs:143:47
    |
143 |             write_thread_name(current_thread, &mut name);
    |                                               ^^^^^^^^^ expected slice `[u8]`, found array `[i8; 16]`
    |
    = note: expected mutable reference `&mut [u8]`
               found mutable reference `&mut [i8; 16]`

error[E0606]: casting `&mut [i8; 16]` as `*mut [u8]` is invalid
   --> /home/fwa/.cargo/registry/src/github.com-1ecc6299db9ec823/pprof-0.4.1/src/profiler.rs:141:28
    |
141 |             let name_ptr = &mut name as *mut [libc::c_char] as *mut libc::c_char;
    |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to 2 previous errors

Some errors have detailed explanations: E0308, E0606.
For more information about an error, try `rustc --explain E0308`.
error: could not compile `pprof`

Can you verify this?
Thanks in advance.

compact the inline functions with `/` is not correct

ๆ•่Žท

As shown in the screenshot, main/is_prime_number2 is at the same level as main, which is totally incorrect. Some users asked to get a cleaner flamegraph, but the reality and truth is the first demand. We can figure out some way to simplify the flamegraph later, (or if you have better ideas, please contact me.)

Question: Concurrency support

If I was to use this to profile a method in a server would I be able to create a profile report for each request event ones occurring concurrently? Or does it use anything like signals that could interfere with this use case?

Generated flamegraph is meaningless

Hi, I am generating a flamegraph of a production build (with debug symbols) of a Tokio-based app running on Linux. The guard is created on main tokio fn and later moved to background thread to periodically dump the flamegraphs. Unfortunately the output of the graph is rather trivial, see:
flamegraph
Interestingly, the exact same code on macOS seems to produce more reasonable output.
Is there something I'm doing wrong? Thanks so much for any help.

Cannot link with `pthread_getname_np` on musl

Even with the latest musl, using the extern symbol pthread_getname_np will fail, though nm libc.a |grep pthread|grep getname tells us it has the symbol:

0000000000000000 T pthread_getname_np

Find out why we cannot link with it. Maybe we could prepare a minimal reproducible example and report to the libc crate ๐Ÿค”

build failure on musl host

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Os { code: 2, kind: NotFound, message: "No such file or directory" }', src/libcore/result.rs:1188:5
stack backtrace:
   0: backtrace::backtrace::libunwind::trace
             at /cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.40/src/backtrace/libunwind.rs:88
   1: backtrace::backtrace::trace_unsynchronized
             at /cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.40/src/backtrace/mod.rs:66
   2: std::sys_common::backtrace::_print_fmt
             at src/libstd/sys_common/backtrace.rs:84
   3: <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt
             at src/libstd/sys_common/backtrace.rs:61
   4: core::fmt::write
             at src/libcore/fmt/mod.rs:1025
   5: std::io::Write::write_fmt
             at src/libstd/io/mod.rs:1426
   6: std::sys_common::backtrace::_print
             at src/libstd/sys_common/backtrace.rs:65
   7: std::sys_common::backtrace::print
             at src/libstd/sys_common/backtrace.rs:50
   8: std::panicking::default_hook::{{closure}}
             at src/libstd/panicking.rs:193
   9: std::panicking::default_hook
             at src/libstd/panicking.rs:210
  10: std::panicking::rust_panic_with_hook
             at src/libstd/panicking.rs:471
  11: rust_begin_unwind
             at src/libstd/panicking.rs:375
  12: core::panicking::panic_fmt
             at src/libcore/panicking.rs:84
  13: core::result::unwrap_failed
             at src/libcore/result.rs:1188
  14: core::result::Result<T,E>::unwrap
             at /rustc/0de96d37fbcc54978458c18f5067cd9817669bc8/src/libcore/result.rs:956
  15: build_script_build::main
             at ./build.rs:5
  16: std::rt::lang_start::{{closure}}
             at /rustc/0de96d37fbcc54978458c18f5067cd9817669bc8/src/libstd/rt.rs:67
  17: std::rt::lang_start_internal::{{closure}}
             at src/libstd/rt.rs:52
  18: std::panicking::try::do_call
             at src/libstd/panicking.rs:292
  19: __rust_maybe_catch_panic
             at src/libpanic_unwind/lib.rs:78
  20: std::panicking::try
             at src/libstd/panicking.rs:270
  21: std::panic::catch_unwind
             at src/libstd/panic.rs:394
  22: std::rt::lang_start_internal
             at src/libstd/rt.rs:51
  23: main
  24: <unknown>

cannot compile this crate

If it can't compile without the flamegraph feature, why is it optional?

pub enum Output<'a> {
    #[cfg(feature = "flamegraph")]
    Flamegraph(Option<FlamegraphOptions<'a>>),

    #[cfg(feature = "protobuf")]
    Protobuf,
}
 |
17 | pub enum Output<'a> {
   |                 ^^ unused parameter
   |

Add support for multiple outputs.

I would love to be able to generate a flamegraph and have protobuf output all in one benchmarking run without having to make code changes to my criterion benchmarks in between runs.

How to get sample data continously though http port?

Using "pprof" with go we usually start a server inside our programs and get the sample data on the web side, so that we can monitor our programs continuously.
But I see the "pprof-rs" usage examples are usually in a scope: it start with a new ProfilerGuard, and end with guard.report().build() before exit the scope.

Can some show me an example about getting sample data continously though http port? I think "pprof-rs" can do this job because it has been used like here: https://pingcap.medium.com/quickly-find-rust-program-bottlenecks-online-using-a-go-tool-37be0546aa8f

missing thread name for main thread

Hi,

I'm currently testing pprof-rs in a simple single-threaded application.

In the main function I start the profiler, make some work and then stop the profiler.

Being single-threaded I'd expect the thread_name of the samples to be main but it looks like it was not able to capture the name and it falls back to the threadi_id.

Are there some technical reasons to not use std::thread::current().name instead? link here

Windows support

Just tossing this issue out here for others that may reach this lib. Build fails since pthread_self and pthread_getname_np are, predictably, not present. Can std::thread::current and std::thread::Thread.name be used instead? Would it even matter with how signals work here?

random malloc error

malloc(): unsorted double linked list corrupted
malloc(): mismatching next->prev_size (unsorted)
segmentation fault
...

These errors occurs randomly. Here is one of the backtrace.

#0  0x00007fa41b9a4f3b in raise () from /lib64/libc.so.6
#1  0x00007fa41b98e535 in abort () from /lib64/libc.so.6
#2  0x00007fa41b9eadd7 in ?? () from /lib64/libc.so.6
#3  0x00007fa41b9f2c38 in ?? () from /lib64/libc.so.6
#4  0x00007fa41b9f5d0c in ?? () from /lib64/libc.so.6
#5  0x00007fa41b9f7453 in malloc () from /lib64/libc.so.6
#6  0x00005641846f5c2c in <alloc::vec::Vec<T> as core::clone::Clone>::clone ()
#7  0x00005641846f3a3d in <core::iter::adapters::Cloned<I> as core::iter::traits::iterator::Iterator>::fold ()
#8  0x00005641846f5bae in alloc::vec::Vec<T>::extend_from_slice ()
#9  0x00005641846f46f8 in <hashbrown::raw::RawTable<T> as core::clone::Clone>::clone ()
#10 0x00005641846f2d6d in rsperftools::profiler::Profiler::report ()
#11 0x00005641846f1993 in two_way::main ()
#12 0x00005641846f1ec3 in std::rt::lang_start::{{closure}} ()
#13 0x0000564184711fb3 in std::rt::lang_start_internal::{{closure}} () at src/libstd/rt.rs:49
#14 std::panicking::try::do_call () at src/libstd/panicking.rs:296
#15 0x00005641847140fa in __rust_maybe_catch_panic () at src/libpanic_unwind/lib.rs:82
#16 0x0000564184712abd in std::panicking::try () at src/libstd/panicking.rs:275
#17 std::panic::catch_unwind () at src/libstd/panic.rs:394
#18 std::rt::lang_start_internal () at src/libstd/rt.rs:48
#19 0x00005641846f1c42 in main ()

Compiling fails on FreeBSD

% cargo build
   Compiling pprof v0.10.0 (/usr/home/dveeden/dev/pprof-rs)
error[E0425]: cannot find function `create_pipe` in this scope
  --> src/addr_validate.rs:52:35
   |
52 |         let (read_fd, write_fd) = create_pipe()?;
   |                                   ^^^^^^^^^^^ not found in this scope

error[E0425]: cannot find value `addr` in this scope
   --> src/profiler.rs:280:44
    |
280 |                 if profiler.is_blocklisted(addr) {
    |                                            ^^^^ not found in this scope

error[E0308]: mismatched types
   --> src/profiler.rs:205:9
    |
205 | /         unsafe {
206 | |             #[cfg(target_os = "linux")]
207 | |             {
208 | |                 let errno = *libc::__errno_location();
...   |
215 | |             }
216 | |         }
    | |_________^ expected struct `ErrnoProtector`, found `()`

Some errors have detailed explanations: E0308, E0425.
For more information about an error, try `rustc --explain E0308`.
error: could not compile `pprof` due to 3 previous errors
% freebsd-version 
13.0-RELEASE-p11
% cargo --version
cargo 1.63.0-nightly

Segmentation fault / memory leak

Hi, thank you for this really nice crate. I couldn't get most other profilers to work on a macOS laptop (x86_64-apple-darwin), while this one works easily and generates useful flamegraphs.

Unfortunately it seems to be causing a segmentation fault in my program when I use it. There's no backtrace and the location of the crash seems to be non-deterministic so I have only indirect evidence:

  • There was a segmentation fault when I was using pprof::ProfilerGuard and it went away when I removed it.
  • Running the program under leak sanitizer showed a leak from objects allocated from here I think (see output in comment elsewhere), though maybe I've misunderstood.

Maybe the issue is not with this pprof directly but with the combination of this and some other dependency, but I doubt I have the ability to debug further. Just filing this here in case the issue is obvious or it helps somehow,

Detect child processes and external profiling

It does not seem that pprof-rs is detecting child-processes started by the main program. How hard do you think it is to implement this? Also is it possible to externally profile a Rust application? (through its PID)?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.