[libc-commits] [libc] [libc] Enabling code coverage via Linux syscalls (PR #213271)

Tapiwa Gonga via libc-commits libc-commits at lists.llvm.org
Wed Sep 2 02:51:24 PDT 2026


https://github.com/tapiwagonga updated https://github.com/llvm/llvm-project/pull/213271

>From b9edfdb19605e74a9c2733e81e51319691721d62 Mon Sep 17 00:00:00 2001
From: Tapiwa Gonga <tapiwagonga at google.com>
Date: Fri, 7 Aug 2026 10:22:56 +0000
Subject: [PATCH 1/7] [libc][test] Add freestanding code coverage support to
 LibcTestMain

---
 libc/CMakeLists.txt                           |  28 +++
 .../linux/syscall_wrappers/CMakeLists.txt     |  14 ++
 .../OSUtil/linux/syscall_wrappers/getpid.h    |  28 +++
 .../OSUtil/linux/syscall_wrappers/raise.h     |   3 +-
 libc/test/UnitTest/Coverage.h                 | 162 ++++++++++++++++++
 libc/test/UnitTest/ExecuteFunctionUnix.cpp    |  48 ++++++
 libc/test/UnitTest/LibcTestMain.cpp           |  14 +-
 7 files changed, 293 insertions(+), 4 deletions(-)
 create mode 100644 libc/src/__support/OSUtil/linux/syscall_wrappers/getpid.h
 create mode 100644 libc/test/UnitTest/Coverage.h

diff --git a/libc/CMakeLists.txt b/libc/CMakeLists.txt
index 5ef88bd31e76c..6c73f0ada73e2 100644
--- a/libc/CMakeLists.txt
+++ b/libc/CMakeLists.txt
@@ -91,6 +91,33 @@ endif()
 set(LIBC_LINK_OPTIONS_DEFAULT "" CACHE STRING "Arguments used when linking.")
 set(LIBC_TEST_LINK_OPTIONS_DEFAULT "" CACHE STRING "Common link options for all the tests.")
 
+option(LLVM_LIBC_ENABLE_COVERAGE "Build libc with coverage instrumentation" OFF)
+if(LLVM_LIBC_ENABLE_COVERAGE)
+  add_compile_definitions(LLVM_LIBC_ENABLE_COVERAGE)
+  list(APPEND LIBC_COMPILE_OPTIONS_DEFAULT "-fprofile-instr-generate" "-fcoverage-mapping")
+  list(APPEND LIBC_TEST_COMPILE_OPTIONS_DEFAULT "-fprofile-instr-generate" "-fcoverage-mapping")
+  list(APPEND LIBC_TEST_LINK_OPTIONS_DEFAULT "-fprofile-instr-generate" "-fcoverage-mapping")
+
+  # When building with -nostdlib, the compiler does not automatically link the profiling runtime.
+  # We must explicitly query the compiler for the exact architecture-specific profile library path.
+  execute_process(
+    COMMAND ${CMAKE_CXX_COMPILER} --print-libgcc-file-name --rtlib=compiler-rt
+    OUTPUT_VARIABLE COMPILER_RT_BUILTINS
+    OUTPUT_STRIP_TRAILING_WHITESPACE
+    RESULT_VARIABLE COMPILER_RT_RETURN_CODE
+  )
+  if(COMPILER_RT_RETURN_CODE EQUAL 0)
+    string(REPLACE "builtins" "profile" COMPILER_RT_PROFILE "${COMPILER_RT_BUILTINS}")
+    if(EXISTS "${COMPILER_RT_PROFILE}")
+      list(APPEND LIBC_TEST_LINK_OPTIONS_DEFAULT "${COMPILER_RT_PROFILE}")
+    else()
+      message(WARNING "Coverage profiling runtime not found at ${COMPILER_RT_PROFILE}")
+    endif()
+  else()
+    message(WARNING "Failed to locate compiler-rt builtins library for coverage")
+  endif()
+endif()
+
 set(LIBC_TEST_CMD "" CACHE STRING
   "The full test command in the form <command> binary=@BINARY@, if using another program to test (e.g. QEMU)")
 set(LIBC_TEST_HERMETIC_ONLY "" OFF CACHE BOOL "Only enable hermetic tests.")
@@ -157,6 +184,7 @@ if(LLVM_LIBC_FULL_BUILD)
 else()
   set(LLVM_LIBC_OVERLAY ON)
 endif()
+
 option(LLVM_LIBC_IMPLEMENTATION_DEFINED_TEST_BEHAVIOR "Build LLVM libc tests assuming our implementation-defined behavior" ON)
 option(LLVM_LIBC_ENABLE_LINTING "Enables linting of libc source files" OFF)
 option(LLVM_LIBC_ALL_HEADERS "Outputs all functions in header files, regardless of whether they are enabled on this target" OFF)
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt b/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
index 2b22bb8e733de..1d79c82d94b80 100644
--- a/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
@@ -26,6 +26,19 @@ add_header_library(
     libc.include.sys_syscall
 )
 
+add_header_library(
+  getpid
+  HDRS
+    getpid.h
+  DEPENDS
+    libc.src.__support.OSUtil.osutil
+    libc.src.__support.common
+    libc.src.__support.macros.config
+    libc.hdr.types.pid_t
+    libc.include.sys_syscall
+)
+
+
 add_header_library(
   sched_getaffinity
   HDRS
@@ -397,6 +410,7 @@ add_header_library(
     raise.h
   DEPENDS
     libc.src.__support.OSUtil.osutil
+    libc.src.__support.OSUtil.linux.syscall_wrappers.getpid
     libc.src.__support.OSUtil.linux.syscall_wrappers.rt_sigprocmask
     libc.src.__support.common
     libc.src.__support.error_or
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/getpid.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/getpid.h
new file mode 100644
index 0000000000000..6b1d2686cac4b
--- /dev/null
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/getpid.h
@@ -0,0 +1,28 @@
+//===-- Implementation header for getpid ------------------------*- 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___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_GETPID_H
+#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_GETPID_H
+
+#include "hdr/types/pid_t.h"
+#include "src/__support/OSUtil/linux/syscall.h" // syscall_impl
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+#include <sys/syscall.h> // For syscall numbers
+
+namespace LIBC_NAMESPACE_DECL {
+namespace linux_syscalls {
+
+LIBC_INLINE pid_t getpid() {
+  return syscall_impl<pid_t>(SYS_getpid);
+}
+
+} // namespace linux_syscalls
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_GETPID_H
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/raise.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/raise.h
index 2b61e4e624c89..b7c22a51bfe24 100644
--- a/libc/src/__support/OSUtil/linux/syscall_wrappers/raise.h
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/raise.h
@@ -12,6 +12,7 @@
 #include "hdr/signal_macros.h"
 #include "hdr/types/sigset_t.h"
 #include "src/__support/OSUtil/linux/syscall.h" // syscall_impl
+#include "src/__support/OSUtil/linux/syscall_wrappers/getpid.h"
 #include "src/__support/OSUtil/linux/syscall_wrappers/rt_sigprocmask.h"
 #include "src/__support/common.h"
 #include "src/__support/error_or.h"
@@ -46,7 +47,7 @@ LIBC_INLINE ErrorOr<int> raise(int sig) {
     if (!status.has_value())
       return status;
 
-    long pid = syscall_impl<long>(SYS_getpid);
+    pid_t pid = linux_syscalls::getpid();
     if (pid < 0)
       return Error(-static_cast<int>(pid));
 
diff --git a/libc/test/UnitTest/Coverage.h b/libc/test/UnitTest/Coverage.h
new file mode 100644
index 0000000000000..e142775c1a7e0
--- /dev/null
+++ b/libc/test/UnitTest/Coverage.h
@@ -0,0 +1,162 @@
+//===-- Freestanding Code Coverage Extraction Support -----------*- 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_TEST_UNITTEST_COVERAGE_H
+#define LLVM_LIBC_TEST_UNITTEST_COVERAGE_H
+
+#include "src/__support/macros/properties/os.h"
+
+#if defined(LIBC_TARGET_OS_IS_LINUX)
+
+#include "hdr/errno_macros.h"
+#include "hdr/fcntl_macros.h"
+#include "hdr/sys_mman_macros.h"
+#include "src/__support/CPP/optional.h"
+#include "src/__support/CPP/span.h"
+#include "src/__support/CPP/string_view.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/close.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/getpid.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/mmap.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/munmap.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/open.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/write.h"
+#include "src/__support/integer_to_string.h"
+#include "src/string/memory_utils/inline_memcpy.h"
+#include <sys/syscall.h>
+
+extern "C" {
+__attribute__((weak)) uint64_t __llvm_profile_get_size_for_buffer();
+__attribute__((weak)) int __llvm_profile_write_buffer(char *buffer);
+__attribute__((weak)) void
+__llvm_profile_set_filename(const char *filename_pat);
+
+// Override compiler-rt's weak filename symbol. This redirects the default
+// filename to /dev/null to silence the default dumper by default.
+__attribute__((weak)) char __llvm_profile_filename[] = "/dev/null";
+}
+
+namespace {
+
+using LIBC_NAMESPACE::cpp::string_view;
+
+struct FixedSizeBuffer {
+  char data[64];
+  size_t idx = 0;
+
+  FixedSizeBuffer() { data[0] = '\0'; }
+
+  bool append(string_view str) {
+    size_t len = str.size();
+    if (idx + len >= sizeof(data))
+      return false;
+    LIBC_NAMESPACE::inline_memcpy(data + idx, str.data(), len);
+    idx += len;
+    data[idx] = '\0';
+    return true;
+  }
+
+  template <size_t N> bool append(const char (&str)[N]) {
+    size_t len = N - 1;
+    if (idx + len >= sizeof(data))
+      return false;
+    LIBC_NAMESPACE::inline_memcpy(data + idx, str, len);
+    idx += len;
+    data[idx] = '\0';
+    return true;
+  }
+};
+
+LIBC_INLINE void report_error(string_view msg) {
+  LIBC_NAMESPACE::linux_syscalls::write(2, msg.data(), msg.size());
+}
+
+} // anonymous namespace
+
+extern "C" void write_raw_profile() {
+  if (!__llvm_profile_get_size_for_buffer || !__llvm_profile_write_buffer)
+    return;
+
+  size_t required_size =
+      static_cast<size_t>(__llvm_profile_get_size_for_buffer());
+  if (required_size == 0)
+    return;
+
+  auto mmap_or_error = LIBC_NAMESPACE::linux_syscalls::mmap(
+      nullptr, required_size, PROT_READ | PROT_WRITE,
+      MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+  if (!mmap_or_error)
+    return report_error("error: libc coverage failed to mmap buffer\n");
+  char *profile_buffer = static_cast<char *>(mmap_or_error.value());
+
+  if (__llvm_profile_write_buffer(profile_buffer) != 0) {
+    LIBC_NAMESPACE::linux_syscalls::munmap(profile_buffer, required_size);
+    return report_error(
+        "error: libc coverage failed to write profile buffer\n");
+  }
+
+  // Create a minimal filename: libc_cov_<pid>.profraw
+  pid_t pid = LIBC_NAMESPACE::linux_syscalls::getpid();
+  if (pid <= 0)
+    pid = 1;
+
+  FixedSizeBuffer filename;
+  char pid_buf[LIBC_NAMESPACE::IntegerToString<long>::buffer_size()];
+  auto pid_str =
+      LIBC_NAMESPACE::IntegerToString<long>::format_to(pid_buf, pid);
+  if (!pid_str || !filename.append("libc_cov_") || !filename.append(*pid_str) ||
+      !filename.append(".profraw")) {
+    LIBC_NAMESPACE::linux_syscalls::munmap(profile_buffer, required_size);
+    return report_error("error: libc coverage filename buffer overflow\n");
+  }
+
+  auto fd_or_error = LIBC_NAMESPACE::linux_syscalls::open(
+      filename.data, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+  if (!fd_or_error) {
+    LIBC_NAMESPACE::linux_syscalls::munmap(profile_buffer, required_size);
+    return report_error("error: libc coverage failed to open output file\n");
+  }
+  int fd = fd_or_error.value();
+
+  size_t bytes_written = 0;
+  bool write_error_occurred = false;
+  while (bytes_written < required_size) {
+    auto write_or_error = LIBC_NAMESPACE::linux_syscalls::write(
+        fd, profile_buffer + bytes_written, required_size - bytes_written);
+    if (!write_or_error) {
+      if (write_or_error.error() == EINTR)
+        continue;
+      write_error_occurred = true;
+      break;
+    }
+    ssize_t ret = write_or_error.value();
+    if (ret == 0) {
+      write_error_occurred = true;
+      break;
+    }
+    bytes_written += ret;
+  }
+
+  LIBC_NAMESPACE::linux_syscalls::close(fd);
+  LIBC_NAMESPACE::linux_syscalls::munmap(profile_buffer, required_size);
+
+  if (write_error_occurred || bytes_written < required_size)
+    return report_error(
+        "error: libc coverage failed to write all data to file\n");
+
+  // Clear the filename pattern to prevent compiler-rt from writing at exit.
+  if (__llvm_profile_set_filename)
+    __llvm_profile_set_filename("/dev/null");
+}
+
+#else
+
+extern "C" void write_raw_profile() {}
+
+#endif // LIBC_TARGET_OS_IS_LINUX
+
+#endif // LLVM_LIBC_TEST_UNITTEST_COVERAGE_H
diff --git a/libc/test/UnitTest/ExecuteFunctionUnix.cpp b/libc/test/UnitTest/ExecuteFunctionUnix.cpp
index a07c92f61225c..a97ebeaf685be 100644
--- a/libc/test/UnitTest/ExecuteFunctionUnix.cpp
+++ b/libc/test/UnitTest/ExecuteFunctionUnix.cpp
@@ -18,6 +18,7 @@
 #include "include/llvm-libc-types/struct_pollfd.h"
 #include "src/poll/poll.h"
 #include "src/signal/kill.h"
+#include "src/signal/sigaction.h"
 #include "src/stdio/fflush.h"
 #include "src/stdio/stderr.h"
 #include "src/stdio/stdout.h"
@@ -26,6 +27,7 @@
 #include "src/sys/wait/waitpid.h"
 #include "src/unistd/close.h"
 #include "src/unistd/fork.h"
+#include "src/unistd/getpid.h"
 #include "src/unistd/pipe.h"
 
 #define LIBC_IMPL LIBC_NAMESPACE
@@ -42,6 +44,10 @@
 #define LIBC_IMPL
 #endif
 
+#if defined(LLVM_LIBC_ENABLE_COVERAGE)
+extern "C" __attribute__((weak)) void write_raw_profile();
+#endif
+
 namespace LIBC_NAMESPACE_DECL {
 namespace testutils {
 
@@ -58,6 +64,25 @@ int ProcessStatus::get_fatal_signal() {
   return WTERMSIG(platform_defined);
 }
 
+#if defined(LLVM_LIBC_ENABLE_COVERAGE)
+static void coverage_fatal_signal_handler(int sig) {
+  if (write_raw_profile)
+    write_raw_profile();
+  
+  // Restore default signal handler
+#ifdef LIBC_FULL_BUILD
+  struct sigaction sa = {};
+  sa.sa_handler = SIG_DFL;
+  LIBC_IMPL::sigaction(sig, &sa, nullptr);
+#else
+  ::signal(sig, SIG_DFL);
+#endif
+  
+  // Re-raise the signal
+  LIBC_IMPL::kill(LIBC_IMPL::getpid(), sig);
+}
+#endif
+
 ProcessStatus invoke_in_subprocess(FunctionCaller *func, int timeout_ms) {
   int pipe_fds[2];
   if (LIBC_IMPL::pipe(pipe_fds) == -1) {
@@ -75,10 +100,33 @@ ProcessStatus invoke_in_subprocess(FunctionCaller *func, int timeout_ms) {
   }
 
   if (!pid) {
+#if defined(LLVM_LIBC_ENABLE_COVERAGE)
+#ifdef LIBC_FULL_BUILD
+    struct sigaction sa = {};
+    sa.sa_handler = coverage_fatal_signal_handler;
+    LIBC_IMPL::sigaction(SIGABRT, &sa, nullptr);
+    LIBC_IMPL::sigaction(SIGSEGV, &sa, nullptr);
+    LIBC_IMPL::sigaction(SIGILL, &sa, nullptr);
+    LIBC_IMPL::sigaction(SIGFPE, &sa, nullptr);
+    LIBC_IMPL::sigaction(SIGBUS, &sa, nullptr);
+#else
+    ::signal(SIGABRT, coverage_fatal_signal_handler);
+    ::signal(SIGSEGV, coverage_fatal_signal_handler);
+    ::signal(SIGILL, coverage_fatal_signal_handler);
+    ::signal(SIGFPE, coverage_fatal_signal_handler);
+    ::signal(SIGBUS, coverage_fatal_signal_handler);
+#endif
+#endif
+
     (*func)();
     delete func;
+#if defined(LLVM_LIBC_ENABLE_COVERAGE)
+    if (write_raw_profile)
+      write_raw_profile();
+#endif
     LIBC_IMPL::exit(0);
   }
+
   LIBC_IMPL::close(pipe_fds[1]);
 
   pollfd poll_fd{pipe_fds[0], POLLIN, 0};
diff --git a/libc/test/UnitTest/LibcTestMain.cpp b/libc/test/UnitTest/LibcTestMain.cpp
index c348d5ef1aa1b..fb46ec45073a0 100644
--- a/libc/test/UnitTest/LibcTestMain.cpp
+++ b/libc/test/UnitTest/LibcTestMain.cpp
@@ -43,8 +43,10 @@ TestOptions parseOptions(int argc, char **argv) {
 
 } // anonymous namespace
 
-// The C++ standard forbids declaring the main function with a linkage specifier
-// outisde of 'freestanding' mode, only define the linkage for hermetic tests.
+#if defined(LLVM_LIBC_ENABLE_COVERAGE) && defined(LIBC_TARGET_OS_IS_LINUX)
+#include "Coverage.h"
+#endif
+
 #if __STDC_HOSTED__
 #define TEST_MAIN int main
 #else
@@ -56,5 +58,11 @@ TEST_MAIN(int argc, char **argv, char **envp) {
   LIBC_NAMESPACE::testing::argv = argv;
   LIBC_NAMESPACE::testing::envp = envp;
 
-  return LIBC_NAMESPACE::testing::Test::runTests(parseOptions(argc, argv));
+  int result =
+      LIBC_NAMESPACE::testing::Test::runTests(parseOptions(argc, argv));
+#if defined(LLVM_LIBC_ENABLE_COVERAGE) && defined(LIBC_TARGET_OS_IS_LINUX)
+  write_raw_profile();
+#endif
+  return result;
 }
+

>From f58dbca43068925e41328b5030925f85cbf97b08 Mon Sep 17 00:00:00 2001
From: Tapiwa Gonga <tapiwagonga at google.com>
Date: Mon, 24 Aug 2026 09:46:50 +0000
Subject: [PATCH 2/7] [libc] Fix clang-format warnings in coverage runtime

---
 libc/src/__support/OSUtil/linux/syscall_wrappers/getpid.h | 4 +---
 libc/test/UnitTest/Coverage.h                             | 3 +--
 libc/test/UnitTest/ExecuteFunctionUnix.cpp                | 4 ++--
 libc/test/UnitTest/LibcTestMain.cpp                       | 1 -
 4 files changed, 4 insertions(+), 8 deletions(-)

diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/getpid.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/getpid.h
index 6b1d2686cac4b..044597a31ff9e 100644
--- a/libc/src/__support/OSUtil/linux/syscall_wrappers/getpid.h
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/getpid.h
@@ -18,9 +18,7 @@
 namespace LIBC_NAMESPACE_DECL {
 namespace linux_syscalls {
 
-LIBC_INLINE pid_t getpid() {
-  return syscall_impl<pid_t>(SYS_getpid);
-}
+LIBC_INLINE pid_t getpid() { return syscall_impl<pid_t>(SYS_getpid); }
 
 } // namespace linux_syscalls
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/test/UnitTest/Coverage.h b/libc/test/UnitTest/Coverage.h
index e142775c1a7e0..f9c2e76fb9b49 100644
--- a/libc/test/UnitTest/Coverage.h
+++ b/libc/test/UnitTest/Coverage.h
@@ -106,8 +106,7 @@ extern "C" void write_raw_profile() {
 
   FixedSizeBuffer filename;
   char pid_buf[LIBC_NAMESPACE::IntegerToString<long>::buffer_size()];
-  auto pid_str =
-      LIBC_NAMESPACE::IntegerToString<long>::format_to(pid_buf, pid);
+  auto pid_str = LIBC_NAMESPACE::IntegerToString<long>::format_to(pid_buf, pid);
   if (!pid_str || !filename.append("libc_cov_") || !filename.append(*pid_str) ||
       !filename.append(".profraw")) {
     LIBC_NAMESPACE::linux_syscalls::munmap(profile_buffer, required_size);
diff --git a/libc/test/UnitTest/ExecuteFunctionUnix.cpp b/libc/test/UnitTest/ExecuteFunctionUnix.cpp
index a97ebeaf685be..f2d53cf21a92f 100644
--- a/libc/test/UnitTest/ExecuteFunctionUnix.cpp
+++ b/libc/test/UnitTest/ExecuteFunctionUnix.cpp
@@ -68,7 +68,7 @@ int ProcessStatus::get_fatal_signal() {
 static void coverage_fatal_signal_handler(int sig) {
   if (write_raw_profile)
     write_raw_profile();
-  
+
   // Restore default signal handler
 #ifdef LIBC_FULL_BUILD
   struct sigaction sa = {};
@@ -77,7 +77,7 @@ static void coverage_fatal_signal_handler(int sig) {
 #else
   ::signal(sig, SIG_DFL);
 #endif
-  
+
   // Re-raise the signal
   LIBC_IMPL::kill(LIBC_IMPL::getpid(), sig);
 }
diff --git a/libc/test/UnitTest/LibcTestMain.cpp b/libc/test/UnitTest/LibcTestMain.cpp
index fb46ec45073a0..a7e9461711635 100644
--- a/libc/test/UnitTest/LibcTestMain.cpp
+++ b/libc/test/UnitTest/LibcTestMain.cpp
@@ -65,4 +65,3 @@ TEST_MAIN(int argc, char **argv, char **envp) {
 #endif
   return result;
 }
-

>From bb8c742245c22ce8ae0c4363262dece50a4b95ec Mon Sep 17 00:00:00 2001
From: Tapiwa Gonga <tapiwagonga at google.com>
Date: Wed, 26 Aug 2026 08:40:46 +0000
Subject: [PATCH 3/7] [libc] Address review feedback: update headers, rename
 coverage option, and clean formatting

---
 libc/CMakeLists.txt                                  |  7 +++----
 .../__support/OSUtil/linux/syscall_wrappers/getpid.h |  8 +++++++-
 libc/test/UnitTest/Coverage.h                        | 10 +++++++++-
 libc/test/UnitTest/ExecuteFunctionUnix.cpp           | 12 +++++-------
 libc/test/UnitTest/LibcTestMain.cpp                  |  4 ++--
 5 files changed, 26 insertions(+), 15 deletions(-)

diff --git a/libc/CMakeLists.txt b/libc/CMakeLists.txt
index 6c73f0ada73e2..08bd4bb54f793 100644
--- a/libc/CMakeLists.txt
+++ b/libc/CMakeLists.txt
@@ -91,9 +91,9 @@ endif()
 set(LIBC_LINK_OPTIONS_DEFAULT "" CACHE STRING "Arguments used when linking.")
 set(LIBC_TEST_LINK_OPTIONS_DEFAULT "" CACHE STRING "Common link options for all the tests.")
 
-option(LLVM_LIBC_ENABLE_COVERAGE "Build libc with coverage instrumentation" OFF)
-if(LLVM_LIBC_ENABLE_COVERAGE)
-  add_compile_definitions(LLVM_LIBC_ENABLE_COVERAGE)
+option(LIBC_ENABLE_COVERAGE "Build libc with coverage instrumentation" OFF)
+if(LIBC_ENABLE_COVERAGE)
+  add_compile_definitions(LIBC_ENABLE_COVERAGE)
   list(APPEND LIBC_COMPILE_OPTIONS_DEFAULT "-fprofile-instr-generate" "-fcoverage-mapping")
   list(APPEND LIBC_TEST_COMPILE_OPTIONS_DEFAULT "-fprofile-instr-generate" "-fcoverage-mapping")
   list(APPEND LIBC_TEST_LINK_OPTIONS_DEFAULT "-fprofile-instr-generate" "-fcoverage-mapping")
@@ -184,7 +184,6 @@ if(LLVM_LIBC_FULL_BUILD)
 else()
   set(LLVM_LIBC_OVERLAY ON)
 endif()
-
 option(LLVM_LIBC_IMPLEMENTATION_DEFINED_TEST_BEHAVIOR "Build LLVM libc tests assuming our implementation-defined behavior" ON)
 option(LLVM_LIBC_ENABLE_LINTING "Enables linting of libc source files" OFF)
 option(LLVM_LIBC_ALL_HEADERS "Outputs all functions in header files, regardless of whether they are enabled on this target" OFF)
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/getpid.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/getpid.h
index 044597a31ff9e..129aa167c4c71 100644
--- a/libc/src/__support/OSUtil/linux/syscall_wrappers/getpid.h
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/getpid.h
@@ -1,10 +1,15 @@
-//===-- Implementation header for getpid ------------------------*- 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
 //
 //===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation header for getpid.
+///
+//===----------------------------------------------------------------------===//
 
 #ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_GETPID_H
 #define LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_GETPID_H
@@ -18,6 +23,7 @@
 namespace LIBC_NAMESPACE_DECL {
 namespace linux_syscalls {
 
+/// Retrieves the process ID of the calling process via SYS_getpid.
 LIBC_INLINE pid_t getpid() { return syscall_impl<pid_t>(SYS_getpid); }
 
 } // namespace linux_syscalls
diff --git a/libc/test/UnitTest/Coverage.h b/libc/test/UnitTest/Coverage.h
index f9c2e76fb9b49..6fff837aebac6 100644
--- a/libc/test/UnitTest/Coverage.h
+++ b/libc/test/UnitTest/Coverage.h
@@ -1,10 +1,15 @@
-//===-- Freestanding Code Coverage Extraction Support -----------*- 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
 //
 //===----------------------------------------------------------------------===//
+///
+/// \file
+/// Freestanding code coverage extraction support for unit tests.
+///
+//===----------------------------------------------------------------------===//
 
 #ifndef LLVM_LIBC_TEST_UNITTEST_COVERAGE_H
 #define LLVM_LIBC_TEST_UNITTEST_COVERAGE_H
@@ -44,6 +49,8 @@ namespace {
 
 using LIBC_NAMESPACE::cpp::string_view;
 
+/// Minimal fixed-size stack buffer for constructing file paths without dynamic
+/// memory allocation.
 struct FixedSizeBuffer {
   char data[64];
   size_t idx = 0;
@@ -77,6 +84,7 @@ LIBC_INLINE void report_error(string_view msg) {
 
 } // anonymous namespace
 
+/// Writes raw coverage profile data to disk using direct Linux syscalls.
 extern "C" void write_raw_profile() {
   if (!__llvm_profile_get_size_for_buffer || !__llvm_profile_write_buffer)
     return;
diff --git a/libc/test/UnitTest/ExecuteFunctionUnix.cpp b/libc/test/UnitTest/ExecuteFunctionUnix.cpp
index f2d53cf21a92f..2c8a5aa4b7f65 100644
--- a/libc/test/UnitTest/ExecuteFunctionUnix.cpp
+++ b/libc/test/UnitTest/ExecuteFunctionUnix.cpp
@@ -44,7 +44,7 @@
 #define LIBC_IMPL
 #endif
 
-#if defined(LLVM_LIBC_ENABLE_COVERAGE)
+#if defined(LIBC_ENABLE_COVERAGE)
 extern "C" __attribute__((weak)) void write_raw_profile();
 #endif
 
@@ -64,7 +64,7 @@ int ProcessStatus::get_fatal_signal() {
   return WTERMSIG(platform_defined);
 }
 
-#if defined(LLVM_LIBC_ENABLE_COVERAGE)
+#if defined(LIBC_ENABLE_COVERAGE)
 static void coverage_fatal_signal_handler(int sig) {
   if (write_raw_profile)
     write_raw_profile();
@@ -100,8 +100,7 @@ ProcessStatus invoke_in_subprocess(FunctionCaller *func, int timeout_ms) {
   }
 
   if (!pid) {
-#if defined(LLVM_LIBC_ENABLE_COVERAGE)
-#ifdef LIBC_FULL_BUILD
+#if defined(LIBC_ENABLE_COVERAGE) && defined(LIBC_FULL_BUILD)
     struct sigaction sa = {};
     sa.sa_handler = coverage_fatal_signal_handler;
     LIBC_IMPL::sigaction(SIGABRT, &sa, nullptr);
@@ -109,18 +108,17 @@ ProcessStatus invoke_in_subprocess(FunctionCaller *func, int timeout_ms) {
     LIBC_IMPL::sigaction(SIGILL, &sa, nullptr);
     LIBC_IMPL::sigaction(SIGFPE, &sa, nullptr);
     LIBC_IMPL::sigaction(SIGBUS, &sa, nullptr);
-#else
+#elif defined(LIBC_ENABLE_COVERAGE)
     ::signal(SIGABRT, coverage_fatal_signal_handler);
     ::signal(SIGSEGV, coverage_fatal_signal_handler);
     ::signal(SIGILL, coverage_fatal_signal_handler);
     ::signal(SIGFPE, coverage_fatal_signal_handler);
     ::signal(SIGBUS, coverage_fatal_signal_handler);
-#endif
 #endif
 
     (*func)();
     delete func;
-#if defined(LLVM_LIBC_ENABLE_COVERAGE)
+#if defined(LIBC_ENABLE_COVERAGE)
     if (write_raw_profile)
       write_raw_profile();
 #endif
diff --git a/libc/test/UnitTest/LibcTestMain.cpp b/libc/test/UnitTest/LibcTestMain.cpp
index a7e9461711635..60bad42003645 100644
--- a/libc/test/UnitTest/LibcTestMain.cpp
+++ b/libc/test/UnitTest/LibcTestMain.cpp
@@ -43,7 +43,7 @@ TestOptions parseOptions(int argc, char **argv) {
 
 } // anonymous namespace
 
-#if defined(LLVM_LIBC_ENABLE_COVERAGE) && defined(LIBC_TARGET_OS_IS_LINUX)
+#if defined(LIBC_ENABLE_COVERAGE)
 #include "Coverage.h"
 #endif
 
@@ -60,7 +60,7 @@ TEST_MAIN(int argc, char **argv, char **envp) {
 
   int result =
       LIBC_NAMESPACE::testing::Test::runTests(parseOptions(argc, argv));
-#if defined(LLVM_LIBC_ENABLE_COVERAGE) && defined(LIBC_TARGET_OS_IS_LINUX)
+#if defined(LIBC_ENABLE_COVERAGE)
   write_raw_profile();
 #endif
   return result;

>From 2f9f27bb03691c6037695b8a9a277787fc034cf8 Mon Sep 17 00:00:00 2001
From: Tapiwa Gonga <tapiwagonga at google.com>
Date: Thu, 27 Aug 2026 13:10:03 +0000
Subject: [PATCH 4/7] [libc] Add newline separator between CMake options

---
 libc/CMakeLists.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libc/CMakeLists.txt b/libc/CMakeLists.txt
index 08bd4bb54f793..b039452cf27cb 100644
--- a/libc/CMakeLists.txt
+++ b/libc/CMakeLists.txt
@@ -184,6 +184,7 @@ if(LLVM_LIBC_FULL_BUILD)
 else()
   set(LLVM_LIBC_OVERLAY ON)
 endif()
+
 option(LLVM_LIBC_IMPLEMENTATION_DEFINED_TEST_BEHAVIOR "Build LLVM libc tests assuming our implementation-defined behavior" ON)
 option(LLVM_LIBC_ENABLE_LINTING "Enables linting of libc source files" OFF)
 option(LLVM_LIBC_ALL_HEADERS "Outputs all functions in header files, regardless of whether they are enabled on this target" OFF)

>From bcb9a0bc24b5ca7b5a187f97d8e083fe75fb58a7 Mon Sep 17 00:00:00 2001
From: Tapiwa Gonga <tapiwagonga at google.com>
Date: Tue, 1 Sep 2026 10:39:02 +0000
Subject: [PATCH 5/7] Fix PR review feedback for coverage implementation

- Wrap --rtlib query in Clang compiler check
- Remove unnecessary newlines in CMakeLists.txt
- Remove redundant signal handler overrides from ExecuteFunctionUnix.cpp in favor of continuous coverage mode
---
 libc/CMakeLists.txt                           | 29 +++++++------
 .../linux/syscall_wrappers/CMakeLists.txt     |  1 -
 libc/test/UnitTest/ExecuteFunctionUnix.cpp    | 41 +------------------
 3 files changed, 17 insertions(+), 54 deletions(-)

diff --git a/libc/CMakeLists.txt b/libc/CMakeLists.txt
index b039452cf27cb..f5150777b6b81 100644
--- a/libc/CMakeLists.txt
+++ b/libc/CMakeLists.txt
@@ -100,21 +100,25 @@ if(LIBC_ENABLE_COVERAGE)
 
   # When building with -nostdlib, the compiler does not automatically link the profiling runtime.
   # We must explicitly query the compiler for the exact architecture-specific profile library path.
-  execute_process(
-    COMMAND ${CMAKE_CXX_COMPILER} --print-libgcc-file-name --rtlib=compiler-rt
-    OUTPUT_VARIABLE COMPILER_RT_BUILTINS
-    OUTPUT_STRIP_TRAILING_WHITESPACE
-    RESULT_VARIABLE COMPILER_RT_RETURN_CODE
-  )
-  if(COMPILER_RT_RETURN_CODE EQUAL 0)
-    string(REPLACE "builtins" "profile" COMPILER_RT_PROFILE "${COMPILER_RT_BUILTINS}")
-    if(EXISTS "${COMPILER_RT_PROFILE}")
-      list(APPEND LIBC_TEST_LINK_OPTIONS_DEFAULT "${COMPILER_RT_PROFILE}")
+  if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+    execute_process(
+      COMMAND ${CMAKE_CXX_COMPILER} --print-libgcc-file-name --rtlib=compiler-rt
+      OUTPUT_VARIABLE COMPILER_RT_BUILTINS
+      OUTPUT_STRIP_TRAILING_WHITESPACE
+      RESULT_VARIABLE COMPILER_RT_RETURN_CODE
+    )
+    if(COMPILER_RT_RETURN_CODE EQUAL 0)
+      string(REPLACE "builtins" "profile" COMPILER_RT_PROFILE "${COMPILER_RT_BUILTINS}")
+      if(EXISTS "${COMPILER_RT_PROFILE}")
+        list(APPEND LIBC_TEST_LINK_OPTIONS_DEFAULT "${COMPILER_RT_PROFILE}")
+      else()
+        message(WARNING "Coverage profiling runtime not found at ${COMPILER_RT_PROFILE}")
+      endif()
     else()
-      message(WARNING "Coverage profiling runtime not found at ${COMPILER_RT_PROFILE}")
+      message(WARNING "Failed to locate compiler-rt builtins library for coverage")
     endif()
   else()
-    message(WARNING "Failed to locate compiler-rt builtins library for coverage")
+    message(WARNING "Code coverage is currently only supported with Clang")
   endif()
 endif()
 
@@ -184,7 +188,6 @@ if(LLVM_LIBC_FULL_BUILD)
 else()
   set(LLVM_LIBC_OVERLAY ON)
 endif()
-
 option(LLVM_LIBC_IMPLEMENTATION_DEFINED_TEST_BEHAVIOR "Build LLVM libc tests assuming our implementation-defined behavior" ON)
 option(LLVM_LIBC_ENABLE_LINTING "Enables linting of libc source files" OFF)
 option(LLVM_LIBC_ALL_HEADERS "Outputs all functions in header files, regardless of whether they are enabled on this target" OFF)
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt b/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
index 1d79c82d94b80..21a552473d1c5 100644
--- a/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
@@ -38,7 +38,6 @@ add_header_library(
     libc.include.sys_syscall
 )
 
-
 add_header_library(
   sched_getaffinity
   HDRS
diff --git a/libc/test/UnitTest/ExecuteFunctionUnix.cpp b/libc/test/UnitTest/ExecuteFunctionUnix.cpp
index 2c8a5aa4b7f65..9640dab394bcc 100644
--- a/libc/test/UnitTest/ExecuteFunctionUnix.cpp
+++ b/libc/test/UnitTest/ExecuteFunctionUnix.cpp
@@ -44,9 +44,6 @@
 #define LIBC_IMPL
 #endif
 
-#if defined(LIBC_ENABLE_COVERAGE)
-extern "C" __attribute__((weak)) void write_raw_profile();
-#endif
 
 namespace LIBC_NAMESPACE_DECL {
 namespace testutils {
@@ -64,24 +61,6 @@ int ProcessStatus::get_fatal_signal() {
   return WTERMSIG(platform_defined);
 }
 
-#if defined(LIBC_ENABLE_COVERAGE)
-static void coverage_fatal_signal_handler(int sig) {
-  if (write_raw_profile)
-    write_raw_profile();
-
-  // Restore default signal handler
-#ifdef LIBC_FULL_BUILD
-  struct sigaction sa = {};
-  sa.sa_handler = SIG_DFL;
-  LIBC_IMPL::sigaction(sig, &sa, nullptr);
-#else
-  ::signal(sig, SIG_DFL);
-#endif
-
-  // Re-raise the signal
-  LIBC_IMPL::kill(LIBC_IMPL::getpid(), sig);
-}
-#endif
 
 ProcessStatus invoke_in_subprocess(FunctionCaller *func, int timeout_ms) {
   int pipe_fds[2];
@@ -100,28 +79,10 @@ ProcessStatus invoke_in_subprocess(FunctionCaller *func, int timeout_ms) {
   }
 
   if (!pid) {
-#if defined(LIBC_ENABLE_COVERAGE) && defined(LIBC_FULL_BUILD)
-    struct sigaction sa = {};
-    sa.sa_handler = coverage_fatal_signal_handler;
-    LIBC_IMPL::sigaction(SIGABRT, &sa, nullptr);
-    LIBC_IMPL::sigaction(SIGSEGV, &sa, nullptr);
-    LIBC_IMPL::sigaction(SIGILL, &sa, nullptr);
-    LIBC_IMPL::sigaction(SIGFPE, &sa, nullptr);
-    LIBC_IMPL::sigaction(SIGBUS, &sa, nullptr);
-#elif defined(LIBC_ENABLE_COVERAGE)
-    ::signal(SIGABRT, coverage_fatal_signal_handler);
-    ::signal(SIGSEGV, coverage_fatal_signal_handler);
-    ::signal(SIGILL, coverage_fatal_signal_handler);
-    ::signal(SIGFPE, coverage_fatal_signal_handler);
-    ::signal(SIGBUS, coverage_fatal_signal_handler);
-#endif
 
     (*func)();
     delete func;
-#if defined(LIBC_ENABLE_COVERAGE)
-    if (write_raw_profile)
-      write_raw_profile();
-#endif
+
     LIBC_IMPL::exit(0);
   }
 

>From 565476c5d5d686bd7e3f0f35499e9911c6741bdd Mon Sep 17 00:00:00 2001
From: Tapiwa Gonga <tapiwagonga at google.com>
Date: Tue, 1 Sep 2026 11:13:08 +0000
Subject: [PATCH 6/7] [libc][coverage] Refactor to use continuous
 instrumentation profiling

This commit transitions the coverage implementation from a manual flush
strategy to the compiler-native continuous instrumentation mode (%c).

While the previous approach successfully handled coverage extraction
during death tests by intercepting signals and mapping the profile data
manually, using the continuous instrumentation profiling mode handles
this natively at the compiler level.

As a result of adopting the native continuous mode, the manual profiling
layer is no longer required. This commit removes Coverage.h, drops the
getpid syscall wrapper, and reverts raise.h back to using the standard
SYS_getpid macro, significantly streamlining the implementation.
---
 .../linux/syscall_wrappers/CMakeLists.txt     |  13 --
 .../OSUtil/linux/syscall_wrappers/getpid.h    |  32 ----
 .../OSUtil/linux/syscall_wrappers/raise.h     |   3 +-
 libc/test/UnitTest/Coverage.h                 | 169 ------------------
 libc/test/UnitTest/LibcTestMain.cpp           |   6 -
 5 files changed, 1 insertion(+), 222 deletions(-)
 delete mode 100644 libc/src/__support/OSUtil/linux/syscall_wrappers/getpid.h
 delete mode 100644 libc/test/UnitTest/Coverage.h

diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt b/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
index 21a552473d1c5..2b22bb8e733de 100644
--- a/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
@@ -26,18 +26,6 @@ add_header_library(
     libc.include.sys_syscall
 )
 
-add_header_library(
-  getpid
-  HDRS
-    getpid.h
-  DEPENDS
-    libc.src.__support.OSUtil.osutil
-    libc.src.__support.common
-    libc.src.__support.macros.config
-    libc.hdr.types.pid_t
-    libc.include.sys_syscall
-)
-
 add_header_library(
   sched_getaffinity
   HDRS
@@ -409,7 +397,6 @@ add_header_library(
     raise.h
   DEPENDS
     libc.src.__support.OSUtil.osutil
-    libc.src.__support.OSUtil.linux.syscall_wrappers.getpid
     libc.src.__support.OSUtil.linux.syscall_wrappers.rt_sigprocmask
     libc.src.__support.common
     libc.src.__support.error_or
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/getpid.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/getpid.h
deleted file mode 100644
index 129aa167c4c71..0000000000000
--- a/libc/src/__support/OSUtil/linux/syscall_wrappers/getpid.h
+++ /dev/null
@@ -1,32 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file
-/// Implementation header for getpid.
-///
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_GETPID_H
-#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_GETPID_H
-
-#include "hdr/types/pid_t.h"
-#include "src/__support/OSUtil/linux/syscall.h" // syscall_impl
-#include "src/__support/common.h"
-#include "src/__support/macros/config.h"
-#include <sys/syscall.h> // For syscall numbers
-
-namespace LIBC_NAMESPACE_DECL {
-namespace linux_syscalls {
-
-/// Retrieves the process ID of the calling process via SYS_getpid.
-LIBC_INLINE pid_t getpid() { return syscall_impl<pid_t>(SYS_getpid); }
-
-} // namespace linux_syscalls
-} // namespace LIBC_NAMESPACE_DECL
-
-#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_GETPID_H
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/raise.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/raise.h
index b7c22a51bfe24..2b61e4e624c89 100644
--- a/libc/src/__support/OSUtil/linux/syscall_wrappers/raise.h
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/raise.h
@@ -12,7 +12,6 @@
 #include "hdr/signal_macros.h"
 #include "hdr/types/sigset_t.h"
 #include "src/__support/OSUtil/linux/syscall.h" // syscall_impl
-#include "src/__support/OSUtil/linux/syscall_wrappers/getpid.h"
 #include "src/__support/OSUtil/linux/syscall_wrappers/rt_sigprocmask.h"
 #include "src/__support/common.h"
 #include "src/__support/error_or.h"
@@ -47,7 +46,7 @@ LIBC_INLINE ErrorOr<int> raise(int sig) {
     if (!status.has_value())
       return status;
 
-    pid_t pid = linux_syscalls::getpid();
+    long pid = syscall_impl<long>(SYS_getpid);
     if (pid < 0)
       return Error(-static_cast<int>(pid));
 
diff --git a/libc/test/UnitTest/Coverage.h b/libc/test/UnitTest/Coverage.h
deleted file mode 100644
index 6fff837aebac6..0000000000000
--- a/libc/test/UnitTest/Coverage.h
+++ /dev/null
@@ -1,169 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file
-/// Freestanding code coverage extraction support for unit tests.
-///
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_LIBC_TEST_UNITTEST_COVERAGE_H
-#define LLVM_LIBC_TEST_UNITTEST_COVERAGE_H
-
-#include "src/__support/macros/properties/os.h"
-
-#if defined(LIBC_TARGET_OS_IS_LINUX)
-
-#include "hdr/errno_macros.h"
-#include "hdr/fcntl_macros.h"
-#include "hdr/sys_mman_macros.h"
-#include "src/__support/CPP/optional.h"
-#include "src/__support/CPP/span.h"
-#include "src/__support/CPP/string_view.h"
-#include "src/__support/OSUtil/linux/syscall_wrappers/close.h"
-#include "src/__support/OSUtil/linux/syscall_wrappers/getpid.h"
-#include "src/__support/OSUtil/linux/syscall_wrappers/mmap.h"
-#include "src/__support/OSUtil/linux/syscall_wrappers/munmap.h"
-#include "src/__support/OSUtil/linux/syscall_wrappers/open.h"
-#include "src/__support/OSUtil/linux/syscall_wrappers/write.h"
-#include "src/__support/integer_to_string.h"
-#include "src/string/memory_utils/inline_memcpy.h"
-#include <sys/syscall.h>
-
-extern "C" {
-__attribute__((weak)) uint64_t __llvm_profile_get_size_for_buffer();
-__attribute__((weak)) int __llvm_profile_write_buffer(char *buffer);
-__attribute__((weak)) void
-__llvm_profile_set_filename(const char *filename_pat);
-
-// Override compiler-rt's weak filename symbol. This redirects the default
-// filename to /dev/null to silence the default dumper by default.
-__attribute__((weak)) char __llvm_profile_filename[] = "/dev/null";
-}
-
-namespace {
-
-using LIBC_NAMESPACE::cpp::string_view;
-
-/// Minimal fixed-size stack buffer for constructing file paths without dynamic
-/// memory allocation.
-struct FixedSizeBuffer {
-  char data[64];
-  size_t idx = 0;
-
-  FixedSizeBuffer() { data[0] = '\0'; }
-
-  bool append(string_view str) {
-    size_t len = str.size();
-    if (idx + len >= sizeof(data))
-      return false;
-    LIBC_NAMESPACE::inline_memcpy(data + idx, str.data(), len);
-    idx += len;
-    data[idx] = '\0';
-    return true;
-  }
-
-  template <size_t N> bool append(const char (&str)[N]) {
-    size_t len = N - 1;
-    if (idx + len >= sizeof(data))
-      return false;
-    LIBC_NAMESPACE::inline_memcpy(data + idx, str, len);
-    idx += len;
-    data[idx] = '\0';
-    return true;
-  }
-};
-
-LIBC_INLINE void report_error(string_view msg) {
-  LIBC_NAMESPACE::linux_syscalls::write(2, msg.data(), msg.size());
-}
-
-} // anonymous namespace
-
-/// Writes raw coverage profile data to disk using direct Linux syscalls.
-extern "C" void write_raw_profile() {
-  if (!__llvm_profile_get_size_for_buffer || !__llvm_profile_write_buffer)
-    return;
-
-  size_t required_size =
-      static_cast<size_t>(__llvm_profile_get_size_for_buffer());
-  if (required_size == 0)
-    return;
-
-  auto mmap_or_error = LIBC_NAMESPACE::linux_syscalls::mmap(
-      nullptr, required_size, PROT_READ | PROT_WRITE,
-      MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
-  if (!mmap_or_error)
-    return report_error("error: libc coverage failed to mmap buffer\n");
-  char *profile_buffer = static_cast<char *>(mmap_or_error.value());
-
-  if (__llvm_profile_write_buffer(profile_buffer) != 0) {
-    LIBC_NAMESPACE::linux_syscalls::munmap(profile_buffer, required_size);
-    return report_error(
-        "error: libc coverage failed to write profile buffer\n");
-  }
-
-  // Create a minimal filename: libc_cov_<pid>.profraw
-  pid_t pid = LIBC_NAMESPACE::linux_syscalls::getpid();
-  if (pid <= 0)
-    pid = 1;
-
-  FixedSizeBuffer filename;
-  char pid_buf[LIBC_NAMESPACE::IntegerToString<long>::buffer_size()];
-  auto pid_str = LIBC_NAMESPACE::IntegerToString<long>::format_to(pid_buf, pid);
-  if (!pid_str || !filename.append("libc_cov_") || !filename.append(*pid_str) ||
-      !filename.append(".profraw")) {
-    LIBC_NAMESPACE::linux_syscalls::munmap(profile_buffer, required_size);
-    return report_error("error: libc coverage filename buffer overflow\n");
-  }
-
-  auto fd_or_error = LIBC_NAMESPACE::linux_syscalls::open(
-      filename.data, O_WRONLY | O_CREAT | O_TRUNC, 0644);
-  if (!fd_or_error) {
-    LIBC_NAMESPACE::linux_syscalls::munmap(profile_buffer, required_size);
-    return report_error("error: libc coverage failed to open output file\n");
-  }
-  int fd = fd_or_error.value();
-
-  size_t bytes_written = 0;
-  bool write_error_occurred = false;
-  while (bytes_written < required_size) {
-    auto write_or_error = LIBC_NAMESPACE::linux_syscalls::write(
-        fd, profile_buffer + bytes_written, required_size - bytes_written);
-    if (!write_or_error) {
-      if (write_or_error.error() == EINTR)
-        continue;
-      write_error_occurred = true;
-      break;
-    }
-    ssize_t ret = write_or_error.value();
-    if (ret == 0) {
-      write_error_occurred = true;
-      break;
-    }
-    bytes_written += ret;
-  }
-
-  LIBC_NAMESPACE::linux_syscalls::close(fd);
-  LIBC_NAMESPACE::linux_syscalls::munmap(profile_buffer, required_size);
-
-  if (write_error_occurred || bytes_written < required_size)
-    return report_error(
-        "error: libc coverage failed to write all data to file\n");
-
-  // Clear the filename pattern to prevent compiler-rt from writing at exit.
-  if (__llvm_profile_set_filename)
-    __llvm_profile_set_filename("/dev/null");
-}
-
-#else
-
-extern "C" void write_raw_profile() {}
-
-#endif // LIBC_TARGET_OS_IS_LINUX
-
-#endif // LLVM_LIBC_TEST_UNITTEST_COVERAGE_H
diff --git a/libc/test/UnitTest/LibcTestMain.cpp b/libc/test/UnitTest/LibcTestMain.cpp
index 60bad42003645..ef6cc8fa51d9a 100644
--- a/libc/test/UnitTest/LibcTestMain.cpp
+++ b/libc/test/UnitTest/LibcTestMain.cpp
@@ -43,9 +43,6 @@ TestOptions parseOptions(int argc, char **argv) {
 
 } // anonymous namespace
 
-#if defined(LIBC_ENABLE_COVERAGE)
-#include "Coverage.h"
-#endif
 
 #if __STDC_HOSTED__
 #define TEST_MAIN int main
@@ -60,8 +57,5 @@ TEST_MAIN(int argc, char **argv, char **envp) {
 
   int result =
       LIBC_NAMESPACE::testing::Test::runTests(parseOptions(argc, argv));
-#if defined(LIBC_ENABLE_COVERAGE)
-  write_raw_profile();
-#endif
   return result;
 }

>From 3ba2872d3a512d70610ee9d4fff242d262b8d13e Mon Sep 17 00:00:00 2001
From: Tapiwa Gonga <tapiwagonga at google.com>
Date: Wed, 2 Sep 2026 09:51:03 +0000
Subject: [PATCH 7/7] [libc][coverage] Remove getpid and raise implementations
 from coverage PR

---
 .../OSUtil/linux/syscall_wrappers/raise.h     | 67 -------------------
 libc/src/signal/linux/raise.cpp               | 27 --------
 libc/src/signal/raise.h                       | 20 ------
 libc/src/unistd/getpid.h                      | 22 ------
 libc/src/unistd/linux/getpid.cpp              | 23 -------
 libc/test/src/signal/raise_test.cpp           | 21 ------
 libc/test/src/unistd/getpid_test.cpp          | 15 -----
 7 files changed, 195 deletions(-)
 delete mode 100644 libc/src/__support/OSUtil/linux/syscall_wrappers/raise.h
 delete mode 100644 libc/src/signal/linux/raise.cpp
 delete mode 100644 libc/src/signal/raise.h
 delete mode 100644 libc/src/unistd/getpid.h
 delete mode 100644 libc/src/unistd/linux/getpid.cpp
 delete mode 100644 libc/test/src/signal/raise_test.cpp
 delete mode 100644 libc/test/src/unistd/getpid_test.cpp

diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/raise.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/raise.h
deleted file mode 100644
index 2b61e4e624c89..0000000000000
--- a/libc/src/__support/OSUtil/linux/syscall_wrappers/raise.h
+++ /dev/null
@@ -1,67 +0,0 @@
-//===-- Implementation header for raise -------------------------*- 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___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_RAISE_H
-#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_RAISE_H
-
-#include "hdr/signal_macros.h"
-#include "hdr/types/sigset_t.h"
-#include "src/__support/OSUtil/linux/syscall.h" // syscall_impl
-#include "src/__support/OSUtil/linux/syscall_wrappers/rt_sigprocmask.h"
-#include "src/__support/common.h"
-#include "src/__support/error_or.h"
-#include "src/__support/macros/config.h"
-#include <sys/syscall.h> // For syscall numbers
-
-namespace LIBC_NAMESPACE_DECL {
-namespace linux_syscalls {
-LIBC_INLINE ErrorOr<int> raise(int sig) {
-  class SigMaskGuard {
-    [[maybe_unused]] sigset_t old_set;
-    [[maybe_unused]] ErrorOr<int> &status;
-
-  public:
-    LIBC_INLINE SigMaskGuard(ErrorOr<int> &status) : old_set{}, status(status) {
-      sigset_t full_set = sigset_t{{-1UL}};
-      status = linux_syscalls::rt_sigprocmask(SIG_BLOCK, &full_set, &old_set);
-    }
-    LIBC_INLINE ~SigMaskGuard() {
-      if (status.has_value()) {
-        auto restore_result =
-            linux_syscalls::rt_sigprocmask(SIG_SETMASK, &old_set, nullptr);
-        if (!restore_result.has_value())
-          status = restore_result.error();
-      }
-    }
-  };
-  ErrorOr<int> status = 0;
-  {
-    SigMaskGuard sig_mask(status);
-
-    if (!status.has_value())
-      return status;
-
-    long pid = syscall_impl<long>(SYS_getpid);
-    if (pid < 0)
-      return Error(-static_cast<int>(pid));
-
-    long tid = syscall_impl<long>(SYS_gettid);
-    if (tid < 0)
-      return Error(-static_cast<int>(tid));
-
-    int result = syscall_impl<int>(SYS_tgkill, pid, tid, sig);
-    if (result < 0)
-      return Error(-result);
-  }
-  return status;
-}
-
-} // namespace linux_syscalls
-} // namespace LIBC_NAMESPACE_DECL
-
-#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_RAISE_H
diff --git a/libc/src/signal/linux/raise.cpp b/libc/src/signal/linux/raise.cpp
deleted file mode 100644
index 9425e95e46d00..0000000000000
--- a/libc/src/signal/linux/raise.cpp
+++ /dev/null
@@ -1,27 +0,0 @@
-//===-- Linux implementation of signal ------------------------------------===//
-//
-// 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/signal/raise.h"
-
-#include "src/__support/OSUtil/linux/syscall_wrappers/raise.h"
-#include "src/__support/common.h"
-#include "src/__support/libc_errno.h"
-#include "src/__support/macros/config.h"
-
-namespace LIBC_NAMESPACE_DECL {
-
-LLVM_LIBC_FUNCTION(int, raise, (int sig)) {
-  auto result = linux_syscalls::raise(sig);
-  if (!result.has_value()) {
-    libc_errno = result.error();
-    return -1;
-  }
-  return result.value();
-}
-
-} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/signal/raise.h b/libc/src/signal/raise.h
deleted file mode 100644
index f6b9b48e8010b..0000000000000
--- a/libc/src/signal/raise.h
+++ /dev/null
@@ -1,20 +0,0 @@
-//===-- Implementation header for raise function ----------------*- 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_SIGNAL_RAISE_H
-#define LLVM_LIBC_SRC_SIGNAL_RAISE_H
-
-#include "src/__support/macros/config.h"
-
-namespace LIBC_NAMESPACE_DECL {
-
-int raise(int sig);
-
-} // namespace LIBC_NAMESPACE_DECL
-
-#endif // LLVM_LIBC_SRC_SIGNAL_RAISE_H
diff --git a/libc/src/unistd/getpid.h b/libc/src/unistd/getpid.h
deleted file mode 100644
index 9e2f156266b92..0000000000000
--- a/libc/src/unistd/getpid.h
+++ /dev/null
@@ -1,22 +0,0 @@
-//===-- Implementation header for getpid ------------------------*- 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_UNISTD_GETPID_H
-#define LLVM_LIBC_SRC_UNISTD_GETPID_H
-
-#include "hdr/types/pid_t.h"
-#include "hdr/unistd_macros.h"
-#include "src/__support/macros/config.h"
-
-namespace LIBC_NAMESPACE_DECL {
-
-pid_t getpid();
-
-} // namespace LIBC_NAMESPACE_DECL
-
-#endif // LLVM_LIBC_SRC_UNISTD_GETPID_H
diff --git a/libc/src/unistd/linux/getpid.cpp b/libc/src/unistd/linux/getpid.cpp
deleted file mode 100644
index b24c86a15990f..0000000000000
--- a/libc/src/unistd/linux/getpid.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
-//===-- Linux implementation of getpid ------------------------------------===//
-//
-// 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/unistd/getpid.h"
-
-#include "src/__support/OSUtil/syscall.h" // For internal syscall function.
-#include "src/__support/common.h"
-#include "src/__support/macros/config.h"
-
-#include <sys/syscall.h> // For syscall numbers.
-
-namespace LIBC_NAMESPACE_DECL {
-
-LLVM_LIBC_FUNCTION(pid_t, getpid, ()) {
-  return LIBC_NAMESPACE::syscall_impl<pid_t>(SYS_getpid);
-}
-
-} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/test/src/signal/raise_test.cpp b/libc/test/src/signal/raise_test.cpp
deleted file mode 100644
index cb9f7abd8bb89..0000000000000
--- a/libc/test/src/signal/raise_test.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-//===-- Unittests for raise -----------------------------------------------===//
-//
-// 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 "hdr/signal_macros.h"
-#include "src/signal/raise.h"
-#include "test/UnitTest/Test.h"
-
-TEST(LlvmLibcSignalTest, Raise) {
-  // SIGCONT is ingored unless stopped, so we can use it to check the return
-  // value of raise without needing to block.
-  EXPECT_EQ(LIBC_NAMESPACE::raise(SIGCONT), 0);
-
-  // SIGKILL is chosen because other fatal signals could be caught by sanitizers
-  // for example and incorrectly report test failure.
-  EXPECT_DEATH([] { LIBC_NAMESPACE::raise(SIGKILL); }, WITH_SIGNAL(SIGKILL));
-}
diff --git a/libc/test/src/unistd/getpid_test.cpp b/libc/test/src/unistd/getpid_test.cpp
deleted file mode 100644
index 9d6c926415ee4..0000000000000
--- a/libc/test/src/unistd/getpid_test.cpp
+++ /dev/null
@@ -1,15 +0,0 @@
-//===-- Unittests for getpid ----------------------------------------------===//
-//
-// 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/unistd/getpid.h"
-#include "test/UnitTest/Test.h"
-
-TEST(LlvmLibcGetPidTest, SmokeTest) {
-  // getpid always succeeds. So, we just call it as a smoke test.
-  LIBC_NAMESPACE::getpid();
-}



More information about the libc-commits mailing list