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