[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