1use std::cell::Cell;
9use std::sync::Arc;
10
11#[doc(hidden)]
13#[derive(Debug, Clone)]
14pub struct TraceMarker {
15 pub(crate) ips: Arc<[usize]>,
16}
17
18pub trait FrameLike {
20 fn ip(&self) -> usize;
22}
23
24impl FrameLike for backtrace::BacktraceFrame {
25 #[inline]
26 fn ip(&self) -> usize {
27 self.ip() as usize
28 }
29}
30
31impl FrameLike for backtrace::Frame {
32 #[inline]
33 fn ip(&self) -> usize {
34 self.ip() as usize
35 }
36}
37
38impl TraceMarker {
39 pub(crate) fn cut_len(&self, trace: &[impl FrameLike]) -> usize {
42 let m = &self.ips;
43 let mut k = 0;
44 while k < trace.len()
45 && k < m.len()
46 && trace[trace.len() - 1 - k].ip() == m[m.len() - 1 - k]
47 {
48 k += 1;
49 }
50 k
51 }
52}
53
54thread_local! {
55 static MARKER: Cell<Option<TraceMarker>> = const { Cell::new(None) };
56}
57
58fn capture_marker() -> TraceMarker {
59 let mut ips = Vec::with_capacity(32);
60 backtrace::trace(|frame| {
61 ips.push(FrameLike::ip(frame));
62 true
63 });
64 TraceMarker { ips: ips.into() }
65}
66
67pub fn current() -> Option<TraceMarker> {
71 MARKER
72 .try_with(|slot| {
73 let cur = slot.take();
74 let snapshot = cur.clone();
75 slot.set(cur);
76 snapshot
77 })
78 .ok()
79 .flatten()
80}
81
82#[doc(hidden)]
85#[must_use]
86pub fn set_marker() -> Option<TraceMarker> {
87 let data = capture_marker();
88 MARKER
89 .try_with(|slot| {
90 let prev = slot.take();
91 slot.set(Some(data));
92 prev
93 })
94 .ok()
95 .flatten()
96}
97
98#[doc(hidden)]
99pub fn restore_marker(prev: Option<TraceMarker>) {
100 let _ = MARKER.try_with(|slot| slot.set(prev));
101}
102
103#[macro_export]
111macro_rules! start_marker {
112 () => {{
113 let _ = $crate::__private::set_marker();
114 }};
115}
116
117#[cfg(test)]
118mod tests {
119 use super::*;
120
121 impl FrameLike for usize {
122 fn ip(&self) -> usize {
123 *self
124 }
125 }
126
127 fn ips(values: &[usize]) -> Arc<[usize]> {
128 values.into()
129 }
130
131 #[test]
132 fn capture_records_frames() {
133 let marker = capture_marker();
134 assert!(!marker.ips.is_empty());
135 }
136
137 #[test]
138 fn set_marker_is_latest_wins() {
139 let _ = set_marker();
140 let first = current().expect("marker set");
141 let _ = set_marker();
142 let second = current().expect("marker set");
143 assert!(!Arc::ptr_eq(&first.ips, &second.ips));
144 }
145
146 #[test]
147 fn current_returns_clone_and_leaves_slot_intact() {
148 let _ = set_marker();
149 let a = current().expect("marker set");
150 let b = current().expect("still set");
151 assert!(Arc::ptr_eq(&a.ips, &b.ips));
152 }
153
154 #[test]
155 fn set_marker_returns_previous_and_restore_reinstates_it() {
156 let _ = set_marker();
157 let user_marker = current().expect("marker set");
158 let prev = set_marker();
159 let replacement = current().expect("replacement marker set");
160 assert!(!Arc::ptr_eq(&user_marker.ips, &replacement.ips));
161 restore_marker(prev);
162 let restored = current().expect("previous marker restored");
163 assert!(Arc::ptr_eq(&user_marker.ips, &restored.ips));
164 }
165
166 #[test]
167 fn cut_len_exact_suffix() {
168 let marker = TraceMarker {
169 ips: ips(&[900, 10, 20, 30]),
170 };
171 let trace = ips(&[800, 11, 20, 30]);
172 assert_eq!(marker.cut_len(&trace), 2);
173 }
174
175 #[test]
176 fn cut_len_disjoint_stacks_is_zero() {
177 let marker = TraceMarker {
178 ips: ips(&[10, 20]),
179 };
180 assert_eq!(marker.cut_len(&ips(&[77, 88])), 0);
181 }
182
183 #[test]
184 fn cut_len_empty_marker_is_zero() {
185 let marker = TraceMarker { ips: ips(&[]) };
186 assert_eq!(marker.cut_len(&ips(&[1])), 0);
187 }
188
189 #[test]
190 fn cut_len_empty_trace_is_zero() {
191 let marker = TraceMarker {
192 ips: ips(&[10, 20]),
193 };
194 assert_eq!(marker.cut_len(&ips(&[])), 0);
195 }
196
197 #[test]
198 fn cut_len_trace_fully_contained_is_capped() {
199 let marker = TraceMarker {
200 ips: ips(&[10, 20, 30]),
201 };
202 let trace = ips(&[20, 30]);
203 assert_eq!(marker.cut_len(&trace), 2);
204 }
205}