diff options
Diffstat (limited to 'tools/perf/builtin-trace.c')
-rw-r--r-- | tools/perf/builtin-trace.c | 266 |
1 files changed, 222 insertions, 44 deletions
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index f954c26de231..a6c375224f46 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c | |||
@@ -1108,6 +1108,7 @@ struct syscall { | |||
1108 | struct event_format *tp_format; | 1108 | struct event_format *tp_format; |
1109 | const char *name; | 1109 | const char *name; |
1110 | bool filtered; | 1110 | bool filtered; |
1111 | bool is_exit; | ||
1111 | struct syscall_fmt *fmt; | 1112 | struct syscall_fmt *fmt; |
1112 | size_t (**arg_scnprintf)(char *bf, size_t size, struct syscall_arg *arg); | 1113 | size_t (**arg_scnprintf)(char *bf, size_t size, struct syscall_arg *arg); |
1113 | void **arg_parm; | 1114 | void **arg_parm; |
@@ -1132,6 +1133,7 @@ struct thread_trace { | |||
1132 | u64 exit_time; | 1133 | u64 exit_time; |
1133 | bool entry_pending; | 1134 | bool entry_pending; |
1134 | unsigned long nr_events; | 1135 | unsigned long nr_events; |
1136 | unsigned long pfmaj, pfmin; | ||
1135 | char *entry_str; | 1137 | char *entry_str; |
1136 | double runtime_ms; | 1138 | double runtime_ms; |
1137 | struct { | 1139 | struct { |
@@ -1177,6 +1179,9 @@ fail: | |||
1177 | return NULL; | 1179 | return NULL; |
1178 | } | 1180 | } |
1179 | 1181 | ||
1182 | #define TRACE_PFMAJ (1 << 0) | ||
1183 | #define TRACE_PFMIN (1 << 1) | ||
1184 | |||
1180 | struct trace { | 1185 | struct trace { |
1181 | struct perf_tool tool; | 1186 | struct perf_tool tool; |
1182 | struct { | 1187 | struct { |
@@ -1211,6 +1216,8 @@ struct trace { | |||
1211 | bool summary_only; | 1216 | bool summary_only; |
1212 | bool show_comm; | 1217 | bool show_comm; |
1213 | bool show_tool_stats; | 1218 | bool show_tool_stats; |
1219 | bool trace_syscalls; | ||
1220 | int trace_pgfaults; | ||
1214 | }; | 1221 | }; |
1215 | 1222 | ||
1216 | static int trace__set_fd_pathname(struct thread *thread, int fd, const char *pathname) | 1223 | static int trace__set_fd_pathname(struct thread *thread, int fd, const char *pathname) |
@@ -1276,11 +1283,11 @@ static const char *thread__fd_path(struct thread *thread, int fd, | |||
1276 | if (fd < 0) | 1283 | if (fd < 0) |
1277 | return NULL; | 1284 | return NULL; |
1278 | 1285 | ||
1279 | if ((fd > ttrace->paths.max || ttrace->paths.table[fd] == NULL)) | 1286 | if ((fd > ttrace->paths.max || ttrace->paths.table[fd] == NULL)) { |
1280 | if (!trace->live) | 1287 | if (!trace->live) |
1281 | return NULL; | 1288 | return NULL; |
1282 | ++trace->stats.proc_getname; | 1289 | ++trace->stats.proc_getname; |
1283 | if (thread__read_fd_path(thread, fd)) { | 1290 | if (thread__read_fd_path(thread, fd)) |
1284 | return NULL; | 1291 | return NULL; |
1285 | } | 1292 | } |
1286 | 1293 | ||
@@ -1473,6 +1480,8 @@ static int trace__read_syscall_info(struct trace *trace, int id) | |||
1473 | if (sc->tp_format == NULL) | 1480 | if (sc->tp_format == NULL) |
1474 | return -1; | 1481 | return -1; |
1475 | 1482 | ||
1483 | sc->is_exit = !strcmp(name, "exit_group") || !strcmp(name, "exit"); | ||
1484 | |||
1476 | return syscall__set_arg_fmts(sc); | 1485 | return syscall__set_arg_fmts(sc); |
1477 | } | 1486 | } |
1478 | 1487 | ||
@@ -1535,6 +1544,7 @@ static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size, | |||
1535 | } | 1544 | } |
1536 | 1545 | ||
1537 | typedef int (*tracepoint_handler)(struct trace *trace, struct perf_evsel *evsel, | 1546 | typedef int (*tracepoint_handler)(struct trace *trace, struct perf_evsel *evsel, |
1547 | union perf_event *event, | ||
1538 | struct perf_sample *sample); | 1548 | struct perf_sample *sample); |
1539 | 1549 | ||
1540 | static struct syscall *trace__syscall_info(struct trace *trace, | 1550 | static struct syscall *trace__syscall_info(struct trace *trace, |
@@ -1607,6 +1617,7 @@ static void thread__update_stats(struct thread_trace *ttrace, | |||
1607 | } | 1617 | } |
1608 | 1618 | ||
1609 | static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, | 1619 | static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, |
1620 | union perf_event *event __maybe_unused, | ||
1610 | struct perf_sample *sample) | 1621 | struct perf_sample *sample) |
1611 | { | 1622 | { |
1612 | char *msg; | 1623 | char *msg; |
@@ -1629,7 +1640,6 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, | |||
1629 | return -1; | 1640 | return -1; |
1630 | 1641 | ||
1631 | args = perf_evsel__sc_tp_ptr(evsel, args, sample); | 1642 | args = perf_evsel__sc_tp_ptr(evsel, args, sample); |
1632 | ttrace = thread->priv; | ||
1633 | 1643 | ||
1634 | if (ttrace->entry_str == NULL) { | 1644 | if (ttrace->entry_str == NULL) { |
1635 | ttrace->entry_str = malloc(1024); | 1645 | ttrace->entry_str = malloc(1024); |
@@ -1644,7 +1654,7 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, | |||
1644 | printed += syscall__scnprintf_args(sc, msg + printed, 1024 - printed, | 1654 | printed += syscall__scnprintf_args(sc, msg + printed, 1024 - printed, |
1645 | args, trace, thread); | 1655 | args, trace, thread); |
1646 | 1656 | ||
1647 | if (!strcmp(sc->name, "exit_group") || !strcmp(sc->name, "exit")) { | 1657 | if (sc->is_exit) { |
1648 | if (!trace->duration_filter && !trace->summary_only) { | 1658 | if (!trace->duration_filter && !trace->summary_only) { |
1649 | trace__fprintf_entry_head(trace, thread, 1, sample->time, trace->output); | 1659 | trace__fprintf_entry_head(trace, thread, 1, sample->time, trace->output); |
1650 | fprintf(trace->output, "%-70s\n", ttrace->entry_str); | 1660 | fprintf(trace->output, "%-70s\n", ttrace->entry_str); |
@@ -1656,6 +1666,7 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, | |||
1656 | } | 1666 | } |
1657 | 1667 | ||
1658 | static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, | 1668 | static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, |
1669 | union perf_event *event __maybe_unused, | ||
1659 | struct perf_sample *sample) | 1670 | struct perf_sample *sample) |
1660 | { | 1671 | { |
1661 | int ret; | 1672 | int ret; |
@@ -1687,8 +1698,6 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, | |||
1687 | ++trace->stats.vfs_getname; | 1698 | ++trace->stats.vfs_getname; |
1688 | } | 1699 | } |
1689 | 1700 | ||
1690 | ttrace = thread->priv; | ||
1691 | |||
1692 | ttrace->exit_time = sample->time; | 1701 | ttrace->exit_time = sample->time; |
1693 | 1702 | ||
1694 | if (ttrace->entry_time) { | 1703 | if (ttrace->entry_time) { |
@@ -1735,6 +1744,7 @@ out: | |||
1735 | } | 1744 | } |
1736 | 1745 | ||
1737 | static int trace__vfs_getname(struct trace *trace, struct perf_evsel *evsel, | 1746 | static int trace__vfs_getname(struct trace *trace, struct perf_evsel *evsel, |
1747 | union perf_event *event __maybe_unused, | ||
1738 | struct perf_sample *sample) | 1748 | struct perf_sample *sample) |
1739 | { | 1749 | { |
1740 | trace->last_vfs_getname = perf_evsel__rawptr(evsel, sample, "pathname"); | 1750 | trace->last_vfs_getname = perf_evsel__rawptr(evsel, sample, "pathname"); |
@@ -1742,6 +1752,7 @@ static int trace__vfs_getname(struct trace *trace, struct perf_evsel *evsel, | |||
1742 | } | 1752 | } |
1743 | 1753 | ||
1744 | static int trace__sched_stat_runtime(struct trace *trace, struct perf_evsel *evsel, | 1754 | static int trace__sched_stat_runtime(struct trace *trace, struct perf_evsel *evsel, |
1755 | union perf_event *event __maybe_unused, | ||
1745 | struct perf_sample *sample) | 1756 | struct perf_sample *sample) |
1746 | { | 1757 | { |
1747 | u64 runtime = perf_evsel__intval(evsel, sample, "runtime"); | 1758 | u64 runtime = perf_evsel__intval(evsel, sample, "runtime"); |
@@ -1768,6 +1779,80 @@ out_dump: | |||
1768 | return 0; | 1779 | return 0; |
1769 | } | 1780 | } |
1770 | 1781 | ||
1782 | static void print_location(FILE *f, struct perf_sample *sample, | ||
1783 | struct addr_location *al, | ||
1784 | bool print_dso, bool print_sym) | ||
1785 | { | ||
1786 | |||
1787 | if ((verbose || print_dso) && al->map) | ||
1788 | fprintf(f, "%s@", al->map->dso->long_name); | ||
1789 | |||
1790 | if ((verbose || print_sym) && al->sym) | ||
1791 | fprintf(f, "%s+0x%" PRIx64, al->sym->name, | ||
1792 | al->addr - al->sym->start); | ||
1793 | else if (al->map) | ||
1794 | fprintf(f, "0x%" PRIx64, al->addr); | ||
1795 | else | ||
1796 | fprintf(f, "0x%" PRIx64, sample->addr); | ||
1797 | } | ||
1798 | |||
1799 | static int trace__pgfault(struct trace *trace, | ||
1800 | struct perf_evsel *evsel, | ||
1801 | union perf_event *event, | ||
1802 | struct perf_sample *sample) | ||
1803 | { | ||
1804 | struct thread *thread; | ||
1805 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | ||
1806 | struct addr_location al; | ||
1807 | char map_type = 'd'; | ||
1808 | struct thread_trace *ttrace; | ||
1809 | |||
1810 | thread = machine__findnew_thread(trace->host, sample->pid, sample->tid); | ||
1811 | ttrace = thread__trace(thread, trace->output); | ||
1812 | if (ttrace == NULL) | ||
1813 | return -1; | ||
1814 | |||
1815 | if (evsel->attr.config == PERF_COUNT_SW_PAGE_FAULTS_MAJ) | ||
1816 | ttrace->pfmaj++; | ||
1817 | else | ||
1818 | ttrace->pfmin++; | ||
1819 | |||
1820 | if (trace->summary_only) | ||
1821 | return 0; | ||
1822 | |||
1823 | thread__find_addr_location(thread, trace->host, cpumode, MAP__FUNCTION, | ||
1824 | sample->ip, &al); | ||
1825 | |||
1826 | trace__fprintf_entry_head(trace, thread, 0, sample->time, trace->output); | ||
1827 | |||
1828 | fprintf(trace->output, "%sfault [", | ||
1829 | evsel->attr.config == PERF_COUNT_SW_PAGE_FAULTS_MAJ ? | ||
1830 | "maj" : "min"); | ||
1831 | |||
1832 | print_location(trace->output, sample, &al, false, true); | ||
1833 | |||
1834 | fprintf(trace->output, "] => "); | ||
1835 | |||
1836 | thread__find_addr_location(thread, trace->host, cpumode, MAP__VARIABLE, | ||
1837 | sample->addr, &al); | ||
1838 | |||
1839 | if (!al.map) { | ||
1840 | thread__find_addr_location(thread, trace->host, cpumode, | ||
1841 | MAP__FUNCTION, sample->addr, &al); | ||
1842 | |||
1843 | if (al.map) | ||
1844 | map_type = 'x'; | ||
1845 | else | ||
1846 | map_type = '?'; | ||
1847 | } | ||
1848 | |||
1849 | print_location(trace->output, sample, &al, true, false); | ||
1850 | |||
1851 | fprintf(trace->output, " (%c%c)\n", map_type, al.level); | ||
1852 | |||
1853 | return 0; | ||
1854 | } | ||
1855 | |||
1771 | static bool skip_sample(struct trace *trace, struct perf_sample *sample) | 1856 | static bool skip_sample(struct trace *trace, struct perf_sample *sample) |
1772 | { | 1857 | { |
1773 | if ((trace->pid_list && intlist__find(trace->pid_list, sample->pid)) || | 1858 | if ((trace->pid_list && intlist__find(trace->pid_list, sample->pid)) || |
@@ -1781,7 +1866,7 @@ static bool skip_sample(struct trace *trace, struct perf_sample *sample) | |||
1781 | } | 1866 | } |
1782 | 1867 | ||
1783 | static int trace__process_sample(struct perf_tool *tool, | 1868 | static int trace__process_sample(struct perf_tool *tool, |
1784 | union perf_event *event __maybe_unused, | 1869 | union perf_event *event, |
1785 | struct perf_sample *sample, | 1870 | struct perf_sample *sample, |
1786 | struct perf_evsel *evsel, | 1871 | struct perf_evsel *evsel, |
1787 | struct machine *machine __maybe_unused) | 1872 | struct machine *machine __maybe_unused) |
@@ -1799,7 +1884,7 @@ static int trace__process_sample(struct perf_tool *tool, | |||
1799 | 1884 | ||
1800 | if (handler) { | 1885 | if (handler) { |
1801 | ++trace->nr_events; | 1886 | ++trace->nr_events; |
1802 | handler(trace, evsel, sample); | 1887 | handler(trace, evsel, event, sample); |
1803 | } | 1888 | } |
1804 | 1889 | ||
1805 | return err; | 1890 | return err; |
@@ -1826,7 +1911,7 @@ static int parse_target_str(struct trace *trace) | |||
1826 | return 0; | 1911 | return 0; |
1827 | } | 1912 | } |
1828 | 1913 | ||
1829 | static int trace__record(int argc, const char **argv) | 1914 | static int trace__record(struct trace *trace, int argc, const char **argv) |
1830 | { | 1915 | { |
1831 | unsigned int rec_argc, i, j; | 1916 | unsigned int rec_argc, i, j; |
1832 | const char **rec_argv; | 1917 | const char **rec_argv; |
@@ -1835,34 +1920,54 @@ static int trace__record(int argc, const char **argv) | |||
1835 | "-R", | 1920 | "-R", |
1836 | "-m", "1024", | 1921 | "-m", "1024", |
1837 | "-c", "1", | 1922 | "-c", "1", |
1838 | "-e", | ||
1839 | }; | 1923 | }; |
1840 | 1924 | ||
1925 | const char * const sc_args[] = { "-e", }; | ||
1926 | unsigned int sc_args_nr = ARRAY_SIZE(sc_args); | ||
1927 | const char * const majpf_args[] = { "-e", "major-faults" }; | ||
1928 | unsigned int majpf_args_nr = ARRAY_SIZE(majpf_args); | ||
1929 | const char * const minpf_args[] = { "-e", "minor-faults" }; | ||
1930 | unsigned int minpf_args_nr = ARRAY_SIZE(minpf_args); | ||
1931 | |||
1841 | /* +1 is for the event string below */ | 1932 | /* +1 is for the event string below */ |
1842 | rec_argc = ARRAY_SIZE(record_args) + 1 + argc; | 1933 | rec_argc = ARRAY_SIZE(record_args) + sc_args_nr + 1 + |
1934 | majpf_args_nr + minpf_args_nr + argc; | ||
1843 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); | 1935 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); |
1844 | 1936 | ||
1845 | if (rec_argv == NULL) | 1937 | if (rec_argv == NULL) |
1846 | return -ENOMEM; | 1938 | return -ENOMEM; |
1847 | 1939 | ||
1940 | j = 0; | ||
1848 | for (i = 0; i < ARRAY_SIZE(record_args); i++) | 1941 | for (i = 0; i < ARRAY_SIZE(record_args); i++) |
1849 | rec_argv[i] = record_args[i]; | 1942 | rec_argv[j++] = record_args[i]; |
1850 | 1943 | ||
1851 | /* event string may be different for older kernels - e.g., RHEL6 */ | 1944 | if (trace->trace_syscalls) { |
1852 | if (is_valid_tracepoint("raw_syscalls:sys_enter")) | 1945 | for (i = 0; i < sc_args_nr; i++) |
1853 | rec_argv[i] = "raw_syscalls:sys_enter,raw_syscalls:sys_exit"; | 1946 | rec_argv[j++] = sc_args[i]; |
1854 | else if (is_valid_tracepoint("syscalls:sys_enter")) | 1947 | |
1855 | rec_argv[i] = "syscalls:sys_enter,syscalls:sys_exit"; | 1948 | /* event string may be different for older kernels - e.g., RHEL6 */ |
1856 | else { | 1949 | if (is_valid_tracepoint("raw_syscalls:sys_enter")) |
1857 | pr_err("Neither raw_syscalls nor syscalls events exist.\n"); | 1950 | rec_argv[j++] = "raw_syscalls:sys_enter,raw_syscalls:sys_exit"; |
1858 | return -1; | 1951 | else if (is_valid_tracepoint("syscalls:sys_enter")) |
1952 | rec_argv[j++] = "syscalls:sys_enter,syscalls:sys_exit"; | ||
1953 | else { | ||
1954 | pr_err("Neither raw_syscalls nor syscalls events exist.\n"); | ||
1955 | return -1; | ||
1956 | } | ||
1859 | } | 1957 | } |
1860 | i++; | ||
1861 | 1958 | ||
1862 | for (j = 0; j < (unsigned int)argc; j++, i++) | 1959 | if (trace->trace_pgfaults & TRACE_PFMAJ) |
1863 | rec_argv[i] = argv[j]; | 1960 | for (i = 0; i < majpf_args_nr; i++) |
1961 | rec_argv[j++] = majpf_args[i]; | ||
1962 | |||
1963 | if (trace->trace_pgfaults & TRACE_PFMIN) | ||
1964 | for (i = 0; i < minpf_args_nr; i++) | ||
1965 | rec_argv[j++] = minpf_args[i]; | ||
1966 | |||
1967 | for (i = 0; i < (unsigned int)argc; i++) | ||
1968 | rec_argv[j++] = argv[i]; | ||
1864 | 1969 | ||
1865 | return cmd_record(i, rec_argv, NULL); | 1970 | return cmd_record(j, rec_argv, NULL); |
1866 | } | 1971 | } |
1867 | 1972 | ||
1868 | static size_t trace__fprintf_thread_summary(struct trace *trace, FILE *fp); | 1973 | static size_t trace__fprintf_thread_summary(struct trace *trace, FILE *fp); |
@@ -1882,6 +1987,30 @@ static void perf_evlist__add_vfs_getname(struct perf_evlist *evlist) | |||
1882 | perf_evlist__add(evlist, evsel); | 1987 | perf_evlist__add(evlist, evsel); |
1883 | } | 1988 | } |
1884 | 1989 | ||
1990 | static int perf_evlist__add_pgfault(struct perf_evlist *evlist, | ||
1991 | u64 config) | ||
1992 | { | ||
1993 | struct perf_evsel *evsel; | ||
1994 | struct perf_event_attr attr = { | ||
1995 | .type = PERF_TYPE_SOFTWARE, | ||
1996 | .mmap_data = 1, | ||
1997 | }; | ||
1998 | |||
1999 | attr.config = config; | ||
2000 | attr.sample_period = 1; | ||
2001 | |||
2002 | event_attr_init(&attr); | ||
2003 | |||
2004 | evsel = perf_evsel__new(&attr); | ||
2005 | if (!evsel) | ||
2006 | return -ENOMEM; | ||
2007 | |||
2008 | evsel->handler = trace__pgfault; | ||
2009 | perf_evlist__add(evlist, evsel); | ||
2010 | |||
2011 | return 0; | ||
2012 | } | ||
2013 | |||
1885 | static int trace__run(struct trace *trace, int argc, const char **argv) | 2014 | static int trace__run(struct trace *trace, int argc, const char **argv) |
1886 | { | 2015 | { |
1887 | struct perf_evlist *evlist = perf_evlist__new(); | 2016 | struct perf_evlist *evlist = perf_evlist__new(); |
@@ -1897,10 +2026,21 @@ static int trace__run(struct trace *trace, int argc, const char **argv) | |||
1897 | goto out; | 2026 | goto out; |
1898 | } | 2027 | } |
1899 | 2028 | ||
1900 | if (perf_evlist__add_syscall_newtp(evlist, trace__sys_enter, trace__sys_exit)) | 2029 | if (trace->trace_syscalls && |
2030 | perf_evlist__add_syscall_newtp(evlist, trace__sys_enter, | ||
2031 | trace__sys_exit)) | ||
1901 | goto out_error_tp; | 2032 | goto out_error_tp; |
1902 | 2033 | ||
1903 | perf_evlist__add_vfs_getname(evlist); | 2034 | if (trace->trace_syscalls) |
2035 | perf_evlist__add_vfs_getname(evlist); | ||
2036 | |||
2037 | if ((trace->trace_pgfaults & TRACE_PFMAJ) && | ||
2038 | perf_evlist__add_pgfault(evlist, PERF_COUNT_SW_PAGE_FAULTS_MAJ)) | ||
2039 | goto out_error_tp; | ||
2040 | |||
2041 | if ((trace->trace_pgfaults & TRACE_PFMIN) && | ||
2042 | perf_evlist__add_pgfault(evlist, PERF_COUNT_SW_PAGE_FAULTS_MIN)) | ||
2043 | goto out_error_tp; | ||
1904 | 2044 | ||
1905 | if (trace->sched && | 2045 | if (trace->sched && |
1906 | perf_evlist__add_newtp(evlist, "sched", "sched_stat_runtime", | 2046 | perf_evlist__add_newtp(evlist, "sched", "sched_stat_runtime", |
@@ -1982,7 +2122,8 @@ again: | |||
1982 | goto next_event; | 2122 | goto next_event; |
1983 | } | 2123 | } |
1984 | 2124 | ||
1985 | if (sample.raw_data == NULL) { | 2125 | if (evsel->attr.type == PERF_TYPE_TRACEPOINT && |
2126 | sample.raw_data == NULL) { | ||
1986 | fprintf(trace->output, "%s sample with no payload for tid: %d, cpu %d, raw_size=%d, skipping...\n", | 2127 | fprintf(trace->output, "%s sample with no payload for tid: %d, cpu %d, raw_size=%d, skipping...\n", |
1987 | perf_evsel__name(evsel), sample.tid, | 2128 | perf_evsel__name(evsel), sample.tid, |
1988 | sample.cpu, sample.raw_size); | 2129 | sample.cpu, sample.raw_size); |
@@ -1990,7 +2131,7 @@ again: | |||
1990 | } | 2131 | } |
1991 | 2132 | ||
1992 | handler = evsel->handler; | 2133 | handler = evsel->handler; |
1993 | handler(trace, evsel, &sample); | 2134 | handler(trace, evsel, event, &sample); |
1994 | next_event: | 2135 | next_event: |
1995 | perf_evlist__mmap_consume(evlist, i); | 2136 | perf_evlist__mmap_consume(evlist, i); |
1996 | 2137 | ||
@@ -2093,13 +2234,10 @@ static int trace__replay(struct trace *trace) | |||
2093 | if (evsel == NULL) | 2234 | if (evsel == NULL) |
2094 | evsel = perf_evlist__find_tracepoint_by_name(session->evlist, | 2235 | evsel = perf_evlist__find_tracepoint_by_name(session->evlist, |
2095 | "syscalls:sys_enter"); | 2236 | "syscalls:sys_enter"); |
2096 | if (evsel == NULL) { | ||
2097 | pr_err("Data file does not have raw_syscalls:sys_enter event\n"); | ||
2098 | goto out; | ||
2099 | } | ||
2100 | 2237 | ||
2101 | if (perf_evsel__init_syscall_tp(evsel, trace__sys_enter) < 0 || | 2238 | if (evsel && |
2102 | perf_evsel__init_sc_tp_ptr_field(evsel, args)) { | 2239 | (perf_evsel__init_syscall_tp(evsel, trace__sys_enter) < 0 || |
2240 | perf_evsel__init_sc_tp_ptr_field(evsel, args))) { | ||
2103 | pr_err("Error during initialize raw_syscalls:sys_enter event\n"); | 2241 | pr_err("Error during initialize raw_syscalls:sys_enter event\n"); |
2104 | goto out; | 2242 | goto out; |
2105 | } | 2243 | } |
@@ -2109,15 +2247,19 @@ static int trace__replay(struct trace *trace) | |||
2109 | if (evsel == NULL) | 2247 | if (evsel == NULL) |
2110 | evsel = perf_evlist__find_tracepoint_by_name(session->evlist, | 2248 | evsel = perf_evlist__find_tracepoint_by_name(session->evlist, |
2111 | "syscalls:sys_exit"); | 2249 | "syscalls:sys_exit"); |
2112 | if (evsel == NULL) { | 2250 | if (evsel && |
2113 | pr_err("Data file does not have raw_syscalls:sys_exit event\n"); | 2251 | (perf_evsel__init_syscall_tp(evsel, trace__sys_exit) < 0 || |
2252 | perf_evsel__init_sc_tp_uint_field(evsel, ret))) { | ||
2253 | pr_err("Error during initialize raw_syscalls:sys_exit event\n"); | ||
2114 | goto out; | 2254 | goto out; |
2115 | } | 2255 | } |
2116 | 2256 | ||
2117 | if (perf_evsel__init_syscall_tp(evsel, trace__sys_exit) < 0 || | 2257 | evlist__for_each(session->evlist, evsel) { |
2118 | perf_evsel__init_sc_tp_uint_field(evsel, ret)) { | 2258 | if (evsel->attr.type == PERF_TYPE_SOFTWARE && |
2119 | pr_err("Error during initialize raw_syscalls:sys_exit event\n"); | 2259 | (evsel->attr.config == PERF_COUNT_SW_PAGE_FAULTS_MAJ || |
2120 | goto out; | 2260 | evsel->attr.config == PERF_COUNT_SW_PAGE_FAULTS_MIN || |
2261 | evsel->attr.config == PERF_COUNT_SW_PAGE_FAULTS)) | ||
2262 | evsel->handler = trace__pgfault; | ||
2121 | } | 2263 | } |
2122 | 2264 | ||
2123 | err = parse_target_str(trace); | 2265 | err = parse_target_str(trace); |
@@ -2217,6 +2359,10 @@ static int trace__fprintf_one_thread(struct thread *thread, void *priv) | |||
2217 | printed += fprintf(fp, " %s (%d), ", thread__comm_str(thread), thread->tid); | 2359 | printed += fprintf(fp, " %s (%d), ", thread__comm_str(thread), thread->tid); |
2218 | printed += fprintf(fp, "%lu events, ", ttrace->nr_events); | 2360 | printed += fprintf(fp, "%lu events, ", ttrace->nr_events); |
2219 | printed += fprintf(fp, "%.1f%%", ratio); | 2361 | printed += fprintf(fp, "%.1f%%", ratio); |
2362 | if (ttrace->pfmaj) | ||
2363 | printed += fprintf(fp, ", %lu majfaults", ttrace->pfmaj); | ||
2364 | if (ttrace->pfmin) | ||
2365 | printed += fprintf(fp, ", %lu minfaults", ttrace->pfmin); | ||
2220 | printed += fprintf(fp, ", %.3f msec\n", ttrace->runtime_ms); | 2366 | printed += fprintf(fp, ", %.3f msec\n", ttrace->runtime_ms); |
2221 | printed += thread__dump_stats(ttrace, trace, fp); | 2367 | printed += thread__dump_stats(ttrace, trace, fp); |
2222 | 2368 | ||
@@ -2264,6 +2410,23 @@ static int trace__open_output(struct trace *trace, const char *filename) | |||
2264 | return trace->output == NULL ? -errno : 0; | 2410 | return trace->output == NULL ? -errno : 0; |
2265 | } | 2411 | } |
2266 | 2412 | ||
2413 | static int parse_pagefaults(const struct option *opt, const char *str, | ||
2414 | int unset __maybe_unused) | ||
2415 | { | ||
2416 | int *trace_pgfaults = opt->value; | ||
2417 | |||
2418 | if (strcmp(str, "all") == 0) | ||
2419 | *trace_pgfaults |= TRACE_PFMAJ | TRACE_PFMIN; | ||
2420 | else if (strcmp(str, "maj") == 0) | ||
2421 | *trace_pgfaults |= TRACE_PFMAJ; | ||
2422 | else if (strcmp(str, "min") == 0) | ||
2423 | *trace_pgfaults |= TRACE_PFMIN; | ||
2424 | else | ||
2425 | return -1; | ||
2426 | |||
2427 | return 0; | ||
2428 | } | ||
2429 | |||
2267 | int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) | 2430 | int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) |
2268 | { | 2431 | { |
2269 | const char * const trace_usage[] = { | 2432 | const char * const trace_usage[] = { |
@@ -2293,6 +2456,7 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) | |||
2293 | }, | 2456 | }, |
2294 | .output = stdout, | 2457 | .output = stdout, |
2295 | .show_comm = true, | 2458 | .show_comm = true, |
2459 | .trace_syscalls = true, | ||
2296 | }; | 2460 | }; |
2297 | const char *output_name = NULL; | 2461 | const char *output_name = NULL; |
2298 | const char *ev_qualifier_str = NULL; | 2462 | const char *ev_qualifier_str = NULL; |
@@ -2330,20 +2494,34 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) | |||
2330 | "Show only syscall summary with statistics"), | 2494 | "Show only syscall summary with statistics"), |
2331 | OPT_BOOLEAN('S', "with-summary", &trace.summary, | 2495 | OPT_BOOLEAN('S', "with-summary", &trace.summary, |
2332 | "Show all syscalls and summary with statistics"), | 2496 | "Show all syscalls and summary with statistics"), |
2497 | OPT_CALLBACK_DEFAULT('F', "pf", &trace.trace_pgfaults, "all|maj|min", | ||
2498 | "Trace pagefaults", parse_pagefaults, "maj"), | ||
2499 | OPT_BOOLEAN(0, "syscalls", &trace.trace_syscalls, "Trace syscalls"), | ||
2333 | OPT_END() | 2500 | OPT_END() |
2334 | }; | 2501 | }; |
2335 | int err; | 2502 | int err; |
2336 | char bf[BUFSIZ]; | 2503 | char bf[BUFSIZ]; |
2337 | 2504 | ||
2338 | if ((argc > 1) && (strcmp(argv[1], "record") == 0)) | 2505 | argc = parse_options(argc, argv, trace_options, trace_usage, |
2339 | return trace__record(argc-2, &argv[2]); | 2506 | PARSE_OPT_STOP_AT_NON_OPTION); |
2340 | 2507 | ||
2341 | argc = parse_options(argc, argv, trace_options, trace_usage, 0); | 2508 | if (trace.trace_pgfaults) { |
2509 | trace.opts.sample_address = true; | ||
2510 | trace.opts.sample_time = true; | ||
2511 | } | ||
2512 | |||
2513 | if ((argc >= 1) && (strcmp(argv[0], "record") == 0)) | ||
2514 | return trace__record(&trace, argc-1, &argv[1]); | ||
2342 | 2515 | ||
2343 | /* summary_only implies summary option, but don't overwrite summary if set */ | 2516 | /* summary_only implies summary option, but don't overwrite summary if set */ |
2344 | if (trace.summary_only) | 2517 | if (trace.summary_only) |
2345 | trace.summary = trace.summary_only; | 2518 | trace.summary = trace.summary_only; |
2346 | 2519 | ||
2520 | if (!trace.trace_syscalls && !trace.trace_pgfaults) { | ||
2521 | pr_err("Please specify something to trace.\n"); | ||
2522 | return -1; | ||
2523 | } | ||
2524 | |||
2347 | if (output_name != NULL) { | 2525 | if (output_name != NULL) { |
2348 | err = trace__open_output(&trace, output_name); | 2526 | err = trace__open_output(&trace, output_name); |
2349 | if (err < 0) { | 2527 | if (err < 0) { |