diff options
author | Steven Rostedt <srostedt@redhat.com> | 2011-02-22 21:45:54 -0500 |
---|---|---|
committer | Steven Rostedt <rostedt@goodmis.org> | 2011-02-22 21:45:54 -0500 |
commit | f7dedb40bd2969f990a78c82662a555c011da6fa (patch) | |
tree | 8ff8b81a36861c86cd8cf581233e7b93a1dffc3f | |
parent | e9fa08f59877a98b65cc2c26129c81778de31279 (diff) |
trace-cmd: Allow multiple data files to be read
Let the user add -i multiple times to read multiple trace.dat files that
will be merged sorted according to the timestamp that is given.
Suggested-by: Thomas Taranowski <tom@baringforge.com>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
-rw-r--r-- | trace-local.h | 2 | ||||
-rw-r--r-- | trace-read.c | 353 |
2 files changed, 259 insertions, 96 deletions
diff --git a/trace-local.h b/trace-local.h index 73c38d2..72f6450 100644 --- a/trace-local.h +++ b/trace-local.h | |||
@@ -33,7 +33,7 @@ void usage(char **argv); | |||
33 | extern int silence_warnings; | 33 | extern int silence_warnings; |
34 | extern int show_status; | 34 | extern int show_status; |
35 | 35 | ||
36 | struct tracecmd_input *read_trace_header(void); | 36 | struct tracecmd_input *read_trace_header(const char *file); |
37 | int read_trace_files(void); | 37 | int read_trace_files(void); |
38 | 38 | ||
39 | void trace_report(int argc, char **argv); | 39 | void trace_report(int argc, char **argv); |
diff --git a/trace-read.c b/trace-read.c index 326054e..0e9ced8 100644 --- a/trace-read.c +++ b/trace-read.c | |||
@@ -39,6 +39,7 @@ | |||
39 | 39 | ||
40 | #include "trace-local.h" | 40 | #include "trace-local.h" |
41 | #include "trace-hash-local.h" | 41 | #include "trace-hash-local.h" |
42 | #include "list.h" | ||
42 | 43 | ||
43 | static struct filter { | 44 | static struct filter { |
44 | struct filter *next; | 45 | struct filter *next; |
@@ -47,13 +48,30 @@ static struct filter { | |||
47 | } *filter_strings; | 48 | } *filter_strings; |
48 | static struct filter **filter_next = &filter_strings; | 49 | static struct filter **filter_next = &filter_strings; |
49 | 50 | ||
50 | static struct event_filter *event_filters; | 51 | struct handle_list { |
51 | static struct event_filter *event_filter_out; | 52 | struct list_head list; |
53 | struct tracecmd_input *handle; | ||
54 | const char *file; | ||
55 | int cpus; | ||
56 | int done; | ||
57 | struct record *record; | ||
58 | struct event_filter *event_filters; | ||
59 | struct event_filter *event_filter_out; | ||
60 | }; | ||
61 | static struct list_head handle_list; | ||
62 | |||
63 | struct input_files { | ||
64 | struct list_head list; | ||
65 | const char *file; | ||
66 | }; | ||
67 | static struct list_head input_files; | ||
52 | 68 | ||
53 | static unsigned int page_size; | 69 | static unsigned int page_size; |
54 | static int input_fd; | 70 | static int input_fd; |
55 | const char *default_input_file = "trace.dat"; | 71 | static const char *default_input_file = "trace.dat"; |
56 | const char *input_file; | 72 | static const char *input_file; |
73 | static int multi_inputs; | ||
74 | static int max_file_size; | ||
57 | 75 | ||
58 | static int filter_cpu = -1; | 76 | static int filter_cpu = -1; |
59 | static int *filter_cpus; | 77 | static int *filter_cpus; |
@@ -231,6 +249,55 @@ static void test_save(struct record *record, int cpu) | |||
231 | } | 249 | } |
232 | #endif | 250 | #endif |
233 | 251 | ||
252 | static void add_input(const char *file) | ||
253 | { | ||
254 | struct input_files *item; | ||
255 | |||
256 | item = malloc_or_die(sizeof(*item)); | ||
257 | item->file = file; | ||
258 | list_add_tail(&item->list, &input_files); | ||
259 | } | ||
260 | |||
261 | static void add_handle(struct tracecmd_input *handle, const char *file) | ||
262 | { | ||
263 | struct handle_list *item; | ||
264 | |||
265 | item = malloc_or_die(sizeof(*item)); | ||
266 | memset(item, 0, sizeof(*item)); | ||
267 | item->handle = handle; | ||
268 | item->file = file + strlen(file); | ||
269 | /* we want just the base name */ | ||
270 | while (*item->file != '/' && item->file >= file) | ||
271 | item->file--; | ||
272 | item->file++; | ||
273 | if (strlen(item->file) > max_file_size) | ||
274 | max_file_size = strlen(item->file); | ||
275 | |||
276 | list_add_tail(&item->list, &handle_list); | ||
277 | } | ||
278 | |||
279 | static void free_inputs(void) | ||
280 | { | ||
281 | struct input_files *item; | ||
282 | |||
283 | while (!list_empty(&input_files)) { | ||
284 | item = container_of(input_files.next, struct input_files, list); | ||
285 | list_del(&item->list); | ||
286 | free(item); | ||
287 | } | ||
288 | } | ||
289 | |||
290 | static void free_handles(void) | ||
291 | { | ||
292 | struct handle_list *item; | ||
293 | |||
294 | while (!list_empty(&handle_list)) { | ||
295 | item = container_of(handle_list.next, struct handle_list, list); | ||
296 | list_del(&item->list); | ||
297 | free(item); | ||
298 | } | ||
299 | } | ||
300 | |||
234 | static void add_filter(const char *filter, int neg) | 301 | static void add_filter(const char *filter, int neg) |
235 | { | 302 | { |
236 | struct filter *ftr; | 303 | struct filter *ftr; |
@@ -245,7 +312,7 @@ static void add_filter(const char *filter, int neg) | |||
245 | filter_next = &ftr->next; | 312 | filter_next = &ftr->next; |
246 | } | 313 | } |
247 | 314 | ||
248 | static void process_filters(struct tracecmd_input *handle) | 315 | static void process_filters(struct handle_list *handles) |
249 | { | 316 | { |
250 | struct event_filter *event_filter; | 317 | struct event_filter *event_filter; |
251 | struct pevent *pevent; | 318 | struct pevent *pevent; |
@@ -253,17 +320,17 @@ static void process_filters(struct tracecmd_input *handle) | |||
253 | char *errstr; | 320 | char *errstr; |
254 | int ret; | 321 | int ret; |
255 | 322 | ||
256 | pevent = tracecmd_get_pevent(handle); | 323 | pevent = tracecmd_get_pevent(handles->handle); |
257 | event_filters = pevent_filter_alloc(pevent); | 324 | handles->event_filters = pevent_filter_alloc(pevent); |
258 | event_filter_out = pevent_filter_alloc(pevent); | 325 | handles->event_filter_out = pevent_filter_alloc(pevent); |
259 | 326 | ||
260 | while (filter_strings) { | 327 | while (filter_strings) { |
261 | filter = filter_strings; | 328 | filter = filter_strings; |
262 | filter_strings = filter->next; | 329 | filter_strings = filter->next; |
263 | if (filter->neg) | 330 | if (filter->neg) |
264 | event_filter = event_filter_out; | 331 | event_filter = handles->event_filter_out; |
265 | else | 332 | else |
266 | event_filter = event_filters; | 333 | event_filter = handles->event_filters; |
267 | 334 | ||
268 | ret = pevent_filter_add_filter_str(event_filter, | 335 | ret = pevent_filter_add_filter_str(event_filter, |
269 | filter->filter, | 336 | filter->filter, |
@@ -515,30 +582,21 @@ static void read_rest(void) | |||
515 | } while (r > 0); | 582 | } while (r > 0); |
516 | } | 583 | } |
517 | 584 | ||
518 | static void read_data_info(struct tracecmd_input *handle) | 585 | static struct record * |
586 | get_next_record(struct handle_list *handles, int *next_cpu) | ||
519 | { | 587 | { |
520 | unsigned long long ts; | 588 | unsigned long long ts; |
521 | struct record *record; | 589 | struct record *record; |
522 | int cpus; | 590 | int found = 0; |
523 | int next; | 591 | int next; |
524 | int cpu; | 592 | int cpu; |
525 | int ret; | 593 | int ret; |
526 | 594 | ||
527 | ret = tracecmd_init_data(handle); | 595 | if (handles->record) |
528 | if (ret < 0) | 596 | return handles->record; |
529 | die("failed to init data"); | ||
530 | |||
531 | cpus = tracecmd_cpus(handle); | ||
532 | printf("cpus=%d\n", cpus); | ||
533 | |||
534 | /* Latency trace is just all ASCII */ | ||
535 | if (ret > 0) { | ||
536 | read_rest(); | ||
537 | return; | ||
538 | } | ||
539 | 597 | ||
540 | init_wakeup(handle); | 598 | if (handles->done) |
541 | process_filters(handle); | 599 | return NULL; |
542 | 600 | ||
543 | do { | 601 | do { |
544 | next = -1; | 602 | next = -1; |
@@ -550,7 +608,7 @@ static void read_data_info(struct tracecmd_input *handle) | |||
550 | int i; | 608 | int i; |
551 | 609 | ||
552 | for (i = 0; (cpu = filter_cpus[i]) >= 0; i++) { | 610 | for (i = 0; (cpu = filter_cpus[i]) >= 0; i++) { |
553 | precord = tracecmd_peek_data(handle, cpu); | 611 | precord = tracecmd_peek_data(handles->handle, cpu); |
554 | if (precord && | 612 | if (precord && |
555 | (!last_stamp || precord->ts < last_stamp)) { | 613 | (!last_stamp || precord->ts < last_stamp)) { |
556 | next_cpu = cpu; | 614 | next_cpu = cpu; |
@@ -558,41 +616,123 @@ static void read_data_info(struct tracecmd_input *handle) | |||
558 | } | 616 | } |
559 | } | 617 | } |
560 | if (last_stamp) | 618 | if (last_stamp) |
561 | record = tracecmd_read_data(handle, next_cpu); | 619 | record = tracecmd_read_data(handles->handle, next_cpu); |
562 | else | 620 | else |
563 | record = NULL; | 621 | record = NULL; |
564 | 622 | ||
565 | } else if (filter_cpu >= 0) { | 623 | } else if (filter_cpu >= 0) { |
566 | cpu = filter_cpu; | 624 | cpu = filter_cpu; |
567 | record = tracecmd_read_data(handle, cpu); | 625 | record = tracecmd_read_data(handles->handle, cpu); |
568 | } else | 626 | } else |
569 | record = tracecmd_read_next_data(handle, &cpu); | 627 | record = tracecmd_read_next_data(handles->handle, &cpu); |
570 | 628 | ||
571 | if (record) { | 629 | if (record) { |
572 | ret = pevent_filter_match(event_filters, record); | 630 | ret = pevent_filter_match(handles->event_filters, record); |
573 | switch (ret) { | 631 | switch (ret) { |
574 | case FILTER_NONE: | 632 | case FILTER_NONE: |
575 | case FILTER_MATCH: | 633 | case FILTER_MATCH: |
576 | ret = pevent_filter_match(event_filter_out, record); | 634 | ret = pevent_filter_match(handles->event_filter_out, record); |
577 | if (ret != FILTER_MATCH) | 635 | if (ret != FILTER_MATCH) { |
578 | show_data(handle, record, next); | 636 | found = 1; |
579 | break; | 637 | break; |
638 | } | ||
639 | free_record(record); | ||
580 | } | 640 | } |
581 | free_record(record); | ||
582 | } | 641 | } |
583 | } while (record); | 642 | } while (record && !found); |
584 | 643 | ||
585 | pevent_filter_free(event_filters); | 644 | handles->record = record; |
586 | pevent_filter_free(event_filter_out); | 645 | if (!record) |
646 | handles->done = 1; | ||
647 | *next_cpu = next; | ||
587 | 648 | ||
588 | show_test(handle); | 649 | return record; |
589 | } | 650 | } |
590 | 651 | ||
591 | struct tracecmd_input *read_trace_header(void) | 652 | static void free_handle_record(struct handle_list *handles) |
592 | { | 653 | { |
593 | input_fd = open(input_file, O_RDONLY); | 654 | if (!handles->record) |
655 | return; | ||
656 | |||
657 | free_record(handles->record); | ||
658 | handles->record = NULL; | ||
659 | } | ||
660 | |||
661 | static void print_handle_file(struct handle_list *handles) | ||
662 | { | ||
663 | /* Only print file names if more than one file is read */ | ||
664 | if (!multi_inputs) | ||
665 | return; | ||
666 | printf("%*s: ", max_file_size, handles->file); | ||
667 | } | ||
668 | |||
669 | static void read_data_info(struct list_head *handle_list) | ||
670 | { | ||
671 | struct handle_list *handles; | ||
672 | struct handle_list *last_handle; | ||
673 | struct record *record; | ||
674 | struct record *last_record; | ||
675 | int last_cpu; | ||
676 | int cpus; | ||
677 | int next; | ||
678 | int ret; | ||
679 | |||
680 | list_for_each_entry(handles, handle_list, list) { | ||
681 | |||
682 | ret = tracecmd_init_data(handles->handle); | ||
683 | if (ret < 0) | ||
684 | die("failed to init data"); | ||
685 | |||
686 | cpus = tracecmd_cpus(handles->handle); | ||
687 | handles->cpus = cpus; | ||
688 | print_handle_file(handles); | ||
689 | printf("cpus=%d\n", cpus); | ||
690 | |||
691 | /* Latency trace is just all ASCII */ | ||
692 | if (ret > 0) { | ||
693 | if (multi_inputs) | ||
694 | die("latency traces do not work with multiple inputs"); | ||
695 | read_rest(); | ||
696 | return; | ||
697 | } | ||
698 | |||
699 | init_wakeup(handles->handle); | ||
700 | process_filters(handles); | ||
701 | } | ||
702 | |||
703 | do { | ||
704 | last_handle = NULL; | ||
705 | last_record = NULL; | ||
706 | |||
707 | list_for_each_entry(handles, handle_list, list) { | ||
708 | record = get_next_record(handles, &next); | ||
709 | if (!last_record || | ||
710 | (record && record->ts < last_record->ts)) { | ||
711 | last_record = record; | ||
712 | last_handle = handles; | ||
713 | last_cpu = next; | ||
714 | } | ||
715 | } | ||
716 | if (last_record) { | ||
717 | print_handle_file(last_handle); | ||
718 | show_data(last_handle->handle, last_record, last_cpu); | ||
719 | free_handle_record(last_handle); | ||
720 | } | ||
721 | } while (last_record); | ||
722 | |||
723 | list_for_each_entry(handles, handle_list, list) { | ||
724 | pevent_filter_free(handles->event_filters); | ||
725 | pevent_filter_free(handles->event_filter_out); | ||
726 | |||
727 | show_test(handles->handle); | ||
728 | } | ||
729 | } | ||
730 | |||
731 | struct tracecmd_input *read_trace_header(const char *file) | ||
732 | { | ||
733 | input_fd = open(file, O_RDONLY); | ||
594 | if (input_fd < 0) | 734 | if (input_fd < 0) |
595 | die("opening '%s'\n", input_file); | 735 | die("opening '%s'\n", file); |
596 | 736 | ||
597 | return tracecmd_alloc_fd(input_fd); | 737 | return tracecmd_alloc_fd(input_fd); |
598 | } | 738 | } |
@@ -720,6 +860,8 @@ void trace_report (int argc, char **argv) | |||
720 | struct tracecmd_input *handle; | 860 | struct tracecmd_input *handle; |
721 | struct pevent *pevent; | 861 | struct pevent *pevent; |
722 | const char *functions = NULL; | 862 | const char *functions = NULL; |
863 | struct input_files *inputs; | ||
864 | struct handle_list *handles; | ||
723 | int show_funcs = 0; | 865 | int show_funcs = 0; |
724 | int show_endian = 0; | 866 | int show_endian = 0; |
725 | int show_page_size = 0; | 867 | int show_page_size = 0; |
@@ -732,6 +874,9 @@ void trace_report (int argc, char **argv) | |||
732 | int neg = 0; | 874 | int neg = 0; |
733 | int c; | 875 | int c; |
734 | 876 | ||
877 | list_head_init(&handle_list); | ||
878 | list_head_init(&input_files); | ||
879 | |||
735 | if (argc < 2) | 880 | if (argc < 2) |
736 | usage(argv); | 881 | usage(argv); |
737 | 882 | ||
@@ -760,7 +905,13 @@ void trace_report (int argc, char **argv) | |||
760 | usage(argv); | 905 | usage(argv); |
761 | break; | 906 | break; |
762 | case 'i': | 907 | case 'i': |
763 | input_file = optarg; | 908 | if (input_file) { |
909 | if (!multi_inputs) | ||
910 | add_input(input_file); | ||
911 | multi_inputs++; | ||
912 | add_input(optarg); | ||
913 | } else | ||
914 | input_file = optarg; | ||
764 | break; | 915 | break; |
765 | case 'F': | 916 | case 'F': |
766 | add_filter(optarg, neg); | 917 | add_filter(optarg, neg); |
@@ -844,75 +995,87 @@ void trace_report (int argc, char **argv) | |||
844 | if (!input_file) | 995 | if (!input_file) |
845 | input_file = default_input_file; | 996 | input_file = default_input_file; |
846 | 997 | ||
847 | handle = read_trace_header(); | 998 | if (!multi_inputs) |
848 | if (!handle) | 999 | add_input(input_file); |
849 | die("error reading header"); | 1000 | else if (show_wakeup) |
1001 | die("Wakeup tracing can only be done on a single input file"); | ||
850 | 1002 | ||
851 | page_size = tracecmd_page_size(handle); | 1003 | list_for_each_entry(inputs, &input_files, list) { |
1004 | handle = read_trace_header(inputs->file); | ||
1005 | if (!handle) | ||
1006 | die("error reading header for %s", inputs->file); | ||
1007 | add_handle(handle, inputs->file); | ||
852 | 1008 | ||
853 | if (show_page_size) { | 1009 | page_size = tracecmd_page_size(handle); |
854 | printf("file page size is %d, and host page size is %d\n", | ||
855 | page_size, | ||
856 | getpagesize()); | ||
857 | return; | ||
858 | } | ||
859 | 1010 | ||
860 | pevent = tracecmd_get_pevent(handle); | 1011 | if (show_page_size) { |
1012 | printf("file page size is %d, and host page size is %d\n", | ||
1013 | page_size, | ||
1014 | getpagesize()); | ||
1015 | return; | ||
1016 | } | ||
861 | 1017 | ||
862 | if (raw) | 1018 | pevent = tracecmd_get_pevent(handle); |
863 | pevent->print_raw = 1; | ||
864 | 1019 | ||
865 | if (test_filters) | 1020 | if (raw) |
866 | pevent->test_filters = 1; | 1021 | pevent->print_raw = 1; |
867 | 1022 | ||
868 | if (functions) | 1023 | if (test_filters) |
869 | add_functions(pevent, functions); | 1024 | pevent->test_filters = 1; |
870 | 1025 | ||
871 | if (show_endian) { | 1026 | if (functions) |
872 | printf("file is %s endian and host is %s endian\n", | 1027 | add_functions(pevent, functions); |
873 | pevent_is_file_bigendian(pevent) ? "big" : "little", | ||
874 | pevent_is_host_bigendian(pevent) ? "big" : "little"); | ||
875 | return; | ||
876 | } | ||
877 | 1028 | ||
878 | if (print_events) { | 1029 | if (show_endian) { |
879 | tracecmd_print_events(handle); | 1030 | printf("file is %s endian and host is %s endian\n", |
880 | return; | 1031 | pevent_is_file_bigendian(pevent) ? "big" : "little", |
881 | } | 1032 | pevent_is_host_bigendian(pevent) ? "big" : "little"); |
1033 | return; | ||
1034 | } | ||
882 | 1035 | ||
883 | if (tracecmd_read_headers(handle) < 0) | 1036 | if (print_events) { |
884 | return; | 1037 | tracecmd_print_events(handle); |
1038 | return; | ||
1039 | } | ||
885 | 1040 | ||
886 | if (show_funcs) { | 1041 | if (tracecmd_read_headers(handle) < 0) |
887 | pevent_print_funcs(pevent); | 1042 | return; |
888 | return; | ||
889 | } | ||
890 | if (show_printk) { | ||
891 | pevent_print_printk(pevent); | ||
892 | return; | ||
893 | } | ||
894 | 1043 | ||
895 | if (show_events) { | 1044 | if (show_funcs) { |
896 | struct event_format **events; | 1045 | pevent_print_funcs(pevent); |
897 | struct event_format *event; | 1046 | return; |
898 | int i; | 1047 | } |
899 | 1048 | if (show_printk) { | |
900 | events = pevent_list_events(pevent, EVENT_SORT_SYSTEM); | 1049 | pevent_print_printk(pevent); |
901 | for (i = 0; events[i]; i++) { | 1050 | return; |
902 | event = events[i]; | 1051 | } |
903 | if (event->system) | 1052 | |
904 | printf("%s:", event->system); | 1053 | if (show_events) { |
905 | printf("%s\n", event->name); | 1054 | struct event_format **events; |
1055 | struct event_format *event; | ||
1056 | int i; | ||
1057 | |||
1058 | events = pevent_list_events(pevent, EVENT_SORT_SYSTEM); | ||
1059 | for (i = 0; events[i]; i++) { | ||
1060 | event = events[i]; | ||
1061 | if (event->system) | ||
1062 | printf("%s:", event->system); | ||
1063 | printf("%s\n", event->name); | ||
1064 | } | ||
1065 | return; | ||
906 | } | 1066 | } |
907 | return; | ||
908 | } | 1067 | } |
909 | 1068 | ||
910 | if (latency_format) | 1069 | if (latency_format) |
911 | pevent_set_latency_format(pevent, latency_format); | 1070 | pevent_set_latency_format(pevent, latency_format); |
912 | 1071 | ||
913 | read_data_info(handle); | 1072 | read_data_info(&handle_list); |
914 | 1073 | ||
915 | tracecmd_close(handle); | 1074 | list_for_each_entry(handles, &handle_list, list) { |
1075 | tracecmd_close(handles->handle); | ||
1076 | } | ||
1077 | free_handles(); | ||
1078 | free_inputs(); | ||
916 | 1079 | ||
917 | finish_wakeup(); | 1080 | finish_wakeup(); |
918 | 1081 | ||