MSDN.WhiteKnight - Stack Overflow answers
Ответ на "How can you use CaptureStackBackTrace to capture the exception stack, not the calling stack?"
Answer 50208684
On Windows, unhandled C++ exception automatically generates SEH exception. SEH __except block allows to attach a filter that accepts _EXCEPTION_POINTERS structure as a parameter, which contains the pointer to the processor's context record in the moment exception was thrown. Passing this pointer to StackWalk64 function gives the stack trace in the moment of exception. So, this problem can be solved by using SEH-style exception handling instead of C++ style.
Example code:
#include <stdlib.h> #include <locale.h> #include <stdio.h> #include <tchar.h> #include <process.h> #include <iostream> #include <Windows.h> #include "dbghelp.h" using namespace std; const int MaxNameLen = 256; #pragma comment(lib,"Dbghelp.lib") void printStack( CONTEXT* ctx ) //Prints stack trace based on context record { BOOL result; HANDLE process; HANDLE thread; HMODULE hModule; STACKFRAME64 stack; ULONG frame; DWORD64 displacement; DWORD disp; IMAGEHLP_LINE64 *line; char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)]; char name[MaxNameLen]; char module[MaxNameLen]; PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer; // On x64, StackWalk64 modifies the context record, that could // cause crashes, so we create a copy to prevent it CONTEXT ctxCopy; memcpy(&ctxCopy, ctx, sizeof(CONTEXT)); memset( &stack, 0, sizeof( STACKFRAME64 ) ); process = GetCurrentProcess(); thread = GetCurrentThread(); displacement = 0; #if !defined(_M_AMD64) stack.AddrPC.Offset = (*ctx).Eip; stack.AddrPC.Mode = AddrModeFlat; stack.AddrStack.Offset = (*ctx).Esp; stack.AddrStack.Mode = AddrModeFlat; stack.AddrFrame.Offset = (*ctx).Ebp; stack.AddrFrame.Mode = AddrModeFlat; #endif SymInitialize( process, NULL, TRUE ); //load symbols for( frame = 0; ; frame++ ) { //get next call from stack result = StackWalk64 ( #if defined(_M_AMD64) IMAGE_FILE_MACHINE_AMD64 #else IMAGE_FILE_MACHINE_I386 #endif , process, thread, &stack, &ctxCopy, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL ); if( !result ) break; //get symbol name for address pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO); pSymbol->MaxNameLen = MAX_SYM_NAME; SymFromAddr(process, ( ULONG64 )stack.AddrPC.Offset, &displacement, pSymbol); line = (IMAGEHLP_LINE64 *)malloc(sizeof(IMAGEHLP_LINE64)); line->SizeOfStruct = sizeof(IMAGEHLP_LINE64); //try to get line if (SymGetLineFromAddr64(process, stack.AddrPC.Offset, &disp, line)) { printf("\tat %s in %s: line: %lu: address: 0x%0X\n", pSymbol->Name, line->FileName, line->LineNumber, pSymbol->Address); } else { //failed to get line printf("\tat %s, address 0x%0X.\n", pSymbol->Name, pSymbol->Address); hModule = NULL; lstrcpyA(module,""); GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCTSTR)(stack.AddrPC.Offset), &hModule); //at least print module name if(hModule != NULL)GetModuleFileNameA(hModule,module,MaxNameLen); printf ("in %s\n",module); } free(line); line = NULL; } } //****************************************************************************** void function2() { int a = 0; int b = 0; throw exception(); } void function1() { int a = 0; function2(); } void function0() { function1(); } int seh_filter(_EXCEPTION_POINTERS* ex) { printf("*** Exception 0x%x occured ***\n\n",ex->ExceptionRecord->ExceptionCode); printStack(ex->ContextRecord); return EXCEPTION_EXECUTE_HANDLER; } static void threadFunction(void *param) { __try { function0(); } __except(seh_filter(GetExceptionInformation())) { printf("Exception \n"); } } int _tmain(int argc, _TCHAR* argv[]) { _beginthread(threadFunction, 0, NULL); printf("Press any key to exit.\n"); cin.get(); return 0; }
Example output (first two entries are noise, but the rest correctly reflects functions that caused exception):
*** Exception 0xe06d7363 occured *** at RaiseException, address 0xFD3F9E20. in C:\Windows\system32\KERNELBASE.dll at CxxThrowException, address 0xDBB5A520. in C:\Windows\system32\MSVCR110D.dll at function2 in c:\work\projects\test\test.cpp: line: 146: address: 0x3F9C6C00 at function1 in c:\work\projects\test\test.cpp: line: 153: address: 0x3F9C6CB0 at function0 in c:\work\projects\test\test.cpp: line: 158: address: 0x3F9C6CE0 at threadFunction in c:\work\projects\test\test.cpp: line: 174: address: 0x3F9C6D70 at beginthread, address 0xDBA66C60. in C:\Windows\system32\MSVCR110D.dll at endthread, address 0xDBA66E90. in C:\Windows\system32\MSVCR110D.dll at BaseThreadInitThunk, address 0x773C6520. in C:\Windows\system32\kernel32.dll at RtlUserThreadStart, address 0x775FC520. in C:\Windows\SYSTEM32\ntdll.dll
Another option is to create custom exception class that captures context in constructor and use it (or derived classes) to throw exceptions:
class MyException{ public: CONTEXT Context; MyException(){ RtlCaptureContext(&Context); } }; void function2() { throw MyException(); } //... try { function0(); } catch (MyException& e) { printf("Exception \n"); printStack(&e.Context); }
Content is retrieved from StackExchange API.
Auto-generated by ruso-archive tools.