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::{AtomicPtr, AtomicU64, Ordering};
15use std::sync::{Arc, 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, perf_event_attr, perf_event_header, perf_event_mmap_page__bindgen_ty_1,
42 perf_event_read_format_PERF_FORMAT_GROUP, perf_event_read_format_PERF_FORMAT_ID,
43 perf_event_read_format_PERF_FORMAT_LOST, perf_event_read_format_PERF_FORMAT_TOTAL_TIME_ENABLED,
44 perf_event_read_format_PERF_FORMAT_TOTAL_TIME_RUNNING, tid_t, uapi,
45};
46
47use crate::security::{self, TargetTaskType};
48use crate::task::{Kernel, LockedAndTask};
49
50static READ_FORMAT_ID_GENERATOR: AtomicU64 = AtomicU64::new(0);
51const DEFAULT_CHUNK_SIZE: usize = 4096;
53const ESTIMATED_MMAP_BUFFER_SIZE: u64 = 40960;
57const PERF_EVENT_HEADER_SIZE: u16 = 8;
59const FXT_MAGIC_BYTES: [u8; 8] = [0x10, 0x00, 0x04, 0x46, 0x78, 0x54, 0x16, 0x00];
61
62mod event;
63pub use event::{TraceEvent, TraceEventQueue};
64
65#[repr(C)]
66#[derive(Copy, Clone, IntoBytes, Immutable)]
67struct PerfMetadataHeader {
68 version: u32,
69 compat_version: u32,
70}
71
72#[repr(C)]
73#[derive(Copy, Clone, IntoBytes, Immutable)]
74struct PerfMetadataValue {
75 lock: u32,
76 index: u32,
77 offset: i64,
78 time_enabled: u64,
79 time_running: u64,
80 __bindgen_anon_1: perf_event_mmap_page__bindgen_ty_1,
81 pmc_width: u16,
82 time_shift: u16,
83 time_mult: u32,
84 time_offset: u64,
85 time_zero: u64,
86 size: u32,
87 __reserved_1: u32,
88 time_cycles: u64,
89 time_mask: u64,
90 __reserved: [u8; 928usize],
91 data_head: u64,
92 data_tail: u64,
93 data_offset: u64,
94 data_size: u64,
95 aux_head: u64,
96 aux_tail: u64,
97 aux_offset: u64,
98 aux_size: u64,
99}
100
101unsafe impl SeqLockable for PerfMetadataValue {
105 const WRITE_SIZE: WriteSize = WriteSize::Eight;
106 const HAS_INLINE_SEQUENCE: bool = true;
107 const VMO_NAME: &'static [u8] = b"starnix:perf_event";
108}
109
110struct PerfState {
111 format_id_lookup_table: Mutex<HashMap<FileObjectId, u64>>,
117}
118
119impl Default for PerfState {
120 fn default() -> Self {
121 Self { format_id_lookup_table: Mutex::new(HashMap::new()) }
122 }
123}
124
125fn get_perf_state(kernel: &Arc<Kernel>) -> Arc<PerfState> {
126 kernel.expando.get_or_init(PerfState::default)
127}
128
129uapi::check_arch_independent_layout! {
130 perf_event_attr {
131 type_, size,
133 config,
134 __bindgen_anon_1,
135 sample_type,
136 read_format,
137 _bitfield_1,
138 __bindgen_anon_2,
139 bp_type,
140 __bindgen_anon_3,
141 __bindgen_anon_4,
142 branch_sample_type,
143 sample_regs_user,
144 sample_stack_user,
145 clockid,
146 sample_regs_intr,
147 aux_watermark,
148 sample_max_stack,
149 __reserved_2,
150 aux_sample_size,
151 __reserved_3,
152 sig_data,
153 config3,
154 }
155}
156
157#[derive(Clone, Copy, Debug, PartialEq)]
158enum IoctlOp {
159 Enable,
160 Disable,
161}
162
163struct PerfEventFileState {
164 attr: perf_event_attr,
165 rf_value: u64, most_recent_enabled_time: u64,
169 total_time_running: u64,
173 rf_id: u64,
174 sample_id: u64,
175 _rf_lost: u64,
176 disabled: u64,
177 sample_type: u64,
178 perf_data_vmo: zx::Vmo,
181 vmo_write_offset: u64,
184 ioctl_sender: future_mpsc::Sender<(IoctlOp, sync_mpsc::Sender<()>)>,
186}
187
188impl PerfEventFileState {
191 fn new(
192 attr: perf_event_attr,
193 rf_value: u64,
194 disabled: u64,
195 sample_type: u64,
196 perf_data_vmo: zx::Vmo,
197 vmo_write_offset: u64,
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 vmo_write_offset,
212 ioctl_sender,
213 }
214 }
215}
216
217pub struct PerfEventFile {
218 _tid: tid_t,
219 _cpu: i32,
220 perf_event_file: RwLock<PerfEventFileState>,
221 pub security_state: security::PerfEventState,
223 _data_head_pointer: Arc<AtomicPtr<u64>>,
227 seq_lock: SeqLock<PerfMetadataHeader, PerfMetadataValue>,
228}
229
230impl FileOps for PerfEventFile {
235 fileops_impl_nonseekable!();
237 fileops_impl_noop_sync!();
238
239 fn close(
240 self: Box<Self>,
241 _locked: &mut Locked<FileOpsCore>,
242 file: &FileObjectState,
243 current_task: &CurrentTask,
244 ) {
245 let perf_state = get_perf_state(¤t_task.kernel);
246 let mut events = perf_state.format_id_lookup_table.lock();
247 events.remove(&file.id);
248 }
249
250 fn read(
252 &self,
253 _locked: &mut Locked<FileOpsCore>,
254 _file: &FileObject,
255 current_task: &CurrentTask,
256 _offset: usize,
257 data: &mut dyn OutputBuffer,
258 ) -> Result<usize, Errno> {
259 let read_format_data = {
262 let mut perf_event_file = self.perf_event_file.write();
265
266 security::check_perf_event_read_access(current_task, &self)?;
267
268 let mut total_time_running_including_curr = perf_event_file.total_time_running;
269
270 if perf_event_file.disabled == 0 {
272 track_stub!(
277 TODO("https://fxbug.dev/402938671"),
278 "[perf_event_open] implement read_format value"
279 );
280 perf_event_file.rf_value += 1;
281
282 let curr_time = zx::MonotonicInstant::get().into_nanos() as u64;
284 total_time_running_including_curr +=
285 curr_time - perf_event_file.most_recent_enabled_time;
286 }
287
288 let mut output = Vec::<u8>::new();
289 let value = perf_event_file.rf_value.to_ne_bytes();
290 output.extend(value);
291
292 let read_format = perf_event_file.attr.read_format;
293
294 if (read_format & perf_event_read_format_PERF_FORMAT_TOTAL_TIME_ENABLED as u64) != 0 {
295 output.extend(total_time_running_including_curr.to_ne_bytes());
297 }
298 if (read_format & perf_event_read_format_PERF_FORMAT_TOTAL_TIME_RUNNING as u64) != 0 {
299 output.extend(total_time_running_including_curr.to_ne_bytes());
301 }
302 if (read_format & perf_event_read_format_PERF_FORMAT_ID as u64) != 0 {
303 output.extend(perf_event_file.rf_id.to_ne_bytes());
305 }
306
307 output
308 };
309
310 if data.available() < read_format_data.len() {
314 return error!(ENOSPC);
315 }
316 track_stub!(
317 TODO("https://fxbug.dev/402453955"),
318 "[perf_event_open] implement remaining error handling"
319 );
320
321 data.write(&read_format_data)
322 }
323
324 fn ioctl(
325 &self,
326 _locked: &mut Locked<Unlocked>,
327 _file: &FileObject,
328 current_task: &CurrentTask,
329 op: u32,
330 _arg: SyscallArg,
331 ) -> Result<SyscallResult, Errno> {
332 track_stub!(
333 TODO("https://fxbug.dev/405463320"),
334 "[perf_event_open] implement PERF_IOC_FLAG_GROUP"
335 );
336 security::check_perf_event_write_access(current_task, &self)?;
337 let mut perf_event_file = self.perf_event_file.write();
338 match op {
339 PERF_EVENT_IOC_ENABLE => {
340 if perf_event_file.disabled != 0 {
341 perf_event_file.disabled = 0; perf_event_file.most_recent_enabled_time =
343 zx::MonotonicInstant::get().into_nanos() as u64;
344 }
345
346 track_stub!(
349 TODO("https://fxbug.dev/398914921"),
350 "[perf_event_open] implement full sampling features"
351 );
352 if perf_event_file.attr.freq() == 0
353 && unsafe { perf_event_file.attr.__bindgen_anon_1.sample_period != 0 }
356 {
357 ping_receiver(perf_event_file.ioctl_sender.clone(), IoctlOp::Enable);
358 }
359 return Ok(SUCCESS);
360 }
361 PERF_EVENT_IOC_DISABLE => {
362 if perf_event_file.disabled == 0 {
363 perf_event_file.disabled = 1; let curr_time = zx::MonotonicInstant::get().into_nanos() as u64;
367 perf_event_file.total_time_running +=
368 curr_time - perf_event_file.most_recent_enabled_time;
369 }
370 if perf_event_file.attr.freq() == 0
371 && unsafe { perf_event_file.attr.__bindgen_anon_1.sample_period != 0 }
374 {
375 ping_receiver(perf_event_file.ioctl_sender.clone(), IoctlOp::Disable);
376 }
377 return Ok(SUCCESS);
378 }
379 PERF_EVENT_IOC_RESET => {
380 perf_event_file.rf_value = 0;
381 return Ok(SUCCESS);
382 }
383 PERF_EVENT_IOC_REFRESH
384 | PERF_EVENT_IOC_PERIOD
385 | PERF_EVENT_IOC_SET_OUTPUT
386 | PERF_EVENT_IOC_SET_FILTER
387 | PERF_EVENT_IOC_ID
388 | PERF_EVENT_IOC_SET_BPF
389 | PERF_EVENT_IOC_PAUSE_OUTPUT
390 | PERF_EVENT_IOC_MODIFY_ATTRIBUTES
391 | PERF_EVENT_IOC_QUERY_BPF => {
392 track_stub!(
393 TODO("https://fxbug.dev/404941053"),
394 "[perf_event_open] implement remaining ioctl() calls"
395 );
396 return error!(ENOSYS);
397 }
398 _ => error!(ENOTTY),
399 }
400 }
401
402 fn get_memory(
407 &self,
408 _locked: &mut Locked<FileOpsCore>,
409 _file: &FileObject,
410 current_task: &CurrentTask,
411 length: Option<usize>,
412 _prot: ProtectionFlags,
413 ) -> Result<Arc<MemoryObject>, Errno> {
414 let buffer_size: u64 = length.unwrap_or(0) as u64;
415 if buffer_size == 0 {
416 return error!(EINVAL);
417 }
418
419 let mut metadata_value: PerfMetadataValue = self.seq_lock.get();
422 let page_size = zx::system_get_page_size() as u64;
423 metadata_value.data_head = page_size;
424 metadata_value.data_size = buffer_size - page_size;
425 self.seq_lock.set_value(metadata_value);
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.perf_data_vmo.as_handle_ref().duplicate(zx::Rights::SAME_RIGHTS) {
432 Ok(vmo) => {
433 let memory = MemoryObject::Vmo(vmo.into());
434 return Ok(Arc::new(memory));
435 }
436 Err(_) => {
437 track_stub!(
438 TODO("https://fxbug.dev/416323134"),
439 "[perf_event_open] handle get_memory() errors"
440 );
441 return error!(EINVAL);
442 }
443 };
444 }
445
446 fn write(
447 &self,
448 _locked: &mut Locked<FileOpsCore>,
449 _file: &FileObject,
450 _current_task: &CurrentTask,
451 _offset: usize,
452 _data: &mut dyn InputBuffer,
453 ) -> Result<usize, Errno> {
454 track_stub!(
455 TODO("https://fxbug.dev/394960158"),
456 "[perf_event_open] implement perf event functions"
457 );
458 error!(ENOSYS)
459 }
460}
461
462fn write_record_to_vmo(
484 perf_record_sample: PerfRecordSample,
485 perf_data_vmo: &zx::Vmo,
486 _data_head_pointer: &AtomicPtr<u64>,
487 sample_type: u64,
488 sample_id: u64,
489 sample_period: u64,
490 offset: u64,
491) -> u64 {
492 track_stub!(
494 TODO("https://fxbug.dev/432501467"),
495 "[perf_event_open] determines whether the record is KERNEL or USER"
496 );
497 let perf_event_header = perf_event_header {
498 type_: perf_event_type_PERF_RECORD_SAMPLE,
499 misc: PERF_RECORD_MISC_KERNEL as u16,
500 size: PERF_EVENT_HEADER_SIZE,
501 };
502
503 match perf_data_vmo.write(&perf_event_header.as_bytes(), offset) {
504 Ok(_) => (),
505 Err(e) => log_warn!("Failed to write perf_event_header: {}", e),
506 }
507
508 let mut sample = Vec::<u8>::new();
510 if (sample_type & perf_event_sample_format_PERF_SAMPLE_IDENTIFIER as u64) != 0 {
512 sample.extend(sample_id.to_ne_bytes());
513 }
514 if (sample_type & perf_event_sample_format_PERF_SAMPLE_IP as u64) != 0 {
516 sample.extend(perf_record_sample.ips[0].to_ne_bytes());
517 }
518
519 if (sample_type & perf_event_sample_format_PERF_SAMPLE_TID as u64) != 0 {
520 sample.extend(perf_record_sample.pid.expect("missing pid").to_ne_bytes());
522 sample.extend(perf_record_sample.tid.expect("missing tid").to_ne_bytes());
524 }
525
526 if (sample_type & perf_event_sample_format_PERF_SAMPLE_ID as u64) != 0 {
528 sample.extend(sample_id.to_ne_bytes());
529 }
530
531 if (sample_type & perf_event_sample_format_PERF_SAMPLE_PERIOD as u64) != 0 {
533 sample.extend(sample_period.to_ne_bytes());
534 }
535
536 if (sample_type & perf_event_sample_format_PERF_SAMPLE_CALLCHAIN as u64) != 0 {
537 sample.extend(perf_record_sample.ips.len().to_ne_bytes());
539
540 for i in perf_record_sample.ips {
542 sample.extend(i.to_ne_bytes());
543 }
544 }
545 match perf_data_vmo.write(&sample, offset + (std::mem::size_of::<perf_event_header>() as u64)) {
548 Ok(_) => {
549 let bytes_written: u64 =
550 (std::mem::size_of::<perf_event_header>() + sample.len()) as u64;
551
552 return bytes_written;
562 }
563 Err(e) => {
564 log_warn!("Failed to write PerfRecordSample to VMO due to: {}", e);
565 return 0;
567 }
568 }
569}
570
571#[derive(Debug, Clone)]
572struct PerfRecordSample {
573 pid: Option<u32>,
574 tid: Option<u32>,
575 ips: Vec<u64>,
577}
578
579fn parse_perf_record_sample_format(backtrace: &str) -> Option<PerfRecordSample> {
591 let mut pid: Option<u32> = None;
592 let mut tid: Option<u32> = None;
593 let mut ips: Vec<u64> = Vec::new();
594 let mut numbers_found = 0;
595 track_stub!(TODO("https://fxbug.dev/437171287"), "[perf_event_open] handle regex nuances");
596 let backtrace_regex =
597 Regex::new(r"^\s*\{\{\{bt:\d+:((0x[0-9a-fA-F]+)):(?:pc|ra)\}\}\}\s*$").unwrap();
598
599 for line in backtrace.lines() {
600 let trimmed_line = line.trim();
601 if numbers_found < 2 {
603 if let Ok(num) = trimmed_line.parse::<u32>() {
604 if numbers_found == 0 {
605 pid = Some(num);
606 } else {
607 tid = Some(num);
608 }
609 numbers_found += 1;
610 continue;
611 }
612 }
613
614 if let Some(parsed_bt) = backtrace_regex.captures(trimmed_line) {
616 let address_str = parsed_bt.get(1).unwrap().as_str();
617 if let Ok(ip_addr) = u64::from_str_radix(address_str.trim_start_matches("0x"), 16) {
618 ips.push(ip_addr);
619 }
620 }
621 }
622
623 if pid == None || tid == None || ips.is_empty() {
624 log_info!("No ips while getting PerfRecordSample");
626 None
627 } else {
628 Some(PerfRecordSample { pid: pid, tid: tid, ips: ips })
629 }
630}
631
632async fn set_up_profiler(
633 sample_period: zx::MonotonicDuration,
634) -> Result<(profiler::SessionProxy, fidl::AsyncSocket), Errno> {
635 let sample = profiler::Sample {
637 callgraph: Some(profiler::CallgraphConfig {
638 strategy: Some(profiler::CallgraphStrategy::FramePointer),
639 ..Default::default()
640 }),
641 ..Default::default()
642 };
643
644 let sampling_config = profiler::SamplingConfig {
645 period: Some(sample_period.into_nanos() as u64),
646 timebase: Some(profiler::Counter::PlatformIndependent(profiler::CounterId::Nanoseconds)),
647 sample: Some(sample),
648 ..Default::default()
649 };
650
651 let tasks = vec![
652 profiler::Task::SystemWide(profiler::SystemWide {}),
654 ];
655 let targets = profiler::TargetConfig::Tasks(tasks);
656 let config = profiler::Config {
657 configs: Some(vec![sampling_config]),
658 target: Some(targets),
659 ..Default::default()
660 };
661 let (client, server) = fidl::Socket::create_stream();
662 let configure = profiler::SessionConfigureRequest {
663 output: Some(server),
664 config: Some(config),
665 ..Default::default()
666 };
667
668 let proxy = connect_to_protocol::<profiler::SessionMarker>()
669 .context("Error connecting to Profiler protocol");
670 let session_proxy: profiler::SessionProxy = match proxy {
671 Ok(p) => p.clone(),
672 Err(e) => return error!(EINVAL, e),
673 };
674
675 let config_request = session_proxy.configure(configure).await;
677 match config_request {
678 Ok(_) => Ok((session_proxy, fidl::AsyncSocket::from_socket(client))),
679 Err(e) => return error!(EINVAL, e),
680 }
681}
682
683async fn stop_and_collect_samples(
688 session_proxy: profiler::SessionProxy,
689 mut client: fidl::AsyncSocket,
690 perf_data_vmo: &zx::Vmo,
691 data_head_pointer: &AtomicPtr<u64>,
692 sample_type: u64,
693 sample_id: u64,
694 sample_period: u64,
695 vmo_write_offset: u64,
696) -> Result<(), Errno> {
697 let stats = session_proxy.stop().await;
698 let samples_collected = match stats {
699 Ok(stats) => stats.samples_collected.unwrap(),
700 Err(e) => return error!(EINVAL, e),
701 };
702
703 track_stub!(
704 TODO("https://fxbug.dev/422502681"),
705 "[perf_event_open] symbolize sample output and delete the below log_info"
706 );
707 log_info!("profiler samples_collected: {:?}", samples_collected);
708
709 let mut header = [0; 8];
711 let mut bytes_read = 0;
712 while bytes_read < 8 {
713 match client.read(&mut header[bytes_read..]).await {
714 Ok(0) => {
715 log_info!("[perf_event_open] Finished reading fxt record from socket.");
717 break;
718 }
719 Ok(n) => bytes_read += n,
720 Err(e) => {
721 log_warn!("[perf_event_open] Error reading from socket: {:?}", e);
722 break;
723 }
724 }
725 }
726
727 if bytes_read > 0 {
728 if bytes_read == 8 && header == FXT_MAGIC_BYTES {
729 let header_cursor = Cursor::new(header);
731 let reader = header_cursor.chain(client);
732 let (mut stream, _task) = SessionParser::new_async(reader);
733 while let Some(record_result) = stream.next().await {
734 match record_result {
735 Ok(TraceRecord::Profiler(ProfilerRecord::Backtrace(backtrace))) => {
736 let ips: Vec<u64> = backtrace.data;
737 let pid = Some(backtrace.process.0 as u32);
738 let tid = Some(backtrace.thread.0 as u32);
739 let perf_record_sample = PerfRecordSample { pid, tid, ips };
740 write_record_to_vmo(
741 perf_record_sample,
742 perf_data_vmo,
743 data_head_pointer,
744 sample_type,
745 sample_id,
746 sample_period,
747 vmo_write_offset,
748 );
749 }
750 Ok(_) => {
751 }
753 Err(e) => {
754 log_warn!("[perf_event_open] Error parsing FXT: {:?}", e);
755 break;
756 }
757 }
758 }
759 } else {
760 let mut buffer = vec![0; DEFAULT_CHUNK_SIZE];
764
765 loop {
766 let socket_data = client.read(&mut buffer).await;
769
770 match socket_data {
771 Ok(0) => {
772 log_info!("[perf_event_open] Finished reading from socket.");
774 break;
775 }
776 Ok(bytes_read) => {
777 let received_data = match std::str::from_utf8(&buffer[..bytes_read]) {
779 Ok(data) => data,
780 Err(e) => return error!(EINVAL, e),
781 };
782 if let Some(perf_record_sample) =
784 parse_perf_record_sample_format(received_data)
785 {
786 write_record_to_vmo(
787 perf_record_sample,
788 perf_data_vmo,
789 data_head_pointer,
790 sample_type,
791 sample_id,
792 sample_period,
793 vmo_write_offset,
794 );
795 }
796 }
797 Err(e) => {
798 log_warn!("[perf_event_open] Error reading from socket: {:?}", e);
799 break;
800 }
801 }
802 }
803 }
804 }
805
806 let reset_status = session_proxy.reset().await;
807 return match reset_status {
808 Ok(_) => Ok(()),
809 Err(e) => error!(EINVAL, e),
810 };
811}
812
813fn ping_receiver(
818 mut ioctl_sender: future_mpsc::Sender<(IoctlOp, sync_mpsc::Sender<()>)>,
819 command: IoctlOp,
820) {
821 log_info!("[perf_event_open] Received sampling command: {:?}", command);
822 let (profiling_complete_sender, profiling_complete_receiver) = sync_mpsc::channel::<()>();
823 match ioctl_sender.try_send((command, profiling_complete_sender)) {
824 Ok(_) => (),
825 Err(e) => {
826 if e.is_full() {
827 log_warn!("[perf_event_open] Failed to send {:?}: Channel full", command);
828 } else if e.is_disconnected() {
829 log_warn!("[perf_event_open] Failed to send {:?}: Receiver disconnected", command);
830 } else {
831 log_warn!("[perf_event_open] Failed to send {:?} due to {:?}", command, e.source());
832 }
833 }
834 };
835 let _ = profiling_complete_receiver.recv().unwrap();
838}
839
840unsafe fn create_seq_lock(
849 vmo_handle_ref: &zx::NullableHandle,
850) -> SeqLock<PerfMetadataHeader, PerfMetadataValue> {
851 let metadata_header = PerfMetadataHeader { version: 1, compat_version: 2 };
853 let metadata_value = PerfMetadataValue {
854 lock: 0,
855 index: 3,
856 offset: 19337,
857 time_enabled: 0,
858 time_running: 0,
859 __bindgen_anon_1: perf_event_mmap_page__bindgen_ty_1 { capabilities: 30 },
860 pmc_width: 0,
861 time_shift: 0,
862 time_mult: 0,
863 time_offset: 0,
864 time_zero: 0,
865 size: 0,
866 __reserved_1: 0,
867 time_cycles: 0,
868 time_mask: 0,
869 __reserved: [0; 928usize],
870 data_head: 0,
871 data_tail: 0,
873 data_offset: zx::system_get_page_size() as u64,
875 data_size: 0,
877 aux_head: 0,
878 aux_tail: 0,
879 aux_offset: 0,
880 aux_size: 0,
881 };
882 let vmo = zx::Vmo::from(vmo_handle_ref.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap());
883
884 unsafe {
892 SeqLock::new_from_vmo(metadata_header, metadata_value, vmo)
893 .expect("failed to create seq_lock for perf metadata")
894 }
895}
896
897pub fn sys_perf_event_open(
898 locked: &mut Locked<Unlocked>,
899 current_task: &CurrentTask,
900 attr: UserRef<perf_event_attr>,
901 tid: tid_t,
903 cpu: i32,
904 group_fd: FdNumber,
905 _flags: u64,
906) -> Result<SyscallResult, Errno> {
907 let perf_event_attrs: perf_event_attr = current_task.read_object(attr)?;
911
912 if tid == -1 && cpu == -1 {
913 return error!(EINVAL);
914 }
915
916 let target_task_type = match tid {
917 -1 => TargetTaskType::AllTasks,
918 0 => TargetTaskType::CurrentTask,
919 _ => {
920 track_stub!(TODO("https://fxbug.dev/409621963"), "[perf_event_open] implement tid > 0");
921 return error!(ENOSYS);
922 }
923 };
924 security::check_perf_event_open_access(
925 current_task,
926 target_task_type,
927 &perf_event_attrs,
928 perf_event_attrs.type_.try_into()?,
929 )?;
930
931 let (sender, mut receiver) = future_mpsc::channel(8);
935
936 let page_size = zx::system_get_page_size() as u64;
937 let mut perf_event_file = PerfEventFileState::new(
938 perf_event_attrs,
939 0,
940 perf_event_attrs.disabled(),
941 perf_event_attrs.sample_type,
942 zx::Vmo::create(ESTIMATED_MMAP_BUFFER_SIZE).unwrap(),
943 page_size, sender,
945 );
946
947 let read_format = perf_event_attrs.read_format;
948
949 if (read_format & perf_event_read_format_PERF_FORMAT_TOTAL_TIME_ENABLED as u64) != 0
950 || (read_format & perf_event_read_format_PERF_FORMAT_TOTAL_TIME_RUNNING as u64) != 0
951 {
952 if perf_event_file.disabled == 0 {
955 perf_event_file.most_recent_enabled_time =
956 zx::MonotonicInstant::get().into_nanos() as u64;
957 }
958 perf_event_file.total_time_running = 0;
960 }
961
962 let event_id = READ_FORMAT_ID_GENERATOR.fetch_add(1, Ordering::Relaxed);
963 perf_event_file.rf_id = event_id;
964
965 if group_fd.raw() == -1 {
966 perf_event_file.sample_id = event_id;
967 } else {
968 let group_file = current_task.get_file(group_fd)?;
969 let group_file_object_id = group_file.id;
970 let perf_state = get_perf_state(¤t_task.kernel);
971 let events = perf_state.format_id_lookup_table.lock();
972 if let Some(rf_id) = events.get(&group_file_object_id) {
973 perf_event_file.sample_id = *rf_id;
974 } else {
975 return error!(EINVAL);
976 }
977 }
978
979 if (read_format & perf_event_read_format_PERF_FORMAT_GROUP as u64) != 0 {
980 track_stub!(
981 TODO("https://fxbug.dev/402238049"),
982 "[perf_event_open] implement read_format group"
983 );
984 return error!(ENOSYS);
985 }
986 if (read_format & perf_event_read_format_PERF_FORMAT_LOST as u64) != 0 {
987 track_stub!(
988 TODO("https://fxbug.dev/402260383"),
989 "[perf_event_open] implement read_format lost"
990 );
991 }
992
993 let mut vmo_handle_copy =
995 perf_event_file.perf_data_vmo.as_handle_ref().duplicate(zx::Rights::SAME_RIGHTS);
996
997 let sample_period_in_ticks = unsafe { perf_event_file.attr.__bindgen_anon_1.sample_period };
1000 let zx_sample_period = zx::MonotonicDuration::from_nanos(sample_period_in_ticks as i64);
1003
1004 let data_head_pointer = Arc::new(AtomicPtr::new(std::ptr::null_mut::<u64>()));
1005 let cloned_data_head_pointer = Arc::clone(&data_head_pointer);
1007
1008 let seq_lock = unsafe { create_seq_lock(vmo_handle_copy.as_ref().unwrap()) };
1011
1012 let closure = async move |_: LockedAndTask<'_>| {
1013 let mut profiler_state: Option<(profiler::SessionProxy, fidl::AsyncSocket)> = None;
1014
1015 while let Some((command, profiling_complete_receiver)) = receiver.next().await {
1017 match command {
1018 IoctlOp::Enable => {
1019 match set_up_profiler(zx_sample_period).await {
1020 Ok((session_proxy, client)) => {
1021 let start_request = profiler::SessionStartRequest {
1022 buffer_results: Some(true),
1023 buffer_size_mb: Some(8 as u64),
1024 ..Default::default()
1025 };
1026 if let Err(e) = session_proxy.start(&start_request).await {
1027 log_warn!("Failed to start profiling: {}", e);
1028 } else {
1029 profiler_state = Some((session_proxy, client));
1030 }
1031 }
1032 Err(e) => {
1033 log_warn!("Failed to profile: {}", e);
1034 }
1035 };
1036 let _ = profiling_complete_receiver.send(());
1038 }
1039 IoctlOp::Disable => {
1040 if let Some((session_proxy, client)) = profiler_state.take() {
1041 let handle = vmo_handle_copy
1042 .as_mut()
1043 .expect("Failed to get VMO handle")
1044 .as_handle_ref()
1045 .duplicate(zx::Rights::SAME_RIGHTS)
1046 .unwrap();
1047
1048 if let Err(e) = stop_and_collect_samples(
1049 session_proxy,
1050 client,
1051 &zx::Vmo::from(handle),
1052 &*cloned_data_head_pointer,
1053 perf_event_file.sample_type,
1054 perf_event_file.sample_id,
1055 sample_period_in_ticks,
1056 perf_event_file.vmo_write_offset,
1057 )
1058 .await
1059 {
1060 log_warn!("Failed to collect sample: {:?}", e);
1061 }
1062 }
1063 let _ = profiling_complete_receiver.send(());
1065 }
1066 }
1067 }
1068 ()
1069 };
1070 let req = SpawnRequestBuilder::new()
1071 .with_debug_name("perf-event-sampler")
1072 .with_async_closure(closure)
1073 .build();
1074 current_task.kernel().kthreads.spawner().spawn_from_request(req);
1075
1076 let file = Box::new(PerfEventFile {
1077 _tid: tid,
1078 _cpu: cpu,
1079 perf_event_file: RwLock::new(perf_event_file),
1080 security_state: security::perf_event_alloc(current_task),
1081 _data_head_pointer: data_head_pointer,
1082 seq_lock: seq_lock,
1083 });
1084 let file_handle =
1086 Anon::new_private_file(locked, current_task, file, OpenFlags::RDWR, "[perf_event]");
1087 let file_object_id = file_handle.id;
1088 let file_descriptor: Result<FdNumber, Errno> =
1089 current_task.add_file(locked, file_handle, FdFlags::empty());
1090
1091 match file_descriptor {
1092 Ok(fd) => {
1093 if group_fd.raw() == -1 {
1094 let perf_state = get_perf_state(¤t_task.kernel);
1095 let mut events = perf_state.format_id_lookup_table.lock();
1096 events.insert(file_object_id, event_id);
1097 }
1098 Ok(fd.into())
1099 }
1100 Err(_) => {
1101 track_stub!(
1102 TODO("https://fxbug.dev/402453955"),
1103 "[perf_event_open] implement remaining error handling"
1104 );
1105 error!(EMFILE)
1106 }
1107 }
1108}
1109#[cfg(target_arch = "aarch64")]
1111mod arch32 {
1112 pub use super::sys_perf_event_open as sys_arch32_perf_event_open;
1113}
1114
1115#[cfg(target_arch = "aarch64")]
1116pub use arch32::*;
1117
1118use crate::mm::memory::MemoryObject;
1119use crate::mm::{MemoryAccessorExt, ProtectionFlags};
1120use crate::task::CurrentTask;
1121use crate::vfs::{
1122 Anon, FdFlags, FdNumber, FileObject, FileObjectId, FileObjectState, FileOps, InputBuffer,
1123 OutputBuffer,
1124};
1125use crate::{fileops_impl_nonseekable, fileops_impl_noop_sync};