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

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

commit 4944bfd56cd7f00b6568e945a7bf3ffab126be33
Author: Xiang Xiao <[email protected]>
AuthorDate: Tue Mar 12 01:48:22 2024 +0800

    sched: Implement profil function
    
    which is very useful for performance analysis:
    https://man7.org/linux/man-pages/man3/profil.3.html
    
    Signed-off-by: Xiang Xiao <[email protected]>
---
 include/unistd.h           |   3 +
 sched/Kconfig              |   7 +++
 sched/sched/CMakeLists.txt |   1 +
 sched/sched/Make.defs      |   2 +-
 sched/sched/sched_profil.c | 145 +++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 157 insertions(+), 1 deletion(-)

diff --git a/include/unistd.h b/include/unistd.h
index 25a81134a9..9f3191c7c2 100644
--- a/include/unistd.h
+++ b/include/unistd.h
@@ -466,6 +466,9 @@ int     getentropy(FAR void *buffer, size_t length);
 void    sync(void);
 int     syncfs(int fd);
 
+int     profil(FAR unsigned short *buf, size_t bufsiz,
+               size_t offset, unsigned int scale);
+
 #if CONFIG_FORTIFY_SOURCE > 0
 fortify_function(getcwd) FAR char *getcwd(FAR char *buf,
                                           size_t size)
diff --git a/sched/Kconfig b/sched/Kconfig
index 870e217987..02323f936d 100644
--- a/sched/Kconfig
+++ b/sched/Kconfig
@@ -1162,6 +1162,13 @@ config SCHED_CPULOAD_TIMECONSTANT
                tick count exceeds this time constant.  This time constant is in
                units of seconds.
 
+config SCHED_PROFILE_TICKSPERSEC
+       int "Profile sampling rate"
+       default 1000
+       ---help---
+               This is the frequency at which the profil functon will sample 
the
+               running program. The default is 1000Hz.
+
 menuconfig SCHED_INSTRUMENTATION
        bool "System performance monitor hooks"
        default n
diff --git a/sched/sched/CMakeLists.txt b/sched/sched/CMakeLists.txt
index 1c26a91184..0dad747d42 100644
--- a/sched/sched/CMakeLists.txt
+++ b/sched/sched/CMakeLists.txt
@@ -21,6 +21,7 @@
 # 
##############################################################################
 set(SRCS
     sched_getfiles.c
+    sched_profil.c
     sched_addreadytorun.c
     sched_removereadytorun.c
     sched_addprioritized.c
diff --git a/sched/sched/Make.defs b/sched/sched/Make.defs
index e8455a5bc1..3c026b8cba 100644
--- a/sched/sched/Make.defs
+++ b/sched/sched/Make.defs
@@ -20,7 +20,7 @@
 #
 ############################################################################
 
-CSRCS += sched_getfiles.c
+CSRCS += sched_getfiles.c sched_profil.c
 CSRCS += sched_addreadytorun.c sched_removereadytorun.c
 CSRCS += sched_addprioritized.c sched_mergeprioritized.c sched_mergepending.c
 CSRCS += sched_addblocked.c sched_removeblocked.c
diff --git a/sched/sched/sched_profil.c b/sched/sched/sched_profil.c
new file mode 100644
index 0000000000..7afe36f6c2
--- /dev/null
+++ b/sched/sched/sched_profil.c
@@ -0,0 +1,145 @@
+/****************************************************************************
+ * sched/sched/sched_profil.c
+ *
+ * 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 <errno.h>
+#include <unistd.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/wdog.h>
+#include <nuttx/spinlock.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define PROFTICK NSEC2TICK(NSEC_PER_SEC / CONFIG_SCHED_PROFILE_TICKSPERSEC)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct profinfo_s
+{
+  FAR unsigned short *counter; /* Histogram PC sample array */
+  uintptr_t lowpc;             /* Range to be profiled */
+  uintptr_t highpc;            /* Range to be profiled */
+  unsigned int scale;          /* Scale value of bins */
+  struct wdog_s timer;         /* Timer for profiling */
+  spinlock_t lock;             /* Lock for this structure */
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static struct profinfo_s g_prof;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static void profil_timer_handler(wdparm_t arg)
+{
+  FAR struct profinfo_s *prof = (FAR struct profinfo_s *)(uintptr_t)arg;
+  uintptr_t pc = up_getusrpc(NULL);
+  irqstate_t flags;
+
+  flags = spin_lock_irqsave(&prof->lock);
+  if (pc >= prof->lowpc && pc < prof->highpc)
+    {
+      size_t idx = (pc - prof->lowpc) / 2;
+#if UINTMAX_MAX > SIZE_MAX
+      idx = (uintmax_t)idx * prof->scale / 65536;
+#else
+      idx = idx / 65536 * prof->scale + idx % 65536 * prof->scale / 65536;
+#endif
+      prof->counter[idx]++;
+    }
+
+  spin_unlock_irqrestore(&prof->lock, flags);
+  wd_start(&prof->timer, PROFTICK, profil_timer_handler, arg);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: profil
+ *
+ * Description:
+ *   This routine provides a means to find out in what areas your
+ *   program spends most of its time.  The argument buf points to
+ *   bufsiz bytes of core.  the user's program counter (PC) is
+ *   examined SCHED_PROFILE_TICKSPERSEC times in every second:
+ *   offset is subtracted and the result is multiplied by scale
+ *   and divided by 65536.  If the resulting value is less than
+ *   bufsiz, then the corresponding entry in buf is incremented.
+ *   If buf is NULL, profiling is disabled.
+ *
+ * Input Parameters:
+ *   buf    - Buffer to record the hitting count
+ *   bufsiz - Size of buffer in bytes
+ *   offset - The lowest address to be sampled
+ *   scale  - Multiply address by scale / 65536
+ *
+ * Returned Value:
+ *   Zero (OK) if successful. Otherwise, ERROR is returned and
+ *   errno is set to indicate the error.
+ *
+ ****************************************************************************/
+
+int profil(FAR unsigned short *buf, size_t bufsiz,
+           size_t offset, unsigned int scale)
+{
+  FAR struct profinfo_s *prof = &g_prof;
+  irqstate_t flags;
+  uintptr_t highpc;
+
+  if (scale > 65536)
+    {
+      set_errno(EINVAL);
+      return ERROR;
+    }
+
+  if (buf == NULL || scale == 0)
+    {
+      wd_cancel(&prof->timer);
+      return OK;
+    }
+
+  memset(buf, 0, bufsiz);
+  highpc = (uintmax_t)bufsiz * 32768 / scale;
+
+  flags = spin_lock_irqsave(&prof->lock);
+  prof->counter = buf;
+  prof->lowpc   = offset;
+  prof->highpc  = offset + highpc;
+  prof->scale   = scale;
+  spin_unlock_irqrestore(&prof->lock, flags);
+
+  wd_start(&prof->timer, PROFTICK, profil_timer_handler,
+           (wdparm_t)(uintptr_t)prof);
+  return OK;
+}

Reply via email to