int main(int argc, char **argv) { char *endptr; unsigned long nsteps = 1000000; VMState *state; bool hit_bp = false; #ifdef WITH_DIFF VMStateDiff *diff; #endif if (argc <= 1) { printf("Using a default of %lu steps\n", nsteps); } else { nsteps = strtoul(argv[1], &endptr, 0); if (errno) { perror("Invalid command line argument."); return 1; } if (*endptr) fprintf(stderr, "Warning: junk at end of number.\n"); } check_error(state = vm_newstate_no_code(VM_POLICY_INTERRUPT_ALWAYS)); put_code(state); #ifdef WITH_DIFF check_error(diff = vm_newdiff()); if (!vm_step(state, nsteps, diff, &hit_bp)) goto error; #else if (!vm_step(state, nsteps, NULL, &hit_bp)) goto error; #endif return EXIT_SUCCESS; error: fputs(vm_strerror(-1), stderr); fputc('\n', stderr); return EXIT_FAILURE; }
bool vm_step(VMState *state, int nsteps, VMStateDiff *diff, bool *hit_bp) { Opcode *opcode; opcode_handler *handler; VMStateDiff *newdiff; VMIterable *interrupt_item, *previous_interrupt_item = NULL; *hit_bp = false; while (nsteps > 0) { /* acquire and release for every step. This allows for some nice contention! */ ACQUIRE_STATE(state); if (state->break_async) { *hit_bp = true; RELEASE_STATE(state); break; } if (state->interrupts != NULL) { /* interrupt */ interrupt_item = (VMIterable *) state->interrupts; while (interrupt_item) { VMInterruptItem *item; item = (VMInterruptItem *) interrupt_item; if (item->cycles == state->cycles) { bool result; interrupt_handler *handler; handler = state->interrupt_handlers[item->interrupt_type]; result = handler(state, item->interrupt_type); /* delete item from the queue */ if (previous_interrupt_item) { previous_interrupt_item->next = interrupt_item->next; } else { state->interrupts = (VMInterruptItem *) interrupt_item->next; } free(interrupt_item); interrupt_item = previous_interrupt_item; if (!result) return false; } interrupt_item = interrupt_item->next; } } else if (_hit_breakpoint(state)) { /* breakpoint */ *hit_bp = true; RELEASE_STATE(state); break; } else { /* Save PC */ newdiff = vm_newdiff(); if (!newdiff) return false; newdiff->pc = state->pc; newdiff->cycles = state->cycles; diff->next = (VMIterable *) newdiff; diff = newdiff; /* Execute instruction */ opcode = OPCODE(state); handler = opcode_handlers[opcode->opcode_index].handler; if (!handler(state, diff, opcode->opcode)) return false; } nsteps--; RELEASE_STATE(state); } return true; }
static bool _vm_step(VMState *state, int nsteps, VMStateDiff **diff, bool *hit_bp, bool first) { Opcode *opcode; opcode_handler *handler; VMInterruptCallable *callable; VMInterruptItem *interrupt_item, *previous_interrupt_item = NULL; VMStateDiff *newdiff = NULL; #define RETURN(x) do { RELEASE_STATE(state); return (x); } while(0) *hit_bp = false; while (nsteps > 0) { /* acquire and release for every step. This allows for some nice contention! */ ACQUIRE_STATE(state); if (state->break_async) { RETURN(true); } callable = state->interrupt_callables; while (callable) { if (!callable->func(state, callable->argument)) { vm_seterrno(VM_INTERRUPT_CALLABLE_ERROR); RETURN(false); } callable = callable->next; } if (!first && _hit_breakpoint(state)) { /* breakpoint */ *hit_bp = true; RETURN(true); } else { /* PC error checking */ { /* offsets in bytes */ size_t pc; pc = GETPC(state); if (pc < state->executable_segment_offset || pc >= state->instructions_size) { vm_seterrno(VM_PC_OUT_OF_BOUNDS); #ifdef VM_DEBUG printf(LOCATION " pc: %lu max pc: %lu\n", (unsigned long) pc, (unsigned long) state->instructions_size - 1); #endif RETURN(false); } } /* Save changes in diff */ if (diff) { if (!(newdiff = vm_newdiff())) RETURN(false); newdiff->next = *diff; newdiff->pc = state->registers[PC]; newdiff->cycles = state->cycles; *diff = newdiff; } if (state->interrupt_policy != VM_POLICY_INTERRUPT_NEVER) { if (state->interrupts != NULL) { /* There are interrupts specified in the queue, call the user-written set_interrupt callback. */ if (!set_interrupt(state, newdiff)) RETURN(false); } /* For every step, handle an interrupt in the debuggee (if present) bytes calling a user-written function (or a default noop). */ if (!handle_interrupt(state, newdiff)) { RETURN(false); } } /* Execute instruction */ opcode = (Opcode *) OPCODE(state); handler = opcode_handlers[opcode->opcode_index].handler; if (!handler(state, newdiff, opcode->instruction)) { RETURN(state->stopped_running); } } nsteps--; first = false; RELEASE_STATE(state); } return true; #undef RETURN }
static bool _vm_step(VMState *state, unsigned long nsteps, VMStateDiff **diff, bool *hit_bp, bool first) { VMInterruptCallable *callable; VMStateDiff *newdiff = NULL; #define RETURN(x) do { RELEASE_STATE(state); return (x); } while(0) *hit_bp = false; while (nsteps > 0) { /* acquire and release for every step. This allows for some nice contention! */ ACQUIRE_STATE(state); if (state->break_async) { RETURN(true); } callable = state->interrupt_callables; while (callable) { if (!callable->func(state, callable->argument)) { vm_seterrno(VM_INTERRUPT_CALLABLE_ERROR); RETURN(false); } callable = callable->next; } if (!first && _hit_breakpoint(state)) { /* breakpoint */ *hit_bp = true; RETURN(true); } else { Opcode *opcode; opcode_handler *handler; /* Do the PC validity checks before updating any diffs. */ if (!(opcode = get_opcode(state, GETPC(state)))) RETURN(false); /* Save changes in diff */ if (diff) { if (!(newdiff = vm_newdiff())) RETURN(false); newdiff->next = *diff; newdiff->pc = GETPC(state); newdiff->cycles = state->cycles; *diff = newdiff; } if (state->interrupt_policy != VM_POLICY_INTERRUPT_NEVER) { if (state->interrupts != NULL) { /* There are interrupts specified in the queue, call the user-written set_interrupt callback. */ if (!set_interrupt(state, newdiff)) RETURN(false); } /* For every step, handle an interrupt in the debuggee (if present) bytes calling a user-written function (or a default noop). */ if (!handle_interrupt(state, newdiff)) { RETURN(false); } } /* Execute instruction */ handler = opcode_handlers[opcode->opcode_index].handler; if (!handler(state, newdiff, opcode->instruction)) RETURN(state->stopped_running); } nsteps--; first = false; RELEASE_STATE(state); } return true; #undef RETURN }