ruby-debug 0.9.3 → 0.10.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.
Files changed (64) hide show
  1. data/AUTHORS +1 -0
  2. data/CHANGES +41 -0
  3. data/ChangeLog +0 -0
  4. data/README +27 -13
  5. data/Rakefile +220 -0
  6. data/bin/rdebug +116 -42
  7. data/cli/ruby-debug.rb +33 -3
  8. data/cli/ruby-debug/command.rb +49 -12
  9. data/cli/ruby-debug/commands/breakpoints.rb +47 -64
  10. data/cli/ruby-debug/commands/control.rb +41 -13
  11. data/cli/ruby-debug/commands/display.rb +35 -18
  12. data/cli/ruby-debug/commands/enable.rb +159 -0
  13. data/cli/ruby-debug/commands/eval.rb +78 -4
  14. data/cli/ruby-debug/commands/frame.rb +67 -42
  15. data/cli/ruby-debug/commands/help.rb +21 -17
  16. data/cli/ruby-debug/commands/info.rb +210 -0
  17. data/cli/ruby-debug/commands/irb.rb +9 -1
  18. data/cli/ruby-debug/commands/list.rb +11 -8
  19. data/cli/ruby-debug/commands/method.rb +12 -23
  20. data/cli/ruby-debug/commands/script.rb +14 -9
  21. data/cli/ruby-debug/commands/settings.rb +174 -39
  22. data/cli/ruby-debug/commands/show.rb +193 -0
  23. data/cli/ruby-debug/commands/stepping.rb +15 -10
  24. data/cli/ruby-debug/commands/threads.rb +55 -56
  25. data/cli/ruby-debug/commands/variables.rb +27 -27
  26. data/cli/ruby-debug/helper.rb +134 -0
  27. data/cli/ruby-debug/interface.rb +46 -15
  28. data/cli/ruby-debug/processor.rb +156 -25
  29. data/doc/rdebug.1 +236 -0
  30. data/runner.sh +7 -0
  31. data/test/breakpoints.cmd +43 -0
  32. data/test/breakpoints.right +94 -0
  33. data/test/display.cmd +18 -0
  34. data/test/display.right +37 -0
  35. data/test/frame.cmd +21 -0
  36. data/test/frame.right +45 -0
  37. data/test/gcd.rb +18 -0
  38. data/test/help.cmd +12 -0
  39. data/test/help.right +4 -0
  40. data/test/helper.rb +87 -0
  41. data/test/info-var-bug.rb +45 -0
  42. data/test/info-var.cmd +23 -0
  43. data/test/info-var.right +47 -0
  44. data/test/info.cmd +12 -0
  45. data/test/info.right +35 -0
  46. data/test/quit.cmd +9 -0
  47. data/test/quit.right +22 -0
  48. data/test/setshow.cmd +44 -0
  49. data/test/setshow.right +73 -0
  50. data/test/stepping.cmd +17 -0
  51. data/test/stepping.right +40 -0
  52. data/test/tdebug.rb +196 -0
  53. data/test/test-breakpoints.rb +28 -0
  54. data/test/test-columnize.rb +46 -0
  55. data/test/test-display.rb +26 -0
  56. data/test/test-frame.rb +27 -0
  57. data/test/test-help.rb +44 -0
  58. data/test/test-info-var.rb +33 -0
  59. data/test/test-info.rb +28 -0
  60. data/test/test-quit.rb +28 -0
  61. data/test/test-ruby-debug-base.rb +76 -0
  62. data/test/test-setshow.rb +24 -0
  63. data/test/test-stepping.rb +26 -0
  64. metadata +63 -22
data/cli/ruby-debug.rb CHANGED
@@ -9,11 +9,25 @@ module Debugger
9
9
  self.handler = CommandProcessor.new
10
10
 
11
11
  # the port number used for remote debugging
12
- PORT = 8989
12
+ PORT = 8989 unless defined?(PORT)
13
13
 
14
+ # What file is used for debugger startup commands.
15
+ unless defined?(INITFILE)
16
+ if RUBY_PLATFORM =~ /mswin/
17
+ # Of course MS Windows has to be different
18
+ INITFILE = 'rdebug.ini'
19
+ HOME_DIR = (ENV['HOME'] ||
20
+ ENV['HOMEDRIVE'].to_s + ENV['HOMEPATH'].to_s).to_s
21
+ else
22
+ INITFILE = '.rdebugrc'
23
+ HOME_DIR = ENV['HOME'].to_s
24
+ end
25
+ end
26
+
14
27
  class << self
15
28
  # in remote mode, wait for the remote connection
