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 guard_unexpected_signal(struct context* ctx) { int event; /* "0" normally means "syscall", but continue_or_step() guards * against unexpected syscalls. So the caller must have set * "0" intentionally. */ if (0 == ctx->child_sig || SIGTRAP == ctx->child_sig) { return; } if (ctx->child_sig) { event = -ctx->child_sig; } else { struct user_regs_struct regs; read_child_registers(ctx->child_tid, ®s); event = MAX(0, regs.orig_eax); } log_err("Replay got unrecorded event %s while awaiting signal\n" " replaying trace line %d", strevent(event), get_trace_file_lines_counter()); emergency_debug(ctx); /* not reached */ }
void print_info(int fd) { char buf[100]; int version; struct input_id id; char evtype[(EV_MAX+7)/8]; int i; if(ioctl(fd, EVIOCGNAME(100), buf) == -1) { if(errno == ENOTTY) // sim return; perror("Failed to get device name"); } else printf("Device: %s\n", buf); if(ioctl(fd, EVIOCGID, &id) == -1) perror("Failed to get device id"); else { switch(id.bustype) { case BUS_PCI: printf("Bustype PCI "); break; case BUS_ISAPNP: printf("Bustype ISAPNP "); break; case BUS_USB: printf("Bustype USB "); break; case BUS_HIL: printf("Bustype HIL "); break; case BUS_BLUETOOTH: printf("Bustype BLUETOOTH "); break; case BUS_VIRTUAL: printf("Bustype VIRTUAL "); break; case BUS_ISA: printf("Bustype ISA "); break; case BUS_I8042: printf("Bustype I8042 "); break; case BUS_XTKBD: printf("Bustype XTKBD "); break; case BUS_RS232: printf("Bustype RS232 "); break; case BUS_GAMEPORT: printf("Bustype GAMEPORT "); break; case BUS_PARPORT: printf("Bustype PARPORT "); break; case BUS_AMIGA: printf("Bustype AMIGA "); break; case BUS_ADB: printf("Bustype ADB "); break; case BUS_I2C: printf("Bustype I2C "); break; case BUS_HOST: printf("Bustype HOST "); break; case BUS_GSC: printf("Bustype GSC "); break; case BUS_ATARI: printf("Bustype ATARI "); break; case BUS_SPI: printf("Bustype SPI "); break; default: printf("Bustype unknown(%04hx) ", id.bustype); } printf("Vendor %04hx ", id.vendor); printf("Product %04hx ", id.product); printf("Version %04hx\n", id.version); } if(ioctl(fd, EVIOCGVERSION, &version) == -1) perror("Failed to get driver version"); else printf("Driver: %i.%i.%i\n", version>>16, (version>>8) & 0xff, version & 0xff); if(ioctl(fd, EVIOCGBIT(0, EV_MAX), &evtype) == -1) perror("Failed to get event types"); else { printf("Events: "); for(i=0; i<EV_MAX; i++) if(evtype[i/8] & (1<<(i%8))) printf("%s ", strevent(i)); printf("\n"); } }
static void replay_one_trace_frame(struct dbg_context* dbg, struct context* ctx) { struct dbg_request req; struct rep_trace_step step; int event = ctx->trace.stop_reason; int stop_sig = 0; debug("%d: replaying event %s, state %s", ctx->rec_tid, strevent(event), statename(ctx->trace.state)); if (ctx->syscallbuf_hdr) { debug(" (syscllbufsz:%u, abrtcmt:%u)", ctx->syscallbuf_hdr->num_rec_bytes, ctx->syscallbuf_hdr->abort_commit); } /* Advance the trace until we've exec()'d the tracee before * processing debugger requests. Otherwise the debugger host * will be confused about the initial executable image, * rr's. */ if (validate) { req = process_debugger_requests(dbg, ctx); assert(dbg_is_resume_request(&req)); } /* print some kind of progress */ if (ctx->trace.global_time % 10000 == 0) { fprintf(stderr, "time: %u\n",ctx->trace.global_time); } if (ctx->child_sig != 0) { assert(event == -ctx->child_sig || event == -(ctx->child_sig | DET_SIGNAL_BIT)); ctx->child_sig = 0; } /* Ask the trace-interpretation code what to do next in order * to retire the current frame. */ memset(&step, 0, sizeof(step)); switch (event) { case USR_INIT_SCRATCH_MEM: { /* for checksumming: make a note that this area is * scratch and need not be validated. */ struct mmapped_file file; read_next_mmapped_file_stats(&file); replay_init_scratch_memory(ctx, &file); add_scratch((void*)ctx->trace.recorded_regs.eax, file.end - file.start); step.action = TSTEP_RETIRE; break; } case USR_EXIT: rep_sched_deregister_thread(&ctx); /* Early-return because |ctx| is gone now. */ return; case USR_ARM_DESCHED: case USR_DISARM_DESCHED: rep_skip_desched_ioctl(ctx); /* TODO */ step.action = TSTEP_RETIRE; break; case USR_SYSCALLBUF_ABORT_COMMIT: ctx->syscallbuf_hdr->abort_commit = 1; step.action = TSTEP_RETIRE; break; case USR_SYSCALLBUF_FLUSH: rep_process_flush(ctx, rr_flags->redirect); /* TODO */ step.action = TSTEP_RETIRE; break; case USR_SYSCALLBUF_RESET: ctx->syscallbuf_hdr->num_rec_bytes = 0; step.action = TSTEP_RETIRE; break; case USR_SCHED: step.action = TSTEP_PROGRAM_ASYNC_SIGNAL_INTERRUPT; step.target.rcb = ctx->trace.rbc; step.target.regs = &ctx->trace.recorded_regs; step.target.signo = 0; break; case SIG_SEGV_RDTSC: step.action = TSTEP_DETERMINISTIC_SIGNAL; step.signo = SIGSEGV; break; default: /* Pseudosignals are handled above. */ assert(event > LAST_RR_PSEUDOSIGNAL); if (FIRST_DET_SIGNAL <= event && event <= LAST_DET_SIGNAL) { step.action = TSTEP_DETERMINISTIC_SIGNAL; step.signo = (-event & ~DET_SIGNAL_BIT); stop_sig = step.signo; } else if (event < 0) { assert(FIRST_ASYNC_SIGNAL <= event && event <= LAST_ASYNC_SIGNAL); step.action = TSTEP_PROGRAM_ASYNC_SIGNAL_INTERRUPT; step.target.rcb = ctx->trace.rbc; step.target.regs = &ctx->trace.recorded_regs; step.target.signo = -event; stop_sig = step.target.signo; } else { assert(event > 0); /* XXX not so pretty ... */ validate |= (ctx->trace.state == STATE_SYSCALL_EXIT && event == SYS_execve); rep_process_syscall(ctx, rr_flags->redirect, &step); } } /* See the comment below about *not* resetting the hpc for * buffer flushes. Here, we're processing the *other* event, * just after the buffer flush, where the rcb matters. To * simplify the advance-to-target code that follows (namely, * making debugger interrupts simpler), pretend like the * execution in the BUFFER_FLUSH didn't happen by resetting * the rbc and compensating down the target rcb. */ if (TSTEP_PROGRAM_ASYNC_SIGNAL_INTERRUPT == step.action) { uint64_t rcb_now = read_rbc(ctx->hpc); assert(step.target.rcb >= rcb_now); step.target.rcb -= rcb_now; reset_hpc(ctx, 0); } /* Advance until |step| has been fulfilled. */ while (try_one_trace_step(ctx, &step, &req)) { struct user_regs_struct regs; /* Currently we only understand software breakpoints * and successful stepi's. */ assert(SIGTRAP == ctx->child_sig && "Unknown trap"); read_child_registers(ctx->child_tid, ®s); if (ip_is_breakpoint((void*)regs.eip)) { /* SW breakpoint: $ip is just past the * breakpoint instruction. Move $ip back * right before it. */ regs.eip -= sizeof(int_3_insn); write_child_registers(ctx->child_tid, ®s); } else { /* Successful stepi. Nothing else to do. */ assert(DREQ_STEP == req.type && req.target == get_threadid(ctx)); } /* Don't restart with SIGTRAP anywhere. */ ctx->child_sig = 0; /* Notify the debugger and process any new requests * that might have triggered before resuming. */ dbg_notify_stop(dbg, get_threadid(ctx), 0x05/*gdb mandate*/); req = process_debugger_requests(dbg, ctx); assert(dbg_is_resume_request(&req)); } if (dbg && stop_sig) { dbg_notify_stop(dbg, get_threadid(ctx), stop_sig); } /* We flush the syscallbuf in response to detecting *other* * events, like signal delivery. Flushing the syscallbuf is a * sort of side-effect of reaching the other event. But once * we've flushed the syscallbuf during replay, we still must * reach the execution point of the *other* event. For async * signals, that requires us to have an "intact" rbc, with the * same value as it was when the last buffered syscall was * retired during replay. We'll be continuing from that rcb * to reach the rcb we recorded at signal delivery. So don't * reset the counter for buffer flushes. (It doesn't matter * for non-async-signal types, which are deterministic.) */ switch (ctx->trace.stop_reason) { case USR_SYSCALLBUF_ABORT_COMMIT: case USR_SYSCALLBUF_FLUSH: case USR_SYSCALLBUF_RESET: break; default: reset_hpc(ctx, 0); } debug_memory(ctx); }
/** * Retrieves a thread from the pool of active threads in a * round-robin fashion. */ struct task* rec_sched_get_active_thread(struct task* t, int* by_waitpid) { int max_events = rr_flags()->max_events; struct tasklist_entry* entry = current_entry; struct task* next_t = NULL; debug("Scheduling next task"); *by_waitpid = 0; if (!entry) { entry = current_entry = CIRCLEQ_FIRST(&head); } if (t && !t->switchable) { debug(" (%d is un-switchable)", t->tid); if (task_may_be_blocked(t)) { debug(" and not runnable; waiting for state change"); /* |t| is un-switchable, but not runnable in * this state. Wait for it to change state * before "scheduling it", so avoid * busy-waiting with our client. */ #ifdef MONITOR_UNSWITCHABLE_WAITS double start = now_sec(), wait_duration; #endif sys_waitpid(t->tid, &t->status); #ifdef MONITOR_UNSWITCHABLE_WAITS wait_duration = now_sec() - start; if (wait_duration >= 0.010) { warn("Waiting for unswitchable %s took %g ms", strevent(t->event), 1000.0 * wait_duration); } #endif *by_waitpid = 1; debug(" new status is 0x%x", t->status); } return t; } /* Prefer switching to the next task if the current one * exceeded its event limit. */ if (t && t->succ_event_counter > max_events) { debug(" previous task exceeded event limit, preferring next"); entry = current_entry = next_entry(entry); t->succ_event_counter = 0; } /* Go around the task list exactly one time looking for a * runnable thread. */ do { pid_t tid; next_t = &entry->t; tid = next_t->tid; if (next_t->unstable) { debug(" %d is unstable, going to waitpid(-1)", tid); next_t = NULL; break; } if (!task_may_be_blocked(next_t)) { debug(" %d isn't blocked, done", tid); break; } debug(" %d is blocked on %s, checking status ...", tid, strevent(next_t->event)); if (0 != sys_waitpid_nonblock(tid, &next_t->status)) { *by_waitpid = 1; debug(" ready with status 0x%x", next_t->status); break; } debug(" still blocked"); } while (next_t = NULL, current_entry != (entry = next_entry(entry))); if (!next_t) { /* All the tasks are blocked. Wait for the next one to * change state. */ int status; pid_t tid; debug(" all tasks blocked or some unstable, waiting for runnable (%d total)", num_active_threads); while (-1 == (tid = waitpid(-1, &status, __WALL | WSTOPPED | WUNTRACED))) { if (EINTR == errno) { debug(" waitpid() interrupted by EINTR"); continue; } fatal("Failed to waitpid()"); } debug(" %d changed state", tid); entry = get_entry(tid); next_t = &entry->t; assert(next_t->unstable || task_may_be_blocked(next_t)); next_t->status = status; *by_waitpid = 1; } current_entry = entry; note_switch(t, next_t, max_events); return next_t; }