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 <ycsin@meta.com>
This commit is contained in:
Yong Cong Sin 2024-06-11 14:51:02 +08:00 committed by Anas Nashif
commit b98a60773b

View file

@ -24,6 +24,7 @@
#if defined(CONFIG_LOG_RUNTIME_FILTERING) #if defined(CONFIG_LOG_RUNTIME_FILTERING)
#include <zephyr/logging/log_ctrl.h> #include <zephyr/logging/log_ctrl.h>
#endif #endif
#include <zephyr/debug/symtab.h>
#if defined(CONFIG_THREAD_MAX_NAME_LEN) #if defined(CONFIG_THREAD_MAX_NAME_LEN)
#define THREAD_MAX_NAM_LEN 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; 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) static void shell_stack_dump(const struct k_thread *thread, void *user_data)
{ {
const struct shell *sh = (const struct shell *)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) defined(CONFIG_THREAD_MONITOR)
SHELL_CMD(stacks, NULL, "List threads stack usage.", cmd_kernel_stacks), SHELL_CMD(stacks, NULL, "List threads stack usage.", cmd_kernel_stacks),
SHELL_CMD(threads, NULL, "List kernel threads.", cmd_kernel_threads), 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 #endif
#if defined(CONFIG_SYS_HEAP_RUNTIME_STATS) && (K_HEAP_MEM_POOL_SIZE > 0) #if defined(CONFIG_SYS_HEAP_RUNTIME_STATS) && (K_HEAP_MEM_POOL_SIZE > 0)
SHELL_CMD(heap, NULL, "System heap usage statistics.", cmd_kernel_heap), SHELL_CMD(heap, NULL, "System heap usage statistics.", cmd_kernel_heap),