Commit | Line | Data |
---|---|---|
f3f47a67 AV |
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> | |
12922110 | 14 | #include <trace/power.h> |
f3f47a67 AV |
15 | #include <linux/kallsyms.h> |
16 | #include <linux/module.h> | |
17 | ||
18 | #include "trace.h" | |
f0868d1e | 19 | #include "trace_output.h" |
f3f47a67 AV |
20 | |
21 | static struct trace_array *power_trace; | |
22 | static int __read_mostly trace_power_enabled; | |
23 | ||
b5f9fd0f JB |
24 | static void probe_power_start(struct power_trace *it, unsigned int type, |
25 | unsigned int level) | |
26 | { | |
27 | if (!trace_power_enabled) | |
28 | return; | |
29 | ||
30 | memset(it, 0, sizeof(struct power_trace)); | |
31 | it->state = level; | |
32 | it->type = type; | |
33 | it->stamp = ktime_get(); | |
34 | } | |
35 | ||
36 | ||
37 | static void probe_power_end(struct power_trace *it) | |
38 | { | |
39 | struct ring_buffer_event *event; | |
40 | struct trace_power *entry; | |
41 | struct trace_array_cpu *data; | |
42 | struct trace_array *tr = power_trace; | |
43 | ||
44 | if (!trace_power_enabled) | |
45 | return; | |
46 | ||
47 | preempt_disable(); | |
48 | it->end = ktime_get(); | |
49 | data = tr->data[smp_processor_id()]; | |
50 | ||
51 | event = trace_buffer_lock_reserve(tr, TRACE_POWER, | |
52 | sizeof(*entry), 0, 0); | |
53 | if (!event) | |
54 | goto out; | |
55 | entry = ring_buffer_event_data(event); | |
56 | entry->state_data = *it; | |
57 | trace_buffer_unlock_commit(tr, event, 0, 0); | |
58 | out: | |
59 | preempt_enable(); | |
60 | } | |
61 | ||
62 | static void probe_power_mark(struct power_trace *it, unsigned int type, | |
63 | unsigned int level) | |
64 | { | |
65 | struct ring_buffer_event *event; | |
66 | struct trace_power *entry; | |
67 | struct trace_array_cpu *data; | |
68 | struct trace_array *tr = power_trace; | |
69 | ||
70 | if (!trace_power_enabled) | |
71 | return; | |
72 | ||
73 | memset(it, 0, sizeof(struct power_trace)); | |
74 | it->state = level; | |
75 | it->type = type; | |
76 | it->stamp = ktime_get(); | |
77 | preempt_disable(); | |
78 | it->end = it->stamp; | |
79 | data = tr->data[smp_processor_id()]; | |
80 | ||
81 | event = trace_buffer_lock_reserve(tr, TRACE_POWER, | |
82 | sizeof(*entry), 0, 0); | |
83 | if (!event) | |
84 | goto out; | |
85 | entry = ring_buffer_event_data(event); | |
86 | entry->state_data = *it; | |
87 | trace_buffer_unlock_commit(tr, event, 0, 0); | |
88 | out: | |
89 | preempt_enable(); | |
90 | } | |
91 | ||
92 | static int tracing_power_register(void) | |
93 | { | |
94 | int ret; | |
95 | ||
96 | ret = register_trace_power_start(probe_power_start); | |
97 | if (ret) { | |
98 | pr_info("power trace: Couldn't activate tracepoint" | |
99 | " probe to trace_power_start\n"); | |
100 | return ret; | |
101 | } | |
102 | ret = register_trace_power_end(probe_power_end); | |
103 | if (ret) { | |
104 | pr_info("power trace: Couldn't activate tracepoint" | |
105 | " probe to trace_power_end\n"); | |
106 | goto fail_start; | |
107 | } | |
108 | ret = register_trace_power_mark(probe_power_mark); | |
109 | if (ret) { | |
110 | pr_info("power trace: Couldn't activate tracepoint" | |
111 | " probe to trace_power_mark\n"); | |
112 | goto fail_end; | |
113 | } | |
114 | return ret; | |
115 | fail_end: | |
116 | unregister_trace_power_end(probe_power_end); | |
117 | fail_start: | |
118 | unregister_trace_power_start(probe_power_start); | |
119 | return ret; | |
120 | } | |
f3f47a67 AV |
121 | |
122 | static void start_power_trace(struct trace_array *tr) | |
123 | { | |
124 | trace_power_enabled = 1; | |
125 | } | |
126 | ||
127 | static void stop_power_trace(struct trace_array *tr) | |
62524d55 SR |
128 | { |
129 | trace_power_enabled = 0; | |
130 | } | |
131 | ||
132 | static void power_trace_reset(struct trace_array *tr) | |
f3f47a67 AV |
133 | { |
134 | trace_power_enabled = 0; | |
b5f9fd0f JB |
135 | unregister_trace_power_start(probe_power_start); |
136 | unregister_trace_power_end(probe_power_end); | |
137 | unregister_trace_power_mark(probe_power_mark); | |
f3f47a67 AV |
138 | } |
139 | ||
140 | ||
141 | static int power_trace_init(struct trace_array *tr) | |
142 | { | |
143 | int cpu; | |
144 | power_trace = tr; | |
145 | ||
146 | trace_power_enabled = 1; | |
b5f9fd0f | 147 | tracing_power_register(); |
f3f47a67 | 148 | |
4462344e | 149 | for_each_cpu(cpu, cpu_possible_mask) |
f3f47a67 AV |
150 | tracing_reset(tr, cpu); |
151 | return 0; | |
152 | } | |
153 | ||
154 | static enum print_line_t power_print_line(struct trace_iterator *iter) | |
155 | { | |
156 | int ret = 0; | |
157 | struct trace_entry *entry = iter->ent; | |
158 | struct trace_power *field ; | |
159 | struct power_trace *it; | |
160 | struct trace_seq *s = &iter->seq; | |
161 | struct timespec stamp; | |
162 | struct timespec duration; | |
163 | ||
164 | trace_assign_type(field, entry); | |
165 | it = &field->state_data; | |
166 | stamp = ktime_to_timespec(it->stamp); | |
167 | duration = ktime_to_timespec(ktime_sub(it->end, it->stamp)); | |
168 | ||
169 | if (entry->type == TRACE_POWER) { | |
170 | if (it->type == POWER_CSTATE) | |
171 | ret = trace_seq_printf(s, "[%5ld.%09ld] CSTATE: Going to C%i on cpu %i for %ld.%09ld\n", | |
172 | stamp.tv_sec, | |
173 | stamp.tv_nsec, | |
174 | it->state, iter->cpu, | |
175 | duration.tv_sec, | |
176 | duration.tv_nsec); | |
177 | if (it->type == POWER_PSTATE) | |
178 | ret = trace_seq_printf(s, "[%5ld.%09ld] PSTATE: Going to P%i on cpu %i\n", | |
179 | stamp.tv_sec, | |
180 | stamp.tv_nsec, | |
181 | it->state, iter->cpu); | |
182 | if (!ret) | |
183 | return TRACE_TYPE_PARTIAL_LINE; | |
184 | return TRACE_TYPE_HANDLED; | |
185 | } | |
186 | return TRACE_TYPE_UNHANDLED; | |
187 | } | |
188 | ||
a3d03eca Z |
189 | static void power_print_header(struct seq_file *s) |
190 | { | |
191 | seq_puts(s, "# TIMESTAMP STATE EVENT\n"); | |
192 | seq_puts(s, "# | | |\n"); | |
193 | } | |
194 | ||
f3f47a67 AV |
195 | static struct tracer power_tracer __read_mostly = |
196 | { | |
197 | .name = "power", | |
198 | .init = power_trace_init, | |
199 | .start = start_power_trace, | |
200 | .stop = stop_power_trace, | |
62524d55 | 201 | .reset = power_trace_reset, |
f3f47a67 | 202 | .print_line = power_print_line, |
a3d03eca | 203 | .print_header = power_print_header, |
f3f47a67 AV |
204 | }; |
205 | ||
206 | static int init_power_trace(void) | |
207 | { | |
208 | return register_tracer(&power_tracer); | |
209 | } | |
210 | device_initcall(init_power_trace); |