Blame view

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

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

9cd804ac1   Cody P Schafer   trace/trace_stat:...
44
  static void __reset_stat_session(struct stat_session *session)
8f184f273   Frederic Weisbecker   tracing/stat: rep...
45
  {
9cd804ac1   Cody P Schafer   trace/trace_stat:...
46
  	struct stat_node *snode, *n;
8f184f273   Frederic Weisbecker   tracing/stat: rep...
47

9cd804ac1   Cody P Schafer   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   Frederic Weisbecker   tracing/stat: rep...
51
  		kfree(snode);
8f184f273   Frederic Weisbecker   tracing/stat: rep...
52
  	}
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
53

8f184f273   Frederic Weisbecker   tracing/stat: rep...
54
  	session->stat_root = RB_ROOT;
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
55
  }
636eacee3   Li Zefan   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   Frederic Weisbecker   tracing/stat: rep...
62
  static void destroy_session(struct stat_session *session)
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
63
  {
8434dc934   Steven Rostedt (Red Hat)   tracing: Convert ...
64
  	tracefs_remove(session->file);
636eacee3   Li Zefan   tracing/stat: Fix...
65
  	__reset_stat_session(session);
002bb86d8   Frederic Weisbecker   tracing/ftrace: s...
66
67
68
  	mutex_destroy(&session->stat_mutex);
  	kfree(session);
  }
034939b65   Frederic Weisbecker   tracing/ftrace: h...
69

8f184f273   Frederic Weisbecker   tracing/stat: rep...
70
  typedef int (*cmp_stat_t)(void *, void *);
dbd3fbdfe   Li Zefan   tracing/stat: do ...
71
  static int insert_stat(struct rb_root *root, void *stat, cmp_stat_t cmp)
8f184f273   Frederic Weisbecker   tracing/stat: rep...
72
73
  {
  	struct rb_node **new = &(root->rb_node), *parent = NULL;
dbd3fbdfe   Li Zefan   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   Frederic Weisbecker   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   Li Zefan   tracing/stat: do ...
101
  	return 0;
8f184f273   Frederic Weisbecker   tracing/stat: rep...
102
  }
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
103
104
  /*
   * For tracers that don't provide a stat_cmp callback.
dbd3fbdfe   Li Zefan   tracing/stat: do ...
105
106
   * This one will force an insertion as right-most node
   * in the rbtree.
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
107
108
109
   */
  static int dummy_cmp(void *p1, void *p2)
  {
b3dd7ba7d   Li Zefan   tracing/stat: cha...
110
  	return -1;
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
111
112
113
  }
  
  /*
dbd3fbdfe   Li Zefan   tracing/stat: do ...
114
   * Initialize the stat rbtree at each trace_stat file opening.
dbd0b4b33   Frederic Weisbecker   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   Frederic Weisbecker   tracing/stat: rep...
118
  static int stat_seq_init(struct stat_session *session)
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
119
  {
034939b65   Frederic Weisbecker   tracing/ftrace: h...
120
  	struct tracer_stat *ts = session->ts;
dbd3fbdfe   Li Zefan   tracing/stat: do ...
121
  	struct rb_root *root = &session->stat_root;
098335215   Steven Rostedt   tracing: fix memo...
122
  	void *stat;
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
123
124
  	int ret = 0;
  	int i;
034939b65   Frederic Weisbecker   tracing/ftrace: h...
125
  	mutex_lock(&session->stat_mutex);
636eacee3   Li Zefan   tracing/stat: Fix...
126
  	__reset_stat_session(session);
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
127

034939b65   Frederic Weisbecker   tracing/ftrace: h...
128
129
  	if (!ts->stat_cmp)
  		ts->stat_cmp = dummy_cmp;
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
130

425480081   Steven Rostedt   tracing: add hand...
131
  	stat = ts->stat_start(ts);
098335215   Steven Rostedt   tracing: fix memo...
132
133
  	if (!stat)
  		goto exit;
dbd3fbdfe   Li Zefan   tracing/stat: do ...
134
135
  	ret = insert_stat(root, stat, ts->stat_cmp);
  	if (ret)
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
136
  		goto exit;
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
137
138
  
  	/*
dbd3fbdfe   Li Zefan   tracing/stat: do ...
139
  	 * Iterate over the tracer stat entries and store them in an rbtree.
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
140
141
  	 */
  	for (i = 1; ; i++) {
098335215   Steven Rostedt   tracing: fix memo...
142
143
144
145
146
  		stat = ts->stat_next(stat, i);
  
  		/* End of insertion */
  		if (!stat)
  			break;
dbd3fbdfe   Li Zefan   tracing/stat: do ...
147
148
149
  		ret = insert_stat(root, stat, ts->stat_cmp);
  		if (ret)
  			goto exit_free_rbtree;
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
150
  	}
