[libc-commits] [libc] [libc] Enabling code coverage using continuous instrumentation profiling (PR #213271)

Tapiwa Gonga via libc-commits libc-commits at lists.llvm.org
Fri Sep 4 07:50:22 PDT 2026


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

>From ea1c8d4664361b4d6f859baf12f0975ac00e4eeb 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 +++
 .../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 +-
 5 files changed, 251 insertions(+), 4 deletions(-)
 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/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 b20f945dc0d341b2640e718d2a360b0ff098248d 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/test/UnitTest/Coverage.h              | 3 +--
 libc/test/UnitTest/ExecuteFunctionUnix.cpp | 4 ++--
 libc/test/UnitTest/LibcTestMain.cpp        | 1 -
 3 files changed, 3 insertions(+), 5 deletions(-)

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 0161fa19e70579de5149f348114bac038357b144 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 +++----
 libc/test/UnitTest/Coverage.h              | 10 +++++++++-
 libc/test/UnitTest/ExecuteFunctionUnix.cpp | 12 +++++-------
 libc/test/UnitTest/LibcTestMain.cpp        |  4 ++--
 4 files changed, 19 insertions(+), 14 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/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 276ef8c15a64f62f53930d8cc732231e07810baa Mon Sep 17 00:00:00 2001
From: Tapiwa Gonga <tapiwagonga at google.com>
Date: Wed, 2 Sep 2026 11:00:31 +0000
Subject: [PATCH 4/7] Add Clang compiler check for coverage flags and remove
 getpid.h

---
 libc/CMakeLists.txt                           | 42 ++++++++++---------
 .../OSUtil/linux/syscall_wrappers/getpid.h    | 31 --------------
 .../OSUtil/linux/syscall_wrappers/raise.h     |  3 +-
 3 files changed, 24 insertions(+), 52 deletions(-)
 delete mode 100644 libc/src/__support/OSUtil/linux/syscall_wrappers/getpid.h

diff --git a/libc/CMakeLists.txt b/libc/CMakeLists.txt
index 08bd4bb54f793..37d7add6f2957 100644
--- a/libc/CMakeLists.txt
+++ b/libc/CMakeLists.txt
@@ -93,28 +93,32 @@ set(LIBC_TEST_LINK_OPTIONS_DEFAULT "" CACHE STRING "Common link options for all
 
 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")
-
-  # 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")
+    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")
+
+    # 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 "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(FATAL_ERROR "Coverage instrumentation is currently only supported with Clang")
   endif()
 endif()
 
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 7644b67449fdb..0000000000000
--- a/libc/src/__support/OSUtil/linux/syscall_wrappers/getpid.h
+++ /dev/null
@@ -1,31 +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
-/// Syscall wrapper 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 {
-
-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));
 

>From 7fdbe752a5e732aee59fb879241fd798e3522fd0 Mon Sep 17 00:00:00 2001
From: Tapiwa Gonga <tapiwagonga at google.com>
Date: Wed, 2 Sep 2026 13:16:23 +0000
Subject: [PATCH 5/7] [libc][test] Use raw SYS_getpid syscall in Coverage.h to
 remove getpid.h dependency

---
 libc/test/UnitTest/Coverage.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libc/test/UnitTest/Coverage.h b/libc/test/UnitTest/Coverage.h
index 6fff837aebac6..ca691d74982ff 100644
--- a/libc/test/UnitTest/Coverage.h
+++ b/libc/test/UnitTest/Coverage.h
@@ -24,8 +24,8 @@
 #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.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"
@@ -108,7 +108,7 @@ extern "C" void write_raw_profile() {
   }
 
   // Create a minimal filename: libc_cov_<pid>.profraw
-  pid_t pid = LIBC_NAMESPACE::linux_syscalls::getpid();
+  long pid = LIBC_NAMESPACE::syscall_impl<long>(SYS_getpid);
   if (pid <= 0)
     pid = 1;
 

