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