linux/tools/perf/util/sort.c
<<
>>
Prefs
   1#include "sort.h"
   2#include "hist.h"
   3#include "symbol.h"
   4
   5regex_t         parent_regex;
   6const char      default_parent_pattern[] = "^sys_|^do_page_fault";
   7const char      *parent_pattern = default_parent_pattern;
   8const char      default_sort_order[] = "comm,dso,symbol";
   9const char      *sort_order = default_sort_order;
  10regex_t         ignore_callees_regex;
  11int             have_ignore_callees = 0;
  12int             sort__need_collapse = 0;
  13int             sort__has_parent = 0;
  14int             sort__has_sym = 0;
  15enum sort_mode  sort__mode = SORT_MODE__NORMAL;
  16
  17enum sort_type  sort__first_dimension;
  18
  19LIST_HEAD(hist_entry__sort_list);
  20
  21static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...)
  22{
  23        int n;
  24        va_list ap;
  25
  26        va_start(ap, fmt);
  27        n = vsnprintf(bf, size, fmt, ap);
  28        if (symbol_conf.field_sep && n > 0) {
  29                char *sep = bf;
  30
  31                while (1) {
  32                        sep = strchr(sep, *symbol_conf.field_sep);
  33                        if (sep == NULL)
  34                                break;
  35                        *sep = '.';
  36                }
  37        }
  38        va_end(ap);
  39
  40        if (n >= (int)size)
  41                return size - 1;
  42        return n;
  43}
  44
  45static int64_t cmp_null(void *l, void *r)
  46{
  47        if (!l && !r)
  48                return 0;
  49        else if (!l)
  50                return -1;
  51        else
  52                return 1;
  53}
  54
  55/* --sort pid */
  56
  57static int64_t
  58sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
  59{
  60        return right->thread->tid - left->thread->tid;
  61}
  62
  63static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf,
  64                                       size_t size, unsigned int width)
  65{
  66        return repsep_snprintf(bf, size, "%*s:%5d", width - 6,
  67                              self->thread->comm ?: "", self->thread->tid);
  68}
  69
  70struct sort_entry sort_thread = {
  71        .se_header      = "Command:  Pid",
  72        .se_cmp         = sort__thread_cmp,
  73        .se_snprintf    = hist_entry__thread_snprintf,
  74        .se_width_idx   = HISTC_THREAD,
  75};
  76
  77/* --sort comm */
  78
  79static int64_t
  80sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
  81{
  82        return right->thread->tid - left->thread->tid;
  83}
  84
  85static int64_t
  86sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
  87{
  88        char *comm_l = left->thread->comm;
  89        char *comm_r = right->thread->comm;
  90
  91        if (!comm_l || !comm_r)
  92                return cmp_null(comm_l, comm_r);
  93
  94        return strcmp(comm_l, comm_r);
  95}
  96
  97static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf,
  98                                     size_t size, unsigned int width)
  99{
 100        return repsep_snprintf(bf, size, "%*s", width, self->thread->comm);
 101}
 102
 103struct sort_entry sort_comm = {
 104        .se_header      = "Command",
 105        .se_cmp         = sort__comm_cmp,
 106        .se_collapse    = sort__comm_collapse,
 107        .se_snprintf    = hist_entry__comm_snprintf,
 108        .se_width_idx   = HISTC_COMM,
 109};
 110
 111/* --sort dso */
 112
 113static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r)
 114{
 115        struct dso *dso_l = map_l ? map_l->dso : NULL;
 116        struct dso *dso_r = map_r ? map_r->dso : NULL;
 117        const char *dso_name_l, *dso_name_r;
 118
 119        if (!dso_l || !dso_r)
 120                return cmp_null(dso_l, dso_r);
 121
 122        if (verbose) {
 123                dso_name_l = dso_l->long_name;
 124                dso_name_r = dso_r->long_name;
 125        } else {
 126                dso_name_l = dso_l->short_name;
 127                dso_name_r = dso_r->short_name;
 128        }
 129
 130        return strcmp(dso_name_l, dso_name_r);
 131}
 132
 133static int64_t
 134sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
 135{
 136        return _sort__dso_cmp(left->ms.map, right->ms.map);
 137}
 138
 139static int _hist_entry__dso_snprintf(struct map *map, char *bf,
 140                                     size_t size, unsigned int width)
 141{
 142        if (map && map->dso) {
 143                const char *dso_name = !verbose ? map->dso->short_name :
 144                        map->dso->long_name;
 145                return repsep_snprintf(bf, size, "%-*s", width, dso_name);
 146        }
 147
 148        return repsep_snprintf(bf, size, "%-*s", width, "[unknown]");
 149}
 150
 151static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf,
 152                                    size_t size, unsigned int width)
 153{
 154        return _hist_entry__dso_snprintf(self->ms.map, bf, size, width);
 155}
 156
 157struct sort_entry sort_dso = {
 158        .se_header      = "Shared Object",
 159        .se_cmp         = sort__dso_cmp,
 160        .se_snprintf    = hist_entry__dso_snprintf,
 161        .se_width_idx   = HISTC_DSO,
 162};
 163
 164/* --sort symbol */
 165
 166static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r)
 167{
 168        u64 ip_l, ip_r;
 169
 170        if (!sym_l || !sym_r)
 171                return cmp_null(sym_l, sym_r);
 172
 173        if (sym_l == sym_r)
 174                return 0;
 175
 176        ip_l = sym_l->start;
 177        ip_r = sym_r->start;
 178
 179        return (int64_t)(ip_r - ip_l);
 180}
 181
 182static int64_t
 183sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
 184{
 185        if (!left->ms.sym && !right->ms.sym)
 186                return right->level - left->level;
 187
 188        return _sort__sym_cmp(left->ms.sym, right->ms.sym);
 189}
 190
 191static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym,
 192                                     u64 ip, char level, char *bf, size_t size,
 193                                     unsigned int width)
 194{
 195        size_t ret = 0;
 196
 197        if (verbose) {
 198                char o = map ? dso__symtab_origin(map->dso) : '!';
 199                ret += repsep_snprintf(bf, size, "%-#*llx %c ",
 200                                       BITS_PER_LONG / 4 + 2, ip, o);
 201        }
 202
 203        ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level);
 204        if (sym && map) {
 205                if (map->type == MAP__VARIABLE) {
 206                        ret += repsep_snprintf(bf + ret, size - ret, "%s", sym->name);
 207                        ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx",
 208                                        ip - map->unmap_ip(map, sym->start));
 209                        ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
 210                                       width - ret, "");
 211                } else {
 212                        ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
 213                                               width - ret,
 214                                               sym->name);
 215                }
 216        } else {
 217                size_t len = BITS_PER_LONG / 4;
 218                ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx",
 219                                       len, ip);
 220                ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
 221                                       width - ret, "");
 222        }
 223
 224        return ret;
 225}
 226
 227static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
 228                                    size_t size, unsigned int width)
 229{
 230        return _hist_entry__sym_snprintf(self->ms.map, self->ms.sym, self->ip,
 231                                         self->level, bf, size, width);
 232}
 233
 234struct sort_entry sort_sym = {
 235        .se_header      = "Symbol",
 236        .se_cmp         = sort__sym_cmp,
 237        .se_snprintf    = hist_entry__sym_snprintf,
 238        .se_width_idx   = HISTC_SYMBOL,
 239};
 240
 241/* --sort srcline */
 242
 243static int64_t
 244sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right)
 245{
 246        return (int64_t)(right->ip - left->ip);
 247}
 248
 249static int hist_entry__srcline_snprintf(struct hist_entry *self, char *bf,
 250                                        size_t size,
 251                                        unsigned int width __maybe_unused)
 252{
 253        FILE *fp = NULL;
 254        char cmd[PATH_MAX + 2], *path = self->srcline, *nl;
 255        size_t line_len;
 256
 257        if (path != NULL)
 258                goto out_path;
 259
 260        if (!self->ms.map)
 261                goto out_ip;
 262
 263        if (!strncmp(self->ms.map->dso->long_name, "/tmp/perf-", 10))
 264                goto out_ip;
 265
 266        snprintf(cmd, sizeof(cmd), "addr2line -e %s %016" PRIx64,
 267                 self->ms.map->dso->long_name, self->ip);
 268        fp = popen(cmd, "r");
 269        if (!fp)
 270                goto out_ip;
 271
 272        if (getline(&path, &line_len, fp) < 0 || !line_len)
 273                goto out_ip;
 274        self->srcline = strdup(path);
 275        if (self->srcline == NULL)
 276                goto out_ip;
 277
 278        nl = strchr(self->srcline, '\n');
 279        if (nl != NULL)
 280                *nl = '\0';
 281        path = self->srcline;
 282out_path:
 283        if (fp)
 284                pclose(fp);
 285        return repsep_snprintf(bf, size, "%s", path);
 286out_ip:
 287        if (fp)
 288                pclose(fp);
 289        return repsep_snprintf(bf, size, "%-#*llx", BITS_PER_LONG / 4, self->ip);
 290}
 291
 292struct sort_entry sort_srcline = {
 293        .se_header      = "Source:Line",
 294        .se_cmp         = sort__srcline_cmp,
 295        .se_snprintf    = hist_entry__srcline_snprintf,
 296        .se_width_idx   = HISTC_SRCLINE,
 297};
 298
 299/* --sort parent */
 300
 301static int64_t
 302sort__parent_cmp(struct hist_entry *left, struct hist_entry *right)
 303{
 304        struct symbol *sym_l = left->parent;
 305        struct symbol *sym_r = right->parent;
 306
 307        if (!sym_l || !sym_r)
 308                return cmp_null(sym_l, sym_r);
 309
 310        return strcmp(sym_l->name, sym_r->name);
 311}
 312
 313static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf,
 314                                       size_t size, unsigned int width)
 315{
 316        return repsep_snprintf(bf, size, "%-*s", width,
 317                              self->parent ? self->parent->name : "[other]");
 318}
 319
 320struct sort_entry sort_parent = {
 321        .se_header      = "Parent symbol",
 322        .se_cmp         = sort__parent_cmp,
 323        .se_snprintf    = hist_entry__parent_snprintf,
 324        .se_width_idx   = HISTC_PARENT,
 325};
 326
 327/* --sort cpu */
 328
 329static int64_t
 330sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right)
 331{
 332        return right->cpu - left->cpu;
 333}
 334
 335static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf,
 336                                       size_t size, unsigned int width)
 337{
 338        return repsep_snprintf(bf, size, "%*d", width, self->cpu);
 339}
 340
 341struct sort_entry sort_cpu = {
 342        .se_header      = "CPU",
 343        .se_cmp         = sort__cpu_cmp,
 344        .se_snprintf    = hist_entry__cpu_snprintf,
 345        .se_width_idx   = HISTC_CPU,
 346};
 347
 348/* sort keys for branch stacks */
 349
 350static int64_t
 351sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right)
 352{
 353        return _sort__dso_cmp(left->branch_info->from.map,
 354                              right->branch_info->from.map);
 355}
 356
 357static int hist_entry__dso_from_snprintf(struct hist_entry *self, char *bf,
 358                                    size_t size, unsigned int width)
 359{
 360        return _hist_entry__dso_snprintf(self->branch_info->from.map,
 361                                         bf, size, width);
 362}
 363
 364static int64_t
 365sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right)
 366{
 367        return _sort__dso_cmp(left->branch_info->to.map,
 368                              right->branch_info->to.map);
 369}
 370
 371static int hist_entry__dso_to_snprintf(struct hist_entry *self, char *bf,
 372                                       size_t size, unsigned int width)
 373{
 374        return _hist_entry__dso_snprintf(self->branch_info->to.map,
 375                                         bf, size, width);
 376}
 377
 378static int64_t
 379sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right)
 380{
 381        struct addr_map_symbol *from_l = &left->branch_info->from;
 382        struct addr_map_symbol *from_r = &right->branch_info->from;
 383
 384        if (!from_l->sym && !from_r->sym)
 385                return right->level - left->level;
 386
 387        return _sort__sym_cmp(from_l->sym, from_r->sym);
 388}
 389
 390static int64_t
 391sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right)
 392{
 393        struct addr_map_symbol *to_l = &left->branch_info->to;
 394        struct addr_map_symbol *to_r = &right->branch_info->to;
 395
 396        if (!to_l->sym && !to_r->sym)
 397                return right->level - left->level;
 398
 399        return _sort__sym_cmp(to_l->sym, to_r->sym);
 400}
 401
 402static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf,
 403                                         size_t size, unsigned int width)
 404{
 405        struct addr_map_symbol *from = &self->branch_info->from;
 406        return _hist_entry__sym_snprintf(from->map, from->sym, from->addr,
 407                                         self->level, bf, size, width);
 408
 409}
 410
 411static int hist_entry__sym_to_snprintf(struct hist_entry *self, char *bf,
 412                                       size_t size, unsigned int width)
 413{
 414        struct addr_map_symbol *to = &self->branch_info->to;
 415        return _hist_entry__sym_snprintf(to->map, to->sym, to->addr,
 416                                         self->level, bf, size, width);
 417
 418}
 419
 420struct sort_entry sort_dso_from = {
 421        .se_header      = "Source Shared Object",
 422        .se_cmp         = sort__dso_from_cmp,
 423        .se_snprintf    = hist_entry__dso_from_snprintf,
 424        .se_width_idx   = HISTC_DSO_FROM,
 425};
 426
 427struct sort_entry sort_dso_to = {
 428        .se_header      = "Target Shared Object",
 429        .se_cmp         = sort__dso_to_cmp,
 430        .se_snprintf    = hist_entry__dso_to_snprintf,
 431        .se_width_idx   = HISTC_DSO_TO,
 432};
 433
 434struct sort_entry sort_sym_from = {
 435        .se_header      = "Source Symbol",
 436        .se_cmp         = sort__sym_from_cmp,
 437        .se_snprintf    = hist_entry__sym_from_snprintf,
 438        .se_width_idx   = HISTC_SYMBOL_FROM,
 439};
 440
 441struct sort_entry sort_sym_to = {
 442        .se_header      = "Target Symbol",
 443        .se_cmp         = sort__sym_to_cmp,
 444        .se_snprintf    = hist_entry__sym_to_snprintf,
 445        .se_width_idx   = HISTC_SYMBOL_TO,
 446};
 447
 448static int64_t
 449sort__mispredict_cmp(struct hist_entry *left, struct hist_entry *right)
 450{
 451        const unsigned char mp = left->branch_info->flags.mispred !=
 452                                        right->branch_info->flags.mispred;
 453        const unsigned char p = left->branch_info->flags.predicted !=
 454                                        right->branch_info->flags.predicted;
 455
 456        return mp || p;
 457}
 458
 459static int hist_entry__mispredict_snprintf(struct hist_entry *self, char *bf,
 460                                    size_t size, unsigned int width){
 461        static const char *out = "N/A";
 462
 463        if (self->branch_info->flags.predicted)
 464                out = "N";
 465        else if (self->branch_info->flags.mispred)
 466                out = "Y";
 467
 468        return repsep_snprintf(bf, size, "%-*s", width, out);
 469}
 470
 471/* --sort daddr_sym */
 472static int64_t
 473sort__daddr_cmp(struct hist_entry *left, struct hist_entry *right)
 474{
 475        uint64_t l = 0, r = 0;
 476
 477        if (left->mem_info)
 478                l = left->mem_info->daddr.addr;
 479        if (right->mem_info)
 480                r = right->mem_info->daddr.addr;
 481
 482        return (int64_t)(r - l);
 483}
 484
 485static int hist_entry__daddr_snprintf(struct hist_entry *self, char *bf,
 486                                    size_t size, unsigned int width)
 487{
 488        uint64_t addr = 0;
 489        struct map *map = NULL;
 490        struct symbol *sym = NULL;
 491
 492        if (self->mem_info) {
 493                addr = self->mem_info->daddr.addr;
 494                map = self->mem_info->daddr.map;
 495                sym = self->mem_info->daddr.sym;
 496        }
 497        return _hist_entry__sym_snprintf(map, sym, addr, self->level, bf, size,
 498                                         width);
 499}
 500
 501static int64_t
 502sort__dso_daddr_cmp(struct hist_entry *left, struct hist_entry *right)
 503{
 504        struct map *map_l = NULL;
 505        struct map *map_r = NULL;
 506
 507        if (left->mem_info)
 508                map_l = left->mem_info->daddr.map;
 509        if (right->mem_info)
 510                map_r = right->mem_info->daddr.map;
 511
 512        return _sort__dso_cmp(map_l, map_r);
 513}
 514
 515static int hist_entry__dso_daddr_snprintf(struct hist_entry *self, char *bf,
 516                                    size_t size, unsigned int width)
 517{
 518        struct map *map = NULL;
 519
 520        if (self->mem_info)
 521                map = self->mem_info->daddr.map;
 522
 523        return _hist_entry__dso_snprintf(map, bf, size, width);
 524}
 525
 526static int64_t
 527sort__locked_cmp(struct hist_entry *left, struct hist_entry *right)
 528{
 529        union perf_mem_data_src data_src_l;
 530        union perf_mem_data_src data_src_r;
 531
 532        if (left->mem_info)
 533                data_src_l = left->mem_info->data_src;
 534        else
 535                data_src_l.mem_lock = PERF_MEM_LOCK_NA;
 536
 537        if (right->mem_info)
 538                data_src_r = right->mem_info->data_src;
 539        else
 540                data_src_r.mem_lock = PERF_MEM_LOCK_NA;
 541
 542        return (int64_t)(data_src_r.mem_lock - data_src_l.mem_lock);
 543}
 544
 545static int hist_entry__locked_snprintf(struct hist_entry *self, char *bf,
 546                                    size_t size, unsigned int width)
 547{
 548        const char *out;
 549        u64 mask = PERF_MEM_LOCK_NA;
 550
 551        if (self->mem_info)
 552                mask = self->mem_info->data_src.mem_lock;
 553
 554        if (mask & PERF_MEM_LOCK_NA)
 555                out = "N/A";
 556        else if (mask & PERF_MEM_LOCK_LOCKED)
 557                out = "Yes";
 558        else
 559                out = "No";
 560
 561        return repsep_snprintf(bf, size, "%-*s", width, out);
 562}
 563
 564static int64_t
 565sort__tlb_cmp(struct hist_entry *left, struct hist_entry *right)
 566{
 567        union perf_mem_data_src data_src_l;
 568        union perf_mem_data_src data_src_r;
 569
 570        if (left->mem_info)
 571                data_src_l = left->mem_info->data_src;
 572        else
 573                data_src_l.mem_dtlb = PERF_MEM_TLB_NA;
 574
 575        if (right->mem_info)
 576                data_src_r = right->mem_info->data_src;
 577        else
 578                data_src_r.mem_dtlb = PERF_MEM_TLB_NA;
 579
 580        return (int64_t)(data_src_r.mem_dtlb - data_src_l.mem_dtlb);
 581}
 582
 583static const char * const tlb_access[] = {
 584        "N/A",
 585        "HIT",
 586        "MISS",
 587        "L1",
 588        "L2",
 589        "Walker",
 590        "Fault",
 591};
 592#define NUM_TLB_ACCESS (sizeof(tlb_access)/sizeof(const char *))
 593
 594static int hist_entry__tlb_snprintf(struct hist_entry *self, char *bf,
 595                                    size_t size, unsigned int width)
 596{
 597        char out[64];
 598        size_t sz = sizeof(out) - 1; /* -1 for null termination */
 599        size_t l = 0, i;
 600        u64 m = PERF_MEM_TLB_NA;
 601        u64 hit, miss;
 602
 603        out[0] = '\0';
 604
 605        if (self->mem_info)
 606                m = self->mem_info->data_src.mem_dtlb;
 607
 608        hit = m & PERF_MEM_TLB_HIT;
 609        miss = m & PERF_MEM_TLB_MISS;
 610
 611        /* already taken care of */
 612        m &= ~(PERF_MEM_TLB_HIT|PERF_MEM_TLB_MISS);
 613
 614        for (i = 0; m && i < NUM_TLB_ACCESS; i++, m >>= 1) {
 615                if (!(m & 0x1))
 616                        continue;
 617                if (l) {
 618                        strcat(out, " or ");
 619                        l += 4;
 620                }
 621                strncat(out, tlb_access[i], sz - l);
 622                l += strlen(tlb_access[i]);
 623        }
 624        if (*out == '\0')
 625                strcpy(out, "N/A");
 626        if (hit)
 627                strncat(out, " hit", sz - l);
 628        if (miss)
 629                strncat(out, " miss", sz - l);
 630
 631        return repsep_snprintf(bf, size, "%-*s", width, out);
 632}
 633
 634static int64_t
 635sort__lvl_cmp(struct hist_entry *left, struct hist_entry *right)
 636{
 637        union perf_mem_data_src data_src_l;
 638        union perf_mem_data_src data_src_r;
 639
 640        if (left->mem_info)
 641                data_src_l = left->mem_info->data_src;
 642        else
 643                data_src_l.mem_lvl = PERF_MEM_LVL_NA;
 644
 645        if (right->mem_info)
 646                data_src_r = right->mem_info->data_src;
 647        else
 648                data_src_r.mem_lvl = PERF_MEM_LVL_NA;
 649
 650        return (int64_t)(data_src_r.mem_lvl - data_src_l.mem_lvl);
 651}
 652
 653static const char * const mem_lvl[] = {
 654        "N/A",
 655        "HIT",
 656        "MISS",
 657        "L1",
 658        "LFB",
 659        "L2",
 660        "L3",
 661        "Local RAM",
 662        "Remote RAM (1 hop)",
 663        "Remote RAM (2 hops)",
 664        "Remote Cache (1 hop)",
 665        "Remote Cache (2 hops)",
 666        "I/O",
 667        "Uncached",
 668};
 669#define NUM_MEM_LVL (sizeof(mem_lvl)/sizeof(const char *))
 670
 671static int hist_entry__lvl_snprintf(struct hist_entry *self, char *bf,
 672                                    size_t size, unsigned int width)
 673{
 674        char out[64];
 675        size_t sz = sizeof(out) - 1; /* -1 for null termination */
 676        size_t i, l = 0;
 677        u64 m =  PERF_MEM_LVL_NA;
 678        u64 hit, miss;
 679
 680        if (self->mem_info)
 681                m  = self->mem_info->data_src.mem_lvl;
 682
 683        out[0] = '\0';
 684
 685        hit = m & PERF_MEM_LVL_HIT;
 686        miss = m & PERF_MEM_LVL_MISS;
 687
 688        /* already taken care of */
 689        m &= ~(PERF_MEM_LVL_HIT|PERF_MEM_LVL_MISS);
 690
 691        for (i = 0; m && i < NUM_MEM_LVL; i++, m >>= 1) {
 692                if (!(m & 0x1))
 693                        continue;
 694                if (l) {
 695                        strcat(out, " or ");
 696                        l += 4;
 697                }
 698                strncat(out, mem_lvl[i], sz - l);
 699                l += strlen(mem_lvl[i]);
 700        }
 701        if (*out == '\0')
 702                strcpy(out, "N/A");
 703        if (hit)
 704                strncat(out, " hit", sz - l);
 705        if (miss)
 706                strncat(out, " miss", sz - l);
 707
 708        return repsep_snprintf(bf, size, "%-*s", width, out);
 709}
 710
 711static int64_t
 712sort__snoop_cmp(struct hist_entry *left, struct hist_entry *right)
 713{
 714        union perf_mem_data_src data_src_l;
 715        union perf_mem_data_src data_src_r;
 716
 717        if (left->mem_info)
 718                data_src_l = left->mem_info->data_src;
 719        else
 720                data_src_l.mem_snoop = PERF_MEM_SNOOP_NA;
 721
 722        if (right->mem_info)
 723                data_src_r = right->mem_info->data_src;
 724        else
 725                data_src_r.mem_snoop = PERF_MEM_SNOOP_NA;
 726
 727        return (int64_t)(data_src_r.mem_snoop - data_src_l.mem_snoop);
 728}
 729
 730static const char * const snoop_access[] = {
 731        "N/A",
 732        "None",
 733        "Miss",
 734        "Hit",
 735        "HitM",
 736};
 737#define NUM_SNOOP_ACCESS (sizeof(snoop_access)/sizeof(const char *))
 738
 739static int hist_entry__snoop_snprintf(struct hist_entry *self, char *bf,
 740                                    size_t size, unsigned int width)
 741{
 742        char out[64];
 743        size_t sz = sizeof(out) - 1; /* -1 for null termination */
 744        size_t i, l = 0;
 745        u64 m = PERF_MEM_SNOOP_NA;
 746
 747        out[0] = '\0';
 748
 749        if (self->mem_info)
 750                m = self->mem_info->data_src.mem_snoop;
 751
 752        for (i = 0; m && i < NUM_SNOOP_ACCESS; i++, m >>= 1) {
 753                if (!(m & 0x1))
 754                        continue;
 755                if (l) {
 756                        strcat(out, " or ");
 757                        l += 4;
 758                }
 759                strncat(out, snoop_access[i], sz - l);
 760                l += strlen(snoop_access[i]);
 761        }
 762
 763        if (*out == '\0')
 764                strcpy(out, "N/A");
 765
 766        return repsep_snprintf(bf, size, "%-*s", width, out);
 767}
 768
 769struct sort_entry sort_mispredict = {
 770        .se_header      = "Branch Mispredicted",
 771        .se_cmp         = sort__mispredict_cmp,
 772        .se_snprintf    = hist_entry__mispredict_snprintf,
 773        .se_width_idx   = HISTC_MISPREDICT,
 774};
 775
 776static u64 he_weight(struct hist_entry *he)
 777{
 778        return he->stat.nr_events ? he->stat.weight / he->stat.nr_events : 0;
 779}
 780
 781static int64_t
 782sort__local_weight_cmp(struct hist_entry *left, struct hist_entry *right)
 783{
 784        return he_weight(left) - he_weight(right);
 785}
 786
 787static int hist_entry__local_weight_snprintf(struct hist_entry *self, char *bf,
 788                                    size_t size, unsigned int width)
 789{
 790        return repsep_snprintf(bf, size, "%-*llu", width, he_weight(self));
 791}
 792
 793struct sort_entry sort_local_weight = {
 794        .se_header      = "Local Weight",
 795        .se_cmp         = sort__local_weight_cmp,
 796        .se_snprintf    = hist_entry__local_weight_snprintf,
 797        .se_width_idx   = HISTC_LOCAL_WEIGHT,
 798};
 799
 800static int64_t
 801sort__global_weight_cmp(struct hist_entry *left, struct hist_entry *right)
 802{
 803        return left->stat.weight - right->stat.weight;
 804}
 805
 806static int hist_entry__global_weight_snprintf(struct hist_entry *self, char *bf,
 807                                              size_t size, unsigned int width)
 808{
 809        return repsep_snprintf(bf, size, "%-*llu", width, self->stat.weight);
 810}
 811
 812struct sort_entry sort_global_weight = {
 813        .se_header      = "Weight",
 814        .se_cmp         = sort__global_weight_cmp,
 815        .se_snprintf    = hist_entry__global_weight_snprintf,
 816        .se_width_idx   = HISTC_GLOBAL_WEIGHT,
 817};
 818
 819struct sort_entry sort_mem_daddr_sym = {
 820        .se_header      = "Data Symbol",
 821        .se_cmp         = sort__daddr_cmp,
 822        .se_snprintf    = hist_entry__daddr_snprintf,
 823        .se_width_idx   = HISTC_MEM_DADDR_SYMBOL,
 824};
 825
 826struct sort_entry sort_mem_daddr_dso = {
 827        .se_header      = "Data Object",
 828        .se_cmp         = sort__dso_daddr_cmp,
 829        .se_snprintf    = hist_entry__dso_daddr_snprintf,
 830        .se_width_idx   = HISTC_MEM_DADDR_SYMBOL,
 831};
 832
 833struct sort_entry sort_mem_locked = {
 834        .se_header      = "Locked",
 835        .se_cmp         = sort__locked_cmp,
 836        .se_snprintf    = hist_entry__locked_snprintf,
 837        .se_width_idx   = HISTC_MEM_LOCKED,
 838};
 839
 840struct sort_entry sort_mem_tlb = {
 841        .se_header      = "TLB access",
 842        .se_cmp         = sort__tlb_cmp,
 843        .se_snprintf    = hist_entry__tlb_snprintf,
 844        .se_width_idx   = HISTC_MEM_TLB,
 845};
 846
 847struct sort_entry sort_mem_lvl = {
 848        .se_header      = "Memory access",
 849        .se_cmp         = sort__lvl_cmp,
 850        .se_snprintf    = hist_entry__lvl_snprintf,
 851        .se_width_idx   = HISTC_MEM_LVL,
 852};
 853
 854struct sort_entry sort_mem_snoop = {
 855        .se_header      = "Snoop",
 856        .se_cmp         = sort__snoop_cmp,
 857        .se_snprintf    = hist_entry__snoop_snprintf,
 858        .se_width_idx   = HISTC_MEM_SNOOP,
 859};
 860
 861struct sort_dimension {
 862        const char              *name;
 863        struct sort_entry       *entry;
 864        int                     taken;
 865};
 866
 867#define DIM(d, n, func) [d] = { .name = n, .entry = &(func) }
 868
 869static struct sort_dimension common_sort_dimensions[] = {
 870        DIM(SORT_PID, "pid", sort_thread),
 871        DIM(SORT_COMM, "comm", sort_comm),
 872        DIM(SORT_DSO, "dso", sort_dso),
 873        DIM(SORT_SYM, "symbol", sort_sym),
 874        DIM(SORT_PARENT, "parent", sort_parent),
 875        DIM(SORT_CPU, "cpu", sort_cpu),
 876        DIM(SORT_SRCLINE, "srcline", sort_srcline),
 877        DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight),
 878        DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight),
 879};
 880
 881#undef DIM
 882
 883#define DIM(d, n, func) [d - __SORT_BRANCH_STACK] = { .name = n, .entry = &(func) }
 884
 885static struct sort_dimension bstack_sort_dimensions[] = {
 886        DIM(SORT_DSO_FROM, "dso_from", sort_dso_from),
 887        DIM(SORT_DSO_TO, "dso_to", sort_dso_to),
 888        DIM(SORT_SYM_FROM, "symbol_from", sort_sym_from),
 889        DIM(SORT_SYM_TO, "symbol_to", sort_sym_to),
 890        DIM(SORT_MISPREDICT, "mispredict", sort_mispredict),
 891};
 892
 893#undef DIM
 894
 895#define DIM(d, n, func) [d - __SORT_MEMORY_MODE] = { .name = n, .entry = &(func) }
 896
 897static struct sort_dimension memory_sort_dimensions[] = {
 898        DIM(SORT_MEM_DADDR_SYMBOL, "symbol_daddr", sort_mem_daddr_sym),
 899        DIM(SORT_MEM_DADDR_DSO, "dso_daddr", sort_mem_daddr_dso),
 900        DIM(SORT_MEM_LOCKED, "locked", sort_mem_locked),
 901        DIM(SORT_MEM_TLB, "tlb", sort_mem_tlb),
 902        DIM(SORT_MEM_LVL, "mem", sort_mem_lvl),
 903        DIM(SORT_MEM_SNOOP, "snoop", sort_mem_snoop),
 904};
 905
 906#undef DIM
 907
 908static void __sort_dimension__add(struct sort_dimension *sd, enum sort_type idx)
 909{
 910        if (sd->taken)
 911                return;
 912
 913        if (sd->entry->se_collapse)
 914                sort__need_collapse = 1;
 915
 916        if (list_empty(&hist_entry__sort_list))
 917                sort__first_dimension = idx;
 918
 919        list_add_tail(&sd->entry->list, &hist_entry__sort_list);
 920        sd->taken = 1;
 921}
 922
 923int sort_dimension__add(const char *tok)
 924{
 925        unsigned int i;
 926
 927        for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) {
 928                struct sort_dimension *sd = &common_sort_dimensions[i];
 929
 930                if (strncasecmp(tok, sd->name, strlen(tok)))
 931                        continue;
 932
 933                if (sd->entry == &sort_parent) {
 934                        int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED);
 935                        if (ret) {
 936                                char err[BUFSIZ];
 937
 938                                regerror(ret, &parent_regex, err, sizeof(err));
 939                                pr_err("Invalid regex: %s\n%s", parent_pattern, err);
 940                                return -EINVAL;
 941                        }
 942                        sort__has_parent = 1;
 943                } else if (sd->entry == &sort_sym) {
 944                        sort__has_sym = 1;
 945                }
 946
 947                __sort_dimension__add(sd, i);
 948                return 0;
 949        }
 950
 951        for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) {
 952                struct sort_dimension *sd = &bstack_sort_dimensions[i];
 953
 954                if (strncasecmp(tok, sd->name, strlen(tok)))
 955                        continue;
 956
 957                if (sort__mode != SORT_MODE__BRANCH)
 958                        return -EINVAL;
 959
 960                if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to)
 961                        sort__has_sym = 1;
 962
 963                __sort_dimension__add(sd, i + __SORT_BRANCH_STACK);
 964                return 0;
 965        }
 966
 967        for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) {
 968                struct sort_dimension *sd = &memory_sort_dimensions[i];
 969
 970                if (strncasecmp(tok, sd->name, strlen(tok)))
 971                        continue;
 972
 973                if (sort__mode != SORT_MODE__MEMORY)
 974                        return -EINVAL;
 975
 976                if (sd->entry == &sort_mem_daddr_sym)
 977                        sort__has_sym = 1;
 978
 979                __sort_dimension__add(sd, i + __SORT_MEMORY_MODE);
 980                return 0;
 981        }
 982
 983        return -ESRCH;
 984}
 985
 986int setup_sorting(void)
 987{
 988        char *tmp, *tok, *str = strdup(sort_order);
 989        int ret = 0;
 990
 991        if (str == NULL) {
 992                error("Not enough memory to setup sort keys");
 993                return -ENOMEM;
 994        }
 995
 996        for (tok = strtok_r(str, ", ", &tmp);
 997                        tok; tok = strtok_r(NULL, ", ", &tmp)) {
 998                ret = sort_dimension__add(tok);
 999                if (ret == -EINVAL) {
1000                        error("Invalid --sort key: `%s'", tok);
1001                        break;
1002                } else if (ret == -ESRCH) {
1003                        error("Unknown --sort key: `%s'", tok);
1004                        break;
1005                }
1006        }
1007
1008        free(str);
1009        return ret;
1010}
1011
1012static void sort_entry__setup_elide(struct sort_entry *self,
1013                                    struct strlist *list,
1014                                    const char *list_name, FILE *fp)
1015{
1016        if (list && strlist__nr_entries(list) == 1) {
1017                if (fp != NULL)
1018                        fprintf(fp, "# %s: %s\n", list_name,
1019                                strlist__entry(list, 0)->s);
1020                self->elide = true;
1021        }
1022}
1023
1024void sort__setup_elide(FILE *output)
1025{
1026        sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
1027                                "dso", output);
1028        sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list,
1029                                "comm", output);
1030        sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list,
1031                                "symbol", output);
1032
1033        if (sort__mode == SORT_MODE__BRANCH) {
1034                sort_entry__setup_elide(&sort_dso_from,
1035                                        symbol_conf.dso_from_list,
1036                                        "dso_from", output);
1037                sort_entry__setup_elide(&sort_dso_to,
1038                                        symbol_conf.dso_to_list,
1039                                        "dso_to", output);
1040                sort_entry__setup_elide(&sort_sym_from,
1041                                        symbol_conf.sym_from_list,
1042                                        "sym_from", output);
1043                sort_entry__setup_elide(&sort_sym_to,
1044                                        symbol_conf.sym_to_list,
1045                                        "sym_to", output);
1046        } else if (sort__mode == SORT_MODE__MEMORY) {
1047                sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
1048                                        "symbol_daddr", output);
1049                sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
1050                                        "dso_daddr", output);
1051                sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
1052                                        "mem", output);
1053                sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
1054                                        "local_weight", output);
1055                sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
1056                                        "tlb", output);
1057                sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
1058                                        "snoop", output);
1059        }
1060
1061}
1062