diff options
-rw-r--r-- | tools/perf/builtin-record.c | 81 | ||||
-rw-r--r-- | tools/perf/perf.h | 1 | ||||
-rw-r--r-- | tools/perf/util/evlist.c | 96 | ||||
-rw-r--r-- | tools/perf/util/evlist.h | 10 |
4 files changed, 120 insertions, 68 deletions
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index c3ac5415c097..4799195ed246 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
@@ -47,11 +47,9 @@ static struct perf_record_opts record_opts = { | |||
47 | static unsigned int page_size; | 47 | static unsigned int page_size; |
48 | static unsigned int mmap_pages = UINT_MAX; | 48 | static unsigned int mmap_pages = UINT_MAX; |
49 | static int output; | 49 | static int output; |
50 | static int pipe_output = 0; | ||
51 | static const char *output_name = NULL; | 50 | static const char *output_name = NULL; |
52 | static bool group = false; | 51 | static bool group = false; |
53 | static int realtime_prio = 0; | 52 | static int realtime_prio = 0; |
54 | static pid_t child_pid = -1; | ||
55 | static enum write_mode_t write_mode = WRITE_FORCE; | 53 | static enum write_mode_t write_mode = WRITE_FORCE; |
56 | static bool no_buildid = false; | 54 | static bool no_buildid = false; |
57 | static bool no_buildid_cache = false; | 55 | static bool no_buildid_cache = false; |
@@ -144,9 +142,9 @@ static void sig_atexit(void) | |||
144 | { | 142 | { |
145 | int status; | 143 | int status; |
146 | 144 | ||
147 | if (child_pid > 0) { | 145 | if (evsel_list->workload.pid > 0) { |
148 | if (!child_finished) | 146 | if (!child_finished) |
149 | kill(child_pid, SIGTERM); | 147 | kill(evsel_list->workload.pid, SIGTERM); |
150 | 148 | ||
151 | wait(&status); | 149 | wait(&status); |
152 | if (WIFSIGNALED(status)) | 150 | if (WIFSIGNALED(status)) |
@@ -304,7 +302,7 @@ static int process_buildids(void) | |||
304 | 302 | ||
305 | static void atexit_header(void) | 303 | static void atexit_header(void) |
306 | { | 304 | { |
307 | if (!pipe_output) { | 305 | if (!record_opts.pipe_output) { |
308 | session->header.data_size += bytes_written; | 306 | session->header.data_size += bytes_written; |
309 | 307 | ||
310 | if (!no_buildid) | 308 | if (!no_buildid) |
@@ -377,9 +375,7 @@ static int __cmd_record(int argc, const char **argv) | |||
377 | int flags; | 375 | int flags; |
378 | int err; | 376 | int err; |
379 | unsigned long waking = 0; | 377 | unsigned long waking = 0; |
380 | int child_ready_pipe[2], go_pipe[2]; | ||
381 | const bool forks = argc > 0; | 378 | const bool forks = argc > 0; |
382 | char buf; | ||
383 | struct machine *machine; | 379 | struct machine *machine; |
384 | 380 | ||
385 | progname = argv[0]; | 381 | progname = argv[0]; |
@@ -391,20 +387,15 @@ static int __cmd_record(int argc, const char **argv) | |||
391 | signal(SIGINT, sig_handler); | 387 | signal(SIGINT, sig_handler); |
392 | signal(SIGUSR1, sig_handler); | 388 | signal(SIGUSR1, sig_handler); |
393 | 389 | ||
394 | if (forks && (pipe(child_ready_pipe) < 0 || pipe(go_pipe) < 0)) { | ||
395 | perror("failed to create pipes"); | ||
396 | exit(-1); | ||
397 | } | ||
398 | |||
399 | if (!output_name) { | 390 | if (!output_name) { |
400 | if (!fstat(STDOUT_FILENO, &st) && S_ISFIFO(st.st_mode)) | 391 | if (!fstat(STDOUT_FILENO, &st) && S_ISFIFO(st.st_mode)) |
401 | pipe_output = true; | 392 | record_opts.pipe_output = true; |
402 | else | 393 | else |
403 | output_name = "perf.data"; | 394 | output_name = "perf.data"; |
404 | } | 395 | } |
405 | if (output_name) { | 396 | if (output_name) { |
406 | if (!strcmp(output_name, "-")) | 397 | if (!strcmp(output_name, "-")) |
407 | pipe_output = true; | 398 | record_opts.pipe_output = true; |
408 | else if (!stat(output_name, &st) && st.st_size) { | 399 | else if (!stat(output_name, &st) && st.st_size) { |
409 | if (write_mode == WRITE_FORCE) { | 400 | if (write_mode == WRITE_FORCE) { |
410 | char oldname[PATH_MAX]; | 401 | char oldname[PATH_MAX]; |
@@ -424,7 +415,7 @@ static int __cmd_record(int argc, const char **argv) | |||
424 | else | 415 | else |
425 | flags |= O_TRUNC; | 416 | flags |= O_TRUNC; |
426 | 417 | ||
427 | if (pipe_output) | 418 | if (record_opts.pipe_output) |
428 | output = STDOUT_FILENO; | 419 | output = STDOUT_FILENO; |
429 | else | 420 | else |
430 | output = open(output_name, flags, S_IRUSR | S_IWUSR); | 421 | output = open(output_name, flags, S_IRUSR | S_IWUSR); |
@@ -470,57 +461,11 @@ static int __cmd_record(int argc, const char **argv) | |||
470 | mmap_pages = (512 * 1024) / page_size; | 461 | mmap_pages = (512 * 1024) / page_size; |
471 | 462 | ||
472 | if (forks) { | 463 | if (forks) { |
473 | child_pid = fork(); | 464 | err = perf_evlist__prepare_workload(evsel_list, &record_opts, argv); |
474 | if (child_pid < 0) { | 465 | if (err < 0) { |
475 | perror("failed to fork"); | 466 | pr_err("Couldn't run the workload!\n"); |
476 | exit(-1); | 467 | goto out_delete_session; |
477 | } | ||
478 | |||
479 | if (!child_pid) { | ||
480 | if (pipe_output) | ||
481 | dup2(2, 1); | ||
482 | close(child_ready_pipe[0]); | ||
483 | close(go_pipe[1]); | ||
484 | fcntl(go_pipe[0], F_SETFD, FD_CLOEXEC); | ||
485 | |||
486 | /* | ||
487 | * Do a dummy execvp to get the PLT entry resolved, | ||
488 | * so we avoid the resolver overhead on the real | ||
489 | * execvp call. | ||
490 | */ | ||
491 | execvp("", (char **)argv); | ||
492 | |||
493 | /* | ||
494 | * Tell the parent we're ready to go | ||
495 | */ | ||
496 | close(child_ready_pipe[1]); | ||
497 | |||
498 | /* | ||
499 | * Wait until the parent tells us to go. | ||
500 | */ | ||
501 | if (read(go_pipe[0], &buf, 1) == -1) | ||
502 | perror("unable to read pipe"); | ||
503 | |||
504 | execvp(argv[0], (char **)argv); | ||
505 | |||
506 | perror(argv[0]); | ||
507 | kill(getppid(), SIGUSR1); | ||
508 | exit(-1); | ||
509 | } | ||
510 | |||
511 | if (!record_opts.system_wide && record_opts.target_tid == -1 && record_opts.target_pid == -1) | ||
512 | evsel_list->threads->map[0] = child_pid; | ||
513 | |||
514 | close(child_ready_pipe[1]); | ||
515 | close(go_pipe[0]); | ||
516 | /* | ||
517 | * wait for child to settle | ||
518 | */ | ||
519 | if (read(child_ready_pipe[0], &buf, 1) == -1) { | ||
520 | perror("unable to read pipe"); | ||
521 | exit(-1); | ||
522 | } | 468 | } |
523 | close(child_ready_pipe[0]); | ||
524 | } | 469 | } |
525 | 470 | ||
526 | open_counters(evsel_list); | 471 | open_counters(evsel_list); |
@@ -530,7 +475,7 @@ static int __cmd_record(int argc, const char **argv) | |||
530 | */ | 475 | */ |
531 | atexit(atexit_header); | 476 | atexit(atexit_header); |
532 | 477 | ||
533 | if (pipe_output) { | 478 | if (record_opts.pipe_output) { |
534 | err = perf_header__write_pipe(output); | 479 | err = perf_header__write_pipe(output); |
535 | if (err < 0) | 480 | if (err < 0) |
536 | return err; | 481 | return err; |
@@ -543,7 +488,7 @@ static int __cmd_record(int argc, const char **argv) | |||
543 | 488 | ||
544 | post_processing_offset = lseek(output, 0, SEEK_CUR); | 489 | post_processing_offset = lseek(output, 0, SEEK_CUR); |
545 | 490 | ||
546 | if (pipe_output) { | 491 | if (record_opts.pipe_output) { |
547 | err = perf_session__synthesize_attrs(session, | 492 | err = perf_session__synthesize_attrs(session, |
548 | process_synthesized_event); | 493 | process_synthesized_event); |
549 | if (err < 0) { | 494 | if (err < 0) { |
@@ -629,7 +574,7 @@ static int __cmd_record(int argc, const char **argv) | |||
629 | * Let the child rip | 574 | * Let the child rip |
630 | */ | 575 | */ |
631 | if (forks) | 576 | if (forks) |
632 | close(go_pipe[1]); | 577 | perf_evlist__start_workload(evsel_list); |
633 | 578 | ||
634 | for (;;) { | 579 | for (;;) { |
635 | int hits = samples; | 580 | int hits = samples; |
diff --git a/tools/perf/perf.h b/tools/perf/perf.h index eb6a13881887..32ee6ca8eabd 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h | |||
@@ -193,6 +193,7 @@ struct perf_record_opts { | |||
193 | bool no_delay; | 193 | bool no_delay; |
194 | bool no_inherit; | 194 | bool no_inherit; |
195 | bool no_samples; | 195 | bool no_samples; |
196 | bool pipe_output; | ||
196 | bool raw_samples; | 197 | bool raw_samples; |
197 | bool sample_address; | 198 | bool sample_address; |
198 | bool sample_time; | 199 | bool sample_time; |
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index b774341e797f..a472247af191 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c | |||
@@ -13,6 +13,7 @@ | |||
13 | #include "thread_map.h" | 13 | #include "thread_map.h" |
14 | #include "evlist.h" | 14 | #include "evlist.h" |
15 | #include "evsel.h" | 15 | #include "evsel.h" |
16 | #include <unistd.h> | ||
16 | 17 | ||
17 | #include "parse-events.h" | 18 | #include "parse-events.h" |
18 | 19 | ||
@@ -33,6 +34,7 @@ void perf_evlist__init(struct perf_evlist *evlist, struct cpu_map *cpus, | |||
33 | INIT_HLIST_HEAD(&evlist->heads[i]); | 34 | INIT_HLIST_HEAD(&evlist->heads[i]); |
34 | INIT_LIST_HEAD(&evlist->entries); | 35 | INIT_LIST_HEAD(&evlist->entries); |
35 | perf_evlist__set_maps(evlist, cpus, threads); | 36 | perf_evlist__set_maps(evlist, cpus, threads); |
37 | evlist->workload.pid = -1; | ||
36 | } | 38 | } |
37 | 39 | ||
38 | struct perf_evlist *perf_evlist__new(struct cpu_map *cpus, | 40 | struct perf_evlist *perf_evlist__new(struct cpu_map *cpus, |
@@ -674,3 +676,97 @@ out_err: | |||
674 | 676 | ||
675 | return err; | 677 | return err; |
676 | } | 678 | } |
679 | |||
680 | int perf_evlist__prepare_workload(struct perf_evlist *evlist, | ||
681 | struct perf_record_opts *opts, | ||
682 | const char *argv[]) | ||
683 | { | ||
684 | int child_ready_pipe[2], go_pipe[2]; | ||
685 | char bf; | ||
686 | |||
687 | if (pipe(child_ready_pipe) < 0) { | ||
688 | perror("failed to create 'ready' pipe"); | ||
689 | return -1; | ||
690 | } | ||
691 | |||
692 | if (pipe(go_pipe) < 0) { | ||
693 | perror("failed to create 'go' pipe"); | ||
694 | goto out_close_ready_pipe; | ||
695 | } | ||
696 | |||
697 | evlist->workload.pid = fork(); | ||
698 | if (evlist->workload.pid < 0) { | ||
699 | perror("failed to fork"); | ||
700 | goto out_close_pipes; | ||
701 | } | ||
702 | |||
703 | if (!evlist->workload.pid) { | ||
704 | if (opts->pipe_output) | ||
705 | dup2(2, 1); | ||
706 | |||
707 | close(child_ready_pipe[0]); | ||
708 | close(go_pipe[1]); | ||
709 | fcntl(go_pipe[0], F_SETFD, FD_CLOEXEC); | ||
710 | |||
711 | /* | ||
712 | * Do a dummy execvp to get the PLT entry resolved, | ||
713 | * so we avoid the resolver overhead on the real | ||
714 | * execvp call. | ||
715 | */ | ||
716 | execvp("", (char **)argv); | ||
717 | |||
718 | /* | ||
719 | * Tell the parent we're ready to go | ||
720 | */ | ||
721 | close(child_ready_pipe[1]); | ||
722 | |||
723 | /* | ||
724 | * Wait until the parent tells us to go. | ||
725 | */ | ||
726 | if (read(go_pipe[0], &bf, 1) == -1) | ||
727 | perror("unable to read pipe"); | ||
728 | |||
729 | execvp(argv[0], (char **)argv); | ||
730 | |||
731 | perror(argv[0]); | ||
732 | kill(getppid(), SIGUSR1); | ||
733 | exit(-1); | ||
734 | } | ||
735 | |||
736 | if (!opts->system_wide && opts->target_tid == -1 && opts->target_pid == -1) | ||
737 | evlist->threads->map[0] = evlist->workload.pid; | ||
738 | |||
739 | close(child_ready_pipe[1]); | ||
740 | close(go_pipe[0]); | ||
741 | /* | ||
742 | * wait for child to settle | ||
743 | */ | ||
744 | if (read(child_ready_pipe[0], &bf, 1) == -1) { | ||
745 | perror("unable to read pipe"); | ||
746 | goto out_close_pipes; | ||
747 | } | ||
748 | |||
749 | evlist->workload.cork_fd = go_pipe[1]; | ||
750 | close(child_ready_pipe[0]); | ||
751 | return 0; | ||
752 | |||
753 | out_close_pipes: | ||
754 | close(go_pipe[0]); | ||
755 | close(go_pipe[1]); | ||
756 | out_close_ready_pipe: | ||
757 | close(child_ready_pipe[0]); | ||
758 | close(child_ready_pipe[1]); | ||
759 | return -1; | ||
760 | } | ||
761 | |||
762 | int perf_evlist__start_workload(struct perf_evlist *evlist) | ||
763 | { | ||
764 | if (evlist->workload.cork_fd > 0) { | ||
765 | /* | ||
766 | * Remove the cork, let it rip! | ||
767 | */ | ||
768 | return close(evlist->workload.cork_fd); | ||
769 | } | ||
770 | |||
771 | return 0; | ||
772 | } | ||
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 231c06f8286b..07d56b3e6d61 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h | |||
@@ -6,6 +6,7 @@ | |||
6 | #include "../perf.h" | 6 | #include "../perf.h" |
7 | #include "event.h" | 7 | #include "event.h" |
8 | #include "util.h" | 8 | #include "util.h" |
9 | #include <unistd.h> | ||
9 | 10 | ||
10 | struct pollfd; | 11 | struct pollfd; |
11 | struct thread_map; | 12 | struct thread_map; |
@@ -22,6 +23,10 @@ struct perf_evlist { | |||
22 | int nr_fds; | 23 | int nr_fds; |
23 | int nr_mmaps; | 24 | int nr_mmaps; |
24 | int mmap_len; | 25 | int mmap_len; |
26 | struct { | ||
27 | int cork_fd; | ||
28 | pid_t pid; | ||
29 | } workload; | ||
25 | bool overwrite; | 30 | bool overwrite; |
26 | union perf_event event_copy; | 31 | union perf_event event_copy; |
27 | struct perf_mmap *mmap; | 32 | struct perf_mmap *mmap; |
@@ -68,6 +73,11 @@ int perf_evlist__open(struct perf_evlist *evlist, bool group); | |||
68 | void perf_evlist__config_attrs(struct perf_evlist *evlist, | 73 | void perf_evlist__config_attrs(struct perf_evlist *evlist, |
69 | struct perf_record_opts *opts); | 74 | struct perf_record_opts *opts); |
70 | 75 | ||
76 | int perf_evlist__prepare_workload(struct perf_evlist *evlist, | ||
77 | struct perf_record_opts *opts, | ||
78 | const char *argv[]); | ||
79 | int perf_evlist__start_workload(struct perf_evlist *evlist); | ||
80 | |||
71 | int perf_evlist__alloc_mmap(struct perf_evlist *evlist); | 81 | int perf_evlist__alloc_mmap(struct perf_evlist *evlist); |
72 | int perf_evlist__mmap(struct perf_evlist *evlist, int pages, bool overwrite); | 82 | int perf_evlist__mmap(struct perf_evlist *evlist, int pages, bool overwrite); |
73 | void perf_evlist__munmap(struct perf_evlist *evlist); | 83 | void perf_evlist__munmap(struct perf_evlist *evlist); |