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