diff options
Diffstat (limited to 'tools/perf/util/sort.c')
-rw-r--r-- | tools/perf/util/sort.c | 290 |
1 files changed, 290 insertions, 0 deletions
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c new file mode 100644 index 000000000000..b490354d1b23 --- /dev/null +++ b/tools/perf/util/sort.c | |||
@@ -0,0 +1,290 @@ | |||
1 | #include "sort.h" | ||
2 | |||
3 | regex_t parent_regex; | ||
4 | char default_parent_pattern[] = "^sys_|^do_page_fault"; | ||
5 | char *parent_pattern = default_parent_pattern; | ||
6 | char default_sort_order[] = "comm,dso,symbol"; | ||
7 | 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 | static unsigned int parent_symbol__col_width; | ||
17 | char * field_sep; | ||
18 | |||
19 | LIST_HEAD(hist_entry__sort_list); | ||
20 | |||
21 | struct sort_entry sort_thread = { | ||
22 | .header = "Command: Pid", | ||
23 | .cmp = sort__thread_cmp, | ||
24 | .print = sort__thread_print, | ||
25 | .width = &threads__col_width, | ||
26 | }; | ||
27 | |||
28 | struct sort_entry sort_comm = { | ||
29 | .header = "Command", | ||
30 | .cmp = sort__comm_cmp, | ||
31 | .collapse = sort__comm_collapse, | ||
32 | .print = sort__comm_print, | ||
33 | .width = &comms__col_width, | ||
34 | }; | ||
35 | |||
36 | struct sort_entry sort_dso = { | ||
37 | .header = "Shared Object", | ||
38 | .cmp = sort__dso_cmp, | ||
39 | .print = sort__dso_print, | ||
40 | .width = &dsos__col_width, | ||
41 | }; | ||
42 | |||
43 | struct sort_entry sort_sym = { | ||
44 | .header = "Symbol", | ||
45 | .cmp = sort__sym_cmp, | ||
46 | .print = sort__sym_print, | ||
47 | }; | ||
48 | |||
49 | struct sort_entry sort_parent = { | ||
50 | .header = "Parent symbol", | ||
51 | .cmp = sort__parent_cmp, | ||
52 | .print = sort__parent_print, | ||
53 | .width = &parent_symbol__col_width, | ||
54 | }; | ||
55 | |||
56 | struct sort_dimension { | ||
57 | const char *name; | ||
58 | struct sort_entry *entry; | ||
59 | int taken; | ||
60 | }; | ||
61 | |||
62 | static struct sort_dimension sort_dimensions[] = { | ||
63 | { .name = "pid", .entry = &sort_thread, }, | ||
64 | { .name = "comm", .entry = &sort_comm, }, | ||
65 | { .name = "dso", .entry = &sort_dso, }, | ||
66 | { .name = "symbol", .entry = &sort_sym, }, | ||
67 | { .name = "parent", .entry = &sort_parent, }, | ||
68 | }; | ||
69 | |||
70 | int64_t cmp_null(void *l, void *r) | ||
71 | { | ||
72 | if (!l && !r) | ||
73 | return 0; | ||
74 | else if (!l) | ||
75 | return -1; | ||
76 | else | ||
77 | return 1; | ||
78 | } | ||
79 | |||
80 | /* --sort pid */ | ||
81 | |||
82 | int64_t | ||
83 | sort__thread_cmp(struct hist_entry *left, struct hist_entry *right) | ||
84 | { | ||
85 | return right->thread->pid - left->thread->pid; | ||
86 | } | ||
87 | |||
88 | int repsep_fprintf(FILE *fp, const char *fmt, ...) | ||
89 | { | ||
90 | int n; | ||
91 | va_list ap; | ||
92 | |||
93 | va_start(ap, fmt); | ||
94 | if (!field_sep) | ||
95 | n = vfprintf(fp, fmt, ap); | ||
96 | else { | ||
97 | char *bf = NULL; | ||
98 | n = vasprintf(&bf, fmt, ap); | ||
99 | if (n > 0) { | ||
100 | char *sep = bf; | ||
101 | |||
102 | while (1) { | ||
103 | sep = strchr(sep, *field_sep); | ||
104 | if (sep == NULL) | ||
105 | break; | ||
106 | *sep = '.'; | ||
107 | } | ||
108 | } | ||
109 | fputs(bf, fp); | ||
110 | free(bf); | ||
111 | } | ||
112 | va_end(ap); | ||
113 | return n; | ||
114 | } | ||
115 | |||
116 | size_t | ||
117 | sort__thread_print(FILE *fp, struct hist_entry *self, unsigned int width) | ||
118 | { | ||
119 | return repsep_fprintf(fp, "%*s:%5d", width - 6, | ||
120 | self->thread->comm ?: "", self->thread->pid); | ||
121 | } | ||
122 | |||
123 | size_t | ||
124 | sort__comm_print(FILE *fp, struct hist_entry *self, unsigned int width) | ||
125 | { | ||
126 | return repsep_fprintf(fp, "%*s", width, self->thread->comm); | ||
127 | } | ||
128 | |||
129 | /* --sort dso */ | ||
130 | |||
131 | int64_t | ||
132 | sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) | ||
133 | { | ||
134 | struct dso *dso_l = left->map ? left->map->dso : NULL; | ||
135 | struct dso *dso_r = right->map ? right->map->dso : NULL; | ||
136 | const char *dso_name_l, *dso_name_r; | ||
137 | |||
138 | if (!dso_l || !dso_r) | ||
139 | return cmp_null(dso_l, dso_r); | ||
140 | |||
141 | if (verbose) { | ||
142 | dso_name_l = dso_l->long_name; | ||
143 | dso_name_r = dso_r->long_name; | ||
144 | } else { | ||
145 | dso_name_l = dso_l->short_name; | ||
146 | dso_name_r = dso_r->short_name; | ||
147 | } | ||
148 | |||
149 | return strcmp(dso_name_l, dso_name_r); | ||
150 | } | ||
151 | |||
152 | size_t | ||
153 | sort__dso_print(FILE *fp, struct hist_entry *self, unsigned int width) | ||
154 | { | ||
155 | if (self->map && self->map->dso) { | ||
156 | const char *dso_name = !verbose ? self->map->dso->short_name : | ||
157 | self->map->dso->long_name; | ||
158 | return repsep_fprintf(fp, "%-*s", width, dso_name); | ||
159 | } | ||
160 | |||
161 | return repsep_fprintf(fp, "%*llx", width, (u64)self->ip); | ||
162 | } | ||
163 | |||
164 | /* --sort symbol */ | ||
165 | |||
166 | int64_t | ||
167 | sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) | ||
168 | { | ||
169 | u64 ip_l, ip_r; | ||
170 | |||
171 | if (left->sym == right->sym) | ||
172 | return 0; | ||
173 | |||
174 | ip_l = left->sym ? left->sym->start : left->ip; | ||
175 | ip_r = right->sym ? right->sym->start : right->ip; | ||
176 | |||
177 | return (int64_t)(ip_r - ip_l); | ||
178 | } | ||
179 | |||
180 | |||
181 | size_t | ||
182 | sort__sym_print(FILE *fp, struct hist_entry *self, unsigned int width __used) | ||
183 | { | ||
184 | size_t ret = 0; | ||
185 | |||
186 | if (verbose) { | ||
187 | char o = self->map ? dso__symtab_origin(self->map->dso) : '!'; | ||
188 | ret += repsep_fprintf(fp, "%#018llx %c ", (u64)self->ip, o); | ||
189 | } | ||
190 | |||
191 | ret += repsep_fprintf(fp, "[%c] ", self->level); | ||
192 | if (self->sym) | ||
193 | ret += repsep_fprintf(fp, "%s", self->sym->name); | ||
194 | else | ||
195 | ret += repsep_fprintf(fp, "%#016llx", (u64)self->ip); | ||
196 | |||
197 | return ret; | ||
198 | } | ||
199 | |||
200 | /* --sort comm */ | ||
201 | |||
202 | int64_t | ||
203 | sort__comm_cmp(struct hist_entry *left, struct hist_entry *right) | ||
204 | { | ||
205 | return right->thread->pid - left->thread->pid; | ||
206 | } | ||
207 | |||
208 | int64_t | ||
209 | sort__comm_collapse(struct hist_entry *left, struct hist_entry *right) | ||
210 | { | ||
211 | char *comm_l = left->thread->comm; | ||
212 | char *comm_r = right->thread->comm; | ||
213 | |||
214 | if (!comm_l || !comm_r) | ||
215 | return cmp_null(comm_l, comm_r); | ||
216 | |||
217 | return strcmp(comm_l, comm_r); | ||
218 | } | ||
219 | |||
220 | /* --sort parent */ | ||
221 | |||
222 | int64_t | ||
223 | sort__parent_cmp(struct hist_entry *left, struct hist_entry *right) | ||
224 | { | ||
225 | struct symbol *sym_l = left->parent; | ||
226 | struct symbol *sym_r = right->parent; | ||
227 | |||
228 | if (!sym_l || !sym_r) | ||
229 | return cmp_null(sym_l, sym_r); | ||
230 | |||
231 | return strcmp(sym_l->name, sym_r->name); | ||
232 | } | ||
233 | |||
234 | size_t | ||
235 | sort__parent_print(FILE *fp, struct hist_entry *self, unsigned int width) | ||
236 | { | ||
237 | return repsep_fprintf(fp, "%-*s", width, | ||
238 | self->parent ? self->parent->name : "[other]"); | ||
239 | } | ||
240 | |||
241 | int sort_dimension__add(const char *tok) | ||
242 | { | ||
243 | unsigned int i; | ||
244 | |||
245 | for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) { | ||
246 | struct sort_dimension *sd = &sort_dimensions[i]; | ||
247 | |||
248 | if (sd->taken) | ||
249 | continue; | ||
250 | |||
251 | if (strncasecmp(tok, sd->name, strlen(tok))) | ||
252 | continue; | ||
253 | |||
254 | if (sd->entry->collapse) | ||
255 | sort__need_collapse = 1; | ||
256 | |||
257 | if (sd->entry == &sort_parent) { | ||
258 | int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED); | ||
259 | if (ret) { | ||
260 | char err[BUFSIZ]; | ||
261 | |||
262 | regerror(ret, &parent_regex, err, sizeof(err)); | ||
263 | fprintf(stderr, "Invalid regex: %s\n%s", | ||
264 | parent_pattern, err); | ||
265 | exit(-1); | ||
266 | } | ||
267 | sort__has_parent = 1; | ||
268 | } | ||
269 | |||
270 | if (list_empty(&hist_entry__sort_list)) { | ||
271 | if (!strcmp(sd->name, "pid")) | ||
272 | sort__first_dimension = SORT_PID; | ||
273 | else if (!strcmp(sd->name, "comm")) | ||
274 | sort__first_dimension = SORT_COMM; | ||
275 | else if (!strcmp(sd->name, "dso")) | ||
276 | sort__first_dimension = SORT_DSO; | ||
277 | else if (!strcmp(sd->name, "symbol")) | ||
278 | sort__first_dimension = SORT_SYM; | ||
279 | else if (!strcmp(sd->name, "parent")) | ||
280 | sort__first_dimension = SORT_PARENT; | ||
281 | } | ||
282 | |||
283 | list_add_tail(&sd->entry->list, &hist_entry__sort_list); | ||
284 | sd->taken = 1; | ||
285 | |||
286 | return 0; | ||
287 | } | ||
288 | |||
289 | return -ESRCH; | ||
290 | } | ||