libc: add instrument api support

Add registration function instrumentation API,
which can achieve instrumentation of entering and
exiting functions through the compiler's functionality.

We can use CONFIG_ARCH_INSTRUMENT_ALL to add instrumentation for all
source, or add '-finstrument-functions' to CFLAGS for Part of the
source.

Notice:
1. use CONFIG_ARCH_INSTRUMENT_ALL must mark _start or entry noinstrument_function,
   becuase bss not set.
2. Make sure your callbacks are not instrumented recursively.

use instrument_register to register entry function and exit function.
They will be called by the instrumented function

Signed-off-by: anjiahao <anjiahao@xiaomi.com>
This commit is contained in:
anjiahao 2023-10-08 18:20:43 +08:00 committed by Xiang Xiao
parent 404616d621
commit 7dfbd14eba
37 changed files with 403 additions and 43 deletions

View File

@ -335,6 +335,13 @@ config ARCH_COVERAGE_ALL
will get image size increased and performance decreased
significantly.
config ARCH_INSTRUMENT_ALL
bool "Instrument All"
default n
---help---
Add instrument to all source files. we can use instrument_register
to register the instrument function.
comment "Architecture Options"
config ARCH_NOINTC

View File

@ -48,6 +48,7 @@
#include <stdint.h>
#include <nuttx/instrument.h>
#include "arm_internal.h"
#include "nvic.h"
@ -55,10 +56,19 @@
* Private Functions
****************************************************************************/
void __cyg_profile_func_enter(void *func, void *caller) naked_function;
void __cyg_profile_func_exit(void *func, void *caller) naked_function;
static void stack_check_enter(void *func, void *caller, void *arg)
naked_function;
void __stack_overflow_trap(void) naked_function;
/****************************************************************************
* Private Data
****************************************************************************/
static struct instrument_s g_stack_check =
{
.enter = stack_check_enter,
};
/****************************************************************************
* Name: __stack_overflow_trap
****************************************************************************/
@ -87,10 +97,10 @@ void __stack_overflow_trap(void)
}
/****************************************************************************
* Name: __cyg_profile_func_enter
* Name: stack_check_enter
****************************************************************************/
void __cyg_profile_func_enter(void *func, void *caller)
static void stack_check_enter(void *func, void *caller, void *arg)
{
asm volatile (
" mrs r2, ipsr \n" /* Check whether we are in interrupt mode */
@ -112,12 +122,8 @@ void __cyg_profile_func_enter(void *func, void *caller)
);
}
/****************************************************************************
* Name: __cyg_profile_func_exit
****************************************************************************/
void __cyg_profile_func_exit(void *func, void *caller)
void noinstrument_function arm_stack_check_init(void)
{
asm volatile("bx lr");
instrument_register(&g_stack_check);
}
#endif

View File

