2013-04-15 09:20:48 +04:00
/*
* arch / xtensa / kernel / stacktrace . c
*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file " COPYING " in the main directory of this archive
* for more details .
*
* Copyright ( C ) 2001 - 2013 Tensilica Inc .
*/
# include <linux/export.h>
# include <linux/sched.h>
# include <linux/stacktrace.h>
# include <asm/stacktrace.h>
# include <asm/traps.h>
void walk_stackframe ( unsigned long * sp ,
int ( * fn ) ( struct stackframe * frame , void * data ) ,
void * data )
{
unsigned long a0 , a1 ;
unsigned long sp_end ;
a1 = ( unsigned long ) sp ;
sp_end = ALIGN ( a1 , THREAD_SIZE ) ;
spill_registers ( ) ;
while ( a1 < sp_end ) {
struct stackframe frame ;
sp = ( unsigned long * ) a1 ;
a0 = * ( sp - 4 ) ;
a1 = * ( sp - 3 ) ;
if ( a1 < = ( unsigned long ) sp )
break ;
frame . pc = MAKE_PC_FROM_RA ( a0 , a1 ) ;
frame . sp = a1 ;
if ( fn ( & frame , data ) )
return ;
}
}
# ifdef CONFIG_STACKTRACE
struct stack_trace_data {
struct stack_trace * trace ;
unsigned skip ;
} ;
static int stack_trace_cb ( struct stackframe * frame , void * data )
{
struct stack_trace_data * trace_data = data ;
struct stack_trace * trace = trace_data - > trace ;
if ( trace_data - > skip ) {
- - trace_data - > skip ;
return 0 ;
}
if ( ! kernel_text_address ( frame - > pc ) )
return 0 ;
trace - > entries [ trace - > nr_entries + + ] = frame - > pc ;
return trace - > nr_entries > = trace - > max_entries ;
}
void save_stack_trace_tsk ( struct task_struct * task , struct stack_trace * trace )
{
struct stack_trace_data trace_data = {
. trace = trace ,
. skip = trace - > skip ,
} ;
walk_stackframe ( stack_pointer ( task ) , stack_trace_cb , & trace_data ) ;
}
EXPORT_SYMBOL_GPL ( save_stack_trace_tsk ) ;
void save_stack_trace ( struct stack_trace * trace )
{
save_stack_trace_tsk ( current , trace ) ;
}
EXPORT_SYMBOL_GPL ( save_stack_trace ) ;
# endif
2013-04-08 01:29:19 +04:00
# ifdef CONFIG_FRAME_POINTER
struct return_addr_data {
unsigned long addr ;
unsigned skip ;
} ;
static int return_address_cb ( struct stackframe * frame , void * data )
{
struct return_addr_data * r = data ;
if ( r - > skip ) {
- - r - > skip ;
return 0 ;
}
if ( ! kernel_text_address ( frame - > pc ) )
return 0 ;
r - > addr = frame - > pc ;
return 1 ;
}
unsigned long return_address ( unsigned level )
{
struct return_addr_data r = {
. skip = level + 1 ,
} ;
walk_stackframe ( stack_pointer ( NULL ) , return_address_cb , & r ) ;
return r . addr ;
}
EXPORT_SYMBOL ( return_address ) ;
# endif