16
29
  attr_accessor :wait_connection
30
+ attr_accessor :annotate
17
31
 
18
32
  attr_reader :thread, :control_thread
19
33
 
@@ -105,13 +119,29 @@ module Debugger
105
119
  socket.close
106
120
  end
107
121
 
122
+ # Runs normal debugger initialization scripts
123
+ # Reads and executes the commands from init file (if any) in the
124
+ # current working directory. This is only done if the current
125
+ # directory is different from your home directory. Thus, you can
126
+ # have more than one init file, one generic in your home directory,
127
+ # and another, specific to the program you are debugging, in the
128
+ # directory where you invoke ruby-debug.
129
+ def run_init_script(out = handler.interface)
130
+ cwd_script_file = File.expand_path(File.join(".", INITFILE))
131
+ run_script(cwd_script_file, out) if File.exists?(cwd_script_file)
132
+
133
+ home_script_file = File.expand_path(File.join(HOME_DIR, INITFILE))
134
+ run_script(home_script_file, out) if File.exists?(home_script_file) and
135
+ cwd_script_file != home_script_file
136
+ end
137
+
108
138
  #
109
139
  # Runs a script file
110
140
  #
111
141
  def run_script(file, out = handler.interface)
112
- interface = ScriptInterface.new(file, out)
142
+ interface = ScriptInterface.new(File.expand_path(file), out)
113
143
  processor = ControlCommandProcessor.new(interface)
114
144
  processor.process_commands
115
145
  end
116
146
  end
117
- end
147
+ end
@@ -1,4 +1,7 @@
1
+ require 'ruby-debug/helper'
2
+
1
3
  module Debugger
4
+
2
5
  class Command # :nodoc:
3
6
  class << self
4
7
  def commands
@@ -8,10 +11,10 @@ module Debugger
8
11
  DEF_OPTIONS = {
9
12
  :event => true,
10
13
  :control => false,
11
- :always_run => false,
14
+ :always_run => 0,
12
15
  :unknown => false,
13
16
  :need_context => false,
14
- }
17
+ } unless defined?(DEF_OPTIONS)
15
18
 
16
19
  def inherited(klass)
17
20
  DEF_OPTIONS.each do |o, v|
@@ -25,6 +28,9 @@ module Debugger
25
28
  Dir[File.join(dir, 'commands', '*')].each do |file|
26
29
  require file if file =~ /\.rb$/
27
30
  end
31
+ Debugger.constants.grep(/Functions$/).map { |name| Debugger.const_get(name) }.each do |mod|
32
+ include mod
33
+ end
28
34
  end
29
35
 
30
36
  def method_missing(meth, *args, &block)
@@ -49,16 +55,32 @@ module Debugger
49
55
  private :settings_map
50
56
 
51
57
  def settings
52
- unless @settings
58
+ unless true and defined? @settings and @settings
53
59
  @settings = Object.new
54
60
  map = settings_map
55
- class << @settings; self end.send(:define_method, :[]) do |name|
56
- raise "No such setting #{name}" unless map.has_key?(name)
57
- map[name][:getter].call
61
+ c = class << @settings; self end
62
+ if c.respond_to?(:funcall)
63
+ c.funcall(:define_method, :[]) do |name|
64
+ raise "No such setting #{name}" unless map.has_key?(name)
65
+ map[name][:getter].call
66
+ end
67
+ else
68
+ c.send(:define_method, :[]) do |name|
69
+ raise "No such setting #{name}" unless map.has_key?(name)
70
+ map[name][:getter].call
71
+ end
58
72
  end
59
- class << @settings; self end.send(:define_method, :[]=) do |name, value|
60
- raise "No such setting #{name}" unless map.has_key?(name)
61
- map[name][:setter].call(value)
73
+ c = class << @settings; self end
74
+ if c.respond_to?(:funcall)
75
+ c.funcall(:define_method, :[]=) do |name, value|
76
+ raise "No such setting #{name}" unless map.has_key?(name)
77
+ map[name][:setter].call(value)
78
+ end
79
+ else
80
+ c.send(:define_method, :[]=) do |name, value|
81
+ raise "No such setting #{name}" unless map.has_key?(name)
82
+ map[name][:setter].call(value)
83
+ end
62
84
  end
63
85
  end
64
86
  @settings
@@ -82,10 +104,25 @@ module Debugger
82
104
  end
83
105
  end
84
106
 
