diff options
author | Arjan van de Ven <arjan@infradead.org> | 2008-11-23 19:49:58 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-11-26 02:29:32 -0500 |
commit | f3f47a6768a29448866da4422b6f6bee485c947f (patch) | |
tree | ba4bf1b79cbd13412871eec50250294d7140fd09 | |
parent | 509dceef6470442d8c7b8a43ec34125205840b3c (diff) |
tracing: add "power-tracer": C/P state tracer to help power optimization
Impact: new "power-tracer" ftrace plugin
This patch adds a C/P-state ftrace plugin that will generate
detailed statistics about the C/P-states that are being used,
so that we can look at detailed decisions that the C/P-state
code is making, rather than the too high level "average"
that we have today.
An example way of using this is:
mount -t debugfs none /sys/kernel/debug
echo cstate > /sys/kernel/debug/tracing/current_tracer
echo 1 > /sys/kernel/debug/tracing/tracing_enabled
sleep 1
echo 0 > /sys/kernel/debug/tracing/tracing_enabled
cat /sys/kernel/debug/tracing/trace | perl scripts/trace/cstate.pl > out.svg
Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
-rw-r--r-- | arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c | 4 | ||||
-rw-r--r-- | arch/x86/kernel/process.c | 16 | ||||
-rw-r--r-- | include/linux/ftrace.h | 29 | ||||
-rw-r--r-- | kernel/trace/Kconfig | 11 | ||||
-rw-r--r-- | kernel/trace/Makefile | 1 | ||||
-rw-r--r-- | kernel/trace/trace.h | 7 | ||||
-rw-r--r-- | kernel/trace/trace_power.c | 179 | ||||
-rw-r--r-- | scripts/trace/power.pl | 108 |
8 files changed, 355 insertions, 0 deletions
diff --git a/arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c b/arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c index 8e48c5d4467..88ea02dcb62 100644 --- a/arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c +++ b/arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c | |||
@@ -33,6 +33,7 @@ | |||
33 | #include <linux/cpufreq.h> | 33 | #include <linux/cpufreq.h> |
34 | #include <linux/compiler.h> | 34 | #include <linux/compiler.h> |
35 | #include <linux/dmi.h> | 35 | #include <linux/dmi.h> |
36 | #include <linux/ftrace.h> | ||
36 | 37 | ||
37 | #include <linux/acpi.h> | 38 | #include <linux/acpi.h> |
38 | #include <acpi/processor.h> | 39 | #include <acpi/processor.h> |
@@ -391,6 +392,7 @@ static int acpi_cpufreq_target(struct cpufreq_policy *policy, | |||
391 | unsigned int next_perf_state = 0; /* Index into perf table */ | 392 | unsigned int next_perf_state = 0; /* Index into perf table */ |
392 | unsigned int i; | 393 | unsigned int i; |
393 | int result = 0; | 394 | int result = 0; |
395 | struct power_trace it; | ||
394 | 396 | ||
395 | dprintk("acpi_cpufreq_target %d (%d)\n", target_freq, policy->cpu); | 397 | dprintk("acpi_cpufreq_target %d (%d)\n", target_freq, policy->cpu); |
396 | 398 | ||
@@ -427,6 +429,8 @@ static int acpi_cpufreq_target(struct cpufreq_policy *policy, | |||
427 | } | 429 | } |
428 | } | 430 | } |
429 | 431 | ||
432 | trace_power_mark(&it, POWER_PSTATE, next_perf_state); | ||
433 | |||
430 | switch (data->cpu_feature) { | 434 | switch (data->cpu_feature) { |
431 | case SYSTEM_INTEL_MSR_CAPABLE: | 435 | case SYSTEM_INTEL_MSR_CAPABLE: |
432 | cmd.type = SYSTEM_INTEL_MSR_CAPABLE; | 436 | cmd.type = SYSTEM_INTEL_MSR_CAPABLE; |
diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c index c622772744d..c27af49a4ed 100644 --- a/arch/x86/kernel/process.c +++ b/arch/x86/kernel/process.c | |||
@@ -7,6 +7,7 @@ | |||
7 | #include <linux/module.h> | 7 | #include <linux/module.h> |
8 | #include <linux/pm.h> | 8 | #include <linux/pm.h> |
9 | #include <linux/clockchips.h> | 9 | #include <linux/clockchips.h> |
10 | #include <linux/ftrace.h> | ||
10 | #include <asm/system.h> | 11 | #include <asm/system.h> |
11 | 12 | ||
12 | unsigned long idle_halt; | 13 | unsigned long idle_halt; |
@@ -100,6 +101,9 @@ static inline int hlt_use_halt(void) | |||
100 | void default_idle(void) | 101 | void default_idle(void) |
101 | { | 102 | { |
102 | if (hlt_use_halt()) { | 103 | if (hlt_use_halt()) { |
104 | struct power_trace it; | ||
105 | |||
106 | trace_power_start(&it, POWER_CSTATE, 1); | ||
103 | current_thread_info()->status &= ~TS_POLLING; | 107 | current_thread_info()->status &= ~TS_POLLING; |
104 | /* | 108 | /* |
105 | * TS_POLLING-cleared state must be visible before we | 109 | * TS_POLLING-cleared state must be visible before we |
@@ -112,6 +116,7 @@ void default_idle(void) | |||
112 | else | 116 | else |
113 | local_irq_enable(); | 117 | local_irq_enable(); |
114 | current_thread_info()->status |= TS_POLLING; | 118 | current_thread_info()->status |= TS_POLLING; |
119 | trace_power_end(&it); | ||
115 | } else { | 120 | } else { |
116 | local_irq_enable(); | 121 | local_irq_enable(); |
117 | /* loop is done by the caller */ | 122 | /* loop is done by the caller */ |
@@ -154,24 +159,31 @@ EXPORT_SYMBOL_GPL(cpu_idle_wait); | |||
154 | */ | 159 | */ |
155 | void mwait_idle_with_hints(unsigned long ax, unsigned long cx) | 160 | void mwait_idle_with_hints(unsigned long ax, unsigned long cx) |
156 | { | 161 | { |
162 | struct power_trace it; | ||
163 | |||
164 | trace_power_start(&it, POWER_CSTATE, (ax>>4)+1); | ||
157 | if (!need_resched()) { | 165 | if (!need_resched()) { |
158 | __monitor((void *)¤t_thread_info()->flags, 0, 0); | 166 | __monitor((void *)¤t_thread_info()->flags, 0, 0); |
159 | smp_mb(); | 167 | smp_mb(); |
160 | if (!need_resched()) | 168 | if (!need_resched()) |
161 | __mwait(ax, cx); | 169 | __mwait(ax, cx); |
162 | } | 170 | } |
171 | trace_power_end(&it); | ||
163 | } | 172 | } |
164 | 173 | ||
165 | /* Default MONITOR/MWAIT with no hints, used for default C1 state */ | 174 | /* Default MONITOR/MWAIT with no hints, used for default C1 state */ |
166 | static void mwait_idle(void) | 175 | static void mwait_idle(void) |
167 | { | 176 | { |
177 | struct power_trace it; | ||
168 | if (!need_resched()) { | 178 | if (!need_resched()) { |
179 | trace_power_start(&it, POWER_CSTATE, 1); | ||
169 | __monitor((void *)¤t_thread_info()->flags, 0, 0); | 180 | __monitor((void *)¤t_thread_info()->flags, 0, 0); |
170 | smp_mb(); | 181 | smp_mb(); |
171 | if (!need_resched()) | 182 | if (!need_resched()) |
172 | __sti_mwait(0, 0); | 183 | __sti_mwait(0, 0); |
173 | else | 184 | else |
174 | local_irq_enable(); | 185 | local_irq_enable(); |
186 | trace_power_end(&it); | ||
175 | } else | 187 | } else |
176 | local_irq_enable(); | 188 | local_irq_enable(); |
177 | } | 189 | } |
@@ -183,9 +195,13 @@ static void mwait_idle(void) | |||
183 | */ | 195 | */ |
184 | static void poll_idle(void) | 196 | static void poll_idle(void) |
185 | { | 197 | { |
198 | struct power_trace it; | ||
199 | |||
200 | trace_power_start(&it, POWER_CSTATE, 0); | ||
186 | local_irq_enable(); | 201 | local_irq_enable(); |
187 | while (!need_resched()) | 202 | while (!need_resched()) |
188 | cpu_relax(); | 203 | cpu_relax(); |
204 | trace_power_end(&it); | ||
189 | } | 205 | } |
190 | 206 | ||
191 | /* | 207 | /* |
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 7854d87b97b..0df28866620 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h | |||
@@ -311,6 +311,35 @@ ftrace_init_module(struct module *mod, | |||
311 | unsigned long *start, unsigned long *end) { } | 311 | unsigned long *start, unsigned long *end) { } |
312 | #endif | 312 | #endif |
313 | 313 | ||
314 | enum { | ||
315 | POWER_NONE = 0, | ||
316 | POWER_CSTATE = 1, | ||
317 | POWER_PSTATE = 2, | ||
318 | }; | ||
319 | |||
320 | struct power_trace { | ||
321 | #ifdef CONFIG_POWER_TRACER | ||
322 | ktime_t stamp; | ||
323 | ktime_t end; | ||
324 | int type; | ||
325 | int state; | ||
326 | #endif | ||
327 | }; | ||
328 | |||
329 | #ifdef CONFIG_POWER_TRACER | ||
330 | extern void trace_power_start(struct power_trace *it, unsigned int type, | ||
331 | unsigned int state); | ||
332 | extern void trace_power_mark(struct power_trace *it, unsigned int type, | ||
333 | unsigned int state); | ||
334 | extern void trace_power_end(struct power_trace *it); | ||
335 | #else | ||
336 | static inline void trace_power_start(struct power_trace *it, unsigned int type, | ||
337 | unsigned int state) { } | ||
338 | static inline void trace_power_mark(struct power_trace *it, unsigned int type, | ||
339 | unsigned int state) { } | ||
340 | static inline void trace_power_end(struct power_trace *it) { } | ||
341 | #endif | ||
342 | |||
314 | 343 | ||
315 | /* | 344 | /* |
316 | * Structure that defines a return function trace. | 345 | * Structure that defines a return function trace. |
diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig index 620feadff67..d151aab48ed 100644 --- a/kernel/trace/Kconfig +++ b/kernel/trace/Kconfig | |||
@@ -217,6 +217,17 @@ config BRANCH_TRACER | |||
217 | 217 | ||
218 | Say N if unsure. | 218 | Say N if unsure. |
219 | 219 | ||
220 | config POWER_TRACER | ||
221 | bool "Trace power consumption behavior" | ||
222 | depends on DEBUG_KERNEL | ||
223 | depends on X86 | ||
224 | select TRACING | ||
225 | help | ||
226 | This tracer helps developers to analyze and optimize the kernels | ||
227 | power management decisions, specifically the C-state and P-state | ||
228 | behavior. | ||
229 | |||
230 | |||
220 | config STACK_TRACER | 231 | config STACK_TRACER |
221 | bool "Trace max stack" | 232 | bool "Trace max stack" |
222 | depends on HAVE_FUNCTION_TRACER | 233 | depends on HAVE_FUNCTION_TRACER |
diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile index cef4bcb4e82..acaa06553ec 100644 --- a/kernel/trace/Makefile +++ b/kernel/trace/Makefile | |||
@@ -32,5 +32,6 @@ obj-$(CONFIG_BOOT_TRACER) += trace_boot.o | |||
32 | obj-$(CONFIG_FUNCTION_RET_TRACER) += trace_functions_return.o | 32 | obj-$(CONFIG_FUNCTION_RET_TRACER) += trace_functions_return.o |
33 | obj-$(CONFIG_TRACE_BRANCH_PROFILING) += trace_branch.o | 33 | obj-$(CONFIG_TRACE_BRANCH_PROFILING) += trace_branch.o |
34 | obj-$(CONFIG_BTS_TRACER) += trace_bts.o | 34 | obj-$(CONFIG_BTS_TRACER) += trace_bts.o |
35 | obj-$(CONFIG_POWER_TRACER) += trace_power.o | ||
35 | 36 | ||
36 | libftrace-y := ftrace.o | 37 | libftrace-y := ftrace.o |
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 3abd645e8af..4c453778a6a 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h | |||
@@ -28,6 +28,7 @@ enum trace_type { | |||
28 | TRACE_FN_RET, | 28 | TRACE_FN_RET, |
29 | TRACE_USER_STACK, | 29 | TRACE_USER_STACK, |
30 | TRACE_BTS, | 30 | TRACE_BTS, |
31 | TRACE_POWER, | ||
31 | 32 | ||
32 | __TRACE_LAST_TYPE | 33 | __TRACE_LAST_TYPE |
33 | }; | 34 | }; |
@@ -160,6 +161,11 @@ struct bts_entry { | |||
160 | unsigned long to; | 161 | unsigned long to; |
161 | }; | 162 | }; |
162 | 163 | ||
164 | struct trace_power { | ||
165 | struct trace_entry ent; | ||
166 | struct power_trace state_data; | ||
167 | }; | ||
168 | |||
163 | /* | 169 | /* |
164 | * trace_flag_type is an enumeration that holds different | 170 | * trace_flag_type is an enumeration that holds different |
165 | * states when a trace occurs. These are: | 171 | * states when a trace occurs. These are: |
@@ -266,6 +272,7 @@ extern void __ftrace_bad_type(void); | |||
266 | IF_ASSIGN(var, ent, struct trace_branch, TRACE_BRANCH); \ | 272 | IF_ASSIGN(var, ent, struct trace_branch, TRACE_BRANCH); \ |
267 | IF_ASSIGN(var, ent, struct ftrace_ret_entry, TRACE_FN_RET);\ | 273 | IF_ASSIGN(var, ent, struct ftrace_ret_entry, TRACE_FN_RET);\ |
268 | IF_ASSIGN(var, ent, struct bts_entry, TRACE_BTS);\ | 274 | IF_ASSIGN(var, ent, struct bts_entry, TRACE_BTS);\ |
275 | IF_ASSIGN(var, ent, struct trace_power, TRACE_POWER); \ | ||
269 | __ftrace_bad_type(); \ | 276 | __ftrace_bad_type(); \ |
270 | } while (0) | 277 | } while (0) |
271 | 278 | ||
diff --git a/kernel/trace/trace_power.c b/kernel/trace/trace_power.c new file mode 100644 index 00000000000..a7172a352f6 --- /dev/null +++ b/kernel/trace/trace_power.c | |||
@@ -0,0 +1,179 @@ | |||
1 | /* | ||
2 | * ring buffer based C-state tracer | ||
3 | * | ||
4 | * Arjan van de Ven <arjan@linux.intel.com> | ||
5 | * Copyright (C) 2008 Intel Corporation | ||
6 | * | ||
7 | * Much is borrowed from trace_boot.c which is | ||
8 | * Copyright (C) 2008 Frederic Weisbecker <fweisbec@gmail.com> | ||
9 | * | ||
10 | */ | ||
11 | |||
12 | #include <linux/init.h> | ||
13 | #include <linux/debugfs.h> | ||
14 | #include <linux/ftrace.h> | ||
15 | #include <linux/kallsyms.h> | ||
16 | #include <linux/module.h> | ||
17 | |||
18 | #include "trace.h" | ||
19 | |||
20 | static struct trace_array *power_trace; | ||
21 | static int __read_mostly trace_power_enabled; | ||
22 | |||
23 | |||
24 | static void start_power_trace(struct trace_array *tr) | ||
25 | { | ||
26 | trace_power_enabled = 1; | ||
27 | } | ||
28 | |||
29 | static void stop_power_trace(struct trace_array *tr) | ||
30 | { | ||
31 | trace_power_enabled = 0; | ||
32 | } | ||
33 | |||
34 | |||
35 | static int power_trace_init(struct trace_array *tr) | ||
36 | { | ||
37 | int cpu; | ||
38 | power_trace = tr; | ||
39 | |||
40 | trace_power_enabled = 1; | ||
41 | |||
42 | for_each_cpu_mask(cpu, cpu_possible_map) | ||
43 | tracing_reset(tr, cpu); | ||
44 | return 0; | ||
45 | } | ||
46 | |||
47 | static enum print_line_t power_print_line(struct trace_iterator *iter) | ||
48 | { | ||
49 | int ret = 0; | ||
50 | struct trace_entry *entry = iter->ent; | ||
51 | struct trace_power *field ; | ||
52 | struct power_trace *it; | ||
53 | struct trace_seq *s = &iter->seq; | ||
54 | struct timespec stamp; | ||
55 | struct timespec duration; | ||
56 | |||
57 | trace_assign_type(field, entry); | ||
58 | it = &field->state_data; | ||
59 | stamp = ktime_to_timespec(it->stamp); | ||
60 | duration = ktime_to_timespec(ktime_sub(it->end, it->stamp)); | ||
61 | |||
62 | if (entry->type == TRACE_POWER) { | ||
63 | if (it->type == POWER_CSTATE) | ||
64 | ret = trace_seq_printf(s, "[%5ld.%09ld] CSTATE: Going to C%i on cpu %i for %ld.%09ld\n", | ||
65 | stamp.tv_sec, | ||
66 | stamp.tv_nsec, | ||
67 | it->state, iter->cpu, | ||
68 | duration.tv_sec, | ||
69 | duration.tv_nsec); | ||
70 | if (it->type == POWER_PSTATE) | ||
71 | ret = trace_seq_printf(s, "[%5ld.%09ld] PSTATE: Going to P%i on cpu %i\n", | ||
72 | stamp.tv_sec, | ||
73 | stamp.tv_nsec, | ||
74 | it->state, iter->cpu); | ||
75 | if (!ret) | ||
76 | return TRACE_TYPE_PARTIAL_LINE; | ||
77 | return TRACE_TYPE_HANDLED; | ||
78 | } | ||
79 | return TRACE_TYPE_UNHANDLED; | ||
80 | } | ||
81 | |||
82 | static struct tracer power_tracer __read_mostly = | ||
83 | { | ||
84 | .name = "power", | ||
85 | .init = power_trace_init, | ||
86 | .start = start_power_trace, | ||
87 | .stop = stop_power_trace, | ||
88 | .reset = stop_power_trace, | ||
89 | .print_line = power_print_line, | ||
90 | }; | ||
91 | |||
92 | static int init_power_trace(void) | ||
93 | { | ||
94 | return register_tracer(&power_tracer); | ||
95 | } | ||
96 | device_initcall(init_power_trace); | ||
97 | |||
98 | void trace_power_start(struct power_trace *it, unsigned int type, | ||
99 | unsigned int level) | ||
100 | { | ||
101 | if (!trace_power_enabled) | ||
102 | return; | ||
103 | |||
104 | memset(it, 0, sizeof(struct power_trace)); | ||
105 | it->state = level; | ||
106 | it->type = type; | ||
107 | it->stamp = ktime_get(); | ||
108 | } | ||
109 | EXPORT_SYMBOL_GPL(trace_power_start); | ||
110 | |||
111 | |||
112 | void trace_power_end(struct power_trace *it) | ||
113 | { | ||
114 | struct ring_buffer_event *event; | ||
115 | struct trace_power *entry; | ||
116 | struct trace_array_cpu *data; | ||
117 | unsigned long irq_flags; | ||
118 | struct trace_array *tr = power_trace; | ||
119 | |||
120 | if (!trace_power_enabled) | ||
121 | return; | ||
122 | |||
123 | preempt_disable(); | ||
124 | it->end = ktime_get(); | ||
125 | data = tr->data[smp_processor_id()]; | ||
126 | |||
127 | event = ring_buffer_lock_reserve(tr->buffer, sizeof(*entry), | ||
128 | &irq_flags); | ||
129 | if (!event) | ||
130 | goto out; | ||
131 | entry = ring_buffer_event_data(event); | ||
132 | tracing_generic_entry_update(&entry->ent, 0, 0); | ||
133 | entry->ent.type = TRACE_POWER; | ||
134 | entry->state_data = *it; | ||
135 | ring_buffer_unlock_commit(tr->buffer, event, irq_flags); | ||
136 | |||
137 | trace_wake_up(); | ||
138 | |||
139 | out: | ||
140 | preempt_enable(); | ||
141 | } | ||
142 | EXPORT_SYMBOL_GPL(trace_power_end); | ||
143 | |||
144 | void trace_power_mark(struct power_trace *it, unsigned int type, | ||
145 | unsigned int level) | ||
146 | { | ||
147 | struct ring_buffer_event *event; | ||
148 | struct trace_power *entry; | ||
149 | struct trace_array_cpu *data; | ||
150 | unsigned long irq_flags; | ||
151 | struct trace_array *tr = power_trace; | ||
152 | |||
153 | if (!trace_power_enabled) | ||
154 | return; | ||
155 | |||
156 | memset(it, 0, sizeof(struct power_trace)); | ||
157 | it->state = level; | ||
158 | it->type = type; | ||
159 | it->stamp = ktime_get(); | ||
160 | preempt_disable(); | ||
161 | it->end = it->stamp; | ||
162 | data = tr->data[smp_processor_id()]; | ||
163 | |||
164 | event = ring_buffer_lock_reserve(tr->buffer, sizeof(*entry), | ||
165 | &irq_flags); | ||
166 | if (!event) | ||
167 | goto out; | ||
168 | entry = ring_buffer_event_data(event); | ||
169 | tracing_generic_entry_update(&entry->ent, 0, 0); | ||
170 | entry->ent.type = TRACE_POWER; | ||
171 | entry->state_data = *it; | ||
172 | ring_buffer_unlock_commit(tr->buffer, event, irq_flags); | ||
173 | |||
174 | trace_wake_up(); | ||
175 | |||
176 | out: | ||
177 | preempt_enable(); | ||
178 | } | ||
179 | EXPORT_SYMBOL_GPL(trace_power_mark); | ||
diff --git a/scripts/trace/power.pl b/scripts/trace/power.pl new file mode 100644 index 00000000000..4f729b3501e --- /dev/null +++ b/scripts/trace/power.pl | |||
@@ -0,0 +1,108 @@ | |||
1 | #!/usr/bin/perl | ||
2 | |||
3 | # Copyright 2008, Intel Corporation | ||
4 | # | ||
5 | # This file is part of the Linux kernel | ||
6 | # | ||
7 | # This program file is free software; you can redistribute it and/or modify it | ||
8 | # under the terms of the GNU General Public License as published by the | ||
9 | # Free Software Foundation; version 2 of the License. | ||
10 | # | ||
11 | # This program is distributed in the hope that it will be useful, but WITHOUT | ||
12 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
13 | # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
14 | # for more details. | ||
15 | # | ||
16 | # You should have received a copy of the GNU General Public License | ||
17 | # along with this program in a file named COPYING; if not, write to the | ||
18 | # Free Software Foundation, Inc., | ||
19 | # 51 Franklin Street, Fifth Floor, | ||
20 | # Boston, MA 02110-1301 USA | ||
21 | # | ||
22 | # Authors: | ||
23 | # Arjan van de Ven <arjan@linux.intel.com> | ||
24 | |||
25 | |||
26 | # | ||
27 | # This script turns a cstate ftrace output into a SVG graphic that shows | ||
28 | # historic C-state information | ||
29 | # | ||
30 | # | ||
31 | # cat /sys/kernel/debug/tracing/trace | perl power.pl > out.svg | ||
32 | # | ||
33 | |||
34 | my @styles; | ||
35 | my $base = 0; | ||
36 | |||
37 | my @pstate_last; | ||
38 | my @pstate_level; | ||
39 | |||
40 | $styles[0] = "fill:rgb(0,0,255);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)"; | ||
41 | $styles[1] = "fill:rgb(0,255,0);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)"; | ||
42 | $styles[2] = "fill:rgb(255,0,20);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)"; | ||
43 | $styles[3] = "fill:rgb(255,255,20);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)"; | ||
44 | $styles[4] = "fill:rgb(255,0,255);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)"; | ||
45 | $styles[5] = "fill:rgb(0,255,255);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)"; | ||
46 | $styles[6] = "fill:rgb(0,128,255);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)"; | ||
47 | $styles[7] = "fill:rgb(0,255,128);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)"; | ||
48 | $styles[8] = "fill:rgb(0,25,20);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)"; | ||
49 | |||
50 | |||
51 | print "<?xml version=\"1.0\" standalone=\"no\"?> \n"; | ||
52 | print "<svg width=\"10000\" height=\"100%\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">\n"; | ||
53 | |||
54 | my $scale = 30000.0; | ||
55 | while (<>) { | ||
56 | my $line = $_; | ||
57 | if ($line =~ /([0-9\.]+)\] CSTATE: Going to C([0-9]) on cpu ([0-9]+) for ([0-9\.]+)/) { | ||
58 | if ($base == 0) { | ||
59 | $base = $1; | ||
60 | } | ||
61 | my $time = $1 - $base; | ||
62 | $time = $time * $scale; | ||
63 | my $C = $2; | ||
64 | my $cpu = $3; | ||
65 | my $y = 400 * $cpu; | ||
66 | my $duration = $4 * $scale; | ||
67 | my $msec = int($4 * 100000)/100.0; | ||
68 | my $height = $C * 20; | ||
69 | $style = $styles[$C]; | ||
70 | |||
71 | $y = $y + 140 - $height; | ||
72 | |||
73 | $x2 = $time + 4; | ||
74 | $y2 = $y + 4; | ||
75 | |||
76 | |||
77 | print "<rect x=\"$time\" width=\"$duration\" y=\"$y\" height=\"$height\" style=\"$style\"/>\n"; | ||
78 | print "<text transform=\"translate($x2,$y2) rotate(90)\">C$C $msec</text>\n"; | ||
79 | } | ||
80 | if ($line =~ /([0-9\.]+)\] PSTATE: Going to P([0-9]) on cpu ([0-9]+)/) { | ||
81 | my $time = $1 - $base; | ||
82 | my $state = $2; | ||
83 | my $cpu = $3; | ||
84 | |||
85 | if (defined($pstate_last[$cpu])) { | ||
86 | my $from = $pstate_last[$cpu]; | ||
87 | my $oldstate = $pstate_state[$cpu]; | ||
88 | my $duration = ($time-$from) * $scale; | ||
89 | |||
90 | $from = $from * $scale; | ||
91 | my $to = $from + $duration; | ||
92 | my $height = 140 - ($oldstate * (140/8)); | ||
93 | |||
94 | my $y = 400 * $cpu + 200 + $height; | ||
95 | my $y2 = $y+4; | ||
96 | my $style = $styles[8]; | ||
97 | |||
98 | print "<rect x=\"$from\" y=\"$y\" width=\"$duration\" height=\"5\" style=\"$style\"/>\n"; | ||
99 | print "<text transform=\"translate($from,$y2)\">P$oldstate (cpu $cpu)</text>\n"; | ||
100 | }; | ||
101 | |||
102 | $pstate_last[$cpu] = $time; | ||
103 | $pstate_state[$cpu] = $state; | ||
104 | } | ||
105 | } | ||
106 | |||
107 | |||
108 | print "</svg>\n"; | ||