forked from chandlerc/rr
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathRecordSession.h
188 lines (157 loc) · 6.41 KB
/
RecordSession.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
/* -*- Mode: C++; tab-width: 8; c-basic-offset: 2; indent-tabs-mode: nil; -*- */
#ifndef RR_RECORD_SESSION_H_
#define RR_RECORD_SESSION_H_
#include <string>
#include <vector>
#include "Scheduler.h"
#include "SeccompFilterRewriter.h"
#include "Session.h"
#include "TaskGroup.h"
#include "TraceFrame.h"
#include "WaitStatus.h"
namespace rr {
class RecordTask;
/** Encapsulates additional session state related to recording. */
class RecordSession : public Session {
public:
typedef std::shared_ptr<RecordSession> shr_ptr;
/**
* Create a recording session for the initial command line |argv|.
*/
enum SyscallBuffering { ENABLE_SYSCALL_BUF, DISABLE_SYSCALL_BUF };
/**
* BIND_CPU means binding to a randomly chosen CPU.
* UNBOUND_CPU means not binding to a particular CPU.
* A non-negative value means binding to the specific CPU number.
*/
enum BindCPU { BIND_CPU = -2, UNBOUND_CPU = -1 };
static shr_ptr create(
const std::vector<std::string>& argv,
const std::vector<std::string>& extra_env = std::vector<std::string>(),
SyscallBuffering syscallbuf = ENABLE_SYSCALL_BUF,
int bind_cpu = BIND_CPU);
bool use_syscall_buffer() const { return use_syscall_buffer_; }
size_t syscall_buffer_size() const { return syscall_buffer_size_; }
bool use_read_cloning() const { return use_read_cloning_; }
bool use_file_cloning() const { return use_file_cloning_; }
void set_ignore_sig(int sig) { ignore_sig = sig; }
int get_ignore_sig() const { return ignore_sig; }
void set_continue_through_sig(int sig) { continue_through_sig = sig; }
int get_continue_through_sig() const { return continue_through_sig; }
enum RecordStatus {
// Some execution was recorded. record_step() can be called again.
STEP_CONTINUE,
// All tracees are dead. record_step() should not be called again.
STEP_EXITED,
// Spawning the initial tracee failed. An error message will be in
// failure_message.
STEP_SPAWN_FAILED
};
struct RecordResult {
RecordStatus status;
// When status == STEP_EXITED
WaitStatus exit_status;
// When status == STEP_SPAWN_FAILED
std::string failure_message;
};
/**
* Record some tracee execution.
* This may block. If blocking is interrupted by a signal, will return
* STEP_CONTINUE.
* Typically you'd call this in a loop until it returns something other than
* STEP_CONTINUE.
* Note that when this returns, some tasks may be running (not in a ptrace-
* stop). In particular, up to one task may be executing user code and any
* number of tasks may be blocked in syscalls.
*/
RecordResult record_step();
/**
* Flush buffers and write a termination record to the trace. Don't call
* record_step() after this.
*/
void terminate_recording();
virtual RecordSession* as_record() { return this; }
TraceWriter& trace_writer() { return trace_out; }
virtual void on_destroy(Task* t);
Scheduler& scheduler() { return scheduler_; }
SeccompFilterRewriter& seccomp_filter_rewriter() {
return seccomp_filter_rewriter_;
}
enum ContinueType { DONT_CONTINUE = 0, CONTINUE, CONTINUE_SYSCALL };
struct StepState {
// Continue with this continuation type.
ContinueType continue_type;
StepState(ContinueType continue_type) : continue_type(continue_type) {}
};
void set_enable_chaos(bool enable_chaos) {
scheduler().set_enable_chaos(enable_chaos);
this->enable_chaos_ = enable_chaos;
}
bool enable_chaos() const { return enable_chaos_; }
void set_use_read_cloning(bool enable) { use_read_cloning_ = enable; }
void set_use_file_cloning(bool enable) { use_file_cloning_ = enable; }
void set_syscall_buffer_size(size_t size) { syscall_buffer_size_ = size; }
void set_wait_for_all(bool wait_for_all) {
this->wait_for_all_ = wait_for_all;
}
virtual Task* new_task(pid_t tid, pid_t rec_tid, uint32_t serial,
SupportedArch a);
RecordTask* find_task(pid_t rec_tid) const;
RecordTask* find_task(const TaskUid& tuid) const;
/**
* This gets called when we detect that a task has been revived from the
* dead with a PTRACE_EVENT_EXEC. See ptrace man page under "execve(2) under
* ptrace" for the horrid details.
*
* The task in the task-group that triggered the successful execve has changed
* its tid to |rec_tid|. We mirror that, and emit TraceTaskEvents to make it
* look like a new task was spawned and the old task exited.
*/
RecordTask* revive_task_for_exec(pid_t rec_tid);
private:
RecordSession(const std::string& exe_path,
const std::vector<std::string>& argv,
const std::vector<std::string>& envp,
SyscallBuffering syscallbuf, int bind_cpu);
virtual void on_create(Task* t);
void handle_seccomp_traced_syscall(RecordTask* t,
RecordSession::StepState* step_state,
RecordResult* result,
bool* did_enter_syscall);
void process_syscall_entry(RecordTask* t, StepState* step_state,
RecordResult* step_result);
void check_initial_task_syscalls(RecordTask* t, RecordResult* step_result);
bool handle_ptrace_event(RecordTask* t, StepState* step_state,
RecordResult* result, bool* did_enter_syscall);
bool handle_signal_event(RecordTask* t, StepState* step_state);
void runnable_state_changed(RecordTask* t, StepState* step_state,
RecordResult* step_result,
bool can_consume_wait_status);
void signal_state_changed(RecordTask* t, StepState* step_state);
void syscall_state_changed(RecordTask* t, StepState* step_state);
void desched_state_changed(RecordTask* t);
bool prepare_to_inject_signal(RecordTask* t, StepState* step_state);
void task_continue(const StepState& step_state);
bool can_end();
TraceWriter trace_out;
Scheduler scheduler_;
TaskGroup::shr_ptr initial_task_group;
SeccompFilterRewriter seccomp_filter_rewriter_;
int ignore_sig;
int continue_through_sig;
Switchable last_task_switchable;
size_t syscall_buffer_size_;
bool use_syscall_buffer_;
bool use_file_cloning_;
bool use_read_cloning_;
/**
* When true, try to increase the probability of finding bugs.
*/
bool enable_chaos_;
/**
* When true, wait for all tracees to exit before finishing recording.
*/
bool wait_for_all_;
};
} // namespace rr
#endif // RR_RECORD_SESSION_H_