diff options
Diffstat (limited to 'tools/perf/builtin-record.c')
-rw-r--r-- | tools/perf/builtin-record.c | 274 |
1 files changed, 130 insertions, 144 deletions
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index a41ac41546c9..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,23 +675,14 @@ 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_opt(const struct option *opt, | 680 | int record_parse_callchain(const char *arg, struct perf_record_opts *opts) |
716 | const char *arg, int unset) | ||
717 | { | 681 | { |
718 | struct perf_record_opts *opts = opt->value; | ||
719 | char *tok, *name, *saveptr = NULL; | 682 | char *tok, *name, *saveptr = NULL; |
720 | char *buf; | 683 | char *buf; |
721 | int ret = -1; | 684 | int ret = -1; |
722 | 685 | ||
723 | /* --no-call-graph */ | ||
724 | if (unset) | ||
725 | return 0; | ||
726 | |||
727 | /* We specified default option if none is provided. */ | ||
728 | BUG_ON(!arg); | ||
729 | |||
730 | /* We need buffer that we know we can write to. */ | 686 | /* We need buffer that we know we can write to. */ |
731 | buf = malloc(strlen(arg) + 1); | 687 | buf = malloc(strlen(arg) + 1); |
732 | if (!buf) | 688 | if (!buf) |
@@ -748,7 +704,7 @@ int record_parse_callchain_opt(const struct option *opt, | |||
748 | "needed for -g fp\n"); | 704 | "needed for -g fp\n"); |
749 | break; | 705 | break; |
750 | 706 | ||
751 | #ifdef LIBUNWIND_SUPPORT | 707 | #ifdef HAVE_LIBUNWIND_SUPPORT |
752 | /* Dwarf style */ | 708 | /* Dwarf style */ |
753 | } else if (!strncmp(name, "dwarf", sizeof("dwarf"))) { | 709 | } else if (!strncmp(name, "dwarf", sizeof("dwarf"))) { |
754 | const unsigned long default_stack_dump_size = 8192; | 710 | const unsigned long default_stack_dump_size = 8192; |
@@ -764,13 +720,9 @@ int record_parse_callchain_opt(const struct option *opt, | |||
764 | ret = get_stack_size(tok, &size); | 720 | ret = get_stack_size(tok, &size); |
765 | opts->stack_dump_size = size; | 721 | opts->stack_dump_size = size; |
766 | } | 722 | } |
767 | 723 | #endif /* HAVE_LIBUNWIND_SUPPORT */ | |
768 | if (!ret) | ||
769 | pr_debug("callchain: stack dump size %d\n", | ||
770 | opts->stack_dump_size); | ||
771 | #endif /* LIBUNWIND_SUPPORT */ | ||
772 | } else { | 724 | } else { |
773 | pr_err("callchain: Unknown -g option " | 725 | pr_err("callchain: Unknown --call-graph option " |
774 | "value: %s\n", arg); | 726 | "value: %s\n", arg); |
775 | break; | 727 | break; |
776 | } | 728 | } |
@@ -778,13 +730,52 @@ int record_parse_callchain_opt(const struct option *opt, | |||
778 | } while (0); | 730 | } while (0); |
779 | 731 | ||
780 | free(buf); | 732 | free(buf); |
733 | return ret; | ||
734 | } | ||
781 | 735 | ||
736 | static void callchain_debug(struct perf_record_opts *opts) | ||
737 | { | ||
738 | pr_debug("callchain: type %d\n", opts->call_graph); | ||
739 | |||
740 | if (opts->call_graph == CALLCHAIN_DWARF) | ||
741 | pr_debug("callchain: stack dump size %d\n", | ||
742 | opts->stack_dump_size); | ||
743 | } | ||
744 | |||
745 | int record_parse_callchain_opt(const struct option *opt, | ||
746 | const char *arg, | ||
747 | int unset) | ||
748 | { | ||
749 | struct perf_record_opts *opts = opt->value; | ||
750 | int ret; | ||
751 | |||
752 | /* --no-call-graph */ | ||
753 | if (unset) { | ||
754 | opts->call_graph = CALLCHAIN_NONE; | ||
755 | pr_debug("callchain: disabled\n"); | ||
756 | return 0; | ||
757 | } | ||
758 | |||
759 | ret = record_parse_callchain(arg, opts); | ||
782 | if (!ret) | 760 | if (!ret) |
783 | pr_debug("callchain: type %d\n", opts->call_graph); | 761 | callchain_debug(opts); |
784 | 762 | ||
785 | return ret; | 763 | return ret; |
786 | } | 764 | } |
787 | 765 | ||
766 | int record_callchain_opt(const struct option *opt, | ||
767 | const char *arg __maybe_unused, | ||
768 | int unset __maybe_unused) | ||
769 | { | ||
770 | struct perf_record_opts *opts = opt->value; | ||
771 | |||
772 | if (opts->call_graph == CALLCHAIN_NONE) | ||
773 | opts->call_graph = CALLCHAIN_FP; | ||
774 | |||
775 | callchain_debug(opts); | ||
776 | return 0; | ||
777 | } | ||
778 | |||
788 | static const char * const record_usage[] = { | 779 | static const char * const record_usage[] = { |
789 | "perf record [<options>] [<command>]", | 780 | "perf record [<options>] [<command>]", |
790 | "perf record [<options>] -- <command> [<options>]", | 781 | "perf record [<options>] -- <command> [<options>]", |
@@ -813,12 +804,12 @@ static struct perf_record record = { | |||
813 | }, | 804 | }, |
814 | }; | 805 | }; |
815 | 806 | ||
816 | #define CALLCHAIN_HELP "do call-graph (stack chain/backtrace) recording: " | 807 | #define CALLCHAIN_HELP "setup and enables call-graph (stack chain/backtrace) recording: " |
817 | 808 | ||
818 | #ifdef LIBUNWIND_SUPPORT | 809 | #ifdef HAVE_LIBUNWIND_SUPPORT |
819 | const char record_callchain_help[] = CALLCHAIN_HELP "[fp] dwarf"; | 810 | const char record_callchain_help[] = CALLCHAIN_HELP "fp dwarf"; |
820 | #else | 811 | #else |
821 | const char record_callchain_help[] = CALLCHAIN_HELP "[fp]"; | 812 | const char record_callchain_help[] = CALLCHAIN_HELP "fp"; |
822 | #endif | 813 | #endif |
823 | 814 | ||
824 | /* | 815 | /* |
@@ -849,18 +840,22 @@ const struct option record_options[] = { | |||
849 | OPT_STRING('C', "cpu", &record.opts.target.cpu_list, "cpu", | 840 | OPT_STRING('C', "cpu", &record.opts.target.cpu_list, "cpu", |
850 | "list of cpus to monitor"), | 841 | "list of cpus to monitor"), |
851 | 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"), |
852 | OPT_STRING('o', "output", &record.output_name, "file", | 843 | OPT_STRING('o', "output", &record.file.path, "file", |
853 | "output file name"), | 844 | "output file name"), |
854 | OPT_BOOLEAN('i', "no-inherit", &record.opts.no_inherit, | 845 | OPT_BOOLEAN('i', "no-inherit", &record.opts.no_inherit, |
855 | "child tasks do not inherit counters"), | 846 | "child tasks do not inherit counters"), |
856 | 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"), |
857 | OPT_UINTEGER('m', "mmap-pages", &record.opts.mmap_pages, | 848 | OPT_CALLBACK('m', "mmap-pages", &record.opts.mmap_pages, "pages", |
858 | "number of mmap data pages"), | 849 | "number of mmap data pages", |
850 | perf_evlist__parse_mmap_pages), | ||
859 | OPT_BOOLEAN(0, "group", &record.opts.group, | 851 | OPT_BOOLEAN(0, "group", &record.opts.group, |
860 | "put the counters into a counter group"), | 852 | "put the counters into a counter group"), |
861 | OPT_CALLBACK_DEFAULT('g', "call-graph", &record.opts, | 853 | OPT_CALLBACK_NOOPT('g', NULL, &record.opts, |
862 | "mode[,dump_size]", record_callchain_help, | 854 | NULL, "enables call-graph recording" , |
863 | &record_parse_callchain_opt, "fp"), | 855 | &record_callchain_opt), |
856 | OPT_CALLBACK(0, "call-graph", &record.opts, | ||
857 | "mode[,dump_size]", record_callchain_help, | ||
858 | &record_parse_callchain_opt), | ||
864 | OPT_INCR('v', "verbose", &verbose, | 859 | OPT_INCR('v', "verbose", &verbose, |
865 | "be more verbose (show counter open errors, etc)"), | 860 | "be more verbose (show counter open errors, etc)"), |
866 | OPT_BOOLEAN('q', "quiet", &quiet, "don't print any message"), | 861 | OPT_BOOLEAN('q', "quiet", &quiet, "don't print any message"), |
@@ -891,6 +886,10 @@ const struct option record_options[] = { | |||
891 | parse_branch_stack), | 886 | parse_branch_stack), |
892 | OPT_BOOLEAN('W', "weight", &record.opts.sample_weight, | 887 | OPT_BOOLEAN('W', "weight", &record.opts.sample_weight, |
893 | "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"), | ||
894 | OPT_END() | 893 | OPT_END() |
895 | }; | 894 | }; |
896 | 895 | ||
@@ -909,7 +908,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) | |||
909 | 908 | ||
910 | argc = parse_options(argc, argv, record_options, record_usage, | 909 | argc = parse_options(argc, argv, record_options, record_usage, |
911 | PARSE_OPT_STOP_AT_NON_OPTION); | 910 | PARSE_OPT_STOP_AT_NON_OPTION); |
912 | if (!argc && perf_target__none(&rec->opts.target)) | 911 | if (!argc && target__none(&rec->opts.target)) |
913 | usage_with_options(record_usage, record_options); | 912 | usage_with_options(record_usage, record_options); |
914 | 913 | ||
915 | if (nr_cgroups && !rec->opts.target.system_wide) { | 914 | if (nr_cgroups && !rec->opts.target.system_wide) { |
@@ -939,17 +938,17 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) | |||
939 | goto out_symbol_exit; | 938 | goto out_symbol_exit; |
940 | } | 939 | } |
941 | 940 | ||
942 | err = perf_target__validate(&rec->opts.target); | 941 | err = target__validate(&rec->opts.target); |
943 | if (err) { | 942 | if (err) { |
944 | perf_target__strerror(&rec->opts.target, err, errbuf, BUFSIZ); | 943 | target__strerror(&rec->opts.target, err, errbuf, BUFSIZ); |
945 | ui__warning("%s", errbuf); | 944 | ui__warning("%s", errbuf); |
946 | } | 945 | } |
947 | 946 | ||
948 | err = perf_target__parse_uid(&rec->opts.target); | 947 | err = target__parse_uid(&rec->opts.target); |
949 | if (err) { | 948 | if (err) { |
950 | int saved_errno = errno; | 949 | int saved_errno = errno; |
951 | 950 | ||
952 | perf_target__strerror(&rec->opts.target, err, errbuf, BUFSIZ); | 951 | target__strerror(&rec->opts.target, err, errbuf, BUFSIZ); |
953 | ui__error("%s", errbuf); | 952 | ui__error("%s", errbuf); |
954 | 953 | ||
955 | err = -saved_errno; | 954 | err = -saved_errno; |
@@ -960,20 +959,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) | |||
960 | if (perf_evlist__create_maps(evsel_list, &rec->opts.target) < 0) | 959 | if (perf_evlist__create_maps(evsel_list, &rec->opts.target) < 0) |
961 | usage_with_options(record_usage, record_options); | 960 | usage_with_options(record_usage, record_options); |
962 | 961 | ||
963 | if (rec->opts.user_interval != ULLONG_MAX) | 962 | if (perf_record_opts__config(&rec->opts)) { |
964 | rec->opts.default_interval = rec->opts.user_interval; | ||
965 | if (rec->opts.user_freq != UINT_MAX) | ||
966 | rec->opts.freq = rec->opts.user_freq; | ||
967 | |||
968 | /* | ||
969 | * User specified count overrides default frequency. | ||
970 | */ | ||
971 | if (rec->opts.default_interval) | ||
972 | rec->opts.freq = 0; | ||
973 | else if (rec->opts.freq) { | ||
974 | rec->opts.default_interval = rec->opts.freq; | ||
975 | } else { | ||
976 | ui__error("frequency and count are zero, aborting\n"); | ||
977 | err = -EINVAL; | 963 | err = -EINVAL; |
978 | goto out_free_fd; | 964 | goto out_free_fd; |
979 | } | 965 | } |