1use crate::task::dynamic_thread_spawner::SpawnRequestBuilder;
6use anyhow::Context;
7use fidl_fuchsia_cpu_profiler as profiler;
8use fuchsia_component::client::connect_to_protocol;
9use fuchsia_runtime;
10use futures::StreamExt;
11use futures::channel::mpsc as future_mpsc;
12use regex_lite::Regex;
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 futures::io::{AsyncReadExt, Cursor};
20use fxt::TraceRecord;
21use fxt::profiler::ProfilerRecord;
22use fxt::session::SessionParser;
23use seq_lock::{SeqLock, SeqLockable, WriteSize};
24use starnix_logging::{log_info, log_warn, track_stub};
25use starnix_sync::{
26 FileOpsCore, LockDepMutex, LockDepRwLock, Locked, PerfEventLevel, PerfFormatIdLookupTableLock,
27 Unlocked,
28};
29use starnix_syscalls::{SUCCESS, SyscallArg, SyscallResult};
30use starnix_uapi::arch32::{
31 PERF_EVENT_IOC_DISABLE, PERF_EVENT_IOC_ENABLE, PERF_EVENT_IOC_ID,
32 PERF_EVENT_IOC_MODIFY_ATTRIBUTES, PERF_EVENT_IOC_PAUSE_OUTPUT, PERF_EVENT_IOC_PERIOD,
33 PERF_EVENT_IOC_QUERY_BPF, PERF_EVENT_IOC_REFRESH, PERF_EVENT_IOC_RESET, PERF_EVENT_IOC_SET_BPF,
34 PERF_EVENT_IOC_SET_FILTER, PERF_EVENT_IOC_SET_OUTPUT, PERF_RECORD_MISC_KERNEL,
35 perf_event_sample_format_PERF_SAMPLE_CALLCHAIN, perf_event_sample_format_PERF_SAMPLE_ID,
36 perf_event_sample_format_PERF_SAMPLE_IDENTIFIER, perf_event_sample_format_PERF_SAMPLE_IP,
37 perf_event_sample_format_PERF_SAMPLE_PERIOD, perf_event_sample_format_PERF_SAMPLE_TID,
38 perf_event_type_PERF_RECORD_SAMPLE,
39};
40use starnix_uapi::errors::Errno;
41use starnix_uapi::open_flags::OpenFlags;
42use starnix_uapi::user_address::UserRef;
43use starnix_uapi::{
44 errno, error, from_status_like_fdio, perf_event_attr, perf_event_header,
45 perf_event_mmap_page__bindgen_ty_1, perf_event_read_format_PERF_FORMAT_GROUP,
46 perf_event_read_format_PERF_FORMAT_ID, perf_event_read_format_PERF_FORMAT_LOST,
47 perf_event_read_format_PERF_FORMAT_TOTAL_TIME_ENABLED,
48 perf_event_read_format_PERF_FORMAT_TOTAL_TIME_RUNNING, tid_t, uapi,
49};
50
51use crate::security::{self, TargetTaskType};
52use crate::task::{Kernel, LockedAndTask};
53
54static READ_FORMAT_ID_GENERATOR: AtomicU64 = AtomicU64::new(0);
55const DEFAULT_CHUNK_SIZE: usize = 4096;
57const ESTIMATED_MMAP_BUFFER_SIZE: u64 = 40960;
61const FXT_MAGIC_BYTES: [u8; 8] = [0x10, 0x00, 0x04, 0x46, 0x78, 0x54, 0x16, 0x00];
63
64mod event;
65pub use event::{TraceEvent, TraceEventQueue, TraceEventQueueList};
66
67pub mod lockless_ring_buffer;
68
69#[repr(C)]
70#[derive(Copy, Clone, IntoBytes, Immutable)]
71struct PerfMetadataHeader {
72 version: u32,
73 compat_version: u32,
74}
75
76#[repr(C)]
77#[derive(Copy, Clone, IntoBytes, Immutable)]
78struct PerfMetadataValue {
79 lock: u32,
80 index: u32,
81 offset: i64,
82 time_enabled: u64,
83 time_running: u64,
84 __bindgen_anon_1: perf_event_mmap_page__bindgen_ty_1,
85 pmc_width: u16,
86 time_shift: u16,
87 time_mult: u32,
88 time_offset: u64,
89 time_zero: u64,
90 size: u32,
91 __reserved_1: u32,
92 time_cycles: u64,
93 time_mask: u64,
94 __reserved: [u8; 928usize],
95 data_head: u64,
96 data_tail: u64,
97 data_offset: u64,
98 data_size: u64,
99 aux_head: u64,
100 aux_tail: u64,
101 aux_offset: u64,
102 aux_size: u64,
103}
104
105unsafe impl SeqLockable for PerfMetadataValue {
109 const WRITE_SIZE: WriteSize = WriteSize::Eight;
110 const HAS_INLINE_SEQUENCE: bool = true;
111 const VMO_NAME: &'static [u8] = b"starnix:perf_event";
112}
113
114struct PerfState {
115 format_id_lookup_table: LockDepMutex<HashMap<FileObjectId, u64>, PerfFormatIdLookupTableLock>,
121}
122
123impl Default for PerfState {
124 fn default() -> Self {
125 Self { format_id_lookup_table: LockDepMutex::new(HashMap::new()) }
126 }
127}
128
129fn get_perf_state(kernel: &Arc<Kernel>) -> Arc<PerfState> {
130 kernel.expando.get_or_init(PerfState::default)
131}
132
133uapi::check_arch_independent_layout! {
134 perf_event_attr {
135 type_, size,
137 config,
138 __bindgen_anon_1,
139 sample_type,
140 read_format,
141 _bitfield_1,
142 __bindgen_anon_2,
143 bp_type,
144 __bindgen_anon_3,
145 __bindgen_anon_4,
146 branch_sample_type,
147 sample_regs_user,
148 sample_stack_user,
149 clockid,
150 sample_regs_intr,
151 aux_watermark,
152 sample_max_stack,
153 __reserved_2,
154 aux_sample_size,
155 __reserved_3,
156 sig_data,
157 config3,
158 }
159}
160
161#[derive(Clone, Copy, Debug, PartialEq)]
162enum IoctlOp {
163 Enable,
164 Disable,
165}
166
167struct PerfEventFileState {
168 attr: perf_event_attr,
169 rf_value: u64, most_recent_enabled_time: u64,
173 total_time_running: u64,
177 rf_id: u64,
178 sample_id: u64,
179 _rf_lost: u64,
180 disabled: u64,
181 sample_type: u64,
182 perf_data_vmo: zx::Vmo,
185 ioctl_sender: future_mpsc::Sender<(IoctlOp, sync_mpsc::Sender<()>)>,
187}
188
189impl PerfEventFileState {
192 fn new(
193 attr: perf_event_attr,
194 rf_value: u64,
195 disabled: u64,
196 sample_type: u64,
197 perf_data_vmo: zx::Vmo,
198 ioctl_sender: future_mpsc::Sender<(IoctlOp, sync_mpsc::Sender<()>)>,
199 ) -> PerfEventFileState {
200 PerfEventFileState {
201 attr,
202 rf_value,
203 most_recent_enabled_time: 0,
204 total_time_running: 0,
205 rf_id: 0,
206 sample_id: 0,
207 _rf_lost: 0,
208 disabled,
209 sample_type,
210 perf_data_vmo,
211 ioctl_sender,
212 }
213 }
214}
215
216pub struct PerfEventFile {
217 _tid: tid_t,
218 _cpu: i32,
219 perf_event_file: LockDepRwLock<PerfEventFileState, PerfEventLevel>,
220 pub security_state: security::PerfEventState,
222 seq_lock: Arc<OnceLock<Result<SeqLock<PerfMetadataHeader, PerfMetadataValue>, Errno>>>,
223}
224
225impl FileOps for PerfEventFile {
230 fileops_impl_nonseekable!();
232 fileops_impl_noop_sync!();
233
234 fn close(
235 self: Box<Self>,
236 _locked: &mut Locked<FileOpsCore>,
237 file: &FileObjectState,
238 current_task: &CurrentTask,
239 ) {
240 let perf_state = get_perf_state(¤t_task.kernel);
241 let mut events = perf_state.format_id_lookup_table.lock();
242 events.remove(&file.id);
243 }
244
245 fn read(
247 &self,
248 _locked: &mut Locked<FileOpsCore>,
249 _file: &FileObject,
250 current_task: &CurrentTask,
251 _offset: usize,
252 data: &mut dyn OutputBuffer,
253 ) -> Result<usize, Errno> {
254 let read_format_data = {
257 let mut perf_event_file = self.perf_event_file.write();
260
261 security::check_perf_event_read_access(current_task, &self)?;
262
263 let mut total_time_running_including_curr = perf_event_file.total_time_running;
264
265 if perf_event_file.disabled == 0 {
267 track_stub!(
272 TODO("https://fxbug.dev/402938671"),
273 "[perf_event_open] implement read_format value"
274 );
275 perf_event_file.rf_value += 1;
276
277 let curr_time = zx::MonotonicInstant::get().into_nanos() as u64;
279 total_time_running_including_curr +=
280 curr_time - perf_event_file.most_recent_enabled_time;
281 }
282
283 let mut output = Vec::<u8>::new();
284 let value = perf_event_file.rf_value.to_ne_bytes();
285 output.extend(value);
286
287 let read_format = perf_event_file.attr.read_format;
288
289 if (read_format & perf_event_read_format_PERF_FORMAT_TOTAL_TIME_ENABLED as u64) != 0 {
290 output.extend(total_time_running_including_curr.to_ne_bytes());
292 }
293 if (read_format & perf_event_read_format_PERF_FORMAT_TOTAL_TIME_RUNNING as u64) != 0 {
294 output.extend(total_time_running_including_curr.to_ne_bytes());
296 }
297 if (read_format & perf_event_read_format_PERF_FORMAT_ID as u64) != 0 {
298 output.extend(perf_event_file.rf_id.to_ne_bytes());
300 }
301
302 output
303 };
304
305 if data.available() < read_format_data.len() {
309 return error!(ENOSPC);
310 }
311 track_stub!(
312 TODO("https://fxbug.dev/402453955"),
313 "[perf_event_open] implement remaining error handling"
314 );
315
316 data.write(&read_format_data)
317 }
318
319 fn ioctl(
320 &self,
321 _locked: &mut Locked<Unlocked>,
322 _file: &FileObject,
323 current_task: &CurrentTask,
324 op: u32,
325 _arg: SyscallArg,
326 ) -> Result<SyscallResult, Errno> {
327 track_stub!(
328 TODO("https://fxbug.dev/405463320"),
329 "[perf_event_open] implement PERF_IOC_FLAG_GROUP"
330 );
331 security::check_perf_event_write_access(current_task, &self)?;
332 let mut perf_event_file = self.perf_event_file.write();
333 match op {
334 PERF_EVENT_IOC_ENABLE => {
335 if perf_event_file.disabled != 0 {
336 perf_event_file.disabled = 0; perf_event_file.most_recent_enabled_time =
338 zx::MonotonicInstant::get().into_nanos() as u64;
339 }
340
341 track_stub!(
344 TODO("https://fxbug.dev/398914921"),
345 "[perf_event_open] implement full sampling features"
346 );
347 if perf_event_file.attr.freq() == 0
348 && unsafe { perf_event_file.attr.__bindgen_anon_1.sample_period != 0 }
351 {
352 ping_receiver(perf_event_file.ioctl_sender.clone(), IoctlOp::Enable);
353 }
354 return Ok(SUCCESS);
355 }
356 PERF_EVENT_IOC_DISABLE => {
357 if perf_event_file.disabled == 0 {
358 perf_event_file.disabled = 1; let curr_time = zx::MonotonicInstant::get().into_nanos() as u64;
362 perf_event_file.total_time_running +=
363 curr_time - perf_event_file.most_recent_enabled_time;
364 }
365 if perf_event_file.attr.freq() == 0
366 && unsafe { perf_event_file.attr.__bindgen_anon_1.sample_period != 0 }
369 {
370 ping_receiver(perf_event_file.ioctl_sender.clone(), IoctlOp::Disable);
371 }
372 return Ok(SUCCESS);
373 }
374 PERF_EVENT_IOC_RESET => {
375 perf_event_file.rf_value = 0;
376 return Ok(SUCCESS);
377 }
378 PERF_EVENT_IOC_REFRESH
379 | PERF_EVENT_IOC_PERIOD
380 | PERF_EVENT_IOC_SET_OUTPUT
381 | PERF_EVENT_IOC_SET_FILTER
382 | PERF_EVENT_IOC_ID
383 | PERF_EVENT_IOC_SET_BPF
384 | PERF_EVENT_IOC_PAUSE_OUTPUT
385 | PERF_EVENT_IOC_MODIFY_ATTRIBUTES
386 | PERF_EVENT_IOC_QUERY_BPF => {
387 track_stub!(
388 TODO("https://fxbug.dev/404941053"),
389 "[perf_event_open] implement remaining ioctl() calls"
390 );
391 return error!(ENOSYS);
392 }
393 _ => error!(ENOTTY),
394 }
395 }
396
397 fn get_memory(
402 &self,
403 _locked: &mut Locked<FileOpsCore>,
404 _file: &FileObject,
405 current_task: &CurrentTask,
406 length: Option<usize>,
407 _prot: ProtectionFlags,
408 ) -> Result<Arc<MemoryObject>, Errno> {
409 let buffer_size: u64 = length.unwrap_or(0) as u64;
410 if buffer_size == 0 {
411 return error!(EINVAL);
412 }
413
414 self.seq_lock
415 .get_or_init(|| {
416 let perf_event_file = self.perf_event_file.read();
417 let vmo_copy = perf_event_file
418 .perf_data_vmo
419 .as_handle_ref()
420 .duplicate_handle(zx::Rights::SAME_RIGHTS)
421 .map_err(|status| from_status_like_fdio!(status))?;
422 Ok(unsafe { create_seq_lock(&vmo_copy, buffer_size) })
424 })
425 .as_ref()
426 .map_err(|e| e.clone())?;
427
428 security::check_perf_event_read_access(current_task, &self)?;
430 let perf_event_file = self.perf_event_file.read();
431 match perf_event_file
432 .perf_data_vmo
433 .as_handle_ref()
434 .duplicate_handle(zx::Rights::SAME_RIGHTS)
435 {
436 Ok(vmo) => {
437 let vmo: zx::Vmo = vmo.into();
438 let memory = MemoryObject::from(vmo);
439 return Ok(Arc::new(memory));
440 }
441 Err(_) => {
442 track_stub!(
443 TODO("https://fxbug.dev/416323134"),
444 "[perf_event_open] handle get_memory() errors"
445 );
446 return error!(EINVAL);
447 }
448 };
449 }
450
451 fn write(
452 &self,
453 _locked: &mut Locked<FileOpsCore>,
454 _file: &FileObject,
455 _current_task: &CurrentTask,
456 _offset: usize,
457 _data: &mut dyn InputBuffer,
458 ) -> Result<usize, Errno> {
459 track_stub!(
460 TODO("https://fxbug.dev/394960158"),
461 "[perf_event_open] implement perf event functions"
462 );
463 error!(ENOSYS)
464 }
465}
466
467fn write_record_to_vmo(
489 perf_record_sample: PerfRecordSample,
490 perf_data_vmo: &zx::Vmo,
491 sample_type: u64,
492 sample_id: u64,
493 sample_period: u64,
494 offset: u64,
495) -> u64 {
496 let mut sample = Vec::<u8>::new();
498 if (sample_type & perf_event_sample_format_PERF_SAMPLE_IDENTIFIER as u64) != 0 {
500 sample.extend(sample_id.to_ne_bytes());
501 }
502 if (sample_type & perf_event_sample_format_PERF_SAMPLE_IP as u64) != 0 {
504 sample.extend(perf_record_sample.ips[0].to_ne_bytes());
505 }
506
507 if (sample_type & perf_event_sample_format_PERF_SAMPLE_TID as u64) != 0 {
508 sample.extend(perf_record_sample.pid.expect("missing pid").to_ne_bytes());
510 sample.extend(perf_record_sample.tid.expect("missing tid").to_ne_bytes());
512 }
513
514 if (sample_type & perf_event_sample_format_PERF_SAMPLE_ID as u64) != 0 {
516 sample.extend(sample_id.to_ne_bytes());
517 }
518
519 if (sample_type & perf_event_sample_format_PERF_SAMPLE_PERIOD as u64) != 0 {
521 sample.extend(sample_period.to_ne_bytes());
522 }
523
524 if (sample_type & perf_event_sample_format_PERF_SAMPLE_CALLCHAIN as u64) != 0 {
525 sample.extend(perf_record_sample.ips.len().to_ne_bytes());
527
528 for i in perf_record_sample.ips {
530 sample.extend(i.to_ne_bytes());
531 }
532 }
533 let record_size: u64 = (std::mem::size_of::<perf_event_header>() + sample.len()) as u64;
539
540 track_stub!(
541 TODO("https://fxbug.dev/432501467"),
542 "[perf_event_open] determines whether the record is KERNEL or USER"
543 );
544 let perf_event_header = perf_event_header {
545 type_: perf_event_type_PERF_RECORD_SAMPLE,
546 misc: PERF_RECORD_MISC_KERNEL as u16,
547 size: record_size as u16,
548 };
549
550 let data_offset = offset + (zx::system_get_page_size() as u64);
556
557 match perf_data_vmo.write(&perf_event_header.as_bytes(), data_offset) {
559 Ok(_) => (),
560 Err(e) => log_warn!("Failed to write perf_event_header: {}", e),
561 }
562
563 match perf_data_vmo
565 .write(&sample, data_offset + (std::mem::size_of::<perf_event_header>() as u64))
566 {
567 Ok(_) => {
568 return record_size;
571 }
572 Err(e) => {
573 log_warn!("Failed to write PerfRecordSample to VMO due to: {}", e);
574 return 0;
576 }
577 }
578}
579
580#[derive(Debug, Clone)]
581struct PerfRecordSample {
582 pid: Option<u32>,
583 tid: Option<u32>,
584 ips: Vec<u64>,
586}
587
588fn parse_perf_record_sample_format(backtrace: &str) -> Option<PerfRecordSample> {
600 let mut pid: Option<u32> = None;
601 let mut tid: Option<u32> = None;
602 let mut ips: Vec<u64> = Vec::new();
603 let mut numbers_found = 0;
604 track_stub!(TODO("https://fxbug.dev/437171287"), "[perf_event_open] handle regex nuances");
605 let backtrace_regex =
606 Regex::new(r"^\s*\{\{\{bt:\d+:((0x[0-9a-fA-F]+)):(?:pc|ra)\}\}\}\s*$").unwrap();
607
608 for line in backtrace.lines() {
609 let trimmed_line = line.trim();
610 if numbers_found < 2 {
612 if let Ok(num) = trimmed_line.parse::<u32>() {
613 if numbers_found == 0 {
614 pid = Some(num);
615 } else {
616 tid = Some(num);
617 }
618 numbers_found += 1;
619 continue;
620 }
621 }
622
623 if let Some(parsed_bt) = backtrace_regex.captures(trimmed_line) {
625 let address_str = parsed_bt.get(1).unwrap().as_str();
626 if let Ok(ip_addr) = u64::from_str_radix(address_str.trim_start_matches("0x"), 16) {
627 ips.push(ip_addr);
628 }
629 }
630 }
631
632 if pid == None || tid == None || ips.is_empty() {
633 log_info!("No ips while getting PerfRecordSample");
635 None
636 } else {
637 Some(PerfRecordSample { pid: pid, tid: tid, ips: ips })
638 }
639}
640
641async fn set_up_profiler(
642 sample_period: zx::MonotonicDuration,
643) -> Result<(profiler::SessionProxy, fidl::AsyncSocket), Errno> {
644 let sample = profiler::Sample {
646 callgraph: Some(profiler::CallgraphConfig {
647 strategy: Some(profiler::CallgraphStrategy::FramePointer),
648 ..Default::default()
649 }),
650 ..Default::default()
651 };
652
653 let sampling_config = profiler::SamplingConfig {
654 period: Some(sample_period.into_nanos() as u64),
655 timebase: Some(profiler::Counter::PlatformIndependent(profiler::CounterId::Nanoseconds)),
656 sample: Some(sample),
657 ..Default::default()
658 };
659
660 track_stub!(
661 TODO("https://fxbug.dev/398914921"),
662 "[perf_event_open] allow for profiling system-wide not during tests"
663 );
664 let job = fuchsia_runtime::job_default();
665 let koid = job.koid().map_err(|e| errno!(EINVAL, e.to_string()))?;
666 let tasks = vec![
667 profiler::Task::Job(koid.raw_koid()),
669 ];
670 let targets = profiler::TargetConfig::Tasks(tasks);
671 let config = profiler::Config {
672 configs: Some(vec![sampling_config]),
673 target: Some(targets),
674 ..Default::default()
675 };
676 let (client, server) = fidl::Socket::create_stream();
677 let configure = profiler::SessionConfigureRequest {
678 output: Some(server),
679 config: Some(config),
680 ..Default::default()
681 };
682
683 let proxy = connect_to_protocol::<profiler::SessionMarker>()
684 .context("Error connecting to Profiler protocol");
685 let session_proxy: profiler::SessionProxy = match proxy {
686 Ok(p) => p.clone(),
687 Err(e) => return error!(EINVAL, e),
688 };
689
690 let config_request = session_proxy.configure(configure).await;
692 match config_request {
693 Ok(_) => Ok((session_proxy, fidl::AsyncSocket::from_socket(client))),
694 Err(e) => return error!(EINVAL, e),
695 }
696}
697
698async fn stop_and_collect_samples(
703 session_proxy: profiler::SessionProxy,
704 mut client: fidl::AsyncSocket,
705 seq_lock: &OnceLock<Result<SeqLock<PerfMetadataHeader, PerfMetadataValue>, Errno>>,
706 perf_data_vmo: &zx::Vmo,
707 sample_type: u64,
708 sample_id: u64,
709 sample_period: u64,
710 vmo_write_offset: &mut u64,
711) -> Result<(), Errno> {
712 let stats = session_proxy.stop().await;
713
714 let seq_lock_wrapper = match seq_lock.get() {
715 Some(Ok(l)) => l,
716 Some(Err(e)) => return Err(e.clone()),
718 None => return Ok(()),
720 };
721
722 let samples_collected = match stats {
723 Ok(stats) => stats.samples_collected.unwrap(),
724 Err(e) => return error!(EINVAL, e),
725 };
726
727 track_stub!(
728 TODO("https://fxbug.dev/422502681"),
729 "[perf_event_open] symbolize sample output and delete the below log_info"
730 );
731 log_info!("profiler samples_collected: {:?}", samples_collected);
732
733 let mut header = [0; 8];
735 let mut bytes_read = 0;
736 while bytes_read < 8 {
737 match client.read(&mut header[bytes_read..]).await {
738 Ok(0) => {
739 log_info!("[perf_event_open] Finished reading fxt record from socket.");
741 break;
742 }
743 Ok(n) => bytes_read += n,
744 Err(e) => {
745 log_warn!("[perf_event_open] Error reading from socket: {:?}", e);
746 break;
747 }
748 }
749 }
750
751 if bytes_read > 0 {
752 if bytes_read == 8 && header == FXT_MAGIC_BYTES {
753 let header_cursor = Cursor::new(header);
755 let reader = header_cursor.chain(client);
756 let (mut stream, _task) = SessionParser::new_async(reader);
757 while let Some(record_result) = stream.next().await {
758 match record_result {
759 Ok(TraceRecord::Profiler(ProfilerRecord::Backtrace(backtrace))) => {
760 let ips: Vec<u64> = backtrace.data;
761 let pid = Some(backtrace.process.0 as u32);
762 let tid = Some(backtrace.thread.0 as u32);
763 let perf_record_sample = PerfRecordSample { pid, tid, ips };
764 let bytes_written = write_record_to_vmo(
765 perf_record_sample,
766 perf_data_vmo,
767 sample_type,
768 sample_id,
769 sample_period,
770 *vmo_write_offset,
771 );
772 if bytes_written > 0 {
774 *vmo_write_offset += bytes_written;
775 let mut metadata = seq_lock_wrapper.get();
776 metadata.data_head = *vmo_write_offset;
777 seq_lock_wrapper.set_value(metadata);
778 }
779 }
780 Ok(_) => {
781 }
783 Err(e) => {
784 log_warn!("[perf_event_open] Error parsing FXT: {:?}", e);
785 break;
786 }
787 }
788 }
789 } else {
790 let mut buffer = vec![0; DEFAULT_CHUNK_SIZE];
794
795 loop {
796 let socket_data = client.read(&mut buffer).await;
799
800 match socket_data {
801 Ok(0) => {
802 log_info!("[perf_event_open] Finished reading from socket.");
804 break;
805 }
806 Ok(bytes_read) => {
807 let received_data = match std::str::from_utf8(&buffer[..bytes_read]) {
809 Ok(data) => data,
810 Err(e) => return error!(EINVAL, e),
811 };
812 if let Some(perf_record_sample) =
814 parse_perf_record_sample_format(received_data)
815 {
816 let bytes_written = write_record_to_vmo(
817 perf_record_sample,
818 perf_data_vmo,
819 sample_type,
820 sample_id,
821 sample_period,
822 *vmo_write_offset,
823 );
824 if bytes_written > 0 {
826 *vmo_write_offset += bytes_written;
827 let mut metadata = seq_lock_wrapper.get();
828 metadata.data_head = *vmo_write_offset;
829 seq_lock_wrapper.set_value(metadata);
830 }
831 }
832 }
833 Err(e) => {
834 log_warn!("[perf_event_open] Error reading from socket: {:?}", e);
835 break;
836 }
837 }
838 }
839 }
840 }
841
842 let reset_status = session_proxy.reset().await;
843 return match reset_status {
844 Ok(_) => Ok(()),
845 Err(e) => error!(EINVAL, e),
846 };
847}
848
849fn ping_receiver(
854 mut ioctl_sender: future_mpsc::Sender<(IoctlOp, sync_mpsc::Sender<()>)>,
855 command: IoctlOp,
856) {
857 log_info!("[perf_event_open] Received sampling command: {:?}", command);
858 let (profiling_complete_sender, profiling_complete_receiver) = sync_mpsc::channel::<()>();
859 match ioctl_sender.try_send((command, profiling_complete_sender)) {
860 Ok(_) => (),
861 Err(e) => {
862 if e.is_full() {
863 log_warn!("[perf_event_open] Failed to send {:?}: Channel full", command);
864 } else if e.is_disconnected() {
865 log_warn!("[perf_event_open] Failed to send {:?}: Receiver disconnected", command);
866 } else {
867 log_warn!("[perf_event_open] Failed to send {:?} due to {:?}", command, e.source());
868 }
869 }
870 };
871 let _ = profiling_complete_receiver.recv().unwrap();
874}
875
876unsafe fn create_seq_lock(
885 vmo_handle_ref: &zx::NullableHandle,
886 buffer_size: u64,
887) -> SeqLock<PerfMetadataHeader, PerfMetadataValue> {
888 let metadata_header = PerfMetadataHeader { version: 1, compat_version: 2 };
890 let page_size = zx::system_get_page_size() as u64;
891 let metadata_value = PerfMetadataValue {
892 lock: 0,
893 index: 3,
894 offset: 19337,
895 time_enabled: 0,
896 time_running: 0,
897 __bindgen_anon_1: perf_event_mmap_page__bindgen_ty_1 { capabilities: 30 },
898 pmc_width: 0,
899 time_shift: 0,
900 time_mult: 0,
901 time_offset: 0,
902 time_zero: 0,
903 size: 0,
904 __reserved_1: 0,
905 time_cycles: 0,
906 time_mask: 0,
907 __reserved: [0; 928usize],
908 data_head: 0,
910 data_tail: 0,
912 data_offset: page_size,
914 data_size: buffer_size - page_size,
915 aux_head: 0,
916 aux_tail: 0,
917 aux_offset: 0,
918 aux_size: 0,
919 };
920 let vmo = zx::Vmo::from(vmo_handle_ref.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap());
921
922 unsafe {
930 SeqLock::new_from_vmo(metadata_header, metadata_value, vmo)
931 .expect("failed to create seq_lock for perf metadata")
932 }
933}
934
935pub fn sys_perf_event_open(
936 locked: &mut Locked<Unlocked>,
937 current_task: &CurrentTask,
938 attr: UserRef<perf_event_attr>,
939 tid: tid_t,
941 cpu: i32,
942 group_fd: FdNumber,
943 _flags: u64,
944) -> Result<SyscallResult, Errno> {
945 let perf_event_attrs: perf_event_attr = current_task.read_object(attr)?;
949
950 if tid == -1 && cpu == -1 {
951 return error!(EINVAL);
952 }
953
954 let target_task_type = match tid {
955 -1 => TargetTaskType::AllTasks,
956 0 => TargetTaskType::CurrentTask,
957 _ => {
958 track_stub!(TODO("https://fxbug.dev/409621963"), "[perf_event_open] implement tid > 0");
959 return error!(ENOSYS);
960 }
961 };
962 security::check_perf_event_open_access(
963 current_task,
964 target_task_type,
965 &perf_event_attrs,
966 perf_event_attrs.type_.try_into()?,
967 )?;
968
969 let (sender, mut receiver) = future_mpsc::channel(8);
973
974 let mut perf_event_file = PerfEventFileState::new(
975 perf_event_attrs,
976 0,
977 perf_event_attrs.disabled(),
978 perf_event_attrs.sample_type,
979 zx::Vmo::create(ESTIMATED_MMAP_BUFFER_SIZE).unwrap(),
980 sender,
981 );
982
983 let read_format = perf_event_attrs.read_format;
984
985 if (read_format & perf_event_read_format_PERF_FORMAT_TOTAL_TIME_ENABLED as u64) != 0
986 || (read_format & perf_event_read_format_PERF_FORMAT_TOTAL_TIME_RUNNING as u64) != 0
987 {
988 if perf_event_file.disabled == 0 {
991 perf_event_file.most_recent_enabled_time =
992 zx::MonotonicInstant::get().into_nanos() as u64;
993 }
994 perf_event_file.total_time_running = 0;
996 }
997
998 let event_id = READ_FORMAT_ID_GENERATOR.fetch_add(1, Ordering::Relaxed);
999 perf_event_file.rf_id = event_id;
1000
1001 if group_fd.raw() == -1 {
1002 perf_event_file.sample_id = event_id;
1003 } else {
1004 let group_file = current_task.get_file(group_fd)?;
1005 let group_file_object_id = group_file.id;
1006 let perf_state = get_perf_state(¤t_task.kernel);
1007 let events = perf_state.format_id_lookup_table.lock();
1008 if let Some(rf_id) = events.get(&group_file_object_id) {
1009 perf_event_file.sample_id = *rf_id;
1010 } else {
1011 return error!(EINVAL);
1012 }
1013 }
1014
1015 if (read_format & perf_event_read_format_PERF_FORMAT_GROUP as u64) != 0 {
1016 track_stub!(
1017 TODO("https://fxbug.dev/402238049"),
1018 "[perf_event_open] implement read_format group"
1019 );
1020 return error!(ENOSYS);
1021 }
1022 if (read_format & perf_event_read_format_PERF_FORMAT_LOST as u64) != 0 {
1023 track_stub!(
1024 TODO("https://fxbug.dev/402260383"),
1025 "[perf_event_open] implement read_format lost"
1026 );
1027 }
1028
1029 let mut vmo_handle_copy =
1031 perf_event_file.perf_data_vmo.as_handle_ref().duplicate_handle(zx::Rights::SAME_RIGHTS);
1032
1033 let sample_period_in_ticks = unsafe { perf_event_file.attr.__bindgen_anon_1.sample_period };
1036 let zx_sample_period = zx::MonotonicDuration::from_nanos(sample_period_in_ticks as i64);
1039
1040 let seq_lock =
1042 Arc::new(OnceLock::<Result<SeqLock<PerfMetadataHeader, PerfMetadataValue>, Errno>>::new());
1043 let cloned_seq_lock = Arc::clone(&seq_lock);
1044 let mut vmo_write_offset = 0;
1045
1046 let closure = async move |_: LockedAndTask<'_>| {
1047 let mut profiler_state: Option<(profiler::SessionProxy, fidl::AsyncSocket)> = None;
1048
1049 while let Some((command, profiling_complete_receiver)) = receiver.next().await {
1051 match command {
1052 IoctlOp::Enable => {
1053 match set_up_profiler(zx_sample_period).await {
1054 Ok((session_proxy, client)) => {
1055 let start_request = profiler::SessionStartRequest {
1056 buffer_results: Some(true),
1057 buffer_size_mb: Some(8 as u64),
1058 ..Default::default()
1059 };
1060 if let Err(e) = session_proxy.start(&start_request).await {
1061 log_warn!("Failed to start profiling: {}", e);
1062 } else {
1063 profiler_state = Some((session_proxy, client));
1064 }
1065 }
1066 Err(e) => {
1067 log_warn!("Failed to profile: {}", e);
1068 }
1069 };
1070 let _ = profiling_complete_receiver.send(());
1072 }
1073 IoctlOp::Disable => {
1074 if let Some((session_proxy, client)) = profiler_state.take() {
1075 let handle = vmo_handle_copy
1076 .as_mut()
1077 .expect("Failed to get VMO handle")
1078 .as_handle_ref()
1079 .duplicate_handle(zx::Rights::SAME_RIGHTS)
1080 .unwrap();
1081
1082 if let Err(e) = stop_and_collect_samples(
1083 session_proxy,
1084 client,
1085 &cloned_seq_lock,
1086 &zx::Vmo::from(handle),
1087 perf_event_file.sample_type,
1088 perf_event_file.sample_id,
1089 sample_period_in_ticks,
1090 &mut vmo_write_offset,
1091 )
1092 .await
1093 {
1094 log_warn!("Failed to collect sample: {:?}", e);
1095 }
1096 }
1097 let _ = profiling_complete_receiver.send(());
1099 }
1100 }
1101 }
1102 ()
1103 };
1104 let req = SpawnRequestBuilder::new()
1105 .with_debug_name("perf-event-sampler")
1106 .with_async_closure(closure)
1107 .build();
1108 current_task.kernel().kthreads.spawner().spawn_from_request(req);
1109
1110 let file = Box::new(PerfEventFile {
1111 _tid: tid,
1112 _cpu: cpu,
1113 perf_event_file: LockDepRwLock::new(perf_event_file),
1114 security_state: security::perf_event_alloc(current_task),
1115 seq_lock: seq_lock,
1116 });
1117 let file_handle =
1119 Anon::new_private_file(locked, current_task, file, OpenFlags::RDWR, "[perf_event]");
1120 let file_object_id = file_handle.id;
1121 let file_descriptor: Result<FdNumber, Errno> =
1122 current_task.add_file(locked, file_handle, FdFlags::empty());
1123
1124 match file_descriptor {
1125 Ok(fd) => {
1126 if group_fd.raw() == -1 {
1127 let perf_state = get_perf_state(¤t_task.kernel);
1128 let mut events = perf_state.format_id_lookup_table.lock();
1129 events.insert(file_object_id, event_id);
1130 }
1131 Ok(fd.into())
1132 }
1133 Err(_) => {
1134 track_stub!(
1135 TODO("https://fxbug.dev/402453955"),
1136 "[perf_event_open] implement remaining error handling"
1137 );
1138 error!(EMFILE)
1139 }
1140 }
1141}
1142#[cfg(target_arch = "aarch64")]
1144mod arch32 {
1145 pub use super::sys_perf_event_open as sys_arch32_perf_event_open;
1146}
1147
1148#[cfg(target_arch = "aarch64")]
1149pub use arch32::*;
1150
1151use crate::mm::memory::MemoryObject;
1152use crate::mm::{MemoryAccessorExt, ProtectionFlags};
1153use crate::task::CurrentTask;
1154use crate::vfs::{
1155 Anon, FdFlags, FdNumber, FileObject, FileObjectId, FileObjectState, FileOps, InputBuffer,
1156 OutputBuffer,
1157};
1158use crate::{fileops_impl_nonseekable, fileops_impl_noop_sync};