perf evlist: Factor out a function to propagate maps for a single evsel
[firefly-linux-kernel-4.4.55.git] / tools / perf / util / stat.c
index 4014b709f956b96b86dab3526f3b35fec6d8c166..415c359de4654be8f68a30effa530dd06696b52b 100644 (file)
@@ -1,6 +1,8 @@
 #include <math.h>
 #include "stat.h"
+#include "evlist.h"
 #include "evsel.h"
+#include "thread_map.h"
 
 void update_stats(struct stats *stats, u64 val)
 {
@@ -95,38 +97,234 @@ void perf_stat_evsel_id_init(struct perf_evsel *evsel)
        }
 }
 
-struct perf_counts *perf_counts__new(int ncpus)
+void perf_evsel__reset_stat_priv(struct perf_evsel *evsel)
 {
-       int size = sizeof(struct perf_counts) +
-                  ncpus * sizeof(struct perf_counts_values);
+       int i;
+       struct perf_stat *ps = evsel->priv;
+
+       for (i = 0; i < 3; i++)
+               init_stats(&ps->res_stats[i]);
+
+       perf_stat_evsel_id_init(evsel);
+}
 
-       return zalloc(size);
+int perf_evsel__alloc_stat_priv(struct perf_evsel *evsel)
+{
+       evsel->priv = zalloc(sizeof(struct perf_stat));
+       if (evsel->priv == NULL)
+               return -ENOMEM;
+       perf_evsel__reset_stat_priv(evsel);
+       return 0;
+}
+
+void perf_evsel__free_stat_priv(struct perf_evsel *evsel)
+{
+       zfree(&evsel->priv);
 }
 
-void perf_counts__delete(struct perf_counts *counts)
+int perf_evsel__alloc_prev_raw_counts(struct perf_evsel *evsel,
+                                     int ncpus, int nthreads)
 {
-       free(counts);
+       struct perf_counts *counts;
+
+       counts = perf_counts__new(ncpus, nthreads);
+       if (counts)
+               evsel->prev_raw_counts = counts;
+
+       return counts ? 0 : -ENOMEM;
 }
 
-static void perf_counts__reset(struct perf_counts *counts, int ncpus)
+void perf_evsel__free_prev_raw_counts(struct perf_evsel *evsel)
 {
-       memset(counts, 0, (sizeof(*counts) +
-              (ncpus * sizeof(struct perf_counts_values))));
+       perf_counts__delete(evsel->prev_raw_counts);
+       evsel->prev_raw_counts = NULL;
 }
 
-void perf_evsel__reset_counts(struct perf_evsel *evsel, int ncpus)
+int perf_evsel__alloc_stats(struct perf_evsel *evsel, bool alloc_raw)
 {
-       perf_counts__reset(evsel->counts, ncpus);
+       int ncpus = perf_evsel__nr_cpus(evsel);
+       int nthreads = thread_map__nr(evsel->threads);
+
+       if (perf_evsel__alloc_stat_priv(evsel) < 0 ||
+           perf_evsel__alloc_counts(evsel, ncpus, nthreads) < 0 ||
+           (alloc_raw && perf_evsel__alloc_prev_raw_counts(evsel, ncpus, nthreads) < 0))
+               return -ENOMEM;
+
+       return 0;
 }
 
-int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus)
+int perf_evlist__alloc_stats(struct perf_evlist *evlist, bool alloc_raw)
 {
-       evsel->counts = perf_counts__new(ncpus);
-       return evsel->counts != NULL ? 0 : -ENOMEM;
+       struct perf_evsel *evsel;
+
+       evlist__for_each(evlist, evsel) {
+               if (perf_evsel__alloc_stats(evsel, alloc_raw))
+                       goto out_free;
+       }
+
+       return 0;
+
+out_free:
+       perf_evlist__free_stats(evlist);
+       return -1;
 }
 