85
- register_setting_var(:stack_trace_on_error, false)
86
- register_setting_var(:frame_full_path, true)
87
- register_setting_var(:frame_class_names, false)
107
+ register_setting_var(:basename, false) # use basename in showing files?
108
+ register_setting_var(:callstyle, :last)
109
+ register_setting_var(:debuggertesting, false)
88
110
  register_setting_var(:force_stepping, false)
111
+ register_setting_var(:full_path, true)
112
+ register_setting_var(:listsize, 10) # number of lines in list command
113
+ register_setting_var(:stack_trace_on_error, false)
114
+ register_setting_var(:tracing_plus, false) # different linetrace lines?
115
+
116
+ # width of line output. Use COLUMNS value if it exists and is
117
+ # not too rediculously large.
118
+ width = ENV['COLUMNS'].to_i
119
+ width = 80 unless width > 10
120
+ register_setting_var(:width, width)
121
+
122
+ if not defined? Debugger::ARGV
123
+ Debugger::ARGV = ARGV.clone
124
+ end
125
+ register_setting_var(:argv, Debugger::ARGV)
89
126
 
90
127
  def initialize(state)
91
128
  @state = state
@@ -5,11 +5,11 @@ module Debugger
5
5
  def regexp
6
6
  / ^\s*
7
7
  b(?:reak)?
8
- \s+
8
+ (?: \s+
9
9
  (?:
10
10
  (\d+) |
11
11
  (.+?)[:.#]([^.:\s]+)
12
- )
12
+ ))?
13
13
  (?:\s+
14
14
  if\s+(.+)
15
15
  )?
@@ -19,35 +19,50 @@ module Debugger
19
19
 
20
20
  def execute
21
21
  if @match[1]
22
- pos, _, _, expr = @match.captures
22
+ line, _, _, expr = @match.captures
23
23
  else
24
- _, file, pos, expr = @match.captures
24
+ _, file, line, expr = @match.captures
25
25
  end
26
-
26
+
27
+ full_file = nil
27
28
  if file.nil?
29
+ full_file = @state.file
28
30
  file = File.basename(@state.file)
29
- else
30
- if pos !~ /^\d+$/
31
- klass = debug_silent_eval(file)
32
- if klass && !klass.kind_of?(Module)
33
- print "Unknown class #{file}\n"
34
- throw :debug_error
35
- end
36
- file = klass.name if klass
31
+ if line.nil?
32
+ # Set breakpoint at current line
33
+ line = @state.line.to_s
34
+ end
35
+ elsif line !~ /^\d+$/
36
+ # See if "line" is a method/function name
37
+ klass = debug_silent_eval(file)
38
+ if klass && klass.kind_of?(Module)
39
+ class_name = klass.name if klass
37
40
  else
38
- file = File.expand_path(file) if file.index(File::SEPARATOR) || \
39
- File::ALT_SEPARATOR && file.index(File::ALT_SEPARATOR)
41
+ print "Unknown class #{file}.\n"
42
+ throw :debug_error
40
43
  end
44
+ else
45
+ file = File.expand_path(file) if file.index(File::SEPARATOR) || \
46
+ File::ALT_SEPARATOR && file.index(File::ALT_SEPARATOR)
47
+ full_file = file
41
48
  end
42
49
 
43
- if pos =~ /^\d+$/
44
- pos = pos.to_i
50
+ if line =~ /^\d+$/
51
+ line = line.to_i
52
+ lines = Debugger.source_for(full_file)
53
+ if not lines
54
+ print "No source file named %s\n", file
55
+ elsif lines.size < line
56
+ print "No line %d in file \"%s\"\n", line, file
57
+ else
58
+ b = Debugger.add_breakpoint file, line, expr
59
+ print "Breakpoint %d file %s, line %s\n", b.id, file, line.to_s
60
+ end
45
61
  else
46
- pos = pos.intern.id2name
62
+ method = line.intern.id2name
63
+ b = Debugger.add_breakpoint class_name, method, expr
64
+ print "Breakpoint %d at %s::%s\n", b.id, class_name, method.to_s
47
65
  end
48
-
49
- b = Debugger.add_breakpoint file, pos, expr
50
- print "Set breakpoint %d at %s:%s\n", b.id, file, pos.to_s
51
66
  end
52
67
 
53
68
  class << self
@@ -65,58 +80,26 @@ module Debugger
65
80
  end
66
81
  end
67
82
 
