| @@ -598,88 +598,165 @@ out: · 597 unmodified lines |
| 598 | return ret; | 598 | return ret; |
| 599 | } | 599 | } |
| 600 | | 600 | |
| 601 | static void print_event_fields(struct trace_seq *s, struct list_head *head) | 601 | enum { |
| | 602 | FORMAT_HEADER = 1, |
| | 603 | FORMAT_PRINTFMT = 2, |
| | 604 | }; |
| | 605 | |
| | 606 | static void *f_next(struct seq_file *m, void *v, loff_t *pos) |
| 602 | { | 607 | { |
| | 608 | struct ftrace_event_call *call = m->private; |
| 603 | struct ftrace_event_field *field; | 609 | struct ftrace_event_field *field; |
| | 610 | struct list_head *head; |
| 604 | | 611 | |
| 605 | list_for_each_entry_reverse(field, head, link) { | 612 | (*pos)++; |
| 606 | /* | | |
| 607 | * Smartly shows the array type(except dynamic array). | | |
| 608 | * Normal: | | |
| 609 | * field:TYPE VAR | | |
| 610 | * If TYPE := TYPE[LEN], it is shown: | | |
| 611 | * field:TYPE VAR[LEN] | | |
| 612 | */ | | |
| 613 | const char *array_descriptor = strchr(field->type, '['); | | |
| 614 | | 613 | |
| 615 | if (!strncmp(field->type, "__data_loc", 10)) | 614 | switch ((unsigned long)v) { |
| 616 | array_descriptor = NULL; | 615 | case FORMAT_HEADER: |
| | 616 | head = &ftrace_common_fields; |
| 617 | | 617 | |
| 618 | if (!array_descriptor) { | 618 | if (unlikely(list_empty(head))) |
| 619 | trace_seq_printf(s, "\tfield:%s %s;\toffset:%u;" | 619 | return NULL; |
| 620 | "\tsize:%u;\tsigned:%d;\n", | 620 | |
| 621 | field->type, field->name, field->offset, | 621 | field = list_entry(head->prev, struct ftrace_event_field, link); |
| 622 | field->size, !!field->is_signed); | 622 | return field; |
| 623 | } else { | 623 | |
| 624 | trace_seq_printf(s, "\tfield:%.*s %s%s;\toffset:%u;" | 624 | case FORMAT_PRINTFMT: |
| 625 | "\tsize:%u;\tsigned:%d;\n", | 625 | /* all done */ |
| 626 | (int)(array_descriptor - field->type), | 626 | return NULL; |
| 627 | field->type, field->name, | 627 | } |
| 628 | array_descriptor, field->offset, | 628 | |
| 629 | field->size, !!field->is_signed); | 629 | head = trace_get_fields(call); |
| 630 | } | 630 | |
| | 631 | /* |
| | 632 | * To separate common fields from event fields, the |
| | 633 | * LSB is set on the first event field. Clear it in case. |
| | 634 | */ |
| | 635 | v = (void *)((unsigned long)v & ~1L); |
| | 636 | |
| | 637 | field = v; |
| | 638 | /* |
| | 639 | * If this is a common field, and at the end of the list, then |
| | 640 | * continue with main list. |
| | 641 | */ |
| | 642 | if (field->link.prev == &ftrace_common_fields) { |
| | 643 | if (unlikely(list_empty(head))) |
| | 644 | return NULL; |
| | 645 | field = list_entry(head->prev, struct ftrace_event_field, link); |
| | 646 | /* Set the LSB to notify f_show to print an extra newline */ |
| | 647 | field = (struct ftrace_event_field *) |
| | 648 | ((unsigned long)field | 1); |
| | 649 | return field; |
| 631 | } | 650 | } |
| | 651 | |
| | 652 | /* If we are done tell f_show to print the format */ |
| | 653 | if (field->link.prev == head) |
| | 654 | return (void *)FORMAT_PRINTFMT; |
| | 655 | |
| | 656 | field = list_entry(field->link.prev, struct ftrace_event_field, link); |
| | 657 | |
| | 658 | return field; |
| 632 | } | 659 | } |
| 633 | | 660 | |
| 634 | static ssize_t | 661 | static void *f_start(struct seq_file *m, loff_t *pos) |
| 635 | event_format_read(struct file *filp, char __user *ubuf, size_t cnt, | | |
| 636 | loff_t *ppos) | | |
| 637 | { | 662 | { |
| 638 | struct ftrace_event_call *call = filp->private_data; | 663 | loff_t l = 0; |
| 639 | struct list_head *head; | 664 | void *p; |
| 640 | struct trace_seq *s; | | |
| 641 | char *buf; | | |
| 642 | int r; | | |
| 643 | | 665 | |
| 644 | if (*ppos) | 666 | /* Start by showing the header */ |
| | 667 | if (!*pos) |
| | 668 | return (void *)FORMAT_HEADER; |
| | 669 | |
| | 670 | p = (void *)FORMAT_HEADER; |
| | 671 | do { |
| | 672 | p = f_next(m, p, &l); |
| | 673 | } while (p && l < *pos); |
| | 674 | |
| | 675 | return p; |
| | 676 | } |
| | 677 | |
| | 678 | static int f_show(struct seq_file *m, void *v) |
| | 679 | { |
| | 680 | struct ftrace_event_call *call = m->private; |
| | 681 | struct ftrace_event_field *field; |
| | 682 | const char *array_descriptor; |
| | 683 | |
| | 684 | switch ((unsigned long)v) { |
| | 685 | case FORMAT_HEADER: |
| | 686 | seq_printf(m, "name: %s\n", call->name); |
| | 687 | seq_printf(m, "ID: %d\n", call->event.type); |
| | 688 | seq_printf(m, "format:\n"); |
| 645 | return 0; | 689 | return 0; |
| 646 | | 690 | |
| 647 | s = kmalloc(sizeof(*s), GFP_KERNEL); | 691 | case FORMAT_PRINTFMT: |
| 648 | if (!s) | 692 | seq_printf(m, "\nprint fmt: %s\n", |
| 649 | return -ENOMEM; | 693 | call->print_fmt); |
| | 694 | return 0; |
| | 695 | } |
| 650 | | 696 | |
| 651 | trace_seq_init(s); | 697 | /* |
| | 698 | * To separate common fields from event fields, the |
| | 699 | * LSB is set on the first event field. Clear it and |
| | 700 | * print a newline if it is set. |
| | 701 | */ |
| | 702 | if ((unsigned long)v & 1) { |
| | 703 | seq_putc(m, '\n'); |
| | 704 | v = (void *)((unsigned long)v & ~1L); |
| | 705 | } |
| 652 | | 706 | |
| 653 | trace_seq_printf(s, "name: %s\n", call->name); | 707 | field = v; |
| 654 | trace_seq_printf(s, "ID: %d\n", call->event.type); | | |
| 655 | trace_seq_printf(s, "format:\n"); | | |
| 656 | | 708 | |
| 657 | /* print common fields */ | 709 | /* |
| 658 | print_event_fields(s, &ftrace_common_fields); | 710 | * Smartly shows the array type(except dynamic array). |
| | 711 | * Normal: |
| | 712 | * field:TYPE VAR |
| | 713 | * If TYPE := TYPE[LEN], it is shown: |
| | 714 | * field:TYPE VAR[LEN] |
| | 715 | */ |
| | 716 | array_descriptor = strchr(field->type, '['); |
| 659 | | 717 | |
| 660 | trace_seq_putc(s, '\n'); | 718 | if (!strncmp(field->type, "__data_loc", 10)) |
| | 719 | array_descriptor = NULL; |
| 661 | | 720 | |
| 662 | /* print event specific fields */ | 721 | if (!array_descriptor) |
| 663 | head = trace_get_fields(call); | 722 | seq_printf(m, "\tfield:%s %s;\toffset:%u;\tsize:%u;\tsigned:%d;\n", |
| 664 | print_event_fields(s, head); | 723 | field->type, field->name, field->offset, |
| | 724 | field->size, !!field->is_signed); |
| | 725 | else |
| | 726 | seq_printf(m, "\tfield:%.*s %s%s;\toffset:%u;\tsize:%u;\tsigned:%d;\n", |
| | 727 | (int)(array_descriptor - field->type), |
| | 728 | field->type, field->name, |
| | 729 | array_descriptor, field->offset, |
| | 730 | field->size, !!field->is_signed); |
| 665 | | 731 | |
| 666 | r = trace_seq_printf(s, "\nprint fmt: %s\n", call->print_fmt); | 732 | return 0; |
| | 733 | } |
| 667 | | 734 | |
| 668 | if (!r) { | 735 | static void f_stop(struct seq_file *m, void *p) |
| 669 | /* | 736 | { |
| 670 | * ug! The format output is bigger than a PAGE!! | 737 | } |
| 671 | */ | | |
| 672 | buf = "FORMAT TOO BIG\n"; | | |
| 673 | r = simple_read_from_buffer(ubuf, cnt, ppos, | | |
| 674 | buf, strlen(buf)); | | |
| 675 | goto out; | | |
| 676 | } | | |
| 677 | | 738 | |
| 678 | r = simple_read_from_buffer(ubuf, cnt, ppos, | 739 | static const struct seq_operations trace_format_seq_ops = { |
| 679 | s->buffer, s->len); | 740 | .start = f_start, |
| 680 | out: | 741 | .next = f_next, |
| 681 | kfree(s); | 742 | .stop = f_stop, |
| 682 | return r; | 743 | .show = f_show, |
| | 744 | }; |
| | 745 | |
| | 746 | static int trace_format_open(struct inode *inode, struct file *file) |
| | 747 | { |
| | 748 | struct ftrace_event_call *call = inode->i_private; |
| | 749 | struct seq_file *m; |
| | 750 | int ret; |
| | 751 | |
| | 752 | ret = seq_open(file, &trace_format_seq_ops); |
| | 753 | if (ret < 0) |
| | 754 | return ret; |
| | 755 | |
| | 756 | m = file->private_data; |
| | 757 | m->private = call; |
| | 758 | |
| | 759 | return 0; |
| 683 | } | 760 | } |
| 684 | | 761 | |
| 685 | static ssize_t | 762 | static ssize_t |
| @@ -877,8 +954,10 @@ static const struct file_operations ftrace_enable_fops = { · 191 unmodified lines |
| 877 | }; | 954 | }; |
| 878 | | 955 | |
| 879 | static const struct file_operations ftrace_event_format_fops = { | 956 | static const struct file_operations ftrace_event_format_fops = { |
| 880 | .open = tracing_open_generic, | 957 | .open = trace_format_open, |
| 881 | .read = event_format_read, | 958 | .read = seq_read, |
| | 959 | .llseek = seq_lseek, |
| | 960 | .release = seq_release, |
| 882 | }; | 961 | }; |
| 883 | | 962 | |
| 884 | static const struct file_operations ftrace_event_id_fops = { | 963 | static const struct file_operations ftrace_event_id_fops = { |