Bug #17581
closedRuby 3.0 backtrace sometimes returns empty array
Description
class Foo
def bar
p caller(1,1)
end
end
[Foo.new].group_by(&:bar)
3.0: []
2.7: ["test2.rb:6:in `each'"]
A similar thing happens when asking for a larger part of the backtrace:
class Foo
def bar
p caller(0,4)
end
end
[Foo.new].group_by(&:bar)
3.0: ["test2.rb:3:in bar'", "test2.rb:6:in
each'", "test2.rb:6:in `group_by'"]
2.7: ["test2.rb:3:in bar'", "test2.rb:6:in
each'", "test2.rb:6:in group_by'", "test2.rb:6:in
'"]
I suspect it was introduced by: https://github.com/ruby/ruby/commit/3b24b7914c16930bfadc89d6aff6326a51c54295
Since it seems to have to do with which frames are returned, though I haven't verified since the commit is difficult to revert.
Updated by jeremyevans0 (Jeremy Evans) almost 4 years ago
- Assignee set to jeremyevans0 (Jeremy Evans)
- Status changed from Open to Assigned
Agreed, it does look likely to be related to the commit. I'll see if I can debug and fix the issue.
Updated by jeremyevans0 (Jeremy Evans) almost 4 years ago
- Backport changed from 2.5: UNKNOWN, 2.6: UNKNOWN, 2.7: UNKNOWN, 3.0: UNKNOWN to 2.5: DONTNEED, 2.6: DONTNEED, 2.7: DONTNEED, 3.0: REQUIRED
This happens for all backtrace frames where there is an iseq
but no pc
. That part didn't change in the commit mentioned, so it must have been handled as a side effect of always looking at the entire backtrace, and filtering the resulting array.
The fix in this case is to keep two counters while iterating the backtrace, one for the desired backtrace size (so we generate the desired number of frames), and one for the actual backtrace size (so we don't process off the end of the stack). When skipping a frame with an iseq
and no pc
, we need to decrement the first counter, since we aren't adding a frame in that iteration. I've submitted a pull request for this: https://github.com/ruby/ruby/pull/4120
This should be backported to 3.0.
Updated by jeremyevans (Jeremy Evans) almost 4 years ago
- Status changed from Assigned to Closed
Applied in changeset git|87437326214e4587a41946c8937e11418d983acd.
Fix backtrace to not skip frames with iseq without pc
Previously, frames with iseq but no pc were skipped (even before
the refactoring in 3b24b7914c16930bfadc89d6aff6326a51c54295).
Because the entire backtrace was procesed before the refactoring,
this was handled by using later frames instead. However, after
the refactoring, we need to handle those frames or they get
lost.
Keep two iteration counters when iterating, one for the desired
backtrace size (so we generate the desired number of frames), and
one for the actual backtrace size (so we don't process off the end
of the stack). When skipping over an iseq frame with no pc,
decrement the counter for the desired backtrace, so it will
continue to process the expected number of backtrace frames.
Fixes [Bug #17581]
Updated by naruse (Yui NARUSE) almost 4 years ago
- Backport changed from 2.5: DONTNEED, 2.6: DONTNEED, 2.7: DONTNEED, 3.0: REQUIRED to 2.5: DONTNEED, 2.6: DONTNEED, 2.7: DONTNEED, 3.0: DONE
ruby_3_0 4328f93f1bf08296115172a279e2d85a0ed80122 merged revision(s) 87437326214e4587a41946c8937e11418d983acd.
Updated by byroot (Jean Boussier) almost 4 years ago
@jeremyevans I'm not sure this was properly fixed:
def label
p caller.first
end
def label_caller
label
end
p RUBY_VERSION
puts 'raw call'
label_caller
puts 'call from group_by'
[1].group_by { label_caller }
2.7.2:
"2.7.2"
raw call
"/tmp/caller_locations.rb:6:in `label_caller'"
call from group_by
"/tmp/caller_locations.rb:6:in `label_caller'"
ruby_3_0
branch (and master
I presume):
"3.0.0"
raw call
"/tmp/caller_locations.rb:6:in `label_caller'"
call from group_by
"/tmp/caller_locations.rb:2:in `label'"
Updated by jeremyevans0 (Jeremy Evans) almost 4 years ago
@byroot (Jean Boussier) https://github.com/ruby/ruby/pull/4237 hasn't been merged yet, I'm guessing that may fix your example.
Updated by byroot (Jean Boussier) almost 4 years ago
@jeremyevans0 (Jeremy Evans) indeed that fixes the issue. Thanks a lot!