@ -48,6 +48,7 @@
#include <stdint.h>
#include <nuttx/instrument.h>
#include "arm_internal.h"
#include "nvic.h"
@ -55,9 +56,18 @@
* Private Functions
****************************************************************************/
void __cyg_profile_func_enter(void *func, void *caller) naked_function;
void __cyg_profile_func_exit(void *func, void *caller) naked_function;
void __stack_overflow_trap(void) naked_function;
static void stack_check_enter(void *func, void *caller, void *arg)
naked_function;
void __stack_overflow_trap(void) naked_function;
/****************************************************************************
* Private Data
****************************************************************************/
static struct instrument_s g_stack_check =
{
.enter = stack_check_enter,
};
/****************************************************************************
* Name: __stack_overflow_trap
@ -87,10 +97,10 @@ void __stack_overflow_trap(void)
}
/****************************************************************************
* Name: __cyg_profile_func_enter
* Name: stack_check_enter
****************************************************************************/
void __cyg_profile_func_enter(void *func, void *caller)
static void stack_check_enter(void *func, void *caller, void *arg)
{
asm volatile (
" mrs r2, ipsr \n" /* Check whether we are in interrupt mode */
@ -113,11 +123,11 @@ void __cyg_profile_func_enter(void *func, void *caller)
}
/****************************************************************************
* Name: __cyg_profile_func_exit
* Name: arm_stack_check_init
****************************************************************************/
void __cyg_profile_func_exit(void *func, void *caller)
void noinstrument_function arm_stack_check_init(void)
{
asm volatile("bx lr");
instrument_register(&g_stack_check);
}
#endif

View File

@ -65,4 +65,8 @@ else()
list(APPEND PLATFORM_FLAGS -mfloat-abi=soft)
endif()
if(CONFIG_ARCH_INSTRUMENT_ALL AND NOT CONFIG_ARMV8M_STACKCHECK)
list(APPEND PLATFORM_FLAGS -finstrument-functions)
endif()
add_compile_options(${PLATFORM_FLAGS})

View File

@ -90,4 +90,8 @@ if(CONFIG_ARMV7M_STACKCHECK)
list(APPEND PLATFORM_FLAGS -finstrument-functions -ffixed-r10)
endif()
if(CONFIG_ARCH_INSTRUMENT_ALL AND NOT CONFIG_ARMV7M_STACKCHECK)
list(APPEND PLATFORM_FLAGS -finstrument-functions)
endif()
add_compile_options(${PLATFORM_FLAGS})

View File

@ -65,6 +65,10 @@ if(CONFIG_ARMV8M_STACKCHECK)
list(APPEND PLATFORM_FLAGS -finstrument-functions -ffixed-r10)
endif()
if(CONFIG_ARCH_INSTRUMENT_ALL AND NOT CONFIG_ARMV8M_STACKCHECK)
list(APPEND PLATFORM_FLAGS -finstrument-functions)
endif()
if(CONFIG_ARMV8M_CMSE)
list(APPEND PLATFORM_FLAGS -mcmse)
endif()

View File

@ -86,6 +86,12 @@ ifeq ($(CONFIG_MM_KASAN_DISABLE_WRITES_CHECK),y)
ARCHOPTIMIZATION += --param asan-instrument-writes=0
endif
# Instrumentation options
ifeq ($(CONFIG_ARCH_INSTRUMENT_ALL),y)
ARCHOPTIMIZATION += -finstrument-functions
endif
ifeq ($(CONFIG_UNWINDER_ARM),y)
ARCHOPTIMIZATION += -funwind-tables -fasynchronous-unwind-tables
endif

View File

@ -531,6 +531,10 @@ int arm_gen_nonsecurefault(int irq, uint32_t *regs);
# define arm_gen_nonsecurefault(i, r) (0)
#endif
#if defined(CONFIG_ARMV7M_STACKCHECK) || defined(CONFIG_ARMV8M_STACKCHECK)
void arm_stack_check_init(void) noinstrument_function;
#endif
#undef EXTERN
#ifdef __cplusplus
}

View File

@ -152,6 +152,10 @@ void __start(void)
showprogress('C');
#ifdef CONFIG_ARMV7M_STACKCHECK
arm_stack_check_init();
#endif
#ifdef CONFIG_ARMV7M_ITMSYSLOG
/* Perform ARMv7-M ITM SYSLOG initialization */

View File

@ -237,6 +237,10 @@ void __start(void)
*dest++ = *src++;
}
#ifdef CONFIG_ARMV7M_STACKCHECK
arm_stack_check_init();
#endif
/* Configure the UART so that we can get debug output as soon as possible */
gd32_clockconfig();

View File

@ -210,6 +210,10 @@ void __start(void)
}
#endif
#ifdef CONFIG_ARMV7M_STACKCHECK
arm_stack_check_init();
#endif
/* Configure the UART so that we can get debug output as soon as possible */
imxrt_clockconfig();

View File

@ -153,6 +153,10 @@ void __start(void)
}
#endif
#ifdef CONFIG_ARMV7M_STACKCHECK
arm_stack_check_init();
#endif
/* Perform clock and Kinetis module initialization (This depends on
* RAM functions having been copied to RAM).
*/

View File

@ -153,6 +153,10 @@ void __start(void)
showprogress('C');
#ifdef CONFIG_ARMV7M_STACKCHECK
arm_stack_check_init();
#endif
/* Perform early serial initialization */
#ifdef USE_EARLYSERIALINIT

View File

@ -188,6 +188,10 @@ void __start(void)
showprogress('C');
#ifdef CONFIG_ARMV7M_STACKCHECK
arm_stack_check_init();
#endif
#if defined(CONFIG_ARCH_CHIP_NRF52832)
/* Initialize the errdata work-around */

View File

@ -236,6 +236,10 @@ void __start(void)
showprogress('C');
#ifdef CONFIG_ARMV8M_STACKCHECK
arm_stack_check_init();
#endif
#ifdef CONFIG_ARCH_HAVE_FPU
/* Initialize the FPU (if available) */

View File

