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