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