Merge tag 'freevxfs-for-4.8' of git://git.infradead.org/users/hch/freevxfs
[deliverable/linux.git] / tools / perf / builtin-stat.c
index ee7ada78d86f81c5f788feed43f42ec49e33bb96..0c16d20d7e32fa2eb7377247c2e5542d2a5d076a 100644 (file)
 #include "util/thread.h"
 #include "util/thread_map.h"
 #include "util/counts.h"
+#include "util/group.h"
 #include "util/session.h"
 #include "util/tool.h"
+#include "util/group.h"
 #include "asm/bug.h"
 
+#include <api/fs/fs.h>
 #include <stdlib.h>
 #include <sys/prctl.h>
 #include <locale.h>
@@ -98,6 +101,15 @@ static const char * transaction_limited_attrs = {
        "}"
 };
 
+static const char * topdown_attrs[] = {
+       "topdown-total-slots",
+       "topdown-slots-retired",
+       "topdown-recovery-bubbles",
+       "topdown-fetch-bubbles",
+       "topdown-slots-issued",
+       NULL,
+};
+
 static struct perf_evlist      *evsel_list;
 
 static struct target target = {
@@ -112,6 +124,7 @@ static volatile pid_t               child_pid                       = -1;
 static bool                    null_run                        =  false;
 static int                     detailed_run                    =  0;
 static bool                    transaction_run;
+static bool                    topdown_run                     = false;
 static bool                    big_num                         =  true;
 static int                     big_num_opt                     =  -1;
 static const char              *csv_sep                        = NULL;
@@ -124,6 +137,7 @@ static unsigned int         initial_delay                   = 0;
 static unsigned int            unit_width                      = 4; /* strlen("unit") */
 static bool                    forever                         = false;
 static bool                    metric_only                     = false;
+static bool                    force_metric_only               = false;
 static struct timespec         ref_time;
 static struct cpu_map          *aggr_map;
 static aggr_get_id_t           aggr_get_id;
@@ -276,8 +290,12 @@ perf_evsel__write_stat_event(struct perf_evsel *counter, u32 cpu, u32 thread,
 static int read_counter(struct perf_evsel *counter)
 {
        int nthreads = thread_map__nr(evsel_list->threads);
-       int ncpus = perf_evsel__nr_cpus(counter);
-       int cpu, thread;
+       int ncpus, cpu, thread;
+
+       if (target__has_cpu(&target))
+               ncpus = perf_evsel__nr_cpus(counter);
+       else
+               ncpus = 1;
 
        if (!counter->supported)
                return -ENOENT;
@@ -317,7 +335,7 @@ static void read_counters(bool close_counters)
 {
        struct perf_evsel *counter;
 
-       evlist__for_each(evsel_list, counter) {
+       evlist__for_each_entry(evsel_list, counter) {
                if (read_counter(counter))
                        pr_debug("failed to read counter %s\n", counter->name);
 
@@ -403,7 +421,7 @@ static int perf_stat_synthesize_config(bool is_pipe)
         * Synthesize other events stuff not carried within
         * attr event - unit, scale, name
         */
-       evlist__for_each(evsel_list, counter) {
+       evlist__for_each_entry(evsel_list, counter) {
                if (!counter->supported)
                        continue;
 
@@ -536,7 +554,7 @@ static int __run_perf_stat(int argc, const char **argv)
        if (group)
                perf_evlist__set_leader(evsel_list);
 
-       evlist__for_each(evsel_list, counter) {
+       evlist__for_each_entry(evsel_list, counter) {
 try_again:
                if (create_perf_stat_counter(counter) < 0) {
                        /*
@@ -582,7 +600,7 @@ try_again:
        if (perf_evlist__apply_filters(evsel_list, &counter)) {
                error("failed to set filter \"%s\" on event %s with %d (%s)\n",
                        counter->filter, perf_evsel__name(counter), errno,
-                       strerror_r(errno, msg, sizeof(msg)));
+                       str_error_r(errno, msg, sizeof(msg)));
                return -1;
        }
 
@@ -623,7 +641,7 @@ try_again:
                wait(&status);
 
                if (workload_exec_errno) {
-                       const char *emsg = strerror_r(workload_exec_errno, msg, sizeof(msg));
+                       const char *emsg = str_error_r(workload_exec_errno, msg, sizeof(msg));
                        pr_err("Workload failed: %s\n", emsg);
                        return -1;
                }
@@ -1120,7 +1138,7 @@ static void aggr_update_shadow(void)
 
        for (s = 0; s < aggr_map->nr; s++) {
                id = aggr_map->map[s];
-               evlist__for_each(evsel_list, counter) {
+               evlist__for_each_entry(evsel_list, counter) {
                        val = 0;
                        for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) {
                                s2 = aggr_get_id(evsel_list->cpus, cpu);
@@ -1159,7 +1177,7 @@ static void print_aggr(char *prefix)
 
                id = aggr_map->map[s];
                first = true;
-               evlist__for_each(evsel_list, counter) {
+               evlist__for_each_entry(evsel_list, counter) {
                        val = ena = run = 0;
                        nr = 0;
                        for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) {
@@ -1278,7 +1296,7 @@ static void print_no_aggr_metric(char *prefix)
 
                if (prefix)
                        fputs(prefix, stat_config.output);
-               evlist__for_each(evsel_list, counter) {
+               evlist__for_each_entry(evsel_list, counter) {
                        if (first) {
                                aggr_printout(counter, cpu, 0);
                                first = false;
@@ -1302,7 +1320,15 @@ static int aggr_header_lens[] = {
        [AGGR_GLOBAL] = 0,
 };
 
-static void print_metric_headers(char *prefix)
+static const char *aggr_header_csv[] = {
+       [AGGR_CORE]     =       "core,cpus,",
+       [AGGR_SOCKET]   =       "socket,cpus",
+       [AGGR_NONE]     =       "cpu,",
+       [AGGR_THREAD]   =       "comm-pid,",
+       [AGGR_GLOBAL]   =       ""
+};
+
+static void print_metric_headers(const char *prefix, bool no_indent)
 {
        struct perf_stat_output_ctx out;
        struct perf_evsel *counter;
@@ -1313,12 +1339,18 @@ static void print_metric_headers(char *prefix)
        if (prefix)
                fprintf(stat_config.output, "%s", prefix);
 
-       if (!csv_output)
+       if (!csv_output && !no_indent)
                fprintf(stat_config.output, "%*s",
                        aggr_header_lens[stat_config.aggr_mode], "");
+       if (csv_output) {
+               if (stat_config.interval)
+                       fputs("time,", stat_config.output);
+               fputs(aggr_header_csv[stat_config.aggr_mode],
+                       stat_config.output);
+       }
 
        /* Print metrics headers only */
-       evlist__for_each(evsel_list, counter) {
+       evlist__for_each_entry(evsel_list, counter) {
                os.evsel = counter;
                out.ctx = &os;
                out.print_metric = print_metric_header;
@@ -1338,28 +1370,40 @@ static void print_interval(char *prefix, struct timespec *ts)
 
        sprintf(prefix, "%6lu.%09lu%s", ts->tv_sec, ts->tv_nsec, csv_sep);
 
-       if (num_print_interval == 0 && !csv_output && !metric_only) {
+       if (num_print_interval == 0 && !csv_output) {
                switch (stat_config.aggr_mode) {
                case AGGR_SOCKET:
-                       fprintf(output, "#           time socket cpus             counts %*s events\n", unit_width, "unit");
+                       fprintf(output, "#           time socket cpus");
+                       if (!metric_only)
+                               fprintf(output, "             counts %*s events\n", unit_width, "unit");
                        break;
                case AGGR_CORE:
-                       fprintf(output, "#           time core         cpus             counts %*s events\n", unit_width, "unit");
+                       fprintf(output, "#           time core         cpus");
+                       if (!metric_only)
+                               fprintf(output, "             counts %*s events\n", unit_width, "unit");
                        break;
                case AGGR_NONE:
-                       fprintf(output, "#           time CPU                counts %*s events\n", unit_width, "unit");
+                       fprintf(output, "#           time CPU");
+                       if (!metric_only)
+                               fprintf(output, "                counts %*s events\n", unit_width, "unit");
                        break;
                case AGGR_THREAD:
-                       fprintf(output, "#           time             comm-pid                  counts %*s events\n", unit_width, "unit");
+                       fprintf(output, "#           time             comm-pid");
+                       if (!metric_only)
+                               fprintf(output, "                  counts %*s events\n", unit_width, "unit");
                        break;
                case AGGR_GLOBAL:
                default:
-                       fprintf(output, "#           time             counts %*s events\n", unit_width, "unit");
+                       fprintf(output, "#           time");
+                       if (!metric_only)
+                               fprintf(output, "             counts %*s events\n", unit_width, "unit");
                case AGGR_UNSET:
                        break;
                }
        }
 
+       if (num_print_interval == 0 && metric_only)
+               print_metric_headers(" ", true);
        if (++num_print_interval == 25)
                num_print_interval = 0;
 }
@@ -1428,8 +1472,8 @@ static void print_counters(struct timespec *ts, int argc, const char **argv)
        if (metric_only) {
                static int num_print_iv;
 
-               if (num_print_iv == 0)
-                       print_metric_headers(prefix);
+               if (num_print_iv == 0 && !interval)
+                       print_metric_headers(prefix, false);
                if (num_print_iv++ == 25)
                        num_print_iv = 0;
                if (stat_config.aggr_mode == AGGR_GLOBAL && prefix)
@@ -1442,11 +1486,11 @@ static void print_counters(struct timespec *ts, int argc, const char **argv)
                print_aggr(prefix);
                break;
        case AGGR_THREAD:
-               evlist__for_each(evsel_list, counter)
+               evlist__for_each_entry(evsel_list, counter)
                        print_aggr_thread(counter, prefix);
                break;
        case AGGR_GLOBAL:
-               evlist__for_each(evsel_list, counter)
+               evlist__for_each_entry(evsel_list, counter)
                        print_counter_aggr(counter, prefix);
                if (metric_only)
                        fputc('\n', stat_config.output);
@@ -1455,7 +1499,7 @@ static void print_counters(struct timespec *ts, int argc, const char **argv)
                if (metric_only)
                        print_no_aggr_metric(prefix);
                else {
-                       evlist__for_each(evsel_list, counter)
+                       evlist__for_each_entry(evsel_list, counter)
                                print_counter(counter, prefix);
                }
                break;
@@ -1520,6 +1564,14 @@ static int stat__set_big_num(const struct option *opt __maybe_unused,
        return 0;
 }
 
+static int enable_metric_only(const struct option *opt __maybe_unused,
+                             const char *s __maybe_unused, int unset)
+{
+       force_metric_only = true;
+       metric_only = !unset;
+       return 0;
+}
+
 static const struct option stat_options[] = {
        OPT_BOOLEAN('T', "transaction", &transaction_run,
                    "hardware transaction statistics"),
@@ -1578,8 +1630,10 @@ static const struct option stat_options[] = {
                     "aggregate counts per thread", AGGR_THREAD),
        OPT_UINTEGER('D', "delay", &initial_delay,
                     "ms to wait before starting measurement after program start"),
-       OPT_BOOLEAN(0, "metric-only", &metric_only,
-                       "Only print computed metrics. No raw values"),
+       OPT_CALLBACK_NOOPT(0, "metric-only", &metric_only, NULL,
+                       "Only print computed metrics. No raw values", enable_metric_only),
+       OPT_BOOLEAN(0, "topdown", &topdown_run,
+                       "measure topdown level 1 statistics"),
        OPT_END()
 };
 
@@ -1772,12 +1826,62 @@ static int perf_stat_init_aggr_mode_file(struct perf_stat *st)
        return 0;
 }
 
+static int topdown_filter_events(const char **attr, char **str, bool use_group)
+{
+       int off = 0;
+       int i;
+       int len = 0;
+       char *s;
+
+       for (i = 0; attr[i]; i++) {
+               if (pmu_have_event("cpu", attr[i])) {
+                       len += strlen(attr[i]) + 1;
+                       attr[i - off] = attr[i];
+               } else
+                       off++;
+       }
+       attr[i - off] = NULL;
+
+       *str = malloc(len + 1 + 2);
+       if (!*str)
+               return -1;
+       s = *str;
+       if (i - off == 0) {
+               *s = 0;
+               return 0;
+       }
+       if (use_group)
+               *s++ = '{';
+       for (i = 0; attr[i]; i++) {
+               strcpy(s, attr[i]);
+               s += strlen(s);
+               *s++ = ',';
+       }
+       if (use_group) {
+               s[-1] = '}';
+               *s = 0;
+       } else
+               s[-1] = 0;
+       return 0;
+}
+
+__weak bool arch_topdown_check_group(bool *warn)
+{
+       *warn = false;
+       return false;
+}
+
+__weak void arch_topdown_group_warn(void)
+{
+}
+
 /*
  * Add default attributes, if there were no attributes specified or
  * if -d/--detailed, -d -d or -d -d -d is used:
  */
 static int add_default_attributes(void)
 {
+       int err;
        struct perf_event_attr default_attrs0[] = {
 
   { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_TASK_CLOCK             },
@@ -1896,7 +2000,6 @@ static int add_default_attributes(void)
                return 0;
 
        if (transaction_run) {
-               int err;
                if (pmu_have_event("cpu", "cycles-ct") &&
                    pmu_have_event("cpu", "el-start"))
                        err = parse_events(evsel_list, transaction_attrs, NULL);
@@ -1909,6 +2012,46 @@ static int add_default_attributes(void)
                return 0;
        }
 
+       if (topdown_run) {
+               char *str = NULL;
+               bool warn = false;
+
+               if (stat_config.aggr_mode != AGGR_GLOBAL &&
+                   stat_config.aggr_mode != AGGR_CORE) {
+                       pr_err("top down event configuration requires --per-core mode\n");
+                       return -1;
+               }
+               stat_config.aggr_mode = AGGR_CORE;
+               if (nr_cgroups || !target__has_cpu(&target)) {
+                       pr_err("top down event configuration requires system-wide mode (-a)\n");
+                       return -1;
+               }
+
+               if (!force_metric_only)
+                       metric_only = true;
+               if (topdown_filter_events(topdown_attrs, &str,
+                               arch_topdown_check_group(&warn)) < 0) {
+                       pr_err("Out of memory\n");
+                       return -1;
+               }
+               if (topdown_attrs[0] && str) {
+                       if (warn)
+                               arch_topdown_group_warn();
+                       err = parse_events(evsel_list, str, NULL);
+                       if (err) {
+                               fprintf(stderr,
+                                       "Cannot set up top down events %s: %d\n",
+                                       str, err);
+                               free(str);
+                               return -1;
+                       }
+               } else {
+                       fprintf(stderr, "System does not support topdown\n");
+                       return -1;
+               }
+               free(str);
+       }
+
        if (!evsel_list->nr_entries) {
                if (target__has_cpu(&target))
                        default_attrs0[0].config = PERF_COUNT_SW_CPU_CLOCK;
@@ -2010,7 +2153,7 @@ static int process_stat_round_event(struct perf_tool *tool __maybe_unused,
        const char **argv = session->header.env.cmdline_argv;
        int argc = session->header.env.nr_cmdline;
 
-       evlist__for_each(evsel_list, counter)
+       evlist__for_each_entry(evsel_list, counter)
                perf_stat_process_counter(&stat_config, counter);
 
        if (stat_round->type == PERF_STAT_ROUND_TYPE__FINAL)
This page took 0.035167 seconds and 5 git commands to generate.