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}