2008-11-24 03:49:58 +03:00
/*
* ring buffer based C - state tracer
*
* Arjan van de Ven < arjan @ linux . intel . com >
* Copyright ( C ) 2008 Intel Corporation
*
* Much is borrowed from trace_boot . c which is
* Copyright ( C ) 2008 Frederic Weisbecker < fweisbec @ gmail . com >
*
*/
# include <linux/init.h>
# include <linux/debugfs.h>
2009-02-08 00:16:12 +03:00
# include <trace/power.h>
2008-11-24 03:49:58 +03:00
# include <linux/kallsyms.h>
# include <linux/module.h>
# include "trace.h"
2008-12-24 07:24:12 +03:00
# include "trace_output.h"
2008-11-24 03:49:58 +03:00
static struct trace_array * power_trace ;
static int __read_mostly trace_power_enabled ;
2009-02-11 21:57:25 +03:00
static void probe_power_start ( struct power_trace * it , unsigned int type ,
unsigned int level )
{
if ( ! trace_power_enabled )
return ;
memset ( it , 0 , sizeof ( struct power_trace ) ) ;
it - > state = level ;
it - > type = type ;
it - > stamp = ktime_get ( ) ;
}
static void probe_power_end ( struct power_trace * it )
{
struct ring_buffer_event * event ;
struct trace_power * entry ;
struct trace_array_cpu * data ;
struct trace_array * tr = power_trace ;
if ( ! trace_power_enabled )
return ;
preempt_disable ( ) ;
it - > end = ktime_get ( ) ;
data = tr - > data [ smp_processor_id ( ) ] ;
event = trace_buffer_lock_reserve ( tr , TRACE_POWER ,
sizeof ( * entry ) , 0 , 0 ) ;
if ( ! event )
goto out ;
entry = ring_buffer_event_data ( event ) ;
entry - > state_data = * it ;
trace_buffer_unlock_commit ( tr , event , 0 , 0 ) ;
out :
preempt_enable ( ) ;
}
static void probe_power_mark ( struct power_trace * it , unsigned int type ,
unsigned int level )
{
struct ring_buffer_event * event ;
struct trace_power * entry ;
struct trace_array_cpu * data ;
struct trace_array * tr = power_trace ;
if ( ! trace_power_enabled )
return ;
memset ( it , 0 , sizeof ( struct power_trace ) ) ;
it - > state = level ;
it - > type = type ;
it - > stamp = ktime_get ( ) ;
preempt_disable ( ) ;
it - > end = it - > stamp ;
data = tr - > data [ smp_processor_id ( ) ] ;
event = trace_buffer_lock_reserve ( tr , TRACE_POWER ,
sizeof ( * entry ) , 0 , 0 ) ;
if ( ! event )
goto out ;
entry = ring_buffer_event_data ( event ) ;
entry - > state_data = * it ;
trace_buffer_unlock_commit ( tr , event , 0 , 0 ) ;
out :
preempt_enable ( ) ;
}
static int tracing_power_register ( void )
{
int ret ;
ret = register_trace_power_start ( probe_power_start ) ;
if ( ret ) {
pr_info ( " power trace: Couldn't activate tracepoint "
" probe to trace_power_start \n " ) ;
return ret ;
}
ret = register_trace_power_end ( probe_power_end ) ;
if ( ret ) {
pr_info ( " power trace: Couldn't activate tracepoint "
" probe to trace_power_end \n " ) ;
goto fail_start ;
}
ret = register_trace_power_mark ( probe_power_mark ) ;
if ( ret ) {
pr_info ( " power trace: Couldn't activate tracepoint "
" probe to trace_power_mark \n " ) ;
goto fail_end ;
}
return ret ;
fail_end :
unregister_trace_power_end ( probe_power_end ) ;
fail_start :
unregister_trace_power_start ( probe_power_start ) ;
return ret ;
}
2008-11-24 03:49:58 +03:00
static void start_power_trace ( struct trace_array * tr )
{
trace_power_enabled = 1 ;
}
static void stop_power_trace ( struct trace_array * tr )
2009-03-18 03:58:00 +03:00
{
trace_power_enabled = 0 ;
}
static void power_trace_reset ( struct trace_array * tr )
2008-11-24 03:49:58 +03:00
{
trace_power_enabled = 0 ;
2009-02-11 21:57:25 +03:00
unregister_trace_power_start ( probe_power_start ) ;
unregister_trace_power_end ( probe_power_end ) ;
unregister_trace_power_mark ( probe_power_mark ) ;
2008-11-24 03:49:58 +03:00
}
static int power_trace_init ( struct trace_array * tr )
{
int cpu ;
power_trace = tr ;
trace_power_enabled = 1 ;
2009-02-11 21:57:25 +03:00
tracing_power_register ( ) ;
2008-11-24 03:49:58 +03:00
2009-01-01 02:42:23 +03:00
for_each_cpu ( cpu , cpu_possible_mask )
2008-11-24 03:49:58 +03:00
tracing_reset ( tr , cpu ) ;
return 0 ;
}
static enum print_line_t power_print_line ( struct trace_iterator * iter )
{
int ret = 0 ;
struct trace_entry * entry = iter - > ent ;
struct trace_power * field ;
struct power_trace * it ;
struct trace_seq * s = & iter - > seq ;
struct timespec stamp ;
struct timespec duration ;
trace_assign_type ( field , entry ) ;
it = & field - > state_data ;
stamp = ktime_to_timespec ( it - > stamp ) ;
duration = ktime_to_timespec ( ktime_sub ( it - > end , it - > stamp ) ) ;
if ( entry - > type = = TRACE_POWER ) {
if ( it - > type = = POWER_CSTATE )
ret = trace_seq_printf ( s , " [%5ld.%09ld] CSTATE: Going to C%i on cpu %i for %ld.%09ld \n " ,
stamp . tv_sec ,
stamp . tv_nsec ,
it - > state , iter - > cpu ,
duration . tv_sec ,
duration . tv_nsec ) ;
if ( it - > type = = POWER_PSTATE )
ret = trace_seq_printf ( s , " [%5ld.%09ld] PSTATE: Going to P%i on cpu %i \n " ,
stamp . tv_sec ,
stamp . tv_nsec ,
it - > state , iter - > cpu ) ;
if ( ! ret )
return TRACE_TYPE_PARTIAL_LINE ;
return TRACE_TYPE_HANDLED ;
}
return TRACE_TYPE_UNHANDLED ;
}
2009-04-13 11:23:53 +04:00
static void power_print_header ( struct seq_file * s )
{
seq_puts ( s , " # TIMESTAMP STATE EVENT \n " ) ;
seq_puts ( s , " # | | | \n " ) ;
}
2008-11-24 03:49:58 +03:00
static struct tracer power_tracer __read_mostly =
{
. name = " power " ,
. init = power_trace_init ,
. start = start_power_trace ,
. stop = stop_power_trace ,
2009-03-18 03:58:00 +03:00
. reset = power_trace_reset ,
2008-11-24 03:49:58 +03:00
. print_line = power_print_line ,
2009-04-13 11:23:53 +04:00
. print_header = power_print_header ,
2008-11-24 03:49:58 +03:00
} ;
static int init_power_trace ( void )
{
return register_tracer ( & power_tracer ) ;
}
device_initcall ( init_power_trace ) ;