The Pedigree Project 0.1
trace-context.cc
1/* Copyright (c) 2026, Pedigree Developers. */
2#include "pedigree/kernel/LockGuard.h"
3#include "pedigree/kernel/process/Scheduler.h"
4#include "pedigree/kernel/process/Thread.h"
5#include "pedigree/kernel/utilities/assert.h"
6#include "pedigree/kernel/utilities/utility.h"
7
8#include "PosixSubsystem.h"
9#include "trace-context.h"
10
11namespace {
12size_t localId(const Thread& thread) {
13 return const_cast<Thread&>(thread).getId();
14}
15} // namespace
16
17TraceTaskToken::Snapshot TraceTaskToken::snapshot() const {
18 LockGuard<Spinlock> guard(m_Lock);
19 return m_Identity;
20}
21
22bool TraceTaskToken::live() const {
23 return snapshot().state == State::Live;
24}
25
26void TraceTaskToken::publish(const Thread& thread) {
27 LockGuard<Spinlock> guard(m_Lock);
28 assert(m_Identity.state == State::Staged);
29 m_Identity.processId = thread.getParent()->getId();
30 m_Identity.localThreadId = localId(thread);
31 m_Identity.linuxTaskId = thread.getTaskId();
32 m_Identity.state = State::Live;
33}
34
35void TraceTaskToken::promote(const Thread& thread) {
36 LockGuard<Spinlock> guard(m_Lock);
37 if (m_Identity.state != State::Live)
38 return;
39 assert(m_Identity.processId == thread.getParent()->getId());
40 assert(m_Identity.localThreadId == localId(thread));
41 m_Identity.linuxTaskId = thread.getTaskId();
42}
43
44void TraceTaskToken::close() {
45 LockGuard<Spinlock> guard(m_Lock);
46 m_Identity.state = State::Closed;
47}
48
49PosixTraceContext::~PosixTraceContext() {
50 close();
51 // The core creation scope and current syscall retain the owning subsystem.
52 assert(!m_ThreadCreations && !m_EnrollmentPending);
53}
54
55bool PosixTraceContext::valid() const {
56 return !__atomic_load_n(&m_Closed, __ATOMIC_ACQUIRE);
57}
58
59void PosixTraceContext::attach(Process& process) {
60 LockGuard<Mutex> guard(m_AdmissionLock);
61 assert(valid());
62 assert(!m_Attached || m_ProcessId == process.getId());
63 m_ProcessId = process.getId();
64 m_Attached = true;
65}
66
67TraceStatus PosixTraceContext::prepareTask(UniquePointer<PreparedTraceTask>& result) {
68 assert(!result);
69 {
70 LockGuard<Mutex> guard(m_AdmissionLock);
71 if (!m_Attached || !valid())
72 return TraceStatus::Missing;
73 }
75 if (!prepared)
76 return TraceStatus::NoMemory;
77 prepared.get()->m_Token = TraceTaskRef::tryAllocate();
78 if (!prepared.get()->m_Token)
79 return TraceStatus::NoMemory;
80 result = pedigree_std::move(prepared);
81 return TraceStatus::Success;
82}
83
84TraceStatus PosixTraceContext::publishTask(UniquePointer<PreparedTraceTask>& prepared,
85 Thread& thread) {
86 assert(prepared && prepared.get()->m_Token);
87 LockGuard<Mutex> guard(m_AdmissionLock);
88 if (!m_Attached || !valid() || thread.getParent()->getId() != m_ProcessId ||
90 return TraceStatus::Missing;
91 // Shutdown publishes TerminateThread before calling the retirement hook.
92 // Its earlier hook therefore cannot be undone by delayed publication.
93 if (tokenUnlocked(localId(thread)))
94 return TraceStatus::Denied;
95 prepared.get()->m_Token->publish(thread);
96 auto* node = prepared.releaseOwnership();
97 node->m_Next = m_Tasks;
98 m_Tasks = node;
99 return TraceStatus::Success;
100}
101
102TraceTaskRef PosixTraceContext::tokenUnlocked(size_t id) const {
103 for (auto* task = m_Tasks; task; task = task->m_Next) {
104 const auto identity = task->m_Token->snapshot();
105 if (identity.state == TraceTaskToken::State::Live && identity.localThreadId == id)
106 return task->m_Token;
107 }
108 return {};
109}
110
111bool PosixTraceContext::ownsTokenUnlocked(const TraceTaskRef& token) const {
112 for (auto* task = m_Tasks; task; task = task->m_Next) {
113 if (task->m_Token == token)
114 return token->live();
115 }
116 return false;
117}
118
119bool PosixTraceContext::taskToken(const Thread& thread, TraceTaskRef& result) const {
120 assert(!result);
121 TraceTaskRef acquired;
122 {
123 LockGuard<Mutex> guard(m_AdmissionLock);
124 if (!m_Attached || !valid() || thread.getParent()->getId() != m_ProcessId)
125 return false;
126 acquired = tokenUnlocked(localId(thread));
127 }
128 result = pedigree_std::move(acquired);
129 return bool(result);
130}
131
132void PosixTraceContext::setCreator(const TraceTaskRef& token) {
133 LockGuard<Mutex> guard(m_AdmissionLock);
134 assert(m_Attached && valid() && !m_Tasks && !m_Creator);
135 m_Creator = token;
136}
137
138TraceTaskRef PosixTraceContext::creator() const {
139 LockGuard<Mutex> guard(m_AdmissionLock);
140 return valid() ? m_Creator : TraceTaskRef{};
141}
142
143void PosixTraceContext::promoteExec(const Thread& thread) {
144 TraceTaskRef token;
145 {
146 LockGuard<Mutex> guard(m_AdmissionLock);
147 if (!valid())
148 return;
149 assert(thread.getParent()->getId() == m_ProcessId);
150 token = tokenUnlocked(localId(thread));
151 assert(bool(token));
152 token->promote(thread);
153 }
154}
155
156bool PosixTraceContext::acquireIncoming(TraceRelationRef& result) const {
157 assert(!result);
158 LockGuard<Spinlock> guard(m_RelationLock);
159 if (!valid())
160 return false;
161 result = m_Incoming;
162 return bool(result);
163}
164
165bool PosixTraceContext::selectStop(size_t parentPid, bool stopped, bool continued, bool consume,
166 PosixWait::Report& report) {
167 LockGuard<Spinlock> guard(m_RelationLock);
168 if (!valid() || !m_Incoming)
169 return false;
170 const auto owner = m_Incoming->tracer()->snapshot();
171 return owner.state == TraceTaskToken::State::Live && owner.processId == parentPid &&
172 m_Incoming->selectStop(stopped, continued, consume, report);
173}
174
175bool PosixTraceContext::hasIncoming() const {
176 LockGuard<Spinlock> guard(m_RelationLock);
177 return bool(m_Incoming);
178}
179
180void PosixTraceContext::clearIncoming(const PosixTraceRelation* relation) {
181 TraceRelationRef retired;
182 {
183 LockGuard<Spinlock> guard(m_RelationLock);
184 if (m_Incoming.get() == relation)
185 retired = pedigree_std::move(m_Incoming);
186 }
187}
188
189void PosixTraceContext::retireTask(const Thread& thread) {
190 if (!valid())
191 return;
192 PreparedTraceTask* retired = nullptr;
193 TraceRelationRef incoming;
194 {
195 LockGuard<Mutex> guard(m_AdmissionLock);
196 if (!valid() || thread.getParent()->getId() != m_ProcessId)
197 return;
198 auto** position = &m_Tasks;
199 while (*position && (*position)->m_Token->snapshot().localThreadId != localId(thread))
200 position = &(*position)->m_Next;
201 if (!*position)
202 return;
203 retired = *position;
204 *position = retired->m_Next;
205 retired->m_Token->close();
206 LockGuard<Spinlock> publication(m_RelationLock);
207 if (m_Incoming && m_Incoming->tracee() == retired->m_Token)
208 incoming = pedigree_std::move(m_Incoming);
209 }
210 if (incoming)
211 incoming->taskClosed(retired->m_Token);
212 for (;;) {
213 OwnedRelation* outgoing = nullptr;
214 {
215 LockGuard<Mutex> guard(m_AdmissionLock);
216 auto** position = &m_Outgoing;
217 while (*position && (*position)->relation->tracer() != retired->m_Token)
218 position = &(*position)->next;
219 if (*position) {
220 outgoing = *position;
221 *position = outgoing->next;
222 }
223 }
224 if (!outgoing)
225 break;
226 outgoing->relation->taskClosed(retired->m_Token);
227 delete outgoing;
228 }
229 delete retired;
230}
231
232void PosixTraceContext::close() {
233 // The once-only, IRQ-enabled subsystem exit boundary performs first close.
234 // Later terminal destructor fallbacks must not acquire a sleeping lock.
235 if (!valid())
236 return;
237 PreparedTraceTask* tasks = nullptr;
238 OwnedRelation* outgoing = nullptr;
239 TraceRelationRef incoming;
241 {
242 LockGuard<Mutex> guard(m_AdmissionLock);
243 if (!valid())
244 return;
245 __atomic_store_n(&m_Closed, true, __ATOMIC_RELEASE);
246 tasks = m_Tasks;
247 m_Tasks = nullptr;
248 outgoing = m_Outgoing;
249 m_Outgoing = nullptr;
250 for (auto* task = tasks; task; task = task->m_Next)
251 task->m_Token->close();
252 creator = pedigree_std::move(m_Creator);
253 LockGuard<Spinlock> publication(m_RelationLock);
254 incoming = pedigree_std::move(m_Incoming);
255 }
256 if (incoming)
257 incoming->taskClosed(incoming->tracee());
258 while (outgoing) {
259 auto* next = outgoing->next;
260 outgoing->relation->taskClosed(outgoing->relation->tracer());
261 delete outgoing;
262 outgoing = next;
263 }
264 while (tasks) {
265 auto* next = tasks->m_Next;
266 delete tasks;
267 tasks = next;
268 }
269}
270
271TraceStatus posix_trace_acquire_task(const TraceTaskRef& token, Process::ThreadLease& result) {
272 assert(!result);
273 if (!token)
274 return TraceStatus::Missing;
275 const auto identity = token->snapshot();
276 if (identity.state != TraceTaskToken::State::Live)
277 return TraceStatus::Missing;
279 if (!Scheduler::instance().acquireProcessById(process, identity.processId) ||
280 process->getType() != Process::Posix)
281 return TraceStatus::Missing;
283 if (!process->acquireThreadById(task, identity.localThreadId) || !task->acceptingEvents() ||
285 return TraceStatus::Missing;
286 auto* subsystem = static_cast<PosixSubsystem*>(process->getSubsystem());
287 TraceTaskRef actual;
288 if (!subsystem || !subsystem->traceContext().taskToken(*task.get(), actual) || actual != token ||
289 !token->live())
290 return TraceStatus::Missing;
291 result = pedigree_std::move(task);
292 return TraceStatus::Success;
293}
size_t getId()
Definition Process.h:462
MUST_USE_RESULT bool acquireThreadById(ThreadLease &lease, size_t id)
Definition Process.cc:1337
static Scheduler & instance()
Definition Scheduler.h:96
static SharedPointer< TraceTaskToken > tryAllocate(Args...)
T * get() const
@ Continue
No unwind necessary, carry on as normal.
Definition Thread.h:500
@ TerminateThread
Exit only this thread during Process exit.
Definition Thread.h:502
UnwindType getUnwindState()
Definition Thread.h:518
Process * getParent() const
Definition Thread.h:325
size_t getTaskId() const
Definition Thread.h:455
T * releaseOwnership() noexcept
Definition Pointers.h:106
#define assert(x)
Definition assert.h:39