diff options
-rw-r--r-- | kernel/trace/trace.c | 2 | ||||
-rw-r--r-- | kernel/trace/trace.h | 20 | ||||
-rw-r--r-- | kernel/trace/trace_branch.c | 108 | ||||
-rw-r--r-- | kernel/trace/trace_stat.c | 191 | ||||
-rw-r--r-- | kernel/trace/trace_stat.h | 31 |
5 files changed, 172 insertions, 180 deletions
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 0418fc338b5c..40217fb499ea 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c | |||
@@ -2353,7 +2353,6 @@ static int tracing_set_tracer(char *buf) | |||
2353 | if (ret) | 2353 | if (ret) |
2354 | goto out; | 2354 | goto out; |
2355 | } | 2355 | } |
2356 | init_tracer_stat(t); | ||
2357 | 2356 | ||
2358 | trace_branch_enable(tr); | 2357 | trace_branch_enable(tr); |
2359 | out: | 2358 | out: |
@@ -3218,7 +3217,6 @@ __init static int tracer_alloc_buffers(void) | |||
3218 | #else | 3217 | #else |
3219 | current_trace = &nop_trace; | 3218 | current_trace = &nop_trace; |
3220 | #endif | 3219 | #endif |
3221 | init_tracer_stat(current_trace); | ||
3222 | /* All seems OK, enable tracing */ | 3220 | /* All seems OK, enable tracing */ |
3223 | tracing_disabled = 0; | 3221 | tracing_disabled = 0; |
3224 | 3222 | ||
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index b3f9ad1b4d84..79c872100dd5 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h | |||
@@ -334,24 +334,6 @@ struct tracer_flags { | |||
334 | /* Makes more easy to define a tracer opt */ | 334 | /* Makes more easy to define a tracer opt */ |
335 | #define TRACER_OPT(s, b) .name = #s, .bit = b | 335 | #define TRACER_OPT(s, b) .name = #s, .bit = b |
336 | 336 | ||
337 | /* | ||
338 | * If you want to provide a stat file (one-shot statistics), fill | ||
339 | * an iterator with stat_start/stat_next and a stat_show callbacks. | ||
340 | * The others callbacks are optional. | ||
341 | */ | ||
342 | struct tracer_stat { | ||
343 | /* The name of your stat file */ | ||
344 | const char *name; | ||
345 | /* Iteration over statistic entries */ | ||
346 | void *(*stat_start)(void); | ||
347 | void *(*stat_next)(void *prev, int idx); | ||
348 | /* Compare two entries for sorting (optional) for stats */ | ||
349 | int (*stat_cmp)(void *p1, void *p2); | ||
350 | /* Print a stat entry */ | ||
351 | int (*stat_show)(struct seq_file *s, void *p); | ||
352 | /* Print the headers of your stat entries */ | ||
353 | int (*stat_headers)(struct seq_file *s); | ||
354 | }; | ||
355 | 337 | ||
356 | /* | 338 | /* |
357 | * A specific tracer, represented by methods that operate on a trace array: | 339 | * A specific tracer, represented by methods that operate on a trace array: |
@@ -466,8 +448,6 @@ void tracing_start_sched_switch_record(void); | |||
466 | int register_tracer(struct tracer *type); | 448 | int register_tracer(struct tracer *type); |
467 | void unregister_tracer(struct tracer *type); | 449 | void unregister_tracer(struct tracer *type); |
468 | 450 | ||
469 | void init_tracer_stat(struct tracer *trace); | ||
470 | |||
471 | extern unsigned long nsecs_to_usecs(unsigned long nsecs); | 451 | extern unsigned long nsecs_to_usecs(unsigned long nsecs); |
472 | 452 | ||
473 | extern unsigned long tracing_max_latency; | 453 | extern unsigned long tracing_max_latency; |
diff --git a/kernel/trace/trace_branch.c b/kernel/trace/trace_branch.c index da5cf3e5581b..ca017e0a9a27 100644 --- a/kernel/trace/trace_branch.c +++ b/kernel/trace/trace_branch.c | |||
@@ -16,12 +16,12 @@ | |||
16 | #include <asm/local.h> | 16 | #include <asm/local.h> |
17 | 17 | ||
18 | #include "trace.h" | 18 | #include "trace.h" |
19 | #include "trace_stat.h" | ||
19 | #include "trace_output.h" | 20 | #include "trace_output.h" |
20 | 21 | ||
21 | static struct tracer branch_trace; | ||
22 | |||
23 | #ifdef CONFIG_BRANCH_TRACER | 22 | #ifdef CONFIG_BRANCH_TRACER |
24 | 23 | ||
24 | static struct tracer branch_trace; | ||
25 | static int branch_tracing_enabled __read_mostly; | 25 | static int branch_tracing_enabled __read_mostly; |
26 | static DEFINE_MUTEX(branch_tracing_mutex); | 26 | static DEFINE_MUTEX(branch_tracing_mutex); |
27 | 27 | ||
@@ -191,6 +191,30 @@ static struct trace_event trace_branch_event = { | |||
191 | .binary = trace_nop_print, | 191 | .binary = trace_nop_print, |
192 | }; | 192 | }; |
193 | 193 | ||
194 | static struct tracer branch_trace __read_mostly = | ||
195 | { | ||
196 | .name = "branch", | ||
197 | .init = branch_trace_init, | ||
198 | .reset = branch_trace_reset, | ||
199 | #ifdef CONFIG_FTRACE_SELFTEST | ||
200 | .selftest = trace_selftest_startup_branch, | ||
201 | #endif /* CONFIG_FTRACE_SELFTEST */ | ||
202 | }; | ||
203 | |||
204 | __init static int init_branch_tracer(void) | ||
205 | { | ||
206 | int ret; | ||
207 | |||
208 | ret = register_ftrace_event(&trace_branch_event); | ||
209 | if (!ret) { | ||
210 | printk(KERN_WARNING "Warning: could not register " | ||
211 | "branch events\n"); | ||
212 | return 1; | ||
213 | } | ||
214 | return register_tracer(&branch_trace); | ||
215 | } | ||
216 | device_initcall(init_branch_tracer); | ||
217 | |||
194 | #else | 218 | #else |
195 | static inline | 219 | static inline |
196 | void trace_likely_condition(struct ftrace_branch_data *f, int val, int expect) | 220 | void trace_likely_condition(struct ftrace_branch_data *f, int val, int expect) |
@@ -305,6 +329,29 @@ static int annotated_branch_stat_cmp(void *p1, void *p2) | |||
305 | return 0; | 329 | return 0; |
306 | } | 330 | } |
307 | 331 | ||
332 | static struct tracer_stat annotated_branch_stats = { | ||
333 | .name = "branch_annotated", | ||
334 | .stat_start = annotated_branch_stat_start, | ||
335 | .stat_next = annotated_branch_stat_next, | ||
336 | .stat_cmp = annotated_branch_stat_cmp, | ||
337 | .stat_headers = annotated_branch_stat_headers, | ||
338 | .stat_show = branch_stat_show | ||
339 | }; | ||
340 | |||
341 | __init static int init_annotated_branch_stats(void) | ||
342 | { | ||
343 | int ret; | ||
344 | |||
345 | ret = register_stat_tracer(&annotated_branch_stats); | ||
346 | if (!ret) { | ||
347 | printk(KERN_WARNING "Warning: could not register " | ||
348 | "annotated branches stats\n"); | ||
349 | return 1; | ||
350 | } | ||
351 | return 0; | ||
352 | } | ||
353 | fs_initcall(init_annotated_branch_stats); | ||
354 | |||
308 | #ifdef CONFIG_PROFILE_ALL_BRANCHES | 355 | #ifdef CONFIG_PROFILE_ALL_BRANCHES |
309 | 356 | ||
310 | extern unsigned long __start_branch_profile[]; | 357 | extern unsigned long __start_branch_profile[]; |
@@ -339,60 +386,25 @@ all_branch_stat_next(void *v, int idx) | |||
339 | return p; | 386 | return p; |
340 | } | 387 | } |
341 | 388 | ||
342 | static struct tracer_stat branch_stats[] = { | 389 | static struct tracer_stat all_branch_stats = { |
343 | {.name = "annotated", | 390 | .name = "branch_all", |
344 | .stat_start = annotated_branch_stat_start, | ||
345 | .stat_next = annotated_branch_stat_next, | ||
346 | .stat_cmp = annotated_branch_stat_cmp, | ||
347 | .stat_headers = annotated_branch_stat_headers, | ||
348 | .stat_show = branch_stat_show}, | ||
349 | |||
350 | {.name = "all", | ||
351 | .stat_start = all_branch_stat_start, | 391 | .stat_start = all_branch_stat_start, |
352 | .stat_next = all_branch_stat_next, | 392 | .stat_next = all_branch_stat_next, |
353 | .stat_headers = all_branch_stat_headers, | 393 | .stat_headers = all_branch_stat_headers, |
354 | .stat_show = branch_stat_show}, | 394 | .stat_show = branch_stat_show |
355 | |||
356 | { } | ||
357 | }; | ||
358 | #else | ||
359 | static struct tracer_stat branch_stats[] = { | ||
360 | {.name = "annotated", | ||
361 | .stat_start = annotated_branch_stat_start, | ||
362 | .stat_next = annotated_branch_stat_next, | ||
363 | .stat_cmp = annotated_branch_stat_cmp, | ||
364 | .stat_headers = annotated_branch_stat_headers, | ||
365 | .stat_show = branch_stat_show}, | ||
366 | |||
367 | { } | ||
368 | }; | 395 | }; |
369 | #endif /* CONFIG_PROFILE_ALL_BRANCHES */ | ||
370 | 396 | ||
371 | 397 | __init static int all_annotated_branch_stats(void) | |
372 | static struct tracer branch_trace __read_mostly = | ||
373 | { | 398 | { |
374 | .name = "branch", | ||
375 | #ifdef CONFIG_BRANCH_TRACER | ||
376 | .init = branch_trace_init, | ||
377 | .reset = branch_trace_reset, | ||
378 | #ifdef CONFIG_FTRACE_SELFTEST | ||
379 | .selftest = trace_selftest_startup_branch, | ||
380 | #endif /* CONFIG_FTRACE_SELFTEST */ | ||
381 | #endif | ||
382 | .stats = branch_stats | ||
383 | }; | ||
384 | |||
385 | __init static int init_branch_trace(void) | ||
386 | { | ||
387 | #ifdef CONFIG_BRANCH_TRACER | ||
388 | int ret; | 399 | int ret; |
389 | ret = register_ftrace_event(&trace_branch_event); | 400 | |
401 | ret = register_stat_tracer(&all_branch_stats); | ||
390 | if (!ret) { | 402 | if (!ret) { |
391 | printk(KERN_WARNING "Warning: could not register branch events\n"); | 403 | printk(KERN_WARNING "Warning: could not register " |
404 | "all branches stats\n"); | ||
392 | return 1; | 405 | return 1; |
393 | } | 406 | } |
394 | #endif | 407 | return 0; |
395 | |||
396 | return register_tracer(&branch_trace); | ||
397 | } | 408 | } |
398 | device_initcall(init_branch_trace); | 409 | fs_initcall(all_annotated_branch_stats); |
410 | #endif /* CONFIG_PROFILE_ALL_BRANCHES */ | ||
diff --git a/kernel/trace/trace_stat.c b/kernel/trace/trace_stat.c index 1515f9e7adfc..cb29282b9485 100644 --- a/kernel/trace/trace_stat.c +++ b/kernel/trace/trace_stat.c | |||
@@ -10,28 +10,32 @@ | |||
10 | 10 | ||
11 | 11 | ||
12 | #include <linux/list.h> | 12 | #include <linux/list.h> |
13 | #include <linux/seq_file.h> | ||
14 | #include <linux/debugfs.h> | 13 | #include <linux/debugfs.h> |
14 | #include "trace_stat.h" | ||
15 | #include "trace.h" | 15 | #include "trace.h" |
16 | 16 | ||
17 | 17 | ||
18 | /* List of stat entries from a tracer */ | 18 | /* List of stat entries from a tracer */ |
19 | struct trace_stat_list { | 19 | struct trace_stat_list { |
20 | struct list_head list; | 20 | struct list_head list; |
21 | void *stat; | 21 | void *stat; |
22 | }; | 22 | }; |
23 | 23 | ||
24 | /* A stat session is the stats output in one file */ | 24 | /* A stat session is the stats output in one file */ |
25 | struct tracer_stat_session { | 25 | struct tracer_stat_session { |
26 | struct tracer_stat *ts; | 26 | struct list_head session_list; |
27 | struct list_head stat_list; | 27 | struct tracer_stat *ts; |
28 | struct mutex stat_mutex; | 28 | struct list_head stat_list; |
29 | struct mutex stat_mutex; | ||
30 | struct dentry *file; | ||
29 | }; | 31 | }; |
30 | 32 | ||
31 | /* All of the sessions currently in use. Each stat file embeed one session */ | 33 | /* All of the sessions currently in use. Each stat file embeed one session */ |
32 | static struct tracer_stat_session **all_stat_sessions; | 34 | static LIST_HEAD(all_stat_sessions); |
33 | static int nb_sessions; | 35 | static DEFINE_MUTEX(all_stat_sessions_mutex); |
34 | static struct dentry *stat_dir, **stat_files; | 36 | |
37 | /* The root directory for all stat files */ | ||
38 | static struct dentry *stat_dir; | ||
35 | 39 | ||
36 | 40 | ||
37 | static void reset_stat_session(struct tracer_stat_session *session) | 41 | static void reset_stat_session(struct tracer_stat_session *session) |
@@ -44,66 +48,77 @@ static void reset_stat_session(struct tracer_stat_session *session) | |||
44 | INIT_LIST_HEAD(&session->stat_list); | 48 | INIT_LIST_HEAD(&session->stat_list); |
45 | } | 49 | } |
46 | 50 | ||
47 | /* Called when a tracer is initialized */ | 51 | static void destroy_session(struct tracer_stat_session *session) |
48 | static int init_all_sessions(int nb, struct tracer_stat *ts) | ||
49 | { | 52 | { |
50 | int i, j; | 53 | debugfs_remove(session->file); |
51 | struct tracer_stat_session *session; | 54 | reset_stat_session(session); |
55 | mutex_destroy(&session->stat_mutex); | ||
56 | kfree(session); | ||
57 | } | ||
52 | 58 | ||
53 | nb_sessions = 0; | ||
54 | 59 | ||
55 | if (all_stat_sessions) { | 60 | static int init_stat_file(struct tracer_stat_session *session); |
56 | for (i = 0; i < nb_sessions; i++) { | ||
57 | session = all_stat_sessions[i]; | ||
58 | reset_stat_session(session); | ||
59 | mutex_destroy(&session->stat_mutex); | ||
60 | kfree(session); | ||
61 | } | ||
62 | } | ||
63 | all_stat_sessions = kmalloc(sizeof(struct tracer_stat_session *) * nb, | ||
64 | GFP_KERNEL); | ||
65 | if (!all_stat_sessions) | ||
66 | return -ENOMEM; | ||
67 | 61 | ||
68 | for (i = 0; i < nb; i++) { | 62 | int register_stat_tracer(struct tracer_stat *trace) |
69 | session = kmalloc(sizeof(struct tracer_stat_session) * nb, | 63 | { |
70 | GFP_KERNEL); | 64 | struct tracer_stat_session *session, *node, *tmp; |
71 | if (!session) | 65 | int ret; |
72 | goto free_sessions; | 66 | |
67 | if (!trace) | ||
68 | return -EINVAL; | ||
69 | |||
70 | if (!trace->stat_start || !trace->stat_next || !trace->stat_show) | ||
71 | return -EINVAL; | ||
73 | 72 | ||
74 | INIT_LIST_HEAD(&session->stat_list); | 73 | /* Already registered? */ |
75 | mutex_init(&session->stat_mutex); | 74 | mutex_lock(&all_stat_sessions_mutex); |
76 | session->ts = &ts[i]; | 75 | list_for_each_entry_safe(node, tmp, &all_stat_sessions, session_list) { |
77 | all_stat_sessions[i] = session; | 76 | if (node->ts == trace) |
77 | return -EINVAL; | ||
78 | } | 78 | } |
79 | nb_sessions = nb; | 79 | mutex_unlock(&all_stat_sessions_mutex); |
80 | return 0; | 80 | |
81 | /* Init the session */ | ||
82 | session = kmalloc(sizeof(struct tracer_stat_session), GFP_KERNEL); | ||
83 | if (!session) | ||
84 | return -ENOMEM; | ||
81 | 85 | ||
82 | free_sessions: | 86 | session->ts = trace; |
87 | INIT_LIST_HEAD(&session->session_list); | ||
88 | INIT_LIST_HEAD(&session->stat_list); | ||
89 | mutex_init(&session->stat_mutex); | ||
90 | session->file = NULL; | ||
83 | 91 | ||
84 | for (j = 0; j < i; j++) | 92 | ret = init_stat_file(session); |
85 | kfree(all_stat_sessions[i]); | 93 | if (ret) { |
94 | destroy_session(session); | ||
95 | return ret; | ||
96 | } | ||
86 | 97 | ||
87 | kfree(all_stat_sessions); | 98 | /* Register */ |
88 | all_stat_sessions = NULL; | 99 | mutex_lock(&all_stat_sessions_mutex); |
100 | list_add_tail(&session->session_list, &all_stat_sessions); | ||
101 | mutex_unlock(&all_stat_sessions_mutex); | ||
89 | 102 | ||
90 | return -ENOMEM; | 103 | return 0; |
91 | } | 104 | } |
92 | 105 | ||
93 | static int basic_tracer_stat_checks(struct tracer_stat *ts) | 106 | void unregister_stat_tracer(struct tracer_stat *trace) |
94 | { | 107 | { |
95 | int i; | 108 | struct tracer_stat_session *node, *tmp; |
96 | 109 | ||
97 | if (!ts) | 110 | mutex_lock(&all_stat_sessions_mutex); |
98 | return 0; | 111 | list_for_each_entry_safe(node, tmp, &all_stat_sessions, session_list) { |
99 | 112 | if (node->ts == trace) { | |
100 | for (i = 0; ts[i].name; i++) { | 113 | list_del(&node->session_list); |
101 | if (!ts[i].stat_start || !ts[i].stat_next || !ts[i].stat_show) | 114 | destroy_session(node); |
102 | return -EBUSY; | 115 | break; |
116 | } | ||
103 | } | 117 | } |
104 | return i; | 118 | mutex_unlock(&all_stat_sessions_mutex); |
105 | } | 119 | } |
106 | 120 | ||
121 | |||
107 | /* | 122 | /* |
108 | * For tracers that don't provide a stat_cmp callback. | 123 | * For tracers that don't provide a stat_cmp callback. |
109 | * This one will force an immediate insertion on tail of | 124 | * This one will force an immediate insertion on tail of |
@@ -280,63 +295,7 @@ static const struct file_operations tracing_stat_fops = { | |||
280 | .release = tracing_stat_release | 295 | .release = tracing_stat_release |
281 | }; | 296 | }; |
282 | 297 | ||
283 | 298 | static int tracing_stat_init(void) | |
284 | static void destroy_trace_stat_files(void) | ||
285 | { | ||
286 | int i; | ||
287 | |||
288 | if (stat_files) { | ||
289 | for (i = 0; i < nb_sessions; i++) | ||
290 | debugfs_remove(stat_files[i]); | ||
291 | kfree(stat_files); | ||
292 | stat_files = NULL; | ||
293 | } | ||
294 | } | ||
295 | |||
296 | static void init_trace_stat_files(void) | ||
297 | { | ||
298 | int i; | ||
299 | |||
300 | if (!stat_dir || !nb_sessions) | ||
301 | return; | ||
302 | |||
303 | stat_files = kmalloc(sizeof(struct dentry *) * nb_sessions, GFP_KERNEL); | ||
304 | |||
305 | if (!stat_files) { | ||
306 | pr_warning("trace stat: not enough memory\n"); | ||
307 | return; | ||
308 | } | ||
309 | |||
310 | for (i = 0; i < nb_sessions; i++) { | ||
311 | struct tracer_stat_session *session = all_stat_sessions[i]; | ||
312 | stat_files[i] = debugfs_create_file(session->ts->name, 0644, | ||
313 | stat_dir, | ||
314 | session, &tracing_stat_fops); | ||
315 | if (!stat_files[i]) | ||
316 | pr_warning("cannot create %s entry\n", | ||
317 | session->ts->name); | ||
318 | } | ||
319 | } | ||
320 | |||
321 | void init_tracer_stat(struct tracer *trace) | ||
322 | { | ||
323 | int nb = basic_tracer_stat_checks(trace->stats); | ||
324 | |||
325 | destroy_trace_stat_files(); | ||
326 | |||
327 | if (nb < 0) { | ||
328 | pr_warning("stat tracing: missing stat callback on %s\n", | ||
329 | trace->name); | ||
330 | return; | ||
331 | } | ||
332 | if (!nb) | ||
333 | return; | ||
334 | |||
335 | init_all_sessions(nb, trace->stats); | ||
336 | init_trace_stat_files(); | ||
337 | } | ||
338 | |||
339 | static int __init tracing_stat_init(void) | ||
340 | { | 299 | { |
341 | struct dentry *d_tracing; | 300 | struct dentry *d_tracing; |
342 | 301 | ||
@@ -348,4 +307,16 @@ static int __init tracing_stat_init(void) | |||
348 | "'trace_stat' entry\n"); | 307 | "'trace_stat' entry\n"); |
349 | return 0; | 308 | return 0; |
350 | } | 309 | } |
351 | fs_initcall(tracing_stat_init); | 310 | |
311 | static int init_stat_file(struct tracer_stat_session *session) | ||
312 | { | ||
313 | if (!stat_dir && tracing_stat_init()) | ||
314 | return -ENODEV; | ||
315 | |||
316 | session->file = debugfs_create_file(session->ts->name, 0644, | ||
317 | stat_dir, | ||
318 | session, &tracing_stat_fops); | ||
319 | if (!session->file) | ||
320 | return -ENOMEM; | ||
321 | return 0; | ||
322 | } | ||
diff --git a/kernel/trace/trace_stat.h b/kernel/trace/trace_stat.h new file mode 100644 index 000000000000..202274cf7f3d --- /dev/null +++ b/kernel/trace/trace_stat.h | |||
@@ -0,0 +1,31 @@ | |||
1 | #ifndef __TRACE_STAT_H | ||
2 | #define __TRACE_STAT_H | ||
3 | |||
4 | #include <linux/seq_file.h> | ||
5 | |||
6 | /* | ||
7 | * If you want to provide a stat file (one-shot statistics), fill | ||
8 | * an iterator with stat_start/stat_next and a stat_show callbacks. | ||
9 | * The others callbacks are optional. | ||
10 | */ | ||
11 | struct tracer_stat { | ||
12 | /* The name of your stat file */ | ||
13 | const char *name; | ||
14 | /* Iteration over statistic entries */ | ||
15 | void *(*stat_start)(void); | ||
16 | void *(*stat_next)(void *prev, int idx); | ||
17 | /* Compare two entries for stats sorting */ | ||
18 | int (*stat_cmp)(void *p1, void *p2); | ||
19 | /* Print a stat entry */ | ||
20 | int (*stat_show)(struct seq_file *s, void *p); | ||
21 | /* Print the headers of your stat entries */ | ||
22 | int (*stat_headers)(struct seq_file *s); | ||
23 | }; | ||
24 | |||
25 | /* | ||
26 | * Destroy or create a stat file | ||
27 | */ | ||
28 | extern int register_stat_tracer(struct tracer_stat *trace); | ||
29 | extern void unregister_stat_tracer(struct tracer_stat *trace); | ||
30 | |||
31 | #endif /* __TRACE_STAT_H */ | ||