Skip to content

Commit af22c7d

Browse files
committed
debug: stack_size_analyzer: Implement stack size analyzer
Stack size analyzer is simple module that helps in printing stacks usage statistics. Signed-off-by: Radoslaw Koppel <[email protected]> Signed-off-by: Bartosz Gentkowski <[email protected]>
1 parent 0b7cf9c commit af22c7d

File tree

8 files changed

+297
-1
lines changed

8 files changed

+297
-1
lines changed

CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ Kconfig* @SebastianBoe
9797
/subsys/bluetooth/controller/ @joerchan @rugeGerritsen
9898
/subsys/bootloader/ @hakonfam @ioannisg
9999
/subsys/debug/ @nordic-krch @anangl
100+
/subsys/debug/stack_size_analyzer/ @rakons
100101
/subsys/dfu/ @hakonfam @sigvartmh
101102
/subsys/esb/ @Raane @lemrey
102103
/subsys/event_manager/ @pdunaj
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright (c) 2019 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic
5+
*/
6+
#ifndef __STACK_SIZE_ANALYZER_H
7+
#define __STACK_SIZE_ANALYZER_H
8+
#include <stddef.h>
9+
10+
#ifdef __cplusplus
11+
extern "C" {
12+
#endif
13+
14+
/**
15+
* @defgroup stack_size_analyzer Stack analyzer
16+
* @brief Module for analyzing stack usage in samples
17+
*
18+
* This module implements functions and the configuration that simplifies
19+
* stack size analysis.
20+
* @{
21+
*/
22+
23+
/**
24+
* @brief Stack size analyzer callback function
25+
*
26+
* Callback function with stack size statistics.
27+
*
28+
* @param name The name of the thread or stringified address
29+
* of the thread handle if name is not set.
30+
* @param size The total size of the stack
31+
* @param used Stack size in use
32+
*/
33+
typedef void (*stack_size_analyzer_cb)(const char *name,
34+
size_t size, size_t used);
35+
36+
/**
37+
* @brief Run the stack size analyzer and return the results to the callback
38+
*
39+
* This function analyzes stacks for all threads and calls
40+
* a given callback on every thread found.
41+
*
42+
* @param cb The callback function handler
43+
*/
44+
void stack_size_analyzer_run(stack_size_analyzer_cb cb);
45+
46+
/**
47+
* @brief Run the stack size analyzer and print the result
48+
*
49+
* This function runs the stack size analyzer and prints the output in standard
50+
* form.
51+
*/
52+
void stack_size_analyzer_print(void);
53+
54+
/** @} */
55+
56+
#ifdef __cplusplus
57+
}
58+
#endif
59+
60+
#endif /* __STACK_SIZE_ANALYZER_H */
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
.. _stack_size_analyzer:
2+
3+
Stack size analyzer
4+
###################
5+
6+
The stack size analyzer module enables all the Zephyr options required to track
7+
the stack usage.
8+
The analysis is performed on demand when the application calls
9+
:cpp:func:`stack_size_analyzer_run` or :cpp:func:`stack_size_analyzer_print`.
10+
11+
Configuration
12+
*************
13+
Configure this module using the following options.
14+
15+
* ``STACK_SIZE_ANALYZER``: enable the module.
16+
* ``STACK_SIZE_ANALYZER_USE_PRINTK``: use printk for stack statistics.
17+
* ``STACK_SIZE_ANALYZER_USE_LOG``: use the logger for stack statistics.
18+
* ``STACK_SIZE_ANALYZER_AUTO``: run the stack analyzer automatically.
19+
You do not need to add any code to the application when using this option.
20+
* ``STACK_SIZE_ANALYZER_AUTO_PERIOD``: the time for which the module sleeps
21+
between consecutive printing of stack size analysis in automatic mode.
22+
* ``STACK_SIZE_ANALYZER_AUTO_STACK_SIZE``: the stack size for stack size analyzer
23+
automatic thread.
24+
* ``THREAD_NAME``: enable this option in the kernel to print the name of the thread
25+
instead of its ID.
26+
27+
API documentation
28+
*****************
29+
30+
| Header file: :file:`include/debug/stack_size_analyzer.h`
31+
| Source files: :file:`subsys/debug/stack_size_analyzer/`
32+
33+
.. doxygengroup:: stack_size_analyzer
34+
:project: nrf
35+
:members:

