The existing stack_analyze APIs had some problems: 1. Not properly namespaced 2. Accepted the stack object as a parameter, yet the stack object does not contain the necessary information to get the associated buffer region, the thread object is needed for this 3. Caused a crash on certain platforms that do not allow inspection of unused stack space for the currently running thread 4. No user mode access 5. Separately passed in thread name We deprecate these functions and add a new API k_thread_stack_space_get() which addresses all of these issues. A helper API log_stack_usage() also added which resembles STACK_ANALYZE() in functionality. Fixes: #17852 Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
115 lines
2.7 KiB
C
115 lines
2.7 KiB
C
/**
|
|
* @file debug/stack.h
|
|
* Stack usage analysis helpers
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 2015 Intel Corporation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#ifndef ZEPHYR_INCLUDE_DEBUG_STACK_H_
|
|
#define ZEPHYR_INCLUDE_DEBUG_STACK_H_
|
|
|
|
#include <logging/log.h>
|
|
#include <stdbool.h>
|
|
|
|
static inline size_t stack_unused_space_get(const char *stack, size_t size)
|
|
{
|
|
size_t unused = 0;
|
|
|
|
if (IS_ENABLED(CONFIG_INIT_STACKS)) {
|
|
const unsigned char *checked_stack =
|
|
(const unsigned char *)stack;
|
|
|
|
if (IS_ENABLED(CONFIG_STACK_SENTINEL)) {
|
|
/* First 4 bytes of the stack buffer reserved for the
|
|
* sentinel value, it won't be 0xAAAAAAAA for thread
|
|
* stacks.
|
|
*/
|
|
checked_stack += 4;
|
|
}
|
|
|
|
/* TODO Currently all supported platforms have stack growth down
|
|
* and there is no Kconfig option to configure it so this always
|
|
* build "else" branch.
|
|
* When support for platform with stack direction up
|
|
* (or configurable direction) is added this check should be
|
|
* confirmed that correct Kconfig option is used.
|
|
*/
|
|
if (IS_ENABLED(CONFIG_STACK_GROWS_UP)) {
|
|
for (size_t i = size; i > 0; i--) {
|
|
if (checked_stack[i - 1] == 0xaaU) {
|
|
unused++;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
for (size_t i = 0; i < size; i++) {
|
|
if (checked_stack[i] == 0xaaU) {
|
|
unused++;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return unused;
|
|
}
|
|
|
|
__deprecated
|
|
static inline void stack_analyze(const char *name, const char *stack,
|
|
unsigned int size)
|
|
{
|
|
if (IS_ENABLED(CONFIG_INIT_STACKS)) {
|
|
LOG_MODULE_DECLARE(os, CONFIG_KERNEL_LOG_LEVEL);
|
|
|
|
unsigned int unused = stack_unused_space_get(stack, size);
|
|
unsigned int pcnt = ((size - unused) * 100U) / size;
|
|
|
|
LOG_INF("%s :\tunused %u\tusage %u / %u (%u %%)",
|
|
name, unused, size - unused, size, pcnt);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Analyze stacks.
|
|
*
|
|
* Use this macro to get information about stack usage.
|
|
*
|
|
* @param name Name of the stack
|
|
* @param sym The symbol of the stack
|
|
*/
|
|
#define STACK_ANALYZE(name, sym) DEPRECATED_MACRO \
|
|
do { \
|
|
stack_analyze(name, \
|
|
Z_THREAD_STACK_BUFFER(sym), \
|
|
K_THREAD_STACK_SIZEOF(sym)); \
|
|
} while (false)
|
|
|
|
static inline void log_stack_usage(const struct k_thread *thread)
|
|
{
|
|
#if defined(CONFIG_INIT_STACKS) && defined(CONFIG_THREAD_STACK_INFO)
|
|
size_t unused, size = thread->stack_info.size;
|
|
|
|
LOG_MODULE_DECLARE(os, CONFIG_KERNEL_LOG_LEVEL);
|
|
|
|
if (k_thread_stack_space_get(thread, &unused) == 0) {
|
|
unsigned int pcnt = ((size - unused) * 100U) / size;
|
|
const char *tname;
|
|
|
|
tname = k_thread_name_get((k_tid_t)thread);
|
|
if (tname == NULL) {
|
|
tname = "unknown";
|
|
}
|
|
|
|
LOG_INF("%p (%s):\tunused %zu\tusage %zu / %zu (%u %%)",
|
|
thread, tname, unused, size - unused, size,
|
|
pcnt);
|
|
}
|
|
#endif
|
|
}
|
|
#endif /* ZEPHYR_INCLUDE_DEBUG_STACK_H_ */
|