[libc-commits] [libc] [libc] Implement clock_getcpuclockid on Linux. (PR #218531)
Alexey Samsonov via libc-commits
libc-commits at lists.llvm.org
Mon Aug 24 14:49:16 PDT 2026
https://github.com/vonosmas created https://github.com/llvm/llvm-project/pull/218531
Implement `clock_getcpuclockid` POSIX function by mimicking the Linux kernel conversion of pid_t to the clockid_t, and validating the result / permissions with `clock_getres` syscall. Add the corresponding syscall wrapper for the latter.
>From 1ebcd662405a0bc5f2db81f8acf9744e54c5af9e Mon Sep 17 00:00:00 2001
From: Alexey Samsonov <vonosmas at gmail.com>
Date: Mon, 24 Aug 2026 21:43:32 +0000
Subject: [PATCH 1/2] [libc] Implement clock_getcpuclockid on Linux.
---
libc/config/linux/aarch64/entrypoints.txt | 1 +
libc/config/linux/riscv/entrypoints.txt | 1 +
libc/config/linux/x86_64/entrypoints.txt | 1 +
libc/include/CMakeLists.txt | 2 +
libc/include/sys/syscall.h.def | 4 ++
libc/include/time.yaml | 9 ++-
.../linux/syscall_wrappers/CMakeLists.txt | 13 +++++
.../linux/syscall_wrappers/clock_getres.h | 49 +++++++++++++++++
libc/src/time/CMakeLists.txt | 8 +++
libc/src/time/clock_getcpuclockid.h | 27 +++++++++
libc/src/time/linux/CMakeLists.txt | 16 ++++++
libc/src/time/linux/clock_getcpuclockid.cpp | 43 +++++++++++++++
libc/test/src/time/CMakeLists.txt | 17 ++++++
.../src/time/clock_getcpuclockid_test.cpp | 55 +++++++++++++++++++
14 files changed, 245 insertions(+), 1 deletion(-)
create mode 100644 libc/src/__support/OSUtil/linux/syscall_wrappers/clock_getres.h
create mode 100644 libc/src/time/clock_getcpuclockid.h
create mode 100644 libc/src/time/linux/clock_getcpuclockid.cpp
create mode 100644 libc/test/src/time/clock_getcpuclockid_test.cpp
diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index dd20e979745c0..dfba85299c4da 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -1325,6 +1325,7 @@ if(LLVM_LIBC_FULL_BUILD)
libc.src.time.ctime
libc.src.time.ctime_r
libc.src.time.clock
+ libc.src.time.clock_getcpuclockid
libc.src.time.clock_gettime
libc.src.time.clock_settime
libc.src.time.difftime
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index 52f30060acea1..507670684930c 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -1517,6 +1517,7 @@ if(LLVM_LIBC_FULL_BUILD)
libc.src.time.ctime
libc.src.time.ctime_r
libc.src.time.clock
+ libc.src.time.clock_getcpuclockid
libc.src.time.clock_gettime
libc.src.time.clock_settime
libc.src.time.difftime
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index e78b4d47edd9f..7420a4db3330c 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -1530,6 +1530,7 @@ if(LLVM_LIBC_FULL_BUILD)
libc.src.time.ctime
libc.src.time.ctime_r
libc.src.time.clock
+ libc.src.time.clock_getcpuclockid
libc.src.time.clock_gettime
libc.src.time.clock_settime
libc.src.time.difftime
diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt
index eb06aeb133053..5de40c848aa67 100644
--- a/libc/include/CMakeLists.txt
+++ b/libc/include/CMakeLists.txt
@@ -377,6 +377,8 @@ add_header_macro(
.llvm-libc-types.tm_gmtoff_t
.llvm-libc-types.locale_t
.llvm-libc-types.size_t
+ .llvm-libc-types.struct_sigevent
+ .llvm-libc-types.pid_t
)
add_header_macro(
diff --git a/libc/include/sys/syscall.h.def b/libc/include/sys/syscall.h.def
index d197e7fed2337..7235d33f50ee1 100644
--- a/libc/include/sys/syscall.h.def
+++ b/libc/include/sys/syscall.h.def
@@ -169,6 +169,10 @@
#define SYS_clock_getres __NR_clock_getres
#endif
+#ifdef __NR_clock_getres_time64
+#define SYS_clock_getres_time64 __NR_clock_getres_time64
+#endif
+
#ifdef __NR_clock_gettime
#define SYS_clock_gettime __NR_clock_gettime
#endif
diff --git a/libc/include/time.yaml b/libc/include/time.yaml
index 147bd6f5643a6..c11196bfeafc3 100644
--- a/libc/include/time.yaml
+++ b/libc/include/time.yaml
@@ -19,6 +19,7 @@ types:
- type_name: size_t
- type_name: locale_t
- type_name: tm_gmtoff_t
+ - type_name: pid_t
enums: []
objects: []
functions:
@@ -168,4 +169,10 @@ functions:
return_type: int
arguments:
- type: timer_t
-
+ - name: clock_getcpuclockid
+ standards:
+ - posix
+ return_type: int
+ arguments:
+ - type: pid_t
+ - type: clockid_t *
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt b/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
index e01f2a5789e37..6440e985a94c4 100644
--- a/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
@@ -1037,3 +1037,16 @@ add_header_library(
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/clock_getres.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/clock_getres.h
new file mode 100644
index 0000000000000..7f94eee23264c
--- /dev/null
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/clock_getres.h
@@ -0,0 +1,49 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 clock_getres syscall wrapper.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_CLOCK_GETRES_H
+#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_CLOCK_GETRES_H
+
+#include "hdr/types/clockid_t.h"
+#include "hdr/types/struct_timespec.h"
+#include "src/__support/OSUtil/linux/syscall.h" // syscall_checked
+#include "src/__support/common.h"
+#include "src/__support/error_or.h"
+#include "src/__support/macros/config.h"
+#include <sys/syscall.h>
+
+namespace LIBC_NAMESPACE_DECL {
+namespace linux_syscalls {
+
+LIBC_INLINE ErrorOr<int> clock_getres(clockid_t clockid, timespec *res) {
+#if defined(SYS_clock_getres_time64)
+ static_assert(
+ sizeof(time_t) == sizeof(int64_t),
+ "SYS_clock_getres_time64 requires struct timespec with 64-bit members.");
+ return syscall_checked<int>(SYS_clock_getres_time64, clockid, res);
+#elif defined(SYS_clock_getres)
+ static_assert(
+ sizeof(timespec::tv_nsec) == sizeof(long),
+ "This is only safe on platforms where tv_nsec "
+ "matches the register size (long). It is unsafe on 32-bit platforms "
+ "with 64-bit tv_nsec.");
+ return syscall_checked<int>(SYS_clock_getres, clockid, res);
+#else
+#error "SYS_clock_getres and SYS_clock_getres_time64 syscalls not available."
+#endif
+}
+
+} // namespace linux_syscalls
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_CLOCK_GETRES_H
diff --git a/libc/src/time/CMakeLists.txt b/libc/src/time/CMakeLists.txt
index 49ca72376090c..b82cc31fc63ba 100644
--- a/libc/src/time/CMakeLists.txt
+++ b/libc/src/time/CMakeLists.txt
@@ -314,3 +314,11 @@ add_entrypoint_object(
.${LIBC_TARGET_OS}.timer_delete
)
+add_entrypoint_object(
+ clock_getcpuclockid
+ ALIAS
+ DEPENDS
+ .${LIBC_TARGET_OS}.clock_getcpuclockid
+)
+
+
diff --git a/libc/src/time/clock_getcpuclockid.h b/libc/src/time/clock_getcpuclockid.h
new file mode 100644
index 0000000000000..9da558e26aee4
--- /dev/null
+++ b/libc/src/time/clock_getcpuclockid.h
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 clock_getcpuclockid function.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_TIME_CLOCK_GETCPUCLOCKID_H
+#define LLVM_LIBC_SRC_TIME_CLOCK_GETCPUCLOCKID_H
+
+#include "hdr/types/clockid_t.h"
+#include "hdr/types/pid_t.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int clock_getcpuclockid(pid_t pid, clockid_t *clock_id);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_TIME_CLOCK_GETCPUCLOCKID_H
diff --git a/libc/src/time/linux/CMakeLists.txt b/libc/src/time/linux/CMakeLists.txt
index 3f91aa3528405..3490e8110bc35 100644
--- a/libc/src/time/linux/CMakeLists.txt
+++ b/libc/src/time/linux/CMakeLists.txt
@@ -105,3 +105,19 @@ add_entrypoint_object(
libc.src.errno.errno
)
+add_entrypoint_object(
+ clock_getcpuclockid
+ SRCS
+ clock_getcpuclockid.cpp
+ HDRS
+ ../clock_getcpuclockid.h
+ DEPENDS
+ libc.hdr.errno_macros
+ libc.hdr.types.clockid_t
+ libc.hdr.types.pid_t
+ libc.src.__support.OSUtil.linux.syscall_wrappers.clock_getres
+ libc.src.__support.common
+ libc.src.__support.macros.config
+)
+
+
diff --git a/libc/src/time/linux/clock_getcpuclockid.cpp b/libc/src/time/linux/clock_getcpuclockid.cpp
new file mode 100644
index 0000000000000..9b853a159e1c0
--- /dev/null
+++ b/libc/src/time/linux/clock_getcpuclockid.cpp
@@ -0,0 +1,43 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+/// Linux implementation of clock_getcpuclockid function.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/time/clock_getcpuclockid.h"
+#include "hdr/errno_macros.h"
+#include "hdr/types/clockid_t.h"
+#include "hdr/types/pid_t.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/clock_getres.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, clock_getcpuclockid, (pid_t pid, clockid_t *clock_id)) {
+ if (pid < 0)
+ return ESRCH;
+ // In Linux, process CPU clocks are encoded as
+ // (~pid << 3) | clock_id with clock_id CPUCLOCK_SCHED being 2:
+ // https://github.com/torvalds/linux/blob/master/include/linux/posix-timers.h#L22
+ const clockid_t pid_clockid =
+ static_cast<clockid_t>((static_cast<unsigned int>(~pid) << 3) | 2);
+
+ auto result = linux_syscalls::clock_getres(pid_clockid, nullptr);
+ // Note that the clock_getcpuclockid doesn't set errno, it returns the
+ // error directly.
+ if (!result)
+ return result.error() == EINVAL ? ESRCH : result.error();
+
+ *clock_id = pid_clockid;
+ return 0;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/test/src/time/CMakeLists.txt b/libc/test/src/time/CMakeLists.txt
index cf4c18e8d9d37..e8b553342f7b7 100644
--- a/libc/test/src/time/CMakeLists.txt
+++ b/libc/test/src/time/CMakeLists.txt
@@ -118,6 +118,23 @@ add_libc_test(
libc.hdr.time_macros
)
+add_libc_test(
+ clock_getcpuclockid_test
+ SUITE
+ libc_time_unittests
+ SRCS
+ clock_getcpuclockid_test.cpp
+ DEPENDS
+ libc.hdr.errno_macros
+ libc.hdr.types.clockid_t
+ libc.hdr.types.pid_t
+ libc.hdr.types.struct_timespec
+ libc.src.time.clock_getcpuclockid
+ libc.src.time.clock_gettime
+ libc.src.unistd.getpid
+ libc.test.UnitTest.ErrnoCheckingTest
+)
+
add_libc_test(
clock_getres_test
SUITE
diff --git a/libc/test/src/time/clock_getcpuclockid_test.cpp b/libc/test/src/time/clock_getcpuclockid_test.cpp
new file mode 100644
index 0000000000000..72da8dcb6df6f
--- /dev/null
+++ b/libc/test/src/time/clock_getcpuclockid_test.cpp
@@ -0,0 +1,55 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+/// Unittests for clock_getcpuclockid.
+///
+//===----------------------------------------------------------------------===//
+
+#include "hdr/errno_macros.h"
+#include "hdr/types/clockid_t.h"
+#include "hdr/types/pid_t.h"
+#include "hdr/types/struct_timespec.h"
+#include "src/time/clock_getcpuclockid.h"
+#include "src/time/clock_gettime.h"
+#include "src/unistd/getpid.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/Test.h"
+
+using LlvmLibcClockGetCpuClockIdTest =
+ LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+
+TEST_F(LlvmLibcClockGetCpuClockIdTest, CurrentProcessZeroPid) {
+ clockid_t clock_id = 0;
+ ASSERT_EQ(LIBC_NAMESPACE::clock_getcpuclockid(0, &clock_id), 0);
+ // On Linux, pid 0 translates to (~0 << 3) | 2 == -6.
+ EXPECT_EQ(clock_id, static_cast<clockid_t>(-6));
+
+ struct timespec ts;
+ EXPECT_EQ(LIBC_NAMESPACE::clock_gettime(clock_id, &ts), 0);
+ EXPECT_GE(ts.tv_sec, static_cast<time_t>(0));
+}
+
+TEST_F(LlvmLibcClockGetCpuClockIdTest, CurrentProcessExplicitPid) {
+ pid_t pid = LIBC_NAMESPACE::getpid();
+ clockid_t clock_id = 0;
+ ASSERT_EQ(LIBC_NAMESPACE::clock_getcpuclockid(pid, &clock_id), 0);
+ EXPECT_EQ(clock_id,
+ static_cast<clockid_t>((static_cast<unsigned int>(~pid) << 3) | 2));
+
+ struct timespec ts;
+ EXPECT_EQ(LIBC_NAMESPACE::clock_gettime(clock_id, &ts), 0);
+ EXPECT_GE(ts.tv_sec, static_cast<time_t>(0));
+}
+
+TEST_F(LlvmLibcClockGetCpuClockIdTest, InvalidPid) {
+ clockid_t clock_id = 0;
+ // Invalid PIDs should fail with ESRCH.
+ EXPECT_EQ(LIBC_NAMESPACE::clock_getcpuclockid(-1, &clock_id), ESRCH);
+ EXPECT_EQ(LIBC_NAMESPACE::clock_getcpuclockid(1000000000, &clock_id), ESRCH);
+}
>From aeb98146f28772cf3a002c9c4c49c1d1da1fb64b Mon Sep 17 00:00:00 2001
From: Alexey Samsonov <vonosmas at gmail.com>
Date: Mon, 24 Aug 2026 21:49:00 +0000
Subject: [PATCH 2/2] formatting changes
---
libc/src/time/CMakeLists.txt | 2 --
libc/src/time/linux/CMakeLists.txt | 2 --
libc/src/time/linux/clock_getcpuclockid.cpp | 4 ++--
libc/test/src/time/clock_getcpuclockid_test.cpp | 2 ++
4 files changed, 4 insertions(+), 6 deletions(-)
diff --git a/libc/src/time/CMakeLists.txt b/libc/src/time/CMakeLists.txt
index b82cc31fc63ba..62eea1d9bb428 100644
--- a/libc/src/time/CMakeLists.txt
+++ b/libc/src/time/CMakeLists.txt
@@ -320,5 +320,3 @@ add_entrypoint_object(
DEPENDS
.${LIBC_TARGET_OS}.clock_getcpuclockid
)
-
-
diff --git a/libc/src/time/linux/CMakeLists.txt b/libc/src/time/linux/CMakeLists.txt
index 3490e8110bc35..fce4c344233c1 100644
--- a/libc/src/time/linux/CMakeLists.txt
+++ b/libc/src/time/linux/CMakeLists.txt
@@ -119,5 +119,3 @@ add_entrypoint_object(
libc.src.__support.common
libc.src.__support.macros.config
)
-
-
diff --git a/libc/src/time/linux/clock_getcpuclockid.cpp b/libc/src/time/linux/clock_getcpuclockid.cpp
index 9b853a159e1c0..b71af65a42593 100644
--- a/libc/src/time/linux/clock_getcpuclockid.cpp
+++ b/libc/src/time/linux/clock_getcpuclockid.cpp
@@ -24,8 +24,8 @@ namespace LIBC_NAMESPACE_DECL {
LLVM_LIBC_FUNCTION(int, clock_getcpuclockid, (pid_t pid, clockid_t *clock_id)) {
if (pid < 0)
return ESRCH;
- // In Linux, process CPU clocks are encoded as
- // (~pid << 3) | clock_id with clock_id CPUCLOCK_SCHED being 2:
+ // On Linux, process CPU clocks are encoded as (~pid << 3) | clock_id with
+ // clock_id CPUCLOCK_SCHED being 2:
// https://github.com/torvalds/linux/blob/master/include/linux/posix-timers.h#L22
const clockid_t pid_clockid =
static_cast<clockid_t>((static_cast<unsigned int>(~pid) << 3) | 2);
diff --git a/libc/test/src/time/clock_getcpuclockid_test.cpp b/libc/test/src/time/clock_getcpuclockid_test.cpp
index 72da8dcb6df6f..2a3fd6c640cf8 100644
--- a/libc/test/src/time/clock_getcpuclockid_test.cpp
+++ b/libc/test/src/time/clock_getcpuclockid_test.cpp
@@ -30,6 +30,7 @@ TEST_F(LlvmLibcClockGetCpuClockIdTest, CurrentProcessZeroPid) {
// On Linux, pid 0 translates to (~0 << 3) | 2 == -6.
EXPECT_EQ(clock_id, static_cast<clockid_t>(-6));
+ // Validate that we can use resulting clock_id to get time.
struct timespec ts;
EXPECT_EQ(LIBC_NAMESPACE::clock_gettime(clock_id, &ts), 0);
EXPECT_GE(ts.tv_sec, static_cast<time_t>(0));
@@ -42,6 +43,7 @@ TEST_F(LlvmLibcClockGetCpuClockIdTest, CurrentProcessExplicitPid) {
EXPECT_EQ(clock_id,
static_cast<clockid_t>((static_cast<unsigned int>(~pid) << 3) | 2));
+ // Validate that we can use resulting clock_id to get time.
struct timespec ts;
EXPECT_EQ(LIBC_NAMESPACE::clock_gettime(clock_id, &ts), 0);
EXPECT_GE(ts.tv_sec, static_cast<time_t>(0));
More information about the libc-commits
mailing list