Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
cb94a02e | 2 | #include <math.h> |
f87027b9 JO |
3 | #include <stdio.h> |
4 | #include "evsel.h" | |
5 | #include "stat.h" | |
6 | #include "color.h" | |
cb94a02e | 7 | #include "debug.h" |
fb4605ba | 8 | #include "pmu.h" |
37932c18 AK |
9 | #include "rblist.h" |
10 | #include "evlist.h" | |
11 | #include "expr.h" | |
b18f3e36 | 12 | #include "metricgroup.h" |
a1bf2305 | 13 | #include "cgroup.h" |
6859bc0e | 14 | #include "units.h" |
d8f9da24 | 15 | #include <linux/zalloc.h> |
f07952b1 | 16 | #include "iostat.h" |
bd560973 | 17 | #include "util/hashmap.h" |
f87027b9 | 18 | |
44d49a60 AK |
19 | /* |
20 | * AGGR_GLOBAL: Use CPU 0 | |
21 | * AGGR_SOCKET: Use first CPU of socket | |
db5742b6 | 22 | * AGGR_DIE: Use first CPU of die |
44d49a60 AK |
23 | * AGGR_CORE: Use first CPU of core |
24 | * AGGR_NONE: Use matching CPU | |
25 | * AGGR_THREAD: Not supported? | |
26 | */ | |
f87027b9 JO |
27 | |
28 | struct stats walltime_nsecs_stats; | |
c735b0a5 | 29 | struct rusage_stats ru_stats; |
f87027b9 | 30 | |
cc26ffaa IR |
31 | static struct runtime_stat { |
32 | struct rblist value_list; | |
33 | } rt_stat; | |
34 | ||
758bc8e6 IR |
35 | enum { |
36 | CTX_BIT_USER = 1 << 0, | |
37 | CTX_BIT_KERNEL = 1 << 1, | |
38 | CTX_BIT_HV = 1 << 2, | |
39 | CTX_BIT_HOST = 1 << 3, | |
40 | CTX_BIT_IDLE = 1 << 4, | |
41 | CTX_BIT_MAX = 1 << 5, | |
42 | }; | |
43 | ||
44 | enum stat_type { | |
45 | STAT_NONE = 0, | |
46 | STAT_NSECS, | |
47 | STAT_CYCLES, | |
48 | STAT_STALLED_CYCLES_FRONT, | |
49 | STAT_STALLED_CYCLES_BACK, | |
50 | STAT_BRANCHES, | |
51 | STAT_CACHEREFS, | |
52 | STAT_L1_DCACHE, | |
53 | STAT_L1_ICACHE, | |
54 | STAT_LL_CACHE, | |
55 | STAT_ITLB_CACHE, | |
56 | STAT_DTLB_CACHE, | |
57 | STAT_MAX | |
58 | }; | |
59 | ||
37932c18 AK |
60 | struct saved_value { |
61 | struct rb_node rb_node; | |
32dcd021 | 62 | struct evsel *evsel; |
49cd456a JY |
63 | enum stat_type type; |
64 | int ctx; | |
87ae87fd | 65 | int map_idx; /* cpu or thread map index */ |
a1bf2305 | 66 | struct cgroup *cgrp; |
37932c18 | 67 | struct stats stats; |
f01642e4 JY |
68 | u64 metric_total; |
69 | int metric_other; | |
37932c18 AK |
70 | }; |
71 | ||
72 | static int saved_value_cmp(struct rb_node *rb_node, const void *entry) | |
73 | { | |
74 | struct saved_value *a = container_of(rb_node, | |
75 | struct saved_value, | |
76 | rb_node); | |
77 | const struct saved_value *b = entry; | |
78 | ||
dfca2d69 NK |
79 | if (a->map_idx != b->map_idx) |
80 | return a->map_idx - b->map_idx; | |
49cd456a JY |
81 | |
82 | /* | |
83 | * Previously the rbtree was used to link generic metrics. | |
84 | * The keys were evsel/cpu. Now the rbtree is extended to support | |
85 | * per-thread shadow stats. For shadow stats case, the keys | |
86 | * are cpu/type/ctx/stat (evsel is NULL). For generic metrics | |
87 | * case, the keys are still evsel/cpu (type/ctx/stat are 0 or NULL). | |
88 | */ | |
89 | if (a->type != b->type) | |
90 | return a->type - b->type; | |
91 | ||
92 | if (a->ctx != b->ctx) | |
93 | return a->ctx - b->ctx; | |
94 | ||
a1bf2305 NK |
95 | if (a->cgrp != b->cgrp) |
96 | return (char *)a->cgrp < (char *)b->cgrp ? -1 : +1; | |
97 | ||
5e97665f AK |
98 | if (a->evsel == b->evsel) |
99 | return 0; | |
100 | if ((char *)a->evsel < (char *)b->evsel) | |
101 | return -1; | |
102 | return +1; | |
37932c18 AK |
103 | } |
104 | ||
105 | static struct rb_node *saved_value_new(struct rblist *rblist __maybe_unused, | |
106 | const void *entry) | |
107 | { | |
108 | struct saved_value *nd = malloc(sizeof(struct saved_value)); | |
109 | ||
110 | if (!nd) | |
111 | return NULL; | |
112 | memcpy(nd, entry, sizeof(struct saved_value)); | |
113 | return &nd->rb_node; | |
114 | } | |
115 | ||
b984aff7 JY |
116 | static void saved_value_delete(struct rblist *rblist __maybe_unused, |
117 | struct rb_node *rb_node) | |
118 | { | |
119 | struct saved_value *v; | |
120 | ||
121 | BUG_ON(!rb_node); | |
122 | v = container_of(rb_node, struct saved_value, rb_node); | |
123 | free(v); | |
124 | } | |
125 | ||
32dcd021 | 126 | static struct saved_value *saved_value_lookup(struct evsel *evsel, |
dfca2d69 | 127 | int map_idx, |
1fcd0394 JY |
128 | bool create, |
129 | enum stat_type type, | |
130 | int ctx, | |
a1bf2305 | 131 | struct cgroup *cgrp) |
37932c18 | 132 | { |
1fcd0394 | 133 | struct rblist *rblist; |
37932c18 AK |
134 | struct rb_node *nd; |
135 | struct saved_value dm = { | |
dfca2d69 | 136 | .map_idx = map_idx, |
37932c18 | 137 | .evsel = evsel, |
1fcd0394 JY |
138 | .type = type, |
139 | .ctx = ctx, | |
a1bf2305 | 140 | .cgrp = cgrp, |
37932c18 | 141 | }; |
1fcd0394 | 142 | |
cc26ffaa | 143 | rblist = &rt_stat.value_list; |
1fcd0394 | 144 | |
3ff1e718 NK |
145 | /* don't use context info for clock events */ |
146 | if (type == STAT_NSECS) | |
147 | dm.ctx = 0; | |
148 | ||
1fcd0394 | 149 | nd = rblist__find(rblist, &dm); |
37932c18 AK |
150 | if (nd) |
151 | return container_of(nd, struct saved_value, rb_node); | |
152 | if (create) { | |
1fcd0394 JY |
153 | rblist__add_node(rblist, &dm); |
154 | nd = rblist__find(rblist, &dm); | |
37932c18 AK |
155 | if (nd) |
156 | return container_of(nd, struct saved_value, rb_node); | |
157 | } | |
158 | return NULL; | |
159 | } | |
160 | ||
cc26ffaa | 161 | void perf_stat__init_shadow_stats(void) |
8efb2df1 | 162 | { |
cc26ffaa | 163 | struct rblist *rblist = &rt_stat.value_list; |
8efb2df1 JY |
164 | |
165 | rblist__init(rblist); | |
166 | rblist->node_cmp = saved_value_cmp; | |
167 | rblist->node_new = saved_value_new; | |
168 | rblist->node_delete = saved_value_delete; | |
169 | } | |
170 | ||
32dcd021 | 171 | static int evsel_context(struct evsel *evsel) |
f87027b9 JO |
172 | { |
173 | int ctx = 0; | |
174 | ||
1fc632ce | 175 | if (evsel->core.attr.exclude_kernel) |
f87027b9 | 176 | ctx |= CTX_BIT_KERNEL; |
1fc632ce | 177 | if (evsel->core.attr.exclude_user) |
f87027b9 | 178 | ctx |= CTX_BIT_USER; |
1fc632ce | 179 | if (evsel->core.attr.exclude_hv) |
f87027b9 | 180 | ctx |= CTX_BIT_HV; |
1fc632ce | 181 | if (evsel->core.attr.exclude_host) |
f87027b9 | 182 | ctx |= CTX_BIT_HOST; |
1fc632ce | 183 | if (evsel->core.attr.exclude_idle) |
f87027b9 JO |
184 | ctx |= CTX_BIT_IDLE; |
185 | ||
186 | return ctx; | |
187 | } | |
188 | ||
cc26ffaa | 189 | void perf_stat__reset_shadow_per_stat(void) |
f87027b9 | 190 | { |
6a1e2c5c | 191 | struct rblist *rblist; |
37932c18 AK |
192 | struct rb_node *pos, *next; |
193 | ||
cc26ffaa | 194 | rblist = &rt_stat.value_list; |
ca227029 | 195 | next = rb_first_cached(&rblist->entries); |
37932c18 AK |
196 | while (next) { |
197 | pos = next; | |
198 | next = rb_next(pos); | |
199 | memset(&container_of(pos, struct saved_value, rb_node)->stats, | |
200 | 0, | |
201 | sizeof(struct stats)); | |
202 | } | |
f87027b9 JO |
203 | } |
204 | ||
6a1e2c5c JY |
205 | void perf_stat__reset_shadow_stats(void) |
206 | { | |
cc26ffaa | 207 | perf_stat__reset_shadow_per_stat(); |
6a1e2c5c | 208 | memset(&walltime_nsecs_stats, 0, sizeof(walltime_nsecs_stats)); |
c735b0a5 | 209 | memset(&ru_stats, 0, sizeof(ru_stats)); |
6a1e2c5c JY |
210 | } |
211 | ||
3ff1e718 NK |
212 | struct runtime_stat_data { |
213 | int ctx; | |
a1bf2305 | 214 | struct cgroup *cgrp; |
3ff1e718 NK |
215 | }; |
216 | ||
cc26ffaa | 217 | static void update_runtime_stat(enum stat_type type, |
dfca2d69 | 218 | int map_idx, u64 count, |
3ff1e718 | 219 | struct runtime_stat_data *rsd) |
1fcd0394 | 220 | { |
dfca2d69 | 221 | struct saved_value *v = saved_value_lookup(NULL, map_idx, true, type, |
cc26ffaa | 222 | rsd->ctx, rsd->cgrp); |
1fcd0394 JY |
223 | |
224 | if (v) | |
225 | update_stats(&v->stats, count); | |
226 | } | |
227 | ||
f87027b9 JO |
228 | /* |
229 | * Update various tracking values we maintain to print | |
230 | * more semantic information such as miss/hit ratios, | |
231 | * instruction rates, etc: | |
232 | */ | |
32dcd021 | 233 | void perf_stat__update_shadow_stats(struct evsel *counter, u64 count, |
cc26ffaa | 234 | int map_idx) |
f87027b9 | 235 | { |
57ddf091 | 236 | u64 count_ns = count; |
f01642e4 | 237 | struct saved_value *v; |
3ff1e718 NK |
238 | struct runtime_stat_data rsd = { |
239 | .ctx = evsel_context(counter), | |
a1bf2305 | 240 | .cgrp = counter->cgrp, |
3ff1e718 | 241 | }; |
54830dd0 JO |
242 | count *= counter->scale; |
243 | ||
c754c382 | 244 | if (evsel__is_clock(counter)) |
cc26ffaa | 245 | update_runtime_stat(STAT_NSECS, map_idx, count_ns, &rsd); |
c754c382 | 246 | else if (evsel__match(counter, HARDWARE, HW_CPU_CYCLES)) |
cc26ffaa | 247 | update_runtime_stat(STAT_CYCLES, map_idx, count, &rsd); |
c754c382 | 248 | else if (evsel__match(counter, HARDWARE, HW_STALLED_CYCLES_FRONTEND)) |
cc26ffaa | 249 | update_runtime_stat(STAT_STALLED_CYCLES_FRONT, |
dfca2d69 | 250 | map_idx, count, &rsd); |
c754c382 | 251 | else if (evsel__match(counter, HARDWARE, HW_STALLED_CYCLES_BACKEND)) |
cc26ffaa | 252 | update_runtime_stat(STAT_STALLED_CYCLES_BACK, |
dfca2d69 | 253 | map_idx, count, &rsd); |
c754c382 | 254 | else if (evsel__match(counter, HARDWARE, HW_BRANCH_INSTRUCTIONS)) |
cc26ffaa | 255 | update_runtime_stat(STAT_BRANCHES, map_idx, count, &rsd); |
c754c382 | 256 | else if (evsel__match(counter, HARDWARE, HW_CACHE_REFERENCES)) |
cc26ffaa | 257 | update_runtime_stat(STAT_CACHEREFS, map_idx, count, &rsd); |
c754c382 | 258 | else if (evsel__match(counter, HW_CACHE, HW_CACHE_L1D)) |
cc26ffaa | 259 | update_runtime_stat(STAT_L1_DCACHE, map_idx, count, &rsd); |
c754c382 | 260 | else if (evsel__match(counter, HW_CACHE, HW_CACHE_L1I)) |
cc26ffaa | 261 | update_runtime_stat(STAT_L1_ICACHE, map_idx, count, &rsd); |
c754c382 | 262 | else if (evsel__match(counter, HW_CACHE, HW_CACHE_LL)) |
cc26ffaa | 263 | update_runtime_stat(STAT_LL_CACHE, map_idx, count, &rsd); |
c754c382 | 264 | else if (evsel__match(counter, HW_CACHE, HW_CACHE_DTLB)) |
cc26ffaa | 265 | update_runtime_stat(STAT_DTLB_CACHE, map_idx, count, &rsd); |
c754c382 | 266 | else if (evsel__match(counter, HW_CACHE, HW_CACHE_ITLB)) |
cc26ffaa | 267 | update_runtime_stat(STAT_ITLB_CACHE, map_idx, count, &rsd); |
37932c18 AK |
268 | |
269 | if (counter->collect_stat) { | |
cc26ffaa | 270 | v = saved_value_lookup(counter, map_idx, true, STAT_NONE, 0, |
a1bf2305 | 271 | rsd.cgrp); |
54830dd0 | 272 | update_stats(&v->stats, count); |
f01642e4 JY |
273 | if (counter->metric_leader) |
274 | v->metric_total += count; | |
37f322cd | 275 | } else if (counter->metric_leader && !counter->merged_stat) { |
f01642e4 | 276 | v = saved_value_lookup(counter->metric_leader, |
cc26ffaa | 277 | map_idx, true, STAT_NONE, 0, rsd.cgrp); |
f01642e4 JY |
278 | v->metric_total += count; |
279 | v->metric_other++; | |
37932c18 | 280 | } |
f87027b9 JO |
281 | } |
282 | ||
283 | /* used for get_ratio_color() */ | |
284 | enum grc_type { | |
285 | GRC_STALLED_CYCLES_FE, | |
286 | GRC_STALLED_CYCLES_BE, | |
287 | GRC_CACHE_MISSES, | |
288 | GRC_MAX_NR | |
289 | }; | |
290 | ||
291 | static const char *get_ratio_color(enum grc_type type, double ratio) | |
292 | { | |
293 | static const double grc_table[GRC_MAX_NR][3] = { | |
294 | [GRC_STALLED_CYCLES_FE] = { 50.0, 30.0, 10.0 }, | |
295 | [GRC_STALLED_CYCLES_BE] = { 75.0, 50.0, 20.0 }, | |
296 | [GRC_CACHE_MISSES] = { 20.0, 10.0, 5.0 }, | |
297 | }; | |
298 | const char *color = PERF_COLOR_NORMAL; | |
299 | ||
300 | if (ratio > grc_table[type][0]) | |
301 | color = PERF_COLOR_RED; | |
302 | else if (ratio > grc_table[type][1]) | |
303 | color = PERF_COLOR_MAGENTA; | |
304 | else if (ratio > grc_table[type][2]) | |
305 | color = PERF_COLOR_YELLOW; | |
306 | ||
307 | return color; | |
308 | } | |
309 | ||
cc26ffaa | 310 | static double runtime_stat_avg(enum stat_type type, int map_idx, |
3ff1e718 | 311 | struct runtime_stat_data *rsd) |
e0128b30 JY |
312 | { |
313 | struct saved_value *v; | |
314 | ||
cc26ffaa | 315 | v = saved_value_lookup(NULL, map_idx, false, type, rsd->ctx, rsd->cgrp); |
e0128b30 JY |
316 | if (!v) |
317 | return 0.0; | |
318 | ||
319 | return avg_stats(&v->stats); | |
320 | } | |
321 | ||
cc26ffaa | 322 | static double runtime_stat_n(enum stat_type type, int map_idx, |
3ff1e718 | 323 | struct runtime_stat_data *rsd) |
e0128b30 JY |
324 | { |
325 | struct saved_value *v; | |
326 | ||
cc26ffaa | 327 | v = saved_value_lookup(NULL, map_idx, false, type, rsd->ctx, rsd->cgrp); |
e0128b30 JY |
328 | if (!v) |
329 | return 0.0; | |
330 | ||
331 | return v->stats.n; | |
332 | } | |
333 | ||
6ca9a082 | 334 | static void print_stalled_cycles_frontend(struct perf_stat_config *config, |
dfca2d69 | 335 | int map_idx, double avg, |
e0128b30 | 336 | struct perf_stat_output_ctx *out, |
3ff1e718 | 337 | struct runtime_stat_data *rsd) |
f87027b9 JO |
338 | { |
339 | double total, ratio = 0.0; | |
340 | const char *color; | |
f87027b9 | 341 | |
cc26ffaa | 342 | total = runtime_stat_avg(STAT_CYCLES, map_idx, rsd); |
f87027b9 JO |
343 | |
344 | if (total) | |
345 | ratio = avg / total * 100.0; | |
346 | ||
347 | color = get_ratio_color(GRC_STALLED_CYCLES_FE, ratio); | |
348 | ||
140aeadc | 349 | if (ratio) |
6ca9a082 | 350 | out->print_metric(config, out->ctx, color, "%7.2f%%", "frontend cycles idle", |
140aeadc AK |
351 | ratio); |
352 | else | |
6ca9a082 | 353 | out->print_metric(config, out->ctx, NULL, NULL, "frontend cycles idle", 0); |
f87027b9 JO |
354 | } |
355 | ||
6ca9a082 | 356 | static void print_stalled_cycles_backend(struct perf_stat_config *config, |
dfca2d69 | 357 | int map_idx, double avg, |
e0128b30 | 358 | struct perf_stat_output_ctx *out, |
3ff1e718 | 359 | struct runtime_stat_data *rsd) |
f87027b9 JO |
360 | { |
361 | double total, ratio = 0.0; | |
362 | const char *color; | |
f87027b9 | 363 | |
cc26ffaa | 364 | total = runtime_stat_avg(STAT_CYCLES, map_idx, rsd); |
f87027b9 JO |
365 | |
366 | if (total) | |
367 | ratio = avg / total * 100.0; | |
368 | ||
369 | color = get_ratio_color(GRC_STALLED_CYCLES_BE, ratio); | |
370 | ||
6ca9a082 | 371 | out->print_metric(config, out->ctx, color, "%7.2f%%", "backend cycles idle", ratio); |
f87027b9 JO |
372 | } |
373 | ||
6ca9a082 | 374 | static void print_branch_misses(struct perf_stat_config *config, |
dfca2d69 | 375 | int map_idx, double avg, |
e0128b30 | 376 | struct perf_stat_output_ctx *out, |
3ff1e718 | 377 | struct runtime_stat_data *rsd) |
f87027b9 JO |
378 | { |
379 | double total, ratio = 0.0; | |
380 | const char *color; | |
f87027b9 | 381 | |
cc26ffaa | 382 | total = runtime_stat_avg(STAT_BRANCHES, map_idx, rsd); |
f87027b9 JO |
383 | |
384 | if (total) | |
385 | ratio = avg / total * 100.0; | |
386 | ||
387 | color = get_ratio_color(GRC_CACHE_MISSES, ratio); | |
388 | ||
6ca9a082 | 389 | out->print_metric(config, out->ctx, color, "%7.2f%%", "of all branches", ratio); |
f87027b9 JO |
390 | } |
391 | ||
6ca9a082 | 392 | static void print_l1_dcache_misses(struct perf_stat_config *config, |
dfca2d69 | 393 | int map_idx, double avg, |
e0128b30 | 394 | struct perf_stat_output_ctx *out, |
3ff1e718 | 395 | struct runtime_stat_data *rsd) |
f87027b9 JO |
396 | { |
397 | double total, ratio = 0.0; | |
398 | const char *color; | |
f87027b9 | 399 | |
cc26ffaa | 400 | total = runtime_stat_avg(STAT_L1_DCACHE, map_idx, rsd); |
f87027b9 JO |
401 | |
402 | if (total) | |
403 | ratio = avg / total * 100.0; | |
404 | ||
405 | color = get_ratio_color(GRC_CACHE_MISSES, ratio); | |
406 | ||
ce9c13f3 | 407 | out->print_metric(config, out->ctx, color, "%7.2f%%", "of all L1-dcache accesses", ratio); |
f87027b9 JO |
408 | } |
409 | ||
6ca9a082 | 410 | static void print_l1_icache_misses(struct perf_stat_config *config, |
dfca2d69 | 411 | int map_idx, double avg, |
e0128b30 | 412 | struct perf_stat_output_ctx *out, |
3ff1e718 | 413 | struct runtime_stat_data *rsd) |
f87027b9 JO |
414 | { |
415 | double total, ratio = 0.0; | |
416 | const char *color; | |
f87027b9 | 417 | |
cc26ffaa | 418 | total = runtime_stat_avg(STAT_L1_ICACHE, map_idx, rsd); |
f87027b9 JO |
419 | |
420 | if (total) | |
421 | ratio = avg / total * 100.0; | |
422 | ||
423 | color = get_ratio_color(GRC_CACHE_MISSES, ratio); | |
ce9c13f3 | 424 | out->print_metric(config, out->ctx, color, "%7.2f%%", "of all L1-icache accesses", ratio); |
f87027b9 JO |
425 | } |
426 | ||
6ca9a082 | 427 | static void print_dtlb_cache_misses(struct perf_stat_config *config, |
dfca2d69 | 428 | int map_idx, double avg, |
e0128b30 | 429 | struct perf_stat_output_ctx *out, |
3ff1e718 | 430 | struct runtime_stat_data *rsd) |
f87027b9 JO |
431 | { |
432 | double total, ratio = 0.0; | |
433 | const char *color; | |
f87027b9 | 434 | |
cc26ffaa | 435 | total = runtime_stat_avg(STAT_DTLB_CACHE, map_idx, rsd); |
f87027b9 JO |
436 | |
437 | if (total) | |
438 | ratio = avg / total * 100.0; | |
439 | ||
440 | color = get_ratio_color(GRC_CACHE_MISSES, ratio); | |
ce9c13f3 | 441 | out->print_metric(config, out->ctx, color, "%7.2f%%", "of all dTLB cache accesses", ratio); |
f87027b9 JO |
442 | } |
443 | ||
6ca9a082 | 444 | static void print_itlb_cache_misses(struct perf_stat_config *config, |
dfca2d69 | 445 | int map_idx, double avg, |
e0128b30 | 446 | struct perf_stat_output_ctx *out, |
3ff1e718 | 447 | struct runtime_stat_data *rsd) |
f87027b9 JO |
448 | { |
449 | double total, ratio = 0.0; | |
450 | const char *color; | |
f87027b9 | 451 | |
cc26ffaa | 452 | total = runtime_stat_avg(STAT_ITLB_CACHE, map_idx, rsd); |
f87027b9 JO |
453 | |
454 | if (total) | |
455 | ratio = avg / total * 100.0; | |
456 | ||
457 | color = get_ratio_color(GRC_CACHE_MISSES, ratio); | |
ce9c13f3 | 458 | out->print_metric(config, out->ctx, color, "%7.2f%%", "of all iTLB cache accesses", ratio); |
f87027b9 JO |
459 | } |
460 | ||
6ca9a082 | 461 | static void print_ll_cache_misses(struct perf_stat_config *config, |
dfca2d69 | 462 | int map_idx, double avg, |
e0128b30 | 463 | struct perf_stat_output_ctx *out, |
3ff1e718 | 464 | struct runtime_stat_data *rsd) |
f87027b9 JO |
465 | { |
466 | double total, ratio = 0.0; | |
467 | const char *color; | |
f87027b9 | 468 | |
cc26ffaa | 469 | total = runtime_stat_avg(STAT_LL_CACHE, map_idx, rsd); |
f87027b9 JO |
470 | |
471 | if (total) | |
472 | ratio = avg / total * 100.0; | |
473 | ||
474 | color = get_ratio_color(GRC_CACHE_MISSES, ratio); | |
ce9c13f3 | 475 | out->print_metric(config, out->ctx, color, "%7.2f%%", "of all LL-cache accesses", ratio); |
f87027b9 JO |
476 | } |
477 | ||
2cfaa853 | 478 | static int prepare_metric(struct evsel **metric_events, |
fc393839 | 479 | struct metric_ref *metric_refs, |
2cfaa853 | 480 | struct expr_parse_ctx *pctx, |
cc26ffaa | 481 | int map_idx) |
bba49af8 | 482 | { |
2cfaa853 | 483 | double scale; |
ec5c5b3d | 484 | char *n; |
fc393839 | 485 | int i, j, ret; |
bba49af8 | 486 | |
bba49af8 AK |
487 | for (i = 0; metric_events[i]; i++) { |
488 | struct saved_value *v; | |
fd48aad9 | 489 | struct stats *stats; |
f01642e4 | 490 | u64 metric_total = 0; |
9aba0ada | 491 | int source_count; |
fd48aad9 | 492 | |
9aa09230 | 493 | if (evsel__is_tool(metric_events[i])) { |
9aba0ada | 494 | source_count = 1; |
9aa09230 IR |
495 | switch (metric_events[i]->tool_event) { |
496 | case PERF_TOOL_DURATION_TIME: | |
497 | stats = &walltime_nsecs_stats; | |
498 | scale = 1e-9; | |
499 | break; | |
500 | case PERF_TOOL_USER_TIME: | |
501 | stats = &ru_stats.ru_utime_usec_stat; | |
502 | scale = 1e-6; | |
503 | break; | |
504 | case PERF_TOOL_SYSTEM_TIME: | |
505 | stats = &ru_stats.ru_stime_usec_stat; | |
506 | scale = 1e-6; | |
507 | break; | |
508 | case PERF_TOOL_NONE: | |
509 | pr_err("Invalid tool event 'none'"); | |
510 | abort(); | |
511 | case PERF_TOOL_MAX: | |
512 | pr_err("Invalid tool event 'max'"); | |
513 | abort(); | |
514 | default: | |
515 | pr_err("Unknown tool event '%s'", evsel__name(metric_events[i])); | |
516 | abort(); | |
517 | } | |
fd48aad9 | 518 | } else { |
dfca2d69 | 519 | v = saved_value_lookup(metric_events[i], map_idx, false, |
cc26ffaa | 520 | STAT_NONE, 0, |
a1bf2305 | 521 | metric_events[i]->cgrp); |
fd48aad9 AK |
522 | if (!v) |
523 | break; | |
524 | stats = &v->stats; | |
8cff7490 IR |
525 | /* |
526 | * If an event was scaled during stat gathering, reverse | |
527 | * the scale before computing the metric. | |
528 | */ | |
529 | scale = 1.0 / metric_events[i]->scale; | |
530 | ||
9aba0ada | 531 | source_count = evsel__source_count(metric_events[i]); |
f01642e4 JY |
532 | |
533 | if (v->metric_other) | |
8cff7490 | 534 | metric_total = v->metric_total * scale; |
fd48aad9 | 535 | } |
ec5c5b3d | 536 | n = strdup(evsel__metric_id(metric_events[i])); |
e3a94273 | 537 | if (!n) |
2cfaa853 | 538 | return -ENOMEM; |
ec5c5b3d | 539 | |
9aba0ada IR |
540 | expr__add_id_val_source_count(pctx, n, |
541 | metric_total ? : avg_stats(stats) * scale, | |
542 | source_count); | |
bba49af8 | 543 | } |
f01642e4 | 544 | |
fc393839 JO |
545 | for (j = 0; metric_refs && metric_refs[j].metric_name; j++) { |
546 | ret = expr__add_ref(pctx, &metric_refs[j]); | |
547 | if (ret) | |
548 | return ret; | |
549 | } | |
550 | ||
2cfaa853 JO |
551 | return i; |
552 | } | |
553 | ||
554 | static void generic_metric(struct perf_stat_config *config, | |
555 | const char *metric_expr, | |
d0a3052f | 556 | const char *metric_threshold, |
2cfaa853 | 557 | struct evsel **metric_events, |
fc393839 | 558 | struct metric_ref *metric_refs, |
2cfaa853 JO |
559 | char *name, |
560 | const char *metric_name, | |
561 | const char *metric_unit, | |
562 | int runtime, | |
dfca2d69 | 563 | int map_idx, |
cc26ffaa | 564 | struct perf_stat_output_ctx *out) |
2cfaa853 JO |
565 | { |
566 | print_metric_t print_metric = out->print_metric; | |
cb94a02e | 567 | struct expr_parse_ctx *pctx; |
d0a3052f | 568 | double ratio, scale, threshold; |
2cfaa853 JO |
569 | int i; |
570 | void *ctxp = out->ctx; | |
d0a3052f | 571 | const char *color = NULL; |
2cfaa853 | 572 | |
cb94a02e IR |
573 | pctx = expr__ctx_new(); |
574 | if (!pctx) | |
2cfaa853 JO |
575 | return; |
576 | ||
1725e9cd IR |
577 | if (config->user_requested_cpu_list) |
578 | pctx->sctx.user_requested_cpu_list = strdup(config->user_requested_cpu_list); | |
1a6abdde | 579 | pctx->sctx.runtime = runtime; |
1725e9cd | 580 | pctx->sctx.system_wide = config->system_wide; |
cc26ffaa | 581 | i = prepare_metric(metric_events, metric_refs, pctx, map_idx); |
cb94a02e IR |
582 | if (i < 0) { |
583 | expr__ctx_free(pctx); | |
584 | return; | |
585 | } | |
bba49af8 | 586 | if (!metric_events[i]) { |
fa831fbb | 587 | if (expr__parse(&ratio, pctx, metric_expr) == 0) { |
287f2649 JY |
588 | char *unit; |
589 | char metric_bf[64]; | |
590 | ||
d0a3052f | 591 | if (metric_threshold && |
1fd09e29 IR |
592 | expr__parse(&threshold, pctx, metric_threshold) == 0 && |
593 | !isnan(threshold)) { | |
d0a3052f IR |
594 | color = fpclassify(threshold) == FP_ZERO |
595 | ? PERF_COLOR_GREEN : PERF_COLOR_RED; | |
596 | } | |
597 | ||
287f2649 JY |
598 | if (metric_unit && metric_name) { |
599 | if (perf_pmu__convert_scale(metric_unit, | |
600 | &unit, &scale) >= 0) { | |
601 | ratio *= scale; | |
602 | } | |
1e1a873d KJ |
603 | if (strstr(metric_expr, "?")) |
604 | scnprintf(metric_bf, sizeof(metric_bf), | |
605 | "%s %s_%d", unit, metric_name, runtime); | |
606 | else | |
607 | scnprintf(metric_bf, sizeof(metric_bf), | |
287f2649 | 608 | "%s %s", unit, metric_name); |
1e1a873d | 609 | |
d0a3052f | 610 | print_metric(config, ctxp, color, "%8.1f", |
287f2649 JY |
611 | metric_bf, ratio); |
612 | } else { | |
d0a3052f | 613 | print_metric(config, ctxp, color, "%8.2f", |
287f2649 JY |
614 | metric_name ? |
615 | metric_name : | |
616 | out->force_header ? name : "", | |
617 | ratio); | |
618 | } | |
619 | } else { | |
d0a3052f | 620 | print_metric(config, ctxp, color, /*unit=*/NULL, |
4ed962eb AK |
621 | out->force_header ? |
622 | (metric_name ? metric_name : name) : "", 0); | |
287f2649 | 623 | } |
8358f698 | 624 | } else { |
d0a3052f | 625 | print_metric(config, ctxp, color, /*unit=*/NULL, |
8358f698 JY |
626 | out->force_header ? |
627 | (metric_name ? metric_name : name) : "", 0); | |
628 | } | |
e3a94273 | 629 | |
cb94a02e | 630 | expr__ctx_free(pctx); |
bba49af8 AK |
631 | } |
632 | ||
cc26ffaa | 633 | double test_generic_metric(struct metric_expr *mexp, int map_idx) |
6d432c4c | 634 | { |
cb94a02e | 635 | struct expr_parse_ctx *pctx; |
437822bf | 636 | double ratio = 0.0; |
6d432c4c | 637 | |
cb94a02e IR |
638 | pctx = expr__ctx_new(); |
639 | if (!pctx) | |
640 | return NAN; | |
641 | ||
cc26ffaa | 642 | if (prepare_metric(mexp->metric_events, mexp->metric_refs, pctx, map_idx) < 0) |
437822bf | 643 | goto out; |
6d432c4c | 644 | |
fa831fbb | 645 | if (expr__parse(&ratio, pctx, mexp->metric_expr)) |
437822bf | 646 | ratio = 0.0; |
6d432c4c | 647 | |
437822bf | 648 | out: |
cb94a02e | 649 | expr__ctx_free(pctx); |
6d432c4c JO |
650 | return ratio; |
651 | } | |
652 | ||
6ca9a082 | 653 | void perf_stat__print_shadow_stats(struct perf_stat_config *config, |
32dcd021 | 654 | struct evsel *evsel, |
dfca2d69 | 655 | double avg, int map_idx, |
b18f3e36 | 656 | struct perf_stat_output_ctx *out, |
cc26ffaa | 657 | struct rblist *metric_events) |
f87027b9 | 658 | { |
140aeadc AK |
659 | void *ctxp = out->ctx; |
660 | print_metric_t print_metric = out->print_metric; | |
d6964c5b | 661 | double total, ratio = 0.0; |
3ff1e718 NK |
662 | struct runtime_stat_data rsd = { |
663 | .ctx = evsel_context(evsel), | |
a1bf2305 | 664 | .cgrp = evsel->cgrp, |
3ff1e718 | 665 | }; |
b18f3e36 AK |
666 | struct metric_event *me; |
667 | int num = 1; | |
f87027b9 | 668 | |
f07952b1 AA |
669 | if (config->iostat_run) { |
670 | iostat_print_metric(config, evsel, out); | |
671 | } else if (evsel__match(evsel, HARDWARE, HW_INSTRUCTIONS)) { | |
cc26ffaa | 672 | total = runtime_stat_avg(STAT_CYCLES, map_idx, &rsd); |
e0128b30 | 673 | |
f87027b9 JO |
674 | if (total) { |
675 | ratio = avg / total; | |
6ca9a082 | 676 | print_metric(config, ctxp, NULL, "%7.2f ", |
140aeadc | 677 | "insn per cycle", ratio); |
f87027b9 | 678 | } else { |
6ca9a082 | 679 | print_metric(config, ctxp, NULL, NULL, "insn per cycle", 0); |
f87027b9 | 680 | } |
e0128b30 | 681 | |
cc26ffaa | 682 | total = runtime_stat_avg(STAT_STALLED_CYCLES_FRONT, map_idx, &rsd); |
e0128b30 | 683 | |
cc26ffaa | 684 | total = max(total, runtime_stat_avg(STAT_STALLED_CYCLES_BACK, |
dfca2d69 | 685 | map_idx, &rsd)); |
f87027b9 JO |
686 | |
687 | if (total && avg) { | |
6ca9a082 | 688 | out->new_line(config, ctxp); |
f87027b9 | 689 | ratio = total / avg; |
6ca9a082 | 690 | print_metric(config, ctxp, NULL, "%7.2f ", |
140aeadc AK |
691 | "stalled cycles per insn", |
692 | ratio); | |
f87027b9 | 693 | } |
c754c382 | 694 | } else if (evsel__match(evsel, HARDWARE, HW_BRANCH_MISSES)) { |
cc26ffaa IR |
695 | if (runtime_stat_n(STAT_BRANCHES, map_idx, &rsd) != 0) |
696 | print_branch_misses(config, map_idx, avg, out, &rsd); | |
140aeadc | 697 | else |
6ca9a082 | 698 | print_metric(config, ctxp, NULL, NULL, "of all branches", 0); |
f87027b9 | 699 | } else if ( |
1fc632ce JO |
700 | evsel->core.attr.type == PERF_TYPE_HW_CACHE && |
701 | evsel->core.attr.config == ( PERF_COUNT_HW_CACHE_L1D | | |
f87027b9 | 702 | ((PERF_COUNT_HW_CACHE_OP_READ) << 8) | |
140aeadc | 703 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) { |
e0128b30 | 704 | |
cc26ffaa IR |
705 | if (runtime_stat_n(STAT_L1_DCACHE, map_idx, &rsd) != 0) |
706 | print_l1_dcache_misses(config, map_idx, avg, out, &rsd); | |
140aeadc | 707 | else |
ce9c13f3 | 708 | print_metric(config, ctxp, NULL, NULL, "of all L1-dcache accesses", 0); |
f87027b9 | 709 | } else if ( |
1fc632ce JO |
710 | evsel->core.attr.type == PERF_TYPE_HW_CACHE && |
711 | evsel->core.attr.config == ( PERF_COUNT_HW_CACHE_L1I | | |
f87027b9 | 712 | ((PERF_COUNT_HW_CACHE_OP_READ) << 8) | |
140aeadc | 713 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) { |
e0128b30 | 714 | |
cc26ffaa IR |
715 | if (runtime_stat_n(STAT_L1_ICACHE, map_idx, &rsd) != 0) |
716 | print_l1_icache_misses(config, map_idx, avg, out, &rsd); | |
140aeadc | 717 | else |
ce9c13f3 | 718 | print_metric(config, ctxp, NULL, NULL, "of all L1-icache accesses", 0); |
f87027b9 | 719 | } else if ( |
1fc632ce JO |
720 | evsel->core.attr.type == PERF_TYPE_HW_CACHE && |
721 | evsel->core.attr.config == ( PERF_COUNT_HW_CACHE_DTLB | | |
f87027b9 | 722 | ((PERF_COUNT_HW_CACHE_OP_READ) << 8) | |
140aeadc | 723 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) { |
e0128b30 | 724 | |
cc26ffaa IR |
725 | if (runtime_stat_n(STAT_DTLB_CACHE, map_idx, &rsd) != 0) |
726 | print_dtlb_cache_misses(config, map_idx, avg, out, &rsd); | |
140aeadc | 727 | else |
ce9c13f3 | 728 | print_metric(config, ctxp, NULL, NULL, "of all dTLB cache accesses", 0); |
f87027b9 | 729 | } else if ( |
1fc632ce JO |
730 | evsel->core.attr.type == PERF_TYPE_HW_CACHE && |
731 | evsel->core.attr.config == ( PERF_COUNT_HW_CACHE_ITLB | | |
f87027b9 | 732 | ((PERF_COUNT_HW_CACHE_OP_READ) << 8) | |
140aeadc | 733 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) { |
e0128b30 | 734 | |
cc26ffaa IR |
735 | if (runtime_stat_n(STAT_ITLB_CACHE, map_idx, &rsd) != 0) |
736 | print_itlb_cache_misses(config, map_idx, avg, out, &rsd); | |
140aeadc | 737 | else |
ce9c13f3 | 738 | print_metric(config, ctxp, NULL, NULL, "of all iTLB cache accesses", 0); |
f87027b9 | 739 | } else if ( |
1fc632ce JO |
740 | evsel->core.attr.type == PERF_TYPE_HW_CACHE && |
741 | evsel->core.attr.config == ( PERF_COUNT_HW_CACHE_LL | | |
f87027b9 | 742 | ((PERF_COUNT_HW_CACHE_OP_READ) << 8) | |
140aeadc | 743 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) { |
e0128b30 | 744 | |
cc26ffaa IR |
745 | if (runtime_stat_n(STAT_LL_CACHE, map_idx, &rsd) != 0) |
746 | print_ll_cache_misses(config, map_idx, avg, out, &rsd); | |
140aeadc | 747 | else |
ce9c13f3 | 748 | print_metric(config, ctxp, NULL, NULL, "of all LL-cache accesses", 0); |
c754c382 | 749 | } else if (evsel__match(evsel, HARDWARE, HW_CACHE_MISSES)) { |
cc26ffaa | 750 | total = runtime_stat_avg(STAT_CACHEREFS, map_idx, &rsd); |
f87027b9 JO |
751 | |
752 | if (total) | |
753 | ratio = avg * 100 / total; | |
754 | ||
cc26ffaa | 755 | if (runtime_stat_n(STAT_CACHEREFS, map_idx, &rsd) != 0) |
6ca9a082 | 756 | print_metric(config, ctxp, NULL, "%8.3f %%", |
140aeadc AK |
757 | "of all cache refs", ratio); |
758 | else | |
6ca9a082 | 759 | print_metric(config, ctxp, NULL, NULL, "of all cache refs", 0); |
c754c382 | 760 | } else if (evsel__match(evsel, HARDWARE, HW_STALLED_CYCLES_FRONTEND)) { |
cc26ffaa | 761 | print_stalled_cycles_frontend(config, map_idx, avg, out, &rsd); |
c754c382 | 762 | } else if (evsel__match(evsel, HARDWARE, HW_STALLED_CYCLES_BACKEND)) { |
cc26ffaa | 763 | print_stalled_cycles_backend(config, map_idx, avg, out, &rsd); |
c754c382 | 764 | } else if (evsel__match(evsel, HARDWARE, HW_CPU_CYCLES)) { |
cc26ffaa | 765 | total = runtime_stat_avg(STAT_NSECS, map_idx, &rsd); |
f87027b9 JO |
766 | |
767 | if (total) { | |
768 | ratio = avg / total; | |
6ca9a082 | 769 | print_metric(config, ctxp, NULL, "%8.3f", "GHz", ratio); |
f87027b9 | 770 | } else { |
6ca9a082 | 771 | print_metric(config, ctxp, NULL, NULL, "Ghz", 0); |
f87027b9 | 772 | } |
c754c382 | 773 | } else if (evsel__is_clock(evsel)) { |
4579ecc8 | 774 | if ((ratio = avg_stats(&walltime_nsecs_stats)) != 0) |
6ca9a082 | 775 | print_metric(config, ctxp, NULL, "%8.3f", "CPUs utilized", |
0aa802a7 | 776 | avg / (ratio * evsel->scale)); |
4579ecc8 | 777 | else |
6ca9a082 | 778 | print_metric(config, ctxp, NULL, NULL, "CPUs utilized", 0); |
cc26ffaa | 779 | } else if (runtime_stat_n(STAT_NSECS, map_idx, &rsd) != 0) { |
6859bc0e CD |
780 | char unit = ' '; |
781 | char unit_buf[10] = "/sec"; | |
f87027b9 | 782 | |
cc26ffaa | 783 | total = runtime_stat_avg(STAT_NSECS, map_idx, &rsd); |
f87027b9 | 784 | if (total) |
6859bc0e CD |
785 | ratio = convert_unit_double(1000000000.0 * avg / total, &unit); |
786 | ||
787 | if (unit != ' ') | |
788 | snprintf(unit_buf, sizeof(unit_buf), "%c/sec", unit); | |
6ca9a082 | 789 | print_metric(config, ctxp, NULL, "%8.3f", unit_buf, ratio); |
f87027b9 | 790 | } else { |
b18f3e36 | 791 | num = 0; |
f87027b9 | 792 | } |
b18f3e36 AK |
793 | |
794 | if ((me = metricgroup__lookup(metric_events, evsel, false)) != NULL) { | |
795 | struct metric_expr *mexp; | |
796 | ||
797 | list_for_each_entry (mexp, &me->head, nd) { | |
798 | if (num++ > 0) | |
6ca9a082 | 799 | out->new_line(config, ctxp); |
d0a3052f IR |
800 | generic_metric(config, mexp->metric_expr, mexp->metric_threshold, |
801 | mexp->metric_events, mexp->metric_refs, evsel->name, | |
802 | mexp->metric_name, mexp->metric_unit, mexp->runtime, | |
cc26ffaa | 803 | map_idx, out); |
b18f3e36 AK |
804 | } |
805 | } | |
806 | if (num == 0) | |
6ca9a082 | 807 | print_metric(config, ctxp, NULL, NULL, NULL, 0); |
f87027b9 | 808 | } |