linux/tools/perf/util/stat.c
<<
>>
Prefs
   1#include <math.h>
   2#include "stat.h"
   3#include "evlist.h"
   4#include "evsel.h"
   5#include "thread_map.h"
   6
   7void update_stats(struct stats *stats, u64 val)
   8{
   9        double delta;
  10
  11        stats->n++;
  12        delta = val - stats->mean;
  13        stats->mean += delta / stats->n;
  14        stats->M2 += delta*(val - stats->mean);
  15
  16        if (val > stats->max)
  17                stats->max = val;
  18
  19        if (val < stats->min)
  20                stats->min = val;
  21}
  22
  23double avg_stats(struct stats *stats)
  24{
  25        return stats->mean;
  26}
  27
  28/*
  29 * http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance
  30 *
  31 *       (\Sum n_i^2) - ((\Sum n_i)^2)/n
  32 * s^2 = -------------------------------
  33 *                  n - 1
  34 *
  35 * http://en.wikipedia.org/wiki/Stddev
  36 *
  37 * The std dev of the mean is related to the std dev by:
  38 *
  39 *             s
  40 * s_mean = -------
  41 *          sqrt(n)
  42 *
  43 */
  44double stddev_stats(struct stats *stats)
  45{
  46        double variance, variance_mean;
  47
  48        if (stats->n < 2)
  49                return 0.0;
  50
  51        variance = stats->M2 / (stats->n - 1);
  52        variance_mean = variance / stats->n;
  53
  54        return sqrt(variance_mean);
  55}
  56
  57double rel_stddev_stats(double stddev, double avg)
  58{
  59        double pct = 0.0;
  60
  61        if (avg)
  62                pct = 100.0 * stddev/avg;
  63
  64        return pct;
  65}
  66
  67bool __perf_evsel_stat__is(struct perf_evsel *evsel,
  68                           enum perf_stat_evsel_id id)
  69{
  70        struct perf_stat_evsel *ps = evsel->priv;
  71
  72        return ps->id == id;
  73}
  74
  75#define ID(id, name) [PERF_STAT_EVSEL_ID__##id] = #name
  76static const char *id_str[PERF_STAT_EVSEL_ID__MAX] = {
  77        ID(NONE,                x),
  78        ID(CYCLES_IN_TX,        cpu/cycles-t/),
  79        ID(TRANSACTION_START,   cpu/tx-start/),
  80        ID(ELISION_START,       cpu/el-start/),
  81        ID(CYCLES_IN_TX_CP,     cpu/cycles-ct/),
  82        ID(TOPDOWN_TOTAL_SLOTS, topdown-total-slots),
  83        ID(TOPDOWN_SLOTS_ISSUED, topdown-slots-issued),
  84        ID(TOPDOWN_SLOTS_RETIRED, topdown-slots-retired),
  85        ID(TOPDOWN_FETCH_BUBBLES, topdown-fetch-bubbles),
  86        ID(TOPDOWN_RECOVERY_BUBBLES, topdown-recovery-bubbles),
  87};
  88#undef ID
  89
  90void perf_stat_evsel_id_init(struct perf_evsel *evsel)
  91{
  92        struct perf_stat_evsel *ps = evsel->priv;
  93        int i;
  94
  95        /* ps->id is 0 hence PERF_STAT_EVSEL_ID__NONE by default */
  96
  97        for (i = 0; i < PERF_STAT_EVSEL_ID__MAX; i++) {
  98                if (!strcmp(perf_evsel__name(evsel), id_str[i])) {
  99                        ps->id = i;
 100                        break;
 101                }
 102        }
 103}
 104
 105static void perf_evsel__reset_stat_priv(struct perf_evsel *evsel)
 106{
 107        int i;
 108        struct perf_stat_evsel *ps = evsel->priv;
 109
 110        for (i = 0; i < 3; i++)
 111                init_stats(&ps->res_stats[i]);
 112
 113        perf_stat_evsel_id_init(evsel);
 114}
 115
 116static int perf_evsel__alloc_stat_priv(struct perf_evsel *evsel)
 117{
 118        evsel->priv = zalloc(sizeof(struct perf_stat_evsel));
 119        if (evsel->priv == NULL)
 120                return -ENOMEM;
 121        perf_evsel__reset_stat_priv(evsel);
 122        return 0;
 123}
 124
 125static void perf_evsel__free_stat_priv(struct perf_evsel *evsel)
 126{
 127        zfree(&evsel->priv);
 128}
 129
 130static int perf_evsel__alloc_prev_raw_counts(struct perf_evsel *evsel,
 131                                             int ncpus, int nthreads)
 132{
 133        struct perf_counts *counts;
 134
 135        counts = perf_counts__new(ncpus, nthreads);
 136        if (counts)
 137                evsel->prev_raw_counts = counts;
 138
 139        return counts ? 0 : -ENOMEM;
 140}
 141
 142static void perf_evsel__free_prev_raw_counts(struct perf_evsel *evsel)
 143{
 144        perf_counts__delete(evsel->prev_raw_counts);
 145        evsel->prev_raw_counts = NULL;
 146}
 147
 148static int perf_evsel__alloc_stats(struct perf_evsel *evsel, bool alloc_raw)
 149{
 150        int ncpus = perf_evsel__nr_cpus(evsel);
 151        int nthreads = thread_map__nr(evsel->threads);
 152
 153        if (perf_evsel__alloc_stat_priv(evsel) < 0 ||
 154            perf_evsel__alloc_counts(evsel, ncpus, nthreads) < 0 ||
 155            (alloc_raw && perf_evsel__alloc_prev_raw_counts(evsel, ncpus, nthreads) < 0))
 156                return -ENOMEM;
 157
 158        return 0;
 159}
 160
 161int perf_evlist__alloc_stats(struct perf_evlist *evlist, bool alloc_raw)
 162{
 163        struct perf_evsel *evsel;
 164
 165        evlist__for_each_entry(evlist, evsel) {
 166                if (perf_evsel__alloc_stats(evsel, alloc_raw))
 167                        goto out_free;
 168        }
 169
 170        return 0;
 171
 172out_free:
 173        perf_evlist__free_stats(evlist);
 174        return -1;
 175}
 176
 177void perf_evlist__free_stats(struct perf_evlist *evlist)
 178{
 179        struct perf_evsel *evsel;
 180
 181        evlist__for_each_entry(evlist, evsel) {
 182                perf_evsel__free_stat_priv(evsel);
 183                perf_evsel__free_counts(evsel);
 184                perf_evsel__free_prev_raw_counts(evsel);
 185        }
 186}
 187
 188void perf_evlist__reset_stats(struct perf_evlist *evlist)
 189{
 190        struct perf_evsel *evsel;
 191
 192        evlist__for_each_entry(evlist, evsel) {
 193                perf_evsel__reset_stat_priv(evsel);
 194                perf_evsel__reset_counts(evsel);
 195        }
 196}
 197
 198static void zero_per_pkg(struct perf_evsel *counter)
 199{
 200        if (counter->per_pkg_mask)
 201                memset(counter->per_pkg_mask, 0, MAX_NR_CPUS);
 202}
 203
 204static int check_per_pkg(struct perf_evsel *counter,
 205                         struct perf_counts_values *vals, int cpu, bool *skip)
 206{
 207        unsigned long *mask = counter->per_pkg_mask;
 208        struct cpu_map *cpus = perf_evsel__cpus(counter);
 209        int s;
 210
 211        *skip = false;
 212
 213        if (!counter->per_pkg)
 214                return 0;
 215
 216        if (cpu_map__empty(cpus))
 217                return 0;
 218
 219        if (!mask) {
 220                mask = zalloc(MAX_NR_CPUS);
 221                if (!mask)
 222                        return -ENOMEM;
 223
 224                counter->per_pkg_mask = mask;
 225        }
 226
 227        /*
 228         * we do not consider an event that has not run as a good
 229         * instance to mark a package as used (skip=1). Otherwise
 230         * we may run into a situation where the first CPU in a package
 231         * is not running anything, yet the second is, and this function
 232         * would mark the package as used after the first CPU and would
 233         * not read the values from the second CPU.
 234         */
 235        if (!(vals->run && vals->ena))
 236                return 0;
 237
 238        s = cpu_map__get_socket(cpus, cpu, NULL);
 239        if (s < 0)
 240                return -1;
 241
 242        *skip = test_and_set_bit(s, mask) == 1;
 243        return 0;
 244}
 245
 246static int
 247process_counter_values(struct perf_stat_config *config, struct perf_evsel *evsel,
 248                       int cpu, int thread,
 249                       struct perf_counts_values *count)
 250{
 251        struct perf_counts_values *aggr = &evsel->counts->aggr;
 252        static struct perf_counts_values zero;
 253        bool skip = false;
 254
 255        if (check_per_pkg(evsel, count, cpu, &skip)) {
 256                pr_err("failed to read per-pkg counter\n");
 257                return -1;
 258        }
 259
 260        if (skip)
 261                count = &zero;
 262
 263        switch (config->aggr_mode) {
 264        case AGGR_THREAD:
 265        case AGGR_CORE:
 266        case AGGR_SOCKET:
 267        case AGGR_NONE:
 268                if (!evsel->snapshot)
 269                        perf_evsel__compute_deltas(evsel, cpu, thread, count);
 270                perf_counts_values__scale(count, config->scale, NULL);
 271                if (config->aggr_mode == AGGR_NONE)
 272                        perf_stat__update_shadow_stats(evsel, count->values, cpu);
 273                break;
 274        case AGGR_GLOBAL:
 275                aggr->val += count->val;
 276                if (config->scale) {
 277                        aggr->ena += count->ena;
 278                        aggr->run += count->run;
 279                }
 280        case AGGR_UNSET:
 281        default:
 282                break;
 283        }
 284
 285        return 0;
 286}
 287
 288static int process_counter_maps(struct perf_stat_config *config,
 289                                struct perf_evsel *counter)
 290{
 291        int nthreads = thread_map__nr(counter->threads);
 292        int ncpus = perf_evsel__nr_cpus(counter);
 293        int cpu, thread;
 294
 295        if (counter->system_wide)
 296                nthreads = 1;
 297
 298        for (thread = 0; thread < nthreads; thread++) {
 299                for (cpu = 0; cpu < ncpus; cpu++) {
 300                        if (process_counter_values(config, counter, cpu, thread,
 301                                                   perf_counts(counter->counts, cpu, thread)))
 302                                return -1;
 303                }
 304        }
 305
 306        return 0;
 307}
 308
 309int perf_stat_process_counter(struct perf_stat_config *config,
 310                              struct perf_evsel *counter)
 311{
 312        struct perf_counts_values *aggr = &counter->counts->aggr;
 313        struct perf_stat_evsel *ps = counter->priv;
 314        u64 *count = counter->counts->aggr.values;
 315        u64 val;
 316        int i, ret;
 317
 318        aggr->val = aggr->ena = aggr->run = 0;
 319
 320        /*
 321         * We calculate counter's data every interval,
 322         * and the display code shows ps->res_stats
 323         * avg value. We need to zero the stats for
 324         * interval mode, otherwise overall avg running
 325         * averages will be shown for each interval.
 326         */
 327        if (config->interval)
 328                init_stats(ps->res_stats);
 329
 330        if (counter->per_pkg)
 331                zero_per_pkg(counter);
 332
 333        ret = process_counter_maps(config, counter);
 334        if (ret)
 335                return ret;
 336
 337        if (config->aggr_mode != AGGR_GLOBAL)
 338                return 0;
 339
 340        if (!counter->snapshot)
 341                perf_evsel__compute_deltas(counter, -1, -1, aggr);
 342        perf_counts_values__scale(aggr, config->scale, &counter->counts->scaled);
 343
 344        for (i = 0; i < 3; i++)
 345                update_stats(&ps->res_stats[i], count[i]);
 346
 347        if (verbose) {
 348                fprintf(config->output, "%s: %" PRIu64 " %" PRIu64 " %" PRIu64 "\n",
 349                        perf_evsel__name(counter), count[0], count[1], count[2]);
 350        }
 351
 352        /*
 353         * Save the full runtime - to allow normalization during printout:
 354         */
 355        val = counter->scale * *count;
 356        perf_stat__update_shadow_stats(counter, &val, 0);
 357
 358        return 0;
 359}
 360
 361int perf_event__process_stat_event(struct perf_tool *tool __maybe_unused,
 362                                   union perf_event *event,
 363                                   struct perf_session *session)
 364{
 365        struct perf_counts_values count;
 366        struct stat_event *st = &event->stat;
 367        struct perf_evsel *counter;
 368
 369        count.val = st->val;
 370        count.ena = st->ena;
 371        count.run = st->run;
 372
 373        counter = perf_evlist__id2evsel(session->evlist, st->id);
 374        if (!counter) {
 375                pr_err("Failed to resolve counter for stat event.\n");
 376                return -EINVAL;
 377        }
 378
 379        *perf_counts(counter->counts, st->cpu, st->thread) = count;
 380        counter->supported = true;
 381        return 0;
 382}
 383
 384size_t perf_event__fprintf_stat(union perf_event *event, FILE *fp)
 385{
 386        struct stat_event *st = (struct stat_event *) event;
 387        size_t ret;
 388
 389        ret  = fprintf(fp, "\n... id %" PRIu64 ", cpu %d, thread %d\n",
 390                       st->id, st->cpu, st->thread);
 391        ret += fprintf(fp, "... value %" PRIu64 ", enabled %" PRIu64 ", running %" PRIu64 "\n",
 392                       st->val, st->ena, st->run);
 393
 394        return ret;
 395}
 396
 397size_t perf_event__fprintf_stat_round(union perf_event *event, FILE *fp)
 398{
 399        struct stat_round_event *rd = (struct stat_round_event *)event;
 400        size_t ret;
 401
 402        ret = fprintf(fp, "\n... time %" PRIu64 ", type %s\n", rd->time,
 403                      rd->type == PERF_STAT_ROUND_TYPE__FINAL ? "FINAL" : "INTERVAL");
 404
 405        return ret;
 406}
 407
 408size_t perf_event__fprintf_stat_config(union perf_event *event, FILE *fp)
 409{
 410        struct perf_stat_config sc;
 411        size_t ret;
 412
 413        perf_event__read_stat_config(&sc, &event->stat_config);
 414
 415        ret  = fprintf(fp, "\n");
 416        ret += fprintf(fp, "... aggr_mode %d\n", sc.aggr_mode);
 417        ret += fprintf(fp, "... scale     %d\n", sc.scale);
 418        ret += fprintf(fp, "... interval  %u\n", sc.interval);
 419
 420        return ret;
 421}
 422