use std::collections::HashMap;
use std::io::Write;
use anyhow::Error;
use inferno::flamegraph::{Direction, Options};
use crate::stack_trace::StackTrace;
pub struct Flamegraph {
pub counts: HashMap<String, usize>,
pub show_linenumbers: bool,
}
impl Flamegraph {
pub fn new(show_linenumbers: bool) -> Flamegraph {
Flamegraph {
counts: HashMap::new(),
show_linenumbers,
}
}
pub fn increment(&mut self, trace: &StackTrace) -> std::io::Result<()> {
let frame = trace
.frames
.iter()
.rev()
.map(|frame| {
let filename = match &frame.short_filename {
Some(f) => f,
None => &frame.filename,
};
if self.show_linenumbers && frame.line != 0 {
format!("{} ({}:{})", frame.name, filename, frame.line)
} else if !filename.is_empty() {
format!("{} ({})", frame.name, filename)
} else {
frame.name.clone()
}
})
.collect::<Vec<String>>()
.join(";");
*self.counts.entry(frame).or_insert(0) += 1;
Ok(())
}
fn get_lines(&self) -> Vec<String> {
self.counts
.iter()
.map(|(k, v)| format!("{} {}", k, v))
.collect()
}
pub fn write(&self, w: &mut dyn Write) -> Result<(), Error> {
let mut opts = Options::default();
opts.direction = Direction::Inverted;
opts.min_width = 0.1;
opts.title = std::env::args().collect::<Vec<String>>().join(" ");
let lines = self.get_lines();
inferno::flamegraph::from_lines(&mut opts, lines.iter().map(|x| x.as_str()), w)
.map_err(|e| format_err!("Failed to write flamegraph: {}", e))?;
Ok(())
}
pub fn write_raw(&self, w: &mut dyn Write) -> Result<(), Error> {
for line in self.get_lines() {
w.write_all(line.as_bytes())?;
w.write_all(b"\n")?;
}
Ok(())
}
}