diff options
-rw-r--r-- | Makefile | 9 | ||||
-rw-r--r-- | parse-events.h | 2 | ||||
-rw-r--r-- | trace-cmd.c | 642 | ||||
-rw-r--r-- | trace-cmd.h | 33 | ||||
-rw-r--r-- | trace-input.c | 2 | ||||
-rw-r--r-- | trace-output.c | 700 | ||||
-rw-r--r-- | trace-record.c | 133 | ||||
-rw-r--r-- | trace-util.c | 42 |
8 files changed, 1000 insertions, 563 deletions
@@ -39,6 +39,12 @@ trace-util.o:: trace-util.c | |||
39 | trace-input.o:: trace-input.c | 39 | trace-input.o:: trace-input.c |
40 | $(CC) -c $(CFLAGS) $(EXT) $(INCLUDES) -fPIC $< -o $@ | 40 | $(CC) -c $(CFLAGS) $(EXT) $(INCLUDES) -fPIC $< -o $@ |
41 | 41 | ||
42 | trace-output.o:: trace-output.c | ||
43 | $(CC) -c $(CFLAGS) $(EXT) $(INCLUDES) -fPIC $< -o $@ | ||
44 | |||
45 | trace-record.o:: trace-record.c | ||
46 | $(CC) -c $(CFLAGS) $(EXT) $(INCLUDES) -fPIC $< -o $@ | ||
47 | |||
42 | trace-ftrace.o:: trace-ftrace.c | 48 | trace-ftrace.o:: trace-ftrace.c |
43 | $(CC) -c $(CFLAGS) $(EXT) $(INCLUDES) -fPIC $< -o $@ | 49 | $(CC) -c $(CFLAGS) $(EXT) $(INCLUDES) -fPIC $< -o $@ |
44 | 50 | ||
@@ -50,7 +56,8 @@ libparsevent.so: $(PEVENT_LIB_OBJS) | |||
50 | libparsevent.a: $(PEVENT_LIB_OBJS) | 56 | libparsevent.a: $(PEVENT_LIB_OBJS) |
51 | $(RM) $@; $(AR) rcs $@ $^ | 57 | $(RM) $@; $(AR) rcs $@ $^ |
52 | 58 | ||
53 | TCMD_LIB_OBJS = $(PEVENT_LIB_OBJS) trace-util.o trace-input.o trace-ftrace.o | 59 | TCMD_LIB_OBJS = $(PEVENT_LIB_OBJS) trace-util.o trace-input.o trace-ftrace.o \ |
60 | trace-output.o trace-record.o | ||
54 | 61 | ||
55 | libtracecmd.so: $(TCMD_LIB_OBJS) | 62 | libtracecmd.so: $(TCMD_LIB_OBJS) |
56 | $(CC) --shared $^ -o $@ | 63 | $(CC) --shared $^ -o $@ |
diff --git a/parse-events.h b/parse-events.h index db7e259..9b5ba0d 100644 --- a/parse-events.h +++ b/parse-events.h | |||
@@ -273,8 +273,6 @@ void die(char *fmt, ...); | |||
273 | void *malloc_or_die(unsigned int size); | 273 | void *malloc_or_die(unsigned int size); |
274 | void warning(char *fmt, ...); | 274 | void warning(char *fmt, ...); |
275 | 275 | ||
276 | int bigendian(void); | ||
277 | |||
278 | static inline unsigned short | 276 | static inline unsigned short |
279 | __data2host2(struct pevent *pevent, unsigned short data) | 277 | __data2host2(struct pevent *pevent, unsigned short data) |
280 | { | 278 | { |
diff --git a/trace-cmd.c b/trace-cmd.c index aca6f90..aada9a4 100644 --- a/trace-cmd.c +++ b/trace-cmd.c | |||
@@ -35,8 +35,6 @@ | |||
35 | 35 | ||
36 | #include "trace-local.h" | 36 | #include "trace-local.h" |
37 | 37 | ||
38 | #define VERSION "0.5" | ||
39 | |||
40 | #define _STR(x) #x | 38 | #define _STR(x) #x |
41 | #define STR(x) _STR(x) | 39 | #define STR(x) _STR(x) |
42 | #define MAX_PATH 256 | 40 | #define MAX_PATH 256 |
@@ -51,7 +49,6 @@ | |||
51 | unsigned int page_size; | 49 | unsigned int page_size; |
52 | 50 | ||
53 | static const char *output_file = "trace.dat"; | 51 | static const char *output_file = "trace.dat"; |
54 | static int output_fd; | ||
55 | 52 | ||
56 | static int latency; | 53 | static int latency; |
57 | 54 | ||
@@ -72,6 +69,25 @@ struct events { | |||
72 | char *name; | 69 | char *name; |
73 | }; | 70 | }; |
74 | 71 | ||
72 | static struct tracecmd_recorder *recorder; | ||
73 | |||
74 | static char *get_temp_file(int cpu) | ||
75 | { | ||
76 | char *file = NULL; | ||
77 | int size; | ||
78 | |||
79 | size = snprintf(file, 0, "%s.cpu%d", output_file, cpu); | ||
80 | file = malloc_or_die(size + 1); | ||
81 | sprintf(file, "%s.cpu%d", output_file, cpu); | ||
82 | |||
83 | return file; | ||
84 | } | ||
85 | |||
86 | static void put_temp_file(char *file) | ||
87 | { | ||
88 | free(file); | ||
89 | } | ||
90 | |||
75 | static void delete_temp_file(int cpu) | 91 | static void delete_temp_file(int cpu) |
76 | { | 92 | { |
77 | char file[MAX_PATH]; | 93 | char file[MAX_PATH]; |
@@ -174,79 +190,6 @@ void *malloc_or_die(unsigned int size) | |||
174 | return data; | 190 | return data; |
175 | } | 191 | } |
176 | 192 | ||
177 | static const char *find_debugfs(void) | ||
178 | { | ||
179 | static char debugfs[MAX_PATH+1]; | ||
180 | static int debugfs_found; | ||
181 | char type[100]; | ||
182 | FILE *fp; | ||
183 | |||
184 | if (debugfs_found) | ||
185 | return debugfs; | ||
186 | |||
187 | if ((fp = fopen("/proc/mounts","r")) == NULL) | ||
188 | die("Can't open /proc/mounts for read"); | ||
189 | |||
190 | while (fscanf(fp, "%*s %" | ||
191 | STR(MAX_PATH) | ||
192 | "s %99s %*s %*d %*d\n", | ||
193 | debugfs, type) == 2) { | ||
194 | if (strcmp(type, "debugfs") == 0) | ||
195 | break; | ||
196 | } | ||
197 | fclose(fp); | ||
198 | |||
199 | if (strcmp(type, "debugfs") != 0) | ||
200 | die("debugfs not mounted, please mount"); | ||
201 | |||
202 | debugfs_found = 1; | ||
203 | |||
204 | return debugfs; | ||
205 | } | ||
206 | |||
207 | /* | ||
208 | * Finds the path to the debugfs/tracing | ||
209 | * Allocates the string and stores it. | ||
210 | */ | ||
211 | static const char *find_tracing_dir(void) | ||
212 | { | ||
213 | static char *tracing; | ||
214 | static int tracing_found; | ||
215 | const char *debugfs; | ||
216 | |||
217 | if (tracing_found) | ||
218 | return tracing; | ||
219 | |||
220 | debugfs = find_debugfs(); | ||
221 | |||
222 | tracing = malloc_or_die(strlen(debugfs) + 9); | ||
223 | |||
224 | sprintf(tracing, "%s/tracing", debugfs); | ||
225 | |||
226 | tracing_found = 1; | ||
227 | return tracing; | ||
228 | } | ||
229 | |||
230 | static char *get_tracing_file(const char *name) | ||
231 | { | ||
232 | const char *tracing; | ||
233 | char *file; | ||
234 | |||
235 | tracing = find_tracing_dir(); | ||
236 | if (!tracing) | ||
237 | return NULL; | ||
238 | |||
239 | file = malloc_or_die(strlen(tracing) + strlen(name) + 2); | ||
240 | |||
241 | sprintf(file, "%s/%s", tracing, name); | ||
242 | return file; | ||
243 | } | ||
244 | |||
245 | static void put_tracing_file(char *file) | ||
246 | { | ||
247 | free(file); | ||
248 | } | ||
249 | |||
250 | static int set_ftrace(int set) | 193 | static int set_ftrace(int set) |
251 | { | 194 | { |
252 | struct stat buf; | 195 | struct stat buf; |
@@ -284,6 +227,30 @@ void run_cmd(int argc, char **argv) | |||
284 | waitpid(pid, &status, 0); | 227 | waitpid(pid, &status, 0); |
285 | } | 228 | } |
286 | 229 | ||
230 | static char *get_tracing_file(const char *name) | ||
231 | { | ||
232 | static const char *tracing; | ||
233 | char *file; | ||
234 | |||
235 | if (!tracing) { | ||
236 | tracing = tracecmd_find_tracing_dir(); | ||
237 | if (!tracing) | ||
238 | die("Can't find tracing dir"); | ||
239 | } | ||
240 | |||
241 | file = malloc_or_die(strlen(tracing) + strlen(name) + 2); | ||
242 | if (!file) | ||
243 | return NULL; | ||
244 | |||
245 | sprintf(file, "%s/%s", tracing, name); | ||
246 | return file; | ||
247 | } | ||
248 | |||
249 | static void put_tracing_file(char *file) | ||
250 | { | ||
251 | free(file); | ||
252 | } | ||
253 | |||
287 | static void show_events(void) | 254 | static void show_events(void) |
288 | { | 255 | { |
289 | char buf[BUFSIZ]; | 256 | char buf[BUFSIZ]; |
@@ -572,20 +539,15 @@ static int finished; | |||
572 | static void finish(int sig) | 539 | static void finish(int sig) |
573 | { | 540 | { |
574 | /* all done */ | 541 | /* all done */ |
542 | if (recorder) | ||
543 | tracecmd_stop_recording(recorder); | ||
575 | finished = 1; | 544 | finished = 1; |
576 | } | 545 | } |
577 | 546 | ||
578 | static int create_recorder(int cpu) | 547 | static int create_recorder(int cpu) |
579 | { | 548 | { |
580 | char file[MAX_PATH]; | 549 | char *file; |
581 | const char *tracing; | ||
582 | char *path; | ||
583 | int out_fd; | ||
584 | int in_fd; | ||
585 | int brass[2]; | ||
586 | int pid; | 550 | int pid; |
587 | int ret; | ||
588 | char buf[page_size]; | ||
589 | 551 | ||
590 | pid = fork(); | 552 | pid = fork(); |
591 | if (pid < 0) | 553 | if (pid < 0) |
@@ -599,40 +561,15 @@ static int create_recorder(int cpu) | |||
599 | /* do not kill tasks on error */ | 561 | /* do not kill tasks on error */ |
600 | cpu_count = 0; | 562 | cpu_count = 0; |
601 | 563 | ||
602 | snprintf(file, MAX_PATH, "%s.cpu%d", output_file, cpu); | 564 | file = get_temp_file(cpu); |
603 | 565 | ||
604 | out_fd = open(file, O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, 0644); | 566 | recorder = tracecmd_create_recorder(file, cpu); |
605 | if (out_fd < 0) | 567 | put_temp_file(file); |
606 | die("can't create file '%s'", file); | ||
607 | 568 | ||
608 | tracing = find_tracing_dir(); | 569 | if (!recorder) |
609 | 570 | die ("can't create recorder"); | |
610 | path = malloc_or_die(strlen(tracing) + 40); | 571 | tracecmd_start_recording(recorder); |
611 | 572 | tracecmd_free_recorder(recorder); | |
612 | sprintf(path, "%s/per_cpu/cpu%d/trace_pipe_raw", tracing, cpu); | ||
613 | in_fd = open(path, O_RDONLY); | ||
614 | if (in_fd < 0) | ||
615 | die("can not read '%s'", path); | ||
616 | |||
617 | ret = pipe(brass); | ||
618 | if (ret < 0) | ||
619 | die("can not create pipe"); | ||
620 | |||
621 | do { | ||
622 | ret = splice(in_fd, NULL, brass[1], NULL, page_size, 1 /* SPLICE_F_MOVE */); | ||
623 | if (ret < 0) | ||
624 | die("splice in"); | ||
625 | ret = splice(brass[0], NULL, out_fd, NULL, page_size, 3 /* and NON_BLOCK */); | ||
626 | if (ret < 0 && errno != EAGAIN) | ||
627 | die("splice out"); | ||
628 | } while (!finished); | ||
629 | |||
630 | /* splice only reads full pages */ | ||
631 | do { | ||
632 | ret = read(in_fd, buf, page_size); | ||
633 | if (ret > 0) | ||
634 | write(out_fd, buf, ret); | ||
635 | } while (ret > 0); | ||
636 | 573 | ||
637 | exit(0); | 574 | exit(0); |
638 | } | 575 | } |
@@ -656,442 +593,32 @@ static void start_threads(void) | |||
656 | } | 593 | } |
657 | } | 594 | } |
658 | 595 | ||
659 | static ssize_t write_or_die(const void *buf, size_t len) | 596 | static void record_data(void) |
660 | { | ||
661 | int ret; | ||
662 | |||
663 | ret = write(output_fd, buf, len); | ||
664 | if (ret < 0) | ||
665 | die("writing to '%s'", output_file); | ||
666 | |||
667 | return ret; | ||
668 | } | ||
669 | |||
670 | int bigendian(void) | ||
671 | { | ||
672 | unsigned char str[] = { 0x1, 0x2, 0x3, 0x4 }; | ||
673 | unsigned int *ptr; | ||
674 | |||
675 | ptr = (unsigned int *)str; | ||
676 | return *ptr == 0x01020304; | ||
677 | } | ||
678 | |||
679 | static unsigned long long copy_file_fd(int fd) | ||
680 | { | ||
681 | unsigned long long size = 0; | ||
682 | char buf[BUFSIZ]; | ||
683 | int r; | ||
684 | |||
685 | do { | ||
686 | r = read(fd, buf, BUFSIZ); | ||
687 | if (r > 0) { | ||
688 | size += r; | ||
689 | write_or_die(buf, r); | ||
690 | } | ||
691 | } while (r > 0); | ||
692 | |||
693 | return size; | ||
694 | } | ||
695 | |||
696 | static unsigned long long copy_file(const char *file) | ||
697 | { | 597 | { |
698 | unsigned long long size = 0; | 598 | struct tracecmd_output *handle; |
699 | int fd; | 599 | char **temp_files; |
700 | |||
701 | fd = open(file, O_RDONLY); | ||
702 | if (fd < 0) | ||
703 | die("Can't read '%s'", file); | ||
704 | size = copy_file_fd(fd); | ||
705 | close(fd); | ||
706 | |||
707 | return size; | ||
708 | } | ||
709 | |||
710 | static unsigned long get_size_fd(int fd) | ||
711 | { | ||
712 | unsigned long long size = 0; | ||
713 | char buf[BUFSIZ]; | ||
714 | int r; | ||
715 | |||
716 | do { | ||
717 | r = read(fd, buf, BUFSIZ); | ||
718 | if (r > 0) | ||
719 | size += r; | ||
720 | } while (r > 0); | ||
721 | |||
722 | lseek(fd, 0, SEEK_SET); | ||
723 | |||
724 | return size; | ||
725 | } | ||
726 | |||
727 | static unsigned long get_size(const char *file) | ||
728 | { | ||
729 | unsigned long long size = 0; | ||
730 | int fd; | ||
731 | |||
732 | fd = open(file, O_RDONLY); | ||
733 | if (fd < 0) | ||
734 | die("Can't read '%s'", file); | ||
735 | size = get_size_fd(fd); | ||
736 | close(fd); | ||
737 | |||
738 | return size; | ||
739 | } | ||
740 | |||
741 | static void read_header_files(void) | ||
742 | { | ||
743 | unsigned long long size, check_size; | ||
744 | struct stat st; | ||
745 | char *path; | ||
746 | int fd; | ||
747 | int ret; | ||
748 | |||
749 | path = get_tracing_file("events/header_page"); | ||
750 | |||
751 | ret = stat(path, &st); | ||
752 | if (ret < 0) { | ||
753 | /* old style did not show this info, just add zero */ | ||
754 | put_tracing_file(path); | ||
755 | write_or_die("header_page", 12); | ||
756 | size = 0; | ||
757 | write_or_die(&size, 8); | ||
758 | write_or_die("header_event", 13); | ||
759 | write_or_die(&size, 8); | ||
760 | return; | ||
761 | } | ||
762 | |||
763 | fd = open(path, O_RDONLY); | ||
764 | if (fd < 0) | ||
765 | die("can't read '%s'", path); | ||
766 | |||
767 | /* unfortunately, you can not stat debugfs files for size */ | ||
768 | size = get_size_fd(fd); | ||
769 | |||
770 | write_or_die("header_page", 12); | ||
771 | write_or_die(&size, 8); | ||
772 | check_size = copy_file_fd(fd); | ||
773 | if (size != check_size) | ||
774 | die("wrong size for '%s' size=%lld read=%lld", | ||
775 | path, size, check_size); | ||
776 | put_tracing_file(path); | ||
777 | |||
778 | path = get_tracing_file("events/header_event"); | ||
779 | fd = open(path, O_RDONLY); | ||
780 | if (fd < 0) | ||
781 | die("can't read '%s'", path); | ||
782 | |||
783 | size = get_size_fd(fd); | ||
784 | |||
785 | write_or_die("header_event", 13); | ||
786 | write_or_die(&size, 8); | ||
787 | check_size = copy_file_fd(fd); | ||
788 | if (size != check_size) | ||
789 | die("wrong size for '%s'", path); | ||
790 | put_tracing_file(path); | ||
791 | } | ||
792 | |||
793 | static void copy_event_system(const char *sys) | ||
794 | { | ||
795 | unsigned long long size, check_size; | ||
796 | struct dirent *dent; | ||
797 | struct stat st; | ||
798 | char *format; | ||
799 | DIR *dir; | ||
800 | int count = 0; | ||
801 | int ret; | ||
802 | |||
803 | dir = opendir(sys); | ||
804 | if (!dir) | ||
805 | die("can't read directory '%s'", sys); | ||
806 | |||
807 | while ((dent = readdir(dir))) { | ||
808 | if (strcmp(dent->d_name, ".") == 0 || | ||
809 | strcmp(dent->d_name, "..") == 0) | ||
810 | continue; | ||
811 | format = malloc_or_die(strlen(sys) + strlen(dent->d_name) + 10); | ||
812 | sprintf(format, "%s/%s/format", sys, dent->d_name); | ||
813 | ret = stat(format, &st); | ||
814 | free(format); | ||
815 | if (ret < 0) | ||
816 | continue; | ||
817 | count++; | ||
818 | } | ||
819 | |||
820 | write_or_die(&count, 4); | ||
821 | |||
822 | rewinddir(dir); | ||
823 | while ((dent = readdir(dir))) { | ||
824 | if (strcmp(dent->d_name, ".") == 0 || | ||
825 | strcmp(dent->d_name, "..") == 0) | ||
826 | continue; | ||
827 | format = malloc_or_die(strlen(sys) + strlen(dent->d_name) + 10); | ||
828 | sprintf(format, "%s/%s/format", sys, dent->d_name); | ||
829 | ret = stat(format, &st); | ||
830 | |||
831 | if (ret >= 0) { | ||
832 | /* unfortunately, you can not stat debugfs files for size */ | ||
833 | size = get_size(format); | ||
834 | write_or_die(&size, 8); | ||
835 | check_size = copy_file(format); | ||
836 | if (size != check_size) | ||
837 | die("error in size of file '%s'", format); | ||
838 | } | ||
839 | |||
840 | free(format); | ||
841 | } | ||
842 | } | ||
843 | |||
844 | static void read_ftrace_files(void) | ||
845 | { | ||
846 | char *path; | ||
847 | |||
848 | path = get_tracing_file("events/ftrace"); | ||
849 | |||
850 | copy_event_system(path); | ||
851 | |||
852 | put_tracing_file(path); | ||
853 | } | ||
854 | |||
855 | static void read_event_files(void) | ||
856 | { | ||
857 | struct dirent *dent; | ||
858 | struct stat st; | ||
859 | char *path; | ||
860 | char *sys; | ||
861 | DIR *dir; | ||
862 | int count = 0; | ||
863 | int ret; | ||
864 | |||
865 | path = get_tracing_file("events"); | ||
866 | |||
867 | dir = opendir(path); | ||
868 | if (!dir) | ||
869 | die("can't read directory '%s'", path); | ||
870 | |||
871 | while ((dent = readdir(dir))) { | ||
872 | if (strcmp(dent->d_name, ".") == 0 || | ||
873 | strcmp(dent->d_name, "..") == 0 || | ||
874 | strcmp(dent->d_name, "ftrace") == 0) | ||
875 | continue; | ||
876 | sys = malloc_or_die(strlen(path) + strlen(dent->d_name) + 2); | ||
877 | sprintf(sys, "%s/%s", path, dent->d_name); | ||
878 | ret = stat(sys, &st); | ||
879 | free(sys); | ||
880 | if (ret < 0) | ||
881 | continue; | ||
882 | if (S_ISDIR(st.st_mode)) | ||
883 | count++; | ||
884 | } | ||
885 | |||
886 | write_or_die(&count, 4); | ||
887 | |||
888 | rewinddir(dir); | ||
889 | while ((dent = readdir(dir))) { | ||
890 | if (strcmp(dent->d_name, ".") == 0 || | ||
891 | strcmp(dent->d_name, "..") == 0 || | ||
892 | strcmp(dent->d_name, "ftrace") == 0) | ||
893 | continue; | ||
894 | sys = malloc_or_die(strlen(path) + strlen(dent->d_name) + 2); | ||
895 | sprintf(sys, "%s/%s", path, dent->d_name); | ||
896 | ret = stat(sys, &st); | ||
897 | if (ret >= 0) { | ||
898 | if (S_ISDIR(st.st_mode)) { | ||
899 | write_or_die(dent->d_name, strlen(dent->d_name) + 1); | ||
900 | copy_event_system(sys); | ||
901 | } | ||
902 | } | ||
903 | free(sys); | ||
904 | } | ||
905 | |||
906 | put_tracing_file(path); | ||
907 | } | ||
908 | |||
909 | static void read_proc_kallsyms(void) | ||
910 | { | ||
911 | unsigned int size, check_size; | ||
912 | const char *path = "/proc/kallsyms"; | ||
913 | struct stat st; | ||
914 | int ret; | ||
915 | |||
916 | ret = stat(path, &st); | ||
917 | if (ret < 0) { | ||
918 | /* not found */ | ||
919 | size = 0; | ||
920 | write_or_die(&size, 4); | ||
921 | return; | ||
922 | } | ||
923 | size = get_size(path); | ||
924 | write_or_die(&size, 4); | ||
925 | check_size = copy_file(path); | ||
926 | if (size != check_size) | ||
927 | die("error in size of file '%s'", path); | ||
928 | |||
929 | } | ||
930 | |||
931 | static void read_ftrace_printk(void) | ||
932 | { | ||
933 | unsigned int size, check_size; | ||
934 | const char *path; | ||
935 | struct stat st; | ||
936 | int ret; | ||
937 | |||
938 | path = get_tracing_file("printk_formats"); | ||
939 | ret = stat(path, &st); | ||
940 | if (ret < 0) { | ||
941 | /* not found */ | ||
942 | size = 0; | ||
943 | write_or_die(&size, 4); | ||
944 | return; | ||
945 | } | ||
946 | size = get_size(path); | ||
947 | write_or_die(&size, 4); | ||
948 | check_size = copy_file(path); | ||
949 | if (size != check_size) | ||
950 | die("error in size of file '%s'", path); | ||
951 | |||
952 | } | ||
953 | |||
954 | static void read_tracing_data(void) | ||
955 | { | ||
956 | char buf[BUFSIZ]; | ||
957 | |||
958 | output_fd = open(output_file, O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, 0644); | ||
959 | if (output_fd < 0) | ||
960 | die("creating file '%s'", output_file); | ||
961 | |||
962 | buf[0] = 23; | ||
963 | buf[1] = 8; | ||
964 | buf[2] = 68; | ||
965 | memcpy(buf + 3, "tracing", 7); | ||
966 | |||
967 | write_or_die(buf, 10); | ||
968 | |||
969 | write_or_die(VERSION, strlen(VERSION) + 1); | ||
970 | |||
971 | /* save endian */ | ||
972 | if (bigendian()) | ||
973 | buf[0] = 1; | ||
974 | else | ||
975 | buf[0] = 0; | ||
976 | |||
977 | write_or_die(buf, 1); | ||
978 | |||
979 | /* save size of long */ | ||
980 | buf[0] = sizeof(long); | ||
981 | write_or_die(buf, 1); | ||
982 | |||
983 | /* save page_size */ | ||
984 | page_size = getpagesize(); | ||
985 | write_or_die(&page_size, 4); | ||
986 | |||
987 | read_header_files(); | ||
988 | read_ftrace_files(); | ||
989 | read_event_files(); | ||
990 | read_proc_kallsyms(); | ||
991 | read_ftrace_printk(); | ||
992 | } | ||
993 | |||
994 | static unsigned long long read_thread_file(int cpu) | ||
995 | { | ||
996 | unsigned long long size; | ||
997 | char *file; | ||
998 | |||
999 | file = malloc_or_die(strlen(output_file) + 20); | ||
1000 | snprintf(file, MAX_PATH, "%s.cpu%d", output_file, cpu); | ||
1001 | |||
1002 | size = copy_file(file); | ||
1003 | free(file); | ||
1004 | return size; | ||
1005 | } | ||
1006 | |||
1007 | static void read_trace_data(void) | ||
1008 | { | ||
1009 | char *path; | ||
1010 | |||
1011 | write_or_die("latency ", 10); | ||
1012 | |||
1013 | path = get_tracing_file("trace"); | ||
1014 | |||
1015 | copy_file(path); | ||
1016 | |||
1017 | put_tracing_file(path); | ||
1018 | } | ||
1019 | |||
1020 | static void read_thread_data(void) | ||
1021 | { | ||
1022 | unsigned long long offset, check_size; | ||
1023 | unsigned long long *offsets; | ||
1024 | unsigned long long *sizes; | ||
1025 | unsigned long long size; | ||
1026 | long long ret; | ||
1027 | struct stat st; | ||
1028 | char *file; | ||
1029 | int i; | 600 | int i; |
1030 | |||
1031 | if (!cpu_count) | ||
1032 | return; | ||
1033 | |||
1034 | /* | ||
1035 | * Save the command lines; | ||
1036 | */ | ||
1037 | file = get_tracing_file("saved_cmdlines"); | ||
1038 | ret = stat(file, &st); | ||
1039 | if (ret >= 0) { | ||
1040 | size = get_size(file); | ||
1041 | write_or_die(&size, 8); | ||
1042 | check_size = copy_file(file); | ||
1043 | if (size != check_size) | ||
1044 | die("error in size of file '%s'", file); | ||
1045 | } else { | ||
1046 | size = 0; | ||
1047 | write_or_die(&size, 8); | ||
1048 | } | ||
1049 | put_tracing_file(file); | ||
1050 | |||
1051 | write_or_die(&cpu_count, 4); | ||
1052 | |||
1053 | if (latency) { | ||
1054 | read_trace_data(); | ||
1055 | return; | ||
1056 | } | ||
1057 | 601 | ||
1058 | write_or_die("flyrecord", 10); | 602 | if (latency) |
603 | handle = tracecmd_create_file_latency(output_file, cpu_count); | ||
604 | else { | ||
605 | if (!cpu_count) | ||
606 | return; | ||
1059 | 607 | ||
1060 | offsets = malloc_or_die(sizeof(*offsets) * cpu_count); | 608 | temp_files = malloc_or_die(sizeof(*temp_files) * cpu_count); |
1061 | sizes = malloc_or_die(sizeof(*sizes) * cpu_count); | ||
1062 | 609 | ||
1063 | offset = lseek(output_fd, 0, SEEK_CUR); | 610 | for (i = 0; i < cpu_count; i++) |
611 | temp_files[i] = get_temp_file(i); | ||
1064 | 612 | ||
1065 | /* hold any extra data for data */ | 613 | handle = tracecmd_create_file(output_file, cpu_count, temp_files); |
1066 | offset += cpu_count * (16); | ||
1067 | offset = (offset + (page_size - 1)) & ~(PAGE_MASK); | ||
1068 | 614 | ||
1069 | for (i = 0; i < cpu_count; i++) { | 615 | for (i = 0; i < cpu_count; i++) |
1070 | file = malloc_or_die(strlen(output_file) + 20); | 616 | put_temp_file(temp_files[i]); |
1071 | sprintf(file, "%s.cpu%d", output_file, i); | 617 | free(temp_files); |
1072 | ret = stat(file, &st); | ||
1073 | if (ret < 0) | ||
1074 | die("can not stat '%s'", file); | ||
1075 | free(file); | ||
1076 | offsets[i] = offset; | ||
1077 | sizes[i] = st.st_size; | ||
1078 | offset += st.st_size; | ||
1079 | offset = (offset + (page_size - 1)) & ~(PAGE_MASK); | ||
1080 | |||
1081 | write_or_die(&offsets[i], 8); | ||
1082 | write_or_die(&sizes[i], 8); | ||
1083 | } | ||
1084 | |||
1085 | for (i = 0; i < cpu_count; i++) { | ||
1086 | fprintf(stderr, "offset=%llx\n", offsets[i]); | ||
1087 | ret = lseek64(output_fd, offsets[i], SEEK_SET); | ||
1088 | if (ret < 0) | ||
1089 | die("could not seek to %lld\n", offsets[i]); | ||
1090 | check_size = read_thread_file(i); | ||
1091 | if (check_size != sizes[i]) | ||
1092 | die("did not match size of %lld to %lld", | ||
1093 | check_size, sizes[i]); | ||
1094 | } | 618 | } |
619 | if (!handle) | ||
620 | die("could not write to file"); | ||
621 | tracecmd_output_close(handle); | ||
1095 | } | 622 | } |
1096 | 623 | ||
1097 | void usage(char **argv) | 624 | void usage(char **argv) |
@@ -1137,7 +664,7 @@ void usage(char **argv) | |||
1137 | " -e list available events\n" | 664 | " -e list available events\n" |
1138 | " -p list available plugins\n" | 665 | " -p list available plugins\n" |
1139 | " -o list available options\n" | 666 | " -o list available options\n" |
1140 | "\n", p, VERSION, p, p, p, p, p, p); | 667 | "\n", p, TRACECMD_VERSION, p, p, p, p, p, p); |
1141 | exit(-1); | 668 | exit(-1); |
1142 | } | 669 | } |
1143 | 670 | ||
@@ -1268,15 +795,8 @@ int main (int argc, char **argv) | |||
1268 | if (!events && !plugin) | 795 | if (!events && !plugin) |
1269 | die("no event or plugin was specified... aborting"); | 796 | die("no event or plugin was specified... aborting"); |
1270 | 797 | ||
1271 | if (record) { | 798 | if (output) |
1272 | if (output) | 799 | output_file = output; |
1273 | output_file = output; | ||
1274 | |||
1275 | read_tracing_data(); | ||
1276 | |||
1277 | start_threads(); | ||
1278 | signal(SIGINT, finish); | ||
1279 | } | ||
1280 | 800 | ||
1281 | fset = set_ftrace(!disable); | 801 | fset = set_ftrace(!disable); |
1282 | disable_all(); | 802 | disable_all(); |
@@ -1294,9 +814,6 @@ int main (int argc, char **argv) | |||
1294 | strcmp(plugin, "wakeup") == 0 || | 814 | strcmp(plugin, "wakeup") == 0 || |
1295 | strcmp(plugin, "wakeup_rt") == 0) { | 815 | strcmp(plugin, "wakeup_rt") == 0) { |
1296 | latency = 1; | 816 | latency = 1; |
1297 | if (record) | ||
1298 | stop_threads(); | ||
1299 | reset_max_latency(); | ||
1300 | } | 817 | } |
1301 | if (fset < 0 && (strcmp(plugin, "function") == 0 || | 818 | if (fset < 0 && (strcmp(plugin, "function") == 0 || |
1302 | strcmp(plugin, "function_graph") == 0)) | 819 | strcmp(plugin, "function_graph") == 0)) |
@@ -1304,7 +821,15 @@ int main (int argc, char **argv) | |||
1304 | set_plugin(plugin); | 821 | set_plugin(plugin); |
1305 | } | 822 | } |
1306 | 823 | ||
824 | if (record) { | ||
825 | if (!latency) | ||
826 | start_threads(); | ||
827 | signal(SIGINT, finish); | ||
828 | } | ||
829 | |||
1307 | enable_tracing(); | 830 | enable_tracing(); |
831 | if (latency) | ||
832 | reset_max_latency(); | ||
1308 | 833 | ||
1309 | if (!record) | 834 | if (!record) |
1310 | exit(0); | 835 | exit(0); |
@@ -1316,14 +841,13 @@ int main (int argc, char **argv) | |||
1316 | printf("Hit Ctrl^C to stop recording\n"); | 841 | printf("Hit Ctrl^C to stop recording\n"); |
1317 | while (!finished) | 842 | while (!finished) |
1318 | sleep(10); | 843 | sleep(10); |
1319 | |||
1320 | } | 844 | } |
1321 | 845 | ||
1322 | disable_tracing(); | 846 | disable_tracing(); |
1323 | 847 | ||
1324 | stop_threads(); | 848 | stop_threads(); |
1325 | 849 | ||
1326 | read_thread_data(); | 850 | record_data(); |
1327 | delete_thread_data(); | 851 | delete_thread_data(); |
1328 | 852 | ||
1329 | exit(0); | 853 | exit(0); |
diff --git a/trace-cmd.h b/trace-cmd.h index cc63627..9fbd475 100644 --- a/trace-cmd.h +++ b/trace-cmd.h | |||
@@ -10,6 +10,8 @@ extern const char *input_file; | |||
10 | #define PAGE_MASK (page_size - 1) | 10 | #define PAGE_MASK (page_size - 1) |
11 | #endif | 11 | #endif |
12 | 12 | ||
13 | #define TRACECMD_VERSION "0.5" | ||
14 | |||
13 | void parse_cmdlines(struct pevent *pevent, char *file, int size); | 15 | void parse_cmdlines(struct pevent *pevent, char *file, int size); |
14 | void parse_proc_kallsyms(struct pevent *pevent, char *file, unsigned int size); | 16 | void parse_proc_kallsyms(struct pevent *pevent, char *file, unsigned int size); |
15 | void parse_ftrace_printk(char *file, unsigned int size); | 17 | void parse_ftrace_printk(char *file, unsigned int size); |
@@ -35,6 +37,21 @@ struct record { | |||
35 | }; | 37 | }; |
36 | 38 | ||
37 | struct tracecmd_input; | 39 | struct tracecmd_input; |
40 | struct tracecmd_output; | ||
41 | struct tracecmd_recorder; | ||
42 | |||
43 | static inline int tracecmd_host_bigendian(void) | ||
44 | { | ||
45 | unsigned char str[] = { 0x1, 0x2, 0x3, 0x4 }; | ||
46 | unsigned int *ptr; | ||
47 | |||
48 | ptr = (unsigned int *)str; | ||
49 | return *ptr == 0x01020304; | ||
50 | } | ||
51 | |||
52 | char *tracecmd_find_tracing_dir(void); | ||
53 | |||
54 | /* --- Opening and Reading the trace.dat file --- */ | ||
38 | 55 | ||
39 | struct tracecmd_input *tracecmd_open(int fd); | 56 | struct tracecmd_input *tracecmd_open(int fd); |
40 | int tracecmd_read_headers(struct tracecmd_input *handle); | 57 | int tracecmd_read_headers(struct tracecmd_input *handle); |
@@ -68,4 +85,20 @@ struct pevent *tracecmd_get_pevent(struct tracecmd_input *handle); | |||
68 | /* hack for function graph work around */ | 85 | /* hack for function graph work around */ |
69 | extern __thread struct tracecmd_input *tracecmd_curr_thread_handle; | 86 | extern __thread struct tracecmd_input *tracecmd_curr_thread_handle; |
70 | 87 | ||
88 | |||
89 | /* --- Creating and Writing the trace.dat file --- */ | ||
90 | |||
91 | struct tracecmd_output *tracecmd_create_file_latency(const char *output_file, int cpus); | ||
92 | struct tracecmd_output *tracecmd_create_file(const char *output_file, | ||
93 | int cpus, char * const *cpu_data_files); | ||
94 | void tracecmd_output_close(struct tracecmd_output *handle); | ||
95 | |||
96 | /* --- Reading the Fly Recorder Trace --- */ | ||
97 | |||
98 | void tracecmd_free_recorder(struct tracecmd_recorder *recorder); | ||
99 | struct tracecmd_recorder *tracecmd_create_recorder(const char *file, int cpu); | ||
100 | int tracecmd_start_recording(struct tracecmd_recorder *recorder); | ||
101 | void tracecmd_stop_recording(struct tracecmd_recorder *recorder); | ||
102 | |||
103 | |||
71 | #endif /* _TRACE_CMD_H */ | 104 | #endif /* _TRACE_CMD_H */ |
diff --git a/trace-input.c b/trace-input.c index 9e61d20..f37fb6a 100644 --- a/trace-input.c +++ b/trace-input.c | |||
@@ -1197,7 +1197,7 @@ struct tracecmd_input *tracecmd_open(int fd) | |||
1197 | goto failed_read; | 1197 | goto failed_read; |
1198 | 1198 | ||
1199 | handle->pevent->file_bigendian = buf[0]; | 1199 | handle->pevent->file_bigendian = buf[0]; |
1200 | handle->pevent->host_bigendian = bigendian(); | 1200 | handle->pevent->host_bigendian = tracecmd_host_bigendian(); |
1201 | 1201 | ||
1202 | do_read_check(handle, buf, 1); | 1202 | do_read_check(handle, buf, 1); |
1203 | handle->long_size = buf[0]; | 1203 | handle->long_size = buf[0]; |
diff --git a/trace-output.c b/trace-output.c new file mode 100644 index 0000000..8f21fe4 --- /dev/null +++ b/trace-output.c | |||
@@ -0,0 +1,700 @@ | |||
1 | #define _LARGEFILE64_SOURCE | ||
2 | #define _GNU_SOURCE | ||
3 | #include <dirent.h> | ||
4 | #include <stdio.h> | ||
5 | #include <stdlib.h> | ||
6 | #include <string.h> | ||
7 | #include <getopt.h> | ||
8 | #include <stdarg.h> | ||
9 | #include <sys/types.h> | ||
10 | #include <sys/stat.h> | ||
11 | #include <sys/wait.h> | ||
12 | #include <sys/mman.h> | ||
13 | #include <pthread.h> | ||
14 | #include <fcntl.h> | ||
15 | #include <unistd.h> | ||
16 | #include <ctype.h> | ||
17 | #include <errno.h> | ||
18 | |||
19 | #include "trace-cmd.h" | ||
20 | |||
21 | struct tracecmd_output { | ||
22 | int fd; | ||
23 | int page_size; | ||
24 | int cpus; | ||
25 | char *tracing_dir; | ||
26 | }; | ||
27 | |||
28 | static int do_write(struct tracecmd_output *handle, void *data, int size) | ||
29 | { | ||
30 | int tot = 0; | ||
31 | int w; | ||
32 | |||
33 | do { | ||
34 | w = write(handle->fd, data, size - tot); | ||
35 | tot += w; | ||
36 | |||
37 | if (!w) | ||
38 | break; | ||
39 | if (w < 0) | ||
40 | return w; | ||
41 | } while (tot != size); | ||
42 | |||
43 | return tot; | ||
44 | } | ||
45 | |||
46 | static int | ||
47 | do_write_check(struct tracecmd_output *handle, void *data, int size) | ||
48 | { | ||
49 | int ret; | ||
50 | |||
51 | ret = do_write(handle, data, size); | ||
52 | if (ret < 0) | ||
53 | return ret; | ||
54 | if (ret != size) | ||
55 | return -1; | ||
56 | |||
57 | return 0; | ||
58 | } | ||
59 | |||
60 | void tracecmd_output_close(struct tracecmd_output *handle) | ||
61 | { | ||
62 | if (!handle) | ||
63 | return; | ||
64 | |||
65 | if (handle->fd >= 0) { | ||
66 | close(handle->fd); | ||
67 | handle->fd = -1; | ||
68 | } | ||
69 | |||
70 | if (handle->tracing_dir) | ||
71 | free(handle->tracing_dir); | ||
72 | |||
73 | free(handle); | ||
74 | } | ||
75 | |||
76 | static unsigned long get_size_fd(int fd) | ||
77 | { | ||
78 | unsigned long long size = 0; | ||
79 | char buf[BUFSIZ]; | ||
80 | int r; | ||
81 | |||
82 | do { | ||
83 | r = read(fd, buf, BUFSIZ); | ||
84 | if (r > 0) | ||
85 | size += r; | ||
86 | } while (r > 0); | ||
87 | |||
88 | lseek(fd, 0, SEEK_SET); | ||
89 | |||
90 | return size; | ||
91 | } | ||
92 | |||
93 | static unsigned long get_size(const char *file) | ||
94 | { | ||
95 | unsigned long long size = 0; | ||
96 | int fd; | ||
97 | |||
98 | fd = open(file, O_RDONLY); | ||
99 | if (fd < 0) | ||
100 | die("Can't read '%s'", file); | ||
101 | size = get_size_fd(fd); | ||
102 | close(fd); | ||
103 | |||
104 | return size; | ||
105 | } | ||
106 | |||
107 | static unsigned long long copy_file_fd(struct tracecmd_output *handle, int fd) | ||
108 | { | ||
109 | unsigned long long size = 0; | ||
110 | char buf[BUFSIZ]; | ||
111 | int r; | ||
112 | |||
113 | do { | ||
114 | r = read(fd, buf, BUFSIZ); | ||
115 | if (r > 0) { | ||
116 | size += r; | ||
117 | if (do_write_check(handle, buf, r)) | ||
118 | return 0; | ||
119 | } | ||
120 | } while (r > 0); | ||
121 | |||
122 | return size; | ||
123 | } | ||
124 | |||
125 | static unsigned long long copy_file(struct tracecmd_output *handle, | ||
126 | const char *file) | ||
127 | { | ||
128 | unsigned long long size = 0; | ||
129 | int fd; | ||
130 | |||
131 | fd = open(file, O_RDONLY); | ||
132 | if (fd < 0) | ||
133 | die("Can't read '%s'", file); | ||
134 | size = copy_file_fd(handle, fd); | ||
135 | close(fd); | ||
136 | |||
137 | return size; | ||
138 | } | ||
139 | |||
140 | /* | ||
141 | * Finds the path to the debugfs/tracing | ||
142 | * Allocates the string and stores it. | ||
143 | */ | ||
144 | static const char *find_tracing_dir(struct tracecmd_output *handle) | ||
145 | { | ||
146 | if (!handle->tracing_dir) | ||
147 | handle->tracing_dir = tracecmd_find_tracing_dir(); | ||
148 | |||
149 | return handle->tracing_dir; | ||
150 | } | ||
151 | |||
152 | static char *get_tracing_file(struct tracecmd_output *handle, const char *name) | ||
153 | { | ||
154 | const char *tracing; | ||
155 | char *file; | ||
156 | |||
157 | tracing = find_tracing_dir(handle); | ||
158 | if (!tracing) | ||
159 | return NULL; | ||
160 | |||
161 | file = malloc_or_die(strlen(tracing) + strlen(name) + 2); | ||
162 | if (!file) | ||
163 | return NULL; | ||
164 | |||
165 | sprintf(file, "%s/%s", tracing, name); | ||
166 | return file; | ||
167 | } | ||
168 | |||
169 | static void put_tracing_file(char *file) | ||
170 | { | ||
171 | free(file); | ||
172 | } | ||
173 | |||
174 | int tracecmd_ftrace_enable(int set) | ||
175 | { | ||
176 | struct stat buf; | ||
177 | char *path = "/proc/sys/kernel/ftrace_enabled"; | ||
178 | int fd; | ||
179 | char *val = set ? "1" : "0"; | ||
180 | int ret = 0; | ||
181 | |||
182 | /* if ftace_enable does not exist, simply ignore it */ | ||
183 | fd = stat(path, &buf); | ||
184 | if (fd < 0) | ||
185 | return ENODEV; | ||
186 | |||
187 | fd = open(path, O_WRONLY); | ||
188 | if (fd < 0) | ||
189 | die ("Can't %s ftrace", set ? "enable" : "disable"); | ||
190 | |||
191 | if (write(fd, val, 1) < 0) | ||
192 | ret = -1; | ||
193 | close(fd); | ||
194 | |||
195 | return ret; | ||
196 | } | ||
197 | |||
198 | static int read_header_files(struct tracecmd_output *handle) | ||
199 | { | ||
200 | unsigned long long size, check_size; | ||
201 | struct stat st; | ||
202 | char *path; | ||
203 | int fd; | ||
204 | int ret; | ||
205 | |||
206 | path = get_tracing_file(handle, "events/header_page"); | ||
207 | if (!path) | ||
208 | return -1; | ||
209 | |||
210 | ret = stat(path, &st); | ||
211 | if (ret < 0) { | ||
212 | /* old style did not show this info, just add zero */ | ||
213 | put_tracing_file(path); | ||
214 | if (do_write_check(handle, "header_page", 12)) | ||
215 | return -1; | ||
216 | size = 0; | ||
217 | if (do_write_check(handle, &size, 8)) | ||
218 | return -1; | ||
219 | if (do_write_check(handle, "header_event", 13)) | ||
220 | return -1; | ||
221 | if (do_write_check(handle, &size, 8)) | ||
222 | return -1; | ||
223 | return 0; | ||
224 | } | ||
225 | |||
226 | fd = open(path, O_RDONLY); | ||
227 | if (fd < 0) { | ||
228 | warning("can't read '%s'", path); | ||
229 | return -1; | ||
230 | } | ||
231 | |||
232 | /* unfortunately, you can not stat debugfs files for size */ | ||
233 | size = get_size_fd(fd); | ||
234 | |||
235 | if (do_write_check(handle, "header_page", 12)) | ||
236 | goto out_close; | ||
237 | if (do_write_check(handle, &size, 8)) | ||
238 | goto out_close; | ||
239 | check_size = copy_file_fd(handle, fd); | ||
240 | close(fd); | ||
241 | if (size != check_size) { | ||
242 | warning("wrong size for '%s' size=%lld read=%lld", | ||
243 | path, size, check_size); | ||
244 | errno = EINVAL; | ||
245 | return -1; | ||
246 | } | ||
247 | put_tracing_file(path); | ||
248 | |||
249 | path = get_tracing_file(handle, "events/header_event"); | ||
250 | if (!path) | ||
251 | return -1; | ||
252 | |||
253 | fd = open(path, O_RDONLY); | ||
254 | if (fd < 0) | ||
255 | die("can't read '%s'", path); | ||
256 | |||
257 | size = get_size_fd(fd); | ||
258 | |||
259 | if (do_write_check(handle, "header_event", 13)) | ||
260 | goto out_close; | ||
261 | if (do_write_check(handle, &size, 8)) | ||
262 | goto out_close; | ||
263 | check_size = copy_file_fd(handle, fd); | ||
264 | close(fd); | ||
265 | if (size != check_size) { | ||
266 | warning("wrong size for '%s'", path); | ||
267 | return -1; | ||
268 | } | ||
269 | put_tracing_file(path); | ||
270 | return 0; | ||
271 | |||
272 | out_close: | ||
273 | close(fd); | ||
274 | return -1; | ||
275 | } | ||
276 | |||
277 | static int copy_event_system(struct tracecmd_output *handle, const char *sys) | ||
278 | { | ||
279 | unsigned long long size, check_size; | ||
280 | struct dirent *dent; | ||
281 | struct stat st; | ||
282 | char *format; | ||
283 | DIR *dir; | ||
284 | int count = 0; | ||
285 | int ret; | ||
286 | |||
287 | dir = opendir(sys); | ||
288 | if (!dir) { | ||
289 | warning("can't read directory '%s'", sys); | ||
290 | return -1; | ||
291 | } | ||
292 | |||
293 | while ((dent = readdir(dir))) { | ||
294 | if (strcmp(dent->d_name, ".") == 0 || | ||
295 | strcmp(dent->d_name, "..") == 0) | ||
296 | continue; | ||
297 | format = malloc_or_die(strlen(sys) + strlen(dent->d_name) + 10); | ||
298 | if (!format) | ||
299 | return -1; | ||
300 | sprintf(format, "%s/%s/format", sys, dent->d_name); | ||
301 | ret = stat(format, &st); | ||
302 | free(format); | ||
303 | if (ret < 0) | ||
304 | continue; | ||
305 | count++; | ||
306 | } | ||
307 | |||
308 | if (do_write_check(handle, &count, 4)) | ||
309 | return -1; | ||
310 | |||
311 | rewinddir(dir); | ||
312 | while ((dent = readdir(dir))) { | ||
313 | if (strcmp(dent->d_name, ".") == 0 || | ||
314 | strcmp(dent->d_name, "..") == 0) | ||
315 | continue; | ||
316 | format = malloc_or_die(strlen(sys) + strlen(dent->d_name) + 10); | ||
317 | if (!format) | ||
318 | return -1; | ||
319 | sprintf(format, "%s/%s/format", sys, dent->d_name); | ||
320 | ret = stat(format, &st); | ||
321 | |||
322 | if (ret >= 0) { | ||
323 | /* unfortunately, you can not stat debugfs files for size */ | ||
324 | size = get_size(format); | ||
325 | if (do_write_check(handle, &size, 8)) | ||
326 | goto out_free; | ||
327 | check_size = copy_file(handle, format); | ||
328 | if (size != check_size) { | ||
329 | warning("error in size of file '%s'", format); | ||
330 | goto out_free; | ||
331 | } | ||
332 | } | ||
333 | |||
334 | free(format); | ||
335 | } | ||
336 | |||
337 | return 0; | ||
338 | |||
339 | out_free: | ||
340 | free(format); | ||
341 | return -1; | ||
342 | } | ||
343 | |||
344 | static int read_ftrace_files(struct tracecmd_output *handle) | ||
345 | { | ||
346 | char *path; | ||
347 | int ret; | ||
348 | |||
349 | path = get_tracing_file(handle, "events/ftrace"); | ||
350 | if (!path) | ||
351 | return -1; | ||
352 | |||
353 | ret = copy_event_system(handle, path); | ||
354 | |||
355 | put_tracing_file(path); | ||
356 | |||
357 | return ret; | ||
358 | } | ||
359 | |||
360 | static int read_event_files(struct tracecmd_output *handle) | ||
361 | { | ||
362 | struct dirent *dent; | ||
363 | struct stat st; | ||
364 | char *path; | ||
365 | char *sys; | ||
366 | DIR *dir; | ||
367 | int count = 0; | ||
368 | int ret; | ||
369 | |||
370 | path = get_tracing_file(handle, "events"); | ||
371 | if (!path) | ||
372 | return -1; | ||
373 | |||
374 | dir = opendir(path); | ||
375 | if (!dir) | ||
376 | die("can't read directory '%s'", path); | ||
377 | |||
378 | while ((dent = readdir(dir))) { | ||
379 | if (strcmp(dent->d_name, ".") == 0 || | ||
380 | strcmp(dent->d_name, "..") == 0 || | ||
381 | strcmp(dent->d_name, "ftrace") == 0) | ||
382 | continue; | ||
383 | ret = -1; | ||
384 | sys = malloc_or_die(strlen(path) + strlen(dent->d_name) + 2); | ||
385 | if (!sys) | ||
386 | goto out_close_dir; | ||
387 | sprintf(sys, "%s/%s", path, dent->d_name); | ||
388 | ret = stat(sys, &st); | ||
389 | free(sys); | ||
390 | if (ret < 0) | ||
391 | continue; | ||
392 | if (S_ISDIR(st.st_mode)) | ||
393 | count++; | ||
394 | } | ||
395 | |||
396 | ret = -1; | ||
397 | if (do_write_check(handle, &count, 4)) | ||
398 | goto out_close_dir; | ||
399 | |||
400 | rewinddir(dir); | ||
401 | while ((dent = readdir(dir))) { | ||
402 | if (strcmp(dent->d_name, ".") == 0 || | ||
403 | strcmp(dent->d_name, "..") == 0 || | ||
404 | strcmp(dent->d_name, "ftrace") == 0) | ||
405 | continue; | ||
406 | ret = -1; | ||
407 | sys = malloc_or_die(strlen(path) + strlen(dent->d_name) + 2); | ||
408 | if (!sys) | ||
409 | goto out_close_dir; | ||
410 | |||
411 | sprintf(sys, "%s/%s", path, dent->d_name); | ||
412 | ret = stat(sys, &st); | ||
413 | if (ret >= 0) { | ||
414 | if (S_ISDIR(st.st_mode)) { | ||
415 | if (do_write_check(handle, dent->d_name, | ||
416 | strlen(dent->d_name) + 1)) { | ||
417 | free(sys); | ||
418 | ret = -1; | ||
419 | goto out_close_dir; | ||
420 | } | ||
421 | copy_event_system(handle, sys); | ||
422 | } | ||
423 | } | ||
424 | free(sys); | ||
425 | } | ||
426 | |||
427 | put_tracing_file(path); | ||
428 | |||
429 | ret = 0; | ||
430 | |||
431 | out_close_dir: | ||
432 | closedir(dir); | ||
433 | return ret; | ||
434 | } | ||
435 | |||
436 | static int read_proc_kallsyms(struct tracecmd_output *handle) | ||
437 | { | ||
438 | unsigned int size, check_size; | ||
439 | const char *path = "/proc/kallsyms"; | ||
440 | struct stat st; | ||
441 | int ret; | ||
442 | |||
443 | ret = stat(path, &st); | ||
444 | if (ret < 0) { | ||
445 | /* not found */ | ||
446 | size = 0; | ||
447 | if (do_write_check(handle, &size, 4)) | ||
448 | return -1; | ||
449 | return 0; | ||
450 | } | ||
451 | size = get_size(path); | ||
452 | if (do_write_check(handle, &size, 4)) | ||
453 | return -1; | ||
454 | check_size = copy_file(handle, path); | ||
455 | if (size != check_size) { | ||
456 | errno = EINVAL; | ||
457 | warning("error in size of file '%s'", path); | ||
458 | return -1; | ||
459 | } | ||
460 | |||
461 | return 0; | ||
462 | } | ||
463 | |||
464 | static int read_ftrace_printk(struct tracecmd_output *handle) | ||
465 | { | ||
466 | unsigned int size, check_size; | ||
467 | const char *path; | ||
468 | struct stat st; | ||
469 | int ret; | ||
470 | |||
471 | path = get_tracing_file(handle, "printk_formats"); | ||
472 | if (!path) | ||
473 | return -1; | ||
474 | |||
475 | ret = stat(path, &st); | ||
476 | if (ret < 0) { | ||
477 | /* not found */ | ||
478 | size = 0; | ||
479 | if (do_write_check(handle, &size, 4)) | ||
480 | return -1; | ||
481 | return 0; | ||
482 | } | ||
483 | size = get_size(path); | ||
484 | if (do_write_check(handle, &size, 4)) | ||
485 | return -1; | ||
486 | check_size = copy_file(handle, path); | ||
487 | if (size != check_size) { | ||
488 | errno = EINVAL; | ||
489 | warning("error in size of file '%s'", path); | ||
490 | return -1; | ||
491 | } | ||
492 | |||
493 | return 0; | ||
494 | } | ||
495 | |||
496 | static struct tracecmd_output *create_file(const char *output_file, int cpus) | ||
497 | { | ||
498 | struct tracecmd_output *handle; | ||
499 | char buf[BUFSIZ]; | ||
500 | char *file = NULL; | ||
501 | struct stat st; | ||
502 | off64_t check_size; | ||
503 | off64_t size; | ||
504 | int ret; | ||
505 | |||
506 | handle = malloc(sizeof(*handle)); | ||
507 | if (!handle) | ||
508 | return NULL; | ||
509 | memset(handle, 0, sizeof(*handle)); | ||
510 | |||
511 | handle->fd = open(output_file, O_RDWR | O_CREAT | O_TRUNC | O_LARGEFILE, 0644); | ||
512 | if (handle->fd < 0) | ||
513 | goto out_free; | ||
514 | |||
515 | buf[0] = 23; | ||
516 | buf[1] = 8; | ||
517 | buf[2] = 68; | ||
518 | memcpy(buf + 3, "tracing", 7); | ||
519 | |||
520 | if (do_write_check(handle, buf, 10)) | ||
521 | goto out_free; | ||
522 | |||
523 | if (do_write_check(handle, TRACECMD_VERSION, strlen(TRACECMD_VERSION) + 1)) | ||
524 | goto out_free; | ||
525 | |||
526 | /* save endian */ | ||
527 | if (tracecmd_host_bigendian()) | ||
528 | buf[0] = 1; | ||
529 | else | ||
530 | buf[0] = 0; | ||
531 | |||
532 | if (do_write_check(handle, buf, 1)) | ||
533 | goto out_free; | ||
534 | |||
535 | /* save size of long (this may not be what the kernel is) */ | ||
536 | buf[0] = sizeof(long); | ||
537 | if (do_write_check(handle, buf, 1)) | ||
538 | goto out_free; | ||
539 | |||
540 | /* save page_size */ | ||
541 | handle->page_size = getpagesize(); | ||
542 | if (do_write_check(handle, &handle->page_size, 4)) | ||
543 | goto out_free; | ||
544 | |||
545 | if (read_header_files(handle)) | ||
546 | goto out_free; | ||
547 | if (read_ftrace_files(handle)) | ||
548 | goto out_free; | ||
549 | if (read_event_files(handle)) | ||
550 | goto out_free; | ||
551 | if (read_proc_kallsyms(handle)) | ||
552 | goto out_free; | ||
553 | if (read_ftrace_printk(handle)) | ||
554 | goto out_free; | ||
555 | |||
556 | /* | ||
557 | * Save the command lines; | ||
558 | */ | ||
559 | file = get_tracing_file(handle, "saved_cmdlines"); | ||
560 | ret = stat(file, &st); | ||
561 | if (ret >= 0) { | ||
562 | size = get_size(file); | ||
563 | if (do_write_check(handle, &size, 8)) | ||
564 | goto out_free; | ||
565 | check_size = copy_file(handle, file); | ||
566 | if (size != check_size) { | ||
567 | errno = EINVAL; | ||
568 | warning("error in size of file '%s'", file); | ||
569 | goto out_free; | ||
570 | } | ||
571 | } else { | ||
572 | size = 0; | ||
573 | if (do_write_check(handle, &size, 8)) | ||
574 | goto out_free; | ||
575 | } | ||
576 | put_tracing_file(file); | ||
577 | file = NULL; | ||
578 | |||
579 | if (do_write_check(handle, &cpus, 4)) | ||
580 | goto out_free; | ||
581 | |||
582 | return handle; | ||
583 | |||
584 | out_free: | ||
585 | tracecmd_output_close(handle); | ||
586 | return NULL; | ||
587 | } | ||
588 | |||
589 | struct tracecmd_output *tracecmd_create_file_latency(const char *output_file, int cpus) | ||
590 | { | ||
591 | struct tracecmd_output *handle; | ||
592 | char *path; | ||
593 | |||
594 | handle = create_file(output_file, cpus); | ||
595 | if (!handle) | ||
596 | return NULL; | ||
597 | |||
598 | if (do_write_check(handle, "latency ", 10)) | ||
599 | goto out_free; | ||
600 | |||
601 | path = get_tracing_file(handle, "trace"); | ||
602 | if (!path) | ||
603 | goto out_free; | ||
604 | |||
605 | copy_file(handle, path); | ||
606 | |||
607 | put_tracing_file(path); | ||
608 | |||
609 | return handle; | ||
610 | |||
611 | out_free: | ||
612 | tracecmd_output_close(handle); | ||
613 | return NULL; | ||
614 | } | ||
615 | |||
616 | struct tracecmd_output *tracecmd_create_file(const char *output_file, | ||
617 | int cpus, char * const *cpu_data_files) | ||
618 | { | ||
619 | unsigned long long *offsets = NULL; | ||
620 | unsigned long long *sizes = NULL; | ||
621 | struct tracecmd_output *handle; | ||
622 | unsigned long long offset; | ||
623 | off64_t check_size; | ||
624 | char *file = NULL; | ||
625 | struct stat st; | ||
626 | int ret; | ||
627 | int i; | ||
628 | |||
629 | handle = create_file(output_file, cpus); | ||
630 | if (!handle) | ||
631 | return NULL; | ||
632 | |||
633 | if (do_write_check(handle, "flyrecord", 10)) | ||
634 | goto out_free; | ||
635 | |||
636 | offsets = malloc_or_die(sizeof(*offsets) * cpus); | ||
637 | if (!offsets) | ||
638 | goto out_free; | ||
639 | sizes = malloc_or_die(sizeof(*sizes) * cpus); | ||
640 | if (!sizes) | ||
641 | goto out_free; | ||
642 | |||
643 | offset = lseek(handle->fd, 0, SEEK_CUR); | ||
644 | |||
645 | /* hold any extra data for data */ | ||
646 | offset += cpus * (16); | ||
647 | offset = (offset + (handle->page_size - 1)) & ~(handle->page_size - 1); | ||
648 | |||
649 | for (i = 0; i < cpus; i++) { | ||
650 | file = malloc_or_die(strlen(output_file) + 20); | ||
651 | if (!file) | ||
652 | goto out_free; | ||
653 | sprintf(file, "%s.cpu%d", output_file, i); | ||
654 | ret = stat(file, &st); | ||
655 | if (ret < 0) { | ||
656 | warning("can not stat '%s'", file); | ||
657 | goto out_free; | ||
658 | } | ||
659 | free(file); | ||
660 | file = NULL; | ||
661 | offsets[i] = offset; | ||
662 | sizes[i] = st.st_size; | ||
663 | offset += st.st_size; | ||
664 | offset = (offset + (handle->page_size - 1)) & ~(handle->page_size - 1); | ||
665 | |||
666 | if (do_write_check(handle, &offsets[i], 8)) | ||
667 | goto out_free; | ||
668 | if (do_write_check(handle, &sizes[i], 8)) | ||
669 | goto out_free; | ||
670 | } | ||
671 | |||
672 | for (i = 0; i < cpus; i++) { | ||
673 | fprintf(stderr, "offset=%llx\n", offsets[i]); | ||
674 | ret = lseek64(handle->fd, offsets[i], SEEK_SET); | ||
675 | if (ret < 0) { | ||
676 | warning("could not seek to %lld\n", offsets[i]); | ||
677 | goto out_free; | ||
678 | } | ||
679 | check_size = copy_file(handle, cpu_data_files[i]); | ||
680 | if (check_size != sizes[i]) { | ||
681 | errno = EINVAL; | ||
682 | warning("did not match size of %lld to %lld", | ||
683 | check_size, sizes[i]); | ||
684 | goto out_free; | ||
685 | } | ||
686 | } | ||
687 | |||
688 | free(offsets); | ||
689 | free(sizes); | ||
690 | |||
691 | return handle; | ||
692 | |||
693 | out_free: | ||
694 | free(file); | ||
695 | free(offsets); | ||
696 | free(sizes); | ||
697 | |||
698 | tracecmd_output_close(handle); | ||
699 | return NULL; | ||
700 | } | ||
diff --git a/trace-record.c b/trace-record.c new file mode 100644 index 0000000..0cd81ac --- /dev/null +++ b/trace-record.c | |||
@@ -0,0 +1,133 @@ | |||
1 | #define _LARGEFILE64_SOURCE | ||
2 | #define _GNU_SOURCE | ||
3 | #include <dirent.h> | ||
4 | #include <stdio.h> | ||
5 | #include <stdlib.h> | ||
6 | #include <string.h> | ||
7 | #include <stdarg.h> | ||
8 | #include <sys/types.h> | ||
9 | #include <sys/stat.h> | ||
10 | #include <sys/wait.h> | ||
11 | #include <pthread.h> | ||
12 | #include <fcntl.h> | ||
13 | #include <unistd.h> | ||
14 | #include <ctype.h> | ||
15 | #include <errno.h> | ||
16 | |||
17 | #include "trace-cmd.h" | ||
18 | |||
19 | struct tracecmd_recorder { | ||
20 | int fd; | ||
21 | int trace_fd; | ||
22 | int brass[2]; | ||
23 | int page_size; | ||
24 | int cpu; | ||
25 | int stop; | ||
26 | }; | ||
27 | |||
28 | void tracecmd_free_recorder(struct tracecmd_recorder *recorder) | ||
29 | { | ||
30 | if (!recorder) | ||
31 | return; | ||
32 | |||
33 | if (recorder->fd) | ||
34 | close(recorder->fd); | ||
35 | |||
36 | free(recorder); | ||
37 | } | ||
38 | |||
39 | struct tracecmd_recorder *tracecmd_create_recorder(const char *file, int cpu) | ||
40 | { | ||
41 | struct tracecmd_recorder *recorder; | ||
42 | char *tracing = NULL; | ||
43 | char *path = NULL; | ||
44 | int ret; | ||
45 | |||
46 | recorder = malloc_or_die(sizeof(*recorder)); | ||
47 | if (!recorder) | ||
48 | return NULL; | ||
49 | |||
50 | recorder->cpu = cpu; | ||
51 | |||
52 | /* Init to know what to free and release */ | ||
53 | recorder->trace_fd = -1; | ||
54 | recorder->brass[0] = -1; | ||
55 | recorder->brass[1] = -1; | ||
56 | |||
57 | recorder->page_size = getpagesize(); | ||
58 | |||
59 | recorder->fd = open(file, O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, 0644); | ||
60 | if (recorder->fd < 0) | ||
61 | goto out_free; | ||
62 | |||
63 | tracing = tracecmd_find_tracing_dir(); | ||
64 | if (!tracing) { | ||
65 | errno = ENODEV; | ||
66 | goto out_free; | ||
67 | } | ||
68 | |||
69 | path = malloc_or_die(strlen(tracing) + 40); | ||
70 | if (!path) | ||
71 | goto out_free; | ||
72 | |||
73 | sprintf(path, "%s/per_cpu/cpu%d/trace_pipe_raw", tracing, cpu); | ||
74 | recorder->trace_fd = open(path, O_RDONLY); | ||
75 | if (recorder->trace_fd < 0) | ||
76 | goto out_free; | ||
77 | |||
78 | free(tracing); | ||
79 | free(path); | ||
80 | |||
81 | ret = pipe(recorder->brass); | ||
82 | if (ret < 0) | ||
83 | goto out_free; | ||
84 | |||
85 | return recorder; | ||
86 | |||
87 | out_free: | ||
88 | free(tracing); | ||
89 | free(path); | ||
90 | |||
91 | tracecmd_free_recorder(recorder); | ||
92 | return NULL; | ||
93 | } | ||
94 | |||
95 | int tracecmd_start_recording(struct tracecmd_recorder *recorder) | ||
96 | { | ||
97 | char *buf[recorder->page_size]; | ||
98 | int ret; | ||
99 | |||
100 | recorder->stop = 0; | ||
101 | |||
102 | do { | ||
103 | ret = splice(recorder->trace_fd, NULL, recorder->brass[1], NULL, | ||
104 | recorder->page_size, 1 /* SPLICE_F_MOVE */); | ||
105 | if (ret < 0) { | ||
106 | warning("recorder error in splice input"); | ||
107 | return -1; | ||
108 | } | ||
109 | ret = splice(recorder->brass[0], NULL, recorder->fd, NULL, | ||
110 | recorder->page_size, 3 /* and NON_BLOCK */); | ||
111 | if (ret < 0 && errno != EAGAIN) { | ||
112 | warning("recorder error in splice output"); | ||
113 | return -1; | ||
114 | } | ||
115 | } while (!recorder->stop); | ||
116 | |||
117 | /* splice only reads full pages */ | ||
118 | do { | ||
119 | ret = read(recorder->trace_fd, buf, recorder->page_size); | ||
120 | if (ret > 0) | ||
121 | write(recorder->fd, buf, ret); | ||
122 | } while (ret > 0); | ||
123 | |||
124 | return 0; | ||
125 | } | ||
126 | |||
127 | void tracecmd_stop_recording(struct tracecmd_recorder *recorder) | ||
128 | { | ||
129 | if (!recorder) | ||
130 | return; | ||
131 | |||
132 | recorder->stop = 1; | ||
133 | } | ||
diff --git a/trace-util.c b/trace-util.c index a3ee05c..c3d89c6 100644 --- a/trace-util.c +++ b/trace-util.c | |||
@@ -15,6 +15,13 @@ | |||
15 | 15 | ||
16 | #define __weak __attribute__((weak)) | 16 | #define __weak __attribute__((weak)) |
17 | 17 | ||
18 | #define _STR(x) #x | ||
19 | #define STR(x) _STR(x) | ||
20 | |||
21 | #ifndef MAX_PATH | ||
22 | # define MAX_PATH 1024 | ||
23 | #endif | ||
24 | |||
18 | void __weak die(char *fmt, ...) | 25 | void __weak die(char *fmt, ...) |
19 | { | 26 | { |
20 | va_list ap; | 27 | va_list ap; |
@@ -173,6 +180,41 @@ static int load_plugin(struct pevent *pevent, | |||
173 | return ret; | 180 | return ret; |
174 | } | 181 | } |
175 | 182 | ||
183 | char *tracecmd_find_tracing_dir(void) | ||
184 | { | ||
185 | char debugfs[MAX_PATH+1]; | ||
186 | char *tracing_dir; | ||
187 | char type[100]; | ||
188 | FILE *fp; | ||
189 | |||
190 | if ((fp = fopen("/proc/mounts","r")) == NULL) { | ||
191 | warning("Can't open /proc/mounts for read"); | ||
192 | return NULL; | ||
193 | } | ||
194 | |||
195 | while (fscanf(fp, "%*s %" | ||
196 | STR(MAX_PATH) | ||
197 | "s %99s %*s %*d %*d\n", | ||
198 | debugfs, type) == 2) { | ||
199 | if (strcmp(type, "debugfs") == 0) | ||
200 | break; | ||
201 | } | ||
202 | fclose(fp); | ||
203 | |||
204 | if (strcmp(type, "debugfs") != 0) { | ||
205 | warning("debugfs not mounted, please mount"); | ||
206 | return NULL; | ||
207 | } | ||
208 | |||
209 | tracing_dir = malloc_or_die(strlen(debugfs) + 9); | ||
210 | if (!tracing_dir) | ||
211 | return NULL; | ||
212 | |||
213 | sprintf(tracing_dir, "%s/tracing", debugfs); | ||
214 | |||
215 | return tracing_dir; | ||
216 | } | ||
217 | |||
176 | int trace_load_plugins(struct pevent *pevent) | 218 | int trace_load_plugins(struct pevent *pevent) |
177 | { | 219 | { |
178 | struct dirent *dent; | 220 | struct dirent *dent; |