luaur_repl_cli/functions/
profiler_trigger.rs1use alloc::collections::BTreeMap;
2use alloc::string::String;
3use core::ffi::CStr;
4use core::sync::atomic::{AtomicBool, AtomicU64, Ordering};
5use std::thread::JoinHandle;
6
7use luaur_vm::functions::lua_callbacks::lua_callbacks;
8use luaur_vm::functions::lua_getinfo::lua_getinfo;
9use luaur_vm::records::lua_callbacks::LuaCallbacks;
10use luaur_vm::records::lua_debug::LuaDebug;
11use luaur_vm::records::lua_state::lua_State;
12
13pub(crate) struct ProfilerTriggerState {
21 pub(crate) callbacks: *mut LuaCallbacks,
23 pub(crate) frequency: i32,
24 pub(crate) thread: Option<JoinHandle<()>>,
25 pub(crate) exit: AtomicBool,
27 pub(crate) ticks: AtomicU64,
28 pub(crate) samples: AtomicU64,
29 pub(crate) current_ticks: u64,
31 pub(crate) stack_scratch: String,
32 pub(crate) data: Option<BTreeMap<String, u64>>,
33 pub(crate) gc: [u64; 16],
34}
35
36pub(crate) static mut G_PROFILER: ProfilerTriggerState = ProfilerTriggerState {
37 callbacks: core::ptr::null_mut(),
38 frequency: 1000,
39 thread: None,
40 exit: AtomicBool::new(false),
41 ticks: AtomicU64::new(0),
42 samples: AtomicU64::new(0),
43 current_ticks: 0,
44 stack_scratch: String::new(),
45 data: None,
46 gc: [0; 16],
47};
48
49pub unsafe fn profiler_trigger(l: *mut lua_State, gc: i32) {
51 let profiler = core::ptr::addr_of_mut!(G_PROFILER).as_mut().unwrap();
52
53 let current_ticks = profiler.ticks.load(Ordering::Relaxed);
54 let elapsed_ticks = current_ticks - profiler.current_ticks;
55
56 if elapsed_ticks != 0 {
57 let stack = &mut profiler.stack_scratch;
58 stack.clear();
59
60 if gc > 0 {
61 stack.push_str("GC,GC,");
62 }
63
64 let mut ar: LuaDebug = core::mem::zeroed();
65 let mut level = 0;
66 while lua_getinfo(l, level, c"sn".as_ptr(), &mut ar as *mut LuaDebug) != 0 {
67 if !stack.is_empty() {
68 stack.push(';');
69 }
70
71 if !ar.short_src.is_null() {
72 stack.push_str(&CStr::from_ptr(ar.short_src).to_string_lossy());
73 }
74 stack.push(',');
75 if !ar.name.is_null() {
76 stack.push_str(&CStr::from_ptr(ar.name).to_string_lossy());
77 }
78 stack.push(',');
79 if ar.linedefined > 0 {
80 use core::fmt::Write;
81 let _ = write!(stack, "{}", ar.linedefined);
82 }
83
84 level += 1;
85 }
86
87 if !stack.is_empty() {
88 let key = stack.clone();
89 let data = profiler.data.get_or_insert_with(BTreeMap::new);
90 *data.entry(key).or_insert(0) += elapsed_ticks;
91 }
92
93 if gc > 0 {
94 profiler.gc[gc as usize] += elapsed_ticks;
95 }
96 }
97
98 profiler.current_ticks = current_ticks;
99
100 if !profiler.callbacks.is_null() {
101 (*profiler.callbacks).interrupt = None;
102 } else {
103 (*lua_callbacks(l)).interrupt = None;
107 }
108}