Linux Audio

Check our new training course

Loading...
v4.10.11
   1#include <sys/mman.h>
   2#include "sort.h"
   3#include "hist.h"
   4#include "comm.h"
   5#include "symbol.h"
   6#include "evsel.h"
   7#include "evlist.h"
   8#include <traceevent/event-parse.h>
   9#include "mem-events.h"
  10
  11regex_t		parent_regex;
  12const char	default_parent_pattern[] = "^sys_|^do_page_fault";
  13const char	*parent_pattern = default_parent_pattern;
  14const char	*default_sort_order = "comm,dso,symbol";
  15const char	default_branch_sort_order[] = "comm,dso_from,symbol_from,symbol_to,cycles";
  16const char	default_mem_sort_order[] = "local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked";
  17const char	default_top_sort_order[] = "dso,symbol";
  18const char	default_diff_sort_order[] = "dso,symbol";
  19const char	default_tracepoint_sort_order[] = "trace";
  20const char	*sort_order;
  21const char	*field_order;
  22regex_t		ignore_callees_regex;
  23int		have_ignore_callees = 0;
 
 
 
 
  24enum sort_mode	sort__mode = SORT_MODE__NORMAL;
  25
  26/*
  27 * Replaces all occurrences of a char used with the:
  28 *
  29 * -t, --field-separator
  30 *
  31 * option, that uses a special separator character and don't pad with spaces,
  32 * replacing all occurances of this separator in symbol names (and other
  33 * output) with a '.' character, that thus it's the only non valid separator.
  34*/
  35static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...)
  36{
  37	int n;
  38	va_list ap;
  39
  40	va_start(ap, fmt);
  41	n = vsnprintf(bf, size, fmt, ap);
  42	if (symbol_conf.field_sep && n > 0) {
  43		char *sep = bf;
  44
  45		while (1) {
  46			sep = strchr(sep, *symbol_conf.field_sep);
  47			if (sep == NULL)
  48				break;
  49			*sep = '.';
  50		}
  51	}
  52	va_end(ap);
  53
  54	if (n >= (int)size)
  55		return size - 1;
  56	return n;
  57}
  58
  59static int64_t cmp_null(const void *l, const void *r)
  60{
  61	if (!l && !r)
  62		return 0;
  63	else if (!l)
  64		return -1;
  65	else
  66		return 1;
  67}
  68
  69/* --sort pid */
  70
  71static int64_t
  72sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
  73{
  74	return right->thread->tid - left->thread->tid;
  75}
  76
  77static int hist_entry__thread_snprintf(struct hist_entry *he, char *bf,
  78				       size_t size, unsigned int width)
  79{
  80	const char *comm = thread__comm_str(he->thread);
  81
  82	width = max(7U, width) - 8;
  83	return repsep_snprintf(bf, size, "%7d:%-*.*s", he->thread->tid,
  84			       width, width, comm ?: "");
  85}
  86
  87static int hist_entry__thread_filter(struct hist_entry *he, int type, const void *arg)
  88{
  89	const struct thread *th = arg;
  90
  91	if (type != HIST_FILTER__THREAD)
  92		return -1;
  93
  94	return th && he->thread != th;
  95}
  96
  97struct sort_entry sort_thread = {
  98	.se_header	= "    Pid:Command",
  99	.se_cmp		= sort__thread_cmp,
 100	.se_snprintf	= hist_entry__thread_snprintf,
 101	.se_filter	= hist_entry__thread_filter,
 102	.se_width_idx	= HISTC_THREAD,
 103};
 104
 105/* --sort comm */
 106
 107static int64_t
 108sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
 109{
 110	/* Compare the addr that should be unique among comm */
 111	return strcmp(comm__str(right->comm), comm__str(left->comm));
 112}
 113
 114static int64_t
 115sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
 116{
 117	/* Compare the addr that should be unique among comm */
 118	return strcmp(comm__str(right->comm), comm__str(left->comm));
 119}
 120
 121static int64_t
 122sort__comm_sort(struct hist_entry *left, struct hist_entry *right)
 123{
 124	return strcmp(comm__str(right->comm), comm__str(left->comm));
 125}
 126
 127static int hist_entry__comm_snprintf(struct hist_entry *he, char *bf,
 128				     size_t size, unsigned int width)
 129{
 130	return repsep_snprintf(bf, size, "%-*.*s", width, width, comm__str(he->comm));
 131}
 132
 133struct sort_entry sort_comm = {
 134	.se_header	= "Command",
 135	.se_cmp		= sort__comm_cmp,
 136	.se_collapse	= sort__comm_collapse,
 137	.se_sort	= sort__comm_sort,
 138	.se_snprintf	= hist_entry__comm_snprintf,
 139	.se_filter	= hist_entry__thread_filter,
 140	.se_width_idx	= HISTC_COMM,
 141};
 142
 143/* --sort dso */
 144
 145static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r)
 146{
 147	struct dso *dso_l = map_l ? map_l->dso : NULL;
 148	struct dso *dso_r = map_r ? map_r->dso : NULL;
 149	const char *dso_name_l, *dso_name_r;
 150
 151	if (!dso_l || !dso_r)
 152		return cmp_null(dso_r, dso_l);
 153
 154	if (verbose) {
 155		dso_name_l = dso_l->long_name;
 156		dso_name_r = dso_r->long_name;
 157	} else {
 158		dso_name_l = dso_l->short_name;
 159		dso_name_r = dso_r->short_name;
 160	}
 161
 162	return strcmp(dso_name_l, dso_name_r);
 163}
 164
 165static int64_t
 166sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
 167{
 168	return _sort__dso_cmp(right->ms.map, left->ms.map);
 169}
 170
 171static int _hist_entry__dso_snprintf(struct map *map, char *bf,
 172				     size_t size, unsigned int width)
 173{
 174	if (map && map->dso) {
 175		const char *dso_name = !verbose ? map->dso->short_name :
 176			map->dso->long_name;
 177		return repsep_snprintf(bf, size, "%-*.*s", width, width, dso_name);
 178	}
 179
 180	return repsep_snprintf(bf, size, "%-*.*s", width, width, "[unknown]");
 181}
 182
 183static int hist_entry__dso_snprintf(struct hist_entry *he, char *bf,
 184				    size_t size, unsigned int width)
 185{
 186	return _hist_entry__dso_snprintf(he->ms.map, bf, size, width);
 187}
 188
 189static int hist_entry__dso_filter(struct hist_entry *he, int type, const void *arg)
 190{
 191	const struct dso *dso = arg;
 192
 193	if (type != HIST_FILTER__DSO)
 194		return -1;
 195
 196	return dso && (!he->ms.map || he->ms.map->dso != dso);
 197}
 198
 199struct sort_entry sort_dso = {
 200	.se_header	= "Shared Object",
 201	.se_cmp		= sort__dso_cmp,
 202	.se_snprintf	= hist_entry__dso_snprintf,
 203	.se_filter	= hist_entry__dso_filter,
 204	.se_width_idx	= HISTC_DSO,
 205};
 206
 207/* --sort symbol */
 208
 209static int64_t _sort__addr_cmp(u64 left_ip, u64 right_ip)
 210{
 211	return (int64_t)(right_ip - left_ip);
 212}
 213
 214static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r)
 215{
 
 
 216	if (!sym_l || !sym_r)
 217		return cmp_null(sym_l, sym_r);
 218
 219	if (sym_l == sym_r)
 220		return 0;
 221
 222	if (sym_l->start != sym_r->start)
 223		return (int64_t)(sym_r->start - sym_l->start);
 224
 225	return (int64_t)(sym_r->end - sym_l->end);
 226}
 227
 228static int64_t
 229sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
 230{
 231	int64_t ret;
 232
 233	if (!left->ms.sym && !right->ms.sym)
 234		return _sort__addr_cmp(left->ip, right->ip);
 235
 236	/*
 237	 * comparing symbol address alone is not enough since it's a
 238	 * relative address within a dso.
 239	 */
 240	if (!hists__has(left->hists, dso) || hists__has(right->hists, dso)) {
 241		ret = sort__dso_cmp(left, right);
 242		if (ret != 0)
 243			return ret;
 244	}
 245
 246	return _sort__sym_cmp(left->ms.sym, right->ms.sym);
 247}
 248
 249static int64_t
 250sort__sym_sort(struct hist_entry *left, struct hist_entry *right)
 251{
 252	if (!left->ms.sym || !right->ms.sym)
 253		return cmp_null(left->ms.sym, right->ms.sym);
 254
 255	return strcmp(right->ms.sym->name, left->ms.sym->name);
 256}
 257
 258static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym,
 259				     u64 ip, char level, char *bf, size_t size,
 260				     unsigned int width)
 261{
 262	size_t ret = 0;
 263
 264	if (verbose) {
 265		char o = map ? dso__symtab_origin(map->dso) : '!';
 266		ret += repsep_snprintf(bf, size, "%-#*llx %c ",
 267				       BITS_PER_LONG / 4 + 2, ip, o);
 268	}
 269
 270	ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level);
 271	if (sym && map) {
 272		if (map->type == MAP__VARIABLE) {
 273			ret += repsep_snprintf(bf + ret, size - ret, "%s", sym->name);
 274			ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx",
 275					ip - map->unmap_ip(map, sym->start));
 
 
 276		} else {
 277			ret += repsep_snprintf(bf + ret, size - ret, "%.*s",
 278					       width - ret,
 279					       sym->name);
 280		}
 281	} else {
 282		size_t len = BITS_PER_LONG / 4;
 283		ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx",
 284				       len, ip);
 
 
 285	}
 286
 287	return ret;
 288}
 289
 290static int hist_entry__sym_snprintf(struct hist_entry *he, char *bf,
 291				    size_t size, unsigned int width)
 292{
 293	return _hist_entry__sym_snprintf(he->ms.map, he->ms.sym, he->ip,
 294					 he->level, bf, size, width);
 295}
 296
 297static int hist_entry__sym_filter(struct hist_entry *he, int type, const void *arg)
 298{
 299	const char *sym = arg;
 300
 301	if (type != HIST_FILTER__SYMBOL)
 302		return -1;
 303
 304	return sym && (!he->ms.sym || !strstr(he->ms.sym->name, sym));
 305}
 306
 307struct sort_entry sort_sym = {
 308	.se_header	= "Symbol",
 309	.se_cmp		= sort__sym_cmp,
 310	.se_sort	= sort__sym_sort,
 311	.se_snprintf	= hist_entry__sym_snprintf,
 312	.se_filter	= hist_entry__sym_filter,
 313	.se_width_idx	= HISTC_SYMBOL,
 314};
 315
 316/* --sort srcline */
 317
 318char *hist_entry__get_srcline(struct hist_entry *he)
 319{
 320	struct map *map = he->ms.map;
 321
 322	if (!map)
 323		return SRCLINE_UNKNOWN;
 324
 325	return get_srcline(map->dso, map__rip_2objdump(map, he->ip),
 326			   he->ms.sym, true);
 327}
 328
 329static int64_t
 330sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right)
 331{
 332	if (!left->srcline)
 333		left->srcline = hist_entry__get_srcline(left);
 334	if (!right->srcline)
 335		right->srcline = hist_entry__get_srcline(right);
 336
 337	return strcmp(right->srcline, left->srcline);
 
 
 
 
 
 
 
 
 
 
 
 
 
 338}
 339
 340static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf,
 341					size_t size, unsigned int width)
 
 342{
 343	if (!he->srcline)
 344		he->srcline = hist_entry__get_srcline(he);
 345
 346	return repsep_snprintf(bf, size, "%-.*s", width, he->srcline);
 347}
 348
 349struct sort_entry sort_srcline = {
 350	.se_header	= "Source:Line",
 351	.se_cmp		= sort__srcline_cmp,
 352	.se_snprintf	= hist_entry__srcline_snprintf,
 353	.se_width_idx	= HISTC_SRCLINE,
 354};
 355
 356/* --sort srcline_from */
 357
 358static int64_t
 359sort__srcline_from_cmp(struct hist_entry *left, struct hist_entry *right)
 360{
 361	if (!left->branch_info->srcline_from) {
 362		struct map *map = left->branch_info->from.map;
 363		if (!map)
 364			left->branch_info->srcline_from = SRCLINE_UNKNOWN;
 365		else
 366			left->branch_info->srcline_from = get_srcline(map->dso,
 367					   map__rip_2objdump(map,
 368							     left->branch_info->from.al_addr),
 369							 left->branch_info->from.sym, true);
 370	}
 371	if (!right->branch_info->srcline_from) {
 372		struct map *map = right->branch_info->from.map;
 373		if (!map)
 374			right->branch_info->srcline_from = SRCLINE_UNKNOWN;
 375		else
 376			right->branch_info->srcline_from = get_srcline(map->dso,
 377					     map__rip_2objdump(map,
 378							       right->branch_info->from.al_addr),
 379						     right->branch_info->from.sym, true);
 380	}
 381	return strcmp(right->branch_info->srcline_from, left->branch_info->srcline_from);
 382}
 383
 384static int hist_entry__srcline_from_snprintf(struct hist_entry *he, char *bf,
 385					size_t size, unsigned int width)
 386{
 387	return repsep_snprintf(bf, size, "%-*.*s", width, width, he->branch_info->srcline_from);
 388}
 389
 390struct sort_entry sort_srcline_from = {
 391	.se_header	= "From Source:Line",
 392	.se_cmp		= sort__srcline_from_cmp,
 393	.se_snprintf	= hist_entry__srcline_from_snprintf,
 394	.se_width_idx	= HISTC_SRCLINE_FROM,
 395};
 396
 397/* --sort srcline_to */
 398
 399static int64_t
 400sort__srcline_to_cmp(struct hist_entry *left, struct hist_entry *right)
 401{
 402	if (!left->branch_info->srcline_to) {
 403		struct map *map = left->branch_info->to.map;
 404		if (!map)
 405			left->branch_info->srcline_to = SRCLINE_UNKNOWN;
 406		else
 407			left->branch_info->srcline_to = get_srcline(map->dso,
 408					   map__rip_2objdump(map,
 409							     left->branch_info->to.al_addr),
 410							 left->branch_info->from.sym, true);
 411	}
 412	if (!right->branch_info->srcline_to) {
 413		struct map *map = right->branch_info->to.map;
 414		if (!map)
 415			right->branch_info->srcline_to = SRCLINE_UNKNOWN;
 416		else
 417			right->branch_info->srcline_to = get_srcline(map->dso,
 418					     map__rip_2objdump(map,
 419							       right->branch_info->to.al_addr),
 420						     right->branch_info->to.sym, true);
 421	}
 422	return strcmp(right->branch_info->srcline_to, left->branch_info->srcline_to);
 423}
 424
 425static int hist_entry__srcline_to_snprintf(struct hist_entry *he, char *bf,
 426					size_t size, unsigned int width)
 427{
 428	return repsep_snprintf(bf, size, "%-*.*s", width, width, he->branch_info->srcline_to);
 429}
 430
 431struct sort_entry sort_srcline_to = {
 432	.se_header	= "To Source:Line",
 433	.se_cmp		= sort__srcline_to_cmp,
 434	.se_snprintf	= hist_entry__srcline_to_snprintf,
 435	.se_width_idx	= HISTC_SRCLINE_TO,
 436};
 437
 438/* --sort srcfile */
 439
 440static char no_srcfile[1];
 441
 442static char *hist_entry__get_srcfile(struct hist_entry *e)
 443{
 444	char *sf, *p;
 445	struct map *map = e->ms.map;
 446
 447	if (!map)
 448		return no_srcfile;
 449
 450	sf = __get_srcline(map->dso, map__rip_2objdump(map, e->ip),
 451			 e->ms.sym, false, true);
 452	if (!strcmp(sf, SRCLINE_UNKNOWN))
 453		return no_srcfile;
 454	p = strchr(sf, ':');
 455	if (p && *sf) {
 456		*p = 0;
 457		return sf;
 458	}
 459	free(sf);
 460	return no_srcfile;
 461}
 462
 463static int64_t
 464sort__srcfile_cmp(struct hist_entry *left, struct hist_entry *right)
 465{
 466	if (!left->srcfile)
 467		left->srcfile = hist_entry__get_srcfile(left);
 468	if (!right->srcfile)
 469		right->srcfile = hist_entry__get_srcfile(right);
 470
 471	return strcmp(right->srcfile, left->srcfile);
 472}
 473
 474static int hist_entry__srcfile_snprintf(struct hist_entry *he, char *bf,
 475					size_t size, unsigned int width)
 476{
 477	if (!he->srcfile)
 478		he->srcfile = hist_entry__get_srcfile(he);
 479
 480	return repsep_snprintf(bf, size, "%-.*s", width, he->srcfile);
 481}
 482
 483struct sort_entry sort_srcfile = {
 484	.se_header	= "Source File",
 485	.se_cmp		= sort__srcfile_cmp,
 486	.se_snprintf	= hist_entry__srcfile_snprintf,
 487	.se_width_idx	= HISTC_SRCFILE,
 488};
 489
 490/* --sort parent */
 491
 492static int64_t
 493sort__parent_cmp(struct hist_entry *left, struct hist_entry *right)
 494{
 495	struct symbol *sym_l = left->parent;
 496	struct symbol *sym_r = right->parent;
 497
 498	if (!sym_l || !sym_r)
 499		return cmp_null(sym_l, sym_r);
 500
 501	return strcmp(sym_r->name, sym_l->name);
 502}
 503
 504static int hist_entry__parent_snprintf(struct hist_entry *he, char *bf,
 505				       size_t size, unsigned int width)
 506{
 507	return repsep_snprintf(bf, size, "%-*.*s", width, width,
 508			      he->parent ? he->parent->name : "[other]");
 509}
 510
 511struct sort_entry sort_parent = {
 512	.se_header	= "Parent symbol",
 513	.se_cmp		= sort__parent_cmp,
 514	.se_snprintf	= hist_entry__parent_snprintf,
 515	.se_width_idx	= HISTC_PARENT,
 516};
 517
 518/* --sort cpu */
 519
 520static int64_t
 521sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right)
 522{
 523	return right->cpu - left->cpu;
 524}
 525
 526static int hist_entry__cpu_snprintf(struct hist_entry *he, char *bf,
 527				    size_t size, unsigned int width)
 528{
 529	return repsep_snprintf(bf, size, "%*.*d", width, width, he->cpu);
 530}
 531
 532struct sort_entry sort_cpu = {
 533	.se_header      = "CPU",
 534	.se_cmp	        = sort__cpu_cmp,
 535	.se_snprintf    = hist_entry__cpu_snprintf,
 536	.se_width_idx	= HISTC_CPU,
 537};
 538
 539/* --sort socket */
 540
 541static int64_t
 542sort__socket_cmp(struct hist_entry *left, struct hist_entry *right)
 543{
 544	return right->socket - left->socket;
 545}
 546
 547static int hist_entry__socket_snprintf(struct hist_entry *he, char *bf,
 548				    size_t size, unsigned int width)
 549{
 550	return repsep_snprintf(bf, size, "%*.*d", width, width-3, he->socket);
 551}
 552
 553static int hist_entry__socket_filter(struct hist_entry *he, int type, const void *arg)
 554{
 555	int sk = *(const int *)arg;
 556
 557	if (type != HIST_FILTER__SOCKET)
 558		return -1;
 559
 560	return sk >= 0 && he->socket != sk;
 561}
 562
 563struct sort_entry sort_socket = {
 564	.se_header      = "Socket",
 565	.se_cmp	        = sort__socket_cmp,
 566	.se_snprintf    = hist_entry__socket_snprintf,
 567	.se_filter      = hist_entry__socket_filter,
 568	.se_width_idx	= HISTC_SOCKET,
 569};
 570
 571/* --sort trace */
 572
 573static char *get_trace_output(struct hist_entry *he)
 574{
 575	struct trace_seq seq;
 576	struct perf_evsel *evsel;
 577	struct pevent_record rec = {
 578		.data = he->raw_data,
 579		.size = he->raw_size,
 580	};
 581
 582	evsel = hists_to_evsel(he->hists);
 583
 584	trace_seq_init(&seq);
 585	if (symbol_conf.raw_trace) {
 586		pevent_print_fields(&seq, he->raw_data, he->raw_size,
 587				    evsel->tp_format);
 588	} else {
 589		pevent_event_info(&seq, evsel->tp_format, &rec);
 590	}
 591	/*
 592	 * Trim the buffer, it starts at 4KB and we're not going to
 593	 * add anything more to this buffer.
 594	 */
 595	return realloc(seq.buffer, seq.len + 1);
 596}
 597
 598static int64_t
 599sort__trace_cmp(struct hist_entry *left, struct hist_entry *right)
 600{
 601	struct perf_evsel *evsel;
 602
 603	evsel = hists_to_evsel(left->hists);
 604	if (evsel->attr.type != PERF_TYPE_TRACEPOINT)
 605		return 0;
 606
 607	if (left->trace_output == NULL)
 608		left->trace_output = get_trace_output(left);
 609	if (right->trace_output == NULL)
 610		right->trace_output = get_trace_output(right);
 611
 612	return strcmp(right->trace_output, left->trace_output);
 613}
 614
 615static int hist_entry__trace_snprintf(struct hist_entry *he, char *bf,
 616				    size_t size, unsigned int width)
 617{
 618	struct perf_evsel *evsel;
 619
 620	evsel = hists_to_evsel(he->hists);
 621	if (evsel->attr.type != PERF_TYPE_TRACEPOINT)
 622		return scnprintf(bf, size, "%-.*s", width, "N/A");
 623
 624	if (he->trace_output == NULL)
 625		he->trace_output = get_trace_output(he);
 626	return repsep_snprintf(bf, size, "%-.*s", width, he->trace_output);
 627}
 628
 629struct sort_entry sort_trace = {
 630	.se_header      = "Trace output",
 631	.se_cmp	        = sort__trace_cmp,
 632	.se_snprintf    = hist_entry__trace_snprintf,
 633	.se_width_idx	= HISTC_TRACE,
 634};
 635
 636/* sort keys for branch stacks */
 637
 638static int64_t
 639sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right)
 640{
 641	if (!left->branch_info || !right->branch_info)
 642		return cmp_null(left->branch_info, right->branch_info);
 643
 644	return _sort__dso_cmp(left->branch_info->from.map,
 645			      right->branch_info->from.map);
 646}
 647
 648static int hist_entry__dso_from_snprintf(struct hist_entry *he, char *bf,
 649				    size_t size, unsigned int width)
 650{
 651	if (he->branch_info)
 652		return _hist_entry__dso_snprintf(he->branch_info->from.map,
 653						 bf, size, width);
 654	else
 655		return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
 656}
 657
 658static int hist_entry__dso_from_filter(struct hist_entry *he, int type,
 659				       const void *arg)
 660{
 661	const struct dso *dso = arg;
 662
 663	if (type != HIST_FILTER__DSO)
 664		return -1;
 665
 666	return dso && (!he->branch_info || !he->branch_info->from.map ||
 667		       he->branch_info->from.map->dso != dso);
 668}
 669
 670static int64_t
 671sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right)
 672{
 673	if (!left->branch_info || !right->branch_info)
 674		return cmp_null(left->branch_info, right->branch_info);
 675
 676	return _sort__dso_cmp(left->branch_info->to.map,
 677			      right->branch_info->to.map);
 678}
 679
 680static int hist_entry__dso_to_snprintf(struct hist_entry *he, char *bf,
 681				       size_t size, unsigned int width)
 682{
 683	if (he->branch_info)
 684		return _hist_entry__dso_snprintf(he->branch_info->to.map,
 685						 bf, size, width);
 686	else
 687		return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
 688}
 689
 690static int hist_entry__dso_to_filter(struct hist_entry *he, int type,
 691				     const void *arg)
 692{
 693	const struct dso *dso = arg;
 694
 695	if (type != HIST_FILTER__DSO)
 696		return -1;
 697
 698	return dso && (!he->branch_info || !he->branch_info->to.map ||
 699		       he->branch_info->to.map->dso != dso);
 700}
 701
 702static int64_t
 703sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right)
 704{
 705	struct addr_map_symbol *from_l = &left->branch_info->from;
 706	struct addr_map_symbol *from_r = &right->branch_info->from;
 707
 708	if (!left->branch_info || !right->branch_info)
 709		return cmp_null(left->branch_info, right->branch_info);
 710
 711	from_l = &left->branch_info->from;
 712	from_r = &right->branch_info->from;
 713
 714	if (!from_l->sym && !from_r->sym)
 715		return _sort__addr_cmp(from_l->addr, from_r->addr);
 716
 717	return _sort__sym_cmp(from_l->sym, from_r->sym);
 718}
 719
 720static int64_t
 721sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right)
 722{
 723	struct addr_map_symbol *to_l, *to_r;
 724
 725	if (!left->branch_info || !right->branch_info)
 726		return cmp_null(left->branch_info, right->branch_info);
 727
 728	to_l = &left->branch_info->to;
 729	to_r = &right->branch_info->to;
 730
 731	if (!to_l->sym && !to_r->sym)
 732		return _sort__addr_cmp(to_l->addr, to_r->addr);
 733
 734	return _sort__sym_cmp(to_l->sym, to_r->sym);
 735}
 736
 737static int hist_entry__sym_from_snprintf(struct hist_entry *he, char *bf,
 738					 size_t size, unsigned int width)
 739{
 740	if (he->branch_info) {
 741		struct addr_map_symbol *from = &he->branch_info->from;
 742
 743		return _hist_entry__sym_snprintf(from->map, from->sym, from->addr,
 744						 he->level, bf, size, width);
 745	}
 746
 747	return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
 748}
 749
 750static int hist_entry__sym_to_snprintf(struct hist_entry *he, char *bf,
 751				       size_t size, unsigned int width)
 752{
 753	if (he->branch_info) {
 754		struct addr_map_symbol *to = &he->branch_info->to;
 755
 756		return _hist_entry__sym_snprintf(to->map, to->sym, to->addr,
 757						 he->level, bf, size, width);
 758	}
 759
 760	return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
 761}
 762
 763static int hist_entry__sym_from_filter(struct hist_entry *he, int type,
 764				       const void *arg)
 765{
 766	const char *sym = arg;
 767
 768	if (type != HIST_FILTER__SYMBOL)
 769		return -1;
 770
 771	return sym && !(he->branch_info && he->branch_info->from.sym &&
 772			strstr(he->branch_info->from.sym->name, sym));
 773}
 774
 775static int hist_entry__sym_to_filter(struct hist_entry *he, int type,
 776				       const void *arg)
 777{
 778	const char *sym = arg;
 779
 780	if (type != HIST_FILTER__SYMBOL)
 781		return -1;
 782
 783	return sym && !(he->branch_info && he->branch_info->to.sym &&
 784		        strstr(he->branch_info->to.sym->name, sym));
 785}
 786
 787struct sort_entry sort_dso_from = {
 788	.se_header	= "Source Shared Object",
 789	.se_cmp		= sort__dso_from_cmp,
 790	.se_snprintf	= hist_entry__dso_from_snprintf,
 791	.se_filter	= hist_entry__dso_from_filter,
 792	.se_width_idx	= HISTC_DSO_FROM,
 793};
 794
 795struct sort_entry sort_dso_to = {
 796	.se_header	= "Target Shared Object",
 797	.se_cmp		= sort__dso_to_cmp,
 798	.se_snprintf	= hist_entry__dso_to_snprintf,
 799	.se_filter	= hist_entry__dso_to_filter,
 800	.se_width_idx	= HISTC_DSO_TO,
 801};
 802
 803struct sort_entry sort_sym_from = {
 804	.se_header	= "Source Symbol",
 805	.se_cmp		= sort__sym_from_cmp,
 806	.se_snprintf	= hist_entry__sym_from_snprintf,
 807	.se_filter	= hist_entry__sym_from_filter,
 808	.se_width_idx	= HISTC_SYMBOL_FROM,
 809};
 810
 811struct sort_entry sort_sym_to = {
 812	.se_header	= "Target Symbol",
 813	.se_cmp		= sort__sym_to_cmp,
 814	.se_snprintf	= hist_entry__sym_to_snprintf,
 815	.se_filter	= hist_entry__sym_to_filter,
 816	.se_width_idx	= HISTC_SYMBOL_TO,
 817};
 818
 819static int64_t
 820sort__mispredict_cmp(struct hist_entry *left, struct hist_entry *right)
 821{
 822	unsigned char mp, p;
 823
 824	if (!left->branch_info || !right->branch_info)
 825		return cmp_null(left->branch_info, right->branch_info);
 826
 827	mp = left->branch_info->flags.mispred != right->branch_info->flags.mispred;
 828	p  = left->branch_info->flags.predicted != right->branch_info->flags.predicted;
 829	return mp || p;
 830}
 831
 832static int hist_entry__mispredict_snprintf(struct hist_entry *he, char *bf,
 833				    size_t size, unsigned int width){
 834	static const char *out = "N/A";
 835
 836	if (he->branch_info) {
 837		if (he->branch_info->flags.predicted)
 838			out = "N";
 839		else if (he->branch_info->flags.mispred)
 840			out = "Y";
 841	}
 842
 843	return repsep_snprintf(bf, size, "%-*.*s", width, width, out);
 844}
 845
 846static int64_t
 847sort__cycles_cmp(struct hist_entry *left, struct hist_entry *right)
 848{
 849	return left->branch_info->flags.cycles -
 850		right->branch_info->flags.cycles;
 851}
 852
 853static int hist_entry__cycles_snprintf(struct hist_entry *he, char *bf,
 854				    size_t size, unsigned int width)
 855{
 856	if (he->branch_info->flags.cycles == 0)
 857		return repsep_snprintf(bf, size, "%-*s", width, "-");
 858	return repsep_snprintf(bf, size, "%-*hd", width,
 859			       he->branch_info->flags.cycles);
 860}
 861
 862struct sort_entry sort_cycles = {
 863	.se_header	= "Basic Block Cycles",
 864	.se_cmp		= sort__cycles_cmp,
 865	.se_snprintf	= hist_entry__cycles_snprintf,
 866	.se_width_idx	= HISTC_CYCLES,
 867};
 868
 869/* --sort daddr_sym */
 870int64_t
 871sort__daddr_cmp(struct hist_entry *left, struct hist_entry *right)
 872{
 873	uint64_t l = 0, r = 0;
 874
 875	if (left->mem_info)
 876		l = left->mem_info->daddr.addr;
 877	if (right->mem_info)
 878		r = right->mem_info->daddr.addr;
 879
 880	return (int64_t)(r - l);
 881}
 882
 883static int hist_entry__daddr_snprintf(struct hist_entry *he, char *bf,
 884				    size_t size, unsigned int width)
 885{
 886	uint64_t addr = 0;
 887	struct map *map = NULL;
 888	struct symbol *sym = NULL;
 889
 890	if (he->mem_info) {
 891		addr = he->mem_info->daddr.addr;
 892		map = he->mem_info->daddr.map;
 893		sym = he->mem_info->daddr.sym;
 894	}
 895	return _hist_entry__sym_snprintf(map, sym, addr, he->level, bf, size,
 896					 width);
 897}
 898
 899int64_t
 900sort__iaddr_cmp(struct hist_entry *left, struct hist_entry *right)
 901{
 902	uint64_t l = 0, r = 0;
 903
 904	if (left->mem_info)
 905		l = left->mem_info->iaddr.addr;
 906	if (right->mem_info)
 907		r = right->mem_info->iaddr.addr;
 908
 909	return (int64_t)(r - l);
 910}
 911
 912static int hist_entry__iaddr_snprintf(struct hist_entry *he, char *bf,
 913				    size_t size, unsigned int width)
 914{
 915	uint64_t addr = 0;
 916	struct map *map = NULL;
 917	struct symbol *sym = NULL;
 918
 919	if (he->mem_info) {
 920		addr = he->mem_info->iaddr.addr;
 921		map  = he->mem_info->iaddr.map;
 922		sym  = he->mem_info->iaddr.sym;
 923	}
 924	return _hist_entry__sym_snprintf(map, sym, addr, he->level, bf, size,
 925					 width);
 926}
 927
 928static int64_t
 929sort__dso_daddr_cmp(struct hist_entry *left, struct hist_entry *right)
 930{
 931	struct map *map_l = NULL;
 932	struct map *map_r = NULL;
 933
 934	if (left->mem_info)
 935		map_l = left->mem_info->daddr.map;
 936	if (right->mem_info)
 937		map_r = right->mem_info->daddr.map;
 938
 939	return _sort__dso_cmp(map_l, map_r);
 940}
 941
 942static int hist_entry__dso_daddr_snprintf(struct hist_entry *he, char *bf,
 943				    size_t size, unsigned int width)
 944{
 945	struct map *map = NULL;
 946
 947	if (he->mem_info)
 948		map = he->mem_info->daddr.map;
 949
 950	return _hist_entry__dso_snprintf(map, bf, size, width);
 951}
 952
 953static int64_t
 954sort__locked_cmp(struct hist_entry *left, struct hist_entry *right)
 955{
 956	union perf_mem_data_src data_src_l;
 957	union perf_mem_data_src data_src_r;
 958
 959	if (left->mem_info)
 960		data_src_l = left->mem_info->data_src;
 961	else
 962		data_src_l.mem_lock = PERF_MEM_LOCK_NA;
 963
 964	if (right->mem_info)
 965		data_src_r = right->mem_info->data_src;
 966	else
 967		data_src_r.mem_lock = PERF_MEM_LOCK_NA;
 968
 969	return (int64_t)(data_src_r.mem_lock - data_src_l.mem_lock);
 970}
 971
 972static int hist_entry__locked_snprintf(struct hist_entry *he, char *bf,
 973				    size_t size, unsigned int width)
 974{
 975	char out[10];
 
 976
 977	perf_mem__lck_scnprintf(out, sizeof(out), he->mem_info);
 978	return repsep_snprintf(bf, size, "%.*s", width, out);
 
 
 
 
 
 
 
 
 
 979}
 980
 981static int64_t
 982sort__tlb_cmp(struct hist_entry *left, struct hist_entry *right)
 983{
 984	union perf_mem_data_src data_src_l;
 985	union perf_mem_data_src data_src_r;
 986
 987	if (left->mem_info)
 988		data_src_l = left->mem_info->data_src;
 989	else
 990		data_src_l.mem_dtlb = PERF_MEM_TLB_NA;
 991
 992	if (right->mem_info)
 993		data_src_r = right->mem_info->data_src;
 994	else
 995		data_src_r.mem_dtlb = PERF_MEM_TLB_NA;
 996
 997	return (int64_t)(data_src_r.mem_dtlb - data_src_l.mem_dtlb);
 998}
 999
 
 
 
 
 
 
 
 
 
 
 
