1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
use super::*;
impl<'ctx, 'dw> EbpfContext<'ctx, 'dw> {
/// Generate eBPF code for PrintStringIndex instruction
pub fn generate_print_string_index(&mut self, string_index: u16) -> Result<()> {
info!(
"Generating PrintStringIndex instruction: index={}",
string_index
);
// Allocate instruction structure on eBPF stack
// Reserve space in accumulation buffer for this instruction
let inst_buffer = self
.reserve_instruction_region_or_return_zero(
(std::mem::size_of::<InstructionHeader>()
+ std::mem::size_of::<PrintStringIndexData>()) as u64,
)?
.into_value_after_runtime_returns();
// Clear memory with static size
let _inst_size = self.context.i64_type().const_int(
(std::mem::size_of::<PrintStringIndexData>()
+ std::mem::size_of::<ghostscope_protocol::trace_event::InstructionHeader>())
as u64,
false,
);
// Avoid memset on eBPF; global buffer is zero-initialized and we write fields explicitly.
// Fill instruction header using byte offsets
// inst_type at offset 0 (first field of InstructionHeader)
// SAFETY: inst_buffer points at a reserved PrintStringIndex instruction
// region and inst_type is within InstructionHeader.
let inst_type_ptr = unsafe {
self.builder
.build_gep(
self.context.i8_type(),
inst_buffer,
&[self.context.i32_type().const_int(
std::mem::offset_of!(InstructionHeader, inst_type) as u64,
false,
)],
"inst_type_ptr",
)
.map_err(|e| CodeGenError::LLVMError(format!("Failed to get inst_type GEP: {e}")))?
};
let inst_type_val = self
.context
.i8_type()
.const_int(InstructionType::PrintStringIndex as u64, false);
self.builder
.build_store(inst_type_ptr, inst_type_val)
.map_err(|e| CodeGenError::LLVMError(format!("Failed to store inst_type: {e}")))?;
// SAFETY: inst_buffer points at a reserved PrintStringIndex instruction
// region and data_length is within InstructionHeader.
let data_length_ptr = unsafe {
self.builder
.build_gep(
self.context.i8_type(),
inst_buffer,
&[self.context.i32_type().const_int(
std::mem::offset_of!(InstructionHeader, data_length) as u64,
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(AddressSpace::default()),
"data_length_i16_ptr",
)
.map_err(|e| CodeGenError::LLVMError(format!("Failed to cast data_length ptr: {e}")))?;
let data_length_val = self
.context
.i16_type()
.const_int(std::mem::size_of::<PrintStringIndexData>() 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}")))?;
// Fill string index data (after InstructionHeader)
// SAFETY: the string index payload starts immediately after InstructionHeader
// in the reserved PrintStringIndex instruction region.
let string_index_ptr = unsafe {
self.builder
.build_gep(
self.context.i8_type(),
inst_buffer,
&[self
.context
.i32_type()
.const_int(std::mem::size_of::<InstructionHeader>() as u64, false)],
"string_index_ptr",
)
.map_err(|e| {
CodeGenError::LLVMError(format!("Failed to get string_index GEP: {e}"))
})?
};
let string_index_i16_ptr = self
.builder
.build_pointer_cast(
string_index_ptr,
self.context.ptr_type(AddressSpace::default()),
"string_index_i16_ptr",
)
.map_err(|e| {
CodeGenError::LLVMError(format!("Failed to cast string_index ptr: {e}"))
})?;
let string_index_val = self
.context
.i16_type()
.const_int(string_index as u64, false);
self.builder
.build_store(string_index_i16_ptr, string_index_val)
.map_err(|e| CodeGenError::LLVMError(format!("Failed to store string_index: {e}")))?;
// Already accumulated; EndInstruction will send the whole event
Ok(())
}
}