Blame view

kernel/trace/trace_stat.c 8.19 KB
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
1
2
3
  /*
   * Infrastructure for statistic tracing (histogram output).
   *
8f184f273   Frederic Weisbecker   tracing/stat: rep...
4
   * Copyright (C) 2008-2009 Frederic Weisbecker <fweisbec@gmail.com>
dbd0b4b33   Frederic Weisbecker   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   Tejun Heo   include cleanup: ...
13
  #include <linux/slab.h>
8f184f273   Frederic Weisbecker   tracing/stat: rep...
14
  #include <linux/rbtree.h>
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
15
  #include <linux/debugfs.h>
002bb86d8   Frederic Weisbecker   tracing/ftrace: s...
16
  #include "trace_stat.h"
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
17
  #include "trace.h"
8f184f273   Frederic Weisbecker   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   Ingo Molnar   tracing: trace_st...
25
  	void			*stat;
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
26
  };
034939b65   Frederic Weisbecker   tracing/ftrace: h...
27
  /* A stat session is the stats output in one file */
0d64f8342   Frederic Weisbecker   tracing/stat: rep...
28
  struct stat_session {
002bb86d8   Frederic Weisbecker   tracing/ftrace: s...
29
  	struct list_head	session_list;
55922173f   Ingo Molnar   tracing: trace_st...
30
  	struct tracer_stat	*ts;
8f184f273   Frederic Weisbecker   tracing/stat: rep...
31
  	struct rb_root		stat_root;
55922173f   Ingo Molnar   tracing: trace_st...
32
  	struct mutex		stat_mutex;
002bb86d8   Frederic Weisbecker   tracing/ftrace: s...
33
  	struct dentry		*file;
034939b65   Frederic Weisbecker   tracing/ftrace: h...
34
  };
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
35

73d8b8bc4   Wenji Huang   tracing: fix typi...
36
  /* All of the sessions currently in use. Each stat file embed one session */
002bb86d8   Frederic Weisbecker   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   Ingo Molnar   tracing: trace_st...
41
  static struct dentry		*stat_dir;
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
42

8f184f273   Frederic Weisbecker   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   Lai Jiangshan   tracing/stat: Add...
50
51
  static struct rb_node *release_next(struct tracer_stat *ts,
  				    struct rb_node *node)
8f184f273   Frederic Weisbecker   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   Li Zefan   tracing/stat: rem...
62
63
  			;
  		else if (parent->rb_left == node)
8f184f273   Frederic Weisbecker   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   Lai Jiangshan   tracing/stat: Add...
69
70
  		if (ts->stat_release)
  			ts->stat_release(snode->stat);
8f184f273   Frederic Weisbecker   tracing/stat: rep...
71
72
73
74
75
  		kfree(snode);
  
  		return parent;
  	}
  }
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
76

636eacee3   Li Zefan   tracing/stat: Fix...
77
  static void __reset_stat_session(struct stat_session *session)
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
78
  {
8f184f273   Frederic Weisbecker   tracing/stat: rep...
79
  	struct rb_node *node = session->stat_root.rb_node;
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
80

8f184f273   Frederic Weisbecker   tracing/stat: rep...
81
  	while (node)
d8ea37d5d   Lai Jiangshan   tracing/stat: Add...
82
  		node = release_next(session->ts, node);
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
83

8f184f273   Frederic Weisbecker   tracing/stat: rep...
84
  	session->stat_root = RB_ROOT;
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
85
  }
636eacee3   Li Zefan   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   Frederic Weisbecker   tracing/stat: rep...
92
  static void destroy_session(struct stat_session *session)
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
93
  {
002bb86d8   Frederic Weisbecker   tracing/ftrace: s...
94
  	debugfs_remove(session->file);
636eacee3   Li Zefan   tracing/stat: Fix...
95
  	__reset_stat_session(session);
002bb86d8   Frederic Weisbecker   tracing/ftrace: s...
96
97
98
  	mutex_destroy(&session->stat_mutex);
  	kfree(session);
  }
