diff options
Diffstat (limited to 'tools/perf/util/sort.c')
-rw-r--r-- | tools/perf/util/sort.c | 245 |
1 files changed, 129 insertions, 116 deletions
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index cfd1c0feb32d..d41926cb9e3f 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c | |||
@@ -60,7 +60,7 @@ sort__thread_cmp(struct hist_entry *left, struct hist_entry *right) | |||
60 | static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf, | 60 | static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf, |
61 | size_t size, unsigned int width) | 61 | size_t size, unsigned int width) |
62 | { | 62 | { |
63 | return repsep_snprintf(bf, size, "%*s:%5d", width, | 63 | return repsep_snprintf(bf, size, "%*s:%5d", width - 6, |
64 | self->thread->comm ?: "", self->thread->pid); | 64 | self->thread->comm ?: "", self->thread->pid); |
65 | } | 65 | } |
66 | 66 | ||
@@ -97,6 +97,16 @@ static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf, | |||
97 | return repsep_snprintf(bf, size, "%*s", width, self->thread->comm); | 97 | return repsep_snprintf(bf, size, "%*s", width, self->thread->comm); |
98 | } | 98 | } |
99 | 99 | ||
100 | struct sort_entry sort_comm = { | ||
101 | .se_header = "Command", | ||
102 | .se_cmp = sort__comm_cmp, | ||
103 | .se_collapse = sort__comm_collapse, | ||
104 | .se_snprintf = hist_entry__comm_snprintf, | ||
105 | .se_width_idx = HISTC_COMM, | ||
106 | }; | ||
107 | |||
108 | /* --sort dso */ | ||
109 | |||
100 | static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r) | 110 | static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r) |
101 | { | 111 | { |
102 | struct dso *dso_l = map_l ? map_l->dso : NULL; | 112 | struct dso *dso_l = map_l ? map_l->dso : NULL; |
@@ -117,40 +127,12 @@ static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r) | |||
117 | return strcmp(dso_name_l, dso_name_r); | 127 | return strcmp(dso_name_l, dso_name_r); |
118 | } | 128 | } |
119 | 129 | ||
120 | struct sort_entry sort_comm = { | ||
121 | .se_header = "Command", | ||
122 | .se_cmp = sort__comm_cmp, | ||
123 | .se_collapse = sort__comm_collapse, | ||
124 | .se_snprintf = hist_entry__comm_snprintf, | ||
125 | .se_width_idx = HISTC_COMM, | ||
126 | }; | ||
127 | |||
128 | /* --sort dso */ | ||
129 | |||
130 | static int64_t | 130 | static int64_t |
131 | sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) | 131 | sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) |
132 | { | 132 | { |
133 | return _sort__dso_cmp(left->ms.map, right->ms.map); | 133 | return _sort__dso_cmp(left->ms.map, right->ms.map); |
134 | } | 134 | } |
135 | 135 | ||
136 | |||
137 | static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r, | ||
138 | u64 ip_l, u64 ip_r) | ||
139 | { | ||
140 | if (!sym_l || !sym_r) | ||
141 | return cmp_null(sym_l, sym_r); | ||
142 | |||
143 | if (sym_l == sym_r) | ||
144 | return 0; | ||
145 | |||
146 | if (sym_l) | ||
147 | ip_l = sym_l->start; | ||
148 | if (sym_r) | ||
149 | ip_r = sym_r->start; | ||
150 | |||
151 | return (int64_t)(ip_r - ip_l); | ||
152 | } | ||
153 | |||
154 | static int _hist_entry__dso_snprintf(struct map *map, char *bf, | 136 | static int _hist_entry__dso_snprintf(struct map *map, char *bf, |
155 | size_t size, unsigned int width) | 137 | size_t size, unsigned int width) |
156 | { | 138 | { |
@@ -169,9 +151,43 @@ static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf, | |||
169 | return _hist_entry__dso_snprintf(self->ms.map, bf, size, width); | 151 | return _hist_entry__dso_snprintf(self->ms.map, bf, size, width); |
170 | } | 152 | } |
171 | 153 | ||
154 | struct sort_entry sort_dso = { | ||
155 | .se_header = "Shared Object", | ||
156 | .se_cmp = sort__dso_cmp, | ||
157 | .se_snprintf = hist_entry__dso_snprintf, | ||
158 | .se_width_idx = HISTC_DSO, | ||
159 | }; | ||
160 | |||
161 | /* --sort symbol */ | ||
162 | |||
163 | static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r) | ||
164 | { | ||
165 | u64 ip_l, ip_r; | ||
166 | |||
167 | if (!sym_l || !sym_r) | ||
168 | return cmp_null(sym_l, sym_r); | ||
169 | |||
170 | if (sym_l == sym_r) | ||
171 | return 0; | ||
172 | |||
173 | ip_l = sym_l->start; | ||
174 | ip_r = sym_r->start; | ||
175 | |||
176 | return (int64_t)(ip_r - ip_l); | ||
177 | } | ||
178 | |||
179 | static int64_t | ||
180 | sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) | ||
181 | { | ||
182 | if (!left->ms.sym && !right->ms.sym) | ||
183 | return right->level - left->level; | ||
184 | |||
185 | return _sort__sym_cmp(left->ms.sym, right->ms.sym); | ||
186 | } | ||
187 | |||
172 | static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym, | 188 | static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym, |
173 | u64 ip, char level, char *bf, size_t size, | 189 | u64 ip, char level, char *bf, size_t size, |
174 | unsigned int width __maybe_unused) | 190 | unsigned int width) |
175 | { | 191 | { |
176 | size_t ret = 0; | 192 | size_t ret = 0; |
177 | 193 | ||
@@ -197,43 +213,13 @@ static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym, | |||
197 | return ret; | 213 | return ret; |
198 | } | 214 | } |
199 | 215 | ||
200 | |||
201 | struct sort_entry sort_dso = { | ||
202 | .se_header = "Shared Object", | ||
203 | .se_cmp = sort__dso_cmp, | ||
204 | .se_snprintf = hist_entry__dso_snprintf, | ||
205 | .se_width_idx = HISTC_DSO, | ||
206 | }; | ||
207 | |||
208 | static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, | 216 | static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, |
209 | size_t size, | 217 | size_t size, unsigned int width) |
210 | unsigned int width __maybe_unused) | ||
211 | { | 218 | { |
212 | return _hist_entry__sym_snprintf(self->ms.map, self->ms.sym, self->ip, | 219 | return _hist_entry__sym_snprintf(self->ms.map, self->ms.sym, self->ip, |
213 | self->level, bf, size, width); | 220 | self->level, bf, size, width); |
214 | } | 221 | } |
215 | 222 | ||
216 | /* --sort symbol */ | ||
217 | static int64_t | ||
218 | sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) | ||
219 | { | ||
220 | u64 ip_l, ip_r; | ||
221 | |||
222 | if (!left->ms.sym && !right->ms.sym) | ||
223 | return right->level - left->level; | ||
224 | |||
225 | if (!left->ms.sym || !right->ms.sym) | ||
226 | return cmp_null(left->ms.sym, right->ms.sym); | ||
227 | |||
228 | if (left->ms.sym == right->ms.sym) | ||
229 | return 0; | ||
230 | |||
231 | ip_l = left->ms.sym->start; | ||
232 | ip_r = right->ms.sym->start; | ||
233 | |||
234 | return _sort__sym_cmp(left->ms.sym, right->ms.sym, ip_l, ip_r); | ||
235 | } | ||
236 | |||
237 | struct sort_entry sort_sym = { | 223 | struct sort_entry sort_sym = { |
238 | .se_header = "Symbol", | 224 | .se_header = "Symbol", |
239 | .se_cmp = sort__sym_cmp, | 225 | .se_cmp = sort__sym_cmp, |
@@ -253,7 +239,7 @@ static int hist_entry__srcline_snprintf(struct hist_entry *self, char *bf, | |||
253 | size_t size, | 239 | size_t size, |
254 | unsigned int width __maybe_unused) | 240 | unsigned int width __maybe_unused) |
255 | { | 241 | { |
256 | FILE *fp; | 242 | FILE *fp = NULL; |
257 | char cmd[PATH_MAX + 2], *path = self->srcline, *nl; | 243 | char cmd[PATH_MAX + 2], *path = self->srcline, *nl; |
258 | size_t line_len; | 244 | size_t line_len; |
259 | 245 | ||
@@ -274,7 +260,6 @@ static int hist_entry__srcline_snprintf(struct hist_entry *self, char *bf, | |||
274 | 260 | ||
275 | if (getline(&path, &line_len, fp) < 0 || !line_len) | 261 | if (getline(&path, &line_len, fp) < 0 || !line_len) |
276 | goto out_ip; | 262 | goto out_ip; |
277 | fclose(fp); | ||
278 | self->srcline = strdup(path); | 263 | self->srcline = strdup(path); |
279 | if (self->srcline == NULL) | 264 | if (self->srcline == NULL) |
280 | goto out_ip; | 265 | goto out_ip; |
@@ -284,8 +269,12 @@ static int hist_entry__srcline_snprintf(struct hist_entry *self, char *bf, | |||
284 | *nl = '\0'; | 269 | *nl = '\0'; |
285 | path = self->srcline; | 270 | path = self->srcline; |
286 | out_path: | 271 | out_path: |
272 | if (fp) | ||
273 | pclose(fp); | ||
287 | return repsep_snprintf(bf, size, "%s", path); | 274 | return repsep_snprintf(bf, size, "%s", path); |
288 | out_ip: | 275 | out_ip: |
276 | if (fp) | ||
277 | pclose(fp); | ||
289 | return repsep_snprintf(bf, size, "%-#*llx", BITS_PER_LONG / 4, self->ip); | 278 | return repsep_snprintf(bf, size, "%-#*llx", BITS_PER_LONG / 4, self->ip); |
290 | } | 279 | } |
291 | 280 | ||
@@ -335,7 +324,7 @@ sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right) | |||
335 | static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf, | 324 | static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf, |
336 | size_t size, unsigned int width) | 325 | size_t size, unsigned int width) |
337 | { | 326 | { |
338 | return repsep_snprintf(bf, size, "%-*d", width, self->cpu); | 327 | return repsep_snprintf(bf, size, "%*d", width, self->cpu); |
339 | } | 328 | } |
340 | 329 | ||
341 | struct sort_entry sort_cpu = { | 330 | struct sort_entry sort_cpu = { |
@@ -345,6 +334,8 @@ struct sort_entry sort_cpu = { | |||
345 | .se_width_idx = HISTC_CPU, | 334 | .se_width_idx = HISTC_CPU, |
346 | }; | 335 | }; |
347 | 336 | ||
337 | /* sort keys for branch stacks */ | ||
338 | |||
348 | static int64_t | 339 | static int64_t |
349 | sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right) | 340 | sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right) |
350 | { | 341 | { |
@@ -359,13 +350,6 @@ static int hist_entry__dso_from_snprintf(struct hist_entry *self, char *bf, | |||
359 | bf, size, width); | 350 | bf, size, width); |
360 | } | 351 | } |
361 | 352 | ||
362 | struct sort_entry sort_dso_from = { | ||
363 | .se_header = "Source Shared Object", | ||
364 | .se_cmp = sort__dso_from_cmp, | ||
365 | .se_snprintf = hist_entry__dso_from_snprintf, | ||
366 | .se_width_idx = HISTC_DSO_FROM, | ||
367 | }; | ||
368 | |||
369 | static int64_t | 353 | static int64_t |
370 | sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right) | 354 | sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right) |
371 | { | 355 | { |
@@ -389,8 +373,7 @@ sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right) | |||
389 | if (!from_l->sym && !from_r->sym) | 373 | if (!from_l->sym && !from_r->sym) |
390 | return right->level - left->level; | 374 | return right->level - left->level; |
391 | 375 | ||
392 | return _sort__sym_cmp(from_l->sym, from_r->sym, from_l->addr, | 376 | return _sort__sym_cmp(from_l->sym, from_r->sym); |
393 | from_r->addr); | ||
394 | } | 377 | } |
395 | 378 | ||
396 | static int64_t | 379 | static int64_t |
@@ -402,12 +385,11 @@ sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right) | |||
402 | if (!to_l->sym && !to_r->sym) | 385 | if (!to_l->sym && !to_r->sym) |
403 | return right->level - left->level; | 386 | return right->level - left->level; |
404 | 387 | ||
405 | return _sort__sym_cmp(to_l->sym, to_r->sym, to_l->addr, to_r->addr); | 388 | return _sort__sym_cmp(to_l->sym, to_r->sym); |
406 | } | 389 | } |
407 | 390 | ||
408 | static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf, | 391 | static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf, |
409 | size_t size, | 392 | size_t size, unsigned int width) |
410 | unsigned int width __maybe_unused) | ||
411 | { | 393 | { |
412 | struct addr_map_symbol *from = &self->branch_info->from; | 394 | struct addr_map_symbol *from = &self->branch_info->from; |
413 | return _hist_entry__sym_snprintf(from->map, from->sym, from->addr, | 395 | return _hist_entry__sym_snprintf(from->map, from->sym, from->addr, |
@@ -416,8 +398,7 @@ static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf, | |||
416 | } | 398 | } |
417 | 399 | ||
418 | static int hist_entry__sym_to_snprintf(struct hist_entry *self, char *bf, | 400 | static int hist_entry__sym_to_snprintf(struct hist_entry *self, char *bf, |
419 | size_t size, | 401 | size_t size, unsigned int width) |
420 | unsigned int width __maybe_unused) | ||
421 | { | 402 | { |
422 | struct addr_map_symbol *to = &self->branch_info->to; | 403 | struct addr_map_symbol *to = &self->branch_info->to; |
423 | return _hist_entry__sym_snprintf(to->map, to->sym, to->addr, | 404 | return _hist_entry__sym_snprintf(to->map, to->sym, to->addr, |
@@ -425,6 +406,13 @@ static int hist_entry__sym_to_snprintf(struct hist_entry *self, char *bf, | |||
425 | 406 | ||
426 | } | 407 | } |
427 | 408 | ||
409 | struct sort_entry sort_dso_from = { | ||
410 | .se_header = "Source Shared Object", | ||
411 | .se_cmp = sort__dso_from_cmp, | ||
412 | .se_snprintf = hist_entry__dso_from_snprintf, | ||
413 | .se_width_idx = HISTC_DSO_FROM, | ||
414 | }; | ||
415 | |||
428 | struct sort_entry sort_dso_to = { | 416 | struct sort_entry sort_dso_to = { |
429 | .se_header = "Target Shared Object", | 417 | .se_header = "Target Shared Object", |
430 | .se_cmp = sort__dso_to_cmp, | 418 | .se_cmp = sort__dso_to_cmp, |
@@ -484,30 +472,40 @@ struct sort_dimension { | |||
484 | 472 | ||
485 | #define DIM(d, n, func) [d] = { .name = n, .entry = &(func) } | 473 | #define DIM(d, n, func) [d] = { .name = n, .entry = &(func) } |
486 | 474 | ||
487 | static struct sort_dimension sort_dimensions[] = { | 475 | static struct sort_dimension common_sort_dimensions[] = { |
488 | DIM(SORT_PID, "pid", sort_thread), | 476 | DIM(SORT_PID, "pid", sort_thread), |
489 | DIM(SORT_COMM, "comm", sort_comm), | 477 | DIM(SORT_COMM, "comm", sort_comm), |
490 | DIM(SORT_DSO, "dso", sort_dso), | 478 | DIM(SORT_DSO, "dso", sort_dso), |
491 | DIM(SORT_DSO_FROM, "dso_from", sort_dso_from), | ||
492 | DIM(SORT_DSO_TO, "dso_to", sort_dso_to), | ||
493 | DIM(SORT_SYM, "symbol", sort_sym), | 479 | DIM(SORT_SYM, "symbol", sort_sym), |
494 | DIM(SORT_SYM_TO, "symbol_from", sort_sym_from), | ||
495 | DIM(SORT_SYM_FROM, "symbol_to", sort_sym_to), | ||
496 | DIM(SORT_PARENT, "parent", sort_parent), | 480 | DIM(SORT_PARENT, "parent", sort_parent), |
497 | DIM(SORT_CPU, "cpu", sort_cpu), | 481 | DIM(SORT_CPU, "cpu", sort_cpu), |
498 | DIM(SORT_MISPREDICT, "mispredict", sort_mispredict), | ||
499 | DIM(SORT_SRCLINE, "srcline", sort_srcline), | 482 | DIM(SORT_SRCLINE, "srcline", sort_srcline), |
500 | }; | 483 | }; |
501 | 484 | ||
485 | #undef DIM | ||
486 | |||
487 | #define DIM(d, n, func) [d - __SORT_BRANCH_STACK] = { .name = n, .entry = &(func) } | ||
488 | |||
489 | static struct sort_dimension bstack_sort_dimensions[] = { | ||
490 | DIM(SORT_DSO_FROM, "dso_from", sort_dso_from), | ||
491 | DIM(SORT_DSO_TO, "dso_to", sort_dso_to), | ||
492 | DIM(SORT_SYM_FROM, "symbol_from", sort_sym_from), | ||
493 | DIM(SORT_SYM_TO, "symbol_to", sort_sym_to), | ||
494 | DIM(SORT_MISPREDICT, "mispredict", sort_mispredict), | ||
495 | }; | ||
496 | |||
497 | #undef DIM | ||
498 | |||
502 | int sort_dimension__add(const char *tok) | 499 | int sort_dimension__add(const char *tok) |
503 | { | 500 | { |
504 | unsigned int i; | 501 | unsigned int i; |
505 | 502 | ||
506 | for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) { | 503 | for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) { |
507 | struct sort_dimension *sd = &sort_dimensions[i]; | 504 | struct sort_dimension *sd = &common_sort_dimensions[i]; |
508 | 505 | ||
509 | if (strncasecmp(tok, sd->name, strlen(tok))) | 506 | if (strncasecmp(tok, sd->name, strlen(tok))) |
510 | continue; | 507 | continue; |
508 | |||
511 | if (sd->entry == &sort_parent) { | 509 | if (sd->entry == &sort_parent) { |
512 | int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED); | 510 | int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED); |
513 | if (ret) { | 511 | if (ret) { |
@@ -518,9 +516,7 @@ int sort_dimension__add(const char *tok) | |||
518 | return -EINVAL; | 516 | return -EINVAL; |
519 | } | 517 | } |
520 | sort__has_parent = 1; | 518 | sort__has_parent = 1; |
521 | } else if (sd->entry == &sort_sym || | 519 | } else if (sd->entry == &sort_sym) { |
522 | sd->entry == &sort_sym_from || | ||
523 | sd->entry == &sort_sym_to) { | ||
524 | sort__has_sym = 1; | 520 | sort__has_sym = 1; |
525 | } | 521 | } |
526 | 522 | ||
@@ -530,52 +526,69 @@ int sort_dimension__add(const char *tok) | |||
530 | if (sd->entry->se_collapse) | 526 | if (sd->entry->se_collapse) |
531 | sort__need_collapse = 1; | 527 | sort__need_collapse = 1; |
532 | 528 | ||
533 | if (list_empty(&hist_entry__sort_list)) { | 529 | if (list_empty(&hist_entry__sort_list)) |
534 | if (!strcmp(sd->name, "pid")) | 530 | sort__first_dimension = i; |
535 | sort__first_dimension = SORT_PID; | ||
536 | else if (!strcmp(sd->name, "comm")) | ||
537 | sort__first_dimension = SORT_COMM; | ||
538 | else if (!strcmp(sd->name, "dso")) | ||
539 | sort__first_dimension = SORT_DSO; | ||
540 | else if (!strcmp(sd->name, "symbol")) | ||
541 | sort__first_dimension = SORT_SYM; | ||
542 | else if (!strcmp(sd->name, "parent")) | ||
543 | sort__first_dimension = SORT_PARENT; | ||
544 | else if (!strcmp(sd->name, "cpu")) | ||
545 | sort__first_dimension = SORT_CPU; | ||
546 | else if (!strcmp(sd->name, "symbol_from")) | ||
547 | sort__first_dimension = SORT_SYM_FROM; | ||
548 | else if (!strcmp(sd->name, "symbol_to")) | ||
549 | sort__first_dimension = SORT_SYM_TO; | ||
550 | else if (!strcmp(sd->name, "dso_from")) | ||
551 | sort__first_dimension = SORT_DSO_FROM; | ||
552 | else if (!strcmp(sd->name, "dso_to")) | ||
553 | sort__first_dimension = SORT_DSO_TO; | ||
554 | else if (!strcmp(sd->name, "mispredict")) | ||
555 | sort__first_dimension = SORT_MISPREDICT; | ||
556 | } | ||
557 | 531 | ||
558 | list_add_tail(&sd->entry->list, &hist_entry__sort_list); | 532 | list_add_tail(&sd->entry->list, &hist_entry__sort_list); |
559 | sd->taken = 1; | 533 | sd->taken = 1; |
560 | 534 | ||
561 | return 0; | 535 | return 0; |
562 | } | 536 | } |
537 | |||
538 | for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) { | ||
539 | struct sort_dimension *sd = &bstack_sort_dimensions[i]; | ||
540 | |||
541 | if (strncasecmp(tok, sd->name, strlen(tok))) | ||
542 | continue; | ||
543 | |||
544 | if (sort__branch_mode != 1) | ||
545 | return -EINVAL; | ||
546 | |||
547 | if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to) | ||
548 | sort__has_sym = 1; | ||
549 | |||
550 | if (sd->taken) | ||
551 | return 0; | ||
552 | |||
553 | if (sd->entry->se_collapse) | ||
554 | sort__need_collapse = 1; | ||
555 | |||
556 | if (list_empty(&hist_entry__sort_list)) | ||
557 | sort__first_dimension = i + __SORT_BRANCH_STACK; | ||
558 | |||
559 | list_add_tail(&sd->entry->list, &hist_entry__sort_list); | ||
560 | sd->taken = 1; | ||
561 | |||
562 | return 0; | ||
563 | } | ||
564 | |||
563 | return -ESRCH; | 565 | return -ESRCH; |
564 | } | 566 | } |
565 | 567 | ||
566 | void setup_sorting(const char * const usagestr[], const struct option *opts) | 568 | int setup_sorting(void) |
567 | { | 569 | { |
568 | char *tmp, *tok, *str = strdup(sort_order); | 570 | char *tmp, *tok, *str = strdup(sort_order); |
571 | int ret = 0; | ||
572 | |||
573 | if (str == NULL) { | ||
574 | error("Not enough memory to setup sort keys"); | ||
575 | return -ENOMEM; | ||
576 | } | ||
569 | 577 | ||
570 | for (tok = strtok_r(str, ", ", &tmp); | 578 | for (tok = strtok_r(str, ", ", &tmp); |
571 | tok; tok = strtok_r(NULL, ", ", &tmp)) { | 579 | tok; tok = strtok_r(NULL, ", ", &tmp)) { |
572 | if (sort_dimension__add(tok) < 0) { | 580 | ret = sort_dimension__add(tok); |
581 | if (ret == -EINVAL) { | ||
582 | error("Invalid --sort key: `%s'", tok); | ||
583 | break; | ||
584 | } else if (ret == -ESRCH) { | ||
573 | error("Unknown --sort key: `%s'", tok); | 585 | error("Unknown --sort key: `%s'", tok); |
574 | usage_with_options(usagestr, opts); | 586 | break; |
575 | } | 587 | } |
576 | } | 588 | } |
577 | 589 | ||
578 | free(str); | 590 | free(str); |
591 | return ret; | ||
579 | } | 592 | } |
580 | 593 | ||
581 | void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list, | 594 | void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list, |