at master 108 kB view raw
1// SPDX-License-Identifier: GPL-2.0 2#include <errno.h> 3#include <inttypes.h> 4#include <regex.h> 5#include <stdlib.h> 6#include <linux/mman.h> 7#include <linux/time64.h> 8#include "debug.h" 9#include "dso.h" 10#include "sort.h" 11#include "hist.h" 12#include "cacheline.h" 13#include "comm.h" 14#include "map.h" 15#include "maps.h" 16#include "symbol.h" 17#include "map_symbol.h" 18#include "branch.h" 19#include "thread.h" 20#include "evsel.h" 21#include "evlist.h" 22#include "srcline.h" 23#include "strlist.h" 24#include "strbuf.h" 25#include "mem-events.h" 26#include "mem-info.h" 27#include "annotate.h" 28#include "annotate-data.h" 29#include "event.h" 30#include "time-utils.h" 31#include "cgroup.h" 32#include "machine.h" 33#include "trace-event.h" 34#include <linux/kernel.h> 35#include <linux/string.h> 36 37#ifdef HAVE_LIBTRACEEVENT 38#include <event-parse.h> 39#endif 40 41regex_t parent_regex; 42const char default_parent_pattern[] = "^sys_|^do_page_fault"; 43const char *parent_pattern = default_parent_pattern; 44const char *default_sort_order = "comm,dso,symbol"; 45const char default_branch_sort_order[] = "comm,dso_from,symbol_from,symbol_to,cycles"; 46const char default_mem_sort_order[] = "local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked,blocked,local_ins_lat,local_p_stage_cyc"; 47const char default_top_sort_order[] = "dso,symbol"; 48const char default_diff_sort_order[] = "dso,symbol"; 49const char default_tracepoint_sort_order[] = "trace"; 50const char *sort_order; 51const char *field_order; 52regex_t ignore_callees_regex; 53int have_ignore_callees = 0; 54enum sort_mode sort__mode = SORT_MODE__NORMAL; 55static const char *const dynamic_headers[] = {"local_ins_lat", "ins_lat", "local_p_stage_cyc", "p_stage_cyc"}; 56static const char *const arch_specific_sort_keys[] = {"local_p_stage_cyc", "p_stage_cyc"}; 57 58/* 59 * Some architectures have Adjacent Cacheline Prefetch feature, which 60 * behaves like the cacheline size is doubled. Enable this flag to 61 * check things in double cacheline granularity. 62 */ 63bool chk_double_cl; 64 65/* 66 * Replaces all occurrences of a char used with the: 67 * 68 * -t, --field-separator 69 * 70 * option, that uses a special separator character and don't pad with spaces, 71 * replacing all occurrences of this separator in symbol names (and other 72 * output) with a '.' character, that thus it's the only non valid separator. 73*/ 74static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...) 75{ 76 int n; 77 va_list ap; 78 79 va_start(ap, fmt); 80 n = vsnprintf(bf, size, fmt, ap); 81 if (symbol_conf.field_sep && n > 0) { 82 char *sep = bf; 83 84 while (1) { 85 sep = strchr(sep, *symbol_conf.field_sep); 86 if (sep == NULL) 87 break; 88 *sep = '.'; 89 } 90 } 91 va_end(ap); 92 93 if (n >= (int)size) 94 return size - 1; 95 return n; 96} 97 98static int64_t cmp_null(const void *l, const void *r) 99{ 100 if (!l && !r) 101 return 0; 102 else if (!l) 103 return -1; 104 else 105 return 1; 106} 107 108/* --sort pid */ 109 110static int64_t 111sort__thread_cmp(struct hist_entry *left, struct hist_entry *right) 112{ 113 return thread__tid(right->thread) - thread__tid(left->thread); 114} 115 116static int hist_entry__thread_snprintf(struct hist_entry *he, char *bf, 117 size_t size, unsigned int width) 118{ 119 const char *comm = thread__comm_str(he->thread); 120 121 width = max(7U, width) - 8; 122 return repsep_snprintf(bf, size, "%7d:%-*.*s", thread__tid(he->thread), 123 width, width, comm ?: ""); 124} 125 126static int hist_entry__thread_filter(struct hist_entry *he, int type, const void *arg) 127{ 128 const struct thread *th = arg; 129 130 if (type != HIST_FILTER__THREAD) 131 return -1; 132 133 return th && !RC_CHK_EQUAL(he->thread, th); 134} 135 136struct sort_entry sort_thread = { 137 .se_header = " Pid:Command", 138 .se_cmp = sort__thread_cmp, 139 .se_snprintf = hist_entry__thread_snprintf, 140 .se_filter = hist_entry__thread_filter, 141 .se_width_idx = HISTC_THREAD, 142}; 143 144/* --sort tgid */ 145 146static int64_t 147sort__tgid_cmp(struct hist_entry *left, struct hist_entry *right) 148{ 149 return thread__pid(right->thread) - thread__pid(left->thread); 150} 151 152static int hist_entry__tgid_snprintf(struct hist_entry *he, char *bf, 153 size_t size, unsigned int width) 154{ 155 int tgid = thread__pid(he->thread); 156 const char *comm = NULL; 157 158 /* display comm of the thread-group leader */ 159 if (thread__pid(he->thread) == thread__tid(he->thread)) { 160 comm = thread__comm_str(he->thread); 161 } else { 162 struct maps *maps = thread__maps(he->thread); 163 struct thread *leader = machine__find_thread(maps__machine(maps), 164 tgid, tgid); 165 if (leader) { 166 comm = thread__comm_str(leader); 167 thread__put(leader); 168 } 169 } 170 width = max(7U, width) - 8; 171 return repsep_snprintf(bf, size, "%7d:%-*.*s", tgid, width, width, comm ?: ""); 172} 173 174struct sort_entry sort_tgid = { 175 .se_header = " Tgid:Command", 176 .se_cmp = sort__tgid_cmp, 177 .se_snprintf = hist_entry__tgid_snprintf, 178 .se_width_idx = HISTC_TGID, 179}; 180 181/* --sort simd */ 182 183static int64_t 184sort__simd_cmp(struct hist_entry *left, struct hist_entry *right) 185{ 186 if (left->simd_flags.arch != right->simd_flags.arch) 187 return (int64_t) left->simd_flags.arch - right->simd_flags.arch; 188 189 return (int64_t) left->simd_flags.pred - right->simd_flags.pred; 190} 191 192static const char *hist_entry__get_simd_name(struct simd_flags *simd_flags) 193{ 194 u64 arch = simd_flags->arch; 195 196 if (arch & SIMD_OP_FLAGS_ARCH_SVE) 197 return "SVE"; 198 else 199 return "n/a"; 200} 201 202static int hist_entry__simd_snprintf(struct hist_entry *he, char *bf, 203 size_t size, unsigned int width __maybe_unused) 204{ 205 const char *name; 206 207 if (!he->simd_flags.arch) 208 return repsep_snprintf(bf, size, ""); 209 210 name = hist_entry__get_simd_name(&he->simd_flags); 211 212 if (he->simd_flags.pred & SIMD_OP_FLAGS_PRED_EMPTY) 213 return repsep_snprintf(bf, size, "[e] %s", name); 214 else if (he->simd_flags.pred & SIMD_OP_FLAGS_PRED_PARTIAL) 215 return repsep_snprintf(bf, size, "[p] %s", name); 216 217 return repsep_snprintf(bf, size, "[.] %s", name); 218} 219 220struct sort_entry sort_simd = { 221 .se_header = "Simd ", 222 .se_cmp = sort__simd_cmp, 223 .se_snprintf = hist_entry__simd_snprintf, 224 .se_width_idx = HISTC_SIMD, 225}; 226 227/* --sort comm */ 228 229/* 230 * We can't use pointer comparison in functions below, 231 * because it gives different results based on pointer 232 * values, which could break some sorting assumptions. 233 */ 234static int64_t 235sort__comm_cmp(struct hist_entry *left, struct hist_entry *right) 236{ 237 return strcmp(comm__str(right->comm), comm__str(left->comm)); 238} 239 240static int64_t 241sort__comm_collapse(struct hist_entry *left, struct hist_entry *right) 242{ 243 return strcmp(comm__str(right->comm), comm__str(left->comm)); 244} 245 246static int64_t 247sort__comm_sort(struct hist_entry *left, struct hist_entry *right) 248{ 249 return strcmp(comm__str(right->comm), comm__str(left->comm)); 250} 251 252static int hist_entry__comm_snprintf(struct hist_entry *he, char *bf, 253 size_t size, unsigned int width) 254{ 255 return repsep_snprintf(bf, size, "%-*.*s", width, width, comm__str(he->comm)); 256} 257 258struct sort_entry sort_comm = { 259 .se_header = "Command", 260 .se_cmp = sort__comm_cmp, 261 .se_collapse = sort__comm_collapse, 262 .se_sort = sort__comm_sort, 263 .se_snprintf = hist_entry__comm_snprintf, 264 .se_filter = hist_entry__thread_filter, 265 .se_width_idx = HISTC_COMM, 266}; 267 268/* --sort dso */ 269 270static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r) 271{ 272 struct dso *dso_l = map_l ? map__dso(map_l) : NULL; 273 struct dso *dso_r = map_r ? map__dso(map_r) : NULL; 274 const char *dso_name_l, *dso_name_r; 275 276 if (!dso_l || !dso_r) 277 return cmp_null(dso_r, dso_l); 278 279 if (verbose > 0) { 280 dso_name_l = dso__long_name(dso_l); 281 dso_name_r = dso__long_name(dso_r); 282 } else { 283 dso_name_l = dso__short_name(dso_l); 284 dso_name_r = dso__short_name(dso_r); 285 } 286 287 return strcmp(dso_name_l, dso_name_r); 288} 289 290static int64_t 291sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) 292{ 293 return _sort__dso_cmp(right->ms.map, left->ms.map); 294} 295 296static int _hist_entry__dso_snprintf(struct map *map, char *bf, 297 size_t size, unsigned int width) 298{ 299 const struct dso *dso = map ? map__dso(map) : NULL; 300 const char *dso_name = "[unknown]"; 301 302 if (dso) 303 dso_name = verbose > 0 ? dso__long_name(dso) : dso__short_name(dso); 304 305 return repsep_snprintf(bf, size, "%-*.*s", width, width, dso_name); 306} 307 308static int hist_entry__dso_snprintf(struct hist_entry *he, char *bf, 309 size_t size, unsigned int width) 310{ 311 return _hist_entry__dso_snprintf(he->ms.map, bf, size, width); 312} 313 314static int hist_entry__dso_filter(struct hist_entry *he, int type, const void *arg) 315{ 316 const struct dso *dso = arg; 317 318 if (type != HIST_FILTER__DSO) 319 return -1; 320 321 return dso && (!he->ms.map || map__dso(he->ms.map) != dso); 322} 323 324struct sort_entry sort_dso = { 325 .se_header = "Shared Object", 326 .se_cmp = sort__dso_cmp, 327 .se_snprintf = hist_entry__dso_snprintf, 328 .se_filter = hist_entry__dso_filter, 329 .se_width_idx = HISTC_DSO, 330}; 331 332/* --sort symbol */ 333 334static int64_t _sort__addr_cmp(u64 left_ip, u64 right_ip) 335{ 336 return (int64_t)(right_ip - left_ip); 337} 338 339int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r) 340{ 341 if (!sym_l || !sym_r) 342 return cmp_null(sym_l, sym_r); 343 344 if (sym_l == sym_r) 345 return 0; 346 347 if (sym_l->inlined || sym_r->inlined) { 348 int ret = strcmp(sym_l->name, sym_r->name); 349 350 if (ret) 351 return ret; 352 if ((sym_l->start <= sym_r->end) && (sym_l->end >= sym_r->start)) 353 return 0; 354 } 355 356 if (sym_l->start != sym_r->start) 357 return (int64_t)(sym_r->start - sym_l->start); 358 359 return (int64_t)(sym_r->end - sym_l->end); 360} 361 362static int64_t 363sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) 364{ 365 int64_t ret; 366 367 if (!left->ms.sym && !right->ms.sym) 368 return _sort__addr_cmp(left->ip, right->ip); 369 370 /* 371 * comparing symbol address alone is not enough since it's a 372 * relative address within a dso. 373 */ 374 if (!hists__has(left->hists, dso)) { 375 ret = sort__dso_cmp(left, right); 376 if (ret != 0) 377 return ret; 378 } 379 380 return _sort__sym_cmp(left->ms.sym, right->ms.sym); 381} 382 383static int64_t 384sort__sym_sort(struct hist_entry *left, struct hist_entry *right) 385{ 386 if (!left->ms.sym || !right->ms.sym) 387 return cmp_null(left->ms.sym, right->ms.sym); 388 389 return strcmp(right->ms.sym->name, left->ms.sym->name); 390} 391 392static int _hist_entry__sym_snprintf(struct map_symbol *ms, 393 u64 ip, char level, char *bf, size_t size, 394 unsigned int width) 395{ 396 struct symbol *sym = ms->sym; 397 struct map *map = ms->map; 398 size_t ret = 0; 399 400 if (verbose > 0) { 401 struct dso *dso = map ? map__dso(map) : NULL; 402 char o = dso ? dso__symtab_origin(dso) : '!'; 403 u64 rip = ip; 404 405 if (dso && dso__kernel(dso) && dso__adjust_symbols(dso)) 406 rip = map__unmap_ip(map, ip); 407 408 ret += repsep_snprintf(bf, size, "%-#*llx %c ", 409 BITS_PER_LONG / 4 + 2, rip, o); 410 } 411 412 ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level); 413 if (sym && map) { 414 if (sym->type == STT_OBJECT) { 415 ret += repsep_snprintf(bf + ret, size - ret, "%s", sym->name); 416 ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx", 417 ip - map__unmap_ip(map, sym->start)); 418 } else { 419 ret += repsep_snprintf(bf + ret, size - ret, "%.*s", 420 width - ret, 421 sym->name); 422 if (sym->inlined) 423 ret += repsep_snprintf(bf + ret, size - ret, 424 " (inlined)"); 425 } 426 } else { 427 size_t len = BITS_PER_LONG / 4; 428 ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx", 429 len, ip); 430 } 431 432 return ret; 433} 434 435int hist_entry__sym_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) 436{ 437 return _hist_entry__sym_snprintf(&he->ms, he->ip, 438 he->level, bf, size, width); 439} 440 441static int hist_entry__sym_filter(struct hist_entry *he, int type, const void *arg) 442{ 443 const char *sym = arg; 444 445 if (type != HIST_FILTER__SYMBOL) 446 return -1; 447 448 return sym && (!he->ms.sym || !strstr(he->ms.sym->name, sym)); 449} 450 451struct sort_entry sort_sym = { 452 .se_header = "Symbol", 453 .se_cmp = sort__sym_cmp, 454 .se_sort = sort__sym_sort, 455 .se_snprintf = hist_entry__sym_snprintf, 456 .se_filter = hist_entry__sym_filter, 457 .se_width_idx = HISTC_SYMBOL, 458}; 459 460/* --sort symoff */ 461 462static int64_t 463sort__symoff_cmp(struct hist_entry *left, struct hist_entry *right) 464{ 465 int64_t ret; 466 467 ret = sort__sym_cmp(left, right); 468 if (ret) 469 return ret; 470 471 return left->ip - right->ip; 472} 473 474static int64_t 475sort__symoff_sort(struct hist_entry *left, struct hist_entry *right) 476{ 477 int64_t ret; 478 479 ret = sort__sym_sort(left, right); 480 if (ret) 481 return ret; 482 483 return left->ip - right->ip; 484} 485 486static int 487hist_entry__symoff_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) 488{ 489 struct symbol *sym = he->ms.sym; 490 491 if (sym == NULL) 492 return repsep_snprintf(bf, size, "[%c] %-#.*llx", he->level, width - 4, he->ip); 493 494 return repsep_snprintf(bf, size, "[%c] %s+0x%llx", he->level, sym->name, he->ip - sym->start); 495} 496 497struct sort_entry sort_sym_offset = { 498 .se_header = "Symbol Offset", 499 .se_cmp = sort__symoff_cmp, 500 .se_sort = sort__symoff_sort, 501 .se_snprintf = hist_entry__symoff_snprintf, 502 .se_filter = hist_entry__sym_filter, 503 .se_width_idx = HISTC_SYMBOL_OFFSET, 504}; 505 506/* --sort srcline */ 507 508char *hist_entry__srcline(struct hist_entry *he) 509{ 510 return map__srcline(he->ms.map, he->ip, he->ms.sym); 511} 512 513static int64_t 514sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right) 515{ 516 int64_t ret; 517 518 ret = _sort__addr_cmp(left->ip, right->ip); 519 if (ret) 520 return ret; 521 522 return sort__dso_cmp(left, right); 523} 524 525static int64_t 526sort__srcline_collapse(struct hist_entry *left, struct hist_entry *right) 527{ 528 if (!left->srcline) 529 left->srcline = hist_entry__srcline(left); 530 if (!right->srcline) 531 right->srcline = hist_entry__srcline(right); 532 533 return strcmp(right->srcline, left->srcline); 534} 535 536static int64_t 537sort__srcline_sort(struct hist_entry *left, struct hist_entry *right) 538{ 539 return sort__srcline_collapse(left, right); 540} 541 542static void 543sort__srcline_init(struct hist_entry *he) 544{ 545 if (!he->srcline) 546 he->srcline = hist_entry__srcline(he); 547} 548 549static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf, 550 size_t size, unsigned int width) 551{ 552 return repsep_snprintf(bf, size, "%-.*s", width, he->srcline); 553} 554 555struct sort_entry sort_srcline = { 556 .se_header = "Source:Line", 557 .se_cmp = sort__srcline_cmp, 558 .se_collapse = sort__srcline_collapse, 559 .se_sort = sort__srcline_sort, 560 .se_init = sort__srcline_init, 561 .se_snprintf = hist_entry__srcline_snprintf, 562 .se_width_idx = HISTC_SRCLINE, 563}; 564 565/* --sort srcline_from */ 566 567static char *addr_map_symbol__srcline(struct addr_map_symbol *ams) 568{ 569 return map__srcline(ams->ms.map, ams->al_addr, ams->ms.sym); 570} 571 572static int64_t 573sort__srcline_from_cmp(struct hist_entry *left, struct hist_entry *right) 574{ 575 return left->branch_info->from.addr - right->branch_info->from.addr; 576} 577 578static int64_t 579sort__srcline_from_collapse(struct hist_entry *left, struct hist_entry *right) 580{ 581 if (!left->branch_info->srcline_from) 582 left->branch_info->srcline_from = addr_map_symbol__srcline(&left->branch_info->from); 583 584 if (!right->branch_info->srcline_from) 585 right->branch_info->srcline_from = addr_map_symbol__srcline(&right->branch_info->from); 586 587 return strcmp(right->branch_info->srcline_from, left->branch_info->srcline_from); 588} 589 590static int64_t 591sort__srcline_from_sort(struct hist_entry *left, struct hist_entry *right) 592{ 593 return sort__srcline_from_collapse(left, right); 594} 595 596static void sort__srcline_from_init(struct hist_entry *he) 597{ 598 if (!he->branch_info->srcline_from) 599 he->branch_info->srcline_from = addr_map_symbol__srcline(&he->branch_info->from); 600} 601 602static int hist_entry__srcline_from_snprintf(struct hist_entry *he, char *bf, 603 size_t size, unsigned int width) 604{ 605 return repsep_snprintf(bf, size, "%-*.*s", width, width, he->branch_info->srcline_from); 606} 607 608struct sort_entry sort_srcline_from = { 609 .se_header = "From Source:Line", 610 .se_cmp = sort__srcline_from_cmp, 611 .se_collapse = sort__srcline_from_collapse, 612 .se_sort = sort__srcline_from_sort, 613 .se_init = sort__srcline_from_init, 614 .se_snprintf = hist_entry__srcline_from_snprintf, 615 .se_width_idx = HISTC_SRCLINE_FROM, 616}; 617 618/* --sort srcline_to */ 619 620static int64_t 621sort__srcline_to_cmp(struct hist_entry *left, struct hist_entry *right) 622{ 623 return left->branch_info->to.addr - right->branch_info->to.addr; 624} 625 626static int64_t 627sort__srcline_to_collapse(struct hist_entry *left, struct hist_entry *right) 628{ 629 if (!left->branch_info->srcline_to) 630 left->branch_info->srcline_to = addr_map_symbol__srcline(&left->branch_info->to); 631 632 if (!right->branch_info->srcline_to) 633 right->branch_info->srcline_to = addr_map_symbol__srcline(&right->branch_info->to); 634 635 return strcmp(right->branch_info->srcline_to, left->branch_info->srcline_to); 636} 637 638static int64_t 639sort__srcline_to_sort(struct hist_entry *left, struct hist_entry *right) 640{ 641 return sort__srcline_to_collapse(left, right); 642} 643 644static void sort__srcline_to_init(struct hist_entry *he) 645{ 646 if (!he->branch_info->srcline_to) 647 he->branch_info->srcline_to = addr_map_symbol__srcline(&he->branch_info->to); 648} 649 650static int hist_entry__srcline_to_snprintf(struct hist_entry *he, char *bf, 651 size_t size, unsigned int width) 652{ 653 return repsep_snprintf(bf, size, "%-*.*s", width, width, he->branch_info->srcline_to); 654} 655 656struct sort_entry sort_srcline_to = { 657 .se_header = "To Source:Line", 658 .se_cmp = sort__srcline_to_cmp, 659 .se_collapse = sort__srcline_to_collapse, 660 .se_sort = sort__srcline_to_sort, 661 .se_init = sort__srcline_to_init, 662 .se_snprintf = hist_entry__srcline_to_snprintf, 663 .se_width_idx = HISTC_SRCLINE_TO, 664}; 665 666static int hist_entry__sym_ipc_snprintf(struct hist_entry *he, char *bf, 667 size_t size, unsigned int width) 668{ 669 670 struct symbol *sym = he->ms.sym; 671 struct annotated_branch *branch; 672 double ipc = 0.0, coverage = 0.0; 673 char tmp[64]; 674 675 if (!sym) 676 return repsep_snprintf(bf, size, "%-*s", width, "-"); 677 678 branch = symbol__annotation(sym)->branch; 679 680 if (branch && branch->hit_cycles) 681 ipc = branch->hit_insn / ((double)branch->hit_cycles); 682 683 if (branch && branch->total_insn) { 684 coverage = branch->cover_insn * 100.0 / 685 ((double)branch->total_insn); 686 } 687 688 snprintf(tmp, sizeof(tmp), "%-5.2f [%5.1f%%]", ipc, coverage); 689 return repsep_snprintf(bf, size, "%-*s", width, tmp); 690} 691 692struct sort_entry sort_sym_ipc = { 693 .se_header = "IPC [IPC Coverage]", 694 .se_cmp = sort__sym_cmp, 695 .se_snprintf = hist_entry__sym_ipc_snprintf, 696 .se_width_idx = HISTC_SYMBOL_IPC, 697}; 698 699static int hist_entry__sym_ipc_null_snprintf(struct hist_entry *he 700 __maybe_unused, 701 char *bf, size_t size, 702 unsigned int width) 703{ 704 char tmp[64]; 705 706 snprintf(tmp, sizeof(tmp), "%-5s %2s", "-", "-"); 707 return repsep_snprintf(bf, size, "%-*s", width, tmp); 708} 709 710struct sort_entry sort_sym_ipc_null = { 711 .se_header = "IPC [IPC Coverage]", 712 .se_cmp = sort__sym_cmp, 713 .se_snprintf = hist_entry__sym_ipc_null_snprintf, 714 .se_width_idx = HISTC_SYMBOL_IPC, 715}; 716 717/* --sort callchain_branch_predicted */ 718 719static int64_t 720sort__callchain_branch_predicted_cmp(struct hist_entry *left __maybe_unused, 721 struct hist_entry *right __maybe_unused) 722{ 723 return 0; 724} 725 726static int hist_entry__callchain_branch_predicted_snprintf( 727 struct hist_entry *he, char *bf, size_t size, unsigned int width) 728{ 729 u64 branch_count, predicted_count; 730 double percent = 0.0; 731 char str[32]; 732 733 callchain_branch_counts(he->callchain, &branch_count, 734 &predicted_count, NULL, NULL); 735 736 if (branch_count) 737 percent = predicted_count * 100.0 / branch_count; 738 739 snprintf(str, sizeof(str), "%.1f%%", percent); 740 return repsep_snprintf(bf, size, "%-*.*s", width, width, str); 741} 742 743struct sort_entry sort_callchain_branch_predicted = { 744 .se_header = "Predicted", 745 .se_cmp = sort__callchain_branch_predicted_cmp, 746 .se_snprintf = hist_entry__callchain_branch_predicted_snprintf, 747 .se_width_idx = HISTC_CALLCHAIN_BRANCH_PREDICTED, 748}; 749 750/* --sort callchain_branch_abort */ 751 752static int64_t 753sort__callchain_branch_abort_cmp(struct hist_entry *left __maybe_unused, 754 struct hist_entry *right __maybe_unused) 755{ 756 return 0; 757} 758 759static int hist_entry__callchain_branch_abort_snprintf(struct hist_entry *he, 760 char *bf, size_t size, 761 unsigned int width) 762{ 763 u64 branch_count, abort_count; 764 char str[32]; 765 766 callchain_branch_counts(he->callchain, &branch_count, 767 NULL, &abort_count, NULL); 768 769 snprintf(str, sizeof(str), "%" PRId64, abort_count); 770 return repsep_snprintf(bf, size, "%-*.*s", width, width, str); 771} 772 773struct sort_entry sort_callchain_branch_abort = { 774 .se_header = "Abort", 775 .se_cmp = sort__callchain_branch_abort_cmp, 776 .se_snprintf = hist_entry__callchain_branch_abort_snprintf, 777 .se_width_idx = HISTC_CALLCHAIN_BRANCH_ABORT, 778}; 779 780/* --sort callchain_branch_cycles */ 781 782static int64_t 783sort__callchain_branch_cycles_cmp(struct hist_entry *left __maybe_unused, 784 struct hist_entry *right __maybe_unused) 785{ 786 return 0; 787} 788 789static int hist_entry__callchain_branch_cycles_snprintf(struct hist_entry *he, 790 char *bf, size_t size, 791 unsigned int width) 792{ 793 u64 branch_count, cycles_count, cycles = 0; 794 char str[32]; 795 796 callchain_branch_counts(he->callchain, &branch_count, 797 NULL, NULL, &cycles_count); 798 799 if (branch_count) 800 cycles = cycles_count / branch_count; 801 802 snprintf(str, sizeof(str), "%" PRId64 "", cycles); 803 return repsep_snprintf(bf, size, "%-*.*s", width, width, str); 804} 805 806struct sort_entry sort_callchain_branch_cycles = { 807 .se_header = "Cycles", 808 .se_cmp = sort__callchain_branch_cycles_cmp, 809 .se_snprintf = hist_entry__callchain_branch_cycles_snprintf, 810 .se_width_idx = HISTC_CALLCHAIN_BRANCH_CYCLES, 811}; 812 813/* --sort srcfile */ 814 815static char no_srcfile[1]; 816 817static char *hist_entry__get_srcfile(struct hist_entry *e) 818{ 819 char *sf, *p; 820 struct map *map = e->ms.map; 821 822 if (!map) 823 return no_srcfile; 824 825 sf = __get_srcline(map__dso(map), map__rip_2objdump(map, e->ip), 826 e->ms.sym, false, true, true, e->ip); 827 if (sf == SRCLINE_UNKNOWN) 828 return no_srcfile; 829 p = strchr(sf, ':'); 830 if (p && *sf) { 831 *p = 0; 832 return sf; 833 } 834 free(sf); 835 return no_srcfile; 836} 837 838static int64_t 839sort__srcfile_cmp(struct hist_entry *left, struct hist_entry *right) 840{ 841 return sort__srcline_cmp(left, right); 842} 843 844static int64_t 845sort__srcfile_collapse(struct hist_entry *left, struct hist_entry *right) 846{ 847 if (!left->srcfile) 848 left->srcfile = hist_entry__get_srcfile(left); 849 if (!right->srcfile) 850 right->srcfile = hist_entry__get_srcfile(right); 851 852 return strcmp(right->srcfile, left->srcfile); 853} 854 855static int64_t 856sort__srcfile_sort(struct hist_entry *left, struct hist_entry *right) 857{ 858 return sort__srcfile_collapse(left, right); 859} 860 861static void sort__srcfile_init(struct hist_entry *he) 862{ 863 if (!he->srcfile) 864 he->srcfile = hist_entry__get_srcfile(he); 865} 866 867static int hist_entry__srcfile_snprintf(struct hist_entry *he, char *bf, 868 size_t size, unsigned int width) 869{ 870 return repsep_snprintf(bf, size, "%-.*s", width, he->srcfile); 871} 872 873struct sort_entry sort_srcfile = { 874 .se_header = "Source File", 875 .se_cmp = sort__srcfile_cmp, 876 .se_collapse = sort__srcfile_collapse, 877 .se_sort = sort__srcfile_sort, 878 .se_init = sort__srcfile_init, 879 .se_snprintf = hist_entry__srcfile_snprintf, 880 .se_width_idx = HISTC_SRCFILE, 881}; 882 883/* --sort parent */ 884 885static int64_t 886sort__parent_cmp(struct hist_entry *left, struct hist_entry *right) 887{ 888 struct symbol *sym_l = left->parent; 889 struct symbol *sym_r = right->parent; 890 891 if (!sym_l || !sym_r) 892 return cmp_null(sym_l, sym_r); 893 894 return strcmp(sym_r->name, sym_l->name); 895} 896 897static int hist_entry__parent_snprintf(struct hist_entry *he, char *bf, 898 size_t size, unsigned int width) 899{ 900 return repsep_snprintf(bf, size, "%-*.*s", width, width, 901 he->parent ? he->parent->name : "[other]"); 902} 903 904struct sort_entry sort_parent = { 905 .se_header = "Parent symbol", 906 .se_cmp = sort__parent_cmp, 907 .se_snprintf = hist_entry__parent_snprintf, 908 .se_width_idx = HISTC_PARENT, 909}; 910 911/* --sort cpu */ 912 913static int64_t 914sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right) 915{ 916 return right->cpu - left->cpu; 917} 918 919static int hist_entry__cpu_snprintf(struct hist_entry *he, char *bf, 920 size_t size, unsigned int width) 921{ 922 return repsep_snprintf(bf, size, "%*.*d", width, width, he->cpu); 923} 924 925struct sort_entry sort_cpu = { 926 .se_header = "CPU", 927 .se_cmp = sort__cpu_cmp, 928 .se_snprintf = hist_entry__cpu_snprintf, 929 .se_width_idx = HISTC_CPU, 930}; 931 932/* --sort parallelism */ 933 934static int64_t 935sort__parallelism_cmp(struct hist_entry *left, struct hist_entry *right) 936{ 937 return right->parallelism - left->parallelism; 938} 939 940static int hist_entry__parallelism_filter(struct hist_entry *he, int type, const void *arg) 941{ 942 const unsigned long *parallelism_filter = arg; 943 944 if (type != HIST_FILTER__PARALLELISM) 945 return -1; 946 947 return test_bit(he->parallelism, parallelism_filter); 948} 949 950static int hist_entry__parallelism_snprintf(struct hist_entry *he, char *bf, 951 size_t size, unsigned int width) 952{ 953 return repsep_snprintf(bf, size, "%*d", width, he->parallelism); 954} 955 956struct sort_entry sort_parallelism = { 957 .se_header = "Parallelism", 958 .se_cmp = sort__parallelism_cmp, 959 .se_filter = hist_entry__parallelism_filter, 960 .se_snprintf = hist_entry__parallelism_snprintf, 961 .se_width_idx = HISTC_PARALLELISM, 962}; 963 964/* --sort cgroup_id */ 965 966static int64_t _sort__cgroup_dev_cmp(u64 left_dev, u64 right_dev) 967{ 968 return (int64_t)(right_dev - left_dev); 969} 970 971static int64_t _sort__cgroup_inode_cmp(u64 left_ino, u64 right_ino) 972{ 973 return (int64_t)(right_ino - left_ino); 974} 975 976static int64_t 977sort__cgroup_id_cmp(struct hist_entry *left, struct hist_entry *right) 978{ 979 int64_t ret; 980 981 ret = _sort__cgroup_dev_cmp(right->cgroup_id.dev, left->cgroup_id.dev); 982 if (ret != 0) 983 return ret; 984 985 return _sort__cgroup_inode_cmp(right->cgroup_id.ino, 986 left->cgroup_id.ino); 987} 988 989static int hist_entry__cgroup_id_snprintf(struct hist_entry *he, 990 char *bf, size_t size, 991 unsigned int width __maybe_unused) 992{ 993 return repsep_snprintf(bf, size, "%lu/0x%lx", he->cgroup_id.dev, 994 he->cgroup_id.ino); 995} 996 997struct sort_entry sort_cgroup_id = { 998 .se_header = "cgroup id (dev/inode)", 999 .se_cmp = sort__cgroup_id_cmp, 1000 .se_snprintf = hist_entry__cgroup_id_snprintf, 1001 .se_width_idx = HISTC_CGROUP_ID, 1002}; 1003 1004/* --sort cgroup */ 1005 1006static int64_t 1007sort__cgroup_cmp(struct hist_entry *left, struct hist_entry *right) 1008{ 1009 return right->cgroup - left->cgroup; 1010} 1011 1012static int hist_entry__cgroup_snprintf(struct hist_entry *he, 1013 char *bf, size_t size, 1014 unsigned int width __maybe_unused) 1015{ 1016 const char *cgrp_name = "N/A"; 1017 1018 if (he->cgroup) { 1019 struct cgroup *cgrp = cgroup__find(maps__machine(he->ms.maps)->env, 1020 he->cgroup); 1021 if (cgrp != NULL) 1022 cgrp_name = cgrp->name; 1023 else 1024 cgrp_name = "unknown"; 1025 } 1026 1027 return repsep_snprintf(bf, size, "%s", cgrp_name); 1028} 1029 1030struct sort_entry sort_cgroup = { 1031 .se_header = "Cgroup", 1032 .se_cmp = sort__cgroup_cmp, 1033 .se_snprintf = hist_entry__cgroup_snprintf, 1034 .se_width_idx = HISTC_CGROUP, 1035}; 1036 1037/* --sort socket */ 1038 1039static int64_t 1040sort__socket_cmp(struct hist_entry *left, struct hist_entry *right) 1041{ 1042 return right->socket - left->socket; 1043} 1044 1045static int hist_entry__socket_snprintf(struct hist_entry *he, char *bf, 1046 size_t size, unsigned int width) 1047{ 1048 return repsep_snprintf(bf, size, "%*.*d", width, width-3, he->socket); 1049} 1050 1051static int hist_entry__socket_filter(struct hist_entry *he, int type, const void *arg) 1052{ 1053 int sk = *(const int *)arg; 1054 1055 if (type != HIST_FILTER__SOCKET) 1056 return -1; 1057 1058 return sk >= 0 && he->socket != sk; 1059} 1060 1061struct sort_entry sort_socket = { 1062 .se_header = "Socket", 1063 .se_cmp = sort__socket_cmp, 1064 .se_snprintf = hist_entry__socket_snprintf, 1065 .se_filter = hist_entry__socket_filter, 1066 .se_width_idx = HISTC_SOCKET, 1067}; 1068 1069/* --sort time */ 1070 1071static int64_t 1072sort__time_cmp(struct hist_entry *left, struct hist_entry *right) 1073{ 1074 return right->time - left->time; 1075} 1076 1077static int hist_entry__time_snprintf(struct hist_entry *he, char *bf, 1078 size_t size, unsigned int width) 1079{ 1080 char he_time[32]; 1081 1082 if (symbol_conf.nanosecs) 1083 timestamp__scnprintf_nsec(he->time, he_time, 1084 sizeof(he_time)); 1085 else 1086 timestamp__scnprintf_usec(he->time, he_time, 1087 sizeof(he_time)); 1088 1089 return repsep_snprintf(bf, size, "%-.*s", width, he_time); 1090} 1091 1092struct sort_entry sort_time = { 1093 .se_header = "Time", 1094 .se_cmp = sort__time_cmp, 1095 .se_snprintf = hist_entry__time_snprintf, 1096 .se_width_idx = HISTC_TIME, 1097}; 1098 1099/* --sort trace */ 1100 1101#ifdef HAVE_LIBTRACEEVENT 1102static char *get_trace_output(struct hist_entry *he) 1103{ 1104 struct trace_seq seq; 1105 struct evsel *evsel; 1106 struct tep_record rec = { 1107 .data = he->raw_data, 1108 .size = he->raw_size, 1109 }; 1110 struct tep_event *tp_format; 1111 1112 evsel = hists_to_evsel(he->hists); 1113 1114 trace_seq_init(&seq); 1115 tp_format = evsel__tp_format(evsel); 1116 if (tp_format) { 1117 if (symbol_conf.raw_trace) 1118 tep_print_fields(&seq, he->raw_data, he->raw_size, tp_format); 1119 else 1120 tep_print_event(tp_format->tep, &seq, &rec, "%s", TEP_PRINT_INFO); 1121 } 1122 1123 /* 1124 * Trim the buffer, it starts at 4KB and we're not going to 1125 * add anything more to this buffer. 1126 */ 1127 return realloc(seq.buffer, seq.len + 1); 1128} 1129 1130static int64_t 1131sort__trace_cmp(struct hist_entry *left, struct hist_entry *right) 1132{ 1133 struct evsel *evsel; 1134 1135 evsel = hists_to_evsel(left->hists); 1136 if (evsel->core.attr.type != PERF_TYPE_TRACEPOINT) 1137 return 0; 1138 1139 if (left->trace_output == NULL) 1140 left->trace_output = get_trace_output(left); 1141 if (right->trace_output == NULL) 1142 right->trace_output = get_trace_output(right); 1143 1144 return strcmp(right->trace_output, left->trace_output); 1145} 1146 1147static int hist_entry__trace_snprintf(struct hist_entry *he, char *bf, 1148 size_t size, unsigned int width) 1149{ 1150 struct evsel *evsel; 1151 1152 evsel = hists_to_evsel(he->hists); 1153 if (evsel->core.attr.type != PERF_TYPE_TRACEPOINT) 1154 return scnprintf(bf, size, "%-.*s", width, "N/A"); 1155 1156 if (he->trace_output == NULL) 1157 he->trace_output = get_trace_output(he); 1158 return repsep_snprintf(bf, size, "%-.*s", width, he->trace_output); 1159} 1160 1161struct sort_entry sort_trace = { 1162 .se_header = "Trace output", 1163 .se_cmp = sort__trace_cmp, 1164 .se_snprintf = hist_entry__trace_snprintf, 1165 .se_width_idx = HISTC_TRACE, 1166}; 1167#endif /* HAVE_LIBTRACEEVENT */ 1168 1169/* sort keys for branch stacks */ 1170 1171static int64_t 1172sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right) 1173{ 1174 if (!left->branch_info || !right->branch_info) 1175 return cmp_null(left->branch_info, right->branch_info); 1176 1177 return _sort__dso_cmp(left->branch_info->from.ms.map, 1178 right->branch_info->from.ms.map); 1179} 1180 1181static int hist_entry__dso_from_snprintf(struct hist_entry *he, char *bf, 1182 size_t size, unsigned int width) 1183{ 1184 if (he->branch_info) 1185 return _hist_entry__dso_snprintf(he->branch_info->from.ms.map, 1186 bf, size, width); 1187 else 1188 return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A"); 1189} 1190 1191static int hist_entry__dso_from_filter(struct hist_entry *he, int type, 1192 const void *arg) 1193{ 1194 const struct dso *dso = arg; 1195 1196 if (type != HIST_FILTER__DSO) 1197 return -1; 1198 1199 return dso && (!he->branch_info || !he->branch_info->from.ms.map || 1200 map__dso(he->branch_info->from.ms.map) != dso); 1201} 1202 1203static int64_t 1204sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right) 1205{ 1206 if (!left->branch_info || !right->branch_info) 1207 return cmp_null(left->branch_info, right->branch_info); 1208 1209 return _sort__dso_cmp(left->branch_info->to.ms.map, 1210 right->branch_info->to.ms.map); 1211} 1212 1213static int hist_entry__dso_to_snprintf(struct hist_entry *he, char *bf, 1214 size_t size, unsigned int width) 1215{ 1216 if (he->branch_info) 1217 return _hist_entry__dso_snprintf(he->branch_info->to.ms.map, 1218 bf, size, width); 1219 else 1220 return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A"); 1221} 1222 1223static int hist_entry__dso_to_filter(struct hist_entry *he, int type, 1224 const void *arg) 1225{ 1226 const struct dso *dso = arg; 1227 1228 if (type != HIST_FILTER__DSO) 1229 return -1; 1230 1231 return dso && (!he->branch_info || !he->branch_info->to.ms.map || 1232 map__dso(he->branch_info->to.ms.map) != dso); 1233} 1234 1235static int64_t 1236sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right) 1237{ 1238 struct addr_map_symbol *from_l, *from_r; 1239 1240 if (!left->branch_info || !right->branch_info) 1241 return cmp_null(left->branch_info, right->branch_info); 1242 1243 from_l = &left->branch_info->from; 1244 from_r = &right->branch_info->from; 1245 1246 if (!from_l->ms.sym && !from_r->ms.sym) 1247 return _sort__addr_cmp(from_l->addr, from_r->addr); 1248 1249 return _sort__sym_cmp(from_l->ms.sym, from_r->ms.sym); 1250} 1251 1252static int64_t 1253sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right) 1254{ 1255 struct addr_map_symbol *to_l, *to_r; 1256 1257 if (!left->branch_info || !right->branch_info) 1258 return cmp_null(left->branch_info, right->branch_info); 1259 1260 to_l = &left->branch_info->to; 1261 to_r = &right->branch_info->to; 1262 1263 if (!to_l->ms.sym && !to_r->ms.sym) 1264 return _sort__addr_cmp(to_l->addr, to_r->addr); 1265 1266 return _sort__sym_cmp(to_l->ms.sym, to_r->ms.sym); 1267} 1268 1269static int hist_entry__sym_from_snprintf(struct hist_entry *he, char *bf, 1270 size_t size, unsigned int width) 1271{ 1272 if (he->branch_info) { 1273 struct addr_map_symbol *from = &he->branch_info->from; 1274 1275 return _hist_entry__sym_snprintf(&from->ms, from->al_addr, 1276 from->al_level, bf, size, width); 1277 } 1278 1279 return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A"); 1280} 1281 1282static int hist_entry__sym_to_snprintf(struct hist_entry *he, char *bf, 1283 size_t size, unsigned int width) 1284{ 1285 if (he->branch_info) { 1286 struct addr_map_symbol *to = &he->branch_info->to; 1287 1288 return _hist_entry__sym_snprintf(&to->ms, to->al_addr, 1289 to->al_level, bf, size, width); 1290 } 1291 1292 return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A"); 1293} 1294 1295static int hist_entry__sym_from_filter(struct hist_entry *he, int type, 1296 const void *arg) 1297{ 1298 const char *sym = arg; 1299 1300 if (type != HIST_FILTER__SYMBOL) 1301 return -1; 1302 1303 return sym && !(he->branch_info && he->branch_info->from.ms.sym && 1304 strstr(he->branch_info->from.ms.sym->name, sym)); 1305} 1306 1307static int hist_entry__sym_to_filter(struct hist_entry *he, int type, 1308 const void *arg) 1309{ 1310 const char *sym = arg; 1311 1312 if (type != HIST_FILTER__SYMBOL) 1313 return -1; 1314 1315 return sym && !(he->branch_info && he->branch_info->to.ms.sym && 1316 strstr(he->branch_info->to.ms.sym->name, sym)); 1317} 1318 1319struct sort_entry sort_dso_from = { 1320 .se_header = "Source Shared Object", 1321 .se_cmp = sort__dso_from_cmp, 1322 .se_snprintf = hist_entry__dso_from_snprintf, 1323 .se_filter = hist_entry__dso_from_filter, 1324 .se_width_idx = HISTC_DSO_FROM, 1325}; 1326 1327struct sort_entry sort_dso_to = { 1328 .se_header = "Target Shared Object", 1329 .se_cmp = sort__dso_to_cmp, 1330 .se_snprintf = hist_entry__dso_to_snprintf, 1331 .se_filter = hist_entry__dso_to_filter, 1332 .se_width_idx = HISTC_DSO_TO, 1333}; 1334 1335struct sort_entry sort_sym_from = { 1336 .se_header = "Source Symbol", 1337 .se_cmp = sort__sym_from_cmp, 1338 .se_snprintf = hist_entry__sym_from_snprintf, 1339 .se_filter = hist_entry__sym_from_filter, 1340 .se_width_idx = HISTC_SYMBOL_FROM, 1341}; 1342 1343struct sort_entry sort_sym_to = { 1344 .se_header = "Target Symbol", 1345 .se_cmp = sort__sym_to_cmp, 1346 .se_snprintf = hist_entry__sym_to_snprintf, 1347 .se_filter = hist_entry__sym_to_filter, 1348 .se_width_idx = HISTC_SYMBOL_TO, 1349}; 1350 1351static int _hist_entry__addr_snprintf(struct map_symbol *ms, 1352 u64 ip, char level, char *bf, size_t size, 1353 unsigned int width) 1354{ 1355 struct symbol *sym = ms->sym; 1356 struct map *map = ms->map; 1357 size_t ret = 0, offs; 1358 1359 ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level); 1360 if (sym && map) { 1361 if (sym->type == STT_OBJECT) { 1362 ret += repsep_snprintf(bf + ret, size - ret, "%s", sym->name); 1363 ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx", 1364 ip - map__unmap_ip(map, sym->start)); 1365 } else { 1366 ret += repsep_snprintf(bf + ret, size - ret, "%.*s", 1367 width - ret, 1368 sym->name); 1369 offs = ip - sym->start; 1370 if (offs) 1371 ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx", offs); 1372 } 1373 } else { 1374 size_t len = BITS_PER_LONG / 4; 1375 ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx", 1376 len, ip); 1377 } 1378 1379 return ret; 1380} 1381 1382static int hist_entry__addr_from_snprintf(struct hist_entry *he, char *bf, 1383 size_t size, unsigned int width) 1384{ 1385 if (he->branch_info) { 1386 struct addr_map_symbol *from = &he->branch_info->from; 1387 1388 return _hist_entry__addr_snprintf(&from->ms, from->al_addr, 1389 he->level, bf, size, width); 1390 } 1391 1392 return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A"); 1393} 1394 1395static int hist_entry__addr_to_snprintf(struct hist_entry *he, char *bf, 1396 size_t size, unsigned int width) 1397{ 1398 if (he->branch_info) { 1399 struct addr_map_symbol *to = &he->branch_info->to; 1400 1401 return _hist_entry__addr_snprintf(&to->ms, to->al_addr, 1402 he->level, bf, size, width); 1403 } 1404 1405 return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A"); 1406} 1407 1408static int64_t 1409sort__addr_from_cmp(struct hist_entry *left, struct hist_entry *right) 1410{ 1411 struct addr_map_symbol *from_l; 1412 struct addr_map_symbol *from_r; 1413 int64_t ret; 1414 1415 if (!left->branch_info || !right->branch_info) 1416 return cmp_null(left->branch_info, right->branch_info); 1417 1418 from_l = &left->branch_info->from; 1419 from_r = &right->branch_info->from; 1420 1421 /* 1422 * comparing symbol address alone is not enough since it's a 1423 * relative address within a dso. 1424 */ 1425 ret = _sort__dso_cmp(from_l->ms.map, from_r->ms.map); 1426 if (ret != 0) 1427 return ret; 1428 1429 return _sort__addr_cmp(from_l->addr, from_r->addr); 1430} 1431 1432static int64_t 1433sort__addr_to_cmp(struct hist_entry *left, struct hist_entry *right) 1434{ 1435 struct addr_map_symbol *to_l; 1436 struct addr_map_symbol *to_r; 1437 int64_t ret; 1438 1439 if (!left->branch_info || !right->branch_info) 1440 return cmp_null(left->branch_info, right->branch_info); 1441 1442 to_l = &left->branch_info->to; 1443 to_r = &right->branch_info->to; 1444 1445 /* 1446 * comparing symbol address alone is not enough since it's a 1447 * relative address within a dso. 1448 */ 1449 ret = _sort__dso_cmp(to_l->ms.map, to_r->ms.map); 1450 if (ret != 0) 1451 return ret; 1452 1453 return _sort__addr_cmp(to_l->addr, to_r->addr); 1454} 1455 1456struct sort_entry sort_addr_from = { 1457 .se_header = "Source Address", 1458 .se_cmp = sort__addr_from_cmp, 1459 .se_snprintf = hist_entry__addr_from_snprintf, 1460 .se_filter = hist_entry__sym_from_filter, /* shared with sym_from */ 1461 .se_width_idx = HISTC_ADDR_FROM, 1462}; 1463 1464struct sort_entry sort_addr_to = { 1465 .se_header = "Target Address", 1466 .se_cmp = sort__addr_to_cmp, 1467 .se_snprintf = hist_entry__addr_to_snprintf, 1468 .se_filter = hist_entry__sym_to_filter, /* shared with sym_to */ 1469 .se_width_idx = HISTC_ADDR_TO, 1470}; 1471 1472 1473static int64_t 1474sort__mispredict_cmp(struct hist_entry *left, struct hist_entry *right) 1475{ 1476 unsigned char mp, p; 1477 1478 if (!left->branch_info || !right->branch_info) 1479 return cmp_null(left->branch_info, right->branch_info); 1480 1481 mp = left->branch_info->flags.mispred != right->branch_info->flags.mispred; 1482 p = left->branch_info->flags.predicted != right->branch_info->flags.predicted; 1483 return mp || p; 1484} 1485 1486static int hist_entry__mispredict_snprintf(struct hist_entry *he, char *bf, 1487 size_t size, unsigned int width){ 1488 static const char *out = "N/A"; 1489 1490 if (he->branch_info) { 1491 if (he->branch_info->flags.predicted) 1492 out = "N"; 1493 else if (he->branch_info->flags.mispred) 1494 out = "Y"; 1495 } 1496 1497 return repsep_snprintf(bf, size, "%-*.*s", width, width, out); 1498} 1499 1500static int64_t 1501sort__cycles_cmp(struct hist_entry *left, struct hist_entry *right) 1502{ 1503 if (!left->branch_info || !right->branch_info) 1504 return cmp_null(left->branch_info, right->branch_info); 1505 1506 return left->branch_info->flags.cycles - 1507 right->branch_info->flags.cycles; 1508} 1509 1510static int hist_entry__cycles_snprintf(struct hist_entry *he, char *bf, 1511 size_t size, unsigned int width) 1512{ 1513 if (!he->branch_info) 1514 return scnprintf(bf, size, "%-.*s", width, "N/A"); 1515 if (he->branch_info->flags.cycles == 0) 1516 return repsep_snprintf(bf, size, "%-*s", width, "-"); 1517 return repsep_snprintf(bf, size, "%-*hd", width, 1518 he->branch_info->flags.cycles); 1519} 1520 1521struct sort_entry sort_cycles = { 1522 .se_header = "Basic Block Cycles", 1523 .se_cmp = sort__cycles_cmp, 1524 .se_snprintf = hist_entry__cycles_snprintf, 1525 .se_width_idx = HISTC_CYCLES, 1526}; 1527 1528/* --sort daddr_sym */ 1529int64_t 1530sort__daddr_cmp(struct hist_entry *left, struct hist_entry *right) 1531{ 1532 uint64_t l = 0, r = 0; 1533 1534 if (left->mem_info) 1535 l = mem_info__daddr(left->mem_info)->addr; 1536 if (right->mem_info) 1537 r = mem_info__daddr(right->mem_info)->addr; 1538 1539 return (int64_t)(r - l); 1540} 1541 1542static int hist_entry__daddr_snprintf(struct hist_entry *he, char *bf, 1543 size_t size, unsigned int width) 1544{ 1545 uint64_t addr = 0; 1546 struct map_symbol *ms = NULL; 1547 1548 if (he->mem_info) { 1549 addr = mem_info__daddr(he->mem_info)->addr; 1550 ms = &mem_info__daddr(he->mem_info)->ms; 1551 } 1552 return _hist_entry__sym_snprintf(ms, addr, he->level, bf, size, width); 1553} 1554 1555int64_t 1556sort__iaddr_cmp(struct hist_entry *left, struct hist_entry *right) 1557{ 1558 uint64_t l = 0, r = 0; 1559 1560 if (left->mem_info) 1561 l = mem_info__iaddr(left->mem_info)->addr; 1562 if (right->mem_info) 1563 r = mem_info__iaddr(right->mem_info)->addr; 1564 1565 return (int64_t)(r - l); 1566} 1567 1568static int hist_entry__iaddr_snprintf(struct hist_entry *he, char *bf, 1569 size_t size, unsigned int width) 1570{ 1571 uint64_t addr = 0; 1572 struct map_symbol *ms = NULL; 1573 1574 if (he->mem_info) { 1575 addr = mem_info__iaddr(he->mem_info)->addr; 1576 ms = &mem_info__iaddr(he->mem_info)->ms; 1577 } 1578 return _hist_entry__sym_snprintf(ms, addr, he->level, bf, size, width); 1579} 1580 1581static int64_t 1582sort__dso_daddr_cmp(struct hist_entry *left, struct hist_entry *right) 1583{ 1584 struct map *map_l = NULL; 1585 struct map *map_r = NULL; 1586 1587 if (left->mem_info) 1588 map_l = mem_info__daddr(left->mem_info)->ms.map; 1589 if (right->mem_info) 1590 map_r = mem_info__daddr(right->mem_info)->ms.map; 1591 1592 return _sort__dso_cmp(map_l, map_r); 1593} 1594 1595static int hist_entry__dso_daddr_snprintf(struct hist_entry *he, char *bf, 1596 size_t size, unsigned int width) 1597{ 1598 struct map *map = NULL; 1599 1600 if (he->mem_info) 1601 map = mem_info__daddr(he->mem_info)->ms.map; 1602 1603 return _hist_entry__dso_snprintf(map, bf, size, width); 1604} 1605 1606static int64_t 1607sort__locked_cmp(struct hist_entry *left, struct hist_entry *right) 1608{ 1609 union perf_mem_data_src data_src_l; 1610 union perf_mem_data_src data_src_r; 1611 1612 if (left->mem_info) 1613 data_src_l = *mem_info__data_src(left->mem_info); 1614 else 1615 data_src_l.mem_lock = PERF_MEM_LOCK_NA; 1616 1617 if (right->mem_info) 1618 data_src_r = *mem_info__data_src(right->mem_info); 1619 else 1620 data_src_r.mem_lock = PERF_MEM_LOCK_NA; 1621 1622 return (int64_t)(data_src_r.mem_lock - data_src_l.mem_lock); 1623} 1624 1625static int hist_entry__locked_snprintf(struct hist_entry *he, char *bf, 1626 size_t size, unsigned int width) 1627{ 1628 char out[10]; 1629 1630 perf_mem__lck_scnprintf(out, sizeof(out), he->mem_info); 1631 return repsep_snprintf(bf, size, "%.*s", width, out); 1632} 1633 1634static int64_t 1635sort__tlb_cmp(struct hist_entry *left, struct hist_entry *right) 1636{ 1637 union perf_mem_data_src data_src_l; 1638 union perf_mem_data_src data_src_r; 1639 1640 if (left->mem_info) 1641 data_src_l = *mem_info__data_src(left->mem_info); 1642 else 1643 data_src_l.mem_dtlb = PERF_MEM_TLB_NA; 1644 1645 if (right->mem_info) 1646 data_src_r = *mem_info__data_src(right->mem_info); 1647 else 1648 data_src_r.mem_dtlb = PERF_MEM_TLB_NA; 1649 1650 return (int64_t)(data_src_r.mem_dtlb - data_src_l.mem_dtlb); 1651} 1652 1653static int hist_entry__tlb_snprintf(struct hist_entry *he, char *bf, 1654 size_t size, unsigned int width) 1655{ 1656 char out[64]; 1657 1658 perf_mem__tlb_scnprintf(out, sizeof(out), he->mem_info); 1659 return repsep_snprintf(bf, size, "%-*s", width, out); 1660} 1661 1662static int64_t 1663sort__lvl_cmp(struct hist_entry *left, struct hist_entry *right) 1664{ 1665 union perf_mem_data_src data_src_l; 1666 union perf_mem_data_src data_src_r; 1667 1668 if (left->mem_info) 1669 data_src_l = *mem_info__data_src(left->mem_info); 1670 else 1671 data_src_l.mem_lvl = PERF_MEM_LVL_NA; 1672 1673 if (right->mem_info) 1674 data_src_r = *mem_info__data_src(right->mem_info); 1675 else 1676 data_src_r.mem_lvl = PERF_MEM_LVL_NA; 1677 1678 return (int64_t)(data_src_r.mem_lvl - data_src_l.mem_lvl); 1679} 1680 1681static int hist_entry__lvl_snprintf(struct hist_entry *he, char *bf, 1682 size_t size, unsigned int width) 1683{ 1684 char out[64]; 1685 1686 perf_mem__lvl_scnprintf(out, sizeof(out), he->mem_info); 1687 return repsep_snprintf(bf, size, "%-*s", width, out); 1688} 1689 1690static int64_t 1691sort__snoop_cmp(struct hist_entry *left, struct hist_entry *right) 1692{ 1693 union perf_mem_data_src data_src_l; 1694 union perf_mem_data_src data_src_r; 1695 1696 if (left->mem_info) 1697 data_src_l = *mem_info__data_src(left->mem_info); 1698 else 1699 data_src_l.mem_snoop = PERF_MEM_SNOOP_NA; 1700 1701 if (right->mem_info) 1702 data_src_r = *mem_info__data_src(right->mem_info); 1703 else 1704 data_src_r.mem_snoop = PERF_MEM_SNOOP_NA; 1705 1706 return (int64_t)(data_src_r.mem_snoop - data_src_l.mem_snoop); 1707} 1708 1709static int hist_entry__snoop_snprintf(struct hist_entry *he, char *bf, 1710 size_t size, unsigned int width) 1711{ 1712 char out[64]; 1713 1714 perf_mem__snp_scnprintf(out, sizeof(out), he->mem_info); 1715 return repsep_snprintf(bf, size, "%-*s", width, out); 1716} 1717 1718int64_t 1719sort__dcacheline_cmp(struct hist_entry *left, struct hist_entry *right) 1720{ 1721 u64 l, r; 1722 struct map *l_map, *r_map; 1723 struct dso *l_dso, *r_dso; 1724 int rc; 1725 1726 if (!left->mem_info) return -1; 1727 if (!right->mem_info) return 1; 1728 1729 /* group event types together */ 1730 if (left->cpumode > right->cpumode) return -1; 1731 if (left->cpumode < right->cpumode) return 1; 1732 1733 l_map = mem_info__daddr(left->mem_info)->ms.map; 1734 r_map = mem_info__daddr(right->mem_info)->ms.map; 1735 1736 /* if both are NULL, jump to sort on al_addr instead */ 1737 if (!l_map && !r_map) 1738 goto addr; 1739 1740 if (!l_map) return -1; 1741 if (!r_map) return 1; 1742 1743 l_dso = map__dso(l_map); 1744 r_dso = map__dso(r_map); 1745 rc = dso__cmp_id(l_dso, r_dso); 1746 if (rc) 1747 return rc; 1748 /* 1749 * Addresses with no major/minor numbers or build ID are assumed to be 1750 * anonymous in userspace. Sort those on pid then address. 1751 * 1752 * The kernel and non-zero major/minor mapped areas are 1753 * assumed to be unity mapped. Sort those on address. 1754 */ 1755 if (left->cpumode != PERF_RECORD_MISC_KERNEL && (map__flags(l_map) & MAP_SHARED) == 0) { 1756 const struct dso_id *dso_id = dso__id_const(l_dso); 1757 1758 if (!dso_id->mmap2_valid) 1759 dso_id = dso__id_const(r_dso); 1760 1761 if (!build_id__is_defined(&dso_id->build_id) && 1762 (!dso_id->mmap2_valid || (dso_id->maj == 0 && dso_id->min == 0))) { 1763 /* userspace anonymous */ 1764 1765 if (thread__pid(left->thread) > thread__pid(right->thread)) 1766 return -1; 1767 if (thread__pid(left->thread) < thread__pid(right->thread)) 1768 return 1; 1769 } 1770 } 1771 1772addr: 1773 /* al_addr does all the right addr - start + offset calculations */ 1774 l = cl_address(mem_info__daddr(left->mem_info)->al_addr, chk_double_cl); 1775 r = cl_address(mem_info__daddr(right->mem_info)->al_addr, chk_double_cl); 1776 1777 if (l > r) return -1; 1778 if (l < r) return 1; 1779 1780 return 0; 1781} 1782 1783static int hist_entry__dcacheline_snprintf(struct hist_entry *he, char *bf, 1784 size_t size, unsigned int width) 1785{ 1786 1787 uint64_t addr = 0; 1788 struct map_symbol *ms = NULL; 1789 char level = he->level; 1790 1791 if (he->mem_info) { 1792 struct map *map = mem_info__daddr(he->mem_info)->ms.map; 1793 struct dso *dso = map ? map__dso(map) : NULL; 1794 const struct dso_id *dso_id = dso ? dso__id_const(dso) : &dso_id_empty; 1795 1796 addr = cl_address(mem_info__daddr(he->mem_info)->al_addr, chk_double_cl); 1797 ms = &mem_info__daddr(he->mem_info)->ms; 1798 1799 /* print [s] for shared data mmaps */ 1800 if ((he->cpumode != PERF_RECORD_MISC_KERNEL) && 1801 map && !(map__prot(map) & PROT_EXEC) && 1802 (map__flags(map) & MAP_SHARED) && 1803 (!dso_id->mmap2_valid || (dso_id->maj == 0 && dso_id->min == 0))) 1804 level = 's'; 1805 else if (!map) 1806 level = 'X'; 1807 } 1808 return _hist_entry__sym_snprintf(ms, addr, level, bf, size, width); 1809} 1810 1811struct sort_entry sort_mispredict = { 1812 .se_header = "Branch Mispredicted", 1813 .se_cmp = sort__mispredict_cmp, 1814 .se_snprintf = hist_entry__mispredict_snprintf, 1815 .se_width_idx = HISTC_MISPREDICT, 1816}; 1817 1818static int64_t 1819sort__weight_cmp(struct hist_entry *left, struct hist_entry *right) 1820{ 1821 return left->weight - right->weight; 1822} 1823 1824static int hist_entry__local_weight_snprintf(struct hist_entry *he, char *bf, 1825 size_t size, unsigned int width) 1826{ 1827 return repsep_snprintf(bf, size, "%-*llu", width, he->weight); 1828} 1829 1830struct sort_entry sort_local_weight = { 1831 .se_header = "Local Weight", 1832 .se_cmp = sort__weight_cmp, 1833 .se_snprintf = hist_entry__local_weight_snprintf, 1834 .se_width_idx = HISTC_LOCAL_WEIGHT, 1835}; 1836 1837static int hist_entry__global_weight_snprintf(struct hist_entry *he, char *bf, 1838 size_t size, unsigned int width) 1839{ 1840 return repsep_snprintf(bf, size, "%-*llu", width, 1841 he->weight * he->stat.nr_events); 1842} 1843 1844struct sort_entry sort_global_weight = { 1845 .se_header = "Weight", 1846 .se_cmp = sort__weight_cmp, 1847 .se_snprintf = hist_entry__global_weight_snprintf, 1848 .se_width_idx = HISTC_GLOBAL_WEIGHT, 1849}; 1850 1851static int64_t 1852sort__ins_lat_cmp(struct hist_entry *left, struct hist_entry *right) 1853{ 1854 return left->ins_lat - right->ins_lat; 1855} 1856 1857static int hist_entry__local_ins_lat_snprintf(struct hist_entry *he, char *bf, 1858 size_t size, unsigned int width) 1859{ 1860 return repsep_snprintf(bf, size, "%-*u", width, he->ins_lat); 1861} 1862 1863struct sort_entry sort_local_ins_lat = { 1864 .se_header = "Local INSTR Latency", 1865 .se_cmp = sort__ins_lat_cmp, 1866 .se_snprintf = hist_entry__local_ins_lat_snprintf, 1867 .se_width_idx = HISTC_LOCAL_INS_LAT, 1868}; 1869 1870static int hist_entry__global_ins_lat_snprintf(struct hist_entry *he, char *bf, 1871 size_t size, unsigned int width) 1872{ 1873 return repsep_snprintf(bf, size, "%-*u", width, 1874 he->ins_lat * he->stat.nr_events); 1875} 1876 1877struct sort_entry sort_global_ins_lat = { 1878 .se_header = "INSTR Latency", 1879 .se_cmp = sort__ins_lat_cmp, 1880 .se_snprintf = hist_entry__global_ins_lat_snprintf, 1881 .se_width_idx = HISTC_GLOBAL_INS_LAT, 1882}; 1883 1884static int64_t 1885sort__p_stage_cyc_cmp(struct hist_entry *left, struct hist_entry *right) 1886{ 1887 return left->weight3 - right->weight3; 1888} 1889 1890static int hist_entry__global_p_stage_cyc_snprintf(struct hist_entry *he, char *bf, 1891 size_t size, unsigned int width) 1892{ 1893 return repsep_snprintf(bf, size, "%-*u", width, he->weight3 * he->stat.nr_events); 1894} 1895 1896 1897static int hist_entry__p_stage_cyc_snprintf(struct hist_entry *he, char *bf, 1898 size_t size, unsigned int width) 1899{ 1900 return repsep_snprintf(bf, size, "%-*u", width, he->weight3); 1901} 1902 1903struct sort_entry sort_local_p_stage_cyc = { 1904 .se_header = "Local Pipeline Stage Cycle", 1905 .se_cmp = sort__p_stage_cyc_cmp, 1906 .se_snprintf = hist_entry__p_stage_cyc_snprintf, 1907 .se_width_idx = HISTC_LOCAL_P_STAGE_CYC, 1908}; 1909 1910struct sort_entry sort_global_p_stage_cyc = { 1911 .se_header = "Pipeline Stage Cycle", 1912 .se_cmp = sort__p_stage_cyc_cmp, 1913 .se_snprintf = hist_entry__global_p_stage_cyc_snprintf, 1914 .se_width_idx = HISTC_GLOBAL_P_STAGE_CYC, 1915}; 1916 1917struct sort_entry sort_mem_daddr_sym = { 1918 .se_header = "Data Symbol", 1919 .se_cmp = sort__daddr_cmp, 1920 .se_snprintf = hist_entry__daddr_snprintf, 1921 .se_width_idx = HISTC_MEM_DADDR_SYMBOL, 1922}; 1923 1924struct sort_entry sort_mem_iaddr_sym = { 1925 .se_header = "Code Symbol", 1926 .se_cmp = sort__iaddr_cmp, 1927 .se_snprintf = hist_entry__iaddr_snprintf, 1928 .se_width_idx = HISTC_MEM_IADDR_SYMBOL, 1929}; 1930 1931struct sort_entry sort_mem_daddr_dso = { 1932 .se_header = "Data Object", 1933 .se_cmp = sort__dso_daddr_cmp, 1934 .se_snprintf = hist_entry__dso_daddr_snprintf, 1935 .se_width_idx = HISTC_MEM_DADDR_DSO, 1936}; 1937 1938struct sort_entry sort_mem_locked = { 1939 .se_header = "Locked", 1940 .se_cmp = sort__locked_cmp, 1941 .se_snprintf = hist_entry__locked_snprintf, 1942 .se_width_idx = HISTC_MEM_LOCKED, 1943}; 1944 1945struct sort_entry sort_mem_tlb = { 1946 .se_header = "TLB access", 1947 .se_cmp = sort__tlb_cmp, 1948 .se_snprintf = hist_entry__tlb_snprintf, 1949 .se_width_idx = HISTC_MEM_TLB, 1950}; 1951 1952struct sort_entry sort_mem_lvl = { 1953 .se_header = "Memory access", 1954 .se_cmp = sort__lvl_cmp, 1955 .se_snprintf = hist_entry__lvl_snprintf, 1956 .se_width_idx = HISTC_MEM_LVL, 1957}; 1958 1959struct sort_entry sort_mem_snoop = { 1960 .se_header = "Snoop", 1961 .se_cmp = sort__snoop_cmp, 1962 .se_snprintf = hist_entry__snoop_snprintf, 1963 .se_width_idx = HISTC_MEM_SNOOP, 1964}; 1965 1966struct sort_entry sort_mem_dcacheline = { 1967 .se_header = "Data Cacheline", 1968 .se_cmp = sort__dcacheline_cmp, 1969 .se_snprintf = hist_entry__dcacheline_snprintf, 1970 .se_width_idx = HISTC_MEM_DCACHELINE, 1971}; 1972 1973static int64_t 1974sort__blocked_cmp(struct hist_entry *left, struct hist_entry *right) 1975{ 1976 union perf_mem_data_src data_src_l; 1977 union perf_mem_data_src data_src_r; 1978 1979 if (left->mem_info) 1980 data_src_l = *mem_info__data_src(left->mem_info); 1981 else 1982 data_src_l.mem_blk = PERF_MEM_BLK_NA; 1983 1984 if (right->mem_info) 1985 data_src_r = *mem_info__data_src(right->mem_info); 1986 else 1987 data_src_r.mem_blk = PERF_MEM_BLK_NA; 1988 1989 return (int64_t)(data_src_r.mem_blk - data_src_l.mem_blk); 1990} 1991 1992static int hist_entry__blocked_snprintf(struct hist_entry *he, char *bf, 1993 size_t size, unsigned int width) 1994{ 1995 char out[16]; 1996 1997 perf_mem__blk_scnprintf(out, sizeof(out), he->mem_info); 1998 return repsep_snprintf(bf, size, "%.*s", width, out); 1999} 2000 2001struct sort_entry sort_mem_blocked = { 2002 .se_header = "Blocked", 2003 .se_cmp = sort__blocked_cmp, 2004 .se_snprintf = hist_entry__blocked_snprintf, 2005 .se_width_idx = HISTC_MEM_BLOCKED, 2006}; 2007 2008static int64_t 2009sort__phys_daddr_cmp(struct hist_entry *left, struct hist_entry *right) 2010{ 2011 uint64_t l = 0, r = 0; 2012 2013 if (left->mem_info) 2014 l = mem_info__daddr(left->mem_info)->phys_addr; 2015 if (right->mem_info) 2016 r = mem_info__daddr(right->mem_info)->phys_addr; 2017 2018 return (int64_t)(r - l); 2019} 2020 2021static int hist_entry__phys_daddr_snprintf(struct hist_entry *he, char *bf, 2022 size_t size, unsigned int width) 2023{ 2024 uint64_t addr = 0; 2025 size_t ret = 0; 2026 size_t len = BITS_PER_LONG / 4; 2027 2028 addr = mem_info__daddr(he->mem_info)->phys_addr; 2029 2030 ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", he->level); 2031 2032 ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx", len, addr); 2033 2034 ret += repsep_snprintf(bf + ret, size - ret, "%-*s", width - ret, ""); 2035 2036 if (ret > width) 2037 bf[width] = '\0'; 2038 2039 return width; 2040} 2041 2042struct sort_entry sort_mem_phys_daddr = { 2043 .se_header = "Data Physical Address", 2044 .se_cmp = sort__phys_daddr_cmp, 2045 .se_snprintf = hist_entry__phys_daddr_snprintf, 2046 .se_width_idx = HISTC_MEM_PHYS_DADDR, 2047}; 2048 2049static int64_t 2050sort__data_page_size_cmp(struct hist_entry *left, struct hist_entry *right) 2051{ 2052 uint64_t l = 0, r = 0; 2053 2054 if (left->mem_info) 2055 l = mem_info__daddr(left->mem_info)->data_page_size; 2056 if (right->mem_info) 2057 r = mem_info__daddr(right->mem_info)->data_page_size; 2058 2059 return (int64_t)(r - l); 2060} 2061 2062static int hist_entry__data_page_size_snprintf(struct hist_entry *he, char *bf, 2063 size_t size, unsigned int width) 2064{ 2065 char str[PAGE_SIZE_NAME_LEN]; 2066 2067 return repsep_snprintf(bf, size, "%-*s", width, 2068 get_page_size_name(mem_info__daddr(he->mem_info)->data_page_size, str)); 2069} 2070 2071struct sort_entry sort_mem_data_page_size = { 2072 .se_header = "Data Page Size", 2073 .se_cmp = sort__data_page_size_cmp, 2074 .se_snprintf = hist_entry__data_page_size_snprintf, 2075 .se_width_idx = HISTC_MEM_DATA_PAGE_SIZE, 2076}; 2077 2078static int64_t 2079sort__code_page_size_cmp(struct hist_entry *left, struct hist_entry *right) 2080{ 2081 uint64_t l = left->code_page_size; 2082 uint64_t r = right->code_page_size; 2083 2084 return (int64_t)(r - l); 2085} 2086 2087static int hist_entry__code_page_size_snprintf(struct hist_entry *he, char *bf, 2088 size_t size, unsigned int width) 2089{ 2090 char str[PAGE_SIZE_NAME_LEN]; 2091 2092 return repsep_snprintf(bf, size, "%-*s", width, 2093 get_page_size_name(he->code_page_size, str)); 2094} 2095 2096struct sort_entry sort_code_page_size = { 2097 .se_header = "Code Page Size", 2098 .se_cmp = sort__code_page_size_cmp, 2099 .se_snprintf = hist_entry__code_page_size_snprintf, 2100 .se_width_idx = HISTC_CODE_PAGE_SIZE, 2101}; 2102 2103static int64_t 2104sort__abort_cmp(struct hist_entry *left, struct hist_entry *right) 2105{ 2106 if (!left->branch_info || !right->branch_info) 2107 return cmp_null(left->branch_info, right->branch_info); 2108 2109 return left->branch_info->flags.abort != 2110 right->branch_info->flags.abort; 2111} 2112 2113static int hist_entry__abort_snprintf(struct hist_entry *he, char *bf, 2114 size_t size, unsigned int width) 2115{ 2116 static const char *out = "N/A"; 2117 2118 if (he->branch_info) { 2119 if (he->branch_info->flags.abort) 2120 out = "A"; 2121 else 2122 out = "."; 2123 } 2124 2125 return repsep_snprintf(bf, size, "%-*s", width, out); 2126} 2127 2128struct sort_entry sort_abort = { 2129 .se_header = "Transaction abort", 2130 .se_cmp = sort__abort_cmp, 2131 .se_snprintf = hist_entry__abort_snprintf, 2132 .se_width_idx = HISTC_ABORT, 2133}; 2134 2135static int64_t 2136sort__in_tx_cmp(struct hist_entry *left, struct hist_entry *right) 2137{ 2138 if (!left->branch_info || !right->branch_info) 2139 return cmp_null(left->branch_info, right->branch_info); 2140 2141 return left->branch_info->flags.in_tx != 2142 right->branch_info->flags.in_tx; 2143} 2144 2145static int hist_entry__in_tx_snprintf(struct hist_entry *he, char *bf, 2146 size_t size, unsigned int width) 2147{ 2148 static const char *out = "N/A"; 2149 2150 if (he->branch_info) { 2151 if (he->branch_info->flags.in_tx) 2152 out = "T"; 2153 else 2154 out = "."; 2155 } 2156 2157 return repsep_snprintf(bf, size, "%-*s", width, out); 2158} 2159 2160struct sort_entry sort_in_tx = { 2161 .se_header = "Branch in transaction", 2162 .se_cmp = sort__in_tx_cmp, 2163 .se_snprintf = hist_entry__in_tx_snprintf, 2164 .se_width_idx = HISTC_IN_TX, 2165}; 2166 2167static int64_t 2168sort__transaction_cmp(struct hist_entry *left, struct hist_entry *right) 2169{ 2170 return left->transaction - right->transaction; 2171} 2172 2173static inline char *add_str(char *p, const char *str) 2174{ 2175 strcpy(p, str); 2176 return p + strlen(str); 2177} 2178 2179static struct txbit { 2180 unsigned flag; 2181 const char *name; 2182 int skip_for_len; 2183} txbits[] = { 2184 { PERF_TXN_ELISION, "EL ", 0 }, 2185 { PERF_TXN_TRANSACTION, "TX ", 1 }, 2186 { PERF_TXN_SYNC, "SYNC ", 1 }, 2187 { PERF_TXN_ASYNC, "ASYNC ", 0 }, 2188 { PERF_TXN_RETRY, "RETRY ", 0 }, 2189 { PERF_TXN_CONFLICT, "CON ", 0 }, 2190 { PERF_TXN_CAPACITY_WRITE, "CAP-WRITE ", 1 }, 2191 { PERF_TXN_CAPACITY_READ, "CAP-READ ", 0 }, 2192 { 0, NULL, 0 } 2193}; 2194 2195int hist_entry__transaction_len(void) 2196{ 2197 int i; 2198 int len = 0; 2199 2200 for (i = 0; txbits[i].name; i++) { 2201 if (!txbits[i].skip_for_len) 2202 len += strlen(txbits[i].name); 2203 } 2204 len += 4; /* :XX<space> */ 2205 return len; 2206} 2207 2208static int hist_entry__transaction_snprintf(struct hist_entry *he, char *bf, 2209 size_t size, unsigned int width) 2210{ 2211 u64 t = he->transaction; 2212 char buf[128]; 2213 char *p = buf; 2214 int i; 2215 2216 buf[0] = 0; 2217 for (i = 0; txbits[i].name; i++) 2218 if (txbits[i].flag & t) 2219 p = add_str(p, txbits[i].name); 2220 if (t && !(t & (PERF_TXN_SYNC|PERF_TXN_ASYNC))) 2221 p = add_str(p, "NEITHER "); 2222 if (t & PERF_TXN_ABORT_MASK) { 2223 sprintf(p, ":%" PRIx64, 2224 (t & PERF_TXN_ABORT_MASK) >> 2225 PERF_TXN_ABORT_SHIFT); 2226 p += strlen(p); 2227 } 2228 2229 return repsep_snprintf(bf, size, "%-*s", width, buf); 2230} 2231 2232struct sort_entry sort_transaction = { 2233 .se_header = "Transaction ", 2234 .se_cmp = sort__transaction_cmp, 2235 .se_snprintf = hist_entry__transaction_snprintf, 2236 .se_width_idx = HISTC_TRANSACTION, 2237}; 2238 2239/* --sort symbol_size */ 2240 2241static int64_t _sort__sym_size_cmp(struct symbol *sym_l, struct symbol *sym_r) 2242{ 2243 int64_t size_l = sym_l != NULL ? symbol__size(sym_l) : 0; 2244 int64_t size_r = sym_r != NULL ? symbol__size(sym_r) : 0; 2245 2246 return size_l < size_r ? -1 : 2247 size_l == size_r ? 0 : 1; 2248} 2249 2250static int64_t 2251sort__sym_size_cmp(struct hist_entry *left, struct hist_entry *right) 2252{ 2253 return _sort__sym_size_cmp(right->ms.sym, left->ms.sym); 2254} 2255 2256static int _hist_entry__sym_size_snprintf(struct symbol *sym, char *bf, 2257 size_t bf_size, unsigned int width) 2258{ 2259 if (sym) 2260 return repsep_snprintf(bf, bf_size, "%*d", width, symbol__size(sym)); 2261 2262 return repsep_snprintf(bf, bf_size, "%*s", width, "unknown"); 2263} 2264 2265static int hist_entry__sym_size_snprintf(struct hist_entry *he, char *bf, 2266 size_t size, unsigned int width) 2267{ 2268 return _hist_entry__sym_size_snprintf(he->ms.sym, bf, size, width); 2269} 2270 2271struct sort_entry sort_sym_size = { 2272 .se_header = "Symbol size", 2273 .se_cmp = sort__sym_size_cmp, 2274 .se_snprintf = hist_entry__sym_size_snprintf, 2275 .se_width_idx = HISTC_SYM_SIZE, 2276}; 2277 2278/* --sort dso_size */ 2279 2280static int64_t _sort__dso_size_cmp(struct map *map_l, struct map *map_r) 2281{ 2282 int64_t size_l = map_l != NULL ? map__size(map_l) : 0; 2283 int64_t size_r = map_r != NULL ? map__size(map_r) : 0; 2284 2285 return size_l < size_r ? -1 : 2286 size_l == size_r ? 0 : 1; 2287} 2288 2289static int64_t 2290sort__dso_size_cmp(struct hist_entry *left, struct hist_entry *right) 2291{ 2292 return _sort__dso_size_cmp(right->ms.map, left->ms.map); 2293} 2294 2295static int _hist_entry__dso_size_snprintf(struct map *map, char *bf, 2296 size_t bf_size, unsigned int width) 2297{ 2298 if (map && map__dso(map)) 2299 return repsep_snprintf(bf, bf_size, "%*d", width, map__size(map)); 2300 2301 return repsep_snprintf(bf, bf_size, "%*s", width, "unknown"); 2302} 2303 2304static int hist_entry__dso_size_snprintf(struct hist_entry *he, char *bf, 2305 size_t size, unsigned int width) 2306{ 2307 return _hist_entry__dso_size_snprintf(he->ms.map, bf, size, width); 2308} 2309 2310struct sort_entry sort_dso_size = { 2311 .se_header = "DSO size", 2312 .se_cmp = sort__dso_size_cmp, 2313 .se_snprintf = hist_entry__dso_size_snprintf, 2314 .se_width_idx = HISTC_DSO_SIZE, 2315}; 2316 2317/* --sort addr */ 2318 2319static int64_t 2320sort__addr_cmp(struct hist_entry *left, struct hist_entry *right) 2321{ 2322 u64 left_ip = left->ip; 2323 u64 right_ip = right->ip; 2324 struct map *left_map = left->ms.map; 2325 struct map *right_map = right->ms.map; 2326 2327 if (left_map) 2328 left_ip = map__unmap_ip(left_map, left_ip); 2329 if (right_map) 2330 right_ip = map__unmap_ip(right_map, right_ip); 2331 2332 return _sort__addr_cmp(left_ip, right_ip); 2333} 2334 2335static int hist_entry__addr_snprintf(struct hist_entry *he, char *bf, 2336 size_t size, unsigned int width) 2337{ 2338 u64 ip = he->ip; 2339 struct map *map = he->ms.map; 2340 2341 if (map) 2342 ip = map__unmap_ip(map, ip); 2343 2344 return repsep_snprintf(bf, size, "%-#*llx", width, ip); 2345} 2346 2347struct sort_entry sort_addr = { 2348 .se_header = "Address", 2349 .se_cmp = sort__addr_cmp, 2350 .se_snprintf = hist_entry__addr_snprintf, 2351 .se_width_idx = HISTC_ADDR, 2352}; 2353 2354/* --sort type */ 2355 2356struct annotated_data_type unknown_type = { 2357 .self = { 2358 .type_name = (char *)"(unknown)", 2359 .children = LIST_HEAD_INIT(unknown_type.self.children), 2360 }, 2361}; 2362 2363static int64_t 2364sort__type_cmp(struct hist_entry *left, struct hist_entry *right) 2365{ 2366 return sort__addr_cmp(left, right); 2367} 2368 2369static void sort__type_init(struct hist_entry *he) 2370{ 2371 if (he->mem_type) 2372 return; 2373 2374 he->mem_type = hist_entry__get_data_type(he); 2375 if (he->mem_type == NULL) { 2376 he->mem_type = &unknown_type; 2377 he->mem_type_off = 0; 2378 } 2379} 2380 2381static int64_t 2382sort__type_collapse(struct hist_entry *left, struct hist_entry *right) 2383{ 2384 struct annotated_data_type *left_type = left->mem_type; 2385 struct annotated_data_type *right_type = right->mem_type; 2386 2387 if (!left_type) { 2388 sort__type_init(left); 2389 left_type = left->mem_type; 2390 } 2391 2392 if (!right_type) { 2393 sort__type_init(right); 2394 right_type = right->mem_type; 2395 } 2396 2397 return strcmp(left_type->self.type_name, right_type->self.type_name); 2398} 2399 2400static int64_t 2401sort__type_sort(struct hist_entry *left, struct hist_entry *right) 2402{ 2403 return sort__type_collapse(left, right); 2404} 2405 2406static int hist_entry__type_snprintf(struct hist_entry *he, char *bf, 2407 size_t size, unsigned int width) 2408{ 2409 return repsep_snprintf(bf, size, "%-*s", width, he->mem_type->self.type_name); 2410} 2411 2412struct sort_entry sort_type = { 2413 .se_header = "Data Type", 2414 .se_cmp = sort__type_cmp, 2415 .se_collapse = sort__type_collapse, 2416 .se_sort = sort__type_sort, 2417 .se_init = sort__type_init, 2418 .se_snprintf = hist_entry__type_snprintf, 2419 .se_width_idx = HISTC_TYPE, 2420}; 2421 2422/* --sort typeoff */ 2423 2424static int64_t 2425sort__typeoff_sort(struct hist_entry *left, struct hist_entry *right) 2426{ 2427 struct annotated_data_type *left_type = left->mem_type; 2428 struct annotated_data_type *right_type = right->mem_type; 2429 int64_t ret; 2430 2431 if (!left_type) { 2432 sort__type_init(left); 2433 left_type = left->mem_type; 2434 } 2435 2436 if (!right_type) { 2437 sort__type_init(right); 2438 right_type = right->mem_type; 2439 } 2440 2441 ret = strcmp(left_type->self.type_name, right_type->self.type_name); 2442 if (ret) 2443 return ret; 2444 return left->mem_type_off - right->mem_type_off; 2445} 2446 2447static int hist_entry__typeoff_snprintf(struct hist_entry *he, char *bf, 2448 size_t size, unsigned int width __maybe_unused) 2449{ 2450 struct annotated_data_type *he_type = he->mem_type; 2451 char buf[4096]; 2452 2453 if (he_type == &unknown_type || he_type == &stackop_type || 2454 he_type == &canary_type) 2455 return repsep_snprintf(bf, size, "%s", he_type->self.type_name); 2456 2457 if (!annotated_data_type__get_member_name(he_type, buf, sizeof(buf), 2458 he->mem_type_off)) 2459 scnprintf(buf, sizeof(buf), "no field"); 2460 2461 return repsep_snprintf(bf, size, "%s +%#x (%s)", he_type->self.type_name, 2462 he->mem_type_off, buf); 2463} 2464 2465struct sort_entry sort_type_offset = { 2466 .se_header = "Data Type Offset", 2467 .se_cmp = sort__type_cmp, 2468 .se_collapse = sort__typeoff_sort, 2469 .se_sort = sort__typeoff_sort, 2470 .se_init = sort__type_init, 2471 .se_snprintf = hist_entry__typeoff_snprintf, 2472 .se_width_idx = HISTC_TYPE_OFFSET, 2473}; 2474 2475/* --sort typecln */ 2476 2477/* TODO: use actual value in the system */ 2478#define TYPE_CACHELINE_SIZE 64 2479 2480static int64_t 2481sort__typecln_sort(struct hist_entry *left, struct hist_entry *right) 2482{ 2483 struct annotated_data_type *left_type = left->mem_type; 2484 struct annotated_data_type *right_type = right->mem_type; 2485 int64_t left_cln, right_cln; 2486 int64_t ret; 2487 2488 if (!left_type) { 2489 sort__type_init(left); 2490 left_type = left->mem_type; 2491 } 2492 2493 if (!right_type) { 2494 sort__type_init(right); 2495 right_type = right->mem_type; 2496 } 2497 2498 ret = strcmp(left_type->self.type_name, right_type->self.type_name); 2499 if (ret) 2500 return ret; 2501 2502 left_cln = left->mem_type_off / TYPE_CACHELINE_SIZE; 2503 right_cln = right->mem_type_off / TYPE_CACHELINE_SIZE; 2504 return left_cln - right_cln; 2505} 2506 2507static int hist_entry__typecln_snprintf(struct hist_entry *he, char *bf, 2508 size_t size, unsigned int width __maybe_unused) 2509{ 2510 struct annotated_data_type *he_type = he->mem_type; 2511 2512 return repsep_snprintf(bf, size, "%s: cache-line %d", he_type->self.type_name, 2513 he->mem_type_off / TYPE_CACHELINE_SIZE); 2514} 2515 2516struct sort_entry sort_type_cacheline = { 2517 .se_header = "Data Type Cacheline", 2518 .se_cmp = sort__type_cmp, 2519 .se_collapse = sort__typecln_sort, 2520 .se_sort = sort__typecln_sort, 2521 .se_init = sort__type_init, 2522 .se_snprintf = hist_entry__typecln_snprintf, 2523 .se_width_idx = HISTC_TYPE_CACHELINE, 2524}; 2525 2526 2527struct sort_dimension { 2528 const char *name; 2529 struct sort_entry *entry; 2530 int taken; 2531}; 2532 2533static int arch_support_sort_key(const char *sort_key, struct perf_env *env) 2534{ 2535 const char *arch = perf_env__arch(env); 2536 2537 if (!strcmp("x86", arch) || !strcmp("powerpc", arch)) { 2538 if (!strcmp(sort_key, "p_stage_cyc")) 2539 return 1; 2540 if (!strcmp(sort_key, "local_p_stage_cyc")) 2541 return 1; 2542 } 2543 return 0; 2544} 2545 2546static const char *arch_perf_header_entry(const char *se_header, struct perf_env *env) 2547{ 2548 const char *arch = perf_env__arch(env); 2549 2550 if (!strcmp("x86", arch)) { 2551 if (!strcmp(se_header, "Local Pipeline Stage Cycle")) 2552 return "Local Retire Latency"; 2553 else if (!strcmp(se_header, "Pipeline Stage Cycle")) 2554 return "Retire Latency"; 2555 } else if (!strcmp("powerpc", arch)) { 2556 if (!strcmp(se_header, "Local INSTR Latency")) 2557 return "Finish Cyc"; 2558 else if (!strcmp(se_header, "INSTR Latency")) 2559 return "Global Finish_cyc"; 2560 else if (!strcmp(se_header, "Local Pipeline Stage Cycle")) 2561 return "Dispatch Cyc"; 2562 else if (!strcmp(se_header, "Pipeline Stage Cycle")) 2563 return "Global Dispatch_cyc"; 2564 } 2565 return se_header; 2566} 2567 2568static void sort_dimension_add_dynamic_header(struct sort_dimension *sd, struct perf_env *env) 2569{ 2570 sd->entry->se_header = arch_perf_header_entry(sd->entry->se_header, env); 2571} 2572 2573#define DIM(d, n, func) [d] = { .name = n, .entry = &(func) } 2574 2575static struct sort_dimension common_sort_dimensions[] = { 2576 DIM(SORT_PID, "pid", sort_thread), 2577 DIM(SORT_TGID, "tgid", sort_tgid), 2578 DIM(SORT_COMM, "comm", sort_comm), 2579 DIM(SORT_DSO, "dso", sort_dso), 2580 DIM(SORT_SYM, "symbol", sort_sym), 2581 DIM(SORT_PARENT, "parent", sort_parent), 2582 DIM(SORT_CPU, "cpu", sort_cpu), 2583 DIM(SORT_SOCKET, "socket", sort_socket), 2584 DIM(SORT_SRCLINE, "srcline", sort_srcline), 2585 DIM(SORT_SRCFILE, "srcfile", sort_srcfile), 2586 DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight), 2587 DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight), 2588 DIM(SORT_TRANSACTION, "transaction", sort_transaction), 2589#ifdef HAVE_LIBTRACEEVENT 2590 DIM(SORT_TRACE, "trace", sort_trace), 2591#endif 2592 DIM(SORT_SYM_SIZE, "symbol_size", sort_sym_size), 2593 DIM(SORT_DSO_SIZE, "dso_size", sort_dso_size), 2594 DIM(SORT_CGROUP, "cgroup", sort_cgroup), 2595 DIM(SORT_CGROUP_ID, "cgroup_id", sort_cgroup_id), 2596 DIM(SORT_SYM_IPC_NULL, "ipc_null", sort_sym_ipc_null), 2597 DIM(SORT_TIME, "time", sort_time), 2598 DIM(SORT_CODE_PAGE_SIZE, "code_page_size", sort_code_page_size), 2599 DIM(SORT_LOCAL_INS_LAT, "local_ins_lat", sort_local_ins_lat), 2600 DIM(SORT_GLOBAL_INS_LAT, "ins_lat", sort_global_ins_lat), 2601 DIM(SORT_LOCAL_PIPELINE_STAGE_CYC, "local_p_stage_cyc", sort_local_p_stage_cyc), 2602 DIM(SORT_GLOBAL_PIPELINE_STAGE_CYC, "p_stage_cyc", sort_global_p_stage_cyc), 2603 DIM(SORT_ADDR, "addr", sort_addr), 2604 DIM(SORT_LOCAL_RETIRE_LAT, "local_retire_lat", sort_local_p_stage_cyc), 2605 DIM(SORT_GLOBAL_RETIRE_LAT, "retire_lat", sort_global_p_stage_cyc), 2606 DIM(SORT_SIMD, "simd", sort_simd), 2607 DIM(SORT_ANNOTATE_DATA_TYPE, "type", sort_type), 2608 DIM(SORT_ANNOTATE_DATA_TYPE_OFFSET, "typeoff", sort_type_offset), 2609 DIM(SORT_SYM_OFFSET, "symoff", sort_sym_offset), 2610 DIM(SORT_ANNOTATE_DATA_TYPE_CACHELINE, "typecln", sort_type_cacheline), 2611 DIM(SORT_PARALLELISM, "parallelism", sort_parallelism), 2612}; 2613 2614#undef DIM 2615 2616#define DIM(d, n, func) [d - __SORT_BRANCH_STACK] = { .name = n, .entry = &(func) } 2617 2618static struct sort_dimension bstack_sort_dimensions[] = { 2619 DIM(SORT_DSO_FROM, "dso_from", sort_dso_from), 2620 DIM(SORT_DSO_TO, "dso_to", sort_dso_to), 2621 DIM(SORT_SYM_FROM, "symbol_from", sort_sym_from), 2622 DIM(SORT_SYM_TO, "symbol_to", sort_sym_to), 2623 DIM(SORT_MISPREDICT, "mispredict", sort_mispredict), 2624 DIM(SORT_IN_TX, "in_tx", sort_in_tx), 2625 DIM(SORT_ABORT, "abort", sort_abort), 2626 DIM(SORT_CYCLES, "cycles", sort_cycles), 2627 DIM(SORT_SRCLINE_FROM, "srcline_from", sort_srcline_from), 2628 DIM(SORT_SRCLINE_TO, "srcline_to", sort_srcline_to), 2629 DIM(SORT_SYM_IPC, "ipc_lbr", sort_sym_ipc), 2630 DIM(SORT_ADDR_FROM, "addr_from", sort_addr_from), 2631 DIM(SORT_ADDR_TO, "addr_to", sort_addr_to), 2632 DIM(SORT_CALLCHAIN_BRANCH_PREDICTED, 2633 "callchain_branch_predicted", 2634 sort_callchain_branch_predicted), 2635 DIM(SORT_CALLCHAIN_BRANCH_ABORT, 2636 "callchain_branch_abort", 2637 sort_callchain_branch_abort), 2638 DIM(SORT_CALLCHAIN_BRANCH_CYCLES, 2639 "callchain_branch_cycles", 2640 sort_callchain_branch_cycles) 2641}; 2642 2643#undef DIM 2644 2645#define DIM(d, n, func) [d - __SORT_MEMORY_MODE] = { .name = n, .entry = &(func) } 2646 2647static struct sort_dimension memory_sort_dimensions[] = { 2648 DIM(SORT_MEM_DADDR_SYMBOL, "symbol_daddr", sort_mem_daddr_sym), 2649 DIM(SORT_MEM_IADDR_SYMBOL, "symbol_iaddr", sort_mem_iaddr_sym), 2650 DIM(SORT_MEM_DADDR_DSO, "dso_daddr", sort_mem_daddr_dso), 2651 DIM(SORT_MEM_LOCKED, "locked", sort_mem_locked), 2652 DIM(SORT_MEM_TLB, "tlb", sort_mem_tlb), 2653 DIM(SORT_MEM_LVL, "mem", sort_mem_lvl), 2654 DIM(SORT_MEM_SNOOP, "snoop", sort_mem_snoop), 2655 DIM(SORT_MEM_DCACHELINE, "dcacheline", sort_mem_dcacheline), 2656 DIM(SORT_MEM_PHYS_DADDR, "phys_daddr", sort_mem_phys_daddr), 2657 DIM(SORT_MEM_DATA_PAGE_SIZE, "data_page_size", sort_mem_data_page_size), 2658 DIM(SORT_MEM_BLOCKED, "blocked", sort_mem_blocked), 2659}; 2660 2661#undef DIM 2662 2663struct hpp_dimension { 2664 const char *name; 2665 struct perf_hpp_fmt *fmt; 2666 int taken; 2667 int was_taken; 2668 int mem_mode; 2669}; 2670 2671#define DIM(d, n) { .name = n, .fmt = &perf_hpp__format[d], } 2672#define DIM_MEM(d, n) { .name = n, .fmt = &perf_hpp__format[d], .mem_mode = 1, } 2673 2674static struct hpp_dimension hpp_sort_dimensions[] = { 2675 DIM(PERF_HPP__OVERHEAD, "overhead"), 2676 DIM(PERF_HPP__LATENCY, "latency"), 2677 DIM(PERF_HPP__OVERHEAD_SYS, "overhead_sys"), 2678 DIM(PERF_HPP__OVERHEAD_US, "overhead_us"), 2679 DIM(PERF_HPP__OVERHEAD_GUEST_SYS, "overhead_guest_sys"), 2680 DIM(PERF_HPP__OVERHEAD_GUEST_US, "overhead_guest_us"), 2681 DIM(PERF_HPP__OVERHEAD_ACC, "overhead_children"), 2682 DIM(PERF_HPP__LATENCY_ACC, "latency_children"), 2683 DIM(PERF_HPP__SAMPLES, "sample"), 2684 DIM(PERF_HPP__PERIOD, "period"), 2685 DIM(PERF_HPP__WEIGHT1, "weight1"), 2686 DIM(PERF_HPP__WEIGHT2, "weight2"), 2687 DIM(PERF_HPP__WEIGHT3, "weight3"), 2688 /* aliases for weight_struct */ 2689 DIM(PERF_HPP__WEIGHT2, "ins_lat"), 2690 DIM(PERF_HPP__WEIGHT3, "retire_lat"), 2691 DIM(PERF_HPP__WEIGHT3, "p_stage_cyc"), 2692 /* used for output only when SORT_MODE__MEM */ 2693 DIM_MEM(PERF_HPP__MEM_STAT_OP, "op"), 2694 DIM_MEM(PERF_HPP__MEM_STAT_CACHE, "cache"), 2695 DIM_MEM(PERF_HPP__MEM_STAT_MEMORY, "memory"), 2696 DIM_MEM(PERF_HPP__MEM_STAT_SNOOP, "snoop"), 2697 DIM_MEM(PERF_HPP__MEM_STAT_DTLB, "dtlb"), 2698}; 2699 2700#undef DIM_MEM 2701#undef DIM 2702 2703struct hpp_sort_entry { 2704 struct perf_hpp_fmt hpp; 2705 struct sort_entry *se; 2706}; 2707 2708void perf_hpp__reset_sort_width(struct perf_hpp_fmt *fmt, struct hists *hists) 2709{ 2710 struct hpp_sort_entry *hse; 2711 2712 if (!perf_hpp__is_sort_entry(fmt)) 2713 return; 2714 2715 hse = container_of(fmt, struct hpp_sort_entry, hpp); 2716 hists__new_col_len(hists, hse->se->se_width_idx, strlen(fmt->name)); 2717} 2718 2719static int __sort__hpp_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, 2720 struct hists *hists, int line, 2721 int *span __maybe_unused) 2722{ 2723 struct hpp_sort_entry *hse; 2724 size_t len = fmt->user_len; 2725 const char *hdr = ""; 2726 2727 if (line == hists->hpp_list->nr_header_lines - 1) 2728 hdr = fmt->name; 2729 2730 hse = container_of(fmt, struct hpp_sort_entry, hpp); 2731 2732 if (!len) 2733 len = hists__col_len(hists, hse->se->se_width_idx); 2734 2735 return scnprintf(hpp->buf, hpp->size, "%-*.*s", len, len, hdr); 2736} 2737 2738static int __sort__hpp_width(struct perf_hpp_fmt *fmt, 2739 struct perf_hpp *hpp __maybe_unused, 2740 struct hists *hists) 2741{ 2742 struct hpp_sort_entry *hse; 2743 size_t len = fmt->user_len; 2744 2745 hse = container_of(fmt, struct hpp_sort_entry, hpp); 2746 2747 if (!len) 2748 len = hists__col_len(hists, hse->se->se_width_idx); 2749 2750 return len; 2751} 2752 2753static int __sort__hpp_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, 2754 struct hist_entry *he) 2755{ 2756 struct hpp_sort_entry *hse; 2757 size_t len = fmt->user_len; 2758 2759 hse = container_of(fmt, struct hpp_sort_entry, hpp); 2760 2761 if (!len) 2762 len = hists__col_len(he->hists, hse->se->se_width_idx); 2763 2764 return hse->se->se_snprintf(he, hpp->buf, hpp->size, len); 2765} 2766 2767static int64_t __sort__hpp_cmp(struct perf_hpp_fmt *fmt, 2768 struct hist_entry *a, struct hist_entry *b) 2769{ 2770 struct hpp_sort_entry *hse; 2771 2772 hse = container_of(fmt, struct hpp_sort_entry, hpp); 2773 return hse->se->se_cmp(a, b); 2774} 2775 2776static int64_t __sort__hpp_collapse(struct perf_hpp_fmt *fmt, 2777 struct hist_entry *a, struct hist_entry *b) 2778{ 2779 struct hpp_sort_entry *hse; 2780 int64_t (*collapse_fn)(struct hist_entry *, struct hist_entry *); 2781 2782 hse = container_of(fmt, struct hpp_sort_entry, hpp); 2783 collapse_fn = hse->se->se_collapse ?: hse->se->se_cmp; 2784 return collapse_fn(a, b); 2785} 2786 2787static int64_t __sort__hpp_sort(struct perf_hpp_fmt *fmt, 2788 struct hist_entry *a, struct hist_entry *b) 2789{ 2790 struct hpp_sort_entry *hse; 2791 int64_t (*sort_fn)(struct hist_entry *, struct hist_entry *); 2792 2793 hse = container_of(fmt, struct hpp_sort_entry, hpp); 2794 sort_fn = hse->se->se_sort ?: hse->se->se_cmp; 2795 return sort_fn(a, b); 2796} 2797 2798bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format) 2799{ 2800 return format->header == __sort__hpp_header; 2801} 2802 2803#define MK_SORT_ENTRY_CHK(key) \ 2804bool perf_hpp__is_ ## key ## _entry(struct perf_hpp_fmt *fmt) \ 2805{ \ 2806 struct hpp_sort_entry *hse; \ 2807 \ 2808 if (!perf_hpp__is_sort_entry(fmt)) \ 2809 return false; \ 2810 \ 2811 hse = container_of(fmt, struct hpp_sort_entry, hpp); \ 2812 return hse->se == &sort_ ## key ; \ 2813} 2814 2815#ifdef HAVE_LIBTRACEEVENT 2816MK_SORT_ENTRY_CHK(trace) 2817#else 2818bool perf_hpp__is_trace_entry(struct perf_hpp_fmt *fmt __maybe_unused) 2819{ 2820 return false; 2821} 2822#endif 2823MK_SORT_ENTRY_CHK(srcline) 2824MK_SORT_ENTRY_CHK(srcfile) 2825MK_SORT_ENTRY_CHK(thread) 2826MK_SORT_ENTRY_CHK(comm) 2827MK_SORT_ENTRY_CHK(dso) 2828MK_SORT_ENTRY_CHK(sym) 2829MK_SORT_ENTRY_CHK(parallelism) 2830 2831 2832static bool __sort__hpp_equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b) 2833{ 2834 struct hpp_sort_entry *hse_a; 2835 struct hpp_sort_entry *hse_b; 2836 2837 if (!perf_hpp__is_sort_entry(a) || !perf_hpp__is_sort_entry(b)) 2838 return false; 2839 2840 hse_a = container_of(a, struct hpp_sort_entry, hpp); 2841 hse_b = container_of(b, struct hpp_sort_entry, hpp); 2842 2843 return hse_a->se == hse_b->se; 2844} 2845 2846static void hse_free(struct perf_hpp_fmt *fmt) 2847{ 2848 struct hpp_sort_entry *hse; 2849 2850 hse = container_of(fmt, struct hpp_sort_entry, hpp); 2851 free(hse); 2852} 2853 2854static void hse_init(struct perf_hpp_fmt *fmt, struct hist_entry *he) 2855{ 2856 struct hpp_sort_entry *hse; 2857 2858 if (!perf_hpp__is_sort_entry(fmt)) 2859 return; 2860 2861 hse = container_of(fmt, struct hpp_sort_entry, hpp); 2862 2863 if (hse->se->se_init) 2864 hse->se->se_init(he); 2865} 2866 2867static struct hpp_sort_entry * 2868__sort_dimension__alloc_hpp(struct sort_dimension *sd, int level) 2869{ 2870 struct hpp_sort_entry *hse; 2871 2872 hse = malloc(sizeof(*hse)); 2873 if (hse == NULL) { 2874 pr_err("Memory allocation failed\n"); 2875 return NULL; 2876 } 2877 2878 hse->se = sd->entry; 2879 hse->hpp.name = sd->entry->se_header; 2880 hse->hpp.header = __sort__hpp_header; 2881 hse->hpp.width = __sort__hpp_width; 2882 hse->hpp.entry = __sort__hpp_entry; 2883 hse->hpp.color = NULL; 2884 2885 hse->hpp.cmp = __sort__hpp_cmp; 2886 hse->hpp.collapse = __sort__hpp_collapse; 2887 hse->hpp.sort = __sort__hpp_sort; 2888 hse->hpp.equal = __sort__hpp_equal; 2889 hse->hpp.free = hse_free; 2890 hse->hpp.init = hse_init; 2891 2892 INIT_LIST_HEAD(&hse->hpp.list); 2893 INIT_LIST_HEAD(&hse->hpp.sort_list); 2894 hse->hpp.elide = false; 2895 hse->hpp.len = 0; 2896 hse->hpp.user_len = 0; 2897 hse->hpp.level = level; 2898 2899 return hse; 2900} 2901 2902static void hpp_free(struct perf_hpp_fmt *fmt) 2903{ 2904 free(fmt); 2905} 2906 2907static struct perf_hpp_fmt *__hpp_dimension__alloc_hpp(struct hpp_dimension *hd, 2908 int level) 2909{ 2910 struct perf_hpp_fmt *fmt; 2911 2912 fmt = memdup(hd->fmt, sizeof(*fmt)); 2913 if (fmt) { 2914 INIT_LIST_HEAD(&fmt->list); 2915 INIT_LIST_HEAD(&fmt->sort_list); 2916 fmt->free = hpp_free; 2917 fmt->level = level; 2918 } 2919 2920 return fmt; 2921} 2922 2923int hist_entry__filter(struct hist_entry *he, int type, const void *arg) 2924{ 2925 struct perf_hpp_fmt *fmt; 2926 struct hpp_sort_entry *hse; 2927 int ret = -1; 2928 int r; 2929 2930 perf_hpp_list__for_each_format(he->hpp_list, fmt) { 2931 if (!perf_hpp__is_sort_entry(fmt)) 2932 continue; 2933 2934 hse = container_of(fmt, struct hpp_sort_entry, hpp); 2935 if (hse->se->se_filter == NULL) 2936 continue; 2937 2938 /* 2939 * hist entry is filtered if any of sort key in the hpp list 2940 * is applied. But it should skip non-matched filter types. 2941 */ 2942 r = hse->se->se_filter(he, type, arg); 2943 if (r >= 0) { 2944 if (ret < 0) 2945 ret = 0; 2946 ret |= r; 2947 } 2948 } 2949 2950 return ret; 2951} 2952 2953static int __sort_dimension__add_hpp_sort(struct sort_dimension *sd, 2954 struct perf_hpp_list *list, 2955 int level) 2956{ 2957 struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd, level); 2958 2959 if (hse == NULL) 2960 return -1; 2961 2962 perf_hpp_list__register_sort_field(list, &hse->hpp); 2963 return 0; 2964} 2965 2966static int __sort_dimension__add_hpp_output(struct sort_dimension *sd, 2967 struct perf_hpp_list *list, 2968 int level) 2969{ 2970 struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd, level); 2971 2972 if (hse == NULL) 2973 return -1; 2974 2975 perf_hpp_list__column_register(list, &hse->hpp); 2976 return 0; 2977} 2978 2979#ifndef HAVE_LIBTRACEEVENT 2980bool perf_hpp__is_dynamic_entry(struct perf_hpp_fmt *fmt __maybe_unused) 2981{ 2982 return false; 2983} 2984bool perf_hpp__defined_dynamic_entry(struct perf_hpp_fmt *fmt __maybe_unused, 2985 struct hists *hists __maybe_unused) 2986{ 2987 return false; 2988} 2989#else 2990struct hpp_dynamic_entry { 2991 struct perf_hpp_fmt hpp; 2992 struct evsel *evsel; 2993 struct tep_format_field *field; 2994 unsigned dynamic_len; 2995 bool raw_trace; 2996}; 2997 2998static int hde_width(struct hpp_dynamic_entry *hde) 2999{ 3000 if (!hde->hpp.len) { 3001 int len = hde->dynamic_len; 3002 int namelen = strlen(hde->field->name); 3003 int fieldlen = hde->field->size; 3004 3005 if (namelen > len) 3006 len = namelen; 3007 3008 if (!(hde->field->flags & TEP_FIELD_IS_STRING)) { 3009 /* length for print hex numbers */ 3010 fieldlen = hde->field->size * 2 + 2; 3011 } 3012 if (fieldlen > len) 3013 len = fieldlen; 3014 3015 hde->hpp.len = len; 3016 } 3017 return hde->hpp.len; 3018} 3019 3020static void update_dynamic_len(struct hpp_dynamic_entry *hde, 3021 struct hist_entry *he) 3022{ 3023 char *str, *pos; 3024 struct tep_format_field *field = hde->field; 3025 size_t namelen; 3026 bool last = false; 3027 3028 if (hde->raw_trace) 3029 return; 3030 3031 /* parse pretty print result and update max length */ 3032 if (!he->trace_output) 3033 he->trace_output = get_trace_output(he); 3034 3035 namelen = strlen(field->name); 3036 str = he->trace_output; 3037 3038 while (str) { 3039 pos = strchr(str, ' '); 3040 if (pos == NULL) { 3041 last = true; 3042 pos = str + strlen(str); 3043 } 3044 3045 if (!strncmp(str, field->name, namelen)) { 3046 size_t len; 3047 3048 str += namelen + 1; 3049 len = pos - str; 3050 3051 if (len > hde->dynamic_len) 3052 hde->dynamic_len = len; 3053 break; 3054 } 3055 3056 if (last) 3057 str = NULL; 3058 else 3059 str = pos + 1; 3060 } 3061} 3062 3063static int __sort__hde_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, 3064 struct hists *hists __maybe_unused, 3065 int line __maybe_unused, 3066 int *span __maybe_unused) 3067{ 3068 struct hpp_dynamic_entry *hde; 3069 size_t len = fmt->user_len; 3070 3071 hde = container_of(fmt, struct hpp_dynamic_entry, hpp); 3072 3073 if (!len) 3074 len = hde_width(hde); 3075 3076 return scnprintf(hpp->buf, hpp->size, "%*.*s", len, len, hde->field->name); 3077} 3078 3079static int __sort__hde_width(struct perf_hpp_fmt *fmt, 3080 struct perf_hpp *hpp __maybe_unused, 3081 struct hists *hists __maybe_unused) 3082{ 3083 struct hpp_dynamic_entry *hde; 3084 size_t len = fmt->user_len; 3085 3086 hde = container_of(fmt, struct hpp_dynamic_entry, hpp); 3087 3088 if (!len) 3089 len = hde_width(hde); 3090 3091 return len; 3092} 3093 3094bool perf_hpp__defined_dynamic_entry(struct perf_hpp_fmt *fmt, struct hists *hists) 3095{ 3096 struct hpp_dynamic_entry *hde; 3097 3098 hde = container_of(fmt, struct hpp_dynamic_entry, hpp); 3099 3100 return hists_to_evsel(hists) == hde->evsel; 3101} 3102 3103static int __sort__hde_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, 3104 struct hist_entry *he) 3105{ 3106 struct hpp_dynamic_entry *hde; 3107 size_t len = fmt->user_len; 3108 char *str, *pos; 3109 struct tep_format_field *field; 3110 size_t namelen; 3111 bool last = false; 3112 int ret; 3113 3114 hde = container_of(fmt, struct hpp_dynamic_entry, hpp); 3115 3116 if (!len) 3117 len = hde_width(hde); 3118 3119 if (hde->raw_trace) 3120 goto raw_field; 3121 3122 if (!he->trace_output) 3123 he->trace_output = get_trace_output(he); 3124 3125 field = hde->field; 3126 namelen = strlen(field->name); 3127 str = he->trace_output; 3128 3129 while (str) { 3130 pos = strchr(str, ' '); 3131 if (pos == NULL) { 3132 last = true; 3133 pos = str + strlen(str); 3134 } 3135 3136 if (!strncmp(str, field->name, namelen)) { 3137 str += namelen + 1; 3138 str = strndup(str, pos - str); 3139 3140 if (str == NULL) 3141 return scnprintf(hpp->buf, hpp->size, 3142 "%*.*s", len, len, "ERROR"); 3143 break; 3144 } 3145 3146 if (last) 3147 str = NULL; 3148 else 3149 str = pos + 1; 3150 } 3151 3152 if (str == NULL) { 3153 struct trace_seq seq; 3154raw_field: 3155 trace_seq_init(&seq); 3156 tep_print_field(&seq, he->raw_data, hde->field); 3157 str = seq.buffer; 3158 } 3159 3160 ret = scnprintf(hpp->buf, hpp->size, "%*.*s", len, len, str); 3161 free(str); 3162 return ret; 3163} 3164 3165static int64_t __sort__hde_cmp(struct perf_hpp_fmt *fmt, 3166 struct hist_entry *a, struct hist_entry *b) 3167{ 3168 struct hpp_dynamic_entry *hde; 3169 struct tep_format_field *field; 3170 unsigned offset, size; 3171 3172 hde = container_of(fmt, struct hpp_dynamic_entry, hpp); 3173 3174 field = hde->field; 3175 if (field->flags & TEP_FIELD_IS_DYNAMIC) { 3176 unsigned long long dyn; 3177 3178 tep_read_number_field(field, a->raw_data, &dyn); 3179 offset = dyn & 0xffff; 3180 size = (dyn >> 16) & 0xffff; 3181 if (tep_field_is_relative(field->flags)) 3182 offset += field->offset + field->size; 3183 /* record max width for output */ 3184 if (size > hde->dynamic_len) 3185 hde->dynamic_len = size; 3186 } else { 3187 offset = field->offset; 3188 size = field->size; 3189 } 3190 3191 return memcmp(a->raw_data + offset, b->raw_data + offset, size); 3192} 3193 3194bool perf_hpp__is_dynamic_entry(struct perf_hpp_fmt *fmt) 3195{ 3196 return fmt->cmp == __sort__hde_cmp; 3197} 3198 3199static bool __sort__hde_equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b) 3200{ 3201 struct hpp_dynamic_entry *hde_a; 3202 struct hpp_dynamic_entry *hde_b; 3203 3204 if (!perf_hpp__is_dynamic_entry(a) || !perf_hpp__is_dynamic_entry(b)) 3205 return false; 3206 3207 hde_a = container_of(a, struct hpp_dynamic_entry, hpp); 3208 hde_b = container_of(b, struct hpp_dynamic_entry, hpp); 3209 3210 return hde_a->field == hde_b->field; 3211} 3212 3213static void hde_free(struct perf_hpp_fmt *fmt) 3214{ 3215 struct hpp_dynamic_entry *hde; 3216 3217 hde = container_of(fmt, struct hpp_dynamic_entry, hpp); 3218 free(hde); 3219} 3220 3221static void __sort__hde_init(struct perf_hpp_fmt *fmt, struct hist_entry *he) 3222{ 3223 struct hpp_dynamic_entry *hde; 3224 3225 if (!perf_hpp__is_dynamic_entry(fmt)) 3226 return; 3227 3228 hde = container_of(fmt, struct hpp_dynamic_entry, hpp); 3229 update_dynamic_len(hde, he); 3230} 3231 3232static struct hpp_dynamic_entry * 3233__alloc_dynamic_entry(struct evsel *evsel, struct tep_format_field *field, 3234 int level) 3235{ 3236 struct hpp_dynamic_entry *hde; 3237 3238 hde = malloc(sizeof(*hde)); 3239 if (hde == NULL) { 3240 pr_debug("Memory allocation failed\n"); 3241 return NULL; 3242 } 3243 3244 hde->evsel = evsel; 3245 hde->field = field; 3246 hde->dynamic_len = 0; 3247 3248 hde->hpp.name = field->name; 3249 hde->hpp.header = __sort__hde_header; 3250 hde->hpp.width = __sort__hde_width; 3251 hde->hpp.entry = __sort__hde_entry; 3252 hde->hpp.color = NULL; 3253 3254 hde->hpp.init = __sort__hde_init; 3255 hde->hpp.cmp = __sort__hde_cmp; 3256 hde->hpp.collapse = __sort__hde_cmp; 3257 hde->hpp.sort = __sort__hde_cmp; 3258 hde->hpp.equal = __sort__hde_equal; 3259 hde->hpp.free = hde_free; 3260 3261 INIT_LIST_HEAD(&hde->hpp.list); 3262 INIT_LIST_HEAD(&hde->hpp.sort_list); 3263 hde->hpp.elide = false; 3264 hde->hpp.len = 0; 3265 hde->hpp.user_len = 0; 3266 hde->hpp.level = level; 3267 3268 return hde; 3269} 3270#endif /* HAVE_LIBTRACEEVENT */ 3271 3272struct perf_hpp_fmt *perf_hpp_fmt__dup(struct perf_hpp_fmt *fmt) 3273{ 3274 struct perf_hpp_fmt *new_fmt = NULL; 3275 3276 if (perf_hpp__is_sort_entry(fmt)) { 3277 struct hpp_sort_entry *hse, *new_hse; 3278 3279 hse = container_of(fmt, struct hpp_sort_entry, hpp); 3280 new_hse = memdup(hse, sizeof(*hse)); 3281 if (new_hse) 3282 new_fmt = &new_hse->hpp; 3283#ifdef HAVE_LIBTRACEEVENT 3284 } else if (perf_hpp__is_dynamic_entry(fmt)) { 3285 struct hpp_dynamic_entry *hde, *new_hde; 3286 3287 hde = container_of(fmt, struct hpp_dynamic_entry, hpp); 3288 new_hde = memdup(hde, sizeof(*hde)); 3289 if (new_hde) 3290 new_fmt = &new_hde->hpp; 3291#endif 3292 } else { 3293 new_fmt = memdup(fmt, sizeof(*fmt)); 3294 } 3295 3296 INIT_LIST_HEAD(&new_fmt->list); 3297 INIT_LIST_HEAD(&new_fmt->sort_list); 3298 3299 return new_fmt; 3300} 3301 3302static int parse_field_name(char *str, char **event, char **field, char **opt) 3303{ 3304 char *event_name, *field_name, *opt_name; 3305 3306 event_name = str; 3307 field_name = strchr(str, '.'); 3308 3309 if (field_name) { 3310 *field_name++ = '\0'; 3311 } else { 3312 event_name = NULL; 3313 field_name = str; 3314 } 3315 3316 opt_name = strchr(field_name, '/'); 3317 if (opt_name) 3318 *opt_name++ = '\0'; 3319 3320 *event = event_name; 3321 *field = field_name; 3322 *opt = opt_name; 3323 3324 return 0; 3325} 3326 3327/* find match evsel using a given event name. The event name can be: 3328 * 1. '%' + event index (e.g. '%1' for first event) 3329 * 2. full event name (e.g. sched:sched_switch) 3330 * 3. partial event name (should not contain ':') 3331 */ 3332static struct evsel *find_evsel(struct evlist *evlist, char *event_name) 3333{ 3334 struct evsel *evsel = NULL; 3335 struct evsel *pos; 3336 bool full_name; 3337 3338 /* case 1 */ 3339 if (event_name[0] == '%') { 3340 int nr = strtol(event_name+1, NULL, 0); 3341 3342 if (nr > evlist->core.nr_entries) 3343 return NULL; 3344 3345 evsel = evlist__first(evlist); 3346 while (--nr > 0) 3347 evsel = evsel__next(evsel); 3348 3349 return evsel; 3350 } 3351 3352 full_name = !!strchr(event_name, ':'); 3353 evlist__for_each_entry(evlist, pos) { 3354 /* case 2 */ 3355 if (full_name && evsel__name_is(pos, event_name)) 3356 return pos; 3357 /* case 3 */ 3358 if (!full_name && strstr(pos->name, event_name)) { 3359 if (evsel) { 3360 pr_debug("'%s' event is ambiguous: it can be %s or %s\n", 3361 event_name, evsel->name, pos->name); 3362 return NULL; 3363 } 3364 evsel = pos; 3365 } 3366 } 3367 3368 return evsel; 3369} 3370 3371#ifdef HAVE_LIBTRACEEVENT 3372static int __dynamic_dimension__add(struct evsel *evsel, 3373 struct tep_format_field *field, 3374 bool raw_trace, int level) 3375{ 3376 struct hpp_dynamic_entry *hde; 3377 3378 hde = __alloc_dynamic_entry(evsel, field, level); 3379 if (hde == NULL) 3380 return -ENOMEM; 3381 3382 hde->raw_trace = raw_trace; 3383 3384 perf_hpp__register_sort_field(&hde->hpp); 3385 return 0; 3386} 3387 3388static int add_evsel_fields(struct evsel *evsel, bool raw_trace, int level) 3389{ 3390 int ret; 3391 struct tep_event *tp_format = evsel__tp_format(evsel); 3392 struct tep_format_field *field = tp_format ? tp_format->format.fields : NULL; 3393 while (field) { 3394 ret = __dynamic_dimension__add(evsel, field, raw_trace, level); 3395 if (ret < 0) 3396 return ret; 3397 3398 field = field->next; 3399 } 3400 return 0; 3401} 3402 3403static int add_all_dynamic_fields(struct evlist *evlist, bool raw_trace, 3404 int level) 3405{ 3406 int ret; 3407 struct evsel *evsel; 3408 3409 evlist__for_each_entry(evlist, evsel) { 3410 if (evsel->core.attr.type != PERF_TYPE_TRACEPOINT) 3411 continue; 3412 3413 ret = add_evsel_fields(evsel, raw_trace, level); 3414 if (ret < 0) 3415 return ret; 3416 } 3417 return 0; 3418} 3419 3420static int add_all_matching_fields(struct evlist *evlist, 3421 char *field_name, bool raw_trace, int level) 3422{ 3423 int ret = -ESRCH; 3424 struct evsel *evsel; 3425 3426 evlist__for_each_entry(evlist, evsel) { 3427 struct tep_event *tp_format; 3428 struct tep_format_field *field; 3429 3430 if (evsel->core.attr.type != PERF_TYPE_TRACEPOINT) 3431 continue; 3432 3433 tp_format = evsel__tp_format(evsel); 3434 if (tp_format == NULL) 3435 continue; 3436 3437 field = tep_find_any_field(tp_format, field_name); 3438 if (field == NULL) 3439 continue; 3440 3441 ret = __dynamic_dimension__add(evsel, field, raw_trace, level); 3442 if (ret < 0) 3443 break; 3444 } 3445 return ret; 3446} 3447#endif /* HAVE_LIBTRACEEVENT */ 3448 3449static int add_dynamic_entry(struct evlist *evlist, const char *tok, 3450 int level) 3451{ 3452 char *str, *event_name, *field_name, *opt_name; 3453 struct evsel *evsel; 3454 bool raw_trace = symbol_conf.raw_trace; 3455 int ret = 0; 3456 3457 if (evlist == NULL) 3458 return -ENOENT; 3459 3460 str = strdup(tok); 3461 if (str == NULL) 3462 return -ENOMEM; 3463 3464 if (parse_field_name(str, &event_name, &field_name, &opt_name) < 0) { 3465 ret = -EINVAL; 3466 goto out; 3467 } 3468 3469 if (opt_name) { 3470 if (strcmp(opt_name, "raw")) { 3471 pr_debug("unsupported field option %s\n", opt_name); 3472 ret = -EINVAL; 3473 goto out; 3474 } 3475 raw_trace = true; 3476 } 3477 3478#ifdef HAVE_LIBTRACEEVENT 3479 if (!strcmp(field_name, "trace_fields")) { 3480 ret = add_all_dynamic_fields(evlist, raw_trace, level); 3481 goto out; 3482 } 3483 3484 if (event_name == NULL) { 3485 ret = add_all_matching_fields(evlist, field_name, raw_trace, level); 3486 goto out; 3487 } 3488#else 3489 evlist__for_each_entry(evlist, evsel) { 3490 if (evsel->core.attr.type == PERF_TYPE_TRACEPOINT) { 3491 pr_err("%s %s", ret ? "," : "This perf binary isn't linked with libtraceevent, can't process", evsel__name(evsel)); 3492 ret = -ENOTSUP; 3493 } 3494 } 3495 3496 if (ret) { 3497 pr_err("\n"); 3498 goto out; 3499 } 3500#endif 3501 3502 evsel = find_evsel(evlist, event_name); 3503 if (evsel == NULL) { 3504 pr_debug("Cannot find event: %s\n", event_name); 3505 ret = -ENOENT; 3506 goto out; 3507 } 3508 3509 if (evsel->core.attr.type != PERF_TYPE_TRACEPOINT) { 3510 pr_debug("%s is not a tracepoint event\n", event_name); 3511 ret = -EINVAL; 3512 goto out; 3513 } 3514 3515#ifdef HAVE_LIBTRACEEVENT 3516 if (!strcmp(field_name, "*")) { 3517 ret = add_evsel_fields(evsel, raw_trace, level); 3518 } else { 3519 struct tep_event *tp_format = evsel__tp_format(evsel); 3520 struct tep_format_field *field = 3521 tp_format ? tep_find_any_field(tp_format, field_name) : NULL; 3522 3523 if (field == NULL) { 3524 pr_debug("Cannot find event field for %s.%s\n", 3525 event_name, field_name); 3526 return -ENOENT; 3527 } 3528 3529 ret = __dynamic_dimension__add(evsel, field, raw_trace, level); 3530 } 3531#else 3532 (void)level; 3533 (void)raw_trace; 3534#endif /* HAVE_LIBTRACEEVENT */ 3535 3536out: 3537 free(str); 3538 return ret; 3539} 3540 3541static int __sort_dimension__add(struct sort_dimension *sd, 3542 struct perf_hpp_list *list, 3543 int level) 3544{ 3545 if (sd->taken) 3546 return 0; 3547 3548 if (__sort_dimension__add_hpp_sort(sd, list, level) < 0) 3549 return -1; 3550 3551 if (sd->entry->se_collapse) 3552 list->need_collapse = 1; 3553 3554 sd->taken = 1; 3555 3556 return 0; 3557} 3558 3559static int __hpp_dimension__add(struct hpp_dimension *hd, 3560 struct perf_hpp_list *list, 3561 int level) 3562{ 3563 struct perf_hpp_fmt *fmt; 3564 3565 if (hd->taken) 3566 return 0; 3567 3568 fmt = __hpp_dimension__alloc_hpp(hd, level); 3569 if (!fmt) 3570 return -1; 3571 3572 hd->taken = 1; 3573 hd->was_taken = 1; 3574 perf_hpp_list__register_sort_field(list, fmt); 3575 return 0; 3576} 3577 3578static int __sort_dimension__add_output(struct perf_hpp_list *list, 3579 struct sort_dimension *sd, 3580 int level) 3581{ 3582 if (sd->taken) 3583 return 0; 3584 3585 if (__sort_dimension__add_hpp_output(sd, list, level) < 0) 3586 return -1; 3587 3588 sd->taken = 1; 3589 return 0; 3590} 3591 3592static int __hpp_dimension__add_output(struct perf_hpp_list *list, 3593 struct hpp_dimension *hd, 3594 int level) 3595{ 3596 struct perf_hpp_fmt *fmt; 3597 3598 if (hd->taken) 3599 return 0; 3600 3601 fmt = __hpp_dimension__alloc_hpp(hd, level); 3602 if (!fmt) 3603 return -1; 3604 3605 hd->taken = 1; 3606 perf_hpp_list__column_register(list, fmt); 3607 return 0; 3608} 3609 3610int hpp_dimension__add_output(unsigned col, bool implicit) 3611{ 3612 struct hpp_dimension *hd; 3613 3614 BUG_ON(col >= PERF_HPP__MAX_INDEX); 3615 hd = &hpp_sort_dimensions[col]; 3616 if (implicit && !hd->was_taken) 3617 return 0; 3618 return __hpp_dimension__add_output(&perf_hpp_list, hd, /*level=*/0); 3619} 3620 3621int sort_dimension__add(struct perf_hpp_list *list, const char *tok, 3622 struct evlist *evlist, struct perf_env *env, 3623 int level) 3624{ 3625 unsigned int i, j; 3626 3627 /* 3628 * Check to see if there are any arch specific 3629 * sort dimensions not applicable for the current 3630 * architecture. If so, Skip that sort key since 3631 * we don't want to display it in the output fields. 3632 */ 3633 for (j = 0; j < ARRAY_SIZE(arch_specific_sort_keys); j++) { 3634 if (!strcmp(arch_specific_sort_keys[j], tok) && 3635 !arch_support_sort_key(tok, env)) { 3636 return 0; 3637 } 3638 } 3639 3640 for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) { 3641 struct sort_dimension *sd = &common_sort_dimensions[i]; 3642 3643 if (!sd->name || strncasecmp(tok, sd->name, strlen(tok))) 3644 continue; 3645 3646 for (j = 0; j < ARRAY_SIZE(dynamic_headers); j++) { 3647 if (sd->name && !strcmp(dynamic_headers[j], sd->name)) 3648 sort_dimension_add_dynamic_header(sd, env); 3649 } 3650 3651 if (sd->entry == &sort_parent && parent_pattern) { 3652 int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED); 3653 if (ret) { 3654 char err[BUFSIZ]; 3655 3656 regerror(ret, &parent_regex, err, sizeof(err)); 3657 pr_err("Invalid regex: %s\n%s", parent_pattern, err); 3658 return -EINVAL; 3659 } 3660 list->parent = 1; 3661 } else if (sd->entry == &sort_sym) { 3662 list->sym = 1; 3663 /* 3664 * perf diff displays the performance difference amongst 3665 * two or more perf.data files. Those files could come 3666 * from different binaries. So we should not compare 3667 * their ips, but the name of symbol. 3668 */ 3669 if (sort__mode == SORT_MODE__DIFF) 3670 sd->entry->se_collapse = sort__sym_sort; 3671 3672 } else if (sd->entry == &sort_dso) { 3673 list->dso = 1; 3674 } else if (sd->entry == &sort_socket) { 3675 list->socket = 1; 3676 } else if (sd->entry == &sort_thread) { 3677 list->thread = 1; 3678 } else if (sd->entry == &sort_comm) { 3679 list->comm = 1; 3680 } else if (sd->entry == &sort_type_offset) { 3681 symbol_conf.annotate_data_member = true; 3682 } 3683 3684 return __sort_dimension__add(sd, list, level); 3685 } 3686 3687 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) { 3688 struct sort_dimension *sd = &bstack_sort_dimensions[i]; 3689 3690 if (!sd->name || strncasecmp(tok, sd->name, strlen(tok))) 3691 continue; 3692 3693 if ((sort__mode != SORT_MODE__BRANCH) && 3694 strncasecmp(tok, "callchain_branch_predicted", 3695 strlen(tok)) && 3696 strncasecmp(tok, "callchain_branch_abort", 3697 strlen(tok)) && 3698 strncasecmp(tok, "callchain_branch_cycles", 3699 strlen(tok))) 3700 return -EINVAL; 3701 3702 if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to) 3703 list->sym = 1; 3704 3705 __sort_dimension__add(sd, list, level); 3706 return 0; 3707 } 3708 3709 for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) { 3710 struct sort_dimension *sd = &memory_sort_dimensions[i]; 3711 3712 if (!sd->name || strncasecmp(tok, sd->name, strlen(tok))) 3713 continue; 3714 3715 if (sort__mode != SORT_MODE__MEMORY) 3716 return -EINVAL; 3717 3718 if (sd->entry == &sort_mem_dcacheline && cacheline_size() == 0) 3719 return -EINVAL; 3720 3721 if (sd->entry == &sort_mem_daddr_sym) 3722 list->sym = 1; 3723 3724 __sort_dimension__add(sd, list, level); 3725 return 0; 3726 } 3727 3728 for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) { 3729 struct hpp_dimension *hd = &hpp_sort_dimensions[i]; 3730 3731 if (strncasecmp(tok, hd->name, strlen(tok))) 3732 continue; 3733 3734 return __hpp_dimension__add(hd, list, level); 3735 } 3736 3737 if (!add_dynamic_entry(evlist, tok, level)) 3738 return 0; 3739 3740 return -ESRCH; 3741} 3742 3743/* This should match with sort_dimension__add() above */ 3744static bool is_hpp_sort_key(const char *key, struct perf_env *env) 3745{ 3746 unsigned i; 3747 3748 for (i = 0; i < ARRAY_SIZE(arch_specific_sort_keys); i++) { 3749 if (!strcmp(arch_specific_sort_keys[i], key) && 3750 !arch_support_sort_key(key, env)) { 3751 return false; 3752 } 3753 } 3754 3755 for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) { 3756 struct sort_dimension *sd = &common_sort_dimensions[i]; 3757 3758 if (sd->name && !strncasecmp(key, sd->name, strlen(key))) 3759 return false; 3760 } 3761 3762 for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) { 3763 struct hpp_dimension *hd = &hpp_sort_dimensions[i]; 3764 3765 if (!strncasecmp(key, hd->name, strlen(key))) 3766 return true; 3767 } 3768 return false; 3769} 3770 3771static int setup_sort_list(struct perf_hpp_list *list, char *str, 3772 struct evlist *evlist, struct perf_env *env) 3773{ 3774 char *tmp, *tok; 3775 int ret = 0; 3776 int level = 0; 3777 int next_level = 1; 3778 int prev_level = 0; 3779 bool in_group = false; 3780 bool prev_was_hpp = false; 3781 3782 do { 3783 tok = str; 3784 tmp = strpbrk(str, "{}, "); 3785 if (tmp) { 3786 if (in_group) 3787 next_level = level; 3788 else 3789 next_level = level + 1; 3790 3791 if (*tmp == '{') 3792 in_group = true; 3793 else if (*tmp == '}') 3794 in_group = false; 3795 3796 *tmp = '\0'; 3797 str = tmp + 1; 3798 } 3799 3800 if (*tok) { 3801 if (is_hpp_sort_key(tok, env)) { 3802 /* keep output (hpp) sort keys in the same level */ 3803 if (prev_was_hpp) { 3804 bool next_same = (level == next_level); 3805 3806 level = prev_level; 3807 next_level = next_same ? level : level+1; 3808 } 3809 prev_was_hpp = true; 3810 } else { 3811 prev_was_hpp = false; 3812 } 3813 3814 ret = sort_dimension__add(list, tok, evlist, env, level); 3815 if (ret == -EINVAL) { 3816 if (!cacheline_size() && !strncasecmp(tok, "dcacheline", strlen(tok))) 3817 ui__error("The \"dcacheline\" --sort key needs to know the cacheline size and it couldn't be determined on this system"); 3818 else 3819 ui__error("Invalid --sort key: `%s'", tok); 3820 break; 3821 } else if (ret == -ESRCH) { 3822 ui__error("Unknown --sort key: `%s'", tok); 3823 break; 3824 } 3825 prev_level = level; 3826 } 3827 3828 level = next_level; 3829 } while (tmp); 3830 3831 return ret; 3832} 3833 3834static const char *get_default_sort_order(struct evlist *evlist) 3835{ 3836 const char *default_sort_orders[] = { 3837 default_sort_order, 3838 default_branch_sort_order, 3839 default_mem_sort_order, 3840 default_top_sort_order, 3841 default_diff_sort_order, 3842 default_tracepoint_sort_order, 3843 }; 3844 bool use_trace = true; 3845 struct evsel *evsel; 3846 3847 BUG_ON(sort__mode >= ARRAY_SIZE(default_sort_orders)); 3848 3849 if (evlist == NULL || evlist__empty(evlist)) 3850 goto out_no_evlist; 3851 3852 evlist__for_each_entry(evlist, evsel) { 3853 if (evsel->core.attr.type != PERF_TYPE_TRACEPOINT) { 3854 use_trace = false; 3855 break; 3856 } 3857 } 3858 3859 if (use_trace) { 3860 sort__mode = SORT_MODE__TRACEPOINT; 3861 if (symbol_conf.raw_trace) 3862 return "trace_fields"; 3863 } 3864out_no_evlist: 3865 return default_sort_orders[sort__mode]; 3866} 3867 3868static int setup_sort_order(struct evlist *evlist) 3869{ 3870 char *new_sort_order; 3871 3872 /* 3873 * Append '+'-prefixed sort order to the default sort 3874 * order string. 3875 */ 3876 if (!sort_order || is_strict_order(sort_order)) 3877 return 0; 3878 3879 if (sort_order[1] == '\0') { 3880 ui__error("Invalid --sort key: `+'"); 3881 return -EINVAL; 3882 } 3883 3884 /* 3885 * We allocate new sort_order string, but we never free it, 3886 * because it's checked over the rest of the code. 3887 */ 3888 if (asprintf(&new_sort_order, "%s,%s", 3889 get_default_sort_order(evlist), sort_order + 1) < 0) { 3890 pr_err("Not enough memory to set up --sort"); 3891 return -ENOMEM; 3892 } 3893 3894 sort_order = new_sort_order; 3895 return 0; 3896} 3897 3898/* 3899 * Adds 'pre,' prefix into 'str' is 'pre' is 3900 * not already part of 'str'. 3901 */ 3902static char *prefix_if_not_in(const char *pre, char *str) 3903{ 3904 char *n; 3905 3906 if (!str || strstr(str, pre)) 3907 return str; 3908 3909 if (asprintf(&n, "%s,%s", pre, str) < 0) 3910 n = NULL; 3911 3912 free(str); 3913 return n; 3914} 3915 3916static char *setup_overhead(char *keys) 3917{ 3918 if (sort__mode == SORT_MODE__DIFF) 3919 return keys; 3920 3921 if (symbol_conf.prefer_latency) { 3922 keys = prefix_if_not_in("overhead", keys); 3923 keys = prefix_if_not_in("latency", keys); 3924 if (symbol_conf.cumulate_callchain) { 3925 keys = prefix_if_not_in("overhead_children", keys); 3926 keys = prefix_if_not_in("latency_children", keys); 3927 } 3928 } else if (!keys || (!strstr(keys, "overhead") && 3929 !strstr(keys, "latency"))) { 3930 if (symbol_conf.enable_latency) 3931 keys = prefix_if_not_in("latency", keys); 3932 keys = prefix_if_not_in("overhead", keys); 3933 if (symbol_conf.cumulate_callchain) { 3934 if (symbol_conf.enable_latency) 3935 keys = prefix_if_not_in("latency_children", keys); 3936 keys = prefix_if_not_in("overhead_children", keys); 3937 } 3938 } 3939 3940 return keys; 3941} 3942 3943static int __setup_sorting(struct evlist *evlist, struct perf_env *env) 3944{ 3945 char *str; 3946 const char *sort_keys; 3947 int ret = 0; 3948 3949 ret = setup_sort_order(evlist); 3950 if (ret) 3951 return ret; 3952 3953 sort_keys = sort_order; 3954 if (sort_keys == NULL) { 3955 if (is_strict_order(field_order)) { 3956 /* 3957 * If user specified field order but no sort order, 3958 * we'll honor it and not add default sort orders. 3959 */ 3960 return 0; 3961 } 3962 3963 sort_keys = get_default_sort_order(evlist); 3964 } 3965 3966 str = strdup(sort_keys); 3967 if (str == NULL) { 3968 pr_err("Not enough memory to setup sort keys"); 3969 return -ENOMEM; 3970 } 3971 3972 /* 3973 * Prepend overhead fields for backward compatibility. 3974 */ 3975 if (!is_strict_order(field_order)) { 3976 str = setup_overhead(str); 3977 if (str == NULL) { 3978 pr_err("Not enough memory to setup overhead keys"); 3979 return -ENOMEM; 3980 } 3981 } 3982 3983 ret = setup_sort_list(&perf_hpp_list, str, evlist, env); 3984 3985 free(str); 3986 return ret; 3987} 3988 3989void perf_hpp__set_elide(int idx, bool elide) 3990{ 3991 struct perf_hpp_fmt *fmt; 3992 struct hpp_sort_entry *hse; 3993 3994 perf_hpp_list__for_each_format(&perf_hpp_list, fmt) { 3995 if (!perf_hpp__is_sort_entry(fmt)) 3996 continue; 3997 3998 hse = container_of(fmt, struct hpp_sort_entry, hpp); 3999 if (hse->se->se_width_idx == idx) { 4000 fmt->elide = elide; 4001 break; 4002 } 4003 } 4004} 4005 4006static bool __get_elide(struct strlist *list, const char *list_name, FILE *fp) 4007{ 4008 if (list && strlist__nr_entries(list) == 1) { 4009 if (fp != NULL) 4010 fprintf(fp, "# %s: %s\n", list_name, 4011 strlist__entry(list, 0)->s); 4012 return true; 4013 } 4014 return false; 4015} 4016 4017static bool get_elide(int idx, FILE *output) 4018{ 4019 switch (idx) { 4020 case HISTC_SYMBOL: 4021 return __get_elide(symbol_conf.sym_list, "symbol", output); 4022 case HISTC_DSO: 4023 return __get_elide(symbol_conf.dso_list, "dso", output); 4024 case HISTC_COMM: 4025 return __get_elide(symbol_conf.comm_list, "comm", output); 4026 default: 4027 break; 4028 } 4029 4030 if (sort__mode != SORT_MODE__BRANCH) 4031 return false; 4032 4033 switch (idx) { 4034 case HISTC_SYMBOL_FROM: 4035 return __get_elide(symbol_conf.sym_from_list, "sym_from", output); 4036 case HISTC_SYMBOL_TO: 4037 return __get_elide(symbol_conf.sym_to_list, "sym_to", output); 4038 case HISTC_DSO_FROM: 4039 return __get_elide(symbol_conf.dso_from_list, "dso_from", output); 4040 case HISTC_DSO_TO: 4041 return __get_elide(symbol_conf.dso_to_list, "dso_to", output); 4042 case HISTC_ADDR_FROM: 4043 return __get_elide(symbol_conf.sym_from_list, "addr_from", output); 4044 case HISTC_ADDR_TO: 4045 return __get_elide(symbol_conf.sym_to_list, "addr_to", output); 4046 default: 4047 break; 4048 } 4049 4050 return false; 4051} 4052 4053void sort__setup_elide(FILE *output) 4054{ 4055 struct perf_hpp_fmt *fmt; 4056 struct hpp_sort_entry *hse; 4057 4058 perf_hpp_list__for_each_format(&perf_hpp_list, fmt) { 4059 if (!perf_hpp__is_sort_entry(fmt)) 4060 continue; 4061 4062 hse = container_of(fmt, struct hpp_sort_entry, hpp); 4063 fmt->elide = get_elide(hse->se->se_width_idx, output); 4064 } 4065 4066 /* 4067 * It makes no sense to elide all of sort entries. 4068 * Just revert them to show up again. 4069 */ 4070 perf_hpp_list__for_each_format(&perf_hpp_list, fmt) { 4071 if (!perf_hpp__is_sort_entry(fmt)) 4072 continue; 4073 4074 if (!fmt->elide) 4075 return; 4076 } 4077 4078 perf_hpp_list__for_each_format(&perf_hpp_list, fmt) { 4079 if (!perf_hpp__is_sort_entry(fmt)) 4080 continue; 4081 4082 fmt->elide = false; 4083 } 4084} 4085 4086int output_field_add(struct perf_hpp_list *list, const char *tok, int *level) 4087{ 4088 unsigned int i; 4089 4090 for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) { 4091 struct hpp_dimension *hd = &hpp_sort_dimensions[i]; 4092 4093 if (strncasecmp(tok, hd->name, strlen(tok))) 4094 continue; 4095 4096 if (!strcasecmp(tok, "weight")) 4097 ui__warning("--fields weight shows the average value unlike in the --sort key.\n"); 4098 4099 if (hd->mem_mode && sort__mode != SORT_MODE__MEMORY) 4100 continue; 4101 4102 return __hpp_dimension__add_output(list, hd, *level); 4103 } 4104 4105 /* 4106 * A non-output field will increase level so that it can be in a 4107 * different hierarchy. 4108 */ 4109 (*level)++; 4110 4111 for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) { 4112 struct sort_dimension *sd = &common_sort_dimensions[i]; 4113 4114 if (!sd->name || strncasecmp(tok, sd->name, strlen(tok))) 4115 continue; 4116 4117 return __sort_dimension__add_output(list, sd, *level); 4118 } 4119 4120 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) { 4121 struct sort_dimension *sd = &bstack_sort_dimensions[i]; 4122 4123 if (!sd->name || strncasecmp(tok, sd->name, strlen(tok))) 4124 continue; 4125 4126 if (sort__mode != SORT_MODE__BRANCH) 4127 return -EINVAL; 4128 4129 return __sort_dimension__add_output(list, sd, *level); 4130 } 4131 4132 for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) { 4133 struct sort_dimension *sd = &memory_sort_dimensions[i]; 4134 4135 if (!sd->name || strncasecmp(tok, sd->name, strlen(tok))) 4136 continue; 4137 4138 if (sort__mode != SORT_MODE__MEMORY) 4139 return -EINVAL; 4140 4141 return __sort_dimension__add_output(list, sd, *level); 4142 } 4143 4144 return -ESRCH; 4145} 4146 4147static int setup_output_list(struct perf_hpp_list *list, char *str) 4148{ 4149 char *tmp, *tok; 4150 int ret = 0; 4151 int level = 0; 4152 4153 for (tok = strtok_r(str, ", ", &tmp); 4154 tok; tok = strtok_r(NULL, ", ", &tmp)) { 4155 ret = output_field_add(list, tok, &level); 4156 if (ret == -EINVAL) { 4157 ui__error("Invalid --fields key: `%s'", tok); 4158 break; 4159 } else if (ret == -ESRCH) { 4160 ui__error("Unknown --fields key: `%s'", tok); 4161 break; 4162 } 4163 } 4164 4165 return ret; 4166} 4167 4168void reset_dimensions(void) 4169{ 4170 unsigned int i; 4171 4172 for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) 4173 common_sort_dimensions[i].taken = 0; 4174 4175 for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) 4176 hpp_sort_dimensions[i].taken = 0; 4177 4178 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) 4179 bstack_sort_dimensions[i].taken = 0; 4180 4181 for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) 4182 memory_sort_dimensions[i].taken = 0; 4183} 4184 4185bool is_strict_order(const char *order) 4186{ 4187 return order && (*order != '+'); 4188} 4189 4190static int __setup_output_field(void) 4191{ 4192 char *str, *strp; 4193 int ret = -EINVAL; 4194 4195 if (field_order == NULL) 4196 return 0; 4197 4198 strp = str = strdup(field_order); 4199 if (str == NULL) { 4200 pr_err("Not enough memory to setup output fields"); 4201 return -ENOMEM; 4202 } 4203 4204 if (!is_strict_order(field_order)) 4205 strp++; 4206 4207 if (!strlen(strp)) { 4208 ui__error("Invalid --fields key: `+'"); 4209 goto out; 4210 } 4211 4212 ret = setup_output_list(&perf_hpp_list, strp); 4213 4214out: 4215 free(str); 4216 return ret; 4217} 4218 4219int setup_sorting(struct evlist *evlist, struct perf_env *env) 4220{ 4221 int err; 4222 4223 err = __setup_sorting(evlist, env); 4224 if (err < 0) 4225 return err; 4226 4227 if (parent_pattern != default_parent_pattern) { 4228 err = sort_dimension__add(&perf_hpp_list, "parent", evlist, env, -1); 4229 if (err < 0) 4230 return err; 4231 } 4232 4233 reset_dimensions(); 4234 4235 /* 4236 * perf diff doesn't use default hpp output fields. 4237 */ 4238 if (sort__mode != SORT_MODE__DIFF) 4239 perf_hpp__init(); 4240 4241 err = __setup_output_field(); 4242 if (err < 0) 4243 return err; 4244 4245 err = perf_hpp__alloc_mem_stats(&perf_hpp_list, evlist); 4246 if (err < 0) 4247 return err; 4248 4249 /* copy sort keys to output fields */ 4250 perf_hpp__setup_output_field(&perf_hpp_list); 4251 /* and then copy output fields to sort keys */ 4252 perf_hpp__append_sort_keys(&perf_hpp_list); 4253 4254 /* setup hists-specific output fields */ 4255 if (perf_hpp__setup_hists_formats(&perf_hpp_list, evlist) < 0) 4256 return -1; 4257 4258 return 0; 4259} 4260 4261void reset_output_field(void) 4262{ 4263 perf_hpp_list.need_collapse = 0; 4264 perf_hpp_list.parent = 0; 4265 perf_hpp_list.sym = 0; 4266 perf_hpp_list.dso = 0; 4267 4268 field_order = NULL; 4269 sort_order = NULL; 4270 4271 reset_dimensions(); 4272 perf_hpp__reset_output_field(&perf_hpp_list); 4273} 4274 4275#define INDENT (3*8 + 1) 4276 4277static void add_key(struct strbuf *sb, const char *str, int *llen) 4278{ 4279 if (!str) 4280 return; 4281 4282 if (*llen >= 75) { 4283 strbuf_addstr(sb, "\n\t\t\t "); 4284 *llen = INDENT; 4285 } 4286 strbuf_addf(sb, " %s", str); 4287 *llen += strlen(str) + 1; 4288} 4289 4290static void add_sort_string(struct strbuf *sb, struct sort_dimension *s, int n, 4291 int *llen) 4292{ 4293 int i; 4294 4295 for (i = 0; i < n; i++) 4296 add_key(sb, s[i].name, llen); 4297} 4298 4299static void add_hpp_sort_string(struct strbuf *sb, struct hpp_dimension *s, int n, 4300 int *llen) 4301{ 4302 int i; 4303 4304 for (i = 0; i < n; i++) 4305 add_key(sb, s[i].name, llen); 4306} 4307 4308char *sort_help(const char *prefix, enum sort_mode mode) 4309{ 4310 struct strbuf sb; 4311 char *s; 4312 int len = strlen(prefix) + INDENT; 4313 4314 strbuf_init(&sb, 300); 4315 strbuf_addstr(&sb, prefix); 4316 add_hpp_sort_string(&sb, hpp_sort_dimensions, 4317 ARRAY_SIZE(hpp_sort_dimensions), &len); 4318 add_sort_string(&sb, common_sort_dimensions, 4319 ARRAY_SIZE(common_sort_dimensions), &len); 4320 if (mode == SORT_MODE__NORMAL || mode == SORT_MODE__BRANCH) 4321 add_sort_string(&sb, bstack_sort_dimensions, 4322 ARRAY_SIZE(bstack_sort_dimensions), &len); 4323 if (mode == SORT_MODE__NORMAL || mode == SORT_MODE__MEMORY) 4324 add_sort_string(&sb, memory_sort_dimensions, 4325 ARRAY_SIZE(memory_sort_dimensions), &len); 4326 s = strbuf_detach(&sb, NULL); 4327 strbuf_release(&sb); 4328 return s; 4329}