5

The post "How to generate a stacktrace when my gcc C++ app crashes" explains how stack trace can be generated when a app crashes. But the gcc version does not work on windows MinGW. In fact it does not even compile due to a missing header execinfo.h.

Is there something already available for MinGW on windows xp and above?

EDIT

Stackwalking was possible with the below mentioned steps. But I do not get the desired result when I try to walk the stack in the catch block after an exception. I am only able to get the name of the function throwing the exception and after that it shows list-->main-->and so on

Steps :

  1. The first answer provided to the post Win32 API stack walk with MinGW/MSYS? by José Luis Cebrián led to a link where Mr.Edd's stack trace library could be found http://www.mr-edd.co.uk/code/stack_trace

  2. The stackwalker available in the above link has dependency on dbghelp.dll. Since MinGW does not provide an import library for this it was generated using dlltool of mingw. The command for which is

    dlltool -k -d dbghelp.def -l dbghelp.a

    Note1: .def file was found in Wine project

    Note2: Library generation does not work with all versions of MinGW. I had problems generating it using version 4.4.1 but it worked fine with 4.6.1

  3. Libraries -lbfd, -lintl and -liberty were also used for linking along with -ldbghelp

  4. Structured exception handling was used from the link http://www.programmingunlimited.net/siteexec/content.cgi?page=mingw-seh

Code View

  1. "Try" implementation is as below. This part registers the exception handler

    { 
    __SEH_EXCEPTION_REGISTRATION _lseh_er;
    __SEH_HANDLER _lseh_handler;
    _lseh_er.handler =
    reinterpret_cast<PEXCEPTION_HANDLER>(__SEH_HANDLER::ExceptionRouter);
    _lseh_er.exthandler = &_lseh_handler;
    asm volatile ("movl %%fs:0, %0" : "=r" (_lseh_er.prev));
    asm volatile ("movl %0, %%fs:0" : : "r" (&_lseh_er));
    int _lseh_setjmp_res = setjmp(_lseh_handler.context);
    while(true) {
     if(_lseh_setjmp_res != 0) {
       break;
     }
    
  2. The ExceptionRounter function calls another function ExceptionHandler where the context and record are copied. The implementation is as below.

    EXCEPTION_DISPOSITION __SEH_HANDLER::ExceptionHandler(PEXCEPTION_RECORD pRecord, 
    __SEH_EXCEPTION_REGISTRATION* pReg,
    PCONTEXT pContext,
    PEXCEPTION_RECORD pRecord2)
    {
    
    CopyMemory(&excContext, pContext, sizeof(_CONTEXT));
    CopyMemory(&excRecord, pRecord, sizeof(_EXCEPTION_RECORD));
    
    // Jump back to the function where the exception actually occurred.  The 1 is the
    // return code that will be returned by set_jmp.
    longjmp(context, 1);
    }
    
  3. After this is my code that throws exception

  4. Followed by implementation of "catch" or seh_excep.

    break;
    }
    PEXCEPTION_RECORD rec = &_lseh_handler.excRecord;
    PCONTEXT ctx = &_lseh_handler.excContext;
    
    asm volatile ("movl %0, %%fs:0" : : "r" (_lseh_er.prev));
    if(_lseh_setjmp_res != 0)
    
  5. Then comes the code to walk the stack from Mr.Edd's stackwalker.

    lock lk(g_fill_frames_mtx);
    
    symbol_context sc;
    bfd_context bfdc;
    
    
    STACKFRAME frame = empty_pod;
    CONTEXT context = empty_pod;
    context.ContextFlags = CONTEXT_FULL;
    
    /*Below part is commented as the context of the exception causing code has to be  used and not the current context*/
    /*
    windows_dll kernel32("kernel32.dll");
    void (WINAPI *RtlCaptureContext_)(CONTEXT*) =  kernel32.function("RtlCaptureContext");
    RtlCaptureContext_(&context);
    */
    
    context = _lseh_handler.excContex;
    
    frame.AddrPC.Offset = context.Eip;
    frame.AddrPC.Mode = AddrModeFlat;
    frame.AddrStack.Offset = context.Esp;
    frame.AddrStack.Mode = AddrModeFlat;
    frame.AddrFrame.Offset = context.Ebp;
    frame.AddrFrame.Mode = AddrModeFlat;
    
    
    HANDLE process = GetCurrentProcess();
    HANDLE thread = GetCurrentThread();
    
    bool skip = true;
    bool has_limit = limit != 0;
    char symbol_buffer[sizeof(IMAGEHLP_SYMBOL) + 255];
    char module_name_raw[MAX_PATH];
    
    const DWORD machine = IMAGE_FILE_MACHINE_I386;
    
    while(StackWalk(machine, process, thread, &frame, &context, 0, SymFunctionTableAccess, SymGetModuleBase, 0))
    {
        if (skip)
        {
            skip = false;
            continue;
        }
    
        if (has_limit && limit-- == 0) break;
    
        IMAGEHLP_SYMBOL *symbol = reinterpret_cast<IMAGEHLP_SYMBOL *>(symbol_buffer);
        symbol->SizeOfStruct = (sizeof *symbol) + 255;
        symbol->MaxNameLength = 254;
        DWORD module_base = SymGetModuleBase(process, frame.AddrPC.Offset);
        std::string module_name = unknown_module;
        if (module_base && GetModuleFileNameA(reinterpret_cast<HINSTANCE>(module_base), module_name_raw, MAX_PATH))
            module_name = module_name_raw;
            std::string func = bfdc.get_function_name(frame.AddrPC.Offset);
    
            if (func.empty())
            {
                DWORD dummy = 0;
                BOOL got_symbol = SymGetSymFromAddr(process, frame.AddrPC.Offset, &dummy, symbol);
                func = got_symbol ? symbol->Name : unknown_function;
            }
    
        dbg::stack_frame f(reinterpret_cast<const void *>(frame.AddrPC.Offset), func, module_name);
        frames.push_back(f);
        }
      }
    }
    std::copy(frames.begin(), frames.end(), std::ostream_iterator<dbg::stack_frame>(std::cout, "\n"));
    

