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 :
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
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
Libraries -lbfd, -lintl and -liberty were also used for linking along with -ldbghelp
Structured exception handling was used from the link http://www.programmingunlimited.net/siteexec/content.cgi?page=mingw-seh
Code View
"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; }
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); }
After this is my code that throws exception
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)
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