debug 1.4.0 → 1.9.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CONTRIBUTING.md +210 -6
- data/Gemfile +2 -0
- data/LICENSE.txt +0 -0
- data/README.md +161 -85
- data/Rakefile +33 -10
- data/TODO.md +8 -8
- data/debug.gemspec +9 -7
- data/exe/rdbg +23 -4
- data/ext/debug/debug.c +111 -21
- data/ext/debug/extconf.rb +23 -0
- data/ext/debug/iseq_collector.c +2 -0
- data/lib/debug/abbrev_command.rb +77 -0
- data/lib/debug/breakpoint.rb +102 -74
- data/lib/debug/client.rb +46 -12
- data/lib/debug/color.rb +0 -0
- data/lib/debug/config.rb +129 -36
- data/lib/debug/console.rb +46 -40
- data/lib/debug/dap_custom/traceInspector.rb +336 -0
- data/lib/debug/frame_info.rb +40 -25
- data/lib/debug/irb_integration.rb +37 -0
- data/lib/debug/local.rb +17 -11
- data/lib/debug/open.rb +0 -0
- data/lib/debug/open_nonstop.rb +0 -0
- data/lib/debug/prelude.rb +3 -2
- data/lib/debug/server.rb +126 -56
- data/lib/debug/server_cdp.rb +673 -248
- data/lib/debug/server_dap.rb +497 -261
- data/lib/debug/session.rb +899 -441
- data/lib/debug/source_repository.rb +122 -49
- data/lib/debug/start.rb +1 -1
- data/lib/debug/thread_client.rb +460 -155
- data/lib/debug/tracer.rb +10 -16
- data/lib/debug/version.rb +1 -1
- data/lib/debug.rb +7 -2
- data/misc/README.md.erb +106 -56
- metadata +14 -24
- data/.github/ISSUE_TEMPLATE/bug_report.md +0 -24
- data/.github/ISSUE_TEMPLATE/custom.md +0 -10
- data/.github/ISSUE_TEMPLATE/feature_request.md +0 -14
- data/.github/pull_request_template.md +0 -9
- data/.github/workflows/ruby.yml +0 -34
- data/.gitignore +0 -12
- data/bin/console +0 -14
- data/bin/gentest +0 -30
- data/bin/setup +0 -8
- data/lib/debug/bp.vim +0 -68
data/lib/debug/session.rb
CHANGED
@@ -7,7 +7,7 @@ return if ENV['RUBY_DEBUG_ENABLE'] == '0'
|
|
7
7
|
if $0.end_with?('bin/bundle') && ARGV.first == 'exec'
|
8
8
|
trace_var(:$0) do |file|
|
9
9
|
trace_var(:$0, nil)
|
10
|
-
if /-r (#{__dir__}\S+)/ =~ ENV['RUBYOPT']
|
10
|
+
if /-r (#{Regexp.escape(__dir__)}\S+)/ =~ ENV['RUBYOPT']
|
11
11
|
lib = $1
|
12
12
|
$LOADED_FEATURES.delete_if{|path| path.start_with?(__dir__)}
|
13
13
|
ENV['RUBY_DEBUG_INITIAL_SUSPEND_PATH'] = file
|
@@ -19,6 +19,15 @@ if $0.end_with?('bin/bundle') && ARGV.first == 'exec'
|
|
19
19
|
return
|
20
20
|
end
|
21
21
|
|
22
|
+
# restore RUBYOPT
|
23
|
+
if (added_opt = ENV['RUBY_DEBUG_ADDED_RUBYOPT']) &&
|
24
|
+
(rubyopt = ENV['RUBYOPT']) &&
|
25
|
+
rubyopt.end_with?(added_opt)
|
26
|
+
|
27
|
+
ENV['RUBYOPT'] = rubyopt.delete_suffix(added_opt)
|
28
|
+
ENV['RUBY_DEBUG_ADDED_RUBYOPT'] = nil
|
29
|
+
end
|
30
|
+
|
22
31
|
require_relative 'frame_info'
|
23
32
|
require_relative 'config'
|
24
33
|
require_relative 'thread_client'
|
@@ -31,7 +40,8 @@ $LOADED_FEATURES << 'debug.rb'
|
|
31
40
|
$LOADED_FEATURES << File.expand_path(File.join(__dir__, '..', 'debug.rb'))
|
32
41
|
require 'debug' # invalidate the $LOADED_FEATURE cache
|
33
42
|
|
34
|
-
require 'json' if ENV['
|
43
|
+
require 'json' if ENV['RUBY_DEBUG_TEST_UI'] == 'terminal'
|
44
|
+
require 'pp'
|
35
45
|
|
36
46
|
class RubyVM::InstructionSequence
|
37
47
|
def traceable_lines_norec lines
|
@@ -56,34 +66,37 @@ class RubyVM::InstructionSequence
|
|
56
66
|
|
57
67
|
def type
|
58
68
|
self.to_a[9]
|
59
|
-
end
|
69
|
+
end unless method_defined?(:type)
|
60
70
|
|
61
|
-
def
|
62
|
-
self.to_a
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
end
|
71
|
+
def parameters_symbols
|
72
|
+
ary = self.to_a
|
73
|
+
argc = ary[4][:arg_size]
|
74
|
+
locals = ary.to_a[10]
|
75
|
+
locals[0...argc]
|
76
|
+
end unless method_defined?(:parameters_symbols)
|
68
77
|
|
69
78
|
def last_line
|
70
79
|
self.to_a[4][:code_location][2]
|
71
|
-
end
|
80
|
+
end unless method_defined?(:last_line)
|
72
81
|
|
73
82
|
def first_line
|
74
83
|
self.to_a[4][:code_location][0]
|
75
|
-
end
|
76
|
-
end
|
84
|
+
end unless method_defined?(:first_line)
|
85
|
+
end if defined?(RubyVM::InstructionSequence)
|
77
86
|
|
78
87
|
module DEBUGGER__
|
79
|
-
|
88
|
+
PresetCommands = Struct.new(:commands, :source, :auto_continue)
|
89
|
+
SessionCommand = Struct.new(:block, :repeat, :unsafe, :cancel_auto_continue, :postmortem)
|
90
|
+
|
80
91
|
class PostmortemError < RuntimeError; end
|
81
92
|
|
82
93
|
class Session
|
83
|
-
attr_reader :intercepted_sigint_cmd, :process_group
|
94
|
+
attr_reader :intercepted_sigint_cmd, :process_group, :subsession_id
|
84
95
|
|
85
|
-
|
86
|
-
|
96
|
+
include Color
|
97
|
+
|
98
|
+
def initialize
|
99
|
+
@ui = nil
|
87
100
|
@sr = SourceRepository.new
|
88
101
|
@bps = {} # bp.key => bp
|
89
102
|
# [file, line] => LineBreakpoint
|
@@ -104,48 +117,69 @@ module DEBUGGER__
|
|
104
117
|
@intercept_trap_sigint = false
|
105
118
|
@intercepted_sigint_cmd = 'DEFAULT'
|
106
119
|
@process_group = ProcessGroup.new
|
107
|
-
@
|
120
|
+
@subsession_stack = []
|
121
|
+
@subsession_id = 0
|
108
122
|
|
109
123
|
@frame_map = {} # for DAP: {id => [threadId, frame_depth]} and CDP: {id => frame_depth}
|
110
124
|
@var_map = {1 => [:globals], } # {id => ...} for DAP
|
111
125
|
@src_map = {} # {id => src}
|
112
126
|
|
113
|
-
@
|
127
|
+
@scr_id_map = {} # for CDP
|
114
128
|
@obj_map = {} # { object_id => ... } for CDP
|
115
129
|
|
116
130
|
@tp_thread_begin = nil
|
131
|
+
@tp_thread_end = nil
|
132
|
+
|
133
|
+
@commands = {}
|
134
|
+
@unsafe_context = false
|
135
|
+
|
136
|
+
@has_keep_script_lines = defined?(RubyVM.keep_script_lines)
|
137
|
+
|
117
138
|
@tp_load_script = TracePoint.new(:script_compiled){|tp|
|
118
|
-
|
139
|
+
eval_script = tp.eval_script unless @has_keep_script_lines
|
140
|
+
ThreadClient.current.on_load tp.instruction_sequence, eval_script
|
119
141
|
}
|
120
142
|
@tp_load_script.enable
|
121
143
|
|
122
144
|
@thread_stopper = thread_stopper
|
123
|
-
|
124
|
-
activate
|
125
|
-
|
126
145
|
self.postmortem = CONFIG[:postmortem]
|
146
|
+
|
147
|
+
register_default_command
|
127
148
|
end
|
128
149
|
|
129
150
|
def active?
|
130
151
|
!@q_evt.closed?
|
131
152
|
end
|
132
153
|
|
133
|
-
def
|
134
|
-
@
|
154
|
+
def remote?
|
155
|
+
@ui.remote?
|
135
156
|
end
|
136
157
|
|
137
|
-
def
|
138
|
-
@
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
@ui.activate self, on_fork: true
|
158
|
+
def stop_stepping? file, line, subsession_id = nil
|
159
|
+
if @bps.has_key? [file, line]
|
160
|
+
true
|
161
|
+
elsif subsession_id && @subsession_id != subsession_id
|
162
|
+
true
|
143
163
|
else
|
144
|
-
|
164
|
+
false
|
145
165
|
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def activate ui = nil, on_fork: false
|
169
|
+
@ui = ui if ui
|
170
|
+
|
171
|
+
@tp_thread_begin&.disable
|
172
|
+
@tp_thread_end&.disable
|
173
|
+
@tp_thread_begin = nil
|
174
|
+
@tp_thread_end = nil
|
175
|
+
@ui.activate self, on_fork: on_fork
|
146
176
|
|
147
177
|
q = Queue.new
|
178
|
+
first_q = Queue.new
|
148
179
|
@session_server = Thread.new do
|
180
|
+
# make sure `@session_server` is assigned
|
181
|
+
first_q.pop; first_q = nil
|
182
|
+
|
149
183
|
Thread.current.name = 'DEBUGGER__::SESSION@server'
|
150
184
|
Thread.current.abort_on_exception = true
|
151
185
|
|
@@ -163,12 +197,24 @@ module DEBUGGER__
|
|
163
197
|
end
|
164
198
|
@tp_thread_begin.enable
|
165
199
|
|
200
|
+
@tp_thread_end = TracePoint.new(:thread_end) do |tp|
|
201
|
+
@th_clients.delete(Thread.current)
|
202
|
+
end
|
203
|
+
@tp_thread_end.enable
|
204
|
+
|
166
205
|
# session start
|
167
206
|
q << true
|
168
207
|
session_server_main
|
169
208
|
end
|
209
|
+
first_q << :ok
|
170
210
|
|
171
211
|
q.pop
|
212
|
+
|
213
|
+
# For activating irb:rdbg with startup config like `RUBY_DEBUG_IRB_CONSOLE=1`
|
214
|
+
# Because in that case the `Config#if_updated` callback would not be triggered
|
215
|
+
if CONFIG[:irb_console] && !CONFIG[:open]
|
216
|
+
activate_irb_integration
|
217
|
+
end
|
172
218
|
end
|
173
219
|
|
174
220
|
def deactivate
|
@@ -176,6 +222,7 @@ module DEBUGGER__
|
|
176
222
|
@thread_stopper.disable
|
177
223
|
@tp_load_script.disable
|
178
224
|
@tp_thread_begin.disable
|
225
|
+
@tp_thread_end.disable
|
179
226
|
@bps.each_value{|bp| bp.disable}
|
180
227
|
@th_clients.each_value{|thc| thc.close}
|
181
228
|
@tracers.values.each{|t| t.disable}
|
@@ -187,6 +234,16 @@ module DEBUGGER__
|
|
187
234
|
def reset_ui ui
|
188
235
|
@ui.deactivate
|
189
236
|
@ui = ui
|
237
|
+
|
238
|
+
# activate new ui
|
239
|
+
@tp_thread_begin.disable
|
240
|
+
@tp_thread_end.disable
|
241
|
+
@ui.activate self
|
242
|
+
if @ui.respond_to?(:reader_thread) && thc = get_thread_client(@ui.reader_thread)
|
243
|
+
thc.mark_as_management
|
244
|
+
end
|
245
|
+
@tp_thread_begin.enable
|
246
|
+
@tp_thread_end.enable
|
190
247
|
end
|
191
248
|
|
192
249
|
def pop_event
|
@@ -201,63 +258,81 @@ module DEBUGGER__
|
|
201
258
|
deactivate
|
202
259
|
end
|
203
260
|
|
261
|
+
def request_tc(req)
|
262
|
+
@tc << req
|
263
|
+
end
|
264
|
+
|
265
|
+
def request_tc_with_restarted_threads(req)
|
266
|
+
restart_all_threads
|
267
|
+
request_tc(req)
|
268
|
+
end
|
269
|
+
|
270
|
+
def request_eval type, src
|
271
|
+
request_tc_with_restarted_threads [:eval, type, src]
|
272
|
+
end
|
273
|
+
|
204
274
|
def process_event evt
|
205
275
|
# variable `@internal_info` is only used for test
|
206
276
|
tc, output, ev, @internal_info, *ev_args = evt
|
207
|
-
output.each{|str| @ui.puts str} if ev != :suspend
|
208
277
|
|
209
|
-
|
278
|
+
output.each{|str| @ui.puts str} if ev != :suspend
|
210
279
|
|
211
|
-
|
280
|
+
# special event, tc is nil
|
281
|
+
# and we don't want to set @tc to the newly created thread's ThreadClient
|
282
|
+
if ev == :thread_begin
|
212
283
|
th = ev_args.shift
|
213
284
|
q = ev_args.shift
|
214
285
|
on_thread_begin th
|
215
286
|
q << true
|
216
287
|
|
217
|
-
|
218
|
-
|
288
|
+
return
|
289
|
+
end
|
219
290
|
|
291
|
+
@tc = tc
|
292
|
+
|
293
|
+
case ev
|
294
|
+
when :init
|
295
|
+
enter_subsession
|
296
|
+
wait_command_loop
|
220
297
|
when :load
|
221
298
|
iseq, src = ev_args
|
222
299
|
on_load iseq, src
|
223
|
-
|
224
|
-
tc << :continue
|
300
|
+
request_tc :continue
|
225
301
|
|
226
302
|
when :trace
|
227
303
|
trace_id, msg = ev_args
|
228
304
|
if t = @tracers.values.find{|t| t.object_id == trace_id}
|
229
305
|
t.puts msg
|
230
306
|
end
|
231
|
-
|
307
|
+
request_tc :continue
|
232
308
|
|
233
309
|
when :suspend
|
234
310
|
enter_subsession if ev_args.first != :replay
|
235
|
-
output.each{|str| @ui.puts str}
|
311
|
+
output.each{|str| @ui.puts str} unless @ui.ignore_output_on_suspend?
|
236
312
|
|
237
313
|
case ev_args.first
|
238
314
|
when :breakpoint
|
239
315
|
bp, i = bp_index ev_args[1]
|
240
316
|
clean_bps unless bp
|
241
|
-
@ui.event :suspend_bp, i, bp, tc.id
|
317
|
+
@ui.event :suspend_bp, i, bp, @tc.id
|
242
318
|
when :trap
|
243
|
-
@ui.event :suspend_trap, sig = ev_args[1], tc.id
|
319
|
+
@ui.event :suspend_trap, sig = ev_args[1], @tc.id
|
244
320
|
|
245
321
|
if sig == :SIGINT && (@intercepted_sigint_cmd.kind_of?(Proc) || @intercepted_sigint_cmd.kind_of?(String))
|
246
322
|
@ui.puts "#{@intercepted_sigint_cmd.inspect} is registered as SIGINT handler."
|
247
323
|
@ui.puts "`sigint` command execute it."
|
248
324
|
end
|
249
325
|
else
|
250
|
-
@ui.event :suspended, tc.id
|
326
|
+
@ui.event :suspended, @tc.id
|
251
327
|
end
|
252
328
|
|
253
329
|
if @displays.empty?
|
254
|
-
wait_command_loop
|
330
|
+
wait_command_loop
|
255
331
|
else
|
256
|
-
|
332
|
+
request_eval :display, @displays
|
257
333
|
end
|
258
|
-
|
259
334
|
when :result
|
260
|
-
raise "[BUG] not in subsession"
|
335
|
+
raise "[BUG] not in subsession" if @subsession_stack.empty?
|
261
336
|
|
262
337
|
case ev_args.first
|
263
338
|
when :try_display
|
@@ -269,6 +344,7 @@ module DEBUGGER__
|
|
269
344
|
end
|
270
345
|
end
|
271
346
|
|
347
|
+
stop_all_threads
|
272
348
|
when :method_breakpoint, :watch_breakpoint
|
273
349
|
bp = ev_args[1]
|
274
350
|
if bp
|
@@ -282,18 +358,16 @@ module DEBUGGER__
|
|
282
358
|
obj_inspect = ev_args[2]
|
283
359
|
opt = ev_args[3]
|
284
360
|
add_tracer ObjectTracer.new(@ui, obj_id, obj_inspect, **opt)
|
361
|
+
stop_all_threads
|
285
362
|
else
|
286
|
-
|
363
|
+
stop_all_threads
|
287
364
|
end
|
288
365
|
|
289
|
-
wait_command_loop
|
366
|
+
wait_command_loop
|
290
367
|
|
291
|
-
when :
|
292
|
-
|
293
|
-
wait_command_loop
|
294
|
-
when :cdp_result
|
295
|
-
cdp_event ev_args
|
296
|
-
wait_command_loop tc
|
368
|
+
when :protocol_result
|
369
|
+
process_protocol_result ev_args
|
370
|
+
wait_command_loop
|
297
371
|
end
|
298
372
|
end
|
299
373
|
|
@@ -308,7 +382,7 @@ module DEBUGGER__
|
|
308
382
|
if @preset_command && !@preset_command.commands.empty?
|
309
383
|
@preset_command.commands += cs
|
310
384
|
else
|
311
|
-
@preset_command =
|
385
|
+
@preset_command = PresetCommands.new(cs, name, continue)
|
312
386
|
end
|
313
387
|
|
314
388
|
ThreadClient.current.on_init name if kick
|
@@ -326,9 +400,7 @@ module DEBUGGER__
|
|
326
400
|
"DEBUGGER__::SESSION"
|
327
401
|
end
|
328
402
|
|
329
|
-
def wait_command_loop
|
330
|
-
@tc = tc
|
331
|
-
|
403
|
+
def wait_command_loop
|
332
404
|
loop do
|
333
405
|
case wait_command
|
334
406
|
when :retry
|
@@ -369,7 +441,7 @@ module DEBUGGER__
|
|
369
441
|
@ui.puts "(rdbg:#{@preset_command.source}) #{line}"
|
370
442
|
end
|
371
443
|
else
|
372
|
-
@ui.puts "INTERNAL_INFO: #{JSON.generate(@internal_info)}" if ENV['
|
444
|
+
@ui.puts "INTERNAL_INFO: #{JSON.generate(@internal_info)}" if ENV['RUBY_DEBUG_TEST_UI'] == 'terminal'
|
373
445
|
line = @ui.readline prompt
|
374
446
|
end
|
375
447
|
|
@@ -383,97 +455,121 @@ module DEBUGGER__
|
|
383
455
|
end
|
384
456
|
end
|
385
457
|
|
386
|
-
def
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
else
|
391
|
-
return :retry
|
392
|
-
end
|
393
|
-
else
|
394
|
-
@repl_prev_line = line
|
395
|
-
end
|
396
|
-
|
397
|
-
/([^\s]+)(?:\s+(.+))?/ =~ line
|
398
|
-
cmd, arg = $1, $2
|
458
|
+
private def register_command *names,
|
459
|
+
repeat: false, unsafe: true, cancel_auto_continue: false, postmortem: true,
|
460
|
+
&b
|
461
|
+
cmd = SessionCommand.new(b, repeat, unsafe, cancel_auto_continue, postmortem)
|
399
462
|
|
400
|
-
|
463
|
+
names.each{|name|
|
464
|
+
@commands[name] = cmd
|
465
|
+
}
|
466
|
+
end
|
401
467
|
|
402
|
-
|
468
|
+
def register_default_command
|
403
469
|
### Control flow
|
404
470
|
|
405
471
|
# * `s[tep]`
|
406
472
|
# * Step in. Resume the program until next breakable point.
|
407
473
|
# * `s[tep] <n>`
|
408
474
|
# * Step in, resume the program at `<n>`th breakable point.
|
409
|
-
|
410
|
-
|
411
|
-
|
475
|
+
register_command 's', 'step',
|
476
|
+
repeat: true,
|
477
|
+
cancel_auto_continue: true,
|
478
|
+
postmortem: false do |arg|
|
412
479
|
step_command :in, arg
|
480
|
+
end
|
413
481
|
|
414
482
|
# * `n[ext]`
|
415
483
|
# * Step over. Resume the program until next line.
|
416
484
|
# * `n[ext] <n>`
|
417
485
|
# * Step over, same as `step <n>`.
|
418
|
-
|
419
|
-
|
420
|
-
|
486
|
+
register_command 'n', 'next',
|
487
|
+
repeat: true,
|
488
|
+
cancel_auto_continue: true,
|
489
|
+
postmortem: false do |arg|
|
421
490
|
step_command :next, arg
|
491
|
+
end
|
422
492
|
|
423
493
|
# * `fin[ish]`
|
424
494
|
# * Finish this frame. Resume the program until the current frame is finished.
|
425
495
|
# * `fin[ish] <n>`
|
426
496
|
# * Finish `<n>`th frames.
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
497
|
+
register_command 'fin', 'finish',
|
498
|
+
repeat: true,
|
499
|
+
cancel_auto_continue: true,
|
500
|
+
postmortem: false do |arg|
|
431
501
|
if arg&.to_i == 0
|
432
502
|
raise 'finish command with 0 does not make sense.'
|
433
503
|
end
|
434
504
|
|
435
505
|
step_command :finish, arg
|
506
|
+
end
|
436
507
|
|
437
|
-
# * `
|
508
|
+
# * `u[ntil]`
|
509
|
+
# * Similar to `next` command, but only stop later lines or the end of the current frame.
|
510
|
+
# * Similar to gdb's `advance` command.
|
511
|
+
# * `u[ntil] <[file:]line>`
|
512
|
+
# * Run til the program reaches given location or the end of the current frame.
|
513
|
+
# * `u[ntil] <name>`
|
514
|
+
# * Run til the program invokes a method `<name>`. `<name>` can be a regexp with `/name/`.
|
515
|
+
register_command 'u', 'until',
|
516
|
+
repeat: true,
|
517
|
+
cancel_auto_continue: true,
|
518
|
+
postmortem: false do |arg|
|
519
|
+
|
520
|
+
step_command :until, arg
|
521
|
+
end
|
522
|
+
|
523
|
+
# * `c` or `cont` or `continue`
|
438
524
|
# * Resume the program.
|
439
|
-
|
440
|
-
|
525
|
+
register_command 'c', 'cont', 'continue',
|
526
|
+
repeat: true,
|
527
|
+
cancel_auto_continue: true do |arg|
|
441
528
|
leave_subsession :continue
|
529
|
+
end
|
442
530
|
|
443
531
|
# * `q[uit]` or `Ctrl-D`
|
444
532
|
# * Finish debugger (with the debuggee process on non-remote debugging).
|
445
|
-
|
533
|
+
register_command 'q', 'quit' do |arg|
|
446
534
|
if ask 'Really quit?'
|
447
|
-
@ui.quit arg.to_i
|
535
|
+
@ui.quit arg.to_i do
|
536
|
+
request_tc :quit
|
537
|
+
end
|
448
538
|
leave_subsession :continue
|
449
539
|
else
|
450
|
-
|
540
|
+
next :retry
|
451
541
|
end
|
542
|
+
end
|
452
543
|
|
453
544
|
# * `q[uit]!`
|
454
545
|
# * Same as q[uit] but without the confirmation prompt.
|
455
|
-
|
456
|
-
@ui.quit arg.to_i
|
457
|
-
|
546
|
+
register_command 'q!', 'quit!', unsafe: false do |arg|
|
547
|
+
@ui.quit arg.to_i do
|
548
|
+
request_tc :quit
|
549
|
+
end
|
550
|
+
leave_subsession :continue
|
551
|
+
end
|
458
552
|
|
459
553
|
# * `kill`
|
460
554
|
# * Stop the debuggee process with `Kernel#exit!`.
|
461
|
-
|
555
|
+
register_command 'kill' do |arg|
|
462
556
|
if ask 'Really kill?'
|
463
557
|
exit! (arg || 1).to_i
|
464
558
|
else
|
465
|
-
|
559
|
+
next :retry
|
466
560
|
end
|
561
|
+
end
|
467
562
|
|
468
563
|
# * `kill!`
|
469
564
|
# * Same as kill but without the confirmation prompt.
|
470
|
-
|
565
|
+
register_command 'kill!', unsafe: false do |arg|
|
471
566
|
exit! (arg || 1).to_i
|
567
|
+
end
|
472
568
|
|
473
569
|
# * `sigint`
|
474
570
|
# * Execute SIGINT handler registered by the debuggee.
|
475
571
|
# * Note that this command should be used just after stop by `SIGINT`.
|
476
|
-
|
572
|
+
register_command 'sigint' do
|
477
573
|
begin
|
478
574
|
case cmd = @intercepted_sigint_cmd
|
479
575
|
when nil, 'IGNORE', :IGNORE, 'DEFAULT', :DEFAULT
|
@@ -489,8 +585,9 @@ module DEBUGGER__
|
|
489
585
|
rescue Exception => e
|
490
586
|
@ui.puts "Exception: #{e}"
|
491
587
|
@ui.puts e.backtrace.map{|line| " #{e}"}
|
492
|
-
|
588
|
+
next :retry
|
493
589
|
end
|
590
|
+
end
|
494
591
|
|
495
592
|
### Breakpoint
|
496
593
|
|
@@ -510,53 +607,26 @@ module DEBUGGER__
|
|
510
607
|
# * break and run `<command>` before stopping.
|
511
608
|
# * `b[reak] ... do: <command>`
|
512
609
|
# * break and run `<command>`, and continue.
|
513
|
-
# * `b[reak] ... path: <
|
514
|
-
# * break if the
|
610
|
+
# * `b[reak] ... path: <path>`
|
611
|
+
# * break if the path matches to `<path>`. `<path>` can be a regexp with `/regexp/`.
|
515
612
|
# * `b[reak] if: <expr>`
|
516
613
|
# * break if: `<expr>` is true at any lines.
|
517
614
|
# * Note that this feature is super slow.
|
518
|
-
|
519
|
-
check_postmortem
|
520
|
-
|
615
|
+
register_command 'b', 'break', postmortem: false, unsafe: false do |arg|
|
521
616
|
if arg == nil
|
522
617
|
show_bps
|
523
|
-
|
618
|
+
next :retry
|
524
619
|
else
|
525
620
|
case bp = repl_add_breakpoint(arg)
|
526
621
|
when :noretry
|
527
622
|
when nil
|
528
|
-
|
623
|
+
next :retry
|
529
624
|
else
|
530
625
|
show_bps bp
|
531
|
-
|
626
|
+
next :retry
|
532
627
|
end
|
533
628
|
end
|
534
|
-
|
535
|
-
# skip
|
536
|
-
when 'bv'
|
537
|
-
check_postmortem
|
538
|
-
require 'json'
|
539
|
-
|
540
|
-
h = Hash.new{|h, k| h[k] = []}
|
541
|
-
@bps.each_value{|bp|
|
542
|
-
if LineBreakpoint === bp
|
543
|
-
h[bp.path] << {lnum: bp.line}
|
544
|
-
end
|
545
|
-
}
|
546
|
-
if h.empty?
|
547
|
-
# TODO: clean?
|
548
|
-
else
|
549
|
-
open(".rdb_breakpoints.json", 'w'){|f| JSON.dump(h, f)}
|
550
|
-
end
|
551
|
-
|
552
|
-
vimsrc = File.join(__dir__, 'bp.vim')
|
553
|
-
system("vim -R -S #{vimsrc} #{@tc.location.path}")
|
554
|
-
|
555
|
-
if File.exist?(".rdb_breakpoints.json")
|
556
|
-
pp JSON.load(File.read(".rdb_breakpoints.json"))
|
557
|
-
end
|
558
|
-
|
559
|
-
return :retry
|
629
|
+
end
|
560
630
|
|
561
631
|
# * `catch <Error>`
|
562
632
|
# * Set breakpoint on raising `<Error>`.
|
@@ -566,18 +636,18 @@ module DEBUGGER__
|
|
566
636
|
# * runs `<command>` before stopping.
|
567
637
|
# * `catch ... do: <command>`
|
568
638
|
# * stops and run `<command>`, and continue.
|
569
|
-
# * `catch ... path: <
|
570
|
-
# * stops if the exception is raised from a path
|
571
|
-
|
572
|
-
check_postmortem
|
573
|
-
|
639
|
+
# * `catch ... path: <path>`
|
640
|
+
# * stops if the exception is raised from a `<path>`. `<path>` can be a regexp with `/regexp/`.
|
641
|
+
register_command 'catch', postmortem: false, unsafe: false do |arg|
|
574
642
|
if arg
|
575
643
|
bp = repl_add_catch_breakpoint arg
|
576
644
|
show_bps bp if bp
|
577
645
|
else
|
578
646
|
show_bps
|
579
647
|
end
|
580
|
-
|
648
|
+
|
649
|
+
:retry
|
650
|
+
end
|
581
651
|
|
582
652
|
# * `watch @ivar`
|
583
653
|
# * Stop the execution when the result of current scope's `@ivar` is changed.
|
@@ -588,26 +658,22 @@ module DEBUGGER__
|
|
588
658
|
# * runs `<command>` before stopping.
|
589
659
|
# * `watch ... do: <command>`
|
590
660
|
# * stops and run `<command>`, and continue.
|
591
|
-
# * `watch ... path: <
|
592
|
-
# * stops if the
|
593
|
-
|
594
|
-
check_postmortem
|
595
|
-
|
661
|
+
# * `watch ... path: <path>`
|
662
|
+
# * stops if the path matches `<path>`. `<path>` can be a regexp with `/regexp/`.
|
663
|
+
register_command 'wat', 'watch', postmortem: false, unsafe: false do |arg|
|
596
664
|
if arg && arg.match?(/\A@\w+/)
|
597
665
|
repl_add_watch_breakpoint(arg)
|
598
666
|
else
|
599
667
|
show_bps
|
600
|
-
|
668
|
+
:retry
|
601
669
|
end
|
670
|
+
end
|
602
671
|
|
603
672
|
# * `del[ete]`
|
604
673
|
# * delete all breakpoints.
|
605
674
|
# * `del[ete] <bpnum>`
|
606
675
|
# * delete specified breakpoint.
|
607
|
-
|
608
|
-
check_postmortem
|
609
|
-
|
610
|
-
bp =
|
676
|
+
register_command 'del', 'delete', postmortem: false, unsafe: false do |arg|
|
611
677
|
case arg
|
612
678
|
when nil
|
613
679
|
show_bps
|
@@ -615,12 +681,13 @@ module DEBUGGER__
|
|
615
681
|
delete_bp
|
616
682
|
end
|
617
683
|
when /\d+/
|
618
|
-
delete_bp arg.to_i
|
684
|
+
bp = delete_bp arg.to_i
|
619
685
|
else
|
620
686
|
nil
|
621
687
|
end
|
622
688
|
@ui.puts "deleted: \##{bp[0]} #{bp[1]}" if bp
|
623
|
-
|
689
|
+
:retry
|
690
|
+
end
|
624
691
|
|
625
692
|
### Information
|
626
693
|
|
@@ -632,19 +699,20 @@ module DEBUGGER__
|
|
632
699
|
# * Only shows frames with method name or location info that matches `/regexp/`.
|
633
700
|
# * `bt <num> /regexp/` or `backtrace <num> /regexp/`
|
634
701
|
# * Only shows first `<num>` frames with method name or location info that matches `/regexp/`.
|
635
|
-
|
702
|
+
register_command 'bt', 'backtrace', unsafe: false do |arg|
|
636
703
|
case arg
|
637
704
|
when /\A(\d+)\z/
|
638
|
-
|
705
|
+
request_tc_with_restarted_threads [:show, :backtrace, arg.to_i, nil]
|
639
706
|
when /\A\/(.*)\/\z/
|
640
707
|
pattern = $1
|
641
|
-
|
708
|
+
request_tc_with_restarted_threads [:show, :backtrace, nil, Regexp.compile(pattern)]
|
642
709
|
when /\A(\d+)\s+\/(.*)\/\z/
|
643
710
|
max, pattern = $1, $2
|
644
|
-
|
711
|
+
request_tc_with_restarted_threads [:show, :backtrace, max.to_i, Regexp.compile(pattern)]
|
645
712
|
else
|
646
|
-
|
713
|
+
request_tc_with_restarted_threads [:show, :backtrace, nil, nil]
|
647
714
|
end
|
715
|
+
end
|
648
716
|
|
649
717
|
# * `l[ist]`
|
650
718
|
# * Show current frame's source code.
|
@@ -653,57 +721,75 @@ module DEBUGGER__
|
|
653
721
|
# * Show predecessor lines as opposed to the `list` command.
|
654
722
|
# * `l[ist] <start>` or `l[ist] <start>-<end>`
|
655
723
|
# * Show current frame's source code from the line <start> to <end> if given.
|
656
|
-
|
724
|
+
register_command 'l', 'list', repeat: true, unsafe: false do |arg|
|
657
725
|
case arg ? arg.strip : nil
|
658
726
|
when /\A(\d+)\z/
|
659
|
-
|
727
|
+
request_tc [:show, :list, {start_line: arg.to_i - 1}]
|
660
728
|
when /\A-\z/
|
661
|
-
|
729
|
+
request_tc [:show, :list, {dir: -1}]
|
662
730
|
when /\A(\d+)-(\d+)\z/
|
663
|
-
|
731
|
+
request_tc [:show, :list, {start_line: $1.to_i - 1, end_line: $2.to_i}]
|
664
732
|
when nil
|
665
|
-
|
733
|
+
request_tc [:show, :list]
|
666
734
|
else
|
667
735
|
@ui.puts "Can not handle list argument: #{arg}"
|
668
|
-
|
736
|
+
:retry
|
669
737
|
end
|
738
|
+
end
|
739
|
+
|
740
|
+
# * `whereami`
|
741
|
+
# * Show the current frame with source code.
|
742
|
+
register_command 'whereami', unsafe: false do
|
743
|
+
request_tc [:show, :whereami]
|
744
|
+
end
|
670
745
|
|
671
746
|
# * `edit`
|
672
747
|
# * Open the current file on the editor (use `EDITOR` environment variable).
|
673
748
|
# * Note that edited file will not be reloaded.
|
674
749
|
# * `edit <file>`
|
675
750
|
# * Open <file> on the editor.
|
676
|
-
|
751
|
+
register_command 'edit' do |arg|
|
677
752
|
if @ui.remote?
|
678
753
|
@ui.puts "not supported on the remote console."
|
679
|
-
|
754
|
+
next :retry
|
680
755
|
end
|
681
756
|
|
682
757
|
begin
|
683
758
|
arg = resolve_path(arg) if arg
|
684
759
|
rescue Errno::ENOENT
|
685
760
|
@ui.puts "not found: #{arg}"
|
686
|
-
|
761
|
+
next :retry
|
687
762
|
end
|
688
763
|
|
689
|
-
|
764
|
+
request_tc [:show, :edit, arg]
|
765
|
+
end
|
766
|
+
|
767
|
+
info_subcommands = nil
|
768
|
+
info_subcommands_abbrev = nil
|
690
769
|
|
691
770
|
# * `i[nfo]`
|
692
|
-
#
|
693
|
-
# * `i[nfo]
|
771
|
+
# * Show information about current frame (local/instance variables and defined constants).
|
772
|
+
# * `i[nfo]` <subcommand>
|
773
|
+
# * `info` has the following sub-commands.
|
774
|
+
# * Sub-commands can be specified with few letters which is unambiguous, like `l` for 'locals'.
|
775
|
+
# * `i[nfo] l or locals or local_variables`
|
694
776
|
# * Show information about the current frame (local variables)
|
695
|
-
# * It includes `self` as `%self` and a return value as
|
696
|
-
# * `i[nfo] i
|
777
|
+
# * It includes `self` as `%self` and a return value as `_return`.
|
778
|
+
# * `i[nfo] i or ivars or instance_variables`
|
697
779
|
# * Show information about instance variables about `self`.
|
698
|
-
#
|
780
|
+
# * `info ivars <expr>` shows the instance variables of the result of `<expr>`.
|
781
|
+
# * `i[nfo] c or consts or constants`
|
699
782
|
# * Show information about accessible constants except toplevel constants.
|
700
|
-
#
|
783
|
+
# * `info consts <expr>` shows the constants of a class/module of the result of `<expr>`
|
784
|
+
# * `i[nfo] g or globals or global_variables`
|
701
785
|
# * Show information about global variables
|
702
|
-
# * `i[nfo]
|
703
|
-
# * Filter the output with `</pattern/>`.
|
704
|
-
# * `i[nfo] th[read[s]]`
|
786
|
+
# * `i[nfo] th or threads`
|
705
787
|
# * Show all threads (same as `th[read]`).
|
706
|
-
|
788
|
+
# * `i[nfo] b or breakpoints or w or watchpoints`
|
789
|
+
# * Show all breakpoints and watchpoints.
|
790
|
+
# * `i[nfo] ... /regexp/`
|
791
|
+
# * Filter the output with `/regexp/`.
|
792
|
+
register_command 'i', 'info', unsafe: false do |arg|
|
707
793
|
if /\/(.+)\/\z/ =~ arg
|
708
794
|
pat = Regexp.compile($1)
|
709
795
|
sub = $~.pre_match.strip
|
@@ -711,63 +797,98 @@ module DEBUGGER__
|
|
711
797
|
sub = arg
|
712
798
|
end
|
713
799
|
|
800
|
+
if /\A(.+?)\b(.+)/ =~ sub
|
801
|
+
sub = $1
|
802
|
+
opt = $2.strip
|
803
|
+
opt = nil if opt.empty?
|
804
|
+
end
|
805
|
+
|
806
|
+
if sub && !info_subcommands
|
807
|
+
info_subcommands = {
|
808
|
+
locals: %w[ locals local_variables ],
|
809
|
+
ivars: %w[ ivars instance_variables ],
|
810
|
+
consts: %w[ consts constants ],
|
811
|
+
globals:%w[ globals global_variables ],
|
812
|
+
threads:%w[ threads ],
|
813
|
+
breaks: %w[ breakpoints ],
|
814
|
+
watchs: %w[ watchpoints ],
|
815
|
+
}
|
816
|
+
|
817
|
+
require_relative 'abbrev_command'
|
818
|
+
info_subcommands_abbrev = AbbrevCommand.new(info_subcommands)
|
819
|
+
end
|
820
|
+
|
821
|
+
if sub
|
822
|
+
sub = info_subcommands_abbrev.search sub, :unknown do |candidates|
|
823
|
+
# note: unreached now
|
824
|
+
@ui.puts "Ambiguous command '#{sub}': #{candidates.join(' ')}"
|
825
|
+
end
|
826
|
+
end
|
827
|
+
|
714
828
|
case sub
|
715
829
|
when nil
|
716
|
-
|
717
|
-
when
|
718
|
-
|
719
|
-
when
|
720
|
-
|
721
|
-
when
|
722
|
-
|
723
|
-
when
|
724
|
-
|
725
|
-
when
|
830
|
+
request_tc_with_restarted_threads [:show, :default, pat] # something useful
|
831
|
+
when :locals
|
832
|
+
request_tc_with_restarted_threads [:show, :locals, pat]
|
833
|
+
when :ivars
|
834
|
+
request_tc_with_restarted_threads [:show, :ivars, pat, opt]
|
835
|
+
when :consts
|
836
|
+
request_tc_with_restarted_threads [:show, :consts, pat, opt]
|
837
|
+
when :globals
|
838
|
+
request_tc_with_restarted_threads [:show, :globals, pat]
|
839
|
+
when :threads
|
726
840
|
thread_list
|
727
|
-
|
841
|
+
:retry
|
842
|
+
when :breaks, :watchs
|
843
|
+
show_bps
|
844
|
+
:retry
|
728
845
|
else
|
729
846
|
@ui.puts "unrecognized argument for info command: #{arg}"
|
730
847
|
show_help 'info'
|
731
|
-
|
848
|
+
:retry
|
732
849
|
end
|
850
|
+
end
|
733
851
|
|
734
852
|
# * `o[utline]` or `ls`
|
735
853
|
# * Show you available methods, constants, local variables, and instance variables in the current scope.
|
736
854
|
# * `o[utline] <expr>` or `ls <expr>`
|
737
855
|
# * Show you available methods and instance variables of the given object.
|
738
856
|
# * If the object is a class/module, it also lists its constants.
|
739
|
-
|
740
|
-
|
857
|
+
register_command 'outline', 'o', 'ls', unsafe: false do |arg|
|
858
|
+
request_tc_with_restarted_threads [:show, :outline, arg]
|
859
|
+
end
|
741
860
|
|
742
861
|
# * `display`
|
743
862
|
# * Show display setting.
|
744
863
|
# * `display <expr>`
|
745
864
|
# * Show the result of `<expr>` at every suspended timing.
|
746
|
-
|
865
|
+
register_command 'display', postmortem: false do |arg|
|
747
866
|
if arg && !arg.empty?
|
748
867
|
@displays << arg
|
749
|
-
|
868
|
+
request_eval :try_display, @displays
|
750
869
|
else
|
751
|
-
|
870
|
+
request_eval :display, @displays
|
752
871
|
end
|
872
|
+
end
|
753
873
|
|
754
874
|
# * `undisplay`
|
755
875
|
# * Remove all display settings.
|
756
876
|
# * `undisplay <displaynum>`
|
757
877
|
# * Remove a specified display setting.
|
758
|
-
|
878
|
+
register_command 'undisplay', postmortem: false, unsafe: false do |arg|
|
759
879
|
case arg
|
760
880
|
when /(\d+)/
|
761
881
|
if @displays[n = $1.to_i]
|
762
882
|
@displays.delete_at n
|
763
883
|
end
|
764
|
-
|
884
|
+
request_eval :display, @displays
|
765
885
|
when nil
|
766
886
|
if ask "clear all?", 'N'
|
767
887
|
@displays.clear
|
768
888
|
end
|
769
|
-
|
889
|
+
:retry
|
770
890
|
end
|
891
|
+
end
|
771
892
|
|
772
893
|
### Frame control
|
773
894
|
|
@@ -775,53 +896,59 @@ module DEBUGGER__
|
|
775
896
|
# * Show the current frame.
|
776
897
|
# * `f[rame] <framenum>`
|
777
898
|
# * Specify a current frame. Evaluation are run on specified frame.
|
778
|
-
|
779
|
-
|
899
|
+
register_command 'frame', 'f', unsafe: false do |arg|
|
900
|
+
request_tc [:frame, :set, arg]
|
901
|
+
end
|
780
902
|
|
781
903
|
# * `up`
|
782
904
|
# * Specify the upper frame.
|
783
|
-
|
784
|
-
|
905
|
+
register_command 'up', repeat: true, unsafe: false do |arg|
|
906
|
+
request_tc [:frame, :up]
|
907
|
+
end
|
785
908
|
|
786
909
|
# * `down`
|
787
910
|
# * Specify the lower frame.
|
788
|
-
|
789
|
-
|
911
|
+
register_command 'down', repeat: true, unsafe: false do |arg|
|
912
|
+
request_tc [:frame, :down]
|
913
|
+
end
|
790
914
|
|
791
915
|
### Evaluate
|
792
916
|
|
793
917
|
# * `p <expr>`
|
794
918
|
# * Evaluate like `p <expr>` on the current frame.
|
795
|
-
|
796
|
-
|
919
|
+
register_command 'p' do |arg|
|
920
|
+
request_eval :p, arg.to_s
|
921
|
+
end
|
797
922
|
|
798
923
|
# * `pp <expr>`
|
799
924
|
# * Evaluate like `pp <expr>` on the current frame.
|
800
|
-
|
801
|
-
|
925
|
+
register_command 'pp' do |arg|
|
926
|
+
request_eval :pp, arg.to_s
|
927
|
+
end
|
802
928
|
|
803
929
|
# * `eval <expr>`
|
804
930
|
# * Evaluate `<expr>` on the current frame.
|
805
|
-
|
931
|
+
register_command 'eval', 'call' do |arg|
|
806
932
|
if arg == nil || arg.empty?
|
807
933
|
show_help 'eval'
|
808
934
|
@ui.puts "\nTo evaluate the variable `#{cmd}`, use `pp #{cmd}` instead."
|
809
|
-
|
935
|
+
:retry
|
810
936
|
else
|
811
|
-
|
937
|
+
request_eval :call, arg
|
812
938
|
end
|
939
|
+
end
|
813
940
|
|
814
941
|
# * `irb`
|
815
|
-
# *
|
816
|
-
|
942
|
+
# * Activate and switch to `irb:rdbg` console
|
943
|
+
register_command 'irb' do |arg|
|
817
944
|
if @ui.remote?
|
818
|
-
@ui.puts "not supported on the remote console."
|
819
|
-
|
945
|
+
@ui.puts "\nIRB is not supported on the remote console."
|
946
|
+
else
|
947
|
+
config_set :irb_console, true
|
820
948
|
end
|
821
|
-
@tc << [:eval, :irb]
|
822
949
|
|
823
|
-
|
824
|
-
|
950
|
+
:retry
|
951
|
+
end
|
825
952
|
|
826
953
|
### Trace
|
827
954
|
# * `trace`
|
@@ -834,15 +961,15 @@ module DEBUGGER__
|
|
834
961
|
# * Add an exception tracer. It indicates raising exceptions.
|
835
962
|
# * `trace object <expr>`
|
836
963
|
# * Add an object tracer. It indicates that an object by `<expr>` is passed as a parameter or a receiver on method call.
|
837
|
-
# * `trace ...
|
838
|
-
# * Indicates only matched events to
|
964
|
+
# * `trace ... /regexp/`
|
965
|
+
# * Indicates only matched events to `/regexp/`.
|
839
966
|
# * `trace ... into: <file>`
|
840
967
|
# * Save trace information into: `<file>`.
|
841
968
|
# * `trace off <num>`
|
842
969
|
# * Disable tracer specified by `<num>` (use `trace` command to check the numbers).
|
843
970
|
# * `trace off [line|call|pass]`
|
844
971
|
# * Disable all tracers. If `<type>` is provided, disable specified type tracers.
|
845
|
-
|
972
|
+
register_command 'trace', postmortem: false, unsafe: false do |arg|
|
846
973
|
if (re = /\s+into:\s*(.+)/) =~ arg
|
847
974
|
into = $1
|
848
975
|
arg.sub!(re, '')
|
@@ -860,22 +987,22 @@ module DEBUGGER__
|
|
860
987
|
@ui.puts "* \##{i} #{t}"
|
861
988
|
}
|
862
989
|
@ui.puts
|
863
|
-
|
990
|
+
:retry
|
864
991
|
|
865
992
|
when /\Aline\z/
|
866
993
|
add_tracer LineTracer.new(@ui, pattern: pattern, into: into)
|
867
|
-
|
994
|
+
:retry
|
868
995
|
|
869
996
|
when /\Acall\z/
|
870
997
|
add_tracer CallTracer.new(@ui, pattern: pattern, into: into)
|
871
|
-
|
998
|
+
:retry
|
872
999
|
|
873
1000
|
when /\Aexception\z/
|
874
1001
|
add_tracer ExceptionTracer.new(@ui, pattern: pattern, into: into)
|
875
|
-
|
1002
|
+
:retry
|
876
1003
|
|
877
1004
|
when /\Aobject\s+(.+)/
|
878
|
-
|
1005
|
+
request_tc_with_restarted_threads [:trace, :object, $1.strip, {pattern: pattern, into: into}]
|
879
1006
|
|
880
1007
|
when /\Aoff\s+(\d+)\z/
|
881
1008
|
if t = @tracers.values[$1.to_i]
|
@@ -884,7 +1011,7 @@ module DEBUGGER__
|
|
884
1011
|
else
|
885
1012
|
@ui.puts "Unmatched: #{$1}"
|
886
1013
|
end
|
887
|
-
|
1014
|
+
:retry
|
888
1015
|
|
889
1016
|
when /\Aoff(\s+(line|call|exception|object))?\z/
|
890
1017
|
@tracers.values.each{|t|
|
@@ -893,12 +1020,13 @@ module DEBUGGER__
|
|
893
1020
|
@ui.puts "Disable #{t.to_s}"
|
894
1021
|
end
|
895
1022
|
}
|
896
|
-
|
1023
|
+
:retry
|
897
1024
|
|
898
1025
|
else
|
899
1026
|
@ui.puts "Unknown trace option: #{arg.inspect}"
|
900
|
-
|
1027
|
+
:retry
|
901
1028
|
end
|
1029
|
+
end
|
902
1030
|
|
903
1031
|
# Record
|
904
1032
|
# * `record`
|
@@ -910,14 +1038,15 @@ module DEBUGGER__
|
|
910
1038
|
# * `s[tep]` does stepping forward with the last log.
|
911
1039
|
# * `step reset`
|
912
1040
|
# * Stop replay .
|
913
|
-
|
1041
|
+
register_command 'record', postmortem: false, unsafe: false do |arg|
|
914
1042
|
case arg
|
915
1043
|
when nil, 'on', 'off'
|
916
|
-
|
1044
|
+
request_tc [:record, arg&.to_sym]
|
917
1045
|
else
|
918
1046
|
@ui.puts "unknown command: #{arg}"
|
919
|
-
|
1047
|
+
:retry
|
920
1048
|
end
|
1049
|
+
end
|
921
1050
|
|
922
1051
|
### Thread control
|
923
1052
|
|
@@ -925,7 +1054,7 @@ module DEBUGGER__
|
|
925
1054
|
# * Show all threads.
|
926
1055
|
# * `th[read] <thnum>`
|
927
1056
|
# * Switch thread specified by `<thnum>`.
|
928
|
-
|
1057
|
+
register_command 'th', 'thread', unsafe: false do |arg|
|
929
1058
|
case arg
|
930
1059
|
when nil, 'list', 'l'
|
931
1060
|
thread_list
|
@@ -934,7 +1063,8 @@ module DEBUGGER__
|
|
934
1063
|
else
|
935
1064
|
@ui.puts "unknown thread command: #{arg}"
|
936
1065
|
end
|
937
|
-
|
1066
|
+
:retry
|
1067
|
+
end
|
938
1068
|
|
939
1069
|
### Configuration
|
940
1070
|
# * `config`
|
@@ -947,13 +1077,14 @@ module DEBUGGER__
|
|
947
1077
|
# * Append `<val>` to `<name>` if it is an array.
|
948
1078
|
# * `config unset <name>`
|
949
1079
|
# * Set <name> to default.
|
950
|
-
|
1080
|
+
register_command 'config', unsafe: false do |arg|
|
951
1081
|
config_command arg
|
952
|
-
|
1082
|
+
:retry
|
1083
|
+
end
|
953
1084
|
|
954
1085
|
# * `source <file>`
|
955
1086
|
# * Evaluate lines in `<file>` as debug commands.
|
956
|
-
|
1087
|
+
register_command 'source' do |arg|
|
957
1088
|
if arg
|
958
1089
|
begin
|
959
1090
|
cmds = File.readlines(path = File.expand_path(arg))
|
@@ -964,7 +1095,8 @@ module DEBUGGER__
|
|
964
1095
|
else
|
965
1096
|
show_help 'source'
|
966
1097
|
end
|
967
|
-
|
1098
|
+
:retry
|
1099
|
+
end
|
968
1100
|
|
969
1101
|
# * `open`
|
970
1102
|
# * open debuggee port on UNIX domain socket and wait for attaching.
|
@@ -975,26 +1107,28 @@ module DEBUGGER__
|
|
975
1107
|
# * open debuggee port for VSCode and launch VSCode if available.
|
976
1108
|
# * `open chrome`
|
977
1109
|
# * open debuggee port for Chrome and wait for attaching.
|
978
|
-
|
1110
|
+
register_command 'open' do |arg|
|
979
1111
|
case arg&.downcase
|
980
1112
|
when '', nil
|
981
|
-
|
982
|
-
when 'vscode'
|
983
|
-
repl_open_vscode
|
984
|
-
when /\A(.+):(\d+)\z/
|
985
|
-
repl_open_tcp $1, $2.to_i
|
1113
|
+
::DEBUGGER__.open nonstop: true
|
986
1114
|
when /\A(\d+)z/
|
987
|
-
|
1115
|
+
::DEBUGGER__.open_tcp host: nil, port: $1.to_i, nonstop: true
|
1116
|
+
when /\A(.+):(\d+)\z/
|
1117
|
+
::DEBUGGER__.open_tcp host: $1, port: $2.to_i, nonstop: true
|
988
1118
|
when 'tcp'
|
989
|
-
|
1119
|
+
::DEBUGGER__.open_tcp host: CONFIG[:host], port: (CONFIG[:port] || 0), nonstop: true
|
1120
|
+
when 'vscode'
|
1121
|
+
CONFIG[:open] = 'vscode'
|
1122
|
+
::DEBUGGER__.open nonstop: true
|
990
1123
|
when 'chrome', 'cdp'
|
991
|
-
CONFIG[:
|
992
|
-
|
1124
|
+
CONFIG[:open] = 'chrome'
|
1125
|
+
::DEBUGGER__.open_tcp host: CONFIG[:host], port: (CONFIG[:port] || 0), nonstop: true
|
993
1126
|
else
|
994
1127
|
raise "Unknown arg: #{arg}"
|
995
1128
|
end
|
996
1129
|
|
997
|
-
|
1130
|
+
:retry
|
1131
|
+
end
|
998
1132
|
|
999
1133
|
### Help
|
1000
1134
|
|
@@ -1002,30 +1136,38 @@ module DEBUGGER__
|
|
1002
1136
|
# * Show help for all commands.
|
1003
1137
|
# * `h[elp] <command>`
|
1004
1138
|
# * Show help for the given command.
|
1005
|
-
|
1006
|
-
|
1007
|
-
|
1139
|
+
register_command 'h', 'help', '?', unsafe: false do |arg|
|
1140
|
+
show_help arg
|
1141
|
+
:retry
|
1142
|
+
end
|
1143
|
+
end
|
1144
|
+
|
1145
|
+
def process_command line
|
1146
|
+
if line.empty?
|
1147
|
+
if @repl_prev_line
|
1148
|
+
line = @repl_prev_line
|
1008
1149
|
else
|
1009
|
-
|
1150
|
+
return :retry
|
1010
1151
|
end
|
1011
|
-
|
1152
|
+
else
|
1153
|
+
@repl_prev_line = line
|
1154
|
+
end
|
1012
1155
|
|
1013
|
-
|
1156
|
+
/([^\s]+)(?:\s+(.+))?/ =~ line
|
1157
|
+
cmd_name, cmd_arg = $1, $2
|
1158
|
+
|
1159
|
+
if cmd = @commands[cmd_name]
|
1160
|
+
check_postmortem if !cmd.postmortem
|
1161
|
+
check_unsafe if cmd.unsafe
|
1162
|
+
cancel_auto_continue if cmd.cancel_auto_continue
|
1163
|
+
@repl_prev_line = nil if !cmd.repeat
|
1164
|
+
|
1165
|
+
cmd.block.call(cmd_arg)
|
1014
1166
|
else
|
1015
|
-
@tc << [:eval, :pp, line]
|
1016
|
-
=begin
|
1017
1167
|
@repl_prev_line = nil
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
spell_checker = DidYouMean::SpellChecker.new(dictionary: DEBUGGER__.commands)
|
1022
|
-
correction = spell_checker.correct(line.split(/\s/).first || '')
|
1023
|
-
@ui.puts "Did you mean? #{correction.join(' or ')}" unless correction.empty?
|
1024
|
-
rescue LoadError
|
1025
|
-
# Don't use D
|
1026
|
-
end
|
1027
|
-
return :retry
|
1028
|
-
=end
|
1168
|
+
check_unsafe
|
1169
|
+
|
1170
|
+
request_eval :pp, line
|
1029
1171
|
end
|
1030
1172
|
|
1031
1173
|
rescue Interrupt
|
@@ -1041,44 +1183,27 @@ module DEBUGGER__
|
|
1041
1183
|
return :retry
|
1042
1184
|
end
|
1043
1185
|
|
1044
|
-
def
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
thc.mark_as_management
|
1186
|
+
def step_command type, arg
|
1187
|
+
if type == :until
|
1188
|
+
leave_subsession [:step, type, arg]
|
1189
|
+
return
|
1049
1190
|
end
|
1050
|
-
@tp_thread_begin.enable
|
1051
|
-
end
|
1052
1191
|
|
1053
|
-
def repl_open_tcp host, port, **kw
|
1054
|
-
DEBUGGER__.open_tcp host: host, port: port, nonstop: true, **kw
|
1055
|
-
repl_open_setup
|
1056
|
-
end
|
1057
|
-
|
1058
|
-
def repl_open_unix
|
1059
|
-
DEBUGGER__.open_unix nonstop: true
|
1060
|
-
repl_open_setup
|
1061
|
-
end
|
1062
|
-
|
1063
|
-
def repl_open_vscode
|
1064
|
-
CONFIG[:open_frontend] = 'vscode'
|
1065
|
-
repl_open_unix
|
1066
|
-
end
|
1067
|
-
|
1068
|
-
def step_command type, arg
|
1069
1192
|
case arg
|
1070
1193
|
when nil, /\A\d+\z/
|
1071
1194
|
if type == :in && @tc.recorder&.replaying?
|
1072
|
-
|
1195
|
+
request_tc [:step, type, arg&.to_i]
|
1073
1196
|
else
|
1074
1197
|
leave_subsession [:step, type, arg&.to_i]
|
1075
1198
|
end
|
1076
|
-
when /\
|
1199
|
+
when /\A(back)\z/, /\A(back)\s+(\d+)\z/, /\A(reset)\z/
|
1077
1200
|
if type != :in
|
1078
1201
|
@ui.puts "only `step #{arg}` is supported."
|
1079
1202
|
:retry
|
1080
1203
|
else
|
1081
|
-
|
1204
|
+
type = $1.to_sym
|
1205
|
+
iter = $2&.to_i
|
1206
|
+
request_tc [:step, type, iter]
|
1082
1207
|
end
|
1083
1208
|
else
|
1084
1209
|
@ui.puts "Unknown option: #{arg}"
|
@@ -1088,11 +1213,18 @@ module DEBUGGER__
|
|
1088
1213
|
|
1089
1214
|
def config_show key
|
1090
1215
|
key = key.to_sym
|
1091
|
-
|
1216
|
+
config_detail = CONFIG_SET[key]
|
1217
|
+
|
1218
|
+
if config_detail
|
1092
1219
|
v = CONFIG[key]
|
1093
|
-
kv = "#{key} = #{v.
|
1094
|
-
desc =
|
1095
|
-
|
1220
|
+
kv = "#{key} = #{v.inspect}"
|
1221
|
+
desc = config_detail[1]
|
1222
|
+
|
1223
|
+
if config_default = config_detail[3]
|
1224
|
+
desc += " (default: #{config_default})"
|
1225
|
+
end
|
1226
|
+
|
1227
|
+
line = "%-34s \# %s" % [kv, desc]
|
1096
1228
|
if line.size > SESSION.width
|
1097
1229
|
@ui.puts "\# #{desc}\n#{kv}"
|
1098
1230
|
else
|
@@ -1142,7 +1274,7 @@ module DEBUGGER__
|
|
1142
1274
|
config_set $1, $2, append: true
|
1143
1275
|
|
1144
1276
|
when /\A\s*append\s+(\w+)\s+(.+)\z/
|
1145
|
-
config_set $1, $2
|
1277
|
+
config_set $1, $2, append: true
|
1146
1278
|
|
1147
1279
|
when /\A(\w+)\z/
|
1148
1280
|
config_show $1
|
@@ -1159,16 +1291,50 @@ module DEBUGGER__
|
|
1159
1291
|
end
|
1160
1292
|
end
|
1161
1293
|
|
1162
|
-
def show_help arg
|
1163
|
-
DEBUGGER__.
|
1164
|
-
|
1165
|
-
|
1166
|
-
|
1167
|
-
|
1294
|
+
def show_help arg = nil
|
1295
|
+
instructions = (DEBUGGER__.commands.keys + DEBUGGER__.commands.values).uniq
|
1296
|
+
print_instructions = proc do |desc|
|
1297
|
+
desc.split("\n").each do |line|
|
1298
|
+
next if line.start_with?(" ") # workaround for step back
|
1299
|
+
formatted_line = line.gsub(/[\[\]\*]/, "").strip
|
1300
|
+
instructions.each do |inst|
|
1301
|
+
if formatted_line.start_with?("`#{inst}")
|
1302
|
+
desc.sub!(line, colorize(line, [:CYAN, :BOLD]))
|
1303
|
+
end
|
1304
|
+
end
|
1305
|
+
end
|
1306
|
+
@ui.puts desc
|
1307
|
+
end
|
1308
|
+
|
1309
|
+
print_category = proc do |cat|
|
1310
|
+
@ui.puts "\n"
|
1311
|
+
@ui.puts colorize("### #{cat}", [:GREEN, :BOLD])
|
1312
|
+
@ui.puts "\n"
|
1313
|
+
end
|
1314
|
+
|
1315
|
+
DEBUGGER__.helps.each { |cat, cs|
|
1316
|
+
# categories
|
1317
|
+
if arg.nil?
|
1318
|
+
print_category.call(cat)
|
1319
|
+
else
|
1320
|
+
cs.each { |ws, _|
|
1321
|
+
if ws.include?(arg)
|
1322
|
+
print_category.call(cat)
|
1323
|
+
break
|
1324
|
+
end
|
1325
|
+
}
|
1326
|
+
end
|
1327
|
+
|
1328
|
+
# instructions
|
1329
|
+
cs.each { |ws, desc|
|
1330
|
+
if arg.nil? || ws.include?(arg)
|
1331
|
+
print_instructions.call(desc.dup)
|
1332
|
+
return if arg
|
1168
1333
|
end
|
1169
1334
|
}
|
1170
1335
|
}
|
1171
|
-
|
1336
|
+
|
1337
|
+
@ui.puts "not found: #{arg}" if arg
|
1172
1338
|
end
|
1173
1339
|
|
1174
1340
|
def ask msg, default = 'Y'
|
@@ -1231,12 +1397,13 @@ module DEBUGGER__
|
|
1231
1397
|
|
1232
1398
|
def add_bp bp
|
1233
1399
|
# don't repeat commands that add breakpoints
|
1234
|
-
@repl_prev_line = nil
|
1235
|
-
|
1236
1400
|
if @bps.has_key? bp.key
|
1237
|
-
|
1401
|
+
if bp.duplicable?
|
1402
|
+
bp
|
1403
|
+
else
|
1238
1404
|
@ui.puts "duplicated breakpoint: #{bp}"
|
1239
1405
|
bp.disable
|
1406
|
+
nil
|
1240
1407
|
end
|
1241
1408
|
else
|
1242
1409
|
@bps[bp.key] = bp
|
@@ -1261,7 +1428,7 @@ module DEBUGGER__
|
|
1261
1428
|
|
1262
1429
|
BREAK_KEYWORDS = %w(if: do: pre: path:).freeze
|
1263
1430
|
|
1264
|
-
def parse_break arg
|
1431
|
+
private def parse_break type, arg
|
1265
1432
|
mode = :sig
|
1266
1433
|
expr = Hash.new{|h, k| h[k] = []}
|
1267
1434
|
arg.split(' ').each{|w|
|
@@ -1272,14 +1439,25 @@ module DEBUGGER__
|
|
1272
1439
|
end
|
1273
1440
|
}
|
1274
1441
|
expr.default_proc = nil
|
1275
|
-
expr.transform_values{|v| v.join(' ')}
|
1442
|
+
expr = expr.transform_values{|v| v.join(' ')}
|
1443
|
+
|
1444
|
+
if (path = expr[:path]) && path =~ /\A\/(.*)\/\z/
|
1445
|
+
expr[:path] = Regexp.compile($1)
|
1446
|
+
end
|
1447
|
+
|
1448
|
+
if expr[:do] || expr[:pre]
|
1449
|
+
check_unsafe
|
1450
|
+
expr[:cmd] = [type, expr[:pre], expr[:do]]
|
1451
|
+
end
|
1452
|
+
|
1453
|
+
expr
|
1276
1454
|
end
|
1277
1455
|
|
1278
1456
|
def repl_add_breakpoint arg
|
1279
|
-
expr = parse_break arg.strip
|
1457
|
+
expr = parse_break 'break', arg.strip
|
1280
1458
|
cond = expr[:if]
|
1281
|
-
cmd
|
1282
|
-
path =
|
1459
|
+
cmd = expr[:cmd]
|
1460
|
+
path = expr[:path]
|
1283
1461
|
|
1284
1462
|
case expr[:sig]
|
1285
1463
|
when /\A(\d+)\z/
|
@@ -1287,10 +1465,10 @@ module DEBUGGER__
|
|
1287
1465
|
when /\A(.+)[:\s+](\d+)\z/
|
1288
1466
|
add_line_breakpoint $1, $2.to_i, cond: cond, command: cmd
|
1289
1467
|
when /\A(.+)([\.\#])(.+)\z/
|
1290
|
-
|
1468
|
+
request_tc [:breakpoint, :method, $1, $2, $3, cond, cmd, path]
|
1291
1469
|
return :noretry
|
1292
1470
|
when nil
|
1293
|
-
add_check_breakpoint cond, path
|
1471
|
+
add_check_breakpoint cond, path, cmd
|
1294
1472
|
else
|
1295
1473
|
@ui.puts "Unknown breakpoint format: #{arg}"
|
1296
1474
|
@ui.puts
|
@@ -1299,31 +1477,31 @@ module DEBUGGER__
|
|
1299
1477
|
end
|
1300
1478
|
|
1301
1479
|
def repl_add_catch_breakpoint arg
|
1302
|
-
expr = parse_break arg.strip
|
1480
|
+
expr = parse_break 'catch', arg.strip
|
1303
1481
|
cond = expr[:if]
|
1304
|
-
cmd
|
1305
|
-
path =
|
1482
|
+
cmd = expr[:cmd]
|
1483
|
+
path = expr[:path]
|
1306
1484
|
|
1307
1485
|
bp = CatchBreakpoint.new(expr[:sig], cond: cond, command: cmd, path: path)
|
1308
1486
|
add_bp bp
|
1309
1487
|
end
|
1310
1488
|
|
1311
1489
|
def repl_add_watch_breakpoint arg
|
1312
|
-
expr = parse_break arg.strip
|
1490
|
+
expr = parse_break 'watch', arg.strip
|
1313
1491
|
cond = expr[:if]
|
1314
|
-
cmd
|
1492
|
+
cmd = expr[:cmd]
|
1315
1493
|
path = Regexp.compile(expr[:path]) if expr[:path]
|
1316
1494
|
|
1317
|
-
|
1495
|
+
request_tc [:breakpoint, :watch, expr[:sig], cond, cmd, path]
|
1318
1496
|
end
|
1319
1497
|
|
1320
|
-
def add_catch_breakpoint pat
|
1321
|
-
bp = CatchBreakpoint.new(pat)
|
1498
|
+
def add_catch_breakpoint pat, cond: nil
|
1499
|
+
bp = CatchBreakpoint.new(pat, cond: cond)
|
1322
1500
|
add_bp bp
|
1323
1501
|
end
|
1324
1502
|
|
1325
|
-
def add_check_breakpoint
|
1326
|
-
bp = CheckBreakpoint.new(
|
1503
|
+
def add_check_breakpoint cond, path, command
|
1504
|
+
bp = CheckBreakpoint.new(cond: cond, path: path, command: command)
|
1327
1505
|
add_bp bp
|
1328
1506
|
end
|
1329
1507
|
|
@@ -1336,6 +1514,34 @@ module DEBUGGER__
|
|
1336
1514
|
@ui.puts e.message
|
1337
1515
|
end
|
1338
1516
|
|
1517
|
+
def clear_breakpoints(&condition)
|
1518
|
+
@bps.delete_if do |k, bp|
|
1519
|
+
if condition.call(k, bp)
|
1520
|
+
bp.delete
|
1521
|
+
true
|
1522
|
+
end
|
1523
|
+
end
|
1524
|
+
end
|
1525
|
+
|
1526
|
+
def clear_line_breakpoints path
|
1527
|
+
path = resolve_path(path)
|
1528
|
+
clear_breakpoints do |k, bp|
|
1529
|
+
bp.is_a?(LineBreakpoint) && bp.path_is?(path)
|
1530
|
+
end
|
1531
|
+
rescue Errno::ENOENT
|
1532
|
+
# just ignore
|
1533
|
+
end
|
1534
|
+
|
1535
|
+
def clear_catch_breakpoints *exception_names
|
1536
|
+
clear_breakpoints do |k, bp|
|
1537
|
+
bp.is_a?(CatchBreakpoint) && exception_names.include?(k[1])
|
1538
|
+
end
|
1539
|
+
end
|
1540
|
+
|
1541
|
+
def clear_all_breakpoints
|
1542
|
+
clear_breakpoints{true}
|
1543
|
+
end
|
1544
|
+
|
1339
1545
|
def add_iseq_breakpoint iseq, **kw
|
1340
1546
|
bp = ISeqBreakpoint.new(iseq, [:line], **kw)
|
1341
1547
|
add_bp bp
|
@@ -1344,9 +1550,7 @@ module DEBUGGER__
|
|
1344
1550
|
# tracers
|
1345
1551
|
|
1346
1552
|
def add_tracer tracer
|
1347
|
-
|
1348
|
-
@repl_prev_line = nil
|
1349
|
-
if @tracers.has_key? tracer.key
|
1553
|
+
if @tracers[tracer.key]&.enabled?
|
1350
1554
|
tracer.disable
|
1351
1555
|
@ui.puts "Duplicated tracer: #{tracer}"
|
1352
1556
|
else
|
@@ -1503,42 +1707,72 @@ module DEBUGGER__
|
|
1503
1707
|
end
|
1504
1708
|
|
1505
1709
|
private def enter_subsession
|
1506
|
-
|
1507
|
-
|
1508
|
-
|
1509
|
-
|
1510
|
-
|
1710
|
+
@subsession_id += 1
|
1711
|
+
if !@subsession_stack.empty?
|
1712
|
+
DEBUGGER__.debug{ "Enter subsession (nested #{@subsession_stack.size})" }
|
1713
|
+
else
|
1714
|
+
DEBUGGER__.debug{ "Enter subsession" }
|
1715
|
+
stop_all_threads
|
1716
|
+
@process_group.lock
|
1717
|
+
end
|
1718
|
+
|
1719
|
+
@subsession_stack << true
|
1511
1720
|
end
|
1512
1721
|
|
1513
1722
|
private def leave_subsession type
|
1514
|
-
|
1515
|
-
@
|
1516
|
-
|
1517
|
-
|
1723
|
+
raise '[BUG] leave_subsession: not entered' if @subsession_stack.empty?
|
1724
|
+
@subsession_stack.pop
|
1725
|
+
|
1726
|
+
if @subsession_stack.empty?
|
1727
|
+
DEBUGGER__.debug{ "Leave subsession" }
|
1728
|
+
@process_group.unlock
|
1729
|
+
restart_all_threads
|
1730
|
+
else
|
1731
|
+
DEBUGGER__.debug{ "Leave subsession (nested #{@subsession_stack.size})" }
|
1732
|
+
end
|
1733
|
+
|
1734
|
+
request_tc type if type
|
1518
1735
|
@tc = nil
|
1519
|
-
@subsession = false
|
1520
1736
|
rescue Exception => e
|
1521
|
-
STDERR.puts [e, e.backtrace].
|
1737
|
+
STDERR.puts PP.pp([e, e.backtrace], ''.dup)
|
1522
1738
|
raise
|
1523
1739
|
end
|
1524
1740
|
|
1525
1741
|
def in_subsession?
|
1526
|
-
|
1742
|
+
!@subsession_stack.empty?
|
1527
1743
|
end
|
1528
1744
|
|
1529
1745
|
## event
|
1530
1746
|
|
1531
1747
|
def on_load iseq, src
|
1532
1748
|
DEBUGGER__.info "Load #{iseq.absolute_path || iseq.path}"
|
1533
|
-
@sr.add iseq, src
|
1534
|
-
|
1535
|
-
pending_line_breakpoints = @bps.find_all do |key, bp|
|
1536
|
-
LineBreakpoint === bp && !bp.iseq
|
1537
|
-
end
|
1538
1749
|
|
1539
|
-
|
1540
|
-
|
1541
|
-
|
1750
|
+
file_path, reloaded = @sr.add(iseq, src)
|
1751
|
+
@ui.event :load, file_path, reloaded
|
1752
|
+
|
1753
|
+
# check breakpoints
|
1754
|
+
if file_path
|
1755
|
+
@bps.find_all do |_key, bp|
|
1756
|
+
LineBreakpoint === bp && bp.path_is?(file_path) && (iseq.first_lineno..iseq.last_line).cover?(bp.line)
|
1757
|
+
end.each do |_key, bp|
|
1758
|
+
if !bp.iseq
|
1759
|
+
bp.try_activate iseq
|
1760
|
+
elsif reloaded
|
1761
|
+
@bps.delete bp.key # to allow duplicate
|
1762
|
+
|
1763
|
+
# When we delete a breakpoint from the @bps hash, we also need to deactivate it or else its tracepoint event
|
1764
|
+
# will continue to be enabled and we'll suspend on ghost breakpoints
|
1765
|
+
bp.delete
|
1766
|
+
|
1767
|
+
nbp = LineBreakpoint.copy(bp, iseq)
|
1768
|
+
add_bp nbp
|
1769
|
+
end
|
1770
|
+
end
|
1771
|
+
else # !file_path => file_path is not existing
|
1772
|
+
@bps.find_all do |_key, bp|
|
1773
|
+
LineBreakpoint === bp && !bp.iseq && DEBUGGER__.compare_path(bp.path, (iseq.absolute_path || iseq.path))
|
1774
|
+
end.each do |_key, bp|
|
1775
|
+
bp.try_activate iseq
|
1542
1776
|
end
|
1543
1777
|
end
|
1544
1778
|
end
|
@@ -1563,9 +1797,10 @@ module DEBUGGER__
|
|
1563
1797
|
|
1564
1798
|
def method_added tp
|
1565
1799
|
b = tp.binding
|
1800
|
+
|
1566
1801
|
if var_name = b.local_variables.first
|
1567
1802
|
mid = b.local_variable_get(var_name)
|
1568
|
-
|
1803
|
+
resolved = true
|
1569
1804
|
|
1570
1805
|
@bps.each{|k, bp|
|
1571
1806
|
case bp
|
@@ -1576,15 +1811,57 @@ module DEBUGGER__
|
|
1576
1811
|
end
|
1577
1812
|
end
|
1578
1813
|
|
1579
|
-
|
1814
|
+
resolved = false if !bp.enabled?
|
1580
1815
|
end
|
1581
1816
|
}
|
1582
|
-
|
1583
|
-
|
1817
|
+
|
1818
|
+
if resolved
|
1819
|
+
Session.deactivate_method_added_trackers
|
1820
|
+
end
|
1821
|
+
|
1822
|
+
case mid
|
1823
|
+
when :method_added, :singleton_method_added
|
1824
|
+
Session.create_method_added_tracker(tp.self, mid)
|
1825
|
+
Session.activate_method_added_trackers unless resolved
|
1584
1826
|
end
|
1585
1827
|
end
|
1586
1828
|
end
|
1587
1829
|
|
1830
|
+
class ::Module
|
1831
|
+
undef method_added
|
1832
|
+
def method_added mid; end
|
1833
|
+
end
|
1834
|
+
|
1835
|
+
class ::BasicObject
|
1836
|
+
undef singleton_method_added
|
1837
|
+
def singleton_method_added mid; end
|
1838
|
+
end
|
1839
|
+
|
1840
|
+
def self.create_method_added_tracker mod, method_added_id, method_accessor = :method
|
1841
|
+
m = mod.__send__(method_accessor, method_added_id)
|
1842
|
+
METHOD_ADDED_TRACKERS[m] = TracePoint.new(:call) do |tp|
|
1843
|
+
SESSION.method_added tp
|
1844
|
+
end
|
1845
|
+
end
|
1846
|
+
|
1847
|
+
def self.activate_method_added_trackers
|
1848
|
+
METHOD_ADDED_TRACKERS.each do |m, tp|
|
1849
|
+
tp.enable(target: m) unless tp.enabled?
|
1850
|
+
rescue ArgumentError
|
1851
|
+
DEBUGGER__.warn "Methods defined under #{m.owner} can not track by the debugger."
|
1852
|
+
end
|
1853
|
+
end
|
1854
|
+
|
1855
|
+
def self.deactivate_method_added_trackers
|
1856
|
+
METHOD_ADDED_TRACKERS.each do |m, tp|
|
1857
|
+
tp.disable if tp.enabled?
|
1858
|
+
end
|
1859
|
+
end
|
1860
|
+
|
1861
|
+
METHOD_ADDED_TRACKERS = Hash.new
|
1862
|
+
create_method_added_tracker Module, :method_added, :instance_method
|
1863
|
+
create_method_added_tracker BasicObject, :singleton_method_added, :instance_method
|
1864
|
+
|
1588
1865
|
def width
|
1589
1866
|
@ui.width
|
1590
1867
|
end
|
@@ -1595,6 +1872,18 @@ module DEBUGGER__
|
|
1595
1872
|
end
|
1596
1873
|
end
|
1597
1874
|
|
1875
|
+
def check_unsafe
|
1876
|
+
if @unsafe_context
|
1877
|
+
raise RuntimeError, "#{@repl_prev_line.dump} is not allowed on unsafe context."
|
1878
|
+
end
|
1879
|
+
end
|
1880
|
+
|
1881
|
+
def activate_irb_integration
|
1882
|
+
require_relative "irb_integration"
|
1883
|
+
thc = get_thread_client(@session_server)
|
1884
|
+
thc.activate_irb_integration
|
1885
|
+
end
|
1886
|
+
|
1598
1887
|
def enter_postmortem_session exc
|
1599
1888
|
return unless exc.instance_variable_defined? :@__debugger_postmortem_frames
|
1600
1889
|
|
@@ -1674,6 +1963,17 @@ module DEBUGGER__
|
|
1674
1963
|
end
|
1675
1964
|
end
|
1676
1965
|
|
1966
|
+
def set_no_sigint_hook old, new
|
1967
|
+
return unless old != new
|
1968
|
+
return unless @ui.respond_to? :activate_sigint
|
1969
|
+
|
1970
|
+
if old # no -> yes
|
1971
|
+
@ui.activate_sigint
|
1972
|
+
else
|
1973
|
+
@ui.deactivate_sigint
|
1974
|
+
end
|
1975
|
+
end
|
1976
|
+
|
1677
1977
|
def save_int_trap cmd
|
1678
1978
|
prev, @intercepted_sigint_cmd = @intercepted_sigint_cmd, cmd
|
1679
1979
|
prev
|
@@ -1717,6 +2017,13 @@ module DEBUGGER__
|
|
1717
2017
|
def after_fork_parent
|
1718
2018
|
@ui.after_fork_parent
|
1719
2019
|
end
|
2020
|
+
|
2021
|
+
# experimental API
|
2022
|
+
def extend_feature session: nil, thread_client: nil, ui: nil
|
2023
|
+
Session.include session if session
|
2024
|
+
ThreadClient.include thread_client if thread_client
|
2025
|
+
@ui.extend ui if ui
|
2026
|
+
end
|
1720
2027
|
end
|
1721
2028
|
|
1722
2029
|
class ProcessGroup
|
@@ -1765,9 +2072,11 @@ module DEBUGGER__
|
|
1765
2072
|
|
1766
2073
|
def after_fork child: true
|
1767
2074
|
if child || !@lock_file
|
1768
|
-
@m = Mutex.new
|
1769
|
-
@
|
1770
|
-
|
2075
|
+
@m = Mutex.new unless @m
|
2076
|
+
@m.synchronize do
|
2077
|
+
@lock_level = 0
|
2078
|
+
@lock_file = open(@lock_tempfile.path, 'w')
|
2079
|
+
end
|
1771
2080
|
end
|
1772
2081
|
end
|
1773
2082
|
|
@@ -1776,7 +2085,7 @@ module DEBUGGER__
|
|
1776
2085
|
end
|
1777
2086
|
|
1778
2087
|
def locked?
|
1779
|
-
# DEBUGGER__.
|
2088
|
+
# DEBUGGER__.debug{ "locked? #{@lock_level}" }
|
1780
2089
|
@lock_level > 0
|
1781
2090
|
end
|
1782
2091
|
|
@@ -1860,6 +2169,13 @@ module DEBUGGER__
|
|
1860
2169
|
puts "\nStop by #{args.first}"
|
1861
2170
|
end
|
1862
2171
|
end
|
2172
|
+
|
2173
|
+
def ignore_output_on_suspend?
|
2174
|
+
false
|
2175
|
+
end
|
2176
|
+
|
2177
|
+
def flush
|
2178
|
+
end
|
1863
2179
|
end
|
1864
2180
|
|
1865
2181
|
# manual configuration methods
|
@@ -1876,12 +2192,13 @@ module DEBUGGER__
|
|
1876
2192
|
# nil for -r
|
1877
2193
|
def self.require_location
|
1878
2194
|
locs = caller_locations
|
1879
|
-
dir_prefix = /#{__dir__}/
|
2195
|
+
dir_prefix = /#{Regexp.escape(__dir__)}/
|
1880
2196
|
|
1881
2197
|
locs.each do |loc|
|
1882
2198
|
case loc.absolute_path
|
1883
2199
|
when dir_prefix
|
1884
2200
|
when %r{rubygems/core_ext/kernel_require\.rb}
|
2201
|
+
when %r{bundled_gems\.rb}
|
1885
2202
|
else
|
1886
2203
|
return loc if loc.absolute_path
|
1887
2204
|
end
|
@@ -1894,18 +2211,22 @@ module DEBUGGER__
|
|
1894
2211
|
def self.start nonstop: false, **kw
|
1895
2212
|
CONFIG.set_config(**kw)
|
1896
2213
|
|
1897
|
-
|
1898
|
-
|
1899
|
-
|
2214
|
+
if CONFIG[:open]
|
2215
|
+
open nonstop: nonstop, **kw
|
2216
|
+
else
|
2217
|
+
unless defined? SESSION
|
2218
|
+
require_relative 'local'
|
2219
|
+
initialize_session{ UI_LocalConsole.new }
|
2220
|
+
end
|
2221
|
+
setup_initial_suspend unless nonstop
|
1900
2222
|
end
|
1901
|
-
|
1902
|
-
setup_initial_suspend unless nonstop
|
1903
2223
|
end
|
1904
2224
|
|
1905
2225
|
def self.open host: nil, port: CONFIG[:port], sock_path: nil, sock_dir: nil, nonstop: false, **kw
|
1906
2226
|
CONFIG.set_config(**kw)
|
2227
|
+
require_relative 'server'
|
1907
2228
|
|
1908
|
-
if port || CONFIG[:
|
2229
|
+
if port || CONFIG[:open] == 'chrome' || (!::Addrinfo.respond_to?(:unix))
|
1909
2230
|
open_tcp host: host, port: (port || 0), nonstop: nonstop
|
1910
2231
|
else
|
1911
2232
|
open_unix sock_path: sock_path, sock_dir: sock_dir, nonstop: nonstop
|
@@ -1919,7 +2240,7 @@ module DEBUGGER__
|
|
1919
2240
|
if defined? SESSION
|
1920
2241
|
SESSION.reset_ui UI_TcpServer.new(host: host, port: port)
|
1921
2242
|
else
|
1922
|
-
initialize_session UI_TcpServer.new(host: host, port: port)
|
2243
|
+
initialize_session{ UI_TcpServer.new(host: host, port: port) }
|
1923
2244
|
end
|
1924
2245
|
|
1925
2246
|
setup_initial_suspend unless nonstop
|
@@ -1932,7 +2253,7 @@ module DEBUGGER__
|
|
1932
2253
|
if defined? SESSION
|
1933
2254
|
SESSION.reset_ui UI_UnixDomainServer.new(sock_dir: sock_dir, sock_path: sock_path)
|
1934
2255
|
else
|
1935
|
-
initialize_session UI_UnixDomainServer.new(sock_dir: sock_dir, sock_path: sock_path)
|
2256
|
+
initialize_session{ UI_UnixDomainServer.new(sock_dir: sock_dir, sock_path: sock_path) }
|
1936
2257
|
end
|
1937
2258
|
|
1938
2259
|
setup_initial_suspend unless nonstop
|
@@ -1959,13 +2280,26 @@ module DEBUGGER__
|
|
1959
2280
|
end
|
1960
2281
|
|
1961
2282
|
class << self
|
1962
|
-
define_method :initialize_session do |
|
2283
|
+
define_method :initialize_session do |&init_ui|
|
1963
2284
|
DEBUGGER__.info "Session start (pid: #{Process.pid})"
|
1964
|
-
::DEBUGGER__.const_set(:SESSION, Session.new
|
2285
|
+
::DEBUGGER__.const_set(:SESSION, Session.new)
|
2286
|
+
SESSION.activate init_ui.call
|
1965
2287
|
load_rc
|
1966
2288
|
end
|
1967
2289
|
end
|
1968
2290
|
|
2291
|
+
# Exiting control
|
2292
|
+
|
2293
|
+
class << self
|
2294
|
+
def skip_all
|
2295
|
+
@skip_all = true
|
2296
|
+
end
|
2297
|
+
|
2298
|
+
def skip?
|
2299
|
+
@skip_all
|
2300
|
+
end
|
2301
|
+
end
|
2302
|
+
|
1969
2303
|
def self.load_rc
|
1970
2304
|
[[File.expand_path('~/.rdbgrc'), true],
|
1971
2305
|
[File.expand_path('~/.rdbgrc.rb'), true],
|
@@ -1993,34 +2327,53 @@ module DEBUGGER__
|
|
1993
2327
|
end
|
1994
2328
|
end
|
1995
2329
|
|
1996
|
-
|
1997
|
-
undef method_added
|
1998
|
-
def method_added mid; end
|
1999
|
-
def singleton_method_added mid; end
|
2000
|
-
end
|
2330
|
+
# Inspector
|
2001
2331
|
|
2002
|
-
|
2003
|
-
|
2004
|
-
|
2005
|
-
|
2006
|
-
|
2332
|
+
SHORT_INSPECT_LENGTH = 40
|
2333
|
+
|
2334
|
+
class LimitedPP
|
2335
|
+
def self.pp(obj, max=80)
|
2336
|
+
out = self.new(max)
|
2337
|
+
catch out do
|
2338
|
+
PP.singleline_pp(obj, out)
|
2339
|
+
end
|
2340
|
+
out.buf
|
2007
2341
|
end
|
2008
|
-
end
|
2009
2342
|
|
2010
|
-
|
2343
|
+
attr_reader :buf
|
2011
2344
|
|
2012
|
-
|
2345
|
+
def initialize max
|
2346
|
+
@max = max
|
2347
|
+
@cnt = 0
|
2348
|
+
@buf = String.new
|
2349
|
+
end
|
2013
2350
|
|
2014
|
-
|
2015
|
-
|
2351
|
+
def <<(other)
|
2352
|
+
@buf << other
|
2016
2353
|
|
2017
|
-
|
2018
|
-
|
2354
|
+
if @buf.size >= @max
|
2355
|
+
@buf = @buf[0..@max] + '...'
|
2356
|
+
throw self
|
2357
|
+
end
|
2358
|
+
end
|
2359
|
+
end
|
2360
|
+
|
2361
|
+
def self.safe_inspect obj, max_length: SHORT_INSPECT_LENGTH, short: false
|
2362
|
+
if short
|
2363
|
+
LimitedPP.pp(obj, max_length)
|
2364
|
+
else
|
2365
|
+
obj.inspect
|
2366
|
+
end
|
2367
|
+
rescue NoMethodError => e
|
2368
|
+
klass, oid = M_CLASS.bind_call(obj), M_OBJECT_ID.bind_call(obj)
|
2369
|
+
if obj == (r = e.receiver)
|
2370
|
+
"<\##{klass.name}#{oid} does not have \#inspect>"
|
2019
2371
|
else
|
2020
|
-
|
2372
|
+
rklass, roid = M_CLASS.bind_call(r), M_OBJECT_ID.bind_call(r)
|
2373
|
+
"<\##{klass.name}:#{roid} contains <\##{rklass}:#{roid} and it does not have #inspect>"
|
2021
2374
|
end
|
2022
2375
|
rescue Exception => e
|
2023
|
-
|
2376
|
+
"<#inspect raises #{e.inspect}>"
|
2024
2377
|
end
|
2025
2378
|
|
2026
2379
|
def self.warn msg
|
@@ -2031,18 +2384,28 @@ module DEBUGGER__
|
|
2031
2384
|
log :INFO, msg
|
2032
2385
|
end
|
2033
2386
|
|
2034
|
-
def self.
|
2035
|
-
@logfile = STDERR unless defined? @logfile
|
2036
|
-
|
2387
|
+
def self.check_loglevel level
|
2037
2388
|
lv = LOG_LEVELS[level]
|
2038
|
-
config_lv = LOG_LEVELS[CONFIG[:log_level]
|
2389
|
+
config_lv = LOG_LEVELS[CONFIG[:log_level]]
|
2390
|
+
lv <= config_lv
|
2391
|
+
end
|
2039
2392
|
|
2040
|
-
|
2041
|
-
|
2042
|
-
|
2393
|
+
def self.debug(&b)
|
2394
|
+
if check_loglevel :DEBUG
|
2395
|
+
log :DEBUG, b.call
|
2043
2396
|
end
|
2397
|
+
end
|
2398
|
+
|
2399
|
+
def self.log level, msg
|
2400
|
+
if check_loglevel level
|
2401
|
+
@logfile = STDERR unless defined? @logfile
|
2402
|
+
return if @logfile.closed?
|
2403
|
+
|
2404
|
+
if defined? SESSION
|
2405
|
+
pi = SESSION.process_info
|
2406
|
+
process_info = pi ? "[#{pi}]" : nil
|
2407
|
+
end
|
2044
2408
|
|
2045
|
-
if lv <= config_lv
|
2046
2409
|
if level == :WARN
|
2047
2410
|
# :WARN on debugger is general information
|
2048
2411
|
@logfile.puts "DEBUGGER#{process_info}: #{msg}"
|
@@ -2062,17 +2425,85 @@ module DEBUGGER__
|
|
2062
2425
|
yield
|
2063
2426
|
end
|
2064
2427
|
|
2428
|
+
if File.identical?(__FILE__.upcase, __FILE__.downcase)
|
2429
|
+
# For case insensitive file system (like Windows)
|
2430
|
+
# Note that this check is not enough because case sensitive/insensitive is
|
2431
|
+
# depend on the file system. So this check is only roughly estimation.
|
2432
|
+
|
2433
|
+
def self.compare_path(a, b)
|
2434
|
+
a&.downcase == b&.downcase
|
2435
|
+
end
|
2436
|
+
else
|
2437
|
+
def self.compare_path(a, b)
|
2438
|
+
a == b
|
2439
|
+
end
|
2440
|
+
end
|
2441
|
+
|
2065
2442
|
module ForkInterceptor
|
2066
|
-
|
2067
|
-
|
2443
|
+
if Process.respond_to? :_fork
|
2444
|
+
def _fork
|
2445
|
+
return super unless defined?(SESSION) && SESSION.active?
|
2446
|
+
|
2447
|
+
parent_hook, child_hook = __fork_setup_for_debugger
|
2068
2448
|
|
2069
|
-
|
2070
|
-
|
2071
|
-
|
2449
|
+
super.tap do |pid|
|
2450
|
+
if pid != 0
|
2451
|
+
# after fork: parent
|
2452
|
+
parent_hook.call pid
|
2453
|
+
else
|
2454
|
+
# after fork: child
|
2455
|
+
child_hook.call
|
2456
|
+
end
|
2457
|
+
end
|
2458
|
+
end
|
2459
|
+
else
|
2460
|
+
def fork(&given_block)
|
2461
|
+
return super unless defined?(SESSION) && SESSION.active?
|
2462
|
+
parent_hook, child_hook = __fork_setup_for_debugger
|
2463
|
+
|
2464
|
+
if given_block
|
2465
|
+
new_block = proc {
|
2466
|
+
# after fork: child
|
2467
|
+
child_hook.call
|
2468
|
+
given_block.call
|
2469
|
+
}
|
2470
|
+
super(&new_block).tap{|pid| parent_hook.call(pid)}
|
2072
2471
|
else
|
2073
|
-
|
2472
|
+
super.tap do |pid|
|
2473
|
+
if pid
|
2474
|
+
# after fork: parent
|
2475
|
+
parent_hook.call pid
|
2476
|
+
else
|
2477
|
+
# after fork: child
|
2478
|
+
child_hook.call
|
2479
|
+
end
|
2480
|
+
end
|
2481
|
+
end
|
2482
|
+
end
|
2483
|
+
end
|
2484
|
+
|
2485
|
+
module DaemonInterceptor
|
2486
|
+
def daemon(*args)
|
2487
|
+
return super unless defined?(SESSION) && SESSION.active?
|
2488
|
+
|
2489
|
+
_, child_hook = __fork_setup_for_debugger(:child)
|
2490
|
+
|
2491
|
+
unless SESSION.remote?
|
2492
|
+
DEBUGGER__.warn "Can't debug the code after Process.daemon locally. Use the remote debugging feature."
|
2493
|
+
end
|
2494
|
+
|
2495
|
+
super.tap do
|
2496
|
+
child_hook.call
|
2074
2497
|
end
|
2075
2498
|
end
|
2499
|
+
end
|
2500
|
+
|
2501
|
+
private def __fork_setup_for_debugger fork_mode = nil
|
2502
|
+
fork_mode ||= CONFIG[:fork_mode]
|
2503
|
+
|
2504
|
+
if fork_mode == :both && CONFIG[:parent_on_fork]
|
2505
|
+
fork_mode = :parent
|
2506
|
+
end
|
2076
2507
|
|
2077
2508
|
parent_pid = Process.pid
|
2078
2509
|
|
@@ -2083,19 +2514,19 @@ module DEBUGGER__
|
|
2083
2514
|
# Do nothing
|
2084
2515
|
}
|
2085
2516
|
child_hook = -> {
|
2086
|
-
DEBUGGER__.
|
2517
|
+
DEBUGGER__.info "Detaching after fork from child process #{Process.pid}"
|
2087
2518
|
SESSION.deactivate
|
2088
2519
|
}
|
2089
2520
|
when :child
|
2090
2521
|
SESSION.before_fork false
|
2091
2522
|
|
2092
2523
|
parent_hook = -> child_pid {
|
2093
|
-
DEBUGGER__.
|
2524
|
+
DEBUGGER__.info "Detaching after fork from parent process #{Process.pid}"
|
2094
2525
|
SESSION.after_fork_parent
|
2095
2526
|
SESSION.deactivate
|
2096
2527
|
}
|
2097
2528
|
child_hook = -> {
|
2098
|
-
DEBUGGER__.
|
2529
|
+
DEBUGGER__.info "Attaching after process #{parent_pid} fork to child process #{Process.pid}"
|
2099
2530
|
SESSION.activate on_fork: true
|
2100
2531
|
}
|
2101
2532
|
when :both
|
@@ -2106,38 +2537,29 @@ module DEBUGGER__
|
|
2106
2537
|
SESSION.after_fork_parent
|
2107
2538
|
}
|
2108
2539
|
child_hook = -> {
|
2109
|
-
DEBUGGER__.
|
2540
|
+
DEBUGGER__.info "Attaching after process #{parent_pid} fork to child process #{Process.pid}"
|
2110
2541
|
SESSION.process_group.after_fork child: true
|
2111
2542
|
SESSION.activate on_fork: true
|
2112
2543
|
}
|
2113
2544
|
end
|
2114
2545
|
|
2115
|
-
|
2116
|
-
new_block = proc {
|
2117
|
-
# after fork: child
|
2118
|
-
child_hook.call
|
2119
|
-
given_block.call
|
2120
|
-
}
|
2121
|
-
pid = super(&new_block)
|
2122
|
-
parent_hook.call(pid)
|
2123
|
-
pid
|
2124
|
-
else
|
2125
|
-
if pid = super
|
2126
|
-
# after fork: parent
|
2127
|
-
parent_hook.call pid
|
2128
|
-
else
|
2129
|
-
# after fork: child
|
2130
|
-
child_hook.call
|
2131
|
-
end
|
2132
|
-
|
2133
|
-
pid
|
2134
|
-
end
|
2546
|
+
return parent_hook, child_hook
|
2135
2547
|
end
|
2136
2548
|
end
|
2137
2549
|
|
2138
2550
|
module TrapInterceptor
|
2139
2551
|
def trap sig, *command, &command_proc
|
2140
|
-
|
2552
|
+
sym =
|
2553
|
+
case sig
|
2554
|
+
when String
|
2555
|
+
sig.to_sym
|
2556
|
+
when Integer
|
2557
|
+
Signal.signame(sig)&.to_sym
|
2558
|
+
else
|
2559
|
+
sig
|
2560
|
+
end
|
2561
|
+
|
2562
|
+
case sym
|
2141
2563
|
when :INT, :SIGINT
|
2142
2564
|
if defined?(SESSION) && SESSION.active? && SESSION.intercept_trap_sigint?
|
2143
2565
|
return SESSION.save_int_trap(command.empty? ? command_proc : command.first)
|
@@ -2148,28 +2570,48 @@ module DEBUGGER__
|
|
2148
2570
|
end
|
2149
2571
|
end
|
2150
2572
|
|
2151
|
-
if
|
2573
|
+
if Process.respond_to? :_fork
|
2574
|
+
module ::Process
|
2575
|
+
class << self
|
2576
|
+
prepend ForkInterceptor
|
2577
|
+
prepend DaemonInterceptor
|
2578
|
+
end
|
2579
|
+
end
|
2580
|
+
|
2581
|
+
# trap
|
2152
2582
|
module ::Kernel
|
2153
|
-
prepend ForkInterceptor
|
2154
2583
|
prepend TrapInterceptor
|
2155
2584
|
end
|
2585
|
+
module ::Signal
|
2586
|
+
class << self
|
2587
|
+
prepend TrapInterceptor
|
2588
|
+
end
|
2589
|
+
end
|
2156
2590
|
else
|
2157
|
-
|
2158
|
-
|
2159
|
-
|
2591
|
+
if RUBY_VERSION >= '3.0.0'
|
2592
|
+
module ::Kernel
|
2593
|
+
prepend ForkInterceptor
|
2594
|
+
prepend TrapInterceptor
|
2595
|
+
end
|
2596
|
+
else
|
2597
|
+
class ::Object
|
2598
|
+
include ForkInterceptor
|
2599
|
+
include TrapInterceptor
|
2600
|
+
end
|
2160
2601
|
end
|
2161
|
-
end
|
2162
2602
|
|
2163
|
-
|
2164
|
-
|
2165
|
-
|
2166
|
-
|
2603
|
+
module ::Kernel
|
2604
|
+
class << self
|
2605
|
+
prepend ForkInterceptor
|
2606
|
+
prepend TrapInterceptor
|
2607
|
+
end
|
2167
2608
|
end
|
2168
|
-
end
|
2169
2609
|
|
2170
|
-
|
2171
|
-
|
2172
|
-
|
2610
|
+
module ::Process
|
2611
|
+
class << self
|
2612
|
+
prepend ForkInterceptor
|
2613
|
+
prepend DaemonInterceptor
|
2614
|
+
end
|
2173
2615
|
end
|
2174
2616
|
end
|
2175
2617
|
|
@@ -2185,10 +2627,17 @@ module Kernel
|
|
2185
2627
|
return if !defined?(::DEBUGGER__::SESSION) || !::DEBUGGER__::SESSION.active?
|
2186
2628
|
|
2187
2629
|
if pre || (do_expr = binding.local_variable_get(:do))
|
2188
|
-
cmds = ['
|
2630
|
+
cmds = ['#debugger', pre, do_expr]
|
2189
2631
|
end
|
2190
2632
|
|
2191
|
-
|
2633
|
+
if ::DEBUGGER__::SESSION.in_subsession?
|
2634
|
+
if cmds
|
2635
|
+
commands = [*cmds[1], *cmds[2]].map{|c| c.split(';;').join("\n")}
|
2636
|
+
::DEBUGGER__::SESSION.add_preset_commands cmds[0], commands, kick: false, continue: false
|
2637
|
+
end
|
2638
|
+
else
|
2639
|
+
loc = caller_locations(up_level, 1).first; ::DEBUGGER__.add_line_breakpoint loc.path, loc.lineno + 1, oneshot: true, command: cmds
|
2640
|
+
end
|
2192
2641
|
self
|
2193
2642
|
end
|
2194
2643
|
|
@@ -2199,3 +2648,12 @@ class Binding
|
|
2199
2648
|
alias break debugger
|
2200
2649
|
alias b debugger
|
2201
2650
|
end
|
2651
|
+
|
2652
|
+
# for Ruby 2.6 compatibility
|
2653
|
+
unless method(:p).unbind.respond_to? :bind_call
|
2654
|
+
class UnboundMethod
|
2655
|
+
def bind_call(obj, *args)
|
2656
|
+
self.bind(obj).call(*args)
|
2657
|
+
end
|
2658
|
+
end
|
2659
|
+
end
|