diff options
Diffstat (limited to 'kernel/trace/trace_output.c')
-rw-r--r-- | kernel/trace/trace_output.c | 240 |
1 files changed, 213 insertions, 27 deletions
diff --git a/kernel/trace/trace_output.c b/kernel/trace/trace_output.c index 64b54a59c55b..7938f3ae93e3 100644 --- a/kernel/trace/trace_output.c +++ b/kernel/trace/trace_output.c | |||
@@ -14,11 +14,25 @@ | |||
14 | /* must be a power of 2 */ | 14 | /* must be a power of 2 */ |
15 | #define EVENT_HASHSIZE 128 | 15 | #define EVENT_HASHSIZE 128 |
16 | 16 | ||
17 | static DEFINE_MUTEX(trace_event_mutex); | 17 | DECLARE_RWSEM(trace_event_mutex); |
18 | |||
19 | DEFINE_PER_CPU(struct trace_seq, ftrace_event_seq); | ||
20 | EXPORT_PER_CPU_SYMBOL(ftrace_event_seq); | ||
21 | |||
18 | static struct hlist_head event_hash[EVENT_HASHSIZE] __read_mostly; | 22 | static struct hlist_head event_hash[EVENT_HASHSIZE] __read_mostly; |
19 | 23 | ||
20 | static int next_event_type = __TRACE_LAST_TYPE + 1; | 24 | static int next_event_type = __TRACE_LAST_TYPE + 1; |
21 | 25 | ||
26 | void trace_print_seq(struct seq_file *m, struct trace_seq *s) | ||
27 | { | ||
28 | int len = s->len >= PAGE_SIZE ? PAGE_SIZE - 1 : s->len; | ||
29 | |||
30 | s->buffer[len] = 0; | ||
31 | seq_puts(m, s->buffer); | ||
32 | |||
33 | trace_seq_init(s); | ||
34 | } | ||
35 | |||
22 | enum print_line_t trace_print_bprintk_msg_only(struct trace_iterator *iter) | 36 | enum print_line_t trace_print_bprintk_msg_only(struct trace_iterator *iter) |
23 | { | 37 | { |
24 | struct trace_seq *s = &iter->seq; | 38 | struct trace_seq *s = &iter->seq; |
@@ -84,6 +98,39 @@ trace_seq_printf(struct trace_seq *s, const char *fmt, ...) | |||
84 | 98 | ||
85 | return len; | 99 | return len; |
86 | } | 100 | } |
101 | EXPORT_SYMBOL_GPL(trace_seq_printf); | ||
102 | |||
103 | /** | ||
104 | * trace_seq_vprintf - sequence printing of trace information | ||
105 | * @s: trace sequence descriptor | ||
106 | * @fmt: printf format string | ||
107 | * | ||
108 | * The tracer may use either sequence operations or its own | ||
109 | * copy to user routines. To simplify formating of a trace | ||
110 | * trace_seq_printf is used to store strings into a special | ||
111 | * buffer (@s). Then the output may be either used by | ||
112 | * the sequencer or pulled into another buffer. | ||
113 | */ | ||
114 | int | ||
115 | trace_seq_vprintf(struct trace_seq *s, const char *fmt, va_list args) | ||
116 | { | ||
117 | int len = (PAGE_SIZE - 1) - s->len; | ||
118 | int ret; | ||
119 | |||
120 | if (!len) | ||
121 | return 0; | ||
122 | |||
123 | ret = vsnprintf(s->buffer + s->len, len, fmt, args); | ||
124 | |||
125 | /* If we can't write it all, don't bother writing anything */ | ||
126 | if (ret >= len) | ||
127 | return 0; | ||
128 | |||
129 | s->len += ret; | ||
130 | |||
131 | return len; | ||
132 | } | ||
133 | EXPORT_SYMBOL_GPL(trace_seq_vprintf); | ||
87 | 134 | ||
88 | int trace_seq_bprintf(struct trace_seq *s, const char *fmt, const u32 *binary) | 135 | int trace_seq_bprintf(struct trace_seq *s, const char *fmt, const u32 *binary) |
89 | { | 136 | { |
@@ -201,6 +248,67 @@ int trace_seq_path(struct trace_seq *s, struct path *path) | |||
201 | return 0; | 248 | return 0; |
202 | } | 249 | } |
203 | 250 | ||
251 | const char * | ||
252 | ftrace_print_flags_seq(struct trace_seq *p, const char *delim, | ||
253 | unsigned long flags, | ||
254 | const struct trace_print_flags *flag_array) | ||
255 | { | ||
256 | unsigned long mask; | ||
257 | const char *str; | ||
258 | const char *ret = p->buffer + p->len; | ||
259 | int i; | ||
260 | |||
261 | for (i = 0; flag_array[i].name && flags; i++) { | ||
262 | |||
263 | mask = flag_array[i].mask; | ||
264 | if ((flags & mask) != mask) | ||
265 | continue; | ||
266 | |||
267 | str = flag_array[i].name; | ||
268 | flags &= ~mask; | ||
269 | if (p->len && delim) | ||
270 | trace_seq_puts(p, delim); | ||
271 | trace_seq_puts(p, str); | ||
272 | } | ||
273 | |||
274 | /* check for left over flags */ | ||
275 | if (flags) { | ||
276 | if (p->len && delim) | ||
277 | trace_seq_puts(p, delim); | ||
278 | trace_seq_printf(p, "0x%lx", flags); | ||
279 | } | ||
280 | |||
281 | trace_seq_putc(p, 0); | ||
282 | |||
283 | return ret; | ||
284 | } | ||
285 | EXPORT_SYMBOL(ftrace_print_flags_seq); | ||
286 | |||
287 | const char * | ||
288 | ftrace_print_symbols_seq(struct trace_seq *p, unsigned long val, | ||
289 | const struct trace_print_flags *symbol_array) | ||
290 | { | ||
291 | int i; | ||
292 | const char *ret = p->buffer + p->len; | ||
293 | |||
294 | for (i = 0; symbol_array[i].name; i++) { | ||
295 | |||
296 | if (val != symbol_array[i].mask) | ||
297 | continue; | ||
298 | |||
299 | trace_seq_puts(p, symbol_array[i].name); | ||
300 | break; | ||
301 | } | ||
302 | |||
303 | if (!p->len) | ||
304 | trace_seq_printf(p, "0x%lx", val); | ||
305 | |||
306 | trace_seq_putc(p, 0); | ||
307 | |||
308 | return ret; | ||
309 | } | ||
310 | EXPORT_SYMBOL(ftrace_print_symbols_seq); | ||
311 | |||
204 | #ifdef CONFIG_KRETPROBES | 312 | #ifdef CONFIG_KRETPROBES |
205 | static inline const char *kretprobed(const char *name) | 313 | static inline const char *kretprobed(const char *name) |
206 | { | 314 | { |
@@ -311,17 +419,20 @@ seq_print_userip_objs(const struct userstack_entry *entry, struct trace_seq *s, | |||
311 | 419 | ||
312 | if (ip == ULONG_MAX || !ret) | 420 | if (ip == ULONG_MAX || !ret) |
313 | break; | 421 | break; |
314 | if (i && ret) | 422 | if (ret) |
315 | ret = trace_seq_puts(s, " <- "); | 423 | ret = trace_seq_puts(s, " => "); |
316 | if (!ip) { | 424 | if (!ip) { |
317 | if (ret) | 425 | if (ret) |
318 | ret = trace_seq_puts(s, "??"); | 426 | ret = trace_seq_puts(s, "??"); |
427 | if (ret) | ||
428 | ret = trace_seq_puts(s, "\n"); | ||
319 | continue; | 429 | continue; |
320 | } | 430 | } |
321 | if (!ret) | 431 | if (!ret) |
322 | break; | 432 | break; |
323 | if (ret) | 433 | if (ret) |
324 | ret = seq_print_user_ip(s, mm, ip, sym_flags); | 434 | ret = seq_print_user_ip(s, mm, ip, sym_flags); |
435 | ret = trace_seq_puts(s, "\n"); | ||
325 | } | 436 | } |
326 | 437 | ||
327 | if (mm) | 438 | if (mm) |
@@ -455,6 +566,7 @@ static int task_state_char(unsigned long state) | |||
455 | * @type: the type of event to look for | 566 | * @type: the type of event to look for |
456 | * | 567 | * |
457 | * Returns an event of type @type otherwise NULL | 568 | * Returns an event of type @type otherwise NULL |
569 | * Called with trace_event_read_lock() held. | ||
458 | */ | 570 | */ |
459 | struct trace_event *ftrace_find_event(int type) | 571 | struct trace_event *ftrace_find_event(int type) |
460 | { | 572 | { |
@@ -464,7 +576,7 @@ struct trace_event *ftrace_find_event(int type) | |||
464 | 576 | ||
465 | key = type & (EVENT_HASHSIZE - 1); | 577 | key = type & (EVENT_HASHSIZE - 1); |
466 | 578 | ||
467 | hlist_for_each_entry_rcu(event, n, &event_hash[key], node) { | 579 | hlist_for_each_entry(event, n, &event_hash[key], node) { |
468 | if (event->type == type) | 580 | if (event->type == type) |
469 | return event; | 581 | return event; |
470 | } | 582 | } |
@@ -472,6 +584,46 @@ struct trace_event *ftrace_find_event(int type) | |||
472 | return NULL; | 584 | return NULL; |
473 | } | 585 | } |
474 | 586 | ||
587 | static LIST_HEAD(ftrace_event_list); | ||
588 | |||
589 | static int trace_search_list(struct list_head **list) | ||
590 | { | ||
591 | struct trace_event *e; | ||
592 | int last = __TRACE_LAST_TYPE; | ||
593 | |||
594 | if (list_empty(&ftrace_event_list)) { | ||
595 | *list = &ftrace_event_list; | ||
596 | return last + 1; | ||
597 | } | ||
598 | |||
599 | /* | ||
600 | * We used up all possible max events, | ||
601 | * lets see if somebody freed one. | ||
602 | */ | ||
603 | list_for_each_entry(e, &ftrace_event_list, list) { | ||
604 | if (e->type != last + 1) | ||
605 | break; | ||
606 | last++; | ||
607 | } | ||
608 | |||
609 | /* Did we used up all 65 thousand events??? */ | ||
610 | if ((last + 1) > FTRACE_MAX_EVENT) | ||
611 | return 0; | ||
612 | |||
613 | *list = &e->list; | ||
614 | return last + 1; | ||
615 | } | ||
616 | |||
617 | void trace_event_read_lock(void) | ||
618 | { | ||
619 | down_read(&trace_event_mutex); | ||
620 | } | ||
621 | |||
622 | void trace_event_read_unlock(void) | ||
623 | { | ||
624 | up_read(&trace_event_mutex); | ||
625 | } | ||
626 | |||
475 | /** | 627 | /** |
476 | * register_ftrace_event - register output for an event type | 628 | * register_ftrace_event - register output for an event type |
477 | * @event: the event type to register | 629 | * @event: the event type to register |
@@ -492,22 +644,42 @@ int register_ftrace_event(struct trace_event *event) | |||
492 | unsigned key; | 644 | unsigned key; |
493 | int ret = 0; | 645 | int ret = 0; |
494 | 646 | ||
495 | mutex_lock(&trace_event_mutex); | 647 | down_write(&trace_event_mutex); |
496 | 648 | ||
497 | if (!event) { | 649 | if (WARN_ON(!event)) |
498 | ret = next_event_type++; | ||
499 | goto out; | 650 | goto out; |
500 | } | ||
501 | 651 | ||
502 | if (!event->type) | 652 | INIT_LIST_HEAD(&event->list); |
503 | event->type = next_event_type++; | 653 | |
504 | else if (event->type > __TRACE_LAST_TYPE) { | 654 | if (!event->type) { |
655 | struct list_head *list = NULL; | ||
656 | |||
657 | if (next_event_type > FTRACE_MAX_EVENT) { | ||
658 | |||
659 | event->type = trace_search_list(&list); | ||
660 | if (!event->type) | ||
661 | goto out; | ||
662 | |||
663 | } else { | ||
664 | |||
665 | event->type = next_event_type++; | ||
666 | list = &ftrace_event_list; | ||
667 | } | ||
668 | |||
669 | if (WARN_ON(ftrace_find_event(event->type))) | ||
670 | goto out; | ||
671 | |||
672 | list_add_tail(&event->list, list); | ||
673 | |||
674 | } else if (event->type > __TRACE_LAST_TYPE) { | ||
505 | printk(KERN_WARNING "Need to add type to trace.h\n"); | 675 | printk(KERN_WARNING "Need to add type to trace.h\n"); |
506 | WARN_ON(1); | 676 | WARN_ON(1); |
507 | } | ||
508 | |||
509 | if (ftrace_find_event(event->type)) | ||
510 | goto out; | 677 | goto out; |
678 | } else { | ||
679 | /* Is this event already used */ | ||
680 | if (ftrace_find_event(event->type)) | ||
681 | goto out; | ||
682 | } | ||
511 | 683 | ||
512 | if (event->trace == NULL) | 684 | if (event->trace == NULL) |
513 | event->trace = trace_nop_print; | 685 | event->trace = trace_nop_print; |
@@ -520,14 +692,25 @@ int register_ftrace_event(struct trace_event *event) | |||
520 | 692 | ||
521 | key = event->type & (EVENT_HASHSIZE - 1); | 693 | key = event->type & (EVENT_HASHSIZE - 1); |
522 | 694 | ||
523 | hlist_add_head_rcu(&event->node, &event_hash[key]); | 695 | hlist_add_head(&event->node, &event_hash[key]); |
524 | 696 | ||
525 | ret = event->type; | 697 | ret = event->type; |
526 | out: | 698 | out: |
527 | mutex_unlock(&trace_event_mutex); | 699 | up_write(&trace_event_mutex); |
528 | 700 | ||
529 | return ret; | 701 | return ret; |
530 | } | 702 | } |
703 | EXPORT_SYMBOL_GPL(register_ftrace_event); | ||
704 | |||
705 | /* | ||
706 | * Used by module code with the trace_event_mutex held for write. | ||
707 | */ | ||
708 | int __unregister_ftrace_event(struct trace_event *event) | ||
709 | { | ||
710 | hlist_del(&event->node); | ||
711 | list_del(&event->list); | ||
712 | return 0; | ||
713 | } | ||
531 | 714 | ||
532 | /** | 715 | /** |
533 | * unregister_ftrace_event - remove a no longer used event | 716 | * unregister_ftrace_event - remove a no longer used event |
@@ -535,12 +718,13 @@ int register_ftrace_event(struct trace_event *event) | |||
535 | */ | 718 | */ |
536 | int unregister_ftrace_event(struct trace_event *event) | 719 | int unregister_ftrace_event(struct trace_event *event) |
537 | { | 720 | { |
538 | mutex_lock(&trace_event_mutex); | 721 | down_write(&trace_event_mutex); |
539 | hlist_del(&event->node); | 722 | __unregister_ftrace_event(event); |
540 | mutex_unlock(&trace_event_mutex); | 723 | up_write(&trace_event_mutex); |
541 | 724 | ||
542 | return 0; | 725 | return 0; |
543 | } | 726 | } |
727 | EXPORT_SYMBOL_GPL(unregister_ftrace_event); | ||
544 | 728 | ||
545 | /* | 729 | /* |
546 | * Standard events | 730 | * Standard events |
@@ -833,14 +1017,16 @@ static enum print_line_t trace_stack_print(struct trace_iterator *iter, | |||
833 | 1017 | ||
834 | trace_assign_type(field, iter->ent); | 1018 | trace_assign_type(field, iter->ent); |
835 | 1019 | ||
1020 | if (!trace_seq_puts(s, "<stack trace>\n")) | ||
1021 | goto partial; | ||
836 | for (i = 0; i < FTRACE_STACK_ENTRIES; i++) { | 1022 | for (i = 0; i < FTRACE_STACK_ENTRIES; i++) { |
837 | if (i) { | 1023 | if (!field->caller[i] || (field->caller[i] == ULONG_MAX)) |
838 | if (!trace_seq_puts(s, " <= ")) | 1024 | break; |
839 | goto partial; | 1025 | if (!trace_seq_puts(s, " => ")) |
1026 | goto partial; | ||
840 | 1027 | ||
841 | if (!seq_print_ip_sym(s, field->caller[i], flags)) | 1028 | if (!seq_print_ip_sym(s, field->caller[i], flags)) |
842 | goto partial; | 1029 | goto partial; |
843 | } | ||
844 | if (!trace_seq_puts(s, "\n")) | 1030 | if (!trace_seq_puts(s, "\n")) |
845 | goto partial; | 1031 | goto partial; |
846 | } | 1032 | } |
@@ -868,10 +1054,10 @@ static enum print_line_t trace_user_stack_print(struct trace_iterator *iter, | |||
868 | 1054 | ||
869 | trace_assign_type(field, iter->ent); | 1055 | trace_assign_type(field, iter->ent); |
870 | 1056 | ||
871 | if (!seq_print_userip_objs(field, s, flags)) | 1057 | if (!trace_seq_puts(s, "<user stack trace>\n")) |
872 | goto partial; | 1058 | goto partial; |
873 | 1059 | ||
874 | if (!trace_seq_putc(s, '\n')) | 1060 | if (!seq_print_userip_objs(field, s, flags)) |
875 | goto partial; | 1061 | goto partial; |
876 | 1062 | ||
877 | return TRACE_TYPE_HANDLED; | 1063 | return TRACE_TYPE_HANDLED; |