aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteven Rostedt <srostedt@redhat.com>2010-01-08 14:24:49 -0500
committerSteven Rostedt <rostedt@goodmis.org>2010-01-13 14:30:20 -0500
commit87d2a344a4852a4041d12bcc06ac1d976a2ae798 (patch)
tree9d0a9e1afe67d2446b60cb9e927886f14c06ad89
parentc25df862b0a904d01bde0604c8165ba48b8172cd (diff)
trace-cmd: Add split feature
trace-cmd split [options] -o file [start [end]] -o output file to write to (file.1, file.2, etc) -s n split file up by n seconds -m n split file up by n milliseconds -u n split file up by n microseconds -e n split file up by n events -p n split file up by n pages -r repeat from start to end -c per cpu, that is -p 2 will be 2 pages for each CPU if option is specified, it will split the file up starting at start, and ending at end start - decimal start time in seconds (ex: 75678.923853) if left out, will start at beginning of file end - decimal end time in seconds Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
-rw-r--r--Makefile2
-rw-r--r--trace-cmd-local.h40
-rw-r--r--trace-cmd.c20
-rw-r--r--trace-cmd.h5
-rw-r--r--trace-input.c234
-rw-r--r--trace-local.h2
-rw-r--r--trace-output.c135
-rw-r--r--trace-split.c514
8 files changed, 894 insertions, 58 deletions
diff --git a/Makefile b/Makefile
index d64d1c8..d2dd7ed 100644
--- a/Makefile
+++ b/Makefile
@@ -140,7 +140,7 @@ endef
140 $(Q)$(call check_gui) 140 $(Q)$(call check_gui)
141 141
142 142
143TRACE_CMD_OBJS = trace-cmd.o trace-read.o 143TRACE_CMD_OBJS = trace-cmd.o trace-read.o trace-split.o
144TRACE_VIEW_OBJS = trace-view.o trace-view-store.o trace-filter.o trace-compat.o \ 144TRACE_VIEW_OBJS = trace-view.o trace-view-store.o trace-filter.o trace-compat.o \
145 trace-hash.o 145 trace-hash.o
146TRACE_GRAPH_OBJS = trace-graph.o trace-compat.o trace-hash.o trace-filter.o 146TRACE_GRAPH_OBJS = trace-graph.o trace-compat.o trace-hash.o trace-filter.o
diff --git a/trace-cmd-local.h b/trace-cmd-local.h
new file mode 100644
index 0000000..fa0959c
--- /dev/null
+++ b/trace-cmd-local.h
@@ -0,0 +1,40 @@
1#ifndef _TRACE_CMD_LOCAL_H
2#define _TRACE_CMD_LOCAL_H
3
4/* Local for trace-input.c and trace-output.c */
5
6#include "trace-cmd.h"
7
8static int __do_write(int fd, void *data, int size)
9{
10 int tot = 0;
11 int w;
12
13 do {
14 w = write(fd, data, size - tot);
15 tot += w;
16
17 if (!w)
18 break;
19 if (w < 0)
20 return w;
21 } while (tot != size);
22
23 return tot;
24}
25
26static int
27__do_write_check(int fd, void *data, int size)
28{
29 int ret;
30
31 ret = __do_write(fd, data, size);
32 if (ret < 0)
33 return ret;
34 if (ret != size)
35 return -1;
36
37 return 0;
38}
39
40#endif /* _TRACE_CMD_LOCAL_H */
diff --git a/trace-cmd.c b/trace-cmd.c
index 45d8568..8839e74 100644
--- a/trace-cmd.c
+++ b/trace-cmd.c
@@ -750,11 +750,26 @@ void usage(char **argv)
750 " -E show event files stored\n" 750 " -E show event files stored\n"
751 " -l show latency format (default with latency tracers)\n" 751 " -l show latency format (default with latency tracers)\n"
752 "\n" 752 "\n"
753 " %s split [options] -o file [start [end]]\n"
754 " -o output file to write to (file.1, file.2, etc)\n"
755 " -s n split file up by n seconds\n"
756 " -m n split file up by n milliseconds\n"
757 " -u n split file up by n microseconds\n"
758 " -e n split file up by n events\n"
759 " -p n split file up by n pages\n"
760 " -r repeat from start to end\n"
761 " -c per cpu, that is -p 2 will be 2 pages for each CPU\n"
762 " if option is specified, it will split the file\n"
763 " up starting at start, and ending at end\n"
764 " start - decimal start time in seconds (ex: 75678.923853)\n"
765 " if left out, will start at beginning of file\n"
766 " end - decimal end time in seconds\n"
767 "\n"
753 " %s list [-e][-p]\n" 768 " %s list [-e][-p]\n"
754 " -e list available events\n" 769 " -e list available events\n"
755 " -p list available plugins\n" 770 " -p list available plugins\n"
756 " -o list available options\n" 771 " -o list available options\n"
757 "\n", p, VERSION_STRING, p, p, p, p, p, p, p); 772 "\n", p, VERSION_STRING, p, p, p, p, p, p, p, p);
758 exit(-1); 773 exit(-1);
759} 774}
760 775
@@ -786,6 +801,9 @@ int main (int argc, char **argv)
786 if (strcmp(argv[1], "report") == 0) { 801 if (strcmp(argv[1], "report") == 0) {
787 trace_report(argc, argv); 802 trace_report(argc, argv);
788 exit(0); 803 exit(0);
804 } else if (strcmp(argv[1], "split") == 0) {
805 trace_split(argc, argv);
806 exit(0);
789 } else if ((record = (strcmp(argv[1], "record") == 0)) || 807 } else if ((record = (strcmp(argv[1], "record") == 0)) ||
790 (strcmp(argv[1], "start") == 0) || 808 (strcmp(argv[1], "start") == 0) ||
791 ((extract = strcmp(argv[1], "extract") == 0))) { 809 ((extract = strcmp(argv[1], "extract") == 0))) {
diff --git a/trace-cmd.h b/trace-cmd.h
index 13d913a..98b4dec 100644
--- a/trace-cmd.h
+++ b/trace-cmd.h
@@ -49,6 +49,7 @@ int tracecmd_read_headers(struct tracecmd_input *handle);
49int tracecmd_long_size(struct tracecmd_input *handle); 49int tracecmd_long_size(struct tracecmd_input *handle);
50int tracecmd_page_size(struct tracecmd_input *handle); 50int tracecmd_page_size(struct tracecmd_input *handle);
51int tracecmd_cpus(struct tracecmd_input *handle); 51int tracecmd_cpus(struct tracecmd_input *handle);
52int tracecmd_copy_headers(struct tracecmd_input *handle, int fd);
52 53
53void tracecmd_print_events(struct tracecmd_input *handle); 54void tracecmd_print_events(struct tracecmd_input *handle);
54 55
@@ -94,6 +95,10 @@ struct tracecmd_output *tracecmd_create_file_latency(const char *output_file, in
94struct tracecmd_output *tracecmd_create_file(const char *output_file, 95struct tracecmd_output *tracecmd_create_file(const char *output_file,
95 int cpus, char * const *cpu_data_files); 96 int cpus, char * const *cpu_data_files);
96void tracecmd_output_close(struct tracecmd_output *handle); 97void tracecmd_output_close(struct tracecmd_output *handle);
98struct tracecmd_output *tracecmd_copy(struct tracecmd_input *ihandle,
99 const char *file);
100int tracecmd_append_cpu_data(struct tracecmd_output *handle,
101 int cpus, char * const *cpu_data_files);
97 102
98/* --- Reading the Fly Recorder Trace --- */ 103/* --- Reading the Fly Recorder Trace --- */
99 104
diff --git a/trace-input.c b/trace-input.c
index ece0aab..0f2f7d8 100644
--- a/trace-input.c
+++ b/trace-input.c
@@ -16,7 +16,7 @@
16#include <ctype.h> 16#include <ctype.h>
17#include <errno.h> 17#include <errno.h>
18 18
19#include "trace-cmd.h" 19#include "trace-cmd-local.h"
20#include "list.h" 20#include "list.h"
21 21
22/* for debugging read instead of mmap */ 22/* for debugging read instead of mmap */
@@ -1714,6 +1714,238 @@ void tracecmd_close(struct tracecmd_input *handle)
1714 free(handle); 1714 free(handle);
1715} 1715}
1716 1716
1717static long long read_copy_size8(struct tracecmd_input *handle, int fd)
1718{
1719 long long size;
1720
1721 /* read size */
1722 if (do_read_check(handle, &size, 8))
1723 return -1;
1724
1725 if (__do_write_check(fd, &size, 8))
1726 return -1;
1727
1728 size = __data2host8(handle->pevent, size);
1729
1730 return size;
1731}
1732
1733static int read_copy_size4(struct tracecmd_input *handle, int fd)
1734{
1735 int size;
1736
1737 /* read size */
1738 if (do_read_check(handle, &size, 4))
1739 return -1;
1740
1741 if (__do_write_check(fd, &size, 4))
1742 return -1;
1743
1744 size = __data2host4(handle->pevent, size);
1745
1746 return size;
1747}
1748
1749static int read_copy_data(struct tracecmd_input *handle,
1750 unsigned long long size, int fd)
1751{
1752 char *buf;
1753
1754 buf = malloc(size);
1755 if (!buf)
1756 return -1;
1757 if (do_read_check(handle, buf, size))
1758 goto failed_read;
1759
1760 if (__do_write_check(fd, buf, size))
1761 goto failed_read;
1762
1763 free(buf);
1764
1765 return 0;
1766
1767 failed_read:
1768 free(buf);
1769 return -1;
1770}
1771
1772static int copy_header_files(struct tracecmd_input *handle, int fd)
1773{
1774 long long size;
1775
1776 lseek64(handle->fd, handle->header_files_start, SEEK_SET);
1777
1778 /* "header_page" */
1779 if (read_copy_data(handle, 12, fd) < 0)
1780 return -1;
1781
1782 size = read_copy_size8(handle, fd);
1783 if (size < 0)
1784 return -1;
1785
1786 if (read_copy_data(handle, size, fd) < 0)
1787 return -1;
1788
1789 /* "header_event" */
1790 if (read_copy_data(handle, 13, fd) < 0)
1791 return -1;
1792
1793 size = read_copy_size8(handle, fd);
1794 if (size < 0)
1795 return -1;
1796
1797 if (read_copy_data(handle, size, fd) < 0)
1798 return -1;
1799
1800 return 0;
1801}
1802
1803static int copy_ftrace_files(struct tracecmd_input *handle, int fd)
1804{
1805 unsigned long long size;
1806 int count;
1807 int i;
1808
1809 count = read_copy_size4(handle, fd);
1810 if (count < 0)
1811 return -1;
1812
1813 for (i = 0; i < count; i++) {
1814
1815 size = read_copy_size8(handle, fd);
1816 if (size < 0)
1817 return -1;
1818
1819 if (read_copy_data(handle, size, fd) < 0)
1820 return -1;
1821 }
1822
1823 return 0;
1824}
1825
1826static int copy_event_files(struct tracecmd_input *handle, int fd)
1827{
1828 unsigned long long size;
1829 char *system;
1830 int systems;
1831 int count;
1832 int ret;
1833 int i,x;
1834
1835 systems = read_copy_size4(handle, fd);
1836 if (systems < 0)
1837 return -1;
1838
1839 for (i = 0; i < systems; i++) {
1840 system = read_string(handle);
1841 if (!system)
1842 return -1;
1843 if (__do_write_check(fd, system, strlen(system) + 1)) {
1844 free(system);
1845 return -1;
1846 }
1847 free(system);
1848
1849 count = read_copy_size4(handle, fd);
1850 if (count < 0)
1851 return -1;
1852
1853 for (x=0; x < count; x++) {
1854 size = read_copy_size8(handle, fd);
1855 if (size < 0)
1856 return -1;
1857
1858 ret = read_copy_data(handle, size, fd);
1859 if (ret < 0)
1860 return -1;
1861 }
1862 }
1863
1864 return 0;
1865}
1866
1867static int copy_proc_kallsyms(struct tracecmd_input *handle, int fd)
1868{
1869 int size;
1870
1871 size = read_copy_size4(handle, fd);
1872 if (!size)
1873 return 0; /* OK? */
1874
1875 if (size < 0)
1876 return -1;
1877
1878 if (read_copy_data(handle, size, fd) < 0)
1879 return -1;
1880
1881 return 0;
1882}
1883
1884static int copy_ftrace_printk(struct tracecmd_input *handle, int fd)
1885{
1886 int size;
1887
1888 size = read_copy_size4(handle, fd);
1889 if (!size)
1890 return 0; /* OK? */
1891
1892 if (size < 0)
1893 return -1;
1894
1895 if (read_copy_data(handle, size, fd) < 0)
1896 return -1;
1897
1898 return 0;
1899}
1900
1901static int copy_command_lines(struct tracecmd_input *handle, int fd)
1902{
1903 unsigned long size;
1904
1905 size = read_copy_size8(handle, fd);
1906 if (!size)
1907 return 0; /* OK? */
1908
1909 if (size < 0)
1910 return -1;
1911
1912 if (read_copy_data(handle, size, fd) < 0)
1913 return -1;
1914
1915 return 0;
1916}
1917
1918int tracecmd_copy_headers(struct tracecmd_input *handle, int fd)
1919{
1920 int ret;
1921
1922 ret = copy_header_files(handle, fd);
1923 if (ret < 0)
1924 return -1;
1925
1926 ret = copy_ftrace_files(handle, fd);
1927 if (ret < 0)
1928 return -1;
1929
1930 ret = copy_event_files(handle, fd);
1931 if (ret < 0)
1932 return -1;
1933
1934 ret = copy_proc_kallsyms(handle, fd);
1935 if (ret < 0)
1936 return -1;
1937
1938 ret = copy_ftrace_printk(handle, fd);
1939 if (ret < 0)
1940 return -1;
1941
1942 ret = copy_command_lines(handle, fd);
1943 if (ret < 0)
1944 return -1;
1945
1946 return 0;
1947}
1948
1717/** 1949/**
1718 * tracecmd_long_size - return the size of "long" for the arch 1950 * tracecmd_long_size - return the size of "long" for the arch
1719 * @handle: input handle for the trace.dat file 1951 * @handle: input handle for the trace.dat file
diff --git a/trace-local.h b/trace-local.h
index 0be298f..9f49c24 100644
--- a/trace-local.h
+++ b/trace-local.h
@@ -12,4 +12,6 @@ int read_trace_files(void);
12 12
13void trace_report(int argc, char **argv); 13void trace_report(int argc, char **argv);
14 14
15void trace_split(int argc, char **argv);
16
15#endif /* __TRACE_LOCAL_H */ 17#endif /* __TRACE_LOCAL_H */
diff --git a/trace-output.c b/trace-output.c
index 66f95fb..41477f5 100644
--- a/trace-output.c
+++ b/trace-output.c
@@ -16,7 +16,7 @@
16#include <ctype.h> 16#include <ctype.h>
17#include <errno.h> 17#include <errno.h>
18 18
19#include "trace-cmd.h" 19#include "trace-cmd-local.h"
20#include "version.h" 20#include "version.h"
21 21
22struct tracecmd_output { 22struct tracecmd_output {
@@ -26,36 +26,10 @@ struct tracecmd_output {
26 char *tracing_dir; 26 char *tracing_dir;
27}; 27};
28 28
29static int do_write(struct tracecmd_output *handle, void *data, int size)
30{
31 int tot = 0;
32 int w;
33
34 do {
35 w = write(handle->fd, data, size - tot);
36 tot += w;
37
38 if (!w)
39 break;
40 if (w < 0)
41 return w;
42 } while (tot != size);
43
44 return tot;
45}
46
47static int 29static int
48do_write_check(struct tracecmd_output *handle, void *data, int size) 30do_write_check(struct tracecmd_output *handle, void *data, int size)
49{ 31{
50 int ret; 32 return __do_write_check(handle->fd, data, size);
51
52 ret = do_write(handle, data, size);
53 if (ret < 0)
54 return ret;
55 if (ret != size)
56 return -1;
57
58 return 0;
59} 33}
60 34
61void tracecmd_output_close(struct tracecmd_output *handle) 35void tracecmd_output_close(struct tracecmd_output *handle)
@@ -494,9 +468,11 @@ static int read_ftrace_printk(struct tracecmd_output *handle)
494 return 0; 468 return 0;
495} 469}
496 470
497static struct tracecmd_output *create_file(const char *output_file, int cpus) 471static struct tracecmd_output *create_file(const char *output_file, int cpus,
472 struct tracecmd_input *ihandle)
498{ 473{
499 struct tracecmd_output *handle; 474 struct tracecmd_output *handle;
475 struct pevent *pevent;
500 char buf[BUFSIZ]; 476 char buf[BUFSIZ];
501 char *file = NULL; 477 char *file = NULL;
502 struct stat st; 478 struct stat st;
@@ -524,11 +500,21 @@ static struct tracecmd_output *create_file(const char *output_file, int cpus)
524 if (do_write_check(handle, FILE_VERSION_STRING, strlen(FILE_VERSION_STRING) + 1)) 500 if (do_write_check(handle, FILE_VERSION_STRING, strlen(FILE_VERSION_STRING) + 1))
525 goto out_free; 501 goto out_free;
526 502
527 /* save endian */ 503 /* get endian and page size */
528 if (tracecmd_host_bigendian()) 504 if (ihandle) {
529 buf[0] = 1; 505 pevent = tracecmd_get_pevent(ihandle);
530 else 506 if (pevent->file_bigendian)
531 buf[0] = 0; 507 buf[0] = 1;
508 else
509 buf[0] = 0;
510 handle->page_size = tracecmd_page_size(ihandle);
511 } else {
512 if (tracecmd_host_bigendian())
513 buf[0] = 1;
514 else
515 buf[0] = 0;
516 handle->page_size = getpagesize();
517 }
532 518
533 if (do_write_check(handle, buf, 1)) 519 if (do_write_check(handle, buf, 1))
534 goto out_free; 520 goto out_free;
@@ -538,11 +524,12 @@ static struct tracecmd_output *create_file(const char *output_file, int cpus)
538 if (do_write_check(handle, buf, 1)) 524 if (do_write_check(handle, buf, 1))
539 goto out_free; 525 goto out_free;
540 526
541 /* save page_size */
542 handle->page_size = getpagesize();
543 if (do_write_check(handle, &handle->page_size, 4)) 527 if (do_write_check(handle, &handle->page_size, 4))
544 goto out_free; 528 goto out_free;
545 529
530 if (ihandle)
531 return handle;
532
546 if (read_header_files(handle)) 533 if (read_header_files(handle))
547 goto out_free; 534 goto out_free;
548 if (read_ftrace_files(handle)) 535 if (read_ftrace_files(handle))
@@ -577,9 +564,6 @@ static struct tracecmd_output *create_file(const char *output_file, int cpus)
577 put_tracing_file(file); 564 put_tracing_file(file);
578 file = NULL; 565 file = NULL;
579 566
580 if (do_write_check(handle, &cpus, 4))
581 goto out_free;
582
583 return handle; 567 return handle;
584 568
585 out_free: 569 out_free:
@@ -592,10 +576,13 @@ struct tracecmd_output *tracecmd_create_file_latency(const char *output_file, in
592 struct tracecmd_output *handle; 576 struct tracecmd_output *handle;
593 char *path; 577 char *path;
594 578
595 handle = create_file(output_file, cpus); 579 handle = create_file(output_file, cpus, NULL);
596 if (!handle) 580 if (!handle)
597 return NULL; 581 return NULL;
598 582
583 if (do_write_check(handle, &cpus, 4))
584 goto out_free;
585
599 if (do_write_check(handle, "latency ", 10)) 586 if (do_write_check(handle, "latency ", 10))
600 goto out_free; 587 goto out_free;
601 588
@@ -614,22 +601,20 @@ out_free:
614 return NULL; 601 return NULL;
615} 602}
616 603
617struct tracecmd_output *tracecmd_create_file(const char *output_file, 604int tracecmd_append_cpu_data(struct tracecmd_output *handle,
618 int cpus, char * const *cpu_data_files) 605 int cpus, char * const *cpu_data_files)
619{ 606{
620 unsigned long long *offsets = NULL; 607 unsigned long long *offsets = NULL;
621 unsigned long long *sizes = NULL; 608 unsigned long long *sizes = NULL;
622 struct tracecmd_output *handle;
623 unsigned long long offset; 609 unsigned long long offset;
624 off64_t check_size; 610 off64_t check_size;
625 char *file = NULL; 611 char *file;
626 struct stat st; 612 struct stat st;
627 int ret; 613 int ret;
628 int i; 614 int i;
629 615
630 handle = create_file(output_file, cpus); 616 if (do_write_check(handle, &cpus, 4))
631 if (!handle) 617 goto out_free;
632 return NULL;
633 618
634 if (do_write_check(handle, "flyrecord", 10)) 619 if (do_write_check(handle, "flyrecord", 10))
635 goto out_free; 620 goto out_free;
@@ -648,17 +633,12 @@ struct tracecmd_output *tracecmd_create_file(const char *output_file,
648 offset = (offset + (handle->page_size - 1)) & ~(handle->page_size - 1); 633 offset = (offset + (handle->page_size - 1)) & ~(handle->page_size - 1);
649 634
650 for (i = 0; i < cpus; i++) { 635 for (i = 0; i < cpus; i++) {
651 file = malloc_or_die(strlen(output_file) + 20); 636 file = cpu_data_files[i];
652 if (!file)
653 goto out_free;
654 sprintf(file, "%s.cpu%d", output_file, i);
655 ret = stat(file, &st); 637 ret = stat(file, &st);
656 if (ret < 0) { 638 if (ret < 0) {
657 warning("can not stat '%s'", file); 639 warning("can not stat '%s'", file);
658 goto out_free; 640 goto out_free;
659 } 641 }
660 free(file);
661 file = NULL;
662 offsets[i] = offset; 642 offsets[i] = offset;
663 sizes[i] = st.st_size; 643 sizes[i] = st.st_size;
664 offset += st.st_size; 644 offset += st.st_size;
@@ -689,13 +669,58 @@ struct tracecmd_output *tracecmd_create_file(const char *output_file,
689 free(offsets); 669 free(offsets);
690 free(sizes); 670 free(sizes);
691 671
692 return handle; 672 return 0;
693 673
694 out_free: 674 out_free:
695 free(file);
696 free(offsets); 675 free(offsets);
697 free(sizes); 676 free(sizes);
698 677
699 tracecmd_output_close(handle); 678 tracecmd_output_close(handle);
679 return -1;
680}
681
682struct tracecmd_output *tracecmd_create_file(const char *output_file,
683 int cpus, char * const *cpu_data_files)
684{
685 struct tracecmd_output *handle;
686
687 handle = create_file(output_file, cpus, NULL);
688 if (!handle)
689 return NULL;
690
691 if (tracecmd_append_cpu_data(handle, cpus, cpu_data_files) < 0) {
692 free(handle);
693 return NULL;
694 }
695
696 return handle;
697}
698
699/**
700 * tracecmd_copy - copy the headers of one trace.dat file for another
701 * @ihandle: input handle of the trace.dat file to copy
702 * @file: the trace.dat file to create
703 *
704 * Reads the header information and creates a new trace data file
705 * with the same characteristics (events and all) and returns
706 * tracecmd_output handle to this new file.
707 */
708struct tracecmd_output *tracecmd_copy(struct tracecmd_input *ihandle,
709 const char *file)
710{
711 struct tracecmd_output *handle;
712
713 handle = create_file(file, tracecmd_cpus(ihandle), ihandle);
714 if (!handle)
715 return NULL;
716
717 if (tracecmd_copy_headers(ihandle, handle->fd) < 0)
718 goto out_free;
719
720 /* The file is all ready to have cpu data attached */
721 return handle;
722
723out_free:
724 tracecmd_output_close(handle);
700 return NULL; 725 return NULL;
701} 726}
diff --git a/trace-split.c b/trace-split.c
new file mode 100644
index 0000000..96cda2d
--- /dev/null
+++ b/trace-split.c
@@ -0,0 +1,514 @@
1/*
2 * Copyright (C) 2009, Steven Rostedt <srostedt@redhat.com>
3 *
4 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License (not later!)
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
20 */
21#define _LARGEFILE64_SOURCE
22#define _GNU_SOURCE
23#include <dirent.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include <getopt.h>
28#include <stdarg.h>
29#include <sys/types.h>
30#include <sys/stat.h>
31#include <sys/wait.h>
32#include <sys/mman.h>
33#include <pthread.h>
34#include <fcntl.h>
35#include <unistd.h>
36#include <ctype.h>
37#include <errno.h>
38
39#include "trace-local.h"
40
41static unsigned int page_size;
42static const char *default_input_file = "trace.dat";
43static const char *input_file;
44
45enum split_types {
46 SPLIT_NONE,
47 /* The order of these must be reverse of the case statement in the options */
48 SPLIT_SECONDS,
49 SPLIT_MSECS,
50 SPLIT_USECS,
51 SPLIT_EVENTS,
52 SPLIT_PAGES,
53 SPLIT_NR_TYPES,
54};
55
56struct cpu_data {
57 unsigned long long ts;
58 unsigned long long offset;
59 struct record *record;
60 int cpu;
61 int fd;
62 int index;
63 void *commit;
64 void *page;
65 char *file;
66};
67
68static int create_type_len(struct pevent *pevent, int time, int len)
69{
70 static int bigendian = -1;
71 char *ptr;
72 int test;
73
74 if (bigendian < 0) {
75 test = 0x4321;
76 ptr = (char *)&test;
77 if (*ptr == 0x12)
78 bigendian = 0;
79 else
80 bigendian = 1;
81 }
82
83 if (pevent->file_bigendian)
84 time |= (len << 27);
85 else
86 time = (time << 5) | len;
87
88 return __data2host4(pevent, time);
89}
90
91static int write_record(struct tracecmd_input *handle,
92 struct record *record,
93 struct cpu_data *cpu_data,
94 enum split_types type)
95{
96 unsigned long long diff;
97 struct pevent *pevent;
98 void *page;
99 int len;
100 char *ptr;
101 int index = 0;
102 int time;
103
104 page = cpu_data->page;
105
106 pevent = tracecmd_get_pevent(handle);
107
108 ptr = page + cpu_data->index;
109
110 diff = record->ts - cpu_data->ts;
111 if (diff > (1 << 27)) {
112 /* Add a time stamp */
113 len = RINGBUF_TYPE_TIME_EXTEND;
114 time = (unsigned int)(diff & ((1ULL << 27) - 1));
115 time = create_type_len(pevent, time, len);
116 *(unsigned *)ptr = time;
117 ptr += 4;
118 time = (unsigned int)(diff >> 27);
119 *(unsigned *)ptr = __data2host4(pevent, time);
120 cpu_data->ts = record->ts;
121 cpu_data->index += 8;
122 return 0;
123 }
124
125 if (record->size) {
126 if (record->size < 28 * 4)
127 len = record->size / 4;
128 else
129 len = 0;
130 }
131
132 time = (unsigned)diff;
133 time = create_type_len(pevent, time, len);
134
135 memcpy(ptr, &time, 4);
136 ptr += 4;
137 index = 4;
138
139 if (!len) {
140 len = record->size / 4;
141 len += 4;
142 memcpy(ptr, &len, 4);
143 ptr += 4;
144 index += 4;
145 }
146
147 len = (record->size + 3) & ~3;
148 index += len;
149
150 memcpy(ptr, record->data, len);
151
152 cpu_data->index += index;
153 cpu_data->ts = record->ts;
154
155 return 1;
156}
157
158static void write_page(struct cpu_data *cpu_data, int long_size)
159{
160 if (long_size == 8)
161 *(unsigned long long *)cpu_data->commit =
162 (unsigned long long)cpu_data->index;
163 else
164 *(unsigned int *)cpu_data->commit =
165 cpu_data->index;
166 write(cpu_data->fd, cpu_data->page, page_size);
167}
168
169static struct record *read_record(struct tracecmd_input *handle,
170 int percpu, int *cpu)
171{
172 if (percpu)
173 return tracecmd_read_data(handle, *cpu);
174
175 return tracecmd_read_next_data(handle, cpu);
176}
177
178static int parse_cpu(struct tracecmd_input *handle,
179 struct cpu_data *cpu_data,
180 unsigned long long start,
181 unsigned long long end,
182 int count_limit, int percpu, int cpu,
183 enum split_types type)
184{
185 struct record *record;
186 struct pevent *pevent;
187 void *ptr;
188 int page_size;
189 int long_size = 0;
190 int cpus;
191 int count = 0;
192 int pages = 0;
193
194 cpus = tracecmd_cpus(handle);
195
196 long_size = tracecmd_long_size(handle);
197 page_size = tracecmd_page_size(handle);
198 pevent = tracecmd_get_pevent(handle);
199
200 /* Force new creation of first page */
201 if (percpu) {
202 cpu_data[cpu].index = page_size + 1;
203 cpu_data[cpu].page = NULL;
204 } else {
205 for (cpu = 0; cpu < cpus; cpu++) {
206 cpu_data[cpu].index = page_size + 1;
207 cpu_data[cpu].page = NULL;
208 }
209 }
210
211 /*
212 * Get the cpu pointers up to the start of the
213 * start time stamp.
214 */
215
216 record = read_record(handle, percpu, &cpu);
217
218 if (start) {
219 while (record && record->ts < start) {
220 free_record(record);
221 record = read_record(handle, percpu, &cpu);
222 }
223 } else if (record)
224 start = record->ts;
225
226 while (record && (!end || record->ts <= end)) {
227 if (cpu_data[cpu].index + record->record_size > page_size) {
228 if (cpu_data[cpu].page)
229 write_page(&cpu_data[cpu], long_size);
230 else
231 cpu_data[cpu].page = malloc_or_die(page_size);
232
233 if (type == SPLIT_PAGES && pages++ > count_limit)
234 break;
235
236 memset(cpu_data[cpu].page, 0, page_size);
237 ptr = cpu_data[cpu].page;
238
239 *(unsigned long long*)ptr =
240 __data2host8(pevent, record->ts);
241 cpu_data[cpu].ts = record->ts;
242 ptr += 8;
243 cpu_data[cpu].commit = ptr;
244 ptr += long_size;
245 cpu_data[cpu].index = 8 + long_size;
246 }
247
248 cpu_data[cpu].offset = record->offset;
249
250 if (write_record(handle, record, &cpu_data[cpu], type)) {
251 free_record(record);
252 record = read_record(handle, percpu, &cpu);
253
254 /* if we hit the end of the cpu, clear the offset */
255 if (!record) {
256 if (percpu)
257 cpu_data[cpu].offset = 0;
258 else
259 for (cpu = 0; cpu < cpus; cpu++)
260 cpu_data[cpu].offset = 0;
261 }
262
263 switch (type) {
264 case SPLIT_NONE:
265 break;
266 case SPLIT_SECONDS:
267 if (record &&
268 record->ts >
269 (start + (unsigned long long)count_limit * 1000000000ULL)) {
270 free_record(record);
271 record = NULL;
272 }
273 break;
274 case SPLIT_MSECS:
275 if (record &&
276 record->ts >
277 (start + (unsigned long long)count_limit * 1000000ULL)) {
278 free_record(record);
279 record = NULL;
280 }
281 break;
282 case SPLIT_USECS:
283 if (record &&
284 record->ts >
285 (start + (unsigned long long)count_limit * 1000ULL)) {
286 free_record(record);
287 record = NULL;
288 }
289 break;
290 case SPLIT_EVENTS:
291 if (++count >= count_limit) {
292 free_record(record);
293 record = NULL;
294 }
295 break;
296 default:
297 break;
298 }
299 }
300 }
301
302 if (record)
303 free_record(record);
304
305 if (percpu) {
306 if (cpu_data[cpu].page) {
307 write_page(&cpu_data[cpu], long_size);
308 free(cpu_data[cpu].page);
309 cpu_data[cpu].page = NULL;
310 }
311 } else {
312 for (cpu = 0; cpu < cpus; cpu++) {
313 if (cpu_data[cpu].page) {
314 write_page(&cpu_data[cpu], long_size);
315 free(cpu_data[cpu].page);
316 cpu_data[cpu].page = NULL;
317 }
318 }
319 }
320
321 return 0;
322}
323
324static double parse_file(struct tracecmd_input *handle,
325 const char *output_file,
326 unsigned long long start,
327 unsigned long long end, int percpu,
328 int count, enum split_types type)
329{
330 unsigned long long current;
331 struct tracecmd_output *ohandle;
332 struct cpu_data *cpu_data;
333 struct record *record;
334 char **cpu_list;
335 char *file;
336 int cpus;
337 int cpu;
338 int fd;
339
340 ohandle = tracecmd_copy(handle, output_file);
341
342 cpus = tracecmd_cpus(handle);
343 cpu_data = malloc_or_die(sizeof(*cpu_data) * cpus);
344
345 for (cpu = 0; cpu < cpus; cpu++) {
346 file = malloc_or_die(strlen(output_file) + 50);
347 sprintf(file, ".tmp.%s.%d", output_file, cpu);
348 fd = open(file, O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, 0644);
349 cpu_data[cpu].cpu = cpu;
350 cpu_data[cpu].fd = fd;
351 cpu_data[cpu].file = file;
352 cpu_data[cpu].offset = 0;
353 if (start)
354 tracecmd_set_cpu_to_timestamp(handle, cpu, start);
355 }
356
357 if (percpu) {
358 for (cpu = 0; cpu < cpus; cpu++)
359 parse_cpu(handle, cpu_data, start,
360 end, count, percpu, cpu, type);
361 } else
362 parse_cpu(handle, cpu_data, start,
363 end, count, percpu, -1, type);
364
365 cpu_list = malloc_or_die(sizeof(*cpu_list) * cpus);
366 for (cpu = 0; cpu < cpus; cpu ++)
367 cpu_list[cpu] = cpu_data[cpu].file;
368
369 tracecmd_append_cpu_data(ohandle, cpus, cpu_list);
370
371 current = end;
372 for (cpu = 0; cpu < cpus; cpu++) {
373 /* Set the tracecmd cursor to the next set of records */
374 if (cpu_data[cpu].offset) {
375 record = tracecmd_read_at(handle, cpu_data[cpu].offset, NULL);
376 if (record && (!current || record->ts > current))
377 current = record->ts + 1;
378 free_record(record);
379 }
380 unlink(cpu_data[cpu].file);
381 free(cpu_data[cpu].file);
382 }
383 free(cpu_data);
384 free(cpu_list);
385 tracecmd_output_close(ohandle);
386
387 return current;
388}
389
390void trace_split (int argc, char **argv)
391{
392 struct tracecmd_input *handle;
393 struct pevent *pevent;
394 unsigned long long start_ns = 0, end_ns = 0;
395 unsigned long long current;
396 double start, end;
397 char *output = NULL;
398 char *output_file;
399 enum split_types split_type = SPLIT_NONE;
400 enum split_types type = SPLIT_NONE;
401 int count;
402 int repeat = 0;
403 int percpu = 0;
404 int cpu = -1;
405 int ac;
406 int c;
407
408 if (strcmp(argv[1], "split") != 0)
409 usage(argv);
410
411 while ((c = getopt(argc-1, argv+1, "+ho:i:s:m:u:e:p:rcC:")) >= 0) {
412 switch (c) {
413 case 'h':
414 usage(argv);
415 break;
416 case 'p':
417 type++;
418 case 'e':
419 type++;
420 case 'u':
421 type++;
422 case 'm':
423 type++;
424 case 's':
425 type++;
426 if (split_type != SPLIT_NONE)
427 die("Only one type of split is allowed");
428 count = atoi(optarg);
429 split_type = type;
430 break;
431 case 'r':
432 repeat = 1;
433 break;
434 case 'c':
435 percpu = 1;
436 break;
437 case 'C':
438 cpu = atoi(optarg);
439 break;
440 case 'o':
441 if (output)
442 die("only one output file allowed");
443 output = strdup(optarg);
444 break;
445 case 'i':
446 input_file = optarg;
447 break;
448 default:
449 usage(argv);
450 }
451 }
452
453 ac = (argc - optind);
454
455 if (ac >= 2) {
456 optind++;
457 start = strtod(argv[optind], NULL);
458 if (ac > 3)
459 usage(argv);
460
461 start_ns = (unsigned long long)(start * 1000000000.0);
462 optind++;
463 if (ac == 3) {
464 end = strtod(argv[optind], NULL);
465 end_ns = (unsigned long long)(end * 1000000000.0);
466 if (end_ns < start_ns)
467 die("Error: end is less than start");
468 }
469 }
470
471 if (!input_file)
472 input_file = default_input_file;
473
474 handle = tracecmd_open(input_file);
475 if (!handle)
476 die("error reading %s", input_file);
477
478 page_size = tracecmd_page_size(handle);
479
480 pevent = tracecmd_get_pevent(handle);
481
482 if (!output) {
483 if (repeat)
484 output = strdup(input_file);
485 else {
486 output = malloc_or_die(strlen(input_file) + 3);
487 sprintf(output, "%s.1", input_file);
488 }
489 }
490
491 current = start_ns;
492 output_file = malloc_or_die(strlen(output) + 50);
493 c = 1;
494
495 do {
496 if (repeat)
497 sprintf(output_file, "%s.%04d", output, c++);
498 else
499 strcpy(output_file, output);
500
501 current = parse_file(handle, output_file, start_ns, end_ns,
502 percpu, count, type);
503 if (!repeat)
504 break;
505 start_ns = 0;
506 } while (current && (!end_ns || current < end_ns));
507
508 free(output);
509 free(output_file);
510
511 tracecmd_close(handle);
512
513 return;
514}