diff options
Diffstat (limited to 'contrib/llvm/lib/Support/Unix/Signals.inc')
-rw-r--r-- | contrib/llvm/lib/Support/Unix/Signals.inc | 266 |
1 files changed, 209 insertions, 57 deletions
diff --git a/contrib/llvm/lib/Support/Unix/Signals.inc b/contrib/llvm/lib/Support/Unix/Signals.inc index 1841fea..e8f4643 100644 --- a/contrib/llvm/lib/Support/Unix/Signals.inc +++ b/contrib/llvm/lib/Support/Unix/Signals.inc @@ -14,7 +14,14 @@ #include "Unix.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/FileUtilities.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Mutex.h" +#include "llvm/Support/Program.h" +#include "llvm/Support/UniqueLock.h" +#include "llvm/Support/raw_ostream.h" #include <algorithm> #include <string> #include <vector> @@ -36,18 +43,22 @@ #if HAVE_MACH_MACH_H #include <mach/mach.h> #endif +#if HAVE_LINK_H +#include <link.h> +#endif using namespace llvm; static RETSIGTYPE SignalHandler(int Sig); // defined below. -static SmartMutex<true> SignalsMutex; +static ManagedStatic<SmartMutex<true> > SignalsMutex; /// InterruptFunction - The function to call if ctrl-c is pressed. static void (*InterruptFunction)() = nullptr; -static std::vector<std::string> FilesToRemove; -static std::vector<std::pair<void(*)(void*), void*> > CallBacksToRun; +static ManagedStatic<std::vector<std::string>> FilesToRemove; +static ManagedStatic<std::vector<std::pair<void (*)(void *), void *>>> + CallBacksToRun; // IntSigs - Signals that represent requested termination. There's no bug // or failure, or if there is, it's not our direct responsibility. For whatever @@ -55,7 +66,6 @@ static std::vector<std::pair<void(*)(void*), void*> > CallBacksToRun; static const int IntSigs[] = { SIGHUP, SIGINT, SIGPIPE, SIGTERM, SIGUSR1, SIGUSR2 }; -static const int *const IntSigsEnd = std::end(IntSigs); // KillSigs - Signals that represent that we have a bug, and our prompt // termination has been ordered. @@ -74,7 +84,6 @@ static const int KillSigs[] = { , SIGEMT #endif }; -static const int *const KillSigsEnd = std::end(KillSigs); static unsigned NumRegisteredSignals = 0; static struct { @@ -105,8 +114,8 @@ static void RegisterHandlers() { // If the handlers are already registered, we're done. if (NumRegisteredSignals != 0) return; - std::for_each(IntSigs, IntSigsEnd, RegisterHandler); - std::for_each(KillSigs, KillSigsEnd, RegisterHandler); + for (auto S : IntSigs) RegisterHandler(S); + for (auto S : KillSigs) RegisterHandler(S); } static void UnregisterHandlers() { @@ -125,11 +134,12 @@ static void UnregisterHandlers() { static void RemoveFilesToRemove() { // We avoid iterators in case of debug iterators that allocate or release // memory. - for (unsigned i = 0, e = FilesToRemove.size(); i != e; ++i) { + std::vector<std::string>& FilesToRemoveRef = *FilesToRemove; + for (unsigned i = 0, e = FilesToRemoveRef.size(); i != e; ++i) { // We rely on a std::string implementation for which repeated calls to // 'c_str()' don't allocate memory. We pre-call 'c_str()' on all of these // strings to try to ensure this is safe. - const char *path = FilesToRemove[i].c_str(); + const char *path = FilesToRemoveRef[i].c_str(); // Get the status so we can determine if it's a file or directory. If we // can't stat the file, ignore it. @@ -162,28 +172,31 @@ static RETSIGTYPE SignalHandler(int Sig) { sigfillset(&SigMask); sigprocmask(SIG_UNBLOCK, &SigMask, nullptr); - SignalsMutex.acquire(); - RemoveFilesToRemove(); - - if (std::find(IntSigs, IntSigsEnd, Sig) != IntSigsEnd) { - if (InterruptFunction) { - void (*IF)() = InterruptFunction; - SignalsMutex.release(); - InterruptFunction = nullptr; - IF(); // run the interrupt function. + { + unique_lock<SmartMutex<true>> Guard(*SignalsMutex); + RemoveFilesToRemove(); + + if (std::find(std::begin(IntSigs), std::end(IntSigs), Sig) + != std::end(IntSigs)) { + if (InterruptFunction) { + void (*IF)() = InterruptFunction; + Guard.unlock(); + InterruptFunction = nullptr; + IF(); // run the interrupt function. + return; + } + + Guard.unlock(); + raise(Sig); // Execute the default handler. return; - } - - SignalsMutex.release(); - raise(Sig); // Execute the default handler. - return; + } } - SignalsMutex.release(); - // Otherwise if it is a fault (like SEGV) run any handler. - for (unsigned i = 0, e = CallBacksToRun.size(); i != e; ++i) - CallBacksToRun[i].first(CallBacksToRun[i].second); + std::vector<std::pair<void (*)(void *), void *>>& CallBacksToRunRef = + *CallBacksToRun; + for (unsigned i = 0, e = CallBacksToRun->size(); i != e; ++i) + CallBacksToRunRef[i].first(CallBacksToRunRef[i].second); #ifdef __s390__ // On S/390, certain signals are delivered with PSW Address pointing to @@ -196,37 +209,39 @@ static RETSIGTYPE SignalHandler(int Sig) { } void llvm::sys::RunInterruptHandlers() { - SignalsMutex.acquire(); + sys::SmartScopedLock<true> Guard(*SignalsMutex); RemoveFilesToRemove(); - SignalsMutex.release(); } void llvm::sys::SetInterruptFunction(void (*IF)()) { - SignalsMutex.acquire(); - InterruptFunction = IF; - SignalsMutex.release(); + { + sys::SmartScopedLock<true> Guard(*SignalsMutex); + InterruptFunction = IF; + } RegisterHandlers(); } // RemoveFileOnSignal - The public API bool llvm::sys::RemoveFileOnSignal(StringRef Filename, std::string* ErrMsg) { - SignalsMutex.acquire(); - std::string *OldPtr = FilesToRemove.empty() ? nullptr : &FilesToRemove[0]; - FilesToRemove.push_back(Filename); - - // We want to call 'c_str()' on every std::string in this vector so that if - // the underlying implementation requires a re-allocation, it happens here - // rather than inside of the signal handler. If we see the vector grow, we - // have to call it on every entry. If it remains in place, we only need to - // call it on the latest one. - if (OldPtr == &FilesToRemove[0]) - FilesToRemove.back().c_str(); - else - for (unsigned i = 0, e = FilesToRemove.size(); i != e; ++i) - FilesToRemove[i].c_str(); - - SignalsMutex.release(); + { + sys::SmartScopedLock<true> Guard(*SignalsMutex); + std::vector<std::string>& FilesToRemoveRef = *FilesToRemove; + std::string *OldPtr = + FilesToRemoveRef.empty() ? nullptr : &FilesToRemoveRef[0]; + FilesToRemoveRef.push_back(Filename); + + // We want to call 'c_str()' on every std::string in this vector so that if + // the underlying implementation requires a re-allocation, it happens here + // rather than inside of the signal handler. If we see the vector grow, we + // have to call it on every entry. If it remains in place, we only need to + // call it on the latest one. + if (OldPtr == &FilesToRemoveRef[0]) + FilesToRemoveRef.back().c_str(); + else + for (unsigned i = 0, e = FilesToRemoveRef.size(); i != e; ++i) + FilesToRemoveRef[i].c_str(); + } RegisterHandlers(); return false; @@ -234,31 +249,166 @@ bool llvm::sys::RemoveFileOnSignal(StringRef Filename, // DontRemoveFileOnSignal - The public API void llvm::sys::DontRemoveFileOnSignal(StringRef Filename) { - SignalsMutex.acquire(); + sys::SmartScopedLock<true> Guard(*SignalsMutex); std::vector<std::string>::reverse_iterator RI = - std::find(FilesToRemove.rbegin(), FilesToRemove.rend(), Filename); - std::vector<std::string>::iterator I = FilesToRemove.end(); - if (RI != FilesToRemove.rend()) - I = FilesToRemove.erase(RI.base()-1); + std::find(FilesToRemove->rbegin(), FilesToRemove->rend(), Filename); + std::vector<std::string>::iterator I = FilesToRemove->end(); + if (RI != FilesToRemove->rend()) + I = FilesToRemove->erase(RI.base()-1); // We need to call c_str() on every element which would have been moved by // the erase. These elements, in a C++98 implementation where c_str() // requires a reallocation on the first call may have had the call to c_str() // made on insertion become invalid by being copied down an element. - for (std::vector<std::string>::iterator E = FilesToRemove.end(); I != E; ++I) + for (std::vector<std::string>::iterator E = FilesToRemove->end(); I != E; ++I) I->c_str(); - - SignalsMutex.release(); } /// AddSignalHandler - Add a function to be called when a signal is delivered /// 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) { - CallBacksToRun.push_back(std::make_pair(FnPtr, Cookie)); + CallBacksToRun->push_back(std::make_pair(FnPtr, Cookie)); RegisterHandlers(); } +#if defined(HAVE_BACKTRACE) && defined(ENABLE_BACKTRACES) + +#if HAVE_LINK_H && (defined(__linux__) || defined(__FreeBSD__) || \ + defined(__FreeBSD_kernel__) || defined(__NetBSD__)) +struct DlIteratePhdrData { + void **StackTrace; + int depth; + bool first; + const char **modules; + intptr_t *offsets; + const char *main_exec_name; +}; + +static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) { + DlIteratePhdrData *data = (DlIteratePhdrData*)arg; + const char *name = data->first ? data->main_exec_name : info->dlpi_name; + data->first = false; + for (int i = 0; i < info->dlpi_phnum; i++) { + const auto *phdr = &info->dlpi_phdr[i]; + if (phdr->p_type != PT_LOAD) + continue; + intptr_t beg = info->dlpi_addr + phdr->p_vaddr; + intptr_t end = beg + phdr->p_memsz; + for (int j = 0; j < data->depth; j++) { + if (data->modules[j]) + continue; + intptr_t addr = (intptr_t)data->StackTrace[j]; + if (beg <= addr && addr < end) { + data->modules[j] = name; + data->offsets[j] = addr - info->dlpi_addr; + } + } + } + return 0; +} + +static bool findModulesAndOffsets(void **StackTrace, int Depth, + const char **Modules, intptr_t *Offsets, + const char *MainExecutableName) { + DlIteratePhdrData data = {StackTrace, Depth, true, + Modules, Offsets, MainExecutableName}; + dl_iterate_phdr(dl_iterate_phdr_cb, &data); + return true; +} +#else +static bool findModulesAndOffsets(void **StackTrace, int Depth, + const char **Modules, intptr_t *Offsets, + const char *MainExecutableName) { + return false; +} +#endif + +static bool printSymbolizedStackTrace(void **StackTrace, int Depth, FILE *FD) { + // 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]) { + fprintf(FD, "#%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; + fprintf(FD, "#%d %p ", frame_no++, StackTrace[i]); + if (!FunctionName.startswith("??")) + fprintf(FD, "%s ", FunctionName.str().c_str()); + if (CurLine == Lines.end()) + return false; + StringRef FileLineInfo = *CurLine++; + if (!FileLineInfo.startswith("??")) + fprintf(FD, "%s", FileLineInfo.str().c_str()); + else + fprintf(FD, "(%s+%p)", Modules[i], (void *)Offsets[i]); + fprintf(FD, "\n"); + } + } + return true; +} +#endif // defined(HAVE_BACKTRACE) && defined(ENABLE_BACKTRACES) // PrintStackTrace - In the case of a program crash or fault, print out a stack // trace so that the user has an indication of why and where we died. @@ -271,6 +421,8 @@ void llvm::sys::PrintStackTrace(FILE *FD) { // Use backtrace() to output a backtrace on Linux systems with glibc. int depth = backtrace(StackTrace, static_cast<int>(array_lengthof(StackTrace))); + if (printSymbolizedStackTrace(StackTrace, depth, FD)) + return; #if HAVE_DLFCN_H && __GNUG__ int width = 0; for (int i = 0; i < depth; ++i) { |