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