Skip to content

Commit f57d6e1

Browse files
committed
Account for time spent tracing, use RDTSC for faster time
1 parent 57fd9e4 commit f57d6e1

File tree

3 files changed

+264
-78
lines changed

3 files changed

+264
-78
lines changed

src/bin/log/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
pub mod setup;
22
mod tracing_chrome;
3+
mod tracing_chrome_instant;

src/bin/log/tracing_chrome.rs

Lines changed: 93 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,15 @@
77
//! (`git log -- path/to/tracing_chrome.rs`), but in summary:
88
//! - the file attributes were changed and `extern crate` was added at the top
99
//! - if a tracing span has a field called "tracing_separate_thread", it will be given a separate
10-
//! span ID even in [TraceStyle::Threaded] mode, to make it appear on a separate line when viewing
11-
//! the trace in <https://ui.perfetto.dev>. This is the syntax to trigger this behavior:
10+
//! span ID even in [TraceStyle::Threaded] mode, to make it appear on a separate line when viewing
11+
//! the trace in <https://ui.perfetto.dev>. This is the syntax to trigger this behavior:
1212
//! ```rust
1313
//! tracing::info_span!("my_span", tracing_separate_thread = tracing::field::Empty, /* ... */)
1414
//! ```
15-
//! - use i64 instead of u64 for the "id" in [ChromeLayer::get_root_id] to be compatible with Perfetto
15+
//! - use i64 instead of u64 for the "id" in [ChromeLayer::get_root_id] to be compatible with
16+
//! Perfetto
17+
//! - use [ChromeLayer::with_elapsed_micros_subtracting_tracing] to make time measurements faster on
18+
//! Linux x86/x86_64 and to subtract time spent tracing from the timestamps in the trace file
1619
//!
1720
//! Depending on the tracing-chrome crate from crates.io is unfortunately not possible, since it
1821
//! depends on `tracing_core` which conflicts with rustc_private's `tracing_core` (meaning it would
@@ -50,9 +53,16 @@ use std::{
5053
thread::JoinHandle,
5154
};
5255

56+
use crate::log::tracing_chrome_instant::TracingChromeInstant;
57+
58+
struct ThreadData {
59+
tid: usize,
60+
out: Sender<Message>,
61+
start: TracingChromeInstant,
62+
}
63+
5364
thread_local! {
54-
static OUT: RefCell<Option<Sender<Message>>> = const { RefCell::new(None) };
55-
static TID: RefCell<Option<usize>> = const { RefCell::new(None) };
65+
static THREAD_DATA: RefCell<Option<ThreadData>> = const { RefCell::new(None) };
5666
}
5767

