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