debug 1.8.0 → 1.11.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.
@@ -2,7 +2,6 @@
2
2
 
3
3
  require 'json'
4
4
  require 'digest/sha1'
5
- require 'base64'
6
5
  require 'securerandom'
7
6
  require 'stringio'
8
7
  require 'open3'
@@ -59,7 +58,7 @@ module DEBUGGER__
59
58
  ws_client.send sessionId: s_id, id: 5,
60
59
  method: 'Page.navigate',
61
60
  params: {
62
- url: "devtools://devtools/bundled/inspector.html?v8only=true&panel=sources&ws=#{addr}/#{uuid}",
61
+ url: "devtools://devtools/bundled/inspector.html?v8only=true&panel=sources&noJavaScriptCompletion=true&ws=#{addr}/#{uuid}",
63
62
  frameId: f_id
64
63
  }
65
64
  when 101
@@ -131,7 +130,7 @@ module DEBUGGER__
131
130
  stdout.close
132
131
  data = stderr.readpartial 4096
133
132
  stderr.close
134
- if data.match /DevTools listening on ws:\/\/127.0.0.1:(\d+)(.*)/
133
+ if data.match(/DevTools listening on ws:\/\/127.0.0.1:(\d+)(.*)/)
135
134
  port = $1
136
135
  path = $2
137
136
  end
@@ -158,7 +157,7 @@ module DEBUGGER__
158
157
  raise NotFoundChromeEndpointError
159
158
  end
160
159
  stderr.close
161
- if data.match /DevTools listening on ws:\/\/127.0.0.1:(\d+)(.*)/
160
+ if data.match(/DevTools listening on ws:\/\/127.0.0.1:(\d+)(.*)/)
162
161
  port = $1
163
162
  path = $2
164
163
  end
@@ -190,7 +189,7 @@ module DEBUGGER__
190
189
  while i < ITERATIONS
191
190
  i += 1
192
191
  if File.exist?(tf) && data = File.read(tf)
193
- if data.match /DevTools listening on ws:\/\/127.0.0.1:(\d+)(.*)/
192
+ if data.match(/DevTools listening on ws:\/\/127.0.0.1:(\d+)(.*)/)
194
193
  port = $1
195
194
  path = $2
196
195
  return [port, path]
@@ -297,8 +296,8 @@ module DEBUGGER__
297
296
  res = @sock.readpartial 4092
298
297
  show_protocol :<, res
299
298
 
300
- if res.match /^Sec-WebSocket-Accept: (.*)\r\n/
301
- correct_key = Base64.strict_encode64 Digest::SHA1.digest "#{key}==258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
299
+ if res.match(/^Sec-WebSocket-Accept: (.*)\r\n/)
300
+ correct_key = Digest::SHA1.base64digest "#{key}==258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
302
301
  raise "The Sec-WebSocket-Accept value: #{$1} is not valid" unless $1 == correct_key
303
302
  else
304
303
  raise "Unknown response: #{res}"
@@ -378,8 +377,8 @@ module DEBUGGER__
378
377
  req = @sock.readpartial 4096
379
378
  show_protocol '>', req
380
379
 
381
- if req.match /^Sec-WebSocket-Key: (.*)\r\n/
382
- accept = Base64.strict_encode64 Digest::SHA1.digest "#{$1}258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
380
+ if req.match(/^Sec-WebSocket-Key: (.*)\r\n/)
381
+ accept = Digest::SHA1.base64digest "#{$1}258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
383
382
  res = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: #{accept}\r\n\r\n"
384
383
  @sock.print res
385
384
  show_protocol :<, res
@@ -474,7 +473,7 @@ module DEBUGGER__
474
473
  when 'Debugger.getScriptSource'
475
474
  @q_msg << req
476
475
  when 'Debugger.enable'
477
- send_response req
476
+ send_response req, debuggerId: rand.to_s
478
477
  @q_msg << req
479
478
  when 'Runtime.enable'
480
479
  send_response req
@@ -658,7 +657,7 @@ module DEBUGGER__
658
657
 
659
658
  def activate_bp bps
