diff options
| -rw-r--r-- | kernel/trace/trace_events.c | 208 |
1 files changed, 141 insertions, 67 deletions
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index 53cffc0b0801..45a8968707aa 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c | |||
| @@ -29,6 +29,8 @@ DEFINE_MUTEX(event_mutex); | |||
| 29 | 29 | ||
| 30 | LIST_HEAD(ftrace_events); | 30 | LIST_HEAD(ftrace_events); |
| 31 | 31 | ||
| 32 | #define COMMON_FIELD_COUNT 5 | ||
| 33 | |||
| 32 | struct list_head * | 34 | struct list_head * |
| 33 | trace_get_fields(struct ftrace_event_call *event_call) | 35 | trace_get_fields(struct ftrace_event_call *event_call) |
| 34 | { | 36 | { |
| @@ -544,85 +546,155 @@ out: | |||
| 544 | return ret; | 546 | return ret; |
| 545 | } | 547 | } |
| 546 | 548 | ||
| 547 | static ssize_t | 549 | enum { |
| 548 | event_format_read(struct file *filp, char __user *ubuf, size_t cnt, | 550 | FORMAT_HEADER = 1, |
| 549 | loff_t *ppos) | 551 | FORMAT_PRINTFMT = 2, |
| 552 | }; | ||
| 553 | |||
| 554 | static void *f_next(struct seq_file *m, void *v, loff_t *pos) | ||
| 550 | { | 555 | { |
| 551 | struct ftrace_event_call *call = filp->private_data; | 556 | struct ftrace_event_call *call = m->private; |
| 552 | struct ftrace_event_field *field; | 557 | struct ftrace_event_field *field; |
| 553 | struct list_head *head; | 558 | struct list_head *head; |
| 554 | struct trace_seq *s; | 559 | loff_t index = *pos; |
| 555 | int common_field_count = 5; | ||
| 556 | char *buf; | ||
| 557 | int r = 0; | ||
| 558 | |||
| 559 | if (*ppos) | ||
| 560 | return 0; | ||
| 561 | 560 | ||
| 562 | s = kmalloc(sizeof(*s), GFP_KERNEL); | 561 | (*pos)++; |
| 563 | if (!s) | ||
| 564 | return -ENOMEM; | ||
| 565 | 562 | ||
| 566 | trace_seq_init(s); | 563 | head = trace_get_fields(call); |
| 567 | 564 | ||
| 568 | trace_seq_printf(s, "name: %s\n", call->name); | 565 | switch ((unsigned long)v) { |
| 569 | trace_seq_printf(s, "ID: %d\n", call->event.type); | 566 | case FORMAT_HEADER: |
| 570 | trace_seq_printf(s, "format:\n"); | ||
| 571 | 567 | ||
| 572 | head = trace_get_fields(call); | 568 | if (unlikely(list_empty(head))) |
| 573 | list_for_each_entry_reverse(field, head, link) { | 569 | return NULL; |
| 574 | /* | ||
| 575 | * Smartly shows the array type(except dynamic array). | ||
| 576 | * Normal: | ||
| 577 | * field:TYPE VAR | ||
| 578 | * If TYPE := TYPE[LEN], it is shown: | ||
| 579 | * field:TYPE VAR[LEN] | ||
| 580 | */ | ||
| 581 | const char *array_descriptor = strchr(field->type, '['); | ||
| 582 | |||
| 583 | if (!strncmp(field->type, "__data_loc", 10)) | ||
| 584 | array_descriptor = NULL; | ||
| 585 | |||
| 586 | if (!array_descriptor) { | ||
| 587 | r = trace_seq_printf(s, "\tfield:%s %s;\toffset:%u;" | ||
| 588 | "\tsize:%u;\tsigned:%d;\n", | ||
| 589 | field->type, field->name, field->offset, | ||
| 590 | field->size, !!field->is_signed); | ||
| 591 | } else { | ||
| 592 | r = trace_seq_printf(s, "\tfield:%.*s %s%s;\toffset:%u;" | ||
| 593 | "\tsize:%u;\tsigned:%d;\n", | ||
| 594 | (int)(array_descriptor - field->type), | ||
| 595 | field->type, field->name, | ||
| 596 | array_descriptor, field->offset, | ||
| 597 | field->size, !!field->is_signed); | ||
| 598 | } | ||
| 599 | 570 | ||
| 600 | if (--common_field_count == 0) | 571 | field = list_entry(head->prev, struct ftrace_event_field, link); |
| 601 | r = trace_seq_printf(s, "\n"); | 572 | return field; |
| 602 | 573 | ||
| 603 | if (!r) | 574 | case FORMAT_PRINTFMT: |
| 604 | break; | 575 | /* all done */ |
| 576 | return NULL; | ||
| 605 | } | 577 | } |
| 606 | 578 | ||
| 607 | if (r) | 579 | /* |
| 608 | r = trace_seq_printf(s, "\nprint fmt: %s\n", | 580 | * To separate common fields from event fields, the |
| 609 | call->print_fmt); | 581 | * LSB is set on the first event field. Clear it in case. |
| 582 | */ | ||
| 583 | v = (void *)((unsigned long)v & ~1L); | ||
| 610 | 584 | ||
| 611 | if (!r) { | 585 | field = v; |
| 612 | /* | 586 | if (field->link.prev == head) |
| 613 | * ug! The format output is bigger than a PAGE!! | 587 | return (void *)FORMAT_PRINTFMT; |
| 614 | */ | 588 | |
| 615 | buf = "FORMAT TOO BIG\n"; | 589 | field = list_entry(field->link.prev, struct ftrace_event_field, link); |
| 616 | r = simple_read_from_buffer(ubuf, cnt, ppos, | 590 | |
| 617 | buf, strlen(buf)); | 591 | /* Set the LSB to notify f_show to print an extra newline */ |
| 618 | goto out; | 592 | if (index == COMMON_FIELD_COUNT) |
| 593 | field = (struct ftrace_event_field *) | ||
| 594 | ((unsigned long)field | 1); | ||
| 595 | |||
| 596 | return field; | ||
| 597 | } | ||
| 598 | |||
| 599 | static void *f_start(struct seq_file *m, loff_t *pos) | ||
| 600 | { | ||
| 601 | loff_t l = 0; | ||
| 602 | void *p; | ||
| 603 | |||
| 604 | /* Start by showing the header */ | ||
| 605 | if (!*pos) | ||
| 606 | return (void *)FORMAT_HEADER; | ||
| 607 | |||
| 608 | p = (void *)FORMAT_HEADER; | ||
| 609 | do { | ||
| 610 | p = f_next(m, p, &l); | ||
| 611 | } while (p && l < *pos); | ||
| 612 | |||
| 613 | return p; | ||
| 614 | } | ||
| 615 | |||
| 616 | static int f_show(struct seq_file *m, void *v) | ||
| 617 | { | ||
| 618 | struct ftrace_event_call *call = m->private; | ||
| 619 | struct ftrace_event_field *field; | ||
| 620 | const char *array_descriptor; | ||
| 621 | |||
| 622 | switch ((unsigned long)v) { | ||
| 623 | case FORMAT_HEADER: | ||
| 624 | seq_printf(m, "name: %s\n", call->name); | ||
| 625 | seq_printf(m, "ID: %d\n", call->event.type); | ||
| 626 | seq_printf(m, "format:\n"); | ||
| 627 | return 0; | ||
| 628 | |||
| 629 | case FORMAT_PRINTFMT: | ||
| 630 | seq_printf(m, "\nprint fmt: %s\n", | ||
| 631 | call->print_fmt); | ||
| 632 | return 0; | ||
| 619 | } | 633 | } |
| 620 | 634 | ||
| 621 | r = simple_read_from_buffer(ubuf, cnt, ppos, | 635 | /* |
| 622 | s->buffer, s->len); | 636 | * To separate common fields from event fields, the |
| 623 | out: | 637 | * LSB is set on the first event field. Clear it and |
| 624 | kfree(s); | 638 | * print a newline if it is set. |
| 625 | return r; | 639 | */ |
| 640 | if ((unsigned long)v & 1) { | ||
| 641 | seq_putc(m, '\n'); | ||
| 642 | v = (void *)((unsigned long)v & ~1L); | ||
| 643 | } | ||
| 644 | |||
| 645 | field = v; | ||
| 646 | |||
| 647 | /* | ||
| 648 | * Smartly shows the array type(except dynamic array). | ||
| 649 | * Normal: | ||
| 650 | * field:TYPE VAR | ||
| 651 | * If TYPE := TYPE[LEN], it is shown: | ||
| 652 | * field:TYPE VAR[LEN] | ||
| 653 | */ | ||
| 654 | array_descriptor = strchr(field->type, '['); | ||
| 655 | |||
| 656 | if (!strncmp(field->type, "__data_loc", 10)) | ||
| 657 | array_descriptor = NULL; | ||
| 658 | |||
| 659 | if (!array_descriptor) | ||
| 660 | seq_printf(m, "\tfield:%s %s;\toffset:%u;\tsize:%u;\tsigned:%d;\n", | ||
| 661 | field->type, field->name, field->offset, | ||
| 662 | field->size, !!field->is_signed); | ||
| 663 | else | ||
| 664 | seq_printf(m, "\tfield:%.*s %s%s;\toffset:%u;\tsize:%u;\tsigned:%d;\n", | ||
| 665 | (int)(array_descriptor - field->type), | ||
| 666 | field->type, field->name, | ||
| 667 | array_descriptor, field->offset, | ||
| 668 | field->size, !!field->is_signed); | ||
| 669 | |||
| 670 | return 0; | ||
| 671 | } | ||
| 672 | |||
| 673 | static void f_stop(struct seq_file *m, void *p) | ||
| 674 | { | ||
| 675 | } | ||
| 676 | |||
| 677 | static const struct seq_operations trace_format_seq_ops = { | ||
| 678 | .start = f_start, | ||
| 679 | .next = f_next, | ||
| 680 | .stop = f_stop, | ||
| 681 | .show = f_show, | ||
| 682 | }; | ||
| 683 | |||
| 684 | static int trace_format_open(struct inode *inode, struct file *file) | ||
| 685 | { | ||
| 686 | struct ftrace_event_call *call = inode->i_private; | ||
| 687 | struct seq_file *m; | ||
| 688 | int ret; | ||
| 689 | |||
| 690 | ret = seq_open(file, &trace_format_seq_ops); | ||
| 691 | if (ret < 0) | ||
| 692 | return ret; | ||
| 693 | |||
| 694 | m = file->private_data; | ||
| 695 | m->private = call; | ||
| 696 | |||
| 697 | return 0; | ||
| 626 | } | 698 | } |
| 627 | 699 | ||
| 628 | static ssize_t | 700 | static ssize_t |
| @@ -820,8 +892,10 @@ static const struct file_operations ftrace_enable_fops = { | |||
| 820 | }; | 892 | }; |
| 821 | 893 | ||
| 822 | static const struct file_operations ftrace_event_format_fops = { | 894 | static const struct file_operations ftrace_event_format_fops = { |
| 823 | .open = tracing_open_generic, | 895 | .open = trace_format_open, |
| 824 | .read = event_format_read, | 896 | .read = seq_read, |
| 897 | .llseek = seq_lseek, | ||
| 898 | .release = seq_release, | ||
| 825 | }; | 899 | }; |
| 826 | 900 | ||
| 827 | static const struct file_operations ftrace_event_id_fops = { | 901 | static const struct file_operations ftrace_event_id_fops = { |