5868
type NameFn<S> = Box<dyn Fn(&EventOrSpan<'_, '_, S>) -> String + Send + Sync>;
@@ -64,7 +74,6 @@ where
6474
S: Subscriber + for<'span> LookupSpan<'span> + Send + Sync,
6575
{
6676
out: Arc<Mutex<Sender<Message>>>,
67-
start: std::time::Instant,
6877
max_tid: AtomicUsize,
6978
include_args: bool,
7079
include_locations: bool,
@@ -323,7 +332,6 @@ where
323332
{
324333
fn new(mut builder: ChromeLayerBuilder<S>) -> (ChromeLayer<S>, FlushGuard) {
325334
let (tx, rx) = mpsc::channel();
326-
OUT.with(|val| val.replace(Some(tx.clone())));
327335

328336
let out_writer = builder
329337
.out_writer
@@ -443,7 +451,6 @@ where
443451
};
444452
let layer = ChromeLayer {
445453
out: Arc::new(Mutex::new(tx)),
446-
start: std::time::Instant::now(),
447454
max_tid: AtomicUsize::new(0),
448455
name_fn: builder.name_fn.take(),
449456
cat_fn: builder.cat_fn.take(),
@@ -456,22 +463,7 @@ where
456463
(layer, guard)
457464
}
458465

459-
fn get_tid(&self) -> (usize, bool) {
460-
TID.with(|value| {
461-
let tid = *value.borrow();
462-
match tid {
463-
Some(tid) => (tid, false),
464-
None => {
465-
let tid = self.max_tid.fetch_add(1, Ordering::SeqCst);
466-
value.replace(Some(tid));
467-
(tid, true)
468-
}
469-
}
470-
})
471-
}
472-
473-
fn get_callsite(&self, data: EventOrSpan<S>) -> Callsite {
474-
let (tid, new_thread) = self.get_tid();
466+
fn get_callsite(&self, data: EventOrSpan<S>, tid: usize) -> Callsite {
475467
let name = self.name_fn.as_ref().map(|name_fn| name_fn(&data));
476468
let target = self.cat_fn.as_ref().map(|cat_fn| cat_fn(&data));
477469
let meta = match data {
@@ -502,14 +494,6 @@ where
502494
(None, None)
503495
};
504496

505-
if new_thread {
506-
let name = match std::thread::current().name() {
507-
Some(name) => name.to_owned(),
508-
None => tid.to_string(),
509-
};
510-
self.send_message(Message::NewThread(tid, name));
511-
}
512-
513497
Callsite {
514498
tid,
515499
name,
@@ -548,31 +532,55 @@ where
548532
}
549533
}
550534

551-
fn enter_span(&self, span: SpanRef<S>, ts: f64) {
552-
let callsite = self.get_callsite(EventOrSpan::Span(&span));
535+
fn enter_span(&self, span: SpanRef<S>, ts: f64, tid: usize, out: &Sender<Message>) {
536+
let callsite = self.get_callsite(EventOrSpan::Span(&span), tid);
553537
let root_id = self.get_root_id(span);
554-
self.send_message(Message::Enter(ts, callsite, root_id));
538+
let _ignored = out.send(Message::Enter(ts, callsite, root_id));
555539
}
556540

557-
fn exit_span(&self, span: SpanRef<S>, ts: f64) {
558-
let callsite = self.get_callsite(EventOrSpan::Span(&span));
541+
fn exit_span(&self, span: SpanRef<S>, ts: f64, tid: usize, out: &Sender<Message>) {
542+
let callsite = self.get_callsite(EventOrSpan::Span(&span), tid);
559543
let root_id = self.get_root_id(span);
560-
self.send_message(Message::Exit(ts, callsite, root_id));
544+
let _ignored = out.send(Message::Exit(ts, callsite, root_id));
561545
}
562546

563-
fn get_ts(&self) -> f64 {
564-
self.start.elapsed().as_nanos() as f64 / 1000.0
565-
}
547+
/// Helper function that measures how much time is spent while executing `f` and accounts for it
548+
/// in subsequent calls, with the aim to reduce biases in the data collected by `tracing_chrome`
549+
/// by subtracting the time spent inside tracing functions from the timeline. This makes it so
550+
/// that the time spent inside the `tracing_chrome` functions does not impact the timestamps
551+
/// inside the trace file (i.e. `ts`), even if such functions are slow (e.g. because they need
552+
/// to format arguments on the same thread those arguments are collected on, otherwise memory
553+
/// safety would be broken).
554+
///
555+
/// `f` is called with the microseconds elapsed since the current thread was started (**not**
556+
/// since the program start!), with the current thread ID (i.e. `tid`), and with a [Sender] that
557+
/// can be used to send a [Message] to the thread that collects [Message]s and saves them to the
558+
/// trace file.
559+
#[inline(always)]
560+
fn with_elapsed_micros_subtracting_tracing<T: Fn(f64, usize, &Sender<Message>)>(&self, f: T) {
561+
THREAD_DATA.with(|value| {
562+
let mut thread_data = value.borrow_mut();
563+
let (ThreadData { tid, out, start }, new_thread) = match thread_data.as_mut() {
564+
Some(thread_data) => (thread_data, false),
565+
None => {
566+
let tid = self.max_tid.fetch_add(1, Ordering::SeqCst);
567+
let out = self.out.lock().unwrap().clone();
568+
let start = TracingChromeInstant::setup_for_thread_and_start(tid);
569+
*thread_data = Some(ThreadData { tid, out, start });
570+
(thread_data.as_mut().unwrap(), true)
571+
}
572+
};
566573

567-
fn send_message(&self, message: Message) {
568-
OUT.with(move |val| {
569-
if val.borrow().is_some() {
570-
let _ignored = val.borrow().as_ref().unwrap().send(message);
571-
} else {
572-
let out = self.out.lock().unwrap().clone();
573-
let _ignored = out.send(message);
574-
val.replace(Some(out));
575-
}
574+
start.with_elapsed_micros_subtracting_tracing(|ts| {
575+
if new_thread {
576+
let name = match std::thread::current().name() {
577+
Some(name) => name.to_owned(),
578+
None => tid.to_string(),
579+
};
580+
let _ignored = out.send(Message::NewThread(*tid, name));
581+
}
582+
f(ts, *tid, out);
583+
});
576584
});
577585
}
578586
}
@@ -586,61 +594,68 @@ where
586594
return;
587595
}
588596

589-
let ts = self.get_ts();
590-
self.enter_span(ctx.span(id).expect("Span not found."), ts);
597+
self.with_elapsed_micros_subtracting_tracing(|ts, tid, out| {
598+
self.enter_span(ctx.span(id).expect("Span not found."), ts, tid, out);
599+
});
591600
}
592601

593602
fn on_record(&self, id: &span::Id, values: &span::Record<'_>, ctx: Context<'_, S>) {
594603
if self.include_args {
595-
let span = ctx.span(id).unwrap();
596-
let mut exts = span.extensions_mut();
604+
self.with_elapsed_micros_subtracting_tracing(|_, _, _| {
605+
let span = ctx.span(id).unwrap();
606+
let mut exts = span.extensions_mut();
597607

598-
let args = exts.get_mut::<ArgsWrapper>();
608+
let args = exts.get_mut::<ArgsWrapper>();
599609

600-
if let Some(args) = args {
601-
let args = Arc::make_mut(&mut args.args);
602-
values.record(&mut JsonVisitor { object: args });
603-
}
610+
if let Some(args) = args {
611+
let args = Arc::make_mut(&mut args.args);
612+
values.record(&mut JsonVisitor { object: args });
613+
}
614+
});
604615
}
605616
}
606617

607618
fn on_event(&self, event: &Event<'_>, _ctx: Context<'_, S>) {
608-
let ts = self.get_ts();
609-
let callsite = self.get_callsite(EventOrSpan::Event(event));
610-
self.send_message(Message::Event(ts, callsite));
619+
self.with_elapsed_micros_subtracting_tracing(|ts, tid, out| {
620+
let callsite = self.get_callsite(EventOrSpan::Event(event), tid);
621+
let _ignored = out.send(Message::Event(ts, callsite));
622+
});
611623
}
612624

613625
fn on_exit(&self, id: &span::Id, ctx: Context<'_, S>) {
614626
if let TraceStyle::Async = self.trace_style {
615627
return;
616628
}
617-
let ts = self.get_ts();
618-
self.exit_span(ctx.span(id).expect("Span not found."), ts);
629+
self.with_elapsed_micros_subtracting_tracing(|ts, tid, out| {
630+
self.exit_span(ctx.span(id).expect("Span not found."), ts, tid, out);
631+
});
619632
}
620633

621634
fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) {
622-
if self.include_args {
623-
let mut args = Object::new();
624-
attrs.record(&mut JsonVisitor { object: &mut args });
625-
ctx.span(id).unwrap().extensions_mut().insert(ArgsWrapper {
626-
args: Arc::new(args),
627-
});
628-
}
629-
if let TraceStyle::Threaded = self.trace_style {
630-
return;
631-
}
635+
self.with_elapsed_micros_subtracting_tracing(|ts, tid, out| {
636+
if self.include_args {
637+
let mut args = Object::new();
638+
attrs.record(&mut JsonVisitor { object: &mut args });
639+
ctx.span(id).unwrap().extensions_mut().insert(ArgsWrapper {
640+
args: Arc::new(args),
641+
});
642+
}
643+
if let TraceStyle::Threaded = self.trace_style {
644+
return;
645+
}
632646

633-
let ts = self.get_ts();
634-
self.enter_span(ctx.span(id).expect("Span not found."), ts);
647+
self.enter_span(ctx.span(id).expect("Span not found."), ts, tid, out);
648+
});
635649
}
636650

637651
fn on_close(&self, id: span::Id, ctx: Context<'_, S>) {
638652
if let TraceStyle::Threaded = self.trace_style {
639653
return;
640654
}
641655

642-
let ts = self.get_ts();
643-
self.exit_span(ctx.span(&id).expect("Span not found."), ts);
656+
self.with_elapsed_micros_subtracting_tracing(|ts, tid, out| {
657+
self.exit_span(ctx.span(&id).expect("Span not found."), ts, tid, out);
658+
});
644659
}
645660
}
646661

0 commit comments

Comments
 (0)