trace_branch.c 7.3 KB
Newer Older
1 2 3 4 5 6 7 8
/*
 * unlikely profiler
 *
 * Copyright (C) 2008 Steven Rostedt <srostedt@redhat.com>
 */
#include <linux/kallsyms.h>
#include <linux/seq_file.h>
#include <linux/spinlock.h>
9
#include <linux/irqflags.h>
10 11 12 13 14 15 16 17 18
#include <linux/debugfs.h>
#include <linux/uaccess.h>
#include <linux/module.h>
#include <linux/ftrace.h>
#include <linux/hash.h>
#include <linux/fs.h>
#include <asm/local.h>
#include "trace.h"

19
#ifdef CONFIG_BRANCH_TRACER
20

21 22 23
static int branch_tracing_enabled __read_mostly;
static DEFINE_MUTEX(branch_tracing_mutex);
static struct trace_array *branch_tracer;
24 25

static void
26
probe_likely_condition(struct ftrace_branch_data *f, int val, int expect)
27
{
28
	struct trace_array *tr = branch_tracer;
29
	struct ring_buffer_event *event;
30
	struct trace_branch *entry;
31 32 33 34 35 36 37 38 39 40 41 42 43 44
	unsigned long flags, irq_flags;
	int cpu, pc;
	const char *p;

	/*
	 * I would love to save just the ftrace_likely_data pointer, but
	 * this code can also be used by modules. Ugly things can happen
	 * if the module is unloaded, and then we go and read the
	 * pointer.  This is slower, but much safer.
	 */

	if (unlikely(!tr))
		return;

45
	raw_local_irq_save(flags);
46 47 48 49 50 51 52 53 54 55 56 57
	cpu = raw_smp_processor_id();
	if (atomic_inc_return(&tr->data[cpu]->disabled) != 1)
		goto out;

	event = ring_buffer_lock_reserve(tr->buffer, sizeof(*entry),
					 &irq_flags);
	if (!event)
		goto out;

	pc = preempt_count();
	entry	= ring_buffer_event_data(event);
	tracing_generic_entry_update(&entry->ent, flags, pc);
58
	entry->ent.type		= TRACE_BRANCH;
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76

	/* Strip off the path, only save the file */
	p = f->file + strlen(f->file);
	while (p >= f->file && *p != '/')
		p--;
	p++;

	strncpy(entry->func, f->func, TRACE_FUNC_SIZE);
	strncpy(entry->file, p, TRACE_FILE_SIZE);
	entry->func[TRACE_FUNC_SIZE] = 0;
	entry->file[TRACE_FILE_SIZE] = 0;
	entry->line = f->line;
	entry->correct = val == expect;

	ring_buffer_unlock_commit(tr->buffer, event, irq_flags);

 out:
	atomic_dec(&tr->data[cpu]->disabled);
77
	raw_local_irq_restore(flags);
78 79 80
}

static inline
81
void trace_likely_condition(struct ftrace_branch_data *f, int val, int expect)
82
{
83
	if (!branch_tracing_enabled)
84 85 86 87 88
		return;

	probe_likely_condition(f, val, expect);
}

89
int enable_branch_tracing(struct trace_array *tr)
90 91 92
{
	int ret = 0;

93 94
	mutex_lock(&branch_tracing_mutex);
	branch_tracer = tr;
95 96 97 98 99
	/*
	 * Must be seen before enabling. The reader is a condition
	 * where we do not need a matching rmb()
	 */
	smp_wmb();
100 101
	branch_tracing_enabled++;
	mutex_unlock(&branch_tracing_mutex);
102 103 104 105

	return ret;
}

106
void disable_branch_tracing(void)
107
{
108
	mutex_lock(&branch_tracing_mutex);
109

110
	if (!branch_tracing_enabled)
111 112
		goto out_unlock;

113
	branch_tracing_enabled--;
114 115

 out_unlock:
116
	mutex_unlock(&branch_tracing_mutex);
117
}
S
Steven Rostedt 已提交
118 119 120 121 122 123 124 125 126 127 128

static void start_branch_trace(struct trace_array *tr)
{
	enable_branch_tracing(tr);
}

static void stop_branch_trace(struct trace_array *tr)
{
	disable_branch_tracing();
}

129
static int branch_trace_init(struct trace_array *tr)
S
Steven Rostedt 已提交
130 131 132 133 134 135 136
{
	int cpu;

	for_each_online_cpu(cpu)
		tracing_reset(tr, cpu);

	start_branch_trace(tr);
137
	return 0;
S
Steven Rostedt 已提交
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
}

static void branch_trace_reset(struct trace_array *tr)
{
	stop_branch_trace(tr);
}

struct tracer branch_trace __read_mostly =
{
	.name		= "branch",
	.init		= branch_trace_init,
	.reset		= branch_trace_reset,
#ifdef CONFIG_FTRACE_SELFTEST
	.selftest	= trace_selftest_startup_branch,
#endif
};

__init static int init_branch_trace(void)
{
	return register_tracer(&branch_trace);
}

device_initcall(init_branch_trace);
161 162
#else
static inline
163
void trace_likely_condition(struct ftrace_branch_data *f, int val, int expect)
164 165
{
}
166
#endif /* CONFIG_BRANCH_TRACER */
167

