Skip to content

Commit cfdad9a

Browse files
committed
Added created_task member to ReplayResult
This member reflects that a new task was created so that a debugger/debugger-frontend can reliably tell whether or not a new task or task group has been created via clone/v/fork syscalls. Because waiting for exit syscalls in the replay trace is not enough, since the exit replay frame can come long after the spawned task has already started replaying its steps. This member of ReplayResult is passed down to the appropriate place where we create a new task, it does mean we have to change a few function signatures unfortunately.
1 parent 4a5e4c8 commit cfdad9a

File tree

4 files changed

+25
-13
lines changed

4 files changed

+25
-13
lines changed

src/ReplaySession.cc

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -668,7 +668,8 @@ static void emulate_syscall_entry(ReplayTask* t, const TraceFrame& frame,
668668
* occurred.
669669
*/
670670
Completion ReplaySession::enter_syscall(ReplayTask* t,
671-
const StepConstraints& constraints) {
671+
const StepConstraints& constraints,
672+
std::optional<pid_t>& task_created) {
672673
if (t->regs().matches(trace_frame.regs()) &&
673674
t->tick_count() == trace_frame.ticks()) {
674675
// We already entered the syscall via an ENTERING_SYSCALL_PTRACE
@@ -737,7 +738,7 @@ Completion ReplaySession::enter_syscall(ReplayTask* t,
737738
}
738739

739740
if (current_trace_frame().event().Syscall().state == ENTERING_SYSCALL) {
740-
rep_after_enter_syscall(t);
741+
rep_after_enter_syscall(t, task_created);
741742
}
742743
return COMPLETE;
743744
}
@@ -1708,7 +1709,8 @@ Completion ReplaySession::advance_to_ticks_target(
17081709
* more work.
17091710
*/
17101711
Completion ReplaySession::try_one_trace_step(
1711-
ReplayTask* t, const StepConstraints& constraints) {
1712+
ReplayTask* t, const StepConstraints& constraints,
1713+
std::optional<pid_t>& task_created) {
17121714
if (constraints.ticks_target > 0 && !trace_frame.event().has_ticks_slop() &&
17131715
current_trace_frame().ticks() > constraints.ticks_target) {
17141716
// Instead of doing this step, just advance to the ticks_target, since
@@ -1723,7 +1725,7 @@ Completion ReplaySession::try_one_trace_step(
17231725
case TSTEP_RETIRE:
17241726
return COMPLETE;
17251727
case TSTEP_ENTER_SYSCALL:
1726-
return enter_syscall(t, constraints);
1728+
return enter_syscall(t, constraints, task_created);
17271729
case TSTEP_EXIT_SYSCALL:
17281730
return exit_syscall(t);
17291731
case TSTEP_DETERMINISTIC_SIGNAL:
@@ -2056,7 +2058,7 @@ ReplayResult ReplaySession::replay_step(const StepConstraints& constraints) {
20562058
result.break_status.task_context = TaskContext(t);
20572059

20582060
/* Advance towards fulfilling |current_step|. */
2059-
Completion complete = try_one_trace_step(t, constraints);
2061+
Completion complete = try_one_trace_step(t, constraints, result.created_task);
20602062
if (detected_transient_error_) {
20612063
result.status = REPLAY_TRANSIENT_ERROR;
20622064
return result;

src/ReplaySession.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,10 @@ struct ReplayResult {
126126
// one iteration to go. did_fast_forward may be false in this case if the
127127
// instruction executes exactly twice.
128128
bool incomplete_fast_forward;
129+
130+
// If this replay step creates a new task, we need to be able to report it
131+
// reliably to a frontend consuming rr.
132+
std::optional<pid_t> created_task{ std::nullopt };
129133
};
130134

131135
/**
@@ -392,10 +396,12 @@ class ReplaySession final : public Session {
392396
void advance_to_next_trace_frame();
393397
Completion emulate_signal_delivery(ReplayTask* oldtask);
394398
Completion try_one_trace_step(ReplayTask* t,
395-
const StepConstraints& step_constraints);
399+
const StepConstraints& step_constraints,
400+
std::optional<pid_t>& task_created);
396401
Completion cont_syscall_boundary(ReplayTask* t,
397402
const StepConstraints& constraints);
398-
Completion enter_syscall(ReplayTask* t, const StepConstraints& constraints);
403+
Completion enter_syscall(ReplayTask* t, const StepConstraints& constraints,
404+
std::optional<pid_t>& task_created);
399405
Completion exit_syscall(ReplayTask* t);
400406
Completion exit_task(ReplayTask* t);
401407
bool handle_unrecorded_cpuid_fault(ReplayTask* t,

src/replay_syscall.cc

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,8 @@ static bool syscall_shares_vm(Registers r)
150150
}
151151
}
152152

153-
template <typename Arch> static void prepare_clone(ReplayTask* t) {
153+
template <typename Arch>
154+
static void prepare_clone(ReplayTask* t, std::optional<pid_t>& task_created) {
154155
const TraceFrame& trace_frame = t->current_trace_frame();
155156

156157
// We're being called with the syscall entry event, so we can't inspect the result
@@ -267,6 +268,7 @@ template <typename Arch> static void prepare_clone(ReplayTask* t) {
267268
t->session().clone(t, clone_flags_to_task_flags(flags), params.stack,
268269
params.tls, params.ctid, new_tid, rec_tid));
269270
new_task->own_namespace_rec_tid = tte.own_ns_tid();
271+
task_created = new_tid;
270272

271273
if (Arch::clone == t->regs().original_syscallno()) {
272274
/* FIXME: what if registers are non-null and contain an
@@ -968,7 +970,8 @@ static void process_init_buffers(ReplayTask* t, ReplayTraceStep* step) {
968970
static int non_negative_syscall(int sys) { return sys < 0 ? INT32_MAX : sys; }
969971

970972
template <typename Arch>
971-
static void rep_after_enter_syscall_arch(ReplayTask* t) {
973+
static void rep_after_enter_syscall_arch(ReplayTask* t,
974+
std::optional<pid_t>& task_created) {
972975
switch (non_negative_syscall(t->regs().original_syscallno())) {
973976
case Arch::write:
974977
case Arch::writev: {
@@ -981,7 +984,7 @@ static void rep_after_enter_syscall_arch(ReplayTask* t) {
981984
case Arch::fork:
982985
// Create the new task now. It needs to exist before clone/fork/vfork
983986
// returns so that a ptracer can touch it during PTRACE_EVENT handling.
984-
prepare_clone<Arch>(t);
987+
prepare_clone<Arch>(t, task_created);
985988
break;
986989

987990
case Arch::ptrace: {
@@ -1036,8 +1039,9 @@ static void rep_after_enter_syscall_arch(ReplayTask* t) {
10361039
t->apply_all_data_records_from_trace();
10371040
}
10381041

1039-
void rep_after_enter_syscall(ReplayTask* t) {
1040-
RR_ARCH_FUNCTION(rep_after_enter_syscall_arch, t->arch(), t)
1042+
void rep_after_enter_syscall(ReplayTask* t,
1043+
std::optional<pid_t>& task_created) {
1044+
RR_ARCH_FUNCTION(rep_after_enter_syscall_arch, t->arch(), t, task_created)
10411045
}
10421046

10431047
void rep_prepare_run_to_syscall(ReplayTask* t, ReplayTraceStep* step) {

src/replay_syscall.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ void rep_prepare_run_to_syscall(ReplayTask* t, ReplayTraceStep* step);
1919
/**
2020
* Call this when |t| has just entered a syscall.
2121
*/
22-
void rep_after_enter_syscall(ReplayTask* t);
22+
void rep_after_enter_syscall(ReplayTask* t, std::optional<pid_t>& task_created);
2323

2424
/**
2525
* Process pending syscall. Call this when |t| is about to exit a syscall.

0 commit comments

Comments
 (0)