2009-03-06 19:21:47 +03:00
/*
* trace binary printk
*
* Copyright ( C ) 2008 Lai Jiangshan < laijs @ cn . fujitsu . com >
*
*/
2009-03-12 21:23:17 +03:00
# include <linux/seq_file.h>
# include <linux/debugfs.h>
# include <linux/uaccess.h>
2009-03-06 19:21:47 +03:00
# include <linux/kernel.h>
# include <linux/ftrace.h>
# include <linux/string.h>
2009-03-12 21:23:17 +03:00
# include <linux/module.h>
# include <linux/marker.h>
# include <linux/mutex.h>
2009-03-06 19:21:47 +03:00
# include <linux/ctype.h>
# include <linux/list.h>
# include <linux/slab.h>
# include <linux/fs.h>
# include "trace.h"
2009-03-06 19:21:48 +03:00
# ifdef CONFIG_MODULES
/*
2009-03-06 19:21:49 +03:00
* modules trace_printk ( ) ' s formats are autosaved in struct trace_bprintk_fmt
2009-03-06 19:21:48 +03:00
* which are queued on trace_bprintk_fmt_list .
*/
static LIST_HEAD ( trace_bprintk_fmt_list ) ;
2009-03-06 19:21:49 +03:00
/* serialize accesses to trace_bprintk_fmt_list */
static DEFINE_MUTEX ( btrace_mutex ) ;
2009-03-06 19:21:48 +03:00
struct trace_bprintk_fmt {
struct list_head list ;
char fmt [ 0 ] ;
} ;
static inline struct trace_bprintk_fmt * lookup_format ( const char * fmt )
2009-03-06 19:21:47 +03:00
{
2009-03-06 19:21:48 +03:00
struct trace_bprintk_fmt * pos ;
list_for_each_entry ( pos , & trace_bprintk_fmt_list , list ) {
if ( ! strcmp ( pos - > fmt , fmt ) )
return pos ;
}
return NULL ;
2009-03-06 19:21:47 +03:00
}
2009-03-06 19:21:48 +03:00
static
void hold_module_trace_bprintk_format ( const char * * start , const char * * end )
2009-03-06 19:21:47 +03:00
{
2009-03-06 19:21:48 +03:00
const char * * iter ;
2009-03-06 19:21:49 +03:00
mutex_lock ( & btrace_mutex ) ;
2009-03-06 19:21:48 +03:00
for ( iter = start ; iter < end ; iter + + ) {
struct trace_bprintk_fmt * tb_fmt = lookup_format ( * iter ) ;
if ( tb_fmt ) {
* iter = tb_fmt - > fmt ;
continue ;
}
tb_fmt = kmalloc ( offsetof ( struct trace_bprintk_fmt , fmt )
+ strlen ( * iter ) + 1 , GFP_KERNEL ) ;
if ( tb_fmt ) {
list_add_tail ( & tb_fmt - > list , & trace_bprintk_fmt_list ) ;
strcpy ( tb_fmt - > fmt , * iter ) ;
* iter = tb_fmt - > fmt ;
} else
* iter = NULL ;
}
2009-03-06 19:21:49 +03:00
mutex_unlock ( & btrace_mutex ) ;
2009-03-06 19:21:47 +03:00
}
2009-03-06 19:21:48 +03:00
static int module_trace_bprintk_format_notify ( struct notifier_block * self ,
unsigned long val , void * data )
{
struct module * mod = data ;
if ( mod - > num_trace_bprintk_fmt ) {
const char * * start = mod - > trace_bprintk_fmt_start ;
const char * * end = start + mod - > num_trace_bprintk_fmt ;
if ( val = = MODULE_STATE_COMING )
hold_module_trace_bprintk_format ( start , end ) ;
}
return 0 ;
}
# else /* !CONFIG_MODULES */
__init static int
module_trace_bprintk_format_notify ( struct notifier_block * self ,
unsigned long val , void * data )
{
return 0 ;
}
# endif /* CONFIG_MODULES */
__initdata_or_module static
struct notifier_block module_trace_bprintk_format_nb = {
. notifier_call = module_trace_bprintk_format_notify ,
} ;
2009-03-12 20:24:49 +03:00
int __trace_bprintk ( unsigned long ip , const char * fmt , . . . )
2009-03-06 19:21:49 +03:00
{
int ret ;
va_list ap ;
2009-03-06 19:21:47 +03:00
2009-03-06 19:21:49 +03:00
if ( unlikely ( ! fmt ) )
return 0 ;
2009-03-06 19:21:47 +03:00
2009-03-06 19:21:49 +03:00
if ( ! ( trace_flags & TRACE_ITER_PRINTK ) )
return 0 ;
va_start ( ap , fmt ) ;
2009-03-19 21:03:53 +03:00
ret = trace_vbprintk ( ip , fmt , ap ) ;
2009-03-06 19:21:49 +03:00
va_end ( ap ) ;
return ret ;
2009-03-06 19:21:47 +03:00
}
2009-03-12 20:24:49 +03:00
EXPORT_SYMBOL_GPL ( __trace_bprintk ) ;
2009-03-06 19:21:47 +03:00
2009-03-12 20:24:49 +03:00
int __ftrace_vbprintk ( unsigned long ip , const char * fmt , va_list ap )
2009-03-06 19:21:49 +03:00
{
if ( unlikely ( ! fmt ) )
return 0 ;
if ( ! ( trace_flags & TRACE_ITER_PRINTK ) )
return 0 ;
2009-03-19 21:03:53 +03:00
return trace_vbprintk ( ip , fmt , ap ) ;
2009-03-12 20:24:49 +03:00
}
EXPORT_SYMBOL_GPL ( __ftrace_vbprintk ) ;
int __trace_printk ( unsigned long ip , const char * fmt , . . . )
{
int ret ;
va_list ap ;
if ( ! ( trace_flags & TRACE_ITER_PRINTK ) )
return 0 ;
va_start ( ap , fmt ) ;
2009-03-19 21:03:53 +03:00
ret = trace_vprintk ( ip , fmt , ap ) ;
2009-03-12 20:24:49 +03:00
va_end ( ap ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( __trace_printk ) ;
int __ftrace_vprintk ( unsigned long ip , const char * fmt , va_list ap )
{
if ( ! ( trace_flags & TRACE_ITER_PRINTK ) )
return 0 ;
2009-03-19 21:03:53 +03:00
return trace_vprintk ( ip , fmt , ap ) ;
2009-03-06 19:21:47 +03:00
}
2009-03-06 19:21:49 +03:00
EXPORT_SYMBOL_GPL ( __ftrace_vprintk ) ;
2009-03-06 19:21:47 +03:00
2009-03-12 21:23:17 +03:00
static void *
t_next ( struct seq_file * m , void * v , loff_t * pos )
{
const char * * fmt = m - > private ;
const char * * next = fmt ;
( * pos ) + + ;
if ( ( unsigned long ) fmt > = ( unsigned long ) __stop___trace_bprintk_fmt )
return NULL ;
next = fmt ;
m - > private = + + next ;
return fmt ;
}
static void * t_start ( struct seq_file * m , loff_t * pos )
{
return t_next ( m , NULL , pos ) ;
}
static int t_show ( struct seq_file * m , void * v )
{
const char * * fmt = v ;
const char * str = * fmt ;
int i ;
seq_printf ( m , " 0x%lx : \" " , ( unsigned long ) fmt ) ;
/*
* Tabs and new lines need to be converted .
*/
for ( i = 0 ; str [ i ] ; i + + ) {
switch ( str [ i ] ) {
case ' \n ' :
seq_puts ( m , " \\ n " ) ;
break ;
case ' \t ' :
seq_puts ( m , " \\ t " ) ;
break ;
case ' \\ ' :
seq_puts ( m , " \\ " ) ;
break ;
case ' " ' :
seq_puts ( m , " \\ \" " ) ;
break ;
default :
seq_putc ( m , str [ i ] ) ;
}
}
seq_puts ( m , " \" \n " ) ;
return 0 ;
}
static void t_stop ( struct seq_file * m , void * p )
{
}
static const struct seq_operations show_format_seq_ops = {
. start = t_start ,
. next = t_next ,
. show = t_show ,
. stop = t_stop ,
} ;
static int
ftrace_formats_open ( struct inode * inode , struct file * file )
{
int ret ;
ret = seq_open ( file , & show_format_seq_ops ) ;
if ( ! ret ) {
struct seq_file * m = file - > private_data ;
m - > private = __start___trace_bprintk_fmt ;
}
return ret ;
}
static const struct file_operations ftrace_formats_fops = {
. open = ftrace_formats_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = seq_release ,
} ;
static __init int init_trace_printk_function_export ( void )
{
struct dentry * d_tracer ;
struct dentry * entry ;
d_tracer = tracing_init_dentry ( ) ;
if ( ! d_tracer )
return 0 ;
entry = debugfs_create_file ( " printk_formats " , 0444 , d_tracer ,
NULL , & ftrace_formats_fops ) ;
if ( ! entry )
pr_warning ( " Could not create debugfs "
" 'printk_formats' entry \n " ) ;
return 0 ;
}
fs_initcall ( init_trace_printk_function_export ) ;
2009-03-06 19:21:49 +03:00
static __init int init_trace_printk ( void )
2009-03-06 19:21:47 +03:00
{
2009-03-06 19:21:49 +03:00
return register_module_notifier ( & module_trace_bprintk_format_nb ) ;
2009-03-06 19:21:47 +03:00
}
2009-03-06 19:21:49 +03:00
early_initcall ( init_trace_printk ) ;