1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <math.h> |
3 | #include <stdio.h> |
4 | #include "evsel.h" |
5 | #include "stat.h" |
6 | #include "color.h" |
7 | #include "debug.h" |
8 | #include "pmu.h" |
9 | #include "rblist.h" |
10 | #include "evlist.h" |
11 | #include "expr.h" |
12 | #include "metricgroup.h" |
13 | #include "cgroup.h" |
14 | #include "units.h" |
15 | #include <linux/zalloc.h> |
16 | #include "iostat.h" |
17 | #include "util/hashmap.h" |
18 | |
19 | struct stats walltime_nsecs_stats; |
20 | struct rusage_stats ru_stats; |
21 | |
22 | enum { |
23 | CTX_BIT_USER = 1 << 0, |
24 | CTX_BIT_KERNEL = 1 << 1, |
25 | CTX_BIT_HV = 1 << 2, |
26 | CTX_BIT_HOST = 1 << 3, |
27 | CTX_BIT_IDLE = 1 << 4, |
28 | CTX_BIT_MAX = 1 << 5, |
29 | }; |
30 | |
31 | enum stat_type { |
32 | STAT_NONE = 0, |
33 | STAT_NSECS, |
34 | STAT_CYCLES, |
35 | STAT_INSTRUCTIONS, |
36 | STAT_STALLED_CYCLES_FRONT, |
37 | STAT_STALLED_CYCLES_BACK, |
38 | STAT_BRANCHES, |
39 | STAT_BRANCH_MISS, |
40 | STAT_CACHE_REFS, |
41 | STAT_CACHE_MISSES, |
42 | STAT_L1_DCACHE, |
43 | STAT_L1_ICACHE, |
44 | STAT_LL_CACHE, |
45 | STAT_ITLB_CACHE, |
46 | STAT_DTLB_CACHE, |
47 | STAT_L1D_MISS, |
48 | STAT_L1I_MISS, |
49 | STAT_LL_MISS, |
50 | STAT_DTLB_MISS, |
51 | STAT_ITLB_MISS, |
52 | STAT_MAX |
53 | }; |
54 | |
55 | static int evsel_context(const struct evsel *evsel) |
56 | { |
57 | int ctx = 0; |
58 | |
59 | if (evsel->core.attr.exclude_kernel) |
60 | ctx |= CTX_BIT_KERNEL; |
61 | if (evsel->core.attr.exclude_user) |
62 | ctx |= CTX_BIT_USER; |
63 | if (evsel->core.attr.exclude_hv) |
64 | ctx |= CTX_BIT_HV; |
65 | if (evsel->core.attr.exclude_host) |
66 | ctx |= CTX_BIT_HOST; |
67 | if (evsel->core.attr.exclude_idle) |
68 | ctx |= CTX_BIT_IDLE; |
69 | |
70 | return ctx; |
71 | } |
72 | |
73 | void perf_stat__reset_shadow_stats(void) |
74 | { |
75 | memset(&walltime_nsecs_stats, 0, sizeof(walltime_nsecs_stats)); |
76 | memset(&ru_stats, 0, sizeof(ru_stats)); |
77 | } |
78 | |
79 | static enum stat_type evsel__stat_type(const struct evsel *evsel) |
80 | { |
81 | /* Fake perf_hw_cache_op_id values for use with evsel__match. */ |
82 | u64 PERF_COUNT_hw_cache_l1d_miss = PERF_COUNT_HW_CACHE_L1D | |
83 | ((PERF_COUNT_HW_CACHE_OP_READ) << 8) | |
84 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16); |
85 | u64 PERF_COUNT_hw_cache_l1i_miss = PERF_COUNT_HW_CACHE_L1I | |
86 | ((PERF_COUNT_HW_CACHE_OP_READ) << 8) | |
87 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16); |
88 | u64 PERF_COUNT_hw_cache_ll_miss = PERF_COUNT_HW_CACHE_LL | |
89 | ((PERF_COUNT_HW_CACHE_OP_READ) << 8) | |
90 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16); |
91 | u64 PERF_COUNT_hw_cache_dtlb_miss = PERF_COUNT_HW_CACHE_DTLB | |
92 | ((PERF_COUNT_HW_CACHE_OP_READ) << 8) | |
93 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16); |
94 | u64 PERF_COUNT_hw_cache_itlb_miss = PERF_COUNT_HW_CACHE_ITLB | |
95 | ((PERF_COUNT_HW_CACHE_OP_READ) << 8) | |
96 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16); |
97 | |
98 | if (evsel__is_clock(evsel)) |
99 | return STAT_NSECS; |
100 | else if (evsel__match(evsel, HARDWARE, HW_CPU_CYCLES)) |
101 | return STAT_CYCLES; |
102 | else if (evsel__match(evsel, HARDWARE, HW_INSTRUCTIONS)) |
103 | return STAT_INSTRUCTIONS; |
104 | else if (evsel__match(evsel, HARDWARE, HW_STALLED_CYCLES_FRONTEND)) |
105 | return STAT_STALLED_CYCLES_FRONT; |
106 | else if (evsel__match(evsel, HARDWARE, HW_STALLED_CYCLES_BACKEND)) |
107 | return STAT_STALLED_CYCLES_BACK; |
108 | else if (evsel__match(evsel, HARDWARE, HW_BRANCH_INSTRUCTIONS)) |
109 | return STAT_BRANCHES; |
110 | else if (evsel__match(evsel, HARDWARE, HW_BRANCH_MISSES)) |
111 | return STAT_BRANCH_MISS; |
112 | else if (evsel__match(evsel, HARDWARE, HW_CACHE_REFERENCES)) |
113 | return STAT_CACHE_REFS; |
114 | else if (evsel__match(evsel, HARDWARE, HW_CACHE_MISSES)) |
115 | return STAT_CACHE_MISSES; |
116 | else if (evsel__match(evsel, HW_CACHE, HW_CACHE_L1D)) |
117 | return STAT_L1_DCACHE; |
118 | else if (evsel__match(evsel, HW_CACHE, HW_CACHE_L1I)) |
119 | return STAT_L1_ICACHE; |
120 | else if (evsel__match(evsel, HW_CACHE, HW_CACHE_LL)) |
121 | return STAT_LL_CACHE; |
122 | else if (evsel__match(evsel, HW_CACHE, HW_CACHE_DTLB)) |
123 | return STAT_DTLB_CACHE; |
124 | else if (evsel__match(evsel, HW_CACHE, HW_CACHE_ITLB)) |
125 | return STAT_ITLB_CACHE; |
126 | else if (evsel__match(evsel, HW_CACHE, hw_cache_l1d_miss)) |
127 | return STAT_L1D_MISS; |
128 | else if (evsel__match(evsel, HW_CACHE, hw_cache_l1i_miss)) |
129 | return STAT_L1I_MISS; |
130 | else if (evsel__match(evsel, HW_CACHE, hw_cache_ll_miss)) |
131 | return STAT_LL_MISS; |
132 | else if (evsel__match(evsel, HW_CACHE, hw_cache_dtlb_miss)) |
133 | return STAT_DTLB_MISS; |
134 | else if (evsel__match(evsel, HW_CACHE, hw_cache_itlb_miss)) |
135 | return STAT_ITLB_MISS; |
136 | return STAT_NONE; |
137 | } |
138 | |
139 | static const char *get_ratio_color(const double ratios[3], double val) |
140 | { |
141 | const char *color = PERF_COLOR_NORMAL; |
142 | |
143 | if (val > ratios[0]) |
144 | color = PERF_COLOR_RED; |
145 | else if (val > ratios[1]) |
146 | color = PERF_COLOR_MAGENTA; |
147 | else if (val > ratios[2]) |
148 | color = PERF_COLOR_YELLOW; |
149 | |
150 | return color; |
151 | } |
152 | |
153 | static double find_stat(const struct evsel *evsel, int aggr_idx, enum stat_type type) |
154 | { |
155 | const struct evsel *cur; |
156 | int evsel_ctx = evsel_context(evsel); |
157 | |
158 | evlist__for_each_entry(evsel->evlist, cur) { |
159 | struct perf_stat_aggr *aggr; |
160 | |
161 | /* Ignore the evsel that is being searched from. */ |
162 | if (evsel == cur) |
163 | continue; |
164 | |
165 | /* Ignore evsels that are part of different groups. */ |
166 | if (evsel->core.leader->nr_members > 1 && |
167 | evsel->core.leader != cur->core.leader) |
168 | continue; |
169 | /* Ignore evsels with mismatched modifiers. */ |
170 | if (evsel_ctx != evsel_context(evsel: cur)) |
171 | continue; |
172 | /* Ignore if not the cgroup we're looking for. */ |
173 | if (evsel->cgrp != cur->cgrp) |
174 | continue; |
175 | /* Ignore if not the stat we're looking for. */ |
176 | if (type != evsel__stat_type(evsel: cur)) |
177 | continue; |
178 | |
179 | aggr = &cur->stats->aggr[aggr_idx]; |
180 | if (type == STAT_NSECS) |
181 | return aggr->counts.val; |
182 | return aggr->counts.val * cur->scale; |
183 | } |
184 | return 0.0; |
185 | } |
186 | |
187 | static void print_ratio(struct perf_stat_config *config, |
188 | const struct evsel *evsel, int aggr_idx, |
189 | double numerator, struct perf_stat_output_ctx *out, |
190 | enum stat_type denominator_type, |
191 | const double color_ratios[3], const char *unit) |
192 | { |
193 | double denominator = find_stat(evsel, aggr_idx, type: denominator_type); |
194 | |
195 | if (numerator && denominator) { |
196 | double ratio = numerator / denominator * 100.0; |
197 | const char *color = get_ratio_color(ratios: color_ratios, val: ratio); |
198 | |
199 | out->print_metric(config, out->ctx, color, "%7.2f%%" , unit, ratio); |
200 | } else |
201 | out->print_metric(config, out->ctx, NULL, NULL, unit, 0); |
202 | } |
203 | |
204 | static void print_stalled_cycles_front(struct perf_stat_config *config, |
205 | const struct evsel *evsel, |
206 | int aggr_idx, double stalled, |
207 | struct perf_stat_output_ctx *out) |
208 | { |
209 | static const double color_ratios[3] = {50.0, 30.0, 10.0}; |
210 | |
211 | print_ratio(config, evsel, aggr_idx, numerator: stalled, out, denominator_type: STAT_CYCLES, color_ratios, |
212 | unit: "frontend cycles idle" ); |
213 | } |
214 | |
215 | static void print_stalled_cycles_back(struct perf_stat_config *config, |
216 | const struct evsel *evsel, |
217 | int aggr_idx, double stalled, |
218 | struct perf_stat_output_ctx *out) |
219 | { |
220 | static const double color_ratios[3] = {75.0, 50.0, 20.0}; |
221 | |
222 | print_ratio(config, evsel, aggr_idx, numerator: stalled, out, denominator_type: STAT_CYCLES, color_ratios, |
223 | unit: "backend cycles idle" ); |
224 | } |
225 | |
226 | static void print_branch_miss(struct perf_stat_config *config, |
227 | const struct evsel *evsel, |
228 | int aggr_idx, double misses, |
229 | struct perf_stat_output_ctx *out) |
230 | { |
231 | static const double color_ratios[3] = {20.0, 10.0, 5.0}; |
232 | |
233 | print_ratio(config, evsel, aggr_idx, numerator: misses, out, denominator_type: STAT_BRANCHES, color_ratios, |
234 | unit: "of all branches" ); |
235 | } |
236 | |
237 | static void print_l1d_miss(struct perf_stat_config *config, |
238 | const struct evsel *evsel, |
239 | int aggr_idx, double misses, |
240 | struct perf_stat_output_ctx *out) |
241 | { |
242 | static const double color_ratios[3] = {20.0, 10.0, 5.0}; |
243 | |
244 | print_ratio(config, evsel, aggr_idx, numerator: misses, out, denominator_type: STAT_L1_DCACHE, color_ratios, |
245 | unit: "of all L1-dcache accesses" ); |
246 | } |
247 | |
248 | static void print_l1i_miss(struct perf_stat_config *config, |
249 | const struct evsel *evsel, |
250 | int aggr_idx, double misses, |
251 | struct perf_stat_output_ctx *out) |
252 | { |
253 | static const double color_ratios[3] = {20.0, 10.0, 5.0}; |
254 | |
255 | print_ratio(config, evsel, aggr_idx, numerator: misses, out, denominator_type: STAT_L1_ICACHE, color_ratios, |
256 | unit: "of all L1-icache accesses" ); |
257 | } |
258 | |
259 | static void print_ll_miss(struct perf_stat_config *config, |
260 | const struct evsel *evsel, |
261 | int aggr_idx, double misses, |
262 | struct perf_stat_output_ctx *out) |
263 | { |
264 | static const double color_ratios[3] = {20.0, 10.0, 5.0}; |
265 | |
266 | print_ratio(config, evsel, aggr_idx, numerator: misses, out, denominator_type: STAT_LL_CACHE, color_ratios, |
267 | unit: "of all LL-cache accesses" ); |
268 | } |
269 | |
270 | static void print_dtlb_miss(struct perf_stat_config *config, |
271 | const struct evsel *evsel, |
272 | int aggr_idx, double misses, |
273 | struct perf_stat_output_ctx *out) |
274 | { |
275 | static const double color_ratios[3] = {20.0, 10.0, 5.0}; |
276 | |
277 | print_ratio(config, evsel, aggr_idx, numerator: misses, out, denominator_type: STAT_DTLB_CACHE, color_ratios, |
278 | unit: "of all dTLB cache accesses" ); |
279 | } |
280 | |
281 | static void print_itlb_miss(struct perf_stat_config *config, |
282 | const struct evsel *evsel, |
283 | int aggr_idx, double misses, |
284 | struct perf_stat_output_ctx *out) |
285 | { |
286 | static const double color_ratios[3] = {20.0, 10.0, 5.0}; |
287 | |
288 | print_ratio(config, evsel, aggr_idx, numerator: misses, out, denominator_type: STAT_ITLB_CACHE, color_ratios, |
289 | unit: "of all iTLB cache accesses" ); |
290 | } |
291 | |
292 | static void print_cache_miss(struct perf_stat_config *config, |
293 | const struct evsel *evsel, |
294 | int aggr_idx, double misses, |
295 | struct perf_stat_output_ctx *out) |
296 | { |
297 | static const double color_ratios[3] = {20.0, 10.0, 5.0}; |
298 | |
299 | print_ratio(config, evsel, aggr_idx, numerator: misses, out, denominator_type: STAT_CACHE_REFS, color_ratios, |
300 | unit: "of all cache refs" ); |
301 | } |
302 | |
303 | static void print_instructions(struct perf_stat_config *config, |
304 | const struct evsel *evsel, |
305 | int aggr_idx, double instructions, |
306 | struct perf_stat_output_ctx *out) |
307 | { |
308 | print_metric_t print_metric = out->print_metric; |
309 | void *ctxp = out->ctx; |
310 | double cycles = find_stat(evsel, aggr_idx, type: STAT_CYCLES); |
311 | double max_stalled = max(find_stat(evsel, aggr_idx, STAT_STALLED_CYCLES_FRONT), |
312 | find_stat(evsel, aggr_idx, STAT_STALLED_CYCLES_BACK)); |
313 | |
314 | if (cycles) { |
315 | print_metric(config, ctxp, NULL, "%7.2f " , "insn per cycle" , |
316 | instructions / cycles); |
317 | } else |
318 | print_metric(config, ctxp, NULL, NULL, "insn per cycle" , 0); |
319 | |
320 | if (max_stalled && instructions) { |
321 | out->new_line(config, ctxp); |
322 | print_metric(config, ctxp, NULL, "%7.2f " , "stalled cycles per insn" , |
323 | max_stalled / instructions); |
324 | } |
325 | } |
326 | |
327 | static void print_cycles(struct perf_stat_config *config, |
328 | const struct evsel *evsel, |
329 | int aggr_idx, double cycles, |
330 | struct perf_stat_output_ctx *out) |
331 | { |
332 | double nsecs = find_stat(evsel, aggr_idx, type: STAT_NSECS); |
333 | |
334 | if (cycles && nsecs) { |
335 | double ratio = cycles / nsecs; |
336 | |
337 | out->print_metric(config, out->ctx, NULL, "%8.3f" , "GHz" , ratio); |
338 | } else |
339 | out->print_metric(config, out->ctx, NULL, NULL, "GHz" , 0); |
340 | } |
341 | |
342 | static void print_nsecs(struct perf_stat_config *config, |
343 | const struct evsel *evsel, |
344 | int aggr_idx __maybe_unused, double nsecs, |
345 | struct perf_stat_output_ctx *out) |
346 | { |
347 | print_metric_t print_metric = out->print_metric; |
348 | void *ctxp = out->ctx; |
349 | double wall_time = avg_stats(stats: &walltime_nsecs_stats); |
350 | |
351 | if (wall_time) { |
352 | print_metric(config, ctxp, NULL, "%8.3f" , "CPUs utilized" , |
353 | nsecs / (wall_time * evsel->scale)); |
354 | } else |
355 | print_metric(config, ctxp, NULL, NULL, "CPUs utilized" , 0); |
356 | } |
357 | |
358 | static int prepare_metric(const struct metric_expr *mexp, |
359 | const struct evsel *evsel, |
360 | struct expr_parse_ctx *pctx, |
361 | int aggr_idx) |
362 | { |
363 | struct evsel * const *metric_events = mexp->metric_events; |
364 | struct metric_ref *metric_refs = mexp->metric_refs; |
365 | int i; |
366 | |
367 | for (i = 0; metric_events[i]; i++) { |
368 | char *n; |
369 | double val; |
370 | int source_count = 0; |
371 | |
372 | if (evsel__is_tool(evsel: metric_events[i])) { |
373 | struct stats *stats; |
374 | double scale; |
375 | |
376 | switch (metric_events[i]->tool_event) { |
377 | case PERF_TOOL_DURATION_TIME: |
378 | stats = &walltime_nsecs_stats; |
379 | scale = 1e-9; |
380 | break; |
381 | case PERF_TOOL_USER_TIME: |
382 | stats = &ru_stats.ru_utime_usec_stat; |
383 | scale = 1e-6; |
384 | break; |
385 | case PERF_TOOL_SYSTEM_TIME: |
386 | stats = &ru_stats.ru_stime_usec_stat; |
387 | scale = 1e-6; |
388 | break; |
389 | case PERF_TOOL_NONE: |
390 | pr_err("Invalid tool event 'none'" ); |
391 | abort(); |
392 | case PERF_TOOL_MAX: |
393 | pr_err("Invalid tool event 'max'" ); |
394 | abort(); |
395 | default: |
396 | pr_err("Unknown tool event '%s'" , evsel__name(metric_events[i])); |
397 | abort(); |
398 | } |
399 | val = avg_stats(stats) * scale; |
400 | source_count = 1; |
401 | } else { |
402 | struct perf_stat_evsel *ps = metric_events[i]->stats; |
403 | struct perf_stat_aggr *aggr; |
404 | |
405 | /* |
406 | * If there are multiple uncore PMUs and we're not |
407 | * reading the leader's stats, determine the stats for |
408 | * the appropriate uncore PMU. |
409 | */ |
410 | if (evsel && evsel->metric_leader && |
411 | evsel->pmu != evsel->metric_leader->pmu && |
412 | mexp->metric_events[i]->pmu == evsel->metric_leader->pmu) { |
413 | struct evsel *pos; |
414 | |
415 | evlist__for_each_entry(evsel->evlist, pos) { |
416 | if (pos->pmu != evsel->pmu) |
417 | continue; |
418 | if (pos->metric_leader != mexp->metric_events[i]) |
419 | continue; |
420 | ps = pos->stats; |
421 | source_count = 1; |
422 | break; |
423 | } |
424 | } |
425 | aggr = &ps->aggr[aggr_idx]; |
426 | if (!aggr) |
427 | break; |
428 | |
429 | if (!metric_events[i]->supported) { |
430 | /* |
431 | * Not supported events will have a count of 0, |
432 | * which can be confusing in a |
433 | * metric. Explicitly set the value to NAN. Not |
434 | * counted events (enable time of 0) are read as |
435 | * 0. |
436 | */ |
437 | val = NAN; |
438 | source_count = 0; |
439 | } else { |
440 | val = aggr->counts.val; |
441 | if (!source_count) |
442 | source_count = evsel__source_count(evsel: metric_events[i]); |
443 | } |
444 | } |
445 | n = strdup(evsel__metric_id(evsel: metric_events[i])); |
446 | if (!n) |
447 | return -ENOMEM; |
448 | |
449 | expr__add_id_val_source_count(ctx: pctx, id: n, val, source_count); |
450 | } |
451 | |
452 | for (int j = 0; metric_refs && metric_refs[j].metric_name; j++) { |
453 | int ret = expr__add_ref(ctx: pctx, ref: &metric_refs[j]); |
454 | |
455 | if (ret) |
456 | return ret; |
457 | } |
458 | |
459 | return i; |
460 | } |
461 | |
462 | static void generic_metric(struct perf_stat_config *config, |
463 | struct metric_expr *mexp, |
464 | struct evsel *evsel, |
465 | int aggr_idx, |
466 | struct perf_stat_output_ctx *out) |
467 | { |
468 | print_metric_t print_metric = out->print_metric; |
469 | const char *metric_name = mexp->metric_name; |
470 | const char *metric_expr = mexp->metric_expr; |
471 | const char *metric_threshold = mexp->metric_threshold; |
472 | const char *metric_unit = mexp->metric_unit; |
473 | struct evsel * const *metric_events = mexp->metric_events; |
474 | int runtime = mexp->runtime; |
475 | struct expr_parse_ctx *pctx; |
476 | double ratio, scale, threshold; |
477 | int i; |
478 | void *ctxp = out->ctx; |
479 | const char *color = NULL; |
480 | |
481 | pctx = expr__ctx_new(); |
482 | if (!pctx) |
483 | return; |
484 | |
485 | if (config->user_requested_cpu_list) |
486 | pctx->sctx.user_requested_cpu_list = strdup(config->user_requested_cpu_list); |
487 | pctx->sctx.runtime = runtime; |
488 | pctx->sctx.system_wide = config->system_wide; |
489 | i = prepare_metric(mexp, evsel, pctx, aggr_idx); |
490 | if (i < 0) { |
491 | expr__ctx_free(ctx: pctx); |
492 | return; |
493 | } |
494 | if (!metric_events[i]) { |
495 | if (expr__parse(final_val: &ratio, ctx: pctx, expr: metric_expr) == 0) { |
496 | char *unit; |
497 | char metric_bf[64]; |
498 | |
499 | if (metric_threshold && |
500 | expr__parse(final_val: &threshold, ctx: pctx, expr: metric_threshold) == 0 && |
501 | !isnan(threshold)) { |
502 | color = fpclassify(threshold) == FP_ZERO |
503 | ? PERF_COLOR_GREEN : PERF_COLOR_RED; |
504 | } |
505 | |
506 | if (metric_unit && metric_name) { |
507 | if (perf_pmu__convert_scale(scale: metric_unit, |
508 | end: &unit, sval: &scale) >= 0) { |
509 | ratio *= scale; |
510 | } |
511 | if (strstr(metric_expr, "?" )) |
512 | scnprintf(buf: metric_bf, size: sizeof(metric_bf), |
513 | fmt: "%s %s_%d" , unit, metric_name, runtime); |
514 | else |
515 | scnprintf(buf: metric_bf, size: sizeof(metric_bf), |
516 | fmt: "%s %s" , unit, metric_name); |
517 | |
518 | print_metric(config, ctxp, color, "%8.1f" , |
519 | metric_bf, ratio); |
520 | } else { |
521 | print_metric(config, ctxp, color, "%8.2f" , |
522 | metric_name ? |
523 | metric_name : |
524 | out->force_header ? evsel->name : "" , |
525 | ratio); |
526 | } |
527 | } else { |
528 | print_metric(config, ctxp, color, /*unit=*/NULL, |
529 | out->force_header ? |
530 | (metric_name ?: evsel->name) : "" , 0); |
531 | } |
532 | } else { |
533 | print_metric(config, ctxp, color, /*unit=*/NULL, |
534 | out->force_header ? |
535 | (metric_name ?: evsel->name) : "" , 0); |
536 | } |
537 | |
538 | expr__ctx_free(ctx: pctx); |
539 | } |
540 | |
541 | double test_generic_metric(struct metric_expr *mexp, int aggr_idx) |
542 | { |
543 | struct expr_parse_ctx *pctx; |
544 | double ratio = 0.0; |
545 | |
546 | pctx = expr__ctx_new(); |
547 | if (!pctx) |
548 | return NAN; |
549 | |
550 | if (prepare_metric(mexp, /*evsel=*/NULL, pctx, aggr_idx) < 0) |
551 | goto out; |
552 | |
553 | if (expr__parse(final_val: &ratio, ctx: pctx, expr: mexp->metric_expr)) |
554 | ratio = 0.0; |
555 | |
556 | out: |
557 | expr__ctx_free(ctx: pctx); |
558 | return ratio; |
559 | } |
560 | |
561 | static void (struct perf_stat_config *config, |
562 | struct evsel *evsel, |
563 | void *ctxp, |
564 | const char *name, |
565 | struct perf_stat_output_ctx *out) |
566 | { |
567 | bool need_full_name = perf_pmus__num_core_pmus() > 1; |
568 | static const char *last_name; |
569 | static const char *last_pmu; |
570 | char full_name[64]; |
571 | |
572 | /* |
573 | * A metricgroup may have several metric events, |
574 | * e.g.,TopdownL1 on e-core of ADL. |
575 | * The name has been output by the first metric |
576 | * event. Only align with other metics from |
577 | * different metric events. |
578 | */ |
579 | if (last_name && !strcmp(last_name, name)) { |
580 | if (!need_full_name || !strcmp(last_pmu, evsel->pmu_name)) { |
581 | out->print_metricgroup_header(config, ctxp, NULL); |
582 | return; |
583 | } |
584 | } |
585 | |
586 | if (need_full_name) |
587 | scnprintf(buf: full_name, size: sizeof(full_name), fmt: "%s (%s)" , name, evsel->pmu_name); |
588 | else |
589 | scnprintf(buf: full_name, size: sizeof(full_name), fmt: "%s" , name); |
590 | |
591 | out->print_metricgroup_header(config, ctxp, full_name); |
592 | |
593 | last_name = name; |
594 | last_pmu = evsel->pmu_name; |
595 | } |
596 | |
597 | /** |
598 | * perf_stat__print_shadow_stats_metricgroup - Print out metrics associated with the evsel |
599 | * For the non-default, all metrics associated |
600 | * with the evsel are printed. |
601 | * For the default mode, only the metrics from |
602 | * the same metricgroup and the name of the |
603 | * metricgroup are printed. To print the metrics |
604 | * from the next metricgroup (if available), |
605 | * invoke the function with correspoinding |
606 | * metric_expr. |
607 | */ |
608 | void *perf_stat__print_shadow_stats_metricgroup(struct perf_stat_config *config, |
609 | struct evsel *evsel, |
610 | int aggr_idx, |
611 | int *num, |
612 | void *from, |
613 | struct perf_stat_output_ctx *out, |
614 | struct rblist *metric_events) |
615 | { |
616 | struct metric_event *me; |
617 | struct metric_expr *mexp = from; |
618 | void *ctxp = out->ctx; |
619 | bool = false; |
620 | const char *name = NULL; |
621 | |
622 | me = metricgroup__lookup(metric_events, evsel, create: false); |
623 | if (me == NULL) |
624 | return NULL; |
625 | |
626 | if (!mexp) |
627 | mexp = list_first_entry(&me->head, typeof(*mexp), nd); |
628 | |
629 | list_for_each_entry_from(mexp, &me->head, nd) { |
630 | /* Print the display name of the Default metricgroup */ |
631 | if (!config->metric_only && me->is_default) { |
632 | if (!name) |
633 | name = mexp->default_metricgroup_name; |
634 | /* |
635 | * Two or more metricgroup may share the same metric |
636 | * event, e.g., TopdownL1 and TopdownL2 on SPR. |
637 | * Return and print the prefix, e.g., noise, running |
638 | * for the next metricgroup. |
639 | */ |
640 | if (strcmp(name, mexp->default_metricgroup_name)) |
641 | return (void *)mexp; |
642 | /* Only print the name of the metricgroup once */ |
643 | if (!header_printed) { |
644 | header_printed = true; |
645 | perf_stat__print_metricgroup_header(config, evsel, ctxp, |
646 | name, out); |
647 | } |
648 | } |
649 | |
650 | if ((*num)++ > 0) |
651 | out->new_line(config, ctxp); |
652 | generic_metric(config, mexp, evsel, aggr_idx, out); |
653 | } |
654 | |
655 | return NULL; |
656 | } |
657 | |
658 | void perf_stat__print_shadow_stats(struct perf_stat_config *config, |
659 | struct evsel *evsel, |
660 | double avg, int aggr_idx, |
661 | struct perf_stat_output_ctx *out, |
662 | struct rblist *metric_events) |
663 | { |
664 | typedef void (*stat_print_function_t)(struct perf_stat_config *config, |
665 | const struct evsel *evsel, |
666 | int aggr_idx, double misses, |
667 | struct perf_stat_output_ctx *out); |
668 | static const stat_print_function_t stat_print_function[STAT_MAX] = { |
669 | [STAT_INSTRUCTIONS] = print_instructions, |
670 | [STAT_BRANCH_MISS] = print_branch_miss, |
671 | [STAT_L1D_MISS] = print_l1d_miss, |
672 | [STAT_L1I_MISS] = print_l1i_miss, |
673 | [STAT_DTLB_MISS] = print_dtlb_miss, |
674 | [STAT_ITLB_MISS] = print_itlb_miss, |
675 | [STAT_LL_MISS] = print_ll_miss, |
676 | [STAT_CACHE_MISSES] = print_cache_miss, |
677 | [STAT_STALLED_CYCLES_FRONT] = print_stalled_cycles_front, |
678 | [STAT_STALLED_CYCLES_BACK] = print_stalled_cycles_back, |
679 | [STAT_CYCLES] = print_cycles, |
680 | [STAT_NSECS] = print_nsecs, |
681 | }; |
682 | print_metric_t print_metric = out->print_metric; |
683 | void *ctxp = out->ctx; |
684 | int num = 1; |
685 | |
686 | if (config->iostat_run) { |
687 | iostat_print_metric(config, evsel, out); |
688 | } else { |
689 | stat_print_function_t fn = stat_print_function[evsel__stat_type(evsel)]; |
690 | |
691 | if (fn) |
692 | fn(config, evsel, aggr_idx, avg, out); |
693 | else { |
694 | double nsecs = find_stat(evsel, aggr_idx, type: STAT_NSECS); |
695 | |
696 | if (nsecs) { |
697 | char unit = ' '; |
698 | char unit_buf[10] = "/sec" ; |
699 | double ratio = convert_unit_double(value: 1000000000.0 * avg / nsecs, |
700 | unit: &unit); |
701 | |
702 | if (unit != ' ') |
703 | snprintf(buf: unit_buf, size: sizeof(unit_buf), fmt: "%c/sec" , unit); |
704 | print_metric(config, ctxp, NULL, "%8.3f" , unit_buf, ratio); |
705 | } else |
706 | num = 0; |
707 | } |
708 | } |
709 | |
710 | perf_stat__print_shadow_stats_metricgroup(config, evsel, aggr_idx, |
711 | num: &num, NULL, out, metric_events); |
712 | |
713 | if (num == 0) |
714 | print_metric(config, ctxp, NULL, NULL, NULL, 0); |
715 | } |
716 | |
717 | /** |
718 | * perf_stat__skip_metric_event - Skip the evsel in the Default metricgroup, |
719 | * if it's not running or not the metric event. |
720 | */ |
721 | bool perf_stat__skip_metric_event(struct evsel *evsel, |
722 | struct rblist *metric_events, |
723 | u64 ena, u64 run) |
724 | { |
725 | if (!evsel->default_metricgroup) |
726 | return false; |
727 | |
728 | if (!ena || !run) |
729 | return true; |
730 | |
731 | return !metricgroup__lookup(metric_events, evsel, create: false); |
732 | } |
733 | |