AsmGrader 0.0.0
Loading...
Searching...
No Matches
tracer_types.hpp
Go to the documentation of this file.
1
3#pragma once
4
11#include <asmgrader/logging.hpp>
12
13#include <fmt/base.h>
14#include <fmt/format.h>
15#include <gsl/util>
16#include <libassert/assert.hpp>
17
18#include <chrono>
19#include <cstdlib>
20#include <optional>
21#include <string>
22
23#include <bits/types/siginfo_t.h>
24#include <sched.h>
25#include <sys/ptrace.h>
26#include <sys/types.h>
27#include <sys/wait.h>
28
29namespace asmgrader {
30
31enum class PtraceEvent {
32 Stop = PTRACE_EVENT_STOP,
33 Clone = PTRACE_EVENT_CLONE,
34 Exec = PTRACE_EVENT_EXEC,
35 Exit = PTRACE_EVENT_EXIT,
36 Fork = PTRACE_EVENT_FORK,
37 VFork = PTRACE_EVENT_VFORK,
38 VForkDone = PTRACE_EVENT_VFORK_DONE,
39 Seccomp = PTRACE_EVENT_SECCOMP
40};
41
43{
46 int type{};
47
49 std::optional<int> exit_code;
50
52 std::optional<linux::Signal> signal_num;
53
55 std::optional<PtraceEvent> ptrace_event;
56
58 bool is_syscall_trap = false;
59
60 static Expected<TracedWaitid> waitid(idtype_t idtype, id_t id, int options = WSTOPPED | WEXITED) {
61 auto res = linux::waitid(idtype, id, options);
62
63 if (!res) {
64 return res.error();
65 }
66
67 return TracedWaitid::parse(res.value());
68 }
69
70 template <ChronoDuration Duration1, ChronoDuration Duration2 = std::chrono::microseconds>
71 static Result<TracedWaitid> wait_with_timeout(pid_t pid, Duration1 timeout,
72 Duration2 poll_period = std::chrono::microseconds{1}) {
73 using std::chrono::steady_clock;
74
75 ASSERT(timeout > poll_period);
76
77 const auto start_time = steady_clock::now();
78
79 // while elapsed time < timeout
80 while (steady_clock::now() - start_time < timeout) {
81 Expected<siginfo_t> waitid_res =
82 linux::waitid(P_PID, gsl::narrow_cast<id_t>(pid), WEXITED | WSTOPPED | WNOHANG);
83
84 ASSERT(waitid_res);
85
86 // si_pid will only be 0 if waitid returned early from WNOHANG
87 // see waitid(2)
88 if (waitid_res.value().si_pid != 0) {
89 return TracedWaitid::parse(waitid_res.value());
90 }
91
92 std::this_thread::sleep_for(poll_period);
93 }
94
95 LOG_DEBUG("waitid timed out at {}", timeout);
96 return ErrorKind::TimedOut;
97 }
98
99 constexpr static TracedWaitid parse(const siginfo_t& siginfo) {
100 TracedWaitid result{};
101 result.type = siginfo.si_code;
102
103 // Regarding ptrace(2) in conjunction with waitid(2). Firstly, for any ptrace event, a SIGTRAP will
104 // have been received. Then, one of the following will be seen:
105 //
106 // for ptrace(2) events:
107 // (status >> 8) == (SIGTRAP | (PTRACE_EVENT_* << 8))
108 //
109 // for system call traps:
110 // (status >> 8) == (SIGTRAP | 0x80)
111 //
112 // where (status >> 8) is the same as siginfo.si_code
113
114 if (result.type == CLD_EXITED) {
115 result.exit_code = gsl::narrow_cast<int>(siginfo.si_status);
116 return result;
117 }
118
119 constexpr u64 SIG_MASK = 0x7f;
120 constexpr u64 SYSCALL_TRAP_MASK = 0x80;
121 u32 signal_bits = gsl::narrow_cast<u32>(siginfo.si_status);
122
123 // actual signal will be in first 7 bits
124 result.signal_num = signal_bits & SIG_MASK;
125
126 // NOT for ptrace(2) for events/syscall traps
127 if (result.type != CLD_TRAPPED) {
128 return result;
129 }
130
131 if ((signal_bits & SYSCALL_TRAP_MASK) != 0) {
132 result.is_syscall_trap = true;
133 } else if ((signal_bits >> 8) != 0) {
134 result.ptrace_event = static_cast<PtraceEvent>(signal_bits >> 8);
135 }
136
137 return result;
138 }
139};
140
141} // namespace asmgrader
142
143template <>
145{
146 auto format(const ::asmgrader::PtraceEvent& from, format_context& ctx) const {
147 auto res = enum_to_str(from);
148
149 if (res) {
150 return format_to(ctx.out(), "{}", *res);
151 }
152
153 return format_to(ctx.out(), "<unknown ({})>", fmt::underlying(from));
154 }
155
156private:
157 static constexpr std::optional<const char*> enum_to_str(const ::asmgrader::PtraceEvent& from) {
158 using enum ::asmgrader::PtraceEvent;
159 switch (from) {
160 case Stop:
161 return "stop";
162 case Clone:
163 return "clone";
164 case Exec:
165 return "exec";
166 case Exit:
167 return "exit";
168 case Fork:
169 return "fork";
170 case VFork:
171 return "vfork";
172 case VForkDone:
173 return "vforkdone";
174 case Seccomp:
175 return "seccomp";
176 default:
177 return std::nullopt;
178 }
179 }
180};
181
182template <>
184{
185 auto format(const ::asmgrader::TracedWaitid& from, format_context& ctx) const {
186 auto type_to_str = [](int wait_type) -> std::string {
187 switch (wait_type) {
188 case CLD_KILLED:
189 return "KILLED";
190 case CLD_DUMPED:
191 return "DUMPED";
192 case CLD_STOPPED:
193 return "STOPPED";
194 case CLD_CONTINUED:
195 return "CONTINUED";
196 case CLD_TRAPPED:
197 return "TRAPPED";
198 case CLD_EXITED:
199 return "CONTINUED";
200 default:
201 return fmt::format("<unkown ({})>", wait_type);
202 }
203 };
204
205 return fmt::format_to(
206 ctx.out(), "TracedWaitid{{type={}, exit_code={}, signal={}, is_syscall_trap={}, ptrace_event={}}}",
207 type_to_str(from.type), from.exit_code, from.signal_num, from.is_syscall_trap, from.ptrace_event);
208 }
209};
std::variant wrapper for a partial implementation of C++23's expected type
Definition expected.hpp:34
constexpr U & value()
Definition expected.hpp:78
#define LOG_DEBUG(...)
Definition logging.hpp:44
Expected< siginfo_t > waitid(idtype_t idtype, id_t id, int options=WSTOPPED|WEXITED)
see waitid(2) returns success/failure; logs failure at debug level
Definition linux.hpp:257
Definition asm_buffer.hpp:19
PtraceEvent
Definition tracer_types.hpp:31
Definition debug.hpp:9
Definition tracer_types.hpp:43
static constexpr TracedWaitid parse(const siginfo_t &siginfo)
Definition tracer_types.hpp:99
bool is_syscall_trap
Whether a system call trape was delivered via ptrace.
Definition tracer_types.hpp:58
std::optional< int > exit_code
Has value if and only if type == CLD_EXITED.
Definition tracer_types.hpp:49
std::optional< PtraceEvent > ptrace_event
A PTRACE_EVENT_* value if an event was recieved.
Definition tracer_types.hpp:55
std::optional< linux::Signal > signal_num
Has value if type is not CLD_EXITED.
Definition tracer_types.hpp:52
int type
type is si_code in waitid(2). One of: CLD_EXITED, CLD_KILLED, CLD_DUMPED, CLD_STOPPED,...
Definition tracer_types.hpp:46
static Result< TracedWaitid > wait_with_timeout(pid_t pid, Duration1 timeout, Duration2 poll_period=std::chrono::microseconds{1})
Definition tracer_types.hpp:71
static Expected< TracedWaitid > waitid(idtype_t idtype, id_t id, int options=WSTOPPED|WEXITED)
Definition tracer_types.hpp:60
auto format(const ::asmgrader::PtraceEvent &from, format_context &ctx) const
Definition tracer_types.hpp:146
auto format(const ::asmgrader::TracedWaitid &from, format_context &ctx) const
Definition tracer_types.hpp:185