forked from OSchip/llvm-project
[Windows] Symbolize with llvm-symbolizer instead of dbghelp in a self-host
Summary: llvm-symbolizer understands both PDBs and DWARF, so it is more likely to succeed at symbolization. If llvm-symbolizer is unavailable, we will fall back to dbghelp. This also makes our crash traces more similar between Windows and Linux. Reviewers: Bigcheese, zturner, chapuni Subscribers: llvm-commits Differential Revision: http://reviews.llvm.org/D12884 llvm-svn: 252118
This commit is contained in:
parent
b0f87ed692
commit
ba5757da64
|
@ -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 <vector>
|
||||
|
||||
namespace llvm {
|
||||
|
@ -37,6 +47,118 @@ void sys::RunSignalHandlers() {
|
|||
}
|
||||
}
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
static bool findModulesAndOffsets(void **StackTrace, int Depth,
|
||||
const char **Modules, intptr_t *Offsets,
|
||||
const char *MainExecutableName,
|
||||
StringSaver &StrPool);
|
||||
|
||||
/// Format a pointer value as hexadecimal. Zero pad it out so its always the
|
||||
/// same width.
|
||||
static FormattedNumber format_ptr(void *PC) {
|
||||
// Each byte is two hex digits plus 2 for the 0x prefix.
|
||||
unsigned PtrWidth = 2 + 2 * sizeof(void *);
|
||||
return format_hex((uint64_t)PC, PtrWidth);
|
||||
}
|
||||
|
||||
/// 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<std::string> 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<const char *> Modules(Depth, nullptr);
|
||||
std::vector<intptr_t> 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;
|
||||
|
||||
// This report format is based on the sanitizer stack trace printer. See
|
||||
// sanitizer_stacktrace_printer.cc in compiler-rt.
|
||||
auto OutputBuf = MemoryBuffer::getFile(OutputFile.c_str());
|
||||
if (!OutputBuf)
|
||||
return false;
|
||||
StringRef Output = OutputBuf.get()->getBuffer();
|
||||
SmallVector<StringRef, 32> Lines;
|
||||
Output.split(Lines, "\n");
|
||||
auto CurLine = Lines.begin();
|
||||
int frame_no = 0;
|
||||
for (int i = 0; i < Depth; i++) {
|
||||
if (!Modules[i]) {
|
||||
OS << '#' << frame_no++ << ' ' << format_ptr(StackTrace[i]) << '\n';
|
||||
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 << '#' << frame_no++ << ' ' << format_ptr(StackTrace[i]) << ' ';
|
||||
if (!FunctionName.startswith("??"))
|
||||
OS << FunctionName << ' ';
|
||||
if (CurLine == Lines.end())
|
||||
return false;
|
||||
StringRef FileLineInfo = *CurLine++;
|
||||
if (!FileLineInfo.startswith("??"))
|
||||
OS << FileLineInfo;
|
||||
else
|
||||
OS << "(" << Modules[i] << '+' << format_hex(Offsets[i], 0) << ")";
|
||||
OS << "\n";
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Include the platform-specific parts of this class.
|
||||
#ifdef LLVM_ON_UNIX
|
||||
#include "Unix/Signals.inc"
|
||||
|
|
|
@ -291,7 +291,8 @@ static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) {
|
|||
|
||||
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 @@ static bool findModulesAndOffsets(void **StackTrace, int Depth,
|
|||
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<std::string> 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<const char *> Modules(Depth, nullptr);
|
||||
std::vector<intptr_t> 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<StringRef, 32> 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
|
||||
|
|
|
@ -135,6 +135,10 @@ typedef BOOL (WINAPI *fpSymGetLineFromAddr64)(HANDLE, DWORD64,
|
|||
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 @@ 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) {
|
||||
|
@ -155,10 +162,14 @@ static bool load64BitDebugHelp(void) {
|
|||
::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,106 @@ static PTOP_LEVEL_EXCEPTION_FILTER OldFilter = NULL;
|
|||
static CRITICAL_SECTION CriticalSection;
|
||||
static bool CriticalSectionInitialized = false;
|
||||
|
||||
enum {
|
||||
#if defined(_M_X64)
|
||||
NativeMachineType = IMAGE_FILE_MACHINE_AMD64
|
||||
#else
|
||||
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) {
|
||||
DWORD machineType;
|
||||
#if defined(_M_X64)
|
||||
machineType = IMAGE_FILE_MACHINE_AMD64;
|
||||
#else
|
||||
machineType = IMAGE_FILE_MACHINE_I386;
|
||||
#endif
|
||||
|
||||
// Initialize the symbol handler.
|
||||
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 (!fStackWalk64(machineType, hProcess, hThread, &StackFrame, Context, 0,
|
||||
fSymFunctionTableAccess64, fSymGetModuleBase64, 0)) {
|
||||
if (!fStackWalk64(NativeMachineType, hProcess, hThread, &StackFrame,
|
||||
Context, 0, fSymFunctionTableAccess64,
|
||||
fSymGetModuleBase64, 0)) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -404,7 +498,6 @@ extern "C" VOID WINAPI RtlCaptureContext(PCONTEXT ContextRecord);
|
|||
#endif
|
||||
|
||||
void llvm::sys::PrintStackTrace(raw_ostream &OS) {
|
||||
|
||||
STACKFRAME64 StackFrame = {};
|
||||
CONTEXT Context = {};
|
||||
::RtlCaptureContext(&Context);
|
||||
|
|
Loading…
Reference in New Issue