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