Linux Audio

Check our new training course

Yocto / OpenEmbedded training

Mar 24-27, 2025, special US time zones
Register
Loading...
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}
v3.5.6
  1#include "sort.h"
  2#include "hist.h"
 
 
  3
  4regex_t		parent_regex;
  5const char	default_parent_pattern[] = "^sys_|^do_page_fault";
  6const char	*parent_pattern = default_parent_pattern;
  7const char	default_sort_order[] = "comm,dso,symbol";
  8const char	*sort_order = default_sort_order;
 
 
  9int		sort__need_collapse = 0;
 10int		sort__has_parent = 0;
 11int		sort__branch_mode = -1; /* -1 = means not set */
 
 
 12
 13enum sort_type	sort__first_dimension;
 14
 15char * field_sep;
 16
 17LIST_HEAD(hist_entry__sort_list);
 18
 19static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...)
 20{
 21	int n;
 22	va_list ap;
 23
 24	va_start(ap, fmt);
 25	n = vsnprintf(bf, size, fmt, ap);
 26	if (field_sep && n > 0) {
 27		char *sep = bf;
 28
 29		while (1) {
 30			sep = strchr(sep, *field_sep);
 31			if (sep == NULL)
 32				break;
 33			*sep = '.';
 34		}
 35	}
 36	va_end(ap);
 37
 38	if (n >= (int)size)
 39		return size - 1;
 40	return n;
 41}
 42
 43static int64_t cmp_null(void *l, void *r)
 44{
 45	if (!l && !r)
 46		return 0;
 47	else if (!l)
 48		return -1;
 49	else
 50		return 1;
 51}
 52
 53/* --sort pid */
 54
 55static int64_t
 56sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
 57{
 58	return right->thread->pid - left->thread->pid;
 59}
 60
 61static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf,
 62				       size_t size, unsigned int width)
 63{
 64	return repsep_snprintf(bf, size, "%*s:%5d", width,
 65			      self->thread->comm ?: "", self->thread->pid);
 
 66}
 67
 68struct sort_entry sort_thread = {
 69	.se_header	= "Command:  Pid",
 70	.se_cmp		= sort__thread_cmp,
 71	.se_snprintf	= hist_entry__thread_snprintf,
 72	.se_width_idx	= HISTC_THREAD,
 73};
 74
 75/* --sort comm */
 76
 77static int64_t
 78sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
 79{
 80	return right->thread->pid - left->thread->pid;
 
 81}
 82
 83static int64_t
 84sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
 85{
 86	char *comm_l = left->thread->comm;
 87	char *comm_r = right->thread->comm;
 88
 89	if (!comm_l || !comm_r)
 90		return cmp_null(comm_l, comm_r);
 91
 92	return strcmp(comm_l, comm_r);
 93}
 94
 95static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf,
 96				     size_t size, unsigned int width)
 97{
 98	return repsep_snprintf(bf, size, "%*s", width, self->thread->comm);
 99}
