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