debug 1.3.4 → 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.
@@ -6,11 +6,32 @@ 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)
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)
11
28
  (skip_paths = CONFIG[:skip_path]) && skip_paths.any?{|skip_path| path.match?(skip_path)}
12
29
  end
13
30
 
31
+ def skip_internal_path?(path)
32
+ path.start_with?(__dir__) || path.start_with?('<internal:')
33
+ end
34
+
14
35
  def skip_location?(loc)
15
36
  loc_path = loc.absolute_path || "!eval:#{loc.path}"
16
37
  skip_path?(loc_path)
@@ -22,7 +43,7 @@ module DEBUGGER__
22
43
  if thc = Thread.current[:DEBUGGER__ThreadClient]
23
44
  thc
24
45
  else
25
- thc = SESSION.thread_client
46
+ thc = SESSION.get_thread_client
26
47
  Thread.current[:DEBUGGER__ThreadClient] = thc
27
48
  end
28
49
  end
@@ -30,7 +51,11 @@ module DEBUGGER__
30
51
  include Color
31
52
  include SkipPathHelper
32
53
 
33
- attr_reader :location, :thread, :id, :recorder
54
+ attr_reader :thread, :id, :recorder, :check_bp_fulfillment_map
55
+
56
+ def location
57
+ current_frame&.location
58
+ end
34
59
 
35
60
  def assemble_arguments(args)
36
61
  args.map do |arg|
@@ -42,7 +67,8 @@ module DEBUGGER__
42
67
  call_identifier_str =
43
68
  case frame.frame_type
44
69
  when :block
45
- level, block_loc, args = frame.block_identifier
70
+ level, block_loc = frame.block_identifier
71
+ args = frame.parameters_info
46
72
 
47
73
  if !args.empty?
48
74
  args_str = " {|#{assemble_arguments(args)}|}"
@@ -50,7 +76,8 @@ module DEBUGGER__
50
76
 
51
77
  "#{colorize_blue("block")}#{args_str} in #{colorize_blue(block_loc + level)}"
52
78
  when :method
53
- ci, args = frame.method_identifier
79
+ ci = frame.method_identifier
80
+ args = frame.parameters_info
54
81
 
55
82
  if !args.empty?
56
83
  args_str = "(#{assemble_arguments(args)})"
@@ -67,7 +94,7 @@ module DEBUGGER__
67
94
  result = "#{call_identifier_str} at #{location_str}"
68
95
 
69
96
  if return_str = frame.return_str
70
- result += " #=> #{colorize_magenta(frame.return_str)}"
97
+ result += " #=> #{colorize_magenta(return_str)}"
71
98
  end
72
99
 
73
100
  result
@@ -84,8 +111,12 @@ module DEBUGGER__
84
111
  @output = []
85
112
  @frame_formatter = method(:default_frame_formatter)
86
113
  @var_map = {} # { thread_local_var_id => obj } for DAP
114
+ @obj_map = {} # { object_id => obj } for CDP
87
115
  @recorder = nil
88
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 }
89
120
  set_mode :running
90
121
  thr.instance_variable_set(:@__thread_client_id, id)
91
122
 
@@ -100,13 +131,14 @@ module DEBUGGER__
100
131
  @is_management
101
132
  end
102
133
 
103
- def is_management
134
+ def mark_as_management
104
135
  @is_management = true
105
136
  end
106
137
 
107
138
  def set_mode mode
139
+ debug_mode(@mode, mode)
108
140
  # STDERR.puts "#{@mode} => #{mode} @ #{caller.inspect}"
109
- #pp caller
141
+ # pp caller
110
142
 
111
143
  # mode transition check
112
144
  case mode
@@ -148,14 +180,7 @@ module DEBUGGER__
148
180
  end
149
181
 
150
182
  def to_s
151
- loc = current_frame&.location
152
-
153
- if loc
154
- str = "(#{@thread.name || @thread.status})@#{loc}"
155
- else
156
- str = "(#{@thread.name || @thread.status})@#{@thread.to_s}"
157
- end
158
-
183
+ str = "(#{@thread.name || @thread.status})@#{current_frame&.location || @thread.to_s}"
159
184
  str += " (not under control)" unless self.waiting?
