ftrace: Add a C-state tracer to help power optimization
PATCH 1/1 ftrace: Add a C-state tracer to help power optimization
From: Arjan van de Ven <[email protected]>
Date: Fri, 3 Oct 2008 10:18:21 -0700
Subject: [PATCH] ftrace: Add a C-state tracer to help power optimization
This patch adds a C-state ftrace plugin that will generate detailed statistics about the C-states that are being used, so that we can look at detailed decisions that the C-state code is making, rather than the too high level "average" that we have today.
--- arch/x86/kernel/process.c | 9 +++ include/linux/ftrace.h | 13 +++++ kernel/trace/Kconfig | 11 ++++ kernel/trace/Makefile | 1 + kernel/trace/trace.h | 5 ++ kernel/trace/trace_cstate.c | 123 +++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 162 insertions(+), 0 deletions(-) create mode 100644 kernel/trace/trace_cstate.c
arch/x86/kernel/process.c +9 -0
@@ -7,6 +7,7 @@#include <linux/module.h> #include <linux/pm.h> #include <linux/clockchips.h> +#include <linux/ftrace.h> #include <asm/system.h> unsigned long idle_halt;
@@ -100,6 +101,8 @@ static inline int hlt_use_halt(void)void default_idle (void ) {if (hlt_use_halt ()) { +struct cstate_trace it; + it.stamp =ktime_get ();current_thread_info ()->status &= ~TS_POLLING ;/* * TS_POLLING-cleared state must be visible before we
@@ -112,6 +115,8 @@ void default_idle(void)else local_irq_enable ();current_thread_info ()->status |=TS_POLLING ; + it.end =ktime_get (); +trace_cstate (&it,1 ); }else {local_irq_enable ();/* loop is done by the caller */
@@ -154,12 +159,16 @@ EXPORT_SYMBOL_GPL(cpu_idle_wait);*/ void mwait_idle_with_hints (unsigned long ax,unsigned long cx) { +struct cstate_trace it; + it.stamp =ktime_get ();if (!need_resched ()) {__monitor ((void *)¤t_thread_info ()->flags,0 ,0 );smp_mb ();if (!need_resched ())__mwait (ax, cx); } + it.end =ktime_get (); +trace_cstate (&it, (ax>>4 )+1 ); }/* Default MONITOR/MWAIT with no hints, used for default C1 state */
include/linux/ftrace.h +13 -0
@@ -226,6 +226,19 @@ static inline void trace_boot(struct boot_trace *it) { }static inline void start_boot_trace (void ) { }#endif +struct cstate_trace { +ktime_t stamp; +ktime_t end; +int state; +int CPU; +}; + +#ifdef CONFIG_CSTATE_TRACER +extern void trace_cstate (struct cstate_trace *it,int state); +#else +static inline void trace_cstate (struct cstate_trace *it,int state) { } +#endif +#endif /* _LINUX_FTRACE_H */
kernel/trace/Kconfig +11 -0
@@ -134,6 +134,17 @@ config BOOT_TRACER be enabled if this tracer is selected since only one tracer should touch the tracing buffer at a time. +config CSTATE_TRACER + bool "Trace C-state behavior" + depends on HAVE_FTRACE + depends on DEBUG_KERNEL + depends on X86 + select TRACING + help + This tracer helps developers to analyize and optimize the kernels + power management decisions, specifically the C-state behavior. + + config STACK_TRACER bool "Trace max stack" depends on HAVE_FTRACE
kernel/trace/Makefile +1 -0
@@ -24,5 +24,6 @@ obj-$(CONFIG_NOP_TRACER) += trace_nop.o obj-$(CONFIG_STACK_TRACER) += trace_stack.o obj-$(CONFIG_MMIOTRACE) += trace_mmiotrace.o obj-$(CONFIG_BOOT_TRACER) += trace_boot.o +obj-$(CONFIG_CSTATE_TRACER) += trace_cstate.o libftrace-y := ftrace.o
kernel/trace/trace.h +5 -0
@@ -117,6 +117,11 @@ struct trace_boot {struct boot_trace initcall; }; +struct trace_cstate { +struct trace_entry ent; +struct cstate_trace state_data; +}; +/* * trace_flag_type is an enumeration that holds different * states when a trace occurs. These are:
kernel/trace/trace_cstate.c new file +123 -0
123 lines not on this page · raw message
Signature · 7 lines
1.5.5.1 -- Arjan van de Ven Intel Open Source Technology Centre For development, discussion and tips for power savings, visit http://www.lesswatts.org
PATCH 2/2 cstate ftrace userland script
and here's a script to turn the cstate ftrace output into a nice SVG that you can view with inkscape
#!/usr/bin/perl
# Copyright 2008, Intel Corporation
#
# This file is part of the Linux kernel
#
# This program file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the # Free Software Foundation; version 2 of the License. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # # You should have received a copy of the GNU General Public License # along with this program in a file named COPYING; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301 USA # # Authors: # Arjan van de Ven <[email protected]>
#
# This script turns a cstate ftrace output into a SVG graphic that shows
# historic C-state information # # # cat /sys/kernel/debug/tracer/trace | perl cstate.pl > out.svg #
my @rows;
my @styles;
my $base = 0;
$styles[0] = "fill:rgb(0,0,255);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)"; $styles[1] = "fill:rgb(0,255,0);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)"; $styles[2] = "fill:rgb(255,0,20);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)"; $styles[3] = "fill:rgb(255,255,20);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)"; $styles[4] = "fill:rgb(255,0,255);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)"; $styles[5] = "fill:rgb(0,255,255);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)"; $styles[6] = "fill:rgb(0,128,255);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)"; $styles[7] = "fill:rgb(0,255,128);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)"; $styles[8] = "fill:rgb(255,0,128);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)"; my $stylecounter = 0;
print "<?xml version=\"1.0\" standalone=\"no\"?> \n";
print "<svg width=\"10000\" height=\"100%\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">\n";
my $scale = 30000.0;
while (<>) {
my $line = $_;
if ($line =~ /([0-9\.]+)\] Going to C([0-9]) on cpu ([0-9]+) for ([0-9\.]+)/) {
# if ($line =~ /([0-9\.]+)\] Going to C([0-9]) /) {
if ($base == 0) {
$base = $1;
}
my $time = $1 - $base;
my $time = $time * $scale;
my $C = $2;
my $cpu = $3;
my $y = 200 * $cpu;
my $duration = $4 * $scale;
my $msec = int($4 * 100000)/100.0;
my $height = $C * 20;
$style = $styles[$C];
$y = $y + 140 - $height;
$x2 = $time + 4;
$y2 = $y + 4;
print "<rect x=\"$time\" width=\"$duration\" y=\"$y\" height=\"$height\" style=\"$style\"/>\n";
print "<text transform=\"translate($x2,$y2) rotate(90)\">C$C $msec</text>\n";
}
}print "</svg>\n";
Signature · 3 lines
Arjan van de Ven Intel Open Source Technology Centre For development, discussion and tips for power savings, visit http://www.lesswatts.org
Steven Rostedt
maintainerHi Arjan,
Very nice! I just have a few comments below.
kernel/trace/trace.h struct trace_boot { · line 124
Quoted 13 lines · Arjan van de Ven
kernel/trace/trace.h +5
-0@@ -117,6 +117,11 @@ struct trace_boot {struct boot_trace initcall; }; +struct trace_cstate { +struct trace_entry ent; +struct cstate_trace state_data; +}; +
Can you please register this in the trace_assign_type macro.
IF_ASSIGN(var, ent, struct trace_cstate, TRACE_BOOT); \
Quoted 8 lines
kernel/trace/trace.h +0
-0/* * trace_flag_type is an enumeration that holds different * states when a trace occurs. These are: kernel/trace/trace_cstate.c new file +0
-0
kernel/trace/trace_cstate.c line 57
Quoted 7 lines
kernel/trace/trace_cstate.c new file +5
-0+static enum print_line_t cstate_print_line (struct trace_iterator *iter) +{ +int ret; +struct trace_entry *entry = iter->ent; +struct trace_cstate *field = (struct trace_cstate *)entry;
And here, do not typecast. Use the trace_assign_type macro below.
kernel/trace/trace_cstate.c line 60
Quoted 3 lines
kernel/trace/trace_cstate.c new file +3
-0+struct cstate_trace *it = &field->state_data; +struct trace_seq *s = &iter->seq; +struct timespec stamp =ktime_to_timespec (it->stamp);
Also note that iter->ts holds a timestamp counter from bootup in nanosecs. It currently uses the sched_clock to record, but may change later to something a bit better.
kernel/trace/trace_cstate.c line 62
Quoted 2 lines
kernel/trace/trace_cstate.c new file +2
-0+struct timespec duration =ktime_to_timespec ( +ktime_sub (it->end, it->stamp));
trace_assign_type(field, entry);
The above will do a typecheck to force correct types.
But, you may not need to do any of this. See below.
Quoted 12 lines
kernel/trace/trace_cstate.c new file +12
-0+ +if (entry->type ==TRACE_BOOT ) { + ret =trace_seq_printf (s,"[%5ld.%09ld] Going to C%i on cpu %i for %ld.%09ld\n" , + stamp.tv_sec, + stamp.tv_nsec, + it->state, it->CPU, + duration.tv_sec, + duration.tv_nsec); +if (!ret) +return TRACE_TYPE_PARTIAL_LINE ; +return TRACE_TYPE_HANDLED ; + }
kernel/trace/trace_cstate.c line 84
Quoted 10 lines
kernel/trace/trace_cstate.c new file +10
-0+return TRACE_TYPE_UNHANDLED ; +} + +struct tracer cstate_tracer__read_mostly = +{ + .name ="cstate" , + .init = cstate_trace_init, + .reset = stop_cstate_trace, + .ctrl_update = cstate_trace_ctrl_update, + .print_line = cstate_print_line,
Here you bypass the print_line altogether. You really don't even need to make a trace_cstate. You do not use any of the data in the entry field so you can simply just store your cstate_trace data directly into the buffer. The entry is only used for ftrace formatted prints.
Quoted 12 lines
kernel/trace/trace_cstate.c new file +12
-0+}; + +static int init_cstate_trace (void ) +{ +return register_tracer (&cstate_tracer); +} +device_initcall (init_cstate_trace); + +void trace_cstate (struct cstate_trace *it,int level) +{ +struct ring_buffer_event *event; +struct trace_cstate *entry;
kernel/trace/trace_cstate.c line 106
Quoted 10 lines
kernel/trace/trace_cstate.c new file +10
-0+struct trace_array_cpu *data; +unsigned long irq_flags; +struct trace_array *tr = cstate_trace; + +if (!trace_cstate_enabled) +return ; + + it->state = level; +preempt_disable (); + it->CPU =smp_processor_id ();
You don't need to copy the smp_processor_id. Since all the buffers are per_cpu, the cpu id is passed into your print_line via the iter structure.
iter->cpu is the cpu that this entry was recorded on.
kernel/trace/trace_cstate.c line 107
Quoted 1 line
kernel/trace/trace_cstate.c new file +1
-0+ data = tr->data[smp_processor_id ()];
You don't need the above 'data' structure, unless you want to use it to prevent being reentrant. But you currently dont.
kernel/trace/trace_cstate.c line 115
Quoted 8 lines
kernel/trace/trace_cstate.c new file +8
-0+ + 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 ); + entry->ent.type =TRACE_BOOT ;
It doesn't look like you use any of the trace_entry structure. You can just store your normal structure, since you bypass the output with you print_line function.
struct cstate_trace *entry; [...] entry = ring_buffer_event_data(event); *entry = *it; ring_buffer_unlock_commit(tr->buffer, event, irq_flags);
-- Steve
Quoted 17 lines
kernel/trace/trace_cstate.c new file +8
-0+ entry->state_data = *it; +ring_buffer_unlock_commit (tr->buffer, event, irq_flags); + +trace_wake_up (); + + out: +preempt_enable (); +}Signature · 7 lines
1.5.5.1 -- Arjan van de Ven Intel Open Source Technology Centre For development, discussion and tips for power savings, visit http://www.lesswatts.org
Quoted 4 lines · On Fri, 3 Oct 2008 20:32:51 -0400 (EDT) Steven Rostedt <[email protected]>…
Hi Arjan,
Very nice! I just have a few comments below.
since this is my first tracer.... I would suggest having a "golden example" one that people can copy (it seems I copied a less than perfect one ;-)... maybe a trace_example.c ?
I'll look into your comments tomorrow when I'm more awake.
Signature · 3 lines
Arjan van de Ven Intel Open Source Technology Centre For development, discussion and tips for power savings, visit http://www.lesswatts.org
Steven Rostedt
maintainerQuoted 11 lines · Arjan van de Ven
Quoted 4 lines · On Fri, 3 Oct 2008 20:32:51 -0400 (EDT) Steven Rostedt <[email protected]>…
Hi Arjan,
Very nice! I just have a few comments below.
since this is my first tracer.... I would suggest having a "golden example" one that people can copy (it seems I copied a less than perfect one ;-)... maybe a
trace_example.c?
Yeah, that would make sense to have. People are starting to come up with so many different tracers, I'm not sure what a golden example would look like :-/
Some of my comments came about just noticing what you did and realized that there's other ways to do it. I didn't think about those other ways until I looked at your code ;-)
Quoted 2 lines
I'll look into your comments tomorrow when I'm more awake.
I'll comment more when I'm more awake. But that may not be until Monday. Putting in 16 hour days during the week, my wife puts her foot down and keeps me away from the computer during the weekend.
-- Steve
Quoted 11 lines · Steven Rostedt
Quoted 8 lines · Arjan van de Ven
Quoted 4 lines · On Fri, 3 Oct 2008 20:32:51 -0400 (EDT) Steven Rostedt <[email protected]>…
Hi Arjan,
Very nice! I just have a few comments below.
Quoted 7 lines
Quoted 3 lines
since this is my first tracer.... I would suggest having a "golden example" one that people can copy (it seems I copied a less than perfect one ;-)... maybe a
trace_example.c?Yeah, that would make sense to have. People are starting to come up with so many different tracers, I'm not sure what a golden example would look like :-/
i think starting with kernel/trace/trace_nop.c is the simplest: copy that into a new plugin and make it show up in make oldconfig and make it build. Then add ftrace_printk() lines initially - only specialize the trace entries to that plugin later on.
Ingo
Quoted 9 lines · On Fri, 3 Oct 2008 20:32:51 -0400 (EDT) Steven Rostedt <[email protected]>…
kernel/trace/trace.h struct trace_boot { · line 124
Quoted 5 lines
kernel/trace/trace.h +5
-0+struct trace_cstate { +struct trace_entry ent; +struct cstate_trace state_data; +}; +Can you please register this in the
trace_assign_typemacro.IF_ASSIGN(var, ent, struct trace_cstate, TRACE_BOOT); \
I don't see this anywhere in the kernel ;(
Quoted 1 line
And here, do not typecast. Use the
trace_assign_typemacro below.
nor that guy
Quoted 8 lines
kernel/trace/trace_cstate.c line 60
Quoted 4 lines
kernel/trace/trace_cstate.c new file +3
-0+struct cstate_trace *it = &field->state_data; +struct trace_seq *s = &iter->seq; +struct timespec stamp =ktime_to_timespec (it->stamp);Also note that
iter->tsholds a timestamp counter from bootup in nanosecs. It currently uses thesched_clockto record, but may change later to something a bit better.
given that some of these clocks stop during idle... I really do care about which timestamp is used, tracing idle with a clock that stops during idle won't work too well.
Signature · 3 lines
Arjan van de Ven Intel Open Source Technology Centre For development, discussion and tips for power savings, visit http://www.lesswatts.org
Steven Rostedt
maintainerQuoted 10 lines · Arjan van de Ven
Quoted 8 lines · On Fri, 3 Oct 2008 20:32:51 -0400 (EDT) Steven Rostedt <[email protected]>…
kernel/trace/trace.h struct trace_boot { · line 124
Quoted 5 lines
kernel/trace/trace.h +5
-0+struct trace_cstate { +struct trace_entry ent; +struct cstate_trace state_data; +}; +Can you please register this in the
trace_assign_typemacro.
Quoted 7 lines
Quoted 1 line
IF_ASSIGN(var, ent, struct trace_cstate, TRACE_BOOT); \I don't see this anywhere in the kernel ;(
Quoted 1 line
And here, do not typecast. Use the
trace_assign_typemacro below.nor that guy
The two should be in linux-tip. If you are based against mainline, then no they will not be.
If you are already using linux-tip, try refreshing.
Quoted 11 lines
Quoted 10 lines
kernel/trace/trace_cstate.c line 60
Quoted 6 lines
kernel/trace/trace_cstate.c new file +3
-0+struct cstate_trace *it = &field->state_data; +struct trace_seq *s = &iter->seq; +struct timespec stamp =ktime_to_timespec (it->stamp);Also note that
iter->tsholds a timestamp counter from bootup in nanosecs. It currently uses thesched_clockto record, but may change later to something a bit better.
Quoted 4 lines
given that some of these clocks stop during idle... I really do care about which timestamp is used, tracing idle with a clock that stops during idle won't work too well.
OK, I just wanted to let you know about it.
-- Steve