Tuesday, July 28, 2015

Store and Print Stack Trace in Linux Kernel



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:

UA-48797665-1