aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--kernel/trace/trace.c2
-rw-r--r--kernel/trace/trace.h20
-rw-r--r--kernel/trace/trace_branch.c108
-rw-r--r--kernel/trace/trace_stat.c191
-rw-r--r--kernel/trace/trace_stat.h31
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 */
342struct 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);
466int register_tracer(struct tracer *type); 448int register_tracer(struct tracer *type);
467void unregister_tracer(struct tracer *type); 449void unregister_tracer(struct tracer *type);
468 450
469void init_tracer_stat(struct tracer *trace);
470
471extern unsigned long nsecs_to_usecs(unsigned long nsecs); 451extern unsigned long nsecs_to_usecs(unsigned long nsecs);
472 452
473extern unsigned long tracing_max_latency; 453extern 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
21static struct tracer branch_trace;
22
23#ifdef CONFIG_BRANCH_TRACER 22#ifdef CONFIG_BRANCH_TRACER
24 23
24static struct tracer branch_trace;
25static int branch_tracing_enabled __read_mostly; 25static int branch_tracing_enabled __read_mostly;
26static DEFINE_MUTEX(branch_tracing_mutex); 26static 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
194static 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}
216device_initcall(init_branch_tracer);
217
194#else 218#else
195static inline 219static inline
196void trace_likely_condition(struct ftrace_branch_data *f, int val, int expect) 220void 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
332static 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}
353fs_initcall(init_annotated_branch_stats);
354
308#ifdef CONFIG_PROFILE_ALL_BRANCHES 355#ifdef CONFIG_PROFILE_ALL_BRANCHES
309 356
310extern unsigned long __start_branch_profile[]; 357extern 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
342static struct tracer_stat branch_stats[] = { 389static 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
359static 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)
372static 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}
398device_initcall(init_branch_trace); 409fs_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 */
19struct trace_stat_list { 19struct 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 */
25struct tracer_stat_session { 25struct 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 */
32static struct tracer_stat_session **all_stat_sessions; 34static LIST_HEAD(all_stat_sessions);
33static int nb_sessions; 35static DEFINE_MUTEX(all_stat_sessions_mutex);
34static struct dentry *stat_dir, **stat_files; 36
37/* The root directory for all stat files */
38static struct dentry *stat_dir;
35 39
36 40
37static void reset_stat_session(struct tracer_stat_session *session) 41static 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 */ 51static void destroy_session(struct tracer_stat_session *session)
48static 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) { 60static 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++) { 62int 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
82free_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
93static int basic_tracer_stat_checks(struct tracer_stat *ts) 106void 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 298static int tracing_stat_init(void)
284static 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
296static 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
321void 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
339static 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}
351fs_initcall(tracing_stat_init); 310
311static 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 */
11struct 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 */
28extern int register_stat_tracer(struct tracer_stat *trace);
29extern void unregister_stat_tracer(struct tracer_stat *trace);
30
31#endif /* __TRACE_STAT_H */