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