ruby-debug 0.10.3 → 0.10.4

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.
Files changed (108) hide show
  1. data/CHANGES +15 -0
  2. data/ChangeLog +461 -104
  3. data/Rakefile +69 -13
  4. data/VERSION +3 -0
  5. data/bin/rdebug +2 -1
  6. data/cli/ruby-debug.rb +8 -5
  7. data/cli/ruby-debug/command.rb +43 -4
  8. data/cli/ruby-debug/commands/breakpoints.rb +3 -1
  9. data/cli/ruby-debug/commands/catchpoint.rb +5 -3
  10. data/cli/ruby-debug/commands/{continue.RB.save → continue.RB} +0 -0
  11. data/cli/ruby-debug/commands/eval.rb +4 -1
  12. data/cli/ruby-debug/commands/frame.rb +11 -10
  13. data/cli/ruby-debug/commands/help.rb +5 -0
  14. data/cli/ruby-debug/commands/info.rb +6 -3
  15. data/cli/ruby-debug/commands/irb.rb +115 -12
  16. data/cli/ruby-debug/commands/kill.rb +50 -0
  17. data/cli/ruby-debug/commands/list.rb +3 -3
  18. data/cli/ruby-debug/commands/quit.rb +12 -6
  19. data/cli/ruby-debug/commands/raise.RB +41 -0
  20. data/cli/ruby-debug/commands/show.rb +26 -28
  21. data/cli/ruby-debug/helper.rb +5 -0
  22. data/cli/ruby-debug/interface.rb +53 -25
  23. data/cli/ruby-debug/processor.RB +484 -0
  24. data/cli/ruby-debug/processor.rb +140 -56
  25. data/rdbg.rb +0 -0
  26. data/runner.sh +7 -0
  27. data/test/base/base.rb +0 -0
  28. data/test/base/binding.rb +0 -0
  29. data/test/base/catchpoint.rb +0 -0
  30. data/test/base/reload_bug.rb +8 -0
  31. data/test/brkpt-class-bug.rb +8 -0
  32. data/test/cli/commands/unit/regexp.rb +11 -0
  33. data/test/config.yaml +8 -0
  34. data/test/data/annotate.cmd +1 -1
  35. data/test/data/annotate.right +3 -3
  36. data/test/data/break_bad.cmd +1 -1
  37. data/test/data/break_bad.right +1 -1
  38. data/test/data/break_loop_bug.right +1 -1
  39. data/test/data/breakpoints.cmd +1 -1
  40. data/test/data/breakpoints.right +3 -3
  41. data/test/data/brkpt-class-bug.cmd +9 -0
  42. data/test/data/brkpt-class-bug.right +18 -0
  43. data/test/data/catch.right +2 -0
  44. data/test/data/condition.cmd +1 -0
  45. data/test/data/condition.right +5 -3
  46. data/test/data/ctrl.right +2 -2
  47. data/test/data/display.right +1 -1
  48. data/test/data/emacs_basic.right +2 -2
  49. data/test/data/except-bug1.cmd +7 -0
  50. data/test/data/except-bug1.right +13 -0
  51. data/test/data/file-with-space.cmd +7 -0
  52. data/test/data/file-with-space.right +9 -0
  53. data/test/data/finish.right +1 -1
  54. data/test/data/frame.cmd +7 -1
  55. data/test/data/frame.right +12 -1
  56. data/test/data/info-var.right +1 -1
  57. data/test/data/info.cmd +1 -0
  58. data/test/data/info.right +21 -2
  59. data/test/data/list.right +1 -1
  60. data/test/data/method.right +1 -1
  61. data/test/data/post-mortem.right +5 -4
  62. data/test/data/save.right +1 -1
  63. data/test/data/setshow.cmd +0 -10
  64. data/test/data/setshow.right +0 -17
  65. data/test/dollar-0.rb +0 -0
  66. data/test/except-bug1.rb +4 -0
  67. data/test/file with space.rb +1 -0
  68. data/test/gcd-dbg.rb +0 -0
  69. data/test/helper.rb +7 -1
  70. data/test/pm-base.rb +0 -0
  71. data/test/pm.rb +1 -1
  72. data/test/raise.rb +0 -0
  73. data/test/rdebug-save.1 +7 -0
  74. data/test/tdebug.rb +2 -2
  75. data/test/test-annotate.rb +0 -0
  76. data/test/test-break-bad.rb +0 -0
  77. data/test/test-breakpoints.rb +0 -0
  78. data/test/test-brkpt-class-bug.rb +26 -0
  79. data/test/test-catch.rb +1 -1
  80. data/test/test-condition.rb +1 -1
  81. data/test/test-ctrl.rb +1 -0
  82. data/test/test-display.rb +0 -0
  83. data/test/test-dollar-0.rb +0 -0
  84. data/test/test-edit.rb +0 -0
  85. data/test/test-emacs-basic.rb +0 -0
  86. data/test/test-enable.rb +0 -0
  87. data/test/test-except-bug1.rb +31 -0
  88. data/test/test-file-with-space.rb +30 -0
  89. data/test/test-finish.rb +0 -0
  90. data/test/test-frame.rb +0 -0
  91. data/test/test-help.rb +0 -0
  92. data/test/test-hist.rb +0 -0
  93. data/test/test-info-thread.rb +0 -0
  94. data/test/test-info-var.rb +0 -0
  95. data/test/test-info.rb +0 -0
  96. data/test/test-init.rb +7 -1
  97. data/test/test-list.rb +0 -0
  98. data/test/test-method.rb +0 -0
  99. data/test/test-output.rb +0 -0
  100. data/test/test-pm.rb +0 -0
  101. data/test/test-quit.rb +0 -0
  102. data/test/test-raise.rb +1 -1
  103. data/test/test-save.rb +7 -1
  104. data/test/test-setshow.rb +0 -0
  105. data/test/test-source.rb +0 -0
  106. data/test/test-stepping.rb +0 -0
  107. data/test/test-trace.rb +0 -0
  108. metadata +211 -180
