diff options
-rw-r--r-- | trace-cmd.c | 138 |
1 files changed, 113 insertions, 25 deletions
diff --git a/trace-cmd.c b/trace-cmd.c index ac14a61..b755ebe 100644 --- a/trace-cmd.c +++ b/trace-cmd.c | |||
@@ -145,6 +145,19 @@ static void stop_threads(void) | |||
145 | } | 145 | } |
146 | } | 146 | } |
147 | 147 | ||
148 | static void flush_threads(void) | ||
149 | { | ||
150 | int i; | ||
151 | |||
152 | if (!cpu_count) | ||
153 | return; | ||
154 | |||
155 | for (i = 0; i < cpu_count; i++) { | ||
156 | if (pids[i] > 0) | ||
157 | kill(pids[i], SIGUSR1); | ||
158 | } | ||
159 | } | ||
160 | |||
148 | void die(char *fmt, ...) | 161 | void die(char *fmt, ...) |
149 | { | 162 | { |
150 | va_list ap; | 163 | va_list ap; |
@@ -569,6 +582,12 @@ static void finish(int sig) | |||
569 | finished = 1; | 582 | finished = 1; |
570 | } | 583 | } |
571 | 584 | ||
585 | static void flush(int sig) | ||
586 | { | ||
587 | if (recorder) | ||
588 | tracecmd_stop_recording(recorder); | ||
589 | } | ||
590 | |||
572 | static int create_recorder(int cpu) | 591 | static int create_recorder(int cpu) |
573 | { | 592 | { |
574 | char *file; | 593 | char *file; |
@@ -582,6 +601,7 @@ static int create_recorder(int cpu) | |||
582 | return pid; | 601 | return pid; |
583 | 602 | ||
584 | signal(SIGINT, finish); | 603 | signal(SIGINT, finish); |
604 | signal(SIGUSR1, flush); | ||
585 | 605 | ||
586 | /* do not kill tasks on error */ | 606 | /* do not kill tasks on error */ |
587 | cpu_count = 0; | 607 | cpu_count = 0; |
@@ -593,7 +613,8 @@ static int create_recorder(int cpu) | |||
593 | 613 | ||
594 | if (!recorder) | 614 | if (!recorder) |
595 | die ("can't create recorder"); | 615 | die ("can't create recorder"); |
596 | tracecmd_start_recording(recorder, sleep_time); | 616 | while (!finished) |
617 | tracecmd_start_recording(recorder, sleep_time); | ||
597 | tracecmd_free_recorder(recorder); | 618 | tracecmd_free_recorder(recorder); |
598 | 619 | ||
599 | exit(0); | 620 | exit(0); |
@@ -644,6 +665,46 @@ static void record_data(void) | |||
644 | tracecmd_output_close(handle); | 665 | tracecmd_output_close(handle); |
645 | } | 666 | } |
646 | 667 | ||
668 | static int trace_empty(void) | ||
669 | { | ||
670 | char *path; | ||
671 | FILE *fp; | ||
672 | char *line = NULL; | ||
673 | size_t size; | ||
674 | ssize_t n; | ||
675 | int ret; | ||
676 | |||
677 | /* | ||
678 | * Test if the trace file is empty. | ||
679 | * | ||
680 | * Yes, this is a heck of a hack. What is done here | ||
681 | * is to read the trace file and ignore the | ||
682 | * lines starting with '#', and if we get a line | ||
683 | * that is without a '#' the trace is not empty. | ||
684 | * Otherwise it is. | ||
685 | */ | ||
686 | path = get_tracing_file("trace"); | ||
687 | fp = fopen(path, "r"); | ||
688 | if (!fp) | ||
689 | die("reading '%s'", path); | ||
690 | |||
691 | do { | ||
692 | n = getline(&line, &size, fp); | ||
693 | if (!line) | ||
694 | ret = 1; | ||
695 | else if (line[0] != '#') | ||
696 | ret = 0; | ||
697 | if (n < 0) | ||
698 | ret = 1; | ||
699 | } while (line && n > 0); | ||
700 | |||
701 | put_tracing_file(path); | ||
702 | |||
703 | fclose(fp); | ||
704 | |||
705 | return ret; | ||
706 | } | ||
707 | |||
647 | void usage(char **argv) | 708 | void usage(char **argv) |
648 | { | 709 | { |
649 | char *arg = argv[0]; | 710 | char *arg = argv[0]; |
@@ -669,6 +730,9 @@ void usage(char **argv) | |||
669 | " Uses same options as record, but does not run a command.\n" | 730 | " Uses same options as record, but does not run a command.\n" |
670 | " It only enables the tracing and exits\n" | 731 | " It only enables the tracing and exits\n" |
671 | "\n" | 732 | "\n" |
733 | " %s extract [-p plugin][-O option][-o file]\n" | ||
734 | " Uses same options as record, but only reads an existing trace.\n" | ||
735 | "\n" | ||
672 | " %s stop\n" | 736 | " %s stop\n" |
673 | " Stops the tracer from recording more data.\n" | 737 | " Stops the tracer from recording more data.\n" |
674 | " Used in conjunction with start\n" | 738 | " Used in conjunction with start\n" |
@@ -689,7 +753,7 @@ void usage(char **argv) | |||
689 | " -e list available events\n" | 753 | " -e list available events\n" |
690 | " -p list available plugins\n" | 754 | " -p list available plugins\n" |
691 | " -o list available options\n" | 755 | " -o list available options\n" |
692 | "\n", p, TRACECMD_VERSION, p, p, p, p, p, p); | 756 | "\n", p, TRACECMD_VERSION, p, p, p, p, p, p, p); |
693 | exit(-1); | 757 | exit(-1); |
694 | } | 758 | } |
695 | 759 | ||
@@ -705,6 +769,7 @@ int main (int argc, char **argv) | |||
705 | int events = 0; | 769 | int events = 0; |
706 | int options = 0; | 770 | int options = 0; |
707 | int record = 0; | 771 | int record = 0; |
772 | int extract = 0; | ||
708 | int run_command = 0; | 773 | int run_command = 0; |
709 | int neg_event = 0; | 774 | int neg_event = 0; |
710 | int fset; | 775 | int fset; |
@@ -721,7 +786,8 @@ int main (int argc, char **argv) | |||
721 | trace_report(argc, argv); | 786 | trace_report(argc, argv); |
722 | exit(0); | 787 | exit(0); |
723 | } else if ((record = (strcmp(argv[1], "record") == 0)) || | 788 | } else if ((record = (strcmp(argv[1], "record") == 0)) || |
724 | (strcmp(argv[1], "start") == 0)) { | 789 | (strcmp(argv[1], "start") == 0) || |
790 | ((extract = strcmp(argv[1], "extract") == 0))) { | ||
725 | 791 | ||
726 | while ((c = getopt(argc-1, argv+1, "+he:p:do:O:s:v")) >= 0) { | 792 | while ((c = getopt(argc-1, argv+1, "+he:p:do:O:s:v")) >= 0) { |
727 | switch (c) { | 793 | switch (c) { |
@@ -729,6 +795,8 @@ int main (int argc, char **argv) | |||
729 | usage(argv); | 795 | usage(argv); |
730 | break; | 796 | break; |
731 | case 'e': | 797 | case 'e': |
798 | if (extract) | ||
799 | usage(argv); | ||
732 | events = 1; | 800 | events = 1; |
733 | event = malloc_or_die(sizeof(*event)); | 801 | event = malloc_or_die(sizeof(*event)); |
734 | event->event = optarg; | 802 | event->event = optarg; |
@@ -737,6 +805,8 @@ int main (int argc, char **argv) | |||
737 | event_selection = event; | 805 | event_selection = event; |
738 | break; | 806 | break; |
739 | case 'v': | 807 | case 'v': |
808 | if (extract) | ||
809 | usage(argv); | ||
740 | neg_event = 1; | 810 | neg_event = 1; |
741 | break; | 811 | break; |
742 | case 'p': | 812 | case 'p': |
@@ -746,10 +816,12 @@ int main (int argc, char **argv) | |||
746 | fprintf(stderr, " plugin %s\n", plugin); | 816 | fprintf(stderr, " plugin %s\n", plugin); |
747 | break; | 817 | break; |
748 | case 'd': | 818 | case 'd': |
819 | if (extract) | ||
820 | usage(argv); | ||
749 | disable = 1; | 821 | disable = 1; |
750 | break; | 822 | break; |
751 | case 'o': | 823 | case 'o': |
752 | if (!record) | 824 | if (!record && !extract) |
753 | die("start does not take output\n" | 825 | die("start does not take output\n" |
754 | "Did you mean 'record'?"); | 826 | "Did you mean 'record'?"); |
755 | if (output) | 827 | if (output) |
@@ -761,6 +833,8 @@ int main (int argc, char **argv) | |||
761 | set_option(option); | 833 | set_option(option); |
762 | break; | 834 | break; |
763 | case 's': | 835 | case 's': |
836 | if (extract) | ||
837 | usage(argv); | ||
764 | sleep_time = atoi(optarg); | 838 | sleep_time = atoi(optarg); |
765 | break; | 839 | break; |
766 | } | 840 | } |
@@ -824,20 +898,26 @@ int main (int argc, char **argv) | |||
824 | if (!record) | 898 | if (!record) |
825 | die("Command start does not take any commands\n" | 899 | die("Command start does not take any commands\n" |
826 | "Did you mean 'record'?"); | 900 | "Did you mean 'record'?"); |
901 | if (extract) | ||
902 | die("Command extract does not take any commands\n" | ||
903 | "Did you mean 'record'?"); | ||
827 | run_command = 1; | 904 | run_command = 1; |
828 | } | 905 | } |
829 | 906 | ||
830 | if (!events && !plugin) | 907 | if (!events && !plugin && !extract) |
831 | die("no event or plugin was specified... aborting"); | 908 | die("no event or plugin was specified... aborting"); |
832 | 909 | ||
833 | if (output) | 910 | if (output) |
834 | output_file = output; | 911 | output_file = output; |
835 | 912 | ||
836 | fset = set_ftrace(!disable); | 913 | if (!extract) { |
837 | disable_all(); | 914 | fset = set_ftrace(!disable); |
915 | disable_all(); | ||
916 | |||
917 | if (events) | ||
918 | enable_events(); | ||
919 | } | ||
838 | 920 | ||
839 | if (events) | ||
840 | enable_events(); | ||
841 | if (plugin) { | 921 | if (plugin) { |
842 | /* | 922 | /* |
843 | * Latency tracers just save the trace and kill | 923 | * Latency tracers just save the trace and kill |
@@ -853,33 +933,41 @@ int main (int argc, char **argv) | |||
853 | if (fset < 0 && (strcmp(plugin, "function") == 0 || | 933 | if (fset < 0 && (strcmp(plugin, "function") == 0 || |
854 | strcmp(plugin, "function_graph") == 0)) | 934 | strcmp(plugin, "function_graph") == 0)) |
855 | die("function tracing not configured on this kernel"); | 935 | die("function tracing not configured on this kernel"); |
856 | set_plugin(plugin); | 936 | if (!extract) |
937 | set_plugin(plugin); | ||
857 | } | 938 | } |
858 | 939 | ||
859 | if (record) { | 940 | if (record || extract) { |
860 | if (!latency) | 941 | if (!latency) |
861 | start_threads(); | 942 | start_threads(); |
862 | signal(SIGINT, finish); | 943 | signal(SIGINT, finish); |
863 | } | 944 | } |
864 | 945 | ||
865 | enable_tracing(); | 946 | if (extract) { |
866 | if (latency) | 947 | while (!finished && !trace_empty()) { |
867 | reset_max_latency(); | 948 | flush_threads(); |
949 | sleep(1); | ||
950 | } | ||
951 | } else { | ||
952 | enable_tracing(); | ||
953 | if (latency) | ||
954 | reset_max_latency(); | ||
868 | 955 | ||
869 | if (!record) | 956 | if (!record) |
870 | exit(0); | 957 | exit(0); |
958 | |||
959 | if (run_command) | ||
960 | run_cmd((argc - optind) - 1, &argv[optind + 1]); | ||
961 | else { | ||
962 | /* sleep till we are woken with Ctrl^C */ | ||
963 | printf("Hit Ctrl^C to stop recording\n"); | ||
964 | while (!finished) | ||
965 | sleep(10); | ||
966 | } | ||
871 | 967 | ||
872 | if (run_command) | 968 | disable_tracing(); |
873 | run_cmd((argc - optind) - 1, &argv[optind + 1]); | ||
874 | else { | ||
875 | /* sleep till we are woken with Ctrl^C */ | ||
876 | printf("Hit Ctrl^C to stop recording\n"); | ||
877 | while (!finished) | ||
878 | sleep(10); | ||
879 | } | 969 | } |
880 | 970 | ||
881 | disable_tracing(); | ||
882 | |||
883 | stop_threads(); | 971 | stop_threads(); |
884 | 972 | ||
885 | record_data(); | 973 | record_data(); |