Skip to main content

aperture_shared/types/
events.rs

1//! Event type definitions for profiling data
2//!
3//! These types represent the raw events collected by eBPF programs and
4//! processed by the agent.
5
6use serde::{Deserialize, Serialize};
7
8/// Timestamp in nanoseconds since boot
9pub type Timestamp = u64;
10
11/// Process ID
12pub type Pid = i32;
13
14/// Thread ID
15pub type Tid = i32;
16
17/// CPU core number
18pub type CpuId = u32;
19
20/// Stack trace represented as an array of instruction pointers
21pub type StackTrace = Vec<u64>;
22
23/// CPU profiling sample event
24#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct CpuSample {
26    /// Timestamp when the sample was taken
27    pub timestamp: Timestamp,
28
29    /// Process ID
30    pub pid: Pid,
31
32    /// Thread ID
33    pub tid: Tid,
34
35    /// CPU core where the sample was taken
36    pub cpu_id: CpuId,
37
38    /// User-space stack trace
39    pub user_stack: StackTrace,
40
41    /// Kernel-space stack trace
42    pub kernel_stack: StackTrace,
43
44    /// Process name (comm)
45    pub comm: String,
46
47    /// Pre-resolved symbol names for user_stack IPs (parallel array, same length)
48    #[serde(default)]
49    pub user_stack_symbols: Vec<Option<String>>,
50
51    /// Pre-resolved symbol names for kernel_stack IPs (parallel array, same length)
52    #[serde(default)]
53    pub kernel_stack_symbols: Vec<Option<String>>,
54}
55
56/// Lock contention event
57#[derive(Debug, Clone, Serialize, Deserialize)]
58pub struct LockEvent {
59    pub timestamp: Timestamp,
60    pub pid: Pid,
61    pub tid: Tid,
62    pub lock_addr: u64,
63    pub hold_time_ns: u64,
64    pub wait_time_ns: u64,
65    pub stack_trace: StackTrace,
66    pub comm: String,
67
68    /// Pre-resolved symbol names for stack_trace IPs (parallel array, same length)
69    #[serde(default)]
70    pub stack_symbols: Vec<Option<String>>,
71}
72
73/// Syscall event
74#[derive(Debug, Clone, Serialize, Deserialize)]
75pub struct SyscallEvent {
76    pub timestamp: Timestamp,
77    pub pid: Pid,
78    pub tid: Tid,
79    pub syscall_id: u32,
80    pub duration_ns: u64,
81    pub return_value: i64,
82    pub comm: String,
83}
84
85/// GPU kernel execution event
86#[derive(Debug, Clone, Serialize, Deserialize)]
87pub struct GpuKernelEvent {
88    pub timestamp: Timestamp,
89    pub pid: Pid,
90    pub kernel_name: String,
91    pub duration_ns: u64,
92    pub grid_size: (u32, u32, u32),
93    pub block_size: (u32, u32, u32),
94}
95
96/// Unified profiling event type
97#[derive(Debug, Clone, Serialize, Deserialize)]
98pub enum ProfileEvent {
99    CpuSample(CpuSample),
100    Lock(LockEvent),
101    Syscall(SyscallEvent),
102    GpuKernel(GpuKernelEvent),
103}
104
105impl ProfileEvent {
106    /// Get the timestamp of any event type
107    pub fn timestamp(&self) -> Timestamp {
108        match self {
109            ProfileEvent::CpuSample(e) => e.timestamp,
110            ProfileEvent::Lock(e) => e.timestamp,
111            ProfileEvent::Syscall(e) => e.timestamp,
112            ProfileEvent::GpuKernel(e) => e.timestamp,
113        }
114    }
115
116    /// Get the process ID of any event type
117    pub fn pid(&self) -> Pid {
118        match self {
119            ProfileEvent::CpuSample(e) => e.pid,
120            ProfileEvent::Lock(e) => e.pid,
121            ProfileEvent::Syscall(e) => e.pid,
122            ProfileEvent::GpuKernel(e) => e.pid,
123        }
124    }
125}
126
127#[cfg(test)]
128mod tests {
129    use super::*;
130
131    #[test]
132    fn test_cpu_sample_serialization() {
133        let sample = CpuSample {
134            timestamp: 1234567890,
135            pid: 1000,
136            tid: 1001,
137            cpu_id: 0,
138            user_stack: vec![0x400000, 0x400100],
139            kernel_stack: vec![],
140            comm: "test".to_string(),
141            user_stack_symbols: vec![],
142            kernel_stack_symbols: vec![],
143        };
144
145        let json = serde_json::to_string(&sample).unwrap();
146        let deserialized: CpuSample = serde_json::from_str(&json).unwrap();
147
148        assert_eq!(sample.pid, deserialized.pid);
149        assert_eq!(sample.timestamp, deserialized.timestamp);
150    }
151
152    #[test]
153    fn test_profile_event_bincode_serialization() {
154        use bincode::Options;
155        let config = bincode::config::DefaultOptions::new()
156            .with_fixint_encoding()
157            .allow_trailing_bytes();
158
159        let event = ProfileEvent::Syscall(SyscallEvent {
160            timestamp: 0x1122334455667788,
161            pid: 0x11111111,
162            tid: 0x22222222,
163            syscall_id: 198, // The "found 198" value
164            duration_ns: 1000,
165            return_value: 0,
166            comm: "test".to_string(),
167        });
168
169        let bytes = config.serialize(&event).unwrap();
170
171        // Expected layout (fixint):
172        // 0-3: Tag (u32) = 2
173        // 4-11: TS (u64)
174        // 12-15: Pid (i32)
175        // 16-19: Tid (i32)
176        // 20-23: SyscallId (u32) = 198
177
178        assert_eq!(bytes[0..4], [2, 0, 0, 0]); // Tag 2
179        assert_eq!(bytes[20..24], [198, 0, 0, 0]); // Syscall ID 198 (le)
180
181        let deserialized: ProfileEvent = config.deserialize(&bytes).unwrap();
182        match deserialized {
183            ProfileEvent::Syscall(e) => {
184                assert_eq!(e.syscall_id, 198);
185            }
186            _ => panic!("Wrong variant"),
187        }
188    }
189}