debug 1.6.1 → 1.9.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|