first commit
CI / Build (clang, clang++, ubuntu-22.04, linux, 3.10, master, x86,x86_64) (push) Canceled after 0s
CI / Build (clang, clang++, windows-2022, windows, 3.10, master, x86,x86_64) (push) Canceled after 0s
CI / Release (push) Canceled after 0s

This commit is contained in:
2026-09-13 22:18:11 +01:00
commit 08c03ac533
6701 changed files with 1577314 additions and 0 deletions
@@ -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