debug 1.6.1 → 1.9.1
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 +22 -10
- data/Gemfile +0 -0
- data/LICENSE.txt +0 -0
- data/README.md +84 -55
- data/Rakefile +8 -3
- data/TODO.md +8 -8
- data/debug.gemspec +3 -3
- data/exe/rdbg +19 -4
- data/ext/debug/debug.c +33 -5
- data/ext/debug/extconf.rb +1 -0
- data/ext/debug/iseq_collector.c +2 -0
- data/lib/debug/abbrev_command.rb +77 -0
- data/lib/debug/breakpoint.rb +15 -11
- data/lib/debug/client.rb +26 -8
- data/lib/debug/color.rb +0 -0
- data/lib/debug/config.rb +69 -23
- data/lib/debug/console.rb +8 -29
- data/lib/debug/dap_custom/traceInspector.rb +336 -0
- data/lib/debug/frame_info.rb +9 -0
- data/lib/debug/irb_integration.rb +27 -0
- data/lib/debug/local.rb +16 -10
- data/lib/debug/open.rb +0 -0
- data/lib/debug/open_nonstop.rb +0 -0
- data/lib/debug/prelude.rb +2 -1
- data/lib/debug/server.rb +32 -27
- data/lib/debug/server_cdp.rb +360 -155
- data/lib/debug/server_dap.rb +330 -197
- data/lib/debug/session.rb +494 -258
- data/lib/debug/source_repository.rb +41 -21
- data/lib/debug/start.rb +1 -1
- data/lib/debug/thread_client.rb +241 -82
- data/lib/debug/tracer.rb +4 -5
- data/lib/debug/version.rb +1 -1
- data/lib/debug.rb +7 -2
- data/misc/README.md.erb +50 -44
- metadata +13 -10
@@ -6,7 +6,23 @@ module DEBUGGER__
|
|
6
6
|
class SourceRepository
|
7
7
|
include Color
|
8
8
|
|
9
|
-
|
9
|
+
def file_src iseq
|
10
|
+
if (path = (iseq.absolute_path || iseq.path)) && File.exist?(path)
|
11
|
+
File.readlines(path, chomp: true)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def get iseq
|
16
|
+
return unless iseq
|
17
|
+
|
18
|
+
if CONFIG[:show_evaledsrc]
|
19
|
+
orig_src(iseq) || file_src(iseq)
|
20
|
+
else
|
21
|
+
file_src(iseq) || orig_src(iseq)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
if defined?(RubyVM.keep_script_lines)
|
10
26
|
# Ruby 3.1 and later
|
11
27
|
RubyVM.keep_script_lines = true
|
12
28
|
require 'objspace'
|
@@ -18,7 +34,7 @@ module DEBUGGER__
|
|
18
34
|
end
|
19
35
|
|
20
36
|
def add iseq, src
|
21
|
-
#
|
37
|
+
# only manage loaded file names
|
22
38
|
if (path = (iseq.absolute_path || iseq.path)) && File.exist?(path)
|
23
39
|
if @loaded_file_map.has_key? path
|
24
40
|
return path, true # reloaded
|
@@ -29,17 +45,13 @@ module DEBUGGER__
|
|
29
45
|
end
|
30
46
|
end
|
31
47
|
|
32
|
-
def
|
33
|
-
|
34
|
-
|
35
|
-
if
|
36
|
-
lines
|
48
|
+
def orig_src iseq
|
49
|
+
lines = iseq.script_lines&.map(&:chomp)
|
50
|
+
line = iseq.first_line
|
51
|
+
if line > 1
|
52
|
+
[*([''] * (line - 1)), *lines]
|
37
53
|
else
|
38
|
-
|
39
|
-
File.readlines(path, chomp: true)
|
40
|
-
else
|
41
|
-
nil
|
42
|
-
end
|
54
|
+
lines
|
43
55
|
end
|
44
56
|
end
|
45
57
|
|
@@ -63,15 +75,22 @@ module DEBUGGER__
|
|
63
75
|
end
|
64
76
|
|
65
77
|
def add iseq, src
|
66
|
-
|
78
|
+
path = (iseq.absolute_path || iseq.path)
|
79
|
+
|
80
|
+
if path && File.exist?(path)
|
67
81
|
reloaded = @files.has_key? path
|
68
|
-
|
69
|
-
|
70
|
-
|
82
|
+
|
83
|
+
if src
|
84
|
+
add_iseq iseq, src
|
85
|
+
return path, reloaded
|
86
|
+
else
|
87
|
+
add_path path
|
88
|
+
return path, reloaded
|
89
|
+
end
|
90
|
+
else
|
71
91
|
add_iseq iseq, src
|
92
|
+
nil
|
72
93
|
end
|
73
|
-
|
74
|
-
nil
|
75
94
|
end
|
76
95
|
|
77
96
|
private def all_iseq iseq, rs = []
|
@@ -87,7 +106,8 @@ module DEBUGGER__
|
|
87
106
|
if line > 1
|
88
107
|
src = ("\n" * (line - 1)) + src
|
89
108
|
end
|
90
|
-
|
109
|
+
|
110
|
+
si = SrcInfo.new(src.each_line.map{|l| l.chomp})
|
91
111
|
all_iseq(iseq).each{|e|
|
92
112
|
e.instance_variable_set(:@debugger_si, si)
|
93
113
|
e.freeze
|
@@ -102,7 +122,7 @@ module DEBUGGER__
|
|
102
122
|
|
103
123
|
private def get_si iseq
|
104
124
|
return unless iseq
|
105
|
-
|
125
|
+
|
106
126
|
if iseq.instance_variable_defined?(:@debugger_si)
|
107
127
|
iseq.instance_variable_get(:@debugger_si)
|
108
128
|
elsif @files.has_key?(path = (iseq.absolute_path || iseq.path))
|
@@ -112,7 +132,7 @@ module DEBUGGER__
|
|
112
132
|
end
|
113
133
|
end
|
114
134
|
|
115
|
-
def
|
135
|
+
def orig_src iseq
|
116
136
|
if si = get_si(iseq)
|
117
137
|
si.src
|
118
138
|
end
|
data/lib/debug/start.rb
CHANGED
data/lib/debug/thread_client.rb
CHANGED
@@ -5,6 +5,10 @@ require 'pp'
|
|
5
5
|
|
6
6
|
require_relative 'color'
|
7
7
|
|
8
|
+
class ::Thread
|
9
|
+
attr_accessor :debug_thread_client
|
10
|
+
end
|
11
|
+
|
8
12
|
module DEBUGGER__
|
9
13
|
M_INSTANCE_VARIABLES = method(:instance_variables).unbind
|
10
14
|
M_INSTANCE_VARIABLE_GET = method(:instance_variable_get).unbind
|
@@ -14,11 +18,12 @@ module DEBUGGER__
|
|
14
18
|
M_RESPOND_TO_P = method(:respond_to?).unbind
|
15
19
|
M_METHOD = method(:method).unbind
|
16
20
|
M_OBJECT_ID = method(:object_id).unbind
|
21
|
+
M_NAME = method(:name).unbind
|
17
22
|
|
18
23
|
module SkipPathHelper
|
19
24
|
def skip_path?(path)
|
20
25
|
!path ||
|
21
|
-
|
26
|
+
DEBUGGER__.skip? ||
|
22
27
|
ThreadClient.current.management? ||
|
23
28
|
skip_internal_path?(path) ||
|
24
29
|
skip_config_skip_path?(path)
|
@@ -29,7 +34,7 @@ module DEBUGGER__
|
|
29
34
|
end
|
30
35
|
|
31
36
|
def skip_internal_path?(path)
|
32
|
-
path.start_with?(__dir__) || path.start_with?('<internal:')
|
37
|
+
path.start_with?(__dir__) || path.delete_prefix('!eval:').start_with?('<internal:')
|
33
38
|
end
|
34
39
|
|
35
40
|
def skip_location?(loc)
|
@@ -38,18 +43,21 @@ module DEBUGGER__
|
|
38
43
|
end
|
39
44
|
end
|
40
45
|
|
46
|
+
module GlobalVariablesHelper
|
47
|
+
SKIP_GLOBAL_LIST = %i[$= $KCODE $-K $SAFE].freeze
|
48
|
+
def safe_global_variables
|
49
|
+
global_variables.reject{|name| SKIP_GLOBAL_LIST.include? name }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
41
53
|
class ThreadClient
|
42
54
|
def self.current
|
43
|
-
|
44
|
-
thc
|
45
|
-
else
|
46
|
-
thc = SESSION.get_thread_client
|
47
|
-
Thread.current[:DEBUGGER__ThreadClient] = thc
|
48
|
-
end
|
55
|
+
Thread.current.debug_thread_client ||= SESSION.get_thread_client
|
49
56
|
end
|
50
57
|
|
51
58
|
include Color
|
52
59
|
include SkipPathHelper
|
60
|
+
include GlobalVariablesHelper
|
53
61
|
|
54
62
|
attr_reader :thread, :id, :recorder, :check_bp_fulfillment_map
|
55
63
|
|
@@ -291,8 +299,10 @@ module DEBUGGER__
|
|
291
299
|
end
|
292
300
|
|
293
301
|
if event != :pause
|
294
|
-
|
295
|
-
|
302
|
+
unless bp&.skip_src
|
303
|
+
show_src
|
304
|
+
show_frames CONFIG[:show_frames]
|
305
|
+
end
|
296
306
|
|
297
307
|
set_mode :waiting
|
298
308
|
|
@@ -328,11 +338,15 @@ module DEBUGGER__
|
|
328
338
|
@step_tp.disable if @step_tp
|
329
339
|
|
330
340
|
thread = Thread.current
|
341
|
+
subsession_id = SESSION.subsession_id
|
331
342
|
|
332
343
|
if SUPPORT_TARGET_THREAD
|
333
344
|
@step_tp = TracePoint.new(*events){|tp|
|
334
|
-
|
335
|
-
|
345
|
+
if SESSION.stop_stepping? tp.path, tp.lineno, subsession_id
|
346
|
+
tp.disable
|
347
|
+
next
|
348
|
+
end
|
349
|
+
next if !yield(tp)
|
336
350
|
next if tp.path.start_with?(__dir__)
|
337
351
|
next if tp.path.start_with?('<internal:trace_point>')
|
338
352
|
next unless File.exist?(tp.path) if CONFIG[:skip_nosrc]
|
@@ -347,8 +361,11 @@ module DEBUGGER__
|
|
347
361
|
else
|
348
362
|
@step_tp = TracePoint.new(*events){|tp|
|
349
363
|
next if thread != Thread.current
|
350
|
-
|
351
|
-
|
364
|
+
if SESSION.stop_stepping? tp.path, tp.lineno, subsession_id
|
365
|
+
tp.disable
|
366
|
+
next
|
367
|
+
end
|
368
|
+
next if !yield(tp)
|
352
369
|
next if tp.path.start_with?(__dir__)
|
353
370
|
next if tp.path.start_with?('<internal:trace_point>')
|
354
371
|
next unless File.exist?(tp.path) if CONFIG[:skip_nosrc]
|
@@ -384,15 +401,19 @@ module DEBUGGER__
|
|
384
401
|
end
|
385
402
|
end
|
386
403
|
|
387
|
-
def frame_eval_core src, b
|
404
|
+
def frame_eval_core src, b, binding_location: false
|
388
405
|
saved_target_frames = @target_frames
|
389
406
|
saved_current_frame_index = @current_frame_index
|
390
407
|
|
391
408
|
if b
|
392
|
-
|
409
|
+
file, lineno = b.source_location
|
393
410
|
|
394
411
|
tp_allow_reentry do
|
395
|
-
|
412
|
+
if binding_location
|
413
|
+
b.eval(src, file, lineno)
|
414
|
+
else
|
415
|
+
b.eval(src, "(rdbg)/#{file}")
|
416
|
+
end
|
396
417
|
end
|
397
418
|
else
|
398
419
|
frame_self = current_frame.self
|
@@ -411,16 +432,16 @@ module DEBUGGER__
|
|
411
432
|
[:return_value, "_return"],
|
412
433
|
]
|
413
434
|
|
414
|
-
def frame_eval src, re_raise: false
|
435
|
+
def frame_eval src, re_raise: false, binding_location: false
|
415
436
|
@success_last_eval = false
|
416
437
|
|
417
|
-
b = current_frame
|
438
|
+
b = current_frame&.eval_binding || TOPLEVEL_BINDING
|
418
439
|
|
419
440
|
special_local_variables current_frame do |name, var|
|
420
441
|
b.local_variable_set(name, var) if /\%/ !~ name
|
421
442
|
end
|
422
443
|
|
423
|
-
result = frame_eval_core(src, b)
|
444
|
+
result = frame_eval_core(src, b, binding_location: binding_location)
|
424
445
|
|
425
446
|
@success_last_eval = true
|
426
447
|
result
|
@@ -447,10 +468,14 @@ module DEBUGGER__
|
|
447
468
|
if file_lines = frame.file_lines
|
448
469
|
frame_line = frame.location.lineno - 1
|
449
470
|
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
471
|
+
if CONFIG[:no_lineno]
|
472
|
+
lines = file_lines
|
473
|
+
else
|
474
|
+
lines = file_lines.map.with_index do |e, i|
|
475
|
+
cur = i == frame_line ? '=>' : ' '
|
476
|
+
line = colorize_dim('%4d|' % (i+1))
|
477
|
+
"#{cur}#{line} #{e}"
|
478
|
+
end
|
454
479
|
end
|
455
480
|
|
456
481
|
unless start_line
|
@@ -482,19 +507,28 @@ module DEBUGGER__
|
|
482
507
|
exit!
|
483
508
|
end
|
484
509
|
|
485
|
-
def show_src(frame_index: @current_frame_index, update_line: false, max_lines: CONFIG[:show_src_lines], **options)
|
510
|
+
def show_src(frame_index: @current_frame_index, update_line: false, ignore_show_line: false, max_lines: CONFIG[:show_src_lines], **options)
|
486
511
|
if frame = get_frame(frame_index)
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
frame.show_line = end_line
|
512
|
+
begin
|
513
|
+
if ignore_show_line
|
514
|
+
prev_show_line = frame.show_line
|
515
|
+
frame.show_line = nil
|
492
516
|
end
|
493
517
|
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
518
|
+
start_line, end_line, lines = *get_src(frame, max_lines: max_lines, **options)
|
519
|
+
|
520
|
+
if start_line
|
521
|
+
if update_line
|
522
|
+
frame.show_line = end_line
|
523
|
+
end
|
524
|
+
|
525
|
+
puts "[#{start_line+1}, #{end_line}] in #{frame.pretty_path}" if !update_line && max_lines != 1
|
526
|
+
puts lines[start_line...end_line]
|
527
|
+
else
|
528
|
+
puts "# No sourcefile available for #{frame.path}"
|
529
|
+
end
|
530
|
+
ensure
|
531
|
+
frame.show_line = prev_show_line if prev_show_line
|
498
532
|
end
|
499
533
|
end
|
500
534
|
end
|
@@ -546,48 +580,81 @@ module DEBUGGER__
|
|
546
580
|
end
|
547
581
|
end
|
548
582
|
|
549
|
-
def show_ivars pat
|
550
|
-
if
|
551
|
-
|
552
|
-
|
583
|
+
def show_ivars pat, expr = nil
|
584
|
+
if expr && !expr.empty?
|
585
|
+
_self = frame_eval(expr);
|
586
|
+
elsif _self = current_frame&.self
|
587
|
+
else
|
588
|
+
_self = nil
|
589
|
+
end
|
590
|
+
|
591
|
+
if _self
|
592
|
+
M_INSTANCE_VARIABLES.bind_call(_self).sort.each{|iv|
|
593
|
+
value = M_INSTANCE_VARIABLE_GET.bind_call(_self, iv)
|
553
594
|
puts_variable_info iv, value, pat
|
554
595
|
}
|
555
596
|
end
|
556
597
|
end
|
557
598
|
|
558
|
-
def
|
559
|
-
|
599
|
+
def iter_consts c, names = {}
|
600
|
+
c.constants(false).sort.each{|name|
|
601
|
+
next if names.has_key? name
|
602
|
+
names[name] = nil
|
603
|
+
begin
|
604
|
+
value = c.const_get(name)
|
605
|
+
rescue Exception => e
|
606
|
+
value = e
|
607
|
+
end
|
608
|
+
yield name, value
|
609
|
+
}
|
610
|
+
end
|
611
|
+
|
612
|
+
def get_consts expr = nil, only_self: false, &block
|
613
|
+
if expr && !expr.empty?
|
614
|
+
begin
|
615
|
+
_self = frame_eval(expr, re_raise: true)
|
616
|
+
rescue Exception
|
617
|
+
# ignore
|
618
|
+
else
|
619
|
+
if M_KIND_OF_P.bind_call(_self, Module)
|
620
|
+
iter_consts _self, &block
|
621
|
+
return
|
622
|
+
else
|
623
|
+
puts "#{_self.inspect} (by #{expr}) is not a Module."
|
624
|
+
end
|
625
|
+
end
|
626
|
+
elsif _self = current_frame&.self
|
560
627
|
cs = {}
|
561
|
-
if M_KIND_OF_P.bind_call(
|
562
|
-
cs[
|
628
|
+
if M_KIND_OF_P.bind_call(_self, Module)
|
629
|
+
cs[_self] = :self
|
563
630
|
else
|
564
|
-
|
565
|
-
cs[
|
631
|
+
_self = M_CLASS.bind_call(_self)
|
632
|
+
cs[_self] = :self unless only_self
|
566
633
|
end
|
567
634
|
|
568
635
|
unless only_self
|
569
|
-
|
636
|
+
_self.ancestors.each{|c| break if c == Object; cs[c] = :ancestors}
|
570
637
|
if b = current_frame&.binding
|
571
|
-
b.eval('Module.nesting').each{|c| cs[c] = :nesting unless cs.has_key? c}
|
638
|
+
b.eval('::Module.nesting').each{|c| cs[c] = :nesting unless cs.has_key? c}
|
572
639
|
end
|
573
640
|
end
|
574
641
|
|
575
642
|
names = {}
|
576
643
|
|
577
644
|
cs.each{|c, _|
|
578
|
-
c
|
579
|
-
next if names.has_key? name
|
580
|
-
names[name] = nil
|
581
|
-
value = c.const_get(name)
|
582
|
-
puts_variable_info name, value, pat
|
583
|
-
}
|
645
|
+
iter_consts c, names, &block
|
584
646
|
}
|
585
647
|
end
|
586
648
|
end
|
587
649
|
|
588
|
-
|
650
|
+
def show_consts pat, expr = nil, only_self: false
|
651
|
+
get_consts expr, only_self: only_self do |name, value|
|
652
|
+
puts_variable_info name, value, pat
|
653
|
+
end
|
654
|
+
end
|
655
|
+
|
589
656
|
def show_globals pat
|
590
|
-
|
657
|
+
safe_global_variables.sort.each{|name|
|
591
658
|
next if SKIP_GLOBAL_LIST.include? name
|
592
659
|
|
593
660
|
value = eval(name.to_s)
|
@@ -643,7 +710,8 @@ module DEBUGGER__
|
|
643
710
|
if editor = (ENV['RUBY_DEBUG_EDITOR'] || ENV['EDITOR'])
|
644
711
|
puts "command: #{editor}"
|
645
712
|
puts " path: #{path}"
|
646
|
-
|
713
|
+
require 'shellwords'
|
714
|
+
system(*Shellwords.split(editor), path)
|
647
715
|
else
|
648
716
|
puts "can not find editor setting: ENV['RUBY_DEBUG_EDITOR'] or ENV['EDITOR']"
|
649
717
|
end
|
@@ -758,7 +826,7 @@ module DEBUGGER__
|
|
758
826
|
case args.first
|
759
827
|
when :method
|
760
828
|
klass_name, op, method_name, cond, cmd, path = args[1..]
|
761
|
-
bp = MethodBreakpoint.new(current_frame
|
829
|
+
bp = MethodBreakpoint.new(current_frame&.eval_binding || TOPLEVEL_BINDING, klass_name, op, method_name, cond: cond, command: cmd, path: path)
|
762
830
|
begin
|
763
831
|
bp.enable
|
764
832
|
rescue NameError => e
|
@@ -793,8 +861,18 @@ module DEBUGGER__
|
|
793
861
|
class SuspendReplay < Exception
|
794
862
|
end
|
795
863
|
|
864
|
+
if ::Fiber.respond_to?(:blocking)
|
865
|
+
private def fiber_blocking
|
866
|
+
::Fiber.blocking{yield}
|
867
|
+
end
|
868
|
+
else
|
869
|
+
private def fiber_blocking
|
870
|
+
yield
|
871
|
+
end
|
872
|
+
end
|
873
|
+
|
796
874
|
def wait_next_action
|
797
|
-
wait_next_action_
|
875
|
+
fiber_blocking{wait_next_action_}
|
798
876
|
rescue SuspendReplay
|
799
877
|
replay_suspend
|
800
878
|
end
|
@@ -814,6 +892,7 @@ module DEBUGGER__
|
|
814
892
|
set_mode :waiting if !waiting?
|
815
893
|
cmds = @q_cmd.pop
|
816
894
|
# pp [self, cmds: cmds]
|
895
|
+
|
817
896
|
break unless cmds
|
818
897
|
ensure
|
819
898
|
set_mode :running
|
@@ -831,8 +910,9 @@ module DEBUGGER__
|
|
831
910
|
|
832
911
|
case step_type
|
833
912
|
when :in
|
913
|
+
iter = iter || 1
|
834
914
|
if @recorder&.replaying?
|
835
|
-
@recorder.step_forward
|
915
|
+
@recorder.step_forward iter
|
836
916
|
raise SuspendReplay
|
837
917
|
else
|
838
918
|
step_tp iter do
|
@@ -845,6 +925,7 @@ module DEBUGGER__
|
|
845
925
|
frame = @target_frames.first
|
846
926
|
path = frame.location.absolute_path || "!eval:#{frame.path}"
|
847
927
|
line = frame.location.lineno
|
928
|
+
label = frame.location.base_label
|
848
929
|
|
849
930
|
if frame.iseq
|
850
931
|
frame.iseq.traceable_lines_norec(lines = {})
|
@@ -856,35 +937,89 @@ module DEBUGGER__
|
|
856
937
|
|
857
938
|
depth = @target_frames.first.frame_depth
|
858
939
|
|
859
|
-
step_tp iter do
|
940
|
+
step_tp iter do |tp|
|
860
941
|
loc = caller_locations(2, 1).first
|
861
942
|
loc_path = loc.absolute_path || "!eval:#{loc.path}"
|
943
|
+
loc_label = loc.base_label
|
944
|
+
loc_depth = DEBUGGER__.frame_depth - 3
|
862
945
|
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
946
|
+
case
|
947
|
+
when loc_depth == depth && loc_label == label
|
948
|
+
true
|
949
|
+
when loc_depth < depth
|
950
|
+
# lower stack depth
|
951
|
+
true
|
952
|
+
when (next_line &&
|
953
|
+
loc_path == path &&
|
954
|
+
(loc_lineno = loc.lineno) > line &&
|
955
|
+
loc_lineno <= next_line)
|
956
|
+
# different frame (maybe block) but the line is before next_line
|
957
|
+
true
|
958
|
+
end
|
870
959
|
end
|
871
960
|
break
|
872
961
|
|
873
962
|
when :finish
|
874
963
|
finish_frames = (iter || 1) - 1
|
875
|
-
|
964
|
+
frame = @target_frames.first
|
965
|
+
goal_depth = frame.frame_depth - finish_frames - (frame.has_return_value ? 1 : 0)
|
876
966
|
|
877
967
|
step_tp nil, [:return, :b_return] do
|
878
968
|
DEBUGGER__.frame_depth - 3 <= goal_depth ? true : false
|
879
969
|
end
|
880
970
|
break
|
881
971
|
|
972
|
+
when :until
|
973
|
+
location = iter&.strip
|
974
|
+
frame = @target_frames.first
|
975
|
+
depth = frame.frame_depth - (frame.has_return_value ? 1 : 0)
|
976
|
+
target_location_label = frame.location.base_label
|
977
|
+
|
978
|
+
case location
|
979
|
+
when nil, /\A(?:(.+):)?(\d+)\z/
|
980
|
+
no_loc = !location
|
981
|
+
file = $1 || frame.location.path
|
982
|
+
line = ($2 || frame.location.lineno + 1).to_i
|
983
|
+
|
984
|
+
step_tp nil, [:line, :return] do |tp|
|
985
|
+
if tp.event == :line
|
986
|
+
next false if no_loc && depth < DEBUGGER__.frame_depth - 3
|
987
|
+
next false unless tp.path.end_with?(file)
|
988
|
+
next false unless tp.lineno >= line
|
989
|
+
true
|
990
|
+
else
|
991
|
+
true if depth >= DEBUGGER__.frame_depth - 3 &&
|
992
|
+
caller_locations(2, 1).first.label == target_location_label
|
993
|
+
# TODO: imcomplete condition
|
994
|
+
end
|
995
|
+
end
|
996
|
+
else
|
997
|
+
pat = location
|
998
|
+
if /\A\/(.+)\/\z/ =~ pat
|
999
|
+
pat = Regexp.new($1)
|
1000
|
+
end
|
1001
|
+
|
1002
|
+
step_tp nil, [:call, :c_call, :return] do |tp|
|
1003
|
+
case tp.event
|
1004
|
+
when :call, :c_call
|
1005
|
+
true if pat === tp.callee_id.to_s
|
1006
|
+
else # :return, :b_return
|
1007
|
+
true if depth >= DEBUGGER__.frame_depth - 3 &&
|
1008
|
+
caller_locations(2, 1).first.label == target_location_label
|
1009
|
+
# TODO: imcomplete condition
|
1010
|
+
end
|
1011
|
+
end
|
1012
|
+
end
|
1013
|
+
|
1014
|
+
break
|
1015
|
+
|
882
1016
|
when :back
|
1017
|
+
iter = iter || 1
|
883
1018
|
if @recorder&.can_step_back?
|
884
1019
|
unless @recorder.backup_frames
|
885
1020
|
@recorder.backup_frames = @target_frames
|
886
1021
|
end
|
887
|
-
@recorder.step_back
|
1022
|
+
@recorder.step_back iter
|
888
1023
|
raise SuspendReplay
|
889
1024
|
else
|
890
1025
|
puts "Can not step back more."
|
@@ -922,12 +1057,8 @@ module DEBUGGER__
|
|
922
1057
|
when :call
|
923
1058
|
result = frame_eval(eval_src)
|
924
1059
|
when :irb
|
925
|
-
|
926
|
-
|
927
|
-
ensure
|
928
|
-
# workaround: https://github.com/ruby/debug/issues/308
|
929
|
-
Reline.prompt_proc = nil if defined? Reline
|
930
|
-
end
|
1060
|
+
require_relative "irb_integration"
|
1061
|
+
activate_irb_integration
|
931
1062
|
when :display, :try_display
|
932
1063
|
failed_results = []
|
933
1064
|
eval_src.each_with_index{|src, i|
|
@@ -988,6 +1119,10 @@ module DEBUGGER__
|
|
988
1119
|
when :list
|
989
1120
|
show_src(update_line: true, **(args.first || {}))
|
990
1121
|
|
1122
|
+
when :whereami
|
1123
|
+
show_src ignore_show_line: true
|
1124
|
+
show_frames CONFIG[:show_frames]
|
1125
|
+
|
991
1126
|
when :edit
|
992
1127
|
show_by_editor(args.first)
|
993
1128
|
|
@@ -1003,11 +1138,13 @@ module DEBUGGER__
|
|
1003
1138
|
|
1004
1139
|
when :ivars
|
1005
1140
|
pat = args.shift
|
1006
|
-
|
1141
|
+
expr = args.shift
|
1142
|
+
show_ivars pat, expr
|
1007
1143
|
|
1008
1144
|
when :consts
|
1009
1145
|
pat = args.shift
|
1010
|
-
|
1146
|
+
expr = args.shift
|
1147
|
+
show_consts pat, expr
|
1011
1148
|
|
1012
1149
|
when :globals
|
1013
1150
|
pat = args.shift
|
@@ -1093,6 +1230,8 @@ module DEBUGGER__
|
|
1093
1230
|
end
|
1094
1231
|
event! :result, nil
|
1095
1232
|
|
1233
|
+
when :quit
|
1234
|
+
sleep # wait for SystemExit
|
1096
1235
|
when :dap
|
1097
1236
|
process_dap args
|
1098
1237
|
when :cdp
|
@@ -1105,8 +1244,18 @@ module DEBUGGER__
|
|
1105
1244
|
rescue SuspendReplay, SystemExit, Interrupt
|
1106
1245
|
raise
|
1107
1246
|
rescue Exception => e
|
1108
|
-
|
1247
|
+
STDERR.puts e.cause.inspect
|
1248
|
+
STDERR.puts e.inspect
|
1249
|
+
Thread.list.each{|th|
|
1250
|
+
STDERR.puts "@@@ #{th}"
|
1251
|
+
th.backtrace.each{|b|
|
1252
|
+
STDERR.puts " > #{b}"
|
1253
|
+
}
|
1254
|
+
}
|
1255
|
+
p ["DEBUGGER Exception: #{__FILE__}:#{__LINE__}", e, e.backtrace]
|
1109
1256
|
raise
|
1257
|
+
ensure
|
1258
|
+
@returning = false
|
1110
1259
|
end
|
1111
1260
|
|
1112
1261
|
def debug_event(ev, args)
|
@@ -1165,10 +1314,14 @@ module DEBUGGER__
|
|
1165
1314
|
frame._callee = b.eval('__callee__')
|
1166
1315
|
end
|
1167
1316
|
}
|
1168
|
-
|
1317
|
+
append(frames)
|
1169
1318
|
}
|
1170
1319
|
end
|
1171
1320
|
|
1321
|
+
def append frames
|
1322
|
+
@log << frames
|
1323
|
+
end
|
1324
|
+
|
1172
1325
|
def enable
|
1173
1326
|
unless @tp_recorder.enabled?
|
1174
1327
|
@log.clear
|
@@ -1187,12 +1340,18 @@ module DEBUGGER__
|
|
1187
1340
|
@tp_recorder.enabled?
|
1188
1341
|
end
|
1189
1342
|
|
1190
|
-
def step_back
|
1191
|
-
@index +=
|
1343
|
+
def step_back iter
|
1344
|
+
@index += iter
|
1345
|
+
if @index > @log.size
|
1346
|
+
@index = @log.size
|
1347
|
+
end
|
1192
1348
|
end
|
1193
1349
|
|
1194
|
-
def step_forward
|
1195
|
-
@index -=
|
1350
|
+
def step_forward iter
|
1351
|
+
@index -= iter
|
1352
|
+
if @index < 0
|
1353
|
+
@index = 0
|
1354
|
+
end
|
1196
1355
|
end
|
1197
1356
|
|
1198
1357
|
def step_reset
|