How do I get a string description of a Win32 crash while in Top level filter (I am looking for the address of the instruction at the top of the stack)
Asked Answered
H

4

6

If I use a class/method like the one described here how can I get the description/address of the call at the top of the stack?

Basically I want some value I can use in a call to our bug tracking system. I want to "uniquely" identify based on the address of the instruction that caused the exception.

(It is usually something of the form of mydll.dll!1234ABDC())

EDIT:

Some background information:

I am creating a minidump to email to a defect tracking system (fogbugz). In order to reduce duplicates I am trying to come up with a reasonable "signature" for the crash. I know there is an xml PI for FB, but it requires a user logon and we are not sure yet that we can afford to have people sniffing our traffic and getting user information. Emailing is also simpler for now to implement. Later on we will use the XML API to submit minidumps.

Harrus answered 1/3, 2010 at 21:39 Comment(0)
P
4

You need to put the code to do this in your exception filter, by the time you get to the exception handler much of the context information for the exception has been lost.

try 
{
  // whatever
}
except (MyExceptionFilter(GetExceptionInformation()))
{
}

Your filter will look something like this

LONG WINAPI MyExceptionFilter (
   EXCEPTION_POINTERS * pExcept,
   BOOL                 fPassOn)
{
   EXCEPTION_RECORD * pER = pExcept->ExceptionRecord;
   DWORD dwExceptionCode = pER->ExceptionCode;

   TCHAR szOut[MAX_PATH*4]; // exception output goes here.
   szOut[0] = 0;

   MEMORY_BASIC_INFORMATION mbi;
   DWORD cb = VirtualQuery (pER->ExceptionAddress, &mbi, sizeof(mbi));
   if (cb == sizeof(mbi))
      {
      TCHAR szModule[MAX_PATH];
      if (GetModuleFileName ((HMODULE)mbi.AllocationBase, szModule, MAX_PATH))
         {
         wsprintf(szOut, "Exception at '%s' + 0x%X", szModule, 
                  (ULONG_PTR)pER->ExceptionAddress - (ULONG_PTR)mbi.AllocationBase);
         }
      }

   return EXCEPTION_EXECUTE_HANDLER;
}

Of course, you will need to adjust your output a bit for 64 bit architectures, since the ExceptionAddress and AllocationBase will be 64 bit quantities in that case.

Pneumatograph answered 1/3, 2010 at 22:27 Comment(0)
C
1

The EXCEPTION_POINTERS struct which is sent to TopLevelFilter() contains an EXCEPTION_RECORD struct which contains the ExceptionAddress. Which this address you can figure out in which DLL the offending opcode is by enumerating the modules with CreateToolhelp32Snapshot. You can also use the functions in dbghelp.dll to find the symbol which correspond to the address (the function it is in)

Concomitance answered 1/3, 2010 at 22:1 Comment(0)
D
1

GetExceptionInformation will return the EXCEPTION_POINTERS struct which contains information about the exception. The ExceptionRecord member contains an ExceptionAddress member, which is the address of the exception.

You'll need to map this address to a module relative location in your code to be useful. You can use GetModuleHandleEx with the GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS to get the HMODULE (which is also the base address of the module). GetModuleInformation can then be used to get the actual name of the module that the exception occurred in.

This may not be that helpful to you if the fault is actually inside of a system DLL. A more sophisticated scheme would be to generate a stack trace (using Stackwalk64 in dbghelp), and ignoring the topmost frames that are not in your code.

Darrickdarrill answered 1/3, 2010 at 22:3 Comment(1)
+1 yes, it is probably better to just walk the stack until I get to the first call that is our code.Harrus
C
0

You can avoid the pain of printing a string for the exception (what happens if you can save the minidump but can't format a string without crashing?) by saving the minidump instead and using cdb.exe or windbg.exe to extract the exception information.

Capitalize answered 2/3, 2010 at 0:22 Comment(5)
I am not sure what you are suggesting here. I understand I might not be able to format a string, but after my process dies I don't see how I can do this either without any context. My goal is to send an email or another http message.Harrus
@frungash, trying to diagnose a process that has already crashed within the process is pretty fragile. You can either launch a separate watchdog process on startup to catch any crashes or launch one in the exception filter. I highly recommend getting a minidump rather than trying to generate a stack trace or message manually. It makes postmortem debugging infinitely easier.Capitalize
I am saving the minidump - I just want to come up with a "unique" name that describes the crash (and the last few items on the stack seems to be a way to do that effectively) In any case we have the minidump. I am not interested in a new process - I can live with some percentage of failed string creations for more simple code.Harrus
This is a good idea - but I need to report the minidump file to a database and need some kind of "signature" for the error.Harrus
Open the minidump on the server with windbg (or the equivalent), dump out a stack trace, and use that.Capitalize

© 2022 - 2024 — McMap. All rights reserved.