Unhandled C++ exceptions and Doc Watson

Here is my problem. I have an unmanaged C++ application that occasionally has an unhandled C++ exception. Following good debugging practices I have the PDBs installed on the target machines that demonstrate the problem. Under normal circumstances when an exception occurs Doc Watson will log the information I need (like call stack) to duplicate the problem. Doc Watson is properly configured and works for general exceptions like Win32 exceptions.

However for C++ exceptions that aren't handled Doc Watson is never invoked. Now looking into this some more I realized that abort is being called which will display the standard "your program has a problem" dialog and then close the app. Supposedly under VS2005 it should also invoke Doc Watson (because abort_behavior is set to _CALL_REPORTFAULT). However this doesn't happen. I've explicitly set this flag in my code and it has absolutely no effect. Doc Watson absolutely refuses to be displayed. The best I can get is the Windows Error Reporting dialog to show up with the Debug option when I explicitly disable _WRITE_ABORT_MSG and enable _CALL_REPORTFAULT. However try as I may Doc Watson won't generate a log.

Anybody have any idea how I can get Doc Watson to generate a log when an unhandled C++ exception occurs. Here is my sample code that I'm trying to get working properly.

#include <windows.h>
#include <iostream>

void main ( )
{
_set_abort_behavior(0, _WRITE_ABORT_MSG); //T2

_set_abort_behavior(-1, _CALL_REPORTFAULT);
throw "C++ exception"; //T2
//abort(); //TU2
}

Note that commenting out lines T2 and uncommenting lines TU2 does work properly. It almost appears like abort isn't being called in the same manner when an unhandled exception occurs.

Thanks,
Michael Taylor - 2/21/06



Answer this question

