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