use std::alloc;
use std::ffi::CString;
#[doc(hidden)]
pub use tracy_client_sys as sys;
pub struct Span(
sys::TracyCZoneCtx,
std::marker::PhantomData<*mut sys::TracyCZoneCtx>,
);
impl Span {
pub fn new(name: &str, function: &str, file: &str, line: u32, callstack_depth: u16) -> Self {
unsafe {
sys::___tracy_init_thread();
let loc = sys::___tracy_alloc_srcloc_name(
line,
file.as_ptr() as _,
file.len(),
function.as_ptr() as _,
function.len(),
name.as_ptr() as _,
name.len(),
);
if callstack_depth == 0 {
Self(
sys::___tracy_emit_zone_begin_alloc(loc, 1),
std::marker::PhantomData,
)
} else {
Self(
sys::___tracy_emit_zone_begin_alloc_callstack(
loc,
adjust_stack_depth(callstack_depth).into(),
1,
),
std::marker::PhantomData,
)
}
}
}
pub fn emit_value(&self, value: u64) {
unsafe {
sys::___tracy_emit_zone_value(self.0, value);
}
}
pub fn emit_text(&self, text: &str) {
unsafe {
sys::___tracy_emit_zone_text(self.0, text.as_ptr() as _, text.len());
}
}
}
impl Drop for Span {
fn drop(&mut self) {
unsafe {
sys::___tracy_emit_zone_end(self.0);
}
}
}
pub struct ProfiledAllocator<T>(T, u16);
impl<T> ProfiledAllocator<T> {
pub const fn new(inner_allocator: T, callstack_depth: u16) -> Self {
Self(inner_allocator, adjust_stack_depth(callstack_depth))
}
fn emit_alloc(&self, ptr: *mut u8, size: usize) -> *mut u8 {
unsafe {
if self.1 == 0 {
sys::___tracy_emit_memory_alloc(ptr as _, size, 1);
} else {
sys::___tracy_emit_memory_alloc_callstack(ptr as _, size, self.1.into(), 1);
}
}
ptr
}
fn emit_free(&self, ptr: *mut u8) -> *mut u8 {
unsafe {
if self.1 == 0 {
sys::___tracy_emit_memory_free(ptr as _, 1);
} else {
sys::___tracy_emit_memory_free_callstack(ptr as _, self.1.into(), 1);
}
}
ptr
}
}
unsafe impl<T: alloc::GlobalAlloc> alloc::GlobalAlloc for ProfiledAllocator<T> {
unsafe fn alloc(&self, layout: alloc::Layout) -> *mut u8 {
self.emit_alloc(self.0.alloc(layout), layout.size())
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: alloc::Layout) {
self.0.dealloc(self.emit_free(ptr), layout)
}
unsafe fn alloc_zeroed(&self, layout: alloc::Layout) -> *mut u8 {
self.emit_alloc(self.0.alloc_zeroed(layout), layout.size())
}
unsafe fn realloc(&self, ptr: *mut u8, layout: alloc::Layout, new_size: usize) -> *mut u8 {
self.emit_alloc(
self.0.realloc(self.emit_free(ptr), layout, new_size),
new_size,
)
}
}
#[macro_export]
macro_rules! finish_continuous_frame {
() => {
unsafe {
$crate::sys::___tracy_emit_frame_mark(std::ptr::null());
}
};
($name: literal) => {
unsafe {
$crate::sys::___tracy_emit_frame_mark(concat!($name, "\0").as_ptr() as _);
}
};
}
#[macro_export]
macro_rules! start_noncontinuous_frame {
($name: literal) => {
unsafe {
let name = concat!($name, "\0");
$crate::sys::___tracy_emit_frame_mark_start(name.as_ptr() as _);
$crate::Frame::new_unchecked(name)
}
};
}
pub struct Frame(&'static str);
impl Frame {
#[doc(hidden)]
pub const unsafe fn new_unchecked(name: &'static str) -> Self {
Self(name)
}
}
impl Drop for Frame {
fn drop(&mut self) {
unsafe {
sys::___tracy_emit_frame_mark_end(self.0.as_ptr() as _);
}
}
}
pub fn message(message: &str, callstack_depth: u16) {
unsafe {
sys::___tracy_emit_message(
message.as_ptr() as _,
message.len(),
adjust_stack_depth(callstack_depth).into(),
)
}
}
pub fn color_message(message: &str, rgba: u32, callstack_depth: u16) {
unsafe {
sys::___tracy_emit_messageC(
message.as_ptr() as _,
message.len(),
rgba >> 8,
adjust_stack_depth(callstack_depth).into(),
)
}
}
pub fn set_thread_name(name: &str) {
let name = CString::new(name).unwrap();
unsafe { sys::___tracy_set_thread_name(name.as_ptr() as _) }
}
#[macro_export]
macro_rules! create_plot {
($name: literal) => {
unsafe { $crate::Plot::new_unchecked(concat!($name, "\0")) }
};
}
pub struct Plot(&'static str);
impl Plot {
#[doc(hidden)]
pub const unsafe fn new_unchecked(name: &'static str) -> Self {
Self(name)
}
pub fn point(&self, value: f64) {
unsafe {
sys::___tracy_emit_plot(self.0.as_ptr() as _, value);
}
}
}
#[inline(always)]
#[cfg(windows)]
const fn adjust_stack_depth(depth: u16) -> u16 {
62 ^ ((depth ^ 62) & 0u16.wrapping_sub((depth < 62) as _))
}
#[inline(always)]
#[cfg(not(windows))]
const fn adjust_stack_depth(depth: u16) -> u16 {
depth
}
#[cfg(test)]
mod tests {
use super::*;
#[global_allocator]
static GLOBAL: ProfiledAllocator<alloc::System> = ProfiledAllocator::new(alloc::System, 100);
#[test]
fn zone_values() {
let span = Span::new("test zone values", "zone_values", file!(), line!(), 100);
span.emit_value(42);
span.emit_text("some text");
}
#[test]
fn finish_frameset() {
for _ in 0..10 {
finish_continuous_frame!();
}
}
#[test]
fn finish_secondary_frameset() {
for _ in 0..5 {
finish_continuous_frame!("every two seconds");
}
}
#[test]
fn non_continuous_frameset() {
let _: Frame = start_noncontinuous_frame!("weird frameset");
}
#[test]
fn plot_something() {
static PLOT: Plot = create_plot!("a plot");
for i in 0..10 {
PLOT.point(i as f64);
}
}
}