>From 71ac974a2136f9291b2e512ea18619012ec029fe Mon Sep 17 00:00:00 2001
From: Tapiwa Gonga <tapiwagonga at google.com>
Date: Fri, 4 Sep 2026 10:53:19 +0000
Subject: [PATCH 6/7] [libc] Use continuous profiling mode for standalone code
 coverage

Switch the code coverage pipeline to use Clang's continuous profiling mode
(-fprofile-continuous with -fprofile-instr-generate=libc_cov_%p.profraw):

- Removes libc/test/UnitTest/Coverage.h and raw Linux syscall profile writer.
- Reverts libc/test/UnitTest/ExecuteFunctionUnix.cpp to upstream main,
  removing fatal signal handlers and weak symbol overrides.
- Reverts libc/test/UnitTest/LibcTestMain.cpp to upstream main.
- Simplifies libc/CMakeLists.txt by removing manual compiler-rt profile
  static library path resolution, allowing Clang to handle profiling
  runtime requirements natively.

Coverage counters and decision bitmasks are mapped directly to disk via
OS page cache, providing full standalone coverage without custom signal
interception or manual dump routines.

Assisted-by: Automated tooling, human reviewed.
---
 libc/CMakeLists.txt                           |  28 +--
 .../OSUtil/linux/syscall_wrappers/getpid.h    |  31 ++++
 libc/test/UnitTest/Coverage.h                 | 169 ------------------
 libc/test/UnitTest/ExecuteFunctionUnix.cpp    |  46 -----
 libc/test/UnitTest/LibcTestMain.cpp           |  13 +-
 5 files changed, 40 insertions(+), 247 deletions(-)
 create mode 100644 libc/src/__support/OSUtil/linux/syscall_wrappers/getpid.h
 delete mode 100644 libc/test/UnitTest/Coverage.h

diff --git a/libc/CMakeLists.txt b/libc/CMakeLists.txt
index 37d7add6f2957..d86496f1f059b 100644
--- a/libc/CMakeLists.txt
+++ b/libc/CMakeLists.txt
@@ -95,28 +95,12 @@ option(LIBC_ENABLE_COVERAGE "Build libc with coverage instrumentation" OFF)
 if(LIBC_ENABLE_COVERAGE)
   if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
     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")
-
-    # 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()
+    list(APPEND LIBC_COMPILE_OPTIONS_DEFAULT "-fprofile-instr-generate=libc_cov_%p.profraw"
+         "-fcoverage-mapping" "-fprofile-continuous")
+    list(APPEND LIBC_TEST_COMPILE_OPTIONS_DEFAULT "-fprofile-instr-generate=libc_cov_%p.profraw"
+         "-fcoverage-mapping" "-fprofile-continuous")
+    list(APPEND LIBC_TEST_LINK_OPTIONS_DEFAULT "-fprofile-instr-generate=libc_cov_%p.profraw"
+         "-fcoverage-mapping" "-fprofile-continuous")
   else()
     message(FATAL_ERROR "Coverage instrumentation is currently only supported with Clang")
   endif()
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..7644b67449fdb
--- /dev/null
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/getpid.h
@@ -0,0 +1,31 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+/// Syscall wrapper 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 {
+
+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/test/UnitTest/Coverage.h b/libc/test/UnitTest/Coverage.h
deleted file mode 100644
index ca691d74982ff..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.h"
-#include "src/__support/OSUtil/linux/syscall_wrappers/close.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
-  long pid = LIBC_NAMESPACE::syscall_impl<long>(SYS_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 2c8a5aa4b7f65..a07c92f61225c 100644
--- a/libc/test/UnitTest/ExecuteFunctionUnix.cpp
+++ b/libc/test/UnitTest/ExecuteFunctionUnix.cpp
@@ -18,7 +18,6 @@
 #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"
@@ -27,7 +26,6 @@
 #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
@@ -44,10 +42,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,25 +58,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];
   if (LIBC_IMPL::pipe(pipe_fds) == -1) {
@@ -100,31 +75,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);
   }
