diff options
Diffstat (limited to 'kernel/trace/trace_branch.c')
-rw-r--r-- | kernel/trace/trace_branch.c | 320 |
1 files changed, 320 insertions, 0 deletions
diff --git a/kernel/trace/trace_branch.c b/kernel/trace/trace_branch.c new file mode 100644 index 000000000000..85265553918f --- /dev/null +++ b/kernel/trace/trace_branch.c | |||
@@ -0,0 +1,320 @@ | |||
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> | ||
9 | #include <linux/debugfs.h> | ||
10 | #include <linux/uaccess.h> | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/ftrace.h> | ||
13 | #include <linux/hash.h> | ||
14 | #include <linux/fs.h> | ||
15 | #include <asm/local.h> | ||
16 | #include "trace.h" | ||
17 | |||
18 | #ifdef CONFIG_BRANCH_TRACER | ||
19 | |||
20 | static int branch_tracing_enabled __read_mostly; | ||
21 | static DEFINE_MUTEX(branch_tracing_mutex); | ||
22 | static struct trace_array *branch_tracer; | ||
23 | |||
24 | static void | ||
25 | probe_likely_condition(struct ftrace_branch_data *f, int val, int expect) | ||
26 | { | ||
27 | struct trace_array *tr = branch_tracer; | ||
28 | struct ring_buffer_event *event; | ||
29 | struct trace_branch *entry; | ||
30 | unsigned long flags, irq_flags; | ||
31 | int cpu, pc; | ||
32 | const char *p; | ||
33 | |||
34 | /* | ||
35 | * I would love to save just the ftrace_likely_data pointer, but | ||
36 | * this code can also be used by modules. Ugly things can happen | ||
37 | * if the module is unloaded, and then we go and read the | ||
38 | * pointer. This is slower, but much safer. | ||
39 | */ | ||
40 | |||
41 | if (unlikely(!tr)) | ||
42 | return; | ||
43 | |||
44 | local_irq_save(flags); | ||
45 | cpu = raw_smp_processor_id(); | ||
46 | if (atomic_inc_return(&tr->data[cpu]->disabled) != 1) | ||
47 | goto out; | ||
48 | |||
49 | event = ring_buffer_lock_reserve(tr->buffer, sizeof(*entry), | ||
50 | &irq_flags); | ||
51 | if (!event) | ||
52 | goto out; | ||
53 | |||
54 | pc = preempt_count(); | ||
55 | entry = ring_buffer_event_data(event); | ||
56 | tracing_generic_entry_update(&entry->ent, flags, pc); | ||
57 | entry->ent.type = TRACE_BRANCH; | ||
58 | |||
59 | /* Strip off the path, only save the file */ | ||
60 | p = f->file + strlen(f->file); | ||
61 | while (p >= f->file && *p != '/') | ||
62 | p--; | ||
63 | p++; | ||
64 | |||
65 | strncpy(entry->func, f->func, TRACE_FUNC_SIZE); | ||
66 | strncpy(entry->file, p, TRACE_FILE_SIZE); | ||
67 | entry->func[TRACE_FUNC_SIZE] = 0; | ||
68 | entry->file[TRACE_FILE_SIZE] = 0; | ||
69 | entry->line = f->line; | ||
70 | entry->correct = val == expect; | ||
71 | |||
72 | ring_buffer_unlock_commit(tr->buffer, event, irq_flags); | ||
73 | |||
74 | out: | ||
75 | atomic_dec(&tr->data[cpu]->disabled); | ||
76 | local_irq_restore(flags); | ||
77 | } | ||
78 | |||
79 | static inline | ||
80 | void trace_likely_condition(struct ftrace_branch_data *f, int val, int expect) | ||
81 | { | ||
82 | if (!branch_tracing_enabled) | ||
83 | return; | ||
84 | |||
85 | probe_likely_condition(f, val, expect); | ||
86 | } | ||
87 | |||
88 | int enable_branch_tracing(struct trace_array *tr) | ||
89 | { | ||
90 | int ret = 0; | ||
91 | |||
92 | mutex_lock(&branch_tracing_mutex); | ||
93 | branch_tracer = tr; | ||
94 | /* | ||
95 | * Must be seen before enabling. The reader is a condition | ||
96 | * where we do not need a matching rmb() | ||
97 | */ | ||
98 | smp_wmb(); | ||
99 | branch_tracing_enabled++; | ||
100 | mutex_unlock(&branch_tracing_mutex); | ||
101 | |||
102 | return ret; | ||
103 | } | ||
104 | |||
105 | void disable_branch_tracing(void) | ||
106 | { | ||
107 | mutex_lock(&branch_tracing_mutex); | ||
108 | |||
109 | if (!branch_tracing_enabled) | ||
110 | goto out_unlock; | ||
111 | |||
112 | branch_tracing_enabled--; | ||
113 | |||
114 | out_unlock: | ||
115 | mutex_unlock(&branch_tracing_mutex); | ||
116 | } | ||
117 | |||
118 | static void start_branch_trace(struct trace_array *tr) | ||
119 | { | ||
120 | enable_branch_tracing(tr); | ||
121 | } | ||
122 | |||
123 | static void stop_branch_trace(struct trace_array *tr) | ||
124 | { | ||
125 | disable_branch_tracing(); | ||
126 | } | ||
127 | |||
128 | static void branch_trace_init(struct trace_array *tr) | ||
129 | { | ||
130 | int cpu; | ||
131 | |||
132 | for_each_online_cpu(cpu) | ||
133 | tracing_reset(tr, cpu); | ||
134 | |||
135 | start_branch_trace(tr); | ||
136 | } | ||
137 | |||
138 | static void branch_trace_reset(struct trace_array *tr) | ||
139 | { | ||
140 | stop_branch_trace(tr); | ||
141 | } | ||
142 | |||
143 | struct tracer branch_trace __read_mostly = | ||
144 | { | ||
145 | .name = "branch", | ||
146 | .init = branch_trace_init, | ||
147 | .reset = branch_trace_reset, | ||
148 | #ifdef CONFIG_FTRACE_SELFTEST | ||
149 | .selftest = trace_selftest_startup_branch, | ||
150 | #endif | ||
151 | }; | ||
152 | |||
153 | __init static int init_branch_trace(void) | ||
154 | { | ||
155 | return register_tracer(&branch_trace); | ||
156 | } | ||
157 | |||
158 | device_initcall(init_branch_trace); | ||
159 | #else | ||
160 | static inline | ||
161 | void trace_likely_condition(struct ftrace_branch_data *f, int val, int expect) | ||
162 | { | ||
163 | } | ||
164 | #endif /* CONFIG_BRANCH_TRACER */ | ||
165 | |||
166 | void ftrace_likely_update(struct ftrace_branch_data *f, int val, int expect) | ||
167 | { | ||
168 | /* | ||
169 | * I would love to have a trace point here instead, but the | ||
170 | * trace point code is so inundated with unlikely and likely | ||
171 | * conditions that the recursive nightmare that exists is too | ||
172 | * much to try to get working. At least for now. | ||
173 | */ | ||
174 | trace_likely_condition(f, val, expect); | ||
175 | |||
176 | /* FIXME: Make this atomic! */ | ||
177 | if (val == expect) | ||
178 | f->correct++; | ||
179 | else | ||
180 | f->incorrect++; | ||
181 | } | ||
182 | EXPORT_SYMBOL(ftrace_likely_update); | ||
183 | |||
184 | struct ftrace_pointer { | ||
185 | void *start; | ||
186 | void *stop; | ||
187 | }; | ||
188 | |||
189 | static void * | ||
190 | t_next(struct seq_file *m, void *v, loff_t *pos) | ||
191 | { | ||
192 | struct ftrace_pointer *f = m->private; | ||
193 | struct ftrace_branch_data *p = v; | ||
194 | |||
195 | (*pos)++; | ||
196 | |||
197 | if (v == (void *)1) | ||
198 | return f->start; | ||
199 | |||
200 | ++p; | ||
201 | |||
202 | if ((void *)p >= (void *)f->stop) | ||
203 | return NULL; | ||
204 | |||
205 | return p; | ||
206 | } | ||
207 | |||
208 | static void *t_start(struct seq_file *m, loff_t *pos) | ||
209 | { | ||
210 | void *t = (void *)1; | ||
211 | loff_t l = 0; | ||
212 | |||
213 | for (; t && l < *pos; t = t_next(m, t, &l)) | ||
214 | ; | ||
215 | |||
216 | return t; | ||
217 | } | ||
218 | |||
219 | static void t_stop(struct seq_file *m, void *p) | ||
220 | { | ||
221 | } | ||
222 | |||
223 | static int t_show(struct seq_file *m, void *v) | ||
224 | { | ||
225 | struct ftrace_branch_data *p = v; | ||
226 | const char *f; | ||
227 | unsigned long percent; | ||
228 | |||
229 | if (v == (void *)1) { | ||
230 | seq_printf(m, " correct incorrect %% " | ||
231 | " Function " | ||
232 | " File Line\n" | ||
233 | " ------- --------- - " | ||
234 | " -------- " | ||
235 | " ---- ----\n"); | ||
236 | return 0; | ||
237 | } | ||
238 | |||
239 | /* Only print the file, not the path */ | ||
240 | f = p->file + strlen(p->file); | ||
241 | while (f >= p->file && *f != '/') | ||
242 | f--; | ||
243 | f++; | ||
244 | |||
245 | if (p->correct) { | ||
246 | percent = p->incorrect * 100; | ||
247 | percent /= p->correct + p->incorrect; | ||
248 | } else | ||
249 | percent = p->incorrect ? 100 : 0; | ||
250 | |||
251 | seq_printf(m, "%8lu %8lu %3lu ", p->correct, p->incorrect, percent); | ||
252 | seq_printf(m, "%-30.30s %-20.20s %d\n", p->func, f, p->line); | ||
253 | return 0; | ||
254 | } | ||
255 | |||
256 | static struct seq_operations tracing_likely_seq_ops = { | ||
257 | .start = t_start, | ||
258 | .next = t_next, | ||
259 | .stop = t_stop, | ||
260 | .show = t_show, | ||
261 | }; | ||
262 | |||
263 | static int tracing_likely_open(struct inode *inode, struct file *file) | ||
264 | { | ||
265 | int ret; | ||
266 | |||
267 | ret = seq_open(file, &tracing_likely_seq_ops); | ||
268 | if (!ret) { | ||
269 | struct seq_file *m = file->private_data; | ||
270 | m->private = (void *)inode->i_private; | ||
271 | } | ||
272 | |||
273 | return ret; | ||
274 | } | ||
275 | |||
276 | static struct file_operations tracing_likely_fops = { | ||
277 | .open = tracing_likely_open, | ||
278 | .read = seq_read, | ||
279 | .llseek = seq_lseek, | ||
280 | }; | ||
281 | |||
282 | extern unsigned long __start_likely_profile[]; | ||
283 | extern unsigned long __stop_likely_profile[]; | ||
284 | extern unsigned long __start_unlikely_profile[]; | ||
285 | extern unsigned long __stop_unlikely_profile[]; | ||
286 | |||
287 | static struct ftrace_pointer ftrace_likely_pos = { | ||
288 | .start = __start_likely_profile, | ||
289 | .stop = __stop_likely_profile, | ||
290 | }; | ||
291 | |||
292 | static struct ftrace_pointer ftrace_unlikely_pos = { | ||
293 | .start = __start_unlikely_profile, | ||
294 | .stop = __stop_unlikely_profile, | ||
295 | }; | ||
296 | |||
297 | static __init int ftrace_branch_init(void) | ||
298 | { | ||
299 | struct dentry *d_tracer; | ||
300 | struct dentry *entry; | ||
301 | |||
302 | d_tracer = tracing_init_dentry(); | ||
303 | |||
304 | entry = debugfs_create_file("profile_likely", 0444, d_tracer, | ||
305 | &ftrace_likely_pos, | ||
306 | &tracing_likely_fops); | ||
307 | if (!entry) | ||
308 | pr_warning("Could not create debugfs 'profile_likely' entry\n"); | ||
309 | |||
310 | entry = debugfs_create_file("profile_unlikely", 0444, d_tracer, | ||
311 | &ftrace_unlikely_pos, | ||
312 | &tracing_likely_fops); | ||
313 | if (!entry) | ||
314 | pr_warning("Could not create debugfs" | ||
315 | " 'profile_unlikely' entry\n"); | ||
316 | |||
317 | return 0; | ||
318 | } | ||
319 | |||
320 | device_initcall(ftrace_branch_init); | ||