From b98a60773b3f2a592b9a1dea4899ec26ca2ab4fb Mon Sep 17 00:00:00 2001 From: Yong Cong Sin Date: Tue, 11 Jun 2024 14:51:02 +0800 Subject: [PATCH] shell: kernel: add command to unwind a thread Add a shell command to unwind a thread using its thread id. uart:~$ kernel threads Scheduler: 11 since last call Threads: *0x80017138 shell_uart options: 0x0, priority: 14 timeout: 0 state: queued, entry: 0x800029ac stack size 3072, unused 1316, usage 1756 / 3072 (57 %) 0x80017ca8 sysworkq options: 0x1, priority: -1 timeout: 0 state: pending, entry: 0x80006842 stack size 1024, unused 644, usage 380 / 1024 (37 %) 0x800177e0 idle options: 0x1, priority: 15 timeout: 0 state: , entry: 0x800065ae stack size 512, unused 180, usage 332 / 512 (64 %) 0x80017950 main options: 0x1, priority: 0 timeout: 13 state: suspended, entry: 0x80006326 stack size 4096, unused 3604, usage 492 / 4096 (12 %) uart:~$ kernel unwind 0x80017ca8 Unwinding 0x80017ca8 sysworkq ra: 0x80007114 [z_swap+0x58] ra: 0x80007ae8 [z_sched_wait+0x10] ra: 0x8000689a [work_queue_main+0x58] ra: 0x800006de [z_thread_entry+0x2e] Signed-off-by: Yong Cong Sin --- subsys/shell/modules/kernel_service.c | 64 +++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/subsys/shell/modules/kernel_service.c b/subsys/shell/modules/kernel_service.c index 992a83f65b5..246120f9234 100644 --- a/subsys/shell/modules/kernel_service.c +++ b/subsys/shell/modules/kernel_service.c @@ -24,6 +24,7 @@ #if defined(CONFIG_LOG_RUNTIME_FILTERING) #include #endif +#include #if defined(CONFIG_THREAD_MAX_NAME_LEN) #define THREAD_MAX_NAM_LEN CONFIG_THREAD_MAX_NAME_LEN @@ -203,6 +204,66 @@ static int cmd_kernel_threads(const struct shell *sh, return 0; } +#if defined(CONFIG_ARCH_HAS_STACKWALK) + +static bool print_trace_address(void *arg, unsigned long ra) +{ + const struct shell *sh = arg; +#ifdef CONFIG_SYMTAB + uint32_t offset = 0; + const char *name = symtab_find_symbol_name(ra, &offset); + + shell_print(sh, "ra: %p [%s+0x%x]", (void *)ra, name, offset); +#else + shell_print(sh, "ra: %p", (void *)ra); +#endif + + return true; +} + +struct unwind_entry { + const struct k_thread *const thread; + bool valid; +}; + +static void is_valid_thread(const struct k_thread *cthread, void *user_data) +{ + struct unwind_entry *entry = user_data; + + if (cthread == entry->thread) { + entry->valid = true; + } +} + +static int cmd_kernel_unwind(const struct shell *sh, size_t argc, char **argv) +{ + struct k_thread *thread; + + if (argc == 1) { + thread = _current; + } else { + thread = UINT_TO_POINTER(strtoll(argv[1], NULL, 16)); + struct unwind_entry entry = { + .thread = thread, + .valid = false, + }; + + k_thread_foreach_unlocked(is_valid_thread, &entry); + + if (!entry.valid) { + shell_error(sh, "Invalid thread id %p", (void *)thread); + return -EINVAL; + } + } + shell_print(sh, "Unwinding %p %s", (void *)thread, thread->name); + + arch_stack_walk(print_trace_address, (void *)sh, thread, NULL); + + return 0; +} + +#endif /* CONFIG_ARCH_HAS_STACKWALK */ + static void shell_stack_dump(const struct k_thread *thread, void *user_data) { const struct shell *sh = (const struct shell *)user_data; @@ -397,6 +458,9 @@ SHELL_STATIC_SUBCMD_SET_CREATE(sub_kernel, defined(CONFIG_THREAD_MONITOR) SHELL_CMD(stacks, NULL, "List threads stack usage.", cmd_kernel_stacks), SHELL_CMD(threads, NULL, "List kernel threads.", cmd_kernel_threads), +#if defined(CONFIG_ARCH_HAS_STACKWALK) + SHELL_CMD_ARG(unwind, NULL, "Unwind a thread.", cmd_kernel_unwind, 1, 1), +#endif /* CONFIG_ARCH_HAS_STACKWALK */ #endif #if defined(CONFIG_SYS_HEAP_RUNTIME_STATS) && (K_HEAP_MEM_POOL_SIZE > 0) SHELL_CMD(heap, NULL, "System heap usage statistics.", cmd_kernel_heap),