1use crate::mm::PAGE_SIZE;
6use crate::task::dynamic_thread_spawner::SpawnRequestBuilder;
7use anyhow::Context;
8use fidl_fuchsia_cpu_profiler as profiler;
9use fuchsia_component::client::connect_to_protocol;
10use fuchsia_runtime;
11use futures::StreamExt;
12use futures::channel::mpsc as future_mpsc;
13use std::collections::HashMap;
14use std::error::Error;
15use std::sync::atomic::{AtomicU64, Ordering};
16use std::sync::{Arc, OnceLock, mpsc as sync_mpsc};
17use zerocopy::{Immutable, IntoBytes};
18
19use fxt::TraceRecord;
20use fxt::profiler::ProfilerRecord;
21use fxt::session::SessionParser;
22use seq_lock::{SeqLock, SeqLockable, WriteSize};
23use starnix_logging::{log_error, log_info, log_warn, track_stub};
24use starnix_sync::{LockDepMutex, LockDepRwLock, PerfEventLevel, PerfFormatIdLookupTableLock};
25use starnix_syscalls::{SUCCESS, SyscallArg, SyscallResult};
26use starnix_uapi::arch32::{
27 PERF_EVENT_IOC_DISABLE, PERF_EVENT_IOC_ENABLE, PERF_EVENT_IOC_ID,
28 PERF_EVENT_IOC_MODIFY_ATTRIBUTES, PERF_EVENT_IOC_PAUSE_OUTPUT, PERF_EVENT_IOC_PERIOD,
29 PERF_EVENT_IOC_QUERY_BPF, PERF_EVENT_IOC_REFRESH, PERF_EVENT_IOC_RESET, PERF_EVENT_IOC_SET_BPF,
30 PERF_EVENT_IOC_SET_FILTER, PERF_EVENT_IOC_SET_OUTPUT, PERF_RECORD_MISC_USER,
31 perf_event_sample_format_PERF_SAMPLE_CALLCHAIN, perf_event_sample_format_PERF_SAMPLE_ID,
32 perf_event_sample_format_PERF_SAMPLE_IDENTIFIER, perf_event_sample_format_PERF_SAMPLE_IP,
33 perf_event_sample_format_PERF_SAMPLE_PERIOD, perf_event_sample_format_PERF_SAMPLE_READ,
34 perf_event_sample_format_PERF_SAMPLE_REGS_USER,
35 perf_event_sample_format_PERF_SAMPLE_STACK_USER, perf_event_sample_format_PERF_SAMPLE_TID,
36 perf_event_sample_format_PERF_SAMPLE_TIME, perf_event_type_PERF_RECORD_LOST,
37 perf_event_type_PERF_RECORD_SAMPLE,
38};
39use starnix_uapi::errors::Errno;
40use starnix_uapi::open_flags::OpenFlags;
41use starnix_uapi::uapi::{
42 perf_sample_regs_abi_PERF_SAMPLE_REGS_ABI_32, perf_sample_regs_abi_PERF_SAMPLE_REGS_ABI_64,
43 perf_sample_regs_abi_PERF_SAMPLE_REGS_ABI_NONE,
44};
45use starnix_uapi::user_address::UserRef;
46use starnix_uapi::{
47 errno, error, from_status_like_fdio, perf_event_attr, perf_event_header,
48 perf_event_mmap_page__bindgen_ty_1, perf_event_read_format_PERF_FORMAT_GROUP,
49 perf_event_read_format_PERF_FORMAT_ID, perf_event_read_format_PERF_FORMAT_LOST,
50 perf_event_read_format_PERF_FORMAT_TOTAL_TIME_ENABLED,
51 perf_event_read_format_PERF_FORMAT_TOTAL_TIME_RUNNING, tid_t, uapi,
52};
53
54use crate::security::{self, TargetTaskType};
55use crate::task::Kernel;
56use crate::task::tracing::{LinuxIdentity, PidKoidSession};
57
58static READ_FORMAT_ID_GENERATOR: AtomicU64 = AtomicU64::new(0);
59const ESTIMATED_MMAP_BUFFER_SIZE: u64 = 16 * 1024 * 1024;
69const LOST_RECORD_SIZE: u64 = 24;
72const AARCH64_REG_PC: usize = 32;
74const AARCH64_REG_SP: usize = 31;
75const AARCH32_REG_R15: usize = 15; const AARCH32_REG_R13: usize = 13; mod event;
79pub use event::{TraceEvent, TraceEventQueue, TraceEventQueueList};
80
81pub mod lockless_ring_buffer;
82
83#[repr(C)]
84#[derive(Copy, Clone, IntoBytes, Immutable)]
85struct LostRecord {
86 header: perf_event_header,
87 sample_id: u64,
88 lost_events: u64,
89}
90
91#[repr(C)]
92#[derive(Copy, Clone, IntoBytes, Immutable)]
93struct PerfMetadataHeader {
94 version: u32,
95 compat_version: u32,
96}
97
98#[repr(C)]
99#[derive(Copy, Clone, IntoBytes, Immutable)]
100struct PerfMetadataValue {
101 lock: u32,
102 index: u32,
103 offset: i64,
104 time_enabled: u64,
105 time_running: u64,
106 __bindgen_anon_1: perf_event_mmap_page__bindgen_ty_1,
107 pmc_width: u16,
108 time_shift: u16,
109 time_mult: u32,
110 time_offset: u64,
111 time_zero: u64,
112 size: u32,
113 __reserved_1: u32,
114 time_cycles: u64,
115 time_mask: u64,
116 __reserved: [u8; 928usize],
117 data_head: u64,
118 data_tail: u64,
119 data_offset: u64,
120 data_size: u64,
121 aux_head: u64,
122 aux_tail: u64,
123 aux_offset: u64,
124 aux_size: u64,
125}
126
127unsafe impl SeqLockable for PerfMetadataValue {
131 const WRITE_SIZE: WriteSize = WriteSize::Eight;
132 const HAS_INLINE_SEQUENCE: bool = true;
133 const VMO_NAME: &'static [u8] = b"starnix:perf_event";
134}
135
136struct PerfState {
137 format_id_lookup_table: LockDepMutex<HashMap<FileObjectId, u64>, PerfFormatIdLookupTableLock>,
143}
144
145impl Default for PerfState {
146 fn default() -> Self {
147 Self { format_id_lookup_table: Default::default() }
148 }
149}
150
151fn get_perf_state(kernel: &Arc<Kernel>) -> Arc<PerfState> {
152 kernel.expando.get_or_init(PerfState::default)
153}
154
155uapi::check_arch_independent_layout! {
156 perf_event_attr {
157 type_, size,
159 config,
160 __bindgen_anon_1,
161 sample_type,
162 read_format,
163 _bitfield_1,
164 __bindgen_anon_2,
165 bp_type,
166 __bindgen_anon_3,
167 __bindgen_anon_4,
168 branch_sample_type,
169 sample_regs_user,
170 sample_stack_user,
171 clockid,
172 sample_regs_intr,
173 aux_watermark,
174 sample_max_stack,
175 __reserved_2,
176 aux_sample_size,
177 __reserved_3,
178 sig_data,
179 config3,
180 }
181}
182
183#[derive(Clone, Copy, Debug, PartialEq)]
184enum IoctlOp {
185 Enable,
186 Disable,
187}
188
189struct PerfEventFileState {
190 attr: perf_event_attr,
191 rf_value: u64, most_recent_enabled_time: u64,
195 total_time_running: u64,
199 rf_id: u64,
200 sample_id: u64,
201 _rf_lost: u64,
202 disabled: u64,
203 sample_type: u64,
204 perf_data_vmo: zx::Vmo,
207 ioctl_sender: future_mpsc::Sender<(IoctlOp, sync_mpsc::Sender<()>)>,
209}
210
211impl PerfEventFileState {
214 fn new(
215 attr: perf_event_attr,
216 rf_value: u64,
217 disabled: u64,
218 sample_type: u64,
219 perf_data_vmo: zx::Vmo,
220 ioctl_sender: future_mpsc::Sender<(IoctlOp, sync_mpsc::Sender<()>)>,
221 ) -> PerfEventFileState {
222 PerfEventFileState {
223 attr,
224 rf_value,
225 most_recent_enabled_time: 0,
226 total_time_running: 0,
227 rf_id: 0,
228 sample_id: 0,
229 _rf_lost: 0,
230 disabled,
231 sample_type,
232 perf_data_vmo,
233 ioctl_sender,
234 }
235 }
236}
237
238pub struct PerfEventFile {
239 _tid: tid_t,
240 _cpu: i32,
241 perf_event_file: LockDepRwLock<PerfEventFileState, PerfEventLevel>,
242 pub security_state: security::PerfEventState,
244 seq_lock: Arc<OnceLock<Result<SeqLock<PerfMetadataHeader, PerfMetadataValue>, Errno>>>,
245}
246
247impl FileOps for PerfEventFile {
252 fileops_impl_nonseekable!();
254 fileops_impl_noop_sync!();
255
256 fn close(self: Box<Self>, file: &FileObjectState, current_task: &CurrentTask) {
257 {
258 let mut perf_event_file = self.perf_event_file.write();
259 perf_event_file.disabled = 1;
261 ping_receiver(perf_event_file.ioctl_sender.clone(), IoctlOp::Disable);
262 }
263 let perf_state = get_perf_state(¤t_task.kernel);
264 let mut events = perf_state.format_id_lookup_table.lock();
265 events.remove(&file.id);
266 }
267
268 fn read(
270 &self,
271 _file: &FileObject,
272 current_task: &CurrentTask,
273 _offset: usize,
274 data: &mut dyn OutputBuffer,
275 ) -> Result<usize, Errno> {
276 let read_format_data = {
279 let mut perf_event_file = self.perf_event_file.write();
282
283 security::check_perf_event_read_access(current_task, &self)?;
284
285 let mut total_time_running_including_curr = perf_event_file.total_time_running;
286
287 if perf_event_file.disabled == 0 {
289 track_stub!(
294 TODO("https://fxbug.dev/402938671"),
295 "[perf_event_open] implement read_format value"
296 );
297 perf_event_file.rf_value += 1;
298
299 let curr_time = zx::MonotonicInstant::get().into_nanos() as u64;
301 total_time_running_including_curr +=
302 curr_time - perf_event_file.most_recent_enabled_time;
303 }
304
305 let mut output = Vec::<u8>::new();
306 let value = perf_event_file.rf_value.to_ne_bytes();
307 output.extend(value);
308
309 let read_format = perf_event_file.attr.read_format;
310
311 if (read_format & perf_event_read_format_PERF_FORMAT_TOTAL_TIME_ENABLED as u64) != 0 {
312 output.extend(total_time_running_including_curr.to_ne_bytes());
314 }
315 if (read_format & perf_event_read_format_PERF_FORMAT_TOTAL_TIME_RUNNING as u64) != 0 {
316 output.extend(total_time_running_including_curr.to_ne_bytes());
318 }
319 if (read_format & perf_event_read_format_PERF_FORMAT_ID as u64) != 0 {
320 output.extend(perf_event_file.rf_id.to_ne_bytes());
322 }
323
324 output
325 };
326
327 if data.available() < read_format_data.len() {
331 return error!(ENOSPC);
332 }
333 track_stub!(
334 TODO("https://fxbug.dev/402453955"),
335 "[perf_event_open] implement remaining error handling"
336 );
337
338 data.write(&read_format_data)
339 }
340
341 fn ioctl(
342 &self,
343 _file: &FileObject,
344 current_task: &CurrentTask,
345 op: u32,
346 _arg: SyscallArg,
347 ) -> Result<SyscallResult, Errno> {
348 track_stub!(
349 TODO("https://fxbug.dev/405463320"),
350 "[perf_event_open] implement PERF_IOC_FLAG_GROUP"
351 );
352 security::check_perf_event_write_access(current_task, &self)?;
353 let mut perf_event_file = self.perf_event_file.write();
354 match op {
355 PERF_EVENT_IOC_ENABLE => {
356 if perf_event_file.disabled != 0 {
357 perf_event_file.disabled = 0; perf_event_file.most_recent_enabled_time =
359 zx::MonotonicInstant::get().into_nanos() as u64;
360 }
361
362 track_stub!(
365 TODO("https://fxbug.dev/398914921"),
366 "[perf_event_open] implement full sampling features"
367 );
368 if perf_event_file.attr.freq() == 0
369 && unsafe { perf_event_file.attr.__bindgen_anon_1.sample_period != 0 }
372 {
373 ping_receiver(perf_event_file.ioctl_sender.clone(), IoctlOp::Enable);
374 }
375 return Ok(SUCCESS);
376 }
377 PERF_EVENT_IOC_DISABLE => {
378 if perf_event_file.disabled == 0 {
379 perf_event_file.disabled = 1; let curr_time = zx::MonotonicInstant::get().into_nanos() as u64;
383 perf_event_file.total_time_running +=
384 curr_time - perf_event_file.most_recent_enabled_time;
385 }
386 if perf_event_file.attr.freq() == 0
387 && unsafe { perf_event_file.attr.__bindgen_anon_1.sample_period != 0 }
390 {
391 ping_receiver(perf_event_file.ioctl_sender.clone(), IoctlOp::Disable);
392 }
393 return Ok(SUCCESS);
394 }
395 PERF_EVENT_IOC_RESET => {
396 perf_event_file.rf_value = 0;
397 return Ok(SUCCESS);
398 }
399 PERF_EVENT_IOC_REFRESH
400 | PERF_EVENT_IOC_PERIOD
401 | PERF_EVENT_IOC_SET_OUTPUT
402 | PERF_EVENT_IOC_SET_FILTER
403 | PERF_EVENT_IOC_ID
404 | PERF_EVENT_IOC_SET_BPF
405 | PERF_EVENT_IOC_PAUSE_OUTPUT
406 | PERF_EVENT_IOC_MODIFY_ATTRIBUTES
407 | PERF_EVENT_IOC_QUERY_BPF => {
408 track_stub!(
409 TODO("https://fxbug.dev/404941053"),
410 "[perf_event_open] implement remaining ioctl() calls"
411 );
412 return error!(ENOSYS);
413 }
414 _ => error!(ENOTTY),
415 }
416 }
417
418 fn get_memory(
423 &self,
424 _file: &FileObject,
425 current_task: &CurrentTask,
426 length: Option<usize>,
427 _prot: ProtectionFlags,
428 ) -> Result<Arc<MemoryObject>, Errno> {
429 let buffer_size: u64 = length.unwrap_or(0) as u64;
430 let page_size = zx::system_get_page_size() as u64;
431 if buffer_size <= page_size || buffer_size > ESTIMATED_MMAP_BUFFER_SIZE {
432 return error!(EINVAL);
433 }
434 let data_size = buffer_size - page_size;
435 if !data_size.is_power_of_two() {
436 return error!(EINVAL);
437 }
438
439 self.seq_lock
440 .get_or_init(|| {
441 let perf_event_file = self.perf_event_file.read();
442 let vmo_copy = perf_event_file
443 .perf_data_vmo
444 .as_handle_ref()
445 .duplicate_handle(zx::Rights::SAME_RIGHTS)
446 .map_err(|status| from_status_like_fdio!(status))?;
447 Ok(unsafe { create_seq_lock(&vmo_copy, buffer_size) })
449 })
450 .as_ref()
451 .map_err(|e| e.clone())?;
452
453 security::check_perf_event_read_access(current_task, &self)?;
455 let perf_event_file = self.perf_event_file.read();
456 match perf_event_file
457 .perf_data_vmo
458 .as_handle_ref()
459 .duplicate_handle(zx::Rights::SAME_RIGHTS)
460 {
461 Ok(vmo) => {
462 let vmo: zx::Vmo = vmo.into();
463 let memory = MemoryObject::from(vmo);
464 return Ok(Arc::new(memory));
465 }
466 Err(_) => {
467 track_stub!(
468 TODO("https://fxbug.dev/416323134"),
469 "[perf_event_open] handle get_memory() errors"
470 );
471 return error!(EINVAL);
472 }
473 };
474 }
475
476 fn write(
477 &self,
478 _file: &FileObject,
479 _current_task: &CurrentTask,
480 _offset: usize,
481 _data: &mut dyn InputBuffer,
482 ) -> Result<usize, Errno> {
483 track_stub!(
484 TODO("https://fxbug.dev/394960158"),
485 "[perf_event_open] implement perf event functions"
486 );
487 error!(ENOSYS)
488 }
489}
490
491fn write_record_to_vmo(
519 perf_record_sample: PerfRecordSample<'_>,
520 perf_data_vmo: &zx::Vmo,
521 sample_type: u64,
522 sample_id: u64,
523 sample_period: u64,
524 read_format: u64,
525 head: u64,
526 metadata: &PerfMetadataValue,
527 lost_events: &mut u64,
528) -> u64 {
529 let ring_buffer_size = metadata.data_size;
530 if ring_buffer_size == 0 {
531 return 0;
532 }
533 let mut sample = Vec::<u8>::new();
535 if (sample_type & perf_event_sample_format_PERF_SAMPLE_IDENTIFIER as u64) != 0 {
537 sample.extend(sample_id.to_ne_bytes());
538 }
539 if (sample_type & perf_event_sample_format_PERF_SAMPLE_IP as u64) != 0 {
541 let ip = perf_record_sample.ips.first().copied().unwrap_or(0);
542 sample.extend(ip.to_ne_bytes());
543 }
544
545 if (sample_type & perf_event_sample_format_PERF_SAMPLE_TID as u64) != 0 {
546 sample.extend(perf_record_sample.pid.unwrap_or(0).to_ne_bytes());
548 sample.extend(perf_record_sample.tid.unwrap_or(0).to_ne_bytes());
550 }
551
552 if (sample_type & perf_event_sample_format_PERF_SAMPLE_TIME as u64) != 0 {
554 sample.extend((perf_record_sample.time.into_nanos() as u64).to_ne_bytes());
555 }
556
557 if (sample_type & perf_event_sample_format_PERF_SAMPLE_ID as u64) != 0 {
559 sample.extend(sample_id.to_ne_bytes());
560 }
561
562 if (sample_type & perf_event_sample_format_PERF_SAMPLE_PERIOD as u64) != 0 {
564 sample.extend(sample_period.to_ne_bytes());
565 }
566
567 if (sample_type & perf_event_sample_format_PERF_SAMPLE_READ as u64) != 0 {
569 if (read_format & perf_event_read_format_PERF_FORMAT_GROUP as u64) != 0 {
570 sample.extend(1u64.to_ne_bytes());
573 sample.extend(0u64.to_ne_bytes());
574 } else {
575 sample.extend(0u64.to_ne_bytes());
576 }
577 }
578
579 if (sample_type & perf_event_sample_format_PERF_SAMPLE_CALLCHAIN as u64) != 0 {
580 sample.extend(perf_record_sample.ips.len().to_ne_bytes());
582
583 for i in perf_record_sample.ips {
585 sample.extend(i.to_ne_bytes());
586 }
587 }
588
589 if (sample_type & perf_event_sample_format_PERF_SAMPLE_REGS_USER as u64) != 0 {
598 if perf_record_sample.regs_abi == perf_sample_regs_abi_PERF_SAMPLE_REGS_ABI_NONE as u64
599 || perf_record_sample.sample_regs_user == 0
600 || perf_record_sample.regs.is_empty()
601 {
602 sample.extend((perf_sample_regs_abi_PERF_SAMPLE_REGS_ABI_NONE as u64).to_ne_bytes());
603 } else {
604 sample.extend(perf_record_sample.regs_abi.to_ne_bytes());
605 sample.extend_from_slice(perf_record_sample.regs);
606 }
607 }
608
609 if (sample_type & perf_event_sample_format_PERF_SAMPLE_STACK_USER as u64) != 0 {
614 let fixed_len = std::mem::size_of::<perf_event_header>() + sample.len() + 16;
615 let header_space = (u16::MAX as usize).saturating_sub(fixed_len) & !7;
616 let requested = (perf_record_sample.sample_stack_user as usize) & !7;
617 let dest_len = requested.min(header_space);
618
619 sample.extend((dest_len as u64).to_ne_bytes());
620 if dest_len > 0 {
621 let data_len = perf_record_sample.stack.len().min(dest_len) & !7;
622 sample.extend_from_slice(&perf_record_sample.stack[..data_len]);
623 sample.resize(sample.len() + (dest_len - data_len), 0);
625 sample.extend((data_len as u64).to_ne_bytes());
627 }
628 }
629 let record_len = std::mem::size_of::<perf_event_header>() + sample.len();
635 if record_len % 8 != 0 {
641 log_error!("Record length {} is not 8-byte aligned, dropping", record_len);
642 *lost_events += 1;
643 return 0;
644 }
645 let Ok(record_size) = u16::try_from(record_len) else {
649 log_warn!("Dropping {} byte perf sample record: exceeds u16 record size", record_len);
650 *lost_events += 1;
651 return 0;
652 };
653
654 let perf_event_header = perf_event_header {
655 type_: perf_event_type_PERF_RECORD_SAMPLE,
658 misc: PERF_RECORD_MISC_USER as u16,
659 size: record_size,
660 };
661
662 let free_space =
667 (metadata.data_tail.wrapping_sub(head).wrapping_sub(1)) & (ring_buffer_size - 1);
668
669 if free_space < record_len as u64 {
673 *lost_events += 1;
674 return 0;
675 }
676
677 let mut bytes_written: u64 = 0;
678
679 if *lost_events > 0 && free_space >= record_len as u64 + LOST_RECORD_SIZE {
682 let lost_header = perf_event_header {
683 type_: perf_event_type_PERF_RECORD_LOST,
684 misc: 0,
685 size: LOST_RECORD_SIZE as u16,
686 };
687 let lost_record = LostRecord { header: lost_header, sample_id, lost_events: *lost_events };
688 if write_circular(
689 perf_data_vmo,
690 metadata.data_offset,
691 ring_buffer_size,
692 head,
693 lost_record.as_bytes(),
694 )
695 .is_ok()
696 {
697 *lost_events = 0;
698 bytes_written += LOST_RECORD_SIZE;
699 }
700 }
701
702 let mut record = Vec::with_capacity(record_len);
703 record.extend_from_slice(perf_event_header.as_bytes());
704 record.extend_from_slice(&sample);
705
706 match write_circular(
707 perf_data_vmo,
708 metadata.data_offset,
709 ring_buffer_size,
710 head + bytes_written,
711 &record,
712 ) {
713 Ok(()) => bytes_written + record_len as u64,
715 Err(e) => {
716 log_warn!("Failed to write PerfRecordSample to VMO due to: {}", e);
717 bytes_written
718 }
719 }
720}
721
722fn write_circular(
728 vmo: &zx::Vmo,
729 data_offset: u64,
730 ring_buffer_size: u64,
731 head: u64,
732 data: &[u8],
733) -> Result<(), zx::Status> {
734 let position = head % ring_buffer_size;
735 let vmo_offset = data_offset + position;
736 if position + data.len() as u64 <= ring_buffer_size {
737 vmo.write(data, vmo_offset)?;
738 } else {
739 let first_len = (ring_buffer_size - position) as usize;
740 vmo.write(&data[..first_len], vmo_offset)?;
741 vmo.write(&data[first_len..], data_offset)?;
742 }
743 Ok(())
744}
745
746#[derive(Debug, Clone)]
749struct PerfRecordSample<'a> {
750 pid: Option<u32>,
751 tid: Option<u32>,
752 time: zx::BootInstant,
754 ips: Vec<u64>,
756 regs: &'a [u8],
757 stack: &'a [u8],
758 regs_abi: u64,
760 sample_regs_user: u64,
761 sample_stack_user: u64,
762}
763
764async fn set_up_profiler(
765 sample_period: zx::MonotonicDuration,
766) -> Result<(profiler::SessionProxy, fidl::AsyncSocket), Errno> {
767 let sample = profiler::Sample {
769 callgraph: Some(profiler::CallgraphConfig {
770 strategy: Some(profiler::CallgraphStrategy::FramePointer),
771 ..Default::default()
772 }),
773 ..Default::default()
774 };
775
776 let sampling_config = profiler::SamplingConfig {
777 period: Some(sample_period.into_nanos() as u64),
778 timebase: Some(profiler::Counter::PlatformIndependent(profiler::CounterId::Nanoseconds)),
779 sample: Some(sample),
780 ..Default::default()
781 };
782
783 track_stub!(
784 TODO("https://fxbug.dev/398914921"),
785 "[perf_event_open] allow for profiling system-wide not during tests"
786 );
787 let job = fuchsia_runtime::job_default();
788 let koid = job.koid().map_err(|e| errno!(EINVAL, e.to_string()))?;
789 let tasks = vec![
790 profiler::Task::Job(koid.raw_koid()),
792 ];
793 let targets = profiler::TargetConfig::Tasks(tasks);
794 let config = profiler::Config {
795 configs: Some(vec![sampling_config]),
796 target: Some(targets),
797 ..Default::default()
798 };
799 let (client, server) = fidl::Socket::create_stream();
800 let configure = profiler::SessionConfigureRequest {
801 output: Some(server),
802 config: Some(config),
803 ..Default::default()
804 };
805
806 let proxy = connect_to_protocol::<profiler::SessionMarker>()
807 .context("Error connecting to Profiler protocol");
808 let session_proxy: profiler::SessionProxy = match proxy {
809 Ok(p) => p.clone(),
810 Err(e) => return error!(EINVAL, e),
811 };
812
813 let config_request = session_proxy.configure(configure).await;
815 match config_request {
816 Ok(_) => Ok((session_proxy, fidl::AsyncSocket::from_socket(client))),
817 Err(e) => return error!(EINVAL, e),
818 }
819}
820
821fn process_fxt_record(
825 record: TraceRecord,
826 seq_lock_wrapper: &SeqLock<PerfMetadataHeader, PerfMetadataValue>,
827 perf_data_vmo: &zx::Vmo,
828 sample_type: u64,
829 sample_id: u64,
830 sample_period: u64,
831 read_format: u64,
832 sample_regs_user: u64,
833 sample_stack_user: u64,
834 koid_session: Option<&PidKoidSession>,
835 vmo_write_offset: &mut u64,
836 lost_events: &mut u64,
837) {
838 match record {
839 TraceRecord::Profiler(ProfilerRecord::Backtrace(backtrace)) => {
840 let ips: Vec<u64> = backtrace.data;
841 let Some(LinuxIdentity::Thread { pid, tid }) = koid_session.and_then(|s| {
847 s.resolve_koids(
848 zx::Koid::from_raw(backtrace.process.0),
849 zx::Koid::from_raw(backtrace.thread.0),
850 )
851 }) else {
852 return;
853 };
854 let time = zx::BootInstant::from_nanos(backtrace.timestamp.max(0));
855 let perf_record_sample = PerfRecordSample {
856 pid: Some(pid as u32),
857 tid: Some(tid as u32),
858 time,
859 ips,
860 regs: &[],
861 stack: &[],
862 regs_abi: perf_sample_regs_abi_PERF_SAMPLE_REGS_ABI_NONE as u64,
863 sample_regs_user,
864 sample_stack_user,
865 };
866 let metadata = seq_lock_wrapper.get();
867 let bytes_written = write_record_to_vmo(
868 perf_record_sample,
869 perf_data_vmo,
870 sample_type,
871 sample_id,
872 sample_period,
873 read_format,
874 *vmo_write_offset,
875 &metadata,
876 lost_events,
877 );
878 if bytes_written > 0 {
882 *vmo_write_offset += bytes_written;
883 let mut metadata = seq_lock_wrapper.get();
884 metadata.data_head = *vmo_write_offset;
885 seq_lock_wrapper.set_value(metadata);
886 }
887 }
888 TraceRecord::LargeBlob(large_blob) => {
889 if large_blob.name != "stack_sample" {
894 return;
895 }
896 let Some(blob_metadata) = large_blob.metadata else {
897 return;
898 };
899 let bytes = &large_blob.bytes;
900 if bytes.len() < 8 {
901 return;
902 }
903 let regs_size = u64::from_ne_bytes(bytes[0..8].try_into().unwrap()) as usize;
904 let mut offset = 8;
905 if regs_size == 0 || bytes.len() < offset + regs_size {
906 return;
907 }
908
909 const REGS_BYTES: usize = 33 * 8;
915 if regs_size < REGS_BYTES + 8 {
916 return;
917 }
918 #[cfg(target_arch = "aarch64")]
919 let is_32bit = {
920 let cpsr = u64::from_ne_bytes(
921 bytes[offset + REGS_BYTES..offset + REGS_BYTES + 8].try_into().unwrap(),
922 );
923 (cpsr & zx::sys::ZX_REG_CPSR_ARCH_32_MASK) == zx::sys::ZX_REG_CPSR_ARCH_32_MASK
924 };
925 #[cfg(not(target_arch = "aarch64"))]
926 let is_32bit = false;
927
928 let mut blob_regs = bytes[offset..offset + REGS_BYTES].to_vec();
929 offset += regs_size;
930
931 if is_32bit {
932 let pc_offset = AARCH64_REG_PC * 8;
940 let pc_bytes = blob_regs[pc_offset..pc_offset + 8].to_vec();
941 let r15_offset = AARCH32_REG_R15 * 8;
942 blob_regs[r15_offset..r15_offset + 8].copy_from_slice(&pc_bytes);
943 }
944
945 let sp = if is_32bit {
946 let r13_offset = AARCH32_REG_R13 * 8;
948 u64::from_ne_bytes(blob_regs[r13_offset..r13_offset + 8].try_into().unwrap())
949 } else {
950 let sp_offset = AARCH64_REG_SP * 8;
951 u64::from_ne_bytes(blob_regs[sp_offset..sp_offset + 8].try_into().unwrap())
952 };
953 let pc_offset = AARCH64_REG_PC * 8;
954 let pc = u64::from_ne_bytes(blob_regs[pc_offset..pc_offset + 8].try_into().unwrap());
955
956 let mut stack: &[u8] = &[];
965 while offset + 16 <= bytes.len() {
966 let chunk_base = u64::from_ne_bytes(bytes[offset..offset + 8].try_into().unwrap());
967 offset += 8;
968 let chunk_size = u64::from_ne_bytes(bytes[offset..offset + 8].try_into().unwrap());
969 offset += 8;
970 if offset + chunk_size as usize > bytes.len() {
971 break;
972 }
973 let data = &bytes[offset..offset + chunk_size as usize];
974 offset += chunk_size as usize;
975 if stack.is_empty() && chunk_base <= sp && sp < chunk_base + chunk_size {
976 stack = &data[(sp - chunk_base) as usize..];
977 }
978 }
979 if stack.is_empty() {
980 return;
983 }
984
985 let Some(LinuxIdentity::Thread { pid, tid }) = koid_session.and_then(|s| {
986 s.resolve_koids(
987 zx::Koid::from_raw(blob_metadata.process.0),
988 zx::Koid::from_raw(blob_metadata.thread.0),
989 )
990 }) else {
991 return;
992 };
993 let time = zx::BootInstant::from_nanos(blob_metadata.timestamp.max(0));
994 let regs_abi = if is_32bit {
995 perf_sample_regs_abi_PERF_SAMPLE_REGS_ABI_32 as u64
996 } else {
997 perf_sample_regs_abi_PERF_SAMPLE_REGS_ABI_64 as u64
998 };
999 let perf_record_sample = PerfRecordSample {
1000 pid: Some(pid as u32),
1001 tid: Some(tid as u32),
1002 time,
1003 ips: vec![pc],
1004 regs: &blob_regs,
1005 stack,
1006 regs_abi,
1007 sample_regs_user,
1008 sample_stack_user,
1009 };
1010 let metadata = seq_lock_wrapper.get();
1011 let bytes_written = write_record_to_vmo(
1012 perf_record_sample,
1013 perf_data_vmo,
1014 sample_type,
1015 sample_id,
1016 sample_period,
1017 read_format,
1018 *vmo_write_offset,
1019 &metadata,
1020 lost_events,
1021 );
1022 if bytes_written > 0 {
1026 *vmo_write_offset += bytes_written;
1027 let mut metadata = seq_lock_wrapper.get();
1028 metadata.data_head = *vmo_write_offset;
1029 seq_lock_wrapper.set_value(metadata);
1030 }
1031 }
1032 _ => {}
1033 }
1034}
1035
1036fn ping_receiver(
1041 mut ioctl_sender: future_mpsc::Sender<(IoctlOp, sync_mpsc::Sender<()>)>,
1042 command: IoctlOp,
1043) {
1044 log_info!("[perf_event_open] Received sampling command: {:?}", command);
1045 let (profiling_complete_sender, profiling_complete_receiver) = sync_mpsc::channel::<()>();
1046 match ioctl_sender.try_send((command, profiling_complete_sender)) {
1047 Ok(_) => (),
1048 Err(e) => {
1049 if e.is_full() {
1050 log_warn!("[perf_event_open] Failed to send {:?}: Channel full", command);
1051 } else if e.is_disconnected() {
1052 log_warn!("[perf_event_open] Failed to send {:?}: Receiver disconnected", command);
1053 } else {
1054 log_warn!("[perf_event_open] Failed to send {:?} due to {:?}", command, e.source());
1055 }
1056 }
1057 };
1058 let _ = profiling_complete_receiver.recv().unwrap();
1061}
1062
1063unsafe fn create_seq_lock(
1072 vmo_handle_ref: &zx::NullableHandle,
1073 buffer_size: u64,
1074) -> SeqLock<PerfMetadataHeader, PerfMetadataValue> {
1075 let metadata_header = PerfMetadataHeader { version: 1, compat_version: 2 };
1077 let page_size = *PAGE_SIZE;
1078 let metadata_value = PerfMetadataValue {
1079 lock: 0,
1080 index: 3,
1081 offset: 19337,
1082 time_enabled: 0,
1083 time_running: 0,
1084 __bindgen_anon_1: perf_event_mmap_page__bindgen_ty_1 { capabilities: 30 },
1085 pmc_width: 0,
1086 time_shift: 0,
1087 time_mult: 0,
1088 time_offset: 0,
1089 time_zero: 0,
1090 size: 0,
1091 __reserved_1: 0,
1092 time_cycles: 0,
1093 time_mask: 0,
1094 __reserved: [0; 928usize],
1095 data_head: 0,
1097 data_tail: 0,
1099 data_offset: page_size,
1101 data_size: buffer_size - page_size,
1102 aux_head: 0,
1103 aux_tail: 0,
1104 aux_offset: 0,
1105 aux_size: 0,
1106 };
1107 let vmo = zx::Vmo::from(vmo_handle_ref.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap());
1108
1109 unsafe {
1117 SeqLock::new_from_vmo(metadata_header, metadata_value, vmo)
1118 .expect("failed to create seq_lock for perf metadata")
1119 }
1120}
1121
1122pub fn sys_perf_event_open(
1123 current_task: &CurrentTask,
1124 attr: UserRef<perf_event_attr>,
1125 tid: tid_t,
1127 cpu: i32,
1128 group_fd: FdNumber,
1129 _flags: u64,
1130) -> Result<SyscallResult, Errno> {
1131 let perf_event_attrs: perf_event_attr = current_task.read_object(attr)?;
1135
1136 if (perf_event_attrs.sample_type & perf_event_sample_format_PERF_SAMPLE_STACK_USER as u64) != 0
1137 {
1138 if perf_event_attrs.sample_stack_user % 8 != 0 {
1139 return error!(EINVAL);
1140 }
1141 }
1142
1143 if tid == -1 && cpu == -1 {
1144 return error!(EINVAL);
1145 }
1146
1147 let target_task_type = match tid {
1148 -1 => TargetTaskType::AllTasks,
1149 0 => TargetTaskType::CurrentTask,
1150 _ => {
1151 track_stub!(TODO("https://fxbug.dev/409621963"), "[perf_event_open] implement tid > 0");
1152 return error!(ENOSYS);
1153 }
1154 };
1155 security::check_perf_event_open_access(
1156 current_task,
1157 target_task_type,
1158 &perf_event_attrs,
1159 perf_event_attrs.type_.try_into()?,
1160 )?;
1161
1162 let (sender, mut receiver) = future_mpsc::channel(8);
1166
1167 let mut perf_event_file = PerfEventFileState::new(
1168 perf_event_attrs,
1169 0,
1170 perf_event_attrs.disabled(),
1171 perf_event_attrs.sample_type,
1172 zx::Vmo::create(ESTIMATED_MMAP_BUFFER_SIZE).unwrap(),
1173 sender,
1174 );
1175
1176 let read_format = perf_event_attrs.read_format;
1177
1178 if (read_format & perf_event_read_format_PERF_FORMAT_TOTAL_TIME_ENABLED as u64) != 0
1179 || (read_format & perf_event_read_format_PERF_FORMAT_TOTAL_TIME_RUNNING as u64) != 0
1180 {
1181 if perf_event_file.disabled == 0 {
1184 perf_event_file.most_recent_enabled_time =
1185 zx::MonotonicInstant::get().into_nanos() as u64;
1186 }
1187 perf_event_file.total_time_running = 0;
1189 }
1190
1191 let event_id = READ_FORMAT_ID_GENERATOR.fetch_add(1, Ordering::Relaxed);
1192 perf_event_file.rf_id = event_id;
1193
1194 if group_fd.raw() == -1 {
1195 perf_event_file.sample_id = event_id;
1196 } else {
1197 let group_file = current_task.files().get(group_fd)?;
1198 let group_file_object_id = group_file.id;
1199 let perf_state = get_perf_state(¤t_task.kernel);
1200 let events = perf_state.format_id_lookup_table.lock();
1201 if let Some(rf_id) = events.get(&group_file_object_id) {
1202 perf_event_file.sample_id = *rf_id;
1203 } else {
1204 return error!(EINVAL);
1205 }
1206 }
1207
1208 if (read_format & perf_event_read_format_PERF_FORMAT_GROUP as u64) != 0 {
1209 track_stub!(
1210 TODO("https://fxbug.dev/402238049"),
1211 "[perf_event_open] implement read_format group"
1212 );
1213 return error!(ENOSYS);
1214 }
1215 if (read_format & perf_event_read_format_PERF_FORMAT_LOST as u64) != 0 {
1216 track_stub!(
1217 TODO("https://fxbug.dev/402260383"),
1218 "[perf_event_open] implement read_format lost"
1219 );
1220 }
1221
1222 let mut vmo_handle_copy =
1224 perf_event_file.perf_data_vmo.as_handle_ref().duplicate_handle(zx::Rights::SAME_RIGHTS);
1225
1226 let sample_period_in_ticks = unsafe { perf_event_file.attr.__bindgen_anon_1.sample_period };
1229 let zx_sample_period = zx::MonotonicDuration::from_nanos(sample_period_in_ticks as i64);
1232
1233 let seq_lock =
1235 Arc::new(OnceLock::<Result<SeqLock<PerfMetadataHeader, PerfMetadataValue>, Errno>>::new());
1236 let cloned_seq_lock = Arc::clone(&seq_lock);
1237 let mut vmo_write_offset = 0;
1238
1239 let closure = async move |kthread_task: &CurrentTask| {
1240 let mut lost_events: u64 = 0;
1241
1242 while let Some((command, profiling_complete_receiver)) = receiver.next().await {
1244 if command != IoctlOp::Enable {
1246 let _ = profiling_complete_receiver.send(());
1247 continue;
1248 }
1249
1250 let (session_proxy, client) = match set_up_profiler(zx_sample_period).await {
1251 Ok(session) => session,
1252 Err(e) => {
1253 log_warn!("Failed to profile: {}", e);
1254 let _ = profiling_complete_receiver.send(());
1255 continue;
1256 }
1257 };
1258
1259 let pid_koid_session = kthread_task.kernel().trace_event_manager.open();
1263 let start_request =
1264 profiler::SessionStartRequest { buffer_results: Some(false), ..Default::default() };
1265 if let Err(e) = session_proxy.start(&start_request).await {
1266 log_warn!("Failed to start profiler: {:?}", e);
1267 let _ = profiling_complete_receiver.send(());
1268 continue;
1269 }
1270 let _ = profiling_complete_receiver.send(());
1271
1272 let vmo = zx::Vmo::from(
1273 vmo_handle_copy
1274 .as_mut()
1275 .expect("Failed to get VMO handle")
1276 .as_handle_ref()
1277 .duplicate_handle(zx::Rights::SAME_RIGHTS)
1278 .unwrap(),
1279 );
1280 let mut handle_record = |record: TraceRecord| {
1281 if let Some(Ok(seq_lock_wrapper)) = cloned_seq_lock.get() {
1284 process_fxt_record(
1285 record,
1286 seq_lock_wrapper,
1287 &vmo,
1288 perf_event_file.sample_type,
1289 perf_event_file.sample_id,
1290 sample_period_in_ticks,
1291 perf_event_file.attr.read_format,
1292 perf_event_file.attr.sample_regs_user,
1293 perf_event_file.attr.sample_stack_user as u64,
1294 Some(&pid_koid_session),
1295 &mut vmo_write_offset,
1296 &mut lost_events,
1297 );
1298 }
1299 };
1300
1301 let (stream, _parser_task) = SessionParser::new_async(client);
1305 let mut stream = stream.fuse();
1306 let mut stream_ended = false;
1307 let disable_ack = loop {
1308 if stream_ended {
1309 match receiver.next().await {
1312 Some((IoctlOp::Disable, ack)) => break Some(ack),
1313 Some((IoctlOp::Enable, ack)) => {
1314 let _ = ack.send(());
1316 }
1317 None => break None,
1318 }
1319 } else {
1320 futures::select_biased! {
1321 cmd = receiver.next() => match cmd {
1322 Some((IoctlOp::Disable, ack)) => break Some(ack),
1323 Some((IoctlOp::Enable, ack)) => {
1324 let _ = ack.send(());
1326 }
1327 None => break None,
1328 },
1329 record = stream.next() => match record {
1330 Some(Ok(record)) => handle_record(record),
1331 Some(Err(e)) => {
1332 log_warn!("[perf_event_open] Error parsing FXT: {:?}", e);
1338 stream_ended = true;
1339 break None;
1340 }
1341 None => {
1342 log_warn!("[perf_event_open] Profiler stream ended mid-session");
1343 stream_ended = true;
1344 }
1345 },
1346 }
1347 }
1348 };
1349
1350 let stop_and_reset = async {
1356 match session_proxy.stop().await {
1357 Ok(stats) => log_info!(
1358 "[perf_event_open] profiler samples_collected: {:?}",
1359 stats.samples_collected.unwrap_or(0)
1360 ),
1361 Err(e) => log_warn!("[perf_event_open] Failed to stop profiler: {:?}", e),
1362 }
1363 let _ = session_proxy.reset().await;
1366 };
1367 let drain = async move {
1368 if !stream_ended {
1369 while let Some(record) = stream.next().await {
1370 match record {
1371 Ok(record) => handle_record(record),
1372 Err(e) => {
1373 log_warn!("[perf_event_open] Error parsing FXT: {:?}", e);
1376 break;
1377 }
1378 }
1379 }
1380 }
1381 drop(stream);
1384 drop(_parser_task);
1385 };
1386 futures::join!(stop_and_reset, drain);
1387
1388 if let Some(ack) = disable_ack {
1391 let _ = ack.send(());
1392 }
1393 }
1394 };
1395 let req = SpawnRequestBuilder::new()
1396 .with_debug_name("perf-event-sampler")
1397 .with_async_closure(closure)
1398 .build();
1399 current_task.kernel().kthreads.spawner().spawn_from_request(req);
1400
1401 let file = Box::new(PerfEventFile {
1402 _tid: tid,
1403 _cpu: cpu,
1404 perf_event_file: perf_event_file.into(),
1405 security_state: security::perf_event_alloc(current_task),
1406 seq_lock: seq_lock,
1407 });
1408 let file_handle = Anon::new_private_file(current_task, file, OpenFlags::RDWR, "[perf_event]");
1410 let file_object_id = file_handle.id;
1411 let file_descriptor: Result<FdNumber, Errno> =
1412 current_task.add_file(file_handle, FdFlags::empty());
1413
1414 match file_descriptor {
1415 Ok(fd) => {
1416 if group_fd.raw() == -1 {
1417 let perf_state = get_perf_state(¤t_task.kernel);
1418 let mut events = perf_state.format_id_lookup_table.lock();
1419 events.insert(file_object_id, event_id);
1420 }
1421 Ok(fd.into())
1422 }
1423 Err(_) => {
1424 track_stub!(
1425 TODO("https://fxbug.dev/402453955"),
1426 "[perf_event_open] implement remaining error handling"
1427 );
1428 error!(EMFILE)
1429 }
1430 }
1431}
1432#[cfg(target_arch = "aarch64")]
1434mod arch32 {
1435 pub use super::sys_perf_event_open as sys_arch32_perf_event_open;
1436}
1437
1438#[cfg(target_arch = "aarch64")]
1439pub use arch32::*;
1440
1441use crate::mm::memory::MemoryObject;
1442use crate::mm::{MemoryAccessorExt, ProtectionFlags};
1443use crate::task::CurrentTask;
1444use crate::vfs::{
1445 Anon, FdFlags, FdNumber, FileObject, FileObjectId, FileObjectState, FileOps, InputBuffer,
1446 OutputBuffer,
1447};
1448use crate::{fileops_impl_nonseekable, fileops_impl_noop_sync};
1449
1450#[cfg(test)]
1451mod tests {
1452 use super::*;
1453 use crate::task::tracing::{TracePerformanceEventManager, ZirconIdentity};
1454
1455 #[::fuchsia::test]
1456 async fn test_process_fxt_record_resolves_pid_tid() {
1457 let manager = Arc::new(TracePerformanceEventManager::new(std::sync::Weak::new()));
1458 let session = manager.open();
1459 manager.record(
1460 42,
1461 43,
1462 ZirconIdentity { process: zx::Koid::from_raw(1001), thread: zx::Koid::from_raw(1002) },
1463 );
1464
1465 let perf_data_vmo = zx::Vmo::create(ESTIMATED_MMAP_BUFFER_SIZE).unwrap();
1466 let vmo_handle_copy =
1467 perf_data_vmo.as_handle_ref().duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap();
1468 let seq_lock = unsafe { create_seq_lock(&vmo_handle_copy, ESTIMATED_MMAP_BUFFER_SIZE) };
1470
1471 let sample_type = (perf_event_sample_format_PERF_SAMPLE_IP
1472 | perf_event_sample_format_PERF_SAMPLE_TID) as u64;
1473 let mut vmo_write_offset = 0;
1474 let mut lost_events = 0;
1475
1476 let mapped_record =
1478 TraceRecord::Profiler(ProfilerRecord::Backtrace(fxt::profiler::BacktraceRecord {
1479 timestamp: 1000,
1480 process: fxt::ProcessKoid(1001),
1481 thread: fxt::ThreadKoid(1002),
1482 num_records: 1,
1483 data: vec![0x12345678],
1484 }));
1485 process_fxt_record(
1486 mapped_record,
1487 &seq_lock,
1488 &perf_data_vmo,
1489 sample_type,
1490 0,
1491 0,
1492 0,
1493 0,
1494 0,
1495 Some(&session),
1496 &mut vmo_write_offset,
1497 &mut lost_events,
1498 );
1499
1500 let expected_record_size: u64 = 24;
1502 assert_eq!(vmo_write_offset, expected_record_size);
1503
1504 let metadata = seq_lock.get();
1505 assert_eq!(metadata.data_head, expected_record_size);
1506
1507 let mut record_bytes = [0u8; 24];
1508 perf_data_vmo.read(&mut record_bytes, metadata.data_offset).unwrap();
1509
1510 let record_type = u32::from_ne_bytes(record_bytes[0..4].try_into().unwrap());
1511 assert_eq!(record_type, perf_event_type_PERF_RECORD_SAMPLE);
1512
1513 let misc = u16::from_ne_bytes(record_bytes[4..6].try_into().unwrap());
1514 assert_eq!(misc, PERF_RECORD_MISC_USER as u16);
1515
1516 let size = u16::from_ne_bytes(record_bytes[6..8].try_into().unwrap());
1517 assert_eq!(size, expected_record_size as u16);
1518
1519 let ip = u64::from_ne_bytes(record_bytes[8..16].try_into().unwrap());
1520 assert_eq!(ip, 0x12345678);
1521
1522 let pid = u32::from_ne_bytes(record_bytes[16..20].try_into().unwrap());
1523 assert_eq!(pid, 42);
1524
1525 let tid = u32::from_ne_bytes(record_bytes[20..24].try_into().unwrap());
1526 assert_eq!(tid, 43);
1527
1528 let unmapped_record =
1530 TraceRecord::Profiler(ProfilerRecord::Backtrace(fxt::profiler::BacktraceRecord {
1531 timestamp: 2000,
1532 process: fxt::ProcessKoid(9999),
1533 thread: fxt::ThreadKoid(9998),
1534 num_records: 1,
1535 data: vec![0x87654321],
1536 }));
1537 process_fxt_record(
1538 unmapped_record,
1539 &seq_lock,
1540 &perf_data_vmo,
1541 sample_type,
1542 0,
1543 0,
1544 0,
1545 0,
1546 0,
1547 Some(&session),
1548 &mut vmo_write_offset,
1549 &mut lost_events,
1550 );
1551
1552 assert_eq!(vmo_write_offset, expected_record_size);
1554 let metadata = seq_lock.get();
1555 assert_eq!(metadata.data_head, expected_record_size);
1556 let mut trailing_bytes = [0u8; 24];
1557 perf_data_vmo
1558 .read(&mut trailing_bytes, metadata.data_offset + expected_record_size)
1559 .unwrap();
1560 assert_eq!(trailing_bytes, [0u8; 24]);
1561 }
1562
1563 #[::fuchsia::test]
1564 async fn test_write_record_to_vmo_regs_and_stack() {
1565 let perf_data_vmo = zx::Vmo::create(ESTIMATED_MMAP_BUFFER_SIZE).unwrap();
1566 let vmo_handle_copy =
1567 perf_data_vmo.as_handle_ref().duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap();
1568 let seq_lock = unsafe { create_seq_lock(&vmo_handle_copy, ESTIMATED_MMAP_BUFFER_SIZE) };
1570 let metadata = seq_lock.get();
1571 let mut lost_events = 0;
1572 let regs_data = [0x11u8; 16];
1573 let stack_data = [0x22u8; 16];
1574 let sample = PerfRecordSample {
1575 pid: Some(10),
1576 tid: Some(20),
1577 time: zx::BootInstant::from_nanos(123_456_789),
1578 ips: vec![0xdeadbeef],
1579 regs: ®s_data,
1580 stack: &stack_data,
1581 regs_abi: perf_sample_regs_abi_PERF_SAMPLE_REGS_ABI_32 as u64,
1582 sample_regs_user: 3,
1583 sample_stack_user: 16,
1584 };
1585 let sample_type = (perf_event_sample_format_PERF_SAMPLE_IP
1586 | perf_event_sample_format_PERF_SAMPLE_TID
1587 | perf_event_sample_format_PERF_SAMPLE_TIME
1588 | perf_event_sample_format_PERF_SAMPLE_REGS_USER
1589 | perf_event_sample_format_PERF_SAMPLE_STACK_USER) as u64;
1590 let written = write_record_to_vmo(
1591 sample,
1592 &perf_data_vmo,
1593 sample_type,
1594 0,
1595 0,
1596 0,
1597 0,
1598 &metadata,
1599 &mut lost_events,
1600 );
1601 assert!(written > 0);
1602
1603 let mut record_bytes = vec![0u8; written as usize];
1604 perf_data_vmo.read(&mut record_bytes, metadata.data_offset).unwrap();
1605
1606 let record_type = u32::from_ne_bytes(record_bytes[0..4].try_into().unwrap());
1607 assert_eq!(record_type, perf_event_type_PERF_RECORD_SAMPLE);
1608 let misc = u16::from_ne_bytes(record_bytes[4..6].try_into().unwrap());
1609 assert_eq!(misc, PERF_RECORD_MISC_USER as u16);
1610 let size = u16::from_ne_bytes(record_bytes[6..8].try_into().unwrap());
1611 assert_eq!(size as usize, written as usize);
1612
1613 let ip = u64::from_ne_bytes(record_bytes[8..16].try_into().unwrap());
1614 assert_eq!(ip, 0xdeadbeef);
1615
1616 let pid = u32::from_ne_bytes(record_bytes[16..20].try_into().unwrap());
1617 assert_eq!(pid, 10);
1618 let tid = u32::from_ne_bytes(record_bytes[20..24].try_into().unwrap());
1619 assert_eq!(tid, 20);
1620
1621 let time = u64::from_ne_bytes(record_bytes[24..32].try_into().unwrap());
1622 assert_eq!(time, 123_456_789);
1623
1624 let abi = u64::from_ne_bytes(record_bytes[32..40].try_into().unwrap());
1626 assert_eq!(abi, perf_sample_regs_abi_PERF_SAMPLE_REGS_ABI_32 as u64);
1627 assert_eq!(&record_bytes[40..56], ®s_data);
1628
1629 let stack_len = u64::from_ne_bytes(record_bytes[56..64].try_into().unwrap());
1631 assert_eq!(stack_len, 16);
1632 assert_eq!(&record_bytes[64..80], &stack_data);
1633 let dyn_size = u64::from_ne_bytes(record_bytes[80..88].try_into().unwrap());
1634 assert_eq!(dyn_size, 16);
1635 }
1636
1637 #[::fuchsia::test]
1638 async fn test_write_record_to_vmo_regs_abi_64() {
1639 let perf_data_vmo = zx::Vmo::create(ESTIMATED_MMAP_BUFFER_SIZE).unwrap();
1640 let vmo_handle_copy =
1641 perf_data_vmo.as_handle_ref().duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap();
1642 let seq_lock = unsafe { create_seq_lock(&vmo_handle_copy, ESTIMATED_MMAP_BUFFER_SIZE) };
1644 let metadata = seq_lock.get();
1645 let mut lost_events = 0;
1646 let regs_data = [0x44u8; 16];
1647 let stack_data = [0x55u8; 16];
1648 let sample = PerfRecordSample {
1649 pid: Some(10),
1650 tid: Some(20),
1651 time: zx::BootInstant::from_nanos(123_456_789),
1652 ips: vec![0xdeadbeef],
1653 regs: ®s_data,
1654 stack: &stack_data,
1655 regs_abi: perf_sample_regs_abi_PERF_SAMPLE_REGS_ABI_64 as u64,
1656 sample_regs_user: 3,
1657 sample_stack_user: 16,
1658 };
1659 let sample_type = (perf_event_sample_format_PERF_SAMPLE_IP
1660 | perf_event_sample_format_PERF_SAMPLE_TID
1661 | perf_event_sample_format_PERF_SAMPLE_TIME
1662 | perf_event_sample_format_PERF_SAMPLE_REGS_USER
1663 | perf_event_sample_format_PERF_SAMPLE_STACK_USER) as u64;
1664 let written = write_record_to_vmo(
1665 sample,
1666 &perf_data_vmo,
1667 sample_type,
1668 0,
1669 0,
1670 0,
1671 0,
1672 &metadata,
1673 &mut lost_events,
1674 );
1675 assert!(written > 0);
1676
1677 let mut record_bytes = vec![0u8; written as usize];
1678 perf_data_vmo.read(&mut record_bytes, metadata.data_offset).unwrap();
1679
1680 let abi = u64::from_ne_bytes(record_bytes[32..40].try_into().unwrap());
1682 assert_eq!(abi, perf_sample_regs_abi_PERF_SAMPLE_REGS_ABI_64 as u64);
1683 assert_eq!(&record_bytes[40..56], ®s_data);
1684 }
1685
1686 #[::fuchsia::test]
1687 async fn test_write_record_to_vmo_regs_abi_none_when_empty() {
1688 let perf_data_vmo = zx::Vmo::create(ESTIMATED_MMAP_BUFFER_SIZE).unwrap();
1689 let vmo_handle_copy =
1690 perf_data_vmo.as_handle_ref().duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap();
1691 let seq_lock = unsafe { create_seq_lock(&vmo_handle_copy, ESTIMATED_MMAP_BUFFER_SIZE) };
1693 let metadata = seq_lock.get();
1694 let mut lost_events = 0;
1695 let stack_data = [0x33u8; 8];
1696 let sample = PerfRecordSample {
1697 pid: Some(10),
1698 tid: Some(20),
1699 time: zx::BootInstant::from_nanos(999),
1700 ips: vec![0x1000],
1701 regs: &[],
1702 stack: &stack_data,
1703 regs_abi: perf_sample_regs_abi_PERF_SAMPLE_REGS_ABI_NONE as u64,
1704 sample_regs_user: 3,
1705 sample_stack_user: 8,
1706 };
1707 let sample_type = (perf_event_sample_format_PERF_SAMPLE_IP
1708 | perf_event_sample_format_PERF_SAMPLE_TID
1709 | perf_event_sample_format_PERF_SAMPLE_TIME
1710 | perf_event_sample_format_PERF_SAMPLE_REGS_USER
1711 | perf_event_sample_format_PERF_SAMPLE_STACK_USER) as u64;
1712 let written = write_record_to_vmo(
1713 sample,
1714 &perf_data_vmo,
1715 sample_type,
1716 0,
1717 0,
1718 0,
1719 0,
1720 &metadata,
1721 &mut lost_events,
1722 );
1723 assert!(written > 0);
1724
1725 let mut record_bytes = vec![0u8; written as usize];
1726 perf_data_vmo.read(&mut record_bytes, metadata.data_offset).unwrap();
1727
1728 let abi = u64::from_ne_bytes(record_bytes[32..40].try_into().unwrap());
1730 assert_eq!(abi, perf_sample_regs_abi_PERF_SAMPLE_REGS_ABI_NONE as u64);
1731
1732 let stack_len = u64::from_ne_bytes(record_bytes[40..48].try_into().unwrap());
1734 assert_eq!(stack_len, 8);
1735 assert_eq!(&record_bytes[48..56], &stack_data);
1736 let dyn_size = u64::from_ne_bytes(record_bytes[56..64].try_into().unwrap());
1737 assert_eq!(dyn_size, 8);
1738 }
1739}