debug 1.5.0 → 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 +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()
|