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