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,21 @@
1
+ require 'mkmf'
2
+
3
+ if RUBY_VERSION < "2.0"
4
+ STDERR.print("Ruby version is too old\n")
5
+ exit(1)
6
+ end
7
+
8
+ $CFLAGS = '-Wall -Werror'
9
+ $CFLAGS += ' -g3' if ENV['debug']
10
+
11
+ dir_config("ruby")
12
+ create_makefile("byebug")
13
+
14
+ #if !Byebug::RubyCoreSource.create_makefile_with_core(hdrs, "ruby_debug")
15
+ # STDERR.print("Makefile creation failed\n")
16
+ # STDERR.print("*************************************************************\n\n")
17
+ # STDERR.print(" NOTE: If your headers were not found, try passing\n")
18
+ # STDERR.print(" --with-ruby-include=PATH_TO_HEADERS \n\n")
19
+ # STDERR.print("*************************************************************\n\n")
20
+ # exit(1)
21
+ #end
@@ -0,0 +1,53 @@
1
+ #include <byebug.h>
2
+
3
+ static locked_thread_t *locked_head = NULL;
4
+ static locked_thread_t *locked_tail = NULL;
5
+
6
+ extern int
7
+ is_in_locked(VALUE thread)
8
+ {
9
+ locked_thread_t *node;
10
+
11
+ if(!locked_head) return 0;
12
+
13
+ for(node = locked_head; node != locked_tail; node = node->next)
14
+ {
15
+ if(node->thread == thread) return 1;
16
+ }
17
+ return 0;
18
+ }
19
+
20
+ extern void
21
+ add_to_locked(VALUE thread)
22
+ {
23
+ locked_thread_t *node;
24
+
25
+ if(is_in_locked(thread)) return;
26
+
27
+ node = ALLOC(locked_thread_t);
28
+ node->thread = thread;
29
+ node->next = NULL;
30
+ if(locked_tail)
31
+ locked_tail->next = node;
32
+ locked_tail = node;
33
+ if(!locked_head)
34
+ locked_head = node;
35
+ }
36
+
37
+ extern VALUE
38
+ remove_from_locked()
39
+ {
40
+ VALUE thread;
41
+ locked_thread_t *node;
42
+
43
+ if(locked_head == NULL)
44
+ return Qnil;
45
+
46
+ node = locked_head;
47
+ locked_head = locked_head->next;
48
+ if(locked_tail == node)
49
+ locked_tail = NULL;
50
+ thread = node->thread;
51
+ xfree(node);
52
+ return thread;
53
+ }
@@ -0,0 +1,404 @@
1
+ require_relative 'byebug.so'
2
+ require_relative 'byebug/version'
3
+ require_relative 'byebug/context'
4
+ require_relative 'byebug/processor'
5
+ require 'pp'
6
+ require 'stringio'
7
+ require 'socket'
8
+ require 'thread'
9
+ require 'linecache19'
10
+
11
+ module Byebug
12
+
13
+ @reload_source_on_change = false
14
+ @tracing_started = false
15
+
16
+ self.handler = CommandProcessor.new
17
+
18
+ # Default options to Byebug.start
19
+ DEFAULT_START_SETTINGS = {
20
+ :init => true, # Set $0 and save ARGV?
21
+ :post_mortem => false, # post-mortem debugging on uncaught exception?
22
+ :tracing => nil # Byebug.tracing value. true/false resets,
23
+ } unless defined?(DEFAULT_START_SETTINGS)
24
+
25
+ # the port number used for remote debugging
26
+ PORT = 8989 unless defined?(PORT)
27
+
28
+ # What file is used for byebug startup commands.
29
+ unless defined?(INITFILE)
30
+ INITFILE = '.rdebugrc'
31
+ HOME_DIR = ENV['HOME'].to_s
32
+ end
33
+
34
+ class << self
35
+
36
+ # if true, checks the modification time of source files and reloads if it was modified
37
+ attr_accessor :reload_source_on_change
38
+
39
+ attr_accessor :last_exception
40
+ Byebug.last_exception = nil
41
+
42
+ # gdb-style annotation mode. Used in GNU Emacs interface
43
+ attr_accessor :annotate
44
+
45
+ # in remote mode, wait for the remote connection
46
+ attr_accessor :wait_connection
47
+
48
+ # If set, a string to look for in caller() and is used to see
49
+ # if the call stack is truncated.
50
+ attr_accessor :start_sentinal
51
+
52
+ attr_reader :thread, :control_thread, :cmd_port, :ctrl_port
53
+
54
+ #
55
+ # Interrupts the current thread
56
+ #
57
+ def interrupt
58
+ current_context.interrupt
59
+ end
60
+
61
+ #
62
+ # Interrupts the last debugged thread
63
+ #
64
+ def interrupt_last
65
+ if context = last_context
66
+ return nil unless context.thread.alive?
67
+ context.interrupt
68
+ end
69
+ context
70
+ end
71
+
72
+ def source_reload
73
+ Object.send(:remove_const, "SCRIPT_LINES__") if Object.const_defined?("SCRIPT_LINES__")
74
+ Object.const_set("SCRIPT_LINES__", {})
75
+ LineCache::clear_file_cache
76
+ end
77
+
78
+ # Get line +line_number+ from file named +filename+. Return "\n" if
79
+ # there was a problem. Leaking blanks are stripped off.
80
+ def line_at(filename, line_number) # :nodoc:
81
+ @reload_on_change=nil unless defined?(@reload_on_change)
82
+ line = LineCache::getline(filename, line_number, @reload_on_change)
83
+ return "\n" unless line
84
+ return "#{line.gsub(/^\s+/, '').chomp}\n"
85
+ end
86
+
87
+ alias stop remove_tracepoints
88
+
89
+ # @param [String] file
90
+ # @param [Fixnum] line
91
+ # @param [String] expr
92
+ def add_breakpoint(file, line, expr=nil)
93
+ breakpoint = Breakpoint.new(file, line, expr)
94
+ breakpoints << breakpoint
95
+ breakpoint
96
+ end
97
+
98
+ def remove_breakpoint(id)
99
+ Breakpoint.remove breakpoints, id
100
+ end
101
+
102
+ def interface=(value) # :nodoc:
103
+ handler.interface = value
104
+ end
105
+
106
+ #
107
+ # Byebug.start(options) -> bool
108
+ # Byebug.start(options) { ... } -> obj
109
+ #
110
+ # If it's called without a block it returns +true+, unless byebug was
111
+ # already started.
112
+ #
113
+ # If a block is given, it starts byebug and yields to block. When the
114
+ # block is finished executing it stops the byebug with Byebug.stop
115
+ # method. Inside the block you will probably want to have a call to
116
+ # Byebug.byebug. For example:
117
+ #
118
+ # Byebug.start{byebug; foo} # Stop inside of foo
119
+ #
120
+ # Also, byebug only allows one invocation of byebug at a time; nested
121
+ # Byebug.start's have no effect and you can't use this inside the byebug
122
+ # itself.
123
+ #
124
+ # <i>Note that if you want to stop byebug, you must call Byebug.stop as
125
+ # many times as you called Byebug.start method.</i>
126
+ #
127
+ # +options+ is a hash used to set various debugging options.
128
+ # :init - true if you want to save ARGV and some other variables to
129
+ # make a byebug restart possible. Only the first time :init
130
+ # is set to true the values will get set. Since ARGV is
131
+ # saved, you should make sure it hasn't been changed before
132
+ # the (first) call.
133
+ # :post_mortem - true if you want to enter post-mortem debugging on an
134
+ # uncaught exception. Once post-mortem debugging is set, it
135
+ # can't be unset.
136
+ #
137
+ def start(options={}, &block)
138
+ options = Byebug::DEFAULT_START_SETTINGS.merge(options)
139
+ if options[:init]
140
+ Byebug.const_set('ARGV', ARGV.clone) unless defined? Byebug::ARGV
141
+ Byebug.const_set('PROG_SCRIPT', $0) unless defined? Byebug::PROG_SCRIPT
142
+ Byebug.const_set('INITIAL_DIR', Dir.pwd) unless defined? Byebug::INITIAL_DIR
143
+ end
144
+ #Byebug.tracing = options[:tracing] unless options[:tracing].nil?
145
+ if Byebug.started?
146
+ retval = block && block.call(self)
147
+ else
148
+ retval = Byebug._start(&block)
149
+ end
150
+ if options[:post_mortem]
151
+ post_mortem
152
+ end
153
+ return retval
154
+ end
155
+
156
+ #
157
+ # Starts a remote byebug.
158
+ #
159
+ def start_remote(host = nil, port = PORT)
160
+ return if @thread
161
+
162
+ self.interface = nil
163
+ start
164
+
165
+ if port.kind_of?(Array)
166
+ cmd_port, ctrl_port = port
167
+ else
168
+ cmd_port, ctrl_port = port, port + 1
169
+ end
170
+
171
+ ctrl_port = start_control(host, ctrl_port)
172
+
173
+ yield if block_given?
174
+
175
+ mutex = Mutex.new
176
+ proceed = ConditionVariable.new
177
+
178
+ server = TCPServer.new(host, cmd_port)
179
+ @cmd_port = cmd_port = server.addr[1]
180
+ @thread = DebugThread.new do
181
+ while (session = server.accept)
182
+ self.interface = RemoteInterface.new(session)
183
+ if wait_connection
184
+ mutex.synchronize do
185
+ proceed.signal
186
+ end
187
+ end
188
+ end
189
+ end
190
+ if wait_connection
191
+ mutex.synchronize do
192
+ proceed.wait(mutex)
193
+ end
194
+ end
195
+ end
196
+ alias start_server start_remote
197
+
198
+ def start_control(host = nil, ctrl_port = PORT + 1) # :nodoc:
199
+ return @ctrl_port if defined?(@control_thread) && @control_thread
200
+ server = TCPServer.new(host, ctrl_port)
201
+ @ctrl_port = server.addr[1]
202
+ @control_thread = DebugThread.new do
203
+ while (session = server.accept)
204
+ interface = RemoteInterface.new(session)
205
+ processor = ControlCommandProcessor.new(interface)
206
+ processor.process_commands
207
+ end
208
+ end
209
+ @ctrl_port
210
+ end
211
+
212
+ #
213
+ # Connects to the remote byebug
214
+ #
215
+ def start_client(host = 'localhost', port = PORT)
216
+ require "socket"
217
+ interface = Byebug::LocalInterface.new
218
+ socket = TCPSocket.new(host, port)
219
+ puts "Connected."
220
+
221
+ catch(:exit) do
222
+ while (line = socket.gets)
223
+ case line
224
+ when /^PROMPT (.*)$/
225
+ input = interface.read_command($1)
226
+ throw :exit unless input
227
+ socket.puts input
228
+ when /^CONFIRM (.*)$/
229
+ input = interface.confirm($1)
230
+ throw :exit unless input
231
+ socket.puts input
232
+ else
233
+ print line
234
+ end
235
+ end
236
+ end
237
+ socket.close
238
+ end
239
+
240
+ #
241
+ # Runs normal byebug initialization scripts.
242
+ #
243
+ # Reads and executes the commands from init file (if any) in the current
244
+ # working directory. This is only done if the current directory is
245
+ # different from your home directory. Thus, you can have more than one init
246
+ # file, one generic in your home directory, and another, specific to the
247
+ # program you are debugging, in the directory where you invoke byebug.
248
+ #
249
+ def run_init_script(out = handler.interface)
250
+ cwd_script_file = File.expand_path(File.join(".", INITFILE))
251
+ run_script(cwd_script_file, out) if File.exists?(cwd_script_file)
252
+
253
+ home_script_file = File.expand_path(File.join(HOME_DIR, INITFILE))
254
+ run_script(home_script_file, out) if File.exists?(home_script_file) and
255
+ cwd_script_file != home_script_file
256
+ end
257
+
258
+ #
259
+ # Runs a script file
260
+ #
261
+ def run_script(file, out = handler.interface, verbose=false)
262
+ interface = ScriptInterface.new(File.expand_path(file), out)
263
+ processor = ControlCommandProcessor.new(interface)
264
+ processor.process_commands(verbose)
265
+ end
266
+
267
+ # XXX: Implement
268
+
269
+ # # Activates the post-mortem mode. There are two ways of using it:
270
+ # #
271
+ # # == Global post-mortem mode
272
+ # # By calling Byebug.post_mortem method without a block, you install
273
+ # # at_exit hook that intercepts any unhandled by your script exceptions
274
+ # # and enables post-mortem mode.
275
+ # #
276
+ # # == Local post-mortem mode
277
+ # #
278
+ # # If you know that a particular block of code raises an exception you can
279
+ # # enable post-mortem mode by wrapping this block with Byebug.post_mortem, e.g.
280
+ # #
281
+ # # def offender
282
+ # # raise 'error'
283
+ # # end
284
+ # # Byebug.post_mortem do
285
+ # # ...
286
+ # # offender
287
+ # # ...
288
+ # # end
289
+ # def post_mortem
290
+ # if block_given?
291
+ # old_post_mortem = self.post_mortem?
292
+ # begin
293
+ # self.post_mortem = true
294
+ # yield
295
+ # rescue Exception => exp
296
+ # handle_post_mortem(exp)
297
+ # raise
298
+ # ensure
299
+ # self.post_mortem = old_post_mortem
300
+ # end
301
+ # else
302
+ # return if post_mortem?
303
+ # self.post_mortem = true
304
+ # debug_at_exit do
305
+ # handle_post_mortem($!) if $! && post_mortem?
306
+ # end
307
+ # end
308
+ # end
309
+
310
+ # def handle_post_mortem(exp)
311
+ # return if !exp || !exp.__debug_context ||
312
+ # exp.__debug_context.stack_size == 0
313
+ # Byebug.suspend
314
+ # orig_tracing = Byebug.tracing, Byebug.current_context.tracing
315
+ # Byebug.tracing = Byebug.current_context.tracing = false
316
+ # Byebug.last_exception = exp
317
+ # handler.at_line(exp.__debug_context, exp.__debug_file, exp.__debug_line)
318
+ # ensure
319
+ # Byebug.tracing, Byebug.current_context.tracing = orig_tracing
320
+ # Byebug.resume
321
+ # end
322
+ # # private :handle_post_mortem
323
+
324
+ end
325
+
326
+ class DebugThread # :nodoc:
327
+ end
328
+
329
+ class ThreadsTable # :nodoc:
330
+ end
331
+
332
+ end
333
+
334
+ class Exception # :nodoc:
335
+ attr_reader :__debug_file, :__debug_line, :__debug_binding, :__debug_context
336
+ end
337
+
338
+ class Module
339
+ #
340
+ # Wraps the +meth+ method with Byebug.start {...} block.
341
+ #
342
+ def debug_method(meth)
343
+ old_meth = "__debugee_#{meth}"
344
+ old_meth = "#{$1}_set" if old_meth =~ /^(.+)=$/
345
+ alias_method old_meth.to_sym, meth
346
+ class_eval <<-EOD
347
+ def #{meth}(*args, &block)
348
+ Byebug.start do
349
+ byebug 2
350
+ #{old_meth}(*args, &block)
351
+ end
352
+ end
353
+ EOD
354
+ end
355
+
356
+ # XXX: Implement
357
+ # #
358
+ # # Wraps the +meth+ method with Byebug.post_mortem {...} block.
359
+ # #
360
+ # def post_mortem_method(meth)
361
+ # old_meth = "__postmortem_#{meth}"
362
+ # old_meth = "#{$1}_set" if old_meth =~ /^(.+)=$/
363
+ # alias_method old_meth.to_sym, meth
364
+ # class_eval <<-EOD
365
+ # def #{meth}(*args, &block)
366
+ # Byebug.start do |dbg|
367
+ # dbg.post_mortem do
368
+ # #{old_meth}(*args, &block)
369
+ # end
370
+ # end
371
+ # end
372
+ # EOD
373
+ # end
374
+ end
375
+ module Kernel
376
+
377
+ # Enters byebug in the current thread after _steps_ line events occur.
378
+ #
379
+ # Before entering byebug startup, the init script is read. Setting _steps_ to 0
380
+ # will cause a break in the byebug subroutine and not wait for a line event to
381
+ # occur. You will have to go "up 1" in order to be back to your debugged program
382
+ # from byebug program. Setting _steps_ to 0 could be useful if you want to stop
383
+ # right after the last statement in some scope, because the next step will take
384
+ # you out of some scope.
385
+ def byebug(steps = 1)
386
+ Byebug.start
387
+ Byebug.run_init_script(StringIO.new)
388
+ if 0 == steps
389
+ Byebug.current_context.stop_frame = 0
390
+ else
391
+ Byebug.current_context.stop_next = steps
392
+ end
393
+ end
394
+ alias breakpoint byebug unless respond_to?(:breakpoint)
395
+
396
+ #
397
+ # Returns a binding of n-th call frame
398
+ #
399
+ def binding_n(n = 0)
400
+ Byebug.skip do
401
+ Byebug.current_context.frame_binding(n+1)
402
+ end
403
+ end
404
+ end