Merge branch 'x86-pti-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux/fpc-iii.git] / tools / perf / builtin-c2c.c
blob539c3d4601586ab925549e433ee0e5f0c954ae43
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * This is rewrite of original c2c tool introduced in here:
4 * http://lwn.net/Articles/588866/
6 * The original tool was changed to fit in current perf state.
8 * Original authors:
9 * Don Zickus <dzickus@redhat.com>
10 * Dick Fowles <fowles@inreach.com>
11 * Joe Mario <jmario@redhat.com>
13 #include <errno.h>
14 #include <inttypes.h>
15 #include <linux/compiler.h>
16 #include <linux/kernel.h>
17 #include <linux/stringify.h>
18 #include <asm/bug.h>
19 #include <sys/param.h>
20 #include "util.h"
21 #include "debug.h"
22 #include "builtin.h"
23 #include <subcmd/parse-options.h>
24 #include "mem-events.h"
25 #include "session.h"
26 #include "hist.h"
27 #include "sort.h"
28 #include "tool.h"
29 #include "data.h"
30 #include "event.h"
31 #include "evlist.h"
32 #include "evsel.h"
33 #include "ui/browsers/hists.h"
34 #include "thread.h"
36 struct c2c_hists {
37 struct hists hists;
38 struct perf_hpp_list list;
39 struct c2c_stats stats;
42 struct compute_stats {
43 struct stats lcl_hitm;
44 struct stats rmt_hitm;
45 struct stats load;
48 struct c2c_hist_entry {
49 struct c2c_hists *hists;
50 struct c2c_stats stats;
51 unsigned long *cpuset;
52 struct c2c_stats *node_stats;
53 unsigned int cacheline_idx;
55 struct compute_stats cstats;
58 * must be at the end,
59 * because of its callchain dynamic entry
61 struct hist_entry he;
64 static char const *coalesce_default = "pid,iaddr";
66 struct perf_c2c {
67 struct perf_tool tool;
68 struct c2c_hists hists;
70 unsigned long **nodes;
71 int nodes_cnt;
72 int cpus_cnt;
73 int *cpu2node;
74 int node_info;
76 bool show_src;
77 bool show_all;
78 bool use_stdio;
79 bool stats_only;
80 bool symbol_full;
82 /* HITM shared clines stats */
83 struct c2c_stats hitm_stats;
84 int shared_clines;
86 int display;
88 const char *coalesce;
89 char *cl_sort;
90 char *cl_resort;
91 char *cl_output;
94 enum {
95 DISPLAY_LCL,
96 DISPLAY_RMT,
97 DISPLAY_TOT,
98 DISPLAY_MAX,
101 static const char *display_str[DISPLAY_MAX] = {
102 [DISPLAY_LCL] = "Local",
103 [DISPLAY_RMT] = "Remote",
104 [DISPLAY_TOT] = "Total",
107 static const struct option c2c_options[] = {
108 OPT_INCR('v', "verbose", &verbose, "be more verbose (show counter open errors, etc)"),
109 OPT_END()
112 static struct perf_c2c c2c;
114 static void *c2c_he_zalloc(size_t size)
116 struct c2c_hist_entry *c2c_he;
118 c2c_he = zalloc(size + sizeof(*c2c_he));
119 if (!c2c_he)
120 return NULL;
122 c2c_he->cpuset = bitmap_alloc(c2c.cpus_cnt);
123 if (!c2c_he->cpuset)
124 return NULL;
126 c2c_he->node_stats = zalloc(c2c.nodes_cnt * sizeof(*c2c_he->node_stats));
127 if (!c2c_he->node_stats)
128 return NULL;
130 init_stats(&c2c_he->cstats.lcl_hitm);
131 init_stats(&c2c_he->cstats.rmt_hitm);
132 init_stats(&c2c_he->cstats.load);
134 return &c2c_he->he;
137 static void c2c_he_free(void *he)
139 struct c2c_hist_entry *c2c_he;
141 c2c_he = container_of(he, struct c2c_hist_entry, he);
142 if (c2c_he->hists) {
143 hists__delete_entries(&c2c_he->hists->hists);
144 free(c2c_he->hists);
147 free(c2c_he->cpuset);
148 free(c2c_he->node_stats);
149 free(c2c_he);
152 static struct hist_entry_ops c2c_entry_ops = {
153 .new = c2c_he_zalloc,
154 .free = c2c_he_free,
157 static int c2c_hists__init(struct c2c_hists *hists,
158 const char *sort,
159 int nr_header_lines);
161 static struct c2c_hists*
162 he__get_c2c_hists(struct hist_entry *he,
163 const char *sort,
164 int nr_header_lines)
166 struct c2c_hist_entry *c2c_he;
167 struct c2c_hists *hists;
168 int ret;
170 c2c_he = container_of(he, struct c2c_hist_entry, he);
171 if (c2c_he->hists)
172 return c2c_he->hists;
174 hists = c2c_he->hists = zalloc(sizeof(*hists));
175 if (!hists)
176 return NULL;
178 ret = c2c_hists__init(hists, sort, nr_header_lines);
179 if (ret) {
180 free(hists);
181 return NULL;
184 return hists;
187 static void c2c_he__set_cpu(struct c2c_hist_entry *c2c_he,
188 struct perf_sample *sample)
190 if (WARN_ONCE(sample->cpu == (unsigned int) -1,
191 "WARNING: no sample cpu value"))
192 return;
194 set_bit(sample->cpu, c2c_he->cpuset);
197 static void compute_stats(struct c2c_hist_entry *c2c_he,
198 struct c2c_stats *stats,
199 u64 weight)
201 struct compute_stats *cstats = &c2c_he->cstats;
203 if (stats->rmt_hitm)
204 update_stats(&cstats->rmt_hitm, weight);
205 else if (stats->lcl_hitm)
206 update_stats(&cstats->lcl_hitm, weight);
207 else if (stats->load)
208 update_stats(&cstats->load, weight);
211 static int process_sample_event(struct perf_tool *tool __maybe_unused,
212 union perf_event *event,
213 struct perf_sample *sample,
214 struct perf_evsel *evsel,
215 struct machine *machine)
217 struct c2c_hists *c2c_hists = &c2c.hists;
218 struct c2c_hist_entry *c2c_he;
219 struct c2c_stats stats = { .nr_entries = 0, };
220 struct hist_entry *he;
221 struct addr_location al;
222 struct mem_info *mi, *mi_dup;
223 int ret;
225 if (machine__resolve(machine, &al, sample) < 0) {
226 pr_debug("problem processing %d event, skipping it.\n",
227 event->header.type);
228 return -1;
231 ret = sample__resolve_callchain(sample, &callchain_cursor, NULL,
232 evsel, &al, sysctl_perf_event_max_stack);
233 if (ret)
234 goto out;
236 mi = sample__resolve_mem(sample, &al);
237 if (mi == NULL)
238 return -ENOMEM;
240 mi_dup = memdup(mi, sizeof(*mi));
241 if (!mi_dup)
242 goto free_mi;
244 c2c_decode_stats(&stats, mi);
246 he = hists__add_entry_ops(&c2c_hists->hists, &c2c_entry_ops,
247 &al, NULL, NULL, mi,
248 sample, true);
249 if (he == NULL)
250 goto free_mi_dup;
252 c2c_he = container_of(he, struct c2c_hist_entry, he);
253 c2c_add_stats(&c2c_he->stats, &stats);
254 c2c_add_stats(&c2c_hists->stats, &stats);
256 c2c_he__set_cpu(c2c_he, sample);
258 hists__inc_nr_samples(&c2c_hists->hists, he->filtered);
259 ret = hist_entry__append_callchain(he, sample);
261 if (!ret) {
263 * There's already been warning about missing
264 * sample's cpu value. Let's account all to
265 * node 0 in this case, without any further
266 * warning.
268 * Doing node stats only for single callchain data.
270 int cpu = sample->cpu == (unsigned int) -1 ? 0 : sample->cpu;
271 int node = c2c.cpu2node[cpu];
273 mi = mi_dup;
275 mi_dup = memdup(mi, sizeof(*mi));
276 if (!mi_dup)
277 goto free_mi;
279 c2c_hists = he__get_c2c_hists(he, c2c.cl_sort, 2);
280 if (!c2c_hists)
281 goto free_mi_dup;
283 he = hists__add_entry_ops(&c2c_hists->hists, &c2c_entry_ops,
284 &al, NULL, NULL, mi,
285 sample, true);
286 if (he == NULL)
287 goto free_mi_dup;
289 c2c_he = container_of(he, struct c2c_hist_entry, he);
290 c2c_add_stats(&c2c_he->stats, &stats);
291 c2c_add_stats(&c2c_hists->stats, &stats);
292 c2c_add_stats(&c2c_he->node_stats[node], &stats);
294 compute_stats(c2c_he, &stats, sample->weight);
296 c2c_he__set_cpu(c2c_he, sample);
298 hists__inc_nr_samples(&c2c_hists->hists, he->filtered);
299 ret = hist_entry__append_callchain(he, sample);
302 out:
303 addr_location__put(&al);
304 return ret;
306 free_mi_dup:
307 free(mi_dup);
308 free_mi:
309 free(mi);
310 ret = -ENOMEM;
311 goto out;
314 static struct perf_c2c c2c = {
315 .tool = {
316 .sample = process_sample_event,
317 .mmap = perf_event__process_mmap,
318 .mmap2 = perf_event__process_mmap2,
319 .comm = perf_event__process_comm,
320 .exit = perf_event__process_exit,
321 .fork = perf_event__process_fork,
322 .lost = perf_event__process_lost,
323 .ordered_events = true,
324 .ordering_requires_timestamps = true,
328 static const char * const c2c_usage[] = {
329 "perf c2c {record|report}",
330 NULL
333 static const char * const __usage_report[] = {
334 "perf c2c report",
335 NULL
338 static const char * const *report_c2c_usage = __usage_report;
340 #define C2C_HEADER_MAX 2
342 struct c2c_header {
343 struct {
344 const char *text;
345 int span;
346 } line[C2C_HEADER_MAX];
349 struct c2c_dimension {
350 struct c2c_header header;
351 const char *name;
352 int width;
353 struct sort_entry *se;
355 int64_t (*cmp)(struct perf_hpp_fmt *fmt,
356 struct hist_entry *, struct hist_entry *);
357 int (*entry)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
358 struct hist_entry *he);
359 int (*color)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
360 struct hist_entry *he);
363 struct c2c_fmt {
364 struct perf_hpp_fmt fmt;
365 struct c2c_dimension *dim;
368 #define SYMBOL_WIDTH 30
370 static struct c2c_dimension dim_symbol;
371 static struct c2c_dimension dim_srcline;
373 static int symbol_width(struct hists *hists, struct sort_entry *se)
375 int width = hists__col_len(hists, se->se_width_idx);
377 if (!c2c.symbol_full)
378 width = MIN(width, SYMBOL_WIDTH);
380 return width;
383 static int c2c_width(struct perf_hpp_fmt *fmt,
384 struct perf_hpp *hpp __maybe_unused,
385 struct hists *hists)
387 struct c2c_fmt *c2c_fmt;
388 struct c2c_dimension *dim;
390 c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
391 dim = c2c_fmt->dim;
393 if (dim == &dim_symbol || dim == &dim_srcline)
394 return symbol_width(hists, dim->se);
396 return dim->se ? hists__col_len(hists, dim->se->se_width_idx) :
397 c2c_fmt->dim->width;
400 static int c2c_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
401 struct hists *hists, int line, int *span)
403 struct perf_hpp_list *hpp_list = hists->hpp_list;
404 struct c2c_fmt *c2c_fmt;
405 struct c2c_dimension *dim;
406 const char *text = NULL;
407 int width = c2c_width(fmt, hpp, hists);
409 c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
410 dim = c2c_fmt->dim;
412 if (dim->se) {
413 text = dim->header.line[line].text;
414 /* Use the last line from sort_entry if not defined. */
415 if (!text && (line == hpp_list->nr_header_lines - 1))
416 text = dim->se->se_header;
417 } else {
418 text = dim->header.line[line].text;
420 if (*span) {
421 (*span)--;
422 return 0;
423 } else {
424 *span = dim->header.line[line].span;
428 if (text == NULL)
429 text = "";
431 return scnprintf(hpp->buf, hpp->size, "%*s", width, text);
434 #define HEX_STR(__s, __v) \
435 ({ \
436 scnprintf(__s, sizeof(__s), "0x%" PRIx64, __v); \
437 __s; \
440 static int64_t
441 dcacheline_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
442 struct hist_entry *left, struct hist_entry *right)
444 return sort__dcacheline_cmp(left, right);
447 static int dcacheline_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
448 struct hist_entry *he)
450 uint64_t addr = 0;
451 int width = c2c_width(fmt, hpp, he->hists);
452 char buf[20];
454 if (he->mem_info)
455 addr = cl_address(he->mem_info->daddr.addr);
457 return scnprintf(hpp->buf, hpp->size, "%*s", width, HEX_STR(buf, addr));
460 static int offset_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
461 struct hist_entry *he)
463 uint64_t addr = 0;
464 int width = c2c_width(fmt, hpp, he->hists);
465 char buf[20];
467 if (he->mem_info)
468 addr = cl_offset(he->mem_info->daddr.al_addr);
470 return scnprintf(hpp->buf, hpp->size, "%*s", width, HEX_STR(buf, addr));
473 static int64_t
474 offset_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
475 struct hist_entry *left, struct hist_entry *right)
477 uint64_t l = 0, r = 0;
479 if (left->mem_info)
480 l = cl_offset(left->mem_info->daddr.addr);
481 if (right->mem_info)
482 r = cl_offset(right->mem_info->daddr.addr);
484 return (int64_t)(r - l);
487 static int
488 iaddr_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
489 struct hist_entry *he)
491 uint64_t addr = 0;
492 int width = c2c_width(fmt, hpp, he->hists);
493 char buf[20];
495 if (he->mem_info)
496 addr = he->mem_info->iaddr.addr;
498 return scnprintf(hpp->buf, hpp->size, "%*s", width, HEX_STR(buf, addr));
501 static int64_t
502 iaddr_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
503 struct hist_entry *left, struct hist_entry *right)
505 return sort__iaddr_cmp(left, right);
508 static int
509 tot_hitm_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
510 struct hist_entry *he)
512 struct c2c_hist_entry *c2c_he;
513 int width = c2c_width(fmt, hpp, he->hists);
514 unsigned int tot_hitm;
516 c2c_he = container_of(he, struct c2c_hist_entry, he);
517 tot_hitm = c2c_he->stats.lcl_hitm + c2c_he->stats.rmt_hitm;
519 return scnprintf(hpp->buf, hpp->size, "%*u", width, tot_hitm);
522 static int64_t
523 tot_hitm_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
524 struct hist_entry *left, struct hist_entry *right)
526 struct c2c_hist_entry *c2c_left;
527 struct c2c_hist_entry *c2c_right;
528 unsigned int tot_hitm_left;
529 unsigned int tot_hitm_right;
531 c2c_left = container_of(left, struct c2c_hist_entry, he);
532 c2c_right = container_of(right, struct c2c_hist_entry, he);
534 tot_hitm_left = c2c_left->stats.lcl_hitm + c2c_left->stats.rmt_hitm;
535 tot_hitm_right = c2c_right->stats.lcl_hitm + c2c_right->stats.rmt_hitm;
537 return tot_hitm_left - tot_hitm_right;
540 #define STAT_FN_ENTRY(__f) \
541 static int \
542 __f ## _entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, \
543 struct hist_entry *he) \
545 struct c2c_hist_entry *c2c_he; \
546 int width = c2c_width(fmt, hpp, he->hists); \
548 c2c_he = container_of(he, struct c2c_hist_entry, he); \
549 return scnprintf(hpp->buf, hpp->size, "%*u", width, \
550 c2c_he->stats.__f); \
553 #define STAT_FN_CMP(__f) \
554 static int64_t \
555 __f ## _cmp(struct perf_hpp_fmt *fmt __maybe_unused, \
556 struct hist_entry *left, struct hist_entry *right) \
558 struct c2c_hist_entry *c2c_left, *c2c_right; \
560 c2c_left = container_of(left, struct c2c_hist_entry, he); \
561 c2c_right = container_of(right, struct c2c_hist_entry, he); \
562 return c2c_left->stats.__f - c2c_right->stats.__f; \
565 #define STAT_FN(__f) \
566 STAT_FN_ENTRY(__f) \
567 STAT_FN_CMP(__f)
569 STAT_FN(rmt_hitm)
570 STAT_FN(lcl_hitm)
571 STAT_FN(store)
572 STAT_FN(st_l1hit)
573 STAT_FN(st_l1miss)
574 STAT_FN(ld_fbhit)
575 STAT_FN(ld_l1hit)
576 STAT_FN(ld_l2hit)
577 STAT_FN(ld_llchit)
578 STAT_FN(rmt_hit)
580 static uint64_t llc_miss(struct c2c_stats *stats)
582 uint64_t llcmiss;
584 llcmiss = stats->lcl_dram +
585 stats->rmt_dram +
586 stats->rmt_hitm +
587 stats->rmt_hit;
589 return llcmiss;
592 static int
593 ld_llcmiss_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
594 struct hist_entry *he)
596 struct c2c_hist_entry *c2c_he;
597 int width = c2c_width(fmt, hpp, he->hists);
599 c2c_he = container_of(he, struct c2c_hist_entry, he);
601 return scnprintf(hpp->buf, hpp->size, "%*lu", width,
602 llc_miss(&c2c_he->stats));
605 static int64_t
606 ld_llcmiss_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
607 struct hist_entry *left, struct hist_entry *right)
609 struct c2c_hist_entry *c2c_left;
610 struct c2c_hist_entry *c2c_right;
612 c2c_left = container_of(left, struct c2c_hist_entry, he);
613 c2c_right = container_of(right, struct c2c_hist_entry, he);
615 return llc_miss(&c2c_left->stats) - llc_miss(&c2c_right->stats);
618 static uint64_t total_records(struct c2c_stats *stats)
620 uint64_t lclmiss, ldcnt, total;
622 lclmiss = stats->lcl_dram +
623 stats->rmt_dram +
624 stats->rmt_hitm +
625 stats->rmt_hit;
627 ldcnt = lclmiss +
628 stats->ld_fbhit +
629 stats->ld_l1hit +
630 stats->ld_l2hit +
631 stats->ld_llchit +
632 stats->lcl_hitm;
634 total = ldcnt +
635 stats->st_l1hit +
636 stats->st_l1miss;
638 return total;
641 static int
642 tot_recs_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
643 struct hist_entry *he)
645 struct c2c_hist_entry *c2c_he;
646 int width = c2c_width(fmt, hpp, he->hists);
647 uint64_t tot_recs;
649 c2c_he = container_of(he, struct c2c_hist_entry, he);
650 tot_recs = total_records(&c2c_he->stats);
652 return scnprintf(hpp->buf, hpp->size, "%*" PRIu64, width, tot_recs);
655 static int64_t
656 tot_recs_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
657 struct hist_entry *left, struct hist_entry *right)
659 struct c2c_hist_entry *c2c_left;
660 struct c2c_hist_entry *c2c_right;
661 uint64_t tot_recs_left;
662 uint64_t tot_recs_right;
664 c2c_left = container_of(left, struct c2c_hist_entry, he);
665 c2c_right = container_of(right, struct c2c_hist_entry, he);
667 tot_recs_left = total_records(&c2c_left->stats);
668 tot_recs_right = total_records(&c2c_right->stats);
670 return tot_recs_left - tot_recs_right;
673 static uint64_t total_loads(struct c2c_stats *stats)
675 uint64_t lclmiss, ldcnt;
677 lclmiss = stats->lcl_dram +
678 stats->rmt_dram +
679 stats->rmt_hitm +
680 stats->rmt_hit;
682 ldcnt = lclmiss +
683 stats->ld_fbhit +
684 stats->ld_l1hit +
685 stats->ld_l2hit +
686 stats->ld_llchit +
687 stats->lcl_hitm;
689 return ldcnt;
692 static int
693 tot_loads_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
694 struct hist_entry *he)
696 struct c2c_hist_entry *c2c_he;
697 int width = c2c_width(fmt, hpp, he->hists);
698 uint64_t tot_recs;
700 c2c_he = container_of(he, struct c2c_hist_entry, he);
701 tot_recs = total_loads(&c2c_he->stats);
703 return scnprintf(hpp->buf, hpp->size, "%*" PRIu64, width, tot_recs);
706 static int64_t
707 tot_loads_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
708 struct hist_entry *left, struct hist_entry *right)
710 struct c2c_hist_entry *c2c_left;
711 struct c2c_hist_entry *c2c_right;
712 uint64_t tot_recs_left;
713 uint64_t tot_recs_right;
715 c2c_left = container_of(left, struct c2c_hist_entry, he);
716 c2c_right = container_of(right, struct c2c_hist_entry, he);
718 tot_recs_left = total_loads(&c2c_left->stats);
719 tot_recs_right = total_loads(&c2c_right->stats);
721 return tot_recs_left - tot_recs_right;
724 typedef double (get_percent_cb)(struct c2c_hist_entry *);
726 static int
727 percent_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
728 struct hist_entry *he, get_percent_cb get_percent)
730 struct c2c_hist_entry *c2c_he;
731 int width = c2c_width(fmt, hpp, he->hists);
732 double per;
734 c2c_he = container_of(he, struct c2c_hist_entry, he);
735 per = get_percent(c2c_he);
737 #ifdef HAVE_SLANG_SUPPORT
738 if (use_browser)
739 return __hpp__slsmg_color_printf(hpp, "%*.2f%%", width - 1, per);
740 #endif
741 return hpp_color_scnprintf(hpp, "%*.2f%%", width - 1, per);
744 static double percent_hitm(struct c2c_hist_entry *c2c_he)
746 struct c2c_hists *hists;
747 struct c2c_stats *stats;
748 struct c2c_stats *total;
749 int tot = 0, st = 0;
750 double p;
752 hists = container_of(c2c_he->he.hists, struct c2c_hists, hists);
753 stats = &c2c_he->stats;
754 total = &hists->stats;
756 switch (c2c.display) {
757 case DISPLAY_RMT:
758 st = stats->rmt_hitm;
759 tot = total->rmt_hitm;
760 break;
761 case DISPLAY_LCL:
762 st = stats->lcl_hitm;
763 tot = total->lcl_hitm;
764 break;
765 case DISPLAY_TOT:
766 st = stats->tot_hitm;
767 tot = total->tot_hitm;
768 default:
769 break;
772 p = tot ? (double) st / tot : 0;
774 return 100 * p;
777 #define PERC_STR(__s, __v) \
778 ({ \
779 scnprintf(__s, sizeof(__s), "%.2F%%", __v); \
780 __s; \
783 static int
784 percent_hitm_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
785 struct hist_entry *he)
787 struct c2c_hist_entry *c2c_he;
788 int width = c2c_width(fmt, hpp, he->hists);
789 char buf[10];
790 double per;
792 c2c_he = container_of(he, struct c2c_hist_entry, he);
793 per = percent_hitm(c2c_he);
794 return scnprintf(hpp->buf, hpp->size, "%*s", width, PERC_STR(buf, per));
797 static int
798 percent_hitm_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
799 struct hist_entry *he)
801 return percent_color(fmt, hpp, he, percent_hitm);
804 static int64_t
805 percent_hitm_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
806 struct hist_entry *left, struct hist_entry *right)
808 struct c2c_hist_entry *c2c_left;
809 struct c2c_hist_entry *c2c_right;
810 double per_left;
811 double per_right;
813 c2c_left = container_of(left, struct c2c_hist_entry, he);
814 c2c_right = container_of(right, struct c2c_hist_entry, he);
816 per_left = percent_hitm(c2c_left);
817 per_right = percent_hitm(c2c_right);
819 return per_left - per_right;
822 static struct c2c_stats *he_stats(struct hist_entry *he)
824 struct c2c_hist_entry *c2c_he;
826 c2c_he = container_of(he, struct c2c_hist_entry, he);
827 return &c2c_he->stats;
830 static struct c2c_stats *total_stats(struct hist_entry *he)
832 struct c2c_hists *hists;
834 hists = container_of(he->hists, struct c2c_hists, hists);
835 return &hists->stats;
838 static double percent(int st, int tot)
840 return tot ? 100. * (double) st / (double) tot : 0;
843 #define PERCENT(__h, __f) percent(he_stats(__h)->__f, total_stats(__h)->__f)
845 #define PERCENT_FN(__f) \
846 static double percent_ ## __f(struct c2c_hist_entry *c2c_he) \
848 struct c2c_hists *hists; \
850 hists = container_of(c2c_he->he.hists, struct c2c_hists, hists); \
851 return percent(c2c_he->stats.__f, hists->stats.__f); \
854 PERCENT_FN(rmt_hitm)
855 PERCENT_FN(lcl_hitm)
856 PERCENT_FN(st_l1hit)
857 PERCENT_FN(st_l1miss)
859 static int
860 percent_rmt_hitm_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
861 struct hist_entry *he)
863 int width = c2c_width(fmt, hpp, he->hists);
864 double per = PERCENT(he, rmt_hitm);
865 char buf[10];
867 return scnprintf(hpp->buf, hpp->size, "%*s", width, PERC_STR(buf, per));
870 static int
871 percent_rmt_hitm_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
872 struct hist_entry *he)
874 return percent_color(fmt, hpp, he, percent_rmt_hitm);
877 static int64_t
878 percent_rmt_hitm_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
879 struct hist_entry *left, struct hist_entry *right)
881 double per_left;
882 double per_right;
884 per_left = PERCENT(left, lcl_hitm);
885 per_right = PERCENT(right, lcl_hitm);
887 return per_left - per_right;
890 static int
891 percent_lcl_hitm_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
892 struct hist_entry *he)
894 int width = c2c_width(fmt, hpp, he->hists);
895 double per = PERCENT(he, lcl_hitm);
896 char buf[10];
898 return scnprintf(hpp->buf, hpp->size, "%*s", width, PERC_STR(buf, per));
901 static int
902 percent_lcl_hitm_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
903 struct hist_entry *he)
905 return percent_color(fmt, hpp, he, percent_lcl_hitm);
908 static int64_t
909 percent_lcl_hitm_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
910 struct hist_entry *left, struct hist_entry *right)
912 double per_left;
913 double per_right;
915 per_left = PERCENT(left, lcl_hitm);
916 per_right = PERCENT(right, lcl_hitm);
918 return per_left - per_right;
921 static int
922 percent_stores_l1hit_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
923 struct hist_entry *he)
925 int width = c2c_width(fmt, hpp, he->hists);
926 double per = PERCENT(he, st_l1hit);
927 char buf[10];
929 return scnprintf(hpp->buf, hpp->size, "%*s", width, PERC_STR(buf, per));
932 static int
933 percent_stores_l1hit_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
934 struct hist_entry *he)
936 return percent_color(fmt, hpp, he, percent_st_l1hit);
939 static int64_t
940 percent_stores_l1hit_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
941 struct hist_entry *left, struct hist_entry *right)
943 double per_left;
944 double per_right;
946 per_left = PERCENT(left, st_l1hit);
947 per_right = PERCENT(right, st_l1hit);
949 return per_left - per_right;
952 static int
953 percent_stores_l1miss_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
954 struct hist_entry *he)
956 int width = c2c_width(fmt, hpp, he->hists);
957 double per = PERCENT(he, st_l1miss);
958 char buf[10];
960 return scnprintf(hpp->buf, hpp->size, "%*s", width, PERC_STR(buf, per));
963 static int
964 percent_stores_l1miss_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
965 struct hist_entry *he)
967 return percent_color(fmt, hpp, he, percent_st_l1miss);
970 static int64_t
971 percent_stores_l1miss_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
972 struct hist_entry *left, struct hist_entry *right)
974 double per_left;
975 double per_right;
977 per_left = PERCENT(left, st_l1miss);
978 per_right = PERCENT(right, st_l1miss);
980 return per_left - per_right;
983 STAT_FN(lcl_dram)
984 STAT_FN(rmt_dram)
986 static int
987 pid_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
988 struct hist_entry *he)
990 int width = c2c_width(fmt, hpp, he->hists);
992 return scnprintf(hpp->buf, hpp->size, "%*d", width, he->thread->pid_);
995 static int64_t
996 pid_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
997 struct hist_entry *left, struct hist_entry *right)
999 return left->thread->pid_ - right->thread->pid_;
1002 static int64_t
1003 empty_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
1004 struct hist_entry *left __maybe_unused,
1005 struct hist_entry *right __maybe_unused)
1007 return 0;
1010 static int
1011 node_entry(struct perf_hpp_fmt *fmt __maybe_unused, struct perf_hpp *hpp,
1012 struct hist_entry *he)
1014 struct c2c_hist_entry *c2c_he;
1015 bool first = true;
1016 int node;
1017 int ret = 0;
1019 c2c_he = container_of(he, struct c2c_hist_entry, he);
1021 for (node = 0; node < c2c.nodes_cnt; node++) {
1022 DECLARE_BITMAP(set, c2c.cpus_cnt);
1024 bitmap_zero(set, c2c.cpus_cnt);
1025 bitmap_and(set, c2c_he->cpuset, c2c.nodes[node], c2c.cpus_cnt);
1027 if (!bitmap_weight(set, c2c.cpus_cnt)) {
1028 if (c2c.node_info == 1) {
1029 ret = scnprintf(hpp->buf, hpp->size, "%21s", " ");
1030 advance_hpp(hpp, ret);
1032 continue;
1035 if (!first) {
1036 ret = scnprintf(hpp->buf, hpp->size, " ");
1037 advance_hpp(hpp, ret);
1040 switch (c2c.node_info) {
1041 case 0:
1042 ret = scnprintf(hpp->buf, hpp->size, "%2d", node);
1043 advance_hpp(hpp, ret);
1044 break;
1045 case 1:
1047 int num = bitmap_weight(c2c_he->cpuset, c2c.cpus_cnt);
1048 struct c2c_stats *stats = &c2c_he->node_stats[node];
1050 ret = scnprintf(hpp->buf, hpp->size, "%2d{%2d ", node, num);
1051 advance_hpp(hpp, ret);
1053 #define DISPLAY_HITM(__h) \
1054 if (c2c_he->stats.__h> 0) { \
1055 ret = scnprintf(hpp->buf, hpp->size, "%5.1f%% ", \
1056 percent(stats->__h, c2c_he->stats.__h));\
1057 } else { \
1058 ret = scnprintf(hpp->buf, hpp->size, "%6s ", "n/a"); \
1061 switch (c2c.display) {
1062 case DISPLAY_RMT:
1063 DISPLAY_HITM(rmt_hitm);
1064 break;
1065 case DISPLAY_LCL:
1066 DISPLAY_HITM(lcl_hitm);
1067 break;
1068 case DISPLAY_TOT:
1069 DISPLAY_HITM(tot_hitm);
1070 default:
1071 break;
1074 #undef DISPLAY_HITM
1076 advance_hpp(hpp, ret);
1078 if (c2c_he->stats.store > 0) {
1079 ret = scnprintf(hpp->buf, hpp->size, "%5.1f%%}",
1080 percent(stats->store, c2c_he->stats.store));
1081 } else {
1082 ret = scnprintf(hpp->buf, hpp->size, "%6s}", "n/a");
1085 advance_hpp(hpp, ret);
1086 break;
1088 case 2:
1089 ret = scnprintf(hpp->buf, hpp->size, "%2d{", node);
1090 advance_hpp(hpp, ret);
1092 ret = bitmap_scnprintf(set, c2c.cpus_cnt, hpp->buf, hpp->size);
1093 advance_hpp(hpp, ret);
1095 ret = scnprintf(hpp->buf, hpp->size, "}");
1096 advance_hpp(hpp, ret);
1097 break;
1098 default:
1099 break;
1102 first = false;
1105 return 0;
1108 static int
1109 mean_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1110 struct hist_entry *he, double mean)
1112 int width = c2c_width(fmt, hpp, he->hists);
1113 char buf[10];
1115 scnprintf(buf, 10, "%6.0f", mean);
1116 return scnprintf(hpp->buf, hpp->size, "%*s", width, buf);
1119 #define MEAN_ENTRY(__func, __val) \
1120 static int \
1121 __func(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, struct hist_entry *he) \
1123 struct c2c_hist_entry *c2c_he; \
1124 c2c_he = container_of(he, struct c2c_hist_entry, he); \
1125 return mean_entry(fmt, hpp, he, avg_stats(&c2c_he->cstats.__val)); \
1128 MEAN_ENTRY(mean_rmt_entry, rmt_hitm);
1129 MEAN_ENTRY(mean_lcl_entry, lcl_hitm);
1130 MEAN_ENTRY(mean_load_entry, load);
1132 static int
1133 cpucnt_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1134 struct hist_entry *he)
1136 struct c2c_hist_entry *c2c_he;
1137 int width = c2c_width(fmt, hpp, he->hists);
1138 char buf[10];
1140 c2c_he = container_of(he, struct c2c_hist_entry, he);
1142 scnprintf(buf, 10, "%d", bitmap_weight(c2c_he->cpuset, c2c.cpus_cnt));
1143 return scnprintf(hpp->buf, hpp->size, "%*s", width, buf);
1146 static int
1147 cl_idx_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1148 struct hist_entry *he)
1150 struct c2c_hist_entry *c2c_he;
1151 int width = c2c_width(fmt, hpp, he->hists);
1152 char buf[10];
1154 c2c_he = container_of(he, struct c2c_hist_entry, he);
1156 scnprintf(buf, 10, "%u", c2c_he->cacheline_idx);
1157 return scnprintf(hpp->buf, hpp->size, "%*s", width, buf);
1160 static int
1161 cl_idx_empty_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1162 struct hist_entry *he)
1164 int width = c2c_width(fmt, hpp, he->hists);
1166 return scnprintf(hpp->buf, hpp->size, "%*s", width, "");
1169 #define HEADER_LOW(__h) \
1171 .line[1] = { \
1172 .text = __h, \
1173 }, \
1176 #define HEADER_BOTH(__h0, __h1) \
1178 .line[0] = { \
1179 .text = __h0, \
1180 }, \
1181 .line[1] = { \
1182 .text = __h1, \
1183 }, \
1186 #define HEADER_SPAN(__h0, __h1, __s) \
1188 .line[0] = { \
1189 .text = __h0, \
1190 .span = __s, \
1191 }, \
1192 .line[1] = { \
1193 .text = __h1, \
1194 }, \
1197 #define HEADER_SPAN_LOW(__h) \
1199 .line[1] = { \
1200 .text = __h, \
1201 }, \
1204 static struct c2c_dimension dim_dcacheline = {
1205 .header = HEADER_LOW("Cacheline"),
1206 .name = "dcacheline",
1207 .cmp = dcacheline_cmp,
1208 .entry = dcacheline_entry,
1209 .width = 18,
1212 static struct c2c_header header_offset_tui = HEADER_LOW("Off");
1214 static struct c2c_dimension dim_offset = {
1215 .header = HEADER_BOTH("Data address", "Offset"),
1216 .name = "offset",
1217 .cmp = offset_cmp,
1218 .entry = offset_entry,
1219 .width = 18,
1222 static struct c2c_dimension dim_iaddr = {
1223 .header = HEADER_LOW("Code address"),
1224 .name = "iaddr",
1225 .cmp = iaddr_cmp,
1226 .entry = iaddr_entry,
1227 .width = 18,
1230 static struct c2c_dimension dim_tot_hitm = {
1231 .header = HEADER_SPAN("----- LLC Load Hitm -----", "Total", 2),
1232 .name = "tot_hitm",
1233 .cmp = tot_hitm_cmp,
1234 .entry = tot_hitm_entry,
1235 .width = 7,
1238 static struct c2c_dimension dim_lcl_hitm = {
1239 .header = HEADER_SPAN_LOW("Lcl"),
1240 .name = "lcl_hitm",
1241 .cmp = lcl_hitm_cmp,
1242 .entry = lcl_hitm_entry,
1243 .width = 7,
1246 static struct c2c_dimension dim_rmt_hitm = {
1247 .header = HEADER_SPAN_LOW("Rmt"),
1248 .name = "rmt_hitm",
1249 .cmp = rmt_hitm_cmp,
1250 .entry = rmt_hitm_entry,
1251 .width = 7,
1254 static struct c2c_dimension dim_cl_rmt_hitm = {
1255 .header = HEADER_SPAN("----- HITM -----", "Rmt", 1),
1256 .name = "cl_rmt_hitm",
1257 .cmp = rmt_hitm_cmp,
1258 .entry = rmt_hitm_entry,
1259 .width = 7,
1262 static struct c2c_dimension dim_cl_lcl_hitm = {
1263 .header = HEADER_SPAN_LOW("Lcl"),
1264 .name = "cl_lcl_hitm",
1265 .cmp = lcl_hitm_cmp,
1266 .entry = lcl_hitm_entry,
1267 .width = 7,
1270 static struct c2c_dimension dim_stores = {
1271 .header = HEADER_SPAN("---- Store Reference ----", "Total", 2),
1272 .name = "stores",
1273 .cmp = store_cmp,
1274 .entry = store_entry,
1275 .width = 7,
1278 static struct c2c_dimension dim_stores_l1hit = {
1279 .header = HEADER_SPAN_LOW("L1Hit"),
1280 .name = "stores_l1hit",
1281 .cmp = st_l1hit_cmp,
1282 .entry = st_l1hit_entry,
1283 .width = 7,
1286 static struct c2c_dimension dim_stores_l1miss = {
1287 .header = HEADER_SPAN_LOW("L1Miss"),
1288 .name = "stores_l1miss",
1289 .cmp = st_l1miss_cmp,
1290 .entry = st_l1miss_entry,
1291 .width = 7,
1294 static struct c2c_dimension dim_cl_stores_l1hit = {
1295 .header = HEADER_SPAN("-- Store Refs --", "L1 Hit", 1),
1296 .name = "cl_stores_l1hit",
1297 .cmp = st_l1hit_cmp,
1298 .entry = st_l1hit_entry,
1299 .width = 7,
1302 static struct c2c_dimension dim_cl_stores_l1miss = {
1303 .header = HEADER_SPAN_LOW("L1 Miss"),
1304 .name = "cl_stores_l1miss",
1305 .cmp = st_l1miss_cmp,
1306 .entry = st_l1miss_entry,
1307 .width = 7,
1310 static struct c2c_dimension dim_ld_fbhit = {
1311 .header = HEADER_SPAN("----- Core Load Hit -----", "FB", 2),
1312 .name = "ld_fbhit",
1313 .cmp = ld_fbhit_cmp,
1314 .entry = ld_fbhit_entry,
1315 .width = 7,
1318 static struct c2c_dimension dim_ld_l1hit = {
1319 .header = HEADER_SPAN_LOW("L1"),
1320 .name = "ld_l1hit",
1321 .cmp = ld_l1hit_cmp,
1322 .entry = ld_l1hit_entry,
1323 .width = 7,
1326 static struct c2c_dimension dim_ld_l2hit = {
1327 .header = HEADER_SPAN_LOW("L2"),
1328 .name = "ld_l2hit",
1329 .cmp = ld_l2hit_cmp,
1330 .entry = ld_l2hit_entry,
1331 .width = 7,
1334 static struct c2c_dimension dim_ld_llchit = {
1335 .header = HEADER_SPAN("-- LLC Load Hit --", "Llc", 1),
1336 .name = "ld_lclhit",
1337 .cmp = ld_llchit_cmp,
1338 .entry = ld_llchit_entry,
1339 .width = 8,
1342 static struct c2c_dimension dim_ld_rmthit = {
1343 .header = HEADER_SPAN_LOW("Rmt"),
1344 .name = "ld_rmthit",
1345 .cmp = rmt_hit_cmp,
1346 .entry = rmt_hit_entry,
1347 .width = 8,
1350 static struct c2c_dimension dim_ld_llcmiss = {
1351 .header = HEADER_BOTH("LLC", "Ld Miss"),
1352 .name = "ld_llcmiss",
1353 .cmp = ld_llcmiss_cmp,
1354 .entry = ld_llcmiss_entry,
1355 .width = 7,
1358 static struct c2c_dimension dim_tot_recs = {
1359 .header = HEADER_BOTH("Total", "records"),
1360 .name = "tot_recs",
1361 .cmp = tot_recs_cmp,
1362 .entry = tot_recs_entry,
1363 .width = 7,
1366 static struct c2c_dimension dim_tot_loads = {
1367 .header = HEADER_BOTH("Total", "Loads"),
1368 .name = "tot_loads",
1369 .cmp = tot_loads_cmp,
1370 .entry = tot_loads_entry,
1371 .width = 7,
1374 static struct c2c_header percent_hitm_header[] = {
1375 [DISPLAY_LCL] = HEADER_BOTH("Lcl", "Hitm"),
1376 [DISPLAY_RMT] = HEADER_BOTH("Rmt", "Hitm"),
1377 [DISPLAY_TOT] = HEADER_BOTH("Tot", "Hitm"),
1380 static struct c2c_dimension dim_percent_hitm = {
1381 .name = "percent_hitm",
1382 .cmp = percent_hitm_cmp,
1383 .entry = percent_hitm_entry,
1384 .color = percent_hitm_color,
1385 .width = 7,
1388 static struct c2c_dimension dim_percent_rmt_hitm = {
1389 .header = HEADER_SPAN("----- HITM -----", "Rmt", 1),
1390 .name = "percent_rmt_hitm",
1391 .cmp = percent_rmt_hitm_cmp,
1392 .entry = percent_rmt_hitm_entry,
1393 .color = percent_rmt_hitm_color,
1394 .width = 7,
1397 static struct c2c_dimension dim_percent_lcl_hitm = {
1398 .header = HEADER_SPAN_LOW("Lcl"),
1399 .name = "percent_lcl_hitm",
1400 .cmp = percent_lcl_hitm_cmp,
1401 .entry = percent_lcl_hitm_entry,
1402 .color = percent_lcl_hitm_color,
1403 .width = 7,
1406 static struct c2c_dimension dim_percent_stores_l1hit = {
1407 .header = HEADER_SPAN("-- Store Refs --", "L1 Hit", 1),
1408 .name = "percent_stores_l1hit",
1409 .cmp = percent_stores_l1hit_cmp,
1410 .entry = percent_stores_l1hit_entry,
1411 .color = percent_stores_l1hit_color,
1412 .width = 7,
1415 static struct c2c_dimension dim_percent_stores_l1miss = {
1416 .header = HEADER_SPAN_LOW("L1 Miss"),
1417 .name = "percent_stores_l1miss",
1418 .cmp = percent_stores_l1miss_cmp,
1419 .entry = percent_stores_l1miss_entry,
1420 .color = percent_stores_l1miss_color,
1421 .width = 7,
1424 static struct c2c_dimension dim_dram_lcl = {
1425 .header = HEADER_SPAN("--- Load Dram ----", "Lcl", 1),
1426 .name = "dram_lcl",
1427 .cmp = lcl_dram_cmp,
1428 .entry = lcl_dram_entry,
1429 .width = 8,
1432 static struct c2c_dimension dim_dram_rmt = {
1433 .header = HEADER_SPAN_LOW("Rmt"),
1434 .name = "dram_rmt",
1435 .cmp = rmt_dram_cmp,
1436 .entry = rmt_dram_entry,
1437 .width = 8,
1440 static struct c2c_dimension dim_pid = {
1441 .header = HEADER_LOW("Pid"),
1442 .name = "pid",
1443 .cmp = pid_cmp,
1444 .entry = pid_entry,
1445 .width = 7,
1448 static struct c2c_dimension dim_tid = {
1449 .header = HEADER_LOW("Tid"),
1450 .name = "tid",
1451 .se = &sort_thread,
1454 static struct c2c_dimension dim_symbol = {
1455 .name = "symbol",
1456 .se = &sort_sym,
1459 static struct c2c_dimension dim_dso = {
1460 .header = HEADER_BOTH("Shared", "Object"),
1461 .name = "dso",
1462 .se = &sort_dso,
1465 static struct c2c_header header_node[3] = {
1466 HEADER_LOW("Node"),
1467 HEADER_LOW("Node{cpus %hitms %stores}"),
1468 HEADER_LOW("Node{cpu list}"),
1471 static struct c2c_dimension dim_node = {
1472 .name = "node",
1473 .cmp = empty_cmp,
1474 .entry = node_entry,
1475 .width = 4,
1478 static struct c2c_dimension dim_mean_rmt = {
1479 .header = HEADER_SPAN("---------- cycles ----------", "rmt hitm", 2),
1480 .name = "mean_rmt",
1481 .cmp = empty_cmp,
1482 .entry = mean_rmt_entry,
1483 .width = 8,
1486 static struct c2c_dimension dim_mean_lcl = {
1487 .header = HEADER_SPAN_LOW("lcl hitm"),
1488 .name = "mean_lcl",
1489 .cmp = empty_cmp,
1490 .entry = mean_lcl_entry,
1491 .width = 8,
1494 static struct c2c_dimension dim_mean_load = {
1495 .header = HEADER_SPAN_LOW("load"),
1496 .name = "mean_load",
1497 .cmp = empty_cmp,
1498 .entry = mean_load_entry,
1499 .width = 8,
1502 static struct c2c_dimension dim_cpucnt = {
1503 .header = HEADER_BOTH("cpu", "cnt"),
1504 .name = "cpucnt",
1505 .cmp = empty_cmp,
1506 .entry = cpucnt_entry,
1507 .width = 8,
1510 static struct c2c_dimension dim_srcline = {
1511 .name = "cl_srcline",
1512 .se = &sort_srcline,
1515 static struct c2c_dimension dim_dcacheline_idx = {
1516 .header = HEADER_LOW("Index"),
1517 .name = "cl_idx",
1518 .cmp = empty_cmp,
1519 .entry = cl_idx_entry,
1520 .width = 5,
1523 static struct c2c_dimension dim_dcacheline_num = {
1524 .header = HEADER_LOW("Num"),
1525 .name = "cl_num",
1526 .cmp = empty_cmp,
1527 .entry = cl_idx_entry,
1528 .width = 5,
1531 static struct c2c_dimension dim_dcacheline_num_empty = {
1532 .header = HEADER_LOW("Num"),
1533 .name = "cl_num_empty",
1534 .cmp = empty_cmp,
1535 .entry = cl_idx_empty_entry,
1536 .width = 5,
1539 static struct c2c_dimension *dimensions[] = {
1540 &dim_dcacheline,
1541 &dim_offset,
1542 &dim_iaddr,
1543 &dim_tot_hitm,
1544 &dim_lcl_hitm,
1545 &dim_rmt_hitm,
1546 &dim_cl_lcl_hitm,
1547 &dim_cl_rmt_hitm,
1548 &dim_stores,
1549 &dim_stores_l1hit,
1550 &dim_stores_l1miss,
1551 &dim_cl_stores_l1hit,
1552 &dim_cl_stores_l1miss,
1553 &dim_ld_fbhit,
1554 &dim_ld_l1hit,
1555 &dim_ld_l2hit,
1556 &dim_ld_llchit,
1557 &dim_ld_rmthit,
1558 &dim_ld_llcmiss,
1559 &dim_tot_recs,
1560 &dim_tot_loads,
1561 &dim_percent_hitm,
1562 &dim_percent_rmt_hitm,
1563 &dim_percent_lcl_hitm,
1564 &dim_percent_stores_l1hit,
1565 &dim_percent_stores_l1miss,
1566 &dim_dram_lcl,
1567 &dim_dram_rmt,
1568 &dim_pid,
1569 &dim_tid,
1570 &dim_symbol,
1571 &dim_dso,
1572 &dim_node,
1573 &dim_mean_rmt,
1574 &dim_mean_lcl,
1575 &dim_mean_load,
1576 &dim_cpucnt,
1577 &dim_srcline,
1578 &dim_dcacheline_idx,
1579 &dim_dcacheline_num,
1580 &dim_dcacheline_num_empty,
1581 NULL,
1584 static void fmt_free(struct perf_hpp_fmt *fmt)
1586 struct c2c_fmt *c2c_fmt;
1588 c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
1589 free(c2c_fmt);
1592 static bool fmt_equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)
1594 struct c2c_fmt *c2c_a = container_of(a, struct c2c_fmt, fmt);
1595 struct c2c_fmt *c2c_b = container_of(b, struct c2c_fmt, fmt);
1597 return c2c_a->dim == c2c_b->dim;
1600 static struct c2c_dimension *get_dimension(const char *name)
1602 unsigned int i;
1604 for (i = 0; dimensions[i]; i++) {
1605 struct c2c_dimension *dim = dimensions[i];
1607 if (!strcmp(dim->name, name))
1608 return dim;
1611 return NULL;
1614 static int c2c_se_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1615 struct hist_entry *he)
1617 struct c2c_fmt *c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
1618 struct c2c_dimension *dim = c2c_fmt->dim;
1619 size_t len = fmt->user_len;
1621 if (!len) {
1622 len = hists__col_len(he->hists, dim->se->se_width_idx);
1624 if (dim == &dim_symbol || dim == &dim_srcline)
1625 len = symbol_width(he->hists, dim->se);
1628 return dim->se->se_snprintf(he, hpp->buf, hpp->size, len);
1631 static int64_t c2c_se_cmp(struct perf_hpp_fmt *fmt,
1632 struct hist_entry *a, struct hist_entry *b)
1634 struct c2c_fmt *c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
1635 struct c2c_dimension *dim = c2c_fmt->dim;
1637 return dim->se->se_cmp(a, b);
1640 static int64_t c2c_se_collapse(struct perf_hpp_fmt *fmt,
1641 struct hist_entry *a, struct hist_entry *b)
1643 struct c2c_fmt *c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
1644 struct c2c_dimension *dim = c2c_fmt->dim;
1645 int64_t (*collapse_fn)(struct hist_entry *, struct hist_entry *);
1647 collapse_fn = dim->se->se_collapse ?: dim->se->se_cmp;
1648 return collapse_fn(a, b);
1651 static struct c2c_fmt *get_format(const char *name)
1653 struct c2c_dimension *dim = get_dimension(name);
1654 struct c2c_fmt *c2c_fmt;
1655 struct perf_hpp_fmt *fmt;
1657 if (!dim)
1658 return NULL;
1660 c2c_fmt = zalloc(sizeof(*c2c_fmt));
1661 if (!c2c_fmt)
1662 return NULL;
1664 c2c_fmt->dim = dim;
1666 fmt = &c2c_fmt->fmt;
1667 INIT_LIST_HEAD(&fmt->list);
1668 INIT_LIST_HEAD(&fmt->sort_list);
1670 fmt->cmp = dim->se ? c2c_se_cmp : dim->cmp;
1671 fmt->sort = dim->se ? c2c_se_cmp : dim->cmp;
1672 fmt->color = dim->se ? NULL : dim->color;
1673 fmt->entry = dim->se ? c2c_se_entry : dim->entry;
1674 fmt->header = c2c_header;
1675 fmt->width = c2c_width;
1676 fmt->collapse = dim->se ? c2c_se_collapse : dim->cmp;
1677 fmt->equal = fmt_equal;
1678 fmt->free = fmt_free;
1680 return c2c_fmt;
1683 static int c2c_hists__init_output(struct perf_hpp_list *hpp_list, char *name)
1685 struct c2c_fmt *c2c_fmt = get_format(name);
1687 if (!c2c_fmt) {
1688 reset_dimensions();
1689 return output_field_add(hpp_list, name);
1692 perf_hpp_list__column_register(hpp_list, &c2c_fmt->fmt);
1693 return 0;
1696 static int c2c_hists__init_sort(struct perf_hpp_list *hpp_list, char *name)
1698 struct c2c_fmt *c2c_fmt = get_format(name);
1699 struct c2c_dimension *dim;
1701 if (!c2c_fmt) {
1702 reset_dimensions();
1703 return sort_dimension__add(hpp_list, name, NULL, 0);
1706 dim = c2c_fmt->dim;
1707 if (dim == &dim_dso)
1708 hpp_list->dso = 1;
1710 perf_hpp_list__register_sort_field(hpp_list, &c2c_fmt->fmt);
1711 return 0;
1714 #define PARSE_LIST(_list, _fn) \
1715 do { \
1716 char *tmp, *tok; \
1717 ret = 0; \
1719 if (!_list) \
1720 break; \
1722 for (tok = strtok_r((char *)_list, ", ", &tmp); \
1723 tok; tok = strtok_r(NULL, ", ", &tmp)) { \
1724 ret = _fn(hpp_list, tok); \
1725 if (ret == -EINVAL) { \
1726 pr_err("Invalid --fields key: `%s'", tok); \
1727 break; \
1728 } else if (ret == -ESRCH) { \
1729 pr_err("Unknown --fields key: `%s'", tok); \
1730 break; \
1733 } while (0)
1735 static int hpp_list__parse(struct perf_hpp_list *hpp_list,
1736 const char *output_,
1737 const char *sort_)
1739 char *output = output_ ? strdup(output_) : NULL;
1740 char *sort = sort_ ? strdup(sort_) : NULL;
1741 int ret;
1743 PARSE_LIST(output, c2c_hists__init_output);
1744 PARSE_LIST(sort, c2c_hists__init_sort);
1746 /* copy sort keys to output fields */
1747 perf_hpp__setup_output_field(hpp_list);
1750 * We dont need other sorting keys other than those
1751 * we already specified. It also really slows down
1752 * the processing a lot with big number of output
1753 * fields, so switching this off for c2c.
1756 #if 0
1757 /* and then copy output fields to sort keys */
1758 perf_hpp__append_sort_keys(&hists->list);
1759 #endif
1761 free(output);
1762 free(sort);
1763 return ret;
1766 static int c2c_hists__init(struct c2c_hists *hists,
1767 const char *sort,
1768 int nr_header_lines)
1770 __hists__init(&hists->hists, &hists->list);
1773 * Initialize only with sort fields, we need to resort
1774 * later anyway, and that's where we add output fields
1775 * as well.
1777 perf_hpp_list__init(&hists->list);
1779 /* Overload number of header lines.*/
1780 hists->list.nr_header_lines = nr_header_lines;
1782 return hpp_list__parse(&hists->list, NULL, sort);
1785 static int c2c_hists__reinit(struct c2c_hists *c2c_hists,
1786 const char *output,
1787 const char *sort)
1789 perf_hpp__reset_output_field(&c2c_hists->list);
1790 return hpp_list__parse(&c2c_hists->list, output, sort);
1793 #define DISPLAY_LINE_LIMIT 0.0005
1795 static bool he__display(struct hist_entry *he, struct c2c_stats *stats)
1797 struct c2c_hist_entry *c2c_he;
1798 double ld_dist;
1800 if (c2c.show_all)
1801 return true;
1803 c2c_he = container_of(he, struct c2c_hist_entry, he);
1805 #define FILTER_HITM(__h) \
1806 if (stats->__h) { \
1807 ld_dist = ((double)c2c_he->stats.__h / stats->__h); \
1808 if (ld_dist < DISPLAY_LINE_LIMIT) \
1809 he->filtered = HIST_FILTER__C2C; \
1810 } else { \
1811 he->filtered = HIST_FILTER__C2C; \
1814 switch (c2c.display) {
1815 case DISPLAY_LCL:
1816 FILTER_HITM(lcl_hitm);
1817 break;
1818 case DISPLAY_RMT:
1819 FILTER_HITM(rmt_hitm);
1820 break;
1821 case DISPLAY_TOT:
1822 FILTER_HITM(tot_hitm);
1823 default:
1824 break;
1827 #undef FILTER_HITM
1829 return he->filtered == 0;
1832 static inline int valid_hitm_or_store(struct hist_entry *he)
1834 struct c2c_hist_entry *c2c_he;
1835 bool has_hitm;
1837 c2c_he = container_of(he, struct c2c_hist_entry, he);
1838 has_hitm = c2c.display == DISPLAY_TOT ? c2c_he->stats.tot_hitm :
1839 c2c.display == DISPLAY_LCL ? c2c_he->stats.lcl_hitm :
1840 c2c_he->stats.rmt_hitm;
1841 return has_hitm || c2c_he->stats.store;
1844 static void calc_width(struct hist_entry *he)
1846 struct c2c_hists *c2c_hists;
1848 c2c_hists = container_of(he->hists, struct c2c_hists, hists);
1849 hists__calc_col_len(&c2c_hists->hists, he);
1852 static int filter_cb(struct hist_entry *he)
1854 if (c2c.show_src && !he->srcline)
1855 he->srcline = hist_entry__get_srcline(he);
1857 calc_width(he);
1859 if (!valid_hitm_or_store(he))
1860 he->filtered = HIST_FILTER__C2C;
1862 return 0;
1865 static int resort_cl_cb(struct hist_entry *he)
1867 struct c2c_hist_entry *c2c_he;
1868 struct c2c_hists *c2c_hists;
1869 bool display = he__display(he, &c2c.hitm_stats);
1871 c2c_he = container_of(he, struct c2c_hist_entry, he);
1872 c2c_hists = c2c_he->hists;
1874 calc_width(he);
1876 if (display && c2c_hists) {
1877 static unsigned int idx;
1879 c2c_he->cacheline_idx = idx++;
1881 c2c_hists__reinit(c2c_hists, c2c.cl_output, c2c.cl_resort);
1883 hists__collapse_resort(&c2c_hists->hists, NULL);
1884 hists__output_resort_cb(&c2c_hists->hists, NULL, filter_cb);
1887 return 0;
1890 static void setup_nodes_header(void)
1892 dim_node.header = header_node[c2c.node_info];
1895 static int setup_nodes(struct perf_session *session)
1897 struct numa_node *n;
1898 unsigned long **nodes;
1899 int node, cpu;
1900 int *cpu2node;
1902 if (c2c.node_info > 2)
1903 c2c.node_info = 2;
1905 c2c.nodes_cnt = session->header.env.nr_numa_nodes;
1906 c2c.cpus_cnt = session->header.env.nr_cpus_online;
1908 n = session->header.env.numa_nodes;
1909 if (!n)
1910 return -EINVAL;
1912 nodes = zalloc(sizeof(unsigned long *) * c2c.nodes_cnt);
1913 if (!nodes)
1914 return -ENOMEM;
1916 c2c.nodes = nodes;
1918 cpu2node = zalloc(sizeof(int) * c2c.cpus_cnt);
1919 if (!cpu2node)
1920 return -ENOMEM;
1922 for (cpu = 0; cpu < c2c.cpus_cnt; cpu++)
1923 cpu2node[cpu] = -1;
1925 c2c.cpu2node = cpu2node;
1927 for (node = 0; node < c2c.nodes_cnt; node++) {
1928 struct cpu_map *map = n[node].map;
1929 unsigned long *set;
1931 set = bitmap_alloc(c2c.cpus_cnt);
1932 if (!set)
1933 return -ENOMEM;
1935 for (cpu = 0; cpu < map->nr; cpu++) {
1936 set_bit(map->map[cpu], set);
1938 if (WARN_ONCE(cpu2node[map->map[cpu]] != -1, "node/cpu topology bug"))
1939 return -EINVAL;
1941 cpu2node[map->map[cpu]] = node;
1944 nodes[node] = set;
1947 setup_nodes_header();
1948 return 0;
1951 #define HAS_HITMS(__h) ((__h)->stats.lcl_hitm || (__h)->stats.rmt_hitm)
1953 static int resort_hitm_cb(struct hist_entry *he)
1955 struct c2c_hist_entry *c2c_he;
1956 c2c_he = container_of(he, struct c2c_hist_entry, he);
1958 if (HAS_HITMS(c2c_he)) {
1959 c2c.shared_clines++;
1960 c2c_add_stats(&c2c.hitm_stats, &c2c_he->stats);
1963 return 0;
1966 static int hists__iterate_cb(struct hists *hists, hists__resort_cb_t cb)
1968 struct rb_node *next = rb_first(&hists->entries);
1969 int ret = 0;
1971 while (next) {
1972 struct hist_entry *he;
1974 he = rb_entry(next, struct hist_entry, rb_node);
1975 ret = cb(he);
1976 if (ret)
1977 break;
1978 next = rb_next(&he->rb_node);
1981 return ret;
1984 static void print_c2c__display_stats(FILE *out)
1986 int llc_misses;
1987 struct c2c_stats *stats = &c2c.hists.stats;
1989 llc_misses = stats->lcl_dram +
1990 stats->rmt_dram +
1991 stats->rmt_hit +
1992 stats->rmt_hitm;
1994 fprintf(out, "=================================================\n");
1995 fprintf(out, " Trace Event Information \n");
1996 fprintf(out, "=================================================\n");
1997 fprintf(out, " Total records : %10d\n", stats->nr_entries);
1998 fprintf(out, " Locked Load/Store Operations : %10d\n", stats->locks);
1999 fprintf(out, " Load Operations : %10d\n", stats->load);
2000 fprintf(out, " Loads - uncacheable : %10d\n", stats->ld_uncache);
2001 fprintf(out, " Loads - IO : %10d\n", stats->ld_io);
2002 fprintf(out, " Loads - Miss : %10d\n", stats->ld_miss);
2003 fprintf(out, " Loads - no mapping : %10d\n", stats->ld_noadrs);
2004 fprintf(out, " Load Fill Buffer Hit : %10d\n", stats->ld_fbhit);
2005 fprintf(out, " Load L1D hit : %10d\n", stats->ld_l1hit);
2006 fprintf(out, " Load L2D hit : %10d\n", stats->ld_l2hit);
2007 fprintf(out, " Load LLC hit : %10d\n", stats->ld_llchit + stats->lcl_hitm);
2008 fprintf(out, " Load Local HITM : %10d\n", stats->lcl_hitm);
2009 fprintf(out, " Load Remote HITM : %10d\n", stats->rmt_hitm);
2010 fprintf(out, " Load Remote HIT : %10d\n", stats->rmt_hit);
2011 fprintf(out, " Load Local DRAM : %10d\n", stats->lcl_dram);
2012 fprintf(out, " Load Remote DRAM : %10d\n", stats->rmt_dram);
2013 fprintf(out, " Load MESI State Exclusive : %10d\n", stats->ld_excl);
2014 fprintf(out, " Load MESI State Shared : %10d\n", stats->ld_shared);
2015 fprintf(out, " Load LLC Misses : %10d\n", llc_misses);
2016 fprintf(out, " LLC Misses to Local DRAM : %10.1f%%\n", ((double)stats->lcl_dram/(double)llc_misses) * 100.);
2017 fprintf(out, " LLC Misses to Remote DRAM : %10.1f%%\n", ((double)stats->rmt_dram/(double)llc_misses) * 100.);
2018 fprintf(out, " LLC Misses to Remote cache (HIT) : %10.1f%%\n", ((double)stats->rmt_hit /(double)llc_misses) * 100.);
2019 fprintf(out, " LLC Misses to Remote cache (HITM) : %10.1f%%\n", ((double)stats->rmt_hitm/(double)llc_misses) * 100.);
2020 fprintf(out, " Store Operations : %10d\n", stats->store);
2021 fprintf(out, " Store - uncacheable : %10d\n", stats->st_uncache);
2022 fprintf(out, " Store - no mapping : %10d\n", stats->st_noadrs);
2023 fprintf(out, " Store L1D Hit : %10d\n", stats->st_l1hit);
2024 fprintf(out, " Store L1D Miss : %10d\n", stats->st_l1miss);
2025 fprintf(out, " No Page Map Rejects : %10d\n", stats->nomap);
2026 fprintf(out, " Unable to parse data source : %10d\n", stats->noparse);
2029 static void print_shared_cacheline_info(FILE *out)
2031 struct c2c_stats *stats = &c2c.hitm_stats;
2032 int hitm_cnt = stats->lcl_hitm + stats->rmt_hitm;
2034 fprintf(out, "=================================================\n");
2035 fprintf(out, " Global Shared Cache Line Event Information \n");
2036 fprintf(out, "=================================================\n");
2037 fprintf(out, " Total Shared Cache Lines : %10d\n", c2c.shared_clines);
2038 fprintf(out, " Load HITs on shared lines : %10d\n", stats->load);
2039 fprintf(out, " Fill Buffer Hits on shared lines : %10d\n", stats->ld_fbhit);
2040 fprintf(out, " L1D hits on shared lines : %10d\n", stats->ld_l1hit);
2041 fprintf(out, " L2D hits on shared lines : %10d\n", stats->ld_l2hit);
2042 fprintf(out, " LLC hits on shared lines : %10d\n", stats->ld_llchit + stats->lcl_hitm);
2043 fprintf(out, " Locked Access on shared lines : %10d\n", stats->locks);
2044 fprintf(out, " Store HITs on shared lines : %10d\n", stats->store);
2045 fprintf(out, " Store L1D hits on shared lines : %10d\n", stats->st_l1hit);
2046 fprintf(out, " Total Merged records : %10d\n", hitm_cnt + stats->store);
2049 static void print_cacheline(struct c2c_hists *c2c_hists,
2050 struct hist_entry *he_cl,
2051 struct perf_hpp_list *hpp_list,
2052 FILE *out)
2054 char bf[1000];
2055 struct perf_hpp hpp = {
2056 .buf = bf,
2057 .size = 1000,
2059 static bool once;
2061 if (!once) {
2062 hists__fprintf_headers(&c2c_hists->hists, out);
2063 once = true;
2064 } else {
2065 fprintf(out, "\n");
2068 fprintf(out, " -------------------------------------------------------------\n");
2069 __hist_entry__snprintf(he_cl, &hpp, hpp_list);
2070 fprintf(out, "%s\n", bf);
2071 fprintf(out, " -------------------------------------------------------------\n");
2073 hists__fprintf(&c2c_hists->hists, false, 0, 0, 0, out, true);
2076 static void print_pareto(FILE *out)
2078 struct perf_hpp_list hpp_list;
2079 struct rb_node *nd;
2080 int ret;
2082 perf_hpp_list__init(&hpp_list);
2083 ret = hpp_list__parse(&hpp_list,
2084 "cl_num,"
2085 "cl_rmt_hitm,"
2086 "cl_lcl_hitm,"
2087 "cl_stores_l1hit,"
2088 "cl_stores_l1miss,"
2089 "dcacheline",
2090 NULL);
2092 if (WARN_ONCE(ret, "failed to setup sort entries\n"))
2093 return;
2095 nd = rb_first(&c2c.hists.hists.entries);
2097 for (; nd; nd = rb_next(nd)) {
2098 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
2099 struct c2c_hist_entry *c2c_he;
2101 if (he->filtered)
2102 continue;
2104 c2c_he = container_of(he, struct c2c_hist_entry, he);
2105 print_cacheline(c2c_he->hists, he, &hpp_list, out);
2109 static void print_c2c_info(FILE *out, struct perf_session *session)
2111 struct perf_evlist *evlist = session->evlist;
2112 struct perf_evsel *evsel;
2113 bool first = true;
2115 fprintf(out, "=================================================\n");
2116 fprintf(out, " c2c details \n");
2117 fprintf(out, "=================================================\n");
2119 evlist__for_each_entry(evlist, evsel) {
2120 fprintf(out, "%-36s: %s\n", first ? " Events" : "",
2121 perf_evsel__name(evsel));
2122 first = false;
2124 fprintf(out, " Cachelines sort on : %s HITMs\n",
2125 display_str[c2c.display]);
2126 fprintf(out, " Cacheline data grouping : %s\n", c2c.cl_sort);
2129 static void perf_c2c__hists_fprintf(FILE *out, struct perf_session *session)
2131 setup_pager();
2133 print_c2c__display_stats(out);
2134 fprintf(out, "\n");
2135 print_shared_cacheline_info(out);
2136 fprintf(out, "\n");
2137 print_c2c_info(out, session);
2139 if (c2c.stats_only)
2140 return;
2142 fprintf(out, "\n");
2143 fprintf(out, "=================================================\n");
2144 fprintf(out, " Shared Data Cache Line Table \n");
2145 fprintf(out, "=================================================\n");
2146 fprintf(out, "#\n");
2148 hists__fprintf(&c2c.hists.hists, true, 0, 0, 0, stdout, false);
2150 fprintf(out, "\n");
2151 fprintf(out, "=================================================\n");
2152 fprintf(out, " Shared Cache Line Distribution Pareto \n");
2153 fprintf(out, "=================================================\n");
2154 fprintf(out, "#\n");
2156 print_pareto(out);
2159 #ifdef HAVE_SLANG_SUPPORT
2160 static void c2c_browser__update_nr_entries(struct hist_browser *hb)
2162 u64 nr_entries = 0;
2163 struct rb_node *nd = rb_first(&hb->hists->entries);
2165 while (nd) {
2166 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
2168 if (!he->filtered)
2169 nr_entries++;
2171 nd = rb_next(nd);
2174 hb->nr_non_filtered_entries = nr_entries;
2177 struct c2c_cacheline_browser {
2178 struct hist_browser hb;
2179 struct hist_entry *he;
2182 static int
2183 perf_c2c_cacheline_browser__title(struct hist_browser *browser,
2184 char *bf, size_t size)
2186 struct c2c_cacheline_browser *cl_browser;
2187 struct hist_entry *he;
2188 uint64_t addr = 0;
2190 cl_browser = container_of(browser, struct c2c_cacheline_browser, hb);
2191 he = cl_browser->he;
2193 if (he->mem_info)
2194 addr = cl_address(he->mem_info->daddr.addr);
2196 scnprintf(bf, size, "Cacheline 0x%lx", addr);
2197 return 0;
2200 static struct c2c_cacheline_browser*
2201 c2c_cacheline_browser__new(struct hists *hists, struct hist_entry *he)
2203 struct c2c_cacheline_browser *browser;
2205 browser = zalloc(sizeof(*browser));
2206 if (browser) {
2207 hist_browser__init(&browser->hb, hists);
2208 browser->hb.c2c_filter = true;
2209 browser->hb.title = perf_c2c_cacheline_browser__title;
2210 browser->he = he;
2213 return browser;
2216 static int perf_c2c__browse_cacheline(struct hist_entry *he)
2218 struct c2c_hist_entry *c2c_he;
2219 struct c2c_hists *c2c_hists;
2220 struct c2c_cacheline_browser *cl_browser;
2221 struct hist_browser *browser;
2222 int key = -1;
2223 const char help[] =
2224 " ENTER Toggle callchains (if present) \n"
2225 " n Toggle Node details info \n"
2226 " s Toggle full length of symbol and source line columns \n"
2227 " q Return back to cacheline list \n";
2229 /* Display compact version first. */
2230 c2c.symbol_full = false;
2232 c2c_he = container_of(he, struct c2c_hist_entry, he);
2233 c2c_hists = c2c_he->hists;
2235 cl_browser = c2c_cacheline_browser__new(&c2c_hists->hists, he);
2236 if (cl_browser == NULL)
2237 return -1;
2239 browser = &cl_browser->hb;
2241 /* reset abort key so that it can get Ctrl-C as a key */
2242 SLang_reset_tty();
2243 SLang_init_tty(0, 0, 0);
2245 c2c_browser__update_nr_entries(browser);
2247 while (1) {
2248 key = hist_browser__run(browser, "? - help", true);
2250 switch (key) {
2251 case 's':
2252 c2c.symbol_full = !c2c.symbol_full;
2253 break;
2254 case 'n':
2255 c2c.node_info = (c2c.node_info + 1) % 3;
2256 setup_nodes_header();
2257 break;
2258 case 'q':
2259 goto out;
2260 case '?':
2261 ui_browser__help_window(&browser->b, help);
2262 break;
2263 default:
2264 break;
2268 out:
2269 free(cl_browser);
2270 return 0;
2273 static int perf_c2c_browser__title(struct hist_browser *browser,
2274 char *bf, size_t size)
2276 scnprintf(bf, size,
2277 "Shared Data Cache Line Table "
2278 "(%lu entries, sorted on %s HITMs)",
2279 browser->nr_non_filtered_entries,
2280 display_str[c2c.display]);
2281 return 0;
2284 static struct hist_browser*
2285 perf_c2c_browser__new(struct hists *hists)
2287 struct hist_browser *browser = hist_browser__new(hists);
2289 if (browser) {
2290 browser->title = perf_c2c_browser__title;
2291 browser->c2c_filter = true;
2294 return browser;
2297 static int perf_c2c__hists_browse(struct hists *hists)
2299 struct hist_browser *browser;
2300 int key = -1;
2301 const char help[] =
2302 " d Display cacheline details \n"
2303 " ENTER Toggle callchains (if present) \n"
2304 " q Quit \n";
2306 browser = perf_c2c_browser__new(hists);
2307 if (browser == NULL)
2308 return -1;
2310 /* reset abort key so that it can get Ctrl-C as a key */
2311 SLang_reset_tty();
2312 SLang_init_tty(0, 0, 0);
2314 c2c_browser__update_nr_entries(browser);
2316 while (1) {
2317 key = hist_browser__run(browser, "? - help", true);
2319 switch (key) {
2320 case 'q':
2321 goto out;
2322 case 'd':
2323 perf_c2c__browse_cacheline(browser->he_selection);
2324 break;
2325 case '?':
2326 ui_browser__help_window(&browser->b, help);
2327 break;
2328 default:
2329 break;
2333 out:
2334 hist_browser__delete(browser);
2335 return 0;
2338 static void perf_c2c_display(struct perf_session *session)
2340 if (use_browser == 0)
2341 perf_c2c__hists_fprintf(stdout, session);
2342 else
2343 perf_c2c__hists_browse(&c2c.hists.hists);
2345 #else
2346 static void perf_c2c_display(struct perf_session *session)
2348 use_browser = 0;
2349 perf_c2c__hists_fprintf(stdout, session);
2351 #endif /* HAVE_SLANG_SUPPORT */
2353 static void ui_quirks(void)
2355 if (!c2c.use_stdio) {
2356 dim_offset.width = 5;
2357 dim_offset.header = header_offset_tui;
2360 dim_percent_hitm.header = percent_hitm_header[c2c.display];
2363 #define CALLCHAIN_DEFAULT_OPT "graph,0.5,caller,function,percent"
2365 const char callchain_help[] = "Display call graph (stack chain/backtrace):\n\n"
2366 CALLCHAIN_REPORT_HELP
2367 "\n\t\t\t\tDefault: " CALLCHAIN_DEFAULT_OPT;
2369 static int
2370 parse_callchain_opt(const struct option *opt, const char *arg, int unset)
2372 struct callchain_param *callchain = opt->value;
2374 callchain->enabled = !unset;
2376 * --no-call-graph
2378 if (unset) {
2379 symbol_conf.use_callchain = false;
2380 callchain->mode = CHAIN_NONE;
2381 return 0;
2384 return parse_callchain_report_opt(arg);
2387 static int setup_callchain(struct perf_evlist *evlist)
2389 u64 sample_type = perf_evlist__combined_sample_type(evlist);
2390 enum perf_call_graph_mode mode = CALLCHAIN_NONE;
2392 if ((sample_type & PERF_SAMPLE_REGS_USER) &&
2393 (sample_type & PERF_SAMPLE_STACK_USER)) {
2394 mode = CALLCHAIN_DWARF;
2395 dwarf_callchain_users = true;
2396 } else if (sample_type & PERF_SAMPLE_BRANCH_STACK)
2397 mode = CALLCHAIN_LBR;
2398 else if (sample_type & PERF_SAMPLE_CALLCHAIN)
2399 mode = CALLCHAIN_FP;
2401 if (!callchain_param.enabled &&
2402 callchain_param.mode != CHAIN_NONE &&
2403 mode != CALLCHAIN_NONE) {
2404 symbol_conf.use_callchain = true;
2405 if (callchain_register_param(&callchain_param) < 0) {
2406 ui__error("Can't register callchain params.\n");
2407 return -EINVAL;
2411 callchain_param.record_mode = mode;
2412 callchain_param.min_percent = 0;
2413 return 0;
2416 static int setup_display(const char *str)
2418 const char *display = str ?: "tot";
2420 if (!strcmp(display, "tot"))
2421 c2c.display = DISPLAY_TOT;
2422 else if (!strcmp(display, "rmt"))
2423 c2c.display = DISPLAY_RMT;
2424 else if (!strcmp(display, "lcl"))
2425 c2c.display = DISPLAY_LCL;
2426 else {
2427 pr_err("failed: unknown display type: %s\n", str);
2428 return -1;
2431 return 0;
2434 #define for_each_token(__tok, __buf, __sep, __tmp) \
2435 for (__tok = strtok_r(__buf, __sep, &__tmp); __tok; \
2436 __tok = strtok_r(NULL, __sep, &__tmp))
2438 static int build_cl_output(char *cl_sort, bool no_source)
2440 char *tok, *tmp, *buf = strdup(cl_sort);
2441 bool add_pid = false;
2442 bool add_tid = false;
2443 bool add_iaddr = false;
2444 bool add_sym = false;
2445 bool add_dso = false;
2446 bool add_src = false;
2448 if (!buf)
2449 return -ENOMEM;
2451 for_each_token(tok, buf, ",", tmp) {
2452 if (!strcmp(tok, "tid")) {
2453 add_tid = true;
2454 } else if (!strcmp(tok, "pid")) {
2455 add_pid = true;
2456 } else if (!strcmp(tok, "iaddr")) {
2457 add_iaddr = true;
2458 add_sym = true;
2459 add_dso = true;
2460 add_src = no_source ? false : true;
2461 } else if (!strcmp(tok, "dso")) {
2462 add_dso = true;
2463 } else if (strcmp(tok, "offset")) {
2464 pr_err("unrecognized sort token: %s\n", tok);
2465 return -EINVAL;
2469 if (asprintf(&c2c.cl_output,
2470 "%s%s%s%s%s%s%s%s%s%s",
2471 c2c.use_stdio ? "cl_num_empty," : "",
2472 "percent_rmt_hitm,"
2473 "percent_lcl_hitm,"
2474 "percent_stores_l1hit,"
2475 "percent_stores_l1miss,"
2476 "offset,",
2477 add_pid ? "pid," : "",
2478 add_tid ? "tid," : "",
2479 add_iaddr ? "iaddr," : "",
2480 "mean_rmt,"
2481 "mean_lcl,"
2482 "mean_load,"
2483 "tot_recs,"
2484 "cpucnt,",
2485 add_sym ? "symbol," : "",
2486 add_dso ? "dso," : "",
2487 add_src ? "cl_srcline," : "",
2488 "node") < 0)
2489 return -ENOMEM;
2491 c2c.show_src = add_src;
2493 free(buf);
2494 return 0;
2497 static int setup_coalesce(const char *coalesce, bool no_source)
2499 const char *c = coalesce ?: coalesce_default;
2501 if (asprintf(&c2c.cl_sort, "offset,%s", c) < 0)
2502 return -ENOMEM;
2504 if (build_cl_output(c2c.cl_sort, no_source))
2505 return -1;
2507 if (asprintf(&c2c.cl_resort, "offset,%s",
2508 c2c.display == DISPLAY_TOT ?
2509 "tot_hitm" :
2510 c2c.display == DISPLAY_RMT ?
2511 "rmt_hitm,lcl_hitm" :
2512 "lcl_hitm,rmt_hitm") < 0)
2513 return -ENOMEM;
2515 pr_debug("coalesce sort fields: %s\n", c2c.cl_sort);
2516 pr_debug("coalesce resort fields: %s\n", c2c.cl_resort);
2517 pr_debug("coalesce output fields: %s\n", c2c.cl_output);
2518 return 0;
2521 static int perf_c2c__report(int argc, const char **argv)
2523 struct perf_session *session;
2524 struct ui_progress prog;
2525 struct perf_data data = {
2526 .mode = PERF_DATA_MODE_READ,
2528 char callchain_default_opt[] = CALLCHAIN_DEFAULT_OPT;
2529 const char *display = NULL;
2530 const char *coalesce = NULL;
2531 bool no_source = false;
2532 const struct option options[] = {
2533 OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
2534 "file", "vmlinux pathname"),
2535 OPT_STRING('i', "input", &input_name, "file",
2536 "the input file to process"),
2537 OPT_INCR('N', "node-info", &c2c.node_info,
2538 "show extra node info in report (repeat for more info)"),
2539 #ifdef HAVE_SLANG_SUPPORT
2540 OPT_BOOLEAN(0, "stdio", &c2c.use_stdio, "Use the stdio interface"),
2541 #endif
2542 OPT_BOOLEAN(0, "stats", &c2c.stats_only,
2543 "Display only statistic tables (implies --stdio)"),
2544 OPT_BOOLEAN(0, "full-symbols", &c2c.symbol_full,
2545 "Display full length of symbols"),
2546 OPT_BOOLEAN(0, "no-source", &no_source,
2547 "Do not display Source Line column"),
2548 OPT_BOOLEAN(0, "show-all", &c2c.show_all,
2549 "Show all captured HITM lines."),
2550 OPT_CALLBACK_DEFAULT('g', "call-graph", &callchain_param,
2551 "print_type,threshold[,print_limit],order,sort_key[,branch],value",
2552 callchain_help, &parse_callchain_opt,
2553 callchain_default_opt),
2554 OPT_STRING('d', "display", &display, "Switch HITM output type", "lcl,rmt"),
2555 OPT_STRING('c', "coalesce", &coalesce, "coalesce fields",
2556 "coalesce fields: pid,tid,iaddr,dso"),
2557 OPT_BOOLEAN('f', "force", &symbol_conf.force, "don't complain, do it"),
2558 OPT_PARENT(c2c_options),
2559 OPT_END()
2561 int err = 0;
2563 argc = parse_options(argc, argv, options, report_c2c_usage,
2564 PARSE_OPT_STOP_AT_NON_OPTION);
2565 if (argc)
2566 usage_with_options(report_c2c_usage, options);
2568 if (c2c.stats_only)
2569 c2c.use_stdio = true;
2571 if (!input_name || !strlen(input_name))
2572 input_name = "perf.data";
2574 data.file.path = input_name;
2575 data.force = symbol_conf.force;
2577 err = setup_display(display);
2578 if (err)
2579 goto out;
2581 err = setup_coalesce(coalesce, no_source);
2582 if (err) {
2583 pr_debug("Failed to initialize hists\n");
2584 goto out;
2587 err = c2c_hists__init(&c2c.hists, "dcacheline", 2);
2588 if (err) {
2589 pr_debug("Failed to initialize hists\n");
2590 goto out;
2593 session = perf_session__new(&data, 0, &c2c.tool);
2594 if (session == NULL) {
2595 pr_debug("No memory for session\n");
2596 goto out;
2599 err = setup_nodes(session);
2600 if (err) {
2601 pr_err("Failed setup nodes\n");
2602 goto out;
2605 err = setup_callchain(session->evlist);
2606 if (err)
2607 goto out_session;
2609 if (symbol__init(&session->header.env) < 0)
2610 goto out_session;
2612 /* No pipe support at the moment. */
2613 if (perf_data__is_pipe(session->data)) {
2614 pr_debug("No pipe support at the moment.\n");
2615 goto out_session;
2618 if (c2c.use_stdio)
2619 use_browser = 0;
2620 else
2621 use_browser = 1;
2623 setup_browser(false);
2625 err = perf_session__process_events(session);
2626 if (err) {
2627 pr_err("failed to process sample\n");
2628 goto out_session;
2631 c2c_hists__reinit(&c2c.hists,
2632 "cl_idx,"
2633 "dcacheline,"
2634 "tot_recs,"
2635 "percent_hitm,"
2636 "tot_hitm,lcl_hitm,rmt_hitm,"
2637 "stores,stores_l1hit,stores_l1miss,"
2638 "dram_lcl,dram_rmt,"
2639 "ld_llcmiss,"
2640 "tot_loads,"
2641 "ld_fbhit,ld_l1hit,ld_l2hit,"
2642 "ld_lclhit,ld_rmthit",
2643 c2c.display == DISPLAY_TOT ? "tot_hitm" :
2644 c2c.display == DISPLAY_LCL ? "lcl_hitm" : "rmt_hitm"
2647 ui_progress__init(&prog, c2c.hists.hists.nr_entries, "Sorting...");
2649 hists__collapse_resort(&c2c.hists.hists, NULL);
2650 hists__output_resort_cb(&c2c.hists.hists, &prog, resort_hitm_cb);
2651 hists__iterate_cb(&c2c.hists.hists, resort_cl_cb);
2653 ui_progress__finish();
2655 ui_quirks();
2657 perf_c2c_display(session);
2659 out_session:
2660 perf_session__delete(session);
2661 out:
2662 return err;
2665 static int parse_record_events(const struct option *opt,
2666 const char *str, int unset __maybe_unused)
2668 bool *event_set = (bool *) opt->value;
2670 *event_set = true;
2671 return perf_mem_events__parse(str);
2675 static const char * const __usage_record[] = {
2676 "perf c2c record [<options>] [<command>]",
2677 "perf c2c record [<options>] -- <command> [<options>]",
2678 NULL
2681 static const char * const *record_mem_usage = __usage_record;
2683 static int perf_c2c__record(int argc, const char **argv)
2685 int rec_argc, i = 0, j;
2686 const char **rec_argv;
2687 int ret;
2688 bool all_user = false, all_kernel = false;
2689 bool event_set = false;
2690 struct option options[] = {
2691 OPT_CALLBACK('e', "event", &event_set, "event",
2692 "event selector. Use 'perf mem record -e list' to list available events",
2693 parse_record_events),
2694 OPT_BOOLEAN('u', "all-user", &all_user, "collect only user level data"),
2695 OPT_BOOLEAN('k', "all-kernel", &all_kernel, "collect only kernel level data"),
2696 OPT_UINTEGER('l', "ldlat", &perf_mem_events__loads_ldlat, "setup mem-loads latency"),
2697 OPT_PARENT(c2c_options),
2698 OPT_END()
2701 if (perf_mem_events__init()) {
2702 pr_err("failed: memory events not supported\n");
2703 return -1;
2706 argc = parse_options(argc, argv, options, record_mem_usage,
2707 PARSE_OPT_KEEP_UNKNOWN);
2709 rec_argc = argc + 10; /* max number of arguments */
2710 rec_argv = calloc(rec_argc + 1, sizeof(char *));
2711 if (!rec_argv)
2712 return -1;
2714 rec_argv[i++] = "record";
2716 if (!event_set) {
2717 perf_mem_events[PERF_MEM_EVENTS__LOAD].record = true;
2718 perf_mem_events[PERF_MEM_EVENTS__STORE].record = true;
2721 if (perf_mem_events[PERF_MEM_EVENTS__LOAD].record)
2722 rec_argv[i++] = "-W";
2724 rec_argv[i++] = "-d";
2725 rec_argv[i++] = "--sample-cpu";
2727 for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
2728 if (!perf_mem_events[j].record)
2729 continue;
2731 if (!perf_mem_events[j].supported) {
2732 pr_err("failed: event '%s' not supported\n",
2733 perf_mem_events[j].name);
2734 free(rec_argv);
2735 return -1;
2738 rec_argv[i++] = "-e";
2739 rec_argv[i++] = perf_mem_events__name(j);
2742 if (all_user)
2743 rec_argv[i++] = "--all-user";
2745 if (all_kernel)
2746 rec_argv[i++] = "--all-kernel";
2748 for (j = 0; j < argc; j++, i++)
2749 rec_argv[i] = argv[j];
2751 if (verbose > 0) {
2752 pr_debug("calling: ");
2754 j = 0;
2756 while (rec_argv[j]) {
2757 pr_debug("%s ", rec_argv[j]);
2758 j++;
2760 pr_debug("\n");
2763 ret = cmd_record(i, rec_argv);
2764 free(rec_argv);
2765 return ret;
2768 int cmd_c2c(int argc, const char **argv)
2770 argc = parse_options(argc, argv, c2c_options, c2c_usage,
2771 PARSE_OPT_STOP_AT_NON_OPTION);
2773 if (!argc)
2774 usage_with_options(c2c_usage, c2c_options);
2776 if (!strncmp(argv[0], "rec", 3)) {
2777 return perf_c2c__record(argc, argv);
2778 } else if (!strncmp(argv[0], "rep", 3)) {
2779 return perf_c2c__report(argc, argv);
2780 } else {
2781 usage_with_options(c2c_usage, c2c_options);
2784 return 0;