From: Andi Kleen <a...@linux.intel.com>

Add a new mode to only print metrics. Sometimes we don't care about
the raw values, just want the computed metrics. This allows more
compact printing, so with -I each sample is only a single line.
This also allows easier plotting and processing with other tools.

The main target is with using --topdown, but it also works with
-T and standard perf stat. A few metrics are not supported.

To avoiding having to hardcode all the metrics in the code it uses
a two pass approach: first compute dummy metrics and only
print the headers in the print_metric callback. Then use the callback
to print the actual values. There are some additional changes
in the stat printout code to handle all metrics being on a single line.

Example:

% perf stat -a -I 1000 --metric-only
     1.000604977 frontend cycles idle     backend cycles idle      insn per 
cycle           stalled cycles per insn  branch-misses of all branches
     1.000604977                                                    0.76        
                                     2.35%
     2.000924680                                                    0.72        
                                     2.34%
     3.001139592                                                    0.76        
                                     2.57%
     4.001358452                                                    0.73        
                                     2.44%

The output is fairly wide, but that's a trade off for the concise format.

v2: Lots of updates.
v3: Use slightly narrower columns
Signed-off-by: Andi Kleen <a...@linux.intel.com>
---
 tools/perf/Documentation/perf-stat.txt |   4 +
 tools/perf/builtin-stat.c              | 210 +++++++++++++++++++++++++++++++--
 2 files changed, 204 insertions(+), 10 deletions(-)

diff --git a/tools/perf/Documentation/perf-stat.txt 
b/tools/perf/Documentation/perf-stat.txt
index c85b568..1b13cdd 100644
--- a/tools/perf/Documentation/perf-stat.txt
+++ b/tools/perf/Documentation/perf-stat.txt
@@ -139,6 +139,10 @@ Print count deltas every N milliseconds (minimum: 10ms)
 The overhead percentage could be high in some cases, for instance with small, 
sub 100ms intervals.  Use with caution.
        example: 'perf stat -I 1000 -e cycles -a sleep 5'
 
+--metric-only::
+Only print computed metrics. Print them in a single line.
+Don't show any raw values. Not supported with -A or --per-thread.
+
 --per-socket::
 Aggregate counts per processor socket for system-wide mode measurements.  This
 is a useful mode to detect imbalance between sockets.  To enable this mode,
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index 607be16..97de140 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -122,6 +122,7 @@ static bool                 sync_run                        
= false;
 static unsigned int            initial_delay                   = 0;
 static unsigned int            unit_width                      = 4; /* 
strlen("unit") */
 static bool                    forever                         = false;
+static bool                    metric_only                     = false;
 static struct timespec         ref_time;
 static struct cpu_map          *aggr_map;
 static aggr_get_id_t           aggr_get_id;
@@ -828,6 +829,99 @@ static void print_metric_csv(void *ctx,
        fprintf(out, "%s%s%s%s", csv_sep, vals, csv_sep, unit);
 }
 
