xref: /openbmc/linux/kernel/trace/trace_branch.c (revision 36590c50)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
294b80ffdSSteven Rostedt /*
394b80ffdSSteven Rostedt  * unlikely profiler
494b80ffdSSteven Rostedt  *
594b80ffdSSteven Rostedt  * Copyright (C) 2008 Steven Rostedt <srostedt@redhat.com>
694b80ffdSSteven Rostedt  */
794b80ffdSSteven Rostedt #include <linux/kallsyms.h>
894b80ffdSSteven Rostedt #include <linux/seq_file.h>
994b80ffdSSteven Rostedt #include <linux/spinlock.h>
1065c6dc6aSFrederic Weisbecker #include <linux/irqflags.h>
1194b80ffdSSteven Rostedt #include <linux/uaccess.h>
1294b80ffdSSteven Rostedt #include <linux/module.h>
1394b80ffdSSteven Rostedt #include <linux/ftrace.h>
1494b80ffdSSteven Rostedt #include <linux/hash.h>
1594b80ffdSSteven Rostedt #include <linux/fs.h>
1694b80ffdSSteven Rostedt #include <asm/local.h>
17f633cef0SSteven Rostedt 
1894b80ffdSSteven Rostedt #include "trace.h"
19002bb86dSFrederic Weisbecker #include "trace_stat.h"
20f633cef0SSteven Rostedt #include "trace_output.h"
2194b80ffdSSteven Rostedt 
2294b80ffdSSteven Rostedt #ifdef CONFIG_BRANCH_TRACER
2394b80ffdSSteven Rostedt 
24002bb86dSFrederic Weisbecker static struct tracer branch_trace;
2594b80ffdSSteven Rostedt static int branch_tracing_enabled __read_mostly;
2694b80ffdSSteven Rostedt static DEFINE_MUTEX(branch_tracing_mutex);
27e302cf3fSFrederic Weisbecker 
2894b80ffdSSteven Rostedt static struct trace_array *branch_tracer;
2994b80ffdSSteven Rostedt 
3094b80ffdSSteven Rostedt static void
probe_likely_condition(struct ftrace_likely_data * f,int val,int expect)31068f530bSSteven Rostedt (VMware) probe_likely_condition(struct ftrace_likely_data *f, int val, int expect)
3294b80ffdSSteven Rostedt {
332425bcb9SSteven Rostedt (Red Hat) 	struct trace_event_call *call = &event_branch;
3494b80ffdSSteven Rostedt 	struct trace_array *tr = branch_tracer;
3513292494SSteven Rostedt (VMware) 	struct trace_buffer *buffer;
36a7603ff4SSteven Rostedt 	struct trace_array_cpu *data;
3794b80ffdSSteven Rostedt 	struct ring_buffer_event *event;
3894b80ffdSSteven Rostedt 	struct trace_branch *entry;
390a987751SArnaldo Carvalho de Melo 	unsigned long flags;
40*36590c50SSebastian Andrzej Siewior 	unsigned int trace_ctx;
4194b80ffdSSteven Rostedt 	const char *p;
4294b80ffdSSteven Rostedt 
436224beb1SSteven Rostedt (Red Hat) 	if (current->trace_recursion & TRACE_BRANCH_BIT)
446224beb1SSteven Rostedt (Red Hat) 		return;
456224beb1SSteven Rostedt (Red Hat) 
4694b80ffdSSteven Rostedt 	/*
4794b80ffdSSteven Rostedt 	 * I would love to save just the ftrace_likely_data pointer, but
4894b80ffdSSteven Rostedt 	 * this code can also be used by modules. Ugly things can happen
4994b80ffdSSteven Rostedt 	 * if the module is unloaded, and then we go and read the
5094b80ffdSSteven Rostedt 	 * pointer.  This is slower, but much safer.
5194b80ffdSSteven Rostedt 	 */
5294b80ffdSSteven Rostedt 
5394b80ffdSSteven Rostedt 	if (unlikely(!tr))
5494b80ffdSSteven Rostedt 		return;
5594b80ffdSSteven Rostedt 
566224beb1SSteven Rostedt (Red Hat) 	raw_local_irq_save(flags);
576224beb1SSteven Rostedt (Red Hat) 	current->trace_recursion |= TRACE_BRANCH_BIT;
581c5eb448SSteven Rostedt (VMware) 	data = this_cpu_ptr(tr->array_buffer.data);
596224beb1SSteven Rostedt (Red Hat) 	if (atomic_read(&data->disabled))
6094b80ffdSSteven Rostedt 		goto out;
6194b80ffdSSteven Rostedt 
62*36590c50SSebastian Andrzej Siewior 	trace_ctx = tracing_gen_ctx_flags(flags);
631c5eb448SSteven Rostedt (VMware) 	buffer = tr->array_buffer.buffer;
648f6e8a31SSteven Rostedt 	event = trace_buffer_lock_reserve(buffer, TRACE_BRANCH,
65*36590c50SSebastian Andrzej Siewior 					  sizeof(*entry), trace_ctx);
6694b80ffdSSteven Rostedt 	if (!event)
6794b80ffdSSteven Rostedt 		goto out;
6894b80ffdSSteven Rostedt 
6994b80ffdSSteven Rostedt 	entry	= ring_buffer_event_data(event);
7094b80ffdSSteven Rostedt 
7194b80ffdSSteven Rostedt 	/* Strip off the path, only save the file */
72068f530bSSteven Rostedt (VMware) 	p = f->data.file + strlen(f->data.file);
73068f530bSSteven Rostedt (VMware) 	while (p >= f->data.file && *p != '/')
7494b80ffdSSteven Rostedt 		p--;
7594b80ffdSSteven Rostedt 	p++;
7694b80ffdSSteven Rostedt 
77068f530bSSteven Rostedt (VMware) 	strncpy(entry->func, f->data.func, TRACE_FUNC_SIZE);
7894b80ffdSSteven Rostedt 	strncpy(entry->file, p, TRACE_FILE_SIZE);
7994b80ffdSSteven Rostedt 	entry->func[TRACE_FUNC_SIZE] = 0;
8094b80ffdSSteven Rostedt 	entry->file[TRACE_FILE_SIZE] = 0;
81068f530bSSteven Rostedt (VMware) 	entry->constant = f->constant;
82068f530bSSteven Rostedt (VMware) 	entry->line = f->data.line;
8394b80ffdSSteven Rostedt 	entry->correct = val == expect;
8494b80ffdSSteven Rostedt 
85f306cc82STom Zanussi 	if (!call_filter_check_discard(call, entry, buffer, event))
8652ffabe3SSteven Rostedt (Red Hat) 		trace_buffer_unlock_commit_nostack(buffer, event);
8794b80ffdSSteven Rostedt 
8894b80ffdSSteven Rostedt  out:
896224beb1SSteven Rostedt (Red Hat) 	current->trace_recursion &= ~TRACE_BRANCH_BIT;
906224beb1SSteven Rostedt (Red Hat) 	raw_local_irq_restore(flags);
9194b80ffdSSteven Rostedt }
9294b80ffdSSteven Rostedt 
9394b80ffdSSteven Rostedt static inline
trace_likely_condition(struct ftrace_likely_data * f,int val,int expect)94068f530bSSteven Rostedt (VMware) void trace_likely_condition(struct ftrace_likely_data *f, int val, int expect)
9594b80ffdSSteven Rostedt {
9694b80ffdSSteven Rostedt 	if (!branch_tracing_enabled)
9794b80ffdSSteven Rostedt 		return;
9894b80ffdSSteven Rostedt 
9994b80ffdSSteven Rostedt 	probe_likely_condition(f, val, expect);
10094b80ffdSSteven Rostedt }
10194b80ffdSSteven Rostedt 
enable_branch_tracing(struct trace_array * tr)10294b80ffdSSteven Rostedt int enable_branch_tracing(struct trace_array *tr)
10394b80ffdSSteven Rostedt {
10494b80ffdSSteven Rostedt 	mutex_lock(&branch_tracing_mutex);
10594b80ffdSSteven Rostedt 	branch_tracer = tr;
10694b80ffdSSteven Rostedt 	/*
10794b80ffdSSteven Rostedt 	 * Must be seen before enabling. The reader is a condition
10894b80ffdSSteven Rostedt 	 * where we do not need a matching rmb()
10994b80ffdSSteven Rostedt 	 */
11094b80ffdSSteven Rostedt 	smp_wmb();
11194b80ffdSSteven Rostedt 	branch_tracing_enabled++;
11294b80ffdSSteven Rostedt 	mutex_unlock(&branch_tracing_mutex);
11394b80ffdSSteven Rostedt 
114f54fc98aSWenji Huang 	return 0;
11594b80ffdSSteven Rostedt }
11694b80ffdSSteven Rostedt 
disable_branch_tracing(void)11794b80ffdSSteven Rostedt void disable_branch_tracing(void)
11894b80ffdSSteven Rostedt {
11994b80ffdSSteven Rostedt 	mutex_lock(&branch_tracing_mutex);
12094b80ffdSSteven Rostedt 
12194b80ffdSSteven Rostedt 	if (!branch_tracing_enabled)
12294b80ffdSSteven Rostedt 		goto out_unlock;
12394b80ffdSSteven Rostedt 
12494b80ffdSSteven Rostedt 	branch_tracing_enabled--;
12594b80ffdSSteven Rostedt 
12694b80ffdSSteven Rostedt  out_unlock:
12794b80ffdSSteven Rostedt 	mutex_unlock(&branch_tracing_mutex);
12894b80ffdSSteven Rostedt }
12994b80ffdSSteven Rostedt 
branch_trace_init(struct trace_array * tr)1301c80025aSFrederic Weisbecker static int branch_trace_init(struct trace_array *tr)
13194b80ffdSSteven Rostedt {
13230616929SDmitry Safonov 	return enable_branch_tracing(tr);
13394b80ffdSSteven Rostedt }
13494b80ffdSSteven Rostedt 
branch_trace_reset(struct trace_array * tr)13594b80ffdSSteven Rostedt static void branch_trace_reset(struct trace_array *tr)
13694b80ffdSSteven Rostedt {
13730616929SDmitry Safonov 	disable_branch_tracing();
13894b80ffdSSteven Rostedt }
13994b80ffdSSteven Rostedt 
trace_branch_print(struct trace_iterator * iter,int flags,struct trace_event * event)140ae7462b4SArnaldo Carvalho de Melo static enum print_line_t trace_branch_print(struct trace_iterator *iter,
141a9a57763SSteven Rostedt 					    int flags, struct trace_event *event)
142f633cef0SSteven Rostedt {
143f633cef0SSteven Rostedt 	struct trace_branch *field;
144f633cef0SSteven Rostedt 
1452c9b238eSArnaldo Carvalho de Melo 	trace_assign_type(field, iter->ent);
146f633cef0SSteven Rostedt 
1477d40f671SSteven Rostedt (Red Hat) 	trace_seq_printf(&iter->seq, "[%s] %s:%s:%d\n",
148f633cef0SSteven Rostedt 			 field->correct ? "  ok  " : " MISS ",
149f633cef0SSteven Rostedt 			 field->func,
150f633cef0SSteven Rostedt 			 field->file,
1517d40f671SSteven Rostedt (Red Hat) 			 field->line);
152f633cef0SSteven Rostedt 
1537d40f671SSteven Rostedt (Red Hat) 	return trace_handle_return(&iter->seq);
154f633cef0SSteven Rostedt }
155f633cef0SSteven Rostedt 
branch_print_header(struct seq_file * s)156557055beSZhaolei static void branch_print_header(struct seq_file *s)
157557055beSZhaolei {
158557055beSZhaolei 	seq_puts(s, "#           TASK-PID    CPU#    TIMESTAMP  CORRECT"
159d79ac28fSRasmus Villemoes 		    "  FUNC:FILE:LINE\n"
160d79ac28fSRasmus Villemoes 		    "#              | |       |          |         |   "
161557055beSZhaolei 		    "    |\n");
162557055beSZhaolei }
163e302cf3fSFrederic Weisbecker 
164a9a57763SSteven Rostedt static struct trace_event_functions trace_branch_funcs = {
165a9a57763SSteven Rostedt 	.trace		= trace_branch_print,
166a9a57763SSteven Rostedt };
167a9a57763SSteven Rostedt 
168f633cef0SSteven Rostedt static struct trace_event trace_branch_event = {
169f633cef0SSteven Rostedt 	.type		= TRACE_BRANCH,
170a9a57763SSteven Rostedt 	.funcs		= &trace_branch_funcs,
171f633cef0SSteven Rostedt };
172f633cef0SSteven Rostedt 
173002bb86dSFrederic Weisbecker static struct tracer branch_trace __read_mostly =
174002bb86dSFrederic Weisbecker {
175002bb86dSFrederic Weisbecker 	.name		= "branch",
176002bb86dSFrederic Weisbecker 	.init		= branch_trace_init,
177002bb86dSFrederic Weisbecker 	.reset		= branch_trace_reset,
178002bb86dSFrederic Weisbecker #ifdef CONFIG_FTRACE_SELFTEST
179002bb86dSFrederic Weisbecker 	.selftest	= trace_selftest_startup_branch,
180002bb86dSFrederic Weisbecker #endif /* CONFIG_FTRACE_SELFTEST */
181557055beSZhaolei 	.print_header	= branch_print_header,
182002bb86dSFrederic Weisbecker };
183002bb86dSFrederic Weisbecker 
init_branch_tracer(void)184002bb86dSFrederic Weisbecker __init static int init_branch_tracer(void)
185002bb86dSFrederic Weisbecker {
186002bb86dSFrederic Weisbecker 	int ret;
187002bb86dSFrederic Weisbecker 
1889023c930SSteven Rostedt (Red Hat) 	ret = register_trace_event(&trace_branch_event);
189002bb86dSFrederic Weisbecker 	if (!ret) {
190002bb86dSFrederic Weisbecker 		printk(KERN_WARNING "Warning: could not register "
191002bb86dSFrederic Weisbecker 				    "branch events\n");
192002bb86dSFrederic Weisbecker 		return 1;
193002bb86dSFrederic Weisbecker 	}
194002bb86dSFrederic Weisbecker 	return register_tracer(&branch_trace);
195002bb86dSFrederic Weisbecker }
1966f415672SSteven Rostedt core_initcall(init_branch_tracer);
197002bb86dSFrederic Weisbecker 
19894b80ffdSSteven Rostedt #else
19994b80ffdSSteven Rostedt static inline
trace_likely_condition(struct ftrace_likely_data * f,int val,int expect)200068f530bSSteven Rostedt (VMware) void trace_likely_condition(struct ftrace_likely_data *f, int val, int expect)
20194b80ffdSSteven Rostedt {
20294b80ffdSSteven Rostedt }
20394b80ffdSSteven Rostedt #endif /* CONFIG_BRANCH_TRACER */
20494b80ffdSSteven Rostedt 
ftrace_likely_update(struct ftrace_likely_data * f,int val,int expect,int is_constant)205134e6a03SSteven Rostedt (VMware) void ftrace_likely_update(struct ftrace_likely_data *f, int val,
206d45ae1f7SSteven Rostedt (VMware) 			  int expect, int is_constant)
20794b80ffdSSteven Rostedt {
2084a6c91fbSPeter Zijlstra 	unsigned long flags = user_access_save();
2094a6c91fbSPeter Zijlstra 
210d45ae1f7SSteven Rostedt (VMware) 	/* A constant is always correct */
211134e6a03SSteven Rostedt (VMware) 	if (is_constant) {
212134e6a03SSteven Rostedt (VMware) 		f->constant++;
213d45ae1f7SSteven Rostedt (VMware) 		val = expect;
214134e6a03SSteven Rostedt (VMware) 	}
21594b80ffdSSteven Rostedt 	/*
21694b80ffdSSteven Rostedt 	 * I would love to have a trace point here instead, but the
21794b80ffdSSteven Rostedt 	 * trace point code is so inundated with unlikely and likely
21894b80ffdSSteven Rostedt 	 * conditions that the recursive nightmare that exists is too
21994b80ffdSSteven Rostedt 	 * much to try to get working. At least for now.
22094b80ffdSSteven Rostedt 	 */
221068f530bSSteven Rostedt (VMware) 	trace_likely_condition(f, val, expect);
22294b80ffdSSteven Rostedt 
22394b80ffdSSteven Rostedt 	/* FIXME: Make this atomic! */
22494b80ffdSSteven Rostedt 	if (val == expect)
225134e6a03SSteven Rostedt (VMware) 		f->data.correct++;
22694b80ffdSSteven Rostedt 	else
227134e6a03SSteven Rostedt (VMware) 		f->data.incorrect++;
2284a6c91fbSPeter Zijlstra 
2294a6c91fbSPeter Zijlstra 	user_access_restore(flags);
23094b80ffdSSteven Rostedt }
23194b80ffdSSteven Rostedt EXPORT_SYMBOL(ftrace_likely_update);
23294b80ffdSSteven Rostedt 
233e302cf3fSFrederic Weisbecker extern unsigned long __start_annotated_branch_profile[];
234e302cf3fSFrederic Weisbecker extern unsigned long __stop_annotated_branch_profile[];
23594b80ffdSSteven Rostedt 
annotated_branch_stat_headers(struct seq_file * m)236e302cf3fSFrederic Weisbecker static int annotated_branch_stat_headers(struct seq_file *m)
23794b80ffdSSteven Rostedt {
238d79ac28fSRasmus Villemoes 	seq_puts(m, " correct incorrect  % "
239d79ac28fSRasmus Villemoes 		    "       Function                "
24094b80ffdSSteven Rostedt 		    "  File              Line\n"
24194b80ffdSSteven Rostedt 		    " ------- ---------  - "
24294b80ffdSSteven Rostedt 		    "       --------                "
24394b80ffdSSteven Rostedt 		    "  ----              ----\n");
24494b80ffdSSteven Rostedt 	return 0;
24594b80ffdSSteven Rostedt }
24694b80ffdSSteven Rostedt 
get_incorrect_percent(const struct ftrace_branch_data * p)24780042c8fSAndy Shevchenko static inline long get_incorrect_percent(const struct ftrace_branch_data *p)
248e302cf3fSFrederic Weisbecker {
249e302cf3fSFrederic Weisbecker 	long percent;
250e302cf3fSFrederic Weisbecker 
251e302cf3fSFrederic Weisbecker 	if (p->correct) {
252e302cf3fSFrederic Weisbecker 		percent = p->incorrect * 100;
253e302cf3fSFrederic Weisbecker 		percent /= p->correct + p->incorrect;
254e302cf3fSFrederic Weisbecker 	} else
255e302cf3fSFrederic Weisbecker 		percent = p->incorrect ? 100 : -1;
256e302cf3fSFrederic Weisbecker 
257e302cf3fSFrederic Weisbecker 	return percent;
258e302cf3fSFrederic Weisbecker }
259e302cf3fSFrederic Weisbecker 
branch_stat_process_file(struct ftrace_branch_data * p)260134e6a03SSteven Rostedt (VMware) static const char *branch_stat_process_file(struct ftrace_branch_data *p)
261e302cf3fSFrederic Weisbecker {
262e302cf3fSFrederic Weisbecker 	const char *f;
263e302cf3fSFrederic Weisbecker 
26494b80ffdSSteven Rostedt 	/* Only print the file, not the path */
26594b80ffdSSteven Rostedt 	f = p->file + strlen(p->file);
26694b80ffdSSteven Rostedt 	while (f >= p->file && *f != '/')
26794b80ffdSSteven Rostedt 		f--;
268134e6a03SSteven Rostedt (VMware) 	return ++f;
269134e6a03SSteven Rostedt (VMware) }
270134e6a03SSteven Rostedt (VMware) 
branch_stat_show(struct seq_file * m,struct ftrace_branch_data * p,const char * f)271134e6a03SSteven Rostedt (VMware) static void branch_stat_show(struct seq_file *m,
272134e6a03SSteven Rostedt (VMware) 			     struct ftrace_branch_data *p, const char *f)
273134e6a03SSteven Rostedt (VMware) {
274134e6a03SSteven Rostedt (VMware) 	long percent;
27594b80ffdSSteven Rostedt 
2762bcd521aSSteven Rostedt 	/*
2772bcd521aSSteven Rostedt 	 * The miss is overlayed on correct, and hit on incorrect.
2782bcd521aSSteven Rostedt 	 */
279e302cf3fSFrederic Weisbecker 	percent = get_incorrect_percent(p);
28094b80ffdSSteven Rostedt 
281bac28bfeSSteven Rostedt 	if (percent < 0)
282fa6f0cc7SRasmus Villemoes 		seq_puts(m, "  X ");
283bac28bfeSSteven Rostedt 	else
284bac28bfeSSteven Rostedt 		seq_printf(m, "%3ld ", percent);
285134e6a03SSteven Rostedt (VMware) 
28694b80ffdSSteven Rostedt 	seq_printf(m, "%-30.30s %-20.20s %d\n", p->func, f, p->line);
287134e6a03SSteven Rostedt (VMware) }
288134e6a03SSteven Rostedt (VMware) 
branch_stat_show_normal(struct seq_file * m,struct ftrace_branch_data * p,const char * f)289134e6a03SSteven Rostedt (VMware) static int branch_stat_show_normal(struct seq_file *m,
290134e6a03SSteven Rostedt (VMware) 				   struct ftrace_branch_data *p, const char *f)
291134e6a03SSteven Rostedt (VMware) {
292134e6a03SSteven Rostedt (VMware) 	seq_printf(m, "%8lu %8lu ",  p->correct, p->incorrect);
293134e6a03SSteven Rostedt (VMware) 	branch_stat_show(m, p, f);
294134e6a03SSteven Rostedt (VMware) 	return 0;
295134e6a03SSteven Rostedt (VMware) }
296134e6a03SSteven Rostedt (VMware) 
annotate_branch_stat_show(struct seq_file * m,void * v)297134e6a03SSteven Rostedt (VMware) static int annotate_branch_stat_show(struct seq_file *m, void *v)
298134e6a03SSteven Rostedt (VMware) {
299134e6a03SSteven Rostedt (VMware) 	struct ftrace_likely_data *p = v;
300134e6a03SSteven Rostedt (VMware) 	const char *f;
301134e6a03SSteven Rostedt (VMware) 	int l;
302134e6a03SSteven Rostedt (VMware) 
303134e6a03SSteven Rostedt (VMware) 	f = branch_stat_process_file(&p->data);
304134e6a03SSteven Rostedt (VMware) 
305134e6a03SSteven Rostedt (VMware) 	if (!p->constant)
306134e6a03SSteven Rostedt (VMware) 		return branch_stat_show_normal(m, &p->data, f);
307134e6a03SSteven Rostedt (VMware) 
308134e6a03SSteven Rostedt (VMware) 	l = snprintf(NULL, 0, "/%lu", p->constant);
309134e6a03SSteven Rostedt (VMware) 	l = l > 8 ? 0 : 8 - l;
310134e6a03SSteven Rostedt (VMware) 
311134e6a03SSteven Rostedt (VMware) 	seq_printf(m, "%8lu/%lu %*lu ",
312134e6a03SSteven Rostedt (VMware) 		   p->data.correct, p->constant, l, p->data.incorrect);
313134e6a03SSteven Rostedt (VMware) 	branch_stat_show(m, &p->data, f);
31494b80ffdSSteven Rostedt 	return 0;
31594b80ffdSSteven Rostedt }
31694b80ffdSSteven Rostedt 
annotated_branch_stat_start(struct tracer_stat * trace)31742548008SSteven Rostedt static void *annotated_branch_stat_start(struct tracer_stat *trace)
31894b80ffdSSteven Rostedt {
319e302cf3fSFrederic Weisbecker 	return __start_annotated_branch_profile;
32094b80ffdSSteven Rostedt }
32194b80ffdSSteven Rostedt 
322e302cf3fSFrederic Weisbecker static void *
annotated_branch_stat_next(void * v,int idx)323e302cf3fSFrederic Weisbecker annotated_branch_stat_next(void *v, int idx)
324e302cf3fSFrederic Weisbecker {
325134e6a03SSteven Rostedt (VMware) 	struct ftrace_likely_data *p = v;
326e302cf3fSFrederic Weisbecker 
327e302cf3fSFrederic Weisbecker 	++p;
328e302cf3fSFrederic Weisbecker 
329e302cf3fSFrederic Weisbecker 	if ((void *)p >= (void *)__stop_annotated_branch_profile)
330e302cf3fSFrederic Weisbecker 		return NULL;
331e302cf3fSFrederic Weisbecker 
332e302cf3fSFrederic Weisbecker 	return p;
33394b80ffdSSteven Rostedt }
33494b80ffdSSteven Rostedt 
annotated_branch_stat_cmp(const void * p1,const void * p2)33580042c8fSAndy Shevchenko static int annotated_branch_stat_cmp(const void *p1, const void *p2)
33694b80ffdSSteven Rostedt {
33780042c8fSAndy Shevchenko 	const struct ftrace_branch_data *a = p1;
33880042c8fSAndy Shevchenko 	const struct ftrace_branch_data *b = p2;
33994b80ffdSSteven Rostedt 
340e302cf3fSFrederic Weisbecker 	long percent_a, percent_b;
34194b80ffdSSteven Rostedt 
342e302cf3fSFrederic Weisbecker 	percent_a = get_incorrect_percent(a);
343e302cf3fSFrederic Weisbecker 	percent_b = get_incorrect_percent(b);
34494b80ffdSSteven Rostedt 
345e302cf3fSFrederic Weisbecker 	if (percent_a < percent_b)
346e302cf3fSFrederic Weisbecker 		return -1;
347e302cf3fSFrederic Weisbecker 	if (percent_a > percent_b)
348e302cf3fSFrederic Weisbecker 		return 1;
349ede55c9dSSteven Rostedt 
350ede55c9dSSteven Rostedt 	if (a->incorrect < b->incorrect)
351ede55c9dSSteven Rostedt 		return -1;
352ede55c9dSSteven Rostedt 	if (a->incorrect > b->incorrect)
353ede55c9dSSteven Rostedt 		return 1;
354ede55c9dSSteven Rostedt 
355ede55c9dSSteven Rostedt 	/*
356ede55c9dSSteven Rostedt 	 * Since the above shows worse (incorrect) cases
357ede55c9dSSteven Rostedt 	 * first, we continue that by showing best (correct)
358ede55c9dSSteven Rostedt 	 * cases last.
359ede55c9dSSteven Rostedt 	 */
360ede55c9dSSteven Rostedt 	if (a->correct > b->correct)
361ede55c9dSSteven Rostedt 		return -1;
362ede55c9dSSteven Rostedt 	if (a->correct < b->correct)
363ede55c9dSSteven Rostedt 		return 1;
364ede55c9dSSteven Rostedt 
36594b80ffdSSteven Rostedt 	return 0;
36694b80ffdSSteven Rostedt }
36794b80ffdSSteven Rostedt 
368002bb86dSFrederic Weisbecker static struct tracer_stat annotated_branch_stats = {
369002bb86dSFrederic Weisbecker 	.name = "branch_annotated",
370002bb86dSFrederic Weisbecker 	.stat_start = annotated_branch_stat_start,
371002bb86dSFrederic Weisbecker 	.stat_next = annotated_branch_stat_next,
372002bb86dSFrederic Weisbecker 	.stat_cmp = annotated_branch_stat_cmp,
373002bb86dSFrederic Weisbecker 	.stat_headers = annotated_branch_stat_headers,
374134e6a03SSteven Rostedt (VMware) 	.stat_show = annotate_branch_stat_show
375002bb86dSFrederic Weisbecker };
376002bb86dSFrederic Weisbecker 
init_annotated_branch_stats(void)377002bb86dSFrederic Weisbecker __init static int init_annotated_branch_stats(void)
378002bb86dSFrederic Weisbecker {
379002bb86dSFrederic Weisbecker 	int ret;
380002bb86dSFrederic Weisbecker 
381002bb86dSFrederic Weisbecker 	ret = register_stat_tracer(&annotated_branch_stats);
382002bb86dSFrederic Weisbecker 	if (!ret) {
383002bb86dSFrederic Weisbecker 		printk(KERN_WARNING "Warning: could not register "
384002bb86dSFrederic Weisbecker 				    "annotated branches stats\n");
385002bb86dSFrederic Weisbecker 		return 1;
386002bb86dSFrederic Weisbecker 	}
387002bb86dSFrederic Weisbecker 	return 0;
388002bb86dSFrederic Weisbecker }
389002bb86dSFrederic Weisbecker fs_initcall(init_annotated_branch_stats);
390002bb86dSFrederic Weisbecker 
391e302cf3fSFrederic Weisbecker #ifdef CONFIG_PROFILE_ALL_BRANCHES
392e302cf3fSFrederic Weisbecker 
393e302cf3fSFrederic Weisbecker extern unsigned long __start_branch_profile[];
394e302cf3fSFrederic Weisbecker extern unsigned long __stop_branch_profile[];
395e302cf3fSFrederic Weisbecker 
all_branch_stat_headers(struct seq_file * m)396e302cf3fSFrederic Weisbecker static int all_branch_stat_headers(struct seq_file *m)
397e302cf3fSFrederic Weisbecker {
398d79ac28fSRasmus Villemoes 	seq_puts(m, "   miss      hit    % "
399d79ac28fSRasmus Villemoes 		    "       Function                "
400e302cf3fSFrederic Weisbecker 		    "  File              Line\n"
401e302cf3fSFrederic Weisbecker 		    " ------- ---------  - "
402e302cf3fSFrederic Weisbecker 		    "       --------                "
403e302cf3fSFrederic Weisbecker 		    "  ----              ----\n");
404e302cf3fSFrederic Weisbecker 	return 0;
405e302cf3fSFrederic Weisbecker }
406e302cf3fSFrederic Weisbecker 
all_branch_stat_start(struct tracer_stat * trace)40742548008SSteven Rostedt static void *all_branch_stat_start(struct tracer_stat *trace)
408e302cf3fSFrederic Weisbecker {
409e302cf3fSFrederic Weisbecker 	return __start_branch_profile;
410e302cf3fSFrederic Weisbecker }
411e302cf3fSFrederic Weisbecker 
412e302cf3fSFrederic Weisbecker static void *
all_branch_stat_next(void * v,int idx)413e302cf3fSFrederic Weisbecker all_branch_stat_next(void *v, int idx)
414e302cf3fSFrederic Weisbecker {
415e302cf3fSFrederic Weisbecker 	struct ftrace_branch_data *p = v;
416e302cf3fSFrederic Weisbecker 
417e302cf3fSFrederic Weisbecker 	++p;
418e302cf3fSFrederic Weisbecker 
419e302cf3fSFrederic Weisbecker 	if ((void *)p >= (void *)__stop_branch_profile)
420e302cf3fSFrederic Weisbecker 		return NULL;
421e302cf3fSFrederic Weisbecker 
422e302cf3fSFrederic Weisbecker 	return p;
423e302cf3fSFrederic Weisbecker }
424e302cf3fSFrederic Weisbecker 
all_branch_stat_show(struct seq_file * m,void * v)425134e6a03SSteven Rostedt (VMware) static int all_branch_stat_show(struct seq_file *m, void *v)
426134e6a03SSteven Rostedt (VMware) {
427134e6a03SSteven Rostedt (VMware) 	struct ftrace_branch_data *p = v;
428134e6a03SSteven Rostedt (VMware) 	const char *f;
429134e6a03SSteven Rostedt (VMware) 
430134e6a03SSteven Rostedt (VMware) 	f = branch_stat_process_file(p);
431134e6a03SSteven Rostedt (VMware) 	return branch_stat_show_normal(m, p, f);
432134e6a03SSteven Rostedt (VMware) }
433134e6a03SSteven Rostedt (VMware) 
434002bb86dSFrederic Weisbecker static struct tracer_stat all_branch_stats = {
435002bb86dSFrederic Weisbecker 	.name = "branch_all",
436034939b6SFrederic Weisbecker 	.stat_start = all_branch_stat_start,
437034939b6SFrederic Weisbecker 	.stat_next = all_branch_stat_next,
438034939b6SFrederic Weisbecker 	.stat_headers = all_branch_stat_headers,
439134e6a03SSteven Rostedt (VMware) 	.stat_show = all_branch_stat_show
440034939b6SFrederic Weisbecker };
441034939b6SFrederic Weisbecker 
all_annotated_branch_stats(void)442002bb86dSFrederic Weisbecker __init static int all_annotated_branch_stats(void)
443e302cf3fSFrederic Weisbecker {
444e302cf3fSFrederic Weisbecker 	int ret;
445002bb86dSFrederic Weisbecker 
446002bb86dSFrederic Weisbecker 	ret = register_stat_tracer(&all_branch_stats);
447e302cf3fSFrederic Weisbecker 	if (!ret) {
448002bb86dSFrederic Weisbecker 		printk(KERN_WARNING "Warning: could not register "
449002bb86dSFrederic Weisbecker 				    "all branches stats\n");
450e302cf3fSFrederic Weisbecker 		return 1;
451e302cf3fSFrederic Weisbecker 	}
452002bb86dSFrederic Weisbecker 	return 0;
453e302cf3fSFrederic Weisbecker }
454002bb86dSFrederic Weisbecker fs_initcall(all_annotated_branch_stats);
455002bb86dSFrederic Weisbecker #endif /* CONFIG_PROFILE_ALL_BRANCHES */
456