Skip to main content

detcore_model/
pid.rs

1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 * All rights reserved.
4 *
5 * This source code is licensed under the BSD-style license found in the
6 * LICENSE file in the root directory of this source tree.
7 */
8
9use std::fmt;
10use std::str::FromStr;
11
12use nix::unistd;
13use serde::Deserialize;
14use serde::Serialize;
15
16// Deterministic Pids/Tids:
17//--------------------------------------------------------------------------------
18
19/// Deterministic "virtual" version of `reverie::Pid`
20#[derive(
21    PartialEq, // Silly protection from rustfmt disagreements.
22    Debug,
23    Eq,
24    Clone,
25    Copy,
26    Hash,
27    PartialOrd,
28    Ord,
29    Serialize,
30    Deserialize,
31    Default,
32)]
33pub struct DetPid(i32);
34
35impl fmt::Display for DetPid {
36    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
37        fmt::Display::fmt(&self.0, f)
38    }
39}
40
41#[cfg(any())]
42impl From<reverie_syscalls::Pid> for DetPid {
43    fn from(p: reverie_syscalls::Pid) -> Self {
44        DetPid(p.into())
45    }
46}
47
48impl From<unistd::Pid> for DetPid {
49    fn from(p: unistd::Pid) -> Self {
50        DetPid(p.into())
51    }
52}
53
54// implementing From<DetPid> for unistd::Pid would violate foreign trait rules
55#[allow(clippy::from_over_into)]
56impl Into<unistd::Pid> for DetPid {
57    fn into(self) -> unistd::Pid {
58        unistd::Pid::from_raw(self.0)
59    }
60}
61
62impl DetPid {
63    /// Create a DetPid from a raw pid.
64    pub const fn from_raw(pid: i32) -> DetPid {
65        DetPid(pid)
66    }
67
68    /// Convert to a row integer.
69    pub fn as_raw(&self) -> i32 {
70        self.0
71    }
72}
73
74impl FromStr for DetPid {
75    type Err = <i32 as FromStr>::Err;
76
77    fn from_str(s: &str) -> Result<Self, Self::Err> {
78        Ok(Self::from_raw(s.parse::<i32>()?))
79    }
80}
81
82/// Deterministic "virtual" version of `reverie::Tid`
83pub type DetTid = DetPid;
84
85/// How the guest's process and thread IDs are numbered
86/// (`Config::guest_ids`).
87#[derive(
88    Clone,
89    Copy,
90    Debug,
91    Default,
92    PartialEq,
93    Eq,
94    Hash,
95    Serialize,
96    Deserialize
97)]
98pub enum GuestIds {
99    /// The PID namespace Hermit creates for the run numbers the guest: the
100    /// kernel gives each new task the next free ID in that namespace, so the
101    /// IDs depend only on the guest's own creation order.
102    #[default]
103    Namespace,
104    /// The host's IDs, which differ from run to run: `--no-namespace` where
105    /// Detcore cannot choose them, on a backend whose guest tasks are host
106    /// tasks.
107    Host,
108    /// Detcore chooses every ID (`--no-namespace`): the guest's first process
109    /// gets `base`, and automatic choices take the next ID in creation order,
110    /// skipping IDs already used by guest-authored `set_tid` calls. Linux
111    /// assigns the choices through `clone3`'s `set_tid`. No ID is given out twice in a run,
112    /// so the IDs depend only on `base` and the guest's own creation order.
113    Chosen {
114        /// The first process's ID.
115        base: i32,
116        /// When an ID cannot be chosen, refuse the run (`--strict`, or an
117        /// explicit `--pid-base`) rather than let that task have a host ID.
118        refuse_host_ids: bool,
119    },
120}
121
122impl GuestIds {
123    /// The first process's ID, when Detcore chooses the IDs.
124    pub fn base(&self) -> Option<i32> {
125        match self {
126            GuestIds::Chosen { base, .. } => Some(*base),
127            GuestIds::Namespace | GuestIds::Host => None,
128        }
129    }
130
131    /// Whether two runs number their guests alike, whatever each does when an
132    /// ID cannot be chosen: a schedule recorded under one replays under the
133    /// other.
134    pub fn numbers_like(&self, other: &GuestIds) -> bool {
135        match (self, other) {
136            (GuestIds::Chosen { base: a, .. }, GuestIds::Chosen { base: b, .. }) => a == b,
137            (a, b) => a == b,
138        }
139    }
140}
141
142impl fmt::Display for GuestIds {
143    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
144        match self {
145            GuestIds::Namespace => write!(f, "numbered by the run's PID namespace"),
146            GuestIds::Host => write!(f, "host-assigned, different in every run"),
147            GuestIds::Chosen { base, .. } => {
148                write!(
149                    f,
150                    "chosen by Hermit from {base} in creation order (clone3 set_tid)"
151                )
152            }
153        }
154    }
155}