8f184f273   Frederic Weisbecker   tracing/stat: rep...
151

dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
152
  exit:
034939b65   Frederic Weisbecker   tracing/ftrace: h...
153
  	mutex_unlock(&session->stat_mutex);
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
154
  	return ret;
dbd3fbdfe   Li Zefan   tracing/stat: do ...
155
  exit_free_rbtree:
636eacee3   Li Zefan   tracing/stat: Fix...
156
  	__reset_stat_session(session);
034939b65   Frederic Weisbecker   tracing/ftrace: h...
157
  	mutex_unlock(&session->stat_mutex);
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
158
159
160
161
162
163
  	return ret;
  }
  
  
  static void *stat_seq_start(struct seq_file *s, loff_t *pos)
  {
0d64f8342   Frederic Weisbecker   tracing/stat: rep...
164
  	struct stat_session *session = s->private;
8f184f273   Frederic Weisbecker   tracing/stat: rep...
165
  	struct rb_node *node;
97d53202a   Li Zefan   trace_stat: Fix m...
166
  	int n = *pos;
8f184f273   Frederic Weisbecker   tracing/stat: rep...
167
  	int i;
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
168

dbd3fbdfe   Li Zefan   tracing/stat: do ...
169
  	/* Prevent from tracer switch or rbtree modification */
034939b65   Frederic Weisbecker   tracing/ftrace: h...
170
  	mutex_lock(&session->stat_mutex);
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
171
172
  
  	/* If we are in the beginning of the file, print the headers */
97d53202a   Li Zefan   trace_stat: Fix m...
173
174
175
176
177
  	if (session->ts->stat_headers) {
  		if (n == 0)
  			return SEQ_START_TOKEN;
  		n--;
  	}
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
178

8f184f273   Frederic Weisbecker   tracing/stat: rep...
179
  	node = rb_first(&session->stat_root);
97d53202a   Li Zefan   trace_stat: Fix m...
180
  	for (i = 0; node && i < n; i++)
8f184f273   Frederic Weisbecker   tracing/stat: rep...
181
  		node = rb_next(node);
8f184f273   Frederic Weisbecker   tracing/stat: rep...
182
  	return node;
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
183
184
185
186
  }
  
  static void *stat_seq_next(struct seq_file *s, void *p, loff_t *pos)
  {
0d64f8342   Frederic Weisbecker   tracing/stat: rep...
187
  	struct stat_session *session = s->private;
8f184f273   Frederic Weisbecker   tracing/stat: rep...
188
189
190
  	struct rb_node *node = p;
  
  	(*pos)++;
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
191

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

8f184f273   Frederic Weisbecker   tracing/stat: rep...
195
  	return rb_next(node);
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
196
  }
034939b65   Frederic Weisbecker   tracing/ftrace: h...
197
  static void stat_seq_stop(struct seq_file *s, void *p)
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
198
  {
0d64f8342   Frederic Weisbecker   tracing/stat: rep...
199
  	struct stat_session *session = s->private;
034939b65   Frederic Weisbecker   tracing/ftrace: h...
200
  	mutex_unlock(&session->stat_mutex);
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
201
202
203
204
  }
  
  static int stat_seq_show(struct seq_file *s, void *v)
  {
0d64f8342   Frederic Weisbecker   tracing/stat: rep...
205
  	struct stat_session *session = s->private;
8f184f273   Frederic Weisbecker   tracing/stat: rep...
206
  	struct stat_node *l = container_of(v, struct stat_node, node);
ff288b274   Frederic Weisbecker   tracing/ftrace: f...
207

e6f489013   Lai Jiangshan   trace_stat: don't...
208
209
  	if (v == SEQ_START_TOKEN)
  		return session->ts->stat_headers(s);
034939b65   Frederic Weisbecker   tracing/ftrace: h...
210
  	return session->ts->stat_show(s, l->stat);
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
211
212
213
  }
  
  static const struct seq_operations trace_stat_seq_ops = {
55922173f   Ingo Molnar   tracing: trace_st...
214
215
216
217
  	.start		= stat_seq_start,
  	.next		= stat_seq_next,
  	.stop		= stat_seq_stop,
  	.show		= stat_seq_show
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
218
  };
