Skip to contentSkip to conversation

ftrace: Add a C-state tracer to help power optimization

PATCH Superseded Arjan van de Ven 3 participants 8 messages Oct 3, 2008 – Oct 4, 2008

Subsystems (3)

v1 of 2 Superseded Latest is v2 →

lore
Share & tools
lore
#0 lore raw

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
index 3468131..68c7234 100644
@@ -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 *)&current_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
index 91954eb..e6b4da6 100644
@@ -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
index 396aea1..fa2347a 100644
@@ -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
index a85dfba..2b85724 100644
@@ -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
index a921ba5..1ef1ded 100644
@@ -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
#1 lore raw

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
maintainer
#2 lore raw
In reply to Arjan van de Ven

Hi 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
index a921ba5..1ef1ded 100644
@@ -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
new file mode 100644
index 0000000..fcd4e6e

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
#3 lore raw
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
maintainer
#4 lore raw
Quoted 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

#5 lore raw
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

#6 lore raw
In reply to Steven Rostedt
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_type macro.

	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_type macro 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->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.

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
maintainer
#7 lore raw
Quoted 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_type macro.

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_type macro 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->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.

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