use pprof::{ProfilerGuard, ProfilerGuardBuilder, Report};
use crate::backends::Backend;
use crate::backends::State;
use crate::Result;
#[derive(Default)]
pub struct Pprof<'a> {
inner_builder: Option<ProfilerGuardBuilder>,
guard: Option<ProfilerGuard<'a>>,
state: State,
}
impl std::fmt::Debug for Pprof<'_> {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
write!(fmt, "Pprof Backend")
}
}
impl Backend for Pprof<'_> {
fn get_state(&self) -> State {
self.state
}
fn initialize(&mut self, sample_rate: i32) -> Result<()> {
if self.state != State::Uninitialized {
return Err(crate::error::PyroscopeError {
msg: String::from("Pprof Backend is already Initialized"),
});
}
let profiler = ProfilerGuardBuilder::default().frequency(sample_rate);
self.inner_builder = Some(profiler);
self.state = State::Ready;
Ok(())
}
fn start(&mut self) -> Result<()> {
if self.state != State::Ready {
return Err(crate::error::PyroscopeError {
msg: String::from("Pprof Backend is not Ready"),
});
}
self.guard = Some(self.inner_builder.as_ref().unwrap().clone().build()?);
self.state = State::Running;
Ok(())
}
fn stop(&mut self) -> Result<()> {
if self.state != State::Running {
return Err(crate::error::PyroscopeError {
msg: String::from("Pprof Backend is not Running"),
});
}
drop(self.guard.take());
self.state = State::Ready;
Ok(())
}
fn report(&mut self) -> Result<Vec<u8>> {
if self.state != State::Running {
return Err(crate::error::PyroscopeError {
msg: String::from("Pprof Backend is not Running"),
});
}
let mut buffer = Vec::new();
let report = self.guard.as_ref().unwrap().report().build()?;
fold(&report, true, &mut buffer)?;
self.stop()?;
self.start()?;
Ok(buffer)
}
}
fn fold<W>(report: &Report, with_thread_name: bool, mut writer: W) -> Result<()>
where W: std::io::Write {
for (key, value) in report.data.iter() {
if with_thread_name {
if !key.thread_name.is_empty() {
write!(writer, "{};", key.thread_name)?;
} else {
write!(writer, "{:?};", key.thread_id)?;
}
}
let last_frame = key.frames.len() - 1;
for (index, frame) in key.frames.iter().rev().enumerate() {
let last_symbol = frame.len() - 1;
for (index, symbol) in frame.iter().rev().enumerate() {
if index == last_symbol {
write!(writer, "{}", symbol)?;
} else {
write!(writer, "{};", symbol)?;
}
}
if index != last_frame {
write!(writer, ";")?;
}
}
writeln!(writer, " {}", value)?;
}
Ok(())
}