/
Event.h
365 lines (321 loc) · 10.8 KB
/
Event.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
/* -*- Mode: C++; tab-width: 8; c-basic-offset: 2; indent-tabs-mode: nil; -*- */
#ifndef RR_EVENT_H_
#define RR_EVENT_H_
#include <assert.h>
#include <ostream>
#include <stack>
#include <string>
#include "kernel_abi.h"
#include "Registers.h"
enum EventType {
EV_UNASSIGNED,
EV_SENTINEL,
// No associated data.
EV_EXIT,
// Tracee exited its sighandler. We leave this breadcrumb so
// that the popping of not-restarted syscall interruptions and
// sigreturns is replayed in the same order.
EV_EXIT_SIGHANDLER,
// Pretty self-explanatory: recording detected that an
// interrupted syscall wasn't restarted, so the interruption
// record can be popped off the tracee's event stack.
EV_INTERRUPTED_SYSCALL_NOT_RESTARTED,
// TODO: this is actually a pseudo-pseudosignal: it will never
// appear in a trace, but is only used to communicate between
// different parts of the recorder code that should be
// refactored to not have to do that.
EV_NOOP,
// Scheduling signal interrupted the trace.
EV_SCHED,
EV_SEGV_RDTSC,
EV_SYSCALLBUF_FLUSH,
EV_SYSCALLBUF_ABORT_COMMIT,
EV_SYSCALLBUF_RESET,
// Syscall was entered, the syscall instruction was patched, and the
// syscall was aborted. Resume execution at the patch.
EV_PATCH_SYSCALL,
// The trace was terminated before all tasks exited, most
// likely because the recorder was sent a terminating signal.
// There are no more trace frames coming, so the best thing to
// do is probably to shut down.
EV_TRACE_TERMINATION,
// Like USR_EXIT, but recorded when the task is in an
// "unstable" state in which we're not sure we can
// synchronously wait for it to "really finish".
EV_UNSTABLE_EXIT,
// Uses the .desched struct below.
EV_DESCHED,
// Use .signal.
EV_SIGNAL,
EV_SIGNAL_DELIVERY,
EV_SIGNAL_HANDLER,
// Use .syscall.
EV_SYSCALL,
EV_SYSCALL_INTERRUPTION,
EV_LAST
};
enum HasExecInfo {
NO_EXEC_INFO,
HAS_EXEC_INFO
};
/**
* An encoding of the relevant bits of |struct event| that can be
* cheaply and easily serialized.
*/
union EncodedEvent {
struct {
EventType type : 5;
bool is_syscall_entry : 1;
HasExecInfo has_exec_info : 1;
SupportedArch arch_ : 1;
int data : 24;
};
int encoded;
bool operator==(const EncodedEvent& other) const {
return encoded == other.encoded;
}
bool operator!=(const EncodedEvent& other) const { return !(*this == other); }
SupportedArch arch() const { return arch_; }
};
static_assert(sizeof(int) == sizeof(EncodedEvent), "Bit fields are messed up");
static_assert(EV_LAST < (1 << 5), "Allocate more bits to the |type| field");
/**
* Events are interesting occurrences during tracee execution which
* are relevant for replay. Most events correspond to tracee
* execution, but some (a subset of "pseudosigs") save actions that
* the *recorder* took on behalf of the tracee.
*/
struct BaseEvent {
/**
* Pass |HAS_EXEC_INFO| if the event is at a stable execution
* point that we'll reach during replay too.
*/
BaseEvent(HasExecInfo has_exec_info, SupportedArch arch)
: has_exec_info(has_exec_info), arch_(arch) {}
SupportedArch arch() const { return arch_; }
// When replaying an event is expected to leave the tracee in
// the same execution state as during replay, the event has
// meaningful execution info, and it should be recorded for
// checking. But some pseudosigs aren't recorded in the same
// tracee state they'll be replayed, so the tracee exeuction
// state isn't meaningful.
HasExecInfo has_exec_info;
SupportedArch arch_;
};
/**
* Desched events track the fact that a tracee's desched-event
* notification fired during a may-block buffered syscall, which rr
* interprets as the syscall actually blocking (for a potentially
* unbounded amount of time). After the syscall exits, rr advances
* the tracee to where the desched is "disarmed" by the tracee.
*/
enum DeschedState {
ARMING_DESCHED_EVENT,
IN_SYSCALL,
DISARMING_DESCHED_EVENT,
DISARMED_DESCHED_EVENT
};
struct DeschedEvent : public BaseEvent {
/** Desched of |rec|. */
DeschedEvent(const struct syscallbuf_record* rec, SupportedArch arch)
: BaseEvent(NO_EXEC_INFO, arch), rec(rec), state(IN_SYSCALL) {}
// Record of the syscall that was interrupted by a desched
// notification. It's legal to reference this memory /while
// the desched is being processed only/, because |t| is in the
// middle of a desched, which means it's successfully
// allocated (but not yet committed) this syscall record.
const struct syscallbuf_record* rec;
DeschedState state;
};
/**
* Signal events track signals through the delivery phase, and if the
* signal finds a sighandler, on to the end of the handling face.
*/
enum SignalDeterministic {
NONDETERMINISTIC_SIG = 0,
DETERMINISTIC_SIG = 1
};
struct SignalEvent : public BaseEvent {
/**
* Signal |signo| is the signum, and |deterministic| is true
* for deterministically-delivered signals (see
* record_signal.cc).
*/
SignalEvent(const siginfo_t& siginfo, SignalDeterministic deterministic,
SupportedArch arch)
: BaseEvent(HAS_EXEC_INFO, arch),
siginfo(siginfo),
deterministic(deterministic) {}
SignalEvent(int signo, SignalDeterministic deterministic, SupportedArch arch)
: BaseEvent(HAS_EXEC_INFO, arch), deterministic(deterministic) {
memset(&siginfo, 0, sizeof(siginfo));
siginfo.si_signo = signo;
}
// Signal info
siginfo_t siginfo;
// True if this signal will be deterministically raised as the
// side effect of retiring an instruction during replay, for
// example |load $r 0x0| deterministically raises SIGSEGV.
SignalDeterministic deterministic;
};
/**
* Syscall events track syscalls through entry into the kernel,
* processing in the kernel, and exit from the kernel.
*
* This also models interrupted syscalls. During recording, only
* descheduled buffered syscalls /push/ syscall interruptions; all
* others are detected at exit time and transformed into syscall
* interruptions from the original, normal syscalls.
*
* During replay, we push interruptions to know when we need
* to emulate syscall entry, since the kernel won't have set
* things up for the tracee to restart on its own.
*/
enum SyscallState {
NO_SYSCALL,
ENTERING_SYSCALL,
PROCESSING_SYSCALL,
EXITING_SYSCALL
};
struct SyscallEvent : public BaseEvent {
/** Syscall |syscallno| is the syscall number. */
SyscallEvent(int syscallno, SupportedArch arch)
: BaseEvent(HAS_EXEC_INFO, arch),
regs(arch),
desched_rec(nullptr),
state(NO_SYSCALL),
number(syscallno),
is_restart(false) {}
// The original (before scratch is set up) arguments to the
// syscall passed by the tracee. These are used to detect
// restarted syscalls.
Registers regs;
// If this is a descheduled buffered syscall, points at the
// record for that syscall.
const struct syscallbuf_record* desched_rec;
SyscallState state;
// Syscall number.
int number;
// Nonzero when this syscall was restarted after a signal
// interruption.
bool is_restart;
};
struct syscall_interruption_t {};
static const syscall_interruption_t interrupted;
/**
* Sum type for all events (well, a C++ approximation thereof). An
* Event always has a definted EventType. It can be down-casted to
* one of the leaf types above iff the type tag is correct.
*/
struct Event {
Event() : event_type(EV_UNASSIGNED) {}
Event(EventType type, HasExecInfo info, SupportedArch arch)
: event_type(type), base(info, arch) {}
Event(const DeschedEvent& ev) : event_type(EV_DESCHED), desched(ev) {}
Event(const SignalEvent& ev) : event_type(EV_SIGNAL), signal(ev) {}
Event(const SyscallEvent& ev) : event_type(EV_SYSCALL), syscall(ev) {}
Event(const syscall_interruption_t&, const SyscallEvent& ev)
: event_type(EV_SYSCALL_INTERRUPTION), syscall(ev) {}
/**
* Re-construct this from an encoding created by
* |Event::encode()|.
*/
Event(EncodedEvent e);
Event(const Event& o);
~Event();
Event& operator=(const Event& o);
// Events can always be cased to BaseEvent regardless of the
// current concrete type, because all constituent types
// inherit from BaseEvent.
BaseEvent& Base() { return base; }
const BaseEvent& Base() const { return base; }
DeschedEvent& Desched() {
assert(EV_DESCHED == event_type);
return desched;
}
const DeschedEvent& Desched() const {
assert(EV_DESCHED == event_type);
return desched;
}
SignalEvent& Signal() {
assert(is_signal_event());
return signal;
}
const SignalEvent& Signal() const {
assert(is_signal_event());
return signal;
}
SyscallEvent& Syscall() {
assert(is_syscall_event());
return syscall;
}
const SyscallEvent& Syscall() const {
assert(is_syscall_event());
return syscall;
}
enum {
// Deterministic signals are encoded as (signum | DET_SIGNAL_BIT).
DET_SIGNAL_BIT = 0x80
};
/**
* Return an encoding of this event that can be cheaply
* serialized. The encoding is lossy.
*/
EncodedEvent encode() const;
/**
* Return true if a tracee at this event has meaningful
* execution info (registers etc.) that rr should record.
* "Meaningful" means that the same state will be seen when
* reaching this event during replay.
*/
HasExecInfo record_exec_info() const;
HasExecInfo has_exec_info() const { return base.has_exec_info; }
bool has_ticks_slop() const;
/**
* Return true if this is one of the indicated type of events.
*/
bool is_signal_event() const;
bool is_syscall_event() const;
/**
* Dump info about this to INFO log.
*
* Note: usually you want to use |LOG(info) << event;|.
*/
void log() const;
/** Return a string describing this. */
std::string str() const;
/**
* Dynamically change the type of this. Only a small number
* of type changes are allowed.
*/
void transform(EventType new_type);
/** Return the current type of this. */
EventType type() const { return event_type; }
/** Return the architecture associated with this. */
SupportedArch arch() const { return base.arch(); }
/** Change the architecture for this event. */
void set_arch(SupportedArch a) { base.arch_ = a; }
/** Return a string naming |ev|'s type. */
std::string type_name() const;
/** Return an event of type EV_NOOP. */
static Event noop(SupportedArch arch) {
return Event(EV_NOOP, NO_EXEC_INFO, arch);
}
private:
EventType event_type;
union {
BaseEvent base;
DeschedEvent desched;
SignalEvent signal;
SyscallEvent syscall;
};
};
inline static std::ostream& operator<<(std::ostream& o, const Event& ev) {
return o << ev.str();
}
inline static std::ostream& operator<<(std::ostream& o,
const EncodedEvent& ev) {
return o << Event(ev);
}
const char* state_name(SyscallState state);
#endif // EVENT_H_