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