X-Git-Url: http://plrg.eecs.uci.edu/git/?p=oota-llvm.git;a=blobdiff_plain;f=lib%2FSupport%2FUnix%2FProcess.inc;h=df13bd2217390b46c859872aa88b7db2a9f1949f;hp=2d7fd384e8bba0608ad65b9784b73ab116f78175;hb=a983427f67e227649ca3da4cd8f81cc41a56e155;hpb=c9413c745bec9707064211bc5fd02cdfb153c92d diff --git a/lib/Support/Unix/Process.inc b/lib/Support/Unix/Process.inc index 2d7fd384e8b..df13bd22173 100644 --- a/lib/Support/Unix/Process.inc +++ b/lib/Support/Unix/Process.inc @@ -12,17 +12,36 @@ //===----------------------------------------------------------------------===// #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 +#endif #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_SYS_RESOURCE_H #include #endif -// DragonFly BSD has deprecated for instead, -// Unix.h includes this for us already. -#if defined(HAVE_MALLOC_H) && !defined(__DragonFly__) +#ifdef HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SIGNAL_H +#include +#endif +// DragonFlyBSD, OpenBSD, and Bitrig have deprecated for +// instead. Unix.h includes this for us already. +#if defined(HAVE_MALLOC_H) && !defined(__DragonFly__) && \ + !defined(__OpenBSD__) && !defined(__Bitrig__) #include #endif +#if defined(HAVE_MALLCTL) +#include +#endif #ifdef HAVE_MALLOC_MALLOC_H #include #endif @@ -41,18 +60,32 @@ using namespace llvm; using namespace sys; -unsigned -Process::GetPageSize() -{ -#if defined(__CYGWIN__) - // On Cygwin, getpagesize() returns 64k but the page size for the purposes of - // memory protection and mmap() is 4k. - // See http://www.cygwin.com/ml/cygwin/2009-01/threads.html#00492 - const int page_size = 0x1000; -#elif defined(HAVE_GETPAGESIZE) - const int page_size = ::getpagesize(); +static std::pair getRUsageTimes() { +#if defined(HAVE_GETRUSAGE) + struct rusage RU; + ::getrusage(RUSAGE_SELF, &RU); + return std::make_pair( + TimeValue( + static_cast(RU.ru_utime.tv_sec), + static_cast( + RU.ru_utime.tv_usec * TimeValue::NANOSECONDS_PER_MICROSECOND)), + TimeValue( + static_cast(RU.ru_stime.tv_sec), + static_cast( + RU.ru_stime.tv_usec * TimeValue::NANOSECONDS_PER_MICROSECOND))); +#else +#warning Cannot get usage times on this platform + return std::make_pair(TimeValue(), TimeValue()); +#endif +} + +// On Cygwin, getpagesize() returns 64k(AllocationGranularity) and +// offset in mmap(3) should be aligned to the AllocationGranularity. +unsigned Process::getPageSize() { +#if defined(HAVE_GETPAGESIZE) + static const int page_size = ::getpagesize(); #elif defined(HAVE_SYSCONF) - long page_size = ::sysconf(_SC_PAGE_SIZE); + static long page_size = ::sysconf(_SC_PAGE_SIZE); #else #warning Cannot get the page size on this machine #endif @@ -68,6 +101,12 @@ size_t Process::GetMallocUsage() { malloc_statistics_t Stats; malloc_zone_statistics(malloc_default_zone(), &Stats); return Stats.size_in_use; // darwin +#elif defined(HAVE_MALLCTL) + size_t alloc, sz; + sz = sizeof(size_t); + if (mallctl("stats.allocated", &alloc, &sz, NULL, 0) == 0) + return alloc; + return 0; #elif defined(HAVE_SBRK) // Note this is only an approximation and more closely resembles // the value returned by mallinfo in the arena field. @@ -75,65 +114,17 @@ size_t Process::GetMallocUsage() { char *EndOfMemory = (char*)sbrk(0); if (EndOfMemory != ((char*)-1) && StartOfMemory != ((char*)-1)) return EndOfMemory - StartOfMemory; - else - return 0; -#else -#warning Cannot get malloc info on this platform return 0; -#endif -} - -size_t -Process::GetTotalMemoryUsage() -{ -#if defined(HAVE_MALLINFO) - struct mallinfo mi = ::mallinfo(); - return mi.uordblks + mi.hblkhd; -#elif defined(HAVE_MALLOC_ZONE_STATISTICS) && defined(HAVE_MALLOC_MALLOC_H) - malloc_statistics_t Stats; - malloc_zone_statistics(malloc_default_zone(), &Stats); - return Stats.size_allocated; // darwin -#elif defined(HAVE_GETRUSAGE) && !defined(__HAIKU__) - struct rusage usage; - ::getrusage(RUSAGE_SELF, &usage); - return usage.ru_maxrss; #else -#warning Cannot get total memory size on this platform +#warning Cannot get malloc info on this platform return 0; #endif } -void -Process::GetTimeUsage(TimeValue& elapsed, TimeValue& user_time, - TimeValue& sys_time) -{ +void Process::GetTimeUsage(TimeValue &elapsed, TimeValue &user_time, + TimeValue &sys_time) { elapsed = TimeValue::now(); -#if defined(HAVE_GETRUSAGE) - struct rusage usage; - ::getrusage(RUSAGE_SELF, &usage); - user_time = TimeValue( - static_cast( usage.ru_utime.tv_sec ), - static_cast( usage.ru_utime.tv_usec * - TimeValue::NANOSECONDS_PER_MICROSECOND ) ); - sys_time = TimeValue( - static_cast( usage.ru_stime.tv_sec ), - static_cast( usage.ru_stime.tv_usec * - TimeValue::NANOSECONDS_PER_MICROSECOND ) ); -#else -#warning Cannot get usage times on this platform - user_time.seconds(0); - user_time.microseconds(0); - sys_time.seconds(0); - sys_time.microseconds(0); -#endif -} - -int Process::GetCurrentUserId() { - return getuid(); -} - -int Process::GetCurrentGroupId() { - return getgid(); + std::tie(user_time, sys_time) = getRUsageTimes(); } #if defined(HAVE_MACH_MACH_H) && !defined(__GNU__) @@ -180,6 +171,114 @@ void Process::PreventCoreFiles() { #endif } +Optional Process::GetEnv(StringRef Name) { + std::string NameStr = Name.str(); + const char *Val = ::getenv(NameStr.c_str()); + if (!Val) + return None; + return std::string(Val); +} + +std::error_code +Process::GetArgumentVector(SmallVectorImpl &ArgsOut, + ArrayRef ArgsIn, + SpecificBumpPtrAllocator &) { + ArgsOut.append(ArgsIn.begin(), ArgsIn.end()); + + 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 &) = delete; + void operator=(const FDCloser &) = delete; + + 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); } @@ -235,28 +334,76 @@ unsigned Process::StandardErrColumns() { return getColumns(2); } -static bool terminalHasColors() { - if (const char *term = std::getenv("TERM")) { - // Most modern terminals support ANSI escape sequences for colors. - // We could check terminfo, or have a list of known terms that support - // colors, but that would be overkill. - // The user can always ask for no colors by setting TERM to dumb, or - // using a commandline flag. - return strcmp(term, "dumb") != 0; - } +#ifdef HAVE_TERMINFO +// We manually declare these extern functions because finding the correct +// headers from various terminfo, curses, or other sources is harder than +// writing their specs down. +extern "C" int setupterm(char *term, int filedes, int *errret); +extern "C" struct term *set_curterm(struct term *termp); +extern "C" int del_curterm(struct term *termp); +extern "C" int tigetnum(char *capname); +#endif + +#ifdef HAVE_TERMINFO +static ManagedStatic TermColorMutex; +#endif + +static bool terminalHasColors(int fd) { +#ifdef HAVE_TERMINFO + // First, acquire a global lock because these C routines are thread hostile. + MutexGuard G(*TermColorMutex); + + int errret = 0; + if (setupterm((char *)nullptr, fd, &errret) != 0) + // Regardless of why, if we can't get terminfo, we shouldn't try to print + // colors. + return false; + + // Test whether the terminal as set up supports color output. How to do this + // isn't entirely obvious. We can use the curses routine 'has_colors' but it + // would be nice to avoid a dependency on curses proper when we can make do + // with a minimal terminfo parsing library. Also, we don't really care whether + // the terminal supports the curses-specific color changing routines, merely + // if it will interpret ANSI color escape codes in a reasonable way. Thus, the + // strategy here is just to query the baseline colors capability and if it + // supports colors at all to assume it will translate the escape codes into + // whatever range of colors it does support. We can add more detailed tests + // here if users report them as necessary. + // + // The 'tigetnum' routine returns -2 or -1 on errors, and might return 0 if + // the terminfo says that no colors are supported. + bool HasColors = tigetnum(const_cast("colors")) > 0; + + // Now extract the structure allocated by setupterm and free its memory + // through a really silly dance. + struct term *termp = set_curterm((struct term *)nullptr); + (void)del_curterm(termp); // Drop any errors here. + + // Return true if we found a color capabilities for the current terminal. + if (HasColors) + return true; +#endif + + // Otherwise, be conservative. return false; } +bool Process::FileDescriptorHasColors(int fd) { + // A file descriptor has colors if it is displayed and the terminal has + // colors. + return FileDescriptorIsDisplayed(fd) && terminalHasColors(fd); +} + bool Process::StandardOutHasColors() { - if (!StandardOutIsDisplayed()) - return false; - return terminalHasColors(); + return FileDescriptorHasColors(STDOUT_FILENO); } bool Process::StandardErrHasColors() { - if (!StandardErrIsDisplayed()) - return false; - return terminalHasColors(); + return FileDescriptorHasColors(STDERR_FILENO); +} + +void Process::UseANSIEscapeCodes(bool /*enable*/) { + // No effect. } bool Process::ColorNeedsFlush() { @@ -264,24 +411,6 @@ bool Process::ColorNeedsFlush() { return false; } -#define COLOR(FGBG, CODE, BOLD) "\033[0;" BOLD FGBG CODE "m" - -#define ALLCOLORS(FGBG,BOLD) {\ - COLOR(FGBG, "0", BOLD),\ - COLOR(FGBG, "1", BOLD),\ - COLOR(FGBG, "2", BOLD),\ - COLOR(FGBG, "3", BOLD),\ - COLOR(FGBG, "4", BOLD),\ - COLOR(FGBG, "5", BOLD),\ - COLOR(FGBG, "6", BOLD),\ - COLOR(FGBG, "7", BOLD)\ - } - -static const char colorcodes[2][2][8][10] = { - { ALLCOLORS("3",""), ALLCOLORS("3","1;") }, - { ALLCOLORS("4",""), ALLCOLORS("4","1;") } -}; - const char *Process::OutputColor(char code, bool bold, bool bg) { return colorcodes[bg?1:0][bold?1:0][code&7]; } @@ -290,6 +419,40 @@ const char *Process::OutputBold(bool bg) { return "\033[1m"; } +const char *Process::OutputReverse() { + return "\033[7m"; +} + const char *Process::ResetColor() { return "\033[0m"; } + +#if !defined(HAVE_DECL_ARC4RANDOM) || !HAVE_DECL_ARC4RANDOM +static unsigned GetRandomNumberSeed() { + // Attempt to get the initial seed from /dev/urandom, if possible. + if (FILE *RandomSource = ::fopen("/dev/urandom", "r")) { + unsigned seed; + int count = ::fread((void *)&seed, sizeof(seed), 1, RandomSource); + ::fclose(RandomSource); + + // Return the seed if the read was successful. + if (count == 1) + return seed; + } + + // Otherwise, swizzle the current time and the process ID to form a reasonable + // seed. + TimeValue Now = TimeValue::now(); + return hash_combine(Now.seconds(), Now.nanoseconds(), ::getpid()); +} +#endif + +unsigned llvm::sys::Process::GetRandomNumber() { +#if defined(HAVE_DECL_ARC4RANDOM) && HAVE_DECL_ARC4RANDOM + return arc4random(); +#else + static int x = (::srand(GetRandomNumberSeed()), 0); + (void)x; + return ::rand(); +#endif +}