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