diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2018-07-28 10:51:19 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2018-07-28 10:51:19 +0000 |
commit | eb11fae6d08f479c0799db45860a98af528fa6e7 (patch) | |
tree | 44d492a50c8c1a7eb8e2d17ea3360ec4d066f042 /lib/Support/Unix | |
parent | b8a2042aa938069e862750553db0e4d82d25822c (diff) |
Notes
Diffstat (limited to 'lib/Support/Unix')
-rw-r--r-- | lib/Support/Unix/Host.inc | 2 | ||||
-rw-r--r-- | lib/Support/Unix/Memory.inc | 12 | ||||
-rw-r--r-- | lib/Support/Unix/Path.inc | 175 | ||||
-rw-r--r-- | lib/Support/Unix/Process.inc | 31 | ||||
-rw-r--r-- | lib/Support/Unix/Program.inc | 68 | ||||
-rw-r--r-- | lib/Support/Unix/Signals.inc | 302 | ||||
-rw-r--r-- | lib/Support/Unix/ThreadLocal.inc | 2 | ||||
-rw-r--r-- | lib/Support/Unix/Threading.inc | 17 | ||||
-rw-r--r-- | lib/Support/Unix/Unix.h | 2 | ||||
-rw-r--r-- | lib/Support/Unix/Watchdog.inc | 2 |
10 files changed, 397 insertions, 216 deletions
diff --git a/lib/Support/Unix/Host.inc b/lib/Support/Unix/Host.inc index 5580e63893c6..b65f84bf4444 100644 --- a/lib/Support/Unix/Host.inc +++ b/lib/Support/Unix/Host.inc @@ -64,5 +64,5 @@ std::string sys::getDefaultTargetTriple() { TargetTripleString = EnvTriple; #endif - return Triple::normalize(TargetTripleString); + return TargetTripleString; } diff --git a/lib/Support/Unix/Memory.inc b/lib/Support/Unix/Memory.inc index 848548d18177..adbfff2f59a5 100644 --- a/lib/Support/Unix/Memory.inc +++ b/lib/Support/Unix/Memory.inc @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "Unix.h" +#include "llvm/Config/config.h" #include "llvm/Support/DataTypes.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Process.h" @@ -24,6 +25,10 @@ #include <mach/mach.h> #endif +#ifdef __Fuchsia__ +#include <zircon/syscalls.h> +#endif + #if defined(__mips__) # if defined(__OpenBSD__) # include <mips64/sysarch.h> @@ -32,7 +37,7 @@ # endif #endif -#ifdef __APPLE__ +#if defined(__APPLE__) extern "C" void sys_icache_invalidate(const void *Addr, size_t len); #else extern "C" void __clear_cache(void *, void*); @@ -205,6 +210,11 @@ void Memory::InvalidateInstructionCache(const void *Addr, sys_icache_invalidate(const_cast<void *>(Addr), Len); # endif +#elif defined(__Fuchsia__) + + zx_status_t Status = zx_cache_flush(Addr, Len, ZX_CACHE_FLUSH_INSN); + assert(Status == ZX_OK && "cannot invalidate instruction cache"); + #else # if (defined(__POWERPC__) || defined (__ppc__) || \ diff --git a/lib/Support/Unix/Path.inc b/lib/Support/Unix/Path.inc index 2ecb97316c87..7ad57d892ff1 100644 --- a/lib/Support/Unix/Path.inc +++ b/lib/Support/Unix/Path.inc @@ -31,23 +31,8 @@ #ifdef HAVE_SYS_MMAN_H #include <sys/mman.h> #endif -#if HAVE_DIRENT_H -# include <dirent.h> -# define NAMLEN(dirent) strlen((dirent)->d_name) -#else -# define dirent direct -# define NAMLEN(dirent) (dirent)->d_namlen -# if HAVE_SYS_NDIR_H -# include <sys/ndir.h> -# endif -# if HAVE_SYS_DIR_H -# include <sys/dir.h> -# endif -# if HAVE_NDIR_H -# include <ndir.h> -# endif -#endif +#include <dirent.h> #include <pwd.h> #ifdef __APPLE__ @@ -108,6 +93,9 @@ using namespace llvm; namespace llvm { namespace sys { namespace fs { + +const file_t kInvalidFile = -1; + #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || \ defined(__minix) || defined(__FreeBSD_kernel__) || defined(__linux__) || \ defined(__CYGWIN__) || defined(__DragonFly__) || defined(_AIX) @@ -380,6 +368,12 @@ static bool is_local_impl(struct STATVFS &Vfs) { #elif defined(__CYGWIN__) // Cygwin doesn't expose this information; would need to use Win32 API. return false; +#elif defined(__Fuchsia__) + // Fuchsia doesn't yet support remote filesystem mounts. + return true; +#elif defined(__HAIKU__) + // Haiku doesn't expose this information. + return false; #elif defined(__sun) // statvfs::f_basetype contains a null-terminated FSType name of the mounted target StringRef fstype(Vfs.f_basetype); @@ -530,7 +524,7 @@ static void expandTildeExpr(SmallVectorImpl<char> &Path) { } static std::error_code fillStatus(int StatRet, const struct stat &Status, - file_status &Result) { + file_status &Result) { if (StatRet != 0) { std::error_code ec(errno, std::generic_category()); if (ec == errc::no_such_file_or_directory) @@ -643,7 +637,8 @@ std::error_code mapped_file_region::init(int FD, uint64_t Offset, mapped_file_region::mapped_file_region(int fd, mapmode mode, size_t length, uint64_t offset, std::error_code &ec) - : Size(length), Mapping() { + : Size(length), Mapping(), Mode(mode) { + (void)Mode; ec = init(fd, offset, mode); if (ec) Mapping = nullptr; @@ -702,7 +697,7 @@ std::error_code detail::directory_iterator_increment(detail::DirIterState &it) { if (cur_dir == nullptr && errno != 0) { return std::error_code(errno, std::generic_category()); } else if (cur_dir != nullptr) { - StringRef name(cur_dir->d_name, NAMLEN(cur_dir)); + StringRef name(cur_dir->d_name); if ((name.size() == 1 && name[0] == '.') || (name.size() == 2 && name[0] == '.' && name[1] == '.')) return directory_iterator_increment(it); @@ -729,21 +724,83 @@ static bool hasProcSelfFD() { } #endif -std::error_code openFileForRead(const Twine &Name, int &ResultFD, - SmallVectorImpl<char> *RealPath) { - SmallString<128> Storage; - StringRef P = Name.toNullTerminatedStringRef(Storage); - int OpenFlags = O_RDONLY; +static int nativeOpenFlags(CreationDisposition Disp, OpenFlags Flags, + FileAccess Access) { + int Result = 0; + if (Access == FA_Read) + Result |= O_RDONLY; + else if (Access == FA_Write) + Result |= O_WRONLY; + else if (Access == (FA_Read | FA_Write)) + Result |= O_RDWR; + + // This is for compatibility with old code that assumed F_Append implied + // would open an existing file. See Windows/Path.inc for a longer comment. + if (Flags & F_Append) + Disp = CD_OpenAlways; + + if (Disp == CD_CreateNew) { + Result |= O_CREAT; // Create if it doesn't exist. + Result |= O_EXCL; // Fail if it does. + } else if (Disp == CD_CreateAlways) { + Result |= O_CREAT; // Create if it doesn't exist. + Result |= O_TRUNC; // Truncate if it does. + } else if (Disp == CD_OpenAlways) { + Result |= O_CREAT; // Create if it doesn't exist. + } else if (Disp == CD_OpenExisting) { + // Nothing special, just don't add O_CREAT and we get these semantics. + } + + if (Flags & F_Append) + Result |= O_APPEND; + #ifdef O_CLOEXEC - OpenFlags |= O_CLOEXEC; + if (!(Flags & OF_ChildInherit)) + Result |= O_CLOEXEC; #endif - if ((ResultFD = sys::RetryAfterSignal(-1, open, P.begin(), OpenFlags)) < 0) + + return Result; +} + +std::error_code openFile(const Twine &Name, int &ResultFD, + CreationDisposition Disp, FileAccess Access, + OpenFlags Flags, unsigned Mode) { + int OpenFlags = nativeOpenFlags(Disp, Flags, Access); + + SmallString<128> Storage; + StringRef P = Name.toNullTerminatedStringRef(Storage); + if ((ResultFD = sys::RetryAfterSignal(-1, ::open, P.begin(), OpenFlags, Mode)) < + 0) return std::error_code(errno, std::generic_category()); #ifndef O_CLOEXEC - int r = fcntl(ResultFD, F_SETFD, FD_CLOEXEC); - (void)r; - assert(r == 0 && "fcntl(F_SETFD, FD_CLOEXEC) failed"); + if (!(Flags & OF_ChildInherit)) { + int r = fcntl(ResultFD, F_SETFD, FD_CLOEXEC); + (void)r; + assert(r == 0 && "fcntl(F_SETFD, FD_CLOEXEC) failed"); + } #endif + return std::error_code(); +} + +Expected<int> openNativeFile(const Twine &Name, CreationDisposition Disp, + FileAccess Access, OpenFlags Flags, + unsigned Mode) { + + int FD; + std::error_code EC = openFile(Name, FD, Disp, Access, Flags, Mode); + if (EC) + return errorCodeToError(EC); + return FD; +} + +std::error_code openFileForRead(const Twine &Name, int &ResultFD, + OpenFlags Flags, + SmallVectorImpl<char> *RealPath) { + std::error_code EC = + openFile(Name, ResultFD, CD_OpenExisting, FA_Read, Flags, 0666); + if (EC) + return EC; + // Attempt to get the real name of the file, if the user asked if(!RealPath) return std::error_code(); @@ -763,6 +820,9 @@ std::error_code openFileForRead(const Twine &Name, int &ResultFD, if (CharCount > 0) RealPath->append(Buffer, Buffer + CharCount); } else { + SmallString<128> Storage; + StringRef P = Name.toNullTerminatedStringRef(Storage); + // Use ::realpath to get the real path name if (::realpath(P.begin(), Buffer) != nullptr) RealPath->append(Buffer, Buffer + strlen(Buffer)); @@ -771,41 +831,18 @@ std::error_code openFileForRead(const Twine &Name, int &ResultFD, return std::error_code(); } -std::error_code openFileForWrite(const Twine &Name, int &ResultFD, - sys::fs::OpenFlags Flags, unsigned Mode) { - // Verify that we don't have both "append" and "excl". - assert((!(Flags & sys::fs::F_Excl) || !(Flags & sys::fs::F_Append)) && - "Cannot specify both 'excl' and 'append' file creation flags!"); - - int OpenFlags = O_CREAT; - -#ifdef O_CLOEXEC - OpenFlags |= O_CLOEXEC; -#endif - - if (Flags & F_RW) - OpenFlags |= O_RDWR; - else - OpenFlags |= O_WRONLY; - - if (Flags & F_Append) - OpenFlags |= O_APPEND; - else - OpenFlags |= O_TRUNC; - - if (Flags & F_Excl) - OpenFlags |= O_EXCL; +Expected<file_t> openNativeFileForRead(const Twine &Name, OpenFlags Flags, + SmallVectorImpl<char> *RealPath) { + file_t ResultFD; + std::error_code EC = openFileForRead(Name, ResultFD, Flags, RealPath); + if (EC) + return errorCodeToError(EC); + return ResultFD; +} - SmallString<128> Storage; - StringRef P = Name.toNullTerminatedStringRef(Storage); - if ((ResultFD = sys::RetryAfterSignal(-1, open, P.begin(), OpenFlags, Mode)) < 0) - return std::error_code(errno, std::generic_category()); -#ifndef O_CLOEXEC - int r = fcntl(ResultFD, F_SETFD, FD_CLOEXEC); - (void)r; - assert(r == 0 && "fcntl(F_SETFD, FD_CLOEXEC) failed"); -#endif - return std::error_code(); +void closeFile(file_t &F) { + ::close(F); + F = kInvalidFile; } template <typename T> @@ -860,12 +897,12 @@ std::error_code real_path(const Twine &path, SmallVectorImpl<char> &dest, return real_path(Storage, dest, false); } - int fd; - std::error_code EC = openFileForRead(path, fd, &dest); - - if (EC) - return EC; - ::close(fd); + SmallString<128> Storage; + StringRef P = path.toNullTerminatedStringRef(Storage); + char Buffer[PATH_MAX]; + if (::realpath(P.begin(), Buffer) == nullptr) + return std::error_code(errno, std::generic_category()); + dest.append(Buffer, Buffer + strlen(Buffer)); return std::error_code(); } diff --git a/lib/Support/Unix/Process.inc b/lib/Support/Unix/Process.inc index e43650d707e3..fa515d44f3f2 100644 --- a/lib/Support/Unix/Process.inc +++ b/lib/Support/Unix/Process.inc @@ -14,6 +14,7 @@ #include "Unix.h" #include "llvm/ADT/Hashing.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Config/config.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/Mutex.h" #include "llvm/Support/MutexGuard.h" @@ -78,7 +79,7 @@ unsigned Process::getPageSize() { #elif defined(HAVE_SYSCONF) static long page_size = ::sysconf(_SC_PAGE_SIZE); #else -#warning Cannot get the page size on this machine +#error Cannot get the page size on this machine #endif return static_cast<unsigned>(page_size); } @@ -172,15 +173,6 @@ Optional<std::string> Process::GetEnv(StringRef Name) { return std::string(Val); } -std::error_code -Process::GetArgumentVector(SmallVectorImpl<const char *> &ArgsOut, - ArrayRef<const char *> ArgsIn, - SpecificBumpPtrAllocator<char> &) { - ArgsOut.append(ArgsIn.begin(), ArgsIn.end()); - - return std::error_code(); -} - namespace { class FDCloser { public: @@ -207,7 +199,7 @@ std::error_code Process::FixupStandardFileDescriptors() { for (int StandardFD : StandardFDs) { struct stat st; errno = 0; - if (RetryAfterSignal(-1, fstat, StandardFD, &st) < 0) { + if (RetryAfterSignal(-1, ::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) @@ -219,7 +211,7 @@ std::error_code Process::FixupStandardFileDescriptors() { assert(errno == EBADF && "expected errno to have EBADF at this point!"); if (NullFD < 0) { - if ((NullFD = RetryAfterSignal(-1, open, "/dev/null", O_RDWR)) < 0) + if ((NullFD = RetryAfterSignal(-1, ::open, "/dev/null", O_RDWR)) < 0) return std::error_code(errno, std::generic_category()); } @@ -369,6 +361,21 @@ static bool terminalHasColors(int fd) { // Return true if we found a color capabilities for the current terminal. if (HasColors) return true; +#else + // When the terminfo database is not available, check if the current terminal + // is one of terminals that are known to support ANSI color escape codes. + if (const char *TermStr = std::getenv("TERM")) { + return StringSwitch<bool>(TermStr) + .Case("ansi", true) + .Case("cygwin", true) + .Case("linux", true) + .StartsWith("screen", true) + .StartsWith("xterm", true) + .StartsWith("vt100", true) + .StartsWith("rxvt", true) + .EndsWith("color", true) + .Default(false); + } #endif // Otherwise, be conservative. diff --git a/lib/Support/Unix/Program.inc b/lib/Support/Unix/Program.inc index 4f791991f3e8..d0abc3763e82 100644 --- a/lib/Support/Unix/Program.inc +++ b/lib/Support/Unix/Program.inc @@ -23,6 +23,7 @@ #include "llvm/Support/Errc.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" +#include "llvm/Support/StringSaver.h" #include "llvm/Support/raw_ostream.h" #if HAVE_SYS_STAT_H #include <sys/stat.h> @@ -164,8 +165,18 @@ static void SetMemoryLimits(unsigned size) { } -static bool Execute(ProcessInfo &PI, StringRef Program, const char **Args, - const char **Envp, ArrayRef<Optional<StringRef>> Redirects, +static std::vector<const char *> +toNullTerminatedCStringArray(ArrayRef<StringRef> Strings, StringSaver &Saver) { + std::vector<const char *> Result; + for (StringRef S : Strings) + Result.push_back(Saver.save(S).data()); + Result.push_back(nullptr); + return Result; +} + +static bool Execute(ProcessInfo &PI, StringRef Program, + ArrayRef<StringRef> Args, Optional<ArrayRef<StringRef>> Env, + ArrayRef<Optional<StringRef>> Redirects, unsigned MemoryLimit, std::string *ErrMsg) { if (!llvm::sys::fs::exists(Program)) { if (ErrMsg) @@ -174,6 +185,18 @@ static bool Execute(ProcessInfo &PI, StringRef Program, const char **Args, return false; } + BumpPtrAllocator Allocator; + StringSaver Saver(Allocator); + std::vector<const char *> ArgVector, EnvVector; + const char **Argv = nullptr; + const char **Envp = nullptr; + ArgVector = toNullTerminatedCStringArray(Args, Saver); + Argv = ArgVector.data(); + if (Env) { + EnvVector = toNullTerminatedCStringArray(*Env, Saver); + Envp = EnvVector.data(); + } + // If this OS has posix_spawn and there is no memory limit being implied, use // posix_spawn. It is more efficient than fork/exec. #ifdef HAVE_POSIX_SPAWN @@ -227,7 +250,7 @@ static bool Execute(ProcessInfo &PI, StringRef Program, const char **Args, // positive. pid_t PID = 0; int Err = posix_spawn(&PID, Program.str().c_str(), FileActions, - /*attrp*/nullptr, const_cast<char **>(Args), + /*attrp*/ nullptr, const_cast<char **>(Argv), const_cast<char **>(Envp)); if (FileActions) @@ -237,6 +260,7 @@ static bool Execute(ProcessInfo &PI, StringRef Program, const char **Args, return !MakeErrMsg(ErrMsg, "posix_spawn failed", Err); PI.Pid = PID; + PI.Process = PID; return true; } @@ -279,12 +303,10 @@ static bool Execute(ProcessInfo &PI, StringRef Program, const char **Args, // Execute! std::string PathStr = Program; if (Envp != nullptr) - execve(PathStr.c_str(), - const_cast<char **>(Args), + execve(PathStr.c_str(), const_cast<char **>(Argv), const_cast<char **>(Envp)); else - execv(PathStr.c_str(), - const_cast<char **>(Args)); + execv(PathStr.c_str(), const_cast<char **>(Argv)); // If the execve() failed, we should exit. Follow Unix protocol and // return 127 if the executable was not found, and 126 otherwise. // Use _exit rather than exit so that atexit functions and static @@ -300,6 +322,7 @@ static bool Execute(ProcessInfo &PI, StringRef Program, const char **Args, } PI.Pid = child; + PI.Process = child; return true; } @@ -404,14 +427,14 @@ ProcessInfo sys::Wait(const ProcessInfo &PI, unsigned SecondsToWait, return WaitResult; } - std::error_code sys::ChangeStdinToBinary(){ +std::error_code sys::ChangeStdinToBinary() { // Do nothing, as Unix doesn't differentiate between text and binary. - return std::error_code(); + return std::error_code(); } - std::error_code sys::ChangeStdoutToBinary(){ +std::error_code sys::ChangeStdoutToBinary() { // Do nothing, as Unix doesn't differentiate between text and binary. - return std::error_code(); + return std::error_code(); } std::error_code @@ -432,29 +455,38 @@ llvm::sys::writeFileWithEncoding(StringRef FileName, StringRef Contents, } bool llvm::sys::commandLineFitsWithinSystemLimits(StringRef Program, - ArrayRef<const char *> Args) { + ArrayRef<StringRef> Args) { static long ArgMax = sysconf(_SC_ARG_MAX); + // POSIX requires that _POSIX_ARG_MAX is 4096, which is the lowest possible + // value for ARG_MAX on a POSIX compliant system. + static long ArgMin = _POSIX_ARG_MAX; + + // This the same baseline used by xargs. + long EffectiveArgMax = 128 * 1024; + + if (EffectiveArgMax > ArgMax) + EffectiveArgMax = ArgMax; + else if (EffectiveArgMax < ArgMin) + EffectiveArgMax = ArgMin; // System says no practical limit. if (ArgMax == -1) return true; // Conservatively account for space required by environment variables. - long HalfArgMax = ArgMax / 2; + long HalfArgMax = EffectiveArgMax / 2; size_t ArgLength = Program.size() + 1; - for (const char* Arg : Args) { - size_t length = strlen(Arg); - + for (StringRef Arg : Args) { // Ensure that we do not exceed the MAX_ARG_STRLEN constant on Linux, which // does not have a constant unlike what the man pages would have you // believe. Since this limit is pretty high, perform the check // unconditionally rather than trying to be aggressive and limiting it to // Linux only. - if (length >= (32 * 4096)) + if (Arg.size() >= (32 * 4096)) return false; - ArgLength += length + 1; + ArgLength += Arg.size() + 1; if (ArgLength > size_t(HalfArgMax)) { return false; } diff --git a/lib/Support/Unix/Signals.inc b/lib/Support/Unix/Signals.inc index aaf760c5b616..de26695d64ea 100644 --- a/lib/Support/Unix/Signals.inc +++ b/lib/Support/Unix/Signals.inc @@ -11,9 +11,31 @@ // Unix signals occurring while your program is running. // //===----------------------------------------------------------------------===// +// +// This file is extremely careful to only do signal-safe things while in a +// signal handler. In particular, memory allocation and acquiring a mutex +// while in a signal handler should never occur. ManagedStatic isn't usable from +// a signal handler for 2 reasons: +// +// 1. Creating a new one allocates. +// 2. The signal handler could fire while llvm_shutdown is being processed, in +// which case the ManagedStatic is in an unknown state because it could +// already have been destroyed, or be in the process of being destroyed. +// +// Modifying the behavior of the signal handlers (such as registering new ones) +// can acquire a mutex, but all this guarantees is that the signal handler +// behavior is only modified by one thread at a time. A signal handler can still +// fire while this occurs! +// +// Adding work to a signal handler requires lock-freedom (and assume atomics are +// always lock-free) because the signal handler could fire while new work is +// being added. +// +//===----------------------------------------------------------------------===// #include "Unix.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/Config/config.h" #include "llvm/Demangle/Demangle.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/FileUtilities.h" @@ -59,24 +81,133 @@ using namespace llvm; static RETSIGTYPE SignalHandler(int Sig); // defined below. -static ManagedStatic<sys::SmartMutex<true> > SignalsMutex; +/// The function to call if ctrl-c is pressed. +using InterruptFunctionType = void (*)(); +static std::atomic<InterruptFunctionType> InterruptFunction = + ATOMIC_VAR_INIT(nullptr); + +namespace { +/// Signal-safe removal of files. +/// Inserting and erasing from the list isn't signal-safe, but removal of files +/// themselves is signal-safe. Memory is freed when the head is freed, deletion +/// is therefore not signal-safe either. +class FileToRemoveList { + std::atomic<char *> Filename = ATOMIC_VAR_INIT(nullptr); + std::atomic<FileToRemoveList *> Next = ATOMIC_VAR_INIT(nullptr); + + FileToRemoveList() = default; + // Not signal-safe. + FileToRemoveList(const std::string &str) : Filename(strdup(str.c_str())) {} + +public: + // Not signal-safe. + ~FileToRemoveList() { + if (FileToRemoveList *N = Next.exchange(nullptr)) + delete N; + if (char *F = Filename.exchange(nullptr)) + free(F); + } + + // Not signal-safe. + static void insert(std::atomic<FileToRemoveList *> &Head, + const std::string &Filename) { + // Insert the new file at the end of the list. + FileToRemoveList *NewHead = new FileToRemoveList(Filename); + std::atomic<FileToRemoveList *> *InsertionPoint = &Head; + FileToRemoveList *OldHead = nullptr; + while (!InsertionPoint->compare_exchange_strong(OldHead, NewHead)) { + InsertionPoint = &OldHead->Next; + OldHead = nullptr; + } + } + + // Not signal-safe. + static void erase(std::atomic<FileToRemoveList *> &Head, + const std::string &Filename) { + // Use a lock to avoid concurrent erase: the comparison would access + // free'd memory. + static ManagedStatic<sys::SmartMutex<true>> Lock; + sys::SmartScopedLock<true> Writer(*Lock); + + for (FileToRemoveList *Current = Head.load(); Current; + Current = Current->Next.load()) { + if (char *OldFilename = Current->Filename.load()) { + if (OldFilename != Filename) + continue; + // Leave an empty filename. + OldFilename = Current->Filename.exchange(nullptr); + // The filename might have become null between the time we + // compared it and we exchanged it. + if (OldFilename) + free(OldFilename); + } + } + } -/// InterruptFunction - The function to call if ctrl-c is pressed. -static void (*InterruptFunction)() = nullptr; + // Signal-safe. + static void removeAllFiles(std::atomic<FileToRemoveList *> &Head) { + // If cleanup were to occur while we're removing files we'd have a bad time. + // Make sure we're OK by preventing cleanup from doing anything while we're + // removing files. If cleanup races with us and we win we'll have a leak, + // but we won't crash. + FileToRemoveList *OldHead = Head.exchange(nullptr); + + for (FileToRemoveList *currentFile = OldHead; currentFile; + currentFile = currentFile->Next.load()) { + // If erasing was occuring while we're trying to remove files we'd look + // at free'd data. Take away the path and put it back when done. + if (char *path = currentFile->Filename.exchange(nullptr)) { + // Get the status so we can determine if it's a file or directory. If we + // can't stat the file, ignore it. + struct stat buf; + if (stat(path, &buf) != 0) + continue; + + // If this is not a regular file, ignore it. We want to prevent removal + // of special files like /dev/null, even if the compiler is being run + // with the super-user permissions. + if (!S_ISREG(buf.st_mode)) + continue; + + // Otherwise, remove the file. We ignore any errors here as there is + // nothing else we can do. + unlink(path); + + // We're done removing the file, erasing can safely proceed. + currentFile->Filename.exchange(path); + } + } -static ManagedStatic<std::vector<std::string>> FilesToRemove; + // We're done removing files, cleanup can safely proceed. + Head.exchange(OldHead); + } +}; +static std::atomic<FileToRemoveList *> FilesToRemove = ATOMIC_VAR_INIT(nullptr); + +/// Clean up the list in a signal-friendly manner. +/// Recall that signals can fire during llvm_shutdown. If this occurs we should +/// either clean something up or nothing at all, but we shouldn't crash! +struct FilesToRemoveCleanup { + // Not signal-safe. + ~FilesToRemoveCleanup() { + FileToRemoveList *Head = FilesToRemove.exchange(nullptr); + if (Head) + delete Head; + } +}; +} // namespace static StringRef Argv0; -// IntSigs - Signals that represent requested termination. There's no bug -// or failure, or if there is, it's not our direct responsibility. For whatever -// reason, our continued execution is no longer desirable. +// Signals that represent requested termination. There's no bug or failure, or +// if there is, it's not our direct responsibility. For whatever reason, our +// continued execution is no longer desirable. static const int IntSigs[] = { SIGHUP, SIGINT, SIGPIPE, SIGTERM, SIGUSR1, SIGUSR2 }; -// KillSigs - Signals that represent that we have a bug, and our prompt -// termination has been ordered. +// Signals that represent that we have a bug, and our prompt termination has +// been ordered. static const int KillSigs[] = { SIGILL, SIGTRAP, SIGABRT, SIGFPE, SIGBUS, SIGSEGV, SIGQUIT #ifdef SIGSYS @@ -93,30 +224,12 @@ static const int KillSigs[] = { #endif }; -static unsigned NumRegisteredSignals = 0; +static std::atomic<unsigned> NumRegisteredSignals = ATOMIC_VAR_INIT(0); static struct { struct sigaction SA; int SigNo; } RegisteredSignalInfo[array_lengthof(IntSigs) + array_lengthof(KillSigs)]; - -static void RegisterHandler(int Signal) { - assert(NumRegisteredSignals < array_lengthof(RegisteredSignalInfo) && - "Out of space for signal handlers!"); - - struct sigaction NewHandler; - - NewHandler.sa_handler = SignalHandler; - NewHandler.sa_flags = SA_NODEFER | SA_RESETHAND | SA_ONSTACK; - sigemptyset(&NewHandler.sa_mask); - - // Install the new handler, save the old one in RegisteredSignalInfo. - sigaction(Signal, &NewHandler, - &RegisteredSignalInfo[NumRegisteredSignals].SA); - RegisteredSignalInfo[NumRegisteredSignals].SigNo = Signal; - ++NumRegisteredSignals; -} - #if defined(HAVE_SIGALTSTACK) // Hold onto both the old and new alternate signal stack so that it's not // reported as a leak. We don't make any attempt to remove our alt signal @@ -138,7 +251,7 @@ static void CreateSigAltStack() { return; stack_t AltStack = {}; - AltStack.ss_sp = reinterpret_cast<char *>(malloc(AltStackSize)); + AltStack.ss_sp = static_cast<char *>(safe_malloc(AltStackSize)); NewAltStackPointer = AltStack.ss_sp; // Save to avoid reporting a leak. AltStack.ss_size = AltStackSize; if (sigaltstack(&AltStack, &OldAltStack) != 0) @@ -148,64 +261,59 @@ static void CreateSigAltStack() { static void CreateSigAltStack() {} #endif -static void RegisterHandlers() { - sys::SmartScopedLock<true> Guard(*SignalsMutex); +static void RegisterHandlers() { // Not signal-safe. + // The mutex prevents other threads from registering handlers while we're + // doing it. We also have to protect the handlers and their count because + // a signal handler could fire while we're registeting handlers. + static ManagedStatic<sys::SmartMutex<true>> SignalHandlerRegistrationMutex; + sys::SmartScopedLock<true> Guard(*SignalHandlerRegistrationMutex); // If the handlers are already registered, we're done. - if (NumRegisteredSignals != 0) return; + if (NumRegisteredSignals.load() != 0) + return; // Create an alternate stack for signal handling. This is necessary for us to // be able to reliably handle signals due to stack overflow. CreateSigAltStack(); - for (auto S : IntSigs) RegisterHandler(S); - for (auto S : KillSigs) RegisterHandler(S); + auto registerHandler = [&](int Signal) { + unsigned Index = NumRegisteredSignals.load(); + assert(Index < array_lengthof(RegisteredSignalInfo) && + "Out of space for signal handlers!"); + + struct sigaction NewHandler; + + NewHandler.sa_handler = SignalHandler; + NewHandler.sa_flags = SA_NODEFER | SA_RESETHAND | SA_ONSTACK; + sigemptyset(&NewHandler.sa_mask); + + // Install the new handler, save the old one in RegisteredSignalInfo. + sigaction(Signal, &NewHandler, &RegisteredSignalInfo[Index].SA); + RegisteredSignalInfo[Index].SigNo = Signal; + ++NumRegisteredSignals; + }; + + for (auto S : IntSigs) + registerHandler(S); + for (auto S : KillSigs) + registerHandler(S); } static void UnregisterHandlers() { // Restore all of the signal handlers to how they were before we showed up. - for (unsigned i = 0, e = NumRegisteredSignals; i != e; ++i) + for (unsigned i = 0, e = NumRegisteredSignals.load(); i != e; ++i) { sigaction(RegisteredSignalInfo[i].SigNo, &RegisteredSignalInfo[i].SA, nullptr); - NumRegisteredSignals = 0; + --NumRegisteredSignals; + } } - -/// RemoveFilesToRemove - Process the FilesToRemove list. This function -/// should be called with the SignalsMutex lock held. -/// NB: This must be an async signal safe function. It cannot allocate or free -/// memory, even in debug builds. +/// Process the FilesToRemove list. static void RemoveFilesToRemove() { - // Avoid constructing ManagedStatic in the signal handler. - // If FilesToRemove is not constructed, there are no files to remove. - if (!FilesToRemove.isConstructed()) - return; - - // We avoid iterators in case of debug iterators that allocate or release - // memory. - std::vector<std::string>& FilesToRemoveRef = *FilesToRemove; - for (unsigned i = 0, e = FilesToRemoveRef.size(); i != e; ++i) { - 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. - struct stat buf; - if (stat(path, &buf) != 0) - continue; - - // If this is not a regular file, ignore it. We want to prevent removal of - // special files like /dev/null, even if the compiler is being run with the - // super-user permissions. - if (!S_ISREG(buf.st_mode)) - continue; - - // Otherwise, remove the file. We ignore any errors here as there is nothing - // else we can do. - unlink(path); - } + FileToRemoveList::removeAllFiles(FilesToRemove); } -// SignalHandler - The signal handler that runs. +// The signal handler that runs. static RETSIGTYPE SignalHandler(int Sig) { // Restore the signal behavior to default, so that the program actually // crashes when we return and the signal reissues. This also ensures that if @@ -219,20 +327,13 @@ static RETSIGTYPE SignalHandler(int Sig) { sigprocmask(SIG_UNBLOCK, &SigMask, nullptr); { - unique_lock<sys::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; - } + if (auto OldInterruptFunction = InterruptFunction.exchange(nullptr)) + return OldInterruptFunction(); - Guard.unlock(); raise(Sig); // Execute the default handler. return; } @@ -252,45 +353,36 @@ static RETSIGTYPE SignalHandler(int Sig) { } void llvm::sys::RunInterruptHandlers() { - sys::SmartScopedLock<true> Guard(*SignalsMutex); RemoveFilesToRemove(); } void llvm::sys::SetInterruptFunction(void (*IF)()) { - { - sys::SmartScopedLock<true> Guard(*SignalsMutex); - InterruptFunction = IF; - } + InterruptFunction.exchange(IF); RegisterHandlers(); } -// RemoveFileOnSignal - The public API +// The public API bool llvm::sys::RemoveFileOnSignal(StringRef Filename, std::string* ErrMsg) { - { - sys::SmartScopedLock<true> Guard(*SignalsMutex); - FilesToRemove->push_back(Filename); - } - + // Ensure that cleanup will occur as soon as one file is added. + static ManagedStatic<FilesToRemoveCleanup> FilesToRemoveCleanup; + *FilesToRemoveCleanup; + FileToRemoveList::insert(FilesToRemove, Filename.str()); RegisterHandlers(); return false; } -// DontRemoveFileOnSignal - The public API +// The public API void llvm::sys::DontRemoveFileOnSignal(StringRef Filename) { - sys::SmartScopedLock<true> Guard(*SignalsMutex); - std::vector<std::string>::reverse_iterator RI = - find(reverse(*FilesToRemove), Filename); - std::vector<std::string>::iterator I = FilesToRemove->end(); - if (RI != FilesToRemove->rend()) - I = FilesToRemove->erase(RI.base()-1); + FileToRemoveList::erase(FilesToRemove, Filename.str()); } -/// 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)); +/// 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(sys::SignalHandlerCallback FnPtr, + void *Cookie) { // Signal-safe. + insertSignalHandler(FnPtr, Cookie); RegisterHandlers(); } @@ -383,8 +475,8 @@ static int unwindBacktrace(void **StackTrace, int MaxEntries) { } #endif -// 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. +// 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. // // On glibc systems we have the 'backtrace' function, which works nicely, but // doesn't demangle symbols. @@ -463,8 +555,8 @@ static void PrintStackTraceSignalHandler(void *) { void llvm::sys::DisableSystemDialogsOnCrash() {} -/// PrintStackTraceOnErrorSignal - When an error signal (such as SIGABRT or -/// SIGSEGV) is delivered to the process, print a stack trace and then exit. +/// When an error signal (such as SIGABRT or SIGSEGV) is delivered to the +/// process, print a stack trace and then exit. void llvm::sys::PrintStackTraceOnErrorSignal(StringRef Argv0, bool DisableCrashReporting) { ::Argv0 = Argv0; diff --git a/lib/Support/Unix/ThreadLocal.inc b/lib/Support/Unix/ThreadLocal.inc index 31c3f3835b29..a6564f0fa281 100644 --- a/lib/Support/Unix/ThreadLocal.inc +++ b/lib/Support/Unix/ThreadLocal.inc @@ -16,6 +16,8 @@ //=== is guaranteed to work on *all* UNIX variants. //===----------------------------------------------------------------------===// +#include "llvm/Config/config.h" + #if defined(HAVE_PTHREAD_H) && defined(HAVE_PTHREAD_GETSPECIFIC) #include <cassert> diff --git a/lib/Support/Unix/Threading.inc b/lib/Support/Unix/Threading.inc index 7369cff8466c..2d49ce1ad747 100644 --- a/lib/Support/Unix/Threading.inc +++ b/lib/Support/Unix/Threading.inc @@ -21,8 +21,8 @@ #include <pthread.h> -#if defined(__FreeBSD__) -#include <pthread_np.h> // For pthread_getthreadid_np() +#if defined(__FreeBSD__) || defined(__OpenBSD__) +#include <pthread_np.h> // For pthread_getthreadid_np() / pthread_set_name_np() #endif #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) @@ -98,8 +98,6 @@ uint64_t llvm::get_threadid() { return uint64_t(gettid()); #elif defined(__linux__) return uint64_t(syscall(SYS_gettid)); -#elif defined(LLVM_ON_WIN32) - return uint64_t(::GetCurrentThreadId()); #else return uint64_t(pthread_self()); #endif @@ -119,6 +117,8 @@ static constexpr uint32_t get_max_thread_name_length_impl() { #endif #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) return 16; +#elif defined(__OpenBSD__) + return 32; #else return 0; #endif @@ -138,8 +138,9 @@ void llvm::set_thread_name(const Twine &Name) { // terminated, but additionally the end of a long thread name will usually // be more unique than the beginning, since a common pattern is for similar // threads to share a common prefix. + // Note that the name length includes the null terminator. if (get_max_thread_name_length() > 0) - NameStr = NameStr.take_back(get_max_thread_name_length()); + NameStr = NameStr.take_back(get_max_thread_name_length() - 1); (void)NameStr; #if defined(__linux__) #if (defined(__GLIBC__) && defined(_GNU_SOURCE)) || defined(__ANDROID__) @@ -147,7 +148,7 @@ void llvm::set_thread_name(const Twine &Name) { ::pthread_setname_np(::pthread_self(), NameStr.data()); #endif #endif -#elif defined(__FreeBSD__) +#elif defined(__FreeBSD__) || defined(__OpenBSD__) ::pthread_set_name_np(::pthread_self(), NameStr.data()); #elif defined(__NetBSD__) ::pthread_setname_np(::pthread_self(), "%s", @@ -175,7 +176,7 @@ void llvm::get_thread_name(SmallVectorImpl<char> &Name) { if (kp == nullptr || (error != 0 && errno == ENOMEM)) { // Add extra space in case threads are added before next call. len += sizeof(*kp) + len / 10; - nkp = (struct kinfo_proc *)realloc(kp, len); + nkp = (struct kinfo_proc *)::realloc(kp, len); if (nkp == nullptr) { free(kp); return; @@ -203,7 +204,6 @@ void llvm::get_thread_name(SmallVectorImpl<char> &Name) { Name.append(buf, buf + strlen(buf)); #elif defined(__linux__) -#if (defined(__GLIBC__) && defined(_GNU_SOURCE)) || defined(__ANDROID__) #if HAVE_PTHREAD_GETNAME_NP constexpr uint32_t len = get_max_thread_name_length_impl(); char Buffer[len] = {'\0'}; // FIXME: working around MSan false positive. @@ -211,5 +211,4 @@ void llvm::get_thread_name(SmallVectorImpl<char> &Name) { Name.append(Buffer, Buffer + strlen(Buffer)); #endif #endif -#endif } diff --git a/lib/Support/Unix/Unix.h b/lib/Support/Unix/Unix.h index 239a6d60aaef..0c5d4de556d5 100644 --- a/lib/Support/Unix/Unix.h +++ b/lib/Support/Unix/Unix.h @@ -56,7 +56,7 @@ /// This function builds an error message into \p ErrMsg using the \p prefix /// string and the Unix error number given by \p errnum. If errnum is -1, the /// default then the value of errno is used. -/// @brief Make an error message +/// Make an error message /// /// If the error number can be converted to a string, it will be /// separated from prefix by ": ". diff --git a/lib/Support/Unix/Watchdog.inc b/lib/Support/Unix/Watchdog.inc index 5d89c0e51b11..f4253391d952 100644 --- a/lib/Support/Unix/Watchdog.inc +++ b/lib/Support/Unix/Watchdog.inc @@ -11,6 +11,8 @@ // //===----------------------------------------------------------------------===// +#include "llvm/Config/config.h" + #ifdef HAVE_UNISTD_H #include <unistd.h> #endif |