@@ -4,10 +4,13 @@ module Debugger
4
4
  class HelpCommand < Command
5
5
  self.allow_in_control = true
6
6
 
7
+ # An input line is matched against this regular expression. If we have
8
+ # a match, run this command.
7
9
  def regexp
8
10
  /^\s* h(?:elp)? (?:\s+(.+))? $/x
9
11
  end
10
12
 
13
+ # The code that implements this command.
11
14
  def execute
12
15
  if @match[1]
13
16
  args = @match[1].split
@@ -41,10 +44,12 @@ module Debugger
41
44
  end
42
45
 
43
46
  class << self
47
+ # The command name listed via 'help'
44
48
  def help_command
45
49
  'help'
46
50
  end
47
51
 
52
+ # Returns a String given the help description of this command
48
53
  def help(cmd)
49
54
  %{
50
55
  h[elp]\t\tprint this help
@@ -91,7 +91,7 @@ item. If \'verbose\' is given then the entire stack frame is shown.'],
91
91
  end
92
92
 
93
93
  def execute
94
- if @match[1].empty?
94
+ if !@match[1] || @match[1].empty?
95
95
  errmsg "\"info\" must be followed by the name of an info command:\n"
96
96
  print "List of info subcommands:\n\n"
97
97
  for subcmd in Subcommands do
@@ -142,12 +142,15 @@ item. If \'verbose\' is given then the entire stack frame is shown.'],
142
142
  end
143
143
  print "Num Enb What\n"
144
144
  brkpts.each do |b|
145
+ fname = Command.settings[:basename] ?
146
+ File.basename(b.source) : b.source
147
+
145
148
  if b.expr.nil?
146
149
  print "%3d %s at %s:%s\n",
147
- b.id, (b.enabled? ? 'y' : 'n'), b.source, b.pos
150
+ b.id, (b.enabled? ? 'y' : 'n'), fname, b.pos
148
151
  else
149
152
  print "%3d %s at %s:%s if %s\n",
150
- b.id, (b.enabled? ? 'y' : 'n'), b.source, b.pos, b.expr
153
+ b.id, (b.enabled? ? 'y' : 'n'), fname, b.pos, b.expr
151
154
  end
152
155
  hits = b.hit_count
153
156
  if hits > 0
@@ -2,20 +2,78 @@ require 'irb'
2
2
 
3
3
  module IRB # :nodoc:
4
4
  module ExtendCommand # :nodoc:
5
- class Continue # :nodoc:
6
- def self.execute(conf)
7
- throw :IRB_EXIT, :cont
5
+
6
+ # FIXME: should we read these out of a directory to
7
+ # make this more user-customizable?
8
+ # A base command class that resume execution
9
+ class DebuggerResumeCommand
10
+ def self.execute(conf, *opts)
11
+ name =
12
+ if self.name =~ /IRB::ExtendCommand::(\S+)/
13
+ $1.downcase
14
+ else
15
+ 'unknown'
16
+ end
17
+ $rdebug_args = opts
18
+ $rdebug_command =
19
+ if $rdebug_irb_statements
20
+ $rdebug_irb_statements
21
+ else
22
+ ([name] + opts).join(' ')
23
+ end
24
+
25
+ throw :IRB_EXIT, name.to_sym
26
+ end
27
+ end
28
+
29
+ class Continue < DebuggerResumeCommand ; end
30
+ class Next < DebuggerResumeCommand ; end
31
+ class Quit < DebuggerResumeCommand ; end
32
+ class Step < DebuggerResumeCommand ; end
33
+
34
+ # Issues a comamnd to the debugger without continuing
35
+ # execution.
36
+ class Dbgr
37
+ def self.execute(conf, *opts)
38
+ command =
39
+ if opts.size == 1 && opts[0].is_a?(String)
40
+ args = opts[0]
41
+ else
42
+ opts.join(' ')
43
+ end
44
+ if $rdebug_state && $rdebug_state.processor
45
+ processor = $rdebug_state.processor
46
+ processor.one_cmd($rdebug_state.processor.commands,
47
+ $rdebug_state.context,
48
+ command)
49
+ end
8
50
  end
9
51
  end
52
+
53
+ end
54
+ if defined?(ExtendCommandBundle)
55
+ [['cont', :Continue],
56
+ ['dbgr', :Dbgr],
57
+ ['n', :Next],
58
+ ['step', :Step],
59
+ ['q', :Quit]].each do |command, sym|
60
+ ExtendCommandBundle.def_extend_command command, sym
61
+ end
10
62
  end
11
- ExtendCommandBundle.def_extend_command "cont", :Continue
12
63
 
13
64
  def self.start_session(binding)
14
65
  unless @__initialized
15
- args = ARGV
16
- ARGV.replace(ARGV.dup)
66
+ args = ARGV.dup
67
+ ARGV.replace([])
17
68
  IRB.setup(nil)
18
69
  ARGV.replace(args)
70
+
71
+ # If the user has a IRB profile, run that now.
72
+ if ENV['RDEBUG_IRB']
73
+ ENV['IRBRC'] = ENV['RDEBUG_IRB']
74
+ @CONF[:RC_NAME_GENERATOR]=nil
75
+ IRB.run_config
76
+ end
19
77
  @__initialized = true
20
78
  end
21
79
 
@@ -32,6 +90,18 @@ module IRB # :nodoc:
32
90
  end
33
91
  end
34
92
 
93
+ # Monkeypatch to save the current IRB statement to be run and make the instruction sequence
94
+ # "filename" unique. Possibly not needed.
95
+ class IRB::Context
96
+ def evaluate(line, line_no)
97
+ $rdebug_irb_statements = line
98
+ @line_no = line_no
99
+ set_last_value(@workspace.evaluate(self, line, irb_path, line_no))
100
+ # @workspace.evaluate("_ = IRB.conf[:MAIN_CONTEXT]._")
101
+ # @_ = @workspace.evaluate(line, irb_path, line_no)
102
+ end
103
+ end
104
+
35
105
  module Debugger
36
106
 
37
107
  # Implements debugger "irb" command.
@@ -45,7 +115,9 @@ module Debugger
45
115
  end
46
116
 
47
117
  def regexp
48
- /^irb$/
118
+ /^\s* irb
119
+ (?:\s+(-d))?
120
+ \s*$/x
49
121
  end
50
122
 
51
123
  def execute
@@ -55,13 +127,30 @@ module Debugger
55
127
  end
56
128
 
57
129
  save_trap = trap("SIGINT") do
58
- throw :IRB_EXIT, :cont if $debug_in_irb
130
+ throw :IRB_EXIT, :cont if $rdebug_in_irb
59
131
  end
60
132
 
61
- $debug_in_irb = true
133
+ # add_debugging = @match.is_a?(Array) && '-d' == @match[1]
134
+ $rdebug_state = @state
135
+ $rdebug_in_irb = true
62
136
  cont = IRB.start_session(get_binding)
63
- if cont == :cont
137
+ case cont
138
+ when :cont
139
+ @state.proceed
140
+ when :step
141
+ force = Command.settings[:force_stepping]
142
+ @state.context.step(1, force)
64
143
  @state.proceed
144
+ when :next
145
+ force = Command.settings[:force_stepping]
146
+ @state.context.step_over(1, @state.frame_pos, force)
147
+ @state.proceed
148
+ when :quit
149
+ # FIXME: DRY with code from file/command quit.
150
+ if confirm("Really quit? (y/n) ")
151
+ @state.interface.finalize
152
+ exit! # exit -> exit!: No graceful way to stop threads...
153
+ end
65
154
  else
66
155
  file = @state.context.frame_file(0)
67
156
  line = @state.context.frame_line(0)
@@ -70,7 +159,8 @@ module Debugger
70
159
  end
71
160
 
72
161
  ensure
73
- $debug_in_irb = false
162
+ $rdebug_in_irb = nil
163
+ $rdebug_state = nil
74
164
  trap("SIGINT", save_trap) if save_trap
75
165
  end
76
166
 
@@ -81,7 +171,20 @@ module Debugger
81
171
 
82
172
  def help(cmd)
83
173
  %{
84
- irb\tstarts an Interactive Ruby (IRB) session.
174
+ irb [-d]\tstarts an Interactive Ruby (IRB) session.
175
+
176
+ If -d is added you can get access to debugger state via the global variable
177
+ $rdebug_state.
178
+
179
+ irb is extended with methods "cont", "n", "step" and "q" which run the
180
+ corresponding debugger commands. In contrast to the real debugger
181
+ commands these commands do not allow command arguments.
182
+
183
+ However to run any arbitrary rdebug command which does not involve
184
+ execution of the debugged program (like the above "step" "cont", etc.)
185
+ use method "dbgr" and give an array of string parameters. For example:
186
+
187
+ dbgr ['list', '10'] # same as "list 10" inside debugger
85
188
  }
86
189
  end
87
190
  end
@@ -0,0 +1,50 @@
1
+ module Debugger
2
+
3
+ # Implements debugger "kill" command
4
+ class KillCommand < Command
5
+ self.allow_in_control = true
6
+
7
+ def regexp
8
+ / ^\s*
9
+ (?:kill) \s*
10
+ (?:\s+(\S+))?\s*
11
+ $
12
+ /ix
13
+ end
14
+
15
+ def execute
16
+ if @match[1]
17
+ signame = @match[1]
18
+ unless Signal.list.member?(signame)
19
+ errmsg("signal name #{signame} is not a signal I know about\n")
20
+ return false
21
+ end
22
+ if 'KILL' == signame
23
+ @state.interface.finalize
24
+ end
25
+ else
26
+ if not confirm("Really kill? (y/n) ")
27
+ return
28
+ else
29
+ signame = 'KILL'
30
+ end
31
+ end
32
+ Process.kill(signame, Process.pid)
33
+ end
34
+
35
+ class << self
36
+ def help_command
37
+ %w[kill]
38
+ end
39
+
40
+ def help(cmd)
41
+ %{
42
+ kill [SIGNAL]
43
+
44
+ Send [signal] to Process.pid
45
+ Equivalent of Process.kill(Process.pid)
46
+ }
47
+ end
48
+ end
49
+ end
50
+ end
@@ -69,12 +69,12 @@ module Debugger
69
69
  # If we can show from B to E then we return B, otherwise we return the
70
70
  # previous line @state.previous_line.
71
71
  def display_list(b, e, file, current)
72
- print "[%d, %d] in %s\n", b, e, file
73
72
  lines = LineCache::getlines(file,
74
73
  Command.settings[:reload_source_on_change])
75
74
  if lines
76
- return @state.previous_line if b >= lines.size
75
+ b = lines.size - (e - b) if b >= lines.size
77
76
  e = lines.size if lines.size < e
77
+ print "[%d, %d] in %s\n", b, e, file
78
78
  [b, 1].max.upto(e) do |n|
79
79
  if n > 0 && lines[n-1]
80
80
  if n == current
@@ -88,7 +88,7 @@ module Debugger
88
88
  errmsg "No sourcefile available for %s\n", file
89
89
  return @state.previous_line
90
90
  end
91
- return e == lines.size ? @state.previous_line : b
91
+ return b
92
92
  end
93
93
  end
94
94
  end
@@ -4,14 +4,17 @@ module Debugger
4
4
  class QuitCommand < Command
5
5
  self.allow_in_control = true
6
6
 
7
+ # An input line is matched against this regular expression. If we have
8
+ # a match, run this command.
7
9
  def regexp
8
10
  / ^\s*
9
11
  (?:q(?:uit)?|exit) \s*
10
- (\s+unconditionally)? \s*
12
+ (!|\s+unconditionally)? \s*
11
13
  $
12
14
  /ix
13
15
  end
14
16
 
17
+ # The code that implements this command.
15
18
  def execute
16
19
  if @match[1] or confirm("Really quit? (y/n) ")
17
20
  @state.interface.finalize
@@ -20,19 +23,22 @@ module Debugger
20
23
  end
21
24
 
22
25
  class << self
26
+ # The command name listed via 'help'
23
27
  def help_command
24
28
  %w[quit exit]
25
29
  end
26
30
 
31
+ # Returns a String given the help description of this command
27
32
  def help(cmd)
28
33
  %{
29
- q[uit] [unconditionally]\texit from debugger.
30
- exit\talias to quit
34
+ q[uit] [!|unconditionally]\texit from debugger.
35
+ exit[!]\talias to quit
31
36
 
32
37
  Normally we prompt before exiting. However if the parameter
33
- "unconditionally" is given, we stop without asking further questions.
34
- }
35
- end
38
+ "unconditionally" or is given or suffixed with !, we stop
39
+ without asking further questions.
40
+ }
41
+ end
36
42
  end
37
43
  end
38
44
  end
@@ -0,0 +1,41 @@
1
+ module Debugger
2
+ class RaiseCommand < Command # :nodoc:
3
+ self.allow_in_control = false
4
+
5
+ def regexp
6
+ /^\s* raise
7
+ (?:\s+ (\S+))? \s* $/ix
8
+ end
9
+
10
+ def execute
11
+ excn = @match[1]
12
+ exception =
13
+ if not excn
14
+ # No args given.
15
+ RuntimeError
16
+ else
17
+ unless debug_eval("#{excn}.is_a?(Class)", binding)
18
+ errmsg "#{excn} is not known to be a Class\n"
19
+ return
20
+ end
21
+ debug_eval(excn, binding)
22
+ end
23
+ @state.exception = exception
24
+ @state.proceed
25
+ end
26
+
27
+ class << self
28
+ def help_command
29
+ 'raise'
30
+ end
31
+
32
+ def help(cmd)
33
+ %{
34
+ raise EXCEPTION
35
+
36
+ Raise an exception in the debugged program."
37
+ }
38
+ end
39
+ end
40
+ end
41
+ end
@@ -38,37 +38,34 @@ module Debugger
38
38
  style = Command.settings[:callstyle]
39
39
  return "Frame call-display style is #{style}."
40
40
  when /^commands(:?\s+(\d+))?$/
41
- if @state.interface.readline_support?
42
- s = '';
43
- args = @match[1].split
44
- if args[1]
45
- first_line = args[1].to_i - 4
46
- last_line = first_line + 10 - 1
47
- if first_line > Readline::HISTORY.length
48
- first_line = last_line = Readline::HISTORY.length
49
- elsif first_line <= 0
50
- first_line = 1
51
- end
52
- if last_line > Readline::HISTORY.length
53
- last_line = Readline::HISTORY.length
54
- end
55
- i = first_line
56
- commands = Readline::HISTORY.to_a[first_line..last_line]
57
- else
58
- if Readline::HISTORY.length > 10
59
- commands = Readline::HISTORY.to_a[-10..-1]
60
- i = Readline::HISTORY.length - 10
61
- else
62
- commands = Readline::HISTORY.to_a
63
- i = 1
64
- end
41
+ return 'No readline support.' unless @state.interface.readline_support?
42
+ s = '';
43
+ args = @match[1].split
44
+ if args[1]
45
+ first_line = args[1].to_i - 4
46
+ last_line = first_line + 10 - 1
47
+ if first_line > Readline::HISTORY.length
48
+ first_line = last_line = Readline::HISTORY.length
49
+ elsif first_line <= 0
50
+ first_line = 1
65
51
  end
66
- commands.each do |cmd|
67
- s += ("%5d %s\n" % [i, cmd])
68
- i += 1
52
+ if last_line > Readline::HISTORY.length
53
+ last_line = Readline::HISTORY.length
69
54
  end
55
+ i = first_line
56
+ commands = Readline::HISTORY.to_a[first_line..last_line]
70
57
  else
71
- s='No readline suport'
58
+ if Readline::HISTORY.length > 10
59
+ commands = Readline::HISTORY.to_a[-10..-1]
60
+ i = Readline::HISTORY.length - 10
61
+ else
62
+ commands = Readline::HISTORY.to_a
63
+ i = 1
64
+ end
65
+ end
66
+ commands.each do |cmd|
67
+ s += ("%5d %s\n" % [i, cmd])
68
+ i += 1
72
69
  end
73
70
  return s
74
71
  when /^debuggertesting$/
@@ -81,6 +78,7 @@ module Debugger
81
78
  on_off = Command.settings[:full_path]
82
79
  return "Displaying frame's full file names is #{show_onoff(on_off)}."
83
80
  when /^history(:?\s+(filename|save|size))?$/
81
+ return 'No readline support.' unless @state.interface.readline_support?
84
82
  args = @match[1].split
85
83
  interface = @state.interface
86
84
  if args[1]