2006-07-03 11:24:40 +04:00
/*
* Stack trace management functions
*
2009-01-31 04:03:42 +03:00
* Copyright ( C ) 2006 - 2009 Red Hat , Inc . , Ingo Molnar < mingo @ redhat . com >
2006-07-03 11:24:40 +04:00
*/
# include <linux/sched.h>
# include <linux/stacktrace.h>
2006-09-26 12:52:34 +04:00
# include <linux/module.h>
2008-11-22 14:28:47 +03:00
# include <linux/uaccess.h>
2006-09-26 12:52:34 +04:00
# include <asm/stacktrace.h>
2006-07-03 11:24:40 +04:00
2006-09-26 12:52:34 +04:00
static int save_stack_stack ( void * data , char * name )
2006-07-03 11:24:40 +04:00
{
2009-05-15 07:19:09 +04:00
return 0 ;
2006-09-26 12:52:34 +04:00
}
2006-07-03 11:24:40 +04:00
2010-06-03 23:32:43 +04:00
static void
__save_stack_address ( void * data , unsigned long addr , bool reliable , bool nosched )
2006-09-26 12:52:34 +04:00
{
2008-01-30 15:33:23 +03:00
struct stack_trace * trace = data ;
2010-06-03 23:32:39 +04:00
# ifdef CONFIG_FRAME_POINTER
2008-02-22 21:23:58 +03:00
if ( ! reliable )
return ;
2010-06-03 23:32:39 +04:00
# endif
2010-06-03 23:32:43 +04:00
if ( nosched & & in_sched_functions ( addr ) )
return ;
2006-09-26 12:52:34 +04:00
if ( trace - > skip > 0 ) {
trace - > skip - - ;
return ;
2006-07-03 11:24:40 +04:00
}
2007-02-13 15:26:21 +03:00
if ( trace - > nr_entries < trace - > max_entries )
2006-09-26 12:52:34 +04:00
trace - > entries [ trace - > nr_entries + + ] = addr ;
2006-07-03 11:24:40 +04:00
}
2010-06-03 23:32:43 +04:00
static void save_stack_address ( void * data , unsigned long addr , int reliable )
{
return __save_stack_address ( data , addr , reliable , false ) ;
}
2008-01-30 15:33:07 +03:00
static void
save_stack_address_nosched ( void * data , unsigned long addr , int reliable )
2008-01-25 23:08:34 +03:00
{
2010-06-03 23:32:43 +04:00
return __save_stack_address ( data , addr , reliable , true ) ;
2008-01-25 23:08:34 +03:00
}
2007-10-17 20:04:37 +04:00
static const struct stacktrace_ops save_stack_ops = {
2009-12-17 07:40:33 +03:00
. stack = save_stack_stack ,
. address = save_stack_address ,
. walk_stack = print_context_stack ,
2006-09-26 12:52:34 +04:00
} ;
2006-07-03 11:24:40 +04:00
2008-01-25 23:08:34 +03:00
static const struct stacktrace_ops save_stack_ops_nosched = {
2009-12-17 07:40:33 +03:00
. stack = save_stack_stack ,
. address = save_stack_address_nosched ,
. walk_stack = print_context_stack ,
2008-01-25 23:08:34 +03:00
} ;
2006-07-03 11:24:40 +04:00
/*
* 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 )
2006-07-03 11:24:40 +04:00
{
2011-03-18 05:40:06 +03:00
dump_trace ( current , NULL , NULL , 0 , & save_stack_ops , trace ) ;
2007-02-13 15:26:21 +03:00
if ( trace - > nr_entries < trace - > max_entries )
trace - > entries [ trace - > nr_entries + + ] = ULONG_MAX ;
2006-07-03 11:24:40 +04:00
}
2008-06-27 23:20:17 +04:00
EXPORT_SYMBOL_GPL ( save_stack_trace ) ;
2008-01-25 23:08:34 +03:00
2011-06-08 11:09:21 +04:00
void save_stack_trace_regs ( struct pt_regs * regs , struct stack_trace * trace )
2008-05-20 13:15:43 +04:00
{
2011-03-18 05:40:06 +03:00
dump_trace ( current , regs , NULL , 0 , & save_stack_ops , trace ) ;
2008-05-20 13:15:43 +04:00
if ( trace - > nr_entries < trace - > max_entries )
trace - > entries [ trace - > nr_entries + + ] = ULONG_MAX ;
}
2008-01-25 23:08:34 +03:00
void save_stack_trace_tsk ( struct task_struct * tsk , struct stack_trace * trace )
{
2011-03-18 05:40:06 +03:00
dump_trace ( tsk , NULL , NULL , 0 , & save_stack_ops_nosched , trace ) ;
2008-01-25 23:08:34 +03:00
if ( trace - > nr_entries < trace - > max_entries )
trace - > entries [ trace - > nr_entries + + ] = ULONG_MAX ;
}
2008-06-27 23:20:17 +04:00
EXPORT_SYMBOL_GPL ( save_stack_trace_tsk ) ;
2008-11-22 14:28:47 +03:00
/* Userspace stacktrace - based on kernel/trace/trace_sysprof.c */
2010-05-19 23:35:17 +04:00
struct stack_frame_user {
2008-11-22 14:28:47 +03:00
const void __user * next_fp ;
2008-11-23 13:39:06 +03:00
unsigned long ret_addr ;
2008-11-22 14:28:47 +03:00
} ;
2010-05-19 23:35:17 +04:00
static int
copy_stack_frame ( const void __user * fp , struct stack_frame_user * frame )
2008-11-22 14:28:47 +03:00
{
int ret ;
if ( ! access_ok ( VERIFY_READ , fp , sizeof ( * frame ) ) )
return 0 ;
ret = 1 ;
pagefault_disable ( ) ;
if ( __copy_from_user_inatomic ( frame , fp , sizeof ( * frame ) ) )
ret = 0 ;
pagefault_enable ( ) ;
return ret ;
}
2008-11-23 13:39:06 +03:00
static inline void __save_stack_trace_user ( struct stack_trace * trace )
{
const struct pt_regs * regs = task_pt_regs ( current ) ;
const void __user * fp = ( const void __user * ) regs - > bp ;
if ( trace - > nr_entries < trace - > max_entries )
trace - > entries [ trace - > nr_entries + + ] = regs - > ip ;
while ( trace - > nr_entries < trace - > max_entries ) {
2010-05-19 23:35:17 +04:00
struct stack_frame_user frame ;
2008-11-23 13:39:06 +03:00
frame . next_fp = NULL ;
frame . ret_addr = 0 ;
if ( ! copy_stack_frame ( fp , & frame ) )
break ;
if ( ( unsigned long ) fp < regs - > sp )
break ;
if ( frame . ret_addr ) {
trace - > entries [ trace - > nr_entries + + ] =
frame . ret_addr ;
}
if ( fp = = frame . next_fp )
break ;
fp = frame . next_fp ;
}
}
2008-11-22 14:28:47 +03:00
void save_stack_trace_user ( struct stack_trace * trace )
{
/*
* Trace user stack if we are not a kernel thread
*/
if ( current - > mm ) {
2008-11-23 13:39:06 +03:00
__save_stack_trace_user ( trace ) ;
2008-11-22 14:28:47 +03:00
}
if ( trace - > nr_entries < trace - > max_entries )
trace - > entries [ trace - > nr_entries + + ] = ULONG_MAX ;
}