100
 
 
 
 
 
 
 
 
 
 
101static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r)
102{
103	struct dso *dso_l = map_l ? map_l->dso : NULL;
104	struct dso *dso_r = map_r ? map_r->dso : NULL;
105	const char *dso_name_l, *dso_name_r;
106
107	if (!dso_l || !dso_r)
108		return cmp_null(dso_l, dso_r);
109
110	if (verbose) {
111		dso_name_l = dso_l->long_name;
112		dso_name_r = dso_r->long_name;
113	} else {
114		dso_name_l = dso_l->short_name;
115		dso_name_r = dso_r->short_name;
116	}
117
118	return strcmp(dso_name_l, dso_name_r);
119}
120
121struct sort_entry sort_comm = {
122	.se_header	= "Command",
123	.se_cmp		= sort__comm_cmp,
124	.se_collapse	= sort__comm_collapse,
125	.se_snprintf	= hist_entry__comm_snprintf,
126	.se_width_idx	= HISTC_COMM,
127};
128
129/* --sort dso */
130
131static int64_t
132sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
133{
134	return _sort__dso_cmp(left->ms.map, right->ms.map);
135}
136
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
138static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r,
139			      u64 ip_l, u64 ip_r)
140{
 
 
141	if (!sym_l || !sym_r)
142		return cmp_null(sym_l, sym_r);
143
144	if (sym_l == sym_r)
145		return 0;
146
147	if (sym_l)
148		ip_l = sym_l->start;
149	if (sym_r)
150		ip_r = sym_r->start;
151
152	return (int64_t)(ip_r - ip_l);
153}
154
155static int _hist_entry__dso_snprintf(struct map *map, char *bf,
156				     size_t size, unsigned int width)
157{
158	if (map && map->dso) {
159		const char *dso_name = !verbose ? map->dso->short_name :
160			map->dso->long_name;
161		return repsep_snprintf(bf, size, "%-*s", width, dso_name);
 
 
 
 
 
 
 
 
 
162	}
163
164	return repsep_snprintf(bf, size, "%-*s", width, "[unknown]");
165}
166
167static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf,
168				    size_t size, unsigned int width)
169{
170	return _hist_entry__dso_snprintf(self->ms.map, bf, size, width);
171}
172
173static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym,
174				     u64 ip, char level, char *bf, size_t size,
175				     unsigned int width __used)
176{
177	size_t ret = 0;
178
179	if (verbose) {
180		char o = map ? dso__symtab_origin(map->dso) : '!';
181		ret += repsep_snprintf(bf, size, "%-#*llx %c ",
182				       BITS_PER_LONG / 4, ip, o);
183	}
184
185	ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level);
186	if (sym)
187		ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
188				       width - ret,
189				       sym->name);
190	else {
 
 
 
 
 
 
 
 
191		size_t len = BITS_PER_LONG / 4;
192		ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx",
193				       len, ip);
194		ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
195				       width - ret, "");
196	}
197
198	return ret;
199}
200
 
 
 
 
 
 
201
202struct sort_entry sort_dso = {
203	.se_header	= "Shared Object",
204	.se_cmp		= sort__dso_cmp,
205	.se_snprintf	= hist_entry__dso_snprintf,
206	.se_width_idx	= HISTC_DSO,
207};
208
209static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
210				    size_t size, unsigned int width __used)
 
 
211{
212	return _hist_entry__sym_snprintf(self->ms.map, self->ms.sym, self->ip,
213					 self->level, bf, size, width);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
214}
215
216/* --sort symbol */
217static int64_t
218sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
219{
220	u64 ip_l, ip_r;
221
222	if (!left->ms.sym && !right->ms.sym)
223		return right->level - left->level;
224
225	if (!left->ms.sym || !right->ms.sym)
226		return cmp_null(left->ms.sym, right->ms.sym);
227
228	if (left->ms.sym == right->ms.sym)
229		return 0;
230
231	ip_l = left->ms.sym->start;
232	ip_r = right->ms.sym->start;
233
234	return _sort__sym_cmp(left->ms.sym, right->ms.sym, ip_l, ip_r);
235}
236
237struct sort_entry sort_sym = {
238	.se_header	= "Symbol",
239	.se_cmp		= sort__sym_cmp,
240	.se_snprintf	= hist_entry__sym_snprintf,
241	.se_width_idx	= HISTC_SYMBOL,
242};
243
244/* --sort parent */
245
246static int64_t
247sort__parent_cmp(struct hist_entry *left, struct hist_entry *right)
248{
249	struct symbol *sym_l = left->parent;
250	struct symbol *sym_r = right->parent;
251
252	if (!sym_l || !sym_r)
253		return cmp_null(sym_l, sym_r);
254
255	return strcmp(sym_l->name, sym_r->name);
256}
257
258static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf,
259				       size_t size, unsigned int width)
260{
261	return repsep_snprintf(bf, size, "%-*s", width,
262			      self->parent ? self->parent->name : "[other]");
263}
264
265struct sort_entry sort_parent = {
266	.se_header	= "Parent symbol",
267	.se_cmp		= sort__parent_cmp,
268	.se_snprintf	= hist_entry__parent_snprintf,
269	.se_width_idx	= HISTC_PARENT,
270};
271
272/* --sort cpu */
273
274static int64_t
275sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right)
276{
277	return right->cpu - left->cpu;
278}
279
280static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf,
281				       size_t size, unsigned int width)
282{
283	return repsep_snprintf(bf, size, "%-*d", width, self->cpu);
284}
285
286struct sort_entry sort_cpu = {
287	.se_header      = "CPU",
288	.se_cmp	        = sort__cpu_cmp,
289	.se_snprintf    = hist_entry__cpu_snprintf,
290	.se_width_idx	= HISTC_CPU,
291};
292
 
 
293static int64_t
294sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right)
295{
296	return _sort__dso_cmp(left->branch_info->from.map,
297			      right->branch_info->from.map);
298}
299
300static int hist_entry__dso_from_snprintf(struct hist_entry *self, char *bf,
301				    size_t size, unsigned int width)
302{
303	return _hist_entry__dso_snprintf(self->branch_info->from.map,
304					 bf, size, width);
305}
306
307struct sort_entry sort_dso_from = {
308	.se_header	= "Source Shared Object",
309	.se_cmp		= sort__dso_from_cmp,
310	.se_snprintf	= hist_entry__dso_from_snprintf,
311	.se_width_idx	= HISTC_DSO_FROM,
312};
313
314static int64_t
315sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right)
316{
317	return _sort__dso_cmp(left->branch_info->to.map,
318			      right->branch_info->to.map);
319}
320
321static int hist_entry__dso_to_snprintf(struct hist_entry *self, char *bf,
322				       size_t size, unsigned int width)
323{
324	return _hist_entry__dso_snprintf(self->branch_info->to.map,
325					 bf, size, width);
326}
327
328static int64_t
329sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right)
330{
331	struct addr_map_symbol *from_l = &left->branch_info->from;
332	struct addr_map_symbol *from_r = &right->branch_info->from;
333
334	if (!from_l->sym && !from_r->sym)
335		return right->level - left->level;
336
337	return _sort__sym_cmp(from_l->sym, from_r->sym, from_l->addr,
338			     from_r->addr);
339}
340
341static int64_t
342sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right)
343{
344	struct addr_map_symbol *to_l = &left->branch_info->to;
345	struct addr_map_symbol *to_r = &right->branch_info->to;
346
347	if (!to_l->sym && !to_r->sym)
348		return right->level - left->level;
349
350	return _sort__sym_cmp(to_l->sym, to_r->sym, to_l->addr, to_r->addr);
351}
352
353static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf,
354				    size_t size, unsigned int width __used)
355{
356	struct addr_map_symbol *from = &self->branch_info->from;
357	return _hist_entry__sym_snprintf(from->map, from->sym, from->addr,
358					 self->level, bf, size, width);
359
360}
361
362static int hist_entry__sym_to_snprintf(struct hist_entry *self, char *bf,
363				    size_t size, unsigned int width __used)
364{
365	struct addr_map_symbol *to = &self->branch_info->to;
366	return _hist_entry__sym_snprintf(to->map, to->sym, to->addr,
367					 self->level, bf, size, width);
368
369}
370
 
 
 
 
 
 
 
