debug 1.6.1 → 1.9.1

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,7 +6,23 @@ module DEBUGGER__
6
6
  class SourceRepository
7
7
  include Color
8
8
 
9
- if RubyVM.respond_to? :keep_script_lines
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
+
25
+ if defined?(RubyVM.keep_script_lines)
10
26
  # Ruby 3.1 and later
11
27
  RubyVM.keep_script_lines = true
12
28
  require 'objspace'
@@ -18,7 +34,7 @@ module DEBUGGER__
18
34
  end
19
35
 
20
36
  def add iseq, src
21
- # do nothing
37
+ # only manage loaded file names
22
38
  if (path = (iseq.absolute_path || iseq.path)) && File.exist?(path)
23
39
  if @loaded_file_map.has_key? path
24
40
  return path, true # reloaded
@@ -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
+ [*([''] * (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
@@ -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
@@ -14,11 +18,12 @@ module DEBUGGER__
14
18
  M_RESPOND_TO_P = method(:respond_to?).unbind
15
19
  M_METHOD = method(:method).unbind
16
20
  M_OBJECT_ID = method(:object_id).unbind
21
+ M_NAME = method(:name).unbind
17
22
 
18
23
  module SkipPathHelper
19
24
  def skip_path?(path)
20
25
  !path ||
21
- CONFIG.skip? ||
26
+ DEBUGGER__.skip? ||
22
27
  ThreadClient.current.management? ||
23
28
  skip_internal_path?(path) ||
24
29
  skip_config_skip_path?(path)
@@ -29,7 +34,7 @@ module DEBUGGER__
29
34
  end
30
35
 
31
36
  def skip_internal_path?(path)
32
- path.start_with?(__dir__) || path.start_with?('<internal:')
37
+ path.start_with?(__dir__) || path.delete_prefix('!eval:').start_with?('<internal:')
33
38
  end
34
39
 
35
40
  def skip_location?(loc)
@@ -38,18 +43,21 @@ module DEBUGGER__
38
43
  end
39
44
  end
40
45
 
46
+ module GlobalVariablesHelper
47
+ SKIP_GLOBAL_LIST = %i[$= $KCODE $-K $SAFE].freeze
48
+ def safe_global_variables
49
+ global_variables.reject{|name| SKIP_GLOBAL_LIST.include? name }
50
+ end
51
+ end
52
+
41
53
  class ThreadClient
42
54
  def self.current
43
- if thc = Thread.current[:DEBUGGER__ThreadClient]
44
- thc
45
- else
46
- thc = SESSION.get_thread_client
47
- Thread.current[:DEBUGGER__ThreadClient] = thc
48
- end
55
+ Thread.current.debug_thread_client ||= SESSION.get_thread_client
49
56
  end
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
@@ -447,10 +468,14 @@ module DEBUGGER__
447
468
  if file_lines = frame.file_lines
448
469
  frame_line = frame.location.lineno - 1
449
470
 
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}"
471
+ if CONFIG[:no_lineno]
472
+ lines = file_lines
473
+ else
474
+ lines = file_lines.map.with_index do |e, i|
475
+ cur = i == frame_line ? '=>' : ' '
476
+ line = colorize_dim('%4d|' % (i+1))
477
+ "#{cur}#{line} #{e}"
478
+ end
454
479
  end
455
480
 
456
481
  unless start_line
@@ -482,19 +507,28 @@ module DEBUGGER__
482
507
  exit!
483
508
  end
484
509
 
485
- def show_src(frame_index: @current_frame_index, update_line: false, max_lines: CONFIG[:show_src_lines], **options)
510
+ def show_src(frame_index: @current_frame_index, update_line: false, ignore_show_line: false, max_lines: CONFIG[:show_src_lines], **options)
486
511
  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
512
+ begin
513
+ if ignore_show_line
514
+ prev_show_line = frame.show_line
515
+ frame.show_line = nil
492
516
  end
493
517
 
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}"
518
+ start_line, end_line, lines = *get_src(frame, max_lines: max_lines, **options)
519
+
520
+ if start_line
521
+ if update_line
522
+ frame.show_line = end_line
523
+ end
524
+
525
+ puts "[#{start_line+1}, #{end_line}] in #{frame.pretty_path}" if !update_line && max_lines != 1
526
+ puts lines[start_line...end_line]
527
+ else
528
+ puts "# No sourcefile available for #{frame.path}"
529
+ end
530
+ ensure
531
+ frame.show_line = prev_show_line if prev_show_line
498
532
  end
499
533
  end
500
534
  end
@@ -546,48 +580,81 @@ module DEBUGGER__
546
580
  end
547
581
  end
548
582
 
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)
583
+ def show_ivars pat, expr = nil
584
+ if expr && !expr.empty?
585
+ _self = frame_eval(expr);
586
+ elsif _self = current_frame&.self
587
+ else
588
+ _self = nil
589
+ end
590
+
591
+ if _self
592
+ M_INSTANCE_VARIABLES.bind_call(_self).sort.each{|iv|
593
+ value = M_INSTANCE_VARIABLE_GET.bind_call(_self, iv)
553
594
  puts_variable_info iv, value, pat
554
595
  }
