perf trace: Support interrupted syscalls
[deliverable/linux.git] / tools / perf / builtin-trace.c
CommitLineData
514f1c67 1#include "builtin.h"
752fde44 2#include "util/color.h"
514f1c67 3#include "util/evlist.h"
752fde44
ACM
4#include "util/machine.h"
5#include "util/thread.h"
514f1c67
ACM
6#include "util/parse-options.h"
7#include "util/thread_map.h"
8#include "event-parse.h"
9
10#include <libaudit.h>
11#include <stdlib.h>
12
13static struct syscall_fmt {
14 const char *name;
aec1930b 15 const char *alias;
514f1c67
ACM
16 bool errmsg;
17 bool timeout;
18} syscall_fmts[] = {
aec1930b
ACM
19 { .name = "arch_prctl", .errmsg = true, .alias = "prctl", },
20 { .name = "fstat", .errmsg = true, .alias = "newfstat", },
21 { .name = "fstatat", .errmsg = true, .alias = "newfstatat", },
22 { .name = "futex", .errmsg = true, },
23 { .name = "poll", .errmsg = true, .timeout = true, },
24 { .name = "ppoll", .errmsg = true, .timeout = true, },
25 { .name = "read", .errmsg = true, },
26 { .name = "recvfrom", .errmsg = true, },
27 { .name = "select", .errmsg = true, .timeout = true, },
28 { .name = "stat", .errmsg = true, .alias = "newstat", },
514f1c67
ACM
29};
30
31static int syscall_fmt__cmp(const void *name, const void *fmtp)
32{
33 const struct syscall_fmt *fmt = fmtp;
34 return strcmp(name, fmt->name);
35}
36
37static struct syscall_fmt *syscall_fmt__find(const char *name)
38{
39 const int nmemb = ARRAY_SIZE(syscall_fmts);
40 return bsearch(name, syscall_fmts, nmemb, sizeof(struct syscall_fmt), syscall_fmt__cmp);
41}
42
43struct syscall {
44 struct event_format *tp_format;
45 const char *name;
46 struct syscall_fmt *fmt;
47};
48
752fde44
ACM
49struct thread_trace {
50 u64 entry_time;
51 u64 exit_time;
52 bool entry_pending;
53 char *entry_str;
54};
55
56static struct thread_trace *thread_trace__new(void)
57{
58 return zalloc(sizeof(struct thread_trace));
59}
60
61static 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;
73fail:
74 color_fprintf(stdout, PERF_COLOR_RED,
75 "WARNING: not enough memory, dropping samples!\n");
76 return NULL;
77}
78
514f1c67
ACM
79struct trace {
80 int audit_machine;
81 struct {
82 int max;
83 struct syscall *table;
84 } syscalls;
85 struct perf_record_opts opts;
752fde44
ACM
86 struct machine host;
87 u64 base_time;
88 bool multiple_threads;
514f1c67
ACM
89};
90
752fde44
ACM
91static 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
f15eb531
NK
98static bool done = false;
99
100static void sig_handler(int sig __maybe_unused)
101{
102 done = true;
103}
104
752fde44
ACM
105static 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
116static 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
133static 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
141static 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
514f1c67
ACM
166static int trace__read_syscall_info(struct trace *trace, int id)
167{
168 char tp_name[128];
169 struct syscall *sc;
3a531260
ACM
170 const char *name = audit_syscall_to_name(id, trace->audit_machine);
171
172 if (name == NULL)
173 return -1;
514f1c67
ACM
174
175 if (id > trace->syscalls.max) {
176 struct syscall *nsyscalls = realloc(trace->syscalls.table, (id + 1) * sizeof(*sc));
177
178 if (nsyscalls == NULL)
179 return -1;
180
181 if (trace->syscalls.max != -1) {
182 memset(nsyscalls + trace->syscalls.max + 1, 0,
183 (id - trace->syscalls.max) * sizeof(*sc));
184 } else {
185 memset(nsyscalls, 0, (id + 1) * sizeof(*sc));
186 }
187
188 trace->syscalls.table = nsyscalls;
189 trace->syscalls.max = id;
190 }
191
192 sc = trace->syscalls.table + id;
3a531260
ACM
193 sc->name = name;
194 sc->fmt = syscall_fmt__find(sc->name);
514f1c67 195
aec1930b 196 snprintf(tp_name, sizeof(tp_name), "sys_enter_%s", sc->name);
514f1c67 197 sc->tp_format = event_format__new("syscalls", tp_name);
aec1930b
ACM
198
199 if (sc->tp_format == NULL && sc->fmt && sc->fmt->alias) {
200 snprintf(tp_name, sizeof(tp_name), "sys_enter_%s", sc->fmt->alias);
201 sc->tp_format = event_format__new("syscalls", tp_name);
202 }
514f1c67
ACM
203
204 return sc->tp_format != NULL ? 0 : -1;
205}
206
752fde44
ACM
207static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size,
208 unsigned long *args)
514f1c67
ACM
209{
210 int i = 0;
211 size_t printed = 0;
212
213 if (sc->tp_format != NULL) {
214 struct format_field *field;
215
216 for (field = sc->tp_format->format.fields->next; field; field = field->next) {
752fde44
ACM
217 printed += scnprintf(bf + printed, size - printed,
218 "%s%s: %ld", printed ? ", " : "",
219 field->name, args[i++]);
514f1c67
ACM
220 }
221 } else {
222 while (i < 6) {
752fde44
ACM
223 printed += scnprintf(bf + printed, size - printed,
224 "%sarg%d: %ld",
225 printed ? ", " : "", i, args[i]);
514f1c67
ACM
226 ++i;
227 }
228 }
229
230 return printed;
231}
232
ba3d7dee
ACM
233typedef int (*tracepoint_handler)(struct trace *trace, struct perf_evsel *evsel,
234 struct perf_sample *sample);
235
236static struct syscall *trace__syscall_info(struct trace *trace,
237 struct perf_evsel *evsel,
238 struct perf_sample *sample)
239{
240 int id = perf_evsel__intval(evsel, sample, "id");
241
242 if (id < 0) {
243 printf("Invalid syscall %d id, skipping...\n", id);
244 return NULL;
245 }
246
247 if ((id > trace->syscalls.max || trace->syscalls.table[id].name == NULL) &&
248 trace__read_syscall_info(trace, id))
249 goto out_cant_read;
250
251 if ((id > trace->syscalls.max || trace->syscalls.table[id].name == NULL))
252 goto out_cant_read;
253
254 return &trace->syscalls.table[id];
255
256out_cant_read:
257 printf("Problems reading syscall %d information\n", id);
258 return NULL;
259}
260
261static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel,
262 struct perf_sample *sample)
263{
752fde44 264 char *msg;
ba3d7dee 265 void *args;
752fde44
ACM
266 size_t printed = 0;
267 struct thread *thread = machine__findnew_thread(&trace->host, sample->tid);
ba3d7dee 268 struct syscall *sc = trace__syscall_info(trace, evsel, sample);
752fde44 269 struct thread_trace *ttrace = thread__trace(thread);
ba3d7dee 270
752fde44 271 if (ttrace == NULL || sc == NULL)
ba3d7dee
ACM
272 return -1;
273
274 args = perf_evsel__rawptr(evsel, sample, "args");
275 if (args == NULL) {
276 printf("Problems reading syscall arguments\n");
277 return -1;
278 }
279
752fde44
ACM
280 ttrace = thread->priv;
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;
ba3d7dee
ACM
299
300 return 0;
301}
302
303static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel,
304 struct perf_sample *sample)
305{
306 int ret;
752fde44
ACM
307 struct thread *thread = machine__findnew_thread(&trace->host, sample->tid);
308 struct thread_trace *ttrace = thread__trace(thread);
ba3d7dee
ACM
309 struct syscall *sc = trace__syscall_info(trace, evsel, sample);
310
752fde44 311 if (ttrace == NULL || sc == NULL)
ba3d7dee
ACM
312 return -1;
313
314 ret = perf_evsel__intval(evsel, sample, "ret");
315
752fde44
ACM
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
ba3d7dee
ACM
330 if (ret < 0 && sc->fmt && sc->fmt->errmsg) {
331 char bf[256];
332 const char *emsg = strerror_r(-ret, bf, sizeof(bf)),
333 *e = audit_errno_to_name(-ret);
334
335 printf(") = -1 %s %s", e, emsg);
336 } else if (ret == 0 && sc->fmt && sc->fmt->timeout)
337 printf(") = 0 Timeout");
338 else
339 printf(") = %d", ret);
340
341 putchar('\n');
752fde44
ACM
342
343 ttrace->entry_pending = false;
344
ba3d7dee
ACM
345 return 0;
346}
347
f15eb531 348static int trace__run(struct trace *trace, int argc, const char **argv)
514f1c67
ACM
349{
350 struct perf_evlist *evlist = perf_evlist__new(NULL, NULL);
ba3d7dee 351 struct perf_evsel *evsel;
514f1c67 352 int err = -1, i, nr_events = 0, before;
f15eb531 353 const bool forks = argc > 0;
514f1c67
ACM
354
355 if (evlist == NULL) {
356 printf("Not enough memory to run!\n");
357 goto out;
358 }
359
39876e7d
ACM
360 if (perf_evlist__add_newtp(evlist, "raw_syscalls", "sys_enter", trace__sys_enter) ||
361 perf_evlist__add_newtp(evlist, "raw_syscalls", "sys_exit", trace__sys_exit)) {
362 printf("Couldn't read the raw_syscalls tracepoints information!\n");
514f1c67
ACM
363 goto out_delete_evlist;
364 }
365
514f1c67
ACM
366 err = perf_evlist__create_maps(evlist, &trace->opts.target);
367 if (err < 0) {
368 printf("Problems parsing the target to trace, check your options!\n");
369 goto out_delete_evlist;
370 }
371
752fde44
ACM
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
514f1c67
ACM
378 perf_evlist__config_attrs(evlist, &trace->opts);
379
f15eb531
NK
380 signal(SIGCHLD, sig_handler);
381 signal(SIGINT, sig_handler);
382
383 if (forks) {
384 err = perf_evlist__prepare_workload(evlist, &trace->opts, argv);
385 if (err < 0) {
386 printf("Couldn't run the workload!\n");
387 goto out_delete_evlist;
388 }
389 }
390
514f1c67
ACM
391 err = perf_evlist__open(evlist);
392 if (err < 0) {
393 printf("Couldn't create the events: %s\n", strerror(errno));
394 goto out_delete_evlist;
395 }
396
397 err = perf_evlist__mmap(evlist, UINT_MAX, false);
398 if (err < 0) {
399 printf("Couldn't mmap the events: %s\n", strerror(errno));
400 goto out_delete_evlist;
401 }
402
403 perf_evlist__enable(evlist);
f15eb531
NK
404
405 if (forks)
406 perf_evlist__start_workload(evlist);
407
752fde44 408 trace->multiple_threads = evlist->threads->map[0] == -1 || evlist->threads->nr > 1;
514f1c67
ACM
409again:
410 before = nr_events;
411
412 for (i = 0; i < evlist->nr_mmaps; i++) {
413 union perf_event *event;
414
415 while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) {
416 const u32 type = event->header.type;
ba3d7dee 417 tracepoint_handler handler;
514f1c67 418 struct perf_sample sample;
514f1c67
ACM
419
420 ++nr_events;
421
514f1c67
ACM
422 err = perf_evlist__parse_sample(evlist, event, &sample);
423 if (err) {
424 printf("Can't parse sample, err = %d, skipping...\n", err);
425 continue;
426 }
427
752fde44
ACM
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
514f1c67
ACM
436 evsel = perf_evlist__id2evsel(evlist, sample.id);
437 if (evsel == NULL) {
438 printf("Unknown tp ID %" PRIu64 ", skipping...\n", sample.id);
439 continue;
440 }
441
752fde44
ACM
442 if (sample.raw_data == NULL) {
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 }
514f1c67 448
fc551f8d
ACM
449 if (sample.raw_data == NULL) {
450 printf("%s sample with no payload for tid: %d, cpu %d, raw_size=%d, skipping...\n",
451 perf_evsel__name(evsel), sample.tid,
452 sample.cpu, sample.raw_size);
453 continue;
454 }
455
ba3d7dee
ACM
456 handler = evsel->handler.func;
457 handler(trace, evsel, &sample);
514f1c67
ACM
458 }
459 }
460
f15eb531
NK
461 if (nr_events == before) {
462 if (done)
463 goto out_delete_evlist;
464
514f1c67 465 poll(evlist->pollfd, evlist->nr_fds, -1);
f15eb531
NK
466 }
467
468 if (done)
469 perf_evlist__disable(evlist);
514f1c67
ACM
470
471 goto again;
472
473out_delete_evlist:
474 perf_evlist__delete(evlist);
475out:
476 return err;
477}
478
479int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
480{
481 const char * const trace_usage[] = {
f15eb531
NK
482 "perf trace [<options>] [<command>]",
483 "perf trace [<options>] -- <command> [<options>]",
514f1c67
ACM
484 NULL
485 };
486 struct trace trace = {
487 .audit_machine = audit_detect_machine(),
488 .syscalls = {
489 . max = -1,
490 },
491 .opts = {
492 .target = {
493 .uid = UINT_MAX,
494 .uses_mmap = true,
495 },
496 .user_freq = UINT_MAX,
497 .user_interval = ULLONG_MAX,
498 .no_delay = true,
499 .mmap_pages = 1024,
500 },
501 };
502 const struct option trace_options[] = {
503 OPT_STRING('p', "pid", &trace.opts.target.pid, "pid",
504 "trace events on existing process id"),
505 OPT_STRING(0, "tid", &trace.opts.target.tid, "tid",
506 "trace events on existing thread id"),
507 OPT_BOOLEAN(0, "all-cpus", &trace.opts.target.system_wide,
508 "system-wide collection from all CPUs"),
509 OPT_STRING(0, "cpu", &trace.opts.target.cpu_list, "cpu",
510 "list of cpus to monitor"),
511 OPT_BOOLEAN(0, "no-inherit", &trace.opts.no_inherit,
512 "child tasks do not inherit counters"),
513 OPT_UINTEGER(0, "mmap-pages", &trace.opts.mmap_pages,
514 "number of mmap data pages"),
515 OPT_STRING(0, "uid", &trace.opts.target.uid_str, "user",
516 "user to profile"),
517 OPT_END()
518 };
519 int err;
32caf0d1 520 char bf[BUFSIZ];
514f1c67
ACM
521
522 argc = parse_options(argc, argv, trace_options, trace_usage, 0);
514f1c67 523
32caf0d1
NK
524 err = perf_target__validate(&trace.opts.target);
525 if (err) {
526 perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf));
527 printf("%s", bf);
528 return err;
529 }
530
514f1c67
ACM
531 err = perf_target__parse_uid(&trace.opts.target);
532 if (err) {
514f1c67
ACM
533 perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf));
534 printf("%s", bf);
535 return err;
536 }
537
f15eb531 538 if (!argc && perf_target__none(&trace.opts.target))
ee76120e
NK
539 trace.opts.target.system_wide = true;
540
f15eb531 541 return trace__run(&trace, argc, argv);
514f1c67 542}
This page took 0.051809 seconds and 5 git commands to generate.