Skip to main content

zx/
process.rs

1// Copyright 2017 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Type-safe bindings for Zircon processes.
6
7use crate::sys::{self as sys, PadByte, ZX_OBJ_TYPE_UPPER_BOUND, zx_handle_t};
8use crate::{
9    Job, Koid, MapInfo, MonotonicInstant, Name, NullableHandle, ObjectQuery, Rights, Status, Task,
10    Thread, Topic, Vmar, VmoInfo, ok,
11};
12use bitflags::bitflags;
13use static_assertions::const_assert_eq;
14use std::mem::MaybeUninit;
15use zerocopy::{FromBytes, Immutable, KnownLayout};
16
17bitflags! {
18    /// Options that may be used when creating a `Process`.
19    #[repr(transparent)]
20    #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
21    pub struct ProcessOptions: u32 {
22        const SHARED = sys::ZX_PROCESS_SHARED;
23    }
24}
25
26impl Default for ProcessOptions {
27    fn default() -> Self {
28        ProcessOptions::empty()
29    }
30}
31
32#[repr(transparent)]
33#[derive(
34    Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, FromBytes, Immutable, KnownLayout,
35)]
36pub struct ProcessInfoFlags(u32);
37
38bitflags! {
39    impl ProcessInfoFlags: u32 {
40        const STARTED = sys::ZX_INFO_PROCESS_FLAG_STARTED;
41        const EXITED = sys::ZX_INFO_PROCESS_FLAG_EXITED;
42        const DEBUGGER_ATTACHED = sys::ZX_INFO_PROCESS_FLAG_DEBUGGER_ATTACHED;
43    }
44}
45
46/// An object representing a Zircon process.
47///
48/// As essentially a subtype of `NullableHandle`, it can be freely interconverted.
49#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
50#[repr(transparent)]
51pub struct Process(NullableHandle);
52impl_handle_based!(Process);
53unsafe_handle_properties!(object: Process,
54    props: [
55        {query_ty: PROCESS_DEBUG_ADDR, tag: ProcessDebugAddrTag, prop_ty: u64, get:get_debug_addr, set:set_debug_addr},
56        {query_ty: PROCESS_BREAK_ON_LOAD, tag: ProcessBreakOnLoadTag, prop_ty: u64, get:get_break_on_load, set:set_break_on_load},
57    ]
58);
59
60#[repr(C)]
61#[derive(Clone, Copy, Debug, PartialEq, Eq, FromBytes, Immutable, KnownLayout)]
62pub struct ProcessInfo {
63    pub return_code: i64,
64    pub start_time: MonotonicInstant,
65    pub flags: ProcessInfoFlags,
66
67    /// For ABI compatibility with zx_info_process_t.
68    _pad: [PadByte; 4],
69}
70
71impl ProcessInfo {
72    pub fn new(return_code: i64, start_time: MonotonicInstant, flags: ProcessInfoFlags) -> Self {
73        Self { return_code, start_time, flags, _pad: [PadByte::default(); 4] }
74    }
75}
76
77// Ensure this type remains ABI-compatible with zx_info_process_t.
78const_assert_eq!(std::mem::size_of::<ProcessInfo>(), std::mem::size_of::<sys::zx_info_process_t>());
79const_assert_eq!(
80    std::mem::offset_of!(ProcessInfo, return_code),
81    std::mem::offset_of!(sys::zx_info_process_t, return_code)
82);
83const_assert_eq!(
84    std::mem::offset_of!(ProcessInfo, start_time),
85    std::mem::offset_of!(sys::zx_info_process_t, start_time)
86);
87const_assert_eq!(
88    std::mem::offset_of!(ProcessInfo, flags),
89    std::mem::offset_of!(sys::zx_info_process_t, flags)
90);
91
92// ProcessInfo is able to be safely replaced with a byte representation and is a PoD type.
93unsafe impl ObjectQuery for ProcessInfo {
94    const TOPIC: Topic = Topic::PROCESS;
95    type InfoTy = ProcessInfo;
96}
97
98struct ProcessThreadsInfo;
99
100// ProcessThreadsInfo is able to be safely replaced with a byte representation and is a PoD type.
101unsafe impl ObjectQuery for ProcessThreadsInfo {
102    const TOPIC: Topic = Topic::PROCESS_THREADS;
103    type InfoTy = Koid;
104}
105
106sys::zx_info_task_stats_t!(TaskStatsInfo);
107
108impl From<sys::zx_info_task_stats_t> for TaskStatsInfo {
109    fn from(
110        sys::zx_info_task_stats_t {
111            mem_mapped_bytes,
112            mem_private_bytes,
113            mem_shared_bytes,
114            mem_scaled_shared_bytes,
115            mem_fractional_scaled_shared_bytes,
116        }: sys::zx_info_task_stats_t,
117    ) -> TaskStatsInfo {
118        TaskStatsInfo {
119            mem_mapped_bytes,
120            mem_private_bytes,
121            mem_shared_bytes,
122            mem_scaled_shared_bytes,
123            mem_fractional_scaled_shared_bytes,
124        }
125    }
126}
127
128// TaskStatsInfo is able to be safely replaced with a byte representation and is a PoD type.
129unsafe impl ObjectQuery for TaskStatsInfo {
130    const TOPIC: Topic = Topic::TASK_STATS;
131    type InfoTy = TaskStatsInfo;
132}
133
134struct ProcessMapsInfo;
135unsafe impl ObjectQuery for ProcessMapsInfo {
136    const TOPIC: Topic = Topic::PROCESS_MAPS;
137    type InfoTy = MapInfo;
138}
139
140struct ProcessVmoInfo;
141unsafe impl ObjectQuery for ProcessVmoInfo {
142    const TOPIC: Topic = Topic::PROCESS_VMOS;
143    type InfoTy = VmoInfo;
144}
145
146sys::zx_info_process_handle_stats_t!(ProcessHandleStats);
147
148impl Default for ProcessHandleStats {
149    fn default() -> Self {
150        Self { handle_count: [0; ZX_OBJ_TYPE_UPPER_BOUND] }
151    }
152}
153
154unsafe impl ObjectQuery for ProcessHandleStats {
155    const TOPIC: Topic = Topic::PROCESS_HANDLE_STATS;
156    type InfoTy = ProcessHandleStats;
157}
158
159impl Process {
160    /// Create a new process and its address space.
161    ///
162    /// Wraps the
163    /// [zx_process_create](https://fuchsia.dev/fuchsia-src/reference/syscalls/process_create.md)
164    /// syscall.
165    pub fn create(job: &Job, name: Name, options: ProcessOptions) -> Result<(Self, Vmar), Status> {
166        let mut this = 0;
167        let mut vmar = 0;
168
169        // SAFETY: `job` is a valid handle, `name` is valid to read from for `name.len()` bytes, and
170        // `this` and `vmar` are valid to write to.
171        ok(unsafe {
172            crate::sys::zx_process_create(
173                job.raw_handle(),
174                name.as_raw(),
175                name.len(),
176                options.bits(),
177                &mut this,
178                &mut vmar,
179            )
180        })?;
181
182        // SAFETY: the above call succeeded so `this` is a valid handle.
183        let this = Self(unsafe { NullableHandle::from_raw(this) });
184
185        // SAFETY: the above call succeeded so `vmar` is a valid handle.
186        let vmar = Vmar::from(unsafe { NullableHandle::from_raw(vmar) });
187
188        Ok((this, vmar))
189    }
190
191    /// Similar to `Thread::start`, but is used to start the first thread in a process.
192    ///
193    /// Wraps the
194    /// [zx_process_start](https://fuchsia.dev/fuchsia-src/reference/syscalls/process_start.md)
195    /// syscall.
196    pub fn start(
197        &self,
198        thread: &Thread,
199        entry: usize,
200        stack: usize,
201        arg1: NullableHandle,
202        arg2: usize,
203    ) -> Result<(), Status> {
204        let process_raw = self.raw_handle();
205        let thread_raw = thread.raw_handle();
206        let arg1 = arg1.into_raw();
207        ok(unsafe { sys::zx_process_start(process_raw, thread_raw, entry, stack, arg1, arg2) })
208    }
209
210    /// Create a thread inside a process.
211    ///
212    /// Wraps the
213    /// [zx_thread_create](https://fuchsia.dev/fuchsia-src/reference/syscalls/thread_create.md)
214    /// syscall.
215    pub fn create_thread(&self, name: &[u8]) -> Result<Thread, Status> {
216        let process_raw = self.raw_handle();
217        let name_ptr = name.as_ptr();
218        let name_len = name.len();
219        let options = 0;
220        let mut thread_out = 0;
221        let status = unsafe {
222            sys::zx_thread_create(process_raw, name_ptr, name_len, options, &mut thread_out)
223        };
224        ok(status)?;
225        unsafe { Ok(Thread::from(NullableHandle::from_raw(thread_out))) }
226    }
227
228    /// Write memory inside a process.
229    ///
230    /// Wraps the
231    /// [zx_process_write_memory](https://fuchsia.dev/fuchsia-src/reference/syscalls/process_write_memory.md)
232    /// syscall.
233    pub fn write_memory(&self, vaddr: sys::zx_vaddr_t, bytes: &[u8]) -> Result<usize, Status> {
234        let mut actual = 0;
235        let status = unsafe {
236            sys::zx_process_write_memory(
237                self.raw_handle(),
238                vaddr,
239                bytes.as_ptr(),
240                bytes.len(),
241                &mut actual,
242            )
243        };
244        ok(status).map(|()| actual)
245    }
246
247    /// Read memory from inside a process.
248    ///
249    /// Wraps the
250    /// [zx_process_read_memory](https://fuchsia.dev/fuchsia-src/reference/syscalls/process_read_memory.md)
251    /// syscall.
252    pub fn read_memory(&self, vaddr: sys::zx_vaddr_t, bytes: &mut [u8]) -> Result<usize, Status> {
253        // SAFETY: It's OK to interpret &mut [u8] as &mut [MaybeUninit<u8>] as long as we don't
254        // expose the MaybeUninit reference to code that would write uninitialized values to
255        // elements of the slice. Every valid state for a u8 is also a valid state for
256        // MaybeUninit<u8>, although the reverse is not true.
257        let (actually_read, _) = self.read_memory_uninit(vaddr, unsafe {
258            std::slice::from_raw_parts_mut(
259                bytes.as_mut_ptr().cast::<MaybeUninit<u8>>(),
260                bytes.len(),
261            )
262        })?;
263        Ok(actually_read.len())
264    }
265
266    /// Read memory from inside a process without requiring the output buffer to be initialized.
267    ///
268    /// Wraps the
269    /// [zx_process_read_memory](https://fuchsia.dev/fuchsia-src/reference/syscalls/process_read_memory.md)
270    /// syscall.
271    pub fn read_memory_uninit<'a>(
272        &self,
273        vaddr: sys::zx_vaddr_t,
274        buffer: &'a mut [MaybeUninit<u8>],
275    ) -> Result<(&'a mut [u8], &'a mut [MaybeUninit<u8>]), Status> {
276        let mut actually_read = 0;
277        // SAFETY: This is a system call that requires the pointers passed are valid to write to.
278        // We get the pointers from a valid mutable slice so we know it's safe to ask the kernel to
279        // write to them. Casting the *mut MaybeUninit<u8> to a *mut u8 is safe because all valid
280        // values for u8 are a subset of the valid values for MaybeUninit<u8> and we know the
281        // kernel won't write uninitialized values to the slice.
282        let status = unsafe {
283            sys::zx_process_read_memory(
284                self.raw_handle(),
285                vaddr,
286                // TODO(https://fxbug.dev/42079723) use MaybeUninit::slice_as_mut_ptr when stable
287                buffer.as_mut_ptr().cast::<u8>(),
288                buffer.len(),
289                &mut actually_read,
290            )
291        };
292        ok(status)?;
293        let (initialized, uninitialized) = buffer.split_at_mut(actually_read);
294        Ok((
295            // TODO(https://fxbug.dev/42079723) use MaybeUninit::slice_assume_init_mut when stable
296            // SAFETY: We're converting &mut [MaybeUninit<u8>] back to &mut [u8], which is only
297            // valid to do if all elements of `initialized` have actually been initialized. Here we
298            // have to trust that the kernel didn't lie when it said it wrote to the entire buffer,
299            // but as long as that assumption is valid them it's safe to assume this slice is init.
300            unsafe {
301                std::slice::from_raw_parts_mut(
302                    initialized.as_mut_ptr().cast::<u8>(),
303                    initialized.len(),
304                )
305            },
306            uninitialized,
307        ))
308    }
309
310    /// Wraps the
311    /// [zx_object_get_info](https://fuchsia.dev/fuchsia-src/reference/syscalls/object_get_info.md)
312    /// syscall for the ZX_INFO_PROCESS topic.
313    pub fn info(&self) -> Result<ProcessInfo, Status> {
314        self.0.get_info_single::<ProcessInfo>()
315    }
316
317    /// Wraps the
318    /// [zx_object_get_info](https://fuchsia.dev/fuchsia-src/reference/syscalls/object_get_info.md)
319    /// syscall for the ZX_INFO_PROCESS_THREADS topic.
320    pub fn threads(&self) -> Result<Vec<Koid>, Status> {
321        self.0.get_info_vec::<ProcessThreadsInfo>()
322    }
323
324    /// Wraps the
325    /// [zx_object_get_info](https://fuchsia.dev/fuchsia-src/reference/syscalls/object_get_info.md)
326    /// syscall for the ZX_INFO_TASK_STATS topic.
327    pub fn task_stats(&self) -> Result<TaskStatsInfo, Status> {
328        self.0.get_info_single::<TaskStatsInfo>()
329    }
330
331    /// Wraps the
332    /// [zx_object_get_info](https://fuchsia.dev/fuchsia-src/reference/syscalls/object_get_info.md)
333    /// syscall for the ZX_INFO_PROCESS_MAPS topic.
334    pub fn info_maps_vec(&self) -> Result<Vec<MapInfo>, Status> {
335        self.0.get_info_vec::<ProcessMapsInfo>()
336    }
337
338    /// Exit the current process with the given return code.
339    ///
340    /// Wraps the
341    /// [zx_process_exit](https://fuchsia.dev/fuchsia-src/reference/syscalls/process_exit.md)
342    /// syscall.
343    pub fn exit(retcode: i64) -> ! {
344        unsafe {
345            sys::zx_process_exit(retcode);
346            // zither generates the syscall returning a unit value. We know it will not proceed
347            // past this point however.
348            std::hint::unreachable_unchecked()
349        }
350    }
351
352    /// Wraps the
353    /// [zx_object_get_info](https://fuchsia.dev/fuchsia-src/reference/syscalls/object_get_info.md)
354    /// syscall for the ZX_INFO_PROCESS_HANDLE_STATS topic.
355    pub fn handle_stats(&self) -> Result<ProcessHandleStats, Status> {
356        self.0.get_info_single::<ProcessHandleStats>()
357    }
358
359    /// Wraps the
360    /// [zx_object_get_child](https://fuchsia.dev/fuchsia-src/reference/syscalls/object_get_child.md)
361    /// syscall.
362    pub fn get_child(&self, koid: &Koid, rights: Rights) -> Result<Thread, Status> {
363        let mut handle: zx_handle_t = Default::default();
364        let status = unsafe {
365            sys::zx_object_get_child(self.raw_handle(), koid.raw_koid(), rights.bits(), &mut handle)
366        };
367        ok(status)?;
368        Ok(Thread::from(unsafe { NullableHandle::from_raw(handle) }))
369    }
370
371    /// Wraps the
372    /// [zx_object_get_info](https://fuchsia.dev/fuchsia-src/reference/syscalls/object_get_info.md)
373    /// syscall for the ZX_INFO_PROCESS_VMO topic.
374    pub fn info_vmos_vec(&self) -> Result<Vec<VmoInfo>, Status> {
375        self.0.get_info_vec::<ProcessVmoInfo>()
376    }
377
378    /// Wraps the
379    /// [zx_object_get_info](https://fuchsia.dev/fuchsia-src/reference/syscalls/object_get_info.md)
380    /// syscall for the ZX_INFO_PROCESS_MAPS topic. Contrarily to [Process::info_vmos_vec], this
381    /// method ensures that no intermediate copy of the data is made, at the price of a less
382    /// convenient interface.
383    pub fn info_maps<'a>(
384        &self,
385        info_out: &'a mut [std::mem::MaybeUninit<MapInfo>],
386    ) -> Result<(&'a mut [MapInfo], &'a mut [std::mem::MaybeUninit<MapInfo>], usize), Status> {
387        self.0.get_info::<ProcessMapsInfo>(info_out)
388    }
389
390    /// Wraps the
391    /// [zx_object_get_info](https://fuchsia.dev/fuchsia-src/reference/syscalls/object_get_info.md)
392    /// syscall for the ZX_INFO_PROCESS_VMO topic. Contrarily to [Process::info_vmos_vec], this
393    /// method ensures that no intermediate copy of the data is made, at the price of a less
394    /// convenient interface.
395    pub fn info_vmos<'a>(
396        &self,
397        info_out: &'a mut [std::mem::MaybeUninit<VmoInfo>],
398    ) -> Result<(&'a mut [VmoInfo], &'a mut [std::mem::MaybeUninit<VmoInfo>], usize), Status> {
399        self.0.get_info::<ProcessVmoInfo>(info_out)
400    }
401}
402
403impl Task for Process {}
404
405#[cfg(test)]
406mod tests {
407    use crate::cprng_draw;
408    // The unit tests are built with a different crate name, but fdio and fuchsia_runtime return a
409    // "real" zx::Process that we need to use.
410    use assert_matches::assert_matches;
411    use std::ffi::CString;
412    use std::mem::MaybeUninit;
413    use zx::{
414        Instant, MapDetails, ProcessInfo, ProcessInfoFlags, Signals, Task, TaskStatsInfo,
415        VmarFlags, Vmo, sys, system_get_page_size,
416    };
417
418    const STARTED: ProcessInfoFlags = ProcessInfoFlags::STARTED;
419    const STARTED_AND_EXITED: ProcessInfoFlags = STARTED.union(ProcessInfoFlags::EXITED);
420
421    #[test]
422    fn info_self() {
423        let process = fuchsia_runtime::process_self();
424        let info = process.info().unwrap();
425        assert_matches!(
426            info,
427            ProcessInfo {
428                return_code: 0,
429                start_time,
430                flags: STARTED,
431                ..
432            } if start_time.into_nanos() > 0
433        );
434    }
435
436    #[test]
437    fn stats_self() {
438        let process = fuchsia_runtime::process_self();
439        let task_stats = process.task_stats().unwrap();
440
441        // Values greater than zero should be reported back for all memory usage
442        // types.
443        assert!(matches!(task_stats,
444            TaskStatsInfo {
445                mem_mapped_bytes,
446                mem_private_bytes,
447                mem_shared_bytes,
448                mem_scaled_shared_bytes,
449                mem_fractional_scaled_shared_bytes: _
450            }
451            if mem_mapped_bytes > 0
452                && mem_private_bytes > 0
453                && mem_shared_bytes > 0
454                && mem_scaled_shared_bytes > 0));
455    }
456
457    #[test]
458    fn exit_and_info() {
459        let mut randbuf = [0; 8];
460        cprng_draw(&mut randbuf);
461        let expected_code = i64::from_le_bytes(randbuf);
462        let arg = CString::new(format!("{}", expected_code)).unwrap();
463
464        // This test utility will exercise zx::Process::exit, using the provided argument as the
465        // return code.
466        let binpath = CString::new("/pkg/bin/exit_with_code_util").unwrap();
467        let process = fdio::spawn(
468            &fuchsia_runtime::job_default(),
469            fdio::SpawnOptions::DEFAULT_LOADER,
470            &binpath,
471            &[&arg],
472        )
473        .expect("Failed to spawn process");
474
475        process
476            .wait_one(Signals::PROCESS_TERMINATED, Instant::INFINITE)
477            .expect("Wait for process termination failed");
478        let info = process.info().unwrap();
479        assert_matches!(
480            info,
481            ProcessInfo {
482                return_code,
483                start_time,
484                flags: STARTED_AND_EXITED,
485                ..
486            } if return_code == expected_code && start_time.into_nanos() > 0
487        );
488    }
489
490    #[test]
491    fn kill_and_info() {
492        // This test utility will sleep "forever" without exiting, so that we can kill it..
493        let binpath = CString::new("/pkg/bin/sleep_forever_util").unwrap();
494        let process = fdio::spawn(
495            &fuchsia_runtime::job_default(),
496            // Careful not to clone stdio here, or the test runner can hang.
497            fdio::SpawnOptions::DEFAULT_LOADER,
498            &binpath,
499            &[&binpath],
500        )
501        .expect("Failed to spawn process");
502
503        let info = process.info().unwrap();
504        assert_matches!(
505            info,
506            ProcessInfo {
507                return_code: 0,
508                start_time,
509                flags: STARTED,
510                ..
511             } if start_time.into_nanos() > 0
512        );
513
514        process.kill().expect("Failed to kill process");
515        process
516            .wait_one(Signals::PROCESS_TERMINATED, Instant::INFINITE)
517            .expect("Wait for process termination failed");
518
519        let info = process.info().unwrap();
520        assert_matches!(
521            info,
522            ProcessInfo {
523                return_code: sys::ZX_TASK_RETCODE_SYSCALL_KILL,
524                start_time,
525                flags: STARTED_AND_EXITED,
526                ..
527            } if start_time.into_nanos() > 0
528        );
529    }
530
531    #[test]
532    fn maps_info() {
533        let root_vmar = fuchsia_runtime::vmar_root_self();
534        let process = fuchsia_runtime::process_self();
535
536        // Create two mappings so we know what to expect from our test calls.
537        let vmo = Vmo::create(system_get_page_size() as u64).unwrap();
538        let vmo_koid = vmo.koid().unwrap();
539
540        let map1 = root_vmar
541            .map(0, &vmo, 0, system_get_page_size() as usize, VmarFlags::PERM_READ)
542            .unwrap();
543        let map2 = root_vmar
544            .map(0, &vmo, 0, system_get_page_size() as usize, VmarFlags::PERM_READ)
545            .unwrap();
546
547        // Querying a single info. As we know there are at least two mappings this is guaranteed to
548        // not return all of them.
549        let mut data = vec![MaybeUninit::uninit(); 1];
550        let (returned, _, available) = process.info_maps(&mut data).unwrap();
551        assert_eq!(returned.len(), 1);
552        assert!(available > 0);
553
554        // Add some slack to the total to account for mappings created as a result of the heap
555        // allocation in Vec.
556        let total = available + 10;
557
558        // Allocate and retrieve all of the mappings.
559        let mut data = vec![MaybeUninit::uninit(); total];
560
561        let (info, _, available) = process.info_maps(&mut data).unwrap();
562
563        // Ensure we fail if some mappings are missing.
564        assert_eq!(info.len(), available);
565
566        // We should find our two mappings in the info.
567        let count = info
568            .iter()
569            .filter(|info| match info.details() {
570                MapDetails::Mapping(d) => d.vmo_koid == vmo_koid,
571                _ => false,
572            })
573            .count();
574        assert_eq!(count, 2);
575
576        // We created these mappings and are not letting any references to them escape so unmapping
577        // is safe to do.
578        unsafe {
579            root_vmar.unmap(map1, system_get_page_size() as usize).unwrap();
580            root_vmar.unmap(map2, system_get_page_size() as usize).unwrap();
581        }
582    }
583
584    #[test]
585    fn info_maps_vec() {
586        let root_vmar = fuchsia_runtime::vmar_root_self();
587        let process = fuchsia_runtime::process_self();
588
589        // Create two mappings so we know what to expect from our test calls.
590        let vmo = Vmo::create(system_get_page_size() as u64).unwrap();
591        let vmo_koid = vmo.koid().unwrap();
592
593        let map1 = root_vmar
594            .map(0, &vmo, 0, system_get_page_size() as usize, VmarFlags::PERM_READ)
595            .unwrap();
596        let map2 = root_vmar
597            .map(0, &vmo, 0, system_get_page_size() as usize, VmarFlags::PERM_READ)
598            .unwrap();
599
600        let info = process.info_maps_vec().unwrap();
601
602        // We should find our two mappings in the info.
603        let count = info
604            .iter()
605            .filter(|info| match info.details() {
606                MapDetails::Mapping(d) => d.vmo_koid == vmo_koid,
607                _ => false,
608            })
609            .count();
610        assert_eq!(count, 2);
611
612        // We created these mappings and are not letting any references to them escape so unmapping
613        // is safe to do.
614        unsafe {
615            root_vmar.unmap(map1, system_get_page_size() as usize).unwrap();
616            root_vmar.unmap(map2, system_get_page_size() as usize).unwrap();
617        }
618    }
619
620    #[test]
621    fn info_vmos() {
622        let process = fuchsia_runtime::process_self();
623
624        // Create two mappings so we know what to expect from our test calls.
625        let vmo = Vmo::create(system_get_page_size() as u64).unwrap();
626        let vmo_koid = vmo.koid().unwrap();
627
628        let mut data = vec![MaybeUninit::uninit(); 2048];
629        let (info, _, available) = process.info_vmos(&mut data).unwrap();
630
631        // Ensure we fail if some vmos are missing, so we can adjust the buffer size.
632        assert_eq!(info.len(), available);
633
634        // We should find our two mappings in the info.
635        let count = info.iter().filter(|map| map.koid == vmo_koid).count();
636        assert_eq!(count, 1);
637    }
638
639    #[test]
640    fn info_vmos_vec() {
641        let process = fuchsia_runtime::process_self();
642
643        // Create two mappings so we know what to expect from our test calls.
644        let vmo = Vmo::create(system_get_page_size() as u64).unwrap();
645        let vmo_koid = vmo.koid().unwrap();
646
647        let info = process.info_vmos_vec().unwrap();
648
649        // We should find our two mappings in the info.
650        let count = info.iter().filter(|map| map.koid == vmo_koid).count();
651        assert_eq!(count, 1);
652    }
653
654    #[test]
655    fn handle_stats() {
656        let process = fuchsia_runtime::process_self();
657        let handle_stats = process.handle_stats().unwrap();
658
659        // We don't have an opinion about how many handles a typical process has, or what types
660        // they will be (the function returns counts for up to 64 different types),
661        // but a reasonable total should be between 1 and a million.
662        let sum: u32 = handle_stats.handle_count.iter().sum();
663
664        assert!(sum > 0);
665        assert!(sum < 1_000_000);
666    }
667
668    #[test]
669    fn threads_contain_self() {
670        let current_thread_koid =
671            fuchsia_runtime::with_thread_self(|thread| thread.koid().unwrap());
672        let threads_koids = fuchsia_runtime::process_self().threads().unwrap();
673        assert!(threads_koids.contains(&current_thread_koid));
674        let thread_handle = fuchsia_runtime::process_self()
675            .get_child(&current_thread_koid, zx::Rights::NONE)
676            .unwrap();
677        assert_eq!(thread_handle.koid().unwrap(), current_thread_koid);
678    }
679
680    // The vdso_next tests don't have permission to create raw processes.
681    #[cfg(not(feature = "vdso_next"))]
682    #[test]
683    fn new_process_no_threads() {
684        let job = fuchsia_runtime::job_default().create_child_job().unwrap();
685        let (process, _) =
686            job.create_child_process(zx::ProcessOptions::empty(), b"test-process").unwrap();
687        assert!(process.threads().unwrap().is_empty());
688    }
689
690    // The vdso_next tests don't have permission to create raw processes.
691    #[cfg(not(feature = "vdso_next"))]
692    #[test]
693    fn non_started_threads_dont_show_up() {
694        let job = fuchsia_runtime::job_default().create_child_job().unwrap();
695        let (process, _) =
696            job.create_child_process(zx::ProcessOptions::empty(), b"test-process").unwrap();
697
698        let thread = process.create_thread(b"test-thread").unwrap();
699        let thread_koid = thread.koid().unwrap();
700
701        assert!(process.threads().unwrap().is_empty());
702        assert!(process.get_child(&thread_koid, zx::Rights::NONE).is_err());
703    }
704
705    // The vdso_next tests don't have permission to create raw processes.
706    #[cfg(not(feature = "vdso_next"))]
707    #[test]
708    fn started_threads_show_up() {
709        let job = fuchsia_runtime::job_default().create_child_job().unwrap();
710        let (process, root_vmar) =
711            job.create_child_process(zx::ProcessOptions::empty(), b"test-process").unwrap();
712
713        let valid_addr = root_vmar.info().unwrap().base;
714
715        let thread1 = process.create_thread(b"test-thread-1").unwrap();
716        let thread2 = process.create_thread(b"test-thread-2").unwrap();
717
718        // start with the thread suspended, so we don't care about executing invalid code.
719        let thread1_suspended = thread1.suspend().unwrap();
720        process.start(&thread1, valid_addr, valid_addr, zx::NullableHandle::invalid(), 0).unwrap();
721
722        let threads_koids = process.threads().unwrap();
723        assert_eq!(threads_koids.len(), 1);
724        assert_eq!(threads_koids[0], thread1.koid().unwrap());
725        assert_eq!(
726            process.get_child(&threads_koids[0], zx::Rights::NONE).unwrap().koid().unwrap(),
727            threads_koids[0]
728        );
729
730        // Add another thread.
731        let thread2_suspended = thread2.suspend().unwrap();
732        thread2.start(valid_addr, valid_addr, 0, 0).unwrap();
733
734        let threads_koids = process.threads().unwrap();
735        assert_eq!(threads_koids.len(), 2);
736        assert!(threads_koids.contains(&thread1.koid().unwrap()));
737        assert!(threads_koids.contains(&thread2.koid().unwrap()));
738        assert_eq!(
739            process.get_child(&threads_koids[0], zx::Rights::NONE).unwrap().koid().unwrap(),
740            threads_koids[0]
741        );
742        assert_eq!(
743            process.get_child(&threads_koids[1], zx::Rights::NONE).unwrap().koid().unwrap(),
744            threads_koids[1]
745        );
746
747        process.kill().unwrap();
748        process.wait_one(Signals::TASK_TERMINATED, Instant::INFINITE).unwrap();
749
750        drop(thread1_suspended);
751        drop(thread2_suspended);
752
753        assert!(process.threads().unwrap().is_empty());
754    }
755}