Skip to content

Commit 61955b7

Browse files
committed
Rename and support kernel stack symbol resolution
1 parent ad5b240 commit 61955b7

File tree

9 files changed

+366
-123
lines changed

9 files changed

+366
-123
lines changed

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,7 @@ collapsed_output
99
/testt
1010
/.vscode
1111
out.log
12-
cargo-flamegraph.stacks
12+
cargo-flamegraph.stacks
13+
out.logg
14+
out2.logg
15+
stacks_folded.txt

Cargo.lock

Lines changed: 10 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
[package]
2-
name = "winstacks"
2+
name = "blondie"
33
description = "Collect CPU call stack samples from a windows process"
44
version = "0.1.0-alpha2"
55
edition = "2021"
66
license = "MIT OR Apache-2.0 OR BSL-1.0 OR MPL-2.0 OR Zlib OR Unlicense"
7-
homepage = "https://github.com/nico-abram/winstacks/"
8-
repository = "https://github.com/nico-abram/winstacks/"
9-
documentation = "https://docs.rs/winstacks"
7+
homepage = "https://github.com/nico-abram/blondie/"
8+
repository = "https://github.com/nico-abram/blondie/"
9+
documentation = "https://docs.rs/blondie"
1010
readme = "README.md"
1111
keywords = ["windows", "profiling", "sampling", "callstack", "perf"]
1212
categories = [
@@ -33,19 +33,16 @@ windows = { version = "0.36.1", features = [
3333
rustc-hash = "1.1.0"
3434
# Only for the binary crate
3535
inferno = { version = "0.11", optional = true }
36+
clap = { version = "3.1", optional = true }
3637

3738
[profile.release]
3839
debug = true
3940

4041
[[bin]]
41-
name = "winstacks"
42-
path = "src/winstacks_bin.rs"
42+
name = "blondie"
43+
path = "src/blondie_bin.rs"
44+
required-features = ["inferno", "clap"]
4345

4446
[[bin]]
45-
name = "winstacks_inferno"
46-
path = "src/winstacks_inferno.rs"
47-
required-features = ["inferno"]
48-
49-
[[bin]]
50-
name = "winstacks_dtrace"
51-
path = "src/winstacks_dtrace.rs"
47+
name = "blondie_dtrace"
48+
path = "src/blondie_dtrace.rs"

README.md

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,19 @@
1-
# winstacks
1+
# blondie
22

3-
Collect CPU call stack samplles from a windows process.
3+
Collect CPU callstack samples from a windows process.
44

5-
Since the ["SampledProfile"](https://docs.microsoft.com/en-us/windows/win32/etw/sampledprofile) ETW events we use come from a ["kernel event provider"](https://docs.microsoft.com/en-us/windows/win32/etw/event-tracing-mof-classes)(PerfInfo) we must use the ETW ["Kernel Logger session"](https://docs.microsoft.com/en-us/windows/win32/etw/nt-kernel-logger-constants), which requires elevated priviledges. Therefore, **you must run winstack as administrator in order for it to work**.
5+
Since the ["SampledProfile"](https://docs.microsoft.com/en-us/windows/win32/etw/sampledprofile) ETW events we use come from a ["kernel event provider"](https://docs.microsoft.com/en-us/windows/win32/etw/event-tracing-mof-classes)(PerfInfo) we must use the ETW ["Kernel Logger session"](https://docs.microsoft.com/en-us/windows/win32/etw/nt-kernel-logger-constants), which requires elevated priviledges. Therefore, **you must run blondie as administrator in order for it to work**.
66

7-
The `winstacks_inferno` binary can be used to generate a flamegraph using the [`inferno` library](https://github.com/jonhoo/inferno).
7+
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](https://github.com/jonhoo/inferno).
88

9-
The `winstacks` binary can be used to generate a text file with the sample count of each call stack.
10-
11-
The `winstacks_dtrace` binary can be used as a dtrace replacement in [cargo-flamegraph](https://github.com/flamegraph-rs) via the DTRACE environment variable.
9+
The `blondie_dtrace` binary can be used as a dtrace replacement in [cargo-flamegraph](https://github.com/flamegraph-rs) via the DTRACE environment variable.
1210

1311
Examples:
1412

15-
./winstacks_inferno.exe ./target/debug/x86-64-windows-msvc/some_binary_with_debuginfo.exe arg1 arg2 ; ./winferno_flamegraph.svg
13+
./blondie.exe ./target/debug/some_binary_with_debuginfo.exe arg1 arg2 ; ./flamegraph.svg
1614

17-
cargo build --release --bin winstacks_dtrace
18-
$ENV:DTRACE = "current_dir/target/release/winstacks_dtrace.exe" # Or set DTRACE="current_dir/target/release/winstacks_dtrace.exe" in cmd.exe
15+
cargo build --release --bin blondie_dtrace
16+
$ENV:DTRACE = "current_dir/target/release/blondie_dtrace.exe" # Or set DTRACE="current_dir/target/release/blondie_dtrace.exe" in cmd.exe
1917
cd some/other/project
2018
cargo flamegraph ; ./flamegraph.svg
2119

@@ -32,7 +30,7 @@ This is built using the ETW(Event Tracing for Windows)]() API to collect CPU sam
3230

3331
# Example output
3432

35-
Here's a bit of example output from running winstack on [dust](https://github.com/bootandy/dust):
33+
Here's a bit of example output from running blondie on [dust](https://github.com/bootandy/dust):
3634

3735
<details>
3836
<summary>Example output</summary>
@@ -298,3 +296,4 @@ Here's a bit of example output from running winstack on [dust](https://github.co
298296
```
299297

300298
</details>
299+
```

src/blondie_bin.rs

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
use std::ffi::OsString;
2+
3+
use clap::Parser;
4+
use inferno::collapse::Collapse;
5+
6+
#[derive(Parser, Debug)]
7+
#[clap(name = "blondie")]
8+
#[clap(bin_name = "blondie")]
9+
#[clap(author = "Nicolas Abram Lujan <[email protected]>")]
10+
#[clap(version = "0.1-alpha2")]
11+
#[clap(about = "CPU call stack sampling", long_about = None)]
12+
struct Blondie {
13+
/// If kernel stacks should be included in the output
14+
#[clap(short, long)]
15+
kernel_stacks: bool,
16+
/// Output filename. Defaults to flamegraph.svg
17+
#[clap(short, long, parse(from_os_str))]
18+
out: Option<std::path::PathBuf>,
19+
/// Don't redirect stdout/stderr from the target process
20+
#[clap(short, long)]
21+
hide_output: bool,
22+
23+
#[clap(subcommand)]
24+
subcommand: Subcommands,
25+
}
26+
#[derive(clap::Subcommand, Debug)]
27+
enum Subcommands {
28+
/// Generate a flamegraph. Default output is ./flamegraph.svg
29+
#[clap(trailing_var_arg = true)]
30+
Flamegraph {
31+
/// Output filename for trace text callstacks. Defaults to nowhere.
32+
#[clap(short, long, parse(from_os_str))]
33+
trace_file: Option<std::path::PathBuf>,
34+
/// Output filename for inferno collapsed stacks. Defaults to nowhere.
35+
#[clap(short, long, parse(from_os_str))]
36+
collapsed_file: Option<std::path::PathBuf>,
37+
/// The command to run
38+
command: OsString,
39+
/// Arguments for the command to run
40+
args: Vec<OsString>,
41+
},
42+
/// Generate a text file with the folded callstacks. Default output is ./folded_stacks.txt
43+
#[clap(trailing_var_arg = true)]
44+
FoldedText {
45+
/// The command to run
46+
command: OsString,
47+
/// Arguments for the command to run
48+
args: Vec<OsString>,
49+
},
50+
}
51+
52+
fn main() -> Result<(), blondie::Error> {
53+
let args = Blondie::parse();
54+
55+
let (command, command_args) = match &args.subcommand {
56+
Subcommands::Flamegraph { command, args, .. } => (command.clone(), args.clone()),
57+
Subcommands::FoldedText { command, args } => (command.clone(), args.clone()),
58+
};
59+
let mut command_builder = std::process::Command::new(command);
60+
command_builder.args(command_args);
61+
let trace_ctx = blondie::trace_command(command_builder, args.kernel_stacks)?;
62+
63+
match &args.subcommand {
64+
Subcommands::Flamegraph {
65+
trace_file,
66+
collapsed_file,
67+
..
68+
} => {
69+
let filename = args.out.unwrap_or("./flamegraph.svg".into());
70+
71+
let mut trace_output = Vec::new();
72+
trace_ctx.write_dtrace(&mut trace_output)?;
73+
if let Some(trace_file) = trace_file {
74+
std::fs::write(trace_file, &trace_output).unwrap();
75+
}
76+
77+
let mut collapsed_output = Vec::new();
78+
let collapse_options = inferno::collapse::dtrace::Options::default();
79+
inferno::collapse::dtrace::Folder::from(collapse_options)
80+
.collapse(&trace_output[..], &mut collapsed_output)
81+
.expect("unable to collapse generated profile data");
82+
if let Some(collapsed_file) = collapsed_file {
83+
std::fs::write(collapsed_file, &collapsed_output).unwrap();
84+
}
85+
86+
let flamegraph_file = std::fs::File::create(&filename).expect(&format!(
87+
"Error creating flamegraph file {}",
88+
filename.into_os_string().into_string().unwrap()
89+
));
90+
let flamegraph_writer = std::io::BufWriter::new(flamegraph_file);
91+
let mut inferno_opts = inferno::flamegraph::Options::default();
92+
inferno::flamegraph::from_reader(
93+
&mut inferno_opts,
94+
&collapsed_output[..],
95+
flamegraph_writer,
96+
)
97+
.expect("unable to generate a flamegraph from the collapsed stack data");
98+
}
99+
Subcommands::FoldedText { .. } => {
100+
let filename = args.out.unwrap_or("./folded_stacks.txt".into());
101+
102+
let f = std::fs::File::create(&filename).expect(&format!(
103+
"Unable to create file {}",
104+
filename.into_os_string().into_string().unwrap()
105+
));
106+
let mut f = std::io::BufWriter::new(f);
107+
108+
trace_ctx.write_dtrace(&mut f)?;
109+
}
110+
};
111+
112+
Ok(())
113+
}

src/winstacks_dtrace.rs renamed to src/blondie_dtrace.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
// Binary to run cargo-flamegraph using winstacks, which pretends to be dtrace
1+
// Binary to run cargo-flamegraph using blondie, which pretends to be dtrace
22

3-
fn main() -> Result<(), winstacks::Error> {
3+
fn main() -> Result<(), blondie::Error> {
44
let args = std::env::args_os().skip(1).collect::<Vec<_>>();
55
let dash_c_idx = args
66
.iter()
@@ -22,7 +22,11 @@ fn main() -> Result<(), winstacks::Error> {
2222
other_args = &args_v[..];
2323
arg0 = arg0_str.to_os_string();
2424
}
25-
let trace_ctx = winstacks::trace_command(arg0.clone(), other_args)?;
25+
26+
let mut c = std::process::Command::new(&arg0);
27+
c.args(other_args);
28+
let kernel_stacks = false;
29+
let trace_ctx = blondie::trace_command(c, kernel_stacks)?;
2630

2731
let f = std::fs::File::create("./cargo-flamegraph.stacks").expect("Unable to create file");
2832
let mut f = std::io::BufWriter::new(f);

0 commit comments

Comments
 (0)