Regards, Shreyas

Community
  • 1
  • 1
Shreyas S
  • 328
  • 3
  • 11
  • @HansPassant please see my latest edit. walking the stack with StackWalk was possible. But I am having problems trying to walk the stack when there is an exception though the context was copied on exception. – Shreyas S Feb 27 '13 at 17:18
  • @HansPassant , is there a way to copy the stack frames in the exception handler function so that it can be used in the catch clause? Do I have to copy something else other than the context and the record to make this possible? – Shreyas S Feb 27 '13 at 17:33
  • @HansPassant isn't http://www.programmingunlimited.net/siteexec/content.cgi?page=mingw-seh doing this for MinGW? This has been used in the code above. Thank you for your reponses so far. – Shreyas S Feb 28 '13 at 16:04

1 Answers1

0

LibSEH available in the link http://www.programmingunlimited.net/siteexec/content.cgi?page=libseh fixed the issue.

From the responses I had got I could conclude that in my implementation the exception handling code was called in the same context as the exception causing code. This lead to overwriting the now-irrelevant part of the stack.

However exception handling filter functions are executed in a different context and by putting the stack tracer in the filter function the desired results can be achieved.

I thus used libseh, which provides exception handling features similar to that found in MSVC and wrote the stack tracer in the filter function.

Shreyas S
  • 328
  • 3
  • 11
  • 1
    Should be possible to have a simple working code whith stackwalking? e.g., a main.c with the whole example? I'm getting crazy trying to do by myself, sorry.. I'm using GCC 4.6.2 (MingW) and libseh. – morde May 29 '13 at 15:16