diff options
author | Arnaldo Carvalho de Melo <acme@redhat.com> | 2013-08-19 11:01:10 -0400 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2013-08-26 15:51:31 -0400 |
commit | c24ff998fc420891f17d73acab6766823d492175 (patch) | |
tree | 9dd2767d9c6d961d89eb58b60d15c7189efc5812 /tools/perf | |
parent | e3e1a54fce81ee045dd152deb5435b136cb0b75f (diff) |
perf trace: Implement -o/--output filename
To output all 'trace' output to a filename, just like 'strace -ofile'
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Mike Galbraith <efault@gmx.de>
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-6q1homkwoayhmoq64y5vhel6@git.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools/perf')
-rw-r--r-- | tools/perf/Documentation/perf-trace.txt | 4 | ||||
-rw-r--r-- | tools/perf/builtin-trace.c | 134 |
2 files changed, 90 insertions, 48 deletions
diff --git a/tools/perf/Documentation/perf-trace.txt b/tools/perf/Documentation/perf-trace.txt index 3b3552a8959e..2794efce47a1 100644 --- a/tools/perf/Documentation/perf-trace.txt +++ b/tools/perf/Documentation/perf-trace.txt | |||
@@ -30,6 +30,10 @@ OPTIONS | |||
30 | --expr:: | 30 | --expr:: |
31 | List of events to show, currently only syscall names. | 31 | List of events to show, currently only syscall names. |
32 | 32 | ||
33 | -o:: | ||
34 | --output=:: | ||
35 | Output file name. | ||
36 | |||
33 | -p:: | 37 | -p:: |
34 | --pid=:: | 38 | --pid=:: |
35 | Record events on existing process ID (comma separated list). | 39 | Record events on existing process ID (comma separated list). |
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 120fdfb3d920..42353165472a 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c | |||
@@ -63,7 +63,7 @@ static size_t fprintf_duration(unsigned long t, FILE *fp) | |||
63 | printed += color_fprintf(fp, PERF_COLOR_YELLOW, "%6.3f ms", duration); | 63 | printed += color_fprintf(fp, PERF_COLOR_YELLOW, "%6.3f ms", duration); |
64 | else | 64 | else |
65 | printed += color_fprintf(fp, PERF_COLOR_NORMAL, "%6.3f ms", duration); | 65 | printed += color_fprintf(fp, PERF_COLOR_NORMAL, "%6.3f ms", duration); |
66 | return printed + fprintf(stdout, "): "); | 66 | return printed + fprintf(fp, "): "); |
67 | } | 67 | } |
68 | 68 | ||
69 | struct thread_trace { | 69 | struct thread_trace { |
@@ -80,7 +80,7 @@ static struct thread_trace *thread_trace__new(void) | |||
80 | return zalloc(sizeof(struct thread_trace)); | 80 | return zalloc(sizeof(struct thread_trace)); |
81 | } | 81 | } |
82 | 82 | ||
83 | static struct thread_trace *thread__trace(struct thread *thread) | 83 | static struct thread_trace *thread__trace(struct thread *thread, FILE *fp) |
84 | { | 84 | { |
85 | struct thread_trace *ttrace; | 85 | struct thread_trace *ttrace; |
86 | 86 | ||
@@ -98,12 +98,13 @@ static struct thread_trace *thread__trace(struct thread *thread) | |||
98 | 98 | ||
99 | return ttrace; | 99 | return ttrace; |
100 | fail: | 100 | fail: |
101 | color_fprintf(stdout, PERF_COLOR_RED, | 101 | color_fprintf(fp, PERF_COLOR_RED, |
102 | "WARNING: not enough memory, dropping samples!\n"); | 102 | "WARNING: not enough memory, dropping samples!\n"); |
103 | return NULL; | 103 | return NULL; |
104 | } | 104 | } |
105 | 105 | ||
106 | struct trace { | 106 | struct trace { |
107 | struct perf_tool tool; | ||
107 | int audit_machine; | 108 | int audit_machine; |
108 | struct { | 109 | struct { |
109 | int max; | 110 | int max; |
@@ -112,6 +113,7 @@ struct trace { | |||
112 | struct perf_record_opts opts; | 113 | struct perf_record_opts opts; |
113 | struct machine host; | 114 | struct machine host; |
114 | u64 base_time; | 115 | u64 base_time; |
116 | FILE *output; | ||
115 | struct strlist *ev_qualifier; | 117 | struct strlist *ev_qualifier; |
116 | unsigned long nr_events; | 118 | unsigned long nr_events; |
117 | bool sched; | 119 | bool sched; |
@@ -151,13 +153,14 @@ static size_t trace__fprintf_entry_head(struct trace *trace, struct thread *thre | |||
151 | return printed; | 153 | return printed; |
152 | } | 154 | } |
153 | 155 | ||
154 | static int trace__process_event(struct machine *machine, union perf_event *event) | 156 | static int trace__process_event(struct trace *trace, struct machine *machine, |
157 | union perf_event *event) | ||
155 | { | 158 | { |
156 | int ret = 0; | 159 | int ret = 0; |
157 | 160 | ||
158 | switch (event->header.type) { | 161 | switch (event->header.type) { |
159 | case PERF_RECORD_LOST: | 162 | case PERF_RECORD_LOST: |
160 | color_fprintf(stdout, PERF_COLOR_RED, | 163 | color_fprintf(trace->output, PERF_COLOR_RED, |
161 | "LOST %" PRIu64 " events!\n", event->lost.lost); | 164 | "LOST %" PRIu64 " events!\n", event->lost.lost); |
162 | ret = machine__process_lost_event(machine, event); | 165 | ret = machine__process_lost_event(machine, event); |
163 | default: | 166 | default: |
@@ -168,12 +171,13 @@ static int trace__process_event(struct machine *machine, union perf_event *event | |||
168 | return ret; | 171 | return ret; |
169 | } | 172 | } |
170 | 173 | ||
171 | static int trace__tool_process(struct perf_tool *tool __maybe_unused, | 174 | static int trace__tool_process(struct perf_tool *tool, |
172 | union perf_event *event, | 175 | union perf_event *event, |
173 | struct perf_sample *sample __maybe_unused, | 176 | struct perf_sample *sample __maybe_unused, |
174 | struct machine *machine) | 177 | struct machine *machine) |
175 | { | 178 | { |
176 | return trace__process_event(machine, event); | 179 | struct trace *trace = container_of(tool, struct trace, tool); |
180 | return trace__process_event(trace, machine, event); | ||
177 | } | 181 | } |
178 | 182 | ||
179 | static int trace__symbols_init(struct trace *trace, struct perf_evlist *evlist) | 183 | static int trace__symbols_init(struct trace *trace, struct perf_evlist *evlist) |
@@ -187,11 +191,11 @@ static int trace__symbols_init(struct trace *trace, struct perf_evlist *evlist) | |||
187 | machine__create_kernel_maps(&trace->host); | 191 | machine__create_kernel_maps(&trace->host); |
188 | 192 | ||
189 | if (perf_target__has_task(&trace->opts.target)) { | 193 | if (perf_target__has_task(&trace->opts.target)) { |
190 | err = perf_event__synthesize_thread_map(NULL, evlist->threads, | 194 | err = perf_event__synthesize_thread_map(&trace->tool, evlist->threads, |
191 | trace__tool_process, | 195 | trace__tool_process, |
192 | &trace->host); | 196 | &trace->host); |
193 | } else { | 197 | } else { |
194 | err = perf_event__synthesize_threads(NULL, trace__tool_process, | 198 | err = perf_event__synthesize_threads(&trace->tool, trace__tool_process, |
195 | &trace->host); | 199 | &trace->host); |
196 | } | 200 | } |
197 | 201 | ||
@@ -288,7 +292,7 @@ static struct syscall *trace__syscall_info(struct trace *trace, | |||
288 | int id = perf_evsel__intval(evsel, sample, "id"); | 292 | int id = perf_evsel__intval(evsel, sample, "id"); |
289 | 293 | ||
290 | if (id < 0) { | 294 | if (id < 0) { |
291 | printf("Invalid syscall %d id, skipping...\n", id); | 295 | fprintf(trace->output, "Invalid syscall %d id, skipping...\n", id); |
292 | return NULL; | 296 | return NULL; |
293 | } | 297 | } |
294 | 298 | ||
@@ -302,10 +306,10 @@ static struct syscall *trace__syscall_info(struct trace *trace, | |||
302 | return &trace->syscalls.table[id]; | 306 | return &trace->syscalls.table[id]; |
303 | 307 | ||
304 | out_cant_read: | 308 | out_cant_read: |
305 | printf("Problems reading syscall %d", id); | 309 | fprintf(trace->output, "Problems reading syscall %d", id); |
306 | if (id <= trace->syscalls.max && trace->syscalls.table[id].name != NULL) | 310 | if (id <= trace->syscalls.max && trace->syscalls.table[id].name != NULL) |
307 | printf("(%s)", trace->syscalls.table[id].name); | 311 | fprintf(trace->output, "(%s)", trace->syscalls.table[id].name); |
308 | puts(" information"); | 312 | fputs(" information", trace->output); |
309 | return NULL; | 313 | return NULL; |
310 | } | 314 | } |
311 | 315 | ||
@@ -326,13 +330,13 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, | |||
326 | return 0; | 330 | return 0; |
327 | 331 | ||
328 | thread = machine__findnew_thread(&trace->host, sample->tid); | 332 | thread = machine__findnew_thread(&trace->host, sample->tid); |
329 | ttrace = thread__trace(thread); | 333 | ttrace = thread__trace(thread, trace->output); |
330 | if (ttrace == NULL) | 334 | if (ttrace == NULL) |
331 | return -1; | 335 | return -1; |
332 | 336 | ||
333 | args = perf_evsel__rawptr(evsel, sample, "args"); | 337 | args = perf_evsel__rawptr(evsel, sample, "args"); |
334 | if (args == NULL) { | 338 | if (args == NULL) { |
335 | printf("Problems reading syscall arguments\n"); | 339 | fprintf(trace->output, "Problems reading syscall arguments\n"); |
336 | return -1; | 340 | return -1; |
337 | } | 341 | } |
338 | 342 | ||
@@ -352,8 +356,8 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, | |||
352 | 356 | ||
353 | if (!strcmp(sc->name, "exit_group") || !strcmp(sc->name, "exit")) { | 357 | if (!strcmp(sc->name, "exit_group") || !strcmp(sc->name, "exit")) { |
354 | if (!trace->duration_filter) { | 358 | if (!trace->duration_filter) { |
355 | trace__fprintf_entry_head(trace, thread, 1, sample->time, stdout); | 359 | trace__fprintf_entry_head(trace, thread, 1, sample->time, trace->output); |
356 | printf("%-70s\n", ttrace->entry_str); | 360 | fprintf(trace->output, "%-70s\n", ttrace->entry_str); |
357 | } | 361 | } |
358 | } else | 362 | } else |
359 | ttrace->entry_pending = true; | 363 | ttrace->entry_pending = true; |
@@ -377,7 +381,7 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, | |||
377 | return 0; | 381 | return 0; |
378 | 382 | ||
379 | thread = machine__findnew_thread(&trace->host, sample->tid); | 383 | thread = machine__findnew_thread(&trace->host, sample->tid); |
380 | ttrace = thread__trace(thread); | 384 | ttrace = thread__trace(thread, trace->output); |
381 | if (ttrace == NULL) | 385 | if (ttrace == NULL) |
382 | return -1; | 386 | return -1; |
383 | 387 | ||
@@ -394,14 +398,14 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, | |||
394 | } else if (trace->duration_filter) | 398 | } else if (trace->duration_filter) |
395 | goto out; | 399 | goto out; |
396 | 400 | ||
397 | trace__fprintf_entry_head(trace, thread, duration, sample->time, stdout); | 401 | trace__fprintf_entry_head(trace, thread, duration, sample->time, trace->output); |
398 | 402 | ||
399 | if (ttrace->entry_pending) { | 403 | if (ttrace->entry_pending) { |
400 | printf("%-70s", ttrace->entry_str); | 404 | fprintf(trace->output, "%-70s", ttrace->entry_str); |
401 | } else { | 405 | } else { |
402 | printf(" ... ["); | 406 | fprintf(trace->output, " ... ["); |
403 | color_fprintf(stdout, PERF_COLOR_YELLOW, "continued"); | 407 | color_fprintf(trace->output, PERF_COLOR_YELLOW, "continued"); |
404 | printf("]: %s()", sc->name); | 408 | fprintf(trace->output, "]: %s()", sc->name); |
405 | } | 409 | } |
406 | 410 | ||
407 | if (ret < 0 && sc->fmt && sc->fmt->errmsg) { | 411 | if (ret < 0 && sc->fmt && sc->fmt->errmsg) { |
@@ -409,13 +413,13 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, | |||
409 | const char *emsg = strerror_r(-ret, bf, sizeof(bf)), | 413 | const char *emsg = strerror_r(-ret, bf, sizeof(bf)), |
410 | *e = audit_errno_to_name(-ret); | 414 | *e = audit_errno_to_name(-ret); |
411 | 415 | ||
412 | printf(") = -1 %s %s", e, emsg); | 416 | fprintf(trace->output, ") = -1 %s %s", e, emsg); |
413 | } else if (ret == 0 && sc->fmt && sc->fmt->timeout) | 417 | } else if (ret == 0 && sc->fmt && sc->fmt->timeout) |
414 | printf(") = 0 Timeout"); | 418 | fprintf(trace->output, ") = 0 Timeout"); |
415 | else | 419 | else |
416 | printf(") = %d", ret); | 420 | fprintf(trace->output, ") = %d", ret); |
417 | 421 | ||
418 | putchar('\n'); | 422 | fputc('\n', trace->output); |
419 | out: | 423 | out: |
420 | ttrace->entry_pending = false; | 424 | ttrace->entry_pending = false; |
421 | 425 | ||
@@ -428,7 +432,7 @@ static int trace__sched_stat_runtime(struct trace *trace, struct perf_evsel *evs | |||
428 | u64 runtime = perf_evsel__intval(evsel, sample, "runtime"); | 432 | u64 runtime = perf_evsel__intval(evsel, sample, "runtime"); |
429 | double runtime_ms = (double)runtime / NSEC_PER_MSEC; | 433 | double runtime_ms = (double)runtime / NSEC_PER_MSEC; |
430 | struct thread *thread = machine__findnew_thread(&trace->host, sample->tid); | 434 | struct thread *thread = machine__findnew_thread(&trace->host, sample->tid); |
431 | struct thread_trace *ttrace = thread__trace(thread); | 435 | struct thread_trace *ttrace = thread__trace(thread, trace->output); |
432 | 436 | ||
433 | if (ttrace == NULL) | 437 | if (ttrace == NULL) |
434 | goto out_dump; | 438 | goto out_dump; |
@@ -438,7 +442,7 @@ static int trace__sched_stat_runtime(struct trace *trace, struct perf_evsel *evs | |||
438 | return 0; | 442 | return 0; |
439 | 443 | ||
440 | out_dump: | 444 | out_dump: |
441 | printf("%s: comm=%s,pid=%u,runtime=%" PRIu64 ",vruntime=%" PRIu64 ")\n", | 445 | fprintf(trace->output, "%s: comm=%s,pid=%u,runtime=%" PRIu64 ",vruntime=%" PRIu64 ")\n", |
442 | evsel->name, | 446 | evsel->name, |
443 | perf_evsel__strval(evsel, sample, "comm"), | 447 | perf_evsel__strval(evsel, sample, "comm"), |
444 | (pid_t)perf_evsel__intval(evsel, sample, "pid"), | 448 | (pid_t)perf_evsel__intval(evsel, sample, "pid"), |
@@ -456,32 +460,32 @@ static int trace__run(struct trace *trace, int argc, const char **argv) | |||
456 | const bool forks = argc > 0; | 460 | const bool forks = argc > 0; |
457 | 461 | ||
458 | if (evlist == NULL) { | 462 | if (evlist == NULL) { |
459 | printf("Not enough memory to run!\n"); | 463 | fprintf(trace->output, "Not enough memory to run!\n"); |
460 | goto out; | 464 | goto out; |
461 | } | 465 | } |
462 | 466 | ||
463 | if (perf_evlist__add_newtp(evlist, "raw_syscalls", "sys_enter", trace__sys_enter) || | 467 | if (perf_evlist__add_newtp(evlist, "raw_syscalls", "sys_enter", trace__sys_enter) || |
464 | perf_evlist__add_newtp(evlist, "raw_syscalls", "sys_exit", trace__sys_exit)) { | 468 | perf_evlist__add_newtp(evlist, "raw_syscalls", "sys_exit", trace__sys_exit)) { |
465 | printf("Couldn't read the raw_syscalls tracepoints information!\n"); | 469 | fprintf(trace->output, "Couldn't read the raw_syscalls tracepoints information!\n"); |
466 | goto out_delete_evlist; | 470 | goto out_delete_evlist; |
467 | } | 471 | } |
468 | 472 | ||
469 | if (trace->sched && | 473 | if (trace->sched && |
470 | perf_evlist__add_newtp(evlist, "sched", "sched_stat_runtime", | 474 | perf_evlist__add_newtp(evlist, "sched", "sched_stat_runtime", |
471 | trace__sched_stat_runtime)) { | 475 | trace__sched_stat_runtime)) { |
472 | printf("Couldn't read the sched_stat_runtime tracepoint information!\n"); | 476 | fprintf(trace->output, "Couldn't read the sched_stat_runtime tracepoint information!\n"); |
473 | goto out_delete_evlist; | 477 | goto out_delete_evlist; |
474 | } | 478 | } |
475 | 479 | ||
476 | err = perf_evlist__create_maps(evlist, &trace->opts.target); | 480 | err = perf_evlist__create_maps(evlist, &trace->opts.target); |
477 | if (err < 0) { | 481 | if (err < 0) { |
478 | printf("Problems parsing the target to trace, check your options!\n"); | 482 | fprintf(trace->output, "Problems parsing the target to trace, check your options!\n"); |
479 | goto out_delete_evlist; | 483 | goto out_delete_evlist; |
480 | } | 484 | } |
481 | 485 | ||
482 | err = trace__symbols_init(trace, evlist); | 486 | err = trace__symbols_init(trace, evlist); |
483 | if (err < 0) { | 487 | if (err < 0) { |
484 | printf("Problems initializing symbol libraries!\n"); | 488 | fprintf(trace->output, "Problems initializing symbol libraries!\n"); |
485 | goto out_delete_maps; | 489 | goto out_delete_maps; |
486 | } | 490 | } |
487 | 491 | ||
@@ -494,20 +498,20 @@ static int trace__run(struct trace *trace, int argc, const char **argv) | |||
494 | err = perf_evlist__prepare_workload(evlist, &trace->opts.target, | 498 | err = perf_evlist__prepare_workload(evlist, &trace->opts.target, |
495 | argv, false, false); | 499 | argv, false, false); |
496 | if (err < 0) { | 500 | if (err < 0) { |
497 | printf("Couldn't run the workload!\n"); | 501 | fprintf(trace->output, "Couldn't run the workload!\n"); |
498 | goto out_delete_maps; | 502 | goto out_delete_maps; |
499 | } | 503 | } |
500 | } | 504 | } |
501 | 505 | ||
502 | err = perf_evlist__open(evlist); | 506 | err = perf_evlist__open(evlist); |
503 | if (err < 0) { | 507 | if (err < 0) { |
504 | printf("Couldn't create the events: %s\n", strerror(errno)); | 508 | fprintf(trace->output, "Couldn't create the events: %s\n", strerror(errno)); |
505 | goto out_delete_maps; | 509 | goto out_delete_maps; |
506 | } | 510 | } |
507 | 511 | ||
508 | err = perf_evlist__mmap(evlist, UINT_MAX, false); | 512 | err = perf_evlist__mmap(evlist, UINT_MAX, false); |
509 | if (err < 0) { | 513 | if (err < 0) { |
510 | printf("Couldn't mmap the events: %s\n", strerror(errno)); | 514 | fprintf(trace->output, "Couldn't mmap the events: %s\n", strerror(errno)); |
511 | goto out_close_evlist; | 515 | goto out_close_evlist; |
512 | } | 516 | } |
513 | 517 | ||
@@ -532,7 +536,7 @@ again: | |||
532 | 536 | ||
533 | err = perf_evlist__parse_sample(evlist, event, &sample); | 537 | err = perf_evlist__parse_sample(evlist, event, &sample); |
534 | if (err) { | 538 | if (err) { |
535 | printf("Can't parse sample, err = %d, skipping...\n", err); | 539 | fprintf(trace->output, "Can't parse sample, err = %d, skipping...\n", err); |
536 | continue; | 540 | continue; |
537 | } | 541 | } |
538 | 542 | ||
@@ -540,18 +544,18 @@ again: | |||
540 | trace->base_time = sample.time; | 544 | trace->base_time = sample.time; |
541 | 545 | ||
542 | if (type != PERF_RECORD_SAMPLE) { | 546 | if (type != PERF_RECORD_SAMPLE) { |
543 | trace__process_event(&trace->host, event); | 547 | trace__process_event(trace, &trace->host, event); |
544 | continue; | 548 | continue; |
545 | } | 549 | } |
546 | 550 | ||
547 | evsel = perf_evlist__id2evsel(evlist, sample.id); | 551 | evsel = perf_evlist__id2evsel(evlist, sample.id); |
548 | if (evsel == NULL) { | 552 | if (evsel == NULL) { |
549 | printf("Unknown tp ID %" PRIu64 ", skipping...\n", sample.id); | 553 | fprintf(trace->output, "Unknown tp ID %" PRIu64 ", skipping...\n", sample.id); |
550 | continue; | 554 | continue; |
551 | } | 555 | } |
552 | 556 | ||
553 | if (sample.raw_data == NULL) { | 557 | if (sample.raw_data == NULL) { |
554 | printf("%s sample with no payload for tid: %d, cpu %d, raw_size=%d, skipping...\n", | 558 | fprintf(trace->output, "%s sample with no payload for tid: %d, cpu %d, raw_size=%d, skipping...\n", |
555 | perf_evsel__name(evsel), sample.tid, | 559 | perf_evsel__name(evsel), sample.tid, |
556 | sample.cpu, sample.raw_size); | 560 | sample.cpu, sample.raw_size); |
557 | continue; | 561 | continue; |
@@ -640,6 +644,23 @@ static int trace__set_duration(const struct option *opt, const char *str, | |||
640 | return 0; | 644 | return 0; |
641 | } | 645 | } |
642 | 646 | ||
647 | static int trace__open_output(struct trace *trace, const char *filename) | ||
648 | { | ||
649 | struct stat st; | ||
650 | |||
651 | if (!stat(filename, &st) && st.st_size) { | ||
652 | char oldname[PATH_MAX]; | ||
653 | |||
654 | scnprintf(oldname, sizeof(oldname), "%s.old", filename); | ||
655 | unlink(oldname); | ||
656 | rename(filename, oldname); | ||
657 | } | ||
658 | |||
659 | trace->output = fopen(filename, "w"); | ||
660 | |||
661 | return trace->output == NULL ? -errno : 0; | ||
662 | } | ||
663 | |||
643 | int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) | 664 | int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) |
644 | { | 665 | { |
645 | const char * const trace_usage[] = { | 666 | const char * const trace_usage[] = { |
@@ -662,11 +683,14 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) | |||
662 | .no_delay = true, | 683 | .no_delay = true, |
663 | .mmap_pages = 1024, | 684 | .mmap_pages = 1024, |
664 | }, | 685 | }, |
686 | .output = stdout, | ||
665 | }; | 687 | }; |
688 | const char *output_name = NULL; | ||
666 | const char *ev_qualifier_str = NULL; | 689 | const char *ev_qualifier_str = NULL; |
667 | const struct option trace_options[] = { | 690 | const struct option trace_options[] = { |
668 | OPT_STRING('e', "expr", &ev_qualifier_str, "expr", | 691 | OPT_STRING('e', "expr", &ev_qualifier_str, "expr", |
669 | "list of events to trace"), | 692 | "list of events to trace"), |
693 | OPT_STRING('o', "output", &output_name, "file", "output file name"), | ||
670 | OPT_STRING('p', "pid", &trace.opts.target.pid, "pid", | 694 | OPT_STRING('p', "pid", &trace.opts.target.pid, "pid", |
671 | "trace events on existing process id"), | 695 | "trace events on existing process id"), |
672 | OPT_STRING(0, "tid", &trace.opts.target.tid, "tid", | 696 | OPT_STRING(0, "tid", &trace.opts.target.tid, "tid", |
@@ -692,26 +716,36 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) | |||
692 | 716 | ||
693 | argc = parse_options(argc, argv, trace_options, trace_usage, 0); | 717 | argc = parse_options(argc, argv, trace_options, trace_usage, 0); |
694 | 718 | ||
719 | if (output_name != NULL) { | ||
720 | err = trace__open_output(&trace, output_name); | ||
721 | if (err < 0) { | ||
722 | perror("failed to create output file"); | ||
723 | goto out; | ||
724 | } | ||
725 | } | ||
726 | |||
695 | if (ev_qualifier_str != NULL) { | 727 | if (ev_qualifier_str != NULL) { |
696 | trace.ev_qualifier = strlist__new(true, ev_qualifier_str); | 728 | trace.ev_qualifier = strlist__new(true, ev_qualifier_str); |
697 | if (trace.ev_qualifier == NULL) { | 729 | if (trace.ev_qualifier == NULL) { |
698 | puts("Not enough memory to parse event qualifier"); | 730 | fputs("Not enough memory to parse event qualifier", |
699 | return -ENOMEM; | 731 | trace.output); |
732 | err = -ENOMEM; | ||
733 | goto out_close; | ||
700 | } | 734 | } |
701 | } | 735 | } |
702 | 736 | ||
703 | err = perf_target__validate(&trace.opts.target); | 737 | err = perf_target__validate(&trace.opts.target); |
704 | if (err) { | 738 | if (err) { |
705 | perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf)); | 739 | perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf)); |
706 | printf("%s", bf); | 740 | fprintf(trace.output, "%s", bf); |
707 | return err; | 741 | goto out_close; |
708 | } | 742 | } |
709 | 743 | ||
710 | err = perf_target__parse_uid(&trace.opts.target); | 744 | err = perf_target__parse_uid(&trace.opts.target); |
711 | if (err) { | 745 | if (err) { |
712 | perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf)); | 746 | perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf)); |
713 | printf("%s", bf); | 747 | fprintf(trace.output, "%s", bf); |
714 | return err; | 748 | goto out_close; |
715 | } | 749 | } |
716 | 750 | ||
717 | if (!argc && perf_target__none(&trace.opts.target)) | 751 | if (!argc && perf_target__none(&trace.opts.target)) |
@@ -720,7 +754,11 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) | |||
720 | err = trace__run(&trace, argc, argv); | 754 | err = trace__run(&trace, argc, argv); |
721 | 755 | ||
722 | if (trace.sched && !err) | 756 | if (trace.sched && !err) |
723 | trace__fprintf_thread_summary(&trace, stdout); | 757 | trace__fprintf_thread_summary(&trace, trace.output); |
724 | 758 | ||
759 | out_close: | ||
760 | if (output_name != NULL) | ||
761 | fclose(trace.output); | ||
762 | out: | ||
725 | return err; | 763 | return err; |
726 | } | 764 | } |