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