/** * Continue until reaching either the "entry" of an emulated syscall, * or the entry or exit of an executed syscall. |emu| is nonzero when * we're emulating the syscall. Return 0 when the next syscall * boundary is reached, or nonzero if advancing to the boundary was * interrupted by an unknown trap. */ static int cont_syscall_boundary(struct context* ctx, int emu, int stepi) { pid_t tid = ctx->child_tid; if (emu && stepi) { sys_ptrace_sysemu_singlestep(tid); } else if (emu) { sys_ptrace_sysemu(tid); } else if (stepi) { sys_ptrace_singlestep(tid); } else { sys_ptrace_syscall(tid); } sys_waitpid(tid, &ctx->status); switch ((ctx->child_sig = signal_pending(ctx->status))) { case 0: break; case SIGCHLD: /* SIGCHLD is pending, do not deliver it, wait for it * to appear in the trace SIGCHLD is the only signal * that should ever be generated as all other signals * are emulated! */ return cont_syscall_boundary(ctx, emu, stepi); case SIGTRAP: return 1; default: log_err("Replay got unrecorded signal %d", ctx->child_sig); emergency_debug(ctx); } assert(ctx->child_sig == 0); return 0; }
static void continue_or_step(struct context* ctx, int stepi) { pid_t tid = ctx->child_tid; if (stepi) { sys_ptrace_singlestep(tid); } else { /* We continue with PTRACE_SYSCALL for error checking: * since the next event is supposed to be a signal, * entering a syscall here means divergence. There * shouldn't be any straight-line execution overhead * for SYSCALL vs. CONT, so the difference in cost * should be neglible. */ sys_ptrace_syscall(tid); } sys_waitpid(tid, &ctx->status); ctx->child_sig = signal_pending(ctx->status); if (0 == ctx->child_sig) { struct user_regs_struct regs; read_child_registers(ctx->child_tid, ®s); log_err("Replaying `%s' (line %d): expecting tracee signal or trap, but instead at `%s' (rcb: %llu)", strevent(ctx->trace.stop_reason), get_trace_file_lines_counter(), strevent(regs.orig_eax), read_rbc(ctx->hpc)); emergency_debug(ctx); } }
static void singlestep(struct context *ctx, int sig, int expected_val) { sys_ptrace_singlestep(ctx->child_tid, sig); sys_waitpid(ctx->child_tid, &ctx->status); /* we get a simple SIGTRAP in this case */ if (ctx->status != expected_val) { printf("status %x expected %x\n", ctx->status, expected_val); } assert(ctx->status == expected_val); ctx->status = 0; ctx->child_sig = 0; }