1//===- Unix/Process.cpp - Unix Process Implementation --------- -*- C++ -*-===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9// 10// This file provides the generic Unix implementation of the Process class. 11// 12//===----------------------------------------------------------------------===// 13 14#include "Unix.h" 15#include "llvm/ADT/Hashing.h" 16#include "llvm/Support/TimeValue.h" 17#ifdef HAVE_SYS_TIME_H 18#include <sys/time.h> 19#endif 20#ifdef HAVE_SYS_RESOURCE_H 21#include <sys/resource.h> 22#endif 23// DragonFly BSD has deprecated <malloc.h> for <stdlib.h> instead, 24// Unix.h includes this for us already. 25#if defined(HAVE_MALLOC_H) && !defined(__DragonFly__) 26#include <malloc.h> 27#endif 28#ifdef HAVE_MALLOC_MALLOC_H 29#include <malloc/malloc.h> 30#endif 31#ifdef HAVE_SYS_IOCTL_H 32# include <sys/ioctl.h> 33#endif 34#ifdef HAVE_TERMIOS_H 35# include <termios.h> 36#endif 37 38//===----------------------------------------------------------------------===// 39//=== WARNING: Implementation here must contain only generic UNIX code that 40//=== is guaranteed to work on *all* UNIX variants. 41//===----------------------------------------------------------------------===// 42 43using namespace llvm; 44using namespace sys; 45 46unsigned 47Process::GetPageSize() 48{ 49#if defined(__CYGWIN__) 50 // On Cygwin, getpagesize() returns 64k but the page size for the purposes of 51 // memory protection and mmap() is 4k. 52 // See http://www.cygwin.com/ml/cygwin/2009-01/threads.html#00492 53 const int page_size = 0x1000; 54#elif defined(HAVE_GETPAGESIZE) 55 const int page_size = ::getpagesize(); 56#elif defined(HAVE_SYSCONF) 57 long page_size = ::sysconf(_SC_PAGE_SIZE); 58#else 59#warning Cannot get the page size on this machine 60#endif 61 return static_cast<unsigned>(page_size); 62} 63 64size_t Process::GetMallocUsage() { 65#if defined(HAVE_MALLINFO) 66 struct mallinfo mi; 67 mi = ::mallinfo(); 68 return mi.uordblks; 69#elif defined(HAVE_MALLOC_ZONE_STATISTICS) && defined(HAVE_MALLOC_MALLOC_H) 70 malloc_statistics_t Stats; 71 malloc_zone_statistics(malloc_default_zone(), &Stats); 72 return Stats.size_in_use; // darwin 73#elif defined(HAVE_SBRK) 74 // Note this is only an approximation and more closely resembles 75 // the value returned by mallinfo in the arena field. 76 static char *StartOfMemory = reinterpret_cast<char*>(::sbrk(0)); 77 char *EndOfMemory = (char*)sbrk(0); 78 if (EndOfMemory != ((char*)-1) && StartOfMemory != ((char*)-1)) 79 return EndOfMemory - StartOfMemory; 80 else 81 return 0; 82#else 83#warning Cannot get malloc info on this platform 84 return 0; 85#endif 86} 87 88size_t 89Process::GetTotalMemoryUsage() 90{ 91#if defined(HAVE_MALLINFO) 92 struct mallinfo mi = ::mallinfo(); 93 return mi.uordblks + mi.hblkhd; 94#elif defined(HAVE_MALLOC_ZONE_STATISTICS) && defined(HAVE_MALLOC_MALLOC_H) 95 malloc_statistics_t Stats; 96 malloc_zone_statistics(malloc_default_zone(), &Stats); 97 return Stats.size_allocated; // darwin 98#elif defined(HAVE_GETRUSAGE) && !defined(__HAIKU__) 99 struct rusage usage; 100 ::getrusage(RUSAGE_SELF, &usage); 101 return usage.ru_maxrss; 102#else 103#warning Cannot get total memory size on this platform 104 return 0; 105#endif 106} 107 108void 109Process::GetTimeUsage(TimeValue& elapsed, TimeValue& user_time, 110 TimeValue& sys_time) 111{ 112 elapsed = TimeValue::now(); 113#if defined(HAVE_GETRUSAGE) 114 struct rusage usage; 115 ::getrusage(RUSAGE_SELF, &usage); 116 user_time = TimeValue( 117 static_cast<TimeValue::SecondsType>( usage.ru_utime.tv_sec ), 118 static_cast<TimeValue::NanoSecondsType>( usage.ru_utime.tv_usec * 119 TimeValue::NANOSECONDS_PER_MICROSECOND ) ); 120 sys_time = TimeValue( 121 static_cast<TimeValue::SecondsType>( usage.ru_stime.tv_sec ), 122 static_cast<TimeValue::NanoSecondsType>( usage.ru_stime.tv_usec * 123 TimeValue::NANOSECONDS_PER_MICROSECOND ) ); 124#else 125#warning Cannot get usage times on this platform 126 user_time.seconds(0); 127 user_time.microseconds(0); 128 sys_time.seconds(0); 129 sys_time.microseconds(0); 130#endif 131} 132 133int Process::GetCurrentUserId() { 134 return getuid(); 135} 136 137int Process::GetCurrentGroupId() { 138 return getgid(); 139} 140 141#if defined(HAVE_MACH_MACH_H) && !defined(__GNU__) 142#include <mach/mach.h> 143#endif 144 145// Some LLVM programs such as bugpoint produce core files as a normal part of 146// their operation. To prevent the disk from filling up, this function 147// does what's necessary to prevent their generation. 148void Process::PreventCoreFiles() { 149#if HAVE_SETRLIMIT 150 struct rlimit rlim; 151 rlim.rlim_cur = rlim.rlim_max = 0; 152 setrlimit(RLIMIT_CORE, &rlim); 153#endif 154 155#if defined(HAVE_MACH_MACH_H) && !defined(__GNU__) 156 // Disable crash reporting on Mac OS X 10.0-10.4 157 158 // get information about the original set of exception ports for the task 159 mach_msg_type_number_t Count = 0; 160 exception_mask_t OriginalMasks[EXC_TYPES_COUNT]; 161 exception_port_t OriginalPorts[EXC_TYPES_COUNT]; 162 exception_behavior_t OriginalBehaviors[EXC_TYPES_COUNT]; 163 thread_state_flavor_t OriginalFlavors[EXC_TYPES_COUNT]; 164 kern_return_t err = 165 task_get_exception_ports(mach_task_self(), EXC_MASK_ALL, OriginalMasks, 166 &Count, OriginalPorts, OriginalBehaviors, 167 OriginalFlavors); 168 if (err == KERN_SUCCESS) { 169 // replace each with MACH_PORT_NULL. 170 for (unsigned i = 0; i != Count; ++i) 171 task_set_exception_ports(mach_task_self(), OriginalMasks[i], 172 MACH_PORT_NULL, OriginalBehaviors[i], 173 OriginalFlavors[i]); 174 } 175 176 // Disable crash reporting on Mac OS X 10.5 177 signal(SIGABRT, _exit); 178 signal(SIGILL, _exit); 179 signal(SIGFPE, _exit); 180 signal(SIGSEGV, _exit); 181 signal(SIGBUS, _exit); 182#endif 183} 184 185bool Process::StandardInIsUserInput() { 186 return FileDescriptorIsDisplayed(STDIN_FILENO); 187} 188 189bool Process::StandardOutIsDisplayed() { 190 return FileDescriptorIsDisplayed(STDOUT_FILENO); 191} 192 193bool Process::StandardErrIsDisplayed() { 194 return FileDescriptorIsDisplayed(STDERR_FILENO); 195} 196 197bool Process::FileDescriptorIsDisplayed(int fd) { 198#if HAVE_ISATTY 199 return isatty(fd); 200#else 201 // If we don't have isatty, just return false. 202 return false; 203#endif 204} 205 206static unsigned getColumns(int FileID) { 207 // If COLUMNS is defined in the environment, wrap to that many columns. 208 if (const char *ColumnsStr = std::getenv("COLUMNS")) { 209 int Columns = std::atoi(ColumnsStr); 210 if (Columns > 0) 211 return Columns; 212 } 213 214 unsigned Columns = 0; 215 216#if defined(HAVE_SYS_IOCTL_H) && defined(HAVE_TERMIOS_H) 217 // Try to determine the width of the terminal. 218 struct winsize ws; 219 if (ioctl(FileID, TIOCGWINSZ, &ws) == 0) 220 Columns = ws.ws_col; 221#endif 222 223 return Columns; 224} 225 226unsigned Process::StandardOutColumns() { 227 if (!StandardOutIsDisplayed()) 228 return 0; 229 230 return getColumns(1); 231} 232 233unsigned Process::StandardErrColumns() { 234 if (!StandardErrIsDisplayed()) 235 return 0; 236 237 return getColumns(2); 238} 239 240static bool terminalHasColors() { 241 if (const char *term = std::getenv("TERM")) { 242 // Most modern terminals support ANSI escape sequences for colors. 243 // We could check terminfo, or have a list of known terms that support 244 // colors, but that would be overkill. 245 // The user can always ask for no colors by setting TERM to dumb, or 246 // using a commandline flag. 247 return strcmp(term, "dumb") != 0; 248 } 249 return false; 250} 251 252bool Process::StandardOutHasColors() { 253 if (!StandardOutIsDisplayed()) 254 return false; 255 return terminalHasColors(); 256} 257 258bool Process::StandardErrHasColors() { 259 if (!StandardErrIsDisplayed()) 260 return false; 261 return terminalHasColors(); 262} 263 264bool Process::ColorNeedsFlush() { 265 // No, we use ANSI escape sequences. 266 return false; 267} 268 269#define COLOR(FGBG, CODE, BOLD) "\033[0;" BOLD FGBG CODE "m" 270 271#define ALLCOLORS(FGBG,BOLD) {\ 272 COLOR(FGBG, "0", BOLD),\ 273 COLOR(FGBG, "1", BOLD),\ 274 COLOR(FGBG, "2", BOLD),\ 275 COLOR(FGBG, "3", BOLD),\ 276 COLOR(FGBG, "4", BOLD),\ 277 COLOR(FGBG, "5", BOLD),\ 278 COLOR(FGBG, "6", BOLD),\ 279 COLOR(FGBG, "7", BOLD)\ 280 } 281 282static const char colorcodes[2][2][8][10] = { 283 { ALLCOLORS("3",""), ALLCOLORS("3","1;") }, 284 { ALLCOLORS("4",""), ALLCOLORS("4","1;") } 285}; 286 287const char *Process::OutputColor(char code, bool bold, bool bg) { 288 return colorcodes[bg?1:0][bold?1:0][code&7]; 289} 290 291const char *Process::OutputBold(bool bg) { 292 return "\033[1m"; 293} 294 295const char *Process::OutputReverse() { 296 return "\033[7m"; 297} 298 299const char *Process::ResetColor() { 300 return "\033[0m"; 301} 302 303#if !defined(HAVE_ARC4RANDOM) 304static unsigned GetRandomNumberSeed() { 305 // Attempt to get the initial seed from /dev/urandom, if possible. 306 if (FILE *RandomSource = ::fopen("/dev/urandom", "r")) { 307 unsigned seed; 308 int count = ::fread((void *)&seed, sizeof(seed), 1, RandomSource); 309 ::fclose(RandomSource); 310 311 // Return the seed if the read was successful. 312 if (count == 1) 313 return seed; 314 } 315 316 // Otherwise, swizzle the current time and the process ID to form a reasonable 317 // seed. 318 TimeValue Now = llvm::TimeValue::now(); 319 return hash_combine(Now.seconds(), Now.nanoseconds(), ::getpid()); 320} 321#endif 322 323unsigned llvm::sys::Process::GetRandomNumber() { 324#if defined(HAVE_ARC4RANDOM) 325 return arc4random(); 326#else 327 static int x = (::srand(GetRandomNumberSeed()), 0); 328 (void)x; 329 return ::rand(); 330#endif 331} 332