diff options
Diffstat (limited to 'tools/perf/builtin-record.c')
-rw-r--r-- | tools/perf/builtin-record.c | 201 |
1 files changed, 79 insertions, 122 deletions
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index d04651484640..7c8020a32784 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
@@ -24,12 +24,13 @@ | |||
24 | #include "util/symbol.h" | 24 | #include "util/symbol.h" |
25 | #include "util/cpumap.h" | 25 | #include "util/cpumap.h" |
26 | #include "util/thread_map.h" | 26 | #include "util/thread_map.h" |
27 | #include "util/data.h" | ||
27 | 28 | ||
28 | #include <unistd.h> | 29 | #include <unistd.h> |
29 | #include <sched.h> | 30 | #include <sched.h> |
30 | #include <sys/mman.h> | 31 | #include <sys/mman.h> |
31 | 32 | ||
32 | #ifndef HAVE_ON_EXIT | 33 | #ifndef HAVE_ON_EXIT_SUPPORT |
33 | #ifndef ATEXIT_MAX | 34 | #ifndef ATEXIT_MAX |
34 | #define ATEXIT_MAX 32 | 35 | #define ATEXIT_MAX 32 |
35 | #endif | 36 | #endif |
@@ -65,31 +66,25 @@ struct perf_record { | |||
65 | struct perf_tool tool; | 66 | struct perf_tool tool; |
66 | struct perf_record_opts opts; | 67 | struct perf_record_opts opts; |
67 | u64 bytes_written; | 68 | u64 bytes_written; |
68 | const char *output_name; | 69 | struct perf_data_file file; |
69 | struct perf_evlist *evlist; | 70 | struct perf_evlist *evlist; |
70 | struct perf_session *session; | 71 | struct perf_session *session; |
71 | const char *progname; | 72 | const char *progname; |
72 | int output; | ||
73 | unsigned int page_size; | ||
74 | int realtime_prio; | 73 | int realtime_prio; |
75 | bool no_buildid; | 74 | bool no_buildid; |
76 | bool no_buildid_cache; | 75 | bool no_buildid_cache; |
77 | long samples; | 76 | long samples; |
78 | off_t post_processing_offset; | ||
79 | }; | 77 | }; |
80 | 78 | ||
81 | static void advance_output(struct perf_record *rec, size_t size) | 79 | static int do_write_output(struct perf_record *rec, void *buf, size_t size) |
82 | { | 80 | { |
83 | rec->bytes_written += size; | 81 | struct perf_data_file *file = &rec->file; |
84 | } | ||
85 | 82 | ||
86 | static int write_output(struct perf_record *rec, void *buf, size_t size) | ||
87 | { | ||
88 | while (size) { | 83 | while (size) { |
89 | int ret = write(rec->output, buf, size); | 84 | ssize_t ret = write(file->fd, buf, size); |
90 | 85 | ||
91 | if (ret < 0) { | 86 | if (ret < 0) { |
92 | pr_err("failed to write\n"); | 87 | pr_err("failed to write perf data, error: %m\n"); |
93 | return -1; | 88 | return -1; |
94 | } | 89 | } |
95 | 90 | ||
@@ -102,6 +97,11 @@ static int write_output(struct perf_record *rec, void *buf, size_t size) | |||
102 | return 0; | 97 | return 0; |
103 | } | 98 | } |
104 | 99 | ||
100 | static int write_output(struct perf_record *rec, void *buf, size_t size) | ||
101 | { | ||
102 | return do_write_output(rec, buf, size); | ||
103 | } | ||
104 | |||
105 | static int process_synthesized_event(struct perf_tool *tool, | 105 | static int process_synthesized_event(struct perf_tool *tool, |
106 | union perf_event *event, | 106 | union perf_event *event, |
107 | struct perf_sample *sample __maybe_unused, | 107 | struct perf_sample *sample __maybe_unused, |
@@ -119,7 +119,7 @@ static int perf_record__mmap_read(struct perf_record *rec, | |||
119 | { | 119 | { |
120 | unsigned int head = perf_mmap__read_head(md); | 120 | unsigned int head = perf_mmap__read_head(md); |
121 | unsigned int old = md->prev; | 121 | unsigned int old = md->prev; |
122 | unsigned char *data = md->base + rec->page_size; | 122 | unsigned char *data = md->base + page_size; |
123 | unsigned long size; | 123 | unsigned long size; |
124 | void *buf; | 124 | void *buf; |
125 | int rc = 0; | 125 | int rc = 0; |
@@ -234,10 +234,6 @@ try_again: | |||
234 | "or try again with a smaller value of -m/--mmap_pages.\n" | 234 | "or try again with a smaller value of -m/--mmap_pages.\n" |
235 | "(current value: %d)\n", opts->mmap_pages); | 235 | "(current value: %d)\n", opts->mmap_pages); |
236 | rc = -errno; | 236 | rc = -errno; |
237 | } else if (!is_power_of_2(opts->mmap_pages) && | ||
238 | (opts->mmap_pages != UINT_MAX)) { | ||
239 | pr_err("--mmap_pages/-m value must be a power of two."); | ||
240 | rc = -EINVAL; | ||
241 | } else { | 237 | } else { |
242 | pr_err("failed to mmap with %d (%s)\n", errno, strerror(errno)); | 238 | pr_err("failed to mmap with %d (%s)\n", errno, strerror(errno)); |
243 | rc = -errno; | 239 | rc = -errno; |
@@ -253,31 +249,34 @@ out: | |||
253 | 249 | ||
254 | static int process_buildids(struct perf_record *rec) | 250 | static int process_buildids(struct perf_record *rec) |
255 | { | 251 | { |
256 | u64 size = lseek(rec->output, 0, SEEK_CUR); | 252 | struct perf_data_file *file = &rec->file; |
253 | struct perf_session *session = rec->session; | ||
254 | u64 start = session->header.data_offset; | ||
257 | 255 | ||
256 | u64 size = lseek(file->fd, 0, SEEK_CUR); | ||
258 | if (size == 0) | 257 | if (size == 0) |
259 | return 0; | 258 | return 0; |
260 | 259 | ||
261 | rec->session->fd = rec->output; | 260 | return __perf_session__process_events(session, start, |
262 | return __perf_session__process_events(rec->session, rec->post_processing_offset, | 261 | size - start, |
263 | size - rec->post_processing_offset, | ||
264 | size, &build_id__mark_dso_hit_ops); | 262 | size, &build_id__mark_dso_hit_ops); |
265 | } | 263 | } |
266 | 264 | ||
267 | static void perf_record__exit(int status, void *arg) | 265 | static void perf_record__exit(int status, void *arg) |
268 | { | 266 | { |
269 | struct perf_record *rec = arg; | 267 | struct perf_record *rec = arg; |
268 | struct perf_data_file *file = &rec->file; | ||
270 | 269 | ||
271 | if (status != 0) | 270 | if (status != 0) |
272 | return; | 271 | return; |
273 | 272 | ||
274 | if (!rec->opts.pipe_output) { | 273 | if (!file->is_pipe) { |
275 | rec->session->header.data_size += rec->bytes_written; | 274 | rec->session->header.data_size += rec->bytes_written; |
276 | 275 | ||
277 | if (!rec->no_buildid) | 276 | if (!rec->no_buildid) |
278 | process_buildids(rec); | 277 | process_buildids(rec); |
279 | perf_session__write_header(rec->session, rec->evlist, | 278 | perf_session__write_header(rec->session, rec->evlist, |
280 | rec->output, true); | 279 | file->fd, true); |
281 | perf_session__delete(rec->session); | 280 | perf_session__delete(rec->session); |
282 | perf_evlist__delete(rec->evlist); | 281 | perf_evlist__delete(rec->evlist); |
283 | symbol__exit(); | 282 | symbol__exit(); |
@@ -343,64 +342,47 @@ out: | |||
343 | return rc; | 342 | return rc; |
344 | } | 343 | } |
345 | 344 | ||
345 | static void perf_record__init_features(struct perf_record *rec) | ||
346 | { | ||
347 | struct perf_evlist *evsel_list = rec->evlist; | ||
348 | struct perf_session *session = rec->session; | ||
349 | int feat; | ||
350 | |||
351 | for (feat = HEADER_FIRST_FEATURE; feat < HEADER_LAST_FEATURE; feat++) | ||
352 | perf_header__set_feat(&session->header, feat); | ||
353 | |||
354 | if (rec->no_buildid) | ||
355 | perf_header__clear_feat(&session->header, HEADER_BUILD_ID); | ||
356 | |||
357 | if (!have_tracepoints(&evsel_list->entries)) | ||
358 | perf_header__clear_feat(&session->header, HEADER_TRACING_DATA); | ||
359 | |||
360 | if (!rec->opts.branch_stack) | ||
361 | perf_header__clear_feat(&session->header, HEADER_BRANCH_STACK); | ||
362 | } | ||
363 | |||
346 | static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | 364 | static int __cmd_record(struct perf_record *rec, int argc, const char **argv) |
347 | { | 365 | { |
348 | struct stat st; | 366 | int err; |
349 | int flags; | ||
350 | int err, output, feat; | ||
351 | unsigned long waking = 0; | 367 | unsigned long waking = 0; |
352 | const bool forks = argc > 0; | 368 | const bool forks = argc > 0; |
353 | struct machine *machine; | 369 | struct machine *machine; |
354 | struct perf_tool *tool = &rec->tool; | 370 | struct perf_tool *tool = &rec->tool; |
355 | struct perf_record_opts *opts = &rec->opts; | 371 | struct perf_record_opts *opts = &rec->opts; |
356 | struct perf_evlist *evsel_list = rec->evlist; | 372 | struct perf_evlist *evsel_list = rec->evlist; |
357 | const char *output_name = rec->output_name; | 373 | struct perf_data_file *file = &rec->file; |
358 | struct perf_session *session; | 374 | struct perf_session *session; |
359 | bool disabled = false; | 375 | bool disabled = false; |
360 | 376 | ||
361 | rec->progname = argv[0]; | 377 | rec->progname = argv[0]; |
362 | 378 | ||
363 | rec->page_size = sysconf(_SC_PAGE_SIZE); | ||
364 | |||
365 | on_exit(perf_record__sig_exit, rec); | 379 | on_exit(perf_record__sig_exit, rec); |
366 | signal(SIGCHLD, sig_handler); | 380 | signal(SIGCHLD, sig_handler); |
367 | signal(SIGINT, sig_handler); | 381 | signal(SIGINT, sig_handler); |
368 | signal(SIGUSR1, sig_handler); | 382 | signal(SIGUSR1, sig_handler); |
369 | signal(SIGTERM, sig_handler); | 383 | signal(SIGTERM, sig_handler); |
370 | 384 | ||
371 | if (!output_name) { | 385 | session = perf_session__new(file, false, NULL); |
372 | if (!fstat(STDOUT_FILENO, &st) && S_ISFIFO(st.st_mode)) | ||
373 | opts->pipe_output = true; | ||
374 | else | ||
375 | rec->output_name = output_name = "perf.data"; | ||
376 | } | ||
377 | if (output_name) { | ||
378 | if (!strcmp(output_name, "-")) | ||
379 | opts->pipe_output = true; | ||
380 | else if (!stat(output_name, &st) && st.st_size) { | ||
381 | char oldname[PATH_MAX]; | ||
382 | snprintf(oldname, sizeof(oldname), "%s.old", | ||
383 | output_name); | ||
384 | unlink(oldname); | ||
385 | rename(output_name, oldname); | ||
386 | } | ||
387 | } | ||
388 | |||
389 | flags = O_CREAT|O_RDWR|O_TRUNC; | ||
390 | |||
391 | if (opts->pipe_output) | ||
392 | output = STDOUT_FILENO; | ||
393 | else | ||
394 | output = open(output_name, flags, S_IRUSR | S_IWUSR); | ||
395 | if (output < 0) { | ||
396 | perror("failed to create output file"); | ||
397 | return -1; | ||
398 | } | ||
399 | |||
400 | rec->output = output; | ||
401 | |||
402 | session = perf_session__new(output_name, O_WRONLY, | ||
403 | true, false, NULL); | ||
404 | if (session == NULL) { | 386 | if (session == NULL) { |
405 | pr_err("Not enough memory for reading perf file header\n"); | 387 | pr_err("Not enough memory for reading perf file header\n"); |
406 | return -1; | 388 | return -1; |
@@ -408,21 +390,11 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
408 | 390 | ||
409 | rec->session = session; | 391 | rec->session = session; |
410 | 392 | ||
411 | for (feat = HEADER_FIRST_FEATURE; feat < HEADER_LAST_FEATURE; feat++) | 393 | perf_record__init_features(rec); |
412 | perf_header__set_feat(&session->header, feat); | ||
413 | |||
414 | if (rec->no_buildid) | ||
415 | perf_header__clear_feat(&session->header, HEADER_BUILD_ID); | ||
416 | |||
417 | if (!have_tracepoints(&evsel_list->entries)) | ||
418 | perf_header__clear_feat(&session->header, HEADER_TRACING_DATA); | ||
419 | |||
420 | if (!rec->opts.branch_stack) | ||
421 | perf_header__clear_feat(&session->header, HEADER_BRANCH_STACK); | ||
422 | 394 | ||
423 | if (forks) { | 395 | if (forks) { |
424 | err = perf_evlist__prepare_workload(evsel_list, &opts->target, | 396 | err = perf_evlist__prepare_workload(evsel_list, &opts->target, |
425 | argv, opts->pipe_output, | 397 | argv, file->is_pipe, |
426 | true); | 398 | true); |
427 | if (err < 0) { | 399 | if (err < 0) { |
428 | pr_err("Couldn't run the workload!\n"); | 400 | pr_err("Couldn't run the workload!\n"); |
@@ -443,13 +415,13 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
443 | */ | 415 | */ |
444 | on_exit(perf_record__exit, rec); | 416 | on_exit(perf_record__exit, rec); |
445 | 417 | ||
446 | if (opts->pipe_output) { | 418 | if (file->is_pipe) { |
447 | err = perf_header__write_pipe(output); | 419 | err = perf_header__write_pipe(file->fd); |
448 | if (err < 0) | 420 | if (err < 0) |
449 | goto out_delete_session; | 421 | goto out_delete_session; |
450 | } else { | 422 | } else { |
451 | err = perf_session__write_header(session, evsel_list, | 423 | err = perf_session__write_header(session, evsel_list, |
452 | output, false); | 424 | file->fd, false); |
453 | if (err < 0) | 425 | if (err < 0) |
454 | goto out_delete_session; | 426 | goto out_delete_session; |
455 | } | 427 | } |
@@ -462,11 +434,9 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
462 | goto out_delete_session; | 434 | goto out_delete_session; |
463 | } | 435 | } |
464 | 436 | ||
465 | rec->post_processing_offset = lseek(output, 0, SEEK_CUR); | ||
466 | |||
467 | machine = &session->machines.host; | 437 | machine = &session->machines.host; |
468 | 438 | ||
469 | if (opts->pipe_output) { | 439 | if (file->is_pipe) { |
470 | err = perf_event__synthesize_attrs(tool, session, | 440 | err = perf_event__synthesize_attrs(tool, session, |
471 | process_synthesized_event); | 441 | process_synthesized_event); |
472 | if (err < 0) { | 442 | if (err < 0) { |
@@ -483,13 +453,13 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
483 | * return this more properly and also | 453 | * return this more properly and also |
484 | * propagate errors that now are calling die() | 454 | * propagate errors that now are calling die() |
485 | */ | 455 | */ |
486 | err = perf_event__synthesize_tracing_data(tool, output, evsel_list, | 456 | err = perf_event__synthesize_tracing_data(tool, file->fd, evsel_list, |
487 | process_synthesized_event); | 457 | process_synthesized_event); |
488 | if (err <= 0) { | 458 | if (err <= 0) { |
489 | pr_err("Couldn't record tracing data.\n"); | 459 | pr_err("Couldn't record tracing data.\n"); |
490 | goto out_delete_session; | 460 | goto out_delete_session; |
491 | } | 461 | } |
492 | advance_output(rec, err); | 462 | rec->bytes_written += err; |
493 | } | 463 | } |
494 | } | 464 | } |
495 | 465 | ||
@@ -515,16 +485,8 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
515 | perf_event__synthesize_guest_os, tool); | 485 | perf_event__synthesize_guest_os, tool); |
516 | } | 486 | } |
517 | 487 | ||
518 | if (perf_target__has_task(&opts->target)) | 488 | err = __machine__synthesize_threads(machine, tool, &opts->target, evsel_list->threads, |
519 | err = perf_event__synthesize_thread_map(tool, evsel_list->threads, | 489 | process_synthesized_event, opts->sample_address); |
520 | process_synthesized_event, | ||
521 | machine); | ||
522 | else if (perf_target__has_cpu(&opts->target)) | ||
523 | err = perf_event__synthesize_threads(tool, process_synthesized_event, | ||
524 | machine); | ||
525 | else /* command specified */ | ||
526 | err = 0; | ||
527 | |||
528 | if (err != 0) | 490 | if (err != 0) |
529 | goto out_delete_session; | 491 | goto out_delete_session; |
530 | 492 | ||
@@ -544,7 +506,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
544 | * (apart from group members) have enable_on_exec=1 set, | 506 | * (apart from group members) have enable_on_exec=1 set, |
545 | * so don't spoil it by prematurely enabling them. | 507 | * so don't spoil it by prematurely enabling them. |
546 | */ | 508 | */ |
547 | if (!perf_target__none(&opts->target)) | 509 | if (!target__none(&opts->target)) |
548 | perf_evlist__enable(evsel_list); | 510 | perf_evlist__enable(evsel_list); |
549 | 511 | ||
550 | /* | 512 | /* |
@@ -573,7 +535,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
573 | * die with the process and we wait for that. Thus no need to | 535 | * die with the process and we wait for that. Thus no need to |
574 | * disable events in this case. | 536 | * disable events in this case. |
575 | */ | 537 | */ |
576 | if (done && !disabled && !perf_target__none(&opts->target)) { | 538 | if (done && !disabled && !target__none(&opts->target)) { |
577 | perf_evlist__disable(evsel_list); | 539 | perf_evlist__disable(evsel_list); |
578 | disabled = true; | 540 | disabled = true; |
579 | } | 541 | } |
@@ -590,7 +552,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
590 | fprintf(stderr, | 552 | fprintf(stderr, |
591 | "[ perf record: Captured and wrote %.3f MB %s (~%" PRIu64 " samples) ]\n", | 553 | "[ perf record: Captured and wrote %.3f MB %s (~%" PRIu64 " samples) ]\n", |
592 | (double)rec->bytes_written / 1024.0 / 1024.0, | 554 | (double)rec->bytes_written / 1024.0 / 1024.0, |
593 | output_name, | 555 | file->path, |
594 | rec->bytes_written / 24); | 556 | rec->bytes_written / 24); |
595 | 557 | ||
596 | return 0; | 558 | return 0; |
@@ -618,6 +580,9 @@ static const struct branch_mode branch_modes[] = { | |||
618 | BRANCH_OPT("any_call", PERF_SAMPLE_BRANCH_ANY_CALL), | 580 | BRANCH_OPT("any_call", PERF_SAMPLE_BRANCH_ANY_CALL), |
619 | BRANCH_OPT("any_ret", PERF_SAMPLE_BRANCH_ANY_RETURN), | 581 | BRANCH_OPT("any_ret", PERF_SAMPLE_BRANCH_ANY_RETURN), |
620 | BRANCH_OPT("ind_call", PERF_SAMPLE_BRANCH_IND_CALL), | 582 | BRANCH_OPT("ind_call", PERF_SAMPLE_BRANCH_IND_CALL), |
583 | BRANCH_OPT("abort_tx", PERF_SAMPLE_BRANCH_ABORT_TX), | ||
584 | BRANCH_OPT("in_tx", PERF_SAMPLE_BRANCH_IN_TX), | ||
585 | BRANCH_OPT("no_tx", PERF_SAMPLE_BRANCH_NO_TX), | ||
621 | BRANCH_END | 586 | BRANCH_END |
622 | }; | 587 | }; |
623 | 588 | ||
@@ -684,7 +649,7 @@ error: | |||
684 | return ret; | 649 | return ret; |
685 | } | 650 | } |
686 | 651 | ||
687 | #ifdef LIBUNWIND_SUPPORT | 652 | #ifdef HAVE_LIBUNWIND_SUPPORT |
688 | static int get_stack_size(char *str, unsigned long *_size) | 653 | static int get_stack_size(char *str, unsigned long *_size) |
689 | { | 654 | { |
690 | char *endptr; | 655 | char *endptr; |
@@ -710,7 +675,7 @@ static int get_stack_size(char *str, unsigned long *_size) | |||
710 | max_size, str); | 675 | max_size, str); |
711 | return -1; | 676 | return -1; |
712 | } | 677 | } |
713 | #endif /* LIBUNWIND_SUPPORT */ | 678 | #endif /* HAVE_LIBUNWIND_SUPPORT */ |
714 | 679 | ||
715 | int record_parse_callchain(const char *arg, struct perf_record_opts *opts) | 680 | int record_parse_callchain(const char *arg, struct perf_record_opts *opts) |
716 | { | 681 | { |
@@ -739,7 +704,7 @@ int record_parse_callchain(const char *arg, struct perf_record_opts *opts) | |||
739 | "needed for -g fp\n"); | 704 | "needed for -g fp\n"); |
740 | break; | 705 | break; |
741 | 706 | ||
742 | #ifdef LIBUNWIND_SUPPORT | 707 | #ifdef HAVE_LIBUNWIND_SUPPORT |
743 | /* Dwarf style */ | 708 | /* Dwarf style */ |
744 | } else if (!strncmp(name, "dwarf", sizeof("dwarf"))) { | 709 | } else if (!strncmp(name, "dwarf", sizeof("dwarf"))) { |
745 | const unsigned long default_stack_dump_size = 8192; | 710 | const unsigned long default_stack_dump_size = 8192; |
@@ -755,7 +720,7 @@ int record_parse_callchain(const char *arg, struct perf_record_opts *opts) | |||
755 | ret = get_stack_size(tok, &size); | 720 | ret = get_stack_size(tok, &size); |
756 | opts->stack_dump_size = size; | 721 | opts->stack_dump_size = size; |
757 | } | 722 | } |
758 | #endif /* LIBUNWIND_SUPPORT */ | 723 | #endif /* HAVE_LIBUNWIND_SUPPORT */ |
759 | } else { | 724 | } else { |
760 | pr_err("callchain: Unknown --call-graph option " | 725 | pr_err("callchain: Unknown --call-graph option " |
761 | "value: %s\n", arg); | 726 | "value: %s\n", arg); |
@@ -841,7 +806,7 @@ static struct perf_record record = { | |||
841 | 806 | ||
842 | #define CALLCHAIN_HELP "setup and enables call-graph (stack chain/backtrace) recording: " | 807 | #define CALLCHAIN_HELP "setup and enables call-graph (stack chain/backtrace) recording: " |
843 | 808 | ||
844 | #ifdef LIBUNWIND_SUPPORT | 809 | #ifdef HAVE_LIBUNWIND_SUPPORT |
845 | const char record_callchain_help[] = CALLCHAIN_HELP "fp dwarf"; | 810 | const char record_callchain_help[] = CALLCHAIN_HELP "fp dwarf"; |
846 | #else | 811 | #else |
847 | const char record_callchain_help[] = CALLCHAIN_HELP "fp"; | 812 | const char record_callchain_help[] = CALLCHAIN_HELP "fp"; |
@@ -875,13 +840,14 @@ const struct option record_options[] = { | |||
875 | OPT_STRING('C', "cpu", &record.opts.target.cpu_list, "cpu", | 840 | OPT_STRING('C', "cpu", &record.opts.target.cpu_list, "cpu", |
876 | "list of cpus to monitor"), | 841 | "list of cpus to monitor"), |
877 | OPT_U64('c', "count", &record.opts.user_interval, "event period to sample"), | 842 | OPT_U64('c', "count", &record.opts.user_interval, "event period to sample"), |
878 | OPT_STRING('o', "output", &record.output_name, "file", | 843 | OPT_STRING('o', "output", &record.file.path, "file", |
879 | "output file name"), | 844 | "output file name"), |
880 | OPT_BOOLEAN('i', "no-inherit", &record.opts.no_inherit, | 845 | OPT_BOOLEAN('i', "no-inherit", &record.opts.no_inherit, |
881 | "child tasks do not inherit counters"), | 846 | "child tasks do not inherit counters"), |
882 | OPT_UINTEGER('F', "freq", &record.opts.user_freq, "profile at this frequency"), | 847 | OPT_UINTEGER('F', "freq", &record.opts.user_freq, "profile at this frequency"), |
883 | OPT_UINTEGER('m', "mmap-pages", &record.opts.mmap_pages, | 848 | OPT_CALLBACK('m', "mmap-pages", &record.opts.mmap_pages, "pages", |
884 | "number of mmap data pages"), | 849 | "number of mmap data pages", |
850 | perf_evlist__parse_mmap_pages), | ||
885 | OPT_BOOLEAN(0, "group", &record.opts.group, | 851 | OPT_BOOLEAN(0, "group", &record.opts.group, |
886 | "put the counters into a counter group"), | 852 | "put the counters into a counter group"), |
887 | OPT_CALLBACK_NOOPT('g', NULL, &record.opts, | 853 | OPT_CALLBACK_NOOPT('g', NULL, &record.opts, |
@@ -920,6 +886,10 @@ const struct option record_options[] = { | |||
920 | parse_branch_stack), | 886 | parse_branch_stack), |
921 | OPT_BOOLEAN('W', "weight", &record.opts.sample_weight, | 887 | OPT_BOOLEAN('W', "weight", &record.opts.sample_weight, |
922 | "sample by weight (on special events only)"), | 888 | "sample by weight (on special events only)"), |
889 | OPT_BOOLEAN(0, "transaction", &record.opts.sample_transaction, | ||
890 | "sample transaction flags (special events only)"), | ||
891 | OPT_BOOLEAN(0, "force-per-cpu", &record.opts.target.force_per_cpu, | ||
892 | "force the use of per-cpu mmaps"), | ||
923 | OPT_END() | 893 | OPT_END() |
924 | }; | 894 | }; |
925 | 895 | ||
@@ -938,7 +908,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) | |||
938 | 908 | ||
939 | argc = parse_options(argc, argv, record_options, record_usage, | 909 | argc = parse_options(argc, argv, record_options, record_usage, |
940 | PARSE_OPT_STOP_AT_NON_OPTION); | 910 | PARSE_OPT_STOP_AT_NON_OPTION); |
941 | if (!argc && perf_target__none(&rec->opts.target)) | 911 | if (!argc && target__none(&rec->opts.target)) |
942 | usage_with_options(record_usage, record_options); | 912 | usage_with_options(record_usage, record_options); |
943 | 913 | ||
944 | if (nr_cgroups && !rec->opts.target.system_wide) { | 914 | if (nr_cgroups && !rec->opts.target.system_wide) { |
@@ -968,17 +938,17 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) | |||
968 | goto out_symbol_exit; | 938 | goto out_symbol_exit; |
969 | } | 939 | } |
970 | 940 | ||
971 | err = perf_target__validate(&rec->opts.target); | 941 | err = target__validate(&rec->opts.target); |
972 | if (err) { | 942 | if (err) { |
973 | perf_target__strerror(&rec->opts.target, err, errbuf, BUFSIZ); | 943 | target__strerror(&rec->opts.target, err, errbuf, BUFSIZ); |
974 | ui__warning("%s", errbuf); | 944 | ui__warning("%s", errbuf); |
975 | } | 945 | } |
976 | 946 | ||
977 | err = perf_target__parse_uid(&rec->opts.target); | 947 | err = target__parse_uid(&rec->opts.target); |
978 | if (err) { | 948 | if (err) { |
979 | int saved_errno = errno; | 949 | int saved_errno = errno; |
980 | 950 | ||
981 | perf_target__strerror(&rec->opts.target, err, errbuf, BUFSIZ); | 951 | target__strerror(&rec->opts.target, err, errbuf, BUFSIZ); |
982 | ui__error("%s", errbuf); | 952 | ui__error("%s", errbuf); |
983 | 953 | ||
984 | err = -saved_errno; | 954 | err = -saved_errno; |
@@ -989,20 +959,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) | |||
989 | if (perf_evlist__create_maps(evsel_list, &rec->opts.target) < 0) | 959 | if (perf_evlist__create_maps(evsel_list, &rec->opts.target) < 0) |
990 | usage_with_options(record_usage, record_options); | 960 | usage_with_options(record_usage, record_options); |
991 | 961 | ||
992 | if (rec->opts.user_interval != ULLONG_MAX) | 962 | if (perf_record_opts__config(&rec->opts)) { |
993 | rec->opts.default_interval = rec->opts.user_interval; | ||
994 | if (rec->opts.user_freq != UINT_MAX) | ||
995 | rec->opts.freq = rec->opts.user_freq; | ||
996 | |||
997 | /* | ||
998 | * User specified count overrides default frequency. | ||
999 | */ | ||
1000 | if (rec->opts.default_interval) | ||
1001 | rec->opts.freq = 0; | ||
1002 | else if (rec->opts.freq) { | ||
1003 | rec->opts.default_interval = rec->opts.freq; | ||
1004 | } else { | ||
1005 | ui__error("frequency and count are zero, aborting\n"); | ||
1006 | err = -EINVAL; | 963 | err = -EINVAL; |
1007 | goto out_free_fd; | 964 | goto out_free_fd; |
1008 | } | 965 | } |