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 eb8449cb0cfb37e93a65696975a86fdceaf5a09c
Author: yinshengkai <[email protected]>
AuthorDate: Tue Mar 12 18:03:39 2024 +0800

    sched/gprof: add gprof support
    
    gprof can analyze code hot spots based on scheduled sampling.
    After adding the "-pg" parameter when compiling, you can view the code call 
graph.
    
    Signed-off-by: yinshengkai <[email protected]>
---
 arch/arm/src/cmake/gcc.cmake         |   4 +
 arch/arm/src/common/Toolchain.defs   |   4 +
 arch/sim/Kconfig                     |   7 +
 arch/sim/src/Makefile                |   7 +-
 boards/sim/sim/sim/scripts/Make.defs |   4 +
 include/sys/gmon.h                   |  54 ++++
 libs/libc/machine/arm/CMakeLists.txt |   4 +
 libs/libc/machine/arm/Make.defs      |   4 +
 libs/libc/machine/arm/gnu/mcount.S   |  41 +++
 sched/Kconfig                        |  21 ++
 sched/instrument/CMakeLists.txt      |   4 +
 sched/instrument/Make.defs           |   4 +
 sched/instrument/profile_monitor.c   | 584 +++++++++++++++++++++++++++++++++++
 sched/sched/sched_profil.c           |   2 +-
 14 files changed, 742 insertions(+), 2 deletions(-)

diff --git a/arch/arm/src/cmake/gcc.cmake b/arch/arm/src/cmake/gcc.cmake
index daa35d6c68..1a44143144 100644
--- a/arch/arm/src/cmake/gcc.cmake
+++ b/arch/arm/src/cmake/gcc.cmake
@@ -167,6 +167,10 @@ if(CONFIG_ARCH_INSTRUMENT_ALL)
   add_compile_options(-finstrument-functions)
 endif()
 
+if(CONFIG_SCHED_GPROF_ALL)
+  add_compile_options(-pg)
+endif()
+
 if(CONFIG_UNWINDER_ARM)
   add_compile_options(-funwind-tables -fasynchronous-unwind-tables)
 endif()
diff --git a/arch/arm/src/common/Toolchain.defs 
b/arch/arm/src/common/Toolchain.defs
index 03f7b6bce0..284b1331ee 100644
--- a/arch/arm/src/common/Toolchain.defs
+++ b/arch/arm/src/common/Toolchain.defs
@@ -72,6 +72,10 @@ ifneq ($(CONFIG_STACK_USAGE_WARNING),0)
   ARCHOPTIMIZATION += -Wstack-usage=$(CONFIG_STACK_USAGE_WARNING)
 endif
 
+ifeq ($(CONFIG_SCHED_GPROF_ALL),y)
+  ARCHOPTIMIZATION += -pg
+endif
+
 ifeq ($(CONFIG_MM_UBSAN_ALL),y)
   ARCHOPTIMIZATION += $(CONFIG_MM_UBSAN_OPTION)
 endif
diff --git a/arch/sim/Kconfig b/arch/sim/Kconfig
index 92ce5edf66..9f4b92b847 100644
--- a/arch/sim/Kconfig
+++ b/arch/sim/Kconfig
@@ -97,6 +97,13 @@ config SIM_GCOV_ALL
                This option activates code coverage instrumentation for the
                entire image.
 
+config SIM_GPROF
+       bool "Enable gprof"
+       depends on !SCHED_GPROF
+       default n
+       ---help---
+               Enable support gprof profiling tool.
+
 choice
        prompt "X64_64 ABI"
        default SIM_X8664_SYSTEMV if HOST_LINUX
diff --git a/arch/sim/src/Makefile b/arch/sim/src/Makefile
index 1152b041b9..9e89a5faeb 100644
--- a/arch/sim/src/Makefile
+++ b/arch/sim/src/Makefile
@@ -144,6 +144,10 @@ ifeq ($(CONFIG_SCHED_GCOV),y)
   STDLIBS += -lgcov
 endif
 
+ifeq ($(CONFIG_SIM_GPROF),y)
+  HOSTCFLAGS += -pg
+endif
+
 ifeq ($(CONFIG_STACK_COLORATION),y)
   CSRCS += sim_checkstack.c
 endif
