Skip to main content

probex_common/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2
3/// Event types for kernel-userspace communication
4#[repr(u8)]
5#[derive(Clone, Copy, Debug, PartialEq, Eq)]
6pub enum EventType {
7    SchedSwitch = 0,
8    ProcessFork = 1,
9    ProcessExit = 2,
10    PageFault = 3,
11    SyscallReadEnter = 4,
12    SyscallReadExit = 5,
13    SyscallWriteEnter = 6,
14    SyscallWriteExit = 7,
15    SyscallMmapEnter = 8,
16    SyscallMmapExit = 9,
17    SyscallMunmapEnter = 10,
18    SyscallMunmapExit = 11,
19    SyscallBrkEnter = 12,
20    SyscallBrkExit = 13,
21    SyscallIoUringSetupEnter = 14,
22    SyscallIoUringSetupExit = 15,
23    SyscallIoUringEnterEnter = 16,
24    SyscallIoUringEnterExit = 17,
25    SyscallIoUringRegisterEnter = 18,
26    SyscallIoUringRegisterExit = 19,
27    CpuSample = 20,
28    SyscallFsyncEnter = 21,
29    SyscallFsyncExit = 22,
30    SyscallFdatasyncEnter = 23,
31    SyscallFdatasyncExit = 24,
32    IoUringComplete = 25,
33}
34
35impl TryFrom<u8> for EventType {
36    type Error = u8;
37
38    fn try_from(value: u8) -> Result<Self, Self::Error> {
39        match value {
40            0 => Ok(EventType::SchedSwitch),
41            1 => Ok(EventType::ProcessFork),
42            2 => Ok(EventType::ProcessExit),
43            3 => Ok(EventType::PageFault),
44            4 => Ok(EventType::SyscallReadEnter),
45            5 => Ok(EventType::SyscallReadExit),
46            6 => Ok(EventType::SyscallWriteEnter),
47            7 => Ok(EventType::SyscallWriteExit),
48            8 => Ok(EventType::SyscallMmapEnter),
49            9 => Ok(EventType::SyscallMmapExit),
50            10 => Ok(EventType::SyscallMunmapEnter),
51            11 => Ok(EventType::SyscallMunmapExit),
52            12 => Ok(EventType::SyscallBrkEnter),
53            13 => Ok(EventType::SyscallBrkExit),
54            14 => Ok(EventType::SyscallIoUringSetupEnter),
55            15 => Ok(EventType::SyscallIoUringSetupExit),
56            16 => Ok(EventType::SyscallIoUringEnterEnter),
57            17 => Ok(EventType::SyscallIoUringEnterExit),
58            18 => Ok(EventType::SyscallIoUringRegisterEnter),
59            19 => Ok(EventType::SyscallIoUringRegisterExit),
60            20 => Ok(EventType::CpuSample),
61            21 => Ok(EventType::SyscallFsyncEnter),
62            22 => Ok(EventType::SyscallFsyncExit),
63            23 => Ok(EventType::SyscallFdatasyncEnter),
64            24 => Ok(EventType::SyscallFdatasyncExit),
65            25 => Ok(EventType::IoUringComplete),
66            v => Err(v),
67        }
68    }
69}
70
71/// Common header for all events
72#[repr(C)]
73#[derive(Clone, Copy, Debug)]
74pub struct EventHeader {
75    pub timestamp_ns: u64,
76    pub pid: u32,
77    pub tgid: u32,
78    /// User-space stack id from bpf_get_stackid(BPF_F_USER_STACK), or -1.
79    pub stack_id: i32,
80    /// Kernel-space stack id from bpf_get_stackid(0), or -1.
81    pub kernel_stack_id: i32,
82    pub stack_kind: u8,
83    pub event_type: u8,
84    pub cpu: u8,
85    pub _padding: [u8; 5],
86}
87
88pub const STACK_KIND_NONE: u8 = 0;
89pub const STACK_KIND_USER: u8 = 1;
90pub const STACK_KIND_KERNEL: u8 = 2;
91pub const STACK_KIND_BOTH: u8 = STACK_KIND_USER | STACK_KIND_KERNEL;
92
93/// Maximum number of frame-pointer-derived user frames emitted in each cpu sample event.
94pub const MAX_CPU_SAMPLE_FRAMES: usize = 127;
95
96// CPU sampler stats indices (per-CPU array slot 0).
97pub const CPU_SAMPLE_STATS_LEN: usize = 7;
98pub const CPU_SAMPLE_STAT_CALLBACK_TOTAL: usize = 0;
99pub const CPU_SAMPLE_STAT_FILTERED_NOT_TRACED: usize = 1;
100pub const CPU_SAMPLE_STAT_EMITTED: usize = 2;
101pub const CPU_SAMPLE_STAT_RINGBUF_DROPPED: usize = 3;
102pub const CPU_SAMPLE_STAT_USER_STACK: usize = 4;
103pub const CPU_SAMPLE_STAT_KERNEL_STACK: usize = 5;
104pub const CPU_SAMPLE_STAT_NO_STACK: usize = 6;
105
106/// CPU sample event carrying explicit user-space return addresses from frame-pointer walking.
107#[repr(C)]
108#[derive(Clone, Copy, Debug)]
109pub struct CpuSampleEvent {
110    pub header: EventHeader,
111    pub frame_count: u16,
112    pub _padding: [u8; 6],
113    pub frames: [u64; MAX_CPU_SAMPLE_FRAMES],
114}
115
116/// Context switch event
117#[repr(C)]
118#[derive(Clone, Copy, Debug)]
119pub struct SchedSwitchEvent {
120    pub header: EventHeader,
121    pub prev_pid: u32,
122    pub prev_tgid: u32,
123    pub next_pid: u32,
124    pub next_tgid: u32,
125    pub prev_state: i64,
126}
127
128/// Process fork event
129#[repr(C)]
130#[derive(Clone, Copy, Debug)]
131pub struct ProcessForkEvent {
132    pub header: EventHeader,
133    pub parent_pid: u32,
134    pub child_pid: u32,
135}
136
137/// Process exit event
138#[repr(C)]
139#[derive(Clone, Copy, Debug)]
140pub struct ProcessExitEvent {
141    pub header: EventHeader,
142    pub exit_code: i32,
143    pub _padding: u32,
144}
145
146/// Page fault event
147#[repr(C)]
148#[derive(Clone, Copy, Debug)]
149pub struct PageFaultEvent {
150    pub header: EventHeader,
151    pub address: u64,
152    pub error_code: u64,
153}
154
155/// Syscall enter event (for read/write)
156#[repr(C)]
157#[derive(Clone, Copy, Debug)]
158pub struct SyscallEnterEvent {
159    pub header: EventHeader,
160    pub fd: i64,
161    pub count: u64,
162}
163
164/// Syscall exit event (for read/write)
165#[repr(C)]
166#[derive(Clone, Copy, Debug)]
167pub struct SyscallExitEvent {
168    pub header: EventHeader,
169    pub ret: i64,
170}
171
172/// io_uring completion event — emitted when a CQE is posted.
173/// Latency = header.timestamp_ns - submit_ts_ns.
174#[repr(C)]
175#[derive(Clone, Copy, Debug)]
176pub struct IoUringCompleteEvent {
177    pub header: EventHeader,
178    /// Timestamp (ktime_get_ns) when the SQE was submitted via io_uring_submit_req.
179    pub submit_ts_ns: u64,
180    /// io_uring opcode (IORING_OP_*).
181    pub opcode: u8,
182    pub _padding: [u8; 3],
183    /// CQE result — bytes transferred for read/write, or negative errno.
184    pub res: i32,
185}
186
187// Constants for event sizes
188pub const SCHED_SWITCH_EVENT_SIZE: usize = core::mem::size_of::<SchedSwitchEvent>();
189pub const PROCESS_FORK_EVENT_SIZE: usize = core::mem::size_of::<ProcessForkEvent>();
190pub const PROCESS_EXIT_EVENT_SIZE: usize = core::mem::size_of::<ProcessExitEvent>();
191pub const PAGE_FAULT_EVENT_SIZE: usize = core::mem::size_of::<PageFaultEvent>();
192pub const SYSCALL_ENTER_EVENT_SIZE: usize = core::mem::size_of::<SyscallEnterEvent>();
193pub const SYSCALL_EXIT_EVENT_SIZE: usize = core::mem::size_of::<SyscallExitEvent>();
194pub const IO_URING_COMPLETE_EVENT_SIZE: usize = core::mem::size_of::<IoUringCompleteEvent>();
195pub const CPU_SAMPLE_EVENT_SIZE: usize = core::mem::size_of::<CpuSampleEvent>();
196
197// Ring buffer size
198pub const RING_BUF_SIZE: u32 = 64 * 1024 * 1024;
199
200// Maximum number of tracked PIDs
201pub const MAX_TRACKED_PIDS: u32 = 8192;
202
203// Maximum number of in-flight io_uring requests tracked for latency
204pub const MAX_IO_URING_INFLIGHT: u32 = 16384;
205
206#[cfg(feature = "viewer-api")]
207pub mod viewer_api {
208    use serde::{Deserialize, Serialize};
209    use std::collections::HashMap;
210
211    #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
212    pub struct HistogramBucket {
213        pub bucket_start_ns: u64,
214        pub bucket_end_ns: u64,
215        pub count: usize,
216        pub counts_by_type: HashMap<String, usize>,
217    }
218
219    #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
220    pub struct HistogramResponse {
221        pub buckets: Vec<HistogramBucket>,
222        pub total_in_range: usize,
223    }
224
225    #[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
226    pub struct EventTypeCounts {
227        pub counts: HashMap<String, usize>,
228    }
229
230    #[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
231    pub struct LatencySummary {
232        pub count: usize,
233        pub avg_ns: u64,
234        pub p50_ns: u64,
235        pub p95_ns: u64,
236        pub max_ns: u64,
237    }
238
239    #[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
240    pub struct SyscallLatencyStats {
241        pub read: LatencySummary,
242        pub write: LatencySummary,
243        pub io_uring: LatencySummary,
244        pub mmap_alloc_bytes: u64,
245        pub munmap_free_bytes: u64,
246    }
247
248    #[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
249    pub struct TraceSummary {
250        pub total_events: usize,
251        pub event_types: Vec<String>,
252        pub unique_pids: Vec<u32>,
253        pub min_ts_ns: u64,
254        pub max_ts_ns: u64,
255        pub cpu_sample_frequency_hz: u64,
256    }
257
258    #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
259    pub struct ProcessLifetime {
260        pub pid: u32,
261        pub process_name: Option<String>,
262        pub parent_pid: Option<u32>,
263        pub start_ns: u64,
264        pub end_ns: u64,
265        pub exit: Option<i32>,
266        pub was_forked: bool,
267    }
268
269    #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
270    pub struct ProcessLifetimesResponse {
271        pub processes: Vec<ProcessLifetime>,
272    }
273
274    #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
275    pub struct EventMarker {
276        pub ts_ns: u64,
277        pub event_type: String,
278    }
279
280    #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
281    pub struct ProcessEventsResponse {
282        pub events_by_pid: HashMap<u32, Vec<EventMarker>>,
283        pub cpu_sample_counts_by_pid: HashMap<u32, Vec<u16>>,
284        pub cpu_sample_bucket_count: usize,
285    }
286
287    #[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
288    pub struct EventFlamegraphResponse {
289        pub event_type: String,
290        pub total_samples: usize,
291        pub svg: Option<String>,
292    }
293
294    #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
295    pub struct IoTypeStats {
296        pub operation: String,
297        pub total_ops: u64,
298        pub total_bytes: u64,
299        pub avg_latency_ns: u64,
300        /// Representative events for percentiles
301        pub p50_event: Option<EventDetail>,
302        pub p95_event: Option<EventDetail>,
303        pub p99_event: Option<EventDetail>,
304        pub max_event: Option<EventDetail>,
305        /// Sorted raw latency values in nanoseconds.
306        pub latencies_ns: Vec<u64>,
307        /// Sorted raw size values in bytes.
308        pub sizes_bytes: Vec<u64>,
309    }
310
311    #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
312    pub struct IoStatistics {
313        pub by_operation: Vec<IoTypeStats>,
314        pub total_ops: u64,
315        pub total_bytes: u64,
316        pub time_range_ns: (u64, u64),
317    }
318
319    #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
320    pub struct CumulativeMemoryPoint {
321        pub ts_ns: u64,
322        pub cumulative_bytes: i64,
323    }
324
325    #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
326    pub struct MemoryStatistics {
327        pub by_operation: Vec<IoTypeStats>,
328        pub total_alloc_ops: u64,
329        pub total_alloc_bytes: u64,
330        pub total_free_ops: u64,
331        pub total_free_bytes: u64,
332        pub cumulative_usage: Vec<CumulativeMemoryPoint>,
333        pub time_range_ns: (u64, u64),
334    }
335
336    #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
337    pub struct EventDetail {
338        pub ts_ns: u64,
339        pub latency_ns: Option<u64>,
340        pub event_type: String,
341        pub pid: u32,
342        pub stack_trace: Option<Vec<String>>,
343    }
344
345    #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
346    pub struct EventListResponse {
347        pub events: Vec<EventDetail>,
348        pub total_in_range: usize,
349    }
350}