Merge tag 'virtio-next-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[firefly-linux-kernel-4.4.55.git] / tools / perf / builtin-record.c
index a41ac41546c962df3e0801b230f06b4aa9730c7a..4d644fe2d5b78315f6d00f0d66560ae5836e7b02 100644 (file)
 #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
+#ifndef HAVE_ON_EXIT_SUPPORT
 #ifndef ATEXIT_MAX
 #define ATEXIT_MAX 32
 #endif
@@ -65,31 +66,25 @@ struct perf_record {
        struct perf_tool        tool;
        struct perf_record_opts opts;
        u64                     bytes_written;
-       const char              *output_name;
+       struct perf_data_file   file;
        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 void advance_output(struct perf_record *rec, size_t size)
+static int do_write_output(struct perf_record *rec, void *buf, size_t size)
 {
-       rec->bytes_written += size;
-}
+       struct perf_data_file *file = &rec->file;
 
-static int write_output(struct perf_record *rec, void *buf, size_t size)
-{
        while (size) {
-               int ret = write(rec->output, buf, size);
+               ssize_t ret = write(file->fd, buf, size);
 
                if (ret < 0) {
-                       pr_err("failed to write\n");
+                       pr_err("failed to write perf data, error: %m\n");
                        return -1;
                }
 
@@ -102,6 +97,11 @@ static int 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 + rec->page_size;
+       unsigned char *data = md->base + page_size;
        unsigned long size;
        void *buf;
        int rc = 0;
@@ -234,10 +234,6 @@ 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;
@@ -253,31 +249,34 @@ out:
 
 static int process_buildids(struct perf_record *rec)
 {
-       u64 size = lseek(rec->output, 0, SEEK_CUR);
+       struct perf_data_file *file  = &rec->file;
+       struct perf_session *session = rec->session;
+       u64 start = session->header.data_offset;
 
+       u64 size = lseek(file->fd, 0, SEEK_CUR);
        if (size == 0)
                return 0;
 
-       rec->session->fd = rec->output;
-       return __perf_session__process_events(rec->session, rec->post_processing_offset,
-                                             size - rec->post_processing_offset,
+       return __perf_session__process_events(session, start,
+                                             size - start,
                                              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 (!rec->opts.pipe_output) {
+       if (!file->is_pipe) {
                rec->session->header.data_size += rec->bytes_written;
 
                if (!rec->no_buildid)
                        process_buildids(rec);
                perf_session__write_header(rec->session, rec->evlist,
-                                          rec->output, true);
+                                          file->fd, true);
                perf_session__delete(rec->session);
                perf_evlist__delete(rec->evlist);
                symbol__exit();
@@ -343,64 +342,47 @@ 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)
 {
-       struct stat st;
-       int flags;
-       int err, output, feat;
+       int err;
        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;
-       const char *output_name = rec->output_name;
+       struct perf_data_file *file = &rec->file;
        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);
 
-       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);
+       session = perf_session__new(file, false, NULL);
        if (session == NULL) {
                pr_err("Not enough memory for reading perf file header\n");
                return -1;
@@ -408,21 +390,11 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
 
        rec->session = session;
 
-       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);
+       perf_record__init_features(rec);
 
        if (forks) {
                err = perf_evlist__prepare_workload(evsel_list, &opts->target,
-                                                   argv, opts->pipe_output,
+                                                   argv, file->is_pipe,
                                                    true);
                if (err < 0) {
                        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)
         */
        on_exit(perf_record__exit, rec);
 
-       if (opts->pipe_output) {
-               err = perf_header__write_pipe(output);
+       if (file->is_pipe) {
+               err = perf_header__write_pipe(file->fd);
                if (err < 0)
                        goto out_delete_session;
        } else {
                err = perf_session__write_header(session, evsel_list,
-                                                output, false);
+                                                file->fd, false);
                if (err < 0)
                        goto out_delete_session;
        }
@@ -462,11 +434,9 @@ 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 (opts->pipe_output) {
+       if (file->is_pipe) {
                err = perf_event__synthesize_attrs(tool, session,
                                                   process_synthesized_event);
                if (err < 0) {
@@ -483,13 +453,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, output, evsel_list,
+                       err = perf_event__synthesize_tracing_data(tool, file->fd, evsel_list,
                                                                  process_synthesized_event);
                        if (err <= 0) {
                                pr_err("Couldn't record tracing data.\n");
                                goto out_delete_session;
                        }
-                       advance_output(rec, err);
+                       rec->bytes_written += err;
                }
        }
 
@@ -515,16 +485,8 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
                                         perf_event__synthesize_guest_os, tool);
        }
 
-       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;
-
+       err = __machine__synthesize_threads(machine, tool, &opts->target, evsel_list->threads,
+                                           process_synthesized_event, opts->sample_address);
        if (err != 0)
                goto out_delete_session;
 
@@ -544,7 +506,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 (!perf_target__none(&opts->target))
+       if (!target__none(&opts->target))
                perf_evlist__enable(evsel_list);
 
        /*
@@ -573,7 +535,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 && !perf_target__none(&opts->target)) {
+               if (done && !disabled && !target__none(&opts->target)) {
                        perf_evlist__disable(evsel_list);
                        disabled = true;
                }
@@ -590,7 +552,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,
-               output_name,
+               file->path,
                rec->bytes_written / 24);
 
        return 0;
@@ -618,6 +580,9 @@ 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
 };
 
@@ -684,7 +649,7 @@ error:
        return ret;
 }
 
-#ifdef LIBUNWIND_SUPPORT
+#ifdef HAVE_LIBUNWIND_SUPPORT
 static int get_stack_size(char *str, unsigned long *_size)
 {
        char *endptr;
@@ -710,23 +675,14 @@ static int get_stack_size(char *str, unsigned long *_size)
               max_size, str);
        return -1;
 }
-#endif /* LIBUNWIND_SUPPORT */
+#endif /* HAVE_LIBUNWIND_SUPPORT */
 
-int record_parse_callchain_opt(const struct option *opt,
-                              const char *arg, int unset)
+int record_parse_callchain(const char *arg, struct perf_record_opts *opts)
 {
-       struct perf_record_opts *opts = opt->value;
        char *tok, *name, *saveptr = NULL;
        char *buf;
        int ret = -1;
 
-       /* --no-call-graph */
-       if (unset)
-               return 0;
-
-       /* We specified default option if none is provided. */
-       BUG_ON(!arg);
-
        /* We need buffer that we know we can write to. */
        buf = malloc(strlen(arg) + 1);
        if (!buf)
@@ -748,7 +704,7 @@ int record_parse_callchain_opt(const struct option *opt,
                                       "needed for -g fp\n");
                        break;
 
-#ifdef LIBUNWIND_SUPPORT
+#ifdef HAVE_LIBUNWIND_SUPPORT
                /* Dwarf style */
                } else if (!strncmp(name, "dwarf", sizeof("dwarf"))) {
                        const unsigned long default_stack_dump_size = 8192;
@@ -764,13 +720,9 @@ int record_parse_callchain_opt(const struct option *opt,
                                ret = get_stack_size(tok, &size);
                                opts->stack_dump_size = size;
                        }
-
-                       if (!ret)
-                               pr_debug("callchain: stack dump size %d\n",
-                                        opts->stack_dump_size);
-#endif /* LIBUNWIND_SUPPORT */
+#endif /* HAVE_LIBUNWIND_SUPPORT */
                } else {
-                       pr_err("callchain: Unknown -g option "
+                       pr_err("callchain: Unknown --call-graph option "
                               "value: %s\n", arg);
                        break;
                }
@@ -778,13 +730,52 @@ int record_parse_callchain_opt(const struct option *opt,
        } while (0);
 
        free(buf);
+       return ret;
+}
 
+static void callchain_debug(struct perf_record_opts *opts)
+{
+       pr_debug("callchain: type %d\n", opts->call_graph);
+
+       if (opts->call_graph == CALLCHAIN_DWARF)
+               pr_debug("callchain: stack dump size %d\n",
+                        opts->stack_dump_size);
+}
+
+int record_parse_callchain_opt(const struct option *opt,
+                              const char *arg,
+                              int unset)
+{
+       struct perf_record_opts *opts = opt->value;
+       int ret;
+
+       /* --no-call-graph */
+       if (unset) {
+               opts->call_graph = CALLCHAIN_NONE;
+               pr_debug("callchain: disabled\n");
+               return 0;
+       }
+
+       ret = record_parse_callchain(arg, opts);
        if (!ret)
-               pr_debug("callchain: type %d\n", opts->call_graph);
+               callchain_debug(opts);
 
        return ret;
 }
 
+int record_callchain_opt(const struct option *opt,
+                        const char *arg __maybe_unused,
+                        int unset __maybe_unused)
+{
+       struct perf_record_opts *opts = opt->value;
+
+       if (opts->call_graph == CALLCHAIN_NONE)
+               opts->call_graph = CALLCHAIN_FP;
+
+       callchain_debug(opts);
+       return 0;
+}
+
 static const char * const record_usage[] = {
        "perf record [<options>] [<command>]",
        "perf record [<options>] -- <command> [<options>]",
@@ -813,12 +804,12 @@ static struct perf_record record = {
        },
 };
 
-#define CALLCHAIN_HELP "do call-graph (stack chain/backtrace) recording: "
+#define CALLCHAIN_HELP "setup and enables call-graph (stack chain/backtrace) recording: "
 
-#ifdef LIBUNWIND_SUPPORT
-const char record_callchain_help[] = CALLCHAIN_HELP "[fp] dwarf";
+#ifdef HAVE_LIBUNWIND_SUPPORT
+const char record_callchain_help[] = CALLCHAIN_HELP "fp dwarf";
 #else
-const char record_callchain_help[] = CALLCHAIN_HELP "[fp]";
+const char record_callchain_help[] = CALLCHAIN_HELP "fp";
 #endif
 
 /*
@@ -849,18 +840,22 @@ 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.output_name, "file",
+       OPT_STRING('o', "output", &record.file.path, "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_UINTEGER('m', "mmap-pages", &record.opts.mmap_pages,
-                    "number of mmap data pages"),
+       OPT_CALLBACK('m', "mmap-pages", &record.opts.mmap_pages, "pages",
+                    "number of mmap data pages",
+                    perf_evlist__parse_mmap_pages),
        OPT_BOOLEAN(0, "group", &record.opts.group,
                    "put the counters into a counter group"),
-       OPT_CALLBACK_DEFAULT('g', "call-graph", &record.opts,
-                            "mode[,dump_size]", record_callchain_help,
-                            &record_parse_callchain_opt, "fp"),
+       OPT_CALLBACK_NOOPT('g', NULL, &record.opts,
+                          NULL, "enables call-graph recording" ,
+                          &record_callchain_opt),
+       OPT_CALLBACK(0, "call-graph", &record.opts,
+                    "mode[,dump_size]", record_callchain_help,
+                    &record_parse_callchain_opt),
        OPT_INCR('v', "verbose", &verbose,
                    "be more verbose (show counter open errors, etc)"),
        OPT_BOOLEAN('q', "quiet", &quiet, "don't print any message"),
@@ -891,6 +886,8 @@ 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_END()
 };
 
@@ -909,7 +906,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 && perf_target__none(&rec->opts.target))
+       if (!argc && target__none(&rec->opts.target))
                usage_with_options(record_usage, record_options);
 
        if (nr_cgroups && !rec->opts.target.system_wide) {
@@ -939,17 +936,17 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
                goto out_symbol_exit;
        }
 
-       err = perf_target__validate(&rec->opts.target);
+       err = target__validate(&rec->opts.target);
        if (err) {
-               perf_target__strerror(&rec->opts.target, err, errbuf, BUFSIZ);
+               target__strerror(&rec->opts.target, err, errbuf, BUFSIZ);
                ui__warning("%s", errbuf);
        }
 
-       err = perf_target__parse_uid(&rec->opts.target);
+       err = target__parse_uid(&rec->opts.target);
        if (err) {
                int saved_errno = errno;
 
-               perf_target__strerror(&rec->opts.target, err, errbuf, BUFSIZ);
+               target__strerror(&rec->opts.target, err, errbuf, BUFSIZ);
                ui__error("%s", errbuf);
 
                err = -saved_errno;
@@ -960,20 +957,7 @@ 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 (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");
+       if (perf_record_opts__config(&rec->opts)) {
                err = -EINVAL;
                goto out_free_fd;
        }