+#define METRIC_ONLY_LEN 20
+
+/* Filter out some columns that don't work well in metrics only mode */
+
+static bool valid_only_metric(const char *unit)
+{
+       if (!unit)
+               return false;
+       if (strstr(unit, "/sec") ||
+           strstr(unit, "hz") ||
+           strstr(unit, "Hz") ||
+           strstr(unit, "CPUs utilized"))
+               return false;
+       return true;
+}
+
+static const char *fixunit(char *buf, struct perf_evsel *evsel,
+                          const char *unit)
+{
+       if (!strncmp(unit, "of all", 6)) {
+               snprintf(buf, 1024, "%s %s", perf_evsel__name(evsel),
+                        unit);
+               return buf;
+       }
+       return unit;
+}
+
+static void print_metric_only(void *ctx, const char *color, const char *fmt,
+                             const char *unit, double val)
+{
+       struct outstate *os = ctx;
+       FILE *out = os->fh;
+       int n;
+       char buf[1024];
+       unsigned mlen = METRIC_ONLY_LEN;
+
+       if (!valid_only_metric(unit))
+               return;
+       unit = fixunit(buf, os->evsel, unit);
+       if (color)
+               n = color_fprintf(out, color, fmt, val);
+       else
+               n = fprintf(out, fmt, val);
+       if (n > METRIC_ONLY_LEN)
+               n = METRIC_ONLY_LEN;
+       if (mlen < strlen(unit))
+               mlen = strlen(unit) + 1;
+       fprintf(out, "%*s", mlen - n, "");
+}
+
+static void print_metric_only_csv(void *ctx, const char *color __maybe_unused,
+                                 const char *fmt,
+                                 const char *unit, double val)
+{
+       struct outstate *os = ctx;
+       FILE *out = os->fh;
+       char buf[64], *vals, *ends;
+       char tbuf[1024];
+
+       if (!valid_only_metric(unit))
+               return;
+       unit = fixunit(tbuf, os->evsel, unit);
+       snprintf(buf, sizeof buf, fmt, val);
+       vals = buf;
+       while (isspace(*vals))
+               vals++;
+       ends = vals;
+       while (isdigit(*ends) || *ends == '.')
+               ends++;
+       *ends = 0;
+       fprintf(out, "%s%s", vals, csv_sep);
+}
+
+static void new_line_metric(void *ctx __maybe_unused)
+{
+}
+
+static void print_metric_header(void *ctx, const char *color __maybe_unused,
+                               const char *fmt __maybe_unused,
+                               const char *unit, double val __maybe_unused)
+{
+       struct outstate *os = ctx;
+       char tbuf[1024];
+
+       if (!valid_only_metric(unit))
+               return;
+       unit = fixunit(tbuf, os->evsel, unit);
+       if (csv_output)
+               fprintf(os->fh, "%s%s", unit, csv_sep);
+       else
+               fprintf(os->fh, "%-*s ", METRIC_ONLY_LEN, unit);
+}
+
 static void nsec_printout(int id, int nr, struct perf_evsel *evsel, double avg)
 {
        FILE *output = stat_config.output;
@@ -916,9 +1010,16 @@ static void printout(int id, int nr, struct perf_evsel 
*counter, double uval,
        print_metric_t pm = print_metric_std;
        void (*nl)(void *);
 
-       nl = new_line_std;
+       if (metric_only) {
+               nl = new_line_metric;
+               if (csv_output)
+                       pm = print_metric_only_csv;
+               else
+                       pm = print_metric_only;
+       } else
+               nl = new_line_std;
 
-       if (csv_output) {
+       if (csv_output && !metric_only) {
                static int aggr_fields[] = {
                        [AGGR_GLOBAL] = 0,
                        [AGGR_THREAD] = 1,
@@ -937,6 +1038,10 @@ static void printout(int id, int nr, struct perf_evsel 
*counter, double uval,
                os.ena = ena;
        }
        if (run == 0 || ena == 0) {
+               if (metric_only) {
+                       pm(&os, NULL, "", "", 0);
+                       return;
+               }
                aggr_printout(counter, id, nr);
 
                fprintf(stat_config.output, "%*s%s",
@@ -962,7 +1067,9 @@ static void printout(int id, int nr, struct perf_evsel 
*counter, double uval,
                return;
        }
 
-       if (nsec_counter(counter))
+       if (metric_only)
+               /* nothing */;
+       else if (nsec_counter(counter))
                nsec_printout(id, nr, counter, uval);
        else
                abs_printout(id, nr, counter, uval);
@@ -975,8 +1082,11 @@ static void printout(int id, int nr, struct perf_evsel 
*counter, double uval,
                                stat_config.aggr_mode == AGGR_GLOBAL ? 0 :
                                first_shadow_cpu(counter, id),
                                &out);
-       print_noise(counter, noise);
-       print_running(run, ena);
+
+       if (!metric_only) {
+               print_noise(counter, noise);
+               print_running(run, ena);
+       }
 }
 
 static void aggr_update_shadow(void)
@@ -1010,6 +1120,7 @@ static void print_aggr(char *prefix)
        int cpu, s, s2, id, nr;
        double uval;
        u64 ena, run, val;
+       bool first;
 
        if (!(aggr_map || aggr_get_id))
                return;
@@ -1017,7 +1128,11 @@ static void print_aggr(char *prefix)
        aggr_update_shadow();
 
        for (s = 0; s < aggr_map->nr; s++) {
+               if (prefix && metric_only)
+                       fprintf(output, "%s", prefix);
+
                id = aggr_map->map[s];
+               first = true;
                evlist__for_each(evsel_list, counter) {
                        val = ena = run = 0;
                        nr = 0;
@@ -1030,13 +1145,20 @@ static void print_aggr(char *prefix)
                                run += perf_counts(counter->counts, cpu, 
0)->run;
                                nr++;
                        }
-                       if (prefix)
+                       if (first && metric_only) {
+                               first = false;
+                               aggr_printout(counter, id, nr);
+                       }
+                       if (prefix && !metric_only)
                                fprintf(output, "%s", prefix);
 
                        uval = val * counter->scale;
                        printout(id, nr, counter, uval, prefix, run, ena, 1.0);
-                       fputc('\n', output);
+                       if (!metric_only)
+                               fputc('\n', output);
                }
+               if (metric_only)
+                       fputc('\n', output);
        }
 }
 
@@ -1081,12 +1203,13 @@ static void print_counter_aggr(struct perf_evsel 
*counter, char *prefix)
        avg_enabled = avg_stats(&ps->res_stats[1]);
        avg_running = avg_stats(&ps->res_stats[2]);
 
-       if (prefix)
+       if (prefix && !metric_only)
                fprintf(output, "%s", prefix);
 
        uval = avg * counter->scale;
        printout(-1, 0, counter, uval, prefix, avg_running, avg_enabled, avg);
-       fprintf(output, "\n");
+       if (!metric_only)
+               fprintf(output, "\n");
 }
 
 /*
@@ -1115,6 +1238,43 @@ static void print_counter(struct perf_evsel *counter, 
char *prefix)
        }
 }
 
+static int aggr_header_lens[] = {
+       [AGGR_CORE] = 18,
+       [AGGR_SOCKET] = 12,
+       [AGGR_NONE] = 15,
+       [AGGR_THREAD] = 24,
+       [AGGR_GLOBAL] = 0,
+};
+
+static void print_metric_headers(char *prefix)
+{
+       struct perf_stat_output_ctx out;
+       struct perf_evsel *counter;
+       struct outstate os = {
+               .fh = stat_config.output
+       };
+
+       if (prefix)
+               fprintf(stat_config.output, "%s", prefix);
+
+       if (!csv_output)
+               fprintf(stat_config.output, "%*s",
+                       aggr_header_lens[stat_config.aggr_mode], "");
+
+       /* Print metrics headers only */
+       evlist__for_each(evsel_list, counter) {
+               os.evsel = counter;
+               out.ctx = &os;
+               out.print_metric = print_metric_header;
+               out.new_line = new_line_metric;
+               os.evsel = counter;
+               perf_stat__print_shadow_stats(counter, 0,
+                                             0,
+                                             &out);
+       }
+       fputc('\n', stat_config.output);
+}
+
 static void print_interval(char *prefix, struct timespec *ts)
 {
        FILE *output = stat_config.output;
@@ -1122,7 +1282,7 @@ 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) {
+       if (num_print_interval == 0 && !csv_output && !metric_only) {
                switch (stat_config.aggr_mode) {
                case AGGR_SOCKET:
                        fprintf(output, "#           time socket cpus           
  counts %*s events\n", unit_width, "unit");
@@ -1209,6 +1369,17 @@ static void print_counters(struct timespec *ts, int 
argc, const char **argv)
        else
                print_header(argc, argv);
 
+       if (metric_only) {
+               static int num_print_iv;
+
+               if (num_print_iv == 0)
+                       print_metric_headers(prefix);
+               if (num_print_iv++ == 25)
+                       num_print_iv = 0;
+               if (stat_config.aggr_mode == AGGR_GLOBAL && prefix)
+                       fprintf(stat_config.output, "%s", prefix);
+       }
+
        switch (stat_config.aggr_mode) {
        case AGGR_CORE:
        case AGGR_SOCKET:
@@ -1221,6 +1392,8 @@ static void print_counters(struct timespec *ts, int argc, 
const char **argv)
        case AGGR_GLOBAL:
                evlist__for_each(evsel_list, counter)
                        print_counter_aggr(counter, prefix);
+               if (metric_only)
+                       fputc('\n', stat_config.output);
                break;
        case AGGR_NONE:
                evlist__for_each(evsel_list, counter)
@@ -1345,6 +1518,8 @@ 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_END()
 };
 
@@ -1966,6 +2141,21 @@ int cmd_stat(int argc, const char **argv, const char 
*prefix __maybe_unused)
                goto out;
        }
 
+       if (metric_only && stat_config.aggr_mode == AGGR_THREAD) {
+               fprintf(stderr, "--metric-only is not supported with 
--per-thread\n");
+               goto out;
+       }
+
+       if (metric_only && stat_config.aggr_mode == AGGR_NONE) {
+               fprintf(stderr, "--metric-only is not supported with -A\n");
+               goto out;
+       }
+
+       if (metric_only && run_count > 1) {
+               fprintf(stderr, "--metric-only is not supported with -r\n");
+               goto out;
+       }
+
        if (output_fd < 0) {
                fprintf(stderr, "argument to --log-fd must be a > 0\n");
                parse_options_usage(stat_usage, stat_options, "log-fd", 0);
-- 
2.4.3

Reply via email to