The Pedigree Project 0.1
trace-state.cc
1/* Copyright (c) 2026, Pedigree Developers. */
2#include "pedigree/kernel/process/Scheduler.h"
3#include "pedigree/kernel/process/Thread.h"
4#include "pedigree/kernel/process/WaitQueue.h"
5#include "pedigree/kernel/processor/Processor.h"
6#include "pedigree/kernel/processor/ProcessorInformation.h"
7#include "pedigree/kernel/utilities/utility.h"
8
9#include "PosixProcess.h"
10#include "PosixSubsystem.h"
11#include "trace-context.h"
12#include "trace-state.h"
13#include "wait-state.h"
14
16 public:
17 mutable WaitQueue waiters;
18 bool active = false, closed = false, stopped = false, report = false;
19 bool resumed = false, detached = false, execTrap = false;
20 bool ownsReservation = false;
21 uint64_t generation = 0;
23 bool jobStopped = false, continueReport = false;
24 StopKind kind = StopKind::Signal;
25 int signal = 0, replacement = 0;
26 int32_t senderPid = 0;
27 uint32_t senderUid = 0;
28 Amd64UserRegisters registers{};
29 TraceSignalInfo info;
30 PosixWait::Report waitReport, continuationReport;
31};
32
33PosixTraceRelation::PosixTraceRelation(const TraceTaskRef& tracer, const TraceTaskRef& tracee)
34 : m_Tracer(tracer), m_Tracee(tracee), m_State(UniquePointer<State>::allocate()) {}
35PosixTraceRelation::~PosixTraceRelation() = default;
36bool PosixTraceRelation::valid() const {
37 return bool(m_State);
38}
39bool PosixTraceRelation::activate() {
40 if (!valid())
41 return false;
42 auto guard = m_State.get()->waiters.acquire();
43 if (m_State.get()->closed || m_State.get()->active)
44 return false;
45 m_State.get()->active = true;
46 return true;
47}
48bool PosixTraceRelation::stopped() const {
49 if (!valid())
50 return false;
51 auto guard = m_State.get()->waiters.acquire();
52 return m_State.get()->stopped && !m_State.get()->closed && !m_State.get()->resumed;
53}
54TraceStatus PosixTraceRelation::snapshotRegisters(Amd64UserRegisters& output) const {
55 auto guard = m_State.get()->waiters.acquire();
56 if (!m_State.get()->stopped || m_State.get()->closed || m_State.get()->resumed)
57 return TraceStatus::NotStopped;
58 output = m_State.get()->registers;
59 return TraceStatus::Success;
60}
61TraceStatus PosixTraceRelation::snapshotSignalInfo(TraceSignalInfo& output) const {
62 auto guard = m_State.get()->waiters.acquire();
63 if (!m_State.get()->stopped || m_State.get()->closed || m_State.get()->resumed)
64 return TraceStatus::NotStopped;
65 if (m_State.get()->kind == StopKind::Group)
66 return TraceStatus::Invalid;
67 output = m_State.get()->info;
68 return TraceStatus::Success;
69}
70bool PosixTraceRelation::selectStop(bool stopped, bool continued, bool consume,
71 PosixWait::Report& output) {
72 auto guard = m_State.get()->waiters.acquire();
73 if (m_State.get()->closed)
74 return false;
75 if (stopped && m_State.get()->stopped && m_State.get()->report) {
76 output = m_State.get()->waitReport;
77 if (consume)
78 m_State.get()->report = false;
79 return true;
80 }
81 if (continued && m_State.get()->continueReport) {
82 output = m_State.get()->continuationReport;
83 if (consume)
84 m_State.get()->continueReport = false;
85 return true;
86 }
87 return false;
88}
89TraceStatus PosixTraceRelation::resume(int signal, bool detach) {
90 if (signal < 0 || signal > 64)
91 return TraceStatus::Invalid;
92 uint64_t generation;
93 bool prepare = false, inheritReservation = false;
94 {
95 auto guard = m_State.get()->waiters.acquire();
96 if (!m_State.get()->stopped || m_State.get()->closed || m_State.get()->resumed)
97 return TraceStatus::NotStopped;
98 generation = m_State.get()->generation;
99 prepare = signal && m_State.get()->kind != StopKind::Group &&
100 (m_State.get()->kind == StopKind::Exec || signal != m_State.get()->signal);
101 inheritReservation = m_State.get()->ownsReservation;
102 }
103 auto* caller =
104 static_cast<PosixProcess*>(Processor::information().getCurrentThread()->getParent());
105 const auto credentials = caller->snapshotCredentials();
107 if (prepare) {
108 const auto status = posix_trace_prepare_resume(m_Tracee, signal, inheritReservation,
109 static_cast<int32_t>(caller->getUserspaceId()),
110 credentials.ruid, prepared);
111 if (status != TraceStatus::Success)
112 return status;
113 }
114 {
115 auto guard = m_State.get()->waiters.acquire();
116 if (!m_State.get()->stopped || m_State.get()->closed || m_State.get()->resumed ||
117 m_State.get()->generation != generation)
118 return TraceStatus::NotStopped;
119 m_State.get()->prepared = pedigree_std::move(prepared);
120 m_State.get()->senderPid = static_cast<int32_t>(caller->getUserspaceId());
121 m_State.get()->senderUid = credentials.ruid;
122 m_State.get()->replacement = signal;
123 m_State.get()->detached = detach;
124 if (m_State.get()->kind == StopKind::Group && !detach)
125 m_State.get()->jobStopped = false;
126 m_State.get()->resumed = true;
127 m_State.get()->report = false;
128 guard.wakeAll();
129 }
130 if (detach)
131 unlink();
132 return TraceStatus::Success;
133}
134void PosixTraceRelation::taskClosed(const TraceTaskRef& task) {
135 if (!valid() || (task != m_Tracer && task != m_Tracee))
136 return;
138 {
139 auto guard = m_State.get()->waiters.acquire();
140 if (m_State.get()->closed)
141 return;
142 m_State.get()->closed = true;
143 m_State.get()->active = false;
144 m_State.get()->detached = true;
145 if (!m_State.get()->resumed)
146 m_State.get()->replacement =
147 task == m_Tracer && m_State.get()->kind != StopKind::Exec ? m_State.get()->signal : 0;
148 m_State.get()->resumed = true;
149 if (task == m_Tracee)
150 retired = pedigree_std::move(m_State.get()->prepared);
151 m_State.get()->report = false;
152 guard.wakeAll();
153 }
154 unlink();
155}
156void PosixTraceRelation::unlink() {
157 // Each lookup is lexical. Neither tokens nor the relation pin a task while
158 // it is stopped, and a retiring context already detached its own reference.
159 {
161 if (posix_trace_acquire_task(m_Tracee, task) == TraceStatus::Success) {
162 auto* subsystem = static_cast<PosixSubsystem*>(task->getParent()->getSubsystem());
163 subsystem->traceContext().clearIncoming(this);
164 task->clearSignalFrameRequirement();
165 }
166 }
168 if (posix_trace_acquire_task(m_Tracer, tracer) == TraceStatus::Success) {
169 auto* subsystem = static_cast<PosixSubsystem*>(tracer->getParent()->getSubsystem());
170 subsystem->traceContext().unregisterTracee(this);
171 }
172}
173void PosixTraceRelation::continued(Process& process) {
175 if (!Scheduler::instance().acquireProcess(parent, process.getParent())) {
176 auto guard = m_State.get()->waiters.acquire();
177 guard.wakeAll();
178 return;
179 }
180 PosixWait::Report report;
181 report.pid = static_cast<int32_t>(process.getUserspaceId());
182 report.uid = static_cast<PosixProcess&>(process).snapshotCredentials().ruid;
183 report.cause = PosixWait::Continue;
184 report.status = 18;
185 report.userNanoseconds = process.getUserTime() + process.getReapedChildrenUserTime();
186 report.kernelNanoseconds = process.getKernelTime() + process.getReapedChildrenKernelTime();
187 auto parentGuard = parent->acquireChildStateWait();
188 auto guard = m_State.get()->waiters.acquire();
189 if (m_State.get()->jobStopped && !m_State.get()->closed) {
190 m_State.get()->continuationReport = report;
191 m_State.get()->continueReport = true;
192 m_State.get()->jobStopped = false;
193 parentGuard.wakeAll();
194 }
195 guard.wakeAll();
196}
197void PosixTraceRelation::imageCommitted() {
198 auto guard = m_State.get()->waiters.acquire();
199 if (!m_State.get()->closed && m_State.get()->active) {
200 m_State.get()->execTrap = true;
201 m_State.get()->report = false;
202 m_State.get()->stopped = false;
203 }
204}
205bool PosixTraceRelation::takeExecTrap() {
206 auto guard = m_State.get()->waiters.acquire();
207 const bool pending = m_State.get()->execTrap && !m_State.get()->closed;
208 m_State.get()->execTrap = false;
209 return pending;
210}
211PosixTraceRelation::Resume PosixTraceRelation::stop(Thread& thread, UserReturnFrame& frame,
212 StopKind kind, int signal,
213 const TraceSignalInfo& info, size_t epoch,
214 bool ownsReservation) {
216 if (!frame.snapshot(copy) || copy.architecture != UserRegisterSnapshot::Architecture::Amd64)
217 return {0, false, true};
218 Process* process = thread.getParent();
219 PosixWait::Report report;
220 report.pid = static_cast<int32_t>(process->getUserspaceId());
221 report.uid = static_cast<PosixProcess*>(process)->snapshotCredentials().ruid;
222 report.cause = 4; // CLD_TRAPPED.
223 report.status = signal;
224 report.userNanoseconds = process->getUserTime() + process->getReapedChildrenUserTime();
225 report.kernelNanoseconds = process->getKernelTime() + process->getReapedChildrenKernelTime();
226 thread.setUserReturnSignalParked(true);
227 struct ParkReset {
228 Thread& thread;
229 ~ParkReset() {
230 thread.setUserReturnSignalParked(false);
231 }
232 } parkReset{thread};
234 [](void* value) { static_cast<Thread*>(value)->setUserReturnSignalParked(false); }, &thread);
235 {
237 if (!Scheduler::instance().acquireProcess(parent, process->getParent()))
238 return {kind == StopKind::Exec ? 0 : signal, true, false};
239 auto parentGuard = parent->acquireChildStateWait();
240 if (kind == StopKind::Group && process->getContinuationEpoch() != epoch)
241 return {0, false, false};
242 auto guard = m_State.get()->waiters.acquire();
243 if (m_State.get()->closed || !m_State.get()->active)
244 return {kind == StopKind::Exec ? 0 : signal, true, false};
245 ++m_State.get()->generation;
246 m_State.get()->ownsReservation = ownsReservation;
247 m_State.get()->kind = kind;
248 if (kind == StopKind::Group)
249 m_State.get()->jobStopped = true;
250 m_State.get()->signal = signal;
251 m_State.get()->registers = copy.amd64;
252 m_State.get()->info = info;
253 m_State.get()->waitReport = report;
254 m_State.get()->stopped = true;
255 m_State.get()->report = true;
256 m_State.get()->resumed = false;
257 parentGuard.wakeAll();
258 }
259 {
261 Process::ThreadLease recipient;
262 if (Scheduler::instance().acquireProcess(parent, process->getParent()) &&
263 parent->getType() == Process::Posix && parent->acquireProcessSignalThread(recipient)) {
264 auto* subsystem = static_cast<PosixSubsystem*>(parent->getSubsystem());
266 if (subsystem && subsystem->getSignalDisposition(17, disposition) &&
267 !(disposition.flags & 1U /* Linux SA_NOCLDSTOP */))
268 subsystem->queueSignalDelivery(recipient.get(), 17, nullptr, 4, true, signal);
269 }
270 }
271 Resume result;
272 for (;;) {
273 auto guard = m_State.get()->waiters.acquire();
274 if (thread.getUnwindState() != Thread::Continue) {
275 result.terminal = true;
276 break;
277 }
278 if (m_State.get()->resumed) {
279 result.signal = m_State.get()->replacement;
280 result.detached = m_State.get()->detached;
281 result.pid = m_State.get()->senderPid;
282 result.uid = m_State.get()->senderUid;
283 result.prepared = pedigree_std::move(m_State.get()->prepared);
284 break;
285 }
286 const auto wake = guard.wait(WaitQueue::Channel(), Thread::ProcessWait);
287 if (wake == WaitQueue::WakeReason::Terminating || wake == WaitQueue::WakeReason::Unwinding) {
288 result.terminal = true;
289 break;
290 }
291 }
292 {
293 auto guard = m_State.get()->waiters.acquire();
294 m_State.get()->stopped = false;
295 m_State.get()->report = false;
296 }
297 thread.setUserReturnSignalParked(false);
298 if (kind == StopKind::Group && result.detached && !result.terminal)
299 process->suspendIfContinuationEpoch(signal, epoch);
300 return result;
301}
size_t getUserspaceId() const
Definition Process.h:467
MUST_USE_RESULT bool acquireProcessSignalThread(ThreadLease &lease)
Definition Process.cc:1298
Process * getParent()
Definition Process.h:567
WaitQueue::Guard acquireChildStateWait()
Definition Process.h:681
void suspendIfContinuationEpoch(int stopSignal, size_t continuationEpoch)
Definition Process.cc:1896
Time::Timestamp getUserTime() const
Definition Process.h:774
size_t getContinuationEpoch()
Definition Process.cc:1991
static ProcessorInformation & information()
static Scheduler & instance()
Definition Scheduler.h:96
MUST_USE_RESULT bool acquireProcess(ProcessLease &lease, size_t n)
Definition Scheduler.cc:273
@ Continue
No unwind necessary, carry on as normal.
Definition Thread.h:500
UnwindType getUnwindState()
Definition Thread.h:518
Process * getParent() const
Definition Thread.h:325