perf report: Implement --sort cpu
[deliverable/linux.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 }
This page took 0.043982 seconds and 5 git commands to generate.