byebug-10.0.1/0000755000004100000410000000000013256220560013077 5ustar www-datawww-databyebug-10.0.1/exe/0000755000004100000410000000000013256220560013660 5ustar www-datawww-databyebug-10.0.1/exe/byebug0000755000004100000410000000014313256220560015061 0ustar www-datawww-data#!/usr/bin/env ruby # frozen_string_literal: true require "byebug/runner" Byebug::Runner.new.run byebug-10.0.1/GUIDE.md0000644000004100000410000015605013256220560014265 0ustar www-datawww-data# GUIDE ## Introduction ### 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) eval 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) eval 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 `eval` (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 (or the abbreviation `q`). 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 raise("*** Need number of disks or no parameter") if n_args > 1 n = 3 if n_args > 0 begin n = $ARGV[0].to_i rescue ValueError raise("*** Expecting an integer, got: #{$ARGV[0]}") end end raise("*** Number of disks should be between 1 and 100") if n < 1 || n > 100 hanoi(n, :a, :b, :c) ``` 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 DelegateClass Digest timeout initialize_copy initialize_dup initialize_clone sprintf format Integer Float String Array Hash warn raise fail global_variables __method__ __callee__ __dir__ eval local_variables iterator? block_given? catch throw loop respond_to_missing? trace_var untrace_var at_exit syscall open printf print putc puts gets readline select readlines ` p test srand rand trap load require require_relative autoload autoload? proc lambda binding caller caller_locations exec fork exit! system spawn sleep exit abort Rational Complex set_trace_func gem_original_require Pathname pp y URI rubygems_require initialize singleton_method_added singleton_method_removed singleton_method_undefined method_missing (byebug) private_methods.member?(:hanoi) false ``` `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. You can use any Ruby to inspect your program's state at the place it is stopped. Now let's see what happens after stepping: ```bash (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: raise("*** 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/exe/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: raise("*** Number of disks should be between 1 and 100") if n < 1 || n > 100 27: => 28: hanoi(n, :a, :b, :c) (byebug) n_args 1 (byebug) eval 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) eval n 2 ``` Notice in the above to get the value of variable `n` we had to use a print command like `eval 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) 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 linetrace 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). 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 path 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 lib 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 to `q`). Normally, if you are in an interactive session, this command will prompt to ask if you really want to quit. If you want to quit without being prompted, enter `quit unconditionally` (abbreviated to `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/core" 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, 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_history`. 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 break -- Sets breakpoints in the source code catch -- Handles exception catchpoints condition -- Sets conditions on breakpoints continue -- Runs until program ends, hits a breakpoint or reaches a line delete -- Deletes breakpoints disable -- Disables breakpoints or displays display -- Evaluates expressions every time the debugger stops down -- Moves to a lower frame in the stack trace edit -- Edits source files enable -- Enables breakpoints or displays finish -- Runs the program until frame returns frame -- Moves to a frame in the call stack help -- Helps you using byebug history -- Shows byebug's history of commands info -- Shows several informations about the program being debugged interrupt -- Interrupts the program irb -- Starts an IRB session kill -- Sends a signal to the current process list -- Lists lines of source code method -- Shows methods of an object, class or module next -- Runs one or more lines of code pry -- Starts a Pry session quit -- Exits byebug restart -- Restarts the debugged program save -- Saves current byebug session to a file set -- Modifies byebug settings show -- Shows byebug settings source -- Restores a previously saved byebug session step -- Steps into blocks or methods one or more times thread -- Commands to manipulate threads tracevar -- Enables tracing of a global variable undisplay -- Stops displaying all or some expressions when program stops untracevar -- Stops tracing a global variable up -- Moves to a higher frame in the stack trace var -- Shows variables and its values where -- Displays the backtrace ``` With a command name, `help` displays information on how to use the command. ```bash (byebug) help list l[ist][[-=]][ nn-mm] Lists lines of source code Lists lines 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. (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 to `q`). Normally, if you are in an interactive session, this command will prompt you to confirm you really want to quit. If you want to quit without being prompted, enter `quit unconditionally` (abbreviated to `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. ### Evaluation of expressions: irb, pry To examine and change data in your script you can just evaluate any Ruby code from `byebug`'s prompt. Any input that is not recognized as a command will be evaluated, so `byebug` essentially works as a REPL. If you want to evaluate something that conflicts with a `byebug` command, just use Ruby's `eval`. For example, if you want to print a variable called `n`, type `eval n` because typing just `n` will execute `byebug`'s command `next`. Finally, if you need more advanced functionality from REPL's, you can enter `irb` or `pry` using `irb` or `pry` commands. The binding's 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 irb(main):001:0> (0..6).inject { |sum, i| sum += i } => 21 irb(main):002:0> exit (byebug) ``` ### Printing variables: var 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 `.instance_methods(false)`. * `method `. Show methods of the class or module ``. Basically this is the same as running `.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-10.0.1/lib/0000755000004100000410000000000013256220560013645 5ustar www-datawww-databyebug-10.0.1/lib/byebug.rb0000644000004100000410000000007113256220560015445 0ustar www-datawww-data# frozen_string_literal: true require "byebug/attacher" byebug-10.0.1/lib/byebug/0000755000004100000410000000000013256220560015122 5ustar www-datawww-databyebug-10.0.1/lib/byebug/history.rb0000644000004100000410000000520513256220560017152 0ustar www-datawww-data# frozen_string_literal: true begin require "readline" rescue LoadError warn <<-MESSAGE 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. MESSAGE raise end module Byebug # # Handles byebug's history of commands. # class History attr_accessor :size def initialize self.size = 0 end # # Array holding the list of commands in history # def buffer Readline::HISTORY.to_a 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] File.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 = buffer.last(show_size) last_ids(show_size).zip(commands).map do |l| format("%5d %s", position: l[0], command: l[1]) end.join("\n") + "\n" end # # Array of ids of the last +number+ commands. # def last_ids(number) (1 + size - number..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.empty? buffer[Readline::HISTORY.length - 1] == buf end end end byebug-10.0.1/lib/byebug/subcommands.rb0000644000004100000410000000211713256220560017763 0ustar www-datawww-data# frozen_string_literal: true require "forwardable" require "byebug/helpers/reflection" require "byebug/command_list" module Byebug # # Subcommand additions. # module Subcommands def self.included(command) command.extend(ClassMethods) end extend Forwardable def_delegators "self.class", :subcommand_list # # Delegates to subcommands or prints help if no subcommand specified. # def execute subcmd_name = @match[1] return puts(help) unless subcmd_name subcmd = subcommand_list.match(subcmd_name) raise CommandNotFound.new(subcmd_name, self.class) unless subcmd subcmd.new(processor, arguments).execute end # # Class methods added to subcommands # module ClassMethods include Helpers::ReflectionHelper # # Default help text for a command with subcommands # def help super + subcommand_list.to_s end # # Command's subcommands. # def subcommand_list @subcommand_list ||= CommandList.new(commands) end end end end byebug-10.0.1/lib/byebug/source_file_formatter.rb0000644000004100000410000000247713256220560022043 0ustar www-datawww-data# frozen_string_literal: true require "byebug/helpers/file" require "byebug/setting" module Byebug # # Formats specific line ranges in a source file # class SourceFileFormatter include Helpers::FileHelper attr_reader :file, :annotator def initialize(file, annotator) @file = file @annotator = annotator end def lines(min, max) File.foreach(file).with_index.map do |line, lineno| next unless (min..max).cover?(lineno + 1) format( "%s %#{max.to_s.size}d: %s", annotation: annotator.call(lineno + 1), lineno: lineno + 1, source: line ) end end def lines_around(center) lines(*range_around(center)) end def range_around(center) range_from(center - size / 2) end def range_from(min) first = amend_initial(min) [first, first + size - 1] end def amend_initial(line) amend(line, max_initial_line) end def amend_final(line) amend(line, max_line) end def max_initial_line max_line - size + 1 end def max_line @max_line ||= n_lines(file) end def size [Setting[:listsize], max_line].min end def amend(line, ceiling) [ceiling, [1, line].max].min end end end byebug-10.0.1/lib/byebug/interface.rb0000644000004100000410000000606713256220560017420 0ustar www-datawww-data# frozen_string_literal: true require "byebug/setting" require "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 @last_line = "" end def last_if_empty(input) @last_line = input.empty? ? @last_line : input end # # Pops a command from the input stream. # def read_command(prompt) return command_queue.shift unless command_queue.empty? read_input(prompt) 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, parses it into # commands and saves it to history. # # @return [String] Representing something to be run by the debugger. # def read_input(prompt, save_hist = true) line = prepare_input(prompt) return unless line history.push(line) if save_hist command_queue.concat(split_commands(line)) command_queue.shift end # # Reads a new line from the interface's input stream. # # @return [String] New string read or the previous string if the string # read now was empty. # def prepare_input(prompt) line = readline(prompt) return unless line last_if_empty(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 # # Prints an output message to the output stream without a final "\n". # 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.strip 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-10.0.1/lib/byebug/breakpoint.rb0000644000004100000410000000534413256220560017613 0ustar www-datawww-data# frozen_string_literal: true module 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 id [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)}" iseq = RubyVM::InstructionSequence.compile(File.read(filename), name) if iseq.respond_to?(:each_child) potential_lines_with_trace_points(iseq, {}) else potential_lines_without_trace_points(iseq, {}) end end def self.potential_lines_with_trace_points(iseq, lines) iseq.trace_points.each { |(line, _)| lines[line] = true } iseq.each_child do |child| potential_lines_with_trace_points(child, lines) end lines.keys.sort end private_class_method :potential_lines_with_trace_points def self.potential_lines_without_trace_points(iseq, lines) 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 private_class_method :potential_lines_without_trace_points # # 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 { |field| "#{field}: #{send(field)}" }.join(", ") "#" end end end byebug-10.0.1/lib/byebug/remote/0000755000004100000410000000000013256220560016415 5ustar www-datawww-databyebug-10.0.1/lib/byebug/remote/client.rb0000644000004100000410000000221613256220560020221 0ustar www-datawww-data# frozen_string_literal: true require "socket" module Byebug module Remote # # Client for remote debugging # class Client attr_reader :interface, :socket def initialize(interface) @interface = interface @socket = nil end # # Connects to the remote byebug # def start(host = "localhost", port = PORT) connect_at(host, port) while (line = socket.gets) case line when /^PROMPT (.*)$/ input = interface.read_command(Regexp.last_match[1]) break unless input socket.puts input when /^CONFIRM (.*)$/ input = interface.readline(Regexp.last_match[1]) break unless input socket.puts input else interface.puts line end end socket.close end def started? !socket.nil? end private def connect_at(host, port) interface.puts "Connecting to byebug server at #{host}:#{port}..." @socket = TCPSocket.new(host, port) interface.puts "Connected." end end end end byebug-10.0.1/lib/byebug/remote/server.rb0000644000004100000410000000173313256220560020254 0ustar www-datawww-data# frozen_string_literal: true require "socket" module Byebug module Remote # # Server for remote debugging # class Server attr_reader :actual_port, :wait_connection def initialize(wait_connection:, &block) @thread = nil @wait_connection = wait_connection @main_loop = block end # # Start the remote debugging server # def start(host, port) return if @thread if wait_connection mutex = Mutex.new proceed = ConditionVariable.new end server = TCPServer.new(host, port) @actual_port = server.addr[1] yield if block_given? @thread = DebugThread.new do while (session = server.accept) @main_loop.call(session) mutex.synchronize { proceed.signal } if wait_connection end end mutex.synchronize { proceed.wait(mutex) } if wait_connection end end end end byebug-10.0.1/lib/byebug/errors.rb0000644000004100000410000000077213256220560016771 0ustar www-datawww-data# frozen_string_literal: true module Byebug # # Custom exception exception to signal "command not found" errors # class CommandNotFound < NoMethodError def initialize(input, parent = nil) @input = input @parent = parent super("Unknown command '#{name}'. Try '#{help}'") end private def name build_cmd(@parent, @input) end def help build_cmd("help", @parent) end def build_cmd(*args) args.compact.join(" ") end end end byebug-10.0.1/lib/byebug/commands/0000755000004100000410000000000013256220560016723 5ustar www-datawww-databyebug-10.0.1/lib/byebug/commands/display.rb0000644000004100000410000000274013256220560020720 0ustar www-datawww-data# frozen_string_literal: true require "byebug/command" require "byebug/helpers/eval" module Byebug # # Custom expressions to be displayed every time the debugger stops. # class DisplayCommand < Command include Helpers::EvalHelper self.allow_in_post_mortem = false self.always_run = 2 def self.regexp /^\s* disp(?:lay)? (?:\s+ (.+))? \s*$/x end def self.description <<-DESCRIPTION disp[lay][ ] #{short_description} If specified, adds into display expression list. Otherwise, it lists all expressions. DESCRIPTION end def self.short_description "Evaluates expressions every time the debugger stops" end def execute return print_display_expressions unless @match && @match[1] Byebug.displays.push [true, @match[1]] display_expression(@match[1]) end private def display_expression(exp) print pr("display.result", n: Byebug.displays.size, exp: exp, result: eval_expr(exp)) end def print_display_expressions result = prc("display.result", Byebug.displays) do |item, index| active, exp = item { n: index + 1, exp: exp, result: eval_expr(exp) } if active end print result end def eval_expr(expression) error_eval(expression).inspect rescue StandardError "(undefined)" end end end byebug-10.0.1/lib/byebug/commands/show.rb0000644000004100000410000000153013256220560020227 0ustar www-datawww-data# frozen_string_literal: true require "byebug/command" module Byebug # # Show byebug settings. # class ShowCommand < Command self.allow_in_control = true self.allow_in_post_mortem = true def self.regexp /^\s* show (?:\s+(?\w+))? \s*$/x end def self.description <<-DESCRIPTION show #{short_description} You can change them with the "set" command. DESCRIPTION end def self.short_description "Shows byebug settings" end def self.help super + Setting.help_all 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 end end byebug-10.0.1/lib/byebug/commands/history.rb0000644000004100000410000000134213256220560020751 0ustar www-datawww-data# frozen_string_literal: true require "byebug/command" require "byebug/helpers/parse" module Byebug # # Show history of byebug commands. # class HistoryCommand < Command include Helpers::ParseHelper self.allow_in_post_mortem = true def self.regexp /^\s* hist(?:ory)? (?:\s+(?.+))? \s*$/x end def self.description <<-DESCRIPTION hist[ory][ num_cmds] #{short_description} DESCRIPTION end def self.short_description "Shows byebug's history of commands" end def execute history = processor.interface.history size, = get_int(@match[:num_cmds], "history", 1) if @match[:num_cmds] puts history.to_s(size) end end end byebug-10.0.1/lib/byebug/commands/where.rb0000644000004100000410000000223713256220560020366 0ustar www-datawww-data# frozen_string_literal: true require "pathname" require "byebug/command" require "byebug/helpers/frame" module Byebug # # Show current backtrace. # class WhereCommand < Command include Helpers::FrameHelper self.allow_in_post_mortem = true def self.regexp /^\s* (?:w(?:here)?|bt|backtrace) \s*$/x end def self.description <<-DESCRIPTION w[here]|bt|backtrace #{short_description} 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. DESCRIPTION end def self.short_description "Displays the backtrace" end def execute print_backtrace end private def print_backtrace bt = prc("frame.line", (0...context.stack_size)) do |_, index| Frame.new(context, index).to_hash end print(bt) end end end byebug-10.0.1/lib/byebug/commands/condition.rb0000644000004100000410000000267613256220560021251 0ustar www-datawww-data# frozen_string_literal: true require "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 = true def self.regexp /^\s* cond(?:ition)? (?:\s+(\d+)(?:\s+(.*))?)? \s*$/x end def self.description <<-DESCRIPTION cond[ition] [ expr] #{short_description} 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. DESCRIPTION end def self.short_description "Sets conditions on breakpoints" 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 end end byebug-10.0.1/lib/byebug/commands/source.rb0000644000004100000410000000153713256220560020556 0ustar www-datawww-data# frozen_string_literal: true require "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 self.allow_in_post_mortem = true def self.regexp /^\s* so(?:urce)? (?:\s+(\S+))? \s*$/x end def self.description <<-DESCRIPTION source #{short_description} DESCRIPTION end def self.short_description "Restores a previously saved byebug session" end def execute return puts(help) unless @match[1] file = File.expand_path(@match[1]).strip unless File.exist?(file) return errmsg(pr("source.errors.not_found", file: file)) end processor.interface.read_file(file) end end end byebug-10.0.1/lib/byebug/commands/next.rb0000644000004100000410000000142213256220560020225 0ustar www-datawww-data# frozen_string_literal: true require "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 def self.regexp /^\s* n(?:ext)? (?:\s+(\S+))? \s*$/x end def self.description <<-DESCRIPTION n[ext][ nnn] #{short_description} DESCRIPTION end def self.short_description "Runs one or more lines of code" end def execute steps, err = parse_steps(@match[1], "Next") return errmsg(err) unless steps context.step_over(steps, context.frame.pos) processor.proceed! end end end byebug-10.0.1/lib/byebug/commands/down.rb0000644000004100000410000000160113256220560020215 0ustar www-datawww-data# frozen_string_literal: true 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 self.allow_in_post_mortem = true def self.regexp /^\s* down (?:\s+(\S+))? \s*$/x end def self.description <<-DESCRIPTION down[ count] #{short_description} Use the "bt" command to find out where you want to go. DESCRIPTION end def self.short_description "Moves to a lower frame in the stack trace" end def execute pos, err = parse_steps(@match[1], "Down") return errmsg(err) unless pos jump_frames(-pos) ListCommand.new(processor).execute if Setting[:autolist] end end end byebug-10.0.1/lib/byebug/commands/quit.rb0000644000004100000410000000155113256220560020234 0ustar www-datawww-data# frozen_string_literal: true require "byebug/command" module Byebug # # Exit from byebug. # class QuitCommand < Command self.allow_in_control = true self.allow_in_post_mortem = true def self.regexp /^\s* q(?:uit)? \s* (?:(!|\s+unconditionally))? \s*$/x end def self.description <<-DESCRIPTION q[uit] [!|unconditionally] #{short_description} Normally we prompt before exiting. However if the parameter "unconditionally" is given or command is suffixed with !, we exit without asking further questions. DESCRIPTION end def self.short_description "Exits byebug" end def execute return unless @match[1] || confirm(pr("quit.confirmations.really")) processor.interface.autosave processor.interface.close Process.exit! end end end byebug-10.0.1/lib/byebug/commands/step.rb0000644000004100000410000000153613256220560020230 0ustar www-datawww-data# frozen_string_literal: true require "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 def self.regexp /^\s* s(?:tep)? (?:\s+(\S+))? \s*$/x end def self.description <<-DESCRIPTION s[tep][ times] #{short_description} DESCRIPTION end def self.short_description "Steps into blocks or methods one or more times" end def execute steps, err = parse_steps(@match[1], "Steps") return errmsg(err) unless steps context.step_into(steps, context.frame.pos) processor.proceed! end end end byebug-10.0.1/lib/byebug/commands/tracevar.rb0000644000004100000410000000261513256220560021063 0ustar www-datawww-data# frozen_string_literal: true require "byebug/command" module Byebug # # Show (and possibily stop) at every line that changes a global variable. # class TracevarCommand < Command def self.regexp /^\s* tr(?:acevar)? (?: \s+ (\S+))? # (variable-name)? (?: \s+ (stop|nostop))? \s*$/x end def self.description <<-DESCRIPTION tr[acevar] [[no]stop] #{short_description} 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. DESCRIPTION end def self.short_description "Enables tracing of a global variable" 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 private def on_change(name, value, stop) puts pr("trace.messages.on_change", name: name, value: value) context.step_out(1, false) if stop end end end byebug-10.0.1/lib/byebug/commands/thread/0000755000004100000410000000000013256220560020172 5ustar www-datawww-databyebug-10.0.1/lib/byebug/commands/thread/resume.rb0000644000004100000410000000171513256220560022023 0ustar www-datawww-data# frozen_string_literal: true require "byebug/helpers/thread" module Byebug # # Reopens the +thread+ command to define the +resume+ subcommand # class ThreadCommand < Command # # Resumes the specified thread # class ResumeCommand < Command include Helpers::ThreadHelper def self.regexp /^\s* r(?:esume)? (?: \s* (\d+))? \s*$/x end def self.description <<-DESCRIPTION th[read] r[esume] #{short_description} DESCRIPTION end def self.short_description "Resumes execution of the specified thread" 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 end end end byebug-10.0.1/lib/byebug/commands/thread/stop.rb0000644000004100000410000000153213256220560021505 0ustar www-datawww-data# frozen_string_literal: true require "byebug/helpers/thread" module Byebug # # Reopens the +thread+ command to define the +stop+ subcommand # class ThreadCommand < Command # # Stops the specified thread # class StopCommand < Command include Helpers::ThreadHelper def self.regexp /^\s* st(?:op)? (?: \s* (\d+))? \s*$/x end def self.description <<-DESCRIPTION th[read] st[op] #{short_description} DESCRIPTION end def self.short_description "Stops the execution of the specified thread" 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 end end end byebug-10.0.1/lib/byebug/commands/thread/list.rb0000644000004100000410000000147013256220560021474 0ustar www-datawww-data# frozen_string_literal: true require "byebug/helpers/thread" module Byebug # # Reopens the +thread+ command to define the +list+ subcommand # class ThreadCommand < Command # # Information about threads # class ListCommand < Command include Helpers::ThreadHelper def self.regexp /^\s* l(?:ist)? \s*$/x end def self.description <<-DESCRIPTION th[read] l[ist] #{short_description} DESCRIPTION end def self.short_description "Lists all threads" 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 end end end byebug-10.0.1/lib/byebug/commands/thread/switch.rb0000644000004100000410000000160313256220560022020 0ustar www-datawww-data# frozen_string_literal: true require "byebug/helpers/thread" module Byebug # # Reopens the +thread+ command to define the +switch+ subcommand # class ThreadCommand < Command # # Switches to the specified thread # class SwitchCommand < Command include Helpers::ThreadHelper def self.regexp /^\s* sw(?:itch)? (?: \s* (\d+))? \s*$/x end def self.description <<-DESCRIPTION th[read] sw[itch] #{short_description} DESCRIPTION end def self.short_description "Switches execution to the specified thread" 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 processor.proceed! end end end end byebug-10.0.1/lib/byebug/commands/thread/current.rb0000644000004100000410000000126013256220560022200 0ustar www-datawww-data# frozen_string_literal: true require "byebug/helpers/thread" module Byebug # # Reopens the +thread+ command to define the +current+ subcommand # class ThreadCommand < Command # # Information about the current thread # class CurrentCommand < Command include Helpers::ThreadHelper def self.regexp /^\s* c(?:urrent)? \s*$/x end def self.description <<-DESCRIPTION th[read] c[urrent] #{short_description} DESCRIPTION end def self.short_description "Shows current thread information" end def execute display_context(context) end end end end byebug-10.0.1/lib/byebug/commands/var.rb0000644000004100000410000000131513256220560020040 0ustar www-datawww-data# frozen_string_literal: true require "byebug/subcommands" require "byebug/commands/var/all" require "byebug/commands/var/args" 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 self.allow_in_post_mortem = true def self.regexp /^\s* v(?:ar)? (?:\s+ (.+))? \s*$/x end def self.description <<-DESCRIPTION [v]ar #{short_description} DESCRIPTION end def self.short_description "Shows variables and its values" end end end byebug-10.0.1/lib/byebug/commands/var/0000755000004100000410000000000013256220560017513 5ustar www-datawww-databyebug-10.0.1/lib/byebug/commands/var/const.rb0000644000004100000410000000203313256220560021164 0ustar www-datawww-data# frozen_string_literal: true require "byebug/helpers/eval" module Byebug # # Reopens the +var+ command to define the +const+ subcommand # class VarCommand < Command # # Shows constants # class ConstCommand < Command include Helpers::EvalHelper self.allow_in_post_mortem = true def self.regexp /^\s* c(?:onst)? (?:\s+ (.+))? \s*$/x end def self.description <<-DESCRIPTION v[ar] c[onstant] #{short_description} DESCRIPTION end def self.short_description "Shows constants of an object." end def execute obj = warning_eval(str_obj) unless obj.is_a?(Module) return errmsg(pr("variable.errors.not_module", object: str_obj)) end constants = warning_eval("#{str_obj}.constants") puts prv(constants.sort.map { |c| [c, obj.const_get(c)] }, "constant") end private def str_obj @str_obj ||= @match[1] || "self.class" end end end end byebug-10.0.1/lib/byebug/commands/var/global.rb0000644000004100000410000000120213256220560021273 0ustar www-datawww-data# frozen_string_literal: true module Byebug # # Reopens the +var+ command to define the +global+ subcommand # class VarCommand < Command # # Shows global variables # class GlobalCommand < Command include Helpers::VarHelper self.allow_in_post_mortem = true def self.regexp /^\s* g(?:lobal)? \s*$/x end def self.description <<-DESCRIPTION v[ar] g[lobal] #{short_description} DESCRIPTION end def self.short_description "Shows global variables." end def execute var_global end end end end byebug-10.0.1/lib/byebug/commands/var/args.rb0000644000004100000410000000132113256220560020771 0ustar www-datawww-data# frozen_string_literal: true require "byebug/helpers/var" module Byebug # # Reopens the +var+ command to define the +args+ subcommand # class VarCommand < Command # # Information about arguments of the current method/block # class ArgsCommand < Command include Helpers::VarHelper self.allow_in_post_mortem = true def self.regexp /^\s* a(?:rgs)? \s*$/x end def self.description <<-DESCRIPTION v[ar] a[args] #{short_description} DESCRIPTION end def self.short_description "Information about arguments of the current scope" end def execute var_args end end end end byebug-10.0.1/lib/byebug/commands/var/local.rb0000644000004100000410000000127313256220560021135 0ustar www-datawww-data# frozen_string_literal: true require "byebug/helpers/var" module Byebug # # Reopens the +var+ command to define the +local+ subcommand # class VarCommand < Command # # Shows local variables in current scope # class LocalCommand < Command include Helpers::VarHelper self.allow_in_post_mortem = true def self.regexp /^\s* l(?:ocal)? \s*$/x end def self.description <<-DESCRIPTION v[ar] l[ocal] #{short_description} DESCRIPTION end def self.short_description "Shows local variables in current scope." end def execute var_local end end end end byebug-10.0.1/lib/byebug/commands/var/all.rb0000644000004100000410000000136313256220560020613 0ustar www-datawww-data# frozen_string_literal: true require "byebug/helpers/var" module Byebug # # Reopens the +var+ command to define the +all+ subcommand # class VarCommand < Command # # Shows global, instance and local variables # class AllCommand < Command include Helpers::VarHelper self.allow_in_post_mortem = true def self.regexp /^\s* a(?:ll)? \s*$/x end def self.description <<-DESCRIPTION v[ar] a[ll] #{short_description} DESCRIPTION end def self.short_description "Shows local, global and instance variables of self." end def execute var_global var_instance("self") var_local end end end end byebug-10.0.1/lib/byebug/commands/var/instance.rb0000644000004100000410000000135713256220560021652 0ustar www-datawww-data# frozen_string_literal: true require "byebug/helpers/var" module Byebug # # Reopens the +var+ command to define the +instance+ subcommand # class VarCommand < Command # # Shows instance variables # class InstanceCommand < Command include Helpers::VarHelper self.allow_in_post_mortem = true def self.regexp /^\s* i(?:nstance)? (?:\s+ (.+))? \s*$/x end def self.description <<-DESCRIPTION v[ar] i[nstance][ ] #{short_description} DESCRIPTION end def self.short_description "Shows instance variables of self or a specific object." end def execute var_instance(@match[1]) end end end end byebug-10.0.1/lib/byebug/commands/set.rb0000644000004100000410000000343513256220560020050 0ustar www-datawww-data# frozen_string_literal: true require "byebug/command" require "byebug/helpers/parse" module Byebug # # Change byebug settings. # class SetCommand < Command include Helpers::ParseHelper self.allow_in_control = true self.allow_in_post_mortem = true def self.regexp /^\s* set (?:\s+(?\w+))? (?:\s+(?\S+))? \s*$/x end def self.description <<-DESCRIPTION set #{short_description} 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. DESCRIPTION end def self.short_description "Modifies byebug settings" end def self.help super + Setting.help_all 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 private 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 end end byebug-10.0.1/lib/byebug/commands/interrupt.rb0000644000004100000410000000105113256220560021301 0ustar www-datawww-data# frozen_string_literal: true require "byebug/command" module Byebug # # Interrupting execution of current thread. # class InterruptCommand < Command self.allow_in_control = true def self.regexp /^\s*int(?:errupt)?\s*$/ end def self.description <<-DESCRIPTION int[errupt] #{short_description} DESCRIPTION end def self.short_description "Interrupts the program" end def execute Byebug.start Byebug.thread_context(Thread.main).interrupt end end end byebug-10.0.1/lib/byebug/commands/enable/0000755000004100000410000000000013256220560020151 5ustar www-datawww-databyebug-10.0.1/lib/byebug/commands/enable/display.rb0000644000004100000410000000176313256220560022152 0ustar www-datawww-data# frozen_string_literal: true require "byebug/helpers/toggle" module Byebug # # Reopens the +enable+ command to define the +display+ subcommand # class EnableCommand < Command # # Enables all or specific displays # class DisplayCommand < Command include Helpers::ToggleHelper self.allow_in_post_mortem = true def self.regexp /^\s* d(?:isplay)? (?:\s+ (.+))? \s*$/x end def self.description <<-DESCRIPTION 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. DESCRIPTION end def self.short_description "Enables expressions to be displayed when program stops." end def execute enable_disable_display("enable", @match[1]) end end end end byebug-10.0.1/lib/byebug/commands/enable/breakpoints.rb0000644000004100000410000000164413256220560023024 0ustar www-datawww-data# frozen_string_literal: true require "byebug/helpers/toggle" module Byebug # # Reopens the +enable+ command to define the +breakpoints+ subcommand # class EnableCommand < Command # # Enables all or specific breakpoints # class BreakpointsCommand < Command include Helpers::ToggleHelper self.allow_in_post_mortem = true def self.regexp /^\s* b(?:reakpoints)? (?:\s+ (.+))? \s*$/x end def self.description <<-DESCRIPTION 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. DESCRIPTION end def self.short_description "Enable all or specific breakpoints" end def execute enable_disable_breakpoints("enable", @match[1]) end end end end byebug-10.0.1/lib/byebug/commands/up.rb0000644000004100000410000000156713256220560017705 0ustar www-datawww-data# frozen_string_literal: true 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 self.allow_in_post_mortem = true def self.regexp /^\s* up (?:\s+(\S+))? \s*$/x end def self.description <<-DESCRIPTION up[ count] #{short_description} Use the "bt" command to find out where you want to go. DESCRIPTION end def self.short_description "Moves to a higher frame in the stack trace" end def execute pos, err = parse_steps(@match[1], "Up") return errmsg(err) unless pos jump_frames(pos) ListCommand.new(processor).execute if Setting[:autolist] end end end byebug-10.0.1/lib/byebug/commands/continue.rb0000644000004100000410000000215613256220560021100 0ustar www-datawww-data# frozen_string_literal: true require "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 self.regexp /^\s* c(?:ont(?:inue)?)? (?:\s+(\S+))? \s*$/x end def self.description <<-DESCRIPTION c[ont[inue]][ ] #{short_description} DESCRIPTION end def self.short_description "Runs until program ends, hits a breakpoint or reaches a line" end def execute if @match[1] num, err = get_int(@match[1], "Continue", 0, nil) return errmsg(err) unless num filename = File.expand_path(frame.file) unless Breakpoint.potential_line?(filename, num) return errmsg(pr("continue.errors.unstopped_line", line: num)) end Breakpoint.add(filename, num) end processor.proceed! Byebug.stop if Byebug.stoppable? end end end byebug-10.0.1/lib/byebug/commands/catch.rb0000644000004100000410000000373013256220560020335 0ustar www-datawww-data# frozen_string_literal: true require "byebug/command" require "byebug/helpers/eval" module Byebug # # Implements exception catching. # # Enables the user to catch unhandled assertion when they happen. # class CatchCommand < Command include Helpers::EvalHelper self.allow_in_post_mortem = true def self.regexp /^\s* cat(?:ch)? (?:\s+(\S+))? (?:\s+(off))? \s*$/x end def self.description <<-DESCRIPTION cat[ch][ (off|[ off])] #{short_description} catch -- lists catchpoints catch off -- deletes all catchpoints catch -- enables handling catch off -- disables handling DESCRIPTION end def self.short_description "Handles exception catchpoints" end def execute return info unless @match[1] return @match[1] == "off" ? clear : add(@match[1]) unless @match[2] return errmsg pr("catch.errors.off", off: cmd) unless @match[2] == "off" remove(@match[1]) end private def remove(exception) unless Byebug.catchpoints.member?(exception) return errmsg pr("catch.errors.not_found", exception: exception) end puts pr("catch.removed", exception: exception) Byebug.catchpoints.delete(exception) end def add(exception) if warning_eval(exception.is_a?(Class).to_s) errmsg pr("catch.errors.not_class", class: exception) end puts pr("catch.added", exception: exception) Byebug.add_catchpoint(exception) end def clear Byebug.catchpoints.clear if confirm(pr("catch.confirmations.delete_all")) end def info if Byebug.catchpoints && !Byebug.catchpoints.empty? Byebug.catchpoints.each_key do |exception| puts("#{exception}: #{exception.is_a?(Class)}") end else puts "No exceptions set to be caught." end end end end byebug-10.0.1/lib/byebug/commands/info/0000755000004100000410000000000013256220560017656 5ustar www-datawww-databyebug-10.0.1/lib/byebug/commands/info/display.rb0000644000004100000410000000215413256220560021652 0ustar www-datawww-data# frozen_string_literal: true module Byebug # # Reopens the +info+ command to define the +display+ subcommand # class InfoCommand < Command # # Information about display expressions # class DisplayCommand < Command self.allow_in_post_mortem = true def self.regexp /^\s* d(?:isplay)? \s*$/x end def self.description <<-DESCRIPTION inf[o] d[display] #{short_description} DESCRIPTION end def self.short_description "List of expressions to display when program stops" end def execute unless Byebug.displays.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" Byebug.displays.each_with_index do |d, i| interp = format( "%3d: %s %s", number: i + 1, status: d[0] ? "y" : "n", expression: d[1] ) puts(interp) end end end end end byebug-10.0.1/lib/byebug/commands/info/breakpoints.rb0000644000004100000410000000317413256220560022531 0ustar www-datawww-data# frozen_string_literal: true module Byebug # # Reopens the +info+ command to define the +breakpoints+ subcommand # class InfoCommand < Command # # Information about current breakpoints # class BreakpointsCommand < Command self.allow_in_post_mortem = true def self.regexp /^\s* b(?:reakpoints)? (?:\s+ (.+))? \s*$/x end def self.description <<-DESCRIPTION inf[o] b[reakpoints] #{short_description} DESCRIPTION end def self.short_description "Status of user settable breakpoints" 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 private def info_breakpoint(brkpt) interp = format( "%-3d %-3s at %s:%s%s", id: brkpt.id, status: brkpt.enabled? ? "y" : "n", file: brkpt.source, line: brkpt.pos, expression: brkpt.expr.nil? ? "" : " if #{brkpt.expr}" ) puts interp hits = brkpt.hit_count return unless hits > 0 s = hits > 1 ? "s" : "" puts " breakpoint already hit #{hits} time#{s}" end end end end byebug-10.0.1/lib/byebug/commands/info/line.rb0000644000004100000410000000126113256220560021132 0ustar www-datawww-data# frozen_string_literal: true module Byebug # # Reopens the +info+ command to define the +line+ subcommand # class InfoCommand < Command # # Information about current location # class LineCommand < Command self.allow_in_post_mortem = true def self.regexp /^\s* l(?:ine)? \s*$/x end def self.description <<-DESCRIPTION inf[o] l[ine] #{short_description} DESCRIPTION end def self.short_description "Line number and file name of current position in source file." end def execute puts "Line #{frame.line} of \"#{frame.file}\"" end end end end byebug-10.0.1/lib/byebug/commands/info/file.rb0000644000004100000410000000352513256220560021127 0ustar www-datawww-data# frozen_string_literal: true require "byebug/helpers/file" module Byebug # # Reopens the +info+ command to define the +file+ subcommand # class InfoCommand < Command # # Information about a particular source file # class FileCommand < Command include Helpers::FileHelper include Helpers::StringHelper self.allow_in_post_mortem = true def self.regexp /^\s* f(?:ile)? (?:\s+ (.+))? \s*$/x end def self.description <<-DESCRIPTION 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. DESCRIPTION end def self.short_description "Information about a particular source file." end def execute file = @match[1] || frame.file unless File.exist?(file) return errmsg(pr("info.errors.undefined_file", file: file)) end puts prettify <<-RUBY File #{info_file_basic(file)} Breakpoint line numbers: #{info_file_breakpoints(file)} Modification time: #{info_file_mtime(file)} Sha1 Signature: #{info_file_sha1(file)} RUBY 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.join(" ") 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-10.0.1/lib/byebug/commands/info/program.rb0000644000004100000410000000206713256220560021657 0ustar www-datawww-data# frozen_string_literal: true module Byebug # # Reopens the +info+ command to define the +args+ subcommand # class InfoCommand < Command # # Information about arguments of the current method/block # class ProgramCommand < Command self.allow_in_post_mortem = true def self.regexp /^\s* p(?:rogram)? \s*$/x end def self.description <<-DESCRIPTION inf[o] p[rogram] #{short_description} DESCRIPTION end def self.short_description "Information about the current status of the debugged program." end def execute puts "Program stopped. " format_stop_reason context.stop_reason 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-10.0.1/lib/byebug/commands/thread.rb0000644000004100000410000000123213256220560020515 0ustar www-datawww-data# frozen_string_literal: true require "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 self.regexp /^\s* th(?:read)? (?:\s+ (.+))? \s*$/x end def self.description <<-DESCRIPTION th[read] #{short_description} DESCRIPTION end def self.short_description "Commands to manipulate threads" end end end byebug-10.0.1/lib/byebug/commands/debug.rb0000644000004100000410000000135013256220560020335 0ustar www-datawww-data# frozen_string_literal: true require "byebug/command" require "byebug/helpers/eval" module Byebug # # Spawns a subdebugger and evaluates the given expression # class DebugCommand < Command include Helpers::EvalHelper def self.regexp /^\s* debug (?:\s+(\S+))? \s*$/x end def self.description <<-DESCRIPTION debug #{short_description} Allows, for example, setting breakpoints on expressions evaluated from the debugger's prompt. DESCRIPTION end def self.short_description "Spawns a subdebugger" end def execute return puts(help) unless @match[1] puts safe_inspect(separate_thread_eval(@match[1])) end end end byebug-10.0.1/lib/byebug/commands/pry.rb0000644000004100000410000000140013256220560020055 0ustar www-datawww-data# frozen_string_literal: true require "byebug/command" require "byebug/helpers/eval" module Byebug # # Enter Pry from byebug's prompt # class PryCommand < Command self.allow_in_post_mortem = true def self.regexp /^\s* pry \s*$/x end def self.description <<-DESCRIPTION pry #{short_description} DESCRIPTION end def self.short_description "Starts a Pry session" end def execute unless processor.interface.instance_of?(LocalInterface) return errmsg(pr("base.errors.only_local")) end begin require "pry" rescue LoadError return errmsg(pr("pry.errors.not_installed")) end Pry.start(context.frame._binding) end end end byebug-10.0.1/lib/byebug/commands/info.rb0000644000004100000410000000141113256220560020200 0ustar www-datawww-data# frozen_string_literal: true require "byebug/subcommands" require "byebug/commands/info/breakpoints" 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 self.allow_in_post_mortem = true def self.regexp /^\s* i(?:nfo)? (?:\s+ (.+))? \s*$/x end def self.description <<-DESCRIPTION info[ subcommand] #{short_description} DESCRIPTION end def self.short_description "Shows several informations about the program being debugged" end end end byebug-10.0.1/lib/byebug/commands/help.rb0000644000004100000410000000244613256220560020206 0ustar www-datawww-data# frozen_string_literal: true require "byebug/command" require "byebug/errors" module Byebug # # Ask for help from byebug's prompt. # class HelpCommand < Command self.allow_in_control = true self.allow_in_post_mortem = true def self.regexp /^\s* h(?:elp)? (?:\s+(\S+))? (?:\s+(\S+))? \s*$/x end def self.description <<-DESCRIPTION h[elp][ [ ]] #{short_description} help -- prints a summary of all commands help -- prints help on command help -- prints help on 's subcommand DESCRIPTION end def self.short_description "Helps you using byebug" end def execute return help_for_all unless @match[1] return help_for(@match[1], command) unless @match[2] help_for(@match[2], subcommand) end private def help_for_all puts(processor.command_list.to_s) end def help_for(input, cmd) raise CommandNotFound.new(input, command) unless cmd puts(cmd.help) end def command @command ||= processor.command_list.match(@match[1]) end def subcommand return unless command @subcommand ||= command.subcommand_list.match(@match[2]) end end end byebug-10.0.1/lib/byebug/commands/edit.rb0000644000004100000410000000274413256220560020204 0ustar www-datawww-data# frozen_string_literal: true require "byebug/command" module Byebug # # Edit a file from byebug's prompt. # class EditCommand < Command self.allow_in_control = true self.allow_in_post_mortem = true def self.regexp /^\s* ed(?:it)? (?:\s+(\S+))? \s*$/x end def self.description <<-DESCRIPTION edit[ file:lineno] #{short_description} With no argumnt, edits file containing most re line listed. Editing targets can also be specified to start editing at a specific line in a specific file DESCRIPTION end def self.short_description "Edits source files" end def execute file, line = location(@match[1]) return edit_error("not_exist", file) unless File.exist?(file) return edit_error("not_readable", file) unless File.readable?(file) cmd = line ? "#{editor} +#{line} #{file}" : "#{editor} #{file}" Kernel.system(cmd) end private def location(matched) if matched.nil? file = frame.file return errmsg(pr("edit.errors.state")) unless file line = frame.line elsif (@pos_match = /([^:]+)[:]([0-9]+)/.match(matched)) file, line = @pos_match.captures else file = matched line = nil end [File.expand_path(file), line] end def editor ENV["EDITOR"] || "vim" end def edit_error(type, file) errmsg(pr("edit.errors.#{type}", file: file)) end end end byebug-10.0.1/lib/byebug/commands/kill.rb0000644000004100000410000000165513256220560020212 0ustar www-datawww-data# frozen_string_literal: true require "byebug/command" module Byebug # # Send custom signals to the debugged program. # class KillCommand < Command self.allow_in_control = true def self.regexp /^\s* (?:kill) \s* (?:\s+(\S+))? \s*$/x end def self.description <<-DESCRIPTION kill[ signal] #{short_description} Equivalent of Process.kill(Process.pid) DESCRIPTION end def self.short_description "Sends a signal to the current process" end def execute if @match[1] signame = @match[1] unless Signal.list.member?(signame) return errmsg("signal name #{signame} is not a signal I know about\n") end else return unless confirm("Really kill? (y/n) ") signame = "KILL" end processor.interface.close if signame == "KILL" Process.kill(signame, Process.pid) end end end byebug-10.0.1/lib/byebug/commands/untracevar.rb0000644000004100000410000000127713256220560021431 0ustar www-datawww-data# frozen_string_literal: true require "byebug/command" module Byebug # # Stop tracing a global variable. # class UntracevarCommand < Command def self.regexp /^\s* untr(?:acevar)? (?:\s+ (\S+))? \s*$/x end def self.description <<-DESCRIPTION untr[acevar] #{short_description} DESCRIPTION end def self.short_description "Stops tracing a global variable" 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 end end byebug-10.0.1/lib/byebug/commands/undisplay.rb0000644000004100000410000000236113256220560021262 0ustar www-datawww-data# frozen_string_literal: true require "byebug/command" require "byebug/helpers/parse" module Byebug # # Remove expressions from display list. # class UndisplayCommand < Command include Helpers::ParseHelper self.allow_in_post_mortem = true def self.regexp /^\s* undisp(?:lay)? (?:\s+(\S+))? \s*$/x end def self.description <<-DESCRIPTION undisp[lay][ nnn] #{short_description} Arguments are the code numbers of the expressions to stop displaying. No argument means cancel all automatic-display expressions. Type "info display" to see the current list of code numbers. DESCRIPTION end def self.short_description "Stops displaying all or some expressions when program stops" end def execute if @match[1] pos, err = get_int(@match[1], "Undisplay", 1, Byebug.displays.size) return errmsg(err) unless err.nil? unless Byebug.displays[pos - 1] return errmsg(pr("display.errors.undefined", expr: pos)) end Byebug.displays[pos - 1][0] = nil else return unless confirm(pr("display.confirmations.clear_all")) Byebug.displays.each { |d| d[0] = false } end end end end byebug-10.0.1/lib/byebug/commands/list.rb0000644000004100000410000000725513256220560020234 0ustar www-datawww-data# frozen_string_literal: true require "byebug/command" require "byebug/source_file_formatter" 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 self.allow_in_post_mortem = true def self.regexp /^\s* l(?:ist)? (?:\s*([-=])|\s+(\S+))? \s*$/x end def self.description <<-DESCRIPTION l[ist][[-=]][ nn-mm] #{short_description} Lists lines 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. DESCRIPTION end def self.short_description "Lists lines of source code" end def execute msg = "No sourcefile available for #{frame.file}" raise(msg) unless File.exist?(frame.file) b, e = range(@match[2]) display_lines(b, e) processor.prev_line = b 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) return auto_range(@match[1] || "+") unless input b, e = parse_range(input) raise("Invalid line range") unless valid_range?(b, e) [b, e] end def valid_range?(first, last) first <= last && (1..max_line).cover?(first) && (1..max_line).cover?(last) end # # Set line range to be printed by list # # @return first line number to list # @return last line number to list # def auto_range(direction) prev_line = processor.prev_line if direction == "=" || prev_line.nil? source_file_formatter.range_around(frame.line) else source_file_formatter.range_from(move(prev_line, size, direction)) end end def parse_range(input) first, err = get_int(lower_bound(input), "List", 1, max_line) raise(err) unless first if upper_bound(input) last, err = get_int(upper_bound(input), "List", 1, max_line) raise(err) unless last last = amend_final(last) else first -= (size / 2) end [first, last || move(first, size - 1)] end def move(line, size, direction = "+") line.send(direction, size) end # # Show a range of lines in the current file. # # @param min [Integer] Lower bound # @param max [Integer] Upper bound # def display_lines(min, max) puts "\n[#{min}, #{max}] in #{frame.file}" puts source_file_formatter.lines(min, max).join end # # @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 str [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 extend Forwardable def_delegators :source_file_formatter, :amend_final, :size, :max_line def source_file_formatter @source_file_formatter ||= SourceFileFormatter.new( frame.file, ->(n) { n == frame.line ? "=>" : " " } ) end end end byebug-10.0.1/lib/byebug/commands/restart.rb0000644000004100000410000000245113256220560020736 0ustar www-datawww-data# frozen_string_literal: true require "byebug/command" require "byebug/helpers/bin" require "byebug/helpers/path" require "shellwords" require "English" require "rbconfig" module Byebug # # Restart debugged program from within byebug. # class RestartCommand < Command include Helpers::BinHelper include Helpers::PathHelper self.allow_in_control = true self.allow_in_post_mortem = true def self.regexp /^\s* restart (?:\s+(?.+))? \s*$/x end def self.description <<-DESCRIPTION restart [args] #{short_description} This is a re-exec - all byebug state is lost. If command arguments are passed those are used. DESCRIPTION end def self.short_description "Restarts the debugged program" end def execute cmd = [$PROGRAM_NAME] cmd = prepend_byebug_bin(cmd) cmd = prepend_ruby_bin(cmd) cmd += (@match[:args] ? @match[:args].shellsplit : $ARGV) puts pr("restart.success", cmd: cmd.shelljoin) Kernel.exec(*cmd) end private def prepend_byebug_bin(cmd) cmd.unshift(bin_file) if Byebug.mode == :standalone cmd end def prepend_ruby_bin(cmd) cmd.unshift(RbConfig.ruby) if which("ruby") != which(cmd.first) cmd end end end byebug-10.0.1/lib/byebug/commands/delete.rb0000644000004100000410000000225213256220560020513 0ustar www-datawww-data# frozen_string_literal: true require "byebug/command" require "byebug/helpers/parse" module Byebug # # Implements breakpoint deletion. # class DeleteCommand < Command include Helpers::ParseHelper self.allow_in_control = true self.allow_in_post_mortem = true def self.regexp /^\s* del(?:ete)? (?:\s+(.*))?$/x end def self.description <<-DESCRIPTION del[ete][ nnn...] #{short_description} Without and argument, deletes all breakpoints. With integer arguments, it deletes specific breakpoints. DESCRIPTION end def self.short_description "Deletes breakpoints" end def execute unless @match[1] if confirm(pr("break.confirmations.delete_all")) Byebug.breakpoints.clear end return end @match[1].split(/ +/).each do |number| pos, err = get_int(number, "Delete", 1) return errmsg(err) unless pos if Breakpoint.remove(pos) puts(pr("break.messages.breakpoint_deleted", pos: pos)) else errmsg(pr("break.errors.no_breakpoint_delete", pos: pos)) end end end end end byebug-10.0.1/lib/byebug/commands/irb.rb0000644000004100000410000000167313256220560020033 0ustar www-datawww-data# frozen_string_literal: true require "byebug/command" require "irb" require "English" module Byebug # # Enter IRB from byebug's prompt # class IrbCommand < Command self.allow_in_post_mortem = true def self.regexp /^\s* irb \s*$/x end def self.description <<-DESCRIPTION irb #{short_description} DESCRIPTION end def self.short_description "Starts an IRB session" end def execute unless processor.interface.instance_of?(LocalInterface) return errmsg(pr("base.errors.only_local")) end # @todo IRB tries to parse $ARGV so we must clear it (see #197). Add a # test case for it so we can remove this comment. with_clean_argv { IRB.start } end private def with_clean_argv saved_argv = $ARGV.dup $ARGV.clear begin yield ensure $ARGV.concat(saved_argv) end end end end byebug-10.0.1/lib/byebug/commands/method.rb0000644000004100000410000000245013256220560020531 0ustar www-datawww-data# frozen_string_literal: true require "byebug/command" require "byebug/helpers/eval" module Byebug # # Show methods of specific classes/modules/objects. # class MethodCommand < Command include Helpers::EvalHelper self.allow_in_post_mortem = true def self.regexp /^\s* m(?:ethod)? \s+ (i(:?nstance)?\s+)?/x end def self.description <<-DESCRIPTION m[ethod] (i[nstance][ ]|) #{short_description} 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. DESCRIPTION end def self.short_description "Shows methods of an object, class or module" end def execute obj = warning_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 end end byebug-10.0.1/lib/byebug/commands/save.rb0000644000004100000410000000317613256220560020215 0ustar www-datawww-data# frozen_string_literal: true require "byebug/command" module Byebug # # Save current settings to use them in another debug session. # class SaveCommand < Command self.allow_in_control = true self.allow_in_post_mortem = true def self.regexp /^\s* sa(?:ve)? (?:\s+(\S+))? \s*$/x end def self.description <<-DESCRIPTION save[ FILE] #{short_description} Byebug state is saved as a script file. This includes breakpoints, catchpoints, display expressions and some settings. If no filename is given, byebug will fabricate one. Use the "source" command in another debug session to restore the saved file. DESCRIPTION end def self.short_description "Saves current byebug session to a file" 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 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.each_key do |c| file.puts "catch #{c}" end end def save_displays(file) Byebug.displays.each { |d| file.puts "display #{d[1]}" if d[0] } end def save_settings(file) %w[autoirb autolist basename].each do |setting| file.puts "set #{setting} #{Setting[setting.to_sym]}" end end end end byebug-10.0.1/lib/byebug/commands/enable.rb0000644000004100000410000000121713256220560020477 0ustar www-datawww-data# frozen_string_literal: true require "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 self.allow_in_post_mortem = true def self.regexp /^\s* en(?:able)? (?:\s+ (.+))? \s*$/x end def self.description <<-DESCRIPTION en[able][[ b[reakpoints]| d[isplay])][ n1[ n2[ ...[ nn]]]]] #{short_description} DESCRIPTION end def self.short_description "Enables breakpoints or displays" end end end byebug-10.0.1/lib/byebug/commands/frame.rb0000644000004100000410000000270213256220560020343 0ustar www-datawww-data# frozen_string_literal: true 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 self.allow_in_post_mortem = true def self.regexp /^\s* f(?:rame)? (?:\s+(\S+))? \s*$/x end def self.description <<-DESCRIPTION f[rame][ frame-number] #{short_description} If a frame number has been specified, to moves to that frame. Otherwise it moves to the newest frame. 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. DESCRIPTION end def self.short_description "Moves to a frame in the call stack" end def execute return print(pr("frame.line", context.frame.to_hash)) unless @match[1] pos, err = get_int(@match[1], "Frame") return errmsg(err) unless pos switch_to_frame(pos) ListCommand.new(processor).execute if Setting[:autolist] end end end byebug-10.0.1/lib/byebug/commands/disable.rb0000644000004100000410000000122313256220560020651 0ustar www-datawww-data# frozen_string_literal: true require "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 self.allow_in_post_mortem = true def self.regexp /^\s* dis(?:able)? (?:\s+ (.+))? \s*$/x end def self.description <<-DESCRIPTION dis[able][[ breakpoints| display)][ n1[ n2[ ...[ nn]]]]] #{short_description} DESCRIPTION end def self.short_description "Disables breakpoints or displays" end end end byebug-10.0.1/lib/byebug/commands/break.rb0000644000004100000410000000574013256220560020342 0ustar www-datawww-data# frozen_string_literal: true require "byebug/command" require "byebug/helpers/eval" require "byebug/helpers/file" require "byebug/helpers/parse" require "byebug/source_file_formatter" module Byebug # # Implements breakpoint functionality # class BreakCommand < Command include Helpers::EvalHelper include Helpers::FileHelper include Helpers::ParseHelper self.allow_in_control = true def self.regexp /^\s* b(?:reak)? (?:\s+ (.+?))? (?:\s+ if \s+(.+))? \s*$/x end def self.description <<-DESCRIPTION b[reak] [file:]line [if expr] b[reak] [module::...]class(.|#)method [if expr] They can be specified by line or method and an expression can be added for conditionally enabled breakpoints. #{short_description} DESCRIPTION end def self.short_description "Sets breakpoints in the source code" end def execute return puts(help) unless @match[1] b = line_breakpoint(@match[1]) || method_breakpoint(@match[1]) return errmsg(pr("break.errors.location")) unless b 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 end private def line_breakpoint(location) line_match = location.match(/^(\d+)$/) file_line_match = location.match(/^(.+):(\d+)$/) return unless line_match || file_line_match file = line_match ? frame.file : file_line_match[1] line = line_match ? line_match[1].to_i : file_line_match[2].to_i add_line_breakpoint(file, line) end def method_breakpoint(location) location.match(/([^.#]+)[.#](.+)/) do |match| klass = target_object(match[1]) method = match[2].intern Breakpoint.add(klass, method, @match[2]) end end def target_object(str) k = error_eval(str) k && k.is_a?(Module) ? k.name : str rescue StandardError errmsg("Warning: breakpoint source is not yet defined") str end def add_line_breakpoint(file, line) raise(pr("break.errors.source", file: file)) unless File.exist?(file) fullpath = File.realpath(file) if line > n_lines(file) raise(pr("break.errors.far_line", lines: n_lines(file), file: fullpath)) end unless Breakpoint.potential_line?(fullpath, line) msg = pr( "break.errors.line", file: fullpath, line: line, valid_breakpoints: valid_breakpoints_for(fullpath, line) ) raise(msg) end Breakpoint.add(fullpath, line, @match[2]) end def valid_breakpoints_for(path, line) potential_lines = Breakpoint.potential_lines(path) annotator = ->(n) { potential_lines.include?(n) ? "[B]" : " " } source_file_formatter = SourceFileFormatter.new(path, annotator) source_file_formatter.lines_around(line).join.chomp end end end byebug-10.0.1/lib/byebug/commands/finish.rb0000644000004100000410000000236713256220560020540 0ustar www-datawww-data# frozen_string_literal: true require "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 self.regexp /^\s* fin(?:ish)? (?:\s+(\S+))? \s*$/x end def self.description <<-DESCRIPTION fin[ish][ n_frames] #{short_description} 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. DESCRIPTION end def self.short_description "Runs the program until frame returns" end def execute 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.zero? ? true : false context.step_out(context.frame.pos + n_frames, force) context.frame = 0 processor.proceed! end private def max_frames context.stack_size - context.frame.pos end end end byebug-10.0.1/lib/byebug/commands/disable/0000755000004100000410000000000013256220560020326 5ustar www-datawww-databyebug-10.0.1/lib/byebug/commands/disable/display.rb0000644000004100000410000000177213256220560022327 0ustar www-datawww-data# frozen_string_literal: true require "byebug/helpers/toggle" module Byebug # # Reopens the +disable+ command to define the +display+ subcommand # class DisableCommand < Command # # Enables all or specific displays # class DisplayCommand < Command include Helpers::ToggleHelper self.allow_in_post_mortem = true def self.regexp /^\s* d(?:isplay)? (?:\s+ (.+))? \s*$/x end def self.description <<-DESCRIPTION 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. DESCRIPTION end def self.short_description "Disables expressions to be displayed when program stops." end def execute enable_disable_display("disable", @match[1]) end end end end byebug-10.0.1/lib/byebug/commands/disable/breakpoints.rb0000644000004100000410000000167313256220560023203 0ustar www-datawww-data# frozen_string_literal: true require "byebug/helpers/toggle" module Byebug # # Reopens the +disable+ command to define the +breakpoints+ subcommand # class DisableCommand < Command # # Disables all or specific breakpoints # class BreakpointsCommand < Command include Helpers::ToggleHelper self.allow_in_post_mortem = true def self.regexp /^\s* b(?:reakpoints)? (?:\s+ (.+))? \s*$/x end def self.description <<-DESCRIPTION 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. DESCRIPTION end def self.short_description "Disable all or specific breakpoints." end def execute enable_disable_breakpoints("disable", @match[1]) end end end end byebug-10.0.1/lib/byebug/context.rb0000644000004100000410000000561313256220560017140 0ustar www-datawww-data# frozen_string_literal: true require "byebug/frame" require "byebug/helpers/path" require "byebug/helpers/file" require "byebug/processors/command_processor" module 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 include Helpers::FileHelper class << self include Helpers::PathHelper attr_writer :ignored_files # # List of files byebug will ignore while debugging # def ignored_files @ignored_files ||= Byebug.mode == :standalone ? lib_files + [bin_file] : lib_files end attr_writer :interface def interface @interface ||= LocalInterface.new end attr_writer :processor def processor @processor ||= CommandProcessor end end # # Reader for the current frame # def frame @frame ||= Frame.new(self, 0) end # # Writer for the current frame # def frame=(pos) @frame = Frame.new(self, pos) end extend Forwardable def_delegators :frame, :file, :line # # Current file & line information # def location "#{normalize(file)}:#{line}" end # # Current file, line and source code information # def full_location return location if virtual_file?(file) "#{location} #{get_line(file, line)}" 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 # # Line handler # def at_line self.frame = 0 return if ignored_file?(file) processor.at_line end # # Tracing handler # def at_tracing return if ignored_file?(file) processor.at_tracing end # # Breakpoint handler # def at_breakpoint(breakpoint) processor.at_breakpoint(breakpoint) end # # Catchpoint handler # def at_catchpoint(exception) processor.at_catchpoint(exception) end # # Return handler # def at_return(return_value) return if ignored_file?(file) processor.at_return(return_value) end # # End of class definition handler # def at_end return if ignored_file?(file) processor.at_end end private def processor @processor ||= self.class.processor.new(self, self.class.interface) 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 end end byebug-10.0.1/lib/byebug/command.rb0000644000004100000410000000432713256220560017073 0ustar www-datawww-data# frozen_string_literal: true require "forwardable" require "byebug/helpers/string" module Byebug # # Parent class of all byebug commands. # # Subclass it and name the subclass ending with the word Command to implement # your own custom command. # # @example Define a custom command # # class MyCustomCommand < Command # def self.regexp # /custom_regexp/ # end # # def self.description # "Custom long desc" # end # # def.short_description # "Custom short desc" # end # # def execute # # My command's implementation # end # end # class Command extend Forwardable attr_reader :processor def initialize(processor, input = self.class.to_s) @processor = processor @match = match(input) end def context @context ||= processor.context end def frame @frame ||= context.frame end def arguments @match[0].split(" ").drop(1).join(" ") end def_delegators "self.class", :help, :match def_delegator "processor.printer", :print, :pr def_delegator "processor.printer", :print_collection, :prc def_delegator "processor.printer", :print_variables, :prv def_delegators "processor.interface", :errmsg, :puts, :print, :confirm class << self include Helpers::StringHelper # # Special methods to allow command filtering in processors # attr_accessor :allow_in_control, :allow_in_post_mortem attr_writer :always_run def always_run @always_run ||= 0 end # # Name of the command, as executed by the user. # def to_s name .split("::") .map { |n| n.gsub(/Command$/, "").downcase if n =~ /Command$/ } .compact .join(" ") end def columnize(width) format( " %-#{width}s -- %s\n", name: to_s, description: short_description ) end # # Default help text for a command. # def help prettify(description) end # # Command's regexp match against an input # def match(input) regexp.match(input) end end end end byebug-10.0.1/lib/byebug/remote.rb0000644000004100000410000000357413256220560016753 0ustar www-datawww-data# frozen_string_literal: true require "socket" require "byebug/processors/control_processor" require "byebug/remote/server" require "byebug/remote/client" # # Remote debugging functionality. # 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 def actual_port server.actual_port end # The actual port that the control server is started at def actual_control_port control.actual_port end # # Interrupts the current thread # def interrupt current_context.interrupt end # # Starts the remote server main thread # def start_server(host = nil, port = PORT) start_control(host, port.zero? ? 0 : port + 1) server.start(host, port) end # # Starts the remote server control thread # def start_control(host = nil, port = PORT + 1) control.start(host, port) end # # Connects to the remote byebug # def start_client(host = "localhost", port = PORT) client.start(host, port) 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 private def client @client ||= Remote::Client.new(Context.interface) end def server @server ||= Remote::Server.new(wait_connection: wait_connection) do |s| Context.interface = RemoteInterface.new(s) end end def control @control ||= Remote::Server.new(wait_connection: false) do |s| context = Byebug.current_context interface = RemoteInterface.new(s) ControlProcessor.new(context, interface).process_commands end end end end byebug-10.0.1/lib/byebug/setting.rb0000644000004100000410000000315313256220560017126 0ustar www-datawww-data# frozen_string_literal: true require "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| key =~ (value.boolean? ? /#{abbr}/ : /#{shortcut}/) end matches.size == 1 ? matches.values.first : nil end # # @todo DRY this up. Very similar code exists in the CommandList class # def help_all output = " List of supported settings:\n\n" width = settings.keys.max_by(&:size).size settings.each_value do |sett| output += format( " %-#{width}s -- %s\n", name: sett.to_sym, description: sett.banner ) end output + "\n" end end end end byebug-10.0.1/lib/byebug/attacher.rb0000644000004100000410000000145613256220560017250 0ustar www-datawww-data# frozen_string_literal: true # # Main Container for all of Byebug's code # module Byebug # # Starts byebug, and stops at the first line of user's code. # def self.attach require "byebug/core" unless started? self.mode = :attached start run_init_script end current_context.step_out(3, true) end def self.spawn(host = "localhost", port = nil) require "byebug/core" self.wait_connection = true start_server(host, port || PORT) 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 def remote_byebug(host = "localhost", port = nil) Byebug.spawn(host, port) Byebug.attach end alias debugger byebug end byebug-10.0.1/lib/byebug/command_list.rb0000644000004100000410000000105713256220560020123 0ustar www-datawww-data# frozen_string_literal: true require "byebug/errors" module Byebug # # Holds an array of subcommands for a command # class CommandList include Enumerable def initialize(commands) @commands = commands.sort_by(&:to_s) end def match(input) find { |cmd| cmd.match(input) } end def each @commands.each { |cmd| yield(cmd) } end def to_s "\n" + map { |cmd| cmd.columnize(width) }.join + "\n" end private def width @width ||= map(&:to_s).max_by(&:size).size end end end byebug-10.0.1/lib/byebug/version.rb0000644000004100000410000000020413256220560017130 0ustar www-datawww-data# frozen_string_literal: true # # Reopen main module to define the library version # module Byebug VERSION = "10.0.1".freeze end byebug-10.0.1/lib/byebug/printers/0000755000004100000410000000000013256220560016770 5ustar www-datawww-databyebug-10.0.1/lib/byebug/printers/texts/0000755000004100000410000000000013256220560020137 5ustar www-datawww-databyebug-10.0.1/lib/byebug/printers/texts/plain.yml0000644000004100000410000000135013256220560021764 0ustar www-datawww-databreak: created: "Created breakpoint {id} at {file}:{line}" 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 {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-10.0.1/lib/byebug/printers/texts/base.yml0000644000004100000410000000655113256220560021603 0ustar www-datawww-database: errors: only_local: "Command is available only in local mode." break: errors: line: "Line {line} is not a valid breakpoint in file {file}.\n\nValid break points are:\n{valid_breakpoints}" 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?" messages: breakpoint_deleted: "Deleted breakpoint {pos}" catch: added: "Catching exception {exception}." removed: "Catch for exception {exception} removed" errors: off: "Off expected. Got {off}" not_class: "Warning {class} is not known to be a Class" 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" 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" 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." messages: toggled: "Breakpoint {bpnum} {endis}abled" 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-10.0.1/lib/byebug/printers/plain.rb0000644000004100000410000000206113256220560020417 0ustar www-datawww-data# frozen_string_literal: true require "byebug/printers/base" module Byebug module Printers # # Plain text printer # class Plain < Base 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) lines = array_of_args(collection, &block).map do |args| print(path, args) end lines.join end def print_variables(variables, *_unused) 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 contents_files [File.join(__dir__, "texts", "plain.yml")] + super end end end end byebug-10.0.1/lib/byebug/printers/base.rb0000644000004100000410000000324413256220560020232 0ustar www-datawww-data# frozen_string_literal: true require "yaml" module Byebug module Printers # # Base printer # class Base class MissedPath < StandardError; end class MissedArgument < StandardError; end SEPARATOR = ".".freeze def type self.class.name.split("::").last.downcase end private def locate(path) result = nil contents.each_value do |contents| result = parts(path).reduce(contents) do |r, part| r && r.key?(part) ? r[part] : nil end break if result end raise 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) raise 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 = yield item, index array << args if args end end def contents_files [File.join(__dir__, "texts", "base.yml")] end end end end byebug-10.0.1/lib/byebug/option_setter.rb0000644000004100000410000000353213256220560020350 0ustar www-datawww-data# frozen_string_literal: true module Byebug # # Handles byebug's command line options # class OptionSetter def initialize(runner, opts) @runner = runner @opts = opts end def setup debug include_flag post_mortem quit rc stop require_flag remote trace version help end private def debug @opts.on "-d", "--debug", "Set $DEBUG=true" do $DEBUG = true end end def include_flag @opts.on "-I", "--include list", "Add to paths to $LOAD_PATH" do |list| $LOAD_PATH.push(list.split(":")).flatten! end end def post_mortem @opts.on "-m", "--[no-]post-mortem", "Use post-mortem mode" do |v| Setting[:post_mortem] = v end end def quit @opts.on "-q", "--[no-]quit", "Quit when script finishes" do |v| @runner.quit = v end end def rc @opts.on "-x", "--[no-]rc", "Run byebug initialization file" do |v| @runner.init_script = v end end def stop @opts.on "-s", "--[no-]stop", "Stop when script is loaded" do |v| @runner.stop = v end end def require_flag @opts.on "-r", "--require file", "Require library before script" do |lib| require lib end end def remote @opts.on "-R", "--remote [host:]port", "Remote debug [host:]port" do |p| @runner.remote = p end end def trace @opts.on "-t", "--[no-]trace", "Turn on line tracing" do |v| Setting[:linetrace] = v end end def version @opts.on "-v", "--version", "Print program version" do @runner.version = Byebug::VERSION end end def help @opts.on "-h", "--help", "Display this message" do @runner.help = @opts.help end end end end byebug-10.0.1/lib/byebug/settings/0000755000004100000410000000000013256220560016762 5ustar www-datawww-databyebug-10.0.1/lib/byebug/settings/post_mortem.rb0000644000004100000410000000074613256220560021666 0ustar www-datawww-data# frozen_string_literal: true require "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=(val) Byebug.post_mortem = val end def value Byebug.post_mortem? end end end byebug-10.0.1/lib/byebug/settings/callstyle.rb0000644000004100000410000000056313256220560021307 0ustar www-datawww-data# frozen_string_literal: true require "byebug/setting" module Byebug # # Setting to customize the verbosity level for stack frames. # class CallstyleSetting < Setting DEFAULT = "long".freeze 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-10.0.1/lib/byebug/settings/autopry.rb0000644000004100000410000000073413256220560021016 0ustar www-datawww-data# frozen_string_literal: true require "byebug/setting" require "byebug/commands/pry" module Byebug # # Setting for automatically invoking Pry on every stop. # class AutoprySetting < Setting DEFAULT = 0 def initialize PryCommand.always_run = DEFAULT end def banner "Invoke Pry on every stop" end def value=(val) PryCommand.always_run = val ? 1 : 0 end def value PryCommand.always_run == 1 end end end byebug-10.0.1/lib/byebug/settings/histsize.rb0000644000004100000410000000061713256220560021155 0ustar www-datawww-data# frozen_string_literal: true require "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-10.0.1/lib/byebug/settings/autolist.rb0000644000004100000410000000076113256220560021157 0ustar www-datawww-data# frozen_string_literal: true require "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=(val) ListCommand.always_run = val ? 1 : 0 end def value ListCommand.always_run == 1 end end end byebug-10.0.1/lib/byebug/settings/linetrace.rb0000644000004100000410000000051113256220560021252 0ustar www-datawww-data# frozen_string_literal: true require "byebug/setting" module Byebug # # Setting to enable/disable linetracing. # class LinetraceSetting < Setting def banner "Enable line execution tracing" end def value=(val) Byebug.tracing = val end def value Byebug.tracing? end end end byebug-10.0.1/lib/byebug/settings/savefile.rb0000644000004100000410000000064213256220560021107 0ustar www-datawww-data# frozen_string_literal: true require "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 "File where settings are saved to. Default: ~/.byebug_save" end def to_s "The command history file is #{value}\n" end end end byebug-10.0.1/lib/byebug/settings/width.rb0000644000004100000410000000054513256220560020432 0ustar www-datawww-data# frozen_string_literal: true require "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-10.0.1/lib/byebug/settings/histfile.rb0000644000004100000410000000062413256220560021120 0ustar www-datawww-data# frozen_string_literal: true require "byebug/setting" module Byebug # # Setting to customize the file where byebug's history is saved. # class HistfileSetting < Setting DEFAULT = File.expand_path(".byebug_history") def banner "File where cmd history is saved to. Default: ./.byebug_history" end def to_s "The command history file is #{value}\n" end end end byebug-10.0.1/lib/byebug/settings/autosave.rb0000644000004100000410000000051313256220560021135 0ustar www-datawww-data# frozen_string_literal: true require "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-10.0.1/lib/byebug/settings/fullpath.rb0000644000004100000410000000040513256220560021125 0ustar www-datawww-data# frozen_string_literal: true require "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-10.0.1/lib/byebug/settings/listsize.rb0000644000004100000410000000063413256220560021160 0ustar www-datawww-data# frozen_string_literal: true require "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-10.0.1/lib/byebug/settings/basename.rb0000644000004100000410000000050613256220560021063 0ustar www-datawww-data# frozen_string_literal: true require "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-10.0.1/lib/byebug/settings/stack_on_error.rb0000644000004100000410000000045513256220560022325 0ustar www-datawww-data# frozen_string_literal: true require "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-10.0.1/lib/byebug/settings/autoirb.rb0000644000004100000410000000073413256220560020760 0ustar www-datawww-data# frozen_string_literal: true require "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=(val) IrbCommand.always_run = val ? 1 : 0 end def value IrbCommand.always_run == 1 end end end byebug-10.0.1/lib/byebug/core.rb0000644000004100000410000000533513256220560016405 0ustar www-datawww-data# frozen_string_literal: true require "byebug/helpers/reflection" require "byebug/byebug" require "byebug/context" require "byebug/breakpoint" require "byebug/interface" require "byebug/processors/script_processor" require "byebug/processors/post_mortem_processor" require "byebug/commands" require "byebug/remote" require "byebug/printers/plain" # # Main debugger's container module. Everything is defined under this module # module Byebug include Helpers::ReflectionHelper extend self # # Configuration file used for startup commands. Default value is .byebugrc # attr_accessor :init_file self.init_file = ".byebugrc" # # Debugger's display expressions # attr_accessor :displays self.displays = [] # # Running mode of the debugger. Can be either: # # * :attached => Attached to a running program through the `byebug` method. # * :standalone => Started through `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 rc_dirs.each do |dir| rc_file = File.expand_path(File.join(dir, init_file)) next unless File.exist?(rc_file) run_rc_file(rc_file) end end def self.load_settings Dir.glob(File.join(__dir__, "settings", "*.rb")).each do |file| require file end constants.grep(/[a-z]Setting/).map do |name| setting = const_get(name).new Byebug::Setting.settings[setting.to_sym] = setting 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 raised_exception context = raised_exception.__bb_context PostMortemProcessor.new(context).at_line end at_exit { Byebug.handle_post_mortem if Byebug.post_mortem? } private # # Runs a initialization script file # def run_rc_file(rc_file) interface = ScriptInterface.new(rc_file) ScriptProcessor.new(nil, interface).process_commands end # # List of folders to load rc files from # # @note Files will be loaded in the order specified here. # def rc_dirs [ENV["HOME"], Dir.pwd].compact.uniq end end Byebug.load_settings # # 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_context end byebug-10.0.1/lib/byebug/commands.rb0000644000004100000410000000231613256220560017252 0ustar www-datawww-data# frozen_string_literal: true require "byebug/commands/break" require "byebug/commands/catch" require "byebug/commands/condition" require "byebug/commands/continue" require "byebug/commands/debug" require "byebug/commands/delete" require "byebug/commands/disable" require "byebug/commands/display" require "byebug/commands/down" require "byebug/commands/edit" require "byebug/commands/enable" require "byebug/commands/finish" require "byebug/commands/frame" require "byebug/commands/help" require "byebug/commands/history" require "byebug/commands/info" require "byebug/commands/interrupt" require "byebug/commands/irb" require "byebug/commands/kill" require "byebug/commands/list" require "byebug/commands/method" require "byebug/commands/next" require "byebug/commands/pry" require "byebug/commands/quit" require "byebug/commands/restart" require "byebug/commands/save" require "byebug/commands/set" require "byebug/commands/show" require "byebug/commands/source" require "byebug/commands/step" require "byebug/commands/thread" require "byebug/commands/tracevar" require "byebug/commands/undisplay" require "byebug/commands/untracevar" require "byebug/commands/up" require "byebug/commands/var" require "byebug/commands/where" byebug-10.0.1/lib/byebug/runner.rb0000644000004100000410000001013713256220560016762 0ustar www-datawww-data# frozen_string_literal: true require "optparse" require "English" require "byebug/core" require "byebug/version" require "byebug/helpers/bin" require "byebug/helpers/parse" require "byebug/helpers/string" require "byebug/option_setter" require "byebug/processors/control_processor" module Byebug # # Responsible for starting the debugger when started from the command line. # class Runner include Helpers::BinHelper include Helpers::ParseHelper include Helpers::StringHelper # # Special working modes that don't actually start the debugger. # attr_reader :help, :version, :remote # # Signals that we should exit after the debugged program is finished. # attr_accessor :quit # # Signals that we should stop before program starts # attr_accessor :stop # # Signals that we should run rc scripts before program starts # attr_writer :init_script # # @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 def help=(text) @help ||= text interface.puts("#{text}\n") end def version=(number) @version ||= number interface.puts prettify <<-VERSION Running byebug #{number} VERSION end def remote=(host_and_port) @remote ||= Byebug.parse_host_and_port(host_and_port) Byebug.start_client(*@remote) end def init_script defined?(@init_script) ? @init_script : true end # # Usage banner. # def banner prettify <<-BANNER byebug #{Byebug::VERSION} Usage: byebug [options] -- BANNER end # # Starts byebug to debug a program. # def run Byebug.mode = :standalone option_parser.order!($ARGV) return if non_script_option? || error_in_script? $PROGRAM_NAME = program Byebug.run_init_script if init_script loop do debug_program break if quit ControlProcessor.new(nil, interface).process_commands end end def interface @interface ||= Context.interface end # # Processes options passed from the command line. # def option_parser @option_parser ||= OptionParser.new(banner, 25) do |opts| opts.banner = banner OptionSetter.new(self, opts).setup end end def program @program ||= begin candidate = which($ARGV.shift) if [which("ruby"), RbConfig.ruby].include?(candidate) which($ARGV.shift) else candidate end end end # # An option that doesn't need a script specified was given # def non_script_option? version || help || remote end # # There is an error with the specified script # def error_in_script? no_script? || non_existing_script? || invalid_script? end # # No script to debug specified # def no_script? return false unless $ARGV.empty? print_error("You must specify a program to debug") true end # # Extracts debugged program from command line args. # def non_existing_script? return false if program print_error("The script doesn't exist") true end # # Checks the debugged script has correct syntax # def invalid_script? return false if syntax_valid?(File.read(program)) print_error("The script has incorrect syntax") true end # # Debugs a script only if syntax checks okay. # def debug_program error = Byebug.debug_load(program, stop) puts "#{error}\n#{error.backtrace}" if error end # # Prints an error message and a help string # def print_error(msg) interface.errmsg(msg) interface.puts(option_parser.help) end end end byebug-10.0.1/lib/byebug/processors/0000755000004100000410000000000013256220560017324 5ustar www-datawww-databyebug-10.0.1/lib/byebug/processors/script_processor.rb0000644000004100000410000000145713256220560023263 0ustar www-datawww-data# frozen_string_literal: true require "byebug/processors/command_processor" module Byebug # # Processes commands from a file # class ScriptProcessor < CommandProcessor # # Available commands # def commands super.select(&:allow_in_control) end def repl while (input = interface.read_command(prompt)) safely do command = command_list.match(input) raise CommandNotFound.new(input) unless command command.new(self, input).execute end end end def after_repl super interface.close end # # Prompt shown before reading a command. # def prompt "(byebug:ctrl) " end private def without_exceptions yield rescue StandardError nil end end end byebug-10.0.1/lib/byebug/processors/post_mortem_processor.rb0000644000004100000410000000050213256220560024315 0ustar www-datawww-data# frozen_string_literal: true require "byebug/processors/command_processor" module Byebug # # Processes commands in post_mortem mode # class PostMortemProcessor < CommandProcessor def commands super.select(&:allow_in_post_mortem) end def prompt "(byebug:post_mortem) " end end end byebug-10.0.1/lib/byebug/processors/command_processor.rb0000644000004100000410000000630113256220560023366 0ustar www-datawww-data# frozen_string_literal: true require "forwardable" require "byebug/helpers/eval" require "byebug/errors" module Byebug # # Processes commands in regular mode. # # You can override this class to create your own command processor that, for # example, whitelists only certain commands to be executed. # # @see PostMortemProcessor for a example # class CommandProcessor include Helpers::EvalHelper attr_accessor :prev_line attr_reader :context, :interface def initialize(context, interface = LocalInterface.new) @context = context @interface = interface @proceed = false @prev_line = nil end def printer @printer ||= Printers::Plain.new end extend Forwardable def_delegators :@context, :frame def_delegator :printer, :print, :pr def_delegator :printer, :print_collection, :prc def_delegator :printer, :print_variables, :prv def_delegators :interface, :errmsg, :puts, :confirm def_delegators :Byebug, :commands # # Available commands # def command_list @command_list ||= CommandList.new(commands) end def at_line process_commands end def at_tracing puts "Tracing: #{context.full_location}" run_auto_cmds(2) end def at_breakpoint(brkpt) number = Byebug.breakpoints.index(brkpt) + 1 puts "Stopped by breakpoint #{number} at #{frame.file}:#{frame.line}" end def at_catchpoint(exception) puts "Catchpoint at #{context.location}: `#{exception}'" end def at_return(return_value) puts "Return value is: #{safe_inspect(return_value)}" process_commands end def at_end process_commands end # # Let the execution continue # def proceed! @proceed = true end # # Handle byebug commands. # def process_commands before_repl repl ensure after_repl end protected # # Prompt shown before reading a command. # def prompt "(byebug) " end def before_repl @proceed = false @prev_line = nil run_auto_cmds(1) interface.autorestore end def after_repl interface.autosave end # # Main byebug's REPL # def repl until @proceed cmd = interface.read_command(prompt) return if cmd.nil? next if cmd == "" run_cmd(cmd) end end private def auto_cmds_for(run_level) command_list.select { |cmd| cmd.always_run >= run_level } end # # Run permanent commands. # def run_auto_cmds(run_level) safely do auto_cmds_for(run_level).each { |cmd| cmd.new(self).execute } end end # # Executes the received input # # Instantiates a command matching the input and runs it. If a matching # command is not found, it evaluates the unknown input. # def run_cmd(input) safely do command = command_list.match(input) return command.new(self, input).execute if command puts safe_inspect(multiple_thread_eval(input)) end end def safely yield rescue StandardError => e errmsg(e.message) end end end byebug-10.0.1/lib/byebug/processors/control_processor.rb0000644000004100000410000000063713256220560023436 0ustar www-datawww-data# frozen_string_literal: true require "byebug/processors/command_processor" module Byebug # # Processes commands when there's not program running # class ControlProcessor < CommandProcessor # # Available commands # def commands super.select(&:allow_in_control) end # # Prompt shown before reading a command. # def prompt "(byebug:ctrl) " end end end byebug-10.0.1/lib/byebug/interfaces/0000755000004100000410000000000013256220560017245 5ustar www-datawww-databyebug-10.0.1/lib/byebug/interfaces/remote_interface.rb0000644000004100000410000000157313256220560023113 0ustar www-datawww-data# frozen_string_literal: true require "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}") rescue Errno::EPIPE, Errno::ECONNABORTED "continue" end def confirm(prompt) super("CONFIRM #{prompt}") rescue Errno::EPIPE, Errno::ECONNABORTED false end def print(message) super(message) rescue Errno::EPIPE, Errno::ECONNABORTED nil end def puts(message) super(message) rescue Errno::EPIPE, Errno::ECONNABORTED nil end def close output.close end def readline(prompt) puts(prompt) (input.gets || "continue").chomp end end end byebug-10.0.1/lib/byebug/interfaces/script_interface.rb0000644000004100000410000000120213256220560023111 0ustar www-datawww-data# frozen_string_literal: true module Byebug # # Interface class for command execution from script files. # class ScriptInterface < Interface def initialize(file, verbose = false) super() @verbose = verbose @input = File.open(file) @output = verbose ? $stdout : StringIO.new @error = $stderr end def read_command(prompt) readline(prompt, false) end def close input.close end def readline(*) while (result = input.gets) output.puts "+ #{result}" if @verbose next if result =~ /^\s*#/ return result.chomp end end end end byebug-10.0.1/lib/byebug/interfaces/test_interface.rb0000644000004100000410000000213213256220560022567 0ustar www-datawww-data# frozen_string_literal: true module Byebug # # Custom interface for easier assertions # class TestInterface < Interface attr_accessor :test_block def initialize super() clear end def errmsg(message) error.concat(prepare(message)) end def print(message) output.concat(prepare(message)) end def puts(message) output.concat(prepare(message)) 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 private def prepare(message) return message.map(&:to_s) if message.respond_to?(:map) message.to_s.split("\n") end end end byebug-10.0.1/lib/byebug/interfaces/local_interface.rb0000644000004100000410000000204313256220560022703 0ustar www-datawww-data# frozen_string_literal: true module Byebug # # Interface class for standard byebug use. # class LocalInterface < Interface EOF_ALIAS = "continue".freeze def initialize super() @input = $stdin @output = $stdout @error = $stderr end # # Reads a single line of input using Readline. If Ctrl-D is pressed, it # returns "continue", meaning that program's execution will go on. # # @param prompt Prompt to be displayed. # def readline(prompt) with_repl_like_sigint { Readline.readline(prompt) || EOF_ALIAS } end # # Yields the block handling Ctrl-C the following way: if pressed while # waiting for input, the line is reset to only the prompt and we ask for # input again. # # @note Any external 'INT' traps are overriden during this method. # def with_repl_like_sigint orig_handler = trap("INT") { raise Interrupt } yield rescue Interrupt puts("^C") retry ensure trap("INT", orig_handler) end end end byebug-10.0.1/lib/byebug/frame.rb0000644000004100000410000000674713256220560016557 0ustar www-datawww-data# frozen_string_literal: true require "byebug/helpers/file" module Byebug # # Represents a frame in the stack trace # class Frame include Helpers::FileHelper attr_reader :pos def initialize(context, pos) @context = context @pos = pos end def file @context.frame_file(pos) end def line @context.frame_line(pos) end def _self @context.frame_self(pos) end def _binding @context.frame_binding(pos) end def _class @context.frame_class(pos) end def _method @context.frame_method(pos) end def current? @context.frame.pos == pos end # # Gets local variables for the frame. # def locals return [] unless _binding _binding.local_variables.each_with_object({}) do |e, a| a[e] = _binding.local_variable_get(e) a end end # # Gets current method arguments for the frame. # def args return c_args unless _binding ruby_args end # # Returns the current class in the frame or an empty string if the current # +callstyle+ setting is 'short' # def deco_class Setting[:callstyle] == "short" || _class.to_s.empty? ? "" : "#{_class}." end def deco_block _method[/(?:block(?: \(\d+ levels\))?|rescue) in /] || "" end def deco_method _method[/((?:block(?: \(\d+ levels\))?|rescue) in )?(.*)/] end # # Builds a string containing all available args in the frame number, in a # verbose or non verbose way according to the value of the +callstyle+ # setting # def deco_args return "" if args.empty? my_args = args.map do |arg| prefix, default = prefix_and_default(arg[0]) kls = use_short_style?(arg) ? "" : "##{locals[arg[1]].class}" "#{prefix}#{arg[1] || default}#{kls}" end "(#{my_args.join(', ')})" end # # Builds a formatted string containing information about current method call # def deco_call deco_block + deco_class + deco_method + deco_args end # # Formatted filename in frame # def deco_file Setting[:fullpath] ? File.expand_path(file) : shortpath(file) end # # Properly formatted frame number of frame # def deco_pos format("%-2d", pos) end # # Formatted mark for the frame. # # --> marks the current frame # ͱ-- marks c-frames # marks regular frames # def mark return "-->" if current? return " ͱ--" if c_frame? " " end # # Checks whether the frame is a c-frame # def c_frame? _binding.nil? end def to_hash { mark: mark, pos: deco_pos, call: deco_call, file: deco_file, line: line, full_path: File.expand_path(deco_file) } end private def c_args return [] unless _self.to_s != "main" _class.instance_method(_method).parameters end def ruby_args meth_name = _binding.eval("__method__") return [] unless meth_name meth_obj = _class.instance_method(meth_name) return [] unless meth_obj meth_obj.parameters end def use_short_style?(arg) Setting[:callstyle] == "short" || arg[1].nil? || locals.empty? end def prefix_and_default(arg_type) return ["&", "block"] if arg_type == :block return ["*", "args"] if arg_type == :rest ["", nil] end end end byebug-10.0.1/lib/byebug/helpers/0000755000004100000410000000000013256220560016564 5ustar www-datawww-databyebug-10.0.1/lib/byebug/helpers/string.rb0000644000004100000410000000152513256220560020422 0ustar www-datawww-data# frozen_string_literal: true module 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" + deindent(str) + "\n" end # # Removes a number of leading whitespace for each input line. # # @note Might be unnecessary when Ruby 2.2 support is dropped and we can # use squiggly heredoc's. # def deindent(str, leading_spaces: 6) str.gsub(/^ {#{leading_spaces}}/, "") end end end end byebug-10.0.1/lib/byebug/helpers/bin.rb0000644000004100000410000000204313256220560017660 0ustar www-datawww-data# frozen_string_literal: true module Byebug module Helpers # # Utilities for interaction with executables # module BinHelper # # Cross-platform way of finding an executable in the $PATH. # Adapted from: https://gist.github.com/steakknife/88b6c3837a5e90a08296 # def which(cmd) return File.expand_path(cmd) if File.exist?(cmd) [nil, *search_paths].each do |path| exe = find_executable(path, cmd) return exe if exe end nil end def find_executable(path, cmd) executable_file_extensions.each do |ext| exe = File.expand_path(cmd + ext, path) return exe if real_executable?(exe) end nil end def search_paths ENV["PATH"].split(File::PATH_SEPARATOR) end def executable_file_extensions ENV["PATHEXT"] ? ENV["PATHEXT"].split(";") : [""] end def real_executable?(file) File.executable?(file) && !File.directory?(file) end end end end byebug-10.0.1/lib/byebug/helpers/var.rb0000644000004100000410000000250013256220560017676 0ustar www-datawww-data# frozen_string_literal: true require "byebug/helpers/eval" module Byebug module Helpers # # Utilities for variable subcommands # module VarHelper include EvalHelper def var_list(ary, binding = context.frame._binding) vars = ary.sort.map do |name| [name, safe_inspect(silent_eval(name.to_s, binding))] end puts prv(vars, "instance") end def var_global globals = global_variables.reject do |v| %i[$IGNORECASE $= $KCODE $-K $binding].include?(v) end var_list(globals) end def var_instance(str) obj = warning_eval(str || "self") var_list(obj.instance_variables, obj.instance_eval { binding }) end def var_local locals = context.frame.locals cur_self = context.frame._self locals[:self] = cur_self unless cur_self.to_s == "main" puts prv(locals.keys.sort.map { |k| [k, locals[k]] }, "instance") end def var_args args = context.frame.args return if args == [[:rest]] all_locals = context.frame.locals arg_values = args.map { |arg| arg[1] } locals = all_locals.select { |k, _| arg_values.include?(k) } puts prv(locals.keys.sort.map { |k| [k, locals[k]] }, "instance") end end end end byebug-10.0.1/lib/byebug/helpers/path.rb0000644000004100000410000000136413256220560020051 0ustar www-datawww-data# frozen_string_literal: true module Byebug module Helpers # # Utilities for managing gem paths # module PathHelper def bin_file @bin_file ||= File.join(root_path, "exe", "byebug") end def root_path @root_path ||= File.expand_path(File.join("..", "..", ".."), __dir__) end def lib_files @lib_files ||= glob_for("lib") end def test_files @test_files ||= glob_for("test") end def gem_files @gem_files ||= [bin_file] + lib_files end def all_files @all_files ||= gem_files + test_files end private def glob_for(dir) Dir.glob(File.join(root_path, dir, "**", "*.rb")) end end end end byebug-10.0.1/lib/byebug/helpers/thread.rb0000644000004100000410000000310613256220560020360 0ustar www-datawww-data# frozen_string_literal: true module Byebug module Helpers # # Utilities for thread subcommands # module ThreadHelper def display_context(ctx) puts pr("thread.context", thread_arguments(ctx)) end def thread_arguments(ctx) { status_flag: status_flag(ctx), debug_flag: debug_flag(ctx), id: ctx.thnum, thread: ctx.thread.inspect, file_line: location(ctx), pid: Process.pid, status: ctx.thread.status, current: current_thread?(ctx) } end def current_thread?(ctx) ctx.thread == Thread.current end def context_from_thread(thnum) ctx = Byebug.contexts.find { |c| c.thnum.to_s == thnum } err = if ctx.nil? pr("thread.errors.no_thread") elsif ctx == context pr("thread.errors.current_thread") elsif ctx.ignored? pr("thread.errors.ignored", arg: thnum) end [ctx, err] end private # @todo Check whether it is Byebug.current_context or context def location(ctx) return context.location if ctx == Byebug.current_context backtrace = ctx.thread.backtrace_locations return "" unless backtrace && backtrace[0] "#{backtrace[0].path}:#{backtrace[0].lineno}" end def status_flag(ctx) return "$" if ctx.suspended? current_thread?(ctx) ? "+" : " " end def debug_flag(ctx) ctx.ignored? ? "!" : " " end end end end byebug-10.0.1/lib/byebug/helpers/file.rb0000644000004100000410000000273313256220560020035 0ustar www-datawww-data# frozen_string_literal: true module 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([]) { |acc, elem| acc << elem.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) { |acc, _elem| acc + 1 } end # # Regularize file name. # def normalize(filename) return filename if virtual_file?(filename) return File.basename(filename) if Setting[:basename] File.exist?(filename) ? File.realpath(filename) : filename end # # A short version of a long path # def shortpath(fullpath) components = Pathname(fullpath).each_filename.to_a return fullpath if components.size <= 2 File.join("...", components[-3..-1]) end # # True for special files like -e, false otherwise # def virtual_file?(name) ["(irb)", "-e", "(byebug)", "(eval)"].include?(name) end end end end byebug-10.0.1/lib/byebug/helpers/eval.rb0000644000004100000410000000633013256220560020042 0ustar www-datawww-data# frozen_string_literal: true module Byebug module Helpers # # Utilities to assist evaluation of code strings # module EvalHelper # # Evaluates an +expression+ in a separate thread. # # @param expression [String] Expression to evaluate # def separate_thread_eval(expression) allowing_other_threads do in_new_thread { warning_eval(expression) } end end # # Evaluates an +expression+ that might use or defer execution to threads # other than the current one. # # @note This is necessary because when in byebug's prompt, every thread is # "frozen" so that nothing gets run. So we need to unlock threads prior # to evaluation or we will run into a deadlock. # # @param expression [String] Expression to evaluate # def multiple_thread_eval(expression) allowing_other_threads { warning_eval(expression) } end # # Evaluates a string containing Ruby code in a specific binding, # returning nil in an error happens. # def silent_eval(str, binding = frame._binding) safe_eval(str, binding) { |_e| nil } end # # Evaluates a string containing Ruby code in a specific binding, # handling the errors at an error level. # def error_eval(str, binding = frame._binding) safe_eval(str, binding) { |e| raise(e, msg(e)) } end # # Evaluates a string containing Ruby code in a specific binding, # handling the errors at a warning level. # def warning_eval(str, binding = frame._binding) safe_eval(str, binding) { |e| errmsg(msg(e)) } end private def safe_eval(str, binding) binding.eval(str.gsub(/\Aeval /, ""), "(byebug)", 1) rescue StandardError, ScriptError => e yield(e) end def msg(exception) msg = Setting[:stack_on_error] ? error_msg(exception) : warning_msg(exception) pr("eval.exception", text_message: msg) end def error_msg(exception) at = exception.backtrace locations = ["#{at.shift}: #{warning_msg(exception)}"] locations += at.map { |path| " from #{path}" } locations.join("\n") end def warning_msg(exception) "#{exception.class} Exception: #{exception.message}" end # # 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 # # Runs the given block in a new thread, waits for it to finish and # returns the new thread's result. # def in_new_thread res = nil Thread.new { res = yield }.join res end def safe_inspect(var) var.inspect rescue StandardError safe_to_s(var) end def safe_to_s(var) var.to_s rescue StandardError "*Error in evaluation*" end end end end byebug-10.0.1/lib/byebug/helpers/parse.rb0000644000004100000410000000333213256220560020224 0ustar www-datawww-data# frozen_string_literal: true module 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 method's # purpose. # def get_int(str, cmd, min = nil, max = nil) if str !~ /\A-?[0-9]+\z/ return nil, pr("parse.errors.int.not_number", cmd: cmd, str: str) end int = str.to_i if min && int < min err = pr("parse.errors.int.too_low", cmd: cmd, str: str, min: min) return nil, err elsif max && int > max err = pr("parse.errors.int.too_high", cmd: cmd, str: str, max: max) return nil, 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 # # @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 private # # Temporarily disable output to $stderr # def without_stderr old_stderr = $stderr $stderr = StringIO.new yield ensure $stderr = old_stderr end end end end byebug-10.0.1/lib/byebug/helpers/toggle.rb0000644000004100000410000000336513256220560020401 0ustar www-datawww-data# frozen_string_literal: true require "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) raise pr("toggle.errors.no_breakpoints") if Breakpoint.none? select_breakpoints(is_enable, args).each do |b| enabled = (is_enable == "enable") if enabled && !syntax_valid?(b.expr) raise pr("toggle.errors.expression", expr: b.expr) end puts pr("toggle.messages.toggled", bpnum: b.id, endis: enabled ? "en" : "dis") b.enabled = enabled end end def enable_disable_display(is_enable, args) raise pr("toggle.errors.no_display") if n_displays.zero? selected_displays = args ? args.split(/ +/) : [1..n_displays + 1] selected_displays.each do |pos| pos, err = get_int(pos, "#{is_enable} display", 1, n_displays) raise err unless err.nil? Byebug.displays[pos - 1][0] = (is_enable == "enable") end end private def select_breakpoints(is_enable, args) all_breakpoints = Byebug.breakpoints.sort_by(&:id) return all_breakpoints if args.nil? selected_ids = [] args.split(/ +/).each do |pos| last_id = all_breakpoints.last.id pos, err = get_int(pos, "#{is_enable} breakpoints", 1, last_id) raise(ArgumentError, err) unless pos selected_ids << pos end all_breakpoints.select { |b| selected_ids.include?(b.id) } end def n_displays Byebug.displays.size end end end end byebug-10.0.1/lib/byebug/helpers/frame.rb0000644000004100000410000000343613256220560020211 0ustar www-datawww-data# frozen_string_literal: true module Byebug module Helpers # # Utilities to assist frame navigation # module FrameHelper def switch_to_frame(frame) new_frame = index_from_start(frame) return frame_err("c_frame") if Frame.new(context, new_frame).c_frame? adjust_frame(new_frame) end def jump_frames(steps) adjust_frame(navigate_to_frame(steps)) end private def adjust_frame(new_frame) return frame_err("too_low") if new_frame >= context.stack_size return frame_err("too_high") if new_frame < 0 context.frame = new_frame processor.prev_line = nil end def navigate_to_frame(jump_no) current_jumps = 0 current_pos = context.frame.pos loop do current_pos += direction(jump_no) break if out_of_bounds?(current_pos) next if Frame.new(context, current_pos).c_frame? current_jumps += 1 break if current_jumps == jump_no.abs end current_pos end def out_of_bounds?(pos) !(0...context.stack_size).cover?(pos) end def frame_err(msg) errmsg(pr("frame.errors.#{msg}")) end # # @param step [Integer] A positive or negative integer # # @return [Integer] +1 if step is positive / -1 if negative # def direction(step) step / step.abs end # # Convert a possibly negative index to a positive index from the start # of the callstack. -1 is the last position in the stack and so on. # # @param i [Integer] Integer to be converted in a proper positive index. # def index_from_start(index) index >= 0 ? index : context.stack_size + index end end end end byebug-10.0.1/lib/byebug/helpers/reflection.rb0000644000004100000410000000061313256220560021243 0ustar www-datawww-data# frozen_string_literal: true module Byebug module Helpers # # Reflection utilitie # module ReflectionHelper # # List of "command" classes in the including module # def commands constants(false) .map { |const| const_get(const, false) } .select { |c| c.is_a?(Class) && c.name =~ /[a-z]Command$/ } end end end end byebug-10.0.1/CONTRIBUTING.md0000644000004100000410000000764013256220560015337 0ustar www-datawww-data# CONTRIBUTING Please note that this project is released with a [Contributor Code of Conduct](code_of_conduct.md). By participating in this project you agree to abide by its terms. ## Bug Reports * Try to reproduce the issue against the latest revision. There might be unrealeased work that fixes your problem! * Ensure that your issue has not already been reported. * Include the steps you carried out to produce the problem. If we can't reproduce it, we can't fix it. * Include the behavior you observed along with the behavior you expected, and why you expected it. ## Development dependencies * `Byebug` depends on Ruby's 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. * The recommended tool to manage development dependencies is `bundler`. Run `gem install bundler` to install it. * Running `bin/bundle install` inside a local clone of `byebug` will get development dependencies installed. ## Running the test suite * Make sure you compile the C-extension using `bin/rake compile`. Otherwise you won't be able to use `byebug`. * Run the test suite using the default rake task (`bin/rake`). This task is composed of 3 subtasks: `bin/rake compile`, `bin/rake test` & `bin/rake lint`. * If you want to run specific tests, use the provided test runner, like so: * Specific test files. For example, `bin/minitest test/commands/break_test.rb` * Specific test classes. For example, `bin/minitest BreakAtLinesTest` * Specific tests. For example, `bin/minitest test_catch_removes_specific_catchpoint` * Specific fully qualified tests. For example, `bin/minitest BreakAtLinesTest#test_setting_breakpoint_sets_correct_fields` * You can combine any of them and you will get the union of all filters. For example: `bin/minitest BreakAtLinesTest test_catch_removes_specific_catchpoint` ## Code style * Byebug uses [codeclimate][] to enforce code style. You can run codeclimate checks locally using the [codeclimate CLI][] with `codeclimate analyze`. * It also uses some extra style checks that are not available in codeclimate. You can run those using `bin/rake lint`. These tasks are: * Linting of c-files using `clang-format`. Configuration is specific to clang-format 3.8, you may need some extra work to get that installed on macOS, see below. * Checking correct executable bit on repository files. [codeclimate]: https://codeclimate.com/github/deivid-rodriguez/byebug [codeclimate CLI]: https://github.com/codeclimate/codeclimate ### Runnning `clang-format` on macOS At the moment byebug uses older `clang-format` version to enforce C codestyle than can be found in Homebrew. If you are planning to change some C source here it is recommended to use [direnv][] to hook that older version into your shell: * Install [direnv][] as described in their README * Install `clang-format@3.8` with `brew install clang-format@3.8` * In byebug source code directory do `echo 'export PATH="/usr/local/opt/clang-format@3.8/bin:$PATH"' > .envrc` * Allow direnv to use that `.envrc` file with `direnv allow` With that your `$PATH` will be updated to use older `clang-format` every time you `cd` into byebug source code folder. It will reverted back when you `cd` out of it as well. [direnv]: https://github.com/direnv/direnv/ ## 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-10.0.1/LICENSE0000644000004100000410000000243513256220560014110 0ustar www-datawww-dataCopyright (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-10.0.1/CHANGELOG.md0000644000004100000410000005350713256220560014722 0ustar www-datawww-data# CHANGELOG ## Master (Unreleased) ## 10.0.1 - 2018-03-21 ### Fixed * Error when using byebug with `debase` gem (#443, @tzmfreedom) ## 10.0.0 - 2018-01-26 ### Changed * Breaking on methods now stops on the first effective line of a method, not on the line containing the `def` keyword. ### Added * Show valid breakpoint locations when invalid location given (#393, @ko1). * Ruby 2.5.0 support (#397, @yui-knk). * Log host & port when launching byebug's client in remote mode. * Some love & tests to remote debugging (#82). * `remote_byebug` shortcut to start the most common case for remote debugging (#141). ### Fixed * Properly ignore ruby fullpath executable when passed to byebug script (#419). * Remote server crash when interrupting client (#141, #274). * Control server thread being able to `interrupt` main thread only a single time. (#239). ## 9.1.0 - 2017-08-22 ### Added * Better UI messages for breakpoint management. ### Fixed * `where` command failing on instance_exec block stack frames. * `restart` command crashing in certain cases because of a missing `require "English"` (#321, @akaneko3). * `restart` command crashing when debugged script is not executable or has no shebang (#321, @akaneko3). ### Removed * Ruby 2.0 and Ruby 2.1 official & unofficial support. Byebug no longer installs on these platforms. ## 9.0.6 - 2016-09-29 ### Fixed * Error when using `byebug` with a ruby compiled against libedit (#241). * Allow `Byebug.start_server` to yield the block passed to it when the actual port is already known (#277, thanks @cben). * Use a standard license name so it can be more reliably used by tools (#275). ## 9.0.5 - 2016-05-28 ### Fixed * Error loading rc file when `ENV["HOME"]` is unset. ## 9.0.4 - 2016-05-19 ### Fixed * Errors in rc file not being displayed to the user. ## 9.0.3 - 2016-05-16 ### Fixed * Unfriendly output in byebug's executable when no script specified (#256). * Unfriendly output in byebug's executable when script doesn't exist. * Unfriendly output in byebug's executable when script has invalid code. ## 9.0.2 - 2016-05-15 ### Fixed * Skip to get a line in eval context (#263, thanks @k0kubun). * Debugger getting disabled after `continue` even when linetrace is enabled (#264, thanks @k0kubun). ## 9.0.1 - 2016-05-14 ### Fixed * `quit` never exiting when remote debugging (#201). ## 9.0.0 - 2016-05-11 ### Fixed * `irb` command unintentionally changing $PROGRAM_NAME. * `pry` command failing. * Unrelated error message when using `pry` command and Pry not installed. * Interrupting program execution from remote control interface (#239, thanks @izaera). ### Removed * Official Ruby 2.0.0 support. `var local` no longer works in Ruby 2.0. The rest of the commands should still work as before, but `byebug` is no longer tested against this version so they might start breaking in the future. ## 8.2.5 - 2016-04-27 ### Fixed * Allows paths with spaces (#244, thanks @HookyQR). * Allows paths with colons (#244, thanks @HookyQR). ## 8.2.4 - 2016-04-08 ### Fixed * Reverts #211 which leads to an unusable debugger. ## 8.2.3 - 2016-04-07 ### Fixed * Better interaction with utilities like RSpec when hitting Ctrl-C. * `irb` command when original program modified ARGV (#197, thanks @josephks). * Unusable debugger when stdin redirected (#211,thanks @sethk). * RC file loading when no explicit flag included (#223). * Installation on some Windows systems (#175, #226). ## 8.2.2 - 2016-02-01 ### Fixed * Bug in rc file loading where most initialization commands would not be run. ## 8.2.1 - 2015-11-26 ### Fixed * Bug in evaluations using "eval". ## 8.2.0 - 2015-11-12 ### Fixed * [#184](https://github.com/deivid-rodriguez/byebug/issues/184) & [#188](https://github.com/deivid-rodriguez/byebug/issues/188), both due to the way of running evaluations in a separate thread. ### Added * `debug` command to evaluate things in a separate thread, since this behavior was removed from default `eval` to fix the above issues. ## 8.1.0 - 2015-11-09 ### Fixed * Command history should be specific per project. * Better error message in certain edge cases when printing the backtrace. * Bug in evaluator which would show information about having stopped at a breakpoint in some cases. ### Added * Ability to autolist source code after `frame` command. * Ability to stop at lines where methods return. ## 8.0.1 - 2015-11-07 ### Fixed * Error stream wouldn't be properly reset when using standalone `byebug`. * Confusing error message for invalid breakpoint locations. ## 8.0.0 - 2015-11-05 ### Fixed * [#183](https://github.com/deivid-rodriguez/byebug/issues/183). Compilation in Ruby 2.0. Regression introduced in 7.0.0 * "Return value is: nil" would be displayed when stopping right before the end of a class definition. We want to avoid showing anything instead. ### Changed * Plugins now need to implement an `at_end` method (separate from `at_return`) in their custom processors. ## 7.0.0 - 2015-11-04 ### Fixed * [#177](https://github.com/deivid-rodriguez/byebug/issues/177). Some issues with formatting results of evaluations. * [#144](https://github.com/deivid-rodriguez/byebug/issues/144). Ruby process after using byebug does no longer get slow. * [#121](https://github.com/deivid-rodriguez/byebug/issues/121). `byebug` commands inside code evaluated from debugger's prompt are now properly working. * Another evaluation bug in autocommands. * `finish 0` command would sometimes fail to stop right before exiting the current frame. * Runner's `--[no-]stop` option now works (thanks @windwiny). * Change variable name `bool`, avoid conflict clang's predefined macro ### Removed * `ps` command. ### Changed * [#166](https://github.com/deivid-rodriguez/byebug/issues/166). Don't load the entire library on require, but only when a `byebug` call is issued. Thanks @bquorning. * The above fix to the `finish 0` command cause `byebug`'s entrypoint to require 3 steps out instead of 2. In general, plugins using `Byebug::Context.step_out` will need to be changed to consider "c return events" as well. ### Added * `autopry` setting that calls `pry` on every stop. * Return value information to debugger's output when `finish 0` is used. ## 6.0.2 - 2015-08-20 ### Fixed * The user should always be given back a prompt unless (s)he explicitly states the opposite. This provides a more general fix to the bug resolved in 6.0.1. ## 6.0.1 - 2015-08-19 ### Fixed * Bug in evaluation where the user would lose the command prompt when entering an expression with a syntax error. ## 6.0.0 - 2015-08-17 ### Removed * `autoeval` setting. I haven't heard of anyone setting it to false. * `pp`, `putl`, `eval`. People just want to evaluate Ruby code, so the less magic the better. Most of the people probably were not aware that `byebug` was overriding stuff like `pp` or `eval`. Only keeping `ps` as the single "enhanced evaluation" command. * `verbose` setting. * `info catch` command. Use `catch` without arguments instead. * `R` command alias for `restart`. ### Changed * `info args` is now `var args`. * `interrupt` is now aliased to `int`, not to `i`. * API to define custom commands and subcommands (see the Command class). ### Fixed * [#140](https://github.com/deivid-rodriguez/byebug/issues/140). `help` command not showing the list of available commands and their descriptions. * [#147](https://github.com/deivid-rodriguez/byebug/issues/147). Setting breakpoints at symlinked files. ### Added * API to define custom command processors (see the CommandProcessor class). ## 5.0.0 - 2015-05-18 ### 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 * `post_mortem` mode in `bin/byebug` (really). * 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-10.0.1/byebug.gemspec0000644000004100000410000001334713256220560015731 0ustar www-datawww-data######################################################### # This file has been automatically generated by gem2tgz # ######################################################### # -*- encoding: utf-8 -*- Gem::Specification.new do |s| s.name = "byebug" s.version = "10.0.1" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.authors = ["David Rodriguez", "Kent Sibilev", "Mark Moseley"] s.bindir = "exe" s.date = "2018-03-21" s.description = "Byebug is a Ruby debugger. It's implemented using the\n TracePoint C API for execution control and the Debug Inspector C API for\n call stack navigation. The core component provides support that front-ends\n can build on. It provides breakpoint handling and bindings for stack frames\n among other things and it comes with an easy to use command line interface." s.email = "deivid.rodriguez@mail.com" s.executables = ["byebug"] s.extensions = ["ext/byebug/extconf.rb"] s.extra_rdoc_files = ["CHANGELOG.md", "CONTRIBUTING.md", "GUIDE.md", "README.md"] s.files = ["CHANGELOG.md", "CONTRIBUTING.md", "GUIDE.md", "LICENSE", "README.md", "exe/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/command_list.rb", "lib/byebug/commands.rb", "lib/byebug/commands/break.rb", "lib/byebug/commands/catch.rb", "lib/byebug/commands/condition.rb", "lib/byebug/commands/continue.rb", "lib/byebug/commands/debug.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/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/breakpoints.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/pry.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/args.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/errors.rb", "lib/byebug/frame.rb", "lib/byebug/helpers/bin.rb", "lib/byebug/helpers/eval.rb", "lib/byebug/helpers/file.rb", "lib/byebug/helpers/frame.rb", "lib/byebug/helpers/parse.rb", "lib/byebug/helpers/path.rb", "lib/byebug/helpers/reflection.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/option_setter.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/processors/command_processor.rb", "lib/byebug/processors/control_processor.rb", "lib/byebug/processors/post_mortem_processor.rb", "lib/byebug/processors/script_processor.rb", "lib/byebug/remote.rb", "lib/byebug/remote/client.rb", "lib/byebug/remote/server.rb", "lib/byebug/runner.rb", "lib/byebug/setting.rb", "lib/byebug/settings/autoirb.rb", "lib/byebug/settings/autolist.rb", "lib/byebug/settings/autopry.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/width.rb", "lib/byebug/source_file_formatter.rb", "lib/byebug/subcommands.rb", "lib/byebug/version.rb"] s.homepage = "http://github.com/deivid-rodriguez/byebug" s.licenses = ["BSD-2-Clause"] s.require_paths = ["lib"] s.required_ruby_version = Gem::Requirement.new(">= 2.2.0") s.rubygems_version = "1.8.23" s.summary = "Ruby fast debugger - base + CLI" if s.respond_to? :specification_version then s.specification_version = 4 if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then s.add_development_dependency(%q, ["~> 1.7"]) else s.add_dependency(%q, ["~> 1.7"]) end else s.add_dependency(%q, ["~> 1.7"]) end end byebug-10.0.1/ext/0000755000004100000410000000000013256220560013677 5ustar www-datawww-databyebug-10.0.1/ext/byebug/0000755000004100000410000000000013256220560015154 5ustar www-datawww-databyebug-10.0.1/ext/byebug/threads.c0000644000004100000410000001072213256220560016754 0ustar www-datawww-data#include "byebug.h" /* 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 */ static 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. */ static 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 = byebug_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; 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-10.0.1/ext/byebug/extconf.rb0000644000004100000410000000045613256220560017154 0ustar www-datawww-data# frozen_string_literal: true require "mkmf" makefile_config = RbConfig::MAKEFILE_CONFIG makefile_config["CC"] = ENV["CC"] if ENV["CC"] makefile_config["CFLAGS"] << " -gdwarf-2 -g3 -O0" if ENV["debug"] dir_config("ruby") with_cflags(makefile_config["CFLAGS"]) { create_makefile("byebug/byebug") } byebug-10.0.1/ext/byebug/byebug.h0000644000004100000410000000677313256220560016617 0ustar www-datawww-data#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; typedef enum { LOCATION, SELF, CLASS, BINDING } frame_part; struct call_with_inspection_data { debug_context_t *dc; VALUE ctx; 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 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_byebug_context(VALUE mByebug); extern VALUE byebug_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 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-10.0.1/ext/byebug/context.c0000644000004100000410000003715313256220560017015 0ustar www-datawww-data#include "byebug.h" 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 byebug_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, frame_part 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->ctx, 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) -> class * * 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 in the frame. */ 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 method in the frame. */ 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-> Array * * 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 must be positive."); 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 per thread. */ void Init_byebug_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); } byebug-10.0.1/ext/byebug/locker.c0000644000004100000410000000310413256220560016575 0ustar www-datawww-data#include "byebug.h" /** * 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; static 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-10.0.1/ext/byebug/breakpoint.c0000644000004100000410000002701613256220560017464 0ustar www-datawww-data#include "byebug.h" #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 = true | false * * Enables or disables breakpoint. */ static VALUE brkpt_set_enabled(VALUE self, VALUE enabled) { breakpoint_t *breakpoint; Data_Get_Struct(self, breakpoint_t, breakpoint); return breakpoint->enabled = enabled; } /* * 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; } static 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; } static 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 } static 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 (!NIL_P(mod_name) && 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-10.0.1/ext/byebug/byebug.c0000644000004100000410000004733513256220560016611 0ustar www-datawww-data#include "byebug.h" 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; static ID idEmpty; /* 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 -> hash * * Returns the catchpoints hash. */ 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 (!NIL_P(catchpoints)) 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); \ \ trace_arg = rb_tracearg_from_tracepoint(trace_point); \ if (verbose == Qtrue) \ trace_print(trace_arg, dc, 0, 0); \ \ if (CTX_FL_TEST(dc, CTX_FL_IGNORE)) \ return; \ \ acquire_lock(dc); #define CALL_EVENT_SETUP \ dc->calced_stack_size++; \ dc->steps_out = dc->steps_out < 0 ? -1 : dc->steps_out + 1; #define RETURN_EVENT_SETUP \ dc->calced_stack_size--; \ \ if (dc->steps_out == 1) \ dc->steps = 1; #define RETURN_EVENT_TEARDOWN \ dc->steps_out = dc->steps_out <= 0 ? -1 : dc->steps_out - 1; /* Functions that return control to byebug after the different events */ static VALUE call_at(VALUE ctx, debug_context_t *dc, ID mid, int argc, VALUE arg) { struct call_with_inspection_data cwi; VALUE argv[1]; argv[0] = arg; cwi.dc = dc; cwi.ctx = ctx; cwi.id = mid; cwi.argc = argc; cwi.argv = &argv[0]; return call_with_debug_inspector(&cwi); } static VALUE call_at_line(VALUE ctx, debug_context_t *dc) { return call_at(ctx, dc, rb_intern("at_line"), 0, Qnil); } static VALUE call_at_tracing(VALUE ctx, debug_context_t *dc) { return call_at(ctx, dc, rb_intern("at_tracing"), 0, Qnil); } static VALUE call_at_breakpoint(VALUE ctx, debug_context_t *dc, VALUE breakpoint) { dc->stop_reason = CTX_STOP_BREAKPOINT; return call_at(ctx, dc, rb_intern("at_breakpoint"), 1, breakpoint); } static VALUE call_at_catchpoint(VALUE ctx, debug_context_t *dc, VALUE exp) { dc->stop_reason = CTX_STOP_CATCHPOINT; return call_at(ctx, dc, rb_intern("at_catchpoint"), 1, exp); } static VALUE call_at_return(VALUE ctx, debug_context_t *dc, VALUE return_value) { dc->stop_reason = CTX_STOP_BREAKPOINT; return call_at(ctx, dc, rb_intern("at_return"), 1, return_value); } static VALUE call_at_end(VALUE ctx, debug_context_t *dc) { dc->stop_reason = CTX_STOP_BREAKPOINT; return call_at(ctx, dc, rb_intern("at_end"), 0, Qnil); } static void call_at_line_check(VALUE ctx, debug_context_t *dc, VALUE breakpoint) { dc->stop_reason = CTX_STOP_STEP; if (!NIL_P(breakpoint)) call_at_breakpoint(ctx, dc, breakpoint); reset_stepping_stop_points(dc); call_at_line(ctx, dc); } /* 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); 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); else { 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); } EVENT_TEARDOWN; } static void call_event(VALUE trace_point, void *data) { VALUE brkpnt, klass, msym, mid, binding, self; EVENT_SETUP; if (dc->calced_stack_size <= dc->dest_frame) CTX_FL_UNSET(dc, CTX_FL_IGNORE_STEPS); CALL_EVENT_SETUP; /* 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); 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); } EVENT_TEARDOWN; } static void return_event(VALUE trace_point, void *data) { VALUE brkpnt, file, line, binding; EVENT_SETUP; RETURN_EVENT_SETUP; if ((dc->steps_out == 0) && (CTX_FL_TEST(dc, CTX_FL_STOP_ON_RET))) { reset_stepping_stop_points(dc); call_at_return(context, dc, rb_tracearg_return_value(trace_arg)); } else if (!NIL_P(breakpoints)) { file = rb_tracearg_path(trace_arg); /* * @todo Sometimes the TracePoint API gives some return events without * file:line information, so we need to guard for nil until we know what's * going on. This happens, for example, with active_support core extensions: * * [#7] call@.../core_ext/numeric/conversions.rb:124 Fixnum#to_s * [#7] b_call@.../core_ext/numeric/conversions.rb:124 BigDecimal#to_s * [#7] line@.../core_ext/numeric/conversions.rb:125 BigDecimal#to_s * [#7] c_call@.../core_ext/numeric/conversions.rb:125 Kernel#is_a? * [#7] c_return@.../core_ext/numeric/conversions.rb:125 Kernel#is_a? * [#7] line@.../core_ext/numeric/conversions.rb:131 BigDecimal#to_s * [#7] c_call@.../core_ext/numeric/conversions.rb:131 Fixnum#to_default_s * [#7] c_return@.../core_ext/numeric/conversions.rb:131 Fixnum#to_default_s * [#7] b_return@/hort/core_ext/numeric/conversions.rb:133 BigDecimal#to_s * [#7] return@:0 Fixnum#to_s # => This guy... */ if (!NIL_P(file)) { line = rb_tracearg_lineno(trace_arg); binding = rb_tracearg_binding(trace_arg); brkpnt = find_breakpoint_by_pos(breakpoints, file, line, binding); if (!NIL_P(brkpnt)) call_at_return(context, dc, rb_tracearg_return_value(trace_arg)); } } RETURN_EVENT_TEARDOWN; EVENT_TEARDOWN; } static void end_event(VALUE trace_point, void *data) { EVENT_SETUP; RETURN_EVENT_SETUP; if ((dc->steps_out == 0) && (CTX_FL_TEST(dc, CTX_FL_STOP_ON_RET))) { reset_stepping_stop_points(dc); call_at_end(context, dc); } RETURN_EVENT_TEARDOWN; EVENT_TEARDOWN; } static void raw_call_event(VALUE trace_point, void *data) { EVENT_SETUP; CALL_EVENT_SETUP; EVENT_TEARDOWN; } static void raw_return_event(VALUE trace_point, void *data) { EVENT_SETUP; RETURN_EVENT_SETUP; RETURN_EVENT_TEARDOWN; EVENT_TEARDOWN; } static void raise_event(VALUE trace_point, void *data) { VALUE expn_class, ancestors, pm_context; int i; debug_context_t *new_dc; EVENT_SETUP; raised_exception = rb_tracearg_raised_exception(trace_arg); if (post_mortem == Qtrue) { pm_context = context_dup(dc); rb_ivar_set(raised_exception, rb_intern("@__bb_context"), pm_context); Data_Get_Struct(pm_context, debug_context_t, new_dc); rb_debug_inspector_open(context_backtrace_set, (void *)new_dc); } if (NIL_P(catchpoints) || 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 (!NIL_P(hit_count)) { rb_hash_aset(catchpoints, module_name, INT2FIX(FIX2INT(hit_count) + 1)); call_at_catchpoint(context, dc, raised_exception); call_at_line(context, dc); 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; int end_msk = 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 tpEnd = rb_tracepoint_new(Qnil, end_msk, end_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, tpEnd); 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); 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 ? Qtrue : Qfalse; } /* * 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; return Qfalse; } return Qtrue; } static VALUE Stoppable(VALUE self) { VALUE context; debug_context_t *dc; if (!IS_STARTED) return Qfalse; if (!NIL_P(breakpoints) && rb_funcall(breakpoints, idEmpty, 0) == Qfalse) return Qfalse; if (!NIL_P(catchpoints) && rb_funcall(catchpoints, idEmpty, 0) == Qfalse) return Qfalse; if (post_mortem == Qtrue) return Qfalse; if (RTEST(tracing)) return Qfalse; context = Current_context(self); if (!NIL_P(context)) { Data_Get_Struct(context, debug_context_t, dc); if (dc->steps > 0) 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.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.verbose? -> bool * * Returns +true+ if global verbose flag for TracePoint API events is enabled. */ static VALUE Verbose(VALUE self) { UNUSED(self); return verbose; } /* * call-seq: * Byebug.verbose = bool * * Sets the global verbose flag for TracePoint API events is enabled. */ static VALUE Set_verbose(VALUE self, VALUE value) { UNUSED(self); verbose = 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 hash. */ 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, "stoppable?", Stoppable, 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_byebug_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"); idEmpty = rb_intern("empty?"); } byebug-10.0.1/README.md0000644000004100000410000001506713256220560014367 0ustar www-datawww-data# Byebug [![Version][gem]][gem_url] [![Maintainability][mai]][mai_url] [![Coverage][cov]][cov_url] [![Gitter][irc]][irc_url] [gem]: https://img.shields.io/gem/v/byebug.svg [mai]: https://api.codeclimate.com/v1/badges/f1a1bec582752c22da80/maintainability [cov]: https://api.codeclimate.com/v1/badges/f1a1bec582752c22da80/test_coverage [irc]: https://img.shields.io/badge/IRC%20(gitter)-devs%20%26%20users-brightgreen.svg [gem_url]: https://rubygems.org/gems/byebug [mai_url]: https://codeclimate.com/github/deivid-rodriguez/byebug/maintainability [cov_url]: https://codeclimate.com/github/deivid-rodriguez/byebug/test_coverage [irc_url]: https://gitter.im/deivid-rodriguez/byebug Byebug is a simple to use, feature rich debugger for Ruby. It uses the TracePoint API for execution control and the 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 [![Cir][cir]][cir_url] macOS [![Tra][tra]][tra_url] Windows [![Vey][vey]][vey_url] [cir]: https://circleci.com/gh/deivid-rodriguez/byebug/tree/master.svg?style=svg [tra]: https://api.travis-ci.org/deivid-rodriguez/byebug.svg?branch=master [vey]: https://ci.appveyor.com/api/projects/status/github/deivid-rodriguez/byebug?svg=true [cir_url]: https://circleci.com/gh/deivid-rodriguez/byebug/tree/master [tra_url]: https://travis-ci.org/deivid-rodriguez/byebug [vey_url]: https://ci.appveyor.com/project/deivid-rodriguez/byebug ## Requirements * Required: MRI 2.2.0 or higher. * Recommended: MRI 2.3.0 or higher. ## Install ```shell gem install byebug ``` Or if you use `bundler`, ```shell bundle add byebug --group "development, test" ``` ## Usage ### From within the Ruby code Simply drop byebug wherever you want to start debugging and the execution will stop there. If you were debugging Rails, for example, you would add `byebug` to your code. ```ruby def index byebug @articles = Article.find_recent end ``` And then start a Rails server. ```shell bin/rails s ``` Once the execution gets to your `byebug` command you will get a debugging prompt. ### From the command line If you want to debug a Ruby script without editing it, you can invoke byebug from the command line. ```shell byebug myscript.rb ``` ## Byebug's commands Command | Aliases | Subcommands ------- | ------- | ----------- `backtrace` | `bt` `where` | `break` | | `catch` | | `condition` | | `continue` | | `debug` | | `delete` | | `disable` | | `breakpoints` `display` `display` | | `down` | | `edit` | | `enable` | | `breakpoints` `display` `finish` | | `frame` | | `help` | | `history` | | `info` | | `args` `breakpoints` `catch` `display` `file` `line` `program` `interrupt` | | `irb` | | `kill` | | `list` | | `method` | | `instance` `next` | | `pry` | | `quit` | | `restart` | | `save` | | `set` | | `autoirb` `autolist` `autopry` `autosave` `basename` `callstyle` `fullpath` `histfile` `histsize` `linetrace` `listsize` `post_mortem` `savefile` `stack_on_error` `width` `show` | | `autoirb` `autolist` `autopry` `autosave` `basename` `callstyle` `fullpath` `histfile` `histsize` `linetrace` `listsize` `post_mortem` `savefile` `stack_on_error` `width` `source` | | `step` | | `thread` | | `current` `list` `resume` `stop` `switch` `tracevar` | | `undisplay` | | `untracevar`| | `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. * [atom-byebug] provides integration with the Atom editor [EXPERIMENTAL]. ## Contribute See [Getting Started with Development](CONTRIBUTING.md). You can also help `byebug` by leaving a small (or big) tip through [Liberapay][liberapay.com]. [![Support via Liberapay][liberapay-button]][liberapay-donate] ## Credits Everybody who has ever contributed to this forked and reforked piece of software, especially: * @ko1, author of the awesome TracePoint API for Ruby. * @cldwalker, [debugger]'s maintainer. * @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 [atom-byebug]: https://github.com/izaera/atom-byebug [liberapay.com]: https://liberapay.com [liberapay-button]: https://liberapay.com/assets/widgets/donate.svg [liberapay-donate]: https://liberapay.com/byebug/donate