diff options
Diffstat (limited to 'kernel/trace/trace_events.c')
-rw-r--r-- | kernel/trace/trace_events.c | 828 |
1 files changed, 828 insertions, 0 deletions
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c new file mode 100644 index 000000000000..576f4fa2af0d --- /dev/null +++ b/kernel/trace/trace_events.c | |||
@@ -0,0 +1,828 @@ | |||
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 | int trace_define_field(struct ftrace_event_call *call, char *type, | ||
23 | char *name, int offset, int size) | ||
24 | { | ||
25 | struct ftrace_event_field *field; | ||
26 | |||
27 | field = kzalloc(sizeof(*field), GFP_KERNEL); | ||
28 | if (!field) | ||
29 | goto err; | ||
30 | |||
31 | field->name = kstrdup(name, GFP_KERNEL); | ||
32 | if (!field->name) | ||
33 | goto err; | ||
34 | |||
35 | field->type = kstrdup(type, GFP_KERNEL); | ||
36 | if (!field->type) | ||
37 | goto err; | ||
38 | |||
39 | field->offset = offset; | ||
40 | field->size = size; | ||
41 | list_add(&field->link, &call->fields); | ||
42 | |||
43 | return 0; | ||
44 | |||
45 | err: | ||
46 | if (field) { | ||
47 | kfree(field->name); | ||
48 | kfree(field->type); | ||
49 | } | ||
50 | kfree(field); | ||
51 | |||
52 | return -ENOMEM; | ||
53 | } | ||
54 | |||
55 | static void ftrace_clear_events(void) | ||
56 | { | ||
57 | struct ftrace_event_call *call = (void *)__start_ftrace_events; | ||
58 | |||
59 | |||
60 | while ((unsigned long)call < (unsigned long)__stop_ftrace_events) { | ||
61 | |||
62 | if (call->enabled) { | ||
63 | call->enabled = 0; | ||
64 | call->unregfunc(); | ||
65 | } | ||
66 | call++; | ||
67 | } | ||
68 | } | ||
69 | |||
70 | static void ftrace_event_enable_disable(struct ftrace_event_call *call, | ||
71 | int enable) | ||
72 | { | ||
73 | |||
74 | switch (enable) { | ||
75 | case 0: | ||
76 | if (call->enabled) { | ||
77 | call->enabled = 0; | ||
78 | call->unregfunc(); | ||
79 | } | ||
80 | break; | ||
81 | case 1: | ||
82 | if (!call->enabled) { | ||
83 | call->enabled = 1; | ||
84 | call->regfunc(); | ||
85 | } | ||
86 | break; | ||
87 | } | ||
88 | } | ||
89 | |||
90 | static int ftrace_set_clr_event(char *buf, int set) | ||
91 | { | ||
92 | struct ftrace_event_call *call = __start_ftrace_events; | ||
93 | char *event = NULL, *sub = NULL, *match; | ||
94 | int ret = -EINVAL; | ||
95 | |||
96 | /* | ||
97 | * The buf format can be <subsystem>:<event-name> | ||
98 | * *:<event-name> means any event by that name. | ||
99 | * :<event-name> is the same. | ||
100 | * | ||
101 | * <subsystem>:* means all events in that subsystem | ||
102 | * <subsystem>: means the same. | ||
103 | * | ||
104 | * <name> (no ':') means all events in a subsystem with | ||
105 | * the name <name> or any event that matches <name> | ||
106 | */ | ||
107 | |||
108 | match = strsep(&buf, ":"); | ||
109 | if (buf) { | ||
110 | sub = match; | ||
111 | event = buf; | ||
112 | match = NULL; | ||
113 | |||
114 | if (!strlen(sub) || strcmp(sub, "*") == 0) | ||
115 | sub = NULL; | ||
116 | if (!strlen(event) || strcmp(event, "*") == 0) | ||
117 | event = NULL; | ||
118 | } | ||
119 | |||
120 | mutex_lock(&event_mutex); | ||
121 | for_each_event(call) { | ||
122 | |||
123 | if (!call->name || !call->regfunc) | ||
124 | continue; | ||
125 | |||
126 | if (match && | ||
127 | strcmp(match, call->name) != 0 && | ||
128 | strcmp(match, call->system) != 0) | ||
129 | continue; | ||
130 | |||
131 | if (sub && strcmp(sub, call->system) != 0) | ||
132 | continue; | ||
133 | |||
134 | if (event && strcmp(event, call->name) != 0) | ||
135 | continue; | ||
136 | |||
137 | ftrace_event_enable_disable(call, set); | ||
138 | |||
139 | ret = 0; | ||
140 | } | ||
141 | mutex_unlock(&event_mutex); | ||
142 | |||
143 | return ret; | ||
144 | } | ||
145 | |||
146 | /* 128 should be much more than enough */ | ||
147 | #define EVENT_BUF_SIZE 127 | ||
148 | |||
149 | static ssize_t | ||
150 | ftrace_event_write(struct file *file, const char __user *ubuf, | ||
151 | size_t cnt, loff_t *ppos) | ||
152 | { | ||
153 | size_t read = 0; | ||
154 | int i, set = 1; | ||
155 | ssize_t ret; | ||
156 | char *buf; | ||
157 | char ch; | ||
158 | |||
159 | if (!cnt || cnt < 0) | ||
160 | return 0; | ||
161 | |||
162 | ret = tracing_update_buffers(); | ||
163 | if (ret < 0) | ||
164 | return ret; | ||
165 | |||
166 | ret = get_user(ch, ubuf++); | ||
167 | if (ret) | ||
168 | return ret; | ||
169 | read++; | ||
170 | cnt--; | ||
171 | |||
172 | /* skip white space */ | ||
173 | while (cnt && isspace(ch)) { | ||
174 | ret = get_user(ch, ubuf++); | ||
175 | if (ret) | ||
176 | return ret; | ||
177 | read++; | ||
178 | cnt--; | ||
179 | } | ||
180 | |||
181 | /* Only white space found? */ | ||
182 | if (isspace(ch)) { | ||
183 | file->f_pos += read; | ||
184 | ret = read; | ||
185 | return ret; | ||
186 | } | ||
187 | |||
188 | buf = kmalloc(EVENT_BUF_SIZE+1, GFP_KERNEL); | ||
189 | if (!buf) | ||
190 | return -ENOMEM; | ||
191 | |||
192 | if (cnt > EVENT_BUF_SIZE) | ||
193 | cnt = EVENT_BUF_SIZE; | ||
194 | |||
195 | i = 0; | ||
196 | while (cnt && !isspace(ch)) { | ||
197 | if (!i && ch == '!') | ||
198 | set = 0; | ||
199 | else | ||
200 | buf[i++] = ch; | ||
201 | |||
202 | ret = get_user(ch, ubuf++); | ||
203 | if (ret) | ||
204 | goto out_free; | ||
205 | read++; | ||
206 | cnt--; | ||
207 | } | ||
208 | buf[i] = 0; | ||
209 | |||
210 | file->f_pos += read; | ||
211 | |||
212 | ret = ftrace_set_clr_event(buf, set); | ||
213 | if (ret) | ||
214 | goto out_free; | ||
215 | |||
216 | ret = read; | ||
217 | |||
218 | out_free: | ||
219 | kfree(buf); | ||
220 | |||
221 | return ret; | ||
222 | } | ||
223 | |||
224 | static void * | ||
225 | t_next(struct seq_file *m, void *v, loff_t *pos) | ||
226 | { | ||
227 | struct ftrace_event_call *call = m->private; | ||
228 | struct ftrace_event_call *next = call; | ||
229 | |||
230 | (*pos)++; | ||
231 | |||
232 | for (;;) { | ||
233 | if ((unsigned long)call >= (unsigned long)__stop_ftrace_events) | ||
234 | return NULL; | ||
235 | |||
236 | /* | ||
237 | * The ftrace subsystem is for showing formats only. | ||
238 | * They can not be enabled or disabled via the event files. | ||
239 | */ | ||
240 | if (call->regfunc) | ||
241 | break; | ||
242 | |||
243 | call++; | ||
244 | next = call; | ||
245 | } | ||
246 | |||
247 | m->private = ++next; | ||
248 | |||
249 | return call; | ||
250 | } | ||
251 | |||
252 | static void *t_start(struct seq_file *m, loff_t *pos) | ||
253 | { | ||
254 | return t_next(m, NULL, pos); | ||
255 | } | ||
256 | |||
257 | static void * | ||
258 | s_next(struct seq_file *m, void *v, loff_t *pos) | ||
259 | { | ||
260 | struct ftrace_event_call *call = m->private; | ||
261 | struct ftrace_event_call *next; | ||
262 | |||
263 | (*pos)++; | ||
264 | |||
265 | retry: | ||
266 | if ((unsigned long)call >= (unsigned long)__stop_ftrace_events) | ||
267 | return NULL; | ||
268 | |||
269 | if (!call->enabled) { | ||
270 | call++; | ||
271 | goto retry; | ||
272 | } | ||
273 | |||
274 | next = call; | ||
275 | m->private = ++next; | ||
276 | |||
277 | return call; | ||
278 | } | ||
279 | |||
280 | static void *s_start(struct seq_file *m, loff_t *pos) | ||
281 | { | ||
282 | return s_next(m, NULL, pos); | ||
283 | } | ||
284 | |||
285 | static int t_show(struct seq_file *m, void *v) | ||
286 | { | ||
287 | struct ftrace_event_call *call = v; | ||
288 | |||
289 | if (strcmp(call->system, TRACE_SYSTEM) != 0) | ||
290 | seq_printf(m, "%s:", call->system); | ||
291 | seq_printf(m, "%s\n", call->name); | ||
292 | |||
293 | return 0; | ||
294 | } | ||
295 | |||
296 | static void t_stop(struct seq_file *m, void *p) | ||
297 | { | ||
298 | } | ||
299 | |||
300 | static int | ||
301 | ftrace_event_seq_open(struct inode *inode, struct file *file) | ||
302 | { | ||
303 | int ret; | ||
304 | const struct seq_operations *seq_ops; | ||
305 | |||
306 | if ((file->f_mode & FMODE_WRITE) && | ||
307 | !(file->f_flags & O_APPEND)) | ||
308 | ftrace_clear_events(); | ||
309 | |||
310 | seq_ops = inode->i_private; | ||
311 | ret = seq_open(file, seq_ops); | ||
312 | if (!ret) { | ||
313 | struct seq_file *m = file->private_data; | ||
314 | |||
315 | m->private = __start_ftrace_events; | ||
316 | } | ||
317 | return ret; | ||
318 | } | ||
319 | |||
320 | static ssize_t | ||
321 | event_enable_read(struct file *filp, char __user *ubuf, size_t cnt, | ||
322 | loff_t *ppos) | ||
323 | { | ||
324 | struct ftrace_event_call *call = filp->private_data; | ||
325 | char *buf; | ||
326 | |||
327 | if (call->enabled) | ||
328 | buf = "1\n"; | ||
329 | else | ||
330 | buf = "0\n"; | ||
331 | |||
332 | return simple_read_from_buffer(ubuf, cnt, ppos, buf, 2); | ||
333 | } | ||
334 | |||
335 | static ssize_t | ||
336 | event_enable_write(struct file *filp, const char __user *ubuf, size_t cnt, | ||
337 | loff_t *ppos) | ||
338 | { | ||
339 | struct ftrace_event_call *call = filp->private_data; | ||
340 | char buf[64]; | ||
341 | unsigned long val; | ||
342 | int ret; | ||
343 | |||
344 | if (cnt >= sizeof(buf)) | ||
345 | return -EINVAL; | ||
346 | |||
347 | if (copy_from_user(&buf, ubuf, cnt)) | ||
348 | return -EFAULT; | ||
349 | |||
350 | buf[cnt] = 0; | ||
351 | |||
352 | ret = strict_strtoul(buf, 10, &val); | ||
353 | if (ret < 0) | ||
354 | return ret; | ||
355 | |||
356 | ret = tracing_update_buffers(); | ||
357 | if (ret < 0) | ||
358 | return ret; | ||
359 | |||
360 | switch (val) { | ||
361 | case 0: | ||
362 | case 1: | ||
363 | mutex_lock(&event_mutex); | ||
364 | ftrace_event_enable_disable(call, val); | ||
365 | mutex_unlock(&event_mutex); | ||
366 | break; | ||
367 | |||
368 | default: | ||
369 | return -EINVAL; | ||
370 | } | ||
371 | |||
372 | *ppos += cnt; | ||
373 | |||
374 | return cnt; | ||
375 | } | ||
376 | |||
377 | #undef FIELD | ||
378 | #define FIELD(type, name) \ | ||
379 | #type, "common_" #name, offsetof(typeof(field), name), \ | ||
380 | sizeof(field.name) | ||
381 | |||
382 | static int trace_write_header(struct trace_seq *s) | ||
383 | { | ||
384 | struct trace_entry field; | ||
385 | |||
386 | /* struct trace_entry */ | ||
387 | return trace_seq_printf(s, | ||
388 | "\tfield:%s %s;\toffset:%zu;\tsize:%zu;\n" | ||
389 | "\tfield:%s %s;\toffset:%zu;\tsize:%zu;\n" | ||
390 | "\tfield:%s %s;\toffset:%zu;\tsize:%zu;\n" | ||
391 | "\tfield:%s %s;\toffset:%zu;\tsize:%zu;\n" | ||
392 | "\tfield:%s %s;\toffset:%zu;\tsize:%zu;\n" | ||
393 | "\n", | ||
394 | FIELD(unsigned char, type), | ||
395 | FIELD(unsigned char, flags), | ||
396 | FIELD(unsigned char, preempt_count), | ||
397 | FIELD(int, pid), | ||
398 | FIELD(int, tgid)); | ||
399 | } | ||
400 | |||
401 | static ssize_t | ||
402 | event_format_read(struct file *filp, char __user *ubuf, size_t cnt, | ||
403 | loff_t *ppos) | ||
404 | { | ||
405 | struct ftrace_event_call *call = filp->private_data; | ||
406 | struct trace_seq *s; | ||
407 | char *buf; | ||
408 | int r; | ||
409 | |||
410 | if (*ppos) | ||
411 | return 0; | ||
412 | |||
413 | s = kmalloc(sizeof(*s), GFP_KERNEL); | ||
414 | if (!s) | ||
415 | return -ENOMEM; | ||
416 | |||
417 | trace_seq_init(s); | ||
418 | |||
419 | /* If any of the first writes fail, so will the show_format. */ | ||
420 | |||
421 | trace_seq_printf(s, "name: %s\n", call->name); | ||
422 | trace_seq_printf(s, "ID: %d\n", call->id); | ||
423 | trace_seq_printf(s, "format:\n"); | ||
424 | trace_write_header(s); | ||
425 | |||
426 | r = call->show_format(s); | ||
427 | if (!r) { | ||
428 | /* | ||
429 | * ug! The format output is bigger than a PAGE!! | ||
430 | */ | ||
431 | buf = "FORMAT TOO BIG\n"; | ||
432 | r = simple_read_from_buffer(ubuf, cnt, ppos, | ||
433 | buf, strlen(buf)); | ||
434 | goto out; | ||
435 | } | ||
436 | |||
437 | r = simple_read_from_buffer(ubuf, cnt, ppos, | ||
438 | s->buffer, s->len); | ||
439 | out: | ||
440 | kfree(s); | ||
441 | return r; | ||
442 | } | ||
443 | |||
444 | static ssize_t | ||
445 | event_id_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) | ||
446 | { | ||
447 | struct ftrace_event_call *call = filp->private_data; | ||
448 | struct trace_seq *s; | ||
449 | int r; | ||
450 | |||
451 | if (*ppos) | ||
452 | return 0; | ||
453 | |||
454 | s = kmalloc(sizeof(*s), GFP_KERNEL); | ||
455 | if (!s) | ||
456 | return -ENOMEM; | ||
457 | |||
458 | trace_seq_init(s); | ||
459 | trace_seq_printf(s, "%d\n", call->id); | ||
460 | |||
461 | r = simple_read_from_buffer(ubuf, cnt, ppos, | ||
462 | s->buffer, s->len); | ||
463 | kfree(s); | ||
464 | return r; | ||
465 | } | ||
466 | |||
467 | static ssize_t | ||
468 | event_filter_read(struct file *filp, char __user *ubuf, size_t cnt, | ||
469 | loff_t *ppos) | ||
470 | { | ||
471 | struct ftrace_event_call *call = filp->private_data; | ||
472 | struct trace_seq *s; | ||
473 | int r; | ||
474 | |||
475 | if (*ppos) | ||
476 | return 0; | ||
477 | |||
478 | s = kmalloc(sizeof(*s), GFP_KERNEL); | ||
479 | if (!s) | ||
480 | return -ENOMEM; | ||
481 | |||
482 | trace_seq_init(s); | ||
483 | |||
484 | filter_print_preds(call->preds, s); | ||
485 | r = simple_read_from_buffer(ubuf, cnt, ppos, s->buffer, s->len); | ||
486 | |||
487 | kfree(s); | ||
488 | |||
489 | return r; | ||
490 | } | ||
491 | |||
492 | static ssize_t | ||
493 | event_filter_write(struct file *filp, const char __user *ubuf, size_t cnt, | ||
494 | loff_t *ppos) | ||
495 | { | ||
496 | struct ftrace_event_call *call = filp->private_data; | ||
497 | char buf[64], *pbuf = buf; | ||
498 | struct filter_pred *pred; | ||
499 | int err; | ||
500 | |||
501 | if (cnt >= sizeof(buf)) | ||
502 | return -EINVAL; | ||
503 | |||
504 | if (copy_from_user(&buf, ubuf, cnt)) | ||
505 | return -EFAULT; | ||
506 | buf[cnt] = '\0'; | ||
507 | |||
508 | pred = kzalloc(sizeof(*pred), GFP_KERNEL); | ||
509 | if (!pred) | ||
510 | return -ENOMEM; | ||
511 | |||
512 | err = filter_parse(&pbuf, pred); | ||
513 | if (err < 0) { | ||
514 | filter_free_pred(pred); | ||
515 | return err; | ||
516 | } | ||
517 | |||
518 | if (pred->clear) { | ||
519 | filter_free_preds(call); | ||
520 | filter_free_pred(pred); | ||
521 | return cnt; | ||
522 | } | ||
523 | |||
524 | err = filter_add_pred(call, pred); | ||
525 | if (err < 0) { | ||
526 | filter_free_pred(pred); | ||
527 | return err; | ||
528 | } | ||
529 | |||
530 | *ppos += cnt; | ||
531 | |||
532 | return cnt; | ||
533 | } | ||
534 | |||
535 | static ssize_t | ||
536 | subsystem_filter_read(struct file *filp, char __user *ubuf, size_t cnt, | ||
537 | loff_t *ppos) | ||
538 | { | ||
539 | struct event_subsystem *system = filp->private_data; | ||
540 | struct trace_seq *s; | ||
541 | int r; | ||
542 | |||
543 | if (*ppos) | ||
544 | return 0; | ||
545 | |||
546 | s = kmalloc(sizeof(*s), GFP_KERNEL); | ||
547 | if (!s) | ||
548 | return -ENOMEM; | ||
549 | |||
550 | trace_seq_init(s); | ||
551 | |||
552 | filter_print_preds(system->preds, s); | ||
553 | r = simple_read_from_buffer(ubuf, cnt, ppos, s->buffer, s->len); | ||
554 | |||
555 | kfree(s); | ||
556 | |||
557 | return r; | ||
558 | } | ||
559 | |||
560 | static ssize_t | ||
561 | subsystem_filter_write(struct file *filp, const char __user *ubuf, size_t cnt, | ||
562 | loff_t *ppos) | ||
563 | { | ||
564 | struct event_subsystem *system = filp->private_data; | ||
565 | char buf[64], *pbuf = buf; | ||
566 | struct filter_pred *pred; | ||
567 | int err; | ||
568 | |||
569 | if (cnt >= sizeof(buf)) | ||
570 | return -EINVAL; | ||
571 | |||
572 | if (copy_from_user(&buf, ubuf, cnt)) | ||
573 | return -EFAULT; | ||
574 | buf[cnt] = '\0'; | ||
575 | |||
576 | pred = kzalloc(sizeof(*pred), GFP_KERNEL); | ||
577 | if (!pred) | ||
578 | return -ENOMEM; | ||
579 | |||
580 | err = filter_parse(&pbuf, pred); | ||
581 | if (err < 0) { | ||
582 | filter_free_pred(pred); | ||
583 | return err; | ||
584 | } | ||
585 | |||
586 | if (pred->clear) { | ||
587 | filter_free_subsystem_preds(system); | ||
588 | filter_free_pred(pred); | ||
589 | return cnt; | ||
590 | } | ||
591 | |||
592 | err = filter_add_subsystem_pred(system, pred); | ||
593 | if (err < 0) { | ||
594 | filter_free_subsystem_preds(system); | ||
595 | filter_free_pred(pred); | ||
596 | return err; | ||
597 | } | ||
598 | |||
599 | *ppos += cnt; | ||
600 | |||
601 | return cnt; | ||
602 | } | ||
603 | |||
604 | static const struct seq_operations show_event_seq_ops = { | ||
605 | .start = t_start, | ||
606 | .next = t_next, | ||
607 | .show = t_show, | ||
608 | .stop = t_stop, | ||
609 | }; | ||
610 | |||
611 | static const struct seq_operations show_set_event_seq_ops = { | ||
612 | .start = s_start, | ||
613 | .next = s_next, | ||
614 | .show = t_show, | ||
615 | .stop = t_stop, | ||
616 | }; | ||
617 | |||
618 | static const struct file_operations ftrace_avail_fops = { | ||
619 | .open = ftrace_event_seq_open, | ||
620 | .read = seq_read, | ||
621 | .llseek = seq_lseek, | ||
622 | .release = seq_release, | ||
623 | }; | ||
624 | |||
625 | static const struct file_operations ftrace_set_event_fops = { | ||
626 | .open = ftrace_event_seq_open, | ||
627 | .read = seq_read, | ||
628 | .write = ftrace_event_write, | ||
629 | .llseek = seq_lseek, | ||
630 | .release = seq_release, | ||
631 | }; | ||
632 | |||
633 | static const struct file_operations ftrace_enable_fops = { | ||
634 | .open = tracing_open_generic, | ||
635 | .read = event_enable_read, | ||
636 | .write = event_enable_write, | ||
637 | }; | ||
638 | |||
639 | static const struct file_operations ftrace_event_format_fops = { | ||
640 | .open = tracing_open_generic, | ||
641 | .read = event_format_read, | ||
642 | }; | ||
643 | |||
644 | static const struct file_operations ftrace_event_id_fops = { | ||
645 | .open = tracing_open_generic, | ||
646 | .read = event_id_read, | ||
647 | }; | ||
648 | |||
649 | static const struct file_operations ftrace_event_filter_fops = { | ||
650 | .open = tracing_open_generic, | ||
651 | .read = event_filter_read, | ||
652 | .write = event_filter_write, | ||
653 | }; | ||
654 | |||
655 | static const struct file_operations ftrace_subsystem_filter_fops = { | ||
656 | .open = tracing_open_generic, | ||
657 | .read = subsystem_filter_read, | ||
658 | .write = subsystem_filter_write, | ||
659 | }; | ||
660 | |||
661 | static struct dentry *event_trace_events_dir(void) | ||
662 | { | ||
663 | static struct dentry *d_tracer; | ||
664 | static struct dentry *d_events; | ||
665 | |||
666 | if (d_events) | ||
667 | return d_events; | ||
668 | |||
669 | d_tracer = tracing_init_dentry(); | ||
670 | if (!d_tracer) | ||
671 | return NULL; | ||
672 | |||
673 | d_events = debugfs_create_dir("events", d_tracer); | ||
674 | if (!d_events) | ||
675 | pr_warning("Could not create debugfs " | ||
676 | "'events' directory\n"); | ||
677 | |||
678 | return d_events; | ||
679 | } | ||
680 | |||
681 | static LIST_HEAD(event_subsystems); | ||
682 | |||
683 | static struct dentry * | ||
684 | event_subsystem_dir(const char *name, struct dentry *d_events) | ||
685 | { | ||
686 | struct event_subsystem *system; | ||
687 | |||
688 | /* First see if we did not already create this dir */ | ||
689 | list_for_each_entry(system, &event_subsystems, list) { | ||
690 | if (strcmp(system->name, name) == 0) | ||
691 | return system->entry; | ||
692 | } | ||
693 | |||
694 | /* need to create new entry */ | ||
695 | system = kmalloc(sizeof(*system), GFP_KERNEL); | ||
696 | if (!system) { | ||
697 | pr_warning("No memory to create event subsystem %s\n", | ||
698 | name); | ||
699 | return d_events; | ||
700 | } | ||
701 | |||
702 | system->entry = debugfs_create_dir(name, d_events); | ||
703 | if (!system->entry) { | ||
704 | pr_warning("Could not create event subsystem %s\n", | ||
705 | name); | ||
706 | kfree(system); | ||
707 | return d_events; | ||
708 | } | ||
709 | |||
710 | system->name = name; | ||
711 | list_add(&system->list, &event_subsystems); | ||
712 | |||
713 | system->preds = NULL; | ||
714 | |||
715 | return system->entry; | ||
716 | } | ||
717 | |||
718 | static int | ||
719 | event_create_dir(struct ftrace_event_call *call, struct dentry *d_events) | ||
720 | { | ||
721 | struct dentry *entry; | ||
722 | int ret; | ||
723 | |||
724 | /* | ||
725 | * If the trace point header did not define TRACE_SYSTEM | ||
726 | * then the system would be called "TRACE_SYSTEM". | ||
727 | */ | ||
728 | if (strcmp(call->system, "TRACE_SYSTEM") != 0) | ||
729 | d_events = event_subsystem_dir(call->system, d_events); | ||
730 | |||
731 | if (call->raw_init) { | ||
732 | ret = call->raw_init(); | ||
733 | if (ret < 0) { | ||
734 | pr_warning("Could not initialize trace point" | ||
735 | " events/%s\n", call->name); | ||
736 | return ret; | ||
737 | } | ||
738 | } | ||
739 | |||
740 | call->dir = debugfs_create_dir(call->name, d_events); | ||
741 | if (!call->dir) { | ||
742 | pr_warning("Could not create debugfs " | ||
743 | "'%s' directory\n", call->name); | ||
744 | return -1; | ||
745 | } | ||
746 | |||
747 | if (call->regfunc) { | ||
748 | entry = debugfs_create_file("enable", 0644, call->dir, call, | ||
749 | &ftrace_enable_fops); | ||
750 | if (!entry) | ||
751 | pr_warning("Could not create debugfs " | ||
752 | "'%s/enable' entry\n", call->name); | ||
753 | } | ||
754 | |||
755 | if (call->id) { | ||
756 | entry = debugfs_create_file("id", 0444, call->dir, call, | ||
757 | &ftrace_event_id_fops); | ||
758 | if (!entry) | ||
759 | pr_warning("Could not create debugfs '%s/id' entry\n", | ||
760 | call->name); | ||
761 | } | ||
762 | |||
763 | if (call->define_fields) { | ||
764 | ret = call->define_fields(); | ||
765 | if (ret < 0) { | ||
766 | pr_warning("Could not initialize trace point" | ||
767 | " events/%s\n", call->name); | ||
768 | return ret; | ||
769 | } | ||
770 | entry = debugfs_create_file("filter", 0644, call->dir, call, | ||
771 | &ftrace_event_filter_fops); | ||
772 | if (!entry) | ||
773 | pr_warning("Could not create debugfs " | ||
774 | "'%s/filter' entry\n", call->name); | ||
775 | } | ||
776 | |||
777 | /* A trace may not want to export its format */ | ||
778 | if (!call->show_format) | ||
779 | return 0; | ||
780 | |||
781 | entry = debugfs_create_file("format", 0444, call->dir, call, | ||
782 | &ftrace_event_format_fops); | ||
783 | if (!entry) | ||
784 | pr_warning("Could not create debugfs " | ||
785 | "'%s/format' entry\n", call->name); | ||
786 | |||
787 | return 0; | ||
788 | } | ||
789 | |||
790 | static __init int event_trace_init(void) | ||
791 | { | ||
792 | struct ftrace_event_call *call = __start_ftrace_events; | ||
793 | struct dentry *d_tracer; | ||
794 | struct dentry *entry; | ||
795 | struct dentry *d_events; | ||
796 | |||
797 | d_tracer = tracing_init_dentry(); | ||
798 | if (!d_tracer) | ||
799 | return 0; | ||
800 | |||
801 | entry = debugfs_create_file("available_events", 0444, d_tracer, | ||
802 | (void *)&show_event_seq_ops, | ||
803 | &ftrace_avail_fops); | ||
804 | if (!entry) | ||
805 | pr_warning("Could not create debugfs " | ||
806 | "'available_events' entry\n"); | ||
807 | |||
808 | entry = debugfs_create_file("set_event", 0644, d_tracer, | ||
809 | (void *)&show_set_event_seq_ops, | ||
810 | &ftrace_set_event_fops); | ||
811 | if (!entry) | ||
812 | pr_warning("Could not create debugfs " | ||
813 | "'set_event' entry\n"); | ||
814 | |||
815 | d_events = event_trace_events_dir(); | ||
816 | if (!d_events) | ||
817 | return 0; | ||
818 | |||
819 | for_each_event(call) { | ||
820 | /* The linker may leave blanks */ | ||
821 | if (!call->name) | ||
822 | continue; | ||
823 | event_create_dir(call, d_events); | ||
824 | } | ||
825 | |||
826 | return 0; | ||
827 | } | ||
828 | fs_initcall(event_trace_init); | ||