-void perf_evsel__free_counts(struct perf_evsel *evsel)
+void perf_evlist__free_stats(struct perf_evlist *evlist)
 {
-       perf_counts__delete(evsel->counts);
-       evsel->counts = NULL;
+       struct perf_evsel *evsel;
+
+       evlist__for_each(evlist, evsel) {
+               perf_evsel__free_stat_priv(evsel);
+               perf_evsel__free_counts(evsel);
+               perf_evsel__free_prev_raw_counts(evsel);
+       }
+}
+
+void perf_evlist__reset_stats(struct perf_evlist *evlist)
+{
+       struct perf_evsel *evsel;
+
+       evlist__for_each(evlist, evsel) {
+               perf_evsel__reset_stat_priv(evsel);
+               perf_evsel__reset_counts(evsel);
+       }
+}
+
+static void zero_per_pkg(struct perf_evsel *counter)
+{
+       if (counter->per_pkg_mask)
+               memset(counter->per_pkg_mask, 0, MAX_NR_CPUS);
+}
+
+static int check_per_pkg(struct perf_evsel *counter, int cpu, bool *skip)
+{
+       unsigned long *mask = counter->per_pkg_mask;
+       struct cpu_map *cpus = perf_evsel__cpus(counter);
+       int s;
+
+       *skip = false;
+
+       if (!counter->per_pkg)
+               return 0;
+
+       if (cpu_map__empty(cpus))
+               return 0;
+
+       if (!mask) {
+               mask = zalloc(MAX_NR_CPUS);
+               if (!mask)
+                       return -ENOMEM;
+
+               counter->per_pkg_mask = mask;
+       }
+
+       s = cpu_map__get_socket(cpus, cpu);
+       if (s < 0)
+               return -1;
+
+       *skip = test_and_set_bit(s, mask) == 1;
+       return 0;
+}
+
+static int
+process_counter_values(struct perf_stat_config *config, struct perf_evsel *evsel,
+                      int cpu, int thread,
+                      struct perf_counts_values *count)
+{
+       struct perf_counts_values *aggr = &evsel->counts->aggr;
+       static struct perf_counts_values zero;
+       bool skip = false;
+
+       if (check_per_pkg(evsel, cpu, &skip)) {
+               pr_err("failed to read per-pkg counter\n");
+               return -1;
+       }
+
+       if (skip)
+               count = &zero;
+
+       switch (config->aggr_mode) {
+       case AGGR_THREAD:
+       case AGGR_CORE:
+       case AGGR_SOCKET:
+       case AGGR_NONE:
+               if (!evsel->snapshot)
+                       perf_evsel__compute_deltas(evsel, cpu, thread, count);
+               perf_counts_values__scale(count, config->scale, NULL);
+               if (config->aggr_mode == AGGR_NONE)
+                       perf_stat__update_shadow_stats(evsel, count->values, cpu);
+               break;
+       case AGGR_GLOBAL:
+               aggr->val += count->val;
+               if (config->scale) {
+                       aggr->ena += count->ena;
+                       aggr->run += count->run;
+               }
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static int process_counter_maps(struct perf_stat_config *config,
+                               struct perf_evsel *counter)
+{
+       int nthreads = thread_map__nr(counter->threads);
+       int ncpus = perf_evsel__nr_cpus(counter);
+       int cpu, thread;
+
+       if (counter->system_wide)
+               nthreads = 1;
+
+       for (thread = 0; thread < nthreads; thread++) {
+               for (cpu = 0; cpu < ncpus; cpu++) {
+                       if (process_counter_values(config, counter, cpu, thread,
+                                                  perf_counts(counter->counts, cpu, thread)))
+                               return -1;
+               }
+       }
+
+       return 0;
+}
+
+int perf_stat_process_counter(struct perf_stat_config *config,
+                             struct perf_evsel *counter)
+{
+       struct perf_counts_values *aggr = &counter->counts->aggr;
+       struct perf_stat *ps = counter->priv;
+       u64 *count = counter->counts->aggr.values;
+       int i, ret;
+
+       aggr->val = aggr->ena = aggr->run = 0;
+       init_stats(ps->res_stats);
+
+       if (counter->per_pkg)
+               zero_per_pkg(counter);
+
+       ret = process_counter_maps(config, counter);
+       if (ret)
+               return ret;
+
+       if (config->aggr_mode != AGGR_GLOBAL)
+               return 0;
+
+       if (!counter->snapshot)
+               perf_evsel__compute_deltas(counter, -1, -1, aggr);
+       perf_counts_values__scale(aggr, config->scale, &counter->counts->scaled);
+
+       for (i = 0; i < 3; i++)
+               update_stats(&ps->res_stats[i], count[i]);
+
+       if (verbose) {
+               fprintf(config->output, "%s: %" PRIu64 " %" PRIu64 " %" PRIu64 "\n",
+                       perf_evsel__name(counter), count[0], count[1], count[2]);
+       }
+
+       /*
+        * Save the full runtime - to allow normalization during printout:
+        */
+       perf_stat__update_shadow_stats(counter, count, 0);
+
+       return 0;
 }