Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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