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,22 @@ use std::{
5053 thread:: JoinHandle ,
5154} ;
5255
56+ use crate :: log:: tracing_chrome_instant:: TracingChromeInstant ;
57+
58+ /// Contains thread-local data for threads that send tracing spans or events.
59+ struct ThreadData {
60+ /// A unique ID for this thread, will populate "tid" field in the output trace file.
61+ tid : usize ,
62+ /// A clone of [ChromeLayer::out] to avoid the expensive operation of accessing a mutex
63+ /// every time. This is used to send [Message]s to the thread that saves trace data to file.
64+ out : Sender < Message > ,
65+ /// The instant in time this thread was started. All events happening on this thread will be
66+ /// saved to the trace file with a timestamp (the "ts" field) measured relative to this instant.
67+ start : TracingChromeInstant ,
68+ }
69+
5370thread_local ! {
54- static OUT : RefCell <Option <Sender <Message >>> = const { RefCell :: new( None ) } ;
55- static TID : RefCell <Option <usize >> = const { RefCell :: new( None ) } ;
71+ static THREAD_DATA : RefCell <Option <ThreadData >> = const { RefCell :: new( None ) } ;
5672}
5773
5874type NameFn < S > = Box < dyn Fn ( & EventOrSpan < ' _ , ' _ , S > ) -> String + Send + Sync > ;
6480 S : Subscriber + for < ' span > LookupSpan < ' span > + Send + Sync ,
6581{
6682 out : Arc < Mutex < Sender < Message > > > ,
67- start : std:: time:: Instant ,
6883 max_tid : AtomicUsize ,
6984 include_args : bool ,
7085 include_locations : bool ,
@@ -323,7 +338,6 @@ where
323338{
324339 fn new ( mut builder : ChromeLayerBuilder < S > ) -> ( ChromeLayer < S > , FlushGuard ) {
325340 let ( tx, rx) = mpsc:: channel ( ) ;
326- OUT . with ( |val| val. replace ( Some ( tx. clone ( ) ) ) ) ;
327341
328342 let out_writer = builder
329343 . out_writer
@@ -443,7 +457,6 @@ where
443457 } ;
444458 let layer = ChromeLayer {
445459 out : Arc :: new ( Mutex :: new ( tx) ) ,
446- start : std:: time:: Instant :: now ( ) ,
447460 max_tid : AtomicUsize :: new ( 0 ) ,
448461 name_fn : builder. name_fn . take ( ) ,
449462 cat_fn : builder. cat_fn . take ( ) ,
@@ -456,22 +469,7 @@ where
456469 ( layer, guard)
457470 }
458471
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 ( ) ;
472+ fn get_callsite ( & self , data : EventOrSpan < S > , tid : usize ) -> Callsite {
475473 let name = self . name_fn . as_ref ( ) . map ( |name_fn| name_fn ( & data) ) ;
476474 let target = self . cat_fn . as_ref ( ) . map ( |cat_fn| cat_fn ( & data) ) ;
477475 let meta = match data {
@@ -502,14 +500,6 @@ where
502500 ( None , None )
503501 } ;
504502
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-
513503 Callsite {
514504 tid,
515505 name,
@@ -548,31 +538,55 @@ where
548538 }
549539 }
550540
551- fn enter_span ( & self , span : SpanRef < S > , ts : f64 ) {
552- let callsite = self . get_callsite ( EventOrSpan :: Span ( & span) ) ;
541+ fn enter_span ( & self , span : SpanRef < S > , ts : f64 , tid : usize , out : & Sender < Message > ) {
542+ let callsite = self . get_callsite ( EventOrSpan :: Span ( & span) , tid ) ;
553543 let root_id = self . get_root_id ( span) ;
554- self . send_message ( Message :: Enter ( ts, callsite, root_id) ) ;
544+ let _ignored = out . send ( Message :: Enter ( ts, callsite, root_id) ) ;
555545 }
556546
557- fn exit_span ( & self , span : SpanRef < S > , ts : f64 ) {
558- let callsite = self . get_callsite ( EventOrSpan :: Span ( & span) ) ;
547+ fn exit_span ( & self , span : SpanRef < S > , ts : f64 , tid : usize , out : & Sender < Message > ) {
548+ let callsite = self . get_callsite ( EventOrSpan :: Span ( & span) , tid ) ;
559549 let root_id = self . get_root_id ( span) ;
560- self . send_message ( Message :: Exit ( ts, callsite, root_id) ) ;
550+ let _ignored = out . send ( Message :: Exit ( ts, callsite, root_id) ) ;
561551 }
562552
563- fn get_ts ( & self ) -> f64 {
564- self . start . elapsed ( ) . as_nanos ( ) as f64 / 1000.0
565- }
553+ /// Helper function that measures how much time is spent while executing `f` and accounts for it
554+ /// in subsequent calls, with the aim to reduce biases in the data collected by `tracing_chrome`
555+ /// by subtracting the time spent inside tracing functions from the timeline. This makes it so
556+ /// that the time spent inside the `tracing_chrome` functions does not impact the timestamps
557+ /// inside the trace file (i.e. `ts`), even if such functions are slow (e.g. because they need
558+ /// to format arguments on the same thread those arguments are collected on, otherwise memory
559+ /// safety would be broken).
560+ ///
561+ /// `f` is called with the microseconds elapsed since the current thread was started (**not**
562+ /// since the program start!), with the current thread ID (i.e. `tid`), and with a [Sender] that
563+ /// can be used to send a [Message] to the thread that collects [Message]s and saves them to the
564+ /// trace file.
565+ #[ inline( always) ]
566+ fn with_elapsed_micros_subtracting_tracing ( & self , f : impl Fn ( f64 , usize , & Sender < Message > ) ) {
567+ THREAD_DATA . with ( |value| {
568+ let mut thread_data = value. borrow_mut ( ) ;
569+ let ( ThreadData { tid, out, start } , new_thread) = match thread_data. as_mut ( ) {
570+ Some ( thread_data) => ( thread_data, false ) ,
571+ None => {
572+ let tid = self . max_tid . fetch_add ( 1 , Ordering :: SeqCst ) ;
573+ let out = self . out . lock ( ) . unwrap ( ) . clone ( ) ;
574+ let start = TracingChromeInstant :: setup_for_thread_and_start ( tid) ;
575+ * thread_data = Some ( ThreadData { tid, out, start } ) ;
576+ ( thread_data. as_mut ( ) . unwrap ( ) , true )
577+ }
578+ } ;
566579
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- }
580+ start. with_elapsed_micros_subtracting_tracing ( |ts| {
581+ if new_thread {
582+ let name = match std:: thread:: current ( ) . name ( ) {
583+ Some ( name) => name. to_owned ( ) ,
584+ None => tid. to_string ( ) ,
585+ } ;
586+ let _ignored = out. send ( Message :: NewThread ( * tid, name) ) ;
587+ }
588+ f ( ts, * tid, out) ;
589+ } ) ;
576590 } ) ;
577591 }
578592}
@@ -586,61 +600,68 @@ where
586600 return ;
587601 }
588602
589- let ts = self . get_ts ( ) ;
590- self . enter_span ( ctx. span ( id) . expect ( "Span not found." ) , ts) ;
603+ self . with_elapsed_micros_subtracting_tracing ( |ts, tid, out| {
604+ self . enter_span ( ctx. span ( id) . expect ( "Span not found." ) , ts, tid, out) ;
605+ } ) ;
591606 }
592607
593608 fn on_record ( & self , id : & span:: Id , values : & span:: Record < ' _ > , ctx : Context < ' _ , S > ) {
594609 if self . include_args {
595- let span = ctx. span ( id) . unwrap ( ) ;
596- let mut exts = span. extensions_mut ( ) ;
610+ self . with_elapsed_micros_subtracting_tracing ( |_, _, _| {
611+ let span = ctx. span ( id) . unwrap ( ) ;
612+ let mut exts = span. extensions_mut ( ) ;
597613
598- let args = exts. get_mut :: < ArgsWrapper > ( ) ;
614+ let args = exts. get_mut :: < ArgsWrapper > ( ) ;
599615
600- if let Some ( args) = args {
601- let args = Arc :: make_mut ( & mut args. args ) ;
602- values. record ( & mut JsonVisitor { object : args } ) ;
603- }
616+ if let Some ( args) = args {
617+ let args = Arc :: make_mut ( & mut args. args ) ;
618+ values. record ( & mut JsonVisitor { object : args } ) ;
619+ }
620+ } ) ;
604621 }
605622 }
606623
607624 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) ) ;
625+ self . with_elapsed_micros_subtracting_tracing ( |ts, tid, out| {
626+ let callsite = self . get_callsite ( EventOrSpan :: Event ( event) , tid) ;
627+ let _ignored = out. send ( Message :: Event ( ts, callsite) ) ;
628+ } ) ;
611629 }
612630
613631 fn on_exit ( & self , id : & span:: Id , ctx : Context < ' _ , S > ) {
614632 if let TraceStyle :: Async = self . trace_style {
615633 return ;
616634 }
617- let ts = self . get_ts ( ) ;
618- self . exit_span ( ctx. span ( id) . expect ( "Span not found." ) , ts) ;
635+ self . with_elapsed_micros_subtracting_tracing ( |ts, tid, out| {
636+ self . exit_span ( ctx. span ( id) . expect ( "Span not found." ) , ts, tid, out) ;
637+ } ) ;
619638 }
620639
621640 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- }
641+ self . with_elapsed_micros_subtracting_tracing ( |ts, tid, out| {
642+ if self . include_args {
643+ let mut args = Object :: new ( ) ;
644+ attrs. record ( & mut JsonVisitor { object : & mut args } ) ;
645+ ctx. span ( id) . unwrap ( ) . extensions_mut ( ) . insert ( ArgsWrapper {
646+ args : Arc :: new ( args) ,
647+ } ) ;
648+ }
649+ if let TraceStyle :: Threaded = self . trace_style {
650+ return ;
651+ }
632652
633- let ts = self . get_ts ( ) ;
634- self . enter_span ( ctx . span ( id ) . expect ( "Span not found." ) , ts ) ;
653+ self . enter_span ( ctx . span ( id ) . expect ( "Span not found." ) , ts , tid , out ) ;
654+ } ) ;
635655 }
636656
637657 fn on_close ( & self , id : span:: Id , ctx : Context < ' _ , S > ) {
638658 if let TraceStyle :: Threaded = self . trace_style {
639659 return ;
640660 }
641661
642- let ts = self . get_ts ( ) ;
643- self . exit_span ( ctx. span ( & id) . expect ( "Span not found." ) , ts) ;
662+ self . with_elapsed_micros_subtracting_tracing ( |ts, tid, out| {
663+ self . exit_span ( ctx. span ( & id) . expect ( "Span not found." ) , ts, tid, out) ;
664+ } ) ;
644665 }
645666}
646667
0 commit comments