diff options
Diffstat (limited to 'kernel/trace/trace_events.c')
-rw-r--r-- | kernel/trace/trace_events.c | 581 |
1 files changed, 581 insertions, 0 deletions
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c new file mode 100644 index 000000000000..1880a6438097 --- /dev/null +++ b/kernel/trace/trace_events.c | |||
@@ -0,0 +1,581 @@ | |||
1 | /* | ||
2 | * event tracer | ||
3 | * | ||
4 | * Copyright (C) 2008 Red Hat Inc, Steven Rostedt <srostedt@redhat.com> | ||
5 | * | ||
6 | * - Added format output of fields of the trace point. | ||
7 | * This was based off of work by Tom Zanussi <tzanussi@gmail.com>. | ||
8 | * | ||
9 | */ | ||
10 | |||
11 | #include <linux/debugfs.h> | ||
12 | #include <linux/uaccess.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/ctype.h> | ||
15 | |||
16 | #include "trace_output.h" | ||
17 | |||
18 | #define TRACE_SYSTEM "TRACE_SYSTEM" | ||
19 | |||
20 | static DEFINE_MUTEX(event_mutex); | ||
21 | |||
22 | #define events_for_each(event) \ | ||
23 | for (event = __start_ftrace_events; \ | ||
24 | (unsigned long)event < (unsigned long)__stop_ftrace_events; \ | ||
25 | event++) | ||
26 | |||
27 | void event_trace_printk(unsigned long ip, const char *fmt, ...) | ||
28 | { | ||
29 | va_list ap; | ||
30 | |||
31 | va_start(ap, fmt); | ||
32 | tracing_record_cmdline(current); | ||
33 | trace_vprintk(ip, task_curr_ret_stack(current), fmt, ap); | ||
34 | va_end(ap); | ||
35 | } | ||
36 | |||
37 | static void ftrace_clear_events(void) | ||
38 | { | ||
39 | struct ftrace_event_call *call = (void *)__start_ftrace_events; | ||
40 | |||
41 | |||
42 | while ((unsigned long)call < (unsigned long)__stop_ftrace_events) { | ||
43 | |||
44 | if (call->enabled) { | ||
45 | call->enabled = 0; | ||
46 | call->unregfunc(); | ||
47 | } | ||
48 | call++; | ||
49 | } | ||
50 | } | ||
51 | |||
52 | static void ftrace_event_enable_disable(struct ftrace_event_call *call, | ||
53 | int enable) | ||
54 | { | ||
55 | |||
56 | switch (enable) { | ||
57 | case 0: | ||
58 | if (call->enabled) { | ||
59 | call->enabled = 0; | ||
60 | call->unregfunc(); | ||
61 | } | ||
62 | break; | ||
63 | case 1: | ||
64 | if (!call->enabled) { | ||
65 | call->enabled = 1; | ||
66 | call->regfunc(); | ||
67 | } | ||
68 | break; | ||
69 | } | ||
70 | } | ||
71 | |||
72 | static int ftrace_set_clr_event(char *buf, int set) | ||
73 | { | ||
74 | struct ftrace_event_call *call = __start_ftrace_events; | ||
75 | char *event = NULL, *sub = NULL, *match; | ||
76 | int ret = -EINVAL; | ||
77 | |||
78 | /* | ||
79 | * The buf format can be <subsystem>:<event-name> | ||
80 | * *:<event-name> means any event by that name. | ||
81 | * :<event-name> is the same. | ||
82 | * | ||
83 | * <subsystem>:* means all events in that subsystem | ||
84 | * <subsystem>: means the same. | ||
85 | * | ||
86 | * <name> (no ':') means all events in a subsystem with | ||
87 | * the name <name> or any event that matches <name> | ||
88 | */ | ||
89 | |||
90 | match = strsep(&buf, ":"); | ||
91 | if (buf) { | ||
92 | sub = match; | ||
93 | event = buf; | ||
94 | match = NULL; | ||
95 | |||
96 | if (!strlen(sub) || strcmp(sub, "*") == 0) | ||
97 | sub = NULL; | ||
98 | if (!strlen(event) || strcmp(event, "*") == 0) | ||
99 | event = NULL; | ||
100 | } | ||
101 | |||
102 | mutex_lock(&event_mutex); | ||
103 | events_for_each(call) { | ||
104 | |||
105 | if (!call->name) | ||
106 | continue; | ||
107 | |||
108 | if (match && | ||
109 | strcmp(match, call->name) != 0 && | ||
110 | strcmp(match, call->system) != 0) | ||
111 | continue; | ||
112 | |||
113 | if (sub && strcmp(sub, call->system) != 0) | ||
114 | continue; | ||
115 | |||
116 | if (event && strcmp(event, call->name) != 0) | ||
117 | continue; | ||
118 | |||
119 | ftrace_event_enable_disable(call, set); | ||
120 | |||
121 | ret = 0; | ||
122 | } | ||
123 | mutex_unlock(&event_mutex); | ||
124 | |||
125 | return ret; | ||
126 | } | ||
127 | |||
128 | /* 128 should be much more than enough */ | ||
129 | #define EVENT_BUF_SIZE 127 | ||
130 | |||
131 | static ssize_t | ||
132 | ftrace_event_write(struct file *file, const char __user *ubuf, | ||
133 | size_t cnt, loff_t *ppos) | ||
134 | { | ||
135 | size_t read = 0; | ||
136 | int i, set = 1; | ||
137 | ssize_t ret; | ||
138 | char *buf; | ||
139 | char ch; | ||
140 | |||
141 | if (!cnt || cnt < 0) | ||
142 | return 0; | ||
143 | |||
144 | ret = get_user(ch, ubuf++); | ||
145 | if (ret) | ||
146 | return ret; | ||
147 | read++; | ||
148 | cnt--; | ||
149 | |||
150 | /* skip white space */ | ||
151 | while (cnt && isspace(ch)) { | ||
152 | ret = get_user(ch, ubuf++); | ||
153 | if (ret) | ||
154 | return ret; | ||
155 | read++; | ||
156 | cnt--; | ||
157 | } | ||
158 | |||
159 | /* Only white space found? */ | ||
160 | if (isspace(ch)) { | ||
161 | file->f_pos += read; | ||
162 | ret = read; | ||
163 | return ret; | ||
164 | } | ||
165 | |||
166 | buf = kmalloc(EVENT_BUF_SIZE+1, GFP_KERNEL); | ||
167 | if (!buf) | ||
168 | return -ENOMEM; | ||
169 | |||
170 | if (cnt > EVENT_BUF_SIZE) | ||
171 | cnt = EVENT_BUF_SIZE; | ||
172 | |||
173 | i = 0; | ||
174 | while (cnt && !isspace(ch)) { | ||
175 | if (!i && ch == '!') | ||
176 | set = 0; | ||
177 | else | ||
178 | buf[i++] = ch; | ||
179 | |||
180 | ret = get_user(ch, ubuf++); | ||
181 | if (ret) | ||
182 | goto out_free; | ||
183 | read++; | ||
184 | cnt--; | ||
185 | } | ||
186 | buf[i] = 0; | ||
187 | |||
188 | file->f_pos += read; | ||
189 | |||
190 | ret = ftrace_set_clr_event(buf, set); | ||
191 | if (ret) | ||
192 | goto out_free; | ||
193 | |||
194 | ret = read; | ||
195 | |||
196 | out_free: | ||
197 | kfree(buf); | ||
198 | |||
199 | return ret; | ||
200 | } | ||
201 | |||
202 | static void * | ||
203 | t_next(struct seq_file *m, void *v, loff_t *pos) | ||
204 | { | ||
205 | struct ftrace_event_call *call = m->private; | ||
206 | struct ftrace_event_call *next = call; | ||
207 | |||
208 | (*pos)++; | ||
209 | |||
210 | if ((unsigned long)call >= (unsigned long)__stop_ftrace_events) | ||
211 | return NULL; | ||
212 | |||
213 | m->private = ++next; | ||
214 | |||
215 | return call; | ||
216 | } | ||
217 | |||
218 | static void *t_start(struct seq_file *m, loff_t *pos) | ||
219 | { | ||
220 | return t_next(m, NULL, pos); | ||
221 | } | ||
222 | |||
223 | static void * | ||
224 | s_next(struct seq_file *m, void *v, loff_t *pos) | ||
225 | { | ||
226 | struct ftrace_event_call *call = m->private; | ||
227 | struct ftrace_event_call *next; | ||
228 | |||
229 | (*pos)++; | ||
230 | |||
231 | retry: | ||
232 | if ((unsigned long)call >= (unsigned long)__stop_ftrace_events) | ||
233 | return NULL; | ||
234 | |||
235 | if (!call->enabled) { | ||
236 | call++; | ||
237 | goto retry; | ||
238 | } | ||
239 | |||
240 | next = call; | ||
241 | m->private = ++next; | ||
242 | |||
243 | return call; | ||
244 | } | ||
245 | |||
246 | static void *s_start(struct seq_file *m, loff_t *pos) | ||
247 | { | ||
248 | return s_next(m, NULL, pos); | ||
249 | } | ||
250 | |||
251 | static int t_show(struct seq_file *m, void *v) | ||
252 | { | ||
253 | struct ftrace_event_call *call = v; | ||
254 | |||
255 | if (strcmp(call->system, TRACE_SYSTEM) != 0) | ||
256 | seq_printf(m, "%s:", call->system); | ||
257 | seq_printf(m, "%s\n", call->name); | ||
258 | |||
259 | return 0; | ||
260 | } | ||
261 | |||
262 | static void t_stop(struct seq_file *m, void *p) | ||
263 | { | ||
264 | } | ||
265 | |||
266 | static int | ||
267 | ftrace_event_seq_open(struct inode *inode, struct file *file) | ||
268 | { | ||
269 | int ret; | ||
270 | const struct seq_operations *seq_ops; | ||
271 | |||
272 | if ((file->f_mode & FMODE_WRITE) && | ||
273 | !(file->f_flags & O_APPEND)) | ||
274 | ftrace_clear_events(); | ||
275 | |||
276 | seq_ops = inode->i_private; | ||
277 | ret = seq_open(file, seq_ops); | ||
278 | if (!ret) { | ||
279 | struct seq_file *m = file->private_data; | ||
280 | |||
281 | m->private = __start_ftrace_events; | ||
282 | } | ||
283 | return ret; | ||
284 | } | ||
285 | |||
286 | static ssize_t | ||
287 | event_enable_read(struct file *filp, char __user *ubuf, size_t cnt, | ||
288 | loff_t *ppos) | ||
289 | { | ||
290 | struct ftrace_event_call *call = filp->private_data; | ||
291 | char *buf; | ||
292 | |||
293 | if (call->enabled) | ||
294 | buf = "1\n"; | ||
295 | else | ||
296 | buf = "0\n"; | ||
297 | |||
298 | return simple_read_from_buffer(ubuf, cnt, ppos, buf, 2); | ||
299 | } | ||
300 | |||
301 | static ssize_t | ||
302 | event_enable_write(struct file *filp, const char __user *ubuf, size_t cnt, | ||
303 | loff_t *ppos) | ||
304 | { | ||
305 | struct ftrace_event_call *call = filp->private_data; | ||
306 | char buf[64]; | ||
307 | unsigned long val; | ||
308 | int ret; | ||
309 | |||
310 | if (cnt >= sizeof(buf)) | ||
311 | return -EINVAL; | ||
312 | |||
313 | if (copy_from_user(&buf, ubuf, cnt)) | ||
314 | return -EFAULT; | ||
315 | |||
316 | buf[cnt] = 0; | ||
317 | |||
318 | ret = strict_strtoul(buf, 10, &val); | ||
319 | if (ret < 0) | ||
320 | return ret; | ||
321 | |||
322 | switch (val) { | ||
323 | case 0: | ||
324 | case 1: | ||
325 | mutex_lock(&event_mutex); | ||
326 | ftrace_event_enable_disable(call, val); | ||
327 | mutex_unlock(&event_mutex); | ||
328 | break; | ||
329 | |||
330 | default: | ||
331 | return -EINVAL; | ||
332 | } | ||
333 | |||
334 | *ppos += cnt; | ||
335 | |||
336 | return cnt; | ||
337 | } | ||
338 | |||
339 | #undef FIELD | ||
340 | #define FIELD(type, name) \ | ||
341 | #type, #name, (unsigned int)offsetof(typeof(field), name), \ | ||
342 | (unsigned int)sizeof(field.name) | ||
343 | |||
344 | static int trace_write_header(struct trace_seq *s) | ||
345 | { | ||
346 | struct trace_entry field; | ||
347 | |||
348 | /* struct trace_entry */ | ||
349 | return trace_seq_printf(s, | ||
350 | "\tfield:%s %s;\toffset:%u;\tsize:%u;\n" | ||
351 | "\tfield:%s %s;\toffset:%u;\tsize:%u;\n" | ||
352 | "\tfield:%s %s;\toffset:%u;\tsize:%u;\n" | ||
353 | "\tfield:%s %s;\toffset:%u;\tsize:%u;\n" | ||
354 | "\tfield:%s %s;\toffset:%u;\tsize:%u;\n" | ||
355 | "\n", | ||
356 | FIELD(unsigned char, type), | ||
357 | FIELD(unsigned char, flags), | ||
358 | FIELD(unsigned char, preempt_count), | ||
359 | FIELD(int, pid), | ||
360 | FIELD(int, tgid)); | ||
361 | } | ||
362 | |||
363 | static ssize_t | ||
364 | event_format_read(struct file *filp, char __user *ubuf, size_t cnt, | ||
365 | loff_t *ppos) | ||
366 | { | ||
367 | struct ftrace_event_call *call = filp->private_data; | ||
368 | struct trace_seq *s; | ||
369 | char *buf; | ||
370 | int r; | ||
371 | |||
372 | s = kmalloc(sizeof(*s), GFP_KERNEL); | ||
373 | if (!s) | ||
374 | return -ENOMEM; | ||
375 | |||
376 | trace_seq_init(s); | ||
377 | |||
378 | if (*ppos) | ||
379 | return 0; | ||
380 | |||
381 | /* If any of the first writes fail, so will the show_format. */ | ||
382 | |||
383 | trace_seq_printf(s, "name: %s\n", call->name); | ||
384 | trace_seq_printf(s, "ID: %d\n", call->id); | ||
385 | trace_seq_printf(s, "format:\n"); | ||
386 | trace_write_header(s); | ||
387 | |||
388 | r = call->show_format(s); | ||
389 | if (!r) { | ||
390 | /* | ||
391 | * ug! The format output is bigger than a PAGE!! | ||
392 | */ | ||
393 | buf = "FORMAT TOO BIG\n"; | ||
394 | r = simple_read_from_buffer(ubuf, cnt, ppos, | ||
395 | buf, strlen(buf)); | ||
396 | goto out; | ||
397 | } | ||
398 | |||
399 | r = simple_read_from_buffer(ubuf, cnt, ppos, | ||
400 | s->buffer, s->len); | ||
401 | out: | ||
402 | kfree(s); | ||
403 | return r; | ||
404 | } | ||
405 | |||
406 | static const struct seq_operations show_event_seq_ops = { | ||
407 | .start = t_start, | ||
408 | .next = t_next, | ||
409 | .show = t_show, | ||
410 | .stop = t_stop, | ||
411 | }; | ||
412 | |||
413 | static const struct seq_operations show_set_event_seq_ops = { | ||
414 | .start = s_start, | ||
415 | .next = s_next, | ||
416 | .show = t_show, | ||
417 | .stop = t_stop, | ||
418 | }; | ||
419 | |||
420 | static const struct file_operations ftrace_set_event_fops = { | ||
421 | .open = ftrace_event_seq_open, | ||
422 | .read = seq_read, | ||
423 | .write = ftrace_event_write, | ||
424 | .llseek = seq_lseek, | ||
425 | .release = seq_release, | ||
426 | }; | ||
427 | |||
428 | static const struct file_operations ftrace_enable_fops = { | ||
429 | .open = tracing_open_generic, | ||
430 | .read = event_enable_read, | ||
431 | .write = event_enable_write, | ||
432 | }; | ||
433 | |||
434 | static const struct file_operations ftrace_event_format_fops = { | ||
435 | .open = tracing_open_generic, | ||
436 | .read = event_format_read, | ||
437 | }; | ||
438 | |||
439 | static struct dentry *event_trace_events_dir(void) | ||
440 | { | ||
441 | static struct dentry *d_tracer; | ||
442 | static struct dentry *d_events; | ||
443 | |||
444 | if (d_events) | ||
445 | return d_events; | ||
446 | |||
447 | d_tracer = tracing_init_dentry(); | ||
448 | if (!d_tracer) | ||
449 | return NULL; | ||
450 | |||
451 | d_events = debugfs_create_dir("events", d_tracer); | ||
452 | if (!d_events) | ||
453 | pr_warning("Could not create debugfs " | ||
454 | "'events' directory\n"); | ||
455 | |||
456 | return d_events; | ||
457 | } | ||
458 | |||
459 | struct event_subsystem { | ||
460 | struct list_head list; | ||
461 | const char *name; | ||
462 | struct dentry *entry; | ||
463 | }; | ||
464 | |||
465 | static LIST_HEAD(event_subsystems); | ||
466 | |||
467 | static struct dentry * | ||
468 | event_subsystem_dir(const char *name, struct dentry *d_events) | ||
469 | { | ||
470 | struct event_subsystem *system; | ||
471 | |||
472 | /* First see if we did not already create this dir */ | ||
473 | list_for_each_entry(system, &event_subsystems, list) { | ||
474 | if (strcmp(system->name, name) == 0) | ||
475 | return system->entry; | ||
476 | } | ||
477 | |||
478 | /* need to create new entry */ | ||
479 | system = kmalloc(sizeof(*system), GFP_KERNEL); | ||
480 | if (!system) { | ||
481 | pr_warning("No memory to create event subsystem %s\n", | ||
482 | name); | ||
483 | return d_events; | ||
484 | } | ||
485 | |||
486 | system->entry = debugfs_create_dir(name, d_events); | ||
487 | if (!system->entry) { | ||
488 | pr_warning("Could not create event subsystem %s\n", | ||
489 | name); | ||
490 | kfree(system); | ||
491 | return d_events; | ||
492 | } | ||
493 | |||
494 | system->name = name; | ||
495 | list_add(&system->list, &event_subsystems); | ||
496 | |||
497 | return system->entry; | ||
498 | } | ||
499 | |||
500 | static int | ||
501 | event_create_dir(struct ftrace_event_call *call, struct dentry *d_events) | ||
502 | { | ||
503 | struct dentry *entry; | ||
504 | int ret; | ||
505 | |||
506 | /* | ||
507 | * If the trace point header did not define TRACE_SYSTEM | ||
508 | * then the system would be called "TRACE_SYSTEM". | ||
509 | */ | ||
510 | if (strcmp(call->system, "TRACE_SYSTEM") != 0) | ||
511 | d_events = event_subsystem_dir(call->system, d_events); | ||
512 | |||
513 | if (call->raw_init) { | ||
514 | ret = call->raw_init(); | ||
515 | if (ret < 0) { | ||
516 | pr_warning("Could not initialize trace point" | ||
517 | " events/%s\n", call->name); | ||
518 | return ret; | ||
519 | } | ||
520 | } | ||
521 | |||
522 | call->dir = debugfs_create_dir(call->name, d_events); | ||
523 | if (!call->dir) { | ||
524 | pr_warning("Could not create debugfs " | ||
525 | "'%s' directory\n", call->name); | ||
526 | return -1; | ||
527 | } | ||
528 | |||
529 | if (call->regfunc) { | ||
530 | entry = debugfs_create_file("enable", 0644, call->dir, call, | ||
531 | &ftrace_enable_fops); | ||
532 | if (!entry) | ||
533 | pr_warning("Could not create debugfs " | ||
534 | "'%s/enable' entry\n", call->name); | ||
535 | } | ||
536 | |||
537 | /* A trace may not want to export its format */ | ||
538 | if (!call->show_format) | ||
539 | return 0; | ||
540 | |||
541 | entry = debugfs_create_file("format", 0444, call->dir, call, | ||
542 | &ftrace_event_format_fops); | ||
543 | if (!entry) | ||
544 | pr_warning("Could not create debugfs " | ||
545 | "'%s/format' entry\n", call->name); | ||
546 | |||
547 | return 0; | ||
548 | } | ||
549 | |||
550 | static __init int event_trace_init(void) | ||
551 | { | ||
552 | struct ftrace_event_call *call = __start_ftrace_events; | ||
553 | struct dentry *d_tracer; | ||
554 | struct dentry *entry; | ||
555 | struct dentry *d_events; | ||
556 | |||
557 | d_tracer = tracing_init_dentry(); | ||
558 | if (!d_tracer) | ||
559 | return 0; | ||
560 | |||
561 | entry = debugfs_create_file("set_event", 0644, d_tracer, | ||
562 | (void *)&show_set_event_seq_ops, | ||
563 | &ftrace_set_event_fops); | ||
564 | if (!entry) | ||
565 | pr_warning("Could not create debugfs " | ||
566 | "'set_event' entry\n"); | ||
567 | |||
568 | d_events = event_trace_events_dir(); | ||
569 | if (!d_events) | ||
570 | return 0; | ||
571 | |||
572 | events_for_each(call) { | ||
573 | /* The linker may leave blanks */ | ||
574 | if (!call->name) | ||
575 | continue; | ||
576 | event_create_dir(call, d_events); | ||
577 | } | ||
578 | |||
579 | return 0; | ||
580 | } | ||
581 | fs_initcall(event_trace_init); | ||