aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/trace/trace_stat.c
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/trace_stat.c
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/trace_stat.c')
-rw-r--r--kernel/trace/trace_stat.c191
1 files changed, 81 insertions, 110 deletions
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}