1 // SPDX-License-Identifier: GPL-2.0
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.
15 #include <linux/compiler.h>
16 #include <linux/err.h>
17 #include <linux/kernel.h>
18 #include <linux/stringify.h>
19 #include <linux/zalloc.h>
21 #include <sys/param.h>
24 #include <perf/cpumap.h>
25 #include <subcmd/pager.h>
26 #include <subcmd/parse-options.h>
27 #include "map_symbol.h"
28 #include "mem-events.h"
33 #include "cacheline.h"
38 #include "ui/browsers/hists.h"
44 #include "ui/progress.h"
47 #include "util/util.h"
51 struct perf_hpp_list list;
52 struct c2c_stats stats;
55 struct compute_stats {
56 struct stats lcl_hitm;
57 struct stats rmt_hitm;
58 struct stats lcl_peer;
59 struct stats rmt_peer;
63 struct c2c_hist_entry {
64 struct c2c_hists *hists;
65 struct c2c_stats stats;
66 unsigned long *cpuset;
67 unsigned long *nodeset;
68 struct c2c_stats *node_stats;
69 unsigned int cacheline_idx;
71 struct compute_stats cstats;
74 unsigned long paddr_cnt;
80 * because of its callchain dynamic entry
85 static char const *coalesce_default = "iaddr";
88 struct perf_tool tool;
89 struct c2c_hists hists;
90 struct mem2node mem2node;
92 unsigned long **nodes;
105 /* Shared cache line stats */
106 struct c2c_stats shared_clines_stats;
111 const char *coalesce;
125 static const char *display_str[DISPLAY_MAX] = {
126 [DISPLAY_LCL_HITM] = "Local HITMs",
127 [DISPLAY_RMT_HITM] = "Remote HITMs",
128 [DISPLAY_TOT_HITM] = "Total HITMs",
129 [DISPLAY_SNP_PEER] = "Peer Snoop",
132 static const struct option c2c_options[] = {
133 OPT_INCR('v', "verbose", &verbose, "be more verbose (show counter open errors, etc)"),
137 static struct perf_c2c c2c;
139 static void *c2c_he_zalloc(size_t size)
141 struct c2c_hist_entry *c2c_he;
143 c2c_he = zalloc(size + sizeof(*c2c_he));
147 c2c_he->cpuset = bitmap_zalloc(c2c.cpus_cnt);
151 c2c_he->nodeset = bitmap_zalloc(c2c.nodes_cnt);
152 if (!c2c_he->nodeset)
155 c2c_he->node_stats = zalloc(c2c.nodes_cnt * sizeof(*c2c_he->node_stats));
156 if (!c2c_he->node_stats)
159 init_stats(&c2c_he->cstats.lcl_hitm);
160 init_stats(&c2c_he->cstats.rmt_hitm);
161 init_stats(&c2c_he->cstats.lcl_peer);
162 init_stats(&c2c_he->cstats.rmt_peer);
163 init_stats(&c2c_he->cstats.load);
168 zfree(&c2c_he->nodeset);
169 zfree(&c2c_he->cpuset);
174 static void c2c_he_free(void *he)
176 struct c2c_hist_entry *c2c_he;
178 c2c_he = container_of(he, struct c2c_hist_entry, he);
180 hists__delete_entries(&c2c_he->hists->hists);
181 zfree(&c2c_he->hists);
184 zfree(&c2c_he->cpuset);
185 zfree(&c2c_he->nodeset);
186 zfree(&c2c_he->nodestr);
187 zfree(&c2c_he->node_stats);
191 static struct hist_entry_ops c2c_entry_ops = {
192 .new = c2c_he_zalloc,
196 static int c2c_hists__init(struct c2c_hists *hists,
198 int nr_header_lines);
200 static struct c2c_hists*
201 he__get_c2c_hists(struct hist_entry *he,
205 struct c2c_hist_entry *c2c_he;
206 struct c2c_hists *hists;
209 c2c_he = container_of(he, struct c2c_hist_entry, he);
211 return c2c_he->hists;
213 hists = c2c_he->hists = zalloc(sizeof(*hists));
217 ret = c2c_hists__init(hists, sort, nr_header_lines);
226 static void c2c_he__set_cpu(struct c2c_hist_entry *c2c_he,
227 struct perf_sample *sample)
229 if (WARN_ONCE(sample->cpu == (unsigned int) -1,
230 "WARNING: no sample cpu value"))
233 __set_bit(sample->cpu, c2c_he->cpuset);
236 static void c2c_he__set_node(struct c2c_hist_entry *c2c_he,
237 struct perf_sample *sample)
241 if (!sample->phys_addr) {
242 c2c_he->paddr_zero = true;
246 node = mem2node__node(&c2c.mem2node, sample->phys_addr);
247 if (WARN_ONCE(node < 0, "WARNING: failed to find node\n"))
250 __set_bit(node, c2c_he->nodeset);
252 if (c2c_he->paddr != sample->phys_addr) {
254 c2c_he->paddr = sample->phys_addr;
258 static void compute_stats(struct c2c_hist_entry *c2c_he,
259 struct c2c_stats *stats,
262 struct compute_stats *cstats = &c2c_he->cstats;
265 update_stats(&cstats->rmt_hitm, weight);
266 else if (stats->lcl_hitm)
267 update_stats(&cstats->lcl_hitm, weight);
268 else if (stats->rmt_peer)
269 update_stats(&cstats->rmt_peer, weight);
270 else if (stats->lcl_peer)
271 update_stats(&cstats->lcl_peer, weight);
272 else if (stats->load)
273 update_stats(&cstats->load, weight);
276 static int process_sample_event(struct perf_tool *tool __maybe_unused,
277 union perf_event *event,
278 struct perf_sample *sample,
280 struct machine *machine)
282 struct c2c_hists *c2c_hists = &c2c.hists;
283 struct c2c_hist_entry *c2c_he;
284 struct c2c_stats stats = { .nr_entries = 0, };
285 struct hist_entry *he;
286 struct addr_location al;
287 struct mem_info *mi, *mi_dup;
288 struct callchain_cursor *cursor;
291 addr_location__init(&al);
292 if (machine__resolve(machine, &al, sample) < 0) {
293 pr_debug("problem processing %d event, skipping it.\n",
300 thread__set_lbr_stitch_enable(al.thread, true);
302 cursor = get_tls_callchain_cursor();
303 ret = sample__resolve_callchain(sample, cursor, NULL,
304 evsel, &al, sysctl_perf_event_max_stack);
308 mi = sample__resolve_mem(sample, &al);
315 * The mi object is released in hists__add_entry_ops,
316 * if it gets sorted out into existing data, so we need
317 * to take the copy now.
319 mi_dup = mem_info__get(mi);
321 c2c_decode_stats(&stats, mi);
323 he = hists__add_entry_ops(&c2c_hists->hists, &c2c_entry_ops,
324 &al, NULL, NULL, mi, NULL,
329 c2c_he = container_of(he, struct c2c_hist_entry, he);
330 c2c_add_stats(&c2c_he->stats, &stats);
331 c2c_add_stats(&c2c_hists->stats, &stats);
333 c2c_he__set_cpu(c2c_he, sample);
334 c2c_he__set_node(c2c_he, sample);
336 hists__inc_nr_samples(&c2c_hists->hists, he->filtered);
337 ret = hist_entry__append_callchain(he, sample);
341 * There's already been warning about missing
342 * sample's cpu value. Let's account all to
343 * node 0 in this case, without any further
346 * Doing node stats only for single callchain data.
348 int cpu = sample->cpu == (unsigned int) -1 ? 0 : sample->cpu;
349 int node = c2c.cpu2node[cpu];
353 c2c_hists = he__get_c2c_hists(he, c2c.cl_sort, 2);
357 he = hists__add_entry_ops(&c2c_hists->hists, &c2c_entry_ops,
358 &al, NULL, NULL, mi, NULL,
363 c2c_he = container_of(he, struct c2c_hist_entry, he);
364 c2c_add_stats(&c2c_he->stats, &stats);
365 c2c_add_stats(&c2c_hists->stats, &stats);
366 c2c_add_stats(&c2c_he->node_stats[node], &stats);
368 compute_stats(c2c_he, &stats, sample->weight);
370 c2c_he__set_cpu(c2c_he, sample);
371 c2c_he__set_node(c2c_he, sample);
373 hists__inc_nr_samples(&c2c_hists->hists, he->filtered);
374 ret = hist_entry__append_callchain(he, sample);
378 addr_location__exit(&al);
382 mem_info__put(mi_dup);
388 static struct perf_c2c c2c = {
390 .sample = process_sample_event,
391 .mmap = perf_event__process_mmap,
392 .mmap2 = perf_event__process_mmap2,
393 .comm = perf_event__process_comm,
394 .exit = perf_event__process_exit,
395 .fork = perf_event__process_fork,
396 .lost = perf_event__process_lost,
397 .attr = perf_event__process_attr,
398 .auxtrace_info = perf_event__process_auxtrace_info,
399 .auxtrace = perf_event__process_auxtrace,
400 .auxtrace_error = perf_event__process_auxtrace_error,
401 .ordered_events = true,
402 .ordering_requires_timestamps = true,
406 static const char * const c2c_usage[] = {
407 "perf c2c {record|report}",
411 static const char * const __usage_report[] = {
416 static const char * const *report_c2c_usage = __usage_report;
418 #define C2C_HEADER_MAX 2
424 } line[C2C_HEADER_MAX];
427 struct c2c_dimension {
428 struct c2c_header header;
431 struct sort_entry *se;
433 int64_t (*cmp)(struct perf_hpp_fmt *fmt,
434 struct hist_entry *, struct hist_entry *);
435 int (*entry)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
436 struct hist_entry *he);
437 int (*color)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
438 struct hist_entry *he);
442 struct perf_hpp_fmt fmt;
443 struct c2c_dimension *dim;
446 #define SYMBOL_WIDTH 30
448 static struct c2c_dimension dim_symbol;
449 static struct c2c_dimension dim_srcline;
451 static int symbol_width(struct hists *hists, struct sort_entry *se)
453 int width = hists__col_len(hists, se->se_width_idx);
455 if (!c2c.symbol_full)
456 width = MIN(width, SYMBOL_WIDTH);
461 static int c2c_width(struct perf_hpp_fmt *fmt,
462 struct perf_hpp *hpp __maybe_unused,
465 struct c2c_fmt *c2c_fmt;
466 struct c2c_dimension *dim;
468 c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
471 if (dim == &dim_symbol || dim == &dim_srcline)
472 return symbol_width(hists, dim->se);
474 return dim->se ? hists__col_len(hists, dim->se->se_width_idx) :
478 static int c2c_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
479 struct hists *hists, int line, int *span)
481 struct perf_hpp_list *hpp_list = hists->hpp_list;
482 struct c2c_fmt *c2c_fmt;
483 struct c2c_dimension *dim;
484 const char *text = NULL;
485 int width = c2c_width(fmt, hpp, hists);
487 c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
491 text = dim->header.line[line].text;
492 /* Use the last line from sort_entry if not defined. */
493 if (!text && (line == hpp_list->nr_header_lines - 1))
494 text = dim->se->se_header;
496 text = dim->header.line[line].text;
502 *span = dim->header.line[line].span;
509 return scnprintf(hpp->buf, hpp->size, "%*s", width, text);
512 #define HEX_STR(__s, __v) \
514 scnprintf(__s, sizeof(__s), "0x%" PRIx64, __v); \
519 dcacheline_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
520 struct hist_entry *left, struct hist_entry *right)
522 return sort__dcacheline_cmp(left, right);
525 static int dcacheline_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
526 struct hist_entry *he)
529 int width = c2c_width(fmt, hpp, he->hists);
533 addr = cl_address(mem_info__daddr(he->mem_info)->addr, chk_double_cl);
535 return scnprintf(hpp->buf, hpp->size, "%*s", width, HEX_STR(buf, addr));
539 dcacheline_node_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
540 struct hist_entry *he)
542 struct c2c_hist_entry *c2c_he;
543 int width = c2c_width(fmt, hpp, he->hists);
545 c2c_he = container_of(he, struct c2c_hist_entry, he);
546 if (WARN_ON_ONCE(!c2c_he->nodestr))
549 return scnprintf(hpp->buf, hpp->size, "%*s", width, c2c_he->nodestr);
553 dcacheline_node_count(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
554 struct hist_entry *he)
556 struct c2c_hist_entry *c2c_he;
557 int width = c2c_width(fmt, hpp, he->hists);
559 c2c_he = container_of(he, struct c2c_hist_entry, he);
560 return scnprintf(hpp->buf, hpp->size, "%*lu", width, c2c_he->paddr_cnt);
563 static int offset_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
564 struct hist_entry *he)
567 int width = c2c_width(fmt, hpp, he->hists);
571 addr = cl_offset(mem_info__daddr(he->mem_info)->al_addr, chk_double_cl);
573 return scnprintf(hpp->buf, hpp->size, "%*s", width, HEX_STR(buf, addr));
577 offset_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
578 struct hist_entry *left, struct hist_entry *right)
580 uint64_t l = 0, r = 0;
583 l = cl_offset(mem_info__daddr(left->mem_info)->addr, chk_double_cl);
586 r = cl_offset(mem_info__daddr(right->mem_info)->addr, chk_double_cl);
588 return (int64_t)(r - l);
592 iaddr_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
593 struct hist_entry *he)
596 int width = c2c_width(fmt, hpp, he->hists);
600 addr = mem_info__iaddr(he->mem_info)->addr;
602 return scnprintf(hpp->buf, hpp->size, "%*s", width, HEX_STR(buf, addr));
606 iaddr_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
607 struct hist_entry *left, struct hist_entry *right)
609 return sort__iaddr_cmp(left, right);
613 tot_hitm_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
614 struct hist_entry *he)
616 struct c2c_hist_entry *c2c_he;
617 int width = c2c_width(fmt, hpp, he->hists);
618 unsigned int tot_hitm;
620 c2c_he = container_of(he, struct c2c_hist_entry, he);
621 tot_hitm = c2c_he->stats.lcl_hitm + c2c_he->stats.rmt_hitm;
623 return scnprintf(hpp->buf, hpp->size, "%*u", width, tot_hitm);
627 tot_hitm_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
628 struct hist_entry *left, struct hist_entry *right)
630 struct c2c_hist_entry *c2c_left;
631 struct c2c_hist_entry *c2c_right;
632 uint64_t tot_hitm_left;
633 uint64_t tot_hitm_right;
635 c2c_left = container_of(left, struct c2c_hist_entry, he);
636 c2c_right = container_of(right, struct c2c_hist_entry, he);
638 tot_hitm_left = c2c_left->stats.lcl_hitm + c2c_left->stats.rmt_hitm;
639 tot_hitm_right = c2c_right->stats.lcl_hitm + c2c_right->stats.rmt_hitm;
641 return tot_hitm_left - tot_hitm_right;
644 #define STAT_FN_ENTRY(__f) \
646 __f ## _entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, \
647 struct hist_entry *he) \
649 struct c2c_hist_entry *c2c_he; \
650 int width = c2c_width(fmt, hpp, he->hists); \
652 c2c_he = container_of(he, struct c2c_hist_entry, he); \
653 return scnprintf(hpp->buf, hpp->size, "%*u", width, \
654 c2c_he->stats.__f); \
657 #define STAT_FN_CMP(__f) \
659 __f ## _cmp(struct perf_hpp_fmt *fmt __maybe_unused, \
660 struct hist_entry *left, struct hist_entry *right) \
662 struct c2c_hist_entry *c2c_left, *c2c_right; \
664 c2c_left = container_of(left, struct c2c_hist_entry, he); \
665 c2c_right = container_of(right, struct c2c_hist_entry, he); \
666 return (uint64_t) c2c_left->stats.__f - \
667 (uint64_t) c2c_right->stats.__f; \
670 #define STAT_FN(__f) \
689 static uint64_t get_load_llc_misses(struct c2c_stats *stats)
691 return stats->lcl_dram +
697 static uint64_t get_load_cache_hits(struct c2c_stats *stats)
699 return stats->ld_fbhit +
706 static uint64_t get_stores(struct c2c_stats *stats)
708 return stats->st_l1hit +
713 static uint64_t total_records(struct c2c_stats *stats)
715 return get_load_llc_misses(stats) +
716 get_load_cache_hits(stats) +
721 tot_recs_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
722 struct hist_entry *he)
724 struct c2c_hist_entry *c2c_he;
725 int width = c2c_width(fmt, hpp, he->hists);
728 c2c_he = container_of(he, struct c2c_hist_entry, he);
729 tot_recs = total_records(&c2c_he->stats);
731 return scnprintf(hpp->buf, hpp->size, "%*" PRIu64, width, tot_recs);
735 tot_recs_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
736 struct hist_entry *left, struct hist_entry *right)
738 struct c2c_hist_entry *c2c_left;
739 struct c2c_hist_entry *c2c_right;
740 uint64_t tot_recs_left;
741 uint64_t tot_recs_right;
743 c2c_left = container_of(left, struct c2c_hist_entry, he);
744 c2c_right = container_of(right, struct c2c_hist_entry, he);
746 tot_recs_left = total_records(&c2c_left->stats);
747 tot_recs_right = total_records(&c2c_right->stats);
749 return tot_recs_left - tot_recs_right;
752 static uint64_t total_loads(struct c2c_stats *stats)
754 return get_load_llc_misses(stats) +
755 get_load_cache_hits(stats);
759 tot_loads_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
760 struct hist_entry *he)
762 struct c2c_hist_entry *c2c_he;
763 int width = c2c_width(fmt, hpp, he->hists);
766 c2c_he = container_of(he, struct c2c_hist_entry, he);
767 tot_recs = total_loads(&c2c_he->stats);
769 return scnprintf(hpp->buf, hpp->size, "%*" PRIu64, width, tot_recs);
773 tot_loads_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
774 struct hist_entry *left, struct hist_entry *right)
776 struct c2c_hist_entry *c2c_left;
777 struct c2c_hist_entry *c2c_right;
778 uint64_t tot_recs_left;
779 uint64_t tot_recs_right;
781 c2c_left = container_of(left, struct c2c_hist_entry, he);
782 c2c_right = container_of(right, struct c2c_hist_entry, he);
784 tot_recs_left = total_loads(&c2c_left->stats);
785 tot_recs_right = total_loads(&c2c_right->stats);
787 return tot_recs_left - tot_recs_right;
790 typedef double (get_percent_cb)(struct c2c_hist_entry *);
793 percent_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
794 struct hist_entry *he, get_percent_cb get_percent)
796 struct c2c_hist_entry *c2c_he;
797 int width = c2c_width(fmt, hpp, he->hists);
800 c2c_he = container_of(he, struct c2c_hist_entry, he);
801 per = get_percent(c2c_he);
803 #ifdef HAVE_SLANG_SUPPORT
805 return __hpp__slsmg_color_printf(hpp, "%*.2f%%", width - 1, per);
807 return hpp_color_scnprintf(hpp, "%*.2f%%", width - 1, per);
810 static double percent_costly_snoop(struct c2c_hist_entry *c2c_he)
812 struct c2c_hists *hists;
813 struct c2c_stats *stats;
814 struct c2c_stats *total;
818 hists = container_of(c2c_he->he.hists, struct c2c_hists, hists);
819 stats = &c2c_he->stats;
820 total = &hists->stats;
822 switch (c2c.display) {
823 case DISPLAY_RMT_HITM:
824 st = stats->rmt_hitm;
825 tot = total->rmt_hitm;
827 case DISPLAY_LCL_HITM:
828 st = stats->lcl_hitm;
829 tot = total->lcl_hitm;
831 case DISPLAY_TOT_HITM:
832 st = stats->tot_hitm;
833 tot = total->tot_hitm;
835 case DISPLAY_SNP_PEER:
836 st = stats->tot_peer;
837 tot = total->tot_peer;
843 p = tot ? (double) st / tot : 0;
848 #define PERC_STR(__s, __v) \
850 scnprintf(__s, sizeof(__s), "%.2F%%", __v); \
855 percent_costly_snoop_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
856 struct hist_entry *he)
858 struct c2c_hist_entry *c2c_he;
859 int width = c2c_width(fmt, hpp, he->hists);
863 c2c_he = container_of(he, struct c2c_hist_entry, he);
864 per = percent_costly_snoop(c2c_he);
865 return scnprintf(hpp->buf, hpp->size, "%*s", width, PERC_STR(buf, per));
869 percent_costly_snoop_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
870 struct hist_entry *he)
872 return percent_color(fmt, hpp, he, percent_costly_snoop);
876 percent_costly_snoop_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
877 struct hist_entry *left, struct hist_entry *right)
879 struct c2c_hist_entry *c2c_left;
880 struct c2c_hist_entry *c2c_right;
884 c2c_left = container_of(left, struct c2c_hist_entry, he);
885 c2c_right = container_of(right, struct c2c_hist_entry, he);
887 per_left = percent_costly_snoop(c2c_left);
888 per_right = percent_costly_snoop(c2c_right);
890 return per_left - per_right;
893 static struct c2c_stats *he_stats(struct hist_entry *he)
895 struct c2c_hist_entry *c2c_he;
897 c2c_he = container_of(he, struct c2c_hist_entry, he);
898 return &c2c_he->stats;
901 static struct c2c_stats *total_stats(struct hist_entry *he)
903 struct c2c_hists *hists;
905 hists = container_of(he->hists, struct c2c_hists, hists);
906 return &hists->stats;
909 static double percent(u32 st, u32 tot)
911 return tot ? 100. * (double) st / (double) tot : 0;
914 #define PERCENT(__h, __f) percent(he_stats(__h)->__f, total_stats(__h)->__f)
916 #define PERCENT_FN(__f) \
917 static double percent_ ## __f(struct c2c_hist_entry *c2c_he) \
919 struct c2c_hists *hists; \
921 hists = container_of(c2c_he->he.hists, struct c2c_hists, hists); \
922 return percent(c2c_he->stats.__f, hists->stats.__f); \
930 PERCENT_FN(st_l1miss)
934 percent_rmt_hitm_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
935 struct hist_entry *he)
937 int width = c2c_width(fmt, hpp, he->hists);
938 double per = PERCENT(he, rmt_hitm);
941 return scnprintf(hpp->buf, hpp->size, "%*s", width, PERC_STR(buf, per));
945 percent_rmt_hitm_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
946 struct hist_entry *he)
948 return percent_color(fmt, hpp, he, percent_rmt_hitm);
952 percent_rmt_hitm_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
953 struct hist_entry *left, struct hist_entry *right)
958 per_left = PERCENT(left, rmt_hitm);
959 per_right = PERCENT(right, rmt_hitm);
961 return per_left - per_right;
965 percent_lcl_hitm_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
966 struct hist_entry *he)
968 int width = c2c_width(fmt, hpp, he->hists);
969 double per = PERCENT(he, lcl_hitm);
972 return scnprintf(hpp->buf, hpp->size, "%*s", width, PERC_STR(buf, per));
976 percent_lcl_hitm_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
977 struct hist_entry *he)
979 return percent_color(fmt, hpp, he, percent_lcl_hitm);
983 percent_lcl_hitm_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
984 struct hist_entry *left, struct hist_entry *right)
989 per_left = PERCENT(left, lcl_hitm);
990 per_right = PERCENT(right, lcl_hitm);
992 return per_left - per_right;
996 percent_lcl_peer_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
997 struct hist_entry *he)
999 int width = c2c_width(fmt, hpp, he->hists);
1000 double per = PERCENT(he, lcl_peer);
1003 return scnprintf(hpp->buf, hpp->size, "%*s", width, PERC_STR(buf, per));
1007 percent_lcl_peer_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1008 struct hist_entry *he)
1010 return percent_color(fmt, hpp, he, percent_lcl_peer);
1014 percent_lcl_peer_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
1015 struct hist_entry *left, struct hist_entry *right)
1020 per_left = PERCENT(left, lcl_peer);
1021 per_right = PERCENT(right, lcl_peer);
1023 return per_left - per_right;
1027 percent_rmt_peer_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1028 struct hist_entry *he)
1030 int width = c2c_width(fmt, hpp, he->hists);
1031 double per = PERCENT(he, rmt_peer);
1034 return scnprintf(hpp->buf, hpp->size, "%*s", width, PERC_STR(buf, per));
1038 percent_rmt_peer_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1039 struct hist_entry *he)
1041 return percent_color(fmt, hpp, he, percent_rmt_peer);
1045 percent_rmt_peer_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
1046 struct hist_entry *left, struct hist_entry *right)
1051 per_left = PERCENT(left, rmt_peer);
1052 per_right = PERCENT(right, rmt_peer);
1054 return per_left - per_right;
1058 percent_stores_l1hit_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1059 struct hist_entry *he)
1061 int width = c2c_width(fmt, hpp, he->hists);
1062 double per = PERCENT(he, st_l1hit);
1065 return scnprintf(hpp->buf, hpp->size, "%*s", width, PERC_STR(buf, per));
1069 percent_stores_l1hit_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1070 struct hist_entry *he)
1072 return percent_color(fmt, hpp, he, percent_st_l1hit);
1076 percent_stores_l1hit_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
1077 struct hist_entry *left, struct hist_entry *right)
1082 per_left = PERCENT(left, st_l1hit);
1083 per_right = PERCENT(right, st_l1hit);
1085 return per_left - per_right;
1089 percent_stores_l1miss_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1090 struct hist_entry *he)
1092 int width = c2c_width(fmt, hpp, he->hists);
1093 double per = PERCENT(he, st_l1miss);
1096 return scnprintf(hpp->buf, hpp->size, "%*s", width, PERC_STR(buf, per));
1100 percent_stores_l1miss_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1101 struct hist_entry *he)
1103 return percent_color(fmt, hpp, he, percent_st_l1miss);
1107 percent_stores_l1miss_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
1108 struct hist_entry *left, struct hist_entry *right)
1113 per_left = PERCENT(left, st_l1miss);
1114 per_right = PERCENT(right, st_l1miss);
1116 return per_left - per_right;
1120 percent_stores_na_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1121 struct hist_entry *he)
1123 int width = c2c_width(fmt, hpp, he->hists);
1124 double per = PERCENT(he, st_na);
1127 return scnprintf(hpp->buf, hpp->size, "%*s", width, PERC_STR(buf, per));
1131 percent_stores_na_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1132 struct hist_entry *he)
1134 return percent_color(fmt, hpp, he, percent_st_na);
1138 percent_stores_na_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
1139 struct hist_entry *left, struct hist_entry *right)
1144 per_left = PERCENT(left, st_na);
1145 per_right = PERCENT(right, st_na);
1147 return per_left - per_right;
1154 pid_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1155 struct hist_entry *he)
1157 int width = c2c_width(fmt, hpp, he->hists);
1159 return scnprintf(hpp->buf, hpp->size, "%*d", width, thread__pid(he->thread));
1163 pid_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
1164 struct hist_entry *left, struct hist_entry *right)
1166 return thread__pid(left->thread) - thread__pid(right->thread);
1170 empty_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
1171 struct hist_entry *left __maybe_unused,
1172 struct hist_entry *right __maybe_unused)
1177 static int display_metrics(struct perf_hpp *hpp, u32 val, u32 sum)
1182 ret = scnprintf(hpp->buf, hpp->size, "%5.1f%% ",
1185 ret = scnprintf(hpp->buf, hpp->size, "%6s ", "n/a");
1191 node_entry(struct perf_hpp_fmt *fmt __maybe_unused, struct perf_hpp *hpp,
1192 struct hist_entry *he)
1194 struct c2c_hist_entry *c2c_he;
1199 c2c_he = container_of(he, struct c2c_hist_entry, he);
1201 for (node = 0; node < c2c.nodes_cnt; node++) {
1202 DECLARE_BITMAP(set, c2c.cpus_cnt);
1204 bitmap_zero(set, c2c.cpus_cnt);
1205 bitmap_and(set, c2c_he->cpuset, c2c.nodes[node], c2c.cpus_cnt);
1207 if (bitmap_empty(set, c2c.cpus_cnt)) {
1208 if (c2c.node_info == 1) {
1209 ret = scnprintf(hpp->buf, hpp->size, "%21s", " ");
1210 advance_hpp(hpp, ret);
1216 ret = scnprintf(hpp->buf, hpp->size, " ");
1217 advance_hpp(hpp, ret);
1220 switch (c2c.node_info) {
1222 ret = scnprintf(hpp->buf, hpp->size, "%2d", node);
1223 advance_hpp(hpp, ret);
1227 int num = bitmap_weight(set, c2c.cpus_cnt);
1228 struct c2c_stats *stats = &c2c_he->node_stats[node];
1230 ret = scnprintf(hpp->buf, hpp->size, "%2d{%2d ", node, num);
1231 advance_hpp(hpp, ret);
1233 switch (c2c.display) {
1234 case DISPLAY_RMT_HITM:
1235 ret = display_metrics(hpp, stats->rmt_hitm,
1236 c2c_he->stats.rmt_hitm);
1238 case DISPLAY_LCL_HITM:
1239 ret = display_metrics(hpp, stats->lcl_hitm,
1240 c2c_he->stats.lcl_hitm);
1242 case DISPLAY_TOT_HITM:
1243 ret = display_metrics(hpp, stats->tot_hitm,
1244 c2c_he->stats.tot_hitm);
1246 case DISPLAY_SNP_PEER:
1247 ret = display_metrics(hpp, stats->tot_peer,
1248 c2c_he->stats.tot_peer);
1254 advance_hpp(hpp, ret);
1256 if (c2c_he->stats.store > 0) {
1257 ret = scnprintf(hpp->buf, hpp->size, "%5.1f%%}",
1258 percent(stats->store, c2c_he->stats.store));
1260 ret = scnprintf(hpp->buf, hpp->size, "%6s}", "n/a");
1263 advance_hpp(hpp, ret);
1267 ret = scnprintf(hpp->buf, hpp->size, "%2d{", node);
1268 advance_hpp(hpp, ret);
1270 ret = bitmap_scnprintf(set, c2c.cpus_cnt, hpp->buf, hpp->size);
1271 advance_hpp(hpp, ret);
1273 ret = scnprintf(hpp->buf, hpp->size, "}");
1274 advance_hpp(hpp, ret);
1287 mean_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1288 struct hist_entry *he, double mean)
1290 int width = c2c_width(fmt, hpp, he->hists);
1293 scnprintf(buf, 10, "%6.0f", mean);
1294 return scnprintf(hpp->buf, hpp->size, "%*s", width, buf);
1297 #define MEAN_ENTRY(__func, __val) \
1299 __func(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, struct hist_entry *he) \
1301 struct c2c_hist_entry *c2c_he; \
1302 c2c_he = container_of(he, struct c2c_hist_entry, he); \
1303 return mean_entry(fmt, hpp, he, avg_stats(&c2c_he->cstats.__val)); \
1306 MEAN_ENTRY(mean_rmt_entry, rmt_hitm);
1307 MEAN_ENTRY(mean_lcl_entry, lcl_hitm);
1308 MEAN_ENTRY(mean_load_entry, load);
1309 MEAN_ENTRY(mean_rmt_peer_entry, rmt_peer);
1310 MEAN_ENTRY(mean_lcl_peer_entry, lcl_peer);
1313 cpucnt_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1314 struct hist_entry *he)
1316 struct c2c_hist_entry *c2c_he;
1317 int width = c2c_width(fmt, hpp, he->hists);
1320 c2c_he = container_of(he, struct c2c_hist_entry, he);
1322 scnprintf(buf, 10, "%d", bitmap_weight(c2c_he->cpuset, c2c.cpus_cnt));
1323 return scnprintf(hpp->buf, hpp->size, "%*s", width, buf);
1327 cl_idx_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1328 struct hist_entry *he)
1330 struct c2c_hist_entry *c2c_he;
1331 int width = c2c_width(fmt, hpp, he->hists);
1334 c2c_he = container_of(he, struct c2c_hist_entry, he);
1336 scnprintf(buf, 10, "%u", c2c_he->cacheline_idx);
1337 return scnprintf(hpp->buf, hpp->size, "%*s", width, buf);
1341 cl_idx_empty_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1342 struct hist_entry *he)
1344 int width = c2c_width(fmt, hpp, he->hists);
1346 return scnprintf(hpp->buf, hpp->size, "%*s", width, "");
1349 #define HEADER_LOW(__h) \
1356 #define HEADER_BOTH(__h0, __h1) \
1366 #define HEADER_SPAN(__h0, __h1, __s) \
1377 #define HEADER_SPAN_LOW(__h) \
1384 static struct c2c_dimension dim_dcacheline = {
1385 .header = HEADER_SPAN("--- Cacheline ----", "Address", 2),
1386 .name = "dcacheline",
1387 .cmp = dcacheline_cmp,
1388 .entry = dcacheline_entry,
1392 static struct c2c_dimension dim_dcacheline_node = {
1393 .header = HEADER_LOW("Node"),
1394 .name = "dcacheline_node",
1396 .entry = dcacheline_node_entry,
1400 static struct c2c_dimension dim_dcacheline_count = {
1401 .header = HEADER_LOW("PA cnt"),
1402 .name = "dcacheline_count",
1404 .entry = dcacheline_node_count,
1408 static struct c2c_header header_offset_tui = HEADER_SPAN("-----", "Off", 2);
1410 static struct c2c_dimension dim_offset = {
1411 .header = HEADER_SPAN("--- Data address -", "Offset", 2),
1414 .entry = offset_entry,
1418 static struct c2c_dimension dim_offset_node = {
1419 .header = HEADER_LOW("Node"),
1420 .name = "offset_node",
1422 .entry = dcacheline_node_entry,
1426 static struct c2c_dimension dim_iaddr = {
1427 .header = HEADER_LOW("Code address"),
1430 .entry = iaddr_entry,
1434 static struct c2c_dimension dim_tot_hitm = {
1435 .header = HEADER_SPAN("------- Load Hitm -------", "Total", 2),
1437 .cmp = tot_hitm_cmp,
1438 .entry = tot_hitm_entry,
1442 static struct c2c_dimension dim_lcl_hitm = {
1443 .header = HEADER_SPAN_LOW("LclHitm"),
1445 .cmp = lcl_hitm_cmp,
1446 .entry = lcl_hitm_entry,
1450 static struct c2c_dimension dim_rmt_hitm = {
1451 .header = HEADER_SPAN_LOW("RmtHitm"),
1453 .cmp = rmt_hitm_cmp,
1454 .entry = rmt_hitm_entry,
1458 static struct c2c_dimension dim_tot_peer = {
1459 .header = HEADER_SPAN("------- Load Peer -------", "Total", 2),
1461 .cmp = tot_peer_cmp,
1462 .entry = tot_peer_entry,
1466 static struct c2c_dimension dim_lcl_peer = {
1467 .header = HEADER_SPAN_LOW("Local"),
1469 .cmp = lcl_peer_cmp,
1470 .entry = lcl_peer_entry,
1474 static struct c2c_dimension dim_rmt_peer = {
1475 .header = HEADER_SPAN_LOW("Remote"),
1477 .cmp = rmt_peer_cmp,
1478 .entry = rmt_peer_entry,
1482 static struct c2c_dimension dim_cl_rmt_hitm = {
1483 .header = HEADER_SPAN("----- HITM -----", "Rmt", 1),
1484 .name = "cl_rmt_hitm",
1485 .cmp = rmt_hitm_cmp,
1486 .entry = rmt_hitm_entry,
1490 static struct c2c_dimension dim_cl_lcl_hitm = {
1491 .header = HEADER_SPAN_LOW("Lcl"),
1492 .name = "cl_lcl_hitm",
1493 .cmp = lcl_hitm_cmp,
1494 .entry = lcl_hitm_entry,
1498 static struct c2c_dimension dim_cl_rmt_peer = {
1499 .header = HEADER_SPAN("----- Peer -----", "Rmt", 1),
1500 .name = "cl_rmt_peer",
1501 .cmp = rmt_peer_cmp,
1502 .entry = rmt_peer_entry,
1506 static struct c2c_dimension dim_cl_lcl_peer = {
1507 .header = HEADER_SPAN_LOW("Lcl"),
1508 .name = "cl_lcl_peer",
1509 .cmp = lcl_peer_cmp,
1510 .entry = lcl_peer_entry,
1514 static struct c2c_dimension dim_tot_stores = {
1515 .header = HEADER_BOTH("Total", "Stores"),
1516 .name = "tot_stores",
1518 .entry = store_entry,
1522 static struct c2c_dimension dim_stores_l1hit = {
1523 .header = HEADER_SPAN("--------- Stores --------", "L1Hit", 2),
1524 .name = "stores_l1hit",
1525 .cmp = st_l1hit_cmp,
1526 .entry = st_l1hit_entry,
1530 static struct c2c_dimension dim_stores_l1miss = {
1531 .header = HEADER_SPAN_LOW("L1Miss"),
1532 .name = "stores_l1miss",
1533 .cmp = st_l1miss_cmp,
1534 .entry = st_l1miss_entry,
1538 static struct c2c_dimension dim_stores_na = {
1539 .header = HEADER_SPAN_LOW("N/A"),
1540 .name = "stores_na",
1542 .entry = st_na_entry,
1546 static struct c2c_dimension dim_cl_stores_l1hit = {
1547 .header = HEADER_SPAN("------- Store Refs ------", "L1 Hit", 2),
1548 .name = "cl_stores_l1hit",
1549 .cmp = st_l1hit_cmp,
1550 .entry = st_l1hit_entry,
1554 static struct c2c_dimension dim_cl_stores_l1miss = {
1555 .header = HEADER_SPAN_LOW("L1 Miss"),
1556 .name = "cl_stores_l1miss",
1557 .cmp = st_l1miss_cmp,
1558 .entry = st_l1miss_entry,
1562 static struct c2c_dimension dim_cl_stores_na = {
1563 .header = HEADER_SPAN_LOW("N/A"),
1564 .name = "cl_stores_na",
1566 .entry = st_na_entry,
1570 static struct c2c_dimension dim_ld_fbhit = {
1571 .header = HEADER_SPAN("----- Core Load Hit -----", "FB", 2),
1573 .cmp = ld_fbhit_cmp,
1574 .entry = ld_fbhit_entry,
1578 static struct c2c_dimension dim_ld_l1hit = {
1579 .header = HEADER_SPAN_LOW("L1"),
1581 .cmp = ld_l1hit_cmp,
1582 .entry = ld_l1hit_entry,
1586 static struct c2c_dimension dim_ld_l2hit = {
1587 .header = HEADER_SPAN_LOW("L2"),
1589 .cmp = ld_l2hit_cmp,
1590 .entry = ld_l2hit_entry,
1594 static struct c2c_dimension dim_ld_llchit = {
1595 .header = HEADER_SPAN("- LLC Load Hit --", "LclHit", 1),
1596 .name = "ld_lclhit",
1597 .cmp = ld_llchit_cmp,
1598 .entry = ld_llchit_entry,
1602 static struct c2c_dimension dim_ld_rmthit = {
1603 .header = HEADER_SPAN("- RMT Load Hit --", "RmtHit", 1),
1604 .name = "ld_rmthit",
1606 .entry = rmt_hit_entry,
1610 static struct c2c_dimension dim_tot_recs = {
1611 .header = HEADER_BOTH("Total", "records"),
1613 .cmp = tot_recs_cmp,
1614 .entry = tot_recs_entry,
1618 static struct c2c_dimension dim_tot_loads = {
1619 .header = HEADER_BOTH("Total", "Loads"),
1620 .name = "tot_loads",
1621 .cmp = tot_loads_cmp,
1622 .entry = tot_loads_entry,
1626 static struct c2c_header percent_costly_snoop_header[] = {
1627 [DISPLAY_LCL_HITM] = HEADER_BOTH("Lcl", "Hitm"),
1628 [DISPLAY_RMT_HITM] = HEADER_BOTH("Rmt", "Hitm"),
1629 [DISPLAY_TOT_HITM] = HEADER_BOTH("Tot", "Hitm"),
1630 [DISPLAY_SNP_PEER] = HEADER_BOTH("Peer", "Snoop"),
1633 static struct c2c_dimension dim_percent_costly_snoop = {
1634 .name = "percent_costly_snoop",
1635 .cmp = percent_costly_snoop_cmp,
1636 .entry = percent_costly_snoop_entry,
1637 .color = percent_costly_snoop_color,
1641 static struct c2c_dimension dim_percent_rmt_hitm = {
1642 .header = HEADER_SPAN("----- HITM -----", "RmtHitm", 1),
1643 .name = "percent_rmt_hitm",
1644 .cmp = percent_rmt_hitm_cmp,
1645 .entry = percent_rmt_hitm_entry,
1646 .color = percent_rmt_hitm_color,
1650 static struct c2c_dimension dim_percent_lcl_hitm = {
1651 .header = HEADER_SPAN_LOW("LclHitm"),
1652 .name = "percent_lcl_hitm",
1653 .cmp = percent_lcl_hitm_cmp,
1654 .entry = percent_lcl_hitm_entry,
1655 .color = percent_lcl_hitm_color,
1659 static struct c2c_dimension dim_percent_rmt_peer = {
1660 .header = HEADER_SPAN("-- Peer Snoop --", "Rmt", 1),
1661 .name = "percent_rmt_peer",
1662 .cmp = percent_rmt_peer_cmp,
1663 .entry = percent_rmt_peer_entry,
1664 .color = percent_rmt_peer_color,
1668 static struct c2c_dimension dim_percent_lcl_peer = {
1669 .header = HEADER_SPAN_LOW("Lcl"),
1670 .name = "percent_lcl_peer",
1671 .cmp = percent_lcl_peer_cmp,
1672 .entry = percent_lcl_peer_entry,
1673 .color = percent_lcl_peer_color,
1677 static struct c2c_dimension dim_percent_stores_l1hit = {
1678 .header = HEADER_SPAN("------- Store Refs ------", "L1 Hit", 2),
1679 .name = "percent_stores_l1hit",
1680 .cmp = percent_stores_l1hit_cmp,
1681 .entry = percent_stores_l1hit_entry,
1682 .color = percent_stores_l1hit_color,
1686 static struct c2c_dimension dim_percent_stores_l1miss = {
1687 .header = HEADER_SPAN_LOW("L1 Miss"),
1688 .name = "percent_stores_l1miss",
1689 .cmp = percent_stores_l1miss_cmp,
1690 .entry = percent_stores_l1miss_entry,
1691 .color = percent_stores_l1miss_color,
1695 static struct c2c_dimension dim_percent_stores_na = {
1696 .header = HEADER_SPAN_LOW("N/A"),
1697 .name = "percent_stores_na",
1698 .cmp = percent_stores_na_cmp,
1699 .entry = percent_stores_na_entry,
1700 .color = percent_stores_na_color,
1704 static struct c2c_dimension dim_dram_lcl = {
1705 .header = HEADER_SPAN("--- Load Dram ----", "Lcl", 1),
1707 .cmp = lcl_dram_cmp,
1708 .entry = lcl_dram_entry,
1712 static struct c2c_dimension dim_dram_rmt = {
1713 .header = HEADER_SPAN_LOW("Rmt"),
1715 .cmp = rmt_dram_cmp,
1716 .entry = rmt_dram_entry,
1720 static struct c2c_dimension dim_pid = {
1721 .header = HEADER_LOW("Pid"),
1728 static struct c2c_dimension dim_tid = {
1729 .header = HEADER_LOW("Tid"),
1734 static struct c2c_dimension dim_symbol = {
1739 static struct c2c_dimension dim_dso = {
1740 .header = HEADER_BOTH("Shared", "Object"),
1745 static struct c2c_dimension dim_node = {
1748 .entry = node_entry,
1752 static struct c2c_dimension dim_mean_rmt = {
1753 .header = HEADER_SPAN("---------- cycles ----------", "rmt hitm", 2),
1756 .entry = mean_rmt_entry,
1760 static struct c2c_dimension dim_mean_lcl = {
1761 .header = HEADER_SPAN_LOW("lcl hitm"),
1764 .entry = mean_lcl_entry,
1768 static struct c2c_dimension dim_mean_load = {
1769 .header = HEADER_SPAN_LOW("load"),
1770 .name = "mean_load",
1772 .entry = mean_load_entry,
1776 static struct c2c_dimension dim_mean_rmt_peer = {
1777 .header = HEADER_SPAN("---------- cycles ----------", "rmt peer", 2),
1778 .name = "mean_rmt_peer",
1780 .entry = mean_rmt_peer_entry,
1784 static struct c2c_dimension dim_mean_lcl_peer = {
1785 .header = HEADER_SPAN_LOW("lcl peer"),
1786 .name = "mean_lcl_peer",
1788 .entry = mean_lcl_peer_entry,
1792 static struct c2c_dimension dim_cpucnt = {
1793 .header = HEADER_BOTH("cpu", "cnt"),
1796 .entry = cpucnt_entry,
1800 static struct c2c_dimension dim_srcline = {
1801 .name = "cl_srcline",
1802 .se = &sort_srcline,
1805 static struct c2c_dimension dim_dcacheline_idx = {
1806 .header = HEADER_LOW("Index"),
1809 .entry = cl_idx_entry,
1813 static struct c2c_dimension dim_dcacheline_num = {
1814 .header = HEADER_LOW("Num"),
1817 .entry = cl_idx_entry,
1821 static struct c2c_dimension dim_dcacheline_num_empty = {
1822 .header = HEADER_LOW("Num"),
1823 .name = "cl_num_empty",
1825 .entry = cl_idx_empty_entry,
1829 static struct c2c_dimension *dimensions[] = {
1831 &dim_dcacheline_node,
1832 &dim_dcacheline_count,
1850 &dim_cl_stores_l1hit,
1851 &dim_cl_stores_l1miss,
1860 &dim_percent_costly_snoop,
1861 &dim_percent_rmt_hitm,
1862 &dim_percent_lcl_hitm,
1863 &dim_percent_rmt_peer,
1864 &dim_percent_lcl_peer,
1865 &dim_percent_stores_l1hit,
1866 &dim_percent_stores_l1miss,
1867 &dim_percent_stores_na,
1882 &dim_dcacheline_idx,
1883 &dim_dcacheline_num,
1884 &dim_dcacheline_num_empty,
1888 static void fmt_free(struct perf_hpp_fmt *fmt)
1890 struct c2c_fmt *c2c_fmt;
1892 c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
1896 static bool fmt_equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)
1898 struct c2c_fmt *c2c_a = container_of(a, struct c2c_fmt, fmt);
1899 struct c2c_fmt *c2c_b = container_of(b, struct c2c_fmt, fmt);
1901 return c2c_a->dim == c2c_b->dim;
1904 static struct c2c_dimension *get_dimension(const char *name)
1908 for (i = 0; dimensions[i]; i++) {
1909 struct c2c_dimension *dim = dimensions[i];
1911 if (!strcmp(dim->name, name))
1918 static int c2c_se_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1919 struct hist_entry *he)
1921 struct c2c_fmt *c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
1922 struct c2c_dimension *dim = c2c_fmt->dim;
1923 size_t len = fmt->user_len;
1926 len = hists__col_len(he->hists, dim->se->se_width_idx);
1928 if (dim == &dim_symbol || dim == &dim_srcline)
1929 len = symbol_width(he->hists, dim->se);
1932 return dim->se->se_snprintf(he, hpp->buf, hpp->size, len);
1935 static int64_t c2c_se_cmp(struct perf_hpp_fmt *fmt,
1936 struct hist_entry *a, struct hist_entry *b)
1938 struct c2c_fmt *c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
1939 struct c2c_dimension *dim = c2c_fmt->dim;
1941 return dim->se->se_cmp(a, b);
1944 static int64_t c2c_se_collapse(struct perf_hpp_fmt *fmt,
1945 struct hist_entry *a, struct hist_entry *b)
1947 struct c2c_fmt *c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
1948 struct c2c_dimension *dim = c2c_fmt->dim;
1949 int64_t (*collapse_fn)(struct hist_entry *, struct hist_entry *);
1951 collapse_fn = dim->se->se_collapse ?: dim->se->se_cmp;
1952 return collapse_fn(a, b);
1955 static struct c2c_fmt *get_format(const char *name)
1957 struct c2c_dimension *dim = get_dimension(name);
1958 struct c2c_fmt *c2c_fmt;
1959 struct perf_hpp_fmt *fmt;
1964 c2c_fmt = zalloc(sizeof(*c2c_fmt));
1970 fmt = &c2c_fmt->fmt;
1971 INIT_LIST_HEAD(&fmt->list);
1972 INIT_LIST_HEAD(&fmt->sort_list);
1974 fmt->cmp = dim->se ? c2c_se_cmp : dim->cmp;
1975 fmt->sort = dim->se ? c2c_se_cmp : dim->cmp;
1976 fmt->color = dim->se ? NULL : dim->color;
1977 fmt->entry = dim->se ? c2c_se_entry : dim->entry;
1978 fmt->header = c2c_header;
1979 fmt->width = c2c_width;
1980 fmt->collapse = dim->se ? c2c_se_collapse : dim->cmp;
1981 fmt->equal = fmt_equal;
1982 fmt->free = fmt_free;
1987 static int c2c_hists__init_output(struct perf_hpp_list *hpp_list, char *name)
1989 struct c2c_fmt *c2c_fmt = get_format(name);
1993 return output_field_add(hpp_list, name);
1996 perf_hpp_list__column_register(hpp_list, &c2c_fmt->fmt);
2000 static int c2c_hists__init_sort(struct perf_hpp_list *hpp_list, char *name)
2002 struct c2c_fmt *c2c_fmt = get_format(name);
2003 struct c2c_dimension *dim;
2007 return sort_dimension__add(hpp_list, name, NULL, 0);
2011 if (dim == &dim_dso)
2014 perf_hpp_list__register_sort_field(hpp_list, &c2c_fmt->fmt);
2018 #define PARSE_LIST(_list, _fn) \
2026 for (tok = strtok_r((char *)_list, ", ", &tmp); \
2027 tok; tok = strtok_r(NULL, ", ", &tmp)) { \
2028 ret = _fn(hpp_list, tok); \
2029 if (ret == -EINVAL) { \
2030 pr_err("Invalid --fields key: `%s'", tok); \
2032 } else if (ret == -ESRCH) { \
2033 pr_err("Unknown --fields key: `%s'", tok); \
2039 static int hpp_list__parse(struct perf_hpp_list *hpp_list,
2040 const char *output_,
2043 char *output = output_ ? strdup(output_) : NULL;
2044 char *sort = sort_ ? strdup(sort_) : NULL;
2047 PARSE_LIST(output, c2c_hists__init_output);
2048 PARSE_LIST(sort, c2c_hists__init_sort);
2050 /* copy sort keys to output fields */
2051 perf_hpp__setup_output_field(hpp_list);
2054 * We don't need other sorting keys other than those
2055 * we already specified. It also really slows down
2056 * the processing a lot with big number of output
2057 * fields, so switching this off for c2c.
2061 /* and then copy output fields to sort keys */
2062 perf_hpp__append_sort_keys(&hists->list);
2070 static int c2c_hists__init(struct c2c_hists *hists,
2072 int nr_header_lines)
2074 __hists__init(&hists->hists, &hists->list);
2077 * Initialize only with sort fields, we need to resort
2078 * later anyway, and that's where we add output fields
2081 perf_hpp_list__init(&hists->list);
2083 /* Overload number of header lines.*/
2084 hists->list.nr_header_lines = nr_header_lines;
2086 return hpp_list__parse(&hists->list, NULL, sort);
2089 static int c2c_hists__reinit(struct c2c_hists *c2c_hists,
2093 perf_hpp__reset_output_field(&c2c_hists->list);
2094 return hpp_list__parse(&c2c_hists->list, output, sort);
2097 #define DISPLAY_LINE_LIMIT 0.001
2099 static u8 filter_display(u32 val, u32 sum)
2101 if (sum == 0 || ((double)val / sum) < DISPLAY_LINE_LIMIT)
2102 return HIST_FILTER__C2C;
2107 static bool he__display(struct hist_entry *he, struct c2c_stats *stats)
2109 struct c2c_hist_entry *c2c_he;
2114 c2c_he = container_of(he, struct c2c_hist_entry, he);
2116 switch (c2c.display) {
2117 case DISPLAY_LCL_HITM:
2118 he->filtered = filter_display(c2c_he->stats.lcl_hitm,
2121 case DISPLAY_RMT_HITM:
2122 he->filtered = filter_display(c2c_he->stats.rmt_hitm,
2125 case DISPLAY_TOT_HITM:
2126 he->filtered = filter_display(c2c_he->stats.tot_hitm,
2129 case DISPLAY_SNP_PEER:
2130 he->filtered = filter_display(c2c_he->stats.tot_peer,
2137 return he->filtered == 0;
2140 static inline bool is_valid_hist_entry(struct hist_entry *he)
2142 struct c2c_hist_entry *c2c_he;
2143 bool has_record = false;
2145 c2c_he = container_of(he, struct c2c_hist_entry, he);
2147 /* It's a valid entry if contains stores */
2148 if (c2c_he->stats.store)
2151 switch (c2c.display) {
2152 case DISPLAY_LCL_HITM:
2153 has_record = !!c2c_he->stats.lcl_hitm;
2155 case DISPLAY_RMT_HITM:
2156 has_record = !!c2c_he->stats.rmt_hitm;
2158 case DISPLAY_TOT_HITM:
2159 has_record = !!c2c_he->stats.tot_hitm;
2161 case DISPLAY_SNP_PEER:
2162 has_record = !!c2c_he->stats.tot_peer;
2170 static void set_node_width(struct c2c_hist_entry *c2c_he, int len)
2172 struct c2c_dimension *dim;
2174 dim = &c2c.hists == c2c_he->hists ?
2175 &dim_dcacheline_node : &dim_offset_node;
2177 if (len > dim->width)
2181 static int set_nodestr(struct c2c_hist_entry *c2c_he)
2186 if (c2c_he->nodestr)
2189 if (!bitmap_empty(c2c_he->nodeset, c2c.nodes_cnt)) {
2190 len = bitmap_scnprintf(c2c_he->nodeset, c2c.nodes_cnt,
2193 len = scnprintf(buf, sizeof(buf), "N/A");
2196 set_node_width(c2c_he, len);
2197 c2c_he->nodestr = strdup(buf);
2198 return c2c_he->nodestr ? 0 : -ENOMEM;
2201 static void calc_width(struct c2c_hist_entry *c2c_he)
2203 struct c2c_hists *c2c_hists;
2205 c2c_hists = container_of(c2c_he->he.hists, struct c2c_hists, hists);
2206 hists__calc_col_len(&c2c_hists->hists, &c2c_he->he);
2207 set_nodestr(c2c_he);
2210 static int filter_cb(struct hist_entry *he, void *arg __maybe_unused)
2212 struct c2c_hist_entry *c2c_he;
2214 c2c_he = container_of(he, struct c2c_hist_entry, he);
2216 if (c2c.show_src && !he->srcline)
2217 he->srcline = hist_entry__srcline(he);
2221 if (!is_valid_hist_entry(he))
2222 he->filtered = HIST_FILTER__C2C;
2227 static int resort_cl_cb(struct hist_entry *he, void *arg __maybe_unused)
2229 struct c2c_hist_entry *c2c_he;
2230 struct c2c_hists *c2c_hists;
2231 bool display = he__display(he, &c2c.shared_clines_stats);
2233 c2c_he = container_of(he, struct c2c_hist_entry, he);
2234 c2c_hists = c2c_he->hists;
2236 if (display && c2c_hists) {
2237 static unsigned int idx;
2239 c2c_he->cacheline_idx = idx++;
2242 c2c_hists__reinit(c2c_hists, c2c.cl_output, c2c.cl_resort);
2244 hists__collapse_resort(&c2c_hists->hists, NULL);
2245 hists__output_resort_cb(&c2c_hists->hists, NULL, filter_cb);
2251 static struct c2c_header header_node_0 = HEADER_LOW("Node");
2252 static struct c2c_header header_node_1_hitms_stores =
2253 HEADER_LOW("Node{cpus %hitms %stores}");
2254 static struct c2c_header header_node_1_peers_stores =
2255 HEADER_LOW("Node{cpus %peers %stores}");
2256 static struct c2c_header header_node_2 = HEADER_LOW("Node{cpu list}");
2258 static void setup_nodes_header(void)
2260 switch (c2c.node_info) {
2262 dim_node.header = header_node_0;
2265 if (c2c.display == DISPLAY_SNP_PEER)
2266 dim_node.header = header_node_1_peers_stores;
2268 dim_node.header = header_node_1_hitms_stores;
2271 dim_node.header = header_node_2;
2280 static int setup_nodes(struct perf_session *session)
2282 struct numa_node *n;
2283 unsigned long **nodes;
2285 struct perf_cpu cpu;
2288 if (c2c.node_info > 2)
2291 c2c.nodes_cnt = session->header.env.nr_numa_nodes;
2292 c2c.cpus_cnt = session->header.env.nr_cpus_avail;
2294 n = session->header.env.numa_nodes;
2298 nodes = zalloc(sizeof(unsigned long *) * c2c.nodes_cnt);
2304 cpu2node = zalloc(sizeof(int) * c2c.cpus_cnt);
2308 for (idx = 0; idx < c2c.cpus_cnt; idx++)
2311 c2c.cpu2node = cpu2node;
2313 for (node = 0; node < c2c.nodes_cnt; node++) {
2314 struct perf_cpu_map *map = n[node].map;
2317 set = bitmap_zalloc(c2c.cpus_cnt);
2323 perf_cpu_map__for_each_cpu_skip_any(cpu, idx, map) {
2324 __set_bit(cpu.cpu, set);
2326 if (WARN_ONCE(cpu2node[cpu.cpu] != -1, "node/cpu topology bug"))
2329 cpu2node[cpu.cpu] = node;
2333 setup_nodes_header();
2337 #define HAS_HITMS(__h) ((__h)->stats.lcl_hitm || (__h)->stats.rmt_hitm)
2338 #define HAS_PEER(__h) ((__h)->stats.lcl_peer || (__h)->stats.rmt_peer)
2340 static int resort_shared_cl_cb(struct hist_entry *he, void *arg __maybe_unused)
2342 struct c2c_hist_entry *c2c_he;
2343 c2c_he = container_of(he, struct c2c_hist_entry, he);
2345 if (HAS_HITMS(c2c_he) || HAS_PEER(c2c_he)) {
2346 c2c.shared_clines++;
2347 c2c_add_stats(&c2c.shared_clines_stats, &c2c_he->stats);
2353 static int hists__iterate_cb(struct hists *hists, hists__resort_cb_t cb)
2355 struct rb_node *next = rb_first_cached(&hists->entries);
2359 struct hist_entry *he;
2361 he = rb_entry(next, struct hist_entry, rb_node);
2365 next = rb_next(&he->rb_node);
2371 static void print_c2c__display_stats(FILE *out)
2374 struct c2c_stats *stats = &c2c.hists.stats;
2376 llc_misses = get_load_llc_misses(stats);
2378 fprintf(out, "=================================================\n");
2379 fprintf(out, " Trace Event Information \n");
2380 fprintf(out, "=================================================\n");
2381 fprintf(out, " Total records : %10d\n", stats->nr_entries);
2382 fprintf(out, " Locked Load/Store Operations : %10d\n", stats->locks);
2383 fprintf(out, " Load Operations : %10d\n", stats->load);
2384 fprintf(out, " Loads - uncacheable : %10d\n", stats->ld_uncache);
2385 fprintf(out, " Loads - IO : %10d\n", stats->ld_io);
2386 fprintf(out, " Loads - Miss : %10d\n", stats->ld_miss);
2387 fprintf(out, " Loads - no mapping : %10d\n", stats->ld_noadrs);
2388 fprintf(out, " Load Fill Buffer Hit : %10d\n", stats->ld_fbhit);
2389 fprintf(out, " Load L1D hit : %10d\n", stats->ld_l1hit);
2390 fprintf(out, " Load L2D hit : %10d\n", stats->ld_l2hit);
2391 fprintf(out, " Load LLC hit : %10d\n", stats->ld_llchit + stats->lcl_hitm);
2392 fprintf(out, " Load Local HITM : %10d\n", stats->lcl_hitm);
2393 fprintf(out, " Load Remote HITM : %10d\n", stats->rmt_hitm);
2394 fprintf(out, " Load Remote HIT : %10d\n", stats->rmt_hit);
2395 fprintf(out, " Load Local DRAM : %10d\n", stats->lcl_dram);
2396 fprintf(out, " Load Remote DRAM : %10d\n", stats->rmt_dram);
2397 fprintf(out, " Load MESI State Exclusive : %10d\n", stats->ld_excl);
2398 fprintf(out, " Load MESI State Shared : %10d\n", stats->ld_shared);
2399 fprintf(out, " Load LLC Misses : %10d\n", llc_misses);
2400 fprintf(out, " Load access blocked by data : %10d\n", stats->blk_data);
2401 fprintf(out, " Load access blocked by address : %10d\n", stats->blk_addr);
2402 fprintf(out, " Load HIT Local Peer : %10d\n", stats->lcl_peer);
2403 fprintf(out, " Load HIT Remote Peer : %10d\n", stats->rmt_peer);
2404 fprintf(out, " LLC Misses to Local DRAM : %10.1f%%\n", ((double)stats->lcl_dram/(double)llc_misses) * 100.);
2405 fprintf(out, " LLC Misses to Remote DRAM : %10.1f%%\n", ((double)stats->rmt_dram/(double)llc_misses) * 100.);
2406 fprintf(out, " LLC Misses to Remote cache (HIT) : %10.1f%%\n", ((double)stats->rmt_hit /(double)llc_misses) * 100.);
2407 fprintf(out, " LLC Misses to Remote cache (HITM) : %10.1f%%\n", ((double)stats->rmt_hitm/(double)llc_misses) * 100.);
2408 fprintf(out, " Store Operations : %10d\n", stats->store);
2409 fprintf(out, " Store - uncacheable : %10d\n", stats->st_uncache);
2410 fprintf(out, " Store - no mapping : %10d\n", stats->st_noadrs);
2411 fprintf(out, " Store L1D Hit : %10d\n", stats->st_l1hit);
2412 fprintf(out, " Store L1D Miss : %10d\n", stats->st_l1miss);
2413 fprintf(out, " Store No available memory level : %10d\n", stats->st_na);
2414 fprintf(out, " No Page Map Rejects : %10d\n", stats->nomap);
2415 fprintf(out, " Unable to parse data source : %10d\n", stats->noparse);
2418 static void print_shared_cacheline_info(FILE *out)
2420 struct c2c_stats *stats = &c2c.shared_clines_stats;
2421 int hitm_cnt = stats->lcl_hitm + stats->rmt_hitm;
2423 fprintf(out, "=================================================\n");
2424 fprintf(out, " Global Shared Cache Line Event Information \n");
2425 fprintf(out, "=================================================\n");
2426 fprintf(out, " Total Shared Cache Lines : %10d\n", c2c.shared_clines);
2427 fprintf(out, " Load HITs on shared lines : %10d\n", stats->load);
2428 fprintf(out, " Fill Buffer Hits on shared lines : %10d\n", stats->ld_fbhit);
2429 fprintf(out, " L1D hits on shared lines : %10d\n", stats->ld_l1hit);
2430 fprintf(out, " L2D hits on shared lines : %10d\n", stats->ld_l2hit);
2431 fprintf(out, " LLC hits on shared lines : %10d\n", stats->ld_llchit + stats->lcl_hitm);
2432 fprintf(out, " Load hits on peer cache or nodes : %10d\n", stats->lcl_peer + stats->rmt_peer);
2433 fprintf(out, " Locked Access on shared lines : %10d\n", stats->locks);
2434 fprintf(out, " Blocked Access on shared lines : %10d\n", stats->blk_data + stats->blk_addr);
2435 fprintf(out, " Store HITs on shared lines : %10d\n", stats->store);
2436 fprintf(out, " Store L1D hits on shared lines : %10d\n", stats->st_l1hit);
2437 fprintf(out, " Store No available memory level : %10d\n", stats->st_na);
2438 fprintf(out, " Total Merged records : %10d\n", hitm_cnt + stats->store);
2441 static void print_cacheline(struct c2c_hists *c2c_hists,
2442 struct hist_entry *he_cl,
2443 struct perf_hpp_list *hpp_list,
2447 struct perf_hpp hpp = {
2454 hists__fprintf_headers(&c2c_hists->hists, out);
2460 fprintf(out, " ----------------------------------------------------------------------\n");
2461 __hist_entry__snprintf(he_cl, &hpp, hpp_list);
2462 fprintf(out, "%s\n", bf);
2463 fprintf(out, " ----------------------------------------------------------------------\n");
2465 hists__fprintf(&c2c_hists->hists, false, 0, 0, 0, out, false);
2468 static void print_pareto(FILE *out)
2470 struct perf_hpp_list hpp_list;
2473 const char *cl_output;
2475 if (c2c.display != DISPLAY_SNP_PEER)
2476 cl_output = "cl_num,"
2484 cl_output = "cl_num,"
2492 perf_hpp_list__init(&hpp_list);
2493 ret = hpp_list__parse(&hpp_list, cl_output, NULL);
2495 if (WARN_ONCE(ret, "failed to setup sort entries\n"))
2498 nd = rb_first_cached(&c2c.hists.hists.entries);
2500 for (; nd; nd = rb_next(nd)) {
2501 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
2502 struct c2c_hist_entry *c2c_he;
2507 c2c_he = container_of(he, struct c2c_hist_entry, he);
2508 print_cacheline(c2c_he->hists, he, &hpp_list, out);
2512 static void print_c2c_info(FILE *out, struct perf_session *session)
2514 struct evlist *evlist = session->evlist;
2515 struct evsel *evsel;
2518 fprintf(out, "=================================================\n");
2519 fprintf(out, " c2c details \n");
2520 fprintf(out, "=================================================\n");
2522 evlist__for_each_entry(evlist, evsel) {
2523 fprintf(out, "%-36s: %s\n", first ? " Events" : "", evsel__name(evsel));
2526 fprintf(out, " Cachelines sort on : %s\n",
2527 display_str[c2c.display]);
2528 fprintf(out, " Cacheline data grouping : %s\n", c2c.cl_sort);
2531 static void perf_c2c__hists_fprintf(FILE *out, struct perf_session *session)
2535 print_c2c__display_stats(out);
2537 print_shared_cacheline_info(out);
2539 print_c2c_info(out, session);
2545 fprintf(out, "=================================================\n");
2546 fprintf(out, " Shared Data Cache Line Table \n");
2547 fprintf(out, "=================================================\n");
2548 fprintf(out, "#\n");
2550 hists__fprintf(&c2c.hists.hists, true, 0, 0, 0, stdout, true);
2553 fprintf(out, "=================================================\n");
2554 fprintf(out, " Shared Cache Line Distribution Pareto \n");
2555 fprintf(out, "=================================================\n");
2556 fprintf(out, "#\n");
2561 #ifdef HAVE_SLANG_SUPPORT
2562 static void c2c_browser__update_nr_entries(struct hist_browser *hb)
2565 struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2568 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
2576 hb->nr_non_filtered_entries = nr_entries;
2579 struct c2c_cacheline_browser {
2580 struct hist_browser hb;
2581 struct hist_entry *he;
2585 perf_c2c_cacheline_browser__title(struct hist_browser *browser,
2586 char *bf, size_t size)
2588 struct c2c_cacheline_browser *cl_browser;
2589 struct hist_entry *he;
2592 cl_browser = container_of(browser, struct c2c_cacheline_browser, hb);
2593 he = cl_browser->he;
2596 addr = cl_address(mem_info__daddr(he->mem_info)->addr, chk_double_cl);
2598 scnprintf(bf, size, "Cacheline 0x%lx", addr);
2602 static struct c2c_cacheline_browser*
2603 c2c_cacheline_browser__new(struct hists *hists, struct hist_entry *he)
2605 struct c2c_cacheline_browser *browser;
2607 browser = zalloc(sizeof(*browser));
2609 hist_browser__init(&browser->hb, hists);
2610 browser->hb.c2c_filter = true;
2611 browser->hb.title = perf_c2c_cacheline_browser__title;
2618 static int perf_c2c__browse_cacheline(struct hist_entry *he)
2620 struct c2c_hist_entry *c2c_he;
2621 struct c2c_hists *c2c_hists;
2622 struct c2c_cacheline_browser *cl_browser;
2623 struct hist_browser *browser;
2625 static const char help[] =
2626 " ENTER Toggle callchains (if present) \n"
2627 " n Toggle Node details info \n"
2628 " s Toggle full length of symbol and source line columns \n"
2629 " q Return back to cacheline list \n";
2634 /* Display compact version first. */
2635 c2c.symbol_full = false;
2637 c2c_he = container_of(he, struct c2c_hist_entry, he);
2638 c2c_hists = c2c_he->hists;
2640 cl_browser = c2c_cacheline_browser__new(&c2c_hists->hists, he);
2641 if (cl_browser == NULL)
2644 browser = &cl_browser->hb;
2646 /* reset abort key so that it can get Ctrl-C as a key */
2648 SLang_init_tty(0, 0, 0);
2650 c2c_browser__update_nr_entries(browser);
2653 key = hist_browser__run(browser, "? - help", true, 0);
2657 c2c.symbol_full = !c2c.symbol_full;
2660 c2c.node_info = (c2c.node_info + 1) % 3;
2661 setup_nodes_header();
2666 ui_browser__help_window(&browser->b, help);
2678 static int perf_c2c_browser__title(struct hist_browser *browser,
2679 char *bf, size_t size)
2682 "Shared Data Cache Line Table "
2683 "(%lu entries, sorted on %s)",
2684 browser->nr_non_filtered_entries,
2685 display_str[c2c.display]);
2689 static struct hist_browser*
2690 perf_c2c_browser__new(struct hists *hists)
2692 struct hist_browser *browser = hist_browser__new(hists);
2695 browser->title = perf_c2c_browser__title;
2696 browser->c2c_filter = true;
2702 static int perf_c2c__hists_browse(struct hists *hists)
2704 struct hist_browser *browser;
2706 static const char help[] =
2707 " d Display cacheline details \n"
2708 " ENTER Toggle callchains (if present) \n"
2711 browser = perf_c2c_browser__new(hists);
2712 if (browser == NULL)
2715 /* reset abort key so that it can get Ctrl-C as a key */
2717 SLang_init_tty(0, 0, 0);
2719 c2c_browser__update_nr_entries(browser);
2722 key = hist_browser__run(browser, "? - help", true, 0);
2728 perf_c2c__browse_cacheline(browser->he_selection);
2731 ui_browser__help_window(&browser->b, help);
2739 hist_browser__delete(browser);
2743 static void perf_c2c_display(struct perf_session *session)
2745 if (use_browser == 0)
2746 perf_c2c__hists_fprintf(stdout, session);
2748 perf_c2c__hists_browse(&c2c.hists.hists);
2751 static void perf_c2c_display(struct perf_session *session)
2754 perf_c2c__hists_fprintf(stdout, session);
2756 #endif /* HAVE_SLANG_SUPPORT */
2758 static char *fill_line(const char *orig, int len)
2760 int i, j, olen = strlen(orig);
2763 buf = zalloc(len + 1);
2767 j = len / 2 - olen / 2;
2769 for (i = 0; i < j - 1; i++)
2774 strcpy(buf + i, orig);
2780 for (; i < len; i++)
2786 static int ui_quirks(void)
2788 const char *nodestr = "Data address";
2791 if (!c2c.use_stdio) {
2792 dim_offset.width = 5;
2793 dim_offset.header = header_offset_tui;
2794 nodestr = chk_double_cl ? "Double-CL" : "CL";
2797 dim_percent_costly_snoop.header = percent_costly_snoop_header[c2c.display];
2799 /* Fix the zero line for dcacheline column. */
2800 buf = fill_line(chk_double_cl ? "Double-Cacheline" : "Cacheline",
2801 dim_dcacheline.width +
2802 dim_dcacheline_node.width +
2803 dim_dcacheline_count.width + 4);
2807 dim_dcacheline.header.line[0].text = buf;
2809 /* Fix the zero line for offset column. */
2810 buf = fill_line(nodestr, dim_offset.width +
2811 dim_offset_node.width +
2812 dim_dcacheline_count.width + 4);
2816 dim_offset.header.line[0].text = buf;
2821 #define CALLCHAIN_DEFAULT_OPT "graph,0.5,caller,function,percent"
2823 const char callchain_help[] = "Display call graph (stack chain/backtrace):\n\n"
2824 CALLCHAIN_REPORT_HELP
2825 "\n\t\t\t\tDefault: " CALLCHAIN_DEFAULT_OPT;
2828 parse_callchain_opt(const struct option *opt, const char *arg, int unset)
2830 struct callchain_param *callchain = opt->value;
2832 callchain->enabled = !unset;
2837 symbol_conf.use_callchain = false;
2838 callchain->mode = CHAIN_NONE;
2842 return parse_callchain_report_opt(arg);
2845 static int setup_callchain(struct evlist *evlist)
2847 u64 sample_type = evlist__combined_sample_type(evlist);
2848 enum perf_call_graph_mode mode = CALLCHAIN_NONE;
2850 if ((sample_type & PERF_SAMPLE_REGS_USER) &&
2851 (sample_type & PERF_SAMPLE_STACK_USER)) {
2852 mode = CALLCHAIN_DWARF;
2853 dwarf_callchain_users = true;
2854 } else if (sample_type & PERF_SAMPLE_BRANCH_STACK)
2855 mode = CALLCHAIN_LBR;
2856 else if (sample_type & PERF_SAMPLE_CALLCHAIN)
2857 mode = CALLCHAIN_FP;
2859 if (!callchain_param.enabled &&
2860 callchain_param.mode != CHAIN_NONE &&
2861 mode != CALLCHAIN_NONE) {
2862 symbol_conf.use_callchain = true;
2863 if (callchain_register_param(&callchain_param) < 0) {
2864 ui__error("Can't register callchain params.\n");
2869 if (c2c.stitch_lbr && (mode != CALLCHAIN_LBR)) {
2870 ui__warning("Can't find LBR callchain. Switch off --stitch-lbr.\n"
2871 "Please apply --call-graph lbr when recording.\n");
2872 c2c.stitch_lbr = false;
2875 callchain_param.record_mode = mode;
2876 callchain_param.min_percent = 0;
2880 static int setup_display(const char *str)
2882 const char *display = str;
2884 if (!strcmp(display, "tot"))
2885 c2c.display = DISPLAY_TOT_HITM;
2886 else if (!strcmp(display, "rmt"))
2887 c2c.display = DISPLAY_RMT_HITM;
2888 else if (!strcmp(display, "lcl"))
2889 c2c.display = DISPLAY_LCL_HITM;
2890 else if (!strcmp(display, "peer"))
2891 c2c.display = DISPLAY_SNP_PEER;
2893 pr_err("failed: unknown display type: %s\n", str);
2900 #define for_each_token(__tok, __buf, __sep, __tmp) \
2901 for (__tok = strtok_r(__buf, __sep, &__tmp); __tok; \
2902 __tok = strtok_r(NULL, __sep, &__tmp))
2904 static int build_cl_output(char *cl_sort, bool no_source)
2906 char *tok, *tmp, *buf = strdup(cl_sort);
2907 bool add_pid = false;
2908 bool add_tid = false;
2909 bool add_iaddr = false;
2910 bool add_sym = false;
2911 bool add_dso = false;
2912 bool add_src = false;
2918 for_each_token(tok, buf, ",", tmp) {
2919 if (!strcmp(tok, "tid")) {
2921 } else if (!strcmp(tok, "pid")) {
2923 } else if (!strcmp(tok, "iaddr")) {
2927 add_src = no_source ? false : true;
2928 } else if (!strcmp(tok, "dso")) {
2930 } else if (strcmp(tok, "offset")) {
2931 pr_err("unrecognized sort token: %s\n", tok);
2937 if (asprintf(&c2c.cl_output,
2938 "%s%s%s%s%s%s%s%s%s%s%s%s",
2939 c2c.use_stdio ? "cl_num_empty," : "",
2940 c2c.display == DISPLAY_SNP_PEER ? "percent_rmt_peer,"
2941 "percent_lcl_peer," :
2943 "percent_lcl_hitm,",
2944 "percent_stores_l1hit,"
2945 "percent_stores_l1miss,"
2946 "percent_stores_na,"
2947 "offset,offset_node,dcacheline_count,",
2948 add_pid ? "pid," : "",
2949 add_tid ? "tid," : "",
2950 add_iaddr ? "iaddr," : "",
2951 c2c.display == DISPLAY_SNP_PEER ? "mean_rmt_peer,"
2958 add_sym ? "symbol," : "",
2959 add_dso ? "dso," : "",
2960 add_src ? "cl_srcline," : "",
2966 c2c.show_src = add_src;
2972 static int setup_coalesce(const char *coalesce, bool no_source)
2974 const char *c = coalesce ?: coalesce_default;
2975 const char *sort_str = NULL;
2977 if (asprintf(&c2c.cl_sort, "offset,%s", c) < 0)
2980 if (build_cl_output(c2c.cl_sort, no_source))
2983 if (c2c.display == DISPLAY_TOT_HITM)
2984 sort_str = "tot_hitm";
2985 else if (c2c.display == DISPLAY_RMT_HITM)
2986 sort_str = "rmt_hitm,lcl_hitm";
2987 else if (c2c.display == DISPLAY_LCL_HITM)
2988 sort_str = "lcl_hitm,rmt_hitm";
2989 else if (c2c.display == DISPLAY_SNP_PEER)
2990 sort_str = "tot_peer";
2992 if (asprintf(&c2c.cl_resort, "offset,%s", sort_str) < 0)
2995 pr_debug("coalesce sort fields: %s\n", c2c.cl_sort);
2996 pr_debug("coalesce resort fields: %s\n", c2c.cl_resort);
2997 pr_debug("coalesce output fields: %s\n", c2c.cl_output);
3001 static int perf_c2c__report(int argc, const char **argv)
3003 struct itrace_synth_opts itrace_synth_opts = {
3005 .mem = true, /* Only enable memory event */
3006 .default_no_sample = true,
3009 struct perf_session *session;
3010 struct ui_progress prog;
3011 struct perf_data data = {
3012 .mode = PERF_DATA_MODE_READ,
3014 char callchain_default_opt[] = CALLCHAIN_DEFAULT_OPT;
3015 const char *display = NULL;
3016 const char *coalesce = NULL;
3017 bool no_source = false;
3018 const struct option options[] = {
3019 OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
3020 "file", "vmlinux pathname"),
3021 OPT_STRING('i', "input", &input_name, "file",
3022 "the input file to process"),
3023 OPT_INCR('N', "node-info", &c2c.node_info,
3024 "show extra node info in report (repeat for more info)"),
3025 OPT_BOOLEAN(0, "stdio", &c2c.use_stdio, "Use the stdio interface"),
3026 OPT_BOOLEAN(0, "stats", &c2c.stats_only,
3027 "Display only statistic tables (implies --stdio)"),
3028 OPT_BOOLEAN(0, "full-symbols", &c2c.symbol_full,
3029 "Display full length of symbols"),
3030 OPT_BOOLEAN(0, "no-source", &no_source,
3031 "Do not display Source Line column"),
3032 OPT_BOOLEAN(0, "show-all", &c2c.show_all,
3033 "Show all captured HITM lines."),
3034 OPT_CALLBACK_DEFAULT('g', "call-graph", &callchain_param,
3035 "print_type,threshold[,print_limit],order,sort_key[,branch],value",
3036 callchain_help, &parse_callchain_opt,
3037 callchain_default_opt),
3038 OPT_STRING('d', "display", &display, "Switch HITM output type", "tot,lcl,rmt,peer"),
3039 OPT_STRING('c', "coalesce", &coalesce, "coalesce fields",
3040 "coalesce fields: pid,tid,iaddr,dso"),
3041 OPT_BOOLEAN('f', "force", &symbol_conf.force, "don't complain, do it"),
3042 OPT_BOOLEAN(0, "stitch-lbr", &c2c.stitch_lbr,
3043 "Enable LBR callgraph stitching approach"),
3044 OPT_BOOLEAN(0, "double-cl", &chk_double_cl, "Detect adjacent cacheline false sharing"),
3045 OPT_PARENT(c2c_options),
3049 const char *output_str, *sort_str = NULL;
3051 argc = parse_options(argc, argv, options, report_c2c_usage,
3052 PARSE_OPT_STOP_AT_NON_OPTION);
3054 usage_with_options(report_c2c_usage, options);
3056 #ifndef HAVE_SLANG_SUPPORT
3057 c2c.use_stdio = true;
3061 c2c.use_stdio = true;
3063 err = symbol__validate_sym_arguments();
3067 if (!input_name || !strlen(input_name))
3068 input_name = "perf.data";
3070 data.path = input_name;
3071 data.force = symbol_conf.force;
3073 session = perf_session__new(&data, &c2c.tool);
3074 if (IS_ERR(session)) {
3075 err = PTR_ERR(session);
3076 pr_debug("Error creating perf session\n");
3081 * Use the 'tot' as default display type if user doesn't specify it;
3082 * since Arm64 platform doesn't support HITMs flag, use 'peer' as the
3083 * default display type.
3086 if (!strcmp(perf_env__arch(&session->header.env), "arm64"))
3092 err = setup_display(display);
3096 err = setup_coalesce(coalesce, no_source);
3098 pr_debug("Failed to initialize hists\n");
3102 err = c2c_hists__init(&c2c.hists, "dcacheline", 2);
3104 pr_debug("Failed to initialize hists\n");
3108 session->itrace_synth_opts = &itrace_synth_opts;
3110 err = setup_nodes(session);
3112 pr_err("Failed setup nodes\n");
3116 err = mem2node__init(&c2c.mem2node, &session->header.env);
3120 err = setup_callchain(session->evlist);
3124 if (symbol__init(&session->header.env) < 0)
3127 /* No pipe support at the moment. */
3128 if (perf_data__is_pipe(session->data)) {
3129 pr_debug("No pipe support at the moment.\n");
3138 setup_browser(false);
3140 err = perf_session__process_events(session);
3142 pr_err("failed to process sample\n");
3146 if (c2c.display != DISPLAY_SNP_PEER)
3147 output_str = "cl_idx,"
3151 "percent_costly_snoop,"
3152 "tot_hitm,lcl_hitm,rmt_hitm,"
3156 "stores_l1hit,stores_l1miss,stores_na,"
3157 "ld_fbhit,ld_l1hit,ld_l2hit,"
3158 "ld_lclhit,lcl_hitm,"
3159 "ld_rmthit,rmt_hitm,"
3160 "dram_lcl,dram_rmt";
3162 output_str = "cl_idx,"
3166 "percent_costly_snoop,"
3167 "tot_peer,lcl_peer,rmt_peer,"
3171 "stores_l1hit,stores_l1miss,stores_na,"
3172 "ld_fbhit,ld_l1hit,ld_l2hit,"
3173 "ld_lclhit,lcl_hitm,"
3174 "ld_rmthit,rmt_hitm,"
3175 "dram_lcl,dram_rmt";
3177 if (c2c.display == DISPLAY_TOT_HITM)
3178 sort_str = "tot_hitm";
3179 else if (c2c.display == DISPLAY_RMT_HITM)
3180 sort_str = "rmt_hitm";
3181 else if (c2c.display == DISPLAY_LCL_HITM)
3182 sort_str = "lcl_hitm";
3183 else if (c2c.display == DISPLAY_SNP_PEER)
3184 sort_str = "tot_peer";
3186 c2c_hists__reinit(&c2c.hists, output_str, sort_str);
3188 ui_progress__init(&prog, c2c.hists.hists.nr_entries, "Sorting...");
3190 hists__collapse_resort(&c2c.hists.hists, NULL);
3191 hists__output_resort_cb(&c2c.hists.hists, &prog, resort_shared_cl_cb);
3192 hists__iterate_cb(&c2c.hists.hists, resort_cl_cb);
3194 ui_progress__finish();
3197 pr_err("failed to setup UI\n");
3201 perf_c2c_display(session);
3204 mem2node__exit(&c2c.mem2node);
3206 perf_session__delete(session);
3211 static int parse_record_events(const struct option *opt,
3212 const char *str, int unset __maybe_unused)
3214 bool *event_set = (bool *) opt->value;
3215 struct perf_pmu *pmu;
3217 pmu = perf_mem_events_find_pmu();
3219 pr_err("failed: there is no PMU that supports perf c2c\n");
3223 if (!strcmp(str, "list")) {
3224 perf_pmu__mem_events_list(pmu);
3227 if (perf_pmu__mem_events_parse(pmu, str))
3235 static const char * const __usage_record[] = {
3236 "perf c2c record [<options>] [<command>]",
3237 "perf c2c record [<options>] -- <command> [<options>]",
3241 static const char * const *record_mem_usage = __usage_record;
3243 static int perf_c2c__record(int argc, const char **argv)
3245 int rec_argc, i = 0, j;
3246 const char **rec_argv;
3248 bool all_user = false, all_kernel = false;
3249 bool event_set = false;
3250 struct perf_mem_event *e;
3251 struct perf_pmu *pmu;
3252 struct option options[] = {
3253 OPT_CALLBACK('e', "event", &event_set, "event",
3254 "event selector. Use 'perf c2c record -e list' to list available events",
3255 parse_record_events),
3256 OPT_BOOLEAN('u', "all-user", &all_user, "collect only user level data"),
3257 OPT_BOOLEAN('k', "all-kernel", &all_kernel, "collect only kernel level data"),
3258 OPT_UINTEGER('l', "ldlat", &perf_mem_events__loads_ldlat, "setup mem-loads latency"),
3259 OPT_PARENT(c2c_options),
3263 pmu = perf_mem_events_find_pmu();
3265 pr_err("failed: no PMU supports the memory events\n");
3269 if (perf_pmu__mem_events_init(pmu)) {
3270 pr_err("failed: memory events not supported\n");
3274 argc = parse_options(argc, argv, options, record_mem_usage,
3275 PARSE_OPT_KEEP_UNKNOWN);
3277 /* Max number of arguments multiplied by number of PMUs that can support them. */
3278 rec_argc = argc + 11 * (perf_pmu__mem_events_num_mem_pmus(pmu) + 1);
3280 rec_argv = calloc(rec_argc + 1, sizeof(char *));
3284 rec_argv[i++] = "record";
3287 e = perf_pmu__mem_events_ptr(pmu, PERF_MEM_EVENTS__LOAD_STORE);
3289 * The load and store operations are required, use the event
3290 * PERF_MEM_EVENTS__LOAD_STORE if it is supported.
3294 rec_argv[i++] = "-W";
3296 e = perf_pmu__mem_events_ptr(pmu, PERF_MEM_EVENTS__LOAD);
3299 e = perf_pmu__mem_events_ptr(pmu, PERF_MEM_EVENTS__STORE);
3304 e = perf_pmu__mem_events_ptr(pmu, PERF_MEM_EVENTS__LOAD);
3306 rec_argv[i++] = "-W";
3308 rec_argv[i++] = "-d";
3309 rec_argv[i++] = "--phys-data";
3310 rec_argv[i++] = "--sample-cpu";
3312 ret = perf_mem_events__record_args(rec_argv, &i);
3317 rec_argv[i++] = "--all-user";
3320 rec_argv[i++] = "--all-kernel";
3322 for (j = 0; j < argc; j++, i++)
3323 rec_argv[i] = argv[j];
3326 pr_debug("calling: ");
3330 while (rec_argv[j]) {
3331 pr_debug("%s ", rec_argv[j]);
3337 ret = cmd_record(i, rec_argv);
3343 int cmd_c2c(int argc, const char **argv)
3345 argc = parse_options(argc, argv, c2c_options, c2c_usage,
3346 PARSE_OPT_STOP_AT_NON_OPTION);
3349 usage_with_options(c2c_usage, c2c_options);
3351 if (strlen(argv[0]) > 2 && strstarts("record", argv[0])) {
3352 return perf_c2c__record(argc, argv);
3353 } else if (strlen(argv[0]) > 2 && strstarts("report", argv[0])) {
3354 return perf_c2c__report(argc, argv);
3356 usage_with_options(c2c_usage, c2c_options);