Linux Audio

Check our new training course

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