Skip to main content

ghostscope_ui/
events.rs

1mod channels;
2mod debug_info;
3mod runtime;
4mod source_path;
5mod trace_display;
6mod tui_event;
7
8pub use channels::{EventRegistry, RuntimeChannels, DEFAULT_TRACE_CHANNEL_CAPACITY};
9pub use debug_info::{
10    AddressMapping, ModuleDebugInfo, SectionInfo, SharedLibraryInfo, SourceCodeInfo,
11    SourceFileGroup, SourceFileInfo, TargetDebugInfo, TargetType, VariableDebugInfo,
12};
13pub use runtime::{
14    ExecutionStatus, LoadStatus, ModuleLoadingStats, RuntimeCommand, RuntimeStatus,
15    ScriptCompilationDetails, ScriptExecutionResult, TraceDefinition, TraceDetailInfo,
16    TraceLoadDetail, TraceStatus, TraceSummaryInfo,
17};
18pub use source_path::{PathSubstitution, SourcePathInfo};
19pub use trace_display::{
20    BacktraceDisplay, BacktraceDisplayFrame, ComplexVariableDisplay, ExprErrorDisplay,
21    TraceDisplayItem, UiTraceEvent, VariableDisplay,
22};
23pub use tui_event::TuiEvent;
24
25#[cfg(test)]
26mod tests {
27    use super::*;
28    use ghostscope_protocol::{ParsedInstruction, ParsedTraceEvent, TypeKind};
29
30    fn sample_event(trace_id: u64) -> UiTraceEvent {
31        UiTraceEvent {
32            timestamp: 0,
33            trace_id,
34            pid: 42,
35            tid: 42,
36            items: vec![],
37            execution_status: None,
38        }
39    }
40
41    #[test]
42    fn event_registry_uses_bounded_trace_channel_capacity() {
43        let (_registry, channels) = EventRegistry::new_with_trace_capacity(1);
44        assert_eq!(channels.trace_channel_capacity, 1);
45
46        channels.trace_sender.try_send(sample_event(1)).unwrap();
47        assert!(channels.trace_sender.try_send(sample_event(2)).is_err());
48    }
49
50    #[test]
51    fn protocol_event_preserves_structured_print_items() {
52        let event = ParsedTraceEvent {
53            trace_id: 7,
54            timestamp: 11,
55            pid: 42,
56            tid: 43,
57            instructions: vec![
58                ParsedInstruction::PrintString {
59                    content: "value={}".to_string(),
60                },
61                ParsedInstruction::PrintVariable {
62                    name: "counter".to_string(),
63                    type_encoding: TypeKind::U64,
64                    formatted_value: "99".to_string(),
65                    raw_data: Vec::new(),
66                },
67                ParsedInstruction::PrintVariable {
68                    name: "standalone".to_string(),
69                    type_encoding: TypeKind::I32,
70                    formatted_value: "-1".to_string(),
71                    raw_data: Vec::new(),
72                },
73                ParsedInstruction::PrintComplexVariable {
74                    name: "req".to_string(),
75                    access_path: "req.method".to_string(),
76                    type_index: 12,
77                    formatted_value: "req.method = GET".to_string(),
78                    raw_data: Vec::new(),
79                },
80                ParsedInstruction::ExprError {
81                    expr: "memcmp(buf, hex(\"41\"), 1)".to_string(),
82                    error_code: 2,
83                    flags: 0x01,
84                    failing_addr: 0x1234,
85                },
86                ParsedInstruction::EndInstruction {
87                    total_instructions: 5,
88                    execution_status: 1,
89                },
90            ],
91        };
92
93        let display = UiTraceEvent::from_protocol_event(&event);
94        assert_eq!(display.items.len(), 4);
95        assert!(matches!(
96            &display.items[0],
97            TraceDisplayItem::FormattedText { content } if content == "value=99"
98        ));
99        assert!(matches!(
100            &display.items[1],
101            TraceDisplayItem::Variable(variable)
102                if variable.name == "standalone"
103                    && variable.type_name == "I32"
104                    && variable.formatted_value == "-1"
105        ));
106        assert!(matches!(
107            &display.items[2],
108            TraceDisplayItem::ComplexVariable(variable)
109                if variable.display_name() == "req.method"
110                    && variable.to_formatted_output() == "req.method = GET"
111        ));
112        assert!(matches!(
113            &display.items[3],
114            TraceDisplayItem::ExprError(error)
115                if error.reason() == "read error"
116                    && error.readable_flags().as_deref() == Some("first-arg read-fail")
117        ));
118        assert!(display.is_error());
119        assert_eq!(
120            display.to_formatted_output(),
121            vec![
122                "value=99".to_string(),
123                "standalone (I32): -1".to_string(),
124                "req.method = GET".to_string(),
125                "ExprError: memcmp(buf, hex(\"41\"), 1) (read error at 0x0000000000001234, flags: first-arg read-fail)".to_string(),
126            ]
127        );
128    }
129}