diff options
Diffstat (limited to 'tools/perf/util/sort.c')
| -rw-r--r-- | tools/perf/util/sort.c | 959 | 
1 files changed, 802 insertions, 157 deletions
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 5f118a08951..1ec57dd8228 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -1,22 +1,28 @@ +#include <sys/mman.h>  #include "sort.h"  #include "hist.h" +#include "comm.h"  #include "symbol.h" +#include "evsel.h"  regex_t		parent_regex;  const char	default_parent_pattern[] = "^sys_|^do_page_fault";  const char	*parent_pattern = default_parent_pattern;  const char	default_sort_order[] = "comm,dso,symbol"; -const char	*sort_order = default_sort_order; +const char	default_branch_sort_order[] = "comm,dso_from,symbol_from,dso_to,symbol_to"; +const char	default_mem_sort_order[] = "local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked"; +const char	default_top_sort_order[] = "dso,symbol"; +const char	default_diff_sort_order[] = "dso,symbol"; +const char	*sort_order; +const char	*field_order;  regex_t		ignore_callees_regex;  int		have_ignore_callees = 0;  int		sort__need_collapse = 0;  int		sort__has_parent = 0;  int		sort__has_sym = 0; +int		sort__has_dso = 0;  enum sort_mode	sort__mode = SORT_MODE__NORMAL; -enum sort_type	sort__first_dimension; - -LIST_HEAD(hist_entry__sort_list);  static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...)  { @@ -42,7 +48,7 @@ static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...)  	return n;  } -static int64_t cmp_null(void *l, void *r) +static int64_t cmp_null(const void *l, const void *r)  {  	if (!l && !r)  		return 0; @@ -60,11 +66,12 @@ sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)  	return right->thread->tid - left->thread->tid;  } -static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__thread_snprintf(struct hist_entry *he, char *bf,  				       size_t size, unsigned int width)  { +	const char *comm = thread__comm_str(he->thread);  	return repsep_snprintf(bf, size, "%*s:%5d", width - 6, -			      self->thread->comm ?: "", self->thread->tid); +			       comm ?: "", he->thread->tid);  }  struct sort_entry sort_thread = { @@ -79,31 +86,34 @@ struct sort_entry sort_thread = {  static int64_t  sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)  { -	return right->thread->tid - left->thread->tid; +	/* Compare the addr that should be unique among comm */ +	return comm__str(right->comm) - comm__str(left->comm);  }  static int64_t  sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)  { -	char *comm_l = left->thread->comm; -	char *comm_r = right->thread->comm; - -	if (!comm_l || !comm_r) -		return cmp_null(comm_l, comm_r); +	/* Compare the addr that should be unique among comm */ +	return comm__str(right->comm) - comm__str(left->comm); +} -	return strcmp(comm_l, comm_r); +static int64_t +sort__comm_sort(struct hist_entry *left, struct hist_entry *right) +{ +	return strcmp(comm__str(right->comm), comm__str(left->comm));  } -static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__comm_snprintf(struct hist_entry *he, char *bf,  				     size_t size, unsigned int width)  { -	return repsep_snprintf(bf, size, "%*s", width, self->thread->comm); +	return repsep_snprintf(bf, size, "%*s", width, comm__str(he->comm));  }  struct sort_entry sort_comm = {  	.se_header	= "Command",  	.se_cmp		= sort__comm_cmp,  	.se_collapse	= sort__comm_collapse, +	.se_sort	= sort__comm_sort,  	.se_snprintf	= hist_entry__comm_snprintf,  	.se_width_idx	= HISTC_COMM,  }; @@ -117,7 +127,7 @@ static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r)  	const char *dso_name_l, *dso_name_r;  	if (!dso_l || !dso_r) -		return cmp_null(dso_l, dso_r); +		return cmp_null(dso_r, dso_l);  	if (verbose) {  		dso_name_l = dso_l->long_name; @@ -133,7 +143,7 @@ static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r)  static int64_t  sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)  { -	return _sort__dso_cmp(left->ms.map, right->ms.map); +	return _sort__dso_cmp(right->ms.map, left->ms.map);  }  static int _hist_entry__dso_snprintf(struct map *map, char *bf, @@ -148,10 +158,10 @@ static int _hist_entry__dso_snprintf(struct map *map, char *bf,  	return repsep_snprintf(bf, size, "%-*s", width, "[unknown]");  } -static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__dso_snprintf(struct hist_entry *he, char *bf,  				    size_t size, unsigned int width)  { -	return _hist_entry__dso_snprintf(self->ms.map, bf, size, width); +	return _hist_entry__dso_snprintf(he->ms.map, bf, size, width);  }  struct sort_entry sort_dso = { @@ -163,6 +173,11 @@ struct sort_entry sort_dso = {  /* --sort symbol */ +static int64_t _sort__addr_cmp(u64 left_ip, u64 right_ip) +{ +	return (int64_t)(right_ip - left_ip); +} +  static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r)  {  	u64 ip_l, ip_r; @@ -182,12 +197,33 @@ static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r)  static int64_t  sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)  { +	int64_t ret; +  	if (!left->ms.sym && !right->ms.sym) -		return right->level - left->level; +		return _sort__addr_cmp(left->ip, right->ip); + +	/* +	 * comparing symbol address alone is not enough since it's a +	 * relative address within a dso. +	 */ +	if (!sort__has_dso) { +		ret = sort__dso_cmp(left, right); +		if (ret != 0) +			return ret; +	}  	return _sort__sym_cmp(left->ms.sym, right->ms.sym);  } +static int64_t +sort__sym_sort(struct hist_entry *left, struct hist_entry *right) +{ +	if (!left->ms.sym || !right->ms.sym) +		return cmp_null(left->ms.sym, right->ms.sym); + +	return strcmp(right->ms.sym->name, left->ms.sym->name); +} +  static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym,  				     u64 ip, char level, char *bf, size_t size,  				     unsigned int width) @@ -224,16 +260,17 @@ static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym,  	return ret;  } -static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__sym_snprintf(struct hist_entry *he, char *bf,  				    size_t size, unsigned int width)  { -	return _hist_entry__sym_snprintf(self->ms.map, self->ms.sym, self->ip, -					 self->level, bf, size, width); +	return _hist_entry__sym_snprintf(he->ms.map, he->ms.sym, he->ip, +					 he->level, bf, size, width);  }  struct sort_entry sort_sym = {  	.se_header	= "Symbol",  	.se_cmp		= sort__sym_cmp, +	.se_sort	= sort__sym_sort,  	.se_snprintf	= hist_entry__sym_snprintf,  	.se_width_idx	= HISTC_SYMBOL,  }; @@ -243,50 +280,32 @@ struct sort_entry sort_sym = {  static int64_t  sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right)  { -	return (int64_t)(right->ip - left->ip); +	if (!left->srcline) { +		if (!left->ms.map) +			left->srcline = SRCLINE_UNKNOWN; +		else { +			struct map *map = left->ms.map; +			left->srcline = get_srcline(map->dso, +					    map__rip_2objdump(map, left->ip)); +		} +	} +	if (!right->srcline) { +		if (!right->ms.map) +			right->srcline = SRCLINE_UNKNOWN; +		else { +			struct map *map = right->ms.map; +			right->srcline = get_srcline(map->dso, +					    map__rip_2objdump(map, right->ip)); +		} +	} +	return strcmp(right->srcline, left->srcline);  } -static int hist_entry__srcline_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf,  					size_t size,  					unsigned int width __maybe_unused)  { -	FILE *fp = NULL; -	char cmd[PATH_MAX + 2], *path = self->srcline, *nl; -	size_t line_len; - -	if (path != NULL) -		goto out_path; - -	if (!self->ms.map) -		goto out_ip; - -	if (!strncmp(self->ms.map->dso->long_name, "/tmp/perf-", 10)) -		goto out_ip; - -	snprintf(cmd, sizeof(cmd), "addr2line -e %s %016" PRIx64, -		 self->ms.map->dso->long_name, self->ip); -	fp = popen(cmd, "r"); -	if (!fp) -		goto out_ip; - -	if (getline(&path, &line_len, fp) < 0 || !line_len) -		goto out_ip; -	self->srcline = strdup(path); -	if (self->srcline == NULL) -		goto out_ip; - -	nl = strchr(self->srcline, '\n'); -	if (nl != NULL) -		*nl = '\0'; -	path = self->srcline; -out_path: -	if (fp) -		pclose(fp); -	return repsep_snprintf(bf, size, "%s", path); -out_ip: -	if (fp) -		pclose(fp); -	return repsep_snprintf(bf, size, "%-#*llx", BITS_PER_LONG / 4, self->ip); +	return repsep_snprintf(bf, size, "%s", he->srcline);  }  struct sort_entry sort_srcline = { @@ -307,14 +326,14 @@ sort__parent_cmp(struct hist_entry *left, struct hist_entry *right)  	if (!sym_l || !sym_r)  		return cmp_null(sym_l, sym_r); -	return strcmp(sym_l->name, sym_r->name); +	return strcmp(sym_r->name, sym_l->name);  } -static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__parent_snprintf(struct hist_entry *he, char *bf,  				       size_t size, unsigned int width)  {  	return repsep_snprintf(bf, size, "%-*s", width, -			      self->parent ? self->parent->name : "[other]"); +			      he->parent ? he->parent->name : "[other]");  }  struct sort_entry sort_parent = { @@ -332,10 +351,10 @@ sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right)  	return right->cpu - left->cpu;  } -static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf, -				       size_t size, unsigned int width) +static int hist_entry__cpu_snprintf(struct hist_entry *he, char *bf, +				    size_t size, unsigned int width)  { -	return repsep_snprintf(bf, size, "%*d", width, self->cpu); +	return repsep_snprintf(bf, size, "%*d", width, he->cpu);  }  struct sort_entry sort_cpu = { @@ -354,10 +373,10 @@ sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right)  			      right->branch_info->from.map);  } -static int hist_entry__dso_from_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__dso_from_snprintf(struct hist_entry *he, char *bf,  				    size_t size, unsigned int width)  { -	return _hist_entry__dso_snprintf(self->branch_info->from.map, +	return _hist_entry__dso_snprintf(he->branch_info->from.map,  					 bf, size, width);  } @@ -368,10 +387,10 @@ sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right)  			      right->branch_info->to.map);  } -static int hist_entry__dso_to_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__dso_to_snprintf(struct hist_entry *he, char *bf,  				       size_t size, unsigned int width)  { -	return _hist_entry__dso_snprintf(self->branch_info->to.map, +	return _hist_entry__dso_snprintf(he->branch_info->to.map,  					 bf, size, width);  } @@ -382,7 +401,7 @@ sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right)  	struct addr_map_symbol *from_r = &right->branch_info->from;  	if (!from_l->sym && !from_r->sym) -		return right->level - left->level; +		return _sort__addr_cmp(from_l->addr, from_r->addr);  	return _sort__sym_cmp(from_l->sym, from_r->sym);  } @@ -394,26 +413,26 @@ sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right)  	struct addr_map_symbol *to_r = &right->branch_info->to;  	if (!to_l->sym && !to_r->sym) -		return right->level - left->level; +		return _sort__addr_cmp(to_l->addr, to_r->addr);  	return _sort__sym_cmp(to_l->sym, to_r->sym);  } -static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__sym_from_snprintf(struct hist_entry *he, char *bf,  					 size_t size, unsigned int width)  { -	struct addr_map_symbol *from = &self->branch_info->from; +	struct addr_map_symbol *from = &he->branch_info->from;  	return _hist_entry__sym_snprintf(from->map, from->sym, from->addr, -					 self->level, bf, size, width); +					 he->level, bf, size, width);  } -static int hist_entry__sym_to_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__sym_to_snprintf(struct hist_entry *he, char *bf,  				       size_t size, unsigned int width)  { -	struct addr_map_symbol *to = &self->branch_info->to; +	struct addr_map_symbol *to = &he->branch_info->to;  	return _hist_entry__sym_snprintf(to->map, to->sym, to->addr, -					 self->level, bf, size, width); +					 he->level, bf, size, width);  } @@ -456,13 +475,13 @@ sort__mispredict_cmp(struct hist_entry *left, struct hist_entry *right)  	return mp || p;  } -static int hist_entry__mispredict_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__mispredict_snprintf(struct hist_entry *he, char *bf,  				    size_t size, unsigned int width){  	static const char *out = "N/A"; -	if (self->branch_info->flags.predicted) +	if (he->branch_info->flags.predicted)  		out = "N"; -	else if (self->branch_info->flags.mispred) +	else if (he->branch_info->flags.mispred)  		out = "Y";  	return repsep_snprintf(bf, size, "%-*s", width, out); @@ -482,19 +501,19 @@ sort__daddr_cmp(struct hist_entry *left, struct hist_entry *right)  	return (int64_t)(r - l);  } -static int hist_entry__daddr_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__daddr_snprintf(struct hist_entry *he, char *bf,  				    size_t size, unsigned int width)  {  	uint64_t addr = 0;  	struct map *map = NULL;  	struct symbol *sym = NULL; -	if (self->mem_info) { -		addr = self->mem_info->daddr.addr; -		map = self->mem_info->daddr.map; -		sym = self->mem_info->daddr.sym; +	if (he->mem_info) { +		addr = he->mem_info->daddr.addr; +		map = he->mem_info->daddr.map; +		sym = he->mem_info->daddr.sym;  	} -	return _hist_entry__sym_snprintf(map, sym, addr, self->level, bf, size, +	return _hist_entry__sym_snprintf(map, sym, addr, he->level, bf, size,  					 width);  } @@ -512,13 +531,13 @@ sort__dso_daddr_cmp(struct hist_entry *left, struct hist_entry *right)  	return _sort__dso_cmp(map_l, map_r);  } -static int hist_entry__dso_daddr_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__dso_daddr_snprintf(struct hist_entry *he, char *bf,  				    size_t size, unsigned int width)  {  	struct map *map = NULL; -	if (self->mem_info) -		map = self->mem_info->daddr.map; +	if (he->mem_info) +		map = he->mem_info->daddr.map;  	return _hist_entry__dso_snprintf(map, bf, size, width);  } @@ -542,14 +561,14 @@ sort__locked_cmp(struct hist_entry *left, struct hist_entry *right)  	return (int64_t)(data_src_r.mem_lock - data_src_l.mem_lock);  } -static int hist_entry__locked_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__locked_snprintf(struct hist_entry *he, char *bf,  				    size_t size, unsigned int width)  {  	const char *out;  	u64 mask = PERF_MEM_LOCK_NA; -	if (self->mem_info) -		mask = self->mem_info->data_src.mem_lock; +	if (he->mem_info) +		mask = he->mem_info->data_src.mem_lock;  	if (mask & PERF_MEM_LOCK_NA)  		out = "N/A"; @@ -591,7 +610,7 @@ static const char * const tlb_access[] = {  };  #define NUM_TLB_ACCESS (sizeof(tlb_access)/sizeof(const char *)) -static int hist_entry__tlb_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__tlb_snprintf(struct hist_entry *he, char *bf,  				    size_t size, unsigned int width)  {  	char out[64]; @@ -602,8 +621,8 @@ static int hist_entry__tlb_snprintf(struct hist_entry *self, char *bf,  	out[0] = '\0'; -	if (self->mem_info) -		m = self->mem_info->data_src.mem_dtlb; +	if (he->mem_info) +		m = he->mem_info->data_src.mem_dtlb;  	hit = m & PERF_MEM_TLB_HIT;  	miss = m & PERF_MEM_TLB_MISS; @@ -668,7 +687,7 @@ static const char * const mem_lvl[] = {  };  #define NUM_MEM_LVL (sizeof(mem_lvl)/sizeof(const char *)) -static int hist_entry__lvl_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__lvl_snprintf(struct hist_entry *he, char *bf,  				    size_t size, unsigned int width)  {  	char out[64]; @@ -677,8 +696,8 @@ static int hist_entry__lvl_snprintf(struct hist_entry *self, char *bf,  	u64 m =  PERF_MEM_LVL_NA;  	u64 hit, miss; -	if (self->mem_info) -		m  = self->mem_info->data_src.mem_lvl; +	if (he->mem_info) +		m  = he->mem_info->data_src.mem_lvl;  	out[0] = '\0'; @@ -736,7 +755,7 @@ static const char * const snoop_access[] = {  };  #define NUM_SNOOP_ACCESS (sizeof(snoop_access)/sizeof(const char *)) -static int hist_entry__snoop_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__snoop_snprintf(struct hist_entry *he, char *bf,  				    size_t size, unsigned int width)  {  	char out[64]; @@ -746,8 +765,8 @@ static int hist_entry__snoop_snprintf(struct hist_entry *self, char *bf,  	out[0] = '\0'; -	if (self->mem_info) -		m = self->mem_info->data_src.mem_snoop; +	if (he->mem_info) +		m = he->mem_info->data_src.mem_snoop;  	for (i = 0; m && i < NUM_SNOOP_ACCESS; i++, m >>= 1) {  		if (!(m & 0x1)) @@ -766,6 +785,104 @@ static int hist_entry__snoop_snprintf(struct hist_entry *self, char *bf,  	return repsep_snprintf(bf, size, "%-*s", width, out);  } +static inline  u64 cl_address(u64 address) +{ +	/* return the cacheline of the address */ +	return (address & ~(cacheline_size - 1)); +} + +static int64_t +sort__dcacheline_cmp(struct hist_entry *left, struct hist_entry *right) +{ +	u64 l, r; +	struct map *l_map, *r_map; + +	if (!left->mem_info)  return -1; +	if (!right->mem_info) return 1; + +	/* group event types together */ +	if (left->cpumode > right->cpumode) return -1; +	if (left->cpumode < right->cpumode) return 1; + +	l_map = left->mem_info->daddr.map; +	r_map = right->mem_info->daddr.map; + +	/* if both are NULL, jump to sort on al_addr instead */ +	if (!l_map && !r_map) +		goto addr; + +	if (!l_map) return -1; +	if (!r_map) return 1; + +	if (l_map->maj > r_map->maj) return -1; +	if (l_map->maj < r_map->maj) return 1; + +	if (l_map->min > r_map->min) return -1; +	if (l_map->min < r_map->min) return 1; + +	if (l_map->ino > r_map->ino) return -1; +	if (l_map->ino < r_map->ino) return 1; + +	if (l_map->ino_generation > r_map->ino_generation) return -1; +	if (l_map->ino_generation < r_map->ino_generation) return 1; + +	/* +	 * Addresses with no major/minor numbers are assumed to be +	 * anonymous in userspace.  Sort those on pid then address. +	 * +	 * The kernel and non-zero major/minor mapped areas are +	 * assumed to be unity mapped.  Sort those on address. +	 */ + +	if ((left->cpumode != PERF_RECORD_MISC_KERNEL) && +	    (!(l_map->flags & MAP_SHARED)) && +	    !l_map->maj && !l_map->min && !l_map->ino && +	    !l_map->ino_generation) { +		/* userspace anonymous */ + +		if (left->thread->pid_ > right->thread->pid_) return -1; +		if (left->thread->pid_ < right->thread->pid_) return 1; +	} + +addr: +	/* al_addr does all the right addr - start + offset calculations */ +	l = cl_address(left->mem_info->daddr.al_addr); +	r = cl_address(right->mem_info->daddr.al_addr); + +	if (l > r) return -1; +	if (l < r) return 1; + +	return 0; +} + +static int hist_entry__dcacheline_snprintf(struct hist_entry *he, char *bf, +					  size_t size, unsigned int width) +{ + +	uint64_t addr = 0; +	struct map *map = NULL; +	struct symbol *sym = NULL; +	char level = he->level; + +	if (he->mem_info) { +		addr = cl_address(he->mem_info->daddr.al_addr); +		map = he->mem_info->daddr.map; +		sym = he->mem_info->daddr.sym; + +		/* print [s] for shared data mmaps */ +		if ((he->cpumode != PERF_RECORD_MISC_KERNEL) && +		     map && (map->type == MAP__VARIABLE) && +		    (map->flags & MAP_SHARED) && +		    (map->maj || map->min || map->ino || +		     map->ino_generation)) +			level = 's'; +		else if (!map) +			level = 'X'; +	} +	return _hist_entry__sym_snprintf(map, sym, addr, level, bf, size, +					 width); +} +  struct sort_entry sort_mispredict = {  	.se_header	= "Branch Mispredicted",  	.se_cmp		= sort__mispredict_cmp, @@ -784,10 +901,10 @@ sort__local_weight_cmp(struct hist_entry *left, struct hist_entry *right)  	return he_weight(left) - he_weight(right);  } -static int hist_entry__local_weight_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__local_weight_snprintf(struct hist_entry *he, char *bf,  				    size_t size, unsigned int width)  { -	return repsep_snprintf(bf, size, "%-*llu", width, he_weight(self)); +	return repsep_snprintf(bf, size, "%-*llu", width, he_weight(he));  }  struct sort_entry sort_local_weight = { @@ -803,10 +920,10 @@ sort__global_weight_cmp(struct hist_entry *left, struct hist_entry *right)  	return left->stat.weight - right->stat.weight;  } -static int hist_entry__global_weight_snprintf(struct hist_entry *self, char *bf, +static int hist_entry__global_weight_snprintf(struct hist_entry *he, char *bf,  					      size_t size, unsigned int width)  { -	return repsep_snprintf(bf, size, "%-*llu", width, self->stat.weight); +	return repsep_snprintf(bf, size, "%-*llu", width, he->stat.weight);  }  struct sort_entry sort_global_weight = { @@ -858,6 +975,134 @@ struct sort_entry sort_mem_snoop = {  	.se_width_idx	= HISTC_MEM_SNOOP,  }; +struct sort_entry sort_mem_dcacheline = { +	.se_header	= "Data Cacheline", +	.se_cmp		= sort__dcacheline_cmp, +	.se_snprintf	= hist_entry__dcacheline_snprintf, +	.se_width_idx	= HISTC_MEM_DCACHELINE, +}; + +static int64_t +sort__abort_cmp(struct hist_entry *left, struct hist_entry *right) +{ +	return left->branch_info->flags.abort != +		right->branch_info->flags.abort; +} + +static int hist_entry__abort_snprintf(struct hist_entry *he, char *bf, +				    size_t size, unsigned int width) +{ +	static const char *out = "."; + +	if (he->branch_info->flags.abort) +		out = "A"; +	return repsep_snprintf(bf, size, "%-*s", width, out); +} + +struct sort_entry sort_abort = { +	.se_header	= "Transaction abort", +	.se_cmp		= sort__abort_cmp, +	.se_snprintf	= hist_entry__abort_snprintf, +	.se_width_idx	= HISTC_ABORT, +}; + +static int64_t +sort__in_tx_cmp(struct hist_entry *left, struct hist_entry *right) +{ +	return left->branch_info->flags.in_tx != +		right->branch_info->flags.in_tx; +} + +static int hist_entry__in_tx_snprintf(struct hist_entry *he, char *bf, +				    size_t size, unsigned int width) +{ +	static const char *out = "."; + +	if (he->branch_info->flags.in_tx) +		out = "T"; + +	return repsep_snprintf(bf, size, "%-*s", width, out); +} + +struct sort_entry sort_in_tx = { +	.se_header	= "Branch in transaction", +	.se_cmp		= sort__in_tx_cmp, +	.se_snprintf	= hist_entry__in_tx_snprintf, +	.se_width_idx	= HISTC_IN_TX, +}; + +static int64_t +sort__transaction_cmp(struct hist_entry *left, struct hist_entry *right) +{ +	return left->transaction - right->transaction; +} + +static inline char *add_str(char *p, const char *str) +{ +	strcpy(p, str); +	return p + strlen(str); +} + +static struct txbit { +	unsigned flag; +	const char *name; +	int skip_for_len; +} txbits[] = { +	{ PERF_TXN_ELISION,        "EL ",        0 }, +	{ PERF_TXN_TRANSACTION,    "TX ",        1 }, +	{ PERF_TXN_SYNC,           "SYNC ",      1 }, +	{ PERF_TXN_ASYNC,          "ASYNC ",     0 }, +	{ PERF_TXN_RETRY,          "RETRY ",     0 }, +	{ PERF_TXN_CONFLICT,       "CON ",       0 }, +	{ PERF_TXN_CAPACITY_WRITE, "CAP-WRITE ", 1 }, +	{ PERF_TXN_CAPACITY_READ,  "CAP-READ ",  0 }, +	{ 0, NULL, 0 } +}; + +int hist_entry__transaction_len(void) +{ +	int i; +	int len = 0; + +	for (i = 0; txbits[i].name; i++) { +		if (!txbits[i].skip_for_len) +			len += strlen(txbits[i].name); +	} +	len += 4; /* :XX<space> */ +	return len; +} + +static int hist_entry__transaction_snprintf(struct hist_entry *he, char *bf, +					    size_t size, unsigned int width) +{ +	u64 t = he->transaction; +	char buf[128]; +	char *p = buf; +	int i; + +	buf[0] = 0; +	for (i = 0; txbits[i].name; i++) +		if (txbits[i].flag & t) +			p = add_str(p, txbits[i].name); +	if (t && !(t & (PERF_TXN_SYNC|PERF_TXN_ASYNC))) +		p = add_str(p, "NEITHER "); +	if (t & PERF_TXN_ABORT_MASK) { +		sprintf(p, ":%" PRIx64, +			(t & PERF_TXN_ABORT_MASK) >> +			PERF_TXN_ABORT_SHIFT); +		p += strlen(p); +	} + +	return repsep_snprintf(bf, size, "%-*s", width, buf); +} + +struct sort_entry sort_transaction = { +	.se_header	= "Transaction                ", +	.se_cmp		= sort__transaction_cmp, +	.se_snprintf	= hist_entry__transaction_snprintf, +	.se_width_idx	= HISTC_TRANSACTION, +}; +  struct sort_dimension {  	const char		*name;  	struct sort_entry	*entry; @@ -876,6 +1121,7 @@ static struct sort_dimension common_sort_dimensions[] = {  	DIM(SORT_SRCLINE, "srcline", sort_srcline),  	DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight),  	DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight), +	DIM(SORT_TRANSACTION, "transaction", sort_transaction),  };  #undef DIM @@ -888,6 +1134,8 @@ static struct sort_dimension bstack_sort_dimensions[] = {  	DIM(SORT_SYM_FROM, "symbol_from", sort_sym_from),  	DIM(SORT_SYM_TO, "symbol_to", sort_sym_to),  	DIM(SORT_MISPREDICT, "mispredict", sort_mispredict), +	DIM(SORT_IN_TX, "in_tx", sort_in_tx), +	DIM(SORT_ABORT, "abort", sort_abort),  };  #undef DIM @@ -901,23 +1149,199 @@ static struct sort_dimension memory_sort_dimensions[] = {  	DIM(SORT_MEM_TLB, "tlb", sort_mem_tlb),  	DIM(SORT_MEM_LVL, "mem", sort_mem_lvl),  	DIM(SORT_MEM_SNOOP, "snoop", sort_mem_snoop), +	DIM(SORT_MEM_DCACHELINE, "dcacheline", sort_mem_dcacheline),  };  #undef DIM -static void __sort_dimension__add(struct sort_dimension *sd, enum sort_type idx) +struct hpp_dimension { +	const char		*name; +	struct perf_hpp_fmt	*fmt; +	int			taken; +}; + +#define DIM(d, n) { .name = n, .fmt = &perf_hpp__format[d], } + +static struct hpp_dimension hpp_sort_dimensions[] = { +	DIM(PERF_HPP__OVERHEAD, "overhead"), +	DIM(PERF_HPP__OVERHEAD_SYS, "overhead_sys"), +	DIM(PERF_HPP__OVERHEAD_US, "overhead_us"), +	DIM(PERF_HPP__OVERHEAD_GUEST_SYS, "overhead_guest_sys"), +	DIM(PERF_HPP__OVERHEAD_GUEST_US, "overhead_guest_us"), +	DIM(PERF_HPP__OVERHEAD_ACC, "overhead_children"), +	DIM(PERF_HPP__SAMPLES, "sample"), +	DIM(PERF_HPP__PERIOD, "period"), +}; + +#undef DIM + +struct hpp_sort_entry { +	struct perf_hpp_fmt hpp; +	struct sort_entry *se; +}; + +bool perf_hpp__same_sort_entry(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)  { -	if (sd->taken) +	struct hpp_sort_entry *hse_a; +	struct hpp_sort_entry *hse_b; + +	if (!perf_hpp__is_sort_entry(a) || !perf_hpp__is_sort_entry(b)) +		return false; + +	hse_a = container_of(a, struct hpp_sort_entry, hpp); +	hse_b = container_of(b, struct hpp_sort_entry, hpp); + +	return hse_a->se == hse_b->se; +} + +void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists) +{ +	struct hpp_sort_entry *hse; + +	if (!perf_hpp__is_sort_entry(fmt))  		return; +	hse = container_of(fmt, struct hpp_sort_entry, hpp); +	hists__new_col_len(hists, hse->se->se_width_idx, +			   strlen(hse->se->se_header)); +} + +static int __sort__hpp_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, +			      struct perf_evsel *evsel) +{ +	struct hpp_sort_entry *hse; +	size_t len; + +	hse = container_of(fmt, struct hpp_sort_entry, hpp); +	len = hists__col_len(&evsel->hists, hse->se->se_width_idx); + +	return scnprintf(hpp->buf, hpp->size, "%*s", len, hse->se->se_header); +} + +static int __sort__hpp_width(struct perf_hpp_fmt *fmt, +			     struct perf_hpp *hpp __maybe_unused, +			     struct perf_evsel *evsel) +{ +	struct hpp_sort_entry *hse; + +	hse = container_of(fmt, struct hpp_sort_entry, hpp); + +	return hists__col_len(&evsel->hists, hse->se->se_width_idx); +} + +static int __sort__hpp_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, +			     struct hist_entry *he) +{ +	struct hpp_sort_entry *hse; +	size_t len; + +	hse = container_of(fmt, struct hpp_sort_entry, hpp); +	len = hists__col_len(he->hists, hse->se->se_width_idx); + +	return hse->se->se_snprintf(he, hpp->buf, hpp->size, len); +} + +static struct hpp_sort_entry * +__sort_dimension__alloc_hpp(struct sort_dimension *sd) +{ +	struct hpp_sort_entry *hse; + +	hse = malloc(sizeof(*hse)); +	if (hse == NULL) { +		pr_err("Memory allocation failed\n"); +		return NULL; +	} + +	hse->se = sd->entry; +	hse->hpp.header = __sort__hpp_header; +	hse->hpp.width = __sort__hpp_width; +	hse->hpp.entry = __sort__hpp_entry; +	hse->hpp.color = NULL; + +	hse->hpp.cmp = sd->entry->se_cmp; +	hse->hpp.collapse = sd->entry->se_collapse ? : sd->entry->se_cmp; +	hse->hpp.sort = sd->entry->se_sort ? : hse->hpp.collapse; + +	INIT_LIST_HEAD(&hse->hpp.list); +	INIT_LIST_HEAD(&hse->hpp.sort_list); +	hse->hpp.elide = false; + +	return hse; +} + +bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format) +{ +	return format->header == __sort__hpp_header; +} + +static int __sort_dimension__add_hpp_sort(struct sort_dimension *sd) +{ +	struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd); + +	if (hse == NULL) +		return -1; + +	perf_hpp__register_sort_field(&hse->hpp); +	return 0; +} + +static int __sort_dimension__add_hpp_output(struct sort_dimension *sd) +{ +	struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd); + +	if (hse == NULL) +		return -1; + +	perf_hpp__column_register(&hse->hpp); +	return 0; +} + +static int __sort_dimension__add(struct sort_dimension *sd) +{ +	if (sd->taken) +		return 0; + +	if (__sort_dimension__add_hpp_sort(sd) < 0) +		return -1; +  	if (sd->entry->se_collapse)  		sort__need_collapse = 1; -	if (list_empty(&hist_entry__sort_list)) -		sort__first_dimension = idx; +	sd->taken = 1; + +	return 0; +} + +static int __hpp_dimension__add(struct hpp_dimension *hd) +{ +	if (!hd->taken) { +		hd->taken = 1; + +		perf_hpp__register_sort_field(hd->fmt); +	} +	return 0; +} + +static int __sort_dimension__add_output(struct sort_dimension *sd) +{ +	if (sd->taken) +		return 0; + +	if (__sort_dimension__add_hpp_output(sd) < 0) +		return -1; -	list_add_tail(&sd->entry->list, &hist_entry__sort_list);  	sd->taken = 1; +	return 0; +} + +static int __hpp_dimension__add_output(struct hpp_dimension *hd) +{ +	if (!hd->taken) { +		hd->taken = 1; + +		perf_hpp__column_register(hd->fmt); +	} +	return 0;  }  int sort_dimension__add(const char *tok) @@ -942,10 +1366,20 @@ int sort_dimension__add(const char *tok)  			sort__has_parent = 1;  		} else if (sd->entry == &sort_sym) {  			sort__has_sym = 1; +		} else if (sd->entry == &sort_dso) { +			sort__has_dso = 1;  		} -		__sort_dimension__add(sd, i); -		return 0; +		return __sort_dimension__add(sd); +	} + +	for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) { +		struct hpp_dimension *hd = &hpp_sort_dimensions[i]; + +		if (strncasecmp(tok, hd->name, strlen(tok))) +			continue; + +		return __hpp_dimension__add(hd);  	}  	for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) { @@ -960,7 +1394,7 @@ int sort_dimension__add(const char *tok)  		if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to)  			sort__has_sym = 1; -		__sort_dimension__add(sd, i + __SORT_BRANCH_STACK); +		__sort_dimension__add(sd);  		return 0;  	} @@ -976,18 +1410,47 @@ int sort_dimension__add(const char *tok)  		if (sd->entry == &sort_mem_daddr_sym)  			sort__has_sym = 1; -		__sort_dimension__add(sd, i + __SORT_MEMORY_MODE); +		__sort_dimension__add(sd);  		return 0;  	}  	return -ESRCH;  } -int setup_sorting(void) +static const char *get_default_sort_order(void) +{ +	const char *default_sort_orders[] = { +		default_sort_order, +		default_branch_sort_order, +		default_mem_sort_order, +		default_top_sort_order, +		default_diff_sort_order, +	}; + +	BUG_ON(sort__mode >= ARRAY_SIZE(default_sort_orders)); + +	return default_sort_orders[sort__mode]; +} + +static int __setup_sorting(void)  { -	char *tmp, *tok, *str = strdup(sort_order); +	char *tmp, *tok, *str; +	const char *sort_keys = sort_order;  	int ret = 0; +	if (sort_keys == NULL) { +		if (field_order) { +			/* +			 * If user specified field order but no sort order, +			 * we'll honor it and not add default sort orders. +			 */ +			return 0; +		} + +		sort_keys = get_default_sort_order(); +	} + +	str = strdup(sort_keys);  	if (str == NULL) {  		error("Not enough memory to setup sort keys");  		return -ENOMEM; @@ -1009,53 +1472,235 @@ int setup_sorting(void)  	return ret;  } -static void sort_entry__setup_elide(struct sort_entry *self, -				    struct strlist *list, -				    const char *list_name, FILE *fp) +void perf_hpp__set_elide(int idx, bool elide) +{ +	struct perf_hpp_fmt *fmt; +	struct hpp_sort_entry *hse; + +	perf_hpp__for_each_format(fmt) { +		if (!perf_hpp__is_sort_entry(fmt)) +			continue; + +		hse = container_of(fmt, struct hpp_sort_entry, hpp); +		if (hse->se->se_width_idx == idx) { +			fmt->elide = elide; +			break; +		} +	} +} + +static bool __get_elide(struct strlist *list, const char *list_name, FILE *fp)  {  	if (list && strlist__nr_entries(list) == 1) {  		if (fp != NULL)  			fprintf(fp, "# %s: %s\n", list_name,  				strlist__entry(list, 0)->s); -		self->elide = true; +		return true;  	} +	return false; +} + +static bool get_elide(int idx, FILE *output) +{ +	switch (idx) { +	case HISTC_SYMBOL: +		return __get_elide(symbol_conf.sym_list, "symbol", output); +	case HISTC_DSO: +		return __get_elide(symbol_conf.dso_list, "dso", output); +	case HISTC_COMM: +		return __get_elide(symbol_conf.comm_list, "comm", output); +	default: +		break; +	} + +	if (sort__mode != SORT_MODE__BRANCH) +		return false; + +	switch (idx) { +	case HISTC_SYMBOL_FROM: +		return __get_elide(symbol_conf.sym_from_list, "sym_from", output); +	case HISTC_SYMBOL_TO: +		return __get_elide(symbol_conf.sym_to_list, "sym_to", output); +	case HISTC_DSO_FROM: +		return __get_elide(symbol_conf.dso_from_list, "dso_from", output); +	case HISTC_DSO_TO: +		return __get_elide(symbol_conf.dso_to_list, "dso_to", output); +	default: +		break; +	} + +	return false;  }  void sort__setup_elide(FILE *output)  { -	sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, -				"dso", output); -	sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, -				"comm", output); -	sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, -				"symbol", output); - -	if (sort__mode == SORT_MODE__BRANCH) { -		sort_entry__setup_elide(&sort_dso_from, -					symbol_conf.dso_from_list, -					"dso_from", output); -		sort_entry__setup_elide(&sort_dso_to, -					symbol_conf.dso_to_list, -					"dso_to", output); -		sort_entry__setup_elide(&sort_sym_from, -					symbol_conf.sym_from_list, -					"sym_from", output); -		sort_entry__setup_elide(&sort_sym_to, -					symbol_conf.sym_to_list, -					"sym_to", output); -	} else if (sort__mode == SORT_MODE__MEMORY) { -		sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, -					"symbol_daddr", output); -		sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, -					"dso_daddr", output); -		sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, -					"mem", output); -		sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, -					"local_weight", output); -		sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, -					"tlb", output); -		sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, -					"snoop", output); +	struct perf_hpp_fmt *fmt; +	struct hpp_sort_entry *hse; + +	perf_hpp__for_each_format(fmt) { +		if (!perf_hpp__is_sort_entry(fmt)) +			continue; + +		hse = container_of(fmt, struct hpp_sort_entry, hpp); +		fmt->elide = get_elide(hse->se->se_width_idx, output); +	} + +	/* +	 * It makes no sense to elide all of sort entries. +	 * Just revert them to show up again. +	 */ +	perf_hpp__for_each_format(fmt) { +		if (!perf_hpp__is_sort_entry(fmt)) +			continue; + +		if (!fmt->elide) +			return;  	} +	perf_hpp__for_each_format(fmt) { +		if (!perf_hpp__is_sort_entry(fmt)) +			continue; + +		fmt->elide = false; +	} +} + +static int output_field_add(char *tok) +{ +	unsigned int i; + +	for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) { +		struct sort_dimension *sd = &common_sort_dimensions[i]; + +		if (strncasecmp(tok, sd->name, strlen(tok))) +			continue; + +		return __sort_dimension__add_output(sd); +	} + +	for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) { +		struct hpp_dimension *hd = &hpp_sort_dimensions[i]; + +		if (strncasecmp(tok, hd->name, strlen(tok))) +			continue; + +		return __hpp_dimension__add_output(hd); +	} + +	for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) { +		struct sort_dimension *sd = &bstack_sort_dimensions[i]; + +		if (strncasecmp(tok, sd->name, strlen(tok))) +			continue; + +		return __sort_dimension__add_output(sd); +	} + +	for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) { +		struct sort_dimension *sd = &memory_sort_dimensions[i]; + +		if (strncasecmp(tok, sd->name, strlen(tok))) +			continue; + +		return __sort_dimension__add_output(sd); +	} + +	return -ESRCH; +} + +static void reset_dimensions(void) +{ +	unsigned int i; + +	for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) +		common_sort_dimensions[i].taken = 0; + +	for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) +		hpp_sort_dimensions[i].taken = 0; + +	for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) +		bstack_sort_dimensions[i].taken = 0; + +	for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) +		memory_sort_dimensions[i].taken = 0; +} + +static int __setup_output_field(void) +{ +	char *tmp, *tok, *str; +	int ret = 0; + +	if (field_order == NULL) +		return 0; + +	reset_dimensions(); + +	str = strdup(field_order); +	if (str == NULL) { +		error("Not enough memory to setup output fields"); +		return -ENOMEM; +	} + +	for (tok = strtok_r(str, ", ", &tmp); +			tok; tok = strtok_r(NULL, ", ", &tmp)) { +		ret = output_field_add(tok); +		if (ret == -EINVAL) { +			error("Invalid --fields key: `%s'", tok); +			break; +		} else if (ret == -ESRCH) { +			error("Unknown --fields key: `%s'", tok); +			break; +		} +	} + +	free(str); +	return ret; +} + +int setup_sorting(void) +{ +	int err; + +	err = __setup_sorting(); +	if (err < 0) +		return err; + +	if (parent_pattern != default_parent_pattern) { +		err = sort_dimension__add("parent"); +		if (err < 0) +			return err; +	} + +	reset_dimensions(); + +	/* +	 * perf diff doesn't use default hpp output fields. +	 */ +	if (sort__mode != SORT_MODE__DIFF) +		perf_hpp__init(); + +	err = __setup_output_field(); +	if (err < 0) +		return err; + +	/* copy sort keys to output fields */ +	perf_hpp__setup_output_field(); +	/* and then copy output fields to sort keys */ +	perf_hpp__append_sort_keys(); + +	return 0; +} + +void reset_output_field(void) +{ +	sort__need_collapse = 0; +	sort__has_parent = 0; +	sort__has_sym = 0; +	sort__has_dso = 0; + +	field_order = NULL; +	sort_order = NULL; + +	reset_dimensions(); +	perf_hpp__reset_output_field();  }  | 
