debug 1.0.0.alpha1 → 1.0.0.beta5
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/.github/workflows/ruby.yml +34 -0
- data/.gitignore +4 -0
- data/CONTRIBUTING.md +145 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +20 -19
- data/README.md +398 -54
- data/Rakefile +21 -1
- data/TODO.md +33 -0
- data/debug.gemspec +7 -10
- data/exe/rdbg +39 -1
- data/ext/debug/debug.c +119 -0
- data/ext/debug/extconf.rb +2 -0
- data/ext/debug/iseq_collector.c +91 -0
- data/lib/debug.rb +1 -1
- data/lib/debug/breakpoint.rb +362 -34
- data/lib/debug/client.rb +53 -17
- data/lib/debug/color.rb +70 -0
- data/lib/debug/config.rb +169 -7
- data/lib/debug/console.rb +96 -0
- data/lib/debug/frame_info.rb +144 -0
- data/lib/debug/open.rb +12 -0
- data/lib/debug/run.rb +4 -0
- data/lib/debug/server.rb +178 -27
- data/lib/debug/server_dap.rb +605 -0
- data/lib/debug/session.rb +779 -193
- data/lib/debug/source_repository.rb +77 -20
- data/lib/debug/test_console.rb +0 -0
- data/lib/debug/thread_client.rb +390 -179
- data/lib/debug/version.rb +1 -1
- data/misc/README.md.erb +350 -0
- metadata +29 -28
- data/lib/debug/repl.rb +0 -69
- data/lib/debug/tcpserver.rb +0 -22
- data/lib/debug/unixserver.rb +0 -18
|
@@ -1,20 +1,77 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
def
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
1
|
+
require_relative 'color'
|
|
2
|
+
|
|
3
|
+
module DEBUGGER__
|
|
4
|
+
class SourceRepository
|
|
5
|
+
SrcInfo = Struct.new(:src, :colored)
|
|
6
|
+
|
|
7
|
+
def initialize
|
|
8
|
+
@files = {} # filename => SrcInfo
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def add iseq, src
|
|
12
|
+
if (path = iseq.absolute_path) && File.exist?(path)
|
|
13
|
+
add_path path
|
|
14
|
+
elsif src
|
|
15
|
+
add_iseq iseq, src
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def all_iseq iseq, rs = []
|
|
20
|
+
rs << iseq
|
|
21
|
+
iseq.each_child{|ci|
|
|
22
|
+
all_iseq(ci, rs)
|
|
23
|
+
}
|
|
24
|
+
rs
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private def add_iseq iseq, src
|
|
28
|
+
line = iseq.first_line
|
|
29
|
+
if line > 1
|
|
30
|
+
src = ("\n" * (line - 1)) + src
|
|
31
|
+
end
|
|
32
|
+
si = SrcInfo.new(src.lines)
|
|
33
|
+
|
|
34
|
+
all_iseq(iseq).each{|e|
|
|
35
|
+
e.instance_variable_set(:@debugger_si, si)
|
|
36
|
+
e.freeze
|
|
37
|
+
}
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private def add_path path
|
|
41
|
+
begin
|
|
42
|
+
src = File.read(path)
|
|
43
|
+
src = src.gsub("\r\n", "\n") # CRLF -> LF
|
|
44
|
+
@files[path] = SrcInfo.new(src.lines)
|
|
45
|
+
rescue SystemCallError
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private def get_si iseq
|
|
50
|
+
return unless iseq
|
|
51
|
+
|
|
52
|
+
if iseq.instance_variable_defined?(:@debugger_si)
|
|
53
|
+
iseq.instance_variable_get(:@debugger_si)
|
|
54
|
+
elsif @files.has_key?(path = iseq.absolute_path)
|
|
55
|
+
@files[path]
|
|
56
|
+
elsif path
|
|
57
|
+
add_path(path)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def get iseq
|
|
62
|
+
if si = get_si(iseq)
|
|
63
|
+
si.src
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
include Color
|
|
68
|
+
|
|
69
|
+
def get_colored iseq
|
|
70
|
+
if si = get_si(iseq)
|
|
71
|
+
si.colored || begin
|
|
72
|
+
si.colored = colorize_code(si.src.join).lines
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
File without changes
|
data/lib/debug/thread_client.rb
CHANGED
|
@@ -1,25 +1,89 @@
|
|
|
1
|
+
require 'objspace'
|
|
2
|
+
require 'pp'
|
|
3
|
+
|
|
4
|
+
require_relative 'frame_info'
|
|
5
|
+
require_relative 'color'
|
|
6
|
+
|
|
1
7
|
module DEBUGGER__
|
|
2
8
|
class ThreadClient
|
|
3
9
|
def self.current
|
|
4
10
|
Thread.current[:DEBUGGER__ThreadClient] || begin
|
|
5
|
-
tc = SESSION.thread_client
|
|
11
|
+
tc = ::DEBUGGER__::SESSION.thread_client
|
|
6
12
|
Thread.current[:DEBUGGER__ThreadClient] = tc
|
|
7
13
|
end
|
|
8
14
|
end
|
|
9
15
|
|
|
10
|
-
|
|
16
|
+
include Color
|
|
17
|
+
|
|
18
|
+
attr_reader :location, :thread, :mode, :id
|
|
19
|
+
|
|
20
|
+
def assemble_arguments(args)
|
|
21
|
+
args.map do |arg|
|
|
22
|
+
"#{colorize_cyan(arg[:name])}=#{arg[:value]}"
|
|
23
|
+
end.join(", ")
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def default_frame_formatter frame
|
|
27
|
+
call_identifier_str =
|
|
28
|
+
case frame.frame_type
|
|
29
|
+
when :block
|
|
30
|
+
level, block_loc, args = frame.block_identifier
|
|
31
|
+
|
|
32
|
+
if !args.empty?
|
|
33
|
+
args_str = " {|#{assemble_arguments(args)}|}"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
"#{colorize_blue("block")}#{args_str} in #{colorize_blue(block_loc + level)}"
|
|
37
|
+
when :method
|
|
38
|
+
ci, args = frame.method_identifier
|
|
39
|
+
|
|
40
|
+
if !args.empty?
|
|
41
|
+
args_str = "(#{assemble_arguments(args)})"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
"#{colorize_blue(ci)}#{args_str}"
|
|
45
|
+
when :c
|
|
46
|
+
colorize_blue(frame.c_identifier)
|
|
47
|
+
when :other
|
|
48
|
+
colorize_blue(frame.other_identifier)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
location_str = colorize(frame.location_str, [:GREEN])
|
|
52
|
+
result = "#{call_identifier_str} at #{location_str}"
|
|
53
|
+
|
|
54
|
+
if return_str = frame.return_str
|
|
55
|
+
return_str = colorize(frame.return_str, [:MAGENTA, :BOLD])
|
|
56
|
+
result += " #=> #{return_str}"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
result
|
|
60
|
+
end
|
|
11
61
|
|
|
12
|
-
def initialize q_evt, q_cmd, thr = Thread.current
|
|
62
|
+
def initialize id, q_evt, q_cmd, thr = Thread.current
|
|
63
|
+
@id = id
|
|
13
64
|
@thread = thr
|
|
65
|
+
@target_frames = nil
|
|
14
66
|
@q_evt = q_evt
|
|
15
67
|
@q_cmd = q_cmd
|
|
16
68
|
@step_tp = nil
|
|
17
69
|
@output = []
|
|
18
|
-
@
|
|
70
|
+
@src_lines_on_stop = (::DEBUGGER__::CONFIG[:show_src_lines] || 10).to_i
|
|
71
|
+
@show_frames_on_stop = (::DEBUGGER__::CONFIG[:show_frames] || 2).to_i
|
|
72
|
+
@frame_formatter = method(:default_frame_formatter)
|
|
73
|
+
@var_map = {} # { thread_local_var_id => obj } for DAP
|
|
74
|
+
set_mode nil
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def name
|
|
78
|
+
"##{@id} #{@thread.name || @thread.backtrace.last}"
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def close
|
|
82
|
+
@q_cmd.close
|
|
19
83
|
end
|
|
20
84
|
|
|
21
85
|
def inspect
|
|
22
|
-
"#<
|
|
86
|
+
"#<DBG:TC #{self.id}:#{self.mode}@#{@thread.backtrace[-1]}>"
|
|
23
87
|
end
|
|
24
88
|
|
|
25
89
|
def puts str = ''
|
|
@@ -37,18 +101,24 @@ module DEBUGGER__
|
|
|
37
101
|
@q_cmd << req
|
|
38
102
|
end
|
|
39
103
|
|
|
104
|
+
def generate_info
|
|
105
|
+
return unless current_frame
|
|
106
|
+
|
|
107
|
+
{ location: current_frame.location_str, line: current_frame.location.lineno }
|
|
108
|
+
end
|
|
109
|
+
|
|
40
110
|
def event! ev, *args
|
|
41
|
-
@q_evt << [self, @output, ev, *args]
|
|
111
|
+
@q_evt << [self, @output, ev, generate_info, *args]
|
|
42
112
|
@output = []
|
|
43
113
|
end
|
|
44
114
|
|
|
45
115
|
## events
|
|
46
116
|
|
|
47
|
-
def on_trap
|
|
48
|
-
if
|
|
49
|
-
raise Interrupt
|
|
117
|
+
def on_trap sig
|
|
118
|
+
if self.mode == :wait_next_action
|
|
119
|
+
# raise Interrupt
|
|
50
120
|
else
|
|
51
|
-
on_suspend :trap
|
|
121
|
+
on_suspend :trap, sig: sig
|
|
52
122
|
end
|
|
53
123
|
end
|
|
54
124
|
|
|
@@ -56,18 +126,24 @@ module DEBUGGER__
|
|
|
56
126
|
on_suspend :pause
|
|
57
127
|
end
|
|
58
128
|
|
|
129
|
+
def on_thread_begin th
|
|
130
|
+
event! :thread_begin, th
|
|
131
|
+
wait_next_action
|
|
132
|
+
end
|
|
133
|
+
|
|
59
134
|
def on_load iseq, eval_src
|
|
60
135
|
event! :load, iseq, eval_src
|
|
61
136
|
wait_next_action
|
|
62
137
|
end
|
|
63
138
|
|
|
64
|
-
def on_breakpoint tp
|
|
65
|
-
on_suspend tp.event, tp
|
|
139
|
+
def on_breakpoint tp, bp
|
|
140
|
+
on_suspend tp.event, tp, bp: bp
|
|
66
141
|
end
|
|
67
142
|
|
|
68
|
-
def on_suspend event, tp = nil
|
|
143
|
+
def on_suspend event, tp = nil, bp: nil, sig: nil
|
|
69
144
|
@current_frame_index = 0
|
|
70
|
-
@target_frames =
|
|
145
|
+
@target_frames = DEBUGGER__.capture_frames __dir__
|
|
146
|
+
|
|
71
147
|
cf = @target_frames.first
|
|
72
148
|
if cf
|
|
73
149
|
@location = cf.location
|
|
@@ -76,222 +152,277 @@ module DEBUGGER__
|
|
|
76
152
|
cf.has_return_value = true
|
|
77
153
|
cf.return_value = tp.return_value
|
|
78
154
|
end
|
|
155
|
+
|
|
156
|
+
if CatchBreakpoint === bp
|
|
157
|
+
cf.has_raised_exception = true
|
|
158
|
+
cf.raised_exception = bp.last_exc
|
|
159
|
+
end
|
|
79
160
|
end
|
|
80
161
|
|
|
81
162
|
if event != :pause
|
|
82
|
-
show_src
|
|
83
|
-
|
|
84
|
-
|
|
163
|
+
show_src max_lines: @src_lines_on_stop
|
|
164
|
+
show_frames @show_frames_on_stop
|
|
165
|
+
|
|
166
|
+
if bp
|
|
167
|
+
event! :suspend, :breakpoint, bp.key
|
|
168
|
+
elsif sig
|
|
169
|
+
event! :suspend, :trap, sig
|
|
170
|
+
else
|
|
171
|
+
event! :suspend, event
|
|
172
|
+
end
|
|
85
173
|
end
|
|
86
174
|
|
|
87
175
|
wait_next_action
|
|
88
176
|
end
|
|
89
|
-
|
|
177
|
+
|
|
90
178
|
## control all
|
|
91
179
|
|
|
180
|
+
begin
|
|
181
|
+
TracePoint.new(:raise){}.enable(target_thread: Thread.current)
|
|
182
|
+
SUPPORT_TARGET_THREAD = true
|
|
183
|
+
rescue ArgumentError
|
|
184
|
+
SUPPORT_TARGET_THREAD = false
|
|
185
|
+
end
|
|
186
|
+
|
|
92
187
|
def step_tp
|
|
93
188
|
@step_tp.disable if @step_tp
|
|
94
|
-
@step_tp = TracePoint.new(:line, :b_return, :return){|tp|
|
|
95
|
-
next if SESSION.break? tp.path, tp.lineno
|
|
96
|
-
next if !yield
|
|
97
|
-
tp.disable
|
|
98
|
-
on_suspend tp.event, tp
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
@step_tp.enable(target_thread: Thread.current)
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
FrameInfo = Struct.new(:location, :self, :binding, :iseq, :class, :has_return_value, :return_value)
|
|
105
|
-
|
|
106
|
-
def target_frames
|
|
107
|
-
RubyVM::DebugInspector.open{|dc|
|
|
108
|
-
locs = dc.backtrace_locations
|
|
109
|
-
locs.map.with_index{|e, i|
|
|
110
|
-
unless File.dirname(e.path) == File.dirname(__FILE__)
|
|
111
|
-
FrameInfo.new(
|
|
112
|
-
e,
|
|
113
|
-
dc.frame_self(i),
|
|
114
|
-
dc.frame_binding(i),
|
|
115
|
-
dc.frame_iseq(i),
|
|
116
|
-
dc.frame_class(i))
|
|
117
|
-
end
|
|
118
|
-
}.compact
|
|
119
|
-
}
|
|
120
|
-
end
|
|
121
189
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
190
|
+
thread = Thread.current
|
|
191
|
+
|
|
192
|
+
if SUPPORT_TARGET_THREAD
|
|
193
|
+
@step_tp = TracePoint.new(:line, :b_return, :return){|tp|
|
|
194
|
+
next if SESSION.break? tp.path, tp.lineno
|
|
195
|
+
next if !yield
|
|
196
|
+
next if tp.path.start_with?(__dir__)
|
|
197
|
+
next unless File.exist?(tp.path) if ::DEBUGGER__::CONFIG[:skip_nosrc]
|
|
198
|
+
|
|
199
|
+
tp.disable
|
|
200
|
+
on_suspend tp.event, tp
|
|
201
|
+
}
|
|
202
|
+
@step_tp.enable(target_thread: thread)
|
|
203
|
+
else
|
|
204
|
+
@step_tp = TracePoint.new(:line, :b_return, :return){|tp|
|
|
205
|
+
next if thread != Thread.current
|
|
206
|
+
next if SESSION.break? tp.path, tp.lineno
|
|
207
|
+
next if !yield
|
|
208
|
+
next unless File.exist?(tp.path) if ::DEBUGGER__::CONFIG[:skip_nosrc]
|
|
209
|
+
|
|
210
|
+
tp.disable
|
|
211
|
+
on_suspend tp.event, tp
|
|
127
212
|
}
|
|
128
|
-
|
|
213
|
+
@step_tp.enable
|
|
214
|
+
end
|
|
129
215
|
end
|
|
130
216
|
|
|
131
217
|
def current_frame
|
|
132
|
-
@target_frames
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
if (src = SESSION.source(path)) && src[0]
|
|
137
|
-
src[0].lines
|
|
138
|
-
elsif File.exist?(path)
|
|
139
|
-
File.readlines(path)
|
|
218
|
+
if @target_frames
|
|
219
|
+
@target_frames[@current_frame_index]
|
|
220
|
+
else
|
|
221
|
+
nil
|
|
140
222
|
end
|
|
141
223
|
end
|
|
142
224
|
|
|
143
|
-
def show_src
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
225
|
+
def show_src(frame_index: @current_frame_index,
|
|
226
|
+
update_line: false,
|
|
227
|
+
max_lines: 10,
|
|
228
|
+
start_line: nil,
|
|
229
|
+
end_line: nil,
|
|
230
|
+
dir: +1)
|
|
231
|
+
if @target_frames && frame = @target_frames[frame_index]
|
|
232
|
+
if file_lines = frame.file_lines
|
|
233
|
+
frame_line = frame.location.lineno - 1
|
|
234
|
+
|
|
235
|
+
lines = file_lines.map.with_index do |e, i|
|
|
236
|
+
if i == frame_line
|
|
150
237
|
"=> #{'%4d' % (i+1)}| #{e}"
|
|
151
238
|
else
|
|
152
239
|
" #{'%4d' % (i+1)}| #{e}"
|
|
153
240
|
end
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
unless start_line
|
|
244
|
+
if frame.show_line
|
|
245
|
+
if dir > 0
|
|
246
|
+
start_line = frame.show_line
|
|
247
|
+
else
|
|
248
|
+
end_line = frame.show_line - max_lines
|
|
249
|
+
start_line = [end_line - max_lines, 0].max
|
|
250
|
+
end
|
|
251
|
+
else
|
|
252
|
+
start_line = [frame_line - max_lines/2, 0].max
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
unless end_line
|
|
257
|
+
end_line = [start_line + max_lines, lines.size].min
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
if update_line
|
|
261
|
+
frame.show_line = end_line
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
if start_line != end_line && max_lines
|
|
265
|
+
puts "[#{start_line+1}, #{end_line}] in #{frame.pretty_path}" if !update_line && max_lines != 1
|
|
266
|
+
puts lines[start_line ... end_line]
|
|
267
|
+
end
|
|
268
|
+
else # no file lines
|
|
269
|
+
puts "# No sourcefile available for #{frame.path}"
|
|
159
270
|
end
|
|
160
271
|
end
|
|
272
|
+
rescue Exception => e
|
|
273
|
+
p e
|
|
274
|
+
pp e.backtrace
|
|
275
|
+
exit!
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
def show_by_editor path = nil
|
|
279
|
+
unless path
|
|
280
|
+
if @target_frames && frame = @target_frames[@current_frame_index]
|
|
281
|
+
path = frame.path
|
|
282
|
+
else
|
|
283
|
+
return # can't get path
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
if File.exist?(path)
|
|
288
|
+
if editor = (ENV['RUBY_DEBUG_EDITOR'] || ENV['EDITOR'])
|
|
289
|
+
puts "command: #{editor}"
|
|
290
|
+
puts " path: #{path}"
|
|
291
|
+
system(editor, path)
|
|
292
|
+
else
|
|
293
|
+
puts "can not find editor setting: ENV['RUBY_DEBUG_EDITOR'] or ENV['EDITOR']"
|
|
294
|
+
end
|
|
295
|
+
else
|
|
296
|
+
puts "Can not find file: #{path}"
|
|
297
|
+
end
|
|
161
298
|
end
|
|
162
299
|
|
|
163
300
|
def show_locals
|
|
164
301
|
if s = current_frame&.self
|
|
165
|
-
puts " %self => #{s}"
|
|
302
|
+
puts " #{colorize_cyan("%self")} => #{colored_inspect(s)}"
|
|
166
303
|
end
|
|
167
304
|
if current_frame&.has_return_value
|
|
168
|
-
puts " %return => #{current_frame.return_value}"
|
|
305
|
+
puts " #{colorize_cyan("%return")} => #{colored_inspect(current_frame.return_value)}"
|
|
306
|
+
end
|
|
307
|
+
if current_frame&.has_raised_exception
|
|
308
|
+
puts " #{colorize_cyan("%raised")} => #{colored_inspect(current_frame.raised_exception)}"
|
|
169
309
|
end
|
|
170
310
|
if b = current_frame&.binding
|
|
171
311
|
b.local_variables.each{|loc|
|
|
172
|
-
|
|
312
|
+
value = b.local_variable_get(loc)
|
|
313
|
+
puts " #{colorize_cyan(loc)} => #{colored_inspect(value)}"
|
|
173
314
|
}
|
|
174
315
|
end
|
|
175
316
|
end
|
|
176
317
|
|
|
177
318
|
def show_ivars
|
|
178
319
|
if s = current_frame&.self
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
puts " #{iv} => #{
|
|
320
|
+
s.instance_variables.each{|iv|
|
|
321
|
+
value = s.instance_variable_get(iv)
|
|
322
|
+
puts " #{colorize_cyan(iv)} => #{colored_inspect(value)}"
|
|
182
323
|
}
|
|
183
324
|
end
|
|
184
325
|
end
|
|
185
326
|
|
|
186
|
-
def frame_eval src,
|
|
327
|
+
def frame_eval src, re_raise: false
|
|
187
328
|
begin
|
|
329
|
+
@success_last_eval = false
|
|
330
|
+
|
|
188
331
|
b = current_frame.binding
|
|
189
|
-
if b
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
332
|
+
result = if b
|
|
333
|
+
f, _l = b.source_location
|
|
334
|
+
b.eval(src, "(rdbg)/#{f}")
|
|
335
|
+
else
|
|
336
|
+
frame_self = current_frame.self
|
|
337
|
+
frame_self.instance_eval(src)
|
|
338
|
+
end
|
|
339
|
+
@success_last_eval = true
|
|
340
|
+
result
|
|
341
|
+
|
|
196
342
|
rescue Exception => e
|
|
197
|
-
return
|
|
343
|
+
return yield(e) if block_given?
|
|
344
|
+
|
|
345
|
+
puts "eval error: #{e}"
|
|
198
346
|
|
|
199
|
-
puts "Error: #{e}"
|
|
200
347
|
e.backtrace_locations.each do |loc|
|
|
201
348
|
break if loc.path == __FILE__
|
|
202
349
|
puts " #{loc}"
|
|
203
350
|
end
|
|
204
|
-
|
|
351
|
+
raise if re_raise
|
|
205
352
|
end
|
|
206
353
|
end
|
|
207
354
|
|
|
208
|
-
def
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
355
|
+
def frame_str(i)
|
|
356
|
+
cur_str = (@current_frame_index == i ? '=>' : ' ')
|
|
357
|
+
prefix = "#{cur_str}##{i}"
|
|
358
|
+
frame = @target_frames[i]
|
|
359
|
+
frame_string = @frame_formatter.call(frame)
|
|
360
|
+
"#{prefix}\t#{frame_string}"
|
|
212
361
|
end
|
|
213
362
|
|
|
214
|
-
def
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
363
|
+
def show_frames max = (@target_frames || []).size
|
|
364
|
+
if max > 0 && @target_frames
|
|
365
|
+
size = @target_frames.size
|
|
366
|
+
max += 1 if size == max + 1
|
|
367
|
+
max.times{|i|
|
|
368
|
+
break if i >= size
|
|
369
|
+
puts frame_str(i)
|
|
370
|
+
}
|
|
371
|
+
puts " # and #{size - max} frames (use `bt' command for all frames)" if max < size
|
|
220
372
|
end
|
|
221
373
|
end
|
|
222
374
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
def short_inspect obj
|
|
226
|
-
str = obj.inspect
|
|
227
|
-
if str.length > SHORT_INSPECT_LENGTH
|
|
228
|
-
str[0...SHORT_INSPECT_LENGTH] + '...'
|
|
229
|
-
else
|
|
230
|
-
str
|
|
231
|
-
end
|
|
375
|
+
def show_frame i=0
|
|
376
|
+
puts frame_str(i)
|
|
232
377
|
end
|
|
233
378
|
|
|
234
|
-
def
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
buff << (@current_frame_index == i ? '--> ' : ' ')
|
|
240
|
-
if b
|
|
241
|
-
buff << "##{i}\t#{frame.location}"
|
|
379
|
+
def show_object_info expr
|
|
380
|
+
begin
|
|
381
|
+
result = frame_eval(expr, re_raise: true)
|
|
382
|
+
rescue Exception
|
|
383
|
+
# ignore
|
|
242
384
|
else
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
buff << " {|#{args}|}"
|
|
385
|
+
klass = ObjectSpace.internal_class_of(result)
|
|
386
|
+
exists = []
|
|
387
|
+
klass.ancestors.each{|k|
|
|
388
|
+
puts "= #{k}"
|
|
389
|
+
if (ms = (k.instance_methods(false) - exists)).size > 0
|
|
390
|
+
puts ms.sort.join("\t")
|
|
391
|
+
exists |= ms
|
|
251
392
|
end
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
args = parameters_info b, m.parameters.map{|type, v| v}
|
|
256
|
-
ksig = klass_sig frame
|
|
257
|
-
buff << " #{ksig}#{callee}(#{args})"
|
|
258
|
-
end
|
|
259
|
-
end
|
|
393
|
+
}
|
|
394
|
+
end
|
|
395
|
+
end
|
|
260
396
|
|
|
261
|
-
|
|
262
|
-
|
|
397
|
+
def add_breakpoint args
|
|
398
|
+
case args.first
|
|
399
|
+
when :method
|
|
400
|
+
klass_name, op, method_name, cond = args[1..]
|
|
401
|
+
bp = MethodBreakpoint.new(current_frame.binding, klass_name, op, method_name, cond)
|
|
402
|
+
begin
|
|
403
|
+
bp.enable
|
|
404
|
+
rescue Exception => e
|
|
405
|
+
puts e.message
|
|
406
|
+
::DEBUGGER__::METHOD_ADDED_TRACKER.enable
|
|
263
407
|
end
|
|
408
|
+
event! :result, :method_breakpoint, bp
|
|
264
409
|
else
|
|
265
|
-
|
|
410
|
+
raise "unknown breakpoint: #{args}"
|
|
266
411
|
end
|
|
267
|
-
|
|
268
|
-
buff
|
|
269
|
-
end
|
|
270
|
-
|
|
271
|
-
def show_frame_all
|
|
272
|
-
@target_frames.size.times{|i|
|
|
273
|
-
puts frame_str(i)
|
|
274
|
-
}
|
|
275
|
-
end
|
|
276
|
-
|
|
277
|
-
def print_frame i
|
|
278
|
-
puts frame_str(i)
|
|
279
412
|
end
|
|
280
413
|
|
|
281
|
-
def
|
|
282
|
-
|
|
283
|
-
([size, n].min).times{|i|
|
|
284
|
-
print_frame i
|
|
285
|
-
}
|
|
286
|
-
if n < size
|
|
287
|
-
puts " # and #{size - n} frames (use `bt' command for all frames)"
|
|
288
|
-
end
|
|
414
|
+
def set_mode mode
|
|
415
|
+
@mode = mode
|
|
289
416
|
end
|
|
290
417
|
|
|
291
418
|
def wait_next_action
|
|
292
|
-
|
|
419
|
+
set_mode :wait_next_action
|
|
420
|
+
|
|
421
|
+
SESSION.check_forked
|
|
293
422
|
|
|
294
423
|
while cmds = @q_cmd.pop
|
|
424
|
+
# pp [self, cmds: cmds]
|
|
425
|
+
|
|
295
426
|
cmd, *args = *cmds
|
|
296
427
|
|
|
297
428
|
case cmd
|
|
@@ -303,53 +434,105 @@ module DEBUGGER__
|
|
|
303
434
|
when :in
|
|
304
435
|
step_tp{true}
|
|
305
436
|
when :next
|
|
306
|
-
|
|
437
|
+
frame = @target_frames.first
|
|
438
|
+
path = frame.location.absolute_path || "!eval:#{frame.path}"
|
|
439
|
+
line = frame.location.lineno
|
|
440
|
+
frame.iseq.traceable_lines_norec(lines = {})
|
|
441
|
+
next_line = lines.keys.bsearch{|e| e > line}
|
|
442
|
+
if !next_line && (last_line = frame.iseq.last_line) > line
|
|
443
|
+
next_line = last_line
|
|
444
|
+
end
|
|
445
|
+
depth = @target_frames.first.frame_depth
|
|
446
|
+
|
|
307
447
|
step_tp{
|
|
308
|
-
|
|
448
|
+
loc = caller_locations(2, 1).first
|
|
449
|
+
loc_path = loc.absolute_path || "!eval:#{loc.path}"
|
|
450
|
+
|
|
451
|
+
# same stack depth
|
|
452
|
+
(DEBUGGER__.frame_depth - 3 <= depth) ||
|
|
453
|
+
|
|
454
|
+
# different frame
|
|
455
|
+
(next_line && loc_path == path &&
|
|
456
|
+
(loc_lineno = loc.lineno) > line &&
|
|
457
|
+
loc_lineno <= next_line)
|
|
309
458
|
}
|
|
310
459
|
when :finish
|
|
311
|
-
|
|
312
|
-
step_tp{
|
|
460
|
+
depth = @target_frames.first.frame_depth
|
|
461
|
+
step_tp{
|
|
462
|
+
# 3 is debugger's frame count
|
|
463
|
+
DEBUGGER__.frame_depth - 3 < depth
|
|
464
|
+
}
|
|
313
465
|
else
|
|
314
466
|
raise
|
|
315
467
|
end
|
|
316
468
|
break
|
|
317
469
|
when :eval
|
|
318
470
|
eval_type, eval_src = *args
|
|
319
|
-
|
|
471
|
+
|
|
472
|
+
case eval_type
|
|
473
|
+
when :display, :try_display
|
|
474
|
+
else
|
|
475
|
+
result = frame_eval(eval_src)
|
|
476
|
+
end
|
|
477
|
+
result_type = nil
|
|
320
478
|
|
|
321
479
|
case eval_type
|
|
322
480
|
when :p
|
|
323
481
|
puts "=> " + result.inspect
|
|
324
482
|
when :pp
|
|
325
483
|
puts "=> "
|
|
326
|
-
PP.pp(result, out = ''.dup)
|
|
484
|
+
PP.pp(result, out = ''.dup, SESSION.width)
|
|
327
485
|
puts out
|
|
328
486
|
when :call
|
|
329
487
|
result = frame_eval(eval_src)
|
|
330
|
-
when :display
|
|
488
|
+
when :display, :try_display
|
|
489
|
+
failed_results = []
|
|
331
490
|
eval_src.each_with_index{|src, i|
|
|
332
|
-
|
|
491
|
+
result = frame_eval(src){|e|
|
|
492
|
+
failed_results << [i, e.message]
|
|
493
|
+
"<error: #{e.message}>"
|
|
494
|
+
}
|
|
495
|
+
puts "#{i}: #{src} = #{result}"
|
|
333
496
|
}
|
|
334
|
-
|
|
497
|
+
|
|
498
|
+
result_type = eval_type
|
|
499
|
+
result = failed_results
|
|
500
|
+
when :watch
|
|
501
|
+
if @success_last_eval
|
|
502
|
+
if eval_src.match?(/@\w+/)
|
|
503
|
+
object =
|
|
504
|
+
if b = current_frame.binding
|
|
505
|
+
b.receiver
|
|
506
|
+
else
|
|
507
|
+
current_frame.self
|
|
508
|
+
end
|
|
509
|
+
puts "#{object} #{eval_src} = #{result}"
|
|
510
|
+
result = WatchIVarBreakpoint.new(eval_src, object, result)
|
|
511
|
+
end
|
|
512
|
+
|
|
513
|
+
result_type = :watch
|
|
514
|
+
else
|
|
515
|
+
result = nil
|
|
516
|
+
end
|
|
335
517
|
else
|
|
336
|
-
raise "unknown error option: #{args.
|
|
518
|
+
raise "unknown error option: #{args.inspect}"
|
|
337
519
|
end
|
|
338
|
-
|
|
520
|
+
|
|
521
|
+
event! :result, result_type, result
|
|
339
522
|
when :frame
|
|
340
523
|
type, arg = *args
|
|
341
524
|
case type
|
|
342
525
|
when :up
|
|
343
526
|
if @current_frame_index + 1 < @target_frames.size
|
|
344
|
-
@current_frame_index += 1
|
|
527
|
+
@current_frame_index += 1
|
|
345
528
|
show_src max_lines: 1
|
|
346
|
-
|
|
529
|
+
show_frame(@current_frame_index)
|
|
347
530
|
end
|
|
348
531
|
when :down
|
|
349
532
|
if @current_frame_index > 0
|
|
350
533
|
@current_frame_index -= 1
|
|
351
534
|
show_src max_lines: 1
|
|
352
|
-
|
|
535
|
+
show_frame(@current_frame_index)
|
|
353
536
|
end
|
|
354
537
|
when :set
|
|
355
538
|
if arg
|
|
@@ -361,28 +544,47 @@ module DEBUGGER__
|
|
|
361
544
|
end
|
|
362
545
|
end
|
|
363
546
|
show_src max_lines: 1
|
|
364
|
-
|
|
547
|
+
show_frame(@current_frame_index)
|
|
365
548
|
else
|
|
366
549
|
raise "unsupported frame operation: #{arg.inspect}"
|
|
367
550
|
end
|
|
368
551
|
event! :result, nil
|
|
369
552
|
when :show
|
|
370
|
-
type
|
|
553
|
+
type = args.shift
|
|
554
|
+
|
|
371
555
|
case type
|
|
372
556
|
when :backtrace
|
|
373
|
-
|
|
557
|
+
show_frames
|
|
558
|
+
|
|
374
559
|
when :list
|
|
375
|
-
show_src
|
|
376
|
-
|
|
560
|
+
show_src(update_line: true, **(args.first || {}))
|
|
561
|
+
|
|
562
|
+
when :edit
|
|
563
|
+
show_by_editor(args.first)
|
|
564
|
+
|
|
565
|
+
when :local
|
|
566
|
+
show_frame
|
|
377
567
|
show_locals
|
|
378
|
-
when :ivars
|
|
379
568
|
show_ivars
|
|
569
|
+
|
|
570
|
+
when :object_info
|
|
571
|
+
expr = args.shift
|
|
572
|
+
show_object_info expr
|
|
573
|
+
|
|
380
574
|
else
|
|
381
|
-
raise "unknown show param: " + args.inspect
|
|
575
|
+
raise "unknown show param: " + [type, *args].inspect
|
|
382
576
|
end
|
|
577
|
+
|
|
383
578
|
event! :result, nil
|
|
579
|
+
|
|
580
|
+
when :breakpoint
|
|
581
|
+
add_breakpoint args
|
|
582
|
+
|
|
583
|
+
when :dap
|
|
584
|
+
process_dap args
|
|
585
|
+
|
|
384
586
|
else
|
|
385
|
-
raise [
|
|
587
|
+
raise [cmd, *args].inspect
|
|
386
588
|
end
|
|
387
589
|
end
|
|
388
590
|
|
|
@@ -392,11 +594,20 @@ module DEBUGGER__
|
|
|
392
594
|
pp [__FILE__, __LINE__, e, e.backtrace]
|
|
393
595
|
raise
|
|
394
596
|
ensure
|
|
395
|
-
|
|
597
|
+
set_mode nil
|
|
396
598
|
end
|
|
397
599
|
|
|
398
600
|
def to_s
|
|
399
|
-
|
|
601
|
+
loc = current_frame&.location
|
|
602
|
+
|
|
603
|
+
if loc
|
|
604
|
+
str = "(#{@thread.name || @thread.status})@#{loc}"
|
|
605
|
+
else
|
|
606
|
+
str = "(#{@thread.name || @thread.status})@#{@thread.to_s}"
|
|
607
|
+
end
|
|
608
|
+
|
|
609
|
+
str += " (not under control)" unless self.mode
|
|
610
|
+
str
|
|
400
611
|
end
|
|
401
612
|
end
|
|
402
613
|
end
|