1use crate::format_printer::FormatPrinter;
2use crate::trace_context::TraceContext;
3use crate::trace_event::*;
4use crate::TypeKind;
5use tracing::{debug, warn};
6use zerocopy::FromBytes;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
10pub enum EventSource {
11 #[default]
14 RingBuf,
15 PerfEventArray,
18}
19
20#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
22pub enum ParsedInstruction {
23 PrintString {
24 content: String,
25 },
26 PrintVariable {
27 name: String,
28 type_encoding: TypeKind,
29 formatted_value: String,
30 raw_data: Vec<u8>,
31 },
32 ExprError {
34 expr: String,
35 error_code: u8,
36 flags: u8,
37 failing_addr: u64,
38 },
39 PrintComplexFormat {
40 formatted_output: String,
41 },
42 PrintComplexVariable {
43 name: String,
44 access_path: String,
45 type_index: u16,
46 formatted_value: String,
47 raw_data: Vec<u8>,
48 },
49 Backtrace {
50 requested_depth: u8,
51 flags: u8,
52 status: BacktraceStatus,
53 error_code: u16,
54 frames: Vec<ParsedBacktraceFrame>,
55 },
56 EndInstruction {
57 total_instructions: u16,
58 execution_status: u8,
59 },
60}
61
62#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
63pub struct ParsedBacktraceFrame {
64 pub module_cookie: u64,
65 pub pc: u64,
66 pub raw_ip: u64,
67 pub flags: u16,
68}
69
70#[derive(Debug, Clone)]
72pub struct ParsedTraceEvent {
73 pub trace_id: u64,
74 pub timestamp: u64,
75 pub pid: u32,
76 pub tid: u32,
77 pub instructions: Vec<ParsedInstruction>,
78}
79
80impl ParsedTraceEvent {
81 pub fn has_formatted_output(&self) -> bool {
82 self.instructions
83 .iter()
84 .any(|instruction| !matches!(instruction, ParsedInstruction::EndInstruction { .. }))
85 }
86
87 pub fn try_for_each_formatted_output<E>(
88 &self,
89 mut emit: impl FnMut(&str) -> Result<(), E>,
90 ) -> Result<(), E> {
91 let mut i = 0;
92
93 while i < self.instructions.len() {
94 match &self.instructions[i] {
95 ParsedInstruction::PrintString { content } => {
96 if content.contains("{}") {
97 let (formatted, consumed) =
98 self.format_string_with_variables(content, i + 1);
99 emit(&formatted)?;
100 i += consumed;
101 } else {
102 emit(content)?;
103 i += 1;
104 }
105 }
106
107 ParsedInstruction::EndInstruction { .. } => {
108 i += 1;
109 }
110 instruction => {
111 let line = instruction.to_display_string();
112 emit(&line)?;
113 i += 1;
114 }
115 }
116 }
117
118 Ok(())
119 }
120
121 pub fn to_formatted_output(&self) -> Vec<String> {
124 let mut output = Vec::new();
125 let _ =
126 self.try_for_each_formatted_output(|line| -> Result<(), std::convert::Infallible> {
127 output.push(line.to_string());
128 Ok(())
129 });
130
131 output
132 }
133
134 fn format_string_with_variables(
136 &self,
137 format_string: &str,
138 start_index: usize,
139 ) -> (String, usize) {
140 let placeholder_count = format_string.matches("{}").count();
142
143 let mut consumed = 1; let mut result = String::with_capacity(format_string.len());
145 let mut remaining = format_string;
146
147 for instruction_index in
148 start_index..(start_index + placeholder_count).min(self.instructions.len())
149 {
150 let Some(pos) = remaining.find("{}") else {
151 break;
152 };
153
154 if let Some(ParsedInstruction::PrintVariable {
155 formatted_value, ..
156 }) = self.instructions.get(instruction_index)
157 {
158 result.push_str(&remaining[..pos]);
159 result.push_str(formatted_value);
160 consumed += 1;
161 remaining = &remaining[pos + 2..];
162 } else {
163 break;
164 }
165 }
166 result.push_str(remaining);
167
168 (result, consumed)
169 }
170}
171
172#[derive(Debug, Clone)]
174pub enum ParseState {
175 WaitingForHeader,
176 WaitingForMessage {
177 header: TraceEventHeader,
178 },
179 WaitingForInstructions {
180 header: TraceEventHeader,
181 message: TraceEventMessage,
182 instructions: Vec<ParsedInstruction>,
183 },
184 Complete,
185}
186
187pub struct StreamingTraceParser {
190 parse_state: ParseState,
191 buffer: Vec<u8>,
192 event_source: EventSource,
193}
194
195impl Default for StreamingTraceParser {
196 fn default() -> Self {
197 Self::new()
198 }
199}
200
201impl StreamingTraceParser {
202 pub fn new() -> Self {
205 Self::with_event_source(EventSource::RingBuf)
206 }
207
208 pub fn with_event_source(event_source: EventSource) -> Self {
210 Self {
211 parse_state: ParseState::WaitingForHeader,
212 buffer: Vec::with_capacity(1024),
213 event_source,
214 }
215 }
216
217 pub fn process_segment(
220 &mut self,
221 data: &[u8],
222 trace_context: &TraceContext,
223 ) -> Result<Option<ParsedTraceEvent>, String> {
224 self.buffer.extend_from_slice(data);
226
227 debug!(
228 "Processing segment of {} bytes, buffer now has {} bytes, state: {:?}",
229 data.len(),
230 self.buffer.len(),
231 self.parse_state
232 );
233
234 loop {
236 let state = std::mem::replace(&mut self.parse_state, ParseState::Complete);
237 let consumed = match state {
238 ParseState::WaitingForHeader => {
239 let (header, _rest) = match TraceEventHeader::read_from_prefix(&self.buffer) {
241 Ok((h, r)) => (h, r),
242 Err(_) => {
243 self.parse_state = ParseState::WaitingForHeader;
244 debug!(
245 "Waiting for more data for header (have {} bytes, need {})",
246 self.buffer.len(),
247 std::mem::size_of::<TraceEventHeader>()
248 );
249 return Ok(None);
250 }
251 };
252
253 let magic = header.magic;
255 if magic != crate::consts::MAGIC {
256 return Err(format!("Invalid magic number: 0x{magic:x}"));
257 }
258
259 debug!("Received valid header: magic=0x{magic:x}");
260 self.parse_state = ParseState::WaitingForMessage { header };
261 std::mem::size_of::<TraceEventHeader>()
262 }
263
264 ParseState::WaitingForMessage { header } => {
265 let (message, _rest) = match TraceEventMessage::read_from_prefix(&self.buffer) {
267 Ok((m, r)) => (m, r),
268 Err(_) => {
269 self.parse_state = ParseState::WaitingForMessage { header };
270 debug!(
271 "Waiting for more data for message (have {} bytes, need {})",
272 self.buffer.len(),
273 std::mem::size_of::<TraceEventMessage>()
274 );
275 return Ok(None);
276 }
277 };
278
279 let trace_id = message.trace_id;
281 let pid = message.pid;
282 let tid = message.tid;
283 debug!(
284 "Received message: trace_id={}, pid={}, tid={}",
285 trace_id, pid, tid
286 );
287
288 self.parse_state = ParseState::WaitingForInstructions {
289 header,
290 message,
291 instructions: Vec::new(),
292 };
293 std::mem::size_of::<TraceEventMessage>()
294 }
295
296 ParseState::WaitingForInstructions {
297 header,
298 message,
299 mut instructions,
300 } => {
301 match self.try_parse_instruction(&self.buffer, trace_context)? {
303 Some((parsed_instruction, consumed_bytes)) => {
304 if matches!(
306 parsed_instruction,
307 ParsedInstruction::EndInstruction { .. }
308 ) {
309 instructions.push(parsed_instruction);
310
311 let complete_event = ParsedTraceEvent {
313 trace_id: message.trace_id,
314 timestamp: message.timestamp,
315 pid: message.pid,
316 tid: message.tid,
317 instructions,
318 };
319
320 debug!(
321 "Completed trace event with {} instructions",
322 complete_event.instructions.len()
323 );
324
325 self.parse_state = ParseState::WaitingForHeader;
327
328 match self.event_source {
330 EventSource::RingBuf => {
331 self.buffer.drain(..consumed_bytes);
333 debug!(
334 "RingBuf mode: consumed {} bytes, {} bytes remain in buffer",
335 consumed_bytes,
336 self.buffer.len()
337 );
338 }
339 EventSource::PerfEventArray => {
340 self.buffer.clear();
345 debug!("PerfEventArray mode: cleared buffer after complete event");
346 }
347 }
348
349 return Ok(Some(complete_event));
350 } else {
351 instructions.push(parsed_instruction);
353
354 self.parse_state = ParseState::WaitingForInstructions {
355 header,
356 message,
357 instructions,
358 };
359 consumed_bytes
360 }
361 }
362 None => {
363 self.parse_state = ParseState::WaitingForInstructions {
364 header,
365 message,
366 instructions,
367 };
368 debug!("Waiting for more data for instruction");
369 return Ok(None);
370 }
371 }
372 }
373
374 ParseState::Complete => {
375 warn!("Received data while in Complete state, resetting");
376 self.parse_state = ParseState::WaitingForHeader;
377 continue;
378 }
379 };
380
381 if consumed > 0 {
383 self.buffer.drain(..consumed);
384 debug!(
385 "Consumed {} bytes, buffer now has {} bytes",
386 consumed,
387 self.buffer.len()
388 );
389 }
390 }
391 }
392
393 fn try_parse_instruction(
396 &self,
397 data: &[u8],
398 trace_context: &TraceContext,
399 ) -> Result<Option<(ParsedInstruction, usize)>, String> {
400 let (inst_header, _rest) = match InstructionHeader::read_from_prefix(data) {
402 Ok((h, r)) => (h, r),
403 Err(_) => return Ok(None),
404 };
405
406 let expected_total_size =
407 std::mem::size_of::<InstructionHeader>() + inst_header.data_length as usize;
408 if data.len() < expected_total_size {
409 debug!(
410 "Waiting for complete instruction: have {} bytes, need {} bytes",
411 data.len(),
412 expected_total_size
413 );
414 return Ok(None);
415 }
416
417 let inst_data = &data[std::mem::size_of::<InstructionHeader>()..expected_total_size];
418
419 let instruction = match inst_header.inst_type {
420 t if t == InstructionType::PrintStringIndex as u8 => {
421 let (data_struct, _) = PrintStringIndexData::read_from_prefix(inst_data)
422 .map_err(|_| "Invalid PrintStringIndex data".to_string())?;
423
424 let string_index = data_struct.string_index;
425 let string_content = trace_context
426 .get_string(string_index)
427 .ok_or_else(|| format!("Invalid string index: {string_index}"))?;
428
429 ParsedInstruction::PrintString {
430 content: string_content.to_string(),
431 }
432 }
433
434 t if t == InstructionType::PrintVariableIndex as u8 => {
435 let (data_struct, _) = PrintVariableIndexData::read_from_prefix(inst_data)
436 .map_err(|_| "Invalid PrintVariableIndex data".to_string())?;
437
438 let var_name_index = data_struct.var_name_index;
439 let var_name = trace_context
440 .get_variable_name(var_name_index)
441 .ok_or_else(|| format!("Invalid variable index: {var_name_index}"))?;
442
443 let var_data_offset = std::mem::size_of::<PrintVariableIndexData>();
444 if inst_data.len() < var_data_offset + data_struct.data_len as usize {
445 return Err("Invalid variable data length".to_string());
446 }
447
448 let var_data =
449 &inst_data[var_data_offset..var_data_offset + data_struct.data_len as usize];
450
451 let type_encoding =
452 TypeKind::from_u8(data_struct.type_encoding).unwrap_or(TypeKind::Unknown);
453
454 let type_index = data_struct.type_index; tracing::debug!("streaming_parser - type_index = {}", type_index);
457 tracing::debug!(
458 "streaming_parser - TraceContext has {} types",
459 trace_context.types.len()
460 );
461
462 let formatted_value = match trace_context.get_type(type_index) {
463 Some(type_info) => {
464 tracing::debug!(
465 "streaming_parser - Found type_info for index {}",
466 type_index
467 );
468 crate::format_printer::FormatPrinter::format_data_with_type_info(
470 var_data, type_info,
471 )
472 }
473 None => {
474 tracing::debug!(
475 "streaming_parser - No type_info found for index {}",
476 type_index
477 );
478 format!(
480 "<COMPILER_ERROR: type_index {type_index} not found in TraceContext>"
481 )
482 }
483 };
484
485 ParsedInstruction::PrintVariable {
486 name: var_name.to_string(),
487 type_encoding,
488 formatted_value,
489 raw_data: var_data.to_vec(),
490 }
491 }
492
493 t if t == InstructionType::ExprError as u8 => {
494 let (data_struct, _) =
495 crate::trace_event::ExprErrorData::read_from_prefix(inst_data)
496 .map_err(|_| "Invalid ExprError data".to_string())?;
497 let si = data_struct.string_index;
498 let expr = match trace_context.get_string(si) {
499 Some(s) => s.to_string(),
500 None => format!("<INVALID_EXPR_INDEX_{si}>"),
501 };
502 ParsedInstruction::ExprError {
503 expr,
504 error_code: data_struct.error_code,
505 flags: data_struct.flags,
506 failing_addr: data_struct.failing_addr,
507 }
508 }
509
510 t if t == InstructionType::PrintComplexFormat as u8 => {
511 let (format_data, _) = PrintComplexFormatData::read_from_prefix(inst_data)
512 .map_err(|_| "Invalid PrintComplexFormat data".to_string())?;
513
514 let mut complex_variables = Vec::new();
516 let mut data_offset = std::mem::size_of::<PrintComplexFormatData>();
517
518 for _ in 0..format_data.arg_count {
519 if data_offset + PRINT_COMPLEX_FORMAT_ARG_ACCESS_PATH_OFFSET > inst_data.len() {
520 return Err("Invalid PrintComplexFormat argument data".to_string());
521 }
522
523 let var_name_index = u16::from_le_bytes([
525 inst_data[data_offset + PRINT_COMPLEX_FORMAT_ARG_VAR_NAME_INDEX_OFFSET],
526 inst_data[data_offset + PRINT_COMPLEX_FORMAT_ARG_VAR_NAME_INDEX_OFFSET + 1],
527 ]);
528 let type_index = u16::from_le_bytes([
529 inst_data[data_offset + PRINT_COMPLEX_FORMAT_ARG_TYPE_INDEX_OFFSET],
530 inst_data[data_offset + PRINT_COMPLEX_FORMAT_ARG_TYPE_INDEX_OFFSET + 1],
531 ]);
532 let access_path_len = inst_data
533 [data_offset + PRINT_COMPLEX_FORMAT_ARG_ACCESS_PATH_LEN_OFFSET]
534 as usize;
535 let status = inst_data[data_offset + PRINT_COMPLEX_FORMAT_ARG_STATUS_OFFSET];
536 data_offset += PRINT_COMPLEX_FORMAT_ARG_ACCESS_PATH_OFFSET;
537
538 if data_offset + access_path_len > inst_data.len() {
540 return Err("Invalid PrintComplexFormat access path".to_string());
541 }
542 let access_path_bytes = &inst_data[data_offset..data_offset + access_path_len];
543 let access_path = String::from_utf8_lossy(access_path_bytes).to_string();
544 data_offset += access_path_len;
545
546 if data_offset + PRINT_COMPLEX_FORMAT_ARG_DATA_LEN_SIZE > inst_data.len() {
548 return Err("Invalid PrintComplexFormat data length".to_string());
549 }
550 let data_len =
551 u16::from_le_bytes([inst_data[data_offset], inst_data[data_offset + 1]]);
552 data_offset += PRINT_COMPLEX_FORMAT_ARG_DATA_LEN_SIZE;
553
554 if data_offset + data_len as usize > inst_data.len() {
556 return Err("Invalid PrintComplexFormat variable data".to_string());
557 }
558 let var_data = inst_data[data_offset..data_offset + data_len as usize].to_vec();
559 data_offset += data_len as usize;
560
561 complex_variables.push(crate::format_printer::ParsedComplexVariable {
562 var_name_index,
563 type_index,
564 access_path,
565 status,
566 data: var_data,
567 });
568 }
569
570 let formatted_output =
572 crate::format_printer::FormatPrinter::format_complex_print_data(
573 format_data.format_string_index,
574 &complex_variables,
575 trace_context,
576 );
577
578 ParsedInstruction::PrintComplexFormat { formatted_output }
579 }
580
581 t if t == InstructionType::Backtrace as u8 => {
582 let (data_struct, _) = BacktraceData::read_from_prefix(inst_data)
583 .map_err(|_| "Invalid Backtrace data".to_string())?;
584
585 let requested_depth = data_struct.requested_depth;
586 let frame_count = data_struct.frame_count;
587 let flags = data_struct.flags;
588 let status = BacktraceStatus::from_u8(data_struct.status);
589 let error_code = data_struct.error_code;
590 let frame_offset = BACKTRACE_DATA_SIZE;
591 let available_frames =
592 inst_data.len().saturating_sub(frame_offset) / BACKTRACE_FRAME_DATA_SIZE;
593 let parsed_frames = frame_count as usize;
594 if frame_count > requested_depth || parsed_frames > available_frames {
595 return Err("Invalid Backtrace data".to_string());
596 }
597
598 let mut frames = Vec::with_capacity(parsed_frames);
599 for index in 0..parsed_frames {
600 let start = frame_offset + index * BACKTRACE_FRAME_DATA_SIZE;
601 let end = start + BACKTRACE_FRAME_DATA_SIZE;
602 let (frame, _) = BacktraceFrameData::read_from_prefix(&inst_data[start..end])
603 .map_err(|_| "Invalid Backtrace frame data".to_string())?;
604 frames.push(ParsedBacktraceFrame {
605 module_cookie: frame.module_cookie,
606 pc: frame.pc,
607 raw_ip: frame.raw_ip,
608 flags: frame.flags,
609 });
610 }
611
612 ParsedInstruction::Backtrace {
613 requested_depth,
614 flags,
615 status,
616 error_code,
617 frames,
618 }
619 }
620
621 t if t == InstructionType::PrintComplexVariable as u8 => {
622 let (data_struct, _) = PrintComplexVariableData::read_from_prefix(inst_data)
623 .map_err(|_| "Invalid PrintComplexVariable data".to_string())?;
624
625 let var_name_index = data_struct.var_name_index;
627 let var_name = trace_context
628 .get_variable_name(var_name_index)
629 .ok_or_else(|| format!("Invalid variable index: {var_name_index}"))?;
630
631 let access_path_len = data_struct.access_path_len as usize;
633 let struct_size = std::mem::size_of::<PrintComplexVariableData>();
634
635 if inst_data.len() < struct_size + access_path_len {
636 return Err("Invalid PrintComplexVariable access path length".to_string());
637 }
638
639 let access_path_bytes = &inst_data[struct_size..struct_size + access_path_len];
640 let access_path = String::from_utf8_lossy(access_path_bytes);
641
642 let var_data_offset = struct_size + access_path_len;
644 if inst_data.len() < var_data_offset + data_struct.data_len as usize {
645 return Err("Invalid PrintComplexVariable data length".to_string());
646 }
647
648 let var_data =
649 &inst_data[var_data_offset..var_data_offset + data_struct.data_len as usize];
650
651 let formatted_value = FormatPrinter::format_complex_variable_with_status(
653 var_name_index,
654 data_struct.type_index,
655 &access_path,
656 var_data,
657 data_struct.status,
658 trace_context,
659 );
660
661 ParsedInstruction::PrintComplexVariable {
662 name: var_name.to_string(),
663 access_path: access_path.to_string(),
664 type_index: data_struct.type_index,
665 formatted_value,
666 raw_data: var_data.to_vec(),
667 }
668 }
669
670 t if t == InstructionType::EndInstruction as u8 => {
671 let (data_struct, _) = EndInstructionData::read_from_prefix(inst_data)
672 .map_err(|_| "Invalid EndInstruction data".to_string())?;
673
674 ParsedInstruction::EndInstruction {
675 total_instructions: data_struct.total_instructions,
676 execution_status: data_struct.execution_status,
677 }
678 }
679
680 _ => {
681 return Err(format!(
682 "Unknown instruction type: {}",
683 inst_header.inst_type
684 ))
685 }
686 };
687
688 Ok(Some((instruction, expected_total_size)))
689 }
690
691 pub fn reset(&mut self) {
693 self.parse_state = ParseState::WaitingForHeader;
694 self.buffer.clear();
695 }
696
697 pub fn get_state(&self) -> &ParseState {
699 &self.parse_state
700 }
701}
702
703impl ParsedInstruction {
704 pub fn to_display_string(&self) -> String {
706 match self {
707 ParsedInstruction::PrintString { content } => {
708 format!("print \"{content}\"")
709 }
710 ParsedInstruction::PrintVariable {
711 name,
712 type_encoding,
713 formatted_value,
714 raw_data: _,
715 } => {
716 format!("{name} ({type_encoding:?}): {formatted_value}")
717 }
718 ParsedInstruction::ExprError {
719 expr,
720 error_code,
721 flags,
722 failing_addr,
723 } => {
724 let reason = match *error_code {
727 1 => "null deref",
728 2 => "read error",
729 3 => "access error",
730 4 => "truncated",
731 5 => "offsets unavailable",
732 6 => "zero length",
733 _ => "error",
734 };
735
736 fn readable_flags(expr: &str, flags: u8) -> Option<String> {
738 if flags == 0 {
739 return None;
740 }
741 let mut tags: Vec<&'static str> = Vec::new();
742 let is_memcmp = expr.contains("memcmp(");
743 let is_strncmp = expr.contains("strncmp(") || expr.contains("starts_with(");
744 if is_memcmp {
745 if (flags & 0x01) != 0 {
746 tags.push("first-arg read-fail");
747 }
748 if (flags & 0x02) != 0 {
749 tags.push("second-arg read-fail");
750 }
751 if (flags & 0x04) != 0 {
752 tags.push("len-clamped");
753 }
754 if (flags & 0x08) != 0 {
755 tags.push("len=0");
756 }
757 } else if is_strncmp {
758 if (flags & 0x01) != 0 {
759 tags.push("read-fail");
760 }
761 if (flags & 0x04) != 0 {
762 tags.push("len-clamped");
763 }
764 if (flags & 0x08) != 0 {
765 tags.push("len=0");
766 }
767 } else {
768 return Some(format!("0x{flags:02x}"));
770 }
771 if tags.is_empty() {
772 None
773 } else {
774 Some(tags.join(","))
775 }
776 }
777
778 let flags_text = readable_flags(expr, *flags);
779 let addr_text = if *failing_addr != 0 {
780 format!("at 0x{failing_addr:016x}")
781 } else {
782 "at NULL".to_string()
783 };
784 let base = format!("ExprError: {expr} ({reason} {addr_text}");
785 match flags_text {
786 Some(f) => format!("{base}, flags: {f})"),
787 None => format!("{base})"),
788 }
789 }
790
791 ParsedInstruction::PrintComplexFormat { formatted_output } => formatted_output.clone(),
792 ParsedInstruction::PrintComplexVariable {
793 name: _,
794 access_path: _,
795 type_index: _,
796 formatted_value,
797 raw_data: _,
798 } => {
799 formatted_value.clone()
801 }
802 ParsedInstruction::Backtrace {
803 requested_depth,
804 status,
805 error_code,
806 frames,
807 ..
808 } => {
809 let mut lines = vec![format!(
810 "backtrace(max_depth={requested_depth}, frames={}, status={})",
811 frames.len(),
812 status.label()
813 )];
814 for (index, frame) in frames.iter().enumerate() {
815 lines.push(format!(
816 " #{index} cookie=0x{:016x} pc=0x{:x} raw=0x{:x}",
817 frame.module_cookie, frame.pc, frame.raw_ip
818 ));
819 }
820 if *status != BacktraceStatus::Complete {
821 let suffix = match backtrace_error_label(*error_code) {
822 Some("unknown") => format!(" (code={error_code})"),
823 Some(label) => format!(" ({label}, code={error_code})"),
824 None => String::new(),
825 };
826 lines.push(format!(" stopped: {}{}", status.label(), suffix));
827 }
828 lines.join("\n")
829 }
830 ParsedInstruction::EndInstruction {
831 total_instructions,
832 execution_status,
833 } => {
834 let status_str = match *execution_status {
835 0 => "success",
836 1 => "partial_failure",
837 2 => "complete_failure",
838 _ => "unknown",
839 };
840 format!("end({total_instructions} instructions, {status_str})")
841 }
842 }
843 }
844
845 pub fn instruction_type(&self) -> String {
847 match self {
848 ParsedInstruction::PrintString { .. } => "PrintString".to_string(),
849 ParsedInstruction::PrintVariable { .. } => "PrintVariable".to_string(),
850 ParsedInstruction::ExprError { .. } => "ExprError".to_string(),
851
852 ParsedInstruction::PrintComplexFormat { .. } => "PrintComplexFormat".to_string(),
853 ParsedInstruction::PrintComplexVariable { .. } => "PrintComplexVariable".to_string(),
854 ParsedInstruction::Backtrace { .. } => "Backtrace".to_string(),
855 ParsedInstruction::EndInstruction { .. } => "EndInstruction".to_string(),
856 }
857 }
858}
859
860#[cfg(test)]
861mod tests {
862 use super::*;
863
864 #[test]
865 fn test_streaming_parser() {
866 let mut trace_context = TraceContext::new();
867 let _str_idx = trace_context.add_string("hello world".to_string());
868
869 let mut parser = StreamingTraceParser::new();
870
871 let header = TraceEventHeader {
873 magic: crate::consts::MAGIC,
874 };
875
876 let message = TraceEventMessage {
877 trace_id: 12345,
878 timestamp: 1000,
879 pid: 1001,
880 tid: 2002,
881 };
882
883 let header_bytes = zerocopy::IntoBytes::as_bytes(&header);
885 let result = parser
886 .process_segment(header_bytes, &trace_context)
887 .unwrap();
888 assert!(result.is_none()); let message_bytes = zerocopy::IntoBytes::as_bytes(&message);
892 let result = parser
893 .process_segment(message_bytes, &trace_context)
894 .unwrap();
895 assert!(result.is_none()); }
901
902 #[test]
903 fn test_parse_exprerror_instruction() {
904 let mut trace_context = TraceContext::new();
905 let expr_idx = trace_context.add_string("memcmp(buf, hex(\"504f\"), 2)".to_string());
906
907 let mut parser = StreamingTraceParser::new();
908
909 let header = TraceEventHeader {
911 magic: crate::consts::MAGIC,
912 };
913 let header_bytes = zerocopy::IntoBytes::as_bytes(&header);
914 assert!(parser
915 .process_segment(header_bytes, &trace_context)
916 .unwrap()
917 .is_none());
918
919 let message = TraceEventMessage {
921 trace_id: 1,
922 timestamp: 0,
923 pid: 123,
924 tid: 456,
925 };
926 let message_bytes = zerocopy::IntoBytes::as_bytes(&message);
927 assert!(parser
928 .process_segment(message_bytes, &trace_context)
929 .unwrap()
930 .is_none());
931
932 let mut inst = Vec::new();
934 inst.push(InstructionType::ExprError as u8); inst.extend_from_slice(
937 &(std::mem::size_of::<crate::trace_event::ExprErrorData>() as u16).to_le_bytes(),
938 ); inst.push(0u8); inst.extend_from_slice(&expr_idx.to_le_bytes()); inst.push(1u8); inst.push(0u8); inst.extend_from_slice(&0x1234_5678_9abc_def0u64.to_le_bytes()); inst.push(InstructionType::EndInstruction as u8);
948 inst.extend_from_slice(&(std::mem::size_of::<EndInstructionData>() as u16).to_le_bytes());
949 inst.push(0u8); inst.extend_from_slice(&1u16.to_le_bytes()); inst.push(1u8); inst.push(0u8); let event = parser
957 .process_segment(&inst, &trace_context)
958 .unwrap()
959 .expect("complete event");
960 assert_eq!(event.trace_id, 1);
961 assert_eq!(event.pid, 123);
962 assert_eq!(event.tid, 456);
963 assert_eq!(event.instructions.len(), 2);
964 match &event.instructions[0] {
965 ParsedInstruction::ExprError {
966 expr,
967 error_code,
968 flags,
969 failing_addr,
970 } => {
971 assert_eq!(expr, "memcmp(buf, hex(\"504f\"), 2)");
972 assert_eq!(*error_code, 1);
973 assert_eq!(*flags, 0);
974 assert_eq!(*failing_addr, 0x1234_5678_9abc_def0u64);
975 }
976 other => panic!("unexpected first instruction: {other:?}"),
977 }
978 match &event.instructions[1] {
979 ParsedInstruction::EndInstruction {
980 total_instructions,
981 execution_status,
982 } => {
983 assert_eq!(*total_instructions, 1);
984 assert_eq!(*execution_status, 1); }
986 other => panic!("unexpected last instruction: {other:?}"),
987 }
988 }
989
990 #[test]
991 fn test_parse_backtrace_instruction_with_frames() {
992 let trace_context = TraceContext::new();
993 let mut parser = StreamingTraceParser::new();
994
995 let header = TraceEventHeader {
996 magic: crate::consts::MAGIC,
997 };
998 parser
999 .process_segment(zerocopy::IntoBytes::as_bytes(&header), &trace_context)
1000 .unwrap();
1001
1002 let message = TraceEventMessage {
1003 trace_id: 7,
1004 timestamp: 0,
1005 pid: 100,
1006 tid: 101,
1007 };
1008 parser
1009 .process_segment(zerocopy::IntoBytes::as_bytes(&message), &trace_context)
1010 .unwrap();
1011
1012 let mut inst = Vec::new();
1013 inst.push(InstructionType::Backtrace as u8);
1014 inst.extend_from_slice(
1015 &((BACKTRACE_DATA_SIZE + BACKTRACE_FRAME_DATA_SIZE) as u16).to_le_bytes(),
1016 );
1017 inst.push(0);
1018 inst.push(8); inst.push(1); inst.push(BACKTRACE_FLAG_INLINE);
1021 inst.push(BacktraceStatus::UnsupportedCfi as u8);
1022 inst.extend_from_slice(&0u16.to_le_bytes());
1023 inst.extend_from_slice(&0u16.to_le_bytes());
1024 inst.extend_from_slice(&0x1122_3344_5566_7788u64.to_le_bytes());
1025 inst.extend_from_slice(&0x1234u64.to_le_bytes());
1026 inst.extend_from_slice(&0x7fff_0000_1234u64.to_le_bytes());
1027 inst.extend_from_slice(&0u16.to_le_bytes());
1028 inst.extend_from_slice(&0u16.to_le_bytes());
1029 inst.extend_from_slice(&0u32.to_le_bytes());
1030
1031 inst.push(InstructionType::EndInstruction as u8);
1032 inst.extend_from_slice(&(std::mem::size_of::<EndInstructionData>() as u16).to_le_bytes());
1033 inst.push(0);
1034 inst.extend_from_slice(&1u16.to_le_bytes());
1035 inst.push(1);
1036 inst.push(0);
1037
1038 let event = parser
1039 .process_segment(&inst, &trace_context)
1040 .unwrap()
1041 .expect("complete event");
1042 match &event.instructions[0] {
1043 ParsedInstruction::Backtrace {
1044 requested_depth,
1045 flags,
1046 status,
1047 frames,
1048 ..
1049 } => {
1050 assert_eq!(*requested_depth, 8);
1051 assert_eq!(*flags, BACKTRACE_FLAG_INLINE);
1052 assert_eq!(*status, BacktraceStatus::UnsupportedCfi);
1053 assert_eq!(frames.len(), 1);
1054 assert_eq!(frames[0].module_cookie, 0x1122_3344_5566_7788);
1055 assert_eq!(frames[0].pc, 0x1234);
1056 assert_eq!(frames[0].raw_ip, 0x7fff_0000_1234);
1057 }
1058 other => panic!("unexpected instruction: {other:?}"),
1059 }
1060 }
1061
1062 #[test]
1063 fn test_format_string_only_consumes_contiguous_variables() {
1064 let event = ParsedTraceEvent {
1065 trace_id: 1,
1066 timestamp: 0,
1067 pid: 10,
1068 tid: 11,
1069 instructions: vec![
1070 ParsedInstruction::PrintString {
1071 content: "{} {}".to_string(),
1072 },
1073 ParsedInstruction::PrintString {
1074 content: "literal".to_string(),
1075 },
1076 ParsedInstruction::PrintVariable {
1077 name: "value".to_string(),
1078 type_encoding: TypeKind::I32,
1079 formatted_value: "42".to_string(),
1080 raw_data: vec![42],
1081 },
1082 ParsedInstruction::EndInstruction {
1083 total_instructions: 3,
1084 execution_status: 0,
1085 },
1086 ],
1087 };
1088
1089 assert_eq!(
1090 event.to_formatted_output(),
1091 vec![
1092 "{} {}".to_string(),
1093 "literal".to_string(),
1094 "value (I32): 42".to_string(),
1095 ]
1096 );
1097 }
1098}