|
24 | 24 | #if defined(CONFIG_LOG_RUNTIME_FILTERING) |
25 | 25 | #include <zephyr/logging/log_ctrl.h> |
26 | 26 | #endif |
| 27 | +#include <zephyr/debug/symtab.h> |
27 | 28 |
|
28 | 29 | #if defined(CONFIG_THREAD_MAX_NAME_LEN) |
29 | 30 | #define THREAD_MAX_NAM_LEN CONFIG_THREAD_MAX_NAME_LEN |
@@ -203,6 +204,66 @@ static int cmd_kernel_threads(const struct shell *sh, |
203 | 204 | return 0; |
204 | 205 | } |
205 | 206 |
|
| 207 | +#if defined(CONFIG_ARCH_HAS_STACKWALK) |
| 208 | + |
| 209 | +static bool print_trace_address(void *arg, unsigned long ra) |
| 210 | +{ |
| 211 | + const struct shell *sh = arg; |
| 212 | +#ifdef CONFIG_SYMTAB |
| 213 | + uint32_t offset = 0; |
| 214 | + const char *name = symtab_find_symbol_name(ra, &offset); |
| 215 | + |
| 216 | + shell_print(sh, "ra: %p [%s+0x%x]", (void *)ra, name, offset); |
| 217 | +#else |
| 218 | + shell_print(sh, "ra: %p", (void *)ra); |
| 219 | +#endif |
| 220 | + |
| 221 | + return true; |
| 222 | +} |
| 223 | + |
| 224 | +struct unwind_entry { |
| 225 | + const struct k_thread *const thread; |
| 226 | + bool valid; |
| 227 | +}; |
| 228 | + |
| 229 | +static void is_valid_thread(const struct k_thread *cthread, void *user_data) |
| 230 | +{ |
| 231 | + struct unwind_entry *entry = user_data; |
| 232 | + |
| 233 | + if (cthread == entry->thread) { |
| 234 | + entry->valid = true; |
| 235 | + } |
| 236 | +} |
| 237 | + |
| 238 | +static int cmd_kernel_unwind(const struct shell *sh, size_t argc, char **argv) |
| 239 | +{ |
| 240 | + struct k_thread *thread; |
| 241 | + |
| 242 | + if (argc == 1) { |
| 243 | + thread = _current; |
| 244 | + } else { |
| 245 | + thread = UINT_TO_POINTER(strtoll(argv[1], NULL, 16)); |
| 246 | + struct unwind_entry entry = { |
| 247 | + .thread = thread, |
| 248 | + .valid = false, |
| 249 | + }; |
| 250 | + |
| 251 | + k_thread_foreach_unlocked(is_valid_thread, &entry); |
| 252 | + |
| 253 | + if (!entry.valid) { |
| 254 | + shell_error(sh, "Invalid thread id %p", (void *)thread); |
| 255 | + return -EINVAL; |
| 256 | + } |
| 257 | + } |
| 258 | + shell_print(sh, "Unwinding %p %s", (void *)thread, thread->name); |
| 259 | + |
| 260 | + arch_stack_walk(print_trace_address, (void *)sh, thread, NULL); |
| 261 | + |
| 262 | + return 0; |
| 263 | +} |
| 264 | + |
| 265 | +#endif /* CONFIG_ARCH_HAS_STACKWALK */ |
| 266 | + |
206 | 267 | static void shell_stack_dump(const struct k_thread *thread, void *user_data) |
207 | 268 | { |
208 | 269 | const struct shell *sh = (const struct shell *)user_data; |
@@ -397,6 +458,9 @@ SHELL_STATIC_SUBCMD_SET_CREATE(sub_kernel, |
397 | 458 | defined(CONFIG_THREAD_MONITOR) |
398 | 459 | SHELL_CMD(stacks, NULL, "List threads stack usage.", cmd_kernel_stacks), |
399 | 460 | SHELL_CMD(threads, NULL, "List kernel threads.", cmd_kernel_threads), |
| 461 | +#if defined(CONFIG_ARCH_HAS_STACKWALK) |
| 462 | + SHELL_CMD_ARG(unwind, NULL, "Unwind a thread.", cmd_kernel_unwind, 1, 1), |
| 463 | +#endif /* CONFIG_ARCH_HAS_STACKWALK */ |
400 | 464 | #endif |
401 | 465 | #if defined(CONFIG_SYS_HEAP_RUNTIME_STATS) && (K_HEAP_MEM_POOL_SIZE > 0) |
402 | 466 | SHELL_CMD(heap, NULL, "System heap usage statistics.", cmd_kernel_heap), |
|
0 commit comments