debug 1.7.1 → 1.8.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.
data/lib/debug/session.rb CHANGED
@@ -128,16 +128,16 @@ module DEBUGGER__
128
128
  @obj_map = {} # { object_id => ... } for CDP
129
129
 
130
130
  @tp_thread_begin = nil
131
+ @tp_thread_end = nil
132
+
131
133
  @commands = {}
132
134
  @unsafe_context = false
133
135
 
134
- has_keep_script_lines = RubyVM.respond_to? :keep_script_lines
136
+ @has_keep_script_lines = RubyVM.respond_to? :keep_script_lines
135
137
 
136
138
  @tp_load_script = TracePoint.new(:script_compiled){|tp|
137
- if !has_keep_script_lines || bps_pending_until_load?
138
- eval_script = tp.eval_script unless has_keep_script_lines
139
- ThreadClient.current.on_load tp.instruction_sequence, eval_script
140
- end
139
+ eval_script = tp.eval_script unless @has_keep_script_lines
140
+ ThreadClient.current.on_load tp.instruction_sequence, eval_script
141
141
  }
142
142
  @tp_load_script.enable
143
143
 
@@ -169,12 +169,17 @@ module DEBUGGER__
169
169
  @ui = ui if ui
170
170
 
171
171
  @tp_thread_begin&.disable
172
+ @tp_thread_end&.disable
172
173
  @tp_thread_begin = nil
173
-
174
+ @tp_thread_end = nil
174
175
  @ui.activate self, on_fork: on_fork
175
176
 
176
177
  q = Queue.new
178
+ first_q = Queue.new
177
179
  @session_server = Thread.new do
180
+ # make sure `@session_server` is assigned
181
+ first_q.pop; first_q = nil
182
+
178
183
  Thread.current.name = 'DEBUGGER__::SESSION@server'
179
184
  Thread.current.abort_on_exception = true
180
185
 
@@ -192,10 +197,16 @@ module DEBUGGER__
192
197
  end
193
198
  @tp_thread_begin.enable
194
199
 
200
+ @tp_thread_end = TracePoint.new(:thread_end) do |tp|
201
+ @th_clients.delete(Thread.current)
202
+ end
203
+ @tp_thread_end.enable
204
+
195
205
  # session start
196
206
  q << true
197
207
  session_server_main
198
208
  end
209
+ first_q << :ok
199
210
 
200
211
  q.pop
201
212
  end
@@ -205,6 +216,7 @@ module DEBUGGER__
205
216
  @thread_stopper.disable
206
217
  @tp_load_script.disable
207
218
  @tp_thread_begin.disable
219
+ @tp_thread_end.disable
208
220
  @bps.each_value{|bp| bp.disable}
209
221
  @th_clients.each_value{|thc| thc.close}
210
222
  @tracers.values.each{|t| t.disable}
@@ -219,11 +231,13 @@ module DEBUGGER__
219
231
 
220
232
  # activate new ui
221
233
  @tp_thread_begin.disable
234
+ @tp_thread_end.disable
222
235
  @ui.activate self
223
236
  if @ui.respond_to?(:reader_thread) && thc = get_thread_client(@ui.reader_thread)
224
237
  thc.mark_as_management
225
238
  end
226
239
  @tp_thread_begin.enable
240
+ @tp_thread_end.enable
227
241
  end
228
242
 
229
243
  def pop_event
@@ -329,16 +343,13 @@ module DEBUGGER__
329
343
  opt = ev_args[3]
330
344
  add_tracer ObjectTracer.new(@ui, obj_id, obj_inspect, **opt)
331
345
  else
332
- # ignore
346
+ stop_all_threads
333
347
  end
334
348
 
335
349
  wait_command_loop
336
350
 
337
- when :dap_result
338
- dap_event ev_args # server.rb
339
- wait_command_loop
340
- when :cdp_result
341
- cdp_event ev_args
351
+ when :protocol_result
352
+ process_protocol_result ev_args
342
353
  wait_command_loop
343
354
  end
344
355
  end
@@ -480,9 +491,9 @@ module DEBUGGER__
480
491
  # * `u[ntil]`
481
492
  # * Similar to `next` command, but only stop later lines or the end of the current frame.
482
493
  # * Similar to gdb's `advance` command.
483
- # * `u[ntil] <[file:]line>
494
+ # * `u[ntil] <[file:]line>`
484
495
  # * Run til the program reaches given location or the end of the current frame.