@@ -423,7 +427,8 @@ nuttx$(EXEEXT): libarch$(LIBEXT) board/libboard$(LIBEXT) 
$(HEADOBJ) $(LINKOBJS)
 ifneq ($(CONFIG_HOST_MACOS),y)
        $(Q) $(OBJCOPY) --redefine-syms=nuttx-names.dat nuttx.rel
        $(Q) $(CC) $(CFLAGS) -Wl,-verbose 2>&1 | \
-            sed -e '/====/,/====/!d;//d' -e 's/__executable_start/_stext/g' \
+            sed -e '/====/,/====/!d;//d' \
+                -e '/__executable_start/s/$$/PROVIDE(_stext = .);/' \
                 -e 's/^\(\s\+\)\(\.init_array\)/\1\2 : { }\n\1.sinit/g' \
                 -e 's/^\(\s\+\)\(\.fini_array\)/\1\2 : { }\n\1.einit/g' \
                 -e 's/__init_array_start/_sinit/g' -e 
's/__init_array_end/_einit/g' \
diff --git a/boards/sim/sim/sim/scripts/Make.defs 
b/boards/sim/sim/sim/scripts/Make.defs
index c38a6c60ea..bb4f0f0a97 100644
--- a/boards/sim/sim/sim/scripts/Make.defs
+++ b/boards/sim/sim/sim/scripts/Make.defs
@@ -76,6 +76,10 @@ ifeq ($(CONFIG_SIM_GCOV_ALL),y)
   ARCHOPTIMIZATION += -fprofile-generate -ftest-coverage
 endif
 
+ifneq ($(CONFIG_SCHED_GPROF_ALL)$(CONFIG_SIM_GPROF),)
+  ARCHOPTIMIZATION += -pg
+endif
+
 ifeq ($(CONFIG_SIM_ASAN),y)
   ARCHOPTIMIZATION += -fsanitize=address -fsanitize-address-use-after-scope
   ARCHOPTIMIZATION += -fsanitize=pointer-compare -fsanitize=pointer-subtract
diff --git a/include/sys/gmon.h b/include/sys/gmon.h
new file mode 100644
index 0000000000..b98deef88d
--- /dev/null
+++ b/include/sys/gmon.h
@@ -0,0 +1,54 @@
+/****************************************************************************
+ * include/sys/gmon.h
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+#ifndef __INCLUDE_SYS_GMON_H
+#define __INCLUDE_SYS_GMON_H
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/* Start/stop profiling */
+
+void moncontrol(int mode);
+
+/* Set up data structures and start profiling. */
+
+void monstartup(unsigned long lowpc, unsigned long highpc);
+
+/* Clean up profiling and write out gmon.out. */
+
+void _mcleanup(void);
+
+#undef EXTERN
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __INCLUDE_SYS_GMON_H */
diff --git a/libs/libc/machine/arm/CMakeLists.txt 
b/libs/libc/machine/arm/CMakeLists.txt
index 8b16ef6bcf..6dba16de12 100644
--- a/libs/libc/machine/arm/CMakeLists.txt
+++ b/libs/libc/machine/arm/CMakeLists.txt
@@ -62,4 +62,8 @@ if(CONFIG_ARCH_SETJMP_H)
   endif()
 endif()
 
+if(CONFIG_SCHED_GPROF)
+  list(APPEND SRCS gnu/mcount.S)
+endif()
+
 target_sources(c PRIVATE ${SRCS})
diff --git a/libs/libc/machine/arm/Make.defs b/libs/libc/machine/arm/Make.defs
index f8268d09eb..73b41ad4dc 100644
--- a/libs/libc/machine/arm/Make.defs
+++ b/libs/libc/machine/arm/Make.defs
@@ -60,6 +60,10 @@ ASRCS += arch_setjmp.S
 endif
 endif
 
+ifeq ($(CONFIG_SCHED_GPROF),y)
+ASRCS += mcount.S
+endif
+
 ifeq ($(CONFIG_ARCH_TOOLCHAIN_GNU),y)
 DEPPATH += --dep-path machine/arm/gnu
 VPATH += :machine/arm/gnu
