Jump to content
  • Advertisement

Recommended Posts

Hello,

I am at a loss. Went through many resources, but still can't figure it out:

I have a Windows, c++ MSVC 2015 application. It sometimes randomly crashes, but never in debug mode. So I want to set-up a simple stack trace during crash, so that I can better guess where the problem happened. Is it really that complicated, or am I wrong? I am talking about crashes caused by null pointer access or similar. Not exceptions.

Anyone with a simple code snippet I could use?

Thanks!

Share this post


Link to post
Share on other sites
Advertisement

You need to utilize 2 (and more) WINAPI functions to handle this. SetUnhandledExceptionFilter is called when your program crashes due to an uncaught exception. You can then execute or even "rescue" the thread that was cusing the crash and tell the kernel to continue and try executing again from the crashing point.

The other function to get a stack-trace I use in my profiler code is CaptureStackBackTrace. This function copies the pointer addresses of called functions from your current execution stack

Share this post


Link to post
Share on other sites

Thank you very much Shaarigan. I got it to work more or less correctly, using the two functions you mentioned. This basically gives me following code:

LONG WINAPI exceptionHandler(PEXCEPTION_POINTERS pExceptionInfo)
{
    unsigned int   i;
    void         * stack[100];
    unsigned short frames;
    SYMBOL_INFO  * symbol;
    HANDLE         process;

    process = GetCurrentProcess();

    SymInitialize(process, NULL, TRUE);

    frames = CaptureStackBackTrace(0, 100, stack, NULL);
    symbol = (SYMBOL_INFO *)calloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char), 1);
    symbol->MaxNameLen = 255;
    symbol->SizeOfStruct = sizeof(SYMBOL_INFO);

    for (i = 0; i < frames; i++)
    {
        SymFromAddr(process, (DWORD64)(stack[i]), 0, symbol);

        printf("%i: %s - 0x%0X\n", frames - i - 1, symbol->Name, symbol->Address);
    }

    free(symbol);
    return EXCEPTION_EXECUTE_HANDLER;
}

I initialize the exception handler with:

SetUnhandledExceptionFilter(exceptionHandler);

When an exception occurs, it prints the stack backtrace, but some functions appear in that backtrace, that have never been called. How can that be? for instance:

Quote

10: UnhandledExceptionFilter - 0xE48BF10
9: memset - 0x11D80A80
8: _C_specific_handler - 0x11D6AA70
7: _chkstk - 0x11D7EBD0
6: RtlWalkFrameChain - 0x11CE5890
5: KiUserExceptionDispatcher - 0x11D7DBF0
4: apiFunctionX - 0xBA9763A0
3: apiFunctionX - 0xBA9763A0
2: apiFunctionX - 0xBA9763A0
1: BaseThreadInitThunk - 0x11A23020
0: RtlUserThreadStart - 0x11D51410

In above stack backtrace, it indicates 3 times function apiFunctionX. This cannot be for 2 reasons:

  • That function was never called
  • That function is not recursive

Does anyone know what is going on?

Thanks

Share this post


Link to post
Share on other sites
Posted (edited)

