7
7
//! (`git log -- path/to/tracing_chrome.rs`), but in summary:
8
8
//! - the file attributes were changed and `extern crate` was added at the top
9
9
//! - 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:
12
12
//! ```rust
13
13
//! tracing::info_span!("my_span", tracing_separate_thread = tracing::field::Empty, /* ... */)
14
14
//! ```
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
16
19
//!
17
20
//! Depending on the tracing-chrome crate from crates.io is unfortunately not possible, since it
18
21
//! depends on `tracing_core` which conflicts with rustc_private's `tracing_core` (meaning it would
@@ -50,9 +53,22 @@ use std::{
50
53
thread:: JoinHandle ,
51
54
} ;
52
55
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
+
53
70
thread_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 ) } ;
56
72
}
57
73
58
74
type NameFn < S > = Box < dyn Fn ( & EventOrSpan < ' _ , ' _ , S > ) -> String + Send + Sync > ;
64
80
S : Subscriber + for < ' span > LookupSpan < ' span > + Send + Sync ,
65
81
{
66
82
out : Arc < Mutex < Sender < Message > > > ,
67
- start : std:: time:: Instant ,
68
83
max_tid : AtomicUsize ,
69
84
include_args : bool ,
70
85
include_locations : bool ,
@@ -323,7 +338,6 @@ where
323
338
{
324
339
fn new ( mut builder : ChromeLayerBuilder < S > ) -> ( ChromeLayer < S > , FlushGuard ) {
325
340
let ( tx, rx) = mpsc:: channel ( ) ;
326
- OUT . with ( |val| val. replace ( Some ( tx. clone ( ) ) ) ) ;
327
341
328
342
let out_writer = builder
329
343
. out_writer
@@ -443,7 +457,6 @@ where
443
457
} ;
444
458
let layer = ChromeLayer {
445
459
out : Arc :: new ( Mutex :: new ( tx) ) ,
446
- start : std:: time:: Instant :: now ( ) ,
447
460
max_tid : AtomicUsize :: new ( 0 ) ,
448
461
name_fn : builder. name_fn . take ( ) ,
449
462
cat_fn : builder. cat_fn . take ( ) ,
@@ -456,22 +469,7 @@ where
456
469
( layer, guard)
457
470
}
458
471
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 {
475
473
let name = self . name_fn . as_ref ( ) . map ( |name_fn| name_fn ( & data) ) ;
476
474
let target = self . cat_fn . as_ref ( ) . map ( |cat_fn| cat_fn ( & data) ) ;
477
475
let meta = match data {
@@ -502,14 +500,6 @@ where
502
500
( None , None )
503
501
} ;
504
502
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
-
513
503
Callsite {
514
504
tid,
515
505
name,
@@ -548,31 +538,55 @@ where
548
538
}
549
539
}
550
540
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 ) ;
553
543
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) ) ;
555
545
}
556
546
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 ) ;
559
549
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) ) ;
561
551
}
562
552
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
+ } ;
566
579
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
+ } ) ;
576
590
} ) ;
577
591
}
578
592
}
@@ -586,61 +600,68 @@ where
586
600
return ;
587
601
}
588
602
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
+ } ) ;
591
606
}
592
607
593
608
fn on_record ( & self , id : & span:: Id , values : & span:: Record < ' _ > , ctx : Context < ' _ , S > ) {
594
609
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 ( ) ;
597
613
598
- let args = exts. get_mut :: < ArgsWrapper > ( ) ;
614
+ let args = exts. get_mut :: < ArgsWrapper > ( ) ;
599
615
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
+ } ) ;
604
621
}
605
622
}
606
623
607
624
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
+ } ) ;
611
629
}
612
630
613
631
fn on_exit ( & self , id : & span:: Id , ctx : Context < ' _ , S > ) {
614
632
if let TraceStyle :: Async = self . trace_style {
615
633
return ;
616
634
}
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
+ } ) ;
619
638
}
620
639
621
640
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
+ }
632
652
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
+ } ) ;
635
655
}
636
656
637
657
fn on_close ( & self , id : span:: Id , ctx : Context < ' _ , S > ) {
638
658
if let TraceStyle :: Threaded = self . trace_style {
639
659
return ;
640
660
}
641
661
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
+ } ) ;
644
665
}
645
666
}
646
667
0 commit comments