diff --git a/libs/libc/machine/arm/gnu/mcount.S 
b/libs/libc/machine/arm/gnu/mcount.S
new file mode 100644
index 0000000000..f77937a89e
--- /dev/null
+++ b/libs/libc/machine/arm/gnu/mcount.S
@@ -0,0 +1,41 @@
+/****************************************************************************
+ * libs/libc/machine/arm/gnu/mcount.S
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+       .globl  __gnu_mcount_nc
+
+       .syntax unified
+       .file   "mcount.S"
+
+       .type   __gnu_mcount_nc, %function
+__gnu_mcount_nc:
+       push    {r0, r1, r2, r3, lr}                    /* Save registers */
+       bic             r1, lr, #1                      /* R1 contains callee 
address, with thumb bit cleared */
+       ldr             r0, [sp, #20]                   /* R0 contains caller 
address */
+       bic             r0, r0, #1                      /* Clear thumb bit */
+       bl              mcount_internal                 /* Jump to internal 
_mcount() implementation */
+       pop             {r0, r1, r2, r3, ip, lr}        /* Restore saved 
registers */
+       bx              ip                              /* Return to callee */
+
+       .size   __gnu_mcount_nc, .-__gnu_mcount_nc
+       .end
diff --git a/sched/Kconfig b/sched/Kconfig
index 02323f936d..9eb2d266fd 100644
--- a/sched/Kconfig
+++ b/sched/Kconfig
@@ -1357,6 +1357,27 @@ config SCHED_GCOV
                "-fprofile-generate -ftest-coverage" compilation parameters
                to the file to be analyzed.
 
+config SCHED_GPROF
+       bool "Enable gprof profiling"
+       default n
+       ---help---
+               Enable gprof profiling support.  This will cause the compiler to
+               generate additional code to support profiling.  This will also
+               cause the linker to include the gmon.out file in the final
+               executable.
+               Add the "-pg" parameter to the Makefile when compiling to obtain
+               the function call graph of the specified module.
+
+config SCHED_GPROF_ALL
+       bool "Enable gprof call graph for all modules"
+       depends on SCHED_GPROF
+       default n
+       ---help---
+               Enable gprof profiling for all code, it will instrument
+               all code, which will cause a large performance penalty for the 
code.
+               You can add the '-pg' parameter to the specified module in the
+               makefile to only analyze the content of the module.
+
 endmenu
 
 menu "Files and I/O"
diff --git a/sched/instrument/CMakeLists.txt b/sched/instrument/CMakeLists.txt
index e7c38b5eff..16db7390a8 100644
--- a/sched/instrument/CMakeLists.txt
+++ b/sched/instrument/CMakeLists.txt
@@ -26,4 +26,8 @@ if(NOT "${CONFIG_SCHED_STACK_RECORD}" STREQUAL "0")
   list(APPEND SRCS stack_record.c)
 endif()
 
+if(CONFIG_SCHED_GPROF)
+  list(APPEND SRCS profile_monitor.c)
+endif()
+
 target_sources(sched PRIVATE ${SRCS})
diff --git a/sched/instrument/Make.defs b/sched/instrument/Make.defs
index 9ff4e9eac1..e2368fcf6a 100644
--- a/sched/instrument/Make.defs
+++ b/sched/instrument/Make.defs
@@ -26,6 +26,10 @@ ifneq ($(CONFIG_SCHED_STACK_RECORD),0)
 CSRCS += stack_record.c
 endif
 
+ifeq ($(CONFIG_SCHED_GPROF),y)
+CSRCS += profile_monitor.c
+endif
+
 # Include instrument build support
 
 DEPPATH += --dep-path instrument
diff --git a/sched/instrument/profile_monitor.c 
b/sched/instrument/profile_monitor.c
new file mode 100644
index 0000000000..2c98c6b81c
--- /dev/null
+++ b/sched/instrument/profile_monitor.c
@@ -0,0 +1,584 @@
+/****************************************************************************
+ * sched/instrument/profile_monitor.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 <debug.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/gmon.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/init.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/spinlock.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define GMONVERSION     0x00051879
+
+/* Histogram counters are unsigned shorts (according to the kernel). */
+
+#define HISTCOUNTER     unsigned short
+
+/* Fraction of text space to allocate for histogram counters here, 1/2 */
+
+#define HISTFRACTION    2
+
+/* Fraction of text space to allocate for from hash buckets.
+ * The value of HASHFRACTION is based on the minimum number of bytes
+ * of separation between two subroutine call points in the object code.
+ * Given MIN_SUBR_SEPARATION bytes of separation the value of
+ * HASHFRACTION is calculated as:
+ *
+ * HASHFRACTION = MIN_SUBR_SEPARATION / (2 * sizeof(short) - 1);
+ *
+ * For example, on the VAX, the shortest two call sequence is:
+ *
+ *      calls     $0,(r0)
+ *      calls     $0,(r0)
+ *
+ * Which is separated by only three bytes, thus HASHFRACTION is
+ * calculated as:
+ *
+ * HASHFRACTION = 3 / (2 * 2 - 1) = 1
+ *
+ * Note that the division above rounds down, thus if MIN_SUBR_FRACTION
+ * is less than three, this algorithm will not work!
+ *
+ * In practice, however, call instructions are rarely at a minimal
+ * distance.  Hence, we will define HASHFRACTION to be 2 across all
+ * architectures.  This saves a reasonable amount of space for
+ * profiling data structures without (in practice) sacrificing
+ * any granularity.
+ */
+
+#define HASHFRACTION    2
+
+/* Percent of text space to allocate for tostructs with a minimum.
+ * This is a heuristic; we will fail with a warning when profiling
+ * programs with a very large number of very small functions, but
+ * that's normally OK.
+ * 2 is probably still a good value for normal programs.
+ * Profiling a test case with 64000 small functions will work if
+ * you raise this value to 3 and link statically (which bloats the
+ * text size, thus raising the number of arcs expected by the heuristic).
+ */
+
+#define ARCDENSITY      3
+
+/* Always allocate at least this many tostructs.  This
+ * hides the inadequacy of the ARCDENSITY heuristic, at least
+ * for small programs.
+ */
+
+#define MINARCS         50
+
+/* The type used to represent indices into gmonparam.tos[]. */
+
+#define ARCINDEX        unsigned long
+
+/* Maximum number of arcs we want to allow.
+ * Used to be max representable value of ARCINDEX minus 2, but now
+ * that ARCINDEX is a long, that's too large; we don't really want
+ * to allow a 48 gigabyte table.
+ */
+
+#define MAXARCS         (1 << 20)
+
+/* General rounding functions. */
+
+#define ROUNDDOWN(x, y) (((x) / (y)) * (y))
+#define ROUNDUP(x, y)   ((((x) + (y) - 1) / (y)) * (y))
+
+/* See profil(2) where this is described (incorrectly) */
+
+#define SCALE_1_TO_1    0x10000
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct tostruct
+{
+  uintptr_t selfpc; /* Callee address/program counter. The caller address
+                     * is in froms[] array which points to tos[] array
+                     */
+  long      count;  /* How many times it has been called */
+  ARCINDEX  link;   /* Link to next entry in hash table. For tos[0] this
+                     * points to the last used entry
+                     */
+};
+
+/* Structure prepended to gmon.out profiling data file. */
+
+struct gmonhdr
+{
+  uintptr_t lpc;      /* Base pc address of sample buffer */
+  uintptr_t hpc;      /* Max pc address of sampled buffer */
+  uint32_t  ncnt;     /* Size of sample buffer (plus this header) */
+  uint32_t  version;  /* Version number */
+  uint32_t  profrate; /* Profiling clock rate */
+  uint32_t  spare[3]; /* Reserved */
+};
+
+/* A raw arc, with pointers to the calling site and
+ * the called site and a count.
+ */
+
+struct rawarc
+{
+  uintptr_t raw_frompc;
+  uintptr_t raw_selfpc;
+  long      raw_count;
+};
+
+/* The profiling data structures are housed in this structure. */
+
+struct gmonparam
+{
+  bool                 running;
+  FAR unsigned short  *kcount;     /* Histogram PC sample array */
+  size_t               kcountsize; /* Size of kcount[] array in bytes */
+  FAR ARCINDEX        *froms;      /* Array of hashed 'from' addresses. The 
16bit
+                                    * value is an index into the tos[] array
+                                    */
+  size_t               fromssize;  /* Size of froms[] array in bytes */
+  FAR struct tostruct *tos;        /* To struct, contains histogram counter */
+  size_t               tossize;    /* Size of tos[] array in bytes */
+  size_t               tolimit;
+  uintptr_t            lowpc;      /* Low program counter of area */
+  uintptr_t            highpc;     /* High program counter */
+  size_t               textsize;   /* Code size */
+  spinlock_t           lock;       /* Lock for this structure */
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static struct gmonparam g_monparam;
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+extern uint8_t _stext[];
+extern uint8_t _etext[];
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+noinstrument_function
+static int write_gmon(FAR struct gmonparam *p, FAR const char *output)
+{
+  struct gmonhdr gmonhdr;
+  struct rawarc rawarc;
+  struct file file;
+  uintptr_t frompc;
+  ARCINDEX toindex;
+  size_t fromindex;
+  size_t endfrom;
+  int ret;
+
+  ret = file_open(&file, output, O_CREAT | O_TRUNC | O_WRONLY, 0666);
+  if (ret < 0)
+    {
+      serr("cannot open %s\n", output);
+      return ret;
+    }
+
+  gmonhdr.lpc = p->lowpc;
+  gmonhdr.hpc = p->highpc;
+  gmonhdr.ncnt = sizeof(gmonhdr) + p->kcountsize;
+  gmonhdr.version = GMONVERSION;
+  gmonhdr.profrate = CONFIG_SCHED_PROFILE_TICKSPERSEC;
+
+  ret = file_write(&file, &gmonhdr, sizeof(gmonhdr));
+  if (ret != sizeof(gmonhdr))
+    {
+      serr("write gmonhdr failed\n");
+      goto out;
+    }
+
+  ret = file_write(&file, p->kcount, p->kcountsize);
+  if (ret != p->kcountsize)
+    {
+      serr("write kcount failed\n");
+      goto out;
+    }
+
+  endfrom = p->fromssize / sizeof(*p->froms);
+  for (fromindex = 0; fromindex < endfrom; fromindex++)
+    {
+      if (p->froms[fromindex] == 0)
+        {
+          continue;
+        }
+
+      frompc = p->lowpc;
+      frompc += fromindex * HASHFRACTION * sizeof(*p->froms);
+
+      for (toindex = p->froms[fromindex]; toindex != 0;
+           toindex = p->tos[toindex].link)
+        {
+          rawarc.raw_frompc = frompc;
+          rawarc.raw_selfpc = p->tos[toindex].selfpc;
+          rawarc.raw_count = p->tos[toindex].count;
+          ret = file_write(&file, &rawarc, sizeof(rawarc));
+          if (ret != sizeof(rawarc))
+            {
+              serr("write rawarc failed\n");
+              goto out;
+            }
+        }
+    }
+
+out:
+  file_close(&file);
+  return ret < 0 ? ret : 0;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/* Control profiling
+ *  profiling is what mcount checks to see if
+ *  all the data structures are ready.
+ */
+
+noinstrument_function
+void moncontrol(int mode)
+{
+  FAR struct gmonparam *p = &g_monparam;
+  irqstate_t flags;
+
+  if (p->running == !!mode)
+    {
+      return;
+    }
+
+  if (mode)
+    {
+      uintptr_t lowpc = ROUNDDOWN((uintptr_t)&_stext,
+                                   HISTFRACTION * sizeof(HISTCOUNTER));
+      uintptr_t highpc = ROUNDUP((uintptr_t)&_etext,
+                                 HISTFRACTION * sizeof(HISTCOUNTER));
+      size_t textsize = highpc - lowpc;
+      size_t kcountsize = ROUNDUP(textsize / HISTFRACTION,
+                                  sizeof(*p->kcount));
+      int scale = kcountsize >= textsize ? SCALE_1_TO_1 :
+                  (float)kcountsize / textsize * SCALE_1_TO_1;
+      FAR unsigned short *kcount = kmm_zalloc(kcountsize);
+      if (kcount == NULL)
+        {
+          serr("out of memory\n");
+          return;
+        }
+
+      flags = spin_lock_irqsave(&p->lock);
+      if (p->kcount)
+        {
+          spin_unlock_irqrestore(&p->lock, flags);
+          kmm_free(kcount);
+          return;
+        }
+
+      p->running = true;
+      p->lowpc = lowpc;
+      p->highpc = highpc;
+      p->textsize = textsize;
+      p->kcount = kcount;
+      p->kcountsize = kcountsize;
+      spin_unlock_irqrestore(&p->lock, flags);
+
+      profil(kcount, kcountsize, lowpc, scale);
+    }
+  else
+    {
+      bool running;
+
+      flags = spin_lock_irqsave(&p->lock);
+      running = p->running;
+      p->running = false;
+      spin_unlock_irqrestore(&p->lock, flags);
+
+      if (running)
+        {
+          profil(NULL, 0, 0, 0);
+        }
+    }
+}
+
+noinstrument_function
+void monstartup(unsigned long lowpc, unsigned long highpc)
+{
+  FAR struct gmonparam *p = &g_monparam;
+  irqstate_t flags;
+  FAR char *buffer;
+  size_t textsize;
+  size_t fromssize;
+  size_t tolimit;
+  size_t tossize;
+
+  /* If we are incorrectly called twice in a row (without an
+   * intervening call to _mcleanup), ignore the second call to
+   * prevent leaking memory.
+   */
+
+  if (p->tos != NULL)
+    {
+      return;
+    }
+
+  /* Return if the allocation doesn't allow in the current context */
+
+  if (!OSINIT_OS_READY() || up_interrupt_context())
+    {
+      return;
+    }
+
+  /* Round lowpc and highpc to multiples of the density we're using
+   * so the rest of the scaling (here and in gprof) stays in ints.
+   */
+
+  lowpc = ROUNDDOWN(lowpc, HISTFRACTION * sizeof(HISTCOUNTER));
+  highpc = ROUNDUP(highpc, HISTFRACTION * sizeof(HISTCOUNTER));
+  textsize = highpc - lowpc;
+  fromssize = ROUNDUP(textsize / HASHFRACTION, sizeof(*p->froms));
+  tolimit = textsize * ARCDENSITY / 100;
+
+  if (tolimit < MINARCS)
+    {
+      tolimit = MINARCS;
+    }
+  else if (tolimit > MAXARCS)
+    {
+      tolimit = MAXARCS;
+    }
+
+  tossize = tolimit * sizeof(struct tostruct);
+
+  buffer = kmm_zalloc(fromssize + tossize);
+  if (buffer == NULL)
+    {
+      serr("out of memory\n");
+      return;
+    }
+
+  flags = spin_lock_irqsave(&p->lock);
+  if (p->tos != NULL)
+    {
+      spin_unlock_irqrestore(&p->lock, flags);
+      kmm_free(buffer);
+      return;
+    }
+
+  p->lowpc = lowpc;
+  p->highpc = highpc;
+  p->textsize = textsize;
+  p->fromssize = fromssize;
+  p->tolimit = tolimit;
+  p->tossize = tossize;
+
+  p->tos = (FAR struct tostruct *)buffer;
+  buffer += p->tossize;
+  p->froms = (FAR ARCINDEX *)buffer;
+  spin_unlock_irqrestore(&p->lock, flags);
+
+  moncontrol(1);
+}
+
+noinstrument_function
+void _mcleanup(void)
+{
+  FAR struct gmonparam *p = &g_monparam;
+  FAR const char *prefix = NULL;
+
+#ifndef CONFIG_DISABLE_ENVIRON
+  prefix = getenv("GMON_OUT_PREFIX");
+#endif
+  if (prefix == NULL)
+    {
+      prefix = "gmon.out";
+    }
+
+  moncontrol(0);
+  if (p->kcount)
+    {
+      write_gmon(p, prefix);
+    }
+
+  kmm_free(p->tos);
+  kmm_free(p->kcount);
+
+  /* Reset buffer to initial state for safety */
+
+  memset(p, 0, sizeof(*p));
+}
+
+/* mcount_internal is called on entry to each function compiled with
+ * the profiling switch set by an assembly stub in:
+ * libs/libc/machine/xxx/mcount.S
+ * which updates data structures that represent traversals of the
+ * program's call graph edges.  frompc and selfpc are the return
+ * address and function address that represents the given call graph edge.
+ */
+
+noinstrument_function
+void mcount_internal(uintptr_t frompc, uintptr_t selfpc)
+{
+  FAR struct gmonparam *p = &g_monparam;
+  FAR struct tostruct *prevtop;
+  FAR struct tostruct *top;
+  FAR ARCINDEX *frompcindex;
+  ARCINDEX toindex;
+  irqstate_t flags;
+
+  /* Check that we are profiling */
+
+  if (!p->running)
+    {
+      return;
+    }
+
+  /* Initialize the internal structure if not yet */
+
+  monstartup((uintptr_t)&_stext, (uintptr_t)&_etext);
+
+  flags = spin_lock_irqsave(&p->lock);
+
+  /* Try next time if fail to initialize for some reason */
+
+  if (p->tos == NULL)
+    {
+      goto done;
+    }
+
+  /* Check that frompc is a reasonable pc value.
+   * For example: signal catchers get called from the stack,
+   * not from text space.  Too bad.
+   */
+
+  frompc -= p->lowpc;
+  if (frompc > p->textsize)
+    {
+      goto done;
+    }
+
+  frompcindex = &p->froms[frompc / (HASHFRACTION * sizeof(*p->froms))];
+  toindex = *frompcindex; /* Get froms[] value */
+  if (toindex == 0)
+    {
+      /* First time traversing this arc */
+
+      toindex = ++p->tos[0].link; /* The link of tos[0] points to the last
+                                   * used record in the array
+                                   */
+      if (toindex >= p->tolimit)
+        {
+          /* More tos[] entries than we can handle! */
+
+          goto done;
+        }
+
+      /* Store new 'to' value into froms[] */
+
+      *frompcindex = toindex;
+      top = &p->tos[toindex];
+      top->selfpc = selfpc;
+      top->count = 1;
+      top->link = 0;
+      goto done;
+    }
+
+  top = &p->tos[toindex];
+  if (top->selfpc == selfpc)
+    {
+      /* Arc at front of chain; usual case. */
+
+      top->count++;
+      goto done;
+    }
+
+  /* Have to go looking down chain for it.
+   * Top points to what we are looking at,
+   * prevtop points to previous top.
+   * We know it is not at the head of the chain.
+   */
+
+  for (; ; )
+    {
+      if (top->link == 0)
+        {
+          /* Top is end of the chain and none of the chain
+           * had top->selfpc == selfpc.
+           * So we allocate a new tostruct
+           * and link it to the head of the chain.
+           */
+
+          toindex = ++p->tos[0].link;
+          if (toindex >= p->tolimit)
+            {
+              goto done;
+            }
+
+          top = &p->tos[toindex];
+          top->selfpc = selfpc;
+          top->count = 1;
+          top->link = *frompcindex;
+          *frompcindex = toindex;
+          goto done;
+        }
+
+      /* Otherwise, check the next arc on the chain. */
+
+      prevtop = top;
+      top = &p->tos[top->link];
+      if (top->selfpc == selfpc)
+        {
+          /* There it is.
+           * Increment its count
+           * move it to the head of the chain.
+           */
+
+          top->count++;
+          toindex = prevtop->link;
+          prevtop->link = top->link;
+          top->link = *frompcindex;
+          *frompcindex = toindex;
+          goto done;
+        }
+    }
+
+done:
+  spin_unlock_irqrestore(&p->lock, flags);
+}
diff --git a/sched/sched/sched_profil.c b/sched/sched/sched_profil.c
index 7afe36f6c2..46e2b1fef5 100644
--- a/sched/sched/sched_profil.c
+++ b/sched/sched/sched_profil.c
@@ -130,7 +130,7 @@ int profil(FAR unsigned short *buf, size_t bufsiz,
     }
 
   memset(buf, 0, bufsiz);
-  highpc = (uintmax_t)bufsiz * 32768 / scale;
+  highpc = (uintmax_t)bufsiz * 65536 / scale;
 
   flags = spin_lock_irqsave(&prof->lock);
   prof->counter = buf;

Reply via email to