034939b65   Frederic Weisbecker   tracing/ftrace: h...
99

8f184f273   Frederic Weisbecker   tracing/stat: rep...
100
  typedef int (*cmp_stat_t)(void *, void *);
dbd3fbdfe   Li Zefan   tracing/stat: do ...
101
  static int insert_stat(struct rb_root *root, void *stat, cmp_stat_t cmp)
8f184f273   Frederic Weisbecker   tracing/stat: rep...
102
103
  {
  	struct rb_node **new = &(root->rb_node), *parent = NULL;
dbd3fbdfe   Li Zefan   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   Frederic Weisbecker   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   Li Zefan   tracing/stat: do ...
131
  	return 0;
8f184f273   Frederic Weisbecker   tracing/stat: rep...
132
  }
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
133
134
  /*
   * For tracers that don't provide a stat_cmp callback.
dbd3fbdfe   Li Zefan   tracing/stat: do ...
135
136
   * This one will force an insertion as right-most node
   * in the rbtree.
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
137
138
139
   */
  static int dummy_cmp(void *p1, void *p2)
  {
b3dd7ba7d   Li Zefan   tracing/stat: cha...
140
  	return -1;
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
141
142
143
  }
  
  /*
dbd3fbdfe   Li Zefan   tracing/stat: do ...
144
   * Initialize the stat rbtree at each trace_stat file opening.
dbd0b4b33   Frederic Weisbecker   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   Frederic Weisbecker   tracing/stat: rep...
148
  static int stat_seq_init(struct stat_session *session)
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
149
  {
034939b65   Frederic Weisbecker   tracing/ftrace: h...
150
  	struct tracer_stat *ts = session->ts;
dbd3fbdfe   Li Zefan   tracing/stat: do ...
151
  	struct rb_root *root = &session->stat_root;
098335215   Steven Rostedt   tracing: fix memo...
152
  	void *stat;
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
153
154
  	int ret = 0;
  	int i;
034939b65   Frederic Weisbecker   tracing/ftrace: h...
155
  	mutex_lock(&session->stat_mutex);
636eacee3   Li Zefan   tracing/stat: Fix...
156
  	__reset_stat_session(session);
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
157

034939b65   Frederic Weisbecker   tracing/ftrace: h...
158
159
  	if (!ts->stat_cmp)
  		ts->stat_cmp = dummy_cmp;
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
160

425480081   Steven Rostedt   tracing: add hand...
161
  	stat = ts->stat_start(ts);
098335215   Steven Rostedt   tracing: fix memo...
162
163
  	if (!stat)
  		goto exit;
dbd3fbdfe   Li Zefan   tracing/stat: do ...
164
165
  	ret = insert_stat(root, stat, ts->stat_cmp);
  	if (ret)
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
166
  		goto exit;
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
167
168
  
  	/*
dbd3fbdfe   Li Zefan   tracing/stat: do ...
169
  	 * Iterate over the tracer stat entries and store them in an rbtree.
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
170
171
  	 */
  	for (i = 1; ; i++) {
098335215   Steven Rostedt   tracing: fix memo...
172
173
174
175
176
  		stat = ts->stat_next(stat, i);
  
  		/* End of insertion */
  		if (!stat)
  			break;
dbd3fbdfe   Li Zefan   tracing/stat: do ...
177
178
179
  		ret = insert_stat(root, stat, ts->stat_cmp);
  		if (ret)
  			goto exit_free_rbtree;
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
180
  	}
8f184f273   Frederic Weisbecker   tracing/stat: rep...
181

dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
182
  exit:
034939b65   Frederic Weisbecker   tracing/ftrace: h...
183
  	mutex_unlock(&session->stat_mutex);
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
184
  	return ret;
dbd3fbdfe   Li Zefan   tracing/stat: do ...
185
  exit_free_rbtree:
636eacee3   Li Zefan   tracing/stat: Fix...
186
  	__reset_stat_session(session);
