[libc-commits] [libc] 9e2e9b3 - [libc] Port process utilities to hermetic mode and enable some tests (#209999)

via libc-commits libc-commits at lists.llvm.org
Mon Jul 20 00:46:06 PDT 2026


Author: Pavel Labath
Date: 2026-07-20T09:46:00+02:00
New Revision: 9e2e9b33e14808541c0470b92ade34bee6608bf6

URL: https://github.com/llvm/llvm-project/commit/9e2e9b33e14808541c0470b92ade34bee6608bf6
DIFF: https://github.com/llvm/llvm-project/commit/9e2e9b33e14808541c0470b92ade34bee6608bf6.diff

LOG: [libc] Port process utilities to hermetic mode and enable some tests (#209999)

This is achieved by calling internal libc functions in hermetic mode. In
the overlay mode I keep calling the system functions so that the tests
work even on systems which don't have these implemented.

I also needed to implement the delete operators as both the libc proper
and the test framework uses them nowadays.

This is sufficient to enable all stdlib tests in hermetic mode, except
for one, which actually exposes a bug in the implementation. I'll deal
with that in a separate patch.

Added: 
    

Modified: 
    libc/cmake/modules/LLVMLibCTestRules.cmake
    libc/test/UnitTest/CMakeLists.txt
    libc/test/UnitTest/ExecuteFunctionUnix.cpp
    libc/test/UnitTest/HermeticTestUtils.cpp
    libc/test/UnitTest/LibcDeathTestExecutors.cpp
    libc/test/src/stdlib/CMakeLists.txt
    utils/bazel/llvm-project-overlay/libc/test/UnitTest/BUILD.bazel

Removed: 
    


################################################################################
diff  --git a/libc/cmake/modules/LLVMLibCTestRules.cmake b/libc/cmake/modules/LLVMLibCTestRules.cmake
index b861128687868..fdc7764c65177 100644
--- a/libc/cmake/modules/LLVMLibCTestRules.cmake
+++ b/libc/cmake/modules/LLVMLibCTestRules.cmake
@@ -780,6 +780,18 @@ function(add_libc_hermetic test_name)
       libc.src.string.memset
       libc.src.strings.bcmp
       libc.src.strings.bzero
+      # Syscalls used by death tests.
+      libc.src.poll.poll
+      libc.src.signal.kill
+      libc.src.stdio.fflush
+      libc.src.stdio.stderr
+      libc.src.stdio.stdout
+      libc.src.stdlib.exit
+      libc.src.string.strsignal
+      libc.src.sys.wait.waitpid
+      libc.src.unistd.close
+      libc.src.unistd.fork
+      libc.src.unistd.pipe
   )
 
   if(libc.src.compiler.__stack_chk_fail IN_LIST TARGET_LLVMLIBC_ENTRYPOINTS)
@@ -1009,6 +1021,7 @@ function(add_libc_test test_name)
       ${test_name}.__hermetic__
       LINK_LIBRARIES
         LibcTest.hermetic
+        LibcDeathTestExecutors.hermetic
       ${LIBC_TEST_UNPARSED_ARGUMENTS}
     )
     get_fq_target_name(${test_name} fq_test_name)

diff  --git a/libc/test/UnitTest/CMakeLists.txt b/libc/test/UnitTest/CMakeLists.txt
index 4a47597ae3a45..0ce0ec3c54a31 100644
--- a/libc/test/UnitTest/CMakeLists.txt
+++ b/libc/test/UnitTest/CMakeLists.txt
@@ -40,7 +40,7 @@ function(add_unittest_framework_library name)
   endif()
   target_compile_options(${name}.unit PRIVATE ${compile_options})
 
-    _get_hermetic_test_compile_options(compile_options "")
+  _get_hermetic_test_compile_options(compile_options "")
   target_include_directories(${name}.hermetic PRIVATE ${LIBC_INCLUDE_DIR})
   target_compile_options(${name}.hermetic PRIVATE ${compile_options} -nostdinc++)
 
