1use crate::task::dynamic_thread_spawner::SpawnRequestBuilder;
6use anyhow::Context;
7use fuchsia_component::client::connect_to_protocol;
8use futures::StreamExt;
9use futures::channel::mpsc as future_mpsc;
10use regex::Regex;
11use std::collections::HashMap;
12use std::error::Error;
13use std::sync::atomic::{AtomicPtr, AtomicU64, Ordering};
14use std::sync::{Arc, mpsc as sync_mpsc};
15use std::time::Duration;
16use zerocopy::{Immutable, IntoBytes};
17use zx::AsHandleRef;
18use {fidl_fuchsia_cpu_profiler as profiler, fuchsia_async};
19
20use futures::io::{AsyncReadExt, Cursor};
21use fxt::TraceRecord;
22use fxt::profiler::ProfilerRecord;
23use fxt::session::SessionParser;
24use seq_lock::SeqLock;
25use starnix_logging::{log_info, log_warn, track_stub};
26use starnix_sync::{FileOpsCore, Locked, Mutex, RwLock, Unlocked};
27use starnix_syscalls::{SUCCESS, SyscallArg, SyscallResult};
28use starnix_uapi::arch32::{
29 PERF_EVENT_IOC_DISABLE, PERF_EVENT_IOC_ENABLE, PERF_EVENT_IOC_ID,
30 PERF_EVENT_IOC_MODIFY_ATTRIBUTES, PERF_EVENT_IOC_PAUSE_OUTPUT, PERF_EVENT_IOC_PERIOD,
31 PERF_EVENT_IOC_QUERY_BPF, PERF_EVENT_IOC_REFRESH, PERF_EVENT_IOC_RESET, PERF_EVENT_IOC_SET_BPF,
32 PERF_EVENT_IOC_SET_FILTER, PERF_EVENT_IOC_SET_OUTPUT, PERF_RECORD_MISC_KERNEL,
33 perf_event_sample_format_PERF_SAMPLE_CALLCHAIN, perf_event_sample_format_PERF_SAMPLE_ID,
34 perf_event_sample_format_PERF_SAMPLE_IDENTIFIER, perf_event_sample_format_PERF_SAMPLE_IP,
35 perf_event_sample_format_PERF_SAMPLE_PERIOD, perf_event_sample_format_PERF_SAMPLE_TID,
36 perf_event_type_PERF_RECORD_SAMPLE,
37};
38use starnix_uapi::errors::Errno;
39use starnix_uapi::open_flags::OpenFlags;
40use starnix_uapi::user_address::UserRef;
41use starnix_uapi::{
42 error, perf_event_attr, perf_event_header, perf_event_mmap_page__bindgen_ty_1,
43 perf_event_read_format_PERF_FORMAT_GROUP, perf_event_read_format_PERF_FORMAT_ID,
44 perf_event_read_format_PERF_FORMAT_LOST, 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; const PERF_EVENT_HEADER_SIZE: u16 = 8;
57const FXT_MAGIC_BYTES: [u8; 8] = [0x10, 0x00, 0x04, 0x46, 0x78, 0x54, 0x16, 0x00];
59
60#[repr(C)]
61#[derive(Copy, Clone, IntoBytes, Immutable)]
62struct PerfMetadataHeader {
63 version: u32,
64 compat_version: u32,
65}
66
67#[repr(C, packed)]
68#[derive(Copy, Clone, IntoBytes, Immutable)]
69struct PerfMetadataValue {
70 index: u32,
71 offset: i64,
72 time_enabled: u64,
73 time_running: u64,
74 __bindgen_anon_1: perf_event_mmap_page__bindgen_ty_1,
75 pmc_width: u16,
76 time_shift: u16,
77 time_mult: u32,
78 time_offset: u64,
79 time_zero: u64,
80 size: u32,
81 __reserved_1: u32,
82 time_cycles: u64,
83 time_mask: u64,
84 __reserved: [u8; 928usize],
85 data_head: u64,
86 data_tail: u64,
87 data_offset: u64,
88 data_size: u64,
89 aux_head: u64,
90 aux_tail: u64,
91 aux_offset: u64,
92 aux_size: u64,
93}
94
95struct PerfState {
96 format_id_lookup_table: Mutex<HashMap<FileObjectId, u64>>,
102}
103
104impl Default for PerfState {
105 fn default() -> Self {
106 Self { format_id_lookup_table: Mutex::new(HashMap::new()) }
107 }
108}
109
110fn get_perf_state(kernel: &Arc<Kernel>) -> Arc<PerfState> {
111 kernel.expando.get_or_init(PerfState::default)
112}
113
114uapi::check_arch_independent_layout! {
115 perf_event_attr {
116 type_, size,
118 config,
119 __bindgen_anon_1,
120 sample_type,
121 read_format,
122 _bitfield_1,
123 __bindgen_anon_2,
124 bp_type,
125 __bindgen_anon_3,
126 __bindgen_anon_4,
127 branch_sample_type,
128 sample_regs_user,
129 sample_stack_user,
130 clockid,
131 sample_regs_intr,
132 aux_watermark,
133 sample_max_stack,
134 __reserved_2,
135 aux_sample_size,
136 __reserved_3,
137 sig_data,
138 config3,
139 }
140}
141
142#[derive(Clone, Copy, Debug, PartialEq)]
143enum IoctlOp {
144 Enable,
145}
146
147struct PerfEventFileState {
148 attr: perf_event_attr,
149 rf_value: u64, most_recent_enabled_time: u64,
153 total_time_running: u64,
157 rf_id: u64,
158 sample_id: u64,
159 _rf_lost: u64,
160 disabled: u64,
161 sample_type: u64,
162 perf_data_vmo: zx::Vmo,
165 vmo_write_offset: u64,
168 ioctl_sender: future_mpsc::Sender<(IoctlOp, sync_mpsc::Sender<()>)>,
170}
171
172impl PerfEventFileState {
175 fn new(
176 attr: perf_event_attr,
177 rf_value: u64,
178 disabled: u64,
179 sample_type: u64,
180 perf_data_vmo: zx::Vmo,
181 vmo_write_offset: u64,
182 ioctl_sender: future_mpsc::Sender<(IoctlOp, sync_mpsc::Sender<()>)>,
183 ) -> PerfEventFileState {
184 PerfEventFileState {
185 attr,
186 rf_value,
187 most_recent_enabled_time: 0,
188 total_time_running: 0,
189 rf_id: 0,
190 sample_id: 0,
191 _rf_lost: 0,
192 disabled,
193 sample_type,
194 perf_data_vmo,
195 vmo_write_offset,
196 ioctl_sender,
197 }
198 }
199}
200
201pub struct PerfEventFile {
202 _tid: tid_t,
203 _cpu: i32,
204 perf_event_file: RwLock<PerfEventFileState>,
205 pub security_state: security::PerfEventState,
207 data_head_pointer: Arc<AtomicPtr<u64>>,
211}
212
213impl FileOps for PerfEventFile {
218 fileops_impl_nonseekable!();
220 fileops_impl_noop_sync!();
221
222 fn close(
223 self: Box<Self>,
224 _locked: &mut Locked<FileOpsCore>,
225 file: &FileObjectState,
226 current_task: &CurrentTask,
227 ) {
228 let perf_state = get_perf_state(¤t_task.kernel);
229 let mut events = perf_state.format_id_lookup_table.lock();
230 events.remove(&file.id);
231 }
232
233 fn read(
235 &self,
236 _locked: &mut Locked<FileOpsCore>,
237 _file: &FileObject,
238 current_task: &CurrentTask,
239 _offset: usize,
240 data: &mut dyn OutputBuffer,
241 ) -> Result<usize, Errno> {
242 let read_format_data = {
245 let mut perf_event_file = self.perf_event_file.write();
248
249 security::check_perf_event_read_access(current_task, &self)?;
250
251 let mut total_time_running_including_curr = perf_event_file.total_time_running;
252
253 if perf_event_file.disabled == 0 {
255 track_stub!(
260 TODO("https://fxbug.dev/402938671"),
261 "[perf_event_open] implement read_format value"
262 );
263 perf_event_file.rf_value += 1;
264
265 let curr_time = zx::MonotonicInstant::get().into_nanos() as u64;
267 total_time_running_including_curr +=
268 curr_time - perf_event_file.most_recent_enabled_time;
269 }
270
271 let mut output = Vec::<u8>::new();
272 let value = perf_event_file.rf_value.to_ne_bytes();
273 output.extend(value);
274
275 let read_format = perf_event_file.attr.read_format;
276
277 if (read_format & perf_event_read_format_PERF_FORMAT_TOTAL_TIME_ENABLED as u64) != 0 {
278 output.extend(total_time_running_including_curr.to_ne_bytes());
280 }
281 if (read_format & perf_event_read_format_PERF_FORMAT_TOTAL_TIME_RUNNING as u64) != 0 {
282 output.extend(total_time_running_including_curr.to_ne_bytes());
284 }
285 if (read_format & perf_event_read_format_PERF_FORMAT_ID as u64) != 0 {
286 output.extend(perf_event_file.rf_id.to_ne_bytes());
288 }
289
290 output
291 };
292
293 if data.available() < read_format_data.len() {
297 return error!(ENOSPC);
298 }
299 track_stub!(
300 TODO("https://fxbug.dev/402453955"),
301 "[perf_event_open] implement remaining error handling"
302 );
303
304 data.write(&read_format_data)
305 }
306
307 fn ioctl(
308 &self,
309 _locked: &mut Locked<Unlocked>,
310 _file: &FileObject,
311 current_task: &CurrentTask,
312 op: u32,
313 _arg: SyscallArg,
314 ) -> Result<SyscallResult, Errno> {
315 track_stub!(
316 TODO("https://fxbug.dev/405463320"),
317 "[perf_event_open] implement PERF_IOC_FLAG_GROUP"
318 );
319 security::check_perf_event_write_access(current_task, &self)?;
320 let mut perf_event_file = self.perf_event_file.write();
321 match op {
322 PERF_EVENT_IOC_ENABLE => {
323 if perf_event_file.disabled != 0 {
324 perf_event_file.disabled = 0; perf_event_file.most_recent_enabled_time =
326 zx::MonotonicInstant::get().into_nanos() as u64;
327 }
328
329 track_stub!(
332 TODO("https://fxbug.dev/398914921"),
333 "[perf_event_open] implement full sampling features"
334 );
335 if perf_event_file.attr.freq() == 0
336 && unsafe { perf_event_file.attr.__bindgen_anon_1.sample_period != 0 }
339 {
340 ping_receiver(perf_event_file.ioctl_sender.clone(), IoctlOp::Enable);
341 }
342 return Ok(SUCCESS);
343 }
344 PERF_EVENT_IOC_DISABLE => {
345 if perf_event_file.disabled == 0 {
346 perf_event_file.disabled = 1; let curr_time = zx::MonotonicInstant::get().into_nanos() as u64;
350 perf_event_file.total_time_running +=
351 curr_time - perf_event_file.most_recent_enabled_time;
352 }
353 track_stub!(
354 TODO("https://fxbug.dev/422502681"),
355 "[perf_event_open] implement Disable to not hardcode profiling"
356 );
357 return Ok(SUCCESS);
358 }
359 PERF_EVENT_IOC_RESET => {
360 perf_event_file.rf_value = 0;
361 return Ok(SUCCESS);
362 }
363 PERF_EVENT_IOC_REFRESH
364 | PERF_EVENT_IOC_PERIOD
365 | PERF_EVENT_IOC_SET_OUTPUT
366 | PERF_EVENT_IOC_SET_FILTER
367 | PERF_EVENT_IOC_ID
368 | PERF_EVENT_IOC_SET_BPF
369 | PERF_EVENT_IOC_PAUSE_OUTPUT
370 | PERF_EVENT_IOC_MODIFY_ATTRIBUTES
371 | PERF_EVENT_IOC_QUERY_BPF => {
372 track_stub!(
373 TODO("https://fxbug.dev/404941053"),
374 "[perf_event_open] implement remaining ioctl() calls"
375 );
376 return error!(ENOSYS);
377 }
378 _ => error!(ENOTTY),
379 }
380 }
381
382 fn get_memory(
387 &self,
388 _locked: &mut Locked<FileOpsCore>,
389 _file: &FileObject,
390 current_task: &CurrentTask,
391 length: Option<usize>,
392 _prot: ProtectionFlags,
393 ) -> Result<Arc<MemoryObject>, Errno> {
394 let buffer_size: u64 = length.unwrap_or(0) as u64;
395 if buffer_size == 0 {
396 return error!(EINVAL);
397 }
398 let page_size = zx::system_get_page_size() as u64;
399
400 security::check_perf_event_read_access(current_task, &self)?;
401
402 let metadata_header = PerfMetadataHeader { version: 1, compat_version: 2 };
406 let metadata_value = PerfMetadataValue {
407 index: 2,
408 offset: 19337,
409 time_enabled: 0,
410 time_running: 0,
411 __bindgen_anon_1: perf_event_mmap_page__bindgen_ty_1 { capabilities: 30 },
412 pmc_width: 0,
413 time_shift: 0,
414 time_mult: 0,
415 time_offset: 0,
416 time_zero: 0,
417 size: 0,
418 __reserved_1: 0,
419 time_cycles: 0,
420 time_mask: 0,
421 __reserved: [0; 928usize],
422 data_head: page_size,
423 data_tail: 0,
425 data_offset: page_size,
426 data_size: (buffer_size - page_size) as u64,
427 aux_head: 0,
428 aux_tail: 0,
429 aux_offset: 0,
430 aux_size: 0,
431 };
432
433 let perf_event_file = self.perf_event_file.read();
443 let vmo_handle_copy = match perf_event_file
446 .perf_data_vmo
447 .as_handle_ref()
448 .duplicate(zx::Rights::SAME_RIGHTS)
449 {
450 Ok(h) => h,
451 Err(_) => return error!(EINVAL),
452 };
453
454 let mut seq_lock = match unsafe {
458 SeqLock::new_from_vmo(metadata_header, metadata_value, vmo_handle_copy.into())
459 } {
460 Ok(s) => s,
461 Err(_) => return error!(EINVAL),
462 };
463
464 let metadata_struct = seq_lock.get_map_address() as *mut PerfMetadataValue;
467 let data_head_pointer = unsafe { std::ptr::addr_of_mut!((*metadata_struct).data_head) };
469 self.data_head_pointer.store(data_head_pointer, Ordering::Release);
470
471 match perf_event_file.perf_data_vmo.as_handle_ref().duplicate(zx::Rights::SAME_RIGHTS) {
472 Ok(vmo) => {
473 let memory = MemoryObject::Vmo(vmo.into());
474 return Ok(Arc::new(memory));
475 }
476 Err(_) => {
477 track_stub!(
478 TODO("https://fxbug.dev/416323134"),
479 "[perf_event_open] handle get_memory() errors"
480 );
481 return error!(EINVAL);
482 }
483 };
484 }
485
486 fn write(
487 &self,
488 _locked: &mut Locked<FileOpsCore>,
489 _file: &FileObject,
490 _current_task: &CurrentTask,
491 _offset: usize,
492 _data: &mut dyn InputBuffer,
493 ) -> Result<usize, Errno> {
494 track_stub!(
495 TODO("https://fxbug.dev/394960158"),
496 "[perf_event_open] implement perf event functions"
497 );
498 error!(ENOSYS)
499 }
500}
501
502fn write_record_to_vmo(
524 perf_record_sample: PerfRecordSample,
525 perf_data_vmo: &zx::Vmo,
526 _data_head_pointer: &AtomicPtr<u64>,
527 sample_type: u64,
528 sample_id: u64,
529 sample_period: u64,
530 offset: u64,
531) -> u64 {
532 track_stub!(
534 TODO("https://fxbug.dev/432501467"),
535 "[perf_event_open] determines whether the record is KERNEL or USER"
536 );
537 let perf_event_header = perf_event_header {
538 type_: perf_event_type_PERF_RECORD_SAMPLE,
539 misc: PERF_RECORD_MISC_KERNEL as u16,
540 size: PERF_EVENT_HEADER_SIZE,
541 };
542
543 match perf_data_vmo.write(&perf_event_header.as_bytes(), offset) {
544 Ok(_) => (),
545 Err(e) => log_warn!("Failed to write perf_event_header: {}", e),
546 }
547
548 let mut sample = Vec::<u8>::new();
550 if (sample_type & perf_event_sample_format_PERF_SAMPLE_IDENTIFIER as u64) != 0 {
552 sample.extend(sample_id.to_ne_bytes());
553 }
554 if (sample_type & perf_event_sample_format_PERF_SAMPLE_IP as u64) != 0 {
556 sample.extend(perf_record_sample.ips[0].to_ne_bytes());
557 }
558
559 if (sample_type & perf_event_sample_format_PERF_SAMPLE_TID as u64) != 0 {
560 sample.extend(perf_record_sample.pid.expect("missing pid").to_ne_bytes());
562 sample.extend(perf_record_sample.tid.expect("missing tid").to_ne_bytes());
564 }
565
566 if (sample_type & perf_event_sample_format_PERF_SAMPLE_ID as u64) != 0 {
568 sample.extend(sample_id.to_ne_bytes());
569 }
570
571 if (sample_type & perf_event_sample_format_PERF_SAMPLE_PERIOD as u64) != 0 {
573 sample.extend(sample_period.to_ne_bytes());
574 }
575
576 if (sample_type & perf_event_sample_format_PERF_SAMPLE_CALLCHAIN as u64) != 0 {
577 sample.extend(perf_record_sample.ips.len().to_ne_bytes());
579
580 for i in perf_record_sample.ips {
582 sample.extend(i.to_ne_bytes());
583 }
584 }
585 match perf_data_vmo.write(&sample, offset + (std::mem::size_of::<perf_event_header>() as u64)) {
588 Ok(_) => {
589 let bytes_written: u64 =
590 (std::mem::size_of::<perf_event_header>() + sample.len()) as u64;
591
592 return bytes_written;
602 }
603 Err(e) => {
604 log_warn!("Failed to write PerfRecordSample to VMO due to: {}", e);
605 return 0;
607 }
608 }
609}
610
611#[derive(Debug, Clone)]
612struct PerfRecordSample {
613 pid: Option<u32>,
614 tid: Option<u32>,
615 ips: Vec<u64>,
617}
618
619fn parse_perf_record_sample_format(backtrace: &str) -> Option<PerfRecordSample> {
631 let mut pid: Option<u32> = None;
632 let mut tid: Option<u32> = None;
633 let mut ips: Vec<u64> = Vec::new();
634 let mut numbers_found = 0;
635 track_stub!(TODO("https://fxbug.dev/437171287"), "[perf_event_open] handle regex nuances");
636 let backtrace_regex =
637 Regex::new(r"^\s*\{\{\{bt:\d+:((0x[0-9a-fA-F]+)):(?:pc|ra)\}\}\}\s*$").unwrap();
638
639 for line in backtrace.lines() {
640 let trimmed_line = line.trim();
641 if numbers_found < 2 {
643 if let Ok(num) = trimmed_line.parse::<u32>() {
644 if numbers_found == 0 {
645 pid = Some(num);
646 } else {
647 tid = Some(num);
648 }
649 numbers_found += 1;
650 continue;
651 }
652 }
653
654 if let Some(parsed_bt) = backtrace_regex.captures(trimmed_line) {
656 let address_str = parsed_bt.get(1).unwrap().as_str();
657 if let Ok(ip_addr) = u64::from_str_radix(address_str.trim_start_matches("0x"), 16) {
658 ips.push(ip_addr);
659 }
660 }
661 }
662
663 if pid == None || tid == None || ips.is_empty() {
664 log_info!("No ips while getting PerfRecordSample");
666 None
667 } else {
668 Some(PerfRecordSample { pid: pid, tid: tid, ips: ips })
669 }
670}
671
672async fn set_up_profiler(
673 sample_period: zx::MonotonicDuration,
674) -> Result<(profiler::SessionProxy, fidl::AsyncSocket), Errno> {
675 let sample = profiler::Sample {
677 callgraph: Some(profiler::CallgraphConfig {
678 strategy: Some(profiler::CallgraphStrategy::FramePointer),
679 ..Default::default()
680 }),
681 ..Default::default()
682 };
683
684 let sampling_config = profiler::SamplingConfig {
685 period: Some(sample_period.into_nanos() as u64),
686 timebase: Some(profiler::Counter::PlatformIndependent(profiler::CounterId::Nanoseconds)),
687 sample: Some(sample),
688 ..Default::default()
689 };
690
691 let tasks = vec![
692 profiler::Task::SystemWide(profiler::SystemWide {}),
694 ];
695 let targets = profiler::TargetConfig::Tasks(tasks);
696 let config = profiler::Config {
697 configs: Some(vec![sampling_config]),
698 target: Some(targets),
699 ..Default::default()
700 };
701 let (client, server) = fidl::Socket::create_stream();
702 let configure = profiler::SessionConfigureRequest {
703 output: Some(server),
704 config: Some(config),
705 ..Default::default()
706 };
707
708 let proxy = connect_to_protocol::<profiler::SessionMarker>()
709 .context("Error connecting to Profiler protocol");
710 let session_proxy: profiler::SessionProxy = match proxy {
711 Ok(p) => p.clone(),
712 Err(e) => return error!(EINVAL, e),
713 };
714
715 let config_request = session_proxy.configure(configure).await;
717 match config_request {
718 Ok(_) => Ok((session_proxy, fidl::AsyncSocket::from_socket(client))),
719 Err(e) => return error!(EINVAL, e),
720 }
721}
722
723async fn collect_sample(
729 session_proxy: profiler::SessionProxy,
730 mut client: fidl::AsyncSocket,
731 duration: Duration,
732 perf_data_vmo: &zx::Vmo,
733 data_head_pointer: &AtomicPtr<u64>,
734 sample_type: u64,
735 sample_id: u64,
736 sample_period: u64,
737 vmo_write_offset: u64,
738) -> Result<(), Errno> {
739 let start_request = profiler::SessionStartRequest {
740 buffer_results: Some(true),
741 buffer_size_mb: Some(8 as u64),
742 ..Default::default()
743 };
744 let _ = session_proxy.start(&start_request).await.expect("Failed to start profiling");
745
746 track_stub!(
749 TODO("https://fxbug.dev/428974888"),
750 "[perf_event_open] don't hardcode sleep; test/user should decide sample duration"
751 );
752 let _ = fuchsia_async::Timer::new(duration).await;
753
754 let stats = session_proxy.stop().await;
755 let samples_collected = match stats {
756 Ok(stats) => stats.samples_collected.unwrap(),
757 Err(e) => return error!(EINVAL, e),
758 };
759
760 track_stub!(
761 TODO("https://fxbug.dev/422502681"),
762 "[perf_event_open] symbolize sample output and delete the below log_info"
763 );
764 log_info!("profiler samples_collected: {:?}", samples_collected);
765
766 let mut header = [0; 8];
768 let mut bytes_read = 0;
769 while bytes_read < 8 {
770 match client.read(&mut header[bytes_read..]).await {
771 Ok(0) => {
772 log_info!("[perf_event_open] Finished reading fxt record from socket.");
774 break;
775 }
776 Ok(n) => bytes_read += n,
777 Err(e) => {
778 log_warn!("[perf_event_open] Error reading from socket: {:?}", e);
779 break;
780 }
781 }
782 }
783
784 if bytes_read > 0 {
785 if bytes_read == 8 && header == FXT_MAGIC_BYTES {
786 let header_cursor = Cursor::new(header);
788 let reader = header_cursor.chain(client);
789 let (mut stream, _task) = SessionParser::new_async(reader);
790 while let Some(record_result) = stream.next().await {
791 match record_result {
792 Ok(TraceRecord::Profiler(ProfilerRecord::Backtrace(backtrace))) => {
793 let ips: Vec<u64> = backtrace.data;
794 let pid = Some(backtrace.process.0 as u32);
795 let tid = Some(backtrace.thread.0 as u32);
796 let perf_record_sample = PerfRecordSample { pid, tid, ips };
797 write_record_to_vmo(
798 perf_record_sample,
799 perf_data_vmo,
800 data_head_pointer,
801 sample_type,
802 sample_id,
803 sample_period,
804 vmo_write_offset,
805 );
806 }
807 Ok(_) => {
808 }
810 Err(e) => {
811 log_warn!("[perf_event_open] Error parsing FXT: {:?}", e);
812 break;
813 }
814 }
815 }
816 } else {
817 let mut buffer = vec![0; DEFAULT_CHUNK_SIZE];
821
822 loop {
823 let socket_data = client.read(&mut buffer).await;
826
827 match socket_data {
828 Ok(0) => {
829 log_info!("[perf_event_open] Finished reading from socket.");
831 break;
832 }
833 Ok(bytes_read) => {
834 let received_data = match std::str::from_utf8(&buffer[..bytes_read]) {
836 Ok(data) => data,
837 Err(e) => return error!(EINVAL, e),
838 };
839 if let Some(perf_record_sample) =
841 parse_perf_record_sample_format(received_data)
842 {
843 write_record_to_vmo(
844 perf_record_sample,
845 perf_data_vmo,
846 data_head_pointer,
847 sample_type,
848 sample_id,
849 sample_period,
850 vmo_write_offset,
851 );
852 }
853 }
854 Err(e) => {
855 log_warn!("[perf_event_open] Error reading from socket: {:?}", e);
856 break;
857 }
858 }
859 }
860 }
861 }
862
863 let reset_status = session_proxy.reset().await;
864 return match reset_status {
865 Ok(_) => Ok(()),
866 Err(e) => error!(EINVAL, e),
867 };
868}
869
870fn ping_receiver(
875 mut ioctl_sender: future_mpsc::Sender<(IoctlOp, sync_mpsc::Sender<()>)>,
876 command: IoctlOp,
877) {
878 log_info!("[perf_event_open] Received sampling command: {:?}", command);
879 let (profiling_complete_sender, profiling_complete_receiver) = sync_mpsc::channel::<()>();
880 match ioctl_sender.try_send((command, profiling_complete_sender)) {
881 Ok(_) => (),
882 Err(e) => {
883 if e.is_full() {
884 log_warn!("[perf_event_open] Failed to send {:?}: Channel full", command);
885 } else if e.is_disconnected() {
886 log_warn!("[perf_event_open] Failed to send {:?}: Receiver disconnected", command);
887 } else {
888 log_warn!("[perf_event_open] Failed to send {:?} due to {:?}", command, e.source());
889 }
890 }
891 };
892 let _ = profiling_complete_receiver.recv().unwrap();
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.files.get(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 closure = async move |_: LockedAndTask<'_>| {
1009 while let Some((command, profiling_complete_receiver)) = receiver.next().await {
1011 match command {
1012 IoctlOp::Enable => {
1013 match set_up_profiler(zx_sample_period).await {
1014 Ok((session_proxy, client)) => {
1015 track_stub!(
1016 TODO("https://fxbug.dev/422502681"),
1017 "[perf_event_open] don't hardcode profiling duration"
1018 );
1019
1020 let handle = vmo_handle_copy
1021 .as_mut()
1022 .expect("Failed to get VMO handle")
1023 .as_handle_ref()
1024 .duplicate(zx::Rights::SAME_RIGHTS)
1025 .unwrap();
1026
1027 let _ = collect_sample(
1028 session_proxy,
1029 client,
1030 Duration::from_millis(100),
1031 &zx::Vmo::from(handle),
1032 &*cloned_data_head_pointer,
1033 perf_event_file.sample_type,
1034 perf_event_file.sample_id,
1035 sample_period_in_ticks,
1036 perf_event_file.vmo_write_offset,
1037 )
1038 .await;
1039 let _ = profiling_complete_receiver.send(());
1041 }
1042 Err(e) => {
1043 log_warn!("Failed to profile: {}", e);
1044 }
1045 };
1046 }
1047 }
1048 }
1049 ()
1050 };
1051 let req = SpawnRequestBuilder::new()
1052 .with_debug_name("perf-event-sampler")
1053 .with_async_closure(closure)
1054 .build();
1055 current_task.kernel().kthreads.spawner().spawn_from_request(req);
1056
1057 let file = Box::new(PerfEventFile {
1058 _tid: tid,
1059 _cpu: cpu,
1060 perf_event_file: RwLock::new(perf_event_file),
1061 security_state: security::perf_event_alloc(current_task),
1062 data_head_pointer: data_head_pointer,
1063 });
1064 let file_handle =
1066 Anon::new_private_file(locked, current_task, file, OpenFlags::RDWR, "[perf_event]");
1067 let file_object_id = file_handle.id;
1068 let file_descriptor: Result<FdNumber, Errno> =
1069 current_task.add_file(locked, file_handle, FdFlags::empty());
1070
1071 match file_descriptor {
1072 Ok(fd) => {
1073 if group_fd.raw() == -1 {
1074 let perf_state = get_perf_state(¤t_task.kernel);
1075 let mut events = perf_state.format_id_lookup_table.lock();
1076 events.insert(file_object_id, event_id);
1077 }
1078 Ok(fd.into())
1079 }
1080 Err(_) => {
1081 track_stub!(
1082 TODO("https://fxbug.dev/402453955"),
1083 "[perf_event_open] implement remaining error handling"
1084 );
1085 error!(EMFILE)
1086 }
1087 }
1088}
1089#[cfg(target_arch = "aarch64")]
1091mod arch32 {
1092 pub use super::sys_perf_event_open as sys_arch32_perf_event_open;
1093}
1094
1095#[cfg(target_arch = "aarch64")]
1096pub use arch32::*;
1097
1098use crate::mm::memory::MemoryObject;
1099use crate::mm::{MemoryAccessorExt, ProtectionFlags};
1100use crate::task::CurrentTask;
1101use crate::vfs::{
1102 Anon, FdFlags, FdNumber, FileObject, FileObjectId, FileObjectState, FileOps, InputBuffer,
1103 OutputBuffer,
1104};
1105use crate::{fileops_impl_nonseekable, fileops_impl_noop_sync};