Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ Kconfig* @SebastianBoe
/subsys/bluetooth/controller/ @joerchan @rugeGerritsen
/subsys/bootloader/ @hakonfam @ioannisg
/subsys/debug/ @nordic-krch @anangl
/subsys/debug/stack_size_analyzer/ @rakons
/subsys/dfu/ @hakonfam @sigvartmh
/subsys/esb/ @Raane @lemrey
/subsys/event_manager/ @pdunaj
Expand Down
60 changes: 60 additions & 0 deletions include/debug/stack_size_analyzer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright (c) 2019 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic
*/
#ifndef __STACK_SIZE_ANALYZER_H
#define __STACK_SIZE_ANALYZER_H
#include <stddef.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
* @defgroup stack_size_analyzer Stack analyzer
* @brief Module for analyzing stack usage in samples
*
* This module implements functions and the configuration that simplifies
* stack size analysis.
* @{
*/

/**
* @brief Stack size analyzer callback function
*
* Callback function with stack size statistics.
*
* @param name The name of the thread or stringified address
* of the thread handle if name is not set.
* @param size The total size of the stack
* @param used Stack size in use
*/
typedef void (*stack_size_analyzer_cb)(const char *name,
size_t size, size_t used);

/**
* @brief Run the stack size analyzer and return the results to the callback
*
* This function analyzes stacks for all threads and calls
* a given callback on every thread found.
*
* @param cb The callback function handler
*/
void stack_size_analyzer_run(stack_size_analyzer_cb cb);

/**
* @brief Run the stack size analyzer and print the result
*
* This function runs the stack size analyzer and prints the output in standard
* form.
*/
void stack_size_analyzer_print(void);

/** @} */

#ifdef __cplusplus
}
#endif

#endif /* __STACK_SIZE_ANALYZER_H */
35 changes: 35 additions & 0 deletions include/debug/stack_size_analyzer.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
.. _stack_size_analyzer:

Stack size analyzer
###################

The stack size analyzer module enables all the Zephyr options required to track
the stack usage.
The analysis is performed on demand when the application calls
:cpp:func:`stack_size_analyzer_run` or :cpp:func:`stack_size_analyzer_print`.

Configuration
*************
Configure this module using the following options.

* ``STACK_SIZE_ANALYZER``: enable the module.
* ``STACK_SIZE_ANALYZER_USE_PRINTK``: use printk for stack statistics.
* ``STACK_SIZE_ANALYZER_USE_LOG``: use the logger for stack statistics.
* ``STACK_SIZE_ANALYZER_AUTO``: run the stack analyzer automatically.
You do not need to add any code to the application when using this option.
* ``STACK_SIZE_ANALYZER_AUTO_PERIOD``: the time for which the module sleeps
between consecutive printing of stack size analysis in automatic mode.
* ``STACK_SIZE_ANALYZER_AUTO_STACK_SIZE``: the stack size for stack size analyzer
automatic thread.
* ``THREAD_NAME``: enable this option in the kernel to print the name of the thread
instead of its ID.

API documentation
*****************

| Header file: :file:`include/debug/stack_size_analyzer.h`
| Source files: :file:`subsys/debug/stack_size_analyzer/`

.. doxygengroup:: stack_size_analyzer
:project: nrf
:members:
3 changes: 2 additions & 1 deletion subsys/debug/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@
#


add_subdirectory_ifdef(CONFIG_PPI_TRACE ppi_trace)
add_subdirectory_ifdef(CONFIG_PPI_TRACE ppi_trace)
add_subdirectory_ifdef(CONFIG_STACK_SIZE_ANALYZER stack_size_analyzer)
1 change: 1 addition & 0 deletions subsys/debug/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
#

rsource "ppi_trace/Kconfig"
rsource "stack_size_analyzer/Kconfig"
7 changes: 7 additions & 0 deletions subsys/debug/stack_size_analyzer/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#
# Copyright (c) 2019 Nordic Semiconductor
#
# SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic
#

zephyr_sources(stack_size_analyzer.c)
82 changes: 82 additions & 0 deletions subsys/debug/stack_size_analyzer/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#
# Copyright (c) 2019 Nordic Semiconductor
#
# SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic
#

menuconfig STACK_SIZE_ANALYZER
bool "Enable Stack size analyzer functionality"
select INIT_STACKS
select THREAD_MONITOR
select THREAD_STACK_INFO
help
Enable stack size analyzer functionality and all the required modules.
This module may be used to debug stack overflow or to find stacks
which size may be optimized down.
The module uses less resources than shell implementation and does not
require a valid input to type the commands.

if STACK_SIZE_ANALYZER
module = STACK_SIZE_ANALYZER
module-str = stack size analyzer
source "${ZEPHYR_BASE}/subsys/logging/Kconfig.template.log_config"

choice
prompt "Print mode"
default STACK_SIZE_ANALYZER_USE_LOG

config STACK_SIZE_ANALYZER_USE_PRINTK
bool "Use printk function"
help
Use kernel printk function to print stack size information.

config STACK_SIZE_ANALYZER_USE_LOG
bool "Use logger output"
select LOG
help
Use logger output to print stack size information.

endchoice

config STACK_SIZE_ANALYZER_RUN_UNLOCKED
bool "Run analysis with interrupts unlocked"
default y
help
The stack analisys takes quite a long time.
Every stack it founds is analyzed word by word to find any that
does not match the magic number.
Normally while stacks are analyzed the k_thread_foreach function
is used.
It makes safe run from the thread list perspective but may lock
the interrupts for a long time - long enough to disconnect when
bluetooth communication is used.
Setting this flag would force stack analyzer to use
the k_thread_foreach_unlocked function.
This would allow the interrupts to be processed while the stack size
is analyzed.
For the limitation of such configuration see the k_thread_foreach
documentation.

config STACK_SIZE_ANALYZER_AUTO
bool "Enable automatic printing"
default y
help
Run the stack size analyzer automatically, without the need to add
any code to the application.
Stack size analysis would be called periodically.

if STACK_SIZE_ANALYZER_AUTO

config STACK_SIZE_ANALYZER_AUTO_PERIOD
int "Automatic printing period"
default 5000
help
The time in milliseconds to call stack analyzer periodic printing function.

config STACK_SIZE_ANALYZER_AUTO_STACK_SIZE
int "The stack size for the thread to periodically run stack analisys"
default 512

endif # STACK_SIZE_ANALYZER_AUTO

endif # STACK_SIZE_ANALYZER
109 changes: 109 additions & 0 deletions subsys/debug/stack_size_analyzer/stack_size_analyzer.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
* Copyright (c) 2019 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic
*/

/** @file
* @brief Stack analyzer implementation
*/

#include <debug/stack_size_analyzer.h>
#include <debug/stack.h>
#include <kernel.h>
#include <logging/log.h>
#include <stdio.h>

LOG_MODULE_REGISTER(stack_size_analyzer, CONFIG_STACK_SIZE_ANALYZER_LOG_LEVEL);

#if IS_ENABLED(CONFIG_STACK_SIZE_ANALYZER_USE_PRINTK)
#define STACK_SIZE_ANALYZER_PRINT(...) printk(__VA_ARGS__)
#define STACK_SIZE_ANALYZER_FMT(str) str "\n"
#define STASK_SIZE_ANALYZER_VSTR(str) (str)
#else
#define STACK_SIZE_ANALYZER_PRINT(...) LOG_INF(__VA_ARGS__)
#define STACK_SIZE_ANALYZER_FMT(str) str
#define STASK_SIZE_ANALYZER_VSTR(str) log_strdup(str)
#endif

/**
* @brief Maximum length of the pointer when converted to string
*
* Pointer is converted to string in hexadecimal form.
* It would use 2 hex digits for every single byte of the pointer
* but some implementations adds 0x prefix when used with %p format option.
*/
#define PTR_STR_MAXLEN (sizeof(void *) * 2 + 2)


/**
* @brief Internal callback for default print
*
* @param name The name of the thread
* @param size The total size of the stack
* @param used The used size of the stack
*/
static void stack_print_cb(const char *name,
size_t size, size_t used)
{
unsigned int pcnt = (used * 100U) / size;

STACK_SIZE_ANALYZER_PRINT(
STACK_SIZE_ANALYZER_FMT(
" %-20s: unused %u usage %u / %u (%u %%)"),
STASK_SIZE_ANALYZER_VSTR(name),
size - used, used, size, pcnt);
}

static void stack_thread_cb(const struct k_thread *thread, void *user_data)
{
stack_size_analyzer_cb cb = user_data;
const char *stack = (const char *)thread->stack_info.start;
unsigned int size = thread->stack_info.size;
unsigned int unused = stack_unused_space_get(stack, size);
const char *name;
char hexname[PTR_STR_MAXLEN + 1];

name = k_thread_name_get((k_tid_t)thread);
if (!name || name[0] == '\0') {
name = hexname;
sprintf(hexname, "%p", (void *)thread);
}

cb(name, size, size-unused);
}

void stack_size_analyzer_run(stack_size_analyzer_cb cb)
{
if (IS_ENABLED(CONFIG_STACK_SIZE_ANALYZER_RUN_UNLOCKED)) {
k_thread_foreach_unlocked(stack_thread_cb, cb);
} else {
k_thread_foreach(stack_thread_cb, cb);
}
}

void stack_size_analyzer_print(void)
{
STACK_SIZE_ANALYZER_PRINT(STACK_SIZE_ANALYZER_FMT("Stack analyze:"));
stack_size_analyzer_run(stack_print_cb);
}

#if IS_ENABLED(CONFIG_STACK_SIZE_ANALYZER_AUTO)

void stack_size_analyzer_auto(void)
{
for (;;) {
stack_size_analyzer_print();
k_sleep(CONFIG_STACK_SIZE_ANALYZER_AUTO_PERIOD);
}
}

K_THREAD_DEFINE(stack_size_analyzer,
CONFIG_STACK_SIZE_ANALYZER_AUTO_STACK_SIZE,
stack_size_analyzer_auto,
NULL, NULL, NULL,
CONFIG_NUM_PREEMPT_PRIORITIES - 1,
0,
K_NO_WAIT);

#endif