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