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

Trace exception rescuing with ExceptionTracer? #246

Closed
st0012 opened this issue Sep 2, 2021 · 10 comments
Closed

Trace exception rescuing with ExceptionTracer? #246

st0012 opened this issue Sep 2, 2021 · 10 comments

Comments

@st0012
Copy link
Member

st0012 commented Sep 2, 2021

In addition to seeing where an exception is raised, it'll also help a lot to know when an exception is rescued. This will be very useful for debugging Rails applications or Rack middlewares. In these applications, rescue/re-raise exceptions based on different options is common. For example: https://github.com/rails/rails/blob/main/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb#L36-L40

So it'd really help if ExceptionTracer can also trace rescuing events. But I'm not sure if this would be technically possible when such events are not supported by TracePoint?

@ko1
Copy link
Collaborator

ko1 commented Sep 2, 2021

No supports.

@st0012
Copy link
Member Author

st0012 commented Sep 3, 2021

I see 👍

@st0012 st0012 closed this as completed Sep 3, 2021
@ko1
Copy link
Collaborator

ko1 commented Sep 3, 2021

Now there is no support but we can consider about it.
Do you have a sample scenario?

@ko1
Copy link
Collaborator

ko1 commented Sep 3, 2021

For trace exception we can see re-raised exception:

[master]$ exe/rdbg target.rb -e 'trace exception;; c'
DEBUGGER: Session start (pid: 12419)
[1, 8] in target.rb
     1|
     2| begin
=>   3|   raise "foo"
     4| rescue => e
     5|   raise "bar"
     6| end
     7|
     8| __END__
=>#0    <main> at target.rb:3
(rdbg:commands) trace exception
Enable ExceptionTracer (enabled)
(rdbg:commands) c
DEBUGGER (trace/exception) #th:1 #depth:1  #<RuntimeError: foo> at target.rb:3
DEBUGGER (trace/exception) #th:1 #depth:2  #<RuntimeError: bar> at target.rb:5
Traceback (most recent call last):
target.rb:3:in `<main>': foo (RuntimeError)
        1: from target.rb:2:in `<main>'
target.rb:5:in `rescue in <main>': bar (RuntimeError)

@st0012
Copy link
Member Author

st0012 commented Sep 3, 2021

I got this idea from debugging this issue: getsentry/sentry-ruby#1563

In this case, the exception is raised from a middleware but it doesn't surface to the target middleware Sentry::Rack::CaptureExceptions as expected. Here's what I got from trace exception:

截圖 2021-09-03 下午3 45 28

From the above information, I can know that:

  1. It's re-raised by the ActionDispatch::Callbacks middleware
  2. And it's rescued by something before reaching Sentry::Rack::Middleware so it's undetected by the Sentry SDK. Possible middlewares are:
# use Sentry::Rails::CaptureExceptions
use ActiveSupport::Cache::Strategy::LocalCache::Middleware
use Rack::Runtime
use Rack::MethodOverride
use ActionDispatch::RequestId
use ActionDispatch::RemoteIp
use Sprockets::Rails::QuietAssets
use Rails::Rack::Logger
use ActionDispatch::ShowExceptions
use WebConsole::Middleware
use ActionDispatch::DebugExceptions
use ActionDispatch::ActionableExceptions
use ActionDispatch::Reloader
# use ActionDispatch::Callbacks

I can quickly rule out a few of the suspected middlewares by looking at their names. But I still need to check several of them's implementation to figure this out (which is my job today 😂 ).

So if the ExceptionTracer can also point out where is the exception get rescued, it'll save me a lot of work.

@ko1
Copy link
Collaborator

ko1 commented Sep 3, 2021

And it's rescued by something before reaching Sentry::Rack::Middleware so it's undetected by the Sentry SDK. Possible middlewares are:

Ah, it can be separated locations... I understand the situation.
Now Ruby can't do it but on Ruby 3.2 if possible?
It is a good example, thank you.
But not sure how frequent to support by Ruby. So please tell me if you find it is frequent issue or not.

@st0012
Copy link
Member Author

st0012 commented Sep 3, 2021

When developing Rails applications, I rarely see this kind of issues (probably once or twice a year). It's usually caused by a bad rescue statement written years ago and can easily be spotted by search for rescue keyword.

But as a gem maintainer that supports multiple versions of Rails, it happens more frequently. I think I've dealt with similar issues for four or five times since this year. And it's always painful to track down an exception this way 🥲
Another drawback of the current approach is that I can't debug for my clients if they have middlewares that I'm not familiar with. If this feature is supported by the debugger, they'll be able to see how exceptions travel in their apps with a simple binding.b(do: "trace exception").

@st0012
Copy link
Member Author

st0012 commented Sep 3, 2021

For this particular task, I think my gem object_tracer does a bit better than the debugger.

It has a method called print_instance_traces(klass) that essentially does

trace object <every_instance_of_klass_initialized_after_the_call>

So I used print_instance_traces(ActiveRecord::ConnectionTimeoutError) to track the exception's instance and got this:

截圖 2021-09-03 下午4 28 53

Perhaps we can have something similar to this like trace instances <klass>?

Of course, the common use case of the method is not to track multiple instances at once. It's to trace instances that aren't easily accessible (like exceptions) with their classes.

@ko1
Copy link
Collaborator

ko1 commented Sep 12, 2021

trace class <class_expr> ?

@st0012
Copy link
Member Author

st0012 commented Sep 13, 2021

@ko1 trace class gives me an impression that we're focusing on the class's events (like inheritance or initialization). but the behavior I propose doesn't focus on the class but its instances. so I think trace instances (or just instance) expresses the intention clearer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

2 participants