#![doc = include_str!("../README.md")]
use std::cell::{Cell, RefCell};
use std::io::{Result, Write};
use std::arch::asm;
#[cfg(all(not(feature = "off"), feature = "capacity_1_million"))]
pub const TSC_TRACE_CAPACITY: usize = 1_000_000;
#[cfg(all(not(feature = "off"), feature = "capacity_8_million"))]
pub const TSC_TRACE_CAPACITY: usize = 8_000_000;
#[cfg(all(not(feature = "off"), feature = "capacity_16_million"))]
pub const TSC_TRACE_CAPACITY: usize = 16_000_000;
#[cfg(all(not(feature = "off"), feature = "capacity_32_million"))]
pub const TSC_TRACE_CAPACITY: usize = 32_000_000;
#[cfg(all(not(feature = "off"), feature = "capacity_64_million"))]
pub const TSC_TRACE_CAPACITY: usize = 64_000_000;
#[cfg(feature = "off")]
pub const TSC_TRACE_CAPACITY: usize = 0;
#[cfg(all(
not(feature = "off"),
not(feature = "capacity_1_million"),
not(feature = "capacity_8_million"),
not(feature = "capacity_16_million"),
not(feature = "capacity_32_million"),
not(feature = "capacity_64_million")
))]
pub const TSC_TRACE_CAPACITY: usize = 1_000_000;
const CAPACITY: usize = TSC_TRACE_CAPACITY * 3;
#[cfg(feature = "const_array")]
thread_local! {
static TSC_TRACE_SPANS: RefCell<[u64; CAPACITY]> = const { RefCell::new([0; CAPACITY]) };
static TSC_TRACE_INDEX: Cell<usize> = const { Cell::new(0) };
}
#[cfg(not(feature = "const_array"))]
thread_local! {
static TSC_TRACE_SPANS: RefCell<Vec<u64>> = RefCell::new(Vec::with_capacity(CAPACITY));
static TSC_TRACE_INDEX: Cell<usize> = const { Cell::new(0) };
}
pub fn write_traces_csv(writer: &mut impl Write) -> Result<()> {
let mut res = Ok(());
TSC_TRACE_SPANS.with(|spans| {
let spans = spans.borrow();
for chunk in spans.chunks_exact(3) {
if let &[tag, start, stop] = chunk {
if stop == 0 {
break;
}
if let e @ Err(_) = writeln!(writer, "{tag},{start},{stop},{}", stop - start) {
res = e;
break;
}
}
}
});
res
}
pub fn write_traces_binary(writer: &mut impl Write) -> Result<()> {
let mut res = Ok(());
TSC_TRACE_SPANS.with(|spans| {
let spans = spans.borrow();
let bytes: &[u8] = bytemuck::cast_slice(&*spans);
if let e @ Err(_) = writer.write_all(&bytes) {
res = e;
}
});
res
}
#[inline(always)]
#[cfg(target_arch = "x86")]
pub fn rdtsc() -> u64 {
#[cfg(feature = "lfence")]
use core::arch::x86::_mm_lfence;
use core::arch::x86::_rdtsc;
unsafe {
#[cfg(feature = "lfence")]
_mm_lfence();
let r = _rdtsc();
#[cfg(feature = "lfence")]
_mm_lfence();
r
}
}
#[inline(always)]
#[cfg(target_arch = "x86_64")]
pub fn rdtsc() -> u64 {
#[cfg(feature = "lfence")]
use core::arch::x86_64::_mm_lfence;
use core::arch::x86_64::_rdtsc;
unsafe {
#[cfg(feature = "lfence")]
_mm_lfence();
let r = _rdtsc();
#[cfg(feature = "lfence")]
_mm_lfence();
r
}
}
#[inline(always)]
#[cfg(target_arch = "aarch64")]
pub fn rdtsc() -> u64 {
let r: u64;
unsafe{
asm!(
"mrs x0, cntvct_el0",
out("x0") r
);
}
r
}
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")))]
pub fn rdtsc() -> u64 {
unimplemented!("x86 or x86_64 needed for rdtsc, aarch64 needed for workaround")
}
pub struct TraceSpan {
tag: u64,
start: u64,
}
impl TraceSpan {
pub fn new(tag: u64) -> Self {
TraceSpan {
tag,
start: rdtsc(),
}
}
}
impl Drop for TraceSpan {
fn drop(&mut self) {
let stop = rdtsc();
_insert_trace(self.tag, self.start, stop);
}
}
#[inline(always)]
pub fn _insert_trace(tag: u64, start: u64, stop: u64) {
TSC_TRACE_INDEX.with(|index| {
let mut i = index.get();
if i >= CAPACITY {
i = 0;
}
#[cfg(feature = "const_array")]
TSC_TRACE_SPANS.with(|spans| {
let mut spans = spans.borrow_mut();
spans[i] = tag;
spans[i + 1] = start;
spans[i + 2] = stop;
i += 3;
});
#[cfg(not(feature = "const_array"))]
TSC_TRACE_SPANS.with(|spans| {
let mut spans = spans.borrow_mut();
if spans.len() >= CAPACITY {
spans[i] = tag;
spans[i + 1] = start;
spans[i + 2] = stop;
i += 3;
} else {
spans.push(tag);
spans.push(start);
spans.push(stop);
i += 3;
}
});
index.set(i);
})
}
#[macro_export]
#[cfg(not(feature = "off"))]
macro_rules! trace_span {
($e:expr) => {
let _tsc_trace_span = TraceSpan::new(($e) as u64);
};
}
#[macro_export]
#[cfg(feature = "off")]
macro_rules! trace_span {
($e:expr) => {};
}
#[macro_export]
#[cfg(not(feature = "off"))]
macro_rules! insert_trace {
($a:expr, $b:expr, $c:expr) => {
_insert_trace(($a) as u64, ($b) as u64, ($c) as u64);
};
}
#[macro_export]
#[cfg(feature = "off")]
macro_rules! insert_trace {
($a:expr, $b:expr, $c:expr) => {};
}