]> git.proxmox.com Git - mirror_ubuntu-focal-kernel.git/blame - kernel/trace/trace_branch.c
Merge branches 'acpi-fan', 'acpi-video' and 'acpi-ec'
[mirror_ubuntu-focal-kernel.git] / kernel / trace / trace_branch.c
CommitLineData
1f0d69a9
SR
1/*
2 * unlikely profiler
3 *
4 * Copyright (C) 2008 Steven Rostedt <srostedt@redhat.com>
5 */
6#include <linux/kallsyms.h>
7#include <linux/seq_file.h>
8#include <linux/spinlock.h>
65c6dc6a 9#include <linux/irqflags.h>
1f0d69a9
SR
10#include <linux/debugfs.h>
11#include <linux/uaccess.h>
12#include <linux/module.h>
13#include <linux/ftrace.h>
14#include <linux/hash.h>
15#include <linux/fs.h>
16#include <asm/local.h>
f633cef0 17
1f0d69a9 18#include "trace.h"
002bb86d 19#include "trace_stat.h"
f633cef0 20#include "trace_output.h"
1f0d69a9 21
2ed84eeb 22#ifdef CONFIG_BRANCH_TRACER
52f232cb 23
002bb86d 24static struct tracer branch_trace;
9f029e83
SR
25static int branch_tracing_enabled __read_mostly;
26static DEFINE_MUTEX(branch_tracing_mutex);
e302cf3f 27
9f029e83 28static struct trace_array *branch_tracer;
52f232cb
SR
29
30static void
9f029e83 31probe_likely_condition(struct ftrace_branch_data *f, int val, int expect)
52f232cb 32{
e1112b4d 33 struct ftrace_event_call *call = &event_branch;
9f029e83 34 struct trace_array *tr = branch_tracer;
a7603ff4 35 struct trace_array_cpu *data;
52f232cb 36 struct ring_buffer_event *event;
9f029e83 37 struct trace_branch *entry;
8f6e8a31 38 struct ring_buffer *buffer;
0a987751 39 unsigned long flags;
52f232cb
SR
40 int cpu, pc;
41 const char *p;
42
43 /*
44 * I would love to save just the ftrace_likely_data pointer, but
45 * this code can also be used by modules. Ugly things can happen
46 * if the module is unloaded, and then we go and read the
47 * pointer. This is slower, but much safer.
48 */
49
50 if (unlikely(!tr))
51 return;
52
a5e25883 53 local_irq_save(flags);
52f232cb 54 cpu = raw_smp_processor_id();
153e8ed9 55 data = per_cpu_ptr(tr->trace_buffer.data, cpu);
a7603ff4 56 if (atomic_inc_return(&data->disabled) != 1)
52f232cb
SR
57 goto out;
58
51a763dd 59 pc = preempt_count();
153e8ed9 60 buffer = tr->trace_buffer.buffer;
8f6e8a31 61 event = trace_buffer_lock_reserve(buffer, TRACE_BRANCH,
51a763dd 62 sizeof(*entry), flags, pc);
52f232cb
SR
63 if (!event)
64 goto out;
65
52f232cb 66 entry = ring_buffer_event_data(event);
52f232cb
SR
67
68 /* Strip off the path, only save the file */
69 p = f->file + strlen(f->file);
70 while (p >= f->file && *p != '/')
71 p--;
72 p++;
73
74 strncpy(entry->func, f->func, TRACE_FUNC_SIZE);
75 strncpy(entry->file, p, TRACE_FILE_SIZE);
76 entry->func[TRACE_FUNC_SIZE] = 0;
77 entry->file[TRACE_FILE_SIZE] = 0;
78 entry->line = f->line;
79 entry->correct = val == expect;
80
f306cc82 81 if (!call_filter_check_discard(call, entry, buffer, event))
7ffbd48d 82 __buffer_unlock_commit(buffer, event);
52f232cb
SR
83
84 out:
a7603ff4 85 atomic_dec(&data->disabled);
a5e25883 86 local_irq_restore(flags);
52f232cb
SR
87}
88
89static inline
9f029e83 90void trace_likely_condition(struct ftrace_branch_data *f, int val, int expect)
52f232cb 91{
9f029e83 92 if (!branch_tracing_enabled)
52f232cb
SR
93 return;
94
95 probe_likely_condition(f, val, expect);
96}
97
9f029e83 98int enable_branch_tracing(struct trace_array *tr)
52f232cb 99{
9f029e83
SR
100 mutex_lock(&branch_tracing_mutex);
101 branch_tracer = tr;
52f232cb
SR
102 /*
103 * Must be seen before enabling. The reader is a condition
104 * where we do not need a matching rmb()
105 */
106 smp_wmb();
9f029e83
SR
107 branch_tracing_enabled++;
108 mutex_unlock(&branch_tracing_mutex);
52f232cb 109
f54fc98a 110 return 0;
52f232cb
SR
111}
112
9f029e83 113void disable_branch_tracing(void)
52f232cb 114{
9f029e83 115 mutex_lock(&branch_tracing_mutex);
52f232cb 116
9f029e83 117 if (!branch_tracing_enabled)
52f232cb
SR
118 goto out_unlock;
119
9f029e83 120 branch_tracing_enabled--;
52f232cb
SR
121
122 out_unlock:
9f029e83 123 mutex_unlock(&branch_tracing_mutex);
52f232cb 124}
80e5ea45
SR
125
126static void start_branch_trace(struct trace_array *tr)
127{
128 enable_branch_tracing(tr);
129}
130
131static void stop_branch_trace(struct trace_array *tr)
132{
133 disable_branch_tracing();
134}
135
1c80025a 136static int branch_trace_init(struct trace_array *tr)
80e5ea45 137{
80e5ea45 138 start_branch_trace(tr);
1c80025a 139 return 0;
80e5ea45
SR
140}
141
142static void branch_trace_reset(struct trace_array *tr)
143{
144 stop_branch_trace(tr);
145}
146
ae7462b4 147static enum print_line_t trace_branch_print(struct trace_iterator *iter,
a9a57763 148 int flags, struct trace_event *event)
f633cef0
SR
149{
150 struct trace_branch *field;
151
2c9b238e 152 trace_assign_type(field, iter->ent);
f633cef0 153
7d40f671
SRRH
154 trace_seq_printf(&iter->seq, "[%s] %s:%s:%d\n",
155 field->correct ? " ok " : " MISS ",
156 field->func,
157 field->file,
158 field->line);
159
160 return trace_handle_return(&iter->seq);
f633cef0
SR
161}
162
557055be
Z
163static void branch_print_header(struct seq_file *s)
164{
165 seq_puts(s, "# TASK-PID CPU# TIMESTAMP CORRECT"
d79ac28f
RV
166 " FUNC:FILE:LINE\n"
167 "# | | | | | "
168 " |\n");
557055be 169}
e302cf3f 170
a9a57763
SR
171static struct trace_event_functions trace_branch_funcs = {
172 .trace = trace_branch_print,
173};
174
f633cef0 175static struct trace_event trace_branch_event = {
ef18012b 176 .type = TRACE_BRANCH,
a9a57763 177 .funcs = &trace_branch_funcs,
f633cef0
SR
178};
179
002bb86d
FW
180static struct tracer branch_trace __read_mostly =
181{
182 .name = "branch",
183 .init = branch_trace_init,
184 .reset = branch_trace_reset,
185#ifdef CONFIG_FTRACE_SELFTEST
186 .selftest = trace_selftest_startup_branch,
187#endif /* CONFIG_FTRACE_SELFTEST */
557055be 188 .print_header = branch_print_header,
002bb86d
FW
189};
190
191__init static int init_branch_tracer(void)
192{
193 int ret;
194
195 ret = register_ftrace_event(&trace_branch_event);
196 if (!ret) {
197 printk(KERN_WARNING "Warning: could not register "
198 "branch events\n");
199 return 1;
200 }
201 return register_tracer(&branch_trace);
202}
6f415672 203core_initcall(init_branch_tracer);
002bb86d 204
52f232cb
SR
205#else
206static inline
9f029e83 207void trace_likely_condition(struct ftrace_branch_data *f, int val, int expect)
52f232cb
SR
208{
209}
2ed84eeb 210#endif /* CONFIG_BRANCH_TRACER */
52f232cb 211
9f029e83 212void ftrace_likely_update(struct ftrace_branch_data *f, int val, int expect)
1f0d69a9 213{
52f232cb
SR
214 /*
215 * I would love to have a trace point here instead, but the
216 * trace point code is so inundated with unlikely and likely
217 * conditions that the recursive nightmare that exists is too
218 * much to try to get working. At least for now.
219 */
220 trace_likely_condition(f, val, expect);
221
1f0d69a9
SR
222 /* FIXME: Make this atomic! */
223 if (val == expect)
224 f->correct++;
225 else
226 f->incorrect++;
227}
228EXPORT_SYMBOL(ftrace_likely_update);
229
e302cf3f
FW
230extern unsigned long __start_annotated_branch_profile[];
231extern unsigned long __stop_annotated_branch_profile[];
1f0d69a9 232
e302cf3f 233static int annotated_branch_stat_headers(struct seq_file *m)
1f0d69a9 234{
d79ac28f
RV
235 seq_puts(m, " correct incorrect % "
236 " Function "
fa6f0cc7
RV
237 " File Line\n"
238 " ------- --------- - "
239 " -------- "
240 " ---- ----\n");
e302cf3f 241 return 0;
1f0d69a9
SR
242}
243
e302cf3f 244static inline long get_incorrect_percent(struct ftrace_branch_data *p)
1f0d69a9 245{
e302cf3f 246 long percent;
1f0d69a9 247
e302cf3f
FW
248 if (p->correct) {
249 percent = p->incorrect * 100;
250 percent /= p->correct + p->incorrect;
251 } else
252 percent = p->incorrect ? 100 : -1;
1f0d69a9 253
e302cf3f 254 return percent;
1f0d69a9
SR
255}
256
e302cf3f 257static int branch_stat_show(struct seq_file *m, void *v)
1f0d69a9 258{
9f029e83 259 struct ftrace_branch_data *p = v;
1f0d69a9 260 const char *f;
bac28bfe 261 long percent;
1f0d69a9 262
1f0d69a9
SR
263 /* Only print the file, not the path */
264 f = p->file + strlen(p->file);
265 while (f >= p->file && *f != '/')
266 f--;
267 f++;
268
2bcd521a
SR
269 /*
270 * The miss is overlayed on correct, and hit on incorrect.
271 */
e302cf3f 272 percent = get_incorrect_percent(p);
1f0d69a9 273
bac28bfe
SR
274 seq_printf(m, "%8lu %8lu ", p->correct, p->incorrect);
275 if (percent < 0)
fa6f0cc7 276 seq_puts(m, " X ");
bac28bfe
SR
277 else
278 seq_printf(m, "%3ld ", percent);
1f0d69a9
SR
279 seq_printf(m, "%-30.30s %-20.20s %d\n", p->func, f, p->line);
280 return 0;
281}
282
42548008 283static void *annotated_branch_stat_start(struct tracer_stat *trace)
e302cf3f
FW
284{
285 return __start_annotated_branch_profile;
286}
1f0d69a9 287
e302cf3f
FW
288static void *
289annotated_branch_stat_next(void *v, int idx)
1f0d69a9 290{
e302cf3f 291 struct ftrace_branch_data *p = v;
1f0d69a9 292
e302cf3f 293 ++p;
1f0d69a9 294
e302cf3f
FW
295 if ((void *)p >= (void *)__stop_annotated_branch_profile)
296 return NULL;
297
298 return p;
1f0d69a9
SR
299}
300
e302cf3f
FW
301static int annotated_branch_stat_cmp(void *p1, void *p2)
302{
303 struct ftrace_branch_data *a = p1;
304 struct ftrace_branch_data *b = p2;
305
306 long percent_a, percent_b;
307
308 percent_a = get_incorrect_percent(a);
309 percent_b = get_incorrect_percent(b);
310
311 if (percent_a < percent_b)
312 return -1;
313 if (percent_a > percent_b)
314 return 1;
ede55c9d
SR
315
316 if (a->incorrect < b->incorrect)
317 return -1;
318 if (a->incorrect > b->incorrect)
319 return 1;
320
321 /*
322 * Since the above shows worse (incorrect) cases
323 * first, we continue that by showing best (correct)
324 * cases last.
325 */
326 if (a->correct > b->correct)
327 return -1;
328 if (a->correct < b->correct)
329 return 1;
330
331 return 0;
e302cf3f 332}
1f0d69a9 333
002bb86d
FW
334static struct tracer_stat annotated_branch_stats = {
335 .name = "branch_annotated",
336 .stat_start = annotated_branch_stat_start,
337 .stat_next = annotated_branch_stat_next,
338 .stat_cmp = annotated_branch_stat_cmp,
339 .stat_headers = annotated_branch_stat_headers,
340 .stat_show = branch_stat_show
341};
342
343__init static int init_annotated_branch_stats(void)
344{
345 int ret;
346
347 ret = register_stat_tracer(&annotated_branch_stats);
348 if (!ret) {
349 printk(KERN_WARNING "Warning: could not register "
350 "annotated branches stats\n");
351 return 1;
352 }
353 return 0;
354}
355fs_initcall(init_annotated_branch_stats);
356
2bcd521a 357#ifdef CONFIG_PROFILE_ALL_BRANCHES
2bcd521a 358
e302cf3f
FW
359extern unsigned long __start_branch_profile[];
360extern unsigned long __stop_branch_profile[];
1f0d69a9 361
e302cf3f
FW
362static int all_branch_stat_headers(struct seq_file *m)
363{
d79ac28f
RV
364 seq_puts(m, " miss hit % "
365 " Function "
fa6f0cc7
RV
366 " File Line\n"
367 " ------- --------- - "
368 " -------- "
369 " ---- ----\n");
e302cf3f
FW
370 return 0;
371}
1f0d69a9 372
42548008 373static void *all_branch_stat_start(struct tracer_stat *trace)
1f0d69a9 374{
e302cf3f
FW
375 return __start_branch_profile;
376}
377
378static void *
379all_branch_stat_next(void *v, int idx)
380{
381 struct ftrace_branch_data *p = v;
1f0d69a9 382
e302cf3f 383 ++p;
1f0d69a9 384
e302cf3f
FW
385 if ((void *)p >= (void *)__stop_branch_profile)
386 return NULL;
1f0d69a9 387
e302cf3f
FW
388 return p;
389}
2bcd521a 390
002bb86d
FW
391static struct tracer_stat all_branch_stats = {
392 .name = "branch_all",
034939b6
FW
393 .stat_start = all_branch_stat_start,
394 .stat_next = all_branch_stat_next,
395 .stat_headers = all_branch_stat_headers,
002bb86d 396 .stat_show = branch_stat_show
034939b6 397};
e302cf3f 398
002bb86d 399__init static int all_annotated_branch_stats(void)
e302cf3f 400{
e302cf3f 401 int ret;
002bb86d
FW
402
403 ret = register_stat_tracer(&all_branch_stats);
e302cf3f 404 if (!ret) {
002bb86d
FW
405 printk(KERN_WARNING "Warning: could not register "
406 "all branches stats\n");
e302cf3f
FW
407 return 1;
408 }
002bb86d 409 return 0;
e302cf3f 410}
002bb86d
FW
411fs_initcall(all_annotated_branch_stats);
412#endif /* CONFIG_PROFILE_ALL_BRANCHES */