1000static int hist_entry__tlb_snprintf(struct hist_entry *he, char *bf,
1001				    size_t size, unsigned int width)
1002{
1003	char out[64];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1004
1005	perf_mem__tlb_scnprintf(out, sizeof(out), he->mem_info);
1006	return repsep_snprintf(bf, size, "%-*s", width, out);
1007}
1008
1009static int64_t
1010sort__lvl_cmp(struct hist_entry *left, struct hist_entry *right)
1011{
1012	union perf_mem_data_src data_src_l;
1013	union perf_mem_data_src data_src_r;
1014
1015	if (left->mem_info)
1016		data_src_l = left->mem_info->data_src;
1017	else
1018		data_src_l.mem_lvl = PERF_MEM_LVL_NA;
1019
1020	if (right->mem_info)
1021		data_src_r = right->mem_info->data_src;
1022	else
1023		data_src_r.mem_lvl = PERF_MEM_LVL_NA;
1024
1025	return (int64_t)(data_src_r.mem_lvl - data_src_l.mem_lvl);
1026}
1027
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1028static int hist_entry__lvl_snprintf(struct hist_entry *he, char *bf,
1029				    size_t size, unsigned int width)
1030{
1031	char out[64];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1032
1033	perf_mem__lvl_scnprintf(out, sizeof(out), he->mem_info);
1034	return repsep_snprintf(bf, size, "%-*s", width, out);
1035}
1036
1037static int64_t
1038sort__snoop_cmp(struct hist_entry *left, struct hist_entry *right)
1039{
1040	union perf_mem_data_src data_src_l;
1041	union perf_mem_data_src data_src_r;
1042
1043	if (left->mem_info)
1044		data_src_l = left->mem_info->data_src;
1045	else
1046		data_src_l.mem_snoop = PERF_MEM_SNOOP_NA;
1047
1048	if (right->mem_info)
1049		data_src_r = right->mem_info->data_src;
1050	else
1051		data_src_r.mem_snoop = PERF_MEM_SNOOP_NA;
1052
1053	return (int64_t)(data_src_r.mem_snoop - data_src_l.mem_snoop);
1054}
1055
 
 
 
 
 
 
 
 
 
