/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 | } |