2

I have a large WPF application which also uses C++ libraries for some functionality.

From time to time the application crashes due to a unhandled exception or access violation in the C++ code. EDIT: it sometime also crashes on the main thread due to unhandled C# exception.

I already employ the following handlers to log information about the crash:

  • DispatcherUnhandledException
  • TaskScheduler.UnobservedTaskException
  • AppDomain.CurrentDomain.UnhandledException

(EDIT: I register to these events very similar to this example: https://stackoverflow.com/a/46804709/2523211)

However, if I enabled dump file creation and these functions are reached (i.e. in an unhandled exception scenario) then the stack of the original exception has already unwound itself and there is no way for me to inspect the call stack along with the memory and threads at the moment of the error itself.

I can obviously see the stack trace itself in the exception that I get as argument to those handlers, but that is as far as that goes and I wish to see the state of my code when the exception was thrown.

(EDIT: The call stack just shows that I'm in a dispatcher frame and I cannot inspect the variables and other memory state of the application at the moment of the exception. I can use the data from the exception itself and see the call stack from it, but that's not enough to reproduce or really understand why the exception happened)

If I don't subscribe to those events then nothing changes, I still can't see the original call stack in the dump file. (EDIT: because I only get a call stack in the dispatcher)

I've also tried to set e.Handled = false but I think that that is the default value anyways.

Is there someway to indicate to WPF's dispatcher or maybe the issue is somewhere else, so that if an exception does happen, let it propagate all the way up such that when a dump is created for it, I will be able to have a helpful dump file?

ZivS
  • 2,094
  • 2
  • 27
  • 48
  • Did you check Windows Event Viewer? – Dilshod K Jan 06 '22 at 06:16
  • Yes it shows there, but I want to get a dump file with the call stack and memory state – ZivS Jan 06 '22 at 10:11
  • Can you show log info from windows event viewer and also can you show the peace of code of handling exception? – Dilshod K Jan 06 '22 at 10:17
  • How about use https://learn.microsoft.com/en-us/dotnet/api/system.runtime.exceptionservices.handleprocesscorruptedstateexceptionsattribute?view=net-6.0 – lindexi Jan 06 '22 at 13:19
  • Stack unwinding is how the Exception is handler gets called. It isn't possible to handle the exception without the stack unwinding, WITHIN the application. However, VS does this all the time, using the WinDbg APIs/ – Aron Jan 07 '22 at 04:06
  • If you have access to the code of the dll, maybe add some internal logging to it? – Kevin Cook Jan 11 '22 at 13:21

2 Answers2

2

What you are asking for is impossible to do within dotnet. You want to handle an exception without the stack being unwound, and dump your core for later analysis.

You can however do this outside of dotnet using the WinDbg APIs. Luckily, there is a wrapper for the WinDbg APIs called clrmd.

Just implement IDebugEventCallbacks.Exception and then call WriteDumpFile.

See https://github.com/microsoft/clrmd/blob/124b189a2519c4f13610c6bf94e516243647af2e/src/TestTasks/src/Debugger.cs for more details.

However, you should note. This solution would be attaching a debugger to production. Thus has the associated costs.

Aron
  • 15,464
  • 3
  • 31
  • 64
  • You misunderstood me. I don't want to handle the exception and at the same time make the stack remain unwound (on that matter - doesn't adding a `throw;` propagate the exception?). I mentioned exception handling just as something I tried, which does not help me. But, even if I remove the event handling code by not subscribing to those events, the stack I get in the dump file is still not showing with the original stack that threw the exception – ZivS Jan 08 '22 at 21:56
  • @ZivS I didn't misunderstand you. This is the only way. You need to "debug" the crash. Anything else, in process would need to unwind the stack. I said "handle" meaning anything processing of the exception. You program needs to be running to do anything. The stack pointer needs to move to the code that handles the exception. That means stack unwind. – Aron Jan 09 '22 at 07:11
  • @ZivS if your problem with this answer is that, you only want to write a dump file when you crash, and not when you throw an exception, that isn't possible. Either you are at the first chance of the exception, before the stack unwind, which would require solving the halting problem to know if we have crashed. Or, you unwind the stack completely, let it run until the crash, in which case there is nothing left to dump. – Aron Jan 09 '22 at 08:09
1

If you are just trying to collect information to solve the problem some options include

  1. Use ProcDump https://learn.microsoft.com/en-us/sysinternals/downloads/procdump This can be configured to dump on 1st chance and 2nd chance exceptions, collecting multiple dump files if required i.e. allow app to keep running during 1st chance exceptions while capturing the app state in a dump file. Extensive configuration is possible via cmd line i.e. only handle specific exceptions etc, number of dump files to generate.
  2. Use Time Travel Debugging feature of WinDbg, in this case you can walk backwards through your trace to find cause. This will severely degrade performance and if it takes more than several minutes to reproduce your issue these traces can quickly get very massive. If you copy the TTD folder out of WinDbg installation you can use it via command line to do a "ring mode" trace and specify a maximum buffer size which is more suitable if it takes a long time to reproduce issue, finding the options by running ttd.exe with no parameters.
  3. Create a parent process that attaches as a debugger and will give you near full control over how you to decide to handle exceptions in the child process. Example here of creating a basic debugger
Malcolm McCaffery
  • 2,468
  • 1
  • 22
  • 43