ghostscope_protocol/
trace_event.rs

1use crate::TypeKind;
2use serde::{Deserialize, Serialize};
3use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned};
4
5/// Each trace event contains multiple instructions followed by EndInstruction
6#[repr(C, packed)]
7#[derive(Debug, Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
8pub struct TraceEventHeader {
9    pub magic: u32,
10}
11
12#[repr(C, packed)]
13#[derive(Debug, Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
14pub struct TraceEventMessage {
15    pub trace_id: u64,
16    pub timestamp: u64,
17    pub pid: u32,
18    pub tid: u32,
19    // Followed by variable-length instruction sequence ending with EndInstruction
20}
21
22/// Instruction types for trace events
23#[repr(u8)]
24#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
25pub enum InstructionType {
26    PrintStringIndex = 0x01,     // print "string" (using string table index)
27    PrintVariableIndex = 0x02,   // print variable (using variable name index)
28    PrintComplexVariable = 0x03, // print complex variable (with full type info)
29    PrintComplexFormat = 0x05,   // print with complex variables in format args
30    Backtrace = 0x10,            // backtrace instruction
31    /// Structured runtime expression error/warning (control-flow or print context)
32    ExprError = 0x20,
33
34    // Control instructions
35    EndInstruction = 0xFF, // marks end of instruction sequence
36}
37
38/// Common instruction header
39#[repr(C, packed)]
40#[derive(Debug, Clone, Copy, FromBytes, KnownLayout, Immutable, Unaligned)]
41pub struct InstructionHeader {
42    pub inst_type: u8,    // InstructionType
43    pub data_length: u16, // Length of instruction data following this header
44    pub reserved: u8,
45}
46
47/// Per-variable runtime status for data acquisition
48#[repr(u8)]
49#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
50pub enum VariableStatus {
51    Ok = 0,
52    NullDeref = 1,
53    ReadError = 2,
54    AccessError = 3,
55    Truncated = 4,
56    /// Required runtime offsets/proc mapping not available at eBPF time
57    /// (e.g., no (pid,module) offsets to compute address)
58    OffsetsUnavailable = 5,
59    /// Requested dynamic length is <= 0; no bytes were read
60    ZeroLength = 6,
61}
62
63/// Print string instruction data (most optimized)
64#[repr(C, packed)]
65#[derive(Debug, Clone, Copy, FromBytes, KnownLayout, Immutable, Unaligned)]
66pub struct PrintStringIndexData {
67    pub string_index: u16, // Index into string table
68}
69
70/// Print variable instruction data (optimized with name index)
71#[repr(C, packed)]
72#[derive(Debug, Clone, Copy, FromBytes, KnownLayout, Immutable, Unaligned)]
73pub struct PrintVariableIndexData {
74    pub var_name_index: u16, // Index into variable name table
75    pub type_encoding: u8,   // TypeKind
76    pub data_len: u16,       // Length of variable data that follows
77    pub type_index: u16,     // Index into type table (new field)
78    pub status: u8, // Variable read status: see VariableStatus. For script variables this is 0.
79                    // Followed by variable data
80}
81
82/// Print complex variable instruction data (enhanced with full type info)
83#[repr(C, packed)]
84#[derive(Debug, Clone, Copy, FromBytes, KnownLayout, Immutable, Unaligned)]
85pub struct PrintComplexVariableData {
86    pub var_name_index: u16, // Index into variable name table
87    pub type_index: u16,     // Index into type table for complete type information
88    pub access_path_len: u8, // Length of access path description (e.g., "person.name.first")
89    pub status: u8,          // Variable read status: see VariableStatus
90    pub data_len: u16,       // Length of variable data that follows
91                             // Followed by access_path (UTF-8 string) then variable data
92}
93
94/// Complex format print instruction data (with full type info)
95#[repr(C, packed)]
96#[derive(Debug, Clone, Copy, FromBytes, KnownLayout, Immutable, Unaligned)]
97pub struct PrintComplexFormatData {
98    pub format_string_index: u16, // Index into string table for format string
99    pub arg_count: u8,            // Number of arguments
100    pub reserved: u8,             // Padding for alignment
101                                  // Followed by complex argument data:
102                                  // [var_name_index:u16, type_index:u16, access_path_len:u8, status:u8,
103                                  //  access_path:bytes, data_len:u16, data:bytes] * arg_count
104}
105
106// Note: historical PrintVariableError has been removed; per-variable errors
107// are carried via status in PrintVariableIndex/ComplexFormat.
108
109/// Backtrace instruction data
110#[repr(C, packed)]
111#[derive(Debug, Clone, Copy, FromBytes, KnownLayout, Immutable, Unaligned)]
112pub struct BacktraceData {
113    pub depth: u8, // Maximum backtrace depth to capture
114    pub flags: u8, // Backtrace options (0 = default)
115    pub reserved: u16, // Padding for alignment
116                   // Followed by backtrace frame data in the instruction payload
117}
118/// ExprError instruction data - structured warning for runtime expression failure
119#[repr(C, packed)]
120#[derive(Debug, Clone, Copy, FromBytes, KnownLayout, Immutable, Unaligned)]
121pub struct ExprErrorData {
122    pub string_index: u16, // Index into string table for pretty expression text
123    pub error_code: u8,    // Error code (semantic defined by compiler)
124    pub flags: u8,         // Optional flags bitfield (e.g., which side failed)
125    pub failing_addr: u64, // Optional: address involved in failure (0 if unknown)
126}
127
128/// End instruction data - marks the end of instruction sequence
129#[repr(C, packed)]
130#[derive(Debug, Clone, Copy, FromBytes, KnownLayout, Immutable, Unaligned)]
131pub struct EndInstructionData {
132    pub total_instructions: u16, // Total number of instructions before this EndInstruction
133    pub execution_status: u8,    // 0=success, 1=partial_failure, 2=complete_failure
134    pub reserved: u8,            // Padding for alignment
135}
136
137/// High-level instruction representation for compilation and parsing
138#[derive(Debug, Clone, Serialize, Deserialize)]
139pub enum Instruction {
140    PrintStringIndex {
141        string_index: u16,
142    },
143    PrintVariableIndex {
144        var_name_index: u16,
145        type_encoding: TypeKind,
146        type_index: u16, // Index into type table (new field)
147        data: Vec<u8>,
148    },
149    /// Structured runtime expression error/warning
150    ExprError {
151        string_index: u16,
152        error_code: u8,
153        flags: u8,
154        failing_addr: u64,
155    },
156    Backtrace {
157        depth: u8,
158        flags: u8,
159        frames: Vec<u64>, // Stack frame addresses
160    },
161    EndInstruction {
162        total_instructions: u16,
163        execution_status: u8, // 0=success, 1=partial_failure, 2=complete_failure
164    },
165}
166
167impl Instruction {
168    /// Get the instruction type
169    pub fn instruction_type(&self) -> InstructionType {
170        match self {
171            Instruction::PrintStringIndex { .. } => InstructionType::PrintStringIndex,
172            Instruction::PrintVariableIndex { .. } => InstructionType::PrintVariableIndex,
173            Instruction::ExprError { .. } => InstructionType::ExprError,
174            Instruction::Backtrace { .. } => InstructionType::Backtrace,
175            Instruction::EndInstruction { .. } => InstructionType::EndInstruction,
176        }
177    }
178}
179
180#[cfg(test)]
181mod tests {
182    use super::*;
183
184    #[test]
185    fn test_instruction_types() {
186        let inst1 = Instruction::PrintStringIndex { string_index: 0 };
187        assert_eq!(inst1.instruction_type(), InstructionType::PrintStringIndex);
188    }
189
190    #[test]
191    fn test_instruction_types_basic() {
192        let inst = Instruction::EndInstruction {
193            total_instructions: 5,
194            execution_status: 0,
195        };
196        assert_eq!(inst.instruction_type(), InstructionType::EndInstruction);
197    }
198}