asmaloney / asmCrashReport

🐞 Installs signal handlers to capture stack traces for MinGW 32 and macOS builds.
MIT License
47 stars 28 forks source link

MinGW 64 no output #19

Open Duolabs opened 4 months ago

Duolabs commented 4 months ago

Hello and thank you for contributing this code, which seems to be the most promising for bug reporting among all the ones I have searched for. In Windows 64-bit and MinGW64 (Obviously), I am not able to retrieve the function names using addr2line.exe.

I always get this kind of result:

[0] 0x00007ff6b8d385d9 ?? ??:0
[1] 0x00007ff6b8d38301 ?? ??:0
[2] 0x00007ff6b8d382ca ?? ??:0
[3] 0x00007ff6b8d384d5 ?? ??:0
[4] 0x00007ff6b8d34073 ?? ??:0
[5] 0x00007ff6b8d35692 ?? ??:0
[6] 0x00007ff6b8d31395 ?? ??:0
[7] 0x00007ff6b8d314c6 ?? ??:0
[8] 0x00007ffaa0bb257d ?? ??:0
[9] 0x00007ffaa226aa58 ?? ??:0

I am using the cygwin64 version, I have tried the version distributed on QT with MinGW and in no case does addr2line.exe return a value to me. Question: Are memory values like 0x00007ffaa226aa58 (very high) correct? Another question, why did you import it to work only in Release Mode and not in Debug mode? I've been trying everything for 4 days but I can't see the light at the end of the tunnel.

Any help is absolutely welcome.

asmaloney commented 4 months ago

Riccardo:

I have never tried to use this with 64-bit code on Windows. As mentioned in the README and the post on it, it's designed for 32-bit MinGW, though this pull request was supposed to add support for 64-bit!

Though now that I'm looking at asmCrashReport.pri, I think it would need to modified for 64-bit as well? I see win32-g++** but nothing for 64-bit. Maybe this is why it isn't giving you symbols?

If modifying that doesn't work, you might try to look at the sources I used when putting this together (listed in my post) to see if anything else is required for 64-bit support.

why did you import it to work only in Release Mode and not in Debug mode?

Because I was using this in a shipping product. A debug build would be huge and include all kinds of slow debug code. The way it is set up now, it creates a release build with debug symbols.

If you want to use it in a debug build, at minimum you will need to modify asmCrashReport.pri to set the right switches.

Duolabs commented 4 months ago

Hi!

Thank you for your quick answer. I am trying to implement it on 64Bits and keep you posted on that. One useful link I am following is: https://theorangeduck.com/page/printing-stack-trace-mingw which you mentioned in your code too.

I noticed the

win32-g++

I am trying to work on that too.

I'll get back to you as soon as I can figure it out.

Duolabs commented 4 months ago

I was able to get it working with MinGW64 as well by adding a little trick. What is actually missing in Windows, to simplify life, is the .pdb file that is not generated by MinGW. So... you need to generate it and make it available for the StackWalk64 function.

Following the link above, I downloaded the cv2pdb software which allows me to generate the pdb file for my application in QT with MinGW64 and everything worked wonderfully.

I rewrote a small APP from scratch just to verify the behavior and it works perfectly.

FYI I added the following code:

windows {
       QMAKE_CFLAGS_RELEASE -= -O2
       QMAKE_CXXFLAGS_RELEASE -= -O2

       QMAKE_CFLAGS_RELEASE += -g -O0
       QMAKE_CXXFLAGS_RELEASE += -g -O0
       #QMAKE_LFLAGS_RELEASE =

       LIBS += "-L$$PWD/Win/WinDebug" -lDbghelp
}

To make sure that the .o files are generated for Win32, Win64 and possibly ARM.

The final steps are:

  1. Compile the application
  2. Run cv2pdb which generates the pdb file (at this point we are in the same condition as MSVC).
  3. Call the stack parsing function:
void MainWindow::stack_trace()
{

    HANDLE process = GetCurrentProcess();
    HANDLE thread = GetCurrentThread();

    CONTEXT context;
    memset(&context, 0, sizeof(CONTEXT));
    context.ContextFlags = CONTEXT_FULL;
    RtlCaptureContext(&context);

    SymInitialize(process, NULL, TRUE);

    DWORD image;
    STACKFRAME64 stackframe;
    ZeroMemory(&stackframe, sizeof(STACKFRAME64));

#ifdef _M_IX86
    image = IMAGE_FILE_MACHINE_I386;
    stackframe.AddrPC.Offset = context.Eip;
    stackframe.AddrPC.Mode = AddrModeFlat;
    stackframe.AddrFrame.Offset = context.Ebp;
    stackframe.AddrFrame.Mode = AddrModeFlat;
    stackframe.AddrStack.Offset = context.Esp;
    stackframe.AddrStack.Mode = AddrModeFlat;
#elif _M_X64
    image = IMAGE_FILE_MACHINE_AMD64;
    stackframe.AddrPC.Offset = context.Rip;
    stackframe.AddrPC.Mode = AddrModeFlat;
    stackframe.AddrFrame.Offset = context.Rsp;
    stackframe.AddrFrame.Mode = AddrModeFlat;
    stackframe.AddrStack.Offset = context.Rsp;
    stackframe.AddrStack.Mode = AddrModeFlat;
#elif _M_IA64
    image = IMAGE_FILE_MACHINE_IA64;
    stackframe.AddrPC.Offset = context.StIIP;
    stackframe.AddrPC.Mode = AddrModeFlat;
    stackframe.AddrFrame.Offset = context.IntSp;
    stackframe.AddrFrame.Mode = AddrModeFlat;
    stackframe.AddrBStore.Offset = context.RsBSP;
    stackframe.AddrBStore.Mode = AddrModeFlat;
    stackframe.AddrStack.Offset = context.IntSp;
    stackframe.AddrStack.Mode = AddrModeFlat;
#endif

    for (size_t i = 0; i < 25; i++) {

        BOOL result = StackWalk64(
            image, process, thread,
            &stackframe, &context, NULL,
            SymFunctionTableAccess64, SymGetModuleBase64, NULL);

        if (!result) { break; }

        char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
        PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer;
        symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
        symbol->MaxNameLen = MAX_SYM_NAME;

        char traceoutput [1024];
        memset (traceoutput, 0, sizeof(traceoutput));

        DWORD64 displacement = 0;
        if (SymFromAddr(process, stackframe.AddrPC.Offset, &displacement, symbol)) {

            //printf("[%i] %s\n", i, symbol->Name);
            sprintf(traceoutput, "[%i] %s\n", i, symbol->Name);
            ui->DebugMemo->append(traceoutput);
        } else {
            //printf("[%i] ???\n", i);
            sprintf(traceoutput, "[%i] ???\n", i);
            ui->DebugMemo->append(traceoutput);
        }

    }

    SymCleanup(process);

}

Small downside: every time the software is released, the .pdb file needs to be regenerated if any changes have been made.

I believe this can be automated by having QT do it from the command line. ANY SUGGESTIONS FOR THIS?

PS: Function's name comes out in the clear 😀