diff options
-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); |