/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * debug.c: The SPL Command Line Debugger */ #include #include #ifdef ENABLE_READLINE_SUPPORT # include # include #endif #include "spl.h" #include "compat.h" static int open_debug = 1; static int run_init_mode = 0; struct bp_type { struct spl_code *code; int code_ip; char *pattern; int last_did_match; struct bp_type *next; }; static struct bp_type *bp_list = 0; static const int init_mode_ops[] = { SPL_OP_SIG, SPL_OP_NOP, SPL_OP_DBGSYM, SPL_OP_PUSHC, SPL_OP_LOAD, SPL_OP_UNDEF, SPL_OP_ZERO, SPL_OP_ONE, SPL_OP_POPL, SPL_OP_REGF, SPL_OP_REGM, SPL_OP_OBJECT, SPL_OP_ENDOBJ, 0 }; static int check_breakpoint(struct spl_task *task) { if (run_init_mode) { if (task->module) return 0; int base_op = 0; if (task->code) base_op = task->code->code[task->code_ip]; if (base_op < 0x60) base_op &= ~3; for (int i=0; init_mode_ops[i]; i++) if (base_op == init_mode_ops[i]) return 0; run_init_mode = 0; return 1; } if (open_debug) { open_debug = 0; if (task->code && task->code->code[task->code_ip] == SPL_OP_WARNING) open_debug = 1; return 1; } if (task->code && task->code->code[task->code_ip] == SPL_OP_WARNING) open_debug = 1; struct bp_type *b = bp_list; for (int i=1; b; i++) { if (b->code) { if (b->code == task->code && b->code_ip == task->code_ip) { printf("** Reached brakepoint #%d. **\n", i); return 1; } } else if (task->debug_str) { char *t_text = strdup(task->debug_str); int t_linenum = atoi(strtok(t_text, ":")); // ignore character number (void) atoi(strtok(NULL, ":")); char *t_fullname = strtok(NULL, ""); char *t_basename = strrchr(t_fullname, '/'); t_basename = t_basename ? t_basename+1 : t_fullname; char *p_text = strdup(b->pattern); int p_linenum = 0; char *p_name = 0; if (strchr(p_text, ':')) { p_name = strtok(p_text, ":"); p_linenum = atoi(strtok(NULL, "")); } else { p_linenum = atoi(p_text); } int pattern_match = p_linenum == t_linenum; if (pattern_match && p_name) pattern_match = !strcmp(p_name, t_fullname) || !strcmp(p_name, t_basename); free(t_text); free(p_text); if (pattern_match) { if (!b->last_did_match) { b->last_did_match = 1; printf("** Reached brakepoint #%d. **\n", i); return 1; } } else b->last_did_match = 0; } b = b->next; } return 0; } static int command_c(struct spl_task UNUSED(*task), char UNUSED(*args)) { printf("\n"); return 0; } static int command_q(struct spl_task *task, char UNUSED(*args)) { printf("\n"); spl_task_setcode(task, 0); return -1; } static int command_h(struct spl_task *task, char *args); static int command_td(struct spl_task *task, char *args) { printf("\n"); spl_treedump(task->vm, stdout, SPL_TREEDUMP_FLAG_VALUE | SPL_TREEDUMP_FLAG_CTX | SPL_TREEDUMP_FLAG_CLS | SPL_TREEDUMP_FLAG_LINK | SPL_TREEDUMP_FLAG_MODS | SPL_TREEDUMP_FLAG_TASKS | SPL_TREEDUMP_FLAG_FLAGS, args); return 1; } static int command_bt(struct spl_task *task, char UNUSED(*args)) { char *backtrace = spl_backtrace_string(task); printf("\n%s", backtrace); free(backtrace); return 1; } static int command_bl(struct spl_task UNUSED(*task), char UNUSED(*args)) { int i = 0; for (struct bp_type *p = bp_list; p; p = p->next) { printf("\n%3d. %s\n", ++i, p->pattern); if (p->code) printf(" \n", p->code_ip, p->code->id); else printf(" \n"); } return 1; } static int command_bx(struct spl_task UNUSED(*task), char *args) { int i = atoi(args); for (struct bp_type *p = bp_list, **l = &bp_list; p; p = *(l = &p->next)) if (--i == 0) { *l = p->next; if (p->pattern) free(p->pattern); if (p->code) spl_code_put(p->code); free(p); } return 2; } static int command_bf(struct spl_task *task, char *args) { struct spl_node *n = spl_lookup(task, task->vm->root, args, SPL_LOOKUP_TEST); if (!n) { printf("Function/method '%s' not found!\n", args); return 1; } if ( (n->flags & (SPL_NODE_FLAG_FUNCTION|SPL_NODE_FLAG_METHOD)) == 0 ) { printf("Node '%s' is neighter a function nor a method!\n", args); return 1; } struct bp_type *p = calloc(1, sizeof(struct bp_type)); p->pattern = strdup(args); p->code = spl_code_get(n->code); p->code_ip = n->code_ip; p->next = bp_list; bp_list = p; return 2; } static int command_b(struct spl_task UNUSED(*task), char *args) { struct bp_type *p = calloc(1, sizeof(struct bp_type)); p->pattern = strdup(args); p->next = bp_list; bp_list = p; return 2; } static int command_i(struct spl_task UNUSED(*task), char UNUSED(*args)) { printf("\n"); run_init_mode = 1; return 0; } struct command_list_type { int (*func)(struct spl_task *, char *); char *cmdtext; char *shorthelp; char *longhelp; }; static struct command_list_type command_list[] = { { command_c, "c", "Contine", "Continue execution of program until a breakpoint or\n" "a HALT instruction is reached.\n" }, { command_q, "q", "Quit", "Execute a HALT instruction. This instantly terminates\n" "the SPL program.\n" }, { command_h, "h", "Help (type 'h h')", "If an argument is passed, the detailed help message for\n" "the specified debugger command is printed. Without an\n" "argument, a list of available commands is printed.\n" }, { 0, "", "", "" }, { command_td, "td", "Treedump", "Create a treedump of the current VM state.\n" }, { command_bt, "bt", "Backtract", "Create a backtrace of the current task.\n" }, { 0, "", "", "" }, { command_bl, "bl", "List set breakpoints", "Print a list of currently set breakpoints.\n" }, { command_bx, "bx", "Delete a breakpoint", "Delete a breakpoint. The breakpoint number as printed by\n" "the 'bl' command must be passed as argument.\n" }, { command_bf, "bf", "Set breakpoint on function", "Create a breakpoint, using a function/method name as argument.\n" }, { command_b, "b", "Set breakpoint on source pos", "Create a breakpoint, using : or just \n" "as argument.\n" }, { 0, "", "", "" }, { command_i, "i", "Run init", "Execute trivial code which declares functions, objects, etc only.\n" "Break as soon as non-trivial code is reached.\n" }, { 0, 0, 0, 0 } }; static int command_h(struct spl_task UNUSED(*task), char *args) { printf("\n"); if (*args == 0) { for (int i=0; command_list[i].cmdtext; i++) printf("%s\t%s\n",command_list[i].cmdtext, command_list[i].shorthelp); } else { for (int i=0; command_list[i].cmdtext; i++) if (!strcmp(command_list[i].cmdtext, args)) { printf("%s", command_list[i].longhelp); return 1; } printf("Command '%s' does not exist!\n", args); } return 1; } static int debug_commandline(struct spl_task *task) { printf("\n"); printf("At byte %d in code block '%s' (%s).\n", task->code_ip, task->code ? task->code->id : "NONE", task->debug_str ? task->debug_str : "no debug info"); reread:; #ifdef ENABLE_READLINE_SUPPORT static int did_run_using_history = 0; if (!did_run_using_history) { using_history(); did_run_using_history = 1; } char *cmdline = readline("SPL Debug> "); #else char *cmdline = malloc(1024); printf("SPL Debug> "); fflush(stdout); fgets(cmdline, 1023, stdin); cmdline[1023] = 0; for (int i=strlen(cmdline)-1; i>=0; i--) if (cmdline[i] == '\n' || cmdline[i] == '\r') cmdline[i] = 0; else break; #endif if (!cmdline) { printf("\n"); spl_task_setcode(task, 0); return -1; } if (*cmdline == 0) { free(cmdline); goto reread; } #ifdef ENABLE_READLINE_SUPPORT add_history(cmdline); #endif char *args, *token; for (token = args = cmdline; *args; args++) if (*args == ' ' || *args == '\t') break; while (*args == ' ' || *args == '\t') *(args++) = 0; for (int i=0; command_list[i].cmdtext; i++) if (!strcmp(command_list[i].cmdtext, token)) { int rc = command_list[i].func(task, args); free(cmdline); if (rc == 2) goto reread; return rc; } printf("\n"); printf("Unknown debugger command: '%s'\n", token); printf("Type 'h' for help.\n"); free(cmdline); return 1; } void spl_debug_reset(void) { open_debug = 1; run_init_mode = 0; while (bp_list) { struct bp_type *n = bp_list->next; if (bp_list->code) spl_code_put(bp_list->code); if (bp_list->pattern) free(bp_list->pattern); free(bp_list); bp_list = n; } bp_list = 0; } int spl_debug_exec(struct spl_task *task) { int rc; if (check_breakpoint(task)) { while (1) { int inner_rc = debug_commandline(task); if (inner_rc < 0) return 0; if (!inner_rc) break; } } rc = spl_exec(task); if (!task->code) { printf("** Program terminated. **\n"); while (1) { int inner_rc = debug_commandline(task); if (inner_rc <= 0) break; } } else if (rc < 0) { printf("** Caught VM error. **\n"); while (1) { int inner_rc = debug_commandline(task); if (inner_rc <= 0) break; } } else if (open_debug) { printf("** Caught VM warning. **\n"); while (1) { int inner_rc = debug_commandline(task); if (inner_rc <= 0) break; } } open_debug = 0; return rc; }