485
- # * `u[ntil] <name>
496
+ # * `u[ntil] <name>`
486
497
  # * Run til the program invokes a method `<name>`. `<name>` can be a regexp with `/name/`.
487
498
  register_command 'u', 'until',
488
499
  repeat: true,
@@ -889,13 +900,13 @@ module DEBUGGER__
889
900
  # * `p <expr>`
890
901
  # * Evaluate like `p <expr>` on the current frame.
891
902
  register_command 'p' do |arg|
892
- request_tc [:eval, :p, arg.to_s]
903
+ request_eval :p, arg.to_s
893
904
  end
894
905
 
895
906
  # * `pp <expr>`
896
907
  # * Evaluate like `pp <expr>` on the current frame.
897
908
  register_command 'pp' do |arg|
898
- request_tc [:eval, :pp, arg.to_s]
909
+ request_eval :pp, arg.to_s
899
910
  end
900
911
 
901
912
  # * `eval <expr>`
@@ -906,7 +917,7 @@ module DEBUGGER__
906
917
  @ui.puts "\nTo evaluate the variable `#{cmd}`, use `pp #{cmd}` instead."
907
918
  :retry
908
919
  else
909
- request_tc [:eval, :call, arg]
920
+ request_eval :call, arg
910
921
  end
911
922
  end
912
923
 
@@ -917,7 +928,7 @@ module DEBUGGER__
917
928
  @ui.puts "not supported on the remote console."
918
929
  :retry
919
930
  end
920
- request_tc [:eval, :irb]
931
+ request_eval :irb, nil
921
932
  end
922
933
 
923
934
  ### Trace
@@ -1137,7 +1148,7 @@ module DEBUGGER__
1137
1148
  @repl_prev_line = nil
1138
1149
  check_unsafe
1139
1150
 
1140
- request_tc [:eval, :pp, line]
1151
+ request_eval :pp, line
1141
1152
  end
1142
1153
 
1143
1154
  rescue Interrupt
@@ -1153,6 +1164,11 @@ module DEBUGGER__
1153
1164
  return :retry
1154
1165
  end
1155
1166
 
1167
+ def request_eval type, src
1168
+ restart_all_threads
1169
+ request_tc [:eval, type, src]
1170
+ end
1171
+
1156
1172
  def step_command type, arg
1157
1173
  if type == :until
1158
1174
  leave_subsession [:step, type, arg]
@@ -1321,10 +1337,6 @@ module DEBUGGER__
1321
1337
 
1322
1338
  # breakpoint management
1323
1339
 
1324
- def bps_pending_until_load?
1325
- @bps.any?{|key, bp| bp.pending_until_load?}
1326
- end
1327
-
1328
1340
  def iterate_bps
1329
1341
  deleted_bps = []
1330
1342
  i = 0
@@ -1500,7 +1512,7 @@ module DEBUGGER__
1500
1512
  def clear_line_breakpoints path
1501
1513
  path = resolve_path(path)
1502
1514
  clear_breakpoints do |k, bp|
1503
- bp.is_a?(LineBreakpoint) && DEBUGGER__.compare_path(k.first, path)
1515
+ bp.is_a?(LineBreakpoint) && bp.path_is?(path)
1504
1516
  end
1505
1517
  rescue Errno::ENOENT
1506
1518
  # just ignore
@@ -1524,7 +1536,7 @@ module DEBUGGER__
1524
1536
  # tracers
1525
1537
 
1526
1538
  def add_tracer tracer
1527
- if @tracers.has_key? tracer.key
1539
+ if @tracers[tracer.key]&.enabled?
1528
1540
  tracer.disable
1529
1541
  @ui.puts "Duplicated tracer: #{tracer}"
1530
1542
  else
@@ -1724,25 +1736,26 @@ module DEBUGGER__
1724
1736
  file_path, reloaded = @sr.add(iseq, src)
1725
1737
  @ui.event :load, file_path, reloaded
1726
1738
 
1727
- pending_line_breakpoints = @bps.find_all do |key, bp|
1728
- LineBreakpoint === bp && !bp.iseq
1729
- end
1730
-
1731
- pending_line_breakpoints.each do |_key, bp|
1732
- if DEBUGGER__.compare_path(bp.path, (iseq.absolute_path || iseq.path))
1733
- bp.try_activate iseq
1734
- end
1735
- end
1736
-
1737
- if reloaded
1738
- @bps.find_all do |key, bp|
1739
- LineBreakpoint === bp && DEBUGGER__.compare_path(bp.path, file_path)
1739
+ # check breakpoints
1740
+ if file_path
1741
+ @bps.find_all do |_key, bp|
1742
+ LineBreakpoint === bp && bp.path_is?(file_path)
1740
1743
  end.each do |_key, bp|
