What to do when you don’t have a crash dump for your Windows program

If you’ve ever been in the frustrating situation of having a crash report from a machine where crash dumps are not enabled, then you’ve probably been left scratching your head wondering what to do. If you’d had a minidump then you could have just loaded it into Visual Studio and continued from there. Similarly, if the machine is yours to play with, and the fault is easy to reproduce then you could just enable crash dumps, but this isn’t always an option. For example, it might be a customer’s machine, or it could be a fault that occurs so infrequently that it could be weeks before you see it again.

The Technique

Fortunately, if you’re able to get access to the Windows event logs from the machine, then there are still some steps that you can take to narrow down the problem.

The technique is pretty straightforward.

  • Get the name of the faulting module and the fault offset from the event log.
  • Plug these two pieces of information into WinDbg to find the offending line.

A Worked Example

Let’s illustrate this with an example that uses a DLL with a single function that deliberately dereferences a null pointer. If you run this code, then your program will crash with an access violation.

// Crashy.cpp : Defines the exported functions for the DLL application.
//

#include "stdafx.h"
#include "Crashy.h"

CRASHY_API void kaboom(void)
{
	int* p = nullptr;
	*p = 0;
}

Get the faulting module and offset from the event log

When the program crashes, Windows will create an entry in the Application event log. Here is what it looks like in Event Viewer.

20170826_event_viewer

The important information is in the preview pane.

20170826_event_details

Here we can see the Faulting module name, Crashy.dll, and its version, 0.0.0.0.

We can see an Exception code, 0xc0000005. This one is pretty well known – it’s an Access Violation, which means that your program has done something that it doesn’t have permission to do. In the case of our example, it dereferenced a null pointer.

And, finally, we can see the Fault offset, 0x000115b8. This is important as it tells us where the problem occurred, relative to the start of the faulting module.

Using WinDbg to find the problem

Using the name of the faulting module, its version, and the fault offset, we can narrow down the problem, even to a single line of code. We can do this using WinDbg, an ancient, venerable, and powerful debugger.

WinDbg comes with Debugging Tools for Windows, an optional part of the Windows SDK. If you don’t have WinDbg installed already, but you have the SDK installed, then you can install it as follows:

  • Go to Settings -> Apps & features
  • Scroll down until you find Windows Software Development Kit
  • Click on it and select Modify, then click Change in the dialog that appears.
  • When prompted to Select the features that you want to change, select Debugging Tools for Windows.
  • Click Change to download it and install it.

Otherwise, go here for some options on how to install it.

Now, here’s a big assumption. To get anywhere with this you will need the original DLL and the symbol files (.pdb files) generated for it by Visual C++. If you don’t have these then you’ll have to recompile the exact version of the DLL, but if you’ve been strict about versioning then that shouldn’t be a problem.

Now you have everything that you need to find the line that caused the crash.

Run WinDbg with the -z flag to tell it the name of the dump file (in this case, the DLL) and the -y flag to tell it which folder contains the .pdb files.

windbg -z Debug\Crashy.dll -y Debug

WinDbg should start, and you’ll see something like this.

20170826_windbg_start

If you’re not used to WinDbg, don’t worry. It may look cryptic, but it’s powerful and will get us what we need with a few simple keystrokes.

First, use the lm command to list the modules. This will produce output similar to the following:

20170826_windbg_lm

The important thing here is to look for the start address of the DLL. In this example, it’s 0x10000000.

Take the start address of the crashing DLL and add the Fault offset from Event Viewer, and use this expression as an argument to the ln command. The ln command (list nearest) lists the nearest symbols to the given address.

In this example, the start address is 0x10000000 and the fault offset is 115b8, so we just enter the following:


ln 10000000 + 115b8

Here’s what it shows for our example DLL:

20170826_windbg_ln

And now, we can just click on the link to open the source code on the offending line.

20170826_windbg_source

Obviously, this is a toy example, but this technique works just as well for much larger DLLs. It may not be as good as having a proper minidump to play with, but at least now you’ve got something more to work with than “my program crashed and I don’t know why!”

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s