diff options
Diffstat (limited to 'tools/perf/builtin-trace.c')
-rw-r--r-- | tools/perf/builtin-trace.c | 143 |
1 files changed, 80 insertions, 63 deletions
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 329b7832b5da..6b230af940e2 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c | |||
@@ -149,21 +149,32 @@ static void perf_evsel__delete_priv(struct perf_evsel *evsel) | |||
149 | perf_evsel__delete(evsel); | 149 | perf_evsel__delete(evsel); |
150 | } | 150 | } |
151 | 151 | ||
152 | static struct perf_evsel *perf_evsel__syscall_newtp(const char *direction, | 152 | static int perf_evsel__init_syscall_tp(struct perf_evsel *evsel, void *handler) |
153 | void *handler, int idx) | ||
154 | { | 153 | { |
155 | struct perf_evsel *evsel = perf_evsel__newtp("raw_syscalls", direction, idx); | 154 | evsel->priv = malloc(sizeof(struct syscall_tp)); |
156 | 155 | if (evsel->priv != NULL) { | |
157 | if (evsel) { | ||
158 | evsel->priv = malloc(sizeof(struct syscall_tp)); | ||
159 | |||
160 | if (evsel->priv == NULL) | ||
161 | goto out_delete; | ||
162 | |||
163 | if (perf_evsel__init_sc_tp_uint_field(evsel, id)) | 156 | if (perf_evsel__init_sc_tp_uint_field(evsel, id)) |
164 | goto out_delete; | 157 | goto out_delete; |
165 | 158 | ||
166 | evsel->handler = handler; | 159 | evsel->handler = handler; |
160 | return 0; | ||
161 | } | ||
162 | |||
163 | return -ENOMEM; | ||
164 | |||
165 | out_delete: | ||
166 | free(evsel->priv); | ||
167 | evsel->priv = NULL; | ||
168 | return -ENOENT; | ||
169 | } | ||
170 | |||
171 | static struct perf_evsel *perf_evsel__syscall_newtp(const char *direction, void *handler) | ||
172 | { | ||
173 | struct perf_evsel *evsel = perf_evsel__newtp("raw_syscalls", direction); | ||
174 | |||
175 | if (evsel) { | ||
176 | if (perf_evsel__init_syscall_tp(evsel, handler)) | ||
177 | goto out_delete; | ||
167 | } | 178 | } |
168 | 179 | ||
169 | return evsel; | 180 | return evsel; |
@@ -186,17 +197,16 @@ static int perf_evlist__add_syscall_newtp(struct perf_evlist *evlist, | |||
186 | void *sys_exit_handler) | 197 | void *sys_exit_handler) |
187 | { | 198 | { |
188 | int ret = -1; | 199 | int ret = -1; |
189 | int idx = evlist->nr_entries; | ||
190 | struct perf_evsel *sys_enter, *sys_exit; | 200 | struct perf_evsel *sys_enter, *sys_exit; |
191 | 201 | ||
192 | sys_enter = perf_evsel__syscall_newtp("sys_enter", sys_enter_handler, idx++); | 202 | sys_enter = perf_evsel__syscall_newtp("sys_enter", sys_enter_handler); |
193 | if (sys_enter == NULL) | 203 | if (sys_enter == NULL) |
194 | goto out; | 204 | goto out; |
195 | 205 | ||
196 | if (perf_evsel__init_sc_tp_ptr_field(sys_enter, args)) | 206 | if (perf_evsel__init_sc_tp_ptr_field(sys_enter, args)) |
197 | goto out_delete_sys_enter; | 207 | goto out_delete_sys_enter; |
198 | 208 | ||
199 | sys_exit = perf_evsel__syscall_newtp("sys_exit", sys_exit_handler, idx++); | 209 | sys_exit = perf_evsel__syscall_newtp("sys_exit", sys_exit_handler); |
200 | if (sys_exit == NULL) | 210 | if (sys_exit == NULL) |
201 | goto out_delete_sys_enter; | 211 | goto out_delete_sys_enter; |
202 | 212 | ||
@@ -953,7 +963,8 @@ static struct syscall_fmt { | |||
953 | { .name = "mmap", .hexret = true, | 963 | { .name = "mmap", .hexret = true, |
954 | .arg_scnprintf = { [0] = SCA_HEX, /* addr */ | 964 | .arg_scnprintf = { [0] = SCA_HEX, /* addr */ |
955 | [2] = SCA_MMAP_PROT, /* prot */ | 965 | [2] = SCA_MMAP_PROT, /* prot */ |
956 | [3] = SCA_MMAP_FLAGS, /* flags */ }, }, | 966 | [3] = SCA_MMAP_FLAGS, /* flags */ |
967 | [4] = SCA_FD, /* fd */ }, }, | ||
957 | { .name = "mprotect", .errmsg = true, | 968 | { .name = "mprotect", .errmsg = true, |
958 | .arg_scnprintf = { [0] = SCA_HEX, /* start */ | 969 | .arg_scnprintf = { [0] = SCA_HEX, /* start */ |
959 | [2] = SCA_MMAP_PROT, /* prot */ }, }, | 970 | [2] = SCA_MMAP_PROT, /* prot */ }, }, |
@@ -1157,6 +1168,7 @@ struct trace { | |||
1157 | bool sched; | 1168 | bool sched; |
1158 | bool multiple_threads; | 1169 | bool multiple_threads; |
1159 | bool summary; | 1170 | bool summary; |
1171 | bool summary_only; | ||
1160 | bool show_comm; | 1172 | bool show_comm; |
1161 | bool show_tool_stats; | 1173 | bool show_tool_stats; |
1162 | double duration_filter; | 1174 | double duration_filter; |
@@ -1342,15 +1354,8 @@ static int trace__symbols_init(struct trace *trace, struct perf_evlist *evlist) | |||
1342 | if (trace->host == NULL) | 1354 | if (trace->host == NULL) |
1343 | return -ENOMEM; | 1355 | return -ENOMEM; |
1344 | 1356 | ||
1345 | if (perf_target__has_task(&trace->opts.target)) { | 1357 | err = __machine__synthesize_threads(trace->host, &trace->tool, &trace->opts.target, |
1346 | err = perf_event__synthesize_thread_map(&trace->tool, evlist->threads, | 1358 | evlist->threads, trace__tool_process, false); |
1347 | trace__tool_process, | ||
1348 | trace->host); | ||
1349 | } else { | ||
1350 | err = perf_event__synthesize_threads(&trace->tool, trace__tool_process, | ||
1351 | trace->host); | ||
1352 | } | ||
1353 | |||
1354 | if (err) | 1359 | if (err) |
1355 | symbol__exit(); | 1360 | symbol__exit(); |
1356 | 1361 | ||
@@ -1607,7 +1612,7 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, | |||
1607 | args, trace, thread); | 1612 | args, trace, thread); |
1608 | 1613 | ||
1609 | if (!strcmp(sc->name, "exit_group") || !strcmp(sc->name, "exit")) { | 1614 | if (!strcmp(sc->name, "exit_group") || !strcmp(sc->name, "exit")) { |
1610 | if (!trace->duration_filter) { | 1615 | if (!trace->duration_filter && !trace->summary_only) { |
1611 | trace__fprintf_entry_head(trace, thread, 1, sample->time, trace->output); | 1616 | trace__fprintf_entry_head(trace, thread, 1, sample->time, trace->output); |
1612 | fprintf(trace->output, "%-70s\n", ttrace->entry_str); | 1617 | fprintf(trace->output, "%-70s\n", ttrace->entry_str); |
1613 | } | 1618 | } |
@@ -1660,6 +1665,9 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, | |||
1660 | } else if (trace->duration_filter) | 1665 | } else if (trace->duration_filter) |
1661 | goto out; | 1666 | goto out; |
1662 | 1667 | ||
1668 | if (trace->summary_only) | ||
1669 | goto out; | ||
1670 | |||
1663 | trace__fprintf_entry_head(trace, thread, duration, sample->time, trace->output); | 1671 | trace__fprintf_entry_head(trace, thread, duration, sample->time, trace->output); |
1664 | 1672 | ||
1665 | if (ttrace->entry_pending) { | 1673 | if (ttrace->entry_pending) { |
@@ -1762,16 +1770,6 @@ static int trace__process_sample(struct perf_tool *tool, | |||
1762 | return err; | 1770 | return err; |
1763 | } | 1771 | } |
1764 | 1772 | ||
1765 | static bool | ||
1766 | perf_session__has_tp(struct perf_session *session, const char *name) | ||
1767 | { | ||
1768 | struct perf_evsel *evsel; | ||
1769 | |||
1770 | evsel = perf_evlist__find_tracepoint_by_name(session->evlist, name); | ||
1771 | |||
1772 | return evsel != NULL; | ||
1773 | } | ||
1774 | |||
1775 | static int parse_target_str(struct trace *trace) | 1773 | static int parse_target_str(struct trace *trace) |
1776 | { | 1774 | { |
1777 | if (trace->opts.target.pid) { | 1775 | if (trace->opts.target.pid) { |
@@ -1824,8 +1822,7 @@ static size_t trace__fprintf_thread_summary(struct trace *trace, FILE *fp); | |||
1824 | 1822 | ||
1825 | static void perf_evlist__add_vfs_getname(struct perf_evlist *evlist) | 1823 | static void perf_evlist__add_vfs_getname(struct perf_evlist *evlist) |
1826 | { | 1824 | { |
1827 | struct perf_evsel *evsel = perf_evsel__newtp("probe", "vfs_getname", | 1825 | struct perf_evsel *evsel = perf_evsel__newtp("probe", "vfs_getname"); |
1828 | evlist->nr_entries); | ||
1829 | if (evsel == NULL) | 1826 | if (evsel == NULL) |
1830 | return; | 1827 | return; |
1831 | 1828 | ||
@@ -2009,8 +2006,6 @@ out_error: | |||
2009 | static int trace__replay(struct trace *trace) | 2006 | static int trace__replay(struct trace *trace) |
2010 | { | 2007 | { |
2011 | const struct perf_evsel_str_handler handlers[] = { | 2008 | const struct perf_evsel_str_handler handlers[] = { |
2012 | { "raw_syscalls:sys_enter", trace__sys_enter, }, | ||
2013 | { "raw_syscalls:sys_exit", trace__sys_exit, }, | ||
2014 | { "probe:vfs_getname", trace__vfs_getname, }, | 2009 | { "probe:vfs_getname", trace__vfs_getname, }, |
2015 | }; | 2010 | }; |
2016 | struct perf_data_file file = { | 2011 | struct perf_data_file file = { |
@@ -2018,6 +2013,7 @@ static int trace__replay(struct trace *trace) | |||
2018 | .mode = PERF_DATA_MODE_READ, | 2013 | .mode = PERF_DATA_MODE_READ, |
2019 | }; | 2014 | }; |
2020 | struct perf_session *session; | 2015 | struct perf_session *session; |
2016 | struct perf_evsel *evsel; | ||
2021 | int err = -1; | 2017 | int err = -1; |
2022 | 2018 | ||
2023 | trace->tool.sample = trace__process_sample; | 2019 | trace->tool.sample = trace__process_sample; |
@@ -2049,13 +2045,29 @@ static int trace__replay(struct trace *trace) | |||
2049 | if (err) | 2045 | if (err) |
2050 | goto out; | 2046 | goto out; |
2051 | 2047 | ||
2052 | if (!perf_session__has_tp(session, "raw_syscalls:sys_enter")) { | 2048 | evsel = perf_evlist__find_tracepoint_by_name(session->evlist, |
2053 | pr_err("Data file does not have raw_syscalls:sys_enter events\n"); | 2049 | "raw_syscalls:sys_enter"); |
2050 | if (evsel == NULL) { | ||
2051 | pr_err("Data file does not have raw_syscalls:sys_enter event\n"); | ||
2054 | goto out; | 2052 | goto out; |
2055 | } | 2053 | } |
2056 | 2054 | ||
2057 | if (!perf_session__has_tp(session, "raw_syscalls:sys_exit")) { | 2055 | if (perf_evsel__init_syscall_tp(evsel, trace__sys_enter) < 0 || |
2058 | pr_err("Data file does not have raw_syscalls:sys_exit events\n"); | 2056 | perf_evsel__init_sc_tp_ptr_field(evsel, args)) { |
2057 | pr_err("Error during initialize raw_syscalls:sys_enter event\n"); | ||
2058 | goto out; | ||
2059 | } | ||
2060 | |||
2061 | evsel = perf_evlist__find_tracepoint_by_name(session->evlist, | ||
2062 | "raw_syscalls:sys_exit"); | ||
2063 | if (evsel == NULL) { | ||
2064 | pr_err("Data file does not have raw_syscalls:sys_exit event\n"); | ||
2065 | goto out; | ||
2066 | } | ||
2067 | |||
2068 | if (perf_evsel__init_syscall_tp(evsel, trace__sys_exit) < 0 || | ||
2069 | perf_evsel__init_sc_tp_uint_field(evsel, ret)) { | ||
2070 | pr_err("Error during initialize raw_syscalls:sys_exit event\n"); | ||
2059 | goto out; | 2071 | goto out; |
2060 | } | 2072 | } |
2061 | 2073 | ||
@@ -2082,12 +2094,7 @@ static size_t trace__fprintf_threads_header(FILE *fp) | |||
2082 | { | 2094 | { |
2083 | size_t printed; | 2095 | size_t printed; |
2084 | 2096 | ||
2085 | printed = fprintf(fp, "\n _____________________________________________________________________________\n"); | 2097 | printed = fprintf(fp, "\n Summary of events:\n\n"); |
2086 | printed += fprintf(fp, " __) Summary of events (__\n\n"); | ||
2087 | printed += fprintf(fp, " [ task - pid ] [ events ] [ ratio ] [ runtime ]\n"); | ||
2088 | printed += fprintf(fp, " syscall count min max avg stddev\n"); | ||
2089 | printed += fprintf(fp, " msec msec msec %%\n"); | ||
2090 | printed += fprintf(fp, " _____________________________________________________________________________\n\n"); | ||
2091 | 2098 | ||
2092 | return printed; | 2099 | return printed; |
2093 | } | 2100 | } |
@@ -2105,6 +2112,10 @@ static size_t thread__dump_stats(struct thread_trace *ttrace, | |||
2105 | 2112 | ||
2106 | printed += fprintf(fp, "\n"); | 2113 | printed += fprintf(fp, "\n"); |
2107 | 2114 | ||
2115 | printed += fprintf(fp, " msec/call\n"); | ||
2116 | printed += fprintf(fp, " syscall calls min avg max stddev\n"); | ||
2117 | printed += fprintf(fp, " --------------- -------- -------- -------- -------- ------\n"); | ||
2118 | |||
2108 | /* each int_node is a syscall */ | 2119 | /* each int_node is a syscall */ |
2109 | while (inode) { | 2120 | while (inode) { |
2110 | stats = inode->priv; | 2121 | stats = inode->priv; |
@@ -2119,10 +2130,10 @@ static size_t thread__dump_stats(struct thread_trace *ttrace, | |||
2119 | avg /= NSEC_PER_MSEC; | 2130 | avg /= NSEC_PER_MSEC; |
2120 | 2131 | ||
2121 | sc = &trace->syscalls.table[inode->i]; | 2132 | sc = &trace->syscalls.table[inode->i]; |
2122 | printed += fprintf(fp, "%24s %14s : ", "", sc->name); | 2133 | printed += fprintf(fp, " %-15s", sc->name); |
2123 | printed += fprintf(fp, "%5" PRIu64 " %8.3f %8.3f", | 2134 | printed += fprintf(fp, " %8" PRIu64 " %8.3f %8.3f", |
2124 | n, min, max); | 2135 | n, min, avg); |
2125 | printed += fprintf(fp, " %8.3f %6.2f\n", avg, pct); | 2136 | printed += fprintf(fp, " %8.3f %6.2f\n", max, pct); |
2126 | } | 2137 | } |
2127 | 2138 | ||
2128 | inode = intlist__next(inode); | 2139 | inode = intlist__next(inode); |
@@ -2163,10 +2174,10 @@ static int trace__fprintf_one_thread(struct thread *thread, void *priv) | |||
2163 | else if (ratio > 5.0) | 2174 | else if (ratio > 5.0) |
2164 | color = PERF_COLOR_YELLOW; | 2175 | color = PERF_COLOR_YELLOW; |
2165 | 2176 | ||
2166 | printed += color_fprintf(fp, color, "%20s", thread__comm_str(thread)); | 2177 | printed += color_fprintf(fp, color, " %s (%d), ", thread__comm_str(thread), thread->tid); |
2167 | printed += fprintf(fp, " - %-5d :%11lu [", thread->tid, ttrace->nr_events); | 2178 | printed += fprintf(fp, "%lu events, ", ttrace->nr_events); |
2168 | printed += color_fprintf(fp, color, "%5.1f%%", ratio); | 2179 | printed += color_fprintf(fp, color, "%.1f%%", ratio); |
2169 | printed += fprintf(fp, " ] %10.3f ms\n", ttrace->runtime_ms); | 2180 | printed += fprintf(fp, ", %.3f msec\n", ttrace->runtime_ms); |
2170 | printed += thread__dump_stats(ttrace, trace, fp); | 2181 | printed += thread__dump_stats(ttrace, trace, fp); |
2171 | 2182 | ||
2172 | data->printed += printed; | 2183 | data->printed += printed; |
@@ -2275,8 +2286,10 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) | |||
2275 | OPT_INCR('v', "verbose", &verbose, "be more verbose"), | 2286 | OPT_INCR('v', "verbose", &verbose, "be more verbose"), |
2276 | OPT_BOOLEAN('T', "time", &trace.full_time, | 2287 | OPT_BOOLEAN('T', "time", &trace.full_time, |
2277 | "Show full timestamp, not time relative to first start"), | 2288 | "Show full timestamp, not time relative to first start"), |
2278 | OPT_BOOLEAN(0, "summary", &trace.summary, | 2289 | OPT_BOOLEAN('s', "summary", &trace.summary_only, |
2279 | "Show syscall summary with statistics"), | 2290 | "Show only syscall summary with statistics"), |
2291 | OPT_BOOLEAN('S', "with-summary", &trace.summary, | ||
2292 | "Show all syscalls and summary with statistics"), | ||
2280 | OPT_END() | 2293 | OPT_END() |
2281 | }; | 2294 | }; |
2282 | int err; | 2295 | int err; |
@@ -2287,6 +2300,10 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) | |||
2287 | 2300 | ||
2288 | argc = parse_options(argc, argv, trace_options, trace_usage, 0); | 2301 | argc = parse_options(argc, argv, trace_options, trace_usage, 0); |
2289 | 2302 | ||
2303 | /* summary_only implies summary option, but don't overwrite summary if set */ | ||
2304 | if (trace.summary_only) | ||
2305 | trace.summary = trace.summary_only; | ||
2306 | |||
2290 | if (output_name != NULL) { | 2307 | if (output_name != NULL) { |
2291 | err = trace__open_output(&trace, output_name); | 2308 | err = trace__open_output(&trace, output_name); |
2292 | if (err < 0) { | 2309 | if (err < 0) { |
@@ -2310,21 +2327,21 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) | |||
2310 | } | 2327 | } |
2311 | } | 2328 | } |
2312 | 2329 | ||
2313 | err = perf_target__validate(&trace.opts.target); | 2330 | err = target__validate(&trace.opts.target); |
2314 | if (err) { | 2331 | if (err) { |
2315 | perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf)); | 2332 | target__strerror(&trace.opts.target, err, bf, sizeof(bf)); |
2316 | fprintf(trace.output, "%s", bf); | 2333 | fprintf(trace.output, "%s", bf); |
2317 | goto out_close; | 2334 | goto out_close; |
2318 | } | 2335 | } |
2319 | 2336 | ||
2320 | err = perf_target__parse_uid(&trace.opts.target); | 2337 | err = target__parse_uid(&trace.opts.target); |
2321 | if (err) { | 2338 | if (err) { |
2322 | perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf)); | 2339 | target__strerror(&trace.opts.target, err, bf, sizeof(bf)); |
2323 | fprintf(trace.output, "%s", bf); | 2340 | fprintf(trace.output, "%s", bf); |
2324 | goto out_close; | 2341 | goto out_close; |
2325 | } | 2342 | } |
2326 | 2343 | ||
2327 | if (!argc && perf_target__none(&trace.opts.target)) | 2344 | if (!argc && target__none(&trace.opts.target)) |
2328 | trace.opts.target.system_wide = true; | 2345 | trace.opts.target.system_wide = true; |
2329 | 2346 | ||
2330 | if (input_name) | 2347 | if (input_name) |