diff options
author | Frederic Weisbecker <fweisbec@gmail.com> | 2008-12-27 17:25:38 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-12-29 06:55:46 -0500 |
commit | e302cf3f961ceb54c1dd0aff7ba8531df83be07a (patch) | |
tree | 090e3ebba0745f59709f06d15c5ae7a235fd1840 /kernel/trace/trace_branch.c | |
parent | dbd0b4b33074aa6b7832a9d9a5bd985eca5c1aa2 (diff) |
tracing/branch-tracer: adapt to the stat tracing API
Impact: refactor the branch tracer
This patch adapts the branch tracer to the tracing API.
This is a proof of concept because the branch tracer implements two
"stat tracing" that were split in two files.
So I added an option to the branch tracer: stat_all_branch.
If it is set, then trace_stat will output all of the branches
entries stats. Otherwise, it will print the annotated branches.
Its is a kind of quick trick, waiting for a better solution.
By default, the annotated branches stat are sorted by incorrect branch
prediction percentage.
Ie:
correct incorrect % Function File Line
------- --------- - -------- ---- ----
0 1 100 native_smp_prepare_cpus smpboot.c 1228
0 1 100 hpet_rtc_timer_reinit hpet.c 1057
0 18032 100 sched_info_queued sched_stats.h 223
0 684 100 yield_task_fair sched_fair.c 984
0 282 100 pre_schedule_rt sched_rt.c 1263
0 13414 100 sched_info_dequeued sched_stats.h 178
0 21724 100 sched_info_switch sched_stats.h 270
0 1 100 get_signal_to_deliver signal.c 1820
0 8 100 __cancel_work_timer workqueue.c 560
0 212 100 verify_export_symbols module.c 1509
0 17 100 __rmqueue_fallback page_alloc.c 793
0 43 100 clear_page_mlock internal.h 129
0 124 100 try_to_unmap_anon rmap.c 1021
0 53 100 try_to_unmap_anon rmap.c 1013
0 6 100 vma_address rmap.c 232
0 3301 100 try_to_unmap_file rmap.c 1082
0 466 100 try_to_unmap_file rmap.c 1077
0 1 100 mem_cgroup_create memcontrol.c 1090
0 3 100 inotify_find_update_watch inotify.c 726
2 30163 99 perf_counter_task_sched_out perf_counter.c 385
1 2935 99 percpu_free allocpercpu.c 138
1544 297672 99 dentry_lru_del_init dcache.c 153
8 1074 99 input_pass_event input.c 86
1390 76781 98 mapping_unevictable pagemap.h 50
280 6665 95 pick_next_task_rt sched_rt.c 889
750 4826 86 next_pidmap pid.c 194
2 8 80 blocking_notifier_chain_regist notifier.c 220
36 130 78 ioremap_pte_range ioremap.c 22
1093 3247 74 IS_ERR err.h 34
1023 2908 73 sched_slice sched_fair.c 445
22 60 73 disk_put_part genhd.h 206
[...]
It enables a developer to quickly address the source of incorrect branch
predictions. Note that this sorting would be better with a second sort on
the number of incorrect predictions.
Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'kernel/trace/trace_branch.c')
-rw-r--r-- | kernel/trace/trace_branch.c | 268 |
1 files changed, 142 insertions, 126 deletions
diff --git a/kernel/trace/trace_branch.c b/kernel/trace/trace_branch.c index c15222a01073..4785a3b9bc4a 100644 --- a/kernel/trace/trace_branch.c +++ b/kernel/trace/trace_branch.c | |||
@@ -18,10 +18,13 @@ | |||
18 | #include "trace.h" | 18 | #include "trace.h" |
19 | #include "trace_output.h" | 19 | #include "trace_output.h" |
20 | 20 | ||
21 | static struct tracer branch_trace; | ||
22 | |||
21 | #ifdef CONFIG_BRANCH_TRACER | 23 | #ifdef CONFIG_BRANCH_TRACER |
22 | 24 | ||
23 | static int branch_tracing_enabled __read_mostly; | 25 | static int branch_tracing_enabled __read_mostly; |
24 | static DEFINE_MUTEX(branch_tracing_mutex); | 26 | static DEFINE_MUTEX(branch_tracing_mutex); |
27 | |||
25 | static struct trace_array *branch_tracer; | 28 | static struct trace_array *branch_tracer; |
26 | 29 | ||
27 | static void | 30 | static void |
@@ -178,6 +181,7 @@ trace_branch_print(struct trace_seq *s, struct trace_entry *entry, int flags) | |||
178 | return 0; | 181 | return 0; |
179 | } | 182 | } |
180 | 183 | ||
184 | |||
181 | static struct trace_event trace_branch_event = { | 185 | static struct trace_event trace_branch_event = { |
182 | .type = TRACE_BRANCH, | 186 | .type = TRACE_BRANCH, |
183 | .trace = trace_branch_print, | 187 | .trace = trace_branch_print, |
@@ -187,30 +191,6 @@ static struct trace_event trace_branch_event = { | |||
187 | .binary = trace_nop_print, | 191 | .binary = trace_nop_print, |
188 | }; | 192 | }; |
189 | 193 | ||
190 | struct tracer branch_trace __read_mostly = | ||
191 | { | ||
192 | .name = "branch", | ||
193 | .init = branch_trace_init, | ||
194 | .reset = branch_trace_reset, | ||
195 | #ifdef CONFIG_FTRACE_SELFTEST | ||
196 | .selftest = trace_selftest_startup_branch, | ||
197 | #endif | ||
198 | }; | ||
199 | |||
200 | __init static int init_branch_trace(void) | ||
201 | { | ||
202 | int ret; | ||
203 | |||
204 | ret = register_ftrace_event(&trace_branch_event); | ||
205 | if (!ret) { | ||
206 | printk(KERN_WARNING "Warning: could not register branch events\n"); | ||
207 | return 1; | ||
208 | } | ||
209 | |||
210 | return register_tracer(&branch_trace); | ||
211 | } | ||
212 | |||
213 | device_initcall(init_branch_trace); | ||
214 | #else | 194 | #else |
215 | static inline | 195 | static inline |
216 | void trace_likely_condition(struct ftrace_branch_data *f, int val, int expect) | 196 | void trace_likely_condition(struct ftrace_branch_data *f, int val, int expect) |
@@ -236,66 +216,39 @@ void ftrace_likely_update(struct ftrace_branch_data *f, int val, int expect) | |||
236 | } | 216 | } |
237 | EXPORT_SYMBOL(ftrace_likely_update); | 217 | EXPORT_SYMBOL(ftrace_likely_update); |
238 | 218 | ||
239 | struct ftrace_pointer { | 219 | extern unsigned long __start_annotated_branch_profile[]; |
240 | void *start; | 220 | extern unsigned long __stop_annotated_branch_profile[]; |
241 | void *stop; | ||
242 | int hit; | ||
243 | }; | ||
244 | 221 | ||
245 | static void * | 222 | static int annotated_branch_stat_headers(struct seq_file *m) |
246 | t_next(struct seq_file *m, void *v, loff_t *pos) | ||
247 | { | 223 | { |
248 | const struct ftrace_pointer *f = m->private; | 224 | seq_printf(m, " correct incorrect %% "); |
249 | struct ftrace_branch_data *p = v; | 225 | seq_printf(m, " Function " |
250 | 226 | " File Line\n" | |
251 | (*pos)++; | 227 | " ------- --------- - " |
252 | 228 | " -------- " | |
253 | if (v == (void *)1) | 229 | " ---- ----\n"); |
254 | return f->start; | 230 | return 0; |
255 | |||
256 | ++p; | ||
257 | |||
258 | if ((void *)p >= (void *)f->stop) | ||
259 | return NULL; | ||
260 | |||
261 | return p; | ||
262 | } | 231 | } |
263 | 232 | ||
264 | static void *t_start(struct seq_file *m, loff_t *pos) | 233 | static inline long get_incorrect_percent(struct ftrace_branch_data *p) |
265 | { | 234 | { |
266 | void *t = (void *)1; | 235 | long percent; |
267 | loff_t l = 0; | ||
268 | |||
269 | for (; t && l < *pos; t = t_next(m, t, &l)) | ||
270 | ; | ||
271 | 236 | ||
272 | return t; | 237 | if (p->correct) { |
273 | } | 238 | percent = p->incorrect * 100; |
239 | percent /= p->correct + p->incorrect; | ||
240 | } else | ||
241 | percent = p->incorrect ? 100 : -1; | ||
274 | 242 | ||
275 | static void t_stop(struct seq_file *m, void *p) | 243 | return percent; |
276 | { | ||
277 | } | 244 | } |
278 | 245 | ||
279 | static int t_show(struct seq_file *m, void *v) | 246 | static int branch_stat_show(struct seq_file *m, void *v) |
280 | { | 247 | { |
281 | const struct ftrace_pointer *fp = m->private; | ||
282 | struct ftrace_branch_data *p = v; | 248 | struct ftrace_branch_data *p = v; |
283 | const char *f; | 249 | const char *f; |
284 | long percent; | 250 | long percent; |
285 | 251 | ||
286 | if (v == (void *)1) { | ||
287 | if (fp->hit) | ||
288 | seq_printf(m, " miss hit %% "); | ||
289 | else | ||
290 | seq_printf(m, " correct incorrect %% "); | ||
291 | seq_printf(m, " Function " | ||
292 | " File Line\n" | ||
293 | " ------- --------- - " | ||
294 | " -------- " | ||
295 | " ---- ----\n"); | ||
296 | return 0; | ||
297 | } | ||
298 | |||
299 | /* Only print the file, not the path */ | 252 | /* Only print the file, not the path */ |
300 | f = p->file + strlen(p->file); | 253 | f = p->file + strlen(p->file); |
301 | while (f >= p->file && *f != '/') | 254 | while (f >= p->file && *f != '/') |
@@ -305,11 +258,7 @@ static int t_show(struct seq_file *m, void *v) | |||
305 | /* | 258 | /* |
306 | * The miss is overlayed on correct, and hit on incorrect. | 259 | * The miss is overlayed on correct, and hit on incorrect. |
307 | */ | 260 | */ |
308 | if (p->correct) { | 261 | percent = get_incorrect_percent(p); |
309 | percent = p->incorrect * 100; | ||
310 | percent /= p->correct + p->incorrect; | ||
311 | } else | ||
312 | percent = p->incorrect ? 100 : -1; | ||
313 | 262 | ||
314 | seq_printf(m, "%8lu %8lu ", p->correct, p->incorrect); | 263 | seq_printf(m, "%8lu %8lu ", p->correct, p->incorrect); |
315 | if (percent < 0) | 264 | if (percent < 0) |
@@ -320,76 +269,143 @@ static int t_show(struct seq_file *m, void *v) | |||
320 | return 0; | 269 | return 0; |
321 | } | 270 | } |
322 | 271 | ||
323 | static struct seq_operations tracing_likely_seq_ops = { | 272 | static void *annotated_branch_stat_start(void) |
324 | .start = t_start, | 273 | { |
325 | .next = t_next, | 274 | return __start_annotated_branch_profile; |
326 | .stop = t_stop, | 275 | } |
327 | .show = t_show, | ||
328 | }; | ||
329 | 276 | ||
330 | static int tracing_branch_open(struct inode *inode, struct file *file) | 277 | static void * |
278 | annotated_branch_stat_next(void *v, int idx) | ||
331 | { | 279 | { |
332 | int ret; | 280 | struct ftrace_branch_data *p = v; |
333 | 281 | ||
334 | ret = seq_open(file, &tracing_likely_seq_ops); | 282 | ++p; |
335 | if (!ret) { | ||
336 | struct seq_file *m = file->private_data; | ||
337 | m->private = (void *)inode->i_private; | ||
338 | } | ||
339 | 283 | ||
340 | return ret; | 284 | if ((void *)p >= (void *)__stop_annotated_branch_profile) |
285 | return NULL; | ||
286 | |||
287 | return p; | ||
341 | } | 288 | } |
342 | 289 | ||
343 | static const struct file_operations tracing_branch_fops = { | 290 | static int annotated_branch_stat_cmp(void *p1, void *p2) |
344 | .open = tracing_branch_open, | 291 | { |
345 | .read = seq_read, | 292 | struct ftrace_branch_data *a = p1; |
346 | .llseek = seq_lseek, | 293 | struct ftrace_branch_data *b = p2; |
347 | }; | 294 | |
295 | long percent_a, percent_b; | ||
296 | |||
297 | percent_a = get_incorrect_percent(a); | ||
298 | percent_b = get_incorrect_percent(b); | ||
299 | |||
300 | if (percent_a < percent_b) | ||
301 | return -1; | ||
302 | if (percent_a > percent_b) | ||
303 | return 1; | ||
304 | else | ||
305 | return 0; | ||
306 | } | ||
348 | 307 | ||
349 | #ifdef CONFIG_PROFILE_ALL_BRANCHES | 308 | #ifdef CONFIG_PROFILE_ALL_BRANCHES |
350 | extern unsigned long __start_branch_profile[]; | 309 | enum { |
351 | extern unsigned long __stop_branch_profile[]; | 310 | TRACE_BRANCH_OPT_ALL = 0x1 |
311 | }; | ||
352 | 312 | ||
353 | static const struct ftrace_pointer ftrace_branch_pos = { | 313 | static struct tracer_opt branch_opts[] = { |
354 | .start = __start_branch_profile, | 314 | { TRACER_OPT(stat_all_branch, TRACE_BRANCH_OPT_ALL) }, |
355 | .stop = __stop_branch_profile, | 315 | { } |
356 | .hit = 1, | ||
357 | }; | 316 | }; |
358 | 317 | ||
359 | #endif /* CONFIG_PROFILE_ALL_BRANCHES */ | 318 | static struct tracer_flags branch_flags = { |
319 | .val = 0, | ||
320 | .opts = branch_opts | ||
321 | }; | ||
360 | 322 | ||
361 | extern unsigned long __start_annotated_branch_profile[]; | 323 | extern unsigned long __start_branch_profile[]; |
362 | extern unsigned long __stop_annotated_branch_profile[]; | 324 | extern unsigned long __stop_branch_profile[]; |
363 | 325 | ||
364 | static const struct ftrace_pointer ftrace_annotated_branch_pos = { | 326 | static int all_branch_stat_headers(struct seq_file *m) |
365 | .start = __start_annotated_branch_profile, | 327 | { |
366 | .stop = __stop_annotated_branch_profile, | 328 | seq_printf(m, " miss hit %% "); |
367 | }; | 329 | seq_printf(m, " Function " |
330 | " File Line\n" | ||
331 | " ------- --------- - " | ||
332 | " -------- " | ||
333 | " ---- ----\n"); | ||
334 | return 0; | ||
335 | } | ||
368 | 336 | ||
369 | static __init int ftrace_branch_init(void) | 337 | static void *all_branch_stat_start(void) |
370 | { | 338 | { |
371 | struct dentry *d_tracer; | 339 | return __start_branch_profile; |
372 | struct dentry *entry; | 340 | } |
341 | |||
342 | static void * | ||
343 | all_branch_stat_next(void *v, int idx) | ||
344 | { | ||
345 | struct ftrace_branch_data *p = v; | ||
373 | 346 | ||
374 | d_tracer = tracing_init_dentry(); | 347 | ++p; |
375 | 348 | ||
376 | entry = debugfs_create_file("profile_annotated_branch", 0444, d_tracer, | 349 | if ((void *)p >= (void *)__stop_branch_profile) |
377 | (void *)&ftrace_annotated_branch_pos, | 350 | return NULL; |
378 | &tracing_branch_fops); | ||
379 | if (!entry) | ||
380 | pr_warning("Could not create debugfs " | ||
381 | "'profile_annotatet_branch' entry\n"); | ||
382 | 351 | ||
383 | #ifdef CONFIG_PROFILE_ALL_BRANCHES | 352 | return p; |
384 | entry = debugfs_create_file("profile_branch", 0444, d_tracer, | 353 | } |
385 | (void *)&ftrace_branch_pos, | ||
386 | &tracing_branch_fops); | ||
387 | if (!entry) | ||
388 | pr_warning("Could not create debugfs" | ||
389 | " 'profile_branch' entry\n"); | ||
390 | #endif | ||
391 | 354 | ||
355 | static int branch_set_flag(u32 old_flags, u32 bit, int set) | ||
356 | { | ||
357 | if (bit == TRACE_BRANCH_OPT_ALL) { | ||
358 | if (set) { | ||
359 | branch_trace.stat_headers = all_branch_stat_headers; | ||
360 | branch_trace.stat_start = all_branch_stat_start; | ||
361 | branch_trace.stat_next = all_branch_stat_next; | ||
362 | branch_trace.stat_cmp = NULL; | ||
363 | } else { | ||
364 | branch_trace.stat_headers = | ||
365 | annotated_branch_stat_headers; | ||
366 | branch_trace.stat_start = annotated_branch_stat_start; | ||
367 | branch_trace.stat_next = annotated_branch_stat_next; | ||
368 | branch_trace.stat_cmp = annotated_branch_stat_cmp; | ||
369 | } | ||
370 | init_tracer_stat(&branch_trace); | ||
371 | } | ||
392 | return 0; | 372 | return 0; |
393 | } | 373 | } |
394 | 374 | ||
395 | device_initcall(ftrace_branch_init); | 375 | #endif /* CONFIG_PROFILE_ALL_BRANCHES */ |
376 | |||
377 | static struct tracer branch_trace __read_mostly = | ||
378 | { | ||
379 | .name = "branch", | ||
380 | #ifdef CONFIG_BRANCH_TRACER | ||
381 | .init = branch_trace_init, | ||
382 | .reset = branch_trace_reset, | ||
383 | #ifdef CONFIG_FTRACE_SELFTEST | ||
384 | .selftest = trace_selftest_startup_branch, | ||
385 | #endif /* CONFIG_FTRACE_SELFTEST */ | ||
386 | #endif /* CONFIG_BRANCH_TRACER */ | ||
387 | .stat_start = annotated_branch_stat_start, | ||
388 | .stat_next = annotated_branch_stat_next, | ||
389 | .stat_show = branch_stat_show, | ||
390 | .stat_headers = annotated_branch_stat_headers, | ||
391 | .stat_cmp = annotated_branch_stat_cmp, | ||
392 | #ifdef CONFIG_PROFILE_ALL_BRANCHES | ||
393 | .flags = &branch_flags, | ||
394 | .set_flag = branch_set_flag, | ||
395 | #endif | ||
396 | }; | ||
397 | |||
398 | __init static int init_branch_trace(void) | ||
399 | { | ||
400 | #ifdef CONFIG_BRANCH_TRACER | ||
401 | int ret; | ||
402 | ret = register_ftrace_event(&trace_branch_event); | ||
403 | if (!ret) { | ||
404 | printk(KERN_WARNING "Warning: could not register branch events\n"); | ||
405 | return 1; | ||
406 | } | ||
407 | #endif | ||
408 | |||
409 | return register_tracer(&branch_trace); | ||
410 | } | ||
411 | device_initcall(init_branch_trace); | ||