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