160
185
  str
161
186
  end
@@ -175,6 +200,7 @@ module DEBUGGER__
175
200
  end
176
201
 
177
202
  def << req
203
+ debug_cmd(req)
178
204
  @q_cmd << req
179
205
  end
180
206
 
@@ -185,6 +211,7 @@ module DEBUGGER__
185
211
  end
186
212
 
187
213
  def event! ev, *args
214
+ debug_event(ev, args)
188
215
  @q_evt << [self, @output, ev, generate_info, *args]
189
216
  @output = []
190
217
  end
@@ -228,8 +255,9 @@ module DEBUGGER__
228
255
  suspend :pause
229
256
  end
230
257
 
231
- def suspend event, tp = nil, bp: nil, sig: nil, postmortem_frames: nil, replay_frames: nil
258
+ def suspend event, tp = nil, bp: nil, sig: nil, postmortem_frames: nil, replay_frames: nil, postmortem_exc: nil
232
259
  return if management?
260
+ debug_suspend(event)
233
261
 
234
262
  @current_frame_index = 0
235
263
 
@@ -245,7 +273,6 @@ module DEBUGGER__
245
273
 
246
274
  cf = @target_frames.first
247
275
  if cf
248
- @location = cf.location
249
276
  case event
250
277
  when :return, :b_return, :c_return
251
278
  cf.has_return_value = true
@@ -256,11 +283,16 @@ module DEBUGGER__
256
283
  cf.has_raised_exception = true
257
284
  cf.raised_exception = bp.last_exc
258
285
  end
286
+
287
+ if postmortem_exc
288
+ cf.has_raised_exception = true
289
+ cf.raised_exception = postmortem_exc
290
+ end
259
291
  end
260
292
 
261
293
  if event != :pause
262
- show_src max_lines: (CONFIG[:show_src_lines] || 10)
263
- show_frames CONFIG[:show_frames] || 2
294
+ show_src
295
+ show_frames CONFIG[:show_frames]
264
296
 
265
297
  set_mode :waiting
266
298
 
@@ -292,15 +324,15 @@ module DEBUGGER__
292
324
  SUPPORT_TARGET_THREAD = false
293
325
  end
294
326
 
295
- def step_tp iter
327
+ def step_tp iter, events = [:line, :b_return, :return]
296
328
  @step_tp.disable if @step_tp
297
329
 
298
330
  thread = Thread.current
299
331
 
300
332
  if SUPPORT_TARGET_THREAD
