This is an automated email from the ASF dual-hosted git repository.

jiuzhudong pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit 8f0e30b07c0a16d60da20f77d5135d778d32d19f
Author: Yanfeng Liu <[email protected]>
AuthorDate: Mon May 19 07:46:23 2025 +0800

    libc/perf: userspace PMU access
    
    Add ARCH_HAVE_PERF_EVENTS_USER_ACCESS capability to allow applications
    to directly access hardware perf counters via perf_gettime() from
    userspace, enabling performance monitoring and profiling without syscalls.
    
    Signed-off-by: Yanfeng Liu <[email protected]>
---
 arch/Kconfig                   |  7 ++++
 libs/libc/sched/CMakeLists.txt |  4 ++
 libs/libc/sched/Make.defs      |  4 ++
 libs/libc/sched/clock_perf.c   | 91 ++++++++++++++++++++++++++++++++++++++++++
 sched/clock/clock_perf.c       | 17 +++++---
 5 files changed, 118 insertions(+), 5 deletions(-)

diff --git a/arch/Kconfig b/arch/Kconfig
index 4a44cf7d335..63005207278 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -605,6 +605,13 @@ config ARCH_HAVE_PERF_EVENTS
        ---help---
                The architecture supports hardware performance counting.
 
+config ARCH_HAVE_PERF_EVENTS_USER_ACCESS
+       bool
+       default n
+       select ARCH_HAVE_PERF_EVENTS
+       ---help---
+               Select if hardware allows userspace perf counter access.
+
 config ARCH_PERF_EVENTS
        bool "Configure hardware performance counting"
        default y if SCHED_CRITMONITOR || SCHED_IRQMONITOR || RPMSG_PING || 
SEGGER_SYSVIEW
diff --git a/libs/libc/sched/CMakeLists.txt b/libs/libc/sched/CMakeLists.txt
index 70f76c1e014..9fb42c09fe2 100644
--- a/libs/libc/sched/CMakeLists.txt
+++ b/libs/libc/sched/CMakeLists.txt
@@ -43,4 +43,8 @@ if(NOT CONFIG_BUILD_KERNEL)
   list(APPEND SRCS task_startup.c)
 endif()
 
+if(CONFIG_ARCH_HAVE_PERF_EVENTS_USER_ACCESS)
+  list(APPEND SRCS clock_perf.c)
+endif()
+
 target_sources(c PRIVATE ${SRCS})
diff --git a/libs/libc/sched/Make.defs b/libs/libc/sched/Make.defs
index 24793634708..f3ffae413c2 100644
--- a/libs/libc/sched/Make.defs
+++ b/libs/libc/sched/Make.defs
@@ -39,6 +39,10 @@ ifneq ($(CONFIG_BUILD_KERNEL),y)
 CSRCS += task_startup.c
 endif # CONFIG_BUILD_KERNEL
 
+ifeq ($(CONFIG_ARCH_HAVE_PERF_EVENTS_USER_ACCESS),y)
+  CSRCS += clock_perf.c
+endif
+
 # Add the sched directory to the build
 
 DEPPATH += --dep-path sched
diff --git a/libs/libc/sched/clock_perf.c b/libs/libc/sched/clock_perf.c
new file mode 100644
index 00000000000..097d85bd0d7
--- /dev/null
+++ b/libs/libc/sched/clock_perf.c
@@ -0,0 +1,91 @@
+/****************************************************************************
+ * libs/libc/sched/clock_perf.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <stdint.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/bits.h>
+#include <nuttx/clock.h>
+
+#if defined(CONFIG_PERF_OVERFLOW_CORRECTION) && ULONG_MAX != UINT64_MAX
+
+/****************************************************************************
+ * Preprocessors
+ ****************************************************************************/
+
+#define MASK_LO        GENMASK_ULL(31, 0)
+#define MASK_HI        GENMASK_ULL(63, 32)
+
+#define LO(x)          (uint32_t)((x) & MASK_LO)
+#define HI(x)          (uint32_t)(((x) & MASK_HI) >> 32)
+
+#define PACK64(hi,lo)  ((MASK_LO & (lo)) | (((uint64_t)(hi)) << 32))
+#define CLOCK_T(p)     (LO(p) | ((clock_t)HI(p) << \
+                                 CONFIG_ARCH_PERF_COUNT_BITWIDTH))
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static atomic64_t g_perf;  /* hi word is overflow, lo word is last */
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * perf_gettime
+ ****************************************************************************/
+
+clock_t perf_gettime(void)
+{
+  uint64_t snap;
+  uint64_t result;
+  clock_t now;
+
+  do
+    {
+      snap = atomic64_read(&g_perf);
+      now = up_perf_gettime();
+      result = PACK64(now < LO(snap) ? HI(snap) + 1 : HI(snap), now);
+    }
+  while (!atomic64_try_cmpxchg(&g_perf, &snap, result));
+
+  return CLOCK_T(result);
+}
+
+#else
+
+/****************************************************************************
+ * perf_gettime
+ ****************************************************************************/
+
+clock_t perf_gettime(void)
+{
+  return up_perf_gettime();
+}
+
+#endif
diff --git a/sched/clock/clock_perf.c b/sched/clock/clock_perf.c
index 76db84aabe9..33c8398410c 100644
--- a/sched/clock/clock_perf.c
+++ b/sched/clock/clock_perf.c
@@ -31,7 +31,13 @@
 #include <nuttx/spinlock.h>
 #include <nuttx/wdog.h>
 
-#if defined(CONFIG_PERF_OVERFLOW_CORRECTION) && ULONG_MAX != UINT64_MAX
+#ifndef CONFIG_ARCH_HAVE_PERF_EVENTS_USER_ACCESS
+
+/****************************************************************************
+ * Preprocessors
+ ****************************************************************************/
+
+#  if defined(CONFIG_PERF_OVERFLOW_CORRECTION) && ULONG_MAX != UINT64_MAX
 
 /****************************************************************************
  * Private Types
@@ -104,8 +110,8 @@ clock_t perf_gettime(void)
   return result;
 }
 
-#elif defined(CONFIG_ALARM_ARCH) || defined (CONFIG_TIMER_ARCH) || \
-      defined(CONFIG_ARCH_PERF_EVENTS)
+#  elif defined(CONFIG_ALARM_ARCH) || defined (CONFIG_TIMER_ARCH) || \
+        defined(CONFIG_ARCH_PERF_EVENTS)
 
 /****************************************************************************
  * perf_gettime
@@ -116,7 +122,7 @@ clock_t perf_gettime(void)
   return up_perf_gettime();
 }
 
-#else
+#  else
 
 /****************************************************************************
  * perf_gettime
@@ -127,7 +133,8 @@ clock_t perf_gettime(void)
   return clock_systime_ticks();
 }
 
-#endif
+#  endif
+#endif /* !CONFIG_ARCH_HAVE_PERF_EVENTS_USER_ACCESS */
 
 #if defined(CONFIG_ALARM_ARCH) || defined (CONFIG_TIMER_ARCH) || \
     defined(CONFIG_ARCH_PERF_EVENTS)

Reply via email to