byebug-5.0.0/0000755000076400007640000000000012560626142012031 5ustar pravipravibyebug-5.0.0/LICENSE0000644000076400007640000000243512560626142013042 0ustar pravipraviCopyright (c) David Rodríguez All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. byebug-5.0.0/GUIDE.md0000644000076400007640000016720412560626142013222 0ustar pravipravi### First Steps A handful of commands are enough to get started using `byebug`. The following session illustrates these commands. Take the following sample file: ```ruby # # The n'th triangle number: triangle(n) = n*(n+1)/2 = 1 + 2 + ... + n # def triangle(n) tri = 0 0.upto(n) { |i| tri += i } tri end t = triangle(3) puts t ``` Let's debug it. ```bash $ byebug /path/to/triangle.rb [1, 10] in /path/to/triangle.rb 1: # 2: # The n'th triangle number: triangle(n) = n*(n+1)/2 = 1 + 2 + ... + n 3: # => 4: def triangle(n) 5: tri = 0 6: 7: 0.upto(n) { |i| tri += i } 8: 9: tri 10: end (byebug) ``` We are currently stopped before the first executable line of the program: line 4 of `triangle.rb`. If you are used to less dynamic languages and have used debuggers for more statically compiled languages like C, C++, or Java, it may seem odd to be stopped before a function definition but in Ruby line 4 is executed. Byebug's prompt is `(byebug)`. If the program has died and you are in post-mortem debugging, `(byebug:post-mortem)` is used instead. If the program has terminated normally and the `--no-quit` option has been specified in the command line, the prompt will be `(byebug:ctrl)` instead. The commands available change depending on the program's state. Byebug automatically lists 10 lines of code centered around the current line every time it is stopped. The current line is marked with `=>`. If the range would overflow the beggining or the end of the file, byebug will move it accordingly so that only actual real lines of code are displayed. Now let us step through the program. ```bash (byebug) step [5, 14] in /path/to/triangle.rb 5: tri = 0 6: 7: 0.upto(n) { |i| tri += i } 9: end 10: 11: tri 12: end 13: => 14: triangle(3) (byebug) # hit enter [1, 10] in /path/to/triangle.rb 1: # 2: # The n'th triangle number: triangle(n) = n*(n+1)/2 = 1 + 2 + ... + n 3: # 4: def triangle(n) => 5: tri = 0 6: 7: 0.upto(n) { |i| tri += i } 8: 9: tri 10: end (byebug) p tri nil (byebug) step [2, 11] in /path/to/triangle.rb 2: # The n'th triangle number: triangle(n) = n*(n+1)/2 = 1 + 2 + ... + n 3: # 4: def triangle(n) 5: tri = 0 6: => 7: 0.upto(n) { |i| tri += i } 8: 9: tri 10: end 11: (byebug) p tri 0 ``` The first `step` command runs the script one executable unit. The second command we entered was just hitting the return key: `byebug` remembers the last command you entered was `step` and runs it again. One way to print the values of variables is `p` (there are other ways). When we look at the value of `tri` the first time, we see it is `nil`. Again we are stopped _before_ the assignment on line 5, and this variable hadn't been set previously. However after issuing another `step` command we see that the value is 0 as expected. If every time we stop we want to see the value of `tri` to see how things are going, there is a better way by setting a display expression: ```bash (byebug) display tri 1: tri = 0 ``` Now let us run the program until right before we return from the function. We'll want to see which lines get run, so we turn on _line tracing_. If we don't want whole paths to be displayed when tracing, we can turn on _basename_. ```bash (byebug) set linetrace linetrace is on (byebug) set basename basename is on (byebug) finish 0 Tracing: triangle.rb:7 0.upto(n) { |i| tri += i } 1: tri = 0 Tracing: triangle.rb:7 0.upto(n) { |i| tri += i } 1: tri = 0 Tracing: triangle.rb:7 0.upto(n) { |i| tri += i } 1: tri = 1 Tracing: triangle.rb:7 0.upto(n) { |i| tri += i } 1: tri = 3 Tracing: triangle.rb:9 tri 1: tri = 6 1: tri = 6 [4, 13] in /home/davidr/Proyectos/byebug/triangle.rb 4: def triangle(n) 5: tri = 0 6: 7: 0.upto(n) { |i| tri += i } 8: 9: tri => 10: end 11: 12: t = triangle(3) 13: puts t (byebug) quit Really quit? (y/n) y ``` So far, so good. As you can see from the above to get out of `byebug`, one can issue a `quit` command (`q` and `exit` are just as good). If you want to quit without being prompted, suffix the command with an exclamation mark, e.g., `q!`. ### Second Sample Session: Delving Deeper In this section we'll introduce breakpoints, the call stack and restarting. Below we will debug a simple Ruby program to solve the classic Towers of Hanoi puzzle. It is augmented by the bane of programming: some command-parameter processing with error checking. ```ruby # # Solves the classic Towers of Hanoi puzzle. # def hanoi(n, a, b, c) hanoi(n - 1, a, c, b) if n - 1 > 0 puts "Move disk #{a} to #{b}" hanoi(n - 1, c, b, a) if n - 1 > 0 end n_args = $ARGV.length fail('*** Need number of disks or no parameter') if n_args > 1 ``` Recall in the first section it was stated that before the `def` is run, the method it names is undefined. Let's check that out. First let's see what private methods we can call before running `def hanoi`. ```bash $ byebug path/to/hanoi.rb 1: # 2: # Solves the classic Towers of Hanoi puzzle. 3: # 4: def hanoi(n, a, b, c) 5: hanoi(n - 1, a, c, b) if n - 1 > 0 6: 7: puts "Move disk #{a} to #{b}" 8: 9: hanoi(n - 1, c, b, a) if n - 1 > 0 10: end (byebug) private_methods [:public, :private, :include, :using, :define_method, :default_src_encoding, ... ``` `private_methods` is not a byebug command but a Ruby feature. By default, when `byebug` doesn't understand a command, it will evaluate it as if it was a Ruby command. If you don't want this behaviour, you can use `set noautoeval` or even drop it in your `.byebugrc` file if you want that behaviour permanently. The output of `private_methods`, thought, is unwieldy for our purpose: check whether `hanoi` method is in the list. Fortunately, byebug has nice formatting features: we can sort the output and put it into columns list using the print command `ps`. It also has a `width` setting that let's us adapt the width of the output so that it nicely fits our screen. ```bash (byebug) set width 80 Maximum width of byebug's output is 80 (byebug) ps private_methods Array default_src_encoding open sleep Complex define_method p spawn Digest eval pp sprintf Float exec print srand Hash exit printf syscall Integer exit! private system Pathname fail proc test Rational fork public throw String format putc timeout URI gem_original_require puts trace_var __callee__ gets raise trap __dir__ global_variables rand untrace_var __method__ include readline using ` initialize readlines warn abort initialize_clone require y at_exit initialize_copy require_relative autoload initialize_dup respond_to_missing? autoload? iterator? rubygems_require binding lambda select block_given? load set_trace_func caller local_variables singleton_method_added caller_locations loop singleton_method_removed catch method_missing singleton_method_undefined (byebug) ``` Now let's see what happens after stepping: ```bash (byebug) private_methods.member?(:hanoi) false (byebug) step [5, 14] in /path/to/hanoi.rb 5: hanoi(n - 1, a, c, b) if n - 1 > 0 6: 7: puts "Move disk #{a} to #{b}" 8: 9: hanoi(n - 1, c, b, a) if n - 1 > 0 10: end 11: => 12: n_args = $ARGV.length 13: 14: fail('*** Need number of disks or no parameter') if n_args > 1 (byebug) private_methods.member?(:hanoi) true (byebug) ``` Okay, lets go on and talk about program arguments. ```bash (byebug) $ARGV [] ``` Oops. We forgot to specify any parameters to this program. Let's try again. We can use the `restart` command here. ```bash (byebug) restart 3 Re exec'ing: /path/to/bin/byebug /path/to/hanoi.rb 3 [1, 10] in /path/to/hanoi.rb 1: # 2: # Solves the classic Towers of Hanoi puzzle. 3: # => 4: def hanoi(n, a, b, c) 5: hanoi(n - 1, a, c, b) if n - 1 > 0 6: 7: puts "Move disk #{a} to #{b}" 8: 9: hanoi(n - 1, c, b, a) if n - 1 > 0 10: end (byebug) break 5 Created breakpoint 1 at /path/to/hanoi.rb:5 (byebug) continue Stopped by breakpoint 1 at /path/to/hanoi.rb:5 [1, 10] in /path/to/hanoi.rb 1: # 2: # Solves the classic Towers of Hanoi puzzle. 3: # 4: def hanoi(n, a, b, c) => 5: hanoi(n - 1, a, c, b) if n - 1 > 0 6: 7: puts "Move disk #{a} to #{b}" 8: 9: hanoi(n - 1, c, b, a) if n - 1 > 0 10: end (byebug) display n 1: n = 3 (byebug) display a 2: a = :a (byebug) display b 3: b = :b (byebug) undisplay 3 (byebug) continue Stopped by breakpoint 1 at /path/to/hanoi.rb:5 1: n = 2 2: a = :a [1, 10] in /path/to/hanoi.rb 1: # 2: # Solves the classic Towers of Hanoi puzzle. 3: # 4: def hanoi(n, a, b, c) => 5: hanoi(n - 1, a, c, b) if n - 1 > 0 6: 7: puts "Move disk #{a} to #{b}" 8: 9: hanoi(n - 1, c, b, a) if n - 1 > 0 10: end (byebug) c Stopped by breakpoint 1 at /path/to/hanoi.rb:5 1: n = 1 2: a = :a [1, 10] in /path/to/hanoi.rb 1: # 2: # Solves the classic Towers of Hanoi puzzle. 3: # 4: def hanoi(n, a, b, c) => 5: hanoi(n - 1, a, c, b) if n - 1 > 0 6: 7: puts "Move disk #{a} to #{b}" 8: 9: hanoi(n - 1, c, b, a) if n - 1 > 0 10: end (byebug) set nofullpath fullpath is off (byebug) where --> #0 Object.hanoi(n#Fixnum, a#Symbol, b#Symbol, c#Symbol) at .../shortpath/to/hanoi.rb:5 #1 Object.hanoi(n#Fixnum, a#Symbol, b#Symbol, c#Symbol) at .../shortpath/to/hanoi.rb:5 #2 at .../Proyectos/byebug/hanoi.rb:28 (byebug) ``` In the above we added new commands: `break` (see [breakpoints]()), which indicates to stop just before that line of code is run, and `continue`, which resumes execution. To remove a display expression `undisplay` is used. If we give a display number, just that display expression is removed. We also used a new command `where`(see [backtrace]()) to show the callstack. In the above situation, starting from the bottom line we see we called the `hanoi` method from line 28 of the file `hanoi.rb` and the `hanoi` method called itself two more times at line 5. In the callstack we show a _current frame_ mark, the frame number, the method being called, the names of the parameters, the types those parameters _currently_ have and the file-line position. Remember it's possible that when the program was called the parameters had different types, since the types of variables can change dynamically. You can alter the style of what to show in the trace (see [callstyle]()). Now let's move around the callstack. ```bash (byebug) undisplay Clear all expressions? (y/n) y (byebug) n_args NameError Exception: undefined local variable or method `n_args' for main:Object (byebug) frame 2 [19, 28] in /path/to/hanoi.rb 19: begin 20: n = $ARGV[0].to_i 21: rescue ValueError 22: raise("** Expecting an integer, got: #{$ARGV[0]}") 23: end 24: end 25: 26: fail('*** Number of disks should be between 1 and 100') if n < 1 || n > 100 27: => 28: hanoi(n, :a, :b, :c) (byebug) n_args 0 (byebug) p n 3 (byebug) down 2 [1, 10] in /path/to/hanoi.rb 1: # 2: # Solves the classic Towers of Hanoi puzzle. 3: # 4: def hanoi(n, a, b, c) => 5: hanoi(n - 1, a, c, b) if n - 1 > 0 6: 7: puts "Move disk #{a} to #{b}" 8: 9: hanoi(n - 1, c, b, a) if n - 1 > 0 10: end (byebug) p n 2 ``` Notice in the above to get the value of variable `n` we had to use a print command like `p n`. If we entered just `n`, that would be taken to mean byebug command `next`. In the current scope, variable `n_args` is not defined. However I can change to the top-most frame by using the `frame 2` command. Notice that inside frame #2, the value of `n_args` can be shown. Also note that the value of variable `n` is different. ### Attaching to a running program with `byebug` In the previous sessions we've been calling byebug right at the outset, but there is another mode of operation you might use. If there's a lot of code that needs to be run before the part you want to inspect, it might not be efficient or convenient to run byebug from the outset. In this section we'll show how to enter the code in the middle of your program, while delving more into byebug's operation. We will also use unit testing. Using unit tests will greatly reduce the amount of debugging needed, while at the same time, will increase the quality of your program. What we'll do is take the `triangle` code from the first session and write a unit test for that. In a sense we did write a tiny test for the program which was basically the last line where we printed the value of `triangle(3)`. This test however wasn't automated: the expectation is that someone would look at the output and verify that what was printed is what was expected. Before we can turn that into something that can be `required`, we probably want to remove that output. However I like to keep in that line so that when I look at the file, I have an example of how to run it. Therefore we will conditionally run this line if that file is invoked directly, but skip it if it is not. _NOTE: `byebug` resets `$0` to try to make things like this work._ ```ruby if __FILE__ == $PROGRAM_NAME t = triangle(3) puts t end ``` Okay, we're now ready to write our unit test and we'll use the `minitest` framework for that. Here's the test code, it should be placed in the same directory as `triangle.rb`. ```ruby require 'minitest/autorun' require_relative 'triangle.rb' class TestTriangle < Minitest::Test def test_basic solutions = [] 0.upto(5) { |i| solutions << triangle(i) } assert_equal([0, 1, 3, 6, 10, 15], solutions, 'First 5 triangle numbers') end end ``` Let's say we want to stop before the first statement in our test method, we'll add the following: ```ruby ... def test_basic byebug solutions = [] ... ``` Now we run the program, requiring `byebug` ```bash $ ruby -rbyebug test_triangle.rb Run options: --seed 31679 # Running: [2, 11] in test_triangle.rb 2: require_relative 'triangle.rb' 3: 4: class TestTriangle < Minitest::Test 5: def test_basic 6: byebug => 7: solutions = [] 8: 9: 0.upto(5) { |i| solutions << triangle(i) } 10: 11: assert_equal([0, 1, 3, 6, 10, 15], solutions, 'First 5 triangle numbers') (byebug) ``` and we see that we are stopped at line 7 just before the initialization of the list `solutions`. Now let's see where we are... ```bash (byebug) set nofullpath Displaying frame's full file names is off. (byebug) bt --> #0 TestTriangle.test_basic at .../Proyectos/byebug/test_triangle.rb:7 #1 block (3 levels) in Minitest::Test.run at .../lib/minitest/test.rb:108 #2 Minitest::Test.capture_exceptions at .../lib/minitest/test.rb:206 #3 block (2 levels) in Minitest::Test.run at .../lib/minitest/test.rb:105 #4 Minitest::Test.time_it at .../lib/minitest/test.rb:258 #5 block in Minitest::Test.run at .../lib/minitest/test.rb:104 #6 #.on_signal(name#String, action#Proc) at .../minitest-5.5.0/lib/minitest.rb:321 #7 Minitest::Test.with_info_handler(&block#Proc) at .../lib/minitest/test.rb:278 #8 Minitest::Test.run at .../lib/minitest/test.rb:103 #9 #.run_one_method(klass#Class, method_name#String) at .../minitest-5.5.0/lib/minitest.rb:768 #10 #.run_one_method(klass#Class, method_name#String, reporter#Minitest::CompositeReporter) at .../minitest-5.5.0/lib/minitest.rb:295 #11 block (2 levels) in #.run(reporter#Minitest::CompositeReporter, options#Hash) at .../minitest-5.5.0/lib/minitest.rb:289 ͱ-- #12 Array.each at .../minitest-5.5.0/lib/minitest.rb:288 #13 block in #.run(reporter#Minitest::CompositeReporter, options#Hash) at .../minitest-5.5.0/lib/minitest.rb:288 #14 #.on_signal(name#String, action#Proc) at .../minitest-5.5.0/lib/minitest.rb:321 #15 #.with_info_handler(reporter#Minitest::CompositeReporter, &block#Proc) at .../minitest-5.5.0/lib/minitest.rb:308 #16 #.run(reporter#Minitest::CompositeReporter, options#Hash) at .../minitest-5.5.0/lib/minitest.rb:287 #17 block in #.__run(reporter#Minitest::CompositeReporter, options#Hash) at .../minitest-5.5.0/lib/minitest.rb:150 ͱ-- #18 Array.map at .../minitest-5.5.0/lib/minitest.rb:150 #19 #.__run(reporter#Minitest::CompositeReporter, options#Hash) at .../minitest-5.5.0/lib/minitest.rb:150 #20 #.run(args#Array) at .../minitest-5.5.0/lib/minitest.rb:127 #21 block in #.autorun at .../minitest-5.5.0/lib/minitest.rb:56 (byebug) ``` We get the same result as if we had run byebug from the outset. ### Debugging Oddities: How debugging Ruby may be different from other languages If you are used to debugging in other languages like C, C++, Perl, Java or even Bash (see [bashdb](http://bashdb.sf.net)), there may be a number of things that seem or feel a little bit different and may confuse you. A number of these things aren't oddities of the debugger per se but differences in how Ruby works compared to those other languages. Because Ruby works a little differently from those other languages, writing a debugger has to also be a little different as well if it is to be useful. In this respect, using Byebug may help you understand Ruby better. We've already seen one such difference: the fact that we stop on method definitions or `def`'s and that is because these are in fact executable statements. In other compiled languages this would not happen because that's already been done when you compile the program (or in Perl when it scans in the program). In this section we'll consider some other things that might throw off new users to Ruby who are familiar with other languages and debugging in them. * Bouncing Around in Blocks (iterators) * No Parameter Values in a Call Stack * Lines You Can Stop At #### Bouncing Around in Blocks (iterators) When debugging languages with coroutines like Python and Ruby, a method call may not necessarily go to the first statement after the method header. It's possible that the call will continue after a `yield` statement from a prior call. ```ruby # # Enumerator for primes # class SievePrime def initialize @odd_primes = [] end def next_prime candidate = 2 yield candidate not_prime = false candidate += 1 loop do @odd_primes.each do |p| not_prime = (0 == (candidate % p)) break if not_prime end unless not_prime @odd_primes << candidate yield candidate end candidate += 2 end end end SievePrime.new.next_prime do |prime| puts prime break if prime > 10 end ``` ```bash $ byebug primes.rb [1, 10] in /path/to/primes.rb 1: # 2: # Enumerator for primes 3: # => 4: class SievePrime 5: def initialize 6: @odd_primes = [] 7: end 8: 9: def self.next_prime(&block) 10: candidate = 2 (byebug) set tracing line tracing is on. (byebug) set basename basename in on. (byebug) step 9 Tracing: primes.rb:5 def initialize Tracing: primes.rb:9 def next_prime Tracing: primes.rb:31 SievePrime.new.next_prime do |prime| Tracing: primes.rb:6 @odd_primes = [] Tracing: primes.rb:10 candidate = 2 Tracing: primes.rb:11 yield candidate Tracing: primes.rb:32 puts prime 2 Tracing: primes.rb:33 break if prime > 10 Tracing: primes.rb:12 not_prime = false [7, 16] in /path/to/primes.rb 7: end 8: 9: def next_prime 10: candidate = 2 11: yield candidate => 12: not_prime = false 13: candidate += 1 14: 15: loop do 16: @odd_primes.each do |p| 17: not_prime = (0 == (candidate % p)) (byebug) ``` The loop between lines 31-34 gets interleaved between those of `SievePrime#next_prime`, lines 9-28 above. #### No Parameter Values in a Call Stack In traditional debuggers, in a call stack you can generally see the names of the parameters and the values that were passed in. Ruby is a very dynamic language and it tries to be efficient within the confines of the language definition. Values generally aren't taken out of a variable or expression and pushed onto a stack. Instead a new scope is created and the parameters are given initial values. Parameter passing is by _reference_ not by _value_ as it is say Algol, C, or Perl. During the execution of a method, parameter values can change (and often do). In fact even the _class_ of the object can change. So at present, the name of the parameter is shown. The call-style setting ([callstyle]()) can be used to set whether the name is shown or the name and the _current_ class of the object. #### Lines You Can Stop At Consider the following little Ruby program. ```ruby 'Yes it does' =~ / (Yes) \s+ it \s+ does /ix puts $1 ``` The stopping points that Ruby records are the last two lines, lines 5 and 6. Inside `byebug` you can get a list of stoppable lines for a file using the `info file` command. ### Threading support Byebug supports debugging Ruby programs making use of multiple threads. Let's consider the following sample program: ```ruby class Company def initialize(task) @tasks, @results = Queue.new, Queue.new @tasks.push(task) end def run manager = Thread.new { manager_routine } employee = Thread.new { employee_routine } sleep 6 go_home(manager) go_home(employee) end # # An employee doing his thing # def employee_routine loop do if @tasks.empty? have_a_break(0.1) else work_hard(@tasks.pop) end end end # # A manager doing his thing # def manager_routine loop do if @results.empty? have_a_break(1) else show_off(@results.pop) end end end private def show_off(result) puts result end def work_hard(task) task ** task end def have_a_break(amount) sleep amount end def go_home(person) person.kill end end Company.new(10).run ``` The `Company` class simulates a real company. The company has a manager and an employee represented by 2 threads: they work concurrently to achieve the company's targets. * The employee looks for tasks to complete. If there are tasks, it works hard to complete them. Otherwise he has a quick break. ```ruby # # An employee doing his thing # def employee_routine loop do if @tasks.empty? have_a_break(0.1) else work_hard(@tasks.pop) end end end ``` * The manager, on the other hand, sits there all day and sporadically checks whether there are any results to show off. ```ruby # # A manager doing his thing # def manager_routine loop do if @results.empty? have_a_break(1) else show_off(@results.pop) end end end ``` We do some abstractions easily readable in the code. Our tasks are just a `Queue` of numbers, so are our results. What our employer does when he works is some calculation with those numbers and what the manager does with the results is printing them to the screen. We instantiate a new company with an initial task and after running that company we expect the result to be printed in the screen, but it is not. Lets debug our sample program: ```bash [1, 10] in /path/to/company.rb => 1: class Company 2: def initialize(task) 3: @tasks, @results = Queue.new, Queue.new 4: 5: @tasks.push(task) 6: end 7: 8: def run 9: manager = Thread.new { manager_routine } 10: employee = Thread.new { employee_routine } (byebug) l [11, 20] in /path/to/company.rb 11: 12: sleep 6 13: 14: go_home(manager) 15: go_home(employee) 16: end 17: 18: # 19: # An employee doing his thing 20: # (byebug) c 12 Stopped by breakpoint 1 at /path/to/company.rb:12 [7, 16] in /path/to/company.rb 7: 8: def run 9: manager = Thread.new { manager_routine } 10: employee = Thread.new { employee_routine } 11: => 12: sleep 6 13: 14: go_home(manager) 15: go_home(employee) 16: end (byebug) th l + 1 # /path/to/company.rb:12 2 # 3 # ``` What we have done here is just start our program and advance to the point inmediately after our `employee` and `manager` threads have been created. We can then check that the threads are there using the `thread list` command. Now we want to debug both of this threads to check what's happening and look for the bug. ```bash (byebug) th switch 3 [5, 14] in /path/to/company.rb 5: @tasks.push(task) 6: end 7: 8: def run 9: manager = Thread.new { manager_routine } => 10: employee = Thread.new { employee_routine } 11: 12: sleep 6 13: 14: go_home(manager) (byebug) th stop 1; th stop 2 $ 1 # /path/to/company.rb:12 $ 2 # /path/to/company.rb:9 (byebug) th l $ 1 # /path/to/company.rb:12 $ 2 # /path/to/company.rb:55 + 3 # /path/to/company.rb:10 ``` We have started by debugging the `employee` thread. To do that, we switch to that thread using the `thread switch 3` command. The thread number is the one specified by `thread list`, we know this is our worker thread because `thread list` specifies where the thread is defined in the file (and its current position if the thread is currently running, although this is only available since Ruby 2.2.1). After that we stopped the main thread and the worker thread, using the command `thread stop`. We do this because we want to focus on the employee thread first and don't want the program to finish while we are debugging. Notice that stopped threads are marked with the "$" symbol whereas the current thread is marked with the "+" symbol. ```bash (byebug) s [17, 26] in /path/to/company.rb 17: 18: # 19: # An employee doing his thing 20: # 21: def employee_routine => 22: loop do 23: if @tasks.empty? 24: have_a_break(0.1) 25: else 26: work_hard(@tasks.pop) (byebug) s [18, 27] in /path/to/company.rb 18: # 19: # An employee doing his thing 20: # 21: def employee_routine 22: loop do => 23: if @tasks.empty? 24: have_a_break(0.1) 25: else 26: work_hard(@tasks.pop) 27: end (byebug) n [21, 30] in /path/to/company.rb 21: def employee_routine 22: loop do 23: if @tasks.empty? 24: have_a_break(0.1) 25: else => 26: work_hard(@tasks.pop) 27: end 28: end 29: end 30: (byebug) s [49, 58] in /path/to/company.rb 49: def show_off(result) 50: puts result 51: end 52: 53: def work_hard(task) => 54: task ** task 55: end 56: 57: def have_a_break(amount) 58: sleep amount (byebug) s [21, 30] in /path/to/company.rb 21: # 22: # An employee doing his thing 23: # 24: def employee_routine 25: loop do => 26: if @tasks.empty? 27: have_a_break(0.1) 28: else 29: work_hard(@tasks.pop) 30: end (byebug) n [22, 31] in /path/to/company.rb 22: # An employee doing his thing 23: # 24: def employee_routine 25: loop do 26: if @tasks.empty? => 27: have_a_break(0.1) 28: else 29: work_hard(@tasks.pop) 30: end 31: end (byebug) n [21, 30] in /path/to/company.rb 21: # 22: # An employee doing his thing 23: # 24: def employee_routine 25: loop do => 26: if @tasks.empty? 27: have_a_break(0.1) 28: else 29: work_hard(@tasks.pop) 30: end 31: end (byebug) ``` Everything seems fine in this thread. The first iteration the employee will do his job, and after that it will just check for new tasks and sleep. Let's debug the manager task now: ```bash (byebug) th resume 2 2 # /path/to/company.rb:12 (byebug) th switch 2 2 # /path/to/company.rb:12 [7, 16] in /path/to/company.rb 7: 8: # 9: # A CEO running his company 10: # 11: def run => 12: manager = Thread.new { manager_routine } 13: employee = Thread.new { employee_routine } 14: 15: sleep 6 16: (byebug) ``` We used the command `thread resume` to restart the manager's thread and then switch to it using `thread switch`. It's important to resume the thread's execution before switching to it, otherwise we'll get a hang because we cannot run a sleeping thread. Now we can investigate the problem in the employer's side: ```bash (byebug) s [30, 39] in /path/to/company.rb 30: 31: # 32: # A manager doing his thing 33: # 34: def manager_routine => 35: loop do 36: if @results.empty? 37: have_a_break(1) 38: else 39: show_off(@results.pop) (byebug) s [31, 40] in /path/to/company.rb 31: # 32: # A manager doing his thing 33: # 34: def manager_routine 35: loop do => 36: if @results.empty? 37: have_a_break(1) 38: else 39: show_off(@results.pop) 40: end (byebug) n [32, 41] in /path/to/company.rb 32: # A manager doing his thing 33: # 34: def manager_routine 35: loop do 36: if @results.empty? => 37: have_a_break(1) 38: else 39: show_off(@results.pop) 40: end 41: end (byebug) n [31, 40] in /path/to/company.rb 31: # 32: # A manager doing his thing 33: # 34: def manager_routine 35: loop do => 36: if @results.empty? 37: have_a_break(1) 38: else 39: show_off(@results.pop) 40: end (byebug) ``` Now we can see the problem, the `@results` variable is always empty! The employee forgot to leave the results in his manager's deck. We fix it by changing the line ```ruby work_hard(@tasks.pop) ``` in the `employee_routine` method with the line ```ruby @results << work_hard(@tasks.pop) ``` To be continued... * More complex examples with objects, pretty printing and irb. * Line tracing and non-interactive tracing. * Post-mortem debugging. ## Getting in & out ### Starting byebug There is a wrapper script called `byebug` which basically `require`'s the gem then loads `byebug` before its argument (the program to be debugged) is started. If you don't need to pass dash options to your program, which might be confused with byebug options, then you don't need to add the `--`. To get a brief list of options and descriptions, use the `--help` option. ```bash $ byebug --help byebug 3.5.1 Usage: byebug [options] -- -d, --debug Set $DEBUG=true -I, --include list Add to paths to $LOAD_PATH -m, --[no-]post-mortem Use post-mortem mode -q, --[no-]quit Quit when script finishes -x, --[no-]rc Run byebug initialization file -s, --[no-]stop Stop when script is loaded -r, --require file Require library before script -R, --remote [host:]port Remote debug [host:]port -t, --[no-]trace Turn on line tracing -v, --version Print program version -h, --help Display this message ``` Many options appear as a long option name, such as `--help` and a short one letter option name, such as `-h`. The list of options is detailed below: #### -h | --help It causes `byebug` to print some basic help and exit #### -v | --version It causes `byebug` to print its version number and exit. #### -d | --debug Sets `$DEBUG` to `true`. Compatible with Ruby's flag. #### -I | --include Adds `path` to load path. `path` can be a single path or a colon separated path list. #### -m | --post-mortem If your program raises an exception that isn't caught you can enter byebug for inspection of what went wrong. You may also want to use this option in conjunction with `--no-stop`. See also [Post-Mortem Debugging](). #### --no-quit Keep inside `byebug` after your program terminates normally. #### --no-stop Normally `byebug` stops before executing the first statement. If instead you want it to start running initially and perhaps break it later in the execution, use this option. #### -r | --require Requires the library before executing the script. This option is compatible with Ruby's. #### -t | --trace Turns on line tracing. Running `byebug --trace .rb` is pretty much like running `ruby -rtracer .rb`. If all you want to do however is get a line trace, `tracer` is most likely faster than `byebug`. ```bash $ time byebug --trace --no-stop hanoi.rb > /dev/null real 0m0.743s user 0m0.668s sys 0m0.068s $ time ruby -rtracer hanoi.rb > /dev/null real 0m0.077s user 0m0.072s sys 0m0.004s ``` ### Byebug default options Byebug has many command-line options,; it seems that some people want to set them differently from the defaults. For example, some people may want `--no-quit` to be the default behavior. One could write a wrapper script or set a shell alias to handle this. ### Command Files A command file is a file of lines that are `byebug` commands. Comments (lines starting with `#`) may also be included. An empty line in a command file does nothing; it does not mean to repeat the last command, as it would from the terminal. When you start `byebug`, it automatically executes commands from its _init file_, called `.byebugrc`. During startup, `byebug` does the following: * __Processes command line options and operands.__ Reads the init file in your current directory, if any, and then checks your home directory. The home directory is the directory named in the `$HOME` or `$HOMEPATH` environment variable. Thus, you can have more than one init file, one generic in your home directory, and another, specific to the program you are debugging, in the directory where you invoke `byebug`. You can also request the execution of a command file with the `source` command (see [Source]()). ### Quitting byebug To exit `byebug`, use the `quit` command (abbreviated `q` and aliased `exit`). Normally if you are in an interactive session, this command will prompt to ask if you really want to quit. If you don't want any questions asked, enter `quit unconditionally` (abbreviated `q!`). Another way to terminate byebug is to use the `kill` command. This does the more forceful `kill -9`. It can be used in cases where `quit` doesn't work (I haven't seen those yet). ### Calling byebug from inside your program Running a program from byebug adds a bit of overhead and slows it down a little. Furthermore, by necessity, debuggers change the operation of the program they are debugging. And this can lead to unexpected and unwanted differences. It has happened so often that the term [Heisenbugs](http://en.wikipedia.org/wiki/Heisenbug) was coined to describe the situation where using a debugger (among other possibilities) changes the behavior of the program so that the bug doesn't manifest itself anymore. There is another way to get into byebug which adds no overhead or slowdown until you reach the point at which you want to start debugging. However here you must change the script and make an explicit call to byebug. Because byebug isn't involved before the first call, there is no overhead and the script will run at the same speed as if there were no byebug. To enter byebug this way, just drop `byebug` in whichever line you want to start debugging at. You also have to require byebug somehow. If using bundler, it will take care of that for you, otherwise you can use the ruby `-r` flag or add `require 'byebug'` in the line previous to the `byebug` call. If speed is crucial, you may want to start and stop this around certain sections of code, using `Byebug.start` and `Byebug.stop`. Alternatively, instead of issuing an explicit `Byebug.stop` you can add a block to the `Byebug.start` and debugging is turned on for that block. If the block of code raises an uncaught exception that would cause the block to terminate, the `stop` will occur. See [Byebug.start with a block](). When `byebug`is run, `.byebugrc` is read. You may want to enter byebug at several points in the program where there is a problem you want to investigate. And since `byebug` is just a method call it's possible to enclose it in a conditional expression, for example ```ruby byebug if 'bar' == foo and 20 == iter_count ``` ### Restarting Byebug You can restart the program using `restart [program args]`. This is a re-exec - all byebug state is lost. If command arguments are passed, those are used. Otherwise program arguments from the last invocation are used. You won't be able to restart your program in all cases. First, the program should have been invoked at the outset rather than having been called from inside your program or invoked as a result of post-mortem handling. Also, since this relies on the OS `exec` call, this command is available only if your OS supports `exec`. ## Debugging remote programs It is possible to set up debugging so that you can issue byebug commands from outside the process running the Ruby code. In fact, you might even be on a different computer than the one running the Ruby program. To setup remote debugging, drop the following somewhere before the point in the program that you want to debug (In Rails, the `config/environments/development.rb` could be a good candidate). ```ruby require 'byebug' Byebug.wait_connection = true Byebug.start_server('localhost', ) ``` Once this piece gets executed, you can connect to the remote debugger from your local machine, by running: `byebug -R localhost:`. Next, at a place of program execution which gets run just before the code you want to debug, add a call to `byebug` as was done without remote execution: ```ruby # work, work, work... byebug some ruby code # byebug will stop before this line is run ``` ## Byebug Command Reference ### Command Syntax Usually a command is put on a single line. There is no limit on how long it can be. It starts with a command name, which is followed by arguments whose meaning depends on the command name. For example, the command `step` accepts an argument which is the number of times to step, as in `step 5`. You can also use the `step` command with no arguments. Some commands do not allow any arguments. Multiple commands can be put on a line by separating each with a semicolon `;`. You can disable the meaning of a semicolon to separate commands by escaping it with a backslash. For example, if you have [autoeval]() set, which is the default, you might want to enter the following code to compute the 5th Fibonacci number. ```bash (byebug) fib1=0; fib2=1; 5.times {|temp| temp=fib1; fib1=fib2; fib2 += temp } 0 1 SyntaxError Exception: /home/davidr/Proyectos/sample_app/trace.rb:1: syntax error, unexpected end-of-input, expecting '}' 5.times { |temp| temp=fib1 ^ nil 1 SyntaxError Exception: /home/davidr/Proyectos/sample_app/trace.rb:1: syntax error, unexpected tSTRING_DEND, expecting end-of-input fib2 += temp } ^ nil (byebug) fib1=0\; fib2=1\; 5.times {|temp| temp=fib1\; fib1=fib2\; fib2 += temp } 5 (byebug) fib2 8 ``` You might also consider using the [irb]() or [pry]() commands and then you won't have to escape semicolons. A blank line as input (typing just ``) means to repeat the previous command. Byebug uses readline, which handles line editing and retrieval of previous commands. Up arrow, for example, moves to the previous byebug command; down arrow moves to the next more recent command (provided you are not already at the last command). Command history is saved in file `.byebug_hist`. A limit is put on the history size. You can see this with the `show history size` command. See [history]() for history parameters. ### Command Output In the command-line interface, when `byebug` is waiting for input it presents a prompt of the form `(byebug)`. If the program has terminated normally the prompt will be `(byebug:ctrl)` and in post-mortem debugging it will be `(byebug:post-mortem)`. Whenever `byebug` gives an error message such as for an invalid command or an invalid location position, it will generally preface the message with `***`. ### Command Help Once inside `byebug` you can always ask it for information on its commands using the `help` command. You can use `help` (abbreviated `h`) with no arguments to display a short list of named classes of commands ```bash (byebug) help Type "help " for help on a specific command Available commands: backtrace delete enable help method ps save step where break disable eval info next putl set trace catch display exit irb p quit show undisplay condition down finish kill pp skip up continue edit frame list pry restart source var ``` With a command name as `help` argument, `byebug` displays short information on how to use that command. ```bash (byebug) help list l[ist] list forward l[ist] - list backward l[ist] = list current line l[ist] nn-mm list given lines * NOTE - to turn on autolist, use 'set autolist' (byebug) ``` A number of commands, namely `info`, `set`, `show`, `enable` and `disable`, have many sub-parameters or _subcommands_. When you ask for help for one of these commands, you will get help for all of the subcommands that command offers. Sometimes you may want help only on a subcommand and to do this just follow the command with its subcommand name. For example, `help info breakpoints`will just give help about the `info breakpoints` command. Furthermore it will give longer help than the summary information that appears when you ask for help. You don't need to list the full subcommand name, just enough of the letters to make that subcommand distinct from others will do. For example, `help info b` is the same as `help info breakpoints`. Some examples follow. ```bash (byebug) help info info[ subcommand] Generic command for showing things about the program being debugged. -- List of "info" subcommands: -- info args -- Argument variables of current stack frame info breakpoints -- Status of user-settable breakpoints info catch -- Exceptions that can be caught in the current stack frame info display -- Expressions to display when program stops info file -- Info about a particular file read in info files -- File names and timestamps of files read in info line -- Line number and filename of current position in source file info program -- Execution status of the program ``` ```bash (byebug) help info breakpoints Status of user-settable breakpoints. Without argument, list info about all breakpoints. With an integer argument, list info on that breakpoint. ``` ```bash (byebug) help info b Status of user-settable breakpoints. Without argument, list info about all breakpoints. With an integer argument, list info on that breakpoint. ``` ### Control Commands: quit, restart, source #### Quit To exit `byebug`, type `quit` (abbreviated `q` and aliased `exit`). Normally if you are in an interactive session, this command will prompt you to confirm you really want to quit. If you don't want any questions asked, enter `quit unconditionally` (abbreviated `q!`). #### Restart To restart the program, use the `restart|r` command. This is a re-exec - all `byebug` state is lost. If command arguments are passed, those are used. Otherwise program arguments from the last invocation are used. You won't be able to restart your program in all cases. First, the program should have been invoked at the outset rather than having been called from inside your program or invoked as a result of post-mortem handling. #### Source You can run `byebug` commands inside a file, using the command `source `. The lines in a command file are executed sequentially. They are not printed as they are executed. If there is an error, execution proceeds to the next command in the file. For information about command files that get run automatically on startup see [Command Files](). ### Display Commands: display, undisplay #### Display If you find that you want to print the value of an expression frequently (to see how it changes), you might want to add it to the *automatic display list** so that `byebug` evaluates it each time your program stops or after a line is printed if line tracing is enabled. Each expression added to the list is given a number to identify it; to remove an expression from the list, you specify that number. The automatic display looks like this: ```bash (byebug) display n 1: n = 3 ``` This display shows item numbers, expressions and their current values. If the expression is undefined or illegal the expression will be printed but no value will appear. ```bash (byebug) display undefined_variable 2: undefined_variable = (byebug) display 1/0 3: 1/0 = ``` If you use `display` with no argument, `byebug` will display the current values of the expressions in the list, just as it is done when your program stops. Using `info display` has the same effect. #### Undisplay To remove an item from the list, use `undisplay` followed by the number identifying the expression you want to remove. `undisplay` does not repeat if you press ``after using it (otherwise you would just get the error _No display number n_) You can also temporarily disable or enable display expressions, so that the will not be printed but they won't be forgotten either, so you can toggle them again later. To do that, use `disable display` or `enable display` followed by the expression number. ### Print Commands One way to examine and change data in your script is with the `eval` command (abbreviated `p`). `byebug` by default evaluates any input that is not recognized as a command, so in most situations `eval` is not necessary and `byebug` will work like a REPL. One case where it's necessary could be when trying to print a variable called `n`. In this case, you have no choice because typing just `n` will execute `byebug`'s command `next`. A similar command to `eval|p` is `pp` which tries to pretty print the result. If the value you want to print is an array, sometimes a columnized list looks nicer. Use `putl` for that. Notice however that entries are sorted to run down first rather than across. If the value is not an array `putl` will just call pretty-print. Sometimes you may want to print the array not only columnized, but sorted as well. The list of byebug help commands appears this way, and so does the output of the `method` commands. Use `ps` for that. If the value is not an array `ps` will just call pretty-print. ```bash (byebug) Kernel.instance_methods [:nil?, :===, :=~, :!~, :eql?, :hash, :<=>, :class, :singleton_class, :clone, :dup, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :freeze, :frozen?, :to_s, :inspect, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :instance_variables, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :remove_instance_variable, :instance_of?, :kind_of?, :is_a?, :tap, :send, :public_send, :respond_to?, :extend, :display, :method, :public_method, :define_singleton_method, :object_id, :to_enum, :enum_for, :gem, :pretty_inspect, :byebug] (byebug) p Kernel.instance_methods [:nil?, :===, :=~, :!~, :eql?, :hash, :<=>, :class, :singleton_class, :clone, :dup, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :freeze, :frozen?, :to_s, :inspect, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :instance_variables, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :remove_instance_variable, :instance_of?, :kind_of?, :is_a?, :tap, :send, :public_send, :respond_to?, :extend, :display, :method, :public_method, :define_singleton_method, :object_id, :to_enum, :enum_for, :gem, :pretty_inspect, :byebug] (byebug) pp Kernel.instance_methods [:nil?, :===, :=~, :!~, :eql?, :hash, :<=>, :class, :singleton_class, :clone, :dup, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :freeze, :frozen?, :to_s, :inspect, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :instance_variables, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :remove_instance_variable, :instance_of?, :kind_of?, :is_a?, :tap, :send, :public_send, :respond_to?, :extend, :display, :method, :public_method, :define_singleton_method, :object_id, :to_enum, :enum_for, :gem, :pretty_inspect, :byebug] (byebug) putl Kernel.instance_methods nil? trust is_a? === freeze tap =~ frozen? send !~ to_s public_send eql? inspect respond_to? hash methods extend <=> singleton_methods display class protected_methods method singleton_class private_methods public_method clone public_methods singleton_method dup instance_variables define_singleton_method itself instance_variable_get object_id taint instance_variable_set to_enum tainted? instance_variable_defined? enum_for untaint remove_instance_variable gem untrust instance_of? pretty_inspect untrusted? kind_of? (byebug) ps Kernel.instance_methods !~ instance_of? public_send <=> instance_variable_defined? remove_instance_variable === instance_variable_get respond_to? =~ instance_variable_set send class instance_variables singleton_class clone is_a? singleton_method define_singleton_method itself singleton_methods display kind_of? taint dup method tainted? enum_for methods tap eql? nil? to_enum extend object_id to_s freeze pretty_inspect trust frozen? private_methods untaint gem protected_methods untrust hash public_method untrusted? ``` Finally, if you need more advanced functionality from REPL's, you can enter `irb` or `pry` using `irb` or `pry` commands. The bindings environment will be set to the current state in the program. When you leave the repl and go back to `byebug`'s command prompt we show the file, line and text position of the program. If you issue a `list` without location information, the default location used is the current line rather than the current position that may have got updated via a prior `list` command. ``` $ byebug triangle.rb [1, 10] in /path/to/triangle.rb 1: # Compute the n'th triangle number, the hard way: triangle(n) == (n*(n+1))/2 => 2: def triangle(n) 3: tri = 0 4: 0.upto(n) do |i| 5: tri += i 6: end 7: tri 8: end 9: 10: if __FILE__ == $0 (byebug) irb 2.0.0-p247 :001 > (0..6).inject{|sum, i| sum +=i} => 21 2.0.0-p247 :002 > exit /home/davidr/Proyectos/byebug/old_doc/triangle.rb @ 2 def triangle(n) (byebug) list # same line range as before going into irb [1, 10] in /path/to/triangle.rb 1: # Compute the n'th triangle number, the hard way: triangle(n) == (n*(n+1))/2 => 2: def triangle(n) 3: tri = 0 4: 0.upto(n) do |i| 5: tri += i 6: end 7: tri 8: end 9: 10: if __FILE__ == $0 (byebug) ``` ### Printing variables Byebug can print many different information about variables. Such as * `var const `. Show the constants of ``. This is basically listing variables and their values in `.constant`. * `var instance `. Show the instance variables of ``. This is basically listing `.instance_variables`. * `var instance`. Show instance_variables of `self`. * `var local`. Show local variables. * `var global`. Show global variables. * `var all`. Show local, global and instance and class variables of `self`. * `method instance `. Show methods of ``. Basically this is the same as running `ps .instance_methods(false)`. * `method `. Show methods of the class or module ``. Basically this is the same as running `ps .methods`. ### Examining Program Source Files: list `byebug` can print parts of your script's source. When your script stops, `byebug` spontaneously lists the source code around the line where it stopped that line. It does that when you change the current stack frame as well. Implicitly there is a default line location. Each time a list command is run that implicit location is updated, so that running several list commands in succession shows a contiguous block of program text. If you don't need code context displayed every time, you can issue the `set noautolist` command. Now whenever you want code listed, you can explicitly issue the `list` command or its abbreviation `l`. Notice that when a second listing is displayed, we continue listing from the place we last left off. When the beginning or end of the file is reached, the line range to be shown is adjusted so "it doesn't overflow". You can set the `noautolist` option by default by dropping `set noautolist` in byebug's startup file `.byebugrc`. If you want to set how many lines to be printed by default rather than use the initial number of lines, 10, use the `set listsize` command ([listsize()). To see the entire program in one shot, give an explicit starting and ending line number. You can print other portions of source files by giving explicit position as a parameter to the list command. There are several ways to specify what part of the file you want to print. `list nnn` prints lines centered around line number `nnn` in the current source file. `l` prints more lines, following the last lines printed. `list -` prints lines just before the lines last printed. `list nnn-mmm` prints lines between `nnn` and `mmm` inclusive. `list =` prints lines centered around where the script is stopped. Repeating a `list` command with `RET` discards the argument, so it is equivalent to typing just `list`. This is more useful than listing the same lines again. An exception is made for an argument of `-`: that argument is preserved in repetition so that each repetition moves up in the source file. ### Editing Source files: edit To edit a source file, use the `edit` command. The editor of your choice is invoked with the current line set to the active line in the program. Alternatively, you can give a line specification to specify what part of the file you want to edit. You can customize `byebug` to use any editor you want by using the `EDITOR` environment variable. The only restriction is that your editor (say `ex`) recognizes the following command-line syntax: ``` ex +nnn file ``` The optional numeric value `+nnn` specifies the line number in the file where you want to start editing. For example, to configure `byebug` to use the `vi` editor, you could use these commands with the `sh` shell: ```bash EDITOR=/usr/bin/vi export EDITOR byebug ... ``` or in the `csh` shell, ```bash setenv EDITOR /usr/bin/vi byebug ... ``` ### The stack trace When your script has stopped, one thing you'll probably want to know is where it stopped and some idea of how it got there. Each time your script calls a method or enters a block, information about this action is saved. This information is what we call a _stack frame_ or just a _frame_. The set of all frames at a certain point in the program's execution is called the _stack trace_ or just the _stack_. Each frame contains a line number and the source-file name that the line refers to. If the frame is the beginning of a method it also contains the method name. When your script is started, the stack has only one frame, that of the `main` method. This is called the _initial frame_ or the _outermost frame_. Each time a method is called, a new frame is added to the stack trace. Each time a method returns, the frame for that method invocation is removed. If a method is recursive, there can be many frames for the same method. The frame for the method in which execution is actually occurring is called the _innermost frame_. This is the most recently created of all the stack frames that still exist. Every time the debugger stops, one entry in the stack is selected as the current frame. Many byebug commands refer implicitly to the selected block. In particular, whenever you ask Byebug to list lines without giving a line number or location the value is found in the selected frame. There are special commands to select whichever frame you're interested in, such as `up`, `down` and `frame`. After switching frames, when you issue a `list` command without any position information, the position used is the location in the frame that you just switched between, rather than a location that got updated via a prior `list` command. Byebug assigns numbers to all existing stack frames, starting with zero for the _innermost frame_, one for the frame that called it, and so on upward. These numbers do not really exist in your script, they are assigned by Byebug to give you a way of designating stack frames in commands. ### Printing the Stack: `where` command The command `where`, aliased to `bt` or `backtrace` prints the call stack., It shows one line per frame, for many frames, starting with the place that you are stopped at (frame zero), followed by its caller (frame one), and on up the stack. Each frame is numbered and can be referred to in the `frame` command. The position of the current frame is marked with `-->`. The are some special frames generated for methods that are implemented in C. One such method is `each`. They are marked differently in the call stack to indicate that we cannot switch to those frames. This is because they have no source code in Ruby, so we can not debug them using Byebug. ```bash (byebug) where --> #0 Object.gcd(a#Fixnum, b#Fixnum) at line gcd.rb:6 #1 at line gcd.rb:19 ``` ### Selecting a frame: `up`, `down` and `frame` commands * `up `: Move `n` frames up the stack, towards the outermost frame (higher frame numbers, frames that have existed longer). `n` defaults to one. * `down `: Move `n` frames down the stack, towards the _innermost frame_ (lower frame numbers, frames that were created more recently). `n` defaults to one. * `frame `: Allows you to move to an arbitrary frame. `n` is the stack frame number or 0 if no frame number is given. `frame 0` will show the current and most recent stack frame. If a negative number is given, counting is from the other end of the stack frame, so `frame -1` shows the least-recent, outermost stack frame. Without an argument, `frame` prints the current stack frame. byebug-5.0.0/CONTRIBUTING.md0000644000076400007640000000343112560626142014263 0ustar pravipravi## Getting started The following steps should help you getting started: * `Byebug` depends on the TracePoint API provided by `ruby-core`. This is a young API and a lot of bugs have been recently corrected, so make sure you always have the lastest patch level release installed. * Get a local clone of `byebug`'s source code. * Run `bundle install` to get development & test dependencies installed. * Install the [overcommit][] hooks using `bundle exec overcommit --install`. They will review your changes before they are committed, checking they are consistent with the project's code style. If you're changing C files, make sure you have the GNU indent utility installed in your system. `sudo apt-get install indent` for linux or `brew install gnu-indent --with-default-names` should do the job. * Make sure you compile the C-extension using `bundle exec rake compile`. Otherwise you won't be able to use `byebug`. * Run the test suite using the default rake task (`bundle exec rake`). This task is composed of 2 subtasks: `bundle exec rake compile` && `bundle exec rake test`. After having done this, just read the code and improve it! Your contribution is appreciated a lot! [overcommit]: https://github.com/brigade/overcommit/ ## Byebug as a C-extension Byebug is a gem developed as a C-extension. The debugger internal's functionality is implemented in C (the interaction with the TracePoint API). The rest of the gem is implemented in Ruby. Normally you won't need to touch the C-extension, but it will obviously depended on the bug you're trying to fix or the feature you are willing to add. You can learn more about C-extensions [here](http://tenderlovemaking.com/2009/12/18/writing-ruby-c-extensions-part-1.html) or [here](http://tenderlovemaking.com/2010/12/11/writing-ruby-c-extensions-part-2.html). byebug-5.0.0/bin/0000755000076400007640000000000012560626142012601 5ustar pravipravibyebug-5.0.0/bin/byebug0000755000076400007640000000010512560626142014000 0ustar pravipravi#!/usr/bin/env ruby require 'byebug/runner' Byebug::Runner.new.run byebug-5.0.0/README.md0000644000076400007640000001370212560626142013313 0ustar pravipravi# Byebug [![Ver][gem]][gem_url] [![Gpa][gpa]][gpa_url] [![Dep][dep]][dep_url] [![Cov][cov]][cov_url] [![Git][tip]][tip_url] [gem]: https://img.shields.io/gem/v/byebug.svg [gpa]: https://img.shields.io/codeclimate/github/deivid-rodriguez/byebug.svg [dep]: https://img.shields.io/gemnasium/deivid-rodriguez/byebug.svg [cov]: https://img.shields.io/codeclimate/coverage/github/deivid-rodriguez/byebug.svg [tip]: https://img.shields.io/gittip/deivid-rodriguez.svg [gem_url]: https://rubygems.org/gems/byebug [gpa_url]: https://codeclimate.com/github/deivid-rodriguez/byebug [dep_url]: https://gemnasium.com/deivid-rodriguez/byebug [cov_url]: https://codeclimate.com/github/deivid-rodriguez/byebug [tip_url]: https://www.gittip.com/deivid-rodriguez _Debugging in Ruby 2_ Byebug is a simple to use, feature rich debugger for Ruby 2. It uses the new TracePoint API for execution control and the new Debug Inspector API for call stack navigation, so it doesn't depend on internal core sources. It's developed as a C extension, so it's fast. And it has a full test suite so it's reliable. It allows you to see what is going on _inside_ a Ruby program while it executes and offers many of the traditional debugging features such as: * Stepping: Running your program one line at a time. * Breaking: Pausing the program at some event or specified instruction, to examine the current state. * Evaluating: Basic REPL functionality, although [pry][] does a better job at that. * Tracking: Keeping track of the different values of your variables or the different lines executed by your program. ## Build Status Linux & OSX [![Tra][tra]][tra_url] Windows [![Vey][vey]][vey_url] [tra]: https://img.shields.io/travis/deivid-rodriguez/byebug.svg?branch=master [vey]: https://ci.appveyor.com/api/projects/status/github/deivid-rodriguez/byebug?svg=true [tra_url]: https://travis-ci.org/deivid-rodriguez/byebug [vey_url]: https://ci.appveyor.com/project/deivid-rodriguez/byebug ## Requirements * Required: MRI 2.0.0 or higher. For debugging ruby 1.9.3 or older, use [debugger][]. * Recommended: - MRI 2.0.0-p576 or higher. - MRI 2.1.3 or higher. - MRI 2.2.1 or higher. ## Install $ gem install byebug ## Usage Simply drop byebug wherever you want to start debugging and the execution will stop there. If you are debugging rails, start the server and once the execution gets to your `byebug` command you will get a debugging prompt. ## Byebug's commands Command | Aliases | Subcommands ----------- |:------------ |:----------- `backtrace` | `bt` `where` | `break` | | `catch` | | `condition` | | `continue` | | `delete` | | `disable` | | `breakpoints` `display` `display` | | `down` | | `edit` | | `enable` | | `breakpoints` `display` `eval` | | `finish` | | `frame` | | `help` | | `history` | | `info` | | `args` `breakpoints` `catch` `display` `file` `line` `program` `irb` | | `kill` | | `list` | | `method` | | `instance` `next` | | `pp` | | `pry` | | `ps` | | `putl` | | `quit` | `exit` | `restart` | | `save` | | `set` | | `autoeval` `autoirb` `autolist` `autosave` `basename` `callstyle` `fullpath` `histfile` `histsize` `linetrace` `listsize` `post_mortem` `savefile` `stack_on_error` `verbose` `width` `show` | | `autoeval` `autoirb` `autolist` `autosave` `basename` `callstyle` `fullpath` `histfile` `histsize` `linetrace` `listsize` `post_mortem` `savefile` `stack_on_error` `verbose` `width` `source` | | `step` | | `thread` | | `current` `list` `resume` `stop` `switch` `tracevar` | | `undisplay` | | `up` | | `var` | | `all` `constant` `global` `instance` `local` ## Semantic Versioning Byebug tries to follow [semantic versioning](http://semver.org) and tries to bump major version only when backwards incompatible changes are released. Backwards compatibility is targeted to [pry-byebug][] and any other plugins relying on `byebug`. ## Getting Started Read [byebug's markdown guide](https://github.com/deivid-rodriguez/byebug/blob/master/GUIDE.md) to get started. Proper documentation will be eventually written. ## Related projects * [pry-byebug][] adds `next`, `step`, `finish`, `continue` and `break` commands to `pry` using `byebug`. * [ruby-debug-passenger][] adds a rake task that restarts Passenger with Byebug connected. * [minitest-byebug][] starts a byebug session on minitest failures. * [sublime_debugger][] provides a plugin for ruby debugging on Sublime Text. ## Contribute See [Getting Started with Development](CONTRIBUTING.md). ## Credits Everybody who has ever contributed to this forked and reforked piece of software, specially: * @ko1, author of the awesome TracePoint API for Ruby. * @cldwalker, [debugger][]'s mantainer. * @denofevil, author of [debase][], the starting point of this. * @kevjames3 for testing, bug reports and the interest in the project. * @FooBarWidget for working and helping with remote debugging. [debugger]: https://github.com/cldwalker/debugger [pry]: https://github.com/pry/pry [debase]: https://github.com/denofevil/debase [pry-byebug]: https://github.com/deivid-rodriguez/pry-byebug [ruby-debug-passenger]: https://github.com/davejamesmiller/ruby-debug-passenger [minitest-byebug]: https://github.com/kaspth/minitest-byebug [sublime_debugger]: https://github.com/shuky19/sublime_debugger byebug-5.0.0/CHANGELOG.md0000644000076400007640000003410012560626142013640 0ustar pravipravi## 5.0.0 ### Fixed * [#136](https://github.com/deivid-rodriguez/byebug/issues/136). `frame` command not working with negative numbers (thanks @ark6). ### Added * IDE support and a new command/subcommand API for plugins. * Add a "savefile" setting holding the file where "save" command saves current debugger's state. ### Changed * `disable` no longer disable all breakpoints, it just shows command's help instead. To disable all breakpoints now you need to do `disable breakpoints` (or `dis b`). Similarly, you can't no longer use `dis 1 2 3` but need to do `dis b 1 2 3` to disable specific breakpoints. The same applies to the `enable` command. ### Removed * `help set ` no longer works. `help set` includes that same output and it's not verbose enough so that this is a problem. Same with `help show `. ## 4.0.5 - 2015-04-02 ### Fixed * [#131](https://github.com/deivid-rodriguez/byebug/issues/131) * Thread commands help format should be consistent with the rest of the help system now. ## 4.0.4 - 2015-03-27 ### Fixed * [#127](https://github.com/deivid-rodriguez/byebug/issues/127) ## 4.0.3 - 2015-03-19 ### Fixed * Unused variable warning in context.c ## 4.0.2 - 2015-03-16 ### Fixed * [#118](https://github.com/deivid-rodriguez/byebug/issues/118). Remove `rb-readline` as a dependency and show a help message whenever requiring `readline` fails instead. ## 4.0.1 - 2015-03-13 ### Fixed * .yml files needed for printers support were missing from the release... :S * [#118](https://github.com/deivid-rodriguez/byebug/issues/118). Add `readline` as a dependency. ## 4.0.0 - 2015-03-13 ### Added - `untracevar` command that stops tracing a global variable. - Window CI build through AppVeyor. - OSX CI build through Travis. - Style enforcement through RuboCop. - C style enforment using the `indent` command line utility. - Some remote debugging tests (thanks @eric-hu). - Printer's support (thanks @astashov). ### Changed - A lot of internal refactoring. - `tracevar` now requires the full global variable name (with "$"). - [#92](https://github.com/deivid-rodriguez/byebug/issues/92). The `catch` command is not allowed in post_mortem mode anymore. It was not working anyways. - [#85](https://github.com/deivid-rodriguez/byebug/issues/85). `step` is now more user friendly when used in combination with `up`. - `var const` can now be called without an argument and will show constants in the current scope. - `break` with a class name now creates breakpoints regardless of class not being yet defined. If that's the case, it gives a warning but the class is created anyways. ### Fixed - Code reloading issues. - `set fullpath` was not showing fullpaths. Now it is. - `up`, `down` and `frame` commands now work in post_mortem mode (#93). - rc file (`.byebugrc`) loading: invalid commands are just ignored instead of aborting, global (home) rc file is now properly loaded before project's file. - [#93](https://github.com/deivid-rodriguez/byebug/issues/93). Backtraces not working in `post_mortem` mode. - 'cmd1 ; cmd2 ; ...; cmdN' syntax which allows running several commands sequentially. - [#101](https://github.com/deivid-rodriguez/byebug/issues/101). `finish` command not stopping at the correct line. - [#106](https://github.com/deivid-rodriguez/byebug/issues/106). `break` with namespaced class, like `break A::B#c` should now work. - Command history is now persisted before exiting byebug. - Setting breakpoint in a method would stop not only at the beginning of the method but also at the beginning of every block inside the method. - [#122](https://github.com/deivid-rodriguez/byebug/issues/122). Setting breakpoints on module methods (@x-yuri). ### Removed - `autoreload` setting as it's not necessary anymore. Code should always be up to date. - `reload` command for the same reason. - Gem dependency on `debugger-linecache`. - `step+`, `step-`, `next+`, `next-`, `set/show linetrace_plus` and `set/show forcestep` commands. These were all mechanisms to deal with TracePoint API event dupplication, but this duplicated events have been completely removed from the API since [r48609](bugs.ruby-lang.org/projects/ruby-trunk/repository/revisions/48609), so they are no longer necessary. - `info file` subcommands: `info file breakpoints`, `info file mtime`, `info file sha1`, `info file all`. Now all information is listed under `info file`. - `testing` setting. It was just a hack to be able to test `byebug`. Nobody was supposed to actually use it! - `var class` command, just use Ruby (`self.class.class_variables`). - `p` command, just use `eval`, or just type your expression and `byebug` will autoevaluate it. - `exit` alias for `quit`. ## 3.5.1 - 2014-09-29 ### Fixed - [#79](https://github.com/deivid-rodriguez/byebug/issues/79). Windows installation. - `condition` command not properly detecting invalid breakpoint ids. ## 3.5.0 - 2014-09-28 ### Fixed - [#81](https://github.com/deivid-rodriguez/byebug/issues/81). Byebug's history messing up other programs using Readline. - Readline's history not being properly saved and inmediately available. - User not being notified when trying to debug a non existent script. ### Changed - Complete rewrite of byebug's history. - Complete rewrite of list command. - Docs about stacktrace related commands (up, down, frame, backtrace). ## 3.4.2 - 2014-09-26 ### Fixed - [#67](https://github.com/deivid-rodriguez/byebug/issues/67). Debugging commands invoked by ruby executable, as in `byebug -- ruby -Itest a_test.rb -n test_something`. ## 3.4.1 - 2014-09-25 ### Fixed - [#54](https://github.com/deivid-rodriguez/byebug/issues/54). Use of threads inside `eval` command. - `list` command not listing backwards after reaching the end of the file. ## 3.4.0 - 2014-09-01 ### Fixed - deivid-rodriguez/pry-byebug#32 in a better way. ## 3.3.0 - 2014-08-28 ### Fixed - `set verbose` command. - `set post_mortem false` command. - Debugger stopping in `byebug`'s internal frames in some cases. - `backtrace` crashing when `fullpath` setting disabled and calculated stack size being smaller than the real one. ## Changed - The `-t` option for `bin/byebug` now turns tracing on whereas the `-x` option tells byebug to run the initialization file (.byebugrc) on startup. This is the default behaviour though. - `bin/byebug` libified and tests added. ### Removed - `info locals` command. Use `var local` instead. - `info instance_variables` command. Use `var instance` instead. - `info global_variables` command. Use `var global` instead. - `info variables` command. Use `var all` instead. - `irb` command stepping capabilities, see [8e226d0](https://github.com/deivid-rodriguez/byebug/commit/8e226d0). - `script` and `restart-script` options for `bin/byebug`. ## 3.2.0 - 2014-08-02 ### Fixed - [#71](https://github.com/deivid-rodriguez/byebug/issues/71). Remote debugging (thanks @shuky19). - [#69](https://github.com/deivid-rodriguez/byebug/issues/69). `source` command (thanks @Olgagr). ### Removed - `post_mortem` activation through `Byebug.post_mortem`. Use `set post_mortem` instead. - `info stack` command. Use `where` instead. - `method iv` command. Use `var instance` instead. - [#77](https://github.com/deivid-rodriguez/byebug/issues/77). Warning. ## 3.1.2 - 2014-04-23 ### Fixed - (Really) `post_mortem` mode in `bin/byebug`. - Line tracing in `bin/byebug`. ## 3.1.1 - 2014-04-23 ### Fixed - `post_mortem` mode in bin/byebug. ## 3.1.0 - 2014-04-23 ### Removed - `show commands` command. Use `history` instead. - Byebug.start accepting options. Any program settings you want applied from the start should be set in `.byebugrc`. - `trace` command. Use `set linetrace` for line tracing and `tracevar` for global variable tracing. - `show version` command. Use `byebug --version` to check byebug's version. - `set arg` setting. Use the `restart` command instead. ### Changed - `linetrace_plus` setting renamed to `tracing_plus`. ### Added - `history` command to check byebug's history of previous commands. ## 3.0.0 - 2014-04-17 ### Fixed - Plain `byebug` not working when `pry-byebug` installed. - `post_mortem` mode. - Command history not being saved after regular program termination. - [#54](https://github.com/deivid-rodriguez/byebug/issues/54). (Again) calling `Byebug.start` with `Timeout.timeout` (thanks @zmoazeni). ### Added - Allow disabling `post_mortem` mode. ### Changed - `show commands` command for listing history of previous commands now behaves like shell's `history` command. - `show/set history filename` is now `show/set histfile` - `show/set history size` is now `show/set histsize` - `show/set history save` is now `show/set autosave` - `finish` semantics, see [61f9b4d](https://github.com/deivid-rodriguez/byebug/commit/61f9b4d). - Use `per project` history file by default. ## Removed - The `init` option for `Byebug.start`. Information to make the `restart` command work is always saved now. ## 2.7.0 - 2014-02-24 ### Fixed - [#52](https://github.com/deivid-rodriguez/byebug/issues/52). `IGNORED_FILES` slowing down startup. - [#53](https://github.com/deivid-rodriguez/byebug/issues/53) and [#54](https://github.com/deivid-rodriguez/byebug/issues/54). Calling `Byebug.start` with `Timeout.timeout`. ## 2.6.0 - 2014-02-08 ### Fixed - Circular dependency affecting `pry-byebug` (thanks @andreychernih). ## 2.5.0 - 2013-12-14 ### Added - Support for `sublime-debugger`. ## 2.4.1 - 2013-12-05 ### Fixed - [#40](https://github.com/deivid-rodriguez/byebug/issues/40). Installation error in Mac OSX (thanks @luislavena). ## 2.4.0 - 2013-12-02 ### Fixed - `thread list` showing too many threads. - Fix setting post mortem mode with `set post_mortem`. Now this is the only post mortem functionality available as specifying `Byebug.post_mortem` with a block has been removed in this version. ### Added - (Again) `debugger` as an alias to `byebug` (thanks @wallace). - `-R` option for `bin/byebug` to specify server's hostname:port for remote debugging (thanks @mrkn). ### Changed - Use `require` instead of `require_relative` for loading byebug's extension library (thanks @nobu). - `trace variable $foo` should be now `trace variable $foo`. ## 2.3.1 - 2013-10-17 ### Fixed - Breakpoint removal. - Broken test suite. ## 2.3.0 - 2013-10-09 ### Added - Compatibility with Phusion Passenger Enterprise (thanks @FooBarWidget). ### Changed - More minimalist help system. ## 2.2.2 - 2013-09-25 ### Fixed - Compilation issue in 64 bit systems. ## 2.2.1 - 2013-09-24 ### Fixed - [#26](https://github.com/deivid-rodriguez/byebug/issues/26). Compilation issue introduced in `2.2.0`. ### Changed - `show/set stack_trace_on_error` is now `show/set stack_on_error`. ## 2.2.0 - 2013-09-22 ### Fixed - Stack size calculations. - Setting `post_mortem` mode. ### Added - `verbose` setting for TracePoint API event inspection. ### Changed - Warning free byebug. - Allow `edit ` without a line number. ## 2.1.1 - 2013-09-10 ### Fixed - Debugging code inside `-e` Ruby flag. ## 2.1.0 - 2013-09-08 ### Fixed - Remote debugging display. - `eval` crashing when inspecting raised an exception (reported by @iblue). ### Changed - `enable breakpoints` now enables every breakpoint. - `disable breakpoints` now disables every breakpoint. ## 2.0.0 - 2013-08-30 ### Added - "Official" definition of a command API. - Thread support. ### Removed - `jump` command. It had never worked. ### Changed - Several internal refactorings. ## 1.8.2 - 2013-08-16 ### Fixed - `save` command now saves the list of `displays`. - Stack size calculation. ### Changed - More user friendly regexps for commands. - Better help for some commands. ## 1.8.1 - 2013-08-12 ### Fixed - Major regression introduced in 1.8.0. ## 1.8.0 - 2013-08-12 ### Added - Remote debugging support. ## 1.7.0 - 2013-08-03 ### Added - List command automatically called after callstack navigation commands. - C-frames specifically marked in the callstack. - C-frames skipped when navigating the callstack. ## 1.6.1 - 2013-07-10 ### Fixed - Windows compatibiliy: compilation and terminal width issues. ## 1.6.0 - 2013-07-10 ### Fixed - `byebug` placed at the end of a block or method call not working as expected. - `autolist` being applied when Ruby `-e` option used. ### Changed - Backtrace callstyles. Use `long` for detailed frames in callstack and `short` for more concise frames. ## 1.5.0 - 2013-06-21 ### Fixed - Incomplete backtraces when the debugger was not started at program startup. ## 1.4.2 - 2013-06-20 ### Fixed - `help command subcommand` command. - Interaction with Rails Console debugging flag. - `post_mortem` mode when running byebug from the outset. - `no-quit` flag when running byebug from the outset. ## 1.4.1 - 2013-06-15 ### Fixed - Crash when printing some filenames in backtraces. - Allow byebug developers to easily use compilers different from gcc (thanks @GarthSnyder!). ## 1.4.0 - 2013-06-05 ### Fixed - Memory leaks causing `byebug` to randomly crash. ### Changed - Use the Debug Inspector API for backtrace information. ## 1.3.1 - 2013-06-02 ### Fixed - Interaction with Rails debugging flag. - Crash when trying to print lines of code containing the character '%'. - `basename` and `linetrace` options not working together. ## 1.3.0 - 2013-05-25 ### Added - Support colon-delimited include paths in command-line front-end (@ender672). ## 1.2.0 - 2013-05-20 ### Fixed - Ctrl+C during command line editing (works like pry/irb). ### Added - `pry` command. ## 1.1.1 - 2013-05-07 ### Added - `pry-byebug` compatibility. ### Changed - Better help system. - Code cleanup. ## 1.1.0 - 2013-04-30 ### Added - Post Mortem support. ## 1.0.3 - 2013-04-23 ### Fixed - Negative line numbers shown by list command at the beginning of file. - `backtrace` command segfaulting when trying to show info on some frame args. Don't know the reason yet, but the exception is handled now and command does not segfault anymore. ### Changed - `autoreload` is set by default now. - Try some thread support (not even close to usable). ## 1.0.2 - 2013-04-09 ### Fixed - backtraces messed up when using both `next`/`step` and backtrace navigation commands. ### Changed - `autolist` and `autoeval` are default settings now. ## 1.0.1 - 2013-04-06 ### Fixed - Byebug not loading properly. ## 1.0.0 - 2013-03-29 ### Fixed - Green test suite. ## 0.0.1 - 2013-03-18 ### Added - Initial release. byebug-5.0.0/metadata.yml0000644000076400007640000001370712560626142014344 0ustar pravipravi--- !ruby/object:Gem::Specification name: byebug version: !ruby/object:Gem::Version version: 5.0.0 platform: ruby authors: - David Rodriguez - Kent Sibilev - Mark Moseley autorequire: bindir: bin cert_chain: [] date: 2015-05-18 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: columnize requirement: !ruby/object:Gem::Requirement requirements: - - '=' - !ruby/object:Gem::Version version: 0.9.0 type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - '=' - !ruby/object:Gem::Version version: 0.9.0 - !ruby/object:Gem::Dependency name: bundler requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '1.7' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '1.7' description: |- Byebug is a Ruby 2 debugger. It's implemented using the Ruby 2 TracePoint C API for execution control and the Debug Inspector C API for call stack navigation. The core component provides support that front-ends can build on. It provides breakpoint handling and bindings for stack frames among other things and it comes with an easy to use command line interface. email: deivid.rodriguez@mail.com executables: - byebug extensions: - ext/byebug/extconf.rb extra_rdoc_files: - CHANGELOG.md - CONTRIBUTING.md - README.md - GUIDE.md files: - CHANGELOG.md - CONTRIBUTING.md - GUIDE.md - LICENSE - README.md - bin/byebug - ext/byebug/breakpoint.c - ext/byebug/byebug.c - ext/byebug/byebug.h - ext/byebug/context.c - ext/byebug/extconf.rb - ext/byebug/locker.c - ext/byebug/threads.c - lib/byebug.rb - lib/byebug/attacher.rb - lib/byebug/breakpoint.rb - lib/byebug/command.rb - lib/byebug/commands/break.rb - lib/byebug/commands/catch.rb - lib/byebug/commands/condition.rb - lib/byebug/commands/continue.rb - lib/byebug/commands/delete.rb - lib/byebug/commands/disable.rb - lib/byebug/commands/disable/breakpoints.rb - lib/byebug/commands/disable/display.rb - lib/byebug/commands/display.rb - lib/byebug/commands/down.rb - lib/byebug/commands/edit.rb - lib/byebug/commands/enable.rb - lib/byebug/commands/enable/breakpoints.rb - lib/byebug/commands/enable/display.rb - lib/byebug/commands/eval.rb - lib/byebug/commands/finish.rb - lib/byebug/commands/frame.rb - lib/byebug/commands/help.rb - lib/byebug/commands/history.rb - lib/byebug/commands/info.rb - lib/byebug/commands/info/args.rb - lib/byebug/commands/info/breakpoints.rb - lib/byebug/commands/info/catch.rb - lib/byebug/commands/info/display.rb - lib/byebug/commands/info/file.rb - lib/byebug/commands/info/line.rb - lib/byebug/commands/info/program.rb - lib/byebug/commands/interrupt.rb - lib/byebug/commands/irb.rb - lib/byebug/commands/kill.rb - lib/byebug/commands/list.rb - lib/byebug/commands/method.rb - lib/byebug/commands/next.rb - lib/byebug/commands/pp.rb - lib/byebug/commands/pry.rb - lib/byebug/commands/ps.rb - lib/byebug/commands/putl.rb - lib/byebug/commands/quit.rb - lib/byebug/commands/restart.rb - lib/byebug/commands/save.rb - lib/byebug/commands/set.rb - lib/byebug/commands/show.rb - lib/byebug/commands/source.rb - lib/byebug/commands/step.rb - lib/byebug/commands/thread.rb - lib/byebug/commands/thread/current.rb - lib/byebug/commands/thread/list.rb - lib/byebug/commands/thread/resume.rb - lib/byebug/commands/thread/stop.rb - lib/byebug/commands/thread/switch.rb - lib/byebug/commands/tracevar.rb - lib/byebug/commands/undisplay.rb - lib/byebug/commands/untracevar.rb - lib/byebug/commands/up.rb - lib/byebug/commands/var.rb - lib/byebug/commands/var/all.rb - lib/byebug/commands/var/const.rb - lib/byebug/commands/var/global.rb - lib/byebug/commands/var/instance.rb - lib/byebug/commands/var/local.rb - lib/byebug/commands/where.rb - lib/byebug/context.rb - lib/byebug/core.rb - lib/byebug/helpers/eval.rb - lib/byebug/helpers/file.rb - lib/byebug/helpers/frame.rb - lib/byebug/helpers/parse.rb - lib/byebug/helpers/string.rb - lib/byebug/helpers/thread.rb - lib/byebug/helpers/toggle.rb - lib/byebug/helpers/var.rb - lib/byebug/history.rb - lib/byebug/interface.rb - lib/byebug/interfaces/local_interface.rb - lib/byebug/interfaces/remote_interface.rb - lib/byebug/interfaces/script_interface.rb - lib/byebug/interfaces/test_interface.rb - lib/byebug/printers/base.rb - lib/byebug/printers/plain.rb - lib/byebug/printers/texts/base.yml - lib/byebug/printers/texts/plain.yml - lib/byebug/processor.rb - lib/byebug/processors/command_processor.rb - lib/byebug/processors/control_command_processor.rb - lib/byebug/remote.rb - lib/byebug/runner.rb - lib/byebug/setting.rb - lib/byebug/settings/autoeval.rb - lib/byebug/settings/autoirb.rb - lib/byebug/settings/autolist.rb - lib/byebug/settings/autosave.rb - lib/byebug/settings/basename.rb - lib/byebug/settings/callstyle.rb - lib/byebug/settings/fullpath.rb - lib/byebug/settings/histfile.rb - lib/byebug/settings/histsize.rb - lib/byebug/settings/linetrace.rb - lib/byebug/settings/listsize.rb - lib/byebug/settings/post_mortem.rb - lib/byebug/settings/savefile.rb - lib/byebug/settings/stack_on_error.rb - lib/byebug/settings/verbose.rb - lib/byebug/settings/width.rb - lib/byebug/state.rb - lib/byebug/states/control_state.rb - lib/byebug/states/regular_state.rb - lib/byebug/subcommand_list.rb - lib/byebug/subcommands.rb - lib/byebug/version.rb homepage: http://github.com/deivid-rodriguez/byebug licenses: - BSD metadata: {} post_install_message: rdoc_options: [] require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: 2.0.0 required_rubygems_version: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' requirements: [] rubyforge_project: rubygems_version: 2.4.7 signing_key: specification_version: 4 summary: Ruby 2.0 fast debugger - base + CLI test_files: [] byebug-5.0.0/lib/0000755000076400007640000000000012560626142012577 5ustar pravipravibyebug-5.0.0/lib/byebug.rb0000644000076400007640000000006012560626142014375 0ustar pravipravirequire 'byebug/core' require 'byebug/attacher' byebug-5.0.0/lib/byebug/0000755000076400007640000000000012560626142014054 5ustar pravipravibyebug-5.0.0/lib/byebug/settings/0000755000076400007640000000000012560626142015714 5ustar pravipravibyebug-5.0.0/lib/byebug/settings/listsize.rb0000644000076400007640000000057512560626142020116 0ustar pravipravirequire 'byebug/setting' module Byebug # # Setting to customize the number of source code lines to be displayed every # time the "list" command is invoked. # class ListsizeSetting < Setting DEFAULT = 10 def banner 'Set number of source lines to list by default' end def to_s "Number of source lines to list is #{value}\n" end end end byebug-5.0.0/lib/byebug/settings/verbose.rb0000644000076400007640000000051212560626142017704 0ustar pravipravirequire 'byebug/setting' module Byebug # # Setting to show verbose output about TracePoint API events. # class VerboseSetting < Setting def banner 'Enable verbose output of TracePoint API events' end def value=(v) Byebug.verbose = v end def value Byebug.verbose? end end end byebug-5.0.0/lib/byebug/settings/post_mortem.rb0000644000076400007640000000163012560626142020611 0ustar pravipravirequire 'byebug/setting' module Byebug # # Setting to enable/disable post_mortem mode, i.e., a debugger prompt after # program termination by unhandled exception. # class PostMortemSetting < Setting def initialize Byebug.post_mortem = DEFAULT end def banner 'Enable/disable post-mortem mode' end def value=(v) Byebug.post_mortem = v end def value Byebug.post_mortem? end end # # Saves information about the unhandled exception and gives a byebug # prompt back to the user before program termination. # def self.handle_post_mortem return unless Byebug.raised_exception context = Byebug.raised_exception.__bb_context file = Byebug.raised_exception.__bb_file line = Byebug.raised_exception.__bb_line Byebug.handler.at_line(context, file, line) end at_exit { Byebug.handle_post_mortem if Byebug.post_mortem? } end byebug-5.0.0/lib/byebug/settings/basename.rb0000644000076400007640000000044712560626142020021 0ustar pravipravirequire 'byebug/setting' module Byebug # # Command to display short paths in file names. # # For example, when displaying source code information. # class BasenameSetting < Setting def banner ': information after every stop uses short paths' end end end byebug-5.0.0/lib/byebug/settings/histsize.rb0000644000076400007640000000056012560626142020104 0ustar pravipravirequire 'byebug/setting' module Byebug # # Setting to customize the number of byebug commands to be saved in history. # class HistsizeSetting < Setting DEFAULT = 256 def banner 'Maximum number of commands that can be stored in byebug history' end def to_s "Maximum size of byebug's command history is #{value}" end end end byebug-5.0.0/lib/byebug/settings/callstyle.rb0000644000076400007640000000051512560626142020236 0ustar pravipravirequire 'byebug/setting' module Byebug # # Setting to customize the verbosity level for stack frames. # class CallstyleSetting < Setting DEFAULT = 'long' def banner 'Set how you want method call parameters to be displayed' end def to_s "Frame display callstyle is '#{value}'" end end end byebug-5.0.0/lib/byebug/settings/linetrace.rb0000644000076400007640000000044612560626142020213 0ustar pravipravirequire 'byebug/setting' module Byebug # # Setting to enable/disable linetracing. # class LinetraceSetting < Setting def banner 'Enable line execution tracing' end def value=(v) Byebug.tracing = v end def value Byebug.tracing? end end end byebug-5.0.0/lib/byebug/settings/savefile.rb0000644000076400007640000000066412560626142020045 0ustar pravipravirequire 'byebug/setting' module Byebug # # Setting to customize the file where byebug's history is saved. # class SavefileSetting < Setting DEFAULT = File.expand_path("#{ENV['HOME'] || '.'}/.byebug_save") def banner <<-EOB File where save commands saves current settings to. Default: ~/.byebug_save EOB end def to_s "The command history file is #{value}\n" end end end byebug-5.0.0/lib/byebug/settings/stack_on_error.rb0000644000076400007640000000041612560626142021254 0ustar pravipravirequire 'byebug/setting' module Byebug # # Setting to enable/disable the display of backtraces when evaluations raise # errors. # class StackOnErrorSetting < Setting def banner 'Display stack trace when `eval` raises an exception' end end end byebug-5.0.0/lib/byebug/settings/fullpath.rb0000644000076400007640000000034612560626142020063 0ustar pravipravirequire 'byebug/setting' module Byebug # # Setting to display full paths in backtraces. # class FullpathSetting < Setting DEFAULT = true def banner 'Display full file names in backtraces' end end end byebug-5.0.0/lib/byebug/settings/autosave.rb0000644000076400007640000000045412560626142020073 0ustar pravipravirequire 'byebug/setting' module Byebug # # Setting for automatically saving previously entered commands to history # when exiting the debugger. # class AutosaveSetting < Setting DEFAULT = true def banner 'Automatically save command history record on exit' end end end byebug-5.0.0/lib/byebug/settings/histfile.rb0000644000076400007640000000060512560626142020051 0ustar pravipravirequire 'byebug/setting' module Byebug # # Setting to customize the file where byebug's history is saved. # class HistfileSetting < Setting DEFAULT = File.expand_path("#{ENV['HOME'] || '.'}/.byebug_hist") def banner 'File where cmd history is saved to. Default: ~/.byebug_hist' end def to_s "The command history file is #{value}\n" end end end byebug-5.0.0/lib/byebug/settings/autoirb.rb0000644000076400007640000000067112560626142017712 0ustar pravipravirequire 'byebug/setting' require 'byebug/commands/irb' module Byebug # # Setting for automatically invoking IRB on every stop. # class AutoirbSetting < Setting DEFAULT = 0 def initialize IrbCommand.always_run = DEFAULT end def banner 'Invoke IRB on every stop' end def value=(v) IrbCommand.always_run = v ? 1 : 0 end def value IrbCommand.always_run == 1 end end end byebug-5.0.0/lib/byebug/settings/autoeval.rb0000644000076400007640000000036612560626142020066 0ustar pravipravirequire 'byebug/setting' module Byebug # # Setting for automatic evaluation of unknown commands. # class AutoevalSetting < Setting DEFAULT = true def banner 'Automatically evaluate unrecognized commands' end end end byebug-5.0.0/lib/byebug/settings/autolist.rb0000644000076400007640000000071612560626142020111 0ustar pravipravirequire 'byebug/setting' require 'byebug/commands/list' module Byebug # # Setting for automatically listing source code on every stop. # class AutolistSetting < Setting DEFAULT = 1 def initialize ListCommand.always_run = DEFAULT end def banner 'Invoke list command on every stop' end def value=(v) ListCommand.always_run = v ? 1 : 0 end def value ListCommand.always_run == 1 end end end byebug-5.0.0/lib/byebug/settings/width.rb0000644000076400007640000000050612560626142017361 0ustar pravipravirequire 'byebug/setting' module Byebug # # Setting to customize the maximum width of byebug's output. # class WidthSetting < Setting DEFAULT = 160 def banner "Number of characters per line in byebug's output" end def to_s "Maximum width of byebug's output is #{value}" end end end byebug-5.0.0/lib/byebug/runner.rb0000644000076400007640000001070312560626142015713 0ustar pravipravirequire 'optparse' require 'English' require 'byebug/core' require 'byebug/helpers/parse' module Byebug # # Responsible for starting the debugger when started from the command line. # class Runner include Helpers::ParseHelper # # Error class signaling absence of a script to debug # class NoScript < StandardError; end # # Error class signaling a non existent script to debug # class NonExistentScript < StandardError; end # # Error class signaling a script with invalid Ruby syntax # class InvalidScript < StandardError; end # # Special working modes that don't actually start the debugger. # attr_accessor :help, :version, :remote # # @param stop [Boolean] Whether the runner should stop right before # starting the program. # # @param quit [Boolean] Whether the runner should quit right after # finishing the program. # def initialize(stop = true, quit = true) @stop = stop @quit = quit end # # Usage banner. # def banner <<-EOB.gsub(/^ {8}/, '') byebug #{Byebug::VERSION} Usage: byebug [options] -- EOB end # # Starts byebug to debug a program # def run prepare_options.order!($ARGV) if version Byebug.puts("\n Running byebug #{version}\n") return end if help Byebug.puts("#{help}\n") return end if remote Byebug.start_client(*remote) return end setup_cmd_line_args loop do debug_program break if @quit processor = Byebug::ControlCommandProcessor.new processor.process_commands end end private # # Processes options passed from the command line # def prepare_options OptionParser.new(banner, 25) do |opts| opts.banner = banner opts.on '-d', '--debug', 'Set $DEBUG=true' do $DEBUG = true end opts.on('-I', '--include list', 'Add to paths to $LOAD_PATH') do |list| $LOAD_PATH.push(list.split(':')).flatten! end opts.on '-m', '--[no-]post-mortem', 'Use post-mortem mode' do |v| Setting[:post_mortem] = v end opts.on '-q', '--[no-]quit', 'Quit when script finishes' do |v| @quit = v end opts.on '-x', '--[no-]rc', 'Run byebug initialization file' do |v| Byebug.run_init_script if v end opts.on '-s', '--[no-]stop', 'Stop when script is loaded' do |v| @stop = v end opts.on '-r', '--require file', 'Require library before script' do |lib| require lib end opts.on '-R', '--remote [host:]port', 'Remote debug [host:]port' do |p| self.remote = Byebug.parse_host_and_port(p) end opts.on '-t', '--[no-]trace', 'Turn on line tracing' do |v| Setting[:linetrace] = v end opts.on '-v', '--version', 'Print program version' do self.version = VERSION end opts.on('-h', '--help', 'Display this message') do self.help = opts.help end end end # # Extracts debugged program from command line args # def setup_cmd_line_args Byebug.mode = :standalone fail(NoScript, 'You must specify a program to debug...') if $ARGV.empty? program = which($ARGV.shift) program = which($ARGV.shift) if program == which('ruby') fail(NonExistentScript, "The script doesn't exist") unless program $PROGRAM_NAME = program end # # Debugs a script only if syntax checks okay. # def debug_program ok = syntax_valid?(File.read($PROGRAM_NAME)) fail(InvalidScript, 'The script has incorrect syntax') unless ok error = Byebug.debug_load($PROGRAM_NAME, @stop) Byebug.puts "#{status}\n#{status.backtrace}" if error end # # Cross-platform way of finding an executable in the $PATH. # Borrowed from: http://stackoverflow.com/questions/2108727 # def which(cmd) return File.expand_path(cmd) if File.exist?(cmd) exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : [''] ENV['PATH'].split(File::PATH_SEPARATOR).each do |path| exts.each do |ext| exe = File.join(path, "#{cmd}#{ext}") return exe if File.executable?(exe) && !File.directory?(exe) end end nil end end end byebug-5.0.0/lib/byebug/subcommands.rb0000644000076400007640000000211612560626142016714 0ustar pravipravirequire 'byebug/command' require 'byebug/subcommand_list' module Byebug # # Subcommand additions. # module Subcommands # # Summarized description of a subcommand # def short_description fail(NotImplementedError, 'Your custom subcommand needs to define this') end # # Delegates to subcommands or prints help if no subcommand specified. # def execute return puts(help) unless @match[1] subcmd = subcommands.find(@match[1]) return errmsg("Unknown subcommand '#{@match[1]}'\n") unless subcmd subcmd.execute end # # Default help text for a command with subcommands # def help prettify <<-EOH #{description} List of "#{to_name}" subcommands: -- #{subcommands} EOH end # # Command's subcommands. # def subcommands subcmd_klasses = self.class.subcommands return nil unless subcmd_klasses.any? subcmd_list = subcmd_klasses.map { |cmd| cmd.new(@state) } SubcommandList.new(subcmd_list, self.class.name) end end end byebug-5.0.0/lib/byebug/history.rb0000644000076400007640000000471512560626142016111 0ustar pravipravibegin require 'readline' rescue LoadError warn <<-EOW Sorry, you can't use byebug without Readline. To solve this, you need to rebuild Ruby with Readline support. If using Ubuntu, try `sudo apt-get install libreadline-dev` and then reinstall your Ruby. EOW raise end module Byebug # # Handles byebug's history of commands. # class History attr_accessor :size def initialize self.size = 0 end # # Restores history from disk. # def restore return unless File.exist?(Setting[:histfile]) File.readlines(Setting[:histfile]).reverse_each { |l| push(l.chomp) } end # # Saves history to disk. # def save n_cmds = Setting[:histsize] > size ? size : Setting[:histsize] open(Setting[:histfile], 'w') do |file| n_cmds.times { file.puts(pop) } end clear end # # Discards history. # def clear size.times { pop } end # # Adds a new command to Readline's history. # def push(cmd) return if ignore?(cmd) self.size += 1 Readline::HISTORY.push(cmd) end # # Removes a command from Readline's history. # def pop self.size -= 1 Readline::HISTORY.pop end # # Prints the requested numbers of history entries. # def to_s(n_cmds) show_size = n_cmds ? specific_max_size(n_cmds) : default_max_size commands = Readline::HISTORY.to_a.last(show_size) last_ids(show_size).zip(commands).map do |l| format('%5d %s', l[0], l[1]) end.join("\n") + "\n" end # # Array of ids of the last n commands. # def last_ids(n) (1 + size - n..size).to_a end # # Max number of commands to be displayed when no size has been specified. # # Never more than Setting[:histsize]. # def default_max_size [Setting[:histsize], self.size].min end # # Max number of commands to be displayed when a size has been specified. # # The only bound here is not showing more items than available. # def specific_max_size(number) [self.size, number].min end # # Whether a specific command should not be stored in history. # # For now, empty lines and consecutive duplicates. # def ignore?(buf) return true if /^\s*$/ =~ buf return false if Readline::HISTORY.length == 0 Readline::HISTORY[Readline::HISTORY.length - 1] == buf end end end byebug-5.0.0/lib/byebug/states/0000755000076400007640000000000012560626142015357 5ustar pravipravibyebug-5.0.0/lib/byebug/states/control_state.rb0000644000076400007640000000056312560626142020570 0ustar pravipravirequire 'byebug/state' module Byebug # # Controls state of Byebug's REPL when in control mode # class ControlState < State def proceed end extend Forwardable def_delegators :@interface, :errmsg, :puts def confirm(*_args) 'y' end def context nil end def file errmsg 'No filename given.' end end end byebug-5.0.0/lib/byebug/states/regular_state.rb0000644000076400007640000001063012560626142020545 0ustar pravipravirequire 'byebug/state' require 'byebug/helpers/file' module Byebug # # Controls state of Byebug's REPL when in normal mode # class RegularState < State include Helpers::FileHelper attr_accessor :context, :frame, :display, :file, :line, :prev_line attr_writer :interface def initialize(context, display, file, interface, line) super(interface) @context = context @display = display @file = file @frame = 0 @line = line @prev_line = nil @proceed = false end extend Forwardable def_delegators :@interface, :errmsg, :puts, :print, :confirm # # Checks whether that execution can proceed # def proceed? @proceed end # # Signals the REPL that the execution can proceed # def proceed @proceed = true end # # Current (formatted) location # def location l = "#{normalize(file)} @ #{line}\n" l += "#{get_line(file, line)}\n" unless %w((irb) -e').include?(file) l end # # Builds a string containing the class associated to frame number +pos+ # or an empty string if the current +callstyle+ setting is 'short' # # @param pos [Integer] Frame position. # def frame_class(pos) return '' if Setting[:callstyle] == 'short' klass = context.frame_class(pos) return '' if klass.to_s.empty? "#{klass}." end # # Builds a formatted string containing information about block and method # of the frame in position +pos+ # # @param pos [Integer] Frame position. # def frame_block_and_method(pos) deco_regexp = /((?:block(?: \(\d+ levels\))?|rescue) in )?(.+)/ deco_method = "#{context.frame_method(pos)}" block_and_method = deco_regexp.match(deco_method)[1..2] block_and_method.map { |x| x.nil? ? '' : x } end # # Builds a string containing all available args in frame number +pos+, in a # verbose or non verbose way according to the value of the +callstyle+ # setting # # @param pos [Integer] Frame position. # def frame_args(pos) args = context.frame_args(pos) return '' if args.empty? locals = context.frame_locals(pos) unless Setting[:callstyle] == 'short' my_args = args.map do |arg| prefix, default = prefix_and_default_from(arg[0]) kls = if Setting[:callstyle] == 'short' || arg[1].nil? || locals.empty? '' else "##{locals[arg[1]].class}" end "#{prefix}#{arg[1] || default}#{kls}" end "(#{my_args.join(', ')})" end # # Builds a formatted string containing information about current method # call in frame number +pos+. # # @param pos [Integer] Frame position. # def frame_call(pos) block, method = frame_block_and_method(pos) block + frame_class(pos) + method + frame_args(pos) end # # Formatted filename in frame number +pos+ # # @param pos [Integer] Frame position. # def frame_file(pos) fullpath = context.frame_file(pos) Setting[:fullpath] ? fullpath : shortpath(fullpath) end # # Line number in frame number +pos+ # # @param pos [Integer] Frame position. # def frame_line(pos) context.frame_line(pos) end # # Properly formatted frame number of frame in position +pos+ # # @param pos [Integer] Frame position. # def frame_pos(pos) format('%-2d', pos) end # # Formatted mark for number of frame in position +pos+. The mark can # contain the current frame symbo (-->), the c_frame symbol (ͱ--) or both # # @param pos [Integer] Frame position. # def frame_mark(pos) mark = frame == pos ? '-->' : ' ' c_frame?(pos) ? mark + ' ͱ--' : mark end # # Checks whether the frame in position +pos+ is a c-frame or not # # @param pos [Integer] Frame position. # def c_frame?(pos) context.frame_binding(pos).nil? end private def shortpath(fullpath) components = Pathname(fullpath).each_filename.to_a return fullpath if components.size <= 2 File.join('...', components[-3..-1]) end def prefix_and_default_from(arg_type) case arg_type when :block return ['&', 'block'] when :rest return ['*', 'args'] else return ['', nil] end end end end byebug-5.0.0/lib/byebug/subcommand_list.rb0000644000076400007640000000125712560626142017571 0ustar pravipravimodule Byebug # # Holds an array of subcommands for a command # class SubcommandList def initialize(commands, parent) @commands = commands @parent = parent end def find(name) @commands.find { |cmd| cmd.match(name) } end def help(name) subcmd = find(name) return errmsg("Unknown subcommand '#{name}'") unless subcmd subcmd.help end def to_s width = @commands.map(&:to_name).max_by(&:size).size formatted_cmds = @commands.map do |subcmd| format("%s %-#{width}s -- %s\n", @parent, subcmd.to_name, subcmd.short_description) end formatted_cmds.join end end end byebug-5.0.0/lib/byebug/attacher.rb0000644000076400007640000000111212560626142016167 0ustar pravipravi# # Main Container for all of Byebug's code # module Byebug # # Enters byebug right before (or right after if _before_ is false) return # events occur. Before entering byebug the init script is read. # def self.attach unless started? self.mode = :attached start run_init_script end current_context.step_out(2, true) end end # # Adds a `byebug` method to the Kernel module. # # Dropping a `byebug` call anywhere in your code, you get a debug prompt. # module Kernel def byebug Byebug.attach end alias_method :debugger, :byebug end byebug-5.0.0/lib/byebug/interface.rb0000644000076400007640000000465012560626142016346 0ustar pravipravirequire 'byebug/history' require 'byebug/helpers/file' # # Namespace for all of byebug's code # module Byebug # # Main Interface class # # Contains common functionality to all implemented interfaces. # class Interface include Helpers::FileHelper attr_accessor :command_queue, :history attr_reader :input, :output, :error def initialize @command_queue = [] @history = History.new end # # Pops a command from the input stream. # def read_command(prompt) return command_queue.shift unless command_queue.empty? cmds = read_input(prompt) return unless cmds command_queue.concat(cmds) command_queue.shift end # # Pushes lines in +filename+ to the command queue. # def read_file(filename) command_queue.concat(get_lines(filename)) end # # Reads a new line from the interface's input stream. # def read_input(prompt, save_hist = true) line = readline(prompt) return unless line history.push(line) if save_hist split_commands(line) end # # Prints an error message to the error stream. # def errmsg(message) error.print("*** #{message}\n") end # # Prints an output message to the output stream. # def puts(message) output.puts(message) end def print(message) output.print(message) end # # Confirms user introduced an affirmative response to the input stream. # def confirm(prompt) readline(prompt) == 'y' end def close end # # Saves or clears history according to +autosave+ setting. # def autosave Setting[:autosave] ? history.save : history.clear end # # Restores history according to +autosave+ setting. # def autorestore history.restore if Setting[:autosave] end private # # Splits a command line of the form "cmd1 ; cmd2 ; ... ; cmdN" into an # array of commands: [cmd1, cmd2, ..., cmdN] # def split_commands(cmd_line) return [''] if cmd_line.empty? cmd_line.split(/;/).each_with_object([]) do |v, m| if m.empty? || m.last[-1] != '\\' m << v next end m.last[-1, 1] = '' m.last << ';' << v end end end end require 'byebug/interfaces/local_interface' require 'byebug/interfaces/script_interface' require 'byebug/interfaces/remote_interface' byebug-5.0.0/lib/byebug/version.rb0000644000076400007640000000004612560626142016066 0ustar pravipravimodule Byebug VERSION = '5.0.0' end byebug-5.0.0/lib/byebug/context.rb0000644000076400007640000000574512560626142016100 0ustar pravipravimodule Byebug # # Mantains context information for the debugger and it's the main # communication point between the library and the C-extension through the # at_breakpoint, at_catchpoint, at_tracing, at_line and at_return callbacks # class Context # # List of files byebug will ignore while debugging # def self.ignored_files Byebug.mode == :standalone ? lib_files + [bin_file] : lib_files end def self.bin_file @bin_file ||= Gem.bin_path('byebug', 'byebug') end def self.lib_files @lib_files ||= Dir.glob(File.expand_path('../../**/*.rb', __FILE__)) end # # Tells whether a file is ignored by the debugger. # # @param path [String] filename to be checked. # def ignored_file?(path) self.class.ignored_files.include?(path) end # # Context's stack size # def stack_size return 0 unless backtrace backtrace.drop_while { |l| ignored_file?(l.first.path) } .take_while { |l| !ignored_file?(l.first.path) } .size end def interrupt step_into 1 end # # Gets local variables for a frame. # # @param frame_no Frame index in the backtrace. Defaults to 0. # # TODO: Use brand new local_variable_{get,set,defined?} for rubies >= 2.1 # def frame_locals(frame_no = 0) bind = frame_binding(frame_no) return [] unless bind bind.eval('local_variables.inject({}){|h, v| h[v] = eval(v.to_s); h}') end # # Gets current method arguments for a frame. # # @param frame_no Frame index in the backtrace. Defaults to 0. # def frame_args(frame_no = 0) bind = frame_binding(frame_no) return c_frame_args(frame_no) unless bind ruby_frame_args(bind) end def handler Byebug.handler || fail('No interface loaded') end def at_breakpoint(brkpnt) handler.at_breakpoint(self, brkpnt) end def at_catchpoint(excpt) handler.at_catchpoint(self, excpt) end def at_tracing(file, line) handler.at_tracing(self, file, line) unless ignored_file?(file) end def at_line(file, line) handler.at_line(self, file, line) unless ignored_file?(file) end def at_return(file, line) handler.at_return(self, file, line) unless ignored_file?(file) end private # # Gets method arguments for a c-frame. # # @param frame_no Frame index in the backtrace. # def c_frame_args(frame_no) myself = frame_self(frame_no) return [] unless myself.to_s != 'main' myself.method(frame_method(frame_no)).parameters end # # Gets method arguments for a ruby-frame. # # @param bind Binding for the ruby-frame. # def ruby_frame_args(bind) return [] unless bind.eval('__method__') bind.eval('method(__method__).parameters') rescue NameError => e Byebug.errmsg \ "Exception #{e.class} (#{e.message}) while retreving frame params" [] end end end byebug-5.0.0/lib/byebug/breakpoint.rb0000644000076400007640000000417112560626142016542 0ustar pravipravimodule Byebug # # Implements breakpoints # class Breakpoint # # First breakpoint, in order of creation # def self.first Byebug.breakpoints.first end # # Last breakpoint, in order of creation # def self.last Byebug.breakpoints.last end # # Adds a new breakpoint # # @param [String] file # @param [Fixnum] line # @param [String] expr # def self.add(file, line, expr = nil) breakpoint = Breakpoint.new(file, line, expr) Byebug.breakpoints << breakpoint breakpoint end # # Removes a breakpoint # # @param [integer] breakpoint number # def self.remove(id) Byebug.breakpoints.reject! { |b| b.id == id } end # # Returns an array of line numbers in file named +filename+ where # breakpoints could be set. The list will contain an entry for each # distinct line event call so it is possible (and possibly useful) for a # line number appear more than once. # # @param filename [String] File name to inspect for possible breakpoints # def self.potential_lines(filename) name = "#{Time.new.to_i}_#{rand(2**31)}" lines = {} iseq = RubyVM::InstructionSequence.compile(File.read(filename), name) iseq.disasm.each_line do |line| res = /^\d+ (?\w+)\s+.+\(\s*(?\d+)\)$/.match(line) next unless res && res[:insn] == 'trace' lines[res[:lineno].to_i] = true end lines.keys end # # Returns true if a breakpoint could be set in line number +lineno+ in file # name +filename. # def self.potential_line?(filename, lineno) potential_lines(filename).member?(lineno) end # # True if there's no breakpoints # def self.none? Byebug.breakpoints.empty? end # # Prints all information associated to the breakpoint # def inspect meths = %w(id pos source expr hit_condition hit_count hit_value enabled?) values = meths.map do |field| "#{field}: #{send(field)}" end.join(', ') "#" end end end byebug-5.0.0/lib/byebug/helpers/0000755000076400007640000000000012560626142015516 5ustar pravipravibyebug-5.0.0/lib/byebug/helpers/toggle.rb0000644000076400007640000000315612560626142017331 0ustar pravipravirequire 'byebug/helpers/parse' module Byebug module Helpers # # Utilities to assist breakpoint/display enabling/disabling. # module ToggleHelper include ParseHelper def enable_disable_breakpoints(is_enable, args) return errmsg(pr('toggle.errors.no_breakpoints')) if Breakpoint.none? all_breakpoints = Byebug.breakpoints.sort_by(&:id) if args.nil? selected_breakpoints = all_breakpoints else selected_ids = [] args.split(/ +/).each do |pos| last_id = all_breakpoints.last.id pos, err = get_int(pos, "#{is_enable} breakpoints", 1, last_id) return errmsg(err) unless pos selected_ids << pos end selected_breakpoints = all_breakpoints.select do |b| selected_ids.include?(b.id) end end selected_breakpoints.each do |b| enabled = ('enable' == is_enable) if enabled && !syntax_valid?(b.expr) return errmsg(pr('toggle.errors.expression', expr: b.expr)) end b.enabled = enabled end end def enable_disable_display(is_enable, args) display = @state.display return errmsg(pr('toggle.errors.no_display')) if 0 == display.size selected_displays = args.nil? ? [1..display.size + 1] : args.split(/ +/) selected_displays.each do |pos| pos, err = get_int(pos, "#{is_enable} display", 1, display.size) return errmsg(err) unless err.nil? display[pos - 1][0] = ('enable' == is_enable) end end end end end byebug-5.0.0/lib/byebug/helpers/string.rb0000644000076400007640000000101712560626142017350 0ustar pravipravimodule Byebug module Helpers # # Utilities for interaction with strings # module StringHelper # # Converts +str+ from an_underscored-or-dasherized_string to # ACamelizedString. # def camelize(str) str.dup.split(/[_-]/).map(&:capitalize).join('') end # # Improves indentation and spacing in +str+ for readability in Byebug's # command prompt. # def prettify(str) "\n" + str.gsub(/^ {6}/, '') + "\n" end end end end byebug-5.0.0/lib/byebug/helpers/eval.rb0000644000076400007640000000234012560626142016771 0ustar pravipravimodule Byebug module Helpers # # Utilities used by the eval command # module EvalHelper # # Run block temporarily ignoring all TracePoint events. # # Used to evaluate stuff within Byebug's prompt. Otherwise, any code # creating new threads won't be properly evaluated because new threads # will get blocked by byebug's main thread. # def allowing_other_threads Byebug.unlock res = yield Byebug.lock res end # # Get current binding and yield it to the given block # def run_with_binding binding = get_binding yield binding end # # Evaluate +expression+ using +binding+ # # @param binding [Binding] Context where to evaluate the expression # @param expression [String] Expression to evaluation # @param stack_on_error [Boolean] Whether to show a stack trace on error. # def eval_with_setting(binding, expression, stack_on_error) allowing_other_threads do if stack_on_error bb_eval(expression, binding) else bb_warning_eval(expression, binding) end end end end end end byebug-5.0.0/lib/byebug/helpers/var.rb0000644000076400007640000000211612560626142016633 0ustar pravipravimodule Byebug module Helpers # # Utilities for variable subcommands # module VarHelper def var_list(ary, b = get_binding) vars = ary.sort.map do |v| s = begin b.eval(v.to_s).inspect rescue begin b.eval(v.to_s).to_s rescue '*Error in evaluation*' end end [v, s] end puts prv(vars, 'instance') end def var_global globals = global_variables.reject do |v| [:$IGNORECASE, :$=, :$KCODE, :$-K, :$binding].include?(v) end var_list(globals) end def var_instance(str) obj = bb_warning_eval(str || 'self') var_list(obj.instance_variables, obj.instance_eval { binding }) end def var_local locals = @state.context.frame_locals cur_self = @state.context.frame_self(@state.frame) locals[:self] = cur_self unless cur_self.to_s == 'main' puts prv(locals.keys.sort.map { |k| [k, locals[k]] }, 'instance') end end end end byebug-5.0.0/lib/byebug/helpers/frame.rb0000644000076400007640000000371212560626142017140 0ustar pravipravimodule Byebug module Helpers # # Utilities to assist frame navigation # module FrameHelper def switch_to_frame(frame_no) frame_no >= 0 ? frame_no : @state.context.stack_size + frame_no end def navigate_to_frame(jump_no) return if jump_no == 0 current_jumps = 0 current_pos = @state.frame loop do current_pos += direction(jump_no) break if current_pos < 0 || current_pos >= @state.context.stack_size next if @state.c_frame?(current_pos) current_jumps += 1 break if current_jumps == jump_no.abs end current_pos end def adjust_frame(frame, absolute) if absolute abs_frame = switch_to_frame(frame) if @state.c_frame?(abs_frame) return errmsg(pr('frame.errors.c_frame')) end else abs_frame = navigate_to_frame(frame) end if abs_frame >= @state.context.stack_size return errmsg(pr('frame.errors.too_low')) elsif abs_frame < 0 return errmsg(pr('frame.errors.too_high')) end @state.frame = abs_frame @state.file = @state.context.frame_file(@state.frame) @state.line = @state.context.frame_line(@state.frame) @state.prev_line = nil end def get_pr_arguments(frame_no) file = @state.frame_file(frame_no) full_path = File.expand_path(file) line = @state.frame_line(frame_no) call = @state.frame_call(frame_no) mark = @state.frame_mark(frame_no) pos = @state.frame_pos(frame_no) { mark: mark, pos: pos, call: call, file: file, line: line, full_path: full_path } end private # # @param [Integer] A positive or negative integer # # @return [Integer] +1 if step is positive / -1 if negative # def direction(step) step / step.abs end end end end byebug-5.0.0/lib/byebug/helpers/thread.rb0000644000076400007640000000300512560626142017310 0ustar pravipravimodule Byebug module Helpers # # Utilities for thread subcommands # module ThreadHelper def display_context(context) puts pr('thread.context', thread_arguments(context)) end def thread_arguments(context) status_flag = if context.suspended? '$' else context.thread == Thread.current ? '+' : ' ' end debug_flag = context.ignored? ? '!' : ' ' if context == Byebug.current_context file_line = "#{@state.file}:#{@state.line}" else backtrace = context.thread.backtrace_locations if backtrace && backtrace[0] file_line = "#{backtrace[0].path}:#{backtrace[0].lineno}" end end { status_flag: status_flag, debug_flag: debug_flag, id: context.thnum, thread: context.thread.inspect, file_line: file_line || '', pid: Process.pid, status: context.thread.status, current: (context.thread == Thread.current) } end def context_from_thread(thnum) ctx = Byebug.contexts.find { |c| c.thnum.to_s == thnum } err = case when ctx.nil? then pr('thread.errors.no_thread') when ctx == @state.context then pr('thread.errors.current_thread') when ctx.ignored? then pr('thread.errors.ignored', arg: thnum) end [ctx, err] end end end end byebug-5.0.0/lib/byebug/helpers/file.rb0000644000076400007640000000206212560626142016762 0ustar pravipravimodule Byebug module Helpers # # Utilities for interaction with files # module FileHelper # # Reads lines of source file +filename+ into an array # def get_lines(filename) File.foreach(filename).reduce([]) { |a, e| a << e.chomp } end # # Reads line number +lineno+ from file named +filename+ # def get_line(filename, lineno) File.open(filename) do |f| f.gets until f.lineno == lineno - 1 f.gets end end # # Returns the number of lines in file +filename+ in a portable, # one-line-at-a-time way. # def n_lines(filename) File.foreach(filename).reduce(0) { |a, _e| a + 1 } end # # Regularize file name. # def normalize(filename) return filename if ['(irb)', '-e'].include?(filename) return File.basename(filename) if Setting[:basename] path = File.expand_path(filename) File.exist?(path) ? File.realpath(path) : filename end end end end byebug-5.0.0/lib/byebug/helpers/parse.rb0000644000076400007640000000332712560626142017162 0ustar pravipravimodule Byebug module Helpers # # Utilities to assist command parsing # module ParseHelper # # Parses +str+ of command +cmd+ as an integer between +min+ and +max+. # # If either +min+ or +max+ is nil, that value has no bound. # # TODO: Remove the `cmd` parameter. It has nothing to do with the methods # purpose. # def get_int(str, cmd, min = nil, max = nil) if str !~ /\A-?[0-9]+\z/ err = pr('parse.errors.int.not_number', cmd: cmd, str: str) return nil, errmsg(err) end int = str.to_i if min && int < min err = pr('parse.errors.int.too_low', cmd: cmd, str: str, min: min) return min, errmsg(err) elsif max && int > max err = pr('parse.errors.int.too_high', cmd: cmd, str: str, max: max) return max, errmsg(err) end int end # # @return true if code is syntactically correct for Ruby, false otherwise # def syntax_valid?(code) return true unless code without_stderr do begin RubyVM::InstructionSequence.compile(code) true rescue SyntaxError false end end end # # Temporarily disable output to $stderr # def without_stderr stderr = $stderr $stderr.reopen(IO::NULL) yield ensure $stderr.reopen(stderr) end # # @return +str+ as an integer or 1 if +str+ is empty. # def parse_steps(str, cmd) return 1 unless str steps, err = get_int(str, cmd, 1) return nil, err unless steps steps end end end end byebug-5.0.0/lib/byebug/processors/0000755000076400007640000000000012560626142016256 5ustar pravipravibyebug-5.0.0/lib/byebug/processors/control_command_processor.rb0000644000076400007640000000204512560626142024061 0ustar pravipravirequire 'byebug/states/control_state' module Byebug # # Processes commands in 'control' mode, when there's no program running # class ControlCommandProcessor < Processor attr_reader :state def initialize(interface = LocalInterface.new) super(interface) end def commands Byebug.commands.select(&:allow_in_control).map { |cmd| cmd.new(state) } end def process_commands @state = ControlState.new(interface) while (input = @interface.read_command(prompt(nil))) cmd = commands.find { |c| c.match(input) } unless cmd errmsg('Unknown command') next end cmd.execute end @interface.close rescue IOError, SystemCallError @interface.close rescue without_exceptions do puts "INTERNAL ERROR!!! #{$ERROR_INFO}" puts $ERROR_INFO.backtrace.map { |l| "\t#{l}" }.join("\n") end end # # Prompt shown before reading a command. # def prompt(_context) '(byebug:ctrl) ' end end end byebug-5.0.0/lib/byebug/processors/command_processor.rb0000644000076400007640000000751312560626142022326 0ustar pravipravirequire 'byebug/states/regular_state' require 'byebug/helpers/file' module Byebug # # Processes commands in regular mode # class CommandProcessor < Processor include Helpers::FileHelper attr_reader :display, :state def initialize(interface = LocalInterface.new) super(interface) @display = [] @last_cmd = nil # To allow empty (just ) commands @context_was_dead = false # Assume we haven't started. end def interface=(interface) @interface.close if @interface @interface = interface end def at_breakpoint(_context, breakpoint) n = Byebug.breakpoints.index(breakpoint) + 1 file = normalize(breakpoint.source) line = breakpoint.pos puts "Stopped by breakpoint #{n} at #{file}:#{line}" end def at_catchpoint(context, excpt) file = normalize(context.frame_file(0)) line = context.frame_line(0) puts "Catchpoint at #{file}:#{line}: `#{excpt}' (#{excpt.class})" end def at_tracing(context, file, line) puts "Tracing: #{normalize(file)}:#{line} #{get_line(file, line)}" always_run(context, file, line, 2) end def at_line(context, file, line) process_commands(context, file, line) end def at_return(context, file, line) process_commands(context, file, line) end private # # Prompt shown before reading a command. # def prompt(context) "(byebug#{context.dead? ? ':post-mortem' : ''}) " end # # Run commands everytime. # # For example display commands or possibly the list or irb in an "autolist" # or "autoirb". # # @return List of commands acceptable to run bound to the current state # def always_run(context, file, line, run_level) @state = RegularState.new(context, @display, file, @interface, line) # Change default when in irb or code included in command line Setting[:autolist] = false if ['(irb)', '-e'].include?(file) # Bind commands to the current state. commands.each { |cmd| cmd.execute if cmd.class.always_run >= run_level } end def commands Byebug.commands.map { |cmd| cmd.new(state) } end # # Handle byebug commands. # def process_commands(context, file, line) always_run(context, file, line, 1) puts 'The program finished.' if program_just_finished?(context) puts(state.location) if Setting[:autolist] == 0 @interface.autorestore repl(context) ensure @interface.autosave end # # Main byebug's REPL # def repl(context) until state.proceed? cmd = @interface.read_command(prompt(context)) return unless cmd next if cmd == '' && @last_cmd.nil? cmd.empty? ? cmd = @last_cmd : @last_cmd = cmd one_cmd(context, cmd) end end # # Autoevals a single command # def one_unknown_cmd(input) unless Setting[:autoeval] return errmsg("Unknown command: \"#{input}\". Try \"help\"") end eval_cmd = EvalCommand.new(state) eval_cmd.match(input) eval_cmd.execute end # # # Executes a single byebug command # def one_cmd(context, input) cmd = match_cmd(input) return one_unknown_cmd(input) unless cmd if context.dead? && !cmd.class.allow_in_post_mortem return errmsg('Command unavailable in post mortem mode.') end cmd.execute end # # Finds a matches the command matching the input # def match_cmd(input) commands.find { |cmd| cmd.match(input) } end # # Returns true first time control is given to the user after program # termination. # def program_just_finished?(context) result = context.dead? && !@context_was_dead @context_was_dead = false if result == true result end end end byebug-5.0.0/lib/byebug/state.rb0000644000076400007640000000030012560626142015512 0ustar pravipravimodule Byebug # # Common parent class for all of Byebug's states # class State attr_reader :interface def initialize(interface) @interface = interface end end end byebug-5.0.0/lib/byebug/setting.rb0000644000076400007640000000265512560626142016066 0ustar pravipravirequire 'byebug/helpers/string' module Byebug # # Parent class for all byebug settings. # class Setting attr_accessor :value DEFAULT = false def initialize @value = self.class::DEFAULT end def boolean? [true, false].include?(value) end def integer? Integer(value) ? true : false rescue ArgumentError false end def help prettify(banner) end def to_sym name = self.class.name.gsub(/^Byebug::/, '').gsub(/Setting$/, '') name.gsub(/(.)([A-Z])/, '\1_\2').downcase.to_sym end def to_s "#{to_sym} is #{value ? 'on' : 'off'}\n" end class << self def settings @settings ||= {} end def [](name) settings[name].value end def []=(name, value) settings[name].value = value end def find(shortcut) abbr = shortcut =~ /^no/ ? shortcut[2..-1] : shortcut matches = settings.select do |key, value| value.boolean? ? key =~ /#{abbr}/ : key =~ /#{shortcut}/ end matches.size == 1 ? matches.values.first : nil end def help_all output = " List of settings supported in byebug:\n --\n" width = settings.keys.max_by(&:size).size settings.values.each do |sett| output << format(" %-#{width}s -- %s\n", sett.to_sym, sett.banner) end output + "\n" end end end end byebug-5.0.0/lib/byebug/remote.rb0000644000076400007640000000500212560626142015671 0ustar pravipravirequire 'socket' module Byebug # Port number used for remote debugging PORT = 8989 unless defined?(PORT) class << self # If in remote mode, wait for the remote connection attr_accessor :wait_connection # The actual port that the server is started at attr_accessor :actual_port attr_reader :actual_control_port # # Interrupts the current thread # def interrupt current_context.interrupt end # # Starts a remote byebug # def start_server(host = nil, port = PORT) return if @thread handler.interface = nil start start_control(host, port == 0 ? 0 : port + 1) yield if block_given? mutex = Mutex.new proceed = ConditionVariable.new server = TCPServer.new(host, port) self.actual_port = server.addr[1] @thread = DebugThread.new do while (session = server.accept) handler.interface = RemoteInterface.new(session) mutex.synchronize { proceed.signal } if wait_connection end end mutex.synchronize { proceed.wait(mutex) } if wait_connection end def start_control(host = nil, ctrl_port = PORT + 1) return @actual_control_port if @control_thread server = TCPServer.new(host, ctrl_port) @actual_control_port = server.addr[1] @control_thread = DebugThread.new do while (session = server.accept) handler.interface = RemoteInterface.new(session) ControlCommandProcessor.new(handler.interface).process_commands end end @actual_control_port end # # Connects to the remote byebug # def start_client(host = 'localhost', port = PORT) handler.interface = LocalInterface.new puts 'Connecting to byebug server...' socket = TCPSocket.new(host, port) puts 'Connected.' catch(:exit) do while (line = socket.gets) case line when /^PROMPT (.*)$/ input = handler.interface.read_command(Regexp.last_match[1]) throw :exit unless input socket.puts input when /^CONFIRM (.*)$/ input = handler.interface.confirm(Regexp.last_match[1]) throw :exit unless input socket.puts input else puts line end end end socket.close end def parse_host_and_port(host_port_spec) location = host_port_spec.split(':') location[1] ? [location[0], location[1].to_i] : ['localhost', location[0]] end end end byebug-5.0.0/lib/byebug/printers/0000755000076400007640000000000012560626142015722 5ustar pravipravibyebug-5.0.0/lib/byebug/printers/plain.rb0000644000076400007640000000260312560626142017353 0ustar pravipravirequire 'byebug/printers/base' module Byebug module Printers class Plain < Base include Columnize def print(path, args = {}) message = translate(locate(path), args) tail = parts(path).include?('confirmations') ? ' (y/n) ' : "\n" message << tail end def print_collection(path, collection, &block) modifier = get_modifier(path) lines = array_of_args(collection, &block).map do |args| print(path, args) end if modifier == 'c' columnize(lines.map { |l| l.gsub(/\n$/, '') }, Setting[:width]) else lines.join('') end end def print_variables(variables, *_) print_collection('variable.variable', variables) do |(key, value), _| value = value.nil? ? 'nil' : value.to_s if "#{key} = #{value}".size > Setting[:width] key_size = "#{key} = ".size value = value[0..Setting[:width] - key_size - 4] + '...' end { key: key, value: value } end end private def get_modifier(path) modifier_regexp = /\|(\w+)$/ modifier_match = locate(path).match(modifier_regexp) modifier_match && modifier_match[1] end def contents_files [File.expand_path(File.join('..', 'texts', 'plain.yml'), __FILE__)] + super end end end end byebug-5.0.0/lib/byebug/printers/base.rb0000644000076400007640000000317012560626142017162 0ustar pravipravirequire 'yaml' module Byebug module Printers class Base class MissedPath < StandardError; end class MissedArgument < StandardError; end SEPARATOR = '.' def type self.class.name.split('::').last.downcase end private def locate(path) result = nil contents.each do |_, contents| result = parts(path).reduce(contents) do |r, part| r && r.key?(part) ? r[part] : nil end break if result end fail MissedPath, "Can't find part path '#{path}'" unless result result end def translate(string, args = {}) # they may contain #{} string interpolation string.gsub(/\|\w+$/, '').gsub(/([^#]?){([^}]*)}/) do key = Regexp.last_match[2].to_s unless args.key?(key.to_sym) fail MissedArgument, "Missed argument #{key} for '#{string}'" end "#{Regexp.last_match[1]}#{args[key.to_sym]}" end end def parts(path) path.split(SEPARATOR) end def contents @contents ||= contents_files.each_with_object({}) do |filename, hash| hash[filename] = YAML.load_file(filename) || {} end end def array_of_args(collection, &block) collection_with_index = collection.each.with_index collection_with_index.each_with_object([]) do |(item, index), array| args = block.call(item, index) array << args if args end end def contents_files [File.expand_path(File.join('..', 'texts', 'base.yml'), __FILE__)] end end end end byebug-5.0.0/lib/byebug/printers/texts/0000755000076400007640000000000012560626142017071 5ustar pravipravibyebug-5.0.0/lib/byebug/printers/texts/plain.yml0000644000076400007640000000135412560626142020722 0ustar pravipravibreak: created: "Successfully created breakpoint with id {id}" display: result: "{n}: {exp} = {result}" eval: exception: "{text_message}" result: "{result}" frame: line: "{mark} #{pos} {call} at {file}:{line}" method: methods: "{name}|c" restart: success: "Re exec'ing:\n\t{cmd}" thread: context: "{status_flag}{debug_flag}{id} {thread} {file_line}" trace: messages: success: "Tracing global variable \"{var}\"." on_change: "traced global variable '{name}' has value '{value}'" undo: "Not tracing global variable \"{var}\" anymore." errors: var_is_not_global: "'{name}' is not a global variable." needs_global_variable: "tracevar needs a global variable name" variable: variable: "{key} = {value}" byebug-5.0.0/lib/byebug/printers/texts/base.yml0000644000076400007640000000651112560626142020531 0ustar pravipravibase: errors: only_local: 'Command is available only in local mode.' break: errors: line: "Line {line} is not a valid breakpoint in file {file}" location: "Invalid breakpoint location" state: "We are not in a state that has an associated file" class: "Unknown class {klass}" far_line: "There are only {lines} lines in file {file}" source: "No file named {file}" expression: "Incorrect expression \"{expr}\"; breakpoint disabled" no_breakpoint: "Invalid breakpoint id. Use \"info breakpoint\" to find out the correct id" no_breakpoint_delete: "No breakpoint number {pos}" not_changed: "Incorrect expression \"{expr}\", breakpoint not changed" confirmations: delete_all: "Delete all breakpoints?" catch: catching: "Catching exception {exception}." errors: off: "Off expected. Got {off}" not_class: "Warning {class} is not known to be a Class" removed: "Catch for exception {exception} removed" not_found: "Catch for exception {exception} not found" confirmations: delete_all: "Delete all catchpoints? (y or n) " condition: errors: no_breakpoints: "No breakpoints have been set" continue: errors: unstopped_line: "Line {line} is not a valid stopping point in file" display: confirmations: clear_all: "Clear all expressions?" errors: undefined: "Display expression {expr} is not defined" edit: errors: state: "We are not in a state that has an associated file" file_line: "Invalid file[:line] number specification: {file_line}" not_readable: "File {file} is not readable." not_exist: "File {file} does not exist." frame: errors: too_low: "Can't navigate beyond the oldest frame" too_high: "Can't navigate beyond the newest frame" c_frame: "Can't navigate to c-frame" help: errors: undefined: "Undefined command: {cmd}. Try: help" info: errors: undefined_file: "{file} is not a valid source file" pry: errors: not_installed: 'You need to install pry in order to run this command' quit: confirmations: really: "Really quit?" save: messages: done: "Saved to '{path}'" set: errors: unknown_setting: "Unknown setting :{key}" must_specify_value: "You must specify a value for setting :{key}" on_off: "Expecting 'on', 1, true, 'off', 0, false. Got: {arg}." show: errors: unknown_setting: "Unknown setting :{key}" source: errors: not_found: "File \"{file}\" not found" not_available: "Source commmand not available at this time" thread: errors: no_thread: "No such thread" current_thread: "It's the current thread" wrong_action: "Can't {subcmd} thread {arg}" already_running: "Already running" toggle: errors: no_breakpoints: "No breakpoints have been set" no_display: "No display expressions have been set" syntax: "\"{toggle}\" must be followed by \"display\", \"breakpoints\" or breakpoint ids" expression: "Expression \"{expr}\" syntactically incorrect; breakpoint remains disabled." parse: errors: int: too_low: "\"{cmd}\" argument \"{str}\" needs to be at least {min}" too_high: "\"{cmd}\" argument \"{str}\" needs to be at most {max}" not_number: "\"{cmd}\" argument \"{str}\" needs to be a number" variable: errors: not_module: "Should be Class/Module: {object}" cant_get_class_vars: "can't get class variables here.\n" byebug-5.0.0/lib/byebug/processor.rb0000644000076400007640000000162712560626142016426 0ustar pravipravirequire 'forwardable' module Byebug class Processor attr_accessor :interface extend Forwardable def_delegators :@interface, :errmsg, :puts def initialize(interface) @interface = interface end def without_exceptions yield rescue nil end def self.load_commands Dir.glob(File.expand_path('../commands/*.rb', __FILE__)).each do |file| require file end end def self.load_settings Dir.glob(File.expand_path('../settings/*.rb', __FILE__)).each do |file| require file end Byebug.constants.grep(/[a-z]Setting/).map do |name| setting = Byebug.const_get(name).new Byebug::Setting.settings[setting.to_sym] = setting end end end Processor.load_commands Processor.load_settings end require 'byebug/processors/command_processor' require 'byebug/processors/control_command_processor' byebug-5.0.0/lib/byebug/interfaces/0000755000076400007640000000000012560626142016177 5ustar pravipravibyebug-5.0.0/lib/byebug/interfaces/local_interface.rb0000644000076400007640000000110112560626142021627 0ustar pravipravimodule Byebug # # Interface class for standard byebug use. # class LocalInterface < Interface def initialize super() @input = STDIN @output = STDOUT @error = STDERR end # # Reads a single line of input using Readline. If Ctrl-C is pressed in the # middle of input, the line is reset to only the prompt and we ask for input # again. # # @param prompt Prompt to be displayed. # def readline(prompt) Readline.readline(prompt, false) rescue Interrupt puts('^C') retry end end end byebug-5.0.0/lib/byebug/interfaces/script_interface.rb0000644000076400007640000000112512560626142022047 0ustar pravipravimodule Byebug # # Interface class for command execution from script files. # class ScriptInterface < Interface def initialize(file, verbose = false) super() @input = File.open(file) @output = verbose ? STDOUT : StringIO.new @error = verbose ? STDERR : StringIO.new end def read_command(prompt) readline(prompt, false) end def close input.close end def readline(*) while (result = input.gets) output.puts "+ #{result}" next if result =~ /^\s*#/ return result.chomp end end end end byebug-5.0.0/lib/byebug/interfaces/remote_interface.rb0000644000076400007640000000120012560626142022030 0ustar pravipravirequire 'byebug/history' module Byebug # # Interface class for remote use of byebug. # class RemoteInterface < Interface def initialize(socket) super() @input = socket @output = socket @error = socket end def read_command(prompt) super("PROMPT #{prompt}") end def confirm(prompt) super("CONFIRM #{prompt}") end def close output.close rescue IOError errmsg('Error closing the interface...') end def readline(prompt) output.puts(prompt) result = input.gets fail IOError unless result result.chomp end end end byebug-5.0.0/lib/byebug/interfaces/test_interface.rb0000644000076400007640000000176012560626142021527 0ustar pravipravimodule Byebug # # Custom interface for easier assertions # class TestInterface < Interface attr_accessor :test_block def initialize super() @input = [] @output = [] @error = [] end def errmsg(message) error.concat(message.to_s.split("\n")) end def print(message) output.concat(message.to_s.split("\n")) end def puts(message) output.concat(message.to_s.split("\n")) end def read_command(prompt) cmd = super(prompt) return cmd unless cmd.nil? && test_block test_block.call self.test_block = nil end def clear @input = [] @output = [] @error = [] history.clear end def inspect [ 'Input:', input.join("\n"), 'Output:', output.join("\n"), 'Error:', error.join("\n") ].join("\n") end def readline(prompt) puts(prompt) cmd = input.shift cmd.is_a?(Proc) ? cmd.call : cmd end end end byebug-5.0.0/lib/byebug/command.rb0000644000076400007640000000507612560626142016027 0ustar pravipravirequire 'columnize' require 'forwardable' require 'byebug/helpers/string' module Byebug # # Parent class of all byebug commands. # # Subclasses need to implement a `regexp` method and an `execute` method. # class Command extend Forwardable include Helpers::StringHelper def initialize(state) @match = nil @state = state end def match(input) @match = regexp.match(input) end def_delegators :'self.class', :to_name, :description # # Default help text for a command. # def help prettify(description) end def_delegator :"Byebug.printer", :print, :pr def_delegator :"Byebug.printer", :print_collection, :prc def_delegator :"Byebug.printer", :print_variables, :prv protected def_delegators :@state, :errmsg, :puts, :print, :confirm # # Evaluates a string containing Ruby code, using binding +b+. In case of # error full stack trace and error are printed. # def bb_eval(str, b = get_binding) b.eval(str) rescue StandardError, ScriptError => e at = e.backtrace locations = [] locations << "#{at.shift}: #{e.class} Exception(#{e.message})" locations += at.map { |path| "\tfrom #{path}" } errmsg(pr('eval.exception', text_message: locations.join("\n"))) nil end # # Evaluates a string containing Ruby code, using binding +b+. In case of # error, an error message with the exception is printed. # def bb_warning_eval(str, b = get_binding) b.eval(str) rescue StandardError, ScriptError => e text_message = "#{e.class} Exception: #{e.message}" errmsg(pr('eval.exception', text_message: text_message)) nil end def get_binding(pos = @state.frame) @state.context ? @state.context.frame_binding(pos) : TOPLEVEL_BINDING end class << self attr_accessor :allow_in_control attr_writer :allow_in_post_mortem, :always_run def allow_in_post_mortem !defined?(@allow_in_post_mortem) ? true : false end def always_run @always_run ||= 0 end # # Name of the command, as executed by the user. # def to_name name.gsub(/^Byebug::/, '').gsub(/Command$/, '').downcase end # # Available subcommands for the current command # # A subcommand is any class defined inside the parent's command class # def subcommands const_list = constants(false).map { |const| const_get(const, false) } const_list.select { |c| c.is_a?(Class) } end end end end byebug-5.0.0/lib/byebug/commands/0000755000076400007640000000000012560626142015655 5ustar pravipravibyebug-5.0.0/lib/byebug/commands/interrupt.rb0000644000076400007640000000072612560626142020243 0ustar pravipravirequire 'byebug/command' module Byebug # # Interrupting execution of current thread. # class InterruptCommand < Command self.allow_in_control = true self.allow_in_post_mortem = false def regexp /^\s*i(?:nterrupt)?\s*$/ end def execute context = Byebug.thread_context(Thread.main) context.interrupt end def description <<-EOD i[nterrupt] Interrupts the program. EOD end end end byebug-5.0.0/lib/byebug/commands/where.rb0000644000076400007640000000201212560626142017307 0ustar pravipravi# encoding: utf-8 require 'pathname' require 'byebug/command' require 'byebug/helpers/frame' module Byebug # # Show current backtrace. # class WhereCommand < Command include Helpers::FrameHelper def regexp /^\s* (?:w(?:here)?|bt|backtrace) \s*$/x end def execute print_backtrace end def description <<-EOD w[here]|bt|backtrace Display stack frames. Print the entire stack frame. Each frame is numbered; the most recent frame is 0. A frame number can be referred to in the "frame" command. "up" and "down" add or subtract respectively to frame numbers shown. The position of the current frame is marked with -->. C-frames hang from their most immediate Ruby frame to indicate that they are not navigable. EOD end private def print_backtrace bt = prc('frame.line', (0...@state.context.stack_size)) do |_, index| get_pr_arguments(index) end print(bt) end end end byebug-5.0.0/lib/byebug/commands/untracevar.rb0000644000076400007640000000116112560626142020353 0ustar pravipravirequire 'byebug/command' module Byebug # # Stop tracing a global variable. # class UntracevarCommand < Command self.allow_in_post_mortem = false def regexp /^\s* untr(?:acevar)? (?:\s+ (\S+))? \s*$/x end def execute var = @match[1] if global_variables.include?(:"#{var}") untrace_var(:"#{var}") puts pr('trace.messages.undo', var: var) else errmsg pr('trace.errors.not_global', var: var) end end def description <<-EOD untr[acevar] Stop tracing global variable . EOD end end end byebug-5.0.0/lib/byebug/commands/kill.rb0000644000076400007640000000146212560626142017140 0ustar pravipravirequire 'byebug/command' module Byebug # # Send custom signals to the debugged program. # class KillCommand < Command self.allow_in_control = true def regexp /^\s* (?:kill) \s* (?:\s+(\S+))? \s*$/x end def execute if @match[1] signame = @match[1] unless Signal.list.member?(signame) errmsg("signal name #{signame} is not a signal I know about\n") return false end @state.interface.close if 'KILL' == signame else return unless confirm('Really kill? (y/n) ') signame = 'KILL' end Process.kill(signame, Process.pid) end def description <<-EOD kill[ signal] Send [signal] to Process.pid Equivalent of Process.kill(Process.pid) EOD end end end byebug-5.0.0/lib/byebug/commands/history.rb0000644000076400007640000000117612560626142017710 0ustar pravipravirequire 'byebug/command' require 'byebug/helpers/parse' module Byebug # # Show history of byebug commands. # class HistoryCommand < Command include Helpers::ParseHelper def regexp /^\s* hist(?:ory)? (?:\s+(?.+))? \s*$/x end def execute history = @state.interface.history if @match[:num_cmds] size, = get_int(@match[:num_cmds], 'history', 1, history.size) return errmsg(err) unless size end puts history.to_s(size) end def description <<-EOD hist[ory] [num_cmds] Show byebug's command history. EOD end end end byebug-5.0.0/lib/byebug/commands/continue.rb0000644000076400007640000000167512560626142020037 0ustar pravipravirequire 'byebug/command' require 'byebug/helpers/parse' module Byebug # # Implements the continue command. # # Allows the user to continue execution until the next stopping point, a # specific line number or until program termination. # class ContinueCommand < Command include Helpers::ParseHelper def regexp /^\s* c(?:ont(?:inue)?)? (?:\s+(\S+))? \s*$/x end def execute if @match[1] num, err = get_int(@match[1], 'Continue', 0, nil) return errmsg(err) unless num filename = File.expand_path(@state.file) unless Breakpoint.potential_line?(filename, num) return errmsg(pr('continue.errors.unstopped_line', line: num)) end Breakpoint.add(filename, num) end @state.proceed end def description <<-EOD c[ont[inue]][ ] Run until program ends, hits a breakpoint or reaches line . EOD end end end byebug-5.0.0/lib/byebug/commands/quit.rb0000644000076400007640000000135112560626142017164 0ustar pravipravirequire 'byebug/command' module Byebug # # Exit from byebug. # class QuitCommand < Command self.allow_in_control = true def regexp /^\s* q(?:uit)? \s* (?:(!|\s+unconditionally))? \s*$/x end def execute return unless @match[1] || confirm(pr('quit.confirmations.really')) @state.interface.autosave @state.interface.close exit! # exit -> exit!: No graceful way to stop... end def description <<-EOD q[uit] [!|unconditionally] Exits from byebug. Normally we prompt before exiting. However if the parameter "unconditionally" is given or command is suffixed with !, we exit without asking further questions. EOD end end end byebug-5.0.0/lib/byebug/commands/source.rb0000644000076400007640000000145312560626142017505 0ustar pravipravirequire 'byebug/command' module Byebug # # Execute a file containing byebug commands. # # It can be used to restore a previously saved debugging session. # class SourceCommand < Command self.allow_in_control = true def regexp /^\s* so(?:urce)? (?:\s+(\S+))? \s*$/x end def execute return puts(help) unless @match[1] unless @state && @state.interface return errmsg(pr('source.errors.not_available')) end file = File.expand_path(@match[1]).strip unless File.exist?(file) return errmsg(pr('source.errors.not_found', file: file)) end @state.interface.read_file(file) end def description <<-EOD source Executes file containing byebug commands. EOD end end end byebug-5.0.0/lib/byebug/commands/save.rb0000644000076400007640000000265312560626142017146 0ustar pravipravirequire 'byebug/command' module Byebug # # Save current settings to use them in another debug session. # class SaveCommand < Command self.allow_in_control = true def regexp /^\s* sa(?:ve)? (?:\s+(\S+))? \s*$/x end def execute file = File.open(@match[1] || Setting[:savefile], 'w') save_breakpoints(file) save_catchpoints(file) save_displays(file) save_settings(file) print pr('save.messages.done', path: file.path) file.close end def description <<-EOD save[ FILE] Saves current byebug state to FILE as a script file. This includes breakpoints, catchpoints, display expressions and some settings. If no filename is given, we will fabricate one. Use the "source" command in another debug session to restore them. EOD end private def save_breakpoints(file) Byebug.breakpoints.each do |b| file.puts "break #{b.source}:#{b.pos}#{" if #{b.expr}" if b.expr}" end end def save_catchpoints(file) Byebug.catchpoints.keys.each do |c| file.puts "catch #{c}" end end def save_displays(file) @state.display.each { |d| file.puts "display #{d[1]}" if d[0] } end def save_settings(file) %w(autoeval autoirb autolist basename).each do |setting| file.puts "set #{setting} #{Setting[setting.to_sym]}" end end end end byebug-5.0.0/lib/byebug/commands/tracevar.rb0000644000076400007640000000245612560626142020020 0ustar pravipravirequire 'byebug/command' module Byebug # # Show (and possibily stop) at every line that changes a global variable. # class TracevarCommand < Command self.allow_in_post_mortem = false def regexp /^\s* tr(?:acevar)? (?: \s+ (\S+))? # (variable-name)? (?: \s+ (stop|nostop))? \s*$/x end def execute var = @match[1] return errmsg(pr('trace.errors.needs_global_variable')) unless var unless global_variables.include?(:"#{var}") return errmsg(pr('trace.errors.var_is_not_global', name: var)) end stop = @match[2] && @match[2] !~ /nostop/ instance_eval do trace_var(:"#{var}") { |val| on_change(var, val, stop) } end puts pr('trace.messages.success', var: var) end def on_change(name, value, stop) puts pr('trace.messages.on_change', name: name, value: value) @state.context.step_out(1, false) if stop end def description <<-EOD tr[acevar] [[no]stop] Start tracing variable . If "stop" is specified, execution will stop every time the variable changes its value. If nothing or "nostop" is specified, execution won't stop, changes will just be logged in byebug's output. EOD end end end byebug-5.0.0/lib/byebug/commands/set.rb0000644000076400007640000000317312560626142017001 0ustar pravipravirequire 'byebug/command' require 'byebug/helpers/parse' module Byebug # # Change byebug settings. # class SetCommand < Command include Helpers::ParseHelper self.allow_in_control = true def regexp /^\s* set (?:\s+(?\w+))? (?:\s+(?\S+))? \s*$/x end def execute key = @match[:setting] value = @match[:value] return puts(help) if key.nil? && value.nil? setting = Setting.find(key) return errmsg(pr('set.errors.unknown_setting', key: key)) unless setting if !setting.boolean? && value.nil? err = pr('set.errors.must_specify_value', key: key) elsif setting.boolean? value, err = get_onoff(value, key =~ /^no/ ? false : true) elsif setting.integer? value, err = get_int(value, setting.to_sym, 1) end return errmsg(err) if value.nil? setting.value = value puts setting.to_s end def get_onoff(arg, default) return default if arg.nil? case arg when '1', 'on', 'true' true when '0', 'off', 'false' false else [nil, pr('set.errors.on_off', arg: arg)] end end def help description + Setting.help_all end def description <<-EOD set Modifies parts of byebug environment. Boolean values take "on", "off", "true", "false", "1" or "0". If you don't specify a value, the boolean setting will be enabled. Conversely, you can use "set no" to disable them. You can see these environment settings with the "show" command. EOD end end end byebug-5.0.0/lib/byebug/commands/putl.rb0000644000076400007640000000155312560626142017172 0ustar pravipravirequire 'pp' require 'byebug/command' require 'byebug/helpers/eval' module Byebug # # Evaluation, pretty printing and columnizing from byebug's prompt. # class PutlCommand < Command include Helpers::EvalHelper include Columnize self.allow_in_control = true def regexp /^\s* putl (?:\s+ (.+))? \s*$/x end def execute out = StringIO.new run_with_binding do |b| res = eval_with_setting(b, @match[1], Setting[:stack_on_error]) if res.is_a?(Array) puts "#{columnize(res.map(&:to_s), Setting[:width])}" else PP.pp(res, out) puts out.string end end rescue out.puts $ERROR_INFO.message end def description <<-EOD putl Evaluates , an array, and columnize its value. EOD end end end byebug-5.0.0/lib/byebug/commands/method.rb0000644000076400007640000000204112560626142017457 0ustar pravipravirequire 'byebug/command' module Byebug # # Show methods of specific classes/modules/objects. # class MethodCommand < Command include Columnize def regexp /^\s* m(?:ethod)? \s+ (i(:?nstance)?\s+)?/x end def execute obj = bb_eval(@match.post_match) result = if @match[1] prc('method.methods', obj.methods.sort) { |item, _| { name: item } } elsif !obj.is_a?(Module) pr('variable.errors.not_module', object: @match.post_match) else prc('method.methods', obj.instance_methods(false).sort) do |item, _| { name: item } end end puts result end def description <<-EOD m[ethod] (i[nstance][ ]|) When invoked with "instance", shows instance methods of the object specified as argument or of self no object was specified. When invoked only with a class or module, shows class methods of the class or module specified as argument. EOD end end end byebug-5.0.0/lib/byebug/commands/pry.rb0000644000076400007640000000103412560626142017012 0ustar pravipravirequire 'byebug/command' module Byebug # # Enter Pry from byebug's prompt # class PryCommand < Command def regexp /^\s* pry \s*$/x end def execute unless @state.interface.is_a?(LocalInterface) return errmsg(pr('base.errors.only_local')) end begin require 'pry' rescue LoadError errmsg(pr('pry.errors.not_installed')) end get_binding.pry end def description <<-EOD pry Starts a Pry session. EOD end end end byebug-5.0.0/lib/byebug/commands/eval.rb0000644000076400007640000000160612560626142017134 0ustar pravipravirequire 'English' require 'byebug/command' require 'byebug/helpers/eval' module Byebug # # Evaluation of expressions from byebug's prompt. # class EvalCommand < Command include Helpers::EvalHelper def match(input) @input = input super end def regexp /^\s* e(?:val)? \s+/x end def execute expr = @match ? @match.post_match : @input run_with_binding do |b| res = eval_with_setting(b, expr, Setting[:stack_on_error]) print pr('eval.result', expr: expr, result: res.inspect) end rescue puts "#{$ERROR_INFO.class} Exception: #{$ERROR_INFO.message}" end def description <<-EOD e[val] Evaluates and prints its value. * NOTE - unknown input is automatically evaluated, to turn this off use 'set noautoeval'. EOD end end end byebug-5.0.0/lib/byebug/commands/show.rb0000644000076400007640000000131412560626142017161 0ustar pravipravirequire 'byebug/command' module Byebug # # Show byebug settings. # class ShowCommand < Command self.allow_in_control = true def regexp /^\s* show (?:\s+(?\w+))? \s*$/x end def execute key = @match[:setting] return puts(help) unless key setting = Setting.find(key) return errmsg(pr('show.errors.unknown_setting', key: key)) unless setting puts Setting.settings[setting.to_sym] end def help description + Setting.help_all end def description <<-EOD show Generic command for showing byebug settings. You can change them with the "set" command. EOD end end end byebug-5.0.0/lib/byebug/commands/var.rb0000644000076400007640000000100712560626142016770 0ustar pravipravirequire 'byebug/subcommands' require 'byebug/commands/var/all' require 'byebug/commands/var/const' require 'byebug/commands/var/instance' require 'byebug/commands/var/local' require 'byebug/commands/var/global' module Byebug # # Shows variables and its values # class VarCommand < Command include Subcommands def regexp /^\s* v(?:ar)? (?:\s+ (.+))? \s*$/x end def description <<-EOD [v]ar Shows variables and its values. EOD end end end byebug-5.0.0/lib/byebug/commands/condition.rb0000644000076400007640000000243112560626142020170 0ustar pravipravirequire 'byebug/command' require 'byebug/helpers/parse' module Byebug # # Implements conditions on breakpoints. # # Adds the ability to stop on breakpoints only under certain conditions. # class ConditionCommand < Command include Helpers::ParseHelper self.allow_in_post_mortem = false def regexp /^\s* cond(?:ition)? (?:\s+(\d+)(?:\s+(.*))?)? \s*$/x end def execute return puts(help) unless @match[1] breakpoints = Byebug.breakpoints.sort_by(&:id) return errmsg(pr('condition.errors.no_breakpoints')) if breakpoints.empty? pos, err = get_int(@match[1], 'Condition', 1) return errmsg(err) if err breakpoint = breakpoints.find { |b| b.id == pos } return errmsg(pr('break.errors.no_breakpoint')) unless breakpoint unless syntax_valid?(@match[2]) return errmsg(pr('break.errors.not_changed', expr: @match[2])) end breakpoint.expr = @match[2] end def description <<-EOD cond[ition] [ expr] Specify breakpoint number to break only if is true. is an integer and is an expression to be evaluated whenever breakpoint is reached. If no expression is specified, the condition is removed. EOD end end end byebug-5.0.0/lib/byebug/commands/disable/0000755000076400007640000000000012560626142017260 5ustar pravipravibyebug-5.0.0/lib/byebug/commands/disable/breakpoints.rb0000644000076400007640000000153012560626142022125 0ustar pravipravirequire 'byebug/helpers/toggle' module Byebug # # Reopens the +disable+ command to define the +breakpoints+ subcommand # class DisableCommand < Command # # Disables all or specific breakpoints # class BreakpointsSubcommand < Command include Helpers::ToggleHelper def regexp /^\s* b(?:reakpoints)? (?:\s+ (.+))? \s*$/x end def execute enable_disable_breakpoints('disable', @match[1]) end def short_description 'Disable all or specific breakpoints.' end def description <<-EOD dis[able] b[reakpoints][ .. ] #{short_description} Give breakpoint numbers (separated by spaces) as arguments or no argument at all if you want to disable every breakpoint. EOD end end end end byebug-5.0.0/lib/byebug/commands/disable/display.rb0000644000076400007640000000162712560626142021260 0ustar pravipravirequire 'byebug/helpers/toggle' module Byebug # # Reopens the +disable+ command to define the +display+ subcommand # class DisableCommand < Command # # Enables all or specific displays # class DisplaySubcommand < Command include Helpers::ToggleHelper def regexp /^\s* d(?:isplay)? (?:\s+ (.+))? \s*$/x end def execute enable_disable_display('disable', @match[1]) end def short_description 'Disables expressions to be displayed when program stops.' end def description <<-EOD dis[able] d[isplay][ .. ] #{short_description} Arguments are the code numbers of the expressions to disable. Do "info display" to see the current list of code numbers. If no arguments are specified, all displays are disabled. EOD end end end end byebug-5.0.0/lib/byebug/commands/help.rb0000644000076400007640000000166212560626142017137 0ustar pravipravirequire 'byebug/command' module Byebug # # Ask for help from byebug's prompt. # class HelpCommand < Command self.allow_in_control = true def regexp /^\s* h(?:elp)? (?:\s+(\S+))? (?:\s+(\S+))? \s*$/x end def execute return puts(help) unless @match[1] cmd = Byebug.commands.find { |c| c.to_name == @match[1] } return errmsg(pr('help.errors.undefined', cmd: @match[1])) unless cmd cmd = cmd.new(@state) return puts(cmd.help) unless @match[2] subcmd = cmd.subcommands.find(@match[2]) return errmsg(pr('help.errors.undefined', cmd: @match[2])) unless subcmd puts(subcmd.help) end def description <<-EOD h[elp][ [ ]] help -- prints this help. help -- prints help on command . help -- prints help on 's subcommand . EOD end end end byebug-5.0.0/lib/byebug/commands/info/0000755000076400007640000000000012560626142016610 5ustar pravipravibyebug-5.0.0/lib/byebug/commands/info/line.rb0000644000076400007640000000112012560626142020056 0ustar pravipravimodule Byebug # # Reopens the +info+ command to define the +line+ subcommand # class InfoCommand < Command # # Information about current location # class LineSubcommand < Command def regexp /^\s* l(?:ine)? \s*$/x end def execute puts "Line #{@state.line} of \"#{@state.file}\"" end def short_description 'Line number and file name of current position in source file.' end def description <<-EOD inf[o] l[ine] #{short_description} EOD end end end end byebug-5.0.0/lib/byebug/commands/info/breakpoints.rb0000644000076400007640000000271412560626142021462 0ustar pravipravimodule Byebug # # Reopens the +info+ command to define the +breakpoints+ subcommand # class InfoCommand < Command # # Information about current breakpoints # class BreakpointsSubcommand < Command def regexp /^\s* b(?:reakpoints)? (?:\s+ (.+))? \s*$/x end def execute return puts('No breakpoints.') if Byebug.breakpoints.empty? breakpoints = Byebug.breakpoints.sort_by(&:id) if @match[1] indices = @match[1].split(/ +/).map(&:to_i) breakpoints = breakpoints.select { |b| indices.member?(b.id) } if breakpoints.empty? return errmsg('No breakpoints found among list given') end end puts 'Num Enb What' breakpoints.each { |b| info_breakpoint(b) } end def short_description 'Status of user settable breakpoints.' end def description <<-EOD inf[o] b[reakpoints] #{short_description} EOD end private def info_breakpoint(brkpt) expr = brkpt.expr.nil? ? '' : " if #{brkpt.expr}" y_n = brkpt.enabled? ? 'y' : 'n' interp = format('%-3d %-3s at %s:%s%s', brkpt.id, y_n, brkpt.source, brkpt.pos, expr) puts interp hits = brkpt.hit_count return unless hits > 0 s = (hits > 1) ? 's' : '' puts "\tbreakpoint already hit #{hits} time#{s}" end end end end byebug-5.0.0/lib/byebug/commands/info/program.rb0000644000076400007640000000222412560626142020604 0ustar pravipravimodule Byebug # # Reopens the +info+ command to define the +args+ subcommand # class InfoCommand < Command # # Information about arguments of the current method/block # class ProgramSubcommand < Command def regexp /^\s* p(?:rogram)? \s*$/x end def execute if @state.context.dead? puts 'The program crashed.' excpt = Byebug.last_exception return puts("Exception: #{excpt.inspect}") if excpt end puts 'Program stopped. ' format_stop_reason @state.context.stop_reason end def short_description 'Information about the current status of the debugged program.' end def description <<-EOD inf[o] p[rogram] #{short_description} EOD end private def format_stop_reason(stop_reason) case stop_reason when :step puts "It stopped after stepping, next'ing or initial start." when :breakpoint puts 'It stopped at a breakpoint.' when :catchpoint puts 'It stopped at a catchpoint.' end end end end end byebug-5.0.0/lib/byebug/commands/info/display.rb0000644000076400007640000000161412560626142020604 0ustar pravipravimodule Byebug # # Reopens the +info+ command to define the +display+ subcommand # class InfoCommand < Command # # Information about display expressions # class DisplaySubcommand < Command def regexp /^\s* d(?:isplay)? \s*$/x end def execute display = @state.display unless display.find { |d| d[0] } return puts('There are no auto-display expressions now.') end puts 'Auto-display expressions now in effect:' puts 'Num Enb Expression' display.each_with_index do |d, i| puts(format('%3d: %s %s', i + 1, d[0] ? 'y' : 'n', d[1])) end end def short_description 'List of expressions to display when program stops' end def description <<-EOD inf[o] d[display] #{short_description} EOD end end end end byebug-5.0.0/lib/byebug/commands/info/catch.rb0000644000076400007640000000160712560626142020223 0ustar pravipravimodule Byebug # # Reopens the +info+ command to define the +catch+ subcommand # class InfoCommand < Command # # Information on exceptions that can be caught by the debugger # class CatchSubcommand < Command def regexp /^\s* c(?:atch)? (?:\s+ (.+))? \s*$/x end def execute return puts('No frame selected.') unless @state.context if Byebug.catchpoints && !Byebug.catchpoints.empty? Byebug.catchpoints.each do |exception, _hits| puts("#{exception}: #{exception.is_a?(Class)}") end else puts 'No exceptions set to be caught.' end end def short_description 'Exceptions that can be caught in the current stack frame' end def description <<-EOD inf[o] c[atch] #{short_description} EOD end end end end byebug-5.0.0/lib/byebug/commands/info/args.rb0000644000076400007640000000153012560626142020070 0ustar pravipravimodule Byebug # # Reopens the +info+ command to define the +args+ subcommand # class InfoCommand < Command # # Information about arguments of the current method/block # class ArgsSubcommand < Command def regexp /^\s* a(?:rgs)? \s*$/x end def execute locals = @state.context.frame_locals args = @state.context.frame_args return if args == [[:rest]] args.map do |_, name| s = "#{name} = #{locals[name].inspect}" s[Setting[:width] - 3..-1] = '...' if s.size > Setting[:width] puts s end end def short_description 'Information about arguments of the current method/block' end def description <<-EOD inf[o] a[args] #{short_description} EOD end end end end byebug-5.0.0/lib/byebug/commands/info/file.rb0000644000076400007640000000347512560626142020065 0ustar pravipravirequire 'byebug/helpers/file' module Byebug # # Reopens the +info+ command to define the +file+ subcommand # class InfoCommand < Command # # Information about a particular source file # class FileSubcommand < Command include Helpers::FileHelper def regexp /^\s* f(?:ile)? (?:\s+ (\S+))? \s*$/x end def execute file = @match[1] || @state.file unless File.exist?(file) return errmsg(pr('info.errors.undefined_file', file: file)) end puts <<-EOC.gsub(/^ {6}/, '') File #{info_file_basic(file)} Breakpoint line numbers: #{info_file_breakpoints(file)} Modification time: #{info_file_mtime(file)} Sha1 Signature: #{info_file_sha1(file)} EOC end def short_description 'Information about a particular source file.' end def description <<-EOD inf[o] f[ile] #{short_description} It informs about file name, number of lines, possible breakpoints in the file, last modification time and sha1 digest. EOD end private def info_file_basic(file) path = File.expand_path(file) return unless File.exist?(path) s = n_lines(path) == 1 ? '' : 's' "#{path} (#{n_lines(path)} line#{s})" end def info_file_breakpoints(file) breakpoints = Breakpoint.potential_lines(file) return unless breakpoints breakpoints.to_a.sort.columnize(line_prefix: ' ', displaywidth: Setting[:width]) end def info_file_mtime(file) File.stat(file).mtime end def info_file_sha1(file) require 'digest/sha1' Digest::SHA1.hexdigest(file) end end end end byebug-5.0.0/lib/byebug/commands/up.rb0000644000076400007640000000136012560626142016626 0ustar pravipravi# encoding: utf-8 require 'pathname' require 'byebug/command' require 'byebug/helpers/frame' require 'byebug/helpers/parse' module Byebug # # Move the current frame up in the backtrace. # class UpCommand < Command include Helpers::FrameHelper include Helpers::ParseHelper def regexp /^\s* u(?:p)? (?:\s+(\S+))? \s*$/x end def execute pos, err = parse_steps(@match[1], 'Up') return errmsg(err) unless pos adjust_frame(pos, false) ListCommand.new(@state).execute if Setting[:autolist] end def description <<-EOD up[ count] Move to a higher frame in the stack trace. Use the "bt" command to find out where you want to go. EOD end end end byebug-5.0.0/lib/byebug/commands/ps.rb0000644000076400007640000000160312560626142016624 0ustar pravipravirequire 'English' require 'pp' require 'byebug/command' require 'byebug/helpers/eval' module Byebug # # Evaluation, pretty printing, columnizing and sorting from byebug's prompt # class PsCommand < Command include Helpers::EvalHelper include Columnize self.allow_in_control = true def regexp /^\s* ps \s+/x end def execute out = StringIO.new run_with_binding do |b| res = eval_with_setting(b, @match.post_match, Setting[:stack_on_error]) if res.is_a?(Array) puts "#{columnize(res.map(&:to_s).sort!, Setting[:width])}" else PP.pp(res, out) puts out.string end end rescue out.puts $ERROR_INFO.message end def description <<-EOD ps Evaluates , an array, sort and columnize its value. EOD end end end byebug-5.0.0/lib/byebug/commands/var/0000755000076400007640000000000012560626142016445 5ustar pravipravibyebug-5.0.0/lib/byebug/commands/var/const.rb0000644000076400007640000000150512560626142020121 0ustar pravipravimodule Byebug # # Reopens the +var+ command to define the +const+ subcommand # class VarCommand < Command # # Shows constants # class ConstSubcommand < Command def regexp /^\s* c(?:onst)? (?:\s+ (.+))? \s*$/x end def execute str_obj = @match[1] || 'self.class' obj = bb_warning_eval(str_obj) unless obj.is_a?(Module) return errmsg(pr('variable.errors.not_module', object: str_obj)) end constants = bb_eval("#{str_obj}.constants") puts prv(constants.sort.map { |c| [c, obj.const_get(c)] }, 'constant') end def short_description 'Shows constants of an object.' end def description <<-EOD v[ar] c[onstant] #{short_description} EOD end end end end byebug-5.0.0/lib/byebug/commands/var/local.rb0000644000076400007640000000113012560626142020057 0ustar pravipravirequire 'byebug/helpers/var' module Byebug # # Reopens the +var+ command to define the +local+ subcommand # class VarCommand < Command # # Shows local variables in current scope # class LocalSubcommand < Command include Helpers::VarHelper def regexp /^\s* l(?:ocal)? \s*$/x end def execute var_local end def short_description 'Shows local variables in current scope.' end def description <<-EOD v[ar] l[ocal] #{short_description} EOD end end end end byebug-5.0.0/lib/byebug/commands/var/instance.rb0000644000076400007640000000121412560626142020574 0ustar pravipravirequire 'byebug/helpers/var' module Byebug # # Reopens the +var+ command to define the +instance+ subcommand # class VarCommand < Command # # Shows instance variables # class InstanceSubcommand < Command include Helpers::VarHelper def regexp /^\s* i(?:nstance)? (?:\s+ (.+))? \s*$/x end def execute var_instance(@match[1]) end def short_description 'Shows instance variables of self or a specific object.' end def description <<-EOD v[ar] i[nstance][ ] #{short_description} EOD end end end end byebug-5.0.0/lib/byebug/commands/var/global.rb0000644000076400007640000000103712560626142020233 0ustar pravipravimodule Byebug # # Reopens the +var+ command to define the +global+ subcommand # class VarCommand < Command # # Shows global variables # class GlobalSubcommand < Command include Helpers::VarHelper def regexp /^\s* g(?:lobal)? \s*$/x end def execute var_global end def short_description 'Shows global variables.' end def description <<-EOD v[ar] g[lobal] #{short_description} EOD end end end end byebug-5.0.0/lib/byebug/commands/var/all.rb0000644000076400007640000000122012560626142017535 0ustar pravipravirequire 'byebug/helpers/var' module Byebug # # Reopens the +var+ command to define the +all+ subcommand # class VarCommand < Command # # Shows global, instance and local variables # class AllSubcommand < Command include Helpers::VarHelper def regexp /^\s* a(?:ll)? \s*$/x end def execute var_global var_instance('self') var_local end def short_description 'Shows local, global and instance variables of self.' end def description <<-EOD v[ar] a[ll] #{short_description} EOD end end end end byebug-5.0.0/lib/byebug/commands/enable.rb0000644000076400007640000000075412560626142017436 0ustar pravipravirequire 'byebug/subcommands' require 'byebug/commands/enable/breakpoints' require 'byebug/commands/enable/display' module Byebug # # Enabling custom display expressions or breakpoints. # class EnableCommand < Command include Subcommands def regexp /^\s* en(?:able)? (?:\s+ (.+))? \s*$/x end def description <<-EOD en[able][[ b[reakpoints]| d[isplay])][ n1[ n2[ ...[ nn]]]]] Enables breakpoints or displays. EOD end end end byebug-5.0.0/lib/byebug/commands/pp.rb0000644000076400007640000000141712560626142016624 0ustar pravipravirequire 'English' require 'pp' require 'byebug/command' require 'byebug/helpers/eval' module Byebug # # Evaluation and pretty printing from byebug's prompt. # class PpCommand < Command include Helpers::EvalHelper self.allow_in_control = true def regexp /^\s* pp \s+/x end def execute out = StringIO.new run_with_binding do |b| if Setting[:stack_on_error] PP.pp(bb_eval(@match.post_match, b), out) else PP.pp(bb_warning_eval(@match.post_match, b), out) end end puts out.string rescue out.puts $ERROR_INFO.message end def description <<-EOD pp Evaluates and pretty-prints its value. EOD end end end byebug-5.0.0/lib/byebug/commands/disable.rb0000644000076400007640000000142712560626142017611 0ustar pravipravirequire 'byebug/subcommands' require 'byebug/commands/disable/breakpoints' require 'byebug/commands/disable/display' module Byebug # # Disabling custom display expressions or breakpoints. # class DisableCommand < Command include Subcommands def regexp /^\s* dis(?:able)? (?:\s+ (.+))? \s*$/x end def description <<-EOD dis[able][[ breakpoints| display)][ n1[ n2[ ...[ nn]]]]] Disables breakpoints or displays. "disable" by itself shows this help "disable breakpoints" disables all breakpoints. "disable displays" disables all displays. You can also specify a space separated list of breakpoint or display numbers to disable only specific breakpoints or displays. EOD end end end byebug-5.0.0/lib/byebug/commands/display.rb0000644000076400007640000000231212560626142017645 0ustar pravipravirequire 'byebug/command' module Byebug # # Custom expressions to be displayed every time the debugger stops. # class DisplayCommand < Command self.allow_in_post_mortem = false def self.always_run 2 end def regexp /^\s* disp(?:lay)? (?:\s+ (.+))? \s*$/x end def execute return print_display_expressions unless @match && @match[1] @state.display.push [true, @match[1]] display_expression(@match[1]) end def description <<-EOD disp[lay][ ] If specified, adds into display expression list. Otherwise, it lists all expressions. EOD end private def display_expression(exp) print pr('display.result', n: @state.display.size, exp: exp, result: bb_warning_eval(exp).inspect) end def print_display_expressions result = prc('display.result', @state.display) do |item, index| is_active, expression = item if is_active { n: index + 1, exp: expression, result: bb_warning_eval(expression).inspect } end end print result end end end byebug-5.0.0/lib/byebug/commands/thread/0000755000076400007640000000000012560626142017124 5ustar pravipravibyebug-5.0.0/lib/byebug/commands/thread/stop.rb0000644000076400007640000000143712560626142020443 0ustar pravipravirequire 'byebug/helpers/thread' module Byebug # # Reopens the +thread+ command to define the +stop+ subcommand # class ThreadCommand < Command # # Stops the specified thread # class StopSubcommand < Command include Helpers::ThreadHelper def regexp /^\s* st(?:op)? (?: \s* (\d+))? \s*$/x end def execute return puts(help) unless @match[1] context, err = context_from_thread(@match[1]) return errmsg(err) if err context.suspend display_context(context) end def short_description 'Stops the execution of the specified thread' end def description <<-EOD th[read] st[op] #{short_description} EOD end end end end byebug-5.0.0/lib/byebug/commands/thread/switch.rb0000644000076400007640000000150312560626142020751 0ustar pravipravirequire 'byebug/helpers/thread' module Byebug # # Reopens the +thread+ command to define the +switch+ subcommand # class ThreadCommand < Command # # Switches to the specified thread # class SwitchSubcommand < Command include Helpers::ThreadHelper def regexp /^\s* sw(?:itch)? (?: \s* (\d+))? \s*$/x end def execute return puts(help) unless @match[1] context, err = context_from_thread(@match[1]) return errmsg(err) if err display_context(context) context.switch @state.proceed end def short_description 'Switches execution to the specified thread' end def description <<-EOD th[read] sw[itch] #{short_description} EOD end end end end byebug-5.0.0/lib/byebug/commands/thread/current.rb0000644000076400007640000000117412560626142021136 0ustar pravipravirequire 'byebug/helpers/thread' module Byebug # # Reopens the +thread+ command to define the +current+ subcommand # class ThreadCommand < Command # # Information about the current thread # class CurrentSubcommand < Command include Helpers::ThreadHelper def regexp /^\s* c(?:urrent)? \s*$/x end def execute display_context(@state.context) end def short_description 'Shows current thread information' end def description <<-EOD th[read] c[urrent] #{short_description} EOD end end end end byebug-5.0.0/lib/byebug/commands/thread/resume.rb0000644000076400007640000000162212560626142020752 0ustar pravipravirequire 'byebug/helpers/thread' module Byebug # # Reopens the +thread+ command to define the +resume+ subcommand # class ThreadCommand < Command # # Resumes the specified thread # class ResumeSubcommand < Command include Helpers::ThreadHelper def regexp /^\s* r(?:esume)? (?: \s* (\d+))? \s*$/x end def execute return puts(help) unless @match[1] context, err = context_from_thread(@match[1]) return errmsg(err) if err unless context.suspended? return errmsg(pr('thread.errors.already_running')) end context.resume display_context(context) end def short_description 'Resumes execution of the specified thread' end def description <<-EOD th[read] r[esume] #{short_description} EOD end end end end byebug-5.0.0/lib/byebug/commands/thread/list.rb0000644000076400007640000000137512560626142020432 0ustar pravipravirequire 'byebug/helpers/thread' module Byebug # # Reopens the +thread+ command to define the +list+ subcommand # class ThreadCommand < Command # # Information about threads # class ListSubcommand < Command include Helpers::ThreadHelper def regexp /^\s* l(?:ist)? \s*$/x end def execute contexts = Byebug.contexts.sort_by(&:thnum) thread_list = prc('thread.context', contexts) do |context, _| thread_arguments(context) end print(thread_list) end def short_description 'Lists all threads' end def description <<-EOD th[read] l[ist] #{short_description} EOD end end end end byebug-5.0.0/lib/byebug/commands/down.rb0000644000076400007640000000136512560626142017156 0ustar pravipravi# encoding: utf-8 require 'pathname' require 'byebug/command' require 'byebug/helpers/frame' require 'byebug/helpers/parse' module Byebug # # Move the current frame down in the backtrace. # class DownCommand < Command include Helpers::FrameHelper include Helpers::ParseHelper def regexp /^\s* down (?:\s+(\S+))? \s*$/x end def execute pos, err = parse_steps(@match[1], 'Down') return errmsg(err) unless pos adjust_frame(-pos, false) ListCommand.new(@state).execute if Setting[:autolist] end def description <<-EOD down[ count] Move to a lower frame in the stack trace. Use the "bt" command to find out where you want to go. EOD end end end byebug-5.0.0/lib/byebug/commands/break.rb0000644000076400007640000000416312560626142017272 0ustar pravipravirequire 'byebug/command' require 'byebug/helpers/file' require 'byebug/helpers/parse' module Byebug # # Implements breakpoint functionality # class BreakCommand < Command include Helpers::FileHelper include Helpers::ParseHelper self.allow_in_post_mortem = false self.allow_in_control = true def regexp /^\s* b(?:reak)? (?:\s+ (\S+))? (?:\s+ if \s+(.+))? \s*$/x end def execute return puts(help) unless @match[1] b = line_breakpoint(@match[1]) || method_breakpoint(@match[1]) if syntax_valid?(@match[2]) return puts(pr('break.created', id: b.id, file: b.source, line: b.pos)) end errmsg(pr('break.errors.expression', expr: @match[2])) b.enabled = false rescue => e errmsg(e.message) end def short_description 'Set breakpoint to some position, (optionally) if expr == true' end def description <<-EOD b[reak] [file:]line [if expr] b[reak] [module::...]class(.|#)method [if expr] #{short_description} EOD end private def line_breakpoint(loc) line = loc.match(/^(\d+)$/) file_line = loc.match(/^([^:]+):(\d+)$/) return nil unless line || file_line f, l = line ? [@state.file, line[1]] : [file_line[1], file_line[2]] check_errors(f, l.to_i) Breakpoint.add(File.expand_path(f), l.to_i, @match[2]) end def method_breakpoint(location) location.match(/([^.#]+)[.#](.+)/) do |match| k = bb_warning_eval(match[1]) m = match[2] klass = k && k.is_a?(Module) ? k.name : match[1] method = m.intern Breakpoint.add(klass, method, @match[2]) end end def check_errors(file, line) path = File.expand_path(file) deco_path = normalize(file) fail(pr('break.errors.source', file: deco_path)) unless File.exist?(path) if line > n_lines(file) fail(pr('break.errors.far_line', lines: n_lines(file), file: deco_path)) end return if Breakpoint.potential_line?(path, line) fail(pr('break.errors.line', file: deco_path, line: line)) end end end byebug-5.0.0/lib/byebug/commands/step.rb0000644000076400007640000000137412560626142017162 0ustar pravipravirequire 'byebug/command' require 'byebug/helpers/parse' module Byebug # # Implements the step functionality. # # Allows the user the continue execution until the next instruction, possibily # in a different frame. Use step to step into method calls or blocks. # class StepCommand < Command include Helpers::ParseHelper self.allow_in_post_mortem = false def regexp /^\s* s(?:tep)? (?:\s+(\S+))? \s*$/x end def execute steps, err = parse_steps(@match[1], 'Steps') return errmsg(err) unless steps @state.context.step_into(steps, @state.frame) @state.proceed end def description <<-EOD s[tep][ nnn] Steps (into methods) once or nnn times. EOD end end end byebug-5.0.0/lib/byebug/commands/frame.rb0000644000076400007640000000234712560626142017302 0ustar pravipravi# encoding: utf-8 require 'pathname' require 'byebug/command' require 'byebug/helpers/frame' require 'byebug/helpers/parse' module Byebug # # Move to specific frames in the backtrace. # class FrameCommand < Command include Helpers::FrameHelper include Helpers::ParseHelper def regexp /^\s* f(?:rame)? (?:\s+(\S+))? \s*$/x end def execute unless @match[1] print(pr('frame.line', get_pr_arguments(@state.frame))) return end pos, err = get_int(@match[1], 'Frame') return errmsg(err) unless pos adjust_frame(pos, true) end def description <<-EOD f[rame][ frame-number] Move the current frame to the specified frame number, or the 0 if no frame-number has been given. A negative number indicates position from the other end, so "frame -1" moves to the oldest frame, and "frame 0" moves to the newest frame. Without an argument, the command prints the current stack frame. Since the current position is redisplayed, it may trigger a resyncronization if there is a front end also watching over things. Use the "bt" command to find out where you want to go. EOD end end end byebug-5.0.0/lib/byebug/commands/list.rb0000644000076400007640000000746012560626142017164 0ustar pravipravirequire 'byebug/command' require 'byebug/helpers/file' require 'byebug/helpers/parse' module Byebug # # List parts of the source code. # class ListCommand < Command include Helpers::FileHelper include Helpers::ParseHelper def regexp /^\s* l(?:ist)? (?:\s*([-=])|\s+(\S+))? \s*$/x end def execute exist = File.exist?(@state.file) return errmsg "No sourcefile available for #{@state.file}\n" unless exist @match ||= match('list') max_lines = n_lines(@state.file) b, e = range(@match[2], max_lines) return errmsg('Invalid line range') unless valid_range?(b, e, max_lines) display_lines(b, e) @state.prev_line = b end def description <<-EOD l[ist][[-=]][ nn-mm] Lists lines of code forward from current line or from the place where code was last listed. If "list-" is specified, lists backwards instead. If "list=" is specified, lists from current line regardless of where code was last listed. A line range can also be specified to list specific sections of code. EOD end private # # Line range to be printed by `list`. # # If is set, range is parsed from it. # # Otherwise it's automatically chosen. # def range(input, max_line) size = [Setting[:listsize], max_line].min return set_range(size, max_line) unless input parse_range(input, size, max_line) end def valid_range?(first, last, max) first <= last && (1..max).include?(first) && (1..max).include?(last) end # # Set line range to be printed by list # # @param size - number of lines to be printed # @param max_line - max line number that can be printed # # @return first line number to list # @return last line number to list # def set_range(size, max_line) first = amend(lower(size, @match[1] || '+'), max_line - size + 1) [first, move(first, size - 1)] end def parse_range(input, size, max_line) first, err = get_int(lower_bound(input), 'List', 1, max_line) return [-1, -1] if err if upper_bound(input) last, = get_int(upper_bound(input), 'List', 1, max_line) return [-1, -1] unless last last = amend(last, max_line) else first -= (size / 2) end [first, last || move(first, size - 1)] end def amend(line, max_line) return 1 if line < 1 [max_line, line].min end def lower(size, direction = '+') return @state.line - size / 2 if direction == '=' || !@state.prev_line move(@state.prev_line, size, direction) end def move(line, size, direction = '+') line.send(direction, size) end # # Show lines in @state.file from line number to line number . # def display_lines(min, max) puts "\n[#{min}, #{max}] in #{@state.file}" File.foreach(@state.file).with_index do |line, lineno| break if lineno + 1 > max next unless (min..max).include?(lineno + 1) mark = lineno + 1 == @state.line ? '=> ' : ' ' puts format("#{mark}%#{max.to_s.size}d: %s", lineno + 1, line) end end private # # @param range [String] A string with an integer range format # # @return [String] The lower bound of the given range # def lower_bound(range) split_range(range)[0] end # # @param range [String] A string with an integer range format # # @return [String] The upper bound of the given range # def upper_bound(range) split_range(range)[1] end # # @param range [String] A string with an integer range format # # @return [Array] The upper & lower bounds of the given range # def split_range(str) str.split(/[-,]/) end end end byebug-5.0.0/lib/byebug/commands/finish.rb0000644000076400007640000000212312560626142017460 0ustar pravipravirequire 'byebug/command' require 'byebug/helpers/parse' module Byebug # # Implements the finish functionality. # # Allows the user to continue execution until certain frames are finished. # class FinishCommand < Command include Helpers::ParseHelper self.allow_in_post_mortem = false def regexp /^\s* fin(?:ish)? (?:\s+(\S+))? \s*$/x end def execute max_frames = @state.context.stack_size - @state.frame if @match[1] n_frames, err = get_int(@match[1], 'finish', 0, max_frames - 1) return errmsg(err) unless n_frames else n_frames = 1 end force = n_frames == 0 ? true : false @state.context.step_out(@state.frame + n_frames, force) @state.frame = 0 @state.proceed end def description <<-EOD fin[ish][ n_frames] Execute until frame returns. If no number is given, we run until the current frame returns. If a number of frames `n_frames` is given, then we run until `n_frames` return from the current position. EOD end end end byebug-5.0.0/lib/byebug/commands/enable/0000755000076400007640000000000012560626142017103 5ustar pravipravibyebug-5.0.0/lib/byebug/commands/enable/breakpoints.rb0000644000076400007640000000150212560626142021747 0ustar pravipravirequire 'byebug/helpers/toggle' module Byebug # # Reopens the +enable+ command to define the +breakpoints+ subcommand # class EnableCommand < Command # # Enables all or specific breakpoints # class BreakpointsSubcommand < Command include Helpers::ToggleHelper def regexp /^\s* b(?:reakpoints)? (?:\s+ (.+))? \s*$/x end def execute enable_disable_breakpoints('enable', @match[1]) end def short_description 'Disable all or specific breakpoints' end def description <<-EOD en[able] b[reakpoints][ ] #{short_description} Give breakpoint numbers (separated by spaces) as arguments or no argument at all if you want to enable every breakpoint. EOD end end end end byebug-5.0.0/lib/byebug/commands/enable/display.rb0000644000076400007640000000162012560626142021074 0ustar pravipravirequire 'byebug/helpers/toggle' module Byebug # # Reopens the +enable+ command to define the +display+ subcommand # class EnableCommand < Command # # Enables all or specific displays # class DisplaySubcommand < Command include Helpers::ToggleHelper def regexp /^\s* d(?:isplay)? (?:\s+ (.+))? \s*$/x end def execute enable_disable_display('enable', @match[1]) end def short_description 'Enables expressions to be displayed when program stops.' end def description <<-EOD en[able] d[isplay][ .. ] #{short_description} Arguments are the code numbers of the expressions to enable. Do "info display" to see the current list of code numbers. If no arguments are specified, all displays are enabled. EOD end end end end byebug-5.0.0/lib/byebug/commands/catch.rb0000644000076400007640000000253512560626142017271 0ustar pravipravirequire 'byebug/command' module Byebug # # Implements exception catching. # # Enables the user to catch unhandled assertion when they happen. # class CatchCommand < Command def regexp /^\s* cat(?:ch)? (?:\s+(\S+))? (?:\s+(off))? \s*$/x end def execute ex = @match[1] return info_catch unless ex cmd = @match[2] unless cmd if 'off' == ex Byebug.catchpoints.clear if confirm(pr('catch.confirmations.delete_all')) return end is_class = bb_eval("#{ex.is_a?(Class)}") puts pr('catch.errors.not_class', class: ex) unless is_class Byebug.add_catchpoint(ex) return puts pr('catch.catching', exception: ex) end if cmd == 'off' exists = Byebug.catchpoints.member?(ex) return errmsg pr('catch.errors.not_found', exception: ex) unless exists Byebug.catchpoints.delete(ex) return errmsg pr('catch.errors.removed', exception: ex) end errmsg pr('catch.errors.off', off: cmd) end def description <<-EOD cat[ch][ (off|[ off])] "catch" lists catchpoints. "catch off" deletes all catchpoints. "catch " enables handling . "catch off" disables handling . EOD end end end byebug-5.0.0/lib/byebug/commands/undisplay.rb0000644000076400007640000000225312560626142020214 0ustar pravipravirequire 'byebug/command' require 'byebug/helpers/parse' module Byebug # # Remove expressions from display list. # class UndisplayCommand < Command include Helpers::ParseHelper self.allow_in_post_mortem = false def regexp /^\s* undisp(?:lay)? (?:\s+(\S+))? \s*$/x end def execute if @match[1] pos, err = get_int(@match[1], 'Undisplay', 1, @state.display.size) return errmsg(err) unless err.nil? unless @state.display[pos - 1] return errmsg(pr('display.errors.undefined', expr: pos)) end @state.display[pos - 1][0] = nil else return unless confirm(pr('display.confirmations.clear_all')) @state.display.each { |d| d[0] = false } end end def description <<-EOD undisp[lay][ nnn] Cancel some expressions to be displayed when program stops. Arguments are the code numbers of the expressions to stop displaying. No argument means cancel all automatic-display expressions. "delete display" has the same effect as this command. Do "info display" to see the current list of code numbers. EOD end end end byebug-5.0.0/lib/byebug/commands/edit.rb0000644000076400007640000000230712560626142017131 0ustar pravipravirequire 'byebug/command' module Byebug # # Edit a file from byebug's prompt. # class EditCommand < Command self.allow_in_control = true def regexp /^\s* ed(?:it)? (?:\s+(\S+))? \s*$/x end def execute if !@match[1] return errmsg(pr('edit.errors.state')) unless @state.file file = @state.file line = @state.line if @state.line elsif (@pos_match = /([^:]+)[:]([0-9]+)/.match(@match[1])) file, line = @pos_match.captures else file = @match[1] end editor = ENV['EDITOR'] || 'vim' file = File.expand_path(file) unless File.exist?(file) return errmsg(pr('edit.errors.not_exist', file: file)) end unless File.readable?(file) return errmsg(pr('edit.errors.not_readable', file: file)) end cmd = line ? "#{editor} +#{line} #{file}" : "#{editor} #{file}" system(cmd) end def description <<-EOD edit[ file:lineno] Edit specified files. With no argument, edits file containing most recent line listed. Editing targets can also be specified to start editing at a specific line in a specific file. EOD end end end byebug-5.0.0/lib/byebug/commands/next.rb0000644000076400007640000000127012560626142017160 0ustar pravipravirequire 'byebug/command' require 'byebug/helpers/parse' module Byebug # # Implements the next functionality. # # Allows the user the continue execution until the next instruction in the # current frame. # class NextCommand < Command include Helpers::ParseHelper self.allow_in_post_mortem = false def regexp /^\s* n(?:ext)? (?:\s+(\S+))? \s*$/x end def execute steps, err = parse_steps(@match[1], 'Next') return errmsg(err) unless steps @state.context.step_over(steps, @state.frame) @state.proceed end def description <<-EOD n[ext][ nnn] Steps over once or nnn times. EOD end end end byebug-5.0.0/lib/byebug/commands/restart.rb0000644000076400007640000000152412560626142017670 0ustar pravipravirequire 'byebug/command' module Byebug # # Restart debugged program from within byebug. # class RestartCommand < Command self.allow_in_control = true def regexp /^\s* (?:restart|R) (?:\s+(?.+))? \s*$/x end def execute if Byebug.mode == :standalone cmd = "#{Gem.bin_path('byebug', 'byebug')} #{$PROGRAM_NAME}" else cmd = $PROGRAM_NAME end if @match[:args] cmd += " #{@match[:args]}" else require 'shellwords' cmd += " #{$ARGV.compact.shelljoin}" end puts pr('restart.success', cmd: cmd) exec(cmd) end def description <<-EOD restart|R [args] Restart the program. This is a re-exec - all byebug state is lost. If command arguments are passed those are used. EOD end end end byebug-5.0.0/lib/byebug/commands/thread.rb0000644000076400007640000000103412560626142017447 0ustar pravipravirequire 'byebug/subcommands' require 'byebug/commands/thread/current' require 'byebug/commands/thread/list' require 'byebug/commands/thread/resume' require 'byebug/commands/thread/stop' require 'byebug/commands/thread/switch' module Byebug # # Manipulation of Ruby threads # class ThreadCommand < Command include Subcommands def regexp /^\s* th(?:read)? (?:\s+ (.+))? \s*$/x end def description <<-EOD th]read Commands to manipulate threads. EOD end end end byebug-5.0.0/lib/byebug/commands/irb.rb0000644000076400007640000000071712560626142016763 0ustar pravipravirequire 'byebug/command' require 'irb' module Byebug # # Enter IRB from byebug's prompt # class IrbCommand < Command def regexp /^\s* irb \s*$/x end def execute unless @state.interface.is_a?(LocalInterface) return errmsg(pr('base.errors.only_local')) end IRB.start(__FILE__) end def description <<-EOD irb Starts an Interactive Ruby (IRB) session. EOD end end end byebug-5.0.0/lib/byebug/commands/info.rb0000644000076400007640000000127012560626142017135 0ustar pravipravirequire 'byebug/subcommands' require 'byebug/commands/info/args' require 'byebug/commands/info/breakpoints' require 'byebug/commands/info/catch' require 'byebug/commands/info/display' require 'byebug/commands/info/file' require 'byebug/commands/info/line' require 'byebug/commands/info/program' module Byebug # # Shows info about different aspects of the debugger. # class InfoCommand < Command include Subcommands self.allow_in_control = true def regexp /^\s* i(?:nfo)? (?:\s+ (.+))? \s*$/x end def description <<-EOD info[ subcommand] Generic command for showing things about the program being debugged. EOD end end end byebug-5.0.0/lib/byebug/commands/delete.rb0000644000076400007640000000172012560626142017444 0ustar pravipravirequire 'byebug/command' require 'byebug/helpers/parse' module Byebug # # Implements breakpoint deletion. # class DeleteCommand < Command include Helpers::ParseHelper self.allow_in_post_mortem = false self.allow_in_control = true def regexp /^\s* del(?:ete)? (?:\s+(.*))?$/x end def execute unless @match[1] if confirm(pr('break.confirmations.delete_all')) Byebug.breakpoints.clear end return nil end @match[1].split(/ +/).each do |number| pos, err = get_int(number, 'Delete', 1) return errmsg(err) unless pos unless Breakpoint.remove(pos) return errmsg(pr('break.errors.no_breakpoint_delete', pos: pos)) end end end def description <<-EOD del[ete][ nnn...] Without and argument, deletes all breakpoints. With integer arguments, it deletes specific breakpoints. EOD end end end byebug-5.0.0/lib/byebug/core.rb0000644000076400007640000000436512560626142015341 0ustar pravipravirequire 'byebug/byebug' require 'byebug/version' require 'byebug/context' require 'byebug/breakpoint' require 'byebug/interface' require 'byebug/processor' require 'byebug/remote' require 'byebug/printers/plain' module Byebug extend self # # Configuration file used for startup commands. Default value is .byebugrc # INIT_FILE = '.byebugrc' unless defined?(INIT_FILE) # # Main debugger's processor # attr_accessor :handler self.handler = CommandProcessor.new extend Forwardable def_delegators :handler, :errmsg, :puts # # Main debugger's printer # attr_accessor :printer self.printer = Printers::Plain.new # # Running mode of the debugger. Can be either: # # * :attached => Attached to a running program through the `byebug` method. # * :standalone => Started through `bin/byebug` script. # attr_accessor :mode # # Runs normal byebug initialization scripts. # # Reads and executes the commands from init file (if any) in the current # working directory. This is only done if the current directory is different # from your home directory. Thus, you can have more than one init file, one # generic in your home directory, and another, specific to the program you # are debugging, in the directory where you invoke byebug. # def run_init_script home_rc = File.expand_path(File.join(ENV['HOME'].to_s, INIT_FILE)) run_script(home_rc) if File.exist?(home_rc) cwd_rc = File.expand_path(File.join('.', INIT_FILE)) run_script(cwd_rc) if File.exist?(cwd_rc) && cwd_rc != home_rc end # # A Byebug command is a class defined right under the Byebug module and # named Command # def commands const_list = constants.map { |const| const_get(const, false) } const_list.select { |c| c.is_a?(Class) && c.name =~ /[a-z]Command$/ } end private # # Runs a script file # def run_script(file, verbose = false) interface = ScriptInterface.new(file, verbose) processor = ControlCommandProcessor.new(interface) processor.process_commands end end # # Extends the extension class to be able to pass information about the # debugging environment from the c-extension to the user. # class Exception attr_reader :__bb_file, :__bb_line, :__bb_binding, :__bb_context end byebug-5.0.0/ext/0000755000076400007640000000000012560626142012631 5ustar pravipravibyebug-5.0.0/ext/byebug/0000755000076400007640000000000012560626142014106 5ustar pravipravibyebug-5.0.0/ext/byebug/breakpoint.c0000644000076400007640000002675412560626142016426 0ustar pravipravi#include #ifdef _WIN32 #include #endif #if defined DOSISH #define isdirsep(x) ((x) == '/' || (x) == '\\') #else #define isdirsep(x) ((x) == '/') #endif static VALUE cBreakpoint; static int breakpoint_max; static ID idEval; static VALUE eval_expression(VALUE args) { return rb_funcall2(rb_mKernel, idEval, 2, RARRAY_PTR(args)); } /* * call-seq: * breakpoint.enabled? -> bool * * Returns +true+ if breakpoint is enabled, false otherwise. */ static VALUE brkpt_enabled(VALUE self) { breakpoint_t *breakpoint; Data_Get_Struct(self, breakpoint_t, breakpoint); return breakpoint->enabled; } /* * call-seq: * breakpoint.enabled = bool * * Enables or disables breakpoint. */ static VALUE brkpt_set_enabled(VALUE self, VALUE bool) { breakpoint_t *breakpoint; Data_Get_Struct(self, breakpoint_t, breakpoint); return breakpoint->enabled = bool; } /* * call-seq: * breakpoint.expr -> string * * Returns a conditional expression which indicates when this breakpoint should * be activated. */ static VALUE brkpt_expr(VALUE self) { breakpoint_t *breakpoint; Data_Get_Struct(self, breakpoint_t, breakpoint); return breakpoint->expr; } /* * call-seq: * breakpoint.expr = string | nil * * Sets or unsets the conditional expression which indicates when this * breakpoint should be activated. */ static VALUE brkpt_set_expr(VALUE self, VALUE expr) { breakpoint_t *breakpoint; Data_Get_Struct(self, breakpoint_t, breakpoint); breakpoint->expr = NIL_P(expr) ? expr : StringValue(expr); return expr; } /* * call-seq: * breakpoint.hit_condition -> symbol * * Returns the hit condition of the breakpoint: +nil+ if it is an * unconditional breakpoint, or :greater_or_equal, :equal or :modulo otherwise */ static VALUE brkpt_hit_condition(VALUE self) { breakpoint_t *breakpoint; Data_Get_Struct(self, breakpoint_t, breakpoint); switch (breakpoint->hit_condition) { case HIT_COND_GE: return ID2SYM(rb_intern("greater_or_equal")); case HIT_COND_EQ: return ID2SYM(rb_intern("equal")); case HIT_COND_MOD: return ID2SYM(rb_intern("modulo")); case HIT_COND_NONE: default: return Qnil; } } /* * call-seq: * breakpoint.hit_condition = symbol * * Sets the hit condition of the breakpoint which must be one of the following * values: * * +nil+ if it is an unconditional breakpoint, or * :greater_or_equal(:ge), :equal(:eq), :modulo(:mod) */ static VALUE brkpt_set_hit_condition(VALUE self, VALUE value) { breakpoint_t *breakpoint; ID id_value; Data_Get_Struct(self, breakpoint_t, breakpoint); id_value = rb_to_id(value); if (rb_intern("greater_or_equal") == id_value || rb_intern("ge") == id_value) breakpoint->hit_condition = HIT_COND_GE; else if (rb_intern("equal") == id_value || rb_intern("eq") == id_value) breakpoint->hit_condition = HIT_COND_EQ; else if (rb_intern("modulo") == id_value || rb_intern("mod") == id_value) breakpoint->hit_condition = HIT_COND_MOD; else rb_raise(rb_eArgError, "Invalid condition parameter"); return value; } /* * call-seq: * breakpoint.hit_count -> int * * Returns the number of times this breakpoint has been hit. */ static VALUE brkpt_hit_count(VALUE self) { breakpoint_t *breakpoint; Data_Get_Struct(self, breakpoint_t, breakpoint); return INT2FIX(breakpoint->hit_count); } /* * call-seq: * breakpoint.hit_value -> int * * Returns the hit value of the breakpoint, namely, a value to build a * condition on the number of hits of the breakpoint. */ static VALUE brkpt_hit_value(VALUE self) { breakpoint_t *breakpoint; Data_Get_Struct(self, breakpoint_t, breakpoint); return INT2FIX(breakpoint->hit_value); } /* * call-seq: * breakpoint.hit_value = int * * Sets the hit value of the breakpoint. This allows the user to set conditions * on the number of hits to enable/disable the breakpoint. */ static VALUE brkpt_set_hit_value(VALUE self, VALUE value) { breakpoint_t *breakpoint; Data_Get_Struct(self, breakpoint_t, breakpoint); breakpoint->hit_value = FIX2INT(value); return value; } /* * call-seq: * breakpoint.id -> int * * Returns the id of the breakpoint. */ static VALUE brkpt_id(VALUE self) { breakpoint_t *breakpoint; Data_Get_Struct(self, breakpoint_t, breakpoint); return INT2FIX(breakpoint->id); } /* * call-seq: * breakpoint.pos -> string or int * * Returns the position of this breakpoint, either a method name or a line * number. */ static VALUE brkpt_pos(VALUE self) { breakpoint_t *breakpoint; Data_Get_Struct(self, breakpoint_t, breakpoint); if (breakpoint->type == BP_METHOD_TYPE) return rb_str_new2(rb_id2name(breakpoint->pos.mid)); else return INT2FIX(breakpoint->pos.line); } /* * call-seq: * breakpoint.source -> string * * Returns the source file of the breakpoint. */ static VALUE brkpt_source(VALUE self) { breakpoint_t *breakpoint; Data_Get_Struct(self, breakpoint_t, breakpoint); return breakpoint->source; } static void mark_breakpoint(breakpoint_t * breakpoint) { rb_gc_mark(breakpoint->source); rb_gc_mark(breakpoint->expr); } static VALUE brkpt_create(VALUE klass) { breakpoint_t *breakpoint = ALLOC(breakpoint_t); return Data_Wrap_Struct(klass, mark_breakpoint, xfree, breakpoint); } static VALUE brkpt_initialize(VALUE self, VALUE source, VALUE pos, VALUE expr) { breakpoint_t *breakpoint; Data_Get_Struct(self, breakpoint_t, breakpoint); breakpoint->type = FIXNUM_P(pos) ? BP_POS_TYPE : BP_METHOD_TYPE; if (breakpoint->type == BP_POS_TYPE) breakpoint->pos.line = FIX2INT(pos); else breakpoint->pos.mid = SYM2ID(pos); breakpoint->id = ++breakpoint_max; breakpoint->source = StringValue(source); breakpoint->enabled = Qtrue; breakpoint->expr = NIL_P(expr) ? expr : StringValue(expr); breakpoint->hit_count = 0; breakpoint->hit_value = 0; breakpoint->hit_condition = HIT_COND_NONE; return Qnil; } int filename_cmp_impl(VALUE source, char *file) { char *source_ptr, *file_ptr; long s_len, f_len, min_len; long s, f; int dirsep_flag = 0; s_len = RSTRING_LEN(source); f_len = strlen(file); min_len = s_len < f_len ? s_len : f_len; source_ptr = RSTRING_PTR(source); file_ptr = file; for (s = s_len - 1, f = f_len - 1; s >= s_len - min_len && f >= f_len - min_len; s--, f--) { if ((source_ptr[s] == '.' || file_ptr[f] == '.') && dirsep_flag) return 1; if (isdirsep(source_ptr[s]) && isdirsep(file_ptr[f])) dirsep_flag = 1; #ifdef DOSISH_DRIVE_LETTER else if (s == 0) return (toupper(source_ptr[s]) == toupper(file_ptr[f])); #endif else if (source_ptr[s] != file_ptr[f]) return 0; } return 1; } int filename_cmp(VALUE source, char *file) { #ifdef _WIN32 return filename_cmp_impl(source, file); #else #ifdef PATH_MAX char path[PATH_MAX + 1]; path[PATH_MAX] = 0; return filename_cmp_impl(source, realpath(file, path) != NULL ? path : file); #else char *path; int result; path = realpath(file, NULL); result = filename_cmp_impl(source, path == NULL ? file : path); free(path); return result; #endif #endif } int classname_cmp(VALUE name, VALUE klass) { VALUE mod_name; VALUE class_name = NIL_P(name) ? rb_str_new2("main") : name; if (NIL_P(klass)) return 0; mod_name = rb_mod_name(klass); return (mod_name != Qnil && rb_str_cmp(class_name, mod_name) == 0); } static int check_breakpoint_by_hit_condition(VALUE rb_breakpoint) { breakpoint_t *breakpoint; if (NIL_P(rb_breakpoint)) return 0; Data_Get_Struct(rb_breakpoint, breakpoint_t, breakpoint); breakpoint->hit_count++; if (Qtrue != breakpoint->enabled) return 0; switch (breakpoint->hit_condition) { case HIT_COND_NONE: return 1; case HIT_COND_GE: { if (breakpoint->hit_count >= breakpoint->hit_value) return 1; break; } case HIT_COND_EQ: { if (breakpoint->hit_count == breakpoint->hit_value) return 1; break; } case HIT_COND_MOD: { if (breakpoint->hit_count % breakpoint->hit_value == 0) return 1; break; } } return 0; } static int check_breakpoint_by_pos(VALUE rb_breakpoint, char *file, int line) { breakpoint_t *breakpoint; if (NIL_P(rb_breakpoint)) return 0; Data_Get_Struct(rb_breakpoint, breakpoint_t, breakpoint); if (Qfalse == breakpoint->enabled || breakpoint->type != BP_POS_TYPE || breakpoint->pos.line != line) return 0; return filename_cmp(breakpoint->source, file); } static int check_breakpoint_by_method(VALUE rb_breakpoint, VALUE klass, ID mid, VALUE self) { breakpoint_t *breakpoint; if (NIL_P(rb_breakpoint)) return 0; Data_Get_Struct(rb_breakpoint, breakpoint_t, breakpoint); if (Qfalse == breakpoint->enabled || breakpoint->type != BP_METHOD_TYPE || breakpoint->pos.mid != mid) return 0; if (classname_cmp(breakpoint->source, klass) || ((rb_type(self) == T_CLASS || rb_type(self) == T_MODULE) && classname_cmp(breakpoint->source, self))) return 1; return 0; } static int check_breakpoint_by_expr(VALUE rb_breakpoint, VALUE bind) { breakpoint_t *breakpoint; VALUE args, expr_result; if (NIL_P(rb_breakpoint)) return 0; Data_Get_Struct(rb_breakpoint, breakpoint_t, breakpoint); if (Qfalse == breakpoint->enabled) return 0; if (NIL_P(breakpoint->expr)) return 1; args = rb_ary_new3(2, breakpoint->expr, bind); expr_result = rb_protect(eval_expression, args, 0); return RTEST(expr_result); } extern VALUE find_breakpoint_by_pos(VALUE breakpoints, VALUE source, VALUE pos, VALUE bind) { VALUE breakpoint; char *file; int line; int i; file = RSTRING_PTR(source); line = FIX2INT(pos); for (i = 0; i < RARRAY_LENINT(breakpoints); i++) { breakpoint = rb_ary_entry(breakpoints, i); if (check_breakpoint_by_pos(breakpoint, file, line) && check_breakpoint_by_expr(breakpoint, bind) && check_breakpoint_by_hit_condition(breakpoint)) { return breakpoint; } } return Qnil; } extern VALUE find_breakpoint_by_method(VALUE breakpoints, VALUE klass, ID mid, VALUE bind, VALUE self) { VALUE breakpoint; int i; for (i = 0; i < RARRAY_LENINT(breakpoints); i++) { breakpoint = rb_ary_entry(breakpoints, i); if (check_breakpoint_by_method(breakpoint, klass, mid, self) && check_breakpoint_by_expr(breakpoint, bind) && check_breakpoint_by_hit_condition(breakpoint)) { return breakpoint; } } return Qnil; } void Init_breakpoint(VALUE mByebug) { breakpoint_max = 0; cBreakpoint = rb_define_class_under(mByebug, "Breakpoint", rb_cObject); rb_define_alloc_func(cBreakpoint, brkpt_create); rb_define_method(cBreakpoint, "initialize", brkpt_initialize, 3); rb_define_method(cBreakpoint, "enabled?", brkpt_enabled, 0); rb_define_method(cBreakpoint, "enabled=", brkpt_set_enabled, 1); rb_define_method(cBreakpoint, "expr", brkpt_expr, 0); rb_define_method(cBreakpoint, "expr=", brkpt_set_expr, 1); rb_define_method(cBreakpoint, "hit_count", brkpt_hit_count, 0); rb_define_method(cBreakpoint, "hit_condition", brkpt_hit_condition, 0); rb_define_method(cBreakpoint, "hit_condition=", brkpt_set_hit_condition, 1); rb_define_method(cBreakpoint, "hit_value", brkpt_hit_value, 0); rb_define_method(cBreakpoint, "hit_value=", brkpt_set_hit_value, 1); rb_define_method(cBreakpoint, "id", brkpt_id, 0); rb_define_method(cBreakpoint, "pos", brkpt_pos, 0); rb_define_method(cBreakpoint, "source", brkpt_source, 0); idEval = rb_intern("eval"); } byebug-5.0.0/ext/byebug/threads.c0000644000076400007640000001074312560626142015711 0ustar pravipravi#include /* Threads table class */ static VALUE cThreadsTable; /* If not Qnil, holds the next thread that must be run */ VALUE next_thread = Qnil; /* To allow thread syncronization, we must stop threads when debugging */ VALUE locker = Qnil; static int t_tbl_mark_keyvalue(st_data_t key, st_data_t value, st_data_t tbl) { UNUSED(tbl); rb_gc_mark((VALUE) key); if (!value) return ST_CONTINUE; rb_gc_mark((VALUE) value); return ST_CONTINUE; } static void t_tbl_mark(void *data) { threads_table_t *t_tbl = (threads_table_t *) data; st_table *tbl = t_tbl->tbl; st_foreach(tbl, t_tbl_mark_keyvalue, (st_data_t) tbl); } static void t_tbl_free(void *data) { threads_table_t *t_tbl = (threads_table_t *) data; st_free_table(t_tbl->tbl); xfree(t_tbl); } /* * Creates a numeric hash whose keys are the currently active threads and * whose values are their associated contexts. */ VALUE create_threads_table(void) { threads_table_t *t_tbl; t_tbl = ALLOC(threads_table_t); t_tbl->tbl = st_init_numtable(); return Data_Wrap_Struct(cThreadsTable, t_tbl_mark, t_tbl_free, t_tbl); } /* * Checks a single entry in the threads table. * * If it has no associated context or the key doesn't correspond to a living * thread, the entry is removed from the thread's list. */ static int check_thread_i(st_data_t key, st_data_t value, st_data_t data) { UNUSED(data); if (!value) return ST_DELETE; if (!is_living_thread((VALUE) key)) return ST_DELETE; return ST_CONTINUE; } /* * Checks whether a thread is either in the running or sleeping state. */ int is_living_thread(VALUE thread) { VALUE status = rb_funcall(thread, rb_intern("status"), 0); if (NIL_P(status) || status == Qfalse) return 0; if (rb_str_cmp(status, rb_str_new2("run")) == 0 || rb_str_cmp(status, rb_str_new2("sleep")) == 0) return 1; return 0; } /* * Checks threads table for dead/finished threads. */ void cleanup_dead_threads(void) { threads_table_t *t_tbl; Data_Get_Struct(threads, threads_table_t, t_tbl); st_foreach(t_tbl->tbl, check_thread_i, 0); } /* * Looks up a context in the threads table. If not present, it creates it. */ void thread_context_lookup(VALUE thread, VALUE * context) { threads_table_t *t_tbl; Data_Get_Struct(threads, threads_table_t, t_tbl); if (!st_lookup(t_tbl->tbl, thread, context) || !*context) { *context = context_create(thread); st_insert(t_tbl->tbl, thread, *context); } } /* * Holds thread execution while another thread is active. * * Thanks to this, all threads are "frozen" while the user is typing commands. */ void acquire_lock(debug_context_t * dc) { while ((!NIL_P(locker) && locker != rb_thread_current()) || CTX_FL_TEST(dc, CTX_FL_SUSPEND)) { add_to_locked(rb_thread_current()); rb_thread_stop(); if (CTX_FL_TEST(dc, CTX_FL_SUSPEND)) CTX_FL_SET(dc, CTX_FL_WAS_RUNNING); } locker = rb_thread_current(); } /* * Releases our global lock and passes execution on to another thread, either * the thread specified by +next_thread+ or any other thread if +next_thread+ * is nil. */ void release_lock(void) { VALUE thread; cleanup_dead_threads(); locker = Qnil; if (NIL_P(next_thread)) thread = pop_from_locked(); else { remove_from_locked(next_thread); thread = next_thread; } if (thread == next_thread) next_thread = Qnil; if (!NIL_P(thread) && is_living_thread(thread)) rb_thread_run(thread); } /* * call-seq: * Byebug.unlock -> nil * * Unlocks global switch so other threads can run. */ static VALUE Unlock(VALUE self) { UNUSED(self); release_lock(); return locker; } /* * call-seq: * Byebug.lock -> Thread.current * * Locks global switch to reserve execution to current thread exclusively. */ static VALUE Lock(VALUE self) { debug_context_t *dc; VALUE context; UNUSED(self); if (!is_living_thread(rb_thread_current())) rb_raise(rb_eRuntimeError, "Current thread is dead!"); thread_context_lookup(rb_thread_current(), &context); Data_Get_Struct(context, debug_context_t, dc); acquire_lock(dc); return locker; } /* * * Document-class: ThreadsTable * * == Sumary * * Hash table holding currently active threads and their associated contexts */ void Init_threads_table(VALUE mByebug) { cThreadsTable = rb_define_class_under(mByebug, "ThreadsTable", rb_cObject); rb_define_module_function(mByebug, "unlock", Unlock, 0); rb_define_module_function(mByebug, "lock", Lock, 0); } byebug-5.0.0/ext/byebug/extconf.rb0000644000076400007640000000076712560626142016113 0ustar pravipraviif RUBY_VERSION < '2.0' STDERR.print("Ruby version is too old\n") exit(1) end require 'mkmf' makefile_config = RbConfig::MAKEFILE_CONFIG makefile_config['CC'] = ENV['CC'] if ENV['CC'] makefile_config['CFLAGS'] << ' -Wall -Werror' makefile_config['CFLAGS'] << ' -gdwarf-2 -g3 -O0' if ENV['debug'] if makefile_config['CC'] =~ /clang/ makefile_config['CFLAGS'] << ' -Wno-unknown-warning-option' end dir_config('ruby') with_cflags(makefile_config['CFLAGS']) { create_makefile('byebug/byebug') } byebug-5.0.0/ext/byebug/locker.c0000644000076400007640000000310412560626142015527 0ustar pravipravi#include /** * A simple linked list containing locked threads, FIFO style. */ typedef struct locked_thread_t { VALUE thread; struct locked_thread_t *next; } locked_thread_t; static locked_thread_t *locked_head = NULL; static locked_thread_t *locked_tail = NULL; extern int is_in_locked(VALUE thread) { locked_thread_t *node; if (!locked_head) return 0; for (node = locked_head; node != locked_tail; node = node->next) if (node->thread == thread) return 1; return 0; } extern void add_to_locked(VALUE thread) { locked_thread_t *node; if (is_in_locked(thread)) return; node = ALLOC(locked_thread_t); node->thread = thread; node->next = NULL; if (locked_tail) locked_tail->next = node; locked_tail = node; if (!locked_head) locked_head = node; } extern VALUE pop_from_locked() { VALUE thread; locked_thread_t *node; if (!locked_head) return Qnil; node = locked_head; locked_head = locked_head->next; if (locked_tail == node) locked_tail = NULL; thread = node->thread; xfree(node); return thread; } extern void remove_from_locked(VALUE thread) { locked_thread_t *node; locked_thread_t *next_node; if (NIL_P(thread) || !locked_head || !is_in_locked(thread)) return; if (locked_head->thread == thread) { pop_from_locked(); return; } for (node = locked_head; node != locked_tail; node = node->next) if (node->next && node->next->thread == thread) { next_node = node->next; node->next = next_node->next; xfree(next_node); return; } } byebug-5.0.0/ext/byebug/byebug.c0000644000076400007640000004356012560626142015537 0ustar pravipravi#include static VALUE mByebug; /* Ruby Byebug Module object */ static VALUE tracing = Qfalse; static VALUE post_mortem = Qfalse; static VALUE verbose = Qfalse; static VALUE catchpoints = Qnil; static VALUE breakpoints = Qnil; static VALUE tracepoints = Qnil; static VALUE raised_exception = Qnil; static ID idPuts; /* Hash table with active threads and their associated contexts */ VALUE threads = Qnil; /* * call-seq: * Byebug.breakpoints -> array * * Returns an array of breakpoints. */ static VALUE Breakpoints(VALUE self) { UNUSED(self); if (NIL_P(breakpoints)) breakpoints = rb_ary_new(); return breakpoints; } /* * call-seq: * Byebug.catchpoints -> array * * Returns an array of catchpoints. */ static VALUE Catchpoints(VALUE self) { UNUSED(self); return catchpoints; } /* * call-seq: * Byebug.raised_exception -> exception * * Returns raised exception when in post_mortem mode. */ static VALUE Raised_exception(VALUE self) { UNUSED(self); return raised_exception; } #define IS_STARTED (catchpoints != Qnil) static void check_started() { if (!IS_STARTED) { rb_raise(rb_eRuntimeError, "Byebug is not started yet."); } } static void trace_print(rb_trace_arg_t * trace_arg, debug_context_t * dc, const char *file_filter, const char *debug_msg) { char *fullpath = NULL; const char *basename; int filtered = 0; const char *event = rb_id2name(SYM2ID(rb_tracearg_event(trace_arg))); VALUE rb_path = rb_tracearg_path(trace_arg); const char *path = NIL_P(rb_path) ? "" : RSTRING_PTR(rb_path); int line = NUM2INT(rb_tracearg_lineno(trace_arg)); VALUE rb_mid = rb_tracearg_method_id(trace_arg); const char *mid = NIL_P(rb_mid) ? "(top level)" : rb_id2name(SYM2ID(rb_mid)); VALUE rb_cl = rb_tracearg_defined_class(trace_arg); VALUE rb_cl_name = NIL_P(rb_cl) ? rb_cl : rb_mod_name(rb_cl); const char *defined_class = NIL_P(rb_cl_name) ? "" : RSTRING_PTR(rb_cl_name); if (!trace_arg) return; if (file_filter) { #ifndef _WIN32 fullpath = realpath(path, NULL); #endif basename = fullpath ? strrchr(fullpath, '/') : path; if (!basename || strncmp(basename + 1, file_filter, strlen(file_filter))) filtered = 1; #ifndef _WIN32 free(fullpath); #endif } if (!filtered) { if (debug_msg) rb_funcall(mByebug, idPuts, 1, rb_sprintf("[#%d] %s\n", dc->thnum, debug_msg)); else rb_funcall(mByebug, idPuts, 1, rb_sprintf("%*s [#%d] %s@%s:%d %s#%s\n", dc->calced_stack_size, "", dc->thnum, event, path, line, defined_class, mid)); } } static void cleanup(debug_context_t * dc) { dc->stop_reason = CTX_STOP_NONE; release_lock(); } #define EVENT_TEARDOWN cleanup(dc); #define EVENT_SETUP \ debug_context_t *dc; \ VALUE context; \ rb_trace_arg_t *trace_arg; \ \ UNUSED(data); \ \ if (!is_living_thread(rb_thread_current())) \ return; \ \ thread_context_lookup(rb_thread_current(), &context); \ Data_Get_Struct(context, debug_context_t, dc); \ \ if (CTX_FL_TEST(dc, CTX_FL_IGNORE)) \ return; \ \ acquire_lock(dc); \ \ trace_arg = rb_tracearg_from_tracepoint(trace_point); \ if (verbose == Qtrue) \ trace_print(trace_arg, dc, 0, 0); \ /* Functions that return control to byebug after the different events */ static VALUE call_at(VALUE context_obj, debug_context_t * dc, ID mid, int argc, VALUE a0, VALUE a1) { struct call_with_inspection_data cwi; VALUE argv[2]; argv[0] = a0; argv[1] = a1; cwi.dc = dc; cwi.context_obj = context_obj; cwi.id = mid; cwi.argc = argc; cwi.argv = &argv[0]; return call_with_debug_inspector(&cwi); } static VALUE call_at_line(VALUE context_obj, debug_context_t * dc, VALUE file, VALUE line) { return call_at(context_obj, dc, rb_intern("at_line"), 2, file, line); } static VALUE call_at_tracing(VALUE context_obj, debug_context_t * dc, VALUE file, VALUE line) { return call_at(context_obj, dc, rb_intern("at_tracing"), 2, file, line); } static VALUE call_at_breakpoint(VALUE context_obj, debug_context_t * dc, VALUE breakpoint) { dc->stop_reason = CTX_STOP_BREAKPOINT; return call_at(context_obj, dc, rb_intern("at_breakpoint"), 1, breakpoint, 0); } static VALUE call_at_catchpoint(VALUE context_obj, debug_context_t * dc, VALUE exp) { dc->stop_reason = CTX_STOP_CATCHPOINT; return call_at(context_obj, dc, rb_intern("at_catchpoint"), 1, exp, 0); } static VALUE call_at_return(VALUE context_obj, debug_context_t * dc, VALUE file, VALUE line) { dc->stop_reason = CTX_STOP_BREAKPOINT; return call_at(context_obj, dc, rb_intern("at_return"), 2, file, line); } static void call_at_line_check(VALUE context_obj, debug_context_t * dc, VALUE breakpoint, VALUE file, VALUE line) { dc->stop_reason = CTX_STOP_STEP; if (breakpoint != Qnil) call_at_breakpoint(context_obj, dc, breakpoint); reset_stepping_stop_points(dc); call_at_line(context_obj, dc, file, line); } /* TracePoint API event handlers */ static void line_event(VALUE trace_point, void *data) { VALUE brkpnt, file, line, binding; EVENT_SETUP; file = rb_tracearg_path(trace_arg); line = rb_tracearg_lineno(trace_arg); binding = rb_tracearg_binding(trace_arg); if (RTEST(tracing)) call_at_tracing(context, dc, file, line); if (!CTX_FL_TEST(dc, CTX_FL_IGNORE_STEPS)) dc->steps = dc->steps <= 0 ? -1 : dc->steps - 1; if (dc->calced_stack_size <= dc->dest_frame) { dc->dest_frame = dc->calced_stack_size; CTX_FL_UNSET(dc, CTX_FL_IGNORE_STEPS); dc->lines = dc->lines <= 0 ? -1 : dc->lines - 1; } if (dc->steps == 0 || dc->lines == 0) call_at_line_check(context, dc, Qnil, file, line); brkpnt = Qnil; if (!NIL_P(breakpoints)) brkpnt = find_breakpoint_by_pos(breakpoints, file, line, binding); if (!NIL_P(brkpnt)) call_at_line_check(context, dc, brkpnt, file, line); EVENT_TEARDOWN; } static void call_event(VALUE trace_point, void *data) { VALUE brkpnt, klass, msym, mid, binding, self, file, line; EVENT_SETUP; if (dc->calced_stack_size <= dc->dest_frame) CTX_FL_UNSET(dc, CTX_FL_IGNORE_STEPS); dc->calced_stack_size++; dc->steps_out = dc->steps_out <= 0 ? -1 : dc->steps_out + 1; /* nil method_id means we are at top level so there can't be a method * breakpoint here. Just leave then. */ msym = rb_tracearg_method_id(trace_arg); if (NIL_P(msym)) { EVENT_TEARDOWN; return; } mid = SYM2ID(msym); klass = rb_tracearg_defined_class(trace_arg); binding = rb_tracearg_binding(trace_arg); self = rb_tracearg_self(trace_arg); file = rb_tracearg_path(trace_arg); line = rb_tracearg_lineno(trace_arg); brkpnt = Qnil; if (!NIL_P(breakpoints)) brkpnt = find_breakpoint_by_method(breakpoints, klass, mid, binding, self); if (!NIL_P(brkpnt)) { call_at_breakpoint(context, dc, brkpnt); call_at_line(context, dc, file, line); } EVENT_TEARDOWN; } static void return_event(VALUE trace_point, void *data) { EVENT_SETUP; dc->calced_stack_size--; if (dc->steps_out == 1) dc->steps = 1; else if ((dc->steps_out == 0) && (CTX_FL_TEST(dc, CTX_FL_STOP_ON_RET))) { VALUE file, line; reset_stepping_stop_points(dc); file = rb_tracearg_path(trace_arg); line = rb_tracearg_lineno(trace_arg); call_at_return(context, dc, file, line); } dc->steps_out = dc->steps_out <= 0 ? -1 : dc->steps_out - 1; EVENT_TEARDOWN; } static void raw_call_event(VALUE trace_point, void *data) { EVENT_SETUP; dc->calced_stack_size++; EVENT_TEARDOWN; } static void raw_return_event(VALUE trace_point, void *data) { EVENT_SETUP; dc->calced_stack_size--; EVENT_TEARDOWN; } static void raise_event(VALUE trace_point, void *data) { VALUE expn_class, ancestors; VALUE path, lineno, binding, post_mortem_context; int i; debug_context_t *new_dc; EVENT_SETUP; path = rb_tracearg_path(trace_arg); lineno = rb_tracearg_lineno(trace_arg); binding = rb_tracearg_binding(trace_arg); raised_exception = rb_tracearg_raised_exception(trace_arg); if (post_mortem == Qtrue) { post_mortem_context = context_dup(dc); rb_ivar_set(raised_exception, rb_intern("@__bb_file"), path); rb_ivar_set(raised_exception, rb_intern("@__bb_line"), lineno); rb_ivar_set(raised_exception, rb_intern("@__bb_binding"), binding); rb_ivar_set(raised_exception, rb_intern("@__bb_context"), post_mortem_context); Data_Get_Struct(post_mortem_context, debug_context_t, new_dc); rb_debug_inspector_open(context_backtrace_set, (void *)new_dc); } if (catchpoints == Qnil || dc->calced_stack_size == 0 || RHASH_TBL(catchpoints)->num_entries == 0) { EVENT_TEARDOWN; return; } expn_class = rb_obj_class(raised_exception); ancestors = rb_mod_ancestors(expn_class); for (i = 0; i < RARRAY_LENINT(ancestors); i++) { VALUE ancestor_class, module_name, hit_count; ancestor_class = rb_ary_entry(ancestors, i); module_name = rb_mod_name(ancestor_class); hit_count = rb_hash_aref(catchpoints, module_name); /* increment exception */ if (hit_count != Qnil) { rb_hash_aset(catchpoints, module_name, INT2FIX(FIX2INT(hit_count) + 1)); call_at_catchpoint(context, dc, raised_exception); call_at_line(context, dc, path, lineno); break; } } EVENT_TEARDOWN; } /* Setup TracePoint functionality */ static void register_tracepoints(VALUE self) { int i; VALUE traces = tracepoints; UNUSED(self); if (NIL_P(traces)) { int line_msk = RUBY_EVENT_LINE; int call_msk = RUBY_EVENT_CALL; int ret_msk = RUBY_EVENT_RETURN | RUBY_EVENT_B_RETURN | RUBY_EVENT_END; int raw_call_msk = RUBY_EVENT_C_CALL | RUBY_EVENT_B_CALL | RUBY_EVENT_CLASS; int raw_ret_msk = RUBY_EVENT_C_RETURN; int raise_msk = RUBY_EVENT_RAISE; VALUE tpLine = rb_tracepoint_new(Qnil, line_msk, line_event, 0); VALUE tpCall = rb_tracepoint_new(Qnil, call_msk, call_event, 0); VALUE tpReturn = rb_tracepoint_new(Qnil, ret_msk, return_event, 0); VALUE tpCCall = rb_tracepoint_new(Qnil, raw_call_msk, raw_call_event, 0); VALUE tpCReturn = rb_tracepoint_new(Qnil, raw_ret_msk, raw_return_event, 0); VALUE tpRaise = rb_tracepoint_new(Qnil, raise_msk, raise_event, 0); traces = rb_ary_new(); rb_ary_push(traces, tpLine); rb_ary_push(traces, tpCall); rb_ary_push(traces, tpReturn); rb_ary_push(traces, tpCCall); rb_ary_push(traces, tpCReturn); rb_ary_push(traces, tpRaise); tracepoints = traces; } for (i = 0; i < RARRAY_LENINT(traces); i++) rb_tracepoint_enable(rb_ary_entry(traces, i)); } static void clear_tracepoints(VALUE self) { int i; UNUSED(self); for (i = RARRAY_LENINT(tracepoints) - 1; i >= 0; i--) rb_tracepoint_disable(rb_ary_entry(tracepoints, i)); } /* Byebug's Public API */ /* * call-seq: * Byebug.contexts -> array * * Returns an array of all contexts. */ static VALUE Contexts(VALUE self) { volatile VALUE list; volatile VALUE new_list; VALUE context; threads_table_t *t_tbl; debug_context_t *dc; int i; UNUSED(self); check_started(); new_list = rb_ary_new(); list = rb_funcall(rb_cThread, rb_intern("list"), 0); for (i = 0; i < RARRAY_LENINT(list); i++) { VALUE thread = rb_ary_entry(list, i); thread_context_lookup(thread, &context); rb_ary_push(new_list, context); } Data_Get_Struct(threads, threads_table_t, t_tbl); st_clear(t_tbl->tbl); for (i = 0; i < RARRAY_LENINT(new_list); i++) { context = rb_ary_entry(new_list, i); Data_Get_Struct(context, debug_context_t, dc); st_insert(t_tbl->tbl, dc->thread, context); } return new_list; } /* * call-seq: * Byebug.thread_context(thread) -> context * * Returns context of the thread passed as an argument. */ static VALUE Thread_context(VALUE self, VALUE thread) { VALUE context; UNUSED(self); check_started(); thread_context_lookup(thread, &context); return context; } /* * call-seq: * Byebug.current_context -> context * * Returns the current context. * Note: Byebug.current_context.thread == Thread.current */ static VALUE Current_context(VALUE self) { VALUE context; UNUSED(self); check_started(); thread_context_lookup(rb_thread_current(), &context); return context; } /* * call-seq: * Byebug.started? -> bool * * Returns +true+ byebug is started. */ static VALUE Started(VALUE self) { UNUSED(self); return IS_STARTED; } /* * call-seq: * Byebug.stop -> bool * * This method disables byebug. It returns +true+ if byebug was already * disabled, otherwise it returns +false+. */ static VALUE Stop(VALUE self) { UNUSED(self); if (IS_STARTED) { clear_tracepoints(self); breakpoints = Qnil; catchpoints = Qnil; threads = Qnil; return Qfalse; } return Qtrue; } /* * call-seq: * Byebug.start -> bool * * The return value is the value of !Byebug.started? before issuing the * +start+; That is, +true+ is returned, unless byebug was previously started. */ static VALUE Start(VALUE self) { if (IS_STARTED) return Qfalse; catchpoints = rb_hash_new(); threads = create_threads_table(); register_tracepoints(self); return Qtrue; } /* * call-seq: * Byebug.debug_load(file, stop = false) -> nil * * Same as Kernel#load but resets current context's frames. * +stop+ parameter forces byebug to stop at the first line of code in +file+ */ static VALUE Debug_load(int argc, VALUE * argv, VALUE self) { VALUE file, stop, context; debug_context_t *dc; VALUE status = Qnil; int state = 0; UNUSED(self); if (rb_scan_args(argc, argv, "11", &file, &stop) == 1) stop = Qfalse; Start(self); context = Current_context(self); Data_Get_Struct(context, debug_context_t, dc); dc->calced_stack_size = 1; if (RTEST(stop)) dc->steps = 1; rb_load_protect(file, 0, &state); if (0 != state) { status = rb_errinfo(); reset_stepping_stop_points(dc); } return status; } /* * call-seq: * Byebug.verbose? -> bool * * Returns +true+ if verbose output of TracePoint API events is enabled. */ static VALUE Verbose(VALUE self) { UNUSED(self); return verbose; } /* * call-seq: * Byebug.verbose = bool * * Enable verbose output of every TracePoint API events, useful for debugging * byebug. */ static VALUE Set_verbose(VALUE self, VALUE value) { UNUSED(self); verbose = RTEST(value) ? Qtrue : Qfalse; return value; } /* * call-seq: * Byebug.tracing? -> bool * * Returns +true+ if global tracing is enabled. */ static VALUE Tracing(VALUE self) { UNUSED(self); return tracing; } /* * call-seq: * Byebug.tracing = bool * * Sets the global tracing flag. */ static VALUE Set_tracing(VALUE self, VALUE value) { UNUSED(self); tracing = RTEST(value) ? Qtrue : Qfalse; return value; } /* * call-seq: * Byebug.post_mortem? -> bool * * Returns +true+ if post-mortem debugging is enabled. */ static VALUE Post_mortem(VALUE self) { UNUSED(self); return post_mortem; } /* * call-seq: * Byebug.post_mortem = bool * * Sets post-moterm flag. */ static VALUE Set_post_mortem(VALUE self, VALUE value) { UNUSED(self); post_mortem = RTEST(value) ? Qtrue : Qfalse; return value; } /* * call-seq: * Byebug.add_catchpoint(exception) -> exception * * Adds a new exception to the catchpoints array. */ static VALUE Add_catchpoint(VALUE self, VALUE value) { UNUSED(self); if (TYPE(value) != T_STRING) rb_raise(rb_eTypeError, "value of a catchpoint must be String"); rb_hash_aset(catchpoints, rb_str_dup(value), INT2FIX(0)); return value; } /* * Document-class: Byebug * * == Summary * * This is a singleton class allows controlling byebug. Use it to start/stop * byebug, set/remove breakpoints, etc. */ void Init_byebug() { mByebug = rb_define_module("Byebug"); rb_define_module_function(mByebug, "add_catchpoint", Add_catchpoint, 1); rb_define_module_function(mByebug, "breakpoints", Breakpoints, 0); rb_define_module_function(mByebug, "catchpoints", Catchpoints, 0); rb_define_module_function(mByebug, "contexts", Contexts, 0); rb_define_module_function(mByebug, "current_context", Current_context, 0); rb_define_module_function(mByebug, "debug_load", Debug_load, -1); rb_define_module_function(mByebug, "post_mortem?", Post_mortem, 0); rb_define_module_function(mByebug, "post_mortem=", Set_post_mortem, 1); rb_define_module_function(mByebug, "raised_exception", Raised_exception, 0); rb_define_module_function(mByebug, "start", Start, 0); rb_define_module_function(mByebug, "started?", Started, 0); rb_define_module_function(mByebug, "stop", Stop, 0); rb_define_module_function(mByebug, "thread_context", Thread_context, 1); rb_define_module_function(mByebug, "tracing?", Tracing, 0); rb_define_module_function(mByebug, "tracing=", Set_tracing, 1); rb_define_module_function(mByebug, "verbose?", Verbose, 0); rb_define_module_function(mByebug, "verbose=", Set_verbose, 1); Init_threads_table(mByebug); Init_context(mByebug); Init_breakpoint(mByebug); rb_global_variable(&breakpoints); rb_global_variable(&catchpoints); rb_global_variable(&tracepoints); rb_global_variable(&raised_exception); rb_global_variable(&threads); idPuts = rb_intern("puts"); } byebug-5.0.0/ext/byebug/byebug.h0000644000076400007640000000743012560626142015540 0ustar pravipravi#ifndef BYEBUG #define BYEBUG #include #include /* To prevent unused parameter warnings */ #define UNUSED(x) (void)(x) /* flags */ #define CTX_FL_DEAD (1<<1) /* this context belonged to a dead thread */ #define CTX_FL_IGNORE (1<<2) /* this context belongs to ignored thread */ #define CTX_FL_SUSPEND (1<<3) /* thread currently suspended */ #define CTX_FL_TRACING (1<<4) /* call at_tracing method */ #define CTX_FL_WAS_RUNNING (1<<5) /* thread was previously running */ #define CTX_FL_STOP_ON_RET (1<<6) /* can stop on method 'end' */ #define CTX_FL_IGNORE_STEPS (1<<7) /* doesn't countdown steps to break */ /* macro functions */ #define CTX_FL_TEST(c,f) ((c)->flags & (f)) #define CTX_FL_SET(c,f) do { (c)->flags |= (f); } while (0) #define CTX_FL_UNSET(c,f) do { (c)->flags &= ~(f); } while (0) /* types */ typedef enum { CTX_STOP_NONE, CTX_STOP_STEP, CTX_STOP_BREAKPOINT, CTX_STOP_CATCHPOINT } ctx_stop_reason; typedef struct { int calced_stack_size; int flags; ctx_stop_reason stop_reason; VALUE thread; int thnum; int dest_frame; /* next stop's frame if stopped by next */ int lines; /* # of lines in dest_frame before stopping */ int steps; /* # of steps before stopping */ int steps_out; /* # of returns before stopping */ VALUE backtrace; /* [[loc, self, klass, binding], ...] */ } debug_context_t; enum frame_component { LOCATION, SELF, CLASS, BINDING }; struct call_with_inspection_data { debug_context_t *dc; VALUE context_obj; ID id; int argc; VALUE *argv; }; typedef struct { st_table *tbl; } threads_table_t; enum bp_type { BP_POS_TYPE, BP_METHOD_TYPE }; enum hit_condition { HIT_COND_NONE, HIT_COND_GE, HIT_COND_EQ, HIT_COND_MOD }; typedef struct { int id; enum bp_type type; VALUE source; union { int line; ID mid; } pos; VALUE expr; VALUE enabled; int hit_count; int hit_value; enum hit_condition hit_condition; } breakpoint_t; /* functions from locker.c */ extern int is_in_locked(VALUE thread_id); extern void add_to_locked(VALUE thread); extern VALUE pop_from_locked(); extern void remove_from_locked(VALUE thread); /* functions from threads.c */ extern void Init_threads_table(VALUE mByebug); extern VALUE create_threads_table(void); extern void thread_context_lookup(VALUE thread, VALUE *context); extern int is_living_thread(VALUE thread); extern void acquire_lock(debug_context_t *dc); extern void release_lock(void); /* global variables */ extern VALUE threads; extern VALUE next_thread; /* functions from context.c */ extern void Init_context(VALUE mByebug); extern VALUE context_create(VALUE thread); extern VALUE context_dup(debug_context_t *context); extern void reset_stepping_stop_points(debug_context_t *context); extern VALUE call_with_debug_inspector(struct call_with_inspection_data *data); extern VALUE context_backtrace_set(const rb_debug_inspector_t *inspector, void *data); /* functions from breakpoint.c */ extern void Init_breakpoint(VALUE mByebug); extern VALUE catchpoint_hit_count(VALUE catchpoints, VALUE exception, VALUE *exception_name); extern VALUE find_breakpoint_by_pos(VALUE breakpoints, VALUE source, VALUE pos, VALUE bind); extern VALUE find_breakpoint_by_method(VALUE breakpoints, VALUE klass, VALUE mid, VALUE bind, VALUE self); #endif byebug-5.0.0/ext/byebug/context.c0000644000076400007640000003717012560626142015746 0ustar pravipravi#include static VALUE cContext; static VALUE cDebugThread; static int thnum_max = 0; /* "Step", "Next" and "Finish" do their work by saving information about where * to stop next. reset_stepping_stop_points removes/resets this information. */ extern void reset_stepping_stop_points(debug_context_t * context) { context->dest_frame = -1; context->lines = -1; context->steps = -1; context->steps_out = -1; } /* * call-seq: * context.dead? -> bool * * Returns +true+ if context doesn't represent a live context and is created * during post-mortem exception handling. */ static inline VALUE Context_dead(VALUE self) { debug_context_t *context; Data_Get_Struct(self, debug_context_t, context); return CTX_FL_TEST(context, CTX_FL_DEAD) ? Qtrue : Qfalse; } static void context_mark(void *data) { debug_context_t *context = (debug_context_t *) data; rb_gc_mark(context->backtrace); } static VALUE dc_backtrace(const debug_context_t * context) { return context->backtrace; } static int dc_stack_size(debug_context_t * context) { if (NIL_P(dc_backtrace(context))) return 0; return RARRAY_LENINT(dc_backtrace(context)); } extern VALUE context_create(VALUE thread) { debug_context_t *context = ALLOC(debug_context_t); context->flags = 0; context->thnum = ++thnum_max; context->thread = thread; reset_stepping_stop_points(context); context->stop_reason = CTX_STOP_NONE; rb_debug_inspector_open(context_backtrace_set, (void *)context); context->calced_stack_size = dc_stack_size(context) + 1; if (rb_obj_class(thread) == cDebugThread) CTX_FL_SET(context, CTX_FL_IGNORE); return Data_Wrap_Struct(cContext, context_mark, 0, context); } extern VALUE context_dup(debug_context_t * context) { debug_context_t *new_context = ALLOC(debug_context_t); memcpy(new_context, context, sizeof(debug_context_t)); reset_stepping_stop_points(new_context); new_context->backtrace = context->backtrace; CTX_FL_SET(new_context, CTX_FL_DEAD); return Data_Wrap_Struct(cContext, context_mark, 0, new_context); } static VALUE dc_frame_get(const debug_context_t * context, int frame_index, enum frame_component type) { VALUE frame; if (NIL_P(dc_backtrace(context))) rb_raise(rb_eRuntimeError, "Backtrace information is not available"); if (frame_index >= RARRAY_LENINT(dc_backtrace(context))) rb_raise(rb_eRuntimeError, "That frame doesn't exist!"); frame = rb_ary_entry(dc_backtrace(context), frame_index); return rb_ary_entry(frame, type); } static VALUE dc_frame_location(const debug_context_t * context, int frame_index) { return dc_frame_get(context, frame_index, LOCATION); } static VALUE dc_frame_self(const debug_context_t * context, int frame_index) { return dc_frame_get(context, frame_index, SELF); } static VALUE dc_frame_class(const debug_context_t * context, int frame_index) { return dc_frame_get(context, frame_index, CLASS); } static VALUE dc_frame_binding(const debug_context_t * context, int frame_index) { return dc_frame_get(context, frame_index, BINDING); } static VALUE load_backtrace(const rb_debug_inspector_t * inspector) { VALUE backtrace = rb_ary_new(); VALUE locs = rb_debug_inspector_backtrace_locations(inspector); int i; for (i = 0; i < RARRAY_LENINT(locs); i++) { VALUE frame = rb_ary_new(); rb_ary_push(frame, rb_ary_entry(locs, i)); rb_ary_push(frame, rb_debug_inspector_frame_self_get(inspector, i)); rb_ary_push(frame, rb_debug_inspector_frame_class_get(inspector, i)); rb_ary_push(frame, rb_debug_inspector_frame_binding_get(inspector, i)); rb_ary_push(backtrace, frame); } return backtrace; } extern VALUE context_backtrace_set(const rb_debug_inspector_t * inspector, void *data) { debug_context_t *dc = (debug_context_t *) data; dc->backtrace = load_backtrace(inspector); return Qnil; } static VALUE open_debug_inspector_i(const rb_debug_inspector_t * inspector, void *data) { struct call_with_inspection_data *cwi = (struct call_with_inspection_data *)data; cwi->dc->backtrace = load_backtrace(inspector); return rb_funcall2(cwi->context_obj, cwi->id, cwi->argc, cwi->argv); } static VALUE open_debug_inspector(struct call_with_inspection_data *cwi) { return rb_debug_inspector_open(open_debug_inspector_i, cwi); } static VALUE close_debug_inspector(struct call_with_inspection_data *cwi) { cwi->dc->backtrace = Qnil; return Qnil; } extern VALUE call_with_debug_inspector(struct call_with_inspection_data *data) { return rb_ensure(open_debug_inspector, (VALUE) data, close_debug_inspector, (VALUE) data); } #define FRAME_SETUP \ debug_context_t *context; \ VALUE frame_no; \ int frame_n; \ Data_Get_Struct(self, debug_context_t, context); \ if (!rb_scan_args(argc, argv, "01", &frame_no)) \ frame_n = 0; \ else \ frame_n = FIX2INT(frame_no); /* * call-seq: * context.frame_binding(frame_position = 0) -> binding * * Returns frame's binding. */ static VALUE Context_frame_binding(int argc, VALUE * argv, VALUE self) { FRAME_SETUP; return dc_frame_binding(context, frame_n); } /* * call-seq: * context.frame_class(frame_position = 0) -> binding * * Returns frame's defined class. */ static VALUE Context_frame_class(int argc, VALUE * argv, VALUE self) { FRAME_SETUP; return dc_frame_class(context, frame_n); } /* * call-seq: * context.frame_file(frame_position = 0) -> string * * Returns the name of the file in the frame. */ static VALUE Context_frame_file(int argc, VALUE * argv, VALUE self) { VALUE loc, absolute_path; FRAME_SETUP; loc = dc_frame_location(context, frame_n); absolute_path = rb_funcall(loc, rb_intern("absolute_path"), 0); if (!NIL_P(absolute_path)) return absolute_path; return rb_funcall(loc, rb_intern("path"), 0); } /* * call-seq: * context.frame_line(frame_position = 0) -> int * * Returns the line number in the file. */ static VALUE Context_frame_line(int argc, VALUE * argv, VALUE self) { VALUE loc; FRAME_SETUP; loc = dc_frame_location(context, frame_n); return rb_funcall(loc, rb_intern("lineno"), 0); } /* * call-seq: * context.frame_method(frame_position = 0) -> sym * * Returns the sym of the called method. */ static VALUE Context_frame_method(int argc, VALUE * argv, VALUE self) { VALUE loc; FRAME_SETUP; loc = dc_frame_location(context, frame_n); return rb_str_intern(rb_funcall(loc, rb_intern("label"), 0)); } /* * call-seq: * context.frame_self(frame_postion = 0) -> obj * * Returns self object of the frame. */ static VALUE Context_frame_self(int argc, VALUE * argv, VALUE self) { FRAME_SETUP; return dc_frame_self(context, frame_n); } /* * call-seq: * context.ignored? -> bool * * Returns the ignore flag for the context, which marks whether the associated * thread is ignored while debugging. */ static inline VALUE Context_ignored(VALUE self) { debug_context_t *context; Data_Get_Struct(self, debug_context_t, context); return CTX_FL_TEST(context, CTX_FL_IGNORE) ? Qtrue : Qfalse; } /* * call-seq: * context.resume -> nil * * Resumes thread from the suspended mode. */ static VALUE Context_resume(VALUE self) { debug_context_t *context; Data_Get_Struct(self, debug_context_t, context); if (!CTX_FL_TEST(context, CTX_FL_SUSPEND)) return Qnil; CTX_FL_UNSET(context, CTX_FL_SUSPEND); if (CTX_FL_TEST(context, CTX_FL_WAS_RUNNING)) rb_thread_wakeup(context->thread); return Qnil; } /* * call-seq: * context.backtrace-> int * * Returns the frame stack of a context. */ static inline VALUE Context_backtrace(VALUE self) { debug_context_t *context; Data_Get_Struct(self, debug_context_t, context); return dc_backtrace(context); } static VALUE Context_stop_reason(VALUE self) { debug_context_t *context; const char *symbol; Data_Get_Struct(self, debug_context_t, context); if (CTX_FL_TEST(context, CTX_FL_DEAD)) symbol = "post-mortem"; else switch (context->stop_reason) { case CTX_STOP_STEP: symbol = "step"; break; case CTX_STOP_BREAKPOINT: symbol = "breakpoint"; break; case CTX_STOP_CATCHPOINT: symbol = "catchpoint"; break; case CTX_STOP_NONE: default: symbol = "none"; } return ID2SYM(rb_intern(symbol)); } /* * call-seq: * context.step_into(steps, frame = 0) * * Stops the current context after a number of +steps+ are made from frame * +frame+ (by default the newest one). */ static VALUE Context_step_into(int argc, VALUE * argv, VALUE self) { VALUE steps, v_frame; int n_args, from_frame; debug_context_t *context; Data_Get_Struct(self, debug_context_t, context); if (context->calced_stack_size == 0) rb_raise(rb_eRuntimeError, "No frames collected."); n_args = rb_scan_args(argc, argv, "11", &steps, &v_frame); if (FIX2INT(steps) <= 0) rb_raise(rb_eRuntimeError, "Steps argument can't be negative."); from_frame = n_args == 1 ? 0 : FIX2INT(v_frame); if (from_frame < 0 || from_frame >= context->calced_stack_size) rb_raise(rb_eRuntimeError, "Destination frame (%d) is out of range (%d)", from_frame, context->calced_stack_size); else if (from_frame > 0) CTX_FL_SET(context, CTX_FL_IGNORE_STEPS); context->steps = FIX2INT(steps); context->dest_frame = context->calced_stack_size - from_frame; return steps; } /* * call-seq: * context.step_out(n_frames = 1, force = false) * * Stops after +n_frames+ frames are finished. +force+ parameter (if true) * ensures that the execution will stop in the specified frame even when there * are no more instructions to run. In that case, it will stop when the return * event for that frame is triggered. */ static VALUE Context_step_out(int argc, VALUE * argv, VALUE self) { int n_args, n_frames; VALUE v_frames, force; debug_context_t *context; n_args = rb_scan_args(argc, argv, "02", &v_frames, &force); n_frames = n_args == 0 ? 1 : FIX2INT(v_frames); Data_Get_Struct(self, debug_context_t, context); if (n_frames < 0 || n_frames > context->calced_stack_size) rb_raise(rb_eRuntimeError, "You want to finish %d frames, but stack size is only %d", n_frames, context->calced_stack_size); context->steps_out = n_frames; if (n_args == 2 && RTEST(force)) CTX_FL_SET(context, CTX_FL_STOP_ON_RET); else CTX_FL_UNSET(context, CTX_FL_STOP_ON_RET); return Qnil; } /* * call-seq: * context.step_over(lines, frame = 0) * * Steps over +lines+ lines in frame +frame+ (by default the newest one) or * higher (if frame +frame+ finishes). */ static VALUE Context_step_over(int argc, VALUE * argv, VALUE self) { int n_args, frame; VALUE lines, v_frame; debug_context_t *context; Data_Get_Struct(self, debug_context_t, context); if (context->calced_stack_size == 0) rb_raise(rb_eRuntimeError, "No frames collected."); n_args = rb_scan_args(argc, argv, "11", &lines, &v_frame); frame = n_args == 1 ? 0 : FIX2INT(v_frame); if (frame < 0 || frame >= context->calced_stack_size) rb_raise(rb_eRuntimeError, "Destination frame (%d) is out of range (%d)", frame, context->calced_stack_size); context->lines = FIX2INT(lines); context->dest_frame = context->calced_stack_size - frame; return Qnil; } /* * call-seq: * context.suspend -> nil * * Suspends the thread when it is running. */ static VALUE Context_suspend(VALUE self) { VALUE status; debug_context_t *context; Data_Get_Struct(self, debug_context_t, context); status = rb_funcall(context->thread, rb_intern("status"), 0); if (rb_str_cmp(status, rb_str_new2("run")) == 0) CTX_FL_SET(context, CTX_FL_WAS_RUNNING); else if (rb_str_cmp(status, rb_str_new2("sleep")) == 0) CTX_FL_UNSET(context, CTX_FL_WAS_RUNNING); else return Qnil; CTX_FL_SET(context, CTX_FL_SUSPEND); return Qnil; } /* * call-seq: * context.switch -> nil * * Switches execution to this context. */ static VALUE Context_switch(VALUE self) { debug_context_t *context; Data_Get_Struct(self, debug_context_t, context); next_thread = context->thread; context->steps = 1; context->steps_out = 0; CTX_FL_SET(context, CTX_FL_STOP_ON_RET); return Qnil; } /* * call-seq: * context.suspended? -> bool * * Returns +true+ if the thread is suspended by debugger. */ static VALUE Context_is_suspended(VALUE self) { debug_context_t *context; Data_Get_Struct(self, debug_context_t, context); return CTX_FL_TEST(context, CTX_FL_SUSPEND) ? Qtrue : Qfalse; } /* * call-seq: * context.thnum -> int * * Returns the context's number. */ static inline VALUE Context_thnum(VALUE self) { debug_context_t *context; Data_Get_Struct(self, debug_context_t, context); return INT2FIX(context->thnum); } /* * call-seq: * context.thread -> thread * * Returns the thread this context is associated with. */ static inline VALUE Context_thread(VALUE self) { debug_context_t *context; Data_Get_Struct(self, debug_context_t, context); return context->thread; } /* * call-seq: * context.tracing -> bool * * Returns the tracing flag for the current context. */ static VALUE Context_tracing(VALUE self) { debug_context_t *context; Data_Get_Struct(self, debug_context_t, context); return CTX_FL_TEST(context, CTX_FL_TRACING) ? Qtrue : Qfalse; } /* * call-seq: * context.tracing = bool * * Controls the tracing for this context. */ static VALUE Context_set_tracing(VALUE self, VALUE value) { debug_context_t *context; Data_Get_Struct(self, debug_context_t, context); if (RTEST(value)) CTX_FL_SET(context, CTX_FL_TRACING); else CTX_FL_UNSET(context, CTX_FL_TRACING); return value; } /* :nodoc: */ static VALUE dt_inherited(VALUE klass) { UNUSED(klass); rb_raise(rb_eRuntimeError, "Can't inherit Byebug::DebugThread class"); return Qnil; } /* * Document-class: Context * * == Summary * * Byebug keeps a single instance of this class. */ void Init_context(VALUE mByebug) { cContext = rb_define_class_under(mByebug, "Context", rb_cObject); rb_define_method(cContext, "backtrace", Context_backtrace, 0); rb_define_method(cContext, "dead?", Context_dead, 0); rb_define_method(cContext, "frame_binding", Context_frame_binding, -1); rb_define_method(cContext, "frame_class", Context_frame_class, -1); rb_define_method(cContext, "frame_file", Context_frame_file, -1); rb_define_method(cContext, "frame_line", Context_frame_line, -1); rb_define_method(cContext, "frame_method", Context_frame_method, -1); rb_define_method(cContext, "frame_self", Context_frame_self, -1); rb_define_method(cContext, "ignored?", Context_ignored, 0); rb_define_method(cContext, "resume", Context_resume, 0); rb_define_method(cContext, "step_into", Context_step_into, -1); rb_define_method(cContext, "step_out", Context_step_out, -1); rb_define_method(cContext, "step_over", Context_step_over, -1); rb_define_method(cContext, "stop_reason", Context_stop_reason, 0); rb_define_method(cContext, "suspend", Context_suspend, 0); rb_define_method(cContext, "suspended?", Context_is_suspended, 0); rb_define_method(cContext, "switch", Context_switch, 0); rb_define_method(cContext, "thnum", Context_thnum, 0); rb_define_method(cContext, "thread", Context_thread, 0); rb_define_method(cContext, "tracing", Context_tracing, 0); rb_define_method(cContext, "tracing=", Context_set_tracing, 1); cDebugThread = rb_define_class_under(mByebug, "DebugThread", rb_cThread); rb_define_singleton_method(cDebugThread, "inherited", dt_inherited, 1); }