perf tools: Add FIFO file names as alternative options to --control
[linux-block.git] / tools / perf / builtin-stat.c
index 9be020e0098ad0020d39c41c22bd97ffcc4a111f..14688710195cb8e77cd98086032f02b48c3188da 100644 (file)
@@ -188,6 +188,8 @@ static struct perf_stat_config stat_config = {
        .metric_only_len        = METRIC_ONLY_LEN,
        .walltime_nsecs_stats   = &walltime_nsecs_stats,
        .big_num                = true,
+       .ctl_fd                 = -1,
+       .ctl_fd_ack             = -1
 };
 
 static bool cpus_map_matched(struct evsel *a, struct evsel *b)
@@ -402,7 +404,7 @@ static void read_counters(struct timespec *rs)
 {
        struct evsel *counter;
 
-       if (!stat_config.summary && (read_affinity_counters(rs) < 0))
+       if (!stat_config.stop_read_counter && (read_affinity_counters(rs) < 0))
                return;
 
        evlist__for_each_entry(evsel_list, counter) {
@@ -475,18 +477,38 @@ static void process_interval(void)
        print_counters(&rs, 0, NULL);
 }
 
+static bool handle_interval(unsigned int interval, int *times)
+{
+       if (interval) {
+               process_interval();
+               if (interval_count && !(--(*times)))
+                       return true;
+       }
+       return false;
+}
+
 static void enable_counters(void)
 {
-       if (stat_config.initial_delay)
+       if (stat_config.initial_delay < 0) {
+               pr_info(EVLIST_DISABLED_MSG);
+               return;
+       }
+
+       if (stat_config.initial_delay > 0) {
+               pr_info(EVLIST_DISABLED_MSG);
                usleep(stat_config.initial_delay * USEC_PER_MSEC);
+       }
 
        /*
         * We need to enable counters only if:
         * - we don't have tracee (attaching to task or cpu)
         * - we have initial delay configured
         */
-       if (!target__none(&target) || stat_config.initial_delay)
+       if (!target__none(&target) || stat_config.initial_delay) {
                evlist__enable(evsel_list);
+               if (stat_config.initial_delay > 0)
+                       pr_info(EVLIST_ENABLED_MSG);
+       }
 }
 
 static void disable_counters(void)
@@ -540,6 +562,86 @@ static bool is_target_alive(struct target *_target,
        return false;
 }
 
+static void process_evlist(struct evlist *evlist, unsigned int interval)
+{
+       enum evlist_ctl_cmd cmd = EVLIST_CTL_CMD_UNSUPPORTED;
+
+       if (evlist__ctlfd_process(evlist, &cmd) > 0) {
+               switch (cmd) {
+               case EVLIST_CTL_CMD_ENABLE:
+                       pr_info(EVLIST_ENABLED_MSG);
+                       if (interval)
+                               process_interval();
+                       break;
+               case EVLIST_CTL_CMD_DISABLE:
+                       if (interval)
+                               process_interval();
+                       pr_info(EVLIST_DISABLED_MSG);
+                       break;
+               case EVLIST_CTL_CMD_ACK:
+               case EVLIST_CTL_CMD_UNSUPPORTED:
+               default:
+                       break;
+               }
+       }
+}
+
+static void compute_tts(struct timespec *time_start, struct timespec *time_stop,
+                       int *time_to_sleep)
+{
+       int tts = *time_to_sleep;
+       struct timespec time_diff;
+
+       diff_timespec(&time_diff, time_stop, time_start);
+
+       tts -= time_diff.tv_sec * MSEC_PER_SEC +
+              time_diff.tv_nsec / NSEC_PER_MSEC;
+
+       if (tts < 0)
+               tts = 0;
+
+       *time_to_sleep = tts;
+}
+
+static int dispatch_events(bool forks, int timeout, int interval, int *times)
+{
+       int child_exited = 0, status = 0;
+       int time_to_sleep, sleep_time;
+       struct timespec time_start, time_stop;
+
+       if (interval)
+               sleep_time = interval;
+       else if (timeout)
+               sleep_time = timeout;
+       else
+               sleep_time = 1000;
+
+       time_to_sleep = sleep_time;
+
+       while (!done) {
+               if (forks)
+                       child_exited = waitpid(child_pid, &status, WNOHANG);
+               else
+                       child_exited = !is_target_alive(&target, evsel_list->core.threads) ? 1 : 0;
+
+               if (child_exited)
+                       break;
+
+               clock_gettime(CLOCK_MONOTONIC, &time_start);
+               if (!(evlist__poll(evsel_list, time_to_sleep) > 0)) { /* poll timeout or EINTR */
+                       if (timeout || handle_interval(interval, times))
+                               break;
+                       time_to_sleep = sleep_time;
+               } else { /* fd revent */
+                       process_evlist(evsel_list, interval);
+                       clock_gettime(CLOCK_MONOTONIC, &time_stop);
+                       compute_tts(&time_start, &time_stop, &time_to_sleep);
+               }
+       }
+
+       return status;
+}
+
 enum counter_recovery {
        COUNTER_SKIP,
        COUNTER_RETRY,
@@ -603,7 +705,6 @@ static int __run_perf_stat(int argc, const char **argv, int run_idx)
        char msg[BUFSIZ];
        unsigned long long t0, t1;
        struct evsel *counter;
-       struct timespec ts;
        size_t l;
        int status = 0;
        const bool forks = (argc > 0);
@@ -612,17 +713,6 @@ static int __run_perf_stat(int argc, const char **argv, int run_idx)
        int i, cpu;
        bool second_pass = false;
 
-       if (interval) {
-               ts.tv_sec  = interval / USEC_PER_MSEC;
-               ts.tv_nsec = (interval % USEC_PER_MSEC) * NSEC_PER_MSEC;
-       } else if (timeout) {
-               ts.tv_sec  = timeout / USEC_PER_MSEC;
-               ts.tv_nsec = (timeout % USEC_PER_MSEC) * NSEC_PER_MSEC;
-       } else {
-               ts.tv_sec  = 1;
-               ts.tv_nsec = 0;
-       }
-
        if (forks) {
                if (perf_evlist__prepare_workload(evsel_list, &target, argv, is_pipe,
                                                  workload_exec_failed_signal) < 0) {
@@ -779,16 +869,8 @@ try_again_reset:
                perf_evlist__start_workload(evsel_list);
                enable_counters();
 
-               if (interval || timeout) {
-                       while (!waitpid(child_pid, &status, WNOHANG)) {
-                               nanosleep(&ts, NULL);
-                               if (timeout)
-                                       break;
-                               process_interval();
-                               if (interval_count && !(--times))
-                                       break;
-                       }
-               }
+               if (interval || timeout || evlist__ctlfd_initialized(evsel_list))
+                       status = dispatch_events(forks, timeout, interval, &times);
                if (child_pid != -1) {
                        if (timeout)
                                kill(child_pid, SIGTERM);
@@ -805,18 +887,7 @@ try_again_reset:
                        psignal(WTERMSIG(status), argv[0]);
        } else {
                enable_counters();
-               while (!done) {
-                       nanosleep(&ts, NULL);
-                       if (!is_target_alive(&target, evsel_list->core.threads))
-                               break;
-                       if (timeout)
-                               break;
-                       if (interval) {
-                               process_interval();
-                               if (interval_count && !(--times))
-                                       break;
-                       }
-               }
+               status = dispatch_events(forks, timeout, interval, &times);
        }
 
        disable_counters();
@@ -826,9 +897,9 @@ try_again_reset:
        if (stat_config.walltime_run_table)
                stat_config.walltime_run[run_idx] = t1 - t0;
 
-       if (interval) {
+       if (interval && stat_config.summary) {
                stat_config.interval = 0;
-               stat_config.summary = true;
+               stat_config.stop_read_counter = true;
                init_stats(&walltime_nsecs_stats);
                update_stats(&walltime_nsecs_stats, t1 - t0);
 
@@ -970,6 +1041,25 @@ static int parse_metric_groups(const struct option *opt,
                                         &stat_config.metric_events);
 }
 
+static int parse_control_option(const struct option *opt,
+                               const char *str,
+                               int unset __maybe_unused)
+{
+       struct perf_stat_config *config = opt->value;
+
+       return evlist__parse_control(str, &config->ctl_fd, &config->ctl_fd_ack, &config->ctl_fd_close);
+}
+
+static void close_control_option(struct perf_stat_config *config)
+{
+       if (config->ctl_fd_close) {
+               config->ctl_fd_close = false;
+               close(config->ctl_fd);
+               if (config->ctl_fd_ack >= 0)
+                       close(config->ctl_fd_ack);
+       }
+}
+
 static struct option stat_options[] = {
        OPT_BOOLEAN('T', "transaction", &transaction_run,
                    "hardware transaction statistics"),
@@ -1041,8 +1131,8 @@ static struct option stat_options[] = {
                     "aggregate counts per thread", AGGR_THREAD),
        OPT_SET_UINT(0, "per-node", &stat_config.aggr_mode,
                     "aggregate counts per numa node", AGGR_NODE),
-       OPT_UINTEGER('D', "delay", &stat_config.initial_delay,
-                    "ms to wait before starting measurement after program start"),
+       OPT_INTEGER('D', "delay", &stat_config.initial_delay,
+                   "ms to wait before starting measurement after program start (-1: start with events disabled)"),
        OPT_CALLBACK_NOOPT(0, "metric-only", &stat_config.metric_only, NULL,
                        "Only print computed metrics. No raw values", enable_metric_only),
        OPT_BOOLEAN(0, "metric-no-group", &stat_config.metric_no_group,
@@ -1066,11 +1156,18 @@ static struct option stat_options[] = {
                    "Use with 'percore' event qualifier to show the event "
                    "counts of one hardware thread by sum up total hardware "
                    "threads of same physical core"),
+       OPT_BOOLEAN(0, "summary", &stat_config.summary,
+                      "print summary for interval mode"),
 #ifdef HAVE_LIBPFM
        OPT_CALLBACK(0, "pfm-events", &evsel_list, "event",
                "libpfm4 event selector. use 'perf list' to list available events",
                parse_libpfm_events_option),
 #endif
+       OPT_CALLBACK(0, "control", &stat_config, "fd:ctl-fd[,ack-fd] or fifo:ctl-fifo[,ack-fifo]",
+                    "Listen on ctl-fd descriptor for command to control measurement ('enable': enable events, 'disable': disable events).\n"
+                    "\t\t\t  Optionally send control command completion ('ack\\n') to ack-fd descriptor.\n"
+                    "\t\t\t  Alternatively, ctl-fifo / ack-fifo will be opened and used as ctl-fd / ack-fd.",
+                     parse_control_option),
        OPT_END()
 };
 
@@ -1679,19 +1776,17 @@ static int add_default_attributes(void)
                if (target__has_cpu(&target))
                        default_attrs0[0].config = PERF_COUNT_SW_CPU_CLOCK;
 
-               if (perf_evlist__add_default_attrs(evsel_list, default_attrs0) < 0)
+               if (evlist__add_default_attrs(evsel_list, default_attrs0) < 0)
                        return -1;
                if (pmu_have_event("cpu", "stalled-cycles-frontend")) {
-                       if (perf_evlist__add_default_attrs(evsel_list,
-                                               frontend_attrs) < 0)
+                       if (evlist__add_default_attrs(evsel_list, frontend_attrs) < 0)
                                return -1;
                }
                if (pmu_have_event("cpu", "stalled-cycles-backend")) {
-                       if (perf_evlist__add_default_attrs(evsel_list,
-                                               backend_attrs) < 0)
+                       if (evlist__add_default_attrs(evsel_list, backend_attrs) < 0)
                                return -1;
                }
-               if (perf_evlist__add_default_attrs(evsel_list, default_attrs1) < 0)
+               if (evlist__add_default_attrs(evsel_list, default_attrs1) < 0)
                        return -1;
        }
 
@@ -1701,21 +1796,21 @@ static int add_default_attributes(void)
                return 0;
 
        /* Append detailed run extra attributes: */
-       if (perf_evlist__add_default_attrs(evsel_list, detailed_attrs) < 0)
+       if (evlist__add_default_attrs(evsel_list, detailed_attrs) < 0)
                return -1;
 
        if (detailed_run < 2)
                return 0;
 
        /* Append very detailed run extra attributes: */
-       if (perf_evlist__add_default_attrs(evsel_list, very_detailed_attrs) < 0)
+       if (evlist__add_default_attrs(evsel_list, very_detailed_attrs) < 0)
                return -1;
 
        if (detailed_run < 3)
                return 0;
 
        /* Append very, very detailed run extra attributes: */
-       return perf_evlist__add_default_attrs(evsel_list, very_very_detailed_attrs);
+       return evlist__add_default_attrs(evsel_list, very_very_detailed_attrs);
 }
 
 static const char * const stat_record_usage[] = {
@@ -2242,6 +2337,9 @@ int cmd_stat(int argc, const char **argv)
        signal(SIGALRM, skip_signal);
        signal(SIGABRT, skip_signal);
 
+       if (evlist__initialize_ctlfd(evsel_list, stat_config.ctl_fd, stat_config.ctl_fd_ack))
+               goto out;
+
        status = 0;
        for (run_idx = 0; forever || run_idx < stat_config.run_count; run_idx++) {
                if (stat_config.run_count != 1 && verbose > 0)
@@ -2261,6 +2359,8 @@ int cmd_stat(int argc, const char **argv)
        if (!forever && status != -1 && (!interval || stat_config.summary))
                print_counters(NULL, argc, argv);
 
+       evlist__finalize_ctlfd(evsel_list);
+
        if (STAT_RECORD) {
                /*
                 * We synthesize the kernel mmap record just so that older tools
@@ -2307,7 +2407,9 @@ out:
 
        evlist__delete(evsel_list);
 
+       metricgroup__rblist_exit(&stat_config.metric_events);
        runtime_stat_delete(&stat_config);
+       close_control_option(&stat_config);
 
        return status;
 }