SkillAgentSearch skills...

Blondie

Collect CPU callstack samples from a windows process

Install / Use

/learn @nico-abram/Blondie
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

blondie

<img alt="github" src="https://img.shields.io/badge/github-nico--abram/blondie-8da0cb?style=for-the-badge&labelColor=555555&logo=github" height="20"> <img alt="crates.io" src="https://img.shields.io/crates/v/blondie.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20"> <img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-blondie-66c2a5?style=for-the-badge&labelColor=555555&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K" height="20"> <img alt="build status" src="https://img.shields.io/github/workflow/status/nico-abram/blondie/blondie/main?style=for-the-badge" height="20">

Collect CPU callstack samples from a windows process.

Since the "SampledProfile" ETW events we use come from a "kernel event provider"(PerfInfo) we must use the ETW "Kernel Logger session", which requires elevated priviledges. Therefore, you must run blondie as administrator in order for it to work.

The blondie binary can be used to generate a text file with the sample count of each call stack, or a flamegraph using the inferno library.

The blondie_dtrace binary can be used as a dtrace replacement in cargo-flamegraph via the DTRACE environment variable.

Examples:

./blondie.exe flamegraph ./target/debug/some_binary_with_debuginfo.exe arg1 arg2
./flamegraph.svg

cargo build --release --bin blondie_dtrace
$ENV:DTRACE = "current_dir/target/release/blondie_dtrace.exe" # Or set DTRACE="current_dir/target/release/blondie_dtrace.exe" in cmd.exe
cd some/other/project
cargo flamegraph ; ./flamegraph.svg

I wrote this to be able to get flamegraphs using https://github.com/flamegraph-rs on windows.

This is built using the ETW(Event Tracing for Windows) API to collect CPU samples and DLL/EXE load events, and the DbgHelp Symbol Handler API to translate the virtual addresses ETW gives us to symbol names.

Future work?

  • Test on windows 7 and probably fix the things that don't work.
  • ~~Make it possible to trace a pre-existing pid instead of requiring a command to launch.~~ (Thanks to @felstead for implementing this!)
  • Use ETW filters to only receive events for the target process (We currently filter ourselves and discard events from other processes). See https://docs.microsoft.com/en-us/windows/win32/api/evntrace/nf-evntrace-starttracea https://docs.microsoft.com/en-us/windows/win32/api/evntrace/ns-evntrace-event_trace_properties_v2 https://docs.microsoft.com/en-us/windows/win32/api/evntprov/ns-evntprov-event_filter_descriptor specifically EVENT_FILTER_TYPE_PID (note: Does not work on Win7 via StartTrace, would need to use EnableTraceEx2 if we care about Win7)
  • Write a general ETW rust library.

Example output

Example flamegraph from a hello world rust program:

<img src="./img/example_output.png">

Here's a bit of example output from running blondie on dust:

