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