2007-05-15 23:37:20 +04:00
# include <linux/module.h>
2007-04-28 12:59:37 +04:00
# include <linux/sched.h>
# include <linux/stacktrace.h>
2009-02-11 15:07:53 +03:00
# include <asm/stacktrace.h>
# if defined(CONFIG_FRAME_POINTER) && !defined(CONFIG_ARM_UNWIND)
/*
* Unwind the current stack frame and store the new register values in the
* structure passed as argument . Unwinding is equivalent to a function return ,
* hence the new PC value rather than LR should be used for backtrace .
*
* With framepointer enabled , a simple function prologue looks like this :
* mov ip , sp
* stmdb sp ! , { fp , ip , lr , pc }
* sub fp , ip , # 4
*
* A simple function epilogue looks like this :
* ldm sp , { fp , sp , pc }
*
* Note that with framepointer enabled , even the leaf functions have the same
* prologue and epilogue , therefore we can ignore the LR value in this case .
*/
2009-07-21 12:56:27 +04:00
int notrace unwind_frame ( struct stackframe * frame )
2007-04-28 12:59:37 +04:00
{
2009-02-11 15:07:53 +03:00
unsigned long high , low ;
unsigned long fp = frame - > fp ;
2007-04-28 12:59:37 +04:00
2009-02-11 15:07:53 +03:00
/* only go to a higher address on the stack */
low = frame - > sp ;
high = ALIGN ( low , THREAD_SIZE ) + THREAD_SIZE ;
2007-04-28 12:59:37 +04:00
2009-02-11 15:07:53 +03:00
/* check current frame pointer is within bounds */
if ( fp < ( low + 12 ) | | fp + 4 > = high )
return - EINVAL ;
2007-04-28 12:59:37 +04:00
2009-02-11 15:07:53 +03:00
/* restore the registers from the stack frame */
frame - > fp = * ( unsigned long * ) ( fp - 12 ) ;
frame - > sp = * ( unsigned long * ) ( fp - 8 ) ;
frame - > pc = * ( unsigned long * ) ( fp - 4 ) ;
2007-04-28 12:59:37 +04:00
return 0 ;
}
2009-02-11 15:07:53 +03:00
# endif
2009-07-21 12:56:27 +04:00
void notrace walk_stackframe ( struct stackframe * frame ,
2009-02-11 15:07:53 +03:00
int ( * fn ) ( struct stackframe * , void * ) , void * data )
{
while ( 1 ) {
int ret ;
if ( fn ( frame , data ) )
break ;
ret = unwind_frame ( frame ) ;
if ( ret < 0 )
break ;
}
}
2007-05-15 23:37:20 +04:00
EXPORT_SYMBOL ( walk_stackframe ) ;
2007-04-28 12:59:37 +04:00
# ifdef CONFIG_STACKTRACE
struct stack_trace_data {
struct stack_trace * trace ;
2008-04-24 09:31:46 +04:00
unsigned int no_sched_functions ;
2007-04-28 12:59:37 +04:00
unsigned int skip ;
} ;
static int save_trace ( struct stackframe * frame , void * d )
{
struct stack_trace_data * data = d ;
struct stack_trace * trace = data - > trace ;
2009-02-11 15:07:53 +03:00
unsigned long addr = frame - > pc ;
2007-04-28 12:59:37 +04:00
2008-04-24 09:31:46 +04:00
if ( data - > no_sched_functions & & in_sched_functions ( addr ) )
return 0 ;
2007-04-28 12:59:37 +04:00
if ( data - > skip ) {
data - > skip - - ;
return 0 ;
}
2008-04-24 09:31:46 +04:00
trace - > entries [ trace - > nr_entries + + ] = addr ;
2007-04-28 12:59:37 +04:00
return trace - > nr_entries > = trace - > max_entries ;
}
2008-04-24 09:31:46 +04:00
void save_stack_trace_tsk ( struct task_struct * tsk , struct stack_trace * trace )
2007-04-28 12:59:37 +04:00
{
struct stack_trace_data data ;
2009-02-11 15:07:53 +03:00
struct stackframe frame ;
2007-04-28 12:59:37 +04:00
data . trace = trace ;
data . skip = trace - > skip ;
2008-04-24 09:31:46 +04:00
if ( tsk ! = current ) {
# ifdef CONFIG_SMP
/*
* What guarantees do we have here that ' tsk '
* is not running on another CPU ?
*/
BUG ( ) ;
# else
data . no_sched_functions = 1 ;
2009-02-11 15:07:53 +03:00
frame . fp = thread_saved_fp ( tsk ) ;
frame . sp = thread_saved_sp ( tsk ) ;
frame . lr = 0 ; /* recovered from the stack */
frame . pc = thread_saved_pc ( tsk ) ;
2008-04-24 09:31:46 +04:00
# endif
} else {
2009-02-11 15:07:53 +03:00
register unsigned long current_sp asm ( " sp " ) ;
2008-04-24 09:31:46 +04:00
data . no_sched_functions = 0 ;
2009-02-11 15:07:53 +03:00
frame . fp = ( unsigned long ) __builtin_frame_address ( 0 ) ;
frame . sp = current_sp ;
frame . lr = ( unsigned long ) __builtin_return_address ( 0 ) ;
frame . pc = ( unsigned long ) save_stack_trace_tsk ;
2008-04-24 09:31:46 +04:00
}
2007-04-28 12:59:37 +04:00
2009-02-11 15:07:53 +03:00
walk_stackframe ( & frame , save_trace , & data ) ;
2008-04-24 09:31:46 +04:00
if ( trace - > nr_entries < trace - > max_entries )
trace - > entries [ trace - > nr_entries + + ] = ULONG_MAX ;
}
void save_stack_trace ( struct stack_trace * trace )
{
save_stack_trace_tsk ( current , trace ) ;
2007-04-28 12:59:37 +04:00
}
2008-07-03 11:17:55 +04:00
EXPORT_SYMBOL_GPL ( save_stack_trace ) ;
2007-04-28 12:59:37 +04:00
# endif