Unhandled C++ exceptions and Doc Watson

  • Ben Jackson

    I've tried the above suggestions of adding calls to _set_abort_behavior and set_terminate, and like Michael Taylor they didn't seem to work on a virgin WinXP system. So I removed the calls from my app and went back to what I had been working on, but I had forgotten that I left the (intentional) crash in the code. It crashed again, but this time the Dr. Watson log was generated, even after removing the function calls above! I'll be damned if I know what kicked it into generating the log files. Before I started all this it was not generating the files.

    I had configured Dr. Watson to generate a log file, but I did that yesterday when playing with _set_abort_behavior. This morning I added the calls to set_terminate, crashed it, and then removed the calls and ran it again before thinking to look for the log files. Something in this morning's sequence caused it to start generating the log file. And with the PDB file present, the stack trace is wonderful.

    The only other thing I can think is that mysteriously the AEDEBUG registry key became configured correctly (but I didn't go into regedit, so how ):

    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug]
    "Auto"="1"
    "Debugger"="drwtsn32 -p %ld -e %ld -g"
    "UserDebuggerHotKey"=dword:00000000

    Michael - is your Aedebug key already set this way If not, does this make it work

    I'm ecstatic it works on this one box, but I want to know how to enable it on all customer boxes we ship!!



  • GavinWu

    C++ exceptions are enabled for compilation because I require the stack to be unwound and the C++ objects to be properly cleaned up. Here is the entire C++ command line (note that these are the defaults for a new project except for one or two) for the test VS2005 C++ app I gave earlier:

    /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS"
    /Gm /EHsc /RTC1 /MDd /Fo"Debug\\" /Fd"Debug\vc80.pdb"
    /W3 /nologo /c /Wp64 /ZI /TP /errorReport:prompt

    For comparison here is the command line for the actual application that is having the problem (in VS2003):

    /Od /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "_WIN32_WINNT=0x0500"
    /D "WINVER=0x0400" /D "_AFXDLL"
    /FD /EHsc /RTC1 /MDd /GS /GR /Yu"stdafx.h"
    /Fp".\Debug/SDAT.pch" /Fo".\Debug/" /Fd".\Debug/"
    /FR".\Debug/" /W3 /nologo /c /Zi

    Thanks,
    Michael Taylor - 2/22/06


  • Guy C.

    What is your command line for compilation Specifically I'm wondering about /EH... but I'd like to see your whole command line for compile and link.

    Thanks,

    Ben



  • Lee Coward

    I did some more digging and it looks like the correct course of action would be to call set_terminate which is the handler for unhandled exceptions. Check:

    ms-help://MS.VSCC.v80/MS.MSDN.v80/MS.VisualStudio.v80.en/dv_vclang/html/13f09c53-9254-4407-9db9-14e730e047cc.htm

    and

    ms-help://MS.VSCC.v80/MS.MSDN.v80/MS.VisualStudio.v80.en/dv_vcstdlib/html/b949bd45-75ae-4fe8-af2c-f6ce67167d67.htm

    in the 2005 installed msdn help.



  • Helen Feddema

    Bump. I'm having the exact same problem as described in the initial post in VS2003. Unhandled Win32 exceptions generate a Dr Watson log & dump, and unhandled C++ exceptions do not. I'd like to get unhandled C++ exceptions to generate a Watson dump since we have a pretty good reason to let certain C++ exceptions escape: we'd like to get stack overflow recovery working, and to do so we need to eliminate a lot of generic catch(...) {throw app_defined_exception} clauses. We have a large and complicated hardware control application that occasionally crashes without a trace due to stack overflow problems. Removing context-adding catch(...) to cleanly handle stack overflows would be great (see _resetstkoflw()), but we can't afford to lose the context for other errors, so getting the dump and stack trace via Dr. Watson would be ideal.

    Thanks,
    Derek


  • Pimenta

    Running Drwtsn32 -i will set it up properly like that. I have "Auto" set to "0" so it won't auto-start the debugger but otherwise it is the same. Just for grins I change "Auto" but it had no effect.

    Thanks,
    Michael Taylor - 2/23/06


  • N.I.C.

    After playing around some I found something that seems to work. Using SetUnhandledExceptionFilter() to install a filter for unhandled structured exceptions that does nothing but return EXCEPTION_CONTINUE_SEARCH seems to get Dr. Watson to produce a dump on the C++ exception as desired. Since C++ exceptions are built on top of structured exceptions, I'm guessing that the default SE handler detects SE exceptions that are flagged as C++ exceptions and routes them to the default C++ handler (set with set_terminate()), and installing your own SE handler intercepts that. The assumption here is that the core problem is not something to do with Dr. Watson, but instead with how the default C++ handler signals the OS for the dump. I don't know though. In testing, any default C++ handler installed with set_terminate() is ignored, but that is fine by my project, since we want the hard dump on all exceptions, both non-C++ structured and C++. As desired, the dump exception info includes the call stack to the point where the exception was thrown.

    If this didn't work, I was going to try having the SE filter signal a worker thread with the exception info so the worker could use MiniDumpWriteDump() to do the dump from within the application. That would have been undesirable though since it would have added complexity.

    Here's the sample code:

    // UnhandledExceptions.cpp : Defines the entry point for the console application.
    //

    #include "stdafx.h"

    #include <eh.h>

    class SimpleException
    {
    public:
    SimpleException() { m_nDataMember = 5; };

    private:

    int m_nDataMember;
    };

    LONG WINAPI TopLevelExceptionFilter( struct _EXCEPTION_POINTERS * /*a_pExceptionInfo */)
    {
    printf( "TopLevelExceptionFilter()\n" );

    return EXCEPTION_CONTINUE_SEARCH;
    }

    void TerminateHandler()
    {
    printf( "TerminateHandler()\n" );
    }

    void Sub4()
    {
    bool l_bThrow = true;
    if( l_bThrow )
    throw SimpleException();

    // This should not be reached. It's just here to test the dump file info.
    if( l_bThrow )
    throw (int) 5;
    }

    void Sub3()
    {
    Sub4();
    }

    void Sub2()
    {
    Sub3();
    }

    void Sub1()
    {
    Sub2();
    }

    int _tmain(int argc, _TCHAR* argv[])
    {

    SetUnhandledExceptionFilter( (LPTOP_LEVEL_EXCEPTION_FILTER) TopLevelExceptionFilter );

    // TerminateHandler() is never called because we installed the unhandled exception filter.
    // That's OK though because we want the automatic Dr. Watson dump instead.
    set_terminate( (terminate_function) TerminateHandler );

    Sub1();

    return 0;
    }



  • robatdural

    Thank you for your assistance. However I disagree on 2 points.

    1) The default implementation of terminate (of which set_terminate replaces) already calls abort. abort internally checks abort_behavior and reacts accordingly. Therefore calling set_terminate to invoke abort would simply bring up the error dialog twice and still have no other impact nor generate a log.

    2) Just to be sure I tried it and it does as I mentioned in point 1. A DW log will not be generated.

    The problem, I believe, is that DW (which is set up as the JIT debugger) isn't handling C++ exceptions. Internally it appears that it might only work with Win32 exceptions and therefore simply ignores the debugging request. I believe this because when properly configured I can get the C RTL to provide me the option of connecting to the debugger (DW in this case) but then DW doesn't do anything. Therefore it could be a limitation of DW itself.

    Thanks,
    Michael Taylor - 2/22/06




  • Unhandled C++ exceptions and Doc Watson