Spaces:
Build error
Build error
use std::fs::File; | |
use std::io::Write; | |
use std::os::raw::c_int; | |
use std::path::Path; | |
use criterion::profiler::Profiler; | |
use pprof::flamegraph::TextTruncateDirection; | |
use pprof::protos::Message; | |
use pprof::ProfilerGuard; | |
/// Small custom profiler that can be used with Criterion to create a flamegraph for benchmarks. | |
/// Also see [the Criterion documentation on this][custom-profiler]. | |
/// | |
/// ## Example on how to enable the custom profiler: | |
/// | |
/// ``` | |
/// mod perf; | |
/// use perf::FlamegraphProfiler; | |
/// | |
/// fn fibonacci_profiled(criterion: &mut Criterion) { | |
/// // Use the criterion struct as normal here. | |
/// } | |
/// | |
/// fn custom() -> Criterion { | |
/// Criterion::default().with_profiler(FlamegraphProfiler::new()) | |
/// } | |
/// | |
/// criterion_group! { | |
/// name = benches; | |
/// config = custom(); | |
/// targets = fibonacci_profiled | |
/// } | |
/// ``` | |
/// | |
/// The neat thing about this is that it will sample _only_ the benchmark, and not other stuff like | |
/// the setup process. | |
/// | |
/// Further, it will only kick in if `--profile-time <time>` is passed to the benchmark binary. | |
/// A flamegraph will be created for each individual benchmark in its report directory under | |
/// `profile/flamegraph.svg`. | |
/// | |
/// [custom-profiler]: https://bheisler.github.io/criterion.rs/book/user_guide/profiling.html#implementing-in-process-profiling-hooks | |
pub struct FlamegraphProfiler<'a> { | |
frequency: c_int, | |
active_profiler: Option<ProfilerGuard<'a>>, | |
} | |
impl<'a> FlamegraphProfiler<'a> { | |
pub fn new(frequency: c_int) -> Self { | |
FlamegraphProfiler { | |
frequency, | |
active_profiler: None, | |
} | |
} | |
} | |
impl<'a> Profiler for FlamegraphProfiler<'a> { | |
fn start_profiling(&mut self, _benchmark_id: &str, _benchmark_dir: &Path) { | |
self.active_profiler = Some(ProfilerGuard::new(self.frequency).unwrap()); | |
} | |
fn stop_profiling(&mut self, _benchmark_id: &str, benchmark_dir: &Path) { | |
std::fs::create_dir_all(benchmark_dir).unwrap(); | |
let pprof_path = benchmark_dir.join("profile.pb"); | |
let flamegraph_path = benchmark_dir.join("flamegraph.svg"); | |
eprintln!("\nflamegraph_path = {flamegraph_path:#?}"); | |
let flamegraph_file = File::create(&flamegraph_path) | |
.expect("File system error while creating flamegraph.svg"); | |
let mut options = pprof::flamegraph::Options::default(); | |
options.hash = true; | |
options.image_width = Some(2500); | |
options.text_truncate_direction = TextTruncateDirection::Left; | |
options.font_size /= 3; | |
if let Some(profiler) = self.active_profiler.take() { | |
let report = profiler.report().build().unwrap(); | |
let mut file = File::create(pprof_path).unwrap(); | |
let profile = report.pprof().unwrap(); | |
let mut content = Vec::new(); | |
profile.encode(&mut content).unwrap(); | |
file.write_all(&content).unwrap(); | |
report | |
.flamegraph_with_options(flamegraph_file, &mut options) | |
.expect("Error writing flamegraph"); | |
} | |
} | |
} | |