first commit
This commit is contained in:
@@ -0,0 +1,507 @@
|
||||
/*
|
||||
** apibreak.c
|
||||
**
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <mruby.h>
|
||||
#include <mruby/irep.h>
|
||||
#include "mrdb.h"
|
||||
#include <mruby/debug.h>
|
||||
#include <mruby/opcode.h>
|
||||
#include <mruby/class.h>
|
||||
#include <mruby/proc.h>
|
||||
#include <mruby/variable.h>
|
||||
#include "mrdberror.h"
|
||||
#include "apibreak.h"
|
||||
|
||||
#define MAX_BREAKPOINTNO (MAX_BREAKPOINT * 1024)
|
||||
#define MRB_DEBUG_BP_FILE_OK (0x0001)
|
||||
#define MRB_DEBUG_BP_LINENO_OK (0x0002)
|
||||
|
||||
static uint16_t
|
||||
check_lineno(mrb_irep_debug_info_file *info_file, uint16_t lineno)
|
||||
{
|
||||
uint32_t count = info_file->line_entry_count;
|
||||
uint16_t l_idx;
|
||||
|
||||
if (info_file->line_type == mrb_debug_line_ary) {
|
||||
for (l_idx = 0; l_idx < count; ++l_idx) {
|
||||
if (lineno == info_file->lines.ary[l_idx]) {
|
||||
return lineno;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (l_idx = 0; l_idx < count; ++l_idx) {
|
||||
if (lineno == info_file->lines.flat_map[l_idx].line) {
|
||||
return lineno;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t
|
||||
get_break_index(mrb_debug_context *dbg, uint32_t bpno)
|
||||
{
|
||||
uint32_t i;
|
||||
int32_t index;
|
||||
char hit = FALSE;
|
||||
|
||||
for(i = 0 ; i < dbg->bpnum; i++) {
|
||||
if (dbg->bp[i].bpno == bpno) {
|
||||
hit = TRUE;
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hit == FALSE) {
|
||||
return MRB_DEBUG_BREAK_INVALID_NO;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
static void
|
||||
free_breakpoint(mrb_state *mrb, mrb_debug_breakpoint *bp)
|
||||
{
|
||||
switch(bp->type) {
|
||||
case MRB_DEBUG_BPTYPE_LINE:
|
||||
mrb_free(mrb, (void*)bp->point.linepoint.file);
|
||||
break;
|
||||
case MRB_DEBUG_BPTYPE_METHOD:
|
||||
mrb_free(mrb, (void*)bp->point.methodpoint.method_name);
|
||||
if (bp->point.methodpoint.class_name != NULL) {
|
||||
mrb_free(mrb, (void*)bp->point.methodpoint.class_name);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint16_t
|
||||
check_file_lineno(mrb_state *mrb, struct mrb_irep *irep, const char *file, uint16_t lineno)
|
||||
{
|
||||
mrb_irep_debug_info_file *info_file;
|
||||
uint16_t result = 0;
|
||||
uint16_t f_idx;
|
||||
uint16_t fix_lineno;
|
||||
uint16_t i;
|
||||
|
||||
for (f_idx = 0; f_idx < irep->debug_info->flen; ++f_idx) {
|
||||
const char *filename;
|
||||
info_file = irep->debug_info->files[f_idx];
|
||||
filename = mrb_sym_name_len(mrb, info_file->filename_sym, NULL);
|
||||
if (!strcmp(filename, file)) {
|
||||
result = MRB_DEBUG_BP_FILE_OK;
|
||||
|
||||
fix_lineno = check_lineno(info_file, lineno);
|
||||
if (fix_lineno != 0) {
|
||||
return result | MRB_DEBUG_BP_LINENO_OK;
|
||||
}
|
||||
}
|
||||
for (i=0; i < irep->rlen; ++i) {
|
||||
result |= check_file_lineno(mrb, irep->reps[i], file, lineno);
|
||||
if (result == (MRB_DEBUG_BP_FILE_OK | MRB_DEBUG_BP_LINENO_OK)) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int32_t
|
||||
compare_break_method(mrb_state *mrb, mrb_debug_breakpoint *bp, struct RClass *class_obj, mrb_sym method_sym, mrb_bool* isCfunc)
|
||||
{
|
||||
const char* class_name;
|
||||
const char* method_name;
|
||||
mrb_method_t m;
|
||||
struct RClass* sc;
|
||||
const char* sn;
|
||||
mrb_sym ssym;
|
||||
mrb_debug_methodpoint *method_p;
|
||||
mrb_bool is_defined;
|
||||
|
||||
method_name = mrb_sym_name(mrb, method_sym);
|
||||
|
||||
method_p = &bp->point.methodpoint;
|
||||
if (strcmp(method_p->method_name, method_name) == 0) {
|
||||
class_name = mrb_class_name(mrb, class_obj);
|
||||
if (class_name == NULL) {
|
||||
if (method_p->class_name == NULL) {
|
||||
return bp->bpno;
|
||||
}
|
||||
}
|
||||
else if (method_p->class_name != NULL) {
|
||||
m = mrb_method_search_vm(mrb, &class_obj, method_sym);
|
||||
if (MRB_METHOD_UNDEF_P(m)) {
|
||||
return MRB_DEBUG_OK;
|
||||
}
|
||||
if (MRB_METHOD_CFUNC_P(m)) {
|
||||
*isCfunc = TRUE;
|
||||
}
|
||||
|
||||
is_defined = mrb_class_defined(mrb, method_p->class_name);
|
||||
if (is_defined == FALSE) {
|
||||
return MRB_DEBUG_OK;
|
||||
}
|
||||
|
||||
sc = mrb_class_get(mrb, method_p->class_name);
|
||||
ssym = mrb_symbol(mrb_check_intern_cstr(mrb, method_p->method_name));
|
||||
m = mrb_method_search_vm(mrb, &sc, ssym);
|
||||
if (MRB_METHOD_UNDEF_P(m)) {
|
||||
return MRB_DEBUG_OK;
|
||||
}
|
||||
|
||||
class_name = mrb_class_name(mrb, class_obj);
|
||||
sn = mrb_class_name(mrb, sc);
|
||||
if (strcmp(sn, class_name) == 0) {
|
||||
return bp->bpno;
|
||||
}
|
||||
}
|
||||
}
|
||||
return MRB_DEBUG_OK;
|
||||
}
|
||||
|
||||
int32_t
|
||||
mrb_debug_set_break_line(mrb_state *mrb, mrb_debug_context *dbg, const char *file, uint16_t lineno)
|
||||
{
|
||||
int32_t index;
|
||||
char* set_file;
|
||||
uint16_t result;
|
||||
|
||||
if ((mrb == NULL)||(dbg == NULL)||(file == NULL)) {
|
||||
return MRB_DEBUG_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (dbg->bpnum >= MAX_BREAKPOINT) {
|
||||
return MRB_DEBUG_BREAK_NUM_OVER;
|
||||
}
|
||||
|
||||
if (dbg->next_bpno > MAX_BREAKPOINTNO) {
|
||||
return MRB_DEBUG_BREAK_NO_OVER;
|
||||
}
|
||||
|
||||
/* file and lineno check (line type mrb_debug_line_ary only.) */
|
||||
result = check_file_lineno(mrb, dbg->root_irep, file, lineno);
|
||||
if (result == 0) {
|
||||
return MRB_DEBUG_BREAK_INVALID_FILE;
|
||||
}
|
||||
else if (result == MRB_DEBUG_BP_FILE_OK) {
|
||||
return MRB_DEBUG_BREAK_INVALID_LINENO;
|
||||
}
|
||||
|
||||
set_file = (char*)mrb_malloc(mrb, strlen(file) + 1);
|
||||
|
||||
index = dbg->bpnum;
|
||||
dbg->bp[index].bpno = dbg->next_bpno;
|
||||
dbg->next_bpno++;
|
||||
dbg->bp[index].enable = TRUE;
|
||||
dbg->bp[index].type = MRB_DEBUG_BPTYPE_LINE;
|
||||
dbg->bp[index].point.linepoint.lineno = lineno;
|
||||
dbg->bpnum++;
|
||||
|
||||
strncpy(set_file, file, strlen(file) + 1);
|
||||
|
||||
dbg->bp[index].point.linepoint.file = set_file;
|
||||
|
||||
return dbg->bp[index].bpno;
|
||||
}
|
||||
|
||||
int32_t
|
||||
mrb_debug_set_break_method(mrb_state *mrb, mrb_debug_context *dbg, const char *class_name, const char *method_name)
|
||||
{
|
||||
int32_t index;
|
||||
char* set_class;
|
||||
char* set_method;
|
||||
|
||||
if ((mrb == NULL) || (dbg == NULL) || (method_name == NULL)) {
|
||||
return MRB_DEBUG_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (dbg->bpnum >= MAX_BREAKPOINT) {
|
||||
return MRB_DEBUG_BREAK_NUM_OVER;
|
||||
}
|
||||
|
||||
if (dbg->next_bpno > MAX_BREAKPOINTNO) {
|
||||
return MRB_DEBUG_BREAK_NO_OVER;
|
||||
}
|
||||
|
||||
if (class_name != NULL) {
|
||||
set_class = (char*)mrb_malloc(mrb, strlen(class_name) + 1);
|
||||
strncpy(set_class, class_name, strlen(class_name) + 1);
|
||||
}
|
||||
else {
|
||||
set_class = NULL;
|
||||
}
|
||||
|
||||
set_method = (char*)mrb_malloc(mrb, strlen(method_name) + 1);
|
||||
|
||||
strncpy(set_method, method_name, strlen(method_name) + 1);
|
||||
|
||||
index = dbg->bpnum;
|
||||
dbg->bp[index].bpno = dbg->next_bpno;
|
||||
dbg->next_bpno++;
|
||||
dbg->bp[index].enable = TRUE;
|
||||
dbg->bp[index].type = MRB_DEBUG_BPTYPE_METHOD;
|
||||
dbg->bp[index].point.methodpoint.method_name = set_method;
|
||||
dbg->bp[index].point.methodpoint.class_name = set_class;
|
||||
dbg->bpnum++;
|
||||
|
||||
return dbg->bp[index].bpno;
|
||||
}
|
||||
|
||||
int32_t
|
||||
mrb_debug_get_breaknum(mrb_state *mrb, mrb_debug_context *dbg)
|
||||
{
|
||||
if ((mrb == NULL) || (dbg == NULL)) {
|
||||
return MRB_DEBUG_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
return dbg->bpnum;
|
||||
}
|
||||
|
||||
int32_t
|
||||
mrb_debug_get_break_all(mrb_state *mrb, mrb_debug_context *dbg, uint32_t size, mrb_debug_breakpoint *bp)
|
||||
{
|
||||
uint32_t get_size = 0;
|
||||
|
||||
if ((mrb == NULL) || (dbg == NULL) || (bp == NULL)) {
|
||||
return MRB_DEBUG_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (dbg->bpnum >= size) {
|
||||
get_size = size;
|
||||
}
|
||||
else {
|
||||
get_size = dbg->bpnum;
|
||||
}
|
||||
|
||||
memcpy(bp, dbg->bp, sizeof(mrb_debug_breakpoint) * get_size);
|
||||
|
||||
return get_size;
|
||||
}
|
||||
|
||||
int32_t
|
||||
mrb_debug_get_break(mrb_state *mrb, mrb_debug_context *dbg, uint32_t bpno, mrb_debug_breakpoint *bp)
|
||||
{
|
||||
int32_t index;
|
||||
|
||||
if ((mrb == NULL) || (dbg == NULL) || (bp == NULL)) {
|
||||
return MRB_DEBUG_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
index = get_break_index(dbg, bpno);
|
||||
if (index == MRB_DEBUG_BREAK_INVALID_NO) {
|
||||
return MRB_DEBUG_BREAK_INVALID_NO;
|
||||
}
|
||||
|
||||
bp->bpno = dbg->bp[index].bpno;
|
||||
bp->enable = dbg->bp[index].enable;
|
||||
bp->point = dbg->bp[index].point;
|
||||
bp->type = dbg->bp[index].type;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t
|
||||
mrb_debug_delete_break(mrb_state *mrb, mrb_debug_context *dbg, uint32_t bpno)
|
||||
{
|
||||
uint32_t i;
|
||||
int32_t index;
|
||||
|
||||
if ((mrb == NULL) ||(dbg == NULL)) {
|
||||
return MRB_DEBUG_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
index = get_break_index(dbg, bpno);
|
||||
if (index == MRB_DEBUG_BREAK_INVALID_NO) {
|
||||
return MRB_DEBUG_BREAK_INVALID_NO;
|
||||
}
|
||||
|
||||
free_breakpoint(mrb, &dbg->bp[index]);
|
||||
|
||||
for(i = index ; i < dbg->bpnum; i++) {
|
||||
if ((i + 1) == dbg->bpnum) {
|
||||
memset(&dbg->bp[i], 0, sizeof(mrb_debug_breakpoint));
|
||||
}
|
||||
else {
|
||||
memcpy(&dbg->bp[i], &dbg->bp[i + 1], sizeof(mrb_debug_breakpoint));
|
||||
}
|
||||
}
|
||||
|
||||
dbg->bpnum--;
|
||||
|
||||
return MRB_DEBUG_OK;
|
||||
}
|
||||
|
||||
int32_t
|
||||
mrb_debug_delete_break_all(mrb_state *mrb, mrb_debug_context *dbg)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
if ((mrb == NULL) || (dbg == NULL)) {
|
||||
return MRB_DEBUG_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
for(i = 0 ; i < dbg->bpnum ; i++) {
|
||||
free_breakpoint(mrb, &dbg->bp[i]);
|
||||
}
|
||||
|
||||
dbg->bpnum = 0;
|
||||
|
||||
return MRB_DEBUG_OK;
|
||||
}
|
||||
|
||||
int32_t
|
||||
mrb_debug_enable_break(mrb_state *mrb, mrb_debug_context *dbg, uint32_t bpno)
|
||||
{
|
||||
int32_t index = 0;
|
||||
|
||||
if ((mrb == NULL) || (dbg == NULL)) {
|
||||
return MRB_DEBUG_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
index = get_break_index(dbg, bpno);
|
||||
if (index == MRB_DEBUG_BREAK_INVALID_NO) {
|
||||
return MRB_DEBUG_BREAK_INVALID_NO;
|
||||
}
|
||||
|
||||
dbg->bp[index].enable = TRUE;
|
||||
|
||||
return MRB_DEBUG_OK;
|
||||
}
|
||||
|
||||
int32_t
|
||||
mrb_debug_enable_break_all(mrb_state *mrb, mrb_debug_context *dbg)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
if ((mrb == NULL) || (dbg == NULL)) {
|
||||
return MRB_DEBUG_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
for(i = 0 ; i < dbg->bpnum; i++) {
|
||||
dbg->bp[i].enable = TRUE;
|
||||
}
|
||||
|
||||
return MRB_DEBUG_OK;
|
||||
}
|
||||
|
||||
int32_t
|
||||
mrb_debug_disable_break(mrb_state *mrb, mrb_debug_context *dbg, uint32_t bpno)
|
||||
{
|
||||
int32_t index = 0;
|
||||
|
||||
if ((mrb == NULL) || (dbg == NULL)) {
|
||||
return MRB_DEBUG_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
index = get_break_index(dbg, bpno);
|
||||
if (index == MRB_DEBUG_BREAK_INVALID_NO) {
|
||||
return MRB_DEBUG_BREAK_INVALID_NO;
|
||||
}
|
||||
|
||||
dbg->bp[index].enable = FALSE;
|
||||
|
||||
return MRB_DEBUG_OK;
|
||||
}
|
||||
|
||||
int32_t
|
||||
mrb_debug_disable_break_all(mrb_state *mrb, mrb_debug_context *dbg)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
if ((mrb == NULL) || (dbg == NULL)) {
|
||||
return MRB_DEBUG_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
for(i = 0 ; i < dbg->bpnum; i++) {
|
||||
dbg->bp[i].enable = FALSE;
|
||||
}
|
||||
|
||||
return MRB_DEBUG_OK;
|
||||
}
|
||||
|
||||
static mrb_bool
|
||||
check_start_pc_for_line(mrb_state *mrb, mrb_irep *irep, const mrb_code *pc, uint16_t line)
|
||||
{
|
||||
if (pc > irep->iseq) {
|
||||
if (line == mrb_debug_get_line(mrb, irep, pc - irep->iseq - 1)) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int32_t
|
||||
mrb_debug_check_breakpoint_line(mrb_state *mrb, mrb_debug_context *dbg, const char *file, uint16_t line)
|
||||
{
|
||||
mrb_debug_breakpoint *bp;
|
||||
mrb_debug_linepoint *line_p;
|
||||
uint32_t i;
|
||||
|
||||
if ((mrb == NULL) || (dbg == NULL) || (file == NULL) || (line <= 0)) {
|
||||
return MRB_DEBUG_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (!check_start_pc_for_line(mrb, dbg->irep, dbg->pc, line)) {
|
||||
return MRB_DEBUG_OK;
|
||||
}
|
||||
|
||||
bp = dbg->bp;
|
||||
for(i=0; i<dbg->bpnum; i++) {
|
||||
switch (bp->type) {
|
||||
case MRB_DEBUG_BPTYPE_LINE:
|
||||
if (bp->enable == TRUE) {
|
||||
line_p = &bp->point.linepoint;
|
||||
if ((strcmp(line_p->file, file) == 0) && (line_p->lineno == line)) {
|
||||
return bp->bpno;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MRB_DEBUG_BPTYPE_METHOD:
|
||||
break;
|
||||
case MRB_DEBUG_BPTYPE_NONE:
|
||||
default:
|
||||
return MRB_DEBUG_OK;
|
||||
}
|
||||
bp++;
|
||||
}
|
||||
return MRB_DEBUG_OK;
|
||||
}
|
||||
|
||||
|
||||
int32_t
|
||||
mrb_debug_check_breakpoint_method(mrb_state *mrb, mrb_debug_context *dbg, struct RClass *class_obj, mrb_sym method_sym, mrb_bool* isCfunc)
|
||||
{
|
||||
mrb_debug_breakpoint *bp;
|
||||
int32_t bpno;
|
||||
uint32_t i;
|
||||
|
||||
if ((mrb == NULL) || (dbg == NULL) || (class_obj == NULL)) {
|
||||
return MRB_DEBUG_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
bp = dbg->bp;
|
||||
for(i=0; i<dbg->bpnum; i++) {
|
||||
if (bp->type == MRB_DEBUG_BPTYPE_METHOD) {
|
||||
if (bp->enable == TRUE) {
|
||||
bpno = compare_break_method(mrb, bp, class_obj, method_sym, isCfunc);
|
||||
if (bpno > 0) {
|
||||
return bpno;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (bp->type == MRB_DEBUG_BPTYPE_NONE) {
|
||||
break;
|
||||
}
|
||||
bp++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
** apibreak.h
|
||||
**
|
||||
*/
|
||||
|
||||
#ifndef APIBREAK_H_
|
||||
#define APIBREAK_H_
|
||||
|
||||
#include <mruby.h>
|
||||
#include "mrdb.h"
|
||||
|
||||
int32_t mrb_debug_set_break_line(mrb_state *, mrb_debug_context *, const char *, uint16_t);
|
||||
int32_t mrb_debug_set_break_method(mrb_state *, mrb_debug_context *, const char *, const char *);
|
||||
int32_t mrb_debug_get_breaknum(mrb_state *, mrb_debug_context *);
|
||||
int32_t mrb_debug_get_break_all(mrb_state *, mrb_debug_context *, uint32_t, mrb_debug_breakpoint bp[]);
|
||||
int32_t mrb_debug_get_break(mrb_state *, mrb_debug_context *, uint32_t, mrb_debug_breakpoint *);
|
||||
int32_t mrb_debug_delete_break(mrb_state *, mrb_debug_context *, uint32_t);
|
||||
int32_t mrb_debug_delete_break_all(mrb_state *, mrb_debug_context *);
|
||||
int32_t mrb_debug_enable_break(mrb_state *, mrb_debug_context *, uint32_t);
|
||||
int32_t mrb_debug_enable_break_all(mrb_state *, mrb_debug_context *);
|
||||
int32_t mrb_debug_disable_break(mrb_state *, mrb_debug_context *, uint32_t);
|
||||
int32_t mrb_debug_disable_break_all(mrb_state *, mrb_debug_context *);
|
||||
int32_t mrb_debug_check_breakpoint_line(mrb_state *, mrb_debug_context *, const char *, uint16_t);
|
||||
int32_t mrb_debug_check_breakpoint_method(mrb_state *, mrb_debug_context *, struct RClass *, mrb_sym, mrb_bool*);
|
||||
|
||||
#endif /* APIBREAK_H_ */
|
||||
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
* apilist.c
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "mrdb.h"
|
||||
#include "mrdberror.h"
|
||||
#include "apilist.h"
|
||||
#include <mruby/compile.h>
|
||||
#include <mruby/irep.h>
|
||||
#include <mruby/debug.h>
|
||||
|
||||
#define LINE_BUF_SIZE MAX_COMMAND_LINE
|
||||
|
||||
typedef struct source_file {
|
||||
char *path;
|
||||
uint16_t lineno;
|
||||
FILE *fp;
|
||||
} source_file;
|
||||
|
||||
static void
|
||||
source_file_free(mrb_state *mrb, source_file *file)
|
||||
{
|
||||
if (file != NULL) {
|
||||
if (file->path != NULL) {
|
||||
mrb_free(mrb, file->path);
|
||||
}
|
||||
if (file->fp != NULL) {
|
||||
fclose(file->fp);
|
||||
file->fp = NULL;
|
||||
}
|
||||
mrb_free(mrb, file);
|
||||
}
|
||||
}
|
||||
|
||||
static char*
|
||||
build_path(mrb_state *mrb, const char *dir, const char *base)
|
||||
{
|
||||
int len;
|
||||
char *path = NULL;
|
||||
|
||||
len = strlen(base) + 1;
|
||||
|
||||
if (strcmp(dir, ".")) {
|
||||
len += strlen(dir) + sizeof("/") - 1;
|
||||
}
|
||||
|
||||
path = (char*)mrb_malloc(mrb, len);
|
||||
memset(path, 0, len);
|
||||
|
||||
if (strcmp(dir, ".")) {
|
||||
strcat(path, dir);
|
||||
strcat(path, "/");
|
||||
}
|
||||
strcat(path, base);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
static char*
|
||||
dirname(mrb_state *mrb, const char *path)
|
||||
{
|
||||
size_t len;
|
||||
const char *p;
|
||||
char *dir;
|
||||
|
||||
if (path == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
p = strrchr(path, '/');
|
||||
len = p != NULL ? (size_t)(p - path) : strlen(path);
|
||||
|
||||
dir = (char*)mrb_malloc(mrb, len + 1);
|
||||
strncpy(dir, path, len);
|
||||
dir[len] = '\0';
|
||||
|
||||
return dir;
|
||||
}
|
||||
|
||||
static source_file*
|
||||
source_file_new(mrb_state *mrb, mrb_debug_context *dbg, char *filename)
|
||||
{
|
||||
source_file *file;
|
||||
|
||||
file = (source_file*)mrb_malloc(mrb, sizeof(source_file));
|
||||
|
||||
memset(file, '\0', sizeof(source_file));
|
||||
file->fp = fopen(filename, "rb");
|
||||
|
||||
if (file->fp == NULL) {
|
||||
source_file_free(mrb, file);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
file->lineno = 1;
|
||||
file->path = (char*)mrb_malloc(mrb, strlen(filename) + 1);
|
||||
strcpy(file->path, filename);
|
||||
return file;
|
||||
}
|
||||
|
||||
static mrb_bool
|
||||
remove_newlines(char *s, FILE *fp)
|
||||
{
|
||||
int c;
|
||||
char *p;
|
||||
size_t len;
|
||||
|
||||
if ((len = strlen(s)) == 0) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
p = s + len - 1;
|
||||
|
||||
if (*p != '\r' && *p != '\n') {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (*p == '\r') {
|
||||
/* peek the next character and skip '\n' */
|
||||
if ((c = fgetc(fp)) != '\n') {
|
||||
ungetc(c, fp);
|
||||
}
|
||||
}
|
||||
|
||||
/* remove trailing newline characters */
|
||||
while (s <= p && (*p == '\r' || *p == '\n')) {
|
||||
*p-- = '\0';
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
show_lines(source_file *file, uint16_t line_min, uint16_t line_max)
|
||||
{
|
||||
char buf[LINE_BUF_SIZE];
|
||||
int show_lineno = 1, found_newline = 0, is_printed = 0;
|
||||
|
||||
if (file->fp == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (fgets(buf, sizeof(buf), file->fp) != NULL) {
|
||||
found_newline = remove_newlines(buf, file->fp);
|
||||
|
||||
if (line_min <= file->lineno) {
|
||||
if (show_lineno) {
|
||||
printf("%-8d", file->lineno);
|
||||
}
|
||||
show_lineno = found_newline;
|
||||
printf(found_newline ? "%s\n" : "%s", buf);
|
||||
is_printed = 1;
|
||||
}
|
||||
|
||||
if (found_newline) {
|
||||
if (line_max < ++file->lineno) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_printed && !found_newline) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
char*
|
||||
mrb_debug_get_source(mrb_state *mrb, mrdb_state *mrdb, const char *srcpath, const char *filename)
|
||||
{
|
||||
int i;
|
||||
FILE *fp;
|
||||
const char *search_path[3];
|
||||
char *path = NULL;
|
||||
const char *srcname = strrchr(filename, '/');
|
||||
|
||||
if (srcname) srcname++;
|
||||
else srcname = filename;
|
||||
|
||||
search_path[0] = srcpath;
|
||||
search_path[1] = dirname(mrb, mrb_debug_get_filename(mrb, mrdb->dbg->irep, 0));
|
||||
search_path[2] = ".";
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
if (search_path[i] == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((path = build_path(mrb, search_path[i], srcname)) == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((fp = fopen(path, "rb")) == NULL) {
|
||||
mrb_free(mrb, path);
|
||||
path = NULL;
|
||||
continue;
|
||||
}
|
||||
fclose(fp);
|
||||
break;
|
||||
}
|
||||
|
||||
mrb_free(mrb, (void *)search_path[1]);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
int32_t
|
||||
mrb_debug_list(mrb_state *mrb, mrb_debug_context *dbg, char *filename, uint16_t line_min, uint16_t line_max)
|
||||
{
|
||||
char *ext;
|
||||
source_file *file;
|
||||
|
||||
if (mrb == NULL || dbg == NULL || filename == NULL) {
|
||||
return MRB_DEBUG_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
ext = strrchr(filename, '.');
|
||||
|
||||
if (ext == NULL || strcmp(ext, ".rb")) {
|
||||
printf("List command only supports .rb file.\n");
|
||||
return MRB_DEBUG_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (line_min > line_max) {
|
||||
return MRB_DEBUG_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if ((file = source_file_new(mrb, dbg, filename)) != NULL) {
|
||||
show_lines(file, line_min, line_max);
|
||||
source_file_free(mrb, file);
|
||||
return MRB_DEBUG_OK;
|
||||
}
|
||||
else {
|
||||
printf("Invalid source file named %s.\n", filename);
|
||||
return MRB_DEBUG_INVALID_ARGUMENT;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* apilist.h
|
||||
*/
|
||||
|
||||
#ifndef APILIST_H_
|
||||
#define APILIST_H_
|
||||
|
||||
#include <mruby.h>
|
||||
#include "mrdb.h"
|
||||
|
||||
int32_t mrb_debug_list(mrb_state *, mrb_debug_context *, char *, uint16_t, uint16_t);
|
||||
char* mrb_debug_get_source(mrb_state *, mrdb_state *, const char *, const char *);
|
||||
|
||||
#endif /* APILIST_H_ */
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
** apiprint.c
|
||||
**
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "mrdb.h"
|
||||
#include <mruby/value.h>
|
||||
#include <mruby/class.h>
|
||||
#include <mruby/compile.h>
|
||||
#include <mruby/error.h>
|
||||
#include <mruby/numeric.h>
|
||||
#include <mruby/string.h>
|
||||
#include "apiprint.h"
|
||||
|
||||
static void
|
||||
mrdb_check_syntax(mrb_state *mrb, mrb_debug_context *dbg, const char *expr, size_t len)
|
||||
{
|
||||
mrbc_context *c;
|
||||
|
||||
c = mrbc_context_new(mrb);
|
||||
c->no_exec = TRUE;
|
||||
c->capture_errors = TRUE;
|
||||
mrbc_filename(mrb, c, (const char*)dbg->prvfile);
|
||||
c->lineno = dbg->prvline;
|
||||
|
||||
/* Load program */
|
||||
mrb_load_nstring_cxt(mrb, expr, len, c);
|
||||
|
||||
mrbc_context_free(mrb, c);
|
||||
}
|
||||
|
||||
mrb_value
|
||||
mrb_debug_eval(mrb_state *mrb, mrb_debug_context *dbg, const char *expr, size_t len, mrb_bool *exc, int direct_eval)
|
||||
{
|
||||
void (*tmp)(struct mrb_state *, struct mrb_irep *, const mrb_code *, mrb_value *);
|
||||
mrb_value ruby_code;
|
||||
mrb_value s;
|
||||
mrb_value v;
|
||||
mrb_value recv;
|
||||
|
||||
/* disable code_fetch_hook */
|
||||
tmp = mrb->code_fetch_hook;
|
||||
mrb->code_fetch_hook = NULL;
|
||||
|
||||
mrdb_check_syntax(mrb, dbg, expr, len);
|
||||
if (mrb->exc) {
|
||||
v = mrb_obj_value(mrb->exc);
|
||||
mrb->exc = 0;
|
||||
}
|
||||
else if (direct_eval) {
|
||||
recv = dbg->regs[0];
|
||||
|
||||
v = mrb_funcall(mrb, recv, expr, 0);
|
||||
}
|
||||
else {
|
||||
/*
|
||||
* begin
|
||||
* expr
|
||||
* rescue => e
|
||||
* e
|
||||
* end
|
||||
*/
|
||||
ruby_code = mrb_str_new_lit(mrb, "begin\n");
|
||||
ruby_code = mrb_str_cat(mrb, ruby_code, expr, len);
|
||||
ruby_code = mrb_str_cat_lit(mrb, ruby_code, "\nrescue => e\ne\nend");
|
||||
|
||||
recv = dbg->regs[0];
|
||||
|
||||
v = mrb_funcall(mrb, recv, "instance_eval", 1, ruby_code);
|
||||
}
|
||||
|
||||
if (exc) {
|
||||
*exc = mrb_obj_is_kind_of(mrb, v, mrb->eException_class);
|
||||
}
|
||||
|
||||
s = mrb_inspect(mrb, v);
|
||||
|
||||
/* enable code_fetch_hook */
|
||||
mrb->code_fetch_hook = tmp;
|
||||
|
||||
return s;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* apiprint.h
|
||||
*/
|
||||
|
||||
#ifndef APIPRINT_H_
|
||||
#define APIPRINT_H_
|
||||
|
||||
#include <mruby.h>
|
||||
#include "mrdb.h"
|
||||
|
||||
mrb_value mrb_debug_eval(mrb_state*, mrb_debug_context*, const char*, size_t, mrb_bool*, int);
|
||||
|
||||
#endif /* APIPRINT_H_ */
|
||||
@@ -0,0 +1,436 @@
|
||||
/*
|
||||
** 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 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; 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,508 @@
|
||||
/*
|
||||
** cmdmisc.c - mruby debugger miscellaneous command functions
|
||||
**
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "apilist.h"
|
||||
#include <mruby/compile.h>
|
||||
|
||||
typedef struct help_msg {
|
||||
const char *cmd1;
|
||||
const char *cmd2;
|
||||
const char *short_msg;
|
||||
const char *long_msg;
|
||||
} help_msg;
|
||||
|
||||
static help_msg help_msg_list[] = {
|
||||
{
|
||||
"b[reak]", NULL, "Set breakpoint",
|
||||
"Usage: break [file:]line\n"
|
||||
" break [class:]method\n"
|
||||
"\n"
|
||||
"Set breakpoint at specified line or method.\n"
|
||||
"If \'[file:]line\' is specified, break at start of code for that line (in a file).\n"
|
||||
"If \'[class:]method\' is specified, break at start of code for that method (of the class).\n"
|
||||
},
|
||||
{
|
||||
"c[ontinue]", NULL, "Continue program being debugged",
|
||||
"Usage: continue [N]\n"
|
||||
"\n"
|
||||
"Continue program stopped by a breakpoint.\n"
|
||||
"If N, which is non negative value, is passed,\n"
|
||||
"proceed program until the N-th breakpoint is coming.\n"
|
||||
"If N is not passed, N is assumed 1.\n"
|
||||
},
|
||||
{
|
||||
"d[elete]", NULL, "Delete some breakpoints",
|
||||
"Usage: delete [bpno1 [bpno2 [... [bpnoN]]]]\n"
|
||||
"\n"
|
||||
"Delete some breakpoints.\n"
|
||||
"Arguments are breakpoint numbers with spaces in between.\n"
|
||||
"To delete all breakpoints, give no argument.\n"
|
||||
},
|
||||
{
|
||||
"dis[able]", NULL, "Disable some breakpoints",
|
||||
"Usage: disable [bpno1 [bpno2 [... [bpnoN]]]]\n"
|
||||
"\n"
|
||||
"Disable some breakpoints.\n"
|
||||
"Arguments are breakpoint numbers with spaces in between.\n"
|
||||
"To disable all breakpoints, give no argument.\n"
|
||||
},
|
||||
{
|
||||
"en[able]", NULL, "Enable some breakpoints",
|
||||
"Usage: enable [bpno1 [bpno2 [... [bpnoN]]]]\n"
|
||||
"\n"
|
||||
"Enable some breakpoints.\n"
|
||||
"Arguments are breakpoint numbers with spaces in between.\n"
|
||||
"To enable all breakpoints, give no argument.\n"
|
||||
},
|
||||
{
|
||||
"ev[al]", NULL, "Evaluate expression",
|
||||
"Usage: eval expr\n"
|
||||
"\n"
|
||||
"It evaluates and prints the value of the mruby expression.\n"
|
||||
"This is equivalent to the \'print\' command.\n"
|
||||
},
|
||||
{
|
||||
"h[elp]", NULL, "Print this help",
|
||||
"Usage: help [command]\n"
|
||||
"\n"
|
||||
"With no arguments, help displays a short list of commands.\n"
|
||||
"With a command name as help argument, help displays how to use that command.\n"
|
||||
},
|
||||
{
|
||||
"i[nfo]", "b[reakpoints]", "Status of breakpoints",
|
||||
"Usage: info breakpoints [bpno1 [bpno2 [... [bpnoN]]]]\n"
|
||||
"\n"
|
||||
"Status of specified breakpoints (all user-settable breakpoints if no argument).\n"
|
||||
"Arguments are breakpoint numbers with spaces in between.\n"
|
||||
},
|
||||
{
|
||||
"i[nfo]", "l[ocals]", "Print name of local variables",
|
||||
"Usage: info locals\n"
|
||||
"\n"
|
||||
"Print name of local variables.\n"
|
||||
},
|
||||
{
|
||||
"l[ist]", NULL, "List specified line",
|
||||
"Usage: list\n"
|
||||
" list first[,last]\n"
|
||||
" list filename:first[,last]\n"
|
||||
"\n"
|
||||
"Print lines from a source file.\n"
|
||||
"\n"
|
||||
"With first and last, list prints lines from first to last.\n"
|
||||
"When last is empty, it stands for ten lines away from first.\n"
|
||||
"With filename, list prints lines in the specified source file.\n"
|
||||
},
|
||||
{
|
||||
"p[rint]", NULL, "Print value of expression",
|
||||
"Usage: print expr\n"
|
||||
"\n"
|
||||
"It evaluates and prints the value of the mruby expression.\n"
|
||||
"This is equivalent to the \'eval\' command.\n"
|
||||
},
|
||||
{
|
||||
"q[uit]", NULL, "Exit mrdb",
|
||||
"Usage: quit\n"
|
||||
"\n"
|
||||
"Exit mrdb.\n"
|
||||
},
|
||||
{
|
||||
"r[un]", NULL, "Start debugged program",
|
||||
"Usage: run\n"
|
||||
"\n"
|
||||
"Start debugged program.\n"
|
||||
},
|
||||
{
|
||||
"s[tep]", NULL, "Step program until it reaches a different source line",
|
||||
"Usage: step\n"
|
||||
"\n"
|
||||
"Step program until it reaches a different source line.\n"
|
||||
},
|
||||
{ NULL, NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
typedef struct listcmd_parser_state {
|
||||
mrb_bool parse_error;
|
||||
mrb_bool has_line_min;
|
||||
mrb_bool has_line_max;
|
||||
char *filename;
|
||||
uint16_t line_min;
|
||||
uint16_t line_max;
|
||||
} listcmd_parser_state;
|
||||
|
||||
static listcmd_parser_state*
|
||||
listcmd_parser_state_new(mrb_state *mrb)
|
||||
{
|
||||
listcmd_parser_state *st = (listcmd_parser_state*)mrb_malloc(mrb, sizeof(listcmd_parser_state));
|
||||
memset(st, 0, sizeof(listcmd_parser_state));
|
||||
return st;
|
||||
}
|
||||
|
||||
static void
|
||||
listcmd_parser_state_free(mrb_state *mrb, listcmd_parser_state *st)
|
||||
{
|
||||
if (st != NULL) {
|
||||
if (st->filename != NULL) {
|
||||
mrb_free(mrb, st->filename);
|
||||
}
|
||||
mrb_free(mrb, st);
|
||||
}
|
||||
}
|
||||
|
||||
static mrb_bool
|
||||
parse_uint(char **sp, uint16_t *n)
|
||||
{
|
||||
char *p;
|
||||
int i;
|
||||
|
||||
if (*sp == NULL || **sp == '\0') {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
for (p = *sp; *p != '\0' && ISDIGIT(*p); p++) ;
|
||||
|
||||
if (p != *sp && (i = atoi(*sp)) >= 0) {
|
||||
*n = (uint16_t)i;
|
||||
*sp = p;
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static mrb_bool
|
||||
skip_char(char **sp, char c)
|
||||
{
|
||||
if (*sp != NULL && **sp == c) {
|
||||
++*sp;
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static mrb_bool
|
||||
parse_lineno(mrb_state *mrb, char **sp, listcmd_parser_state *st)
|
||||
{
|
||||
if (*sp == NULL || **sp == '\0') {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
st->has_line_min = FALSE;
|
||||
st->has_line_max = FALSE;
|
||||
|
||||
if (parse_uint(sp, &st->line_min)) {
|
||||
st->has_line_min = TRUE;
|
||||
}
|
||||
else {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (skip_char(sp, ',')) {
|
||||
if (parse_uint(sp, &st->line_max)) {
|
||||
st->has_line_max = TRUE;
|
||||
}
|
||||
else {
|
||||
st->parse_error = TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static mrb_bool
|
||||
parse_filename(mrb_state *mrb, char **sp, listcmd_parser_state *st)
|
||||
{
|
||||
char *p;
|
||||
int len;
|
||||
|
||||
if (st->filename != NULL) {
|
||||
mrb_free(mrb, st->filename);
|
||||
st->filename = NULL;
|
||||
}
|
||||
|
||||
if ((p = strchr(*sp, ':')) != NULL) {
|
||||
len = p - *sp;
|
||||
}
|
||||
else {
|
||||
len = strlen(*sp);
|
||||
}
|
||||
|
||||
if (len > 0) {
|
||||
st->filename = (char*)mrb_malloc(mrb, len + 1);
|
||||
strncpy(st->filename, *sp, len);
|
||||
st->filename[len] = '\0';
|
||||
*sp += len;
|
||||
return TRUE;
|
||||
}
|
||||
else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
char*
|
||||
replace_ext(mrb_state *mrb, const char *filename, const char *ext)
|
||||
{
|
||||
size_t len;
|
||||
const char *p;
|
||||
char *s;
|
||||
|
||||
if (filename == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((p = strrchr(filename, '.')) != NULL && strchr(p, '/') == NULL) {
|
||||
len = p - filename;
|
||||
}
|
||||
else {
|
||||
len = strlen(filename);
|
||||
}
|
||||
|
||||
s = (char*)mrb_malloc(mrb, len + strlen(ext) + 1);
|
||||
memset(s, '\0', len + strlen(ext) + 1);
|
||||
strncpy(s, filename, len);
|
||||
strcat(s, ext);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static mrb_bool
|
||||
parse_listcmd_args(mrb_state *mrb, mrdb_state *mrdb, listcmd_parser_state *st)
|
||||
{
|
||||
char *p;
|
||||
|
||||
switch (mrdb->wcnt) {
|
||||
case 2:
|
||||
p = mrdb->words[1];
|
||||
|
||||
/* mrdb->words[1] ::= <lineno> | <filename> ':' <lineno> | <filename> */
|
||||
if (!parse_lineno(mrb, &p, st)) {
|
||||
if (parse_filename(mrb, &p, st)) {
|
||||
if (skip_char(&p, ':')) {
|
||||
if (!parse_lineno(mrb, &p, st)) {
|
||||
st->parse_error = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
st->parse_error = TRUE;
|
||||
}
|
||||
}
|
||||
if (*p != '\0') {
|
||||
st->parse_error = TRUE;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
case 0:
|
||||
/* do nothing */
|
||||
break;
|
||||
default:
|
||||
st->parse_error = TRUE;
|
||||
printf("too many arguments\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (!st->parse_error) {
|
||||
if (!st->has_line_min) {
|
||||
st->line_min = (!st->filename && mrdb->dbg->prvline > 0) ? mrdb->dbg->prvline : 1;
|
||||
}
|
||||
|
||||
if (!st->has_line_max) {
|
||||
st->line_max = st->line_min + 9;
|
||||
}
|
||||
|
||||
if (st->filename == NULL) {
|
||||
if (mrdb->dbg->prvfile && strcmp(mrdb->dbg->prvfile, "-")) {
|
||||
st->filename = replace_ext(mrb, mrdb->dbg->prvfile, ".rb");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (st->parse_error || st->filename == NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static mrb_bool
|
||||
check_cmd_pattern(const char *pattern, const char *cmd)
|
||||
{
|
||||
const char *lbracket, *rbracket, *p, *q;
|
||||
|
||||
if (pattern == NULL && cmd == NULL) {
|
||||
return TRUE;
|
||||
}
|
||||
if (pattern == NULL || cmd == NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
if ((lbracket = strchr(pattern, '[')) == NULL) {
|
||||
return !strcmp(pattern, cmd);
|
||||
}
|
||||
if ((rbracket = strchr(pattern, ']')) == NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
if (strncmp(pattern, cmd, lbracket - pattern)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
p = lbracket + 1;
|
||||
q = (char *)cmd + (lbracket - pattern);
|
||||
|
||||
for ( ; p < rbracket && *q != '\0'; p++, q++) {
|
||||
if (*p != *q) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return *q == '\0';
|
||||
}
|
||||
|
||||
static help_msg*
|
||||
get_help_msg(char *cmd1, char *cmd2)
|
||||
{
|
||||
help_msg *p;
|
||||
|
||||
if (cmd1 == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
for (p = help_msg_list; p->cmd1 != NULL; p++) {
|
||||
if (check_cmd_pattern(p->cmd1, cmd1) && check_cmd_pattern(p->cmd2, cmd2)) {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static mrb_bool
|
||||
show_short_help(void)
|
||||
{
|
||||
help_msg *p;
|
||||
|
||||
printf("Commands\n");
|
||||
|
||||
for (p = help_msg_list; p->cmd1 != NULL; p++) {
|
||||
if (p->cmd2 == NULL) {
|
||||
printf(" %s -- %s\n", p->cmd1, p->short_msg);
|
||||
}
|
||||
else {
|
||||
printf(" %s %s -- %s\n", p->cmd1, p->cmd2, p->short_msg);
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static mrb_bool
|
||||
show_long_help(char *cmd1, char *cmd2)
|
||||
{
|
||||
help_msg *help;
|
||||
|
||||
if ((help = get_help_msg(cmd1, cmd2)) == NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
printf("%s", help->long_msg);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
dbgcmd_state
|
||||
dbgcmd_list(mrb_state *mrb, mrdb_state *mrdb)
|
||||
{
|
||||
char *filename;
|
||||
listcmd_parser_state *st = listcmd_parser_state_new(mrb);
|
||||
|
||||
if (parse_listcmd_args(mrb, mrdb, st)) {
|
||||
if ((filename = mrb_debug_get_source(mrb, mrdb, mrdb->srcpath, st->filename)) == NULL) {
|
||||
filename = st->filename;
|
||||
}
|
||||
mrb_debug_list(mrb, mrdb->dbg, filename, st->line_min, st->line_max);
|
||||
|
||||
if (filename != NULL && filename != st->filename) {
|
||||
mrb_free(mrb, filename);
|
||||
}
|
||||
listcmd_parser_state_free(mrb, st);
|
||||
}
|
||||
|
||||
return DBGST_PROMPT;
|
||||
}
|
||||
|
||||
dbgcmd_state
|
||||
dbgcmd_help(mrb_state *mrb, mrdb_state *mrdb)
|
||||
{
|
||||
mrb_bool is_valid;
|
||||
int i;
|
||||
|
||||
switch (mrdb->wcnt) {
|
||||
case 0:
|
||||
case 1:
|
||||
is_valid = show_short_help();
|
||||
break;
|
||||
case 2:
|
||||
is_valid = show_long_help(mrdb->words[1], NULL);
|
||||
break;
|
||||
case 3:
|
||||
is_valid = show_long_help(mrdb->words[1], mrdb->words[2]);
|
||||
break;
|
||||
default:
|
||||
is_valid = FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!is_valid) {
|
||||
printf("Invalid command \"");
|
||||
for (i = 1; i < mrdb->wcnt; i++) {
|
||||
printf("%s%s", i == 1 ? "" : " ", mrdb->words[i]);
|
||||
}
|
||||
printf("\". Try \"help\".\n");
|
||||
}
|
||||
|
||||
return DBGST_PROMPT;
|
||||
}
|
||||
|
||||
dbgcmd_state
|
||||
dbgcmd_quit(mrb_state *mrb, mrdb_state *mrdb)
|
||||
{
|
||||
switch (mrdb->dbg->xm) {
|
||||
case DBG_RUN:
|
||||
case DBG_STEP:
|
||||
case DBG_NEXT:
|
||||
while (1) {
|
||||
char c;
|
||||
int buf;
|
||||
|
||||
printf("The program is running. Exit anyway? (y or n) ");
|
||||
fflush(stdout);
|
||||
|
||||
if ((buf = getchar()) == EOF) {
|
||||
mrdb->dbg->xm = DBG_QUIT;
|
||||
break;
|
||||
}
|
||||
c = buf;
|
||||
while (buf != '\n' && (buf = getchar()) != EOF) ;
|
||||
|
||||
if (c == 'y' || c == 'Y') {
|
||||
mrdb->dbg->xm = DBG_QUIT;
|
||||
break;
|
||||
}
|
||||
else if (c == 'n' || c == 'N') {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
printf("Please answer y or n.\n");
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
mrdb->dbg->xm = DBG_QUIT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (mrdb->dbg->xm == DBG_QUIT) {
|
||||
struct RClass *exc;
|
||||
exc = mrb_define_class(mrb, "DebuggerExit", mrb->eException_class);
|
||||
mrb_raise(mrb, exc, "Exit mrdb.");
|
||||
}
|
||||
return DBGST_PROMPT;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
** cmdprint.c - mruby debugger print command functions
|
||||
**
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "mrdb.h"
|
||||
#include <mruby/value.h>
|
||||
#include <mruby/class.h>
|
||||
#include <mruby/compile.h>
|
||||
#include <mruby/error.h>
|
||||
#include <mruby/numeric.h>
|
||||
#include <mruby/string.h>
|
||||
#include "apiprint.h"
|
||||
|
||||
dbgcmd_state
|
||||
dbgcmd_print(mrb_state *mrb, mrdb_state *mrdb)
|
||||
{
|
||||
mrb_value expr;
|
||||
mrb_value result;
|
||||
uint8_t wcnt;
|
||||
int ai;
|
||||
|
||||
if (mrdb->wcnt <= 1) {
|
||||
puts("Parameter not specified.");
|
||||
return DBGST_PROMPT;
|
||||
}
|
||||
|
||||
ai = mrb_gc_arena_save(mrb);
|
||||
|
||||
/* eval expr */
|
||||
expr = mrb_str_new_cstr(mrb, NULL);
|
||||
for (wcnt=1; wcnt<mrdb->wcnt; wcnt++) {
|
||||
expr = mrb_str_cat_lit(mrb, expr, " ");
|
||||
expr = mrb_str_cat_cstr(mrb, expr, mrdb->words[wcnt]);
|
||||
}
|
||||
|
||||
result = mrb_debug_eval(mrb, mrdb->dbg, RSTRING_PTR(expr), RSTRING_LEN(expr), NULL, 0);
|
||||
|
||||
/* $print_no = result */
|
||||
printf("$%lu = ", (unsigned long)mrdb->print_no++);
|
||||
fwrite(RSTRING_PTR(result), RSTRING_LEN(result), 1, stdout);
|
||||
putc('\n', stdout);
|
||||
|
||||
if (mrdb->print_no == 0) {
|
||||
mrdb->print_no = 1;
|
||||
}
|
||||
|
||||
mrb_gc_arena_restore(mrb, ai);
|
||||
|
||||
return DBGST_PROMPT;
|
||||
}
|
||||
|
||||
dbgcmd_state
|
||||
dbgcmd_eval(mrb_state *mrb, mrdb_state *mrdb)
|
||||
{
|
||||
return dbgcmd_print(mrb, mrdb);
|
||||
}
|
||||
|
||||
dbgcmd_state
|
||||
dbgcmd_info_local(mrb_state *mrb, mrdb_state *mrdb)
|
||||
{
|
||||
mrb_value result;
|
||||
mrb_value s;
|
||||
int ai;
|
||||
|
||||
ai = mrb_gc_arena_save(mrb);
|
||||
|
||||
result = mrb_debug_eval(mrb, mrdb->dbg, "local_variables", 0, NULL, 1);
|
||||
|
||||
s = mrb_str_cat_lit(mrb, result, "\0");
|
||||
printf("$%lu = %s\n", (unsigned long)mrdb->print_no++, RSTRING_PTR(s));
|
||||
|
||||
if (mrdb->print_no == 0) {
|
||||
mrdb->print_no = 1;
|
||||
}
|
||||
|
||||
mrb_gc_arena_restore(mrb, ai);
|
||||
|
||||
return DBGST_PROMPT;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
** cmdrun.c - mruby debugger run command functions
|
||||
**
|
||||
*/
|
||||
|
||||
#include <mruby/opcode.h>
|
||||
#include "mrdb.h"
|
||||
|
||||
dbgcmd_state
|
||||
dbgcmd_run(mrb_state *mrb, mrdb_state *mrdb)
|
||||
{
|
||||
mrb_debug_context *dbg = mrdb->dbg;
|
||||
|
||||
if (dbg->xm == DBG_INIT){
|
||||
dbg->xm = DBG_RUN;
|
||||
}
|
||||
else {
|
||||
dbg->xm = DBG_QUIT;
|
||||
if (dbg->xphase == DBG_PHASE_RUNNING){
|
||||
struct RClass *exc;
|
||||
puts("Start it from the beginning.");
|
||||
exc = mrb_define_class(mrb, "DebuggerRestart", mrb->eException_class);
|
||||
mrb_raise(mrb, exc, "Restart mrdb.");
|
||||
}
|
||||
}
|
||||
|
||||
return DBGST_RESTART;
|
||||
}
|
||||
|
||||
dbgcmd_state
|
||||
dbgcmd_continue(mrb_state *mrb, mrdb_state *mrdb)
|
||||
{
|
||||
mrb_debug_context *dbg = mrdb->dbg;
|
||||
int ccnt = 1;
|
||||
|
||||
if (mrdb->wcnt > 1){
|
||||
sscanf(mrdb->words[1], "%d", &ccnt);
|
||||
}
|
||||
dbg->ccnt = (uint16_t)(ccnt > 0 ? ccnt : 1); /* count of continue */
|
||||
|
||||
if (dbg->xphase == DBG_PHASE_AFTER_RUN){
|
||||
puts("The program is not running.");
|
||||
dbg->xm = DBG_QUIT;
|
||||
}
|
||||
else {
|
||||
dbg->xm = DBG_RUN;
|
||||
}
|
||||
return DBGST_CONTINUE;
|
||||
}
|
||||
|
||||
dbgcmd_state
|
||||
dbgcmd_step(mrb_state *mrb, mrdb_state *mrdb)
|
||||
{
|
||||
mrdb->dbg->xm = DBG_STEP;
|
||||
return DBGST_CONTINUE;
|
||||
}
|
||||
|
||||
dbgcmd_state
|
||||
dbgcmd_next(mrb_state *mrb, mrdb_state *mrdb)
|
||||
{
|
||||
mrdb->dbg->xm = DBG_NEXT;
|
||||
mrdb->dbg->prvci = mrb->c->ci;
|
||||
return DBGST_CONTINUE;
|
||||
}
|
||||
@@ -0,0 +1,761 @@
|
||||
/*
|
||||
** mrdb.c - mruby debugger
|
||||
**
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <mruby.h>
|
||||
#include <mruby/dump.h>
|
||||
#include <mruby/debug.h>
|
||||
#include <mruby/class.h>
|
||||
#include <mruby/opcode.h>
|
||||
#include <mruby/variable.h>
|
||||
|
||||
#include "mrdb.h"
|
||||
#include "apibreak.h"
|
||||
#include "apilist.h"
|
||||
|
||||
void mrdb_state_free(mrb_state *);
|
||||
|
||||
static mrb_debug_context *_debug_context = NULL;
|
||||
static mrdb_state *_mrdb_state = NULL;
|
||||
|
||||
struct _args {
|
||||
FILE *rfp;
|
||||
char* fname;
|
||||
char* srcpath;
|
||||
int argc;
|
||||
char** argv;
|
||||
mrb_bool mrbfile : 1;
|
||||
};
|
||||
|
||||
typedef struct debug_command {
|
||||
const char *cmd1;
|
||||
const char *cmd2;
|
||||
uint8_t len1;
|
||||
uint8_t len2;
|
||||
uint8_t div;
|
||||
debug_command_id id;
|
||||
debug_command_func func;
|
||||
} debug_command;
|
||||
|
||||
static const debug_command debug_command_list[] = {
|
||||
{"break", NULL, 1, 0, 0, DBGCMD_BREAK, dbgcmd_break}, /* b[reak] */
|
||||
{"continue", NULL, 1, 0, 0, DBGCMD_CONTINUE, dbgcmd_continue}, /* c[ontinue] */
|
||||
{"delete", NULL, 1, 0, 1, DBGCMD_DELETE, dbgcmd_delete}, /* d[elete] */
|
||||
{"disable", NULL, 3, 0, 1, DBGCMD_DISABLE, dbgcmd_disable}, /* dis[able] */
|
||||
{"enable", NULL, 2, 0, 1, DBGCMD_ENABLE, dbgcmd_enable}, /* en[able] */
|
||||
{"eval", NULL, 2, 0, 0, DBGCMD_EVAL, dbgcmd_eval}, /* ev[al] */
|
||||
{"help", NULL, 1, 0, 1, DBGCMD_HELP, dbgcmd_help}, /* h[elp] */
|
||||
{"info", "breakpoints", 1, 1, 1, DBGCMD_INFO_BREAK, dbgcmd_info_break}, /* i[nfo] b[reakpoints] */
|
||||
{"info", "locals", 1, 1, 0, DBGCMD_INFO_LOCAL, dbgcmd_info_local}, /* i[nfo] l[ocals] */
|
||||
{"list", NULL, 1, 0, 1, DBGCMD_LIST, dbgcmd_list}, /* l[ist] */
|
||||
{"print", NULL, 1, 0, 0, DBGCMD_PRINT, dbgcmd_print}, /* p[rint] */
|
||||
{"quit", NULL, 1, 0, 0, DBGCMD_QUIT, dbgcmd_quit}, /* q[uit] */
|
||||
{"run", NULL, 1, 0, 0, DBGCMD_RUN, dbgcmd_run}, /* r[un] */
|
||||
{"step", NULL, 1, 0, 1, DBGCMD_STEP, dbgcmd_step}, /* s[tep] */
|
||||
{"next", NULL, 1, 0, 1, DBGCMD_NEXT, dbgcmd_next}, /* n[ext] */
|
||||
{NULL}
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
usage(const char *name)
|
||||
{
|
||||
static const char *const usage_msg[] = {
|
||||
"switches:",
|
||||
"-b load and execute RiteBinary (mrb) file",
|
||||
"-d specify source directory",
|
||||
"--version print the version",
|
||||
"--copyright print the copyright",
|
||||
NULL
|
||||
};
|
||||
const char *const *p = usage_msg;
|
||||
|
||||
printf("Usage: %s [switches] programfile\n", name);
|
||||
while (*p) {
|
||||
printf(" %s\n", *p++);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
parse_args(mrb_state *mrb, int argc, char **argv, struct _args *args)
|
||||
{
|
||||
char **origargv = argv;
|
||||
static const struct _args args_zero = { 0 };
|
||||
|
||||
*args = args_zero;
|
||||
|
||||
for (argc--,argv++; argc > 0; argc--,argv++) {
|
||||
char *item;
|
||||
if (argv[0][0] != '-') break;
|
||||
|
||||
item = argv[0] + 1;
|
||||
switch (*item++) {
|
||||
case 'b':
|
||||
args->mrbfile = TRUE;
|
||||
break;
|
||||
case 'd':
|
||||
if (item[0]) {
|
||||
goto append_srcpath;
|
||||
}
|
||||
else if (argc > 1) {
|
||||
argc--; argv++;
|
||||
item = argv[0];
|
||||
append_srcpath:
|
||||
if (!args->srcpath) {
|
||||
size_t buflen;
|
||||
char *buf;
|
||||
|
||||
buflen = strlen(item) + 1;
|
||||
buf = (char *)mrb_malloc(mrb, buflen);
|
||||
memcpy(buf, item, buflen);
|
||||
args->srcpath = buf;
|
||||
}
|
||||
else {
|
||||
size_t srcpathlen;
|
||||
size_t itemlen;
|
||||
|
||||
srcpathlen = strlen(args->srcpath);
|
||||
itemlen = strlen(item);
|
||||
args->srcpath =
|
||||
(char *)mrb_realloc(mrb, args->srcpath, srcpathlen + itemlen + 2);
|
||||
args->srcpath[srcpathlen] = '\n';
|
||||
memcpy(args->srcpath + srcpathlen + 1, item, itemlen + 1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
printf("%s: No path specified for -d\n", *origargv);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
break;
|
||||
case '-':
|
||||
if (strcmp((*argv) + 2, "version") == 0) {
|
||||
mrb_show_version(mrb);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
else if (strcmp((*argv) + 2, "copyright") == 0) {
|
||||
mrb_show_copyright(mrb);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
default:
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (args->rfp == NULL) {
|
||||
if (*argv == NULL) {
|
||||
printf("%s: Program file not specified.\n", *origargv);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
else {
|
||||
args->rfp = fopen(argv[0], args->mrbfile ? "rb" : "r");
|
||||
if (args->rfp == NULL) {
|
||||
printf("%s: Cannot open program file. (%s)\n", *origargv, *argv);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
args->fname = argv[0];
|
||||
argc--; argv++;
|
||||
}
|
||||
}
|
||||
args->argv = (char **)mrb_realloc(mrb, args->argv, sizeof(char*) * (argc + 1));
|
||||
memcpy(args->argv, argv, (argc+1) * sizeof(char*));
|
||||
args->argc = argc;
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
static void
|
||||
cleanup(mrb_state *mrb, struct _args *args)
|
||||
{
|
||||
if (args->rfp)
|
||||
fclose(args->rfp);
|
||||
if (args->srcpath)
|
||||
mrb_free(mrb, args->srcpath);
|
||||
if (args->argv)
|
||||
mrb_free(mrb, args->argv);
|
||||
mrdb_state_free(mrb);
|
||||
mrb_close(mrb);
|
||||
}
|
||||
|
||||
static mrb_debug_context*
|
||||
mrb_debug_context_new(mrb_state *mrb)
|
||||
{
|
||||
mrb_debug_context *dbg = (mrb_debug_context*)mrb_malloc(mrb, sizeof(mrb_debug_context));
|
||||
|
||||
memset(dbg, 0, sizeof(mrb_debug_context));
|
||||
|
||||
dbg->xm = DBG_INIT;
|
||||
dbg->xphase = DBG_PHASE_BEFORE_RUN;
|
||||
dbg->next_bpno = 1;
|
||||
|
||||
return dbg;
|
||||
}
|
||||
|
||||
mrb_debug_context*
|
||||
mrb_debug_context_get(mrb_state *mrb)
|
||||
{
|
||||
if (!_debug_context) {
|
||||
_debug_context = mrb_debug_context_new(mrb);
|
||||
}
|
||||
return _debug_context;
|
||||
}
|
||||
|
||||
void
|
||||
mrb_debug_context_set(mrb_debug_context *dbg)
|
||||
{
|
||||
_debug_context = dbg;
|
||||
}
|
||||
|
||||
void
|
||||
mrb_debug_context_free(mrb_state *mrb)
|
||||
{
|
||||
if (_debug_context) {
|
||||
mrb_debug_delete_break_all(mrb, _debug_context);
|
||||
mrb_free(mrb, _debug_context);
|
||||
_debug_context = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static mrdb_state*
|
||||
mrdb_state_new(mrb_state *mrb)
|
||||
{
|
||||
mrdb_state *mrdb = (mrdb_state*)mrb_malloc(mrb, sizeof(mrdb_state));
|
||||
|
||||
memset(mrdb, 0, sizeof(mrdb_state));
|
||||
|
||||
mrdb->dbg = mrb_debug_context_get(mrb);
|
||||
mrdb->command = (char*)mrb_malloc(mrb, MAX_COMMAND_LINE+1);
|
||||
mrdb->print_no = 1;
|
||||
|
||||
return mrdb;
|
||||
}
|
||||
|
||||
mrdb_state*
|
||||
mrdb_state_get(mrb_state *mrb)
|
||||
{
|
||||
if (!_mrdb_state) {
|
||||
_mrdb_state = mrdb_state_new(mrb);
|
||||
}
|
||||
return _mrdb_state;
|
||||
}
|
||||
|
||||
void
|
||||
mrdb_state_set(mrdb_state *mrdb)
|
||||
{
|
||||
_mrdb_state = mrdb;
|
||||
}
|
||||
|
||||
void
|
||||
mrdb_state_free(mrb_state *mrb)
|
||||
{
|
||||
mrb_debug_context_free(mrb);
|
||||
if (_mrdb_state) {
|
||||
mrb_free(mrb, _mrdb_state->command);
|
||||
mrb_free(mrb, _mrdb_state);
|
||||
_mrdb_state = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static char*
|
||||
get_command(mrb_state *mrb, mrdb_state *mrdb)
|
||||
{
|
||||
int i;
|
||||
int c;
|
||||
|
||||
for (i=0; i<MAX_COMMAND_LINE; i++) {
|
||||
if ((c=getchar()) == EOF || c == '\n') break;
|
||||
mrdb->command[i] = c;
|
||||
}
|
||||
|
||||
if (i == 0 && feof(stdin)) {
|
||||
clearerr(stdin);
|
||||
strcpy(mrdb->command, "quit");
|
||||
i += sizeof("quit") - 1;
|
||||
}
|
||||
|
||||
if (i == MAX_COMMAND_LINE) {
|
||||
for ( ; (c=getchar()) != EOF && c !='\n'; i++) ;
|
||||
}
|
||||
|
||||
if (i > MAX_COMMAND_LINE) {
|
||||
printf("command line too long.\n");
|
||||
i = 0; /* discard command data */
|
||||
}
|
||||
mrdb->command[i] = '\0';
|
||||
|
||||
return mrdb->command;
|
||||
}
|
||||
|
||||
static char*
|
||||
pick_out_word(mrb_state *mrb, char **pp)
|
||||
{
|
||||
char *ps;
|
||||
|
||||
for (ps=*pp; ISBLANK(*ps); ps++) ;
|
||||
if (*ps == '\0') {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (*ps == '\"' || *ps == '\'') {
|
||||
*pp = strchr(ps+1, *ps);
|
||||
if (*pp) (*pp)++;
|
||||
}
|
||||
else {
|
||||
*pp = strpbrk(ps, " \t");
|
||||
}
|
||||
|
||||
if (!*pp) {
|
||||
*pp = ps + strlen(ps);
|
||||
}
|
||||
|
||||
if (**pp != '\0') {
|
||||
**pp = '\0';
|
||||
(*pp)++;
|
||||
}
|
||||
|
||||
return ps;
|
||||
}
|
||||
|
||||
static debug_command*
|
||||
parse_command(mrb_state *mrb, mrdb_state *mrdb, char *buf)
|
||||
{
|
||||
debug_command *cmd = NULL;
|
||||
char *p = buf;
|
||||
size_t wlen;
|
||||
|
||||
/* get word #1 */
|
||||
mrdb->words[0] = pick_out_word(mrb, &p);
|
||||
if (!mrdb->words[0]) {
|
||||
return NULL;
|
||||
}
|
||||
mrdb->wcnt = 1;
|
||||
/* set remain parameter */
|
||||
for ( ; *p && ISBLANK(*p); p++) ;
|
||||
if (*p) {
|
||||
mrdb->words[mrdb->wcnt++] = p;
|
||||
}
|
||||
|
||||
/* check word #1 */
|
||||
for (cmd=(debug_command*)debug_command_list; cmd->cmd1; cmd++) {
|
||||
wlen = strlen(mrdb->words[0]);
|
||||
if (wlen >= cmd->len1 &&
|
||||
strncmp(mrdb->words[0], cmd->cmd1, wlen) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (cmd->cmd2) {
|
||||
if (mrdb->wcnt > 1) {
|
||||
/* get word #2 */
|
||||
mrdb->words[1] = pick_out_word(mrb, &p);
|
||||
if (mrdb->words[1]) {
|
||||
/* update remain parameter */
|
||||
for ( ; *p && ISBLANK(*p); p++) ;
|
||||
if (*p) {
|
||||
mrdb->words[mrdb->wcnt++] = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* check word #1,#2 */
|
||||
for ( ; cmd->cmd1; cmd++) {
|
||||
wlen = strlen(mrdb->words[0]);
|
||||
if (wlen < cmd->len1 ||
|
||||
strncmp(mrdb->words[0], cmd->cmd1, wlen)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!cmd->cmd2) break; /* word #1 only */
|
||||
|
||||
if (mrdb->wcnt == 1) continue; /* word #2 not specified */
|
||||
|
||||
wlen = strlen(mrdb->words[1]);
|
||||
if (wlen >= cmd->len2 &&
|
||||
strncmp(mrdb->words[1], cmd->cmd2, wlen) == 0) {
|
||||
break; /* word #1 and #2 */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* divide remain parameters */
|
||||
if (cmd->cmd1 && cmd->div) {
|
||||
p = mrdb->words[--mrdb->wcnt];
|
||||
for ( ; mrdb->wcnt<MAX_COMMAND_WORD; mrdb->wcnt++) {
|
||||
mrdb->words[mrdb->wcnt] = pick_out_word(mrb, &p);
|
||||
if (!mrdb->words[mrdb->wcnt]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cmd->cmd1 ? cmd : NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
print_info_stopped_break(mrb_state *mrb, mrdb_state *mrdb)
|
||||
{
|
||||
mrb_debug_breakpoint bp;
|
||||
int32_t ret;
|
||||
uint16_t lineno;
|
||||
const char *file;
|
||||
const char *method_name;
|
||||
const char *class_name;
|
||||
|
||||
ret = mrb_debug_get_break(mrb, mrdb->dbg, mrdb->dbg->stopped_bpno, &bp);
|
||||
if (ret == 0) {
|
||||
switch(bp.type) {
|
||||
case MRB_DEBUG_BPTYPE_LINE:
|
||||
file = bp.point.linepoint.file;
|
||||
lineno = bp.point.linepoint.lineno;
|
||||
printf("Breakpoint %d, at %s:%d\n", bp.bpno, file, lineno);
|
||||
break;
|
||||
case MRB_DEBUG_BPTYPE_METHOD:
|
||||
method_name = bp.point.methodpoint.method_name;
|
||||
class_name = bp.point.methodpoint.class_name;
|
||||
if (class_name == NULL) {
|
||||
printf("Breakpoint %d, %s\n", bp.bpno, method_name);
|
||||
}
|
||||
else {
|
||||
printf("Breakpoint %d, %s:%s\n", bp.bpno, class_name, method_name);
|
||||
}
|
||||
if (mrdb->dbg->isCfunc) {
|
||||
printf("Stopped before calling the C function.\n");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
print_info_stopped_step_next(mrb_state *mrb, mrdb_state *mrdb)
|
||||
{
|
||||
const char* file = mrdb->dbg->prvfile;
|
||||
uint16_t lineno = mrdb->dbg->prvline;
|
||||
printf("%s:%d\n", file, lineno);
|
||||
}
|
||||
|
||||
static void
|
||||
print_info_stopped_code(mrb_state *mrb, mrdb_state *mrdb)
|
||||
{
|
||||
char* file = mrb_debug_get_source(mrb, mrdb, mrdb->srcpath, mrdb->dbg->prvfile);
|
||||
uint16_t lineno = mrdb->dbg->prvline;
|
||||
if (file != NULL) {
|
||||
mrb_debug_list(mrb, mrdb->dbg, file, lineno, lineno);
|
||||
mrb_free(mrb, file);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
print_info_stopped(mrb_state *mrb, mrdb_state *mrdb)
|
||||
{
|
||||
switch(mrdb->dbg->bm) {
|
||||
case BRK_BREAK:
|
||||
print_info_stopped_break(mrb, mrdb);
|
||||
print_info_stopped_code(mrb, mrdb);
|
||||
break;
|
||||
case BRK_STEP:
|
||||
case BRK_NEXT:
|
||||
print_info_stopped_step_next(mrb, mrdb);
|
||||
print_info_stopped_code(mrb, mrdb);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static debug_command*
|
||||
get_and_parse_command(mrb_state *mrb, mrdb_state *mrdb)
|
||||
{
|
||||
debug_command *cmd = NULL;
|
||||
char *p;
|
||||
int i;
|
||||
|
||||
while (!cmd) {
|
||||
for (p=NULL; !p || *p=='\0'; ) {
|
||||
printf("(%s:%d) ", mrdb->dbg->prvfile, mrdb->dbg->prvline);
|
||||
fflush(stdout);
|
||||
p = get_command(mrb, mrdb);
|
||||
}
|
||||
|
||||
cmd = parse_command(mrb, mrdb, p);
|
||||
#ifdef _DBG_MRDB_PARSER_
|
||||
for (i=0; i<mrdb->wcnt; i++) {
|
||||
printf("%d: %s\n", i, mrdb->words[i]);
|
||||
}
|
||||
#endif
|
||||
if (!cmd) {
|
||||
printf("invalid command (");
|
||||
for (i=0; i<mrdb->wcnt; i++) {
|
||||
if (i>0) {
|
||||
printf(" ");
|
||||
}
|
||||
printf("%s", mrdb->words[i]);
|
||||
}
|
||||
puts(")");
|
||||
}
|
||||
}
|
||||
return cmd;
|
||||
}
|
||||
|
||||
static int32_t
|
||||
check_method_breakpoint(mrb_state *mrb, mrb_irep *irep, const mrb_code *pc, mrb_value *regs)
|
||||
{
|
||||
struct RClass* c;
|
||||
mrb_sym sym;
|
||||
int32_t bpno;
|
||||
mrb_bool isCfunc;
|
||||
struct mrb_insn_data insn;
|
||||
|
||||
mrb_debug_context *dbg = mrb_debug_context_get(mrb);
|
||||
|
||||
isCfunc = FALSE;
|
||||
bpno = dbg->method_bpno;
|
||||
dbg->method_bpno = 0;
|
||||
|
||||
insn = mrb_decode_insn(pc);
|
||||
switch(insn.insn) {
|
||||
case OP_SEND:
|
||||
case OP_SENDB:
|
||||
c = mrb_class(mrb, regs[insn.a]);
|
||||
sym = irep->syms[insn.b];
|
||||
break;
|
||||
case OP_SUPER:
|
||||
c = mrb->c->ci->target_class->super;
|
||||
sym = mrb->c->ci->mid;
|
||||
break;
|
||||
default:
|
||||
sym = 0;
|
||||
break;
|
||||
}
|
||||
if (sym != 0) {
|
||||
dbg->method_bpno = mrb_debug_check_breakpoint_method(mrb, dbg, c, sym, &isCfunc);
|
||||
if (isCfunc) {
|
||||
bpno = dbg->method_bpno;
|
||||
dbg->method_bpno = 0;
|
||||
}
|
||||
}
|
||||
dbg->isCfunc = isCfunc;
|
||||
return bpno;
|
||||
}
|
||||
|
||||
static void
|
||||
mrb_code_fetch_hook(mrb_state *mrb, mrb_irep *irep, const mrb_code *pc, mrb_value *regs)
|
||||
{
|
||||
const char *file;
|
||||
int32_t line;
|
||||
int32_t bpno;
|
||||
|
||||
mrb_debug_context *dbg = mrb_debug_context_get(mrb);
|
||||
|
||||
mrb_assert(dbg);
|
||||
|
||||
dbg->irep = irep;
|
||||
dbg->pc = pc;
|
||||
dbg->regs = regs;
|
||||
|
||||
if (dbg->xphase == DBG_PHASE_RESTART) {
|
||||
dbg->root_irep = irep;
|
||||
dbg->prvfile = NULL;
|
||||
dbg->prvline = 0;
|
||||
dbg->prvci = NULL;
|
||||
dbg->xm = DBG_RUN;
|
||||
dbg->xphase = DBG_PHASE_RUNNING;
|
||||
}
|
||||
|
||||
file = mrb_debug_get_filename(mrb, irep, pc - irep->iseq);
|
||||
line = mrb_debug_get_line(mrb, irep, pc - irep->iseq);
|
||||
|
||||
switch (dbg->xm) {
|
||||
case DBG_STEP:
|
||||
if (!file || (dbg->prvfile == file && dbg->prvline == line)) {
|
||||
return;
|
||||
}
|
||||
dbg->method_bpno = 0;
|
||||
dbg->bm = BRK_STEP;
|
||||
break;
|
||||
|
||||
case DBG_NEXT:
|
||||
if (!file || (dbg->prvfile == file && dbg->prvline == line)) {
|
||||
return;
|
||||
}
|
||||
if ((intptr_t)(dbg->prvci) < (intptr_t)(mrb->c->ci)) {
|
||||
return;
|
||||
}
|
||||
dbg->prvci = NULL;
|
||||
dbg->method_bpno = 0;
|
||||
dbg->bm = BRK_NEXT;
|
||||
break;
|
||||
|
||||
case DBG_RUN:
|
||||
bpno = check_method_breakpoint(mrb, irep, pc, regs);
|
||||
if (bpno > 0) {
|
||||
dbg->stopped_bpno = bpno;
|
||||
dbg->bm = BRK_BREAK;
|
||||
break;
|
||||
}
|
||||
if (dbg->prvfile != file || dbg->prvline != line) {
|
||||
bpno = mrb_debug_check_breakpoint_line(mrb, dbg, file, line);
|
||||
if (bpno > 0) {
|
||||
dbg->stopped_bpno = bpno;
|
||||
dbg->bm = BRK_BREAK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
dbg->prvfile = file;
|
||||
dbg->prvline = line;
|
||||
return;
|
||||
case DBG_INIT:
|
||||
dbg->root_irep = irep;
|
||||
dbg->bm = BRK_INIT;
|
||||
if (!file || line < 0) {
|
||||
puts("Cannot get debugging information.");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
dbg->prvfile = file;
|
||||
dbg->prvline = line;
|
||||
|
||||
if (dbg->bm == BRK_BREAK && --dbg->ccnt > 0) {
|
||||
return;
|
||||
}
|
||||
dbg->break_hook(mrb, dbg);
|
||||
|
||||
dbg->xphase = DBG_PHASE_RUNNING;
|
||||
}
|
||||
|
||||
static mrdb_exemode
|
||||
mrb_debug_break_hook(mrb_state *mrb, mrb_debug_context *dbg)
|
||||
{
|
||||
debug_command *cmd;
|
||||
dbgcmd_state st = DBGST_CONTINUE;
|
||||
mrdb_state *mrdb = mrdb_state_get(mrb);
|
||||
|
||||
print_info_stopped(mrb, mrdb);
|
||||
|
||||
while (1) {
|
||||
cmd = get_and_parse_command(mrb, mrdb);
|
||||
mrb_assert(cmd);
|
||||
|
||||
st = cmd->func(mrb, mrdb);
|
||||
|
||||
if ((st == DBGST_CONTINUE) || (st == DBGST_RESTART)) break;
|
||||
}
|
||||
return dbg->xm;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
mrb_state *mrb = mrb_open();
|
||||
int n = -1;
|
||||
struct _args args;
|
||||
mrb_value v;
|
||||
mrdb_state *mrdb;
|
||||
mrdb_state *mrdb_backup;
|
||||
mrb_debug_context* dbg_backup;
|
||||
debug_command *cmd;
|
||||
|
||||
l_restart:
|
||||
|
||||
if (mrb == NULL) {
|
||||
fputs("Invalid mrb_state, exiting mruby\n", stderr);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* parse command parameters */
|
||||
n = parse_args(mrb, argc, argv, &args);
|
||||
if (n == EXIT_FAILURE || args.rfp == NULL) {
|
||||
cleanup(mrb, &args);
|
||||
usage(argv[0]);
|
||||
return n;
|
||||
}
|
||||
|
||||
/* initialize debugger information */
|
||||
mrdb = mrdb_state_get(mrb);
|
||||
mrb_assert(mrdb && mrdb->dbg);
|
||||
mrdb->srcpath = args.srcpath;
|
||||
|
||||
if (mrdb->dbg->xm == DBG_QUIT) {
|
||||
mrdb->dbg->xphase = DBG_PHASE_RESTART;
|
||||
}
|
||||
else {
|
||||
mrdb->dbg->xphase = DBG_PHASE_BEFORE_RUN;
|
||||
}
|
||||
mrdb->dbg->xm = DBG_INIT;
|
||||
mrdb->dbg->ccnt = 1;
|
||||
|
||||
/* setup hook functions */
|
||||
mrb->code_fetch_hook = mrb_code_fetch_hook;
|
||||
mrdb->dbg->break_hook = mrb_debug_break_hook;
|
||||
|
||||
if (args.mrbfile) { /* .mrb */
|
||||
v = mrb_load_irep_file(mrb, args.rfp);
|
||||
}
|
||||
else { /* .rb */
|
||||
mrbc_context *cc = mrbc_context_new(mrb);
|
||||
mrbc_filename(mrb, cc, args.fname);
|
||||
v = mrb_load_file_cxt(mrb, args.rfp, cc);
|
||||
mrbc_context_free(mrb, cc);
|
||||
}
|
||||
if (mrdb->dbg->xm == DBG_QUIT && !mrb_undef_p(v) && mrb->exc) {
|
||||
const char *classname = mrb_obj_classname(mrb, mrb_obj_value(mrb->exc));
|
||||
if (!strcmp(classname, "DebuggerExit")) {
|
||||
cleanup(mrb, &args);
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp(classname, "DebuggerRestart")) {
|
||||
mrdb_backup = mrdb_state_get(mrb);
|
||||
dbg_backup = mrb_debug_context_get(mrb);
|
||||
|
||||
mrdb_state_set(NULL);
|
||||
mrb_debug_context_set(NULL);
|
||||
|
||||
cleanup(mrb, &args);
|
||||
mrb = mrb_open();
|
||||
|
||||
mrdb_state_set(mrdb_backup);
|
||||
mrb_debug_context_set(dbg_backup);
|
||||
|
||||
goto l_restart;
|
||||
}
|
||||
}
|
||||
puts("mruby application exited.");
|
||||
mrdb->dbg->xphase = DBG_PHASE_AFTER_RUN;
|
||||
if (!mrb_undef_p(v)) {
|
||||
if (mrb->exc) {
|
||||
mrb_print_error(mrb);
|
||||
}
|
||||
else {
|
||||
printf(" => ");
|
||||
mrb_p(mrb, v);
|
||||
}
|
||||
}
|
||||
|
||||
mrdb->dbg->prvfile = "-";
|
||||
mrdb->dbg->prvline = 0;
|
||||
|
||||
while (1) {
|
||||
cmd = get_and_parse_command(mrb, mrdb);
|
||||
mrb_assert(cmd);
|
||||
|
||||
if (cmd->id == DBGCMD_QUIT) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ( cmd->func(mrb, mrdb) == DBGST_RESTART ) goto l_restart;
|
||||
}
|
||||
|
||||
cleanup(mrb, &args);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
** mrdb.h - mruby debugger
|
||||
**
|
||||
*/
|
||||
|
||||
#ifndef MRDB_H
|
||||
#define MRDB_H
|
||||
|
||||
#include <mruby.h>
|
||||
|
||||
#include "mrdbconf.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# define __func__ __FUNCTION__
|
||||
#endif
|
||||
|
||||
#define MAX_COMMAND_WORD (16)
|
||||
|
||||
typedef enum debug_command_id {
|
||||
DBGCMD_RUN,
|
||||
DBGCMD_CONTINUE,
|
||||
DBGCMD_NEXT,
|
||||
DBGCMD_STEP,
|
||||
DBGCMD_BREAK,
|
||||
DBGCMD_INFO_BREAK,
|
||||
DBGCMD_INFO_LOCAL,
|
||||
DBGCMD_WATCH,
|
||||
DBGCMD_INFO_WATCH,
|
||||
DBGCMD_ENABLE,
|
||||
DBGCMD_DISABLE,
|
||||
DBGCMD_DELETE,
|
||||
DBGCMD_PRINT,
|
||||
DBGCMD_DISPLAY,
|
||||
DBGCMD_INFO_DISPLAY,
|
||||
DBGCMD_DELETE_DISPLAY,
|
||||
DBGCMD_EVAL,
|
||||
DBGCMD_BACKTRACE,
|
||||
DBGCMD_LIST,
|
||||
DBGCMD_HELP,
|
||||
DBGCMD_QUIT,
|
||||
DBGCMD_UNKNOWN
|
||||
} debug_command_id;
|
||||
|
||||
typedef enum dbgcmd_state {
|
||||
DBGST_CONTINUE,
|
||||
DBGST_PROMPT,
|
||||
DBGST_COMMAND_ERROR,
|
||||
DBGST_MAX,
|
||||
DBGST_RESTART
|
||||
} dbgcmd_state;
|
||||
|
||||
typedef enum mrdb_exemode {
|
||||
DBG_INIT,
|
||||
DBG_RUN,
|
||||
DBG_STEP,
|
||||
DBG_NEXT,
|
||||
DBG_QUIT,
|
||||
} mrdb_exemode;
|
||||
|
||||
typedef enum mrdb_exephase {
|
||||
DBG_PHASE_BEFORE_RUN,
|
||||
DBG_PHASE_RUNNING,
|
||||
DBG_PHASE_AFTER_RUN,
|
||||
DBG_PHASE_RESTART,
|
||||
} mrdb_exephase;
|
||||
|
||||
typedef enum mrdb_brkmode {
|
||||
BRK_INIT,
|
||||
BRK_BREAK,
|
||||
BRK_STEP,
|
||||
BRK_NEXT,
|
||||
BRK_QUIT,
|
||||
} mrdb_brkmode;
|
||||
|
||||
typedef enum {
|
||||
MRB_DEBUG_BPTYPE_NONE,
|
||||
MRB_DEBUG_BPTYPE_LINE,
|
||||
MRB_DEBUG_BPTYPE_METHOD,
|
||||
} mrb_debug_bptype;
|
||||
|
||||
struct mrb_irep;
|
||||
struct mrbc_context;
|
||||
struct mrb_debug_context;
|
||||
|
||||
typedef struct mrb_debug_linepoint {
|
||||
const char *file;
|
||||
uint16_t lineno;
|
||||
} mrb_debug_linepoint;
|
||||
|
||||
typedef struct mrb_debug_methodpoint {
|
||||
const char *class_name;
|
||||
const char *method_name;
|
||||
} mrb_debug_methodpoint;
|
||||
|
||||
typedef struct mrb_debug_breakpoint {
|
||||
uint32_t bpno;
|
||||
uint8_t enable;
|
||||
mrb_debug_bptype type;
|
||||
union point {
|
||||
mrb_debug_linepoint linepoint;
|
||||
mrb_debug_methodpoint methodpoint;
|
||||
} point;
|
||||
} mrb_debug_breakpoint;
|
||||
|
||||
typedef struct mrb_debug_context {
|
||||
struct mrb_irep *root_irep;
|
||||
struct mrb_irep *irep;
|
||||
const mrb_code *pc;
|
||||
mrb_value *regs;
|
||||
|
||||
const char *prvfile;
|
||||
int32_t prvline;
|
||||
mrb_callinfo *prvci;
|
||||
|
||||
mrdb_exemode xm;
|
||||
mrdb_exephase xphase;
|
||||
mrdb_brkmode bm;
|
||||
int16_t bmi;
|
||||
|
||||
uint16_t ccnt;
|
||||
uint16_t scnt;
|
||||
|
||||
mrb_debug_breakpoint bp[MAX_BREAKPOINT];
|
||||
uint32_t bpnum;
|
||||
int32_t next_bpno;
|
||||
int32_t method_bpno;
|
||||
int32_t stopped_bpno;
|
||||
mrb_bool isCfunc;
|
||||
|
||||
mrdb_exemode (*break_hook)(mrb_state *mrb, struct mrb_debug_context *dbg);
|
||||
|
||||
} mrb_debug_context;
|
||||
|
||||
typedef struct mrdb_state {
|
||||
char *command;
|
||||
uint8_t wcnt;
|
||||
uint8_t pi;
|
||||
char *words[MAX_COMMAND_WORD];
|
||||
const char *srcpath;
|
||||
uint32_t print_no;
|
||||
|
||||
mrb_debug_context *dbg;
|
||||
} mrdb_state;
|
||||
|
||||
typedef dbgcmd_state (*debug_command_func)(mrb_state*, mrdb_state*);
|
||||
|
||||
/* cmdrun.c */
|
||||
dbgcmd_state dbgcmd_run(mrb_state*, mrdb_state*);
|
||||
dbgcmd_state dbgcmd_continue(mrb_state*, mrdb_state*);
|
||||
dbgcmd_state dbgcmd_step(mrb_state*, mrdb_state*);
|
||||
dbgcmd_state dbgcmd_next(mrb_state*, mrdb_state*);
|
||||
/* cmdbreak.c */
|
||||
dbgcmd_state dbgcmd_break(mrb_state*, mrdb_state*);
|
||||
dbgcmd_state dbgcmd_info_break(mrb_state*, mrdb_state*);
|
||||
dbgcmd_state dbgcmd_info_local(mrb_state*, mrdb_state*);
|
||||
dbgcmd_state dbgcmd_delete(mrb_state*, mrdb_state*);
|
||||
dbgcmd_state dbgcmd_enable(mrb_state*, mrdb_state*);
|
||||
dbgcmd_state dbgcmd_disable(mrb_state*, mrdb_state*);
|
||||
/* cmdprint.c */
|
||||
dbgcmd_state dbgcmd_print(mrb_state*, mrdb_state*);
|
||||
dbgcmd_state dbgcmd_eval(mrb_state*, mrdb_state*);
|
||||
/* cmdmisc.c */
|
||||
dbgcmd_state dbgcmd_list(mrb_state*, mrdb_state*);
|
||||
dbgcmd_state dbgcmd_help(mrb_state*, mrdb_state*);
|
||||
dbgcmd_state dbgcmd_quit(mrb_state*, mrdb_state*);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
** mrdbconf.h - mruby debugger configuration
|
||||
**
|
||||
*/
|
||||
|
||||
#ifndef MRDBCONF_H
|
||||
#define MRDBCONF_H
|
||||
|
||||
#ifndef MRB_ENABLE_DEBUG_HOOK
|
||||
# error mruby-bin-debugger need 'MRB_ENABLE_DEBUG_HOOK' configuration in your 'build_config.rb'
|
||||
#endif
|
||||
|
||||
#ifdef MRB_DISABLE_STDIO
|
||||
# error mruby-bin-debugger conflicts 'MRB_DISABLE_STDIO' configuration in your 'build_config.rb'
|
||||
#endif
|
||||
|
||||
/* configuration options: */
|
||||
/* maximum size for command buffer */
|
||||
#define MAX_COMMAND_LINE 1024
|
||||
|
||||
/* maximum number of setable breakpoint */
|
||||
#define MAX_BREAKPOINT 5
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
** mrdberror.h - mruby debugger error code
|
||||
**
|
||||
*/
|
||||
|
||||
#ifndef MRDBERROR_H
|
||||
#define MRDBERROR_H
|
||||
|
||||
#define MRB_DEBUG_OK (0)
|
||||
#define MRB_DEBUG_NOBUF (-1)
|
||||
#define MRB_DEBUG_INVALID_ARGUMENT (-2)
|
||||
|
||||
#define MRB_DEBUG_BREAK_INVALID_LINENO (-11)
|
||||
#define MRB_DEBUG_BREAK_INVALID_FILE (-12)
|
||||
#define MRB_DEBUG_BREAK_INVALID_NO (-13)
|
||||
#define MRB_DEBUG_BREAK_NUM_OVER (-14)
|
||||
#define MRB_DEBUG_BREAK_NO_OVER (-15)
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user