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 | |
f87027b9 | 19 | struct stats walltime_nsecs_stats; |
c735b0a5 | 20 | struct rusage_stats ru_stats; |
f87027b9 | 21 | |
758bc8e6 IR |
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, | |
0a57b910 | 35 | STAT_INSTRUCTIONS, |
758bc8e6 IR |
36 | STAT_STALLED_CYCLES_FRONT, |
37 | STAT_STALLED_CYCLES_BACK, | |
38 | STAT_BRANCHES, | |
0a57b910 IR |
39 | STAT_BRANCH_MISS, |
40 | STAT_CACHE_REFS, | |
41 | STAT_CACHE_MISSES, | |
758bc8e6 IR |
42 | STAT_L1_DCACHE, |
43 | STAT_L1_ICACHE, | |
44 | STAT_LL_CACHE, | |
45 | STAT_ITLB_CACHE, | |
46 | STAT_DTLB_CACHE, | |
0a57b910 IR |
47 | STAT_L1D_MISS, |
48 | STAT_L1I_MISS, | |
49 | STAT_LL_MISS, | |
50 | STAT_DTLB_MISS, | |
51 | STAT_ITLB_MISS, | |
758bc8e6 IR |
52 | STAT_MAX |
53 | }; | |
54 | ||
0a57b910 | 55 | static int evsel_context(const struct evsel *evsel) |
f87027b9 JO |
56 | { |
57 | int ctx = 0; | |
58 | ||
1fc632ce | 59 | if (evsel->core.attr.exclude_kernel) |
f87027b9 | 60 | ctx |= CTX_BIT_KERNEL; |
1fc632ce | 61 | if (evsel->core.attr.exclude_user) |
f87027b9 | 62 | ctx |= CTX_BIT_USER; |
1fc632ce | 63 | if (evsel->core.attr.exclude_hv) |
f87027b9 | 64 | ctx |= CTX_BIT_HV; |
1fc632ce | 65 | if (evsel->core.attr.exclude_host) |
f87027b9 | 66 | ctx |= CTX_BIT_HOST; |
1fc632ce | 67 | if (evsel->core.attr.exclude_idle) |
f87027b9 JO |
68 | ctx |= CTX_BIT_IDLE; |
69 | ||
70 | return ctx; | |
71 | } | |
72 | ||
6a1e2c5c JY |
73 | void perf_stat__reset_shadow_stats(void) |
74 | { | |
6a1e2c5c | 75 | memset(&walltime_nsecs_stats, 0, sizeof(walltime_nsecs_stats)); |
c735b0a5 | 76 | memset(&ru_stats, 0, sizeof(ru_stats)); |
6a1e2c5c JY |
77 | } |
78 | ||
0a57b910 IR |
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 | } | |
f87027b9 | 138 | |
0a57b910 | 139 | static const char *get_ratio_color(const double ratios[3], double val) |
f87027b9 | 140 | { |
f87027b9 JO |
141 | const char *color = PERF_COLOR_NORMAL; |
142 | ||
0a57b910 | 143 | if (val > ratios[0]) |
f87027b9 | 144 | color = PERF_COLOR_RED; |
0a57b910 | 145 | else if (val > ratios[1]) |
f87027b9 | 146 | color = PERF_COLOR_MAGENTA; |
0a57b910 | 147 | else if (val > ratios[2]) |
f87027b9 JO |
148 | color = PERF_COLOR_YELLOW; |
149 | ||
150 | return color; | |
151 | } | |
152 | ||
0a57b910 | 153 | static double find_stat(const struct evsel *evsel, int aggr_idx, enum stat_type type) |
e0128b30 | 154 | { |
0a57b910 IR |
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. */ | |
ce5b8590 | 166 | if (evsel->core.leader->nr_members > 1 && |
0a57b910 IR |
167 | evsel->core.leader != cur->core.leader) |
168 | continue; | |
169 | /* Ignore evsels with mismatched modifiers. */ | |
170 | if (evsel_ctx != evsel_context(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(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; | |
e0128b30 JY |
185 | } |
186 | ||
0a57b910 IR |
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) | |
e0128b30 | 192 | { |
0a57b910 | 193 | double denominator = find_stat(evsel, aggr_idx, denominator_type); |
e0128b30 | 194 | |
0a57b910 IR |
195 | if (numerator && denominator) { |
196 | double ratio = numerator / denominator * 100.0; | |
197 | const char *color = get_ratio_color(color_ratios, ratio); | |
e0128b30 | 198 | |
0a57b910 IR |
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); | |
e0128b30 JY |
202 | } |
203 | ||
0a57b910 IR |
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) | |
f87027b9 | 208 | { |
0a57b910 | 209 | static const double color_ratios[3] = {50.0, 30.0, 10.0}; |
f87027b9 | 210 | |
0a57b910 IR |
211 | print_ratio(config, evsel, aggr_idx, stalled, out, STAT_CYCLES, color_ratios, |
212 | "frontend cycles idle"); | |
f87027b9 JO |
213 | } |
214 | ||
0a57b910 IR |
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) | |
f87027b9 | 219 | { |
0a57b910 | 220 | static const double color_ratios[3] = {75.0, 50.0, 20.0}; |
f87027b9 | 221 | |
0a57b910 IR |
222 | print_ratio(config, evsel, aggr_idx, stalled, out, STAT_CYCLES, color_ratios, |
223 | "backend cycles idle"); | |
f87027b9 JO |
224 | } |
225 | ||
0a57b910 IR |
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) | |
f87027b9 | 230 | { |
0a57b910 | 231 | static const double color_ratios[3] = {20.0, 10.0, 5.0}; |
f87027b9 | 232 | |
0a57b910 IR |
233 | print_ratio(config, evsel, aggr_idx, misses, out, STAT_BRANCHES, color_ratios, |
234 | "of all branches"); | |
f87027b9 JO |
235 | } |
236 | ||
0a57b910 IR |
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) | |
f87027b9 | 241 | { |
0a57b910 | 242 | static const double color_ratios[3] = {20.0, 10.0, 5.0}; |
f87027b9 | 243 | |
0a57b910 IR |
244 | print_ratio(config, evsel, aggr_idx, misses, out, STAT_L1_DCACHE, color_ratios, |
245 | "of all L1-dcache accesses"); | |
246 | } | |
f87027b9 | 247 | |
0a57b910 IR |
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}; | |
f87027b9 | 254 | |
0a57b910 IR |
255 | print_ratio(config, evsel, aggr_idx, misses, out, STAT_L1_ICACHE, color_ratios, |
256 | "of all L1-icache accesses"); | |
f87027b9 JO |
257 | } |
258 | ||
0a57b910 IR |
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) | |
f87027b9 | 263 | { |
0a57b910 | 264 | static const double color_ratios[3] = {20.0, 10.0, 5.0}; |
f87027b9 | 265 | |
0a57b910 | 266 | print_ratio(config, evsel, aggr_idx, misses, out, STAT_LL_CACHE, color_ratios, |
f2567e12 | 267 | "of all LL-cache accesses"); |
0a57b910 | 268 | } |
f87027b9 | 269 | |
0a57b910 IR |
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}; | |
f87027b9 | 276 | |
0a57b910 IR |
277 | print_ratio(config, evsel, aggr_idx, misses, out, STAT_DTLB_CACHE, color_ratios, |
278 | "of all dTLB cache accesses"); | |
f87027b9 JO |
279 | } |
280 | ||
0a57b910 IR |
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) | |
f87027b9 | 285 | { |
0a57b910 | 286 | static const double color_ratios[3] = {20.0, 10.0, 5.0}; |
f87027b9 | 287 | |
0a57b910 IR |
288 | print_ratio(config, evsel, aggr_idx, misses, out, STAT_ITLB_CACHE, color_ratios, |
289 | "of all iTLB cache accesses"); | |
290 | } | |
f87027b9 | 291 | |
0a57b910 IR |
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}; | |
f87027b9 | 298 | |
0a57b910 IR |
299 | print_ratio(config, evsel, aggr_idx, misses, out, STAT_CACHE_REFS, color_ratios, |
300 | "of all cache refs"); | |
f87027b9 JO |
301 | } |
302 | ||
0a57b910 IR |
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) | |
f87027b9 | 307 | { |
0a57b910 IR |
308 | print_metric_t print_metric = out->print_metric; |
309 | void *ctxp = out->ctx; | |
310 | double cycles = find_stat(evsel, aggr_idx, 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 | } | |
f87027b9 | 326 | |
0a57b910 IR |
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, STAT_NSECS); | |
f87027b9 | 333 | |
0a57b910 IR |
334 | if (cycles && nsecs) { |
335 | double ratio = cycles / nsecs; | |
f87027b9 | 336 | |
0a57b910 IR |
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); | |
f87027b9 JO |
340 | } |
341 | ||
0a57b910 IR |
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) | |
f87027b9 | 346 | { |
0a57b910 IR |
347 | print_metric_t print_metric = out->print_metric; |
348 | void *ctxp = out->ctx; | |
349 | double wall_time = avg_stats(&walltime_nsecs_stats); | |
f87027b9 | 350 | |
0a57b910 IR |
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); | |
f87027b9 JO |
356 | } |
357 | ||
eee41e6b | 358 | static int prepare_metric(const struct metric_expr *mexp, |
a59fb796 | 359 | const struct evsel *evsel, |
2cfaa853 | 360 | struct expr_parse_ctx *pctx, |
8945bef3 | 361 | int aggr_idx) |
bba49af8 | 362 | { |
eee41e6b IR |
363 | struct evsel * const *metric_events = mexp->metric_events; |
364 | struct metric_ref *metric_refs = mexp->metric_refs; | |
37cc8ad7 | 365 | int i; |
bba49af8 | 366 | |
bba49af8 | 367 | for (i = 0; metric_events[i]; i++) { |
37cc8ad7 IR |
368 | char *n; |
369 | double val; | |
370 | int source_count = 0; | |
fd48aad9 | 371 | |
9aa09230 | 372 | if (evsel__is_tool(metric_events[i])) { |
37cc8ad7 IR |
373 | struct stats *stats; |
374 | double scale; | |
375 | ||
9aa09230 IR |
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 | } | |
37cc8ad7 IR |
399 | val = avg_stats(stats) * scale; |
400 | source_count = 1; | |
fd48aad9 | 401 | } else { |
37cc8ad7 | 402 | struct perf_stat_evsel *ps = metric_events[i]->stats; |
a59fb796 IR |
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]; | |
37cc8ad7 | 426 | if (!aggr) |
fd48aad9 | 427 | break; |
37cc8ad7 | 428 | |
eee41e6b | 429 | if (!metric_events[i]->supported) { |
2a939c86 IR |
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 { | |
6d6be5eb | 440 | val = aggr->counts.val; |
a59fb796 IR |
441 | if (!source_count) |
442 | source_count = evsel__source_count(metric_events[i]); | |
2a939c86 | 443 | } |
fd48aad9 | 444 | } |
ec5c5b3d | 445 | n = strdup(evsel__metric_id(metric_events[i])); |
e3a94273 | 446 | if (!n) |
2cfaa853 | 447 | return -ENOMEM; |
ec5c5b3d | 448 | |
37cc8ad7 | 449 | expr__add_id_val_source_count(pctx, n, val, source_count); |
bba49af8 | 450 | } |
f01642e4 | 451 | |
37cc8ad7 IR |
452 | for (int j = 0; metric_refs && metric_refs[j].metric_name; j++) { |
453 | int ret = expr__add_ref(pctx, &metric_refs[j]); | |
454 | ||
fc393839 JO |
455 | if (ret) |
456 | return ret; | |
457 | } | |
458 | ||
2cfaa853 JO |
459 | return i; |
460 | } | |
461 | ||
462 | static void generic_metric(struct perf_stat_config *config, | |
eee41e6b IR |
463 | struct metric_expr *mexp, |
464 | struct evsel *evsel, | |
8945bef3 | 465 | int aggr_idx, |
cc26ffaa | 466 | struct perf_stat_output_ctx *out) |
2cfaa853 JO |
467 | { |
468 | print_metric_t print_metric = out->print_metric; | |
eee41e6b IR |
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; | |
cb94a02e | 475 | struct expr_parse_ctx *pctx; |
d0a3052f | 476 | double ratio, scale, threshold; |
2cfaa853 JO |
477 | int i; |
478 | void *ctxp = out->ctx; | |
d0a3052f | 479 | const char *color = NULL; |
2cfaa853 | 480 | |
cb94a02e IR |
481 | pctx = expr__ctx_new(); |
482 | if (!pctx) | |
2cfaa853 JO |
483 | return; |
484 | ||
1725e9cd IR |
485 | if (config->user_requested_cpu_list) |
486 | pctx->sctx.user_requested_cpu_list = strdup(config->user_requested_cpu_list); | |
1a6abdde | 487 | pctx->sctx.runtime = runtime; |
1725e9cd | 488 | pctx->sctx.system_wide = config->system_wide; |
a59fb796 | 489 | i = prepare_metric(mexp, evsel, pctx, aggr_idx); |
cb94a02e IR |
490 | if (i < 0) { |
491 | expr__ctx_free(pctx); | |
492 | return; | |
493 | } | |
bba49af8 | 494 | if (!metric_events[i]) { |
fa831fbb | 495 | if (expr__parse(&ratio, pctx, metric_expr) == 0) { |
287f2649 JY |
496 | char *unit; |
497 | char metric_bf[64]; | |
498 | ||
d0a3052f | 499 | if (metric_threshold && |
1fd09e29 IR |
500 | expr__parse(&threshold, pctx, metric_threshold) == 0 && |
501 | !isnan(threshold)) { | |
d0a3052f IR |
502 | color = fpclassify(threshold) == FP_ZERO |
503 | ? PERF_COLOR_GREEN : PERF_COLOR_RED; | |
504 | } | |
505 | ||
287f2649 JY |
506 | if (metric_unit && metric_name) { |
507 | if (perf_pmu__convert_scale(metric_unit, | |
508 | &unit, &scale) >= 0) { | |
509 | ratio *= scale; | |
510 | } | |
1e1a873d KJ |
511 | if (strstr(metric_expr, "?")) |
512 | scnprintf(metric_bf, sizeof(metric_bf), | |
513 | "%s %s_%d", unit, metric_name, runtime); | |
514 | else | |
515 | scnprintf(metric_bf, sizeof(metric_bf), | |
287f2649 | 516 | "%s %s", unit, metric_name); |
1e1a873d | 517 | |
d0a3052f | 518 | print_metric(config, ctxp, color, "%8.1f", |
287f2649 JY |
519 | metric_bf, ratio); |
520 | } else { | |
d0a3052f | 521 | print_metric(config, ctxp, color, "%8.2f", |
287f2649 JY |
522 | metric_name ? |
523 | metric_name : | |
eee41e6b | 524 | out->force_header ? evsel->name : "", |
287f2649 JY |
525 | ratio); |
526 | } | |
527 | } else { | |
d0a3052f | 528 | print_metric(config, ctxp, color, /*unit=*/NULL, |
4ed962eb | 529 | out->force_header ? |
eee41e6b | 530 | (metric_name ?: evsel->name) : "", 0); |
287f2649 | 531 | } |
8358f698 | 532 | } else { |
d0a3052f | 533 | print_metric(config, ctxp, color, /*unit=*/NULL, |
8358f698 | 534 | out->force_header ? |
eee41e6b | 535 | (metric_name ?: evsel->name) : "", 0); |
8358f698 | 536 | } |
e3a94273 | 537 | |
cb94a02e | 538 | expr__ctx_free(pctx); |
bba49af8 AK |
539 | } |
540 | ||
8945bef3 | 541 | double test_generic_metric(struct metric_expr *mexp, int aggr_idx) |
6d432c4c | 542 | { |
cb94a02e | 543 | struct expr_parse_ctx *pctx; |
437822bf | 544 | double ratio = 0.0; |
6d432c4c | 545 | |
cb94a02e IR |
546 | pctx = expr__ctx_new(); |
547 | if (!pctx) | |
548 | return NAN; | |
549 | ||
a59fb796 | 550 | if (prepare_metric(mexp, /*evsel=*/NULL, pctx, aggr_idx) < 0) |
437822bf | 551 | goto out; |
6d432c4c | 552 | |
fa831fbb | 553 | if (expr__parse(&ratio, pctx, mexp->metric_expr)) |
437822bf | 554 | ratio = 0.0; |
6d432c4c | 555 | |
437822bf | 556 | out: |
cb94a02e | 557 | expr__ctx_free(pctx); |
6d432c4c JO |
558 | return ratio; |
559 | } | |
560 | ||
6a80d794 KL |
561 | static void perf_stat__print_metricgroup_header(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(full_name, sizeof(full_name), "%s (%s)", name, evsel->pmu_name); | |
588 | else | |
589 | scnprintf(full_name, sizeof(full_name), "%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 header_printed = false; | |
620 | const char *name = NULL; | |
621 | ||
622 | me = metricgroup__lookup(metric_events, evsel, 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); | |
eee41e6b | 652 | generic_metric(config, mexp, evsel, aggr_idx, out); |
6a80d794 KL |
653 | } |
654 | ||
655 | return NULL; | |
656 | } | |
657 | ||
6ca9a082 | 658 | void perf_stat__print_shadow_stats(struct perf_stat_config *config, |
32dcd021 | 659 | struct evsel *evsel, |
8945bef3 | 660 | double avg, int aggr_idx, |
b18f3e36 | 661 | struct perf_stat_output_ctx *out, |
cc26ffaa | 662 | struct rblist *metric_events) |
f87027b9 | 663 | { |
0a57b910 IR |
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, | |
3ff1e718 | 681 | }; |
0a57b910 IR |
682 | print_metric_t print_metric = out->print_metric; |
683 | void *ctxp = out->ctx; | |
b18f3e36 | 684 | int num = 1; |
f87027b9 | 685 | |
f07952b1 AA |
686 | if (config->iostat_run) { |
687 | iostat_print_metric(config, evsel, out); | |
f87027b9 | 688 | } else { |
0a57b910 IR |
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, STAT_NSECS); | |
695 | ||
696 | if (nsecs) { | |
697 | char unit = ' '; | |
698 | char unit_buf[10] = "/sec"; | |
699 | double ratio = convert_unit_double(1000000000.0 * avg / nsecs, | |
700 | &unit); | |
701 | ||
702 | if (unit != ' ') | |
703 | snprintf(unit_buf, sizeof(unit_buf), "%c/sec", unit); | |
704 | print_metric(config, ctxp, NULL, "%8.3f", unit_buf, ratio); | |
705 | } else | |
706 | num = 0; | |
707 | } | |
f87027b9 | 708 | } |
b18f3e36 | 709 | |
6a80d794 KL |
710 | perf_stat__print_shadow_stats_metricgroup(config, evsel, aggr_idx, |
711 | &num, NULL, out, metric_events); | |
b18f3e36 | 712 | |
b18f3e36 | 713 | if (num == 0) |
6ca9a082 | 714 | print_metric(config, ctxp, NULL, NULL, NULL, 0); |
f87027b9 | 715 | } |
6a80d794 KL |
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, false); | |
732 | } |