byebug 0.0.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.
Files changed (133) hide show
  1. data/.gitignore +10 -0
  2. data/.travis.yml +8 -0
  3. data/AUTHORS +10 -0
  4. data/CHANGELOG.md +2 -0
  5. data/CONTRIBUTING.md +1 -0
  6. data/Gemfile +3 -0
  7. data/LICENSE +20 -0
  8. data/README.md +5 -0
  9. data/Rakefile +28 -0
  10. data/bin/byebug +395 -0
  11. data/byebug.gemspec +29 -0
  12. data/doc/hanoi.rb +35 -0
  13. data/doc/primes.rb +28 -0
  14. data/doc/rdebug-emacs.texi +1030 -0
  15. data/doc/test-tri2.rb +18 -0
  16. data/doc/tri3.rb +8 -0
  17. data/doc/triangle.rb +12 -0
  18. data/ext/byebug/breakpoint.c +476 -0
  19. data/ext/byebug/byebug.c +512 -0
  20. data/ext/byebug/byebug.h +131 -0
  21. data/ext/byebug/context.c +424 -0
  22. data/ext/byebug/extconf.rb +21 -0
  23. data/ext/byebug/locker.c +53 -0
  24. data/lib/byebug.rb +404 -0
  25. data/lib/byebug/command.rb +232 -0
  26. data/lib/byebug/commands/breakpoints.rb +153 -0
  27. data/lib/byebug/commands/catchpoint.rb +56 -0
  28. data/lib/byebug/commands/condition.rb +49 -0
  29. data/lib/byebug/commands/continue.rb +38 -0
  30. data/lib/byebug/commands/control.rb +110 -0
  31. data/lib/byebug/commands/display.rb +122 -0
  32. data/lib/byebug/commands/edit.rb +48 -0
  33. data/lib/byebug/commands/enable.rb +202 -0
  34. data/lib/byebug/commands/eval.rb +176 -0
  35. data/lib/byebug/commands/finish.rb +43 -0
  36. data/lib/byebug/commands/frame.rb +303 -0
  37. data/lib/byebug/commands/help.rb +56 -0
  38. data/lib/byebug/commands/info.rb +462 -0
  39. data/lib/byebug/commands/irb.rb +123 -0
  40. data/lib/byebug/commands/jump.rb +66 -0
  41. data/lib/byebug/commands/kill.rb +51 -0
  42. data/lib/byebug/commands/list.rb +94 -0
  43. data/lib/byebug/commands/method.rb +84 -0
  44. data/lib/byebug/commands/quit.rb +39 -0
  45. data/lib/byebug/commands/reload.rb +40 -0
  46. data/lib/byebug/commands/save.rb +90 -0
  47. data/lib/byebug/commands/set.rb +210 -0
  48. data/lib/byebug/commands/show.rb +246 -0
  49. data/lib/byebug/commands/skip.rb +35 -0
  50. data/lib/byebug/commands/source.rb +36 -0
  51. data/lib/byebug/commands/stepping.rb +83 -0
  52. data/lib/byebug/commands/threads.rb +189 -0
  53. data/lib/byebug/commands/tmate.rb +36 -0
  54. data/lib/byebug/commands/trace.rb +56 -0
  55. data/lib/byebug/commands/variables.rb +199 -0
  56. data/lib/byebug/context.rb +58 -0
  57. data/lib/byebug/helper.rb +69 -0
  58. data/lib/byebug/interface.rb +223 -0
  59. data/lib/byebug/processor.rb +468 -0
  60. data/lib/byebug/version.rb +3 -0
  61. data/man/rdebug.1 +241 -0
  62. data/test/breakpoints_test.rb +357 -0
  63. data/test/conditions_test.rb +77 -0
  64. data/test/continue_test.rb +44 -0
  65. data/test/display_test.rb +141 -0
  66. data/test/edit_test.rb +56 -0
  67. data/test/eval_test.rb +92 -0
  68. data/test/examples/breakpoint1.rb +15 -0
  69. data/test/examples/breakpoint2.rb +7 -0
  70. data/test/examples/conditions.rb +4 -0
  71. data/test/examples/continue.rb +4 -0
  72. data/test/examples/display.rb +5 -0
  73. data/test/examples/edit.rb +3 -0
  74. data/test/examples/edit2.rb +3 -0
  75. data/test/examples/eval.rb +4 -0
  76. data/test/examples/finish.rb +20 -0
  77. data/test/examples/frame.rb +20 -0
  78. data/test/examples/frame_threads.rb +31 -0
  79. data/test/examples/help.rb +2 -0
  80. data/test/examples/info.rb +38 -0
  81. data/test/examples/info2.rb +3 -0
  82. data/test/examples/info_threads.rb +48 -0
  83. data/test/examples/irb.rb +6 -0
  84. data/test/examples/jump.rb +14 -0
  85. data/test/examples/kill.rb +2 -0
  86. data/test/examples/list.rb +12 -0
  87. data/test/examples/method.rb +15 -0
  88. data/test/examples/post_mortem.rb +19 -0
  89. data/test/examples/quit.rb +2 -0
  90. data/test/examples/reload.rb +6 -0
  91. data/test/examples/restart.rb +6 -0
  92. data/test/examples/save.rb +3 -0
  93. data/test/examples/set.rb +3 -0
  94. data/test/examples/set_annotate.rb +12 -0
  95. data/test/examples/settings.rb +1 -0
  96. data/test/examples/show.rb +2 -0
  97. data/test/examples/source.rb +3 -0
  98. data/test/examples/stepping.rb +21 -0
  99. data/test/examples/thread.rb +32 -0
  100. data/test/examples/tmate.rb +10 -0
  101. data/test/examples/trace.rb +7 -0
  102. data/test/examples/trace_threads.rb +20 -0
  103. data/test/examples/variables.rb +26 -0
  104. data/test/finish_test.rb +48 -0
  105. data/test/frame_test.rb +143 -0
  106. data/test/help_test.rb +50 -0
  107. data/test/info_test.rb +313 -0
  108. data/test/irb_test.rb +81 -0
  109. data/test/jump_test.rb +70 -0
  110. data/test/kill_test.rb +48 -0
  111. data/test/list_test.rb +145 -0
  112. data/test/method_test.rb +70 -0
  113. data/test/post_mortem_test.rb +27 -0
  114. data/test/quit_test.rb +56 -0
  115. data/test/reload_test.rb +44 -0
  116. data/test/restart_test.rb +164 -0
  117. data/test/save_test.rb +92 -0
  118. data/test/set_test.rb +177 -0
  119. data/test/show_test.rb +293 -0
  120. data/test/source_test.rb +45 -0
  121. data/test/stepping_test.rb +130 -0
  122. data/test/support/breakpoint.rb +13 -0
  123. data/test/support/context.rb +14 -0
  124. data/test/support/matchers.rb +67 -0
  125. data/test/support/mocha_extensions.rb +72 -0
  126. data/test/support/processor.rb +7 -0
  127. data/test/support/test_dsl.rb +206 -0
  128. data/test/support/test_interface.rb +68 -0
  129. data/test/test_helper.rb +10 -0
  130. data/test/tmate_test.rb +44 -0
  131. data/test/trace_test.rb +159 -0
  132. data/test/variables_test.rb +119 -0
  133. metadata +265 -0
