From 4944bfd56cd7f00b6568e945a7bf3ffab126be33 Mon Sep 17 00:00:00 2001 From: Xiang Xiao Date: Tue, 12 Mar 2024 01:48:22 +0800 Subject: [PATCH] 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 --- 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(-) create mode 100644 sched/sched/sched_profil.c 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 +#include + +#include +#include +#include + +/**************************************************************************** + * 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; +}