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+
5364thread_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
5868type NameFn < S > = Box < dyn Fn ( & EventOrSpan < ' _ , ' _ , S > ) -> String + Send + Sync > ;
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