debug 1.6.2 → 1.7.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -6,6 +6,22 @@ module DEBUGGER__
6
6
  class SourceRepository
7
7
  include Color
8
8
 
9
+ def file_src iseq
10
+ if (path = (iseq.absolute_path || iseq.path)) && File.exist?(path)
11
+ File.readlines(path, chomp: true)
12
+ end
13
+ end
14
+
15
+ def get iseq
16
+ return unless iseq
17
+
18
+ if CONFIG[:show_evaledsrc]
19
+ orig_src(iseq) || file_src(iseq)
20
+ else
21
+ file_src(iseq) || orig_src(iseq)
22
+ end
23
+ end
24
+
9
25
  if RubyVM.respond_to? :keep_script_lines
10
26
  # Ruby 3.1 and later
11
27
  RubyVM.keep_script_lines = true
@@ -29,17 +45,13 @@ module DEBUGGER__
29
45
  end
30
46
  end
31
47
 
32
- def get iseq
33
- return unless iseq
34
-
35
- if lines = iseq.script_lines&.map(&:chomp)
36
- lines
48
+ def orig_src iseq
49
+ lines = iseq.script_lines&.map(&:chomp)
50
+ line = iseq.first_line
51
+ if line > 1
52
+ lines = [*([''] * (line - 1)), *lines]
37
53
  else
38
- if (path = (iseq.absolute_path || iseq.path)) && File.exist?(path)
39
- File.readlines(path, chomp: true)
40
- else
41
- nil
42
- end
54
+ lines
43
55
  end
44
56
  end
45
57
 
@@ -63,15 +75,22 @@ module DEBUGGER__
63
75
  end
64
76
 
65
77
  def add iseq, src
66
- if (path = (iseq.absolute_path || iseq.path)) && File.exist?(path)
78
+ path = (iseq.absolute_path || iseq.path)
79
+
80
+ if path && File.exist?(path)
67
81
  reloaded = @files.has_key? path
68
- add_path path
69
- return path, reloaded
70
- elsif src
82
+
83
+ if src
84
+ add_iseq iseq, src
85
+ return path, reloaded
86
+ else
87
+ add_path path
88
+ return path, reloaded
89
+ end
90
+ else
71
91
  add_iseq iseq, src
92
+ nil
72
93
  end
73
-
74
- nil
75
94
  end
76
95
 
77
96
  private def all_iseq iseq, rs = []
@@ -87,7 +106,8 @@ module DEBUGGER__
87
106
  if line > 1
88
107
  src = ("\n" * (line - 1)) + src
89
108
  end
