2008-09-23 14:32:08 +04:00
/*
* ring buffer based initcalls tracer
*
* Copyright ( C ) 2008 Frederic Weisbecker < fweisbec @ gmail . com >
*
*/
# include <linux/init.h>
# include <linux/debugfs.h>
# include <linux/ftrace.h>
2008-10-02 15:26:05 +04:00
# include <linux/kallsyms.h>
2008-09-23 14:32:08 +04:00
# include "trace.h"
2008-12-24 07:24:12 +03:00
# include "trace_output.h"
2008-09-23 14:32:08 +04:00
static struct trace_array * boot_trace ;
2008-10-31 14:57:20 +03:00
static bool pre_initcalls_finished ;
2008-09-23 14:32:08 +04:00
2008-10-31 14:57:20 +03:00
/* Tells the boot tracer that the pre_smp_initcalls are finished.
* So we are ready .
* It doesn ' t enable sched events tracing however .
* You have to call enable_boot_trace to do so .
*/
2008-09-23 14:32:08 +04:00
void start_boot_trace ( void )
{
2008-10-31 14:57:20 +03:00
pre_initcalls_finished = true ;
}
void enable_boot_trace ( void )
{
2009-02-03 05:38:33 +03:00
if ( boot_trace & & pre_initcalls_finished )
2008-11-08 06:36:02 +03:00
tracing_start_sched_switch_record ( ) ;
2008-09-23 14:32:08 +04:00
}
2008-10-31 14:57:20 +03:00
void disable_boot_trace ( void )
2008-09-23 14:32:08 +04:00
{
2009-02-03 05:38:33 +03:00
if ( boot_trace & & pre_initcalls_finished )
2008-11-08 06:36:02 +03:00
tracing_stop_sched_switch_record ( ) ;
2008-09-23 14:32:08 +04:00
}
2008-11-16 07:57:26 +03:00
static int boot_trace_init ( struct trace_array * tr )
2008-09-23 14:32:08 +04:00
{
int cpu ;
boot_trace = tr ;
2009-02-03 05:38:33 +03:00
if ( ! tr )
return 0 ;
2009-01-01 02:42:23 +03:00
for_each_cpu ( cpu , cpu_possible_mask )
2008-09-30 07:02:41 +04:00
tracing_reset ( tr , cpu ) ;
2008-10-31 15:20:08 +03:00
2008-11-08 06:36:02 +03:00
tracing_sched_switch_assign_trace ( tr ) ;
2008-11-16 07:57:26 +03:00
return 0 ;
2008-09-23 14:32:08 +04:00
}
2008-11-12 01:24:42 +03:00
static enum print_line_t
initcall_call_print_line ( struct trace_iterator * iter )
2008-09-23 14:32:08 +04:00
{
2008-11-12 01:24:42 +03:00
struct trace_entry * entry = iter - > ent ;
struct trace_seq * s = & iter - > seq ;
struct trace_boot_call * field ;
struct boot_trace_call * call ;
u64 ts ;
unsigned long nsec_rem ;
2008-09-29 22:31:58 +04:00
int ret ;
2008-11-12 01:24:42 +03:00
trace_assign_type ( field , entry ) ;
call = & field - > boot_call ;
ts = iter - > ts ;
nsec_rem = do_div ( ts , 1000000000 ) ;
ret = trace_seq_printf ( s , " [%5ld.%09ld] calling %s @ %i \n " ,
( unsigned long ) ts , nsec_rem , call - > func , call - > caller ) ;
if ( ! ret )
return TRACE_TYPE_PARTIAL_LINE ;
else
return TRACE_TYPE_HANDLED ;
}
static enum print_line_t
initcall_ret_print_line ( struct trace_iterator * iter )
{
2008-09-23 14:32:08 +04:00
struct trace_entry * entry = iter - > ent ;
struct trace_seq * s = & iter - > seq ;
2008-11-12 01:24:42 +03:00
struct trace_boot_ret * field ;
struct boot_trace_ret * init_ret ;
u64 ts ;
unsigned long nsec_rem ;
int ret ;
trace_assign_type ( field , entry ) ;
init_ret = & field - > boot_ret ;
ts = iter - > ts ;
nsec_rem = do_div ( ts , 1000000000 ) ;
ret = trace_seq_printf ( s , " [%5ld.%09ld] initcall %s "
" returned %d after %llu msecs \n " ,
( unsigned long ) ts ,
nsec_rem ,
init_ret - > func , init_ret - > result , init_ret - > duration ) ;
if ( ! ret )
return TRACE_TYPE_PARTIAL_LINE ;
else
2008-10-02 14:59:20 +04:00
return TRACE_TYPE_HANDLED ;
2008-11-12 01:24:42 +03:00
}
static enum print_line_t initcall_print_line ( struct trace_iterator * iter )
{
struct trace_entry * entry = iter - > ent ;
switch ( entry - > type ) {
case TRACE_BOOT_CALL :
return initcall_call_print_line ( iter ) ;
case TRACE_BOOT_RET :
return initcall_ret_print_line ( iter ) ;
default :
return TRACE_TYPE_UNHANDLED ;
2008-09-29 22:31:58 +04:00
}
2008-09-23 14:32:08 +04:00
}
struct tracer boot_tracer __read_mostly =
{
. name = " initcall " ,
. init = boot_trace_init ,
2008-12-19 13:08:39 +03:00
. reset = tracing_reset_online_cpus ,
2008-09-23 14:32:08 +04:00
. print_line = initcall_print_line ,
} ;
2008-11-12 01:24:42 +03:00
void trace_boot_call ( struct boot_trace_call * bt , initcall_t fn )
2008-09-23 14:32:08 +04:00
{
2008-09-30 07:02:41 +04:00
struct ring_buffer_event * event ;
2008-11-12 01:24:42 +03:00
struct trace_boot_call * entry ;
2008-09-23 14:32:08 +04:00
unsigned long irq_flags ;
struct trace_array * tr = boot_trace ;
2009-02-03 05:38:33 +03:00
if ( ! tr | | ! pre_initcalls_finished )
2008-09-23 14:32:08 +04:00
return ;
2008-10-02 15:26:05 +04:00
/* Get its name now since this function could
* disappear because it is in the . init section .
*/
2008-11-12 01:24:42 +03:00
sprint_symbol ( bt - > func , ( unsigned long ) fn ) ;
preempt_disable ( ) ;
event = ring_buffer_lock_reserve ( tr - > buffer , sizeof ( * entry ) ,
& irq_flags ) ;
if ( ! event )
goto out ;
entry = ring_buffer_event_data ( event ) ;
tracing_generic_entry_update ( & entry - > ent , 0 , 0 ) ;
entry - > ent . type = TRACE_BOOT_CALL ;
entry - > boot_call = * bt ;
ring_buffer_unlock_commit ( tr - > buffer , event , irq_flags ) ;
trace_wake_up ( ) ;
out :
preempt_enable ( ) ;
}
void trace_boot_ret ( struct boot_trace_ret * bt , initcall_t fn )
{
struct ring_buffer_event * event ;
struct trace_boot_ret * entry ;
unsigned long irq_flags ;
struct trace_array * tr = boot_trace ;
2009-02-03 05:38:33 +03:00
if ( ! tr | | ! pre_initcalls_finished )
2008-11-12 01:24:42 +03:00
return ;
sprint_symbol ( bt - > func , ( unsigned long ) fn ) ;
2008-09-23 14:32:08 +04:00
preempt_disable ( ) ;
2008-09-30 07:02:41 +04:00
event = ring_buffer_lock_reserve ( tr - > buffer , sizeof ( * entry ) ,
& irq_flags ) ;
if ( ! event )
goto out ;
entry = ring_buffer_event_data ( event ) ;
2008-10-01 21:14:09 +04:00
tracing_generic_entry_update ( & entry - > ent , 0 , 0 ) ;
2008-11-12 01:24:42 +03:00
entry - > ent . type = TRACE_BOOT_RET ;
entry - > boot_ret = * bt ;
2008-09-30 07:02:41 +04:00
ring_buffer_unlock_commit ( tr - > buffer , event , irq_flags ) ;
2008-09-23 14:32:08 +04:00
trace_wake_up ( ) ;
2008-09-30 07:02:41 +04:00
out :
2008-09-23 14:32:08 +04:00
preempt_enable ( ) ;
}