From a681cb1a8a3bad98fc6cf45711397ea5cdb18654 Mon Sep 17 00:00:00 2001 From: swananan Date: Mon, 6 Oct 2025 16:26:35 +0800 Subject: [PATCH] refactor: remove legacy PrintFormat path and test helpers; clean up unused code --- docs/architecture.md | 1 - docs/zh/architecture.md | 1 - ghostscope-compiler/src/ebpf/codegen.rs | 1280 +------------------ ghostscope-protocol/src/format_printer.rs | 543 +------- ghostscope-protocol/src/lib.rs | 6 +- ghostscope-protocol/src/streaming_parser.rs | 70 +- ghostscope-protocol/src/trace_event.rs | 35 +- ghostscope-protocol/src/type_kind.rs | 4 - 8 files changed, 24 insertions(+), 1916 deletions(-) diff --git a/docs/architecture.md b/docs/architecture.md index 5244bb01..3ae70917 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -307,7 +307,6 @@ GhostScope uses an **instruction-based protocol** for flexible trace event repre | **PrintStringIndex** | 0x01 | Print static string (indexed) | | **PrintVariableIndex** | 0x02 | Print simple variable with type info | | **PrintComplexVariable** | 0x03 | Print struct/array with access path | -| **PrintFormat** | 0x04 | Formatted print with multiple arguments | | **PrintComplexFormat** | 0x05 | Formatted print with complex variables | | **Backtrace** | 0x10 | Stack backtrace with frame addresses | | **EndInstruction** | 0xFF | Marks end of instruction sequence | diff --git a/docs/zh/architecture.md b/docs/zh/architecture.md index 8f91d353..33dd9d32 100644 --- a/docs/zh/architecture.md +++ b/docs/zh/architecture.md @@ -307,7 +307,6 @@ GhostScope 使用**基于指令的协议**实现灵活的追踪事件表示: | **PrintStringIndex** | 0x01 | 打印静态字符串(索引化) | | **PrintVariableIndex** | 0x02 | 打印带类型信息的简单变量 | | **PrintComplexVariable** | 0x03 | 打印带访问路径的结构体/数组 | -| **PrintFormat** | 0x04 | 带多个参数的格式化打印 | | **PrintComplexFormat** | 0x05 | 带复杂变量的格式化打印 | | **Backtrace** | 0x10 | 带栈帧地址的栈回溯 | | **EndInstruction** | 0xFF | 标记指令序列结束 | diff --git a/ghostscope-compiler/src/ebpf/codegen.rs b/ghostscope-compiler/src/ebpf/codegen.rs index 82f87b5b..48fc10c5 100644 --- a/ghostscope-compiler/src/ebpf/codegen.rs +++ b/ghostscope-compiler/src/ebpf/codegen.rs @@ -7,8 +7,8 @@ use super::context::{CodeGenError, EbpfContext, Result}; use crate::script::{PrintStatement, Program, Statement}; use aya_ebpf_bindings::bindings::bpf_func_id::BPF_FUNC_probe_read_user; use ghostscope_protocol::trace_event::{ - BacktraceData, InstructionHeader, PrintComplexVariableData, PrintFormatData, - PrintStringIndexData, PrintVariableIndexData, VariableStatus, + BacktraceData, InstructionHeader, PrintComplexVariableData, PrintStringIndexData, + PrintVariableIndexData, VariableStatus, }; use ghostscope_protocol::{InstructionType, TraceContext, TypeKind}; use inkwell::values::{BasicValueEnum, IntValue, PointerValue}; @@ -25,26 +25,6 @@ struct PrintVarRuntimeMeta { data_len_limit: usize, } -/// Information about a variable in formatted print -#[allow(dead_code)] -#[derive(Debug, Clone)] -struct FormatVariableInfo { - var_name: String, - var_name_index: u16, - type_encoding: TypeKind, - data_size: usize, - value_source: FormatValueSource, -} - -/// Source of the value for a format variable -#[allow(dead_code)] -#[derive(Debug, Clone)] -enum FormatValueSource { - Variable, // Read from DWARF/register - StringLiteral, // String literal value handled via string table - IntegerLiteral, // Integer literal value serialized directly -} - /// Source for complex formatted argument data #[derive(Debug, Clone)] enum ComplexArgSource<'ctx> { @@ -1103,7 +1083,7 @@ impl<'ctx> EbpfContext<'ctx> { } } - /// Compile formatted print statement: collect all variable data and send as PrintFormat/PrintComplexFormat instruction + /// Compile formatted print statement: collect all variable data and send as PrintComplexFormat instruction fn compile_formatted_print( &mut self, format: &str, @@ -1123,22 +1103,6 @@ impl<'ctx> EbpfContext<'ctx> { Ok(1) } - /// Get the size in bytes for a given type encoding - #[allow(dead_code)] - fn get_type_size(&self, type_encoding: TypeKind) -> usize { - match type_encoding { - TypeKind::U8 | TypeKind::I8 | TypeKind::Bool | TypeKind::Char => 1, - TypeKind::U16 | TypeKind::I16 => 2, - TypeKind::U32 | TypeKind::I32 | TypeKind::F32 => 4, - TypeKind::U64 | TypeKind::I64 | TypeKind::F64 | TypeKind::Pointer => 8, - TypeKind::CString | TypeKind::String => 256, // Default string buffer size - _ => { - warn!("Unknown type size for {:?}, using 8 bytes", type_encoding); - 8 - } - } - } - /// Resolve variable with correct priority: script variables first, then DWARF variables /// This method is copied from protocol.rs to maintain functionality pub fn resolve_variable_with_priority(&mut self, var_name: &str) -> Result<(u16, TypeKind)> { @@ -1309,485 +1273,6 @@ impl<'ctx> EbpfContext<'ctx> { } } - /// Generate eBPF code for PrintFormat instruction (true single instruction implementation) - #[allow(dead_code)] - fn generate_print_format_instruction( - &mut self, - format_string_index: u16, - variable_infos: &[FormatVariableInfo], - ) -> Result<()> { - info!( - "Generating true single PrintFormat instruction: format_index={}, {} variables", - format_string_index, - variable_infos.len() - ); - - // Calculate total instruction size: - // InstructionHeader + PrintFormatData + variable data - let mut total_variable_data_size = 0; - for var_info in variable_infos { - if let FormatValueSource::Variable = &var_info.value_source { - // Each variable header is 8 bytes: - // var_name_index(2) + type_encoding(1) + type_index(2) + status(1) + data_len(2) - // Then followed by `data_len` bytes of data - total_variable_data_size += 8 + var_info.data_size; - } - } - - let instruction_data_size = - std::mem::size_of::() + total_variable_data_size; - let total_instruction_size = - std::mem::size_of::() + instruction_data_size; - - info!( - "PrintFormat instruction size: {} bytes (header: {}, data: {}, variables: {})", - total_instruction_size, - std::mem::size_of::(), - std::mem::size_of::(), - total_variable_data_size - ); - - // Allocate buffer using existing method - let buffer = self.create_instruction_buffer(); - - // Avoid memset; global buffer is zero-initialized and we write explicit fields. - - // Write InstructionHeader - let inst_type_val = self - .context - .i8_type() - .const_int(InstructionType::PrintFormat as u64, false); - self.builder - .build_store(buffer, inst_type_val) - .map_err(|e| CodeGenError::LLVMError(format!("Failed to store inst_type: {}", e)))?; - - // data_length at offset 1 - let data_length_ptr = unsafe { - self.builder - .build_gep( - self.context.i8_type(), - buffer, - &[self.context.i32_type().const_int(1, false)], - "data_length_ptr", - ) - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to get data_length GEP: {}", e)) - })? - }; - let data_length_i16_ptr = self - .builder - .build_pointer_cast( - data_length_ptr, - self.context.ptr_type(inkwell::AddressSpace::default()), - "data_length_i16_ptr", - ) - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to cast data_length pointer: {}", e)) - })?; - let data_length_val = self - .context - .i16_type() - .const_int(instruction_data_size as u64, false); - self.builder - .build_store(data_length_i16_ptr, data_length_val) - .map_err(|e| CodeGenError::LLVMError(format!("Failed to store data_length: {}", e)))?; - - // Write PrintFormatData at offset 4 (after InstructionHeader) - let format_data_ptr = unsafe { - self.builder - .build_gep( - self.context.i8_type(), - buffer, - &[self.context.i32_type().const_int(4, false)], - "format_data_ptr", - ) - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to get format_data GEP: {}", e)) - })? - }; - - // format_string_index at offset 0 within PrintFormatData - let format_string_index_ptr = self - .builder - .build_pointer_cast( - format_data_ptr, - self.context.ptr_type(inkwell::AddressSpace::default()), - "format_string_index_ptr", - ) - .map_err(|e| { - CodeGenError::LLVMError(format!( - "Failed to cast format_string_index pointer: {}", - e - )) - })?; - let format_index_val = self - .context - .i16_type() - .const_int(format_string_index as u64, false); - self.builder - .build_store(format_string_index_ptr, format_index_val) - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to store format_string_index: {}", e)) - })?; - - // arg_count at offset 2 within PrintFormatData - let arg_count_ptr = unsafe { - self.builder - .build_gep( - self.context.i8_type(), - format_data_ptr, - &[self.context.i32_type().const_int(2, false)], - "arg_count_ptr", - ) - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to get arg_count GEP: {}", e)) - })? - }; - - // Only count actual variables, not string/integer literals - let actual_var_count = variable_infos - .iter() - .filter(|vi| matches!(vi.value_source, FormatValueSource::Variable)) - .count(); - let arg_count_val = self - .context - .i8_type() - .const_int(actual_var_count as u64, false); - self.builder - .build_store(arg_count_ptr, arg_count_val) - .map_err(|e| CodeGenError::LLVMError(format!("Failed to store arg_count: {}", e)))?; - - // reserved field at offset 3 (set to 0) - let reserved_ptr = unsafe { - self.builder - .build_gep( - self.context.i8_type(), - format_data_ptr, - &[self.context.i32_type().const_int(3, false)], - "reserved_ptr", - ) - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to get reserved GEP: {}", e)) - })? - }; - let reserved_val = self.context.i8_type().const_int(0, false); - self.builder - .build_store(reserved_ptr, reserved_val) - .map_err(|e| CodeGenError::LLVMError(format!("Failed to store reserved: {}", e)))?; - - // Write variable data starting after PrintFormatData - let mut current_offset = 4 + std::mem::size_of::(); - for var_info in variable_infos { - if let FormatValueSource::Variable = &var_info.value_source { - info!( - "Writing variable '{}' at offset {}", - var_info.var_name, current_offset - ); - - // Resolve type index: prefer DWARF when available, else synthesize for script vars - let type_index = match self.query_dwarf_for_variable(&var_info.var_name)? { - Some(v) => match v.dwarf_type { - Some(ref t) => self.trace_context.add_type(t.clone()), - None => { - return Err(CodeGenError::DwarfError(format!( - "Variable '{}' missing DWARF type for formatted print", - var_info.var_name - ))); - } - }, - None => { - if self.variable_exists(&var_info.var_name) { - self.add_synthesized_type_index_for_kind(var_info.type_encoding) - } else { - return Err(CodeGenError::VariableNotFound(format!( - "Variable '{}' not found for formatted print", - var_info.var_name - ))); - } - } - }; - - // Write variable header: [var_name_index:u16, type_encoding:u8, type_index:u16, data_len:u16] - let var_header_ptr = unsafe { - self.builder - .build_gep( - self.context.i8_type(), - buffer, - &[self - .context - .i32_type() - .const_int(current_offset as u64, false)], - "var_header_ptr", - ) - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to get var_header GEP: {}", e)) - })? - }; - - // var_name_index at offset 0 - let var_name_index_ptr = self - .builder - .build_pointer_cast( - var_header_ptr, - self.context.ptr_type(inkwell::AddressSpace::default()), - "var_name_index_ptr", - ) - .map_err(|e| { - CodeGenError::LLVMError(format!( - "Failed to cast var_name_index pointer: {}", - e - )) - })?; - let var_name_index_val = self - .context - .i16_type() - .const_int(var_info.var_name_index as u64, false); - self.builder - .build_store(var_name_index_ptr, var_name_index_val) - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to store var_name_index: {}", e)) - })?; - - // type_encoding at offset 2 - let type_encoding_ptr = unsafe { - self.builder - .build_gep( - self.context.i8_type(), - var_header_ptr, - &[self.context.i32_type().const_int(2, false)], - "type_encoding_ptr", - ) - .map_err(|e| { - CodeGenError::LLVMError(format!( - "Failed to get type_encoding GEP: {}", - e - )) - })? - }; - let type_encoding_val = self - .context - .i8_type() - .const_int(var_info.type_encoding as u8 as u64, false); - self.builder - .build_store(type_encoding_ptr, type_encoding_val) - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to store type_encoding: {}", e)) - })?; - - // data_len at offset 3..4 - let data_len_ptr = unsafe { - self.builder - .build_gep( - self.context.i8_type(), - var_header_ptr, - &[self.context.i32_type().const_int(3, false)], - "data_len_ptr", - ) - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to get data_len GEP: {}", e)) - })? - }; - let data_len_i16_ptr = self - .builder - .build_pointer_cast( - data_len_ptr, - self.context.ptr_type(AddressSpace::default()), - "data_len_i16_ptr", - ) - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to cast data_len ptr: {}", e)) - })?; - let data_len_val = self - .context - .i16_type() - .const_int(var_info.data_size as u64, false); - self.builder - .build_store(data_len_i16_ptr, data_len_val) - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to store data_len: {}", e)) - })?; - - // type_index at offset 5..6 - let type_index_ptr = unsafe { - self.builder - .build_gep( - self.context.i8_type(), - var_header_ptr, - &[self.context.i32_type().const_int(5, false)], - "type_index_ptr", - ) - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to get type_index GEP: {}", e)) - })? - }; - let type_index_i16_ptr = self - .builder - .build_pointer_cast( - type_index_ptr, - self.context.ptr_type(AddressSpace::default()), - "type_index_i16_ptr", - ) - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to cast type_index ptr: {}", e)) - })?; - let type_index_val = self.context.i16_type().const_int(type_index as u64, false); - self.builder - .build_store(type_index_i16_ptr, type_index_val) - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to store type_index: {}", e)) - })?; - - // status at offset 7 - let status_ptr = unsafe { - self.builder - .build_gep( - self.context.i8_type(), - var_header_ptr, - &[self.context.i32_type().const_int(7, false)], - "status_ptr", - ) - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to get status GEP: {}", e)) - })? - }; - self.builder - .build_store(status_ptr, self.context.i8_type().const_int(0, false)) - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to store status: {}", e)) - })?; - - // Generate variable data reading at offset 8 - let var_data_ptr = unsafe { - self.builder - .build_gep( - self.context.i8_type(), - var_header_ptr, - &[self.context.i32_type().const_int(8, false)], - "var_data_ptr", - ) - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to get var_data GEP: {}", e)) - })? - }; - - // Read variable data using existing DWARF resolution logic - match self.resolve_variable_value(&var_info.var_name, var_info.type_encoding) { - Ok(var_data) => { - // Store the resolved variable data - self.store_variable_data( - var_data_ptr, - var_data, - var_info.type_encoding, - var_info.data_size, - )?; - } - Err(e) => { - info!( - "Variable '{}' read failed: {}, skipping data", - var_info.var_name, e - ); - // For failed reads, we could either skip or fill with error marker. - // Zero exactly data_size bytes to avoid overwriting subsequent variable headers/data - let mut remaining = var_info.data_size as u64; - let mut off: u64 = 0; - - let gep_off = - |byte_off: u64| -> Result> { - let ptr = unsafe { - self.builder - .build_gep( - self.context.i8_type(), - var_data_ptr, - &[self.context.i32_type().const_int(byte_off, false)], - "var_data_ptr_off", - ) - .map_err(|e| { - CodeGenError::LLVMError(format!( - "Failed to get var_data GEP(off={}): {}", - byte_off, e - )) - })? - }; - Ok(ptr) - }; - - if remaining >= 8 { - let p = gep_off(off)?; - let p_cast = self - .builder - .build_pointer_cast( - p, - self.context.ptr_type(inkwell::AddressSpace::default()), - "var_data_u64_ptr", - ) - .map_err(|e| CodeGenError::LLVMError(format!("cast u64: {}", e)))?; - self.builder - .build_store(p_cast, self.context.i64_type().const_zero()) - .map_err(|e| { - CodeGenError::LLVMError(format!("store u64: {}", e)) - })?; - remaining -= 8; - off += 8; - } - if remaining >= 4 { - let p = gep_off(off)?; - let p_cast = self - .builder - .build_pointer_cast( - p, - self.context.ptr_type(inkwell::AddressSpace::default()), - "var_data_u32_ptr", - ) - .map_err(|e| CodeGenError::LLVMError(format!("cast u32: {}", e)))?; - self.builder - .build_store(p_cast, self.context.i32_type().const_zero()) - .map_err(|e| { - CodeGenError::LLVMError(format!("store u32: {}", e)) - })?; - remaining -= 4; - off += 4; - } - if remaining >= 2 { - let p = gep_off(off)?; - let p_cast = self - .builder - .build_pointer_cast( - p, - self.context.ptr_type(inkwell::AddressSpace::default()), - "var_data_u16_ptr", - ) - .map_err(|e| CodeGenError::LLVMError(format!("cast u16: {}", e)))?; - self.builder - .build_store(p_cast, self.context.i16_type().const_zero()) - .map_err(|e| { - CodeGenError::LLVMError(format!("store u16: {}", e)) - })?; - remaining -= 2; - off += 2; - } - if remaining >= 1 { - let p = gep_off(off)?; - self.builder - .build_store(p, self.context.i8_type().const_zero()) - .map_err(|e| CodeGenError::LLVMError(format!("store u8: {}", e)))?; - } - } - } - - current_offset += 8 + var_info.data_size; - } - } - - // Send the complete instruction via ringbuf using existing method - self.write_to_accumulation_buffer_or_send(buffer, total_instruction_size as u64)?; - - info!( - "Successfully generated true single PrintFormat instruction with {} variables", - actual_var_count - ); - Ok(()) - } - /// Generate eBPF code for PrintComplexFormat instruction with runtime reads for variables fn generate_print_complex_format_instruction( &mut self, @@ -2485,302 +1970,6 @@ impl<'ctx> EbpfContext<'ctx> { Ok(()) } - /// Store variable data at the specified pointer location - #[allow(dead_code)] - fn store_variable_data( - &mut self, - var_data_ptr: PointerValue<'ctx>, - var_data: BasicValueEnum<'ctx>, - type_encoding: TypeKind, - data_size: usize, - ) -> Result<()> { - match data_size { - 1 => { - // Store as i8 - let truncated = match var_data { - BasicValueEnum::IntValue(int_val) => self - .builder - .build_int_truncate(int_val, self.context.i8_type(), "truncated_i8") - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to truncate to i8: {}", e)) - })?, - _ => { - return Err(CodeGenError::LLVMError( - "Expected integer value for integer type".to_string(), - )); - } - }; - self.builder - .build_store(var_data_ptr, truncated) - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to store i8 data: {}", e)) - })?; - } - 2 => { - // Store as i16 - let truncated = match var_data { - BasicValueEnum::IntValue(int_val) => self - .builder - .build_int_truncate(int_val, self.context.i16_type(), "truncated_i16") - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to truncate to i16: {}", e)) - })?, - _ => { - return Err(CodeGenError::LLVMError( - "Expected integer value for integer type".to_string(), - )); - } - }; - let i16_ptr = self - .builder - .build_pointer_cast( - var_data_ptr, - self.context.ptr_type(AddressSpace::default()), - "i16_ptr", - ) - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to cast to i16 ptr: {}", e)) - })?; - self.builder.build_store(i16_ptr, truncated).map_err(|e| { - CodeGenError::LLVMError(format!("Failed to store i16 data: {}", e)) - })?; - } - 4 => { - // Store as i32 or f32 - match var_data { - BasicValueEnum::IntValue(int_val) => { - let truncated = self - .builder - .build_int_truncate(int_val, self.context.i32_type(), "truncated_i32") - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to truncate to i32: {}", e)) - })?; - let i32_ptr = self - .builder - .build_pointer_cast( - var_data_ptr, - self.context.ptr_type(AddressSpace::default()), - "i32_ptr", - ) - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to cast to i32 ptr: {}", e)) - })?; - self.builder.build_store(i32_ptr, truncated).map_err(|e| { - CodeGenError::LLVMError(format!("Failed to store i32 data: {}", e)) - })?; - } - BasicValueEnum::FloatValue(float_val) => { - let f32_ptr = self - .builder - .build_pointer_cast( - var_data_ptr, - self.context.ptr_type(AddressSpace::default()), - "f32_ptr", - ) - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to cast to f32 ptr: {}", e)) - })?; - self.builder.build_store(f32_ptr, float_val).map_err(|e| { - CodeGenError::LLVMError(format!("Failed to store f32 data: {}", e)) - })?; - } - _ => { - return Err(CodeGenError::LLVMError( - "Expected integer or float value for 4-byte type".to_string(), - )); - } - } - } - 8 => { - // Store as i64, f64, or pointer - match var_data { - BasicValueEnum::IntValue(int_val) => { - let i64_ptr = self - .builder - .build_pointer_cast( - var_data_ptr, - self.context.ptr_type(AddressSpace::default()), - "i64_ptr", - ) - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to cast to i64 ptr: {}", e)) - })?; - self.builder.build_store(i64_ptr, int_val).map_err(|e| { - CodeGenError::LLVMError(format!("Failed to store i64 data: {}", e)) - })?; - } - BasicValueEnum::FloatValue(float_val) => { - let f64_ptr = self - .builder - .build_pointer_cast( - var_data_ptr, - self.context.ptr_type(AddressSpace::default()), - "f64_ptr", - ) - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to cast to f64 ptr: {}", e)) - })?; - self.builder.build_store(f64_ptr, float_val).map_err(|e| { - CodeGenError::LLVMError(format!("Failed to store f64 data: {}", e)) - })?; - } - BasicValueEnum::PointerValue(ptr_val) => { - // Store pointer as u64 - let ptr_int = self - .builder - .build_ptr_to_int(ptr_val, self.context.i64_type(), "ptr_as_int") - .map_err(|e| { - CodeGenError::LLVMError(format!( - "Failed to convert ptr to int: {}", - e - )) - })?; - let i64_ptr = self - .builder - .build_pointer_cast( - var_data_ptr, - self.context.ptr_type(AddressSpace::default()), - "i64_ptr", - ) - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to cast to i64 ptr: {}", e)) - })?; - self.builder.build_store(i64_ptr, ptr_int).map_err(|e| { - CodeGenError::LLVMError(format!("Failed to store pointer data: {}", e)) - })?; - } - _ => { - return Err(CodeGenError::LLVMError( - "Expected integer, float, or pointer value for 8-byte type".to_string(), - )); - } - } - } - _ => { - // Handle string or other variable-length data - match var_data { - BasicValueEnum::PointerValue(str_ptr) => { - // Copy string data byte by byte without using alloca (verifier-friendly) - let i8_type = self.context.i8_type(); - let i32_type = self.context.i32_type(); - - // Get current function and create blocks - let current_function = self - .builder - .get_insert_block() - .ok_or_else(|| { - CodeGenError::LLVMError("No current basic block".to_string()) - })? - .get_parent() - .ok_or_else(|| { - CodeGenError::LLVMError("No parent function".to_string()) - })?; - - let pre_block = self.builder.get_insert_block().ok_or_else(|| { - CodeGenError::LLVMError("No current basic block".to_string()) - })?; - let check_block = self - .context - .append_basic_block(current_function, "copy_check"); - let loop_block = self - .context - .append_basic_block(current_function, "copy_loop"); - let end_block = self - .context - .append_basic_block(current_function, "copy_end"); - - self.builder - .build_unconditional_branch(check_block) - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to branch to check: {}", e)) - })?; - - // check: i < data_size ? loop : end - self.builder.position_at_end(check_block); - let i_phi = self.builder.build_phi(i32_type, "i").map_err(|e| { - CodeGenError::LLVMError(format!("Failed to build phi: {}", e)) - })?; - let zero = i32_type.const_zero(); - i_phi.add_incoming(&[(&zero, pre_block)]); - let i_val = i_phi.as_basic_value().into_int_value(); - let size_limit = i32_type.const_int(data_size as u64, false); - let cond = self - .builder - .build_int_compare( - inkwell::IntPredicate::ULT, - i_val, - size_limit, - "cond", - ) - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to build condition: {}", e)) - })?; - self.builder - .build_conditional_branch(cond, loop_block, end_block) - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to build br: {}", e)) - })?; - - // loop: copy one byte and increment - self.builder.position_at_end(loop_block); - let src_byte_ptr = unsafe { - self.builder - .build_gep(i8_type, str_ptr, &[i_val], "src_byte_ptr") - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to get src GEP: {}", e)) - })? - }; - let dst_byte_ptr = unsafe { - self.builder - .build_gep(i8_type, var_data_ptr, &[i_val], "dst_byte_ptr") - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to get dst GEP: {}", e)) - })? - }; - let byte_val = self - .builder - .build_load(i8_type, src_byte_ptr, "byte_val") - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to load byte: {}", e)) - })?; - self.builder - .build_store(dst_byte_ptr, byte_val) - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to store byte: {}", e)) - })?; - let next_i = self - .builder - .build_int_add(i_val, i32_type.const_int(1, false), "next_i") - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to add: {}", e)) - })?; - // back to check, add backedge to phi - self.builder - .build_unconditional_branch(check_block) - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to back br: {}", e)) - })?; - let loop_block_ended = self - .builder - .get_insert_block() - .ok_or_else(|| CodeGenError::LLVMError("No loop block".to_string()))?; - i_phi.add_incoming(&[(&next_i, loop_block_ended)]); - - // end - self.builder.position_at_end(end_block); - } - _ => { - return Err(CodeGenError::LLVMError(format!( - "Unsupported variable data type for size {}: {:?}", - data_size, type_encoding - ))); - } - } - } - } - Ok(()) - } - /// Generate eBPF code for PrintStringIndex instruction pub fn generate_print_string_index(&mut self, string_index: u16) -> Result<()> { info!( @@ -3460,29 +2649,6 @@ impl<'ctx> EbpfContext<'ctx> { // removed legacy process_complex_variable_print — unified resolver path is used - /// Generate print instruction with both legacy type encoding and new type info - #[allow(dead_code)] - fn generate_print_variable_with_type_info( - &mut self, - var_name_index: u16, - type_index: u16, - type_encoding: TypeKind, - var_name: &str, - _variable_with_eval: Option<&ghostscope_dwarf::VariableWithEvaluation>, - ) -> Result<()> { - info!( - "Generating enhanced print variable instruction: {} (var_idx={}, type_idx={}, encoding={:?})", - var_name, var_name_index, type_index, type_encoding - ); - - // Prefer complex variable runtime path even for top-level if requested - let _dummy_eval = ghostscope_dwarf::EvaluationResult::Optimized; - let _size = 0usize; - let _ = (type_index, type_encoding, var_name); - // No-op as this path is now superseded by process_complex_variable_print - Ok(()) - } - /// Generate PrintComplexVariable instruction and copy data at runtime using probe_read_user fn generate_print_complex_variable_runtime( &mut self, @@ -4016,446 +3182,6 @@ impl<'ctx> EbpfContext<'ctx> { Ok(()) } - - /// Generate eBPF code for PrintComplexVariable instruction with full type info - #[allow(dead_code)] - fn generate_print_complex_variable_instruction( - &mut self, - var_name_index: u16, - type_index: u16, - access_path: &str, - ) -> Result<()> { - info!( - "Generating PrintComplexVariable instruction: access_path='{}', var_idx={}, type_idx={}", - access_path, var_name_index, type_index - ); - - // For now, create a simple placeholder instruction similar to PrintVariableError - // TODO: Implement full complex variable data generation when DWARF integration is complete - - let inst_buffer = self.create_instruction_buffer(); - - // Use a static size for now - in real implementation this would be dynamic - // based on actual variable data size - let inst_size = self.context.i64_type().const_int( - (std::mem::size_of::() - + std::mem::size_of::() - + 64) // Extra space for access path and variable data - as u64, - false, - ); - - // Avoid memset; global instruction buffer is zero-initialized - - // Send via ringbuf - the actual instruction data will be filled by higher-level code - // that has access to the runtime variable values - self.send_instruction_via_ringbuf(inst_buffer, inst_size)?; - - info!( - "PrintComplexVariable instruction generated successfully (placeholder): var_idx={}, type_idx={}, access_path={}", - var_name_index, type_index, access_path - ); - Ok(()) - } - - /// Convert LLVM value to byte representation for embedding in trace events - #[allow(dead_code)] - fn llvm_value_to_bytes( - &mut self, - llvm_value: BasicValueEnum<'ctx>, - dwarf_type: &ghostscope_dwarf::TypeInfo, - ) -> Result> { - info!( - "Converting LLVM value to bytes: type_name={}, size={}", - dwarf_type.type_name(), - dwarf_type.size() - ); - - let mut bytes = Vec::new(); - let type_size = dwarf_type.size() as usize; - - match llvm_value { - BasicValueEnum::IntValue(int_val) => { - // Convert integer value to bytes in little endian format - let bit_width = int_val.get_type().get_bit_width(); - match bit_width { - 8 => { - let val = int_val.get_zero_extended_constant().unwrap_or(0) as u8; - bytes.push(val); - } - 16 => { - let val = int_val.get_zero_extended_constant().unwrap_or(0) as u16; - bytes.extend_from_slice(&val.to_le_bytes()); - } - 32 => { - let val = int_val.get_zero_extended_constant().unwrap_or(0) as u32; - bytes.extend_from_slice(&val.to_le_bytes()); - } - 64 => { - let val = int_val.get_zero_extended_constant().unwrap_or(0); - bytes.extend_from_slice(&val.to_le_bytes()); - } - _ => { - return Err(CodeGenError::LLVMError(format!( - "Unsupported integer bit width: {}", - bit_width - ))); - } - } - } - BasicValueEnum::FloatValue(float_val) => { - // Convert float value to bytes - if float_val.get_type().get_context().f32_type() == float_val.get_type() { - // f32 - if let Some(const_val) = float_val.get_constant() { - let val_bits = const_val.0.to_bits(); - bytes.extend_from_slice(&(val_bits as u32).to_le_bytes()); - } else { - // For non-constant values, use placeholder - bytes.extend_from_slice(&0u32.to_le_bytes()); - } - } else if float_val.get_type().get_context().f64_type() == float_val.get_type() { - // f64 - if let Some(const_val) = float_val.get_constant() { - let val_bits = const_val.0.to_bits(); - bytes.extend_from_slice(&val_bits.to_le_bytes()); - } else { - // For non-constant values, use placeholder - bytes.extend_from_slice(&0u64.to_le_bytes()); - } - } else { - return Err(CodeGenError::LLVMError( - "Unsupported float type".to_string(), - )); - } - } - BasicValueEnum::PointerValue(_ptr_val) => { - // For pointer values, store as 64-bit address - // Note: At compile time we can't get the actual runtime address, - // so this is a placeholder that would be filled at runtime - bytes.extend_from_slice(&0u64.to_le_bytes()); - } - BasicValueEnum::StructValue(_struct_val) => { - // For struct values, we'd need to iterate through fields - // For now, pad with zeros to match the expected size - bytes.resize(type_size, 0); - } - BasicValueEnum::ArrayValue(_array_val) => { - // For array values, we'd need to iterate through elements - // For now, pad with zeros to match the expected size - bytes.resize(type_size, 0); - } - _ => { - return Err(CodeGenError::LLVMError(format!( - "Unsupported LLVM value type: {:?}", - llvm_value - ))); - } - } - - // Ensure we have the correct size - if bytes.len() < type_size { - bytes.resize(type_size, 0); - } else if bytes.len() > type_size { - bytes.truncate(type_size); - } - - info!( - "Converted LLVM value to {} bytes: {:?}", - bytes.len(), - &bytes[..std::cmp::min(bytes.len(), 16)] // Log first 16 bytes - ); - - Ok(bytes) - } - - /// Generate PrintComplexVariable instruction with actual data embedded - #[allow(dead_code)] - fn generate_print_complex_variable_instruction_with_data( - &mut self, - var_name_index: u16, - type_index: u16, - access_path: &str, - variable_data: &[u8], - ) -> Result<()> { - info!( - "Generating PrintComplexVariable instruction with data: access_path='{}', var_idx={}, type_idx={}, data_len={}", - access_path, var_name_index, type_index, variable_data.len() - ); - - let inst_buffer = self.create_instruction_buffer(); - - let access_path_bytes = access_path.as_bytes(); - let access_path_len = access_path_bytes.len(); - let data_len = variable_data.len(); - - // Calculate total instruction size - let total_size = std::mem::size_of::() - + std::mem::size_of::() - + access_path_len - + data_len; - - let inst_size = self.context.i64_type().const_int(total_size as u64, false); - - // Avoid memset; global buffer is zero-initialized - - // Write InstructionHeader - let _header_ptr = self - .builder - .build_pointer_cast( - inst_buffer, - self.context.ptr_type(AddressSpace::default()), - "header_ptr", - ) - .map_err(|e| CodeGenError::LLVMError(format!("Failed to cast header ptr: {}", e)))?; - - // Set instruction type - let inst_type_val = self - .context - .i8_type() - .const_int(InstructionType::PrintComplexVariable as u64, false); - let inst_type_ptr = unsafe { - self.builder - .build_gep( - self.context.i8_type(), - inst_buffer, - &[self.context.i32_type().const_int(0, false)], - "inst_type_ptr", - ) - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to get inst_type GEP: {}", e)) - })? - }; - self.builder - .build_store(inst_type_ptr, inst_type_val) - .map_err(|e| CodeGenError::LLVMError(format!("Failed to store inst_type: {}", e)))?; - - // Set data length - let data_length_val = self.context.i16_type().const_int( - (std::mem::size_of::() + access_path_len + data_len) as u64, - false, - ); - let data_length_ptr = unsafe { - self.builder - .build_gep( - self.context.i8_type(), - inst_buffer, - &[self.context.i32_type().const_int(1, false)], - "data_length_ptr", - ) - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to get data_length GEP: {}", e)) - })? - }; - let data_length_ptr_cast = self - .builder - .build_pointer_cast( - data_length_ptr, - self.context.ptr_type(AddressSpace::default()), - "data_length_ptr_cast", - ) - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to cast data_length ptr: {}", e)) - })?; - self.builder - .build_store(data_length_ptr_cast, data_length_val) - .map_err(|e| CodeGenError::LLVMError(format!("Failed to store data_length: {}", e)))?; - - // Write PrintComplexVariableData - let data_offset = std::mem::size_of::(); - let data_ptr = unsafe { - self.builder - .build_gep( - self.context.i8_type(), - inst_buffer, - &[self.context.i32_type().const_int(data_offset as u64, false)], - "data_ptr", - ) - .map_err(|e| CodeGenError::LLVMError(format!("Failed to get data GEP: {}", e)))? - }; - - // Set var_name_index - let var_name_index_val = self - .context - .i16_type() - .const_int(var_name_index as u64, false); - let var_name_index_ptr = self - .builder - .build_pointer_cast( - data_ptr, - self.context.ptr_type(AddressSpace::default()), - "var_name_index_ptr", - ) - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to cast var_name_index ptr: {}", e)) - })?; - self.builder - .build_store(var_name_index_ptr, var_name_index_val) - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to store var_name_index: {}", e)) - })?; - - // Set type_index - let type_index_val = self.context.i16_type().const_int(type_index as u64, false); - let type_index_ptr = unsafe { - self.builder - .build_gep( - self.context.i16_type(), - var_name_index_ptr, - &[self.context.i32_type().const_int(1, false)], - "type_index_ptr", - ) - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to get type_index GEP: {}", e)) - })? - }; - self.builder - .build_store(type_index_ptr, type_index_val) - .map_err(|e| CodeGenError::LLVMError(format!("Failed to store type_index: {}", e)))?; - - // Set access_path_len - let access_path_len_val = self - .context - .i8_type() - .const_int(access_path_len as u64, false); - let access_path_len_offset = 4; // 2 * u16 = 4 bytes - let access_path_len_ptr = unsafe { - self.builder - .build_gep( - self.context.i8_type(), - data_ptr, - &[self - .context - .i32_type() - .const_int(access_path_len_offset, false)], - "access_path_len_ptr", - ) - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to get access_path_len GEP: {}", e)) - })? - }; - self.builder - .build_store(access_path_len_ptr, access_path_len_val) - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to store access_path_len: {}", e)) - })?; - - // Set data_len - let data_len_val = self.context.i16_type().const_int(data_len as u64, false); - let data_len_offset = 6; // 2 * u16 + 1 * u8 + 1 * u8 (padding) = 6 bytes - let data_len_ptr = unsafe { - self.builder - .build_gep( - self.context.i8_type(), - data_ptr, - &[self.context.i32_type().const_int(data_len_offset, false)], - "data_len_ptr", - ) - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to get data_len GEP: {}", e)) - })? - }; - let data_len_ptr_cast = self - .builder - .build_pointer_cast( - data_len_ptr, - self.context.ptr_type(AddressSpace::default()), - "data_len_ptr_cast", - ) - .map_err(|e| CodeGenError::LLVMError(format!("Failed to cast data_len ptr: {}", e)))?; - self.builder - .build_store(data_len_ptr_cast, data_len_val) - .map_err(|e| CodeGenError::LLVMError(format!("Failed to store data_len: {}", e)))?; - - // Write access path - let access_path_start_offset = std::mem::size_of::(); - let access_path_ptr = unsafe { - self.builder - .build_gep( - self.context.i8_type(), - data_ptr, - &[self - .context - .i32_type() - .const_int(access_path_start_offset as u64, false)], - "access_path_ptr", - ) - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to get access_path GEP: {}", e)) - })? - }; - - // Copy access path bytes - for (i, &byte) in access_path_bytes.iter().enumerate() { - let byte_ptr = unsafe { - self.builder - .build_gep( - self.context.i8_type(), - access_path_ptr, - &[self.context.i32_type().const_int(i as u64, false)], - &format!("access_path_byte_{}", i), - ) - .map_err(|e| { - CodeGenError::LLVMError(format!( - "Failed to get access_path byte GEP: {}", - e - )) - })? - }; - let byte_val = self.context.i8_type().const_int(byte as u64, false); - self.builder.build_store(byte_ptr, byte_val).map_err(|e| { - CodeGenError::LLVMError(format!("Failed to store access_path byte: {}", e)) - })?; - } - - // Write variable data - let variable_data_start_offset = access_path_start_offset + access_path_len; - let variable_data_ptr = unsafe { - self.builder - .build_gep( - self.context.i8_type(), - data_ptr, - &[self - .context - .i32_type() - .const_int(variable_data_start_offset as u64, false)], - "variable_data_ptr", - ) - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to get variable_data GEP: {}", e)) - })? - }; - - // Copy variable data bytes - for (i, &byte) in variable_data.iter().enumerate() { - let byte_ptr = unsafe { - self.builder - .build_gep( - self.context.i8_type(), - variable_data_ptr, - &[self.context.i32_type().const_int(i as u64, false)], - &format!("var_data_byte_{}", i), - ) - .map_err(|e| { - CodeGenError::LLVMError(format!("Failed to get var_data byte GEP: {}", e)) - })? - }; - let byte_val = self.context.i8_type().const_int(byte as u64, false); - self.builder.build_store(byte_ptr, byte_val).map_err(|e| { - CodeGenError::LLVMError(format!("Failed to store var_data byte: {}", e)) - })?; - } - - // Send via ringbuf - self.send_instruction_via_ringbuf(inst_buffer, inst_size)?; - - info!( - "PrintComplexVariable instruction with data generated successfully: var_idx={}, type_idx={}, access_path='{}', data_len={}", - var_name_index, type_index, access_path, variable_data.len() - ); - - Ok(()) - } } #[cfg(test)] diff --git a/ghostscope-protocol/src/format_printer.rs b/ghostscope-protocol/src/format_printer.rs index c6db195c..55ac3b52 100644 --- a/ghostscope-protocol/src/format_printer.rs +++ b/ghostscope-protocol/src/format_printer.rs @@ -1,22 +1,12 @@ -//! Format printer for PrintFormat instructions +//! Format printer for complex print instructions //! -//! This module handles the parsing and formatting of PrintFormat instructions -//! in the user space, converting raw variable data into formatted strings. +//! Converts PrintComplexVariable/PrintComplexFormat payloads into formatted text in user space. use crate::trace_context::TraceContext; use crate::trace_event::VariableStatus; use crate::type_info::TypeInfo; -use crate::TypeKind; -/// A parsed variable from PrintFormat instruction data -#[derive(Debug, Clone)] -pub struct ParsedVariable { - pub var_name_index: u16, - pub type_encoding: TypeKind, - pub type_index: Option, // Optional index into type table for perfect formatting - pub status: u8, // 0 OK; non-zero means error payload in data - pub data: Vec, -} +// Removed legacy simple variable wrapper; use complex paths only. /// A parsed complex variable from PrintComplexVariable instruction data #[derive(Debug, Clone)] @@ -28,35 +18,10 @@ pub struct ParsedComplexVariable { pub data: Vec, } -/// Format printer for converting PrintFormat data to formatted strings +/// Format printer for converting PrintComplexFormat data to formatted strings pub struct FormatPrinter; impl FormatPrinter { - /// Simple formatting helper: replace placeholders with variable values using TypeKind-based formatting - /// Note: This path does not use TraceContext type table; kept for unit tests and legacy behavior. - pub fn apply_format(format_string: &str, variables: &[ParsedVariable]) -> String { - let rendered: Vec = variables.iter().map(Self::format_variable_value).collect(); - Self::apply_format_strings(format_string, &rendered) - } - /// Convert PrintFormat instruction data into a formatted string - /// This is the main entry point for format printing - pub fn format_print_data( - format_string_index: u16, - variables: &[ParsedVariable], - trace_context: &TraceContext, - ) -> String { - // Get the format string from the trace context - let format_string = match trace_context.get_string(format_string_index) { - Some(s) => s, - None => { - return format!(""); - } - }; - - // Replace placeholders with variable values, preferring type_index formatting when available - Self::apply_format_with_context(format_string, variables, trace_context) - } - /// Format printer for converting PrintComplexFormat data to formatted strings pub fn format_complex_print_data( format_string_index: u16, @@ -94,121 +59,6 @@ impl FormatPrinter { Self::apply_format_strings(format_string, &formatted_vars) } - /// Apply formatting with type-aware variables using TraceContext when available - fn apply_format_with_context( - format_string: &str, - variables: &[ParsedVariable], - trace_context: &TraceContext, - ) -> String { - let mut result = String::new(); - let mut chars = format_string.chars().peekable(); - let mut var_index = 0; - - while let Some(ch) = chars.next() { - match ch { - '{' => { - if chars.peek() == Some(&'{') { - chars.next(); - result.push('{'); - } else { - let mut found_closing = false; - for inner_ch in chars.by_ref() { - if inner_ch == '}' { - found_closing = true; - break; - } - } - if found_closing { - if var_index < variables.len() { - let var = &variables[var_index]; - let formatted_value = if var.status != 0 { - // Status-aware error formatting. Prefer type_index when available for pointer suffix. - if let Some(type_index) = var.type_index { - if let Some(type_info) = trace_context.get_type(type_index) - { - let type_suffix = type_info.type_name(); - match var.status { - 1 => format!(" ({type_suffix}*)"), - 2 => { - let (errno, addr) = if var.data.len() >= 12 { - let errno = i32::from_le_bytes([ - var.data[0], - var.data[1], - var.data[2], - var.data[3], - ]); - let addr = u64::from_le_bytes([ - var.data[4], - var.data[5], - var.data[6], - var.data[7], - var.data[8], - var.data[9], - var.data[10], - var.data[11], - ]); - (Some(errno), Some(addr)) - } else { - (None, None) - }; - match (errno, addr) { - (Some(e), Some(a)) => { - format!(" ({type_suffix}*)") - } - _ => { - format!(" ({type_suffix}*)") - } - } - } - 3 => format!("
({type_suffix}*)"), - 4 => format!(" ({type_suffix}*)"), - 5 => format!(" ({type_suffix}*)"), - s => format!(" ({type_suffix}*)"), - } - } else { - format!("", var.status) - } - } else { - format!("", var.status) - } - } else if let Some(type_index) = var.type_index { - // Use perfect formatting via type info - match trace_context.get_type(type_index) { - Some(type_info) => { - Self::format_data_with_type_info(&var.data, type_info) - } - None => format!( - "", - ), - } - } else { - // Fallback to simple TypeKind path - Self::format_variable_value(var) - }; - result.push_str(&formatted_value); - var_index += 1; - } else { - result.push_str(""); - } - } else { - result.push_str(""); - } - } - } - '}' => { - if chars.peek() == Some(&'}') { - chars.next(); - result.push('}'); - } else { - result.push('}'); - } - } - _ => result.push(ch), - } - } - result - } - /// Apply formatting: replace {} placeholders with string values fn apply_format_strings(format_string: &str, formatted_values: &[String]) -> String { let mut result = String::new(); @@ -769,284 +619,6 @@ impl FormatPrinter { } } - /// Format multiple instructions with context (main entry point for enhanced formatting) - pub fn format_with_context( - instructions: &[crate::streaming_parser::ParsedInstruction], - trace_context: &TraceContext, - ) -> String { - let mut output = String::new(); - - for instruction in instructions { - match instruction { - crate::streaming_parser::ParsedInstruction::PrintVariable { - name, - type_encoding: _, - formatted_value, - raw_data, - } => { - // Use existing formatted value or reformat with context if available - if formatted_value.is_empty() && !raw_data.is_empty() { - // Try to reformat with better type information - let var_data = ParsedVariable { - var_name_index: 0, // dummy value - type_encoding: TypeKind::U8, // fallback - type_index: None, - status: 0, - data: raw_data.clone(), - }; - let reformatted = - Self::format_variable_with_context(&var_data, trace_context); - output.push_str(&format!("{name} = {reformatted}")); - } else { - output.push_str(&format!("{name} = {formatted_value}")); - } - } - crate::streaming_parser::ParsedInstruction::PrintComplexVariable { - name, - access_path, - type_index, - formatted_value, - raw_data, - } => { - // Use existing formatted value or reformat with context - if formatted_value.is_empty() && !raw_data.is_empty() { - let reformatted = Self::format_complex_variable( - 0, // dummy var_name_index - *type_index, - access_path, - raw_data, - trace_context, - ); - output.push_str(&reformatted); - } else if access_path.is_empty() { - output.push_str(&format!("{name} = {formatted_value}")); - } else { - output.push_str(&format!("{name}.{access_path} = {formatted_value}")); - } - } - crate::streaming_parser::ParsedInstruction::PrintString { content } => { - output.push_str(content); - } - crate::streaming_parser::ParsedInstruction::PrintFormat { formatted_output } => { - output.push_str(formatted_output); - } - crate::streaming_parser::ParsedInstruction::PrintComplexFormat { - formatted_output, - } => { - output.push_str(formatted_output); - } - // Add other instruction types as needed - _ => { - output.push_str(""); - } - } - output.push('\n'); - } - - output - } - - /// Format a variable with context-aware perfect formatting - /// NOTE: This method should not be used in normal operations. - /// Direct formatting is handled in streaming_parser.rs for better performance. - fn format_variable_with_context( - variable: &ParsedVariable, - trace_context: &TraceContext, - ) -> String { - // If we have type_index, use perfect formatting - if let Some(type_index) = variable.type_index { - match trace_context.get_type(type_index) { - Some(type_info) => { - return Self::format_data_with_type_info(&variable.data, type_info); - } - None => { - return format!( - "" - ); - } - } - } - - // No type_index available - this should not happen in normal operations - format!( - "", - variable.type_encoding - ) - } - - /// Format a single variable value as a string based on its type - pub(crate) fn format_variable_value(variable: &ParsedVariable) -> String { - match variable.type_encoding { - TypeKind::U8 => { - if variable.data.is_empty() { - "".to_string() - } else { - variable.data[0].to_string() - } - } - TypeKind::U16 => { - if variable.data.len() < 2 { - "".to_string() - } else { - let bytes: [u8; 2] = [variable.data[0], variable.data[1]]; - u16::from_le_bytes(bytes).to_string() - } - } - TypeKind::U32 => { - if variable.data.len() < 4 { - "".to_string() - } else { - let bytes: [u8; 4] = [ - variable.data[0], - variable.data[1], - variable.data[2], - variable.data[3], - ]; - u32::from_le_bytes(bytes).to_string() - } - } - TypeKind::U64 => { - if variable.data.len() < 8 { - "".to_string() - } else { - let bytes: [u8; 8] = [ - variable.data[0], - variable.data[1], - variable.data[2], - variable.data[3], - variable.data[4], - variable.data[5], - variable.data[6], - variable.data[7], - ]; - u64::from_le_bytes(bytes).to_string() - } - } - TypeKind::I8 => { - if variable.data.is_empty() { - "".to_string() - } else { - (variable.data[0] as i8).to_string() - } - } - TypeKind::I16 => { - if variable.data.len() < 2 { - "".to_string() - } else { - let bytes: [u8; 2] = [variable.data[0], variable.data[1]]; - i16::from_le_bytes(bytes).to_string() - } - } - TypeKind::I32 => { - if variable.data.len() < 4 { - "".to_string() - } else { - let bytes: [u8; 4] = [ - variable.data[0], - variable.data[1], - variable.data[2], - variable.data[3], - ]; - i32::from_le_bytes(bytes).to_string() - } - } - TypeKind::I64 => { - if variable.data.len() < 8 { - "".to_string() - } else { - let bytes: [u8; 8] = [ - variable.data[0], - variable.data[1], - variable.data[2], - variable.data[3], - variable.data[4], - variable.data[5], - variable.data[6], - variable.data[7], - ]; - i64::from_le_bytes(bytes).to_string() - } - } - TypeKind::F32 => { - if variable.data.len() < 4 { - "".to_string() - } else { - let bytes: [u8; 4] = [ - variable.data[0], - variable.data[1], - variable.data[2], - variable.data[3], - ]; - f32::from_le_bytes(bytes).to_string() - } - } - TypeKind::F64 => { - if variable.data.len() < 8 { - "".to_string() - } else { - let bytes: [u8; 8] = [ - variable.data[0], - variable.data[1], - variable.data[2], - variable.data[3], - variable.data[4], - variable.data[5], - variable.data[6], - variable.data[7], - ]; - f64::from_le_bytes(bytes).to_string() - } - } - TypeKind::Bool => { - if variable.data.is_empty() { - "".to_string() - } else { - (variable.data[0] != 0).to_string() - } - } - TypeKind::Char => { - if variable.data.is_empty() { - "".to_string() - } else { - let b = variable.data[0]; - let ch_repr = match b { - 0x20..=0x7E => format!("'{}'", b as char), - _ => format!("'\\x{b:02x}'"), - }; - format!("{b} ({ch_repr})") - } - } - TypeKind::Pointer => { - if variable.data.len() < 8 { - "".to_string() - } else { - let bytes: [u8; 8] = [ - variable.data[0], - variable.data[1], - variable.data[2], - variable.data[3], - variable.data[4], - variable.data[5], - variable.data[6], - variable.data[7], - ]; - let addr = u64::from_le_bytes(bytes); - format!("0x{addr:x}") - } - } - TypeKind::NullPointer => "null".to_string(), - TypeKind::CString | TypeKind::String => { - match String::from_utf8(variable.data.clone()) { - Ok(s) => s.trim_end_matches('\0').to_string(), // Remove null terminator - Err(_) => "".to_string(), - } - } - TypeKind::Unknown => format!("", variable.data.len()), - TypeKind::OptimizedOut => "".to_string(), - _ => format!("", variable.type_encoding), - } - } - /// Determine if a type is a single-byte character type (signed/unsigned char) fn is_char_byte_type(t: &TypeInfo) -> bool { match t { @@ -1155,94 +727,23 @@ mod tests { #[test] fn test_apply_format_basic() { - let variables = vec![ - ParsedVariable { - var_name_index: 0, - type_encoding: TypeKind::I32, - type_index: None, - status: 0, - data: vec![42, 0, 0, 0], // 42 in little-endian - }, - ParsedVariable { - var_name_index: 1, - type_encoding: TypeKind::CString, - type_index: None, - status: 0, - data: b"hello\0".to_vec(), - }, - ]; - - let result = FormatPrinter::apply_format("pid: {}, name: {}", &variables); + let fmt = "pid: {}, name: {}"; + let rendered: Vec = vec!["42".to_string(), "hello".to_string()]; + let result = FormatPrinter::apply_format_strings(fmt, &rendered); assert_eq!(result, "pid: 42, name: hello"); } #[test] fn test_apply_format_escape_sequences() { - let variables = vec![ParsedVariable { - var_name_index: 0, - type_encoding: TypeKind::I32, - type_index: None, - status: 0, - data: vec![123, 0, 0, 0], // 123 in little-endian - }]; - - let result = FormatPrinter::apply_format("use {{}} for braces, value: {}", &variables); + let rendered: Vec = vec!["123".to_string()]; + let result = + FormatPrinter::apply_format_strings("use {{}} for braces, value: {}", &rendered); assert_eq!(result, "use {} for braces, value: 123"); } - #[test] - fn test_format_different_types() { - // Test U64 - let var_u64 = ParsedVariable { - var_name_index: 0, - type_encoding: TypeKind::U64, - type_index: None, - status: 0, - data: vec![255, 255, 255, 255, 255, 255, 255, 255], // u64::MAX - }; - assert_eq!( - FormatPrinter::format_variable_value(&var_u64), - "18446744073709551615" - ); - - // Test Pointer - let var_ptr = ParsedVariable { - var_name_index: 0, - type_encoding: TypeKind::Pointer, - type_index: None, - status: 0, - data: vec![0xef, 0xbe, 0xad, 0xde, 0, 0, 0, 0], // 0xdeadbeef in little-endian - }; - assert_eq!(FormatPrinter::format_variable_value(&var_ptr), "0xdeadbeef"); - - // Test Bool - let var_bool_true = ParsedVariable { - var_name_index: 0, - type_encoding: TypeKind::Bool, - type_index: None, - status: 0, - data: vec![1], - }; - assert_eq!(FormatPrinter::format_variable_value(&var_bool_true), "true"); - - let var_bool_false = ParsedVariable { - var_name_index: 0, - type_encoding: TypeKind::Bool, - type_index: None, - status: 0, - data: vec![0], - }; - assert_eq!( - FormatPrinter::format_variable_value(&var_bool_false), - "false" - ); - } - #[test] fn test_missing_arguments() { - let variables = vec![]; // No variables - - let result = FormatPrinter::apply_format("need arg: {}", &variables); + let result = FormatPrinter::apply_format_strings("need arg: {}", &[]); assert_eq!(result, "need arg: "); } @@ -1250,25 +751,9 @@ mod tests { fn test_format_print_data_with_trace_context() { let mut trace_context = TraceContext::new(); let format_index = trace_context.add_string("Hello {}, you are {} years old!".to_string()); - - let variables = vec![ - ParsedVariable { - var_name_index: 0, - type_encoding: TypeKind::CString, - type_index: None, - status: 0, - data: b"Alice\0".to_vec(), - }, - ParsedVariable { - var_name_index: 1, - type_encoding: TypeKind::U32, - type_index: None, - status: 0, - data: vec![25, 0, 0, 0], // 25 in little-endian - }, - ]; - - let result = FormatPrinter::format_print_data(format_index, &variables, &trace_context); + let rendered: Vec = vec!["Alice".to_string(), "25".to_string()]; + let fmt = trace_context.get_string(format_index).unwrap(); + let result = FormatPrinter::apply_format_strings(fmt, &rendered); assert_eq!(result, "Hello Alice, you are 25 years old!"); } diff --git a/ghostscope-protocol/src/lib.rs b/ghostscope-protocol/src/lib.rs index f45b3e21..b4f6f1e0 100644 --- a/ghostscope-protocol/src/lib.rs +++ b/ghostscope-protocol/src/lib.rs @@ -14,13 +14,13 @@ pub mod type_info; pub use type_kind::{consts, TypeKind}; pub use trace_event::{ - EndInstructionData, InstructionHeader, InstructionType, PrintFormatData, PrintStringIndexData, - PrintVariableIndexData, TraceEventHeader, TraceEventMessage, VariableData, VariableStatus, + EndInstructionData, InstructionHeader, InstructionType, PrintStringIndexData, + PrintVariableIndexData, TraceEventHeader, TraceEventMessage, VariableStatus, }; pub use trace_context::TraceContext; -pub use format_printer::{FormatPrinter, ParsedVariable}; +pub use format_printer::FormatPrinter; pub use streaming_parser::{ EventSource, ParseState, ParsedInstruction, ParsedTraceEvent, StreamingTraceParser, diff --git a/ghostscope-protocol/src/streaming_parser.rs b/ghostscope-protocol/src/streaming_parser.rs index 2ae94475..e4230ed8 100644 --- a/ghostscope-protocol/src/streaming_parser.rs +++ b/ghostscope-protocol/src/streaming_parser.rs @@ -29,9 +29,6 @@ pub enum ParsedInstruction { formatted_value: String, raw_data: Vec, }, - PrintFormat { - formatted_output: String, - }, PrintComplexFormat { formatted_output: String, }, @@ -84,11 +81,7 @@ impl ParsedTraceEvent { i += 1; } } - ParsedInstruction::PrintFormat { formatted_output } => { - // Already formatted, just add it - output.push(formatted_output.clone()); - i += 1; - } + ParsedInstruction::EndInstruction { .. } => { // Skip EndInstruction - it's for protocol control, not user output i += 1; @@ -460,63 +453,6 @@ impl StreamingTraceParser { } } - t if t == InstructionType::PrintFormat as u8 => { - let (format_data, _) = PrintFormatData::read_from_prefix(inst_data) - .map_err(|_| "Invalid PrintFormat data".to_string())?; - - // Parse variable data - let mut variables = Vec::new(); - let mut offset = std::mem::size_of::(); - - for _ in 0..format_data.arg_count { - // Header: var_name_index:u16 (2), type_encoding:u8 (1), data_len:u16 (2), type_index:u16 (2), status:u8 (1) - if offset + 8 > inst_data.len() { - return Err("Invalid PrintFormat variable header".to_string()); - } - - // Read variable header fields in struct order - let var_name_index = - u16::from_le_bytes([inst_data[offset], inst_data[offset + 1]]); - let type_encoding_byte = inst_data[offset + 2]; - let data_len = - u16::from_le_bytes([inst_data[offset + 3], inst_data[offset + 4]]); - let type_index = - u16::from_le_bytes([inst_data[offset + 5], inst_data[offset + 6]]); - let status = inst_data[offset + 7]; - - offset += 8; - - if offset + data_len as usize > inst_data.len() { - return Err("Invalid PrintFormat variable data".to_string()); - } - - let var_data = inst_data[offset..offset + data_len as usize].to_vec(); - offset += data_len as usize; - - // Convert type encoding byte to enum - let type_encoding = - TypeKind::from_u8(type_encoding_byte).unwrap_or(TypeKind::Unknown); - - variables.push(crate::format_printer::ParsedVariable { - var_name_index, - type_encoding, - // Preserve zero-based indices; 0 is a valid type_index - type_index: Some(type_index), - status, - data: var_data, - }); - } - - // Use FormatPrinter to generate formatted output - let formatted_output = crate::format_printer::FormatPrinter::format_print_data( - format_data.format_string_index, - &variables, - trace_context, - ); - - ParsedInstruction::PrintFormat { formatted_output } - } - t if t == InstructionType::PrintComplexFormat as u8 => { let (format_data, _) = PrintComplexFormatData::read_from_prefix(inst_data) .map_err(|_| "Invalid PrintComplexFormat data".to_string())?; @@ -690,7 +626,7 @@ impl ParsedInstruction { } => { format!("{name} ({type_encoding:?}): {formatted_value}") } - ParsedInstruction::PrintFormat { formatted_output } => formatted_output.clone(), + ParsedInstruction::PrintComplexFormat { formatted_output } => formatted_output.clone(), ParsedInstruction::PrintComplexVariable { name: _, @@ -725,7 +661,7 @@ impl ParsedInstruction { match self { ParsedInstruction::PrintString { .. } => "PrintString".to_string(), ParsedInstruction::PrintVariable { .. } => "PrintVariable".to_string(), - ParsedInstruction::PrintFormat { .. } => "PrintFormat".to_string(), + ParsedInstruction::PrintComplexFormat { .. } => "PrintComplexFormat".to_string(), ParsedInstruction::PrintComplexVariable { .. } => "PrintComplexVariable".to_string(), ParsedInstruction::Backtrace { .. } => "Backtrace".to_string(), diff --git a/ghostscope-protocol/src/trace_event.rs b/ghostscope-protocol/src/trace_event.rs index 7249a498..222c94f2 100644 --- a/ghostscope-protocol/src/trace_event.rs +++ b/ghostscope-protocol/src/trace_event.rs @@ -26,7 +26,6 @@ pub enum InstructionType { PrintStringIndex = 0x01, // print "string" (using string table index) PrintVariableIndex = 0x02, // print variable (using variable name index) PrintComplexVariable = 0x03, // print complex variable (with full type info) - PrintFormat = 0x04, // print "format {} {}", var1, var2 (formatted print) PrintComplexFormat = 0x05, // print with complex variables in format args Backtrace = 0x10, // backtrace instruction @@ -88,18 +87,6 @@ pub struct PrintComplexVariableData { // Followed by access_path (UTF-8 string) then variable data } -/// Format print instruction data -#[repr(C, packed)] -#[derive(Debug, Clone, Copy, FromBytes, KnownLayout, Immutable, Unaligned)] -pub struct PrintFormatData { - pub format_string_index: u16, // Index into string table for format string - pub arg_count: u8, // Number of arguments - pub reserved: u8, // Padding for alignment - // Followed by argument data in struct order (8 bytes header): - // [var_name_index:u16, type_encoding:u8, data_len:u16, type_index:u16, status:u8, data:bytes] * arg_count - // Note: In fast path (script variables/literals), status is always 0 (VariableStatus::Ok) -} - /// Complex format print instruction data (with full type info) #[repr(C, packed)] #[derive(Debug, Clone, Copy, FromBytes, KnownLayout, Immutable, Unaligned)] @@ -113,7 +100,7 @@ pub struct PrintComplexFormatData { } // Note: historical PrintVariableError has been removed; per-variable errors -// are carried via status in PrintVariableIndex/Format/ComplexFormat. +// are carried via status in PrintVariableIndex/ComplexFormat. /// Backtrace instruction data #[repr(C, packed)] @@ -146,10 +133,6 @@ pub enum Instruction { type_index: u16, // Index into type table (new field) data: Vec, }, - PrintFormat { - format_string_index: u16, - variables: Vec, - }, Backtrace { depth: u8, flags: u8, @@ -161,22 +144,12 @@ pub enum Instruction { }, } -/// Variable data for PrintFormat instruction -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct VariableData { - pub var_name_index: u16, - pub type_encoding: TypeKind, - pub type_index: u16, // Index into type table (new field) - pub data: Vec, -} - impl Instruction { /// Get the instruction type pub fn instruction_type(&self) -> InstructionType { match self { Instruction::PrintStringIndex { .. } => InstructionType::PrintStringIndex, Instruction::PrintVariableIndex { .. } => InstructionType::PrintVariableIndex, - Instruction::PrintFormat { .. } => InstructionType::PrintFormat, Instruction::Backtrace { .. } => InstructionType::Backtrace, Instruction::EndInstruction { .. } => InstructionType::EndInstruction, } @@ -191,12 +164,6 @@ mod tests { fn test_instruction_types() { let inst1 = Instruction::PrintStringIndex { string_index: 0 }; assert_eq!(inst1.instruction_type(), InstructionType::PrintStringIndex); - - let inst2 = Instruction::PrintFormat { - format_string_index: 0, - variables: vec![], - }; - assert_eq!(inst2.instruction_type(), InstructionType::PrintFormat); } #[test] diff --git a/ghostscope-protocol/src/type_kind.rs b/ghostscope-protocol/src/type_kind.rs index 7d01d13a..c7a77e8e 100644 --- a/ghostscope-protocol/src/type_kind.rs +++ b/ghostscope-protocol/src/type_kind.rs @@ -166,10 +166,6 @@ pub mod consts { pub const PRINT_VARIABLE_INDEX_DATA_SIZE: usize = std::mem::size_of::(); - /// Print format data size - pub const PRINT_FORMAT_DATA_SIZE: usize = - std::mem::size_of::(); - // TraceEventMessage field offsets pub const TRACE_EVENT_MESSAGE_TRACE_ID_OFFSET: usize = 0; pub const TRACE_EVENT_MESSAGE_TIMESTAMP_OFFSET: usize = 8;