|
| 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