mm/migrate: correct failure handling if !hugepage_migration_support()
[firefly-linux-kernel-4.4.55.git] / tools / perf / builtin-record.c
index c1c1200d2f0a69555111aa9872c3d4be04d786dc..3c394bf16fa8cd86ce01d3d5852bab8a078f5ae4 100644 (file)
@@ -62,9 +62,9 @@ static void __handle_on_exit_funcs(void)
 }
 #endif
 
-struct perf_record {
+struct record {
        struct perf_tool        tool;
-       struct perf_record_opts opts;
+       struct record_opts      opts;
        u64                     bytes_written;
        struct perf_data_file   file;
        struct perf_evlist      *evlist;
@@ -76,24 +76,14 @@ struct perf_record {
        long                    samples;
 };
 
-static int perf_record__write(struct perf_record *rec, void *buf, size_t size)
+static int record__write(struct record *rec, void *bf, size_t size)
 {
-       struct perf_data_file *file = &rec->file;
-
-       while (size) {
-               ssize_t ret = write(file->fd, buf, size);
-
-               if (ret < 0) {
-                       pr_err("failed to write perf data, error: %m\n");
-                       return -1;
-               }
-
-               size -= ret;
-               buf += ret;
-
-               rec->bytes_written += ret;
+       if (perf_data_file__write(rec->session->file, bf, size) < 0) {
+               pr_err("failed to write perf data, error: %m\n");
+               return -1;
        }
 
+       rec->bytes_written += size;
        return 0;
 }
 
@@ -102,12 +92,11 @@ static int process_synthesized_event(struct perf_tool *tool,
                                     struct perf_sample *sample __maybe_unused,
                                     struct machine *machine __maybe_unused)
 {
-       struct perf_record *rec = container_of(tool, struct perf_record, tool);
-       return perf_record__write(rec, event, event->header.size);
+       struct record *rec = container_of(tool, struct record, tool);
+       return record__write(rec, event, event->header.size);
 }
 
-static int perf_record__mmap_read(struct perf_record *rec,
-                                  struct perf_mmap *md)
+static int record__mmap_read(struct record *rec, struct perf_mmap *md)
 {
        unsigned int head = perf_mmap__read_head(md);
        unsigned int old = md->prev;
@@ -128,7 +117,7 @@ static int perf_record__mmap_read(struct perf_record *rec,
                size = md->mask + 1 - (old & md->mask);
                old += size;
 
-               if (perf_record__write(rec, buf, size) < 0) {
+               if (record__write(rec, buf, size) < 0) {
                        rc = -1;
                        goto out;
                }
@@ -138,7 +127,7 @@ static int perf_record__mmap_read(struct perf_record *rec,
        size = head - old;
        old += size;
 
-       if (perf_record__write(rec, buf, size) < 0) {
+       if (record__write(rec, buf, size) < 0) {
                rc = -1;
                goto out;
        }
@@ -163,9 +152,9 @@ static void sig_handler(int sig)
        signr = sig;
 }
 
-static void perf_record__sig_exit(int exit_status __maybe_unused, void *arg)
+static void record__sig_exit(int exit_status __maybe_unused, void *arg)
 {
-       struct perf_record *rec = arg;
+       struct record *rec = arg;
        int status;
 
        if (rec->evlist->workload.pid > 0) {
@@ -183,18 +172,18 @@ static void perf_record__sig_exit(int exit_status __maybe_unused, void *arg)
        signal(signr, SIG_DFL);
 }
 
-static int perf_record__open(struct perf_record *rec)
+static int record__open(struct record *rec)
 {
        char msg[512];
        struct perf_evsel *pos;
        struct perf_evlist *evlist = rec->evlist;
        struct perf_session *session = rec->session;
-       struct perf_record_opts *opts = &rec->opts;
+       struct record_opts *opts = &rec->opts;
        int rc = 0;
 
        perf_evlist__config(evlist, opts);
 
-       list_for_each_entry(pos, &evlist->entries, node) {
+       evlist__for_each(evlist, pos) {
 try_again:
                if (perf_evsel__open(pos, evlist->cpus, evlist->threads) < 0) {
                        if (perf_evsel__fallback(pos, errno, msg, sizeof(msg))) {
@@ -239,7 +228,7 @@ out:
        return rc;
 }
 
-static int process_buildids(struct perf_record *rec)
+static int process_buildids(struct record *rec)
 {
        struct perf_data_file *file  = &rec->file;
        struct perf_session *session = rec->session;
@@ -254,9 +243,9 @@ static int process_buildids(struct perf_record *rec)
                                              size, &build_id__mark_dso_hit_ops);
 }
 
-static void perf_record__exit(int status, void *arg)
+static void record__exit(int status, void *arg)
 {
-       struct perf_record *rec = arg;
+       struct record *rec = arg;
        struct perf_data_file *file = &rec->file;
 
        if (status != 0)
@@ -312,14 +301,14 @@ static struct perf_event_header finished_round_event = {
        .type = PERF_RECORD_FINISHED_ROUND,
 };
 
-static int perf_record__mmap_read_all(struct perf_record *rec)
+static int record__mmap_read_all(struct record *rec)
 {
        int i;
        int rc = 0;
 
        for (i = 0; i < rec->evlist->nr_mmaps; i++) {
                if (rec->evlist->mmap[i].base) {
-                       if (perf_record__mmap_read(rec, &rec->evlist->mmap[i]) != 0) {
+                       if (record__mmap_read(rec, &rec->evlist->mmap[i]) != 0) {
                                rc = -1;
                                goto out;
                        }
@@ -327,16 +316,14 @@ static int perf_record__mmap_read_all(struct perf_record *rec)
        }
 
        if (perf_header__has_feat(&rec->session->header, HEADER_TRACING_DATA))
-               rc = perf_record__write(rec, &finished_round_event,
-                                       sizeof(finished_round_event));
+               rc = record__write(rec, &finished_round_event, sizeof(finished_round_event));
 
 out:
        return rc;
 }
 
-static void perf_record__init_features(struct perf_record *rec)
+static void record__init_features(struct record *rec)
 {
-       struct perf_evlist *evsel_list = rec->evlist;
        struct perf_session *session = rec->session;
        int feat;
 
@@ -346,32 +333,46 @@ static void perf_record__init_features(struct perf_record *rec)
        if (rec->no_buildid)
                perf_header__clear_feat(&session->header, HEADER_BUILD_ID);
 
-       if (!have_tracepoints(&evsel_list->entries))
+       if (!have_tracepoints(&rec->evlist->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)
+static volatile int workload_exec_errno;
+
+/*
+ * perf_evlist__prepare_workload will send a SIGUSR1
+ * if the fork fails, since we asked by setting its
+ * want_signal to true.
+ */
+static void workload_exec_failed_signal(int signo, siginfo_t *info,
+                                       void *ucontext __maybe_unused)
+{
+       workload_exec_errno = info->si_value.sival_int;
+       done = 1;
+       signr = signo;
+       child_finished = 1;
+}
+
+static int __cmd_record(struct record *rec, int argc, const char **argv)
 {
        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;
+       struct record_opts *opts = &rec->opts;
        struct perf_data_file *file = &rec->file;
        struct perf_session *session;
        bool disabled = false;
 
        rec->progname = argv[0];
 
-       on_exit(perf_record__sig_exit, rec);
+       on_exit(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);
@@ -382,37 +383,37 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
 
        rec->session = session;
 
-       perf_record__init_features(rec);
+       record__init_features(rec);
 
        if (forks) {
-               err = perf_evlist__prepare_workload(evsel_list, &opts->target,
+               err = perf_evlist__prepare_workload(rec->evlist, &opts->target,
                                                    argv, file->is_pipe,
-                                                   true);
+                                                   workload_exec_failed_signal);
                if (err < 0) {
                        pr_err("Couldn't run the workload!\n");
                        goto out_delete_session;
                }
        }
 
-       if (perf_record__open(rec) != 0) {
+       if (record__open(rec) != 0) {
                err = -1;
                goto out_delete_session;
        }
 
-       if (!evsel_list->nr_groups)
+       if (!rec->evlist->nr_groups)
                perf_header__clear_feat(&session->header, HEADER_GROUP_DESC);
 
        /*
-        * perf_session__delete(session) will be called at perf_record__exit()
+        * perf_session__delete(session) will be called at record__exit()
         */
-       on_exit(perf_record__exit, rec);
+       on_exit(record__exit, rec);
 
        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,
+               err = perf_session__write_header(session, rec->evlist,
                                                 file->fd, false);
                if (err < 0)
                        goto out_delete_session;
@@ -436,7 +437,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
                        goto out_delete_session;
                }
 
-               if (have_tracepoints(&evsel_list->entries)) {
+               if (have_tracepoints(&rec->evlist->entries)) {
                        /*
                         * FIXME err <= 0 here actually means that
                         * there were no tracepoints so its not really
@@ -445,7 +446,7 @@ 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, file->fd, rec->evlist,
                                                                  process_synthesized_event);
                        if (err <= 0) {
                                pr_err("Couldn't record tracing data.\n");
@@ -477,7 +478,7 @@ 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,
+       err = __machine__synthesize_threads(machine, tool, &opts->target, rec->evlist->threads,
                                            process_synthesized_event, opts->sample_address);
        if (err != 0)
                goto out_delete_session;
@@ -498,19 +499,24 @@ 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))
-               perf_evlist__enable(evsel_list);
+       if (!target__none(&opts->target) && !opts->initial_delay)
+               perf_evlist__enable(rec->evlist);
 
        /*
         * Let the child rip
         */
        if (forks)
-               perf_evlist__start_workload(evsel_list);
+               perf_evlist__start_workload(rec->evlist);
+
+       if (opts->initial_delay) {
+               usleep(opts->initial_delay * 1000);
+               perf_evlist__enable(rec->evlist);
+       }
 
        for (;;) {
                int hits = rec->samples;
 
-               if (perf_record__mmap_read_all(rec) < 0) {
+               if (record__mmap_read_all(rec) < 0) {
                        err = -1;
                        goto out_delete_session;
                }
@@ -518,7 +524,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
                if (hits == rec->samples) {
                        if (done)
                                break;
-                       err = poll(evsel_list->pollfd, evsel_list->nr_fds, -1);
+                       err = poll(rec->evlist->pollfd, rec->evlist->nr_fds, -1);
                        waking++;
                }
 
@@ -528,11 +534,19 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
                 * disable events in this case.
                 */
                if (done && !disabled && !target__none(&opts->target)) {
-                       perf_evlist__disable(evsel_list);
+                       perf_evlist__disable(rec->evlist);
                        disabled = true;
                }
        }
 
+       if (forks && workload_exec_errno) {
+               char msg[512];
+               const char *emsg = strerror_r(workload_exec_errno, msg, sizeof(msg));
+               pr_err("Workload failed: %s\n", emsg);
+               err = -1;
+               goto out_delete_session;
+       }
+
        if (quiet || signr == SIGUSR1)
                return 0;
 
@@ -669,7 +683,7 @@ static int get_stack_size(char *str, unsigned long *_size)
 }
 #endif /* HAVE_LIBUNWIND_SUPPORT */
 
-int record_parse_callchain(const char *arg, struct perf_record_opts *opts)
+int record_parse_callchain(const char *arg, struct record_opts *opts)
 {
        char *tok, *name, *saveptr = NULL;
        char *buf;
@@ -725,7 +739,7 @@ int record_parse_callchain(const char *arg, struct perf_record_opts *opts)
        return ret;
 }
 
-static void callchain_debug(struct perf_record_opts *opts)
+static void callchain_debug(struct record_opts *opts)
 {
        pr_debug("callchain: type %d\n", opts->call_graph);
 
@@ -738,7 +752,7 @@ int record_parse_callchain_opt(const struct option *opt,
                               const char *arg,
                               int unset)
 {
-       struct perf_record_opts *opts = opt->value;
+       struct record_opts *opts = opt->value;
        int ret;
 
        /* --no-call-graph */
@@ -759,7 +773,7 @@ int record_callchain_opt(const struct option *opt,
                         const char *arg __maybe_unused,
                         int unset __maybe_unused)
 {
-       struct perf_record_opts *opts = opt->value;
+       struct record_opts *opts = opt->value;
 
        if (opts->call_graph == CALLCHAIN_NONE)
                opts->call_graph = CALLCHAIN_FP;
@@ -775,8 +789,8 @@ static const char * const record_usage[] = {
 };
 
 /*
- * XXX Ideally would be local to cmd_record() and passed to a perf_record__new
- * because we need to have access to it in perf_record__exit, that is called
+ * XXX Ideally would be local to cmd_record() and passed to a record__new
+ * because we need to have access to it in record__exit, that is called
  * after cmd_record() exits, but since record_options need to be accessible to
  * builtin-script, leave it here.
  *
@@ -784,7 +798,7 @@ static const char * const record_usage[] = {
  *
  * Just say no to tons of global variables, sigh.
  */
-static struct perf_record record = {
+static struct record record = {
        .opts = {
                .mmap_pages          = UINT_MAX,
                .user_freq           = UINT_MAX,
@@ -808,7 +822,7 @@ const char record_callchain_help[] = CALLCHAIN_HELP "fp";
 /*
  * XXX Will stay a global variable till we fix builtin-script.c to stop messing
  * with it and switch to use the library functions in perf_evlist that came
- * from builtin-record.c, i.e. use perf_record_opts,
+ * from builtin-record.c, i.e. use record_opts,
  * perf_evlist__prepare_workload, etc instead of fork+exec'in 'perf record',
  * using pipes, etc.
  */
@@ -824,7 +838,7 @@ const struct option record_options[] = {
                    "record events on existing thread id"),
        OPT_INTEGER('r', "realtime", &record.realtime_prio,
                    "collect data with this RT SCHED_FIFO priority"),
-       OPT_BOOLEAN('D', "no-delay", &record.opts.no_delay,
+       OPT_BOOLEAN(0, "no-buffering", &record.opts.no_buffering,
                    "collect data without buffering"),
        OPT_BOOLEAN('R', "raw-samples", &record.opts.raw_samples,
                    "collect raw sample records from all opened counters"),
@@ -868,6 +882,8 @@ const struct option record_options[] = {
        OPT_CALLBACK('G', "cgroup", &record.evlist, "name",
                     "monitor event in cgroup name only",
                     parse_cgroups),
+       OPT_UINTEGER('D', "delay", &record.opts.initial_delay,
+                 "ms to wait before starting measurement after program start"),
        OPT_STRING('u', "uid", &record.opts.target.uid_str, "user",
                   "user to profile"),
 
@@ -890,16 +906,13 @@ const struct option record_options[] = {
 int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
 {
        int err = -ENOMEM;
-       struct perf_evlist *evsel_list;
-       struct perf_record *rec = &record;
+       struct record *rec = &record;
        char errbuf[BUFSIZ];
 
-       evsel_list = perf_evlist__new();
-       if (evsel_list == NULL)
+       rec->evlist = perf_evlist__new();
+       if (rec->evlist == NULL)
                return -ENOMEM;
 
-       rec->evlist = evsel_list;
-
        argc = parse_options(argc, argv, record_options, record_usage,
                            PARSE_OPT_STOP_AT_NON_OPTION);
        if (!argc && target__none(&rec->opts.target))
@@ -926,8 +939,8 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
        if (rec->no_buildid_cache || rec->no_buildid)
                disable_buildid_cache();
 
-       if (evsel_list->nr_entries == 0 &&
-           perf_evlist__add_default(evsel_list) < 0) {
+       if (rec->evlist->nr_entries == 0 &&
+           perf_evlist__add_default(rec->evlist) < 0) {
                pr_err("Not enough memory for event selector list\n");
                goto out_symbol_exit;
        }
@@ -953,20 +966,15 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
        }
 
        err = -ENOMEM;
-       if (perf_evlist__create_maps(evsel_list, &rec->opts.target) < 0)
+       if (perf_evlist__create_maps(rec->evlist, &rec->opts.target) < 0)
                usage_with_options(record_usage, record_options);
 
-       if (perf_record_opts__config(&rec->opts)) {
+       if (record_opts__config(&rec->opts)) {
                err = -EINVAL;
-               goto out_free_fd;
+               goto out_symbol_exit;
        }
 
        err = __cmd_record(&record, argc, argv);
-
-       perf_evlist__munmap(evsel_list);
-       perf_evlist__close(evsel_list);
-out_free_fd:
-       perf_evlist__delete_maps(evsel_list);
 out_symbol_exit:
        symbol__exit();
        return err;