diff options
Diffstat (limited to 'tools/perf/builtin-record.c')
-rw-r--r-- | tools/perf/builtin-record.c | 201 |
1 files changed, 122 insertions, 79 deletions
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 7c8020a..d046514 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -24,13 +24,12 @@ #include "util/symbol.h" #include "util/cpumap.h" #include "util/thread_map.h" -#include "util/data.h" #include <unistd.h> #include <sched.h> #include <sys/mman.h> -#ifndef HAVE_ON_EXIT_SUPPORT +#ifndef HAVE_ON_EXIT #ifndef ATEXIT_MAX #define ATEXIT_MAX 32 #endif @@ -66,25 +65,31 @@ struct perf_record { struct perf_tool tool; struct perf_record_opts opts; u64 bytes_written; - struct perf_data_file file; + const char *output_name; struct perf_evlist *evlist; struct perf_session *session; const char *progname; + int output; + unsigned int page_size; int realtime_prio; bool no_buildid; bool no_buildid_cache; long samples; + off_t post_processing_offset; }; -static int do_write_output(struct perf_record *rec, void *buf, size_t size) +static void advance_output(struct perf_record *rec, size_t size) { - struct perf_data_file *file = &rec->file; + rec->bytes_written += size; +} +static int write_output(struct perf_record *rec, void *buf, size_t size) +{ while (size) { - ssize_t ret = write(file->fd, buf, size); + int ret = write(rec->output, buf, size); if (ret < 0) { - pr_err("failed to write perf data, error: %m\n"); + pr_err("failed to write\n"); return -1; } @@ -97,11 +102,6 @@ static int do_write_output(struct perf_record *rec, void *buf, size_t size) return 0; } -static int write_output(struct perf_record *rec, void *buf, size_t size) -{ - return do_write_output(rec, buf, size); -} - static int process_synthesized_event(struct perf_tool *tool, union perf_event *event, struct perf_sample *sample __maybe_unused, @@ -119,7 +119,7 @@ static int perf_record__mmap_read(struct perf_record *rec, { unsigned int head = perf_mmap__read_head(md); unsigned int old = md->prev; - unsigned char *data = md->base + page_size; + unsigned char *data = md->base + rec->page_size; unsigned long size; void *buf; int rc = 0; @@ -234,6 +234,10 @@ try_again: "or try again with a smaller value of -m/--mmap_pages.\n" "(current value: %d)\n", opts->mmap_pages); rc = -errno; + } else if (!is_power_of_2(opts->mmap_pages) && + (opts->mmap_pages != UINT_MAX)) { + pr_err("--mmap_pages/-m value must be a power of two."); + rc = -EINVAL; } else { pr_err("failed to mmap with %d (%s)\n", errno, strerror(errno)); rc = -errno; @@ -249,34 +253,31 @@ out: static int process_buildids(struct perf_record *rec) { - struct perf_data_file *file = &rec->file; - struct perf_session *session = rec->session; - u64 start = session->header.data_offset; + u64 size = lseek(rec->output, 0, SEEK_CUR); - u64 size = lseek(file->fd, 0, SEEK_CUR); if (size == 0) return 0; - return __perf_session__process_events(session, start, - size - start, + rec->session->fd = rec->output; + return __perf_session__process_events(rec->session, rec->post_processing_offset, + size - rec->post_processing_offset, size, &build_id__mark_dso_hit_ops); } static void perf_record__exit(int status, void *arg) { struct perf_record *rec = arg; - struct perf_data_file *file = &rec->file; if (status != 0) return; - if (!file->is_pipe) { + if (!rec->opts.pipe_output) { rec->session->header.data_size += rec->bytes_written; if (!rec->no_buildid) process_buildids(rec); perf_session__write_header(rec->session, rec->evlist, - file->fd, true); + rec->output, true); perf_session__delete(rec->session); perf_evlist__delete(rec->evlist); symbol__exit(); @@ -342,47 +343,64 @@ out: return rc; } -static void perf_record__init_features(struct perf_record *rec) -{ - struct perf_evlist *evsel_list = rec->evlist; - struct perf_session *session = rec->session; - int feat; - - for (feat = HEADER_FIRST_FEATURE; feat < HEADER_LAST_FEATURE; feat++) - perf_header__set_feat(&session->header, feat); - - if (rec->no_buildid) - perf_header__clear_feat(&session->header, HEADER_BUILD_ID); - - if (!have_tracepoints(&evsel_list->entries)) - perf_header__clear_feat(&session->header, HEADER_TRACING_DATA); - - if (!rec->opts.branch_stack) - perf_header__clear_feat(&session->header, HEADER_BRANCH_STACK); -} - static int __cmd_record(struct perf_record *rec, int argc, const char **argv) { - int err; + struct stat st; + int flags; + int err, output, feat; unsigned long waking = 0; const bool forks = argc > 0; struct machine *machine; struct perf_tool *tool = &rec->tool; struct perf_record_opts *opts = &rec->opts; struct perf_evlist *evsel_list = rec->evlist; - struct perf_data_file *file = &rec->file; + const char *output_name = rec->output_name; struct perf_session *session; bool disabled = false; rec->progname = argv[0]; + rec->page_size = sysconf(_SC_PAGE_SIZE); + on_exit(perf_record__sig_exit, rec); signal(SIGCHLD, sig_handler); signal(SIGINT, sig_handler); signal(SIGUSR1, sig_handler); signal(SIGTERM, sig_handler); - session = perf_session__new(file, false, NULL); + if (!output_name) { + if (!fstat(STDOUT_FILENO, &st) && S_ISFIFO(st.st_mode)) + opts->pipe_output = true; + else + rec->output_name = output_name = "perf.data"; + } + if (output_name) { + if (!strcmp(output_name, "-")) + opts->pipe_output = true; + else if (!stat(output_name, &st) && st.st_size) { + char oldname[PATH_MAX]; + snprintf(oldname, sizeof(oldname), "%s.old", + output_name); + unlink(oldname); + rename(output_name, oldname); + } + } + + flags = O_CREAT|O_RDWR|O_TRUNC; + + if (opts->pipe_output) + output = STDOUT_FILENO; + else + output = open(output_name, flags, S_IRUSR | S_IWUSR); + if (output < 0) { + perror("failed to create output file"); + return -1; + } + + rec->output = output; + + session = perf_session__new(output_name, O_WRONLY, + true, false, NULL); if (session == NULL) { pr_err("Not enough memory for reading perf file header\n"); return -1; @@ -390,11 +408,21 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) rec->session = session; - perf_record__init_features(rec); + for (feat = HEADER_FIRST_FEATURE; feat < HEADER_LAST_FEATURE; feat++) + perf_header__set_feat(&session->header, feat); + + if (rec->no_buildid) + perf_header__clear_feat(&session->header, HEADER_BUILD_ID); + + if (!have_tracepoints(&evsel_list->entries)) + perf_header__clear_feat(&session->header, HEADER_TRACING_DATA); + + if (!rec->opts.branch_stack) + perf_header__clear_feat(&session->header, HEADER_BRANCH_STACK); if (forks) { err = perf_evlist__prepare_workload(evsel_list, &opts->target, - argv, file->is_pipe, + argv, opts->pipe_output, true); if (err < 0) { pr_err("Couldn't run the workload!\n"); @@ -415,13 +443,13 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) */ on_exit(perf_record__exit, rec); - if (file->is_pipe) { - err = perf_header__write_pipe(file->fd); + if (opts->pipe_output) { + err = perf_header__write_pipe(output); if (err < 0) goto out_delete_session; } else { err = perf_session__write_header(session, evsel_list, - file->fd, false); + output, false); if (err < 0) goto out_delete_session; } @@ -434,9 +462,11 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) goto out_delete_session; } + rec->post_processing_offset = lseek(output, 0, SEEK_CUR); + machine = &session->machines.host; - if (file->is_pipe) { + if (opts->pipe_output) { err = perf_event__synthesize_attrs(tool, session, process_synthesized_event); if (err < 0) { @@ -453,13 +483,13 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) * return this more properly and also * propagate errors that now are calling die() */ - err = perf_event__synthesize_tracing_data(tool, file->fd, evsel_list, + err = perf_event__synthesize_tracing_data(tool, output, evsel_list, process_synthesized_event); if (err <= 0) { pr_err("Couldn't record tracing data.\n"); goto out_delete_session; } - rec->bytes_written += err; + advance_output(rec, err); } } @@ -485,8 +515,16 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) perf_event__synthesize_guest_os, tool); } - err = __machine__synthesize_threads(machine, tool, &opts->target, evsel_list->threads, - process_synthesized_event, opts->sample_address); + if (perf_target__has_task(&opts->target)) + err = perf_event__synthesize_thread_map(tool, evsel_list->threads, + process_synthesized_event, + machine); + else if (perf_target__has_cpu(&opts->target)) + err = perf_event__synthesize_threads(tool, process_synthesized_event, + machine); + else /* command specified */ + err = 0; + if (err != 0) goto out_delete_session; @@ -506,7 +544,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) * (apart from group members) have enable_on_exec=1 set, * so don't spoil it by prematurely enabling them. */ - if (!target__none(&opts->target)) + if (!perf_target__none(&opts->target)) perf_evlist__enable(evsel_list); /* @@ -535,7 +573,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) * die with the process and we wait for that. Thus no need to * disable events in this case. */ - if (done && !disabled && !target__none(&opts->target)) { + if (done && !disabled && !perf_target__none(&opts->target)) { perf_evlist__disable(evsel_list); disabled = true; } @@ -552,7 +590,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) fprintf(stderr, "[ perf record: Captured and wrote %.3f MB %s (~%" PRIu64 " samples) ]\n", (double)rec->bytes_written / 1024.0 / 1024.0, - file->path, + output_name, rec->bytes_written / 24); return 0; @@ -580,9 +618,6 @@ static const struct branch_mode branch_modes[] = { BRANCH_OPT("any_call", PERF_SAMPLE_BRANCH_ANY_CALL), BRANCH_OPT("any_ret", PERF_SAMPLE_BRANCH_ANY_RETURN), BRANCH_OPT("ind_call", PERF_SAMPLE_BRANCH_IND_CALL), - BRANCH_OPT("abort_tx", PERF_SAMPLE_BRANCH_ABORT_TX), - BRANCH_OPT("in_tx", PERF_SAMPLE_BRANCH_IN_TX), - BRANCH_OPT("no_tx", PERF_SAMPLE_BRANCH_NO_TX), BRANCH_END }; @@ -649,7 +684,7 @@ error: return ret; } -#ifdef HAVE_LIBUNWIND_SUPPORT +#ifdef LIBUNWIND_SUPPORT static int get_stack_size(char *str, unsigned long *_size) { char *endptr; @@ -675,7 +710,7 @@ static int get_stack_size(char *str, unsigned long *_size) max_size, str); return -1; } -#endif /* HAVE_LIBUNWIND_SUPPORT */ +#endif /* LIBUNWIND_SUPPORT */ int record_parse_callchain(const char *arg, struct perf_record_opts *opts) { @@ -704,7 +739,7 @@ int record_parse_callchain(const char *arg, struct perf_record_opts *opts) "needed for -g fp\n"); break; -#ifdef HAVE_LIBUNWIND_SUPPORT +#ifdef LIBUNWIND_SUPPORT /* Dwarf style */ } else if (!strncmp(name, "dwarf", sizeof("dwarf"))) { const unsigned long default_stack_dump_size = 8192; @@ -720,7 +755,7 @@ int record_parse_callchain(const char *arg, struct perf_record_opts *opts) ret = get_stack_size(tok, &size); opts->stack_dump_size = size; } -#endif /* HAVE_LIBUNWIND_SUPPORT */ +#endif /* LIBUNWIND_SUPPORT */ } else { pr_err("callchain: Unknown --call-graph option " "value: %s\n", arg); @@ -806,7 +841,7 @@ static struct perf_record record = { #define CALLCHAIN_HELP "setup and enables call-graph (stack chain/backtrace) recording: " -#ifdef HAVE_LIBUNWIND_SUPPORT +#ifdef LIBUNWIND_SUPPORT const char record_callchain_help[] = CALLCHAIN_HELP "fp dwarf"; #else const char record_callchain_help[] = CALLCHAIN_HELP "fp"; @@ -840,14 +875,13 @@ const struct option record_options[] = { OPT_STRING('C', "cpu", &record.opts.target.cpu_list, "cpu", "list of cpus to monitor"), OPT_U64('c', "count", &record.opts.user_interval, "event period to sample"), - OPT_STRING('o', "output", &record.file.path, "file", + OPT_STRING('o', "output", &record.output_name, "file", "output file name"), OPT_BOOLEAN('i', "no-inherit", &record.opts.no_inherit, "child tasks do not inherit counters"), OPT_UINTEGER('F', "freq", &record.opts.user_freq, "profile at this frequency"), - OPT_CALLBACK('m', "mmap-pages", &record.opts.mmap_pages, "pages", - "number of mmap data pages", - perf_evlist__parse_mmap_pages), + OPT_UINTEGER('m', "mmap-pages", &record.opts.mmap_pages, + "number of mmap data pages"), OPT_BOOLEAN(0, "group", &record.opts.group, "put the counters into a counter group"), OPT_CALLBACK_NOOPT('g', NULL, &record.opts, @@ -886,10 +920,6 @@ const struct option record_options[] = { parse_branch_stack), OPT_BOOLEAN('W', "weight", &record.opts.sample_weight, "sample by weight (on special events only)"), - OPT_BOOLEAN(0, "transaction", &record.opts.sample_transaction, - "sample transaction flags (special events only)"), - OPT_BOOLEAN(0, "force-per-cpu", &record.opts.target.force_per_cpu, - "force the use of per-cpu mmaps"), OPT_END() }; @@ -908,7 +938,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) argc = parse_options(argc, argv, record_options, record_usage, PARSE_OPT_STOP_AT_NON_OPTION); - if (!argc && target__none(&rec->opts.target)) + if (!argc && perf_target__none(&rec->opts.target)) usage_with_options(record_usage, record_options); if (nr_cgroups && !rec->opts.target.system_wide) { @@ -938,17 +968,17 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) goto out_symbol_exit; } - err = target__validate(&rec->opts.target); + err = perf_target__validate(&rec->opts.target); if (err) { - target__strerror(&rec->opts.target, err, errbuf, BUFSIZ); + perf_target__strerror(&rec->opts.target, err, errbuf, BUFSIZ); ui__warning("%s", errbuf); } - err = target__parse_uid(&rec->opts.target); + err = perf_target__parse_uid(&rec->opts.target); if (err) { int saved_errno = errno; - target__strerror(&rec->opts.target, err, errbuf, BUFSIZ); + perf_target__strerror(&rec->opts.target, err, errbuf, BUFSIZ); ui__error("%s", errbuf); err = -saved_errno; @@ -959,7 +989,20 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) if (perf_evlist__create_maps(evsel_list, &rec->opts.target) < 0) usage_with_options(record_usage, record_options); - if (perf_record_opts__config(&rec->opts)) { + if (rec->opts.user_interval != ULLONG_MAX) + rec->opts.default_interval = rec->opts.user_interval; + if (rec->opts.user_freq != UINT_MAX) + rec->opts.freq = rec->opts.user_freq; + + /* + * User specified count overrides default frequency. + */ + if (rec->opts.default_interval) + rec->opts.freq = 0; + else if (rec->opts.freq) { + rec->opts.default_interval = rec->opts.freq; + } else { + ui__error("frequency and count are zero, aborting\n"); err = -EINVAL; goto out_free_fd; } |