コード例 #1
0
ファイル: replayer.c プロジェクト: smillaedler/rr
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, &regs);

		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);
	}
}
コード例 #2
0
ファイル: replayer.c プロジェクト: smillaedler/rr
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, &regs);
		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 */
}
コード例 #3
0
ファイル: iod.c プロジェクト: Yomin/neobox
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");
    }
}
コード例 #4
0
ファイル: replayer.c プロジェクト: smillaedler/rr
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, &regs);
		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, &regs);
		} 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);
}
コード例 #5
0
ファイル: rec_sched.c プロジェクト: wisicn/rr
/**
 * 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;
}