Skip to main content

thread_db/
ffi.rs

1#![allow(clippy::too_many_arguments)]
2
3use dlopen::wrapper::WrapperApi;
4use dlopen_derive::WrapperApi;
5use nix::libc;
6use nix::unistd::Pid;
7use object::{Object, ObjectSymbol};
8use std::collections::HashMap;
9use std::path::Path;
10use std::{fs, io};
11
12#[derive(Copy, Clone)]
13pub struct TdThrHandle {
14    _th_ta_p: *mut TdThrAgent,
15    _th_unique: *mut PsAddr,
16}
17
18#[derive(Debug)]
19#[repr(C)]
20pub enum TdThrState {
21    AnyState,
22    Unknown,
23    Stopped,
24    Run,
25    Active,
26    Zombie,
27    Sleep,
28    StoppedAsleep,
29}
30
31#[derive(Debug)]
32#[repr(C)]
33pub enum TdErr {
34    /// No error.
35    Ok,
36    /// No further specified error.
37    Err,
38    /// No matching thread found.
39    NoThr,
40    /// No matching synchronization handle found.
41    NoSv,
42    /// No matching light-weighted process found.
43    NoLWP,
44    /// Invalid process handle.
45    BadPH,
46    /// Invalid thread handle.
47    BadTH,
48    /// Invalid synchronization handle.
49    BadSH,
50    /// Invalid thread agent.
51    BadTA,
52    /// Invalid key.
53    BadKEY,
54    /// No event available.
55    NoMsg,
56    /// No floating-point register content available.
57    NoFPRegs,
58    /// Application not linked with thread library.
59    NoLibthread,
60    /// Requested event is not supported.
61    NoEvent,
62    /// Capability not available.
63    NoCapab,
64    /// Internal debug library error.
65    DbErr,
66    /// Operation is not applicable.
67    NoAplic,
68    /// No thread-specific data available.
69    NoTSD,
70    /// Out of memory.
71    Malloc,
72    /// Not entire register set was read or written.
73    PartialReg,
74    /// X register set not available for given thread.
75    NoXregs,
76    /// Thread has not yet allocated TLS for given module.
77    TLSDefer,
78    NoTalloc,
79    /// Version if libpthread and libthread_db do not match.
80    Version,
81    /// There is no TLS segment in the given module.
82    NoTLS,
83}
84
85pub type TdThrAgent = libc::c_void;
86pub type PsAddr = libc::c_void;
87
88/// Type of the thread (system vs user thread).
89#[allow(dead_code)]
90#[derive(Debug)]
91#[repr(C)]
92pub enum TdThrType {
93    AnyType,
94    User,
95    System,
96}
97
98///Bitmask of enabled events.
99#[derive(Debug)]
100#[repr(C)]
101pub struct TdThrEvents {
102    event_bits: [u32; 2],
103}
104
105/// Gathered statistics about the process.
106#[repr(C)]
107#[derive(Debug)]
108pub struct TdTaStats {
109    /// Total number of threads in use.
110    pub nthreads: i32,
111    /// Concurrency level requested by user.
112    pub r_concurrency: i32,
113    /// Average runnable threads, numerator.
114    pub nrunnable_num: i32,
115    /// Average runnable threads, denominator.
116    pub nrunnable_den: i32,
117    /// Achieved concurrency level, numerator.
118    pub a_concurrency_num: i32,
119    /// Achieved concurrency level, denominator.
120    pub a_concurrency_den: i32,
121    /// Average number of processes in use, numerator.
122    pub nlwps_num: i32,
123    /// Average number of processes in use, denominator.
124    pub nlwps_den: i32,
125    /// Average number of idling processes, numerator.
126    pub nidle_num: i32,
127    /// Average number of idling processes, denominator.
128    pub nidle_den: i32,
129}
130
131/// Information about the thread.
132#[repr(C)]
133#[derive(Debug)]
134pub struct TdThrInfo {
135    /// Process handle.
136    ti_ta_p: *mut TdThrAgent,
137    /// Unused.
138    ti_user_flags: libc::c_uint,
139    /// Thread ID returned by pthread_create().
140    pub ti_tid: libc::pthread_t,
141    /// Pointer to thread-local data.
142    pub ti_tls: *mut u8,
143    /// Start function passed to pthread_create().
144    pub ti_startfunc: *mut PsAddr,
145    /// Base of thread's stack.
146    pub ti_stkbase: *mut PsAddr,
147    /// Size of thread's stack.
148    pub ti_stksize: libc::c_long,
149    /// Unused.
150    ti_ro_area: *mut PsAddr,
151    /// Unused.
152    ti_ro_size: libc::c_int,
153    /// Thread state.
154    pub ti_state: TdThrState,
155    /// Nonzero if suspended by debugger
156    pub ti_db_suspended: libc::c_uchar,
157    /// Type of the thread (system vs user thread).
158    pub ti_type: TdThrType,
159    /// Unused.
160    ti_pc: libc::intptr_t,
161    /// Unused.
162    ti_sp: libc::intptr_t,
163    /// Unused.
164    ti_flags: libc::c_ushort,
165    /// Thread priority.
166    pub ti_pri: libc::c_int,
167    /// Kernel PID for this thread.
168    pub ti_lid: libc::pid_t,
169    /// Signal mask.
170    pub ti_sigmask: libc::sigset_t,
171    /// Nonzero if event reporting enabled.
172    pub ti_traceme: libc::c_uchar,
173    /// Unused.
174    ti_preemptflag: libc::c_uchar,
175    /// Unused.
176    ti_pirecflag: libc::c_uchar,
177    /// Set of pending signals.
178    pub ti_pending: libc::sigset_t,
179    /// Set of enabled events.
180    pub ti_events: TdThrEvents,
181}
182
183type SymbolMapping = HashMap<String, HashMap<String, usize>>;
184
185pub struct ProcHandle {
186    pub pid: Pid,
187    pub symbols: SymbolMapping,
188}
189
190impl ProcHandle {
191    pub fn new(pid: Pid) -> io::Result<ProcHandle> {
192        Ok(ProcHandle {
193            pid,
194            symbols: Self::extract_symbols(pid)?,
195        })
196    }
197
198    fn extract_symbols(pid: Pid) -> io::Result<SymbolMapping> {
199        proc_maps::get_process_maps(pid.as_raw())?
200            .into_iter()
201            .filter(|map| map.offset == 0)
202            .filter_map(|map| map.filename().map(|path| (path.to_path_buf(), map.start())))
203            .filter(|(path, _)| path.starts_with("/"))
204            .map(
205                |(path, start)| -> Result<(String, HashMap<String, usize>), io::Error> {
206                    let k = path.to_str().unwrap_or_default().to_owned();
207                    let v = extract_symbols_from_lib(&path, start)?;
208                    Ok((k, v))
209                },
210            )
211            .collect()
212    }
213}
214
215fn extract_symbols_from_lib(
216    lib_path: &Path,
217    map_addr: usize,
218) -> io::Result<HashMap<String, usize>> {
219    let mut symbols = HashMap::new();
220
221    let bin_data = fs::read(lib_path)?;
222    let binary = object::File::parse(&*bin_data)
223        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
224
225    for sym in binary.symbols() {
226        if let Ok(name) = sym.name() {
227            symbols.insert(name.to_string(), sym.address() as usize + map_addr);
228        }
229    }
230    for sym in binary.dynamic_symbols() {
231        if let Ok(name) = sym.name() {
232            symbols.insert(name.to_string(), sym.address() as usize + map_addr);
233        }
234    }
235    Ok(symbols)
236}
237
238#[derive(WrapperApi)]
239pub struct ThreadDb {
240    /// Initialize the thread debug support library.
241    td_init: unsafe extern "C" fn() -> TdErr,
242    /// Generate new thread debug library handle for process PS.
243    td_ta_new: unsafe extern "C" fn(ps: *mut ProcHandle, ta: *mut *mut TdThrAgent) -> TdErr,
244    /// Free resources allocated for TA.
245    td_ta_delete: unsafe extern "C" fn(ta: *mut TdThrAgent) -> TdErr,
246
247    /// Get number of currently running threads in process associated with TA.
248    td_ta_get_nthreads: unsafe extern "C" fn(ta: *const TdThrAgent, np: *mut i32) -> TdErr,
249
250    /// Enable collecting statistics for process associated with TA.
251    td_ta_enable_stats: unsafe extern "C" fn(ta: *mut TdThrAgent, enable: i32) -> TdErr,
252    /// Reset statistics.
253    td_ta_reset_stats: unsafe extern "C" fn(ta: *mut TdThrAgent) -> TdErr,
254    /// Retrieve statistics from process associated with TA.
255    td_ta_get_stats: unsafe extern "C" fn(ta: *const TdThrAgent, stats: *mut TdTaStats) -> TdErr,
256
257    /// Call for each thread in a process associated with TA the callback function CALLBACK.
258    td_ta_thr_iter: unsafe extern "C" fn(
259        ta: *mut TdThrAgent,
260        callback: unsafe extern "C" fn(handle: *const TdThrHandle, cbdata: *mut libc::c_void) -> i32,
261        cbdata: *mut libc::c_void,
262        state: TdThrState,
263        pri: i32,
264        ti_sigmask: *mut libc::sigset_t,
265        ti_user_flags: u32,
266    ) -> TdErr,
267
268    /// Validate that TH is a thread handle.
269    td_thr_validate: unsafe extern "C" fn(handle: *const TdThrHandle) -> TdErr,
270
271    /// Map process ID LWPID to thread debug library handle for process associated with TA and store result in *TH.
272    td_ta_map_lwp2thr: unsafe extern "C" fn(
273        ta: *const TdThrAgent,
274        pid: libc::pid_t,
275        handle: *mut TdThrHandle,
276    ) -> TdErr,
277
278    /// Return information about thread TH.
279    td_thr_get_info:
280        unsafe extern "C" fn(handle: *const TdThrHandle, info: *mut TdThrInfo) -> TdErr,
281
282    /// Get address of the given module's TLS storage area for the given thread.
283    td_thr_tlsbase: unsafe extern "C" fn(
284        handle: *const TdThrHandle,
285        modid: u32,
286        base: *mut *mut libc::c_void,
287    ) -> TdErr,
288
289    /// Get address of thread local variable.
290    td_thr_tls_get_addr: unsafe extern "C" fn(
291        handle: *const TdThrHandle,
292        map_address: *const libc::c_void,
293        offset: libc::size_t,
294        addr: *mut *mut libc::c_void,
295    ) -> TdErr,
296}