/* ** cmdbreak.c ** */ #include #include #include "mruby.h" #include "mruby/dump.h" #include "mruby/debug.h" #include "mruby/string.h" #include "mrdb.h" #include "mrdberror.h" #include "apibreak.h" #define BREAK_SET_MSG_LINE "Breakpoint %d: file %s, line %d.\n" #define BREAK_SET_MSG_METHOD "Breakpoint %d: method %s.\n" #define BREAK_SET_MSG_CLASS_METHOD "Breakpoint %d: class %s, method %s.\n" #define BREAK_INFO_MSG_HEADER "Num Type Enb What" #define BREAK_INFO_MSG_LINEBREAK "%-8ubreakpoint %s at %s:%u\n" #define BREAK_INFO_MSG_METHODBREAK "%-8ubreakpoint %s in %s:%s\n" #define BREAK_INFO_MSG_METHODBREAK_NOCLASS "%-8ubreakpoint %s in %s\n" #define BREAK_INFO_MSG_ENABLE "y" #define BREAK_INFO_MSG_DISABLE "n" #define BREAK_ERR_MSG_INVALIDARG "Internal error." #define BREAK_ERR_MSG_BLANK "Try \'help break\' for more information." #define BREAK_ERR_MSG_RANGEOVER "The line number range is from 1 to 65535." #define BREAK_ERR_MSG_NUMOVER "Exceeded the setable number of breakpoint." #define BREAK_ERR_MSG_NOOVER "Breakno is over the available number.Please 'quit' and restart mrdb." #define BREAK_ERR_MSG_INVALIDSTR "String \'%s\' is invalid.\n" #define BREAK_ERR_MSG_INVALIDLINENO "Line %d in file \"%s\" is unavailable.\n" #define BREAK_ERR_MSG_INVALIDCLASS "Class name \'%s\' is invalid.\n" #define BREAK_ERR_MSG_INVALIDMETHOD "Method name \'%s\' is invalid.\n" #define BREAK_ERR_MSG_INVALIDFILE "Source file named \"%s\" is unavailable.\n" #define BREAK_ERR_MSG_INVALIDBPNO "warning: bad breakpoint number at or near '%s'\n" #define BREAK_ERR_MSG_INVALIDBPNO_INFO "Args must be numbers variables." #define BREAK_ERR_MSG_NOBPNO "No breakpoint number %d.\n" #define BREAK_ERR_MSG_NOBPNO_INFO "No breakpoint matching '%d'\n" #define BREAK_ERR_MSG_NOBPNO_INFOALL "No breakpoints." #define LINENO_MAX_DIGIT 6 #define BPNO_LETTER_NUM 9 typedef int32_t (*all_command_func)(mrb_state *, mrb_debug_context *); typedef int32_t (*select_command_func)(mrb_state *, mrb_debug_context *, uint32_t); static void print_api_common_error(int32_t error) { switch(error) { case MRB_DEBUG_INVALID_ARGUMENT: puts(BREAK_ERR_MSG_INVALIDARG); break; default: break; } } #undef STRTOUL #define STRTOUL(ul,s) { \ int i; \ ul = 0; \ for(i=0; ISDIGIT(s[i]); i++) ul = 10*ul + (s[i] -'0'); \ } static int32_t parse_breakpoint_no(char* args) { char* ps = args; uint32_t l; if((*ps == '0')||(strlen(ps) >= BPNO_LETTER_NUM)) { return 0; } while( !(ISBLANK(*ps)||ISCNTRL(*ps)) ) { if(!ISDIGIT(*ps)) { return 0; } ps++; } STRTOUL(l, args); return l; } static mrb_bool exe_set_command_all(mrb_state *mrb, mrdb_state *mrdb, all_command_func func) { int32_t ret = MRB_DEBUG_OK; if(mrdb->wcnt == 1) { ret = func(mrb, mrdb->dbg); print_api_common_error(ret); return TRUE; } return FALSE; } static void exe_set_command_select(mrb_state *mrb, mrdb_state *mrdb, select_command_func func) { char* ps; int32_t ret = MRB_DEBUG_OK; int32_t bpno = 0; int32_t i; for(i=1; iwcnt; i++) { ps = mrdb->words[i]; bpno = parse_breakpoint_no(ps); if(bpno == 0) { printf(BREAK_ERR_MSG_INVALIDBPNO, ps); break; } ret = func(mrb, mrdb->dbg, (uint32_t)bpno); if(ret == MRB_DEBUG_BREAK_INVALID_NO) { printf(BREAK_ERR_MSG_NOBPNO, bpno); } else if(ret != MRB_DEBUG_OK) { print_api_common_error(ret); } } } mrb_debug_bptype check_bptype(char* args) { char* ps = args; if(ISBLANK(*ps)||ISCNTRL(*ps)) { puts(BREAK_ERR_MSG_BLANK); return MRB_DEBUG_BPTYPE_NONE; } if(!ISDIGIT(*ps)) { return MRB_DEBUG_BPTYPE_METHOD; } while( !(ISBLANK(*ps)||ISCNTRL(*ps)) ) { if(!ISDIGIT(*ps)) { printf(BREAK_ERR_MSG_INVALIDSTR, args); return MRB_DEBUG_BPTYPE_NONE; } ps++; } if((*args == '0')||(strlen(args) >= LINENO_MAX_DIGIT)) { puts(BREAK_ERR_MSG_RANGEOVER); return MRB_DEBUG_BPTYPE_NONE; } return MRB_DEBUG_BPTYPE_LINE; } static void print_breakpoint(mrb_debug_breakpoint *bp) { const char* enable_letter[] = {BREAK_INFO_MSG_DISABLE, BREAK_INFO_MSG_ENABLE}; if(bp->type == MRB_DEBUG_BPTYPE_LINE) { printf(BREAK_INFO_MSG_LINEBREAK, bp->bpno, enable_letter[bp->enable], bp->point.linepoint.file, bp->point.linepoint.lineno); } else { if(bp->point.methodpoint.class_name == NULL) { printf(BREAK_INFO_MSG_METHODBREAK_NOCLASS, bp->bpno, enable_letter[bp->enable], bp->point.methodpoint.method_name); } else { printf(BREAK_INFO_MSG_METHODBREAK, bp->bpno, enable_letter[bp->enable], bp->point.methodpoint.class_name, bp->point.methodpoint.method_name); } } } static void info_break_all(mrb_state *mrb, mrdb_state *mrdb) { int32_t bpnum = 0; int32_t i = 0; int32_t ret = MRB_DEBUG_OK; mrb_debug_breakpoint *bp_list; bpnum = mrb_debug_get_breaknum(mrb, mrdb->dbg); if(bpnum < 0) { print_api_common_error(bpnum); return; } else if(bpnum == 0) { puts(BREAK_ERR_MSG_NOBPNO_INFOALL); return; } bp_list = (mrb_debug_breakpoint*)mrb_malloc(mrb, bpnum * sizeof(mrb_debug_breakpoint)); ret = mrb_debug_get_break_all(mrb, mrdb->dbg, (uint32_t)bpnum, bp_list); if(ret < 0) { print_api_common_error(ret); return; } puts(BREAK_INFO_MSG_HEADER); for(i = 0 ; i < bpnum ; i++) { print_breakpoint(&bp_list[i]); } mrb_free(mrb, bp_list); } static void info_break_select(mrb_state *mrb, mrdb_state *mrdb) { int32_t ret = MRB_DEBUG_OK; int32_t bpno = 0; char* ps = mrdb->command; mrb_debug_breakpoint bp; mrb_bool isFirst = TRUE; int32_t i; for(i=2; iwcnt; i++) { ps = mrdb->words[i]; bpno = parse_breakpoint_no(ps); if(bpno == 0) { puts(BREAK_ERR_MSG_INVALIDBPNO_INFO); break; } ret = mrb_debug_get_break(mrb, mrdb->dbg, bpno, &bp); if(ret == MRB_DEBUG_BREAK_INVALID_NO) { printf(BREAK_ERR_MSG_NOBPNO_INFO, bpno); break; } else if(ret != MRB_DEBUG_OK) { print_api_common_error(ret); break; } else if(isFirst == TRUE) { isFirst = FALSE; puts(BREAK_INFO_MSG_HEADER); } print_breakpoint(&bp); } } mrb_debug_bptype parse_breakcommand(mrdb_state *mrdb, const char **file, uint32_t *line, char **cname, char **method) { mrb_debug_context *dbg = mrdb->dbg; char *args; char *body; mrb_debug_bptype type; uint32_t l; if(mrdb->wcnt <= 1) { puts(BREAK_ERR_MSG_BLANK); return MRB_DEBUG_BPTYPE_NONE; } args = mrdb->words[1]; if((body = strrchr(args, ':')) == NULL) { body = args; type = check_bptype(body); } else { if(body == args) { printf(BREAK_ERR_MSG_INVALIDSTR, args); return MRB_DEBUG_BPTYPE_NONE; } *body = '\0'; type = check_bptype(++body); } switch(type) { case MRB_DEBUG_BPTYPE_LINE: STRTOUL(l, body); if( l <= 65535 ) { *line = l; *file = (body == args)? mrb_debug_get_filename(dbg->irep, (uint32_t)(dbg->pc - dbg->irep->iseq)): args; } else { puts(BREAK_ERR_MSG_RANGEOVER); type = MRB_DEBUG_BPTYPE_NONE; } break; case MRB_DEBUG_BPTYPE_METHOD: if(body == args) { /* method only */ if( ISUPPER(*body)||ISLOWER(*body)||(*body == '_') ) { *method = body; *cname = NULL; } else { printf(BREAK_ERR_MSG_INVALIDMETHOD, args); type = MRB_DEBUG_BPTYPE_NONE; } } else { if( ISUPPER(*args) ) { switch(*body) { case '@': case '$': case '?': case '.': case ',': case ':': case ';': case '#': case '\\': case '\'': case '\"': printf(BREAK_ERR_MSG_INVALIDMETHOD, body); type = MRB_DEBUG_BPTYPE_NONE; break; default: *method = body; *cname = args; break; } } else { printf(BREAK_ERR_MSG_INVALIDCLASS, args); type = MRB_DEBUG_BPTYPE_NONE; } } break; case MRB_DEBUG_BPTYPE_NONE: default: break; } return type; } dbgcmd_state dbgcmd_break(mrb_state *mrb, mrdb_state *mrdb) { mrb_debug_bptype type; mrb_debug_context *dbg = mrdb->dbg; const char *file = NULL; uint32_t line = 0; char *cname = NULL; char *method = NULL; int32_t ret; type = parse_breakcommand(mrdb, &file, &line, &cname, &method); switch (type) { case MRB_DEBUG_BPTYPE_LINE: ret = mrb_debug_set_break_line(mrb, dbg, file, line); break; case MRB_DEBUG_BPTYPE_METHOD: ret = mrb_debug_set_break_method(mrb, dbg, cname, method); break; case MRB_DEBUG_BPTYPE_NONE: default: return DBGST_PROMPT; } if (ret >= 0) { if (type == MRB_DEBUG_BPTYPE_LINE) { printf(BREAK_SET_MSG_LINE, ret, file, line); } else if ((type == MRB_DEBUG_BPTYPE_METHOD)&&(cname == NULL)) { printf(BREAK_SET_MSG_METHOD, ret, method); } else { printf(BREAK_SET_MSG_CLASS_METHOD, ret, cname, method); } } else { switch (ret) { case MRB_DEBUG_BREAK_INVALID_LINENO: printf(BREAK_ERR_MSG_INVALIDLINENO, line, file); break; case MRB_DEBUG_BREAK_INVALID_FILE: printf(BREAK_ERR_MSG_INVALIDFILE, file); break; case MRB_DEBUG_BREAK_NUM_OVER: puts(BREAK_ERR_MSG_NUMOVER); break; case MRB_DEBUG_BREAK_NO_OVER: puts(BREAK_ERR_MSG_NOOVER); break; case MRB_DEBUG_INVALID_ARGUMENT: puts(BREAK_ERR_MSG_INVALIDARG); break; case MRB_DEBUG_NOBUF: puts("T.B.D."); break; default: break; } } return DBGST_PROMPT; } dbgcmd_state dbgcmd_info_break(mrb_state *mrb, mrdb_state *mrdb) { if(mrdb->wcnt == 2) { info_break_all(mrb, mrdb); } else { info_break_select(mrb, mrdb); } return DBGST_PROMPT; } dbgcmd_state dbgcmd_delete(mrb_state *mrb, mrdb_state *mrdb) { mrb_bool ret = FALSE; ret = exe_set_command_all(mrb, mrdb, mrb_debug_delete_break_all); if(ret != TRUE) { exe_set_command_select(mrb, mrdb, mrb_debug_delete_break); } return DBGST_PROMPT; } dbgcmd_state dbgcmd_enable(mrb_state *mrb, mrdb_state *mrdb) { mrb_bool ret = FALSE; ret = exe_set_command_all(mrb, mrdb, mrb_debug_enable_break_all); if(ret != TRUE) { exe_set_command_select(mrb, mrdb, mrb_debug_enable_break); } return DBGST_PROMPT; } dbgcmd_state dbgcmd_disable(mrb_state *mrb, mrdb_state *mrdb) { mrb_bool ret = FALSE; ret = exe_set_command_all(mrb, mrdb, mrb_debug_disable_break_all); if(ret != TRUE) { exe_set_command_select(mrb, mrdb, mrb_debug_disable_break); } return DBGST_PROMPT; }