#include <algorithm>
#include <signal.h>
#include <stdio.h>
-#include <vector>
#include "llvm/Support/Format.h"
#include "llvm/Support/raw_ostream.h"
#ifdef _MSC_VER
#pragma comment(lib, "psapi.lib")
- #pragma comment(lib, "dbghelp.lib")
#elif __MINGW32__
- #if ((HAVE_LIBIMAGEHLP != 1) || (HAVE_LIBPSAPI != 1))
- #error "libimagehlp.a & libpsapi.a should be present"
+ #if (HAVE_LIBPSAPI != 1)
+ #error "libpsapi.a should be present"
#endif
// The version of g++ that comes with MinGW does *not* properly understand
// the ll format specifier for printf. However, MinGW passes the format
DWORD64 Reserved[3];
KDHELP64 KdHelp;
} STACKFRAME64, *LPSTACKFRAME64;
+ #endif // !defined(__MINGW64_VERSION_MAJOR)
+#endif // __MINGW32__
typedef BOOL (__stdcall *PREAD_PROCESS_MEMORY_ROUTINE64)(HANDLE hProcess,
DWORD64 qwBaseAddress, PVOID lpBuffer, DWORD nSize,
PFUNCTION_TABLE_ACCESS_ROUTINE64,
PGET_MODULE_BASE_ROUTINE64,
PTRANSLATE_ADDRESS_ROUTINE64);
-static fpStackWalk64 StackWalk64;
+static fpStackWalk64 fStackWalk64;
typedef DWORD64 (WINAPI *fpSymGetModuleBase64)(HANDLE, DWORD64);
-static fpSymGetModuleBase64 SymGetModuleBase64;
+static fpSymGetModuleBase64 fSymGetModuleBase64;
typedef BOOL (WINAPI *fpSymGetSymFromAddr64)(HANDLE, DWORD64,
PDWORD64, PIMAGEHLP_SYMBOL64);
-static fpSymGetSymFromAddr64 SymGetSymFromAddr64;
+static fpSymGetSymFromAddr64 fSymGetSymFromAddr64;
typedef BOOL (WINAPI *fpSymGetLineFromAddr64)(HANDLE, DWORD64,
PDWORD, PIMAGEHLP_LINE64);
-static fpSymGetLineFromAddr64 SymGetLineFromAddr64;
+static fpSymGetLineFromAddr64 fSymGetLineFromAddr64;
+
+typedef BOOL(WINAPI *fpSymGetModuleInfo64)(HANDLE hProcess, DWORD64 dwAddr,
+ PIMAGEHLP_MODULE64 ModuleInfo);
+static fpSymGetModuleInfo64 fSymGetModuleInfo64;
typedef PVOID (WINAPI *fpSymFunctionTableAccess64)(HANDLE, DWORD64);
-static fpSymFunctionTableAccess64 SymFunctionTableAccess64;
+static fpSymFunctionTableAccess64 fSymFunctionTableAccess64;
+
+typedef DWORD (WINAPI *fpSymSetOptions)(DWORD);
+static fpSymSetOptions fSymSetOptions;
+
+typedef BOOL (WINAPI *fpSymInitialize)(HANDLE, PCSTR, BOOL);
+static fpSymInitialize fSymInitialize;
+
+typedef BOOL (WINAPI *fpEnumerateLoadedModules)(HANDLE,PENUMLOADED_MODULES_CALLBACK64,PVOID);
+static fpEnumerateLoadedModules fEnumerateLoadedModules;
static bool load64BitDebugHelp(void) {
HMODULE hLib = ::LoadLibraryW(L"Dbghelp.dll");
if (hLib) {
- StackWalk64 = (fpStackWalk64)
+ fStackWalk64 = (fpStackWalk64)
::GetProcAddress(hLib, "StackWalk64");
- SymGetModuleBase64 = (fpSymGetModuleBase64)
+ fSymGetModuleBase64 = (fpSymGetModuleBase64)
::GetProcAddress(hLib, "SymGetModuleBase64");
- SymGetSymFromAddr64 = (fpSymGetSymFromAddr64)
+ fSymGetSymFromAddr64 = (fpSymGetSymFromAddr64)
::GetProcAddress(hLib, "SymGetSymFromAddr64");
- SymGetLineFromAddr64 = (fpSymGetLineFromAddr64)
+ fSymGetLineFromAddr64 = (fpSymGetLineFromAddr64)
::GetProcAddress(hLib, "SymGetLineFromAddr64");
- SymFunctionTableAccess64 = (fpSymFunctionTableAccess64)
+ fSymGetModuleInfo64 = (fpSymGetModuleInfo64)
+ ::GetProcAddress(hLib, "SymGetModuleInfo64");
+ fSymFunctionTableAccess64 = (fpSymFunctionTableAccess64)
::GetProcAddress(hLib, "SymFunctionTableAccess64");
+ fSymSetOptions = (fpSymSetOptions)::GetProcAddress(hLib, "SymSetOptions");
+ fSymInitialize = (fpSymInitialize)::GetProcAddress(hLib, "SymInitialize");
+ fEnumerateLoadedModules = (fpEnumerateLoadedModules)
+ ::GetProcAddress(hLib, "EnumerateLoadedModules64");
}
- return StackWalk64 != NULL;
+ return fStackWalk64 && fSymInitialize && fSymSetOptions;
}
- #endif // !defined(__MINGW64_VERSION_MAJOR)
-#endif // __MINGW32__
+
+using namespace llvm;
// Forward declare.
static LONG WINAPI LLVMUnhandledExceptionFilter(LPEXCEPTION_POINTERS ep);
static void (*InterruptFunction)() = 0;
static std::vector<std::string> *FilesToRemove = NULL;
-static std::vector<std::pair<void(*)(void*), void*> > *CallBacksToRun = 0;
static bool RegisteredUnhandledExceptionFilter = false;
static bool CleanupExecuted = false;
static PTOP_LEVEL_EXCEPTION_FILTER OldFilter = NULL;
static CRITICAL_SECTION CriticalSection;
static bool CriticalSectionInitialized = false;
-static void PrintStackTraceForThread(llvm::raw_ostream &OS, HANDLE hProcess,
- HANDLE hThread, STACKFRAME64 &StackFrame,
- CONTEXT *Context) {
- DWORD machineType;
+enum {
#if defined(_M_X64)
- machineType = IMAGE_FILE_MACHINE_AMD64;
+ NativeMachineType = IMAGE_FILE_MACHINE_AMD64
#else
- machineType = IMAGE_FILE_MACHINE_I386;
+ NativeMachineType = IMAGE_FILE_MACHINE_I386
#endif
+};
+
+static bool printStackTraceWithLLVMSymbolizer(llvm::raw_ostream &OS,
+ HANDLE hProcess, HANDLE hThread,
+ STACKFRAME64 &StackFrameOrig,
+ CONTEXT *ContextOrig) {
+ // StackWalk64 modifies the incoming stack frame and context, so copy them.
+ STACKFRAME64 StackFrame = StackFrameOrig;
+
+ // Copy the register context so that we don't modify it while we unwind. We
+ // could use InitializeContext + CopyContext, but that's only required to get
+ // at AVX registers, which typically aren't needed by StackWalk64. Reduce the
+ // flag set to indicate that there's less data.
+ CONTEXT Context = *ContextOrig;
+ Context.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER;
+
+ static void *StackTrace[256];
+ size_t Depth = 0;
+ while (fStackWalk64(NativeMachineType, hProcess, hThread, &StackFrame,
+ &Context, 0, fSymFunctionTableAccess64,
+ fSymGetModuleBase64, 0)) {
+ if (StackFrame.AddrFrame.Offset == 0)
+ break;
+ StackTrace[Depth++] = (void *)(uintptr_t)StackFrame.AddrPC.Offset;
+ if (Depth >= array_lengthof(StackTrace))
+ break;
+ }
+
+ return printSymbolizedStackTrace(&StackTrace[0], Depth, OS);
+}
+
+namespace {
+struct FindModuleData {
+ void **StackTrace;
+ int Depth;
+ const char **Modules;
+ intptr_t *Offsets;
+ StringSaver *StrPool;
+};
+}
+static BOOL CALLBACK findModuleCallback(WIN32_ELMCB_PCSTR ModuleName,
+ DWORD64 ModuleBase, ULONG ModuleSize,
+ void *VoidData) {
+ FindModuleData *Data = (FindModuleData*)VoidData;
+ intptr_t Beg = ModuleBase;
+ intptr_t End = Beg + ModuleSize;
+ for (int I = 0; I < Data->Depth; I++) {
+ if (Data->Modules[I])
+ continue;
+ intptr_t Addr = (intptr_t)Data->StackTrace[I];
+ if (Beg <= Addr && Addr < End) {
+ Data->Modules[I] = Data->StrPool->save(ModuleName);
+ Data->Offsets[I] = Addr - Beg;
+ }
+ }
+ return TRUE;
+}
+
+static bool findModulesAndOffsets(void **StackTrace, int Depth,
+ const char **Modules, intptr_t *Offsets,
+ const char *MainExecutableName,
+ StringSaver &StrPool) {
+ if (!fEnumerateLoadedModules)
+ return false;
+ FindModuleData Data;
+ Data.StackTrace = StackTrace;
+ Data.Depth = Depth;
+ Data.Modules = Modules;
+ Data.Offsets = Offsets;
+ Data.StrPool = &StrPool;
+ fEnumerateLoadedModules(GetCurrentProcess(), findModuleCallback, &Data);
+ return true;
+}
+
+static void PrintStackTraceForThread(llvm::raw_ostream &OS, HANDLE hProcess,
+ HANDLE hThread, STACKFRAME64 &StackFrame,
+ CONTEXT *Context) {
// Initialize the symbol handler.
- SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES);
- SymInitialize(hProcess, NULL, TRUE);
+ fSymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES);
+ fSymInitialize(hProcess, NULL, TRUE);
+
+ // Try llvm-symbolizer first. llvm-symbolizer knows how to deal with both PDBs
+ // and DWARF, so it should do a good job regardless of what debug info or
+ // linker is in use.
+ if (printStackTraceWithLLVMSymbolizer(OS, hProcess, hThread, StackFrame,
+ Context)) {
+ return;
+ }
while (true) {
- if (!StackWalk64(machineType, hProcess, hThread, &StackFrame, Context, NULL,
- SymFunctionTableAccess64, SymGetModuleBase64, NULL)) {
+ if (!fStackWalk64(NativeMachineType, hProcess, hThread, &StackFrame,
+ Context, 0, fSymFunctionTableAccess64,
+ fSymGetModuleBase64, 0)) {
break;
}
static_cast<DWORD>(StackFrame.Params[3]));
#endif
// Verify the PC belongs to a module in this process.
- if (!SymGetModuleBase64(hProcess, PC)) {
+ if (!fSymGetModuleBase64(hProcess, PC)) {
OS << " <unknown module>\n";
continue;
}
symbol->MaxNameLength = 512 - sizeof(IMAGEHLP_SYMBOL64);
DWORD64 dwDisp;
- if (!SymGetSymFromAddr64(hProcess, PC, &dwDisp, symbol)) {
+ if (!fSymGetSymFromAddr64(hProcess, PC, &dwDisp, symbol)) {
OS << '\n';
continue;
}
IMAGEHLP_LINE64 line = {};
DWORD dwLineDisp;
line.SizeOfStruct = sizeof(line);
- if (SymGetLineFromAddr64(hProcess, PC, &dwLineDisp, &line)) {
+ if (fSymGetLineFromAddr64(hProcess, PC, &dwLineDisp, &line)) {
OS << format(", %s, line %lu", line.FileName, line.LineNumber);
if (dwLineDisp > 0)
OS << format(" + 0x%lX byte(s)", dwLineDisp);
}
static void RegisterHandler() {
-#if __MINGW32__ && !defined(__MINGW64_VERSION_MAJOR)
- // On MinGW.org, we need to load up the symbols explicitly, because the
- // Win32 framework they include does not have support for the 64-bit
- // versions of the APIs we need. If we cannot load up the APIs (which
- // would be unexpected as they should exist on every version of Windows
- // we support), we will bail out since there would be nothing to report.
+ // If we cannot load up the APIs (which would be unexpected as they should
+ // exist on every version of Windows we support), we will bail out since
+ // there would be nothing to report.
if (!load64BitDebugHelp()) {
assert(false && "These APIs should always be available");
return;
}
-#endif
if (RegisteredUnhandledExceptionFilter) {
EnterCriticalSection(&CriticalSection);
#endif
void llvm::sys::PrintStackTrace(raw_ostream &OS) {
-
STACKFRAME64 StackFrame = {};
CONTEXT Context = {};
::RtlCaptureContext(&Context);
/// to the process. The handler can have a cookie passed to it to identify
/// what instance of the handler it is.
void llvm::sys::AddSignalHandler(void (*FnPtr)(void *), void *Cookie) {
- if (CallBacksToRun == 0)
- CallBacksToRun = new std::vector<std::pair<void(*)(void*), void*> >();
CallBacksToRun->push_back(std::make_pair(FnPtr, Cookie));
RegisterHandler();
LeaveCriticalSection(&CriticalSection);
}
static void Cleanup() {
+ if (CleanupExecuted)
+ return;
+
EnterCriticalSection(&CriticalSection);
// Prevent other thread from registering new files and directories for
CleanupExecuted = true;
// FIXME: open files cannot be deleted.
-
if (FilesToRemove != NULL)
while (!FilesToRemove->empty()) {
llvm::sys::fs::remove(FilesToRemove->back());
FilesToRemove->pop_back();
}
-
- if (CallBacksToRun)
- for (unsigned i = 0, e = CallBacksToRun->size(); i != e; ++i)
- (*CallBacksToRun)[i].first((*CallBacksToRun)[i].second);
-
+ llvm::sys::RunSignalHandlers();
LeaveCriticalSection(&CriticalSection);
}