subsys/debug/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@
55
#
66

77

8-
add_subdirectory_ifdef(CONFIG_PPI_TRACE ppi_trace)
8+
add_subdirectory_ifdef(CONFIG_PPI_TRACE ppi_trace)
9+
add_subdirectory_ifdef(CONFIG_STACK_SIZE_ANALYZER stack_size_analyzer)

subsys/debug/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@
55
#
66

77
rsource "ppi_trace/Kconfig"
8+
rsource "stack_size_analyzer/Kconfig"
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#
2+
# Copyright (c) 2019 Nordic Semiconductor
3+
#
4+
# SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic
5+
#
6+
7+
zephyr_sources(stack_size_analyzer.c)
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
#
2+
# Copyright (c) 2019 Nordic Semiconductor
3+
#
4+
# SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic
5+
#
6+
7+
menuconfig STACK_SIZE_ANALYZER
8+
bool "Enable Stack size analyzer functionality"
9+
select INIT_STACKS
10+
select THREAD_MONITOR
11+
select THREAD_STACK_INFO
12+
help
13+
Enable stack size analyzer functionality and all the required modules.
14+
This module may be used to debug stack overflow or to find stacks
15+
which size may be optimized down.
16+
The module uses less resources than shell implementation and does not
17+
require a valid input to type the commands.
18+
19+
if STACK_SIZE_ANALYZER
20+
module = STACK_SIZE_ANALYZER
21+
module-str = stack size analyzer
22+
source "${ZEPHYR_BASE}/subsys/logging/Kconfig.template.log_config"
23+
24+
choice
25+
prompt "Print mode"
26+
default STACK_SIZE_ANALYZER_USE_LOG
27+
28+
config STACK_SIZE_ANALYZER_USE_PRINTK
29+
bool "Use printk function"
30+
help
31+
Use kernel printk function to print stack size information.
32+
33+
config STACK_SIZE_ANALYZER_USE_LOG
34+
bool "Use logger output"
35+
select LOG
36+
help
37+
Use logger output to print stack size information.
38+
39+
endchoice
40+
41+
config STACK_SIZE_ANALYZER_RUN_UNLOCKED
42+
bool "Run analysis with interrupts unlocked"
43+
default y
44+
help
45+
The stack analisys takes quite a long time.
46+
Every stack it founds is analyzed word by word to find any that
47+
does not match the magic number.
48+
Normally while stacks are analyzed the k_thread_foreach function
49+
is used.
50+
It makes safe run from the thread list perspective but may lock
51+
the interrupts for a long time - long enough to disconnect when
52+
bluetooth communication is used.
53+
Setting this flag would force stack analyzer to use
54+
the k_thread_foreach_unlocked function.
55+
This would allow the interrupts to be processed while the stack size
56+
is analyzed.
57+
For the limitation of such configuration see the k_thread_foreach
58+
documentation.
59+
60+
config STACK_SIZE_ANALYZER_AUTO
61+
bool "Enable automatic printing"
62+
default y
63+
help
64+
Run the stack size analyzer automatically, without the need to add
65+
any code to the application.
66+
Stack size analysis would be called periodically.
67+
68+
if STACK_SIZE_ANALYZER_AUTO
69+
70+
config STACK_SIZE_ANALYZER_AUTO_PERIOD
71+
int "Automatic printing period"
72+
default 5000
73+
help
74+
The time in milliseconds to call stack analyzer periodic printing function.
75+
76+
config STACK_SIZE_ANALYZER_AUTO_STACK_SIZE
77+
int "The stack size for the thread to periodically run stack analisys"
78+
default 512
79+
80+
endif # STACK_SIZE_ANALYZER_AUTO
81+
82+
endif # STACK_SIZE_ANALYZER
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/*
2+
* Copyright (c) 2019 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic
5+
*/
6+
7+
/** @file
8+
* @brief Stack analyzer implementation
9+
*/
10+
11+
#include <debug/stack_size_analyzer.h>
12+
#include <debug/stack.h>
13+
#include <kernel.h>
14+
#include <logging/log.h>
15+
#include <stdio.h>
16+
17+
LOG_MODULE_REGISTER(stack_size_analyzer, CONFIG_STACK_SIZE_ANALYZER_LOG_LEVEL);
18+
19+
#if IS_ENABLED(CONFIG_STACK_SIZE_ANALYZER_USE_PRINTK)
20+
#define STACK_SIZE_ANALYZER_PRINT(...) printk(__VA_ARGS__)
21+
#define STACK_SIZE_ANALYZER_FMT(str) str "\n"
22+
#define STASK_SIZE_ANALYZER_VSTR(str) (str)
23+
#else
24+
#define STACK_SIZE_ANALYZER_PRINT(...) LOG_INF(__VA_ARGS__)
25+
#define STACK_SIZE_ANALYZER_FMT(str) str
26+
#define STASK_SIZE_ANALYZER_VSTR(str) log_strdup(str)
27+
#endif
28+
29+
/**
30+
* @brief Maximum length of the pointer when converted to string
31+
*
32+
* Pointer is converted to string in hexadecimal form.
33+
* It would use 2 hex digits for every single byte of the pointer
34+
* but some implementations adds 0x prefix when used with %p format option.
35+
*/
36+
#define PTR_STR_MAXLEN (sizeof(void *) * 2 + 2)
37+
38+
39+
/**
40+
* @brief Internal callback for default print
41+
*
42+
* @param name The name of the thread
43+
* @param size The total size of the stack
44+
* @param used The used size of the stack
45+
*/
46+
static void stack_print_cb(const char *name,
47+
size_t size, size_t used)
48+
{
49+
unsigned int pcnt = (used * 100U) / size;
50+
51+
STACK_SIZE_ANALYZER_PRINT(
52+
STACK_SIZE_ANALYZER_FMT(
53+
" %-20s: unused %u usage %u / %u (%u %%)"),
54+
STASK_SIZE_ANALYZER_VSTR(name),
55+
size - used, used, size, pcnt);
56+
}
57+
58+
static void stack_thread_cb(const struct k_thread *thread, void *user_data)
59+
{
60+
stack_size_analyzer_cb cb = user_data;
61+
const char *stack = (const char *)thread->stack_info.start;
62+
unsigned int size = thread->stack_info.size;
63+
unsigned int unused = stack_unused_space_get(stack, size);
64+
const char *name;
65+
char hexname[PTR_STR_MAXLEN + 1];
66+
67+
name = k_thread_name_get((k_tid_t)thread);
68+
if (!name || name[0] == '\0') {
69+
name = hexname;
70+
sprintf(hexname, "%p", (void *)thread);
71+
}
72+
73+
cb(name, size, size-unused);
74+
}
75+
76+
void stack_size_analyzer_run(stack_size_analyzer_cb cb)
77+
{
78+
if (IS_ENABLED(CONFIG_STACK_SIZE_ANALYZER_RUN_UNLOCKED)) {
79+
k_thread_foreach_unlocked(stack_thread_cb, cb);
80+
} else {
81+
k_thread_foreach(stack_thread_cb, cb);
82+
}
83+
}
84+
85+
void stack_size_analyzer_print(void)
86+
{
87+
STACK_SIZE_ANALYZER_PRINT(STACK_SIZE_ANALYZER_FMT("Stack analyze:"));
88+
stack_size_analyzer_run(stack_print_cb);
89+
}
90+
91+
#if IS_ENABLED(CONFIG_STACK_SIZE_ANALYZER_AUTO)
92+
93+
void stack_size_analyzer_auto(void)
94+
{
95+
for (;;) {
96+
stack_size_analyzer_print();
97+
k_sleep(CONFIG_STACK_SIZE_ANALYZER_AUTO_PERIOD);
98+
}
99+
}
100+
101+
K_THREAD_DEFINE(stack_size_analyzer,
102+
CONFIG_STACK_SIZE_ANALYZER_AUTO_STACK_SIZE,
103+
stack_size_analyzer_auto,
104+
NULL, NULL, NULL,
105+
CONFIG_NUM_PREEMPT_PRIORITIES - 1,
106+
0,
107+
K_NO_WAIT);
108+
109+
#endif

0 commit comments

Comments
 (0)