xref: /openbmc/linux/kernel/trace/trace_branch.c (revision f04109bf)
194b80ffdSSteven Rostedt /*
294b80ffdSSteven Rostedt  * unlikely profiler
394b80ffdSSteven Rostedt  *
494b80ffdSSteven Rostedt  * Copyright (C) 2008 Steven Rostedt <srostedt@redhat.com>
594b80ffdSSteven Rostedt  */
694b80ffdSSteven Rostedt #include <linux/kallsyms.h>
794b80ffdSSteven Rostedt #include <linux/seq_file.h>
894b80ffdSSteven Rostedt #include <linux/spinlock.h>
965c6dc6aSFrederic Weisbecker #include <linux/irqflags.h>
1094b80ffdSSteven Rostedt #include <linux/debugfs.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
3194b80ffdSSteven Rostedt probe_likely_condition(struct ftrace_branch_data *f, int val, int expect)
3294b80ffdSSteven Rostedt {
3394b80ffdSSteven Rostedt 	struct trace_array *tr = branch_tracer;
3494b80ffdSSteven Rostedt 	struct ring_buffer_event *event;
3594b80ffdSSteven Rostedt 	struct trace_branch *entry;
3694b80ffdSSteven Rostedt 	unsigned long flags, irq_flags;
3794b80ffdSSteven Rostedt 	int cpu, pc;
3894b80ffdSSteven Rostedt 	const char *p;
3994b80ffdSSteven Rostedt 
4094b80ffdSSteven Rostedt 	/*
4194b80ffdSSteven Rostedt 	 * I would love to save just the ftrace_likely_data pointer, but
4294b80ffdSSteven Rostedt 	 * this code can also be used by modules. Ugly things can happen
4394b80ffdSSteven Rostedt 	 * if the module is unloaded, and then we go and read the
4494b80ffdSSteven Rostedt 	 * pointer.  This is slower, but much safer.
4594b80ffdSSteven Rostedt 	 */
4694b80ffdSSteven Rostedt 
4794b80ffdSSteven Rostedt 	if (unlikely(!tr))
4894b80ffdSSteven Rostedt 		return;
4994b80ffdSSteven Rostedt 
50a5e25883SSteven Rostedt 	local_irq_save(flags);
5194b80ffdSSteven Rostedt 	cpu = raw_smp_processor_id();
5294b80ffdSSteven Rostedt 	if (atomic_inc_return(&tr->data[cpu]->disabled) != 1)
5394b80ffdSSteven Rostedt 		goto out;
5494b80ffdSSteven Rostedt 
5594b80ffdSSteven Rostedt 	event = ring_buffer_lock_reserve(tr->buffer, sizeof(*entry),
5694b80ffdSSteven Rostedt 					 &irq_flags);
5794b80ffdSSteven Rostedt 	if (!event)
5894b80ffdSSteven Rostedt 		goto out;
5994b80ffdSSteven Rostedt 
6094b80ffdSSteven Rostedt 	pc = preempt_count();
6194b80ffdSSteven Rostedt 	entry	= ring_buffer_event_data(event);
6294b80ffdSSteven Rostedt 	tracing_generic_entry_update(&entry->ent, flags, pc);
6394b80ffdSSteven Rostedt 	entry->ent.type		= TRACE_BRANCH;
6494b80ffdSSteven Rostedt 
6594b80ffdSSteven Rostedt 	/* Strip off the path, only save the file */
6694b80ffdSSteven Rostedt 	p = f->file + strlen(f->file);
6794b80ffdSSteven Rostedt 	while (p >= f->file && *p != '/')
6894b80ffdSSteven Rostedt 		p--;
6994b80ffdSSteven Rostedt 	p++;
7094b80ffdSSteven Rostedt 
7194b80ffdSSteven Rostedt 	strncpy(entry->func, f->func, TRACE_FUNC_SIZE);
7294b80ffdSSteven Rostedt 	strncpy(entry->file, p, TRACE_FILE_SIZE);
7394b80ffdSSteven Rostedt 	entry->func[TRACE_FUNC_SIZE] = 0;
7494b80ffdSSteven Rostedt 	entry->file[TRACE_FILE_SIZE] = 0;
7594b80ffdSSteven Rostedt 	entry->line = f->line;
7694b80ffdSSteven Rostedt 	entry->correct = val == expect;
7794b80ffdSSteven Rostedt 
7894b80ffdSSteven Rostedt 	ring_buffer_unlock_commit(tr->buffer, event, irq_flags);
7994b80ffdSSteven Rostedt 
8094b80ffdSSteven Rostedt  out:
8194b80ffdSSteven Rostedt 	atomic_dec(&tr->data[cpu]->disabled);
82a5e25883SSteven Rostedt 	local_irq_restore(flags);
8394b80ffdSSteven Rostedt }
8494b80ffdSSteven Rostedt 
8594b80ffdSSteven Rostedt static inline
8694b80ffdSSteven Rostedt void trace_likely_condition(struct ftrace_branch_data *f, int val, int expect)
8794b80ffdSSteven Rostedt {
8894b80ffdSSteven Rostedt 	if (!branch_tracing_enabled)
8994b80ffdSSteven Rostedt 		return;
9094b80ffdSSteven Rostedt 
9194b80ffdSSteven Rostedt 	probe_likely_condition(f, val, expect);
9294b80ffdSSteven Rostedt }
9394b80ffdSSteven Rostedt 
9494b80ffdSSteven Rostedt int enable_branch_tracing(struct trace_array *tr)
9594b80ffdSSteven Rostedt {
9694b80ffdSSteven Rostedt 	int ret = 0;
9794b80ffdSSteven Rostedt 
9894b80ffdSSteven Rostedt 	mutex_lock(&branch_tracing_mutex);
9994b80ffdSSteven Rostedt 	branch_tracer = tr;
10094b80ffdSSteven Rostedt 	/*
10194b80ffdSSteven Rostedt 	 * Must be seen before enabling. The reader is a condition
10294b80ffdSSteven Rostedt 	 * where we do not need a matching rmb()
10394b80ffdSSteven Rostedt 	 */
10494b80ffdSSteven Rostedt 	smp_wmb();
10594b80ffdSSteven Rostedt 	branch_tracing_enabled++;
10694b80ffdSSteven Rostedt 	mutex_unlock(&branch_tracing_mutex);
10794b80ffdSSteven Rostedt 
10894b80ffdSSteven Rostedt 	return ret;
10994b80ffdSSteven Rostedt }
11094b80ffdSSteven Rostedt 
11194b80ffdSSteven Rostedt void disable_branch_tracing(void)
11294b80ffdSSteven Rostedt {
11394b80ffdSSteven Rostedt 	mutex_lock(&branch_tracing_mutex);
11494b80ffdSSteven Rostedt 
11594b80ffdSSteven Rostedt 	if (!branch_tracing_enabled)
11694b80ffdSSteven Rostedt 		goto out_unlock;
11794b80ffdSSteven Rostedt 
11894b80ffdSSteven Rostedt 	branch_tracing_enabled--;
11994b80ffdSSteven Rostedt 
12094b80ffdSSteven Rostedt  out_unlock:
12194b80ffdSSteven Rostedt 	mutex_unlock(&branch_tracing_mutex);
12294b80ffdSSteven Rostedt }
12394b80ffdSSteven Rostedt 
12494b80ffdSSteven Rostedt static void start_branch_trace(struct trace_array *tr)
12594b80ffdSSteven Rostedt {
12694b80ffdSSteven Rostedt 	enable_branch_tracing(tr);
12794b80ffdSSteven Rostedt }
12894b80ffdSSteven Rostedt 
12994b80ffdSSteven Rostedt static void stop_branch_trace(struct trace_array *tr)
13094b80ffdSSteven Rostedt {
13194b80ffdSSteven Rostedt 	disable_branch_tracing();
13294b80ffdSSteven Rostedt }
13394b80ffdSSteven Rostedt 
1341c80025aSFrederic Weisbecker static int branch_trace_init(struct trace_array *tr)
13594b80ffdSSteven Rostedt {
136f04109bfSArnaldo Carvalho de Melo 	tracing_reset_online_cpus(tr);
13794b80ffdSSteven Rostedt 	start_branch_trace(tr);
1381c80025aSFrederic Weisbecker 	return 0;
13994b80ffdSSteven Rostedt }
14094b80ffdSSteven Rostedt 
14194b80ffdSSteven Rostedt static void branch_trace_reset(struct trace_array *tr)
14294b80ffdSSteven Rostedt {
14394b80ffdSSteven Rostedt 	stop_branch_trace(tr);
14494b80ffdSSteven Rostedt }
14594b80ffdSSteven Rostedt 
146f633cef0SSteven Rostedt static int
147f633cef0SSteven Rostedt trace_print_print(struct trace_seq *s, struct trace_entry *entry, int flags)
148f633cef0SSteven Rostedt {
149f633cef0SSteven Rostedt 	struct print_entry *field;
150f633cef0SSteven Rostedt 
151f633cef0SSteven Rostedt 	trace_assign_type(field, entry);
152f633cef0SSteven Rostedt 
153f633cef0SSteven Rostedt 	if (seq_print_ip_sym(s, field->ip, flags))
154f633cef0SSteven Rostedt 		goto partial;
155f633cef0SSteven Rostedt 
156f633cef0SSteven Rostedt 	if (trace_seq_printf(s, ": %s", field->buf))
157f633cef0SSteven Rostedt 		goto partial;
158f633cef0SSteven Rostedt 
159f633cef0SSteven Rostedt  partial:
160f633cef0SSteven Rostedt 	return TRACE_TYPE_PARTIAL_LINE;
161f633cef0SSteven Rostedt }
162f633cef0SSteven Rostedt 
163f633cef0SSteven Rostedt static int
164f633cef0SSteven Rostedt trace_branch_print(struct trace_seq *s, struct trace_entry *entry, int flags)
165f633cef0SSteven Rostedt {
166f633cef0SSteven Rostedt 	struct trace_branch *field;
167f633cef0SSteven Rostedt 
168f633cef0SSteven Rostedt 	trace_assign_type(field, entry);
169f633cef0SSteven Rostedt 
170f633cef0SSteven Rostedt 	if (trace_seq_printf(s, "[%s] %s:%s:%d\n",
171f633cef0SSteven Rostedt 			     field->correct ? "  ok  " : " MISS ",
172f633cef0SSteven Rostedt 			     field->func,
173f633cef0SSteven Rostedt 			     field->file,
174f633cef0SSteven Rostedt 			     field->line))
175f633cef0SSteven Rostedt 		return TRACE_TYPE_PARTIAL_LINE;
176f633cef0SSteven Rostedt 
177f633cef0SSteven Rostedt 	return 0;
178f633cef0SSteven Rostedt }
179f633cef0SSteven Rostedt 
180e302cf3fSFrederic Weisbecker 
181f633cef0SSteven Rostedt static struct trace_event trace_branch_event = {
182f633cef0SSteven Rostedt 	.type	 	= TRACE_BRANCH,
183f633cef0SSteven Rostedt 	.trace		= trace_branch_print,
184f633cef0SSteven Rostedt 	.latency_trace	= trace_branch_print,
185f633cef0SSteven Rostedt 	.raw		= trace_nop_print,
186f633cef0SSteven Rostedt 	.hex		= trace_nop_print,
187f633cef0SSteven Rostedt 	.binary		= trace_nop_print,
188f633cef0SSteven Rostedt };
189f633cef0SSteven Rostedt 
190002bb86dSFrederic Weisbecker static struct tracer branch_trace __read_mostly =
191002bb86dSFrederic Weisbecker {
192002bb86dSFrederic Weisbecker 	.name		= "branch",
193002bb86dSFrederic Weisbecker 	.init		= branch_trace_init,
194002bb86dSFrederic Weisbecker 	.reset		= branch_trace_reset,
195002bb86dSFrederic Weisbecker #ifdef CONFIG_FTRACE_SELFTEST
196002bb86dSFrederic Weisbecker 	.selftest	= trace_selftest_startup_branch,
197002bb86dSFrederic Weisbecker #endif /* CONFIG_FTRACE_SELFTEST */
198002bb86dSFrederic Weisbecker };
199002bb86dSFrederic Weisbecker 
200002bb86dSFrederic Weisbecker __init static int init_branch_tracer(void)
201002bb86dSFrederic Weisbecker {
202002bb86dSFrederic Weisbecker 	int ret;
203002bb86dSFrederic Weisbecker 
204002bb86dSFrederic Weisbecker 	ret = register_ftrace_event(&trace_branch_event);
205002bb86dSFrederic Weisbecker 	if (!ret) {
206002bb86dSFrederic Weisbecker 		printk(KERN_WARNING "Warning: could not register "
207002bb86dSFrederic Weisbecker 				    "branch events\n");
208002bb86dSFrederic Weisbecker 		return 1;
209002bb86dSFrederic Weisbecker 	}
210002bb86dSFrederic Weisbecker 	return register_tracer(&branch_trace);
211002bb86dSFrederic Weisbecker }
212002bb86dSFrederic Weisbecker device_initcall(init_branch_tracer);
213002bb86dSFrederic Weisbecker 
21494b80ffdSSteven Rostedt #else
21594b80ffdSSteven Rostedt static inline
21694b80ffdSSteven Rostedt void trace_likely_condition(struct ftrace_branch_data *f, int val, int expect)
21794b80ffdSSteven Rostedt {
21894b80ffdSSteven Rostedt }
21994b80ffdSSteven Rostedt #endif /* CONFIG_BRANCH_TRACER */
22094b80ffdSSteven Rostedt 
22194b80ffdSSteven Rostedt void ftrace_likely_update(struct ftrace_branch_data *f, int val, int expect)
22294b80ffdSSteven Rostedt {
22394b80ffdSSteven Rostedt 	/*
22494b80ffdSSteven Rostedt 	 * I would love to have a trace point here instead, but the
22594b80ffdSSteven Rostedt 	 * trace point code is so inundated with unlikely and likely
22694b80ffdSSteven Rostedt 	 * conditions that the recursive nightmare that exists is too
22794b80ffdSSteven Rostedt 	 * much to try to get working. At least for now.
22894b80ffdSSteven Rostedt 	 */
22994b80ffdSSteven Rostedt 	trace_likely_condition(f, val, expect);
23094b80ffdSSteven Rostedt 
23194b80ffdSSteven Rostedt 	/* FIXME: Make this atomic! */
23294b80ffdSSteven Rostedt 	if (val == expect)
23394b80ffdSSteven Rostedt 		f->correct++;
23494b80ffdSSteven Rostedt 	else
23594b80ffdSSteven Rostedt 		f->incorrect++;
23694b80ffdSSteven Rostedt }
23794b80ffdSSteven Rostedt EXPORT_SYMBOL(ftrace_likely_update);
23894b80ffdSSteven Rostedt 
239e302cf3fSFrederic Weisbecker extern unsigned long __start_annotated_branch_profile[];
240e302cf3fSFrederic Weisbecker extern unsigned long __stop_annotated_branch_profile[];
24194b80ffdSSteven Rostedt 
242e302cf3fSFrederic Weisbecker static int annotated_branch_stat_headers(struct seq_file *m)
24394b80ffdSSteven Rostedt {
2442bcd521aSSteven Rostedt 	seq_printf(m, " correct incorrect  %% ");
2452bcd521aSSteven Rostedt 	seq_printf(m, "       Function                "
24694b80ffdSSteven Rostedt 			      "  File              Line\n"
24794b80ffdSSteven Rostedt 			      " ------- ---------  - "
24894b80ffdSSteven Rostedt 			      "       --------                "
24994b80ffdSSteven Rostedt 			      "  ----              ----\n");
25094b80ffdSSteven Rostedt 	return 0;
25194b80ffdSSteven Rostedt }
25294b80ffdSSteven Rostedt 
253e302cf3fSFrederic Weisbecker static inline long get_incorrect_percent(struct ftrace_branch_data *p)
254e302cf3fSFrederic Weisbecker {
255e302cf3fSFrederic Weisbecker 	long percent;
256e302cf3fSFrederic Weisbecker 
257e302cf3fSFrederic Weisbecker 	if (p->correct) {
258e302cf3fSFrederic Weisbecker 		percent = p->incorrect * 100;
259e302cf3fSFrederic Weisbecker 		percent /= p->correct + p->incorrect;
260e302cf3fSFrederic Weisbecker 	} else
261e302cf3fSFrederic Weisbecker 		percent = p->incorrect ? 100 : -1;
262e302cf3fSFrederic Weisbecker 
263e302cf3fSFrederic Weisbecker 	return percent;
264e302cf3fSFrederic Weisbecker }
265e302cf3fSFrederic Weisbecker 
266e302cf3fSFrederic Weisbecker static int branch_stat_show(struct seq_file *m, void *v)
267e302cf3fSFrederic Weisbecker {
268e302cf3fSFrederic Weisbecker 	struct ftrace_branch_data *p = v;
269e302cf3fSFrederic Weisbecker 	const char *f;
270e302cf3fSFrederic Weisbecker 	long percent;
271e302cf3fSFrederic Weisbecker 
27294b80ffdSSteven Rostedt 	/* Only print the file, not the path */
27394b80ffdSSteven Rostedt 	f = p->file + strlen(p->file);
27494b80ffdSSteven Rostedt 	while (f >= p->file && *f != '/')
27594b80ffdSSteven Rostedt 		f--;
27694b80ffdSSteven Rostedt 	f++;
27794b80ffdSSteven Rostedt 
2782bcd521aSSteven Rostedt 	/*
2792bcd521aSSteven Rostedt 	 * The miss is overlayed on correct, and hit on incorrect.
2802bcd521aSSteven Rostedt 	 */
281e302cf3fSFrederic Weisbecker 	percent = get_incorrect_percent(p);
28294b80ffdSSteven Rostedt 
283bac28bfeSSteven Rostedt 	seq_printf(m, "%8lu %8lu ",  p->correct, p->incorrect);
284bac28bfeSSteven Rostedt 	if (percent < 0)
285bac28bfeSSteven Rostedt 		seq_printf(m, "  X ");
286bac28bfeSSteven Rostedt 	else
287bac28bfeSSteven Rostedt 		seq_printf(m, "%3ld ", percent);
28894b80ffdSSteven Rostedt 	seq_printf(m, "%-30.30s %-20.20s %d\n", p->func, f, p->line);
28994b80ffdSSteven Rostedt 	return 0;
29094b80ffdSSteven Rostedt }
29194b80ffdSSteven Rostedt 
292e302cf3fSFrederic Weisbecker static void *annotated_branch_stat_start(void)
29394b80ffdSSteven Rostedt {
294e302cf3fSFrederic Weisbecker 	return __start_annotated_branch_profile;
29594b80ffdSSteven Rostedt }
29694b80ffdSSteven Rostedt 
297e302cf3fSFrederic Weisbecker static void *
298e302cf3fSFrederic Weisbecker annotated_branch_stat_next(void *v, int idx)
299e302cf3fSFrederic Weisbecker {
300e302cf3fSFrederic Weisbecker 	struct ftrace_branch_data *p = v;
301e302cf3fSFrederic Weisbecker 
302e302cf3fSFrederic Weisbecker 	++p;
303e302cf3fSFrederic Weisbecker 
304e302cf3fSFrederic Weisbecker 	if ((void *)p >= (void *)__stop_annotated_branch_profile)
305e302cf3fSFrederic Weisbecker 		return NULL;
306e302cf3fSFrederic Weisbecker 
307e302cf3fSFrederic Weisbecker 	return p;
30894b80ffdSSteven Rostedt }
30994b80ffdSSteven Rostedt 
310e302cf3fSFrederic Weisbecker static int annotated_branch_stat_cmp(void *p1, void *p2)
31194b80ffdSSteven Rostedt {
312e302cf3fSFrederic Weisbecker 	struct ftrace_branch_data *a = p1;
313e302cf3fSFrederic Weisbecker 	struct ftrace_branch_data *b = p2;
31494b80ffdSSteven Rostedt 
315e302cf3fSFrederic Weisbecker 	long percent_a, percent_b;
31694b80ffdSSteven Rostedt 
317e302cf3fSFrederic Weisbecker 	percent_a = get_incorrect_percent(a);
318e302cf3fSFrederic Weisbecker 	percent_b = get_incorrect_percent(b);
31994b80ffdSSteven Rostedt 
320e302cf3fSFrederic Weisbecker 	if (percent_a < percent_b)
321e302cf3fSFrederic Weisbecker 		return -1;
322e302cf3fSFrederic Weisbecker 	if (percent_a > percent_b)
323e302cf3fSFrederic Weisbecker 		return 1;
324e302cf3fSFrederic Weisbecker 	else
32594b80ffdSSteven Rostedt 		return 0;
32694b80ffdSSteven Rostedt }
32794b80ffdSSteven Rostedt 
328002bb86dSFrederic Weisbecker static struct tracer_stat annotated_branch_stats = {
329002bb86dSFrederic Weisbecker 	.name = "branch_annotated",
330002bb86dSFrederic Weisbecker 	.stat_start = annotated_branch_stat_start,
331002bb86dSFrederic Weisbecker 	.stat_next = annotated_branch_stat_next,
332002bb86dSFrederic Weisbecker 	.stat_cmp = annotated_branch_stat_cmp,
333002bb86dSFrederic Weisbecker 	.stat_headers = annotated_branch_stat_headers,
334002bb86dSFrederic Weisbecker 	.stat_show = branch_stat_show
335002bb86dSFrederic Weisbecker };
336002bb86dSFrederic Weisbecker 
337002bb86dSFrederic Weisbecker __init static int init_annotated_branch_stats(void)
338002bb86dSFrederic Weisbecker {
339002bb86dSFrederic Weisbecker 	int ret;
340002bb86dSFrederic Weisbecker 
341002bb86dSFrederic Weisbecker 	ret = register_stat_tracer(&annotated_branch_stats);
342002bb86dSFrederic Weisbecker 	if (!ret) {
343002bb86dSFrederic Weisbecker 		printk(KERN_WARNING "Warning: could not register "
344002bb86dSFrederic Weisbecker 				    "annotated branches stats\n");
345002bb86dSFrederic Weisbecker 		return 1;
346002bb86dSFrederic Weisbecker 	}
347002bb86dSFrederic Weisbecker 	return 0;
348002bb86dSFrederic Weisbecker }
349002bb86dSFrederic Weisbecker fs_initcall(init_annotated_branch_stats);
350002bb86dSFrederic Weisbecker 
351e302cf3fSFrederic Weisbecker #ifdef CONFIG_PROFILE_ALL_BRANCHES
352e302cf3fSFrederic Weisbecker 
353e302cf3fSFrederic Weisbecker extern unsigned long __start_branch_profile[];
354e302cf3fSFrederic Weisbecker extern unsigned long __stop_branch_profile[];
355e302cf3fSFrederic Weisbecker 
356e302cf3fSFrederic Weisbecker static int all_branch_stat_headers(struct seq_file *m)
357e302cf3fSFrederic Weisbecker {
358e302cf3fSFrederic Weisbecker 	seq_printf(m, "   miss      hit    %% ");
359e302cf3fSFrederic Weisbecker 	seq_printf(m, "       Function                "
360e302cf3fSFrederic Weisbecker 			      "  File              Line\n"
361e302cf3fSFrederic Weisbecker 			      " ------- ---------  - "
362e302cf3fSFrederic Weisbecker 			      "       --------                "
363e302cf3fSFrederic Weisbecker 			      "  ----              ----\n");
364e302cf3fSFrederic Weisbecker 	return 0;
365e302cf3fSFrederic Weisbecker }
366e302cf3fSFrederic Weisbecker 
367e302cf3fSFrederic Weisbecker static void *all_branch_stat_start(void)
368e302cf3fSFrederic Weisbecker {
369e302cf3fSFrederic Weisbecker 	return __start_branch_profile;
370e302cf3fSFrederic Weisbecker }
371e302cf3fSFrederic Weisbecker 
372e302cf3fSFrederic Weisbecker static void *
373e302cf3fSFrederic Weisbecker all_branch_stat_next(void *v, int idx)
374e302cf3fSFrederic Weisbecker {
375e302cf3fSFrederic Weisbecker 	struct ftrace_branch_data *p = v;
376e302cf3fSFrederic Weisbecker 
377e302cf3fSFrederic Weisbecker 	++p;
378e302cf3fSFrederic Weisbecker 
379e302cf3fSFrederic Weisbecker 	if ((void *)p >= (void *)__stop_branch_profile)
380e302cf3fSFrederic Weisbecker 		return NULL;
381e302cf3fSFrederic Weisbecker 
382e302cf3fSFrederic Weisbecker 	return p;
383e302cf3fSFrederic Weisbecker }
384e302cf3fSFrederic Weisbecker 
385002bb86dSFrederic Weisbecker static struct tracer_stat all_branch_stats = {
386002bb86dSFrederic Weisbecker 	.name = "branch_all",
387034939b6SFrederic Weisbecker 	.stat_start = all_branch_stat_start,
388034939b6SFrederic Weisbecker 	.stat_next = all_branch_stat_next,
389034939b6SFrederic Weisbecker 	.stat_headers = all_branch_stat_headers,
390002bb86dSFrederic Weisbecker 	.stat_show = branch_stat_show
391034939b6SFrederic Weisbecker };
392034939b6SFrederic Weisbecker 
393002bb86dSFrederic Weisbecker __init static int all_annotated_branch_stats(void)
394e302cf3fSFrederic Weisbecker {
395e302cf3fSFrederic Weisbecker 	int ret;
396002bb86dSFrederic Weisbecker 
397002bb86dSFrederic Weisbecker 	ret = register_stat_tracer(&all_branch_stats);
398e302cf3fSFrederic Weisbecker 	if (!ret) {
399002bb86dSFrederic Weisbecker 		printk(KERN_WARNING "Warning: could not register "
400002bb86dSFrederic Weisbecker 				    "all branches stats\n");
401e302cf3fSFrederic Weisbecker 		return 1;
402e302cf3fSFrederic Weisbecker 	}
403002bb86dSFrederic Weisbecker 	return 0;
404e302cf3fSFrederic Weisbecker }
405002bb86dSFrederic Weisbecker fs_initcall(all_annotated_branch_stats);
406002bb86dSFrederic Weisbecker #endif /* CONFIG_PROFILE_ALL_BRANCHES */
407