tracing_coz/
lib.rs

1//! Bridge between the `coz` Causal Profiler and tracing
2//!
3//! This crate takes advantage of places where tracing exposes tracepoints rust code, letting coz
4//! see these tracepoints as latency for spans and throughput for events.
5//!
6//! For usage information, consult the [`README.md` for the `coz`
7//! repository][coz-readme] as well as the [`README.md` for
8//! `coz-rs`][rust-readme].
9//!
10//! [coz-readme]: https://github.com/plasma-umass/coz/blob/master/README.md
11//! [rust-readme]: https://github.com/alexcrichton/coz-rs/blob/master/README.md
12
13// Reexport this for the times its needed
14pub use coz::thread_init;
15use coz::Counter;
16
17use std::sync::atomic::{Ordering, AtomicUsize};
18
19use tracing::{
20    Id, Metadata, Event,
21    span,
22    subscriber::{self, Subscriber},
23};
24
25use chashmap::CHashMap;
26
27struct ThroughputCounter {
28    start: Counter,
29    end: Counter,
30}
31
32impl ThroughputCounter {
33    fn new(name: &'static str) -> Self {
34        ThroughputCounter {
35            start: Counter::begin(name),
36            end: Counter::end(name),
37        }
38    }
39}
40
41/// A bridge between the coz-profiler and tracing
42pub struct TracingCozBridge {
43    next_id: AtomicUsize,
44    latency_counters: CHashMap<&'static str, Counter>,
45    throughput_counters: CHashMap<Id, ThroughputCounter>,
46    idents: CHashMap<&'static str, Id>,
47}
48
49impl TracingCozBridge {
50    /// Creates a bridge between tracing and the coz profiler
51    pub fn new() -> Self {
52        TracingCozBridge {
53            next_id: AtomicUsize::new(1),
54            latency_counters: CHashMap::new(),
55            throughput_counters: CHashMap::new(),
56            idents: CHashMap::new(),
57        }
58    }
59
60    fn next_id(&self) -> Id {
61        Id::from_u64(self.next_id.fetch_add(1, Ordering::SeqCst) as u64)
62    }
63}
64
65impl Subscriber for TracingCozBridge {
66    fn register_callsite(&self, _meta: &Metadata<'_>) -> subscriber::Interest {
67        subscriber::Interest::always()
68    }
69
70    fn new_span(&self, new_span: &span::Attributes<'_>) -> Id {
71        let name = new_span.metadata().name();
72        let throughput_counters = &self.throughput_counters;
73        self.idents.upsert(name, || {
74            let next_id = self.next_id();
75            throughput_counters.upsert(next_id.clone(), || ThroughputCounter::new(name), |_| ());
76            next_id
77        }, |_| ());
78
79        (*self.idents.get(name).unwrap()).clone()
80    }
81
82    fn record_follows_from(&self, _span: &Id, _follows: &Id) {
83        // ignored
84    }
85
86    fn record(&self, _: &Id, _values: &span::Record<'_>) {
87        // ignored
88    }
89
90    fn event(&self, event: &Event<'_>) {
91        let name = event.metadata().name();
92        self.latency_counters.upsert(name, || Counter::progress(name), |cnt| cnt.increment());
93    }
94
95    fn enabled(&self, _metadata: &Metadata<'_>) -> bool {
96        true
97    }
98
99    fn enter(&self, span: &Id) {
100        self.throughput_counters.get(span).unwrap().start.increment()
101    }
102
103    fn exit(&self, span: &Id) {
104        self.throughput_counters.get(span).unwrap().end.increment()
105    }
106}