5 const char default_parent_pattern
[] = "^sys_|^do_page_fault";
6 const char *parent_pattern
= default_parent_pattern
;
7 const char default_sort_order
[] = "comm,dso,symbol";
8 const char *sort_order
= default_sort_order
;
9 int sort__need_collapse
= 0;
10 int sort__has_parent
= 0;
11 int sort__branch_mode
= -1; /* -1 = means not set */
13 enum sort_type sort__first_dimension
;
17 LIST_HEAD(hist_entry__sort_list
);
19 static int repsep_snprintf(char *bf
, size_t size
, const char *fmt
, ...)
25 n
= vsnprintf(bf
, size
, fmt
, ap
);
26 if (field_sep
&& n
> 0) {
30 sep
= strchr(sep
, *field_sep
);
43 static int64_t cmp_null(void *l
, void *r
)
56 sort__thread_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
58 return right
->thread
->pid
- left
->thread
->pid
;
61 static int hist_entry__thread_snprintf(struct hist_entry
*self
, char *bf
,
62 size_t size
, unsigned int width
)
64 return repsep_snprintf(bf
, size
, "%*s:%5d", width
,
65 self
->thread
->comm
?: "", self
->thread
->pid
);
68 struct sort_entry sort_thread
= {
69 .se_header
= "Command: Pid",
70 .se_cmp
= sort__thread_cmp
,
71 .se_snprintf
= hist_entry__thread_snprintf
,
72 .se_width_idx
= HISTC_THREAD
,
78 sort__comm_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
80 return right
->thread
->pid
- left
->thread
->pid
;
84 sort__comm_collapse(struct hist_entry
*left
, struct hist_entry
*right
)
86 char *comm_l
= left
->thread
->comm
;
87 char *comm_r
= right
->thread
->comm
;
89 if (!comm_l
|| !comm_r
)
90 return cmp_null(comm_l
, comm_r
);
92 return strcmp(comm_l
, comm_r
);
95 static int hist_entry__comm_snprintf(struct hist_entry
*self
, char *bf
,
96 size_t size
, unsigned int width
)
98 return repsep_snprintf(bf
, size
, "%*s", width
, self
->thread
->comm
);
101 static int64_t _sort__dso_cmp(struct map
*map_l
, struct map
*map_r
)
103 struct dso
*dso_l
= map_l
? map_l
->dso
: NULL
;
104 struct dso
*dso_r
= map_r
? map_r
->dso
: NULL
;
105 const char *dso_name_l
, *dso_name_r
;
107 if (!dso_l
|| !dso_r
)
108 return cmp_null(dso_l
, dso_r
);
111 dso_name_l
= dso_l
->long_name
;
112 dso_name_r
= dso_r
->long_name
;
114 dso_name_l
= dso_l
->short_name
;
115 dso_name_r
= dso_r
->short_name
;
118 return strcmp(dso_name_l
, dso_name_r
);
121 struct sort_entry sort_comm
= {
122 .se_header
= "Command",
123 .se_cmp
= sort__comm_cmp
,
124 .se_collapse
= sort__comm_collapse
,
125 .se_snprintf
= hist_entry__comm_snprintf
,
126 .se_width_idx
= HISTC_COMM
,
132 sort__dso_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
134 return _sort__dso_cmp(left
->ms
.map
, right
->ms
.map
);
138 static int64_t _sort__sym_cmp(struct symbol
*sym_l
, struct symbol
*sym_r
,
141 if (!sym_l
|| !sym_r
)
142 return cmp_null(sym_l
, sym_r
);
152 return (int64_t)(ip_r
- ip_l
);
155 static int _hist_entry__dso_snprintf(struct map
*map
, char *bf
,
156 size_t size
, unsigned int width
)
158 if (map
&& map
->dso
) {
159 const char *dso_name
= !verbose
? map
->dso
->short_name
:
161 return repsep_snprintf(bf
, size
, "%-*s", width
, dso_name
);
164 return repsep_snprintf(bf
, size
, "%-*s", width
, "[unknown]");
167 static int hist_entry__dso_snprintf(struct hist_entry
*self
, char *bf
,
168 size_t size
, unsigned int width
)
170 return _hist_entry__dso_snprintf(self
->ms
.map
, bf
, size
, width
);
173 static int _hist_entry__sym_snprintf(struct map
*map
, struct symbol
*sym
,
174 u64 ip
, char level
, char *bf
, size_t size
,
175 unsigned int width __used
)
180 char o
= map
? dso__symtab_origin(map
->dso
) : '!';
181 ret
+= repsep_snprintf(bf
, size
, "%-#*llx %c ",
182 BITS_PER_LONG
/ 4, ip
, o
);
185 ret
+= repsep_snprintf(bf
+ ret
, size
- ret
, "[%c] ", level
);
187 ret
+= repsep_snprintf(bf
+ ret
, size
- ret
, "%-*s",
191 size_t len
= BITS_PER_LONG
/ 4;
192 ret
+= repsep_snprintf(bf
+ ret
, size
- ret
, "%-#.*llx",
194 ret
+= repsep_snprintf(bf
+ ret
, size
- ret
, "%-*s",
202 struct sort_entry sort_dso
= {
203 .se_header
= "Shared Object",
204 .se_cmp
= sort__dso_cmp
,
205 .se_snprintf
= hist_entry__dso_snprintf
,
206 .se_width_idx
= HISTC_DSO
,
209 static int hist_entry__sym_snprintf(struct hist_entry
*self
, char *bf
,
210 size_t size
, unsigned int width __used
)
212 return _hist_entry__sym_snprintf(self
->ms
.map
, self
->ms
.sym
, self
->ip
,
213 self
->level
, bf
, size
, width
);
218 sort__sym_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
222 if (!left
->ms
.sym
&& !right
->ms
.sym
)
223 return right
->level
- left
->level
;
225 if (!left
->ms
.sym
|| !right
->ms
.sym
)
226 return cmp_null(left
->ms
.sym
, right
->ms
.sym
);
228 if (left
->ms
.sym
== right
->ms
.sym
)
231 ip_l
= left
->ms
.sym
->start
;
232 ip_r
= right
->ms
.sym
->start
;
234 return _sort__sym_cmp(left
->ms
.sym
, right
->ms
.sym
, ip_l
, ip_r
);
237 struct sort_entry sort_sym
= {
238 .se_header
= "Symbol",
239 .se_cmp
= sort__sym_cmp
,
240 .se_snprintf
= hist_entry__sym_snprintf
,
241 .se_width_idx
= HISTC_SYMBOL
,
247 sort__parent_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
249 struct symbol
*sym_l
= left
->parent
;
250 struct symbol
*sym_r
= right
->parent
;
252 if (!sym_l
|| !sym_r
)
253 return cmp_null(sym_l
, sym_r
);
255 return strcmp(sym_l
->name
, sym_r
->name
);
258 static int hist_entry__parent_snprintf(struct hist_entry
*self
, char *bf
,
259 size_t size
, unsigned int width
)
261 return repsep_snprintf(bf
, size
, "%-*s", width
,
262 self
->parent
? self
->parent
->name
: "[other]");
265 struct sort_entry sort_parent
= {
266 .se_header
= "Parent symbol",
267 .se_cmp
= sort__parent_cmp
,
268 .se_snprintf
= hist_entry__parent_snprintf
,
269 .se_width_idx
= HISTC_PARENT
,
275 sort__cpu_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
277 return right
->cpu
- left
->cpu
;
280 static int hist_entry__cpu_snprintf(struct hist_entry
*self
, char *bf
,
281 size_t size
, unsigned int width
)
283 return repsep_snprintf(bf
, size
, "%-*d", width
, self
->cpu
);
286 struct sort_entry sort_cpu
= {
288 .se_cmp
= sort__cpu_cmp
,
289 .se_snprintf
= hist_entry__cpu_snprintf
,
290 .se_width_idx
= HISTC_CPU
,
294 sort__dso_from_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
296 return _sort__dso_cmp(left
->branch_info
->from
.map
,
297 right
->branch_info
->from
.map
);
300 static int hist_entry__dso_from_snprintf(struct hist_entry
*self
, char *bf
,
301 size_t size
, unsigned int width
)
303 return _hist_entry__dso_snprintf(self
->branch_info
->from
.map
,
307 struct sort_entry sort_dso_from
= {
308 .se_header
= "Source Shared Object",
309 .se_cmp
= sort__dso_from_cmp
,
310 .se_snprintf
= hist_entry__dso_from_snprintf
,
311 .se_width_idx
= HISTC_DSO_FROM
,
315 sort__dso_to_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
317 return _sort__dso_cmp(left
->branch_info
->to
.map
,
318 right
->branch_info
->to
.map
);
321 static int hist_entry__dso_to_snprintf(struct hist_entry
*self
, char *bf
,
322 size_t size
, unsigned int width
)
324 return _hist_entry__dso_snprintf(self
->branch_info
->to
.map
,
329 sort__sym_from_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
331 struct addr_map_symbol
*from_l
= &left
->branch_info
->from
;
332 struct addr_map_symbol
*from_r
= &right
->branch_info
->from
;
334 if (!from_l
->sym
&& !from_r
->sym
)
335 return right
->level
- left
->level
;
337 return _sort__sym_cmp(from_l
->sym
, from_r
->sym
, from_l
->addr
,
342 sort__sym_to_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
344 struct addr_map_symbol
*to_l
= &left
->branch_info
->to
;
345 struct addr_map_symbol
*to_r
= &right
->branch_info
->to
;
347 if (!to_l
->sym
&& !to_r
->sym
)
348 return right
->level
- left
->level
;
350 return _sort__sym_cmp(to_l
->sym
, to_r
->sym
, to_l
->addr
, to_r
->addr
);
353 static int hist_entry__sym_from_snprintf(struct hist_entry
*self
, char *bf
,
354 size_t size
, unsigned int width __used
)
356 struct addr_map_symbol
*from
= &self
->branch_info
->from
;
357 return _hist_entry__sym_snprintf(from
->map
, from
->sym
, from
->addr
,
358 self
->level
, bf
, size
, width
);
362 static int hist_entry__sym_to_snprintf(struct hist_entry
*self
, char *bf
,
363 size_t size
, unsigned int width __used
)
365 struct addr_map_symbol
*to
= &self
->branch_info
->to
;
366 return _hist_entry__sym_snprintf(to
->map
, to
->sym
, to
->addr
,
367 self
->level
, bf
, size
, width
);
371 struct sort_entry sort_dso_to
= {
372 .se_header
= "Target Shared Object",
373 .se_cmp
= sort__dso_to_cmp
,
374 .se_snprintf
= hist_entry__dso_to_snprintf
,
375 .se_width_idx
= HISTC_DSO_TO
,
378 struct sort_entry sort_sym_from
= {
379 .se_header
= "Source Symbol",
380 .se_cmp
= sort__sym_from_cmp
,
381 .se_snprintf
= hist_entry__sym_from_snprintf
,
382 .se_width_idx
= HISTC_SYMBOL_FROM
,
385 struct sort_entry sort_sym_to
= {
386 .se_header
= "Target Symbol",
387 .se_cmp
= sort__sym_to_cmp
,
388 .se_snprintf
= hist_entry__sym_to_snprintf
,
389 .se_width_idx
= HISTC_SYMBOL_TO
,
393 sort__mispredict_cmp(struct hist_entry
*left
, struct hist_entry
*right
)
395 const unsigned char mp
= left
->branch_info
->flags
.mispred
!=
396 right
->branch_info
->flags
.mispred
;
397 const unsigned char p
= left
->branch_info
->flags
.predicted
!=
398 right
->branch_info
->flags
.predicted
;
403 static int hist_entry__mispredict_snprintf(struct hist_entry
*self
, char *bf
,
404 size_t size
, unsigned int width
){
405 static const char *out
= "N/A";
407 if (self
->branch_info
->flags
.predicted
)
409 else if (self
->branch_info
->flags
.mispred
)
412 return repsep_snprintf(bf
, size
, "%-*s", width
, out
);
415 struct sort_entry sort_mispredict
= {
416 .se_header
= "Branch Mispredicted",
417 .se_cmp
= sort__mispredict_cmp
,
418 .se_snprintf
= hist_entry__mispredict_snprintf
,
419 .se_width_idx
= HISTC_MISPREDICT
,
422 struct sort_dimension
{
424 struct sort_entry
*entry
;
428 #define DIM(d, n, func) [d] = { .name = n, .entry = &(func) }
430 static struct sort_dimension sort_dimensions
[] = {
431 DIM(SORT_PID
, "pid", sort_thread
),
432 DIM(SORT_COMM
, "comm", sort_comm
),
433 DIM(SORT_DSO
, "dso", sort_dso
),
434 DIM(SORT_DSO_FROM
, "dso_from", sort_dso_from
),
435 DIM(SORT_DSO_TO
, "dso_to", sort_dso_to
),
436 DIM(SORT_SYM
, "symbol", sort_sym
),
437 DIM(SORT_SYM_TO
, "symbol_from", sort_sym_from
),
438 DIM(SORT_SYM_FROM
, "symbol_to", sort_sym_to
),
439 DIM(SORT_PARENT
, "parent", sort_parent
),
440 DIM(SORT_CPU
, "cpu", sort_cpu
),
441 DIM(SORT_MISPREDICT
, "mispredict", sort_mispredict
),
444 int sort_dimension__add(const char *tok
)
448 for (i
= 0; i
< ARRAY_SIZE(sort_dimensions
); i
++) {
449 struct sort_dimension
*sd
= &sort_dimensions
[i
];
451 if (strncasecmp(tok
, sd
->name
, strlen(tok
)))
453 if (sd
->entry
== &sort_parent
) {
454 int ret
= regcomp(&parent_regex
, parent_pattern
, REG_EXTENDED
);
458 regerror(ret
, &parent_regex
, err
, sizeof(err
));
459 pr_err("Invalid regex: %s\n%s", parent_pattern
, err
);
462 sort__has_parent
= 1;
468 if (sd
->entry
->se_collapse
)
469 sort__need_collapse
= 1;
471 if (list_empty(&hist_entry__sort_list
)) {
472 if (!strcmp(sd
->name
, "pid"))
473 sort__first_dimension
= SORT_PID
;
474 else if (!strcmp(sd
->name
, "comm"))
475 sort__first_dimension
= SORT_COMM
;
476 else if (!strcmp(sd
->name
, "dso"))
477 sort__first_dimension
= SORT_DSO
;
478 else if (!strcmp(sd
->name
, "symbol"))
479 sort__first_dimension
= SORT_SYM
;
480 else if (!strcmp(sd
->name
, "parent"))
481 sort__first_dimension
= SORT_PARENT
;
482 else if (!strcmp(sd
->name
, "cpu"))
483 sort__first_dimension
= SORT_CPU
;
484 else if (!strcmp(sd
->name
, "symbol_from"))
485 sort__first_dimension
= SORT_SYM_FROM
;
486 else if (!strcmp(sd
->name
, "symbol_to"))
487 sort__first_dimension
= SORT_SYM_TO
;
488 else if (!strcmp(sd
->name
, "dso_from"))
489 sort__first_dimension
= SORT_DSO_FROM
;
490 else if (!strcmp(sd
->name
, "dso_to"))
491 sort__first_dimension
= SORT_DSO_TO
;
492 else if (!strcmp(sd
->name
, "mispredict"))
493 sort__first_dimension
= SORT_MISPREDICT
;
496 list_add_tail(&sd
->entry
->list
, &hist_entry__sort_list
);
504 void setup_sorting(const char * const usagestr
[], const struct option
*opts
)
506 char *tmp
, *tok
, *str
= strdup(sort_order
);
508 for (tok
= strtok_r(str
, ", ", &tmp
);
509 tok
; tok
= strtok_r(NULL
, ", ", &tmp
)) {
510 if (sort_dimension__add(tok
) < 0) {
511 error("Unknown --sort key: `%s'", tok
);
512 usage_with_options(usagestr
, opts
);
519 void sort_entry__setup_elide(struct sort_entry
*self
, struct strlist
*list
,
520 const char *list_name
, FILE *fp
)
522 if (list
&& strlist__nr_entries(list
) == 1) {
524 fprintf(fp
, "# %s: %s\n", list_name
,
525 strlist__entry(list
, 0)->s
);