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