034939b65   Frederic Weisbecker   tracing/ftrace: h...
187
  	mutex_unlock(&session->stat_mutex);
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
188
189
190
191
192
193
  	return ret;
  }
  
  
  static void *stat_seq_start(struct seq_file *s, loff_t *pos)
  {
0d64f8342   Frederic Weisbecker   tracing/stat: rep...
194
  	struct stat_session *session = s->private;
8f184f273   Frederic Weisbecker   tracing/stat: rep...
195
  	struct rb_node *node;
97d53202a   Li Zefan   trace_stat: Fix m...
196
  	int n = *pos;
8f184f273   Frederic Weisbecker   tracing/stat: rep...
197
  	int i;
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
198

dbd3fbdfe   Li Zefan   tracing/stat: do ...
199
  	/* Prevent from tracer switch or rbtree modification */
034939b65   Frederic Weisbecker   tracing/ftrace: h...
200
  	mutex_lock(&session->stat_mutex);
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
201
202
  
  	/* If we are in the beginning of the file, print the headers */
97d53202a   Li Zefan   trace_stat: Fix m...
203
204
205
206
207
  	if (session->ts->stat_headers) {
  		if (n == 0)
  			return SEQ_START_TOKEN;
  		n--;
  	}
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
208

8f184f273   Frederic Weisbecker   tracing/stat: rep...
209
  	node = rb_first(&session->stat_root);
97d53202a   Li Zefan   trace_stat: Fix m...
210
  	for (i = 0; node && i < n; i++)
8f184f273   Frederic Weisbecker   tracing/stat: rep...
211
  		node = rb_next(node);
8f184f273   Frederic Weisbecker   tracing/stat: rep...
212
  	return node;
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
213
214
215
216
  }
  
  static void *stat_seq_next(struct seq_file *s, void *p, loff_t *pos)
  {
0d64f8342   Frederic Weisbecker   tracing/stat: rep...
217
  	struct stat_session *session = s->private;
8f184f273   Frederic Weisbecker   tracing/stat: rep...
218
219
220
  	struct rb_node *node = p;
  
  	(*pos)++;
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
221

e6f489013   Lai Jiangshan   trace_stat: don't...
222
  	if (p == SEQ_START_TOKEN)
8f184f273   Frederic Weisbecker   tracing/stat: rep...
223
  		return rb_first(&session->stat_root);
e6f489013   Lai Jiangshan   trace_stat: don't...
224

8f184f273   Frederic Weisbecker   tracing/stat: rep...
225
  	return rb_next(node);
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
226
  }
034939b65   Frederic Weisbecker   tracing/ftrace: h...
227
  static void stat_seq_stop(struct seq_file *s, void *p)
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
228
  {
0d64f8342   Frederic Weisbecker   tracing/stat: rep...
229
  	struct stat_session *session = s->private;
034939b65   Frederic Weisbecker   tracing/ftrace: h...
230
  	mutex_unlock(&session->stat_mutex);
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
231
232
233
234
  }
  
  static int stat_seq_show(struct seq_file *s, void *v)
  {
0d64f8342   Frederic Weisbecker   tracing/stat: rep...
235
  	struct stat_session *session = s->private;
8f184f273   Frederic Weisbecker   tracing/stat: rep...
236
  	struct stat_node *l = container_of(v, struct stat_node, node);
ff288b274   Frederic Weisbecker   tracing/ftrace: f...
237

e6f489013   Lai Jiangshan   trace_stat: don't...
238
239
  	if (v == SEQ_START_TOKEN)
  		return session->ts->stat_headers(s);
034939b65   Frederic Weisbecker   tracing/ftrace: h...
240
  	return session->ts->stat_show(s, l->stat);
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
241
242
243
  }
  
  static const struct seq_operations trace_stat_seq_ops = {
55922173f   Ingo Molnar   tracing: trace_st...
244
245
246
247
  	.start		= stat_seq_start,
  	.next		= stat_seq_next,
  	.stop		= stat_seq_stop,
  	.show		= stat_seq_show
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
248
  };
