debug 1.5.0 → 1.6.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CONTRIBUTING.md +80 -12
- data/README.md +41 -18
- data/Rakefile +25 -7
- data/debug.gemspec +1 -1
- data/exe/rdbg +22 -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 +13 -3
- 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/prelude.rb +1 -0
- data/lib/debug/server.rb +44 -17
- data/lib/debug/server_cdp.rb +6 -6
- data/lib/debug/server_dap.rb +224 -118
- data/lib/debug/session.rb +247 -135
- data/lib/debug/source_repository.rb +13 -0
- data/lib/debug/thread_client.rb +162 -65
- data/lib/debug/tracer.rb +4 -3
- data/lib/debug/version.rb +1 -1
- data/lib/debug.rb +1 -0
- data/misc/README.md.erb +30 -10
- metadata +5 -6
- data/lib/debug/bp.vim +0 -68
data/lib/debug/session.rb
CHANGED
@@ -19,6 +19,15 @@ 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.end_with?(added_opt)
|
26
|
+
|
27
|
+
ENV['RUBYOPT'] = rubyopt.delete_suffix(added_opt)
|
28
|
+
ENV['RUBY_DEBUG_ADDED_RUBYOPT'] = nil
|
29
|
+
end
|
30
|
+
|
22
31
|
require_relative 'frame_info'
|
23
32
|
require_relative 'config'
|
24
33
|
require_relative 'thread_client'
|
@@ -32,6 +41,7 @@ $LOADED_FEATURES << File.expand_path(File.join(__dir__, '..', 'debug.rb'))
|
|
32
41
|
require 'debug' # invalidate the $LOADED_FEATURE cache
|
33
42
|
|
34
43
|
require 'json' if ENV['RUBY_DEBUG_TEST_UI'] == 'terminal'
|
44
|
+
require 'pp'
|
35
45
|
|
36
46
|
class RubyVM::InstructionSequence
|
37
47
|
def traceable_lines_norec lines
|
@@ -197,35 +207,44 @@ module DEBUGGER__
|
|
197
207
|
deactivate
|
198
208
|
end
|
199
209
|
|
210
|
+
def request_tc(req)
|
211
|
+
@tc << req
|
212
|
+
end
|
213
|
+
|
200
214
|
def process_event evt
|
201
215
|
# variable `@internal_info` is only used for test
|
202
216
|
tc, output, ev, @internal_info, *ev_args = evt
|
203
|
-
output.each{|str| @ui.puts str} if ev != :suspend
|
204
217
|
|
205
|
-
|
218
|
+
output.each{|str| @ui.puts str} if ev != :suspend
|
206
219
|
|
207
|
-
|
220
|
+
# special event, tc is nil
|
221
|
+
# and we don't want to set @tc to the newly created thread's ThreadClient
|
222
|
+
if ev == :thread_begin
|
208
223
|
th = ev_args.shift
|
209
224
|
q = ev_args.shift
|
210
225
|
on_thread_begin th
|
211
226
|
q << true
|
212
227
|
|
228
|
+
return
|
229
|
+
end
|
230
|
+
|
231
|
+
@tc = tc
|
232
|
+
|
233
|
+
case ev
|
213
234
|
when :init
|
214
235
|
enter_subsession
|
215
|
-
wait_command_loop
|
216
|
-
|
236
|
+
wait_command_loop
|
217
237
|
when :load
|
218
238
|
iseq, src = ev_args
|
219
239
|
on_load iseq, src
|
220
|
-
|
221
|
-
tc << :continue
|
240
|
+
request_tc :continue
|
222
241
|
|
223
242
|
when :trace
|
224
243
|
trace_id, msg = ev_args
|
225
244
|
if t = @tracers.values.find{|t| t.object_id == trace_id}
|
226
245
|
t.puts msg
|
227
246
|
end
|
228
|
-
|
247
|
+
request_tc :continue
|
229
248
|
|
230
249
|
when :suspend
|
231
250
|
enter_subsession if ev_args.first != :replay
|
@@ -235,24 +254,23 @@ module DEBUGGER__
|
|
235
254
|
when :breakpoint
|
236
255
|
bp, i = bp_index ev_args[1]
|
237
256
|
clean_bps unless bp
|
238
|
-
@ui.event :suspend_bp, i, bp, tc.id
|
257
|
+
@ui.event :suspend_bp, i, bp, @tc.id
|
239
258
|
when :trap
|
240
|
-
@ui.event :suspend_trap, sig = ev_args[1], tc.id
|
259
|
+
@ui.event :suspend_trap, sig = ev_args[1], @tc.id
|
241
260
|
|
242
261
|
if sig == :SIGINT && (@intercepted_sigint_cmd.kind_of?(Proc) || @intercepted_sigint_cmd.kind_of?(String))
|
243
262
|
@ui.puts "#{@intercepted_sigint_cmd.inspect} is registered as SIGINT handler."
|
244
263
|
@ui.puts "`sigint` command execute it."
|
245
264
|
end
|
246
265
|
else
|
247
|
-
@ui.event :suspended, tc.id
|
266
|
+
@ui.event :suspended, @tc.id
|
248
267
|
end
|
249
268
|
|
250
269
|
if @displays.empty?
|
251
|
-
wait_command_loop
|
270
|
+
wait_command_loop
|
252
271
|
else
|
253
|
-
|
272
|
+
request_tc [:eval, :display, @displays]
|
254
273
|
end
|
255
|
-
|
256
274
|
when :result
|
257
275
|
raise "[BUG] not in subsession" if @subsession_stack.empty?
|
258
276
|
|
@@ -283,14 +301,14 @@ module DEBUGGER__
|
|
283
301
|
# ignore
|
284
302
|
end
|
285
303
|
|
286
|
-
wait_command_loop
|
304
|
+
wait_command_loop
|
287
305
|
|
288
306
|
when :dap_result
|
289
307
|
dap_event ev_args # server.rb
|
290
|
-
wait_command_loop
|
308
|
+
wait_command_loop
|
291
309
|
when :cdp_result
|
292
310
|
cdp_event ev_args
|
293
|
-
wait_command_loop
|
311
|
+
wait_command_loop
|
294
312
|
end
|
295
313
|
end
|
296
314
|
|
@@ -323,9 +341,7 @@ module DEBUGGER__
|
|
323
341
|
"DEBUGGER__::SESSION"
|
324
342
|
end
|
325
343
|
|
326
|
-
def wait_command_loop
|
327
|
-
@tc = tc
|
328
|
-
|
344
|
+
def wait_command_loop
|
329
345
|
loop do
|
330
346
|
case wait_command
|
331
347
|
when :retry
|
@@ -529,32 +545,6 @@ module DEBUGGER__
|
|
529
545
|
end
|
530
546
|
end
|
531
547
|
|
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
548
|
# * `catch <Error>`
|
559
549
|
# * Set breakpoint on raising `<Error>`.
|
560
550
|
# * `catch ... if: <expr>`
|
@@ -632,15 +622,15 @@ module DEBUGGER__
|
|
632
622
|
when 'bt', 'backtrace'
|
633
623
|
case arg
|
634
624
|
when /\A(\d+)\z/
|
635
|
-
|
625
|
+
request_tc [:show, :backtrace, arg.to_i, nil]
|
636
626
|
when /\A\/(.*)\/\z/
|
637
627
|
pattern = $1
|
638
|
-
|
628
|
+
request_tc [:show, :backtrace, nil, Regexp.compile(pattern)]
|
639
629
|
when /\A(\d+)\s+\/(.*)\/\z/
|
640
630
|
max, pattern = $1, $2
|
641
|
-
|
631
|
+
request_tc [:show, :backtrace, max.to_i, Regexp.compile(pattern)]
|
642
632
|
else
|
643
|
-
|
633
|
+
request_tc [:show, :backtrace, nil, nil]
|
644
634
|
end
|
645
635
|
|
646
636
|
# * `l[ist]`
|
@@ -653,13 +643,13 @@ module DEBUGGER__
|
|
653
643
|
when 'l', 'list'
|
654
644
|
case arg ? arg.strip : nil
|
655
645
|
when /\A(\d+)\z/
|
656
|
-
|
646
|
+
request_tc [:show, :list, {start_line: arg.to_i - 1}]
|
657
647
|
when /\A-\z/
|
658
|
-
|
648
|
+
request_tc [:show, :list, {dir: -1}]
|
659
649
|
when /\A(\d+)-(\d+)\z/
|
660
|
-
|
650
|
+
request_tc [:show, :list, {start_line: $1.to_i - 1, end_line: $2.to_i}]
|
661
651
|
when nil
|
662
|
-
|
652
|
+
request_tc [:show, :list]
|
663
653
|
else
|
664
654
|
@ui.puts "Can not handle list argument: #{arg}"
|
665
655
|
return :retry
|
@@ -683,7 +673,7 @@ module DEBUGGER__
|
|
683
673
|
return :retry
|
684
674
|
end
|
685
675
|
|
686
|
-
|
676
|
+
request_tc [:show, :edit, arg]
|
687
677
|
|
688
678
|
# * `i[nfo]`
|
689
679
|
# * Show information about current frame (local/instance variables and defined constants).
|
@@ -710,15 +700,15 @@ module DEBUGGER__
|
|
710
700
|
|
711
701
|
case sub
|
712
702
|
when nil
|
713
|
-
|
703
|
+
request_tc [:show, :default, pat] # something useful
|
714
704
|
when 'l', /^locals?/
|
715
|
-
|
705
|
+
request_tc [:show, :locals, pat]
|
716
706
|
when 'i', /^ivars?/i, /^instance[_ ]variables?/i
|
717
|
-
|
707
|
+
request_tc [:show, :ivars, pat]
|
718
708
|
when 'c', /^consts?/i, /^constants?/i
|
719
|
-
|
709
|
+
request_tc [:show, :consts, pat]
|
720
710
|
when 'g', /^globals?/i, /^global[_ ]variables?/i
|
721
|
-
|
711
|
+
request_tc [:show, :globals, pat]
|
722
712
|
when 'th', /threads?/
|
723
713
|
thread_list
|
724
714
|
return :retry
|
@@ -734,7 +724,7 @@ module DEBUGGER__
|
|
734
724
|
# * Show you available methods and instance variables of the given object.
|
735
725
|
# * If the object is a class/module, it also lists its constants.
|
736
726
|
when 'outline', 'o', 'ls'
|
737
|
-
|
727
|
+
request_tc [:show, :outline, arg]
|
738
728
|
|
739
729
|
# * `display`
|
740
730
|
# * Show display setting.
|
@@ -743,9 +733,9 @@ module DEBUGGER__
|
|
743
733
|
when 'display'
|
744
734
|
if arg && !arg.empty?
|
745
735
|
@displays << arg
|
746
|
-
|
736
|
+
request_tc [:eval, :try_display, @displays]
|
747
737
|
else
|
748
|
-
|
738
|
+
request_tc [:eval, :display, @displays]
|
749
739
|
end
|
750
740
|
|
751
741
|
# * `undisplay`
|
@@ -758,7 +748,7 @@ module DEBUGGER__
|
|
758
748
|
if @displays[n = $1.to_i]
|
759
749
|
@displays.delete_at n
|
760
750
|
end
|
761
|
-
|
751
|
+
request_tc [:eval, :display, @displays]
|
762
752
|
when nil
|
763
753
|
if ask "clear all?", 'N'
|
764
754
|
@displays.clear
|
@@ -773,29 +763,29 @@ module DEBUGGER__
|
|
773
763
|
# * `f[rame] <framenum>`
|
774
764
|
# * Specify a current frame. Evaluation are run on specified frame.
|
775
765
|
when 'frame', 'f'
|
776
|
-
|
766
|
+
request_tc [:frame, :set, arg]
|
777
767
|
|
778
768
|
# * `up`
|
779
769
|
# * Specify the upper frame.
|
780
770
|
when 'up'
|
781
|
-
|
771
|
+
request_tc [:frame, :up]
|
782
772
|
|
783
773
|
# * `down`
|
784
774
|
# * Specify the lower frame.
|
785
775
|
when 'down'
|
786
|
-
|
776
|
+
request_tc [:frame, :down]
|
787
777
|
|
788
778
|
### Evaluate
|
789
779
|
|
790
780
|
# * `p <expr>`
|
791
781
|
# * Evaluate like `p <expr>` on the current frame.
|
792
782
|
when 'p'
|
793
|
-
|
783
|
+
request_tc [:eval, :p, arg.to_s]
|
794
784
|
|
795
785
|
# * `pp <expr>`
|
796
786
|
# * Evaluate like `pp <expr>` on the current frame.
|
797
787
|
when 'pp'
|
798
|
-
|
788
|
+
request_tc [:eval, :pp, arg.to_s]
|
799
789
|
|
800
790
|
# * `eval <expr>`
|
801
791
|
# * Evaluate `<expr>` on the current frame.
|
@@ -805,7 +795,7 @@ module DEBUGGER__
|
|
805
795
|
@ui.puts "\nTo evaluate the variable `#{cmd}`, use `pp #{cmd}` instead."
|
806
796
|
return :retry
|
807
797
|
else
|
808
|
-
|
798
|
+
request_tc [:eval, :call, arg]
|
809
799
|
end
|
810
800
|
|
811
801
|
# * `irb`
|
@@ -815,7 +805,7 @@ module DEBUGGER__
|
|
815
805
|
@ui.puts "not supported on the remote console."
|
816
806
|
return :retry
|
817
807
|
end
|
818
|
-
|
808
|
+
request_tc [:eval, :irb]
|
819
809
|
|
820
810
|
# don't repeat irb command
|
821
811
|
@repl_prev_line = nil
|
@@ -872,7 +862,7 @@ module DEBUGGER__
|
|
872
862
|
return :retry
|
873
863
|
|
874
864
|
when /\Aobject\s+(.+)/
|
875
|
-
|
865
|
+
request_tc [:trace, :object, $1.strip, {pattern: pattern, into: into}]
|
876
866
|
|
877
867
|
when /\Aoff\s+(\d+)\z/
|
878
868
|
if t = @tracers.values[$1.to_i]
|
@@ -910,7 +900,7 @@ module DEBUGGER__
|
|
910
900
|
when 'record'
|
911
901
|
case arg
|
912
902
|
when nil, 'on', 'off'
|
913
|
-
|
903
|
+
request_tc [:record, arg&.to_sym]
|
914
904
|
else
|
915
905
|
@ui.puts "unknown command: #{arg}"
|
916
906
|
return :retry
|
@@ -1005,7 +995,7 @@ module DEBUGGER__
|
|
1005
995
|
|
1006
996
|
### END
|
1007
997
|
else
|
1008
|
-
|
998
|
+
request_tc [:eval, :pp, line]
|
1009
999
|
=begin
|
1010
1000
|
@repl_prev_line = nil
|
1011
1001
|
@ui.puts "unknown command: #{line}"
|
@@ -1062,7 +1052,7 @@ module DEBUGGER__
|
|
1062
1052
|
case arg
|
1063
1053
|
when nil, /\A\d+\z/
|
1064
1054
|
if type == :in && @tc.recorder&.replaying?
|
1065
|
-
|
1055
|
+
request_tc [:step, type, arg&.to_i]
|
1066
1056
|
else
|
1067
1057
|
leave_subsession [:step, type, arg&.to_i]
|
1068
1058
|
end
|
@@ -1071,7 +1061,7 @@ module DEBUGGER__
|
|
1071
1061
|
@ui.puts "only `step #{arg}` is supported."
|
1072
1062
|
:retry
|
1073
1063
|
else
|
1074
|
-
|
1064
|
+
request_tc [:step, arg.to_sym]
|
1075
1065
|
end
|
1076
1066
|
else
|
1077
1067
|
@ui.puts "Unknown option: #{arg}"
|
@@ -1081,11 +1071,18 @@ module DEBUGGER__
|
|
1081
1071
|
|
1082
1072
|
def config_show key
|
1083
1073
|
key = key.to_sym
|
1084
|
-
|
1074
|
+
config_detail = CONFIG_SET[key]
|
1075
|
+
|
1076
|
+
if config_detail
|
1085
1077
|
v = CONFIG[key]
|
1086
|
-
kv = "#{key} = #{v.
|
1087
|
-
desc =
|
1088
|
-
|
1078
|
+
kv = "#{key} = #{v.inspect}"
|
1079
|
+
desc = config_detail[1]
|
1080
|
+
|
1081
|
+
if config_default = config_detail[3]
|
1082
|
+
desc += " (default: #{config_default})"
|
1083
|
+
end
|
1084
|
+
|
1085
|
+
line = "%-34s \# %s" % [kv, desc]
|
1089
1086
|
if line.size > SESSION.width
|
1090
1087
|
@ui.puts "\# #{desc}\n#{kv}"
|
1091
1088
|
else
|
@@ -1135,7 +1132,7 @@ module DEBUGGER__
|
|
1135
1132
|
config_set $1, $2, append: true
|
1136
1133
|
|
1137
1134
|
when /\A\s*append\s+(\w+)\s+(.+)\z/
|
1138
|
-
config_set $1, $2
|
1135
|
+
config_set $1, $2, append: true
|
1139
1136
|
|
1140
1137
|
when /\A(\w+)\z/
|
1141
1138
|
config_show $1
|
@@ -1261,9 +1258,12 @@ module DEBUGGER__
|
|
1261
1258
|
@repl_prev_line = nil
|
1262
1259
|
|
1263
1260
|
if @bps.has_key? bp.key
|
1264
|
-
|
1261
|
+
if bp.duplicable?
|
1262
|
+
bp
|
1263
|
+
else
|
1265
1264
|
@ui.puts "duplicated breakpoint: #{bp}"
|
1266
1265
|
bp.disable
|
1266
|
+
nil
|
1267
1267
|
end
|
1268
1268
|
else
|
1269
1269
|
@bps[bp.key] = bp
|
@@ -1320,7 +1320,7 @@ module DEBUGGER__
|
|
1320
1320
|
when /\A(.+)[:\s+](\d+)\z/
|
1321
1321
|
add_line_breakpoint $1, $2.to_i, cond: cond, command: cmd
|
1322
1322
|
when /\A(.+)([\.\#])(.+)\z/
|
1323
|
-
|
1323
|
+
request_tc [:breakpoint, :method, $1, $2, $3, cond, cmd, path]
|
1324
1324
|
return :noretry
|
1325
1325
|
when nil
|
1326
1326
|
add_check_breakpoint cond, path, cmd
|
@@ -1347,11 +1347,11 @@ module DEBUGGER__
|
|
1347
1347
|
cmd = ['watch', expr[:pre], expr[:do]] if expr[:pre] || expr[:do]
|
1348
1348
|
path = Regexp.compile(expr[:path]) if expr[:path]
|
1349
1349
|
|
1350
|
-
|
1350
|
+
request_tc [:breakpoint, :watch, expr[:sig], cond, cmd, path]
|
1351
1351
|
end
|
1352
1352
|
|
1353
|
-
def add_catch_breakpoint pat
|
1354
|
-
bp = CatchBreakpoint.new(pat)
|
1353
|
+
def add_catch_breakpoint pat, cond: nil
|
1354
|
+
bp = CatchBreakpoint.new(pat, cond: cond)
|
1355
1355
|
add_bp bp
|
1356
1356
|
end
|
1357
1357
|
|
@@ -1369,15 +1369,34 @@ module DEBUGGER__
|
|
1369
1369
|
@ui.puts e.message
|
1370
1370
|
end
|
1371
1371
|
|
1372
|
-
def
|
1373
|
-
path = resolve_path(path)
|
1372
|
+
def clear_breakpoints(&condition)
|
1374
1373
|
@bps.delete_if do |k, bp|
|
1375
|
-
if
|
1374
|
+
if condition.call(k, bp)
|
1376
1375
|
bp.delete
|
1376
|
+
true
|
1377
1377
|
end
|
1378
1378
|
end
|
1379
1379
|
end
|
1380
1380
|
|
1381
|
+
def clear_line_breakpoints path
|
1382
|
+
path = resolve_path(path)
|
1383
|
+
clear_breakpoints do |k, bp|
|
1384
|
+
bp.is_a?(LineBreakpoint) && DEBUGGER__.compare_path(k.first, path)
|
1385
|
+
end
|
1386
|
+
rescue Errno::ENOENT
|
1387
|
+
# just ignore
|
1388
|
+
end
|
1389
|
+
|
1390
|
+
def clear_catch_breakpoints *exception_names
|
1391
|
+
clear_breakpoints do |k, bp|
|
1392
|
+
bp.is_a?(CatchBreakpoint) && exception_names.include?(k[1])
|
1393
|
+
end
|
1394
|
+
end
|
1395
|
+
|
1396
|
+
def clear_all_breakpoints
|
1397
|
+
clear_breakpoints{true}
|
1398
|
+
end
|
1399
|
+
|
1381
1400
|
def add_iseq_breakpoint iseq, **kw
|
1382
1401
|
bp = ISeqBreakpoint.new(iseq, [:line], **kw)
|
1383
1402
|
add_bp bp
|
@@ -1568,7 +1587,7 @@ module DEBUGGER__
|
|
1568
1587
|
DEBUGGER__.info "Leave subsession (nested #{@subsession_stack.size})"
|
1569
1588
|
end
|
1570
1589
|
|
1571
|
-
|
1590
|
+
request_tc type if type
|
1572
1591
|
@tc = nil
|
1573
1592
|
rescue Exception => e
|
1574
1593
|
STDERR.puts PP.pp([e, e.backtrace], ''.dup)
|
@@ -1583,7 +1602,9 @@ module DEBUGGER__
|
|
1583
1602
|
|
1584
1603
|
def on_load iseq, src
|
1585
1604
|
DEBUGGER__.info "Load #{iseq.absolute_path || iseq.path}"
|
1586
|
-
|
1605
|
+
|
1606
|
+
file_path, reloaded = @sr.add(iseq, src)
|
1607
|
+
@ui.event :load, file_path, reloaded
|
1587
1608
|
|
1588
1609
|
pending_line_breakpoints = @bps.find_all do |key, bp|
|
1589
1610
|
LineBreakpoint === bp && !bp.iseq
|
@@ -1591,7 +1612,18 @@ module DEBUGGER__
|
|
1591
1612
|
|
1592
1613
|
pending_line_breakpoints.each do |_key, bp|
|
1593
1614
|
if DEBUGGER__.compare_path(bp.path, (iseq.absolute_path || iseq.path))
|
1594
|
-
bp.try_activate
|
1615
|
+
bp.try_activate iseq
|
1616
|
+
end
|
1617
|
+
end
|
1618
|
+
|
1619
|
+
if reloaded
|
1620
|
+
@bps.find_all do |key, bp|
|
1621
|
+
LineBreakpoint === bp && DEBUGGER__.compare_path(bp.path, file_path)
|
1622
|
+
end.each do |_key, bp|
|
1623
|
+
@bps.delete bp.key # to allow duplicate
|
1624
|
+
if nbp = LineBreakpoint.copy(bp, iseq)
|
1625
|
+
add_bp nbp
|
1626
|
+
end
|
1595
1627
|
end
|
1596
1628
|
end
|
1597
1629
|
end
|
@@ -1616,9 +1648,10 @@ module DEBUGGER__
|
|
1616
1648
|
|
1617
1649
|
def method_added tp
|
1618
1650
|
b = tp.binding
|
1651
|
+
|
1619
1652
|
if var_name = b.local_variables.first
|
1620
1653
|
mid = b.local_variable_get(var_name)
|
1621
|
-
|
1654
|
+
resolved = true
|
1622
1655
|
|
1623
1656
|
@bps.each{|k, bp|
|
1624
1657
|
case bp
|
@@ -1629,15 +1662,57 @@ module DEBUGGER__
|
|
1629
1662
|
end
|
1630
1663
|
end
|
1631
1664
|
|
1632
|
-
|
1665
|
+
resolved = false if !bp.enabled?
|
1633
1666
|
end
|
1634
1667
|
}
|
1635
|
-
|
1636
|
-
|
1668
|
+
|
1669
|
+
if resolved
|
1670
|
+
Session.deactivate_method_added_trackers
|
1671
|
+
end
|
1672
|
+
|
1673
|
+
case mid
|
1674
|
+
when :method_added, :singleton_method_added
|
1675
|
+
Session.create_method_added_tracker(tp.self, mid)
|
1676
|
+
Session.activate_method_added_trackers unless resolved
|
1637
1677
|
end
|
1638
1678
|
end
|
1639
1679
|
end
|
1640
1680
|
|
1681
|
+
class ::Module
|
1682
|
+
undef method_added
|
1683
|
+
def method_added mid; end
|
1684
|
+
end
|
1685
|
+
|
1686
|
+
class ::BasicObject
|
1687
|
+
undef singleton_method_added
|
1688
|
+
def singleton_method_added mid; end
|
1689
|
+
end
|
1690
|
+
|
1691
|
+
def self.create_method_added_tracker mod, method_added_id, method_accessor = :method
|
1692
|
+
m = mod.__send__(method_accessor, method_added_id)
|
1693
|
+
METHOD_ADDED_TRACKERS[m] = TracePoint.new(:call) do |tp|
|
1694
|
+
SESSION.method_added tp
|
1695
|
+
end
|
1696
|
+
end
|
1697
|
+
|
1698
|
+
def self.activate_method_added_trackers
|
1699
|
+
METHOD_ADDED_TRACKERS.each do |m, tp|
|
1700
|
+
tp.enable(target: m) unless tp.enabled?
|
1701
|
+
rescue ArgumentError
|
1702
|
+
DEBUGGER__.warn "Methods defined under #{m.owner} can not track by the debugger."
|
1703
|
+
end
|
1704
|
+
end
|
1705
|
+
|
1706
|
+
def self.deactivate_method_added_trackers
|
1707
|
+
METHOD_ADDED_TRACKERS.each do |m, tp|
|
1708
|
+
tp.disable if tp.enabled?
|
1709
|
+
end
|
1710
|
+
end
|
1711
|
+
|
1712
|
+
METHOD_ADDED_TRACKERS = Hash.new
|
1713
|
+
create_method_added_tracker Module, :method_added, :instance_method
|
1714
|
+
create_method_added_tracker BasicObject, :singleton_method_added, :instance_method
|
1715
|
+
|
1641
1716
|
def width
|
1642
1717
|
@ui.width
|
1643
1718
|
end
|
@@ -1818,9 +1893,11 @@ module DEBUGGER__
|
|
1818
1893
|
|
1819
1894
|
def after_fork child: true
|
1820
1895
|
if child || !@lock_file
|
1821
|
-
@m = Mutex.new
|
1822
|
-
@
|
1823
|
-
|
1896
|
+
@m = Mutex.new unless @m
|
1897
|
+
@m.synchronize do
|
1898
|
+
@lock_level = 0
|
1899
|
+
@lock_file = open(@lock_tempfile.path, 'w')
|
1900
|
+
end
|
1824
1901
|
end
|
1825
1902
|
end
|
1826
1903
|
|
@@ -2051,34 +2128,53 @@ module DEBUGGER__
|
|
2051
2128
|
end
|
2052
2129
|
end
|
2053
2130
|
|
2054
|
-
|
2055
|
-
undef method_added
|
2056
|
-
def method_added mid; end
|
2057
|
-
def singleton_method_added mid; end
|
2058
|
-
end
|
2131
|
+
# Inspector
|
2059
2132
|
|
2060
|
-
|
2061
|
-
|
2062
|
-
|
2063
|
-
|
2064
|
-
|
2133
|
+
SHORT_INSPECT_LENGTH = 40
|
2134
|
+
|
2135
|
+
class LimitedPP
|
2136
|
+
def self.pp(obj, max=80)
|
2137
|
+
out = self.new(max)
|
2138
|
+
catch out do
|
2139
|
+
PP.singleline_pp(obj, out)
|
2140
|
+
end
|
2141
|
+
out.buf
|
2065
2142
|
end
|
2066
|
-
end
|
2067
2143
|
|
2068
|
-
|
2144
|
+
attr_reader :buf
|
2069
2145
|
|
2070
|
-
|
2146
|
+
def initialize max
|
2147
|
+
@max = max
|
2148
|
+
@cnt = 0
|
2149
|
+
@buf = String.new
|
2150
|
+
end
|
2071
2151
|
|
2072
|
-
|
2073
|
-
|
2152
|
+
def <<(other)
|
2153
|
+
@buf << other
|
2154
|
+
|
2155
|
+
if @buf.size >= @max
|
2156
|
+
@buf = @buf[0..@max] + '...'
|
2157
|
+
throw self
|
2158
|
+
end
|
2159
|
+
end
|
2160
|
+
end
|
2074
2161
|
|
2075
|
-
|
2076
|
-
|
2162
|
+
def self.safe_inspect obj, max_length: SHORT_INSPECT_LENGTH, short: false
|
2163
|
+
if short
|
2164
|
+
LimitedPP.pp(obj, max_length)
|
2165
|
+
else
|
2166
|
+
obj.inspect
|
2167
|
+
end
|
2168
|
+
rescue NoMethodError => e
|
2169
|
+
klass, oid = M_CLASS.bind_call(obj), M_OBJECT_ID.bind_call(obj)
|
2170
|
+
if obj == (r = e.receiver)
|
2171
|
+
"<\##{klass.name}#{oid} does not have \#inspect>"
|
2077
2172
|
else
|
2078
|
-
|
2173
|
+
rklass, roid = M_CLASS.bind_call(r), M_OBJECT_ID.bind_call(r)
|
2174
|
+
"<\##{klass.name}:#{roid} contains <\##{rklass}:#{roid} and it does not have #inspect>"
|
2079
2175
|
end
|
2080
2176
|
rescue Exception => e
|
2081
|
-
|
2177
|
+
"<#inspect raises #{e.inspect}>"
|
2082
2178
|
end
|
2083
2179
|
|
2084
2180
|
def self.warn msg
|
@@ -2089,18 +2185,27 @@ module DEBUGGER__
|
|
2089
2185
|
log :INFO, msg
|
2090
2186
|
end
|
2091
2187
|
|
2092
|
-
def self.
|
2093
|
-
@logfile = STDERR unless defined? @logfile
|
2094
|
-
|
2188
|
+
def self.check_loglevel level
|
2095
2189
|
lv = LOG_LEVELS[level]
|
2096
|
-
config_lv = LOG_LEVELS[CONFIG[:log_level]
|
2190
|
+
config_lv = LOG_LEVELS[CONFIG[:log_level]]
|
2191
|
+
lv <= config_lv
|
2192
|
+
end
|
2097
2193
|
|
2098
|
-
|
2099
|
-
|
2100
|
-
|
2194
|
+
def self.debug(&b)
|
2195
|
+
if check_loglevel :DEBUG
|
2196
|
+
log :DEBUG, b.call
|
2101
2197
|
end
|
2198
|
+
end
|
2199
|
+
|
2200
|
+
def self.log level, msg
|
2201
|
+
if check_loglevel level
|
2202
|
+
@logfile = STDERR unless defined? @logfile
|
2203
|
+
|
2204
|
+
if defined? SESSION
|
2205
|
+
pi = SESSION.process_info
|
2206
|
+
process_info = pi ? "[#{pi}]" : nil
|
2207
|
+
end
|
2102
2208
|
|
2103
|
-
if lv <= config_lv
|
2104
2209
|
if level == :WARN
|
2105
2210
|
# :WARN on debugger is general information
|
2106
2211
|
@logfile.puts "DEBUGGER#{process_info}: #{msg}"
|
@@ -2126,7 +2231,7 @@ module DEBUGGER__
|
|
2126
2231
|
# depend on the file system. So this check is only roughly estimation.
|
2127
2232
|
|
2128
2233
|
def self.compare_path(a, b)
|
2129
|
-
a
|
2234
|
+
a&.downcase == b&.downcase
|
2130
2235
|
end
|
2131
2236
|
else
|
2132
2237
|
def self.compare_path(a, b)
|
@@ -2137,7 +2242,7 @@ module DEBUGGER__
|
|
2137
2242
|
module ForkInterceptor
|
2138
2243
|
if Process.respond_to? :_fork
|
2139
2244
|
def _fork
|
2140
|
-
return
|
2245
|
+
return super unless defined?(SESSION) && SESSION.active?
|
2141
2246
|
|
2142
2247
|
parent_hook, child_hook = __fork_setup_for_debugger
|
2143
2248
|
|
@@ -2153,7 +2258,7 @@ module DEBUGGER__
|
|
2153
2258
|
end
|
2154
2259
|
else
|
2155
2260
|
def fork(&given_block)
|
2156
|
-
return
|
2261
|
+
return super unless defined?(SESSION) && SESSION.active?
|
2157
2262
|
parent_hook, child_hook = __fork_setup_for_debugger
|
2158
2263
|
|
2159
2264
|
if given_block
|
@@ -2178,12 +2283,10 @@ module DEBUGGER__
|
|
2178
2283
|
end
|
2179
2284
|
|
2180
2285
|
private def __fork_setup_for_debugger
|
2181
|
-
|
2182
|
-
|
2183
|
-
|
2184
|
-
|
2185
|
-
fork_mode = :both
|
2186
|
-
end
|
2286
|
+
fork_mode = CONFIG[:fork_mode]
|
2287
|
+
|
2288
|
+
if fork_mode == :both && CONFIG[:parent_on_fork]
|
2289
|
+
fork_mode = :parent
|
2187
2290
|
end
|
2188
2291
|
|
2189
2292
|
parent_pid = Process.pid
|
@@ -2310,3 +2413,12 @@ class Binding
|
|
2310
2413
|
alias break debugger
|
2311
2414
|
alias b debugger
|
2312
2415
|
end
|
2416
|
+
|
2417
|
+
# for Ruby 2.6 compatibility
|
2418
|
+
unless method(:p).unbind.respond_to? :bind_call
|
2419
|
+
class UnboundMethod
|
2420
|
+
def bind_call(obj, *args)
|
2421
|
+
self.bind(obj).call(*args)
|
2422
|
+
end
|
2423
|
+
end
|
2424
|
+
end
|