Commit e5bacc03 authored by mimaki's avatar mimaki

Add mruby debugger (mrdb)

parent 4c7e812b
......@@ -20,3 +20,14 @@ Original Authors "mruby developers" are:
Tatsuhiko Kubo
Takeshi Watanabe
Yuki Kurihara
specified non-profit corporation mruby Forum
Kazuaki Tanaka
Hiromasa Ishii
Hiroshi Mimaki
Satoshi Odawara
Mitsubishi Electric Micro-Computer Application Software Co.,Ltd.
Hiroyuki Matsuzaki
Yuhei Okazaki
Manycolors, Inc.
Shota Nakano
Yuichi Osawa
......@@ -75,6 +75,16 @@ MRuby.each_target do |target|
FileUtils.cp t.prerequisites.first, t.name, { :verbose => $verbose }
end
depfiles += [ install_path ]
elsif target == MRuby.targets['host-debug']
unless MRuby.targets['host'].gems.map {|g| g.bins}.include?([bin])
install_path = MRuby.targets['host-debug'].exefile("#{MRUBY_ROOT}/bin/#{bin}")
file install_path => exec do |t|
FileUtils.rm_f t.name, { :verbose => $verbose }
FileUtils.cp t.prerequisites.first, t.name, { :verbose => $verbose }
end
depfiles += [ install_path ]
end
else
depfiles += [ exec ]
end
......
......@@ -83,6 +83,31 @@ MRuby::Build.new do |conf|
# conf.enable_bintest
end
MRuby::Build.new('host-debug') do |conf|
# load specific toolchain settings
# Gets set by the VS command prompts.
if ENV['VisualStudioVersion'] || ENV['VSINSTALLDIR']
toolchain :visualcpp
else
toolchain :gcc
end
enable_debug
# include the default GEMs
conf.gembox 'default'
# C compiler settings
conf.cc.defines = %w(ENABLE_DEBUG)
# Generate mruby debugger command (require mruby-eval)
conf.gem :core => "mruby-bin-debugger"
# bintest
# conf.enable_bintest
end
# Define cross build settings
# MRuby::CrossBuild.new('32bit') do |conf|
# toolchain :gcc
......
# How to use mruby debugger mrdb
copyright (c) 2014 Specified Non-Profit Coorporation mruby Forum
## 1.Summary
This file documents the method for using the mruby debugger 'mrdb'
## 2 debugging with mrdb
## 2.1 Building mrdb
The trunk of the mruby source tree can be checked out with following command/
```bash
$ git clone https://github.com/Kumikomi-Ruby/forum-mruby.git mruby
```
Run make command
```bash
$ cd mruby
$ make
```
By default, make command will install debugger files into mruby/bin.
You can add the path for mrdb on your host environment with following command
```bash
$ echo "export PATH=\$PATH:MRUBY_ROOT/bin" >> ~/.bashrc
$ source ~/.bashrc
```
*MRUBY_ROOT is the directory in which mruby source cords will be installed.
To confirm mrdb was instaleed properly,run mrdb with --version option
```bash
$ mrdb --version
mruby 1.1.0 (2014-11-19)
```
## 2.2 Bassic Operation
### 2.2.1 debugging mruby script file(rb file) with mrdb
To invoke mruby debugger, just type mrdb.
To specify the script file ,
```bash
$ mrdb [option] file name
```
For example : Debugging sample.rb
```bash
$ mrdb sample.rb
```
You can excute shell commands listed below
|command|description|
|:-:|:--|
|run|execute programs|
|step|execute stepping|
|continue|execute continuing program|
|break|configure the breaking point|
|delete|deleting the breaking points|
|disable|disabling the breaking points|
|enable|enabling the breaking points|
|info breakpoints|showing list of the breaking points|
|print|eavaluating and printing the values of the mruby expressions in the script|
|list|displaying the source cords|
|help|showing help|
|quit|terminating the mruby debugger|
### 2.2.2 debugging mruby binary file(mrb file) with mrdb
You can debugg the mruby binary files
#### 2.2.2.1 debuggg the binary files
* notice
To debugg mruby binary files, you need to compile mruby files with option -g.
```bash
$ mrbc -g sample.rb
```
You can debugg the mruby binary files with following command and the option -b.
```bash
$ mrdb -b sample.mrb
```
Then you can execute all debugger shell commands.
#### break command
you can give any breakpoint to stop the program by specifiyng the line number and method name.
And the breakpoint list will be displayed after you finished to set the breakpoint succesfully.
Usage:
```
break [file:]lineno
b [file:]lineno
break [class:]method
b [class:]method
```
The breakpoint will be numbered in serial order from 1.The number which was given to deleted breakpoint will not be given to another breakpoint again.
You can give multiple breakpoints to specified the line number and method.
Be ware that breakpoint command will not check the validity of the class name and method name.
You can see the current breakpoint information by following options.
breakpoint breakpoint number : file name. line number
breakpoint breakpoint number : [class name,] method name
#### continue command
Usage:
```
continue [N]
c [N]
```
N: the next breakpoint number
Resuming the program and will stop the program at breakpoint at N (N-1 breakpoint will be ignored)
When you run continue command without any specifying N ,Program will be stopped at next breakpoint.
Example:
```
(foo.rb:1) continue 3
```
Resuming the program and stopping the program at the third breakpoint.
#### delete command
Deleting specified breakpoint
Usage:
```
delete [breakpointno]
d [breakpointno]
```
breakpointno: breakpoint number
Example:
```
(foo.rb:1) delete
```
Deleting all brealpoint
```
(foo.rb:1) delete 1 3
```
Deleting the breakpoint 1 and 3
#### diable command
Disabling the specified breakpoint
Usage:
```
disable [breakpointno]
dis [breakpointno]
```
brealpointno: breakpoint number
Example:
```
(foo.rb:1) disable
```
Desabling all brealpoint
```
(foo.rb:1) disable 1 3
```
Disabling the breakpoint 1 and 3
#### enable command
Enababling the specified breakpoint
Usage:
```
enable [breakpointno]
e [breakpointno]
```
brealpointno: breakpoint number
Example:
```
(foo.rb:1) enable
```
Enabling all brealpoint
```
(foo.rb:1) enable 1 3
```
Enabling the breakpoint 1 and 3
#### eval command
Evaluating the string as source code and printing the value.
Same as print command, please see print command.
#### help command
Displaying the help message.
Usage:
```
help [comand]
h [command]
```
Typing help without any option will displays the command list.
#### info breakpoints command
Displaying the specified breakpoint information.
Usage:
```
info breakpoints [breakpointno]
i b [breakpointno]
```
breakpointno: breakpoint number
Typing "info breakpoints" without ant option will display all breakpoint information.
Example
```
(sample.rb:1) info breakpoints
Num Type Enb What
1 breakpoint y at sample.rb:3 -> file name,line number
2 breakpoint n in Sample_class:sample_class_method -> [class:]method name
3 breakpoint y in sample_global_method
```
Displaying specified the breakpoint number
```
(foo.rb:1) info breakpoints 1 3
Num Type Enb What
1 breakpoint y at sample.rb:3
3 breakpoint y in sample_global_method
```
#### list command
Displaying the cords of the source file.
Usage:
```
list [filename:]first[,last]
l [filename]:first[,last]
```
first: the opening row number
last : the closing row number
When you specify first , but not specify option "last" , you will get 10 rows.
When you don not specify both of first and last, you will next 10 rows.
Example:
```
Specifying file name and first row number
sample.rb:1) list sample2.rb:5
```
Specifying file name and first and last row number
```
(sample.rb:1) list sample2.rb:6,7
```
#### print command
Evaluating the string as source code and printing the value.
Usage:
```
print [expr]
p [expr]
```
expr: expression
To specify the expression is indispensableness.
The displayed expressions will be numbered in serial order from 1.
If an exception occurs, the exception information will be displayed and the debugging will be continued.
Example:
```
(sample.rb:1) print 1+2
$1 = 3
(sample.rb:1) print self
$2 = main
```
below is the case of the exception:
```
(sample.rb:1) print (1+2
$1 = SyntaxError: line 1: syntax error, unexpected $end, expecting ')'
```
#### quit command
Quitting the debugger.
Usage:
```
quit
q
```
#### run command
Running the program and stopping at the first breakpoint.
Usage:
```
run
r
```
#### step command
Running the program step by step.
When the method and the block will be invoked, the program will be stop at the first row.
The program which is developed by C language will be ignored.
period
......@@ -358,6 +358,8 @@ MRB_API mrb_value mrb_obj_clone(mrb_state *mrb, mrb_value self);
#define ISALPHA(c) (ISASCII(c) && isalpha((int)(unsigned char)(c)))
#define ISDIGIT(c) (ISASCII(c) && isdigit((int)(unsigned char)(c)))
#define ISXDIGIT(c) (ISASCII(c) && isxdigit((int)(unsigned char)(c)))
#define ISBLANK(c) (ISASCII(c) && isblank((int)(unsigned char)(c)))
#define ISCNTRL(c) (ISASCII(c) && iscntrl((int)(unsigned char)(c)))
#define TOUPPER(c) (ISASCII(c) ? toupper((int)(unsigned char)(c)) : (c))
#define TOLOWER(c) (ISASCII(c) ? tolower((int)(unsigned char)(c)) : (c))
#endif
......
......@@ -4,6 +4,6 @@ MRuby::GemBox.new do |conf|
Dir.glob("#{root}/mrbgems/mruby-*/mrbgem.rake") do |x|
g = File.basename File.dirname x
conf.gem :core => g unless g =~ /^mruby-(print|sprintf)$/
conf.gem :core => g unless g =~ /^mruby-(print|sprintf|bin-debugger)$/
end
end
require 'open3'
require 'tempfile'
class BinTest_MrubyBinDebugger
@debug1=false
@debug2=true
@debug3=true
def self.test(rubysource, testcase)
script, bin = Tempfile.new(['test', '.rb']), Tempfile.new(['test', '.mrb'])
# .rb
script.write rubysource
script.flush
# compile
`./bin/mrbc -g -o "#{bin.path}" "#{script.path}"`
# add mrdb quit
testcase << {:cmd=>"quit"}
stdin_data = testcase.map{|t| t[:cmd]}.join("\n") << "\n"
["bin/mrdb #{script.path}","bin/mrdb -b #{bin.path}"].each do |cmd|
o, s = Open3.capture2(cmd, :stdin_data => stdin_data)
exp_vals = testcase.map{|t| t.fetch(:exp, nil)}
unexp_vals = testcase.map{|t| t.fetch(:unexp, nil)}
if @debug1
o.split("\n").each_with_index do |i,actual|
p [i,actual]
end
end
# compare actual / expected
o.split("\n").each do |actual|
next if actual.empty?
exp = exp_vals.shift
if @debug2
a = true
a = actual.include?(exp) unless exp.nil?
p [actual, exp] unless a
end
assert_true actual.include?(exp) unless exp.nil?
end
# compare actual / unexpected
o.split("\n").each do |actual|
next if actual.empty?
unexp = unexp_vals.shift
if @debug3
a = false
a = actual.include?(unexp) unless unexp.nil?
p [actual, unexp] if a
end
assert_false actual.include?(unexp) unless unexp.nil?
end
end
end
end
INVCMD = "invalid command"
assert('mruby-bin-debugger(mrdb) command line') do
# ruby source
src = "foo = 'foo'\n"
str = ""
103.times {
str += "1234567890"
}
cmd = "p a=#{str}"
# test case
BinTest_MrubyBinDebugger.test(src, [{:cmd=>cmd[0...1023], :unexp=>'command line too long.'}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>cmd[0...1024], :unexp=>'command line too long.'}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>cmd[0...1025], :exp=>'command line too long.'}])
end
assert('mruby-bin-debugger(mrdb) command: "break"') do
# ruby source
src = "foo = 'foo'\n"
# test case
tc = []
tc << {:cmd=>"b", :unexp=>INVCMD}
tc << {:cmd=>"br", :unexp=>INVCMD}
tc << {:cmd=>"brea", :unexp=>INVCMD}
tc << {:cmd=>"break", :unexp=>INVCMD}
BinTest_MrubyBinDebugger.test(src, tc)
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"bl", :exp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"breaka", :exp=>INVCMD}])
end
assert('mruby-bin-debugger(mrdb) command: "continue"') do
# ruby source
src = "foo = 'foo'\n"
# test case
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"c", :unexp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"co", :unexp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"continu", :unexp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"continue", :unexp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"cn", :exp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"continuee", :exp=>INVCMD}])
end
assert('mruby-bin-debugger(mrdb) command: "delete"') do
# ruby source
src = "foo = 'foo'\n"
# test case
tc = []
tc << {:cmd=>"d 1", :unexp=>INVCMD}
tc << {:cmd=>"de 1", :unexp=>INVCMD}
tc << {:cmd=>"delet 1", :unexp=>INVCMD}
tc << {:cmd=>"delete 1", :unexp=>INVCMD}
BinTest_MrubyBinDebugger.test(src, tc)
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"dd 1", :exp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"deletee 1", :exp=>INVCMD}])
end
assert('mruby-bin-debugger(mrdb) command: "disable"') do
# ruby source
src = "foo = 'foo'\n"
# test case
tc = []
tc << {:cmd=>"dis", :unexp=>INVCMD}
tc << {:cmd=>"disa", :unexp=>INVCMD}
tc << {:cmd=>"disabl", :unexp=>INVCMD}
tc << {:cmd=>"disable", :unexp=>INVCMD}
BinTest_MrubyBinDebugger.test(src, tc)
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"di", :exp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"disb", :exp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"disablee", :exp=>INVCMD}])
end
assert('mruby-bin-debugger(mrdb) command: "enable"') do
# ruby source
src = "foo = 'foo'\n"
# test case
tc = []
tc << {:cmd=>"en", :unexp=>INVCMD}
tc << {:cmd=>"ena", :unexp=>INVCMD}
tc << {:cmd=>"enabl", :unexp=>INVCMD}
tc << {:cmd=>"enable", :unexp=>INVCMD}
BinTest_MrubyBinDebugger.test(src, tc)
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"e", :exp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"enb", :exp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"enablee", :exp=>INVCMD}])
end
assert('mruby-bin-debugger(mrdb) command: "eval"') do
# ruby source
src = "foo = 'foo'\n"
# test case
tc = []
tc << {:cmd=>"ev", :unexp=>INVCMD}
tc << {:cmd=>"eva", :unexp=>INVCMD}
tc << {:cmd=>"eval", :unexp=>INVCMD}
BinTest_MrubyBinDebugger.test(src, tc)
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"e", :exp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"evl", :exp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"evall", :exp=>INVCMD}])
end
assert('mruby-bin-debugger(mrdb) command: "help"') do
# ruby source
src = "foo = 'foo'\n"
# test case
tc = []
tc << {:cmd=>"h", :unexp=>INVCMD}
tc << {:cmd=>"he", :unexp=>INVCMD}
tc << {:cmd=>"hel", :unexp=>INVCMD}
tc << {:cmd=>"help", :unexp=>INVCMD}
BinTest_MrubyBinDebugger.test(src, tc)
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"hl", :exp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"helpp", :exp=>INVCMD}])
end
assert('mruby-bin-debugger(mrdb) command: "info breakpoints"') do
# ruby source
src = "foo = 'foo'\n"
# test case
tc = []
tc << {:cmd=>"i b", :unexp=>INVCMD}
tc << {:cmd=>"in b", :unexp=>INVCMD}
tc << {:cmd=>"i br", :unexp=>INVCMD}
tc << {:cmd=>"inf breakpoint", :unexp=>INVCMD}
tc << {:cmd=>"info breakpoints", :unexp=>INVCMD}
BinTest_MrubyBinDebugger.test(src, tc)
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"ii b", :exp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"i bb", :exp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"infoo breakpoints", :exp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"info breakpointss", :exp=>INVCMD}])
end
assert('mruby-bin-debugger(mrdb) command: "list"') do
# ruby source
src = "foo = 'foo'\n"
# test case
tc = []
tc << {:cmd=>"l", :unexp=>INVCMD}
tc << {:cmd=>"li", :unexp=>INVCMD}
tc << {:cmd=>"lis", :unexp=>INVCMD}
tc << {:cmd=>"list", :unexp=>INVCMD}
BinTest_MrubyBinDebugger.test(src, tc)
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"ll", :exp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"listt", :exp=>INVCMD}])
end
assert('mruby-bin-debugger(mrdb) command: "print"') do
# ruby source
src = "foo = 'foo'\n"
# test case
tc = []
tc << {:cmd=>"p", :unexp=>INVCMD}
tc << {:cmd=>"pr", :unexp=>INVCMD}
tc << {:cmd=>"prin", :unexp=>INVCMD}
tc << {:cmd=>"print", :unexp=>INVCMD}
BinTest_MrubyBinDebugger.test(src, tc)
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"pp", :exp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"printt", :exp=>INVCMD}])
end
assert('mruby-bin-debugger(mrdb) command: "quit"') do
# ruby source
src = "foo = 'foo'\n"
# test case
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"q", :unexp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"qu", :unexp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"qui", :unexp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"quit", :unexp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"qq", :exp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"quitt", :exp=>INVCMD}])
end
assert('mruby-bin-debugger(mrdb) command: "run"') do
# ruby source
src = "foo = 'foo'\n"
# test case
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"r", :unexp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"ru", :unexp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"run", :unexp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"rr", :exp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"runn", :exp=>INVCMD}])
end
assert('mruby-bin-debugger(mrdb) command: "step"') do
# ruby source
src = <<"SRC"
while true
foo = 'foo'
end
SRC
# test case
tc = []
tc << {:cmd=>"s", :unexp=>INVCMD}
tc << {:cmd=>"st", :unexp=>INVCMD}
tc << {:cmd=>"ste", :unexp=>INVCMD}
tc << {:cmd=>"step", :unexp=>INVCMD}
BinTest_MrubyBinDebugger.test(src, tc)
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"ss", :exp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"stepp", :exp=>INVCMD}])
end
This diff is collapsed.
MRuby::Gem::Specification.new('mruby-bin-debugger') do |spec|
spec.license = 'MIT'
spec.author = 'mruby developers'
spec.summary = 'mruby debuggeer command'
spec.add_dependency('mruby-eval', :core => 'mruby-eval')
spec.bins = %w(mrdb)
end
This diff is collapsed.
/*
** 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_ */
/*
* 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) + strlen("/");
}
if ((path = mrb_malloc(mrb, len)) != NULL) {
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;
char *p, *dir;
if (path == NULL) {
return NULL;
}
p = strrchr(path, '/');
len = p != NULL ? p - path : strlen(path);
if ((dir = mrb_malloc(mrb, len + 1)) != NULL) {
strncpy(dir, path, len);
}
return dir;
}
static source_file*
source_file_new(mrb_state *mrb, mrb_debug_context *dbg, char *filename)
{
source_file *file = NULL;
if ((file = mrb_malloc(mrb, sizeof(source_file))) == NULL) {
return NULL;
}
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 = mrb_malloc(mrb, strlen(filename) + 1);
strcpy(file->path, filename);
return file;
}
static mrb_bool
remove_newlines(char *s, FILE *fp)
{
char c, *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 ((unsigned char)(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;
search_path[0] = srcpath;
search_path[1] = dirname(mrb, mrb_debug_get_filename(mrdb->dbg->root_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], filename)) == NULL) {
continue;
}
if ((fp = fopen(path, "rb")) == NULL) {
mrb_free(mrb, path);
path = NULL;
continue;
}
fclose(fp);
break;
}
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;
}
}
/*
* 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_ */
/*
** 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;
c->filename = (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)
{
void *tmp;
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 {
/*
* begin
* expr
* rescue => e
* e
* end
*/
ruby_code = mrb_str_new_cstr(mrb, "begin\n");
ruby_code = mrb_str_cat(mrb, ruby_code, expr, len);
ruby_code = mrb_str_cat_cstr(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_funcall(mrb, v, "inspect", 0);
/* enable code_fetch_hook */
mrb->code_fetch_hook = tmp;
return s;
}
/*
* 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*);
#endif /* APIPRINT_H_ */
This diff is collapsed.
This diff is collapsed.
/*
** 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;
mrb_value s;
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_cstr(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);
/* $print_no = result */
s = mrb_str_cat_cstr(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;
}
dbgcmd_state
dbgcmd_eval(mrb_state *mrb, mrdb_state *mrdb)
{
return dbgcmd_print(mrb, mrdb);
}
/*
** 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_class_get(mrb, "Exception"));
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;
}
This diff is collapsed.
/*
** 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_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;
mrb_code *pc;
mrb_value *regs;
const char *prvfile;
int32_t prvline;
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*);
/* cmdbreak.c */
dbgcmd_state dbgcmd_break(mrb_state*, mrdb_state*);
dbgcmd_state dbgcmd_info_break(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
/*
** mrdbconf.h - mruby debugger configuration
**
*/
#ifndef MRDBCONF_H
#define MRDBCONF_H
/* configuration options: */
/* maximum size for command buffer */
#define MAX_COMMAND_LINE 1024
/* maximum number of setable breakpoint */
#define MAX_BREAKPOINT 5
#endif
/*
** 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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment