ruby-debug 0.10.3 → 0.10.4

Sign up to get free protection for your applications and to get access to all the features.
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]