/* debug.c */ #include #include #include #include #include #include "getline.h" #include "pcode.h" typedef int (*command_func_t)(interp_t* interp, int arg1, int arg2); typedef struct command { char* name; command_func_t func; int min_argc, max_argc; } command_t; static char* color(interp_t* interp, int fg) { int i; static int virgin = 1; static char buf[16][8]; if (virgin) { for (i = 0; i < 8; ++i) { sprintf(buf[i + 0], "\033[0;3%dm", i); sprintf(buf[i + 8], "\033[1;3%dm", i); } virgin = 0; } if (!interp->color || fg < -1 || fg > 15) return "" ; else if (fg == -1) return "\033[0m"; else return buf[fg] ; } #define C(num) color(interp, (num)) static void print_mem(interp_t* interp, int data, int addr) { vm_t* vm = &interp->vm; insn_t* insn; char buf[12]; gmp_sprintf(buf, "%+011Zd", vm->data[addr].val); if (data) { printf("%s$%s%03d%s:%s", C(4), C(12), addr, C(8), C(-1)); printf("%.2s %.3s %.3s %.3s", buf, buf + 2, buf + 5, buf + 8); } else { insn = &vm->text[addr]; printf("%s@%s%03d%s:%s", C(6), C(14), addr, C(8), C(-1)); if (insn->opcode == NULL) { printf("%s?? ??? ??? ???%s", C(9), C(-1)); } else { printf("%.2s %03d %03d %03d", insn->opcode->code, insn->arg[0], insn->arg[1], insn->arg[2]); } } } static void print_insn(interp_t* interp, int addr) { int i; vm_t* vm = &interp->vm; insn_t* insn = &vm->text[addr]; print_mem(interp, 0, addr); if (insn->opcode == NULL) { printf(" %s[%s * uninitialized text * %s]%s", C(8), C(9), C(8), C(-1)); } else { printf(" %s[ %s%-5s ", C(8), C(insn->opcode->valid ? 10 : 9), insn->opcode->name); for (i = 0; i < 3; ++i) { switch (insn->opcode->arg[i]) { case ARG_IGN : printf("%s.... ", C(8)); continue; case ARG_DATA: printf("%s$%s", C(4), C(12)); break; case ARG_TEXT: printf("%s@%s", C(6), C(14)); break; case ARG_BASE: printf("%s#%s", C(3), C(11)); break; case ARG_IDX : printf("%s+%s", C(5), C(13)); break; } printf("%03d ", insn->arg[i]); } printf(" %s]%s", C(8), C(-1)); } } static int command_quit(interp_t* interp, int arg1, int arg2) { return -1; } static int command_help(interp_t* interp, int arg1, int arg2) { printf("quit %s:%s terminate the program\n", C(8), C(-1)); printf("help %s:%s display this message\n", C(8), C(-1)); printf("run %s:%s disable single-step mode\n", C(8), C(-1)); printf("next %s(default) :%s execute next instruction\n", C(8), C(-1)); printf("list %s[%saddr%s] [%snum%s] :%s display %snum%s (def 1) " "insns at text address %saddr%s (def next)\n", C(8), C(6), C(8), C(6), C(8), C(-1), C(6), C(-1), C(6), C(-1)); printf("break %saddr %s:%s pause execution at text address " "%saddr%s\n", C(6), C(8), C(-1), C(6), C(-1)); printf("unbreak %saddr %s:%s cancel breakpoint at text address " "%saddr%s\n", C(6), C(8), C(-1), C(6), C(-1)); printf("dump %saddr%s %s[%snum%s] :%s display %snum%s (default 1) " "values at data address %saddr%s\n", C(6), C(-1), C(8), C(6), C(8), C(-1), C(6), C(-1), C(6), C(-1)); printf("watch %saddr%s %s[%snum%s] :%s watch %snum%s (default 1) " "values at data address %saddr%s\n", C(6), C(-1), C(8), C(6), C(8), C(-1), C(6), C(-1), C(6), C(-1)); printf("unwatch %saddr%s %s[%snum%s] :%s stop watching %snum%s (default 1) " "values at data address %saddr%s\n", C(6), C(-1), C(8), C(6), C(8), C(-1), C(6), C(-1), C(6), C(-1)); return 0; } static int command_run(interp_t* interp, int arg1, int arg2) { interp->single_step = 0; return 1; } static int command_next(interp_t* interp, int arg1, int arg2) { return 1; } static int command_list(interp_t* interp, int arg1, int arg2) { int i; if (arg1 == -1) arg1 = interp->vm.pc; if (arg2 < 1) arg2 = 1; if (arg2 + arg1 > 1000) arg2 = 1000 - arg1; for (i = arg1; i < arg1 + arg2; ++i) { print_insn(interp, i); printf("\n"); } return 0; } static int command_break(interp_t* interp, int arg1, int arg2) { int i, len = VEC_LEN(int, &interp->breakpoints); for (i = 0; i < len + 1; ++i) { if (i < len && arg1 == *VEC_ELT(int, &interp->breakpoints, i)) { printf("Breakpoint %03d already set\n", arg1); return 0; } else if (i == len || arg1 > *VEC_ELT(int, &interp->breakpoints, i)) { VEC_INS1(int, &interp->breakpoints, i, arg1); return 0; } } } static int command_unbreak(interp_t* interp, int arg1, int arg2) { int i, len = VEC_LEN(int, &interp->breakpoints); for (i = 0; i < len; ++i) { if (arg1 == *VEC_ELT(int, &interp->breakpoints, i)) { VEC_CUT1(int, &interp->breakpoints, i, NULL); return 0; } else if (arg1 > *VEC_ELT(int, &interp->breakpoints, i)) { break; } } printf("Breakpoint %03d doesn't exist\n", arg1); return 0; } static int command_dump(interp_t* interp, int arg1, int arg2) { int i, col; if (arg2 < 1) arg2 = 1; if (arg2 + arg1 > 1000) arg2 = 1000 - arg1; for (i = arg1, col = 0; i < arg1 + arg2; ++i) { if (col == 4) {printf("\n"); col = 0;} else if (i != arg1) {printf(" ");} print_mem(interp, 1, i); ++col; } printf("\n"); return 0; } static int command_watch(interp_t* interp, int arg1, int arg2) { int i, j; if (arg2 < 1) arg2 = 1; if (arg2 + arg1 > 1000) arg2 = 1000 - arg1; for (i = 0; i < VEC_LEN(int, &interp->watches); ++i) { if (*VEC_ELT(int, &interp->watches, i) >= arg1) break; } for (j = arg1; j < arg1 + arg2; ++i, ++j) { if (i >= VEC_LEN(int, &interp->watches) || *VEC_ELT(int, &interp->watches, i) != j) { VEC_INS1(int, &interp->watches, i, j); } } return 0; } static int command_unwatch(interp_t* interp, int arg1, int arg2) { int i, found = 0, start = 0, end = -1; if (arg2 < 1) arg2 = 1; if (arg2 + arg1 > 1000) arg2 = 1000 - arg1; for (i = 0; i < VEC_LEN(int, &interp->watches); ++i) { if (!found) { if (*VEC_ELT(int, &interp->watches, i) >= arg1) { found = 1; start = i; } } else { if (*VEC_ELT(int, &interp->watches, i) >= arg1 + arg2) { end = i; break; } } } if (end == -1) end = VEC_LEN(int, &interp->watches); VEC_CUTN(int, &interp->watches, start, NULL, end - start); return 0; } static command_t commands[] = { {"quit" , command_quit , 0, 0}, {"help" , command_help , 0, 0}, {"run" , command_run , 0, 0}, {"next" , command_next , 0, 0}, {"" , command_next , 0, 0}, {"list" , command_list , 0, 2}, {"break" , command_break , 1, 1}, {"unbreak", command_unbreak, 1, 1}, {"dump" , command_dump , 1, 2}, {"watch" , command_watch , 1, 2}, {"unwatch", command_unwatch, 1, 2} }; void debug_init(interp_t* interp) { interp->single_step = 1; VEC_INIT(int, &interp->breakpoints); VEC_INIT(int, &interp->watches ); } void debug_clear(interp_t* interp) { VEC_FREE(int, &interp->breakpoints); VEC_FREE(int, &interp->watches ); } int debug_step(interp_t* interp) { int i, col, ret, len = 0, retval = 0, arg[2], argc; char* buf = NULL, *cmd, *p, *q, *ws = " \f\r\t\v", *err; /* Display the next instruction. */ print_insn(interp, interp->vm.pc); /* Display all watched memory locations. */ for (i = 0, col = 4; i < VEC_LEN(int, &interp->watches); ++i) { if (col == 4) {printf("\n"); col = 0;} else if (i != 0) {printf(" ");} print_mem(interp, 1, *VEC_ELT(int, &interp->watches, i)); ++col; } printf("\n"); /* Check to see if we hit a breakpoint. */ for (i = 0; i < VEC_LEN(int, &interp->breakpoints); ++i) { if (interp->vm.pc == *VEC_ELT(int, &interp->breakpoints, i)) { interp->single_step = 1; } } /* If not single-stepping, we're done. */ if (!interp->single_step) return 0; again: /* Get the command. */ printf("%s--%s>%s ", C(5), C(13), C(-1)); fflush(stdout); if ((ret = getline(&buf, &len, stdin)) == -1) { printf("\n"); if (!feof(stdin)) perror("getline"); retval = -1; goto end; } if (buf[ret - 1] == '\n') buf[ret - 1] = 0; /* Split it into a command name and up to two arguments. */ q = buf; for (i = 0; i < 4; ++i) { p = q + strspn (q, ws); q = p + strcspn(p, ws); if (*p == 0) { /* End of the line. */ if (i == 0) {cmd = ""; argc = 0;} else argc = i - 1; for (i = argc; i < 2; ++i) arg[i] = -1; break; } else { *q++ = 0; if (i == 0) cmd = p; /* Got the command name. */ else if (i == 3) {printf("Excess arguments\n"); goto again;} else { /* Got an argument. */ arg[i - 1] = strtoul(p, &err, 10); if (*err != 0 || arg[i - 1] > 999) { printf("Malformed argument\n"); goto again; } } } } /* Locate and execute the command's function. */ for (i = 0; i < sizeof(commands) / sizeof(*commands); ++i) { /* Check the command name. If it doesn't match, try the next. */ if (strcmp(commands[i].name, cmd) != 0) continue; /* Found the command, so check the argument count. */ if (argc < commands[i].min_argc) { printf("Insufficient arguments; at least %d needed\n", commands[i].min_argc); goto again; } else if (argc > commands[i].max_argc) { printf("Too many arguments; at most %d allowed\n", commands[i].max_argc); goto again; } /* So far, so good. Try running the function. */ switch (commands[i].func(interp, arg[0], arg[1])) { case -1: retval = -1; goto end ; case 0: goto again; case 1: retval = 0; goto end ; } } /* If we fell through to here, then there was no match. */ printf("Command `%s' not recognized\n" "Try `help' for a list of available commands\n", cmd); goto again; end: free(buf); return retval; } /* vim: set ts=4 sts=4 sw=4 tw=80 et: */