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