168
void ftrace_likely_update(struct ftrace_branch_data *f, int val, int expect)
169
{
170 171 172 173 174 175 176 177
	/*
	 * I would love to have a trace point here instead, but the
	 * trace point code is so inundated with unlikely and likely
	 * conditions that the recursive nightmare that exists is too
	 * much to try to get working. At least for now.
	 */
	trace_likely_condition(f, val, expect);

178 179 180 181 182 183 184 185 186 187 188
	/* FIXME: Make this atomic! */
	if (val == expect)
		f->correct++;
	else
		f->incorrect++;
}
EXPORT_SYMBOL(ftrace_likely_update);

struct ftrace_pointer {
	void		*start;
	void		*stop;
189
	int		hit;
190 191 192 193 194
};

static void *
t_next(struct seq_file *m, void *v, loff_t *pos)
{
195
	const struct ftrace_pointer *f = m->private;
196
	struct ftrace_branch_data *p = v;
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227

	(*pos)++;

	if (v == (void *)1)
		return f->start;

	++p;

	if ((void *)p >= (void *)f->stop)
		return NULL;

	return p;
}

static void *t_start(struct seq_file *m, loff_t *pos)
{
	void *t = (void *)1;
	loff_t l = 0;

	for (; t && l < *pos; t = t_next(m, t, &l))
		;

	return t;
}

static void t_stop(struct seq_file *m, void *p)
{
}

static int t_show(struct seq_file *m, void *v)
{
228
	const struct ftrace_pointer *fp = m->private;
229
	struct ftrace_branch_data *p = v;
230
	const char *f;
231
	long percent;
232 233

	if (v == (void *)1) {
234 235 236 237 238
		if (fp->hit)
			seq_printf(m, "   miss      hit    %% ");
		else
			seq_printf(m, " correct incorrect  %% ");
		seq_printf(m, "       Function                "
239 240 241 242 243 244 245 246 247 248 249 250 251
			      "  File              Line\n"
			      " ------- ---------  - "
			      "       --------                "
			      "  ----              ----\n");
		return 0;
	}

	/* Only print the file, not the path */
	f = p->file + strlen(p->file);
	while (f >= p->file && *f != '/')
		f--;
	f++;

252 253 254
	/*
	 * The miss is overlayed on correct, and hit on incorrect.
	 */
255 256 257 258
	if (p->correct) {
		percent = p->incorrect * 100;
		percent /= p->correct + p->incorrect;
	} else
259
		percent = p->incorrect ? 100 : -1;
260

261 262 263 264 265
	seq_printf(m, "%8lu %8lu ",  p->correct, p->incorrect);
	if (percent < 0)
		seq_printf(m, "  X ");
	else
		seq_printf(m, "%3ld ", percent);
266 267 268 269 270 271 272 273 274 275 276
	seq_printf(m, "%-30.30s %-20.20s %d\n", p->func, f, p->line);
	return 0;
}

static struct seq_operations tracing_likely_seq_ops = {
	.start		= t_start,
	.next		= t_next,
	.stop		= t_stop,
	.show		= t_show,
};

277
static int tracing_branch_open(struct inode *inode, struct file *file)
278 279 280 281 282 283 284 285 286 287 288 289
{
	int ret;

	ret = seq_open(file, &tracing_likely_seq_ops);
	if (!ret) {
		struct seq_file *m = file->private_data;
		m->private = (void *)inode->i_private;
	}

	return ret;
}

290 291
static const struct file_operations tracing_branch_fops = {
	.open		= tracing_branch_open,
292 293 294 295
	.read		= seq_read,
	.llseek		= seq_lseek,
};

296 297 298 299
#ifdef CONFIG_PROFILE_ALL_BRANCHES
extern unsigned long __start_branch_profile[];
extern unsigned long __stop_branch_profile[];

300
static const struct ftrace_pointer ftrace_branch_pos = {
301 302 303 304 305 306 307
	.start			= __start_branch_profile,
	.stop			= __stop_branch_profile,
	.hit			= 1,
};

#endif /* CONFIG_PROFILE_ALL_BRANCHES */

308 309
extern unsigned long __start_annotated_branch_profile[];
extern unsigned long __stop_annotated_branch_profile[];
310

311 312 313
static const struct ftrace_pointer ftrace_annotated_branch_pos = {
	.start			= __start_annotated_branch_profile,
	.stop			= __stop_annotated_branch_profile,
314 315
};

316
static __init int ftrace_branch_init(void)
317 318 319 320 321 322
{
	struct dentry *d_tracer;
	struct dentry *entry;

	d_tracer = tracing_init_dentry();

323
	entry = debugfs_create_file("profile_annotated_branch", 0444, d_tracer,
324
				    (void *)&ftrace_annotated_branch_pos,
325
				    &tracing_branch_fops);
326
	if (!entry)
327 328
		pr_warning("Could not create debugfs "
			   "'profile_annotatet_branch' entry\n");
329

330 331
#ifdef CONFIG_PROFILE_ALL_BRANCHES
	entry = debugfs_create_file("profile_branch", 0444, d_tracer,
332
				    (void *)&ftrace_branch_pos,
333 334 335 336 337 338
				    &tracing_branch_fops);
	if (!entry)
		pr_warning("Could not create debugfs"
			   " 'profile_branch' entry\n");
#endif

339 340 341
	return 0;
}

342
device_initcall(ftrace_branch_init);