1//===-- ExecuteFunction implementation for Unix-like Systems --------------===//
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#include "ExecuteFunction.h"
10#include "src/__support/macros/config.h"
11#include "test/UnitTest/ExecuteFunction.h" // FunctionCaller
12#include <assert.h>
13#include <poll.h>
14#include <signal.h>
15#include <stdio.h>
16#include <stdlib.h>
17#include <string.h>
18#include <sys/wait.h>
19#include <unistd.h>
20
21namespace LIBC_NAMESPACE_DECL {
22namespace testutils {
23
24bool ProcessStatus::exited_normally() { return WIFEXITED(platform_defined); }
25
26int ProcessStatus::get_exit_code() {
27 assert(exited_normally() && "Abnormal termination, no exit code");
28 return WEXITSTATUS(platform_defined);
29}
30
31int ProcessStatus::get_fatal_signal() {
32 if (exited_normally())
33 return 0;
34 return WTERMSIG(platform_defined);
35}
36
37ProcessStatus invoke_in_subprocess(FunctionCaller *func, int timeout_ms) {
38 int pipe_fds[2];
39 if (::pipe(pipedes: pipe_fds) == -1) {
40 delete func;
41 return ProcessStatus::error(error: "pipe(2) failed");
42 }
43
44 // Don't copy the buffers into the child process and print twice.
45 ::fflush(stderr);
46 ::fflush(stdout);
47 pid_t pid = ::fork();
48 if (pid == -1) {
49 delete func;
50 return ProcessStatus::error(error: "fork(2) failed");
51 }
52
53 if (!pid) {
54 (*func)();
55 delete func;
56 ::exit(status: 0);
57 }
58 ::close(fd: pipe_fds[1]);
59
60 struct pollfd poll_fd {
61 .fd: pipe_fds[0], .events: 0, .revents: 0
62 };
63 // No events requested so this call will only return after the timeout or if
64 // the pipes peer was closed, signaling the process exited.
65 if (::poll(fds: &poll_fd, nfds: 1, timeout: timeout_ms) == -1) {
66 delete func;
67 return ProcessStatus::error(error: "poll(2) failed");
68 }
69 // If the pipe wasn't closed by the child yet then timeout has expired.
70 if (!(poll_fd.revents & POLLHUP)) {
71 ::kill(pid: pid, SIGKILL);
72 delete func;
73 return ProcessStatus::timed_out_ps();
74 }
75
76 int wstatus = 0;
77 // Wait on the pid of the subprocess here so it gets collected by the system
78 // and doesn't turn into a zombie.
79 pid_t status = ::waitpid(pid: pid, stat_loc: &wstatus, options: 0);
80 if (status == -1) {
81 delete func;
82 return ProcessStatus::error(error: "waitpid(2) failed");
83 }
84 assert(status == pid);
85 delete func;
86 return {.platform_defined: wstatus};
87}
88
89const char *signal_as_string(int signum) { return ::strsignal(sig: signum); }
90
91} // namespace testutils
92} // namespace LIBC_NAMESPACE_DECL
93

source code of libc/test/UnitTest/ExecuteFunctionUnix.cpp