<details> <summary>Example output</summary>

    	dust`__chkstk+0x37
    	dust`std::sys::windows::fs::File::file_attr+0x11
    	dust`std::sys::windows::fs::stat+0x6E
    	dust`std::fs::metadata<ref$<std::path::Path> >+0x4C
    	dust`std::path::Path::metadata+0x1B
    	dust`dust::platform::get_metadata+0x47
    	dust`dust::node::build_node+0x10B
    	dust`dust::dir_walker::walk::closure$0+0x2AF
    	dust`rayon::iter::filter_map::impl$6::consume<enum$<core::result::Result<std::fs::DirEntry,std::io::error::Error> >,dust::node::Node,rayon::iter::fold::FoldFolder<rayon::iter::map::MapFolder<rayon::iter::reduce::ReduceFolder<alloc::collections::linked_list::LinkedList<alloc::vec::Vec<dust::node::Node,alloc::alloc::Global> > (*)(alloc::collections::linked_list::LinkedList<alloc::vec::Vec<dust::node::Node,alloc::alloc::Global> >,alloc::collections::linked_list::LinkedList<alloc::vec::Vec<dust::node::Node,alloc::alloc::Global> >),alloc::collections::linked_list::LinkedList<alloc::vec::Vec<dust::node::Node,alloc::alloc::Global> > >,alloc::collections::linked_list::LinkedList<alloc::vec::Vec<dust::node::Node,alloc::alloc::Global> > (*)(alloc::vec::Vec<dust::node::Node,alloc::alloc::Global>)>,alloc::vec::Vec<dust::node::Node,alloc::alloc::Global>,alloc::vec::Vec<dust::node::Node,alloc::alloc::Global> (*)(alloc::vec::Vec<dust::node::Node,alloc::alloc::Global>,dust::node::Node)>,dust::dir_walker::walk::closure_env$0>+0x96
    	dust`rayon::iter::par_bridge::impl$3::fold_with<std::fs::ReadDir,rayon::iter::filter_map::FilterMapFolder<rayon::iter::fold::FoldFolder<rayon::iter::map::MapFolder<rayon::iter::reduce::ReduceFolder<alloc::collections::linked_list::LinkedList<alloc::vec::Vec<dust::node::Node,alloc::alloc::Global> > (*)(alloc::collections::linked_list::LinkedList<alloc::vec::Vec<dust::node::Node,alloc::alloc::Global> >,alloc::collections::linked_list::LinkedList<alloc::vec::Vec<dust::node::Node,alloc::alloc::Global> >),alloc::collections::linked_list::LinkedList<alloc::vec::Vec<dust::node::Node,alloc::alloc::Global> > >,alloc::collections::linked_list::LinkedList<alloc::vec::Vec<dust::node::Node,alloc::alloc::Global> > (*)(alloc::vec::Vec<dust::node::Node,alloc::alloc::Global>)>,alloc::vec::Vec<dust::node::Node,alloc::alloc::Global>,alloc::vec::Vec<dust::node::Node,alloc::alloc::Global> (*)(alloc::vec::Vec<dust::node::Node,alloc::alloc::Global>,dust::node::Node)>,dust::dir_walker::walk::closure_env$0> >+0x13F
    	dust`rayon::iter::plumbing::bridge_unindexed_producer_consumer<rayon::iter::par_bridge::IterParallelProducer<std::fs::ReadDir>,rayon::iter::filter_map::FilterMapConsumer<rayon::iter::fold::FoldConsumer<rayon::iter::map::MapConsumer<rayon::iter::reduce::ReduceConsumer<alloc::collections::linked_list::LinkedList<alloc::vec::Vec<dust::node::Node,alloc::alloc::Global> > (*)(alloc::collections::linked_list::LinkedList<alloc::vec::Vec<dust::node::Node,alloc::alloc::Global> >,alloc::collections::linked_list::LinkedList<alloc::vec::Vec<dust::node::Node,alloc::alloc::Global> >),alloc::collections::linked_list::LinkedList<alloc::vec::Vec<dust::node::Node,alloc::alloc::Global> > (*)()>,alloc::collections::linked_list::LinkedList<alloc::vec::Vec<dust::node::Node,alloc::alloc::Global> > (*)(alloc::vec::Vec<dust::node::Node,alloc::alloc::Global>)>,alloc::vec::Vec<dust::node::Node,alloc::alloc::Global> (*)(),alloc::vec::Vec<dust::node::Node,alloc::alloc::Global> (*)(alloc::vec::Vec<dust::node::Node,alloc::alloc::Global>,dust::node::Node)>,dust::dir_walker::walk::closure_env$0> >+0x664
    	dust`rayon::iter::plumbing::bridge_unindexed_producer_consumer::closure$0<rayon::iter::par_bridge::IterParallelProducer<std::fs::ReadDir>,rayon::iter::filter_map::FilterMapConsumer<rayon::iter::fold::FoldConsumer<rayon::iter::map::MapConsumer<rayon::iter::reduce::ReduceConsumer<alloc::collections::linked_list::LinkedList<alloc::vec::Vec<dust::node::Node,alloc::alloc::Global> > (*)(alloc::collections::linked_list::LinkedList<alloc::vec::Vec<dust::node::Node,alloc::alloc::Global> >,alloc::collections::linked_list::LinkedList<alloc::vec::Vec<dust::node::Node,alloc::alloc::Global> >),alloc::collections::linked_list::LinkedList<alloc::vec::Vec<dust::node::Node,alloc::alloc::Global> > (*)()>,alloc::collections::linked_list::LinkedList<alloc::vec::Vec<dust::node::Node,alloc::alloc::Global> > (*)(alloc::vec::Vec<dust::node::Node,alloc::alloc::Global>)>,alloc::vec::Vec<dust::node::Node,alloc::alloc::Global> (*)(),alloc::vec::Vec<dust::node::Node,alloc::alloc::Global> (*)(alloc::vec::Vec<dust::node::Node,alloc::alloc::Global>,dust::node::Node)>,dust::dir_walker::walk::closure_env$0> >+0xC5
    	dust`rayon_core::join::join_context::call_a::closure$0<alloc::collections::linked_list::LinkedList<alloc::vec::Vec<dust::node::Node,alloc::alloc::Global> >,rayon::iter::plumbing::bridge_unindexed_producer_consumer::closure_env$0<rayon::iter::par_bridge::IterParallelProducer<std::fs::ReadDir>,rayon::iter::filter_map::FilterMapConsumer<rayon::iter::fold::FoldConsumer<rayon::iter::map::MapConsumer<rayon::iter::reduce::ReduceConsumer<alloc::collections::linked_list::LinkedList<alloc::vec::Vec<dust::node::Node,alloc::alloc::Global> > (*)(alloc::collections::linked_list::LinkedList<alloc::vec::Vec<dust::node::Node,alloc::alloc::Global> >,alloc::collections::linked_list::LinkedList<alloc::vec::Vec<dust::node::Node,alloc::alloc::Global> >),alloc::collections::linked_list::LinkedList<alloc::vec::Vec<dust::node::Node,alloc::alloc::Global> > (*)()>

Related Skills

View on GitHub
GitHub Stars55
CategoryDevelopment
Updated1d ago
Forks7

Languages

Rust

Security Score

95/100

Audited on Mar 26, 2026

No findings