1
2#include <stdio.h>
3#include "evsel.h"
4#include "stat.h"
5#include "color.h"
6#include "pmu.h"
7#include "rblist.h"
8#include "evlist.h"
9#include "expr.h"
10#include "metricgroup.h"
11#include <linux/zalloc.h>
12
13
14
15
16
17
18
19
20
21
22struct runtime_stat rt_stat;
23struct stats walltime_nsecs_stats;
24
25struct saved_value {
26 struct rb_node rb_node;
27 struct evsel *evsel;
28 enum stat_type type;
29 int ctx;
30 int cpu;
31 struct runtime_stat *stat;
32 struct stats stats;
33 u64 metric_total;
34 int metric_other;
35};
36
37static int saved_value_cmp(struct rb_node *rb_node, const void *entry)
38{
39 struct saved_value *a = container_of(rb_node,
40 struct saved_value,
41 rb_node);
42 const struct saved_value *b = entry;
43
44 if (a->cpu != b->cpu)
45 return a->cpu - b->cpu;
46
47
48
49
50
51
52
53
54 if (a->type != b->type)
55 return a->type - b->type;
56
57 if (a->ctx != b->ctx)
58 return a->ctx - b->ctx;
59
60 if (a->evsel == NULL && b->evsel == NULL) {
61 if (a->stat == b->stat)
62 return 0;
63
64 if ((char *)a->stat < (char *)b->stat)
65 return -1;
66
67 return 1;
68 }
69
70 if (a->evsel == b->evsel)
71 return 0;
72 if ((char *)a->evsel < (char *)b->evsel)
73 return -1;
74 return +1;
75}
76
77static struct rb_node *saved_value_new(struct rblist *rblist __maybe_unused,
78 const void *entry)
79{
80 struct saved_value *nd = malloc(sizeof(struct saved_value));
81
82 if (!nd)
83 return NULL;
84 memcpy(nd, entry, sizeof(struct saved_value));
85 return &nd->rb_node;
86}
87
88static void saved_value_delete(struct rblist *rblist __maybe_unused,
89 struct rb_node *rb_node)
90{
91 struct saved_value *v;
92
93 BUG_ON(!rb_node);
94 v = container_of(rb_node, struct saved_value, rb_node);
95 free(v);
96}
97
98static struct saved_value *saved_value_lookup(struct evsel *evsel,
99 int cpu,
100 bool create,
101 enum stat_type type,
102 int ctx,
103 struct runtime_stat *st)
104{
105 struct rblist *rblist;
106 struct rb_node *nd;
107 struct saved_value dm = {
108 .cpu = cpu,
109 .evsel = evsel,
110 .type = type,
111 .ctx = ctx,
112 .stat = st,
113 };
114
115 rblist = &st->value_list;
116
117 nd = rblist__find(rblist, &dm);
118 if (nd)
119 return container_of(nd, struct saved_value, rb_node);
120 if (create) {
121 rblist__add_node(rblist, &dm);
122 nd = rblist__find(rblist, &dm);
123 if (nd)
124 return container_of(nd, struct saved_value, rb_node);
125 }
126 return NULL;
127}
128
129void runtime_stat__init(struct runtime_stat *st)
130{
131 struct rblist *rblist = &st->value_list;
132
133 rblist__init(rblist);
134 rblist->node_cmp = saved_value_cmp;
135 rblist->node_new = saved_value_new;
136 rblist->node_delete = saved_value_delete;
137}
138
139void runtime_stat__exit(struct runtime_stat *st)
140{
141 rblist__exit(&st->value_list);
142}
143
144void perf_stat__init_shadow_stats(void)
145{
146 runtime_stat__init(&rt_stat);
147}
148
149static int evsel_context(struct evsel *evsel)
150{
151 int ctx = 0;
152
153 if (evsel->core.attr.exclude_kernel)
154 ctx |= CTX_BIT_KERNEL;
155 if (evsel->core.attr.exclude_user)
156 ctx |= CTX_BIT_USER;
157 if (evsel->core.attr.exclude_hv)
158 ctx |= CTX_BIT_HV;
159 if (evsel->core.attr.exclude_host)
160 ctx |= CTX_BIT_HOST;
161 if (evsel->core.attr.exclude_idle)
162 ctx |= CTX_BIT_IDLE;
163
164 return ctx;
165}
166
167static void reset_stat(struct runtime_stat *st)
168{
169 struct rblist *rblist;
170 struct rb_node *pos, *next;
171
172 rblist = &st->value_list;
173 next = rb_first_cached(&rblist->entries);
174 while (next) {
175 pos = next;
176 next = rb_next(pos);
177 memset(&container_of(pos, struct saved_value, rb_node)->stats,
178 0,
179 sizeof(struct stats));
180 }
181}
182
183void perf_stat__reset_shadow_stats(void)
184{
185 reset_stat(&rt_stat);
186 memset(&walltime_nsecs_stats, 0, sizeof(walltime_nsecs_stats));
187}
188
189void perf_stat__reset_shadow_per_stat(struct runtime_stat *st)
190{
191 reset_stat(st);
192}
193
194static void update_runtime_stat(struct runtime_stat *st,
195 enum stat_type type,
196 int ctx, int cpu, u64 count)
197{
198 struct saved_value *v = saved_value_lookup(NULL, cpu, true,
199 type, ctx, st);
200
201 if (v)
202 update_stats(&v->stats, count);
203}
204
205
206
207
208
209
210void perf_stat__update_shadow_stats(struct evsel *counter, u64 count,
211 int cpu, struct runtime_stat *st)
212{
213 int ctx = evsel_context(counter);
214 u64 count_ns = count;
215 struct saved_value *v;
216
217 count *= counter->scale;
218
219 if (evsel__is_clock(counter))
220 update_runtime_stat(st, STAT_NSECS, 0, cpu, count_ns);
221 else if (evsel__match(counter, HARDWARE, HW_CPU_CYCLES))
222 update_runtime_stat(st, STAT_CYCLES, ctx, cpu, count);
223 else if (perf_stat_evsel__is(counter, CYCLES_IN_TX))
224 update_runtime_stat(st, STAT_CYCLES_IN_TX, ctx, cpu, count);
225 else if (perf_stat_evsel__is(counter, TRANSACTION_START))
226 update_runtime_stat(st, STAT_TRANSACTION, ctx, cpu, count);
227 else if (perf_stat_evsel__is(counter, ELISION_START))
228 update_runtime_stat(st, STAT_ELISION, ctx, cpu, count);
229 else if (perf_stat_evsel__is(counter, TOPDOWN_TOTAL_SLOTS))
230 update_runtime_stat(st, STAT_TOPDOWN_TOTAL_SLOTS,
231 ctx, cpu, count);
232 else if (perf_stat_evsel__is(counter, TOPDOWN_SLOTS_ISSUED))
233 update_runtime_stat(st, STAT_TOPDOWN_SLOTS_ISSUED,
234 ctx, cpu, count);
235 else if (perf_stat_evsel__is(counter, TOPDOWN_SLOTS_RETIRED))
236 update_runtime_stat(st, STAT_TOPDOWN_SLOTS_RETIRED,
237 ctx, cpu, count);
238 else if (perf_stat_evsel__is(counter, TOPDOWN_FETCH_BUBBLES))
239 update_runtime_stat(st, STAT_TOPDOWN_FETCH_BUBBLES,
240 ctx, cpu, count);
241 else if (perf_stat_evsel__is(counter, TOPDOWN_RECOVERY_BUBBLES))
242 update_runtime_stat(st, STAT_TOPDOWN_RECOVERY_BUBBLES,
243 ctx, cpu, count);
244 else if (evsel__match(counter, HARDWARE, HW_STALLED_CYCLES_FRONTEND))
245 update_runtime_stat(st, STAT_STALLED_CYCLES_FRONT,
246 ctx, cpu, count);
247 else if (evsel__match(counter, HARDWARE, HW_STALLED_CYCLES_BACKEND))
248 update_runtime_stat(st, STAT_STALLED_CYCLES_BACK,
249 ctx, cpu, count);
250 else if (evsel__match(counter, HARDWARE, HW_BRANCH_INSTRUCTIONS))
251 update_runtime_stat(st, STAT_BRANCHES, ctx, cpu, count);
252 else if (evsel__match(counter, HARDWARE, HW_CACHE_REFERENCES))
253 update_runtime_stat(st, STAT_CACHEREFS, ctx, cpu, count);
254 else if (evsel__match(counter, HW_CACHE, HW_CACHE_L1D))
255 update_runtime_stat(st, STAT_L1_DCACHE, ctx, cpu, count);
256 else if (evsel__match(counter, HW_CACHE, HW_CACHE_L1I))
257 update_runtime_stat(st, STAT_L1_ICACHE, ctx, cpu, count);
258 else if (evsel__match(counter, HW_CACHE, HW_CACHE_LL))
259 update_runtime_stat(st, STAT_LL_CACHE, ctx, cpu, count);
260 else if (evsel__match(counter, HW_CACHE, HW_CACHE_DTLB))
261 update_runtime_stat(st, STAT_DTLB_CACHE, ctx, cpu, count);
262 else if (evsel__match(counter, HW_CACHE, HW_CACHE_ITLB))
263 update_runtime_stat(st, STAT_ITLB_CACHE, ctx, cpu, count);
264 else if (perf_stat_evsel__is(counter, SMI_NUM))
265 update_runtime_stat(st, STAT_SMI_NUM, ctx, cpu, count);
266 else if (perf_stat_evsel__is(counter, APERF))
267 update_runtime_stat(st, STAT_APERF, ctx, cpu, count);
268
269 if (counter->collect_stat) {
270 v = saved_value_lookup(counter, cpu, true, STAT_NONE, 0, st);
271 update_stats(&v->stats, count);
272 if (counter->metric_leader)
273 v->metric_total += count;
274 } else if (counter->metric_leader) {
275 v = saved_value_lookup(counter->metric_leader,
276 cpu, true, STAT_NONE, 0, st);
277 v->metric_total += count;
278 v->metric_other++;
279 }
280}
281
282
283enum grc_type {
284 GRC_STALLED_CYCLES_FE,
285 GRC_STALLED_CYCLES_BE,
286 GRC_CACHE_MISSES,
287 GRC_MAX_NR
288};
289
290static const char *get_ratio_color(enum grc_type type, double ratio)
291{
292 static const double grc_table[GRC_MAX_NR][3] = {
293 [GRC_STALLED_CYCLES_FE] = { 50.0, 30.0, 10.0 },
294 [GRC_STALLED_CYCLES_BE] = { 75.0, 50.0, 20.0 },
295 [GRC_CACHE_MISSES] = { 20.0, 10.0, 5.0 },
296 };
297 const char *color = PERF_COLOR_NORMAL;
298
299 if (ratio > grc_table[type][0])
300 color = PERF_COLOR_RED;
301 else if (ratio > grc_table[type][1])
302 color = PERF_COLOR_MAGENTA;
303 else if (ratio > grc_table[type][2])
304 color = PERF_COLOR_YELLOW;
305
306 return color;
307}
308
309static struct evsel *perf_stat__find_event(struct evlist *evsel_list,
310 const char *name)
311{
312 struct evsel *c2;
313
314 evlist__for_each_entry (evsel_list, c2) {
315 if (!strcasecmp(c2->name, name) && !c2->collect_stat)
316 return c2;
317 }
318 return NULL;
319}
320
321
322void perf_stat__collect_metric_expr(struct evlist *evsel_list)
323{
324 struct evsel *counter, *leader, **metric_events, *oc;
325 bool found;
326 struct expr_parse_ctx ctx;
327 struct hashmap_entry *cur;
328 size_t bkt;
329 int i;
330
331 expr__ctx_init(&ctx);
332 evlist__for_each_entry(evsel_list, counter) {
333 bool invalid = false;
334
335 leader = counter->leader;
336 if (!counter->metric_expr)
337 continue;
338
339 expr__ctx_clear(&ctx);
340 metric_events = counter->metric_events;
341 if (!metric_events) {
342 if (expr__find_other(counter->metric_expr,
343 counter->name,
344 &ctx, 1) < 0)
345 continue;
346
347 metric_events = calloc(sizeof(struct evsel *),
348 hashmap__size(&ctx.ids) + 1);
349 if (!metric_events) {
350 expr__ctx_clear(&ctx);
351 return;
352 }
353 counter->metric_events = metric_events;
354 }
355
356 i = 0;
357 hashmap__for_each_entry((&ctx.ids), cur, bkt) {
358 const char *metric_name = (const char *)cur->key;
359
360 found = false;
361 if (leader) {
362
363 for_each_group_member (oc, leader) {
364 if (!strcasecmp(oc->name,
365 metric_name) &&
366 !oc->collect_stat) {
367 found = true;
368 break;
369 }
370 }
371 }
372 if (!found) {
373
374 oc = perf_stat__find_event(evsel_list,
375 metric_name);
376 }
377 if (!oc) {
378
379 static char *printed;
380
381
382
383
384
385
386
387
388 if (!printed ||
389 strcasecmp(printed, metric_name)) {
390 fprintf(stderr,
391 "Add %s event to groups to get metric expression for %s\n",
392 metric_name,
393 counter->name);
394 printed = strdup(metric_name);
395 }
396 invalid = true;
397 continue;
398 }
399 metric_events[i++] = oc;
400 oc->collect_stat = true;
401 }
402 metric_events[i] = NULL;
403 if (invalid) {
404 free(metric_events);
405 counter->metric_events = NULL;
406 counter->metric_expr = NULL;
407 }
408 }
409 expr__ctx_clear(&ctx);
410}
411
412static double runtime_stat_avg(struct runtime_stat *st,
413 enum stat_type type, int ctx, int cpu)
414{
415 struct saved_value *v;
416
417 v = saved_value_lookup(NULL, cpu, false, type, ctx, st);
418 if (!v)
419 return 0.0;
420
421 return avg_stats(&v->stats);
422}
423
424static double runtime_stat_n(struct runtime_stat *st,
425 enum stat_type type, int ctx, int cpu)
426{
427 struct saved_value *v;
428
429 v = saved_value_lookup(NULL, cpu, false, type, ctx, st);
430 if (!v)
431 return 0.0;
432
433 return v->stats.n;
434}
435
436static void print_stalled_cycles_frontend(struct perf_stat_config *config,
437 int cpu,
438 struct evsel *evsel, double avg,
439 struct perf_stat_output_ctx *out,
440 struct runtime_stat *st)
441{
442 double total, ratio = 0.0;
443 const char *color;
444 int ctx = evsel_context(evsel);
445
446 total = runtime_stat_avg(st, STAT_CYCLES, ctx, cpu);
447
448 if (total)
449 ratio = avg / total * 100.0;
450
451 color = get_ratio_color(GRC_STALLED_CYCLES_FE, ratio);
452
453 if (ratio)
454 out->print_metric(config, out->ctx, color, "%7.2f%%", "frontend cycles idle",
455 ratio);
456 else
457 out->print_metric(config, out->ctx, NULL, NULL, "frontend cycles idle", 0);
458}
459
460static void print_stalled_cycles_backend(struct perf_stat_config *config,
461 int cpu,
462 struct evsel *evsel, double avg,
463 struct perf_stat_output_ctx *out,
464 struct runtime_stat *st)
465{
466 double total, ratio = 0.0;
467 const char *color;
468 int ctx = evsel_context(evsel);
469
470 total = runtime_stat_avg(st, STAT_CYCLES, ctx, cpu);
471
472 if (total)
473 ratio = avg / total * 100.0;
474
475 color = get_ratio_color(GRC_STALLED_CYCLES_BE, ratio);
476
477 out->print_metric(config, out->ctx, color, "%7.2f%%", "backend cycles idle", ratio);
478}
479
480static void print_branch_misses(struct perf_stat_config *config,
481 int cpu,
482 struct evsel *evsel,
483 double avg,
484 struct perf_stat_output_ctx *out,
485 struct runtime_stat *st)
486{
487 double total, ratio = 0.0;
488 const char *color;
489 int ctx = evsel_context(evsel);
490
491 total = runtime_stat_avg(st, STAT_BRANCHES, ctx, cpu);
492
493 if (total)
494 ratio = avg / total * 100.0;
495
496 color = get_ratio_color(GRC_CACHE_MISSES, ratio);
497
498 out->print_metric(config, out->ctx, color, "%7.2f%%", "of all branches", ratio);
499}
500
501static void print_l1_dcache_misses(struct perf_stat_config *config,
502 int cpu,
503 struct evsel *evsel,
504 double avg,
505 struct perf_stat_output_ctx *out,
506 struct runtime_stat *st)
507
508{
509 double total, ratio = 0.0;
510 const char *color;
511 int ctx = evsel_context(evsel);
512
513 total = runtime_stat_avg(st, STAT_L1_DCACHE, ctx, cpu);
514
515 if (total)
516 ratio = avg / total * 100.0;
517
518 color = get_ratio_color(GRC_CACHE_MISSES, ratio);
519
520 out->print_metric(config, out->ctx, color, "%7.2f%%", "of all L1-dcache hits", ratio);
521}
522
523static void print_l1_icache_misses(struct perf_stat_config *config,
524 int cpu,
525 struct evsel *evsel,
526 double avg,
527 struct perf_stat_output_ctx *out,
528 struct runtime_stat *st)
529
530{
531 double total, ratio = 0.0;
532 const char *color;
533 int ctx = evsel_context(evsel);
534
535 total = runtime_stat_avg(st, STAT_L1_ICACHE, ctx, cpu);
536
537 if (total)
538 ratio = avg / total * 100.0;
539
540 color = get_ratio_color(GRC_CACHE_MISSES, ratio);
541 out->print_metric(config, out->ctx, color, "%7.2f%%", "of all L1-icache hits", ratio);
542}
543
544static void print_dtlb_cache_misses(struct perf_stat_config *config,
545 int cpu,
546 struct evsel *evsel,
547 double avg,
548 struct perf_stat_output_ctx *out,
549 struct runtime_stat *st)
550{
551 double total, ratio = 0.0;
552 const char *color;
553 int ctx = evsel_context(evsel);
554
555 total = runtime_stat_avg(st, STAT_DTLB_CACHE, ctx, cpu);
556
557 if (total)
558 ratio = avg / total * 100.0;
559
560 color = get_ratio_color(GRC_CACHE_MISSES, ratio);
561 out->print_metric(config, out->ctx, color, "%7.2f%%", "of all dTLB cache hits", ratio);
562}
563
564static void print_itlb_cache_misses(struct perf_stat_config *config,
565 int cpu,
566 struct evsel *evsel,
567 double avg,
568 struct perf_stat_output_ctx *out,
569 struct runtime_stat *st)
570{
571 double total, ratio = 0.0;
572 const char *color;
573 int ctx = evsel_context(evsel);
574
575 total = runtime_stat_avg(st, STAT_ITLB_CACHE, ctx, cpu);
576
577 if (total)
578 ratio = avg / total * 100.0;
579
580 color = get_ratio_color(GRC_CACHE_MISSES, ratio);
581 out->print_metric(config, out->ctx, color, "%7.2f%%", "of all iTLB cache hits", ratio);
582}
583
584static void print_ll_cache_misses(struct perf_stat_config *config,
585 int cpu,
586 struct evsel *evsel,
587 double avg,
588 struct perf_stat_output_ctx *out,
589 struct runtime_stat *st)
590{
591 double total, ratio = 0.0;
592 const char *color;
593 int ctx = evsel_context(evsel);
594
595 total = runtime_stat_avg(st, STAT_LL_CACHE, ctx, cpu);
596
597 if (total)
598 ratio = avg / total * 100.0;
599
600 color = get_ratio_color(GRC_CACHE_MISSES, ratio);
601 out->print_metric(config, out->ctx, color, "%7.2f%%", "of all LL-cache hits", ratio);
602}
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646static double sanitize_val(double x)
647{
648 if (x < 0 && x >= -0.02)
649 return 0.0;
650 return x;
651}
652
653static double td_total_slots(int ctx, int cpu, struct runtime_stat *st)
654{
655 return runtime_stat_avg(st, STAT_TOPDOWN_TOTAL_SLOTS, ctx, cpu);
656}
657
658static double td_bad_spec(int ctx, int cpu, struct runtime_stat *st)
659{
660 double bad_spec = 0;
661 double total_slots;
662 double total;
663
664 total = runtime_stat_avg(st, STAT_TOPDOWN_SLOTS_ISSUED, ctx, cpu) -
665 runtime_stat_avg(st, STAT_TOPDOWN_SLOTS_RETIRED, ctx, cpu) +
666 runtime_stat_avg(st, STAT_TOPDOWN_RECOVERY_BUBBLES, ctx, cpu);
667
668 total_slots = td_total_slots(ctx, cpu, st);
669 if (total_slots)
670 bad_spec = total / total_slots;
671 return sanitize_val(bad_spec);
672}
673
674static double td_retiring(int ctx, int cpu, struct runtime_stat *st)
675{
676 double retiring = 0;
677 double total_slots = td_total_slots(ctx, cpu, st);
678 double ret_slots = runtime_stat_avg(st, STAT_TOPDOWN_SLOTS_RETIRED,
679 ctx, cpu);
680
681 if (total_slots)
682 retiring = ret_slots / total_slots;
683 return retiring;
684}
685
686static double td_fe_bound(int ctx, int cpu, struct runtime_stat *st)
687{
688 double fe_bound = 0;
689 double total_slots = td_total_slots(ctx, cpu, st);
690 double fetch_bub = runtime_stat_avg(st, STAT_TOPDOWN_FETCH_BUBBLES,
691 ctx, cpu);
692
693 if (total_slots)
694 fe_bound = fetch_bub / total_slots;
695 return fe_bound;
696}
697
698static double td_be_bound(int ctx, int cpu, struct runtime_stat *st)
699{
700 double sum = (td_fe_bound(ctx, cpu, st) +
701 td_bad_spec(ctx, cpu, st) +
702 td_retiring(ctx, cpu, st));
703 if (sum == 0)
704 return 0;
705 return sanitize_val(1.0 - sum);
706}
707
708static void print_smi_cost(struct perf_stat_config *config,
709 int cpu, struct evsel *evsel,
710 struct perf_stat_output_ctx *out,
711 struct runtime_stat *st)
712{
713 double smi_num, aperf, cycles, cost = 0.0;
714 int ctx = evsel_context(evsel);
715 const char *color = NULL;
716
717 smi_num = runtime_stat_avg(st, STAT_SMI_NUM, ctx, cpu);
718 aperf = runtime_stat_avg(st, STAT_APERF, ctx, cpu);
719 cycles = runtime_stat_avg(st, STAT_CYCLES, ctx, cpu);
720
721 if ((cycles == 0) || (aperf == 0))
722 return;
723
724 if (smi_num)
725 cost = (aperf - cycles) / aperf * 100.00;
726
727 if (cost > 10)
728 color = PERF_COLOR_RED;
729 out->print_metric(config, out->ctx, color, "%8.1f%%", "SMI cycles%", cost);
730 out->print_metric(config, out->ctx, NULL, "%4.0f", "SMI#", smi_num);
731}
732
733static void generic_metric(struct perf_stat_config *config,
734 const char *metric_expr,
735 struct evsel **metric_events,
736 char *name,
737 const char *metric_name,
738 const char *metric_unit,
739 int runtime,
740 int cpu,
741 struct perf_stat_output_ctx *out,
742 struct runtime_stat *st)
743{
744 print_metric_t print_metric = out->print_metric;
745 struct expr_parse_ctx pctx;
746 double ratio, scale;
747 int i;
748 void *ctxp = out->ctx;
749 char *n, *pn;
750
751 expr__ctx_init(&pctx);
752 for (i = 0; metric_events[i]; i++) {
753 struct saved_value *v;
754 struct stats *stats;
755 u64 metric_total = 0;
756
757 if (!strcmp(metric_events[i]->name, "duration_time")) {
758 stats = &walltime_nsecs_stats;
759 scale = 1e-9;
760 } else {
761 v = saved_value_lookup(metric_events[i], cpu, false,
762 STAT_NONE, 0, st);
763 if (!v)
764 break;
765 stats = &v->stats;
766 scale = 1.0;
767
768 if (v->metric_other)
769 metric_total = v->metric_total;
770 }
771
772 n = strdup(metric_events[i]->name);
773 if (!n)
774 return;
775
776
777
778
779
780 pn = strchr(n, ' ');
781 if (pn)
782 *pn = 0;
783
784 if (metric_total)
785 expr__add_id(&pctx, n, metric_total);
786 else
787 expr__add_id(&pctx, n, avg_stats(stats)*scale);
788 }
789
790 if (!metric_events[i]) {
791 if (expr__parse(&ratio, &pctx, metric_expr, runtime) == 0) {
792 char *unit;
793 char metric_bf[64];
794
795 if (metric_unit && metric_name) {
796 if (perf_pmu__convert_scale(metric_unit,
797 &unit, &scale) >= 0) {
798 ratio *= scale;
799 }
800 if (strstr(metric_expr, "?"))
801 scnprintf(metric_bf, sizeof(metric_bf),
802 "%s %s_%d", unit, metric_name, runtime);
803 else
804 scnprintf(metric_bf, sizeof(metric_bf),
805 "%s %s", unit, metric_name);
806
807 print_metric(config, ctxp, NULL, "%8.1f",
808 metric_bf, ratio);
809 } else {
810 print_metric(config, ctxp, NULL, "%8.2f",
811 metric_name ?
812 metric_name :
813 out->force_header ? name : "",
814 ratio);
815 }
816 } else {
817 print_metric(config, ctxp, NULL, NULL,
818 out->force_header ?
819 (metric_name ? metric_name : name) : "", 0);
820 }
821 } else {
822 print_metric(config, ctxp, NULL, NULL,
823 out->force_header ?
824 (metric_name ? metric_name : name) : "", 0);
825 }
826
827 expr__ctx_clear(&pctx);
828}
829
830void perf_stat__print_shadow_stats(struct perf_stat_config *config,
831 struct evsel *evsel,
832 double avg, int cpu,
833 struct perf_stat_output_ctx *out,
834 struct rblist *metric_events,
835 struct runtime_stat *st)
836{
837 void *ctxp = out->ctx;
838 print_metric_t print_metric = out->print_metric;
839 double total, ratio = 0.0, total2;
840 const char *color = NULL;
841 int ctx = evsel_context(evsel);
842 struct metric_event *me;
843 int num = 1;
844
845 if (evsel__match(evsel, HARDWARE, HW_INSTRUCTIONS)) {
846 total = runtime_stat_avg(st, STAT_CYCLES, ctx, cpu);
847
848 if (total) {
849 ratio = avg / total;
850 print_metric(config, ctxp, NULL, "%7.2f ",
851 "insn per cycle", ratio);
852 } else {
853 print_metric(config, ctxp, NULL, NULL, "insn per cycle", 0);
854 }
855
856 total = runtime_stat_avg(st, STAT_STALLED_CYCLES_FRONT,
857 ctx, cpu);
858
859 total = max(total, runtime_stat_avg(st,
860 STAT_STALLED_CYCLES_BACK,
861 ctx, cpu));
862
863 if (total && avg) {
864 out->new_line(config, ctxp);
865 ratio = total / avg;
866 print_metric(config, ctxp, NULL, "%7.2f ",
867 "stalled cycles per insn",
868 ratio);
869 }
870 } else if (evsel__match(evsel, HARDWARE, HW_BRANCH_MISSES)) {
871 if (runtime_stat_n(st, STAT_BRANCHES, ctx, cpu) != 0)
872 print_branch_misses(config, cpu, evsel, avg, out, st);
873 else
874 print_metric(config, ctxp, NULL, NULL, "of all branches", 0);
875 } else if (
876 evsel->core.attr.type == PERF_TYPE_HW_CACHE &&
877 evsel->core.attr.config == ( PERF_COUNT_HW_CACHE_L1D |
878 ((PERF_COUNT_HW_CACHE_OP_READ) << 8) |
879 ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) {
880
881 if (runtime_stat_n(st, STAT_L1_DCACHE, ctx, cpu) != 0)
882 print_l1_dcache_misses(config, cpu, evsel, avg, out, st);
883 else
884 print_metric(config, ctxp, NULL, NULL, "of all L1-dcache hits", 0);
885 } else if (
886 evsel->core.attr.type == PERF_TYPE_HW_CACHE &&
887 evsel->core.attr.config == ( PERF_COUNT_HW_CACHE_L1I |
888 ((PERF_COUNT_HW_CACHE_OP_READ) << 8) |
889 ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) {
890
891 if (runtime_stat_n(st, STAT_L1_ICACHE, ctx, cpu) != 0)
892 print_l1_icache_misses(config, cpu, evsel, avg, out, st);
893 else
894 print_metric(config, ctxp, NULL, NULL, "of all L1-icache hits", 0);
895 } else if (
896 evsel->core.attr.type == PERF_TYPE_HW_CACHE &&
897 evsel->core.attr.config == ( PERF_COUNT_HW_CACHE_DTLB |
898 ((PERF_COUNT_HW_CACHE_OP_READ) << 8) |
899 ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) {
900
901 if (runtime_stat_n(st, STAT_DTLB_CACHE, ctx, cpu) != 0)
902 print_dtlb_cache_misses(config, cpu, evsel, avg, out, st);
903 else
904 print_metric(config, ctxp, NULL, NULL, "of all dTLB cache hits", 0);
905 } else if (
906 evsel->core.attr.type == PERF_TYPE_HW_CACHE &&
907 evsel->core.attr.config == ( PERF_COUNT_HW_CACHE_ITLB |
908 ((PERF_COUNT_HW_CACHE_OP_READ) << 8) |
909 ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) {
910
911 if (runtime_stat_n(st, STAT_ITLB_CACHE, ctx, cpu) != 0)
912 print_itlb_cache_misses(config, cpu, evsel, avg, out, st);
913 else
914 print_metric(config, ctxp, NULL, NULL, "of all iTLB cache hits", 0);
915 } else if (
916 evsel->core.attr.type == PERF_TYPE_HW_CACHE &&
917 evsel->core.attr.config == ( PERF_COUNT_HW_CACHE_LL |
918 ((PERF_COUNT_HW_CACHE_OP_READ) << 8) |
919 ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) {
920
921 if (runtime_stat_n(st, STAT_LL_CACHE, ctx, cpu) != 0)
922 print_ll_cache_misses(config, cpu, evsel, avg, out, st);
923 else
924 print_metric(config, ctxp, NULL, NULL, "of all LL-cache hits", 0);
925 } else if (evsel__match(evsel, HARDWARE, HW_CACHE_MISSES)) {
926 total = runtime_stat_avg(st, STAT_CACHEREFS, ctx, cpu);
927
928 if (total)
929 ratio = avg * 100 / total;
930
931 if (runtime_stat_n(st, STAT_CACHEREFS, ctx, cpu) != 0)
932 print_metric(config, ctxp, NULL, "%8.3f %%",
933 "of all cache refs", ratio);
934 else
935 print_metric(config, ctxp, NULL, NULL, "of all cache refs", 0);
936 } else if (evsel__match(evsel, HARDWARE, HW_STALLED_CYCLES_FRONTEND)) {
937 print_stalled_cycles_frontend(config, cpu, evsel, avg, out, st);
938 } else if (evsel__match(evsel, HARDWARE, HW_STALLED_CYCLES_BACKEND)) {
939 print_stalled_cycles_backend(config, cpu, evsel, avg, out, st);
940 } else if (evsel__match(evsel, HARDWARE, HW_CPU_CYCLES)) {
941 total = runtime_stat_avg(st, STAT_NSECS, 0, cpu);
942
943 if (total) {
944 ratio = avg / total;
945 print_metric(config, ctxp, NULL, "%8.3f", "GHz", ratio);
946 } else {
947 print_metric(config, ctxp, NULL, NULL, "Ghz", 0);
948 }
949 } else if (perf_stat_evsel__is(evsel, CYCLES_IN_TX)) {
950 total = runtime_stat_avg(st, STAT_CYCLES, ctx, cpu);
951
952 if (total)
953 print_metric(config, ctxp, NULL,
954 "%7.2f%%", "transactional cycles",
955 100.0 * (avg / total));
956 else
957 print_metric(config, ctxp, NULL, NULL, "transactional cycles",
958 0);
959 } else if (perf_stat_evsel__is(evsel, CYCLES_IN_TX_CP)) {
960 total = runtime_stat_avg(st, STAT_CYCLES, ctx, cpu);
961 total2 = runtime_stat_avg(st, STAT_CYCLES_IN_TX, ctx, cpu);
962
963 if (total2 < avg)
964 total2 = avg;
965 if (total)
966 print_metric(config, ctxp, NULL, "%7.2f%%", "aborted cycles",
967 100.0 * ((total2-avg) / total));
968 else
969 print_metric(config, ctxp, NULL, NULL, "aborted cycles", 0);
970 } else if (perf_stat_evsel__is(evsel, TRANSACTION_START)) {
971 total = runtime_stat_avg(st, STAT_CYCLES_IN_TX,
972 ctx, cpu);
973
974 if (avg)
975 ratio = total / avg;
976
977 if (runtime_stat_n(st, STAT_CYCLES_IN_TX, ctx, cpu) != 0)
978 print_metric(config, ctxp, NULL, "%8.0f",
979 "cycles / transaction", ratio);
980 else
981 print_metric(config, ctxp, NULL, NULL, "cycles / transaction",
982 0);
983 } else if (perf_stat_evsel__is(evsel, ELISION_START)) {
984 total = runtime_stat_avg(st, STAT_CYCLES_IN_TX,
985 ctx, cpu);
986
987 if (avg)
988 ratio = total / avg;
989
990 print_metric(config, ctxp, NULL, "%8.0f", "cycles / elision", ratio);
991 } else if (evsel__is_clock(evsel)) {
992 if ((ratio = avg_stats(&walltime_nsecs_stats)) != 0)
993 print_metric(config, ctxp, NULL, "%8.3f", "CPUs utilized",
994 avg / (ratio * evsel->scale));
995 else
996 print_metric(config, ctxp, NULL, NULL, "CPUs utilized", 0);
997 } else if (perf_stat_evsel__is(evsel, TOPDOWN_FETCH_BUBBLES)) {
998 double fe_bound = td_fe_bound(ctx, cpu, st);
999
1000 if (fe_bound > 0.2)
1001 color = PERF_COLOR_RED;
1002 print_metric(config, ctxp, color, "%8.1f%%", "frontend bound",
1003 fe_bound * 100.);
1004 } else if (perf_stat_evsel__is(evsel, TOPDOWN_SLOTS_RETIRED)) {
1005 double retiring = td_retiring(ctx, cpu, st);
1006
1007 if (retiring > 0.7)
1008 color = PERF_COLOR_GREEN;
1009 print_metric(config, ctxp, color, "%8.1f%%", "retiring",
1010 retiring * 100.);
1011 } else if (perf_stat_evsel__is(evsel, TOPDOWN_RECOVERY_BUBBLES)) {
1012 double bad_spec = td_bad_spec(ctx, cpu, st);
1013
1014 if (bad_spec > 0.1)
1015 color = PERF_COLOR_RED;
1016 print_metric(config, ctxp, color, "%8.1f%%", "bad speculation",
1017 bad_spec * 100.);
1018 } else if (perf_stat_evsel__is(evsel, TOPDOWN_SLOTS_ISSUED)) {
1019 double be_bound = td_be_bound(ctx, cpu, st);
1020 const char *name = "backend bound";
1021 static int have_recovery_bubbles = -1;
1022
1023
1024 if (have_recovery_bubbles < 0)
1025 have_recovery_bubbles = pmu_have_event("cpu",
1026 "topdown-recovery-bubbles");
1027 if (!have_recovery_bubbles)
1028 name = "backend bound/bad spec";
1029
1030 if (be_bound > 0.2)
1031 color = PERF_COLOR_RED;
1032 if (td_total_slots(ctx, cpu, st) > 0)
1033 print_metric(config, ctxp, color, "%8.1f%%", name,
1034 be_bound * 100.);
1035 else
1036 print_metric(config, ctxp, NULL, NULL, name, 0);
1037 } else if (evsel->metric_expr) {
1038 generic_metric(config, evsel->metric_expr, evsel->metric_events, evsel->name,
1039 evsel->metric_name, NULL, 1, cpu, out, st);
1040 } else if (runtime_stat_n(st, STAT_NSECS, 0, cpu) != 0) {
1041 char unit = 'M';
1042 char unit_buf[10];
1043
1044 total = runtime_stat_avg(st, STAT_NSECS, 0, cpu);
1045
1046 if (total)
1047 ratio = 1000.0 * avg / total;
1048 if (ratio < 0.001) {
1049 ratio *= 1000;
1050 unit = 'K';
1051 }
1052 snprintf(unit_buf, sizeof(unit_buf), "%c/sec", unit);
1053 print_metric(config, ctxp, NULL, "%8.3f", unit_buf, ratio);
1054 } else if (perf_stat_evsel__is(evsel, SMI_NUM)) {
1055 print_smi_cost(config, cpu, evsel, out, st);
1056 } else {
1057 num = 0;
1058 }
1059
1060 if ((me = metricgroup__lookup(metric_events, evsel, false)) != NULL) {
1061 struct metric_expr *mexp;
1062
1063 list_for_each_entry (mexp, &me->head, nd) {
1064 if (num++ > 0)
1065 out->new_line(config, ctxp);
1066 generic_metric(config, mexp->metric_expr, mexp->metric_events,
1067 evsel->name, mexp->metric_name,
1068 mexp->metric_unit, mexp->runtime, cpu, out, st);
1069 }
1070 }
1071 if (num == 0)
1072 print_metric(config, ctxp, NULL, NULL, NULL, 0);
1073}
1074