Skip to content
  • Jeremy Evans's avatar
    c07545bb
    Fix multiple bugs in partial backtrace optimization · c07545bb
    Jeremy Evans authored
    This fixes multiple bugs found in the partial backtrace
    optimization added in 3b24b791.
    These bugs occurs when passing a start argument to caller where
    the start argument lands on a iseq frame without a pc.
    
    Before this commit, the following code results in the same
    line being printed twice, both for the #each method.
    
    ```
    def a; [1].group_by { b } end
    def b; puts(caller(2, 1).first, caller(3, 1).first) end
    a
    ```
    
    After this commit and in Ruby 2.7, the lines are different,
    with the first line being for each and the second for group_by.
    
    Before this commit, the following code can either segfault or
    result in an infinite loop:
    
    ```
    def foo
      caller_locations(2, 1).inspect # segfault
      caller_locations(2, 1)[0].path # infinite loop
    end
    
    1.times.map { 1.times.map { foo } }
    ```
    
    After this commit, this code works correctly.
    
    In terms of the implementation, this correctly skips iseq frames
    without pc that occur before the number of frames the caller
    requested to skip.
    
    This rewrites the algorithm used for handling the partial
    backtraces.  It scans from the current frame outward to the
    earliest frame, until it has found the desired number of frames.
    It records that frame as the start frame.  If needed, it continues
    scanning backwards until arg->prev_cfp is set, as that is needed
    to set the location of the first frame. Due to the fact that arg
    is a void pointer, it's not possible to check this directly, but
    this calls the iter_skip function in a situation where it knows
    it will set arg->prev_cfp, and then breaks out of the loop.
    
    Fixes [Bug #18053]
    c07545bb
    Fix multiple bugs in partial backtrace optimization
    Jeremy Evans authored
    This fixes multiple bugs found in the partial backtrace
    optimization added in 3b24b791.
    These bugs occurs when passing a start argument to caller where
    the start argument lands on a iseq frame without a pc.
    
    Before this commit, the following code results in the same
    line being printed twice, both for the #each method.
    
    ```
    def a; [1].group_by { b } end
    def b; puts(caller(2, 1).first, caller(3, 1).first) end
    a
    ```
    
    After this commit and in Ruby 2.7, the lines are different,
    with the first line being for each and the second for group_by.
    
    Before this commit, the following code can either segfault or
    result in an infinite loop:
    
    ```
    def foo
      caller_locations(2, 1).inspect # segfault
      caller_locations(2, 1)[0].path # infinite loop
    end
    
    1.times.map { 1.times.map { foo } }
    ```
    
    After this commit, this code works correctly.
    
    In terms of the implementation, this correctly skips iseq frames
    without pc that occur before the number of frames the caller
    requested to skip.
    
    This rewrites the algorithm used for handling the partial
    backtraces.  It scans from the current frame outward to the
    earliest frame, until it has found the desired number of frames.
    It records that frame as the start frame.  If needed, it continues
    scanning backwards until arg->prev_cfp is set, as that is needed
    to set the location of the first frame. Due to the fact that arg
    is a void pointer, it's not possible to check this directly, but
    this calls the iter_skip function in a situation where it knows
    it will set arg->prev_cfp, and then breaks out of the loop.
    
    Fixes [Bug #18053]
Loading