034939b65   Frederic Weisbecker   tracing/ftrace: h...
219
  /* The session stat is refilled and resorted at each stat file opening */
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
220
221
222
  static int tracing_stat_open(struct inode *inode, struct file *file)
  {
  	int ret;
636eacee3   Li Zefan   tracing/stat: Fix...
223
  	struct seq_file *m;
0d64f8342   Frederic Weisbecker   tracing/stat: rep...
224
  	struct stat_session *session = inode->i_private;
034939b65   Frederic Weisbecker   tracing/ftrace: h...
225

636eacee3   Li Zefan   tracing/stat: Fix...
226
227
228
  	ret = stat_seq_init(session);
  	if (ret)
  		return ret;
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
229
  	ret = seq_open(file, &trace_stat_seq_ops);
636eacee3   Li Zefan   tracing/stat: Fix...
230
231
232
  	if (ret) {
  		reset_stat_session(session);
  		return ret;
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
233
  	}
636eacee3   Li Zefan   tracing/stat: Fix...
234
235
  	m = file->private_data;
  	m->private = session;
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
236
237
  	return ret;
  }
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
238
  /*
dbd3fbdfe   Li Zefan   tracing/stat: do ...
239
   * Avoid consuming memory with our now useless rbtree.
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
240
241
242
   */
  static int tracing_stat_release(struct inode *i, struct file *f)
  {
0d64f8342   Frederic Weisbecker   tracing/stat: rep...
243
  	struct stat_session *session = i->i_private;
034939b65   Frederic Weisbecker   tracing/ftrace: h...
244

034939b65   Frederic Weisbecker   tracing/ftrace: h...
245
  	reset_stat_session(session);
034939b65   Frederic Weisbecker   tracing/ftrace: h...
246

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

dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
260
  	d_tracing = tracing_init_dentry();
14a5ae40f   Steven Rostedt (Red Hat)   tracing: Use IS_E...
261
  	if (IS_ERR(d_tracing))
ed6f1c996   Namhyung Kim   tracing: Check re...
262
  		return 0;
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
263

8434dc934   Steven Rostedt (Red Hat)   tracing: Convert ...
264
  	stat_dir = tracefs_create_dir("trace_stat", d_tracing);
034939b65   Frederic Weisbecker   tracing/ftrace: h...
265
  	if (!stat_dir)
a395d6a7e   Joe Perches   kernel/...: conve...
266
267
  		pr_warn("Could not create tracefs 'trace_stat' entry
  ");
dbd0b4b33   Frederic Weisbecker   tracing/ftrace: p...
268
269
  	return 0;
  }
002bb86d8   Frederic Weisbecker   tracing/ftrace: s...
270

0d64f8342   Frederic Weisbecker   tracing/stat: rep...
271
  static int init_stat_file(struct stat_session *session)
002bb86d8   Frederic Weisbecker   tracing/ftrace: s...
272
273
274
  {
  	if (!stat_dir && tracing_stat_init())
  		return -ENODEV;
8434dc934   Steven Rostedt (Red Hat)   tracing: Convert ...
275
  	session->file = tracefs_create_file(session->ts->name, 0644,
002bb86d8   Frederic Weisbecker   tracing/ftrace: s...
276
277
278
279
280
281
  					    stat_dir,
  					    session, &tracing_stat_fops);
  	if (!session->file)
  		return -ENOMEM;
  	return 0;
  }
55922173f   Ingo Molnar   tracing: trace_st...
282
283
284
  
  int register_stat_tracer(struct tracer_stat *trace)
  {
43bd12362   Frederic Weisbecker   tracing/stat: rem...
285
  	struct stat_session *session, *node;
55922173f   Ingo Molnar   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   Frederic Weisbecker   tracing/stat: rem...
296
  	list_for_each_entry(node, &all_stat_sessions, session_list) {
55922173f   Ingo Molnar   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   Frederic Weisbecker   tracing/stat: rep...
305
  	session = kzalloc(sizeof(*session), GFP_KERNEL);
55922173f   Ingo Molnar   tracing: trace_st...
306
307
308
309
310
  	if (!session)
  		return -ENOMEM;
  
  	session->ts = trace;
  	INIT_LIST_HEAD(&session->session_list);
55922173f   Ingo Molnar   tracing: trace_st...
311
  	mutex_init(&session->stat_mutex);
55922173f   Ingo Molnar   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   Frederic Weisbecker   tracing/stat: rep...
329
  	struct stat_session *node, *tmp;
55922173f   Ingo Molnar   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);
  }