Use [DebuggerDisableUserUnhandledExceptions] by halter73 · Pull Request #57148 · dotnet/aspnetcore (original) (raw)
Doing so would wind up breaking more than once, so I think you don't want to do that
Yes, but my assumption is that the debugger would only break if the exception propagated through user code. In this case, that would mean the exception was thrown from a user-defined exception filter or a filter that called user-defined code. I think the weirdest thing about the logic in the current PR is that the debugger would break out of order with causality seemingly reversed.
- The original user-unhandled exception gets processed by the user-defined exception filter which then throws a secondary exception. The debugger hasn't stopped for the original user-unhandled exception yet because we were still waiting to see if any exception filters handle the exception.
- We then call
Debugger.BreakForUserUnhandledException(ex2)
to break for the secondary exception thrown by the user-defined exception filter. - Then right after that, we call
Debugger.BreakForUserUnhandledException(ex)
with the original exception the triggered the exception filter.
I think this is pretty bad, and I should not have even opened the PR with this behavior. I think any of the following are better options.
- Option A: Never call
BreakForUserUnhandledException
for exceptions thrown from an exception filter since user-defined exception filters are pretty uncommon to begin with and handling this scenario is complicated. - Option B: Don't use
[DebuggerDisableUserUnhandledExceptions]
in the developer exception page middleware at all and live with the debugger breaking before theDatabaseDeveloperPageExceptionFilter
gets to run and offer the option to run the database migration by default. - Option C: Once we see an exception filter has thrown an uncaught exception, call
BreakForUserUnhandledException
for the original exception first then the secondary exception from the exception filter second immediately afterwards.
Do either of you have a preference for which option we should take? Or know of a better option?
I don't think there will be any user-code frames on the exception stack (or the real stack), when I tried it out, you just get a bunch of non-user code frames in the debugger. We also don't have a link between ex2 and the async user frame that threw
Maybe this is the disconnect. My impression is that Debugger.BreakForUserUnhandledException
will only do anything if both of the following conditions are met:
- You have "Just my code" enabled and you have not unchecked "Break when this exception type is user-unhandled"
or configured the "Additional Actions" in the exception settings to "Continue when unhandled in user code"
https://learn.microsoft.com/en-us/visualstudio/debugger/managing-exceptions-with-the-debugger?view=vs-2022#BKMK_UserUnhandled
P.S. the discoverability of disabling this for all exception types just disabling "Just my code" should probably be improved. There's no way I would have considered adding the "Additional Actions" column to exception settings without reading the docs. Can we just have it on by default? There aren't many columns to begin with. Also, it would be nice if I could enable breaking on user-unhandled exceptions even if "Just my code" is disabled. - The exception actually propagates through user code. If there are no user-code frames on the exception stack, I would expect
Debugger.BreakForUserUnhandledException
to never do anything regardless of how VS was configured because the user never had the opportunity to handle the exception.
Is the exception caught here related to the user-code exception, besides the fact that we're in the exception page middleware?
We don't know if the secondary exception relates to the original exception. It certainly could be related if something about the original exception triggered different behavior in a user-defined filter.
I'm not sure how likely it is that a developer would care about this particular exception, seeing as they didn't throw it.
The developer exception page middleware has no good way of knowing if either exception was thrown from user code. I thought it would be up to the debugger to figure this out. Even the original exceptions from inner middleware that the developer exception page middleware handles may not come from or propagate through user code. The same goes for any secondary exceptions thrown by exception filters.