@ -77,6 +77,11 @@ int promisc_recv_lens_func(void *padapter, uint8_t *payload, uint8_t plen)
void app_start(void)
{
__asm volatile("MSR msplim, %0" : : "r"(0));
#ifdef CONFIG_ARMV8M_STACKCHECK
arm_stack_check_init();
#endif
arm_earlyserialinit();
#ifdef CONFIG_MBEDTLS240_AMEBAZ_HARDWARE_CRYPTO
extern int mbedtls_platform_set_calloc_free(

View File

@ -164,6 +164,10 @@ void __start(void)
}
#endif
#ifdef CONFIG_ARMV7M_STACKCHECK
arm_stack_check_init();
#endif
/* Configure the UART so that we can get debug output as soon as possible */
sam_clockconfig();

View File

@ -167,6 +167,10 @@ void __start(void)
}
#endif
#ifdef CONFIG_ARMV7M_STACKCHECK
arm_stack_check_init();
#endif
/* Initialize clocking and the FPU. Configure the console UART so that
* we can get debug output as soon as possible.
*/

View File

@ -199,6 +199,10 @@ void __start(void)
}
#endif
#ifdef CONFIG_ARMV7M_STACKCHECK
arm_stack_check_init();
#endif
/* Configure the UART so that we can get debug output as soon as possible */
sam_clockconfig();

View File

@ -158,6 +158,10 @@ void __start(void)
showprogress('C');
#ifdef CONFIG_ARMV7M_STACKCHECK
arm_stack_check_init();
#endif
#ifdef CONFIG_ARCH_PERF_EVENTS
up_perf_init((void *)STM32_SYSCLK_FREQUENCY);
#endif

View File

@ -223,6 +223,10 @@ void __start(void)
}
#endif
#ifdef CONFIG_ARMV7M_STACKCHECK
arm_stack_check_init();
#endif
/* Configure the UART so that we can get debug output as soon as possible */
stm32_clockconfig();

View File

@ -239,6 +239,10 @@ void __start(void)
}
#endif
#ifdef CONFIG_ARMV7M_STACKCHECK
arm_stack_check_init();
#endif
/* Configure the UART so that we can get debug output as soon as possible */
stm32_clockconfig();

View File

@ -179,6 +179,10 @@ void __start(void)
showprogress('C');
#ifdef CONFIG_ARMV7M_STACKCHECK
arm_stack_check_init();
#endif
#ifdef CONFIG_ARCH_PERF_EVENTS
up_perf_init((void *)STM32_SYSCLK_FREQUENCY);
#endif

View File

@ -181,6 +181,10 @@ void __start(void)
showprogress('C');
#ifdef CONFIG_ARMV8M_STACKCHECK
arm_stack_check_init();
#endif
#ifdef CONFIG_ARCH_PERF_EVENTS
up_perf_init((void *)STM32_SYSCLK_FREQUENCY);
#endif

View File

@ -181,6 +181,10 @@ void __start(void)
showprogress('C');
#ifdef CONFIG_ARMV8M_STACKCHECK
arm_stack_check_init();
#endif
#ifdef CONFIG_ARCH_PERF_EVENTS
up_perf_init((void *)STM32_SYSCLK_FREQUENCY);
#endif

View File

@ -199,6 +199,10 @@ void __start(void)
showprogress('C');
#ifdef CONFIG_ARMV7M_STACKCHECK
arm_stack_check_init();
#endif
#ifdef CONFIG_ARCH_PERF_EVENTS
up_perf_init((void *)STM32_SYSCLK_FREQUENCY);
#endif

View File

@ -220,6 +220,10 @@ void __start(void)
}
#endif
#ifdef CONFIG_ARMV7M_STACKCHECK
arm_stack_check_init();
#endif
/* Set FLASH wait states prior to the configuration of clocking */
xmc4_flash_waitstates();

View File

@ -84,6 +84,12 @@ ifeq ($(CONFIG_MM_KASAN_ALL),y)
ARCHOPTIMIZATION += -fsanitize=kernel-address
endif
# Instrumentation options
ifeq ($(CONFIG_ARCH_INSTRUMENT_ALL),y)
ARCHOPTIMIZATION += -finstrument-functions
endif
ifeq ($(CONFIG_ARCH_FPU),y)
ARCHCXXFLAGS += -D_LDBL_EQ_DBL
ARCHCFLAGS += -D_LDBL_EQ_DBL

View File

@ -251,6 +251,12 @@ ifeq ($(CONFIG_MM_UBSAN_TRAP_ON_ERROR),y)
ARCHOPTIMIZATION += -fsanitize-undefined-trap-on-error
endif
# Instrumentation options
ifeq ($(CONFIG_ARCH_INSTRUMENT_ALL),y)
ARCHOPTIMIZATION += -finstrument-functions
endif
# Default toolchain
CC = $(CROSSDEV)gcc