1741
- @bps.delete bp.key # to allow duplicate
1742
- if nbp = LineBreakpoint.copy(bp, iseq)
1743
- add_bp nbp
1744
+ if !bp.iseq
1745
+ bp.try_activate iseq
1746
+ elsif reloaded
1747
+ @bps.delete bp.key # to allow duplicate
1748
+ if nbp = LineBreakpoint.copy(bp, iseq)
1749
+ add_bp nbp
1750
+ end
1744
1751
  end
1745
1752
  end
1753
+ else # !file_path => file_path is not existing
1754
+ @bps.find_all do |_key, bp|
1755
+ LineBreakpoint === bp && !bp.iseq && DEBUGGER__.compare_path(bp.path, (iseq.absolute_path || iseq.path))
1756
+ end.each do |_key, bp|
1757
+ bp.try_activate iseq
1758
+ end
1746
1759
  end
1747
1760
  end
1748
1761
 
@@ -1980,6 +1993,13 @@ module DEBUGGER__
1980
1993
  def after_fork_parent
1981
1994
  @ui.after_fork_parent
1982
1995
  end
1996
+
1997
+ # experimental API
1998
+ def extend_feature session: nil, thread_client: nil, ui: nil
1999
+ Session.include session if session
2000
+ ThreadClient.include thread_client if thread_client
2001
+ @ui.extend ui if ui
2002
+ end
1983
2003
  end
1984
2004
 
1985
2005
  class ProcessGroup
@@ -2438,7 +2458,7 @@ module DEBUGGER__
2438
2458
  end
2439
2459
 
2440
2460
  module DaemonInterceptor
2441
- def daemon
2461
+ def daemon(*args)
2442
2462
  return super unless defined?(SESSION) && SESSION.active?
2443
2463
 
2444
2464
  _, child_hook = __fork_setup_for_debugger(:child)
@@ -2504,7 +2524,17 @@ module DEBUGGER__
2504
2524
 
2505
2525
  module TrapInterceptor
2506
2526
  def trap sig, *command, &command_proc
2507
- case sig&.to_sym
2527
+ sym =
2528
+ case sig
2529
+ when String
2530
+ sig.to_sym
2531
+ when Integer
2532
+ Signal.signame(sig)&.to_sym
2533
+ else
2534
+ sig
2535
+ end
2536
+
2537
+ case sig&.to_s&.to_sym
2508
2538
  when :INT, :SIGINT
2509
2539
  if defined?(SESSION) && SESSION.active? && SESSION.intercept_trap_sigint?
2510
2540
  return SESSION.save_int_trap(command.empty? ? command_proc : command.first)
@@ -34,7 +34,7 @@ module DEBUGGER__
34
34
  end
35
35
 
36
36
  def add iseq, src
37
- # do nothing
37
+ # only manage loaded file names
38
38
  if (path = (iseq.absolute_path || iseq.path)) && File.exist?(path)
39
39
  if @loaded_file_map.has_key? path
40
40
  return path, true # reloaded
@@ -49,7 +49,7 @@ module DEBUGGER__
49
49
  lines = iseq.script_lines&.map(&:chomp)
50
50
  line = iseq.first_line
51
51
  if line > 1
52
- lines = [*([''] * (line - 1)), *lines]
52
+ [*([''] * (line - 1)), *lines]
53
53
  else
54
54
  lines
55
55
  end
@@ -14,6 +14,7 @@ module DEBUGGER__
14
14
  M_RESPOND_TO_P = method(:respond_to?).unbind
15
15
  M_METHOD = method(:method).unbind
16
16
  M_OBJECT_ID = method(:object_id).unbind
17
+ M_NAME = method(:name).unbind
17
18
 
18
19
  module SkipPathHelper
19
20
  def skip_path?(path)
@@ -468,10 +469,14 @@ module DEBUGGER__
468
469
  if file_lines = frame.file_lines
469
470
  frame_line = frame.location.lineno - 1
470
471
 
