Coverage Report

Created: 2025-09-08 19:25

/home/runner/work/DirectXShaderCompiler/DirectXShaderCompiler/lib/Support/Unix/Process.inc
Line
Count
Source (jump to first uncovered line)
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/ADT/StringRef.h"
17
#include "llvm/Support/ManagedStatic.h"
18
#include "llvm/Support/Mutex.h"
19
#include "llvm/Support/MutexGuard.h"
20
#include "llvm/Support/TimeValue.h"
21
#if HAVE_FCNTL_H
22
#include <fcntl.h>
23
#endif
24
#ifdef HAVE_SYS_TIME_H
25
#include <sys/time.h>
26
#endif
27
#ifdef HAVE_SYS_RESOURCE_H
28
#include <sys/resource.h>
29
#endif
30
#ifdef HAVE_SYS_STAT_H
31
#include <sys/stat.h>
32
#endif
33
#if HAVE_SIGNAL_H
34
#include <signal.h>
35
#endif
36
// DragonFlyBSD, OpenBSD, and Bitrig have deprecated <malloc.h> for
37
// <stdlib.h> instead. Unix.h includes this for us already.
38
#if defined(HAVE_MALLOC_H) && !defined(__DragonFly__) && \
39
    !defined(__OpenBSD__) && !defined(__Bitrig__)
