Blame view
kernel/trace/trace_stat.c
8.19 KB
dbd0b4b33 tracing/ftrace: p... |
1 2 3 |
/* * Infrastructure for statistic tracing (histogram output). * |
8f184f273 tracing/stat: rep... |
4 |
* Copyright (C) 2008-2009 Frederic Weisbecker <fweisbec@gmail.com> |
dbd0b4b33 tracing/ftrace: p... |
5 6 7 8 9 10 11 12 |
* * Based on the code from trace_branch.c which is * Copyright (C) 2008 Steven Rostedt <srostedt@redhat.com> * */ #include <linux/list.h> |
5a0e3ad6a include cleanup: ... |
13 |
#include <linux/slab.h> |
8f184f273 tracing/stat: rep... |
14 |
#include <linux/rbtree.h> |
dbd0b4b33 tracing/ftrace: p... |
15 |
#include <linux/debugfs.h> |
002bb86d8 tracing/ftrace: s... |
16 |
#include "trace_stat.h" |
dbd0b4b33 tracing/ftrace: p... |
17 |
#include "trace.h" |
8f184f273 tracing/stat: rep... |
18 19 20 21 22 23 24 |
/* * List of stat red-black nodes from a tracer * We use a such tree to sort quickly the stat * entries from the tracer. */ struct stat_node { struct rb_node node; |
55922173f tracing: trace_st... |
25 |
void *stat; |
dbd0b4b33 tracing/ftrace: p... |
26 |
}; |
034939b65 tracing/ftrace: h... |
27 |
/* A stat session is the stats output in one file */ |
0d64f8342 tracing/stat: rep... |
28 |
struct stat_session { |
002bb86d8 tracing/ftrace: s... |
29 |
struct list_head session_list; |
55922173f tracing: trace_st... |
30 |
struct tracer_stat *ts; |
8f184f273 tracing/stat: rep... |
31 |
struct rb_root stat_root; |
55922173f tracing: trace_st... |
32 |
struct mutex stat_mutex; |
002bb86d8 tracing/ftrace: s... |
33 |
struct dentry *file; |
034939b65 tracing/ftrace: h... |
34 |
}; |
dbd0b4b33 tracing/ftrace: p... |
35 |
|
73d8b8bc4 tracing: fix typi... |
36 |
/* All of the sessions currently in use. Each stat file embed one session */ |
002bb86d8 tracing/ftrace: s... |
37 38 39 40 |
static LIST_HEAD(all_stat_sessions); static DEFINE_MUTEX(all_stat_sessions_mutex); /* The root directory for all stat files */ |
55922173f tracing: trace_st... |
41 |
static struct dentry *stat_dir; |
dbd0b4b33 tracing/ftrace: p... |
42 |
|
8f184f273 tracing/stat: rep... |
43 44 45 46 47 48 49 |
/* * Iterate through the rbtree using a post order traversal path * to release the next node. * It won't necessary release one at each iteration * but it will at least advance closer to the next one * to be released. */ |
d8ea37d5d tracing/stat: Add... |
50 51 |
static struct rb_node *release_next(struct tracer_stat *ts, struct rb_node *node) |
8f184f273 tracing/stat: rep... |
52 53 54 55 56 57 58 59 60 61 |
{ struct stat_node *snode; struct rb_node *parent = rb_parent(node); if (node->rb_left) return node->rb_left; else if (node->rb_right) return node->rb_right; else { if (!parent) |
e16228069 tracing/stat: rem... |
62 63 |
; else if (parent->rb_left == node) |
8f184f273 tracing/stat: rep... |
64 65 66 67 68 |
parent->rb_left = NULL; else parent->rb_right = NULL; snode = container_of(node, struct stat_node, node); |
d8ea37d5d tracing/stat: Add... |
69 70 |
if (ts->stat_release) ts->stat_release(snode->stat); |
8f184f273 tracing/stat: rep... |
71 72 73 74 75 |
kfree(snode); return parent; } } |
dbd0b4b33 tracing/ftrace: p... |
76 |
|
636eacee3 tracing/stat: Fix... |
77 |
static void __reset_stat_session(struct stat_session *session) |
dbd0b4b33 tracing/ftrace: p... |
78 |
{ |
8f184f273 tracing/stat: rep... |
79 |
struct rb_node *node = session->stat_root.rb_node; |
dbd0b4b33 tracing/ftrace: p... |
80 |
|
8f184f273 tracing/stat: rep... |
81 |
while (node) |
d8ea37d5d tracing/stat: Add... |
82 |
node = release_next(session->ts, node); |
dbd0b4b33 tracing/ftrace: p... |
83 |
|
8f184f273 tracing/stat: rep... |
84 |
session->stat_root = RB_ROOT; |
dbd0b4b33 tracing/ftrace: p... |
85 |
} |
636eacee3 tracing/stat: Fix... |
86 87 88 89 90 91 |
static void reset_stat_session(struct stat_session *session) { mutex_lock(&session->stat_mutex); __reset_stat_session(session); mutex_unlock(&session->stat_mutex); } |
0d64f8342 tracing/stat: rep... |
92 |
static void destroy_session(struct stat_session *session) |
dbd0b4b33 tracing/ftrace: p... |
93 |
{ |
002bb86d8 tracing/ftrace: s... |
94 |
debugfs_remove(session->file); |
636eacee3 tracing/stat: Fix... |
95 |
__reset_stat_session(session); |
002bb86d8 tracing/ftrace: s... |
96 97 98 |
mutex_destroy(&session->stat_mutex); kfree(session); } |
034939b65 tracing/ftrace: h... |
99 |
|
8f184f273 tracing/stat: rep... |
100 |
typedef int (*cmp_stat_t)(void *, void *); |
dbd3fbdfe tracing/stat: do ... |
101 |
static int insert_stat(struct rb_root *root, void *stat, cmp_stat_t cmp) |
8f184f273 tracing/stat: rep... |
102 103 |
{ struct rb_node **new = &(root->rb_node), *parent = NULL; |
dbd3fbdfe tracing/stat: do ... |
104 105 106 107 108 109 |
struct stat_node *data; data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; data->stat = stat; |
8f184f273 tracing/stat: rep... |
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
/* * Figure out where to put new node * This is a descendent sorting */ while (*new) { struct stat_node *this; int result; this = container_of(*new, struct stat_node, node); result = cmp(data->stat, this->stat); parent = *new; if (result >= 0) new = &((*new)->rb_left); else new = &((*new)->rb_right); } rb_link_node(&data->node, parent, new); rb_insert_color(&data->node, root); |
dbd3fbdfe tracing/stat: do ... |
131 |
return 0; |
8f184f273 tracing/stat: rep... |
132 |
} |
dbd0b4b33 tracing/ftrace: p... |
133 134 |
/* * For tracers that don't provide a stat_cmp callback. |
dbd3fbdfe tracing/stat: do ... |
135 136 |
* This one will force an insertion as right-most node * in the rbtree. |
dbd0b4b33 tracing/ftrace: p... |
137 138 139 |
*/ static int dummy_cmp(void *p1, void *p2) { |
b3dd7ba7d tracing/stat: cha... |
140 |
return -1; |
dbd0b4b33 tracing/ftrace: p... |
141 142 143 |
} /* |
dbd3fbdfe tracing/stat: do ... |
144 |
* Initialize the stat rbtree at each trace_stat file opening. |
dbd0b4b33 tracing/ftrace: p... |
145 146 147 |
* All of these copies and sorting are required on all opening * since the stats could have changed between two file sessions. */ |
0d64f8342 tracing/stat: rep... |
148 |
static int stat_seq_init(struct stat_session *session) |
dbd0b4b33 tracing/ftrace: p... |
149 |
{ |
034939b65 tracing/ftrace: h... |
150 |
struct tracer_stat *ts = session->ts; |
dbd3fbdfe tracing/stat: do ... |
151 |
struct rb_root *root = &session->stat_root; |
098335215 tracing: fix memo... |
152 |
void *stat; |
dbd0b4b33 tracing/ftrace: p... |
153 154 |
int ret = 0; int i; |
034939b65 tracing/ftrace: h... |
155 |
mutex_lock(&session->stat_mutex); |
636eacee3 tracing/stat: Fix... |
156 |
__reset_stat_session(session); |
dbd0b4b33 tracing/ftrace: p... |
157 |
|
034939b65 tracing/ftrace: h... |
158 159 |
if (!ts->stat_cmp) ts->stat_cmp = dummy_cmp; |
dbd0b4b33 tracing/ftrace: p... |
160 |
|
425480081 tracing: add hand... |
161 |
stat = ts->stat_start(ts); |
098335215 tracing: fix memo... |
162 163 |
if (!stat) goto exit; |
dbd3fbdfe tracing/stat: do ... |
164 165 |
ret = insert_stat(root, stat, ts->stat_cmp); if (ret) |
dbd0b4b33 tracing/ftrace: p... |
166 |
goto exit; |
dbd0b4b33 tracing/ftrace: p... |
167 168 |
/* |
dbd3fbdfe tracing/stat: do ... |
169 |
* Iterate over the tracer stat entries and store them in an rbtree. |
dbd0b4b33 tracing/ftrace: p... |
170 171 |
*/ for (i = 1; ; i++) { |
098335215 tracing: fix memo... |
172 173 174 175 176 |
stat = ts->stat_next(stat, i); /* End of insertion */ if (!stat) break; |
dbd3fbdfe tracing/stat: do ... |
177 178 179 |
ret = insert_stat(root, stat, ts->stat_cmp); if (ret) goto exit_free_rbtree; |
dbd0b4b33 tracing/ftrace: p... |
180 |
} |
8f184f273 tracing/stat: rep... |
181 |
|
dbd0b4b33 tracing/ftrace: p... |
182 |
exit: |
034939b65 tracing/ftrace: h... |
183 |
mutex_unlock(&session->stat_mutex); |
dbd0b4b33 tracing/ftrace: p... |
184 |
return ret; |
dbd3fbdfe tracing/stat: do ... |
185 |
exit_free_rbtree: |
636eacee3 tracing/stat: Fix... |
186 |
__reset_stat_session(session); |
034939b65 tracing/ftrace: h... |
187 |
mutex_unlock(&session->stat_mutex); |
dbd0b4b33 tracing/ftrace: p... |
188 189 190 191 192 193 |
return ret; } static void *stat_seq_start(struct seq_file *s, loff_t *pos) { |
0d64f8342 tracing/stat: rep... |
194 |
struct stat_session *session = s->private; |
8f184f273 tracing/stat: rep... |
195 |
struct rb_node *node; |
97d53202a trace_stat: Fix m... |
196 |
int n = *pos; |
8f184f273 tracing/stat: rep... |
197 |
int i; |
dbd0b4b33 tracing/ftrace: p... |
198 |
|
dbd3fbdfe tracing/stat: do ... |
199 |
/* Prevent from tracer switch or rbtree modification */ |
034939b65 tracing/ftrace: h... |
200 |
mutex_lock(&session->stat_mutex); |
dbd0b4b33 tracing/ftrace: p... |
201 202 |
/* If we are in the beginning of the file, print the headers */ |
97d53202a trace_stat: Fix m... |
203 204 205 206 207 |
if (session->ts->stat_headers) { if (n == 0) return SEQ_START_TOKEN; n--; } |
dbd0b4b33 tracing/ftrace: p... |
208 |
|
8f184f273 tracing/stat: rep... |
209 |
node = rb_first(&session->stat_root); |
97d53202a trace_stat: Fix m... |
210 |
for (i = 0; node && i < n; i++) |
8f184f273 tracing/stat: rep... |
211 |
node = rb_next(node); |
8f184f273 tracing/stat: rep... |
212 |
return node; |
dbd0b4b33 tracing/ftrace: p... |
213 214 215 216 |
} static void *stat_seq_next(struct seq_file *s, void *p, loff_t *pos) { |
0d64f8342 tracing/stat: rep... |
217 |
struct stat_session *session = s->private; |
8f184f273 tracing/stat: rep... |
218 219 220 |
struct rb_node *node = p; (*pos)++; |
dbd0b4b33 tracing/ftrace: p... |
221 |
|
e6f489013 trace_stat: don't... |
222 |
if (p == SEQ_START_TOKEN) |
8f184f273 tracing/stat: rep... |
223 |
return rb_first(&session->stat_root); |
e6f489013 trace_stat: don't... |
224 |
|
8f184f273 tracing/stat: rep... |
225 |
return rb_next(node); |
dbd0b4b33 tracing/ftrace: p... |
226 |
} |
034939b65 tracing/ftrace: h... |
227 |
static void stat_seq_stop(struct seq_file *s, void *p) |
dbd0b4b33 tracing/ftrace: p... |
228 |
{ |
0d64f8342 tracing/stat: rep... |
229 |
struct stat_session *session = s->private; |
034939b65 tracing/ftrace: h... |
230 |
mutex_unlock(&session->stat_mutex); |
dbd0b4b33 tracing/ftrace: p... |
231 232 233 234 |
} static int stat_seq_show(struct seq_file *s, void *v) { |
0d64f8342 tracing/stat: rep... |
235 |
struct stat_session *session = s->private; |
8f184f273 tracing/stat: rep... |
236 |
struct stat_node *l = container_of(v, struct stat_node, node); |
ff288b274 tracing/ftrace: f... |
237 |
|
e6f489013 trace_stat: don't... |
238 239 |
if (v == SEQ_START_TOKEN) return session->ts->stat_headers(s); |
034939b65 tracing/ftrace: h... |
240 |
return session->ts->stat_show(s, l->stat); |
dbd0b4b33 tracing/ftrace: p... |
241 242 243 |
} static const struct seq_operations trace_stat_seq_ops = { |
55922173f tracing: trace_st... |
244 245 246 247 |
.start = stat_seq_start, .next = stat_seq_next, .stop = stat_seq_stop, .show = stat_seq_show |
dbd0b4b33 tracing/ftrace: p... |
248 |
}; |
034939b65 tracing/ftrace: h... |
249 |
/* The session stat is refilled and resorted at each stat file opening */ |
dbd0b4b33 tracing/ftrace: p... |
250 251 252 |
static int tracing_stat_open(struct inode *inode, struct file *file) { int ret; |
636eacee3 tracing/stat: Fix... |
253 |
struct seq_file *m; |
0d64f8342 tracing/stat: rep... |
254 |
struct stat_session *session = inode->i_private; |
034939b65 tracing/ftrace: h... |
255 |
|
636eacee3 tracing/stat: Fix... |
256 257 258 |
ret = stat_seq_init(session); if (ret) return ret; |
dbd0b4b33 tracing/ftrace: p... |
259 |
ret = seq_open(file, &trace_stat_seq_ops); |
636eacee3 tracing/stat: Fix... |
260 261 262 |
if (ret) { reset_stat_session(session); return ret; |
dbd0b4b33 tracing/ftrace: p... |
263 |
} |
636eacee3 tracing/stat: Fix... |
264 265 |
m = file->private_data; m->private = session; |
dbd0b4b33 tracing/ftrace: p... |
266 267 |
return ret; } |
dbd0b4b33 tracing/ftrace: p... |
268 |
/* |
dbd3fbdfe tracing/stat: do ... |
269 |
* Avoid consuming memory with our now useless rbtree. |
dbd0b4b33 tracing/ftrace: p... |
270 271 272 |
*/ static int tracing_stat_release(struct inode *i, struct file *f) { |
0d64f8342 tracing/stat: rep... |
273 |
struct stat_session *session = i->i_private; |
034939b65 tracing/ftrace: h... |
274 |
|
034939b65 tracing/ftrace: h... |
275 |
reset_stat_session(session); |
034939b65 tracing/ftrace: h... |
276 |
|
636eacee3 tracing/stat: Fix... |
277 |
return seq_release(i, f); |
dbd0b4b33 tracing/ftrace: p... |
278 279 280 281 282 283 284 285 |
} static const struct file_operations tracing_stat_fops = { .open = tracing_stat_open, .read = seq_read, .llseek = seq_lseek, .release = tracing_stat_release }; |
002bb86d8 tracing/ftrace: s... |
286 |
static int tracing_stat_init(void) |
dbd0b4b33 tracing/ftrace: p... |
287 288 |
{ struct dentry *d_tracing; |
dbd0b4b33 tracing/ftrace: p... |
289 |
|
dbd0b4b33 tracing/ftrace: p... |
290 |
d_tracing = tracing_init_dentry(); |
034939b65 tracing/ftrace: h... |
291 292 |
stat_dir = debugfs_create_dir("trace_stat", d_tracing); if (!stat_dir) |
dbd0b4b33 tracing/ftrace: p... |
293 294 295 296 297 |
pr_warning("Could not create debugfs " "'trace_stat' entry "); return 0; } |
002bb86d8 tracing/ftrace: s... |
298 |
|
0d64f8342 tracing/stat: rep... |
299 |
static int init_stat_file(struct stat_session *session) |
002bb86d8 tracing/ftrace: s... |
300 301 302 303 304 305 306 307 308 309 310 |
{ if (!stat_dir && tracing_stat_init()) return -ENODEV; session->file = debugfs_create_file(session->ts->name, 0644, stat_dir, session, &tracing_stat_fops); if (!session->file) return -ENOMEM; return 0; } |
55922173f tracing: trace_st... |
311 312 313 |
int register_stat_tracer(struct tracer_stat *trace) { |
43bd12362 tracing/stat: rem... |
314 |
struct stat_session *session, *node; |
55922173f tracing: trace_st... |
315 316 317 318 319 320 321 322 323 324 |
int ret; if (!trace) return -EINVAL; if (!trace->stat_start || !trace->stat_next || !trace->stat_show) return -EINVAL; /* Already registered? */ mutex_lock(&all_stat_sessions_mutex); |
43bd12362 tracing/stat: rem... |
325 |
list_for_each_entry(node, &all_stat_sessions, session_list) { |
55922173f tracing: trace_st... |
326 327 328 329 330 331 332 333 |
if (node->ts == trace) { mutex_unlock(&all_stat_sessions_mutex); return -EINVAL; } } mutex_unlock(&all_stat_sessions_mutex); /* Init the session */ |
8f184f273 tracing/stat: rep... |
334 |
session = kzalloc(sizeof(*session), GFP_KERNEL); |
55922173f tracing: trace_st... |
335 336 337 338 339 |
if (!session) return -ENOMEM; session->ts = trace; INIT_LIST_HEAD(&session->session_list); |
55922173f tracing: trace_st... |
340 |
mutex_init(&session->stat_mutex); |
55922173f tracing: trace_st... |
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 |
ret = init_stat_file(session); if (ret) { destroy_session(session); return ret; } /* Register */ mutex_lock(&all_stat_sessions_mutex); list_add_tail(&session->session_list, &all_stat_sessions); mutex_unlock(&all_stat_sessions_mutex); return 0; } void unregister_stat_tracer(struct tracer_stat *trace) { |
0d64f8342 tracing/stat: rep... |
358 |
struct stat_session *node, *tmp; |
55922173f tracing: trace_st... |
359 360 361 362 363 364 365 366 367 368 369 |
mutex_lock(&all_stat_sessions_mutex); list_for_each_entry_safe(node, tmp, &all_stat_sessions, session_list) { if (node->ts == trace) { list_del(&node->session_list); destroy_session(node); break; } } mutex_unlock(&all_stat_sessions_mutex); } |