blob: 8deee19d2e7f150969c9abe00ff10eacd954c3f4 [file] [log] [blame]
John Kacurdd68ada2009-09-24 18:02:49 +02001#include "sort.h"
Arnaldo Carvalho de Melo8a6c5b22010-07-20 14:42:52 -03002#include "hist.h"
Namhyung Kim08e71542013-04-03 21:26:19 +09003#include "symbol.h"
John Kacurdd68ada2009-09-24 18:02:49 +02004
5regex_t parent_regex;
Arnaldo Carvalho de Meloedb7c602010-05-17 16:22:41 -03006const char default_parent_pattern[] = "^sys_|^do_page_fault";
7const char *parent_pattern = default_parent_pattern;
8const char default_sort_order[] = "comm,dso,symbol";
9const char *sort_order = default_sort_order;
Frederic Weisbeckeraf0a6fa2009-10-22 23:23:22 +020010int sort__need_collapse = 0;
11int sort__has_parent = 0;
Namhyung Kim1af556402012-09-14 17:35:27 +090012int sort__has_sym = 0;
Namhyung Kim55369fc2013-04-01 20:35:20 +090013enum sort_mode sort__mode = SORT_MODE__NORMAL;
Frederic Weisbeckera4fb5812009-10-22 23:23:23 +020014
15enum sort_type sort__first_dimension;
John Kacurdd68ada2009-09-24 18:02:49 +020016
John Kacurdd68ada2009-09-24 18:02:49 +020017LIST_HEAD(hist_entry__sort_list);
18
Arnaldo Carvalho de Meloa4e3b952010-03-31 11:33:40 -030019static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...)
John Kacurdd68ada2009-09-24 18:02:49 +020020{
21 int n;
22 va_list ap;
23
24 va_start(ap, fmt);
Arnaldo Carvalho de Meloa4e3b952010-03-31 11:33:40 -030025 n = vsnprintf(bf, size, fmt, ap);
Jiri Olsa0ca0c132012-09-06 17:46:56 +020026 if (symbol_conf.field_sep && n > 0) {
Arnaldo Carvalho de Meloa4e3b952010-03-31 11:33:40 -030027 char *sep = bf;
John Kacurdd68ada2009-09-24 18:02:49 +020028
Arnaldo Carvalho de Meloa4e3b952010-03-31 11:33:40 -030029 while (1) {
Jiri Olsa0ca0c132012-09-06 17:46:56 +020030 sep = strchr(sep, *symbol_conf.field_sep);
Arnaldo Carvalho de Meloa4e3b952010-03-31 11:33:40 -030031 if (sep == NULL)
32 break;
33 *sep = '.';
John Kacurdd68ada2009-09-24 18:02:49 +020034 }
John Kacurdd68ada2009-09-24 18:02:49 +020035 }
36 va_end(ap);
Anton Blanchardb8327962012-03-07 11:42:49 +110037
38 if (n >= (int)size)
39 return size - 1;
John Kacurdd68ada2009-09-24 18:02:49 +020040 return n;
41}
42
Frederic Weisbecker872a8782011-06-29 03:14:52 +020043static int64_t cmp_null(void *l, void *r)
44{
45 if (!l && !r)
46 return 0;
47 else if (!l)
48 return -1;
49 else
50 return 1;
51}
52
53/* --sort pid */
54
55static int64_t
56sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
57{
Adrian Hunter38051232013-07-04 16:20:31 +030058 return right->thread->tid - left->thread->tid;
Frederic Weisbecker872a8782011-06-29 03:14:52 +020059}
60
Arnaldo Carvalho de Meloa4e3b952010-03-31 11:33:40 -030061static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf,
62 size_t size, unsigned int width)
John Kacurdd68ada2009-09-24 18:02:49 +020063{
Namhyung Kimfb29a332012-12-27 18:11:40 +090064 return repsep_snprintf(bf, size, "%*s:%5d", width - 6,
Adrian Hunter38051232013-07-04 16:20:31 +030065 self->thread->comm ?: "", self->thread->tid);
John Kacurdd68ada2009-09-24 18:02:49 +020066}
67
Frederic Weisbecker872a8782011-06-29 03:14:52 +020068struct sort_entry sort_thread = {
69 .se_header = "Command: Pid",
70 .se_cmp = sort__thread_cmp,
71 .se_snprintf = hist_entry__thread_snprintf,
72 .se_width_idx = HISTC_THREAD,
73};
74
75/* --sort comm */
76
77static int64_t
78sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
79{
Adrian Hunter38051232013-07-04 16:20:31 +030080 return right->thread->tid - left->thread->tid;
Frederic Weisbecker872a8782011-06-29 03:14:52 +020081}
82
83static int64_t
84sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
85{
86 char *comm_l = left->thread->comm;
87 char *comm_r = right->thread->comm;
88
89 if (!comm_l || !comm_r)
90 return cmp_null(comm_l, comm_r);
91
92 return strcmp(comm_l, comm_r);
93}
94
Arnaldo Carvalho de Meloa4e3b952010-03-31 11:33:40 -030095static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf,
96 size_t size, unsigned int width)
John Kacurdd68ada2009-09-24 18:02:49 +020097{
Arnaldo Carvalho de Meloa4e3b952010-03-31 11:33:40 -030098 return repsep_snprintf(bf, size, "%*s", width, self->thread->comm);
John Kacurdd68ada2009-09-24 18:02:49 +020099}
100
Namhyung Kim14d1ac72012-12-27 18:11:38 +0900101struct sort_entry sort_comm = {
102 .se_header = "Command",
103 .se_cmp = sort__comm_cmp,
104 .se_collapse = sort__comm_collapse,
105 .se_snprintf = hist_entry__comm_snprintf,
106 .se_width_idx = HISTC_COMM,
107};
108
109/* --sort dso */
110
Roberto Agostino Vitillob5387522012-02-09 23:21:01 +0100111static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r)
John Kacurdd68ada2009-09-24 18:02:49 +0200112{
Roberto Agostino Vitillob5387522012-02-09 23:21:01 +0100113 struct dso *dso_l = map_l ? map_l->dso : NULL;
114 struct dso *dso_r = map_r ? map_r->dso : NULL;
Arnaldo Carvalho de Melo439d4732009-10-02 03:29:58 -0300115 const char *dso_name_l, *dso_name_r;
John Kacurdd68ada2009-09-24 18:02:49 +0200116
117 if (!dso_l || !dso_r)
118 return cmp_null(dso_l, dso_r);
119
Arnaldo Carvalho de Melo439d4732009-10-02 03:29:58 -0300120 if (verbose) {
121 dso_name_l = dso_l->long_name;
122 dso_name_r = dso_r->long_name;
123 } else {
124 dso_name_l = dso_l->short_name;
125 dso_name_r = dso_r->short_name;
126 }
127
128 return strcmp(dso_name_l, dso_name_r);
John Kacurdd68ada2009-09-24 18:02:49 +0200129}
130
Roberto Agostino Vitillob5387522012-02-09 23:21:01 +0100131static int64_t
132sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
John Kacurdd68ada2009-09-24 18:02:49 +0200133{
Roberto Agostino Vitillob5387522012-02-09 23:21:01 +0100134 return _sort__dso_cmp(left->ms.map, right->ms.map);
135}
136
Roberto Agostino Vitillob5387522012-02-09 23:21:01 +0100137static int _hist_entry__dso_snprintf(struct map *map, char *bf,
138 size_t size, unsigned int width)
139{
140 if (map && map->dso) {
141 const char *dso_name = !verbose ? map->dso->short_name :
142 map->dso->long_name;
Arnaldo Carvalho de Meloa4e3b952010-03-31 11:33:40 -0300143 return repsep_snprintf(bf, size, "%-*s", width, dso_name);
Arnaldo Carvalho de Melo439d4732009-10-02 03:29:58 -0300144 }
John Kacurdd68ada2009-09-24 18:02:49 +0200145
Ian Munsie1437a302010-12-06 13:37:04 +1100146 return repsep_snprintf(bf, size, "%-*s", width, "[unknown]");
John Kacurdd68ada2009-09-24 18:02:49 +0200147}
148
Roberto Agostino Vitillob5387522012-02-09 23:21:01 +0100149static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf,
150 size_t size, unsigned int width)
151{
152 return _hist_entry__dso_snprintf(self->ms.map, bf, size, width);
153}
154
Namhyung Kim14d1ac72012-12-27 18:11:38 +0900155struct sort_entry sort_dso = {
156 .se_header = "Shared Object",
157 .se_cmp = sort__dso_cmp,
158 .se_snprintf = hist_entry__dso_snprintf,
159 .se_width_idx = HISTC_DSO,
160};
161
162/* --sort symbol */
163
Namhyung Kim51f27d12013-02-06 14:57:15 +0900164static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r)
Namhyung Kim14d1ac72012-12-27 18:11:38 +0900165{
Namhyung Kim51f27d12013-02-06 14:57:15 +0900166 u64 ip_l, ip_r;
167
Namhyung Kim14d1ac72012-12-27 18:11:38 +0900168 if (!sym_l || !sym_r)
169 return cmp_null(sym_l, sym_r);
170
171 if (sym_l == sym_r)
172 return 0;
173
174 ip_l = sym_l->start;
175 ip_r = sym_r->start;
176
177 return (int64_t)(ip_r - ip_l);
178}
179
180static int64_t
181sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
182{
Namhyung Kim14d1ac72012-12-27 18:11:38 +0900183 if (!left->ms.sym && !right->ms.sym)
184 return right->level - left->level;
185
Namhyung Kim51f27d12013-02-06 14:57:15 +0900186 return _sort__sym_cmp(left->ms.sym, right->ms.sym);
Namhyung Kim14d1ac72012-12-27 18:11:38 +0900187}
188
Roberto Agostino Vitillob5387522012-02-09 23:21:01 +0100189static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym,
190 u64 ip, char level, char *bf, size_t size,
Namhyung Kim43355522012-12-27 18:11:39 +0900191 unsigned int width)
Roberto Agostino Vitillob5387522012-02-09 23:21:01 +0100192{
193 size_t ret = 0;
194
195 if (verbose) {
196 char o = map ? dso__symtab_origin(map->dso) : '!';
197 ret += repsep_snprintf(bf, size, "%-#*llx %c ",
Namhyung Kimded19d52013-04-01 20:35:19 +0900198 BITS_PER_LONG / 4 + 2, ip, o);
Roberto Agostino Vitillob5387522012-02-09 23:21:01 +0100199 }
200
201 ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level);
Stephane Eranian98a3b322013-01-24 16:10:35 +0100202 if (sym && map) {
203 if (map->type == MAP__VARIABLE) {
204 ret += repsep_snprintf(bf + ret, size - ret, "%s", sym->name);
205 ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx",
Namhyung Kim62667742013-01-24 16:10:42 +0100206 ip - map->unmap_ip(map, sym->start));
Stephane Eranian98a3b322013-01-24 16:10:35 +0100207 ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
208 width - ret, "");
209 } else {
210 ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
211 width - ret,
212 sym->name);
213 }
214 } else {
Roberto Agostino Vitillob5387522012-02-09 23:21:01 +0100215 size_t len = BITS_PER_LONG / 4;
216 ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx",
217 len, ip);
218 ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
219 width - ret, "");
220 }
221
222 return ret;
223}
224
Roberto Agostino Vitillob5387522012-02-09 23:21:01 +0100225static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
Namhyung Kim43355522012-12-27 18:11:39 +0900226 size_t size, unsigned int width)
Roberto Agostino Vitillob5387522012-02-09 23:21:01 +0100227{
228 return _hist_entry__sym_snprintf(self->ms.map, self->ms.sym, self->ip,
229 self->level, bf, size, width);
230}
John Kacurdd68ada2009-09-24 18:02:49 +0200231
Frederic Weisbecker872a8782011-06-29 03:14:52 +0200232struct sort_entry sort_sym = {
233 .se_header = "Symbol",
234 .se_cmp = sort__sym_cmp,
235 .se_snprintf = hist_entry__sym_snprintf,
236 .se_width_idx = HISTC_SYMBOL,
237};
John Kacurdd68ada2009-09-24 18:02:49 +0200238
Arnaldo Carvalho de Melo409a8be2012-05-30 10:33:24 -0300239/* --sort srcline */
240
241static int64_t
242sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right)
243{
244 return (int64_t)(right->ip - left->ip);
245}
246
247static int hist_entry__srcline_snprintf(struct hist_entry *self, char *bf,
Irina Tirdea1d037ca2012-09-11 01:15:03 +0300248 size_t size,
249 unsigned int width __maybe_unused)
Arnaldo Carvalho de Melo409a8be2012-05-30 10:33:24 -0300250{
Thomas Jarosch8eb44dd2013-01-25 11:02:13 +0100251 FILE *fp = NULL;
Arnaldo Carvalho de Melo409a8be2012-05-30 10:33:24 -0300252 char cmd[PATH_MAX + 2], *path = self->srcline, *nl;
253 size_t line_len;
254
255 if (path != NULL)
256 goto out_path;
257
Namhyung Kimffe10c62012-10-15 12:39:42 +0900258 if (!self->ms.map)
259 goto out_ip;
260
Namhyung Kim88481b62012-10-15 12:39:43 +0900261 if (!strncmp(self->ms.map->dso->long_name, "/tmp/perf-", 10))
262 goto out_ip;
263
Arnaldo Carvalho de Melo409a8be2012-05-30 10:33:24 -0300264 snprintf(cmd, sizeof(cmd), "addr2line -e %s %016" PRIx64,
265 self->ms.map->dso->long_name, self->ip);
266 fp = popen(cmd, "r");
267 if (!fp)
268 goto out_ip;
269
270 if (getline(&path, &line_len, fp) < 0 || !line_len)
271 goto out_ip;
Arnaldo Carvalho de Melo409a8be2012-05-30 10:33:24 -0300272 self->srcline = strdup(path);
273 if (self->srcline == NULL)
274 goto out_ip;
275
276 nl = strchr(self->srcline, '\n');
277 if (nl != NULL)
278 *nl = '\0';
279 path = self->srcline;
280out_path:
Thomas Jarosch8eb44dd2013-01-25 11:02:13 +0100281 if (fp)
282 pclose(fp);
Arnaldo Carvalho de Melo409a8be2012-05-30 10:33:24 -0300283 return repsep_snprintf(bf, size, "%s", path);
284out_ip:
Thomas Jarosch8eb44dd2013-01-25 11:02:13 +0100285 if (fp)
286 pclose(fp);
Arnaldo Carvalho de Melo409a8be2012-05-30 10:33:24 -0300287 return repsep_snprintf(bf, size, "%-#*llx", BITS_PER_LONG / 4, self->ip);
288}
289
290struct sort_entry sort_srcline = {
291 .se_header = "Source:Line",
292 .se_cmp = sort__srcline_cmp,
293 .se_snprintf = hist_entry__srcline_snprintf,
294 .se_width_idx = HISTC_SRCLINE,
295};
296
John Kacurdd68ada2009-09-24 18:02:49 +0200297/* --sort parent */
298
Frederic Weisbecker872a8782011-06-29 03:14:52 +0200299static int64_t
John Kacurdd68ada2009-09-24 18:02:49 +0200300sort__parent_cmp(struct hist_entry *left, struct hist_entry *right)
301{
302 struct symbol *sym_l = left->parent;
303 struct symbol *sym_r = right->parent;
304
305 if (!sym_l || !sym_r)
306 return cmp_null(sym_l, sym_r);
307
308 return strcmp(sym_l->name, sym_r->name);
309}
310
Arnaldo Carvalho de Meloa4e3b952010-03-31 11:33:40 -0300311static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf,
312 size_t size, unsigned int width)
John Kacurdd68ada2009-09-24 18:02:49 +0200313{
Arnaldo Carvalho de Meloa4e3b952010-03-31 11:33:40 -0300314 return repsep_snprintf(bf, size, "%-*s", width,
John Kacurdd68ada2009-09-24 18:02:49 +0200315 self->parent ? self->parent->name : "[other]");
316}
317
Frederic Weisbecker872a8782011-06-29 03:14:52 +0200318struct sort_entry sort_parent = {
319 .se_header = "Parent symbol",
320 .se_cmp = sort__parent_cmp,
321 .se_snprintf = hist_entry__parent_snprintf,
322 .se_width_idx = HISTC_PARENT,
323};
324
Arun Sharmaf60f3592010-06-04 11:27:10 -0300325/* --sort cpu */
326
Frederic Weisbecker872a8782011-06-29 03:14:52 +0200327static int64_t
Arun Sharmaf60f3592010-06-04 11:27:10 -0300328sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right)
329{
330 return right->cpu - left->cpu;
331}
332
333static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf,
334 size_t size, unsigned int width)
335{
Namhyung Kimdccf1802012-12-27 18:11:41 +0900336 return repsep_snprintf(bf, size, "%*d", width, self->cpu);
Arun Sharmaf60f3592010-06-04 11:27:10 -0300337}
338
Frederic Weisbecker872a8782011-06-29 03:14:52 +0200339struct sort_entry sort_cpu = {
340 .se_header = "CPU",
341 .se_cmp = sort__cpu_cmp,
342 .se_snprintf = hist_entry__cpu_snprintf,
343 .se_width_idx = HISTC_CPU,
344};
345
Namhyung Kim14d1ac72012-12-27 18:11:38 +0900346/* sort keys for branch stacks */
347
Roberto Agostino Vitillob5387522012-02-09 23:21:01 +0100348static int64_t
349sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right)
350{
351 return _sort__dso_cmp(left->branch_info->from.map,
352 right->branch_info->from.map);
353}
354
355static int hist_entry__dso_from_snprintf(struct hist_entry *self, char *bf,
356 size_t size, unsigned int width)
357{
358 return _hist_entry__dso_snprintf(self->branch_info->from.map,
359 bf, size, width);
360}
361
Roberto Agostino Vitillob5387522012-02-09 23:21:01 +0100362static int64_t
363sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right)
364{
365 return _sort__dso_cmp(left->branch_info->to.map,
366 right->branch_info->to.map);
367}
368
369static int hist_entry__dso_to_snprintf(struct hist_entry *self, char *bf,
370 size_t size, unsigned int width)
371{
372 return _hist_entry__dso_snprintf(self->branch_info->to.map,
373 bf, size, width);
374}
375
376static int64_t
377sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right)
378{
379 struct addr_map_symbol *from_l = &left->branch_info->from;
380 struct addr_map_symbol *from_r = &right->branch_info->from;
381
382 if (!from_l->sym && !from_r->sym)
383 return right->level - left->level;
384
Namhyung Kim51f27d12013-02-06 14:57:15 +0900385 return _sort__sym_cmp(from_l->sym, from_r->sym);
Roberto Agostino Vitillob5387522012-02-09 23:21:01 +0100386}
387
388static int64_t
389sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right)
390{
391 struct addr_map_symbol *to_l = &left->branch_info->to;
392 struct addr_map_symbol *to_r = &right->branch_info->to;
393
394 if (!to_l->sym && !to_r->sym)
395 return right->level - left->level;
396
Namhyung Kim51f27d12013-02-06 14:57:15 +0900397 return _sort__sym_cmp(to_l->sym, to_r->sym);
Roberto Agostino Vitillob5387522012-02-09 23:21:01 +0100398}
399
400static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf,
Namhyung Kim43355522012-12-27 18:11:39 +0900401 size_t size, unsigned int width)
Roberto Agostino Vitillob5387522012-02-09 23:21:01 +0100402{
403 struct addr_map_symbol *from = &self->branch_info->from;
404 return _hist_entry__sym_snprintf(from->map, from->sym, from->addr,
405 self->level, bf, size, width);
406
407}
408
409static int hist_entry__sym_to_snprintf(struct hist_entry *self, char *bf,
Namhyung Kim43355522012-12-27 18:11:39 +0900410 size_t size, unsigned int width)
Roberto Agostino Vitillob5387522012-02-09 23:21:01 +0100411{
412 struct addr_map_symbol *to = &self->branch_info->to;
413 return _hist_entry__sym_snprintf(to->map, to->sym, to->addr,
414 self->level, bf, size, width);
415
416}
417
Namhyung Kim14d1ac72012-12-27 18:11:38 +0900418struct sort_entry sort_dso_from = {
419 .se_header = "Source Shared Object",
420 .se_cmp = sort__dso_from_cmp,
421 .se_snprintf = hist_entry__dso_from_snprintf,
422 .se_width_idx = HISTC_DSO_FROM,
423};
424
Roberto Agostino Vitillob5387522012-02-09 23:21:01 +0100425struct sort_entry sort_dso_to = {
426 .se_header = "Target Shared Object",
427 .se_cmp = sort__dso_to_cmp,
428 .se_snprintf = hist_entry__dso_to_snprintf,
429 .se_width_idx = HISTC_DSO_TO,
430};
431
432struct sort_entry sort_sym_from = {
433 .se_header = "Source Symbol",
434 .se_cmp = sort__sym_from_cmp,
435 .se_snprintf = hist_entry__sym_from_snprintf,
436 .se_width_idx = HISTC_SYMBOL_FROM,
437};
438
439struct sort_entry sort_sym_to = {
440 .se_header = "Target Symbol",
441 .se_cmp = sort__sym_to_cmp,
442 .se_snprintf = hist_entry__sym_to_snprintf,
443 .se_width_idx = HISTC_SYMBOL_TO,
444};
445
446static int64_t
447sort__mispredict_cmp(struct hist_entry *left, struct hist_entry *right)
448{
449 const unsigned char mp = left->branch_info->flags.mispred !=
450 right->branch_info->flags.mispred;
451 const unsigned char p = left->branch_info->flags.predicted !=
452 right->branch_info->flags.predicted;
453
454 return mp || p;
455}
456
457static int hist_entry__mispredict_snprintf(struct hist_entry *self, char *bf,
458 size_t size, unsigned int width){
459 static const char *out = "N/A";
460
461 if (self->branch_info->flags.predicted)
462 out = "N";
463 else if (self->branch_info->flags.mispred)
464 out = "Y";
465
466 return repsep_snprintf(bf, size, "%-*s", width, out);
467}
468
Stephane Eranian98a3b322013-01-24 16:10:35 +0100469/* --sort daddr_sym */
470static int64_t
471sort__daddr_cmp(struct hist_entry *left, struct hist_entry *right)
472{
473 uint64_t l = 0, r = 0;
474
475 if (left->mem_info)
476 l = left->mem_info->daddr.addr;
477 if (right->mem_info)
478 r = right->mem_info->daddr.addr;
479
480 return (int64_t)(r - l);
481}
482
483static int hist_entry__daddr_snprintf(struct hist_entry *self, char *bf,
484 size_t size, unsigned int width)
485{
486 uint64_t addr = 0;
487 struct map *map = NULL;
488 struct symbol *sym = NULL;
489
490 if (self->mem_info) {
491 addr = self->mem_info->daddr.addr;
492 map = self->mem_info->daddr.map;
493 sym = self->mem_info->daddr.sym;
494 }
495 return _hist_entry__sym_snprintf(map, sym, addr, self->level, bf, size,
496 width);
497}
498
499static int64_t
500sort__dso_daddr_cmp(struct hist_entry *left, struct hist_entry *right)
501{
502 struct map *map_l = NULL;
503 struct map *map_r = NULL;
504
505 if (left->mem_info)
506 map_l = left->mem_info->daddr.map;
507 if (right->mem_info)
508 map_r = right->mem_info->daddr.map;
509
510 return _sort__dso_cmp(map_l, map_r);
511}
512
513static int hist_entry__dso_daddr_snprintf(struct hist_entry *self, char *bf,
514 size_t size, unsigned int width)
515{
516 struct map *map = NULL;
517
518 if (self->mem_info)
519 map = self->mem_info->daddr.map;
520
521 return _hist_entry__dso_snprintf(map, bf, size, width);
522}
523
524static int64_t
525sort__locked_cmp(struct hist_entry *left, struct hist_entry *right)
526{
527 union perf_mem_data_src data_src_l;
528 union perf_mem_data_src data_src_r;
529
530 if (left->mem_info)
531 data_src_l = left->mem_info->data_src;
532 else
533 data_src_l.mem_lock = PERF_MEM_LOCK_NA;
534
535 if (right->mem_info)
536 data_src_r = right->mem_info->data_src;
537 else
538 data_src_r.mem_lock = PERF_MEM_LOCK_NA;
539
540 return (int64_t)(data_src_r.mem_lock - data_src_l.mem_lock);
541}
542
543static int hist_entry__locked_snprintf(struct hist_entry *self, char *bf,
544 size_t size, unsigned int width)
545{
546 const char *out;
547 u64 mask = PERF_MEM_LOCK_NA;
548
549 if (self->mem_info)
550 mask = self->mem_info->data_src.mem_lock;
551
552 if (mask & PERF_MEM_LOCK_NA)
553 out = "N/A";
554 else if (mask & PERF_MEM_LOCK_LOCKED)
555 out = "Yes";
556 else
557 out = "No";
558
559 return repsep_snprintf(bf, size, "%-*s", width, out);
560}
561
562static int64_t
563sort__tlb_cmp(struct hist_entry *left, struct hist_entry *right)
564{
565 union perf_mem_data_src data_src_l;
566 union perf_mem_data_src data_src_r;
567
568 if (left->mem_info)
569 data_src_l = left->mem_info->data_src;
570 else
571 data_src_l.mem_dtlb = PERF_MEM_TLB_NA;
572
573 if (right->mem_info)
574 data_src_r = right->mem_info->data_src;
575 else
576 data_src_r.mem_dtlb = PERF_MEM_TLB_NA;
577
578 return (int64_t)(data_src_r.mem_dtlb - data_src_l.mem_dtlb);
579}
580
581static const char * const tlb_access[] = {
582 "N/A",
583 "HIT",
584 "MISS",
585 "L1",
586 "L2",
587 "Walker",
588 "Fault",
589};
590#define NUM_TLB_ACCESS (sizeof(tlb_access)/sizeof(const char *))
591
592static int hist_entry__tlb_snprintf(struct hist_entry *self, char *bf,
593 size_t size, unsigned int width)
594{
595 char out[64];
596 size_t sz = sizeof(out) - 1; /* -1 for null termination */
597 size_t l = 0, i;
598 u64 m = PERF_MEM_TLB_NA;
599 u64 hit, miss;
600
601 out[0] = '\0';
602
603 if (self->mem_info)
604 m = self->mem_info->data_src.mem_dtlb;
605
606 hit = m & PERF_MEM_TLB_HIT;
607 miss = m & PERF_MEM_TLB_MISS;
608
609 /* already taken care of */
610 m &= ~(PERF_MEM_TLB_HIT|PERF_MEM_TLB_MISS);
611
612 for (i = 0; m && i < NUM_TLB_ACCESS; i++, m >>= 1) {
613 if (!(m & 0x1))
614 continue;
615 if (l) {
616 strcat(out, " or ");
617 l += 4;
618 }
619 strncat(out, tlb_access[i], sz - l);
620 l += strlen(tlb_access[i]);
621 }
622 if (*out == '\0')
623 strcpy(out, "N/A");
624 if (hit)
625 strncat(out, " hit", sz - l);
626 if (miss)
627 strncat(out, " miss", sz - l);
628
629 return repsep_snprintf(bf, size, "%-*s", width, out);
630}
631
632static int64_t
633sort__lvl_cmp(struct hist_entry *left, struct hist_entry *right)
634{
635 union perf_mem_data_src data_src_l;
636 union perf_mem_data_src data_src_r;
637
638 if (left->mem_info)
639 data_src_l = left->mem_info->data_src;
640 else
641 data_src_l.mem_lvl = PERF_MEM_LVL_NA;
642
643 if (right->mem_info)
644 data_src_r = right->mem_info->data_src;
645 else
646 data_src_r.mem_lvl = PERF_MEM_LVL_NA;
647
648 return (int64_t)(data_src_r.mem_lvl - data_src_l.mem_lvl);
649}
650
651static const char * const mem_lvl[] = {
652 "N/A",
653 "HIT",
654 "MISS",
655 "L1",
656 "LFB",
657 "L2",
658 "L3",
659 "Local RAM",
660 "Remote RAM (1 hop)",
661 "Remote RAM (2 hops)",
662 "Remote Cache (1 hop)",
663 "Remote Cache (2 hops)",
664 "I/O",
665 "Uncached",
666};
667#define NUM_MEM_LVL (sizeof(mem_lvl)/sizeof(const char *))
668
669static int hist_entry__lvl_snprintf(struct hist_entry *self, char *bf,
670 size_t size, unsigned int width)
671{
672 char out[64];
673 size_t sz = sizeof(out) - 1; /* -1 for null termination */
674 size_t i, l = 0;
675 u64 m = PERF_MEM_LVL_NA;
676 u64 hit, miss;
677
678 if (self->mem_info)
679 m = self->mem_info->data_src.mem_lvl;
680
681 out[0] = '\0';
682
683 hit = m & PERF_MEM_LVL_HIT;
684 miss = m & PERF_MEM_LVL_MISS;
685
686 /* already taken care of */
687 m &= ~(PERF_MEM_LVL_HIT|PERF_MEM_LVL_MISS);
688
689 for (i = 0; m && i < NUM_MEM_LVL; i++, m >>= 1) {
690 if (!(m & 0x1))
691 continue;
692 if (l) {
693 strcat(out, " or ");
694 l += 4;
695 }
696 strncat(out, mem_lvl[i], sz - l);
697 l += strlen(mem_lvl[i]);
698 }
699 if (*out == '\0')
700 strcpy(out, "N/A");
701 if (hit)
702 strncat(out, " hit", sz - l);
703 if (miss)
704 strncat(out, " miss", sz - l);
705
706 return repsep_snprintf(bf, size, "%-*s", width, out);
707}
708
709static int64_t
710sort__snoop_cmp(struct hist_entry *left, struct hist_entry *right)
711{
712 union perf_mem_data_src data_src_l;
713 union perf_mem_data_src data_src_r;
714
715 if (left->mem_info)
716 data_src_l = left->mem_info->data_src;
717 else
718 data_src_l.mem_snoop = PERF_MEM_SNOOP_NA;
719
720 if (right->mem_info)
721 data_src_r = right->mem_info->data_src;
722 else
723 data_src_r.mem_snoop = PERF_MEM_SNOOP_NA;
724
725 return (int64_t)(data_src_r.mem_snoop - data_src_l.mem_snoop);
726}
727
728static const char * const snoop_access[] = {
729 "N/A",
730 "None",
731 "Miss",
732 "Hit",
733 "HitM",
734};
735#define NUM_SNOOP_ACCESS (sizeof(snoop_access)/sizeof(const char *))
736
737static int hist_entry__snoop_snprintf(struct hist_entry *self, char *bf,
738 size_t size, unsigned int width)
739{
740 char out[64];
741 size_t sz = sizeof(out) - 1; /* -1 for null termination */
742 size_t i, l = 0;
743 u64 m = PERF_MEM_SNOOP_NA;
744
745 out[0] = '\0';
746
747 if (self->mem_info)
748 m = self->mem_info->data_src.mem_snoop;
749
750 for (i = 0; m && i < NUM_SNOOP_ACCESS; i++, m >>= 1) {
751 if (!(m & 0x1))
752 continue;
753 if (l) {
754 strcat(out, " or ");
755 l += 4;
756 }
757 strncat(out, snoop_access[i], sz - l);
758 l += strlen(snoop_access[i]);
759 }
760
761 if (*out == '\0')
762 strcpy(out, "N/A");
763
764 return repsep_snprintf(bf, size, "%-*s", width, out);
765}
766
Roberto Agostino Vitillob5387522012-02-09 23:21:01 +0100767struct sort_entry sort_mispredict = {
768 .se_header = "Branch Mispredicted",
769 .se_cmp = sort__mispredict_cmp,
770 .se_snprintf = hist_entry__mispredict_snprintf,
771 .se_width_idx = HISTC_MISPREDICT,
772};
773
Andi Kleen05484292013-01-24 16:10:29 +0100774static u64 he_weight(struct hist_entry *he)
775{
776 return he->stat.nr_events ? he->stat.weight / he->stat.nr_events : 0;
777}
778
779static int64_t
780sort__local_weight_cmp(struct hist_entry *left, struct hist_entry *right)
781{
782 return he_weight(left) - he_weight(right);
783}
784
785static int hist_entry__local_weight_snprintf(struct hist_entry *self, char *bf,
786 size_t size, unsigned int width)
787{
788 return repsep_snprintf(bf, size, "%-*llu", width, he_weight(self));
789}
790
791struct sort_entry sort_local_weight = {
792 .se_header = "Local Weight",
793 .se_cmp = sort__local_weight_cmp,
794 .se_snprintf = hist_entry__local_weight_snprintf,
795 .se_width_idx = HISTC_LOCAL_WEIGHT,
796};
797
798static int64_t
799sort__global_weight_cmp(struct hist_entry *left, struct hist_entry *right)
800{
801 return left->stat.weight - right->stat.weight;
802}
803
804static int hist_entry__global_weight_snprintf(struct hist_entry *self, char *bf,
805 size_t size, unsigned int width)
806{
807 return repsep_snprintf(bf, size, "%-*llu", width, self->stat.weight);
808}
809
810struct sort_entry sort_global_weight = {
811 .se_header = "Weight",
812 .se_cmp = sort__global_weight_cmp,
813 .se_snprintf = hist_entry__global_weight_snprintf,
814 .se_width_idx = HISTC_GLOBAL_WEIGHT,
815};
816
Stephane Eranian98a3b322013-01-24 16:10:35 +0100817struct sort_entry sort_mem_daddr_sym = {
818 .se_header = "Data Symbol",
819 .se_cmp = sort__daddr_cmp,
820 .se_snprintf = hist_entry__daddr_snprintf,
821 .se_width_idx = HISTC_MEM_DADDR_SYMBOL,
822};
823
824struct sort_entry sort_mem_daddr_dso = {
825 .se_header = "Data Object",
826 .se_cmp = sort__dso_daddr_cmp,
827 .se_snprintf = hist_entry__dso_daddr_snprintf,
828 .se_width_idx = HISTC_MEM_DADDR_SYMBOL,
829};
830
831struct sort_entry sort_mem_locked = {
832 .se_header = "Locked",
833 .se_cmp = sort__locked_cmp,
834 .se_snprintf = hist_entry__locked_snprintf,
835 .se_width_idx = HISTC_MEM_LOCKED,
836};
837
838struct sort_entry sort_mem_tlb = {
839 .se_header = "TLB access",
840 .se_cmp = sort__tlb_cmp,
841 .se_snprintf = hist_entry__tlb_snprintf,
842 .se_width_idx = HISTC_MEM_TLB,
843};
844
845struct sort_entry sort_mem_lvl = {
846 .se_header = "Memory access",
847 .se_cmp = sort__lvl_cmp,
848 .se_snprintf = hist_entry__lvl_snprintf,
849 .se_width_idx = HISTC_MEM_LVL,
850};
851
852struct sort_entry sort_mem_snoop = {
853 .se_header = "Snoop",
854 .se_cmp = sort__snoop_cmp,
855 .se_snprintf = hist_entry__snoop_snprintf,
856 .se_width_idx = HISTC_MEM_SNOOP,
857};
858
Frederic Weisbecker872a8782011-06-29 03:14:52 +0200859struct sort_dimension {
860 const char *name;
861 struct sort_entry *entry;
862 int taken;
863};
864
Roberto Agostino Vitillob5387522012-02-09 23:21:01 +0100865#define DIM(d, n, func) [d] = { .name = n, .entry = &(func) }
866
Namhyung Kimfc5871e2012-12-27 18:11:46 +0900867static struct sort_dimension common_sort_dimensions[] = {
Roberto Agostino Vitillob5387522012-02-09 23:21:01 +0100868 DIM(SORT_PID, "pid", sort_thread),
869 DIM(SORT_COMM, "comm", sort_comm),
870 DIM(SORT_DSO, "dso", sort_dso),
Roberto Agostino Vitillob5387522012-02-09 23:21:01 +0100871 DIM(SORT_SYM, "symbol", sort_sym),
Roberto Agostino Vitillob5387522012-02-09 23:21:01 +0100872 DIM(SORT_PARENT, "parent", sort_parent),
873 DIM(SORT_CPU, "cpu", sort_cpu),
Arnaldo Carvalho de Melo409a8be2012-05-30 10:33:24 -0300874 DIM(SORT_SRCLINE, "srcline", sort_srcline),
Frederic Weisbecker872a8782011-06-29 03:14:52 +0200875};
876
Namhyung Kimfc5871e2012-12-27 18:11:46 +0900877#undef DIM
878
879#define DIM(d, n, func) [d - __SORT_BRANCH_STACK] = { .name = n, .entry = &(func) }
880
881static struct sort_dimension bstack_sort_dimensions[] = {
882 DIM(SORT_DSO_FROM, "dso_from", sort_dso_from),
883 DIM(SORT_DSO_TO, "dso_to", sort_dso_to),
884 DIM(SORT_SYM_FROM, "symbol_from", sort_sym_from),
885 DIM(SORT_SYM_TO, "symbol_to", sort_sym_to),
886 DIM(SORT_MISPREDICT, "mispredict", sort_mispredict),
887};
888
889#undef DIM
890
Namhyung Kimafab87b2013-04-03 21:26:11 +0900891#define DIM(d, n, func) [d - __SORT_MEMORY_MODE] = { .name = n, .entry = &(func) }
892
893static struct sort_dimension memory_sort_dimensions[] = {
894 DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight),
895 DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight),
896 DIM(SORT_MEM_DADDR_SYMBOL, "symbol_daddr", sort_mem_daddr_sym),
897 DIM(SORT_MEM_DADDR_DSO, "dso_daddr", sort_mem_daddr_dso),
898 DIM(SORT_MEM_LOCKED, "locked", sort_mem_locked),
899 DIM(SORT_MEM_TLB, "tlb", sort_mem_tlb),
900 DIM(SORT_MEM_LVL, "mem", sort_mem_lvl),
901 DIM(SORT_MEM_SNOOP, "snoop", sort_mem_snoop),
902};
903
904#undef DIM
905
Namhyung Kim2f532d02013-04-03 21:26:10 +0900906static void __sort_dimension__add(struct sort_dimension *sd, enum sort_type idx)
907{
908 if (sd->taken)
909 return;
910
911 if (sd->entry->se_collapse)
912 sort__need_collapse = 1;
913
914 if (list_empty(&hist_entry__sort_list))
915 sort__first_dimension = idx;
916
917 list_add_tail(&sd->entry->list, &hist_entry__sort_list);
918 sd->taken = 1;
919}
920
John Kacurdd68ada2009-09-24 18:02:49 +0200921int sort_dimension__add(const char *tok)
922{
923 unsigned int i;
924
Namhyung Kimfc5871e2012-12-27 18:11:46 +0900925 for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) {
926 struct sort_dimension *sd = &common_sort_dimensions[i];
John Kacurdd68ada2009-09-24 18:02:49 +0200927
John Kacurdd68ada2009-09-24 18:02:49 +0200928 if (strncasecmp(tok, sd->name, strlen(tok)))
929 continue;
Namhyung Kimfc5871e2012-12-27 18:11:46 +0900930
John Kacurdd68ada2009-09-24 18:02:49 +0200931 if (sd->entry == &sort_parent) {
932 int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED);
933 if (ret) {
934 char err[BUFSIZ];
935
936 regerror(ret, &parent_regex, err, sizeof(err));
Arnaldo Carvalho de Melo2aefa4f2010-04-02 12:30:57 -0300937 pr_err("Invalid regex: %s\n%s", parent_pattern, err);
938 return -EINVAL;
John Kacurdd68ada2009-09-24 18:02:49 +0200939 }
940 sort__has_parent = 1;
Namhyung Kim930477b2013-04-05 10:26:36 +0900941 } else if (sd->entry == &sort_sym) {
Namhyung Kim1af556402012-09-14 17:35:27 +0900942 sort__has_sym = 1;
John Kacurdd68ada2009-09-24 18:02:49 +0200943 }
944
Namhyung Kim2f532d02013-04-03 21:26:10 +0900945 __sort_dimension__add(sd, i);
John Kacurdd68ada2009-09-24 18:02:49 +0200946 return 0;
947 }
Namhyung Kimfc5871e2012-12-27 18:11:46 +0900948
949 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) {
950 struct sort_dimension *sd = &bstack_sort_dimensions[i];
951
952 if (strncasecmp(tok, sd->name, strlen(tok)))
953 continue;
954
Namhyung Kim55369fc2013-04-01 20:35:20 +0900955 if (sort__mode != SORT_MODE__BRANCH)
Namhyung Kimfc5871e2012-12-27 18:11:46 +0900956 return -EINVAL;
957
958 if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to)
959 sort__has_sym = 1;
960
Namhyung Kim2f532d02013-04-03 21:26:10 +0900961 __sort_dimension__add(sd, i + __SORT_BRANCH_STACK);
Namhyung Kimfc5871e2012-12-27 18:11:46 +0900962 return 0;
963 }
964
Namhyung Kimafab87b2013-04-03 21:26:11 +0900965 for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) {
966 struct sort_dimension *sd = &memory_sort_dimensions[i];
967
968 if (strncasecmp(tok, sd->name, strlen(tok)))
969 continue;
970
971 if (sort__mode != SORT_MODE__MEMORY)
972 return -EINVAL;
973
974 if (sd->entry == &sort_mem_daddr_sym)
975 sort__has_sym = 1;
976
977 __sort_dimension__add(sd, i + __SORT_MEMORY_MODE);
978 return 0;
979 }
980
John Kacurdd68ada2009-09-24 18:02:49 +0200981 return -ESRCH;
982}
Arnaldo Carvalho de Meloc8829c72009-12-14 20:09:29 -0200983
Namhyung Kim55309982013-02-06 14:57:16 +0900984int setup_sorting(void)
Arnaldo Carvalho de Meloc8829c72009-12-14 20:09:29 -0200985{
986 char *tmp, *tok, *str = strdup(sort_order);
Namhyung Kim55309982013-02-06 14:57:16 +0900987 int ret = 0;
Arnaldo Carvalho de Meloc8829c72009-12-14 20:09:29 -0200988
Namhyung Kim5936f542013-02-06 14:57:17 +0900989 if (str == NULL) {
990 error("Not enough memory to setup sort keys");
991 return -ENOMEM;
992 }
993
Arnaldo Carvalho de Meloc8829c72009-12-14 20:09:29 -0200994 for (tok = strtok_r(str, ", ", &tmp);
995 tok; tok = strtok_r(NULL, ", ", &tmp)) {
Namhyung Kim55309982013-02-06 14:57:16 +0900996 ret = sort_dimension__add(tok);
Namhyung Kimfc5871e2012-12-27 18:11:46 +0900997 if (ret == -EINVAL) {
998 error("Invalid --sort key: `%s'", tok);
Namhyung Kim55309982013-02-06 14:57:16 +0900999 break;
Namhyung Kimfc5871e2012-12-27 18:11:46 +09001000 } else if (ret == -ESRCH) {
Arnaldo Carvalho de Meloc8829c72009-12-14 20:09:29 -02001001 error("Unknown --sort key: `%s'", tok);
Namhyung Kim55309982013-02-06 14:57:16 +09001002 break;
Arnaldo Carvalho de Meloc8829c72009-12-14 20:09:29 -02001003 }
1004 }
1005
1006 free(str);
Namhyung Kim55309982013-02-06 14:57:16 +09001007 return ret;
Arnaldo Carvalho de Meloc8829c72009-12-14 20:09:29 -02001008}
Arnaldo Carvalho de Meloc351c282009-12-16 13:49:27 -02001009
Namhyung Kim08e71542013-04-03 21:26:19 +09001010static void sort_entry__setup_elide(struct sort_entry *self,
1011 struct strlist *list,
1012 const char *list_name, FILE *fp)
Arnaldo Carvalho de Meloc351c282009-12-16 13:49:27 -02001013{
1014 if (list && strlist__nr_entries(list) == 1) {
1015 if (fp != NULL)
1016 fprintf(fp, "# %s: %s\n", list_name,
1017 strlist__entry(list, 0)->s);
1018 self->elide = true;
1019 }
1020}
Namhyung Kim08e71542013-04-03 21:26:19 +09001021
1022void sort__setup_elide(FILE *output)
1023{
1024 sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
1025 "dso", output);
1026 sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list,
1027 "comm", output);
1028 sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list,
1029 "symbol", output);
1030
1031 if (sort__mode == SORT_MODE__BRANCH) {
1032 sort_entry__setup_elide(&sort_dso_from,
1033 symbol_conf.dso_from_list,
1034 "dso_from", output);
1035 sort_entry__setup_elide(&sort_dso_to,
1036 symbol_conf.dso_to_list,
1037 "dso_to", output);
1038 sort_entry__setup_elide(&sort_sym_from,
1039 symbol_conf.sym_from_list,
1040 "sym_from", output);
1041 sort_entry__setup_elide(&sort_sym_to,
1042 symbol_conf.sym_to_list,
1043 "sym_to", output);
1044 } else if (sort__mode == SORT_MODE__MEMORY) {
1045 sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
1046 "symbol_daddr", output);
1047 sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
1048 "dso_daddr", output);
1049 sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
1050 "mem", output);
1051 sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
1052 "local_weight", output);
1053 sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
1054 "tlb", output);
1055 sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
1056 "snoop", output);
1057 }
1058
1059}