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