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