diff options
author | Steven Rostedt <srostedt@redhat.com> | 2010-06-03 15:21:34 -0400 |
---|---|---|
committer | Steven Rostedt <rostedt@goodmis.org> | 2010-08-12 16:59:29 -0400 |
commit | 2a37a3df57c44e947271758a1aa4bea7bff9feab (patch) | |
tree | 89d3e35a1c29f021d7ca0c468a391ec35f0db34b /kernel/trace/trace_events.c | |
parent | 465c6cca2668a2db2a4ffce3dca5714017873f2b (diff) |
tracing/events: Convert format output to seq_file
Two new events were added that broke the current format output.
Both from the SCSI system: scsi_dispatch_cmd_done and scsi_dispatch_cmd_timeout
The reason is that their print_fmt exceeded a page size. Since the output
of the format used simple_read_from_buffer and trace_seq, it was limited
to a page size in output.
This patch converts the printing of the format of an event into seq_file,
which allows greater than a page size to be shown.
I diffed all event formats comparing the output with and without this
patch. All matched except for the above two, which showed just:
FORMAT TOO BIG
without this patch, but now properly displays the output with this patch.
v2: Remove updating *pos in seq start function.
[ Thanks to Li Zefan for pointing that out ]
Reviewed-by: Li Zefan <lizf@cn.fujitsu.com>
Cc: Martin K. Petersen <martin.petersen@oracle.com>
Cc: Kei Tokunaga <tokunaga.keiich@jp.fujitsu.com>
Cc: James Bottomley <James.Bottomley@suse.de>
Cc: Tomohiro Kusumi <kusumi.tomohiro@jp.fujitsu.com>
Cc: Xiao Guangrong <xiaoguangrong@cn.fujitsu.com>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Diffstat (limited to 'kernel/trace/trace_events.c')
-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 = { |