301
- @step_tp = TracePoint.new(:line, :b_return, :return){|tp|
333
+ @step_tp = TracePoint.new(*events){|tp|
302
334
  next if SESSION.break_at? tp.path, tp.lineno
303
- next if !yield
335
+ next if !yield(tp.event)
304
336
  next if tp.path.start_with?(__dir__)
305
337
  next if tp.path.start_with?('<internal:trace_point>')
306
338
  next unless File.exist?(tp.path) if CONFIG[:skip_nosrc]
@@ -313,10 +345,10 @@ module DEBUGGER__
313
345
  }
314
346
  @step_tp.enable(target_thread: thread)
315
347
  else
316
- @step_tp = TracePoint.new(:line, :b_return, :return){|tp|
348
+ @step_tp = TracePoint.new(*events){|tp|
317
349
  next if thread != Thread.current
318
350
  next if SESSION.break_at? tp.path, tp.lineno
319
- next if !yield
351
+ next if !yield(tp.event)
320
352
  next if tp.path.start_with?(__dir__)
321
353
  next if tp.path.start_with?('<internal:trace_point>')
322
354
  next unless File.exist?(tp.path) if CONFIG[:skip_nosrc]
@@ -333,123 +365,191 @@ module DEBUGGER__
333
365
 
334
366
  ## cmd helpers
335
367
 
336
- # this method is extracted to hide frame_eval's local variables from C method eval's binding
337
- def instance_eval_for_cmethod frame_self, src
338
- frame_self.instance_eval(src)
368
+ if TracePoint.respond_to? :allow_reentry
369
+ def tp_allow_reentry
370
+ TracePoint.allow_reentry do
371
+ yield
372
+ end
373
+ rescue RuntimeError => e
374
+ # on the postmortem mode, it is not stopped in TracePoint
375
+ if e.message == 'No need to allow reentrance.'
376
+ yield
377
+ else
378
+ raise
379
+ end
380
+ end
381
+ else
382
+ def tp_allow_reentry
383
+ yield
384
+ end
339
385
  end
340
386
 
387
+ def frame_eval_core src, b
388
+ saved_target_frames = @target_frames
389
+ saved_current_frame_index = @current_frame_index
390
+
391
+ if b
392
+ f, _l = b.source_location
393
+
394
+ tp_allow_reentry do
395
+ b.eval(src, "(rdbg)/#{f}")
396
+ end
397
+ else
398
+ frame_self = current_frame.self
399
+
400
+ tp_allow_reentry do
401
+ frame_self.instance_eval(src)
402
+ end
403
+ end
404
+ ensure
405
+ @target_frames = saved_target_frames
406
+ @current_frame_index = saved_current_frame_index
407
+ end
408
+
409
+ SPECIAL_LOCAL_VARS = [
410
+ [:raised_exception, "_raised"],
411
+ [:return_value, "_return"],
412
+ ]
413
+
341
414
  def frame_eval src, re_raise: false
342
- begin
343
- @success_last_eval = false
415
+ @success_last_eval = false
416
+
417
+ b = current_frame.eval_binding
344
418
 
345
- b = current_frame&.eval_binding || TOPLEVEL_BINDING
419
+ special_local_variables current_frame do |name, var|
420
+ b.local_variable_set(name, var) if /\%/ !~ name
421
+ end
346
422
 
347
- result = if b
348
- f, _l = b.source_location
349
- b.eval(src, "(rdbg)/#{f}")
350
- else
351
- frame_self = current_frame.self
352
- instance_eval_for_cmethod(frame_self, src)
353
- end
354
- @success_last_eval = true
355
- result
423
+ result = frame_eval_core(src, b)
356
424
 
357
- rescue Exception => e
358
- return yield(e) if block_given?
425
+ @success_last_eval = true
426
+ result
359
427
 
360
- puts "eval error: #{e}"
428
+ rescue SystemExit
429
+ raise
430
+ rescue Exception => e
431
+ return yield(e) if block_given?
361
432
 
362
- e.backtrace_locations&.each do |loc|
363
- break if loc.path == __FILE__
364
- puts " #{loc}"
365
- end
366
- raise if re_raise
433
+ puts "eval error: #{e}"
434
+
435
+ e.backtrace_locations&.each do |loc|
436
+ break if loc.path == __FILE__
437
+ puts " #{loc}"
367
438
  end
439
+ raise if re_raise
368
440
  end
369
441
 
370
- def show_src(frame_index: @current_frame_index,
371
- update_line: false,
372
- max_lines: 10,
373
- start_line: nil,
374
- end_line: nil,
375
- dir: +1)
376
- if @target_frames && frame = @target_frames[frame_index]
377
- if file_lines = frame.file_lines
378
- frame_line = frame.location.lineno - 1
379
-
380
- lines = file_lines.map.with_index do |e, i|
381
- cur = i == frame_line ? '=>' : ' '
382
- line = colorize_dim('%4d|' % (i+1))
383
- "#{cur}#{line} #{e}"
384
- 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
385
455
 
386
- unless start_line
387
- if frame.show_line
388
- if dir > 0
389
- start_line = frame.show_line
390
- else
391
- end_line = frame.show_line - max_lines
392
- start_line = [end_line - max_lines, 0].max
393
- end
456
+ unless start_line
457
+ if frame.show_line
458
+ if dir > 0
459
+ start_line = frame.show_line
394
460
  else
395
- start_line = [frame_line - max_lines/2, 0].max
461
+ end_line = frame.show_line - max_lines
462
+ start_line = [end_line - max_lines, 0].max
396
463
  end
464
+ else
465
+ start_line = [frame_line - max_lines/2, 0].max
397
466
  end
467
+ end
398
468
 
399
- unless end_line
400
- end_line = [start_line + max_lines, lines.size].min
401
- end
469
+ unless end_line
470
+ end_line = [start_line + max_lines, lines.size].min
471
+ end
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
402
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
403
490
  if update_line
404
491
  frame.show_line = end_line
405
492
  end
406
493
 
407
- if start_line != end_line && max_lines
408
- puts "[#{start_line+1}, #{end_line}] in #{frame.pretty_path}" if !update_line && max_lines != 1
409
- puts lines[start_line ... end_line]
410
- end
411
- 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
412
497
  puts "# No sourcefile available for #{frame.path}"
413
498
  end
414
499
  end
415
- rescue Exception => e
416
- p e
417
- pp e.backtrace
418
- exit!
419
500
  end
420
501
 
421
502
  def current_frame
503
+ get_frame(@current_frame_index)
504
+ end
505
+
506
+ def get_frame(index)
422
507
  if @target_frames
423
- @target_frames[@current_frame_index]
508
+ @target_frames[index]
424
509
  else
425
510
  nil
426
511
  end
427
512
  end
428
513
 
429
- ## cmd: show
514
+ def collect_locals(frame)
515
+ locals = []
430
516
 
431
- def show_locals pat
432
- if s = current_frame&.self
433
- puts_variable_info '%self', s, pat
434
- end
435
- if current_frame&.has_return_value
436
- puts_variable_info '%return', current_frame.return_value, pat
517
+ if s = frame&.self
518
+ locals << ["%self", s]
437
519
  end
438
- if current_frame&.has_raised_exception
439
- puts_variable_info "%raised", current_frame.raised_exception, pat
520
+ special_local_variables frame do |name, val|
521
+ locals << [name, val]
440
522
  end
441
523
 
442
- if vars = current_frame&.local_variables
524
+ if vars = frame&.local_variables
443
525
  vars.each{|var, val|
444
- puts_variable_info var, val, pat
526
+ locals << [var, val]
445
527
  }
446
528
  end
529
+
530
+ locals
531
+ end
532
+
533
+ ## cmd: show
534
+
535
+ def special_local_variables frame
536
+ SPECIAL_LOCAL_VARS.each do |mid, name|
537
+ next unless frame&.send("has_#{mid}")
538
+ name = name.sub('_', '%') if frame.eval_binding.local_variable_defined?(name)
539
+ yield name, frame.send(mid)
540
+ end
541
+ end
542
+
543
+ def show_locals pat
544
+ collect_locals(current_frame).each do |var, val|
545
+ puts_variable_info(var, val, pat)
546
+ end
447
547
  end
448
548
 
449
549
  def show_ivars pat
450
550
  if s = current_frame&.self
451
- s.instance_variables.sort.each{|iv|
452
- value = s.instance_variable_get(iv)
551
+ M_INSTANCE_VARIABLES.bind_call(s).sort.each{|iv|
552
+ value = M_INSTANCE_VARIABLE_GET.bind_call(s, iv)
453
553
  puts_variable_info iv, value, pat
454
554
  }
455
555
  end
@@ -458,10 +558,10 @@ module DEBUGGER__
458
558
  def show_consts pat, only_self: false
459
559
  if s = current_frame&.self
460
560
  cs = {}
461
- if s.kind_of? Module
561
+ if M_KIND_OF_P.bind_call(s, Module)
462
562
  cs[s] = :self
463
563
  else
464
- s = s.class
564
+ s = M_CLASS.bind_call(s)
465
565
  cs[s] = :self unless only_self
466
566
  end
467
567
 
@@ -499,7 +599,7 @@ module DEBUGGER__
499
599
  return if pat && pat !~ label
500
600
 
501
601
  begin
502
- inspected = obj.inspect
602
+ inspected = DEBUGGER__.safe_inspect(obj)
503
603
  rescue Exception => e
504
604
  inspected = e.inspect
505
605
  end
@@ -508,28 +608,32 @@ module DEBUGGER__
508
608
  w = SESSION::width
509
609
 
510
610
  if mono_info.length >= w
511
- info = truncate(mono_info, width: w)
611
+ maximum_value_width = w - "#{label} = ".length
612
+ valstr = truncate(inspected, width: maximum_value_width)
512
613
  else
513
614
  valstr = colored_inspect(obj, width: 2 ** 30)
514
615
  valstr = inspected if valstr.lines.size > 1
515
- info = "#{colorize_cyan(label)} = #{valstr}"
516
616
  end
517
617
 
618
+ info = "#{colorize_cyan(label)} = #{valstr}"
619
+
518
620
  puts info
519
621
  end
520
622
 
521
623
  def truncate(string, width:)
522
- str = string[0 .. (width-4)] + '...'
523
- str += ">" if str.start_with?("#<")
524
- str
624
+ if string.start_with?("#<")
625
+ string[0 .. (width-5)] + '...>'
626
+ else
627
+ string[0 .. (width-4)] + '...'
628
+ end
525
629
  end
526
630
 
527
631
  ### cmd: show edit
528
632
 
529
633
  def show_by_editor path = nil
530
634
  unless path
531
- if @target_frames && frame = @target_frames[@current_frame_index]
532
- path = frame.path
635
+ if current_frame
636
+ path = current_frame.path
533
637
  else
534
638
  return # can't get path
535
639
  end
@@ -554,15 +658,11 @@ module DEBUGGER__
554
658
  if @target_frames && (max ||= @target_frames.size) > 0
555
659
  frames = []
556
660
  @target_frames.each_with_index{|f, i|
557
- next if pattern && !(f.name.match?(pattern) || f.location_str.match?(pattern))
558
- next if CONFIG[:skip_path] && CONFIG[:skip_path].any?{|pat|
559
- case pat
560
- when String
561
- f.location_str.start_with?(pat)
562
- when Regexp
563
- f.location_str.match?(pat)
564
- end
565
- }
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)
566
666
 
567
667
  frames << [i, f]
568
668
  }
@@ -599,18 +699,25 @@ module DEBUGGER__
599
699
  o = Output.new(@output)
600
700
 
601
701
  locals = current_frame&.local_variables
602
- klass = (obj.class == Class || obj.class == Module ? obj : obj.class)
603
702
 
604
- o.dump("constants", obj.constants) if obj.respond_to?(:constants)
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)
605
707
  outline_method(o, klass, obj)
606
- o.dump("instance variables", obj.instance_variables)
708
+ o.dump("instance variables", M_INSTANCE_VARIABLES.bind_call(obj))
607
709
  o.dump("class variables", klass.class_variables)
608
710
  o.dump("locals", locals.keys) if locals
609
711
  end
610
712
  end
611
713
 
612
714
  def outline_method(o, klass, obj)
613
- singleton_class = begin obj.singleton_class; rescue TypeError; nil end
715
+ begin
716
+ singleton_class = M_SINGLETON_CLASS.bind_call(obj)
717
+ rescue TypeError
718
+ singleton_class = nil
719
+ end
720
+
614
721
  maps = class_method_map((singleton_class || klass).ancestors)
615
722
  maps.each do |mod, methods|
616
723
  name = mod == singleton_class ? "#{klass}.methods" : "#{mod}#methods"
@@ -630,22 +737,54 @@ module DEBUGGER__
630
737
 
631
738
  ## cmd: breakpoint
632
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
+
633
757
  def make_breakpoint args
634
758
  case args.first
635
759
  when :method
636
- klass_name, op, method_name, cond, cmd = args[1..]
637
- bp = MethodBreakpoint.new(current_frame.binding, klass_name, op, method_name, cond: cond, command: cmd)
760
+ klass_name, op, method_name, cond, cmd, path = args[1..]
761
+ bp = MethodBreakpoint.new(current_frame.eval_binding, klass_name, op, method_name, cond: cond, command: cmd, path: path)
638
762
  begin
639
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
640
779
  rescue Exception => e
641
- puts e.message
642
- ::DEBUGGER__::METHOD_ADDED_TRACKER.enable
780
+ puts e.inspect
781
+ bp = nil
643
782
  end
644
783
 
645
784
  bp
646
785
  when :watch
647
- ivar, object, result = args[1..]
648
- WatchIVarBreakpoint.new(ivar, object, result)
786
+ ivar, object, result, cond, command, path = args[1..]
787
+ WatchIVarBreakpoint.new(ivar, object, result, cond: cond, command: command, path: path)
649
788
  else
650
789
  raise "unknown breakpoint: #{args}"
651
790
  end
@@ -732,10 +871,11 @@ module DEBUGGER__
732
871
  break
733
872
 
734
873
  when :finish
735
- depth = @target_frames.first.frame_depth
736
- step_tp iter do
737
- # 3 is debugger's frame count
738
- DEBUGGER__.frame_depth - 3 < depth
874
+ finish_frames = (iter || 1) - 1
875
+ goal_depth = @target_frames.first.frame_depth - finish_frames
876
+
877
+ step_tp nil, [:return, :b_return] do
878
+ DEBUGGER__.frame_depth - 3 <= goal_depth ? true : false
739
879
  end
740
880
  break
741
881
 
@@ -834,6 +974,7 @@ module DEBUGGER__
834
974
  else
835
975
  raise "unsupported frame operation: #{arg.inspect}"
836
976
  end
977
+
837
978
  event! :result, nil
838
979
 
839
980
  when :show
@@ -887,7 +1028,7 @@ module DEBUGGER__
887
1028
  bp = make_breakpoint args
888
1029
  event! :result, :method_breakpoint, bp
889
1030
  when :watch
890
- ivar = args[1]
1031
+ ivar, cond, command, path = args[1..]
891
1032
  result = frame_eval(ivar)
892
1033
 
893
1034
  if @success_last_eval
@@ -897,7 +1038,7 @@ module DEBUGGER__
897
1038
  else
898
1039
  current_frame.self
899
1040
  end
900
- bp = make_breakpoint [:watch, ivar, object, result]
1041
+ bp = make_breakpoint [:watch, ivar, object, result, cond, command, path]
901
1042
  event! :result, :watch_breakpoint, bp
902
1043
  else
903
1044
  event! :result, nil
@@ -910,7 +1051,7 @@ module DEBUGGER__
910
1051
  begin
911
1052
  obj = frame_eval args.shift, re_raise: true
912
1053
  opt = args.shift
913
- obj_inspect = obj.inspect
1054
+ obj_inspect = DEBUGGER__.safe_inspect(obj)
914
1055
 
915
1056
  width = 50
916
1057
 
@@ -918,7 +1059,7 @@ module DEBUGGER__
918
1059
  obj_inspect = truncate(obj_inspect, width: width)
919
1060
  end
920
1061
 
921
- event! :result, :trace_pass, obj.object_id, obj_inspect, opt
1062
+ event! :result, :trace_pass, M_OBJECT_ID.bind_call(obj), obj_inspect, opt
922
1063
  rescue => e
923
1064
  puts e.message
924
1065
  event! :result, nil
@@ -935,8 +1076,8 @@ module DEBUGGER__
935
1076
  # enable recording
936
1077
  if !@recorder
937
1078
  @recorder = Recorder.new
938
- @recorder.enable
939
1079
  end
1080
+ @recorder.enable
940
1081
  when :off
941
1082
  if @recorder&.enabled?
942
1083
  @recorder.disable
@@ -968,6 +1109,33 @@ module DEBUGGER__
968
1109
  raise
969
1110
  end
970
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
+
971
1139
  class Recorder
972
1140
  attr_reader :log, :index
973
1141
  attr_accessor :backup_frames
@@ -982,8 +1150,8 @@ module DEBUGGER__
982
1150
 
983
1151
  @tp_recorder ||= TracePoint.new(:line){|tp|
984
1152
  next unless Thread.current == thread
985
- next if tp.path.start_with? __dir__
986
- next if tp.path.start_with? '<internal:'
1153
+ # can't be replaced by skip_location
1154
+ next if skip_internal_path?(tp.path)
987
1155
  loc = caller_locations(1, 1).first
988
1156
  next if skip_location?(loc)
989
1157
 
@@ -1067,7 +1235,7 @@ module DEBUGGER__
1067
1235
  end
1068
1236
  end
1069
1237
 
1070
- # copyed from irb
1238
+ # copied from irb
1071
1239
  class Output
1072
1240
  include Color
1073
1241