Merge commit 'v2.6.35-rc3' into perf/core
[sfrench/cifs-2.6.git] / tools / perf / util / sort.c
1 #include "sort.h"
2
3 regex_t         parent_regex;
4 const char      default_parent_pattern[] = "^sys_|^do_page_fault";
5 const char      *parent_pattern = default_parent_pattern;
6 const char      default_sort_order[] = "comm,dso,symbol";
7 const char      *sort_order = default_sort_order;
8 int             sort__need_collapse = 0;
9 int             sort__has_parent = 0;
10
11 enum sort_type  sort__first_dimension;
12
13 unsigned int dsos__col_width;
14 unsigned int comms__col_width;
15 unsigned int threads__col_width;
16 unsigned int cpus__col_width;
17 static unsigned int parent_symbol__col_width;
18 char * field_sep;
19
20 LIST_HEAD(hist_entry__sort_list);
21
22 static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf,
23                                        size_t size, unsigned int width);
24 static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf,
25                                      size_t size, unsigned int width);
26 static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf,
27                                     size_t size, unsigned int width);
28 static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
29                                     size_t size, unsigned int width);
30 static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf,
31                                        size_t size, unsigned int width);
32 static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf,
33                                     size_t size, unsigned int width);
34
35 struct sort_entry sort_thread = {
36         .se_header      = "Command:  Pid",
37         .se_cmp         = sort__thread_cmp,
38         .se_snprintf    = hist_entry__thread_snprintf,
39         .se_width       = &threads__col_width,
40 };
41
42 struct sort_entry sort_comm = {
43         .se_header      = "Command",
44         .se_cmp         = sort__comm_cmp,
45         .se_collapse    = sort__comm_collapse,
46         .se_snprintf    = hist_entry__comm_snprintf,
47         .se_width       = &comms__col_width,
48 };
49
50 struct sort_entry sort_dso = {
51         .se_header      = "Shared Object",
52         .se_cmp         = sort__dso_cmp,
53         .se_snprintf    = hist_entry__dso_snprintf,
54         .se_width       = &dsos__col_width,
55 };
56
57 struct sort_entry sort_sym = {
58         .se_header      = "Symbol",
59         .se_cmp         = sort__sym_cmp,
60         .se_snprintf    = hist_entry__sym_snprintf,
61 };
62
63 struct sort_entry sort_parent = {
64         .se_header      = "Parent symbol",
65         .se_cmp         = sort__parent_cmp,
66         .se_snprintf    = hist_entry__parent_snprintf,
67         .se_width       = &parent_symbol__col_width,
68 };
69  
70 struct sort_entry sort_cpu = {
71         .se_header      = "CPU",
72         .se_cmp         = sort__cpu_cmp,
73         .se_snprintf    = hist_entry__cpu_snprintf,
74         .se_width       = &cpus__col_width,
75 };
76
77 struct sort_dimension {
78         const char              *name;
79         struct sort_entry       *entry;
80         int                     taken;
81 };
82
83 static struct sort_dimension sort_dimensions[] = {
84         { .name = "pid",        .entry = &sort_thread,  },
85         { .name = "comm",       .entry = &sort_comm,    },
86         { .name = "dso",        .entry = &sort_dso,     },
87         { .name = "symbol",     .entry = &sort_sym,     },
88         { .name = "parent",     .entry = &sort_parent,  },
89         { .name = "cpu",        .entry = &sort_cpu,     },
90 };
91
92 int64_t cmp_null(void *l, void *r)
93 {
94         if (!l && !r)
95                 return 0;
96         else if (!l)
97                 return -1;
98         else
99                 return 1;
100 }
101
102 /* --sort pid */
103
104 int64_t
105 sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
106 {
107         return right->thread->pid - left->thread->pid;
108 }
109
110 static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...)
111 {
112         int n;
113         va_list ap;
114
115         va_start(ap, fmt);
116         n = vsnprintf(bf, size, fmt, ap);
117         if (field_sep && n > 0) {
118                 char *sep = bf;
119
120                 while (1) {
121                         sep = strchr(sep, *field_sep);
122                         if (sep == NULL)
123                                 break;
124                         *sep = '.';
125                 }
126         }
127         va_end(ap);
128         return n;
129 }
130
131 static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf,
132                                        size_t size, unsigned int width)
133 {
134         return repsep_snprintf(bf, size, "%*s:%5d", width,
135                               self->thread->comm ?: "", self->thread->pid);
136 }
137
138 static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf,
139                                      size_t size, unsigned int width)
140 {
141         return repsep_snprintf(bf, size, "%*s", width, self->thread->comm);
142 }
143
144 /* --sort dso */
145
146 int64_t
147 sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
148 {
149         struct dso *dso_l = left->ms.map ? left->ms.map->dso : NULL;
150         struct dso *dso_r = right->ms.map ? right->ms.map->dso : NULL;
151         const char *dso_name_l, *dso_name_r;
152
153         if (!dso_l || !dso_r)
154                 return cmp_null(dso_l, dso_r);
155
156         if (verbose) {
157                 dso_name_l = dso_l->long_name;
158                 dso_name_r = dso_r->long_name;
159         } else {
160                 dso_name_l = dso_l->short_name;
161                 dso_name_r = dso_r->short_name;
162         }
163
164         return strcmp(dso_name_l, dso_name_r);
165 }
166
167 static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf,
168                                     size_t size, unsigned int width)
169 {
170         if (self->ms.map && self->ms.map->dso) {
171                 const char *dso_name = !verbose ? self->ms.map->dso->short_name :
172                                                   self->ms.map->dso->long_name;
173                 return repsep_snprintf(bf, size, "%-*s", width, dso_name);
174         }
175
176         return repsep_snprintf(bf, size, "%*Lx", width, self->ip);
177 }
178
179 /* --sort symbol */
180
181 int64_t
182 sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
183 {
184         u64 ip_l, ip_r;
185
186         if (left->ms.sym == right->ms.sym)
187                 return 0;
188
189         ip_l = left->ms.sym ? left->ms.sym->start : left->ip;
190         ip_r = right->ms.sym ? right->ms.sym->start : right->ip;
191
192         return (int64_t)(ip_r - ip_l);
193 }
194
195 static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
196                                     size_t size, unsigned int width __used)
197 {
198         size_t ret = 0;
199
200         if (verbose) {
201                 char o = self->ms.map ? dso__symtab_origin(self->ms.map->dso) : '!';
202                 ret += repsep_snprintf(bf, size, "%#018llx %c ", self->ip, o);
203         }
204
205         ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", self->level);
206         if (self->ms.sym)
207                 ret += repsep_snprintf(bf + ret, size - ret, "%s",
208                                        self->ms.sym->name);
209         else
210                 ret += repsep_snprintf(bf + ret, size - ret, "%#016llx", self->ip);
211
212         return ret;
213 }
214
215 /* --sort comm */
216
217 int64_t
218 sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
219 {
220         return right->thread->pid - left->thread->pid;
221 }
222
223 int64_t
224 sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
225 {
226         char *comm_l = left->thread->comm;
227         char *comm_r = right->thread->comm;
228
229         if (!comm_l || !comm_r)
230                 return cmp_null(comm_l, comm_r);
231
232         return strcmp(comm_l, comm_r);
233 }
234
235 /* --sort parent */
236
237 int64_t
238 sort__parent_cmp(struct hist_entry *left, struct hist_entry *right)
239 {
240         struct symbol *sym_l = left->parent;
241         struct symbol *sym_r = right->parent;
242
243         if (!sym_l || !sym_r)
244                 return cmp_null(sym_l, sym_r);
245
246         return strcmp(sym_l->name, sym_r->name);
247 }
248
249 static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf,
250                                        size_t size, unsigned int width)
251 {
252         return repsep_snprintf(bf, size, "%-*s", width,
253                               self->parent ? self->parent->name : "[other]");
254 }
255
256 /* --sort cpu */
257
258 int64_t
259 sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right)
260 {
261         return right->cpu - left->cpu;
262 }
263
264 static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf,
265                                        size_t size, unsigned int width)
266 {
267         return repsep_snprintf(bf, size, "%-*d", width, self->cpu);
268 }
269
270 int sort_dimension__add(const char *tok)
271 {
272         unsigned int i;
273
274         for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) {
275                 struct sort_dimension *sd = &sort_dimensions[i];
276
277                 if (sd->taken)
278                         continue;
279
280                 if (strncasecmp(tok, sd->name, strlen(tok)))
281                         continue;
282
283                 if (sd->entry->se_collapse)
284                         sort__need_collapse = 1;
285
286                 if (sd->entry == &sort_parent) {
287                         int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED);
288                         if (ret) {
289                                 char err[BUFSIZ];
290
291                                 regerror(ret, &parent_regex, err, sizeof(err));
292                                 pr_err("Invalid regex: %s\n%s", parent_pattern, err);
293                                 return -EINVAL;
294                         }
295                         sort__has_parent = 1;
296                 }
297
298                 if (list_empty(&hist_entry__sort_list)) {
299                         if (!strcmp(sd->name, "pid"))
300                                 sort__first_dimension = SORT_PID;
301                         else if (!strcmp(sd->name, "comm"))
302                                 sort__first_dimension = SORT_COMM;
303                         else if (!strcmp(sd->name, "dso"))
304                                 sort__first_dimension = SORT_DSO;
305                         else if (!strcmp(sd->name, "symbol"))
306                                 sort__first_dimension = SORT_SYM;
307                         else if (!strcmp(sd->name, "parent"))
308                                 sort__first_dimension = SORT_PARENT;
309                         else if (!strcmp(sd->name, "cpu"))
310                                 sort__first_dimension = SORT_CPU;
311                 }
312
313                 list_add_tail(&sd->entry->list, &hist_entry__sort_list);
314                 sd->taken = 1;
315
316                 return 0;
317         }
318
319         return -ESRCH;
320 }
321
322 void setup_sorting(const char * const usagestr[], const struct option *opts)
323 {
324         char *tmp, *tok, *str = strdup(sort_order);
325
326         for (tok = strtok_r(str, ", ", &tmp);
327                         tok; tok = strtok_r(NULL, ", ", &tmp)) {
328                 if (sort_dimension__add(tok) < 0) {
329                         error("Unknown --sort key: `%s'", tok);
330                         usage_with_options(usagestr, opts);
331                 }
332         }
333
334         free(str);
335 }
336
337 void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list,
338                              const char *list_name, FILE *fp)
339 {
340         if (list && strlist__nr_entries(list) == 1) {
341                 if (fp != NULL)
342                         fprintf(fp, "# %s: %s\n", list_name,
343                                 strlist__entry(list, 0)->s);
344                 self->elide = true;
345         }
346 }