68
- class BreakpointsCommand < Command # :nodoc:
69
- self.control = true
70
-
71
- def regexp
72
- /^\s*b(?:reak)?$/
73
- end
74
-
75
- def execute
76
- unless Debugger.breakpoints.empty?
77
- print "Breakpoints:\n"
78
- Debugger.breakpoints.sort_by{|b| b.id }.each do |b|
79
- if b.expr.nil?
80
- print " %d %s:%s\n", b.id, b.source, b.pos
81
- else
82
- print " %d %s:%s if %s\n", b.id, b.source, b.pos, b.expr
83
- end
84
- end
85
- else
86
- print "No breakpoints\n"
87
- end
88
- end
89
-
90
- class << self
91
- def help_command
92
- 'break'
93
- end
94
-
95
- def help(cmd)
96
- %{
97
- b[reak]\tlist breakpoints
98
- }
99
- end
100
- end
101
- end
102
-
103
83
  class DeleteBreakpointCommand < Command # :nodoc:
104
84
  self.control = true
105
85
 
106
86
  def regexp
107
- /^\s*del(?:ete)?(?:\s+(\d+))?$/
87
+ /^\s*del(?:ete)?(?:\s+(.*))?$/
108
88
  end
109
89
 
110
90
  def execute
111
- pos = @match[1]
112
- unless pos
113
- if confirm("Clear all breakpoints? (y/n) ")
91
+ brkpts = @match[1]
92
+ unless brkpts
93
+ if confirm("Delete all breakpoints? (y or n) ")
114
94
  Debugger.breakpoints.clear
115
95
  end
116
96
  else
117
- pos = pos.to_i
118
- unless Debugger.remove_breakpoint(pos)
119
- print "Breakpoint %d is not defined\n", pos
97
+ brkpts.split(/[ \t]+/).each do |pos|
98
+ pos = get_int(pos, "Delete", 1)
99
+ return unless pos
100
+ unless Debugger.remove_breakpoint(pos)
101
+ print "No breakpoint number %d\n", pos
102
+ end
120
103
  end
121
104
  end
122
105
  end
@@ -128,7 +111,7 @@ module Debugger
128
111
 
129
112
  def help(cmd)
130
113
  %{
131
- del[ete][ nnn]\tdelete some or all breakpoints
114
+ del[ete][ nnn...]\tdelete some or all breakpoints
132
115
  }
133
116
  end
134
117
  end
@@ -3,11 +3,11 @@ module Debugger
3
3
  self.control = true
4
4
 
5
5
  def regexp
6
- /^\s*(?:q(?:uit)?|exit)\s*$/
6
+ /^\s*(?:q(?:uit)?|exit)\s*(\s+unconditionally)?\s*$/
7
7
  end
8
8
 
9
9
  def execute
10
- if confirm("Really quit? (y/n) ")
10
+ if @match[1] or confirm("Really quit? (y/n) ")
11
11
  Debugger.save_history if Debugger.respond_to? :save_history
12
12
  exit! # exit -> exit!: No graceful way to stop threads...
13
13
  end
@@ -20,8 +20,11 @@ module Debugger
20
20
 
21
21
  def help(cmd)
22
22
  %{
23
- q[uit]\texit from debugger,
23
+ q[uit] [unconditionally]\texit from debugger.
24
24
  exit\talias to quit
25
+
26
+ Normally we prompt before exiting. However if the parameter
27
+ "unconditionally" is given, we stop without asking further questions.
25
28
  }
26
29
  end
27
30
  end
@@ -32,25 +35,50 @@ module Debugger
32
35
 
33
36
  def regexp
34
37
  / ^\s*
35
- (restart|R)
36
- (\s+ \S+ .*)?
38
+ (?:restart|R)
39
+ (?:\s+ (\S?.*\S))? \s*
37
40
  $
38
- /x
41
+ /ix
39
42
  end
40
43
 
41
44
  def execute
42
- if not defined? Debugger::RDEBUG_SCRIPT or not defined? Debugger::ARGV
43
- print "We are not in a context we can restart from.\n"
45
+ if not defined? Debugger::RDEBUG_SCRIPT
46
+ # FIXME? Should ask for confirmation?
47
+ print "Debugger was not called from the outset...\n"
48
+ rdebug_script = ''
49
+ else
50
+ rdebug_script = Debugger::RDEBUG_SCRIPT + " "
51
+ end
52
+ prog_script = Debugger::PROG_SCRIPT
53
+ begin
54
+ Dir.chdir(Debugger::INITIAL_DIR)
55
+ rescue
56
+ print "Failed to change initial directory #{Debugger::INITIAL_DIR}"
57
+ end
58
+ if not File.exists?(prog_script)
59
+ print "Ruby program #{prog_script} doesn't exist\n"
44
60
  return
45
61
  end
