mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
@@ -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
|
||||
|
||||
Executable
+341
@@ -0,0 +1,341 @@
|
||||
# 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
|
||||
@@ -359,6 +359,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
|
||||
|
||||
@@ -10,15 +10,15 @@
|
||||
#define MRUBY_RUBY_VERSION "1.9"
|
||||
#define MRUBY_RUBY_ENGINE "mruby"
|
||||
|
||||
#define MRUBY_VERSION "1.0.1"
|
||||
#define MRUBY_VERSION "1.1.0"
|
||||
#define MRUBY_RELEASE_MAJOR 1
|
||||
#define MRUBY_RELEASE_MINOR 0
|
||||
#define MRUBY_RELEASE_MINOR 1
|
||||
#define MRUBY_RELEASE_TEENY 1
|
||||
#define MRUBY_RELEASE_NO 10001
|
||||
#define MRUBY_RELEASE_DATE "2014-01-10"
|
||||
#define MRUBY_RELEASE_NO 10101
|
||||
#define MRUBY_RELEASE_DATE "2014-11-19"
|
||||
#define MRUBY_RELEASE_YEAR 2014
|
||||
#define MRUBY_RELEASE_MONTH 1
|
||||
#define MRUBY_RELEASE_DAY 10
|
||||
#define MRUBY_RELEASE_MONTH 11
|
||||
#define MRUBY_RELEASE_DAY 19
|
||||
|
||||
#define MRUBY_BIRTH_YEAR 2010
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Executable
+286
@@ -0,0 +1,286 @@
|
||||
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
|
||||
Executable
+701
@@ -0,0 +1,701 @@
|
||||
require 'open3'
|
||||
require 'tempfile'
|
||||
|
||||
class BinTest_MrubyBinDebugger
|
||||
@debug1=false
|
||||
@debug2=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)}
|
||||
=begin
|
||||
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
|
||||
=end
|
||||
idx = 0
|
||||
exp_vals.each do |exp|
|
||||
next if exp.nil?
|
||||
idx = o.index(exp, idx)
|
||||
assert_false idx.nil?
|
||||
break unless idx
|
||||
idx += 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
assert('mruby-bin-debugger(print) invalid arguments') do
|
||||
# ruby source
|
||||
src = "foo = 'foo'\n"
|
||||
|
||||
# test case
|
||||
tc = []
|
||||
tc << {:cmd=>"p", :exp=>"Parameter not specified."}
|
||||
|
||||
BinTest_MrubyBinDebugger.test(src, tc)
|
||||
end
|
||||
|
||||
assert('mruby-bin-debugger(print) nomal') do
|
||||
# ruby source
|
||||
src = <<"SRC"
|
||||
foo = 'foo'
|
||||
bar = foo
|
||||
baz = bar
|
||||
SRC
|
||||
|
||||
# test case
|
||||
tc = []
|
||||
tc << {:cmd=>"s"}
|
||||
tc << {:cmd=>"p (1+2)", :exp=>'$1 = 3'}
|
||||
tc << {:cmd=>"p foo", :exp=>'$2 = "foo"'}
|
||||
tc << {:cmd=>"p foo*=2", :exp=>'$3 = "foofoo"'}
|
||||
tc << {:cmd=>"s"}
|
||||
tc << {:cmd=>"p bar", :exp=>'$4 = "foofoo"'}
|
||||
|
||||
BinTest_MrubyBinDebugger.test(src, tc)
|
||||
end
|
||||
|
||||
assert('mruby-bin-debugger(print) error') do
|
||||
# ruby source
|
||||
src = "foo = 'foo'\n"
|
||||
|
||||
# test case
|
||||
tc = []
|
||||
tc << {:cmd=>"p (1+2", :exp=>'$1 = SyntaxError'}
|
||||
tc << {:cmd=>"p bar", :exp=>'$2 = NoMethodError'}
|
||||
|
||||
BinTest_MrubyBinDebugger.test(src, tc)
|
||||
end
|
||||
|
||||
# Kernel#instance_eval(string) does't work multiple statements.
|
||||
=begin
|
||||
assert('mruby-bin-debugger(print) multiple statements') do
|
||||
# ruby source
|
||||
src = <<"SRC"
|
||||
x = 0
|
||||
y = 0
|
||||
z = 0
|
||||
SRC
|
||||
|
||||
# test case
|
||||
tc = []
|
||||
tc << {:cmd=>"s",}
|
||||
tc << {:cmd=>"p x=1;x+=2", :exp=>"3"}
|
||||
tc << {:cmd=>"s",}
|
||||
tc << {:cmd=>"p x", :exp=>"3"}
|
||||
|
||||
BinTest_MrubyBinDebugger.test(src, tc)
|
||||
end
|
||||
=end
|
||||
|
||||
assert('mruby-bin-debugger(print) scope:top') do
|
||||
# ruby source (bp is break point)
|
||||
src = "bp=nil\n"
|
||||
|
||||
# test case
|
||||
tc = []
|
||||
tc << {:cmd=>"p self", :exp=>'$1 = main'}
|
||||
|
||||
BinTest_MrubyBinDebugger.test(src, tc)
|
||||
end
|
||||
|
||||
assert('mruby-bin-debugger(print) scope:class') do
|
||||
# ruby source (bp is break point)
|
||||
src = <<"SRC"
|
||||
class TestClassScope
|
||||
bp = nil
|
||||
end
|
||||
SRC
|
||||
|
||||
# test case
|
||||
tc = []
|
||||
tc << {:cmd=>"s"}
|
||||
tc << {:cmd=>"p self", :exp=>'$1 = TestClassScope'}
|
||||
|
||||
BinTest_MrubyBinDebugger.test(src, tc)
|
||||
end
|
||||
|
||||
assert('mruby-bin-debugger(print) scope:module') do
|
||||
# ruby source (bp is break point)
|
||||
src = <<"SRC"
|
||||
class TestModuleScope
|
||||
bp = nil
|
||||
end
|
||||
SRC
|
||||
|
||||
# test case
|
||||
tc = []
|
||||
tc << {:cmd=>"s"}
|
||||
tc << {:cmd=>"p self", :exp=>'$1 = TestModuleScope'}
|
||||
|
||||
BinTest_MrubyBinDebugger.test(src, tc)
|
||||
end
|
||||
|
||||
assert('mruby-bin-debugger(print) scope:instance method') do
|
||||
# ruby source (bp is break point)
|
||||
src = <<"SRC"
|
||||
class TestMethodScope
|
||||
def m
|
||||
bp = nil
|
||||
end
|
||||
end
|
||||
TestMethodScope.new.m
|
||||
SRC
|
||||
|
||||
tc = []
|
||||
tc << {:cmd=>"b 3"}
|
||||
tc << {:cmd=>"r"}
|
||||
tc << {:cmd=>"p self", :exp=>'$1 = #<TestMethodScope:'}
|
||||
|
||||
BinTest_MrubyBinDebugger.test(src, tc)
|
||||
end
|
||||
|
||||
assert('mruby-bin-debugger(print) scope:class method') do
|
||||
# ruby source (bp is break point)
|
||||
src = <<"SRC"
|
||||
class TestClassMethodScope
|
||||
def self.cm
|
||||
bp = nil
|
||||
end
|
||||
end
|
||||
TestClassMethodScope.cm
|
||||
SRC
|
||||
|
||||
tc = []
|
||||
tc << {:cmd=>"b 3"}
|
||||
tc << {:cmd=>"r"}
|
||||
tc << {:cmd=>"p self", :exp=>'$1 = TestClassMethodScope'}
|
||||
|
||||
BinTest_MrubyBinDebugger.test(src, tc)
|
||||
end
|
||||
|
||||
assert('mruby-bin-debugger(print) scope:block') do
|
||||
# ruby source (bp is break point)
|
||||
src = <<"SRC"
|
||||
1.times do
|
||||
bp = nil
|
||||
end
|
||||
class TestBlockScope
|
||||
1.times do
|
||||
bp = nil
|
||||
end
|
||||
def m
|
||||
1.times do
|
||||
bp = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
TestBlockScope.new.m
|
||||
SRC
|
||||
|
||||
tc = []
|
||||
tc << {:cmd=>"b 2"}
|
||||
tc << {:cmd=>"b 6"}
|
||||
tc << {:cmd=>"b 10"}
|
||||
tc << {:cmd=>"c"}
|
||||
tc << {:cmd=>"p self", :exp=>'$1 = main'}
|
||||
tc << {:cmd=>"c"}
|
||||
tc << {:cmd=>"p self", :exp=>'$2 = TestBlockScope'}
|
||||
tc << {:cmd=>"c"}
|
||||
tc << {:cmd=>"p self", :exp=>'$3 = #<TestBlockScope:'}
|
||||
|
||||
BinTest_MrubyBinDebugger.test(src, tc)
|
||||
end
|
||||
|
||||
assert('mruby-bin-debugger(print) same name:local variabe') do
|
||||
# ruby source (bp is break point)
|
||||
src = <<"SRC"
|
||||
lv = 'top'
|
||||
class TestLocalVariableName
|
||||
lv = 'class'
|
||||
def m
|
||||
lv = 'instance method'
|
||||
bp = nil
|
||||
end
|
||||
bp = nil
|
||||
end
|
||||
TestLocalVariableName.new.m
|
||||
bp = nil
|
||||
SRC
|
||||
|
||||
tc = []
|
||||
tc << {:cmd=>"b 6"}
|
||||
tc << {:cmd=>"b 8"}
|
||||
tc << {:cmd=>"b 11"}
|
||||
tc << {:cmd=>"r"}
|
||||
tc << {:cmd=>"p lv", :exp=>'$1 = "class"'}
|
||||
tc << {:cmd=>"c"}
|
||||
tc << {:cmd=>"p lv", :exp=>'$2 = "instance method"'}
|
||||
tc << {:cmd=>"c"}
|
||||
tc << {:cmd=>"p lv", :exp=>'$3 = "top"'}
|
||||
|
||||
BinTest_MrubyBinDebugger.test(src, tc)
|
||||
end
|
||||
|
||||
assert('mruby-bin-debugger(print) same name:instance variabe') do
|
||||
# ruby source (bp is break point)
|
||||
src = <<"SRC"
|
||||
@iv = 'top'
|
||||
class TestInstanceVariableName
|
||||
def initialize(v)
|
||||
@iv = v
|
||||
end
|
||||
def m
|
||||
bp = nil
|
||||
end
|
||||
end
|
||||
i1 = TestInstanceVariableName.new('instance1')
|
||||
i2 = TestInstanceVariableName.new('instance2')
|
||||
i1.m
|
||||
i2.m
|
||||
bp = nil
|
||||
SRC
|
||||
|
||||
tc = []
|
||||
tc << {:cmd=>"b 7"}
|
||||
tc << {:cmd=>"b 14"}
|
||||
tc << {:cmd=>"r"}
|
||||
tc << {:cmd=>"p @iv", :exp=>'$1 = "instance1"'}
|
||||
tc << {:cmd=>"c"}
|
||||
tc << {:cmd=>"p @iv", :exp=>'$2 = "instance2"'}
|
||||
tc << {:cmd=>"c"}
|
||||
tc << {:cmd=>"p @iv", :exp=>'$3 = "top"'}
|
||||
|
||||
BinTest_MrubyBinDebugger.test(src, tc)
|
||||
end
|
||||
|
||||
# Kernel#instance_eval(string) does't work const.
|
||||
=begin
|
||||
assert('mruby-bin-debugger(print) same name:const') do
|
||||
# ruby source (bp is break point)
|
||||
src = <<"SRC"
|
||||
CONST='top'
|
||||
class TestConstNameSuperClass
|
||||
CONST='super class'
|
||||
def m
|
||||
bp = nil
|
||||
end
|
||||
end
|
||||
class TestConstNameSubClass < TestConstNameSuperClass
|
||||
CONST='sub class'
|
||||
def m
|
||||
bp = nil
|
||||
end
|
||||
end
|
||||
|
||||
TestConstNameSuperClass.new.m()
|
||||
TestConstNameSubClass.new.m()
|
||||
bp = nil
|
||||
SRC
|
||||
|
||||
# todo: wait for 'break' to be implimented
|
||||
tc = []
|
||||
9.times { tc << {:cmd=>"s"} }
|
||||
tc << {:cmd=>"p CONST", :exp=>"super class"}
|
||||
3.times { tc << {:cmd=>"s"} }
|
||||
tc << {:cmd=>"p CONST", :exp=>"sub class"}
|
||||
1.times { tc << {:cmd=>"s"} }
|
||||
tc << {:cmd=>"p CONST", :exp=>"top"}
|
||||
|
||||
BinTest_MrubyBinDebugger.test(src, tc)
|
||||
end
|
||||
=end
|
||||
|
||||
assert('mruby-bin-debugger(print) Literal:Numeric') do
|
||||
# ruby source
|
||||
src = "foo = 'foo'\n"
|
||||
|
||||
# test case
|
||||
tc = []
|
||||
tc << {:cmd=>"p 100", :exp=>'$1 = 100'}
|
||||
tc << {:cmd=>"p -0b100", :exp=>'$2 = -4'}
|
||||
tc << {:cmd=>"p +0100", :exp=>'$3 = 64'}
|
||||
tc << {:cmd=>"p 0x100", :exp=>'$4 = 256'}
|
||||
tc << {:cmd=>"p 1_234", :exp=>'$5 = 1234'}
|
||||
tc << {:cmd=>"p 0b1000_0000", :exp=>"$6 = #{0b1000_0000.to_s}"}
|
||||
tc << {:cmd=>"p 0x1000_0000", :exp=>"$7 = #{0x1000_0000.to_s}"}
|
||||
|
||||
tc << {:cmd=>"p 3.14", :exp=>'$8 = 3.14'}
|
||||
tc << {:cmd=>"p -12.3", :exp=>'$9 = -12.3'}
|
||||
tc << {:cmd=>"p +12.000", :exp=>'$10 = 12.0'}
|
||||
tc << {:cmd=>"p 1e4", :exp=>'$11 = 10000.0'}
|
||||
tc << {:cmd=>"p -0.1e-2", :exp=>'$12 = -0.001'}
|
||||
|
||||
BinTest_MrubyBinDebugger.test(src, tc)
|
||||
end
|
||||
|
||||
assert('mruby-bin-debugger(print) Literal:String') do
|
||||
# ruby source
|
||||
src = <<"SRC"
|
||||
foo = 'foo'
|
||||
bar = "bar"
|
||||
baz = "baz"
|
||||
SRC
|
||||
|
||||
# test case
|
||||
tc = []
|
||||
tc << {:cmd=>"s"}
|
||||
tc << {:cmd=>"s"}
|
||||
|
||||
tc << {:cmd=>'p "str"', :exp=>'$1 = "str"'}
|
||||
tc << {:cmd=>'p "s\tt\rr\n"', :exp=>'$2 = "s\\tt\\rr\\n"'}
|
||||
tc << {:cmd=>'p "\C-a\C-z"', :exp=>'$3 = "\\001\\032"'}
|
||||
tc << {:cmd=>'p "#{foo+bar}"', :exp=>'$4 = "foobar"'}
|
||||
|
||||
tc << {:cmd=>'p \'str\'', :exp=>'$5 = "str"'}
|
||||
tc << {:cmd=>'p \'s\\tt\\rr\\n\'', :exp=>'$6 = "s\\\\tt\\\\rr\\\\n"'}
|
||||
tc << {:cmd=>'p \'\\C-a\\C-z\'', :exp=>'$7 = "\\\\C-a\\\\C-z"'}
|
||||
tc << {:cmd=>'p \'#{foo+bar}\'', :exp=>'$8 = "#{foo+bar}"'}
|
||||
|
||||
tc << {:cmd=>'p %!str!', :exp=>'$9 = "str"'}
|
||||
tc << {:cmd=>'p %!s\tt\rr\n!', :exp=>'$10 = "s\\tt\\rr\\n"'}
|
||||
tc << {:cmd=>'p %!\C-a\C-z!', :exp=>'$11 = "\\001\\032"'}
|
||||
tc << {:cmd=>'p %!#{foo+bar}!', :exp=>'$12 = "foobar"'}
|
||||
|
||||
tc << {:cmd=>'p %Q!str!', :exp=>'$13 = "str"'}
|
||||
tc << {:cmd=>'p %Q!s\tt\rr\n!', :exp=>'$14 = "s\\tt\\rr\\n"'}
|
||||
tc << {:cmd=>'p %Q!\C-a\C-z!', :exp=>'$15 = "\\001\\032"'}
|
||||
tc << {:cmd=>'p %Q!#{foo+bar}!', :exp=>'$16 = "foobar"'}
|
||||
|
||||
tc << {:cmd=>'p %q!str!', :exp=>'$17 = "str"'}
|
||||
tc << {:cmd=>'p %q!s\\tt\\rr\\n!', :exp=>'$18 = "s\\\\tt\\\\rr\\\\n"'}
|
||||
tc << {:cmd=>'p %q!\\C-a\\C-z!', :exp=>'$19 = "\\\\C-a\\\\C-z"'}
|
||||
tc << {:cmd=>'p %q!#{foo+bar}!', :exp=>'$20 = "#{foo+bar}"'}
|
||||
|
||||
BinTest_MrubyBinDebugger.test(src, tc)
|
||||
end
|
||||
|
||||
assert('mruby-bin-debugger(print) Literal:Array') do
|
||||
# ruby source
|
||||
src = <<"SRC"
|
||||
foo = 'foo'
|
||||
bar = "bar"
|
||||
baz = "baz"
|
||||
SRC
|
||||
|
||||
# test case
|
||||
tc = []
|
||||
tc << {:cmd=>"s"}
|
||||
tc << {:cmd=>"s"}
|
||||
|
||||
tc << {:cmd=>'p []', :exp=>'$1 = []'}
|
||||
tc << {:cmd=>'p [ 5, 12, 8, 10, ]', :exp=>'$2 = [5, 12, 8, 10]'}
|
||||
tc << {:cmd=>'p [1,2.5,"#{foo+bar}"]', :exp=>'$3 = [1, 2.5, "foobar"]'}
|
||||
tc << {:cmd=>'p %w[3.14 A\ &\ B #{foo}]', :exp=>'$4 = ["3.14", "A & B", "#{foo}"]'}
|
||||
tc << {:cmd=>'p %W[3.14 A\ &\ B #{foo}]', :exp=>'$5 = ["3.14", "A & B", "foo"]'}
|
||||
|
||||
BinTest_MrubyBinDebugger.test(src, tc)
|
||||
end
|
||||
|
||||
assert('mruby-bin-debugger(print) Literal:Hash') do
|
||||
# ruby source
|
||||
src = <<"SRC"
|
||||
foo = 'foo'
|
||||
bar = "bar"
|
||||
baz = "baz"
|
||||
SRC
|
||||
|
||||
# test case
|
||||
tc = []
|
||||
tc << {:cmd=>"s"}
|
||||
tc << {:cmd=>"s"}
|
||||
|
||||
tc << {:cmd=>'p {}', :exp=>'$1 = {}'}
|
||||
tc << {:cmd=>'p {"one"=>1,"two"=>2}', :exp=>'$2 = {"one"=>1, "two"=>2}'}
|
||||
tc << {:cmd=>'p {:eins=>"1", :zwei=>"2", }', :exp=>'$3 = {:eins=>"1", :zwei=>"2"}'}
|
||||
tc << {:cmd=>'p {uno:"one", dos: 2}', :exp=>'$4 = {:uno=>"one", :dos=>2}'}
|
||||
tc << {:cmd=>'p {"one"=>1, :zwei=>2, tres:3}', :exp=>'$5 = {"one"=>1, :zwei=>2, :tres=>3}'}
|
||||
tc << {:cmd=>'p {:foo=>"#{foo}",:bar=>"#{bar}"}', :exp=>'$6 = {:foo=>"foo", :bar=>"bar"}'}
|
||||
|
||||
BinTest_MrubyBinDebugger.test(src, tc)
|
||||
end
|
||||
|
||||
assert('mruby-bin-debugger(print) Literal:Range') do
|
||||
# ruby source
|
||||
src = "foo = 'foo'\n"
|
||||
|
||||
# test case
|
||||
tc = []
|
||||
tc << {:cmd=>'p 1..10', :exp=>'$1 = 1..10'}
|
||||
tc << {:cmd=>'p 1...10', :exp=>'$2 = 1...10'}
|
||||
tc << {:cmd=>'p 100..10', :exp=>'$3 = 100..10'}
|
||||
tc << {:cmd=>'p 1 ... 10', :exp=>'$4 = 1...10'}
|
||||
|
||||
tc << {:cmd=>'p "1" .. "9"', :exp=>'$5 = "1".."9"'}
|
||||
tc << {:cmd=>'p "A" ... "Z"', :exp=>'$6 = "A"..."Z"'}
|
||||
|
||||
BinTest_MrubyBinDebugger.test(src, tc)
|
||||
end
|
||||
|
||||
assert('mruby-bin-debugger(print) Literal:Symbol') do
|
||||
# ruby source
|
||||
src = <<"SRC"
|
||||
foo = 'foo'
|
||||
bar = "bar"
|
||||
baz = "baz"
|
||||
SRC
|
||||
|
||||
# test case
|
||||
tc = []
|
||||
tc << {:cmd=>"s"}
|
||||
tc << {:cmd=>"s"}
|
||||
|
||||
tc << {:cmd=>'p :sym', :exp=>'$1 = :sym'}
|
||||
tc << {:cmd=>'p :"sd"', :exp=>'$2 = :sd'}
|
||||
tc << {:cmd=>"p :'ss'", :exp=>'$3 = :ss'}
|
||||
tc << {:cmd=>'p :"123"', :exp=>'$4 = :"123"'}
|
||||
tc << {:cmd=>'p :"#{foo} baz"', :exp=>'$5 = :"foo baz"'}
|
||||
tc << {:cmd=>'p %s!symsym!', :exp=>'$6 = :symsym'}
|
||||
|
||||
BinTest_MrubyBinDebugger.test(src, tc)
|
||||
end
|
||||
|
||||
assert('mruby-bin-debugger(print) Unary operation') do
|
||||
# ruby source
|
||||
src = "foo = 'foo'\n"
|
||||
|
||||
# test case
|
||||
tc = []
|
||||
tc << {:cmd=>'p +10', :exp=>'$1 = 10'}
|
||||
tc << {:cmd=>'p -100', :exp=>'$2 = -100'}
|
||||
tc << {:cmd=>'p !true', :exp=>'$3 = false'}
|
||||
tc << {:cmd=>'p !false', :exp=>'$4 = true'}
|
||||
tc << {:cmd=>'p !nil', :exp=>'$5 = true'}
|
||||
tc << {:cmd=>'p !1', :exp=>'$6 = false'}
|
||||
|
||||
BinTest_MrubyBinDebugger.test(src, tc)
|
||||
end
|
||||
|
||||
assert('mruby-bin-debugger(print) Binary operation') do
|
||||
# ruby source
|
||||
src = <<"SRC"
|
||||
CONST = 100
|
||||
a,b,c = 1, 5, 8
|
||||
foo,bar,baz = 'foo','bar','baz'
|
||||
ary = []
|
||||
SRC
|
||||
|
||||
# test case
|
||||
tc = []
|
||||
tc << {:cmd=>'s'}
|
||||
tc << {:cmd=>'s'}
|
||||
tc << {:cmd=>'s'}
|
||||
|
||||
tc << {:cmd=>'p a+1', :exp=>'$1 = 2'}
|
||||
tc << {:cmd=>'p 2-b', :exp=>'$2 = -3'}
|
||||
tc << {:cmd=>'p c * 3', :exp=>'$3 = 24'}
|
||||
tc << {:cmd=>'p a/b', :exp=>'$4 = 0.2'}
|
||||
tc << {:cmd=>'p c%b', :exp=>'$5 = 3'}
|
||||
tc << {:cmd=>'p 2**10', :exp=>'$6 = 1024'}
|
||||
tc << {:cmd=>'p ~3', :exp=>'$7 = -4'}
|
||||
|
||||
tc << {:cmd=>'p 1<<2', :exp=>'$8 = 4'}
|
||||
tc << {:cmd=>'p 64>>5', :exp=>'$9 = 2'}
|
||||
|
||||
tc << {:cmd=>'p a|c', :exp=>'$10 = 9'}
|
||||
tc << {:cmd=>'p a&b', :exp=>'$11 = 1'}
|
||||
tc << {:cmd=>'p a^b', :exp=>'$12 = 4'}
|
||||
|
||||
tc << {:cmd=>'p a>b', :exp=>'$13 = false'}
|
||||
tc << {:cmd=>'p a<b', :exp=>'$14 = true'}
|
||||
tc << {:cmd=>'p b>=5', :exp=>'$15 = true'}
|
||||
tc << {:cmd=>'p b<=5', :exp=>'$16 = true'}
|
||||
|
||||
tc << {:cmd=>'p "A"<=>"B"', :exp=>'$17 = -1'}
|
||||
tc << {:cmd=>'p "A"=="B"', :exp=>'$18 = false'}
|
||||
tc << {:cmd=>'p "A"==="B"', :exp=>'$19 = false'}
|
||||
tc << {:cmd=>'p "A"!="B"', :exp=>'$20 = true'}
|
||||
|
||||
tc << {:cmd=>'p false || true', :exp=>'$21 = true'}
|
||||
tc << {:cmd=>'p false && true', :exp=>'$22 = false'}
|
||||
|
||||
tc << {:cmd=>'p not nil', :exp=>'$23 = true'}
|
||||
tc << {:cmd=>'p false or true', :exp=>'$24 = true'}
|
||||
tc << {:cmd=>'p false and true', :exp=>'$25 = false'}
|
||||
|
||||
BinTest_MrubyBinDebugger.test(src, tc)
|
||||
end
|
||||
|
||||
assert('mruby-bin-debugger(print) Ternary operation') do
|
||||
# ruby source
|
||||
src = <<"SRC"
|
||||
CONST = 100
|
||||
a,b,c = 1, 5, -10
|
||||
foo,bar,baz = 'foo','bar','baz'
|
||||
ary = []
|
||||
SRC
|
||||
|
||||
# test case
|
||||
tc = []
|
||||
tc << {:cmd=>'s'}
|
||||
tc << {:cmd=>'s'}
|
||||
tc << {:cmd=>'s'}
|
||||
|
||||
tc << {:cmd=>'p (a < b) ? a : b', :exp=>'$1 = 1'}
|
||||
tc << {:cmd=>'p (a > b) ? a : b', :exp=>'$2 = 5'}
|
||||
tc << {:cmd=>'p true ? "true" : "false"', :exp=>'$3 = "true"'}
|
||||
tc << {:cmd=>'p false ? "true" : "false"', :exp=>'$4 = "false"'}
|
||||
tc << {:cmd=>'p nil ? "true" : "false"', :exp=>'$5 = "false"'}
|
||||
|
||||
BinTest_MrubyBinDebugger.test(src, tc)
|
||||
end
|
||||
|
||||
assert('mruby-bin-debugger(print) Substitution:simple') do
|
||||
# ruby source
|
||||
src = <<"SRC"
|
||||
CONST = 100
|
||||
a,b,c = 1, 5, -10
|
||||
foo,bar,baz = 'foo','bar','baz'
|
||||
ary = []
|
||||
SRC
|
||||
|
||||
# test case
|
||||
tc = []
|
||||
tc << {:cmd=>'s'}
|
||||
tc << {:cmd=>'s'}
|
||||
tc << {:cmd=>'s'}
|
||||
|
||||
tc << {:cmd=>'p a=2', :exp=>'$1 = 2'}
|
||||
tc << {:cmd=>'p foo=[foo,bar,baz]', :exp=>'$2 = ["foo", "bar", "baz"]'}
|
||||
|
||||
tc << {:cmd=>'p undefined=-1', :exp=>'$3 = -1'}
|
||||
tc << {:cmd=>'p "#{undefined}"', :exp=>'$4 = NoMethodError'}
|
||||
|
||||
BinTest_MrubyBinDebugger.test(src, tc)
|
||||
end
|
||||
|
||||
assert('mruby-bin-debugger(print) Substitution:self') do
|
||||
# ruby source
|
||||
src = <<"SRC"
|
||||
CONST = 100
|
||||
a,b,c = 1, 5, -10
|
||||
foo,bar,baz = 'foo','bar','baz'
|
||||
ary = []
|
||||
SRC
|
||||
|
||||
# test case
|
||||
tc = []
|
||||
tc << {:cmd=>'s'}
|
||||
tc << {:cmd=>'s'}
|
||||
tc << {:cmd=>'s'}
|
||||
|
||||
tc << {:cmd=>'p a+=9', :exp=>'$1 = 10'}
|
||||
tc << {:cmd=>'p b-=c', :exp=>'$2 = 15'}
|
||||
tc << {:cmd=>'p bar*=2', :exp=>'$3 = "barbar"'}
|
||||
tc << {:cmd=>'p a/=4', :exp=>'$4 = 2.5'}
|
||||
tc << {:cmd=>'p c%=4', :exp=>'$5 = 2'}
|
||||
|
||||
tc << {:cmd=>'p b&=0b0101', :exp=>'$6 = 5'}
|
||||
tc << {:cmd=>'p c|=0x10', :exp=>'$7 = 18'}
|
||||
|
||||
tc << {:cmd=>'p "#{a} #{b} #{c}"', :exp=>'$8 = "2.5 5 18"'}
|
||||
tc << {:cmd=>'p "#{foo}#{bar}#{baz}"', :exp=>'$9 = "foobarbarbaz"'}
|
||||
|
||||
tc << {:cmd=>'p a,b,c=[10,20,30]',:exp=>'$10 = [10, 20, 30]'}
|
||||
tc << {:cmd=>'p [a,b,c]', :exp=>'$11 = [10, 20, 30]'}
|
||||
tc << {:cmd=>'p a,b=b,a', :exp=>'$12 = [20, 10]'}
|
||||
tc << {:cmd=>'p [a,b]', :exp=>'$13 = [20, 10]'}
|
||||
|
||||
tc << {:cmd=>'p undefined=-1', :exp=>'$14 = -1'}
|
||||
tc << {:cmd=>'p "#{undefined}"', :exp=>'$15 = NoMethodError'}
|
||||
|
||||
BinTest_MrubyBinDebugger.test(src, tc)
|
||||
end
|
||||
|
||||
assert('mruby-bin-debugger(print) Substitution:multiple') do
|
||||
# ruby source
|
||||
src = <<"SRC"
|
||||
CONST = 100
|
||||
a,b,c = 1, 5, -10
|
||||
foo,bar,baz = 'foo','bar','baz'
|
||||
ary = []
|
||||
SRC
|
||||
|
||||
# test case
|
||||
tc = []
|
||||
tc << {:cmd=>'s'}
|
||||
tc << {:cmd=>'s'}
|
||||
tc << {:cmd=>'s'}
|
||||
|
||||
tc << {:cmd=>'p a,b=[10,20]', :exp=>'$1 = [10, 20]'}
|
||||
tc << {:cmd=>'p [a,b,c]', :exp=>'$2 = [10, 20, -10]'}
|
||||
|
||||
tc << {:cmd=>'p foo,bar=["FOO","BAR","BAZ"]', :exp=>'$3 = ["FOO", "BAR", "BAZ"]'}
|
||||
tc << {:cmd=>'p [foo,bar,baz]', :exp=>'$4 = ["FOO", "BAR", "baz"]'}
|
||||
|
||||
tc << {:cmd=>'p a,foo=foo,a', :exp=>'$5 = ["FOO", 10]'}
|
||||
tc << {:cmd=>'p [a,foo]', :exp=>'$6 = ["FOO", 10]'}
|
||||
|
||||
# tc << {:cmd=>'p a,*b=[123, 456, 789]'}
|
||||
# tc << {:cmd=>'p [a,b]', :exp=>'[123, [456, 789]]'}
|
||||
|
||||
BinTest_MrubyBinDebugger.test(src, tc)
|
||||
end
|
||||
|
||||
assert('mruby-bin-debugger(print) Substitution:self') do
|
||||
# ruby source
|
||||
src = <<"SRC"
|
||||
CONST = 100
|
||||
a,b,c = 1, 5, -10
|
||||
foo,bar,baz = 'foo','bar','baz'
|
||||
ary = []
|
||||
SRC
|
||||
|
||||
# test case
|
||||
tc = []
|
||||
tc << {:cmd=>'s'}
|
||||
tc << {:cmd=>'s'}
|
||||
tc << {:cmd=>'s'}
|
||||
|
||||
tc << {:cmd=>'p a+=9', :exp=>'$1 = 10'}
|
||||
tc << {:cmd=>'p b-=c', :exp=>'$2 = 15'}
|
||||
tc << {:cmd=>'p bar*=2', :exp=>'$3 = "barbar"'}
|
||||
tc << {:cmd=>'p a/=4', :exp=>'$4 = 2.5'}
|
||||
tc << {:cmd=>'p c%=4', :exp=>'$5 = 2'}
|
||||
|
||||
tc << {:cmd=>'p b&=0b0101', :exp=>'$6 = 5'}
|
||||
tc << {:cmd=>'p c|=0x10', :exp=>'$7 = 18'}
|
||||
|
||||
tc << {:cmd=>'p "#{a} #{b} #{c}"', :exp=>'$8 = "2.5 5 18"'}
|
||||
tc << {:cmd=>'p "#{foo}#{bar}#{baz}"', :exp=>'$9 = "foobarbarbaz"'}
|
||||
|
||||
tc << {:cmd=>'p a,b,c=[10,20,30]',:exp=>'$10 = [10, 20, 30]'}
|
||||
tc << {:cmd=>'p [a,b,c]', :exp=>'$11 = [10, 20, 30]'}
|
||||
tc << {:cmd=>'p a,b=b,a', :exp=>'$12 = [20, 10]'}
|
||||
tc << {:cmd=>'p [a,b]', :exp=>'$13 = [20, 10]'}
|
||||
|
||||
tc << {:cmd=>'p undefined=-1', :exp=>'$14 = -1'}
|
||||
tc << {:cmd=>'p "#{undefined}"', :exp=>'$15 = NoMethodError'}
|
||||
|
||||
BinTest_MrubyBinDebugger.test(src, tc)
|
||||
end
|
||||
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
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
|
||||
+529
@@ -0,0 +1,529 @@
|
||||
/*
|
||||
** 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, int32_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( 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) {
|
||||
info_file = irep->debug_info->files[f_idx];
|
||||
if(!strcmp(info_file->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(irep->reps[i], file, lineno);
|
||||
if(result == (MRB_DEBUG_BP_FILE_OK | MRB_DEBUG_BP_LINENO_OK)) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static const char*
|
||||
get_class_name( mrb_state *mrb, struct RClass *class_obj )
|
||||
{
|
||||
struct RClass *outer;
|
||||
mrb_sym class_sym;
|
||||
|
||||
outer = mrb_class_outer_module(mrb, class_obj);
|
||||
class_sym = mrb_class_sym(mrb, class_obj, outer);
|
||||
return mrb_sym2name(mrb, class_sym);
|
||||
}
|
||||
|
||||
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;
|
||||
struct RProc* m;
|
||||
struct RClass* sc;
|
||||
const char* sn;
|
||||
mrb_sym ssym;
|
||||
mrb_debug_methodpoint *method_p;
|
||||
mrb_bool is_defined;
|
||||
|
||||
method_name = mrb_sym2name(mrb, method_sym);
|
||||
|
||||
method_p = &bp->point.methodpoint;
|
||||
if(strcmp(method_p->method_name, method_name) == 0) {
|
||||
class_name = get_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(m == NULL) {
|
||||
return MRB_DEBUG_OK;
|
||||
}
|
||||
if(MRB_PROC_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(m == NULL) {
|
||||
return MRB_DEBUG_OK;
|
||||
}
|
||||
|
||||
class_name = get_class_name(mrb, class_obj);
|
||||
sn = get_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( 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 = mrb_malloc(mrb, strlen(file) + 1);
|
||||
if(set_file == NULL) {
|
||||
return MRB_DEBUG_NOBUF;
|
||||
}
|
||||
|
||||
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 = mrb_malloc(mrb, strlen(class_name) + 1);
|
||||
if(set_class == NULL) {
|
||||
return MRB_DEBUG_NOBUF;
|
||||
}
|
||||
|
||||
strncpy(set_class, class_name, strlen(class_name) + 1);
|
||||
}
|
||||
else {
|
||||
set_class = NULL;
|
||||
}
|
||||
|
||||
set_method = mrb_malloc(mrb, strlen(method_name) + 1);
|
||||
if(set_method == NULL) {
|
||||
if(set_class != NULL) {
|
||||
mrb_free(mrb, (void*)set_class);
|
||||
}
|
||||
return MRB_DEBUG_NOBUF;
|
||||
}
|
||||
|
||||
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 )
|
||||
{
|
||||
uint32_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(dbg->bp[i + 1].type == MRB_DEBUG_BPTYPE_NONE) {
|
||||
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_irep *irep, mrb_code *pc, uint16_t line )
|
||||
{
|
||||
if( pc > irep->iseq ) {
|
||||
if( line == mrb_debug_get_line(irep, (uint32_t)(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;
|
||||
int i;
|
||||
|
||||
if((mrb == NULL) || (dbg == NULL) || (file == NULL) || (line <= 0)) {
|
||||
return MRB_DEBUG_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if(!check_start_pc_for_line(dbg->irep, dbg->pc, line)) {
|
||||
return MRB_DEBUG_OK;
|
||||
}
|
||||
|
||||
bp = dbg->bp;
|
||||
for(i=0; i<MAX_BREAKPOINT; 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;
|
||||
int i;
|
||||
|
||||
if((mrb == NULL) || (dbg == NULL) || (class_obj == NULL)) {
|
||||
return MRB_DEBUG_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
bp = dbg->bp;
|
||||
for(i=0; i<MAX_BREAKPOINT; 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;
|
||||
}
|
||||
|
||||
|
||||
+26
@@ -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_ */
|
||||
+232
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
+14
@@ -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_ */
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
** 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;
|
||||
}
|
||||
+13
@@ -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*);
|
||||
|
||||
#endif /* APIPRINT_H_ */
|
||||
+426
@@ -0,0 +1,426 @@
|
||||
/*
|
||||
** 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) \
|
||||
ul = 0; \
|
||||
for(int 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)
|
||||
{
|
||||
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(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 = strchr(args, ':')) == NULL) {
|
||||
body = args;
|
||||
type = check_bptype(body);
|
||||
} else {
|
||||
if(body == args) {
|
||||
printf(BREAK_ERR_MSG_INVALIDSTR, args);
|
||||
return MRB_DEBUG_BPTYPE_NONE;
|
||||
}
|
||||
*body = '\0';
|
||||
type = check_bptype(++body);
|
||||
}
|
||||
|
||||
switch(type) {
|
||||
case MRB_DEBUG_BPTYPE_LINE:
|
||||
STRTOUL(l, body);
|
||||
if( l <= 65535 ) {
|
||||
*line = l;
|
||||
*file = (body == args)? mrb_debug_get_filename(dbg->irep, (uint32_t)(dbg->pc - dbg->irep->iseq)): args;
|
||||
} else {
|
||||
puts(BREAK_ERR_MSG_RANGEOVER);
|
||||
type = MRB_DEBUG_BPTYPE_NONE;
|
||||
}
|
||||
break;
|
||||
case MRB_DEBUG_BPTYPE_METHOD:
|
||||
if(body == args) {
|
||||
/* method only */
|
||||
if( ISUPPER(*body)||ISLOWER(*body)||(*body == '_') ) {
|
||||
*method = body;
|
||||
*cname = NULL;
|
||||
} else {
|
||||
printf(BREAK_ERR_MSG_INVALIDMETHOD, args);
|
||||
type = MRB_DEBUG_BPTYPE_NONE;
|
||||
}
|
||||
} else {
|
||||
if( ISUPPER(*args) ) {
|
||||
switch(*body) {
|
||||
case '@': case '$': case '?': case '.': case ',': case ':':
|
||||
case ';': case '#': case '\\': case '\'': case '\"':
|
||||
printf(BREAK_ERR_MSG_INVALIDMETHOD, body);
|
||||
type = MRB_DEBUG_BPTYPE_NONE;
|
||||
break;
|
||||
default:
|
||||
*method = body;
|
||||
*cname = args;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
printf(BREAK_ERR_MSG_INVALIDCLASS, args);
|
||||
type = MRB_DEBUG_BPTYPE_NONE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MRB_DEBUG_BPTYPE_NONE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
dbgcmd_state
|
||||
dbgcmd_break(mrb_state *mrb, mrdb_state *mrdb)
|
||||
{
|
||||
mrb_debug_bptype type;
|
||||
mrb_debug_context *dbg = mrdb->dbg;
|
||||
const char *file = NULL;
|
||||
uint32_t line = 0;
|
||||
char *cname = NULL;
|
||||
char *method = NULL;
|
||||
int32_t ret;
|
||||
|
||||
type = parse_breakcommand(mrdb, &file, &line, &cname, &method);
|
||||
switch (type) {
|
||||
case MRB_DEBUG_BPTYPE_LINE:
|
||||
ret = mrb_debug_set_break_line(mrb, dbg, file, line);
|
||||
break;
|
||||
case MRB_DEBUG_BPTYPE_METHOD:
|
||||
ret = mrb_debug_set_break_method(mrb, dbg, cname, method);
|
||||
break;
|
||||
case MRB_DEBUG_BPTYPE_NONE:
|
||||
default:
|
||||
return DBGST_PROMPT;
|
||||
}
|
||||
|
||||
if (ret >= 0) {
|
||||
if (type == MRB_DEBUG_BPTYPE_LINE) {
|
||||
printf(BREAK_SET_MSG_LINE, ret, file, line);
|
||||
} else if ((type == MRB_DEBUG_BPTYPE_METHOD)&&(cname == NULL)) {
|
||||
printf(BREAK_SET_MSG_METHOD, ret, method);
|
||||
} else {
|
||||
printf(BREAK_SET_MSG_CLASS_METHOD, ret, cname, method);
|
||||
}
|
||||
} else {
|
||||
switch (ret) {
|
||||
case MRB_DEBUG_BREAK_INVALID_LINENO:
|
||||
printf(BREAK_ERR_MSG_INVALIDLINENO, line, file);
|
||||
break;
|
||||
case MRB_DEBUG_BREAK_INVALID_FILE:
|
||||
printf(BREAK_ERR_MSG_INVALIDFILE, file);
|
||||
break;
|
||||
case MRB_DEBUG_BREAK_NUM_OVER:
|
||||
puts(BREAK_ERR_MSG_NUMOVER);
|
||||
break;
|
||||
case MRB_DEBUG_BREAK_NO_OVER:
|
||||
puts(BREAK_ERR_MSG_NOOVER);
|
||||
break;
|
||||
case MRB_DEBUG_INVALID_ARGUMENT:
|
||||
puts(BREAK_ERR_MSG_INVALIDARG);
|
||||
break;
|
||||
case MRB_DEBUG_NOBUF:
|
||||
puts("T.B.D.");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return DBGST_PROMPT;
|
||||
}
|
||||
|
||||
dbgcmd_state
|
||||
dbgcmd_info_break(mrb_state *mrb, mrdb_state *mrdb)
|
||||
{
|
||||
if(mrdb->wcnt == 2) {
|
||||
info_break_all(mrb, mrdb);
|
||||
}
|
||||
else {
|
||||
info_break_select(mrb, mrdb);
|
||||
}
|
||||
|
||||
return DBGST_PROMPT;
|
||||
}
|
||||
|
||||
dbgcmd_state
|
||||
dbgcmd_delete(mrb_state *mrb, mrdb_state *mrdb)
|
||||
{
|
||||
mrb_bool ret = FALSE;
|
||||
|
||||
ret = exe_set_command_all(mrb, mrdb, mrb_debug_delete_break_all);
|
||||
if(ret != TRUE) {
|
||||
exe_set_command_select(mrb, mrdb, mrb_debug_delete_break);
|
||||
}
|
||||
|
||||
return DBGST_PROMPT;
|
||||
}
|
||||
|
||||
dbgcmd_state
|
||||
dbgcmd_enable(mrb_state *mrb, mrdb_state *mrdb)
|
||||
{
|
||||
mrb_bool ret = FALSE;
|
||||
|
||||
ret = exe_set_command_all(mrb, mrdb, mrb_debug_enable_break_all);
|
||||
if(ret != TRUE) {
|
||||
exe_set_command_select(mrb, mrdb, mrb_debug_enable_break);
|
||||
}
|
||||
|
||||
return DBGST_PROMPT;
|
||||
}
|
||||
|
||||
dbgcmd_state
|
||||
dbgcmd_disable(mrb_state *mrb, mrdb_state *mrdb)
|
||||
{
|
||||
mrb_bool ret = FALSE;
|
||||
|
||||
ret = exe_set_command_all(mrb, mrdb, mrb_debug_disable_break_all);
|
||||
if(ret != TRUE) {
|
||||
exe_set_command_select(mrb, mrdb, mrb_debug_disable_break);
|
||||
}
|
||||
return DBGST_PROMPT;
|
||||
}
|
||||
+500
@@ -0,0 +1,500 @@
|
||||
/*
|
||||
** 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 comming.\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"
|
||||
},
|
||||
{
|
||||
"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 = 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 = 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;
|
||||
char *p, *s;
|
||||
|
||||
if (filename == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((p = strrchr(filename, '.')) != NULL && strchr(p, '/') == NULL) {
|
||||
len = p - filename;
|
||||
}
|
||||
else {
|
||||
len = strlen(filename);
|
||||
}
|
||||
|
||||
if ((s = mrb_malloc(mrb, len + strlen(ext) + 1)) != NULL) {
|
||||
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)
|
||||
{
|
||||
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);
|
||||
listcmd_parser_state_free(mrb, st);
|
||||
|
||||
if (filename != NULL && filename != st->filename) {
|
||||
mrb_free(mrb, filename);
|
||||
}
|
||||
}
|
||||
|
||||
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) ");
|
||||
|
||||
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_class_get(mrb, "Exception"));
|
||||
mrb_raise(mrb, exc, "Exit mrdb.");
|
||||
}
|
||||
return DBGST_PROMPT;
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
** 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);
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
** 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;
|
||||
}
|
||||
Executable
+746
@@ -0,0 +1,746 @@
|
||||
/*
|
||||
** mrdb.c - mruby debugger
|
||||
**
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.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 mrb_show_version(mrb_state *);
|
||||
void mrb_show_copyright(mrb_state *);
|
||||
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] */
|
||||
{"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] */
|
||||
{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_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_free(mrb, _debug_context);
|
||||
_debug_context = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static mrdb_state*
|
||||
mrdb_state_new(mrb_state *mrb)
|
||||
{
|
||||
mrdb_state *mrdb = mrb_malloc(mrb, sizeof(mrb_state));
|
||||
|
||||
memset(mrdb, 0, sizeof(mrb_state));
|
||||
|
||||
mrdb->dbg = mrb_debug_context_get(mrb);
|
||||
mrdb->command = 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 += strlen("quit");
|
||||
}
|
||||
|
||||
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);
|
||||
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 const int32_t
|
||||
check_method_breakpoint(mrb_state *mrb, mrb_irep *irep, mrb_code *pc, mrb_value *regs)
|
||||
{
|
||||
struct RClass* c;
|
||||
mrb_sym sym;
|
||||
int32_t bpno;
|
||||
mrb_bool isCfunc;
|
||||
|
||||
mrb_debug_context *dbg = mrb_debug_context_get(mrb);
|
||||
|
||||
isCfunc = FALSE;
|
||||
bpno = dbg->method_bpno;
|
||||
dbg->method_bpno = 0;
|
||||
|
||||
switch(GET_OPCODE(*pc)) {
|
||||
case OP_SEND:
|
||||
case OP_SENDB:
|
||||
c = mrb_class(mrb, regs[GETARG_A(*pc)]);
|
||||
sym = irep->syms[GETARG_B(*pc)];
|
||||
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, 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->xm = DBG_RUN;
|
||||
dbg->xphase = DBG_PHASE_RUNNING;
|
||||
}
|
||||
|
||||
file = mrb_debug_get_filename(irep, (uint32_t)(pc - irep->iseq));
|
||||
line = mrb_debug_get_line(irep, (uint32_t)(pc - irep->iseq));
|
||||
|
||||
switch (dbg->xm) {
|
||||
case DBG_STEP:
|
||||
case DBG_NEXT: // temporary
|
||||
if (dbg->prvfile == file && dbg->prvline == line) {
|
||||
return;
|
||||
}
|
||||
dbg->method_bpno = 0;
|
||||
dbg->bm = BRK_STEP;
|
||||
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;
|
||||
|
||||
if (mrb == NULL) {
|
||||
fputs("Invalid mrb_state, exiting mruby\n", stderr);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
l_restart:
|
||||
|
||||
/* 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;
|
||||
}
|
||||
Executable
+163
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
** 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
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
** 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
|
||||
+20
@@ -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