• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
8 regex_t		parent_regex;
9 const char	default_parent_pattern[] = "^sys_|^do_page_fault";
10 const char	*parent_pattern = default_parent_pattern;
11 const char	default_sort_order[] = "comm,dso,symbol";
12 const char	default_branch_sort_order[] = "comm,dso_from,symbol_from,dso_to,symbol_to";
13 const char	default_mem_sort_order[] = "local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked";
14 const char	default_top_sort_order[] = "dso,symbol";
15 const char	default_diff_sort_order[] = "dso,symbol";
16 const char	*sort_order;
17 const char	*field_order;
18 regex_t		ignore_callees_regex;
19 int		have_ignore_callees = 0;
20 int		sort__need_collapse = 0;
21 int		sort__has_parent = 0;
22 int		sort__has_sym = 0;
23 int		sort__has_dso = 0;
24 enum sort_mode	sort__mode = SORT_MODE__NORMAL;
25 
26 
repsep_snprintf(char * bf,size_t size,const char * fmt,...)27 static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...)
28 {
29 	int n;
30 	va_list ap;
31 
32 	va_start(ap, fmt);
33 	n = vsnprintf(bf, size, fmt, ap);
34 	if (symbol_conf.field_sep && n > 0) {
35 		char *sep = bf;
36 
37 		while (1) {
38 			sep = strchr(sep, *symbol_conf.field_sep);
39 			if (sep == NULL)
40 				break;
41 			*sep = '.';
42 		}
43 	}
44 	va_end(ap);
45 
46 	if (n >= (int)size)
47 		return size - 1;
48 	return n;
49 }
50 
cmp_null(const void * l,const void * r)51 static int64_t cmp_null(const void *l, const void *r)
52 {
53 	if (!l && !r)
54 		return 0;
55 	else if (!l)
56 		return -1;
57 	else
58 		return 1;
59 }
60 
61 /* --sort pid */
62 
63 static int64_t
sort__thread_cmp(struct hist_entry * left,struct hist_entry * right)64 sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
65 {
66 	return right->thread->tid - left->thread->tid;
67 }
68 
hist_entry__thread_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)69 static int hist_entry__thread_snprintf(struct hist_entry *he, char *bf,
70 				       size_t size, unsigned int width)
71 {
72 	const char *comm = thread__comm_str(he->thread);
73 
74 	width = max(7U, width) - 6;
75 	return repsep_snprintf(bf, size, "%5d:%-*.*s", he->thread->tid,
76 			       width, width, comm ?: "");
77 }
78 
79 struct sort_entry sort_thread = {
80 	.se_header	= "  Pid:Command",
81 	.se_cmp		= sort__thread_cmp,
82 	.se_snprintf	= hist_entry__thread_snprintf,
83 	.se_width_idx	= HISTC_THREAD,
84 };
85 
86 /* --sort comm */
87 
88 static int64_t
sort__comm_cmp(struct hist_entry * left,struct hist_entry * right)89 sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
90 {
91 	/* Compare the addr that should be unique among comm */
92 	return comm__str(right->comm) - comm__str(left->comm);
93 }
94 
95 static int64_t
sort__comm_collapse(struct hist_entry * left,struct hist_entry * right)96 sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
97 {
98 	/* Compare the addr that should be unique among comm */
99 	return comm__str(right->comm) - comm__str(left->comm);
100 }
101 
102 static int64_t
sort__comm_sort(struct hist_entry * left,struct hist_entry * right)103 sort__comm_sort(struct hist_entry *left, struct hist_entry *right)
104 {
105 	return strcmp(comm__str(right->comm), comm__str(left->comm));
106 }
107 
hist_entry__comm_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)108 static int hist_entry__comm_snprintf(struct hist_entry *he, char *bf,
109 				     size_t size, unsigned int width)
110 {
111 	return repsep_snprintf(bf, size, "%-*.*s", width, width, comm__str(he->comm));
112 }
113 
114 struct sort_entry sort_comm = {
115 	.se_header	= "Command",
116 	.se_cmp		= sort__comm_cmp,
117 	.se_collapse	= sort__comm_collapse,
118 	.se_sort	= sort__comm_sort,
119 	.se_snprintf	= hist_entry__comm_snprintf,
120 	.se_width_idx	= HISTC_COMM,
121 };
122 
123 /* --sort dso */
124 
_sort__dso_cmp(struct map * map_l,struct map * map_r)125 static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r)
126 {
127 	struct dso *dso_l = map_l ? map_l->dso : NULL;
128 	struct dso *dso_r = map_r ? map_r->dso : NULL;
129 	const char *dso_name_l, *dso_name_r;
130 
131 	if (!dso_l || !dso_r)
132 		return cmp_null(dso_r, dso_l);
133 
134 	if (verbose) {
135 		dso_name_l = dso_l->long_name;
136 		dso_name_r = dso_r->long_name;
137 	} else {
138 		dso_name_l = dso_l->short_name;
139 		dso_name_r = dso_r->short_name;
140 	}
141 
142 	return strcmp(dso_name_l, dso_name_r);
143 }
144 
145 static int64_t
sort__dso_cmp(struct hist_entry * left,struct hist_entry * right)146 sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
147 {
148 	return _sort__dso_cmp(right->ms.map, left->ms.map);
149 }
150 
_hist_entry__dso_snprintf(struct map * map,char * bf,size_t size,unsigned int width)151 static int _hist_entry__dso_snprintf(struct map *map, char *bf,
152 				     size_t size, unsigned int width)
153 {
154 	if (map && map->dso) {
155 		const char *dso_name = !verbose ? map->dso->short_name :
156 			map->dso->long_name;
157 		return repsep_snprintf(bf, size, "%-*.*s", width, width, dso_name);
158 	}
159 
160 	return repsep_snprintf(bf, size, "%-*.*s", width, width, "[unknown]");
161 }
162 
hist_entry__dso_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)163 static int hist_entry__dso_snprintf(struct hist_entry *he, char *bf,
164 				    size_t size, unsigned int width)
165 {
166 	return _hist_entry__dso_snprintf(he->ms.map, bf, size, width);
167 }
168 
169 struct sort_entry sort_dso = {
170 	.se_header	= "Shared Object",
171 	.se_cmp		= sort__dso_cmp,
172 	.se_snprintf	= hist_entry__dso_snprintf,
173 	.se_width_idx	= HISTC_DSO,
174 };
175 
176 /* --sort symbol */
177 
_sort__addr_cmp(u64 left_ip,u64 right_ip)178 static int64_t _sort__addr_cmp(u64 left_ip, u64 right_ip)
179 {
180 	return (int64_t)(right_ip - left_ip);
181 }
182 
_sort__sym_cmp(struct symbol * sym_l,struct symbol * sym_r)183 static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r)
184 {
185 	u64 ip_l, ip_r;
186 
187 	if (!sym_l || !sym_r)
188 		return cmp_null(sym_l, sym_r);
189 
190 	if (sym_l == sym_r)
191 		return 0;
192 
193 	ip_l = sym_l->start;
194 	ip_r = sym_r->start;
195 
196 	return (int64_t)(ip_r - ip_l);
197 }
198 
199 static int64_t
sort__sym_cmp(struct hist_entry * left,struct hist_entry * right)200 sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
201 {
202 	int64_t ret;
203 
204 	if (!left->ms.sym && !right->ms.sym)
205 		return _sort__addr_cmp(left->ip, right->ip);
206 
207 	/*
208 	 * comparing symbol address alone is not enough since it's a
209 	 * relative address within a dso.
210 	 */
211 	if (!sort__has_dso) {
212 		ret = sort__dso_cmp(left, right);
213 		if (ret != 0)
214 			return ret;
215 	}
216 
217 	return _sort__sym_cmp(left->ms.sym, right->ms.sym);
218 }
219 
220 static int64_t
sort__sym_sort(struct hist_entry * left,struct hist_entry * right)221 sort__sym_sort(struct hist_entry *left, struct hist_entry *right)
222 {
223 	if (!left->ms.sym || !right->ms.sym)
224 		return cmp_null(left->ms.sym, right->ms.sym);
225 
226 	return strcmp(right->ms.sym->name, left->ms.sym->name);
227 }
228 
_hist_entry__sym_snprintf(struct map * map,struct symbol * sym,u64 ip,char level,char * bf,size_t size,unsigned int width)229 static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym,
230 				     u64 ip, char level, char *bf, size_t size,
231 				     unsigned int width)
232 {
233 	size_t ret = 0;
234 
235 	if (verbose) {
236 		char o = map ? dso__symtab_origin(map->dso) : '!';
237 		ret += repsep_snprintf(bf, size, "%-#*llx %c ",
238 				       BITS_PER_LONG / 4 + 2, ip, o);
239 	}
240 
241 	ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level);
242 	if (sym && map) {
243 		if (map->type == MAP__VARIABLE) {
244 			ret += repsep_snprintf(bf + ret, size - ret, "%s", sym->name);
245 			ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx",
246 					ip - map->unmap_ip(map, sym->start));
247 			ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
248 				       width - ret, "");
249 		} else {
250 			ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
251 					       width - ret,
252 					       sym->name);
253 		}
254 	} else {
255 		size_t len = BITS_PER_LONG / 4;
256 		ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx",
257 				       len, ip);
258 		ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
259 				       width - ret, "");
260 	}
261 
262 	if (ret > width)
263 		bf[width] = '\0';
264 
265 	return width;
266 }
267 
hist_entry__sym_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)268 static int hist_entry__sym_snprintf(struct hist_entry *he, char *bf,
269 				    size_t size, unsigned int width)
270 {
271 	return _hist_entry__sym_snprintf(he->ms.map, he->ms.sym, he->ip,
272 					 he->level, bf, size, width);
273 }
274 
275 struct sort_entry sort_sym = {
276 	.se_header	= "Symbol",
277 	.se_cmp		= sort__sym_cmp,
278 	.se_sort	= sort__sym_sort,
279 	.se_snprintf	= hist_entry__sym_snprintf,
280 	.se_width_idx	= HISTC_SYMBOL,
281 };
282 
283 /* --sort srcline */
284 
285 static int64_t
sort__srcline_cmp(struct hist_entry * left,struct hist_entry * right)286 sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right)
287 {
288 	if (!left->srcline) {
289 		if (!left->ms.map)
290 			left->srcline = SRCLINE_UNKNOWN;
291 		else {
292 			struct map *map = left->ms.map;
293 			left->srcline = get_srcline(map->dso,
294 					    map__rip_2objdump(map, left->ip));
295 		}
296 	}
297 	if (!right->srcline) {
298 		if (!right->ms.map)
299 			right->srcline = SRCLINE_UNKNOWN;
300 		else {
301 			struct map *map = right->ms.map;
302 			right->srcline = get_srcline(map->dso,
303 					    map__rip_2objdump(map, right->ip));
304 		}
305 	}
306 	return strcmp(right->srcline, left->srcline);
307 }
308 
hist_entry__srcline_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)309 static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf,
310 					size_t size, unsigned int width)
311 {
312 	return repsep_snprintf(bf, size, "%*.*-s", width, width, he->srcline);
313 }
314 
315 struct sort_entry sort_srcline = {
316 	.se_header	= "Source:Line",
317 	.se_cmp		= sort__srcline_cmp,
318 	.se_snprintf	= hist_entry__srcline_snprintf,
319 	.se_width_idx	= HISTC_SRCLINE,
320 };
321 
322 /* --sort parent */
323 
324 static int64_t
sort__parent_cmp(struct hist_entry * left,struct hist_entry * right)325 sort__parent_cmp(struct hist_entry *left, struct hist_entry *right)
326 {
327 	struct symbol *sym_l = left->parent;
328 	struct symbol *sym_r = right->parent;
329 
330 	if (!sym_l || !sym_r)
331 		return cmp_null(sym_l, sym_r);
332 
333 	return strcmp(sym_r->name, sym_l->name);
334 }
335 
hist_entry__parent_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)336 static int hist_entry__parent_snprintf(struct hist_entry *he, char *bf,
337 				       size_t size, unsigned int width)
338 {
339 	return repsep_snprintf(bf, size, "%-*.*s", width, width,
340 			      he->parent ? he->parent->name : "[other]");
341 }
342 
343 struct sort_entry sort_parent = {
344 	.se_header	= "Parent symbol",
345 	.se_cmp		= sort__parent_cmp,
346 	.se_snprintf	= hist_entry__parent_snprintf,
347 	.se_width_idx	= HISTC_PARENT,
348 };
349 
350 /* --sort cpu */
351 
352 static int64_t
sort__cpu_cmp(struct hist_entry * left,struct hist_entry * right)353 sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right)
354 {
355 	return right->cpu - left->cpu;
356 }
357 
hist_entry__cpu_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)358 static int hist_entry__cpu_snprintf(struct hist_entry *he, char *bf,
359 				    size_t size, unsigned int width)
360 {
361 	return repsep_snprintf(bf, size, "%*.*d", width, width, he->cpu);
362 }
363 
364 struct sort_entry sort_cpu = {
365 	.se_header      = "CPU",
366 	.se_cmp	        = sort__cpu_cmp,
367 	.se_snprintf    = hist_entry__cpu_snprintf,
368 	.se_width_idx	= HISTC_CPU,
369 };
370 
371 /* sort keys for branch stacks */
372 
373 static int64_t
sort__dso_from_cmp(struct hist_entry * left,struct hist_entry * right)374 sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right)
375 {
376 	if (!left->branch_info || !right->branch_info)
377 		return cmp_null(left->branch_info, right->branch_info);
378 
379 	return _sort__dso_cmp(left->branch_info->from.map,
380 			      right->branch_info->from.map);
381 }
382 
hist_entry__dso_from_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)383 static int hist_entry__dso_from_snprintf(struct hist_entry *he, char *bf,
384 				    size_t size, unsigned int width)
385 {
386 	if (he->branch_info)
387 		return _hist_entry__dso_snprintf(he->branch_info->from.map,
388 						 bf, size, width);
389 	else
390 		return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
391 }
392 
393 static int64_t
sort__dso_to_cmp(struct hist_entry * left,struct hist_entry * right)394 sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right)
395 {
396 	if (!left->branch_info || !right->branch_info)
397 		return cmp_null(left->branch_info, right->branch_info);
398 
399 	return _sort__dso_cmp(left->branch_info->to.map,
400 			      right->branch_info->to.map);
401 }
402 
hist_entry__dso_to_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)403 static int hist_entry__dso_to_snprintf(struct hist_entry *he, char *bf,
404 				       size_t size, unsigned int width)
405 {
406 	if (he->branch_info)
407 		return _hist_entry__dso_snprintf(he->branch_info->to.map,
408 						 bf, size, width);
409 	else
410 		return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
411 }
412 
413 static int64_t
sort__sym_from_cmp(struct hist_entry * left,struct hist_entry * right)414 sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right)
415 {
416 	struct addr_map_symbol *from_l = &left->branch_info->from;
417 	struct addr_map_symbol *from_r = &right->branch_info->from;
418 
419 	if (!left->branch_info || !right->branch_info)
420 		return cmp_null(left->branch_info, right->branch_info);
421 
422 	from_l = &left->branch_info->from;
423 	from_r = &right->branch_info->from;
424 
425 	if (!from_l->sym && !from_r->sym)
426 		return _sort__addr_cmp(from_l->addr, from_r->addr);
427 
428 	return _sort__sym_cmp(from_l->sym, from_r->sym);
429 }
430 
431 static int64_t
sort__sym_to_cmp(struct hist_entry * left,struct hist_entry * right)432 sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right)
433 {
434 	struct addr_map_symbol *to_l, *to_r;
435 
436 	if (!left->branch_info || !right->branch_info)
437 		return cmp_null(left->branch_info, right->branch_info);
438 
439 	to_l = &left->branch_info->to;
440 	to_r = &right->branch_info->to;
441 
442 	if (!to_l->sym && !to_r->sym)
443 		return _sort__addr_cmp(to_l->addr, to_r->addr);
444 
445 	return _sort__sym_cmp(to_l->sym, to_r->sym);
446 }
447 
hist_entry__sym_from_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)448 static int hist_entry__sym_from_snprintf(struct hist_entry *he, char *bf,
449 					 size_t size, unsigned int width)
450 {
451 	if (he->branch_info) {
452 		struct addr_map_symbol *from = &he->branch_info->from;
453 
454 		return _hist_entry__sym_snprintf(from->map, from->sym, from->addr,
455 						 he->level, bf, size, width);
456 	}
457 
458 	return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
459 }
460 
hist_entry__sym_to_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)461 static int hist_entry__sym_to_snprintf(struct hist_entry *he, char *bf,
462 				       size_t size, unsigned int width)
463 {
464 	if (he->branch_info) {
465 		struct addr_map_symbol *to = &he->branch_info->to;
466 
467 		return _hist_entry__sym_snprintf(to->map, to->sym, to->addr,
468 						 he->level, bf, size, width);
469 	}
470 
471 	return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
472 }
473 
474 struct sort_entry sort_dso_from = {
475 	.se_header	= "Source Shared Object",
476 	.se_cmp		= sort__dso_from_cmp,
477 	.se_snprintf	= hist_entry__dso_from_snprintf,
478 	.se_width_idx	= HISTC_DSO_FROM,
479 };
480 
481 struct sort_entry sort_dso_to = {
482 	.se_header	= "Target Shared Object",
483 	.se_cmp		= sort__dso_to_cmp,
484 	.se_snprintf	= hist_entry__dso_to_snprintf,
485 	.se_width_idx	= HISTC_DSO_TO,
486 };
487 
488 struct sort_entry sort_sym_from = {
489 	.se_header	= "Source Symbol",
490 	.se_cmp		= sort__sym_from_cmp,
491 	.se_snprintf	= hist_entry__sym_from_snprintf,
492 	.se_width_idx	= HISTC_SYMBOL_FROM,
493 };
494 
495 struct sort_entry sort_sym_to = {
496 	.se_header	= "Target Symbol",
497 	.se_cmp		= sort__sym_to_cmp,
498 	.se_snprintf	= hist_entry__sym_to_snprintf,
499 	.se_width_idx	= HISTC_SYMBOL_TO,
500 };
501 
502 static int64_t
sort__mispredict_cmp(struct hist_entry * left,struct hist_entry * right)503 sort__mispredict_cmp(struct hist_entry *left, struct hist_entry *right)
504 {
505 	unsigned char mp, p;
506 
507 	if (!left->branch_info || !right->branch_info)
508 		return cmp_null(left->branch_info, right->branch_info);
509 
510 	mp = left->branch_info->flags.mispred != right->branch_info->flags.mispred;
511 	p  = left->branch_info->flags.predicted != right->branch_info->flags.predicted;
512 	return mp || p;
513 }
514 
hist_entry__mispredict_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)515 static int hist_entry__mispredict_snprintf(struct hist_entry *he, char *bf,
516 				    size_t size, unsigned int width){
517 	static const char *out = "N/A";
518 
519 	if (he->branch_info) {
520 		if (he->branch_info->flags.predicted)
521 			out = "N";
522 		else if (he->branch_info->flags.mispred)
523 			out = "Y";
524 	}
525 
526 	return repsep_snprintf(bf, size, "%-*.*s", width, width, out);
527 }
528 
529 /* --sort daddr_sym */
530 static int64_t
sort__daddr_cmp(struct hist_entry * left,struct hist_entry * right)531 sort__daddr_cmp(struct hist_entry *left, struct hist_entry *right)
532 {
533 	uint64_t l = 0, r = 0;
534 
535 	if (left->mem_info)
536 		l = left->mem_info->daddr.addr;
537 	if (right->mem_info)
538 		r = right->mem_info->daddr.addr;
539 
540 	return (int64_t)(r - l);
541 }
542 
hist_entry__daddr_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)543 static int hist_entry__daddr_snprintf(struct hist_entry *he, char *bf,
544 				    size_t size, unsigned int width)
545 {
546 	uint64_t addr = 0;
547 	struct map *map = NULL;
548 	struct symbol *sym = NULL;
549 
550 	if (he->mem_info) {
551 		addr = he->mem_info->daddr.addr;
552 		map = he->mem_info->daddr.map;
553 		sym = he->mem_info->daddr.sym;
554 	}
555 	return _hist_entry__sym_snprintf(map, sym, addr, he->level, bf, size,
556 					 width);
557 }
558 
559 static int64_t
sort__dso_daddr_cmp(struct hist_entry * left,struct hist_entry * right)560 sort__dso_daddr_cmp(struct hist_entry *left, struct hist_entry *right)
561 {
562 	struct map *map_l = NULL;
563 	struct map *map_r = NULL;
564 
565 	if (left->mem_info)
566 		map_l = left->mem_info->daddr.map;
567 	if (right->mem_info)
568 		map_r = right->mem_info->daddr.map;
569 
570 	return _sort__dso_cmp(map_l, map_r);
571 }
572 
hist_entry__dso_daddr_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)573 static int hist_entry__dso_daddr_snprintf(struct hist_entry *he, char *bf,
574 				    size_t size, unsigned int width)
575 {
576 	struct map *map = NULL;
577 
578 	if (he->mem_info)
579 		map = he->mem_info->daddr.map;
580 
581 	return _hist_entry__dso_snprintf(map, bf, size, width);
582 }
583 
584 static int64_t
sort__locked_cmp(struct hist_entry * left,struct hist_entry * right)585 sort__locked_cmp(struct hist_entry *left, struct hist_entry *right)
586 {
587 	union perf_mem_data_src data_src_l;
588 	union perf_mem_data_src data_src_r;
589 
590 	if (left->mem_info)
591 		data_src_l = left->mem_info->data_src;
592 	else
593 		data_src_l.mem_lock = PERF_MEM_LOCK_NA;
594 
595 	if (right->mem_info)
596 		data_src_r = right->mem_info->data_src;
597 	else
598 		data_src_r.mem_lock = PERF_MEM_LOCK_NA;
599 
600 	return (int64_t)(data_src_r.mem_lock - data_src_l.mem_lock);
601 }
602 
hist_entry__locked_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)603 static int hist_entry__locked_snprintf(struct hist_entry *he, char *bf,
604 				    size_t size, unsigned int width)
605 {
606 	const char *out;
607 	u64 mask = PERF_MEM_LOCK_NA;
608 
609 	if (he->mem_info)
610 		mask = he->mem_info->data_src.mem_lock;
611 
612 	if (mask & PERF_MEM_LOCK_NA)
613 		out = "N/A";
614 	else if (mask & PERF_MEM_LOCK_LOCKED)
615 		out = "Yes";
616 	else
617 		out = "No";
618 
619 	return repsep_snprintf(bf, size, "%-*s", width, out);
620 }
621 
622 static int64_t
sort__tlb_cmp(struct hist_entry * left,struct hist_entry * right)623 sort__tlb_cmp(struct hist_entry *left, struct hist_entry *right)
624 {
625 	union perf_mem_data_src data_src_l;
626 	union perf_mem_data_src data_src_r;
627 
628 	if (left->mem_info)
629 		data_src_l = left->mem_info->data_src;
630 	else
631 		data_src_l.mem_dtlb = PERF_MEM_TLB_NA;
632 
633 	if (right->mem_info)
634 		data_src_r = right->mem_info->data_src;
635 	else
636 		data_src_r.mem_dtlb = PERF_MEM_TLB_NA;
637 
638 	return (int64_t)(data_src_r.mem_dtlb - data_src_l.mem_dtlb);
639 }
640 
641 static const char * const tlb_access[] = {
642 	"N/A",
643 	"HIT",
644 	"MISS",
645 	"L1",
646 	"L2",
647 	"Walker",
648 	"Fault",
649 };
650 #define NUM_TLB_ACCESS (sizeof(tlb_access)/sizeof(const char *))
651 
hist_entry__tlb_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)652 static int hist_entry__tlb_snprintf(struct hist_entry *he, char *bf,
653 				    size_t size, unsigned int width)
654 {
655 	char out[64];
656 	size_t sz = sizeof(out) - 1; /* -1 for null termination */
657 	size_t l = 0, i;
658 	u64 m = PERF_MEM_TLB_NA;
659 	u64 hit, miss;
660 
661 	out[0] = '\0';
662 
663 	if (he->mem_info)
664 		m = he->mem_info->data_src.mem_dtlb;
665 
666 	hit = m & PERF_MEM_TLB_HIT;
667 	miss = m & PERF_MEM_TLB_MISS;
668 
669 	/* already taken care of */
670 	m &= ~(PERF_MEM_TLB_HIT|PERF_MEM_TLB_MISS);
671 
672 	for (i = 0; m && i < NUM_TLB_ACCESS; i++, m >>= 1) {
673 		if (!(m & 0x1))
674 			continue;
675 		if (l) {
676 			strcat(out, " or ");
677 			l += 4;
678 		}
679 		strncat(out, tlb_access[i], sz - l);
680 		l += strlen(tlb_access[i]);
681 	}
682 	if (*out == '\0')
683 		strcpy(out, "N/A");
684 	if (hit)
685 		strncat(out, " hit", sz - l);
686 	if (miss)
687 		strncat(out, " miss", sz - l);
688 
689 	return repsep_snprintf(bf, size, "%-*s", width, out);
690 }
691 
692 static int64_t
sort__lvl_cmp(struct hist_entry * left,struct hist_entry * right)693 sort__lvl_cmp(struct hist_entry *left, struct hist_entry *right)
694 {
695 	union perf_mem_data_src data_src_l;
696 	union perf_mem_data_src data_src_r;
697 
698 	if (left->mem_info)
699 		data_src_l = left->mem_info->data_src;
700 	else
701 		data_src_l.mem_lvl = PERF_MEM_LVL_NA;
702 
703 	if (right->mem_info)
704 		data_src_r = right->mem_info->data_src;
705 	else
706 		data_src_r.mem_lvl = PERF_MEM_LVL_NA;
707 
708 	return (int64_t)(data_src_r.mem_lvl - data_src_l.mem_lvl);
709 }
710 
711 static const char * const mem_lvl[] = {
712 	"N/A",
713 	"HIT",
714 	"MISS",
715 	"L1",
716 	"LFB",
717 	"L2",
718 	"L3",
719 	"Local RAM",
720 	"Remote RAM (1 hop)",
721 	"Remote RAM (2 hops)",
722 	"Remote Cache (1 hop)",
723 	"Remote Cache (2 hops)",
724 	"I/O",
725 	"Uncached",
726 };
727 #define NUM_MEM_LVL (sizeof(mem_lvl)/sizeof(const char *))
728 
hist_entry__lvl_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)729 static int hist_entry__lvl_snprintf(struct hist_entry *he, char *bf,
730 				    size_t size, unsigned int width)
731 {
732 	char out[64];
733 	size_t sz = sizeof(out) - 1; /* -1 for null termination */
734 	size_t i, l = 0;
735 	u64 m =  PERF_MEM_LVL_NA;
736 	u64 hit, miss;
737 
738 	if (he->mem_info)
739 		m  = he->mem_info->data_src.mem_lvl;
740 
741 	out[0] = '\0';
742 
743 	hit = m & PERF_MEM_LVL_HIT;
744 	miss = m & PERF_MEM_LVL_MISS;
745 
746 	/* already taken care of */
747 	m &= ~(PERF_MEM_LVL_HIT|PERF_MEM_LVL_MISS);
748 
749 	for (i = 0; m && i < NUM_MEM_LVL; i++, m >>= 1) {
750 		if (!(m & 0x1))
751 			continue;
752 		if (l) {
753 			strcat(out, " or ");
754 			l += 4;
755 		}
756 		strncat(out, mem_lvl[i], sz - l);
757 		l += strlen(mem_lvl[i]);
758 	}
759 	if (*out == '\0')
760 		strcpy(out, "N/A");
761 	if (hit)
762 		strncat(out, " hit", sz - l);
763 	if (miss)
764 		strncat(out, " miss", sz - l);
765 
766 	return repsep_snprintf(bf, size, "%-*s", width, out);
767 }
768 
769 static int64_t
sort__snoop_cmp(struct hist_entry * left,struct hist_entry * right)770 sort__snoop_cmp(struct hist_entry *left, struct hist_entry *right)
771 {
772 	union perf_mem_data_src data_src_l;
773 	union perf_mem_data_src data_src_r;
774 
775 	if (left->mem_info)
776 		data_src_l = left->mem_info->data_src;
777 	else
778 		data_src_l.mem_snoop = PERF_MEM_SNOOP_NA;
779 
780 	if (right->mem_info)
781 		data_src_r = right->mem_info->data_src;
782 	else
783 		data_src_r.mem_snoop = PERF_MEM_SNOOP_NA;
784 
785 	return (int64_t)(data_src_r.mem_snoop - data_src_l.mem_snoop);
786 }
787 
788 static const char * const snoop_access[] = {
789 	"N/A",
790 	"None",
791 	"Miss",
792 	"Hit",
793 	"HitM",
794 };
795 #define NUM_SNOOP_ACCESS (sizeof(snoop_access)/sizeof(const char *))
796 
hist_entry__snoop_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)797 static int hist_entry__snoop_snprintf(struct hist_entry *he, char *bf,
798 				    size_t size, unsigned int width)
799 {
800 	char out[64];
801 	size_t sz = sizeof(out) - 1; /* -1 for null termination */
802 	size_t i, l = 0;
803 	u64 m = PERF_MEM_SNOOP_NA;
804 
805 	out[0] = '\0';
806 
807 	if (he->mem_info)
808 		m = he->mem_info->data_src.mem_snoop;
809 
810 	for (i = 0; m && i < NUM_SNOOP_ACCESS; i++, m >>= 1) {
811 		if (!(m & 0x1))
812 			continue;
813 		if (l) {
814 			strcat(out, " or ");
815 			l += 4;
816 		}
817 		strncat(out, snoop_access[i], sz - l);
818 		l += strlen(snoop_access[i]);
819 	}
820 
821 	if (*out == '\0')
822 		strcpy(out, "N/A");
823 
824 	return repsep_snprintf(bf, size, "%-*s", width, out);
825 }
826 
cl_address(u64 address)827 static inline  u64 cl_address(u64 address)
828 {
829 	/* return the cacheline of the address */
830 	return (address & ~(cacheline_size - 1));
831 }
832 
833 static int64_t
sort__dcacheline_cmp(struct hist_entry * left,struct hist_entry * right)834 sort__dcacheline_cmp(struct hist_entry *left, struct hist_entry *right)
835 {
836 	u64 l, r;
837 	struct map *l_map, *r_map;
838 
839 	if (!left->mem_info)  return -1;
840 	if (!right->mem_info) return 1;
841 
842 	/* group event types together */
843 	if (left->cpumode > right->cpumode) return -1;
844 	if (left->cpumode < right->cpumode) return 1;
845 
846 	l_map = left->mem_info->daddr.map;
847 	r_map = right->mem_info->daddr.map;
848 
849 	/* if both are NULL, jump to sort on al_addr instead */
850 	if (!l_map && !r_map)
851 		goto addr;
852 
853 	if (!l_map) return -1;
854 	if (!r_map) return 1;
855 
856 	if (l_map->maj > r_map->maj) return -1;
857 	if (l_map->maj < r_map->maj) return 1;
858 
859 	if (l_map->min > r_map->min) return -1;
860 	if (l_map->min < r_map->min) return 1;
861 
862 	if (l_map->ino > r_map->ino) return -1;
863 	if (l_map->ino < r_map->ino) return 1;
864 
865 	if (l_map->ino_generation > r_map->ino_generation) return -1;
866 	if (l_map->ino_generation < r_map->ino_generation) return 1;
867 
868 	/*
869 	 * Addresses with no major/minor numbers are assumed to be
870 	 * anonymous in userspace.  Sort those on pid then address.
871 	 *
872 	 * The kernel and non-zero major/minor mapped areas are
873 	 * assumed to be unity mapped.  Sort those on address.
874 	 */
875 
876 	if ((left->cpumode != PERF_RECORD_MISC_KERNEL) &&
877 	    (!(l_map->flags & MAP_SHARED)) &&
878 	    !l_map->maj && !l_map->min && !l_map->ino &&
879 	    !l_map->ino_generation) {
880 		/* userspace anonymous */
881 
882 		if (left->thread->pid_ > right->thread->pid_) return -1;
883 		if (left->thread->pid_ < right->thread->pid_) return 1;
884 	}
885 
886 addr:
887 	/* al_addr does all the right addr - start + offset calculations */
888 	l = cl_address(left->mem_info->daddr.al_addr);
889 	r = cl_address(right->mem_info->daddr.al_addr);
890 
891 	if (l > r) return -1;
892 	if (l < r) return 1;
893 
894 	return 0;
895 }
896 
hist_entry__dcacheline_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)897 static int hist_entry__dcacheline_snprintf(struct hist_entry *he, char *bf,
898 					  size_t size, unsigned int width)
899 {
900 
901 	uint64_t addr = 0;
902 	struct map *map = NULL;
903 	struct symbol *sym = NULL;
904 	char level = he->level;
905 
906 	if (he->mem_info) {
907 		addr = cl_address(he->mem_info->daddr.al_addr);
908 		map = he->mem_info->daddr.map;
909 		sym = he->mem_info->daddr.sym;
910 
911 		/* print [s] for shared data mmaps */
912 		if ((he->cpumode != PERF_RECORD_MISC_KERNEL) &&
913 		     map && (map->type == MAP__VARIABLE) &&
914 		    (map->flags & MAP_SHARED) &&
915 		    (map->maj || map->min || map->ino ||
916 		     map->ino_generation))
917 			level = 's';
918 		else if (!map)
919 			level = 'X';
920 	}
921 	return _hist_entry__sym_snprintf(map, sym, addr, level, bf, size,
922 					 width);
923 }
924 
925 struct sort_entry sort_mispredict = {
926 	.se_header	= "Branch Mispredicted",
927 	.se_cmp		= sort__mispredict_cmp,
928 	.se_snprintf	= hist_entry__mispredict_snprintf,
929 	.se_width_idx	= HISTC_MISPREDICT,
930 };
931 
he_weight(struct hist_entry * he)932 static u64 he_weight(struct hist_entry *he)
933 {
934 	return he->stat.nr_events ? he->stat.weight / he->stat.nr_events : 0;
935 }
936 
937 static int64_t
sort__local_weight_cmp(struct hist_entry * left,struct hist_entry * right)938 sort__local_weight_cmp(struct hist_entry *left, struct hist_entry *right)
939 {
940 	return he_weight(left) - he_weight(right);
941 }
942 
hist_entry__local_weight_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)943 static int hist_entry__local_weight_snprintf(struct hist_entry *he, char *bf,
944 				    size_t size, unsigned int width)
945 {
946 	return repsep_snprintf(bf, size, "%-*llu", width, he_weight(he));
947 }
948 
949 struct sort_entry sort_local_weight = {
950 	.se_header	= "Local Weight",
951 	.se_cmp		= sort__local_weight_cmp,
952 	.se_snprintf	= hist_entry__local_weight_snprintf,
953 	.se_width_idx	= HISTC_LOCAL_WEIGHT,
954 };
955 
956 static int64_t
sort__global_weight_cmp(struct hist_entry * left,struct hist_entry * right)957 sort__global_weight_cmp(struct hist_entry *left, struct hist_entry *right)
958 {
959 	return left->stat.weight - right->stat.weight;
960 }
961 
hist_entry__global_weight_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)962 static int hist_entry__global_weight_snprintf(struct hist_entry *he, char *bf,
963 					      size_t size, unsigned int width)
964 {
965 	return repsep_snprintf(bf, size, "%-*llu", width, he->stat.weight);
966 }
967 
968 struct sort_entry sort_global_weight = {
969 	.se_header	= "Weight",
970 	.se_cmp		= sort__global_weight_cmp,
971 	.se_snprintf	= hist_entry__global_weight_snprintf,
972 	.se_width_idx	= HISTC_GLOBAL_WEIGHT,
973 };
974 
975 struct sort_entry sort_mem_daddr_sym = {
976 	.se_header	= "Data Symbol",
977 	.se_cmp		= sort__daddr_cmp,
978 	.se_snprintf	= hist_entry__daddr_snprintf,
979 	.se_width_idx	= HISTC_MEM_DADDR_SYMBOL,
980 };
981 
982 struct sort_entry sort_mem_daddr_dso = {
983 	.se_header	= "Data Object",
984 	.se_cmp		= sort__dso_daddr_cmp,
985 	.se_snprintf	= hist_entry__dso_daddr_snprintf,
986 	.se_width_idx	= HISTC_MEM_DADDR_SYMBOL,
987 };
988 
989 struct sort_entry sort_mem_locked = {
990 	.se_header	= "Locked",
991 	.se_cmp		= sort__locked_cmp,
992 	.se_snprintf	= hist_entry__locked_snprintf,
993 	.se_width_idx	= HISTC_MEM_LOCKED,
994 };
995 
996 struct sort_entry sort_mem_tlb = {
997 	.se_header	= "TLB access",
998 	.se_cmp		= sort__tlb_cmp,
999 	.se_snprintf	= hist_entry__tlb_snprintf,
1000 	.se_width_idx	= HISTC_MEM_TLB,
1001 };
1002 
1003 struct sort_entry sort_mem_lvl = {
1004 	.se_header	= "Memory access",
1005 	.se_cmp		= sort__lvl_cmp,
1006 	.se_snprintf	= hist_entry__lvl_snprintf,
1007 	.se_width_idx	= HISTC_MEM_LVL,
1008 };
1009 
1010 struct sort_entry sort_mem_snoop = {
1011 	.se_header	= "Snoop",
1012 	.se_cmp		= sort__snoop_cmp,
1013 	.se_snprintf	= hist_entry__snoop_snprintf,
1014 	.se_width_idx	= HISTC_MEM_SNOOP,
1015 };
1016 
1017 struct sort_entry sort_mem_dcacheline = {
1018 	.se_header	= "Data Cacheline",
1019 	.se_cmp		= sort__dcacheline_cmp,
1020 	.se_snprintf	= hist_entry__dcacheline_snprintf,
1021 	.se_width_idx	= HISTC_MEM_DCACHELINE,
1022 };
1023 
1024 static int64_t
sort__abort_cmp(struct hist_entry * left,struct hist_entry * right)1025 sort__abort_cmp(struct hist_entry *left, struct hist_entry *right)
1026 {
1027 	if (!left->branch_info || !right->branch_info)
1028 		return cmp_null(left->branch_info, right->branch_info);
1029 
1030 	return left->branch_info->flags.abort !=
1031 		right->branch_info->flags.abort;
1032 }
1033 
hist_entry__abort_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)1034 static int hist_entry__abort_snprintf(struct hist_entry *he, char *bf,
1035 				    size_t size, unsigned int width)
1036 {
1037 	static const char *out = "N/A";
1038 
1039 	if (he->branch_info) {
1040 		if (he->branch_info->flags.abort)
1041 			out = "A";
1042 		else
1043 			out = ".";
1044 	}
1045 
1046 	return repsep_snprintf(bf, size, "%-*s", width, out);
1047 }
1048 
1049 struct sort_entry sort_abort = {
1050 	.se_header	= "Transaction abort",
1051 	.se_cmp		= sort__abort_cmp,
1052 	.se_snprintf	= hist_entry__abort_snprintf,
1053 	.se_width_idx	= HISTC_ABORT,
1054 };
1055 
1056 static int64_t
sort__in_tx_cmp(struct hist_entry * left,struct hist_entry * right)1057 sort__in_tx_cmp(struct hist_entry *left, struct hist_entry *right)
1058 {
1059 	if (!left->branch_info || !right->branch_info)
1060 		return cmp_null(left->branch_info, right->branch_info);
1061 
1062 	return left->branch_info->flags.in_tx !=
1063 		right->branch_info->flags.in_tx;
1064 }
1065 
hist_entry__in_tx_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)1066 static int hist_entry__in_tx_snprintf(struct hist_entry *he, char *bf,
1067 				    size_t size, unsigned int width)
1068 {
1069 	static const char *out = "N/A";
1070 
1071 	if (he->branch_info) {
1072 		if (he->branch_info->flags.in_tx)
1073 			out = "T";
1074 		else
1075 			out = ".";
1076 	}
1077 
1078 	return repsep_snprintf(bf, size, "%-*s", width, out);
1079 }
1080 
1081 struct sort_entry sort_in_tx = {
1082 	.se_header	= "Branch in transaction",
1083 	.se_cmp		= sort__in_tx_cmp,
1084 	.se_snprintf	= hist_entry__in_tx_snprintf,
1085 	.se_width_idx	= HISTC_IN_TX,
1086 };
1087 
1088 static int64_t
sort__transaction_cmp(struct hist_entry * left,struct hist_entry * right)1089 sort__transaction_cmp(struct hist_entry *left, struct hist_entry *right)
1090 {
1091 	return left->transaction - right->transaction;
1092 }
1093 
add_str(char * p,const char * str)1094 static inline char *add_str(char *p, const char *str)
1095 {
1096 	strcpy(p, str);
1097 	return p + strlen(str);
1098 }
1099 
1100 static struct txbit {
1101 	unsigned flag;
1102 	const char *name;
1103 	int skip_for_len;
1104 } txbits[] = {
1105 	{ PERF_TXN_ELISION,        "EL ",        0 },
1106 	{ PERF_TXN_TRANSACTION,    "TX ",        1 },
1107 	{ PERF_TXN_SYNC,           "SYNC ",      1 },
1108 	{ PERF_TXN_ASYNC,          "ASYNC ",     0 },
1109 	{ PERF_TXN_RETRY,          "RETRY ",     0 },
1110 	{ PERF_TXN_CONFLICT,       "CON ",       0 },
1111 	{ PERF_TXN_CAPACITY_WRITE, "CAP-WRITE ", 1 },
1112 	{ PERF_TXN_CAPACITY_READ,  "CAP-READ ",  0 },
1113 	{ 0, NULL, 0 }
1114 };
1115 
hist_entry__transaction_len(void)1116 int hist_entry__transaction_len(void)
1117 {
1118 	int i;
1119 	int len = 0;
1120 
1121 	for (i = 0; txbits[i].name; i++) {
1122 		if (!txbits[i].skip_for_len)
1123 			len += strlen(txbits[i].name);
1124 	}
1125 	len += 4; /* :XX<space> */
1126 	return len;
1127 }
1128 
hist_entry__transaction_snprintf(struct hist_entry * he,char * bf,size_t size,unsigned int width)1129 static int hist_entry__transaction_snprintf(struct hist_entry *he, char *bf,
1130 					    size_t size, unsigned int width)
1131 {
1132 	u64 t = he->transaction;
1133 	char buf[128];
1134 	char *p = buf;
1135 	int i;
1136 
1137 	buf[0] = 0;
1138 	for (i = 0; txbits[i].name; i++)
1139 		if (txbits[i].flag & t)
1140 			p = add_str(p, txbits[i].name);
1141 	if (t && !(t & (PERF_TXN_SYNC|PERF_TXN_ASYNC)))
1142 		p = add_str(p, "NEITHER ");
1143 	if (t & PERF_TXN_ABORT_MASK) {
1144 		sprintf(p, ":%" PRIx64,
1145 			(t & PERF_TXN_ABORT_MASK) >>
1146 			PERF_TXN_ABORT_SHIFT);
1147 		p += strlen(p);
1148 	}
1149 
1150 	return repsep_snprintf(bf, size, "%-*s", width, buf);
1151 }
1152 
1153 struct sort_entry sort_transaction = {
1154 	.se_header	= "Transaction                ",
1155 	.se_cmp		= sort__transaction_cmp,
1156 	.se_snprintf	= hist_entry__transaction_snprintf,
1157 	.se_width_idx	= HISTC_TRANSACTION,
1158 };
1159 
1160 struct sort_dimension {
1161 	const char		*name;
1162 	struct sort_entry	*entry;
1163 	int			taken;
1164 };
1165 
1166 #define DIM(d, n, func) [d] = { .name = n, .entry = &(func) }
1167 
1168 static struct sort_dimension common_sort_dimensions[] = {
1169 	DIM(SORT_PID, "pid", sort_thread),
1170 	DIM(SORT_COMM, "comm", sort_comm),
1171 	DIM(SORT_DSO, "dso", sort_dso),
1172 	DIM(SORT_SYM, "symbol", sort_sym),
1173 	DIM(SORT_PARENT, "parent", sort_parent),
1174 	DIM(SORT_CPU, "cpu", sort_cpu),
1175 	DIM(SORT_SRCLINE, "srcline", sort_srcline),
1176 	DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight),
1177 	DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight),
1178 	DIM(SORT_TRANSACTION, "transaction", sort_transaction),
1179 };
1180 
1181 #undef DIM
1182 
1183 #define DIM(d, n, func) [d - __SORT_BRANCH_STACK] = { .name = n, .entry = &(func) }
1184 
1185 static struct sort_dimension bstack_sort_dimensions[] = {
1186 	DIM(SORT_DSO_FROM, "dso_from", sort_dso_from),
1187 	DIM(SORT_DSO_TO, "dso_to", sort_dso_to),
1188 	DIM(SORT_SYM_FROM, "symbol_from", sort_sym_from),
1189 	DIM(SORT_SYM_TO, "symbol_to", sort_sym_to),
1190 	DIM(SORT_MISPREDICT, "mispredict", sort_mispredict),
1191 	DIM(SORT_IN_TX, "in_tx", sort_in_tx),
1192 	DIM(SORT_ABORT, "abort", sort_abort),
1193 };
1194 
1195 #undef DIM
1196 
1197 #define DIM(d, n, func) [d - __SORT_MEMORY_MODE] = { .name = n, .entry = &(func) }
1198 
1199 static struct sort_dimension memory_sort_dimensions[] = {
1200 	DIM(SORT_MEM_DADDR_SYMBOL, "symbol_daddr", sort_mem_daddr_sym),
1201 	DIM(SORT_MEM_DADDR_DSO, "dso_daddr", sort_mem_daddr_dso),
1202 	DIM(SORT_MEM_LOCKED, "locked", sort_mem_locked),
1203 	DIM(SORT_MEM_TLB, "tlb", sort_mem_tlb),
1204 	DIM(SORT_MEM_LVL, "mem", sort_mem_lvl),
1205 	DIM(SORT_MEM_SNOOP, "snoop", sort_mem_snoop),
1206 	DIM(SORT_MEM_DCACHELINE, "dcacheline", sort_mem_dcacheline),
1207 };
1208 
1209 #undef DIM
1210 
1211 struct hpp_dimension {
1212 	const char		*name;
1213 	struct perf_hpp_fmt	*fmt;
1214 	int			taken;
1215 };
1216 
1217 #define DIM(d, n) { .name = n, .fmt = &perf_hpp__format[d], }
1218 
1219 static struct hpp_dimension hpp_sort_dimensions[] = {
1220 	DIM(PERF_HPP__OVERHEAD, "overhead"),
1221 	DIM(PERF_HPP__OVERHEAD_SYS, "overhead_sys"),
1222 	DIM(PERF_HPP__OVERHEAD_US, "overhead_us"),
1223 	DIM(PERF_HPP__OVERHEAD_GUEST_SYS, "overhead_guest_sys"),
1224 	DIM(PERF_HPP__OVERHEAD_GUEST_US, "overhead_guest_us"),
1225 	DIM(PERF_HPP__OVERHEAD_ACC, "overhead_children"),
1226 	DIM(PERF_HPP__SAMPLES, "sample"),
1227 	DIM(PERF_HPP__PERIOD, "period"),
1228 };
1229 
1230 #undef DIM
1231 
1232 struct hpp_sort_entry {
1233 	struct perf_hpp_fmt hpp;
1234 	struct sort_entry *se;
1235 };
1236 
perf_hpp__same_sort_entry(struct perf_hpp_fmt * a,struct perf_hpp_fmt * b)1237 bool perf_hpp__same_sort_entry(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)
1238 {
1239 	struct hpp_sort_entry *hse_a;
1240 	struct hpp_sort_entry *hse_b;
1241 
1242 	if (!perf_hpp__is_sort_entry(a) || !perf_hpp__is_sort_entry(b))
1243 		return false;
1244 
1245 	hse_a = container_of(a, struct hpp_sort_entry, hpp);
1246 	hse_b = container_of(b, struct hpp_sort_entry, hpp);
1247 
1248 	return hse_a->se == hse_b->se;
1249 }
1250 
perf_hpp__reset_sort_width(struct perf_hpp_fmt * fmt,struct hists * hists)1251 void perf_hpp__reset_sort_width(struct perf_hpp_fmt *fmt, struct hists *hists)
1252 {
1253 	struct hpp_sort_entry *hse;
1254 
1255 	if (!perf_hpp__is_sort_entry(fmt))
1256 		return;
1257 
1258 	hse = container_of(fmt, struct hpp_sort_entry, hpp);
1259 	hists__new_col_len(hists, hse->se->se_width_idx, strlen(fmt->name));
1260 }
1261 
__sort__hpp_header(struct perf_hpp_fmt * fmt,struct perf_hpp * hpp,struct perf_evsel * evsel)1262 static int __sort__hpp_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1263 			      struct perf_evsel *evsel)
1264 {
1265 	struct hpp_sort_entry *hse;
1266 	size_t len = fmt->user_len;
1267 
1268 	hse = container_of(fmt, struct hpp_sort_entry, hpp);
1269 
1270 	if (!len)
1271 		len = hists__col_len(evsel__hists(evsel), hse->se->se_width_idx);
1272 
1273 	return scnprintf(hpp->buf, hpp->size, "%-*.*s", len, len, fmt->name);
1274 }
1275 
__sort__hpp_width(struct perf_hpp_fmt * fmt,struct perf_hpp * hpp __maybe_unused,struct perf_evsel * evsel)1276 static int __sort__hpp_width(struct perf_hpp_fmt *fmt,
1277 			     struct perf_hpp *hpp __maybe_unused,
1278 			     struct perf_evsel *evsel)
1279 {
1280 	struct hpp_sort_entry *hse;
1281 	size_t len = fmt->user_len;
1282 
1283 	hse = container_of(fmt, struct hpp_sort_entry, hpp);
1284 
1285 	if (!len)
1286 		len = hists__col_len(evsel__hists(evsel), hse->se->se_width_idx);
1287 
1288 	return len;
1289 }
1290 
__sort__hpp_entry(struct perf_hpp_fmt * fmt,struct perf_hpp * hpp,struct hist_entry * he)1291 static int __sort__hpp_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1292 			     struct hist_entry *he)
1293 {
1294 	struct hpp_sort_entry *hse;
1295 	size_t len = fmt->user_len;
1296 
1297 	hse = container_of(fmt, struct hpp_sort_entry, hpp);
1298 
1299 	if (!len)
1300 		len = hists__col_len(he->hists, hse->se->se_width_idx);
1301 
1302 	return hse->se->se_snprintf(he, hpp->buf, hpp->size, len);
1303 }
1304 
1305 static struct hpp_sort_entry *
__sort_dimension__alloc_hpp(struct sort_dimension * sd)1306 __sort_dimension__alloc_hpp(struct sort_dimension *sd)
1307 {
1308 	struct hpp_sort_entry *hse;
1309 
1310 	hse = malloc(sizeof(*hse));
1311 	if (hse == NULL) {
1312 		pr_err("Memory allocation failed\n");
1313 		return NULL;
1314 	}
1315 
1316 	hse->se = sd->entry;
1317 	hse->hpp.name = sd->entry->se_header;
1318 	hse->hpp.header = __sort__hpp_header;
1319 	hse->hpp.width = __sort__hpp_width;
1320 	hse->hpp.entry = __sort__hpp_entry;
1321 	hse->hpp.color = NULL;
1322 
1323 	hse->hpp.cmp = sd->entry->se_cmp;
1324 	hse->hpp.collapse = sd->entry->se_collapse ? : sd->entry->se_cmp;
1325 	hse->hpp.sort = sd->entry->se_sort ? : hse->hpp.collapse;
1326 
1327 	INIT_LIST_HEAD(&hse->hpp.list);
1328 	INIT_LIST_HEAD(&hse->hpp.sort_list);
1329 	hse->hpp.elide = false;
1330 	hse->hpp.len = 0;
1331 	hse->hpp.user_len = 0;
1332 
1333 	return hse;
1334 }
1335 
perf_hpp__is_sort_entry(struct perf_hpp_fmt * format)1336 bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format)
1337 {
1338 	return format->header == __sort__hpp_header;
1339 }
1340 
__sort_dimension__add_hpp_sort(struct sort_dimension * sd)1341 static int __sort_dimension__add_hpp_sort(struct sort_dimension *sd)
1342 {
1343 	struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd);
1344 
1345 	if (hse == NULL)
1346 		return -1;
1347 
1348 	perf_hpp__register_sort_field(&hse->hpp);
1349 	return 0;
1350 }
1351 
__sort_dimension__add_hpp_output(struct sort_dimension * sd)1352 static int __sort_dimension__add_hpp_output(struct sort_dimension *sd)
1353 {
1354 	struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd);
1355 
1356 	if (hse == NULL)
1357 		return -1;
1358 
1359 	perf_hpp__column_register(&hse->hpp);
1360 	return 0;
1361 }
1362 
__sort_dimension__add(struct sort_dimension * sd)1363 static int __sort_dimension__add(struct sort_dimension *sd)
1364 {
1365 	if (sd->taken)
1366 		return 0;
1367 
1368 	if (__sort_dimension__add_hpp_sort(sd) < 0)
1369 		return -1;
1370 
1371 	if (sd->entry->se_collapse)
1372 		sort__need_collapse = 1;
1373 
1374 	sd->taken = 1;
1375 
1376 	return 0;
1377 }
1378 
__hpp_dimension__add(struct hpp_dimension * hd)1379 static int __hpp_dimension__add(struct hpp_dimension *hd)
1380 {
1381 	if (!hd->taken) {
1382 		hd->taken = 1;
1383 
1384 		perf_hpp__register_sort_field(hd->fmt);
1385 	}
1386 	return 0;
1387 }
1388 
__sort_dimension__add_output(struct sort_dimension * sd)1389 static int __sort_dimension__add_output(struct sort_dimension *sd)
1390 {
1391 	if (sd->taken)
1392 		return 0;
1393 
1394 	if (__sort_dimension__add_hpp_output(sd) < 0)
1395 		return -1;
1396 
1397 	sd->taken = 1;
1398 	return 0;
1399 }
1400 
__hpp_dimension__add_output(struct hpp_dimension * hd)1401 static int __hpp_dimension__add_output(struct hpp_dimension *hd)
1402 {
1403 	if (!hd->taken) {
1404 		hd->taken = 1;
1405 
1406 		perf_hpp__column_register(hd->fmt);
1407 	}
1408 	return 0;
1409 }
1410 
sort_dimension__add(const char * tok)1411 int sort_dimension__add(const char *tok)
1412 {
1413 	unsigned int i;
1414 
1415 	for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) {
1416 		struct sort_dimension *sd = &common_sort_dimensions[i];
1417 
1418 		if (strncasecmp(tok, sd->name, strlen(tok)))
1419 			continue;
1420 
1421 		if (sd->entry == &sort_parent) {
1422 			int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED);
1423 			if (ret) {
1424 				char err[BUFSIZ];
1425 
1426 				regerror(ret, &parent_regex, err, sizeof(err));
1427 				pr_err("Invalid regex: %s\n%s", parent_pattern, err);
1428 				return -EINVAL;
1429 			}
1430 			sort__has_parent = 1;
1431 		} else if (sd->entry == &sort_sym) {
1432 			sort__has_sym = 1;
1433 		} else if (sd->entry == &sort_dso) {
1434 			sort__has_dso = 1;
1435 		}
1436 
1437 		return __sort_dimension__add(sd);
1438 	}
1439 
1440 	for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) {
1441 		struct hpp_dimension *hd = &hpp_sort_dimensions[i];
1442 
1443 		if (strncasecmp(tok, hd->name, strlen(tok)))
1444 			continue;
1445 
1446 		return __hpp_dimension__add(hd);
1447 	}
1448 
1449 	for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) {
1450 		struct sort_dimension *sd = &bstack_sort_dimensions[i];
1451 
1452 		if (strncasecmp(tok, sd->name, strlen(tok)))
1453 			continue;
1454 
1455 		if (sort__mode != SORT_MODE__BRANCH)
1456 			return -EINVAL;
1457 
1458 		if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to)
1459 			sort__has_sym = 1;
1460 
1461 		__sort_dimension__add(sd);
1462 		return 0;
1463 	}
1464 
1465 	for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) {
1466 		struct sort_dimension *sd = &memory_sort_dimensions[i];
1467 
1468 		if (strncasecmp(tok, sd->name, strlen(tok)))
1469 			continue;
1470 
1471 		if (sort__mode != SORT_MODE__MEMORY)
1472 			return -EINVAL;
1473 
1474 		if (sd->entry == &sort_mem_daddr_sym)
1475 			sort__has_sym = 1;
1476 
1477 		__sort_dimension__add(sd);
1478 		return 0;
1479 	}
1480 
1481 	return -ESRCH;
1482 }
1483 
get_default_sort_order(void)1484 static const char *get_default_sort_order(void)
1485 {
1486 	const char *default_sort_orders[] = {
1487 		default_sort_order,
1488 		default_branch_sort_order,
1489 		default_mem_sort_order,
1490 		default_top_sort_order,
1491 		default_diff_sort_order,
1492 	};
1493 
1494 	BUG_ON(sort__mode >= ARRAY_SIZE(default_sort_orders));
1495 
1496 	return default_sort_orders[sort__mode];
1497 }
1498 
setup_sort_order(void)1499 static int setup_sort_order(void)
1500 {
1501 	char *new_sort_order;
1502 
1503 	/*
1504 	 * Append '+'-prefixed sort order to the default sort
1505 	 * order string.
1506 	 */
1507 	if (!sort_order || is_strict_order(sort_order))
1508 		return 0;
1509 
1510 	if (sort_order[1] == '\0') {
1511 		error("Invalid --sort key: `+'");
1512 		return -EINVAL;
1513 	}
1514 
1515 	/*
1516 	 * We allocate new sort_order string, but we never free it,
1517 	 * because it's checked over the rest of the code.
1518 	 */
1519 	if (asprintf(&new_sort_order, "%s,%s",
1520 		     get_default_sort_order(), sort_order + 1) < 0) {
1521 		error("Not enough memory to set up --sort");
1522 		return -ENOMEM;
1523 	}
1524 
1525 	sort_order = new_sort_order;
1526 	return 0;
1527 }
1528 
__setup_sorting(void)1529 static int __setup_sorting(void)
1530 {
1531 	char *tmp, *tok, *str;
1532 	const char *sort_keys;
1533 	int ret = 0;
1534 
1535 	ret = setup_sort_order();
1536 	if (ret)
1537 		return ret;
1538 
1539 	sort_keys = sort_order;
1540 	if (sort_keys == NULL) {
1541 		if (is_strict_order(field_order)) {
1542 			/*
1543 			 * If user specified field order but no sort order,
1544 			 * we'll honor it and not add default sort orders.
1545 			 */
1546 			return 0;
1547 		}
1548 
1549 		sort_keys = get_default_sort_order();
1550 	}
1551 
1552 	str = strdup(sort_keys);
1553 	if (str == NULL) {
1554 		error("Not enough memory to setup sort keys");
1555 		return -ENOMEM;
1556 	}
1557 
1558 	for (tok = strtok_r(str, ", ", &tmp);
1559 			tok; tok = strtok_r(NULL, ", ", &tmp)) {
1560 		ret = sort_dimension__add(tok);
1561 		if (ret == -EINVAL) {
1562 			error("Invalid --sort key: `%s'", tok);
1563 			break;
1564 		} else if (ret == -ESRCH) {
1565 			error("Unknown --sort key: `%s'", tok);
1566 			break;
1567 		}
1568 	}
1569 
1570 	free(str);
1571 	return ret;
1572 }
1573 
perf_hpp__set_elide(int idx,bool elide)1574 void perf_hpp__set_elide(int idx, bool elide)
1575 {
1576 	struct perf_hpp_fmt *fmt;
1577 	struct hpp_sort_entry *hse;
1578 
1579 	perf_hpp__for_each_format(fmt) {
1580 		if (!perf_hpp__is_sort_entry(fmt))
1581 			continue;
1582 
1583 		hse = container_of(fmt, struct hpp_sort_entry, hpp);
1584 		if (hse->se->se_width_idx == idx) {
1585 			fmt->elide = elide;
1586 			break;
1587 		}
1588 	}
1589 }
1590 
__get_elide(struct strlist * list,const char * list_name,FILE * fp)1591 static bool __get_elide(struct strlist *list, const char *list_name, FILE *fp)
1592 {
1593 	if (list && strlist__nr_entries(list) == 1) {
1594 		if (fp != NULL)
1595 			fprintf(fp, "# %s: %s\n", list_name,
1596 				strlist__entry(list, 0)->s);
1597 		return true;
1598 	}
1599 	return false;
1600 }
1601 
get_elide(int idx,FILE * output)1602 static bool get_elide(int idx, FILE *output)
1603 {
1604 	switch (idx) {
1605 	case HISTC_SYMBOL:
1606 		return __get_elide(symbol_conf.sym_list, "symbol", output);
1607 	case HISTC_DSO:
1608 		return __get_elide(symbol_conf.dso_list, "dso", output);
1609 	case HISTC_COMM:
1610 		return __get_elide(symbol_conf.comm_list, "comm", output);
1611 	default:
1612 		break;
1613 	}
1614 
1615 	if (sort__mode != SORT_MODE__BRANCH)
1616 		return false;
1617 
1618 	switch (idx) {
1619 	case HISTC_SYMBOL_FROM:
1620 		return __get_elide(symbol_conf.sym_from_list, "sym_from", output);
1621 	case HISTC_SYMBOL_TO:
1622 		return __get_elide(symbol_conf.sym_to_list, "sym_to", output);
1623 	case HISTC_DSO_FROM:
1624 		return __get_elide(symbol_conf.dso_from_list, "dso_from", output);
1625 	case HISTC_DSO_TO:
1626 		return __get_elide(symbol_conf.dso_to_list, "dso_to", output);
1627 	default:
1628 		break;
1629 	}
1630 
1631 	return false;
1632 }
1633 
sort__setup_elide(FILE * output)1634 void sort__setup_elide(FILE *output)
1635 {
1636 	struct perf_hpp_fmt *fmt;
1637 	struct hpp_sort_entry *hse;
1638 
1639 	perf_hpp__for_each_format(fmt) {
1640 		if (!perf_hpp__is_sort_entry(fmt))
1641 			continue;
1642 
1643 		hse = container_of(fmt, struct hpp_sort_entry, hpp);
1644 		fmt->elide = get_elide(hse->se->se_width_idx, output);
1645 	}
1646 
1647 	/*
1648 	 * It makes no sense to elide all of sort entries.
1649 	 * Just revert them to show up again.
1650 	 */
1651 	perf_hpp__for_each_format(fmt) {
1652 		if (!perf_hpp__is_sort_entry(fmt))
1653 			continue;
1654 
1655 		if (!fmt->elide)
1656 			return;
1657 	}
1658 
1659 	perf_hpp__for_each_format(fmt) {
1660 		if (!perf_hpp__is_sort_entry(fmt))
1661 			continue;
1662 
1663 		fmt->elide = false;
1664 	}
1665 }
1666 
output_field_add(char * tok)1667 static int output_field_add(char *tok)
1668 {
1669 	unsigned int i;
1670 
1671 	for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) {
1672 		struct sort_dimension *sd = &common_sort_dimensions[i];
1673 
1674 		if (strncasecmp(tok, sd->name, strlen(tok)))
1675 			continue;
1676 
1677 		return __sort_dimension__add_output(sd);
1678 	}
1679 
1680 	for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) {
1681 		struct hpp_dimension *hd = &hpp_sort_dimensions[i];
1682 
1683 		if (strncasecmp(tok, hd->name, strlen(tok)))
1684 			continue;
1685 
1686 		return __hpp_dimension__add_output(hd);
1687 	}
1688 
1689 	for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) {
1690 		struct sort_dimension *sd = &bstack_sort_dimensions[i];
1691 
1692 		if (strncasecmp(tok, sd->name, strlen(tok)))
1693 			continue;
1694 
1695 		return __sort_dimension__add_output(sd);
1696 	}
1697 
1698 	for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) {
1699 		struct sort_dimension *sd = &memory_sort_dimensions[i];
1700 
1701 		if (strncasecmp(tok, sd->name, strlen(tok)))
1702 			continue;
1703 
1704 		return __sort_dimension__add_output(sd);
1705 	}
1706 
1707 	return -ESRCH;
1708 }
1709 
reset_dimensions(void)1710 static void reset_dimensions(void)
1711 {
1712 	unsigned int i;
1713 
1714 	for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++)
1715 		common_sort_dimensions[i].taken = 0;
1716 
1717 	for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++)
1718 		hpp_sort_dimensions[i].taken = 0;
1719 
1720 	for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++)
1721 		bstack_sort_dimensions[i].taken = 0;
1722 
1723 	for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++)
1724 		memory_sort_dimensions[i].taken = 0;
1725 }
1726 
is_strict_order(const char * order)1727 bool is_strict_order(const char *order)
1728 {
1729 	return order && (*order != '+');
1730 }
1731 
__setup_output_field(void)1732 static int __setup_output_field(void)
1733 {
1734 	char *tmp, *tok, *str, *strp;
1735 	int ret = -EINVAL;
1736 
1737 	if (field_order == NULL)
1738 		return 0;
1739 
1740 	reset_dimensions();
1741 
1742 	strp = str = strdup(field_order);
1743 	if (str == NULL) {
1744 		error("Not enough memory to setup output fields");
1745 		return -ENOMEM;
1746 	}
1747 
1748 	if (!is_strict_order(field_order))
1749 		strp++;
1750 
1751 	if (!strlen(strp)) {
1752 		error("Invalid --fields key: `+'");
1753 		goto out;
1754 	}
1755 
1756 	for (tok = strtok_r(strp, ", ", &tmp);
1757 			tok; tok = strtok_r(NULL, ", ", &tmp)) {
1758 		ret = output_field_add(tok);
1759 		if (ret == -EINVAL) {
1760 			error("Invalid --fields key: `%s'", tok);
1761 			break;
1762 		} else if (ret == -ESRCH) {
1763 			error("Unknown --fields key: `%s'", tok);
1764 			break;
1765 		}
1766 	}
1767 
1768 out:
1769 	free(str);
1770 	return ret;
1771 }
1772 
setup_sorting(void)1773 int setup_sorting(void)
1774 {
1775 	int err;
1776 
1777 	err = __setup_sorting();
1778 	if (err < 0)
1779 		return err;
1780 
1781 	if (parent_pattern != default_parent_pattern) {
1782 		err = sort_dimension__add("parent");
1783 		if (err < 0)
1784 			return err;
1785 	}
1786 
1787 	reset_dimensions();
1788 
1789 	/*
1790 	 * perf diff doesn't use default hpp output fields.
1791 	 */
1792 	if (sort__mode != SORT_MODE__DIFF)
1793 		perf_hpp__init();
1794 
1795 	err = __setup_output_field();
1796 	if (err < 0)
1797 		return err;
1798 
1799 	/* copy sort keys to output fields */
1800 	perf_hpp__setup_output_field();
1801 	/* and then copy output fields to sort keys */
1802 	perf_hpp__append_sort_keys();
1803 
1804 	return 0;
1805 }
1806 
reset_output_field(void)1807 void reset_output_field(void)
1808 {
1809 	sort__need_collapse = 0;
1810 	sort__has_parent = 0;
1811 	sort__has_sym = 0;
1812 	sort__has_dso = 0;
1813 
1814 	field_order = NULL;
1815 	sort_order = NULL;
1816 
1817 	reset_dimensions();
1818 	perf_hpp__reset_output_field();
1819 }
1820