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, idx; 2019 struct perf_cpu cpu; 2020 int *cpu2node; 2021 2022 if (c2c.node_info > 2) 2023 c2c.node_info = 2; 2024 2025 c2c.nodes_cnt = session->header.env.nr_numa_nodes; 2026 c2c.cpus_cnt = session->header.env.nr_cpus_avail; 2027 2028 n = session->header.env.numa_nodes; 2029 if (!n) 2030 return -EINVAL; 2031 2032 nodes = zalloc(sizeof(unsigned long *) * c2c.nodes_cnt); 2033 if (!nodes) 2034 return -ENOMEM; 2035 2036 c2c.nodes = nodes; 2037 2038 cpu2node = zalloc(sizeof(int) * c2c.cpus_cnt); 2039 if (!cpu2node) 2040 return -ENOMEM; 2041 2042 for (idx = 0; idx < c2c.cpus_cnt; idx++) 2043 cpu2node[idx] = -1; 2044 2045 c2c.cpu2node = cpu2node; 2046 2047 for (node = 0; node < c2c.nodes_cnt; node++) { 2048 struct perf_cpu_map *map = n[node].map; 2049 unsigned long *set; 2050 2051 set = bitmap_zalloc(c2c.cpus_cnt); 2052 if (!set) 2053 return -ENOMEM; 2054 2055 nodes[node] = set; 2056 2057 /* empty node, skip */ 2058 if (perf_cpu_map__empty(map)) 2059 continue; 2060 2061 perf_cpu_map__for_each_cpu(cpu, idx, map) { 2062 set_bit(cpu.cpu, set); 2063 2064 if (WARN_ONCE(cpu2node[cpu.cpu] != -1, "node/cpu topology bug")) 2065 return -EINVAL; 2066 2067 cpu2node[cpu.cpu] = node; 2068 } 2069 } 2070 2071 setup_nodes_header(); 2072 return 0; 2073 } 2074 2075 #define HAS_HITMS(__h) ((__h)->stats.lcl_hitm || (__h)->stats.rmt_hitm) 2076 2077 static int resort_shared_cl_cb(struct hist_entry *he, void *arg __maybe_unused) 2078 { 2079 struct c2c_hist_entry *c2c_he; 2080 c2c_he = container_of(he, struct c2c_hist_entry, he); 2081 2082 if (HAS_HITMS(c2c_he)) { 2083 c2c.shared_clines++; 2084 c2c_add_stats(&c2c.shared_clines_stats, &c2c_he->stats); 2085 } 2086 2087 return 0; 2088 } 2089 2090 static int hists__iterate_cb(struct hists *hists, hists__resort_cb_t cb) 2091 { 2092 struct rb_node *next = rb_first_cached(&hists->entries); 2093 int ret = 0; 2094 2095 while (next) { 2096 struct hist_entry *he; 2097 2098 he = rb_entry(next, struct hist_entry, rb_node); 2099 ret = cb(he, NULL); 2100 if (ret) 2101 break; 2102 next = rb_next(&he->rb_node); 2103 } 2104 2105 return ret; 2106 } 2107 2108 static void print_c2c__display_stats(FILE *out) 2109 { 2110 int llc_misses; 2111 struct c2c_stats *stats = &c2c.hists.stats; 2112 2113 llc_misses = stats->lcl_dram + 2114 stats->rmt_dram + 2115 stats->rmt_hit + 2116 stats->rmt_hitm; 2117 2118 fprintf(out, "=================================================\n"); 2119 fprintf(out, " Trace Event Information \n"); 2120 fprintf(out, "=================================================\n"); 2121 fprintf(out, " Total records : %10d\n", stats->nr_entries); 2122 fprintf(out, " Locked Load/Store Operations : %10d\n", stats->locks); 2123 fprintf(out, " Load Operations : %10d\n", stats->load); 2124 fprintf(out, " Loads - uncacheable : %10d\n", stats->ld_uncache); 2125 fprintf(out, " Loads - IO : %10d\n", stats->ld_io); 2126 fprintf(out, " Loads - Miss : %10d\n", stats->ld_miss); 2127 fprintf(out, " Loads - no mapping : %10d\n", stats->ld_noadrs); 2128 fprintf(out, " Load Fill Buffer Hit : %10d\n", stats->ld_fbhit); 2129 fprintf(out, " Load L1D hit : %10d\n", stats->ld_l1hit); 2130 fprintf(out, " Load L2D hit : %10d\n", stats->ld_l2hit); 2131 fprintf(out, " Load LLC hit : %10d\n", stats->ld_llchit + stats->lcl_hitm); 2132 fprintf(out, " Load Local HITM : %10d\n", stats->lcl_hitm); 2133 fprintf(out, " Load Remote HITM : %10d\n", stats->rmt_hitm); 2134 fprintf(out, " Load Remote HIT : %10d\n", stats->rmt_hit); 2135 fprintf(out, " Load Local DRAM : %10d\n", stats->lcl_dram); 2136 fprintf(out, " Load Remote DRAM : %10d\n", stats->rmt_dram); 2137 fprintf(out, " Load MESI State Exclusive : %10d\n", stats->ld_excl); 2138 fprintf(out, " Load MESI State Shared : %10d\n", stats->ld_shared); 2139 fprintf(out, " Load LLC Misses : %10d\n", llc_misses); 2140 fprintf(out, " Load access blocked by data : %10d\n", stats->blk_data); 2141 fprintf(out, " Load access blocked by address : %10d\n", stats->blk_addr); 2142 fprintf(out, " LLC Misses to Local DRAM : %10.1f%%\n", ((double)stats->lcl_dram/(double)llc_misses) * 100.); 2143 fprintf(out, " LLC Misses to Remote DRAM : %10.1f%%\n", ((double)stats->rmt_dram/(double)llc_misses) * 100.); 2144 fprintf(out, " LLC Misses to Remote cache (HIT) : %10.1f%%\n", ((double)stats->rmt_hit /(double)llc_misses) * 100.); 2145 fprintf(out, " LLC Misses to Remote cache (HITM) : %10.1f%%\n", ((double)stats->rmt_hitm/(double)llc_misses) * 100.); 2146 fprintf(out, " Store Operations : %10d\n", stats->store); 2147 fprintf(out, " Store - uncacheable : %10d\n", stats->st_uncache); 2148 fprintf(out, " Store - no mapping : %10d\n", stats->st_noadrs); 2149 fprintf(out, " Store L1D Hit : %10d\n", stats->st_l1hit); 2150 fprintf(out, " Store L1D Miss : %10d\n", stats->st_l1miss); 2151 fprintf(out, " No Page Map Rejects : %10d\n", stats->nomap); 2152 fprintf(out, " Unable to parse data source : %10d\n", stats->noparse); 2153 } 2154 2155 static void print_shared_cacheline_info(FILE *out) 2156 { 2157 struct c2c_stats *stats = &c2c.shared_clines_stats; 2158 int hitm_cnt = stats->lcl_hitm + stats->rmt_hitm; 2159 2160 fprintf(out, "=================================================\n"); 2161 fprintf(out, " Global Shared Cache Line Event Information \n"); 2162 fprintf(out, "=================================================\n"); 2163 fprintf(out, " Total Shared Cache Lines : %10d\n", c2c.shared_clines); 2164 fprintf(out, " Load HITs on shared lines : %10d\n", stats->load); 2165 fprintf(out, " Fill Buffer Hits on shared lines : %10d\n", stats->ld_fbhit); 2166 fprintf(out, " L1D hits on shared lines : %10d\n", stats->ld_l1hit); 2167 fprintf(out, " L2D hits on shared lines : %10d\n", stats->ld_l2hit); 2168 fprintf(out, " LLC hits on shared lines : %10d\n", stats->ld_llchit + stats->lcl_hitm); 2169 fprintf(out, " Locked Access on shared lines : %10d\n", stats->locks); 2170 fprintf(out, " Blocked Access on shared lines : %10d\n", stats->blk_data + stats->blk_addr); 2171 fprintf(out, " Store HITs on shared lines : %10d\n", stats->store); 2172 fprintf(out, " Store L1D hits on shared lines : %10d\n", stats->st_l1hit); 2173 fprintf(out, " Total Merged records : %10d\n", hitm_cnt + stats->store); 2174 } 2175 2176 static void print_cacheline(struct c2c_hists *c2c_hists, 2177 struct hist_entry *he_cl, 2178 struct perf_hpp_list *hpp_list, 2179 FILE *out) 2180 { 2181 char bf[1000]; 2182 struct perf_hpp hpp = { 2183 .buf = bf, 2184 .size = 1000, 2185 }; 2186 static bool once; 2187 2188 if (!once) { 2189 hists__fprintf_headers(&c2c_hists->hists, out); 2190 once = true; 2191 } else { 2192 fprintf(out, "\n"); 2193 } 2194 2195 fprintf(out, " -------------------------------------------------------------\n"); 2196 __hist_entry__snprintf(he_cl, &hpp, hpp_list); 2197 fprintf(out, "%s\n", bf); 2198 fprintf(out, " -------------------------------------------------------------\n"); 2199 2200 hists__fprintf(&c2c_hists->hists, false, 0, 0, 0, out, false); 2201 } 2202 2203 static void print_pareto(FILE *out) 2204 { 2205 struct perf_hpp_list hpp_list; 2206 struct rb_node *nd; 2207 int ret; 2208 const char *cl_output; 2209 2210 cl_output = "cl_num," 2211 "cl_rmt_hitm," 2212 "cl_lcl_hitm," 2213 "cl_stores_l1hit," 2214 "cl_stores_l1miss," 2215 "dcacheline"; 2216 2217 perf_hpp_list__init(&hpp_list); 2218 ret = hpp_list__parse(&hpp_list, cl_output, NULL); 2219 2220 if (WARN_ONCE(ret, "failed to setup sort entries\n")) 2221 return; 2222 2223 nd = rb_first_cached(&c2c.hists.hists.entries); 2224 2225 for (; nd; nd = rb_next(nd)) { 2226 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node); 2227 struct c2c_hist_entry *c2c_he; 2228 2229 if (he->filtered) 2230 continue; 2231 2232 c2c_he = container_of(he, struct c2c_hist_entry, he); 2233 print_cacheline(c2c_he->hists, he, &hpp_list, out); 2234 } 2235 } 2236 2237 static void print_c2c_info(FILE *out, struct perf_session *session) 2238 { 2239 struct evlist *evlist = session->evlist; 2240 struct evsel *evsel; 2241 bool first = true; 2242 2243 fprintf(out, "=================================================\n"); 2244 fprintf(out, " c2c details \n"); 2245 fprintf(out, "=================================================\n"); 2246 2247 evlist__for_each_entry(evlist, evsel) { 2248 fprintf(out, "%-36s: %s\n", first ? " Events" : "", evsel__name(evsel)); 2249 first = false; 2250 } 2251 fprintf(out, " Cachelines sort on : %s HITMs\n", 2252 display_str[c2c.display]); 2253 fprintf(out, " Cacheline data grouping : %s\n", c2c.cl_sort); 2254 } 2255 2256 static void perf_c2c__hists_fprintf(FILE *out, struct perf_session *session) 2257 { 2258 setup_pager(); 2259 2260 print_c2c__display_stats(out); 2261 fprintf(out, "\n"); 2262 print_shared_cacheline_info(out); 2263 fprintf(out, "\n"); 2264 print_c2c_info(out, session); 2265 2266 if (c2c.stats_only) 2267 return; 2268 2269 fprintf(out, "\n"); 2270 fprintf(out, "=================================================\n"); 2271 fprintf(out, " Shared Data Cache Line Table \n"); 2272 fprintf(out, "=================================================\n"); 2273 fprintf(out, "#\n"); 2274 2275 hists__fprintf(&c2c.hists.hists, true, 0, 0, 0, stdout, true); 2276 2277 fprintf(out, "\n"); 2278 fprintf(out, "=================================================\n"); 2279 fprintf(out, " Shared Cache Line Distribution Pareto \n"); 2280 fprintf(out, "=================================================\n"); 2281 fprintf(out, "#\n"); 2282 2283 print_pareto(out); 2284 } 2285 2286 #ifdef HAVE_SLANG_SUPPORT 2287 static void c2c_browser__update_nr_entries(struct hist_browser *hb) 2288 { 2289 u64 nr_entries = 0; 2290 struct rb_node *nd = rb_first_cached(&hb->hists->entries); 2291 2292 while (nd) { 2293 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node); 2294 2295 if (!he->filtered) 2296 nr_entries++; 2297 2298 nd = rb_next(nd); 2299 } 2300 2301 hb->nr_non_filtered_entries = nr_entries; 2302 } 2303 2304 struct c2c_cacheline_browser { 2305 struct hist_browser hb; 2306 struct hist_entry *he; 2307 }; 2308 2309 static int 2310 perf_c2c_cacheline_browser__title(struct hist_browser *browser, 2311 char *bf, size_t size) 2312 { 2313 struct c2c_cacheline_browser *cl_browser; 2314 struct hist_entry *he; 2315 uint64_t addr = 0; 2316 2317 cl_browser = container_of(browser, struct c2c_cacheline_browser, hb); 2318 he = cl_browser->he; 2319 2320 if (he->mem_info) 2321 addr = cl_address(he->mem_info->daddr.addr); 2322 2323 scnprintf(bf, size, "Cacheline 0x%lx", addr); 2324 return 0; 2325 } 2326 2327 static struct c2c_cacheline_browser* 2328 c2c_cacheline_browser__new(struct hists *hists, struct hist_entry *he) 2329 { 2330 struct c2c_cacheline_browser *browser; 2331 2332 browser = zalloc(sizeof(*browser)); 2333 if (browser) { 2334 hist_browser__init(&browser->hb, hists); 2335 browser->hb.c2c_filter = true; 2336 browser->hb.title = perf_c2c_cacheline_browser__title; 2337 browser->he = he; 2338 } 2339 2340 return browser; 2341 } 2342 2343 static int perf_c2c__browse_cacheline(struct hist_entry *he) 2344 { 2345 struct c2c_hist_entry *c2c_he; 2346 struct c2c_hists *c2c_hists; 2347 struct c2c_cacheline_browser *cl_browser; 2348 struct hist_browser *browser; 2349 int key = -1; 2350 static const char help[] = 2351 " ENTER Toggle callchains (if present) \n" 2352 " n Toggle Node details info \n" 2353 " s Toggle full length of symbol and source line columns \n" 2354 " q Return back to cacheline list \n"; 2355 2356 if (!he) 2357 return 0; 2358 2359 /* Display compact version first. */ 2360 c2c.symbol_full = false; 2361 2362 c2c_he = container_of(he, struct c2c_hist_entry, he); 2363 c2c_hists = c2c_he->hists; 2364 2365 cl_browser = c2c_cacheline_browser__new(&c2c_hists->hists, he); 2366 if (cl_browser == NULL) 2367 return -1; 2368 2369 browser = &cl_browser->hb; 2370 2371 /* reset abort key so that it can get Ctrl-C as a key */ 2372 SLang_reset_tty(); 2373 SLang_init_tty(0, 0, 0); 2374 2375 c2c_browser__update_nr_entries(browser); 2376 2377 while (1) { 2378 key = hist_browser__run(browser, "? - help", true, 0); 2379 2380 switch (key) { 2381 case 's': 2382 c2c.symbol_full = !c2c.symbol_full; 2383 break; 2384 case 'n': 2385 c2c.node_info = (c2c.node_info + 1) % 3; 2386 setup_nodes_header(); 2387 break; 2388 case 'q': 2389 goto out; 2390 case '?': 2391 ui_browser__help_window(&browser->b, help); 2392 break; 2393 default: 2394 break; 2395 } 2396 } 2397 2398 out: 2399 free(cl_browser); 2400 return 0; 2401 } 2402 2403 static int perf_c2c_browser__title(struct hist_browser *browser, 2404 char *bf, size_t size) 2405 { 2406 scnprintf(bf, size, 2407 "Shared Data Cache Line Table " 2408 "(%lu entries, sorted on %s HITMs)", 2409 browser->nr_non_filtered_entries, 2410 display_str[c2c.display]); 2411 return 0; 2412 } 2413 2414 static struct hist_browser* 2415 perf_c2c_browser__new(struct hists *hists) 2416 { 2417 struct hist_browser *browser = hist_browser__new(hists); 2418 2419 if (browser) { 2420 browser->title = perf_c2c_browser__title; 2421 browser->c2c_filter = true; 2422 } 2423 2424 return browser; 2425 } 2426 2427 static int perf_c2c__hists_browse(struct hists *hists) 2428 { 2429 struct hist_browser *browser; 2430 int key = -1; 2431 static const char help[] = 2432 " d Display cacheline details \n" 2433 " ENTER Toggle callchains (if present) \n" 2434 " q Quit \n"; 2435 2436 browser = perf_c2c_browser__new(hists); 2437 if (browser == NULL) 2438 return -1; 2439 2440 /* reset abort key so that it can get Ctrl-C as a key */ 2441 SLang_reset_tty(); 2442 SLang_init_tty(0, 0, 0); 2443 2444 c2c_browser__update_nr_entries(browser); 2445 2446 while (1) { 2447 key = hist_browser__run(browser, "? - help", true, 0); 2448 2449 switch (key) { 2450 case 'q': 2451 goto out; 2452 case 'd': 2453 perf_c2c__browse_cacheline(browser->he_selection); 2454 break; 2455 case '?': 2456 ui_browser__help_window(&browser->b, help); 2457 break; 2458 default: 2459 break; 2460 } 2461 } 2462 2463 out: 2464 hist_browser__delete(browser); 2465 return 0; 2466 } 2467 2468 static void perf_c2c_display(struct perf_session *session) 2469 { 2470 if (use_browser == 0) 2471 perf_c2c__hists_fprintf(stdout, session); 2472 else 2473 perf_c2c__hists_browse(&c2c.hists.hists); 2474 } 2475 #else 2476 static void perf_c2c_display(struct perf_session *session) 2477 { 2478 use_browser = 0; 2479 perf_c2c__hists_fprintf(stdout, session); 2480 } 2481 #endif /* HAVE_SLANG_SUPPORT */ 2482 2483 static char *fill_line(const char *orig, int len) 2484 { 2485 int i, j, olen = strlen(orig); 2486 char *buf; 2487 2488 buf = zalloc(len + 1); 2489 if (!buf) 2490 return NULL; 2491 2492 j = len / 2 - olen / 2; 2493 2494 for (i = 0; i < j - 1; i++) 2495 buf[i] = '-'; 2496 2497 buf[i++] = ' '; 2498 2499 strcpy(buf + i, orig); 2500 2501 i += olen; 2502 2503 buf[i++] = ' '; 2504 2505 for (; i < len; i++) 2506 buf[i] = '-'; 2507 2508 return buf; 2509 } 2510 2511 static int ui_quirks(void) 2512 { 2513 const char *nodestr = "Data address"; 2514 char *buf; 2515 2516 if (!c2c.use_stdio) { 2517 dim_offset.width = 5; 2518 dim_offset.header = header_offset_tui; 2519 nodestr = "CL"; 2520 } 2521 2522 dim_percent_hitm.header = percent_hitm_header[c2c.display]; 2523 2524 /* Fix the zero line for dcacheline column. */ 2525 buf = fill_line("Cacheline", dim_dcacheline.width + 2526 dim_dcacheline_node.width + 2527 dim_dcacheline_count.width + 4); 2528 if (!buf) 2529 return -ENOMEM; 2530 2531 dim_dcacheline.header.line[0].text = buf; 2532 2533 /* Fix the zero line for offset column. */ 2534 buf = fill_line(nodestr, dim_offset.width + 2535 dim_offset_node.width + 2536 dim_dcacheline_count.width + 4); 2537 if (!buf) 2538 return -ENOMEM; 2539 2540 dim_offset.header.line[0].text = buf; 2541 2542 return 0; 2543 } 2544 2545 #define CALLCHAIN_DEFAULT_OPT "graph,0.5,caller,function,percent" 2546 2547 const char callchain_help[] = "Display call graph (stack chain/backtrace):\n\n" 2548 CALLCHAIN_REPORT_HELP 2549 "\n\t\t\t\tDefault: " CALLCHAIN_DEFAULT_OPT; 2550 2551 static int 2552 parse_callchain_opt(const struct option *opt, const char *arg, int unset) 2553 { 2554 struct callchain_param *callchain = opt->value; 2555 2556 callchain->enabled = !unset; 2557 /* 2558 * --no-call-graph 2559 */ 2560 if (unset) { 2561 symbol_conf.use_callchain = false; 2562 callchain->mode = CHAIN_NONE; 2563 return 0; 2564 } 2565 2566 return parse_callchain_report_opt(arg); 2567 } 2568 2569 static int setup_callchain(struct evlist *evlist) 2570 { 2571 u64 sample_type = evlist__combined_sample_type(evlist); 2572 enum perf_call_graph_mode mode = CALLCHAIN_NONE; 2573 2574 if ((sample_type & PERF_SAMPLE_REGS_USER) && 2575 (sample_type & PERF_SAMPLE_STACK_USER)) { 2576 mode = CALLCHAIN_DWARF; 2577 dwarf_callchain_users = true; 2578 } else if (sample_type & PERF_SAMPLE_BRANCH_STACK) 2579 mode = CALLCHAIN_LBR; 2580 else if (sample_type & PERF_SAMPLE_CALLCHAIN) 2581 mode = CALLCHAIN_FP; 2582 2583 if (!callchain_param.enabled && 2584 callchain_param.mode != CHAIN_NONE && 2585 mode != CALLCHAIN_NONE) { 2586 symbol_conf.use_callchain = true; 2587 if (callchain_register_param(&callchain_param) < 0) { 2588 ui__error("Can't register callchain params.\n"); 2589 return -EINVAL; 2590 } 2591 } 2592 2593 if (c2c.stitch_lbr && (mode != CALLCHAIN_LBR)) { 2594 ui__warning("Can't find LBR callchain. Switch off --stitch-lbr.\n" 2595 "Please apply --call-graph lbr when recording.\n"); 2596 c2c.stitch_lbr = false; 2597 } 2598 2599 callchain_param.record_mode = mode; 2600 callchain_param.min_percent = 0; 2601 return 0; 2602 } 2603 2604 static int setup_display(const char *str) 2605 { 2606 const char *display = str ?: "tot"; 2607 2608 if (!strcmp(display, "tot")) 2609 c2c.display = DISPLAY_TOT; 2610 else if (!strcmp(display, "rmt")) 2611 c2c.display = DISPLAY_RMT; 2612 else if (!strcmp(display, "lcl")) 2613 c2c.display = DISPLAY_LCL; 2614 else { 2615 pr_err("failed: unknown display type: %s\n", str); 2616 return -1; 2617 } 2618 2619 return 0; 2620 } 2621 2622 #define for_each_token(__tok, __buf, __sep, __tmp) \ 2623 for (__tok = strtok_r(__buf, __sep, &__tmp); __tok; \ 2624 __tok = strtok_r(NULL, __sep, &__tmp)) 2625 2626 static int build_cl_output(char *cl_sort, bool no_source) 2627 { 2628 char *tok, *tmp, *buf = strdup(cl_sort); 2629 bool add_pid = false; 2630 bool add_tid = false; 2631 bool add_iaddr = false; 2632 bool add_sym = false; 2633 bool add_dso = false; 2634 bool add_src = false; 2635 int ret = 0; 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 ret = -EINVAL; 2655 goto err; 2656 } 2657 } 2658 2659 if (asprintf(&c2c.cl_output, 2660 "%s%s%s%s%s%s%s%s%s%s", 2661 c2c.use_stdio ? "cl_num_empty," : "", 2662 "percent_rmt_hitm," 2663 "percent_lcl_hitm," 2664 "percent_stores_l1hit," 2665 "percent_stores_l1miss," 2666 "offset,offset_node,dcacheline_count,", 2667 add_pid ? "pid," : "", 2668 add_tid ? "tid," : "", 2669 add_iaddr ? "iaddr," : "", 2670 "mean_rmt," 2671 "mean_lcl," 2672 "mean_load," 2673 "tot_recs," 2674 "cpucnt,", 2675 add_sym ? "symbol," : "", 2676 add_dso ? "dso," : "", 2677 add_src ? "cl_srcline," : "", 2678 "node") < 0) { 2679 ret = -ENOMEM; 2680 goto err; 2681 } 2682 2683 c2c.show_src = add_src; 2684 err: 2685 free(buf); 2686 return ret; 2687 } 2688 2689 static int setup_coalesce(const char *coalesce, bool no_source) 2690 { 2691 const char *c = coalesce ?: coalesce_default; 2692 2693 if (asprintf(&c2c.cl_sort, "offset,%s", c) < 0) 2694 return -ENOMEM; 2695 2696 if (build_cl_output(c2c.cl_sort, no_source)) 2697 return -1; 2698 2699 if (asprintf(&c2c.cl_resort, "offset,%s", 2700 c2c.display == DISPLAY_TOT ? 2701 "tot_hitm" : 2702 c2c.display == DISPLAY_RMT ? 2703 "rmt_hitm,lcl_hitm" : 2704 "lcl_hitm,rmt_hitm") < 0) 2705 return -ENOMEM; 2706 2707 pr_debug("coalesce sort fields: %s\n", c2c.cl_sort); 2708 pr_debug("coalesce resort fields: %s\n", c2c.cl_resort); 2709 pr_debug("coalesce output fields: %s\n", c2c.cl_output); 2710 return 0; 2711 } 2712 2713 static int perf_c2c__report(int argc, const char **argv) 2714 { 2715 struct itrace_synth_opts itrace_synth_opts = { 2716 .set = true, 2717 .mem = true, /* Only enable memory event */ 2718 .default_no_sample = true, 2719 }; 2720 2721 struct perf_session *session; 2722 struct ui_progress prog; 2723 struct perf_data data = { 2724 .mode = PERF_DATA_MODE_READ, 2725 }; 2726 char callchain_default_opt[] = CALLCHAIN_DEFAULT_OPT; 2727 const char *display = NULL; 2728 const char *coalesce = NULL; 2729 bool no_source = false; 2730 const struct option options[] = { 2731 OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, 2732 "file", "vmlinux pathname"), 2733 OPT_STRING('i', "input", &input_name, "file", 2734 "the input file to process"), 2735 OPT_INCR('N', "node-info", &c2c.node_info, 2736 "show extra node info in report (repeat for more info)"), 2737 #ifdef HAVE_SLANG_SUPPORT 2738 OPT_BOOLEAN(0, "stdio", &c2c.use_stdio, "Use the stdio interface"), 2739 #endif 2740 OPT_BOOLEAN(0, "stats", &c2c.stats_only, 2741 "Display only statistic tables (implies --stdio)"), 2742 OPT_BOOLEAN(0, "full-symbols", &c2c.symbol_full, 2743 "Display full length of symbols"), 2744 OPT_BOOLEAN(0, "no-source", &no_source, 2745 "Do not display Source Line column"), 2746 OPT_BOOLEAN(0, "show-all", &c2c.show_all, 2747 "Show all captured HITM lines."), 2748 OPT_CALLBACK_DEFAULT('g', "call-graph", &callchain_param, 2749 "print_type,threshold[,print_limit],order,sort_key[,branch],value", 2750 callchain_help, &parse_callchain_opt, 2751 callchain_default_opt), 2752 OPT_STRING('d', "display", &display, "Switch HITM output type", "lcl,rmt"), 2753 OPT_STRING('c', "coalesce", &coalesce, "coalesce fields", 2754 "coalesce fields: pid,tid,iaddr,dso"), 2755 OPT_BOOLEAN('f', "force", &symbol_conf.force, "don't complain, do it"), 2756 OPT_BOOLEAN(0, "stitch-lbr", &c2c.stitch_lbr, 2757 "Enable LBR callgraph stitching approach"), 2758 OPT_PARENT(c2c_options), 2759 OPT_END() 2760 }; 2761 int err = 0; 2762 const char *output_str, *sort_str = NULL; 2763 2764 argc = parse_options(argc, argv, options, report_c2c_usage, 2765 PARSE_OPT_STOP_AT_NON_OPTION); 2766 if (argc) 2767 usage_with_options(report_c2c_usage, options); 2768 2769 if (c2c.stats_only) 2770 c2c.use_stdio = true; 2771 2772 err = symbol__validate_sym_arguments(); 2773 if (err) 2774 goto out; 2775 2776 if (!input_name || !strlen(input_name)) 2777 input_name = "perf.data"; 2778 2779 data.path = input_name; 2780 data.force = symbol_conf.force; 2781 2782 err = setup_display(display); 2783 if (err) 2784 goto out; 2785 2786 err = setup_coalesce(coalesce, no_source); 2787 if (err) { 2788 pr_debug("Failed to initialize hists\n"); 2789 goto out; 2790 } 2791 2792 err = c2c_hists__init(&c2c.hists, "dcacheline", 2); 2793 if (err) { 2794 pr_debug("Failed to initialize hists\n"); 2795 goto out; 2796 } 2797 2798 session = perf_session__new(&data, &c2c.tool); 2799 if (IS_ERR(session)) { 2800 err = PTR_ERR(session); 2801 pr_debug("Error creating perf session\n"); 2802 goto out; 2803 } 2804 2805 session->itrace_synth_opts = &itrace_synth_opts; 2806 2807 err = setup_nodes(session); 2808 if (err) { 2809 pr_err("Failed setup nodes\n"); 2810 goto out; 2811 } 2812 2813 err = mem2node__init(&c2c.mem2node, &session->header.env); 2814 if (err) 2815 goto out_session; 2816 2817 err = setup_callchain(session->evlist); 2818 if (err) 2819 goto out_mem2node; 2820 2821 if (symbol__init(&session->header.env) < 0) 2822 goto out_mem2node; 2823 2824 /* No pipe support at the moment. */ 2825 if (perf_data__is_pipe(session->data)) { 2826 pr_debug("No pipe support at the moment.\n"); 2827 goto out_mem2node; 2828 } 2829 2830 if (c2c.use_stdio) 2831 use_browser = 0; 2832 else 2833 use_browser = 1; 2834 2835 setup_browser(false); 2836 2837 err = perf_session__process_events(session); 2838 if (err) { 2839 pr_err("failed to process sample\n"); 2840 goto out_mem2node; 2841 } 2842 2843 output_str = "cl_idx," 2844 "dcacheline," 2845 "dcacheline_node," 2846 "dcacheline_count," 2847 "percent_hitm," 2848 "tot_hitm,lcl_hitm,rmt_hitm," 2849 "tot_recs," 2850 "tot_loads," 2851 "tot_stores," 2852 "stores_l1hit,stores_l1miss," 2853 "ld_fbhit,ld_l1hit,ld_l2hit," 2854 "ld_lclhit,lcl_hitm," 2855 "ld_rmthit,rmt_hitm," 2856 "dram_lcl,dram_rmt"; 2857 2858 if (c2c.display == DISPLAY_TOT) 2859 sort_str = "tot_hitm"; 2860 else if (c2c.display == DISPLAY_RMT) 2861 sort_str = "rmt_hitm"; 2862 else if (c2c.display == DISPLAY_LCL) 2863 sort_str = "lcl_hitm"; 2864 2865 c2c_hists__reinit(&c2c.hists, output_str, sort_str); 2866 2867 ui_progress__init(&prog, c2c.hists.hists.nr_entries, "Sorting..."); 2868 2869 hists__collapse_resort(&c2c.hists.hists, NULL); 2870 hists__output_resort_cb(&c2c.hists.hists, &prog, resort_shared_cl_cb); 2871 hists__iterate_cb(&c2c.hists.hists, resort_cl_cb); 2872 2873 ui_progress__finish(); 2874 2875 if (ui_quirks()) { 2876 pr_err("failed to setup UI\n"); 2877 goto out_mem2node; 2878 } 2879 2880 perf_c2c_display(session); 2881 2882 out_mem2node: 2883 mem2node__exit(&c2c.mem2node); 2884 out_session: 2885 perf_session__delete(session); 2886 out: 2887 return err; 2888 } 2889 2890 static int parse_record_events(const struct option *opt, 2891 const char *str, int unset __maybe_unused) 2892 { 2893 bool *event_set = (bool *) opt->value; 2894 2895 if (!strcmp(str, "list")) { 2896 perf_mem_events__list(); 2897 exit(0); 2898 } 2899 if (perf_mem_events__parse(str)) 2900 exit(-1); 2901 2902 *event_set = true; 2903 return 0; 2904 } 2905 2906 2907 static const char * const __usage_record[] = { 2908 "perf c2c record [<options>] [<command>]", 2909 "perf c2c record [<options>] -- <command> [<options>]", 2910 NULL 2911 }; 2912 2913 static const char * const *record_mem_usage = __usage_record; 2914 2915 static int perf_c2c__record(int argc, const char **argv) 2916 { 2917 int rec_argc, i = 0, j, rec_tmp_nr = 0; 2918 const char **rec_argv; 2919 char **rec_tmp; 2920 int ret; 2921 bool all_user = false, all_kernel = false; 2922 bool event_set = false; 2923 struct perf_mem_event *e; 2924 struct option options[] = { 2925 OPT_CALLBACK('e', "event", &event_set, "event", 2926 "event selector. Use 'perf c2c record -e list' to list available events", 2927 parse_record_events), 2928 OPT_BOOLEAN('u', "all-user", &all_user, "collect only user level data"), 2929 OPT_BOOLEAN('k', "all-kernel", &all_kernel, "collect only kernel level data"), 2930 OPT_UINTEGER('l', "ldlat", &perf_mem_events__loads_ldlat, "setup mem-loads latency"), 2931 OPT_PARENT(c2c_options), 2932 OPT_END() 2933 }; 2934 2935 if (perf_mem_events__init()) { 2936 pr_err("failed: memory events not supported\n"); 2937 return -1; 2938 } 2939 2940 argc = parse_options(argc, argv, options, record_mem_usage, 2941 PARSE_OPT_KEEP_UNKNOWN); 2942 2943 if (!perf_pmu__has_hybrid()) 2944 rec_argc = argc + 11; /* max number of arguments */ 2945 else 2946 rec_argc = argc + 11 * perf_pmu__hybrid_pmu_num(); 2947 2948 rec_argv = calloc(rec_argc + 1, sizeof(char *)); 2949 if (!rec_argv) 2950 return -1; 2951 2952 rec_tmp = calloc(rec_argc + 1, sizeof(char *)); 2953 if (!rec_tmp) { 2954 free(rec_argv); 2955 return -1; 2956 } 2957 2958 rec_argv[i++] = "record"; 2959 2960 if (!event_set) { 2961 e = perf_mem_events__ptr(PERF_MEM_EVENTS__LOAD_STORE); 2962 /* 2963 * The load and store operations are required, use the event 2964 * PERF_MEM_EVENTS__LOAD_STORE if it is supported. 2965 */ 2966 if (e->tag) { 2967 e->record = true; 2968 } else { 2969 e = perf_mem_events__ptr(PERF_MEM_EVENTS__LOAD); 2970 e->record = true; 2971 2972 e = perf_mem_events__ptr(PERF_MEM_EVENTS__STORE); 2973 e->record = true; 2974 } 2975 } 2976 2977 e = perf_mem_events__ptr(PERF_MEM_EVENTS__LOAD); 2978 if (e->record) 2979 rec_argv[i++] = "-W"; 2980 2981 rec_argv[i++] = "-d"; 2982 rec_argv[i++] = "--phys-data"; 2983 rec_argv[i++] = "--sample-cpu"; 2984 2985 ret = perf_mem_events__record_args(rec_argv, &i, rec_tmp, &rec_tmp_nr); 2986 if (ret) 2987 goto out; 2988 2989 if (all_user) 2990 rec_argv[i++] = "--all-user"; 2991 2992 if (all_kernel) 2993 rec_argv[i++] = "--all-kernel"; 2994 2995 for (j = 0; j < argc; j++, i++) 2996 rec_argv[i] = argv[j]; 2997 2998 if (verbose > 0) { 2999 pr_debug("calling: "); 3000 3001 j = 0; 3002 3003 while (rec_argv[j]) { 3004 pr_debug("%s ", rec_argv[j]); 3005 j++; 3006 } 3007 pr_debug("\n"); 3008 } 3009 3010 ret = cmd_record(i, rec_argv); 3011 out: 3012 for (i = 0; i < rec_tmp_nr; i++) 3013 free(rec_tmp[i]); 3014 3015 free(rec_tmp); 3016 free(rec_argv); 3017 return ret; 3018 } 3019 3020 int cmd_c2c(int argc, const char **argv) 3021 { 3022 argc = parse_options(argc, argv, c2c_options, c2c_usage, 3023 PARSE_OPT_STOP_AT_NON_OPTION); 3024 3025 if (!argc) 3026 usage_with_options(c2c_usage, c2c_options); 3027 3028 if (!strncmp(argv[0], "rec", 3)) { 3029 return perf_c2c__record(argc, argv); 3030 } else if (!strncmp(argv[0], "rep", 3)) { 3031 return perf_c2c__report(argc, argv); 3032 } else { 3033 usage_with_options(c2c_usage, c2c_options); 3034 } 3035 3036 return 0; 3037 } 3038