thruput_sdk/diagnostics/
debug_context.rs1use core::any::Any;
2use core::fmt::{self, Display, Write};
3use core::panic::Location;
4use core::ptr::NonNull;
5use heapless::String;
6
7pub trait Context: Any + Display {}
8
9impl<T> Context for T where T: Any + Display {}
10
11struct Frame {
12 context: NonNull<dyn Context>,
13 location: LocString,
14 #[cfg_attr(not(feature = "std"), allow(dead_code))]
17 next: Option<NonNull<Frame>>,
18}
19
20type LocString = String<100>;
21
22#[cfg(any(doc, target_arch = "riscv32"))]
26static FRAMES: crate::ContractCell<Option<NonNull<Frame>>> =
27 crate::ContractCell::new(None);
28
29#[cfg(not(any(doc, target_arch = "riscv32")))]
30std::thread_local! {
31 static FRAMES: core::cell::Cell<Option<NonNull<Frame>>> =
32 const { core::cell::Cell::new(None) };
33}
34
35#[track_caller]
37pub fn with_debug_context<F, T>(context: &(impl Context + 'static), f: F) -> T
38where
39 F: FnOnce() -> T,
40{
41 let location_str = write_location(Location::caller());
42
43 let frames = FRAMES.get();
44
45 let frame = Frame {
46 context: NonNull::from(context),
47 location: location_str,
48 next: frames,
49 };
50
51 FRAMES.set(Some(NonNull::from(&frame)));
52
53 let _guard = FramesGuard { prev: frames };
54
55 f()
56}
57
58fn write_location(loc: &Location) -> LocString {
62 let mut out = LocString::new();
63 let mut rem = out.capacity();
64
65 let mut line_col = String::<16>::new();
66 write!(line_col, ":{}:{}", loc.line(), loc.column()).unwrap();
67 rem -= line_col.len();
68
69 let file = loc.file();
70 if file.len() > rem {
71 let elipsis = "...";
73 out.write_str(elipsis).unwrap();
74 rem -= elipsis.len();
75 out.write_str(&file[file.len() - rem..]).unwrap();
76 } else {
77 out.write_str(file).unwrap();
78 }
79
80 out.write_str(line_col.as_str()).unwrap();
81
82 out
83}
84
85struct FramesGuard {
86 prev: Option<NonNull<Frame>>,
87}
88
89impl Drop for FramesGuard {
90 fn drop(&mut self) {
91 FRAMES.set(self.prev);
92 }
93}
94
95impl Display for Frame {
96 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
97 let context = unsafe { self.context.as_ref() };
101 write!(f, "{} at {}", context, self.location)
102 }
103}
104
105#[cfg_attr(not(feature = "std"), allow(dead_code))]
114pub(crate) struct Backtrace {
115 top: Option<NonNull<Frame>>,
116}
117
118impl Display for Backtrace {
119 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
120 let mut frames = self.top;
121 let mut depth = 1; while let Some(frame) = frames {
124 let frame = unsafe { frame.as_ref() };
128
129 writeln!(f, " {}: {}", depth, frame)?;
130
131 frames = frame.next;
132 depth += 1;
133 }
134
135 Ok(())
136 }
137}
138
139#[cfg_attr(not(feature = "std"), allow(dead_code))]
140impl Backtrace {
141 pub(crate) unsafe fn capture() -> Self {
151 Backtrace { top: FRAMES.get() }
152 }
153}