Skip to content

Commit

Permalink
Allow reading perf.data directly. Fixes #52
Browse files Browse the repository at this point in the history
  • Loading branch information
romange committed Oct 25, 2021
1 parent 02b0524 commit ab02ba1
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 23 deletions.
20 changes: 15 additions & 5 deletions src/bin/flamegraph.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::path::PathBuf;

use anyhow::anyhow;
use structopt::StructOpt;

Expand All @@ -19,6 +21,9 @@ struct Opt {
#[structopt(flatten)]
graph: flamegraph::Options,

#[structopt(parse(from_os_str), long = "perfdata", conflicts_with = "pid")]
perf_file: Option<PathBuf>,

trailing_arguments: Vec<String>,
}

Expand All @@ -40,11 +45,16 @@ fn main() -> anyhow::Result<()> {
};
}

let workload = match (opt.pid, opt.trailing_arguments.is_empty()) {
(Some(p), true) => Workload::Pid(p),
(None, false) => Workload::Command(opt.trailing_arguments.clone()),
(Some(_), false) => return Err(anyhow!("cannot pass in command with --pid")),
(None, true) => return Err(anyhow!("no workload given to generate a flamegraph for")),
let workload = if let Some(perf_file) = opt.perf_file {
let path = perf_file.to_str().unwrap();
Workload::ReadPerf(path.to_string())
} else {
match (opt.pid, opt.trailing_arguments.is_empty()) {
(Some(p), true) => Workload::Pid(p),
(None, false) => Workload::Command(opt.trailing_arguments.clone()),
(Some(_), false) => return Err(anyhow!("cannot pass in command with --pid")),
(None, true) => return Err(anyhow!("no workload given to generate a flamegraph for")),
}
};
flamegraph::generate_flamegraph_for_workload(workload, opt.graph)
}
43 changes: 25 additions & 18 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ use inferno::{
pub enum Workload {
Command(Vec<String>),
Pid(u32),
ReadPerf(String),
}

#[cfg(target_os = "linux")]
Expand Down Expand Up @@ -82,6 +83,7 @@ mod arch {
command.arg("-p");
command.arg(p.to_string());
}
Workload::ReadPerf(_) => (),
}

(command, perf_output)
Expand Down Expand Up @@ -163,6 +165,7 @@ mod arch {
command.arg("-p");
command.arg(p.to_string());
}
Workload::ReadPerf(_) => (),
}

(command, None)
Expand Down Expand Up @@ -231,27 +234,31 @@ pub fn generate_flamegraph_for_workload(
signal_hook::low_level::register(SIGINT, || {}).expect("cannot register signal handler")
};

let (mut command, perf_output) =
arch::initial_command(workload, opts.root, opts.frequency, opts.custom_cmd);
if opts.verbose {
println!("command {:?}", command);
}
let perf_output = if let Workload::ReadPerf(perf_file) = workload {
Some(perf_file)
} else {
let (mut command, perf_output2) =
arch::initial_command(workload, opts.root, opts.frequency, opts.custom_cmd);
if opts.verbose {
println!("command {:?}", command);
}

let mut recorder = command.spawn().expect(arch::SPAWN_ERROR);
let mut recorder = command.spawn().expect(arch::SPAWN_ERROR);

let exit_status = recorder.wait().expect(arch::WAIT_ERROR);
let exit_status = recorder.wait().expect(arch::WAIT_ERROR);

#[cfg(unix)]
signal_hook::low_level::unregister(handler);

// only stop if perf exited unsuccessfully, but
// was not killed by a signal (assuming that the
// latter case usually means the user interrupted
// it in some way)
if terminated_by_error(exit_status) {
eprintln!("failed to sample program");
std::process::exit(1);
}
#[cfg(unix)]
signal_hook::low_level::unregister(handler);
// only stop if perf exited unsuccessfully, but
// was not killed by a signal (assuming that the
// latter case usually means the user interrupted
// it in some way)
if terminated_by_error(exit_status) {
eprintln!("failed to sample program");
std::process::exit(1);
}
perf_output2
};

let output = arch::output(perf_output, opts.script_no_inline)?;

Expand Down

0 comments on commit ab02ba1

Please sign in to comment.