-
   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 60bad42003645..c348d5ef1aa1b 100644
--- a/libc/test/UnitTest/LibcTestMain.cpp
+++ b/libc/test/UnitTest/LibcTestMain.cpp
@@ -43,10 +43,8 @@ TestOptions parseOptions(int argc, char **argv) {
 
 } // anonymous namespace
 
-#if defined(LIBC_ENABLE_COVERAGE)
-#include "Coverage.h"
-#endif
-
+// The C++ standard forbids declaring the main function with a linkage specifier
+// outisde of 'freestanding' mode, only define the linkage for hermetic tests.
 #if __STDC_HOSTED__
 #define TEST_MAIN int main
 #else
@@ -58,10 +56,5 @@ TEST_MAIN(int argc, char **argv, char **envp) {
   LIBC_NAMESPACE::testing::argv = argv;
   LIBC_NAMESPACE::testing::envp = envp;
 
-  int result =
-      LIBC_NAMESPACE::testing::Test::runTests(parseOptions(argc, argv));
-#if defined(LIBC_ENABLE_COVERAGE)
-  write_raw_profile();
-#endif
-  return result;
+  return LIBC_NAMESPACE::testing::Test::runTests(parseOptions(argc, argv));
 }

>From 1d8b15b19f228defd2d71f39e683aeaf9ecb2aab Mon Sep 17 00:00:00 2001
From: Tapiwa Gonga <tapiwagonga at google.com>
Date: Fri, 4 Sep 2026 14:50:00 +0000
Subject: [PATCH 7/7] [libc] Clean up continuous coverage CMake configuration

- Remove unused LIBC_ENABLE_COVERAGE preprocessor definition.
- Deduplicate coverage flags by applying them solely to LIBC_COMPILE_OPTIONS_DEFAULT.
- Remove frontend-only -fcoverage-mapping from linker options.
- Standardize comments and 80-column formatting.

Assisted-by: Automated tooling, human reviewed.
---
 libc/CMakeLists.txt | 18 ++++++++++--------
 1 file changed, 10 insertions(+), 8 deletions(-)

diff --git a/libc/CMakeLists.txt b/libc/CMakeLists.txt
index d86496f1f059b..9b7f0737650f0 100644
--- a/libc/CMakeLists.txt
+++ b/libc/CMakeLists.txt
@@ -91,18 +91,20 @@ 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.")
 
+# Enable source-based code coverage using Clang's continuous profiling mode.
 option(LIBC_ENABLE_COVERAGE "Build libc with coverage instrumentation" OFF)
 if(LIBC_ENABLE_COVERAGE)
   if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
-    add_compile_definitions(LIBC_ENABLE_COVERAGE)
-    list(APPEND LIBC_COMPILE_OPTIONS_DEFAULT "-fprofile-instr-generate=libc_cov_%p.profraw"
-         "-fcoverage-mapping" "-fprofile-continuous")
-    list(APPEND LIBC_TEST_COMPILE_OPTIONS_DEFAULT "-fprofile-instr-generate=libc_cov_%p.profraw"
-         "-fcoverage-mapping" "-fprofile-continuous")
-    list(APPEND LIBC_TEST_LINK_OPTIONS_DEFAULT "-fprofile-instr-generate=libc_cov_%p.profraw"
-         "-fcoverage-mapping" "-fprofile-continuous")
+    list(APPEND LIBC_COMPILE_OPTIONS_DEFAULT
+         "-fprofile-instr-generate=libc_cov_%p.profraw"
+         "-fcoverage-mapping"
+         "-fprofile-continuous")
+    list(APPEND LIBC_TEST_LINK_OPTIONS_DEFAULT
+         "-fprofile-instr-generate=libc_cov_%p.profraw"
+         "-fprofile-continuous")
   else()
-    message(FATAL_ERROR "Coverage instrumentation is currently only supported with Clang")
+    message(FATAL_ERROR
+            "Coverage instrumentation is currently only supported with Clang")
   endif()
 endif()
 



More information about the libc-commits mailing list