Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Filter backtrace before formatting in #handle_exception #916

Merged
merged 1 commit into from
Apr 5, 2024

Conversation

joshbroughton
Copy link
Contributor

Why is this change needed?

The current implementation of handle_exception formats the exception backtrace using Exception#full_message prior to applying filter_backtrace on split lines of the full_message. Other projects that use irb intuitively expect that backtrace filtering will act on objects that conform to the Exception#backtrace API, and this discrepancy has introduced a bug in Rails where backtraces in the Rails console are entirely silenced (rails/rails#49259)

Closes #913

Why this solution?

We considered working around this in Rails, but the backtrace_cleaner class in Rails is not only used for the console; it still needs to work in other contexts where backtrace cleaning is actually being applied to Exception#backtrace.

This proposed change is the least amount of change to fix the behaviour - the filter now acts on Exception#backtrace, but the rest of the logic for formatting and order remains unchanged and relies on Exception#full_message. This seems to be a reasonable use case for Exception#full_message and avoids needing to rewrite the formatting and ordering logic that is already working.

The filtering previously did not apply to irb bugs (it was within unless irb_bug), so it was added to the else branch of the irb_bug conditional to preserve that behaviour.

Testing

This branch passes the tests run with bundle exec rake. It looks like handle_exception is tested by the tests eval_input within test_context.rb, which I briefly looked over and the test coverage seems good to me. This is my first contribution to irb, please let me know if there are other testing requirements.

With respect to fixing the bug in Rails, here is a screenshot showing the correct/expected backtrace in the Rails console in a fresh Rails test app pointing to the version of irb proposed in this PR.
Screenshot 2024-04-03 at 11 17 05 AM

@@ -1222,6 +1222,8 @@ def handle_exception(exc)
irb_bug = true
else
irb_bug = false
backtrace = exc.backtrace.map { |l| @context.workspace.filter_backtrace(l) }.compact
exc.set_backtrace(backtrace)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think IRB should not modify the original exception's backtrace.
How about exc = exc.dup before set_backtrace?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a great point, I'll push up that change

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, this results in some test failures, whether added directly above exc.set_backtrace(backtrace) or at the very top of the method (before the conditional). I wonder if its because exc.dup creates a shallow copy?

I will investigate those failures more to see if exc.dup can work, but if not, should I look for some other way to avoid modifying the original exception's backtrace?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should I look for some other way to avoid modifying the original exception's backtrace?

Yes, please.

One way could be
lines = lines.map { |l| @context.workspace.filter_backtrace(l[/REGEXP_TO_EXTACT_BACKTRACE_PART/]) }.compact
Another way is to modify the backtrace and restore it in ensure statement, but I'd like to avoid doing this if possible.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good. I appreciate the feedback, I'll explore the options when I have some time.

Should I close this PR and open a new one when I have a solution that doesn't modify the original exception's backtrace? Or should I just push that solution here when it is ready?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I tried using exc.clone rather than exc.dup and that appears to resolve whatever part of the state wasn't getting copied using dup.

@joshbroughton joshbroughton force-pushed the jb/fix-handle-exception branch 2 times, most recently from 509aff6 to 2c490c1 Compare April 4, 2024 17:49
Copy link
Member

@st0012 st0012 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's worth having this small fix to immediately improve Rails users' dev experience.

Comment on lines +1225 to +1231
backtrace = exc.backtrace.map { |l| @context.workspace.filter_backtrace(l) }.compact
exc = exc.clone
exc.set_backtrace(backtrace)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can add some comments to make the intention and context clearer:

Suggested change
backtrace = exc.backtrace.map { |l| @context.workspace.filter_backtrace(l) }.compact
exc = exc.clone
exc.set_backtrace(backtrace)
# This is mostly to make IRB work nicely with Rails console's backtrace filtering, which patches WorkSpace#filter_backtrace
# In such use case, we want to filter the exception's backtrace before its displayed through Exception#full_message
# And we clone the exception object in order to avoid mutating the original exception
# TODO: introduce better API to expose exception backtrace externally
backtrace = exc.backtrace.map { |l| @context.workspace.filter_backtrace(l) }.compact
exc = exc.clone
exc.set_backtrace(backtrace)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Stan!

What will release for this look like? Will it be patched onto older versions of irb or available on the next release? I have no idea how these things work. I'm wondering when we can mark the rails issue as fixed

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be included in the next IRB release (1.13 or 1.12.1). If Rails apps bump their IRB version, they'll be running Rails console with this patch.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome, thanks!

handle_exception now applies the filter_backtrace to exception
backtraces prior to formatting the lines with Exception#full_message

This fixes a bug in upstream projects, notably Rails, where the
backtrace filtering logic expects the lines to be formatted as
Exception#backtrace.

Co-authored-by: Hartley McGuire <[email protected]>
@tompng
Copy link
Member

tompng commented Apr 5, 2024

Thank you! looks good 👍

@tompng tompng merged commit 805ee00 into ruby:master Apr 5, 2024
29 checks passed
matzbot pushed a commit to ruby/ruby that referenced this pull request Apr 5, 2024
(ruby/irb#916)

handle_exception now applies the filter_backtrace to exception
backtraces prior to formatting the lines with Exception#full_message

This fixes a bug in upstream projects, notably Rails, where the
backtrace filtering logic expects the lines to be formatted as
Exception#backtrace.

ruby/irb@805ee008f9

Co-authored-by: Hartley McGuire <[email protected]>
artur-intech pushed a commit to artur-intech/ruby that referenced this pull request Apr 26, 2024
(ruby/irb#916)

handle_exception now applies the filter_backtrace to exception
backtraces prior to formatting the lines with Exception#full_message

This fixes a bug in upstream projects, notably Rails, where the
backtrace filtering logic expects the lines to be formatted as
Exception#backtrace.

ruby/irb@805ee008f9

Co-authored-by: Hartley McGuire <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

Rewrite handle_exception to apply backtrace filtering to Exception#backtrace instead of Exception#full_message
3 participants