use core::sync::atomic::{AtomicI64, Ordering};
use thiserror::Error;
#[cfg(unix)]
use std::io::Write;
#[repr(C)]
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum OpTypes {
ProfilerInactive = 0,
ProfilerCollectingSample,
ProfilerUnwinding,
ProfilerSerializing,
SIZE,
}
impl OpTypes {
pub fn name(i: usize) -> Result<&'static str, CounterError> {
let rval = match i {
0 => "profiler_inactive",
1 => "profiler_collecting_sample",
2 => "profiler_unwinding",
3 => "profiler_serializing",
_ => return Err(CounterError::InvalidEnumValue(i)),
};
Ok(rval)
}
}
#[allow(clippy::declare_interior_mutable_const)]
const ATOMIC_ZERO: AtomicI64 = AtomicI64::new(0);
static OP_COUNTERS: [AtomicI64; OpTypes::SIZE as usize] = [ATOMIC_ZERO; OpTypes::SIZE as usize];
pub fn begin_op(op: OpTypes) -> Result<(), CounterError> {
let old = OP_COUNTERS[op as usize].fetch_add(1, Ordering::Relaxed);
if old == i64::MAX - 1 {
return Err(CounterError::CounterOverflow(op));
}
Ok(())
}
pub fn end_op(op: OpTypes) -> Result<(), CounterError> {
let old = OP_COUNTERS[op as usize].fetch_sub(1, Ordering::Relaxed);
if old <= 0 {
return Err(CounterError::OperationNotStarted(op));
}
Ok(())
}
#[cfg(unix)]
pub fn emit_counters(w: &mut impl Write) -> Result<(), CounterError> {
use crate::shared::constants::*;
writeln!(w, "{DD_CRASHTRACK_BEGIN_COUNTERS}")?;
for (i, c) in OP_COUNTERS.iter().enumerate() {
writeln!(
w,
"{{\"{}\": {}}}",
OpTypes::name(i)?,
c.load(Ordering::Relaxed)
)?;
}
writeln!(w, "{DD_CRASHTRACK_END_COUNTERS}")?;
w.flush()?;
Ok(())
}
pub fn reset_counters() -> Result<(), CounterError> {
for c in OP_COUNTERS.iter() {
c.store(0, Ordering::Relaxed);
}
Ok(())
}
#[derive(Debug, Error)]
pub enum CounterError {
#[error("Invalid enum value: {0}")]
InvalidEnumValue(usize),
#[error("Counter overflow for operation {0:?}")]
CounterOverflow(OpTypes),
#[error("Attempted to end operation {0:?} but it was never started or already ended")]
OperationNotStarted(OpTypes),
#[error("Failed to write to output: {0}")]
WriteError(#[from] std::io::Error),
}