ruby-debug 0.9.3 → 0.10.0

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