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