034939b65   Frederic Weisbecker   tracing/ftrace: h...
249
  /* The session stat is refilled and resorted at each stat file opening */
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
250
251
252
  static int tracing_stat_open(struct inode *inode, struct file *file)
  {
  	int ret;
636eacee3   Li Zefan   tracing/stat: Fix...
253
  	struct seq_file *m;
0d64f8342   Frederic Weisbecker   tracing/stat: rep...
254
  	struct stat_session *session = inode->i_private;
034939b65   Frederic Weisbecker   tracing/ftrace: h...
255

636eacee3   Li Zefan   tracing/stat: Fix...
256
257
258
  	ret = stat_seq_init(session);
  	if (ret)
  		return ret;
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
259
  	ret = seq_open(file, &trace_stat_seq_ops);
636eacee3   Li Zefan   tracing/stat: Fix...
260
261
262
  	if (ret) {
  		reset_stat_session(session);
  		return ret;
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
263
  	}
636eacee3   Li Zefan   tracing/stat: Fix...
264
265
  	m = file->private_data;
  	m->private = session;
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
266
267
  	return ret;
  }
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
268
  /*
dbd3fbdfe   Li Zefan   tracing/stat: do ...
269
   * Avoid consuming memory with our now useless rbtree.
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
270
271
272
   */
  static int tracing_stat_release(struct inode *i, struct file *f)
  {
0d64f8342   Frederic Weisbecker   tracing/stat: rep...
273
  	struct stat_session *session = i->i_private;
034939b65   Frederic Weisbecker   tracing/ftrace: h...
274

034939b65   Frederic Weisbecker   tracing/ftrace: h...
275
  	reset_stat_session(session);
034939b65   Frederic Weisbecker   tracing/ftrace: h...
276

636eacee3   Li Zefan   tracing/stat: Fix...
277
  	return seq_release(i, f);
dbd0b4b33   Frederic Weisbecker   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   Frederic Weisbecker   tracing/ftrace: s...
286
  static int tracing_stat_init(void)
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
287
288
  {
  	struct dentry *d_tracing;
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
289

dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
290
  	d_tracing = tracing_init_dentry();
034939b65   Frederic Weisbecker   tracing/ftrace: h...
291
292
  	stat_dir = debugfs_create_dir("trace_stat", d_tracing);
  	if (!stat_dir)
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
293
294
295
296
297
  		pr_warning("Could not create debugfs "
  			   "'trace_stat' entry
  ");
  	return 0;
  }
002bb86d8   Frederic Weisbecker   tracing/ftrace: s...
298

0d64f8342   Frederic Weisbecker   tracing/stat: rep...
299
  static int init_stat_file(struct stat_session *session)
002bb86d8   Frederic Weisbecker   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   Ingo Molnar   tracing: trace_st...
311
312
313
  
  int register_stat_tracer(struct tracer_stat *trace)
  {
43bd12362   Frederic Weisbecker   tracing/stat: rem...
314
  	struct stat_session *session, *node;
55922173f   Ingo Molnar   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   Frederic Weisbecker   tracing/stat: rem...
325
  	list_for_each_entry(node, &all_stat_sessions, session_list) {
55922173f   Ingo Molnar   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   Frederic Weisbecker   tracing/stat: rep...
334
  	session = kzalloc(sizeof(*session), GFP_KERNEL);
55922173f   Ingo Molnar   tracing: trace_st...
335
336
337
338
339
  	if (!session)
  		return -ENOMEM;
  
  	session->ts = trace;
  	INIT_LIST_HEAD(&session->session_list);
55922173f   Ingo Molnar   tracing: trace_st...
340
  	mutex_init(&session->stat_mutex);
55922173f   Ingo Molnar   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   Frederic Weisbecker   tracing/stat: rep...
358
  	struct stat_session *node, *tmp;
55922173f   Ingo Molnar   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);
  }