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 futures::StreamExt;
10use futures::channel::mpsc as future_mpsc;
11use regex_lite::Regex;
12use std::collections::HashMap;
13use std::error::Error;
14use std::sync::atomic::{AtomicU64, Ordering};
15use std::sync::{Arc, OnceLock, mpsc as sync_mpsc};
16use zerocopy::{Immutable, IntoBytes};
17use zx::HandleBased;
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::{FileOpsCore, Locked, Mutex, RwLock, Unlocked};
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 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, LockedAndTask};
50
51static READ_FORMAT_ID_GENERATOR: AtomicU64 = AtomicU64::new(0);
52const DEFAULT_CHUNK_SIZE: usize = 4096;
54const ESTIMATED_MMAP_BUFFER_SIZE: u64 = 40960;
58const PERF_EVENT_HEADER_SIZE: u16 = 8;
60const FXT_MAGIC_BYTES: [u8; 8] = [0x10, 0x00, 0x04, 0x46, 0x78, 0x54, 0x16, 0x00];
62
63mod event;
64pub use event::{TraceEvent, TraceEventQueue, TraceEventQueueList};
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: Mutex<HashMap<FileObjectId, u64>>,
118}
119
120impl Default for PerfState {
121 fn default() -> Self {
122 Self { format_id_lookup_table: Mutex::new(HashMap::new()) }
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: RwLock<PerfEventFileState>,
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(
232 self: Box<Self>,
233 _locked: &mut Locked<FileOpsCore>,
234 file: &FileObjectState,
235 current_task: &CurrentTask,
236 ) {
237 let perf_state = get_perf_state(¤t_task.kernel);
238 let mut events = perf_state.format_id_lookup_table.lock();
239 events.remove(&file.id);
240 }
241
242 fn read(
244 &self,
245 _locked: &mut Locked<FileOpsCore>,
246 _file: &FileObject,
247 current_task: &CurrentTask,
248 _offset: usize,
249 data: &mut dyn OutputBuffer,
250 ) -> Result<usize, Errno> {
251 let read_format_data = {
254 let mut perf_event_file = self.perf_event_file.write();
257
258 security::check_perf_event_read_access(current_task, &self)?;
259
260 let mut total_time_running_including_curr = perf_event_file.total_time_running;
261
262 if perf_event_file.disabled == 0 {
264 track_stub!(
269 TODO("https://fxbug.dev/402938671"),
270 "[perf_event_open] implement read_format value"
271 );
272 perf_event_file.rf_value += 1;
273
274 let curr_time = zx::MonotonicInstant::get().into_nanos() as u64;
276 total_time_running_including_curr +=
277 curr_time - perf_event_file.most_recent_enabled_time;
278 }
279
280 let mut output = Vec::<u8>::new();
281 let value = perf_event_file.rf_value.to_ne_bytes();
282 output.extend(value);
283
284 let read_format = perf_event_file.attr.read_format;
285
286 if (read_format & perf_event_read_format_PERF_FORMAT_TOTAL_TIME_ENABLED as u64) != 0 {
287 output.extend(total_time_running_including_curr.to_ne_bytes());
289 }
290 if (read_format & perf_event_read_format_PERF_FORMAT_TOTAL_TIME_RUNNING as u64) != 0 {
291 output.extend(total_time_running_including_curr.to_ne_bytes());
293 }
294 if (read_format & perf_event_read_format_PERF_FORMAT_ID as u64) != 0 {
295 output.extend(perf_event_file.rf_id.to_ne_bytes());
297 }
298
299 output
300 };
301
302 if data.available() < read_format_data.len() {
306 return error!(ENOSPC);
307 }
308 track_stub!(
309 TODO("https://fxbug.dev/402453955"),
310 "[perf_event_open] implement remaining error handling"
311 );
312
313 data.write(&read_format_data)
314 }
315
316 fn ioctl(
317 &self,
318 _locked: &mut Locked<Unlocked>,
319 _file: &FileObject,
320 current_task: &CurrentTask,
321 op: u32,
322 _arg: SyscallArg,
323 ) -> Result<SyscallResult, Errno> {
324 track_stub!(
325 TODO("https://fxbug.dev/405463320"),
326 "[perf_event_open] implement PERF_IOC_FLAG_GROUP"
327 );
328 security::check_perf_event_write_access(current_task, &self)?;
329 let mut perf_event_file = self.perf_event_file.write();
330 match op {
331 PERF_EVENT_IOC_ENABLE => {
332 if perf_event_file.disabled != 0 {
333 perf_event_file.disabled = 0; perf_event_file.most_recent_enabled_time =
335 zx::MonotonicInstant::get().into_nanos() as u64;
336 }
337
338 track_stub!(
341 TODO("https://fxbug.dev/398914921"),
342 "[perf_event_open] implement full sampling features"
343 );
344 if perf_event_file.attr.freq() == 0
345 && unsafe { perf_event_file.attr.__bindgen_anon_1.sample_period != 0 }
348 {
349 ping_receiver(perf_event_file.ioctl_sender.clone(), IoctlOp::Enable);
350 }
351 return Ok(SUCCESS);
352 }
353 PERF_EVENT_IOC_DISABLE => {
354 if perf_event_file.disabled == 0 {
355 perf_event_file.disabled = 1; let curr_time = zx::MonotonicInstant::get().into_nanos() as u64;
359 perf_event_file.total_time_running +=
360 curr_time - perf_event_file.most_recent_enabled_time;
361 }
362 if perf_event_file.attr.freq() == 0
363 && unsafe { perf_event_file.attr.__bindgen_anon_1.sample_period != 0 }
366 {
367 ping_receiver(perf_event_file.ioctl_sender.clone(), IoctlOp::Disable);
368 }
369 return Ok(SUCCESS);
370 }
371 PERF_EVENT_IOC_RESET => {
372 perf_event_file.rf_value = 0;
373 return Ok(SUCCESS);
374 }
375 PERF_EVENT_IOC_REFRESH
376 | PERF_EVENT_IOC_PERIOD
377 | PERF_EVENT_IOC_SET_OUTPUT
378 | PERF_EVENT_IOC_SET_FILTER
379 | PERF_EVENT_IOC_ID
380 | PERF_EVENT_IOC_SET_BPF
381 | PERF_EVENT_IOC_PAUSE_OUTPUT
382 | PERF_EVENT_IOC_MODIFY_ATTRIBUTES
383 | PERF_EVENT_IOC_QUERY_BPF => {
384 track_stub!(
385 TODO("https://fxbug.dev/404941053"),
386 "[perf_event_open] implement remaining ioctl() calls"
387 );
388 return error!(ENOSYS);
389 }
390 _ => error!(ENOTTY),
391 }
392 }
393
394 fn get_memory(
399 &self,
400 _locked: &mut Locked<FileOpsCore>,
401 _file: &FileObject,
402 current_task: &CurrentTask,
403 length: Option<usize>,
404 _prot: ProtectionFlags,
405 ) -> Result<Arc<MemoryObject>, Errno> {
406 let buffer_size: u64 = length.unwrap_or(0) as u64;
407 if buffer_size == 0 {
408 return error!(EINVAL);
409 }
410
411 self.seq_lock
412 .get_or_init(|| {
413 let perf_event_file = self.perf_event_file.read();
414 let vmo_copy = perf_event_file
415 .perf_data_vmo
416 .as_handle_ref()
417 .duplicate(zx::Rights::SAME_RIGHTS)
418 .map_err(|status| from_status_like_fdio!(status))?;
419 Ok(unsafe { create_seq_lock(&vmo_copy, buffer_size) })
421 })
422 .as_ref()
423 .map_err(|e| e.clone())?;
424
425 security::check_perf_event_read_access(current_task, &self)?;
427 let perf_event_file = self.perf_event_file.read();
428 match perf_event_file.perf_data_vmo.as_handle_ref().duplicate(zx::Rights::SAME_RIGHTS) {
429 Ok(vmo) => {
430 let vmo: zx::Vmo = vmo.into();
431 let memory = MemoryObject::from(vmo);
432 return Ok(Arc::new(memory));
433 }
434 Err(_) => {
435 track_stub!(
436 TODO("https://fxbug.dev/416323134"),
437 "[perf_event_open] handle get_memory() errors"
438 );
439 return error!(EINVAL);
440 }
441 };
442 }
443
444 fn write(
445 &self,
446 _locked: &mut Locked<FileOpsCore>,
447 _file: &FileObject,
448 _current_task: &CurrentTask,
449 _offset: usize,
450 _data: &mut dyn InputBuffer,
451 ) -> Result<usize, Errno> {
452 track_stub!(
453 TODO("https://fxbug.dev/394960158"),
454 "[perf_event_open] implement perf event functions"
455 );
456 error!(ENOSYS)
457 }
458}
459
460fn write_record_to_vmo(
482 perf_record_sample: PerfRecordSample,
483 perf_data_vmo: &zx::Vmo,
484 sample_type: u64,
485 sample_id: u64,
486 sample_period: u64,
487 offset: u64,
488) -> u64 {
489 track_stub!(
491 TODO("https://fxbug.dev/432501467"),
492 "[perf_event_open] determines whether the record is KERNEL or USER"
493 );
494 let perf_event_header = perf_event_header {
495 type_: perf_event_type_PERF_RECORD_SAMPLE,
496 misc: PERF_RECORD_MISC_KERNEL as u16,
497 size: PERF_EVENT_HEADER_SIZE,
498 };
499
500 let data_offset = offset + (zx::system_get_page_size() as u64);
506
507 match perf_data_vmo.write(&perf_event_header.as_bytes(), data_offset) {
508 Ok(_) => (),
509 Err(e) => log_warn!("Failed to write perf_event_header: {}", e),
510 }
511
512 let mut sample = Vec::<u8>::new();
514 if (sample_type & perf_event_sample_format_PERF_SAMPLE_IDENTIFIER as u64) != 0 {
516 sample.extend(sample_id.to_ne_bytes());
517 }
518 if (sample_type & perf_event_sample_format_PERF_SAMPLE_IP as u64) != 0 {
520 sample.extend(perf_record_sample.ips[0].to_ne_bytes());
521 }
522
523 if (sample_type & perf_event_sample_format_PERF_SAMPLE_TID as u64) != 0 {
524 sample.extend(perf_record_sample.pid.expect("missing pid").to_ne_bytes());
526 sample.extend(perf_record_sample.tid.expect("missing tid").to_ne_bytes());
528 }
529
530 if (sample_type & perf_event_sample_format_PERF_SAMPLE_ID as u64) != 0 {
532 sample.extend(sample_id.to_ne_bytes());
533 }
534
535 if (sample_type & perf_event_sample_format_PERF_SAMPLE_PERIOD as u64) != 0 {
537 sample.extend(sample_period.to_ne_bytes());
538 }
539
540 if (sample_type & perf_event_sample_format_PERF_SAMPLE_CALLCHAIN as u64) != 0 {
541 sample.extend(perf_record_sample.ips.len().to_ne_bytes());
543
544 for i in perf_record_sample.ips {
546 sample.extend(i.to_ne_bytes());
547 }
548 }
549 match perf_data_vmo
552 .write(&sample, data_offset + (std::mem::size_of::<perf_event_header>() as u64))
553 {
554 Ok(_) => {
555 let bytes_written: u64 =
556 (std::mem::size_of::<perf_event_header>() + sample.len()) as u64;
557 return bytes_written;
560 }
561 Err(e) => {
562 log_warn!("Failed to write PerfRecordSample to VMO due to: {}", e);
563 return 0;
565 }
566 }
567}
568
569#[derive(Debug, Clone)]
570struct PerfRecordSample {
571 pid: Option<u32>,
572 tid: Option<u32>,
573 ips: Vec<u64>,
575}
576
577fn parse_perf_record_sample_format(backtrace: &str) -> Option<PerfRecordSample> {
589 let mut pid: Option<u32> = None;
590 let mut tid: Option<u32> = None;
591 let mut ips: Vec<u64> = Vec::new();
592 let mut numbers_found = 0;
593 track_stub!(TODO("https://fxbug.dev/437171287"), "[perf_event_open] handle regex nuances");
594 let backtrace_regex =
595 Regex::new(r"^\s*\{\{\{bt:\d+:((0x[0-9a-fA-F]+)):(?:pc|ra)\}\}\}\s*$").unwrap();
596
597 for line in backtrace.lines() {
598 let trimmed_line = line.trim();
599 if numbers_found < 2 {
601 if let Ok(num) = trimmed_line.parse::<u32>() {
602 if numbers_found == 0 {
603 pid = Some(num);
604 } else {
605 tid = Some(num);
606 }
607 numbers_found += 1;
608 continue;
609 }
610 }
611
612 if let Some(parsed_bt) = backtrace_regex.captures(trimmed_line) {
614 let address_str = parsed_bt.get(1).unwrap().as_str();
615 if let Ok(ip_addr) = u64::from_str_radix(address_str.trim_start_matches("0x"), 16) {
616 ips.push(ip_addr);
617 }
618 }
619 }
620
621 if pid == None || tid == None || ips.is_empty() {
622 log_info!("No ips while getting PerfRecordSample");
624 None
625 } else {
626 Some(PerfRecordSample { pid: pid, tid: tid, ips: ips })
627 }
628}
629
630async fn set_up_profiler(
631 sample_period: zx::MonotonicDuration,
632) -> Result<(profiler::SessionProxy, fidl::AsyncSocket), Errno> {
633 let sample = profiler::Sample {
635 callgraph: Some(profiler::CallgraphConfig {
636 strategy: Some(profiler::CallgraphStrategy::FramePointer),
637 ..Default::default()
638 }),
639 ..Default::default()
640 };
641
642 let sampling_config = profiler::SamplingConfig {
643 period: Some(sample_period.into_nanos() as u64),
644 timebase: Some(profiler::Counter::PlatformIndependent(profiler::CounterId::Nanoseconds)),
645 sample: Some(sample),
646 ..Default::default()
647 };
648
649 let tasks = vec![
650 profiler::Task::SystemWide(profiler::SystemWide {}),
652 ];
653 let targets = profiler::TargetConfig::Tasks(tasks);
654 let config = profiler::Config {
655 configs: Some(vec![sampling_config]),
656 target: Some(targets),
657 ..Default::default()
658 };
659 let (client, server) = fidl::Socket::create_stream();
660 let configure = profiler::SessionConfigureRequest {
661 output: Some(server),
662 config: Some(config),
663 ..Default::default()
664 };
665
666 let proxy = connect_to_protocol::<profiler::SessionMarker>()
667 .context("Error connecting to Profiler protocol");
668 let session_proxy: profiler::SessionProxy = match proxy {
669 Ok(p) => p.clone(),
670 Err(e) => return error!(EINVAL, e),
671 };
672
673 let config_request = session_proxy.configure(configure).await;
675 match config_request {
676 Ok(_) => Ok((session_proxy, fidl::AsyncSocket::from_socket(client))),
677 Err(e) => return error!(EINVAL, e),
678 }
679}
680
681async fn stop_and_collect_samples(
686 session_proxy: profiler::SessionProxy,
687 mut client: fidl::AsyncSocket,
688 seq_lock: &OnceLock<Result<SeqLock<PerfMetadataHeader, PerfMetadataValue>, Errno>>,
689 perf_data_vmo: &zx::Vmo,
690 sample_type: u64,
691 sample_id: u64,
692 sample_period: u64,
693 vmo_write_offset: &mut u64,
694) -> Result<(), Errno> {
695 let stats = session_proxy.stop().await;
696
697 let seq_lock_wrapper = match seq_lock.get() {
698 Some(Ok(l)) => l,
699 Some(Err(e)) => return Err(e.clone()),
701 None => return Ok(()),
703 };
704
705 let samples_collected = match stats {
706 Ok(stats) => stats.samples_collected.unwrap(),
707 Err(e) => return error!(EINVAL, e),
708 };
709
710 track_stub!(
711 TODO("https://fxbug.dev/422502681"),
712 "[perf_event_open] symbolize sample output and delete the below log_info"
713 );
714 log_info!("profiler samples_collected: {:?}", samples_collected);
715
716 let mut header = [0; 8];
718 let mut bytes_read = 0;
719 while bytes_read < 8 {
720 match client.read(&mut header[bytes_read..]).await {
721 Ok(0) => {
722 log_info!("[perf_event_open] Finished reading fxt record from socket.");
724 break;
725 }
726 Ok(n) => bytes_read += n,
727 Err(e) => {
728 log_warn!("[perf_event_open] Error reading from socket: {:?}", e);
729 break;
730 }
731 }
732 }
733
734 if bytes_read > 0 {
735 if bytes_read == 8 && header == FXT_MAGIC_BYTES {
736 let header_cursor = Cursor::new(header);
738 let reader = header_cursor.chain(client);
739 let (mut stream, _task) = SessionParser::new_async(reader);
740 while let Some(record_result) = stream.next().await {
741 match record_result {
742 Ok(TraceRecord::Profiler(ProfilerRecord::Backtrace(backtrace))) => {
743 let ips: Vec<u64> = backtrace.data;
744 let pid = Some(backtrace.process.0 as u32);
745 let tid = Some(backtrace.thread.0 as u32);
746 let perf_record_sample = PerfRecordSample { pid, tid, ips };
747 let bytes_written = write_record_to_vmo(
748 perf_record_sample,
749 perf_data_vmo,
750 sample_type,
751 sample_id,
752 sample_period,
753 *vmo_write_offset,
754 );
755 if bytes_written > 0 {
757 *vmo_write_offset += bytes_written;
758 let mut metadata = seq_lock_wrapper.get();
759 metadata.data_head = *vmo_write_offset;
760 seq_lock_wrapper.set_value(metadata);
761 }
762 }
763 Ok(_) => {
764 }
766 Err(e) => {
767 log_warn!("[perf_event_open] Error parsing FXT: {:?}", e);
768 break;
769 }
770 }
771 }
772 } else {
773 let mut buffer = vec![0; DEFAULT_CHUNK_SIZE];
777
778 loop {
779 let socket_data = client.read(&mut buffer).await;
782
783 match socket_data {
784 Ok(0) => {
785 log_info!("[perf_event_open] Finished reading from socket.");
787 break;
788 }
789 Ok(bytes_read) => {
790 let received_data = match std::str::from_utf8(&buffer[..bytes_read]) {
792 Ok(data) => data,
793 Err(e) => return error!(EINVAL, e),
794 };
795 if let Some(perf_record_sample) =
797 parse_perf_record_sample_format(received_data)
798 {
799 let bytes_written = write_record_to_vmo(
800 perf_record_sample,
801 perf_data_vmo,
802 sample_type,
803 sample_id,
804 sample_period,
805 *vmo_write_offset,
806 );
807 if bytes_written > 0 {
809 *vmo_write_offset += bytes_written;
810 let mut metadata = seq_lock_wrapper.get();
811 metadata.data_head = *vmo_write_offset;
812 seq_lock_wrapper.set_value(metadata);
813 }
814 }
815 }
816 Err(e) => {
817 log_warn!("[perf_event_open] Error reading from socket: {:?}", e);
818 break;
819 }
820 }
821 }
822 }
823 }
824
825 let reset_status = session_proxy.reset().await;
826 return match reset_status {
827 Ok(_) => Ok(()),
828 Err(e) => error!(EINVAL, e),
829 };
830}
831
832fn ping_receiver(
837 mut ioctl_sender: future_mpsc::Sender<(IoctlOp, sync_mpsc::Sender<()>)>,
838 command: IoctlOp,
839) {
840 log_info!("[perf_event_open] Received sampling command: {:?}", command);
841 let (profiling_complete_sender, profiling_complete_receiver) = sync_mpsc::channel::<()>();
842 match ioctl_sender.try_send((command, profiling_complete_sender)) {
843 Ok(_) => (),
844 Err(e) => {
845 if e.is_full() {
846 log_warn!("[perf_event_open] Failed to send {:?}: Channel full", command);
847 } else if e.is_disconnected() {
848 log_warn!("[perf_event_open] Failed to send {:?}: Receiver disconnected", command);
849 } else {
850 log_warn!("[perf_event_open] Failed to send {:?} due to {:?}", command, e.source());
851 }
852 }
853 };
854 let _ = profiling_complete_receiver.recv().unwrap();
857}
858
859unsafe fn create_seq_lock(
868 vmo_handle_ref: &zx::NullableHandle,
869 buffer_size: u64,
870) -> SeqLock<PerfMetadataHeader, PerfMetadataValue> {
871 let metadata_header = PerfMetadataHeader { version: 1, compat_version: 2 };
873 let page_size = zx::system_get_page_size() as u64;
874 let metadata_value = PerfMetadataValue {
875 lock: 0,
876 index: 3,
877 offset: 19337,
878 time_enabled: 0,
879 time_running: 0,
880 __bindgen_anon_1: perf_event_mmap_page__bindgen_ty_1 { capabilities: 30 },
881 pmc_width: 0,
882 time_shift: 0,
883 time_mult: 0,
884 time_offset: 0,
885 time_zero: 0,
886 size: 0,
887 __reserved_1: 0,
888 time_cycles: 0,
889 time_mask: 0,
890 __reserved: [0; 928usize],
891 data_head: 0,
893 data_tail: 0,
895 data_offset: page_size,
897 data_size: buffer_size - page_size,
898 aux_head: 0,
899 aux_tail: 0,
900 aux_offset: 0,
901 aux_size: 0,
902 };
903 let vmo = zx::Vmo::from(vmo_handle_ref.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap());
904
905 unsafe {
913 SeqLock::new_from_vmo(metadata_header, metadata_value, vmo)
914 .expect("failed to create seq_lock for perf metadata")
915 }
916}
917
918pub fn sys_perf_event_open(
919 locked: &mut Locked<Unlocked>,
920 current_task: &CurrentTask,
921 attr: UserRef<perf_event_attr>,
922 tid: tid_t,
924 cpu: i32,
925 group_fd: FdNumber,
926 _flags: u64,
927) -> Result<SyscallResult, Errno> {
928 let perf_event_attrs: perf_event_attr = current_task.read_object(attr)?;
932
933 if tid == -1 && cpu == -1 {
934 return error!(EINVAL);
935 }
936
937 let target_task_type = match tid {
938 -1 => TargetTaskType::AllTasks,
939 0 => TargetTaskType::CurrentTask,
940 _ => {
941 track_stub!(TODO("https://fxbug.dev/409621963"), "[perf_event_open] implement tid > 0");
942 return error!(ENOSYS);
943 }
944 };
945 security::check_perf_event_open_access(
946 current_task,
947 target_task_type,
948 &perf_event_attrs,
949 perf_event_attrs.type_.try_into()?,
950 )?;
951
952 let (sender, mut receiver) = future_mpsc::channel(8);
956
957 let mut perf_event_file = PerfEventFileState::new(
958 perf_event_attrs,
959 0,
960 perf_event_attrs.disabled(),
961 perf_event_attrs.sample_type,
962 zx::Vmo::create(ESTIMATED_MMAP_BUFFER_SIZE).unwrap(),
963 sender,
964 );
965
966 let read_format = perf_event_attrs.read_format;
967
968 if (read_format & perf_event_read_format_PERF_FORMAT_TOTAL_TIME_ENABLED as u64) != 0
969 || (read_format & perf_event_read_format_PERF_FORMAT_TOTAL_TIME_RUNNING as u64) != 0
970 {
971 if perf_event_file.disabled == 0 {
974 perf_event_file.most_recent_enabled_time =
975 zx::MonotonicInstant::get().into_nanos() as u64;
976 }
977 perf_event_file.total_time_running = 0;
979 }
980
981 let event_id = READ_FORMAT_ID_GENERATOR.fetch_add(1, Ordering::Relaxed);
982 perf_event_file.rf_id = event_id;
983
984 if group_fd.raw() == -1 {
985 perf_event_file.sample_id = event_id;
986 } else {
987 let group_file = current_task.get_file(group_fd)?;
988 let group_file_object_id = group_file.id;
989 let perf_state = get_perf_state(¤t_task.kernel);
990 let events = perf_state.format_id_lookup_table.lock();
991 if let Some(rf_id) = events.get(&group_file_object_id) {
992 perf_event_file.sample_id = *rf_id;
993 } else {
994 return error!(EINVAL);
995 }
996 }
997
998 if (read_format & perf_event_read_format_PERF_FORMAT_GROUP as u64) != 0 {
999 track_stub!(
1000 TODO("https://fxbug.dev/402238049"),
1001 "[perf_event_open] implement read_format group"
1002 );
1003 return error!(ENOSYS);
1004 }
1005 if (read_format & perf_event_read_format_PERF_FORMAT_LOST as u64) != 0 {
1006 track_stub!(
1007 TODO("https://fxbug.dev/402260383"),
1008 "[perf_event_open] implement read_format lost"
1009 );
1010 }
1011
1012 let mut vmo_handle_copy =
1014 perf_event_file.perf_data_vmo.as_handle_ref().duplicate(zx::Rights::SAME_RIGHTS);
1015
1016 let sample_period_in_ticks = unsafe { perf_event_file.attr.__bindgen_anon_1.sample_period };
1019 let zx_sample_period = zx::MonotonicDuration::from_nanos(sample_period_in_ticks as i64);
1022
1023 let seq_lock =
1025 Arc::new(OnceLock::<Result<SeqLock<PerfMetadataHeader, PerfMetadataValue>, Errno>>::new());
1026 let cloned_seq_lock = Arc::clone(&seq_lock);
1027 let mut vmo_write_offset = 0;
1028
1029 let closure = async move |_: LockedAndTask<'_>| {
1030 let mut profiler_state: Option<(profiler::SessionProxy, fidl::AsyncSocket)> = None;
1031
1032 while let Some((command, profiling_complete_receiver)) = receiver.next().await {
1034 match command {
1035 IoctlOp::Enable => {
1036 match set_up_profiler(zx_sample_period).await {
1037 Ok((session_proxy, client)) => {
1038 let start_request = profiler::SessionStartRequest {
1039 buffer_results: Some(true),
1040 buffer_size_mb: Some(8 as u64),
1041 ..Default::default()
1042 };
1043 if let Err(e) = session_proxy.start(&start_request).await {
1044 log_warn!("Failed to start profiling: {}", e);
1045 } else {
1046 profiler_state = Some((session_proxy, client));
1047 }
1048 }
1049 Err(e) => {
1050 log_warn!("Failed to profile: {}", e);
1051 }
1052 };
1053 let _ = profiling_complete_receiver.send(());
1055 }
1056 IoctlOp::Disable => {
1057 if let Some((session_proxy, client)) = profiler_state.take() {
1058 let handle = vmo_handle_copy
1059 .as_mut()
1060 .expect("Failed to get VMO handle")
1061 .as_handle_ref()
1062 .duplicate(zx::Rights::SAME_RIGHTS)
1063 .unwrap();
1064
1065 if let Err(e) = stop_and_collect_samples(
1066 session_proxy,
1067 client,
1068 &cloned_seq_lock,
1069 &zx::Vmo::from(handle),
1070 perf_event_file.sample_type,
1071 perf_event_file.sample_id,
1072 sample_period_in_ticks,
1073 &mut vmo_write_offset,
1074 )
1075 .await
1076 {
1077 log_warn!("Failed to collect sample: {:?}", e);
1078 }
1079 }
1080 let _ = profiling_complete_receiver.send(());
1082 }
1083 }
1084 }
1085 ()
1086 };
1087 let req = SpawnRequestBuilder::new()
1088 .with_debug_name("perf-event-sampler")
1089 .with_async_closure(closure)
1090 .build();
1091 current_task.kernel().kthreads.spawner().spawn_from_request(req);
1092
1093 let file = Box::new(PerfEventFile {
1094 _tid: tid,
1095 _cpu: cpu,
1096 perf_event_file: RwLock::new(perf_event_file),
1097 security_state: security::perf_event_alloc(current_task),
1098 seq_lock: seq_lock,
1099 });
1100 let file_handle =
1102 Anon::new_private_file(locked, current_task, file, OpenFlags::RDWR, "[perf_event]");
1103 let file_object_id = file_handle.id;
1104 let file_descriptor: Result<FdNumber, Errno> =
1105 current_task.add_file(locked, file_handle, FdFlags::empty());
1106
1107 match file_descriptor {
1108 Ok(fd) => {
1109 if group_fd.raw() == -1 {
1110 let perf_state = get_perf_state(¤t_task.kernel);
1111 let mut events = perf_state.format_id_lookup_table.lock();
1112 events.insert(file_object_id, event_id);
1113 }
1114 Ok(fd.into())
1115 }
1116 Err(_) => {
1117 track_stub!(
1118 TODO("https://fxbug.dev/402453955"),
1119 "[perf_event_open] implement remaining error handling"
1120 );
1121 error!(EMFILE)
1122 }
1123 }
1124}
1125#[cfg(target_arch = "aarch64")]
1127mod arch32 {
1128 pub use super::sys_perf_event_open as sys_arch32_perf_event_open;
1129}
1130
1131#[cfg(target_arch = "aarch64")]
1132pub use arch32::*;
1133
1134use crate::mm::memory::MemoryObject;
1135use crate::mm::{MemoryAccessorExt, ProtectionFlags};
1136use crate::task::CurrentTask;
1137use crate::vfs::{
1138 Anon, FdFlags, FdNumber, FileObject, FileObjectId, FileObjectState, FileOps, InputBuffer,
1139 OutputBuffer,
1140};
1141use crate::{fileops_impl_nonseekable, fileops_impl_noop_sync};