running_process_core/
rust_debug.rs1use std::cell::RefCell;
2use std::collections::HashMap;
3use std::fmt::Write as _;
4use std::sync::{Arc, Mutex, OnceLock};
5use std::thread;
6
7#[derive(Clone)]
8struct RustDebugFrame {
9 label: &'static str,
10 file: &'static str,
11 line: u32,
12}
13
14type ThreadStack = Arc<Mutex<Vec<RustDebugFrame>>>;
15
16fn registry() -> &'static Mutex<HashMap<String, ThreadStack>> {
17 static REGISTRY: OnceLock<Mutex<HashMap<String, ThreadStack>>> = OnceLock::new();
18 REGISTRY.get_or_init(|| Mutex::new(HashMap::new()))
19}
20
21fn thread_key() -> String {
22 let current = thread::current();
23 match current.name() {
24 Some(name) => format!("{name} ({:?})", current.id()),
25 None => format!("{:?}", current.id()),
26 }
27}
28
29fn thread_stack() -> ThreadStack {
30 thread_local! {
31 static STACK: RefCell<Option<ThreadStack>> = const { RefCell::new(None) };
32 }
33
34 STACK.with(|cell| {
35 let mut slot = cell.borrow_mut();
36 if let Some(existing) = slot.as_ref() {
37 return Arc::clone(existing);
38 }
39 let handle = Arc::new(Mutex::new(Vec::new()));
40 registry()
41 .lock()
42 .expect("rust debug registry mutex poisoned")
43 .insert(thread_key(), Arc::clone(&handle));
44 *slot = Some(Arc::clone(&handle));
45 handle
46 })
47}
48
49pub struct RustDebugScopeGuard {
50 stack: ThreadStack,
51}
52
53impl RustDebugScopeGuard {
54 pub fn enter(label: &'static str, file: &'static str, line: u32) -> Self {
55 let stack = thread_stack();
56 stack
57 .lock()
58 .expect("rust debug stack mutex poisoned")
59 .push(RustDebugFrame { label, file, line });
60 Self { stack }
61 }
62}
63
64impl Drop for RustDebugScopeGuard {
65 fn drop(&mut self) {
66 let _ = self
67 .stack
68 .lock()
69 .expect("rust debug stack mutex poisoned")
70 .pop();
71 }
72}
73
74pub fn render_rust_debug_traces() -> String {
75 let registry = registry()
76 .lock()
77 .expect("rust debug registry mutex poisoned");
78 let mut items: Vec<_> = registry.iter().collect();
79 items.sort_by(|left, right| left.0.cmp(right.0));
80
81 let mut rendered = String::new();
82 for (thread_name, stack) in items {
83 let stack = stack.lock().expect("rust debug stack mutex poisoned");
84 if stack.is_empty() {
85 continue;
86 }
87 let _ = writeln!(&mut rendered, "thread: {thread_name}");
88 for (index, frame) in stack.iter().enumerate() {
89 let _ = writeln!(
90 &mut rendered,
91 " {index}: {} ({}:{})",
92 frame.label, frame.file, frame.line
93 );
94 }
95 }
96 rendered
97}