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
data/lib/debug/session.rb
CHANGED
@@ -19,6 +19,14 @@ if $0.end_with?('bin/bundle') && ARGV.first == 'exec'
|
|
19
19
|
return
|
20
20
|
end
|
21
21
|
|
22
|
+
# restore RUBYOPT
|
23
|
+
if (added_opt = ENV['RUBY_DEBUG_ADDED_RUBYOPT']) &&
|
24
|
+
(rubyopt = ENV['RUBYOPT']) &&
|
25
|
+
rubyopt.start_with?(added_opt)
|
26
|
+
ENV['RUBYOPT'] = rubyopt.delete_prefix(rubyopt)
|
27
|
+
ENV['RUBY_DEBUG_ADDED_RUBYOPT'] = nil
|
28
|
+
end
|
29
|
+
|
22
30
|
require_relative 'frame_info'
|
23
31
|
require_relative 'config'
|
24
32
|
require_relative 'thread_client'
|
@@ -32,6 +40,7 @@ $LOADED_FEATURES << File.expand_path(File.join(__dir__, '..', 'debug.rb'))
|
|
32
40
|
require 'debug' # invalidate the $LOADED_FEATURE cache
|
33
41
|
|
34
42
|
require 'json' if ENV['RUBY_DEBUG_TEST_UI'] == 'terminal'
|
43
|
+
require 'pp'
|
35
44
|
|
36
45
|
class RubyVM::InstructionSequence
|
37
46
|
def traceable_lines_norec lines
|
@@ -197,9 +206,14 @@ module DEBUGGER__
|
|
197
206
|
deactivate
|
198
207
|
end
|
199
208
|
|
209
|
+
def request_tc(req)
|
210
|
+
@tc << req
|
211
|
+
end
|
212
|
+
|
200
213
|
def process_event evt
|
201
214
|
# variable `@internal_info` is only used for test
|
202
|
-
tc, output, ev, @internal_info, *ev_args = evt
|
215
|
+
@tc, output, ev, @internal_info, *ev_args = evt
|
216
|
+
|
203
217
|
output.each{|str| @ui.puts str} if ev != :suspend
|
204
218
|
|
205
219
|
case ev
|
@@ -212,20 +226,18 @@ module DEBUGGER__
|
|
212
226
|
|
213
227
|
when :init
|
214
228
|
enter_subsession
|
215
|
-
wait_command_loop
|
216
|
-
|
229
|
+
wait_command_loop
|
217
230
|
when :load
|
218
231
|
iseq, src = ev_args
|
219
232
|
on_load iseq, src
|
220
|
-
|
221
|
-
tc << :continue
|
233
|
+
request_tc :continue
|
222
234
|
|
223
235
|
when :trace
|
224
236
|
trace_id, msg = ev_args
|
225
237
|
if t = @tracers.values.find{|t| t.object_id == trace_id}
|
226
238
|
t.puts msg
|
227
239
|
end
|
228
|
-
|
240
|
+
request_tc :continue
|
229
241
|
|
230
242
|
when :suspend
|
231
243
|
enter_subsession if ev_args.first != :replay
|
@@ -235,24 +247,23 @@ module DEBUGGER__
|
|
235
247
|
when :breakpoint
|
236
248
|
bp, i = bp_index ev_args[1]
|
237
249
|
clean_bps unless bp
|
238
|
-
@ui.event :suspend_bp, i, bp, tc.id
|
250
|
+
@ui.event :suspend_bp, i, bp, @tc.id
|
239
251
|
when :trap
|
240
|
-
@ui.event :suspend_trap, sig = ev_args[1], tc.id
|
252
|
+
@ui.event :suspend_trap, sig = ev_args[1], @tc.id
|
241
253
|
|
242
254
|
if sig == :SIGINT && (@intercepted_sigint_cmd.kind_of?(Proc) || @intercepted_sigint_cmd.kind_of?(String))
|
243
255
|
@ui.puts "#{@intercepted_sigint_cmd.inspect} is registered as SIGINT handler."
|
244
256
|
@ui.puts "`sigint` command execute it."
|
245
257
|
end
|
246
258
|
else
|
247
|
-
@ui.event :suspended, tc.id
|
259
|
+
@ui.event :suspended, @tc.id
|
248
260
|
end
|
249
261
|
|
250
262
|
if @displays.empty?
|
251
|
-
wait_command_loop
|
263
|
+
wait_command_loop
|
252
264
|
else
|
253
|
-
|
265
|
+
request_tc [:eval, :display, @displays]
|
254
266
|
end
|
255
|
-
|
256
267
|
when :result
|
257
268
|
raise "[BUG] not in subsession" if @subsession_stack.empty?
|
258
269
|
|
@@ -283,14 +294,14 @@ module DEBUGGER__
|
|
283
294
|
# ignore
|
284
295
|
end
|
285
296
|
|
286
|
-
wait_command_loop
|
297
|
+
wait_command_loop
|
287
298
|
|
288
299
|
when :dap_result
|
289
300
|
dap_event ev_args # server.rb
|
290
|
-
wait_command_loop
|
301
|
+
wait_command_loop
|
291
302
|
when :cdp_result
|
292
303
|
cdp_event ev_args
|
293
|
-
wait_command_loop
|
304
|
+
wait_command_loop
|
294
305
|
end
|
295
306
|
end
|
296
307
|
|
@@ -323,9 +334,7 @@ module DEBUGGER__
|
|
323
334
|
"DEBUGGER__::SESSION"
|
324
335
|
end
|
325
336
|
|
326
|
-
def wait_command_loop
|
327
|
-
@tc = tc
|
328
|
-
|
337
|
+
def wait_command_loop
|
329
338
|
loop do
|
330
339
|
case wait_command
|
331
340
|
when :retry
|
@@ -529,32 +538,6 @@ module DEBUGGER__
|
|
529
538
|
end
|
530
539
|
end
|
531
540
|
|
532
|
-
# skip
|
533
|
-
when 'bv'
|
534
|
-
check_postmortem
|
535
|
-
require 'json'
|
536
|
-
|
537
|
-
h = Hash.new{|h, k| h[k] = []}
|
538
|
-
@bps.each_value{|bp|
|
539
|
-
if LineBreakpoint === bp
|
540
|
-
h[bp.path] << {lnum: bp.line}
|
541
|
-
end
|
542
|
-
}
|
543
|
-
if h.empty?
|
544
|
-
# TODO: clean?
|
545
|
-
else
|
546
|
-
open(".rdb_breakpoints.json", 'w'){|f| JSON.dump(h, f)}
|
547
|
-
end
|
548
|
-
|
549
|
-
vimsrc = File.join(__dir__, 'bp.vim')
|
550
|
-
system("vim -R -S #{vimsrc} #{@tc.location.path}")
|
551
|
-
|
552
|
-
if File.exist?(".rdb_breakpoints.json")
|
553
|
-
pp JSON.load(File.read(".rdb_breakpoints.json"))
|
554
|
-
end
|
555
|
-
|
556
|
-
return :retry
|
557
|
-
|
558
541
|
# * `catch <Error>`
|
559
542
|
# * Set breakpoint on raising `<Error>`.
|
560
543
|
# * `catch ... if: <expr>`
|
@@ -632,15 +615,15 @@ module DEBUGGER__
|
|
632
615
|
when 'bt', 'backtrace'
|
633
616
|
case arg
|
634
617
|
when /\A(\d+)\z/
|
635
|
-
|
618
|
+
request_tc [:show, :backtrace, arg.to_i, nil]
|
636
619
|
when /\A\/(.*)\/\z/
|
637
620
|
pattern = $1
|
638
|
-
|
621
|
+
request_tc [:show, :backtrace, nil, Regexp.compile(pattern)]
|
639
622
|
when /\A(\d+)\s+\/(.*)\/\z/
|
640
623
|
max, pattern = $1, $2
|
641
|
-
|
624
|
+
request_tc [:show, :backtrace, max.to_i, Regexp.compile(pattern)]
|
642
625
|
else
|
643
|
-
|
626
|
+
request_tc [:show, :backtrace, nil, nil]
|
644
627
|
end
|
645
628
|
|
646
629
|
# * `l[ist]`
|
@@ -653,13 +636,13 @@ module DEBUGGER__
|
|
653
636
|
when 'l', 'list'
|
654
637
|
case arg ? arg.strip : nil
|
655
638
|
when /\A(\d+)\z/
|
656
|
-
|
639
|
+
request_tc [:show, :list, {start_line: arg.to_i - 1}]
|
657
640
|
when /\A-\z/
|
658
|
-
|
641
|
+
request_tc [:show, :list, {dir: -1}]
|
659
642
|
when /\A(\d+)-(\d+)\z/
|
660
|
-
|
643
|
+
request_tc [:show, :list, {start_line: $1.to_i - 1, end_line: $2.to_i}]
|
661
644
|
when nil
|
662
|
-
|
645
|
+
request_tc [:show, :list]
|
663
646
|
else
|
664
647
|
@ui.puts "Can not handle list argument: #{arg}"
|
665
648
|
return :retry
|
@@ -683,7 +666,7 @@ module DEBUGGER__
|
|
683
666
|
return :retry
|
684
667
|
end
|
685
668
|
|
686
|
-
|
669
|
+
request_tc [:show, :edit, arg]
|
687
670
|
|
688
671
|
# * `i[nfo]`
|
689
672
|
# * Show information about current frame (local/instance variables and defined constants).
|
@@ -710,15 +693,15 @@ module DEBUGGER__
|
|
710
693
|
|
711
694
|
case sub
|
712
695
|
when nil
|
713
|
-
|
696
|
+
request_tc [:show, :default, pat] # something useful
|
714
697
|
when 'l', /^locals?/
|
715
|
-
|
698
|
+
request_tc [:show, :locals, pat]
|
716
699
|
when 'i', /^ivars?/i, /^instance[_ ]variables?/i
|
717
|
-
|
700
|
+
request_tc [:show, :ivars, pat]
|
718
701
|
when 'c', /^consts?/i, /^constants?/i
|
719
|
-
|
702
|
+
request_tc [:show, :consts, pat]
|
720
703
|
when 'g', /^globals?/i, /^global[_ ]variables?/i
|
721
|
-
|
704
|
+
request_tc [:show, :globals, pat]
|
722
705
|
when 'th', /threads?/
|
723
706
|
thread_list
|
724
707
|
return :retry
|
@@ -734,7 +717,7 @@ module DEBUGGER__
|
|
734
717
|
# * Show you available methods and instance variables of the given object.
|
735
718
|
# * If the object is a class/module, it also lists its constants.
|
736
719
|
when 'outline', 'o', 'ls'
|
737
|
-
|
720
|
+
request_tc [:show, :outline, arg]
|
738
721
|
|
739
722
|
# * `display`
|
740
723
|
# * Show display setting.
|
@@ -743,9 +726,9 @@ module DEBUGGER__
|
|
743
726
|
when 'display'
|
744
727
|
if arg && !arg.empty?
|
745
728
|
@displays << arg
|
746
|
-
|
729
|
+
request_tc [:eval, :try_display, @displays]
|
747
730
|
else
|
748
|
-
|
731
|
+
request_tc [:eval, :display, @displays]
|
749
732
|
end
|
750
733
|
|
751
734
|
# * `undisplay`
|
@@ -758,7 +741,7 @@ module DEBUGGER__
|
|
758
741
|
if @displays[n = $1.to_i]
|
759
742
|
@displays.delete_at n
|
760
743
|
end
|
761
|
-
|
744
|
+
request_tc [:eval, :display, @displays]
|
762
745
|
when nil
|
763
746
|
if ask "clear all?", 'N'
|
764
747
|
@displays.clear
|
@@ -773,29 +756,29 @@ module DEBUGGER__
|
|
773
756
|
# * `f[rame] <framenum>`
|
774
757
|
# * Specify a current frame. Evaluation are run on specified frame.
|
775
758
|
when 'frame', 'f'
|
776
|
-
|
759
|
+
request_tc [:frame, :set, arg]
|
777
760
|
|
778
761
|
# * `up`
|
779
762
|
# * Specify the upper frame.
|
780
763
|
when 'up'
|
781
|
-
|
764
|
+
request_tc [:frame, :up]
|
782
765
|
|
783
766
|
# * `down`
|
784
767
|
# * Specify the lower frame.
|
785
768
|
when 'down'
|
786
|
-
|
769
|
+
request_tc [:frame, :down]
|
787
770
|
|
788
771
|
### Evaluate
|
789
772
|
|
790
773
|
# * `p <expr>`
|
791
774
|
# * Evaluate like `p <expr>` on the current frame.
|
792
775
|
when 'p'
|
793
|
-
|
776
|
+
request_tc [:eval, :p, arg.to_s]
|
794
777
|
|
795
778
|
# * `pp <expr>`
|
796
779
|
# * Evaluate like `pp <expr>` on the current frame.
|
797
780
|
when 'pp'
|
798
|
-
|
781
|
+
request_tc [:eval, :pp, arg.to_s]
|
799
782
|
|
800
783
|
# * `eval <expr>`
|
801
784
|
# * Evaluate `<expr>` on the current frame.
|
@@ -805,7 +788,7 @@ module DEBUGGER__
|
|
805
788
|
@ui.puts "\nTo evaluate the variable `#{cmd}`, use `pp #{cmd}` instead."
|
806
789
|
return :retry
|
807
790
|
else
|
808
|
-
|
791
|
+
request_tc [:eval, :call, arg]
|
809
792
|
end
|
810
793
|
|
811
794
|
# * `irb`
|
@@ -815,7 +798,7 @@ module DEBUGGER__
|
|
815
798
|
@ui.puts "not supported on the remote console."
|
816
799
|
return :retry
|
817
800
|
end
|
818
|
-
|
801
|
+
request_tc [:eval, :irb]
|
819
802
|
|
820
803
|
# don't repeat irb command
|
821
804
|
@repl_prev_line = nil
|
@@ -872,7 +855,7 @@ module DEBUGGER__
|
|
872
855
|
return :retry
|
873
856
|
|
874
857
|
when /\Aobject\s+(.+)/
|
875
|
-
|
858
|
+
request_tc [:trace, :object, $1.strip, {pattern: pattern, into: into}]
|
876
859
|
|
877
860
|
when /\Aoff\s+(\d+)\z/
|
878
861
|
if t = @tracers.values[$1.to_i]
|
@@ -910,7 +893,7 @@ module DEBUGGER__
|
|
910
893
|
when 'record'
|
911
894
|
case arg
|
912
895
|
when nil, 'on', 'off'
|
913
|
-
|
896
|
+
request_tc [:record, arg&.to_sym]
|
914
897
|
else
|
915
898
|
@ui.puts "unknown command: #{arg}"
|
916
899
|
return :retry
|
@@ -1005,7 +988,7 @@ module DEBUGGER__
|
|
1005
988
|
|
1006
989
|
### END
|
1007
990
|
else
|
1008
|
-
|
991
|
+
request_tc [:eval, :pp, line]
|
1009
992
|
=begin
|
1010
993
|
@repl_prev_line = nil
|
1011
994
|
@ui.puts "unknown command: #{line}"
|
@@ -1062,7 +1045,7 @@ module DEBUGGER__
|
|
1062
1045
|
case arg
|
1063
1046
|
when nil, /\A\d+\z/
|
1064
1047
|
if type == :in && @tc.recorder&.replaying?
|
1065
|
-
|
1048
|
+
request_tc [:step, type, arg&.to_i]
|
1066
1049
|
else
|
1067
1050
|
leave_subsession [:step, type, arg&.to_i]
|
1068
1051
|
end
|
@@ -1071,7 +1054,7 @@ module DEBUGGER__
|
|
1071
1054
|
@ui.puts "only `step #{arg}` is supported."
|
1072
1055
|
:retry
|
1073
1056
|
else
|
1074
|
-
|
1057
|
+
request_tc [:step, arg.to_sym]
|
1075
1058
|
end
|
1076
1059
|
else
|
1077
1060
|
@ui.puts "Unknown option: #{arg}"
|
@@ -1081,11 +1064,18 @@ module DEBUGGER__
|
|
1081
1064
|
|
1082
1065
|
def config_show key
|
1083
1066
|
key = key.to_sym
|
1084
|
-
|
1067
|
+
config_detail = CONFIG_SET[key]
|
1068
|
+
|
1069
|
+
if config_detail
|
1085
1070
|
v = CONFIG[key]
|
1086
|
-
kv = "#{key} = #{v.
|
1087
|
-
desc =
|
1088
|
-
|
1071
|
+
kv = "#{key} = #{v.inspect}"
|
1072
|
+
desc = config_detail[1]
|
1073
|
+
|
1074
|
+
if config_default = config_detail[3]
|
1075
|
+
desc += " (default: #{config_default})"
|
1076
|
+
end
|
1077
|
+
|
1078
|
+
line = "%-34s \# %s" % [kv, desc]
|
1089
1079
|
if line.size > SESSION.width
|
1090
1080
|
@ui.puts "\# #{desc}\n#{kv}"
|
1091
1081
|
else
|
@@ -1135,7 +1125,7 @@ module DEBUGGER__
|
|
1135
1125
|
config_set $1, $2, append: true
|
1136
1126
|
|
1137
1127
|
when /\A\s*append\s+(\w+)\s+(.+)\z/
|
1138
|
-
config_set $1, $2
|
1128
|
+
config_set $1, $2, append: true
|
1139
1129
|
|
1140
1130
|
when /\A(\w+)\z/
|
1141
1131
|
config_show $1
|
@@ -1261,9 +1251,12 @@ module DEBUGGER__
|
|
1261
1251
|
@repl_prev_line = nil
|
1262
1252
|
|
1263
1253
|
if @bps.has_key? bp.key
|
1264
|
-
|
1254
|
+
if bp.duplicable?
|
1255
|
+
bp
|
1256
|
+
else
|
1265
1257
|
@ui.puts "duplicated breakpoint: #{bp}"
|
1266
1258
|
bp.disable
|
1259
|
+
nil
|
1267
1260
|
end
|
1268
1261
|
else
|
1269
1262
|
@bps[bp.key] = bp
|
@@ -1320,7 +1313,7 @@ module DEBUGGER__
|
|
1320
1313
|
when /\A(.+)[:\s+](\d+)\z/
|
1321
1314
|
add_line_breakpoint $1, $2.to_i, cond: cond, command: cmd
|
1322
1315
|
when /\A(.+)([\.\#])(.+)\z/
|
1323
|
-
|
1316
|
+
request_tc [:breakpoint, :method, $1, $2, $3, cond, cmd, path]
|
1324
1317
|
return :noretry
|
1325
1318
|
when nil
|
1326
1319
|
add_check_breakpoint cond, path, cmd
|
@@ -1347,11 +1340,11 @@ module DEBUGGER__
|
|
1347
1340
|
cmd = ['watch', expr[:pre], expr[:do]] if expr[:pre] || expr[:do]
|
1348
1341
|
path = Regexp.compile(expr[:path]) if expr[:path]
|
1349
1342
|
|
1350
|
-
|
1343
|
+
request_tc [:breakpoint, :watch, expr[:sig], cond, cmd, path]
|
1351
1344
|
end
|
1352
1345
|
|
1353
|
-
def add_catch_breakpoint pat
|
1354
|
-
bp = CatchBreakpoint.new(pat)
|
1346
|
+
def add_catch_breakpoint pat, cond: nil
|
1347
|
+
bp = CatchBreakpoint.new(pat, cond: cond)
|
1355
1348
|
add_bp bp
|
1356
1349
|
end
|
1357
1350
|
|
@@ -1369,15 +1362,34 @@ module DEBUGGER__
|
|
1369
1362
|
@ui.puts e.message
|
1370
1363
|
end
|
1371
1364
|
|
1372
|
-
def
|
1373
|
-
path = resolve_path(path)
|
1365
|
+
def clear_breakpoints(&condition)
|
1374
1366
|
@bps.delete_if do |k, bp|
|
1375
|
-
if
|
1367
|
+
if condition.call(k, bp)
|
1376
1368
|
bp.delete
|
1369
|
+
true
|
1377
1370
|
end
|
1378
1371
|
end
|
1379
1372
|
end
|
1380
1373
|
|
1374
|
+
def clear_line_breakpoints path
|
1375
|
+
path = resolve_path(path)
|
1376
|
+
clear_breakpoints do |k, bp|
|
1377
|
+
bp.is_a?(LineBreakpoint) && DEBUGGER__.compare_path(k.first, path)
|
1378
|
+
end
|
1379
|
+
rescue Errno::ENOENT
|
1380
|
+
# just ignore
|
1381
|
+
end
|
1382
|
+
|
1383
|
+
def clear_catch_breakpoints *exception_names
|
1384
|
+
clear_breakpoints do |k, bp|
|
1385
|
+
bp.is_a?(CatchBreakpoint) && exception_names.include?(k[1])
|
1386
|
+
end
|
1387
|
+
end
|
1388
|
+
|
1389
|
+
def clear_all_breakpoints
|
1390
|
+
clear_breakpoints{true}
|
1391
|
+
end
|
1392
|
+
|
1381
1393
|
def add_iseq_breakpoint iseq, **kw
|
1382
1394
|
bp = ISeqBreakpoint.new(iseq, [:line], **kw)
|
1383
1395
|
add_bp bp
|
@@ -1568,7 +1580,7 @@ module DEBUGGER__
|
|
1568
1580
|
DEBUGGER__.info "Leave subsession (nested #{@subsession_stack.size})"
|
1569
1581
|
end
|
1570
1582
|
|
1571
|
-
|
1583
|
+
request_tc type if type
|
1572
1584
|
@tc = nil
|
1573
1585
|
rescue Exception => e
|
1574
1586
|
STDERR.puts PP.pp([e, e.backtrace], ''.dup)
|
@@ -1583,7 +1595,9 @@ module DEBUGGER__
|
|
1583
1595
|
|
1584
1596
|
def on_load iseq, src
|
1585
1597
|
DEBUGGER__.info "Load #{iseq.absolute_path || iseq.path}"
|
1586
|
-
|
1598
|
+
|
1599
|
+
file_path, reloaded = @sr.add(iseq, src)
|
1600
|
+
@ui.event :load, file_path, reloaded
|
1587
1601
|
|
1588
1602
|
pending_line_breakpoints = @bps.find_all do |key, bp|
|
1589
1603
|
LineBreakpoint === bp && !bp.iseq
|
@@ -1591,7 +1605,18 @@ module DEBUGGER__
|
|
1591
1605
|
|
1592
1606
|
pending_line_breakpoints.each do |_key, bp|
|
1593
1607
|
if DEBUGGER__.compare_path(bp.path, (iseq.absolute_path || iseq.path))
|
1594
|
-
bp.try_activate
|
1608
|
+
bp.try_activate iseq
|
1609
|
+
end
|
1610
|
+
end
|
1611
|
+
|
1612
|
+
if reloaded
|
1613
|
+
@bps.find_all do |key, bp|
|
1614
|
+
LineBreakpoint === bp && DEBUGGER__.compare_path(bp.path, file_path)
|
1615
|
+
end.each do |_key, bp|
|
1616
|
+
@bps.delete bp.key # to allow duplicate
|
1617
|
+
if nbp = LineBreakpoint.copy(bp, iseq)
|
1618
|
+
add_bp nbp
|
1619
|
+
end
|
1595
1620
|
end
|
1596
1621
|
end
|
1597
1622
|
end
|
@@ -1618,7 +1643,7 @@ module DEBUGGER__
|
|
1618
1643
|
b = tp.binding
|
1619
1644
|
if var_name = b.local_variables.first
|
1620
1645
|
mid = b.local_variable_get(var_name)
|
1621
|
-
|
1646
|
+
resolved = true
|
1622
1647
|
|
1623
1648
|
@bps.each{|k, bp|
|
1624
1649
|
case bp
|
@@ -1629,15 +1654,53 @@ module DEBUGGER__
|
|
1629
1654
|
end
|
1630
1655
|
end
|
1631
1656
|
|
1632
|
-
|
1657
|
+
resolved = false if !bp.enabled?
|
1633
1658
|
end
|
1634
1659
|
}
|
1635
|
-
|
1636
|
-
|
1660
|
+
|
1661
|
+
if resolved
|
1662
|
+
Session.deactivate_method_added_trackers
|
1663
|
+
end
|
1664
|
+
|
1665
|
+
case mid
|
1666
|
+
when :method_added, :singleton_method_added
|
1667
|
+
Session.create_method_added_tracker(tp.self, mid)
|
1668
|
+
Session.create_method_added_tracker unless resolved
|
1637
1669
|
end
|
1638
1670
|
end
|
1639
1671
|
end
|
1640
1672
|
|
1673
|
+
class ::Module
|
1674
|
+
undef method_added
|
1675
|
+
def method_added mid; end
|
1676
|
+
def singleton_method_added mid; end
|
1677
|
+
end
|
1678
|
+
|
1679
|
+
def self.create_method_added_tracker mod, method_added_id, method_accessor = :method
|
1680
|
+
m = mod.__send__(method_accessor, method_added_id)
|
1681
|
+
METHOD_ADDED_TRACKERS[m] = TracePoint.new(:call) do |tp|
|
1682
|
+
SESSION.method_added tp
|
1683
|
+
end
|
1684
|
+
end
|
1685
|
+
|
1686
|
+
def self.activate_method_added_trackers
|
1687
|
+
METHOD_ADDED_TRACKERS.each do |m, tp|
|
1688
|
+
tp.enable(target: m) unless tp.enabled?
|
1689
|
+
rescue ArgumentError
|
1690
|
+
DEBUGGER__.warn "Methods defined under #{m.owner} can not track by the debugger."
|
1691
|
+
end
|
1692
|
+
end
|
1693
|
+
|
1694
|
+
def self.deactivate_method_added_trackers
|
1695
|
+
METHOD_ADDED_TRACKERS.each do |m, tp|
|
1696
|
+
tp.disable if tp.enabled?
|
1697
|
+
end
|
1698
|
+
end
|
1699
|
+
|
1700
|
+
METHOD_ADDED_TRACKERS = Hash.new
|
1701
|
+
create_method_added_tracker Module, :method_added, :instance_method
|
1702
|
+
create_method_added_tracker Module, :singleton_method_added, :instance_method
|
1703
|
+
|
1641
1704
|
def width
|
1642
1705
|
@ui.width
|
1643
1706
|
end
|
@@ -2051,34 +2114,53 @@ module DEBUGGER__
|
|
2051
2114
|
end
|
2052
2115
|
end
|
2053
2116
|
|
2054
|
-
|
2055
|
-
|
2056
|
-
|
2057
|
-
def singleton_method_added mid; end
|
2058
|
-
end
|
2117
|
+
# Inspector
|
2118
|
+
|
2119
|
+
SHORT_INSPECT_LENGTH = 40
|
2059
2120
|
|
2060
|
-
|
2061
|
-
|
2062
|
-
|
2063
|
-
|
2064
|
-
|
2121
|
+
class LimitedPP
|
2122
|
+
def self.pp(obj, max=80)
|
2123
|
+
out = self.new(max)
|
2124
|
+
catch out do
|
2125
|
+
PP.singleline_pp(obj, out)
|
2126
|
+
end
|
2127
|
+
out.buf
|
2065
2128
|
end
|
2066
|
-
end
|
2067
2129
|
|
2068
|
-
|
2130
|
+
attr_reader :buf
|
2069
2131
|
|
2070
|
-
|
2132
|
+
def initialize max
|
2133
|
+
@max = max
|
2134
|
+
@cnt = 0
|
2135
|
+
@buf = String.new
|
2136
|
+
end
|
2071
2137
|
|
2072
|
-
|
2073
|
-
|
2138
|
+
def <<(other)
|
2139
|
+
@buf << other
|
2074
2140
|
|
2075
|
-
|
2076
|
-
|
2141
|
+
if @buf.size >= @max
|
2142
|
+
@buf = @buf[0..@max] + '...'
|
2143
|
+
throw self
|
2144
|
+
end
|
2145
|
+
end
|
2146
|
+
end
|
2147
|
+
|
2148
|
+
def self.safe_inspect obj, max_length: SHORT_INSPECT_LENGTH, short: false
|
2149
|
+
if short
|
2150
|
+
LimitedPP.pp(obj, max_length)
|
2151
|
+
else
|
2152
|
+
obj.inspect
|
2153
|
+
end
|
2154
|
+
rescue NoMethodError => e
|
2155
|
+
klass, oid = M_CLASS.bind_call(obj), M_OBJECT_ID.bind_call(obj)
|
2156
|
+
if obj == (r = e.receiver)
|
2157
|
+
"<\##{klass.name}#{oid} does not have \#inspect>"
|
2077
2158
|
else
|
2078
|
-
|
2159
|
+
rklass, roid = M_CLASS.bind_call(r), M_OBJECT_ID.bind_call(r)
|
2160
|
+
"<\##{klass.name}:#{roid} contains <\##{rklass}:#{roid} and it does not have #inspect>"
|
2079
2161
|
end
|
2080
2162
|
rescue Exception => e
|
2081
|
-
|
2163
|
+
"<#inspect raises #{e.inspect}>"
|
2082
2164
|
end
|
2083
2165
|
|
2084
2166
|
def self.warn msg
|
@@ -2089,18 +2171,27 @@ module DEBUGGER__
|
|
2089
2171
|
log :INFO, msg
|
2090
2172
|
end
|
2091
2173
|
|
2092
|
-
def self.
|
2093
|
-
@logfile = STDERR unless defined? @logfile
|
2094
|
-
|
2174
|
+
def self.check_loglevel level
|
2095
2175
|
lv = LOG_LEVELS[level]
|
2096
|
-
config_lv = LOG_LEVELS[CONFIG[:log_level]
|
2176
|
+
config_lv = LOG_LEVELS[CONFIG[:log_level]]
|
2177
|
+
lv <= config_lv
|
2178
|
+
end
|
2097
2179
|
|
2098
|
-
|
2099
|
-
|
2100
|
-
|
2180
|
+
def self.debug(&b)
|
2181
|
+
if check_loglevel :DEBUG
|
2182
|
+
log :DEBUG, b.call
|
2101
2183
|
end
|
2184
|
+
end
|
2185
|
+
|
2186
|
+
def self.log level, msg
|
2187
|
+
if check_loglevel level
|
2188
|
+
@logfile = STDERR unless defined? @logfile
|
2189
|
+
|
2190
|
+
if defined? SESSION
|
2191
|
+
pi = SESSION.process_info
|
2192
|
+
process_info = pi ? "[#{pi}]" : nil
|
2193
|
+
end
|
2102
2194
|
|
2103
|
-
if lv <= config_lv
|
2104
2195
|
if level == :WARN
|
2105
2196
|
# :WARN on debugger is general information
|
2106
2197
|
@logfile.puts "DEBUGGER#{process_info}: #{msg}"
|
@@ -2137,7 +2228,7 @@ module DEBUGGER__
|
|
2137
2228
|
module ForkInterceptor
|
2138
2229
|
if Process.respond_to? :_fork
|
2139
2230
|
def _fork
|
2140
|
-
return
|
2231
|
+
return super unless defined?(SESSION) && SESSION.active?
|
2141
2232
|
|
2142
2233
|
parent_hook, child_hook = __fork_setup_for_debugger
|
2143
2234
|
|
@@ -2153,7 +2244,7 @@ module DEBUGGER__
|
|
2153
2244
|
end
|
2154
2245
|
else
|
2155
2246
|
def fork(&given_block)
|
2156
|
-
return
|
2247
|
+
return super unless defined?(SESSION) && SESSION.active?
|
2157
2248
|
parent_hook, child_hook = __fork_setup_for_debugger
|
2158
2249
|
|
2159
2250
|
if given_block
|
@@ -2178,12 +2269,10 @@ module DEBUGGER__
|
|
2178
2269
|
end
|
2179
2270
|
|
2180
2271
|
private def __fork_setup_for_debugger
|
2181
|
-
|
2182
|
-
|
2183
|
-
|
2184
|
-
|
2185
|
-
fork_mode = :both
|
2186
|
-
end
|
2272
|
+
fork_mode = CONFIG[:fork_mode]
|
2273
|
+
|
2274
|
+
if fork_mode == :both && CONFIG[:parent_on_fork]
|
2275
|
+
fork_mode = :parent
|
2187
2276
|
end
|
2188
2277
|
|
2189
2278
|
parent_pid = Process.pid
|
@@ -2310,3 +2399,12 @@ class Binding
|
|
2310
2399
|
alias break debugger
|
2311
2400
|
alias b debugger
|
2312
2401
|
end
|
2402
|
+
|
2403
|
+
# for Ruby 2.6 compatibility
|
2404
|
+
unless method(:p).unbind.respond_to? :bind_call
|
2405
|
+
class UnboundMethod
|
2406
|
+
def bind_call(obj, *args)
|
2407
|
+
self.bind(obj).call(*args)
|
2408
|
+
end
|
2409
|
+
end
|
2410
|
+
end
|