46
- if @match[2]
47
- args = Debugger::PROG_SCRIPT + " " + @match[2]
62
+ if not File.executable?(prog_script) and rdebug_script == ''
63
+ print "Ruby program #{prog_script} doesn't seem to be executable...\n"
64
+ print "We'll add a call to Ruby.\n"
65
+ ruby = begin defined?(Gem) ? Gem.ruby : "ruby" rescue "ruby" end
66
+ rdebug_script = "#{ruby} -I#{$:.join(' -I')} #{prog_script}"
67
+ end
68
+ if @match[1]
69
+ argv = [prog_script] + @match[1].split(/[ \t]+/)
48
70
  else
49
- args = Debugger::ARGV.join(" ")
71
+ if not defined? Command.settings[:argv]
72
+ print "Arguments have not been set. Use 'set args' to set them.\n"
73
+ return
74
+ else
75
+ argv = Command.settings[:argv]
76
+ end
50
77
  end
78
+ args = argv.join(" ")
51
79
 
52
80
  # An execv would be preferable to the "exec" below.
53
- cmd = Debugger::RDEBUG_SCRIPT + " " + args
81
+ cmd = rdebug_script + args
54
82
  print "Re exec'ing:\n\t#{cmd}\n"
55
83
  exec cmd
56
84
  rescue Errno::EOPNOTSUPP
@@ -100,4 +128,4 @@ module Debugger
100
128
  end
101
129
  end
102
130
  end
103
- end
131
+ end
@@ -3,11 +3,24 @@ module Debugger
3
3
  def display_expression(exp)
4
4
  print "%s = %s\n", exp, debug_silent_eval(exp).to_s
5
5
  end
6
+
7
+ def active_display_expressions?
8
+ @state.display.select{|d| d[0]}.size > 0
9
+ end
10
+
11
+ def print_display_expressions
12
+ n = 1
13
+ for d in @state.display
14
+ if d[0]
15
+ print "%d: ", n
16
+ display_expression(d[1])
17
+ end
18
+ n += 1
19
+ end
20
+ end
6
21
  end
7
22
 
8
23
  class AddDisplayCommand < Command # :nodoc:
9
- include DisplayFunctions
10
-
11
24
  def regexp
12
25
  /^\s*disp(?:lay)?\s+(.+)$/
13
26
  end
@@ -33,22 +46,21 @@ module Debugger
33
46
  end
34
47
 
35
48
  class DisplayCommand < Command # :nodoc:
36
- self.always_run = true
37
- include DisplayFunctions
49
+ def self.always_run
50
+ Debugger.annotate = 0 unless Debugger.annotate
51
+ if Debugger.annotate > 1
52
+ 0
53
+ else
54
+ 2
55
+ end
56
+ end
38
57
 
39
58
  def regexp
40
59
  /^\s*disp(?:lay)?$/
41
60
  end
42
61
 
43
62
  def execute
44
- n = 1
45
- for d in @state.display
46
- if d[0]
47
- print "%d: ", n
48
- display_expression(d[1])
49
- end
50
- n += 1
51
- end
63
+ print_display_expressions
52
64
  end
53
65
 
54
66
  class << self
@@ -65,10 +77,9 @@ module Debugger
65
77
  end
66
78
 
67
79
  class DeleteDisplayCommand < Command # :nodoc:
68
- include DisplayFunctions
69
80
 
70
81
  def regexp
71
- /^\s*undisp(?:lay)?(?:\s+(\d+))?$/
82
+ /^\s* undisp(?:lay)? \s* (?:(\S+))?$/x
72
83
  end
73
84
 
74
85
  def execute
@@ -79,9 +90,10 @@ module Debugger
79
90
  end
80
91
  end
81
92
  else
82
- pos = pos.to_i
93
+ pos = get_int(pos, "Undisplay")
94
+ return unless pos
83
95
  if @state.display[pos-1]
84
- @state.display[pos-1][0] = false
96
+ @state.display[pos-1][0] = nil
85
97
  else
86
98
  print "Display expression %d is not defined\n", pos
87
99
  end
@@ -95,9 +107,14 @@ module Debugger
95
107
 
96
108
  def help(cmd)
97
109
  %{
98
- undisp[lay][ nnn]\tdelete one particular or all display expressions
110
+ undisp[lay][ nnn]
111
+ Cancel some expressions to be displayed when program stops.
112
+ Arguments are the code numbers of the expressions to stop displaying.
113
+ No argument means cancel all automatic-display expressions.
114
+ "delete display" has the same effect as this command.
115
+ Do "info display" to see current list of code numbers.
99
116
  }
100
117
  end
101
118
  end
102
119
  end
103
- end
120
+ end