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