555
596
  end
556
597
  end
557
598
 
558
- def show_consts pat, only_self: false
559
- if s = current_frame&.self
599
+ def iter_consts c, names = {}
600
+ c.constants(false).sort.each{|name|
601
+ next if names.has_key? name
602
+ names[name] = nil
603
+ begin
604
+ value = c.const_get(name)
605
+ rescue Exception => e
606
+ value = e
607
+ end
608
+ yield name, value
609
+ }
610
+ end
611
+
612
+ def get_consts expr = nil, only_self: false, &block
613
+ if expr && !expr.empty?
614
+ begin
615
+ _self = frame_eval(expr, re_raise: true)
616
+ rescue Exception
617
+ # ignore
618
+ else
619
+ if M_KIND_OF_P.bind_call(_self, Module)
620
+ iter_consts _self, &block
621
+ return
622
+ else
623
+ puts "#{_self.inspect} (by #{expr}) is not a Module."
624
+ end
625
+ end
626
+ elsif _self = current_frame&.self
560
627
  cs = {}
561
- if M_KIND_OF_P.bind_call(s, Module)
562
- cs[s] = :self
628
+ if M_KIND_OF_P.bind_call(_self, Module)
629
+ cs[_self] = :self
563
630
  else
564
- s = M_CLASS.bind_call(s)
565
- cs[s] = :self unless only_self
631
+ _self = M_CLASS.bind_call(_self)
632
+ cs[_self] = :self unless only_self
566
633
  end
567
634
 
568
635
  unless only_self
569
- s.ancestors.each{|c| break if c == Object; cs[c] = :ancestors}
636
+ _self.ancestors.each{|c| break if c == Object; cs[c] = :ancestors}
570
637
  if b = current_frame&.binding
571
- b.eval('Module.nesting').each{|c| cs[c] = :nesting unless cs.has_key? c}
638
+ b.eval('::Module.nesting').each{|c| cs[c] = :nesting unless cs.has_key? c}
572
639
  end
573
640
  end
574
641
 
575
642
  names = {}
576
643
 
577
644
  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
- }
645
+ iter_consts c, names, &block
584
646
  }
585
647
  end
586
648
  end
587
649
 
588
- SKIP_GLOBAL_LIST = %i[$= $KCODE $-K $SAFE].freeze
650
+ def show_consts pat, expr = nil, only_self: false
651
+ get_consts expr, only_self: only_self do |name, value|
652
+ puts_variable_info name, value, pat
653
+ end
654
+ end
655
+
589
656
  def show_globals pat