371struct sort_entry sort_dso_to = {
372	.se_header	= "Target Shared Object",
373	.se_cmp		= sort__dso_to_cmp,
374	.se_snprintf	= hist_entry__dso_to_snprintf,
375	.se_width_idx	= HISTC_DSO_TO,
376};
377
378struct sort_entry sort_sym_from = {
379	.se_header	= "Source Symbol",
380	.se_cmp		= sort__sym_from_cmp,
381	.se_snprintf	= hist_entry__sym_from_snprintf,
382	.se_width_idx	= HISTC_SYMBOL_FROM,
383};
384
385struct sort_entry sort_sym_to = {
386	.se_header	= "Target Symbol",
387	.se_cmp		= sort__sym_to_cmp,
388	.se_snprintf	= hist_entry__sym_to_snprintf,
389	.se_width_idx	= HISTC_SYMBOL_TO,
390};
391
392static int64_t
393sort__mispredict_cmp(struct hist_entry *left, struct hist_entry *right)
394{
395	const unsigned char mp = left->branch_info->flags.mispred !=
396					right->branch_info->flags.mispred;
397	const unsigned char p = left->branch_info->flags.predicted !=
398					right->branch_info->flags.predicted;
399
400	return mp || p;
401}
402
403static int hist_entry__mispredict_snprintf(struct hist_entry *self, char *bf,
404				    size_t size, unsigned int width){
405	static const char *out = "N/A";
406
407	if (self->branch_info->flags.predicted)
408		out = "N";
409	else if (self->branch_info->flags.mispred)
410		out = "Y";
411
412	return repsep_snprintf(bf, size, "%-*s", width, out);
413}
414
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
415struct sort_entry sort_mispredict = {
416	.se_header	= "Branch Mispredicted",
417	.se_cmp		= sort__mispredict_cmp,
418	.se_snprintf	= hist_entry__mispredict_snprintf,
419	.se_width_idx	= HISTC_MISPREDICT,
420};
421
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
422struct sort_dimension {
423	const char		*name;
424	struct sort_entry	*entry;
425	int			taken;
426};
427
428#define DIM(d, n, func) [d] = { .name = n, .entry = &(func) }
429
430static struct sort_dimension sort_dimensions[] = {
431	DIM(SORT_PID, "pid", sort_thread),
432	DIM(SORT_COMM, "comm", sort_comm),
433	DIM(SORT_DSO, "dso", sort_dso),
434	DIM(SORT_DSO_FROM, "dso_from", sort_dso_from),
435	DIM(SORT_DSO_TO, "dso_to", sort_dso_to),
436	DIM(SORT_SYM, "symbol", sort_sym),
437	DIM(SORT_SYM_TO, "symbol_from", sort_sym_from),
438	DIM(SORT_SYM_FROM, "symbol_to", sort_sym_to),
439	DIM(SORT_PARENT, "parent", sort_parent),
440	DIM(SORT_CPU, "cpu", sort_cpu),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
441	DIM(SORT_MISPREDICT, "mispredict", sort_mispredict),
 
 
442};
443
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
444int sort_dimension__add(const char *tok)
445{
446	unsigned int i;
447
448	for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) {
449		struct sort_dimension *sd = &sort_dimensions[i];
450
451		if (strncasecmp(tok, sd->name, strlen(tok)))
452			continue;
 
453		if (sd->entry == &sort_parent) {
454			int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED);
455			if (ret) {
456				char err[BUFSIZ];
457
458				regerror(ret, &parent_regex, err, sizeof(err));
459				pr_err("Invalid regex: %s\n%s", parent_pattern, err);
460				return -EINVAL;
461			}
462			sort__has_parent = 1;
 
 
 
 
463		}
464
465		if (sd->taken)
466			return 0;
 
 
 
 
467
468		if (sd->entry->se_collapse)
469			sort__need_collapse = 1;
470
471		if (list_empty(&hist_entry__sort_list)) {
472			if (!strcmp(sd->name, "pid"))
473				sort__first_dimension = SORT_PID;
474			else if (!strcmp(sd->name, "comm"))
475				sort__first_dimension = SORT_COMM;
476			else if (!strcmp(sd->name, "dso"))
477				sort__first_dimension = SORT_DSO;
478			else if (!strcmp(sd->name, "symbol"))
479				sort__first_dimension = SORT_SYM;
480			else if (!strcmp(sd->name, "parent"))
481				sort__first_dimension = SORT_PARENT;
482			else if (!strcmp(sd->name, "cpu"))
483				sort__first_dimension = SORT_CPU;
484			else if (!strcmp(sd->name, "symbol_from"))
485				sort__first_dimension = SORT_SYM_FROM;
486			else if (!strcmp(sd->name, "symbol_to"))
487				sort__first_dimension = SORT_SYM_TO;
488			else if (!strcmp(sd->name, "dso_from"))
489				sort__first_dimension = SORT_DSO_FROM;
490			else if (!strcmp(sd->name, "dso_to"))
491				sort__first_dimension = SORT_DSO_TO;
492			else if (!strcmp(sd->name, "mispredict"))
493				sort__first_dimension = SORT_MISPREDICT;
494		}
495
496		list_add_tail(&sd->entry->list, &hist_entry__sort_list);
497		sd->taken = 1;
498
 
499		return 0;
500	}
 
501	return -ESRCH;
502}
503
504void setup_sorting(const char * const usagestr[], const struct option *opts)
505{
506	char *tmp, *tok, *str = strdup(sort_order);
 
 
 
 
 
 
507
508	for (tok = strtok_r(str, ", ", &tmp);
509			tok; tok = strtok_r(NULL, ", ", &tmp)) {
510		if (sort_dimension__add(tok) < 0) {
 
 
 
 
511			error("Unknown --sort key: `%s'", tok);
512			usage_with_options(usagestr, opts);
513		}
514	}
515
516	free(str);
 
517}
518
519void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list,
520			     const char *list_name, FILE *fp)
 
521{
522	if (list && strlist__nr_entries(list) == 1) {
523		if (fp != NULL)
524			fprintf(fp, "# %s: %s\n", list_name,
525				strlist__entry(list, 0)->s);
526		self->elide = true;
527	}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
528}