2008-11-12 08:14:39 +03:00
/*
* unlikely profiler
*
* Copyright ( C ) 2008 Steven Rostedt < srostedt @ redhat . com >
*/
# include <linux/kallsyms.h>
# include <linux/seq_file.h>
# include <linux/spinlock.h>
# include <linux/debugfs.h>
# include <linux/uaccess.h>
# include <linux/module.h>
# include <linux/ftrace.h>
# include <linux/hash.h>
# include <linux/fs.h>
# include <asm/local.h>
# include "trace.h"
2008-11-12 23:24:24 +03:00
# ifdef CONFIG_BRANCH_TRACER
2008-11-12 08:14:40 +03:00
2008-11-12 23:24:24 +03:00
static int branch_tracing_enabled __read_mostly ;
static DEFINE_MUTEX ( branch_tracing_mutex ) ;
static struct trace_array * branch_tracer ;
2008-11-12 08:14:40 +03:00
static void
2008-11-12 23:24:24 +03:00
probe_likely_condition ( struct ftrace_branch_data * f , int val , int expect )
2008-11-12 08:14:40 +03:00
{
2008-11-12 23:24:24 +03:00
struct trace_array * tr = branch_tracer ;
2008-11-12 08:14:40 +03:00
struct ring_buffer_event * event ;
2008-11-12 23:24:24 +03:00
struct trace_branch * entry ;
2008-11-12 08:14:40 +03:00
unsigned long flags , irq_flags ;
int cpu , pc ;
const char * p ;
/*
* I would love to save just the ftrace_likely_data pointer , but
* this code can also be used by modules . Ugly things can happen
* if the module is unloaded , and then we go and read the
* pointer . This is slower , but much safer .
*/
if ( unlikely ( ! tr ) )
return ;
2008-11-16 07:59:52 +03:00
raw_local_irq_save ( flags ) ;
2008-11-12 08:14:40 +03:00
cpu = raw_smp_processor_id ( ) ;
if ( atomic_inc_return ( & tr - > data [ cpu ] - > disabled ) ! = 1 )
goto out ;
event = ring_buffer_lock_reserve ( tr - > buffer , sizeof ( * entry ) ,
& irq_flags ) ;
if ( ! event )
goto out ;
pc = preempt_count ( ) ;
entry = ring_buffer_event_data ( event ) ;
tracing_generic_entry_update ( & entry - > ent , flags , pc ) ;
2008-11-12 23:24:24 +03:00
entry - > ent . type = TRACE_BRANCH ;
2008-11-12 08:14:40 +03:00
/* Strip off the path, only save the file */
p = f - > file + strlen ( f - > file ) ;
while ( p > = f - > file & & * p ! = ' / ' )
p - - ;
p + + ;
strncpy ( entry - > func , f - > func , TRACE_FUNC_SIZE ) ;
strncpy ( entry - > file , p , TRACE_FILE_SIZE ) ;
entry - > func [ TRACE_FUNC_SIZE ] = 0 ;
entry - > file [ TRACE_FILE_SIZE ] = 0 ;
entry - > line = f - > line ;
entry - > correct = val = = expect ;
ring_buffer_unlock_commit ( tr - > buffer , event , irq_flags ) ;
out :
atomic_dec ( & tr - > data [ cpu ] - > disabled ) ;
2008-11-16 07:59:52 +03:00
raw_local_irq_restore ( flags ) ;
2008-11-12 08:14:40 +03:00
}
static inline
2008-11-12 23:24:24 +03:00
void trace_likely_condition ( struct ftrace_branch_data * f , int val , int expect )
2008-11-12 08:14:40 +03:00
{
2008-11-12 23:24:24 +03:00
if ( ! branch_tracing_enabled )
2008-11-12 08:14:40 +03:00
return ;
probe_likely_condition ( f , val , expect ) ;
}
2008-11-12 23:24:24 +03:00
int enable_branch_tracing ( struct trace_array * tr )
2008-11-12 08:14:40 +03:00
{
int ret = 0 ;
2008-11-12 23:24:24 +03:00
mutex_lock ( & branch_tracing_mutex ) ;
branch_tracer = tr ;
2008-11-12 08:14:40 +03:00
/*
* Must be seen before enabling . The reader is a condition
* where we do not need a matching rmb ( )
*/
smp_wmb ( ) ;
2008-11-12 23:24:24 +03:00
branch_tracing_enabled + + ;
mutex_unlock ( & branch_tracing_mutex ) ;
2008-11-12 08:14:40 +03:00
return ret ;
}
2008-11-12 23:24:24 +03:00
void disable_branch_tracing ( void )
2008-11-12 08:14:40 +03:00
{
2008-11-12 23:24:24 +03:00
mutex_lock ( & branch_tracing_mutex ) ;
2008-11-12 08:14:40 +03:00
2008-11-12 23:24:24 +03:00
if ( ! branch_tracing_enabled )
2008-11-12 08:14:40 +03:00
goto out_unlock ;
2008-11-12 23:24:24 +03:00
branch_tracing_enabled - - ;
2008-11-12 08:14:40 +03:00
out_unlock :
2008-11-12 23:24:24 +03:00
mutex_unlock ( & branch_tracing_mutex ) ;
2008-11-12 08:14:40 +03:00
}
2008-11-12 23:24:24 +03:00
static void start_branch_trace ( struct trace_array * tr )
{
enable_branch_tracing ( tr ) ;
}
static void stop_branch_trace ( struct trace_array * tr )
{
disable_branch_tracing ( ) ;
}
2008-11-16 07:57:26 +03:00
static int branch_trace_init ( struct trace_array * tr )
2008-11-12 23:24:24 +03:00
{
int cpu ;
for_each_online_cpu ( cpu )
tracing_reset ( tr , cpu ) ;
start_branch_trace ( tr ) ;
2008-11-16 07:57:26 +03:00
return 0 ;
2008-11-12 23:24:24 +03:00
}
static void branch_trace_reset ( struct trace_array * tr )
{
stop_branch_trace ( tr ) ;
}
struct tracer branch_trace __read_mostly =
{
. name = " branch " ,
. init = branch_trace_init ,
. reset = branch_trace_reset ,
# ifdef CONFIG_FTRACE_SELFTEST
. selftest = trace_selftest_startup_branch ,
# endif
} ;
__init static int init_branch_trace ( void )
{
return register_tracer ( & branch_trace ) ;
}
device_initcall ( init_branch_trace ) ;
2008-11-12 08:14:40 +03:00
# else
static inline
2008-11-12 23:24:24 +03:00
void trace_likely_condition ( struct ftrace_branch_data * f , int val , int expect )
2008-11-12 08:14:40 +03:00
{
}
2008-11-12 23:24:24 +03:00
# endif /* CONFIG_BRANCH_TRACER */
2008-11-12 08:14:40 +03:00
2008-11-12 23:24:24 +03:00
void ftrace_likely_update ( struct ftrace_branch_data * f , int val , int expect )
2008-11-12 08:14:39 +03:00
{
2008-11-12 08:14:40 +03:00
/*
* I would love to have a trace point here instead , but the
* trace point code is so inundated with unlikely and likely
* conditions that the recursive nightmare that exists is too
* much to try to get working . At least for now .
*/
trace_likely_condition ( f , val , expect ) ;
2008-11-12 08:14:39 +03:00
/* FIXME: Make this atomic! */
if ( val = = expect )
f - > correct + + ;
else
f - > incorrect + + ;
}
EXPORT_SYMBOL ( ftrace_likely_update ) ;
struct ftrace_pointer {
void * start ;
void * stop ;
2008-11-21 09:30:54 +03:00
int hit ;
2008-11-12 08:14:39 +03:00
} ;
static void *
t_next ( struct seq_file * m , void * v , loff_t * pos )
{
2008-11-21 22:44:57 +03:00
const struct ftrace_pointer * f = m - > private ;
2008-11-12 23:24:24 +03:00
struct ftrace_branch_data * p = v ;
2008-11-12 08:14:39 +03:00
( * pos ) + + ;
if ( v = = ( void * ) 1 )
return f - > start ;
+ + p ;
if ( ( void * ) p > = ( void * ) f - > stop )
return NULL ;
return p ;
}
static void * t_start ( struct seq_file * m , loff_t * pos )
{
void * t = ( void * ) 1 ;
loff_t l = 0 ;
for ( ; t & & l < * pos ; t = t_next ( m , t , & l ) )
;
return t ;
}
static void t_stop ( struct seq_file * m , void * p )
{
}
static int t_show ( struct seq_file * m , void * v )
{
2008-11-21 22:44:57 +03:00
const struct ftrace_pointer * fp = m - > private ;
2008-11-12 23:24:24 +03:00
struct ftrace_branch_data * p = v ;
2008-11-12 08:14:39 +03:00
const char * f ;
2008-11-21 09:51:53 +03:00
long percent ;
2008-11-12 08:14:39 +03:00
if ( v = = ( void * ) 1 ) {
2008-11-21 09:30:54 +03:00
if ( fp - > hit )
seq_printf ( m , " miss hit %% " ) ;
else
seq_printf ( m , " correct incorrect %% " ) ;
seq_printf ( m , " Function "
2008-11-12 08:14:39 +03:00
" File Line \n "
" ------- --------- - "
" -------- "
" ---- ---- \n " ) ;
return 0 ;
}
/* Only print the file, not the path */
f = p - > file + strlen ( p - > file ) ;
while ( f > = p - > file & & * f ! = ' / ' )
f - - ;
f + + ;
2008-11-21 09:30:54 +03:00
/*
* The miss is overlayed on correct , and hit on incorrect .
*/
2008-11-12 08:14:39 +03:00
if ( p - > correct ) {
percent = p - > incorrect * 100 ;
percent / = p - > correct + p - > incorrect ;
} else
2008-11-21 09:51:53 +03:00
percent = p - > incorrect ? 100 : - 1 ;
2008-11-12 08:14:39 +03:00
2008-11-21 09:51:53 +03:00
seq_printf ( m , " %8lu %8lu " , p - > correct , p - > incorrect ) ;
if ( percent < 0 )
seq_printf ( m , " X " ) ;
else
seq_printf ( m , " %3ld " , percent ) ;
2008-11-12 08:14:39 +03:00
seq_printf ( m , " %-30.30s %-20.20s %d \n " , p - > func , f , p - > line ) ;
return 0 ;
}
static struct seq_operations tracing_likely_seq_ops = {
. start = t_start ,
. next = t_next ,
. stop = t_stop ,
. show = t_show ,
} ;
2008-11-21 08:40:40 +03:00
static int tracing_branch_open ( struct inode * inode , struct file * file )
2008-11-12 08:14:39 +03:00
{
int ret ;
ret = seq_open ( file , & tracing_likely_seq_ops ) ;
if ( ! ret ) {
struct seq_file * m = file - > private_data ;
m - > private = ( void * ) inode - > i_private ;
}
return ret ;
}
2008-11-21 08:40:40 +03:00
static const struct file_operations tracing_branch_fops = {
. open = tracing_branch_open ,
2008-11-12 08:14:39 +03:00
. read = seq_read ,
. llseek = seq_lseek ,
} ;
2008-11-21 09:30:54 +03:00
# ifdef CONFIG_PROFILE_ALL_BRANCHES
extern unsigned long __start_branch_profile [ ] ;
extern unsigned long __stop_branch_profile [ ] ;
2008-11-21 22:44:57 +03:00
static const struct ftrace_pointer ftrace_branch_pos = {
2008-11-21 09:30:54 +03:00
. start = __start_branch_profile ,
. stop = __stop_branch_profile ,
. hit = 1 ,
} ;
# endif /* CONFIG_PROFILE_ALL_BRANCHES */
2008-11-21 08:40:40 +03:00
extern unsigned long __start_annotated_branch_profile [ ] ;
extern unsigned long __stop_annotated_branch_profile [ ] ;
2008-11-12 08:14:39 +03:00
2008-11-21 08:40:40 +03:00
static const struct ftrace_pointer ftrace_annotated_branch_pos = {
. start = __start_annotated_branch_profile ,
. stop = __stop_annotated_branch_profile ,
2008-11-12 08:14:39 +03:00
} ;
2008-11-12 23:24:24 +03:00
static __init int ftrace_branch_init ( void )
2008-11-12 08:14:39 +03:00
{
struct dentry * d_tracer ;
struct dentry * entry ;
d_tracer = tracing_init_dentry ( ) ;
2008-11-21 08:40:40 +03:00
entry = debugfs_create_file ( " profile_annotated_branch " , 0444 , d_tracer ,
2008-11-21 22:44:57 +03:00
( void * ) & ftrace_annotated_branch_pos ,
2008-11-21 08:40:40 +03:00
& tracing_branch_fops ) ;
2008-11-12 08:14:39 +03:00
if ( ! entry )
2008-11-21 08:40:40 +03:00
pr_warning ( " Could not create debugfs "
" 'profile_annotatet_branch' entry \n " ) ;
2008-11-12 08:14:39 +03:00
2008-11-21 09:30:54 +03:00
# ifdef CONFIG_PROFILE_ALL_BRANCHES
entry = debugfs_create_file ( " profile_branch " , 0444 , d_tracer ,
2008-11-21 22:44:57 +03:00
( void * ) & ftrace_branch_pos ,
2008-11-21 09:30:54 +03:00
& tracing_branch_fops ) ;
if ( ! entry )
pr_warning ( " Could not create debugfs "
" 'profile_branch' entry \n " ) ;
# endif
2008-11-12 08:14:39 +03:00
return 0 ;
}
2008-11-12 23:24:24 +03:00
device_initcall ( ftrace_branch_init ) ;