pyroscope_rbspy_oncpu/recorder/
record.rs1use anyhow::{Context, Error, Result};
2use std::fs::File;
3use std::path::PathBuf;
4use std::sync::{Arc, Mutex};
5
6use crate::storage::Store;
7use crate::ui::summary;
8
9pub struct Config {
11 pub format: crate::core::types::OutputFormat,
13 pub raw_path: Option<PathBuf>,
15 pub out_path: Option<PathBuf>,
17 pub pid: crate::core::process::Pid,
21 pub with_subprocesses: bool,
24 pub sample_rate: u32,
26 pub maybe_duration: Option<std::time::Duration>,
29 pub flame_min_width: f64,
33 pub lock_process: bool,
39 pub force_version: Option<String>,
44 pub on_cpu: bool,
45}
46
47pub struct Recorder {
48 format: crate::core::types::OutputFormat,
49 flame_min_width: f64,
50 out_path: Option<PathBuf>,
51 raw_path: Option<PathBuf>,
52 sample_rate: u32,
53 sampler: crate::sampler::Sampler,
54 summary: Arc<Mutex<summary::Stats>>,
55}
56
57impl Recorder {
58 pub fn new(config: Config) -> Self {
59 let sampler = crate::sampler::Sampler::new(
60 config.pid,
61 config.sample_rate,
62 config.lock_process,
63 config.maybe_duration,
64 config.with_subprocesses,
65 config.force_version,
66 config.on_cpu,
67 );
68
69 Recorder {
70 format: config.format,
71 flame_min_width: config.flame_min_width,
72 out_path: config.out_path,
73 raw_path: config.raw_path,
74 sample_rate: config.sample_rate,
75 sampler,
76 summary: Arc::new(Mutex::new(summary::Stats::new())),
77 }
78 }
79
80 pub fn record(&self) -> Result<(), Error> {
82 let (trace_sender, trace_receiver) = std::sync::mpsc::sync_channel(100);
87 let (result_sender, result_receiver) = std::sync::mpsc::channel();
88 self.sampler.start(trace_sender, result_sender)?;
89
90 let mut out = None;
94 if self.out_path.is_some() {
95 out = Some(self.format.clone().outputter(self.flame_min_width));
96 }
97 let mut raw_store = None;
98 if let Some(raw_path) = &self.raw_path {
99 raw_store = Some(Store::new(&raw_path, self.sample_rate)?);
100 }
101
102 for trace in trace_receiver {
103 if let Some(out) = &mut out {
104 out.record(&trace)?;
105 }
106 if let Some(raw_store) = &mut raw_store {
107 raw_store.write(&trace)?;
108 }
109
110 let mut summary = self.summary.lock().unwrap();
111 summary.add_function_name(&trace.trace);
112 }
113
114 if let (Some(out), Some(out_path)) = (&mut out, self.out_path.as_ref()) {
116 if out_path.display().to_string() == "-" {
117 out.complete(&mut std::io::stdout())?;
118 } else {
119 let mut out_file = File::create(&out_path).context(format!(
120 "Failed to create output file {}",
121 &out_path.display()
122 ))?;
123 out.complete(&mut out_file)?;
124 }
125 }
126 if let Some(raw_store) = raw_store {
127 raw_store.complete();
128 }
129
130 let mut num_ok = 0;
134 let mut last_result = Ok(());
135 for result in result_receiver {
136 if result.is_ok() {
137 num_ok += 1;
138 }
139 last_result = result;
140 }
141
142 match num_ok {
143 0 => last_result,
144 _ => Ok(()),
145 }
146 }
147
148 pub fn stop(&self) {
150 self.sampler.stop();
151 }
152
153 pub fn write_summary(&self, w: &mut dyn std::io::Write) -> Result<(), Error> {
155 let width = match term_size::dimensions() {
156 Some((w, _)) => Some(w as usize),
157 None => None,
158 };
159 let timing_error_traces = self.sampler.timing_error_traces();
160 let total_traces = self.sampler.total_traces();
161 let percent_timing_error = (timing_error_traces as f64) / (total_traces as f64) * 100.0;
162
163 let summary = self.summary.lock().unwrap();
164 writeln!(
165 w,
166 "Time since start: {}s. Press Ctrl+C to stop.",
167 summary.elapsed_time().as_secs()
168 )?;
169
170 writeln!(w, "Summary of profiling data so far:")?;
171 summary.write_top_n(w, 20, width)?;
172
173 if total_traces > 100 && percent_timing_error > 0.5 {
174 writeln!(w, "{:.1}% ({}/{}) of stack traces were sampled late because we couldn't sample at expected rate; results may be inaccurate. Current rate: {}. Try sampling at a lower rate with `--rate`.", percent_timing_error, timing_error_traces, total_traces, self.sample_rate)?;
177 }
178 Ok(())
179 }
180}
181
182impl Drop for Recorder {
183 fn drop(&mut self) {
184 self.stop();
185 }
186}