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