# test.rb
def foo(a, b)
a.x = nil if b == 5
end
a = Struct.new(:x).new(10)
10.times do |i|
foo(a, i)
puts a.x + i
end
It has a bug and will crash when i is 5:
$ ruby test.rb
10
11
12
13
14
test.rb:9:in `block in <main>': undefined method `+' for nil (NoMethodError)
puts a.x + i
^
from <internal:numeric>:237:in `times'
from test.rb:9:in `<main>'
We can do this:
$ rdbg test.rb
[1, 10] in test.rb
=> 1| def foo(a, b)
2| a.x = nil if b == 5
3| end
4|
5| a = Struct.new(:x).new(10)
6|
7| 10.times do |i|
8| foo(a, i)
9| puts a.x + i
10| end
=>#0 <main> at test.rb:1
(rdbg) break 9 if: a.x == nil # command
#0 BP - Line /Users/loic.nageleisen/test.rb:9 (line) if: a.x == nil
(rdbg) record on # command
Recorder for #<Thread:0x000000010292b0c8 run>: on (0 records)
(rdbg) continue # command
10
11
12
13
14
[4, 10] in test.rb
4|
5| a = Struct.new(:x).new(10)
6|
7| 10.times do |i|
8| foo(a, i)
=> 9| puts a.x + i
10| end
=>#0 block {|i=5|} in <main> at test.rb:9
#1 Integer#times at <internal:numeric>:237
# and 1 frames (use `bt' command for all frames)
Stop by #0 BP - Line /Users/loic.nageleisen/test.rb:9 (line) if: a.x == nil
(rdbg) step back # command
[replay] [4, 10] in test.rb
[replay] 4|
[replay] 5| a = Struct.new(:x).new(10)
[replay] 6|
[replay] 7| 10.times do |i|
[replay] 8| foo(a, i)
[replay] => 9| puts a.x + i
[replay] 10| end
[replay] =>#0 block {|i=5|} in <main> at test.rb:9
[replay] #1 Integer#times at <internal:numeric>:237
[replay] # and 1 frames (use `bt' command for all frames)
(rdbg) step back # command
[replay] [1, 10] in test.rb
[replay] 1| def foo(a, b)
[replay] => 2| a.x = nil if b == 5
[replay] 3| end
[replay] 4|
[replay] 5| a = Struct.new(:x).new(10)
[replay] 6|
[replay] 7| 10.times do |i|
[replay] 8| foo(a, i)
[replay] 9| puts a.x + i
[replay] 10| end
[replay] =>#0 Object#foo(a=#<struct x=nil>, b=5) at test.rb:2
[replay] #1 block {|i=5|} in <main> at test.rb:8
[replay] # and 2 frames (use `bt' command for all frames)
(rdbg)
And there we find the line that caused our bug.
Sadly many a time the code is much more sizeable and complex, and it's not that easy to go back step by step.
I would like to propose possible usage:
break 9 if: a.x == nil
record on
continue
# <= breakpoint hit
break if: a.x != nil # or `break a.x=`
continue back # or `reverse continue`
# <= lands on line 3
break 9 if: a.x == nil
record on
continue
# <= breakpoint hit
watch a.x
continue back
# <= lands on line 3
break 9 if: a.x == nil
record on
continue
# <= breakpoint hit
step back until a.x != nil # or `reverse until a.x != nil`
# <= lands on line 3
Both would automate going from the issue occurence back until a specific condition is met.
Your proposal
Here's a simplistic piece of code:
It has a bug and will crash when
i
is5
:We can do this:
And there we find the line that caused our bug.
Sadly many a time the code is much more sizeable and complex, and it's not that easy to go back step by step.
I would like to propose possible usage:
Both would automate going from the issue occurence back until a specific condition is met.
Additional context
Inspired by
rr
'sreverse-continue
andwatch -l