Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions tracing-tracy/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,15 @@ thread_local! {
static TRACY_SPAN_STACK: VecCell<(Span, u64)> = const { VecCell::new() };
}

#[cfg(feature = "fibers")]
thread_local! {
/// Cache of fiber names, keyed by span name. Bounded by the number of distinct span names.
static FIBER_NAMES: std::cell::RefCell<std::collections::HashMap<&'static str, client::FiberName>> =
std::cell::RefCell::new(std::collections::HashMap::new());
/// Tracks span nesting depth on this thread for fiber root detection.
static SPAN_DEPTH: std::cell::Cell<usize> = const { std::cell::Cell::new(0) };
}

/// A tracing layer that collects data in Tracy profiling format.
///
/// # Examples
Expand Down Expand Up @@ -234,6 +243,25 @@ where
fn on_enter(&self, id: &Id, ctx: Context<S>) {
let Some(span) = ctx.span(id) else { return };

#[cfg(feature = "fibers")]
{
let depth = SPAN_DEPTH.with(|d| {
let depth = d.get();
d.set(depth + 1);
depth
});
if depth == 0 {
let span_name = span.metadata().name();
let fiber_name = FIBER_NAMES.with(|cache| {
let mut cache = cache.borrow_mut();
*cache.entry(span_name).or_insert_with(|| {
client::FiberName::new_leak(span_name.to_string())
})
});
self.client.fiber_enter(fiber_name);
}
}

let extensions = span.extensions();
let fields = extensions.get::<TracyFields<C>>();
let stack_frame = {
Expand Down Expand Up @@ -301,6 +329,18 @@ where
"Exiting a tracing span, but got nothing on the tracy span stack!",
);
}

#[cfg(feature = "fibers")]
{
let depth = SPAN_DEPTH.with(|d| {
let depth = d.get().saturating_sub(1);
d.set(depth);
depth
});
if depth == 0 {
self.client.fiber_leave();
}
}
}

fn on_close(&self, id: Id, ctx: Context<'_, S>) {
Expand All @@ -310,6 +350,7 @@ where
let buf = mem::take(&mut fields.fields);
CACHE.with(|cache| drop(StrCacheGuard::new(cache, buf)));
};

}
}

Expand Down
103 changes: 103 additions & 0 deletions tracy-client/src/fiber.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
use crate::Client;

/// A name for a Tracy fiber.
///
/// Create with the [`fiber_name!`](crate::fiber_name) macro.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct FiberName(pub(crate) &'static str);

impl FiberName {
/// Construct a `FiberName` dynamically, leaking the provided String.
///
/// You should call this function once for a given name, and store the returned `FiberName` for
/// continued use, to avoid rapid memory use growth. Whenever possible, prefer the
/// [`fiber_name!`](crate::fiber_name) macro, which takes a literal name and doesn't leak
/// memory.
#[must_use]
pub fn new_leak(name: String) -> Self {
#[cfg(feature = "enable")]
{
let mut name = name;
name.push('\0');
let name = Box::leak(name.into_boxed_str());
Self(name)
}
#[cfg(not(feature = "enable"))]
{
drop(name);
Self("\0")
}
}
}

/// Instrumentation for fibers, coroutines, and similar cooperative multitasking primitives.
///
/// Tracy fibers allow attributing zones and messages to a logical fiber rather than to the
/// underlying OS thread. This is useful for profiling async runtimes where multiple tasks
/// share a single thread.
impl Client {
/// Enter a fiber context.
///
/// All subsequent zones, messages, and other events on the current thread will be attributed
/// to the named fiber until [`Client::fiber_leave`] is called or another fiber is entered.
///
/// It is valid to call `fiber_enter` multiple times without an intermediate `fiber_leave`
/// call; this represents a direct context switch between fibers.
pub fn fiber_enter(&self, name: FiberName) {
#[cfg(all(feature = "enable", feature = "fibers"))]
unsafe {
let () = sys::___tracy_fiber_enter(name.0.as_ptr().cast());
}
#[cfg(not(all(feature = "enable", feature = "fibers")))]
let _ = name;
}

/// Leave the current fiber context.
///
/// Subsequent events will be attributed to the current OS thread.
pub fn fiber_leave(&self) {
#[cfg(all(feature = "enable", feature = "fibers"))]
unsafe {
let () = sys::___tracy_fiber_leave();
}
}
}

/// Construct a [`FiberName`].
///
/// The resulting value may be used as an argument for the [`Client::fiber_enter`] method.
/// The macro can be used in a `const` context.
///
/// # Example
///
/// ```rust
/// let name: tracy_client::FiberName = tracy_client::fiber_name!("my-fiber");
/// ```
#[macro_export]
macro_rules! fiber_name {
($name: literal) => {{
unsafe { $crate::internal::create_fiber_name(concat!($name, "\0")) }
}};
}

/// Convenience shortcut for [`Client::fiber_enter`] on the current client.
///
/// # Panics
///
/// - If a `Client` isn't currently running.
pub fn fiber_enter(name: FiberName) {
Client::running()
.expect("fiber_enter without a running Client")
.fiber_enter(name);
}

/// Convenience shortcut for [`Client::fiber_leave`] on the current client.
///
/// # Panics
///
/// - If a `Client` isn't currently running.
pub fn fiber_leave() {
Client::running()
.expect("fiber_leave without a running Client")
.fiber_leave();
}
8 changes: 8 additions & 0 deletions tracy-client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
//!
#![doc = include_str!("../FEATURES.mkd")]

pub use crate::fiber::{fiber_enter, fiber_leave, FiberName};
pub use crate::frame::{frame_image, frame_mark, Frame, FrameName};
pub use crate::gpu::{
GpuContext, GpuContextCreationError, GpuContextType, GpuSpan, GpuSpanCreationError,
Expand All @@ -37,6 +38,7 @@ use std::alloc;
use std::ffi::CString;
pub use sys;

mod fiber;
mod frame;
mod gpu;
mod plot;
Expand Down Expand Up @@ -86,6 +88,12 @@ pub mod internal {
crate::SpanLocation { _internal: () }
}

#[inline(always)]
#[must_use]
pub const unsafe fn create_fiber_name(name: &'static str) -> crate::fiber::FiberName {
crate::fiber::FiberName(name)
}

#[inline(always)]
#[must_use]
pub const unsafe fn create_frame_name(name: &'static str) -> crate::frame::FrameName {
Expand Down
14 changes: 14 additions & 0 deletions tracy-client/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,18 @@ fn nameless_span() {
set_thread_name!("test thread");
}

#[cfg(feature = "fibers")]
fn use_fibers() {
const MY_FIBER: FiberName = fiber_name!("my-fiber");
let client = Client::start();
client.fiber_enter(MY_FIBER);
client.fiber_leave();

let dynamic = FiberName::new_leak("dynamic-fiber".to_string());
fiber_enter(dynamic);
fiber_leave();
}

fn gpu() {
let client = Client::start();

Expand Down Expand Up @@ -152,6 +164,8 @@ fn main() {
thread.join().unwrap();
set_thread_name();
gpu();
#[cfg(feature = "fibers")]
use_fibers();
// Sleep to give time to the client to send the data to the profiler.
std::thread::sleep(Duration::from_secs(5));
}
Expand Down