[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 04:00:51 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/4] [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/4] [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/4] [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 fc9a1d396d6600483174edd19206ae6aaf436e73 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/4] Add Clang compiler check for coverage flags and remove
getpid.h
---
libc/CMakeLists.txt | 42 +++---
.../linux/syscall_wrappers/CMakeLists.txt | 124 ++++++++++++++++--
.../OSUtil/linux/syscall_wrappers/getpid.h | 32 -----
.../OSUtil/linux/syscall_wrappers/raise.h | 3 +-
4 files changed, 134 insertions(+), 67 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/CMakeLists.txt b/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
index 1d79c82d94b80..6440e985a94c4 100644
--- a/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
@@ -26,19 +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
@@ -410,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
@@ -908,6 +894,7 @@ add_header_library(
HDRS
sysinfo.h
DEPENDS
+ libc.hdr.types.struct_sysinfo
libc.src.__support.OSUtil.osutil
libc.src.__support.common
libc.src.__support.error_or
@@ -954,3 +941,112 @@ add_header_library(
libc.src.__support.macros.config
libc.include.sys_syscall
)
+
+add_header_library(
+ rt_sigaction
+ HDRS
+ rt_sigaction.h
+ DEPENDS
+ libc.hdr.types.sigset_t
+ libc.src.__support.OSUtil.osutil
+ libc.src.__support.common
+ libc.src.__support.error_or
+ libc.src.__support.macros.config
+ libc.include.sys_syscall
+)
+
+add_header_library(
+ clone3
+ HDRS
+ clone3.h
+ DEPENDS
+ libc.hdr.types.pid_t
+ libc.src.__support.OSUtil.osutil
+ libc.src.__support.common
+ libc.src.__support.error_or
+ libc.src.__support.macros.config
+ libc.include.sys_syscall
+)
+
+add_header_library(
+ execle
+ HDRS
+ execle.h
+ DEPENDS
+ libc.src.__support.OSUtil.osutil
+ libc.src.__support.common
+ libc.src.__support.error_or
+ libc.src.__support.macros.config
+ libc.include.sys_syscall
+)
+
+add_header_library(
+ wait4
+ HDRS
+ wait4.h
+ DEPENDS
+ libc.hdr.types.pid_t
+ libc.hdr.types.struct_rusage
+ libc.src.__support.OSUtil.osutil
+ libc.src.__support.common
+ libc.src.__support.error_or
+ libc.src.__support.macros.config
+ libc.include.sys_syscall
+)
+
+add_header_library(
+ execve
+ HDRS
+ execve.h
+ DEPENDS
+ libc.hdr.fcntl_macros
+ libc.src.__support.OSUtil.osutil
+ libc.src.__support.common
+ libc.src.__support.error_or
+ libc.src.__support.macros.config
+ libc.include.sys_syscall
+)
+
+add_header_library(
+ timer_create
+ HDRS
+ timer_create.h
+ DEPENDS
+ libc.hdr.signal_macros
+ libc.hdr.types.clockid_t
+ libc.hdr.types.pid_t
+ libc.hdr.types.struct_sigevent
+ libc.hdr.types.timer_t
+ libc.include.sys_syscall
+ libc.src.__support.OSUtil.osutil
+ libc.src.__support.common
+ libc.src.__support.error_or
+ libc.src.__support.macros.config
+)
+
+add_header_library(
+ timer_delete
+ HDRS
+ timer_delete.h
+ DEPENDS
+ libc.hdr.types.timer_t
+ libc.include.sys_syscall
+ libc.src.__support.OSUtil.osutil
+ libc.src.__support.common
+ libc.src.__support.error_or
+ libc.src.__support.macros.config
+)
+
+add_header_library(
+ clock_getres
+ HDRS
+ clock_getres.h
+ DEPENDS
+ libc.hdr.types.clockid_t
+ libc.hdr.types.struct_timespec
+ libc.include.sys_syscall
+ libc.src.__support.OSUtil.osutil
+ libc.src.__support.common
+ libc.src.__support.error_or
+ libc.src.__support.macros.config
+)
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));
More information about the libc-commits
mailing list