I wanted to try using the class CImage, which comes from GDI+, to load an image and draw it at some place in my Graphical User Interface.
However, the programm crashes as soon as I call CImage's constructor, it seems, displaying the error:
Unhandled exception at 0x7757D968 (ntdll.dll) in myProgramm.exe: 0xC0000005: Access violation writing location 0x00000014
If I choose to break the programm after the exception, I can see that the cursor, which indicate the current called function, is pointing some CImage's method :
CImage::CInitGDIPlus::IncreaseCImageCount()
That's a reason why I was supposing GDI+ initialization could be the cause of the problem. So I have written a sample to possibly identify the problem, trying whether to initialize GDI+ or not.
// main.cpp
// (Do not forget to link gdiplus.lib and activate "Use MFC in a shared DLL")
#include <afxwin.h>
#include <afxdockablepane.h> // CImage seems to be unknown without this include. (My actual program uses it anyway.)
#include <Gdiplus.h>
void testWithoutGdipInititalization()
{
CImage image ; // The programm seems to crash here, at the construction of the image...
}; // ...And not here, at the destruction of the image
void testWithGdipInititalization()
{
// Initialize GDI+.
Gdiplus::GdiplusStartupInput gdipStartupInput;
ULONG_PTR gdipToken ;
Gdiplus::GdiplusStartup(&gdipToken, &gdipStartupInput, NULL);
{
CImage image ; // The programm seems to crash here, at the construction of the image...
} // ...And not here, at the destruction of the image
// Close Gdiplus
Gdiplus::GdiplusShutdown(gdipToken);
};
int main(int argc, char *argv[])
{
bool withInitialization = true ;
if(withInitialization)
testWithGdipInititalization();
else
testWithoutGdipInititalization();
return EXIT_SUCCESS;
}
...And both fail the same way.
Does someboy know what I'm doing wrong ?
Related
I have a question which is quite general, but I hope someone will be able to at least point me in the right direction.
I created my project a I was building it only in Debug mode with /MDd flag.
But it started to have perfomance issues, so I wanted to try it in Release mode to see, how it goes.
Problem is, that when I use /MD or /MT flag and Release mode my application instantly crashes.
So I tried to find out why. It works fine in Debug. I've tried some code changes, but nothing helped. So I decided to make my app just start and comment out rest of my code. But it was still crashing. Even when my code was unused. It didn't crash only, when I completly removed those unused parts of code.
I think it's something with variable inicialization/declaration, but I'm not quite sure what I should look for.
Could someone suggest me what can cause application to crash even if it's just Declaration/Inicialization and is not even used in RunTime?
I hope you can somehow understand what is my problem.
Thanks for any suggestions!
EDIT: Code which crashes, when unused code is in project, but does not crash when i remove unused code.
#include "core/oxygine.h"
#include "Stage.h"
#include "DebugActor.h"
//#include "Galatex.h"
using namespace oxygine;
//called each frame
int mainloop()
{
//galatex_update();
//update our stage
//update all actors. Actor::update would be called also for all children
getStage()->update();
if (core::beginRendering())
{
Color clearColor(32, 32, 32, 255);
Rect viewport(Point(0, 0), core::getDisplaySize());
//render all actors. Actor::render would be called also for all children
getStage()->render(clearColor, viewport);
core::swapDisplayBuffers();
}
//update internal components
//all input events would be passed to Stage::instance.handleEvent
//if done is true then User requests quit from app.
bool done = core::update();
return done ? 1 : 0;
}
//it is application entry point
void run()
{
ObjectBase::__startTracingLeaks();
//initialize Oxygine's internal stuff
core::init_desc desc;
#if OXYGINE_SDL || OXYGINE_EMSCRIPTEN
//we could setup initial window size on SDL builds
desc.w = 1800;
desc.h = 1000;
//marmalade settings could be changed from emulator's menu
#endif
//galatex_preinit();
core::init(&desc);
//create Stage. Stage is a root node
Stage::instance = new Stage(true);
Point size = core::getDisplaySize();
getStage()->setSize(size);
//DebugActor is a helper actor node. It shows FPS, memory usage and other useful stuff
DebugActor::show();
//initialize this example stuff. see example.cpp
//galatex_init();
#ifdef EMSCRIPTEN
/*
if you build for Emscripten mainloop would be called automatically outside.
see emscripten_set_main_loop below
*/
return;
#endif
//here is main game loop
while (1)
{
int done = mainloop();
if (done)
break;
}
//user wants to leave application...
//lets dump all created objects into log
//all created and not freed resources would be displayed
ObjectBase::dumpCreatedObjects();
//lets cleanup everything right now and call ObjectBase::dumpObjects() again
//we need to free all allocated resources and delete all created actors
//all actors/sprites are smart pointer objects and actually you don't need it remove them by hands
//but now we want delete it by hands
//check example.cpp
//galatex_destroy();
//renderer.cleanup();
/**releases all internal components and Stage*/
core::release();
//dump list should be empty now
//we deleted everything and could be sure that there aren't any memory leaks
ObjectBase::dumpCreatedObjects();
ObjectBase::__stopTracingLeaks();
//end
}
#ifdef __S3E__
int main(int argc, char* argv[])
{
run();
return 0;
}
#endif
#ifdef OXYGINE_SDL
#include "SDL_main.h"
extern "C"
{
int main(int argc, char* argv[])
{
run();
return 0;
}
};
#endif
#ifdef EMSCRIPTEN
#include <emscripten.h>
void one() { mainloop(); }
int main(int argc, char* argv[])
{
run();
emscripten_set_main_loop(one, 0, 0);
return 0;
}
#endif
So I'll write it here for possibly other newbies like me which would find themselves in similar sutiation.
My problem was in Initialization of static and other variables which were "outside of function". For example:
MyObject object = new MyObject(); //This was the reason, why it was crashing, just had to move
// initialization of such variables to function which was called with object creation.
void MyClass::myFunction(){
object->doSomething();
}
So when program started inicialization of those variables caused crash of program.
Note: It seems like it was problem with objects, cause variables like Integers or such were just fine.
Well, I'm not totally sure why this is allowed in Debug mode, but crashes Release mode right after start, maybe someone could answer under this comment and explain this behavior, I'm just begginer and I'm doing lot of bad stuff, but I'm trying and that's good, right? :D
I hope i didn't waste too much of your time guys and maybe this post will be useful to someone in future.
I'm creating a DirectX 11 helper class that looks kind of like this:
#import "DXClass.h" // I have declared the constructor and the other methods here
// All of the DirectX libraries are imported in the header as well
DXClass::DXClass()
{
// Pointers created, etc.
}
DXClass:~DXClass()
{
// Other DirectX objects released
// With an if (bbSRView) {}, the exception still occurs, so bbSRView is not NULL
// bbSRView is a ID3D11ShaderResourceView*
// When the other violation does not occur, one does here:
bbSRView->Release();
bbSRView = NULL;
// More releases
void DXClass::Initialize()
{
SetupDisplay();
// Other initialization that works fine
}
void DXClass::SetupDisplay()
{
// This is where the debugger shows the access violation.
// factory is declared as DXGIFactory*
HRESULT hr = CreateDXGIFactory(__uuidof(IDXGIFactory), (void **)&factory);
// Loop through adapters and outputs, etc.
}
This class is initialized like this: dxClass = new DXClass();
The Initialize() function is called in another method of the class that created dxClass.
When the application is run, I get an access violation at the beginning of the setupDisplay() function. However, if I take the code in setupDisplay() and put it in Initialize(), removing the call to setupDisplay(), no access violation occurs. Also, if I remove the code from setupDisplay() so that it is an empty function, and then call it in Initialize(), no access violation occurs.
It appears that no pointers are NULL, and the application will start fine if it is changed as described above. However, on another note, the application receives another access violation when quitting. The debugger points to a Release() call on an ID3D11ShaderResourceView*, which I have pointed out in my code snippet. This pointer also appears to be valid.
I have also checked the similar questions, but the this pointer of the class appears to be valid, and I am not creating any buffers that could be overflowing. There also isn't anything that could be deleting/freeing the object early.
I have no idea what could be causing the errors. :/
Thanks :D
EDIT:
Here's an isolated test, with the same errors:
I have in my main function:
INT APIENTRY wWinMain(HINSTANCE, HINSTANCE, LPWSTR, INT)
{
App *app = new App();
app->Run();
app->Release();
}
In my App class, I have removed all window functionality and any other variables so that it looks like this:
App::App()
{
dxClass = new DXClass();
}
App::~App()
{
delete dxClass;
}
void App::Run()
{
dxClass->Initialize();
while (true) {} // Never reaches here
}
The access violation still occurs at the same place. Also, same results if I replace the factory instance variable with:
IDXGIFactory *f;
HRESULT hr = CreateDXGIFactory(__uuidof(IDXGIFactory), (void **)&f);
Which has worked for me in other applications.
An access violation when calling Release() usually means the object has already received it's final Release() from somewhere else (and it has destroyed itself). One possible solution would be to AddRef() when passing the pointer into your DXClass
I have a class that manages a SDL_Surface for me, and I want to make it so that I don't have to worry about freeing the surface. I have this code so far:
//In Entity.h
class Entity
{
public:
int x, y, width, height;
Entity(std::string);
~Entity();
void render();
private:
SDL_Surface* texture;
};
//In Entity.cpp
Entity::~Entity()
{
printf("Destroying an Entity");
SDL_FreeSurface(texture);
}
//In main.cpp
Entity puppy("puppy.bmp");
void render() {
clearScreen();
puppy.render();
}
int main (int argc, char **argv) {
printf("started");
init();
bool done = false;
while(!done)
{
...
// Draw the screen
printf("About to render");
render();
SDL_Flip(screen);
}
SDL_Quit();
return 0;
}
When I run it, I get Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_INVALID_ADDRESS at address: 0x0000000000000260
0x000000010002fd9b in SDL_DisplayFormat () Am I correct in assuming that the destructor is getting called early and it's trying to render a NULL texture? The code works fine if I don't have the destructor and just waste memory. I've also tried it with puppy being a local variable and passing it to render, but that doesn't help.
EDIT: The code runs for only a second, then crashes. It doesn't wait to exit the loop. Also, stepping through the program with gdb reveals the puppy to be drawn at least once for some reason. I've uploaded the full source here: http://dl.getdropbox.com/u/2223161/sdlgame.zip
This is a global variable:
Entity puppy("puppy.bmp");
it gets destroyed after your main() ends, so your SDL_FreeSurface gets called after SDL_Quit(). Is it valid? Check with documentation.
You're looking at the wrong end of the problem. Your code ran for me after I moved the initialization of puppy into main.
By placing puppy as a global object, its constructor runs prior to SDL_Init. The constructor calls load_image, which calls SDL_DisplayFormat. The documentation warns:
You have to call SDL_Init before using the SDL_DisplayFormat function. If you don't, your program will crash with an access violation.
http://sdl.beuc.net/sdl.wiki/SDL_DisplayFormat
Of course, you should also ensure the destructor is called prior to SDL_Quit(), as is proper, though that's not what's causing your access violation at the moment.
Program works fine (with random crashes) and Memory Validator reports Uninitialized read problem in pD3D = Direct3DCreate9.
What could be the problem ?
init3D.h
class CD3DWindow
{
public:
CD3DWindow();
~CD3DWindow();
LPDIRECT3D9 pD3D;
HRESULT PreInitD3D();
HWND hWnd;
bool killed;
VOID KillD3DWindow();
};
init3D.cpp
CD3DWindow::CD3DWindow()
{
pD3D=NULL;
}
CD3DWindow::~CD3DWindow()
{
if (!killed) KillD3DWindow();
}
HRESULT CD3DWindow::PreInitD3D()
{
pD3D = Direct3DCreate9( D3D_SDK_VERSION ); // Here it reports a problem
if( pD3D == NULL ) return E_FAIL;
// Other not related code
VOID CD3DWindow::KillD3DWindow()
{
if (killed) return;
diwrap::input.UnCreate();
if (hWnd) DestroyWindow(hWnd);
UnregisterClass( "D3D Window", wc.hInstance );
killed = true;
}
Inside main app .h
CD3DWindow *d3dWin;
Inside main app .cpp
d3dWin = new CD3DWindow;
d3dWin->PreInitD3D();
And here is the error report:
Error: UNINITIALIZED READ: reading register ebx
#0:00:02.969 in thread 4092
0x7c912a1f <ntdll.dll+0x12a1f> ntdll.dll!RtlUnicodeToMultiByteN
0x7e42d4c4 <USER32.dll+0x1d4c4> USER32.dll!WCSToMBEx
0x7e428b79 <USER32.dll+0x18b79> USER32.dll!EnumDisplayDevicesA
0x4fdfc8c7 <d3d9.dll+0x2c8c7> d3d9.dll!DebugSetLevel
0x4fdfa701 <d3d9.dll+0x2a701> d3d9.dll!D3DPERF_GetStatus
0x4fdfafad <d3d9.dll+0x2afad> d3d9.dll!Direct3DCreate9
0x00644c59 <Temp.exe+0x244c59> Temp.exe!CD3DWindow::PreInitD3D
c:\_work\Temp\initd3d.cpp:32
Edit: Your stack trace is very, very strange- inside the USER32.dll? That's part of Windows.
What I might suggest is that you're linking the multi-byte Direct3D against the Unicode D3D libraries, or something like that. You shouldn't be able to cause Windows functions to trigger an error.
Your Memory Validator application is reporting false positives to you. I would ignore this error and move on.
There is no copy constructor in your class CD3DWindow. This might not be the cause, but it is the very first thing that comes to mind.
If, by any chance, anywhere in your code a temporary copy is made of a CD3DWindow instance, the destructor of that copy will destroy the window handle. Afterwards, your original will try to use that same, now invalid, handle.
The same holds for the assignment operator.
This might even work, if the memory is not overwritten yet, for some time. Then suddenly, the memory is reused and your code crashes.
So start by adding this to your class:
private:
CD3DWindow(const CD3DWindow&); // left unimplemented intentionally
CD3DWindow& operator=(const CD3DWindow&); // left unimplemented intentionally
If the compiler complains, check the code it refers to.
Update: Of course, this problem might apply to all your other classes. Please read up on the "Rule of Three".
Is there any sense to step-execute release code? I noticed that some lines of code are omitted, i.e. some method calls. Also variable preview doesn't show some variables and shows invalid (not real) values for some others, so it's all quite misleading.
I'm asking this question, because loading WinDbg crashdump file into Visual Studio brings the same stack and variables partial view as step-execution. Are there any way to improve crashdump analyze experience, except recompiling application without optimalizations?
Windows, Visual Studio 2005, unmanaged C++
Yes - if you have the .pdb for the build, and the .dmp file from the crash, then you can open the debugger on the exact point of failure, and examine the state of your app at that point.
As several have noted - some variables will be optimized away, but if you're mildly creative / inquisitive, you'll find ways to obtain those values.
You can build in a root crash handler for your code to generate a .dmp file automatically which works on all Windows flavors (assuming you are creating a Windows app) using something like the following:
// capture the unhandled exception hook - we will create a mini dump for ourselves
// NOTE: according to docs, if a debugger is present, this API won't succeed (ie. debug builds ignore this)
MiniDumper::Install(
true,
filename,
"Please send a copy of this file, along with a brief description of the problem, to [insert your email address here] so that we might fix this issue."
);
The above would require the MiniDumper class I wrote, below:
#pragma once
#include <dbghelp.h>
#include "DynamicLinkLibrary.h"
#include "FileName.h"
//////////////////////////////////////////////////////////////////////////
// MiniDumper
//
// Provides a mechanism whereby an application will generate its own mini dump file anytime
// it throws an unhandled exception (or at the client's request - see GenerateMiniDump, below).
//
// Warning: the C-runtime will NOT invoke our unhandled handler if you are running a debugger
// due to the way that the SetUnhandledExceptionFilter() API works (q.v.)
//
// To use this facility, simply call MiniDumper::Install - for example, during CWinApp initialization.
//
// Once this has been installed, all current and future threads in this process will be covered.
// This is unlike the StructuredException and CRTInvalidParameter classes, which must be installed for
// for each thread for which you wish to use their services.
//
class MiniDumper
{
public:
// install the mini dumper (and optionally, hook the unhandled exception filter chain)
// #param filename is the mini dump filename to use (please include a path)
// #return success or failure
// NOTE: we can be called more than once to change our options (unhook unhandled, change the filename)
static bool Install(bool bHookUnhandledExceptionFilter, const CFilename & filenameMiniDump, const CString & strCustomizedMessage, DWORD dwMiniDumpType = MiniDumpNormal)
{
return GetSingleton().Initialize(bHookUnhandledExceptionFilter, filenameMiniDump, strCustomizedMessage, dwMiniDumpType);
}
// returns true if we've been initialized (but doesn't indicate if we have hooked the unhandled exception filter or not)
static bool IsInitialized() { return g_bInstalled; }
// returns true if we've been setup to intercept unhandled exceptions
static bool IsUnhandledExceptionHooked() { return g_bInstalled && GetSingleton().m_bHookedUnhandledExceptionFilter; }
// returns the filename we've been configured to write to if we're requested to generate a mini dump
static CFilename GetMiniDumpFilename() { return g_bInstalled ? GetSingleton().m_filenameMiniDump : ""; }
// you may use this wherever you have a valid EXCEPTION_POINTERS in order to generate a mini dump of whatever exception just occurred
// use the GetExceptionInformation() intrinsic to obtain the EXCEPTION_POINTERS in an __except(filter) context
// returns success or failure
// DO NOT hand the result of GenerateMiniDump to your __except(filter) - instead use a proper disposition value (q.v. __except)
// NOTE: you *must* have already installed MiniDumper or this will only error
static bool GenerateMiniDump(EXCEPTION_POINTERS * pExceptionPointers);
private:
// based on dbghelp.h
typedef BOOL (WINAPI * MINIDUMPWRITEDUMP_FUNC_PTR)(
HANDLE hProcess,
DWORD dwPid,
HANDLE hFile,
MINIDUMP_TYPE DumpType,
CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam
);
// data we need to pass to our mini dump thread
struct ExceptionThreadData
{
ExceptionThreadData(EXCEPTION_POINTERS * exceptionPointers, bool bUnhandled, DWORD threadID = ::GetCurrentThreadId())
: pExceptionPointers(exceptionPointers)
, dwThreadID(threadID)
, bUnhandledException(bUnhandled)
{
}
EXCEPTION_POINTERS * pExceptionPointers;
DWORD dwThreadID;
bool bUnhandledException;
};
// our unhandled exception filter (called automatically by the run time if we've been installed to do so)
static LONG CALLBACK UnhandledExceptionFilter(EXCEPTION_POINTERS * pExceptionPointers);
// creates a new thread in which to generate our mini dump (so we don't run out of stack)
static bool ExecuteMiniDumpThread(EXCEPTION_POINTERS * pExceptionPointers, bool bUnhandledException);
// thread entry point for generating a mini dump file
static DWORD WINAPI MiniDumpThreadProc(LPVOID lpParam);
// obtains the one and only instance
static MiniDumper & GetSingleton();
// flag to indicate if we're installed or not
static bool g_bInstalled;
// create us
MiniDumper()
: m_pPreviousFilter(NULL)
, m_pWriteMiniDumpFunction(NULL)
, m_bHookedUnhandledExceptionFilter(false)
{
}
// install our unhandled exception filter
bool Initialize(bool bHookUnhandledExceptionFilter, const CFilename & filenameMiniDump, const CString & strCustomizedMessage, DWORD dwMiniDumpType);
// generates a mini dump file
bool GenerateMiniDumpFile(ExceptionThreadData * pData);
// handle an unhandled exception
bool HandleUnhandledException(ExceptionThreadData * pData);
bool m_bHookedUnhandledExceptionFilter;
CFilename m_filenameMiniDump;
CString m_strCustomizedMessage;
DWORD m_dwMiniDumpType;
MINIDUMPWRITEDUMP_FUNC_PTR m_pWriteMiniDumpFunction;
LPTOP_LEVEL_EXCEPTION_FILTER m_pPreviousFilter;
};
And its implementation:
#include "StdAfx.h"
#include "MiniDumper.h"
using namespace Toolbox;
//////////////////////////////////////////////////////////////////////////
// Static Members
bool MiniDumper::g_bInstalled = false;
// returns true if we were able to create a mini dump for this exception
bool MiniDumper::GenerateMiniDump(EXCEPTION_POINTERS * pExceptionPointers)
{
// obtain the mini dump in a new thread context (which will have its own stack)
return ExecuteMiniDumpThread(pExceptionPointers, false);
}
// this is called from the run time if we were installed to hook the unhandled exception filter
LONG CALLBACK MiniDumper::UnhandledExceptionFilter(EXCEPTION_POINTERS * pExceptionPointers)
{
// attempt to generate the mini dump (use a separate thread to ensure this one is frozen & we have a fresh stack to work with)
ExecuteMiniDumpThread(pExceptionPointers, true);
// terminate this process, now
::TerminateProcess(GetCurrentProcess(), 0xFFFFFFFF);
// carry on as normal (we should never get here due to TerminateProcess, above)
return EXCEPTION_CONTINUE_SEARCH;
}
bool MiniDumper::ExecuteMiniDumpThread(EXCEPTION_POINTERS * pExceptionPointers, bool bUnhandledException)
{
// because this may have been created by a stack overflow
// we may be very very low on stack space
// so we'll create a new, temporary stack to work with until we fix this situation
ExceptionThreadData data(pExceptionPointers, bUnhandledException);
DWORD dwScratch;
HANDLE hMiniDumpThread = ::CreateThread(NULL, 0, MiniDumpThreadProc, &data, 0, &dwScratch);
if (hMiniDumpThread)
{
VERIFY(::WaitForSingleObject(hMiniDumpThread, INFINITE) == WAIT_OBJECT_0);
VERIFY(::GetExitCodeThread(hMiniDumpThread, &dwScratch));
VERIFY(::CloseHandle(hMiniDumpThread));
return AsBool(dwScratch);
}
return false;
}
DWORD WINAPI MiniDumper::MiniDumpThreadProc(LPVOID lpParam)
{
// retrieve our exception context from our creator
ExceptionThreadData * pData = (ExceptionThreadData *)lpParam;
// generate the actual mini dump file in this thread context - with our own stack
if (pData->bUnhandledException)
return GetSingleton().HandleUnhandledException(pData);
else
return GetSingleton().GenerateMiniDumpFile(pData);
}
bool MiniDumper::HandleUnhandledException(ExceptionThreadData * pData)
{
// generate the actual mini dump file first - hopefully we get this even if the following errors
const bool bMiniDumpSucceeded = GenerateMiniDumpFile(pData);
// try to inform the user of what's happened
CString strMessage = FString("An Unhandled Exception occurred in %s\n\nUnfortunately, this requires that the application be terminated.", CFilename::GetModuleFilename());
// create the mini dump file
if (bMiniDumpSucceeded)
{
// let user know about the mini dump
strMessage.AppendFormat("\n\nOn a higher note, we have saved some diagnostic information in %s", m_filenameMiniDump.c_str());
}
// append any custom message(s)
if (!IsEmpty(m_strCustomizedMessage))
strMessage.AppendFormat("\n\n%s", m_strCustomizedMessage);
// cap it off with an apology
strMessage.Append("\n\nThis application must be terminated now. All unsaved data will be lost. We are deeply sorry for the inconvenience.");
// let the user know that things have gone terribly wrong
::MessageBox(GetAppWindow(), strMessage, "Internal Error - Unhandled Exception", MB_ICONERROR);
// indicate success or not
return bMiniDumpSucceeded;
}
//////////////////////////////////////////////////////////////////////////
// Instance Members
MiniDumper & MiniDumper::GetSingleton()
{
static std::auto_ptr<MiniDumper> g_pSingleton(new MiniDumper);
return *g_pSingleton.get();
}
bool MiniDumper::Initialize(bool bHookUnhandledExceptionFilter, const CFilename & filenameMiniDump, const CString & strCustomizedMessage, DWORD dwMiniDumpType)
{
// check if we need to link to the the mini dump function
if (!m_pWriteMiniDumpFunction)
{
try
{
// attempt to load the debug helper DLL
DynamicLinkLibrary dll("DBGHelp.dll", true);
// get the function address we need
m_pWriteMiniDumpFunction = (MINIDUMPWRITEDUMP_FUNC_PTR)dll.GetProcAddress("MiniDumpWriteDump", false);
}
catch (CCustomException &)
{
// we failed to load the dll, or the function didn't exist
// either way, m_pWriteMiniDumpFunction will be NULL
ASSERT(m_pWriteMiniDumpFunction == NULL);
// there is nothing functional about the mini dumper if we have no mini dump function pointer
return false;
}
}
// record the filename to write our mini dumps to (NOTE: we don't do error checking on the filename provided!)
if (!IsEmpty(filenameMiniDump))
m_filenameMiniDump = filenameMiniDump;
// record the custom message to tell the user on an unhandled exception
m_strCustomizedMessage = strCustomizedMessage;
// check if they're updating the unhandled filter chain
if (bHookUnhandledExceptionFilter && !m_bHookedUnhandledExceptionFilter)
{
// we need to hook the unhandled exception filter chain
m_pPreviousFilter = ::SetUnhandledExceptionFilter(&MiniDumper::UnhandledExceptionFilter);
}
else if (!bHookUnhandledExceptionFilter && m_bHookedUnhandledExceptionFilter)
{
// we need to un-hook the unhandled exception filter chain
VERIFY(&MiniDumper::UnhandledExceptionFilter == ::SetUnhandledExceptionFilter(m_pPreviousFilter));
}
// set type of mini dump to generate
m_dwMiniDumpType = dwMiniDumpType;
// record that we've been installed
g_bInstalled = true;
// if we got here, we must have been successful
return true;
}
bool MiniDumper::GenerateMiniDumpFile(ExceptionThreadData * pData)
{
// NOTE: we don't check this before now because this allows us to generate an exception in a different thread context (rather than an exception while processing an exception in the main thread)
ASSERT(g_bInstalled);
if (!g_bInstalled)
return false;
HANDLE hFile = ::CreateFile(m_filenameMiniDump.c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
// indicate failure
return false;
}
else
{
// NOTE: don't use exception_info - its a #define!!!
Initialized<_MINIDUMP_EXCEPTION_INFORMATION> ex_info;
ex_info.ThreadId = pData->dwThreadID;
ex_info.ExceptionPointers = pData->pExceptionPointers;
// generate our mini dump
bool bStatus = FALSE != ((*m_pWriteMiniDumpFunction)(GetCurrentProcess(), GetCurrentProcessId(), hFile, (MINIDUMP_TYPE)m_dwMiniDumpType, &ex_info, NULL, NULL));
// close the mini dump file
::CloseHandle(hFile);
return bStatus;
}
}
I apologize for the fact that this is not a drop-in solution. There are dependencies on other parts of my Toolbox library. But I think it would go a long way towards giving you the right idea as to how to build-in "capture a crash mini-dump" automatically from your code, which you can then combine with your .dsp files that you can make a normal part of your development cycle - so that when a .dmp comes in - you can fire up the debugger on it with your saved .pdb from your release build (which you don't distribute!) and you can debug the crash conditions quite easily.
The above code is an amalgam of many different sources - code snippets from debugging books, from MSDN documentation, etc., etc. If I have left out attribution I mean no harm. However, I do no believe that any of the above code is significantly created by anyone but myself.
Recompile just the file of interest without optimisations :)
In general:
Switch to interleaved disassembly mode. Single-stepping through the disassembly will enable you to step into function calls that would otherwise be skipped over, and make inlined code more evident.
Look for alternative ways of getting at values in variables the debugger is not able to directly show you. If they were passed in as arguments, look up the callstack - you will often find they are visible in the caller. If they were retrieved via getters from some object, examine that object; glance over the assembly generated by the code that calculates them to work out where they were stored; etc. If all else fails and disabling optimisations / adding a printf() distorts timings sufficiently to affect debugging, add a dummy global variable and set it to the value of interest on entry to the section of interest.
At least is not a IA64 dump...
There really isn't much you can do beyond having full dump and private symbols. Modern compilers have a field day with your code and is barely recognisable, specially if you add something like LTCG.
There are two things I found usefull:
Walk up the stack until you get a good anchor on what 'this' really points to. Most times when you are in an object method frame 'this' is unreliable because of registry optmizations. Usually several calls up the stack you get an object that has the correct address and you can navigate, member reference by member reference, until your crash point and have a correct value for 'this'
uf (Windbg's unassembly function command). This little helper can list a function dissasembly in a more manageable form than the normal dissasembly view. Because it follows jumps and code re-arranges, is easier to follow the logic of uf output.
The most important thing is to have the symbol files (*.pdb). You can generate them for release builds, by default they are not active.
Then you have to know that because of optimizations, code might get re-ordered, so debugging could look a bit jerky. Also some intermediate variables might have got optimized away. Generally speaking the behaviour and visibility of data might have some restrictions.
With Visual Studio C++ 2008 you can automatically debug the *.dmp files. I believe it also works for VS 2005. For older compilers I am afraid you´ll have to use WinDbg... (Also specify of course the *.pdb files for WinDbg, otherwise the info will be quite limited)