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