Created
June 30, 2014 15:03
-
-
Save dicej/7c11c8f27b3a34ffc3ad to your computer and use it in GitHub Desktop.
attempt to use CaptureStackBackTrace for lightweight crash reporting
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/src/system/windows/signal.cpp b/src/system/windows/signal.cpp | |
index 23d57b9..0114474 100644 | |
--- a/src/system/windows/signal.cpp | |
+++ b/src/system/windows/signal.cpp | |
@@ -90,6 +90,24 @@ struct MINIDUMP_EXCEPTION_INFORMATION { | |
LPEXCEPTION_POINTERS exception; | |
BOOL exceptionInCurrentAddressSpace; | |
}; | |
+ | |
+struct SYMBOL_INFO { | |
+ ULONG SizeOfStruct; | |
+ ULONG TypeIndex; | |
+ ULONG64 Reserved[2]; | |
+ ULONG Index; | |
+ ULONG Size; | |
+ ULONG64 ModBase; | |
+ ULONG Flags; | |
+ ULONG64 Value; | |
+ ULONG64 Address; | |
+ ULONG Register; | |
+ ULONG Scope; | |
+ ULONG Tag; | |
+ ULONG NameLen; | |
+ ULONG MaxNameLen; | |
+ TCHAR Name[1]; | |
+}; | |
#pragma pack(pop) | |
struct MINIDUMP_USER_STREAM_INFORMATION; | |
@@ -111,6 +129,20 @@ typedef BOOL (*MiniDumpWriteDumpType)(HANDLE processHandle, | |
const MINIDUMP_CALLBACK_INFORMATION | |
* callback); | |
+typedef BOOL WINAPI (*SymInitializeType)(HANDLE hProcess, | |
+ PCTSTR UserSearchPath, | |
+ BOOL fInvadeProcess); | |
+ | |
+typedef BOOL WINAPI (*SymFromAddrType)(HANDLE hProcess, | |
+ DWORD64 Address, | |
+ PDWORD64 Displacement, | |
+ SYMBOL_INFO* Symbol); | |
+ | |
+extern "C" | |
+NTSYSAPI WORD NTAPI RtlCaptureStackBackTrace(DWORD FramesToSkip, | |
+ DWORD FramesToCapture, | |
+ PVOID* BackTrace, | |
+ PDWORD BackTraceHash); | |
#endif | |
void dump(LPEXCEPTION_POINTERS e, const char* directory) | |
@@ -155,6 +187,100 @@ void dump(LPEXCEPTION_POINTERS e, const char* directory) | |
} | |
} | |
+void logException(LPEXCEPTION_POINTERS e, const char* directory) | |
+{ | |
+ char name[MAX_PATH]; | |
+ _timeb tb; | |
+ FTIME(&tb); | |
+ vm::snprintf(name, | |
+ MAX_PATH, | |
+ "%s\\exceptions.txt", | |
+ directory); | |
+ | |
+ FILE* log =vm::fopen(name, "ab"); | |
+ if (log) { | |
+#ifdef ARCH_x86_32 | |
+ void* ip = reinterpret_cast<void*>(e->ContextRecord->Eip); | |
+ void* base = reinterpret_cast<void*>(e->ContextRecord->Ebp); | |
+ void* stack = reinterpret_cast<void*>(e->ContextRecord->Esp); | |
+ void* thread = reinterpret_cast<void*>(e->ContextRecord->Ebx); | |
+#elif defined ARCH_x86_64 | |
+ void* ip = reinterpret_cast<void*>(e->ContextRecord->Rip); | |
+ void* base = reinterpret_cast<void*>(e->ContextRecord->Rbp); | |
+ void* stack = reinterpret_cast<void*>(e->ContextRecord->Rsp); | |
+ void* thread = reinterpret_cast<void*>(e->ContextRecord->Rbx); | |
+#endif | |
+ | |
+ HMODULE module; | |
+ if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, | |
+ static_cast<LPCSTR>(ip), | |
+ &module)) | |
+ { | |
+ GetModuleFileName(module, name, MAX_PATH); | |
+ } else { | |
+ module = 0; | |
+ } | |
+ | |
+ fprintf(log, "timestamp %" LLD | |
+ " code %ld ip %p base %p stack %p thread %p module %s\n", | |
+ (static_cast<int64_t>(tb.time) * 1000) + static_cast | |
+ <int64_t>(tb.millitm), | |
+ e->ExceptionRecord->ExceptionCode, | |
+ ip, base, stack, thread, module ? name : "(unknown)"); | |
+ | |
+ HINSTANCE dbghelp = LoadLibrary("dbghelp.dll"); | |
+ | |
+ if (dbghelp) { | |
+ SymInitializeType SymInitialize = reinterpret_cast | |
+ <SymInitializeType>(GetProcAddress(dbghelp, "SymInitialize")); | |
+ | |
+ SymFromAddrType SymFromAddr = reinterpret_cast | |
+ <SymFromAddrType>(GetProcAddress(dbghelp, "SymFromAddr")); | |
+ | |
+ if (SymInitialize and SymFromAddr) { | |
+ HANDLE process = GetCurrentProcess(); | |
+ | |
+ SymInitialize(process, 0, TRUE); | |
+ | |
+ const unsigned maxTrace = 100; | |
+ void* stack[maxTrace]; | |
+ | |
+ unsigned frames = RtlCaptureStackBackTrace(0, maxTrace, stack, 0); | |
+ | |
+ const unsigned maxName = 256; | |
+ SYMBOL_INFO* symbol = static_cast<SYMBOL_INFO*> | |
+ (calloc(sizeof(SYMBOL_INFO) + maxName * sizeof(char), 1)); | |
+ | |
+ symbol->MaxNameLen = maxName - 1; | |
+ symbol->SizeOfStruct = sizeof(SYMBOL_INFO); | |
+ | |
+ for(unsigned i = 0; i < frames; ++i) { | |
+ if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, | |
+ static_cast<LPCSTR>(stack[i]), | |
+ &module)) | |
+ { | |
+ GetModuleFileName(module, name, MAX_PATH); | |
+ } else { | |
+ module = 0; | |
+ } | |
+ | |
+ SymFromAddr(process, reinterpret_cast<uintptr_t>(stack[i]), 0, | |
+ symbol); | |
+ | |
+ fprintf(log, " %s in %s - %p\n", symbol->Name, | |
+ module ? name : "(unknown)", reinterpret_cast<void*> | |
+ (symbol->Address)); | |
+ } | |
+ | |
+ free(symbol); | |
+ } | |
+ } | |
+ | |
+ fflush(log); | |
+ fclose(log); | |
+ } | |
+} | |
+ | |
LONG CALLBACK handleException(LPEXCEPTION_POINTERS e) | |
{ | |
SignalRegistrar::Handler* handler = 0; | |
@@ -180,22 +306,38 @@ LONG CALLBACK handleException(LPEXCEPTION_POINTERS e) | |
bool jump = handler->handleSignal(&ip, &base, &stack, &thread); | |
+ if (jump) { | |
#ifdef ARCH_x86_32 | |
- e->ContextRecord->Eip = reinterpret_cast<DWORD>(ip); | |
- e->ContextRecord->Ebp = reinterpret_cast<DWORD>(base); | |
- e->ContextRecord->Esp = reinterpret_cast<DWORD>(stack); | |
- e->ContextRecord->Ebx = reinterpret_cast<DWORD>(thread); | |
+ e->ContextRecord->Eip = reinterpret_cast<DWORD>(ip); | |
+ e->ContextRecord->Ebp = reinterpret_cast<DWORD>(base); | |
+ e->ContextRecord->Esp = reinterpret_cast<DWORD>(stack); | |
+ e->ContextRecord->Ebx = reinterpret_cast<DWORD>(thread); | |
#elif defined ARCH_x86_64 | |
- e->ContextRecord->Rip = reinterpret_cast<DWORD64>(ip); | |
- e->ContextRecord->Rbp = reinterpret_cast<DWORD64>(base); | |
- e->ContextRecord->Rsp = reinterpret_cast<DWORD64>(stack); | |
- e->ContextRecord->Rbx = reinterpret_cast<DWORD64>(thread); | |
+ e->ContextRecord->Rip = reinterpret_cast<DWORD64>(ip); | |
+ e->ContextRecord->Rbp = reinterpret_cast<DWORD64>(base); | |
+ e->ContextRecord->Rsp = reinterpret_cast<DWORD64>(stack); | |
+ e->ContextRecord->Rbx = reinterpret_cast<DWORD64>(thread); | |
#endif | |
- if (jump) { | |
return EXCEPTION_CONTINUE_EXECUTION; | |
} else if (SignalRegistrar::Data::instance->crashDumpDirectory) { | |
- dump(e, SignalRegistrar::Data::instance->crashDumpDirectory); | |
+ // We only generate a crash dump if exception occurred in code | |
+ // belonging to the current executable. If the exception | |
+ // occurred in a library, there may be a handler available to | |
+ // handle it, in which case it is premature to assume we're | |
+ // going to crash. Generating a full memory dump on each such | |
+ // event is time consuming and eats up disk space, so we'd | |
+ // prefer to avoid it unless we're really crashing. | |
+ HMODULE module; | |
+ if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, | |
+ static_cast<LPCSTR>(ip), | |
+ &module) | |
+ and module == GetModuleHandle(0)) | |
+ { | |
+ dump(e, SignalRegistrar::Data::instance->crashDumpDirectory); | |
+ } else { | |
+ logException(e, SignalRegistrar::Data::instance->crashDumpDirectory); | |
+ } | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment