2009-03-06 17:21:47 +01:00
/*
* trace binary printk
*
* Copyright ( C ) 2008 Lai Jiangshan < laijs @ cn . fujitsu . com >
*
*/
2009-03-12 14:23:17 -04:00
# include <linux/seq_file.h>
# include <linux/debugfs.h>
# include <linux/uaccess.h>
2009-03-06 17:21:47 +01:00
# include <linux/kernel.h>
# include <linux/ftrace.h>
# include <linux/string.h>
2009-03-12 14:23:17 -04:00
# include <linux/module.h>
# include <linux/mutex.h>
2009-03-06 17:21:47 +01:00
# include <linux/ctype.h>
# include <linux/list.h>
# include <linux/slab.h>
# include <linux/fs.h>
# include "trace.h"
2009-03-06 17:21:48 +01:00
# ifdef CONFIG_MODULES
/*
2009-03-06 17:21:49 +01:00
* modules trace_printk ( ) ' s formats are autosaved in struct trace_bprintk_fmt
2009-03-06 17:21:48 +01:00
* which are queued on trace_bprintk_fmt_list .
*/
static LIST_HEAD ( trace_bprintk_fmt_list ) ;
2009-03-06 17:21:49 +01:00
/* serialize accesses to trace_bprintk_fmt_list */
static DEFINE_MUTEX ( btrace_mutex ) ;
2009-03-06 17:21:48 +01:00
struct trace_bprintk_fmt {
struct list_head list ;
2011-03-21 22:59:21 -04:00
const char * fmt ;
2009-03-06 17:21:48 +01:00
} ;
static inline struct trace_bprintk_fmt * lookup_format ( const char * fmt )
2009-03-06 17:21:47 +01:00
{
2009-03-06 17:21:48 +01: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 17:21:47 +01:00
}
2009-03-06 17:21:48 +01:00
static
void hold_module_trace_bprintk_format ( const char * * start , const char * * end )
2009-03-06 17:21:47 +01:00
{
2009-03-06 17:21:48 +01:00
const char * * iter ;
2011-03-21 22:59:21 -04:00
char * fmt ;
2009-03-06 17:21:49 +01:00
mutex_lock ( & btrace_mutex ) ;
2009-03-06 17:21:48 +01:00
for ( iter = start ; iter < end ; iter + + ) {
struct trace_bprintk_fmt * tb_fmt = lookup_format ( * iter ) ;
if ( tb_fmt ) {
* iter = tb_fmt - > fmt ;
continue ;
}
2011-03-21 22:59:21 -04:00
tb_fmt = kmalloc ( sizeof ( * tb_fmt ) , GFP_KERNEL ) ;
if ( tb_fmt )
fmt = kmalloc ( strlen ( * iter ) + 1 , GFP_KERNEL ) ;
if ( tb_fmt & & fmt ) {
2009-03-06 17:21:48 +01:00
list_add_tail ( & tb_fmt - > list , & trace_bprintk_fmt_list ) ;
2011-03-21 22:59:21 -04:00
strcpy ( fmt , * iter ) ;
tb_fmt - > fmt = fmt ;
2009-03-06 17:21:48 +01:00
* iter = tb_fmt - > fmt ;
2011-03-21 22:59:21 -04:00
} else {
kfree ( tb_fmt ) ;
2009-03-06 17:21:48 +01:00
* iter = NULL ;
2011-03-21 22:59:21 -04:00
}
2009-03-06 17:21:48 +01:00
}
2009-03-06 17:21:49 +01:00
mutex_unlock ( & btrace_mutex ) ;
2009-03-06 17:21:47 +01:00
}
2009-03-06 17:21:48 +01: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 18:24:49 +01:00
int __trace_bprintk ( unsigned long ip , const char * fmt , . . . )
2009-03-06 17:21:49 +01:00
{
int ret ;
va_list ap ;
2009-03-06 17:21:47 +01:00
2009-03-06 17:21:49 +01:00
if ( unlikely ( ! fmt ) )
return 0 ;
2009-03-06 17:21:47 +01:00
2009-03-06 17:21:49 +01:00
if ( ! ( trace_flags & TRACE_ITER_PRINTK ) )
return 0 ;
va_start ( ap , fmt ) ;
2009-03-19 14:03:53 -04:00
ret = trace_vbprintk ( ip , fmt , ap ) ;
2009-03-06 17:21:49 +01:00
va_end ( ap ) ;
return ret ;
2009-03-06 17:21:47 +01:00
}
2009-03-12 18:24:49 +01:00
EXPORT_SYMBOL_GPL ( __trace_bprintk ) ;
2009-03-06 17:21:47 +01:00
2009-03-12 18:24:49 +01:00
int __ftrace_vbprintk ( unsigned long ip , const char * fmt , va_list ap )
2009-03-06 17:21:49 +01:00
{
if ( unlikely ( ! fmt ) )
return 0 ;
if ( ! ( trace_flags & TRACE_ITER_PRINTK ) )
return 0 ;
2009-03-19 14:03:53 -04:00
return trace_vbprintk ( ip , fmt , ap ) ;
2009-03-12 18:24:49 +01: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 14:03:53 -04:00
ret = trace_vprintk ( ip , fmt , ap ) ;
2009-03-12 18:24:49 +01: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 14:03:53 -04:00
return trace_vprintk ( ip , fmt , ap ) ;
2009-03-06 17:21:47 +01:00
}
2009-03-06 17:21:49 +01:00
EXPORT_SYMBOL_GPL ( __ftrace_vprintk ) ;
2009-03-06 17:21:47 +01:00
2009-03-12 14:23:17 -04:00
static void *
2009-06-24 09:52:58 +08:00
t_start ( struct seq_file * m , loff_t * pos )
2009-03-12 14:23:17 -04:00
{
2009-06-24 09:52:58 +08:00
const char * * fmt = __start___trace_bprintk_fmt + * pos ;
2009-03-12 14:23:17 -04:00
if ( ( unsigned long ) fmt > = ( unsigned long ) __stop___trace_bprintk_fmt )
return NULL ;
return fmt ;
}
2009-06-24 09:52:58 +08:00
static void * t_next ( struct seq_file * m , void * v , loff_t * pos )
2009-03-12 14:23:17 -04:00
{
2009-06-24 09:52:58 +08:00
( * pos ) + + ;
return t_start ( m , pos ) ;
2009-03-12 14:23:17 -04:00
}
static int t_show ( struct seq_file * m , void * v )
{
const char * * fmt = v ;
const char * str = * fmt ;
int i ;
2009-07-22 23:11:03 -04:00
seq_printf ( m , " 0x%lx : \" " , * ( unsigned long * ) fmt ) ;
2009-03-12 14:23:17 -04:00
/*
* 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 )
{
2009-06-24 09:52:58 +08:00
return seq_open ( file , & show_format_seq_ops ) ;
2009-03-12 14:23:17 -04:00
}
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 ;
d_tracer = tracing_init_dentry ( ) ;
if ( ! d_tracer )
return 0 ;
2009-03-27 00:25:38 +01:00
trace_create_file ( " printk_formats " , 0444 , d_tracer ,
2009-03-12 14:23:17 -04:00
NULL , & ftrace_formats_fops ) ;
return 0 ;
}
fs_initcall ( init_trace_printk_function_export ) ;
2009-03-06 17:21:49 +01:00
static __init int init_trace_printk ( void )
2009-03-06 17:21:47 +01:00
{
2009-03-06 17:21:49 +01:00
return register_module_notifier ( & module_trace_bprintk_format_nb ) ;
2009-03-06 17:21:47 +01:00
}
2009-03-06 17:21:49 +01:00
early_initcall ( init_trace_printk ) ;