471
- lines = file_lines.map.with_index do |e, i|
472
- cur = i == frame_line ? '=>' : ' '
473
- line = colorize_dim('%4d|' % (i+1))
474
- "#{cur}#{line} #{e}"
472
+ if CONFIG[:no_lineno]
473
+ lines = file_lines
474
+ else
475
+ lines = file_lines.map.with_index do |e, i|
476
+ cur = i == frame_line ? '=>' : ' '
477
+ line = colorize_dim('%4d|' % (i+1))
478
+ "#{cur}#{line} #{e}"
479
+ end
475
480
  end
476
481
 
477
482
  unless start_line
@@ -607,12 +612,17 @@ module DEBUGGER__
607
612
 
608
613
  def get_consts expr = nil, only_self: false, &block
609
614
  if expr && !expr.empty?
610
- _self = frame_eval(expr)
611
- if M_KIND_OF_P.bind_call(_self, Module)
612
- iter_consts _self, &block
613
- return
615
+ begin
616
+ _self = frame_eval(expr, re_raise: true)
617
+ rescue Exception => e
618
+ # ignore
614
619
  else
615
- raise "#{_self.inspect} (by #{expr}) is not a Module."
620
+ if M_KIND_OF_P.bind_call(_self, Module)
621
+ iter_consts _self, &block
622
+ return
623
+ else
624
+ puts "#{_self.inspect} (by #{expr}) is not a Module."
625
+ end
616
626
  end
617
627
  elsif _self = current_frame&.self
618
628
  cs = {}
@@ -1230,7 +1240,15 @@ module DEBUGGER__
1230
1240
  rescue SuspendReplay, SystemExit, Interrupt
1231
1241
  raise
1232
1242
  rescue Exception => e
1233
- pp ["DEBUGGER Exception: #{__FILE__}:#{__LINE__}", e, e.backtrace]
1243
+ STDERR.puts e.cause.inspect
1244
+ STDERR.puts e.inspect
1245
+ Thread.list.each{|th|
1246
+ STDERR.puts "@@@ #{th}"
1247
+ th.backtrace.each{|b|
1248
+ STDERR.puts " > #{b}"
1249
+ }
1250
+ }
1251
+ p ["DEBUGGER Exception: #{__FILE__}:#{__LINE__}", e, e.backtrace]
1234
1252
  raise
1235
1253
  ensure
1236
1254
  @returning = false
@@ -1292,10 +1310,14 @@ module DEBUGGER__
1292
1310
  frame._callee = b.eval('__callee__')
1293
1311
  end
1294
1312
  }
1295
- @log << frames
1313
+ append(frames)
1296
1314
  }
1297
1315
  end
1298
1316
 
1317
+ def append frames
1318
+ @log << frames
1319
+ end
1320
+
1299
1321
  def enable
1300
1322
  unless @tp_recorder.enabled?
1301
1323
  @log.clear
data/lib/debug/tracer.rb CHANGED
@@ -54,6 +54,10 @@ module DEBUGGER__
54
54
  @tracer.disable
55
55
  end
56
56
 
57
+ def enabled?
58
+ @tracer.enabled?
59
+ end
60
+
57
61
  def description
58
62
  nil
59
63
  end
@@ -85,11 +89,6 @@ module DEBUGGER__
85
89
  end
86
90
  end
87
91
 
88
- def puts msg
89
- @output.puts msg
90
- @output.flush
91
- end
92
-
93
92
  def minfo tp
94
93
  return "block{}" if tp.event == :b_call
95
94
 
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.7.1"
4
+ VERSION = "1.8.0"
5
5
  end
data/misc/README.md.erb CHANGED
@@ -26,7 +26,7 @@ New debug.rb has several advantages:
26
26
  * Support threads (almost done) and ractors (TODO).
27
27
  * Support suspending and entering to the console debugging with `Ctrl-C` at most of timing.
28
28
  * Show parameters on backtrace command.
29
- * Support recording & reply debugging.
29
+ * Support recording & replay debugging.
30
30
 
31
31
  # Installation
32
32
 
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.7.1
4
+ version: 1.8.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-12-22 00:00:00.000000000 Z
11
+ date: 2023-05-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: irb
@@ -66,6 +66,7 @@ files:
66
66
  - lib/debug/color.rb
67
67
  - lib/debug/config.rb
68
68
  - lib/debug/console.rb
69
+ - lib/debug/dap_custom/traceInspector.rb
69
70
  - lib/debug/frame_info.rb
70
71
  - lib/debug/local.rb
71
72
  - lib/debug/open.rb