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