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}