2006-09-26 18:44:01 +04:00
/*
* Stack trace management functions
*
* Copyright ( C ) 2006 Atsushi Nemoto < anemo @ mba . ocn . ne . jp >
*/
# include <linux/sched.h>
# include <linux/stacktrace.h>
2011-07-24 00:30:40 +04:00
# include <linux/export.h>
2006-09-26 18:44:01 +04:00
# include <asm/stacktrace.h>
/*
* Save stack - backtrace addresses into a stack_trace buffer :
*/
static void save_raw_context_stack ( struct stack_trace * trace ,
2006-09-28 14:15:33 +04:00
unsigned long reg29 )
2006-09-26 18:44:01 +04:00
{
unsigned long * sp = ( unsigned long * ) reg29 ;
unsigned long addr ;
while ( ! kstack_end ( sp ) ) {
addr = * sp + + ;
if ( __kernel_text_address ( addr ) ) {
2006-09-28 14:15:33 +04:00
if ( trace - > skip > 0 )
trace - > skip - - ;
2006-09-26 18:44:01 +04:00
else
2006-09-28 14:15:33 +04:00
trace - > entries [ trace - > nr_entries + + ] = addr ;
2006-09-26 18:44:01 +04:00
if ( trace - > nr_entries > = trace - > max_entries )
break ;
}
}
}
2008-11-10 11:26:08 +03:00
static void save_context_stack ( struct stack_trace * trace ,
struct task_struct * tsk , struct pt_regs * regs )
2006-09-26 18:44:01 +04:00
{
unsigned long sp = regs - > regs [ 29 ] ;
# ifdef CONFIG_KALLSYMS
unsigned long ra = regs - > regs [ 31 ] ;
unsigned long pc = regs - > cp0_epc ;
if ( raw_show_trace | | ! __kernel_text_address ( pc ) ) {
2006-09-29 13:02:51 +04:00
unsigned long stack_page =
2008-11-10 11:26:08 +03:00
( unsigned long ) task_stack_page ( tsk ) ;
2006-09-28 14:15:33 +04:00
if ( stack_page & & sp > = stack_page & &
sp < = stack_page + THREAD_SIZE - 32 )
save_raw_context_stack ( trace , sp ) ;
2006-09-29 13:02:51 +04:00
return ;
2006-09-26 18:44:01 +04:00
}
do {
2006-09-28 14:15:33 +04:00
if ( trace - > skip > 0 )
trace - > skip - - ;
2006-09-26 18:44:01 +04:00
else
2006-09-28 14:15:33 +04:00
trace - > entries [ trace - > nr_entries + + ] = pc ;
2006-09-26 18:44:01 +04:00
if ( trace - > nr_entries > = trace - > max_entries )
break ;
2008-11-10 11:26:08 +03:00
pc = unwind_stack ( tsk , & sp , pc , & ra ) ;
2006-09-26 18:44:01 +04:00
} while ( pc ) ;
# else
2006-10-16 17:48:49 +04:00
save_raw_context_stack ( trace , sp ) ;
2006-09-26 18:44:01 +04:00
# endif
}
/*
* Save stack - backtrace addresses into a stack_trace buffer .
*/
2007-05-08 11:23:29 +04:00
void save_stack_trace ( struct stack_trace * trace )
2008-11-10 11:26:08 +03:00
{
save_stack_trace_tsk ( current , trace ) ;
}
EXPORT_SYMBOL_GPL ( save_stack_trace ) ;
void save_stack_trace_tsk ( struct task_struct * tsk , struct stack_trace * trace )
2006-09-26 18:44:01 +04:00
{
struct pt_regs dummyregs ;
struct pt_regs * regs = & dummyregs ;
WARN_ON ( trace - > nr_entries | | ! trace - > max_entries ) ;
2008-11-10 11:26:08 +03:00
if ( tsk ! = current ) {
regs - > regs [ 29 ] = tsk - > thread . reg29 ;
regs - > regs [ 31 ] = 0 ;
regs - > cp0_epc = tsk - > thread . reg31 ;
} else
prepare_frametrace ( regs ) ;
save_context_stack ( trace , tsk , regs ) ;
2006-09-26 18:44:01 +04:00
}
2008-11-10 11:26:08 +03:00
EXPORT_SYMBOL_GPL ( save_stack_trace_tsk ) ;