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