debug 0.2.0 → 1.0.0.alpha0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +0 -1
- data/LICENSE.txt +19 -20
- data/README.md +142 -28
- data/Rakefile +9 -0
- data/debug.gemspec +19 -9
- data/exe/rdbg +3 -0
- data/lib/debug.rb +11 -1107
- data/lib/debug/bp.vim +68 -0
- data/lib/debug/breakpoint.rb +97 -0
- data/lib/debug/client.rb +135 -0
- data/lib/debug/config.rb +26 -0
- data/lib/debug/repl.rb +67 -0
- data/lib/debug/server.rb +119 -0
- data/lib/debug/session.rb +598 -0
- data/lib/debug/source_repository.rb +20 -0
- data/lib/debug/tcpserver.rb +23 -0
- data/lib/debug/thread_client.rb +402 -0
- data/lib/debug/unixserver.rb +19 -0
- data/lib/debug/version.rb +3 -0
- metadata +61 -21
- data/Gemfile +0 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 58f91a1d4079133e3e7590329d65b7b811490f7158ef96d7b3f826968bc3ad1f
|
4
|
+
data.tar.gz: 4da17de6911dc4506cb869298d84df46de08121aca704bb9e2643f774015fbb3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: baa1a1708b02b63f47d7d751ce9e5830edcdc690c8ca84c956f0e4ce839db5f3f63cf22e856ad4cec43190bb7401b8dcfd13a3d55de72d0a438520a3fab394bc
|
7
|
+
data.tar.gz: 3816a302b56757b55501950ff2814e21d978a5cdf58a4431d663bd1dfbdaae1c7652e0a9565e4401201b29119cdfaa35f47986eee1b17b0b60c1759ec21f3e6a
|
data/.gitignore
CHANGED
data/LICENSE.txt
CHANGED
@@ -1,22 +1,21 @@
|
|
1
|
-
|
1
|
+
The MIT License (MIT)
|
2
2
|
|
3
|
-
|
4
|
-
modification, are permitted provided that the following conditions
|
5
|
-
are met:
|
6
|
-
1. Redistributions of source code must retain the above copyright
|
7
|
-
notice, this list of conditions and the following disclaimer.
|
8
|
-
2. Redistributions in binary form must reproduce the above copyright
|
9
|
-
notice, this list of conditions and the following disclaimer in the
|
10
|
-
documentation and/or other materials provided with the distribution.
|
3
|
+
Copyright (c) 2021 Koichi Sasada
|
11
4
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
CHANGED
@@ -1,52 +1,166 @@
|
|
1
|
-
#
|
1
|
+
# debug.rb
|
2
2
|
|
3
|
-
|
3
|
+
## How to install
|
4
4
|
|
5
|
-
|
5
|
+
This is temporary installation guide until gemify.
|
6
6
|
|
7
|
-
|
7
|
+
```
|
8
|
+
$ git clone https://github.com/ko1/debug.git
|
9
|
+
$ gem install debug_inspector
|
10
|
+
$ gem install iseq_collector
|
11
|
+
$ export RUBYOPT=-I`pwd`/debug/lib
|
12
|
+
# or add "-I`pwd`/debug/lib" for the following command
|
13
|
+
```
|
14
|
+
|
15
|
+
# How to use
|
16
|
+
|
17
|
+
## Invoke with debugger
|
18
|
+
|
19
|
+
### REPL debug
|
8
20
|
|
9
|
-
```
|
10
|
-
|
21
|
+
```
|
22
|
+
$ ruby -r debug target.rb
|
11
23
|
```
|
12
24
|
|
13
|
-
|
25
|
+
and you can see the debugger prompt. The program was suspended at the beggining of target.rb. To continue the program, type `c` (or `continue`). See other debug commands below.
|
14
26
|
|
15
|
-
|
27
|
+
You can re-enable debug command mode by `Ctrl-C`.
|
16
28
|
|
17
|
-
|
29
|
+
### Remote debug (1) UNIX domain socket
|
18
30
|
|
19
|
-
|
31
|
+
```
|
32
|
+
$ ruby -r debug/unixserver target.rb
|
33
|
+
```
|
20
34
|
|
21
|
-
|
35
|
+
It runs target.rb and accept debugger connection within UNIX domain socket.
|
22
36
|
|
23
|
-
|
24
|
-
program:
|
37
|
+
You can attach the program with the folliowing command:
|
25
38
|
|
26
|
-
```
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
end
|
39
|
+
```
|
40
|
+
$ ruby -r debug/client -e connect
|
41
|
+
Debugger can attach via UNIX domain socket (/home/ko1/.ruby-debug-sock/ruby-debug-ko1-20642)
|
42
|
+
...
|
31
43
|
```
|
32
44
|
|
33
|
-
|
34
|
-
method is run.
|
45
|
+
The debugee process will be suspended and wait for the debug command.
|
35
46
|
|
36
|
-
|
47
|
+
If you are running multiple debuggee processes, this command shows the selection like that:
|
37
48
|
|
38
49
|
```
|
39
|
-
|
40
|
-
|
50
|
+
$ ruby -r debug/client -e connect
|
51
|
+
Please select a debug session:
|
52
|
+
ruby-debug-ko1-19638
|
53
|
+
ruby-debug-ko1-19603
|
41
54
|
```
|
42
55
|
|
43
|
-
|
56
|
+
and you need to specify one:
|
44
57
|
|
45
|
-
|
58
|
+
```
|
59
|
+
$ ruby -r debug/client -e connect ruby-debug-ko1-19638
|
60
|
+
```
|
61
|
+
|
62
|
+
The socket file is located at
|
63
|
+
* `RUBY_DEBUG_SOCK_DIR` environment variable if available.
|
64
|
+
* `XDG_RUNTIME_DIR` environment variable if available.
|
65
|
+
* `$HOME/ruby-debug-sock` if `$HOME` is available.
|
46
66
|
|
47
|
-
|
67
|
+
### Remote debug (2) TCP/IP
|
48
68
|
|
49
|
-
|
69
|
+
```
|
70
|
+
$ RUBY_DEBUG_PORT=12345 RUBY_DEBUG_HOST=localhost ruby -r debug/tcpserver target.rb
|
71
|
+
Debugger can attach via TCP/IP (localhost:12345)
|
72
|
+
...
|
73
|
+
```
|
74
|
+
|
75
|
+
This command invoke target.rb with TCP/IP attach server with given port and host. If host is not given, `localhost` will be used.
|
76
|
+
|
77
|
+
```
|
78
|
+
$ ruby -r debug/client -e connect localhost 12345
|
79
|
+
```
|
50
80
|
|
51
|
-
|
81
|
+
tries to connect with given host (`localhost`) and port (`12345`). You can eliminate host part and `localhost` will be used.
|
82
|
+
|
83
|
+
|
84
|
+
## Debug command
|
85
|
+
|
86
|
+
* `Enter` repeats the last command (useful when repeating `step`s).
|
87
|
+
* `Ctrl-D` is equal to `quit` command.
|
88
|
+
|
89
|
+
### Control flow
|
90
|
+
|
91
|
+
* `s[tep]`
|
92
|
+
* Step in. Resume the program until next breakable point.
|
93
|
+
* `n[ext]`
|
94
|
+
* Step over. Resume the program until next line.
|
95
|
+
* `fin[ish]`
|
96
|
+
* Finish this frame. Resume the program until the current frame is finished.
|
97
|
+
* `c[ontinue]`
|
98
|
+
* Resume the program.
|
99
|
+
* `q[uit]` or `Ctrl-D`
|
100
|
+
* Finish debugger (with a process, if not remote debugging).
|
101
|
+
* `kill`
|
102
|
+
* Stop the debuggee program.
|
103
|
+
|
104
|
+
### Breakpoint
|
105
|
+
|
106
|
+
* `b[reak]`
|
107
|
+
* Show all breakpoints.
|
108
|
+
* `b[reak] <line>`
|
109
|
+
* Set breakpoint on `<line>` at the current frame's file.
|
110
|
+
* `b[reak] <file>:<line>`
|
111
|
+
* Set breakpoint on `<file>:<line>`.
|
112
|
+
* `catch <Error>`
|
113
|
+
* Set breakpoint on raising `<Error>`.
|
114
|
+
* `del[ete]`
|
115
|
+
* delete all breakpoints.
|
116
|
+
* `del[ete] <bpnum>`
|
117
|
+
* delete specified breakpoint.
|
118
|
+
|
119
|
+
### Frame control
|
120
|
+
|
121
|
+
* `bt` or `backtrace`
|
122
|
+
* Show backtrace information.
|
123
|
+
* `f[rame]`
|
124
|
+
* Show current frame.
|
125
|
+
* `f[rame] <framenum>`
|
126
|
+
* Specify frame. Evaluation are run on this frame environement.
|
127
|
+
* `up`
|
128
|
+
* Specify upper frame.
|
129
|
+
* `down`
|
130
|
+
* Specify down frame.
|
131
|
+
|
132
|
+
### Evaluate
|
133
|
+
|
134
|
+
* `p <expr>`
|
135
|
+
* Evaluate like `p <expr>` on the current frame.
|
136
|
+
* `pp <expr>`
|
137
|
+
* Evaluate like `pp <expr>` on the current frame.
|
138
|
+
* `e[val] <expr>`
|
139
|
+
* Evaluate `<expr>` on the current frame.
|
140
|
+
|
141
|
+
### Information
|
142
|
+
|
143
|
+
* `list`
|
144
|
+
* Show current frame's source code.
|
145
|
+
* `info l[ocal[s]]`
|
146
|
+
* Show current frame's local variables. It includes `self` as `%self` and a return value as `%return`.
|
147
|
+
* `info i[nstance]` or `info ivars`
|
148
|
+
* Show current frame's insntance variables.
|
149
|
+
* `display`
|
150
|
+
* Show display setting.
|
151
|
+
* `display <expr>`
|
152
|
+
* Add `<expr>` at suspended timing.
|
153
|
+
* `undisplay`
|
154
|
+
* Remove all display settings.
|
155
|
+
* `undisplay <displaynum>`
|
156
|
+
* Remove a specified display setting.
|
157
|
+
* `trace [on|off]`
|
158
|
+
* enable or disable line tracer.
|
159
|
+
|
160
|
+
### Thread control
|
161
|
+
|
162
|
+
* `th[read] [l[ist]]`
|
163
|
+
* Show all threads.
|
164
|
+
* `th[read] <thnum>`
|
165
|
+
* Switch thread specified by `<thnum>`
|
52
166
|
|
data/Rakefile
CHANGED
data/debug.gemspec
CHANGED
@@ -1,22 +1,32 @@
|
|
1
|
+
require_relative 'lib/debug/version'
|
2
|
+
|
1
3
|
Gem::Specification.new do |spec|
|
2
4
|
spec.name = "debug"
|
3
|
-
spec.version =
|
4
|
-
spec.authors = ["
|
5
|
-
spec.email = ["
|
5
|
+
spec.version = DEBUGGER__::VERSION
|
6
|
+
spec.authors = ["Koichi Sasada"]
|
7
|
+
spec.email = ["ko1@atdot.net"]
|
8
|
+
|
9
|
+
spec.summary = %q{debug.rb}
|
10
|
+
spec.description = %q{debug.rb}
|
11
|
+
spec.homepage = "https://github.com/ko1/debug"
|
12
|
+
spec.license = "MIT"
|
13
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 2.6.0")
|
6
14
|
|
7
|
-
spec.
|
8
|
-
spec.description = %q{Debugging functionality for Ruby}
|
9
|
-
spec.homepage = "https://github.com/ruby/debug"
|
10
|
-
spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
|
11
|
-
spec.licenses = ["Ruby", "BSD-2-Clause"]
|
15
|
+
# spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
|
12
16
|
|
13
17
|
spec.metadata["homepage_uri"] = spec.homepage
|
14
|
-
spec.metadata["source_code_uri"] =
|
18
|
+
spec.metadata["source_code_uri"] = "https://github.com/ko1/debug"
|
19
|
+
# spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
|
15
20
|
|
21
|
+
# Specify which files should be added to the gem when it is released.
|
22
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
16
23
|
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
17
24
|
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
25
|
end
|
19
26
|
spec.bindir = "exe"
|
20
27
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
21
28
|
spec.require_paths = ["lib"]
|
29
|
+
|
30
|
+
spec.add_runtime_dependency 'debug_inspector'
|
31
|
+
spec.add_runtime_dependency 'iseq_collector'
|
22
32
|
end
|
data/exe/rdbg
ADDED
data/lib/debug.rb
CHANGED
@@ -1,1111 +1,15 @@
|
|
1
|
-
|
2
|
-
# Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
|
3
|
-
# Copyright (C) 2000 Information-technology Promotion Agency, Japan
|
4
|
-
# Copyright (C) 2000-2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>
|
1
|
+
require_relative 'debug/repl'
|
5
2
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
end
|
12
|
-
|
13
|
-
require 'tracer'
|
14
|
-
require 'pp'
|
15
|
-
|
16
|
-
class Tracer # :nodoc:
|
17
|
-
def Tracer.trace_func(*vars)
|
18
|
-
Single.trace_func(*vars)
|
19
|
-
end
|
3
|
+
# default break points
|
4
|
+
DEBUGGER__.add_catch_breakpoint 'RuntimeError'
|
5
|
+
class Binding
|
6
|
+
DEBUGGER__.add_line_breakpoint __FILE__, __LINE__ + 1
|
7
|
+
def bp; nil; end
|
20
8
|
end
|
21
9
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
# To add a debugger to your code, start by requiring +debug+ in your
|
28
|
-
# program:
|
29
|
-
#
|
30
|
-
# def say(word)
|
31
|
-
# require 'debug'
|
32
|
-
# puts word
|
33
|
-
# end
|
34
|
-
#
|
35
|
-
# This will cause Ruby to interrupt execution and show a prompt when the +say+
|
36
|
-
# method is run.
|
37
|
-
#
|
38
|
-
# Once you're inside the prompt, you can start debugging your program.
|
39
|
-
#
|
40
|
-
# (rdb:1) p word
|
41
|
-
# "hello"
|
42
|
-
#
|
43
|
-
# == Getting help
|
44
|
-
#
|
45
|
-
# You can get help at any time by pressing +h+.
|
46
|
-
#
|
47
|
-
# (rdb:1) h
|
48
|
-
# Debugger help v.-0.002b
|
49
|
-
# Commands
|
50
|
-
# b[reak] [file:|class:]<line|method>
|
51
|
-
# b[reak] [class.]<line|method>
|
52
|
-
# set breakpoint to some position
|
53
|
-
# wat[ch] <expression> set watchpoint to some expression
|
54
|
-
# cat[ch] (<exception>|off) set catchpoint to an exception
|
55
|
-
# b[reak] list breakpoints
|
56
|
-
# cat[ch] show catchpoint
|
57
|
-
# del[ete][ nnn] delete some or all breakpoints
|
58
|
-
# disp[lay] <expression> add expression into display expression list
|
59
|
-
# undisp[lay][ nnn] delete one particular or all display expressions
|
60
|
-
# c[ont] run until program ends or hit breakpoint
|
61
|
-
# s[tep][ nnn] step (into methods) one line or till line nnn
|
62
|
-
# n[ext][ nnn] go over one line or till line nnn
|
63
|
-
# w[here] display frames
|
64
|
-
# f[rame] alias for where
|
65
|
-
# l[ist][ (-|nn-mm)] list program, - lists backwards
|
66
|
-
# nn-mm lists given lines
|
67
|
-
# up[ nn] move to higher frame
|
68
|
-
# down[ nn] move to lower frame
|
69
|
-
# fin[ish] return to outer frame
|
70
|
-
# tr[ace] (on|off) set trace mode of current thread
|
71
|
-
# tr[ace] (on|off) all set trace mode of all threads
|
72
|
-
# q[uit] exit from debugger
|
73
|
-
# v[ar] g[lobal] show global variables
|
74
|
-
# v[ar] l[ocal] show local variables
|
75
|
-
# v[ar] i[nstance] <object> show instance variables of object
|
76
|
-
# v[ar] c[onst] <object> show constants of object
|
77
|
-
# m[ethod] i[nstance] <obj> show methods of object
|
78
|
-
# m[ethod] <class|module> show instance methods of class or module
|
79
|
-
# th[read] l[ist] list all threads
|
80
|
-
# th[read] c[ur[rent]] show current thread
|
81
|
-
# th[read] [sw[itch]] <nnn> switch thread context to nnn
|
82
|
-
# th[read] stop <nnn> stop thread nnn
|
83
|
-
# th[read] resume <nnn> resume thread nnn
|
84
|
-
# p expression evaluate expression and print its value
|
85
|
-
# h[elp] print this help
|
86
|
-
# <everything else> evaluate
|
87
|
-
#
|
88
|
-
# == Usage
|
89
|
-
#
|
90
|
-
# The following is a list of common functionalities that the debugger
|
91
|
-
# provides.
|
92
|
-
#
|
93
|
-
# === Navigating through your code
|
94
|
-
#
|
95
|
-
# In general, a debugger is used to find bugs in your program, which
|
96
|
-
# often means pausing execution and inspecting variables at some point
|
97
|
-
# in time.
|
98
|
-
#
|
99
|
-
# Let's look at an example:
|
100
|
-
#
|
101
|
-
# def my_method(foo)
|
102
|
-
# require 'debug'
|
103
|
-
# foo = get_foo if foo.nil?
|
104
|
-
# raise if foo.nil?
|
105
|
-
# end
|
106
|
-
#
|
107
|
-
# When you run this program, the debugger will kick in just before the
|
108
|
-
# +foo+ assignment.
|
109
|
-
#
|
110
|
-
# (rdb:1) p foo
|
111
|
-
# nil
|
112
|
-
#
|
113
|
-
# In this example, it'd be interesting to move to the next line and
|
114
|
-
# inspect the value of +foo+ again. You can do that by pressing +n+:
|
115
|
-
#
|
116
|
-
# (rdb:1) n # goes to next line
|
117
|
-
# (rdb:1) p foo
|
118
|
-
# nil
|
119
|
-
#
|
120
|
-
# You now know that the original value of +foo+ was nil, and that it
|
121
|
-
# still was nil after calling +get_foo+.
|
122
|
-
#
|
123
|
-
# Other useful commands for navigating through your code are:
|
124
|
-
#
|
125
|
-
# +c+::
|
126
|
-
# Runs the program until it either exists or encounters another breakpoint.
|
127
|
-
# You usually press +c+ when you are finished debugging your program and
|
128
|
-
# want to resume its execution.
|
129
|
-
# +s+::
|
130
|
-
# Steps into method definition. In the previous example, +s+ would take you
|
131
|
-
# inside the method definition of +get_foo+.
|
132
|
-
# +r+::
|
133
|
-
# Restart the program.
|
134
|
-
# +q+::
|
135
|
-
# Quit the program.
|
136
|
-
#
|
137
|
-
# === Inspecting variables
|
138
|
-
#
|
139
|
-
# You can use the debugger to easily inspect both local and global variables.
|
140
|
-
# We've seen how to inspect local variables before:
|
141
|
-
#
|
142
|
-
# (rdb:1) p my_arg
|
143
|
-
# 42
|
144
|
-
#
|
145
|
-
# You can also pretty print the result of variables or expressions:
|
146
|
-
#
|
147
|
-
# (rdb:1) pp %w{a very long long array containing many words}
|
148
|
-
# ["a",
|
149
|
-
# "very",
|
150
|
-
# "long",
|
151
|
-
# ...
|
152
|
-
# ]
|
153
|
-
#
|
154
|
-
# You can list all local variables with +v l+:
|
155
|
-
#
|
156
|
-
# (rdb:1) v l
|
157
|
-
# foo => "hello"
|
158
|
-
#
|
159
|
-
# Similarly, you can show all global variables with +v g+:
|
160
|
-
#
|
161
|
-
# (rdb:1) v g
|
162
|
-
# all global variables
|
163
|
-
#
|
164
|
-
# Finally, you can omit +p+ if you simply want to evaluate a variable or
|
165
|
-
# expression
|
166
|
-
#
|
167
|
-
# (rdb:1) 5**2
|
168
|
-
# 25
|
169
|
-
#
|
170
|
-
# === Going beyond basics
|
171
|
-
#
|
172
|
-
# Ruby Debug provides more advanced functionalities like switching
|
173
|
-
# between threads, setting breakpoints and watch expressions, and more.
|
174
|
-
# The full list of commands is available at any time by pressing +h+.
|
175
|
-
#
|
176
|
-
# == Staying out of trouble
|
177
|
-
#
|
178
|
-
# Make sure you remove every instance of +require 'debug'+ before
|
179
|
-
# shipping your code. Failing to do so may result in your program
|
180
|
-
# hanging unpredictably.
|
181
|
-
#
|
182
|
-
# Debug is not available in safe mode.
|
183
|
-
|
184
|
-
class DEBUGGER__
|
185
|
-
MUTEX = Thread::Mutex.new # :nodoc:
|
186
|
-
|
187
|
-
class Context # :nodoc:
|
188
|
-
DEBUG_LAST_CMD = []
|
189
|
-
|
190
|
-
begin
|
191
|
-
require 'readline'
|
192
|
-
def readline(prompt, hist)
|
193
|
-
Readline::readline(prompt, hist)
|
194
|
-
end
|
195
|
-
rescue LoadError
|
196
|
-
def readline(prompt, hist)
|
197
|
-
STDOUT.print prompt
|
198
|
-
STDOUT.flush
|
199
|
-
line = STDIN.gets
|
200
|
-
exit unless line
|
201
|
-
line.chomp!
|
202
|
-
line
|
203
|
-
end
|
204
|
-
USE_READLINE = false
|
205
|
-
end
|
206
|
-
|
207
|
-
def initialize
|
208
|
-
if Thread.current == Thread.main
|
209
|
-
@stop_next = 1
|
210
|
-
else
|
211
|
-
@stop_next = 0
|
212
|
-
end
|
213
|
-
@last_file = nil
|
214
|
-
@file = nil
|
215
|
-
@line = nil
|
216
|
-
@no_step = nil
|
217
|
-
@frames = []
|
218
|
-
@finish_pos = 0
|
219
|
-
@trace = false
|
220
|
-
@catch = "StandardError"
|
221
|
-
@suspend_next = false
|
222
|
-
end
|
223
|
-
|
224
|
-
def stop_next(n=1)
|
225
|
-
@stop_next = n
|
226
|
-
end
|
227
|
-
|
228
|
-
def set_suspend
|
229
|
-
@suspend_next = true
|
230
|
-
end
|
231
|
-
|
232
|
-
def clear_suspend
|
233
|
-
@suspend_next = false
|
234
|
-
end
|
235
|
-
|
236
|
-
def suspend_all
|
237
|
-
DEBUGGER__.suspend
|
238
|
-
end
|
239
|
-
|
240
|
-
def resume_all
|
241
|
-
DEBUGGER__.resume
|
242
|
-
end
|
243
|
-
|
244
|
-
def check_suspend
|
245
|
-
while MUTEX.synchronize {
|
246
|
-
if @suspend_next
|
247
|
-
DEBUGGER__.waiting.push Thread.current
|
248
|
-
@suspend_next = false
|
249
|
-
true
|
250
|
-
end
|
251
|
-
}
|
252
|
-
end
|
253
|
-
end
|
254
|
-
|
255
|
-
def trace?
|
256
|
-
@trace
|
257
|
-
end
|
258
|
-
|
259
|
-
def set_trace(arg)
|
260
|
-
@trace = arg
|
261
|
-
end
|
262
|
-
|
263
|
-
def stdout
|
264
|
-
DEBUGGER__.stdout
|
265
|
-
end
|
266
|
-
|
267
|
-
def break_points
|
268
|
-
DEBUGGER__.break_points
|
269
|
-
end
|
270
|
-
|
271
|
-
def display
|
272
|
-
DEBUGGER__.display
|
273
|
-
end
|
274
|
-
|
275
|
-
def context(th)
|
276
|
-
DEBUGGER__.context(th)
|
277
|
-
end
|
278
|
-
|
279
|
-
def set_trace_all(arg)
|
280
|
-
DEBUGGER__.set_trace(arg)
|
281
|
-
end
|
282
|
-
|
283
|
-
def set_last_thread(th)
|
284
|
-
DEBUGGER__.set_last_thread(th)
|
285
|
-
end
|
286
|
-
|
287
|
-
def debug_eval(str, binding)
|
288
|
-
begin
|
289
|
-
eval(str, binding)
|
290
|
-
rescue StandardError, ScriptError => e
|
291
|
-
at = eval("caller(1)", binding)
|
292
|
-
stdout.printf "%s:%s\n", at.shift, e.to_s.sub(/\(eval\):1:(in `.*?':)?/, '')
|
293
|
-
for i in at
|
294
|
-
stdout.printf "\tfrom %s\n", i
|
295
|
-
end
|
296
|
-
throw :debug_error
|
297
|
-
end
|
298
|
-
end
|
299
|
-
|
300
|
-
def debug_silent_eval(str, binding)
|
301
|
-
begin
|
302
|
-
eval(str, binding)
|
303
|
-
rescue StandardError, ScriptError
|
304
|
-
nil
|
305
|
-
end
|
306
|
-
end
|
307
|
-
|
308
|
-
def var_list(ary, binding)
|
309
|
-
ary.sort!
|
310
|
-
for v in ary
|
311
|
-
stdout.printf " %s => %s\n", v, eval(v.to_s, binding).inspect
|
312
|
-
end
|
313
|
-
end
|
314
|
-
|
315
|
-
def debug_variable_info(input, binding)
|
316
|
-
case input
|
317
|
-
when /^\s*g(?:lobal)?\s*$/
|
318
|
-
var_list(global_variables, binding)
|
319
|
-
|
320
|
-
when /^\s*l(?:ocal)?\s*$/
|
321
|
-
var_list(eval("local_variables", binding), binding)
|
322
|
-
|
323
|
-
when /^\s*i(?:nstance)?\s+/
|
324
|
-
obj = debug_eval($', binding)
|
325
|
-
var_list(obj.instance_variables, obj.instance_eval{binding()})
|
326
|
-
|
327
|
-
when /^\s*c(?:onst(?:ant)?)?\s+/
|
328
|
-
obj = debug_eval($', binding)
|
329
|
-
unless obj.kind_of? Module
|
330
|
-
stdout.print "Should be Class/Module: ", $', "\n"
|
331
|
-
else
|
332
|
-
var_list(obj.constants, obj.module_eval{binding()})
|
333
|
-
end
|
334
|
-
end
|
335
|
-
end
|
336
|
-
|
337
|
-
def debug_method_info(input, binding)
|
338
|
-
case input
|
339
|
-
when /^i(:?nstance)?\s+/
|
340
|
-
obj = debug_eval($', binding)
|
341
|
-
|
342
|
-
len = 0
|
343
|
-
for v in obj.methods.sort
|
344
|
-
len += v.size + 1
|
345
|
-
if len > 70
|
346
|
-
len = v.size + 1
|
347
|
-
stdout.print "\n"
|
348
|
-
end
|
349
|
-
stdout.print v, " "
|
350
|
-
end
|
351
|
-
stdout.print "\n"
|
352
|
-
|
353
|
-
else
|
354
|
-
obj = debug_eval(input, binding)
|
355
|
-
unless obj.kind_of? Module
|
356
|
-
stdout.print "Should be Class/Module: ", input, "\n"
|
357
|
-
else
|
358
|
-
len = 0
|
359
|
-
for v in obj.instance_methods(false).sort
|
360
|
-
len += v.size + 1
|
361
|
-
if len > 70
|
362
|
-
len = v.size + 1
|
363
|
-
stdout.print "\n"
|
364
|
-
end
|
365
|
-
stdout.print v, " "
|
366
|
-
end
|
367
|
-
stdout.print "\n"
|
368
|
-
end
|
369
|
-
end
|
370
|
-
end
|
371
|
-
|
372
|
-
def thnum
|
373
|
-
num = DEBUGGER__.instance_eval{@thread_list[Thread.current]}
|
374
|
-
unless num
|
375
|
-
DEBUGGER__.make_thread_list
|
376
|
-
num = DEBUGGER__.instance_eval{@thread_list[Thread.current]}
|
377
|
-
end
|
378
|
-
num
|
379
|
-
end
|
380
|
-
|
381
|
-
def debug_command(file, line, id, binding)
|
382
|
-
MUTEX.lock
|
383
|
-
unless defined?($debugger_restart) and $debugger_restart
|
384
|
-
callcc{|c| $debugger_restart = c}
|
385
|
-
end
|
386
|
-
set_last_thread(Thread.current)
|
387
|
-
frame_pos = 0
|
388
|
-
binding_file = file
|
389
|
-
binding_line = line
|
390
|
-
previous_line = nil
|
391
|
-
if ENV['EMACS']
|
392
|
-
stdout.printf "\032\032%s:%d:\n", binding_file, binding_line
|
393
|
-
else
|
394
|
-
stdout.printf "%s:%d:%s", binding_file, binding_line,
|
395
|
-
line_at(binding_file, binding_line)
|
396
|
-
end
|
397
|
-
@frames[0] = [binding, file, line, id]
|
398
|
-
display_expressions(binding)
|
399
|
-
prompt = true
|
400
|
-
while prompt and input = readline("(rdb:%d) "%thnum(), true)
|
401
|
-
catch(:debug_error) do
|
402
|
-
if input == ""
|
403
|
-
next unless DEBUG_LAST_CMD[0]
|
404
|
-
input = DEBUG_LAST_CMD[0]
|
405
|
-
stdout.print input, "\n"
|
406
|
-
else
|
407
|
-
DEBUG_LAST_CMD[0] = input
|
408
|
-
end
|
409
|
-
|
410
|
-
case input
|
411
|
-
when /^\s*tr(?:ace)?(?:\s+(on|off))?(?:\s+(all))?$/
|
412
|
-
if defined?( $2 )
|
413
|
-
if $1 == 'on'
|
414
|
-
set_trace_all true
|
415
|
-
else
|
416
|
-
set_trace_all false
|
417
|
-
end
|
418
|
-
elsif defined?( $1 )
|
419
|
-
if $1 == 'on'
|
420
|
-
set_trace true
|
421
|
-
else
|
422
|
-
set_trace false
|
423
|
-
end
|
424
|
-
end
|
425
|
-
if trace?
|
426
|
-
stdout.print "Trace on.\n"
|
427
|
-
else
|
428
|
-
stdout.print "Trace off.\n"
|
429
|
-
end
|
430
|
-
|
431
|
-
when /^\s*b(?:reak)?\s+(?:(.+):)?([^.:]+)$/
|
432
|
-
pos = $2
|
433
|
-
if $1
|
434
|
-
klass = debug_silent_eval($1, binding)
|
435
|
-
file = $1
|
436
|
-
end
|
437
|
-
if pos =~ /^\d+$/
|
438
|
-
pname = pos
|
439
|
-
pos = pos.to_i
|
440
|
-
else
|
441
|
-
pname = pos = pos.intern.id2name
|
442
|
-
end
|
443
|
-
break_points.push [true, 0, klass || file, pos]
|
444
|
-
stdout.printf "Set breakpoint %d at %s:%s\n", break_points.size, klass || file, pname
|
445
|
-
|
446
|
-
when /^\s*b(?:reak)?\s+(.+)[#.]([^.:]+)$/
|
447
|
-
pos = $2.intern.id2name
|
448
|
-
klass = debug_eval($1, binding)
|
449
|
-
break_points.push [true, 0, klass, pos]
|
450
|
-
stdout.printf "Set breakpoint %d at %s.%s\n", break_points.size, klass, pos
|
451
|
-
|
452
|
-
when /^\s*wat(?:ch)?\s+(.+)$/
|
453
|
-
exp = $1
|
454
|
-
break_points.push [true, 1, exp]
|
455
|
-
stdout.printf "Set watchpoint %d:%s\n", break_points.size, exp
|
456
|
-
|
457
|
-
when /^\s*b(?:reak)?$/
|
458
|
-
if break_points.find{|b| b[1] == 0}
|
459
|
-
n = 1
|
460
|
-
stdout.print "Breakpoints:\n"
|
461
|
-
break_points.each do |b|
|
462
|
-
if b[0] and b[1] == 0
|
463
|
-
stdout.printf " %d %s:%s\n", n, b[2], b[3]
|
464
|
-
end
|
465
|
-
n += 1
|
466
|
-
end
|
467
|
-
end
|
468
|
-
if break_points.find{|b| b[1] == 1}
|
469
|
-
n = 1
|
470
|
-
stdout.print "\n"
|
471
|
-
stdout.print "Watchpoints:\n"
|
472
|
-
for b in break_points
|
473
|
-
if b[0] and b[1] == 1
|
474
|
-
stdout.printf " %d %s\n", n, b[2]
|
475
|
-
end
|
476
|
-
n += 1
|
477
|
-
end
|
478
|
-
end
|
479
|
-
if break_points.size == 0
|
480
|
-
stdout.print "No breakpoints\n"
|
481
|
-
else
|
482
|
-
stdout.print "\n"
|
483
|
-
end
|
484
|
-
|
485
|
-
when /^\s*del(?:ete)?(?:\s+(\d+))?$/
|
486
|
-
pos = $1
|
487
|
-
unless pos
|
488
|
-
input = readline("Clear all breakpoints? (y/n) ", false)
|
489
|
-
if input == "y"
|
490
|
-
for b in break_points
|
491
|
-
b[0] = false
|
492
|
-
end
|
493
|
-
end
|
494
|
-
else
|
495
|
-
pos = pos.to_i
|
496
|
-
if break_points[pos-1]
|
497
|
-
break_points[pos-1][0] = false
|
498
|
-
else
|
499
|
-
stdout.printf "Breakpoint %d is not defined\n", pos
|
500
|
-
end
|
501
|
-
end
|
502
|
-
|
503
|
-
when /^\s*disp(?:lay)?\s+(.+)$/
|
504
|
-
exp = $1
|
505
|
-
display.push [true, exp]
|
506
|
-
stdout.printf "%d: ", display.size
|
507
|
-
display_expression(exp, binding)
|
508
|
-
|
509
|
-
when /^\s*disp(?:lay)?$/
|
510
|
-
display_expressions(binding)
|
511
|
-
|
512
|
-
when /^\s*undisp(?:lay)?(?:\s+(\d+))?$/
|
513
|
-
pos = $1
|
514
|
-
unless pos
|
515
|
-
input = readline("Clear all expressions? (y/n) ", false)
|
516
|
-
if input == "y"
|
517
|
-
for d in display
|
518
|
-
d[0] = false
|
519
|
-
end
|
520
|
-
end
|
521
|
-
else
|
522
|
-
pos = pos.to_i
|
523
|
-
if display[pos-1]
|
524
|
-
display[pos-1][0] = false
|
525
|
-
else
|
526
|
-
stdout.printf "Display expression %d is not defined\n", pos
|
527
|
-
end
|
528
|
-
end
|
529
|
-
|
530
|
-
when /^\s*c(?:ont)?$/
|
531
|
-
prompt = false
|
532
|
-
|
533
|
-
when /^\s*s(?:tep)?(?:\s+(\d+))?$/
|
534
|
-
if $1
|
535
|
-
lev = $1.to_i
|
536
|
-
else
|
537
|
-
lev = 1
|
538
|
-
end
|
539
|
-
@stop_next = lev
|
540
|
-
prompt = false
|
541
|
-
|
542
|
-
when /^\s*n(?:ext)?(?:\s+(\d+))?$/
|
543
|
-
if $1
|
544
|
-
lev = $1.to_i
|
545
|
-
else
|
546
|
-
lev = 1
|
547
|
-
end
|
548
|
-
@stop_next = lev
|
549
|
-
@no_step = @frames.size - frame_pos
|
550
|
-
prompt = false
|
551
|
-
|
552
|
-
when /^\s*w(?:here)?$/, /^\s*f(?:rame)?$/
|
553
|
-
display_frames(frame_pos)
|
554
|
-
|
555
|
-
when /^\s*l(?:ist)?(?:\s+(.+))?$/
|
556
|
-
if not $1
|
557
|
-
b = previous_line ? previous_line + 10 : binding_line - 5
|
558
|
-
e = b + 9
|
559
|
-
elsif $1 == '-'
|
560
|
-
b = previous_line ? previous_line - 10 : binding_line - 5
|
561
|
-
e = b + 9
|
562
|
-
else
|
563
|
-
b, e = $1.split(/[-,]/)
|
564
|
-
if e
|
565
|
-
b = b.to_i
|
566
|
-
e = e.to_i
|
567
|
-
else
|
568
|
-
b = b.to_i - 5
|
569
|
-
e = b + 9
|
570
|
-
end
|
571
|
-
end
|
572
|
-
previous_line = b
|
573
|
-
display_list(b, e, binding_file, binding_line)
|
574
|
-
|
575
|
-
when /^\s*up(?:\s+(\d+))?$/
|
576
|
-
previous_line = nil
|
577
|
-
if $1
|
578
|
-
lev = $1.to_i
|
579
|
-
else
|
580
|
-
lev = 1
|
581
|
-
end
|
582
|
-
frame_pos += lev
|
583
|
-
if frame_pos >= @frames.size
|
584
|
-
frame_pos = @frames.size - 1
|
585
|
-
stdout.print "At toplevel\n"
|
586
|
-
end
|
587
|
-
binding, binding_file, binding_line = @frames[frame_pos]
|
588
|
-
stdout.print format_frame(frame_pos)
|
589
|
-
|
590
|
-
when /^\s*down(?:\s+(\d+))?$/
|
591
|
-
previous_line = nil
|
592
|
-
if $1
|
593
|
-
lev = $1.to_i
|
594
|
-
else
|
595
|
-
lev = 1
|
596
|
-
end
|
597
|
-
frame_pos -= lev
|
598
|
-
if frame_pos < 0
|
599
|
-
frame_pos = 0
|
600
|
-
stdout.print "At stack bottom\n"
|
601
|
-
end
|
602
|
-
binding, binding_file, binding_line = @frames[frame_pos]
|
603
|
-
stdout.print format_frame(frame_pos)
|
604
|
-
|
605
|
-
when /^\s*fin(?:ish)?$/
|
606
|
-
if frame_pos == @frames.size
|
607
|
-
stdout.print "\"finish\" not meaningful in the outermost frame.\n"
|
608
|
-
else
|
609
|
-
@finish_pos = @frames.size - frame_pos
|
610
|
-
frame_pos = 0
|
611
|
-
prompt = false
|
612
|
-
end
|
613
|
-
|
614
|
-
when /^\s*cat(?:ch)?(?:\s+(.+))?$/
|
615
|
-
if $1
|
616
|
-
excn = $1
|
617
|
-
if excn == 'off'
|
618
|
-
@catch = nil
|
619
|
-
stdout.print "Clear catchpoint.\n"
|
620
|
-
else
|
621
|
-
@catch = excn
|
622
|
-
stdout.printf "Set catchpoint %s.\n", @catch
|
623
|
-
end
|
624
|
-
else
|
625
|
-
if @catch
|
626
|
-
stdout.printf "Catchpoint %s.\n", @catch
|
627
|
-
else
|
628
|
-
stdout.print "No catchpoint.\n"
|
629
|
-
end
|
630
|
-
end
|
631
|
-
|
632
|
-
when /^\s*q(?:uit)?$/
|
633
|
-
input = readline("Really quit? (y/n) ", false)
|
634
|
-
if input == "y"
|
635
|
-
exit! # exit -> exit!: No graceful way to stop threads...
|
636
|
-
end
|
637
|
-
|
638
|
-
when /^\s*v(?:ar)?\s+/
|
639
|
-
debug_variable_info($', binding)
|
640
|
-
|
641
|
-
when /^\s*m(?:ethod)?\s+/
|
642
|
-
debug_method_info($', binding)
|
643
|
-
|
644
|
-
when /^\s*th(?:read)?\s+/
|
645
|
-
if DEBUGGER__.debug_thread_info($', binding) == :cont
|
646
|
-
prompt = false
|
647
|
-
end
|
648
|
-
|
649
|
-
when /^\s*pp\s+/
|
650
|
-
PP.pp(debug_eval($', binding), stdout)
|
651
|
-
|
652
|
-
when /^\s*p\s+/
|
653
|
-
stdout.printf "%s\n", debug_eval($', binding).inspect
|
654
|
-
|
655
|
-
when /^\s*r(?:estart)?$/
|
656
|
-
$debugger_restart.call
|
657
|
-
|
658
|
-
when /^\s*h(?:elp)?$/
|
659
|
-
debug_print_help()
|
660
|
-
|
661
|
-
else
|
662
|
-
v = debug_eval(input, binding)
|
663
|
-
stdout.printf "%s\n", v.inspect
|
664
|
-
end
|
665
|
-
end
|
666
|
-
end
|
667
|
-
MUTEX.unlock
|
668
|
-
resume_all
|
669
|
-
end
|
670
|
-
|
671
|
-
def debug_print_help
|
672
|
-
stdout.print <<EOHELP
|
673
|
-
Debugger help v.-0.002b
|
674
|
-
Commands
|
675
|
-
b[reak] [file:|class:]<line|method>
|
676
|
-
b[reak] [class.]<line|method>
|
677
|
-
set breakpoint to some position
|
678
|
-
wat[ch] <expression> set watchpoint to some expression
|
679
|
-
cat[ch] (<exception>|off) set catchpoint to an exception
|
680
|
-
b[reak] list breakpoints
|
681
|
-
cat[ch] show catchpoint
|
682
|
-
del[ete][ nnn] delete some or all breakpoints
|
683
|
-
disp[lay] <expression> add expression into display expression list
|
684
|
-
undisp[lay][ nnn] delete one particular or all display expressions
|
685
|
-
c[ont] run until program ends or hit breakpoint
|
686
|
-
s[tep][ nnn] step (into methods) one line or till line nnn
|
687
|
-
n[ext][ nnn] go over one line or till line nnn
|
688
|
-
w[here] display frames
|
689
|
-
f[rame] alias for where
|
690
|
-
l[ist][ (-|nn-mm)] list program, - lists backwards
|
691
|
-
nn-mm lists given lines
|
692
|
-
up[ nn] move to higher frame
|
693
|
-
down[ nn] move to lower frame
|
694
|
-
fin[ish] return to outer frame
|
695
|
-
tr[ace] (on|off) set trace mode of current thread
|
696
|
-
tr[ace] (on|off) all set trace mode of all threads
|
697
|
-
q[uit] exit from debugger
|
698
|
-
v[ar] g[lobal] show global variables
|
699
|
-
v[ar] l[ocal] show local variables
|
700
|
-
v[ar] i[nstance] <object> show instance variables of object
|
701
|
-
v[ar] c[onst] <object> show constants of object
|
702
|
-
m[ethod] i[nstance] <obj> show methods of object
|
703
|
-
m[ethod] <class|module> show instance methods of class or module
|
704
|
-
th[read] l[ist] list all threads
|
705
|
-
th[read] c[ur[rent]] show current thread
|
706
|
-
th[read] [sw[itch]] <nnn> switch thread context to nnn
|
707
|
-
th[read] stop <nnn> stop thread nnn
|
708
|
-
th[read] resume <nnn> resume thread nnn
|
709
|
-
pp expression evaluate expression and pretty_print its value
|
710
|
-
p expression evaluate expression and print its value
|
711
|
-
r[estart] restart program
|
712
|
-
h[elp] print this help
|
713
|
-
<everything else> evaluate
|
714
|
-
EOHELP
|
715
|
-
end
|
716
|
-
|
717
|
-
def display_expressions(binding)
|
718
|
-
n = 1
|
719
|
-
for d in display
|
720
|
-
if d[0]
|
721
|
-
stdout.printf "%d: ", n
|
722
|
-
display_expression(d[1], binding)
|
723
|
-
end
|
724
|
-
n += 1
|
725
|
-
end
|
726
|
-
end
|
727
|
-
|
728
|
-
def display_expression(exp, binding)
|
729
|
-
stdout.printf "%s = %s\n", exp, debug_silent_eval(exp, binding).to_s
|
730
|
-
end
|
731
|
-
|
732
|
-
def frame_set_pos(file, line)
|
733
|
-
if @frames[0]
|
734
|
-
@frames[0][1] = file
|
735
|
-
@frames[0][2] = line
|
736
|
-
end
|
737
|
-
end
|
738
|
-
|
739
|
-
def display_frames(pos)
|
740
|
-
0.upto(@frames.size - 1) do |n|
|
741
|
-
if n == pos
|
742
|
-
stdout.print "--> "
|
743
|
-
else
|
744
|
-
stdout.print " "
|
745
|
-
end
|
746
|
-
stdout.print format_frame(n)
|
747
|
-
end
|
748
|
-
end
|
749
|
-
|
750
|
-
def format_frame(pos)
|
751
|
-
_, file, line, id = @frames[pos]
|
752
|
-
sprintf "#%d %s:%s%s\n", pos + 1, file, line,
|
753
|
-
(id ? ":in `#{id.id2name}'" : "")
|
754
|
-
end
|
755
|
-
|
756
|
-
def script_lines(file, line)
|
757
|
-
unless (lines = SCRIPT_LINES__[file]) and lines != true
|
758
|
-
Tracer::Single.get_line(file, line) if File.exist?(file)
|
759
|
-
lines = SCRIPT_LINES__[file]
|
760
|
-
lines = nil if lines == true
|
761
|
-
end
|
762
|
-
lines
|
763
|
-
end
|
764
|
-
|
765
|
-
def display_list(b, e, file, line)
|
766
|
-
if lines = script_lines(file, line)
|
767
|
-
stdout.printf "[%d, %d] in %s\n", b, e, file
|
768
|
-
b.upto(e) do |n|
|
769
|
-
if n > 0 && lines[n-1]
|
770
|
-
if n == line
|
771
|
-
stdout.printf "=> %d %s\n", n, lines[n-1].chomp
|
772
|
-
else
|
773
|
-
stdout.printf " %d %s\n", n, lines[n-1].chomp
|
774
|
-
end
|
775
|
-
end
|
776
|
-
end
|
777
|
-
else
|
778
|
-
stdout.printf "No sourcefile available for %s\n", file
|
779
|
-
end
|
780
|
-
end
|
781
|
-
|
782
|
-
def line_at(file, line)
|
783
|
-
lines = script_lines(file, line)
|
784
|
-
if lines and line = lines[line-1]
|
785
|
-
return line
|
786
|
-
end
|
787
|
-
return "\n"
|
788
|
-
end
|
789
|
-
|
790
|
-
def debug_funcname(id)
|
791
|
-
if id.nil?
|
792
|
-
"toplevel"
|
793
|
-
else
|
794
|
-
id.id2name
|
795
|
-
end
|
796
|
-
end
|
797
|
-
|
798
|
-
def check_break_points(file, klass, pos, binding, id)
|
799
|
-
return false if break_points.empty?
|
800
|
-
n = 1
|
801
|
-
for b in break_points
|
802
|
-
if b[0] # valid
|
803
|
-
if b[1] == 0 # breakpoint
|
804
|
-
if (b[2] == file and b[3] == pos) or
|
805
|
-
(klass and b[2] == klass and b[3] == pos)
|
806
|
-
stdout.printf "Breakpoint %d, %s at %s:%s\n", n, debug_funcname(id), file, pos
|
807
|
-
return true
|
808
|
-
end
|
809
|
-
elsif b[1] == 1 # watchpoint
|
810
|
-
if debug_silent_eval(b[2], binding)
|
811
|
-
stdout.printf "Watchpoint %d, %s at %s:%s\n", n, debug_funcname(id), file, pos
|
812
|
-
return true
|
813
|
-
end
|
814
|
-
end
|
815
|
-
end
|
816
|
-
n += 1
|
817
|
-
end
|
818
|
-
return false
|
819
|
-
end
|
820
|
-
|
821
|
-
def excn_handle(file, line, id, binding)
|
822
|
-
if $!.class <= SystemExit
|
823
|
-
set_trace_func nil
|
824
|
-
exit
|
825
|
-
end
|
826
|
-
|
827
|
-
if @catch and ($!.class.ancestors.find { |e| e.to_s == @catch })
|
828
|
-
stdout.printf "%s:%d: `%s' (%s)\n", file, line, $!, $!.class
|
829
|
-
fs = @frames.size
|
830
|
-
tb = caller(0)[-fs..-1]
|
831
|
-
if tb
|
832
|
-
for i in tb
|
833
|
-
stdout.printf "\tfrom %s\n", i
|
834
|
-
end
|
835
|
-
end
|
836
|
-
suspend_all
|
837
|
-
debug_command(file, line, id, binding)
|
838
|
-
end
|
839
|
-
end
|
840
|
-
|
841
|
-
def trace_func(event, file, line, id, binding, klass)
|
842
|
-
Tracer.trace_func(event, file, line, id, binding, klass) if trace?
|
843
|
-
context(Thread.current).check_suspend
|
844
|
-
@file = file
|
845
|
-
@line = line
|
846
|
-
case event
|
847
|
-
when 'line'
|
848
|
-
frame_set_pos(file, line)
|
849
|
-
if !@no_step or @frames.size == @no_step
|
850
|
-
@stop_next -= 1
|
851
|
-
@stop_next = -1 if @stop_next < 0
|
852
|
-
elsif @frames.size < @no_step
|
853
|
-
@stop_next = 0 # break here before leaving...
|
854
|
-
else
|
855
|
-
# nothing to do. skipped.
|
856
|
-
end
|
857
|
-
if @stop_next == 0 or check_break_points(file, nil, line, binding, id)
|
858
|
-
@no_step = nil
|
859
|
-
suspend_all
|
860
|
-
debug_command(file, line, id, binding)
|
861
|
-
end
|
862
|
-
|
863
|
-
when 'call'
|
864
|
-
@frames.unshift [binding, file, line, id]
|
865
|
-
if check_break_points(file, klass, id.id2name, binding, id)
|
866
|
-
suspend_all
|
867
|
-
debug_command(file, line, id, binding)
|
868
|
-
end
|
869
|
-
|
870
|
-
when 'c-call'
|
871
|
-
frame_set_pos(file, line)
|
872
|
-
|
873
|
-
when 'class'
|
874
|
-
@frames.unshift [binding, file, line, id]
|
875
|
-
|
876
|
-
when 'return', 'end'
|
877
|
-
if @frames.size == @finish_pos
|
878
|
-
@stop_next = 1
|
879
|
-
@finish_pos = 0
|
880
|
-
end
|
881
|
-
@frames.shift
|
882
|
-
|
883
|
-
when 'raise'
|
884
|
-
excn_handle(file, line, id, binding)
|
885
|
-
|
886
|
-
end
|
887
|
-
@last_file = file
|
888
|
-
end
|
889
|
-
end
|
890
|
-
|
891
|
-
trap("INT") { DEBUGGER__.interrupt }
|
892
|
-
@last_thread = Thread::main
|
893
|
-
@max_thread = 1
|
894
|
-
@thread_list = {Thread::main => 1}
|
895
|
-
@break_points = []
|
896
|
-
@display = []
|
897
|
-
@waiting = []
|
898
|
-
@stdout = STDOUT
|
899
|
-
|
900
|
-
class << DEBUGGER__
|
901
|
-
# Returns the IO used as stdout. Defaults to STDOUT
|
902
|
-
def stdout
|
903
|
-
@stdout
|
904
|
-
end
|
905
|
-
|
906
|
-
# Sets the IO used as stdout. Defaults to STDOUT
|
907
|
-
def stdout=(s)
|
908
|
-
@stdout = s
|
909
|
-
end
|
910
|
-
|
911
|
-
# Returns the display expression list
|
912
|
-
#
|
913
|
-
# See DEBUGGER__ for more usage
|
914
|
-
def display
|
915
|
-
@display
|
916
|
-
end
|
917
|
-
|
918
|
-
# Returns the list of break points where execution will be stopped.
|
919
|
-
#
|
920
|
-
# See DEBUGGER__ for more usage
|
921
|
-
def break_points
|
922
|
-
@break_points
|
923
|
-
end
|
924
|
-
|
925
|
-
# Returns the list of waiting threads.
|
926
|
-
#
|
927
|
-
# When stepping through the traces of a function, thread gets suspended, to
|
928
|
-
# be resumed later.
|
929
|
-
def waiting
|
930
|
-
@waiting
|
931
|
-
end
|
932
|
-
|
933
|
-
def set_trace( arg )
|
934
|
-
MUTEX.synchronize do
|
935
|
-
make_thread_list
|
936
|
-
for th, in @thread_list
|
937
|
-
context(th).set_trace arg
|
938
|
-
end
|
939
|
-
end
|
940
|
-
arg
|
941
|
-
end
|
942
|
-
|
943
|
-
def set_last_thread(th)
|
944
|
-
@last_thread = th
|
945
|
-
end
|
946
|
-
|
947
|
-
def suspend
|
948
|
-
MUTEX.synchronize do
|
949
|
-
make_thread_list
|
950
|
-
for th, in @thread_list
|
951
|
-
next if th == Thread.current
|
952
|
-
context(th).set_suspend
|
953
|
-
end
|
954
|
-
end
|
955
|
-
# Schedule other threads to suspend as soon as possible.
|
956
|
-
Thread.pass
|
957
|
-
end
|
958
|
-
|
959
|
-
def resume
|
960
|
-
MUTEX.synchronize do
|
961
|
-
make_thread_list
|
962
|
-
@thread_list.each do |th,|
|
963
|
-
next if th == Thread.current
|
964
|
-
context(th).clear_suspend
|
965
|
-
end
|
966
|
-
waiting.each do |th|
|
967
|
-
th.run
|
968
|
-
end
|
969
|
-
waiting.clear
|
970
|
-
end
|
971
|
-
# Schedule other threads to restart as soon as possible.
|
972
|
-
Thread.pass
|
973
|
-
end
|
974
|
-
|
975
|
-
def context(thread=Thread.current)
|
976
|
-
c = thread[:__debugger_data__]
|
977
|
-
unless c
|
978
|
-
thread[:__debugger_data__] = c = Context.new
|
979
|
-
end
|
980
|
-
c
|
981
|
-
end
|
982
|
-
|
983
|
-
def interrupt
|
984
|
-
context(@last_thread).stop_next
|
985
|
-
end
|
986
|
-
|
987
|
-
def get_thread(num)
|
988
|
-
th = @thread_list.key(num)
|
989
|
-
unless th
|
990
|
-
@stdout.print "No thread ##{num}\n"
|
991
|
-
throw :debug_error
|
992
|
-
end
|
993
|
-
th
|
994
|
-
end
|
995
|
-
|
996
|
-
def thread_list(num)
|
997
|
-
th = get_thread(num)
|
998
|
-
if th == Thread.current
|
999
|
-
@stdout.print "+"
|
1000
|
-
else
|
1001
|
-
@stdout.print " "
|
1002
|
-
end
|
1003
|
-
@stdout.printf "%d ", num
|
1004
|
-
@stdout.print th.inspect, "\t"
|
1005
|
-
file = context(th).instance_eval{@file}
|
1006
|
-
if file
|
1007
|
-
@stdout.print file,":",context(th).instance_eval{@line}
|
1008
|
-
end
|
1009
|
-
@stdout.print "\n"
|
1010
|
-
end
|
1011
|
-
|
1012
|
-
# Prints all threads in @thread_list to @stdout. Returns a sorted array of
|
1013
|
-
# values from the @thread_list hash.
|
1014
|
-
#
|
1015
|
-
# While in the debugger you can list all of
|
1016
|
-
# the threads with: <b>DEBUGGER__.thread_list_all</b>
|
1017
|
-
#
|
1018
|
-
# (rdb:1) DEBUGGER__.thread_list_all
|
1019
|
-
# +1 #<Thread:0x007fb2320c03f0 run> debug_me.rb.rb:3
|
1020
|
-
# 2 #<Thread:0x007fb23218a538@debug_me.rb.rb:3 sleep>
|
1021
|
-
# 3 #<Thread:0x007fb23218b0f0@debug_me.rb.rb:3 sleep>
|
1022
|
-
# [1, 2, 3]
|
1023
|
-
#
|
1024
|
-
# Your current thread is indicated by a <b>+</b>
|
1025
|
-
#
|
1026
|
-
# Additionally you can list all threads with <b>th l</b>
|
1027
|
-
#
|
1028
|
-
# (rdb:1) th l
|
1029
|
-
# +1 #<Thread:0x007f99328c0410 run> debug_me.rb:3
|
1030
|
-
# 2 #<Thread:0x007f9932938230@debug_me.rb:3 sleep> debug_me.rb:3
|
1031
|
-
# 3 #<Thread:0x007f9932938e10@debug_me.rb:3 sleep> debug_me.rb:3
|
1032
|
-
#
|
1033
|
-
# See DEBUGGER__ for more usage.
|
1034
|
-
|
1035
|
-
def thread_list_all
|
1036
|
-
for th in @thread_list.values.sort
|
1037
|
-
thread_list(th)
|
1038
|
-
end
|
1039
|
-
end
|
1040
|
-
|
1041
|
-
def make_thread_list
|
1042
|
-
hash = {}
|
1043
|
-
for th in Thread::list
|
1044
|
-
if @thread_list.key? th
|
1045
|
-
hash[th] = @thread_list[th]
|
1046
|
-
else
|
1047
|
-
@max_thread += 1
|
1048
|
-
hash[th] = @max_thread
|
1049
|
-
end
|
1050
|
-
end
|
1051
|
-
@thread_list = hash
|
1052
|
-
end
|
1053
|
-
|
1054
|
-
def debug_thread_info(input, binding)
|
1055
|
-
case input
|
1056
|
-
when /^l(?:ist)?/
|
1057
|
-
make_thread_list
|
1058
|
-
thread_list_all
|
1059
|
-
|
1060
|
-
when /^c(?:ur(?:rent)?)?$/
|
1061
|
-
make_thread_list
|
1062
|
-
thread_list(@thread_list[Thread.current])
|
1063
|
-
|
1064
|
-
when /^(?:sw(?:itch)?\s+)?(\d+)/
|
1065
|
-
make_thread_list
|
1066
|
-
th = get_thread($1.to_i)
|
1067
|
-
if th == Thread.current
|
1068
|
-
@stdout.print "It's the current thread.\n"
|
1069
|
-
else
|
1070
|
-
thread_list(@thread_list[th])
|
1071
|
-
context(th).stop_next
|
1072
|
-
th.run
|
1073
|
-
return :cont
|
1074
|
-
end
|
1075
|
-
|
1076
|
-
when /^stop\s+(\d+)/
|
1077
|
-
make_thread_list
|
1078
|
-
th = get_thread($1.to_i)
|
1079
|
-
if th == Thread.current
|
1080
|
-
@stdout.print "It's the current thread.\n"
|
1081
|
-
elsif th.stop?
|
1082
|
-
@stdout.print "Already stopped.\n"
|
1083
|
-
else
|
1084
|
-
thread_list(@thread_list[th])
|
1085
|
-
context(th).suspend
|
1086
|
-
end
|
1087
|
-
|
1088
|
-
when /^resume\s+(\d+)/
|
1089
|
-
make_thread_list
|
1090
|
-
th = get_thread($1.to_i)
|
1091
|
-
if th == Thread.current
|
1092
|
-
@stdout.print "It's the current thread.\n"
|
1093
|
-
elsif !th.stop?
|
1094
|
-
@stdout.print "Already running."
|
1095
|
-
else
|
1096
|
-
thread_list(@thread_list[th])
|
1097
|
-
th.run
|
1098
|
-
end
|
1099
|
-
end
|
1100
|
-
end
|
1101
|
-
end
|
1102
|
-
|
1103
|
-
stdout.printf "Debug.rb\n"
|
1104
|
-
stdout.printf "Emacs support available.\n\n"
|
1105
|
-
RubyVM::InstructionSequence.compile_option = {
|
1106
|
-
trace_instruction: true
|
1107
|
-
}
|
1108
|
-
set_trace_func proc { |event, file, line, id, binding, klass, *rest|
|
1109
|
-
DEBUGGER__.context.trace_func event, file, line, id, binding, klass
|
1110
|
-
}
|
10
|
+
if $0 == __FILE__
|
11
|
+
# DEBUGGER__.add_line_breakpoint __dir__ + '/target.rb', 1
|
12
|
+
# load __dir__ + '/target.rb'
|
13
|
+
else
|
14
|
+
DEBUGGER__.add_line_breakpoint $0, 1
|
1111
15
|
end
|