660
659
  bps.each_key{|k|
661
- if k.match /^\d+:(\d+):(.*)/
660
+ if k.match(/^\d+:(\d+):(.*)/)
662
661
  line = $1
663
662
  path = $2
664
663
  SESSION.add_line_breakpoint(path, line.to_i + 1)
@@ -689,7 +688,7 @@ module DEBUGGER__
689
688
  yield $stderr
690
689
  end
691
690
 
692
- def puts result=''
691
+ def puts result = ""
693
692
  # STDERR.puts "puts: #{result}"
694
693
  # send_event 'output', category: 'stderr', output: "PUTS!!: " + result.to_s
695
694
  end
@@ -501,7 +501,7 @@ module DEBUGGER__
501
501
  send_response(req, **res)
502
502
  end
503
503
 
504
- def puts result
504
+ def puts result = ""
505
505
  # STDERR.puts "puts: #{result}"
506
506
  send_event 'output', category: 'console', output: "#{result&.chomp}\n"
507
507
  end
@@ -788,7 +788,9 @@ module DEBUGGER__
788
788
 
789
789
  def dap_eval b, expr, _context, prompt: '(repl_eval)'
790
790
  begin
791
- b.eval(expr.to_s, prompt)
791
+ tp_allow_reentry do
792
+ b.eval(expr.to_s, prompt)
793
+ end
792
794
  rescue Exception => e
793
795
  e
794
796
  end
data/lib/debug/session.rb CHANGED
@@ -82,7 +82,7 @@ class RubyVM::InstructionSequence
82
82
  def first_line
83
83
  self.to_a[4][:code_location][0]
84
84
  end unless method_defined?(:first_line)
85
- end
85
+ end if defined?(RubyVM::InstructionSequence)
86
86
 
87
87
  module DEBUGGER__
88
88
  PresetCommands = Struct.new(:commands, :source, :auto_continue)
@@ -133,7 +133,7 @@ module DEBUGGER__
133
133
  @commands = {}
134
134
  @unsafe_context = false
135
135
 
136
- @has_keep_script_lines = RubyVM.respond_to? :keep_script_lines
136
+ @has_keep_script_lines = defined?(RubyVM.keep_script_lines)
137
137
 
138
138
  @tp_load_script = TracePoint.new(:script_compiled){|tp|
139
139
  eval_script = tp.eval_script unless @has_keep_script_lines
@@ -209,6 +209,12 @@ module DEBUGGER__
209
209
  first_q << :ok
210
210
 
211
211
  q.pop
212
+
213
+ # For activating irb:rdbg with startup config like `RUBY_DEBUG_IRB_CONSOLE=1`
214
+ # Because in that case the `Config#if_updated` callback would not be triggered
215
+ if CONFIG[:irb_console] && !CONFIG[:open]
216
+ activate_irb_integration
217
+ end
212
218
  end
213
219
 
214
220
  def deactivate
@@ -256,6 +262,15 @@ module DEBUGGER__
256
262
  @tc << req
257
263
  end
258
264
 
265
+ def request_tc_with_restarted_threads(req)
266
+ restart_all_threads
267
+ request_tc(req)
268
+ end
269
+
270
+ def request_eval type, src
271
+ request_tc_with_restarted_threads [:eval, type, src]
272
+ end
273
+
259
274
  def process_event evt
260
275
  # variable `@internal_info` is only used for test
261
276
  tc, output, ev, @internal_info, *ev_args = evt
@@ -314,7 +329,7 @@ module DEBUGGER__
314
329
  if @displays.empty?
315
330
  wait_command_loop
316
331
  else
317
- request_tc [:eval, :display, @displays]
332
+ request_eval :display, @displays
318
333
  end
319
334
  when :result
320
335
  raise "[BUG] not in subsession" if @subsession_stack.empty?
@@ -329,6 +344,7 @@ module DEBUGGER__
329
344
  end
330
345
  end
331
346
 
347
+ stop_all_threads
332
348
  when :method_breakpoint, :watch_breakpoint
333
349
  bp = ev_args[1]
334
350
  if bp
@@ -342,6 +358,7 @@ module DEBUGGER__
342
358
  obj_inspect = ev_args[2]
343
359
  opt = ev_args[3]
344
360
  add_tracer ObjectTracer.new(@ui, obj_id, obj_inspect, **opt)
361
+ stop_all_threads
345
362
  else
346
363
  stop_all_threads
347
364
  end
@@ -685,15 +702,15 @@ module DEBUGGER__
685
702
  register_command 'bt', 'backtrace', unsafe: false do |arg|
686
703
  case arg
687
704
  when /\A(\d+)\z/
688
- request_tc [:show, :backtrace, arg.to_i, nil]
705
+ request_tc_with_restarted_threads [:show, :backtrace, arg.to_i, nil]
689
706
  when /\A\/(.*)\/\z/
690
707
  pattern = $1
691
- request_tc [:show, :backtrace, nil, Regexp.compile(pattern)]
708
+ request_tc_with_restarted_threads [:show, :backtrace, nil, Regexp.compile(pattern)]
692
709
  when /\A(\d+)\s+\/(.*)\/\z/
693
710
  max, pattern = $1, $2
694
- request_tc [:show, :backtrace, max.to_i, Regexp.compile(pattern)]
711
+ request_tc_with_restarted_threads [:show, :backtrace, max.to_i, Regexp.compile(pattern)]
695
712
  else
696
- request_tc [:show, :backtrace, nil, nil]
713
+ request_tc_with_restarted_threads [:show, :backtrace, nil, nil]
697
714
  end
698
715
  end
699
716
 
@@ -810,15 +827,15 @@ module DEBUGGER__
810
827
 
811
828
  case sub
812
829
  when nil
813
- request_tc [:show, :default, pat] # something useful
830
+ request_tc_with_restarted_threads [:show, :default, pat] # something useful
814
831
  when :locals
815
- request_tc [:show, :locals, pat]
832
+ request_tc_with_restarted_threads [:show, :locals, pat]
816
833
  when :ivars
817
- request_tc [:show, :ivars, pat, opt]
834
+ request_tc_with_restarted_threads [:show, :ivars, pat, opt]
818
835
  when :consts
819
- request_tc [:show, :consts, pat, opt]
836
+ request_tc_with_restarted_threads [:show, :consts, pat, opt]
820
837
  when :globals
821
- request_tc [:show, :globals, pat]
838
+ request_tc_with_restarted_threads [:show, :globals, pat]
822
839
  when :threads
823
840
  thread_list
824
841
  :retry
@@ -838,7 +855,7 @@ module DEBUGGER__
838
855
  # * Show you available methods and instance variables of the given object.
839
856
  # * If the object is a class/module, it also lists its constants.
840
857
  register_command 'outline', 'o', 'ls', unsafe: false do |arg|
841
- request_tc [:show, :outline, arg]
858
+ request_tc_with_restarted_threads [:show, :outline, arg]
842
859
  end
843
860
 
844
861
  # * `display`
@@ -848,9 +865,9 @@ module DEBUGGER__
848
865
  register_command 'display', postmortem: false do |arg|
849
866
  if arg && !arg.empty?
850
867
  @displays << arg
851
- request_tc [:eval, :try_display, @displays]
868
+ request_eval :try_display, @displays
852
869
  else
853
- request_tc [:eval, :display, @displays]
870
+ request_eval :display, @displays
854
871
  end
855
872
  end
856
873
 
@@ -864,7 +881,7 @@ module DEBUGGER__
864
881
  if @displays[n = $1.to_i]
865
882
  @displays.delete_at n
866
883
  end
867
- request_tc [:eval, :display, @displays]
884
+ request_eval :display, @displays
868
885
  when nil
869
886
  if ask "clear all?", 'N'
870
887
  @displays.clear
@@ -914,7 +931,6 @@ module DEBUGGER__
914
931
  register_command 'eval', 'call' do |arg|
915
932
  if arg == nil || arg.empty?
916
933
  show_help 'eval'
917
- @ui.puts "\nTo evaluate the variable `#{cmd}`, use `pp #{cmd}` instead."
918
934
  :retry
919
935
  else
920
936
  request_eval :call, arg
@@ -922,13 +938,15 @@ module DEBUGGER__
922
938
  end
923
939
 
924
940
  # * `irb`
925
- # * Invoke `irb` on the current frame.
941
+ # * Activate and switch to `irb:rdbg` console
926
942
  register_command 'irb' do |arg|
927
943
  if @ui.remote?
928
- @ui.puts "not supported on the remote console."
929
- :retry
944
+ @ui.puts "\nIRB is not supported on the remote console."
945
+ else
946
+ config_set :irb_console, true
930
947
  end
931
- request_eval :irb, nil
948
+
949
+ :retry
932
950
  end
933
951
 
934
952
  ### Trace
@@ -983,7 +1001,7 @@ module DEBUGGER__
983
1001
  :retry
984
1002
 
985
1003
  when /\Aobject\s+(.+)/
986
- request_tc [:trace, :object, $1.strip, {pattern: pattern, into: into}]
1004
+ request_tc_with_restarted_threads [:trace, :object, $1.strip, {pattern: pattern, into: into}]
987
1005
 
988
1006
  when /\Aoff\s+(\d+)\z/
989
1007
  if t = @tracers.values[$1.to_i]
@@ -1125,7 +1143,7 @@ module DEBUGGER__
1125
1143
 
1126
1144
  def process_command line
1127
1145
  if line.empty?
1128
- if @repl_prev_line
1146
+ if @repl_prev_line && !CONFIG[:no_repeat]
1129
1147
  line = @repl_prev_line
1130
1148
  else
1131
1149
  return :retry
@@ -1164,11 +1182,6 @@ module DEBUGGER__
1164
1182
  return :retry
1165
1183
  end
1166
1184
 
1167
- def request_eval type, src
1168
- restart_all_threads
1169
- request_tc [:eval, type, src]
1170
- end
1171
-
1172
1185
  def step_command type, arg
1173
1186
  if type == :until
1174
1187
  leave_subsession [:step, type, arg]
@@ -1739,15 +1752,19 @@ module DEBUGGER__
1739
1752
  # check breakpoints
1740
1753
  if file_path
1741
1754
  @bps.find_all do |_key, bp|
1742
- LineBreakpoint === bp && bp.path_is?(file_path)
1755
+ LineBreakpoint === bp && bp.path_is?(file_path) && (iseq.first_lineno..iseq.last_line).cover?(bp.line)
1743
1756
  end.each do |_key, bp|
1744
1757
  if !bp.iseq
1745
1758
  bp.try_activate iseq
1746
1759
  elsif reloaded
1747
1760
  @bps.delete bp.key # to allow duplicate
1748
- if nbp = LineBreakpoint.copy(bp, iseq)
1749
- add_bp nbp
1750
- end
1761
+
1762
+ # When we delete a breakpoint from the @bps hash, we also need to deactivate it or else its tracepoint event
1763
+ # will continue to be enabled and we'll suspend on ghost breakpoints
1764
+ bp.delete
1765
+
1766
+ nbp = LineBreakpoint.copy(bp, iseq)
1767
+ add_bp nbp
1751
1768
  end
1752
1769
  end
1753
1770
  else # !file_path => file_path is not existing
@@ -1860,6 +1877,12 @@ module DEBUGGER__
1860
1877
  end
1861
1878
  end
1862
1879
 
1880
+ def activate_irb_integration
1881
+ require_relative "irb_integration"
1882
+ thc = get_thread_client(@session_server)
1883
+ thc.activate_irb_integration
1884
+ end
1885
+
1863
1886
  def enter_postmortem_session exc
1864
1887
  return unless exc.instance_variable_defined? :@__debugger_postmortem_frames
1865
1888
 
@@ -2174,6 +2197,7 @@ module DEBUGGER__
2174
2197
  case loc.absolute_path
2175
2198
  when dir_prefix
2176
2199
  when %r{rubygems/core_ext/kernel_require\.rb}
2200
+ when %r{bundled_gems\.rb}
2177
2201
  else
2178
2202
  return loc if loc.absolute_path
2179
2203
  end
@@ -2276,11 +2300,20 @@ module DEBUGGER__
2276
2300
  end
2277
2301
 
2278
2302
  def self.load_rc
2279
- [[File.expand_path('~/.rdbgrc'), true],
2280
- [File.expand_path('~/.rdbgrc.rb'), true],
2281
- # ['./.rdbgrc', true], # disable because of security concern
2282
- [CONFIG[:init_script], false],
2283
- ].each{|(path, rc)|
2303
+ rc_file_paths = [
2304
+ [File.expand_path('~/.rdbgrc'), true],
2305
+ [File.expand_path('~/.rdbgrc.rb'), true],
2306
+ # ['./.rdbgrc', true], # disable because of security concern
2307
+ ]
2308
+
2309
+ if (xdg_home = ENV["XDG_CONFIG_HOME"])
2310
+ rc_file_paths << [File.expand_path(File.join(xdg_home, "rdbg", "config")), true]
2311
+ rc_file_paths << [File.expand_path(File.join(xdg_home, "rdbg", "config.rb")), true]
2312
+ end
2313
+
2314
+ rc_file_paths << [CONFIG[:init_script], false]
2315
+
2316
+ rc_file_paths.each{|(path, rc)|
2284
2317
  next unless path
2285
2318
  next if rc && CONFIG[:no_rc] # ignore rc
2286
2319
 
@@ -2534,7 +2567,7 @@ module DEBUGGER__
2534
2567
  sig
2535
2568
  end
2536
2569
 
2537
- case sig&.to_s&.to_sym
2570
+ case sym
2538
2571
  when :INT, :SIGINT
2539
2572
  if defined?(SESSION) && SESSION.active? && SESSION.intercept_trap_sigint?
2540
2573
  return SESSION.save_int_trap(command.empty? ? command_proc : command.first)
@@ -22,7 +22,7 @@ module DEBUGGER__
22
22
  end
23
23
  end
24
24
 
25
- if RubyVM.respond_to? :keep_script_lines
25
+ if defined?(RubyVM.keep_script_lines)
26
26
  # Ruby 3.1 and later
27
27
  RubyVM.keep_script_lines = true
28
28
  require 'objspace'
@@ -5,6 +5,10 @@ require 'pp'
5
5
 
6
6
  require_relative 'color'
7
7
 
8
+ class ::Thread
9
+ attr_accessor :debug_thread_client
10
+ end
11
+
8
12
  module DEBUGGER__
9
13
  M_INSTANCE_VARIABLES = method(:instance_variables).unbind
10
14
  M_INSTANCE_VARIABLE_GET = method(:instance_variable_get).unbind
@@ -40,7 +44,7 @@ module DEBUGGER__
40
44
  end
41
45
 
42
46
  module GlobalVariablesHelper
43
- SKIP_GLOBAL_LIST = %i[$= $KCODE $-K $SAFE].freeze
47
+ SKIP_GLOBAL_LIST = %i[$= $KCODE $-K $SAFE $FILENAME].freeze
44
48
  def safe_global_variables
45
49
  global_variables.reject{|name| SKIP_GLOBAL_LIST.include? name }
46
50
  end
@@ -48,12 +52,7 @@ module DEBUGGER__
48
52
 
49
53
  class ThreadClient
50
54
  def self.current
51
- if thc = Thread.current[:DEBUGGER__ThreadClient]
52
- thc
53
- else
54
- thc = SESSION.get_thread_client
55
- Thread.current[:DEBUGGER__ThreadClient] = thc
56
- end
55
+ Thread.current.debug_thread_client ||= SESSION.get_thread_client
57
56
  end
58
57
 
59
58
  include Color
@@ -351,6 +350,7 @@ module DEBUGGER__
351
350
  next if tp.path.start_with?(__dir__)
352
351
  next if tp.path.start_with?('<internal:trace_point>')
353
352
  next unless File.exist?(tp.path) if CONFIG[:skip_nosrc]
353
+ next if skip_internal_path?(tp.path)
354
354
  loc = caller_locations(1, 1).first
355
355
  next if skip_location?(loc)
356
356
  next if iter && (iter -= 1) > 0
@@ -370,6 +370,7 @@ module DEBUGGER__
370
370
  next if tp.path.start_with?(__dir__)
371
371
  next if tp.path.start_with?('<internal:trace_point>')
372
372
  next unless File.exist?(tp.path) if CONFIG[:skip_nosrc]
373
+ next if skip_internal_path?(tp.path)
373
374
  loc = caller_locations(1, 1).first
374
375
  next if skip_location?(loc)
375
376
  next if iter && (iter -= 1) > 0
@@ -614,7 +615,7 @@ module DEBUGGER__
614
615
  if expr && !expr.empty?
615
616
  begin
616
617
  _self = frame_eval(expr, re_raise: true)
617
- rescue Exception => e
618
+ rescue Exception
618
619
  # ignore
619
620
  else
620
621
  if M_KIND_OF_P.bind_call(_self, Module)
@@ -862,8 +863,18 @@ module DEBUGGER__
862
863
  class SuspendReplay < Exception
863
864
  end
864
865
 
866
+ if ::Fiber.respond_to?(:blocking)
867
+ private def fiber_blocking
868
+ ::Fiber.blocking{yield}
869
+ end
870
+ else
871
+ private def fiber_blocking
872
+ yield
873
+ end
874
+ end
875
+
865
876
  def wait_next_action
866
- wait_next_action_
877
+ fiber_blocking{wait_next_action_}
867
878
  rescue SuspendReplay
868
879
  replay_suspend
869
880
  end
@@ -980,7 +991,7 @@ module DEBUGGER__
980
991
  true
981
992
  else
982
993
  true if depth >= DEBUGGER__.frame_depth - 3 &&
983
- caller_locations(2, 1).first.label == target_location_label
994
+ caller_locations(2, 1).first.base_label == target_location_label
984
995
  # TODO: imcomplete condition
985
996
  end
986
997
  end
@@ -996,7 +1007,7 @@ module DEBUGGER__
996
1007
  true if pat === tp.callee_id.to_s
997
1008
  else # :return, :b_return
998
1009
  true if depth >= DEBUGGER__.frame_depth - 3 &&
999
- caller_locations(2, 1).first.label == target_location_label
1010
+ caller_locations(2, 1).first.base_label == target_location_label
1000
1011
  # TODO: imcomplete condition
1001
1012
  end
1002
1013
  end
@@ -1047,14 +1058,6 @@ module DEBUGGER__
1047
1058
  end
1048
1059
  when :call
1049
1060
  result = frame_eval(eval_src)
1050
- when :irb
1051
- require 'irb' # prelude's binding.irb doesn't have show_code option
1052
- begin
1053
- result = frame_eval('binding.irb(show_code: false)', binding_location: true)
1054
- ensure
1055
- # workaround: https://github.com/ruby/debug/issues/308
1056
- Reline.prompt_proc = nil if defined? Reline
1057
- end
1058
1061
  when :display, :try_display
1059
1062
  failed_results = []
1060
1063
  eval_src.each_with_index{|src, i|
@@ -1078,13 +1081,13 @@ module DEBUGGER__
1078
1081
  when :up
1079
1082
  if @current_frame_index + 1 < @target_frames.size
1080
1083
  @current_frame_index += 1
1081
- show_src max_lines: 1
1084
+ show_src max_lines: CONFIG[:show_src_lines_frame]
1082
1085
  show_frame(@current_frame_index)
1083
1086
  end
1084
1087
  when :down
1085
1088
  if @current_frame_index > 0
1086
1089
  @current_frame_index -= 1
1087
- show_src max_lines: 1
1090
+ show_src max_lines: CONFIG[:show_src_lines_frame]
1088
1091
  show_frame(@current_frame_index)
1089
1092
  end
1090
1093
  when :set
@@ -1096,7 +1099,7 @@ module DEBUGGER__
1096
1099
  puts "out of frame index: #{index}"
1097
1100
  end
1098
1101
  end
1099
- show_src max_lines: 1
1102
+ show_src max_lines: CONFIG[:show_src_lines_frame]
1100
1103
  show_frame(@current_frame_index)
1101
1104
  else
1102
1105
  raise "unsupported frame operation: #{arg.inspect}"
data/lib/debug/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DEBUGGER__
4
- VERSION = "1.8.0"
4
+ VERSION = "1.11.0"
5
5
  end