590
- global_variables.sort.each{|name|
657
+ safe_global_variables.sort.each{|name|
591
658
  next if SKIP_GLOBAL_LIST.include? name
592
659
 
593
660
  value = eval(name.to_s)
@@ -643,7 +710,8 @@ module DEBUGGER__
643
710
  if editor = (ENV['RUBY_DEBUG_EDITOR'] || ENV['EDITOR'])
644
711
  puts "command: #{editor}"
645
712
  puts " path: #{path}"
646
- system(editor, path)
713
+ require 'shellwords'
714
+ system(*Shellwords.split(editor), path)
647
715
  else
648
716
  puts "can not find editor setting: ENV['RUBY_DEBUG_EDITOR'] or ENV['EDITOR']"
649
717
  end
@@ -758,7 +826,7 @@ module DEBUGGER__
758
826
  case args.first
759
827
  when :method
760
828
  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)
829
+ bp = MethodBreakpoint.new(current_frame&.eval_binding || TOPLEVEL_BINDING, klass_name, op, method_name, cond: cond, command: cmd, path: path)
762
830
  begin
763
831
  bp.enable
764
832
  rescue NameError => e
@@ -793,8 +861,18 @@ module DEBUGGER__
793
861
  class SuspendReplay < Exception
794
862
  end
795
863
 
864
+ if ::Fiber.respond_to?(:blocking)
865
+ private def fiber_blocking
866
+ ::Fiber.blocking{yield}
867
+ end
868
+ else
869
+ private def fiber_blocking
870
+ yield
871
+ end
872
+ end
873
+
796
874
  def wait_next_action
797
- wait_next_action_
875
+ fiber_blocking{wait_next_action_}
798
876
  rescue SuspendReplay
799
877
  replay_suspend
800
878
  end
@@ -814,6 +892,7 @@ module DEBUGGER__
814
892
  set_mode :waiting if !waiting?
815
893
  cmds = @q_cmd.pop
816
894
  # pp [self, cmds: cmds]
895
+
817
896
  break unless cmds
818
897
  ensure
819
898
  set_mode :running
@@ -831,8 +910,9 @@ module DEBUGGER__
831
910
 
832
911
  case step_type
833
912
  when :in
913
+ iter = iter || 1
834
914
  if @recorder&.replaying?
835
- @recorder.step_forward
915
+ @recorder.step_forward iter
836
916
  raise SuspendReplay
837
917
  else
838
918
  step_tp iter do
@@ -845,6 +925,7 @@ module DEBUGGER__
845
925
  frame = @target_frames.first
846
926
  path = frame.location.absolute_path || "!eval:#{frame.path}"
847
927
  line = frame.location.lineno
928
+ label = frame.location.base_label
848
929
 
849
930
  if frame.iseq
850
931
  frame.iseq.traceable_lines_norec(lines = {})
@@ -856,35 +937,89 @@ module DEBUGGER__
856
937
 
857
938
  depth = @target_frames.first.frame_depth
858
939
 
859
- step_tp iter do
940
+ step_tp iter do |tp|
860
941
  loc = caller_locations(2, 1).first
861
942
  loc_path = loc.absolute_path || "!eval:#{loc.path}"
943
+ loc_label = loc.base_label
944
+ loc_depth = DEBUGGER__.frame_depth - 3
862
945
 
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)
946
+ case
947
+ when loc_depth == depth && loc_label == label
948
+ true
949
+ when loc_depth < depth
950
+ # lower stack depth
951
+ true
952
+ when (next_line &&
953
+ loc_path == path &&
954
+ (loc_lineno = loc.lineno) > line &&
955
+ loc_lineno <= next_line)
956
+ # different frame (maybe block) but the line is before next_line
957
+ true
958
+ end
870
959
  end
871
960
  break
872
961
 
873
962
  when :finish
874
963
  finish_frames = (iter || 1) - 1
875
- goal_depth = @target_frames.first.frame_depth - finish_frames
964
+ frame = @target_frames.first
965
+ goal_depth = frame.frame_depth - finish_frames - (frame.has_return_value ? 1 : 0)
876
966
 
877
967
  step_tp nil, [:return, :b_return] do
878
968
  DEBUGGER__.frame_depth - 3 <= goal_depth ? true : false
879
969
  end
880
970
  break
881
971
 
972
+ when :until
973
+ location = iter&.strip
974
+ frame = @target_frames.first
975
+ depth = frame.frame_depth - (frame.has_return_value ? 1 : 0)
976
+ target_location_label = frame.location.base_label
977
+
978
+ case location
979
+ when nil, /\A(?:(.+):)?(\d+)\z/
980
+ no_loc = !location
981
+ file = $1 || frame.location.path
982
+ line = ($2 || frame.location.lineno + 1).to_i
983
+
984
+ step_tp nil, [:line, :return] do |tp|
985
+ if tp.event == :line
986
+ next false if no_loc && depth < DEBUGGER__.frame_depth - 3
987
+ next false unless tp.path.end_with?(file)
988
+ next false unless tp.lineno >= line
989
+ true
990
+ else
991
+ true if depth >= DEBUGGER__.frame_depth - 3 &&
992
+ caller_locations(2, 1).first.label == target_location_label
993
+ # TODO: imcomplete condition
994
+ end
995
+ end
996
+ else
997
+ pat = location
998
+ if /\A\/(.+)\/\z/ =~ pat
999
+ pat = Regexp.new($1)
1000
+ end
1001
+
1002
+ step_tp nil, [:call, :c_call, :return] do |tp|
1003
+ case tp.event
1004
+ when :call, :c_call
1005
+ true if pat === tp.callee_id.to_s
1006
+ else # :return, :b_return
1007
+ true if depth >= DEBUGGER__.frame_depth - 3 &&
1008
+ caller_locations(2, 1).first.label == target_location_label
1009
+ # TODO: imcomplete condition
1010
+ end
1011
+ end
1012
+ end
1013
+
1014
+ break
1015
+
882
1016
  when :back
