diff --git a/libc/config/linux/api.td b/libc/config/linux/api.td --- a/libc/config/linux/api.td +++ b/libc/config/linux/api.td @@ -272,7 +272,7 @@ } def SysWaitAPI : PublicAPI<"sys/wait.h"> { - let Types = ["pid_t"]; + let Types = ["pid_t", "struct rusage"]; } def SysSendfileAPI : PublicAPI<"sys/sendfile.h"> { diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -133,6 +133,8 @@ # sys/wait.h entrypoints libc.src.sys.wait.wait + libc.src.sys.wait.wait4 + libc.src.sys.wait.waitpid # unistd.h entrypoints libc.src.unistd.access diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt --- a/libc/include/CMakeLists.txt +++ b/libc/include/CMakeLists.txt @@ -284,6 +284,7 @@ .llvm_libc_common_h .llvm-libc-macros.sys_wait_macros .llvm-libc-types.pid_t + .llvm-libc-types.struct_rusage ) if(NOT LLVM_LIBC_FULL_BUILD) diff --git a/libc/include/llvm-libc-macros/linux/sys-wait-macros.h b/libc/include/llvm-libc-macros/linux/sys-wait-macros.h --- a/libc/include/llvm-libc-macros/linux/sys-wait-macros.h +++ b/libc/include/llvm-libc-macros/linux/sys-wait-macros.h @@ -9,6 +9,11 @@ #ifndef __LLVM_LIBC_MACROS_LINUX_SYS_WAIT_MACROS_H #define __LLVM_LIBC_MACROS_LINUX_SYS_WAIT_MACROS_H +// Wait flags +#define WNOHANG 1 // Do not block +#define WUNTRACED 2 // Report is a child has stopped even if untraced +#define WCONTINUED 8 // Report if a stopped child has been resumed by SIGCONT + // Wait status info macros #define WTERMSIG(status) (((status)&0x7F)) #define WIFEXITED(status) (WTERMSIG(status) == 0) diff --git a/libc/include/llvm-libc-types/CMakeLists.txt b/libc/include/llvm-libc-types/CMakeLists.txt --- a/libc/include/llvm-libc-types/CMakeLists.txt +++ b/libc/include/llvm-libc-types/CMakeLists.txt @@ -43,14 +43,17 @@ add_header(pthread_mutexattr_t HDR pthread_mutexattr_t.h) add_header(pthread_once_t HDR pthread_once_t.h DEPENDS .__futex_word) add_header(rlim_t HDR rlim_t.h) +add_header(time_t HDR time_t.h) +add_header(suseconds_t HDR suseconds_t.h) +add_header(struct_timeval HDR struct_timeval.h DEPENDS .suseconds_t .time_t) add_header(struct_rlimit HDR struct_rlimit.h DEPENDS .rlim_t) +add_header(struct_rusage HDR struct_rusage.h DEPENDS .struct_timeval) add_header(ssize_t HDR ssize_t.h) add_header(struct_dirent HDR struct_dirent.h DEPENDS .ino_t .off_t) add_header(union_sigval HDR union_sigval.h) add_header(siginfo_t HDR siginfo_t.h DEPENDS .union_sigval .pid_t .uid_t) add_header(sigset_t HDR sigset_t.h DEPENDS libc.include.llvm-libc-macros.signal_macros) add_header(struct_sigaction HDR struct_sigaction.h DEPENDS .sigset_t .siginfo_t) -add_header(time_t HDR time_t.h) add_header(struct_timespec HDR struct_timespec.h DEPENDS .time_t) add_header( struct_stat diff --git a/libc/include/llvm-libc-types/struct_rusage.h b/libc/include/llvm-libc-types/struct_rusage.h new file mode 100644 --- /dev/null +++ b/libc/include/llvm-libc-types/struct_rusage.h @@ -0,0 +1,37 @@ +//===-- Definition of type struct rusage ----------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef __LLVM_LIBC_TYPES_STRUCT_RUSAGE_H__ +#define __LLVM_LIBC_TYPES_STRUCT_RUSAGE_H__ + +#include + +struct rusage { + struct timeval ru_utime; + struct timeval ru_stime; +#ifdef __linux__ + // Following fields are linux extensions as expected by the + // linux syscalls. + long ru_maxrss; // Maximum resident set size + long ru_ixrss; // Integral shared memory size + long ru_idrss; // Integral unshared data size + long ru_isrss; // Integral unshared stack size + long ru_minflt; // Page reclaims + long ru_majflt; // Page faults + long ru_nswap; // Swaps + long ru_inblock; // Block input operations + long ru_oublock; // Block output operations + long ru_msgsnd; // Messages sent + long ru_msgrcv; // Messages received + long ru_nsignals; // Signals received + long ru_nvcsw; // Voluntary context switches + long ru_nivcsw; // Involuntary context switches +#endif +}; + +#endif // __LLVM_LIBC_TYPES_STRUCT_RUSAGE_H__ diff --git a/libc/include/llvm-libc-types/struct_timeval.h b/libc/include/llvm-libc-types/struct_timeval.h new file mode 100644 --- /dev/null +++ b/libc/include/llvm-libc-types/struct_timeval.h @@ -0,0 +1,20 @@ +//===-- Definition of struct timeval -------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef __LLVM_LIBC_TYPES_TIMEVAL_H__ +#define __LLVM_LIBC_TYPES_TIMEVAL_H__ + +#include +#include + +struct timeval { + time_t tv_sec; // Seconds + suseconds_t tv_usec; // Micro seconds +}; + +#endif // __LLVM_LIBC_TYPES_TIMEVAL_H__ diff --git a/libc/include/llvm-libc-types/suseconds_t.h b/libc/include/llvm-libc-types/suseconds_t.h new file mode 100644 --- /dev/null +++ b/libc/include/llvm-libc-types/suseconds_t.h @@ -0,0 +1,14 @@ +//===-- Definition of suseconds_t type ------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef __LLVM_LIBC_TYPES_SUSECONDS_T_H__ +#define __LLVM_LIBC_TYPES_SUSECONDS_T_H__ + +typedef __INT32_TYPE__ suseconds_t; + +#endif // __LLVM_LIBC_TYPES_SUSECONDS_T_H__ diff --git a/libc/spec/bsd_ext.td b/libc/spec/bsd_ext.td --- a/libc/spec/bsd_ext.td +++ b/libc/spec/bsd_ext.td @@ -18,7 +18,22 @@ ] >; + HeaderSpec SysWait = HeaderSpec< + "sys/wait.h", + [], // Macros + [StructRUsage], // Types + [], // Enumerations + [ + FunctionSpec< + "wait4", + RetValSpec, + [ArgSpec, ArgSpec, ArgSpec, ArgSpec] + > + ] + >; + let Headers = [ String, + SysWait, ]; } diff --git a/libc/spec/posix.td b/libc/spec/posix.td --- a/libc/spec/posix.td +++ b/libc/spec/posix.td @@ -971,13 +971,18 @@ HeaderSpec SysWait = HeaderSpec< "sys/wait.h", [], // Macros - [PidT], + [PidT, StructRUsage], [], // Enumerations [ FunctionSpec< "wait", RetValSpec, [ArgSpec] + >, + FunctionSpec< + "waitpid", + RetValSpec, + [ArgSpec, ArgSpec, ArgSpec] > ] >; diff --git a/libc/spec/spec.td b/libc/spec/spec.td --- a/libc/spec/spec.td +++ b/libc/spec/spec.td @@ -115,6 +115,8 @@ def PThreadTType : NamedType<"pthread_t">; def PidT : NamedType<"pid_t">; +def StructRUsage : NamedType<"struct rusage">; +def StructRUsagePtr : PtrType; //added because __assert_fail needs it. def UnsignedType : NamedType<"unsigned">; diff --git a/libc/src/sys/wait/CMakeLists.txt b/libc/src/sys/wait/CMakeLists.txt --- a/libc/src/sys/wait/CMakeLists.txt +++ b/libc/src/sys/wait/CMakeLists.txt @@ -8,3 +8,17 @@ DEPENDS .${LIBC_TARGET_OS}.wait ) + +add_entrypoint_object( + wait4 + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.wait4 +) + +add_entrypoint_object( + waitpid + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.waitpid +) diff --git a/libc/src/sys/wait/linux/CMakeLists.txt b/libc/src/sys/wait/linux/CMakeLists.txt --- a/libc/src/sys/wait/linux/CMakeLists.txt +++ b/libc/src/sys/wait/linux/CMakeLists.txt @@ -11,3 +11,31 @@ libc.src.__support.OSUtil.osutil libc.src.errno.errno ) + +add_entrypoint_object( + wait4 + SRCS + wait4.cpp + HDRS + ../wait4.h + DEPENDS + libc.include.errno + libc.include.sys_wait + libc.include.sys_syscall + libc.src.__support.OSUtil.osutil + libc.src.errno.errno +) + +add_entrypoint_object( + waitpid + SRCS + waitpid.cpp + HDRS + ../waitpid.h + DEPENDS + libc.include.errno + libc.include.sys_wait + libc.include.sys_syscall + libc.src.__support.OSUtil.osutil + libc.src.errno.errno +) diff --git a/libc/src/sys/wait/linux/wait4.cpp b/libc/src/sys/wait/linux/wait4.cpp new file mode 100644 --- /dev/null +++ b/libc/src/sys/wait/linux/wait4.cpp @@ -0,0 +1,31 @@ +//===-- Linux implementation of wait4 -------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/sys/wait/wait4.h" + +#include "src/__support/OSUtil/syscall.h" // For internal syscall function. +#include "src/__support/common.h" + +#include +#include // For syscall numbers. +#include + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(pid_t, wait4, + (pid_t pid, int *wait_status, int options, + struct rusage *usage)) { + pid = __llvm_libc::syscall_impl(SYS_wait4, pid, wait_status, options, usage); + if (pid < 0) { + errno = -pid; + return -1; + } + return pid; +} + +} // namespace __llvm_libc diff --git a/libc/src/sys/wait/linux/waitpid.cpp b/libc/src/sys/wait/linux/waitpid.cpp new file mode 100644 --- /dev/null +++ b/libc/src/sys/wait/linux/waitpid.cpp @@ -0,0 +1,29 @@ +//===-- Linux implementation of waitpid -----------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/sys/wait/waitpid.h" + +#include "src/__support/OSUtil/syscall.h" // For internal syscall function. +#include "src/__support/common.h" + +#include +#include // For syscall numbers. +#include + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(pid_t, waitpid, (pid_t pid, int *wait_status, int options)) { + pid = __llvm_libc::syscall_impl(SYS_wait4, pid, wait_status, options, 0); + if (pid < 0) { + errno = -pid; + return -1; + } + return pid; +} + +} // namespace __llvm_libc diff --git a/libc/src/sys/wait/wait4.h b/libc/src/sys/wait/wait4.h new file mode 100644 --- /dev/null +++ b/libc/src/sys/wait/wait4.h @@ -0,0 +1,20 @@ +//===-- Implementation header for wait4 -------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_SYS_WAIT_WAIT4_H +#define LLVM_LIBC_SRC_SYS_WAIT_WAIT4_H + +#include + +namespace __llvm_libc { + +pid_t wait4(pid_t pid, int *waitstatus, int options, struct rusage *usage); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_SYS_WAIT_WAIT4_H diff --git a/libc/src/sys/wait/waitpid.h b/libc/src/sys/wait/waitpid.h new file mode 100644 --- /dev/null +++ b/libc/src/sys/wait/waitpid.h @@ -0,0 +1,20 @@ +//===-- Implementation header for waitpid -----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_SYS_WAIT_WAITPID_H +#define LLVM_LIBC_SRC_SYS_WAIT_WAITPID_H + +#include + +namespace __llvm_libc { + +pid_t waitpid(pid_t pid, int *waitstatus, int options); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_SYS_WAIT_WAITPID_H diff --git a/libc/test/integration/src/unistd/CMakeLists.txt b/libc/test/integration/src/unistd/CMakeLists.txt --- a/libc/test/integration/src/unistd/CMakeLists.txt +++ b/libc/test/integration/src/unistd/CMakeLists.txt @@ -16,5 +16,7 @@ libc.include.unistd libc.src.signal.raise libc.src.sys.wait.wait + libc.src.sys.wait.wait4 + libc.src.sys.wait.waitpid libc.src.unistd.fork ) diff --git a/libc/test/integration/src/unistd/fork_test.cpp b/libc/test/integration/src/unistd/fork_test.cpp --- a/libc/test/integration/src/unistd/fork_test.cpp +++ b/libc/test/integration/src/unistd/fork_test.cpp @@ -8,6 +8,8 @@ #include "src/signal/raise.h" #include "src/sys/wait/wait.h" +#include "src/sys/wait/wait4.h" +#include "src/sys/wait/waitpid.h" #include "src/unistd/fork.h" #include "utils/IntegrationTest/test.h" @@ -17,6 +19,11 @@ #include #include +// The tests wait4 and waitpid are present as tests for those functions +// really and not for the fork function. They are here along with the tests +// for fork because it is convenient to invoke and test them after forking +// a child. + void fork_and_wait_normal_exit() { pid_t pid = __llvm_libc::fork(); if (pid == 0) @@ -29,6 +36,33 @@ ASSERT_TRUE(WIFEXITED(status)); } +void fork_and_wait4_normal_exit() { + pid_t pid = __llvm_libc::fork(); + if (pid == 0) + return; // Just end without any thing special. + ASSERT_TRUE(pid > 0); + int status; + struct rusage usage; + usage.ru_utime = {0, 0}; + usage.ru_stime = {0, 0}; + pid_t cpid = __llvm_libc::wait4(pid, &status, 0, &usage); + ASSERT_TRUE(cpid > 0); + ASSERT_EQ(cpid, pid); + ASSERT_TRUE(WIFEXITED(status)); +} + +void fork_and_waitpid_normal_exit() { + pid_t pid = __llvm_libc::fork(); + if (pid == 0) + return; // Just end without any thing special. + ASSERT_TRUE(pid > 0); + int status; + pid_t cpid = __llvm_libc::waitpid(pid, &status, 0); + ASSERT_TRUE(cpid > 0); + ASSERT_EQ(cpid, pid); + ASSERT_TRUE(WIFEXITED(status)); +} + void fork_and_wait_signal_exit() { pid_t pid = __llvm_libc::fork(); if (pid == 0) @@ -42,8 +76,41 @@ ASSERT_TRUE(WTERMSIG(status) == SIGUSR1); } +void fork_and_wait4_signal_exit() { + pid_t pid = __llvm_libc::fork(); + if (pid == 0) + __llvm_libc::raise(SIGUSR1); + ASSERT_TRUE(pid > 0); + int status; + struct rusage usage; + usage.ru_utime = {0, 0}; + usage.ru_stime = {0, 0}; + pid_t cpid = __llvm_libc::wait4(pid, &status, 0, &usage); + ASSERT_TRUE(cpid > 0); + ASSERT_EQ(cpid, pid); + ASSERT_FALSE(WIFEXITED(status)); + ASSERT_TRUE(WTERMSIG(status) == SIGUSR1); +} + +void fork_and_waitpid_signal_exit() { + pid_t pid = __llvm_libc::fork(); + if (pid == 0) + __llvm_libc::raise(SIGUSR1); + ASSERT_TRUE(pid > 0); + int status; + pid_t cpid = __llvm_libc::waitpid(pid, &status, 0); + ASSERT_TRUE(cpid > 0); + ASSERT_EQ(cpid, pid); + ASSERT_FALSE(WIFEXITED(status)); + ASSERT_TRUE(WTERMSIG(status) == SIGUSR1); +} + TEST_MAIN(int argc, char **argv, char **envp) { fork_and_wait_normal_exit(); + fork_and_wait4_normal_exit(); + fork_and_waitpid_normal_exit(); fork_and_wait_signal_exit(); + fork_and_wait4_signal_exit(); + fork_and_waitpid_signal_exit(); return 0; } diff --git a/libc/test/src/sys/CMakeLists.txt b/libc/test/src/sys/CMakeLists.txt --- a/libc/test/src/sys/CMakeLists.txt +++ b/libc/test/src/sys/CMakeLists.txt @@ -3,3 +3,4 @@ add_subdirectory(sendfile) add_subdirectory(stat) add_subdirectory(utsname) +add_subdirectory(wait) diff --git a/libc/test/src/sys/wait/CMakeLists.txt b/libc/test/src/sys/wait/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/test/src/sys/wait/CMakeLists.txt @@ -0,0 +1,25 @@ +add_libc_testsuite(libc_sys_wait_unittests) + +add_libc_unittest( + waitpid_test + SUITE + libc_sys_stat_unittests + SRCS + waitpid_test.cpp + DEPENDS + libc.include.errno + libc.include.sys_wait + libc.src.sys.wait.waitpid +) + +add_libc_unittest( + wait4_test + SUITE + libc_sys_stat_unittests + SRCS + wait4_test.cpp + DEPENDS + libc.include.errno + libc.include.sys_wait + libc.src.sys.wait.wait4 +) diff --git a/libc/test/src/sys/wait/wait4_test.cpp b/libc/test/src/sys/wait/wait4_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/sys/wait/wait4_test.cpp @@ -0,0 +1,23 @@ +//===-- Unittests for wait4 -----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/sys/wait/wait4.h" +#include "test/ErrnoSetterMatcher.h" +#include "utils/UnitTest/Test.h" + +#include +#include + +// The test here is a simpl test for WNOHANG functionality. For a more +// involved test, look at fork_test. + +TEST(LlvmLibcwait4Test, NoHangTest) { + using __llvm_libc::testing::ErrnoSetterMatcher::Fails; + int status; + ASSERT_THAT(__llvm_libc::wait4(-1, &status, WNOHANG, nullptr), Fails(ECHILD)); +} diff --git a/libc/test/src/sys/wait/waitpid_test.cpp b/libc/test/src/sys/wait/waitpid_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/sys/wait/waitpid_test.cpp @@ -0,0 +1,23 @@ +//===-- Unittests for waitpid ---------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/sys/wait/waitpid.h" +#include "test/ErrnoSetterMatcher.h" +#include "utils/UnitTest/Test.h" + +#include +#include + +// The test here is a simpl test for WNOHANG functionality. For a more +// involved test, look at fork_test. + +TEST(LlvmLibcWaitPidTest, NoHangTest) { + using __llvm_libc::testing::ErrnoSetterMatcher::Fails; + int status; + ASSERT_THAT(__llvm_libc::waitpid(-1, &status, WNOHANG), Fails(ECHILD)); +}