use std;
use std::collections::HashMap;
use std::fs::File;
use failure::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.len() > 0 {
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: &File) -> Result<(), Error> {
let mut opts = Options {
direction: Direction::Inverted,
min_width: 1.0,
title: "py-spy".to_owned(),
..Default::default()
};
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 File) -> Result<(), Error> {
use std::io::Write;
for line in self.get_lines() {
w.write_all(line.as_bytes())?;
w.write_all(b"\n")?;
}
Ok(())
}
}