4cb4ff27646d70b1f014d1d6561c4f0f1aeb3d2b
[safe/jmp/linux-2.6] / kernel / trace / trace_stat.c
1 /*
2  * Infrastructure for statistic tracing (histogram output).
3  *
4  * Copyright (C) 2008 Frederic Weisbecker <fweisbec@gmail.com>
5  *
6  * Based on the code from trace_branch.c which is
7  * Copyright (C) 2008 Steven Rostedt <srostedt@redhat.com>
8  *
9  */
10
11
12 #include <linux/list.h>
13 #include <linux/seq_file.h>
14 #include <linux/debugfs.h>
15 #include "trace.h"
16
17
18 /* List of stat entries from a tracer */
19 struct trace_stat_list {
20         struct list_head list;
21         void *stat;
22 };
23
24 static LIST_HEAD(stat_list);
25
26 /*
27  * This is a copy of the current tracer to avoid racy
28  * and dangerous output while the current tracer is
29  * switched.
30  */
31 static struct tracer current_tracer;
32
33 /*
34  * Protect both the current tracer and the global
35  * stat list.
36  */
37 static DEFINE_MUTEX(stat_list_mutex);
38
39
40 static void reset_stat_list(void)
41 {
42         struct trace_stat_list *node, *next;
43
44         list_for_each_entry_safe(node, next, &stat_list, list)
45                 kfree(node);
46
47         INIT_LIST_HEAD(&stat_list);
48 }
49
50 void init_tracer_stat(struct tracer *trace)
51 {
52         mutex_lock(&stat_list_mutex);
53         current_tracer = *trace;
54         mutex_unlock(&stat_list_mutex);
55 }
56
57 /*
58  * For tracers that don't provide a stat_cmp callback.
59  * This one will force an immediate insertion on tail of
60  * the list.
61  */
62 static int dummy_cmp(void *p1, void *p2)
63 {
64         return 1;
65 }
66
67 /*
68  * Initialize the stat list at each trace_stat file opening.
69  * All of these copies and sorting are required on all opening
70  * since the stats could have changed between two file sessions.
71  */
72 static int stat_seq_init(void)
73 {
74         struct trace_stat_list *iter_entry, *new_entry;
75         void *prev_stat;
76         int ret = 0;
77         int i;
78
79         mutex_lock(&stat_list_mutex);
80         reset_stat_list();
81
82         if (!current_tracer.stat_start || !current_tracer.stat_next ||
83                                         !current_tracer.stat_show)
84                 goto exit;
85
86         if (!current_tracer.stat_cmp)
87                 current_tracer.stat_cmp = dummy_cmp;
88
89         /*
90          * The first entry. Actually this is the second, but the first
91          * one (the stat_list head) is pointless.
92          */
93         new_entry = kmalloc(sizeof(struct trace_stat_list), GFP_KERNEL);
94         if (!new_entry) {
95                 ret = -ENOMEM;
96                 goto exit;
97         }
98
99         INIT_LIST_HEAD(&new_entry->list);
100         list_add(&new_entry->list, &stat_list);
101         new_entry->stat = current_tracer.stat_start();
102
103         prev_stat = new_entry->stat;
104
105         /*
106          * Iterate over the tracer stat entries and store them in a sorted
107          * list.
108          */
109         for (i = 1; ; i++) {
110                 new_entry = kmalloc(sizeof(struct trace_stat_list), GFP_KERNEL);
111                 if (!new_entry) {
112                         ret = -ENOMEM;
113                         goto exit_free_list;
114                 }
115
116                 INIT_LIST_HEAD(&new_entry->list);
117                 new_entry->stat = current_tracer.stat_next(prev_stat, i);
118
119                 /* End of insertion */
120                 if (!new_entry->stat)
121                         break;
122
123                 list_for_each_entry(iter_entry, &stat_list, list) {
124                         /* Insertion with a descendent sorting */
125                         if (current_tracer.stat_cmp(new_entry->stat,
126                                                 iter_entry->stat) > 0) {
127
128                                 list_add_tail(&new_entry->list,
129                                                 &iter_entry->list);
130                                 break;
131
132                         /* The current smaller value */
133                         } else if (list_is_last(&iter_entry->list,
134                                                 &stat_list)) {
135                                 list_add(&new_entry->list, &iter_entry->list);
136                                 break;
137                         }
138                 }
139
140                 prev_stat = new_entry->stat;
141         }
142 exit:
143         mutex_unlock(&stat_list_mutex);
144         return ret;
145
146 exit_free_list:
147         reset_stat_list();
148         mutex_unlock(&stat_list_mutex);
149         return ret;
150 }
151
152
153 static void *stat_seq_start(struct seq_file *s, loff_t *pos)
154 {
155         struct list_head *l = (struct list_head *)s->private;
156
157         /* Prevent from tracer switch or stat_list modification */
158         mutex_lock(&stat_list_mutex);
159
160         /* If we are in the beginning of the file, print the headers */
161         if (!*pos && current_tracer.stat_headers)
162                 current_tracer.stat_headers(s);
163
164         return seq_list_start(l, *pos);
165 }
166
167 static void *stat_seq_next(struct seq_file *s, void *p, loff_t *pos)
168 {
169         struct list_head *l = (struct list_head *)s->private;
170
171         return seq_list_next(p, l, pos);
172 }
173
174 static void stat_seq_stop(struct seq_file *m, void *p)
175 {
176         mutex_unlock(&stat_list_mutex);
177 }
178
179 static int stat_seq_show(struct seq_file *s, void *v)
180 {
181         struct trace_stat_list *entry =
182                 list_entry(v, struct trace_stat_list, list);
183
184         return current_tracer.stat_show(s, entry->stat);
185 }
186
187 static const struct seq_operations trace_stat_seq_ops = {
188         .start = stat_seq_start,
189         .next = stat_seq_next,
190         .stop = stat_seq_stop,
191         .show = stat_seq_show
192 };
193
194 static int tracing_stat_open(struct inode *inode, struct file *file)
195 {
196         int ret;
197
198         ret = seq_open(file, &trace_stat_seq_ops);
199         if (!ret) {
200                 struct seq_file *m = file->private_data;
201                 m->private = &stat_list;
202                 ret = stat_seq_init();
203         }
204
205         return ret;
206 }
207
208
209 /*
210  * Avoid consuming memory with our now useless list.
211  */
212 static int tracing_stat_release(struct inode *i, struct file *f)
213 {
214         mutex_lock(&stat_list_mutex);
215         reset_stat_list();
216         mutex_unlock(&stat_list_mutex);
217         return 0;
218 }
219
220 static const struct file_operations tracing_stat_fops = {
221         .open           = tracing_stat_open,
222         .read           = seq_read,
223         .llseek         = seq_lseek,
224         .release        = tracing_stat_release
225 };
226
227 static int __init tracing_stat_init(void)
228 {
229         struct dentry *d_tracing;
230         struct dentry *entry;
231
232         d_tracing = tracing_init_dentry();
233
234         entry = debugfs_create_file("trace_stat", 0444, d_tracing,
235                                         NULL,
236                                     &tracing_stat_fops);
237         if (!entry)
238                 pr_warning("Could not create debugfs "
239                            "'trace_stat' entry\n");
240         return 0;
241 }
242 fs_initcall(tracing_stat_init);