90
- si = SrcInfo.new(src.lines)
109
+
110
+ si = SrcInfo.new(src.each_line.map{|l| l.chomp})
91
111
  all_iseq(iseq).each{|e|
92
112
  e.instance_variable_set(:@debugger_si, si)
93
113
  e.freeze
@@ -102,7 +122,7 @@ module DEBUGGER__
102
122
 
103
123
  private def get_si iseq
104
124
  return unless iseq
105
-
125
+
106
126
  if iseq.instance_variable_defined?(:@debugger_si)
107
127
  iseq.instance_variable_get(:@debugger_si)
108
128
  elsif @files.has_key?(path = (iseq.absolute_path || iseq.path))
@@ -112,7 +132,7 @@ module DEBUGGER__
112
132
  end
113
133
  end
114
134
 
115
- def get iseq
135
+ def orig_src iseq
116
136
  if si = get_si(iseq)
117
137
  si.src
118
138
  end
data/lib/debug/start.rb CHANGED
@@ -2,4 +2,4 @@
2
2
 
3
3
  require_relative 'session'
4
4
  return unless defined?(DEBUGGER__)
5
- DEBUGGER__.start
5
+ DEBUGGER__.start no_sigint_hook: false
@@ -18,7 +18,7 @@ module DEBUGGER__
18
18
  module SkipPathHelper
19
19
  def skip_path?(path)
20
20
  !path ||
21
- CONFIG.skip? ||
21
+ DEBUGGER__.skip? ||
22
22
  ThreadClient.current.management? ||
23
23
  skip_internal_path?(path) ||
24
24
  skip_config_skip_path?(path)
@@ -29,7 +29,7 @@ module DEBUGGER__
29
29
  end
30
30
 
31
31
  def skip_internal_path?(path)
32
- path.start_with?(__dir__) || path.start_with?('<internal:')
32
+ path.start_with?(__dir__) || path.delete_prefix('!eval:').start_with?('<internal:')
33
33
  end
34
34
 
35
35
  def skip_location?(loc)
@@ -38,6 +38,13 @@ module DEBUGGER__
38
38
  end
39
39
  end
40
40
 
41
+ module GlobalVariablesHelper
42
+ SKIP_GLOBAL_LIST = %i[$= $KCODE $-K $SAFE].freeze
43
+ def safe_global_variables
44
+ global_variables.reject{|name| SKIP_GLOBAL_LIST.include? name }
45
+ end
46
+ end
47
+
41
48
  class ThreadClient
42
49
  def self.current
43
50
  if thc = Thread.current[:DEBUGGER__ThreadClient]
@@ -50,6 +57,7 @@ module DEBUGGER__
50
57
 
51
58
  include Color
52
59
  include SkipPathHelper
60
+ include GlobalVariablesHelper
53
61
 
54
62
  attr_reader :thread, :id, :recorder, :check_bp_fulfillment_map
55
63
 
@@ -291,8 +299,10 @@ module DEBUGGER__
291
299
  end
292
300
 
293
301
  if event != :pause
294
- show_src
295
- show_frames CONFIG[:show_frames]
302
+ unless bp&.skip_src
303
+ show_src
304
+ show_frames CONFIG[:show_frames]
305
+ end
296
306
 
297
307
  set_mode :waiting
298
308
 
@@ -328,11 +338,15 @@ module DEBUGGER__
328
338
  @step_tp.disable if @step_tp
329
339
 
330
340
  thread = Thread.current
341
+ subsession_id = SESSION.subsession_id
331
342
 
332
343
  if SUPPORT_TARGET_THREAD
333
344
  @step_tp = TracePoint.new(*events){|tp|
334
- next if SESSION.break_at? tp.path, tp.lineno
335
- next if !yield(tp.event)
345
+ if SESSION.stop_stepping? tp.path, tp.lineno, subsession_id
346
+ tp.disable
347
+ next
348
+ end
349
+ next if !yield(tp)
336
350
  next if tp.path.start_with?(__dir__)
337
351
  next if tp.path.start_with?('<internal:trace_point>')
338
352
  next unless File.exist?(tp.path) if CONFIG[:skip_nosrc]
@@ -347,8 +361,11 @@ module DEBUGGER__
347
361
  else
348
362
  @step_tp = TracePoint.new(*events){|tp|
349
363
  next if thread != Thread.current
350
- next if SESSION.break_at? tp.path, tp.lineno
351
- next if !yield(tp.event)
364
+ if SESSION.stop_stepping? tp.path, tp.lineno, subsession_id
365
+ tp.disable
366
+ next
367
+ end
368
+ next if !yield(tp)
352
369
  next if tp.path.start_with?(__dir__)
353
370
  next if tp.path.start_with?('<internal:trace_point>')
354
371
  next unless File.exist?(tp.path) if CONFIG[:skip_nosrc]
@@ -384,15 +401,19 @@ module DEBUGGER__
384
401
  end
385
402
  end
386
403
 
387
- def frame_eval_core src, b
404
+ def frame_eval_core src, b, binding_location: false
388
405
  saved_target_frames = @target_frames
389
406
  saved_current_frame_index = @current_frame_index
390
407
 
391
408
  if b
392
- f, _l = b.source_location
409
+ file, lineno = b.source_location
393
410
 
394
411
  tp_allow_reentry do
395
- b.eval(src, "(rdbg)/#{f}")
412
+ if binding_location
413
+ b.eval(src, file, lineno)
414
+ else
415
+ b.eval(src, "(rdbg)/#{file}")
416
+ end
396
417
  end
397
418
  else
398
419
  frame_self = current_frame.self
@@ -411,16 +432,16 @@ module DEBUGGER__
411
432
  [:return_value, "_return"],
412
433
  ]
413
434
 
414
- def frame_eval src, re_raise: false
435
+ def frame_eval src, re_raise: false, binding_location: false
415
436
  @success_last_eval = false
416
437
 
417
- b = current_frame.eval_binding
438
+ b = current_frame&.eval_binding || TOPLEVEL_BINDING
418
439
 
419
440
  special_local_variables current_frame do |name, var|
420
441
  b.local_variable_set(name, var) if /\%/ !~ name
421
442
  end
422
443
 
423
- result = frame_eval_core(src, b)
444
+ result = frame_eval_core(src, b, binding_location: binding_location)
424
445
 
425
446
  @success_last_eval = true
426
447
  result
@@ -482,19 +503,28 @@ module DEBUGGER__
482
503
  exit!
483
504
  end
484
505
 
485
- def show_src(frame_index: @current_frame_index, update_line: false, max_lines: CONFIG[:show_src_lines], **options)
506
+ def show_src(frame_index: @current_frame_index, update_line: false, ignore_show_line: false, max_lines: CONFIG[:show_src_lines], **options)
486
507
  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
490
- if update_line
491
- frame.show_line = end_line
508
+ begin
509
+ if ignore_show_line
510
+ prev_show_line = frame.show_line
511
+ frame.show_line = nil
492
512
  end
493
513
 
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
497
- puts "# No sourcefile available for #{frame.path}"
514
+ start_line, end_line, lines = *get_src(frame, max_lines: max_lines, **options)
515
+
516
+ if start_line
517
+ if update_line
518
+ frame.show_line = end_line
519
+ end
520
+
521
+ puts "[#{start_line+1}, #{end_line}] in #{frame.pretty_path}" if !update_line && max_lines != 1
522
+ puts lines[start_line...end_line]
523
+ else
524
+ puts "# No sourcefile available for #{frame.path}"
525
+ end
526
+ ensure
527
+ frame.show_line = prev_show_line if prev_show_line
498
528
  end
499
529
  end
500
530
  end
@@ -546,48 +576,76 @@ module DEBUGGER__
546
576
  end
547
577
  end
548
578
 
549
- def show_ivars pat
550
- if s = current_frame&.self
551
- M_INSTANCE_VARIABLES.bind_call(s).sort.each{|iv|
552
- value = M_INSTANCE_VARIABLE_GET.bind_call(s, iv)
579
+ def show_ivars pat, expr = nil
580
+ if expr && !expr.empty?
581
+ _self = frame_eval(expr);
582
+ elsif _self = current_frame&.self
583
+ else
584
+ _self = nil
585
+ end
586
+
587
+ if _self
588
+ M_INSTANCE_VARIABLES.bind_call(_self).sort.each{|iv|
589
+ value = M_INSTANCE_VARIABLE_GET.bind_call(_self, iv)
553
590
  puts_variable_info iv, value, pat
554
591
  }
555
592
  end
556
593
  end
557
594
 
558
- def show_consts pat, only_self: false
559
- if s = current_frame&.self
595
+ def iter_consts c, names = {}
596
+ c.constants(false).sort.each{|name|
597
+ next if names.has_key? name
598
+ names[name] = nil
599
+ begin
600
+ value = c.const_get(name)
601
+ rescue Exception => e
602
+ value = e
603
+ end
604
+ yield name, value
605
+ }
606
+ end
607
+
608
+ def get_consts expr = nil, only_self: false, &block
609
+ 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
614
+ else
615
+ raise "#{_self.inspect} (by #{expr}) is not a Module."
616
+ end
617
+ elsif _self = current_frame&.self
560
618
  cs = {}
561
- if M_KIND_OF_P.bind_call(s, Module)
562
- cs[s] = :self
619
+ if M_KIND_OF_P.bind_call(_self, Module)
620
+ cs[_self] = :self
563
621
  else
564
- s = M_CLASS.bind_call(s)
565
- cs[s] = :self unless only_self
622
+ _self = M_CLASS.bind_call(_self)
623
+ cs[_self] = :self unless only_self
566
624
  end
567
625
 
568
626
  unless only_self
569
- s.ancestors.each{|c| break if c == Object; cs[c] = :ancestors}
627
+ _self.ancestors.each{|c| break if c == Object; cs[c] = :ancestors}
570
628
  if b = current_frame&.binding
571
- b.eval('Module.nesting').each{|c| cs[c] = :nesting unless cs.has_key? c}
629
+ b.eval('::Module.nesting').each{|c| cs[c] = :nesting unless cs.has_key? c}
572
630
  end
573
631
  end
574
632
 
575
633
  names = {}
576
634
 
577
635
  cs.each{|c, _|
578
- c.constants(false).sort.each{|name|
579
- next if names.has_key? name
580
- names[name] = nil
581
- value = c.const_get(name)
582
- puts_variable_info name, value, pat
583
- }
636
+ iter_consts c, names, &block
584
637
  }
585
638
  end
586
639
  end
587
640
 
588
- SKIP_GLOBAL_LIST = %i[$= $KCODE $-K $SAFE].freeze
641
+ def show_consts pat, expr = nil, only_self: false
642
+ get_consts expr, only_self: only_self do |name, value|
643
+ puts_variable_info name, value, pat
644
+ end
645
+ end
646
+
589
647
  def show_globals pat
590
- global_variables.sort.each{|name|
648
+ safe_global_variables.sort.each{|name|
591
649
  next if SKIP_GLOBAL_LIST.include? name
592
650
 
593
651
  value = eval(name.to_s)
@@ -643,7 +701,8 @@ module DEBUGGER__
643
701
  if editor = (ENV['RUBY_DEBUG_EDITOR'] || ENV['EDITOR'])
644
702
  puts "command: #{editor}"
645
703
  puts " path: #{path}"
646
- system(editor, path)
704
+ require 'shellwords'
705
+ system(*Shellwords.split(editor), path)
647
706
  else
648
707
  puts "can not find editor setting: ENV['RUBY_DEBUG_EDITOR'] or ENV['EDITOR']"
649
708
  end
@@ -758,7 +817,7 @@ module DEBUGGER__
758
817
  case args.first
759
818
  when :method
760
819
  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)
820
+ bp = MethodBreakpoint.new(current_frame&.eval_binding || TOPLEVEL_BINDING, klass_name, op, method_name, cond: cond, command: cmd, path: path)
762
821
  begin
763
822
  bp.enable
764
823
  rescue NameError => e
@@ -814,6 +873,7 @@ module DEBUGGER__
814
873
  set_mode :waiting if !waiting?
815
874
  cmds = @q_cmd.pop
816
875
  # pp [self, cmds: cmds]
876
+
817
877
  break unless cmds
818
878
  ensure
819
879
  set_mode :running
@@ -831,8 +891,9 @@ module DEBUGGER__
831
891
 
832
892
  case step_type
833
893
  when :in
894
+ iter = iter || 1
834
895
  if @recorder&.replaying?
835
- @recorder.step_forward
896
+ @recorder.step_forward iter
836
897
  raise SuspendReplay
837
898
  else
838
899
  step_tp iter do
@@ -845,6 +906,7 @@ module DEBUGGER__
845
906
  frame = @target_frames.first
846
907
  path = frame.location.absolute_path || "!eval:#{frame.path}"
847
908
  line = frame.location.lineno
909
+ label = frame.location.base_label
848
910
 
849
911
  if frame.iseq
850
912
  frame.iseq.traceable_lines_norec(lines = {})
@@ -856,35 +918,89 @@ module DEBUGGER__
856
918
 
857
919
  depth = @target_frames.first.frame_depth
858
920
 
859
- step_tp iter do
921
+ step_tp iter do |tp|
860
922
  loc = caller_locations(2, 1).first
861
923
  loc_path = loc.absolute_path || "!eval:#{loc.path}"
924
+ loc_label = loc.base_label
925
+ loc_depth = DEBUGGER__.frame_depth - 3
862
926
 
863
- # same stack depth
864
- (DEBUGGER__.frame_depth - 3 <= depth) ||
865
-
866
- # different frame
867
- (next_line && loc_path == path &&
868
- (loc_lineno = loc.lineno) > line &&
869
- loc_lineno <= next_line)
927
+ case
928
+ when loc_depth == depth && loc_label == label
929
+ true
930
+ when loc_depth < depth
931
+ # lower stack depth
932
+ true
933
+ when (next_line &&
934
+ loc_path == path &&
935
+ (loc_lineno = loc.lineno) > line &&
936
+ loc_lineno <= next_line)
937
+ # different frame (maybe block) but the line is before next_line
938
+ true
939
+ end
870
940
  end
871
941
  break
872
942
 
873
943
  when :finish
874
944
  finish_frames = (iter || 1) - 1
875
- goal_depth = @target_frames.first.frame_depth - finish_frames
945
+ frame = @target_frames.first
946
+ goal_depth = frame.frame_depth - finish_frames - (frame.has_return_value ? 1 : 0)
876
947
 
877
948
  step_tp nil, [:return, :b_return] do
878
949
  DEBUGGER__.frame_depth - 3 <= goal_depth ? true : false
879
950
  end
880
951
  break
881
952
 
953
+ when :until
954
+ location = iter&.strip
955
+ frame = @target_frames.first
956
+ depth = frame.frame_depth - (frame.has_return_value ? 1 : 0)
957
+ target_location_label = frame.location.base_label
958
+
959
+ case location
960
+ when nil, /\A(?:(.+):)?(\d+)\z/
961
+ no_loc = !location
962
+ file = $1 || frame.location.path
963
+ line = ($2 || frame.location.lineno + 1).to_i
964
+
965
+ step_tp nil, [:line, :return] do |tp|
966
+ if tp.event == :line
967
+ next false if no_loc && depth < DEBUGGER__.frame_depth - 3
968
+ next false unless tp.path.end_with?(file)
969
+ next false unless tp.lineno >= line
970
+ true
971
+ else
972
+ true if depth >= DEBUGGER__.frame_depth - 3 &&
973
+ caller_locations(2, 1).first.label == target_location_label
974
+ # TODO: imcomplete condition
975
+ end
976
+ end
977
+ else
978
+ pat = location
979
+ if /\A\/(.+)\/\z/ =~ pat
980
+ pat = Regexp.new($1)
981
+ end
982
+
983
+ step_tp nil, [:call, :c_call, :return] do |tp|
984
+ case tp.event
985
+ when :call, :c_call
986
+ true if pat === tp.callee_id.to_s
987
+ else # :return, :b_return
988
+ true if depth >= DEBUGGER__.frame_depth - 3 &&
989
+ caller_locations(2, 1).first.label == target_location_label
990
+ # TODO: imcomplete condition
991
+ end
992
+ end
993
+ end
994
+
995
+ break
996
+
882
997
  when :back
998
+ iter = iter || 1
883
999
  if @recorder&.can_step_back?
884
1000
  unless @recorder.backup_frames
885
1001
  @recorder.backup_frames = @target_frames
886
1002
  end
887
- @recorder.step_back
1003
+ @recorder.step_back iter
888
1004
  raise SuspendReplay
889
1005
  else
890
1006
  puts "Can not step back more."
@@ -922,8 +1038,9 @@ module DEBUGGER__
922
1038
  when :call
923
1039
  result = frame_eval(eval_src)
924
1040
  when :irb
1041
+ require 'irb' # prelude's binding.irb doesn't have show_code option
925
1042
  begin
926
- result = frame_eval('binding.irb')
1043
+ result = frame_eval('binding.irb(show_code: false)', binding_location: true)
927
1044
  ensure
928
1045
  # workaround: https://github.com/ruby/debug/issues/308
929
1046
  Reline.prompt_proc = nil if defined? Reline
@@ -988,6 +1105,10 @@ module DEBUGGER__
988
1105
  when :list
989
1106
  show_src(update_line: true, **(args.first || {}))
990
1107
 
1108
+ when :whereami
1109
+ show_src ignore_show_line: true
1110
+ show_frames CONFIG[:show_frames]
1111
+
991
1112
  when :edit
992
1113
  show_by_editor(args.first)
993
1114
 
@@ -1003,11 +1124,13 @@ module DEBUGGER__
1003
1124
 
1004
1125
  when :ivars
1005
1126
  pat = args.shift
1006
- show_ivars pat
1127
+ expr = args.shift
1128
+ show_ivars pat, expr
1007
1129
 
1008
1130
  when :consts
1009
1131
  pat = args.shift
1010
- show_consts pat
1132
+ expr = args.shift
1133
+ show_consts pat, expr
1011
1134
 
1012
1135
  when :globals
1013
1136
  pat = args.shift
@@ -1093,6 +1216,8 @@ module DEBUGGER__
1093
1216
  end
1094
1217
  event! :result, nil
1095
1218
 
1219
+ when :quit
1220
+ sleep # wait for SystemExit
1096
1221
  when :dap
1097
1222
  process_dap args
1098
1223
  when :cdp
@@ -1107,6 +1232,8 @@ module DEBUGGER__
1107
1232
  rescue Exception => e
1108
1233
  pp ["DEBUGGER Exception: #{__FILE__}:#{__LINE__}", e, e.backtrace]
1109
1234
  raise
1235
+ ensure
1236
+ @returning = false
1110
1237
  end
1111
1238
 
1112
1239
  def debug_event(ev, args)
@@ -1187,12 +1314,18 @@ module DEBUGGER__
1187
1314
  @tp_recorder.enabled?
1188
1315
  end
1189
1316
 
1190
- def step_back
1191
- @index += 1
1317
+ def step_back iter
1318
+ @index += iter
1319
+ if @index > @log.size
1320
+ @index = @log.size
1321
+ end
1192
1322
  end
1193
1323
 
1194
- def step_forward
1195
- @index -= 1
1324
+ def step_forward iter
1325
+ @index -= iter
1326
+ if @index < 0
1327
+ @index = 0
1328
+ end
1196
1329
  end
1197
1330
 
1198
1331
  def step_reset
data/lib/debug/tracer.rb CHANGED
File without changes
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.6.2"
4
+ VERSION = "1.7.1"
5
5
  end
data/lib/debug.rb CHANGED
@@ -1,5 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'debug/session'
4
- return unless defined?(DEBUGGER__)
5
- DEBUGGER__::start no_sigint_hook: true, nonstop: true
3
+ if ENV['RUBY_DEBUG_LAZY']
4
+ require_relative 'debug/prelude'
5
+ else
6
+ require_relative 'debug/session'
7
+ return unless defined?(DEBUGGER__)
8
+ DEBUGGER__::start no_sigint_hook: true, nonstop: true
9
+ end
data/misc/README.md.erb CHANGED
@@ -438,12 +438,12 @@ $ rdbg target.rb --open=chrome
438
438
  DEBUGGER: Debugger can attach via TCP/IP (127.0.0.1:43633)
439
439
  DEBUGGER: With Chrome browser, type the following URL in the address-bar:
440
440
 
441
- devtools://devtools/bundled/inspector.html?ws=127.0.0.1:43633
441
+ devtools://devtools/bundled/inspector.html?v8only=true&panel=sources&ws=127.0.0.1:57231/b32a55cd-2eb5-4c5c-87d8-b3dfc59d80ef
442
442
 
443
443
  DEBUGGER: wait for debugger connection...
444
444
  ```
445
445
 
446
- Type `devtools://devtools/bundled/inspector.html?ws=127.0.0.1:43633` in the address-bar on Chrome browser, and you can continue the debugging with chrome browser.
446
+ Type `devtools://devtools/bundled/inspector.html?v8only=true&panel=sources&ws=127.0.0.1:57231/b32a55cd-2eb5-4c5c-87d8-b3dfc59d80ef` in the address-bar on Chrome browser, and you can continue the debugging with chrome browser.
447
447
 
448
448
  Also `open chrome` command works like `open vscode`.
449
449
 
@@ -498,7 +498,8 @@ There are additional features:
498
498
  * `<expr>` without debug command is almost same as `pp <expr>`.
499
499
  * If the input line `<expr>` does *NOT* start with any debug command, the line `<expr>` will be evaluated as a Ruby expression and the result will be printed with `pp` method. So that the input `foo.bar` is same as `pp foo.bar`.
500
500
  * If `<expr>` is recognized as a debug command, of course it is not evaluated as a Ruby expression, but is executed as debug command. For example, you can not evaluate such single letter local variables `i`, `b`, `n`, `c` because they are single letter debug commands. Use `p i` instead.
501
- * `Enter` without any input repeats the last command (useful when repeating `step`s).
501
+ * So the author (Koichi Sasada) recommends to use `p`, `pp` or `eval` command to evaluate the Ruby expression everytime.
502
+ * `Enter` without any input repeats the last command (useful when repeating `step`s) for some commands.
502
503
  * `Ctrl-D` is equal to `quit` command.
503
504
  * [debug command compare sheet - Google Sheets](https://docs.google.com/spreadsheets/d/1TlmmUDsvwK4sSIyoMv-io52BUUz__R5wpu-ComXlsw0/edit?usp=sharing)
504
505
 
@@ -567,11 +568,11 @@ It is useful if you only want to call a debug command and don't want to stop the
567
568
  ```
568
569
  def initialize
569
570
  @a = 1
570
- binding.b do: 'watch @a'
571
+ binding.b do: 'info \n watch @a'
571
572
  end
572
573
  ```
573
574
 
574
- On this case, register a watch breakpoint for `@a` and continue to run.
575
+ On this case, execute the `info` command then register a watch breakpoint for `@a` and continue to run. You can also use `;;` instead of `\n` to separate your commands.
575
576
 
576
577
  If `pre: 'command'` is specified, the debugger suspends the program and run the `command` as a debug command, and keep suspend.
577
578
  It is useful if you have operations before suspend.
@@ -591,6 +592,11 @@ On this case, you can see the result of `bar()` every time you stop there.
591
592
  <%= `exe/rdbg --help` %>
592
593
  ```
593
594
 
595
+ # Additional Resources
596
+
597
+ - [From byebug to ruby/debug](https://st0012.dev/from-byebug-to-ruby-debug) by Stan Lo - A migration guide for `byebug` users.
598
+ - [ruby/debug cheatsheet](https://st0012.dev/ruby-debug-cheatsheet) by Stan Lo
599
+
594
600
  # Contributing
595
601
 
596
602
  Bug reports and pull requests are welcome on GitHub at https://github.com/ruby/debug.