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