cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

builtin-c2c.c (75000B)


      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 "symbol.h"
     42#include "ui/ui.h"
     43#include "ui/progress.h"
     44#include "../perf.h"
     45#include "pmu.h"
     46#include "pmu-hybrid.h"
     47#include "string2.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		 load;
     59};
     60
     61struct c2c_hist_entry {
     62	struct c2c_hists	*hists;
     63	struct c2c_stats	 stats;
     64	unsigned long		*cpuset;
     65	unsigned long		*nodeset;
     66	struct c2c_stats	*node_stats;
     67	unsigned int		 cacheline_idx;
     68
     69	struct compute_stats	 cstats;
     70
     71	unsigned long		 paddr;
     72	unsigned long		 paddr_cnt;
     73	bool			 paddr_zero;
     74	char			*nodestr;
     75
     76	/*
     77	 * must be at the end,
     78	 * because of its callchain dynamic entry
     79	 */
     80	struct hist_entry	he;
     81};
     82
     83static char const *coalesce_default = "iaddr";
     84
     85struct perf_c2c {
     86	struct perf_tool	tool;
     87	struct c2c_hists	hists;
     88	struct mem2node		mem2node;
     89
     90	unsigned long		**nodes;
     91	int			 nodes_cnt;
     92	int			 cpus_cnt;
     93	int			*cpu2node;
     94	int			 node_info;
     95
     96	bool			 show_src;
     97	bool			 show_all;
     98	bool			 use_stdio;
     99	bool			 stats_only;
    100	bool			 symbol_full;
    101	bool			 stitch_lbr;
    102
    103	/* Shared cache line stats */
    104	struct c2c_stats	shared_clines_stats;
    105	int			shared_clines;
    106
    107	int			 display;
    108
    109	const char		*coalesce;
    110	char			*cl_sort;
    111	char			*cl_resort;
    112	char			*cl_output;
    113};
    114
    115enum {
    116	DISPLAY_LCL,
    117	DISPLAY_RMT,
    118	DISPLAY_TOT,
    119	DISPLAY_MAX,
    120};
    121
    122static const char *display_str[DISPLAY_MAX] = {
    123	[DISPLAY_LCL] = "Local",
    124	[DISPLAY_RMT] = "Remote",
    125	[DISPLAY_TOT] = "Total",
    126};
    127
    128static const struct option c2c_options[] = {
    129	OPT_INCR('v', "verbose", &verbose, "be more verbose (show counter open errors, etc)"),
    130	OPT_END()
    131};
    132
    133static struct perf_c2c c2c;
    134
    135static void *c2c_he_zalloc(size_t size)
    136{
    137	struct c2c_hist_entry *c2c_he;
    138
    139	c2c_he = zalloc(size + sizeof(*c2c_he));
    140	if (!c2c_he)
    141		return NULL;
    142
    143	c2c_he->cpuset = bitmap_zalloc(c2c.cpus_cnt);
    144	if (!c2c_he->cpuset)
    145		return NULL;
    146
    147	c2c_he->nodeset = bitmap_zalloc(c2c.nodes_cnt);
    148	if (!c2c_he->nodeset)
    149		return NULL;
    150
    151	c2c_he->node_stats = zalloc(c2c.nodes_cnt * sizeof(*c2c_he->node_stats));
    152	if (!c2c_he->node_stats)
    153		return NULL;
    154
    155	init_stats(&c2c_he->cstats.lcl_hitm);
    156	init_stats(&c2c_he->cstats.rmt_hitm);
    157	init_stats(&c2c_he->cstats.load);
    158
    159	return &c2c_he->he;
    160}
    161
    162static void c2c_he_free(void *he)
    163{
    164	struct c2c_hist_entry *c2c_he;
    165
    166	c2c_he = container_of(he, struct c2c_hist_entry, he);
    167	if (c2c_he->hists) {
    168		hists__delete_entries(&c2c_he->hists->hists);
    169		free(c2c_he->hists);
    170	}
    171
    172	free(c2c_he->cpuset);
    173	free(c2c_he->nodeset);
    174	free(c2c_he->nodestr);
    175	free(c2c_he->node_stats);
    176	free(c2c_he);
    177}
    178
    179static struct hist_entry_ops c2c_entry_ops = {
    180	.new	= c2c_he_zalloc,
    181	.free	= c2c_he_free,
    182};
    183
    184static int c2c_hists__init(struct c2c_hists *hists,
    185			   const char *sort,
    186			   int nr_header_lines);
    187
    188static struct c2c_hists*
    189he__get_c2c_hists(struct hist_entry *he,
    190		  const char *sort,
    191		  int nr_header_lines)
    192{
    193	struct c2c_hist_entry *c2c_he;
    194	struct c2c_hists *hists;
    195	int ret;
    196
    197	c2c_he = container_of(he, struct c2c_hist_entry, he);
    198	if (c2c_he->hists)
    199		return c2c_he->hists;
    200
    201	hists = c2c_he->hists = zalloc(sizeof(*hists));
    202	if (!hists)
    203		return NULL;
    204
    205	ret = c2c_hists__init(hists, sort, nr_header_lines);
    206	if (ret) {
    207		free(hists);
    208		return NULL;
    209	}
    210
    211	return hists;
    212}
    213
    214static void c2c_he__set_cpu(struct c2c_hist_entry *c2c_he,
    215			    struct perf_sample *sample)
    216{
    217	if (WARN_ONCE(sample->cpu == (unsigned int) -1,
    218		      "WARNING: no sample cpu value"))
    219		return;
    220
    221	set_bit(sample->cpu, c2c_he->cpuset);
    222}
    223
    224static void c2c_he__set_node(struct c2c_hist_entry *c2c_he,
    225			     struct perf_sample *sample)
    226{
    227	int node;
    228
    229	if (!sample->phys_addr) {
    230		c2c_he->paddr_zero = true;
    231		return;
    232	}
    233
    234	node = mem2node__node(&c2c.mem2node, sample->phys_addr);
    235	if (WARN_ONCE(node < 0, "WARNING: failed to find node\n"))
    236		return;
    237
    238	set_bit(node, c2c_he->nodeset);
    239
    240	if (c2c_he->paddr != sample->phys_addr) {
    241		c2c_he->paddr_cnt++;
    242		c2c_he->paddr = sample->phys_addr;
    243	}
    244}
    245
    246static void compute_stats(struct c2c_hist_entry *c2c_he,
    247			  struct c2c_stats *stats,
    248			  u64 weight)
    249{
    250	struct compute_stats *cstats = &c2c_he->cstats;
    251
    252	if (stats->rmt_hitm)
    253		update_stats(&cstats->rmt_hitm, weight);
    254	else if (stats->lcl_hitm)
    255		update_stats(&cstats->lcl_hitm, weight);
    256	else if (stats->load)
    257		update_stats(&cstats->load, weight);
    258}
    259
    260static int process_sample_event(struct perf_tool *tool __maybe_unused,
    261				union perf_event *event,
    262				struct perf_sample *sample,
    263				struct evsel *evsel,
    264				struct machine *machine)
    265{
    266	struct c2c_hists *c2c_hists = &c2c.hists;
    267	struct c2c_hist_entry *c2c_he;
    268	struct c2c_stats stats = { .nr_entries = 0, };
    269	struct hist_entry *he;
    270	struct addr_location al;
    271	struct mem_info *mi, *mi_dup;
    272	int ret;
    273
    274	if (machine__resolve(machine, &al, sample) < 0) {
    275		pr_debug("problem processing %d event, skipping it.\n",
    276			 event->header.type);
    277		return -1;
    278	}
    279
    280	if (c2c.stitch_lbr)
    281		al.thread->lbr_stitch_enable = true;
    282
    283	ret = sample__resolve_callchain(sample, &callchain_cursor, NULL,
    284					evsel, &al, sysctl_perf_event_max_stack);
    285	if (ret)
    286		goto out;
    287
    288	mi = sample__resolve_mem(sample, &al);
    289	if (mi == NULL)
    290		return -ENOMEM;
    291
    292	/*
    293	 * The mi object is released in hists__add_entry_ops,
    294	 * if it gets sorted out into existing data, so we need
    295	 * to take the copy now.
    296	 */
    297	mi_dup = mem_info__get(mi);
    298
    299	c2c_decode_stats(&stats, mi);
    300
    301	he = hists__add_entry_ops(&c2c_hists->hists, &c2c_entry_ops,
    302				  &al, NULL, NULL, mi,
    303				  sample, true);
    304	if (he == NULL)
    305		goto free_mi;
    306
    307	c2c_he = container_of(he, struct c2c_hist_entry, he);
    308	c2c_add_stats(&c2c_he->stats, &stats);
    309	c2c_add_stats(&c2c_hists->stats, &stats);
    310
    311	c2c_he__set_cpu(c2c_he, sample);
    312	c2c_he__set_node(c2c_he, sample);
    313
    314	hists__inc_nr_samples(&c2c_hists->hists, he->filtered);
    315	ret = hist_entry__append_callchain(he, sample);
    316
    317	if (!ret) {
    318		/*
    319		 * There's already been warning about missing
    320		 * sample's cpu value. Let's account all to
    321		 * node 0 in this case, without any further
    322		 * warning.
    323		 *
    324		 * Doing node stats only for single callchain data.
    325		 */
    326		int cpu = sample->cpu == (unsigned int) -1 ? 0 : sample->cpu;
    327		int node = c2c.cpu2node[cpu];
    328
    329		mi = mi_dup;
    330
    331		c2c_hists = he__get_c2c_hists(he, c2c.cl_sort, 2);
    332		if (!c2c_hists)
    333			goto free_mi;
    334
    335		he = hists__add_entry_ops(&c2c_hists->hists, &c2c_entry_ops,
    336					  &al, NULL, NULL, mi,
    337					  sample, true);
    338		if (he == NULL)
    339			goto free_mi;
    340
    341		c2c_he = container_of(he, struct c2c_hist_entry, he);
    342		c2c_add_stats(&c2c_he->stats, &stats);
    343		c2c_add_stats(&c2c_hists->stats, &stats);
    344		c2c_add_stats(&c2c_he->node_stats[node], &stats);
    345
    346		compute_stats(c2c_he, &stats, sample->weight);
    347
    348		c2c_he__set_cpu(c2c_he, sample);
    349		c2c_he__set_node(c2c_he, sample);
    350
    351		hists__inc_nr_samples(&c2c_hists->hists, he->filtered);
    352		ret = hist_entry__append_callchain(he, sample);
    353	}
    354
    355out:
    356	addr_location__put(&al);
    357	return ret;
    358
    359free_mi:
    360	mem_info__put(mi_dup);
    361	mem_info__put(mi);
    362	ret = -ENOMEM;
    363	goto out;
    364}
    365
    366static struct perf_c2c c2c = {
    367	.tool = {
    368		.sample		= process_sample_event,
    369		.mmap		= perf_event__process_mmap,
    370		.mmap2		= perf_event__process_mmap2,
    371		.comm		= perf_event__process_comm,
    372		.exit		= perf_event__process_exit,
    373		.fork		= perf_event__process_fork,
    374		.lost		= perf_event__process_lost,
    375		.attr		= perf_event__process_attr,
    376		.auxtrace_info  = perf_event__process_auxtrace_info,
    377		.auxtrace       = perf_event__process_auxtrace,
    378		.auxtrace_error = perf_event__process_auxtrace_error,
    379		.ordered_events	= true,
    380		.ordering_requires_timestamps = true,
    381	},
    382};
    383
    384static const char * const c2c_usage[] = {
    385	"perf c2c {record|report}",
    386	NULL
    387};
    388
    389static const char * const __usage_report[] = {
    390	"perf c2c report",
    391	NULL
    392};
    393
    394static const char * const *report_c2c_usage = __usage_report;
    395
    396#define C2C_HEADER_MAX 2
    397
    398struct c2c_header {
    399	struct {
    400		const char *text;
    401		int	    span;
    402	} line[C2C_HEADER_MAX];
    403};
    404
    405struct c2c_dimension {
    406	struct c2c_header	 header;
    407	const char		*name;
    408	int			 width;
    409	struct sort_entry	*se;
    410
    411	int64_t (*cmp)(struct perf_hpp_fmt *fmt,
    412		       struct hist_entry *, struct hist_entry *);
    413	int   (*entry)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
    414		       struct hist_entry *he);
    415	int   (*color)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
    416		       struct hist_entry *he);
    417};
    418
    419struct c2c_fmt {
    420	struct perf_hpp_fmt	 fmt;
    421	struct c2c_dimension	*dim;
    422};
    423
    424#define SYMBOL_WIDTH 30
    425
    426static struct c2c_dimension dim_symbol;
    427static struct c2c_dimension dim_srcline;
    428
    429static int symbol_width(struct hists *hists, struct sort_entry *se)
    430{
    431	int width = hists__col_len(hists, se->se_width_idx);
    432
    433	if (!c2c.symbol_full)
    434		width = MIN(width, SYMBOL_WIDTH);
    435
    436	return width;
    437}
    438
    439static int c2c_width(struct perf_hpp_fmt *fmt,
    440		     struct perf_hpp *hpp __maybe_unused,
    441		     struct hists *hists)
    442{
    443	struct c2c_fmt *c2c_fmt;
    444	struct c2c_dimension *dim;
    445
    446	c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
    447	dim = c2c_fmt->dim;
    448
    449	if (dim == &dim_symbol || dim == &dim_srcline)
    450		return symbol_width(hists, dim->se);
    451
    452	return dim->se ? hists__col_len(hists, dim->se->se_width_idx) :
    453			 c2c_fmt->dim->width;
    454}
    455
    456static int c2c_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
    457		      struct hists *hists, int line, int *span)
    458{
    459	struct perf_hpp_list *hpp_list = hists->hpp_list;
    460	struct c2c_fmt *c2c_fmt;
    461	struct c2c_dimension *dim;
    462	const char *text = NULL;
    463	int width = c2c_width(fmt, hpp, hists);
    464
    465	c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
    466	dim = c2c_fmt->dim;
    467
    468	if (dim->se) {
    469		text = dim->header.line[line].text;
    470		/* Use the last line from sort_entry if not defined. */
    471		if (!text && (line == hpp_list->nr_header_lines - 1))
    472			text = dim->se->se_header;
    473	} else {
    474		text = dim->header.line[line].text;
    475
    476		if (*span) {
    477			(*span)--;
    478			return 0;
    479		} else {
    480			*span = dim->header.line[line].span;
    481		}
    482	}
    483
    484	if (text == NULL)
    485		text = "";
    486
    487	return scnprintf(hpp->buf, hpp->size, "%*s", width, text);
    488}
    489
    490#define HEX_STR(__s, __v)				\
    491({							\
    492	scnprintf(__s, sizeof(__s), "0x%" PRIx64, __v);	\
    493	__s;						\
    494})
    495
    496static int64_t
    497dcacheline_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
    498	       struct hist_entry *left, struct hist_entry *right)
    499{
    500	return sort__dcacheline_cmp(left, right);
    501}
    502
    503static int dcacheline_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
    504			    struct hist_entry *he)
    505{
    506	uint64_t addr = 0;
    507	int width = c2c_width(fmt, hpp, he->hists);
    508	char buf[20];
    509
    510	if (he->mem_info)
    511		addr = cl_address(he->mem_info->daddr.addr);
    512
    513	return scnprintf(hpp->buf, hpp->size, "%*s", width, HEX_STR(buf, addr));
    514}
    515
    516static int
    517dcacheline_node_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
    518		      struct hist_entry *he)
    519{
    520	struct c2c_hist_entry *c2c_he;
    521	int width = c2c_width(fmt, hpp, he->hists);
    522
    523	c2c_he = container_of(he, struct c2c_hist_entry, he);
    524	if (WARN_ON_ONCE(!c2c_he->nodestr))
    525		return 0;
    526
    527	return scnprintf(hpp->buf, hpp->size, "%*s", width, c2c_he->nodestr);
    528}
    529
    530static int
    531dcacheline_node_count(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
    532		      struct hist_entry *he)
    533{
    534	struct c2c_hist_entry *c2c_he;
    535	int width = c2c_width(fmt, hpp, he->hists);
    536
    537	c2c_he = container_of(he, struct c2c_hist_entry, he);
    538	return scnprintf(hpp->buf, hpp->size, "%*lu", width, c2c_he->paddr_cnt);
    539}
    540
    541static int offset_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
    542			struct hist_entry *he)
    543{
    544	uint64_t addr = 0;
    545	int width = c2c_width(fmt, hpp, he->hists);
    546	char buf[20];
    547
    548	if (he->mem_info)
    549		addr = cl_offset(he->mem_info->daddr.al_addr);
    550
    551	return scnprintf(hpp->buf, hpp->size, "%*s", width, HEX_STR(buf, addr));
    552}
    553
    554static int64_t
    555offset_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
    556	   struct hist_entry *left, struct hist_entry *right)
    557{
    558	uint64_t l = 0, r = 0;
    559
    560	if (left->mem_info)
    561		l = cl_offset(left->mem_info->daddr.addr);
    562	if (right->mem_info)
    563		r = cl_offset(right->mem_info->daddr.addr);
    564
    565	return (int64_t)(r - l);
    566}
    567
    568static int
    569iaddr_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
    570	    struct hist_entry *he)
    571{
    572	uint64_t addr = 0;
    573	int width = c2c_width(fmt, hpp, he->hists);
    574	char buf[20];
    575
    576	if (he->mem_info)
    577		addr = he->mem_info->iaddr.addr;
    578
    579	return scnprintf(hpp->buf, hpp->size, "%*s", width, HEX_STR(buf, addr));
    580}
    581
    582static int64_t
    583iaddr_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
    584	  struct hist_entry *left, struct hist_entry *right)
    585{
    586	return sort__iaddr_cmp(left, right);
    587}
    588
    589static int
    590tot_hitm_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
    591	       struct hist_entry *he)
    592{
    593	struct c2c_hist_entry *c2c_he;
    594	int width = c2c_width(fmt, hpp, he->hists);
    595	unsigned int tot_hitm;
    596
    597	c2c_he = container_of(he, struct c2c_hist_entry, he);
    598	tot_hitm = c2c_he->stats.lcl_hitm + c2c_he->stats.rmt_hitm;
    599
    600	return scnprintf(hpp->buf, hpp->size, "%*u", width, tot_hitm);
    601}
    602
    603static int64_t
    604tot_hitm_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
    605	     struct hist_entry *left, struct hist_entry *right)
    606{
    607	struct c2c_hist_entry *c2c_left;
    608	struct c2c_hist_entry *c2c_right;
    609	uint64_t tot_hitm_left;
    610	uint64_t tot_hitm_right;
    611
    612	c2c_left  = container_of(left, struct c2c_hist_entry, he);
    613	c2c_right = container_of(right, struct c2c_hist_entry, he);
    614
    615	tot_hitm_left  = c2c_left->stats.lcl_hitm + c2c_left->stats.rmt_hitm;
    616	tot_hitm_right = c2c_right->stats.lcl_hitm + c2c_right->stats.rmt_hitm;
    617
    618	return tot_hitm_left - tot_hitm_right;
    619}
    620
    621#define STAT_FN_ENTRY(__f)					\
    622static int							\
    623__f ## _entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,	\
    624	      struct hist_entry *he)				\
    625{								\
    626	struct c2c_hist_entry *c2c_he;				\
    627	int width = c2c_width(fmt, hpp, he->hists);		\
    628								\
    629	c2c_he = container_of(he, struct c2c_hist_entry, he);	\
    630	return scnprintf(hpp->buf, hpp->size, "%*u", width,	\
    631			 c2c_he->stats.__f);			\
    632}
    633
    634#define STAT_FN_CMP(__f)						\
    635static int64_t								\
    636__f ## _cmp(struct perf_hpp_fmt *fmt __maybe_unused,			\
    637	    struct hist_entry *left, struct hist_entry *right)		\
    638{									\
    639	struct c2c_hist_entry *c2c_left, *c2c_right;			\
    640									\
    641	c2c_left  = container_of(left, struct c2c_hist_entry, he);	\
    642	c2c_right = container_of(right, struct c2c_hist_entry, he);	\
    643	return (uint64_t) c2c_left->stats.__f -				\
    644	       (uint64_t) c2c_right->stats.__f;				\
    645}
    646
    647#define STAT_FN(__f)		\
    648	STAT_FN_ENTRY(__f)	\
    649	STAT_FN_CMP(__f)
    650
    651STAT_FN(rmt_hitm)
    652STAT_FN(lcl_hitm)
    653STAT_FN(store)
    654STAT_FN(st_l1hit)
    655STAT_FN(st_l1miss)
    656STAT_FN(st_na)
    657STAT_FN(ld_fbhit)
    658STAT_FN(ld_l1hit)
    659STAT_FN(ld_l2hit)
    660STAT_FN(ld_llchit)
    661STAT_FN(rmt_hit)
    662
    663static uint64_t total_records(struct c2c_stats *stats)
    664{
    665	uint64_t lclmiss, ldcnt, total;
    666
    667	lclmiss  = stats->lcl_dram +
    668		   stats->rmt_dram +
    669		   stats->rmt_hitm +
    670		   stats->rmt_hit;
    671
    672	ldcnt    = lclmiss +
    673		   stats->ld_fbhit +
    674		   stats->ld_l1hit +
    675		   stats->ld_l2hit +
    676		   stats->ld_llchit +
    677		   stats->lcl_hitm;
    678
    679	total    = ldcnt +
    680		   stats->st_l1hit +
    681		   stats->st_l1miss +
    682		   stats->st_na;
    683
    684	return total;
    685}
    686
    687static int
    688tot_recs_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
    689		struct hist_entry *he)
    690{
    691	struct c2c_hist_entry *c2c_he;
    692	int width = c2c_width(fmt, hpp, he->hists);
    693	uint64_t tot_recs;
    694
    695	c2c_he = container_of(he, struct c2c_hist_entry, he);
    696	tot_recs = total_records(&c2c_he->stats);
    697
    698	return scnprintf(hpp->buf, hpp->size, "%*" PRIu64, width, tot_recs);
    699}
    700
    701static int64_t
    702tot_recs_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
    703	     struct hist_entry *left, struct hist_entry *right)
    704{
    705	struct c2c_hist_entry *c2c_left;
    706	struct c2c_hist_entry *c2c_right;
    707	uint64_t tot_recs_left;
    708	uint64_t tot_recs_right;
    709
    710	c2c_left  = container_of(left, struct c2c_hist_entry, he);
    711	c2c_right = container_of(right, struct c2c_hist_entry, he);
    712
    713	tot_recs_left  = total_records(&c2c_left->stats);
    714	tot_recs_right = total_records(&c2c_right->stats);
    715
    716	return tot_recs_left - tot_recs_right;
    717}
    718
    719static uint64_t total_loads(struct c2c_stats *stats)
    720{
    721	uint64_t lclmiss, ldcnt;
    722
    723	lclmiss  = stats->lcl_dram +
    724		   stats->rmt_dram +
    725		   stats->rmt_hitm +
    726		   stats->rmt_hit;
    727
    728	ldcnt    = lclmiss +
    729		   stats->ld_fbhit +
    730		   stats->ld_l1hit +
    731		   stats->ld_l2hit +
    732		   stats->ld_llchit +
    733		   stats->lcl_hitm;
    734
    735	return ldcnt;
    736}
    737
    738static int
    739tot_loads_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
    740		struct hist_entry *he)
    741{
    742	struct c2c_hist_entry *c2c_he;
    743	int width = c2c_width(fmt, hpp, he->hists);
    744	uint64_t tot_recs;
    745
    746	c2c_he = container_of(he, struct c2c_hist_entry, he);
    747	tot_recs = total_loads(&c2c_he->stats);
    748
    749	return scnprintf(hpp->buf, hpp->size, "%*" PRIu64, width, tot_recs);
    750}
    751
    752static int64_t
    753tot_loads_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
    754	      struct hist_entry *left, struct hist_entry *right)
    755{
    756	struct c2c_hist_entry *c2c_left;
    757	struct c2c_hist_entry *c2c_right;
    758	uint64_t tot_recs_left;
    759	uint64_t tot_recs_right;
    760
    761	c2c_left  = container_of(left, struct c2c_hist_entry, he);
    762	c2c_right = container_of(right, struct c2c_hist_entry, he);
    763
    764	tot_recs_left  = total_loads(&c2c_left->stats);
    765	tot_recs_right = total_loads(&c2c_right->stats);
    766
    767	return tot_recs_left - tot_recs_right;
    768}
    769
    770typedef double (get_percent_cb)(struct c2c_hist_entry *);
    771
    772static int
    773percent_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
    774	      struct hist_entry *he, get_percent_cb get_percent)
    775{
    776	struct c2c_hist_entry *c2c_he;
    777	int width = c2c_width(fmt, hpp, he->hists);
    778	double per;
    779
    780	c2c_he = container_of(he, struct c2c_hist_entry, he);
    781	per = get_percent(c2c_he);
    782
    783#ifdef HAVE_SLANG_SUPPORT
    784	if (use_browser)
    785		return __hpp__slsmg_color_printf(hpp, "%*.2f%%", width - 1, per);
    786#endif
    787	return hpp_color_scnprintf(hpp, "%*.2f%%", width - 1, per);
    788}
    789
    790static double percent_hitm(struct c2c_hist_entry *c2c_he)
    791{
    792	struct c2c_hists *hists;
    793	struct c2c_stats *stats;
    794	struct c2c_stats *total;
    795	int tot = 0, st = 0;
    796	double p;
    797
    798	hists = container_of(c2c_he->he.hists, struct c2c_hists, hists);
    799	stats = &c2c_he->stats;
    800	total = &hists->stats;
    801
    802	switch (c2c.display) {
    803	case DISPLAY_RMT:
    804		st  = stats->rmt_hitm;
    805		tot = total->rmt_hitm;
    806		break;
    807	case DISPLAY_LCL:
    808		st  = stats->lcl_hitm;
    809		tot = total->lcl_hitm;
    810		break;
    811	case DISPLAY_TOT:
    812		st  = stats->tot_hitm;
    813		tot = total->tot_hitm;
    814	default:
    815		break;
    816	}
    817
    818	p = tot ? (double) st / tot : 0;
    819
    820	return 100 * p;
    821}
    822
    823#define PERC_STR(__s, __v)				\
    824({							\
    825	scnprintf(__s, sizeof(__s), "%.2F%%", __v);	\
    826	__s;						\
    827})
    828
    829static int
    830percent_hitm_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
    831		   struct hist_entry *he)
    832{
    833	struct c2c_hist_entry *c2c_he;
    834	int width = c2c_width(fmt, hpp, he->hists);
    835	char buf[10];
    836	double per;
    837
    838	c2c_he = container_of(he, struct c2c_hist_entry, he);
    839	per = percent_hitm(c2c_he);
    840	return scnprintf(hpp->buf, hpp->size, "%*s", width, PERC_STR(buf, per));
    841}
    842
    843static int
    844percent_hitm_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
    845		   struct hist_entry *he)
    846{
    847	return percent_color(fmt, hpp, he, percent_hitm);
    848}
    849
    850static int64_t
    851percent_hitm_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
    852		 struct hist_entry *left, struct hist_entry *right)
    853{
    854	struct c2c_hist_entry *c2c_left;
    855	struct c2c_hist_entry *c2c_right;
    856	double per_left;
    857	double per_right;
    858
    859	c2c_left  = container_of(left, struct c2c_hist_entry, he);
    860	c2c_right = container_of(right, struct c2c_hist_entry, he);
    861
    862	per_left  = percent_hitm(c2c_left);
    863	per_right = percent_hitm(c2c_right);
    864
    865	return per_left - per_right;
    866}
    867
    868static struct c2c_stats *he_stats(struct hist_entry *he)
    869{
    870	struct c2c_hist_entry *c2c_he;
    871
    872	c2c_he = container_of(he, struct c2c_hist_entry, he);
    873	return &c2c_he->stats;
    874}
    875
    876static struct c2c_stats *total_stats(struct hist_entry *he)
    877{
    878	struct c2c_hists *hists;
    879
    880	hists = container_of(he->hists, struct c2c_hists, hists);
    881	return &hists->stats;
    882}
    883
    884static double percent(u32 st, u32 tot)
    885{
    886	return tot ? 100. * (double) st / (double) tot : 0;
    887}
    888
    889#define PERCENT(__h, __f) percent(he_stats(__h)->__f, total_stats(__h)->__f)
    890
    891#define PERCENT_FN(__f)								\
    892static double percent_ ## __f(struct c2c_hist_entry *c2c_he)			\
    893{										\
    894	struct c2c_hists *hists;						\
    895										\
    896	hists = container_of(c2c_he->he.hists, struct c2c_hists, hists);	\
    897	return percent(c2c_he->stats.__f, hists->stats.__f);			\
    898}
    899
    900PERCENT_FN(rmt_hitm)
    901PERCENT_FN(lcl_hitm)
    902PERCENT_FN(st_l1hit)
    903PERCENT_FN(st_l1miss)
    904PERCENT_FN(st_na)
    905
    906static int
    907percent_rmt_hitm_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
    908		       struct hist_entry *he)
    909{
    910	int width = c2c_width(fmt, hpp, he->hists);
    911	double per = PERCENT(he, rmt_hitm);
    912	char buf[10];
    913
    914	return scnprintf(hpp->buf, hpp->size, "%*s", width, PERC_STR(buf, per));
    915}
    916
    917static int
    918percent_rmt_hitm_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
    919		       struct hist_entry *he)
    920{
    921	return percent_color(fmt, hpp, he, percent_rmt_hitm);
    922}
    923
    924static int64_t
    925percent_rmt_hitm_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
    926		     struct hist_entry *left, struct hist_entry *right)
    927{
    928	double per_left;
    929	double per_right;
    930
    931	per_left  = PERCENT(left, rmt_hitm);
    932	per_right = PERCENT(right, rmt_hitm);
    933
    934	return per_left - per_right;
    935}
    936
    937static int
    938percent_lcl_hitm_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
    939		       struct hist_entry *he)
    940{
    941	int width = c2c_width(fmt, hpp, he->hists);
    942	double per = PERCENT(he, lcl_hitm);
    943	char buf[10];
    944
    945	return scnprintf(hpp->buf, hpp->size, "%*s", width, PERC_STR(buf, per));
    946}
    947
    948static int
    949percent_lcl_hitm_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
    950		       struct hist_entry *he)
    951{
    952	return percent_color(fmt, hpp, he, percent_lcl_hitm);
    953}
    954
    955static int64_t
    956percent_lcl_hitm_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
    957		     struct hist_entry *left, struct hist_entry *right)
    958{
    959	double per_left;
    960	double per_right;
    961
    962	per_left  = PERCENT(left, lcl_hitm);
    963	per_right = PERCENT(right, lcl_hitm);
    964
    965	return per_left - per_right;
    966}
    967
    968static int
    969percent_stores_l1hit_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
    970			   struct hist_entry *he)
    971{
    972	int width = c2c_width(fmt, hpp, he->hists);
    973	double per = PERCENT(he, st_l1hit);
    974	char buf[10];
    975
    976	return scnprintf(hpp->buf, hpp->size, "%*s", width, PERC_STR(buf, per));
    977}
    978
    979static int
    980percent_stores_l1hit_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
    981			   struct hist_entry *he)
    982{
    983	return percent_color(fmt, hpp, he, percent_st_l1hit);
    984}
    985
    986static int64_t
    987percent_stores_l1hit_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
    988			struct hist_entry *left, struct hist_entry *right)
    989{
    990	double per_left;
    991	double per_right;
    992
    993	per_left  = PERCENT(left, st_l1hit);
    994	per_right = PERCENT(right, st_l1hit);
    995
    996	return per_left - per_right;
    997}
    998
    999static int
   1000percent_stores_l1miss_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
   1001			   struct hist_entry *he)
   1002{
   1003	int width = c2c_width(fmt, hpp, he->hists);
   1004	double per = PERCENT(he, st_l1miss);
   1005	char buf[10];
   1006
   1007	return scnprintf(hpp->buf, hpp->size, "%*s", width, PERC_STR(buf, per));
   1008}
   1009
   1010static int
   1011percent_stores_l1miss_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
   1012			    struct hist_entry *he)
   1013{
   1014	return percent_color(fmt, hpp, he, percent_st_l1miss);
   1015}
   1016
   1017static int64_t
   1018percent_stores_l1miss_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
   1019			  struct hist_entry *left, struct hist_entry *right)
   1020{
   1021	double per_left;
   1022	double per_right;
   1023
   1024	per_left  = PERCENT(left, st_l1miss);
   1025	per_right = PERCENT(right, st_l1miss);
   1026
   1027	return per_left - per_right;
   1028}
   1029
   1030static int
   1031percent_stores_na_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
   1032			struct hist_entry *he)
   1033{
   1034	int width = c2c_width(fmt, hpp, he->hists);
   1035	double per = PERCENT(he, st_na);
   1036	char buf[10];
   1037
   1038	return scnprintf(hpp->buf, hpp->size, "%*s", width, PERC_STR(buf, per));
   1039}
   1040
   1041static int
   1042percent_stores_na_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
   1043			struct hist_entry *he)
   1044{
   1045	return percent_color(fmt, hpp, he, percent_st_na);
   1046}
   1047
   1048static int64_t
   1049percent_stores_na_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
   1050		      struct hist_entry *left, struct hist_entry *right)
   1051{
   1052	double per_left;
   1053	double per_right;
   1054
   1055	per_left  = PERCENT(left, st_na);
   1056	per_right = PERCENT(right, st_na);
   1057
   1058	return per_left - per_right;
   1059}
   1060
   1061STAT_FN(lcl_dram)
   1062STAT_FN(rmt_dram)
   1063
   1064static int
   1065pid_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
   1066	  struct hist_entry *he)
   1067{
   1068	int width = c2c_width(fmt, hpp, he->hists);
   1069
   1070	return scnprintf(hpp->buf, hpp->size, "%*d", width, he->thread->pid_);
   1071}
   1072
   1073static int64_t
   1074pid_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
   1075	struct hist_entry *left, struct hist_entry *right)
   1076{
   1077	return left->thread->pid_ - right->thread->pid_;
   1078}
   1079
   1080static int64_t
   1081empty_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
   1082	  struct hist_entry *left __maybe_unused,
   1083	  struct hist_entry *right __maybe_unused)
   1084{
   1085	return 0;
   1086}
   1087
   1088static int display_metrics(struct perf_hpp *hpp, u32 val, u32 sum)
   1089{
   1090	int ret;
   1091
   1092	if (sum != 0)
   1093		ret = scnprintf(hpp->buf, hpp->size, "%5.1f%% ",
   1094				percent(val, sum));
   1095	else
   1096		ret = scnprintf(hpp->buf, hpp->size, "%6s ", "n/a");
   1097
   1098	return ret;
   1099}
   1100
   1101static int
   1102node_entry(struct perf_hpp_fmt *fmt __maybe_unused, struct perf_hpp *hpp,
   1103	   struct hist_entry *he)
   1104{
   1105	struct c2c_hist_entry *c2c_he;
   1106	bool first = true;
   1107	int node;
   1108	int ret = 0;
   1109
   1110	c2c_he = container_of(he, struct c2c_hist_entry, he);
   1111
   1112	for (node = 0; node < c2c.nodes_cnt; node++) {
   1113		DECLARE_BITMAP(set, c2c.cpus_cnt);
   1114
   1115		bitmap_zero(set, c2c.cpus_cnt);
   1116		bitmap_and(set, c2c_he->cpuset, c2c.nodes[node], c2c.cpus_cnt);
   1117
   1118		if (bitmap_empty(set, c2c.cpus_cnt)) {
   1119			if (c2c.node_info == 1) {
   1120				ret = scnprintf(hpp->buf, hpp->size, "%21s", " ");
   1121				advance_hpp(hpp, ret);
   1122			}
   1123			continue;
   1124		}
   1125
   1126		if (!first) {
   1127			ret = scnprintf(hpp->buf, hpp->size, " ");
   1128			advance_hpp(hpp, ret);
   1129		}
   1130
   1131		switch (c2c.node_info) {
   1132		case 0:
   1133			ret = scnprintf(hpp->buf, hpp->size, "%2d", node);
   1134			advance_hpp(hpp, ret);
   1135			break;
   1136		case 1:
   1137		{
   1138			int num = bitmap_weight(set, c2c.cpus_cnt);
   1139			struct c2c_stats *stats = &c2c_he->node_stats[node];
   1140
   1141			ret = scnprintf(hpp->buf, hpp->size, "%2d{%2d ", node, num);
   1142			advance_hpp(hpp, ret);
   1143
   1144			switch (c2c.display) {
   1145			case DISPLAY_RMT:
   1146				ret = display_metrics(hpp, stats->rmt_hitm,
   1147						      c2c_he->stats.rmt_hitm);
   1148				break;
   1149			case DISPLAY_LCL:
   1150				ret = display_metrics(hpp, stats->lcl_hitm,
   1151						      c2c_he->stats.lcl_hitm);
   1152				break;
   1153			case DISPLAY_TOT:
   1154				ret = display_metrics(hpp, stats->tot_hitm,
   1155						      c2c_he->stats.tot_hitm);
   1156				break;
   1157			default:
   1158				break;
   1159			}
   1160
   1161			advance_hpp(hpp, ret);
   1162
   1163			if (c2c_he->stats.store > 0) {
   1164				ret = scnprintf(hpp->buf, hpp->size, "%5.1f%%}",
   1165						percent(stats->store, c2c_he->stats.store));
   1166			} else {
   1167				ret = scnprintf(hpp->buf, hpp->size, "%6s}", "n/a");
   1168			}
   1169
   1170			advance_hpp(hpp, ret);
   1171			break;
   1172		}
   1173		case 2:
   1174			ret = scnprintf(hpp->buf, hpp->size, "%2d{", node);
   1175			advance_hpp(hpp, ret);
   1176
   1177			ret = bitmap_scnprintf(set, c2c.cpus_cnt, hpp->buf, hpp->size);
   1178			advance_hpp(hpp, ret);
   1179
   1180			ret = scnprintf(hpp->buf, hpp->size, "}");
   1181			advance_hpp(hpp, ret);
   1182			break;
   1183		default:
   1184			break;
   1185		}
   1186
   1187		first = false;
   1188	}
   1189
   1190	return 0;
   1191}
   1192
   1193static int
   1194mean_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
   1195	   struct hist_entry *he, double mean)
   1196{
   1197	int width = c2c_width(fmt, hpp, he->hists);
   1198	char buf[10];
   1199
   1200	scnprintf(buf, 10, "%6.0f", mean);
   1201	return scnprintf(hpp->buf, hpp->size, "%*s", width, buf);
   1202}
   1203
   1204#define MEAN_ENTRY(__func, __val)						\
   1205static int									\
   1206__func(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, struct hist_entry *he)	\
   1207{										\
   1208	struct c2c_hist_entry *c2c_he;						\
   1209	c2c_he = container_of(he, struct c2c_hist_entry, he);			\
   1210	return mean_entry(fmt, hpp, he, avg_stats(&c2c_he->cstats.__val));	\
   1211}
   1212
   1213MEAN_ENTRY(mean_rmt_entry,  rmt_hitm);
   1214MEAN_ENTRY(mean_lcl_entry,  lcl_hitm);
   1215MEAN_ENTRY(mean_load_entry, load);
   1216
   1217static int
   1218cpucnt_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
   1219	     struct hist_entry *he)
   1220{
   1221	struct c2c_hist_entry *c2c_he;
   1222	int width = c2c_width(fmt, hpp, he->hists);
   1223	char buf[10];
   1224
   1225	c2c_he = container_of(he, struct c2c_hist_entry, he);
   1226
   1227	scnprintf(buf, 10, "%d", bitmap_weight(c2c_he->cpuset, c2c.cpus_cnt));
   1228	return scnprintf(hpp->buf, hpp->size, "%*s", width, buf);
   1229}
   1230
   1231static int
   1232cl_idx_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
   1233	     struct hist_entry *he)
   1234{
   1235	struct c2c_hist_entry *c2c_he;
   1236	int width = c2c_width(fmt, hpp, he->hists);
   1237	char buf[10];
   1238
   1239	c2c_he = container_of(he, struct c2c_hist_entry, he);
   1240
   1241	scnprintf(buf, 10, "%u", c2c_he->cacheline_idx);
   1242	return scnprintf(hpp->buf, hpp->size, "%*s", width, buf);
   1243}
   1244
   1245static int
   1246cl_idx_empty_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
   1247		   struct hist_entry *he)
   1248{
   1249	int width = c2c_width(fmt, hpp, he->hists);
   1250
   1251	return scnprintf(hpp->buf, hpp->size, "%*s", width, "");
   1252}
   1253
   1254#define HEADER_LOW(__h)			\
   1255	{				\
   1256		.line[1] = {		\
   1257			.text = __h,	\
   1258		},			\
   1259	}
   1260
   1261#define HEADER_BOTH(__h0, __h1)		\
   1262	{				\
   1263		.line[0] = {		\
   1264			.text = __h0,	\
   1265		},			\
   1266		.line[1] = {		\
   1267			.text = __h1,	\
   1268		},			\
   1269	}
   1270
   1271#define HEADER_SPAN(__h0, __h1, __s)	\
   1272	{				\
   1273		.line[0] = {		\
   1274			.text = __h0,	\
   1275			.span = __s,	\
   1276		},			\
   1277		.line[1] = {		\
   1278			.text = __h1,	\
   1279		},			\
   1280	}
   1281
   1282#define HEADER_SPAN_LOW(__h)		\
   1283	{				\
   1284		.line[1] = {		\
   1285			.text = __h,	\
   1286		},			\
   1287	}
   1288
   1289static struct c2c_dimension dim_dcacheline = {
   1290	.header		= HEADER_SPAN("--- Cacheline ----", "Address", 2),
   1291	.name		= "dcacheline",
   1292	.cmp		= dcacheline_cmp,
   1293	.entry		= dcacheline_entry,
   1294	.width		= 18,
   1295};
   1296
   1297static struct c2c_dimension dim_dcacheline_node = {
   1298	.header		= HEADER_LOW("Node"),
   1299	.name		= "dcacheline_node",
   1300	.cmp		= empty_cmp,
   1301	.entry		= dcacheline_node_entry,
   1302	.width		= 4,
   1303};
   1304
   1305static struct c2c_dimension dim_dcacheline_count = {
   1306	.header		= HEADER_LOW("PA cnt"),
   1307	.name		= "dcacheline_count",
   1308	.cmp		= empty_cmp,
   1309	.entry		= dcacheline_node_count,
   1310	.width		= 6,
   1311};
   1312
   1313static struct c2c_header header_offset_tui = HEADER_SPAN("-----", "Off", 2);
   1314
   1315static struct c2c_dimension dim_offset = {
   1316	.header		= HEADER_SPAN("--- Data address -", "Offset", 2),
   1317	.name		= "offset",
   1318	.cmp		= offset_cmp,
   1319	.entry		= offset_entry,
   1320	.width		= 18,
   1321};
   1322
   1323static struct c2c_dimension dim_offset_node = {
   1324	.header		= HEADER_LOW("Node"),
   1325	.name		= "offset_node",
   1326	.cmp		= empty_cmp,
   1327	.entry		= dcacheline_node_entry,
   1328	.width		= 4,
   1329};
   1330
   1331static struct c2c_dimension dim_iaddr = {
   1332	.header		= HEADER_LOW("Code address"),
   1333	.name		= "iaddr",
   1334	.cmp		= iaddr_cmp,
   1335	.entry		= iaddr_entry,
   1336	.width		= 18,
   1337};
   1338
   1339static struct c2c_dimension dim_tot_hitm = {
   1340	.header		= HEADER_SPAN("------- Load Hitm -------", "Total", 2),
   1341	.name		= "tot_hitm",
   1342	.cmp		= tot_hitm_cmp,
   1343	.entry		= tot_hitm_entry,
   1344	.width		= 7,
   1345};
   1346
   1347static struct c2c_dimension dim_lcl_hitm = {
   1348	.header		= HEADER_SPAN_LOW("LclHitm"),
   1349	.name		= "lcl_hitm",
   1350	.cmp		= lcl_hitm_cmp,
   1351	.entry		= lcl_hitm_entry,
   1352	.width		= 7,
   1353};
   1354
   1355static struct c2c_dimension dim_rmt_hitm = {
   1356	.header		= HEADER_SPAN_LOW("RmtHitm"),
   1357	.name		= "rmt_hitm",
   1358	.cmp		= rmt_hitm_cmp,
   1359	.entry		= rmt_hitm_entry,
   1360	.width		= 7,
   1361};
   1362
   1363static struct c2c_dimension dim_cl_rmt_hitm = {
   1364	.header		= HEADER_SPAN("----- HITM -----", "Rmt", 1),
   1365	.name		= "cl_rmt_hitm",
   1366	.cmp		= rmt_hitm_cmp,
   1367	.entry		= rmt_hitm_entry,
   1368	.width		= 7,
   1369};
   1370
   1371static struct c2c_dimension dim_cl_lcl_hitm = {
   1372	.header		= HEADER_SPAN_LOW("Lcl"),
   1373	.name		= "cl_lcl_hitm",
   1374	.cmp		= lcl_hitm_cmp,
   1375	.entry		= lcl_hitm_entry,
   1376	.width		= 7,
   1377};
   1378
   1379static struct c2c_dimension dim_tot_stores = {
   1380	.header		= HEADER_BOTH("Total", "Stores"),
   1381	.name		= "tot_stores",
   1382	.cmp		= store_cmp,
   1383	.entry		= store_entry,
   1384	.width		= 7,
   1385};
   1386
   1387static struct c2c_dimension dim_stores_l1hit = {
   1388	.header		= HEADER_SPAN("--------- Stores --------", "L1Hit", 2),
   1389	.name		= "stores_l1hit",
   1390	.cmp		= st_l1hit_cmp,
   1391	.entry		= st_l1hit_entry,
   1392	.width		= 7,
   1393};
   1394
   1395static struct c2c_dimension dim_stores_l1miss = {
   1396	.header		= HEADER_SPAN_LOW("L1Miss"),
   1397	.name		= "stores_l1miss",
   1398	.cmp		= st_l1miss_cmp,
   1399	.entry		= st_l1miss_entry,
   1400	.width		= 7,
   1401};
   1402
   1403static struct c2c_dimension dim_stores_na = {
   1404	.header		= HEADER_SPAN_LOW("N/A"),
   1405	.name		= "stores_na",
   1406	.cmp		= st_na_cmp,
   1407	.entry		= st_na_entry,
   1408	.width		= 7,
   1409};
   1410
   1411static struct c2c_dimension dim_cl_stores_l1hit = {
   1412	.header		= HEADER_SPAN("------- Store Refs ------", "L1 Hit", 2),
   1413	.name		= "cl_stores_l1hit",
   1414	.cmp		= st_l1hit_cmp,
   1415	.entry		= st_l1hit_entry,
   1416	.width		= 7,
   1417};
   1418
   1419static struct c2c_dimension dim_cl_stores_l1miss = {
   1420	.header		= HEADER_SPAN_LOW("L1 Miss"),
   1421	.name		= "cl_stores_l1miss",
   1422	.cmp		= st_l1miss_cmp,
   1423	.entry		= st_l1miss_entry,
   1424	.width		= 7,
   1425};
   1426
   1427static struct c2c_dimension dim_cl_stores_na = {
   1428	.header		= HEADER_SPAN_LOW("N/A"),
   1429	.name		= "cl_stores_na",
   1430	.cmp		= st_na_cmp,
   1431	.entry		= st_na_entry,
   1432	.width		= 7,
   1433};
   1434
   1435static struct c2c_dimension dim_ld_fbhit = {
   1436	.header		= HEADER_SPAN("----- Core Load Hit -----", "FB", 2),
   1437	.name		= "ld_fbhit",
   1438	.cmp		= ld_fbhit_cmp,
   1439	.entry		= ld_fbhit_entry,
   1440	.width		= 7,
   1441};
   1442
   1443static struct c2c_dimension dim_ld_l1hit = {
   1444	.header		= HEADER_SPAN_LOW("L1"),
   1445	.name		= "ld_l1hit",
   1446	.cmp		= ld_l1hit_cmp,
   1447	.entry		= ld_l1hit_entry,
   1448	.width		= 7,
   1449};
   1450
   1451static struct c2c_dimension dim_ld_l2hit = {
   1452	.header		= HEADER_SPAN_LOW("L2"),
   1453	.name		= "ld_l2hit",
   1454	.cmp		= ld_l2hit_cmp,
   1455	.entry		= ld_l2hit_entry,
   1456	.width		= 7,
   1457};
   1458
   1459static struct c2c_dimension dim_ld_llchit = {
   1460	.header		= HEADER_SPAN("- LLC Load Hit --", "LclHit", 1),
   1461	.name		= "ld_lclhit",
   1462	.cmp		= ld_llchit_cmp,
   1463	.entry		= ld_llchit_entry,
   1464	.width		= 8,
   1465};
   1466
   1467static struct c2c_dimension dim_ld_rmthit = {
   1468	.header		= HEADER_SPAN("- RMT Load Hit --", "RmtHit", 1),
   1469	.name		= "ld_rmthit",
   1470	.cmp		= rmt_hit_cmp,
   1471	.entry		= rmt_hit_entry,
   1472	.width		= 8,
   1473};
   1474
   1475static struct c2c_dimension dim_tot_recs = {
   1476	.header		= HEADER_BOTH("Total", "records"),
   1477	.name		= "tot_recs",
   1478	.cmp		= tot_recs_cmp,
   1479	.entry		= tot_recs_entry,
   1480	.width		= 7,
   1481};
   1482
   1483static struct c2c_dimension dim_tot_loads = {
   1484	.header		= HEADER_BOTH("Total", "Loads"),
   1485	.name		= "tot_loads",
   1486	.cmp		= tot_loads_cmp,
   1487	.entry		= tot_loads_entry,
   1488	.width		= 7,
   1489};
   1490
   1491static struct c2c_header percent_hitm_header[] = {
   1492	[DISPLAY_LCL] = HEADER_BOTH("Lcl", "Hitm"),
   1493	[DISPLAY_RMT] = HEADER_BOTH("Rmt", "Hitm"),
   1494	[DISPLAY_TOT] = HEADER_BOTH("Tot", "Hitm"),
   1495};
   1496
   1497static struct c2c_dimension dim_percent_hitm = {
   1498	.name		= "percent_hitm",
   1499	.cmp		= percent_hitm_cmp,
   1500	.entry		= percent_hitm_entry,
   1501	.color		= percent_hitm_color,
   1502	.width		= 7,
   1503};
   1504
   1505static struct c2c_dimension dim_percent_rmt_hitm = {
   1506	.header		= HEADER_SPAN("----- HITM -----", "RmtHitm", 1),
   1507	.name		= "percent_rmt_hitm",
   1508	.cmp		= percent_rmt_hitm_cmp,
   1509	.entry		= percent_rmt_hitm_entry,
   1510	.color		= percent_rmt_hitm_color,
   1511	.width		= 7,
   1512};
   1513
   1514static struct c2c_dimension dim_percent_lcl_hitm = {
   1515	.header		= HEADER_SPAN_LOW("LclHitm"),
   1516	.name		= "percent_lcl_hitm",
   1517	.cmp		= percent_lcl_hitm_cmp,
   1518	.entry		= percent_lcl_hitm_entry,
   1519	.color		= percent_lcl_hitm_color,
   1520	.width		= 7,
   1521};
   1522
   1523static struct c2c_dimension dim_percent_stores_l1hit = {
   1524	.header		= HEADER_SPAN("------- Store Refs ------", "L1 Hit", 2),
   1525	.name		= "percent_stores_l1hit",
   1526	.cmp		= percent_stores_l1hit_cmp,
   1527	.entry		= percent_stores_l1hit_entry,
   1528	.color		= percent_stores_l1hit_color,
   1529	.width		= 7,
   1530};
   1531
   1532static struct c2c_dimension dim_percent_stores_l1miss = {
   1533	.header		= HEADER_SPAN_LOW("L1 Miss"),
   1534	.name		= "percent_stores_l1miss",
   1535	.cmp		= percent_stores_l1miss_cmp,
   1536	.entry		= percent_stores_l1miss_entry,
   1537	.color		= percent_stores_l1miss_color,
   1538	.width		= 7,
   1539};
   1540
   1541static struct c2c_dimension dim_percent_stores_na = {
   1542	.header		= HEADER_SPAN_LOW("N/A"),
   1543	.name		= "percent_stores_na",
   1544	.cmp		= percent_stores_na_cmp,
   1545	.entry		= percent_stores_na_entry,
   1546	.color		= percent_stores_na_color,
   1547	.width		= 7,
   1548};
   1549
   1550static struct c2c_dimension dim_dram_lcl = {
   1551	.header		= HEADER_SPAN("--- Load Dram ----", "Lcl", 1),
   1552	.name		= "dram_lcl",
   1553	.cmp		= lcl_dram_cmp,
   1554	.entry		= lcl_dram_entry,
   1555	.width		= 8,
   1556};
   1557
   1558static struct c2c_dimension dim_dram_rmt = {
   1559	.header		= HEADER_SPAN_LOW("Rmt"),
   1560	.name		= "dram_rmt",
   1561	.cmp		= rmt_dram_cmp,
   1562	.entry		= rmt_dram_entry,
   1563	.width		= 8,
   1564};
   1565
   1566static struct c2c_dimension dim_pid = {
   1567	.header		= HEADER_LOW("Pid"),
   1568	.name		= "pid",
   1569	.cmp		= pid_cmp,
   1570	.entry		= pid_entry,
   1571	.width		= 7,
   1572};
   1573
   1574static struct c2c_dimension dim_tid = {
   1575	.header		= HEADER_LOW("Tid"),
   1576	.name		= "tid",
   1577	.se		= &sort_thread,
   1578};
   1579
   1580static struct c2c_dimension dim_symbol = {
   1581	.name		= "symbol",
   1582	.se		= &sort_sym,
   1583};
   1584
   1585static struct c2c_dimension dim_dso = {
   1586	.header		= HEADER_BOTH("Shared", "Object"),
   1587	.name		= "dso",
   1588	.se		= &sort_dso,
   1589};
   1590
   1591static struct c2c_header header_node[3] = {
   1592	HEADER_LOW("Node"),
   1593	HEADER_LOW("Node{cpus %hitms %stores}"),
   1594	HEADER_LOW("Node{cpu list}"),
   1595};
   1596
   1597static struct c2c_dimension dim_node = {
   1598	.name		= "node",
   1599	.cmp		= empty_cmp,
   1600	.entry		= node_entry,
   1601	.width		= 4,
   1602};
   1603
   1604static struct c2c_dimension dim_mean_rmt = {
   1605	.header		= HEADER_SPAN("---------- cycles ----------", "rmt hitm", 2),
   1606	.name		= "mean_rmt",
   1607	.cmp		= empty_cmp,
   1608	.entry		= mean_rmt_entry,
   1609	.width		= 8,
   1610};
   1611
   1612static struct c2c_dimension dim_mean_lcl = {
   1613	.header		= HEADER_SPAN_LOW("lcl hitm"),
   1614	.name		= "mean_lcl",
   1615	.cmp		= empty_cmp,
   1616	.entry		= mean_lcl_entry,
   1617	.width		= 8,
   1618};
   1619
   1620static struct c2c_dimension dim_mean_load = {
   1621	.header		= HEADER_SPAN_LOW("load"),
   1622	.name		= "mean_load",
   1623	.cmp		= empty_cmp,
   1624	.entry		= mean_load_entry,
   1625	.width		= 8,
   1626};
   1627
   1628static struct c2c_dimension dim_cpucnt = {
   1629	.header		= HEADER_BOTH("cpu", "cnt"),
   1630	.name		= "cpucnt",
   1631	.cmp		= empty_cmp,
   1632	.entry		= cpucnt_entry,
   1633	.width		= 8,
   1634};
   1635
   1636static struct c2c_dimension dim_srcline = {
   1637	.name		= "cl_srcline",
   1638	.se		= &sort_srcline,
   1639};
   1640
   1641static struct c2c_dimension dim_dcacheline_idx = {
   1642	.header		= HEADER_LOW("Index"),
   1643	.name		= "cl_idx",
   1644	.cmp		= empty_cmp,
   1645	.entry		= cl_idx_entry,
   1646	.width		= 5,
   1647};
   1648
   1649static struct c2c_dimension dim_dcacheline_num = {
   1650	.header		= HEADER_LOW("Num"),
   1651	.name		= "cl_num",
   1652	.cmp		= empty_cmp,
   1653	.entry		= cl_idx_entry,
   1654	.width		= 5,
   1655};
   1656
   1657static struct c2c_dimension dim_dcacheline_num_empty = {
   1658	.header		= HEADER_LOW("Num"),
   1659	.name		= "cl_num_empty",
   1660	.cmp		= empty_cmp,
   1661	.entry		= cl_idx_empty_entry,
   1662	.width		= 5,
   1663};
   1664
   1665static struct c2c_dimension *dimensions[] = {
   1666	&dim_dcacheline,
   1667	&dim_dcacheline_node,
   1668	&dim_dcacheline_count,
   1669	&dim_offset,
   1670	&dim_offset_node,
   1671	&dim_iaddr,
   1672	&dim_tot_hitm,
   1673	&dim_lcl_hitm,
   1674	&dim_rmt_hitm,
   1675	&dim_cl_lcl_hitm,
   1676	&dim_cl_rmt_hitm,
   1677	&dim_tot_stores,
   1678	&dim_stores_l1hit,
   1679	&dim_stores_l1miss,
   1680	&dim_stores_na,
   1681	&dim_cl_stores_l1hit,
   1682	&dim_cl_stores_l1miss,
   1683	&dim_cl_stores_na,
   1684	&dim_ld_fbhit,
   1685	&dim_ld_l1hit,
   1686	&dim_ld_l2hit,
   1687	&dim_ld_llchit,
   1688	&dim_ld_rmthit,
   1689	&dim_tot_recs,
   1690	&dim_tot_loads,
   1691	&dim_percent_hitm,
   1692	&dim_percent_rmt_hitm,
   1693	&dim_percent_lcl_hitm,
   1694	&dim_percent_stores_l1hit,
   1695	&dim_percent_stores_l1miss,
   1696	&dim_percent_stores_na,
   1697	&dim_dram_lcl,
   1698	&dim_dram_rmt,
   1699	&dim_pid,
   1700	&dim_tid,
   1701	&dim_symbol,
   1702	&dim_dso,
   1703	&dim_node,
   1704	&dim_mean_rmt,
   1705	&dim_mean_lcl,
   1706	&dim_mean_load,
   1707	&dim_cpucnt,
   1708	&dim_srcline,
   1709	&dim_dcacheline_idx,
   1710	&dim_dcacheline_num,
   1711	&dim_dcacheline_num_empty,
   1712	NULL,
   1713};
   1714
   1715static void fmt_free(struct perf_hpp_fmt *fmt)
   1716{
   1717	struct c2c_fmt *c2c_fmt;
   1718
   1719	c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
   1720	free(c2c_fmt);
   1721}
   1722
   1723static bool fmt_equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)
   1724{
   1725	struct c2c_fmt *c2c_a = container_of(a, struct c2c_fmt, fmt);
   1726	struct c2c_fmt *c2c_b = container_of(b, struct c2c_fmt, fmt);
   1727
   1728	return c2c_a->dim == c2c_b->dim;
   1729}
   1730
   1731static struct c2c_dimension *get_dimension(const char *name)
   1732{
   1733	unsigned int i;
   1734
   1735	for (i = 0; dimensions[i]; i++) {
   1736		struct c2c_dimension *dim = dimensions[i];
   1737
   1738		if (!strcmp(dim->name, name))
   1739			return dim;
   1740	}
   1741
   1742	return NULL;
   1743}
   1744
   1745static int c2c_se_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
   1746			struct hist_entry *he)
   1747{
   1748	struct c2c_fmt *c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
   1749	struct c2c_dimension *dim = c2c_fmt->dim;
   1750	size_t len = fmt->user_len;
   1751
   1752	if (!len) {
   1753		len = hists__col_len(he->hists, dim->se->se_width_idx);
   1754
   1755		if (dim == &dim_symbol || dim == &dim_srcline)
   1756			len = symbol_width(he->hists, dim->se);
   1757	}
   1758
   1759	return dim->se->se_snprintf(he, hpp->buf, hpp->size, len);
   1760}
   1761
   1762static int64_t c2c_se_cmp(struct perf_hpp_fmt *fmt,
   1763			  struct hist_entry *a, struct hist_entry *b)
   1764{
   1765	struct c2c_fmt *c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
   1766	struct c2c_dimension *dim = c2c_fmt->dim;
   1767
   1768	return dim->se->se_cmp(a, b);
   1769}
   1770
   1771static int64_t c2c_se_collapse(struct perf_hpp_fmt *fmt,
   1772			       struct hist_entry *a, struct hist_entry *b)
   1773{
   1774	struct c2c_fmt *c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
   1775	struct c2c_dimension *dim = c2c_fmt->dim;
   1776	int64_t (*collapse_fn)(struct hist_entry *, struct hist_entry *);
   1777
   1778	collapse_fn = dim->se->se_collapse ?: dim->se->se_cmp;
   1779	return collapse_fn(a, b);
   1780}
   1781
   1782static struct c2c_fmt *get_format(const char *name)
   1783{
   1784	struct c2c_dimension *dim = get_dimension(name);
   1785	struct c2c_fmt *c2c_fmt;
   1786	struct perf_hpp_fmt *fmt;
   1787
   1788	if (!dim)
   1789		return NULL;
   1790
   1791	c2c_fmt = zalloc(sizeof(*c2c_fmt));
   1792	if (!c2c_fmt)
   1793		return NULL;
   1794
   1795	c2c_fmt->dim = dim;
   1796
   1797	fmt = &c2c_fmt->fmt;
   1798	INIT_LIST_HEAD(&fmt->list);
   1799	INIT_LIST_HEAD(&fmt->sort_list);
   1800
   1801	fmt->cmp	= dim->se ? c2c_se_cmp   : dim->cmp;
   1802	fmt->sort	= dim->se ? c2c_se_cmp   : dim->cmp;
   1803	fmt->color	= dim->se ? NULL	 : dim->color;
   1804	fmt->entry	= dim->se ? c2c_se_entry : dim->entry;
   1805	fmt->header	= c2c_header;
   1806	fmt->width	= c2c_width;
   1807	fmt->collapse	= dim->se ? c2c_se_collapse : dim->cmp;
   1808	fmt->equal	= fmt_equal;
   1809	fmt->free	= fmt_free;
   1810
   1811	return c2c_fmt;
   1812}
   1813
   1814static int c2c_hists__init_output(struct perf_hpp_list *hpp_list, char *name)
   1815{
   1816	struct c2c_fmt *c2c_fmt = get_format(name);
   1817
   1818	if (!c2c_fmt) {
   1819		reset_dimensions();
   1820		return output_field_add(hpp_list, name);
   1821	}
   1822
   1823	perf_hpp_list__column_register(hpp_list, &c2c_fmt->fmt);
   1824	return 0;
   1825}
   1826
   1827static int c2c_hists__init_sort(struct perf_hpp_list *hpp_list, char *name)
   1828{
   1829	struct c2c_fmt *c2c_fmt = get_format(name);
   1830	struct c2c_dimension *dim;
   1831
   1832	if (!c2c_fmt) {
   1833		reset_dimensions();
   1834		return sort_dimension__add(hpp_list, name, NULL, 0);
   1835	}
   1836
   1837	dim = c2c_fmt->dim;
   1838	if (dim == &dim_dso)
   1839		hpp_list->dso = 1;
   1840
   1841	perf_hpp_list__register_sort_field(hpp_list, &c2c_fmt->fmt);
   1842	return 0;
   1843}
   1844
   1845#define PARSE_LIST(_list, _fn)							\
   1846	do {									\
   1847		char *tmp, *tok;						\
   1848		ret = 0;							\
   1849										\
   1850		if (!_list)							\
   1851			break;							\
   1852										\
   1853		for (tok = strtok_r((char *)_list, ", ", &tmp);			\
   1854				tok; tok = strtok_r(NULL, ", ", &tmp)) {	\
   1855			ret = _fn(hpp_list, tok);				\
   1856			if (ret == -EINVAL) {					\
   1857				pr_err("Invalid --fields key: `%s'", tok);	\
   1858				break;						\
   1859			} else if (ret == -ESRCH) {				\
   1860				pr_err("Unknown --fields key: `%s'", tok);	\
   1861				break;						\
   1862			}							\
   1863		}								\
   1864	} while (0)
   1865
   1866static int hpp_list__parse(struct perf_hpp_list *hpp_list,
   1867			   const char *output_,
   1868			   const char *sort_)
   1869{
   1870	char *output = output_ ? strdup(output_) : NULL;
   1871	char *sort   = sort_   ? strdup(sort_) : NULL;
   1872	int ret;
   1873
   1874	PARSE_LIST(output, c2c_hists__init_output);
   1875	PARSE_LIST(sort,   c2c_hists__init_sort);
   1876
   1877	/* copy sort keys to output fields */
   1878	perf_hpp__setup_output_field(hpp_list);
   1879
   1880	/*
   1881	 * We dont need other sorting keys other than those
   1882	 * we already specified. It also really slows down
   1883	 * the processing a lot with big number of output
   1884	 * fields, so switching this off for c2c.
   1885	 */
   1886
   1887#if 0
   1888	/* and then copy output fields to sort keys */
   1889	perf_hpp__append_sort_keys(&hists->list);
   1890#endif
   1891
   1892	free(output);
   1893	free(sort);
   1894	return ret;
   1895}
   1896
   1897static int c2c_hists__init(struct c2c_hists *hists,
   1898			   const char *sort,
   1899			   int nr_header_lines)
   1900{
   1901	__hists__init(&hists->hists, &hists->list);
   1902
   1903	/*
   1904	 * Initialize only with sort fields, we need to resort
   1905	 * later anyway, and that's where we add output fields
   1906	 * as well.
   1907	 */
   1908	perf_hpp_list__init(&hists->list);
   1909
   1910	/* Overload number of header lines.*/
   1911	hists->list.nr_header_lines = nr_header_lines;
   1912
   1913	return hpp_list__parse(&hists->list, NULL, sort);
   1914}
   1915
   1916static int c2c_hists__reinit(struct c2c_hists *c2c_hists,
   1917			     const char *output,
   1918			     const char *sort)
   1919{
   1920	perf_hpp__reset_output_field(&c2c_hists->list);
   1921	return hpp_list__parse(&c2c_hists->list, output, sort);
   1922}
   1923
   1924#define DISPLAY_LINE_LIMIT  0.001
   1925
   1926static u8 filter_display(u32 val, u32 sum)
   1927{
   1928	if (sum == 0 || ((double)val / sum) < DISPLAY_LINE_LIMIT)
   1929		return HIST_FILTER__C2C;
   1930
   1931	return 0;
   1932}
   1933
   1934static bool he__display(struct hist_entry *he, struct c2c_stats *stats)
   1935{
   1936	struct c2c_hist_entry *c2c_he;
   1937
   1938	if (c2c.show_all)
   1939		return true;
   1940
   1941	c2c_he = container_of(he, struct c2c_hist_entry, he);
   1942
   1943	switch (c2c.display) {
   1944	case DISPLAY_LCL:
   1945		he->filtered = filter_display(c2c_he->stats.lcl_hitm,
   1946					      stats->lcl_hitm);
   1947		break;
   1948	case DISPLAY_RMT:
   1949		he->filtered = filter_display(c2c_he->stats.rmt_hitm,
   1950					      stats->rmt_hitm);
   1951		break;
   1952	case DISPLAY_TOT:
   1953		he->filtered = filter_display(c2c_he->stats.tot_hitm,
   1954					      stats->tot_hitm);
   1955		break;
   1956	default:
   1957		break;
   1958	}
   1959
   1960	return he->filtered == 0;
   1961}
   1962
   1963static inline bool is_valid_hist_entry(struct hist_entry *he)
   1964{
   1965	struct c2c_hist_entry *c2c_he;
   1966	bool has_record = false;
   1967
   1968	c2c_he = container_of(he, struct c2c_hist_entry, he);
   1969
   1970	/* It's a valid entry if contains stores */
   1971	if (c2c_he->stats.store)
   1972		return true;
   1973
   1974	switch (c2c.display) {
   1975	case DISPLAY_LCL:
   1976		has_record = !!c2c_he->stats.lcl_hitm;
   1977		break;
   1978	case DISPLAY_RMT:
   1979		has_record = !!c2c_he->stats.rmt_hitm;
   1980		break;
   1981	case DISPLAY_TOT:
   1982		has_record = !!c2c_he->stats.tot_hitm;
   1983		break;
   1984	default:
   1985		break;
   1986	}
   1987
   1988	return has_record;
   1989}
   1990
   1991static void set_node_width(struct c2c_hist_entry *c2c_he, int len)
   1992{
   1993	struct c2c_dimension *dim;
   1994
   1995	dim = &c2c.hists == c2c_he->hists ?
   1996	      &dim_dcacheline_node : &dim_offset_node;
   1997
   1998	if (len > dim->width)
   1999		dim->width = len;
   2000}
   2001
   2002static int set_nodestr(struct c2c_hist_entry *c2c_he)
   2003{
   2004	char buf[30];
   2005	int len;
   2006
   2007	if (c2c_he->nodestr)
   2008		return 0;
   2009
   2010	if (!bitmap_empty(c2c_he->nodeset, c2c.nodes_cnt)) {
   2011		len = bitmap_scnprintf(c2c_he->nodeset, c2c.nodes_cnt,
   2012				      buf, sizeof(buf));
   2013	} else {
   2014		len = scnprintf(buf, sizeof(buf), "N/A");
   2015	}
   2016
   2017	set_node_width(c2c_he, len);
   2018	c2c_he->nodestr = strdup(buf);
   2019	return c2c_he->nodestr ? 0 : -ENOMEM;
   2020}
   2021
   2022static void calc_width(struct c2c_hist_entry *c2c_he)
   2023{
   2024	struct c2c_hists *c2c_hists;
   2025
   2026	c2c_hists = container_of(c2c_he->he.hists, struct c2c_hists, hists);
   2027	hists__calc_col_len(&c2c_hists->hists, &c2c_he->he);
   2028	set_nodestr(c2c_he);
   2029}
   2030
   2031static int filter_cb(struct hist_entry *he, void *arg __maybe_unused)
   2032{
   2033	struct c2c_hist_entry *c2c_he;
   2034
   2035	c2c_he = container_of(he, struct c2c_hist_entry, he);
   2036
   2037	if (c2c.show_src && !he->srcline)
   2038		he->srcline = hist_entry__srcline(he);
   2039
   2040	calc_width(c2c_he);
   2041
   2042	if (!is_valid_hist_entry(he))
   2043		he->filtered = HIST_FILTER__C2C;
   2044
   2045	return 0;
   2046}
   2047
   2048static int resort_cl_cb(struct hist_entry *he, void *arg __maybe_unused)
   2049{
   2050	struct c2c_hist_entry *c2c_he;
   2051	struct c2c_hists *c2c_hists;
   2052	bool display = he__display(he, &c2c.shared_clines_stats);
   2053
   2054	c2c_he = container_of(he, struct c2c_hist_entry, he);
   2055	c2c_hists = c2c_he->hists;
   2056
   2057	if (display && c2c_hists) {
   2058		static unsigned int idx;
   2059
   2060		c2c_he->cacheline_idx = idx++;
   2061		calc_width(c2c_he);
   2062
   2063		c2c_hists__reinit(c2c_hists, c2c.cl_output, c2c.cl_resort);
   2064
   2065		hists__collapse_resort(&c2c_hists->hists, NULL);
   2066		hists__output_resort_cb(&c2c_hists->hists, NULL, filter_cb);
   2067	}
   2068
   2069	return 0;
   2070}
   2071
   2072static void setup_nodes_header(void)
   2073{
   2074	dim_node.header = header_node[c2c.node_info];
   2075}
   2076
   2077static int setup_nodes(struct perf_session *session)
   2078{
   2079	struct numa_node *n;
   2080	unsigned long **nodes;
   2081	int node, idx;
   2082	struct perf_cpu cpu;
   2083	int *cpu2node;
   2084
   2085	if (c2c.node_info > 2)
   2086		c2c.node_info = 2;
   2087
   2088	c2c.nodes_cnt = session->header.env.nr_numa_nodes;
   2089	c2c.cpus_cnt  = session->header.env.nr_cpus_avail;
   2090
   2091	n = session->header.env.numa_nodes;
   2092	if (!n)
   2093		return -EINVAL;
   2094
   2095	nodes = zalloc(sizeof(unsigned long *) * c2c.nodes_cnt);
   2096	if (!nodes)
   2097		return -ENOMEM;
   2098
   2099	c2c.nodes = nodes;
   2100
   2101	cpu2node = zalloc(sizeof(int) * c2c.cpus_cnt);
   2102	if (!cpu2node)
   2103		return -ENOMEM;
   2104
   2105	for (idx = 0; idx < c2c.cpus_cnt; idx++)
   2106		cpu2node[idx] = -1;
   2107
   2108	c2c.cpu2node = cpu2node;
   2109
   2110	for (node = 0; node < c2c.nodes_cnt; node++) {
   2111		struct perf_cpu_map *map = n[node].map;
   2112		unsigned long *set;
   2113
   2114		set = bitmap_zalloc(c2c.cpus_cnt);
   2115		if (!set)
   2116			return -ENOMEM;
   2117
   2118		nodes[node] = set;
   2119
   2120		/* empty node, skip */
   2121		if (perf_cpu_map__empty(map))
   2122			continue;
   2123
   2124		perf_cpu_map__for_each_cpu(cpu, idx, map) {
   2125			set_bit(cpu.cpu, set);
   2126
   2127			if (WARN_ONCE(cpu2node[cpu.cpu] != -1, "node/cpu topology bug"))
   2128				return -EINVAL;
   2129
   2130			cpu2node[cpu.cpu] = node;
   2131		}
   2132	}
   2133
   2134	setup_nodes_header();
   2135	return 0;
   2136}
   2137
   2138#define HAS_HITMS(__h) ((__h)->stats.lcl_hitm || (__h)->stats.rmt_hitm)
   2139
   2140static int resort_shared_cl_cb(struct hist_entry *he, void *arg __maybe_unused)
   2141{
   2142	struct c2c_hist_entry *c2c_he;
   2143	c2c_he = container_of(he, struct c2c_hist_entry, he);
   2144
   2145	if (HAS_HITMS(c2c_he)) {
   2146		c2c.shared_clines++;
   2147		c2c_add_stats(&c2c.shared_clines_stats, &c2c_he->stats);
   2148	}
   2149
   2150	return 0;
   2151}
   2152
   2153static int hists__iterate_cb(struct hists *hists, hists__resort_cb_t cb)
   2154{
   2155	struct rb_node *next = rb_first_cached(&hists->entries);
   2156	int ret = 0;
   2157
   2158	while (next) {
   2159		struct hist_entry *he;
   2160
   2161		he = rb_entry(next, struct hist_entry, rb_node);
   2162		ret = cb(he, NULL);
   2163		if (ret)
   2164			break;
   2165		next = rb_next(&he->rb_node);
   2166	}
   2167
   2168	return ret;
   2169}
   2170
   2171static void print_c2c__display_stats(FILE *out)
   2172{
   2173	int llc_misses;
   2174	struct c2c_stats *stats = &c2c.hists.stats;
   2175
   2176	llc_misses = stats->lcl_dram +
   2177		     stats->rmt_dram +
   2178		     stats->rmt_hit +
   2179		     stats->rmt_hitm;
   2180
   2181	fprintf(out, "=================================================\n");
   2182	fprintf(out, "            Trace Event Information              \n");
   2183	fprintf(out, "=================================================\n");
   2184	fprintf(out, "  Total records                     : %10d\n", stats->nr_entries);
   2185	fprintf(out, "  Locked Load/Store Operations      : %10d\n", stats->locks);
   2186	fprintf(out, "  Load Operations                   : %10d\n", stats->load);
   2187	fprintf(out, "  Loads - uncacheable               : %10d\n", stats->ld_uncache);
   2188	fprintf(out, "  Loads - IO                        : %10d\n", stats->ld_io);
   2189	fprintf(out, "  Loads - Miss                      : %10d\n", stats->ld_miss);
   2190	fprintf(out, "  Loads - no mapping                : %10d\n", stats->ld_noadrs);
   2191	fprintf(out, "  Load Fill Buffer Hit              : %10d\n", stats->ld_fbhit);
   2192	fprintf(out, "  Load L1D hit                      : %10d\n", stats->ld_l1hit);
   2193	fprintf(out, "  Load L2D hit                      : %10d\n", stats->ld_l2hit);
   2194	fprintf(out, "  Load LLC hit                      : %10d\n", stats->ld_llchit + stats->lcl_hitm);
   2195	fprintf(out, "  Load Local HITM                   : %10d\n", stats->lcl_hitm);
   2196	fprintf(out, "  Load Remote HITM                  : %10d\n", stats->rmt_hitm);
   2197	fprintf(out, "  Load Remote HIT                   : %10d\n", stats->rmt_hit);
   2198	fprintf(out, "  Load Local DRAM                   : %10d\n", stats->lcl_dram);
   2199	fprintf(out, "  Load Remote DRAM                  : %10d\n", stats->rmt_dram);
   2200	fprintf(out, "  Load MESI State Exclusive         : %10d\n", stats->ld_excl);
   2201	fprintf(out, "  Load MESI State Shared            : %10d\n", stats->ld_shared);
   2202	fprintf(out, "  Load LLC Misses                   : %10d\n", llc_misses);
   2203	fprintf(out, "  Load access blocked by data       : %10d\n", stats->blk_data);
   2204	fprintf(out, "  Load access blocked by address    : %10d\n", stats->blk_addr);
   2205	fprintf(out, "  LLC Misses to Local DRAM          : %10.1f%%\n", ((double)stats->lcl_dram/(double)llc_misses) * 100.);
   2206	fprintf(out, "  LLC Misses to Remote DRAM         : %10.1f%%\n", ((double)stats->rmt_dram/(double)llc_misses) * 100.);
   2207	fprintf(out, "  LLC Misses to Remote cache (HIT)  : %10.1f%%\n", ((double)stats->rmt_hit /(double)llc_misses) * 100.);
   2208	fprintf(out, "  LLC Misses to Remote cache (HITM) : %10.1f%%\n", ((double)stats->rmt_hitm/(double)llc_misses) * 100.);
   2209	fprintf(out, "  Store Operations                  : %10d\n", stats->store);
   2210	fprintf(out, "  Store - uncacheable               : %10d\n", stats->st_uncache);
   2211	fprintf(out, "  Store - no mapping                : %10d\n", stats->st_noadrs);
   2212	fprintf(out, "  Store L1D Hit                     : %10d\n", stats->st_l1hit);
   2213	fprintf(out, "  Store L1D Miss                    : %10d\n", stats->st_l1miss);
   2214	fprintf(out, "  Store No available memory level   : %10d\n", stats->st_na);
   2215	fprintf(out, "  No Page Map Rejects               : %10d\n", stats->nomap);
   2216	fprintf(out, "  Unable to parse data source       : %10d\n", stats->noparse);
   2217}
   2218
   2219static void print_shared_cacheline_info(FILE *out)
   2220{
   2221	struct c2c_stats *stats = &c2c.shared_clines_stats;
   2222	int hitm_cnt = stats->lcl_hitm + stats->rmt_hitm;
   2223
   2224	fprintf(out, "=================================================\n");
   2225	fprintf(out, "    Global Shared Cache Line Event Information   \n");
   2226	fprintf(out, "=================================================\n");
   2227	fprintf(out, "  Total Shared Cache Lines          : %10d\n", c2c.shared_clines);
   2228	fprintf(out, "  Load HITs on shared lines         : %10d\n", stats->load);
   2229	fprintf(out, "  Fill Buffer Hits on shared lines  : %10d\n", stats->ld_fbhit);
   2230	fprintf(out, "  L1D hits on shared lines          : %10d\n", stats->ld_l1hit);
   2231	fprintf(out, "  L2D hits on shared lines          : %10d\n", stats->ld_l2hit);
   2232	fprintf(out, "  LLC hits on shared lines          : %10d\n", stats->ld_llchit + stats->lcl_hitm);
   2233	fprintf(out, "  Locked Access on shared lines     : %10d\n", stats->locks);
   2234	fprintf(out, "  Blocked Access on shared lines    : %10d\n", stats->blk_data + stats->blk_addr);
   2235	fprintf(out, "  Store HITs on shared lines        : %10d\n", stats->store);
   2236	fprintf(out, "  Store L1D hits on shared lines    : %10d\n", stats->st_l1hit);
   2237	fprintf(out, "  Store No available memory level   : %10d\n", stats->st_na);
   2238	fprintf(out, "  Total Merged records              : %10d\n", hitm_cnt + stats->store);
   2239}
   2240
   2241static void print_cacheline(struct c2c_hists *c2c_hists,
   2242			    struct hist_entry *he_cl,
   2243			    struct perf_hpp_list *hpp_list,
   2244			    FILE *out)
   2245{
   2246	char bf[1000];
   2247	struct perf_hpp hpp = {
   2248		.buf            = bf,
   2249		.size           = 1000,
   2250	};
   2251	static bool once;
   2252
   2253	if (!once) {
   2254		hists__fprintf_headers(&c2c_hists->hists, out);
   2255		once = true;
   2256	} else {
   2257		fprintf(out, "\n");
   2258	}
   2259
   2260	fprintf(out, "  ----------------------------------------------------------------------\n");
   2261	__hist_entry__snprintf(he_cl, &hpp, hpp_list);
   2262	fprintf(out, "%s\n", bf);
   2263	fprintf(out, "  ----------------------------------------------------------------------\n");
   2264
   2265	hists__fprintf(&c2c_hists->hists, false, 0, 0, 0, out, false);
   2266}
   2267
   2268static void print_pareto(FILE *out)
   2269{
   2270	struct perf_hpp_list hpp_list;
   2271	struct rb_node *nd;
   2272	int ret;
   2273	const char *cl_output;
   2274
   2275	cl_output = "cl_num,"
   2276		    "cl_rmt_hitm,"
   2277		    "cl_lcl_hitm,"
   2278		    "cl_stores_l1hit,"
   2279		    "cl_stores_l1miss,"
   2280		    "cl_stores_na,"
   2281		    "dcacheline";
   2282
   2283	perf_hpp_list__init(&hpp_list);
   2284	ret = hpp_list__parse(&hpp_list, cl_output, NULL);
   2285
   2286	if (WARN_ONCE(ret, "failed to setup sort entries\n"))
   2287		return;
   2288
   2289	nd = rb_first_cached(&c2c.hists.hists.entries);
   2290
   2291	for (; nd; nd = rb_next(nd)) {
   2292		struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
   2293		struct c2c_hist_entry *c2c_he;
   2294
   2295		if (he->filtered)
   2296			continue;
   2297
   2298		c2c_he = container_of(he, struct c2c_hist_entry, he);
   2299		print_cacheline(c2c_he->hists, he, &hpp_list, out);
   2300	}
   2301}
   2302
   2303static void print_c2c_info(FILE *out, struct perf_session *session)
   2304{
   2305	struct evlist *evlist = session->evlist;
   2306	struct evsel *evsel;
   2307	bool first = true;
   2308
   2309	fprintf(out, "=================================================\n");
   2310	fprintf(out, "                 c2c details                     \n");
   2311	fprintf(out, "=================================================\n");
   2312
   2313	evlist__for_each_entry(evlist, evsel) {
   2314		fprintf(out, "%-36s: %s\n", first ? "  Events" : "", evsel__name(evsel));
   2315		first = false;
   2316	}
   2317	fprintf(out, "  Cachelines sort on                : %s HITMs\n",
   2318		display_str[c2c.display]);
   2319	fprintf(out, "  Cacheline data grouping           : %s\n", c2c.cl_sort);
   2320}
   2321
   2322static void perf_c2c__hists_fprintf(FILE *out, struct perf_session *session)
   2323{
   2324	setup_pager();
   2325
   2326	print_c2c__display_stats(out);
   2327	fprintf(out, "\n");
   2328	print_shared_cacheline_info(out);
   2329	fprintf(out, "\n");
   2330	print_c2c_info(out, session);
   2331
   2332	if (c2c.stats_only)
   2333		return;
   2334
   2335	fprintf(out, "\n");
   2336	fprintf(out, "=================================================\n");
   2337	fprintf(out, "           Shared Data Cache Line Table          \n");
   2338	fprintf(out, "=================================================\n");
   2339	fprintf(out, "#\n");
   2340
   2341	hists__fprintf(&c2c.hists.hists, true, 0, 0, 0, stdout, true);
   2342
   2343	fprintf(out, "\n");
   2344	fprintf(out, "=================================================\n");
   2345	fprintf(out, "      Shared Cache Line Distribution Pareto      \n");
   2346	fprintf(out, "=================================================\n");
   2347	fprintf(out, "#\n");
   2348
   2349	print_pareto(out);
   2350}
   2351
   2352#ifdef HAVE_SLANG_SUPPORT
   2353static void c2c_browser__update_nr_entries(struct hist_browser *hb)
   2354{
   2355	u64 nr_entries = 0;
   2356	struct rb_node *nd = rb_first_cached(&hb->hists->entries);
   2357
   2358	while (nd) {
   2359		struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
   2360
   2361		if (!he->filtered)
   2362			nr_entries++;
   2363
   2364		nd = rb_next(nd);
   2365	}
   2366
   2367	hb->nr_non_filtered_entries = nr_entries;
   2368}
   2369
   2370struct c2c_cacheline_browser {
   2371	struct hist_browser	 hb;
   2372	struct hist_entry	*he;
   2373};
   2374
   2375static int
   2376perf_c2c_cacheline_browser__title(struct hist_browser *browser,
   2377				  char *bf, size_t size)
   2378{
   2379	struct c2c_cacheline_browser *cl_browser;
   2380	struct hist_entry *he;
   2381	uint64_t addr = 0;
   2382
   2383	cl_browser = container_of(browser, struct c2c_cacheline_browser, hb);
   2384	he = cl_browser->he;
   2385
   2386	if (he->mem_info)
   2387		addr = cl_address(he->mem_info->daddr.addr);
   2388
   2389	scnprintf(bf, size, "Cacheline 0x%lx", addr);
   2390	return 0;
   2391}
   2392
   2393static struct c2c_cacheline_browser*
   2394c2c_cacheline_browser__new(struct hists *hists, struct hist_entry *he)
   2395{
   2396	struct c2c_cacheline_browser *browser;
   2397
   2398	browser = zalloc(sizeof(*browser));
   2399	if (browser) {
   2400		hist_browser__init(&browser->hb, hists);
   2401		browser->hb.c2c_filter	= true;
   2402		browser->hb.title	= perf_c2c_cacheline_browser__title;
   2403		browser->he		= he;
   2404	}
   2405
   2406	return browser;
   2407}
   2408
   2409static int perf_c2c__browse_cacheline(struct hist_entry *he)
   2410{
   2411	struct c2c_hist_entry *c2c_he;
   2412	struct c2c_hists *c2c_hists;
   2413	struct c2c_cacheline_browser *cl_browser;
   2414	struct hist_browser *browser;
   2415	int key = -1;
   2416	static const char help[] =
   2417	" ENTER         Toggle callchains (if present) \n"
   2418	" n             Toggle Node details info \n"
   2419	" s             Toggle full length of symbol and source line columns \n"
   2420	" q             Return back to cacheline list \n";
   2421
   2422	if (!he)
   2423		return 0;
   2424
   2425	/* Display compact version first. */
   2426	c2c.symbol_full = false;
   2427
   2428	c2c_he = container_of(he, struct c2c_hist_entry, he);
   2429	c2c_hists = c2c_he->hists;
   2430
   2431	cl_browser = c2c_cacheline_browser__new(&c2c_hists->hists, he);
   2432	if (cl_browser == NULL)
   2433		return -1;
   2434
   2435	browser = &cl_browser->hb;
   2436
   2437	/* reset abort key so that it can get Ctrl-C as a key */
   2438	SLang_reset_tty();
   2439	SLang_init_tty(0, 0, 0);
   2440
   2441	c2c_browser__update_nr_entries(browser);
   2442
   2443	while (1) {
   2444		key = hist_browser__run(browser, "? - help", true, 0);
   2445
   2446		switch (key) {
   2447		case 's':
   2448			c2c.symbol_full = !c2c.symbol_full;
   2449			break;
   2450		case 'n':
   2451			c2c.node_info = (c2c.node_info + 1) % 3;
   2452			setup_nodes_header();
   2453			break;
   2454		case 'q':
   2455			goto out;
   2456		case '?':
   2457			ui_browser__help_window(&browser->b, help);
   2458			break;
   2459		default:
   2460			break;
   2461		}
   2462	}
   2463
   2464out:
   2465	free(cl_browser);
   2466	return 0;
   2467}
   2468
   2469static int perf_c2c_browser__title(struct hist_browser *browser,
   2470				   char *bf, size_t size)
   2471{
   2472	scnprintf(bf, size,
   2473		  "Shared Data Cache Line Table     "
   2474		  "(%lu entries, sorted on %s HITMs)",
   2475		  browser->nr_non_filtered_entries,
   2476		  display_str[c2c.display]);
   2477	return 0;
   2478}
   2479
   2480static struct hist_browser*
   2481perf_c2c_browser__new(struct hists *hists)
   2482{
   2483	struct hist_browser *browser = hist_browser__new(hists);
   2484
   2485	if (browser) {
   2486		browser->title = perf_c2c_browser__title;
   2487		browser->c2c_filter = true;
   2488	}
   2489
   2490	return browser;
   2491}
   2492
   2493static int perf_c2c__hists_browse(struct hists *hists)
   2494{
   2495	struct hist_browser *browser;
   2496	int key = -1;
   2497	static const char help[] =
   2498	" d             Display cacheline details \n"
   2499	" ENTER         Toggle callchains (if present) \n"
   2500	" q             Quit \n";
   2501
   2502	browser = perf_c2c_browser__new(hists);
   2503	if (browser == NULL)
   2504		return -1;
   2505
   2506	/* reset abort key so that it can get Ctrl-C as a key */
   2507	SLang_reset_tty();
   2508	SLang_init_tty(0, 0, 0);
   2509
   2510	c2c_browser__update_nr_entries(browser);
   2511
   2512	while (1) {
   2513		key = hist_browser__run(browser, "? - help", true, 0);
   2514
   2515		switch (key) {
   2516		case 'q':
   2517			goto out;
   2518		case 'd':
   2519			perf_c2c__browse_cacheline(browser->he_selection);
   2520			break;
   2521		case '?':
   2522			ui_browser__help_window(&browser->b, help);
   2523			break;
   2524		default:
   2525			break;
   2526		}
   2527	}
   2528
   2529out:
   2530	hist_browser__delete(browser);
   2531	return 0;
   2532}
   2533
   2534static void perf_c2c_display(struct perf_session *session)
   2535{
   2536	if (use_browser == 0)
   2537		perf_c2c__hists_fprintf(stdout, session);
   2538	else
   2539		perf_c2c__hists_browse(&c2c.hists.hists);
   2540}
   2541#else
   2542static void perf_c2c_display(struct perf_session *session)
   2543{
   2544	use_browser = 0;
   2545	perf_c2c__hists_fprintf(stdout, session);
   2546}
   2547#endif /* HAVE_SLANG_SUPPORT */
   2548
   2549static char *fill_line(const char *orig, int len)
   2550{
   2551	int i, j, olen = strlen(orig);
   2552	char *buf;
   2553
   2554	buf = zalloc(len + 1);
   2555	if (!buf)
   2556		return NULL;
   2557
   2558	j = len / 2 - olen / 2;
   2559
   2560	for (i = 0; i < j - 1; i++)
   2561		buf[i] = '-';
   2562
   2563	buf[i++] = ' ';
   2564
   2565	strcpy(buf + i, orig);
   2566
   2567	i += olen;
   2568
   2569	buf[i++] = ' ';
   2570
   2571	for (; i < len; i++)
   2572		buf[i] = '-';
   2573
   2574	return buf;
   2575}
   2576
   2577static int ui_quirks(void)
   2578{
   2579	const char *nodestr = "Data address";
   2580	char *buf;
   2581
   2582	if (!c2c.use_stdio) {
   2583		dim_offset.width  = 5;
   2584		dim_offset.header = header_offset_tui;
   2585		nodestr = "CL";
   2586	}
   2587
   2588	dim_percent_hitm.header = percent_hitm_header[c2c.display];
   2589
   2590	/* Fix the zero line for dcacheline column. */
   2591	buf = fill_line("Cacheline", dim_dcacheline.width +
   2592				     dim_dcacheline_node.width +
   2593				     dim_dcacheline_count.width + 4);
   2594	if (!buf)
   2595		return -ENOMEM;
   2596
   2597	dim_dcacheline.header.line[0].text = buf;
   2598
   2599	/* Fix the zero line for offset column. */
   2600	buf = fill_line(nodestr, dim_offset.width +
   2601			         dim_offset_node.width +
   2602				 dim_dcacheline_count.width + 4);
   2603	if (!buf)
   2604		return -ENOMEM;
   2605
   2606	dim_offset.header.line[0].text = buf;
   2607
   2608	return 0;
   2609}
   2610
   2611#define CALLCHAIN_DEFAULT_OPT  "graph,0.5,caller,function,percent"
   2612
   2613const char callchain_help[] = "Display call graph (stack chain/backtrace):\n\n"
   2614				CALLCHAIN_REPORT_HELP
   2615				"\n\t\t\t\tDefault: " CALLCHAIN_DEFAULT_OPT;
   2616
   2617static int
   2618parse_callchain_opt(const struct option *opt, const char *arg, int unset)
   2619{
   2620	struct callchain_param *callchain = opt->value;
   2621
   2622	callchain->enabled = !unset;
   2623	/*
   2624	 * --no-call-graph
   2625	 */
   2626	if (unset) {
   2627		symbol_conf.use_callchain = false;
   2628		callchain->mode = CHAIN_NONE;
   2629		return 0;
   2630	}
   2631
   2632	return parse_callchain_report_opt(arg);
   2633}
   2634
   2635static int setup_callchain(struct evlist *evlist)
   2636{
   2637	u64 sample_type = evlist__combined_sample_type(evlist);
   2638	enum perf_call_graph_mode mode = CALLCHAIN_NONE;
   2639
   2640	if ((sample_type & PERF_SAMPLE_REGS_USER) &&
   2641	    (sample_type & PERF_SAMPLE_STACK_USER)) {
   2642		mode = CALLCHAIN_DWARF;
   2643		dwarf_callchain_users = true;
   2644	} else if (sample_type & PERF_SAMPLE_BRANCH_STACK)
   2645		mode = CALLCHAIN_LBR;
   2646	else if (sample_type & PERF_SAMPLE_CALLCHAIN)
   2647		mode = CALLCHAIN_FP;
   2648
   2649	if (!callchain_param.enabled &&
   2650	    callchain_param.mode != CHAIN_NONE &&
   2651	    mode != CALLCHAIN_NONE) {
   2652		symbol_conf.use_callchain = true;
   2653		if (callchain_register_param(&callchain_param) < 0) {
   2654			ui__error("Can't register callchain params.\n");
   2655			return -EINVAL;
   2656		}
   2657	}
   2658
   2659	if (c2c.stitch_lbr && (mode != CALLCHAIN_LBR)) {
   2660		ui__warning("Can't find LBR callchain. Switch off --stitch-lbr.\n"
   2661			    "Please apply --call-graph lbr when recording.\n");
   2662		c2c.stitch_lbr = false;
   2663	}
   2664
   2665	callchain_param.record_mode = mode;
   2666	callchain_param.min_percent = 0;
   2667	return 0;
   2668}
   2669
   2670static int setup_display(const char *str)
   2671{
   2672	const char *display = str ?: "tot";
   2673
   2674	if (!strcmp(display, "tot"))
   2675		c2c.display = DISPLAY_TOT;
   2676	else if (!strcmp(display, "rmt"))
   2677		c2c.display = DISPLAY_RMT;
   2678	else if (!strcmp(display, "lcl"))
   2679		c2c.display = DISPLAY_LCL;
   2680	else {
   2681		pr_err("failed: unknown display type: %s\n", str);
   2682		return -1;
   2683	}
   2684
   2685	return 0;
   2686}
   2687
   2688#define for_each_token(__tok, __buf, __sep, __tmp)		\
   2689	for (__tok = strtok_r(__buf, __sep, &__tmp); __tok;	\
   2690	     __tok = strtok_r(NULL,  __sep, &__tmp))
   2691
   2692static int build_cl_output(char *cl_sort, bool no_source)
   2693{
   2694	char *tok, *tmp, *buf = strdup(cl_sort);
   2695	bool add_pid   = false;
   2696	bool add_tid   = false;
   2697	bool add_iaddr = false;
   2698	bool add_sym   = false;
   2699	bool add_dso   = false;
   2700	bool add_src   = false;
   2701	int ret = 0;
   2702
   2703	if (!buf)
   2704		return -ENOMEM;
   2705
   2706	for_each_token(tok, buf, ",", tmp) {
   2707		if (!strcmp(tok, "tid")) {
   2708			add_tid = true;
   2709		} else if (!strcmp(tok, "pid")) {
   2710			add_pid = true;
   2711		} else if (!strcmp(tok, "iaddr")) {
   2712			add_iaddr = true;
   2713			add_sym   = true;
   2714			add_dso   = true;
   2715			add_src   = no_source ? false : true;
   2716		} else if (!strcmp(tok, "dso")) {
   2717			add_dso = true;
   2718		} else if (strcmp(tok, "offset")) {
   2719			pr_err("unrecognized sort token: %s\n", tok);
   2720			ret = -EINVAL;
   2721			goto err;
   2722		}
   2723	}
   2724
   2725	if (asprintf(&c2c.cl_output,
   2726		"%s%s%s%s%s%s%s%s%s%s",
   2727		c2c.use_stdio ? "cl_num_empty," : "",
   2728		"percent_rmt_hitm,"
   2729		"percent_lcl_hitm,"
   2730		"percent_stores_l1hit,"
   2731		"percent_stores_l1miss,"
   2732		"percent_stores_na,"
   2733		"offset,offset_node,dcacheline_count,",
   2734		add_pid   ? "pid," : "",
   2735		add_tid   ? "tid," : "",
   2736		add_iaddr ? "iaddr," : "",
   2737		"mean_rmt,"
   2738		"mean_lcl,"
   2739		"mean_load,"
   2740		"tot_recs,"
   2741		"cpucnt,",
   2742		add_sym ? "symbol," : "",
   2743		add_dso ? "dso," : "",
   2744		add_src ? "cl_srcline," : "",
   2745		"node") < 0) {
   2746		ret = -ENOMEM;
   2747		goto err;
   2748	}
   2749
   2750	c2c.show_src = add_src;
   2751err:
   2752	free(buf);
   2753	return ret;
   2754}
   2755
   2756static int setup_coalesce(const char *coalesce, bool no_source)
   2757{
   2758	const char *c = coalesce ?: coalesce_default;
   2759
   2760	if (asprintf(&c2c.cl_sort, "offset,%s", c) < 0)
   2761		return -ENOMEM;
   2762
   2763	if (build_cl_output(c2c.cl_sort, no_source))
   2764		return -1;
   2765
   2766	if (asprintf(&c2c.cl_resort, "offset,%s",
   2767		     c2c.display == DISPLAY_TOT ?
   2768		     "tot_hitm" :
   2769		     c2c.display == DISPLAY_RMT ?
   2770		     "rmt_hitm,lcl_hitm" :
   2771		     "lcl_hitm,rmt_hitm") < 0)
   2772		return -ENOMEM;
   2773
   2774	pr_debug("coalesce sort   fields: %s\n", c2c.cl_sort);
   2775	pr_debug("coalesce resort fields: %s\n", c2c.cl_resort);
   2776	pr_debug("coalesce output fields: %s\n", c2c.cl_output);
   2777	return 0;
   2778}
   2779
   2780static int perf_c2c__report(int argc, const char **argv)
   2781{
   2782	struct itrace_synth_opts itrace_synth_opts = {
   2783		.set = true,
   2784		.mem = true,	/* Only enable memory event */
   2785		.default_no_sample = true,
   2786	};
   2787
   2788	struct perf_session *session;
   2789	struct ui_progress prog;
   2790	struct perf_data data = {
   2791		.mode = PERF_DATA_MODE_READ,
   2792	};
   2793	char callchain_default_opt[] = CALLCHAIN_DEFAULT_OPT;
   2794	const char *display = NULL;
   2795	const char *coalesce = NULL;
   2796	bool no_source = false;
   2797	const struct option options[] = {
   2798	OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
   2799		   "file", "vmlinux pathname"),
   2800	OPT_STRING('i', "input", &input_name, "file",
   2801		   "the input file to process"),
   2802	OPT_INCR('N', "node-info", &c2c.node_info,
   2803		 "show extra node info in report (repeat for more info)"),
   2804	OPT_BOOLEAN(0, "stdio", &c2c.use_stdio, "Use the stdio interface"),
   2805	OPT_BOOLEAN(0, "stats", &c2c.stats_only,
   2806		    "Display only statistic tables (implies --stdio)"),
   2807	OPT_BOOLEAN(0, "full-symbols", &c2c.symbol_full,
   2808		    "Display full length of symbols"),
   2809	OPT_BOOLEAN(0, "no-source", &no_source,
   2810		    "Do not display Source Line column"),
   2811	OPT_BOOLEAN(0, "show-all", &c2c.show_all,
   2812		    "Show all captured HITM lines."),
   2813	OPT_CALLBACK_DEFAULT('g', "call-graph", &callchain_param,
   2814			     "print_type,threshold[,print_limit],order,sort_key[,branch],value",
   2815			     callchain_help, &parse_callchain_opt,
   2816			     callchain_default_opt),
   2817	OPT_STRING('d', "display", &display, "Switch HITM output type", "lcl,rmt"),
   2818	OPT_STRING('c', "coalesce", &coalesce, "coalesce fields",
   2819		   "coalesce fields: pid,tid,iaddr,dso"),
   2820	OPT_BOOLEAN('f', "force", &symbol_conf.force, "don't complain, do it"),
   2821	OPT_BOOLEAN(0, "stitch-lbr", &c2c.stitch_lbr,
   2822		    "Enable LBR callgraph stitching approach"),
   2823	OPT_PARENT(c2c_options),
   2824	OPT_END()
   2825	};
   2826	int err = 0;
   2827	const char *output_str, *sort_str = NULL;
   2828
   2829	argc = parse_options(argc, argv, options, report_c2c_usage,
   2830			     PARSE_OPT_STOP_AT_NON_OPTION);
   2831	if (argc)
   2832		usage_with_options(report_c2c_usage, options);
   2833
   2834#ifndef HAVE_SLANG_SUPPORT
   2835	c2c.use_stdio = true;
   2836#endif
   2837
   2838	if (c2c.stats_only)
   2839		c2c.use_stdio = true;
   2840
   2841	err = symbol__validate_sym_arguments();
   2842	if (err)
   2843		goto out;
   2844
   2845	if (!input_name || !strlen(input_name))
   2846		input_name = "perf.data";
   2847
   2848	data.path  = input_name;
   2849	data.force = symbol_conf.force;
   2850
   2851	err = setup_display(display);
   2852	if (err)
   2853		goto out;
   2854
   2855	err = setup_coalesce(coalesce, no_source);
   2856	if (err) {
   2857		pr_debug("Failed to initialize hists\n");
   2858		goto out;
   2859	}
   2860
   2861	err = c2c_hists__init(&c2c.hists, "dcacheline", 2);
   2862	if (err) {
   2863		pr_debug("Failed to initialize hists\n");
   2864		goto out;
   2865	}
   2866
   2867	session = perf_session__new(&data, &c2c.tool);
   2868	if (IS_ERR(session)) {
   2869		err = PTR_ERR(session);
   2870		pr_debug("Error creating perf session\n");
   2871		goto out;
   2872	}
   2873
   2874	session->itrace_synth_opts = &itrace_synth_opts;
   2875
   2876	err = setup_nodes(session);
   2877	if (err) {
   2878		pr_err("Failed setup nodes\n");
   2879		goto out;
   2880	}
   2881
   2882	err = mem2node__init(&c2c.mem2node, &session->header.env);
   2883	if (err)
   2884		goto out_session;
   2885
   2886	err = setup_callchain(session->evlist);
   2887	if (err)
   2888		goto out_mem2node;
   2889
   2890	if (symbol__init(&session->header.env) < 0)
   2891		goto out_mem2node;
   2892
   2893	/* No pipe support at the moment. */
   2894	if (perf_data__is_pipe(session->data)) {
   2895		pr_debug("No pipe support at the moment.\n");
   2896		goto out_mem2node;
   2897	}
   2898
   2899	if (c2c.use_stdio)
   2900		use_browser = 0;
   2901	else
   2902		use_browser = 1;
   2903
   2904	setup_browser(false);
   2905
   2906	err = perf_session__process_events(session);
   2907	if (err) {
   2908		pr_err("failed to process sample\n");
   2909		goto out_mem2node;
   2910	}
   2911
   2912	output_str = "cl_idx,"
   2913		     "dcacheline,"
   2914		     "dcacheline_node,"
   2915		     "dcacheline_count,"
   2916		     "percent_hitm,"
   2917		     "tot_hitm,lcl_hitm,rmt_hitm,"
   2918		     "tot_recs,"
   2919		     "tot_loads,"
   2920		     "tot_stores,"
   2921		     "stores_l1hit,stores_l1miss,stores_na,"
   2922		     "ld_fbhit,ld_l1hit,ld_l2hit,"
   2923		     "ld_lclhit,lcl_hitm,"
   2924		     "ld_rmthit,rmt_hitm,"
   2925		     "dram_lcl,dram_rmt";
   2926
   2927	if (c2c.display == DISPLAY_TOT)
   2928		sort_str = "tot_hitm";
   2929	else if (c2c.display == DISPLAY_RMT)
   2930		sort_str = "rmt_hitm";
   2931	else if (c2c.display == DISPLAY_LCL)
   2932		sort_str = "lcl_hitm";
   2933
   2934	c2c_hists__reinit(&c2c.hists, output_str, sort_str);
   2935
   2936	ui_progress__init(&prog, c2c.hists.hists.nr_entries, "Sorting...");
   2937
   2938	hists__collapse_resort(&c2c.hists.hists, NULL);
   2939	hists__output_resort_cb(&c2c.hists.hists, &prog, resort_shared_cl_cb);
   2940	hists__iterate_cb(&c2c.hists.hists, resort_cl_cb);
   2941
   2942	ui_progress__finish();
   2943
   2944	if (ui_quirks()) {
   2945		pr_err("failed to setup UI\n");
   2946		goto out_mem2node;
   2947	}
   2948
   2949	perf_c2c_display(session);
   2950
   2951out_mem2node:
   2952	mem2node__exit(&c2c.mem2node);
   2953out_session:
   2954	perf_session__delete(session);
   2955out:
   2956	return err;
   2957}
   2958
   2959static int parse_record_events(const struct option *opt,
   2960			       const char *str, int unset __maybe_unused)
   2961{
   2962	bool *event_set = (bool *) opt->value;
   2963
   2964	if (!strcmp(str, "list")) {
   2965		perf_mem_events__list();
   2966		exit(0);
   2967	}
   2968	if (perf_mem_events__parse(str))
   2969		exit(-1);
   2970
   2971	*event_set = true;
   2972	return 0;
   2973}
   2974
   2975
   2976static const char * const __usage_record[] = {
   2977	"perf c2c record [<options>] [<command>]",
   2978	"perf c2c record [<options>] -- <command> [<options>]",
   2979	NULL
   2980};
   2981
   2982static const char * const *record_mem_usage = __usage_record;
   2983
   2984static int perf_c2c__record(int argc, const char **argv)
   2985{
   2986	int rec_argc, i = 0, j, rec_tmp_nr = 0;
   2987	const char **rec_argv;
   2988	char **rec_tmp;
   2989	int ret;
   2990	bool all_user = false, all_kernel = false;
   2991	bool event_set = false;
   2992	struct perf_mem_event *e;
   2993	struct option options[] = {
   2994	OPT_CALLBACK('e', "event", &event_set, "event",
   2995		     "event selector. Use 'perf c2c record -e list' to list available events",
   2996		     parse_record_events),
   2997	OPT_BOOLEAN('u', "all-user", &all_user, "collect only user level data"),
   2998	OPT_BOOLEAN('k', "all-kernel", &all_kernel, "collect only kernel level data"),
   2999	OPT_UINTEGER('l', "ldlat", &perf_mem_events__loads_ldlat, "setup mem-loads latency"),
   3000	OPT_PARENT(c2c_options),
   3001	OPT_END()
   3002	};
   3003
   3004	if (perf_mem_events__init()) {
   3005		pr_err("failed: memory events not supported\n");
   3006		return -1;
   3007	}
   3008
   3009	argc = parse_options(argc, argv, options, record_mem_usage,
   3010			     PARSE_OPT_KEEP_UNKNOWN);
   3011
   3012	if (!perf_pmu__has_hybrid())
   3013		rec_argc = argc + 11; /* max number of arguments */
   3014	else
   3015		rec_argc = argc + 11 * perf_pmu__hybrid_pmu_num();
   3016
   3017	rec_argv = calloc(rec_argc + 1, sizeof(char *));
   3018	if (!rec_argv)
   3019		return -1;
   3020
   3021	rec_tmp = calloc(rec_argc + 1, sizeof(char *));
   3022	if (!rec_tmp) {
   3023		free(rec_argv);
   3024		return -1;
   3025	}
   3026
   3027	rec_argv[i++] = "record";
   3028
   3029	if (!event_set) {
   3030		e = perf_mem_events__ptr(PERF_MEM_EVENTS__LOAD_STORE);
   3031		/*
   3032		 * The load and store operations are required, use the event
   3033		 * PERF_MEM_EVENTS__LOAD_STORE if it is supported.
   3034		 */
   3035		if (e->tag) {
   3036			e->record = true;
   3037		} else {
   3038			e = perf_mem_events__ptr(PERF_MEM_EVENTS__LOAD);
   3039			e->record = true;
   3040
   3041			e = perf_mem_events__ptr(PERF_MEM_EVENTS__STORE);
   3042			e->record = true;
   3043		}
   3044	}
   3045
   3046	e = perf_mem_events__ptr(PERF_MEM_EVENTS__LOAD);
   3047	if (e->record)
   3048		rec_argv[i++] = "-W";
   3049
   3050	rec_argv[i++] = "-d";
   3051	rec_argv[i++] = "--phys-data";
   3052	rec_argv[i++] = "--sample-cpu";
   3053
   3054	ret = perf_mem_events__record_args(rec_argv, &i, rec_tmp, &rec_tmp_nr);
   3055	if (ret)
   3056		goto out;
   3057
   3058	if (all_user)
   3059		rec_argv[i++] = "--all-user";
   3060
   3061	if (all_kernel)
   3062		rec_argv[i++] = "--all-kernel";
   3063
   3064	for (j = 0; j < argc; j++, i++)
   3065		rec_argv[i] = argv[j];
   3066
   3067	if (verbose > 0) {
   3068		pr_debug("calling: ");
   3069
   3070		j = 0;
   3071
   3072		while (rec_argv[j]) {
   3073			pr_debug("%s ", rec_argv[j]);
   3074			j++;
   3075		}
   3076		pr_debug("\n");
   3077	}
   3078
   3079	ret = cmd_record(i, rec_argv);
   3080out:
   3081	for (i = 0; i < rec_tmp_nr; i++)
   3082		free(rec_tmp[i]);
   3083
   3084	free(rec_tmp);
   3085	free(rec_argv);
   3086	return ret;
   3087}
   3088
   3089int cmd_c2c(int argc, const char **argv)
   3090{
   3091	argc = parse_options(argc, argv, c2c_options, c2c_usage,
   3092			     PARSE_OPT_STOP_AT_NON_OPTION);
   3093
   3094	if (!argc)
   3095		usage_with_options(c2c_usage, c2c_options);
   3096
   3097	if (strlen(argv[0]) > 2 && strstarts("record", argv[0])) {
   3098		return perf_c2c__record(argc, argv);
   3099	} else if (strlen(argv[0]) > 2 && strstarts("report", argv[0])) {
   3100		return perf_c2c__report(argc, argv);
   3101	} else {
   3102		usage_with_options(c2c_usage, c2c_options);
   3103	}
   3104
   3105	return 0;
   3106}