Index: lib/Support/Signals.cpp =================================================================== --- lib/Support/Signals.cpp +++ lib/Support/Signals.cpp @@ -12,10 +12,20 @@ // //===----------------------------------------------------------------------===// +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringRef.h" #include "llvm/Config/config.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/FileUtilities.h" +#include "llvm/Support/Format.h" #include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Mutex.h" +#include "llvm/Support/Program.h" #include "llvm/Support/Signals.h" - +#include "llvm/Support/StringSaver.h" +#include "llvm/Support/raw_ostream.h" #include namespace llvm { @@ -37,6 +47,121 @@ } } +using namespace llvm; + +static bool findModulesAndOffsets(void **StackTrace, int Depth, + const char **Modules, intptr_t *Offsets, + const char *MainExecutableName, + StringSaver &StrPool); + +/// FMT_PTR - Portable format specifier for a pointer. MSVC prints pointers +/// without the 0x prefix in all upper case. llvm-symbolizer expects the 0x +/// prefix. +#ifndef _MSC_VER +#define FMT_PTR "%p" +#else +#ifdef _M_X64 +#define FMT_PTR "0x%016llx" +#else +#define FMT_PTR "0x%08llx" +#endif +#endif + +/// Helper that launches llvm-symbolizer and symbolizes a backtrace. +static bool printSymbolizedStackTrace(void **StackTrace, int Depth, + llvm::raw_ostream &OS) { + // FIXME: Subtract necessary number from StackTrace entries to turn return addresses + // into actual instruction addresses. + // Use llvm-symbolizer tool to symbolize the stack traces. + ErrorOr LLVMSymbolizerPathOrErr = + sys::findProgramByName("llvm-symbolizer"); + if (!LLVMSymbolizerPathOrErr) + return false; + const std::string &LLVMSymbolizerPath = *LLVMSymbolizerPathOrErr; + // We don't know argv0 or the address of main() at this point, but try + // to guess it anyway (it's possible on some platforms). + std::string MainExecutableName = sys::fs::getMainExecutable(nullptr, nullptr); + if (MainExecutableName.empty() || + MainExecutableName.find("llvm-symbolizer") != std::string::npos) + return false; + + BumpPtrAllocator Allocator; + StringSaver StrPool(Allocator); + std::vector Modules(Depth, nullptr); + std::vector Offsets(Depth, 0); + if (!findModulesAndOffsets(StackTrace, Depth, Modules.data(), Offsets.data(), + MainExecutableName.c_str(), StrPool)) + return false; + int InputFD; + SmallString<32> InputFile, OutputFile; + sys::fs::createTemporaryFile("symbolizer-input", "", InputFD, InputFile); + sys::fs::createTemporaryFile("symbolizer-output", "", OutputFile); + FileRemover InputRemover(InputFile.c_str()); + FileRemover OutputRemover(OutputFile.c_str()); + + { + raw_fd_ostream Input(InputFD, true); + for (int i = 0; i < Depth; i++) { + if (Modules[i]) + Input << Modules[i] << " " << (void*)Offsets[i] << "\n"; + } + } + + StringRef InputFileStr(InputFile); + StringRef OutputFileStr(OutputFile); + StringRef StderrFileStr; + const StringRef *Redirects[] = {&InputFileStr, &OutputFileStr, + &StderrFileStr}; + const char *Args[] = {"llvm-symbolizer", "--functions=linkage", "--inlining", +#ifdef LLVM_ON_WIN32 + // Pass --relative-address on Windows so that we don't + // have to add ImageBase from PE file. + // FIXME: Make this the default for llvm-symbolizer. + "--relative-address", +#endif + "--demangle", nullptr}; + int RunResult = + sys::ExecuteAndWait(LLVMSymbolizerPath, Args, nullptr, Redirects); + if (RunResult != 0) + return false; + + auto OutputBuf = MemoryBuffer::getFile(OutputFile.c_str()); + if (!OutputBuf) + return false; + StringRef Output = OutputBuf.get()->getBuffer(); + SmallVector Lines; + Output.split(Lines, "\n"); + auto CurLine = Lines.begin(); + int frame_no = 0; + for (int i = 0; i < Depth; i++) { + if (!Modules[i]) { + OS << format("#%d " FMT_PTR "\n", frame_no++, StackTrace[i]); + continue; + } + // Read pairs of lines (function name and file/line info) until we + // encounter empty line. + for (;;) { + if (CurLine == Lines.end()) + return false; + StringRef FunctionName = *CurLine++; + if (FunctionName.empty()) + break; + OS << format("#%d " FMT_PTR " ", frame_no++, StackTrace[i]); + if (!FunctionName.startswith("??")) + OS << format("%s ", FunctionName.str().c_str()); + if (CurLine == Lines.end()) + return false; + StringRef FileLineInfo = *CurLine++; + if (!FileLineInfo.startswith("??")) + OS << format("%s", FileLineInfo.str().c_str()); + else + OS << format("(%s+" FMT_PTR ")", Modules[i], (void *)Offsets[i]); + OS << "\n"; + } + } + return true; +} + // Include the platform-specific parts of this class. #ifdef LLVM_ON_UNIX #include "Unix/Signals.inc" Index: lib/Support/Unix/Signals.inc =================================================================== --- lib/Support/Unix/Signals.inc +++ lib/Support/Unix/Signals.inc @@ -291,7 +291,8 @@ static bool findModulesAndOffsets(void **StackTrace, int Depth, const char **Modules, intptr_t *Offsets, - const char *MainExecutableName) { + const char *MainExecutableName, + StringSaver &StrPool) { DlIteratePhdrData data = {StackTrace, Depth, true, Modules, Offsets, MainExecutableName}; dl_iterate_phdr(dl_iterate_phdr_cb, &data); @@ -304,92 +305,6 @@ return false; } #endif - -static bool printSymbolizedStackTrace(void **StackTrace, int Depth, - llvm::raw_ostream &OS) { - // FIXME: Subtract necessary number from StackTrace entries to turn return addresses - // into actual instruction addresses. - // Use llvm-symbolizer tool to symbolize the stack traces. - ErrorOr LLVMSymbolizerPathOrErr = - sys::findProgramByName("llvm-symbolizer"); - if (!LLVMSymbolizerPathOrErr) - return false; - const std::string &LLVMSymbolizerPath = *LLVMSymbolizerPathOrErr; - // We don't know argv0 or the address of main() at this point, but try - // to guess it anyway (it's possible on some platforms). - std::string MainExecutableName = sys::fs::getMainExecutable(nullptr, nullptr); - if (MainExecutableName.empty() || - MainExecutableName.find("llvm-symbolizer") != std::string::npos) - return false; - - std::vector Modules(Depth, nullptr); - std::vector Offsets(Depth, 0); - if (!findModulesAndOffsets(StackTrace, Depth, Modules.data(), Offsets.data(), - MainExecutableName.c_str())) - return false; - int InputFD; - SmallString<32> InputFile, OutputFile; - sys::fs::createTemporaryFile("symbolizer-input", "", InputFD, InputFile); - sys::fs::createTemporaryFile("symbolizer-output", "", OutputFile); - FileRemover InputRemover(InputFile.c_str()); - FileRemover OutputRemover(OutputFile.c_str()); - - { - raw_fd_ostream Input(InputFD, true); - for (int i = 0; i < Depth; i++) { - if (Modules[i]) - Input << Modules[i] << " " << (void*)Offsets[i] << "\n"; - } - } - - StringRef InputFileStr(InputFile); - StringRef OutputFileStr(OutputFile); - StringRef StderrFileStr; - const StringRef *Redirects[] = {&InputFileStr, &OutputFileStr, - &StderrFileStr}; - const char *Args[] = {"llvm-symbolizer", "--functions=linkage", "--inlining", - "--demangle", nullptr}; - int RunResult = - sys::ExecuteAndWait(LLVMSymbolizerPath, Args, nullptr, Redirects); - if (RunResult != 0) - return false; - - auto OutputBuf = MemoryBuffer::getFile(OutputFile.c_str()); - if (!OutputBuf) - return false; - StringRef Output = OutputBuf.get()->getBuffer(); - SmallVector Lines; - Output.split(Lines, "\n"); - auto CurLine = Lines.begin(); - int frame_no = 0; - for (int i = 0; i < Depth; i++) { - if (!Modules[i]) { - OS << format("#%d %p\n", frame_no++, StackTrace[i]); - continue; - } - // Read pairs of lines (function name and file/line info) until we - // encounter empty line. - for (;;) { - if (CurLine == Lines.end()) - return false; - StringRef FunctionName = *CurLine++; - if (FunctionName.empty()) - break; - OS << format("#%d %p ", frame_no++, StackTrace[i]); - if (!FunctionName.startswith("??")) - OS << format("%s ", FunctionName.str().c_str()); - if (CurLine == Lines.end()) - return false; - StringRef FileLineInfo = *CurLine++; - if (!FileLineInfo.startswith("??")) - OS << format("%s", FileLineInfo.str().c_str()); - else - OS << format("(%s+%p)", Modules[i], (void *)Offsets[i]); - OS << "\n"; - } - } - return true; -} #endif // defined(HAVE_BACKTRACE) && defined(ENABLE_BACKTRACES) // PrintStackTrace - In the case of a program crash or fault, print out a stack Index: lib/Support/Windows/Signals.inc =================================================================== --- lib/Support/Windows/Signals.inc +++ lib/Support/Windows/Signals.inc @@ -135,6 +135,10 @@ PDWORD, PIMAGEHLP_LINE64); 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 fSymFunctionTableAccess64; @@ -144,6 +148,9 @@ 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) { @@ -155,10 +162,14 @@ ::GetProcAddress(hLib, "SymGetSymFromAddr64"); fSymGetLineFromAddr64 = (fpSymGetLineFromAddr64) ::GetProcAddress(hLib, "SymGetLineFromAddr64"); + 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 fStackWalk64 && fSymInitialize && fSymSetOptions; } @@ -183,23 +194,111 @@ 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]; + int 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. fSymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES); fSymInitialize(hProcess, NULL, TRUE); + // Use llvm-symbolizer when we're self-hosting on Windows. 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. +#ifdef __clang__ + if (printStackTraceWithLLVMSymbolizer(OS, hProcess, hThread, StackFrame, + Context)) { + return; + } +#else + // Suppress warnings about unused static functions. + (void)&printStackTraceWithLLVMSymbolizer; +#endif + while (true) { - if (!fStackWalk64(machineType, hProcess, hThread, &StackFrame, Context, 0, - fSymFunctionTableAccess64, fSymGetModuleBase64, 0)) { + if (!fStackWalk64(NativeMachineType, hProcess, hThread, &StackFrame, + Context, 0, fSymFunctionTableAccess64, + fSymGetModuleBase64, 0)) { break; } @@ -404,7 +503,6 @@ #endif void llvm::sys::PrintStackTrace(raw_ostream &OS) { - STACKFRAME64 StackFrame = {}; CONTEXT Context = {}; ::RtlCaptureContext(&Context);