diff options
Diffstat (limited to 'lib/Support/Unix')
-rw-r--r-- | lib/Support/Unix/Host.inc | 6 | ||||
-rw-r--r-- | lib/Support/Unix/Path.inc | 138 | ||||
-rw-r--r-- | lib/Support/Unix/Process.inc | 108 | ||||
-rw-r--r-- | lib/Support/Unix/Program.inc | 79 | ||||
-rw-r--r-- | lib/Support/Unix/RWMutex.inc | 12 | ||||
-rw-r--r-- | lib/Support/Unix/Signals.inc | 266 | ||||
-rw-r--r-- | lib/Support/Unix/TimeValue.inc | 2 | ||||
-rw-r--r-- | lib/Support/Unix/Unix.h | 4 |
8 files changed, 449 insertions, 166 deletions
diff --git a/lib/Support/Unix/Host.inc b/lib/Support/Unix/Host.inc index c5d36ff..fcb3638 100644 --- a/lib/Support/Unix/Host.inc +++ b/lib/Support/Unix/Host.inc @@ -1,4 +1,4 @@ - //===- llvm/Support/Unix/Host.inc -------------------------------*- C++ -*-===// +//===- llvm/Support/Unix/Host.inc -------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -22,7 +22,6 @@ #include <sys/utsname.h> #include <cctype> #include <string> -#include <cstdlib> // ::getenv using namespace llvm; @@ -39,7 +38,8 @@ std::string sys::getDefaultTargetTriple() { StringRef TargetTripleString(LLVM_DEFAULT_TARGET_TRIPLE); std::pair<StringRef, StringRef> ArchSplit = TargetTripleString.split('-'); - // Normalize the arch, since the target triple may not actually match the target. + // Normalize the arch, since the target triple may not actually match the + // target. std::string Arch = ArchSplit.first; std::string Triple(Arch); diff --git a/lib/Support/Unix/Path.inc b/lib/Support/Unix/Path.inc index 623547a..634d404 100644 --- a/lib/Support/Unix/Path.inc +++ b/lib/Support/Unix/Path.inc @@ -87,22 +87,6 @@ namespace { }; } -static std::error_code TempDir(SmallVectorImpl<char> &result) { - // FIXME: Don't use TMPDIR if program is SUID or SGID enabled. - const char *dir = nullptr; - (dir = std::getenv("TMPDIR")) || (dir = std::getenv("TMP")) || - (dir = std::getenv("TEMP")) || (dir = std::getenv("TEMPDIR")) || -#ifdef P_tmpdir - (dir = P_tmpdir) || -#endif - (dir = "/tmp"); - - result.clear(); - StringRef d(dir); - result.append(d.begin(), d.end()); - return std::error_code(); -} - namespace llvm { namespace sys { namespace fs { @@ -272,19 +256,6 @@ std::error_code create_directory(const Twine &path, bool IgnoreExisting) { return std::error_code(); } -std::error_code normalize_separators(SmallVectorImpl<char> &Path) { - for (auto PI = Path.begin(), PE = Path.end(); PI < PE; ++PI) { - if (*PI == '\\') { - auto PN = PI + 1; - if (PN < PE && *PN == '\\') - ++PI; // increment once, the for loop will move over the escaped slash - else - *PI = '/'; - } - } - return std::error_code(); -} - // Note that we are using symbolic link because hard links are not supported by // all filesystems (SMB doesn't). std::error_code create_link(const Twine &to, const Twine &from) { @@ -350,38 +321,35 @@ std::error_code resize_file(const Twine &path, uint64_t size) { return std::error_code(); } -std::error_code exists(const Twine &path, bool &result) { - SmallString<128> path_storage; - StringRef p = path.toNullTerminatedStringRef(path_storage); - - if (::access(p.begin(), F_OK) == -1) { - if (errno != ENOENT) - return std::error_code(errno, std::generic_category()); - result = false; - } else - result = true; - - return std::error_code(); +static int convertAccessMode(AccessMode Mode) { + switch (Mode) { + case AccessMode::Exist: + return F_OK; + case AccessMode::Write: + return W_OK; + case AccessMode::Execute: + return R_OK | X_OK; // scripts also need R_OK. + } + llvm_unreachable("invalid enum"); } -bool can_write(const Twine &Path) { +std::error_code access(const Twine &Path, AccessMode Mode) { SmallString<128> PathStorage; StringRef P = Path.toNullTerminatedStringRef(PathStorage); - return 0 == access(P.begin(), W_OK); -} -bool can_execute(const Twine &Path) { - SmallString<128> PathStorage; - StringRef P = Path.toNullTerminatedStringRef(PathStorage); + if (::access(P.begin(), convertAccessMode(Mode)) == -1) + return std::error_code(errno, std::generic_category()); - if (0 != access(P.begin(), R_OK | X_OK)) - return false; - struct stat buf; - if (0 != stat(P.begin(), &buf)) - return false; - if (!S_ISREG(buf.st_mode)) - return false; - return true; + if (Mode == AccessMode::Execute) { + // Don't say that directories are executable. + struct stat buf; + if (0 != stat(P.begin(), &buf)) + return errc::permission_denied; + if (!S_ISREG(buf.st_mode)) + return errc::permission_denied; + } + + return std::error_code(); } bool equivalent(file_status A, file_status B) { @@ -678,6 +646,66 @@ bool home_directory(SmallVectorImpl<char> &result) { return false; } +static const char *getEnvTempDir() { + // Check whether the temporary directory is specified by an environment + // variable. + const char *EnvironmentVariables[] = {"TMPDIR", "TMP", "TEMP", "TEMPDIR"}; + for (const char *Env : EnvironmentVariables) { + if (const char *Dir = std::getenv(Env)) + return Dir; + } + + return nullptr; +} + +static const char *getDefaultTempDir(bool ErasedOnReboot) { +#ifdef P_tmpdir + if ((bool)P_tmpdir) + return P_tmpdir; +#endif + + if (ErasedOnReboot) + return "/tmp"; + return "/var/tmp"; +} + +void system_temp_directory(bool ErasedOnReboot, SmallVectorImpl<char> &Result) { + Result.clear(); + + if (ErasedOnReboot) { + // There is no env variable for the cache directory. + if (const char *RequestedDir = getEnvTempDir()) { + Result.append(RequestedDir, RequestedDir + strlen(RequestedDir)); + return; + } + } + +#if defined(_CS_DARWIN_USER_TEMP_DIR) && defined(_CS_DARWIN_USER_CACHE_DIR) + // On Darwin, use DARWIN_USER_TEMP_DIR or DARWIN_USER_CACHE_DIR. + // macros defined in <unistd.h> on darwin >= 9 + int ConfName = ErasedOnReboot? _CS_DARWIN_USER_TEMP_DIR + : _CS_DARWIN_USER_CACHE_DIR; + size_t ConfLen = confstr(ConfName, nullptr, 0); + if (ConfLen > 0) { + do { + Result.resize(ConfLen); + ConfLen = confstr(ConfName, Result.data(), Result.size()); + } while (ConfLen > 0 && ConfLen != Result.size()); + + if (ConfLen > 0) { + assert(Result.back() == 0); + Result.pop_back(); + return; + } + + Result.clear(); + } +#endif + + const char *RequestedDir = getDefaultTempDir(ErasedOnReboot); + Result.append(RequestedDir, RequestedDir + strlen(RequestedDir)); +} + } // end namespace path } // end namespace sys diff --git a/lib/Support/Unix/Process.inc b/lib/Support/Unix/Process.inc index d2c5dbc..a429bb3 100644 --- a/lib/Support/Unix/Process.inc +++ b/lib/Support/Unix/Process.inc @@ -14,15 +14,25 @@ #include "Unix.h" #include "llvm/ADT/Hashing.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/ManagedStatic.h" #include "llvm/Support/Mutex.h" #include "llvm/Support/MutexGuard.h" #include "llvm/Support/TimeValue.h" +#if HAVE_FCNTL_H +#include <fcntl.h> +#endif #ifdef HAVE_SYS_TIME_H #include <sys/time.h> #endif #ifdef HAVE_SYS_RESOURCE_H #include <sys/resource.h> #endif +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#if HAVE_SIGNAL_H +#include <signal.h> +#endif // DragonFlyBSD, OpenBSD, and Bitrig have deprecated <malloc.h> for // <stdlib.h> instead. Unix.h includes this for us already. #if defined(HAVE_MALLOC_H) && !defined(__DragonFly__) && \ @@ -198,6 +208,97 @@ Process::GetArgumentVector(SmallVectorImpl<const char *> &ArgsOut, return std::error_code(); } +namespace { +class FDCloser { +public: + FDCloser(int &FD) : FD(FD), KeepOpen(false) {} + void keepOpen() { KeepOpen = true; } + ~FDCloser() { + if (!KeepOpen && FD >= 0) + ::close(FD); + } + +private: + FDCloser(const FDCloser &) LLVM_DELETED_FUNCTION; + void operator=(const FDCloser &) LLVM_DELETED_FUNCTION; + + int &FD; + bool KeepOpen; +}; +} + +std::error_code Process::FixupStandardFileDescriptors() { + int NullFD = -1; + FDCloser FDC(NullFD); + const int StandardFDs[] = {STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO}; + for (int StandardFD : StandardFDs) { + struct stat st; + errno = 0; + while (fstat(StandardFD, &st) < 0) { + assert(errno && "expected errno to be set if fstat failed!"); + // fstat should return EBADF if the file descriptor is closed. + if (errno == EBADF) + break; + // retry fstat if we got EINTR, otherwise bubble up the failure. + if (errno != EINTR) + return std::error_code(errno, std::generic_category()); + } + // if fstat succeeds, move on to the next FD. + if (!errno) + continue; + assert(errno == EBADF && "expected errno to have EBADF at this point!"); + + if (NullFD < 0) { + while ((NullFD = open("/dev/null", O_RDWR)) < 0) { + if (errno == EINTR) + continue; + return std::error_code(errno, std::generic_category()); + } + } + + if (NullFD == StandardFD) + FDC.keepOpen(); + else if (dup2(NullFD, StandardFD) < 0) + return std::error_code(errno, std::generic_category()); + } + return std::error_code(); +} + +std::error_code Process::SafelyCloseFileDescriptor(int FD) { + // Create a signal set filled with *all* signals. + sigset_t FullSet; + if (sigfillset(&FullSet) < 0) + return std::error_code(errno, std::generic_category()); + // Atomically swap our current signal mask with a full mask. + sigset_t SavedSet; +#if LLVM_ENABLE_THREADS + if (int EC = pthread_sigmask(SIG_SETMASK, &FullSet, &SavedSet)) + return std::error_code(EC, std::generic_category()); +#else + if (sigprocmask(SIG_SETMASK, &FullSet, &SavedSet) < 0) + return std::error_code(errno, std::generic_category()); +#endif + // Attempt to close the file descriptor. + // We need to save the error, if one occurs, because our subsequent call to + // pthread_sigmask might tamper with errno. + int ErrnoFromClose = 0; + if (::close(FD) < 0) + ErrnoFromClose = errno; + // Restore the signal mask back to what we saved earlier. + int EC = 0; +#if LLVM_ENABLE_THREADS + EC = pthread_sigmask(SIG_SETMASK, &SavedSet, nullptr); +#else + if (sigprocmask(SIG_SETMASK, &SavedSet, nullptr) < 0) + EC = errno; +#endif + // The error code from close takes precedence over the one from + // pthread_sigmask. + if (ErrnoFromClose) + return std::error_code(ErrnoFromClose, std::generic_category()); + return std::error_code(EC, std::generic_category()); +} + bool Process::StandardInIsUserInput() { return FileDescriptorIsDisplayed(STDIN_FILENO); } @@ -263,11 +364,14 @@ extern "C" int del_curterm(struct term *termp); extern "C" int tigetnum(char *capname); #endif +#ifdef HAVE_TERMINFO +static ManagedStatic<sys::Mutex> TermColorMutex; +#endif + static bool terminalHasColors(int fd) { #ifdef HAVE_TERMINFO // First, acquire a global lock because these C routines are thread hostile. - static sys::Mutex M; - MutexGuard G(M); + MutexGuard G(*TermColorMutex); int errret = 0; if (setupterm((char *)nullptr, fd, &errret) != 0) diff --git a/lib/Support/Unix/Program.inc b/lib/Support/Unix/Program.inc index 06a33cd..0670ad3 100644 --- a/lib/Support/Unix/Program.inc +++ b/lib/Support/Unix/Program.inc @@ -17,8 +17,10 @@ //===----------------------------------------------------------------------===// #include "Unix.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/raw_ostream.h" #include <llvm/Config/config.h> #if HAVE_SYS_STAT_H #include <sys/stat.h> @@ -53,50 +55,31 @@ using namespace sys; ProcessInfo::ProcessInfo() : Pid(0), ReturnCode(0) {} -// This function just uses the PATH environment variable to find the program. -std::string -sys::FindProgramByName(const std::string& progName) { - - // Check some degenerate cases - if (progName.length() == 0) // no program - return ""; - std::string temp = progName; +ErrorOr<std::string> sys::findProgramByName(StringRef Name, + ArrayRef<StringRef> Paths) { + assert(!Name.empty() && "Must have a name!"); // Use the given path verbatim if it contains any slashes; this matches // the behavior of sh(1) and friends. - if (progName.find('/') != std::string::npos) - return temp; - - // At this point, the file name is valid and does not contain slashes. Search - // for it through the directories specified in the PATH environment variable. + if (Name.find('/') != StringRef::npos) + return std::string(Name); - // Get the path. If its empty, we can't do anything to find it. - const char *PathStr = getenv("PATH"); - if (!PathStr) - return ""; + if (Paths.empty()) { + SmallVector<StringRef, 16> SearchPaths; + SplitString(std::getenv("PATH"), SearchPaths, ":"); + return findProgramByName(Name, SearchPaths); + } - // Now we have a colon separated list of directories to search; try them. - size_t PathLen = strlen(PathStr); - while (PathLen) { - // Find the first colon... - const char *Colon = std::find(PathStr, PathStr+PathLen, ':'); + for (auto Path : Paths) { + if (Path.empty()) + continue; // Check to see if this first directory contains the executable... - SmallString<128> FilePath(PathStr,Colon); - sys::path::append(FilePath, progName); - if (sys::fs::can_execute(Twine(FilePath))) - return FilePath.str(); // Found the executable! - - // Nope it wasn't in this directory, check the next path in the list! - PathLen -= Colon-PathStr; - PathStr = Colon; - - // Advance past duplicate colons - while (*PathStr == ':') { - PathStr++; - PathLen--; - } + SmallString<128> FilePath(Path); + sys::path::append(FilePath, Name); + if (sys::fs::can_execute(FilePath.c_str())) + return std::string(FilePath.str()); // Found the executable! } - return ""; + return std::errc::no_such_file_or_directory; } static bool RedirectIO(const StringRef *Path, int FD, std::string* ErrMsg) { @@ -334,7 +317,6 @@ ProcessInfo sys::Wait(const ProcessInfo &PI, unsigned SecondsToWait, pid_t ChildPid = PI.Pid; if (WaitUntilTerminates) { SecondsToWait = 0; - ChildPid = -1; // mimic a wait() using waitpid() } else if (SecondsToWait) { // Install a timeout handler. The handler itself does nothing, but the // simple fact of having a handler at all causes the wait below to return @@ -440,6 +422,23 @@ ProcessInfo sys::Wait(const ProcessInfo &PI, unsigned SecondsToWait, return std::error_code(); } +std::error_code +llvm::sys::writeFileWithEncoding(StringRef FileName, StringRef Contents, + WindowsEncodingMethod Encoding /*unused*/) { + std::error_code EC; + llvm::raw_fd_ostream OS(FileName, EC, llvm::sys::fs::OpenFlags::F_Text); + + if (EC) + return EC; + + OS << Contents; + + if (OS.has_error()) + return std::make_error_code(std::errc::io_error); + + return EC; +} + bool llvm::sys::argumentsFitWithinSystemLimits(ArrayRef<const char*> Args) { static long ArgMax = sysconf(_SC_ARG_MAX); @@ -448,13 +447,13 @@ bool llvm::sys::argumentsFitWithinSystemLimits(ArrayRef<const char*> Args) { return true; // Conservatively account for space required by environment variables. - ArgMax /= 2; + long HalfArgMax = ArgMax / 2; size_t ArgLength = 0; for (ArrayRef<const char*>::iterator I = Args.begin(), E = Args.end(); I != E; ++I) { ArgLength += strlen(*I) + 1; - if (ArgLength > size_t(ArgMax)) { + if (ArgLength > size_t(HalfArgMax)) { return false; } } diff --git a/lib/Support/Unix/RWMutex.inc b/lib/Support/Unix/RWMutex.inc index edcbd52..85a1043 100644 --- a/lib/Support/Unix/RWMutex.inc +++ b/lib/Support/Unix/RWMutex.inc @@ -26,26 +26,26 @@ using namespace sys; // will therefore deadlock if a thread tries to acquire a read lock // multiple times. -RWMutexImpl::RWMutexImpl() : data_(new Mutex(false)) { } +RWMutexImpl::RWMutexImpl() : data_(new MutexImpl(false)) { } RWMutexImpl::~RWMutexImpl() { - delete static_cast<Mutex *>(data_); + delete static_cast<MutexImpl *>(data_); } bool RWMutexImpl::reader_acquire() { - return static_cast<Mutex *>(data_)->acquire(); + return static_cast<MutexImpl *>(data_)->acquire(); } bool RWMutexImpl::reader_release() { - return static_cast<Mutex *>(data_)->release(); + return static_cast<MutexImpl *>(data_)->release(); } bool RWMutexImpl::writer_acquire() { - return static_cast<Mutex *>(data_)->acquire(); + return static_cast<MutexImpl *>(data_)->acquire(); } bool RWMutexImpl::writer_release() { - return static_cast<Mutex *>(data_)->release(); + return static_cast<MutexImpl *>(data_)->release(); } } diff --git a/lib/Support/Unix/Signals.inc b/lib/Support/Unix/Signals.inc index 1841fea..e8f4643 100644 --- a/lib/Support/Unix/Signals.inc +++ b/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) { diff --git a/lib/Support/Unix/TimeValue.inc b/lib/Support/Unix/TimeValue.inc index 7d4acf7..042e0da 100644 --- a/lib/Support/Unix/TimeValue.inc +++ b/lib/Support/Unix/TimeValue.inc @@ -41,7 +41,7 @@ TimeValue TimeValue::now() { // errors concern the timezone parameter which we're passing in as 0. // In the unlikely case it does happen, just return MinTime, no error // message needed. - return MinTime; + return MinTime(); } return TimeValue( diff --git a/lib/Support/Unix/Unix.h b/lib/Support/Unix/Unix.h index ba688e3..e16a226 100644 --- a/lib/Support/Unix/Unix.h +++ b/lib/Support/Unix/Unix.h @@ -11,8 +11,8 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_SYSTEM_UNIX_UNIX_H -#define LLVM_SYSTEM_UNIX_UNIX_H +#ifndef LLVM_LIB_SUPPORT_UNIX_UNIX_H +#define LLVM_LIB_SUPPORT_UNIX_UNIX_H //===----------------------------------------------------------------------===// //=== WARNING: Implementation here must contain only generic UNIX code that |