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/
5 *
6 * The original tool was changed to fit in current perf state.
7 *
8 * Original authors:
9 *   Don Zickus <dzickus@redhat.com>
10 *   Dick Fowles <fowles@inreach.com>
11 *   Joe Mario <jmario@redhat.com>
12 */
13#include <errno.h>
14#include <inttypes.h>
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>
20#include <asm/bug.h>
21#include <sys/param.h>
22#include "debug.h"
23#include "builtin.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"
29#include "session.h"
30#include "hist.h"
31#include "sort.h"
32#include "tool.h"
33#include "cacheline.h"
34#include "data.h"
35#include "event.h"
36#include "evlist.h"
37#include "evsel.h"
38#include "ui/browsers/hists.h"
39#include "thread.h"
40#include "mem2node.h"
41#include "mem-info.h"
42#include "symbol.h"
43#include "ui/ui.h"
44#include "ui/progress.h"
45#include "pmus.h"
46#include "string2.h"
47#include "util/util.h"
48
49struct c2c_hists {
50	struct hists		hists;
51	struct perf_hpp_list	list;
52	struct c2c_stats	stats;
53};
54
55struct compute_stats {
56	struct stats		 lcl_hitm;
57	struct stats		 rmt_hitm;
58	struct stats		 lcl_peer;
59	struct stats		 rmt_peer;
60	struct stats		 load;
61};
62
63struct 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;
70
71	struct compute_stats	 cstats;
72
73	unsigned long		 paddr;
74	unsigned long		 paddr_cnt;
75	bool			 paddr_zero;
76	char			*nodestr;
77
78	/*
79	 * must be at the end,
80	 * because of its callchain dynamic entry
81	 */
82	struct hist_entry	he;
83};
84
85static char const *coalesce_default = "iaddr";
86
87struct perf_c2c {
88	struct perf_tool	tool;
89	struct c2c_hists	hists;
90	struct mem2node		mem2node;
91
92	unsigned long		**nodes;
93	int			 nodes_cnt;
94	int			 cpus_cnt;
95	int			*cpu2node;
96	int			 node_info;
97
98	bool			 show_src;
99	bool			 show_all;
100	bool			 use_stdio;
101	bool			 stats_only;
102	bool			 symbol_full;
103	bool			 stitch_lbr;
104
105	/* Shared cache line stats */
106	struct c2c_stats	shared_clines_stats;
107	int			shared_clines;
108
109	int			 display;
110
111	const char		*coalesce;
112	char			*cl_sort;
113	char			*cl_resort;
114	char			*cl_output;
115};
116
117enum {
118	DISPLAY_LCL_HITM,
119	DISPLAY_RMT_HITM,
120	DISPLAY_TOT_HITM,
121	DISPLAY_SNP_PEER,
122	DISPLAY_MAX,
123};
124
125static 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",
130};
131
132static const struct option c2c_options[] = {
133	OPT_INCR('v', "verbose", &verbose, "be more verbose (show counter open errors, etc)"),
134	OPT_END()
135};
136
137static struct perf_c2c c2c;
138
139static void *c2c_he_zalloc(size_t size)
140{
141	struct c2c_hist_entry *c2c_he;
142
143	c2c_he = zalloc(size + sizeof(*c2c_he));
144	if (!c2c_he)
145		return NULL;
146
147	c2c_he->cpuset = bitmap_zalloc(c2c.cpus_cnt);
148	if (!c2c_he->cpuset)
149		goto out_free;
150
151	c2c_he->nodeset = bitmap_zalloc(c2c.nodes_cnt);
152	if (!c2c_he->nodeset)
153		goto out_free;
154
155	c2c_he->node_stats = zalloc(c2c.nodes_cnt * sizeof(*c2c_he->node_stats));
156	if (!c2c_he->node_stats)
157		goto out_free;
158
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);
164
165	return &c2c_he->he;
166
167out_free:
168	zfree(&c2c_he->nodeset);
169	zfree(&c2c_he->cpuset);
170	free(c2c_he);
171	return NULL;
172}
173
174static void c2c_he_free(void *he)
175{
176	struct c2c_hist_entry *c2c_he;
177
178	c2c_he = container_of(he, struct c2c_hist_entry, he);
179	if (c2c_he->hists) {
180		hists__delete_entries(&c2c_he->hists->hists);
181		zfree(&c2c_he->hists);
182	}
183
184	zfree(&c2c_he->cpuset);
185	zfree(&c2c_he->nodeset);
186	zfree(&c2c_he->nodestr);
187	zfree(&c2c_he->node_stats);
188	free(c2c_he);
189}
190
191static struct hist_entry_ops c2c_entry_ops = {
192	.new	= c2c_he_zalloc,
193	.free	= c2c_he_free,
194};
195
196static int c2c_hists__init(struct c2c_hists *hists,
197			   const char *sort,
198			   int nr_header_lines);
199
200static struct c2c_hists*
201he__get_c2c_hists(struct hist_entry *he,
202		  const char *sort,
203		  int nr_header_lines)
204{
205	struct c2c_hist_entry *c2c_he;
206	struct c2c_hists *hists;
207	int ret;
208
209	c2c_he = container_of(he, struct c2c_hist_entry, he);
210	if (c2c_he->hists)
211		return c2c_he->hists;
212
213	hists = c2c_he->hists = zalloc(sizeof(*hists));
214	if (!hists)
215		return NULL;
216
217	ret = c2c_hists__init(hists, sort, nr_header_lines);
218	if (ret) {
219		free(hists);
220		return NULL;
221	}
222
223	return hists;
224}
225
226static void c2c_he__set_cpu(struct c2c_hist_entry *c2c_he,
227			    struct perf_sample *sample)
228{
229	if (WARN_ONCE(sample->cpu == (unsigned int) -1,
230		      "WARNING: no sample cpu value"))
231		return;
232
233	__set_bit(sample->cpu, c2c_he->cpuset);
234}
235
236static void c2c_he__set_node(struct c2c_hist_entry *c2c_he,
237			     struct perf_sample *sample)
238{
239	int node;
240
241	if (!sample->phys_addr) {
242		c2c_he->paddr_zero = true;
243		return;
244	}
245
246	node = mem2node__node(&c2c.mem2node, sample->phys_addr);
247	if (WARN_ONCE(node < 0, "WARNING: failed to find node\n"))
248		return;
249
250	__set_bit(node, c2c_he->nodeset);
251
252	if (c2c_he->paddr != sample->phys_addr) {
253		c2c_he->paddr_cnt++;
254		c2c_he->paddr = sample->phys_addr;
255	}
256}
257
258static void compute_stats(struct c2c_hist_entry *c2c_he,
259			  struct c2c_stats *stats,
260			  u64 weight)
261{
262	struct compute_stats *cstats = &c2c_he->cstats;
263
264	if (stats->rmt_hitm)
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);
274}
275
276static int process_sample_event(struct perf_tool *tool __maybe_unused,
277				union perf_event *event,
278				struct perf_sample *sample,
279				struct evsel *evsel,
280				struct machine *machine)
281{
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;
289	int ret;
290
291	addr_location__init(&al);
292	if (machine__resolve(machine, &al, sample) < 0) {
293		pr_debug("problem processing %d event, skipping it.\n",
294			 event->header.type);
295		ret = -1;
296		goto out;
297	}
298
299	if (c2c.stitch_lbr)
300		thread__set_lbr_stitch_enable(al.thread, true);
301
302	cursor = get_tls_callchain_cursor();
303	ret = sample__resolve_callchain(sample, cursor, NULL,
304					evsel, &al, sysctl_perf_event_max_stack);
305	if (ret)
306		goto out;
307
308	mi = sample__resolve_mem(sample, &al);
309	if (mi == NULL) {
310		ret = -ENOMEM;
311		goto out;
312	}
313
314	/*
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.
318	 */
319	mi_dup = mem_info__get(mi);
320
321	c2c_decode_stats(&stats, mi);
322
323	he = hists__add_entry_ops(&c2c_hists->hists, &c2c_entry_ops,
324				  &al, NULL, NULL, mi, NULL,
325				  sample, true);
326	if (he == NULL)
327		goto free_mi;
328
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);
332
333	c2c_he__set_cpu(c2c_he, sample);
334	c2c_he__set_node(c2c_he, sample);
335
336	hists__inc_nr_samples(&c2c_hists->hists, he->filtered);
337	ret = hist_entry__append_callchain(he, sample);
338
339	if (!ret) {
340		/*
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
344		 * warning.
345		 *
346		 * Doing node stats only for single callchain data.
347		 */
348		int cpu = sample->cpu == (unsigned int) -1 ? 0 : sample->cpu;
349		int node = c2c.cpu2node[cpu];
350
351		mi = mi_dup;
352
353		c2c_hists = he__get_c2c_hists(he, c2c.cl_sort, 2);
354		if (!c2c_hists)
355			goto free_mi;
356
357		he = hists__add_entry_ops(&c2c_hists->hists, &c2c_entry_ops,
358					  &al, NULL, NULL, mi, NULL,
359					  sample, true);
360		if (he == NULL)
361			goto free_mi;
362
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);
367
368		compute_stats(c2c_he, &stats, sample->weight);
369
370		c2c_he__set_cpu(c2c_he, sample);
371		c2c_he__set_node(c2c_he, sample);
372
373		hists__inc_nr_samples(&c2c_hists->hists, he->filtered);
374		ret = hist_entry__append_callchain(he, sample);
375	}
376
377out:
378	addr_location__exit(&al);
379	return ret;
380
381free_mi:
382	mem_info__put(mi_dup);
383	mem_info__put(mi);
384	ret = -ENOMEM;
385	goto out;
386}
387
388static struct perf_c2c c2c = {
389	.tool = {
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,
403	},
404};
405
406static const char * const c2c_usage[] = {
407	"perf c2c {record|report}",
408	NULL
409};
410
411static const char * const __usage_report[] = {
412	"perf c2c report",
413	NULL
414};
415
416static const char * const *report_c2c_usage = __usage_report;
417
418#define C2C_HEADER_MAX 2
419
420struct c2c_header {
421	struct {
422		const char *text;
423		int	    span;
424	} line[C2C_HEADER_MAX];
425};
426
427struct c2c_dimension {
428	struct c2c_header	 header;
429	const char		*name;
430	int			 width;
431	struct sort_entry	*se;
432
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);
439};
440
441struct c2c_fmt {
442	struct perf_hpp_fmt	 fmt;
443	struct c2c_dimension	*dim;
444};
445
446#define SYMBOL_WIDTH 30
447
448static struct c2c_dimension dim_symbol;
449static struct c2c_dimension dim_srcline;
450
451static int symbol_width(struct hists *hists, struct sort_entry *se)
452{
453	int width = hists__col_len(hists, se->se_width_idx);
454
455	if (!c2c.symbol_full)
456		width = MIN(width, SYMBOL_WIDTH);
457
458	return width;
459}
460
461static int c2c_width(struct perf_hpp_fmt *fmt,
462		     struct perf_hpp *hpp __maybe_unused,
463		     struct hists *hists)
464{
465	struct c2c_fmt *c2c_fmt;
466	struct c2c_dimension *dim;
467
468	c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
469	dim = c2c_fmt->dim;
470
471	if (dim == &dim_symbol || dim == &dim_srcline)
472		return symbol_width(hists, dim->se);
473
474	return dim->se ? hists__col_len(hists, dim->se->se_width_idx) :
475			 c2c_fmt->dim->width;
476}
477
478static int c2c_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
479		      struct hists *hists, int line, int *span)
480{
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);
486
487	c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
488	dim = c2c_fmt->dim;
489
490	if (dim->se) {
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;
495	} else {
496		text = dim->header.line[line].text;
497
498		if (*span) {
499			(*span)--;
500			return 0;
501		} else {
502			*span = dim->header.line[line].span;
503		}
504	}
505
506	if (text == NULL)
507		text = "";
508
509	return scnprintf(hpp->buf, hpp->size, "%*s", width, text);
510}
511
512#define HEX_STR(__s, __v)				\
513({							\
514	scnprintf(__s, sizeof(__s), "0x%" PRIx64, __v);	\
515	__s;						\
516})
517
518static int64_t
519dcacheline_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
520	       struct hist_entry *left, struct hist_entry *right)
521{
522	return sort__dcacheline_cmp(left, right);
523}
524
525static int dcacheline_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
526			    struct hist_entry *he)
527{
528	uint64_t addr = 0;
529	int width = c2c_width(fmt, hpp, he->hists);
530	char buf[20];
531
532	if (he->mem_info)
533		addr = cl_address(mem_info__daddr(he->mem_info)->addr, chk_double_cl);
534
535	return scnprintf(hpp->buf, hpp->size, "%*s", width, HEX_STR(buf, addr));
536}
537
538static int
539dcacheline_node_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
540		      struct hist_entry *he)
541{
542	struct c2c_hist_entry *c2c_he;
543	int width = c2c_width(fmt, hpp, he->hists);
544
545	c2c_he = container_of(he, struct c2c_hist_entry, he);
546	if (WARN_ON_ONCE(!c2c_he->nodestr))
547		return 0;
548
549	return scnprintf(hpp->buf, hpp->size, "%*s", width, c2c_he->nodestr);
550}
551
552static int
553dcacheline_node_count(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
554		      struct hist_entry *he)
555{
556	struct c2c_hist_entry *c2c_he;
557	int width = c2c_width(fmt, hpp, he->hists);
558
559	c2c_he = container_of(he, struct c2c_hist_entry, he);
560	return scnprintf(hpp->buf, hpp->size, "%*lu", width, c2c_he->paddr_cnt);
561}
562
563static int offset_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
564			struct hist_entry *he)
565{
566	uint64_t addr = 0;
567	int width = c2c_width(fmt, hpp, he->hists);
568	char buf[20];
569
570	if (he->mem_info)
571		addr = cl_offset(mem_info__daddr(he->mem_info)->al_addr, chk_double_cl);
572
573	return scnprintf(hpp->buf, hpp->size, "%*s", width, HEX_STR(buf, addr));
574}
575
576static int64_t
577offset_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
578	   struct hist_entry *left, struct hist_entry *right)
579{
580	uint64_t l = 0, r = 0;
581
582	if (left->mem_info)
583		l = cl_offset(mem_info__daddr(left->mem_info)->addr, chk_double_cl);
584
585	if (right->mem_info)
586		r = cl_offset(mem_info__daddr(right->mem_info)->addr, chk_double_cl);
587
588	return (int64_t)(r - l);
589}
590
591static int
592iaddr_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
593	    struct hist_entry *he)
594{
595	uint64_t addr = 0;
596	int width = c2c_width(fmt, hpp, he->hists);
597	char buf[20];
598
599	if (he->mem_info)
600		addr = mem_info__iaddr(he->mem_info)->addr;
601
602	return scnprintf(hpp->buf, hpp->size, "%*s", width, HEX_STR(buf, addr));
603}
604
605static int64_t
606iaddr_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
607	  struct hist_entry *left, struct hist_entry *right)
608{
609	return sort__iaddr_cmp(left, right);
610}
611
612static int
613tot_hitm_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
614	       struct hist_entry *he)
615{
616	struct c2c_hist_entry *c2c_he;
617	int width = c2c_width(fmt, hpp, he->hists);
618	unsigned int tot_hitm;
619
620	c2c_he = container_of(he, struct c2c_hist_entry, he);
621	tot_hitm = c2c_he->stats.lcl_hitm + c2c_he->stats.rmt_hitm;
622
623	return scnprintf(hpp->buf, hpp->size, "%*u", width, tot_hitm);
624}
625
626static int64_t
627tot_hitm_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
628	     struct hist_entry *left, struct hist_entry *right)
629{
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;
634
635	c2c_left  = container_of(left, struct c2c_hist_entry, he);
636	c2c_right = container_of(right, struct c2c_hist_entry, he);
637
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;
640
641	return tot_hitm_left - tot_hitm_right;
642}
643
644#define STAT_FN_ENTRY(__f)					\
645static int							\
646__f ## _entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,	\
647	      struct hist_entry *he)				\
648{								\
649	struct c2c_hist_entry *c2c_he;				\
650	int width = c2c_width(fmt, hpp, he->hists);		\
651								\
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);			\
655}
656
657#define STAT_FN_CMP(__f)						\
658static int64_t								\
659__f ## _cmp(struct perf_hpp_fmt *fmt __maybe_unused,			\
660	    struct hist_entry *left, struct hist_entry *right)		\
661{									\
662	struct c2c_hist_entry *c2c_left, *c2c_right;			\
663									\
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;				\
668}
669
670#define STAT_FN(__f)		\
671	STAT_FN_ENTRY(__f)	\
672	STAT_FN_CMP(__f)
673
674STAT_FN(rmt_hitm)
675STAT_FN(lcl_hitm)
676STAT_FN(rmt_peer)
677STAT_FN(lcl_peer)
678STAT_FN(tot_peer)
679STAT_FN(store)
680STAT_FN(st_l1hit)
681STAT_FN(st_l1miss)
682STAT_FN(st_na)
683STAT_FN(ld_fbhit)
684STAT_FN(ld_l1hit)
685STAT_FN(ld_l2hit)
686STAT_FN(ld_llchit)
687STAT_FN(rmt_hit)
688
689static uint64_t get_load_llc_misses(struct c2c_stats *stats)
690{
691	return stats->lcl_dram +
692	       stats->rmt_dram +
693	       stats->rmt_hitm +
694	       stats->rmt_hit;
695}
696
697static uint64_t get_load_cache_hits(struct c2c_stats *stats)
698{
699	return stats->ld_fbhit +
700	       stats->ld_l1hit +
701	       stats->ld_l2hit +
702	       stats->ld_llchit +
703	       stats->lcl_hitm;
704}
705
706static uint64_t get_stores(struct c2c_stats *stats)
707{
708	return stats->st_l1hit +
709	       stats->st_l1miss +
710	       stats->st_na;
711}
712
713static uint64_t total_records(struct c2c_stats *stats)
714{
715	return get_load_llc_misses(stats) +
716	       get_load_cache_hits(stats) +
717	       get_stores(stats);
718}
719
720static int
721tot_recs_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
722		struct hist_entry *he)
723{
724	struct c2c_hist_entry *c2c_he;
725	int width = c2c_width(fmt, hpp, he->hists);
726	uint64_t tot_recs;
727
728	c2c_he = container_of(he, struct c2c_hist_entry, he);
729	tot_recs = total_records(&c2c_he->stats);
730
731	return scnprintf(hpp->buf, hpp->size, "%*" PRIu64, width, tot_recs);
732}
733
734static int64_t
735tot_recs_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
736	     struct hist_entry *left, struct hist_entry *right)
737{
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;
742
743	c2c_left  = container_of(left, struct c2c_hist_entry, he);
744	c2c_right = container_of(right, struct c2c_hist_entry, he);
745
746	tot_recs_left  = total_records(&c2c_left->stats);
747	tot_recs_right = total_records(&c2c_right->stats);
748
749	return tot_recs_left - tot_recs_right;
750}
751
752static uint64_t total_loads(struct c2c_stats *stats)
753{
754	return get_load_llc_misses(stats) +
755	       get_load_cache_hits(stats);
756}
757
758static int
759tot_loads_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
760		struct hist_entry *he)
761{
762	struct c2c_hist_entry *c2c_he;
763	int width = c2c_width(fmt, hpp, he->hists);
764	uint64_t tot_recs;
765
766	c2c_he = container_of(he, struct c2c_hist_entry, he);
767	tot_recs = total_loads(&c2c_he->stats);
768
769	return scnprintf(hpp->buf, hpp->size, "%*" PRIu64, width, tot_recs);
770}
771
772static int64_t
773tot_loads_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
774	      struct hist_entry *left, struct hist_entry *right)
775{
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;
780
781	c2c_left  = container_of(left, struct c2c_hist_entry, he);
782	c2c_right = container_of(right, struct c2c_hist_entry, he);
783
784	tot_recs_left  = total_loads(&c2c_left->stats);
785	tot_recs_right = total_loads(&c2c_right->stats);
786
787	return tot_recs_left - tot_recs_right;
788}
789
790typedef double (get_percent_cb)(struct c2c_hist_entry *);
791
792static int
793percent_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
794	      struct hist_entry *he, get_percent_cb get_percent)
795{
796	struct c2c_hist_entry *c2c_he;
797	int width = c2c_width(fmt, hpp, he->hists);
798	double per;
799
800	c2c_he = container_of(he, struct c2c_hist_entry, he);
801	per = get_percent(c2c_he);
802
803#ifdef HAVE_SLANG_SUPPORT
804	if (use_browser)
805		return __hpp__slsmg_color_printf(hpp, "%*.2f%%", width - 1, per);
806#endif
807	return hpp_color_scnprintf(hpp, "%*.2f%%", width - 1, per);
808}
809
810static double percent_costly_snoop(struct c2c_hist_entry *c2c_he)
811{
812	struct c2c_hists *hists;
813	struct c2c_stats *stats;
814	struct c2c_stats *total;
815	int tot = 0, st = 0;
816	double p;
817
818	hists = container_of(c2c_he->he.hists, struct c2c_hists, hists);
819	stats = &c2c_he->stats;
820	total = &hists->stats;
821
822	switch (c2c.display) {
823	case DISPLAY_RMT_HITM:
824		st  = stats->rmt_hitm;
825		tot = total->rmt_hitm;
826		break;
827	case DISPLAY_LCL_HITM:
828		st  = stats->lcl_hitm;
829		tot = total->lcl_hitm;
830		break;
831	case DISPLAY_TOT_HITM:
832		st  = stats->tot_hitm;
833		tot = total->tot_hitm;
834		break;
835	case DISPLAY_SNP_PEER:
836		st  = stats->tot_peer;
837		tot = total->tot_peer;
838		break;
839	default:
840		break;
841	}
842
843	p = tot ? (double) st / tot : 0;
844
845	return 100 * p;
846}
847
848#define PERC_STR(__s, __v)				\
849({							\
850	scnprintf(__s, sizeof(__s), "%.2F%%", __v);	\
851	__s;						\
852})
853
854static int
855percent_costly_snoop_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
856			   struct hist_entry *he)
857{
858	struct c2c_hist_entry *c2c_he;
859	int width = c2c_width(fmt, hpp, he->hists);
860	char buf[10];
861	double per;
862
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));
866}
867
868static int
869percent_costly_snoop_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
870			   struct hist_entry *he)
871{
872	return percent_color(fmt, hpp, he, percent_costly_snoop);
873}
874
875static int64_t
876percent_costly_snoop_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
877			 struct hist_entry *left, struct hist_entry *right)
878{
879	struct c2c_hist_entry *c2c_left;
880	struct c2c_hist_entry *c2c_right;
881	double per_left;
882	double per_right;
883
884	c2c_left  = container_of(left, struct c2c_hist_entry, he);
885	c2c_right = container_of(right, struct c2c_hist_entry, he);
886
887	per_left  = percent_costly_snoop(c2c_left);
888	per_right = percent_costly_snoop(c2c_right);
889
890	return per_left - per_right;
891}
892
893static struct c2c_stats *he_stats(struct hist_entry *he)
894{
895	struct c2c_hist_entry *c2c_he;
896
897	c2c_he = container_of(he, struct c2c_hist_entry, he);
898	return &c2c_he->stats;
899}
900
901static struct c2c_stats *total_stats(struct hist_entry *he)
902{
903	struct c2c_hists *hists;
904
905	hists = container_of(he->hists, struct c2c_hists, hists);
906	return &hists->stats;
907}
908
909static double percent(u32 st, u32 tot)
910{
911	return tot ? 100. * (double) st / (double) tot : 0;
912}
913
914#define PERCENT(__h, __f) percent(he_stats(__h)->__f, total_stats(__h)->__f)
915
916#define PERCENT_FN(__f)								\
917static double percent_ ## __f(struct c2c_hist_entry *c2c_he)			\
918{										\
919	struct c2c_hists *hists;						\
920										\
921	hists = container_of(c2c_he->he.hists, struct c2c_hists, hists);	\
922	return percent(c2c_he->stats.__f, hists->stats.__f);			\
923}
924
925PERCENT_FN(rmt_hitm)
926PERCENT_FN(lcl_hitm)
927PERCENT_FN(rmt_peer)
928PERCENT_FN(lcl_peer)
929PERCENT_FN(st_l1hit)
930PERCENT_FN(st_l1miss)
931PERCENT_FN(st_na)
932
933static int
934percent_rmt_hitm_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
935		       struct hist_entry *he)
936{
937	int width = c2c_width(fmt, hpp, he->hists);
938	double per = PERCENT(he, rmt_hitm);
939	char buf[10];
940
941	return scnprintf(hpp->buf, hpp->size, "%*s", width, PERC_STR(buf, per));
942}
943
944static int
945percent_rmt_hitm_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
946		       struct hist_entry *he)
947{
948	return percent_color(fmt, hpp, he, percent_rmt_hitm);
949}
950
951static int64_t
952percent_rmt_hitm_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
953		     struct hist_entry *left, struct hist_entry *right)
954{
955	double per_left;
956	double per_right;
957
958	per_left  = PERCENT(left, rmt_hitm);
959	per_right = PERCENT(right, rmt_hitm);
960
961	return per_left - per_right;
962}
963
964static int
965percent_lcl_hitm_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
966		       struct hist_entry *he)
967{
968	int width = c2c_width(fmt, hpp, he->hists);
969	double per = PERCENT(he, lcl_hitm);
970	char buf[10];
971
972	return scnprintf(hpp->buf, hpp->size, "%*s", width, PERC_STR(buf, per));
973}
974
975static int
976percent_lcl_hitm_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
977		       struct hist_entry *he)
978{
979	return percent_color(fmt, hpp, he, percent_lcl_hitm);
980}
981
982static int64_t
983percent_lcl_hitm_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
984		     struct hist_entry *left, struct hist_entry *right)
985{
986	double per_left;
987	double per_right;
988
989	per_left  = PERCENT(left, lcl_hitm);
990	per_right = PERCENT(right, lcl_hitm);
991
992	return per_left - per_right;
993}
994
995static int
996percent_lcl_peer_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
997		       struct hist_entry *he)
998{
999	int width = c2c_width(fmt, hpp, he->hists);
1000	double per = PERCENT(he, lcl_peer);
1001	char buf[10];
1002
1003	return scnprintf(hpp->buf, hpp->size, "%*s", width, PERC_STR(buf, per));
1004}
1005
1006static int
1007percent_lcl_peer_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1008		       struct hist_entry *he)
1009{
1010	return percent_color(fmt, hpp, he, percent_lcl_peer);
1011}
1012
1013static int64_t
1014percent_lcl_peer_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
1015		     struct hist_entry *left, struct hist_entry *right)
1016{
1017	double per_left;
1018	double per_right;
1019
1020	per_left  = PERCENT(left, lcl_peer);
1021	per_right = PERCENT(right, lcl_peer);
1022
1023	return per_left - per_right;
1024}
1025
1026static int
1027percent_rmt_peer_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1028		       struct hist_entry *he)
1029{
1030	int width = c2c_width(fmt, hpp, he->hists);
1031	double per = PERCENT(he, rmt_peer);
1032	char buf[10];
1033
1034	return scnprintf(hpp->buf, hpp->size, "%*s", width, PERC_STR(buf, per));
1035}
1036
1037static int
1038percent_rmt_peer_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1039		       struct hist_entry *he)
1040{
1041	return percent_color(fmt, hpp, he, percent_rmt_peer);
1042}
1043
1044static int64_t
1045percent_rmt_peer_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
1046		     struct hist_entry *left, struct hist_entry *right)
1047{
1048	double per_left;
1049	double per_right;
1050
1051	per_left  = PERCENT(left, rmt_peer);
1052	per_right = PERCENT(right, rmt_peer);
1053
1054	return per_left - per_right;
1055}
1056
1057static int
1058percent_stores_l1hit_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1059			   struct hist_entry *he)
1060{
1061	int width = c2c_width(fmt, hpp, he->hists);
1062	double per = PERCENT(he, st_l1hit);
1063	char buf[10];
1064
1065	return scnprintf(hpp->buf, hpp->size, "%*s", width, PERC_STR(buf, per));
1066}
1067
1068static int
1069percent_stores_l1hit_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1070			   struct hist_entry *he)
1071{
1072	return percent_color(fmt, hpp, he, percent_st_l1hit);
1073}
1074
1075static int64_t
1076percent_stores_l1hit_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
1077			struct hist_entry *left, struct hist_entry *right)
1078{
1079	double per_left;
1080	double per_right;
1081
1082	per_left  = PERCENT(left, st_l1hit);
1083	per_right = PERCENT(right, st_l1hit);
1084
1085	return per_left - per_right;
1086}
1087
1088static int
1089percent_stores_l1miss_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1090			   struct hist_entry *he)
1091{
1092	int width = c2c_width(fmt, hpp, he->hists);
1093	double per = PERCENT(he, st_l1miss);
1094	char buf[10];
1095
1096	return scnprintf(hpp->buf, hpp->size, "%*s", width, PERC_STR(buf, per));
1097}
1098
1099static int
1100percent_stores_l1miss_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1101			    struct hist_entry *he)
1102{
1103	return percent_color(fmt, hpp, he, percent_st_l1miss);
1104}
1105
1106static int64_t
1107percent_stores_l1miss_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
1108			  struct hist_entry *left, struct hist_entry *right)
1109{
1110	double per_left;
1111	double per_right;
1112
1113	per_left  = PERCENT(left, st_l1miss);
1114	per_right = PERCENT(right, st_l1miss);
1115
1116	return per_left - per_right;
1117}
1118
1119static int
1120percent_stores_na_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1121			struct hist_entry *he)
1122{
1123	int width = c2c_width(fmt, hpp, he->hists);
1124	double per = PERCENT(he, st_na);
1125	char buf[10];
1126
1127	return scnprintf(hpp->buf, hpp->size, "%*s", width, PERC_STR(buf, per));
1128}
1129
1130static int
1131percent_stores_na_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1132			struct hist_entry *he)
1133{
1134	return percent_color(fmt, hpp, he, percent_st_na);
1135}
1136
1137static int64_t
1138percent_stores_na_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
1139		      struct hist_entry *left, struct hist_entry *right)
1140{
1141	double per_left;
1142	double per_right;
1143
1144	per_left  = PERCENT(left, st_na);
1145	per_right = PERCENT(right, st_na);
1146
1147	return per_left - per_right;
1148}
1149
1150STAT_FN(lcl_dram)
1151STAT_FN(rmt_dram)
1152
1153static int
1154pid_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1155	  struct hist_entry *he)
1156{
1157	int width = c2c_width(fmt, hpp, he->hists);
1158
1159	return scnprintf(hpp->buf, hpp->size, "%*d", width, thread__pid(he->thread));
1160}
1161
1162static int64_t
1163pid_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
1164	struct hist_entry *left, struct hist_entry *right)
1165{
1166	return thread__pid(left->thread) - thread__pid(right->thread);
1167}
1168
1169static int64_t
1170empty_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
1171	  struct hist_entry *left __maybe_unused,
1172	  struct hist_entry *right __maybe_unused)
1173{
1174	return 0;
1175}
1176
1177static int display_metrics(struct perf_hpp *hpp, u32 val, u32 sum)
1178{
1179	int ret;
1180
1181	if (sum != 0)
1182		ret = scnprintf(hpp->buf, hpp->size, "%5.1f%% ",
1183				percent(val, sum));
1184	else
1185		ret = scnprintf(hpp->buf, hpp->size, "%6s ", "n/a");
1186
1187	return ret;
1188}
1189
1190static int
1191node_entry(struct perf_hpp_fmt *fmt __maybe_unused, struct perf_hpp *hpp,
1192	   struct hist_entry *he)
1193{
1194	struct c2c_hist_entry *c2c_he;
1195	bool first = true;
1196	int node;
1197	int ret = 0;
1198
1199	c2c_he = container_of(he, struct c2c_hist_entry, he);
1200
1201	for (node = 0; node < c2c.nodes_cnt; node++) {
1202		DECLARE_BITMAP(set, c2c.cpus_cnt);
1203
1204		bitmap_zero(set, c2c.cpus_cnt);
1205		bitmap_and(set, c2c_he->cpuset, c2c.nodes[node], c2c.cpus_cnt);
1206
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);
1211			}
1212			continue;
1213		}
1214
1215		if (!first) {
1216			ret = scnprintf(hpp->buf, hpp->size, " ");
1217			advance_hpp(hpp, ret);
1218		}
1219
1220		switch (c2c.node_info) {
1221		case 0:
1222			ret = scnprintf(hpp->buf, hpp->size, "%2d", node);
1223			advance_hpp(hpp, ret);
1224			break;
1225		case 1:
1226		{
1227			int num = bitmap_weight(set, c2c.cpus_cnt);
1228			struct c2c_stats *stats = &c2c_he->node_stats[node];
1229
1230			ret = scnprintf(hpp->buf, hpp->size, "%2d{%2d ", node, num);
1231			advance_hpp(hpp, ret);
1232
1233			switch (c2c.display) {
1234			case DISPLAY_RMT_HITM:
1235				ret = display_metrics(hpp, stats->rmt_hitm,
1236						      c2c_he->stats.rmt_hitm);
1237				break;
1238			case DISPLAY_LCL_HITM:
1239				ret = display_metrics(hpp, stats->lcl_hitm,
1240						      c2c_he->stats.lcl_hitm);
1241				break;
1242			case DISPLAY_TOT_HITM:
1243				ret = display_metrics(hpp, stats->tot_hitm,
1244						      c2c_he->stats.tot_hitm);
1245				break;
1246			case DISPLAY_SNP_PEER:
1247				ret = display_metrics(hpp, stats->tot_peer,
1248						      c2c_he->stats.tot_peer);
1249				break;
1250			default:
1251				break;
1252			}
1253
1254			advance_hpp(hpp, ret);
1255
1256			if (c2c_he->stats.store > 0) {
1257				ret = scnprintf(hpp->buf, hpp->size, "%5.1f%%}",
1258						percent(stats->store, c2c_he->stats.store));
1259			} else {
1260				ret = scnprintf(hpp->buf, hpp->size, "%6s}", "n/a");
1261			}
1262
1263			advance_hpp(hpp, ret);
1264			break;
1265		}
1266		case 2:
1267			ret = scnprintf(hpp->buf, hpp->size, "%2d{", node);
1268			advance_hpp(hpp, ret);
1269
1270			ret = bitmap_scnprintf(set, c2c.cpus_cnt, hpp->buf, hpp->size);
1271			advance_hpp(hpp, ret);
1272
1273			ret = scnprintf(hpp->buf, hpp->size, "}");
1274			advance_hpp(hpp, ret);
1275			break;
1276		default:
1277			break;
1278		}
1279
1280		first = false;
1281	}
1282
1283	return 0;
1284}
1285
1286static int
1287mean_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1288	   struct hist_entry *he, double mean)
1289{
1290	int width = c2c_width(fmt, hpp, he->hists);
1291	char buf[10];
1292
1293	scnprintf(buf, 10, "%6.0f", mean);
1294	return scnprintf(hpp->buf, hpp->size, "%*s", width, buf);
1295}
1296
1297#define MEAN_ENTRY(__func, __val)						\
1298static int									\
1299__func(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, struct hist_entry *he)	\
1300{										\
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));	\
1304}
1305
1306MEAN_ENTRY(mean_rmt_entry,  rmt_hitm);
1307MEAN_ENTRY(mean_lcl_entry,  lcl_hitm);
1308MEAN_ENTRY(mean_load_entry, load);
1309MEAN_ENTRY(mean_rmt_peer_entry, rmt_peer);
1310MEAN_ENTRY(mean_lcl_peer_entry, lcl_peer);
1311
1312static int
1313cpucnt_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1314	     struct hist_entry *he)
1315{
1316	struct c2c_hist_entry *c2c_he;
1317	int width = c2c_width(fmt, hpp, he->hists);
1318	char buf[10];
1319
1320	c2c_he = container_of(he, struct c2c_hist_entry, he);
1321
1322	scnprintf(buf, 10, "%d", bitmap_weight(c2c_he->cpuset, c2c.cpus_cnt));
1323	return scnprintf(hpp->buf, hpp->size, "%*s", width, buf);
1324}
1325
1326static int
1327cl_idx_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1328	     struct hist_entry *he)
1329{
1330	struct c2c_hist_entry *c2c_he;
1331	int width = c2c_width(fmt, hpp, he->hists);
1332	char buf[10];
1333
1334	c2c_he = container_of(he, struct c2c_hist_entry, he);
1335
1336	scnprintf(buf, 10, "%u", c2c_he->cacheline_idx);
1337	return scnprintf(hpp->buf, hpp->size, "%*s", width, buf);
1338}
1339
1340static int
1341cl_idx_empty_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1342		   struct hist_entry *he)
1343{
1344	int width = c2c_width(fmt, hpp, he->hists);
1345
1346	return scnprintf(hpp->buf, hpp->size, "%*s", width, "");
1347}
1348
1349#define HEADER_LOW(__h)			\
1350	{				\
1351		.line[1] = {		\
1352			.text = __h,	\
1353		},			\
1354	}
1355
1356#define HEADER_BOTH(__h0, __h1)		\
1357	{				\
1358		.line[0] = {		\
1359			.text = __h0,	\
1360		},			\
1361		.line[1] = {		\
1362			.text = __h1,	\
1363		},			\
1364	}
1365
1366#define HEADER_SPAN(__h0, __h1, __s)	\
1367	{				\
1368		.line[0] = {		\
1369			.text = __h0,	\
1370			.span = __s,	\
1371		},			\
1372		.line[1] = {		\
1373			.text = __h1,	\
1374		},			\
1375	}
1376
1377#define HEADER_SPAN_LOW(__h)		\
1378	{				\
1379		.line[1] = {		\
1380			.text = __h,	\
1381		},			\
1382	}
1383
1384static struct c2c_dimension dim_dcacheline = {
1385	.header		= HEADER_SPAN("--- Cacheline ----", "Address", 2),
1386	.name		= "dcacheline",
1387	.cmp		= dcacheline_cmp,
1388	.entry		= dcacheline_entry,
1389	.width		= 18,
1390};
1391
1392static struct c2c_dimension dim_dcacheline_node = {
1393	.header		= HEADER_LOW("Node"),
1394	.name		= "dcacheline_node",
1395	.cmp		= empty_cmp,
1396	.entry		= dcacheline_node_entry,
1397	.width		= 4,
1398};
1399
1400static struct c2c_dimension dim_dcacheline_count = {
1401	.header		= HEADER_LOW("PA cnt"),
1402	.name		= "dcacheline_count",
1403	.cmp		= empty_cmp,
1404	.entry		= dcacheline_node_count,
1405	.width		= 6,
1406};
1407
1408static struct c2c_header header_offset_tui = HEADER_SPAN("-----", "Off", 2);
1409
1410static struct c2c_dimension dim_offset = {
1411	.header		= HEADER_SPAN("--- Data address -", "Offset", 2),
1412	.name		= "offset",
1413	.cmp		= offset_cmp,
1414	.entry		= offset_entry,
1415	.width		= 18,
1416};
1417
1418static struct c2c_dimension dim_offset_node = {
1419	.header		= HEADER_LOW("Node"),
1420	.name		= "offset_node",
1421	.cmp		= empty_cmp,
1422	.entry		= dcacheline_node_entry,
1423	.width		= 4,
1424};
1425
1426static struct c2c_dimension dim_iaddr = {
1427	.header		= HEADER_LOW("Code address"),
1428	.name		= "iaddr",
1429	.cmp		= iaddr_cmp,
1430	.entry		= iaddr_entry,
1431	.width		= 18,
1432};
1433
1434static struct c2c_dimension dim_tot_hitm = {
1435	.header		= HEADER_SPAN("------- Load Hitm -------", "Total", 2),
1436	.name		= "tot_hitm",
1437	.cmp		= tot_hitm_cmp,
1438	.entry		= tot_hitm_entry,
1439	.width		= 7,
1440};
1441
1442static struct c2c_dimension dim_lcl_hitm = {
1443	.header		= HEADER_SPAN_LOW("LclHitm"),
1444	.name		= "lcl_hitm",
1445	.cmp		= lcl_hitm_cmp,
1446	.entry		= lcl_hitm_entry,
1447	.width		= 7,
1448};
1449
1450static struct c2c_dimension dim_rmt_hitm = {
1451	.header		= HEADER_SPAN_LOW("RmtHitm"),
1452	.name		= "rmt_hitm",
1453	.cmp		= rmt_hitm_cmp,
1454	.entry		= rmt_hitm_entry,
1455	.width		= 7,
1456};
1457
1458static struct c2c_dimension dim_tot_peer = {
1459	.header		= HEADER_SPAN("------- Load Peer -------", "Total", 2),
1460	.name		= "tot_peer",
1461	.cmp		= tot_peer_cmp,
1462	.entry		= tot_peer_entry,
1463	.width		= 7,
1464};
1465
1466static struct c2c_dimension dim_lcl_peer = {
1467	.header		= HEADER_SPAN_LOW("Local"),
1468	.name		= "lcl_peer",
1469	.cmp		= lcl_peer_cmp,
1470	.entry		= lcl_peer_entry,
1471	.width		= 7,
1472};
1473
1474static struct c2c_dimension dim_rmt_peer = {
1475	.header		= HEADER_SPAN_LOW("Remote"),
1476	.name		= "rmt_peer",
1477	.cmp		= rmt_peer_cmp,
1478	.entry		= rmt_peer_entry,
1479	.width		= 7,
1480};
1481
1482static 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,
1487	.width		= 7,
1488};
1489
1490static 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,
1495	.width		= 7,
1496};
1497
1498static 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,
1503	.width		= 7,
1504};
1505
1506static 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,
1511	.width		= 7,
1512};
1513
1514static struct c2c_dimension dim_tot_stores = {
1515	.header		= HEADER_BOTH("Total", "Stores"),
1516	.name		= "tot_stores",
1517	.cmp		= store_cmp,
1518	.entry		= store_entry,
1519	.width		= 7,
1520};
1521
1522static 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,
1527	.width		= 7,
1528};
1529
1530static 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,
1535	.width		= 7,
1536};
1537
1538static struct c2c_dimension dim_stores_na = {
1539	.header		= HEADER_SPAN_LOW("N/A"),
1540	.name		= "stores_na",
1541	.cmp		= st_na_cmp,
1542	.entry		= st_na_entry,
1543	.width		= 7,
1544};
1545
1546static 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,
1551	.width		= 7,
1552};
1553
1554static 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,
1559	.width		= 7,
1560};
1561
1562static struct c2c_dimension dim_cl_stores_na = {
1563	.header		= HEADER_SPAN_LOW("N/A"),
1564	.name		= "cl_stores_na",
1565	.cmp		= st_na_cmp,
1566	.entry		= st_na_entry,
1567	.width		= 7,
1568};
1569
1570static struct c2c_dimension dim_ld_fbhit = {
1571	.header		= HEADER_SPAN("----- Core Load Hit -----", "FB", 2),
1572	.name		= "ld_fbhit",
1573	.cmp		= ld_fbhit_cmp,
1574	.entry		= ld_fbhit_entry,
1575	.width		= 7,
1576};
1577
1578static struct c2c_dimension dim_ld_l1hit = {
1579	.header		= HEADER_SPAN_LOW("L1"),
1580	.name		= "ld_l1hit",
1581	.cmp		= ld_l1hit_cmp,
1582	.entry		= ld_l1hit_entry,
1583	.width		= 7,
1584};
1585
1586static struct c2c_dimension dim_ld_l2hit = {
1587	.header		= HEADER_SPAN_LOW("L2"),
1588	.name		= "ld_l2hit",
1589	.cmp		= ld_l2hit_cmp,
1590	.entry		= ld_l2hit_entry,
1591	.width		= 7,
1592};
1593
1594static 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,
1599	.width		= 8,
1600};
1601
1602static struct c2c_dimension dim_ld_rmthit = {
1603	.header		= HEADER_SPAN("- RMT Load Hit --", "RmtHit", 1),
1604	.name		= "ld_rmthit",
1605	.cmp		= rmt_hit_cmp,
1606	.entry		= rmt_hit_entry,
1607	.width		= 8,
1608};
1609
1610static struct c2c_dimension dim_tot_recs = {
1611	.header		= HEADER_BOTH("Total", "records"),
1612	.name		= "tot_recs",
1613	.cmp		= tot_recs_cmp,
1614	.entry		= tot_recs_entry,
1615	.width		= 7,
1616};
1617
1618static 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,
1623	.width		= 7,
1624};
1625
1626static 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"),
1631};
1632
1633static 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,
1638	.width		= 7,
1639};
1640
1641static 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,
1647	.width		= 7,
1648};
1649
1650static 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,
1656	.width		= 7,
1657};
1658
1659static 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,
1665	.width		= 7,
1666};
1667
1668static 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,
1674	.width		= 7,
1675};
1676
1677static 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,
1683	.width		= 7,
1684};
1685
1686static 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,
1692	.width		= 7,
1693};
1694
1695static 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,
1701	.width		= 7,
1702};
1703
1704static struct c2c_dimension dim_dram_lcl = {
1705	.header		= HEADER_SPAN("--- Load Dram ----", "Lcl", 1),
1706	.name		= "dram_lcl",
1707	.cmp		= lcl_dram_cmp,
1708	.entry		= lcl_dram_entry,
1709	.width		= 8,
1710};
1711
1712static struct c2c_dimension dim_dram_rmt = {
1713	.header		= HEADER_SPAN_LOW("Rmt"),
1714	.name		= "dram_rmt",
1715	.cmp		= rmt_dram_cmp,
1716	.entry		= rmt_dram_entry,
1717	.width		= 8,
1718};
1719
1720static struct c2c_dimension dim_pid = {
1721	.header		= HEADER_LOW("Pid"),
1722	.name		= "pid",
1723	.cmp		= pid_cmp,
1724	.entry		= pid_entry,
1725	.width		= 7,
1726};
1727
1728static struct c2c_dimension dim_tid = {
1729	.header		= HEADER_LOW("Tid"),
1730	.name		= "tid",
1731	.se		= &sort_thread,
1732};
1733
1734static struct c2c_dimension dim_symbol = {
1735	.name		= "symbol",
1736	.se		= &sort_sym,
1737};
1738
1739static struct c2c_dimension dim_dso = {
1740	.header		= HEADER_BOTH("Shared", "Object"),
1741	.name		= "dso",
1742	.se		= &sort_dso,
1743};
1744
1745static struct c2c_dimension dim_node = {
1746	.name		= "node",
1747	.cmp		= empty_cmp,
1748	.entry		= node_entry,
1749	.width		= 4,
1750};
1751
1752static struct c2c_dimension dim_mean_rmt = {
1753	.header		= HEADER_SPAN("---------- cycles ----------", "rmt hitm", 2),
1754	.name		= "mean_rmt",
1755	.cmp		= empty_cmp,
1756	.entry		= mean_rmt_entry,
1757	.width		= 8,
1758};
1759
1760static struct c2c_dimension dim_mean_lcl = {
1761	.header		= HEADER_SPAN_LOW("lcl hitm"),
1762	.name		= "mean_lcl",
1763	.cmp		= empty_cmp,
1764	.entry		= mean_lcl_entry,
1765	.width		= 8,
1766};
1767
1768static struct c2c_dimension dim_mean_load = {
1769	.header		= HEADER_SPAN_LOW("load"),
1770	.name		= "mean_load",
1771	.cmp		= empty_cmp,
1772	.entry		= mean_load_entry,
1773	.width		= 8,
1774};
1775
1776static struct c2c_dimension dim_mean_rmt_peer = {
1777	.header		= HEADER_SPAN("---------- cycles ----------", "rmt peer", 2),
1778	.name		= "mean_rmt_peer",
1779	.cmp		= empty_cmp,
1780	.entry		= mean_rmt_peer_entry,
1781	.width		= 8,
1782};
1783
1784static struct c2c_dimension dim_mean_lcl_peer = {
1785	.header		= HEADER_SPAN_LOW("lcl peer"),
1786	.name		= "mean_lcl_peer",
1787	.cmp		= empty_cmp,
1788	.entry		= mean_lcl_peer_entry,
1789	.width		= 8,
1790};
1791
1792static struct c2c_dimension dim_cpucnt = {
1793	.header		= HEADER_BOTH("cpu", "cnt"),
1794	.name		= "cpucnt",
1795	.cmp		= empty_cmp,
1796	.entry		= cpucnt_entry,
1797	.width		= 8,
1798};
1799
1800static struct c2c_dimension dim_srcline = {
1801	.name		= "cl_srcline",
1802	.se		= &sort_srcline,
1803};
1804
1805static struct c2c_dimension dim_dcacheline_idx = {
1806	.header		= HEADER_LOW("Index"),
1807	.name		= "cl_idx",
1808	.cmp		= empty_cmp,
1809	.entry		= cl_idx_entry,
1810	.width		= 5,
1811};
1812
1813static struct c2c_dimension dim_dcacheline_num = {
1814	.header		= HEADER_LOW("Num"),
1815	.name		= "cl_num",
1816	.cmp		= empty_cmp,
1817	.entry		= cl_idx_entry,
1818	.width		= 5,
1819};
1820
1821static struct c2c_dimension dim_dcacheline_num_empty = {
1822	.header		= HEADER_LOW("Num"),
1823	.name		= "cl_num_empty",
1824	.cmp		= empty_cmp,
1825	.entry		= cl_idx_empty_entry,
1826	.width		= 5,
1827};
1828
1829static struct c2c_dimension *dimensions[] = {
1830	&dim_dcacheline,
1831	&dim_dcacheline_node,
1832	&dim_dcacheline_count,
1833	&dim_offset,
1834	&dim_offset_node,
1835	&dim_iaddr,
1836	&dim_tot_hitm,
1837	&dim_lcl_hitm,
1838	&dim_rmt_hitm,
1839	&dim_tot_peer,
1840	&dim_lcl_peer,
1841	&dim_rmt_peer,
1842	&dim_cl_lcl_hitm,
1843	&dim_cl_rmt_hitm,
1844	&dim_cl_lcl_peer,
1845	&dim_cl_rmt_peer,
1846	&dim_tot_stores,
1847	&dim_stores_l1hit,
1848	&dim_stores_l1miss,
1849	&dim_stores_na,
1850	&dim_cl_stores_l1hit,
1851	&dim_cl_stores_l1miss,
1852	&dim_cl_stores_na,
1853	&dim_ld_fbhit,
1854	&dim_ld_l1hit,
1855	&dim_ld_l2hit,
1856	&dim_ld_llchit,
1857	&dim_ld_rmthit,
1858	&dim_tot_recs,
1859	&dim_tot_loads,
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,
1868	&dim_dram_lcl,
1869	&dim_dram_rmt,
1870	&dim_pid,
1871	&dim_tid,
1872	&dim_symbol,
1873	&dim_dso,
1874	&dim_node,
1875	&dim_mean_rmt,
1876	&dim_mean_lcl,
1877	&dim_mean_rmt_peer,
1878	&dim_mean_lcl_peer,
1879	&dim_mean_load,
1880	&dim_cpucnt,
1881	&dim_srcline,
1882	&dim_dcacheline_idx,
1883	&dim_dcacheline_num,
1884	&dim_dcacheline_num_empty,
1885	NULL,
1886};
1887
1888static void fmt_free(struct perf_hpp_fmt *fmt)
1889{
1890	struct c2c_fmt *c2c_fmt;
1891
1892	c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
1893	free(c2c_fmt);
1894}
1895
1896static bool fmt_equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)
1897{
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);
1900
1901	return c2c_a->dim == c2c_b->dim;
1902}
1903
1904static struct c2c_dimension *get_dimension(const char *name)
1905{
1906	unsigned int i;
1907
1908	for (i = 0; dimensions[i]; i++) {
1909		struct c2c_dimension *dim = dimensions[i];
1910
1911		if (!strcmp(dim->name, name))
1912			return dim;
1913	}
1914
1915	return NULL;
1916}
1917
1918static int c2c_se_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1919			struct hist_entry *he)
1920{
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;
1924
1925	if (!len) {
1926		len = hists__col_len(he->hists, dim->se->se_width_idx);
1927
1928		if (dim == &dim_symbol || dim == &dim_srcline)
1929			len = symbol_width(he->hists, dim->se);
1930	}
1931
1932	return dim->se->se_snprintf(he, hpp->buf, hpp->size, len);
1933}
1934
1935static int64_t c2c_se_cmp(struct perf_hpp_fmt *fmt,
1936			  struct hist_entry *a, struct hist_entry *b)
1937{
1938	struct c2c_fmt *c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
1939	struct c2c_dimension *dim = c2c_fmt->dim;
1940
1941	return dim->se->se_cmp(a, b);
1942}
1943
1944static int64_t c2c_se_collapse(struct perf_hpp_fmt *fmt,
1945			       struct hist_entry *a, struct hist_entry *b)
1946{
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 *);
1950
1951	collapse_fn = dim->se->se_collapse ?: dim->se->se_cmp;
1952	return collapse_fn(a, b);
1953}
1954
1955static struct c2c_fmt *get_format(const char *name)
1956{
1957	struct c2c_dimension *dim = get_dimension(name);
1958	struct c2c_fmt *c2c_fmt;
1959	struct perf_hpp_fmt *fmt;
1960
1961	if (!dim)
1962		return NULL;
1963
1964	c2c_fmt = zalloc(sizeof(*c2c_fmt));
1965	if (!c2c_fmt)
1966		return NULL;
1967
1968	c2c_fmt->dim = dim;
1969
1970	fmt = &c2c_fmt->fmt;
1971	INIT_LIST_HEAD(&fmt->list);
1972	INIT_LIST_HEAD(&fmt->sort_list);
1973
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;
1983
1984	return c2c_fmt;
1985}
1986
1987static int c2c_hists__init_output(struct perf_hpp_list *hpp_list, char *name)
1988{
1989	struct c2c_fmt *c2c_fmt = get_format(name);
1990
1991	if (!c2c_fmt) {
1992		reset_dimensions();
1993		return output_field_add(hpp_list, name);
1994	}
1995
1996	perf_hpp_list__column_register(hpp_list, &c2c_fmt->fmt);
1997	return 0;
1998}
1999
2000static int c2c_hists__init_sort(struct perf_hpp_list *hpp_list, char *name)
2001{
2002	struct c2c_fmt *c2c_fmt = get_format(name);
2003	struct c2c_dimension *dim;
2004
2005	if (!c2c_fmt) {
2006		reset_dimensions();
2007		return sort_dimension__add(hpp_list, name, NULL, 0);
2008	}
2009
2010	dim = c2c_fmt->dim;
2011	if (dim == &dim_dso)
2012		hpp_list->dso = 1;
2013
2014	perf_hpp_list__register_sort_field(hpp_list, &c2c_fmt->fmt);
2015	return 0;
2016}
2017
2018#define PARSE_LIST(_list, _fn)							\
2019	do {									\
2020		char *tmp, *tok;						\
2021		ret = 0;							\
2022										\
2023		if (!_list)							\
2024			break;							\
2025										\
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);	\
2031				break;						\
2032			} else if (ret == -ESRCH) {				\
2033				pr_err("Unknown --fields key: `%s'", tok);	\
2034				break;						\
2035			}							\
2036		}								\
2037	} while (0)
2038
2039static int hpp_list__parse(struct perf_hpp_list *hpp_list,
2040			   const char *output_,
2041			   const char *sort_)
2042{
2043	char *output = output_ ? strdup(output_) : NULL;
2044	char *sort   = sort_   ? strdup(sort_) : NULL;
2045	int ret;
2046
2047	PARSE_LIST(output, c2c_hists__init_output);
2048	PARSE_LIST(sort,   c2c_hists__init_sort);
2049
2050	/* copy sort keys to output fields */
2051	perf_hpp__setup_output_field(hpp_list);
2052
2053	/*
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.
2058	 */
2059
2060#if 0
2061	/* and then copy output fields to sort keys */
2062	perf_hpp__append_sort_keys(&hists->list);
2063#endif
2064
2065	free(output);
2066	free(sort);
2067	return ret;
2068}
2069
2070static int c2c_hists__init(struct c2c_hists *hists,
2071			   const char *sort,
2072			   int nr_header_lines)
2073{
2074	__hists__init(&hists->hists, &hists->list);
2075
2076	/*
2077	 * Initialize only with sort fields, we need to resort
2078	 * later anyway, and that's where we add output fields
2079	 * as well.
2080	 */
2081	perf_hpp_list__init(&hists->list);
2082
2083	/* Overload number of header lines.*/
2084	hists->list.nr_header_lines = nr_header_lines;
2085
2086	return hpp_list__parse(&hists->list, NULL, sort);
2087}
2088
2089static int c2c_hists__reinit(struct c2c_hists *c2c_hists,
2090			     const char *output,
2091			     const char *sort)
2092{
2093	perf_hpp__reset_output_field(&c2c_hists->list);
2094	return hpp_list__parse(&c2c_hists->list, output, sort);
2095}
2096
2097#define DISPLAY_LINE_LIMIT  0.001
2098
2099static u8 filter_display(u32 val, u32 sum)
2100{
2101	if (sum == 0 || ((double)val / sum) < DISPLAY_LINE_LIMIT)
2102		return HIST_FILTER__C2C;
2103
2104	return 0;
2105}
2106
2107static bool he__display(struct hist_entry *he, struct c2c_stats *stats)
2108{
2109	struct c2c_hist_entry *c2c_he;
2110
2111	if (c2c.show_all)
2112		return true;
2113
2114	c2c_he = container_of(he, struct c2c_hist_entry, he);
2115
2116	switch (c2c.display) {
2117	case DISPLAY_LCL_HITM:
2118		he->filtered = filter_display(c2c_he->stats.lcl_hitm,
2119					      stats->lcl_hitm);
2120		break;
2121	case DISPLAY_RMT_HITM:
2122		he->filtered = filter_display(c2c_he->stats.rmt_hitm,
2123					      stats->rmt_hitm);
2124		break;
2125	case DISPLAY_TOT_HITM:
2126		he->filtered = filter_display(c2c_he->stats.tot_hitm,
2127					      stats->tot_hitm);
2128		break;
2129	case DISPLAY_SNP_PEER:
2130		he->filtered = filter_display(c2c_he->stats.tot_peer,
2131					      stats->tot_peer);
2132		break;
2133	default:
2134		break;
2135	}
2136
2137	return he->filtered == 0;
2138}
2139
2140static inline bool is_valid_hist_entry(struct hist_entry *he)
2141{
2142	struct c2c_hist_entry *c2c_he;
2143	bool has_record = false;
2144
2145	c2c_he = container_of(he, struct c2c_hist_entry, he);
2146
2147	/* It's a valid entry if contains stores */
2148	if (c2c_he->stats.store)
2149		return true;
2150
2151	switch (c2c.display) {
2152	case DISPLAY_LCL_HITM:
2153		has_record = !!c2c_he->stats.lcl_hitm;
2154		break;
2155	case DISPLAY_RMT_HITM:
2156		has_record = !!c2c_he->stats.rmt_hitm;
2157		break;
2158	case DISPLAY_TOT_HITM:
2159		has_record = !!c2c_he->stats.tot_hitm;
2160		break;
2161	case DISPLAY_SNP_PEER:
2162		has_record = !!c2c_he->stats.tot_peer;
2163	default:
2164		break;
2165	}
2166
2167	return has_record;
2168}
2169
2170static void set_node_width(struct c2c_hist_entry *c2c_he, int len)
2171{
2172	struct c2c_dimension *dim;
2173
2174	dim = &c2c.hists == c2c_he->hists ?
2175	      &dim_dcacheline_node : &dim_offset_node;
2176
2177	if (len > dim->width)
2178		dim->width = len;
2179}
2180
2181static int set_nodestr(struct c2c_hist_entry *c2c_he)
2182{
2183	char buf[30];
2184	int len;
2185
2186	if (c2c_he->nodestr)
2187		return 0;
2188
2189	if (!bitmap_empty(c2c_he->nodeset, c2c.nodes_cnt)) {
2190		len = bitmap_scnprintf(c2c_he->nodeset, c2c.nodes_cnt,
2191				      buf, sizeof(buf));
2192	} else {
2193		len = scnprintf(buf, sizeof(buf), "N/A");
2194	}
2195
2196	set_node_width(c2c_he, len);
2197	c2c_he->nodestr = strdup(buf);
2198	return c2c_he->nodestr ? 0 : -ENOMEM;
2199}
2200
2201static void calc_width(struct c2c_hist_entry *c2c_he)
2202{
2203	struct c2c_hists *c2c_hists;
2204
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);
2208}
2209
2210static int filter_cb(struct hist_entry *he, void *arg __maybe_unused)
2211{
2212	struct c2c_hist_entry *c2c_he;
2213
2214	c2c_he = container_of(he, struct c2c_hist_entry, he);
2215
2216	if (c2c.show_src && !he->srcline)
2217		he->srcline = hist_entry__srcline(he);
2218
2219	calc_width(c2c_he);
2220
2221	if (!is_valid_hist_entry(he))
2222		he->filtered = HIST_FILTER__C2C;
2223
2224	return 0;
2225}
2226
2227static int resort_cl_cb(struct hist_entry *he, void *arg __maybe_unused)
2228{
2229	struct c2c_hist_entry *c2c_he;
2230	struct c2c_hists *c2c_hists;
2231	bool display = he__display(he, &c2c.shared_clines_stats);
2232
2233	c2c_he = container_of(he, struct c2c_hist_entry, he);
2234	c2c_hists = c2c_he->hists;
2235
2236	if (display && c2c_hists) {
2237		static unsigned int idx;
2238
2239		c2c_he->cacheline_idx = idx++;
2240		calc_width(c2c_he);
2241
2242		c2c_hists__reinit(c2c_hists, c2c.cl_output, c2c.cl_resort);
2243
2244		hists__collapse_resort(&c2c_hists->hists, NULL);
2245		hists__output_resort_cb(&c2c_hists->hists, NULL, filter_cb);
2246	}
2247
2248	return 0;
2249}
2250
2251static struct c2c_header header_node_0 = HEADER_LOW("Node");
2252static struct c2c_header header_node_1_hitms_stores =
2253		HEADER_LOW("Node{cpus %hitms %stores}");
2254static struct c2c_header header_node_1_peers_stores =
2255		HEADER_LOW("Node{cpus %peers %stores}");
2256static struct c2c_header header_node_2 = HEADER_LOW("Node{cpu list}");
2257
2258static void setup_nodes_header(void)
2259{
2260	switch (c2c.node_info) {
2261	case 0:
2262		dim_node.header = header_node_0;
2263		break;
2264	case 1:
2265		if (c2c.display == DISPLAY_SNP_PEER)
2266			dim_node.header = header_node_1_peers_stores;
2267		else
2268			dim_node.header = header_node_1_hitms_stores;
2269		break;
2270	case 2:
2271		dim_node.header = header_node_2;
2272		break;
2273	default:
2274		break;
2275	}
2276
2277	return;
2278}
2279
2280static int setup_nodes(struct perf_session *session)
2281{
2282	struct numa_node *n;
2283	unsigned long **nodes;
2284	int node, idx;
2285	struct perf_cpu cpu;
2286	int *cpu2node;
2287
2288	if (c2c.node_info > 2)
2289		c2c.node_info = 2;
2290
2291	c2c.nodes_cnt = session->header.env.nr_numa_nodes;
2292	c2c.cpus_cnt  = session->header.env.nr_cpus_avail;
2293
2294	n = session->header.env.numa_nodes;
2295	if (!n)
2296		return -EINVAL;
2297
2298	nodes = zalloc(sizeof(unsigned long *) * c2c.nodes_cnt);
2299	if (!nodes)
2300		return -ENOMEM;
2301
2302	c2c.nodes = nodes;
2303
2304	cpu2node = zalloc(sizeof(int) * c2c.cpus_cnt);
2305	if (!cpu2node)
2306		return -ENOMEM;
2307
2308	for (idx = 0; idx < c2c.cpus_cnt; idx++)
2309		cpu2node[idx] = -1;
2310
2311	c2c.cpu2node = cpu2node;
2312
2313	for (node = 0; node < c2c.nodes_cnt; node++) {
2314		struct perf_cpu_map *map = n[node].map;
2315		unsigned long *set;
2316
2317		set = bitmap_zalloc(c2c.cpus_cnt);
2318		if (!set)
2319			return -ENOMEM;
2320
2321		nodes[node] = set;
2322
2323		perf_cpu_map__for_each_cpu_skip_any(cpu, idx, map) {
2324			__set_bit(cpu.cpu, set);
2325
2326			if (WARN_ONCE(cpu2node[cpu.cpu] != -1, "node/cpu topology bug"))
2327				return -EINVAL;
2328
2329			cpu2node[cpu.cpu] = node;
2330		}
2331	}
2332
2333	setup_nodes_header();
2334	return 0;
2335}
2336
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)
2339
2340static int resort_shared_cl_cb(struct hist_entry *he, void *arg __maybe_unused)
2341{
2342	struct c2c_hist_entry *c2c_he;
2343	c2c_he = container_of(he, struct c2c_hist_entry, he);
2344
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);
2348	}
2349
2350	return 0;
2351}
2352
2353static int hists__iterate_cb(struct hists *hists, hists__resort_cb_t cb)
2354{
2355	struct rb_node *next = rb_first_cached(&hists->entries);
2356	int ret = 0;
2357
2358	while (next) {
2359		struct hist_entry *he;
2360
2361		he = rb_entry(next, struct hist_entry, rb_node);
2362		ret = cb(he, NULL);
2363		if (ret)
2364			break;
2365		next = rb_next(&he->rb_node);
2366	}
2367
2368	return ret;
2369}
2370
2371static void print_c2c__display_stats(FILE *out)
2372{
2373	int llc_misses;
2374	struct c2c_stats *stats = &c2c.hists.stats;
2375
2376	llc_misses = get_load_llc_misses(stats);
2377
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);
2416}
2417
2418static void print_shared_cacheline_info(FILE *out)
2419{
2420	struct c2c_stats *stats = &c2c.shared_clines_stats;
2421	int hitm_cnt = stats->lcl_hitm + stats->rmt_hitm;
2422
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);
2439}
2440
2441static void print_cacheline(struct c2c_hists *c2c_hists,
2442			    struct hist_entry *he_cl,
2443			    struct perf_hpp_list *hpp_list,
2444			    FILE *out)
2445{
2446	char bf[1000];
2447	struct perf_hpp hpp = {
2448		.buf            = bf,
2449		.size           = 1000,
2450	};
2451	static bool once;
2452
2453	if (!once) {
2454		hists__fprintf_headers(&c2c_hists->hists, out);
2455		once = true;
2456	} else {
2457		fprintf(out, "\n");
2458	}
2459
2460	fprintf(out, "  ----------------------------------------------------------------------\n");
2461	__hist_entry__snprintf(he_cl, &hpp, hpp_list);
2462	fprintf(out, "%s\n", bf);
2463	fprintf(out, "  ----------------------------------------------------------------------\n");
2464
2465	hists__fprintf(&c2c_hists->hists, false, 0, 0, 0, out, false);
2466}
2467
2468static void print_pareto(FILE *out)
2469{
2470	struct perf_hpp_list hpp_list;
2471	struct rb_node *nd;
2472	int ret;
2473	const char *cl_output;
2474
2475	if (c2c.display != DISPLAY_SNP_PEER)
2476		cl_output = "cl_num,"
2477			    "cl_rmt_hitm,"
2478			    "cl_lcl_hitm,"
2479			    "cl_stores_l1hit,"
2480			    "cl_stores_l1miss,"
2481			    "cl_stores_na,"
2482			    "dcacheline";
2483	else
2484		cl_output = "cl_num,"
2485			    "cl_rmt_peer,"
2486			    "cl_lcl_peer,"
2487			    "cl_stores_l1hit,"
2488			    "cl_stores_l1miss,"
2489			    "cl_stores_na,"
2490			    "dcacheline";
2491
2492	perf_hpp_list__init(&hpp_list);
2493	ret = hpp_list__parse(&hpp_list, cl_output, NULL);
2494
2495	if (WARN_ONCE(ret, "failed to setup sort entries\n"))
2496		return;
2497
2498	nd = rb_first_cached(&c2c.hists.hists.entries);
2499
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;
2503
2504		if (he->filtered)
2505			continue;
2506
2507		c2c_he = container_of(he, struct c2c_hist_entry, he);
2508		print_cacheline(c2c_he->hists, he, &hpp_list, out);
2509	}
2510}
2511
2512static void print_c2c_info(FILE *out, struct perf_session *session)
2513{
2514	struct evlist *evlist = session->evlist;
2515	struct evsel *evsel;
2516	bool first = true;
2517
2518	fprintf(out, "=================================================\n");
2519	fprintf(out, "                 c2c details                     \n");
2520	fprintf(out, "=================================================\n");
2521
2522	evlist__for_each_entry(evlist, evsel) {
2523		fprintf(out, "%-36s: %s\n", first ? "  Events" : "", evsel__name(evsel));
2524		first = false;
2525	}
2526	fprintf(out, "  Cachelines sort on                : %s\n",
2527		display_str[c2c.display]);
2528	fprintf(out, "  Cacheline data grouping           : %s\n", c2c.cl_sort);
2529}
2530
2531static void perf_c2c__hists_fprintf(FILE *out, struct perf_session *session)
2532{
2533	setup_pager();
2534
2535	print_c2c__display_stats(out);
2536	fprintf(out, "\n");
2537	print_shared_cacheline_info(out);
2538	fprintf(out, "\n");
2539	print_c2c_info(out, session);
2540
2541	if (c2c.stats_only)
2542		return;
2543
2544	fprintf(out, "\n");
2545	fprintf(out, "=================================================\n");
2546	fprintf(out, "           Shared Data Cache Line Table          \n");
2547	fprintf(out, "=================================================\n");
2548	fprintf(out, "#\n");
2549
2550	hists__fprintf(&c2c.hists.hists, true, 0, 0, 0, stdout, true);
2551
2552	fprintf(out, "\n");
2553	fprintf(out, "=================================================\n");
2554	fprintf(out, "      Shared Cache Line Distribution Pareto      \n");
2555	fprintf(out, "=================================================\n");
2556	fprintf(out, "#\n");
2557
2558	print_pareto(out);
2559}
2560
2561#ifdef HAVE_SLANG_SUPPORT
2562static void c2c_browser__update_nr_entries(struct hist_browser *hb)
2563{
2564	u64 nr_entries = 0;
2565	struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2566
2567	while (nd) {
2568		struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
2569
2570		if (!he->filtered)
2571			nr_entries++;
2572
2573		nd = rb_next(nd);
2574	}
2575
2576	hb->nr_non_filtered_entries = nr_entries;
2577}
2578
2579struct c2c_cacheline_browser {
2580	struct hist_browser	 hb;
2581	struct hist_entry	*he;
2582};
2583
2584static int
2585perf_c2c_cacheline_browser__title(struct hist_browser *browser,
2586				  char *bf, size_t size)
2587{
2588	struct c2c_cacheline_browser *cl_browser;
2589	struct hist_entry *he;
2590	uint64_t addr = 0;
2591
2592	cl_browser = container_of(browser, struct c2c_cacheline_browser, hb);
2593	he = cl_browser->he;
2594
2595	if (he->mem_info)
2596		addr = cl_address(mem_info__daddr(he->mem_info)->addr, chk_double_cl);
2597
2598	scnprintf(bf, size, "Cacheline 0x%lx", addr);
2599	return 0;
2600}
2601
2602static struct c2c_cacheline_browser*
2603c2c_cacheline_browser__new(struct hists *hists, struct hist_entry *he)
2604{
2605	struct c2c_cacheline_browser *browser;
2606
2607	browser = zalloc(sizeof(*browser));
2608	if (browser) {
2609		hist_browser__init(&browser->hb, hists);
2610		browser->hb.c2c_filter	= true;
2611		browser->hb.title	= perf_c2c_cacheline_browser__title;
2612		browser->he		= he;
2613	}
2614
2615	return browser;
2616}
2617
2618static int perf_c2c__browse_cacheline(struct hist_entry *he)
2619{
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;
2624	int key = -1;
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";
2630
2631	if (!he)
2632		return 0;
2633
2634	/* Display compact version first. */
2635	c2c.symbol_full = false;
2636
2637	c2c_he = container_of(he, struct c2c_hist_entry, he);
2638	c2c_hists = c2c_he->hists;
2639
2640	cl_browser = c2c_cacheline_browser__new(&c2c_hists->hists, he);
2641	if (cl_browser == NULL)
2642		return -1;
2643
2644	browser = &cl_browser->hb;
2645
2646	/* reset abort key so that it can get Ctrl-C as a key */
2647	SLang_reset_tty();
2648	SLang_init_tty(0, 0, 0);
2649
2650	c2c_browser__update_nr_entries(browser);
2651
2652	while (1) {
2653		key = hist_browser__run(browser, "? - help", true, 0);
2654
2655		switch (key) {
2656		case 's':
2657			c2c.symbol_full = !c2c.symbol_full;
2658			break;
2659		case 'n':
2660			c2c.node_info = (c2c.node_info + 1) % 3;
2661			setup_nodes_header();
2662			break;
2663		case 'q':
2664			goto out;
2665		case '?':
2666			ui_browser__help_window(&browser->b, help);
2667			break;
2668		default:
2669			break;
2670		}
2671	}
2672
2673out:
2674	free(cl_browser);
2675	return 0;
2676}
2677
2678static int perf_c2c_browser__title(struct hist_browser *browser,
2679				   char *bf, size_t size)
2680{
2681	scnprintf(bf, size,
2682		  "Shared Data Cache Line Table     "
2683		  "(%lu entries, sorted on %s)",
2684		  browser->nr_non_filtered_entries,
2685		  display_str[c2c.display]);
2686	return 0;
2687}
2688
2689static struct hist_browser*
2690perf_c2c_browser__new(struct hists *hists)
2691{
2692	struct hist_browser *browser = hist_browser__new(hists);
2693
2694	if (browser) {
2695		browser->title = perf_c2c_browser__title;
2696		browser->c2c_filter = true;
2697	}
2698
2699	return browser;
2700}
2701
2702static int perf_c2c__hists_browse(struct hists *hists)
2703{
2704	struct hist_browser *browser;
2705	int key = -1;
2706	static const char help[] =
2707	" d             Display cacheline details \n"
2708	" ENTER         Toggle callchains (if present) \n"
2709	" q             Quit \n";
2710
2711	browser = perf_c2c_browser__new(hists);
2712	if (browser == NULL)
2713		return -1;
2714
2715	/* reset abort key so that it can get Ctrl-C as a key */
2716	SLang_reset_tty();
2717	SLang_init_tty(0, 0, 0);
2718
2719	c2c_browser__update_nr_entries(browser);
2720
2721	while (1) {
2722		key = hist_browser__run(browser, "? - help", true, 0);
2723
2724		switch (key) {
2725		case 'q':
2726			goto out;
2727		case 'd':
2728			perf_c2c__browse_cacheline(browser->he_selection);
2729			break;
2730		case '?':
2731			ui_browser__help_window(&browser->b, help);
2732			break;
2733		default:
2734			break;
2735		}
2736	}
2737
2738out:
2739	hist_browser__delete(browser);
2740	return 0;
2741}
2742
2743static void perf_c2c_display(struct perf_session *session)
2744{
2745	if (use_browser == 0)
2746		perf_c2c__hists_fprintf(stdout, session);
2747	else
2748		perf_c2c__hists_browse(&c2c.hists.hists);
2749}
2750#else
2751static void perf_c2c_display(struct perf_session *session)
2752{
2753	use_browser = 0;
2754	perf_c2c__hists_fprintf(stdout, session);
2755}
2756#endif /* HAVE_SLANG_SUPPORT */
2757
2758static char *fill_line(const char *orig, int len)
2759{
2760	int i, j, olen = strlen(orig);
2761	char *buf;
2762
2763	buf = zalloc(len + 1);
2764	if (!buf)
2765		return NULL;
2766
2767	j = len / 2 - olen / 2;
2768
2769	for (i = 0; i < j - 1; i++)
2770		buf[i] = '-';
2771
2772	buf[i++] = ' ';
2773
2774	strcpy(buf + i, orig);
2775
2776	i += olen;
2777
2778	buf[i++] = ' ';
2779
2780	for (; i < len; i++)
2781		buf[i] = '-';
2782
2783	return buf;
2784}
2785
2786static int ui_quirks(void)
2787{
2788	const char *nodestr = "Data address";
2789	char *buf;
2790
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";
2795	}
2796
2797	dim_percent_costly_snoop.header = percent_costly_snoop_header[c2c.display];
2798
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);
2804	if (!buf)
2805		return -ENOMEM;
2806
2807	dim_dcacheline.header.line[0].text = buf;
2808
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);
2813	if (!buf)
2814		return -ENOMEM;
2815
2816	dim_offset.header.line[0].text = buf;
2817
2818	return 0;
2819}
2820
2821#define CALLCHAIN_DEFAULT_OPT  "graph,0.5,caller,function,percent"
2822
2823const char callchain_help[] = "Display call graph (stack chain/backtrace):\n\n"
2824				CALLCHAIN_REPORT_HELP
2825				"\n\t\t\t\tDefault: " CALLCHAIN_DEFAULT_OPT;
2826
2827static int
2828parse_callchain_opt(const struct option *opt, const char *arg, int unset)
2829{
2830	struct callchain_param *callchain = opt->value;
2831
2832	callchain->enabled = !unset;
2833	/*
2834	 * --no-call-graph
2835	 */
2836	if (unset) {
2837		symbol_conf.use_callchain = false;
2838		callchain->mode = CHAIN_NONE;
2839		return 0;
2840	}
2841
2842	return parse_callchain_report_opt(arg);
2843}
2844
2845static int setup_callchain(struct evlist *evlist)
2846{
2847	u64 sample_type = evlist__combined_sample_type(evlist);
2848	enum perf_call_graph_mode mode = CALLCHAIN_NONE;
2849
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;
2858
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");
2865			return -EINVAL;
2866		}
2867	}
2868
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;
2873	}
2874
2875	callchain_param.record_mode = mode;
2876	callchain_param.min_percent = 0;
2877	return 0;
2878}
2879
2880static int setup_display(const char *str)
2881{
2882	const char *display = str;
2883
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;
2892	else {
2893		pr_err("failed: unknown display type: %s\n", str);
2894		return -1;
2895	}
2896
2897	return 0;
2898}
2899
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))
2903
2904static int build_cl_output(char *cl_sort, bool no_source)
2905{
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;
2913	int ret = 0;
2914
2915	if (!buf)
2916		return -ENOMEM;
2917
2918	for_each_token(tok, buf, ",", tmp) {
2919		if (!strcmp(tok, "tid")) {
2920			add_tid = true;
2921		} else if (!strcmp(tok, "pid")) {
2922			add_pid = true;
2923		} else if (!strcmp(tok, "iaddr")) {
2924			add_iaddr = true;
2925			add_sym   = true;
2926			add_dso   = true;
2927			add_src   = no_source ? false : true;
2928		} else if (!strcmp(tok, "dso")) {
2929			add_dso = true;
2930		} else if (strcmp(tok, "offset")) {
2931			pr_err("unrecognized sort token: %s\n", tok);
2932			ret = -EINVAL;
2933			goto err;
2934		}
2935	}
2936
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," :
2942						  "percent_rmt_hitm,"
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,"
2952						  "mean_lcl_peer," :
2953						  "mean_rmt,"
2954						  "mean_lcl,",
2955		"mean_load,"
2956		"tot_recs,"
2957		"cpucnt,",
2958		add_sym ? "symbol," : "",
2959		add_dso ? "dso," : "",
2960		add_src ? "cl_srcline," : "",
2961		"node") < 0) {
2962		ret = -ENOMEM;
2963		goto err;
2964	}
2965
2966	c2c.show_src = add_src;
2967err:
2968	free(buf);
2969	return ret;
2970}
2971
2972static int setup_coalesce(const char *coalesce, bool no_source)
2973{
2974	const char *c = coalesce ?: coalesce_default;
2975	const char *sort_str = NULL;
2976
2977	if (asprintf(&c2c.cl_sort, "offset,%s", c) < 0)
2978		return -ENOMEM;
2979
2980	if (build_cl_output(c2c.cl_sort, no_source))
2981		return -1;
2982
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";
2991
2992	if (asprintf(&c2c.cl_resort, "offset,%s", sort_str) < 0)
2993		return -ENOMEM;
2994
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);
2998	return 0;
2999}
3000
3001static int perf_c2c__report(int argc, const char **argv)
3002{
3003	struct itrace_synth_opts itrace_synth_opts = {
3004		.set = true,
3005		.mem = true,	/* Only enable memory event */
3006		.default_no_sample = true,
3007	};
3008
3009	struct perf_session *session;
3010	struct ui_progress prog;
3011	struct perf_data data = {
3012		.mode = PERF_DATA_MODE_READ,
3013	};
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),
3046	OPT_END()
3047	};
3048	int err = 0;
3049	const char *output_str, *sort_str = NULL;
3050
3051	argc = parse_options(argc, argv, options, report_c2c_usage,
3052			     PARSE_OPT_STOP_AT_NON_OPTION);
3053	if (argc)
3054		usage_with_options(report_c2c_usage, options);
3055
3056#ifndef HAVE_SLANG_SUPPORT
3057	c2c.use_stdio = true;
3058#endif
3059
3060	if (c2c.stats_only)
3061		c2c.use_stdio = true;
3062
3063	err = symbol__validate_sym_arguments();
3064	if (err)
3065		goto out;
3066
3067	if (!input_name || !strlen(input_name))
3068		input_name = "perf.data";
3069
3070	data.path  = input_name;
3071	data.force = symbol_conf.force;
3072
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");
3077		goto out;
3078	}
3079
3080	/*
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.
3084	 */
3085	if (!display) {
3086		if (!strcmp(perf_env__arch(&session->header.env), "arm64"))
3087			display = "peer";
3088		else
3089			display = "tot";
3090	}
3091
3092	err = setup_display(display);
3093	if (err)
3094		goto out_session;
3095
3096	err = setup_coalesce(coalesce, no_source);
3097	if (err) {
3098		pr_debug("Failed to initialize hists\n");
3099		goto out_session;
3100	}
3101
3102	err = c2c_hists__init(&c2c.hists, "dcacheline", 2);
3103	if (err) {
3104		pr_debug("Failed to initialize hists\n");
3105		goto out_session;
3106	}
3107
3108	session->itrace_synth_opts = &itrace_synth_opts;
3109
3110	err = setup_nodes(session);
3111	if (err) {
3112		pr_err("Failed setup nodes\n");
3113		goto out_session;
3114	}
3115
3116	err = mem2node__init(&c2c.mem2node, &session->header.env);
3117	if (err)
3118		goto out_session;
3119
3120	err = setup_callchain(session->evlist);
3121	if (err)
3122		goto out_mem2node;
3123
3124	if (symbol__init(&session->header.env) < 0)
3125		goto out_mem2node;
3126
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");
3130		goto out_mem2node;
3131	}
3132
3133	if (c2c.use_stdio)
3134		use_browser = 0;
3135	else
3136		use_browser = 1;
3137
3138	setup_browser(false);
3139
3140	err = perf_session__process_events(session);
3141	if (err) {
3142		pr_err("failed to process sample\n");
3143		goto out_mem2node;
3144	}
3145
3146	if (c2c.display != DISPLAY_SNP_PEER)
3147		output_str = "cl_idx,"
3148			     "dcacheline,"
3149			     "dcacheline_node,"
3150			     "dcacheline_count,"
3151			     "percent_costly_snoop,"
3152			     "tot_hitm,lcl_hitm,rmt_hitm,"
3153			     "tot_recs,"
3154			     "tot_loads,"
3155			     "tot_stores,"
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";
3161	else
3162		output_str = "cl_idx,"
3163			     "dcacheline,"
3164			     "dcacheline_node,"
3165			     "dcacheline_count,"
3166			     "percent_costly_snoop,"
3167			     "tot_peer,lcl_peer,rmt_peer,"
3168			     "tot_recs,"
3169			     "tot_loads,"
3170			     "tot_stores,"
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";
3176
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";
3185
3186	c2c_hists__reinit(&c2c.hists, output_str, sort_str);
3187
3188	ui_progress__init(&prog, c2c.hists.hists.nr_entries, "Sorting...");
3189
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);
3193
3194	ui_progress__finish();
3195
3196	if (ui_quirks()) {
3197		pr_err("failed to setup UI\n");
3198		goto out_mem2node;
3199	}
3200
3201	perf_c2c_display(session);
3202
3203out_mem2node:
3204	mem2node__exit(&c2c.mem2node);
3205out_session:
3206	perf_session__delete(session);
3207out:
3208	return err;
3209}
3210
3211static int parse_record_events(const struct option *opt,
3212			       const char *str, int unset __maybe_unused)
3213{
3214	bool *event_set = (bool *) opt->value;
3215	struct perf_pmu *pmu;
3216
3217	pmu = perf_mem_events_find_pmu();
3218	if (!pmu) {
3219		pr_err("failed: there is no PMU that supports perf c2c\n");
3220		exit(-1);
3221	}
3222
3223	if (!strcmp(str, "list")) {
3224		perf_pmu__mem_events_list(pmu);
3225		exit(0);
3226	}
3227	if (perf_pmu__mem_events_parse(pmu, str))
3228		exit(-1);
3229
3230	*event_set = true;
3231	return 0;
3232}
3233
3234
3235static const char * const __usage_record[] = {
3236	"perf c2c record [<options>] [<command>]",
3237	"perf c2c record [<options>] -- <command> [<options>]",
3238	NULL
3239};
3240
3241static const char * const *record_mem_usage = __usage_record;
3242
3243static int perf_c2c__record(int argc, const char **argv)
3244{
3245	int rec_argc, i = 0, j;
3246	const char **rec_argv;
3247	int ret;
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),
3260	OPT_END()
3261	};
3262
3263	pmu = perf_mem_events_find_pmu();
3264	if (!pmu) {
3265		pr_err("failed: no PMU supports the memory events\n");
3266		return -1;
3267	}
3268
3269	if (perf_pmu__mem_events_init(pmu)) {
3270		pr_err("failed: memory events not supported\n");
3271		return -1;
3272	}
3273
3274	argc = parse_options(argc, argv, options, record_mem_usage,
3275			     PARSE_OPT_KEEP_UNKNOWN);
3276
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);
3279
3280	rec_argv = calloc(rec_argc + 1, sizeof(char *));
3281	if (!rec_argv)
3282		return -1;
3283
3284	rec_argv[i++] = "record";
3285
3286	if (!event_set) {
3287		e = perf_pmu__mem_events_ptr(pmu, PERF_MEM_EVENTS__LOAD_STORE);
3288		/*
3289		 * The load and store operations are required, use the event
3290		 * PERF_MEM_EVENTS__LOAD_STORE if it is supported.
3291		 */
3292		if (e->tag) {
3293			e->record = true;
3294			rec_argv[i++] = "-W";
3295		} else {
3296			e = perf_pmu__mem_events_ptr(pmu, PERF_MEM_EVENTS__LOAD);
3297			e->record = true;
3298
3299			e = perf_pmu__mem_events_ptr(pmu, PERF_MEM_EVENTS__STORE);
3300			e->record = true;
3301		}
3302	}
3303
3304	e = perf_pmu__mem_events_ptr(pmu, PERF_MEM_EVENTS__LOAD);
3305	if (e->record)
3306		rec_argv[i++] = "-W";
3307
3308	rec_argv[i++] = "-d";
3309	rec_argv[i++] = "--phys-data";
3310	rec_argv[i++] = "--sample-cpu";
3311
3312	ret = perf_mem_events__record_args(rec_argv, &i);
3313	if (ret)
3314		goto out;
3315
3316	if (all_user)
3317		rec_argv[i++] = "--all-user";
3318
3319	if (all_kernel)
3320		rec_argv[i++] = "--all-kernel";
3321
3322	for (j = 0; j < argc; j++, i++)
3323		rec_argv[i] = argv[j];
3324
3325	if (verbose > 0) {
3326		pr_debug("calling: ");
3327
3328		j = 0;
3329
3330		while (rec_argv[j]) {
3331			pr_debug("%s ", rec_argv[j]);
3332			j++;
3333		}
3334		pr_debug("\n");
3335	}
3336
3337	ret = cmd_record(i, rec_argv);
3338out:
3339	free(rec_argv);
3340	return ret;
3341}
3342
3343int cmd_c2c(int argc, const char **argv)
3344{
3345	argc = parse_options(argc, argv, c2c_options, c2c_usage,
3346			     PARSE_OPT_STOP_AT_NON_OPTION);
3347
3348	if (!argc)
3349		usage_with_options(c2c_usage, c2c_options);
3350
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);
3355	} else {
3356		usage_with_options(c2c_usage, c2c_options);
3357	}
3358
3359	return 0;
3360}
3361