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