aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteven Rostedt <srostedt@redhat.com>2011-02-24 21:46:56 -0500
committerSteven Rostedt <rostedt@goodmis.org>2011-02-24 21:46:56 -0500
commite09a5db1a929ab668c273b87c4f0a32b81e1c21a (patch)
tree25a0f925b9bf53501e400b4d857bad0dc6304526
parentd9604ab0589a5c474be90ec62d842ad8504e50b0 (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.txt5
-rw-r--r--trace-record.c289
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
181EXAMPLES 186EXAMPLES
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
83static int finished; 86static int finished;
84 87
88/* Try a few times to get an accurate date */
89static int date2ts_tries = 5;
90
85struct func_list { 91struct 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
1420static void record_data(void) 1424static 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
1545static unsigned long long
1546find_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
1580static 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
1632static 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 */
1670static 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
1534void set_buffer_size(void) 1771void 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);