426 lines
11 KiB
C
Vendored
426 lines
11 KiB
C
Vendored
/*
|
|
** cmdbreak.c
|
|
**
|
|
*/
|
|
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
#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 settable 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; i<mrdb->wcnt; 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; i<mrdb->wcnt; 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(mrb_state *mrb, 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(mrb, dbg->irep, 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(mrb, 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;
|
|
}
|
|
|
|
static dbgcmd_state
|
|
dbgcmd_set_breakpoint(mrb_state *mrb, mrdb_state *mrdb,
|
|
all_command_func all_func, select_command_func select_func)
|
|
{
|
|
if (!exe_set_command_all(mrb, mrdb, all_func)) {
|
|
exe_set_command_select(mrb, mrdb, select_func);
|
|
}
|
|
return DBGST_PROMPT;
|
|
}
|
|
|
|
dbgcmd_state
|
|
dbgcmd_delete(mrb_state *mrb, mrdb_state *mrdb)
|
|
{
|
|
return dbgcmd_set_breakpoint(mrb, mrdb, mrb_debug_delete_break_all, mrb_debug_delete_break);
|
|
}
|
|
|
|
dbgcmd_state
|
|
dbgcmd_enable(mrb_state *mrb, mrdb_state *mrdb)
|
|
{
|
|
return dbgcmd_set_breakpoint(mrb, mrdb, mrb_debug_enable_break_all, mrb_debug_enable_break);
|
|
}
|
|
|
|
dbgcmd_state
|
|
dbgcmd_disable(mrb_state *mrb, mrdb_state *mrdb)
|
|
{
|
|
return dbgcmd_set_breakpoint(mrb, mrdb, mrb_debug_disable_break_all, mrb_debug_disable_break);
|
|
}
|