2008-05-12 23:20:47 +04:00
/*
* trace stack traces
*
2008-05-12 23:20:54 +04:00
* Copyright ( C ) 2004 - 2008 , Soeren Sandmann
2008-05-12 23:20:47 +04:00
* Copyright ( C ) 2007 Steven Rostedt < srostedt @ redhat . com >
* Copyright ( C ) 2008 Ingo Molnar < mingo @ redhat . com >
*/
# include <linux/kallsyms.h>
2008-05-12 23:20:47 +04:00
# include <linux/debugfs.h>
# include <linux/hrtimer.h>
2008-05-12 23:20:47 +04:00
# include <linux/uaccess.h>
# include <linux/ftrace.h>
2008-05-12 23:20:47 +04:00
# include <linux/module.h>
2008-05-12 23:20:47 +04:00
# include <linux/irq.h>
2008-05-12 23:20:47 +04:00
# include <linux/fs.h>
2008-05-12 23:20:47 +04:00
2008-05-12 23:20:54 +04:00
# include <asm/stacktrace.h>
2008-05-12 23:20:47 +04:00
# include "trace.h"
2008-05-12 23:20:47 +04:00
static struct trace_array * sysprof_trace ;
2008-05-12 23:20:47 +04:00
static int __read_mostly tracer_enabled ;
2008-05-12 23:20:47 +04:00
/*
2008-05-12 23:20:49 +04:00
* 1 msec sample interval by default :
2008-05-12 23:20:47 +04:00
*/
2008-05-12 23:20:49 +04:00
static unsigned long sample_period = 1000000 ;
2008-05-12 23:20:47 +04:00
static const unsigned int sample_max_depth = 512 ;
2008-05-12 23:20:47 +04:00
2008-05-12 23:20:49 +04:00
static DEFINE_MUTEX ( sample_timer_lock ) ;
2008-05-12 23:20:47 +04:00
/*
* Per CPU hrtimers that do the profiling :
*/
static DEFINE_PER_CPU ( struct hrtimer , stack_trace_hrtimer ) ;
2008-05-12 23:20:47 +04:00
struct stack_frame {
const void __user * next_fp ;
unsigned long return_address ;
} ;
static int copy_stack_frame ( const void __user * fp , struct stack_frame * frame )
{
2008-05-12 23:20:48 +04:00
int ret ;
2008-05-12 23:20:47 +04:00
if ( ! access_ok ( VERIFY_READ , fp , sizeof ( * frame ) ) )
return 0 ;
2008-05-12 23:20:48 +04:00
ret = 1 ;
pagefault_disable ( ) ;
if ( __copy_from_user_inatomic ( frame , fp , sizeof ( * frame ) ) )
ret = 0 ;
pagefault_enable ( ) ;
2008-05-12 23:20:47 +04:00
2008-05-12 23:20:48 +04:00
return ret ;
2008-05-12 23:20:47 +04:00
}
2008-05-12 23:20:54 +04:00
struct backtrace_info {
struct trace_array_cpu * data ;
struct trace_array * tr ;
int pos ;
} ;
static void
backtrace_warning_symbol ( void * data , char * msg , unsigned long symbol )
{
/* Ignore warnings */
}
static void backtrace_warning ( void * data , char * msg )
{
/* Ignore warnings */
}
static int backtrace_stack ( void * data , char * name )
{
/* Don't bother with IRQ stacks for now */
return - 1 ;
}
static void backtrace_address ( void * data , unsigned long addr , int reliable )
{
struct backtrace_info * info = data ;
if ( info - > pos < sample_max_depth & & reliable ) {
__trace_special ( info - > tr , info - > data , 1 , addr , 0 ) ;
info - > pos + + ;
}
}
const static struct stacktrace_ops backtrace_ops = {
. warning = backtrace_warning ,
. warning_symbol = backtrace_warning_symbol ,
. stack = backtrace_stack ,
. address = backtrace_address ,
} ;
2008-05-12 07:28:50 +04:00
static int
2008-05-12 23:20:54 +04:00
trace_kernel ( struct pt_regs * regs , struct trace_array * tr ,
struct trace_array_cpu * data )
{
struct backtrace_info info ;
unsigned long bp ;
char * stack ;
info . tr = tr ;
info . data = data ;
info . pos = 1 ;
__trace_special ( info . tr , info . data , 1 , regs - > ip , 0 ) ;
stack = ( ( char * ) regs + sizeof ( struct pt_regs ) ) ;
# ifdef CONFIG_FRAME_POINTER
bp = regs - > bp ;
# else
bp = 0 ;
# endif
dump_trace ( NULL , regs , ( void * ) stack , bp , & backtrace_ops , & info ) ;
2008-05-12 07:28:50 +04:00
return info . pos ;
2008-05-12 23:20:54 +04:00
}
2008-05-12 23:20:47 +04:00
static void timer_notify ( struct pt_regs * regs , int cpu )
{
struct trace_array_cpu * data ;
struct stack_frame frame ;
struct trace_array * tr ;
2008-05-12 23:20:48 +04:00
const void __user * fp ;
2008-05-12 23:20:47 +04:00
int is_user ;
int i ;
if ( ! regs )
return ;
tr = sysprof_trace ;
data = tr - > data [ cpu ] ;
is_user = user_mode ( regs ) ;
if ( ! current | | current - > pid = = 0 )
return ;
if ( is_user & & current - > state ! = TASK_RUNNING )
return ;
2008-05-12 23:20:54 +04:00
__trace_special ( tr , data , 0 , 0 , current - > pid ) ;
2008-05-12 23:20:47 +04:00
2008-05-12 23:20:54 +04:00
if ( ! is_user )
2008-05-12 07:28:50 +04:00
i = trace_kernel ( regs , tr , data ) ;
else
i = 0 ;
2008-05-12 23:20:47 +04:00
2008-05-12 07:28:50 +04:00
/*
* Trace user stack if we are not a kernel thread
*/
if ( current - > mm & & i < sample_max_depth ) {
regs = ( struct pt_regs * ) current - > thread . sp0 - 1 ;
2008-05-12 23:20:47 +04:00
2008-05-12 07:28:50 +04:00
fp = ( void __user * ) regs - > bp ;
2008-05-12 23:20:54 +04:00
2008-05-12 07:28:50 +04:00
__trace_special ( tr , data , 2 , regs - > ip , 0 ) ;
2008-05-12 23:20:47 +04:00
2008-05-12 07:28:50 +04:00
while ( i < sample_max_depth ) {
2008-07-25 12:48:39 +04:00
frame . next_fp = NULL ;
2008-05-12 07:28:50 +04:00
frame . return_address = 0 ;
if ( ! copy_stack_frame ( fp , & frame ) )
break ;
if ( ( unsigned long ) fp < regs - > sp )
break ;
2008-05-12 23:20:47 +04:00
2008-05-12 07:28:50 +04:00
__trace_special ( tr , data , 2 , frame . return_address ,
( unsigned long ) fp ) ;
fp = frame . next_fp ;
i + + ;
}
}
2008-05-12 23:20:47 +04:00
2008-05-12 23:20:48 +04:00
/*
* Special trace entry if we overflow the max depth :
*/
2008-05-12 23:20:47 +04:00
if ( i = = sample_max_depth )
2008-05-24 01:55:54 +04:00
__trace_special ( tr , data , - 1 , - 1 , - 1 ) ;
2008-05-12 07:28:50 +04:00
__trace_special ( tr , data , 3 , current - > pid , i ) ;
2008-05-12 23:20:47 +04:00
}
2008-05-12 23:20:47 +04:00
static enum hrtimer_restart stack_trace_timer_fn ( struct hrtimer * hrtimer )
{
/* trace here */
2008-05-12 23:20:47 +04:00
timer_notify ( get_irq_regs ( ) , smp_processor_id ( ) ) ;
2008-05-12 23:20:47 +04:00
hrtimer_forward_now ( hrtimer , ns_to_ktime ( sample_period ) ) ;
return HRTIMER_RESTART ;
}
2009-01-01 02:42:22 +03:00
static void start_stack_timer ( void * unused )
2008-05-12 23:20:47 +04:00
{
2009-01-01 02:42:22 +03:00
struct hrtimer * hrtimer = & __get_cpu_var ( stack_trace_hrtimer ) ;
2008-05-12 23:20:47 +04:00
hrtimer_init ( hrtimer , CLOCK_MONOTONIC , HRTIMER_MODE_REL ) ;
hrtimer - > function = stack_trace_timer_fn ;
hrtimer_start ( hrtimer , ns_to_ktime ( sample_period ) , HRTIMER_MODE_REL ) ;
}
static void start_stack_timers ( void )
{
2009-01-01 02:42:22 +03:00
on_each_cpu ( start_stack_timer , NULL , 1 ) ;
2008-05-12 23:20:47 +04:00
}
static void stop_stack_timer ( int cpu )
{
struct hrtimer * hrtimer = & per_cpu ( stack_trace_hrtimer , cpu ) ;
hrtimer_cancel ( hrtimer ) ;
}
static void stop_stack_timers ( void )
{
int cpu ;
for_each_online_cpu ( cpu )
stop_stack_timer ( cpu ) ;
}
2008-05-24 01:50:41 +04:00
static void start_stack_trace ( struct trace_array * tr )
2008-05-12 23:20:47 +04:00
{
2008-05-12 23:20:49 +04:00
mutex_lock ( & sample_timer_lock ) ;
2008-12-19 13:08:39 +03:00
tracing_reset_online_cpus ( tr ) ;
2008-05-12 23:20:47 +04:00
start_stack_timers ( ) ;
2008-05-12 23:20:47 +04:00
tracer_enabled = 1 ;
2008-05-12 23:20:49 +04:00
mutex_unlock ( & sample_timer_lock ) ;
2008-05-12 23:20:47 +04:00
}
2008-05-24 01:50:41 +04:00
static void stop_stack_trace ( struct trace_array * tr )
2008-05-12 23:20:47 +04:00
{
2008-05-12 23:20:49 +04:00
mutex_lock ( & sample_timer_lock ) ;
2008-05-12 23:20:47 +04:00
stop_stack_timers ( ) ;
2008-05-12 23:20:47 +04:00
tracer_enabled = 0 ;
2008-05-12 23:20:49 +04:00
mutex_unlock ( & sample_timer_lock ) ;
2008-05-12 23:20:47 +04:00
}
2008-11-16 07:57:26 +03:00
static int stack_trace_init ( struct trace_array * tr )
2008-05-12 23:20:47 +04:00
{
2008-05-12 23:20:47 +04:00
sysprof_trace = tr ;
2008-05-12 23:20:47 +04:00
2008-11-08 06:36:02 +03:00
start_stack_trace ( tr ) ;
2008-11-16 07:57:26 +03:00
return 0 ;
2008-05-12 23:20:47 +04:00
}
2008-05-24 01:50:41 +04:00
static void stack_trace_reset ( struct trace_array * tr )
2008-05-12 23:20:47 +04:00
{
2008-11-08 06:36:02 +03:00
stop_stack_trace ( tr ) ;
2008-05-12 23:20:47 +04:00
}
static struct tracer stack_trace __read_mostly =
{
. name = " sysprof " ,
. init = stack_trace_init ,
. reset = stack_trace_reset ,
# ifdef CONFIG_FTRACE_SELFTEST
2008-05-12 23:20:47 +04:00
. selftest = trace_selftest_startup_sysprof ,
2008-05-12 23:20:47 +04:00
# endif
} ;
__init static int init_stack_trace ( void )
{
return register_tracer ( & stack_trace ) ;
}
device_initcall ( init_stack_trace ) ;
2008-05-12 23:20:49 +04:00
# define MAX_LONG_DIGITS 22
static ssize_t
sysprof_sample_read ( struct file * filp , char __user * ubuf ,
size_t cnt , loff_t * ppos )
{
char buf [ MAX_LONG_DIGITS ] ;
int r ;
r = sprintf ( buf , " %ld \n " , nsecs_to_usecs ( sample_period ) ) ;
return simple_read_from_buffer ( ubuf , cnt , ppos , buf , r ) ;
}
static ssize_t
sysprof_sample_write ( struct file * filp , const char __user * ubuf ,
size_t cnt , loff_t * ppos )
{
char buf [ MAX_LONG_DIGITS ] ;
unsigned long val ;
if ( cnt > MAX_LONG_DIGITS - 1 )
cnt = MAX_LONG_DIGITS - 1 ;
if ( copy_from_user ( & buf , ubuf , cnt ) )
return - EFAULT ;
buf [ cnt ] = 0 ;
val = simple_strtoul ( buf , NULL , 10 ) ;
/*
* Enforce a minimum sample period of 100 usecs :
*/
if ( val < 100 )
val = 100 ;
mutex_lock ( & sample_timer_lock ) ;
stop_stack_timers ( ) ;
sample_period = val * 1000 ;
start_stack_timers ( ) ;
mutex_unlock ( & sample_timer_lock ) ;
return cnt ;
}
static struct file_operations sysprof_sample_fops = {
. read = sysprof_sample_read ,
. write = sysprof_sample_write ,
} ;
void init_tracer_sysprof_debugfs ( struct dentry * d_tracer )
{
struct dentry * entry ;
entry = debugfs_create_file ( " sysprof_sample_period " , 0644 ,
d_tracer , NULL , & sysprof_sample_fops ) ;
if ( entry )
return ;
pr_warning ( " Could not create debugfs 'dyn_ftrace_total_info' entry \n " ) ;
}