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