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