Skip to main content

veilid_tools/
debugging.rs

1#![cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
2
3use super::*;
4
5/// Debugger attachment state of the current process.
6#[derive(Copy, Clone, Debug, PartialEq, Eq)]
7pub enum DebuggerState {
8    /// No debugger is attached
9    Detached,
10    /// A debugger is attached
11    Attached,
12    /// Attachment cannot be determined on this platform
13    Unknown,
14}
15
16/// Determine whether a debugger is attached to the current process.
17#[must_use]
18#[allow(unreachable_code)]
19pub fn debugger_state() -> DebuggerState {
20    #[cfg(windows)]
21    unsafe {
22        return if winapi::um::debugapi::IsDebuggerPresent() == 0 {
23            DebuggerState::Detached
24        } else {
25            DebuggerState::Attached
26        };
27    }
28
29    #[cfg(any(target_os = "ios", target_os = "macos"))]
30    {
31        use libc::{c_int, c_void, getpid, size_t, sysctl, CTL_KERN, KERN_PROC, KERN_PROC_PID};
32        // kinfo_proc.kp_proc.p_flag & P_TRACED
33        const P_TRACED: u32 = 0x00800;
34        unsafe {
35            let mut info = [0u8; 648];
36            let mut size: size_t = info.len();
37            let mut mib: [c_int; 4] = [CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()];
38            let ret = sysctl(
39                mib.as_mut_ptr(),
40                4,
41                info.as_mut_ptr() as *mut c_void,
42                &mut size as *mut size_t,
43                core::ptr::null_mut(),
44                0,
45            );
46            if ret == 0 {
47                return if (u32::from_le_bytes(info[32..36].try_into().unwrap()) & P_TRACED) != 0 {
48                    DebuggerState::Attached
49                } else {
50                    DebuggerState::Detached
51                };
52            }
53        }
54    }
55
56    // TracerPid is nonzero when a debugger is attached; works on linux, android, some BSDs
57    #[cfg(unix)]
58    {
59        if let Ok(file) = std::fs::File::open("/proc/self/status") {
60            let file = std::io::BufReader::new(file);
61            use std::io::BufRead;
62            for line in file.lines().map_while(Result::ok) {
63                let line = line.trim();
64                if line.starts_with("TracerPid:") {
65                    return match [" 0", "\t0", ":0"].iter().any(|zero| line.ends_with(zero)) {
66                        true => DebuggerState::Detached,
67                        false => DebuggerState::Attached,
68                    };
69                }
70            }
71        }
72    }
73
74    DebuggerState::Unknown
75}
76
77/// Block until a debugger attaches, or until the optional timeout elapses.
78pub fn wait_until_debugger_attached<T: Into<Option<Duration>>>(
79    timeout: T,
80) -> Result<(), &'static str> {
81    let timeout = timeout.into().map(|dur| std::time::Instant::now() + dur);
82    loop {
83        match debugger_state() {
84            DebuggerState::Attached => return Ok(()),
85            DebuggerState::Unknown => {
86                return Err("debugger state is unknown, cannot wait for attach")
87            }
88            DebuggerState::Detached => {
89                if let Some(timeout) = timeout {
90                    if std::time::Instant::now() >= timeout {
91                        return Err("wait for debugger attach timed out");
92                    }
93                }
94                std::thread::sleep(Duration::from_millis(16));
95            }
96        }
97    }
98}