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