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