Linux kernel has facilities to dump the stack (show_stack) from any function in kernel or kernel module. However, I haven't come across a facility that allows to store the stack trace for later retrieval and print the same. In the past, I used linux code and wrote my own function to achieve these. I will provide sample code for these facilities in this post.
Here is a sample code:
| #include "asm_powerpc_stk.h" #define MAX_STK_DEPTH 30 | |
| #define STK_TRACE_SKIP_CNT 2 | |
| #ifdef CONFIG_IRQSTACKS | |
| static __always_inline int my_valid_irq_stack(unsigned long sp, struct task_struct *p, | |
| unsigned long nbytes) | |
| { | |
| unsigned long stack_page; | |
| unsigned long cpu = task_cpu(p); | |
| /* | |
| * Avoid crashing if the stack has overflowed and corrupted | |
| * task_cpu(p), which is in the thread_info struct. | |
| */ | |
| if (cpu < NR_CPUS && cpu_possible(cpu)) { | |
| stack_page = (unsigned long) hardirq_ctx[cpu]; | |
| if (sp >= stack_page + sizeof(struct thread_struct) | |
| && sp <= stack_page + THREAD_SIZE - nbytes) | |
| return 1; | |
| stack_page = (unsigned long) softirq_ctx[cpu]; | |
| if (sp >= stack_page + sizeof(struct thread_struct) | |
| && sp <= stack_page + THREAD_SIZE - nbytes) | |
| return 1; | |
| } | |
| return 0; | |
| } | |
| #else | |
| #define my_valid_irq_stack(sp, p, nb) 0 | |
| #endif /* CONFIG_IRQSTACKS */ | |
| static __always_inline int my_validate_sp(unsigned long sp, struct task_struct *p, | |
| unsigned long nbytes) | |
| { | |
| unsigned long stack_page = (unsigned long)task_stack_page(p); | |
| if (sp >= (unsigned long)end_of_stack(p) | |
| && sp <= stack_page + THREAD_SIZE - nbytes) | |
| return 1; | |
| return my_valid_irq_stack(sp, p, nbytes); | |
| } | |
| static __always_inline unsigned int my_get_stack_trace(unsigned long *stk_trace) | |
| { | |
| unsigned long sp; | |
| unsigned int i; | |
| sp = my_get_current_stack_pointer(sp); /*Platform-specific func */ | |
| for (i=1; i< STK_TRACE_SKIP_CNT; i++) | |
| { | |
| if (!my_validate_sp(sp, current, STACK_FRAME_OVERHEAD)) | |
| return 0; | |
| sp = ((unsigned long *)sp)[0]; | |
| } | |
| for (i=0; i< MAX_STK_DEPTH; i++) | |
| { | |
| if (!my_validate_sp(sp, current, STACK_FRAME_OVERHEAD)) | |
| return i; | |
| stk_trace[i] = ((unsigned long *)sp)[STACK_FRAME_LR_SAVE]; | |
| sp = ((unsigned long *)sp)[0]; | |
| } | |
| return MAX_STK_DEPTH; | |
| } | |
| static void my_get_and_print_stack_trace() | |
| { | |
| unsigned long stk_trace[MAX_STK_DEPTH]; | |
| unsigned int stk_trace_len; | |
| stk_trace_len = my_get_stack_trace(stk_trace); | |
| for (k = 0; (k < stk_trace_len); k++) { | |
| printk(" %pS\n", (void *)stk_trace[k]); | |
| } | |
| } |
Here is the complete code in my git hub repository: https://github.com/babuneelam/linux_kernel_stack_trace.
Hope this helps.
References: