debug 1.5.0 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CONTRIBUTING.md +79 -11
- data/README.md +39 -16
- data/Rakefile +25 -7
- data/debug.gemspec +1 -1
- data/exe/rdbg +7 -3
- data/ext/debug/debug.c +0 -22
- data/ext/debug/extconf.rb +18 -7
- data/lib/debug/breakpoint.rb +68 -33
- data/lib/debug/client.rb +12 -2
- data/lib/debug/config.rb +55 -24
- data/lib/debug/console.rb +9 -3
- data/lib/debug/frame_info.rb +31 -24
- data/lib/debug/server.rb +44 -17
- data/lib/debug/server_cdp.rb +5 -5
- data/lib/debug/server_dap.rb +221 -115
- data/lib/debug/session.rb +227 -129
- data/lib/debug/source_repository.rb +13 -0
- data/lib/debug/thread_client.rb +161 -64
- data/lib/debug/tracer.rb +4 -3
- data/lib/debug/version.rb +1 -1
- data/misc/README.md.erb +28 -8
- metadata +5 -6
- data/lib/debug/bp.vim +0 -68
@@ -14,10 +14,19 @@ module DEBUGGER__
|
|
14
14
|
def initialize
|
15
15
|
# cache
|
16
16
|
@cmap = ObjectSpace::WeakMap.new
|
17
|
+
@loaded_file_map = {} # path => nil
|
17
18
|
end
|
18
19
|
|
19
20
|
def add iseq, src
|
20
21
|
# do nothing
|
22
|
+
if (path = (iseq.absolute_path || iseq.path)) && File.exist?(path)
|
23
|
+
if @loaded_file_map.has_key? path
|
24
|
+
return path, true # reloaded
|
25
|
+
else
|
26
|
+
@loaded_file_map[path] = path
|
27
|
+
return path, false
|
28
|
+
end
|
29
|
+
end
|
21
30
|
end
|
22
31
|
|
23
32
|
def get iseq
|
@@ -55,10 +64,14 @@ module DEBUGGER__
|
|
55
64
|
|
56
65
|
def add iseq, src
|
57
66
|
if (path = (iseq.absolute_path || iseq.path)) && File.exist?(path)
|
67
|
+
reloaded = @files.has_key? path
|
58
68
|
add_path path
|
69
|
+
return path, reloaded
|
59
70
|
elsif src
|
60
71
|
add_iseq iseq, src
|
61
72
|
end
|
73
|
+
|
74
|
+
nil
|
62
75
|
end
|
63
76
|
|
64
77
|
private def all_iseq iseq, rs = []
|
data/lib/debug/thread_client.rb
CHANGED
@@ -6,9 +6,26 @@ 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)
|
11
|
-
!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)
|
28
|
+
(skip_paths = CONFIG[:skip_path]) && skip_paths.any?{|skip_path| path.match?(skip_path)}
|
12
29
|
end
|
13
30
|
|
14
31
|
def skip_internal_path?(path)
|
@@ -34,7 +51,7 @@ module DEBUGGER__
|
|
34
51
|
include Color
|
35
52
|
include SkipPathHelper
|
36
53
|
|
37
|
-
attr_reader :thread, :id, :recorder
|
54
|
+
attr_reader :thread, :id, :recorder, :check_bp_fulfillment_map
|
38
55
|
|
39
56
|
def location
|
40
57
|
current_frame&.location
|
@@ -50,7 +67,8 @@ module DEBUGGER__
|
|
50
67
|
call_identifier_str =
|
51
68
|
case frame.frame_type
|
52
69
|
when :block
|
53
|
-
level, block_loc
|
70
|
+
level, block_loc = frame.block_identifier
|
71
|
+
args = frame.parameters_info
|
54
72
|
|
55
73
|
if !args.empty?
|
56
74
|
args_str = " {|#{assemble_arguments(args)}|}"
|
@@ -58,7 +76,8 @@ module DEBUGGER__
|
|
58
76
|
|
59
77
|
"#{colorize_blue("block")}#{args_str} in #{colorize_blue(block_loc + level)}"
|
60
78
|
when :method
|
61
|
-
ci
|
79
|
+
ci = frame.method_identifier
|
80
|
+
args = frame.parameters_info
|
62
81
|
|
63
82
|
if !args.empty?
|
64
83
|
args_str = "(#{assemble_arguments(args)})"
|
@@ -95,6 +114,9 @@ module DEBUGGER__
|
|
95
114
|
@obj_map = {} # { object_id => obj } for CDP
|
96
115
|
@recorder = nil
|
97
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 }
|
98
120
|
set_mode :running
|
99
121
|
thr.instance_variable_set(:@__thread_client_id, id)
|
100
122
|
|
@@ -114,6 +136,7 @@ module DEBUGGER__
|
|
114
136
|
end
|
115
137
|
|
116
138
|
def set_mode mode
|
139
|
+
debug_mode(@mode, mode)
|
117
140
|
# STDERR.puts "#{@mode} => #{mode} @ #{caller.inspect}"
|
118
141
|
# pp caller
|
119
142
|
|
@@ -177,6 +200,7 @@ module DEBUGGER__
|
|
177
200
|
end
|
178
201
|
|
179
202
|
def << req
|
203
|
+
debug_cmd(req)
|
180
204
|
@q_cmd << req
|
181
205
|
end
|
182
206
|
|
@@ -187,6 +211,7 @@ module DEBUGGER__
|
|
187
211
|
end
|
188
212
|
|
189
213
|
def event! ev, *args
|
214
|
+
debug_event(ev, args)
|
190
215
|
@q_evt << [self, @output, ev, generate_info, *args]
|
191
216
|
@output = []
|
192
217
|
end
|
@@ -232,6 +257,7 @@ module DEBUGGER__
|
|
232
257
|
|
233
258
|
def suspend event, tp = nil, bp: nil, sig: nil, postmortem_frames: nil, replay_frames: nil, postmortem_exc: nil
|
234
259
|
return if management?
|
260
|
+
debug_suspend(event)
|
235
261
|
|
236
262
|
@current_frame_index = 0
|
237
263
|
|
@@ -266,7 +292,7 @@ module DEBUGGER__
|
|
266
292
|
|
267
293
|
if event != :pause
|
268
294
|
show_src
|
269
|
-
show_frames CONFIG[:show_frames]
|
295
|
+
show_frames CONFIG[:show_frames]
|
270
296
|
|
271
297
|
set_mode :waiting
|
272
298
|
|
@@ -413,55 +439,64 @@ module DEBUGGER__
|
|
413
439
|
raise if re_raise
|
414
440
|
end
|
415
441
|
|
416
|
-
def
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
"#{cur}#{line} #{e}"
|
430
|
-
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
|
431
455
|
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
else
|
437
|
-
end_line = frame.show_line - max_lines
|
438
|
-
start_line = [end_line - max_lines, 0].max
|
439
|
-
end
|
456
|
+
unless start_line
|
457
|
+
if frame.show_line
|
458
|
+
if dir > 0
|
459
|
+
start_line = frame.show_line
|
440
460
|
else
|
441
|
-
|
461
|
+
end_line = frame.show_line - max_lines
|
462
|
+
start_line = [end_line - max_lines, 0].max
|
442
463
|
end
|
464
|
+
else
|
465
|
+
start_line = [frame_line - max_lines/2, 0].max
|
443
466
|
end
|
467
|
+
end
|
444
468
|
|
445
|
-
|
446
|
-
|
447
|
-
|
469
|
+
unless end_line
|
470
|
+
end_line = [start_line + max_lines, lines.size].min
|
471
|
+
end
|
448
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
|
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
|
449
490
|
if update_line
|
450
491
|
frame.show_line = end_line
|
451
492
|
end
|
452
493
|
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
end
|
457
|
-
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
|
458
497
|
puts "# No sourcefile available for #{frame.path}"
|
459
498
|
end
|
460
499
|
end
|
461
|
-
rescue Exception => e
|
462
|
-
p e
|
463
|
-
pp e.backtrace
|
464
|
-
exit!
|
465
500
|
end
|
466
501
|
|
467
502
|
def current_frame
|
@@ -513,8 +548,8 @@ module DEBUGGER__
|
|
513
548
|
|
514
549
|
def show_ivars pat
|
515
550
|
if s = current_frame&.self
|
516
|
-
s.
|
517
|
-
value =
|
551
|
+
M_INSTANCE_VARIABLES.bind_call(s).sort.each{|iv|
|
552
|
+
value = M_INSTANCE_VARIABLE_GET.bind_call(s, iv)
|
518
553
|
puts_variable_info iv, value, pat
|
519
554
|
}
|
520
555
|
end
|
@@ -523,10 +558,10 @@ module DEBUGGER__
|
|
523
558
|
def show_consts pat, only_self: false
|
524
559
|
if s = current_frame&.self
|
525
560
|
cs = {}
|
526
|
-
if s
|
561
|
+
if M_KIND_OF_P.bind_call(s, Module)
|
527
562
|
cs[s] = :self
|
528
563
|
else
|
529
|
-
s = s
|
564
|
+
s = M_CLASS.bind_call(s)
|
530
565
|
cs[s] = :self unless only_self
|
531
566
|
end
|
532
567
|
|
@@ -564,7 +599,7 @@ module DEBUGGER__
|
|
564
599
|
return if pat && pat !~ label
|
565
600
|
|
566
601
|
begin
|
567
|
-
inspected = obj
|
602
|
+
inspected = DEBUGGER__.safe_inspect(obj)
|
568
603
|
rescue Exception => e
|
569
604
|
inspected = e.inspect
|
570
605
|
end
|
@@ -623,15 +658,11 @@ module DEBUGGER__
|
|
623
658
|
if @target_frames && (max ||= @target_frames.size) > 0
|
624
659
|
frames = []
|
625
660
|
@target_frames.each_with_index{|f, i|
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
when Regexp
|
632
|
-
f.location_str.match?(pat)
|
633
|
-
end
|
634
|
-
}
|
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)
|
635
666
|
|
636
667
|
frames << [i, f]
|
637
668
|
}
|
@@ -668,18 +699,25 @@ module DEBUGGER__
|
|
668
699
|
o = Output.new(@output)
|
669
700
|
|
670
701
|
locals = current_frame&.local_variables
|
671
|
-
klass = (obj.class == Class || obj.class == Module ? obj : obj.class)
|
672
702
|
|
673
|
-
|
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)
|
674
707
|
outline_method(o, klass, obj)
|
675
|
-
o.dump("instance variables", obj
|
708
|
+
o.dump("instance variables", M_INSTANCE_VARIABLES.bind_call(obj))
|
676
709
|
o.dump("class variables", klass.class_variables)
|
677
710
|
o.dump("locals", locals.keys) if locals
|
678
711
|
end
|
679
712
|
end
|
680
713
|
|
681
714
|
def outline_method(o, klass, obj)
|
682
|
-
|
715
|
+
begin
|
716
|
+
singleton_class = M_SINGLETON_CLASS.bind_call(obj)
|
717
|
+
rescue TypeError
|
718
|
+
singleton_class = nil
|
719
|
+
end
|
720
|
+
|
683
721
|
maps = class_method_map((singleton_class || klass).ancestors)
|
684
722
|
maps.each do |mod, methods|
|
685
723
|
name = mod == singleton_class ? "#{klass}.methods" : "#{mod}#methods"
|
@@ -699,6 +737,23 @@ module DEBUGGER__
|
|
699
737
|
|
700
738
|
## cmd: breakpoint
|
701
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
|
+
|
702
757
|
def make_breakpoint args
|
703
758
|
case args.first
|
704
759
|
when :method
|
@@ -706,9 +761,24 @@ module DEBUGGER__
|
|
706
761
|
bp = MethodBreakpoint.new(current_frame.eval_binding, klass_name, op, method_name, cond: cond, command: cmd, path: path)
|
707
762
|
begin
|
708
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
|
709
779
|
rescue Exception => e
|
710
|
-
puts e.
|
711
|
-
|
780
|
+
puts e.inspect
|
781
|
+
bp = nil
|
712
782
|
end
|
713
783
|
|
714
784
|
bp
|
@@ -981,7 +1051,7 @@ module DEBUGGER__
|
|
981
1051
|
begin
|
982
1052
|
obj = frame_eval args.shift, re_raise: true
|
983
1053
|
opt = args.shift
|
984
|
-
obj_inspect = obj
|
1054
|
+
obj_inspect = DEBUGGER__.safe_inspect(obj)
|
985
1055
|
|
986
1056
|
width = 50
|
987
1057
|
|
@@ -989,7 +1059,7 @@ module DEBUGGER__
|
|
989
1059
|
obj_inspect = truncate(obj_inspect, width: width)
|
990
1060
|
end
|
991
1061
|
|
992
|
-
event! :result, :trace_pass, obj
|
1062
|
+
event! :result, :trace_pass, M_OBJECT_ID.bind_call(obj), obj_inspect, opt
|
993
1063
|
rescue => e
|
994
1064
|
puts e.message
|
995
1065
|
event! :result, nil
|
@@ -1006,8 +1076,8 @@ module DEBUGGER__
|
|
1006
1076
|
# enable recording
|
1007
1077
|
if !@recorder
|
1008
1078
|
@recorder = Recorder.new
|
1009
|
-
@recorder.enable
|
1010
1079
|
end
|
1080
|
+
@recorder.enable
|
1011
1081
|
when :off
|
1012
1082
|
if @recorder&.enabled?
|
1013
1083
|
@recorder.disable
|
@@ -1039,6 +1109,33 @@ module DEBUGGER__
|
|
1039
1109
|
raise
|
1040
1110
|
end
|
1041
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
|
+
|
1042
1139
|
class Recorder
|
1043
1140
|
attr_reader :log, :index
|
1044
1141
|
attr_accessor :backup_frames
|
data/lib/debug/tracer.rb
CHANGED
@@ -74,7 +74,7 @@ module DEBUGGER__
|
|
74
74
|
end
|
75
75
|
|
76
76
|
def out tp, msg = nil, depth = caller.size - 1
|
77
|
-
location_str = colorize("#{tp.path}:#{tp.lineno}", [:GREEN])
|
77
|
+
location_str = colorize("#{FrameInfo.pretty_path(tp.path)}:#{tp.lineno}", [:GREEN])
|
78
78
|
buff = "#{header(depth)}#{msg} at #{location_str}"
|
79
79
|
|
80
80
|
if false # TODO: Ractor.main?
|
@@ -119,7 +119,6 @@ module DEBUGGER__
|
|
119
119
|
next if skip?(tp)
|
120
120
|
|
121
121
|
depth = caller.size
|
122
|
-
sp = ' ' * depth
|
123
122
|
|
124
123
|
call_identifier_str =
|
125
124
|
if tp.defined_class
|
@@ -133,9 +132,11 @@ module DEBUGGER__
|
|
133
132
|
case tp.event
|
134
133
|
when :call, :c_call, :b_call
|
135
134
|
depth += 1 if tp.event == :c_call
|
135
|
+
sp = ' ' * depth
|
136
136
|
out tp, ">#{sp}#{call_identifier_str}", depth
|
137
137
|
when :return, :c_return, :b_return
|
138
138
|
depth += 1 if tp.event == :c_return
|
139
|
+
sp = ' ' * depth
|
139
140
|
return_str = colorize_magenta(DEBUGGER__.safe_inspect(tp.return_value, short: true))
|
140
141
|
out tp, "<#{sp}#{call_identifier_str} #=> #{return_str}", depth
|
141
142
|
end
|
@@ -185,7 +186,7 @@ module DEBUGGER__
|
|
185
186
|
@tracer = TracePoint.new(:a_call){|tp|
|
186
187
|
next if skip?(tp)
|
187
188
|
|
188
|
-
if tp.self
|
189
|
+
if M_OBJECT_ID.bind_call(tp.self) == @obj_id
|
189
190
|
klass = tp.defined_class
|
190
191
|
method = tp.method_id
|
191
192
|
method_info =
|
data/lib/debug/version.rb
CHANGED
data/misc/README.md.erb
CHANGED
@@ -9,11 +9,15 @@ New debug.rb has several advantages:
|
|
9
9
|
|
10
10
|
* Fast: No performance penalty on non-stepping mode and non-breakpoints.
|
11
11
|
* [Remote debugging](#remote-debugging): Support remote debugging natively.
|
12
|
-
* UNIX domain socket
|
12
|
+
* UNIX domain socket (UDS)
|
13
13
|
* TCP/IP
|
14
|
-
* Integration with rich debugger
|
15
|
-
|
16
|
-
|
14
|
+
* Integration with rich debugger frontends
|
15
|
+
|
16
|
+
Frontend | [Console](https://github.com/ruby/debug#invoke-as-a-remote-debuggee) | [VSCode](https://github.com/ruby/debug#vscode-integration) | [Chrome DevTool](#chrome-devtool-integration) |
|
17
|
+
---|---|---|---|
|
18
|
+
Connection | UDS, TCP/IP | UDS, TCP/IP | TCP/IP |
|
19
|
+
Requirement | No | [vscode-rdbg](https://marketplace.visualstudio.com/items?itemName=KoichiSasada.vscode-rdbg) | Chrome |
|
20
|
+
|
17
21
|
* Extensible: application can introduce debugging support with several ways:
|
18
22
|
* By `rdbg` command
|
19
23
|
* By loading libraries with `-r` command line option
|
@@ -38,6 +42,9 @@ If you use Bundler, write the following line to your Gemfile.
|
|
38
42
|
gem "debug", ">= 1.0.0"
|
39
43
|
```
|
40
44
|
|
45
|
+
(The version constraint is important; `debug < 1.0.0` is an older,
|
46
|
+
abandoned gem that is completely different from this product.)
|
47
|
+
|
41
48
|
# HOW TO USE
|
42
49
|
|
43
50
|
To use a debugger, roughly you will do the following steps:
|
@@ -134,7 +141,7 @@ d => 4
|
|
134
141
|
### Invoke the program from the debugger as a traditional debuggers
|
135
142
|
|
136
143
|
If you don't want to modify the source code, you can set breakpoints with a debug command `break` (`b` for short).
|
137
|
-
Using `rdbg` command to launch the program without any modifications, you can run the program with the debugger.
|
144
|
+
Using `rdbg` command (or `bundle exec rdbg`) to launch the program without any modifications, you can run the program with the debugger.
|
138
145
|
|
139
146
|
```shell
|
140
147
|
$ cat target.rb # Sample program
|
@@ -280,7 +287,12 @@ You can run your application as a remote debuggee and the remote debugger consol
|
|
280
287
|
|
281
288
|
### Invoke as a remote debuggee
|
282
289
|
|
283
|
-
There are
|
290
|
+
There are multiple ways to run your program as a debuggee:
|
291
|
+
|
292
|
+
Stop at program start | [`rdbg` option](https://github.com/ruby/debug#rdbg---open-or-rdbg--o-for-short) | [require](https://github.com/ruby/debug#require-debugopen-in-a-program) | [debugger API](https://github.com/ruby/debug#start-by-method)
|
293
|
+
---|---|---|---|
|
294
|
+
Yes | `rdbg --open` | `require "debug/open"` | `DEBUGGER__.open`
|
295
|
+
No | `rdbg --open --nonstop` | `require "debug/open_nonstop"` | `DEBUGGER__.open(nonstop: true)`
|
284
296
|
|
285
297
|
#### `rdbg --open` (or `rdbg -O` for short)
|
286
298
|
|
@@ -452,10 +464,18 @@ config set log_level INFO
|
|
452
464
|
config set no_color true
|
453
465
|
```
|
454
466
|
|
455
|
-
<% cat = nil; DEBUGGER__::CONFIG_SET.each do |key, (env, desc)| %>
|
467
|
+
<% cat = nil; DEBUGGER__::CONFIG_SET.each do |key, (env, desc, _, default)| %>
|
456
468
|
<% /\A(\w+): (.+)/ =~ desc; if cat != $1; cat = 1 %>
|
457
469
|
* <%= $1 %>
|
458
|
-
<% cat = $1; end %> * `<%= env %>` (`<%= key %>`): <%= $2 %><% end %>
|
470
|
+
<% cat = $1; end %> * `<%= env %>` (`<%= key %>`): <%= default ? "#{$2} (default: #{default})" : $2 %><% end %>
|
471
|
+
|
472
|
+
There are other environment variables:
|
473
|
+
|
474
|
+
* `NO_COLOR`: If the value is set, set `RUBY_DEBUG_NO_COLOR` ([NO_COLOR: disabling ANSI color output in various Unix commands](https://no-color.org/)).
|
475
|
+
* `RUBY_DEBUG_ENABLE`: If the value is `0`, do not enable debug.gem feature.
|
476
|
+
* `RUBY_DEBUG_ADDED_RUBYOPT`: Remove this value from `RUBYOPT` at first. This feature helps loading debug.gem with `RUBYOPT='-r debug/...'` and you don't want to derive it to child processes. In this case you can set `RUBY_DEBUG_ADDED_RUBYOPT='-r debug/...'` (same value) and this string will be deleted from `RUBYOPT` at first.
|
477
|
+
* `RUBY_DEBUG_EDITOR` or `EDITOR`: An editor used by `edit` debug command.
|
478
|
+
* `RUBY_DEBUG_BB`: Define `Kernel#bb` method which is alias of `Kernel#debugger`.
|
459
479
|
|
460
480
|
### Initial scripts
|
461
481
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: debug
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Koichi Sasada
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-07-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: irb
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 0.
|
33
|
+
version: 0.3.1
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 0.
|
40
|
+
version: 0.3.1
|
41
41
|
description: Debugging functionality for Ruby. This is completely rewritten debug.rb
|
42
42
|
which was contained by the ancient Ruby versions.
|
43
43
|
email:
|
@@ -60,7 +60,6 @@ files:
|
|
60
60
|
- ext/debug/extconf.rb
|
61
61
|
- ext/debug/iseq_collector.c
|
62
62
|
- lib/debug.rb
|
63
|
-
- lib/debug/bp.vim
|
64
63
|
- lib/debug/breakpoint.rb
|
65
64
|
- lib/debug/client.rb
|
66
65
|
- lib/debug/color.rb
|
@@ -103,7 +102,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
103
102
|
- !ruby/object:Gem::Version
|
104
103
|
version: '0'
|
105
104
|
requirements: []
|
106
|
-
rubygems_version: 3.
|
105
|
+
rubygems_version: 3.3.7
|
107
106
|
signing_key:
|
108
107
|
specification_version: 4
|
109
108
|
summary: Debugging functionality for Ruby
|
data/lib/debug/bp.vim
DELETED
@@ -1,68 +0,0 @@
|
|
1
|
-
let g:rdb_bps = {}
|
2
|
-
|
3
|
-
function SET_BP()
|
4
|
-
let signed = sign_getplaced(bufname(), {'lnum': line('.')})
|
5
|
-
if empty(signed[0]['signs'])
|
6
|
-
call sign_place(0, '', 'signBP', bufname(), {'lnum': line('.')})
|
7
|
-
else
|
8
|
-
"echo signed[0]['signs']
|
9
|
-
call sign_unplace('', {'buffer': bufname(), 'id': signed[0]['signs'][0]['id']})
|
10
|
-
endif
|
11
|
-
endfunction
|
12
|
-
|
13
|
-
function UPDATE_BPS()
|
14
|
-
let signs = sign_getplaced(bufname())
|
15
|
-
let key = expand('%:p')
|
16
|
-
|
17
|
-
if empty(signs[0]['signs'])
|
18
|
-
let removed = remove(g:rdb_bps, key)
|
19
|
-
else
|
20
|
-
let g:rdb_bps[key] = signs[0]['signs']
|
21
|
-
endif
|
22
|
-
endfunction
|
23
|
-
|
24
|
-
function APPLY_BPS()
|
25
|
-
let key = expand('%:p')
|
26
|
-
if has_key(g:rdb_bps, key)
|
27
|
-
for b in g:rdb_bps[key]
|
28
|
-
call sign_place(0, '', 'signBP', bufname(), {'lnum': b['lnum']})
|
29
|
-
endfor
|
30
|
-
endif
|
31
|
-
endfunction
|
32
|
-
|
33
|
-
function WRITE_BPS()
|
34
|
-
call writefile([json_encode(g:rdb_bps)], '.rdb_breakpoints.json')
|
35
|
-
endfunction
|
36
|
-
|
37
|
-
" load
|
38
|
-
try
|
39
|
-
let json = readfile('.rdb_breakpoints.json')
|
40
|
-
let g:rdb_bps = json_decode(json[0])
|
41
|
-
" {"/full/path/to/file1": [{"lnum": 10}, ...], ...}
|
42
|
-
catch /Can't open/
|
43
|
-
let g:rdb_bps = {}
|
44
|
-
catch /Invalid arguments for function json_decode/
|
45
|
-
let g:rdb_bps = {}
|
46
|
-
endtry
|
47
|
-
|
48
|
-
sign define signBP text=BR
|
49
|
-
|
50
|
-
call APPLY_BPS()
|
51
|
-
|
52
|
-
autocmd BufReadPost * call APPLY_BPS()
|
53
|
-
autocmd BufUnload * call UPDATE_BPS()
|
54
|
-
autocmd VimLeave * call WRITE_BPS()
|
55
|
-
|
56
|
-
function! s:ruby_bp_settings() abort
|
57
|
-
echomsg "Type <Space> to toggle break points and <q> to quit"
|
58
|
-
|
59
|
-
if &readonly
|
60
|
-
nnoremap <silent> <buffer> <Space> :call SET_BP()<CR>
|
61
|
-
nnoremap <silent> <buffer> q :<C-u>quit<CR>
|
62
|
-
endif
|
63
|
-
endfunction
|
64
|
-
|
65
|
-
" autocmd FileType ruby call s:ruby_bp_settings()
|
66
|
-
autocmd BufEnter *.rb call s:ruby_bp_settings()
|
67
|
-
|
68
|
-
call s:ruby_bp_settings()
|