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 struct perf_stat_evsel *ps = evsel->priv; 132 133 if (ps) 134 free(ps->group_data); 135 zfree(&evsel->priv); 136 } 137 138 static int perf_evsel__alloc_prev_raw_counts(struct perf_evsel *evsel, 139 int ncpus, int nthreads) 140 { 141 struct perf_counts *counts; 142 143 counts = perf_counts__new(ncpus, nthreads); 144 if (counts) 145 evsel->prev_raw_counts = counts; 146 147 return counts ? 0 : -ENOMEM; 148 } 149 150 static void perf_evsel__free_prev_raw_counts(struct perf_evsel *evsel) 151 { 152 perf_counts__delete(evsel->prev_raw_counts); 153 evsel->prev_raw_counts = NULL; 154 } 155 156 static int perf_evsel__alloc_stats(struct perf_evsel *evsel, bool alloc_raw) 157 { 158 int ncpus = perf_evsel__nr_cpus(evsel); 159 int nthreads = thread_map__nr(evsel->threads); 160 161 if (perf_evsel__alloc_stat_priv(evsel) < 0 || 162 perf_evsel__alloc_counts(evsel, ncpus, nthreads) < 0 || 163 (alloc_raw && perf_evsel__alloc_prev_raw_counts(evsel, ncpus, nthreads) < 0)) 164 return -ENOMEM; 165 166 return 0; 167 } 168 169 int perf_evlist__alloc_stats(struct perf_evlist *evlist, bool alloc_raw) 170 { 171 struct perf_evsel *evsel; 172 173 evlist__for_each_entry(evlist, evsel) { 174 if (perf_evsel__alloc_stats(evsel, alloc_raw)) 175 goto out_free; 176 } 177 178 return 0; 179 180 out_free: 181 perf_evlist__free_stats(evlist); 182 return -1; 183 } 184 185 void perf_evlist__free_stats(struct perf_evlist *evlist) 186 { 187 struct perf_evsel *evsel; 188 189 evlist__for_each_entry(evlist, evsel) { 190 perf_evsel__free_stat_priv(evsel); 191 perf_evsel__free_counts(evsel); 192 perf_evsel__free_prev_raw_counts(evsel); 193 } 194 } 195 196 void perf_evlist__reset_stats(struct perf_evlist *evlist) 197 { 198 struct perf_evsel *evsel; 199 200 evlist__for_each_entry(evlist, evsel) { 201 perf_evsel__reset_stat_priv(evsel); 202 perf_evsel__reset_counts(evsel); 203 } 204 } 205 206 static void zero_per_pkg(struct perf_evsel *counter) 207 { 208 if (counter->per_pkg_mask) 209 memset(counter->per_pkg_mask, 0, MAX_NR_CPUS); 210 } 211 212 static int check_per_pkg(struct perf_evsel *counter, 213 struct perf_counts_values *vals, int cpu, bool *skip) 214 { 215 unsigned long *mask = counter->per_pkg_mask; 216 struct cpu_map *cpus = perf_evsel__cpus(counter); 217 int s; 218 219 *skip = false; 220 221 if (!counter->per_pkg) 222 return 0; 223 224 if (cpu_map__empty(cpus)) 225 return 0; 226 227 if (!mask) { 228 mask = zalloc(MAX_NR_CPUS); 229 if (!mask) 230 return -ENOMEM; 231 232 counter->per_pkg_mask = mask; 233 } 234 235 /* 236 * we do not consider an event that has not run as a good 237 * instance to mark a package as used (skip=1). Otherwise 238 * we may run into a situation where the first CPU in a package 239 * is not running anything, yet the second is, and this function 240 * would mark the package as used after the first CPU and would 241 * not read the values from the second CPU. 242 */ 243 if (!(vals->run && vals->ena)) 244 return 0; 245 246 s = cpu_map__get_socket(cpus, cpu, NULL); 247 if (s < 0) 248 return -1; 249 250 *skip = test_and_set_bit(s, mask) == 1; 251 return 0; 252 } 253 254 static int 255 process_counter_values(struct perf_stat_config *config, struct perf_evsel *evsel, 256 int cpu, int thread, 257 struct perf_counts_values *count) 258 { 259 struct perf_counts_values *aggr = &evsel->counts->aggr; 260 static struct perf_counts_values zero; 261 bool skip = false; 262 263 if (check_per_pkg(evsel, count, cpu, &skip)) { 264 pr_err("failed to read per-pkg counter\n"); 265 return -1; 266 } 267 268 if (skip) 269 count = &zero; 270 271 switch (config->aggr_mode) { 272 case AGGR_THREAD: 273 case AGGR_CORE: 274 case AGGR_SOCKET: 275 case AGGR_NONE: 276 if (!evsel->snapshot) 277 perf_evsel__compute_deltas(evsel, cpu, thread, count); 278 perf_counts_values__scale(count, config->scale, NULL); 279 if (config->aggr_mode == AGGR_NONE) 280 perf_stat__update_shadow_stats(evsel, count->values, cpu); 281 break; 282 case AGGR_GLOBAL: 283 aggr->val += count->val; 284 if (config->scale) { 285 aggr->ena += count->ena; 286 aggr->run += count->run; 287 } 288 case AGGR_UNSET: 289 default: 290 break; 291 } 292 293 return 0; 294 } 295 296 static int process_counter_maps(struct perf_stat_config *config, 297 struct perf_evsel *counter) 298 { 299 int nthreads = thread_map__nr(counter->threads); 300 int ncpus = perf_evsel__nr_cpus(counter); 301 int cpu, thread; 302 303 if (counter->system_wide) 304 nthreads = 1; 305 306 for (thread = 0; thread < nthreads; thread++) { 307 for (cpu = 0; cpu < ncpus; cpu++) { 308 if (process_counter_values(config, counter, cpu, thread, 309 perf_counts(counter->counts, cpu, thread))) 310 return -1; 311 } 312 } 313 314 return 0; 315 } 316 317 int perf_stat_process_counter(struct perf_stat_config *config, 318 struct perf_evsel *counter) 319 { 320 struct perf_counts_values *aggr = &counter->counts->aggr; 321 struct perf_stat_evsel *ps = counter->priv; 322 u64 *count = counter->counts->aggr.values; 323 u64 val; 324 int i, ret; 325 326 aggr->val = aggr->ena = aggr->run = 0; 327 328 /* 329 * We calculate counter's data every interval, 330 * and the display code shows ps->res_stats 331 * avg value. We need to zero the stats for 332 * interval mode, otherwise overall avg running 333 * averages will be shown for each interval. 334 */ 335 if (config->interval) 336 init_stats(ps->res_stats); 337 338 if (counter->per_pkg) 339 zero_per_pkg(counter); 340 341 ret = process_counter_maps(config, counter); 342 if (ret) 343 return ret; 344 345 if (config->aggr_mode != AGGR_GLOBAL) 346 return 0; 347 348 if (!counter->snapshot) 349 perf_evsel__compute_deltas(counter, -1, -1, aggr); 350 perf_counts_values__scale(aggr, config->scale, &counter->counts->scaled); 351 352 for (i = 0; i < 3; i++) 353 update_stats(&ps->res_stats[i], count[i]); 354 355 if (verbose > 0) { 356 fprintf(config->output, "%s: %" PRIu64 " %" PRIu64 " %" PRIu64 "\n", 357 perf_evsel__name(counter), count[0], count[1], count[2]); 358 } 359 360 /* 361 * Save the full runtime - to allow normalization during printout: 362 */ 363 val = counter->scale * *count; 364 perf_stat__update_shadow_stats(counter, &val, 0); 365 366 return 0; 367 } 368 369 int perf_event__process_stat_event(struct perf_tool *tool __maybe_unused, 370 union perf_event *event, 371 struct perf_session *session) 372 { 373 struct perf_counts_values count; 374 struct stat_event *st = &event->stat; 375 struct perf_evsel *counter; 376 377 count.val = st->val; 378 count.ena = st->ena; 379 count.run = st->run; 380 381 counter = perf_evlist__id2evsel(session->evlist, st->id); 382 if (!counter) { 383 pr_err("Failed to resolve counter for stat event.\n"); 384 return -EINVAL; 385 } 386 387 *perf_counts(counter->counts, st->cpu, st->thread) = count; 388 counter->supported = true; 389 return 0; 390 } 391 392 size_t perf_event__fprintf_stat(union perf_event *event, FILE *fp) 393 { 394 struct stat_event *st = (struct stat_event *) event; 395 size_t ret; 396 397 ret = fprintf(fp, "\n... id %" PRIu64 ", cpu %d, thread %d\n", 398 st->id, st->cpu, st->thread); 399 ret += fprintf(fp, "... value %" PRIu64 ", enabled %" PRIu64 ", running %" PRIu64 "\n", 400 st->val, st->ena, st->run); 401 402 return ret; 403 } 404 405 size_t perf_event__fprintf_stat_round(union perf_event *event, FILE *fp) 406 { 407 struct stat_round_event *rd = (struct stat_round_event *)event; 408 size_t ret; 409 410 ret = fprintf(fp, "\n... time %" PRIu64 ", type %s\n", rd->time, 411 rd->type == PERF_STAT_ROUND_TYPE__FINAL ? "FINAL" : "INTERVAL"); 412 413 return ret; 414 } 415 416 size_t perf_event__fprintf_stat_config(union perf_event *event, FILE *fp) 417 { 418 struct perf_stat_config sc; 419 size_t ret; 420 421 perf_event__read_stat_config(&sc, &event->stat_config); 422 423 ret = fprintf(fp, "\n"); 424 ret += fprintf(fp, "... aggr_mode %d\n", sc.aggr_mode); 425 ret += fprintf(fp, "... scale %d\n", sc.scale); 426 ret += fprintf(fp, "... interval %u\n", sc.interval); 427 428 return ret; 429 } 430