diff options
author | David Ahern <dsahern@gmail.com> | 2013-10-08 23:26:53 -0400 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2013-10-14 09:28:50 -0400 |
commit | bf2575c121ca11247ef07fd02b43f7430834f7b1 (patch) | |
tree | 73f45129f7579714c2d4abfc69f322b0deec4e34 /tools/perf/builtin-trace.c | |
parent | 813335b8b27d9ceeb67a073f501ada8b8dde37a7 (diff) |
perf trace: Add summary option to dump syscall statistics
When enabled dumps a summary of all syscalls by task with the usual
statistics -- min, max, average and relative stddev. For example,
make - 26341 : 3344 [ 17.4% ] 0.000 ms
read : 52 0.000 4.802 0.644 30.08
write : 20 0.004 0.036 0.010 21.72
open : 24 0.003 0.046 0.014 23.68
close : 64 0.002 0.055 0.008 22.53
stat : 2714 0.002 0.222 0.004 4.47
fstat : 18 0.001 0.041 0.006 46.26
mmap : 30 0.003 0.009 0.006 5.71
mprotect : 8 0.006 0.039 0.016 32.16
munmap : 12 0.007 0.077 0.020 38.25
brk : 48 0.002 0.014 0.004 10.18
rt_sigaction : 18 0.002 0.002 0.002 2.11
rt_sigprocmask : 60 0.002 0.128 0.010 32.88
access : 2 0.006 0.006 0.006 0.00
pipe : 12 0.004 0.048 0.013 35.98
vfork : 34 0.448 0.980 0.692 3.04
execve : 20 0.000 0.387 0.046 56.66
wait4 : 34 0.017 9923.287 593.221 68.45
fcntl : 8 0.001 0.041 0.013 48.79
getdents : 48 0.002 0.079 0.013 19.62
getcwd : 2 0.005 0.005 0.005 0.00
chdir : 2 0.070 0.070 0.070 0.00
getrlimit : 2 0.045 0.045 0.045 0.00
arch_prctl : 2 0.002 0.002 0.002 0.00
setrlimit : 2 0.002 0.002 0.002 0.00
openat : 94 0.003 0.005 0.003 2.11
Signed-off-by: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Namhyung Kim <namhyung.kim@lge.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/r/1381289214-24885-3-git-send-email-dsahern@gmail.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools/perf/builtin-trace.c')
-rw-r--r-- | tools/perf/builtin-trace.c | 110 |
1 files changed, 98 insertions, 12 deletions
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 5496546b7c5c..d0f91fe755a3 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c | |||
@@ -10,6 +10,7 @@ | |||
10 | #include "util/strlist.h" | 10 | #include "util/strlist.h" |
11 | #include "util/intlist.h" | 11 | #include "util/intlist.h" |
12 | #include "util/thread_map.h" | 12 | #include "util/thread_map.h" |
13 | #include "util/stat.h" | ||
13 | 14 | ||
14 | #include <libaudit.h> | 15 | #include <libaudit.h> |
15 | #include <stdlib.h> | 16 | #include <stdlib.h> |
@@ -909,6 +910,8 @@ struct thread_trace { | |||
909 | int max; | 910 | int max; |
910 | char **table; | 911 | char **table; |
911 | } paths; | 912 | } paths; |
913 | |||
914 | struct intlist *syscall_stats; | ||
912 | }; | 915 | }; |
913 | 916 | ||
914 | static struct thread_trace *thread_trace__new(void) | 917 | static struct thread_trace *thread_trace__new(void) |
@@ -918,6 +921,8 @@ static struct thread_trace *thread_trace__new(void) | |||
918 | if (ttrace) | 921 | if (ttrace) |
919 | ttrace->paths.max = -1; | 922 | ttrace->paths.max = -1; |
920 | 923 | ||
924 | ttrace->syscall_stats = intlist__new(NULL); | ||
925 | |||
921 | return ttrace; | 926 | return ttrace; |
922 | } | 927 | } |
923 | 928 | ||
@@ -964,6 +969,7 @@ struct trace { | |||
964 | struct intlist *pid_list; | 969 | struct intlist *pid_list; |
965 | bool sched; | 970 | bool sched; |
966 | bool multiple_threads; | 971 | bool multiple_threads; |
972 | bool summary; | ||
967 | bool show_comm; | 973 | bool show_comm; |
968 | double duration_filter; | 974 | double duration_filter; |
969 | double runtime_ms; | 975 | double runtime_ms; |
@@ -1291,10 +1297,8 @@ typedef int (*tracepoint_handler)(struct trace *trace, struct perf_evsel *evsel, | |||
1291 | struct perf_sample *sample); | 1297 | struct perf_sample *sample); |
1292 | 1298 | ||
1293 | static struct syscall *trace__syscall_info(struct trace *trace, | 1299 | static struct syscall *trace__syscall_info(struct trace *trace, |
1294 | struct perf_evsel *evsel, | 1300 | struct perf_evsel *evsel, int id) |
1295 | struct perf_sample *sample) | ||
1296 | { | 1301 | { |
1297 | int id = perf_evsel__intval(evsel, sample, "id"); | ||
1298 | 1302 | ||
1299 | if (id < 0) { | 1303 | if (id < 0) { |
1300 | 1304 | ||
@@ -1335,6 +1339,32 @@ out_cant_read: | |||
1335 | return NULL; | 1339 | return NULL; |
1336 | } | 1340 | } |
1337 | 1341 | ||
1342 | static void thread__update_stats(struct thread_trace *ttrace, | ||
1343 | int id, struct perf_sample *sample) | ||
1344 | { | ||
1345 | struct int_node *inode; | ||
1346 | struct stats *stats; | ||
1347 | u64 duration = 0; | ||
1348 | |||
1349 | inode = intlist__findnew(ttrace->syscall_stats, id); | ||
1350 | if (inode == NULL) | ||
1351 | return; | ||
1352 | |||
1353 | stats = inode->priv; | ||
1354 | if (stats == NULL) { | ||
1355 | stats = malloc(sizeof(struct stats)); | ||
1356 | if (stats == NULL) | ||
1357 | return; | ||
1358 | init_stats(stats); | ||
1359 | inode->priv = stats; | ||
1360 | } | ||
1361 | |||
1362 | if (ttrace->entry_time && sample->time > ttrace->entry_time) | ||
1363 | duration = sample->time - ttrace->entry_time; | ||
1364 | |||
1365 | update_stats(stats, duration); | ||
1366 | } | ||
1367 | |||
1338 | static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, | 1368 | static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, |
1339 | struct perf_sample *sample) | 1369 | struct perf_sample *sample) |
1340 | { | 1370 | { |
@@ -1342,7 +1372,8 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, | |||
1342 | void *args; | 1372 | void *args; |
1343 | size_t printed = 0; | 1373 | size_t printed = 0; |
1344 | struct thread *thread; | 1374 | struct thread *thread; |
1345 | struct syscall *sc = trace__syscall_info(trace, evsel, sample); | 1375 | int id = perf_evsel__intval(evsel, sample, "id"); |
1376 | struct syscall *sc = trace__syscall_info(trace, evsel, id); | ||
1346 | struct thread_trace *ttrace; | 1377 | struct thread_trace *ttrace; |
1347 | 1378 | ||
1348 | if (sc == NULL) | 1379 | if (sc == NULL) |
@@ -1394,7 +1425,8 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, | |||
1394 | int ret; | 1425 | int ret; |
1395 | u64 duration = 0; | 1426 | u64 duration = 0; |
1396 | struct thread *thread; | 1427 | struct thread *thread; |
1397 | struct syscall *sc = trace__syscall_info(trace, evsel, sample); | 1428 | int id = perf_evsel__intval(evsel, sample, "id"); |
1429 | struct syscall *sc = trace__syscall_info(trace, evsel, id); | ||
1398 | struct thread_trace *ttrace; | 1430 | struct thread_trace *ttrace; |
1399 | 1431 | ||
1400 | if (sc == NULL) | 1432 | if (sc == NULL) |
@@ -1408,6 +1440,9 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, | |||
1408 | if (ttrace == NULL) | 1440 | if (ttrace == NULL) |
1409 | return -1; | 1441 | return -1; |
1410 | 1442 | ||
1443 | if (trace->summary) | ||
1444 | thread__update_stats(ttrace, id, sample); | ||
1445 | |||
1411 | ret = perf_evsel__intval(evsel, sample, "ret"); | 1446 | ret = perf_evsel__intval(evsel, sample, "ret"); |
1412 | 1447 | ||
1413 | ttrace = thread->priv; | 1448 | ttrace = thread->priv; |
@@ -1574,6 +1609,8 @@ static int trace__record(int argc, const char **argv) | |||
1574 | return cmd_record(i, rec_argv, NULL); | 1609 | return cmd_record(i, rec_argv, NULL); |
1575 | } | 1610 | } |
1576 | 1611 | ||
1612 | static size_t trace__fprintf_thread_summary(struct trace *trace, FILE *fp); | ||
1613 | |||
1577 | static int trace__run(struct trace *trace, int argc, const char **argv) | 1614 | static int trace__run(struct trace *trace, int argc, const char **argv) |
1578 | { | 1615 | { |
1579 | struct perf_evlist *evlist = perf_evlist__new(); | 1616 | struct perf_evlist *evlist = perf_evlist__new(); |
@@ -1703,6 +1740,9 @@ again: | |||
1703 | goto again; | 1740 | goto again; |
1704 | 1741 | ||
1705 | out_unmap_evlist: | 1742 | out_unmap_evlist: |
1743 | if (!err && trace->summary) | ||
1744 | trace__fprintf_thread_summary(trace, trace->output); | ||
1745 | |||
1706 | perf_evlist__munmap(evlist); | 1746 | perf_evlist__munmap(evlist); |
1707 | out_close_evlist: | 1747 | out_close_evlist: |
1708 | perf_evlist__close(evlist); | 1748 | perf_evlist__close(evlist); |
@@ -1798,6 +1838,9 @@ static int trace__replay(struct trace *trace) | |||
1798 | if (err) | 1838 | if (err) |
1799 | pr_err("Failed to process events, error %d", err); | 1839 | pr_err("Failed to process events, error %d", err); |
1800 | 1840 | ||
1841 | else if (trace->summary) | ||
1842 | trace__fprintf_thread_summary(trace, trace->output); | ||
1843 | |||
1801 | out: | 1844 | out: |
1802 | perf_session__delete(session); | 1845 | perf_session__delete(session); |
1803 | 1846 | ||
@@ -1808,10 +1851,53 @@ static size_t trace__fprintf_threads_header(FILE *fp) | |||
1808 | { | 1851 | { |
1809 | size_t printed; | 1852 | size_t printed; |
1810 | 1853 | ||
1811 | printed = fprintf(fp, "\n _____________________________________________________________________\n"); | 1854 | printed = fprintf(fp, "\n _____________________________________________________________________________\n"); |
1812 | printed += fprintf(fp," __) Summary of events (__\n\n"); | 1855 | printed += fprintf(fp, " __) Summary of events (__\n\n"); |
1813 | printed += fprintf(fp," [ task - pid ] [ events ] [ ratio ] [ runtime ]\n"); | 1856 | printed += fprintf(fp, " [ task - pid ] [ events ] [ ratio ] [ runtime ]\n"); |
1814 | printed += fprintf(fp," _____________________________________________________________________\n\n"); | 1857 | printed += fprintf(fp, " syscall count min max avg stddev\n"); |
1858 | printed += fprintf(fp, " msec msec msec %%\n"); | ||
1859 | printed += fprintf(fp, " _____________________________________________________________________________\n\n"); | ||
1860 | |||
1861 | return printed; | ||
1862 | } | ||
1863 | |||
1864 | static size_t thread__dump_stats(struct thread_trace *ttrace, | ||
1865 | struct trace *trace, FILE *fp) | ||
1866 | { | ||
1867 | struct stats *stats; | ||
1868 | size_t printed = 0; | ||
1869 | struct syscall *sc; | ||
1870 | struct int_node *inode = intlist__first(ttrace->syscall_stats); | ||
1871 | |||
1872 | if (inode == NULL) | ||
1873 | return 0; | ||
1874 | |||
1875 | printed += fprintf(fp, "\n"); | ||
1876 | |||
1877 | /* each int_node is a syscall */ | ||
1878 | while (inode) { | ||
1879 | stats = inode->priv; | ||
1880 | if (stats) { | ||
1881 | double min = (double)(stats->min) / NSEC_PER_MSEC; | ||
1882 | double max = (double)(stats->max) / NSEC_PER_MSEC; | ||
1883 | double avg = avg_stats(stats); | ||
1884 | double pct; | ||
1885 | u64 n = (u64) stats->n; | ||
1886 | |||
1887 | pct = avg ? 100.0 * stddev_stats(stats)/avg : 0.0; | ||
1888 | avg /= NSEC_PER_MSEC; | ||
1889 | |||
1890 | sc = &trace->syscalls.table[inode->i]; | ||
1891 | printed += fprintf(fp, "%24s %14s : ", "", sc->name); | ||
1892 | printed += fprintf(fp, "%5" PRIu64 " %8.3f %8.3f", | ||
1893 | n, min, max); | ||
1894 | printed += fprintf(fp, " %8.3f %6.2f\n", avg, pct); | ||
1895 | } | ||
1896 | |||
1897 | inode = intlist__next(inode); | ||
1898 | } | ||
1899 | |||
1900 | printed += fprintf(fp, "\n\n"); | ||
1815 | 1901 | ||
1816 | return printed; | 1902 | return printed; |
1817 | } | 1903 | } |
@@ -1850,6 +1936,7 @@ static int trace__fprintf_one_thread(struct thread *thread, void *priv) | |||
1850 | printed += fprintf(fp, " - %-5d :%11lu [", thread->tid, ttrace->nr_events); | 1936 | printed += fprintf(fp, " - %-5d :%11lu [", thread->tid, ttrace->nr_events); |
1851 | printed += color_fprintf(fp, color, "%5.1f%%", ratio); | 1937 | printed += color_fprintf(fp, color, "%5.1f%%", ratio); |
1852 | printed += fprintf(fp, " ] %10.3f ms\n", ttrace->runtime_ms); | 1938 | printed += fprintf(fp, " ] %10.3f ms\n", ttrace->runtime_ms); |
1939 | printed += thread__dump_stats(ttrace, trace, fp); | ||
1853 | 1940 | ||
1854 | data->printed += printed; | 1941 | data->printed += printed; |
1855 | 1942 | ||
@@ -1953,6 +2040,8 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1953 | OPT_INCR('v', "verbose", &verbose, "be more verbose"), | 2040 | OPT_INCR('v', "verbose", &verbose, "be more verbose"), |
1954 | OPT_BOOLEAN('T', "time", &trace.full_time, | 2041 | OPT_BOOLEAN('T', "time", &trace.full_time, |
1955 | "Show full timestamp, not time relative to first start"), | 2042 | "Show full timestamp, not time relative to first start"), |
2043 | OPT_BOOLEAN(0, "summary", &trace.summary, | ||
2044 | "Show syscall summary with statistics"), | ||
1956 | OPT_END() | 2045 | OPT_END() |
1957 | }; | 2046 | }; |
1958 | int err; | 2047 | int err; |
@@ -2008,9 +2097,6 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) | |||
2008 | else | 2097 | else |
2009 | err = trace__run(&trace, argc, argv); | 2098 | err = trace__run(&trace, argc, argv); |
2010 | 2099 | ||
2011 | if (trace.sched && !err) | ||
2012 | trace__fprintf_thread_summary(&trace, trace.output); | ||
2013 | |||
2014 | out_close: | 2100 | out_close: |
2015 | if (output_name != NULL) | 2101 | if (output_name != NULL) |
2016 | fclose(trace.output); | 2102 | fclose(trace.output); |