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