brintos

brintos / linux-shallow public Read only

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