1017
+ iter = iter || 1
883
1018
  if @recorder&.can_step_back?
884
1019
  unless @recorder.backup_frames
885
1020
  @recorder.backup_frames = @target_frames
886
1021
  end
887
- @recorder.step_back
1022
+ @recorder.step_back iter
888
1023
  raise SuspendReplay
889
1024
  else
890
1025
  puts "Can not step back more."
@@ -922,12 +1057,8 @@ module DEBUGGER__
922
1057
  when :call
923
1058
  result = frame_eval(eval_src)
924
1059
  when :irb
925
- begin
926
- result = frame_eval('binding.irb')
927
- ensure
928
- # workaround: https://github.com/ruby/debug/issues/308
929
- Reline.prompt_proc = nil if defined? Reline
930
- end
1060
+ require_relative "irb_integration"
1061
+ activate_irb_integration
931
1062
  when :display, :try_display
932
1063
  failed_results = []
933
1064
  eval_src.each_with_index{|src, i|
@@ -988,6 +1119,10 @@ module DEBUGGER__
988
1119
  when :list
989
1120
  show_src(update_line: true, **(args.first || {}))
990
1121
 
1122
+ when :whereami
1123
+ show_src ignore_show_line: true
1124
+ show_frames CONFIG[:show_frames]
1125
+
991
1126
  when :edit
992
1127
  show_by_editor(args.first)
993
1128
 
@@ -1003,11 +1138,13 @@ module DEBUGGER__
1003
1138
 
1004
1139
  when :ivars
1005
1140
  pat = args.shift
1006
- show_ivars pat
1141
+ expr = args.shift
1142
+ show_ivars pat, expr
1007
1143
 
1008
1144
  when :consts
1009
1145
  pat = args.shift
1010
- show_consts pat
1146
+ expr = args.shift
1147
+ show_consts pat, expr
1011
1148
 
1012
1149
  when :globals
1013
1150
  pat = args.shift
@@ -1093,6 +1230,8 @@ module DEBUGGER__
1093
1230
  end
1094
1231
  event! :result, nil
1095
1232
 
1233
+ when :quit
1234
+ sleep # wait for SystemExit
1096
1235
  when :dap
1097
1236
  process_dap args
1098
1237
  when :cdp
@@ -1105,8 +1244,18 @@ module DEBUGGER__
1105
1244
  rescue SuspendReplay, SystemExit, Interrupt
1106
1245
  raise
1107
1246
  rescue Exception => e
1108
- pp ["DEBUGGER Exception: #{__FILE__}:#{__LINE__}", e, e.backtrace]
1247
+ STDERR.puts e.cause.inspect
1248
+ STDERR.puts e.inspect
1249
+ Thread.list.each{|th|
1250
+ STDERR.puts "@@@ #{th}"
1251
+ th.backtrace.each{|b|
1252
+ STDERR.puts " > #{b}"
1253
+ }
1254
+ }
1255
+ p ["DEBUGGER Exception: #{__FILE__}:#{__LINE__}", e, e.backtrace]
1109
1256
  raise
1257
+ ensure
1258
+ @returning = false
1110
1259
  end
1111
1260
 
1112
1261
  def debug_event(ev, args)
@@ -1165,10 +1314,14 @@ module DEBUGGER__
1165
1314
  frame._callee = b.eval('__callee__')
1166
1315
  end
1167
1316
  }
1168
- @log << frames
1317
+ append(frames)
1169
1318
  }
1170
1319
  end
1171
1320
 
1321
+ def append frames
1322
+ @log << frames
1323
+ end
1324
+
1172
1325
  def enable
1173
1326
  unless @tp_recorder.enabled?
1174
1327
  @log.clear
@@ -1187,12 +1340,18 @@ module DEBUGGER__
1187
1340
  @tp_recorder.enabled?
1188
1341
  end
1189
1342
 
1190
- def step_back
1191
- @index += 1
1343
+ def step_back iter
1344
+ @index += iter
1345
+ if @index > @log.size
1346
+ @index = @log.size
1347
+ end
1192
1348
  end
1193
1349
 
1194
- def step_forward
1195
- @index -= 1
1350
+ def step_forward iter
1351
+ @index -= iter
1352
+ if @index < 0
1353
+ @index = 0
1354
+ end
1196
1355
  end
1197
1356
 
1198
1357
  def step_reset