byebug 0.0.1

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