View File

@ -95,6 +95,12 @@ ifeq ($(CONFIG_ARCH_COVERAGE_ALL),y)
ARCHOPTIMIZATION += -fprofile-generate -ftest-coverage
endif
# Instrumentation options
ifeq ($(CONFIG_ARCH_INSTRUMENT_ALL),y)
ARCHOPTIMIZATION += -finstrument-functions
endif
ARCHCFLAGS += -fno-common
ARCHCXXFLAGS += -fno-common -nostdinc++

View File

@ -99,6 +99,12 @@ ifeq ($(CONFIG_ARCH_COVERAGE_ALL),y)
ARCHOPTIMIZATION += -fprofile-generate -ftest-coverage
endif
# Instrumentation options
ifeq ($(CONFIG_ARCH_INSTRUMENT_ALL),y)
ARCHOPTIMIZATION += -finstrument-functions
endif
ARCHCFLAGS += -fno-common
ARCHCXXFLAGS += -fno-common -nostdinc++

View File

@ -95,6 +95,10 @@ else
endif
endif
ifeq ($(CONFIG_ARCH_INSTRUMENT_ALL),y)
ARCHOPTIMIZATION += -finstrument-functions
endif
# Add -fno-common because macOS "ld -r" doesn't seem to pick objects
# for common symbols.
ARCHCFLAGS += -fno-common

View File

