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