debug 1.3.4 → 1.6.0
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/CONTRIBUTING.md +234 -9
- data/Gemfile +1 -0
- data/README.md +81 -31
- data/Rakefile +28 -10
- data/debug.gemspec +7 -5
- data/exe/rdbg +7 -3
- data/ext/debug/debug.c +80 -15
- data/ext/debug/extconf.rb +22 -0
- data/lib/debug/breakpoint.rb +141 -67
- data/lib/debug/client.rb +77 -20
- data/lib/debug/color.rb +29 -19
- data/lib/debug/config.rb +61 -27
- data/lib/debug/console.rb +59 -18
- data/lib/debug/frame_info.rb +41 -40
- data/lib/debug/local.rb +1 -1
- data/lib/debug/prelude.rb +2 -2
- data/lib/debug/server.rb +136 -103
- data/lib/debug/server_cdp.rb +880 -162
- data/lib/debug/server_dap.rb +445 -164
- data/lib/debug/session.rb +540 -269
- data/lib/debug/source_repository.rb +103 -52
- data/lib/debug/thread_client.rb +306 -138
- data/lib/debug/tracer.rb +8 -13
- data/lib/debug/version.rb +1 -1
- data/misc/README.md.erb +44 -16
- metadata +6 -15
- 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/workflows/ruby.yml +0 -34
- data/.gitignore +0 -12
- data/bin/console +0 -14
- data/bin/gentest +0 -22
- data/bin/setup +0 -8
- data/lib/debug/bp.vim +0 -68
data/lib/debug/thread_client.rb
CHANGED
@@ -6,11 +6,32 @@ require 'pp'
|
|
6
6
|
require_relative 'color'
|
7
7
|
|
8
8
|
module DEBUGGER__
|
9
|
+
M_INSTANCE_VARIABLES = method(:instance_variables).unbind
|
10
|
+
M_INSTANCE_VARIABLE_GET = method(:instance_variable_get).unbind
|
11
|
+
M_CLASS = method(:class).unbind
|
12
|
+
M_SINGLETON_CLASS = method(:singleton_class).unbind
|
13
|
+
M_KIND_OF_P = method(:kind_of?).unbind
|
14
|
+
M_RESPOND_TO_P = method(:respond_to?).unbind
|
15
|
+
M_METHOD = method(:method).unbind
|
16
|
+
M_OBJECT_ID = method(:object_id).unbind
|
17
|
+
|
9
18
|
module SkipPathHelper
|
10
19
|
def skip_path?(path)
|
20
|
+
!path ||
|
21
|
+
CONFIG.skip? ||
|
22
|
+
ThreadClient.current.management? ||
|
23
|
+
skip_internal_path?(path) ||
|
24
|
+
skip_config_skip_path?(path)
|
25
|
+
end
|
26
|
+
|
27
|
+
def skip_config_skip_path?(path)
|
11
28
|
(skip_paths = CONFIG[:skip_path]) && skip_paths.any?{|skip_path| path.match?(skip_path)}
|
12
29
|
end
|
13
30
|
|
31
|
+
def skip_internal_path?(path)
|
32
|
+
path.start_with?(__dir__) || path.start_with?('<internal:')
|
33
|
+
end
|
34
|
+
|
14
35
|
def skip_location?(loc)
|
15
36
|
loc_path = loc.absolute_path || "!eval:#{loc.path}"
|
16
37
|
skip_path?(loc_path)
|
@@ -22,7 +43,7 @@ module DEBUGGER__
|
|
22
43
|
if thc = Thread.current[:DEBUGGER__ThreadClient]
|
23
44
|
thc
|
24
45
|
else
|
25
|
-
thc = SESSION.
|
46
|
+
thc = SESSION.get_thread_client
|
26
47
|
Thread.current[:DEBUGGER__ThreadClient] = thc
|
27
48
|
end
|
28
49
|
end
|
@@ -30,7 +51,11 @@ module DEBUGGER__
|
|
30
51
|
include Color
|
31
52
|
include SkipPathHelper
|
32
53
|
|
33
|
-
attr_reader :
|
54
|
+
attr_reader :thread, :id, :recorder, :check_bp_fulfillment_map
|
55
|
+
|
56
|
+
def location
|
57
|
+
current_frame&.location
|
58
|
+
end
|
34
59
|
|
35
60
|
def assemble_arguments(args)
|
36
61
|
args.map do |arg|
|
@@ -42,7 +67,8 @@ module DEBUGGER__
|
|
42
67
|
call_identifier_str =
|
43
68
|
case frame.frame_type
|
44
69
|
when :block
|
45
|
-
level, block_loc
|
70
|
+
level, block_loc = frame.block_identifier
|
71
|
+
args = frame.parameters_info
|
46
72
|
|
47
73
|
if !args.empty?
|
48
74
|
args_str = " {|#{assemble_arguments(args)}|}"
|
@@ -50,7 +76,8 @@ module DEBUGGER__
|
|
50
76
|
|
51
77
|
"#{colorize_blue("block")}#{args_str} in #{colorize_blue(block_loc + level)}"
|
52
78
|
when :method
|
53
|
-
ci
|
79
|
+
ci = frame.method_identifier
|
80
|
+
args = frame.parameters_info
|
54
81
|
|
55
82
|
if !args.empty?
|
56
83
|
args_str = "(#{assemble_arguments(args)})"
|
@@ -67,7 +94,7 @@ module DEBUGGER__
|
|
67
94
|
result = "#{call_identifier_str} at #{location_str}"
|
68
95
|
|
69
96
|
if return_str = frame.return_str
|
70
|
-
result += " #=> #{colorize_magenta(
|
97
|
+
result += " #=> #{colorize_magenta(return_str)}"
|
71
98
|
end
|
72
99
|
|
73
100
|
result
|
@@ -84,8 +111,12 @@ module DEBUGGER__
|
|
84
111
|
@output = []
|
85
112
|
@frame_formatter = method(:default_frame_formatter)
|
86
113
|
@var_map = {} # { thread_local_var_id => obj } for DAP
|
114
|
+
@obj_map = {} # { object_id => obj } for CDP
|
87
115
|
@recorder = nil
|
88
116
|
@mode = :waiting
|
117
|
+
@current_frame_index = 0
|
118
|
+
# every thread should maintain its own CheckBreakpoint fulfillment state
|
119
|
+
@check_bp_fulfillment_map = {} # { check_bp => boolean }
|
89
120
|
set_mode :running
|
90
121
|
thr.instance_variable_set(:@__thread_client_id, id)
|
91
122
|
|
@@ -100,13 +131,14 @@ module DEBUGGER__
|
|
100
131
|
@is_management
|
101
132
|
end
|
102
133
|
|
103
|
-
def
|
134
|
+
def mark_as_management
|
104
135
|
@is_management = true
|
105
136
|
end
|
106
137
|
|
107
138
|
def set_mode mode
|
139
|
+
debug_mode(@mode, mode)
|
108
140
|
# STDERR.puts "#{@mode} => #{mode} @ #{caller.inspect}"
|
109
|
-
#pp caller
|
141
|
+
# pp caller
|
110
142
|
|
111
143
|
# mode transition check
|
112
144
|
case mode
|
@@ -148,14 +180,7 @@ module DEBUGGER__
|
|
148
180
|
end
|
149
181
|
|
150
182
|
def to_s
|
151
|
-
|
152
|
-
|
153
|
-
if loc
|
154
|
-
str = "(#{@thread.name || @thread.status})@#{loc}"
|
155
|
-
else
|
156
|
-
str = "(#{@thread.name || @thread.status})@#{@thread.to_s}"
|
157
|
-
end
|
158
|
-
|
183
|
+
str = "(#{@thread.name || @thread.status})@#{current_frame&.location || @thread.to_s}"
|
159
184
|
str += " (not under control)" unless self.waiting?
|
160
185
|
str
|
161
186
|
end
|
@@ -175,6 +200,7 @@ module DEBUGGER__
|
|
175
200
|
end
|
176
201
|
|
177
202
|
def << req
|
203
|
+
debug_cmd(req)
|
178
204
|
@q_cmd << req
|
179
205
|
end
|
180
206
|
|
@@ -185,6 +211,7 @@ module DEBUGGER__
|
|
185
211
|
end
|
186
212
|
|
187
213
|
def event! ev, *args
|
214
|
+
debug_event(ev, args)
|
188
215
|
@q_evt << [self, @output, ev, generate_info, *args]
|
189
216
|
@output = []
|
190
217
|
end
|
@@ -228,8 +255,9 @@ module DEBUGGER__
|
|
228
255
|
suspend :pause
|
229
256
|
end
|
230
257
|
|
231
|
-
def suspend event, tp = nil, bp: nil, sig: nil, postmortem_frames: nil, replay_frames: nil
|
258
|
+
def suspend event, tp = nil, bp: nil, sig: nil, postmortem_frames: nil, replay_frames: nil, postmortem_exc: nil
|
232
259
|
return if management?
|
260
|
+
debug_suspend(event)
|
233
261
|
|
234
262
|
@current_frame_index = 0
|
235
263
|
|
@@ -245,7 +273,6 @@ module DEBUGGER__
|
|
245
273
|
|
246
274
|
cf = @target_frames.first
|
247
275
|
if cf
|
248
|
-
@location = cf.location
|
249
276
|
case event
|
250
277
|
when :return, :b_return, :c_return
|
251
278
|
cf.has_return_value = true
|
@@ -256,11 +283,16 @@ module DEBUGGER__
|
|
256
283
|
cf.has_raised_exception = true
|
257
284
|
cf.raised_exception = bp.last_exc
|
258
285
|
end
|
286
|
+
|
287
|
+
if postmortem_exc
|
288
|
+
cf.has_raised_exception = true
|
289
|
+
cf.raised_exception = postmortem_exc
|
290
|
+
end
|
259
291
|
end
|
260
292
|
|
261
293
|
if event != :pause
|
262
|
-
show_src
|
263
|
-
show_frames CONFIG[:show_frames]
|
294
|
+
show_src
|
295
|
+
show_frames CONFIG[:show_frames]
|
264
296
|
|
265
297
|
set_mode :waiting
|
266
298
|
|
@@ -292,15 +324,15 @@ module DEBUGGER__
|
|
292
324
|
SUPPORT_TARGET_THREAD = false
|
293
325
|
end
|
294
326
|
|
295
|
-
def step_tp iter
|
327
|
+
def step_tp iter, events = [:line, :b_return, :return]
|
296
328
|
@step_tp.disable if @step_tp
|
297
329
|
|
298
330
|
thread = Thread.current
|
299
331
|
|
300
332
|
if SUPPORT_TARGET_THREAD
|
301
|
-
@step_tp = TracePoint.new(
|
333
|
+
@step_tp = TracePoint.new(*events){|tp|
|
302
334
|
next if SESSION.break_at? tp.path, tp.lineno
|
303
|
-
next if !yield
|
335
|
+
next if !yield(tp.event)
|
304
336
|
next if tp.path.start_with?(__dir__)
|
305
337
|
next if tp.path.start_with?('<internal:trace_point>')
|
306
338
|
next unless File.exist?(tp.path) if CONFIG[:skip_nosrc]
|
@@ -313,10 +345,10 @@ module DEBUGGER__
|
|
313
345
|
}
|
314
346
|
@step_tp.enable(target_thread: thread)
|
315
347
|
else
|
316
|
-
@step_tp = TracePoint.new(
|
348
|
+
@step_tp = TracePoint.new(*events){|tp|
|
317
349
|
next if thread != Thread.current
|
318
350
|
next if SESSION.break_at? tp.path, tp.lineno
|
319
|
-
next if !yield
|
351
|
+
next if !yield(tp.event)
|
320
352
|
next if tp.path.start_with?(__dir__)
|
321
353
|
next if tp.path.start_with?('<internal:trace_point>')
|
322
354
|
next unless File.exist?(tp.path) if CONFIG[:skip_nosrc]
|
@@ -333,123 +365,191 @@ module DEBUGGER__
|
|
333
365
|
|
334
366
|
## cmd helpers
|
335
367
|
|
336
|
-
|
337
|
-
|
338
|
-
|
368
|
+
if TracePoint.respond_to? :allow_reentry
|
369
|
+
def tp_allow_reentry
|
370
|
+
TracePoint.allow_reentry do
|
371
|
+
yield
|
372
|
+
end
|
373
|
+
rescue RuntimeError => e
|
374
|
+
# on the postmortem mode, it is not stopped in TracePoint
|
375
|
+
if e.message == 'No need to allow reentrance.'
|
376
|
+
yield
|
377
|
+
else
|
378
|
+
raise
|
379
|
+
end
|
380
|
+
end
|
381
|
+
else
|
382
|
+
def tp_allow_reentry
|
383
|
+
yield
|
384
|
+
end
|
339
385
|
end
|
340
386
|
|
387
|
+
def frame_eval_core src, b
|
388
|
+
saved_target_frames = @target_frames
|
389
|
+
saved_current_frame_index = @current_frame_index
|
390
|
+
|
391
|
+
if b
|
392
|
+
f, _l = b.source_location
|
393
|
+
|
394
|
+
tp_allow_reentry do
|
395
|
+
b.eval(src, "(rdbg)/#{f}")
|
396
|
+
end
|
397
|
+
else
|
398
|
+
frame_self = current_frame.self
|
399
|
+
|
400
|
+
tp_allow_reentry do
|
401
|
+
frame_self.instance_eval(src)
|
402
|
+
end
|
403
|
+
end
|
404
|
+
ensure
|
405
|
+
@target_frames = saved_target_frames
|
406
|
+
@current_frame_index = saved_current_frame_index
|
407
|
+
end
|
408
|
+
|
409
|
+
SPECIAL_LOCAL_VARS = [
|
410
|
+
[:raised_exception, "_raised"],
|
411
|
+
[:return_value, "_return"],
|
412
|
+
]
|
413
|
+
|
341
414
|
def frame_eval src, re_raise: false
|
342
|
-
|
343
|
-
|
415
|
+
@success_last_eval = false
|
416
|
+
|
417
|
+
b = current_frame.eval_binding
|
344
418
|
|
345
|
-
|
419
|
+
special_local_variables current_frame do |name, var|
|
420
|
+
b.local_variable_set(name, var) if /\%/ !~ name
|
421
|
+
end
|
346
422
|
|
347
|
-
|
348
|
-
f, _l = b.source_location
|
349
|
-
b.eval(src, "(rdbg)/#{f}")
|
350
|
-
else
|
351
|
-
frame_self = current_frame.self
|
352
|
-
instance_eval_for_cmethod(frame_self, src)
|
353
|
-
end
|
354
|
-
@success_last_eval = true
|
355
|
-
result
|
423
|
+
result = frame_eval_core(src, b)
|
356
424
|
|
357
|
-
|
358
|
-
|
425
|
+
@success_last_eval = true
|
426
|
+
result
|
359
427
|
|
360
|
-
|
428
|
+
rescue SystemExit
|
429
|
+
raise
|
430
|
+
rescue Exception => e
|
431
|
+
return yield(e) if block_given?
|
361
432
|
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
433
|
+
puts "eval error: #{e}"
|
434
|
+
|
435
|
+
e.backtrace_locations&.each do |loc|
|
436
|
+
break if loc.path == __FILE__
|
437
|
+
puts " #{loc}"
|
367
438
|
end
|
439
|
+
raise if re_raise
|
368
440
|
end
|
369
441
|
|
370
|
-
def
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
"#{cur}#{line} #{e}"
|
384
|
-
end
|
442
|
+
def get_src(frame,
|
443
|
+
max_lines:,
|
444
|
+
start_line: nil,
|
445
|
+
end_line: nil,
|
446
|
+
dir: +1)
|
447
|
+
if file_lines = frame.file_lines
|
448
|
+
frame_line = frame.location.lineno - 1
|
449
|
+
|
450
|
+
lines = file_lines.map.with_index do |e, i|
|
451
|
+
cur = i == frame_line ? '=>' : ' '
|
452
|
+
line = colorize_dim('%4d|' % (i+1))
|
453
|
+
"#{cur}#{line} #{e}"
|
454
|
+
end
|
385
455
|
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
else
|
391
|
-
end_line = frame.show_line - max_lines
|
392
|
-
start_line = [end_line - max_lines, 0].max
|
393
|
-
end
|
456
|
+
unless start_line
|
457
|
+
if frame.show_line
|
458
|
+
if dir > 0
|
459
|
+
start_line = frame.show_line
|
394
460
|
else
|
395
|
-
|
461
|
+
end_line = frame.show_line - max_lines
|
462
|
+
start_line = [end_line - max_lines, 0].max
|
396
463
|
end
|
464
|
+
else
|
465
|
+
start_line = [frame_line - max_lines/2, 0].max
|
397
466
|
end
|
467
|
+
end
|
398
468
|
|
399
|
-
|
400
|
-
|
401
|
-
|
469
|
+
unless end_line
|
470
|
+
end_line = [start_line + max_lines, lines.size].min
|
471
|
+
end
|
472
|
+
|
473
|
+
if start_line != end_line && max_lines
|
474
|
+
[start_line, end_line, lines]
|
475
|
+
end
|
476
|
+
else # no file lines
|
477
|
+
nil
|
478
|
+
end
|
479
|
+
rescue Exception => e
|
480
|
+
p e
|
481
|
+
pp e.backtrace
|
482
|
+
exit!
|
483
|
+
end
|
402
484
|
|
485
|
+
def show_src(frame_index: @current_frame_index, update_line: false, max_lines: CONFIG[:show_src_lines], **options)
|
486
|
+
if frame = get_frame(frame_index)
|
487
|
+
start_line, end_line, lines = *get_src(frame, max_lines: max_lines, **options)
|
488
|
+
|
489
|
+
if start_line
|
403
490
|
if update_line
|
404
491
|
frame.show_line = end_line
|
405
492
|
end
|
406
493
|
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
end
|
411
|
-
else # no file lines
|
494
|
+
puts "[#{start_line+1}, #{end_line}] in #{frame.pretty_path}" if !update_line && max_lines != 1
|
495
|
+
puts lines[start_line...end_line]
|
496
|
+
else
|
412
497
|
puts "# No sourcefile available for #{frame.path}"
|
413
498
|
end
|
414
499
|
end
|
415
|
-
rescue Exception => e
|
416
|
-
p e
|
417
|
-
pp e.backtrace
|
418
|
-
exit!
|
419
500
|
end
|
420
501
|
|
421
502
|
def current_frame
|
503
|
+
get_frame(@current_frame_index)
|
504
|
+
end
|
505
|
+
|
506
|
+
def get_frame(index)
|
422
507
|
if @target_frames
|
423
|
-
@target_frames[
|
508
|
+
@target_frames[index]
|
424
509
|
else
|
425
510
|
nil
|
426
511
|
end
|
427
512
|
end
|
428
513
|
|
429
|
-
|
514
|
+
def collect_locals(frame)
|
515
|
+
locals = []
|
430
516
|
|
431
|
-
|
432
|
-
|
433
|
-
puts_variable_info '%self', s, pat
|
434
|
-
end
|
435
|
-
if current_frame&.has_return_value
|
436
|
-
puts_variable_info '%return', current_frame.return_value, pat
|
517
|
+
if s = frame&.self
|
518
|
+
locals << ["%self", s]
|
437
519
|
end
|
438
|
-
|
439
|
-
|
520
|
+
special_local_variables frame do |name, val|
|
521
|
+
locals << [name, val]
|
440
522
|
end
|
441
523
|
|
442
|
-
if vars =
|
524
|
+
if vars = frame&.local_variables
|
443
525
|
vars.each{|var, val|
|
444
|
-
|
526
|
+
locals << [var, val]
|
445
527
|
}
|
446
528
|
end
|
529
|
+
|
530
|
+
locals
|
531
|
+
end
|
532
|
+
|
533
|
+
## cmd: show
|
534
|
+
|
535
|
+
def special_local_variables frame
|
536
|
+
SPECIAL_LOCAL_VARS.each do |mid, name|
|
537
|
+
next unless frame&.send("has_#{mid}")
|
538
|
+
name = name.sub('_', '%') if frame.eval_binding.local_variable_defined?(name)
|
539
|
+
yield name, frame.send(mid)
|
540
|
+
end
|
541
|
+
end
|
542
|
+
|
543
|
+
def show_locals pat
|
544
|
+
collect_locals(current_frame).each do |var, val|
|
545
|
+
puts_variable_info(var, val, pat)
|
546
|
+
end
|
447
547
|
end
|
448
548
|
|
449
549
|
def show_ivars pat
|
450
550
|
if s = current_frame&.self
|
451
|
-
s.
|
452
|
-
value =
|
551
|
+
M_INSTANCE_VARIABLES.bind_call(s).sort.each{|iv|
|
552
|
+
value = M_INSTANCE_VARIABLE_GET.bind_call(s, iv)
|
453
553
|
puts_variable_info iv, value, pat
|
454
554
|
}
|
455
555
|
end
|
@@ -458,10 +558,10 @@ module DEBUGGER__
|
|
458
558
|
def show_consts pat, only_self: false
|
459
559
|
if s = current_frame&.self
|
460
560
|
cs = {}
|
461
|
-
if s
|
561
|
+
if M_KIND_OF_P.bind_call(s, Module)
|
462
562
|
cs[s] = :self
|
463
563
|
else
|
464
|
-
s = s
|
564
|
+
s = M_CLASS.bind_call(s)
|
465
565
|
cs[s] = :self unless only_self
|
466
566
|
end
|
467
567
|
|
@@ -499,7 +599,7 @@ module DEBUGGER__
|
|
499
599
|
return if pat && pat !~ label
|
500
600
|
|
501
601
|
begin
|
502
|
-
inspected = obj
|
602
|
+
inspected = DEBUGGER__.safe_inspect(obj)
|
503
603
|
rescue Exception => e
|
504
604
|
inspected = e.inspect
|
505
605
|
end
|
@@ -508,28 +608,32 @@ module DEBUGGER__
|
|
508
608
|
w = SESSION::width
|
509
609
|
|
510
610
|
if mono_info.length >= w
|
511
|
-
|
611
|
+
maximum_value_width = w - "#{label} = ".length
|
612
|
+
valstr = truncate(inspected, width: maximum_value_width)
|
512
613
|
else
|
513
614
|
valstr = colored_inspect(obj, width: 2 ** 30)
|
514
615
|
valstr = inspected if valstr.lines.size > 1
|
515
|
-
info = "#{colorize_cyan(label)} = #{valstr}"
|
516
616
|
end
|
517
617
|
|
618
|
+
info = "#{colorize_cyan(label)} = #{valstr}"
|
619
|
+
|
518
620
|
puts info
|
519
621
|
end
|
520
622
|
|
521
623
|
def truncate(string, width:)
|
522
|
-
|
523
|
-
|
524
|
-
|
624
|
+
if string.start_with?("#<")
|
625
|
+
string[0 .. (width-5)] + '...>'
|
626
|
+
else
|
627
|
+
string[0 .. (width-4)] + '...'
|
628
|
+
end
|
525
629
|
end
|
526
630
|
|
527
631
|
### cmd: show edit
|
528
632
|
|
529
633
|
def show_by_editor path = nil
|
530
634
|
unless path
|
531
|
-
if
|
532
|
-
path =
|
635
|
+
if current_frame
|
636
|
+
path = current_frame.path
|
533
637
|
else
|
534
638
|
return # can't get path
|
535
639
|
end
|
@@ -554,15 +658,11 @@ module DEBUGGER__
|
|
554
658
|
if @target_frames && (max ||= @target_frames.size) > 0
|
555
659
|
frames = []
|
556
660
|
@target_frames.each_with_index{|f, i|
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
when Regexp
|
563
|
-
f.location_str.match?(pat)
|
564
|
-
end
|
565
|
-
}
|
661
|
+
# we need to use FrameInfo#matchable_location because #location_str is for display
|
662
|
+
# and it may change based on configs (e.g. use_short_path)
|
663
|
+
next if pattern && !(f.name.match?(pattern) || f.matchable_location.match?(pattern))
|
664
|
+
# avoid using skip_path? because we still want to display internal frames
|
665
|
+
next if skip_config_skip_path?(f.matchable_location)
|
566
666
|
|
567
667
|
frames << [i, f]
|
568
668
|
}
|
@@ -599,18 +699,25 @@ module DEBUGGER__
|
|
599
699
|
o = Output.new(@output)
|
600
700
|
|
601
701
|
locals = current_frame&.local_variables
|
602
|
-
klass = (obj.class == Class || obj.class == Module ? obj : obj.class)
|
603
702
|
|
604
|
-
|
703
|
+
klass = M_CLASS.bind_call(obj)
|
704
|
+
klass = obj if Class == klass || Module == klass
|
705
|
+
|
706
|
+
o.dump("constants", obj.constants) if M_RESPOND_TO_P.bind_call(obj, :constants)
|
605
707
|
outline_method(o, klass, obj)
|
606
|
-
o.dump("instance variables", obj
|
708
|
+
o.dump("instance variables", M_INSTANCE_VARIABLES.bind_call(obj))
|
607
709
|
o.dump("class variables", klass.class_variables)
|
608
710
|
o.dump("locals", locals.keys) if locals
|
609
711
|
end
|
610
712
|
end
|
611
713
|
|
612
714
|
def outline_method(o, klass, obj)
|
613
|
-
|
715
|
+
begin
|
716
|
+
singleton_class = M_SINGLETON_CLASS.bind_call(obj)
|
717
|
+
rescue TypeError
|
718
|
+
singleton_class = nil
|
719
|
+
end
|
720
|
+
|
614
721
|
maps = class_method_map((singleton_class || klass).ancestors)
|
615
722
|
maps.each do |mod, methods|
|
616
723
|
name = mod == singleton_class ? "#{klass}.methods" : "#{mod}#methods"
|
@@ -630,22 +737,54 @@ module DEBUGGER__
|
|
630
737
|
|
631
738
|
## cmd: breakpoint
|
632
739
|
|
740
|
+
# TODO: support non-ASCII Constant name
|
741
|
+
def constant_name? name
|
742
|
+
case name
|
743
|
+
when /\A::\b/
|
744
|
+
constant_name? $~.post_match
|
745
|
+
when /\A[A-Z]\w*/
|
746
|
+
post = $~.post_match
|
747
|
+
if post.empty?
|
748
|
+
true
|
749
|
+
else
|
750
|
+
constant_name? post
|
751
|
+
end
|
752
|
+
else
|
753
|
+
false
|
754
|
+
end
|
755
|
+
end
|
756
|
+
|
633
757
|
def make_breakpoint args
|
634
758
|
case args.first
|
635
759
|
when :method
|
636
|
-
klass_name, op, method_name, cond, cmd = args[1..]
|
637
|
-
bp = MethodBreakpoint.new(current_frame.
|
760
|
+
klass_name, op, method_name, cond, cmd, path = args[1..]
|
761
|
+
bp = MethodBreakpoint.new(current_frame.eval_binding, klass_name, op, method_name, cond: cond, command: cmd, path: path)
|
638
762
|
begin
|
639
763
|
bp.enable
|
764
|
+
rescue NameError => e
|
765
|
+
if bp.klass
|
766
|
+
puts "Unknown method name: \"#{e.name}\""
|
767
|
+
else
|
768
|
+
# klass_name can not be evaluated
|
769
|
+
if constant_name? klass_name
|
770
|
+
puts "Unknown constant name: \"#{e.name}\""
|
771
|
+
else
|
772
|
+
# only Class name is allowed
|
773
|
+
puts "Not a constant name: \"#{klass_name}\""
|
774
|
+
bp = nil
|
775
|
+
end
|
776
|
+
end
|
777
|
+
|
778
|
+
Session.activate_method_added_trackers if bp
|
640
779
|
rescue Exception => e
|
641
|
-
puts e.
|
642
|
-
|
780
|
+
puts e.inspect
|
781
|
+
bp = nil
|
643
782
|
end
|
644
783
|
|
645
784
|
bp
|
646
785
|
when :watch
|
647
|
-
ivar, object, result = args[1..]
|
648
|
-
WatchIVarBreakpoint.new(ivar, object, result)
|
786
|
+
ivar, object, result, cond, command, path = args[1..]
|
787
|
+
WatchIVarBreakpoint.new(ivar, object, result, cond: cond, command: command, path: path)
|
649
788
|
else
|
650
789
|
raise "unknown breakpoint: #{args}"
|
651
790
|
end
|
@@ -732,10 +871,11 @@ module DEBUGGER__
|
|
732
871
|
break
|
733
872
|
|
734
873
|
when :finish
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
874
|
+
finish_frames = (iter || 1) - 1
|
875
|
+
goal_depth = @target_frames.first.frame_depth - finish_frames
|
876
|
+
|
877
|
+
step_tp nil, [:return, :b_return] do
|
878
|
+
DEBUGGER__.frame_depth - 3 <= goal_depth ? true : false
|
739
879
|
end
|
740
880
|
break
|
741
881
|
|
@@ -834,6 +974,7 @@ module DEBUGGER__
|
|
834
974
|
else
|
835
975
|
raise "unsupported frame operation: #{arg.inspect}"
|
836
976
|
end
|
977
|
+
|
837
978
|
event! :result, nil
|
838
979
|
|
839
980
|
when :show
|
@@ -887,7 +1028,7 @@ module DEBUGGER__
|
|
887
1028
|
bp = make_breakpoint args
|
888
1029
|
event! :result, :method_breakpoint, bp
|
889
1030
|
when :watch
|
890
|
-
ivar = args[1]
|
1031
|
+
ivar, cond, command, path = args[1..]
|
891
1032
|
result = frame_eval(ivar)
|
892
1033
|
|
893
1034
|
if @success_last_eval
|
@@ -897,7 +1038,7 @@ module DEBUGGER__
|
|
897
1038
|
else
|
898
1039
|
current_frame.self
|
899
1040
|
end
|
900
|
-
bp = make_breakpoint [:watch, ivar, object, result]
|
1041
|
+
bp = make_breakpoint [:watch, ivar, object, result, cond, command, path]
|
901
1042
|
event! :result, :watch_breakpoint, bp
|
902
1043
|
else
|
903
1044
|
event! :result, nil
|
@@ -910,7 +1051,7 @@ module DEBUGGER__
|
|
910
1051
|
begin
|
911
1052
|
obj = frame_eval args.shift, re_raise: true
|
912
1053
|
opt = args.shift
|
913
|
-
obj_inspect = obj
|
1054
|
+
obj_inspect = DEBUGGER__.safe_inspect(obj)
|
914
1055
|
|
915
1056
|
width = 50
|
916
1057
|
|
@@ -918,7 +1059,7 @@ module DEBUGGER__
|
|
918
1059
|
obj_inspect = truncate(obj_inspect, width: width)
|
919
1060
|
end
|
920
1061
|
|
921
|
-
event! :result, :trace_pass, obj
|
1062
|
+
event! :result, :trace_pass, M_OBJECT_ID.bind_call(obj), obj_inspect, opt
|
922
1063
|
rescue => e
|
923
1064
|
puts e.message
|
924
1065
|
event! :result, nil
|
@@ -935,8 +1076,8 @@ module DEBUGGER__
|
|
935
1076
|
# enable recording
|
936
1077
|
if !@recorder
|
937
1078
|
@recorder = Recorder.new
|
938
|
-
@recorder.enable
|
939
1079
|
end
|
1080
|
+
@recorder.enable
|
940
1081
|
when :off
|
941
1082
|
if @recorder&.enabled?
|
942
1083
|
@recorder.disable
|
@@ -968,6 +1109,33 @@ module DEBUGGER__
|
|
968
1109
|
raise
|
969
1110
|
end
|
970
1111
|
|
1112
|
+
def debug_event(ev, args)
|
1113
|
+
DEBUGGER__.debug{
|
1114
|
+
args = args.map { |arg| DEBUGGER__.safe_inspect(arg) }
|
1115
|
+
"#{inspect} sends Event { type: #{ev.inspect}, args: #{args} } to Session"
|
1116
|
+
}
|
1117
|
+
end
|
1118
|
+
|
1119
|
+
def debug_mode(old_mode, new_mode)
|
1120
|
+
DEBUGGER__.debug{
|
1121
|
+
"#{inspect} changes mode (#{old_mode} -> #{new_mode})"
|
1122
|
+
}
|
1123
|
+
end
|
1124
|
+
|
1125
|
+
def debug_cmd(cmds)
|
1126
|
+
DEBUGGER__.debug{
|
1127
|
+
cmd, *args = *cmds
|
1128
|
+
args = args.map { |arg| DEBUGGER__.safe_inspect(arg) }
|
1129
|
+
"#{inspect} receives Cmd { type: #{cmd.inspect}, args: #{args} } from Session"
|
1130
|
+
}
|
1131
|
+
end
|
1132
|
+
|
1133
|
+
def debug_suspend(event)
|
1134
|
+
DEBUGGER__.debug{
|
1135
|
+
"#{inspect} is suspended for #{event.inspect}"
|
1136
|
+
}
|
1137
|
+
end
|
1138
|
+
|
971
1139
|
class Recorder
|
972
1140
|
attr_reader :log, :index
|
973
1141
|
attr_accessor :backup_frames
|
@@ -982,8 +1150,8 @@ module DEBUGGER__
|
|
982
1150
|
|
983
1151
|
@tp_recorder ||= TracePoint.new(:line){|tp|
|
984
1152
|
next unless Thread.current == thread
|
985
|
-
|
986
|
-
next if tp.path
|
1153
|
+
# can't be replaced by skip_location
|
1154
|
+
next if skip_internal_path?(tp.path)
|
987
1155
|
loc = caller_locations(1, 1).first
|
988
1156
|
next if skip_location?(loc)
|
989
1157
|
|
@@ -1067,7 +1235,7 @@ module DEBUGGER__
|
|
1067
1235
|
end
|
1068
1236
|
end
|
1069
1237
|
|
1070
|
-
#
|
1238
|
+
# copied from irb
|
1071
1239
|
class Output
|
1072
1240
|
include Color
|
1073
1241
|
|