@ -40,9 +40,14 @@
#include <nuttx/note/notelog_driver.h>
#include <nuttx/spinlock.h>
#include <nuttx/sched_note.h>
#include <nuttx/instrument.h>
#include "sched/sched.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#if defined(CONFIG_DRIVERS_NOTERAM) + defined(CONFIG_DRIVERS_NOTELOG) + \
defined(CONFIG_DRIVERS_NOTESNAP) + defined(CONFIG_DRIVERS_NOTERTT) + \
defined(CONFIG_SEGGER_SYSVIEW) > CONFIG_DRIVERS_NOTE_MAX
@ -150,6 +155,18 @@ struct note_taskname_s
* Private Data
****************************************************************************/
#ifdef CONFIG_SCHED_INSTRUMENTATION_FUNCTION
static void note_driver_instrument_enter(FAR void *this_fn,
FAR void *call_site, FAR void *arg) noinstrument_function;
static void note_driver_instrument_leave(FAR void *this_fn,
FAR void *call_site, FAR void *arg) noinstrument_function;
static struct instrument_s g_note_instrument =
{
.entry = note_driver_instrument_enter,
.exit = note_driver_instrument_leave,
};
#endif
#ifdef CONFIG_SCHED_INSTRUMENTATION_FILTER
static struct note_filter_s g_note_filter =
{
@ -1924,6 +1941,22 @@ FAR const char *note_get_taskname(pid_t pid)
#endif
#ifdef CONFIG_SCHED_INSTRUMENTATION_FUNCTION
static void note_driver_instrument_enter(FAR void *this_fn,
FAR void *call_site,
FAR void *arg)
{
sched_note_string_ip(NOTE_TAG_ALWAYS, (uintptr_t)this_fn, "B");
}
static void note_driver_instrument_leave(FAR void *this_fn,
FAR void *call_site,
FAR void *arg)
{
sched_note_string_ip(NOTE_TAG_ALWAYS, (uintptr_t)this_fn, "E");
}
#endif
/****************************************************************************
* Name: note_driver_register
****************************************************************************/
@ -1931,8 +1964,17 @@ FAR const char *note_get_taskname(pid_t pid)
int note_driver_register(FAR struct note_driver_s *driver)
{
int i;
DEBUGASSERT(driver);
#ifdef CONFIG_SCHED_INSTRUMENTATION_FUNCTION
static bool initialized;
if (!initialized)
{
instrument_register(g_note_instrument)
initialized = true;
}
#endif
DEBUGASSERT(driver);
for (i = 0; i < CONFIG_DRIVERS_NOTE_MAX; i++)
{
if (g_note_drivers[i] == NULL)
@ -1945,25 +1987,3 @@ int note_driver_register(FAR struct note_driver_s *driver)
return -ENOMEM;
}
#ifdef CONFIG_SCHED_INSTRUMENTATION_FUNCTION
/****************************************************************************
* Name: __cyg_profile_func_enter
****************************************************************************/
void noinstrument_function
__cyg_profile_func_enter(void *this_fn, void *call_site)
{
sched_note_string_ip(NOTE_TAG_ALWAYS, (uintptr_t)this_fn, "B");
}
/****************************************************************************
* Name: __cyg_profile_func_exit
****************************************************************************/
void noinstrument_function
__cyg_profile_func_exit(void *this_fn, void *call_site)
{
sched_note_string_ip(NOTE_TAG_ALWAYS, (uintptr_t)this_fn, "E");
}
#endif

View File

@ -0,0 +1,68 @@
/****************************************************************************
* include/nuttx/instrument.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_NUTTX_INSTRUMENT_H
#define __INCLUDE_NUTTX_INSTRUMENT_H
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/compiler.h>
#include <nuttx/queue.h>
/****************************************************************************
* Public Types
****************************************************************************/
typedef CODE void (instrument_fun_t)(FAR void *this_fn,
FAR void *call_site,
FAR void *arg);
struct instrument_s
{
sq_entry_t entry;
FAR instrument_fun_t *enter;
FAR instrument_fun_t *leave;
FAR void *arg;
};
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
/****************************************************************************
* Name: instrument_register
*
* Description: register instrument, it will be called
* when function enter or exit.
*
* Input Parameters:
* entry - instrument entry structure.
* Notice:
* use CONFIG_ARCH_INSTRUMENT_ALL must mark _start or entry
* noinstrument_function, becuase bss not set.
* Make sure your callbacks are not instrumented recursively.
*
****************************************************************************/
void instrument_register(FAR struct instrument_s *entry);
#endif /* __INCLUDE_NUTTX_INSTRUMENT_H */

View File

@ -79,7 +79,8 @@ list(
lib_glob.c
lib_execinfo.c
lib_ftok.c
lib_err.c)
lib_err.c
lib_instrument.c)
# Keyboard driver encoder/decoder

View File

@ -40,7 +40,7 @@ endif
CSRCS += lib_dumpbuffer.c lib_dumpvbuffer.c lib_fnmatch.c lib_debug.c
CSRCS += lib_crc64.c lib_crc32.c lib_crc16.c lib_crc16ccitt.c lib_crc8.c
CSRCS += lib_crc8ccitt.c lib_crc8table.c lib_glob.c lib_execinfo.c
CSRCS += lib_ftok.c lib_err.c
CSRCS += lib_ftok.c lib_err.c lib_instrument.c
# Keyboard driver encoder/decoder

View File

@ -0,0 +1,121 @@
/****************************************************************************
* libs/libc/misc/lib_instrument.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 <nuttx/instrument.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Avoid instrument bootstrap */
#define MAIGC_NUMBMER 0x5a5a5a5a
/****************************************************************************
* Private Data
****************************************************************************/
/* Use static to avoid instrument bootstrap */
static volatile uint32_t g_magic;
static sq_queue_t g_instrument_queue;
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: __cyg_profile_func_enter
****************************************************************************/
void noinstrument_function
__cyg_profile_func_enter(FAR void *this_fn, FAR void *call_site)
{
FAR struct instrument_s *instrument;
FAR sq_entry_t *entry;
if (g_magic != MAIGC_NUMBMER)
{
return;
}
sq_for_every(&g_instrument_queue, entry)
{
instrument = (FAR struct instrument_s *)entry;
if (instrument->enter)
{
instrument->enter(this_fn, call_site, instrument->arg);
}
}
}
/****************************************************************************
* Name: __cyg_profile_func_exit
****************************************************************************/
void noinstrument_function
__cyg_profile_func_exit(FAR void *this_fn, FAR void *call_site)
{
FAR struct instrument_s *instrument;
FAR sq_entry_t *entry;
if (g_magic != MAIGC_NUMBMER)
{
return;
}
sq_for_every(&g_instrument_queue, entry)
{
instrument = (FAR struct instrument_s *)entry;
if (instrument->leave)
{
instrument->leave(this_fn, call_site, instrument->arg);
}
}
}
/****************************************************************************
* Name: instrument_register
*
* Description: register instrument, it will be called
* when function enter or exit.
*
* Input Parameters:
* entry - instrument entry structure.
* Notice:
* use CONFIG_ARCH_INSTRUMENT_ALL must mark _start or entry
* noinstrument_function, becuase bss not set.
* Make sure your callbacks are not instrumented recursively.
*
****************************************************************************/
void noinstrument_function
instrument_register(FAR struct instrument_s *entry)
{
if (entry != NULL)
{
sq_addlast((FAR sq_entry_t *)entry, &g_instrument_queue);
g_magic = MAIGC_NUMBMER;
}
}