diff options
author | Steven Rostedt <srostedt@redhat.com> | 2011-02-24 21:46:56 -0500 |
---|---|---|
committer | Steven Rostedt <rostedt@goodmis.org> | 2011-02-24 21:46:56 -0500 |
commit | e09a5db1a929ab668c273b87c4f0a32b81e1c21a (patch) | |
tree | 25a0f925b9bf53501e400b4d857bad0dc6304526 | |
parent | d9604ab0589a5c474be90ec62d842ad8504e50b0 (diff) |
trace-cmd: Add trace-cmd record --date option
Adding the --date option to trace-cmd record (and extract) will
cause trace-cmd to write a timestamp into the ftrace buffer and
use it to mapped the ftrace buffer's timestamps with gettimeofday.
This will allow reading of the trace.dat file to be able to convert
the timestamps into actual wall time.
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
-rw-r--r-- | Documentation/trace-cmd-record.1.txt | 5 | ||||
-rw-r--r-- | trace-record.c | 289 |
2 files changed, 283 insertions, 11 deletions
diff --git a/Documentation/trace-cmd-record.1.txt b/Documentation/trace-cmd-record.1.txt index 0954540..2ae6748 100644 --- a/Documentation/trace-cmd-record.1.txt +++ b/Documentation/trace-cmd-record.1.txt | |||
@@ -177,6 +177,11 @@ OPTIONS | |||
177 | reliable, the amount of data is not that intensive, and a guarantee is | 177 | reliable, the amount of data is not that intensive, and a guarantee is |
178 | needed that all traced information is transfered successfully. | 178 | needed that all traced information is transfered successfully. |
179 | 179 | ||
180 | *--date*:: | ||
181 | With the *--date* option, "trace-cmd" will write timestamps into the | ||
182 | trace buffer after it has finished recording. It will then map the | ||
183 | timestamp to gettimeofday which will allow wall time output from the | ||
184 | timestamps reading the created 'trace.dat' file. | ||
180 | 185 | ||
181 | EXAMPLES | 186 | EXAMPLES |
182 | -------- | 187 | -------- |
diff --git a/trace-record.c b/trace-record.c index 0d6b38a..52d9fd8 100644 --- a/trace-record.c +++ b/trace-record.c | |||
@@ -25,8 +25,10 @@ | |||
25 | #include <stdlib.h> | 25 | #include <stdlib.h> |
26 | #include <string.h> | 26 | #include <string.h> |
27 | #include <stdarg.h> | 27 | #include <stdarg.h> |
28 | #include <getopt.h> | ||
28 | #include <sys/types.h> | 29 | #include <sys/types.h> |
29 | #include <sys/stat.h> | 30 | #include <sys/stat.h> |
31 | #include <sys/time.h> | ||
30 | #include <sys/wait.h> | 32 | #include <sys/wait.h> |
31 | #include <sys/socket.h> | 33 | #include <sys/socket.h> |
32 | #include <sys/ptrace.h> | 34 | #include <sys/ptrace.h> |
@@ -51,6 +53,7 @@ | |||
51 | #define CURRENT "current_tracer" | 53 | #define CURRENT "current_tracer" |
52 | #define ITER_CTRL "trace_options" | 54 | #define ITER_CTRL "trace_options" |
53 | #define MAX_LATENCY "tracing_max_latency" | 55 | #define MAX_LATENCY "tracing_max_latency" |
56 | #define STAMP "stamp" | ||
54 | 57 | ||
55 | #define UDP_MAX_PACKET (65536 - 20) | 58 | #define UDP_MAX_PACKET (65536 - 20) |
56 | 59 | ||
@@ -82,6 +85,9 @@ static int filter_pid = -1; | |||
82 | 85 | ||
83 | static int finished; | 86 | static int finished; |
84 | 87 | ||
88 | /* Try a few times to get an accurate date */ | ||
89 | static int date2ts_tries = 5; | ||
90 | |||
85 | struct func_list { | 91 | struct func_list { |
86 | struct func_list *next; | 92 | struct func_list *next; |
87 | const char *func; | 93 | const char *func; |
@@ -1337,8 +1343,6 @@ static void setup_network(void) | |||
1337 | write(sfd, buf, strlen(buf)+1); | 1343 | write(sfd, buf, strlen(buf)+1); |
1338 | 1344 | ||
1339 | /* write the pagesize (in ASCII) */ | 1345 | /* write the pagesize (in ASCII) */ |
1340 | |||
1341 | page_size = getpagesize(); | ||
1342 | sprintf(buf, "%d", page_size); | 1346 | sprintf(buf, "%d", page_size); |
1343 | 1347 | ||
1344 | /* include \0 */ | 1348 | /* include \0 */ |
@@ -1417,7 +1421,7 @@ static void start_threads(void) | |||
1417 | } | 1421 | } |
1418 | } | 1422 | } |
1419 | 1423 | ||
1420 | static void record_data(void) | 1424 | static void record_data(char *date2ts) |
1421 | { | 1425 | { |
1422 | struct tracecmd_output *handle; | 1426 | struct tracecmd_output *handle; |
1423 | char **temp_files; | 1427 | char **temp_files; |
@@ -1439,8 +1443,15 @@ static void record_data(void) | |||
1439 | for (i = 0; i < cpu_count; i++) | 1443 | for (i = 0; i < cpu_count; i++) |
1440 | temp_files[i] = get_temp_file(i); | 1444 | temp_files[i] = get_temp_file(i); |
1441 | 1445 | ||
1442 | handle = tracecmd_create_file_glob(output_file, cpu_count, | 1446 | handle = tracecmd_create_init_file_glob(output_file, listed_events); |
1443 | temp_files, listed_events); | 1447 | if (!handle) |
1448 | die("Error creating output file"); | ||
1449 | |||
1450 | if (date2ts) | ||
1451 | tracecmd_add_option(handle, TRACECMD_OPTION_DATE, | ||
1452 | strlen(date2ts)+1, date2ts); | ||
1453 | |||
1454 | tracecmd_append_cpu_data(handle, cpu_count, temp_files); | ||
1444 | 1455 | ||
1445 | for (i = 0; i < cpu_count; i++) | 1456 | for (i = 0; i < cpu_count; i++) |
1446 | put_temp_file(temp_files[i]); | 1457 | put_temp_file(temp_files[i]); |
@@ -1531,6 +1542,232 @@ static void add_func(struct func_list **list, const char *func) | |||
1531 | *list = item; | 1542 | *list = item; |
1532 | } | 1543 | } |
1533 | 1544 | ||
1545 | static unsigned long long | ||
1546 | find_ts_in_page(struct pevent *pevent, void *page, int size) | ||
1547 | { | ||
1548 | struct event_format *event; | ||
1549 | struct format_field *field; | ||
1550 | struct record *last_record = NULL; | ||
1551 | struct record *record; | ||
1552 | unsigned long long ts = 0; | ||
1553 | int id; | ||
1554 | |||
1555 | if (size <= 0) | ||
1556 | return 0; | ||
1557 | |||
1558 | while (!ts) { | ||
1559 | record = tracecmd_read_page_record(pevent, page, size, | ||
1560 | last_record); | ||
1561 | if (!record) | ||
1562 | break; | ||
1563 | free_record(last_record); | ||
1564 | id = pevent_data_type(pevent, record); | ||
1565 | event = pevent_data_event_from_type(pevent, id); | ||
1566 | if (event) { | ||
1567 | /* Make sure this is our event */ | ||
1568 | field = pevent_find_field(event, "buf"); | ||
1569 | /* the trace_marker adds a '\n' */ | ||
1570 | if (field && strcmp(STAMP"\n", record->data + field->offset) == 0) | ||
1571 | ts = record->ts; | ||
1572 | } | ||
1573 | last_record = record; | ||
1574 | } | ||
1575 | free_record(last_record); | ||
1576 | |||
1577 | return ts; | ||
1578 | } | ||
1579 | |||
1580 | static unsigned long long find_time_stamp(struct pevent *pevent) | ||
1581 | { | ||
1582 | struct dirent *dent; | ||
1583 | unsigned long long ts = 0; | ||
1584 | void *page; | ||
1585 | char *path; | ||
1586 | char *file; | ||
1587 | DIR *dir; | ||
1588 | int len; | ||
1589 | int fd; | ||
1590 | int r; | ||
1591 | |||
1592 | path = tracecmd_get_tracing_file("per_cpu"); | ||
1593 | if (!path) | ||
1594 | return 0; | ||
1595 | |||
1596 | dir = opendir(path); | ||
1597 | if (!dir) | ||
1598 | goto out; | ||
1599 | |||
1600 | len = strlen(path); | ||
1601 | file = malloc_or_die(len + strlen("trace_pipe_raw") + 32); | ||
1602 | page = malloc_or_die(page_size); | ||
1603 | |||
1604 | while ((dent = readdir(dir))) { | ||
1605 | const char *name = dent->d_name; | ||
1606 | |||
1607 | if (strncmp(name, "cpu", 3) != 0) | ||
1608 | continue; | ||
1609 | |||
1610 | sprintf(file, "%s/%s/trace_pipe_raw", path, name); | ||
1611 | fd = open(file, O_RDONLY); | ||
1612 | if (fd < 0) | ||
1613 | continue; | ||
1614 | do { | ||
1615 | r = read(fd, page, page_size); | ||
1616 | ts = find_ts_in_page(pevent, page, r); | ||
1617 | if (ts) | ||
1618 | break; | ||
1619 | } while (r > 0); | ||
1620 | if (ts) | ||
1621 | break; | ||
1622 | } | ||
1623 | free(file); | ||
1624 | free(page); | ||
1625 | closedir(dir); | ||
1626 | |||
1627 | out: | ||
1628 | tracecmd_put_tracing_file(path); | ||
1629 | return ts; | ||
1630 | } | ||
1631 | |||
1632 | static char *read_file(char *file, int *psize) | ||
1633 | { | ||
1634 | char buffer[BUFSIZ]; | ||
1635 | char *path; | ||
1636 | char *buf; | ||
1637 | int size = 0; | ||
1638 | int fd; | ||
1639 | int r; | ||
1640 | |||
1641 | path = tracecmd_get_tracing_file(file); | ||
1642 | fd = open(path, O_RDONLY); | ||
1643 | tracecmd_put_tracing_file(path); | ||
1644 | if (fd < 0) { | ||
1645 | warning("%s not found, --date ignored", file); | ||
1646 | return NULL; | ||
1647 | } | ||
1648 | do { | ||
1649 | r = read(fd, buffer, BUFSIZ); | ||
1650 | if (r <= 0) | ||
1651 | continue; | ||
1652 | if (size) { | ||
1653 | buf = realloc(buf, size+r); | ||
1654 | if (!buf) | ||
1655 | die("malloc"); | ||
1656 | } else | ||
1657 | buf = malloc_or_die(r); | ||
1658 | memcpy(buf+size, buffer, r); | ||
1659 | size += r; | ||
1660 | } while (r); | ||
1661 | |||
1662 | *psize = size; | ||
1663 | return buf; | ||
1664 | } | ||
1665 | |||
1666 | /* | ||
1667 | * Try to write the date into the ftrace buffer and then | ||
1668 | * read it back, mapping the timestamp to the date. | ||
1669 | */ | ||
1670 | static char *get_date_to_ts(void) | ||
1671 | { | ||
1672 | unsigned long long min = -1ULL; | ||
1673 | unsigned long long diff; | ||
1674 | unsigned long long stamp; | ||
1675 | unsigned long long min_stamp; | ||
1676 | unsigned long long min_ts; | ||
1677 | unsigned long long ts; | ||
1678 | struct pevent *pevent; | ||
1679 | struct timeval start; | ||
1680 | struct timeval end; | ||
1681 | char *date2ts = NULL; | ||
1682 | char *path; | ||
1683 | char *buf; | ||
1684 | int size; | ||
1685 | int tfd; | ||
1686 | int ret; | ||
1687 | int i; | ||
1688 | |||
1689 | /* Set up a pevent to read the raw format */ | ||
1690 | pevent = pevent_alloc(); | ||
1691 | if (!pevent) { | ||
1692 | warning("failed to alloc pevent, --date ignored"); | ||
1693 | return NULL; | ||
1694 | } | ||
1695 | |||
1696 | buf = read_file("events/header_page", &size); | ||
1697 | if (!buf) | ||
1698 | goto out_pevent; | ||
1699 | ret = pevent_parse_header_page(pevent, buf, size, sizeof(unsigned long)); | ||
1700 | free(buf); | ||
1701 | if (ret < 0) { | ||
1702 | warning("Can't parse header page, --date ignored"); | ||
1703 | goto out_pevent; | ||
1704 | } | ||
1705 | |||
1706 | /* Find the format for ftrace:print. */ | ||
1707 | buf = read_file("events/ftrace/print/format", &size); | ||
1708 | if (!buf) | ||
1709 | goto out_pevent; | ||
1710 | ret = pevent_parse_event(pevent, buf, size, "ftrace"); | ||
1711 | free(buf); | ||
1712 | if (ret < 0) { | ||
1713 | warning("Can't parse print event, --date ignored"); | ||
1714 | goto out_pevent; | ||
1715 | } | ||
1716 | |||
1717 | path = tracecmd_get_tracing_file("trace_marker"); | ||
1718 | tfd = open(path, O_WRONLY); | ||
1719 | tracecmd_put_tracing_file(path); | ||
1720 | if (tfd < 0) { | ||
1721 | warning("Can not open 'trace_marker', --date ignored"); | ||
1722 | goto out_pevent; | ||
1723 | } | ||
1724 | |||
1725 | for (i = 0; i < date2ts_tries; i++) { | ||
1726 | disable_tracing(); | ||
1727 | clear_trace(); | ||
1728 | enable_tracing(); | ||
1729 | |||
1730 | gettimeofday(&start, NULL); | ||
1731 | write(tfd, STAMP, 5); | ||
1732 | gettimeofday(&end, NULL); | ||
1733 | |||
1734 | disable_tracing(); | ||
1735 | ts = find_time_stamp(pevent); | ||
1736 | if (!ts) | ||
1737 | continue; | ||
1738 | |||
1739 | diff = (unsigned long long)end.tv_sec * 1000000; | ||
1740 | diff += (unsigned long long)end.tv_usec; | ||
1741 | stamp = diff; | ||
1742 | diff -= (unsigned long long)start.tv_sec * 1000000; | ||
1743 | diff -= (unsigned long long)start.tv_usec; | ||
1744 | |||
1745 | if (diff < min) { | ||
1746 | min_ts = ts; | ||
1747 | min_stamp = stamp - diff / 2; | ||
1748 | min = diff; | ||
1749 | } | ||
1750 | } | ||
1751 | |||
1752 | close(tfd); | ||
1753 | |||
1754 | /* 16 hex chars + 0x + \0 */ | ||
1755 | date2ts = malloc(19); | ||
1756 | if (!date2ts) | ||
1757 | goto out_pevent; | ||
1758 | |||
1759 | /* | ||
1760 | * The difference between the timestamp and the gtod is | ||
1761 | * stored as an ASCII string in hex. | ||
1762 | */ | ||
1763 | snprintf(date2ts, 19, "0x%llx", min_stamp - min_ts / 1000); | ||
1764 | |||
1765 | out_pevent: | ||
1766 | pevent_free(pevent); | ||
1767 | |||
1768 | return date2ts; | ||
1769 | } | ||
1770 | |||
1534 | void set_buffer_size(void) | 1771 | void set_buffer_size(void) |
1535 | { | 1772 | { |
1536 | char buf[BUFSIZ]; | 1773 | char buf[BUFSIZ]; |
@@ -1566,6 +1803,7 @@ void trace_record (int argc, char **argv) | |||
1566 | struct event_list *last_event; | 1803 | struct event_list *last_event; |
1567 | struct tracecmd_event_list *list; | 1804 | struct tracecmd_event_list *list; |
1568 | struct trace_seq s; | 1805 | struct trace_seq s; |
1806 | char *date2ts = NULL; | ||
1569 | int record_all = 0; | 1807 | int record_all = 0; |
1570 | int disable = 0; | 1808 | int disable = 0; |
1571 | int events = 0; | 1809 | int events = 0; |
@@ -1606,7 +1844,18 @@ void trace_record (int argc, char **argv) | |||
1606 | } else | 1844 | } else |
1607 | usage(argv); | 1845 | usage(argv); |
1608 | 1846 | ||
1609 | while ((c = getopt(argc-1, argv+1, "+hae:f:Fp:cdo:O:s:r:vg:l:n:P:N:tb:ki")) >= 0) { | 1847 | for (;;) { |
1848 | int option_index = 0; | ||
1849 | static struct option long_options[] = { | ||
1850 | {"date", no_argument, NULL, 0}, | ||
1851 | {"help", no_argument, NULL, '?'}, | ||
1852 | {NULL, 0, NULL, 0} | ||
1853 | }; | ||
1854 | |||
1855 | c = getopt_long (argc-1, argv+1, "+hae:f:Fp:cdo:O:s:r:vg:l:n:P:N:tb:ki", | ||
1856 | long_options, &option_index); | ||
1857 | if (c == -1) | ||
1858 | break; | ||
1610 | switch (c) { | 1859 | switch (c) { |
1611 | case 'h': | 1860 | case 'h': |
1612 | usage(argv); | 1861 | usage(argv); |
@@ -1746,6 +1995,17 @@ void trace_record (int argc, char **argv) | |||
1746 | case 'i': | 1995 | case 'i': |
1747 | ignore_event_not_found = 1; | 1996 | ignore_event_not_found = 1; |
1748 | break; | 1997 | break; |
1998 | case 0: | ||
1999 | switch (option_index) { | ||
2000 | case 0: /* date */ | ||
2001 | date = 1; | ||
2002 | break; | ||
2003 | default: | ||
2004 | usage(argv); | ||
2005 | } | ||
2006 | break; | ||
2007 | default: | ||
2008 | usage(argv); | ||
1749 | } | 2009 | } |
1750 | } | 2010 | } |
1751 | 2011 | ||
@@ -1773,6 +2033,8 @@ void trace_record (int argc, char **argv) | |||
1773 | if (event_selection) | 2033 | if (event_selection) |
1774 | expand_event_list(); | 2034 | expand_event_list(); |
1775 | 2035 | ||
2036 | page_size = getpagesize(); | ||
2037 | |||
1776 | if (!extract) { | 2038 | if (!extract) { |
1777 | fset = set_ftrace(!disable); | 2039 | fset = set_ftrace(!disable); |
1778 | disable_all(); | 2040 | disable_all(); |
@@ -1841,8 +2103,9 @@ void trace_record (int argc, char **argv) | |||
1841 | 2103 | ||
1842 | stop_threads(); | 2104 | stop_threads(); |
1843 | 2105 | ||
1844 | record_data(); | 2106 | |
1845 | delete_thread_data(); | 2107 | if (!keep) |
2108 | disable_all(); | ||
1846 | 2109 | ||
1847 | printf("Kernel buffer statistics:\n" | 2110 | printf("Kernel buffer statistics:\n" |
1848 | " Note: \"entries\" are the entries left in the kernel ring buffer and are not\n" | 2111 | " Note: \"entries\" are the entries left in the kernel ring buffer and are not\n" |
@@ -1856,12 +2119,16 @@ void trace_record (int argc, char **argv) | |||
1856 | printf("\n"); | 2119 | printf("\n"); |
1857 | } | 2120 | } |
1858 | 2121 | ||
2122 | if (date) | ||
2123 | date2ts = get_date_to_ts(); | ||
2124 | |||
2125 | record_data(date2ts); | ||
2126 | delete_thread_data(); | ||
2127 | |||
2128 | |||
1859 | if (keep) | 2129 | if (keep) |
1860 | exit(0); | 2130 | exit(0); |
1861 | 2131 | ||
1862 | /* Turn off everything */ | ||
1863 | disable_all(); | ||
1864 | |||
1865 | /* If tracing_on was enabled before we started, set it on now */ | 2132 | /* If tracing_on was enabled before we started, set it on now */ |
1866 | if (tracing_on_init_val) | 2133 | if (tracing_on_init_val) |
1867 | write_tracing_on(tracing_on_init_val); | 2134 | write_tracing_on(tracing_on_init_val); |