pyroscope_rbspy_oncpu/ui/
output.rs

1use std::io::Write;
2
3use crate::core::types::{StackFrame, StackTrace};
4use crate::ui::{callgrind, flamegraph, pprof, speedscope, summary};
5
6use anyhow::Result;
7
8pub trait Outputter {
9    fn record(&mut self, stack: &StackTrace) -> Result<()>;
10    fn complete(&mut self, write: &mut dyn Write) -> Result<()>;
11}
12
13// Uses Inferno to visualize stack traces
14pub struct Flamegraph {
15    stats: flamegraph::Stats,
16    min_width: f64,
17}
18
19impl Outputter for Flamegraph {
20    fn record(&mut self, stack: &StackTrace) -> Result<()> {
21        self.stats.record(&stack.trace)
22    }
23
24    fn complete(&mut self, write: &mut dyn Write) -> Result<()> {
25        self.stats.write_flamegraph(write, self.min_width)
26    }
27}
28
29impl Flamegraph {
30    pub fn new(min_width: f64) -> Flamegraph {
31        Flamegraph {
32            min_width,
33            stats: Default::default(),
34        }
35    }
36}
37
38// Collapsed stacks are the intermediate flamegraph format,
39// useful for making additional processing or using other flamegraph generators.
40#[derive(Default)]
41pub struct Collapsed(pub flamegraph::Stats);
42
43impl Outputter for Collapsed {
44    fn record(&mut self, stack: &StackTrace) -> Result<()> {
45        self.0.record(&stack.trace)
46    }
47
48    fn complete(&mut self, mut write: &mut dyn Write) -> Result<()> {
49        self.0.write_collapsed(&mut write)
50    }
51}
52
53pub struct Callgrind(pub callgrind::Stats);
54
55impl Outputter for Callgrind {
56    fn record(&mut self, stack: &StackTrace) -> Result<()> {
57        self.0.add(&filter_unknown(&stack.trace));
58        Ok(())
59    }
60
61    fn complete(&mut self, mut write: &mut dyn Write) -> Result<()> {
62        self.0.finish();
63        self.0.write(&mut write)
64    }
65}
66
67pub struct Summary(pub summary::Stats);
68
69impl Outputter for Summary {
70    fn record(&mut self, stack: &StackTrace) -> Result<()> {
71        self.0.add_function_name(&filter_unknown(&stack.trace));
72        Ok(())
73    }
74
75    fn complete(&mut self, mut write: &mut dyn Write) -> Result<()> {
76        self.0.write(&mut write)
77    }
78}
79
80pub struct SummaryLine(pub summary::Stats);
81
82impl Outputter for SummaryLine {
83    fn record(&mut self, stack: &StackTrace) -> Result<()> {
84        self.0.add_lineno(&filter_unknown(&stack.trace));
85        Ok(())
86    }
87
88    fn complete(&mut self, mut write: &mut dyn Write) -> Result<()> {
89        self.0.write(&mut write)
90    }
91}
92
93pub struct Speedscope(pub speedscope::Stats);
94
95impl Outputter for Speedscope {
96    fn record(&mut self, stack: &StackTrace) -> Result<()> {
97        self.0.record(&stack)
98    }
99
100    fn complete(&mut self, write: &mut dyn Write) -> Result<()> {
101        self.0.write(write)
102    }
103}
104
105pub struct Pprof(pub pprof::Stats);
106
107impl Outputter for Pprof {
108    fn record(&mut self, stack: &StackTrace) -> Result<()> {
109        self.0.record(stack)
110    }
111
112    fn complete(&mut self, write: &mut dyn Write) -> Result<()> {
113        self.0.write(write)
114    }
115}
116
117/// Filter out unknown functions from stack trace before reporting.
118/// Most of the time it isn't useful to include the "unknown C function" stacks.
119fn filter_unknown(trace: &[StackFrame]) -> Vec<StackFrame> {
120    let unknown = StackFrame::unknown_c_function();
121    let vec: Vec<StackFrame> = trace.iter().filter(|&x| x != &unknown).cloned().collect();
122    if vec.is_empty() {
123        vec![unknown]
124    } else {
125        vec
126    }
127}