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