@@ -100,6 +100,17 @@ set(libc_death_test_srcs LibcDeathTestExecutors.cpp)
 if(${LIBC_TARGET_OS} STREQUAL "linux" OR ${LIBC_TARGET_OS} STREQUAL "darwin")
   list(APPEND libc_death_test_srcs ExecuteFunctionUnix.cpp)
 endif()
+set(libc_death_tess_deps libc.hdr.stdint_proxy)
+if (LLVM_LIBC_FULL_BUILD)
+  list(APPEND libc_death_tess_deps
+    libc.include.llvm-libc-macros.poll-macros
+    libc.include.llvm-libc-macros.signal_macros
+    libc.include.llvm-libc-macros.sys_wait_macros
+    libc.include.llvm-libc-types.pid_t
+    libc.include.llvm-libc-types.struct_pollfd
+    libc.src.__support.libc_assert
+  )
+endif()
 
 add_unittest_framework_library(
   LibcDeathTestExecutors
@@ -108,7 +119,7 @@ add_unittest_framework_library(
   HDRS
     ExecuteFunction.h
   DEPENDS
-    libc.hdr.stdint_proxy
+    ${libc_death_tess_deps}
 )
 
 add_unittest_framework_library(

diff  --git a/libc/test/UnitTest/ExecuteFunctionUnix.cpp b/libc/test/UnitTest/ExecuteFunctionUnix.cpp
index ab18f7a2ebf52..6ca867d5e65df 100644
--- a/libc/test/UnitTest/ExecuteFunctionUnix.cpp
+++ b/libc/test/UnitTest/ExecuteFunctionUnix.cpp
@@ -7,9 +7,32 @@
 //===----------------------------------------------------------------------===//
 
 #include "ExecuteFunction.h"
+#include "src/__support/libc_assert.h"
 #include "src/__support/macros/config.h"
 #include "test/UnitTest/ExecuteFunction.h" // FunctionCaller
-#include <assert.h>
+
+#ifdef LIBC_FULL_BUILD
+#include "include/llvm-libc-macros/poll-macros.h"
+#include "include/llvm-libc-macros/signal-macros.h"
+#include "include/llvm-libc-macros/sys-wait-macros.h"
+#include "include/llvm-libc-types/pid_t.h"
+#include "include/llvm-libc-types/struct_pollfd.h"
+#include "src/__support/libc_assert.h"
+#include "src/poll/poll.h"
+#include "src/signal/kill.h"
+#include "src/stdio/fflush.h"
+#include "src/stdio/stderr.h"
+#include "src/stdio/stdout.h"
+#include "src/stdlib/exit.h"
+#include "src/string/strsignal.h"
+#include "src/sys/wait/waitpid.h"
+#include "src/unistd/close.h"
+#include "src/unistd/fork.h"
+#include "src/unistd/pipe.h"
+
+#define LIBC_IMPL LIBC_NAMESPACE
+
+#else // Overlay mode
 #include <poll.h>
 #include <signal.h>
 #include <stdio.h>
@@ -18,13 +41,16 @@
 #include <sys/wait.h>
 #include <unistd.h>
 
+#define LIBC_IMPL
+#endif
+
 namespace LIBC_NAMESPACE_DECL {
 namespace testutils {
 
 bool ProcessStatus::exited_normally() { return WIFEXITED(platform_defined); }
 
 int ProcessStatus::get_exit_code() {
-  assert(exited_normally() && "Abnormal termination, no exit code");
+  LIBC_ASSERT(exited_normally() && "Abnormal termination, no exit code");
   return WEXITSTATUS(platform_defined);
 }
 
@@ -36,15 +62,15 @@ int ProcessStatus::get_fatal_signal() {
 
 ProcessStatus invoke_in_subprocess(FunctionCaller *func, int timeout_ms) {
   int pipe_fds[2];
-  if (::pipe(pipe_fds) == -1) {
+  if (LIBC_IMPL::pipe(pipe_fds) == -1) {
     delete func;
     return ProcessStatus::error("pipe(2) failed");
   }
 
   // Don't copy the buffers into the child process and print twice.
-  ::fflush(stderr);
-  ::fflush(stdout);
-  pid_t pid = ::fork();
+  LIBC_IMPL::fflush(stderr);
+  LIBC_IMPL::fflush(stdout);
+  pid_t pid = LIBC_IMPL::fork();
   if (pid == -1) {
     delete func;
     return ProcessStatus::error("fork(2) failed");
@@ -53,20 +79,20 @@ ProcessStatus invoke_in_subprocess(FunctionCaller *func, int timeout_ms) {
   if (!pid) {
     (*func)();
     delete func;
-    ::exit(0);
+    LIBC_IMPL::exit(0);
   }
-  ::close(pipe_fds[1]);
+  LIBC_IMPL::close(pipe_fds[1]);
 
   pollfd poll_fd{pipe_fds[0], POLLIN, 0};
   // No events requested so this call will only return after the timeout or if
   // the pipes peer was closed, signaling the process exited.
-  if (::poll(&poll_fd, 1, timeout_ms) == -1) {
+  if (LIBC_IMPL::poll(&poll_fd, 1, timeout_ms) == -1) {
     delete func;
     return ProcessStatus::error("poll(2) failed");
   }
   // If the pipe wasn't closed by the child yet then timeout has expired.
   if (!(poll_fd.revents & POLLHUP)) {
-    ::kill(pid, SIGKILL);
+    LIBC_IMPL::kill(pid, SIGKILL);
     delete func;
     return ProcessStatus::timed_out_ps();
   }
@@ -74,17 +100,19 @@ ProcessStatus invoke_in_subprocess(FunctionCaller *func, int timeout_ms) {
   int wstatus = 0;
   // Wait on the pid of the subprocess here so it gets collected by the system
   // and doesn't turn into a zombie.
-  pid_t status = ::waitpid(pid, &wstatus, 0);
+  pid_t status = LIBC_IMPL::waitpid(pid, &wstatus, 0);
   if (status == -1) {
     delete func;
     return ProcessStatus::error("waitpid(2) failed");
   }
-  assert(status == pid);
+  LIBC_ASSERT(status == pid);
   delete func;
   return {wstatus};
 }
 
-const char *signal_as_string(int signum) { return ::strsignal(signum); }
+const char *signal_as_string(int signum) {
+  return LIBC_IMPL::strsignal(signum);
+}
 
 } // namespace testutils
 } // namespace LIBC_NAMESPACE_DECL

diff  --git a/libc/test/UnitTest/HermeticTestUtils.cpp b/libc/test/UnitTest/HermeticTestUtils.cpp
index 6fa1b0177f00b..5ce661b2202c0 100644
--- a/libc/test/UnitTest/HermeticTestUtils.cpp
+++ b/libc/test/UnitTest/HermeticTestUtils.cpp
@@ -132,15 +132,9 @@ void *operator new(size_t size) { return malloc(size); }
 
 void *operator new[](size_t size) { return malloc(size); }
 
-void operator delete(void *) {
-  // The libc runtime should not use the global delete operator. Hence,
-  // we just trap here to catch any such accidental usages.
-  __builtin_trap();
-}
+void operator delete(void *ptr) { free(ptr); }
 
-void operator delete([[maybe_unused]] void *ptr, [[maybe_unused]] size_t size) {
-  __builtin_trap();
-}
+void operator delete(void *ptr, [[maybe_unused]] size_t size) { free(ptr); }
 
 // Defining members in the std namespace is not preferred. But, we do it here
 // so that we can use it to define the operator new which takes std::align_val_t
@@ -149,11 +143,8 @@ namespace std {
 enum class align_val_t : size_t {};
 } // namespace std
 
-void operator delete([[maybe_unused]] void *mem, std::align_val_t) noexcept {
-  __builtin_trap();
-}
+void operator delete(void *ptr, std::align_val_t) noexcept { free(ptr); }
 
-void operator delete([[maybe_unused]] void *mem, unsigned int,
-                     std::align_val_t) noexcept {
-  __builtin_trap();
+void operator delete(void *ptr, unsigned int, std::align_val_t) noexcept {
+  free(ptr);
 }

diff  --git a/libc/test/UnitTest/LibcDeathTestExecutors.cpp b/libc/test/UnitTest/LibcDeathTestExecutors.cpp
index 5f8d7b86a5187..9a0a749a3404a 100644
--- a/libc/test/UnitTest/LibcDeathTestExecutors.cpp
+++ b/libc/test/UnitTest/LibcDeathTestExecutors.cpp
@@ -8,12 +8,11 @@
 
 #include "LibcTest.h"
 
+#include "src/__support/libc_assert.h"
 #include "src/__support/macros/config.h"
 #include "test/UnitTest/ExecuteFunction.h"
 #include "test/UnitTest/TestLogger.h"
 
-#include <assert.h>
-
 namespace {
 constexpr unsigned TIMEOUT_MS = 10000;
 } // Anonymous namespace
@@ -51,7 +50,7 @@ bool Test::testProcessKilled(testutils::FunctionCaller *Func, int Signal,
   }
 
   int KilledBy = Result.get_fatal_signal();
-  assert(KilledBy != 0 && "Not killed by any signal");
+  LIBC_ASSERT(KilledBy != 0 && "Not killed by any signal");
   if (Signal == -1 || KilledBy == Signal)
     return true;
 

diff  --git a/libc/test/src/stdlib/CMakeLists.txt b/libc/test/src/stdlib/CMakeLists.txt
index 761aa4f792e29..6a1b0069424d3 100644
--- a/libc/test/src/stdlib/CMakeLists.txt
+++ b/libc/test/src/stdlib/CMakeLists.txt
@@ -509,8 +509,6 @@ if(LLVM_LIBC_FULL_BUILD)
 
   add_libc_test(
     _Exit_test
-    # The EXPECT_EXITS test is only availible for unit tests.
-    UNIT_TEST_ONLY
     SUITE
       libc-stdlib-tests
     SRCS
@@ -522,8 +520,6 @@ if(LLVM_LIBC_FULL_BUILD)
 
   add_libc_test(
     exit_test
-    # The EXPECT_EXITS test is only availible for unit tests.
-    UNIT_TEST_ONLY
     SUITE
       libc-stdlib-tests
     SRCS
@@ -535,7 +531,7 @@ if(LLVM_LIBC_FULL_BUILD)
 
   add_libc_test(
     atexit_test
-    # The EXPECT_EXITS test is only availible for unit tests.
+    # TODO: Fix this test in hermetic mode.
     UNIT_TEST_ONLY
     SUITE
       libc-stdlib-tests
@@ -550,8 +546,6 @@ if(LLVM_LIBC_FULL_BUILD)
 
   add_libc_test(
     at_quick_exit_test
-    # The EXPECT_EXITS test is only availible for unit tests.
-    UNIT_TEST_ONLY
     SUITE
       libc-stdlib-tests
     SRCS
@@ -565,8 +559,6 @@ if(LLVM_LIBC_FULL_BUILD)
 
   add_libc_test(
     abort_test
-    # The EXPECT_DEATH test is only availible for unit tests.
-    UNIT_TEST_ONLY
     SUITE
       libc-stdlib-tests
     SRCS
@@ -580,8 +572,6 @@ if(LLVM_LIBC_FULL_BUILD)
 
   add_libc_test(
     quick_exit_test
-    # The EXPECT_EXITS test is only availible for unit tests.
-    UNIT_TEST_ONLY
     SUITE
       libc-stdlib-tests
     SRCS

diff  --git a/utils/bazel/llvm-project-overlay/libc/test/UnitTest/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/test/UnitTest/BUILD.bazel
index 9eacb93043352..2f62ab26bca97 100644
--- a/utils/bazel/llvm-project-overlay/libc/test/UnitTest/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/libc/test/UnitTest/BUILD.bazel
@@ -61,6 +61,7 @@ libc_test_library(
         "//libc:__support_fputil_fp_bits",
         "//libc:__support_fputil_fpbits_str",
         "//libc:__support_fputil_rounding_mode",
+        "//libc:__support_libc_assert",
         "//libc:__support_libc_errno",
         "//libc:__support_macros_config",
         "//libc:__support_macros_properties_architectures",


        


More information about the libc-commits mailing list