1056static int hist_entry__snoop_snprintf(struct hist_entry *he, char *bf,
1057				    size_t size, unsigned int width)
1058{
1059	char out[64];
 
 
 
1060
1061	perf_mem__snp_scnprintf(out, sizeof(out), he->mem_info);
1062	return repsep_snprintf(bf, size, "%-*s", width, out);
1063}
1064
1065int64_t
1066sort__dcacheline_cmp(struct hist_entry *left, struct hist_entry *right)
1067{
1068	u64 l, r;
1069	struct map *l_map, *r_map;
1070
1071	if (!left->mem_info)  return -1;
1072	if (!right->mem_info) return 1;
1073
1074	/* group event types together */
1075	if (left->cpumode > right->cpumode) return -1;
1076	if (left->cpumode < right->cpumode) return 1;
1077
1078	l_map = left->mem_info->daddr.map;
1079	r_map = right->mem_info->daddr.map;
1080
1081	/* if both are NULL, jump to sort on al_addr instead */
1082	if (!l_map && !r_map)
1083		goto addr;
1084
1085	if (!l_map) return -1;
1086	if (!r_map) return 1;
1087
1088	if (l_map->maj > r_map->maj) return -1;
1089	if (l_map->maj < r_map->maj) return 1;
1090
1091	if (l_map->min > r_map->min) return -1;
1092	if (l_map->min < r_map->min) return 1;
1093
1094	if (l_map->ino > r_map->ino) return -1;
1095	if (l_map->ino < r_map->ino) return 1;
1096
1097	if (l_map->ino_generation > r_map->ino_generation) return -1;
1098	if (l_map->ino_generation < r_map->ino_generation) return 1;
1099
1100	/*
1101	 * Addresses with no major/minor numbers are assumed to be
1102	 * anonymous in userspace.  Sort those on pid then address.
1103	 *
1104	 * The kernel and non-zero major/minor mapped areas are
1105	 * assumed to be unity mapped.  Sort those on address.
1106	 */
1107
1108	if ((left->cpumode != PERF_RECORD_MISC_KERNEL) &&
1109	    (!(l_map->flags & MAP_SHARED)) &&
1110	    !l_map->maj && !l_map->min && !l_map->ino &&
1111	    !l_map->ino_generation) {
1112		/* userspace anonymous */
1113
1114		if (left->thread->pid_ > right->thread->pid_) return -1;
1115		if (left->thread->pid_ < right->thread->pid_) return 1;
 
 
 
 
 
 
 
1116	}
1117
1118addr:
1119	/* al_addr does all the right addr - start + offset calculations */
1120	l = cl_address(left->mem_info->daddr.al_addr);
1121	r = cl_address(right->mem_info->daddr.al_addr);
1122
1123	if (l > r) return -1;
1124	if (l < r) return 1;
1125
1126	return 0;
1127}
1128
1129static int hist_entry__dcacheline_snprintf(struct hist_entry *he, char *bf,
1130					  size_t size, unsigned int width)
1131{
1132
1133	uint64_t addr = 0;
1134	struct map *map = NULL;
1135	struct symbol *sym = NULL;
1136	char level = he->level;
1137
1138	if (he->mem_info) {
1139		addr = cl_address(he->mem_info->daddr.al_addr);
1140		map = he->mem_info->daddr.map;
1141		sym = he->mem_info->daddr.sym;
1142
1143		/* print [s] for shared data mmaps */
1144		if ((he->cpumode != PERF_RECORD_MISC_KERNEL) &&
1145		     map && (map->type == MAP__VARIABLE) &&
1146		    (map->flags & MAP_SHARED) &&
1147		    (map->maj || map->min || map->ino ||
1148		     map->ino_generation))
1149			level = 's';
1150		else if (!map)
1151			level = 'X';
1152	}
1153	return _hist_entry__sym_snprintf(map, sym, addr, level, bf, size,
1154					 width);
1155}
1156
1157struct sort_entry sort_mispredict = {
1158	.se_header	= "Branch Mispredicted",
1159	.se_cmp		= sort__mispredict_cmp,
1160	.se_snprintf	= hist_entry__mispredict_snprintf,
1161	.se_width_idx	= HISTC_MISPREDICT,
1162};
1163
1164static u64 he_weight(struct hist_entry *he)
1165{
1166	return he->stat.nr_events ? he->stat.weight / he->stat.nr_events : 0;
1167}
1168
1169static int64_t
1170sort__local_weight_cmp(struct hist_entry *left, struct hist_entry *right)
1171{
1172	return he_weight(left) - he_weight(right);
1173}
1174
1175static int hist_entry__local_weight_snprintf(struct hist_entry *he, char *bf,
1176				    size_t size, unsigned int width)
1177{
1178	return repsep_snprintf(bf, size, "%-*llu", width, he_weight(he));
1179}
1180
1181struct sort_entry sort_local_weight = {
1182	.se_header	= "Local Weight",
1183	.se_cmp		= sort__local_weight_cmp,
1184	.se_snprintf	= hist_entry__local_weight_snprintf,
1185	.se_width_idx	= HISTC_LOCAL_WEIGHT,
1186};
1187
1188static int64_t
1189sort__global_weight_cmp(struct hist_entry *left, struct hist_entry *right)
1190{
1191	return left->stat.weight - right->stat.weight;
1192}
1193
1194static int hist_entry__global_weight_snprintf(struct hist_entry *he, char *bf,
1195					      size_t size, unsigned int width)
1196{
1197	return repsep_snprintf(bf, size, "%-*llu", width, he->stat.weight);
1198}
1199
1200struct sort_entry sort_global_weight = {
1201	.se_header	= "Weight",
1202	.se_cmp		= sort__global_weight_cmp,
1203	.se_snprintf	= hist_entry__global_weight_snprintf,
1204	.se_width_idx	= HISTC_GLOBAL_WEIGHT,
1205};
1206
1207struct sort_entry sort_mem_daddr_sym = {
1208	.se_header	= "Data Symbol",
1209	.se_cmp		= sort__daddr_cmp,
1210	.se_snprintf	= hist_entry__daddr_snprintf,
1211	.se_width_idx	= HISTC_MEM_DADDR_SYMBOL,
1212};
1213
1214struct sort_entry sort_mem_iaddr_sym = {
1215	.se_header	= "Code Symbol",
1216	.se_cmp		= sort__iaddr_cmp,
1217	.se_snprintf	= hist_entry__iaddr_snprintf,
1218	.se_width_idx	= HISTC_MEM_IADDR_SYMBOL,
1219};
1220
1221struct sort_entry sort_mem_daddr_dso = {
1222	.se_header	= "Data Object",
1223	.se_cmp		= sort__dso_daddr_cmp,
1224	.se_snprintf	= hist_entry__dso_daddr_snprintf,
1225	.se_width_idx	= HISTC_MEM_DADDR_DSO,
1226};
1227
1228struct sort_entry sort_mem_locked = {
1229	.se_header	= "Locked",
1230	.se_cmp		= sort__locked_cmp,
1231	.se_snprintf	= hist_entry__locked_snprintf,
1232	.se_width_idx	= HISTC_MEM_LOCKED,
1233};
1234
1235struct sort_entry sort_mem_tlb = {
1236	.se_header	= "TLB access",
1237	.se_cmp		= sort__tlb_cmp,
1238	.se_snprintf	= hist_entry__tlb_snprintf,
1239	.se_width_idx	= HISTC_MEM_TLB,
1240};
1241
1242struct sort_entry sort_mem_lvl = {
1243	.se_header	= "Memory access",
1244	.se_cmp		= sort__lvl_cmp,
1245	.se_snprintf	= hist_entry__lvl_snprintf,
1246	.se_width_idx	= HISTC_MEM_LVL,
1247};
1248
1249struct sort_entry sort_mem_snoop = {
1250	.se_header	= "Snoop",
1251	.se_cmp		= sort__snoop_cmp,
1252	.se_snprintf	= hist_entry__snoop_snprintf,
1253	.se_width_idx	= HISTC_MEM_SNOOP,
1254};
1255
1256struct sort_entry sort_mem_dcacheline = {
1257	.se_header	= "Data Cacheline",
1258	.se_cmp		= sort__dcacheline_cmp,
1259	.se_snprintf	= hist_entry__dcacheline_snprintf,
1260	.se_width_idx	= HISTC_MEM_DCACHELINE,
1261};
1262
1263static int64_t
1264sort__abort_cmp(struct hist_entry *left, struct hist_entry *right)
1265{
1266	if (!left->branch_info || !right->branch_info)
1267		return cmp_null(left->branch_info, right->branch_info);
1268
1269	return left->branch_info->flags.abort !=
1270		right->branch_info->flags.abort;
1271}
1272
1273static int hist_entry__abort_snprintf(struct hist_entry *he, char *bf,
1274				    size_t size, unsigned int width)
1275{
1276	static const char *out = "N/A";
1277
1278	if (he->branch_info) {
1279		if (he->branch_info->flags.abort)
1280			out = "A";
1281		else
1282			out = ".";
1283	}
1284
 
 
1285	return repsep_snprintf(bf, size, "%-*s", width, out);
1286}
1287
1288struct sort_entry sort_abort = {
1289	.se_header	= "Transaction abort",
1290	.se_cmp		= sort__abort_cmp,
1291	.se_snprintf	= hist_entry__abort_snprintf,
1292	.se_width_idx	= HISTC_ABORT,
1293};
1294
1295static int64_t
1296sort__in_tx_cmp(struct hist_entry *left, struct hist_entry *right)
1297{
1298	if (!left->branch_info || !right->branch_info)
1299		return cmp_null(left->branch_info, right->branch_info);
1300
1301	return left->branch_info->flags.in_tx !=
1302		right->branch_info->flags.in_tx;
1303}
1304
1305static int hist_entry__in_tx_snprintf(struct hist_entry *he, char *bf,
1306				    size_t size, unsigned int width)
1307{
1308	static const char *out = "N/A";
1309
1310	if (he->branch_info) {
1311		if (he->branch_info->flags.in_tx)
1312			out = "T";
1313		else
1314			out = ".";
1315	}
1316
1317	return repsep_snprintf(bf, size, "%-*s", width, out);
1318}
1319
1320struct sort_entry sort_in_tx = {
1321	.se_header	= "Branch in transaction",
1322	.se_cmp		= sort__in_tx_cmp,
1323	.se_snprintf	= hist_entry__in_tx_snprintf,
1324	.se_width_idx	= HISTC_IN_TX,
1325};
1326
1327static int64_t
1328sort__transaction_cmp(struct hist_entry *left, struct hist_entry *right)
1329{
1330	return left->transaction - right->transaction;
1331}
1332
1333static inline char *add_str(char *p, const char *str)
1334{
1335	strcpy(p, str);
1336	return p + strlen(str);
1337}
1338
1339static struct txbit {
1340	unsigned flag;
1341	const char *name;
1342	int skip_for_len;
1343} txbits[] = {
1344	{ PERF_TXN_ELISION,        "EL ",        0 },
1345	{ PERF_TXN_TRANSACTION,    "TX ",        1 },
1346	{ PERF_TXN_SYNC,           "SYNC ",      1 },
1347	{ PERF_TXN_ASYNC,          "ASYNC ",     0 },
1348	{ PERF_TXN_RETRY,          "RETRY ",     0 },
1349	{ PERF_TXN_CONFLICT,       "CON ",       0 },
1350	{ PERF_TXN_CAPACITY_WRITE, "CAP-WRITE ", 1 },
1351	{ PERF_TXN_CAPACITY_READ,  "CAP-READ ",  0 },
1352	{ 0, NULL, 0 }
1353};
1354
1355int hist_entry__transaction_len(void)
1356{
1357	int i;
1358	int len = 0;
1359
1360	for (i = 0; txbits[i].name; i++) {
1361		if (!txbits[i].skip_for_len)
1362			len += strlen(txbits[i].name);
1363	}
1364	len += 4; /* :XX<space> */
1365	return len;
1366}
1367
1368static int hist_entry__transaction_snprintf(struct hist_entry *he, char *bf,
1369					    size_t size, unsigned int width)
1370{
1371	u64 t = he->transaction;
1372	char buf[128];
1373	char *p = buf;
1374	int i;
1375
1376	buf[0] = 0;
1377	for (i = 0; txbits[i].name; i++)
1378		if (txbits[i].flag & t)
1379			p = add_str(p, txbits[i].name);
1380	if (t && !(t & (PERF_TXN_SYNC|PERF_TXN_ASYNC)))
1381		p = add_str(p, "NEITHER ");
1382	if (t & PERF_TXN_ABORT_MASK) {
1383		sprintf(p, ":%" PRIx64,
1384			(t & PERF_TXN_ABORT_MASK) >>
1385			PERF_TXN_ABORT_SHIFT);
1386		p += strlen(p);
1387	}
1388
1389	return repsep_snprintf(bf, size, "%-*s", width, buf);
1390}
1391
1392struct sort_entry sort_transaction = {
1393	.se_header	= "Transaction                ",
1394	.se_cmp		= sort__transaction_cmp,
1395	.se_snprintf	= hist_entry__transaction_snprintf,
1396	.se_width_idx	= HISTC_TRANSACTION,
1397};
1398
1399struct sort_dimension {
1400	const char		*name;
1401	struct sort_entry	*entry;
1402	int			taken;
1403};
1404
1405#define DIM(d, n, func) [d] = { .name = n, .entry = &(func) }
1406
1407static struct sort_dimension common_sort_dimensions[] = {
1408	DIM(SORT_PID, "pid", sort_thread),
1409	DIM(SORT_COMM, "comm", sort_comm),
1410	DIM(SORT_DSO, "dso", sort_dso),
1411	DIM(SORT_SYM, "symbol", sort_sym),
1412	DIM(SORT_PARENT, "parent", sort_parent),
1413	DIM(SORT_CPU, "cpu", sort_cpu),
1414	DIM(SORT_SOCKET, "socket", sort_socket),
1415	DIM(SORT_SRCLINE, "srcline", sort_srcline),
1416	DIM(SORT_SRCFILE, "srcfile", sort_srcfile),
1417	DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight),
1418	DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight),
1419	DIM(SORT_TRANSACTION, "transaction", sort_transaction),
1420	DIM(SORT_TRACE, "trace", sort_trace),
1421};
1422
1423#undef DIM
1424
1425#define DIM(d, n, func) [d - __SORT_BRANCH_STACK] = { .name = n, .entry = &(func) }
1426
1427static struct sort_dimension bstack_sort_dimensions[] = {
1428	DIM(SORT_DSO_FROM, "dso_from", sort_dso_from),
1429	DIM(SORT_DSO_TO, "dso_to", sort_dso_to),
1430	DIM(SORT_SYM_FROM, "symbol_from", sort_sym_from),
1431	DIM(SORT_SYM_TO, "symbol_to", sort_sym_to),
1432	DIM(SORT_MISPREDICT, "mispredict", sort_mispredict),
1433	DIM(SORT_IN_TX, "in_tx", sort_in_tx),
1434	DIM(SORT_ABORT, "abort", sort_abort),
1435	DIM(SORT_CYCLES, "cycles", sort_cycles),
1436	DIM(SORT_SRCLINE_FROM, "srcline_from", sort_srcline_from),
1437	DIM(SORT_SRCLINE_TO, "srcline_to", sort_srcline_to),
1438};
1439
1440#undef DIM
1441
1442#define DIM(d, n, func) [d - __SORT_MEMORY_MODE] = { .name = n, .entry = &(func) }
1443
1444static struct sort_dimension memory_sort_dimensions[] = {
1445	DIM(SORT_MEM_DADDR_SYMBOL, "symbol_daddr", sort_mem_daddr_sym),
1446	DIM(SORT_MEM_IADDR_SYMBOL, "symbol_iaddr", sort_mem_iaddr_sym),
1447	DIM(SORT_MEM_DADDR_DSO, "dso_daddr", sort_mem_daddr_dso),
1448	DIM(SORT_MEM_LOCKED, "locked", sort_mem_locked),
1449	DIM(SORT_MEM_TLB, "tlb", sort_mem_tlb),
1450	DIM(SORT_MEM_LVL, "mem", sort_mem_lvl),
1451	DIM(SORT_MEM_SNOOP, "snoop", sort_mem_snoop),
1452	DIM(SORT_MEM_DCACHELINE, "dcacheline", sort_mem_dcacheline),
1453};
1454
1455#undef DIM
1456
1457struct hpp_dimension {
1458	const char		*name;
1459	struct perf_hpp_fmt	*fmt;
1460	int			taken;
1461};
1462
1463#define DIM(d, n) { .name = n, .fmt = &perf_hpp__format[d], }
1464
1465static struct hpp_dimension hpp_sort_dimensions[] = {
1466	DIM(PERF_HPP__OVERHEAD, "overhead"),
1467	DIM(PERF_HPP__OVERHEAD_SYS, "overhead_sys"),
1468	DIM(PERF_HPP__OVERHEAD_US, "overhead_us"),
1469	DIM(PERF_HPP__OVERHEAD_GUEST_SYS, "overhead_guest_sys"),
1470	DIM(PERF_HPP__OVERHEAD_GUEST_US, "overhead_guest_us"),
1471	DIM(PERF_HPP__OVERHEAD_ACC, "overhead_children"),
1472	DIM(PERF_HPP__SAMPLES, "sample"),
1473	DIM(PERF_HPP__PERIOD, "period"),
1474};
1475
1476#undef DIM
1477
1478struct hpp_sort_entry {
1479	struct perf_hpp_fmt hpp;
1480	struct sort_entry *se;
1481};
1482
1483void perf_hpp__reset_sort_width(struct perf_hpp_fmt *fmt, struct hists *hists)
1484{
1485	struct hpp_sort_entry *hse;
1486
1487	if (!perf_hpp__is_sort_entry(fmt))
1488		return;
1489
1490	hse = container_of(fmt, struct hpp_sort_entry, hpp);
1491	hists__new_col_len(hists, hse->se->se_width_idx, strlen(fmt->name));
1492}
1493
1494static int __sort__hpp_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1495			      struct hists *hists, int line __maybe_unused,
1496			      int *span __maybe_unused)
1497{
1498	struct hpp_sort_entry *hse;
1499	size_t len = fmt->user_len;
1500
1501	hse = container_of(fmt, struct hpp_sort_entry, hpp);
1502
1503	if (!len)
1504		len = hists__col_len(hists, hse->se->se_width_idx);
1505
1506	return scnprintf(hpp->buf, hpp->size, "%-*.*s", len, len, fmt->name);
1507}
1508
1509static int __sort__hpp_width(struct perf_hpp_fmt *fmt,
1510			     struct perf_hpp *hpp __maybe_unused,
1511			     struct hists *hists)
1512{
1513	struct hpp_sort_entry *hse;
1514	size_t len = fmt->user_len;
1515
1516	hse = container_of(fmt, struct hpp_sort_entry, hpp);
1517
1518	if (!len)
1519		len = hists__col_len(hists, hse->se->se_width_idx);
1520
1521	return len;
1522}
1523
1524static int __sort__hpp_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1525			     struct hist_entry *he)
1526{
1527	struct hpp_sort_entry *hse;
1528	size_t len = fmt->user_len;
1529
1530	hse = container_of(fmt, struct hpp_sort_entry, hpp);
1531
1532	if (!len)
1533		len = hists__col_len(he->hists, hse->se->se_width_idx);
1534
1535	return hse->se->se_snprintf(he, hpp->buf, hpp->size, len);
1536}
1537
1538static int64_t __sort__hpp_cmp(struct perf_hpp_fmt *fmt,
1539			       struct hist_entry *a, struct hist_entry *b)
1540{
1541	struct hpp_sort_entry *hse;
1542
1543	hse = container_of(fmt, struct hpp_sort_entry, hpp);
1544	return hse->se->se_cmp(a, b);
1545}
1546
1547static int64_t __sort__hpp_collapse(struct perf_hpp_fmt *fmt,
1548				    struct hist_entry *a, struct hist_entry *b)
1549{
1550	struct hpp_sort_entry *hse;
1551	int64_t (*collapse_fn)(struct hist_entry *, struct hist_entry *);
1552
1553	hse = container_of(fmt, struct hpp_sort_entry, hpp);
1554	collapse_fn = hse->se->se_collapse ?: hse->se->se_cmp;
1555	return collapse_fn(a, b);
1556}
1557
1558static int64_t __sort__hpp_sort(struct perf_hpp_fmt *fmt,
1559				struct hist_entry *a, struct hist_entry *b)
1560{
1561	struct hpp_sort_entry *hse;
1562	int64_t (*sort_fn)(struct hist_entry *, struct hist_entry *);
1563
1564	hse = container_of(fmt, struct hpp_sort_entry, hpp);
1565	sort_fn = hse->se->se_sort ?: hse->se->se_cmp;
1566	return sort_fn(a, b);
1567}
1568
1569bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format)
1570{
1571	return format->header == __sort__hpp_header;
1572}
1573
1574#define MK_SORT_ENTRY_CHK(key)					\
1575bool perf_hpp__is_ ## key ## _entry(struct perf_hpp_fmt *fmt)	\
1576{								\
1577	struct hpp_sort_entry *hse;				\
1578								\
1579	if (!perf_hpp__is_sort_entry(fmt))			\
1580		return false;					\
1581								\
1582	hse = container_of(fmt, struct hpp_sort_entry, hpp);	\
1583	return hse->se == &sort_ ## key ;			\
1584}
1585
1586MK_SORT_ENTRY_CHK(trace)
1587MK_SORT_ENTRY_CHK(srcline)
1588MK_SORT_ENTRY_CHK(srcfile)
1589MK_SORT_ENTRY_CHK(thread)
1590MK_SORT_ENTRY_CHK(comm)
1591MK_SORT_ENTRY_CHK(dso)
1592MK_SORT_ENTRY_CHK(sym)
1593
1594
1595static bool __sort__hpp_equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)
1596{
1597	struct hpp_sort_entry *hse_a;
1598	struct hpp_sort_entry *hse_b;
1599
1600	if (!perf_hpp__is_sort_entry(a) || !perf_hpp__is_sort_entry(b))
1601		return false;
1602
1603	hse_a = container_of(a, struct hpp_sort_entry, hpp);
1604	hse_b = container_of(b, struct hpp_sort_entry, hpp);
1605
1606	return hse_a->se == hse_b->se;
1607}
1608
1609static void hse_free(struct perf_hpp_fmt *fmt)
1610{
1611	struct hpp_sort_entry *hse;
1612
1613	hse = container_of(fmt, struct hpp_sort_entry, hpp);
1614	free(hse);
1615}
1616
1617static struct hpp_sort_entry *
1618__sort_dimension__alloc_hpp(struct sort_dimension *sd, int level)
1619{
1620	struct hpp_sort_entry *hse;
1621
1622	hse = malloc(sizeof(*hse));
1623	if (hse == NULL) {
1624		pr_err("Memory allocation failed\n");
1625		return NULL;
1626	}
1627
1628	hse->se = sd->entry;
1629	hse->hpp.name = sd->entry->se_header;
1630	hse->hpp.header = __sort__hpp_header;
1631	hse->hpp.width = __sort__hpp_width;
1632	hse->hpp.entry = __sort__hpp_entry;
1633	hse->hpp.color = NULL;
1634
1635	hse->hpp.cmp = __sort__hpp_cmp;
1636	hse->hpp.collapse = __sort__hpp_collapse;
1637	hse->hpp.sort = __sort__hpp_sort;
1638	hse->hpp.equal = __sort__hpp_equal;
1639	hse->hpp.free = hse_free;
1640
1641	INIT_LIST_HEAD(&hse->hpp.list);
1642	INIT_LIST_HEAD(&hse->hpp.sort_list);
1643	hse->hpp.elide = false;
1644	hse->hpp.len = 0;
1645	hse->hpp.user_len = 0;
1646	hse->hpp.level = level;
1647
1648	return hse;
1649}
1650
1651static void hpp_free(struct perf_hpp_fmt *fmt)
1652{
1653	free(fmt);
1654}
1655
1656static struct perf_hpp_fmt *__hpp_dimension__alloc_hpp(struct hpp_dimension *hd,
1657						       int level)
1658{
1659	struct perf_hpp_fmt *fmt;
1660
1661	fmt = memdup(hd->fmt, sizeof(*fmt));
1662	if (fmt) {
1663		INIT_LIST_HEAD(&fmt->list);
1664		INIT_LIST_HEAD(&fmt->sort_list);
1665		fmt->free = hpp_free;
1666		fmt->level = level;
1667	}
1668
1669	return fmt;
1670}
1671
1672int hist_entry__filter(struct hist_entry *he, int type, const void *arg)
1673{
1674	struct perf_hpp_fmt *fmt;
1675	struct hpp_sort_entry *hse;
1676	int ret = -1;
1677	int r;
1678
1679	perf_hpp_list__for_each_format(he->hpp_list, fmt) {
1680		if (!perf_hpp__is_sort_entry(fmt))
1681			continue;
1682
1683		hse = container_of(fmt, struct hpp_sort_entry, hpp);
1684		if (hse->se->se_filter == NULL)
1685			continue;
1686
1687		/*
1688		 * hist entry is filtered if any of sort key in the hpp list
1689		 * is applied.  But it should skip non-matched filter types.
1690		 */
1691		r = hse->se->se_filter(he, type, arg);
1692		if (r >= 0) {
1693			if (ret < 0)
1694				ret = 0;
1695			ret |= r;
1696		}
1697	}
1698
1699	return ret;
1700}
1701
1702static int __sort_dimension__add_hpp_sort(struct sort_dimension *sd,
1703					  struct perf_hpp_list *list,
1704					  int level)
1705{
1706	struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd, level);
1707
1708	if (hse == NULL)
1709		return -1;
1710
1711	perf_hpp_list__register_sort_field(list, &hse->hpp);
1712	return 0;
1713}
1714
1715static int __sort_dimension__add_hpp_output(struct sort_dimension *sd,
1716					    struct perf_hpp_list *list)
1717{
1718	struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd, 0);
1719
1720	if (hse == NULL)
1721		return -1;
1722
1723	perf_hpp_list__column_register(list, &hse->hpp);
1724	return 0;
1725}
1726
1727struct hpp_dynamic_entry {
1728	struct perf_hpp_fmt hpp;
1729	struct perf_evsel *evsel;
1730	struct format_field *field;
1731	unsigned dynamic_len;
1732	bool raw_trace;
1733};
1734
1735static int hde_width(struct hpp_dynamic_entry *hde)
1736{
1737	if (!hde->hpp.len) {
1738		int len = hde->dynamic_len;
1739		int namelen = strlen(hde->field->name);
1740		int fieldlen = hde->field->size;
1741
1742		if (namelen > len)
1743			len = namelen;
1744
1745		if (!(hde->field->flags & FIELD_IS_STRING)) {
1746			/* length for print hex numbers */
1747			fieldlen = hde->field->size * 2 + 2;
1748		}
1749		if (fieldlen > len)
1750			len = fieldlen;
1751
1752		hde->hpp.len = len;
1753	}
1754	return hde->hpp.len;
1755}
1756
1757static void update_dynamic_len(struct hpp_dynamic_entry *hde,
1758			       struct hist_entry *he)
1759{
1760	char *str, *pos;
1761	struct format_field *field = hde->field;
1762	size_t namelen;
1763	bool last = false;
1764
1765	if (hde->raw_trace)
1766		return;
1767
1768	/* parse pretty print result and update max length */
1769	if (!he->trace_output)
1770		he->trace_output = get_trace_output(he);
1771
1772	namelen = strlen(field->name);
1773	str = he->trace_output;
1774
1775	while (str) {
1776		pos = strchr(str, ' ');
1777		if (pos == NULL) {
1778			last = true;
1779			pos = str + strlen(str);
1780		}
1781
1782		if (!strncmp(str, field->name, namelen)) {
1783			size_t len;
1784
1785			str += namelen + 1;
1786			len = pos - str;
1787
1788			if (len > hde->dynamic_len)
1789				hde->dynamic_len = len;
1790			break;
1791		}
1792
1793		if (last)
1794			str = NULL;
1795		else
1796			str = pos + 1;
1797	}
1798}
1799
1800static int __sort__hde_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1801			      struct hists *hists __maybe_unused,
1802			      int line __maybe_unused,
1803			      int *span __maybe_unused)
1804{
1805	struct hpp_dynamic_entry *hde;
1806	size_t len = fmt->user_len;
1807
1808	hde = container_of(fmt, struct hpp_dynamic_entry, hpp);
1809
1810	if (!len)
1811		len = hde_width(hde);
1812
1813	return scnprintf(hpp->buf, hpp->size, "%*.*s", len, len, hde->field->name);
1814}
1815
1816static int __sort__hde_width(struct perf_hpp_fmt *fmt,
1817			     struct perf_hpp *hpp __maybe_unused,
1818			     struct hists *hists __maybe_unused)
1819{
1820	struct hpp_dynamic_entry *hde;
1821	size_t len = fmt->user_len;
1822
1823	hde = container_of(fmt, struct hpp_dynamic_entry, hpp);
1824
1825	if (!len)
1826		len = hde_width(hde);
1827
1828	return len;
1829}
1830
1831bool perf_hpp__defined_dynamic_entry(struct perf_hpp_fmt *fmt, struct hists *hists)
1832{
1833	struct hpp_dynamic_entry *hde;
1834
1835	hde = container_of(fmt, struct hpp_dynamic_entry, hpp);
1836
1837	return hists_to_evsel(hists) == hde->evsel;
1838}
1839
1840static int __sort__hde_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1841			     struct hist_entry *he)
1842{
1843	struct hpp_dynamic_entry *hde;
1844	size_t len = fmt->user_len;
1845	char *str, *pos;
1846	struct format_field *field;
1847	size_t namelen;
1848	bool last = false;
1849	int ret;
1850
1851	hde = container_of(fmt, struct hpp_dynamic_entry, hpp);
1852
1853	if (!len)
1854		len = hde_width(hde);
1855
1856	if (hde->raw_trace)
1857		goto raw_field;
1858
1859	if (!he->trace_output)
1860		he->trace_output = get_trace_output(he);
1861
1862	field = hde->field;
1863	namelen = strlen(field->name);
1864	str = he->trace_output;
1865
1866	while (str) {
1867		pos = strchr(str, ' ');
1868		if (pos == NULL) {
1869			last = true;
1870			pos = str + strlen(str);
1871		}
1872
1873		if (!strncmp(str, field->name, namelen)) {
1874			str += namelen + 1;
1875			str = strndup(str, pos - str);
1876
1877			if (str == NULL)
1878				return scnprintf(hpp->buf, hpp->size,
1879						 "%*.*s", len, len, "ERROR");
1880			break;
1881		}
1882
1883		if (last)
1884			str = NULL;
1885		else
1886			str = pos + 1;
1887	}
1888
1889	if (str == NULL) {
1890		struct trace_seq seq;
1891raw_field:
1892		trace_seq_init(&seq);
1893		pevent_print_field(&seq, he->raw_data, hde->field);
1894		str = seq.buffer;
1895	}
1896
1897	ret = scnprintf(hpp->buf, hpp->size, "%*.*s", len, len, str);
1898	free(str);
1899	return ret;
1900}
1901
1902static int64_t __sort__hde_cmp(struct perf_hpp_fmt *fmt,
1903			       struct hist_entry *a, struct hist_entry *b)
1904{
1905	struct hpp_dynamic_entry *hde;
1906	struct format_field *field;
1907	unsigned offset, size;
1908
1909	hde = container_of(fmt, struct hpp_dynamic_entry, hpp);
1910
1911	if (b == NULL) {
1912		update_dynamic_len(hde, a);
1913		return 0;
1914	}
1915
1916	field = hde->field;
1917	if (field->flags & FIELD_IS_DYNAMIC) {
1918		unsigned long long dyn;
1919
1920		pevent_read_number_field(field, a->raw_data, &dyn);
1921		offset = dyn & 0xffff;
1922		size = (dyn >> 16) & 0xffff;
1923
1924		/* record max width for output */
1925		if (size > hde->dynamic_len)
1926			hde->dynamic_len = size;
1927	} else {
1928		offset = field->offset;
1929		size = field->size;
1930	}
1931
1932	return memcmp(a->raw_data + offset, b->raw_data + offset, size);
1933}
1934
1935bool perf_hpp__is_dynamic_entry(struct perf_hpp_fmt *fmt)
1936{
1937	return fmt->cmp == __sort__hde_cmp;
1938}
1939
1940static bool __sort__hde_equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)
1941{
1942	struct hpp_dynamic_entry *hde_a;
1943	struct hpp_dynamic_entry *hde_b;
1944
1945	if (!perf_hpp__is_dynamic_entry(a) || !perf_hpp__is_dynamic_entry(b))
1946		return false;
1947
1948	hde_a = container_of(a, struct hpp_dynamic_entry, hpp);
1949	hde_b = container_of(b, struct hpp_dynamic_entry, hpp);
1950
1951	return hde_a->field == hde_b->field;
1952}
1953
1954static void hde_free(struct perf_hpp_fmt *fmt)
1955{
1956	struct hpp_dynamic_entry *hde;
1957
1958	hde = container_of(fmt, struct hpp_dynamic_entry, hpp);
1959	free(hde);
1960}
1961
1962static struct hpp_dynamic_entry *
1963__alloc_dynamic_entry(struct perf_evsel *evsel, struct format_field *field,
1964		      int level)
1965{
1966	struct hpp_dynamic_entry *hde;
1967
1968	hde = malloc(sizeof(*hde));
1969	if (hde == NULL) {
1970		pr_debug("Memory allocation failed\n");
1971		return NULL;
1972	}
1973
1974	hde->evsel = evsel;
1975	hde->field = field;
1976	hde->dynamic_len = 0;
1977
1978	hde->hpp.name = field->name;
1979	hde->hpp.header = __sort__hde_header;
1980	hde->hpp.width  = __sort__hde_width;
1981	hde->hpp.entry  = __sort__hde_entry;
1982	hde->hpp.color  = NULL;
1983
1984	hde->hpp.cmp = __sort__hde_cmp;
1985	hde->hpp.collapse = __sort__hde_cmp;
1986	hde->hpp.sort = __sort__hde_cmp;
1987	hde->hpp.equal = __sort__hde_equal;
1988	hde->hpp.free = hde_free;
1989
1990	INIT_LIST_HEAD(&hde->hpp.list);
1991	INIT_LIST_HEAD(&hde->hpp.sort_list);
1992	hde->hpp.elide = false;
1993	hde->hpp.len = 0;
1994	hde->hpp.user_len = 0;
1995	hde->hpp.level = level;
1996
1997	return hde;
1998}
1999
2000struct perf_hpp_fmt *perf_hpp_fmt__dup(struct perf_hpp_fmt *fmt)
2001{
2002	struct perf_hpp_fmt *new_fmt = NULL;
2003
2004	if (perf_hpp__is_sort_entry(fmt)) {
2005		struct hpp_sort_entry *hse, *new_hse;
2006
2007		hse = container_of(fmt, struct hpp_sort_entry, hpp);
2008		new_hse = memdup(hse, sizeof(*hse));
2009		if (new_hse)
2010			new_fmt = &new_hse->hpp;
2011	} else if (perf_hpp__is_dynamic_entry(fmt)) {
2012		struct hpp_dynamic_entry *hde, *new_hde;
2013
2014		hde = container_of(fmt, struct hpp_dynamic_entry, hpp);
2015		new_hde = memdup(hde, sizeof(*hde));
2016		if (new_hde)
2017			new_fmt = &new_hde->hpp;
2018	} else {
2019		new_fmt = memdup(fmt, sizeof(*fmt));
2020	}
2021
2022	INIT_LIST_HEAD(&new_fmt->list);
2023	INIT_LIST_HEAD(&new_fmt->sort_list);
2024
2025	return new_fmt;
2026}
2027
2028static int parse_field_name(char *str, char **event, char **field, char **opt)
2029{
2030	char *event_name, *field_name, *opt_name;
2031
2032	event_name = str;
2033	field_name = strchr(str, '.');
2034
2035	if (field_name) {
2036		*field_name++ = '\0';
2037	} else {
2038		event_name = NULL;
2039		field_name = str;
2040	}
2041
2042	opt_name = strchr(field_name, '/');
2043	if (opt_name)
2044		*opt_name++ = '\0';
2045
2046	*event = event_name;
2047	*field = field_name;
2048	*opt   = opt_name;
2049
2050	return 0;
2051}
2052
2053/* find match evsel using a given event name.  The event name can be:
2054 *   1. '%' + event index (e.g. '%1' for first event)
2055 *   2. full event name (e.g. sched:sched_switch)
2056 *   3. partial event name (should not contain ':')
2057 */
2058static struct perf_evsel *find_evsel(struct perf_evlist *evlist, char *event_name)
2059{
2060	struct perf_evsel *evsel = NULL;
2061	struct perf_evsel *pos;
2062	bool full_name;
2063
2064	/* case 1 */
2065	if (event_name[0] == '%') {
2066		int nr = strtol(event_name+1, NULL, 0);
2067
2068		if (nr > evlist->nr_entries)
2069			return NULL;
2070
2071		evsel = perf_evlist__first(evlist);
2072		while (--nr > 0)
2073			evsel = perf_evsel__next(evsel);
2074
2075		return evsel;
2076	}
2077
2078	full_name = !!strchr(event_name, ':');
2079	evlist__for_each_entry(evlist, pos) {
2080		/* case 2 */
2081		if (full_name && !strcmp(pos->name, event_name))
2082			return pos;
2083		/* case 3 */
2084		if (!full_name && strstr(pos->name, event_name)) {
2085			if (evsel) {
2086				pr_debug("'%s' event is ambiguous: it can be %s or %s\n",
2087					 event_name, evsel->name, pos->name);
2088				return NULL;
2089			}
2090			evsel = pos;
2091		}
2092	}
2093
2094	return evsel;
2095}
2096
2097static int __dynamic_dimension__add(struct perf_evsel *evsel,
2098				    struct format_field *field,
2099				    bool raw_trace, int level)
2100{
2101	struct hpp_dynamic_entry *hde;
2102
2103	hde = __alloc_dynamic_entry(evsel, field, level);
2104	if (hde == NULL)
2105		return -ENOMEM;
2106
2107	hde->raw_trace = raw_trace;
2108
2109	perf_hpp__register_sort_field(&hde->hpp);
2110	return 0;
2111}
2112
2113static int add_evsel_fields(struct perf_evsel *evsel, bool raw_trace, int level)
2114{
2115	int ret;
2116	struct format_field *field;
2117
2118	field = evsel->tp_format->format.fields;
2119	while (field) {
2120		ret = __dynamic_dimension__add(evsel, field, raw_trace, level);
2121		if (ret < 0)
2122			return ret;
2123
2124		field = field->next;
2125	}
2126	return 0;
2127}
2128
2129static int add_all_dynamic_fields(struct perf_evlist *evlist, bool raw_trace,
2130				  int level)
2131{
2132	int ret;
2133	struct perf_evsel *evsel;
2134
2135	evlist__for_each_entry(evlist, evsel) {
2136		if (evsel->attr.type != PERF_TYPE_TRACEPOINT)
2137			continue;
2138
2139		ret = add_evsel_fields(evsel, raw_trace, level);
2140		if (ret < 0)
2141			return ret;
2142	}
2143	return 0;
2144}
2145
2146static int add_all_matching_fields(struct perf_evlist *evlist,
2147				   char *field_name, bool raw_trace, int level)
2148{
2149	int ret = -ESRCH;
2150	struct perf_evsel *evsel;
2151	struct format_field *field;
2152
2153	evlist__for_each_entry(evlist, evsel) {
2154		if (evsel->attr.type != PERF_TYPE_TRACEPOINT)
2155			continue;
2156
2157		field = pevent_find_any_field(evsel->tp_format, field_name);
2158		if (field == NULL)
2159			continue;
2160
2161		ret = __dynamic_dimension__add(evsel, field, raw_trace, level);
2162		if (ret < 0)
2163			break;
2164	}
2165	return ret;
2166}
2167
2168static int add_dynamic_entry(struct perf_evlist *evlist, const char *tok,
2169			     int level)
2170{
2171	char *str, *event_name, *field_name, *opt_name;
2172	struct perf_evsel *evsel;
2173	struct format_field *field;
2174	bool raw_trace = symbol_conf.raw_trace;
2175	int ret = 0;
2176
2177	if (evlist == NULL)
2178		return -ENOENT;
2179
2180	str = strdup(tok);
2181	if (str == NULL)
2182		return -ENOMEM;
2183
2184	if (parse_field_name(str, &event_name, &field_name, &opt_name) < 0) {
2185		ret = -EINVAL;
2186		goto out;
2187	}
2188
2189	if (opt_name) {
2190		if (strcmp(opt_name, "raw")) {
2191			pr_debug("unsupported field option %s\n", opt_name);
2192			ret = -EINVAL;
2193			goto out;
2194		}
2195		raw_trace = true;
2196	}
2197
2198	if (!strcmp(field_name, "trace_fields")) {
2199		ret = add_all_dynamic_fields(evlist, raw_trace, level);
2200		goto out;
2201	}
2202
2203	if (event_name == NULL) {
2204		ret = add_all_matching_fields(evlist, field_name, raw_trace, level);
2205		goto out;
2206	}
2207
2208	evsel = find_evsel(evlist, event_name);
2209	if (evsel == NULL) {
2210		pr_debug("Cannot find event: %s\n", event_name);
2211		ret = -ENOENT;
2212		goto out;
2213	}
2214
2215	if (evsel->attr.type != PERF_TYPE_TRACEPOINT) {
2216		pr_debug("%s is not a tracepoint event\n", event_name);
2217		ret = -EINVAL;
2218		goto out;
2219	}
2220
2221	if (!strcmp(field_name, "*")) {
2222		ret = add_evsel_fields(evsel, raw_trace, level);
2223	} else {
2224		field = pevent_find_any_field(evsel->tp_format, field_name);
2225		if (field == NULL) {
2226			pr_debug("Cannot find event field for %s.%s\n",
2227				 event_name, field_name);
2228			return -ENOENT;
2229		}
2230
2231		ret = __dynamic_dimension__add(evsel, field, raw_trace, level);
2232	}
2233
2234out:
2235	free(str);
2236	return ret;
2237}
2238
2239static int __sort_dimension__add(struct sort_dimension *sd,
2240				 struct perf_hpp_list *list,
2241				 int level)
2242{
2243	if (sd->taken)
2244		return 0;
2245
2246	if (__sort_dimension__add_hpp_sort(sd, list, level) < 0)
2247		return -1;
2248
2249	if (sd->entry->se_collapse)
2250		list->need_collapse = 1;
2251
2252	sd->taken = 1;
2253
2254	return 0;
2255}
2256
2257static int __hpp_dimension__add(struct hpp_dimension *hd,
2258				struct perf_hpp_list *list,
2259				int level)
2260{
2261	struct perf_hpp_fmt *fmt;
2262
2263	if (hd->taken)
2264		return 0;
2265
2266	fmt = __hpp_dimension__alloc_hpp(hd, level);
2267	if (!fmt)
2268		return -1;
2269
2270	hd->taken = 1;
2271	perf_hpp_list__register_sort_field(list, fmt);
2272	return 0;
2273}
2274
2275static int __sort_dimension__add_output(struct perf_hpp_list *list,
2276					struct sort_dimension *sd)
2277{
2278	if (sd->taken)
2279		return 0;
2280
2281	if (__sort_dimension__add_hpp_output(sd, list) < 0)
2282		return -1;
2283
 
2284	sd->taken = 1;
2285	return 0;
2286}
2287
2288static int __hpp_dimension__add_output(struct perf_hpp_list *list,
2289				       struct hpp_dimension *hd)
2290{
2291	struct perf_hpp_fmt *fmt;
2292
2293	if (hd->taken)
2294		return 0;
2295
2296	fmt = __hpp_dimension__alloc_hpp(hd, 0);
2297	if (!fmt)
2298		return -1;
2299
2300	hd->taken = 1;
2301	perf_hpp_list__column_register(list, fmt);
2302	return 0;
2303}
2304
2305int hpp_dimension__add_output(unsigned col)
2306{
2307	BUG_ON(col >= PERF_HPP__MAX_INDEX);
2308	return __hpp_dimension__add_output(&perf_hpp_list, &hpp_sort_dimensions[col]);
2309}
2310
2311int sort_dimension__add(struct perf_hpp_list *list, const char *tok,
2312			struct perf_evlist *evlist,
2313			int level)
2314{
2315	unsigned int i;
2316
2317	for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) {
2318		struct sort_dimension *sd = &common_sort_dimensions[i];
2319
2320		if (strncasecmp(tok, sd->name, strlen(tok)))
2321			continue;
2322
2323		if (sd->entry == &sort_parent) {
2324			int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED);
2325			if (ret) {
2326				char err[BUFSIZ];
2327
2328				regerror(ret, &parent_regex, err, sizeof(err));
2329				pr_err("Invalid regex: %s\n%s", parent_pattern, err);
2330				return -EINVAL;
2331			}
2332			list->parent = 1;
2333		} else if (sd->entry == &sort_sym) {
2334			list->sym = 1;
2335			/*
2336			 * perf diff displays the performance difference amongst
2337			 * two or more perf.data files. Those files could come
2338			 * from different binaries. So we should not compare
2339			 * their ips, but the name of symbol.
2340			 */
2341			if (sort__mode == SORT_MODE__DIFF)
2342				sd->entry->se_collapse = sort__sym_sort;
2343
2344		} else if (sd->entry == &sort_dso) {
2345			list->dso = 1;
2346		} else if (sd->entry == &sort_socket) {
2347			list->socket = 1;
2348		} else if (sd->entry == &sort_thread) {
2349			list->thread = 1;
2350		} else if (sd->entry == &sort_comm) {
2351			list->comm = 1;
2352		}
2353
2354		return __sort_dimension__add(sd, list, level);
2355	}
2356
2357	for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) {
2358		struct hpp_dimension *hd = &hpp_sort_dimensions[i];
2359
2360		if (strncasecmp(tok, hd->name, strlen(tok)))
2361			continue;
2362
2363		return __hpp_dimension__add(hd, list, level);
2364	}
2365
2366	for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) {
2367		struct sort_dimension *sd = &bstack_sort_dimensions[i];
2368
2369		if (strncasecmp(tok, sd->name, strlen(tok)))
2370			continue;
2371
2372		if (sort__mode != SORT_MODE__BRANCH)
2373			return -EINVAL;
2374
2375		if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to)
2376			list->sym = 1;
2377
2378		__sort_dimension__add(sd, list, level);
2379		return 0;
2380	}
2381
2382	for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) {
2383		struct sort_dimension *sd = &memory_sort_dimensions[i];
2384
2385		if (strncasecmp(tok, sd->name, strlen(tok)))
2386			continue;
2387
2388		if (sort__mode != SORT_MODE__MEMORY)
2389			return -EINVAL;
2390
2391		if (sd->entry == &sort_mem_dcacheline && cacheline_size == 0)
2392			return -EINVAL;
2393
2394		if (sd->entry == &sort_mem_daddr_sym)
2395			list->sym = 1;
2396
2397		__sort_dimension__add(sd, list, level);
2398		return 0;
2399	}
2400
2401	if (!add_dynamic_entry(evlist, tok, level))
2402		return 0;
2403
2404	return -ESRCH;
2405}
2406
2407static int setup_sort_list(struct perf_hpp_list *list, char *str,
2408			   struct perf_evlist *evlist)
2409{
2410	char *tmp, *tok;
2411	int ret = 0;
2412	int level = 0;
2413	int next_level = 1;
2414	bool in_group = false;
2415
2416	do {
2417		tok = str;
2418		tmp = strpbrk(str, "{}, ");
2419		if (tmp) {
2420			if (in_group)
2421				next_level = level;
2422			else
2423				next_level = level + 1;
2424
2425			if (*tmp == '{')
2426				in_group = true;
2427			else if (*tmp == '}')
2428				in_group = false;
2429
2430			*tmp = '\0';
2431			str = tmp + 1;
2432		}
2433
2434		if (*tok) {
2435			ret = sort_dimension__add(list, tok, evlist, level);
2436			if (ret == -EINVAL) {
2437				if (!cacheline_size && !strncasecmp(tok, "dcacheline", strlen(tok)))
2438					error("The \"dcacheline\" --sort key needs to know the cacheline size and it couldn't be determined on this system");
2439				else
2440					error("Invalid --sort key: `%s'", tok);
2441				break;
2442			} else if (ret == -ESRCH) {
2443				error("Unknown --sort key: `%s'", tok);
2444				break;
2445			}
2446		}
2447
2448		level = next_level;
2449	} while (tmp);
2450
2451	return ret;
2452}
2453
2454static const char *get_default_sort_order(struct perf_evlist *evlist)
2455{
2456	const char *default_sort_orders[] = {
2457		default_sort_order,
2458		default_branch_sort_order,
2459		default_mem_sort_order,
2460		default_top_sort_order,
2461		default_diff_sort_order,
2462		default_tracepoint_sort_order,
2463	};
2464	bool use_trace = true;
2465	struct perf_evsel *evsel;
2466
2467	BUG_ON(sort__mode >= ARRAY_SIZE(default_sort_orders));
2468
2469	if (evlist == NULL)
2470		goto out_no_evlist;
2471
2472	evlist__for_each_entry(evlist, evsel) {
2473		if (evsel->attr.type != PERF_TYPE_TRACEPOINT) {
2474			use_trace = false;
2475			break;
2476		}
2477	}
2478
2479	if (use_trace) {
2480		sort__mode = SORT_MODE__TRACEPOINT;
2481		if (symbol_conf.raw_trace)
2482			return "trace_fields";
2483	}
2484out_no_evlist:
2485	return default_sort_orders[sort__mode];
2486}
2487
2488static int setup_sort_order(struct perf_evlist *evlist)
2489{
2490	char *new_sort_order;
2491
2492	/*
2493	 * Append '+'-prefixed sort order to the default sort
2494	 * order string.
2495	 */
2496	if (!sort_order || is_strict_order(sort_order))
2497		return 0;
2498
2499	if (sort_order[1] == '\0') {
2500		error("Invalid --sort key: `+'");
2501		return -EINVAL;
2502	}
2503
2504	/*
2505	 * We allocate new sort_order string, but we never free it,
2506	 * because it's checked over the rest of the code.
2507	 */
2508	if (asprintf(&new_sort_order, "%s,%s",
2509		     get_default_sort_order(evlist), sort_order + 1) < 0) {
2510		error("Not enough memory to set up --sort");
2511		return -ENOMEM;
2512	}
2513
2514	sort_order = new_sort_order;
2515	return 0;
2516}
2517
2518/*
2519 * Adds 'pre,' prefix into 'str' is 'pre' is
2520 * not already part of 'str'.
2521 */
2522static char *prefix_if_not_in(const char *pre, char *str)
2523{
2524	char *n;
2525
2526	if (!str || strstr(str, pre))
2527		return str;
2528
2529	if (asprintf(&n, "%s,%s", pre, str) < 0)
2530		return NULL;
2531
2532	free(str);
2533	return n;
2534}
2535
2536static char *setup_overhead(char *keys)
2537{
2538	if (sort__mode == SORT_MODE__DIFF)
2539		return keys;
2540
2541	keys = prefix_if_not_in("overhead", keys);
2542
2543	if (symbol_conf.cumulate_callchain)
2544		keys = prefix_if_not_in("overhead_children", keys);
2545
2546	return keys;
2547}
2548
2549static int __setup_sorting(struct perf_evlist *evlist)
2550{
2551	char *str;
2552	const char *sort_keys;
2553	int ret = 0;
2554
2555	ret = setup_sort_order(evlist);
2556	if (ret)
2557		return ret;
2558
2559	sort_keys = sort_order;
2560	if (sort_keys == NULL) {
2561		if (is_strict_order(field_order)) {
2562			/*
2563			 * If user specified field order but no sort order,
2564			 * we'll honor it and not add default sort orders.
2565			 */
2566			return 0;
2567		}
2568
2569		sort_keys = get_default_sort_order(evlist);
2570	}
2571
2572	str = strdup(sort_keys);
2573	if (str == NULL) {
2574		error("Not enough memory to setup sort keys");
2575		return -ENOMEM;
2576	}
2577
2578	/*
2579	 * Prepend overhead fields for backward compatibility.
2580	 */
2581	if (!is_strict_order(field_order)) {
2582		str = setup_overhead(str);
2583		if (str == NULL) {
2584			error("Not enough memory to setup overhead keys");
2585			return -ENOMEM;
 
2586		}
2587	}
2588
2589	ret = setup_sort_list(&perf_hpp_list, str, evlist);
2590
2591	free(str);
2592	return ret;
2593}
2594
2595void perf_hpp__set_elide(int idx, bool elide)
2596{
2597	struct perf_hpp_fmt *fmt;
2598	struct hpp_sort_entry *hse;
2599
2600	perf_hpp_list__for_each_format(&perf_hpp_list, fmt) {
2601		if (!perf_hpp__is_sort_entry(fmt))
2602			continue;
2603
2604		hse = container_of(fmt, struct hpp_sort_entry, hpp);
2605		if (hse->se->se_width_idx == idx) {
2606			fmt->elide = elide;
2607			break;
2608		}
2609	}
2610}
2611
2612static bool __get_elide(struct strlist *list, const char *list_name, FILE *fp)
2613{
2614	if (list && strlist__nr_entries(list) == 1) {
2615		if (fp != NULL)
2616			fprintf(fp, "# %s: %s\n", list_name,
2617				strlist__entry(list, 0)->s);
2618		return true;
2619	}
2620	return false;
2621}
2622
2623static bool get_elide(int idx, FILE *output)
2624{
2625	switch (idx) {
2626	case HISTC_SYMBOL:
2627		return __get_elide(symbol_conf.sym_list, "symbol", output);
2628	case HISTC_DSO:
2629		return __get_elide(symbol_conf.dso_list, "dso", output);
2630	case HISTC_COMM:
2631		return __get_elide(symbol_conf.comm_list, "comm", output);
2632	default:
2633		break;
2634	}
2635
2636	if (sort__mode != SORT_MODE__BRANCH)
2637		return false;
2638
2639	switch (idx) {
2640	case HISTC_SYMBOL_FROM:
2641		return __get_elide(symbol_conf.sym_from_list, "sym_from", output);
2642	case HISTC_SYMBOL_TO:
2643		return __get_elide(symbol_conf.sym_to_list, "sym_to", output);
2644	case HISTC_DSO_FROM:
2645		return __get_elide(symbol_conf.dso_from_list, "dso_from", output);
2646	case HISTC_DSO_TO:
2647		return __get_elide(symbol_conf.dso_to_list, "dso_to", output);
2648	default:
2649		break;
2650	}
2651
2652	return false;
2653}
2654
2655void sort__setup_elide(FILE *output)
2656{
2657	struct perf_hpp_fmt *fmt;
2658	struct hpp_sort_entry *hse;
2659
2660	perf_hpp_list__for_each_format(&perf_hpp_list, fmt) {
2661		if (!perf_hpp__is_sort_entry(fmt))
2662			continue;
2663
2664		hse = container_of(fmt, struct hpp_sort_entry, hpp);
2665		fmt->elide = get_elide(hse->se->se_width_idx, output);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2666	}
2667
2668	/*
2669	 * It makes no sense to elide all of sort entries.
2670	 * Just revert them to show up again.
2671	 */
2672	perf_hpp_list__for_each_format(&perf_hpp_list, fmt) {
2673		if (!perf_hpp__is_sort_entry(fmt))
2674			continue;
2675
2676		if (!fmt->elide)
2677			return;
2678	}
2679
2680	perf_hpp_list__for_each_format(&perf_hpp_list, fmt) {
2681		if (!perf_hpp__is_sort_entry(fmt))
2682			continue;
2683
2684		fmt->elide = false;
2685	}
2686}
2687
2688int output_field_add(struct perf_hpp_list *list, char *tok)
2689{
2690	unsigned int i;
2691
2692	for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) {
2693		struct sort_dimension *sd = &common_sort_dimensions[i];
2694
2695		if (strncasecmp(tok, sd->name, strlen(tok)))
2696			continue;
2697
2698		return __sort_dimension__add_output(list, sd);
2699	}
2700
2701	for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) {
2702		struct hpp_dimension *hd = &hpp_sort_dimensions[i];
2703
2704		if (strncasecmp(tok, hd->name, strlen(tok)))
2705			continue;
2706
2707		return __hpp_dimension__add_output(list, hd);
2708	}
2709
2710	for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) {
2711		struct sort_dimension *sd = &bstack_sort_dimensions[i];
2712
2713		if (strncasecmp(tok, sd->name, strlen(tok)))
2714			continue;
2715
2716		return __sort_dimension__add_output(list, sd);
2717	}
2718
2719	for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) {
2720		struct sort_dimension *sd = &memory_sort_dimensions[i];
2721
2722		if (strncasecmp(tok, sd->name, strlen(tok)))
2723			continue;
2724
2725		return __sort_dimension__add_output(list, sd);
2726	}
2727
2728	return -ESRCH;
2729}
2730
2731static int setup_output_list(struct perf_hpp_list *list, char *str)
2732{
2733	char *tmp, *tok;
2734	int ret = 0;
2735
2736	for (tok = strtok_r(str, ", ", &tmp);
2737			tok; tok = strtok_r(NULL, ", ", &tmp)) {
2738		ret = output_field_add(list, tok);
2739		if (ret == -EINVAL) {
2740			error("Invalid --fields key: `%s'", tok);
2741			break;
2742		} else if (ret == -ESRCH) {
2743			error("Unknown --fields key: `%s'", tok);
2744			break;
2745		}
2746	}
2747
2748	return ret;
2749}
2750
2751void reset_dimensions(void)
2752{
2753	unsigned int i;
2754
2755	for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++)
2756		common_sort_dimensions[i].taken = 0;
2757
2758	for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++)
2759		hpp_sort_dimensions[i].taken = 0;
2760
2761	for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++)
2762		bstack_sort_dimensions[i].taken = 0;
2763
2764	for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++)
2765		memory_sort_dimensions[i].taken = 0;
2766}
2767
2768bool is_strict_order(const char *order)
2769{
2770	return order && (*order != '+');
2771}
2772
2773static int __setup_output_field(void)
2774{
2775	char *str, *strp;
2776	int ret = -EINVAL;
2777
2778	if (field_order == NULL)
2779		return 0;
2780
2781	strp = str = strdup(field_order);
2782	if (str == NULL) {
2783		error("Not enough memory to setup output fields");
2784		return -ENOMEM;
2785	}
2786
2787	if (!is_strict_order(field_order))
2788		strp++;
2789
2790	if (!strlen(strp)) {
2791		error("Invalid --fields key: `+'");
2792		goto out;
2793	}
2794
2795	ret = setup_output_list(&perf_hpp_list, strp);
2796
2797out:
2798	free(str);
2799	return ret;
2800}
2801
2802int setup_sorting(struct perf_evlist *evlist)
2803{
2804	int err;
2805
2806	err = __setup_sorting(evlist);
2807	if (err < 0)
2808		return err;
2809
2810	if (parent_pattern != default_parent_pattern) {
2811		err = sort_dimension__add(&perf_hpp_list, "parent", evlist, -1);
2812		if (err < 0)
2813			return err;
2814	}
2815
2816	reset_dimensions();
2817
2818	/*
2819	 * perf diff doesn't use default hpp output fields.
2820	 */
2821	if (sort__mode != SORT_MODE__DIFF)
2822		perf_hpp__init();
2823
2824	err = __setup_output_field();
2825	if (err < 0)
2826		return err;
2827
2828	/* copy sort keys to output fields */
2829	perf_hpp__setup_output_field(&perf_hpp_list);
2830	/* and then copy output fields to sort keys */
2831	perf_hpp__append_sort_keys(&perf_hpp_list);
2832
2833	/* setup hists-specific output fields */
2834	if (perf_hpp__setup_hists_formats(&perf_hpp_list, evlist) < 0)
2835		return -1;
2836
2837	return 0;
2838}
2839
2840void reset_output_field(void)
2841{
2842	perf_hpp_list.need_collapse = 0;
2843	perf_hpp_list.parent = 0;
2844	perf_hpp_list.sym = 0;
2845	perf_hpp_list.dso = 0;
2846
2847	field_order = NULL;
2848	sort_order = NULL;
2849
2850	reset_dimensions();
2851	perf_hpp__reset_output_field(&perf_hpp_list);
2852}
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}