| @@ -598,88 +598,165 @@ out: · 597 unmodified lines |
| 598 | 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 | 609 | struct ftrace_event_field *field; |
| 610 | + struct list_head *head; |
| 604 | 611 | |
| 605 | | − list_for_each_entry_reverse(field, head, link) { |
| 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, '['); |
| 612 | + (*pos)++; |
| 614 | 613 | |
| 615 | | − if (!strncmp(field->type, "__data_loc", 10)) |
| 616 | | − array_descriptor = NULL; |
| 614 | + switch ((unsigned long)v) { |
| 615 | + case FORMAT_HEADER: |
| 616 | + head = &ftrace_common_fields; |
| 617 | 617 | |
| 618 | | − if (!array_descriptor) { |
| 619 | | − trace_seq_printf(s, "\tfield:%s %s;\toffset:%u;" |
| 620 | | − "\tsize:%u;\tsigned:%d;\n", |
| 621 | | − field->type, field->name, field->offset, |
| 622 | | − field->size, !!field->is_signed); |
| 623 | | − } else { |
| 624 | | − trace_seq_printf(s, "\tfield:%.*s %s%s;\toffset:%u;" |
| 625 | | − "\tsize:%u;\tsigned:%d;\n", |
| 626 | | − (int)(array_descriptor - field->type), |
| 627 | | − field->type, field->name, |
| 628 | | − array_descriptor, field->offset, |
| 629 | | − field->size, !!field->is_signed); |
| 630 | | − } |
| 618 | + if (unlikely(list_empty(head))) |
| 619 | + return NULL; |
| 620 | + |
| 621 | + field = list_entry(head->prev, struct ftrace_event_field, link); |
| 622 | + return field; |
| 623 | + |
| 624 | + case FORMAT_PRINTFMT: |
| 625 | + /* all done */ |
| 626 | + return NULL; |
| 627 | + } |
| 628 | + |
| 629 | + head = trace_get_fields(call); |
| 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 |
| 635 | | −event_format_read(struct file *filp, char __user *ubuf, size_t cnt, |
| 636 | | − loff_t *ppos) |
| 661 | +static void *f_start(struct seq_file *m, loff_t *pos) |
| 637 | 662 | { |
| 638 | | − struct ftrace_event_call *call = filp->private_data; |
| 639 | | − struct list_head *head; |
| 640 | | − struct trace_seq *s; |
| 641 | | − char *buf; |
| 642 | | − int r; |
| 663 | + loff_t l = 0; |
| 664 | + void *p; |
| 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 | 689 | return 0; |
| 646 | 690 | |
| 647 | | − s = kmalloc(sizeof(*s), GFP_KERNEL); |
| 648 | | − if (!s) |
| 649 | | − return -ENOMEM; |
| 691 | + case FORMAT_PRINTFMT: |
| 692 | + seq_printf(m, "\nprint fmt: %s\n", |
| 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); |
| 654 | | − trace_seq_printf(s, "ID: %d\n", call->event.type); |
| 655 | | − trace_seq_printf(s, "format:\n"); |
| 707 | + field = v; |
| 656 | 708 | |
| 657 | | − /* print common fields */ |
| 658 | | − print_event_fields(s, &ftrace_common_fields); |
| 709 | + /* |
| 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 */ |
| 663 | | − head = trace_get_fields(call); |
| 664 | | − print_event_fields(s, head); |
| 721 | + if (!array_descriptor) |
| 722 | + seq_printf(m, "\tfield:%s %s;\toffset:%u;\tsize:%u;\tsigned:%d;\n", |
| 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) { |
| 669 | | − /* |
| 670 | | − * ug! The format output is bigger than a PAGE!! |
| 671 | | − */ |
| 672 | | − buf = "FORMAT TOO BIG\n"; |
| 673 | | − r = simple_read_from_buffer(ubuf, cnt, ppos, |
| 674 | | − buf, strlen(buf)); |
| 675 | | − goto out; |
| 676 | | − } |
| 735 | +static void f_stop(struct seq_file *m, void *p) |
| 736 | +{ |
| 737 | +} |
| 677 | 738 | |
| 678 | | − r = simple_read_from_buffer(ubuf, cnt, ppos, |
| 679 | | − s->buffer, s->len); |
| 680 | | − out: |
| 681 | | − kfree(s); |
| 682 | | − return r; |
| 739 | +static const struct seq_operations trace_format_seq_ops = { |
| 740 | + .start = f_start, |
| 741 | + .next = f_next, |
| 742 | + .stop = f_stop, |
| 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 | 762 | static ssize_t |
| @@ -877,8 +954,10 @@ static const struct file_operations ftrace_enable_fops = { · 191 unmodified lines |
| 877 | 954 | }; |
| 878 | 955 | |
| 879 | 956 | static const struct file_operations ftrace_event_format_fops = { |
| 880 | | − .open = tracing_open_generic, |
| 881 | | − .read = event_format_read, |
| 957 | + .open = trace_format_open, |
| 958 | + .read = seq_read, |
| 959 | + .llseek = seq_lseek, |
| 960 | + .release = seq_release, |
| 882 | 961 | }; |
| 883 | 962 | |
| 884 | 963 | static const struct file_operations ftrace_event_id_fops = { |