stak_profiler/
flamegraph.rs1use crate::{DurationRecord, Error, Stack, StackedRecord};
2use alloc::collections::BTreeMap;
3use std::io::Write;
4
5pub fn calculate_flamegraph(
7 records: impl IntoIterator<Item = Result<DurationRecord, Error>>,
8 mut writer: impl Write,
9) -> Result<(), Error> {
10 let mut map = BTreeMap::<Stack, u128>::new();
11
12 for record in records {
13 let record = record?;
14
15 if let Some(time) = map.get_mut(record.stack()) {
16 *time += record.time();
17 } else {
18 map.insert(record.stack().clone(), record.time());
19 }
20 }
21
22 for (stack, time) in map {
23 writeln!(writer, "{} {}", stack.display_local("<local>"), time)?;
24 }
25
26 Ok(())
27}
28
29#[cfg(test)]
30mod tests {
31 use super::*;
32 use indoc::indoc;
33 use pretty_assertions::assert_eq;
34
35 #[test]
36 fn test_calculate_flamegraph() {
37 let mut buffer = vec![];
38
39 calculate_flamegraph(
40 [
41 Ok(DurationRecord::new(
42 Stack::new(vec![None, Some("foo".into()), Some("bar".into())]),
43 1,
44 )),
45 Ok(DurationRecord::new(
46 Stack::new(vec![None, Some("foo".into()), Some("baz".into())]),
47 2,
48 )),
49 Ok(DurationRecord::new(
50 Stack::new(vec![None, Some("foo".into())]),
51 3,
52 )),
53 Ok(DurationRecord::new(
54 Stack::new(vec![None, Some("qux".into())]),
55 4,
56 )),
57 Ok(DurationRecord::new(
58 Stack::new(vec![None, Some("foo".into()), Some("baz".into())]),
59 42,
60 )),
61 ],
62 &mut buffer,
63 )
64 .unwrap();
65
66 assert_eq!(
67 String::from_utf8(buffer).unwrap(),
68 indoc!(
69 "
70 <local>;foo 3
71 <local>;foo;bar 1
72 <local>;foo;baz 44
73 <local>;qux 4
74 "
75 )
76 );
77 }
78}