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