1use crate::sys::{self as sys, zx_handle_t, zx_time_t, ZX_OBJ_TYPE_UPPER_BOUND};
8use crate::{
9 object_get_info, object_get_info_single, object_get_info_vec, object_get_property,
10 object_set_property, ok, AsHandleRef, Handle, HandleBased, HandleRef, Job, Koid, MapInfo, Name,
11 ObjectQuery, Property, PropertyQuery, Rights, Status, Task, Thread, Topic, Vmar, VmoInfo,
12};
13use bitflags::bitflags;
14use std::mem::MaybeUninit;
15
16bitflags! {
17 #[repr(transparent)]
19 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
20 pub struct ProcessOptions: u32 {
21 const SHARED = sys::ZX_PROCESS_SHARED;
22 }
23}
24
25impl Default for ProcessOptions {
26 fn default() -> Self {
27 ProcessOptions::empty()
28 }
29}
30
31bitflags! {
32 #[repr(transparent)]
33 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
34 pub struct ProcessInfoFlags: u32 {
35 const STARTED = sys::ZX_INFO_PROCESS_FLAG_STARTED;
36 const EXITED = sys::ZX_INFO_PROCESS_FLAG_EXITED;
37 const DEBUGGER_ATTACHED = sys::ZX_INFO_PROCESS_FLAG_DEBUGGER_ATTACHED;
38 }
39}
40
41#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
45#[repr(transparent)]
46pub struct Process(Handle);
47impl_handle_based!(Process);
48unsafe_handle_properties!(object: Process,
49 props: [
50 {query_ty: PROCESS_DEBUG_ADDR, tag: ProcessDebugAddrTag, prop_ty: u64, get:get_debug_addr, set:set_debug_addr},
51 {query_ty: PROCESS_BREAK_ON_LOAD, tag: ProcessBreakOnLoadTag, prop_ty: u64, get:get_break_on_load, set:set_break_on_load},
52 ]
53);
54
55sys::zx_info_process_t!(ProcessInfo);
56
57impl From<sys::zx_info_process_t> for ProcessInfo {
58 fn from(info: sys::zx_info_process_t) -> ProcessInfo {
59 let sys::zx_info_process_t { return_code, start_time, flags } = info;
60 ProcessInfo { return_code, start_time, flags }
61 }
62}
63
64unsafe impl ObjectQuery for ProcessInfo {
66 const TOPIC: Topic = Topic::PROCESS;
67 type InfoTy = ProcessInfo;
68}
69
70struct ProcessThreadsInfo;
71
72unsafe impl ObjectQuery for ProcessThreadsInfo {
74 const TOPIC: Topic = Topic::PROCESS_THREADS;
75 type InfoTy = Koid;
76}
77
78sys::zx_info_task_stats_t!(TaskStatsInfo);
79
80impl From<sys::zx_info_task_stats_t> for TaskStatsInfo {
81 fn from(
82 sys::zx_info_task_stats_t {
83 mem_mapped_bytes,
84 mem_private_bytes,
85 mem_shared_bytes,
86 mem_scaled_shared_bytes,
87 mem_fractional_scaled_shared_bytes,
88 }: sys::zx_info_task_stats_t,
89 ) -> TaskStatsInfo {
90 TaskStatsInfo {
91 mem_mapped_bytes,
92 mem_private_bytes,
93 mem_shared_bytes,
94 mem_scaled_shared_bytes,
95 mem_fractional_scaled_shared_bytes,
96 }
97 }
98}
99
100unsafe impl ObjectQuery for TaskStatsInfo {
102 const TOPIC: Topic = Topic::TASK_STATS;
103 type InfoTy = TaskStatsInfo;
104}
105
106struct ProcessMapsInfo;
107unsafe impl ObjectQuery for ProcessMapsInfo {
108 const TOPIC: Topic = Topic::PROCESS_MAPS;
109 type InfoTy = MapInfo;
110}
111
112struct ProcessVmoInfo;
113unsafe impl ObjectQuery for ProcessVmoInfo {
114 const TOPIC: Topic = Topic::PROCESS_VMOS;
115 type InfoTy = VmoInfo;
116}
117
118sys::zx_info_process_handle_stats_t!(ProcessHandleStats);
119
120impl Default for ProcessHandleStats {
121 fn default() -> Self {
122 Self { handle_count: [0; ZX_OBJ_TYPE_UPPER_BOUND] }
123 }
124}
125
126unsafe impl ObjectQuery for ProcessHandleStats {
127 const TOPIC: Topic = Topic::PROCESS_HANDLE_STATS;
128 type InfoTy = ProcessHandleStats;
129}
130
131impl Process {
132 pub fn create(job: &Job, name: Name, options: ProcessOptions) -> Result<(Self, Vmar), Status> {
138 let mut this = 0;
139 let mut vmar = 0;
140
141 ok(unsafe {
144 crate::sys::zx_process_create(
145 job.raw_handle(),
146 name.as_raw(),
147 name.len(),
148 options.bits(),
149 &mut this,
150 &mut vmar,
151 )
152 })?;
153
154 let this = Self(unsafe { Handle::from_raw(this) });
156
157 let vmar = Vmar::from(unsafe { Handle::from_raw(vmar) });
159
160 Ok((this, vmar))
161 }
162
163 pub fn start(
169 &self,
170 thread: &Thread,
171 entry: usize,
172 stack: usize,
173 arg1: Handle,
174 arg2: usize,
175 ) -> Result<(), Status> {
176 let process_raw = self.raw_handle();
177 let thread_raw = thread.raw_handle();
178 let arg1 = arg1.into_raw();
179 ok(unsafe { sys::zx_process_start(process_raw, thread_raw, entry, stack, arg1, arg2) })
180 }
181
182 pub fn create_thread(&self, name: &[u8]) -> Result<Thread, Status> {
188 let process_raw = self.raw_handle();
189 let name_ptr = name.as_ptr();
190 let name_len = name.len();
191 let options = 0;
192 let mut thread_out = 0;
193 let status = unsafe {
194 sys::zx_thread_create(process_raw, name_ptr, name_len, options, &mut thread_out)
195 };
196 ok(status)?;
197 unsafe { Ok(Thread::from(Handle::from_raw(thread_out))) }
198 }
199
200 pub fn write_memory(&self, vaddr: sys::zx_vaddr_t, bytes: &[u8]) -> Result<usize, Status> {
206 let mut actual = 0;
207 let status = unsafe {
208 sys::zx_process_write_memory(
209 self.raw_handle(),
210 vaddr,
211 bytes.as_ptr(),
212 bytes.len(),
213 &mut actual,
214 )
215 };
216 ok(status).map(|()| actual)
217 }
218
219 pub fn read_memory(&self, vaddr: sys::zx_vaddr_t, bytes: &mut [u8]) -> Result<usize, Status> {
225 let (actually_read, _) = self.read_memory_uninit(vaddr, unsafe {
230 std::slice::from_raw_parts_mut(
231 bytes.as_mut_ptr().cast::<MaybeUninit<u8>>(),
232 bytes.len(),
233 )
234 })?;
235 Ok(actually_read.len())
236 }
237
238 pub fn read_memory_uninit<'a>(
244 &self,
245 vaddr: sys::zx_vaddr_t,
246 buffer: &'a mut [MaybeUninit<u8>],
247 ) -> Result<(&'a mut [u8], &'a mut [MaybeUninit<u8>]), Status> {
248 let mut actually_read = 0;
249 let status = unsafe {
255 sys::zx_process_read_memory(
256 self.raw_handle(),
257 vaddr,
258 buffer.as_mut_ptr().cast::<u8>(),
260 buffer.len(),
261 &mut actually_read,
262 )
263 };
264 ok(status)?;
265 let (initialized, uninitialized) = buffer.split_at_mut(actually_read);
266 Ok((
267 unsafe {
273 std::slice::from_raw_parts_mut(
274 initialized.as_mut_ptr().cast::<u8>(),
275 initialized.len(),
276 )
277 },
278 uninitialized,
279 ))
280 }
281
282 pub fn info(&self) -> Result<ProcessInfo, Status> {
286 object_get_info_single::<ProcessInfo>(self.as_handle_ref())
287 }
288
289 pub fn threads(&self) -> Result<Vec<Koid>, Status> {
293 object_get_info_vec::<ProcessThreadsInfo>(self.as_handle_ref())
294 }
295
296 pub fn task_stats(&self) -> Result<TaskStatsInfo, Status> {
300 object_get_info_single::<TaskStatsInfo>(self.as_handle_ref())
301 }
302
303 pub fn info_maps_vec(&self) -> Result<Vec<MapInfo>, Status> {
307 object_get_info_vec::<ProcessMapsInfo>(self.as_handle_ref())
308 }
309
310 pub fn exit(retcode: i64) -> ! {
316 unsafe {
317 sys::zx_process_exit(retcode);
318 std::hint::unreachable_unchecked()
321 }
322 }
323
324 pub fn handle_stats(&self) -> Result<ProcessHandleStats, Status> {
328 object_get_info_single::<ProcessHandleStats>(self.as_handle_ref())
329 }
330
331 pub fn get_child(&self, koid: &Koid, rights: Rights) -> Result<Thread, Status> {
335 let mut handle: zx_handle_t = Default::default();
336 let status = unsafe {
337 sys::zx_object_get_child(self.raw_handle(), koid.raw_koid(), rights.bits(), &mut handle)
338 };
339 ok(status)?;
340 Ok(Thread::from(unsafe { Handle::from_raw(handle) }))
341 }
342
343 pub fn info_vmos_vec(&self) -> Result<Vec<VmoInfo>, Status> {
347 let raw_info = object_get_info_vec::<ProcessVmoInfo>(self.as_handle_ref())?;
348 Ok(raw_info)
349 }
350
351 pub fn info_maps<'a>(
357 &self,
358 info_out: &'a mut [std::mem::MaybeUninit<MapInfo>],
359 ) -> Result<(&'a mut [MapInfo], &'a mut [std::mem::MaybeUninit<MapInfo>], usize), Status> {
360 object_get_info::<ProcessMapsInfo>(self.as_handle_ref(), info_out)
361 }
362
363 pub fn info_vmos<'a>(
369 &self,
370 info_out: &'a mut [std::mem::MaybeUninit<VmoInfo>],
371 ) -> Result<(&'a mut [VmoInfo], &'a mut [std::mem::MaybeUninit<VmoInfo>], usize), Status> {
372 object_get_info::<ProcessVmoInfo>(self.as_handle_ref(), info_out)
373 }
374}
375
376impl Task for Process {}
377
378#[cfg(test)]
379mod tests {
380 use crate::cprng_draw;
381 use assert_matches::assert_matches;
384 use std::ffi::CString;
385 use std::mem::MaybeUninit;
386 use zx::{
387 sys, system_get_page_size, AsHandleRef, Handle, Instant, MapDetails, ProcessInfo,
388 ProcessInfoFlags, ProcessOptions, Signals, Task, TaskStatsInfo, VmarFlags, Vmo,
389 };
390
391 #[test]
392 fn info_self() {
393 let process = fuchsia_runtime::process_self();
394 let info = process.info().unwrap();
395 const STARTED: u32 = ProcessInfoFlags::STARTED.bits();
396 assert_matches!(
397 info,
398 ProcessInfo {
399 return_code: 0,
400 start_time,
401 flags: STARTED,
402 } if start_time > 0
403 );
404 }
405
406 #[test]
407 fn stats_self() {
408 let process = fuchsia_runtime::process_self();
409 let task_stats = process.task_stats().unwrap();
410
411 assert!(matches!(task_stats,
414 TaskStatsInfo {
415 mem_mapped_bytes,
416 mem_private_bytes,
417 mem_shared_bytes,
418 mem_scaled_shared_bytes,
419 mem_fractional_scaled_shared_bytes: _
420 }
421 if mem_mapped_bytes > 0
422 && mem_private_bytes > 0
423 && mem_shared_bytes > 0
424 && mem_scaled_shared_bytes > 0));
425 }
426
427 #[test]
428 fn exit_and_info() {
429 let mut randbuf = [0; 8];
430 cprng_draw(&mut randbuf);
431 let expected_code = i64::from_le_bytes(randbuf);
432 let arg = CString::new(format!("{}", expected_code)).unwrap();
433
434 let binpath = CString::new("/pkg/bin/exit_with_code_util").unwrap();
437 let process = fdio::spawn(
438 &fuchsia_runtime::job_default(),
439 fdio::SpawnOptions::DEFAULT_LOADER,
440 &binpath,
441 &[&arg],
442 )
443 .expect("Failed to spawn process");
444
445 process
446 .wait_handle(Signals::PROCESS_TERMINATED, Instant::INFINITE)
447 .expect("Wait for process termination failed");
448 let info = process.info().unwrap();
449 const STARTED_AND_EXITED: u32 =
450 ProcessInfoFlags::STARTED.bits() | ProcessInfoFlags::EXITED.bits();
451 assert_matches!(
452 info,
453 ProcessInfo {
454 return_code,
455 start_time,
456 flags: STARTED_AND_EXITED,
457 } if return_code == expected_code && start_time > 0
458 );
459 }
460
461 #[test]
462 fn kill_and_info() {
463 let binpath = CString::new("/pkg/bin/sleep_forever_util").unwrap();
465 let process = fdio::spawn(
466 &fuchsia_runtime::job_default(),
467 fdio::SpawnOptions::DEFAULT_LOADER,
469 &binpath,
470 &[&binpath],
471 )
472 .expect("Failed to spawn process");
473
474 let info = process.info().unwrap();
475 const STARTED: u32 = ProcessInfoFlags::STARTED.bits();
476 assert_matches!(
477 info,
478 ProcessInfo {
479 return_code: 0,
480 start_time,
481 flags: STARTED,
482 } if start_time > 0
483 );
484
485 process.kill().expect("Failed to kill process");
486 process
487 .wait_handle(Signals::PROCESS_TERMINATED, Instant::INFINITE)
488 .expect("Wait for process termination failed");
489
490 let info = process.info().unwrap();
491 const STARTED_AND_EXITED: u32 =
492 ProcessInfoFlags::STARTED.bits() | ProcessInfoFlags::EXITED.bits();
493 assert_matches!(
494 info,
495 ProcessInfo {
496 return_code: sys::ZX_TASK_RETCODE_SYSCALL_KILL,
497 start_time,
498 flags: STARTED_AND_EXITED,
499 } if start_time > 0
500 );
501 }
502
503 #[test]
504 fn maps_info() {
505 let root_vmar = fuchsia_runtime::vmar_root_self();
506 let process = fuchsia_runtime::process_self();
507
508 let vmo = Vmo::create(system_get_page_size() as u64).unwrap();
510 let vmo_koid = vmo.get_koid().unwrap();
511
512 let map1 = root_vmar
513 .map(0, &vmo, 0, system_get_page_size() as usize, VmarFlags::PERM_READ)
514 .unwrap();
515 let map2 = root_vmar
516 .map(0, &vmo, 0, system_get_page_size() as usize, VmarFlags::PERM_READ)
517 .unwrap();
518
519 let mut data = vec![MaybeUninit::uninit(); 1];
522 let (returned, _, available) = process.info_maps(&mut data).unwrap();
523 assert_eq!(returned.len(), 1);
524 assert!(available > 0);
525
526 let total = available + 10;
529
530 let mut data = vec![MaybeUninit::uninit(); total];
532
533 let (info, _, available) = process.info_maps(&mut data).unwrap();
534
535 assert_eq!(info.len(), available);
537
538 let count = info
540 .iter()
541 .filter(|info| match info.details() {
542 MapDetails::Mapping(d) => d.vmo_koid == vmo_koid,
543 _ => false,
544 })
545 .count();
546 assert_eq!(count, 2);
547
548 unsafe {
551 root_vmar.unmap(map1, system_get_page_size() as usize).unwrap();
552 root_vmar.unmap(map2, system_get_page_size() as usize).unwrap();
553 }
554 }
555
556 #[test]
557 fn info_maps_vec() {
558 let root_vmar = fuchsia_runtime::vmar_root_self();
559 let process = fuchsia_runtime::process_self();
560
561 let vmo = Vmo::create(system_get_page_size() as u64).unwrap();
563 let vmo_koid = vmo.get_koid().unwrap();
564
565 let map1 = root_vmar
566 .map(0, &vmo, 0, system_get_page_size() as usize, VmarFlags::PERM_READ)
567 .unwrap();
568 let map2 = root_vmar
569 .map(0, &vmo, 0, system_get_page_size() as usize, VmarFlags::PERM_READ)
570 .unwrap();
571
572 let info = process.info_maps_vec().unwrap();
573
574 let count = info
576 .iter()
577 .filter(|info| match info.details() {
578 MapDetails::Mapping(d) => d.vmo_koid == vmo_koid,
579 _ => false,
580 })
581 .count();
582 assert_eq!(count, 2);
583
584 unsafe {
587 root_vmar.unmap(map1, system_get_page_size() as usize).unwrap();
588 root_vmar.unmap(map2, system_get_page_size() as usize).unwrap();
589 }
590 }
591
592 #[test]
593 fn info_vmos() {
594 let process = fuchsia_runtime::process_self();
595
596 let vmo = Vmo::create(system_get_page_size() as u64).unwrap();
598 let vmo_koid = vmo.get_koid().unwrap();
599
600 let mut data = vec![MaybeUninit::uninit(); 2048];
601 let (info, _, available) = process.info_vmos(&mut data).unwrap();
602
603 assert_eq!(info.len(), available);
605
606 let count = info.iter().filter(|map| map.koid == vmo_koid).count();
608 assert_eq!(count, 1);
609 }
610
611 #[test]
612 fn info_vmos_vec() {
613 let process = fuchsia_runtime::process_self();
614
615 let vmo = Vmo::create(system_get_page_size() as u64).unwrap();
617 let vmo_koid = vmo.get_koid().unwrap();
618
619 let info = process.info_vmos_vec().unwrap();
620
621 let count = info.iter().filter(|map| map.koid == vmo_koid).count();
623 assert_eq!(count, 1);
624 }
625
626 #[test]
627 fn handle_stats() {
628 let process = fuchsia_runtime::process_self();
629 let handle_stats = process.handle_stats().unwrap();
630
631 let sum: u32 = handle_stats.handle_count.iter().sum();
635
636 assert!(sum > 0);
637 assert!(sum < 1_000_000);
638 }
639
640 #[test]
641 fn threads_contain_self() {
642 let current_thread_koid = fuchsia_runtime::thread_self().get_koid().unwrap();
643 let threads_koids = fuchsia_runtime::process_self().threads().unwrap();
644 assert!(threads_koids.contains(¤t_thread_koid));
645 let thread_handle = fuchsia_runtime::process_self()
646 .get_child(¤t_thread_koid, zx::Rights::NONE)
647 .unwrap();
648 assert_eq!(thread_handle.get_koid().unwrap(), current_thread_koid);
649 }
650
651 #[test]
652 fn new_process_no_threads() {
653 let job = fuchsia_runtime::job_default().create_child_job().unwrap();
654 let (process, _) =
655 job.create_child_process(ProcessOptions::empty(), b"test-process").unwrap();
656 assert!(process.threads().unwrap().is_empty());
657 }
658
659 #[test]
660 fn non_started_threads_dont_show_up() {
661 let job = fuchsia_runtime::job_default().create_child_job().unwrap();
662 let (process, _) =
663 job.create_child_process(ProcessOptions::empty(), b"test-process").unwrap();
664
665 let thread = process.create_thread(b"test-thread").unwrap();
666 let thread_koid = thread.get_koid().unwrap();
667
668 assert!(process.threads().unwrap().is_empty());
669 assert!(process.get_child(&thread_koid, zx::Rights::NONE).is_err());
670 }
671
672 #[test]
673 fn started_threads_show_up() {
674 let job = fuchsia_runtime::job_default().create_child_job().unwrap();
675 let (process, root_vmar) =
676 job.create_child_process(ProcessOptions::empty(), b"test-process").unwrap();
677
678 let valid_addr = root_vmar.info().unwrap().base;
679
680 let thread1 = process.create_thread(b"test-thread-1").unwrap();
681 let thread2 = process.create_thread(b"test-thread-2").unwrap();
682
683 let thread1_suspended = thread1.suspend().unwrap();
685 process.start(&thread1, valid_addr, valid_addr, Handle::invalid(), 0).unwrap();
686
687 let threads_koids = process.threads().unwrap();
688 assert_eq!(threads_koids.len(), 1);
689 assert_eq!(threads_koids[0], thread1.get_koid().unwrap());
690 assert_eq!(
691 process.get_child(&threads_koids[0], zx::Rights::NONE).unwrap().get_koid().unwrap(),
692 threads_koids[0]
693 );
694
695 let thread2_suspended = thread2.suspend().unwrap();
697 thread2.start(valid_addr, valid_addr, 0, 0).unwrap();
698
699 let threads_koids = process.threads().unwrap();
700 assert_eq!(threads_koids.len(), 2);
701 assert!(threads_koids.contains(&thread1.get_koid().unwrap()));
702 assert!(threads_koids.contains(&thread2.get_koid().unwrap()));
703 assert_eq!(
704 process.get_child(&threads_koids[0], zx::Rights::NONE).unwrap().get_koid().unwrap(),
705 threads_koids[0]
706 );
707 assert_eq!(
708 process.get_child(&threads_koids[1], zx::Rights::NONE).unwrap().get_koid().unwrap(),
709 threads_koids[1]
710 );
711
712 process.kill().unwrap();
713 process.wait_handle(Signals::TASK_TERMINATED, Instant::INFINITE).unwrap();
714
715 drop(thread1_suspended);
716 drop(thread2_suspended);
717
718 assert!(process.threads().unwrap().is_empty());
719 }
720}