diff options
author | Arnaldo Carvalho de Melo <acme@redhat.com> | 2012-10-06 17:43:19 -0400 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2012-10-24 15:23:03 -0400 |
commit | 752fde44fd1c4a411d709c7d4ad0f121f427f234 (patch) | |
tree | 333fcb269d142c023845a844bc8df32c28c57775 /tools/perf/builtin-trace.c | |
parent | 9a8e85ad0b61ec0720df7d24392163c2a12626d0 (diff) |
perf trace: Support interrupted syscalls
Using the same strategies as in the tmp.perf/trace2, i.e. the 'trace'
tool implemented by tglx, just updated to the current codebase.
Example:
[root@sandy linux]# perf trace usleep 1 | tail
2.003: mmap(addr: 0, len: 4096, prot: 3, flags: 34, fd: 4294967295, off: 0 ) = -2128396288
2.017: mmap(addr: 0, len: 4096, prot: 3, flags: 34, fd: 4294967295, off: 0 ) = -2128400384
2.029: arch_prctl(option: 4098, arg2: 140146949441280, arg3: 140146949435392, arg4: 34, arg5: 4294967295) = 0
2.084: mprotect(start: 208741634048, len: 16384, prot: 1 ) = 0
2.098: mprotect(start: 208735956992, len: 4096, prot: 1 ) = 0
2.122: munmap(addr: 140146949447680, len: 91882 ) = 0
2.359: brk(brk: 0 ) = 28987392
2.371: brk(brk: 29122560 ) = 29122560
2.490: nanosleep(rqtp: 140735694241504, rmtp: 0 ) = 0
2.507: exit_group(error_code: 0
[root@sandy linux]#
For now the timestamp and duration are always on, will be selectable.
Also if multiple threads are being monitored, its tid will appear.
The ret output continues to be interpreted a la strace.
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Namhyung Kim <namhyung@gmail.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/n/tip-ly9ulroru4my5isn0xe9gr0m@git.kernel.org
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 | 199 |
1 files changed, 177 insertions, 22 deletions
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 14b322961807..873f50b10cc9 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c | |||
@@ -1,5 +1,8 @@ | |||
1 | #include "builtin.h" | 1 | #include "builtin.h" |
2 | #include "util/color.h" | ||
2 | #include "util/evlist.h" | 3 | #include "util/evlist.h" |
4 | #include "util/machine.h" | ||
5 | #include "util/thread.h" | ||
3 | #include "util/parse-options.h" | 6 | #include "util/parse-options.h" |
4 | #include "util/thread_map.h" | 7 | #include "util/thread_map.h" |
5 | #include "event-parse.h" | 8 | #include "event-parse.h" |
@@ -43,6 +46,36 @@ struct syscall { | |||
43 | struct syscall_fmt *fmt; | 46 | struct syscall_fmt *fmt; |
44 | }; | 47 | }; |
45 | 48 | ||
49 | struct thread_trace { | ||
50 | u64 entry_time; | ||
51 | u64 exit_time; | ||
52 | bool entry_pending; | ||
53 | char *entry_str; | ||
54 | }; | ||
55 | |||
56 | static struct thread_trace *thread_trace__new(void) | ||
57 | { | ||
58 | return zalloc(sizeof(struct thread_trace)); | ||
59 | } | ||
60 | |||
61 | static struct thread_trace *thread__trace(struct thread *thread) | ||
62 | { | ||
63 | if (thread == NULL) | ||
64 | goto fail; | ||
65 | |||
66 | if (thread->priv == NULL) | ||
67 | thread->priv = thread_trace__new(); | ||
68 | |||
69 | if (thread->priv == NULL) | ||
70 | goto fail; | ||
71 | |||
72 | return thread->priv; | ||
73 | fail: | ||
74 | color_fprintf(stdout, PERF_COLOR_RED, | ||
75 | "WARNING: not enough memory, dropping samples!\n"); | ||
76 | return NULL; | ||
77 | } | ||
78 | |||
46 | struct trace { | 79 | struct trace { |
47 | int audit_machine; | 80 | int audit_machine; |
48 | struct { | 81 | struct { |
@@ -50,8 +83,18 @@ struct trace { | |||
50 | struct syscall *table; | 83 | struct syscall *table; |
51 | } syscalls; | 84 | } syscalls; |
52 | struct perf_record_opts opts; | 85 | struct perf_record_opts opts; |
86 | struct machine host; | ||
87 | u64 base_time; | ||
88 | bool multiple_threads; | ||
53 | }; | 89 | }; |
54 | 90 | ||
91 | static size_t trace__fprintf_tstamp(struct trace *trace, u64 tstamp, FILE *fp) | ||
92 | { | ||
93 | double ts = (double)(tstamp - trace->base_time) / NSEC_PER_MSEC; | ||
94 | |||
95 | return fprintf(fp, "%10.3f: ", ts); | ||
96 | } | ||
97 | |||
55 | static bool done = false; | 98 | static bool done = false; |
56 | 99 | ||
57 | static void sig_handler(int sig __maybe_unused) | 100 | static void sig_handler(int sig __maybe_unused) |
@@ -59,6 +102,67 @@ static void sig_handler(int sig __maybe_unused) | |||
59 | done = true; | 102 | done = true; |
60 | } | 103 | } |
61 | 104 | ||
105 | static size_t trace__fprintf_entry_head(struct trace *trace, struct thread *thread, | ||
106 | u64 tstamp, FILE *fp) | ||
107 | { | ||
108 | size_t printed = trace__fprintf_tstamp(trace, tstamp, fp); | ||
109 | |||
110 | if (trace->multiple_threads) | ||
111 | printed += fprintf(fp, "%d ", thread->pid); | ||
112 | |||
113 | return printed; | ||
114 | } | ||
115 | |||
116 | static int trace__process_event(struct machine *machine, union perf_event *event) | ||
117 | { | ||
118 | int ret = 0; | ||
119 | |||
120 | switch (event->header.type) { | ||
121 | case PERF_RECORD_LOST: | ||
122 | color_fprintf(stdout, PERF_COLOR_RED, | ||
123 | "LOST %" PRIu64 " events!\n", event->lost.lost); | ||
124 | ret = machine__process_lost_event(machine, event); | ||
125 | default: | ||
126 | ret = machine__process_event(machine, event); | ||
127 | break; | ||
128 | } | ||
129 | |||
130 | return ret; | ||
131 | } | ||
132 | |||
133 | static int trace__tool_process(struct perf_tool *tool __maybe_unused, | ||
134 | union perf_event *event, | ||
135 | struct perf_sample *sample __maybe_unused, | ||
136 | struct machine *machine) | ||
137 | { | ||
138 | return trace__process_event(machine, event); | ||
139 | } | ||
140 | |||
141 | static int trace__symbols_init(struct trace *trace, struct perf_evlist *evlist) | ||
142 | { | ||
143 | int err = symbol__init(); | ||
144 | |||
145 | if (err) | ||
146 | return err; | ||
147 | |||
148 | machine__init(&trace->host, "", HOST_KERNEL_ID); | ||
149 | machine__create_kernel_maps(&trace->host); | ||
150 | |||
151 | if (perf_target__has_task(&trace->opts.target)) { | ||
152 | err = perf_event__synthesize_thread_map(NULL, evlist->threads, | ||
153 | trace__tool_process, | ||
154 | &trace->host); | ||
155 | } else { | ||
156 | err = perf_event__synthesize_threads(NULL, trace__tool_process, | ||
157 | &trace->host); | ||
158 | } | ||
159 | |||
160 | if (err) | ||
161 | symbol__exit(); | ||
162 | |||
163 | return err; | ||
164 | } | ||
165 | |||
62 | static int trace__read_syscall_info(struct trace *trace, int id) | 166 | static int trace__read_syscall_info(struct trace *trace, int id) |
63 | { | 167 | { |
64 | char tp_name[128]; | 168 | char tp_name[128]; |
@@ -100,7 +204,8 @@ static int trace__read_syscall_info(struct trace *trace, int id) | |||
100 | return sc->tp_format != NULL ? 0 : -1; | 204 | return sc->tp_format != NULL ? 0 : -1; |
101 | } | 205 | } |
102 | 206 | ||
103 | static size_t syscall__fprintf_args(struct syscall *sc, unsigned long *args, FILE *fp) | 207 | static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size, |
208 | unsigned long *args) | ||
104 | { | 209 | { |
105 | int i = 0; | 210 | int i = 0; |
106 | size_t printed = 0; | 211 | size_t printed = 0; |
@@ -109,12 +214,15 @@ static size_t syscall__fprintf_args(struct syscall *sc, unsigned long *args, FIL | |||
109 | struct format_field *field; | 214 | struct format_field *field; |
110 | 215 | ||
111 | for (field = sc->tp_format->format.fields->next; field; field = field->next) { | 216 | for (field = sc->tp_format->format.fields->next; field; field = field->next) { |
112 | printed += fprintf(fp, "%s%s: %ld", printed ? ", " : "", | 217 | printed += scnprintf(bf + printed, size - printed, |
113 | field->name, args[i++]); | 218 | "%s%s: %ld", printed ? ", " : "", |
219 | field->name, args[i++]); | ||
114 | } | 220 | } |
115 | } else { | 221 | } else { |
116 | while (i < 6) { | 222 | while (i < 6) { |
117 | printed += fprintf(fp, "%sarg%d: %ld", printed ? ", " : "", i, args[i]); | 223 | printed += scnprintf(bf + printed, size - printed, |
224 | "%sarg%d: %ld", | ||
225 | printed ? ", " : "", i, args[i]); | ||
118 | ++i; | 226 | ++i; |
119 | } | 227 | } |
120 | } | 228 | } |
@@ -153,10 +261,14 @@ out_cant_read: | |||
153 | static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, | 261 | static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, |
154 | struct perf_sample *sample) | 262 | struct perf_sample *sample) |
155 | { | 263 | { |
264 | char *msg; | ||
156 | void *args; | 265 | void *args; |
266 | size_t printed = 0; | ||
267 | struct thread *thread = machine__findnew_thread(&trace->host, sample->tid); | ||
157 | struct syscall *sc = trace__syscall_info(trace, evsel, sample); | 268 | struct syscall *sc = trace__syscall_info(trace, evsel, sample); |
269 | struct thread_trace *ttrace = thread__trace(thread); | ||
158 | 270 | ||
159 | if (sc == NULL) | 271 | if (ttrace == NULL || sc == NULL) |
160 | return -1; | 272 | return -1; |
161 | 273 | ||
162 | args = perf_evsel__rawptr(evsel, sample, "args"); | 274 | args = perf_evsel__rawptr(evsel, sample, "args"); |
@@ -165,8 +277,25 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, | |||
165 | return -1; | 277 | return -1; |
166 | } | 278 | } |
167 | 279 | ||
168 | printf("%s(", sc->name); | 280 | ttrace = thread->priv; |
169 | syscall__fprintf_args(sc, args, stdout); | 281 | |
282 | if (ttrace->entry_str == NULL) { | ||
283 | ttrace->entry_str = malloc(1024); | ||
284 | if (!ttrace->entry_str) | ||
285 | return -1; | ||
286 | } | ||
287 | |||
288 | ttrace->entry_time = sample->time; | ||
289 | msg = ttrace->entry_str; | ||
290 | printed += scnprintf(msg + printed, 1024 - printed, "%s(", sc->name); | ||
291 | |||
292 | printed += syscall__scnprintf_args(sc, msg + printed, 1024 - printed, args); | ||
293 | |||
294 | if (!strcmp(sc->name, "exit_group") || !strcmp(sc->name, "exit")) { | ||
295 | trace__fprintf_entry_head(trace, thread, sample->time, stdout); | ||
296 | printf("%-70s\n", ttrace->entry_str); | ||
297 | } else | ||
298 | ttrace->entry_pending = true; | ||
170 | 299 | ||
171 | return 0; | 300 | return 0; |
172 | } | 301 | } |
@@ -175,13 +304,29 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, | |||
175 | struct perf_sample *sample) | 304 | struct perf_sample *sample) |
176 | { | 305 | { |
177 | int ret; | 306 | int ret; |
307 | struct thread *thread = machine__findnew_thread(&trace->host, sample->tid); | ||
308 | struct thread_trace *ttrace = thread__trace(thread); | ||
178 | struct syscall *sc = trace__syscall_info(trace, evsel, sample); | 309 | struct syscall *sc = trace__syscall_info(trace, evsel, sample); |
179 | 310 | ||
180 | if (sc == NULL) | 311 | if (ttrace == NULL || sc == NULL) |
181 | return -1; | 312 | return -1; |
182 | 313 | ||
183 | ret = perf_evsel__intval(evsel, sample, "ret"); | 314 | ret = perf_evsel__intval(evsel, sample, "ret"); |
184 | 315 | ||
316 | ttrace = thread->priv; | ||
317 | |||
318 | ttrace->exit_time = sample->time; | ||
319 | |||
320 | trace__fprintf_entry_head(trace, thread, sample->time, stdout); | ||
321 | |||
322 | if (ttrace->entry_pending) { | ||
323 | printf("%-70s", ttrace->entry_str); | ||
324 | } else { | ||
325 | printf(" ... ["); | ||
326 | color_fprintf(stdout, PERF_COLOR_YELLOW, "continued"); | ||
327 | printf("]: %s()", sc->name); | ||
328 | } | ||
329 | |||
185 | if (ret < 0 && sc->fmt && sc->fmt->errmsg) { | 330 | if (ret < 0 && sc->fmt && sc->fmt->errmsg) { |
186 | char bf[256]; | 331 | char bf[256]; |
187 | const char *emsg = strerror_r(-ret, bf, sizeof(bf)), | 332 | const char *emsg = strerror_r(-ret, bf, sizeof(bf)), |
@@ -194,6 +339,9 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, | |||
194 | printf(") = %d", ret); | 339 | printf(") = %d", ret); |
195 | 340 | ||
196 | putchar('\n'); | 341 | putchar('\n'); |
342 | |||
343 | ttrace->entry_pending = false; | ||
344 | |||
197 | return 0; | 345 | return 0; |
198 | } | 346 | } |
199 | 347 | ||
@@ -221,6 +369,12 @@ static int trace__run(struct trace *trace, int argc, const char **argv) | |||
221 | goto out_delete_evlist; | 369 | goto out_delete_evlist; |
222 | } | 370 | } |
223 | 371 | ||
372 | err = trace__symbols_init(trace, evlist); | ||
373 | if (err < 0) { | ||
374 | printf("Problems initializing symbol libraries!\n"); | ||
375 | goto out_delete_evlist; | ||
376 | } | ||
377 | |||
224 | perf_evlist__config_attrs(evlist, &trace->opts); | 378 | perf_evlist__config_attrs(evlist, &trace->opts); |
225 | 379 | ||
226 | signal(SIGCHLD, sig_handler); | 380 | signal(SIGCHLD, sig_handler); |
@@ -251,6 +405,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv) | |||
251 | if (forks) | 405 | if (forks) |
252 | perf_evlist__start_workload(evlist); | 406 | perf_evlist__start_workload(evlist); |
253 | 407 | ||
408 | trace->multiple_threads = evlist->threads->map[0] == -1 || evlist->threads->nr > 1; | ||
254 | again: | 409 | again: |
255 | before = nr_events; | 410 | before = nr_events; |
256 | 411 | ||
@@ -264,32 +419,32 @@ again: | |||
264 | 419 | ||
265 | ++nr_events; | 420 | ++nr_events; |
266 | 421 | ||
267 | switch (type) { | ||
268 | case PERF_RECORD_SAMPLE: | ||
269 | break; | ||
270 | case PERF_RECORD_LOST: | ||
271 | printf("LOST %" PRIu64 " events!\n", event->lost.lost); | ||
272 | continue; | ||
273 | default: | ||
274 | printf("Unexpected %s event, skipping...\n", | ||
275 | perf_event__name(type)); | ||
276 | continue; | ||
277 | } | ||
278 | |||
279 | err = perf_evlist__parse_sample(evlist, event, &sample); | 422 | err = perf_evlist__parse_sample(evlist, event, &sample); |
280 | if (err) { | 423 | if (err) { |
281 | printf("Can't parse sample, err = %d, skipping...\n", err); | 424 | printf("Can't parse sample, err = %d, skipping...\n", err); |
282 | continue; | 425 | continue; |
283 | } | 426 | } |
284 | 427 | ||
428 | if (trace->base_time == 0) | ||
429 | trace->base_time = sample.time; | ||
430 | |||
431 | if (type != PERF_RECORD_SAMPLE) { | ||
432 | trace__process_event(&trace->host, event); | ||
433 | continue; | ||
434 | } | ||
435 | |||
285 | evsel = perf_evlist__id2evsel(evlist, sample.id); | 436 | evsel = perf_evlist__id2evsel(evlist, sample.id); |
286 | if (evsel == NULL) { | 437 | if (evsel == NULL) { |
287 | printf("Unknown tp ID %" PRIu64 ", skipping...\n", sample.id); | 438 | printf("Unknown tp ID %" PRIu64 ", skipping...\n", sample.id); |
288 | continue; | 439 | continue; |
289 | } | 440 | } |
290 | 441 | ||
291 | if (evlist->threads->map[0] == -1 || evlist->threads->nr > 1) | 442 | if (sample.raw_data == NULL) { |
292 | printf("%d ", sample.tid); | 443 | printf("%s sample with no payload for tid: %d, cpu %d, raw_size=%d, skipping...\n", |
444 | perf_evsel__name(evsel), sample.tid, | ||
445 | sample.cpu, sample.raw_size); | ||
446 | continue; | ||
447 | } | ||
293 | 448 | ||
294 | if (sample.raw_data == NULL) { | 449 | if (sample.raw_data == NULL) { |
295 | printf("%s sample with no payload for tid: %d, cpu %d, raw_size=%d, skipping...\n", | 450 | printf("%s sample with no payload for tid: %d, cpu %d, raw_size=%d, skipping...\n", |