@@ -0,0 +1,232 @@
1
+ require 'columnize'
2
+ require_relative 'helper'
3
+
4
+ module Byebug
5
+
6
+ BYEBUG_DIR = File.expand_path(File.dirname(__FILE__)) unless
7
+ defined?(BYEBUG_DIR)
8
+
9
+ class Command # :nodoc:
10
+ SubcmdStruct = Struct.new(:name, :min, :short_help, :long_help) unless
11
+ defined?(SubcmdStruct)
12
+
13
+ include Columnize
14
+
15
+ # Find param in subcmds. param id downcased and can be abbreviated
16
+ # to the minimum length listed in the subcommands
17
+ def find(subcmds, param)
18
+ param.downcase!
19
+ for try_subcmd in subcmds do
20
+ if (param.size >= try_subcmd.min) and
21
+ (try_subcmd.name[0..param.size-1] == param)
22
+ return try_subcmd
23
+ end
24
+ end
25
+ return nil
26
+ end
27
+
28
+ class << self
29
+ def commands
30
+ @commands ||= []
31
+ end
32
+
33
+ DEF_OPTIONS = {
34
+ :allow_in_control => false,
35
+ :allow_in_post_mortem => true,
36
+ :event => true,
37
+ :always_run => 0,
38
+ :unknown => false,
39
+ :need_context => false,
40
+ } unless defined?(DEF_OPTIONS)
41
+
42
+ def inherited(klass)
43
+ DEF_OPTIONS.each do |o, v|
44
+ klass.options[o] = v if klass.options[o].nil?
45
+ end
46
+ commands << klass
47
+ end
48
+
49
+ def load_commands
50
+ Dir[File.join(Byebug.const_get(:BYEBUG_DIR), 'commands', '*')].each do |file|
51
+ require file if file =~ /\.rb$/
52
+ end
53
+ Byebug.constants.grep(/Functions$/).map { |name| Byebug.const_get(name) }.each do |mod|
54
+ include mod
55
+ end
56
+ end
57
+
58
+ def method_missing(meth, *args, &block)
59
+ if meth.to_s =~ /^(.+?)=$/
60
+ @options[$1.intern] = args.first
61
+ else
62
+ if @options.has_key?(meth)
63
+ @options[meth]
64
+ else
65
+ super
66
+ end
67
+ end
68
+ end
69
+
70
+ def options
71
+ @options ||= {}
72
+ end
73
+
74
+ def settings_map
75
+ @@settings_map ||= {}
76
+ end
77
+ private :settings_map
78
+
79
+ def settings
80
+ unless true and defined? @settings and @settings
81
+ @settings = Object.new
82
+ map = settings_map
83
+ c = class << @settings; self end
84
+ if c.respond_to?(:funcall)
85
+ c.funcall(:define_method, :[]) do |name|
86
+ raise "No such setting #{name}" unless map.has_key?(name)
87
+ map[name][:getter].call
88
+ end
89
+ else
90
+ c.send(:define_method, :[]) do |name|
91
+ raise "No such setting #{name}" unless map.has_key?(name)
92
+ map[name][:getter].call
93
+ end
94
+ end
95
+ c = class << @settings; self end
96
+ if c.respond_to?(:funcall)
97
+ c.funcall(:define_method, :[]=) do |name, value|
98
+ raise "No such setting #{name}" unless map.has_key?(name)
99
+ map[name][:setter].call(value)
100
+ end
101
+ else
102
+ c.send(:define_method, :[]=) do |name, value|
103
+ raise "No such setting #{name}" unless map.has_key?(name)
104
+ map[name][:setter].call(value)
105
+ end
106
+ end
107
+ end
108
+ @settings
109
+ end
110
+
111
+ def register_setting_var(name, default)
112
+ var_name = "@@#{name}"
113
+ class_variable_set(var_name, default)
114
+ register_setting_get(name) { class_variable_get(var_name) }
115
+ register_setting_set(name) { |value| class_variable_set(var_name, value) }
116
+ end
117
+
118
+ def register_setting_get(name, &block)
119
+ settings_map[name] ||= {}
120
+ settings_map[name][:getter] = block
121
+ end
122
+
123
+ def register_setting_set(name, &block)
124
+ settings_map[name] ||= {}
125
+ settings_map[name][:setter] = block
126
+ end
127
+ end
128
+
129
+ register_setting_var(:basename, false) # use basename in showing files?
130
+ register_setting_var(:callstyle, :last)
131
+ register_setting_var(:byebugtesting, false)
132
+ register_setting_var(:force_stepping, false)
133
+ register_setting_var(:full_path, true)
134
+ register_setting_var(:listsize, 10) # number of lines in list command
135
+ register_setting_var(:stack_trace_on_error, false)
136
+ register_setting_var(:tracing_plus, false) # different linetrace lines?
137
+
138
+ # width of line output. Use COLUMNS value if it exists and is not too large.
139
+ width = ENV['COLUMNS'].to_i
140
+ width = 80 unless width > 10
141
+ register_setting_var(:width, width)
142
+
143
+ if not defined? Byebug::ARGV
144
+ Byebug::ARGV = ARGV.clone
145
+ end
146
+ register_setting_var(:argv, Byebug::ARGV)
147
+
148
+ def initialize(state)
149
+ @state = state
150
+ end
151
+
152
+ def match(input)
153
+ @match = regexp.match(input)
154
+ end
155
+
156
+ protected
157
+
158
+ # FIXME: use delegate?
159
+ def errmsg(*args)
160
+ @state.errmsg(*args)
161
+ end
162
+
163
+ def print(*args)
164
+ @state.print(*args)
165
+ end
166
+
167
+ def confirm(msg)
168
+ @state.confirm(msg) == 'y'
169
+ end
170
+
171
+ def debug_eval(str, b = get_binding)
172
+ begin
173
+ val = eval(str, b)
174
+ rescue StandardError, ScriptError => e
175
+ if Command.settings[:stack_trace_on_error]
176
+ at = eval("caller(1)", b)
177
+ print "%s:%s\n", at.shift, e.to_s.sub(/\(eval\):1:(in `.*?':)?/, '')
178
+ for i in at
179
+ print "\tfrom %s\n", i
180
+ end
181
+ else
182
+ print "#{e.class} Exception: #{e.message}\n"
183
+ end
184
+ throw :debug_error
185
+ end
186
+ end
187
+
188
+ def debug_silent_eval(str)
189
+ begin
190
+ eval(str, get_binding)
191
+ rescue StandardError, ScriptError
192
+ nil
193
+ end
194
+ end
195
+
196
+ def get_binding
197
+ @state.context.frame_binding(@state.frame_pos)
198
+ end
199
+
200
+ def line_at(file, line)
201
+ Byebug.line_at(file, line)
202
+ end
203
+
204
+ def get_context(thnum)
205
+ Byebug.contexts.find{|c| c.thnum == thnum}
206
+ end
207
+ end
208
+
209
+ Command.load_commands
210
+
211
+ #
212
+ # Returns ths settings object.
213
+ # Use Byebug.settings[] and Byebug.settings[]= methods to query and set
214
+ # byebug settings. These settings are available:
215
+ #
216
+ # :autolist - automatically calls 'list' command on breakpoint
217
+ # :autoeval - evaluates input in the current binding if it's
218
+ # not recognized as a byebug command
219
+ # :autoirb - automatically calls 'irb' command on breakpoint
220
+ # :stack_trace_on_error - shows full stack trace if eval command results in
221
+ # an exception
222
+ # :frame_full_path - displays full paths when showing frame stack
223
+ # :frame_class_names - displays method's class name when showing frame
224
+ # stack
225
+ # :reload_source_on_change - makes 'list' command always display up-to-date
226
+ # source code
227
+ # :force_stepping - stepping command always move to the new line
228
+ #
229
+ def self.settings
230
+ Command.settings
231
+ end
232
+ end
@@ -0,0 +1,153 @@
1
+ module Byebug
2
+
3
+ # Implements byebug "break" command.
4
+ class AddBreakpoint < Command
5
+ self.allow_in_control = true
6
+
7
+ def regexp
8
+ / ^\s*
9
+ b(?:reak)?
10
+ (?: \s+ #{Position_regexp})? \s*
11
+ (?: \s+ (.*))? \s*
12
+ $
13
+ /x
14
+ end
15
+
16
+ def execute
17
+ if @match[1]
18
+ line, _, _, expr = @match.captures
19
+ else
20
+ _, file, line, expr = @match.captures
21
+ end
22
+ if expr
23
+ if expr !~ /^\s*if\s+(.+)/
24
+ if file or line
25
+ errmsg "Expecting 'if' in breakpoint condition; got: #{expr}.\n"
26
+ else
27
+ errmsg "Invalid breakpoint location: #{expr}.\n"
28
+ end
29
+ return
30
+ else
31
+ expr = $1
32
+ end
33
+ end
34
+
35
+ brkpt_filename = nil
36
+ if file.nil?
37
+ unless @state.context
38
+ errmsg "We are not in a state that has an associated file.\n"
39
+ return
40
+ end
41
+ brkpt_filename = @state.file
42
+ file = File.basename(@state.file)
43
+ if line.nil?
44
+ # Set breakpoint at current line
45
+ line = @state.line.to_s
46
+ end
47
+ elsif line !~ /^\d+$/
48
+ # See if "line" is a method/function name
49
+ klass = debug_silent_eval(file)
50
+ if klass && klass.kind_of?(Module)
51
+ class_name = klass.name if klass
52
+ else
53
+ errmsg "Unknown class #{file}.\n"
54
+ throw :debug_error
55
+ end
56
+ else
57
+ # FIXME: This should be done in LineCache.
58
+ file = File.expand_path(file) if file.index(File::SEPARATOR) || \
59
+ File::ALT_SEPARATOR && file.index(File::ALT_SEPARATOR)
60
+ brkpt_filename = file
61
+ end
62
+
63
+ if line =~ /^\d+$/
64
+ line = line.to_i
65
+ if LineCache.cache(brkpt_filename, Command.settings[:reload_source_on_change])
66
+ last_line = LineCache.size(brkpt_filename)
67
+ if line > last_line
68
+ errmsg("There are only %d lines in file \"%s\".\n", last_line, file)
69
+ return
70
+ end
71
+ unless LineCache.trace_line_numbers(brkpt_filename).member?(line)
72
+ errmsg("Line %d is not a stopping point in file \"%s\".\n", line, file)
73
+ return
74
+ end
75
+ else
76
+ errmsg("No source file named %s\n" % file)
77
+ return unless confirm("Set breakpoint anyway? (y/n) ")
78
+ end
79
+
80
+ unless @state.context
81
+ errmsg "We are not in a state we can add breakpoints.\n"
82
+ return
83
+ end
84
+ b = Byebug.add_breakpoint brkpt_filename, line, expr
85
+ print "Breakpoint %d file %s, line %s\n", b.id, brkpt_filename, line.to_s
86
+ unless syntax_valid?(expr)
87
+ errmsg("Expression \"#{expr}\" syntactically incorrect; breakpoint disabled.\n")
88
+ b.enabled = false
89
+ end
90
+ else
91
+ method = line.intern
92
+ b = Byebug.add_breakpoint class_name, method, expr
93
+ print "Breakpoint %d at %s::%s\n", b.id, class_name, method.to_s
94
+ end
95
+ end
96
+
97
+ class << self
98
+ def help_command
99
+ 'break'
100
+ end
101
+
102
+ def help(cmd)
103
+ %{
104
+ b[reak] file:line [if expr]
105
+ b[reak] class(.|#)method [if expr]
106
+ \tset breakpoint to some position, (optionally) if expr == true
107
+ }
108
+ end
109
+ end
110
+ end
111
+
112
+ # Implements byebug "delete" command.
113
+ class DeleteBreakpointCommand < Command
114
+ self.allow_in_control = true
115
+
116
+ def regexp
117
+ /^\s *del(?:ete)? (?:\s+(.*))?$/ix
118
+ end
119
+
120
+ def execute
121
+ unless @state.context
122
+ errmsg "We are not in a state we can delete breakpoints.\n"
123
+ return
124
+ end
125
+ brkpts = @match[1]
126
+ unless brkpts
127
+ if confirm("Delete all breakpoints? (y or n) ")
128
+ Byebug.breakpoints.clear
129
+ end
130
+ else
131
+ brkpts.split(/[ \t]+/).each do |pos|
132
+ pos = get_int(pos, "Delete", 1)
133
+ return unless pos
134
+ unless Byebug.remove_breakpoint(pos)
135
+ errmsg "No breakpoint number %d\n", pos
136
+ end
137
+ end
138
+ end
139
+ end
140
+
141
+ class << self
142
+ def help_command
143
+ 'delete'
144
+ end
145
+
146
+ def help(cmd)
147
+ %{
148
+ del[ete][ nnn...]\tdelete some or all breakpoints
149
+ }
150
+ end
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,56 @@
1
+ module Byebug
2
+
3
+ class CatchCommand < Command # :nodoc:
4
+ self.allow_in_control = true
5
+
6
+ def regexp
7
+ /^\s* cat(?:ch)?
8
+ (?:\s+ (\S+))?
9
+ (?:\s+ (off))? \s* $/ix
10
+ end
11
+
12
+ def execute
13
+ excn = @match[1]
14
+ if not excn
15
+ # No args given.
16
+ info_catch
17
+ elsif not @match[2]
18
+ # One arg given.
19
+ if 'off' == excn
20
+ Byebug.catchpoints.clear if
21
+ confirm("Delete all catchpoints? (y or n) ")
22
+ else
23
+ binding = @state.context ? get_binding : TOPLEVEL_BINDING
24
+ unless debug_eval("#{excn}.is_a?(Class)", binding)
25
+ print "Warning #{excn} is not known to be a Class\n"
26
+ end
27
+ Byebug.add_catchpoint(excn)
28
+ print "Catch exception %s.\n", excn
29
+ end
30
+ elsif @match[2] != 'off'
31
+ errmsg "Off expected. Got %s\n", @match[2]
32
+ elsif Byebug.catchpoints.member?(excn)
33
+ Byebug.catchpoints.delete(excn)
34
+ print "Catch for exception %s removed.\n", excn
35
+ else
36
+ errmsg "Catch for exception %s not found.\n", excn
37
+ end
38
+ end
39
+
40
+ class << self
41
+ def help_command
42
+ 'catch'
43
+ end
44
+
45
+ def help(cmd)
46
+ %{
47
+ cat[ch]\t\tsame as "info catch"
48
+ cat[ch] <exception-name> [on|off]
49
+ \tIntercept <exception-name> when there would otherwise be no handler.
50
+ \tWith an "on" or "off", turn handling the exception on or off.
51
+ cat[ch] off\tdelete all catchpoints
52
+ }
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,49 @@
1
+ module Byebug
2
+
3
+ class ConditionCommand < Command # :nodoc:
4
+
5
+ def regexp
6
+ /^\s* cond(?:ition)? (?:\s+(\d+)\s*(.*))?$/ix
7
+ end
8
+
9
+ def execute
10
+ if not @match[1]
11
+ errmsg "\"condition\" must be followed by breakpoint number and expression\n"
12
+ else
13
+ breakpoints = Byebug.breakpoints.sort_by{|b| b.id }
14
+ largest = breakpoints.inject(0) do |tally, b|
15
+ tally = b.id if b.id > tally
16
+ end
17
+ if 0 == largest
18
+ print "No breakpoints have been set.\n"
19
+ return
20
+ end
21
+ pos = get_int(@match[1], "Condition", 1, largest)
22
+ return unless pos
23
+ breakpoints.each do |b|
24
+ if b.id == pos
25
+ b.expr = @match[2].empty? ? nil : @match[2]
26
+ break
27
+ end
28
+ end
29
+
30
+ end
31
+ end
32
+
33
+ class << self
34
+ def help_command
35
+ 'condition'
36
+ end
37
+
38
+ def help(cmd)
39
+ %{
40
+ Condition breakpoint-number expression
41
+ Specify breakpoint number N to break only if COND is true. N is an integer and
42
+ COND is an expression to be evaluated whenever breakpoint N is reached. If the
43
+ empty string is used, the condition is removed.
44
+ }
45
+ end
46
+ end
47
+ end
48
+
49
+ end # module Byebug