1use super::context::{CodeGenError, EbpfContext, Result};
7use ghostscope_protocol::trace_event::{EndInstructionData, TraceEventHeader, TraceEventMessage};
8use ghostscope_protocol::{consts, InstructionType};
9use inkwell::basic_block::BasicBlock;
10use inkwell::values::{FunctionValue, PointerValue};
11use inkwell::AddressSpace;
12use tracing::info;
13
14#[cfg(test)]
15const fn split_pid_tgid(pid_tgid: u64) -> (u32, u32) {
16 ((pid_tgid >> 32) as u32, pid_tgid as u32)
17}
18
19#[derive(Clone, Copy, Debug, Eq, PartialEq)]
20pub(crate) enum RuntimeEarlyReturnReason {
21 AccumulationBufferNull,
22 EventBufferOverflow,
23}
24
25#[derive(Clone, Copy)]
26pub(crate) struct RuntimeEarlyReturn<'ctx> {
27 reason: RuntimeEarlyReturnReason,
28 block: BasicBlock<'ctx>,
29}
30
31#[derive(Clone, Copy)]
32pub(crate) struct CodegenContinuation<'ctx> {
33 function: FunctionValue<'ctx>,
34 block: BasicBlock<'ctx>,
35}
36
37pub(crate) struct RuntimeReturnAwareValue<'ctx, T> {
38 value: T,
39 continuation: CodegenContinuation<'ctx>,
40 early_returns: Vec<RuntimeEarlyReturn<'ctx>>,
41}
42
43impl<'ctx, T> RuntimeReturnAwareValue<'ctx, T> {
44 pub(crate) fn into_value_after_runtime_returns(self) -> T {
45 let Self {
46 value,
47 continuation,
48 early_returns,
49 } = self;
50 let _ = (&continuation.function, &continuation.block);
51 for early_return in &early_returns {
52 let _ = (&early_return.reason, &early_return.block);
53 }
54 value
55 }
56}
57
58impl<'ctx, 'dw> EbpfContext<'ctx, 'dw> {
59 fn reserve_event_space_or_return_zero(
63 &mut self,
64 size: u64,
65 ) -> Result<RuntimeReturnAwareValue<'ctx, PointerValue<'ctx>>> {
66 let i32_ty = self.context.i32_type();
67 let i64_ty = self.context.i64_type();
68
69 let accum_buffer_lookup = self.get_or_create_perf_accumulation_buffer_or_return_zero()?;
71 let accum_buffer = accum_buffer_lookup.value;
72 let mut early_returns = accum_buffer_lookup.early_returns;
73 let offset_ptr = self.get_or_create_perf_buffer_offset()?;
74
75 let offset_val = self
77 .builder
78 .build_load(i32_ty, offset_ptr, "offset")
79 .map_err(|e| CodeGenError::LLVMError(format!("Failed to load offset: {e}")))?
80 .into_int_value();
81
82 let buffer_size = i32_ty.const_int(self.compile_options.max_trace_event_size as u64, false);
83 let req_size_i32 = i32_ty.const_int(size, false);
84
85 let CodegenContinuation {
87 function: parent_fn,
88 ..
89 } = self.current_codegen_continuation("reserve event space")?;
90 let bb_overflow = self
91 .context
92 .append_basic_block(parent_fn, "reserve_overflow");
93 let bb_check_size = self
94 .context
95 .append_basic_block(parent_fn, "reserve_check_size");
96 let bb_check_fit = self
97 .context
98 .append_basic_block(parent_fn, "reserve_check_fit");
99
100 let off_in = self
102 .builder
103 .build_int_compare(
104 inkwell::IntPredicate::ULT,
105 offset_val,
106 buffer_size,
107 "off_in",
108 )
109 .map_err(|e| CodeGenError::LLVMError(format!("Failed to compare offset: {e}")))?;
110 self.builder
111 .build_conditional_branch(off_in, bb_check_size, bb_overflow)
112 .map_err(|e| {
113 CodeGenError::LLVMError(format!("Failed to branch on offset bounds: {e}"))
114 })?;
115
116 self.builder.position_at_end(bb_overflow);
118 self.builder
119 .build_store(offset_ptr, i32_ty.const_zero())
120 .map_err(|e| CodeGenError::LLVMError(format!("Failed to reset offset: {e}")))?;
121 self.builder
122 .build_return(Some(&i32_ty.const_zero()))
123 .map_err(|e| {
124 CodeGenError::LLVMError(format!("Failed to build overflow return: {e}"))
125 })?;
126 early_returns.push(RuntimeEarlyReturn {
127 reason: RuntimeEarlyReturnReason::EventBufferOverflow,
128 block: bb_overflow,
129 });
130
131 self.builder.position_at_end(bb_check_size);
133 let size_ok = self
134 .builder
135 .build_int_compare(
136 inkwell::IntPredicate::ULE,
137 req_size_i32,
138 buffer_size,
139 "size_ok",
140 )
141 .map_err(|e| CodeGenError::LLVMError(format!("Failed to compare reserve size: {e}")))?;
142 self.builder
143 .build_conditional_branch(size_ok, bb_check_fit, bb_overflow)
144 .map_err(|e| {
145 CodeGenError::LLVMError(format!("Failed to branch on reserve size: {e}"))
146 })?;
147
148 self.builder.position_at_end(bb_check_fit);
150 let limit = self
151 .builder
152 .build_int_sub(buffer_size, req_size_i32, "limit")
153 .map_err(|e| {
154 CodeGenError::LLVMError(format!("Failed to compute reserve limit: {e}"))
155 })?;
156 let fits = self
157 .builder
158 .build_int_compare(inkwell::IntPredicate::ULE, offset_val, limit, "fits")
159 .map_err(|e| CodeGenError::LLVMError(format!("Failed to compare reserve fit: {e}")))?;
160 let bb_ok = self.context.append_basic_block(parent_fn, "reserve_ok");
161 self.builder
162 .build_conditional_branch(fits, bb_ok, bb_overflow)
163 .map_err(|e| {
164 CodeGenError::LLVMError(format!("Failed to branch on reserve fit: {e}"))
165 })?;
166
167 self.builder.position_at_end(bb_ok);
169 let off64 = self
170 .builder
171 .build_int_z_extend(offset_val, i64_ty, "off64")
172 .map_err(|e| CodeGenError::LLVMError(format!("Failed to extend offset: {e}")))?;
173 let dest_i8 = unsafe {
176 self.builder
177 .build_gep(self.context.i8_type(), accum_buffer, &[off64], "dest_i8")
178 .map_err(|e| {
179 CodeGenError::LLVMError(format!("Failed to compute destination: {e}"))
180 })?
181 };
182 let new_off = self
183 .builder
184 .build_int_add(offset_val, req_size_i32, "new_off")
185 .map_err(|e| CodeGenError::LLVMError(format!("Failed to update offset: {e}")))?;
186 self.builder
187 .build_store(offset_ptr, new_off)
188 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store updated offset: {e}")))?;
189 self.compile_time_event_bytes_upper_bound = self
190 .compile_time_event_bytes_upper_bound
191 .saturating_add(size as usize);
192
193 Ok(RuntimeReturnAwareValue {
194 value: dest_i8,
195 continuation: CodegenContinuation {
196 function: parent_fn,
197 block: bb_ok,
198 },
199 early_returns,
200 })
201 }
202
203 pub(crate) fn reserve_instruction_region_or_return_zero(
205 &mut self,
206 size: u64,
207 ) -> Result<RuntimeReturnAwareValue<'ctx, PointerValue<'ctx>>> {
208 self.reserve_event_space_or_return_zero(size)
209 }
210
211 fn get_or_create_perf_accumulation_buffer_or_return_zero(
213 &mut self,
214 ) -> Result<RuntimeReturnAwareValue<'ctx, PointerValue<'ctx>>> {
215 let ptr_ty = self.context.ptr_type(AddressSpace::default());
216 let val_ptr = self.lookup_percpu_value_ptr("event_accum_buffer", 0)?;
217
218 let is_null = self
220 .builder
221 .build_is_null(val_ptr, "accum_buf_is_null")
222 .map_err(|e| CodeGenError::LLVMError(format!("Failed to check buffer null: {e}")))?;
223 let current_fn = self
224 .current_codegen_continuation("check accumulation buffer")?
225 .function;
226 let cont_bb = self
227 .context
228 .append_basic_block(current_fn, "accum_buf_cont");
229 let ret_bb = self.context.append_basic_block(current_fn, "accum_buf_ret");
230 self.builder
231 .build_conditional_branch(is_null, ret_bb, cont_bb)
232 .map_err(|e| {
233 CodeGenError::LLVMError(format!("Failed to branch on accumulation buffer: {e}"))
234 })?;
235 self.builder.position_at_end(ret_bb);
237 self.builder
238 .build_return(Some(&self.context.i32_type().const_zero()))
239 .map_err(|e| {
240 CodeGenError::LLVMError(format!("Failed to build accumulation buffer return: {e}"))
241 })?;
242 self.builder.position_at_end(cont_bb);
244
245 let accum_buffer = self
247 .builder
248 .build_bit_cast(val_ptr, ptr_ty, "accum_buf_ptr")
249 .map_err(|e| {
250 CodeGenError::LLVMError(format!("Failed to cast accumulation buffer: {e}"))
251 })?;
252 let accum_buffer = match accum_buffer {
253 inkwell::values::BasicValueEnum::PointerValue(ptr) => ptr,
254 _ => {
255 return Err(CodeGenError::LLVMError(
256 "Accumulation buffer cast did not produce a pointer".to_string(),
257 ));
258 }
259 };
260
261 Ok(RuntimeReturnAwareValue {
262 value: accum_buffer,
263 continuation: CodegenContinuation {
264 function: current_fn,
265 block: cont_bb,
266 },
267 early_returns: vec![RuntimeEarlyReturn {
268 reason: RuntimeEarlyReturnReason::AccumulationBufferNull,
269 block: ret_bb,
270 }],
271 })
272 }
273
274 fn get_or_create_perf_buffer_offset(&self) -> Result<PointerValue<'ctx>> {
276 self.event_offset_alloca.ok_or_else(|| {
277 CodeGenError::LLVMError("event_offset not allocated in entry block".to_string())
278 })
279 }
280
281 fn current_codegen_continuation(&self, op: &str) -> Result<CodegenContinuation<'ctx>> {
282 let block = self.builder.get_insert_block().ok_or_else(|| {
283 CodeGenError::Builder(format!("{op} requires an active insert block"))
284 })?;
285 let function = block
286 .get_parent()
287 .ok_or_else(|| CodeGenError::Builder(format!("{op} requires a parent function")))?;
288 Ok(CodegenContinuation { function, block })
289 }
290
291 pub fn send_trace_event_header(&mut self) -> Result<()> {
293 info!("Sending TraceEventHeader segment");
294 self.compile_time_event_bytes_upper_bound = 0;
295
296 if matches!(
298 self.compile_options.event_map_type,
299 crate::EventMapType::PerfEventArray
300 ) {
301 let offset_ptr = self.get_or_create_perf_buffer_offset()?;
302 self.builder
303 .build_store(offset_ptr, self.context.i32_type().const_zero())
304 .map_err(|e| CodeGenError::LLVMError(format!("Failed to reset offset: {e}")))?;
305 }
306
307 let header_size = std::mem::size_of::<TraceEventHeader>() as u64;
309 let header_buffer = self
310 .reserve_instruction_region_or_return_zero(header_size)?
311 .into_value_after_runtime_returns();
312
313 let magic_ptr = header_buffer;
316 let magic_u32_ptr = self
317 .builder
318 .build_pointer_cast(
319 magic_ptr,
320 self.context.ptr_type(AddressSpace::default()),
321 "magic_u32_ptr",
322 )
323 .map_err(|e| CodeGenError::LLVMError(format!("Failed to cast magic ptr: {e}")))?;
324 let magic_val = self
325 .context
326 .i32_type()
327 .const_int(ghostscope_protocol::consts::MAGIC.into(), false);
328 self.builder
329 .build_store(magic_u32_ptr, magic_val)
330 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store magic: {e}")))?;
331
332 Ok(())
335 }
336
337 pub fn send_trace_event_message(&mut self, trace_id: u64) -> Result<()> {
339 info!(
340 "Sending TraceEventMessage segment for trace_id: {}",
341 trace_id
342 );
343
344 let message_size = std::mem::size_of::<TraceEventMessage>() as u64;
346 let message_buffer = self
347 .reserve_instruction_region_or_return_zero(message_size)?
348 .into_value_after_runtime_returns();
349
350 let trace_id_ptr = message_buffer;
353 let trace_id_u64_ptr = self
354 .builder
355 .build_pointer_cast(
356 trace_id_ptr,
357 self.context.ptr_type(AddressSpace::default()),
358 "trace_id_u64_ptr",
359 )
360 .map_err(|e| CodeGenError::LLVMError(format!("Failed to cast trace_id ptr: {e}")))?;
361 let trace_id_val = self.context.i64_type().const_int(trace_id, false);
362 self.builder
363 .build_store(trace_id_u64_ptr, trace_id_val)
364 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store trace_id: {e}")))?;
365
366 let timestamp = self.get_current_timestamp()?;
368 let timestamp_ptr = unsafe {
371 self.builder
372 .build_gep(
373 self.context.i8_type(),
374 message_buffer,
375 &[self
376 .context
377 .i32_type()
378 .const_int(consts::TRACE_EVENT_MESSAGE_TIMESTAMP_OFFSET as u64, false)],
379 "timestamp_ptr",
380 )
381 .map_err(|e| CodeGenError::LLVMError(format!("Failed to get timestamp GEP: {e}")))?
382 };
383 let timestamp_u64_ptr = self
384 .builder
385 .build_pointer_cast(
386 timestamp_ptr,
387 self.context.ptr_type(AddressSpace::default()),
388 "timestamp_u64_ptr",
389 )
390 .map_err(|e| CodeGenError::LLVMError(format!("Failed to cast timestamp ptr: {e}")))?;
391 self.builder
392 .build_store(timestamp_u64_ptr, timestamp)
393 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store timestamp: {e}")))?;
394
395 let (event_pid, event_tid) = self.get_host_pid_tid_values()?;
398
399 let pid_ptr = unsafe {
403 self.builder
404 .build_gep(
405 self.context.i8_type(),
406 message_buffer,
407 &[self
408 .context
409 .i32_type()
410 .const_int(consts::TRACE_EVENT_MESSAGE_PID_OFFSET as u64, false)],
411 "pid_ptr",
412 )
413 .map_err(|e| CodeGenError::LLVMError(format!("Failed to get pid GEP: {e}")))?
414 };
415 let pid_u32_ptr = self
416 .builder
417 .build_pointer_cast(
418 pid_ptr,
419 self.context.ptr_type(AddressSpace::default()),
420 "pid_u32_ptr",
421 )
422 .map_err(|e| CodeGenError::LLVMError(format!("Failed to cast pid ptr: {e}")))?;
423 self.builder
424 .build_store(pid_u32_ptr, event_pid)
425 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store pid: {e}")))?;
426
427 let tid_ptr = unsafe {
431 self.builder
432 .build_gep(
433 self.context.i8_type(),
434 message_buffer,
435 &[self
436 .context
437 .i32_type()
438 .const_int(consts::TRACE_EVENT_MESSAGE_TID_OFFSET as u64, false)],
439 "tid_ptr",
440 )
441 .map_err(|e| CodeGenError::LLVMError(format!("Failed to get tid GEP: {e}")))?
442 };
443 let tid_u32_ptr = self
444 .builder
445 .build_pointer_cast(
446 tid_ptr,
447 self.context.ptr_type(AddressSpace::default()),
448 "tid_u32_ptr",
449 )
450 .map_err(|e| CodeGenError::LLVMError(format!("Failed to cast tid ptr: {e}")))?;
451 self.builder
452 .build_store(tid_u32_ptr, event_tid)
453 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store tid: {e}")))?;
454
455 Ok(())
458 }
459
460 pub(crate) fn write_end_instruction(&mut self, total_instructions: u16) -> Result<()> {
462 info!(
463 "Writing EndInstruction segment with {} total instructions",
464 total_instructions
465 );
466
467 let total_size =
469 (std::mem::size_of::<ghostscope_protocol::trace_event::InstructionHeader>()
470 + std::mem::size_of::<EndInstructionData>()) as u64;
471 let end_buffer = self
472 .reserve_instruction_region_or_return_zero(total_size)?
473 .into_value_after_runtime_returns();
474
475 let inst_type_ptr = end_buffer;
478 let inst_type_val = self
479 .context
480 .i8_type()
481 .const_int(InstructionType::EndInstruction as u64, false);
482 self.builder
483 .build_store(inst_type_ptr, inst_type_val)
484 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store inst_type: {e}")))?;
485
486 let data_length_ptr = unsafe {
490 self.builder
491 .build_gep(
492 self.context.i8_type(),
493 end_buffer,
494 &[self
495 .context
496 .i32_type()
497 .const_int(consts::INSTRUCTION_HEADER_DATA_LENGTH_OFFSET as u64, false)],
498 "data_length_ptr",
499 )
500 .map_err(|e| {
501 CodeGenError::LLVMError(format!("Failed to get data_length GEP: {e}"))
502 })?
503 };
504 let data_length_i16_ptr = self
505 .builder
506 .build_pointer_cast(
507 data_length_ptr,
508 self.context.ptr_type(AddressSpace::default()),
509 "data_length_i16_ptr",
510 )
511 .map_err(|e| CodeGenError::LLVMError(format!("Failed to cast data_length ptr: {e}")))?;
512 let data_length_val = self
513 .context
514 .i16_type()
515 .const_int(std::mem::size_of::<EndInstructionData>() as u64, false);
516 self.builder
517 .build_store(data_length_i16_ptr, data_length_val)
518 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store data_length: {e}")))?;
519
520 let total_instructions_ptr = unsafe {
525 self.builder
526 .build_gep(
527 self.context.i8_type(),
528 end_buffer,
529 &[self
530 .context
531 .i32_type()
532 .const_int(consts::END_INSTRUCTION_DATA_OFFSET as u64, false)],
533 "total_instructions_ptr",
534 )
535 .map_err(|e| {
536 CodeGenError::LLVMError(format!("Failed to get total_instructions GEP: {e}"))
537 })?
538 };
539 let total_instructions_i16_ptr = self
540 .builder
541 .build_pointer_cast(
542 total_instructions_ptr,
543 self.context.ptr_type(AddressSpace::default()),
544 "total_instructions_i16_ptr",
545 )
546 .map_err(|e| {
547 CodeGenError::LLVMError(format!("Failed to cast total_instructions ptr: {e}"))
548 })?;
549 let total_instructions_val = self
550 .context
551 .i16_type()
552 .const_int(total_instructions as u64, false);
553 self.builder
554 .build_store(total_instructions_i16_ptr, total_instructions_val)
555 .map_err(|e| {
556 CodeGenError::LLVMError(format!("Failed to store total_instructions: {e}"))
557 })?;
558
559 let status_ptr = unsafe {
563 self.builder
564 .build_gep(
565 self.context.i8_type(),
566 end_buffer,
567 &[self.context.i32_type().const_int(
568 (consts::END_INSTRUCTION_DATA_OFFSET
569 + consts::END_INSTRUCTION_EXECUTION_STATUS_OFFSET)
570 as u64,
571 false,
572 )],
573 "status_ptr",
574 )
575 .map_err(|e| CodeGenError::LLVMError(format!("Failed to get status GEP: {e}")))?
576 };
577 let any_fail_ptr = self.get_or_create_flag_global("_gs_any_fail");
579 let any_succ_ptr = self.get_or_create_flag_global("_gs_any_success");
580
581 let any_fail_val = self
582 .builder
583 .build_load(self.context.i8_type(), any_fail_ptr, "any_fail")
584 .map_err(|e| CodeGenError::LLVMError(format!("Failed to load any_fail: {e}")))?
585 .into_int_value();
586 let any_succ_val = self
587 .builder
588 .build_load(self.context.i8_type(), any_succ_ptr, "any_succ")
589 .map_err(|e| CodeGenError::LLVMError(format!("Failed to load any_succ: {e}")))?
590 .into_int_value();
591
592 let zero = self.context.i8_type().const_zero();
593 let is_fail = self
594 .builder
595 .build_int_compare(inkwell::IntPredicate::NE, any_fail_val, zero, "is_fail")
596 .map_err(|e| CodeGenError::LLVMError(format!("Failed to cmp any_fail: {e}")))?;
597 let is_succ = self
598 .builder
599 .build_int_compare(inkwell::IntPredicate::NE, any_succ_val, zero, "is_succ")
600 .map_err(|e| CodeGenError::LLVMError(format!("Failed to cmp any_succ: {e}")))?;
601
602 let not_succ = self
606 .builder
607 .build_not(is_succ, "not_succ")
608 .map_err(|e| CodeGenError::LLVMError(format!("Failed to build not: {e}")))?;
609 let only_fail = self
610 .builder
611 .build_and(is_fail, not_succ, "only_fail")
612 .map_err(|e| CodeGenError::LLVMError(format!("Failed to build and: {e}")))?;
613 let both = self
614 .builder
615 .build_and(is_fail, is_succ, "both")
616 .map_err(|e| CodeGenError::LLVMError(format!("Failed to build and: {e}")))?;
617
618 let two = self.context.i8_type().const_int(2, false);
619 let one = self.context.i8_type().const_int(1, false);
620 let sel1 = self
621 .builder
622 .build_select(only_fail, two, zero, "status_sel1")
623 .map_err(|e| CodeGenError::LLVMError(format!("Failed to build select: {e}")))?
624 .into_int_value();
625 let sel2 = self
626 .builder
627 .build_select(both, one, sel1, "status_sel2")
628 .map_err(|e| CodeGenError::LLVMError(format!("Failed to build select: {e}")))?
629 .into_int_value();
630
631 self.builder
632 .build_store(status_ptr, sel2)
633 .map_err(|e| CodeGenError::LLVMError(format!("Failed to store status: {e}")))?;
634
635 Ok(())
638 }
639
640 pub fn send_end_instruction(&mut self, total_instructions: u16) -> Result<()> {
642 self.write_end_instruction(total_instructions)?;
643 self.emit_accumulated_event_output_from_stack_offset()
644 }
645
646 pub(crate) fn emit_accumulated_event_output_from_stack_offset(&mut self) -> Result<()> {
647 let accum_buffer = self
648 .get_or_create_perf_accumulation_buffer_or_return_zero()?
649 .into_value_after_runtime_returns();
650 let offset_ptr = self.get_or_create_perf_buffer_offset()?;
651
652 let total_accumulated_size = self
653 .builder
654 .build_load(self.context.i32_type(), offset_ptr, "total_size")
655 .map_err(|e| CodeGenError::LLVMError(format!("Failed to load total size: {e}")))?
656 .into_int_value();
657 self.emit_accumulated_event_output(accum_buffer, total_accumulated_size)?;
658
659 self.builder
660 .build_store(offset_ptr, self.context.i32_type().const_zero())
661 .map_err(|e| {
662 CodeGenError::LLVMError(format!("Failed to reset offset after send: {e}"))
663 })?;
664 Ok(())
665 }
666
667 pub(crate) fn emit_accumulated_event_output(
668 &mut self,
669 accum_buffer: PointerValue<'ctx>,
670 total_accumulated_size: inkwell::values::IntValue<'ctx>,
671 ) -> Result<()> {
672 let max_size_i32 = self
673 .context
674 .i32_type()
675 .const_int(self.compile_options.max_trace_event_size as u64, false);
676 let size_le_max = self
677 .builder
678 .build_int_compare(
679 inkwell::IntPredicate::ULE,
680 total_accumulated_size,
681 max_size_i32,
682 "size_le_max",
683 )
684 .map_err(|e| CodeGenError::LLVMError(format!("Failed to compare end size: {e}")))?;
685 let clamped_size_i32 = self
686 .builder
687 .build_select(
688 size_le_max,
689 total_accumulated_size,
690 max_size_i32,
691 "clamped_size_i32",
692 )
693 .map_err(|e| CodeGenError::LLVMError(format!("Failed to select clamp size: {e}")))?
694 .into_int_value();
695
696 let total_size_i64 = self
697 .builder
698 .build_int_z_extend(clamped_size_i32, self.context.i64_type(), "total_size_i64")
699 .map_err(|e| CodeGenError::LLVMError(format!("Failed to extend size: {e}")))?;
700
701 match self.compile_options.event_map_type {
702 crate::EventMapType::PerfEventArray => {
703 self.create_perf_event_output_dynamic(accum_buffer, total_size_i64)?;
704 }
705 crate::EventMapType::RingBuf => {
706 self.create_ringbuf_output_dynamic(accum_buffer, total_size_i64)?;
707 }
708 }
709 Ok(())
710 }
711}
712
713#[cfg(test)]
714mod tests {
715 use super::split_pid_tgid;
716
717 #[test]
718 fn split_pid_tgid_uses_tgid_for_pid_and_pid_for_tid() {
719 let raw = (0x1122_3344_u64 << 32) | 0x5566_7788;
720
721 let (pid, tid) = split_pid_tgid(raw);
722
723 assert_eq!(pid, 0x1122_3344);
724 assert_eq!(tid, 0x5566_7788);
725 }
726}