I suggest examining the raw stack memory for that thread, as well as the disassembly for the function that appears three times unexpectedly.  Optimized code can do some weird stuff, or the stack might be corrupt somehow causing the stack trace to be slightly wrong (stack traces rely on the stack memory - if it's corrupt, the trace may not be able to walk the function pointers properly).

Even if you can't reliably debug optimized code like usual, you can always attach a debugger and inspect the raw memory/disassembly.

Edited by Nypyren

Share this post


Link to post
Share on other sites
14 minutes ago, codingJoe said:

 

In above stack backtrace, it indicates 3 times function apiFunctionX. This cannot be for 2 reasons:

  • That function was never called
  • That function is not recursive

Does anyone know what is going on?

Thanks

Just a couple quick comments. On occasion it's sometimes helpful to go old school. i.e. put in printf or something similar.  Second I've seen rare situations where functions were running that were never called. This sometimes means your stack got corrupted somehow.  One thing to look for is local arrays (arrays on the stack) that you may be writing off the end of.

Share this post


Link to post
Share on other sites

Thanks to both of you. I tried the printf of course, and as expected, it doesn't get called in the incriminated apiFunctionX.

The location where I intentionally raise an exception is at the very beginning of the first API function that is being called, i.e. apiFunctionA (actually just after setting up the exception handler). No recursive functions were called before that:

simInt apiFunctionA()
{
    SetUnhandledExceptionFilter(exceptionHandler);
    RaiseException( EXCEPTION_ACCESS_VIOLATION, 0, 0, 0);
	...
}

I also tried to recompile the project with all optimizations turned off, this doesn't make a difference. My application is very large, and runs perfectly fine.

Trying to run the debugger (in release mode), via qtCreator and msvc2015, I get following disassembly at the point where the exception is raised (not in RaiseException, but one level before), in apiFunctionA (but the debugger tells me it is the code for apiFunctionX):

0x7ffab6900f9a  <+0x3ab7a>         lea     rcx,[myApp!apiFunctionX+0x6dc0 (00007ffa`b68cd1e0)]
0x7ffab6900fa1  <+0x3ab81>         call    qword ptr [myApp!apiFunctionX+0x4b6ce0 (00007ffa`b6d7d100)]
0x7ffab6900fa7  <+0x3ab87>         xor     r9d,r9d
0x7ffab6900faa  <+0x3ab8a>         xor     r8d,r8d
0x7ffab6900fad  <+0x3ab8d>         xor     edx,edx
0x7ffab6900faf  <+0x3ab8f>         mov     ecx,0C0000005h
0x7ffab6900fb4  <+0x3ab94>         call    qword ptr [myApp!apiFunctionX+0x4b6cd8 (00007ffa`b6d7d0f8)]
point where debugger paused --> 0x7ffab6900fba  <+0x3ab9a>         mov     edx,0Eh
0x7ffab6900fbf  <+0x3ab9f>         lea     rcx,[rbp-61h]
0x7ffab6900fc3  <+0x3aba3>         call    myApp!apiFunctionX+0x1911d0 (00007ffa`b6a575f0)
0x7ffab6900fc8  <+0x3aba8>         nop
0x7ffab6900fc9  <+0x3aba9>         call    myApp!apiFunctionX+0x7490 (00007ffa`b68cd8b0)
0x7ffab6900fce  <+0x3abae>         mov     r8d,2
0x7ffab6900fd4  <+0x3abb4>         lea     rdx,[myApp!apiFunctionX+0x4d8a58 (00007ffa`b6d9ee78)]
0x7ffab6900fdb  <+0x3abbb>         lea     rcx,[rbp-31h]
0x7ffab6900fdf  <+0x3abbf>         call    myApp!apiFunctionX+0x18fa90 (00007ffa`b6a55eb0)

So I notice all those myApp!apiFunctionX in above disassembly... which appears completely wrong.

I am at a loss..
 

 

Share this post


Link to post
Share on other sites

So just to elaborate on what I said earlier, the exact scenario where this can happen is as follows:

You call a function somewhere with a local array which you write off the end of.  When the function exits the return address is on the stack but you have destroyed it.  In most cases this will cause your program to crash, however if the data you write over the return address with, happens to also be a valid address in your code, your program will go to that point and continue running.  Depending on the exact case it can actually call routines from there and might go for a while before you get a crash.

The best why to find this is somehow step through you code either with the debugger or by putting in a lot of write statements. Sometimes it's best to write to a file but make sure you try to flush the output buffers after every write so you get the last location before the crash.

Again you might also want just look for obvious places where this might happen. If you know there  are local arrays somewhere, check those places first.

I'm not sure if this is what's happening to you however it is one way your code can go off into some crazy location you didn't expect it to go.

Share this post


Link to post
Share on other sites
Posted (edited)

"myApp!apiFunctionX+0x4b6ce0"

I've never seen a function that large.  This likely means that there aren't symbols for that range of your EXE/DLL, so it's getting the nearest function it has symbols for instead.  My best guess is that you're statically linking something that doesn't have any symbols, or possibly using runtime code generation of some kind.

Edited by Nypyren

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!