aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/trace
diff options
context:
space:
mode:
authorFrederic Weisbecker <fweisbec@gmail.com>2009-01-10 14:34:13 -0500
committerIngo Molnar <mingo@elte.hu>2009-01-14 06:11:37 -0500
commit002bb86d8d42f18937aef396c3ecd65c7e02e21a (patch)
treece8e81f6dc2515f6442198bbd4b527d900982f8e /kernel/trace
parenta14a07b8018b714e03a39ff2180c66e307ef4238 (diff)
tracing/ftrace: separate events tracing and stats tracing engine
Impact: tracing's Api change Currently, the stat tracing depends on the events tracing. When you switch to a new tracer, the stats files of the previous tracer will disappear. But it's more scalable to separate those two engines. This way, we can keep the stat files of one or several tracers when we want, without bothering of multiple tracer stat files or tracer switching. To build/destroys its stats files, a tracer just have to call register_stat_tracer/unregister_stat_tracer everytimes it wants to. Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com> Signed-off-by: Steven Rostedt <srostedt@redhat.com> Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'kernel/trace')
-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 */