1218885Sdim//===- Unix/Process.cpp - Unix Process Implementation --------- -*- C++ -*-===//
2218885Sdim//
3218885Sdim//                     The LLVM Compiler Infrastructure
4218885Sdim//
5218885Sdim// This file is distributed under the University of Illinois Open Source
6218885Sdim// License. See LICENSE.TXT for details.
7218885Sdim//
8218885Sdim//===----------------------------------------------------------------------===//
9218885Sdim//
10218885Sdim// This file provides the generic Unix implementation of the Process class.
11218885Sdim//
12218885Sdim//===----------------------------------------------------------------------===//
13218885Sdim
14218885Sdim#include "Unix.h"
15239462Sdim#include "llvm/ADT/Hashing.h"
16239462Sdim#include "llvm/Support/TimeValue.h"
17218885Sdim#ifdef HAVE_SYS_TIME_H
18218885Sdim#include <sys/time.h>
19218885Sdim#endif
20218885Sdim#ifdef HAVE_SYS_RESOURCE_H
21218885Sdim#include <sys/resource.h>
22218885Sdim#endif
23239462Sdim// DragonFlyBSD, OpenBSD, and Bitrig have deprecated <malloc.h> for
24239462Sdim// <stdlib.h> instead. Unix.h includes this for us already.
25239462Sdim#if defined(HAVE_MALLOC_H) && !defined(__DragonFly__) && \
26239462Sdim    !defined(__OpenBSD__) && !defined(__Bitrig__)
27218885Sdim#include <malloc.h>
28218885Sdim#endif
29218885Sdim#ifdef HAVE_MALLOC_MALLOC_H
30218885Sdim#include <malloc/malloc.h>
31218885Sdim#endif
32218885Sdim#ifdef HAVE_SYS_IOCTL_H
33218885Sdim#  include <sys/ioctl.h>
34218885Sdim#endif
35218885Sdim#ifdef HAVE_TERMIOS_H
36218885Sdim#  include <termios.h>
37218885Sdim#endif
38218885Sdim
39218885Sdim//===----------------------------------------------------------------------===//
40218885Sdim//=== WARNING: Implementation here must contain only generic UNIX code that
41218885Sdim//===          is guaranteed to work on *all* UNIX variants.
42218885Sdim//===----------------------------------------------------------------------===//
43218885Sdim
44218885Sdimusing namespace llvm;
45218885Sdimusing namespace sys;
46218885Sdim
47249423Sdim
48249423Sdimprocess::id_type self_process::get_id() {
49249423Sdim  return getpid();
50249423Sdim}
51249423Sdim
52249423Sdimstatic std::pair<TimeValue, TimeValue> getRUsageTimes() {
53249423Sdim#if defined(HAVE_GETRUSAGE)
54249423Sdim  struct rusage RU;
55249423Sdim  ::getrusage(RUSAGE_SELF, &RU);
56249423Sdim  return std::make_pair(
57249423Sdim      TimeValue(
58249423Sdim          static_cast<TimeValue::SecondsType>(RU.ru_utime.tv_sec),
59249423Sdim          static_cast<TimeValue::NanoSecondsType>(
60249423Sdim              RU.ru_utime.tv_usec * TimeValue::NANOSECONDS_PER_MICROSECOND)),
61249423Sdim      TimeValue(
62249423Sdim          static_cast<TimeValue::SecondsType>(RU.ru_stime.tv_sec),
63249423Sdim          static_cast<TimeValue::NanoSecondsType>(
64249423Sdim              RU.ru_stime.tv_usec * TimeValue::NANOSECONDS_PER_MICROSECOND)));
65249423Sdim#else
66249423Sdim#warning Cannot get usage times on this platform
67249423Sdim  return std::make_pair(TimeValue(), TimeValue());
68249423Sdim#endif
69249423Sdim}
70249423Sdim
71249423SdimTimeValue self_process::get_user_time() const {
72249423Sdim#if _POSIX_TIMERS > 0 && _POSIX_CPUTIME > 0
73249423Sdim  // Try to get a high resolution CPU timer.
74249423Sdim  struct timespec TS;
75249423Sdim  if (::clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &TS) == 0)
76249423Sdim    return TimeValue(static_cast<TimeValue::SecondsType>(TS.tv_sec),
77249423Sdim                     static_cast<TimeValue::NanoSecondsType>(TS.tv_nsec));
78249423Sdim#endif
79249423Sdim
80249423Sdim  // Otherwise fall back to rusage based timing.
81249423Sdim  return getRUsageTimes().first;
82249423Sdim}
83249423Sdim
84249423SdimTimeValue self_process::get_system_time() const {
85249423Sdim  // We can only collect system time by inspecting the results of getrusage.
86249423Sdim  return getRUsageTimes().second;
87249423Sdim}
88249423Sdim
89249423Sdimstatic unsigned getPageSize() {
90218885Sdim#if defined(__CYGWIN__)
91218885Sdim  // On Cygwin, getpagesize() returns 64k but the page size for the purposes of
92218885Sdim  // memory protection and mmap() is 4k.
93218885Sdim  // See http://www.cygwin.com/ml/cygwin/2009-01/threads.html#00492
94218885Sdim  const int page_size = 0x1000;
95218885Sdim#elif defined(HAVE_GETPAGESIZE)
96218885Sdim  const int page_size = ::getpagesize();
97218885Sdim#elif defined(HAVE_SYSCONF)
98218885Sdim  long page_size = ::sysconf(_SC_PAGE_SIZE);
99218885Sdim#else
100218885Sdim#warning Cannot get the page size on this machine
101218885Sdim#endif
102218885Sdim  return static_cast<unsigned>(page_size);
103218885Sdim}
104218885Sdim
105249423Sdim// This constructor guaranteed to be run exactly once on a single thread, and
106249423Sdim// sets up various process invariants that can be queried cheaply from then on.
107249423Sdimself_process::self_process() : PageSize(getPageSize()) {
108249423Sdim}
109249423Sdim
110249423Sdim
111218885Sdimsize_t Process::GetMallocUsage() {
112218885Sdim#if defined(HAVE_MALLINFO)
113218885Sdim  struct mallinfo mi;
114218885Sdim  mi = ::mallinfo();
115218885Sdim  return mi.uordblks;
116218885Sdim#elif defined(HAVE_MALLOC_ZONE_STATISTICS) && defined(HAVE_MALLOC_MALLOC_H)
117218885Sdim  malloc_statistics_t Stats;
118218885Sdim  malloc_zone_statistics(malloc_default_zone(), &Stats);
119218885Sdim  return Stats.size_in_use;   // darwin
120218885Sdim#elif defined(HAVE_SBRK)
121218885Sdim  // Note this is only an approximation and more closely resembles
122218885Sdim  // the value returned by mallinfo in the arena field.
123218885Sdim  static char *StartOfMemory = reinterpret_cast<char*>(::sbrk(0));
124218885Sdim  char *EndOfMemory = (char*)sbrk(0);
125218885Sdim  if (EndOfMemory != ((char*)-1) && StartOfMemory != ((char*)-1))
126218885Sdim    return EndOfMemory - StartOfMemory;
127218885Sdim  else
128218885Sdim    return 0;
129218885Sdim#else
130218885Sdim#warning Cannot get malloc info on this platform
131218885Sdim  return 0;
132218885Sdim#endif
133218885Sdim}
134218885Sdim
135249423Sdimvoid Process::GetTimeUsage(TimeValue &elapsed, TimeValue &user_time,
136249423Sdim                           TimeValue &sys_time) {
137218885Sdim  elapsed = TimeValue::now();
138249423Sdim  llvm::tie(user_time, sys_time) = getRUsageTimes();
139218885Sdim}
140218885Sdim
141218885Sdimint Process::GetCurrentUserId() {
142218885Sdim  return getuid();
143218885Sdim}
144218885Sdim
145218885Sdimint Process::GetCurrentGroupId() {
146218885Sdim  return getgid();
147218885Sdim}
148218885Sdim
149234353Sdim#if defined(HAVE_MACH_MACH_H) && !defined(__GNU__)
150218885Sdim#include <mach/mach.h>
151218885Sdim#endif
152218885Sdim
153218885Sdim// Some LLVM programs such as bugpoint produce core files as a normal part of
154218885Sdim// their operation. To prevent the disk from filling up, this function
155218885Sdim// does what's necessary to prevent their generation.
156218885Sdimvoid Process::PreventCoreFiles() {
157218885Sdim#if HAVE_SETRLIMIT
158218885Sdim  struct rlimit rlim;
159218885Sdim  rlim.rlim_cur = rlim.rlim_max = 0;
160218885Sdim  setrlimit(RLIMIT_CORE, &rlim);
161218885Sdim#endif
162218885Sdim
163234353Sdim#if defined(HAVE_MACH_MACH_H) && !defined(__GNU__)
164218885Sdim  // Disable crash reporting on Mac OS X 10.0-10.4
165218885Sdim
166218885Sdim  // get information about the original set of exception ports for the task
167218885Sdim  mach_msg_type_number_t Count = 0;
168218885Sdim  exception_mask_t OriginalMasks[EXC_TYPES_COUNT];
169218885Sdim  exception_port_t OriginalPorts[EXC_TYPES_COUNT];
170218885Sdim  exception_behavior_t OriginalBehaviors[EXC_TYPES_COUNT];
171218885Sdim  thread_state_flavor_t OriginalFlavors[EXC_TYPES_COUNT];
172218885Sdim  kern_return_t err =
173218885Sdim    task_get_exception_ports(mach_task_self(), EXC_MASK_ALL, OriginalMasks,
174218885Sdim                             &Count, OriginalPorts, OriginalBehaviors,
175218885Sdim                             OriginalFlavors);
176218885Sdim  if (err == KERN_SUCCESS) {
177218885Sdim    // replace each with MACH_PORT_NULL.
178218885Sdim    for (unsigned i = 0; i != Count; ++i)
179218885Sdim      task_set_exception_ports(mach_task_self(), OriginalMasks[i],
180218885Sdim                               MACH_PORT_NULL, OriginalBehaviors[i],
181218885Sdim                               OriginalFlavors[i]);
182218885Sdim  }
183218885Sdim
184218885Sdim  // Disable crash reporting on Mac OS X 10.5
185218885Sdim  signal(SIGABRT, _exit);
186218885Sdim  signal(SIGILL,  _exit);
187218885Sdim  signal(SIGFPE,  _exit);
188218885Sdim  signal(SIGSEGV, _exit);
189218885Sdim  signal(SIGBUS,  _exit);
190218885Sdim#endif
191218885Sdim}
192218885Sdim
193218885Sdimbool Process::StandardInIsUserInput() {
194218885Sdim  return FileDescriptorIsDisplayed(STDIN_FILENO);
195218885Sdim}
196218885Sdim
197218885Sdimbool Process::StandardOutIsDisplayed() {
198218885Sdim  return FileDescriptorIsDisplayed(STDOUT_FILENO);
199218885Sdim}
200218885Sdim
201218885Sdimbool Process::StandardErrIsDisplayed() {
202218885Sdim  return FileDescriptorIsDisplayed(STDERR_FILENO);
203218885Sdim}
204218885Sdim
205218885Sdimbool Process::FileDescriptorIsDisplayed(int fd) {
206218885Sdim#if HAVE_ISATTY
207218885Sdim  return isatty(fd);
208218885Sdim#else
209218885Sdim  // If we don't have isatty, just return false.
210218885Sdim  return false;
211218885Sdim#endif
212218885Sdim}
213218885Sdim
214218885Sdimstatic unsigned getColumns(int FileID) {
215218885Sdim  // If COLUMNS is defined in the environment, wrap to that many columns.
216218885Sdim  if (const char *ColumnsStr = std::getenv("COLUMNS")) {
217218885Sdim    int Columns = std::atoi(ColumnsStr);
218218885Sdim    if (Columns > 0)
219218885Sdim      return Columns;
220218885Sdim  }
221218885Sdim
222218885Sdim  unsigned Columns = 0;
223218885Sdim
224218885Sdim#if defined(HAVE_SYS_IOCTL_H) && defined(HAVE_TERMIOS_H)
225218885Sdim  // Try to determine the width of the terminal.
226218885Sdim  struct winsize ws;
227249423Sdim  // Zero-fill ws to avoid a false positive from MemorySanitizer.
228249423Sdim  memset(&ws, 0, sizeof(ws));
229218885Sdim  if (ioctl(FileID, TIOCGWINSZ, &ws) == 0)
230218885Sdim    Columns = ws.ws_col;
231218885Sdim#endif
232218885Sdim
233218885Sdim  return Columns;
234218885Sdim}
235218885Sdim
236218885Sdimunsigned Process::StandardOutColumns() {
237218885Sdim  if (!StandardOutIsDisplayed())
238218885Sdim    return 0;
239218885Sdim
240218885Sdim  return getColumns(1);
241218885Sdim}
242218885Sdim
243218885Sdimunsigned Process::StandardErrColumns() {
244218885Sdim  if (!StandardErrIsDisplayed())
245218885Sdim    return 0;
246218885Sdim
247218885Sdim  return getColumns(2);
248218885Sdim}
249218885Sdim
250218885Sdimstatic bool terminalHasColors() {
251218885Sdim  if (const char *term = std::getenv("TERM")) {
252218885Sdim    // Most modern terminals support ANSI escape sequences for colors.
253218885Sdim    // We could check terminfo, or have a list of known terms that support
254218885Sdim    // colors, but that would be overkill.
255218885Sdim    // The user can always ask for no colors by setting TERM to dumb, or
256218885Sdim    // using a commandline flag.
257218885Sdim    return strcmp(term, "dumb") != 0;
258218885Sdim  }
259218885Sdim  return false;
260218885Sdim}
261218885Sdim
262239462Sdimbool Process::FileDescriptorHasColors(int fd) {
263239462Sdim  // A file descriptor has colors if it is displayed and the terminal has
264239462Sdim  // colors.
265239462Sdim  return FileDescriptorIsDisplayed(fd) && terminalHasColors();
266239462Sdim}
267239462Sdim
268218885Sdimbool Process::StandardOutHasColors() {
269239462Sdim  return FileDescriptorHasColors(STDOUT_FILENO);
270218885Sdim}
271218885Sdim
272218885Sdimbool Process::StandardErrHasColors() {
273239462Sdim  return FileDescriptorHasColors(STDERR_FILENO);
274218885Sdim}
275218885Sdim
276218885Sdimbool Process::ColorNeedsFlush() {
277218885Sdim  // No, we use ANSI escape sequences.
278218885Sdim  return false;
279218885Sdim}
280218885Sdim
281218885Sdim#define COLOR(FGBG, CODE, BOLD) "\033[0;" BOLD FGBG CODE "m"
282218885Sdim
283218885Sdim#define ALLCOLORS(FGBG,BOLD) {\
284218885Sdim    COLOR(FGBG, "0", BOLD),\
285218885Sdim    COLOR(FGBG, "1", BOLD),\
286218885Sdim    COLOR(FGBG, "2", BOLD),\
287218885Sdim    COLOR(FGBG, "3", BOLD),\
288218885Sdim    COLOR(FGBG, "4", BOLD),\
289218885Sdim    COLOR(FGBG, "5", BOLD),\
290218885Sdim    COLOR(FGBG, "6", BOLD),\
291218885Sdim    COLOR(FGBG, "7", BOLD)\
292218885Sdim  }
293218885Sdim
294218885Sdimstatic const char colorcodes[2][2][8][10] = {
295218885Sdim { ALLCOLORS("3",""), ALLCOLORS("3","1;") },
296218885Sdim { ALLCOLORS("4",""), ALLCOLORS("4","1;") }
297218885Sdim};
298218885Sdim
299218885Sdimconst char *Process::OutputColor(char code, bool bold, bool bg) {
300218885Sdim  return colorcodes[bg?1:0][bold?1:0][code&7];
301218885Sdim}
302218885Sdim
303218885Sdimconst char *Process::OutputBold(bool bg) {
304218885Sdim  return "\033[1m";
305218885Sdim}
306218885Sdim
307234982Sdimconst char *Process::OutputReverse() {
308234982Sdim  return "\033[7m";
309234982Sdim}
310234982Sdim
311218885Sdimconst char *Process::ResetColor() {
312218885Sdim  return "\033[0m";
313218885Sdim}
314239462Sdim
315239462Sdim#if !defined(HAVE_ARC4RANDOM)
316239462Sdimstatic unsigned GetRandomNumberSeed() {
317239462Sdim  // Attempt to get the initial seed from /dev/urandom, if possible.
318239462Sdim  if (FILE *RandomSource = ::fopen("/dev/urandom", "r")) {
319239462Sdim    unsigned seed;
320239462Sdim    int count = ::fread((void *)&seed, sizeof(seed), 1, RandomSource);
321239462Sdim    ::fclose(RandomSource);
322239462Sdim
323239462Sdim    // Return the seed if the read was successful.
324239462Sdim    if (count == 1)
325239462Sdim      return seed;
326239462Sdim  }
327239462Sdim
328239462Sdim  // Otherwise, swizzle the current time and the process ID to form a reasonable
329239462Sdim  // seed.
330249423Sdim  TimeValue Now = TimeValue::now();
331239462Sdim  return hash_combine(Now.seconds(), Now.nanoseconds(), ::getpid());
332239462Sdim}
333239462Sdim#endif
334239462Sdim
335239462Sdimunsigned llvm::sys::Process::GetRandomNumber() {
336239462Sdim#if defined(HAVE_ARC4RANDOM)
337239462Sdim  return arc4random();
338239462Sdim#else
339239462Sdim  static int x = (::srand(GetRandomNumberSeed()), 0);
340239462Sdim  (void)x;
341239462Sdim  return ::rand();
342239462Sdim#endif
343239462Sdim}
344