40
#include <malloc.h>
41
#endif
42
#if defined(HAVE_MALLCTL)
43
#include <malloc_np.h>
44
#endif
45
#ifdef HAVE_MALLOC_MALLOC_H
46
#include <malloc/malloc.h>
47
#endif
48
#ifdef HAVE_SYS_IOCTL_H
49
#  include <sys/ioctl.h>
50
#endif
51
#ifdef HAVE_TERMIOS_H
52
#  include <termios.h>
53
#endif
54
55
//===----------------------------------------------------------------------===//
56
//=== WARNING: Implementation here must contain only generic UNIX code that
57
//===          is guaranteed to work on *all* UNIX variants.
58
//===----------------------------------------------------------------------===//
59
60
using namespace llvm;
61
using namespace sys;
62
63
2.65k
static std::pair<TimeValue, TimeValue> getRUsageTimes() {
64
2.65k
#if defined(HAVE_GETRUSAGE)
65
2.65k
  struct rusage RU;
66
2.65k
  ::getrusage(RUSAGE_SELF, &RU);
67
2.65k
  return std::make_pair(
68
2.65k
      TimeValue(
69
2.65k
          static_cast<TimeValue::SecondsType>(RU.ru_utime.tv_sec),
70
2.65k
          static_cast<TimeValue::NanoSecondsType>(
71
2.65k
              RU.ru_utime.tv_usec * TimeValue::NANOSECONDS_PER_MICROSECOND)),
72
2.65k
      TimeValue(
73
2.65k
          static_cast<TimeValue::SecondsType>(RU.ru_stime.tv_sec),
74
2.65k
          static_cast<TimeValue::NanoSecondsType>(
75
2.65k
              RU.ru_stime.tv_usec * TimeValue::NANOSECONDS_PER_MICROSECOND)));
76
#else
77
#warning Cannot get usage times on this platform
78
  return std::make_pair(TimeValue(), TimeValue());
79
#endif
80
2.65k
}
81
82
// On Cygwin, getpagesize() returns 64k(AllocationGranularity) and
83
// offset in mmap(3) should be aligned to the AllocationGranularity.
84
16.1k
unsigned Process::getPageSize() {
85
16.1k
#if defined(HAVE_GETPAGESIZE)
86
16.1k
  static const int page_size = ::getpagesize();
87
#elif defined(HAVE_SYSCONF)
88
  static long page_size = ::sysconf(_SC_PAGE_SIZE);
89
#else
90
#warning Cannot get the page size on this machine
91
#endif
92
16.1k
  return static_cast<unsigned>(page_size);
93
16.1k
}
94
95
0
size_t Process::GetMallocUsage() {
96
0
#if defined(HAVE_MALLINFO2)
97
0
  struct mallinfo2 mi;
98
0
  mi = ::mallinfo2();
99
0
  return mi.uordblks;
100
#elif defined(HAVE_MALLINFO)
101
  struct mallinfo mi;
102
  mi = ::mallinfo();
103
  return mi.uordblks;
104
#elif defined(HAVE_MALLOC_ZONE_STATISTICS) && defined(HAVE_MALLOC_MALLOC_H)
105
  malloc_statistics_t Stats;
106
  malloc_zone_statistics(malloc_default_zone(), &Stats);
107
  return Stats.size_in_use;   // darwin
108
#elif defined(HAVE_MALLCTL)
109
  size_t alloc, sz;
110
  sz = sizeof(size_t);
111
  if (mallctl("stats.allocated", &alloc, &sz, NULL, 0) == 0)
112
    return alloc;
113
  return 0;
114
#elif defined(HAVE_SBRK)
115
  // Note this is only an approximation and more closely resembles
116
  // the value returned by mallinfo in the arena field.
117
  static char *StartOfMemory = reinterpret_cast<char*>(::sbrk(0));
118
  char *EndOfMemory = (char*)sbrk(0);
119
  if (EndOfMemory != ((char*)-1) && StartOfMemory != ((char*)-1))
120
    return EndOfMemory - StartOfMemory;
121
  return 0;
122
#else
123
#warning Cannot get malloc info on this platform
124
  return 0;
125
#endif
126
0
}
127
128
void Process::GetTimeUsage(TimeValue &elapsed, TimeValue &user_time,
129
2.65k
                           TimeValue &sys_time) {
130
2.65k
  elapsed = TimeValue::now();
131
2.65k
  std::tie(user_time, sys_time) = getRUsageTimes();
132
2.65k
}
133
134
#if defined(HAVE_MACH_MACH_H) && !defined(__GNU__)
135
#include <mach/mach.h>
136
#endif
137
138
// Some LLVM programs such as bugpoint produce core files as a normal part of
139
// their operation. To prevent the disk from filling up, this function
140
// does what's necessary to prevent their generation.
141
0
void Process::PreventCoreFiles() {
142
0
#if HAVE_SETRLIMIT
143
0
  struct rlimit rlim;
144
0
  rlim.rlim_cur = rlim.rlim_max = 0;
145
0
  setrlimit(RLIMIT_CORE, &rlim);
146
0
#endif
147
148
#if defined(HAVE_MACH_MACH_H) && !defined(__GNU__)
149
  // Disable crash reporting on Mac OS X 10.0-10.4
150
151
  // get information about the original set of exception ports for the task
152
  mach_msg_type_number_t Count = 0;
153
  exception_mask_t OriginalMasks[EXC_TYPES_COUNT];
154
  exception_port_t OriginalPorts[EXC_TYPES_COUNT];
155
  exception_behavior_t OriginalBehaviors[EXC_TYPES_COUNT];
156
  thread_state_flavor_t OriginalFlavors[EXC_TYPES_COUNT];
157
  kern_return_t err =
158
    task_get_exception_ports(mach_task_self(), EXC_MASK_ALL, OriginalMasks,
159
                             &Count, OriginalPorts, OriginalBehaviors,
160
                             OriginalFlavors);
161
  if (err == KERN_SUCCESS) {
162
    // replace each with MACH_PORT_NULL.
163
    for (unsigned i = 0; i != Count; ++i)
164
      task_set_exception_ports(mach_task_self(), OriginalMasks[i],
165
                               MACH_PORT_NULL, OriginalBehaviors[i],
166
                               OriginalFlavors[i]);
167
  }
168
169
  // Disable crash reporting on Mac OS X 10.5
170
  signal(SIGABRT, _exit);
171
  signal(SIGILL,  _exit);
172
  signal(SIGFPE,  _exit);
173
  signal(SIGSEGV, _exit);
174
  signal(SIGBUS,  _exit);
175
#endif
176
0
}
177
178
0
Optional<std::string> Process::GetEnv(StringRef Name) {
179
0
  std::string NameStr = Name.str();
180
0
  const char *Val = ::getenv(NameStr.c_str());
181
0
  if (!Val)
182
0
    return None;
183
0
  return std::string(Val);
184
0
}
185
186
std::error_code
187
Process::GetArgumentVector(SmallVectorImpl<const char *> &ArgsOut,
188
                           ArrayRef<const char *> ArgsIn,
189
0
                           SpecificBumpPtrAllocator<char> &) {
190
0
  ArgsOut.append(ArgsIn.begin(), ArgsIn.end());
191
192
0
  return std::error_code();
193
0
}
194
195
namespace {
196
class FDCloser {
197
public:
198
0
  FDCloser(int &FD) : FD(FD), KeepOpen(false) {}
199
0
  void keepOpen() { KeepOpen = true; }
200
0
  ~FDCloser() {
201
0
    if (!KeepOpen && FD >= 0)
202
0
      ::close(FD);
203
0
  }
204
205
private:
206
  FDCloser(const FDCloser &) = delete;
207
  void operator=(const FDCloser &) = delete;
208
209
  int &FD;
210
  bool KeepOpen;
211
};
212
}
213
214
0
std::error_code Process::FixupStandardFileDescriptors() {
215
0
  int NullFD = -1;
216
0
  FDCloser FDC(NullFD);
217
0
  const int StandardFDs[] = {STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO};
218
0
  for (int StandardFD : StandardFDs) {
219
0
    struct stat st;
220
0
    errno = 0;
221
0
    while (fstat(StandardFD, &st) < 0) {
222
0
      assert(errno && "expected errno to be set if fstat failed!");
223
      // fstat should return EBADF if the file descriptor is closed.
224
0
      if (errno == EBADF)
225
0
        break;
226
      // retry fstat if we got EINTR, otherwise bubble up the failure.
227
0
      if (errno != EINTR)
228
0
        return std::error_code(errno, std::generic_category());
229
0
    }
230
    // if fstat succeeds, move on to the next FD.
231
0
    if (!errno)
232
0
      continue;
233
0
    assert(errno == EBADF && "expected errno to have EBADF at this point!");
234
235
0
    if (NullFD < 0) {
236
0
      while ((NullFD = open("/dev/null", O_RDWR)) < 0) {
237
0
        if (errno == EINTR)
238
0
          continue;
239
0
        return std::error_code(errno, std::generic_category());
240
0
      }
241
0
    }
242
243
0
    if (NullFD == StandardFD)
244
0
      FDC.keepOpen();
245
0
    else if (dup2(NullFD, StandardFD) < 0)
246
0
      return std::error_code(errno, std::generic_category());
247
0
  }
248
0
  return std::error_code();
249
0
}
250
251
27.3k
std::error_code Process::SafelyCloseFileDescriptor(int FD) {
252
  // Create a signal set filled with *all* signals.
253
27.3k
  sigset_t FullSet;
254
27.3k
  if (sigfillset(&FullSet) < 0)
255
0
    return std::error_code(errno, std::generic_category());
256
  // Atomically swap our current signal mask with a full mask.
257
27.3k
  sigset_t SavedSet;
258
27.3k
#if LLVM_ENABLE_THREADS
259
27.3k
  if (int EC = pthread_sigmask(SIG_SETMASK, &FullSet, &SavedSet))
260
0
    return std::error_code(EC, std::generic_category());
261
#else
262
  if (sigprocmask(SIG_SETMASK, &FullSet, &SavedSet) < 0)
263
    return std::error_code(errno, std::generic_category());
264
#endif
265
  // Attempt to close the file descriptor.
266
  // We need to save the error, if one occurs, because our subsequent call to
267
  // pthread_sigmask might tamper with errno.
268
27.3k
  int ErrnoFromClose = 0;
269
27.3k
  if (fs::msf_close(FD) < 0)
270
48
    ErrnoFromClose = errno;
271
  // Restore the signal mask back to what we saved earlier.
272
27.3k
  int EC = 0;
273
27.3k
#if LLVM_ENABLE_THREADS
274
27.3k
  EC = pthread_sigmask(SIG_SETMASK, &SavedSet, nullptr);
275
#else
276
  if (sigprocmask(SIG_SETMASK, &SavedSet, nullptr) < 0)
277
    EC = errno;
278
#endif
279
  // The error code from close takes precedence over the one from
280
  // pthread_sigmask.
281
27.3k
  if (ErrnoFromClose)
282
48
    return std::error_code(ErrnoFromClose, std::generic_category());
283
27.3k
  return std::error_code(EC, std::generic_category());
284
27.3k
}
285
286
0
bool Process::StandardInIsUserInput() {
287
0
  return FileDescriptorIsDisplayed(STDIN_FILENO);
288
0
}
289
290
0
bool Process::StandardOutIsDisplayed() {
291
0
  return FileDescriptorIsDisplayed(STDOUT_FILENO);
292
0
}
293
294
0
bool Process::StandardErrIsDisplayed() {
295
0
  return FileDescriptorIsDisplayed(STDERR_FILENO);
296
0
}
297
298
692
bool Process::FileDescriptorIsDisplayed(int fd) {
299
692
#if HAVE_ISATTY
300
692
  return isatty(fd);
301
#else
302
  // If we don't have isatty, just return false.
303
  return false;
304
#endif
305
692
}
306
307
0
static unsigned getColumns(int FileID) {
308
  // If COLUMNS is defined in the environment, wrap to that many columns.
309
0
  if (const char *ColumnsStr = std::getenv("COLUMNS")) {
310
0
    int Columns = std::atoi(ColumnsStr);
311
0
    if (Columns > 0)
312
0
      return Columns;
313
0
  }
314
315
0
  unsigned Columns = 0;
316
317
0
#if defined(HAVE_SYS_IOCTL_H) && defined(HAVE_TERMIOS_H)
318
  // Try to determine the width of the terminal.
319
0
  struct winsize ws;
320
0
  if (ioctl(FileID, TIOCGWINSZ, &ws) == 0)
321
0
    Columns = ws.ws_col;
322
0
#endif
323
324
0
  return Columns;
325
0
}
326
327
0
unsigned Process::StandardOutColumns() {
328
0
  if (!StandardOutIsDisplayed())
329
0
    return 0;
330
331
0
  return getColumns(1);
332
0
}
333
334
0
unsigned Process::StandardErrColumns() {
335
0
  if (!StandardErrIsDisplayed())
336
0
    return 0;
337
338
0
  return getColumns(2);
339
0
}
340
341
#ifdef HAVE_TERMINFO
342
// We manually declare these extern functions because finding the correct
343
// headers from various terminfo, curses, or other sources is harder than
344
// writing their specs down.
345
extern "C" int setupterm(char *term, int filedes, int *errret);
346
extern "C" struct term *set_curterm(struct term *termp);
347
extern "C" int del_curterm(struct term *termp);
348
extern "C" int tigetnum(char *capname);
349
#endif
350
351
#ifdef HAVE_TERMINFO
352
static ManagedStatic<sys::Mutex> TermColorMutex;
353
#endif
354
355
0
static bool terminalHasColors(int fd) {
356
#ifdef HAVE_TERMINFO
357
  // First, acquire a global lock because these C routines are thread hostile.
358
  MutexGuard G(*TermColorMutex);
359
360
  int errret = 0;
361
  if (setupterm((char *)nullptr, fd, &errret) != 0)
362
    // Regardless of why, if we can't get terminfo, we shouldn't try to print
363
    // colors.
364
    return false;
365
366
  // Test whether the terminal as set up supports color output. How to do this
367
  // isn't entirely obvious. We can use the curses routine 'has_colors' but it
368
  // would be nice to avoid a dependency on curses proper when we can make do
369
  // with a minimal terminfo parsing library. Also, we don't really care whether
370
  // the terminal supports the curses-specific color changing routines, merely
371
  // if it will interpret ANSI color escape codes in a reasonable way. Thus, the
372
  // strategy here is just to query the baseline colors capability and if it
373
  // supports colors at all to assume it will translate the escape codes into
374
  // whatever range of colors it does support. We can add more detailed tests
375
  // here if users report them as necessary.
376
  //
377
  // The 'tigetnum' routine returns -2 or -1 on errors, and might return 0 if
378
  // the terminfo says that no colors are supported.
379
  bool HasColors = tigetnum(const_cast<char *>("colors")) > 0;
380
381
  // Now extract the structure allocated by setupterm and free its memory
382
  // through a really silly dance.
383
  struct term *termp = set_curterm((struct term *)nullptr);
384
  (void)del_curterm(termp); // Drop any errors here.
385
386
  // Return true if we found a color capabilities for the current terminal.
387
  if (HasColors)
388
    return true;
389
#endif
390
391
  // Otherwise, be conservative.
392
0
  return false;
393
0
}
394
395
608
bool Process::FileDescriptorHasColors(int fd) {
396
  // A file descriptor has colors if it is displayed and the terminal has
397
  // colors.
398
608
  return FileDescriptorIsDisplayed(fd) && 
terminalHasColors(fd)0
;
399
608
}
400
401
338
bool Process::StandardOutHasColors() {
402
338
  return FileDescriptorHasColors(STDOUT_FILENO);
403
338
}
404
405
0
bool Process::StandardErrHasColors() {
406
0
  return FileDescriptorHasColors(STDERR_FILENO);
407
0
}
408
409
0
void Process::UseANSIEscapeCodes(bool /*enable*/) {
410
  // No effect.
411
0
}
412
413
0
bool Process::ColorNeedsFlush() {
414
  // No, we use ANSI escape sequences.
415
0
  return false;
416
0
}
417
418
0
const char *Process::OutputColor(char code, bool bold, bool bg) {
419
0
  return colorcodes[bg?1:0][bold?1:0][code&7];
420
0
}
421
422
0
const char *Process::OutputBold(bool bg) {
423
0
  return "\033[1m";
424
0
}
425
426
0
const char *Process::OutputReverse() {
427
0
  return "\033[7m";
428
0
}
429
430
0
const char *Process::ResetColor() {
431
0
  return "\033[0m";
432
0
}
433
434
#if !defined(HAVE_DECL_ARC4RANDOM) || !HAVE_DECL_ARC4RANDOM
435
static unsigned GetRandomNumberSeed() {
436
  // Attempt to get the initial seed from /dev/urandom, if possible.
437
  if (FILE *RandomSource = ::fopen("/dev/urandom", "r")) {
438
    unsigned seed;
439
    int count = ::fread((void *)&seed, sizeof(seed), 1, RandomSource);
440
    ::fclose(RandomSource);
441
442
    // Return the seed if the read was successful.
443
    if (count == 1)
444
      return seed;
445
  }
446
447
  // Otherwise, swizzle the current time and the process ID to form a reasonable
448
  // seed.
449
  TimeValue Now = TimeValue::now();
450
  return hash_combine(Now.seconds(), Now.nanoseconds(), ::getpid());
451
}
452
#endif
453
454
112
unsigned llvm::sys::Process::GetRandomNumber() {
455
112
#if defined(HAVE_DECL_ARC4RANDOM) && HAVE_DECL_ARC4RANDOM
456
112
  return arc4random();
457
#else
458
  static int x = (::srand(GetRandomNumberSeed()), 0);
459
  (void)x;
460
  return ::rand();
461
#endif
462
112
}