1 | //===-- Host.cpp ----------------------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | // C includes |
10 | #include <cerrno> |
11 | #include <climits> |
12 | #include <cstdlib> |
13 | #include <sys/types.h> |
14 | #ifndef _WIN32 |
15 | #include <dlfcn.h> |
16 | #include <grp.h> |
17 | #include <netdb.h> |
18 | #include <pwd.h> |
19 | #include <sys/stat.h> |
20 | #include <unistd.h> |
21 | #endif |
22 | |
23 | #if defined(__APPLE__) |
24 | #include <mach-o/dyld.h> |
25 | #include <mach/mach_init.h> |
26 | #include <mach/mach_port.h> |
27 | #endif |
28 | |
29 | #if defined(__linux__) || defined(__FreeBSD__) || \ |
30 | defined(__FreeBSD_kernel__) || defined(__APPLE__) || \ |
31 | defined(__NetBSD__) || defined(__OpenBSD__) || defined(__EMSCRIPTEN__) |
32 | #if !defined(__ANDROID__) |
33 | #include <spawn.h> |
34 | #endif |
35 | #include <sys/syscall.h> |
36 | #include <sys/wait.h> |
37 | #endif |
38 | |
39 | #if defined(__FreeBSD__) |
40 | #include <pthread_np.h> |
41 | #endif |
42 | |
43 | #if defined(__NetBSD__) |
44 | #include <lwp.h> |
45 | #endif |
46 | |
47 | #include <csignal> |
48 | |
49 | #include "lldb/Host/FileAction.h" |
50 | #include "lldb/Host/FileSystem.h" |
51 | #include "lldb/Host/Host.h" |
52 | #include "lldb/Host/HostInfo.h" |
53 | #include "lldb/Host/HostProcess.h" |
54 | #include "lldb/Host/MonitoringProcessLauncher.h" |
55 | #include "lldb/Host/ProcessLaunchInfo.h" |
56 | #include "lldb/Host/ProcessLauncher.h" |
57 | #include "lldb/Host/ThreadLauncher.h" |
58 | #include "lldb/Host/posix/ConnectionFileDescriptorPosix.h" |
59 | #include "lldb/Utility/FileSpec.h" |
60 | #include "lldb/Utility/LLDBLog.h" |
61 | #include "lldb/Utility/Log.h" |
62 | #include "lldb/Utility/Predicate.h" |
63 | #include "lldb/Utility/Status.h" |
64 | #include "lldb/lldb-private-forward.h" |
65 | #include "llvm/ADT/SmallString.h" |
66 | #include "llvm/Support/Errno.h" |
67 | #include "llvm/Support/FileSystem.h" |
68 | |
69 | #if defined(_WIN32) |
70 | #include "lldb/Host/windows/ConnectionGenericFileWindows.h" |
71 | #include "lldb/Host/windows/ProcessLauncherWindows.h" |
72 | #else |
73 | #include "lldb/Host/posix/ProcessLauncherPosixFork.h" |
74 | #endif |
75 | |
76 | #if defined(__APPLE__) |
77 | #ifndef _POSIX_SPAWN_DISABLE_ASLR |
78 | #define _POSIX_SPAWN_DISABLE_ASLR 0x0100 |
79 | #endif |
80 | |
81 | extern "C" { |
82 | int __pthread_chdir(const char *path); |
83 | int __pthread_fchdir(int fildes); |
84 | } |
85 | |
86 | #endif |
87 | |
88 | using namespace lldb; |
89 | using namespace lldb_private; |
90 | |
91 | #if !defined(__APPLE__) |
92 | void Host::SystemLog(llvm::StringRef message) { llvm::errs() << message; } |
93 | #endif |
94 | |
95 | #if !defined(__APPLE__) && !defined(_WIN32) |
96 | static thread_result_t |
97 | MonitorChildProcessThreadFunction(::pid_t pid, |
98 | Host::MonitorChildProcessCallback callback); |
99 | |
100 | llvm::Expected<HostThread> Host::StartMonitoringChildProcess( |
101 | const Host::MonitorChildProcessCallback &callback, lldb::pid_t pid) { |
102 | char thread_name[256]; |
103 | ::snprintf(s: thread_name, maxlen: sizeof(thread_name), |
104 | format: "<lldb.host.wait4(pid=%" PRIu64 ")>" , pid); |
105 | assert(pid <= UINT32_MAX); |
106 | return ThreadLauncher::LaunchThread(name: thread_name, thread_function: [pid, callback] { |
107 | return MonitorChildProcessThreadFunction(pid, callback); |
108 | }); |
109 | } |
110 | |
111 | #ifndef __linux__ |
112 | // Scoped class that will disable thread canceling when it is constructed, and |
113 | // exception safely restore the previous value it when it goes out of scope. |
114 | class ScopedPThreadCancelDisabler { |
115 | public: |
116 | ScopedPThreadCancelDisabler() { |
117 | // Disable the ability for this thread to be cancelled |
118 | int err = ::pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &m_old_state); |
119 | if (err != 0) |
120 | m_old_state = -1; |
121 | } |
122 | |
123 | ~ScopedPThreadCancelDisabler() { |
124 | // Restore the ability for this thread to be cancelled to what it |
125 | // previously was. |
126 | if (m_old_state != -1) |
127 | ::pthread_setcancelstate(m_old_state, 0); |
128 | } |
129 | |
130 | private: |
131 | int m_old_state; // Save the old cancelability state. |
132 | }; |
133 | #endif // __linux__ |
134 | |
135 | #ifdef __linux__ |
136 | static thread_local volatile sig_atomic_t g_usr1_called; |
137 | |
138 | static void SigUsr1Handler(int) { g_usr1_called = 1; } |
139 | #endif // __linux__ |
140 | |
141 | static bool CheckForMonitorCancellation() { |
142 | #ifdef __linux__ |
143 | if (g_usr1_called) { |
144 | g_usr1_called = 0; |
145 | return true; |
146 | } |
147 | #else |
148 | ::pthread_testcancel(); |
149 | #endif |
150 | return false; |
151 | } |
152 | |
153 | static thread_result_t |
154 | MonitorChildProcessThreadFunction(::pid_t pid, |
155 | Host::MonitorChildProcessCallback callback) { |
156 | Log *log = GetLog(mask: LLDBLog::Process); |
157 | LLDB_LOG(log, "pid = {0}" , pid); |
158 | |
159 | int status = -1; |
160 | |
161 | #ifdef __linux__ |
162 | // This signal is only used to interrupt the thread from waitpid |
163 | struct sigaction sigUsr1Action; |
164 | memset(s: &sigUsr1Action, c: 0, n: sizeof(sigUsr1Action)); |
165 | sigUsr1Action.sa_handler = SigUsr1Handler; |
166 | ::sigaction(SIGUSR1, act: &sigUsr1Action, oact: nullptr); |
167 | #endif // __linux__ |
168 | |
169 | while (true) { |
170 | log = GetLog(mask: LLDBLog::Process); |
171 | LLDB_LOG(log, "::waitpid({0}, &status, 0)..." , pid); |
172 | |
173 | if (CheckForMonitorCancellation()) |
174 | return nullptr; |
175 | |
176 | const ::pid_t wait_pid = ::waitpid(pid: pid, stat_loc: &status, options: 0); |
177 | |
178 | LLDB_LOG(log, "::waitpid({0}, &status, 0) => pid = {1}, status = {2:x}" , pid, |
179 | wait_pid, status); |
180 | |
181 | if (CheckForMonitorCancellation()) |
182 | return nullptr; |
183 | |
184 | if (wait_pid != -1) |
185 | break; |
186 | if (errno != EINTR) { |
187 | LLDB_LOG(log, "pid = {0}, thread exiting because waitpid failed ({1})..." , |
188 | pid, llvm::sys::StrError()); |
189 | return nullptr; |
190 | } |
191 | } |
192 | |
193 | int signal = 0; |
194 | int exit_status = 0; |
195 | if (WIFEXITED(status)) { |
196 | exit_status = WEXITSTATUS(status); |
197 | } else if (WIFSIGNALED(status)) { |
198 | signal = WTERMSIG(status); |
199 | exit_status = -1; |
200 | } else { |
201 | llvm_unreachable("Unknown status" ); |
202 | } |
203 | |
204 | // Scope for pthread_cancel_disabler |
205 | { |
206 | #ifndef __linux__ |
207 | ScopedPThreadCancelDisabler pthread_cancel_disabler; |
208 | #endif |
209 | |
210 | if (callback) |
211 | callback(pid, signal, exit_status); |
212 | } |
213 | |
214 | LLDB_LOG(GetLog(LLDBLog::Process), "pid = {0} thread exiting..." , pid); |
215 | return nullptr; |
216 | } |
217 | |
218 | #endif // #if !defined (__APPLE__) && !defined (_WIN32) |
219 | |
220 | lldb::pid_t Host::GetCurrentProcessID() { return ::getpid(); } |
221 | |
222 | #ifndef _WIN32 |
223 | |
224 | lldb::thread_t Host::GetCurrentThread() { |
225 | return lldb::thread_t(pthread_self()); |
226 | } |
227 | |
228 | const char *Host::GetSignalAsCString(int signo) { |
229 | switch (signo) { |
230 | case SIGHUP: |
231 | return "SIGHUP" ; // 1 hangup |
232 | case SIGINT: |
233 | return "SIGINT" ; // 2 interrupt |
234 | case SIGQUIT: |
235 | return "SIGQUIT" ; // 3 quit |
236 | case SIGILL: |
237 | return "SIGILL" ; // 4 illegal instruction (not reset when caught) |
238 | case SIGTRAP: |
239 | return "SIGTRAP" ; // 5 trace trap (not reset when caught) |
240 | case SIGABRT: |
241 | return "SIGABRT" ; // 6 abort() |
242 | #if defined(SIGPOLL) |
243 | #if !defined(SIGIO) || (SIGPOLL != SIGIO) |
244 | // Under some GNU/Linux, SIGPOLL and SIGIO are the same. Causing the build to |
245 | // fail with 'multiple define cases with same value' |
246 | case SIGPOLL: |
247 | return "SIGPOLL" ; // 7 pollable event ([XSR] generated, not supported) |
248 | #endif |
249 | #endif |
250 | #if defined(SIGEMT) |
251 | case SIGEMT: |
252 | return "SIGEMT" ; // 7 EMT instruction |
253 | #endif |
254 | case SIGFPE: |
255 | return "SIGFPE" ; // 8 floating point exception |
256 | case SIGKILL: |
257 | return "SIGKILL" ; // 9 kill (cannot be caught or ignored) |
258 | case SIGBUS: |
259 | return "SIGBUS" ; // 10 bus error |
260 | case SIGSEGV: |
261 | return "SIGSEGV" ; // 11 segmentation violation |
262 | case SIGSYS: |
263 | return "SIGSYS" ; // 12 bad argument to system call |
264 | case SIGPIPE: |
265 | return "SIGPIPE" ; // 13 write on a pipe with no one to read it |
266 | case SIGALRM: |
267 | return "SIGALRM" ; // 14 alarm clock |
268 | case SIGTERM: |
269 | return "SIGTERM" ; // 15 software termination signal from kill |
270 | case SIGURG: |
271 | return "SIGURG" ; // 16 urgent condition on IO channel |
272 | case SIGSTOP: |
273 | return "SIGSTOP" ; // 17 sendable stop signal not from tty |
274 | case SIGTSTP: |
275 | return "SIGTSTP" ; // 18 stop signal from tty |
276 | case SIGCONT: |
277 | return "SIGCONT" ; // 19 continue a stopped process |
278 | case SIGCHLD: |
279 | return "SIGCHLD" ; // 20 to parent on child stop or exit |
280 | case SIGTTIN: |
281 | return "SIGTTIN" ; // 21 to readers pgrp upon background tty read |
282 | case SIGTTOU: |
283 | return "SIGTTOU" ; // 22 like TTIN for output if (tp->t_local<OSTOP) |
284 | #if defined(SIGIO) |
285 | case SIGIO: |
286 | return "SIGIO" ; // 23 input/output possible signal |
287 | #endif |
288 | case SIGXCPU: |
289 | return "SIGXCPU" ; // 24 exceeded CPU time limit |
290 | case SIGXFSZ: |
291 | return "SIGXFSZ" ; // 25 exceeded file size limit |
292 | case SIGVTALRM: |
293 | return "SIGVTALRM" ; // 26 virtual time alarm |
294 | case SIGPROF: |
295 | return "SIGPROF" ; // 27 profiling time alarm |
296 | #if defined(SIGWINCH) |
297 | case SIGWINCH: |
298 | return "SIGWINCH" ; // 28 window size changes |
299 | #endif |
300 | #if defined(SIGINFO) |
301 | case SIGINFO: |
302 | return "SIGINFO" ; // 29 information request |
303 | #endif |
304 | case SIGUSR1: |
305 | return "SIGUSR1" ; // 30 user defined signal 1 |
306 | case SIGUSR2: |
307 | return "SIGUSR2" ; // 31 user defined signal 2 |
308 | default: |
309 | break; |
310 | } |
311 | return nullptr; |
312 | } |
313 | |
314 | #endif |
315 | |
316 | #if !defined(__APPLE__) // see Host.mm |
317 | |
318 | bool Host::GetBundleDirectory(const FileSpec &file, FileSpec &bundle) { |
319 | bundle.Clear(); |
320 | return false; |
321 | } |
322 | |
323 | bool Host::ResolveExecutableInBundle(FileSpec &file) { return false; } |
324 | #endif |
325 | |
326 | #ifndef _WIN32 |
327 | |
328 | FileSpec Host::GetModuleFileSpecForHostAddress(const void *host_addr) { |
329 | FileSpec module_filespec; |
330 | #if !defined(__ANDROID__) |
331 | Dl_info info; |
332 | if (::dladdr(address: host_addr, info: &info)) { |
333 | if (info.dli_fname) { |
334 | module_filespec.SetFile(path: info.dli_fname, style: FileSpec::Style::native); |
335 | FileSystem::Instance().Resolve(file_spec&: module_filespec); |
336 | } |
337 | } |
338 | #endif |
339 | return module_filespec; |
340 | } |
341 | |
342 | #endif |
343 | |
344 | #if !defined(__linux__) |
345 | bool Host::FindProcessThreads(const lldb::pid_t pid, TidMap &tids_to_attach) { |
346 | return false; |
347 | } |
348 | #endif |
349 | |
350 | struct ShellInfo { |
351 | ShellInfo() : process_reaped(false) {} |
352 | |
353 | lldb_private::Predicate<bool> process_reaped; |
354 | lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; |
355 | int signo = -1; |
356 | int status = -1; |
357 | }; |
358 | |
359 | static void |
360 | MonitorShellCommand(std::shared_ptr<ShellInfo> shell_info, lldb::pid_t pid, |
361 | int signo, // Zero for no signal |
362 | int status) // Exit value of process if signal is zero |
363 | { |
364 | shell_info->pid = pid; |
365 | shell_info->signo = signo; |
366 | shell_info->status = status; |
367 | // Let the thread running Host::RunShellCommand() know that the process |
368 | // exited and that ShellInfo has been filled in by broadcasting to it |
369 | shell_info->process_reaped.SetValue(value: true, broadcast_type: eBroadcastAlways); |
370 | } |
371 | |
372 | Status Host::RunShellCommand(llvm::StringRef command, |
373 | const FileSpec &working_dir, int *status_ptr, |
374 | int *signo_ptr, std::string *command_output_ptr, |
375 | const Timeout<std::micro> &timeout, |
376 | bool run_in_shell, bool hide_stderr) { |
377 | return RunShellCommand(shell: llvm::StringRef(), args: Args(command), working_dir, |
378 | status_ptr, signo_ptr, command_output: command_output_ptr, timeout, |
379 | run_in_shell, hide_stderr); |
380 | } |
381 | |
382 | Status Host::RunShellCommand(llvm::StringRef shell_path, |
383 | llvm::StringRef command, |
384 | const FileSpec &working_dir, int *status_ptr, |
385 | int *signo_ptr, std::string *command_output_ptr, |
386 | const Timeout<std::micro> &timeout, |
387 | bool run_in_shell, bool hide_stderr) { |
388 | return RunShellCommand(shell: shell_path, args: Args(command), working_dir, status_ptr, |
389 | signo_ptr, command_output: command_output_ptr, timeout, run_in_shell, |
390 | hide_stderr); |
391 | } |
392 | |
393 | Status Host::RunShellCommand(const Args &args, const FileSpec &working_dir, |
394 | int *status_ptr, int *signo_ptr, |
395 | std::string *command_output_ptr, |
396 | const Timeout<std::micro> &timeout, |
397 | bool run_in_shell, bool hide_stderr) { |
398 | return RunShellCommand(shell: llvm::StringRef(), args, working_dir, status_ptr, |
399 | signo_ptr, command_output: command_output_ptr, timeout, run_in_shell, |
400 | hide_stderr); |
401 | } |
402 | |
403 | Status Host::RunShellCommand(llvm::StringRef shell_path, const Args &args, |
404 | const FileSpec &working_dir, int *status_ptr, |
405 | int *signo_ptr, std::string *command_output_ptr, |
406 | const Timeout<std::micro> &timeout, |
407 | bool run_in_shell, bool hide_stderr) { |
408 | Status error; |
409 | ProcessLaunchInfo launch_info; |
410 | launch_info.SetArchitecture(HostInfo::GetArchitecture()); |
411 | if (run_in_shell) { |
412 | // Run the command in a shell |
413 | FileSpec shell = HostInfo::GetDefaultShell(); |
414 | if (!shell_path.empty()) |
415 | shell.SetPath(shell_path); |
416 | |
417 | launch_info.SetShell(shell); |
418 | launch_info.GetArguments().AppendArguments(rhs: args); |
419 | const bool will_debug = false; |
420 | const bool first_arg_is_full_shell_command = false; |
421 | launch_info.ConvertArgumentsForLaunchingInShell( |
422 | error, will_debug, first_arg_is_full_shell_command, num_resumes: 0); |
423 | } else { |
424 | // No shell, just run it |
425 | const bool first_arg_is_executable = true; |
426 | launch_info.SetArguments(args, first_arg_is_executable); |
427 | } |
428 | |
429 | launch_info.GetEnvironment() = Host::GetEnvironment(); |
430 | |
431 | if (working_dir) |
432 | launch_info.SetWorkingDirectory(working_dir); |
433 | llvm::SmallString<64> output_file_path; |
434 | |
435 | if (command_output_ptr) { |
436 | // Create a temporary file to get the stdout/stderr and redirect the output |
437 | // of the command into this file. We will later read this file if all goes |
438 | // well and fill the data into "command_output_ptr" |
439 | if (FileSpec tmpdir_file_spec = HostInfo::GetProcessTempDir()) { |
440 | tmpdir_file_spec.AppendPathComponent(component: "lldb-shell-output.%%%%%%" ); |
441 | llvm::sys::fs::createUniqueFile(Model: tmpdir_file_spec.GetPath(), |
442 | ResultPath&: output_file_path); |
443 | } else { |
444 | llvm::sys::fs::createTemporaryFile(Prefix: "lldb-shell-output.%%%%%%" , Suffix: "" , |
445 | ResultPath&: output_file_path); |
446 | } |
447 | } |
448 | |
449 | FileSpec output_file_spec(output_file_path.str()); |
450 | // Set up file descriptors. |
451 | launch_info.AppendSuppressFileAction(STDIN_FILENO, read: true, write: false); |
452 | if (output_file_spec) |
453 | launch_info.AppendOpenFileAction(STDOUT_FILENO, file_spec: output_file_spec, read: false, |
454 | write: true); |
455 | else |
456 | launch_info.AppendSuppressFileAction(STDOUT_FILENO, read: false, write: true); |
457 | |
458 | if (output_file_spec && !hide_stderr) |
459 | launch_info.AppendDuplicateFileAction(STDOUT_FILENO, STDERR_FILENO); |
460 | else |
461 | launch_info.AppendSuppressFileAction(STDERR_FILENO, read: false, write: true); |
462 | |
463 | std::shared_ptr<ShellInfo> shell_info_sp(new ShellInfo()); |
464 | launch_info.SetMonitorProcessCallback( |
465 | std::bind(f&: MonitorShellCommand, args&: shell_info_sp, args: std::placeholders::_1, |
466 | args: std::placeholders::_2, args: std::placeholders::_3)); |
467 | |
468 | error = LaunchProcess(launch_info); |
469 | const lldb::pid_t pid = launch_info.GetProcessID(); |
470 | |
471 | if (error.Success() && pid == LLDB_INVALID_PROCESS_ID) |
472 | error.SetErrorString("failed to get process ID" ); |
473 | |
474 | if (error.Success()) { |
475 | if (!shell_info_sp->process_reaped.WaitForValueEqualTo(value: true, timeout)) { |
476 | error.SetErrorString("timed out waiting for shell command to complete" ); |
477 | |
478 | // Kill the process since it didn't complete within the timeout specified |
479 | Kill(pid, SIGKILL); |
480 | // Wait for the monitor callback to get the message |
481 | shell_info_sp->process_reaped.WaitForValueEqualTo( |
482 | value: true, timeout: std::chrono::seconds(1)); |
483 | } else { |
484 | if (status_ptr) |
485 | *status_ptr = shell_info_sp->status; |
486 | |
487 | if (signo_ptr) |
488 | *signo_ptr = shell_info_sp->signo; |
489 | |
490 | if (command_output_ptr) { |
491 | command_output_ptr->clear(); |
492 | uint64_t file_size = |
493 | FileSystem::Instance().GetByteSize(file_spec: output_file_spec); |
494 | if (file_size > 0) { |
495 | if (file_size > command_output_ptr->max_size()) { |
496 | error.SetErrorStringWithFormat( |
497 | "shell command output is too large to fit into a std::string" ); |
498 | } else { |
499 | WritableDataBufferSP Buffer = |
500 | FileSystem::Instance().CreateWritableDataBuffer( |
501 | file_spec: output_file_spec); |
502 | if (error.Success()) |
503 | command_output_ptr->assign( |
504 | s: reinterpret_cast<char *>(Buffer->GetBytes()), |
505 | n: Buffer->GetByteSize()); |
506 | } |
507 | } |
508 | } |
509 | } |
510 | } |
511 | |
512 | llvm::sys::fs::remove(path: output_file_spec.GetPath()); |
513 | return error; |
514 | } |
515 | |
516 | // The functions below implement process launching for non-Apple-based |
517 | // platforms |
518 | #if !defined(__APPLE__) |
519 | Status Host::LaunchProcess(ProcessLaunchInfo &launch_info) { |
520 | std::unique_ptr<ProcessLauncher> delegate_launcher; |
521 | #if defined(_WIN32) |
522 | delegate_launcher.reset(new ProcessLauncherWindows()); |
523 | #else |
524 | delegate_launcher.reset(p: new ProcessLauncherPosixFork()); |
525 | #endif |
526 | MonitoringProcessLauncher launcher(std::move(delegate_launcher)); |
527 | |
528 | Status error; |
529 | HostProcess process = launcher.LaunchProcess(launch_info, error); |
530 | |
531 | // TODO(zturner): It would be better if the entire HostProcess were returned |
532 | // instead of writing it into this structure. |
533 | launch_info.SetProcessID(process.GetProcessId()); |
534 | |
535 | return error; |
536 | } |
537 | #endif // !defined(__APPLE__) |
538 | |
539 | #ifndef _WIN32 |
540 | void Host::Kill(lldb::pid_t pid, int signo) { ::kill(pid: pid, sig: signo); } |
541 | |
542 | #endif |
543 | |
544 | #if !defined(__APPLE__) |
545 | llvm::Error Host::OpenFileInExternalEditor(llvm::StringRef editor, |
546 | const FileSpec &file_spec, |
547 | uint32_t line_no) { |
548 | return llvm::errorCodeToError( |
549 | EC: std::error_code(ENOTSUP, std::system_category())); |
550 | } |
551 | |
552 | bool Host::IsInteractiveGraphicSession() { return false; } |
553 | #endif |
554 | |
555 | std::unique_ptr<Connection> Host::CreateDefaultConnection(llvm::StringRef url) { |
556 | #if defined(_WIN32) |
557 | if (url.starts_with("file://" )) |
558 | return std::unique_ptr<Connection>(new ConnectionGenericFile()); |
559 | #endif |
560 | return std::unique_ptr<Connection>(new ConnectionFileDescriptor()); |
561 | } |
562 | |
563 | #if defined(LLVM_ON_UNIX) |
564 | WaitStatus WaitStatus::Decode(int wstatus) { |
565 | if (WIFEXITED(wstatus)) |
566 | return {Exit, uint8_t(WEXITSTATUS(wstatus))}; |
567 | else if (WIFSIGNALED(wstatus)) |
568 | return {Signal, uint8_t(WTERMSIG(wstatus))}; |
569 | else if (WIFSTOPPED(wstatus)) |
570 | return {Stop, uint8_t(WSTOPSIG(wstatus))}; |
571 | llvm_unreachable("Unknown wait status" ); |
572 | } |
573 | #endif |
574 | |
575 | void llvm::format_provider<WaitStatus>::format(const WaitStatus &WS, |
576 | raw_ostream &OS, |
577 | StringRef Options) { |
578 | if (Options == "g" ) { |
579 | char type; |
580 | switch (WS.type) { |
581 | case WaitStatus::Exit: |
582 | type = 'W'; |
583 | break; |
584 | case WaitStatus::Signal: |
585 | type = 'X'; |
586 | break; |
587 | case WaitStatus::Stop: |
588 | type = 'S'; |
589 | break; |
590 | } |
591 | OS << formatv(Fmt: "{0}{1:x-2}" , Vals&: type, Vals: WS.status); |
592 | return; |
593 | } |
594 | |
595 | assert(Options.empty()); |
596 | const char *desc; |
597 | switch(WS.type) { |
598 | case WaitStatus::Exit: |
599 | desc = "Exited with status" ; |
600 | break; |
601 | case WaitStatus::Signal: |
602 | desc = "Killed by signal" ; |
603 | break; |
604 | case WaitStatus::Stop: |
605 | desc = "Stopped by signal" ; |
606 | break; |
607 | } |
608 | OS << desc << " " << int(WS.status); |
609 | } |
610 | |
611 | uint32_t Host::FindProcesses(const ProcessInstanceInfoMatch &match_info, |
612 | ProcessInstanceInfoList &process_infos) { |
613 | return FindProcessesImpl(match_info, proc_infos&: process_infos); |
614 | } |
615 | |
616 | char SystemLogHandler::ID; |
617 | |
618 | SystemLogHandler::SystemLogHandler() {} |
619 | |
620 | void SystemLogHandler::Emit(llvm::StringRef message) { |
621 | Host::SystemLog(message); |
622 | } |
623 | |