Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

XCrashReport : Exception Handling and Crash Reporting - Part 3

0.00/5 (No votes)
19 Oct 2003 4  
Add basic exception handling and crash reporting to your application

Introduction

In Part 1, I discussed the problems of debugging a program that has been put into production or shipped to a customer. On the developer's workstation, the program might work fine, but on the customer's system, there are random crashes. To fix the problem, you need to know where the program is crashing, and in Part 1 I discussed an approach to determining this.

In Part 2, I described a simple way to instrument the source code that would facilitate the analysis of stack dumps. But this technique has its own problems, and in this article I will describe my attempts to improve on this, and how I came to the perfect solution.

Deus Ex Minidump

After I wrote Part 2, I really thought this was as far as stack tracing could be taken, without resorting to a much larger exception handler footprint or other technology that might not work on Win9x systems.

I was aware of a new Visual Studio .Net technology called minidumps, which you could apparently load directly into the .Net debugger and see the call stack, but I could find very little information on how this was done. Not having Visual Studio .Net, it did not seem worth pursuing - even if I could generate a minidump, how could I look at it? Also, there was a question about whether Win9x systems could create a minidump.

Then two things happened. I found out that the dbghelp.dll - which contains the MiniDumpWriteDump() API - could be redistributed. Second, I learned of a way to read the minidumps, without using Visual Studio .Net.

Preparation

The MiniDumpWriteDump() API is pretty easy to use, for what it does. Here is what MSDN says:

MiniDumpWriteDump

The MiniDumpWriteDump function writes user-mode minidump information to the specified file.

BOOL MiniDumpWriteDump(
HANDLE hProcess,
DWORD ProcessId,
HANDLE hFile,
MINIDUMP_TYPE DumpType,
PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
PMINIDUMP_CALLBACK_INFORMATION CallbackParam
);

Parameters

hProcess
[in] Handle to the process for which the information is to be generated. This handle must have read and query access.

ProcessId
[in] Identifier of the process for which the information is to be generated.

hFile
[in] Handle to the file in which the information is to be written.

DumpType
[in] Type of information to be generated. This parameter can be one of the values from the MINIDUMP_TYPE enumeration.

ExceptionParam
[in] Pointer to a MINIDUMP_EXCEPTION_INFORMATION structure describing the client exception that caused the minidump to be generated. If the value of this parameter is NULL, no exception information is included in the minidump file.

UserStreamParam
[in] Pointer to an array of MINIDUMP_USER_STREAM_INFORMATION structures. If the value of this parameter is NULL, no user-defined information is included in the minidump file.

CallbackParam
[in] Pointer to a MINIDUMP_CALLBACK_INFORMATION structure that specifies a callback routine which is to receive extended minidump information. If the value of this parameter is NULL, no callbacks are performed.

Return Values
If the function succeeds, the return value is TRUE; otherwise, the return value is FALSE.

Remarks
The MiniDumpCallback function receives extended minidump information from MiniDumpWriteDump. It also provides a way for the caller to determine the granularity of information written to the minidump file, as the callback function can filter the default information.

MiniDumpWriteDump may not produce a valid stack trace for the calling thread. To work around this problem, you must capture the state of the calling thread before calling MiniDumpWriteDump and use it as the ExceptionParam parameter. One way to do this is to force an exception inside a __try/__except block and use the EXCEPTION_POINTERS information provided by GetExceptionInformation. Alternatively, you can call the function from a new worker thread and filter this worker thread from the dump.

Requirements
Client: Included in Windows XP.
Server: Included in Windows Server 2003.
Redistributable: Requires dbghelp.dll on Windows 2000, Windows NT 4.0, and Windows Me/98/95.
Header: Declared in DbgHelp.h.
Library: Use Dbghelp.lib.

The one tricky parameter is the DumpType. The only value for DumpType that I could get to work on Win9x systems was MiniDumpNormal.

Theory Into Practice

In order to get set up to call MiniDumpWriteDump(), we need to open a file for writing and pass its handle to MiniDumpWriteDump(). In the newly revised ExceptionHandler.cpp module, this is how we call MiniDumpWriteDump():
///////////////////////////////////////////////////////////////////////////////
// DumpMiniDump
static void DumpMiniDump(HANDLE hFile, PEXCEPTION_POINTERS excpInfo)
{
    if (excpInfo == NULL) 
    {
        // Generate exception to get proper context in dump
        __try 
        {
            OutputDebugString(_T("raising exception\r\n"));
            RaiseException(EXCEPTION_BREAKPOINT, 0, 0, NULL);
        } 
        __except(DumpMiniDump(hFile, GetExceptionInformation()),
                 EXCEPTION_CONTINUE_EXECUTION) 
        {
        }
    } 
    else
    {
        OutputDebugString(_T("writing minidump\r\n"));
        MINIDUMP_EXCEPTION_INFORMATION eInfo;
        eInfo.ThreadId = GetCurrentThreadId();
        eInfo.ExceptionPointers = excpInfo;
        eInfo.ClientPointers = FALSE;

        // note:  MiniDumpWithIndirectlyReferencedMemory does not work on Win98
        MiniDumpWriteDump(
            GetCurrentProcess(),
            GetCurrentProcessId(),
            hFile,
            MiniDumpNormal,
            excpInfo ? &eInfo : NULL,
            NULL,
            NULL);
    }
}

This new ExceptionHandler.cpp is incorporated into the XCrashReportTest demo program. When the program is run, you may choose different types of crashes:

screenshot

When you click on one of the buttons, the program crashes and it generates a minidump file called CRASH.DMP Remember when I said that I learned of a way to read the minidumps, without using Visual Studio .Net? Here is how: use WinDbg, that is available for download from Microsoft Debugging Tools. WinDbg requires that the pdb file be in the same directory as the exe and the CRASH.DMP file. After starting WinDbg, go to File | Open Crash Dump and select the CRASH.DMP file. Next enter ALT+1 to switch to the command window, and type .ecxr and hit Enter. You will see:

screenshot

In the lower window you will see that OnCrash called Crash1 which called Crash2 which called CrashTestFunction, and the crash occurred at line 33 in crashtest.cpp.

Summary

We now have a way of getting the call stack and finding the crash location, without needing to instrument the source code. Downloading and learning to use WinDbg require a few minutes - OK, maybe more than a few if you have dialup. But it is only a one-time thing, and it seems worthwhile. The bottom line: exception handling and minidumps can be added to any existing MFC application by adding a few files, changing three build settings, and recompiling. The only additional work for you is to save the pdb file, each time you make a new version.

I thought this was going to be the last article about crash reporting, but then I came across something so brilliant that I knew I had to incorporate it into my approach to handling exceptions and crash reporting. I will tell you all about it in Part 4.

Revision History

Version 1.1 - 2003 October 19

  • Initial public release

Usage

This software is released into the public domain. You are free to use it in any way you like, except that you may not sell this source code. If you modify it or extend it, please to consider posting new code here for everyone to share. This software is provided "as is" with no expressed or implied warranty. I accept no liability for any damage or loss of business that this software may cause.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here