linux/tools/perf/util/sort.c
<<
>>
Prefs
   1#include <sys/mman.h>
   2#include "sort.h"
   3#include "hist.h"
   4#include "comm.h"
   5#include "symbol.h"
   6#include "evsel.h"
   7
   8regex_t         parent_regex;
   9const char      default_parent_pattern[] = "^sys_|^do_page_fault";
  10const char      *parent_pattern = default_parent_pattern;
  11const char      default_sort_order[] = "comm,dso,symbol";
  12const char      default_branch_sort_order[] = "comm,dso_from,symbol_from,dso_to,symbol_to";
  13const char      default_mem_sort_order[] = "local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked";
  14const char      default_top_sort_order[] = "dso,symbol";
  15const char      default_diff_sort_order[] = "dso,symbol";
  16const char      *sort_order;
  17const char      *field_order;
  18regex_t         ignore_callees_regex;
  19int             have_ignore_callees = 0;
  20int             sort__need_collapse = 0;
  21int             sort__has_parent = 0;
  22int             sort__has_sym = 0;
  23int             sort__has_dso = 0;
  24enum sort_mode  sort__mode = SORT_MODE__NORMAL;
  25
  26
  27static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...)
  28{
  29        int n;
  30        va_list ap;
  31
  32        va_start(ap, fmt);
  33        n = vsnprintf(bf, size, fmt, ap);
  34        if (symbol_conf.field_sep && n > 0) {
  35                char *sep = bf;
  36
  37                while (1) {
  38                        sep = strchr(sep, *symbol_conf.field_sep);
  39                        if (sep == NULL)
  40                                break;
  41                        *sep = '.';
  42                }
  43        }
  44        va_end(ap);
  45
  46        if (n >= (int)size)
  47                return size - 1;
  48        return n;
  49}
  50
  51static int64_t cmp_null(const void *l, const void *r)
  52{
  53        if (!l && !r)
  54                return 0;
  55        else if (!l)
  56                return -1;
  57        else
  58                return 1;
  59}
  60
  61/* --sort pid */
  62
  63static int64_t
  64sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
  65{
  66        return right->thread->tid - left->thread->tid;
  67}
  68
  69static int hist_entry__thread_snprintf(struct hist_entry *he, char *bf,
  70                                       size_t size, unsigned int width)
  71{
  72        const char *comm = thread__comm_str(he->thread);
  73
  74        width = max(7U, width) - 6;
  75        return repsep_snprintf(bf, size, "%5d:%-*.*s", he->thread->tid,
  76                               width, width, comm ?: "");
  77}
  78
  79struct sort_entry sort_thread = {
  80        .se_header      = "  Pid:Command",
  81        .se_cmp         = sort__thread_cmp,
  82        .se_snprintf    = hist_entry__thread_snprintf,
  83        .se_width_idx   = HISTC_THREAD,
  84};
  85
  86/* --sort comm */
  87
  88static int64_t
  89sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
  90{
  91        /* Compare the addr that should be unique among comm */
  92        return strcmp(comm__str(right->comm), comm__str(left->comm));
  93}
  94
  95static int64_t
  96sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
  97{
  98        /* Compare the addr that should be unique among comm */
  99        return strcmp(comm__str(right->comm), comm__str(left->comm));
 100}
 101
 102static int64_t
 103sort__comm_sort(struct hist_entry *left, struct hist_entry *right)
 104{
 105        return strcmp(comm__str(right->comm), comm__str(left->comm));
 106}
 107
 108static int hist_entry__comm_snprintf(struct hist_entry *he, char *bf,
 109                                     size_t size, unsigned int width)
 110{
 111        return repsep_snprintf(bf, size, "%-*.*s", width, width, comm__str(he->comm));
 112}
 113
 114struct sort_entry sort_comm = {
 115        .se_header      = "Command",
 116        .se_cmp         = sort__comm_cmp,
 117        .se_collapse    = sort__comm_collapse,
 118        .se_sort        = sort__comm_sort,
 119        .se_snprintf    = hist_entry__comm_snprintf,
 120        .se_width_idx   = HISTC_COMM,
 121};
 122
 123/* --sort dso */
 124
 125static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r)
 126{
 127        struct dso *dso_l = map_l ? map_l->dso : NULL;
 128        struct dso *dso_r = map_r ? map_r->dso : NULL;
 129        const char *dso_name_l, *dso_name_r;
 130
 131        if (!dso_l || !dso_r)
 132                return cmp_null(dso_r, dso_l);
 133
 134        if (verbose) {
 135                dso_name_l = dso_l->long_name;
 136                dso_name_r = dso_r->long_name;
 137        } else {
 138                dso_name_l = dso_l->short_name;
 139                dso_name_r = dso_r->short_name;
 140        }
 141
 142        return strcmp(dso_name_l, dso_name_r);
 143}
 144
 145static int64_t
 146sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
 147{
 148        return _sort__dso_cmp(right->ms.map, left->ms.map);
 149}
 150
 151static int _hist_entry__dso_snprintf(struct map *map, char *bf,
 152                                     size_t size, unsigned int width)
 153{
 154        if (map && map->dso) {
 155                const char *dso_name = !verbose ? map->dso->short_name :
 156                        map->dso->long_name;
 157                return repsep_snprintf(bf, size, "%-*.*s", width, width, dso_name);
 158        }
 159
 160        return repsep_snprintf(bf, size, "%-*.*s", width, width, "[unknown]");
 161}
 162
 163static int hist_entry__dso_snprintf(struct hist_entry *he, char *bf,
 164                                    size_t size, unsigned int width)
 165{
 166        return _hist_entry__dso_snprintf(he->ms.map, bf, size, width);
 167}
 168
 169struct sort_entry sort_dso = {
 170        .se_header      = "Shared Object",
 171        .se_cmp         = sort__dso_cmp,
 172        .se_snprintf    = hist_entry__dso_snprintf,
 173        .se_width_idx   = HISTC_DSO,
 174};
 175
 176/* --sort symbol */
 177
 178static int64_t _sort__addr_cmp(u64 left_ip, u64 right_ip)
 179{
 180        return (int64_t)(right_ip - left_ip);
 181}
 182
 183static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r)
 184{
 185        if (!sym_l || !sym_r)
 186                return cmp_null(sym_l, sym_r);
 187
 188        if (sym_l == sym_r)
 189                return 0;
 190
 191        if (sym_l->start != sym_r->start)
 192                return (int64_t)(sym_r->start - sym_l->start);
 193
 194        return (int64_t)(sym_r->end - sym_l->end);
 195}
 196
 197static int64_t
 198sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
 199{
 200        int64_t ret;
 201
 202        if (!left->ms.sym && !right->ms.sym)
 203                return _sort__addr_cmp(left->ip, right->ip);
 204
 205        /*
 206         * comparing symbol address alone is not enough since it's a
 207         * relative address within a dso.
 208         */
 209        if (!sort__has_dso) {
 210                ret = sort__dso_cmp(left, right);
 211                if (ret != 0)
 212                        return ret;
 213        }
 214
 215        return _sort__sym_cmp(left->ms.sym, right->ms.sym);
 216}
 217
 218static int64_t
 219sort__sym_sort(struct hist_entry *left, struct hist_entry *right)
 220{
 221        if (!left->ms.sym || !right->ms.sym)
 222                return cmp_null(left->ms.sym, right->ms.sym);
 223
 224        return strcmp(right->ms.sym->name, left->ms.sym->name);
 225}
 226
 227static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym,
 228                                     u64 ip, char level, char *bf, size_t size,
 229                                     unsigned int width)
 230{
 231        size_t ret = 0;
 232
 233        if (verbose) {
 234                char o = map ? dso__symtab_origin(map->dso) : '!';
 235                ret += repsep_snprintf(bf, size, "%-#*llx %c ",
 236                                       BITS_PER_LONG / 4 + 2, ip, o);
 237        }
 238
 239        ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level);
 240        if (sym && map) {
 241                if (map->type == MAP__VARIABLE) {
 242                        ret += repsep_snprintf(bf + ret, size - ret, "%s", sym->name);
 243                        ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx",
 244                                        ip - map->unmap_ip(map, sym->start));
 245                        ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
 246                                       width - ret, "");
 247                } else {
 248                        ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
 249                                               width - ret,
 250                                               sym->name);
 251                }
 252        } else {
 253                size_t len = BITS_PER_LONG / 4;
 254                ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx",
 255                                       len, ip);
 256                ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
 257                                       width - ret, "");
 258        }
 259
 260        if (ret > width)
 261                bf[width] = '\0';
 262
 263        return width;
 264}
 265
 266static int hist_entry__sym_snprintf(struct hist_entry *he, char *bf,
 267                                    size_t size, unsigned int width)
 268{
 269        return _hist_entry__sym_snprintf(he->ms.map, he->ms.sym, he->ip,
 270                                         he->level, bf, size, width);
 271}
 272
 273struct sort_entry sort_sym = {
 274        .se_header      = "Symbol",
 275        .se_cmp         = sort__sym_cmp,
 276        .se_sort        = sort__sym_sort,
 277        .se_snprintf    = hist_entry__sym_snprintf,
 278        .se_width_idx   = HISTC_SYMBOL,
 279};
 280
 281/* --sort srcline */
 282
 283static int64_t
 284sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right)
 285{
 286        if (!left->srcline) {
 287                if (!left->ms.map)
 288                        left->srcline = SRCLINE_UNKNOWN;
 289                else {
 290                        struct map *map = left->ms.map;
 291                        left->srcline = get_srcline(map->dso,
 292                                           map__rip_2objdump(map, left->ip),
 293                                                    left->ms.sym, true);
 294                }
 295        }
 296        if (!right->srcline) {
 297                if (!right->ms.map)
 298                        right->srcline = SRCLINE_UNKNOWN;
 299                else {
 300                        struct map *map = right->ms.map;
 301                        right->srcline = get_srcline(map->dso,
 302                                             map__rip_2objdump(map, right->ip),
 303                                                     right->ms.sym, true);
 304                }
 305        }
 306        return strcmp(right->srcline, left->srcline);
 307}
 308
 309static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf,
 310                                        size_t size, unsigned int width)
 311{
 312        return repsep_snprintf(bf, size, "%-*.*s", width, width, he->srcline);
 313}
 314
 315struct sort_entry sort_srcline = {
 316        .se_header      = "Source:Line",
 317        .se_cmp         = sort__srcline_cmp,
 318        .se_snprintf    = hist_entry__srcline_snprintf,
 319        .se_width_idx   = HISTC_SRCLINE,
 320};
 321
 322/* --sort parent */
 323
 324static int64_t
 325sort__parent_cmp(struct hist_entry *left, struct hist_entry *right)
 326{
 327        struct symbol *sym_l = left->parent;
 328        struct symbol *sym_r = right->parent;
 329
 330        if (!sym_l || !sym_r)
 331                return cmp_null(sym_l, sym_r);
 332
 333        return strcmp(sym_r->name, sym_l->name);
 334}
 335
 336static int hist_entry__parent_snprintf(struct hist_entry *he, char *bf,
 337                                       size_t size, unsigned int width)
 338{
 339        return repsep_snprintf(bf, size, "%-*.*s", width, width,
 340                              he->parent ? he->parent->name : "[other]");
 341}
 342
 343struct sort_entry sort_parent = {
 344        .se_header      = "Parent symbol",
 345        .se_cmp         = sort__parent_cmp,
 346        .se_snprintf    = hist_entry__parent_snprintf,
 347        .se_width_idx   = HISTC_PARENT,
 348};
 349
 350/* --sort cpu */
 351
 352static int64_t
 353sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right)
 354{
 355        return right->cpu - left->cpu;
 356}
 357
 358static int hist_entry__cpu_snprintf(struct hist_entry *he, char *bf,
 359                                    size_t size, unsigned int width)
 360{
 361        return repsep_snprintf(bf, size, "%*.*d", width, width, he->cpu);
 362}
 363
 364struct sort_entry sort_cpu = {
 365        .se_header      = "CPU",
 366        .se_cmp         = sort__cpu_cmp,
 367        .se_snprintf    = hist_entry__cpu_snprintf,
 368        .se_width_idx   = HISTC_CPU,
 369};
 370
 371/* sort keys for branch stacks */
 372
 373static int64_t
 374sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right)
 375{
 376        if (!left->branch_info || !right->branch_info)
 377                return cmp_null(left->branch_info, right->branch_info);
 378
 379        return _sort__dso_cmp(left->branch_info->from.map,
 380                              right->branch_info->from.map);
 381}
 382
 383static int hist_entry__dso_from_snprintf(struct hist_entry *he, char *bf,
 384                                    size_t size, unsigned int width)
 385{
 386        if (he->branch_info)
 387                return _hist_entry__dso_snprintf(he->branch_info->from.map,
 388                                                 bf, size, width);
 389        else
 390                return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
 391}
 392
 393static int64_t
 394sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right)
 395{
 396        if (!left->branch_info || !right->branch_info)
 397                return cmp_null(left->branch_info, right->branch_info);
 398
 399        return _sort__dso_cmp(left->branch_info->to.map,
 400                              right->branch_info->to.map);
 401}
 402
 403static int hist_entry__dso_to_snprintf(struct hist_entry *he, char *bf,
 404                                       size_t size, unsigned int width)
 405{
 406        if (he->branch_info)
 407                return _hist_entry__dso_snprintf(he->branch_info->to.map,
 408                                                 bf, size, width);
 409        else
 410                return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
 411}
 412
 413static int64_t
 414sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right)
 415{
 416        struct addr_map_symbol *from_l = &left->branch_info->from;
 417        struct addr_map_symbol *from_r = &right->branch_info->from;
 418
 419        if (!left->branch_info || !right->branch_info)
 420                return cmp_null(left->branch_info, right->branch_info);
 421
 422        from_l = &left->branch_info->from;
 423        from_r = &right->branch_info->from;
 424
 425        if (!from_l->sym && !from_r->sym)
 426                return _sort__addr_cmp(from_l->addr, from_r->addr);
 427
 428        return _sort__sym_cmp(from_l->sym, from_r->sym);
 429}
 430
 431static int64_t
 432sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right)
 433{
 434        struct addr_map_symbol *to_l, *to_r;
 435
 436        if (!left->branch_info || !right->branch_info)
 437                return cmp_null(left->branch_info, right->branch_info);
 438
 439        to_l = &left->branch_info->to;
 440        to_r = &right->branch_info->to;
 441
 442        if (!to_l->sym && !to_r->sym)
 443                return _sort__addr_cmp(to_l->addr, to_r->addr);
 444
 445        return _sort__sym_cmp(to_l->sym, to_r->sym);
 446}
 447
 448static int hist_entry__sym_from_snprintf(struct hist_entry *he, char *bf,
 449                                         size_t size, unsigned int width)
 450{
 451        if (he->branch_info) {
 452                struct addr_map_symbol *from = &he->branch_info->from;
 453
 454                return _hist_entry__sym_snprintf(from->map, from->sym, from->addr,
 455                                                 he->level, bf, size, width);
 456        }
 457
 458        return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
 459}
 460
 461static int hist_entry__sym_to_snprintf(struct hist_entry *he, char *bf,
 462                                       size_t size, unsigned int width)
 463{
 464        if (he->branch_info) {
 465                struct addr_map_symbol *to = &he->branch_info->to;
 466
 467                return _hist_entry__sym_snprintf(to->map, to->sym, to->addr,
 468                                                 he->level, bf, size, width);
 469        }
 470
 471        return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
 472}
 473
 474struct sort_entry sort_dso_from = {
 475        .se_header      = "Source Shared Object",
 476        .se_cmp         = sort__dso_from_cmp,
 477        .se_snprintf    = hist_entry__dso_from_snprintf,
 478        .se_width_idx   = HISTC_DSO_FROM,
 479};
 480
 481struct sort_entry sort_dso_to = {
 482        .se_header      = "Target Shared Object",
 483        .se_cmp         = sort__dso_to_cmp,
 484        .se_snprintf    = hist_entry__dso_to_snprintf,
 485        .se_width_idx   = HISTC_DSO_TO,
 486};
 487
 488struct sort_entry sort_sym_from = {
 489        .se_header      = "Source Symbol",
 490        .se_cmp         = sort__sym_from_cmp,
 491        .se_snprintf    = hist_entry__sym_from_snprintf,
 492        .se_width_idx   = HISTC_SYMBOL_FROM,
 493};
 494
 495struct sort_entry sort_sym_to = {
 496        .se_header      = "Target Symbol",
 497        .se_cmp         = sort__sym_to_cmp,
 498        .se_snprintf    = hist_entry__sym_to_snprintf,
 499        .se_width_idx   = HISTC_SYMBOL_TO,
 500};
 501
 502static int64_t
 503sort__mispredict_cmp(struct hist_entry *left, struct hist_entry *right)
 504{
 505        unsigned char mp, p;
 506
 507        if (!left->branch_info || !right->branch_info)
 508                return cmp_null(left->branch_info, right->branch_info);
 509
 510        mp = left->branch_info->flags.mispred != right->branch_info->flags.mispred;
 511        p  = left->branch_info->flags.predicted != right->branch_info->flags.predicted;
 512        return mp || p;
 513}
 514
 515static int hist_entry__mispredict_snprintf(struct hist_entry *he, char *bf,
 516                                    size_t size, unsigned int width){
 517        static const char *out = "N/A";
 518
 519        if (he->branch_info) {
 520                if (he->branch_info->flags.predicted)
 521                        out = "N";
 522                else if (he->branch_info->flags.mispred)
 523                        out = "Y";
 524        }
 525
 526        return repsep_snprintf(bf, size, "%-*.*s", width, width, out);
 527}
 528
 529/* --sort daddr_sym */
 530static int64_t
 531sort__daddr_cmp(struct hist_entry *left, struct hist_entry *right)
 532{
 533        uint64_t l = 0, r = 0;
 534
 535        if (left->mem_info)
 536                l = left->mem_info->daddr.addr;
 537        if (right->mem_info)
 538                r = right->mem_info->daddr.addr;
 539
 540        return (int64_t)(r - l);
 541}
 542
 543static int hist_entry__daddr_snprintf(struct hist_entry *he, char *bf,
 544                                    size_t size, unsigned int width)
 545{
 546        uint64_t addr = 0;
 547        struct map *map = NULL;
 548        struct symbol *sym = NULL;
 549
 550        if (he->mem_info) {
 551                addr = he->mem_info->daddr.addr;
 552                map = he->mem_info->daddr.map;
 553                sym = he->mem_info->daddr.sym;
 554        }
 555        return _hist_entry__sym_snprintf(map, sym, addr, he->level, bf, size,
 556                                         width);
 557}
 558
 559static int64_t
 560sort__dso_daddr_cmp(struct hist_entry *left, struct hist_entry *right)
 561{
 562        struct map *map_l = NULL;
 563        struct map *map_r = NULL;
 564
 565        if (left->mem_info)
 566                map_l = left->mem_info->daddr.map;
 567        if (right->mem_info)
 568                map_r = right->mem_info->daddr.map;
 569
 570        return _sort__dso_cmp(map_l, map_r);
 571}
 572
 573static int hist_entry__dso_daddr_snprintf(struct hist_entry *he, char *bf,
 574                                    size_t size, unsigned int width)
 575{
 576        struct map *map = NULL;
 577
 578        if (he->mem_info)
 579                map = he->mem_info->daddr.map;
 580
 581        return _hist_entry__dso_snprintf(map, bf, size, width);
 582}
 583
 584static int64_t
 585sort__locked_cmp(struct hist_entry *left, struct hist_entry *right)
 586{
 587        union perf_mem_data_src data_src_l;
 588        union perf_mem_data_src data_src_r;
 589
 590        if (left->mem_info)
 591                data_src_l = left->mem_info->data_src;
 592        else
 593                data_src_l.mem_lock = PERF_MEM_LOCK_NA;
 594
 595        if (right->mem_info)
 596                data_src_r = right->mem_info->data_src;
 597        else
 598                data_src_r.mem_lock = PERF_MEM_LOCK_NA;
 599
 600        return (int64_t)(data_src_r.mem_lock - data_src_l.mem_lock);
 601}
 602
 603static int hist_entry__locked_snprintf(struct hist_entry *he, char *bf,
 604                                    size_t size, unsigned int width)
 605{
 606        const char *out;
 607        u64 mask = PERF_MEM_LOCK_NA;
 608
 609        if (he->mem_info)
 610                mask = he->mem_info->data_src.mem_lock;
 611
 612        if (mask & PERF_MEM_LOCK_NA)
 613                out = "N/A";
 614        else if (mask & PERF_MEM_LOCK_LOCKED)
 615                out = "Yes";
 616        else
 617                out = "No";
 618
 619        return repsep_snprintf(bf, size, "%-*s", width, out);
 620}
 621
 622static int64_t
 623sort__tlb_cmp(struct hist_entry *left, struct hist_entry *right)
 624{
 625        union perf_mem_data_src data_src_l;
 626        union perf_mem_data_src data_src_r;
 627
 628        if (left->mem_info)
 629                data_src_l = left->mem_info->data_src;
 630        else
 631                data_src_l.mem_dtlb = PERF_MEM_TLB_NA;
 632
 633        if (right->mem_info)
 634                data_src_r = right->mem_info->data_src;
 635        else
 636                data_src_r.mem_dtlb = PERF_MEM_TLB_NA;
 637
 638        return (int64_t)(data_src_r.mem_dtlb - data_src_l.mem_dtlb);
 639}
 640
 641static const char * const tlb_access[] = {
 642        "N/A",
 643        "HIT",
 644        "MISS",
 645        "L1",
 646        "L2",
 647        "Walker",
 648        "Fault",
 649};
 650#define NUM_TLB_ACCESS (sizeof(tlb_access)/sizeof(const char *))
 651
 652static int hist_entry__tlb_snprintf(struct hist_entry *he, char *bf,
 653                                    size_t size, unsigned int width)
 654{
 655        char out[64];
 656        size_t sz = sizeof(out) - 1; /* -1 for null termination */
 657        size_t l = 0, i;
 658        u64 m = PERF_MEM_TLB_NA;
 659        u64 hit, miss;
 660
 661        out[0] = '\0';
 662
 663        if (he->mem_info)
 664                m = he->mem_info->data_src.mem_dtlb;
 665
 666        hit = m & PERF_MEM_TLB_HIT;
 667        miss = m & PERF_MEM_TLB_MISS;
 668
 669        /* already taken care of */
 670        m &= ~(PERF_MEM_TLB_HIT|PERF_MEM_TLB_MISS);
 671
 672        for (i = 0; m && i < NUM_TLB_ACCESS; i++, m >>= 1) {
 673                if (!(m & 0x1))
 674                        continue;
 675                if (l) {
 676                        strcat(out, " or ");
 677                        l += 4;
 678                }
 679                strncat(out, tlb_access[i], sz - l);
 680                l += strlen(tlb_access[i]);
 681        }
 682        if (*out == '\0')
 683                strcpy(out, "N/A");
 684        if (hit)
 685                strncat(out, " hit", sz - l);
 686        if (miss)
 687                strncat(out, " miss", sz - l);
 688
 689        return repsep_snprintf(bf, size, "%-*s", width, out);
 690}
 691
 692static int64_t
 693sort__lvl_cmp(struct hist_entry *left, struct hist_entry *right)
 694{
 695        union perf_mem_data_src data_src_l;
 696        union perf_mem_data_src data_src_r;
 697
 698        if (left->mem_info)
 699                data_src_l = left->mem_info->data_src;
 700        else
 701                data_src_l.mem_lvl = PERF_MEM_LVL_NA;
 702
 703        if (right->mem_info)
 704                data_src_r = right->mem_info->data_src;
 705        else
 706                data_src_r.mem_lvl = PERF_MEM_LVL_NA;
 707
 708        return (int64_t)(data_src_r.mem_lvl - data_src_l.mem_lvl);
 709}
 710
 711static const char * const mem_lvl[] = {
 712        "N/A",
 713        "HIT",
 714        "MISS",
 715        "L1",
 716        "LFB",
 717        "L2",
 718        "L3",
 719        "Local RAM",
 720        "Remote RAM (1 hop)",
 721        "Remote RAM (2 hops)",
 722        "Remote Cache (1 hop)",
 723        "Remote Cache (2 hops)",
 724        "I/O",
 725        "Uncached",
 726};
 727#define NUM_MEM_LVL (sizeof(mem_lvl)/sizeof(const char *))
 728
 729static int hist_entry__lvl_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_LVL_NA;
 736        u64 hit, miss;
 737
 738        if (he->mem_info)
 739                m  = he->mem_info->data_src.mem_lvl;
 740
 741        out[0] = '\0';
 742
 743        hit = m & PERF_MEM_LVL_HIT;
 744        miss = m & PERF_MEM_LVL_MISS;
 745
 746        /* already taken care of */
 747        m &= ~(PERF_MEM_LVL_HIT|PERF_MEM_LVL_MISS);
 748
 749        for (i = 0; m && i < NUM_MEM_LVL; i++, m >>= 1) {
 750                if (!(m & 0x1))
 751                        continue;
 752                if (l) {
 753                        strcat(out, " or ");
 754                        l += 4;
 755                }
 756                strncat(out, mem_lvl[i], sz - l);
 757                l += strlen(mem_lvl[i]);
 758        }
 759        if (*out == '\0')
 760                strcpy(out, "N/A");
 761        if (hit)
 762                strncat(out, " hit", sz - l);
 763        if (miss)
 764                strncat(out, " miss", sz - l);
 765
 766        return repsep_snprintf(bf, size, "%-*s", width, out);
 767}
 768
 769static int64_t
 770sort__snoop_cmp(struct hist_entry *left, struct hist_entry *right)
 771{
 772        union perf_mem_data_src data_src_l;
 773        union perf_mem_data_src data_src_r;
 774
 775        if (left->mem_info)
 776                data_src_l = left->mem_info->data_src;
 777        else
 778                data_src_l.mem_snoop = PERF_MEM_SNOOP_NA;
 779
 780        if (right->mem_info)
 781                data_src_r = right->mem_info->data_src;
 782        else
 783                data_src_r.mem_snoop = PERF_MEM_SNOOP_NA;
 784
 785        return (int64_t)(data_src_r.mem_snoop - data_src_l.mem_snoop);
 786}
 787
 788static const char * const snoop_access[] = {
 789        "N/A",
 790        "None",
 791        "Miss",
 792        "Hit",
 793        "HitM",
 794};
 795#define NUM_SNOOP_ACCESS (sizeof(snoop_access)/sizeof(const char *))
 796
 797static int hist_entry__snoop_snprintf(struct hist_entry *he, char *bf,
 798                                    size_t size, unsigned int width)
 799{
 800        char out[64];
 801        size_t sz = sizeof(out) - 1; /* -1 for null termination */
 802        size_t i, l = 0;
 803        u64 m = PERF_MEM_SNOOP_NA;
 804
 805        out[0] = '\0';
 806
 807        if (he->mem_info)
 808                m = he->mem_info->data_src.mem_snoop;
 809
 810        for (i = 0; m && i < NUM_SNOOP_ACCESS; i++, m >>= 1) {
 811                if (!(m & 0x1))
 812                        continue;
 813                if (l) {
 814                        strcat(out, " or ");
 815                        l += 4;
 816                }
 817                strncat(out, snoop_access[i], sz - l);
 818                l += strlen(snoop_access[i]);
 819        }
 820
 821        if (*out == '\0')
 822                strcpy(out, "N/A");
 823
 824        return repsep_snprintf(bf, size, "%-*s", width, out);
 825}
 826
 827static inline  u64 cl_address(u64 address)
 828{
 829        /* return the cacheline of the address */
 830        return (address & ~(cacheline_size - 1));
 831}
 832
 833static int64_t
 834sort__dcacheline_cmp(struct hist_entry *left, struct hist_entry *right)
 835{
 836        u64 l, r;
 837        struct map *l_map, *r_map;
 838
 839        if (!left->mem_info)  return -1;
 840        if (!right->mem_info) return 1;
 841
 842        /* group event types together */
 843        if (left->cpumode > right->cpumode) return -1;
 844        if (left->cpumode < right->cpumode) return 1;
 845
 846        l_map = left->mem_info->daddr.map;
 847        r_map = right->mem_info->daddr.map;
 848
 849        /* if both are NULL, jump to sort on al_addr instead */
 850        if (!l_map && !r_map)
 851                goto addr;
 852
 853        if (!l_map) return -1;
 854        if (!r_map) return 1;
 855
 856        if (l_map->maj > r_map->maj) return -1;
 857        if (l_map->maj < r_map->maj) return 1;
 858
 859        if (l_map->min > r_map->min) return -1;
 860        if (l_map->min < r_map->min) return 1;
 861
 862        if (l_map->ino > r_map->ino) return -1;
 863        if (l_map->ino < r_map->ino) return 1;
 864
 865        if (l_map->ino_generation > r_map->ino_generation) return -1;
 866        if (l_map->ino_generation < r_map->ino_generation) return 1;
 867
 868        /*
 869         * Addresses with no major/minor numbers are assumed to be
 870         * anonymous in userspace.  Sort those on pid then address.
 871         *
 872         * The kernel and non-zero major/minor mapped areas are
 873         * assumed to be unity mapped.  Sort those on address.
 874         */
 875
 876        if ((left->cpumode != PERF_RECORD_MISC_KERNEL) &&
 877            (!(l_map->flags & MAP_SHARED)) &&
 878            !l_map->maj && !l_map->min && !l_map->ino &&
 879            !l_map->ino_generation) {
 880                /* userspace anonymous */
 881
 882                if (left->thread->pid_ > right->thread->pid_) return -1;
 883                if (left->thread->pid_ < right->thread->pid_) return 1;
 884        }
 885
 886addr:
 887        /* al_addr does all the right addr - start + offset calculations */
 888        l = cl_address(left->mem_info->daddr.al_addr);
 889        r = cl_address(right->mem_info->daddr.al_addr);
 890
 891        if (l > r) return -1;
 892        if (l < r) return 1;
 893
 894        return 0;
 895}
 896
 897static int hist_entry__dcacheline_snprintf(struct hist_entry *he, char *bf,
 898                                          size_t size, unsigned int width)
 899{
 900
 901        uint64_t addr = 0;
 902        struct map *map = NULL;
 903        struct symbol *sym = NULL;
 904        char level = he->level;
 905
 906        if (he->mem_info) {
 907                addr = cl_address(he->mem_info->daddr.al_addr);
 908                map = he->mem_info->daddr.map;
 909                sym = he->mem_info->daddr.sym;
 910
 911                /* print [s] for shared data mmaps */
 912                if ((he->cpumode != PERF_RECORD_MISC_KERNEL) &&
 913                     map && (map->type == MAP__VARIABLE) &&
 914                    (map->flags & MAP_SHARED) &&
 915                    (map->maj || map->min || map->ino ||
 916                     map->ino_generation))
 917                        level = 's';
 918                else if (!map)
 919                        level = 'X';
 920        }
 921        return _hist_entry__sym_snprintf(map, sym, addr, level, bf, size,
 922                                         width);
 923}
 924
 925struct sort_entry sort_mispredict = {
 926        .se_header      = "Branch Mispredicted",
 927        .se_cmp         = sort__mispredict_cmp,
 928        .se_snprintf    = hist_entry__mispredict_snprintf,
 929        .se_width_idx   = HISTC_MISPREDICT,
 930};
 931
 932static u64 he_weight(struct hist_entry *he)
 933{
 934        return he->stat.nr_events ? he->stat.weight / he->stat.nr_events : 0;
 935}
 936
 937static int64_t
 938sort__local_weight_cmp(struct hist_entry *left, struct hist_entry *right)
 939{
 940        return he_weight(left) - he_weight(right);
 941}
 942
 943static int hist_entry__local_weight_snprintf(struct hist_entry *he, char *bf,
 944                                    size_t size, unsigned int width)
 945{
 946        return repsep_snprintf(bf, size, "%-*llu", width, he_weight(he));
 947}
 948
 949struct sort_entry sort_local_weight = {
 950        .se_header      = "Local Weight",
 951        .se_cmp         = sort__local_weight_cmp,
 952        .se_snprintf    = hist_entry__local_weight_snprintf,
 953        .se_width_idx   = HISTC_LOCAL_WEIGHT,
 954};
 955
 956static int64_t
 957sort__global_weight_cmp(struct hist_entry *left, struct hist_entry *right)
 958{
 959        return left->stat.weight - right->stat.weight;
 960}
 961
 962static int hist_entry__global_weight_snprintf(struct hist_entry *he, char *bf,
 963                                              size_t size, unsigned int width)
 964{
 965        return repsep_snprintf(bf, size, "%-*llu", width, he->stat.weight);
 966}
 967
 968struct sort_entry sort_global_weight = {
 969        .se_header      = "Weight",
 970        .se_cmp         = sort__global_weight_cmp,
 971        .se_snprintf    = hist_entry__global_weight_snprintf,
 972        .se_width_idx   = HISTC_GLOBAL_WEIGHT,
 973};
 974
 975struct sort_entry sort_mem_daddr_sym = {
 976        .se_header      = "Data Symbol",
 977        .se_cmp         = sort__daddr_cmp,
 978        .se_snprintf    = hist_entry__daddr_snprintf,
 979        .se_width_idx   = HISTC_MEM_DADDR_SYMBOL,
 980};
 981
 982struct sort_entry sort_mem_daddr_dso = {
 983        .se_header      = "Data Object",
 984        .se_cmp         = sort__dso_daddr_cmp,
 985        .se_snprintf    = hist_entry__dso_daddr_snprintf,
 986        .se_width_idx   = HISTC_MEM_DADDR_SYMBOL,
 987};
 988
 989struct sort_entry sort_mem_locked = {
 990        .se_header      = "Locked",
 991        .se_cmp         = sort__locked_cmp,
 992        .se_snprintf    = hist_entry__locked_snprintf,
 993        .se_width_idx   = HISTC_MEM_LOCKED,
 994};
 995
 996struct sort_entry sort_mem_tlb = {
 997        .se_header      = "TLB access",
 998        .se_cmp         = sort__tlb_cmp,
 999        .se_snprintf    = hist_entry__tlb_snprintf,
1000        .se_width_idx   = HISTC_MEM_TLB,
1001};
1002
1003struct sort_entry sort_mem_lvl = {
1004        .se_header      = "Memory access",
1005        .se_cmp         = sort__lvl_cmp,
1006        .se_snprintf    = hist_entry__lvl_snprintf,
1007        .se_width_idx   = HISTC_MEM_LVL,
1008};
1009
1010struct sort_entry sort_mem_snoop = {
1011        .se_header      = "Snoop",
1012        .se_cmp         = sort__snoop_cmp,
1013        .se_snprintf    = hist_entry__snoop_snprintf,
1014        .se_width_idx   = HISTC_MEM_SNOOP,
1015};
1016
1017struct sort_entry sort_mem_dcacheline = {
1018        .se_header      = "Data Cacheline",
1019        .se_cmp         = sort__dcacheline_cmp,
1020        .se_snprintf    = hist_entry__dcacheline_snprintf,
1021        .se_width_idx   = HISTC_MEM_DCACHELINE,
1022};
1023
1024static int64_t
1025sort__abort_cmp(struct hist_entry *left, struct hist_entry *right)
1026{
1027        if (!left->branch_info || !right->branch_info)
1028                return cmp_null(left->branch_info, right->branch_info);
1029
1030        return left->branch_info->flags.abort !=
1031                right->branch_info->flags.abort;
1032}
1033
1034static int hist_entry__abort_snprintf(struct hist_entry *he, char *bf,
1035                                    size_t size, unsigned int width)
1036{
1037        static const char *out = "N/A";
1038
1039        if (he->branch_info) {
1040                if (he->branch_info->flags.abort)
1041                        out = "A";
1042                else
1043                        out = ".";
1044        }
1045
1046        return repsep_snprintf(bf, size, "%-*s", width, out);
1047}
1048
1049struct sort_entry sort_abort = {
1050        .se_header      = "Transaction abort",
1051        .se_cmp         = sort__abort_cmp,
1052        .se_snprintf    = hist_entry__abort_snprintf,
1053        .se_width_idx   = HISTC_ABORT,
1054};
1055
1056static int64_t
1057sort__in_tx_cmp(struct hist_entry *left, struct hist_entry *right)
1058{
1059        if (!left->branch_info || !right->branch_info)
1060                return cmp_null(left->branch_info, right->branch_info);
1061
1062        return left->branch_info->flags.in_tx !=
1063                right->branch_info->flags.in_tx;
1064}
1065
1066static int hist_entry__in_tx_snprintf(struct hist_entry *he, char *bf,
1067                                    size_t size, unsigned int width)
1068{
1069        static const char *out = "N/A";
1070
1071        if (he->branch_info) {
1072                if (he->branch_info->flags.in_tx)
1073                        out = "T";
1074                else
1075                        out = ".";
1076        }
1077
1078        return repsep_snprintf(bf, size, "%-*s", width, out);
1079}
1080
1081struct sort_entry sort_in_tx = {
1082        .se_header      = "Branch in transaction",
1083        .se_cmp         = sort__in_tx_cmp,
1084        .se_snprintf    = hist_entry__in_tx_snprintf,
1085        .se_width_idx   = HISTC_IN_TX,
1086};
1087
1088static int64_t
1089sort__transaction_cmp(struct hist_entry *left, struct hist_entry *right)
1090{
1091        return left->transaction - right->transaction;
1092}
1093
1094static inline char *add_str(char *p, const char *str)
1095{
1096        strcpy(p, str);
1097        return p + strlen(str);
1098}
1099
1100static struct txbit {
1101        unsigned flag;
1102        const char *name;
1103        int skip_for_len;
1104} txbits[] = {
1105        { PERF_TXN_ELISION,        "EL ",        0 },
1106        { PERF_TXN_TRANSACTION,    "TX ",        1 },
1107        { PERF_TXN_SYNC,           "SYNC ",      1 },
1108        { PERF_TXN_ASYNC,          "ASYNC ",     0 },
1109        { PERF_TXN_RETRY,          "RETRY ",     0 },
1110        { PERF_TXN_CONFLICT,       "CON ",       0 },
1111        { PERF_TXN_CAPACITY_WRITE, "CAP-WRITE ", 1 },
1112        { PERF_TXN_CAPACITY_READ,  "CAP-READ ",  0 },
1113        { 0, NULL, 0 }
1114};
1115
1116int hist_entry__transaction_len(void)
1117{
1118        int i;
1119        int len = 0;
1120
1121        for (i = 0; txbits[i].name; i++) {
1122                if (!txbits[i].skip_for_len)
1123                        len += strlen(txbits[i].name);
1124        }
1125        len += 4; /* :XX<space> */
1126        return len;
1127}
1128
1129static int hist_entry__transaction_snprintf(struct hist_entry *he, char *bf,
1130                                            size_t size, unsigned int width)
1131{
1132        u64 t = he->transaction;
1133        char buf[128];
1134        char *p = buf;
1135        int i;
1136
1137        buf[0] = 0;
1138        for (i = 0; txbits[i].name; i++)
1139                if (txbits[i].flag & t)
1140                        p = add_str(p, txbits[i].name);
1141        if (t && !(t & (PERF_TXN_SYNC|PERF_TXN_ASYNC)))
1142                p = add_str(p, "NEITHER ");
1143        if (t & PERF_TXN_ABORT_MASK) {
1144                sprintf(p, ":%" PRIx64,
1145                        (t & PERF_TXN_ABORT_MASK) >>
1146                        PERF_TXN_ABORT_SHIFT);
1147                p += strlen(p);
1148        }
1149
1150        return repsep_snprintf(bf, size, "%-*s", width, buf);
1151}
1152
1153struct sort_entry sort_transaction = {
1154        .se_header      = "Transaction                ",
1155        .se_cmp         = sort__transaction_cmp,
1156        .se_snprintf    = hist_entry__transaction_snprintf,
1157        .se_width_idx   = HISTC_TRANSACTION,
1158};
1159
1160struct sort_dimension {
1161        const char              *name;
1162        struct sort_entry       *entry;
1163        int                     taken;
1164};
1165
1166#define DIM(d, n, func) [d] = { .name = n, .entry = &(func) }
1167
1168static struct sort_dimension common_sort_dimensions[] = {
1169        DIM(SORT_PID, "pid", sort_thread),
1170        DIM(SORT_COMM, "comm", sort_comm),
1171        DIM(SORT_DSO, "dso", sort_dso),
1172        DIM(SORT_SYM, "symbol", sort_sym),
1173        DIM(SORT_PARENT, "parent", sort_parent),
1174        DIM(SORT_CPU, "cpu", sort_cpu),
1175        DIM(SORT_SRCLINE, "srcline", sort_srcline),
1176        DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight),
1177        DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight),
1178        DIM(SORT_TRANSACTION, "transaction", sort_transaction),
1179};
1180
1181#undef DIM
1182
1183#define DIM(d, n, func) [d - __SORT_BRANCH_STACK] = { .name = n, .entry = &(func) }
1184
1185static struct sort_dimension bstack_sort_dimensions[] = {
1186        DIM(SORT_DSO_FROM, "dso_from", sort_dso_from),
1187        DIM(SORT_DSO_TO, "dso_to", sort_dso_to),
1188        DIM(SORT_SYM_FROM, "symbol_from", sort_sym_from),
1189        DIM(SORT_SYM_TO, "symbol_to", sort_sym_to),
1190        DIM(SORT_MISPREDICT, "mispredict", sort_mispredict),
1191        DIM(SORT_IN_TX, "in_tx", sort_in_tx),
1192        DIM(SORT_ABORT, "abort", sort_abort),
1193};
1194
1195#undef DIM
1196
1197#define DIM(d, n, func) [d - __SORT_MEMORY_MODE] = { .name = n, .entry = &(func) }
1198
1199static struct sort_dimension memory_sort_dimensions[] = {
1200        DIM(SORT_MEM_DADDR_SYMBOL, "symbol_daddr", sort_mem_daddr_sym),
1201        DIM(SORT_MEM_DADDR_DSO, "dso_daddr", sort_mem_daddr_dso),
1202        DIM(SORT_MEM_LOCKED, "locked", sort_mem_locked),
1203        DIM(SORT_MEM_TLB, "tlb", sort_mem_tlb),
1204        DIM(SORT_MEM_LVL, "mem", sort_mem_lvl),
1205        DIM(SORT_MEM_SNOOP, "snoop", sort_mem_snoop),
1206        DIM(SORT_MEM_DCACHELINE, "dcacheline", sort_mem_dcacheline),
1207};
1208
1209#undef DIM
1210
1211struct hpp_dimension {
1212        const char              *name;
1213        struct perf_hpp_fmt     *fmt;
1214        int                     taken;
1215};
1216
1217#define DIM(d, n) { .name = n, .fmt = &perf_hpp__format[d], }
1218
1219static struct hpp_dimension hpp_sort_dimensions[] = {
1220        DIM(PERF_HPP__OVERHEAD, "overhead"),
1221        DIM(PERF_HPP__OVERHEAD_SYS, "overhead_sys"),
1222        DIM(PERF_HPP__OVERHEAD_US, "overhead_us"),
1223        DIM(PERF_HPP__OVERHEAD_GUEST_SYS, "overhead_guest_sys"),
1224        DIM(PERF_HPP__OVERHEAD_GUEST_US, "overhead_guest_us"),
1225        DIM(PERF_HPP__OVERHEAD_ACC, "overhead_children"),
1226        DIM(PERF_HPP__SAMPLES, "sample"),
1227        DIM(PERF_HPP__PERIOD, "period"),
1228};
1229
1230#undef DIM
1231
1232struct hpp_sort_entry {
1233        struct perf_hpp_fmt hpp;
1234        struct sort_entry *se;
1235};
1236
1237bool perf_hpp__same_sort_entry(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)
1238{
1239        struct hpp_sort_entry *hse_a;
1240        struct hpp_sort_entry *hse_b;
1241
1242        if (!perf_hpp__is_sort_entry(a) || !perf_hpp__is_sort_entry(b))
1243                return false;
1244
1245        hse_a = container_of(a, struct hpp_sort_entry, hpp);
1246        hse_b = container_of(b, struct hpp_sort_entry, hpp);
1247
1248        return hse_a->se == hse_b->se;
1249}
1250
1251void perf_hpp__reset_sort_width(struct perf_hpp_fmt *fmt, struct hists *hists)
1252{
1253        struct hpp_sort_entry *hse;
1254
1255        if (!perf_hpp__is_sort_entry(fmt))
1256                return;
1257
1258        hse = container_of(fmt, struct hpp_sort_entry, hpp);
1259        hists__new_col_len(hists, hse->se->se_width_idx, strlen(fmt->name));
1260}
1261
1262static int __sort__hpp_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1263                              struct perf_evsel *evsel)
1264{
1265        struct hpp_sort_entry *hse;
1266        size_t len = fmt->user_len;
1267
1268        hse = container_of(fmt, struct hpp_sort_entry, hpp);
1269
1270        if (!len)
1271                len = hists__col_len(evsel__hists(evsel), hse->se->se_width_idx);
1272
1273        return scnprintf(hpp->buf, hpp->size, "%-*.*s", len, len, fmt->name);
1274}
1275
1276static int __sort__hpp_width(struct perf_hpp_fmt *fmt,
1277                             struct perf_hpp *hpp __maybe_unused,
1278                             struct perf_evsel *evsel)
1279{
1280        struct hpp_sort_entry *hse;
1281        size_t len = fmt->user_len;
1282
1283        hse = container_of(fmt, struct hpp_sort_entry, hpp);
1284
1285        if (!len)
1286                len = hists__col_len(evsel__hists(evsel), hse->se->se_width_idx);
1287
1288        return len;
1289}
1290
1291static int __sort__hpp_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1292                             struct hist_entry *he)
1293{
1294        struct hpp_sort_entry *hse;
1295        size_t len = fmt->user_len;
1296
1297        hse = container_of(fmt, struct hpp_sort_entry, hpp);
1298
1299        if (!len)
1300                len = hists__col_len(he->hists, hse->se->se_width_idx);
1301
1302        return hse->se->se_snprintf(he, hpp->buf, hpp->size, len);
1303}
1304
1305static int64_t __sort__hpp_cmp(struct perf_hpp_fmt *fmt,
1306                               struct hist_entry *a, struct hist_entry *b)
1307{
1308        struct hpp_sort_entry *hse;
1309
1310        hse = container_of(fmt, struct hpp_sort_entry, hpp);
1311        return hse->se->se_cmp(a, b);
1312}
1313
1314static int64_t __sort__hpp_collapse(struct perf_hpp_fmt *fmt,
1315                                    struct hist_entry *a, struct hist_entry *b)
1316{
1317        struct hpp_sort_entry *hse;
1318        int64_t (*collapse_fn)(struct hist_entry *, struct hist_entry *);
1319
1320        hse = container_of(fmt, struct hpp_sort_entry, hpp);
1321        collapse_fn = hse->se->se_collapse ?: hse->se->se_cmp;
1322        return collapse_fn(a, b);
1323}
1324
1325static int64_t __sort__hpp_sort(struct perf_hpp_fmt *fmt,
1326                                struct hist_entry *a, struct hist_entry *b)
1327{
1328        struct hpp_sort_entry *hse;
1329        int64_t (*sort_fn)(struct hist_entry *, struct hist_entry *);
1330
1331        hse = container_of(fmt, struct hpp_sort_entry, hpp);
1332        sort_fn = hse->se->se_sort ?: hse->se->se_cmp;
1333        return sort_fn(a, b);
1334}
1335
1336static struct hpp_sort_entry *
1337__sort_dimension__alloc_hpp(struct sort_dimension *sd)
1338{
1339        struct hpp_sort_entry *hse;
1340
1341        hse = malloc(sizeof(*hse));
1342        if (hse == NULL) {
1343                pr_err("Memory allocation failed\n");
1344                return NULL;
1345        }
1346
1347        hse->se = sd->entry;
1348        hse->hpp.name = sd->entry->se_header;
1349        hse->hpp.header = __sort__hpp_header;
1350        hse->hpp.width = __sort__hpp_width;
1351        hse->hpp.entry = __sort__hpp_entry;
1352        hse->hpp.color = NULL;
1353
1354        hse->hpp.cmp = __sort__hpp_cmp;
1355        hse->hpp.collapse = __sort__hpp_collapse;
1356        hse->hpp.sort = __sort__hpp_sort;
1357
1358        INIT_LIST_HEAD(&hse->hpp.list);
1359        INIT_LIST_HEAD(&hse->hpp.sort_list);
1360        hse->hpp.elide = false;
1361        hse->hpp.len = 0;
1362        hse->hpp.user_len = 0;
1363
1364        return hse;
1365}
1366
1367bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format)
1368{
1369        return format->header == __sort__hpp_header;
1370}
1371
1372static int __sort_dimension__add_hpp_sort(struct sort_dimension *sd)
1373{
1374        struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd);
1375
1376        if (hse == NULL)
1377                return -1;
1378
1379        perf_hpp__register_sort_field(&hse->hpp);
1380        return 0;
1381}
1382
1383static int __sort_dimension__add_hpp_output(struct sort_dimension *sd)
1384{
1385        struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd);
1386
1387        if (hse == NULL)
1388                return -1;
1389
1390        perf_hpp__column_register(&hse->hpp);
1391        return 0;
1392}
1393
1394static int __sort_dimension__add(struct sort_dimension *sd)
1395{
1396        if (sd->taken)
1397                return 0;
1398
1399        if (__sort_dimension__add_hpp_sort(sd) < 0)
1400                return -1;
1401
1402        if (sd->entry->se_collapse)
1403                sort__need_collapse = 1;
1404
1405        sd->taken = 1;
1406
1407        return 0;
1408}
1409
1410static int __hpp_dimension__add(struct hpp_dimension *hd)
1411{
1412        if (!hd->taken) {
1413                hd->taken = 1;
1414
1415                perf_hpp__register_sort_field(hd->fmt);
1416        }
1417        return 0;
1418}
1419
1420static int __sort_dimension__add_output(struct sort_dimension *sd)
1421{
1422        if (sd->taken)
1423                return 0;
1424
1425        if (__sort_dimension__add_hpp_output(sd) < 0)
1426                return -1;
1427
1428        sd->taken = 1;
1429        return 0;
1430}
1431
1432static int __hpp_dimension__add_output(struct hpp_dimension *hd)
1433{
1434        if (!hd->taken) {
1435                hd->taken = 1;
1436
1437                perf_hpp__column_register(hd->fmt);
1438        }
1439        return 0;
1440}
1441
1442int sort_dimension__add(const char *tok)
1443{
1444        unsigned int i;
1445
1446        for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) {
1447                struct sort_dimension *sd = &common_sort_dimensions[i];
1448
1449                if (strncasecmp(tok, sd->name, strlen(tok)))
1450                        continue;
1451
1452                if (sd->entry == &sort_parent) {
1453                        int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED);
1454                        if (ret) {
1455                                char err[BUFSIZ];
1456
1457                                regerror(ret, &parent_regex, err, sizeof(err));
1458                                pr_err("Invalid regex: %s\n%s", parent_pattern, err);
1459                                return -EINVAL;
1460                        }
1461                        sort__has_parent = 1;
1462                } else if (sd->entry == &sort_sym) {
1463                        sort__has_sym = 1;
1464                        /*
1465                         * perf diff displays the performance difference amongst
1466                         * two or more perf.data files. Those files could come
1467                         * from different binaries. So we should not compare
1468                         * their ips, but the name of symbol.
1469                         */
1470                        if (sort__mode == SORT_MODE__DIFF)
1471                                sd->entry->se_collapse = sort__sym_sort;
1472
1473                } else if (sd->entry == &sort_dso) {
1474                        sort__has_dso = 1;
1475                }
1476
1477                return __sort_dimension__add(sd);
1478        }
1479
1480        for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) {
1481                struct hpp_dimension *hd = &hpp_sort_dimensions[i];
1482
1483                if (strncasecmp(tok, hd->name, strlen(tok)))
1484                        continue;
1485
1486                return __hpp_dimension__add(hd);
1487        }
1488
1489        for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) {
1490                struct sort_dimension *sd = &bstack_sort_dimensions[i];
1491
1492                if (strncasecmp(tok, sd->name, strlen(tok)))
1493                        continue;
1494
1495                if (sort__mode != SORT_MODE__BRANCH)
1496                        return -EINVAL;
1497
1498                if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to)
1499                        sort__has_sym = 1;
1500
1501                __sort_dimension__add(sd);
1502                return 0;
1503        }
1504
1505        for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) {
1506                struct sort_dimension *sd = &memory_sort_dimensions[i];
1507
1508                if (strncasecmp(tok, sd->name, strlen(tok)))
1509                        continue;
1510
1511                if (sort__mode != SORT_MODE__MEMORY)
1512                        return -EINVAL;
1513
1514                if (sd->entry == &sort_mem_daddr_sym)
1515                        sort__has_sym = 1;
1516
1517                __sort_dimension__add(sd);
1518                return 0;
1519        }
1520
1521        return -ESRCH;
1522}
1523
1524static const char *get_default_sort_order(void)
1525{
1526        const char *default_sort_orders[] = {
1527                default_sort_order,
1528                default_branch_sort_order,
1529                default_mem_sort_order,
1530                default_top_sort_order,
1531                default_diff_sort_order,
1532        };
1533
1534        BUG_ON(sort__mode >= ARRAY_SIZE(default_sort_orders));
1535
1536        return default_sort_orders[sort__mode];
1537}
1538
1539static int setup_sort_order(void)
1540{
1541        char *new_sort_order;
1542
1543        /*
1544         * Append '+'-prefixed sort order to the default sort
1545         * order string.
1546         */
1547        if (!sort_order || is_strict_order(sort_order))
1548                return 0;
1549
1550        if (sort_order[1] == '\0') {
1551                error("Invalid --sort key: `+'");
1552                return -EINVAL;
1553        }
1554
1555        /*
1556         * We allocate new sort_order string, but we never free it,
1557         * because it's checked over the rest of the code.
1558         */
1559        if (asprintf(&new_sort_order, "%s,%s",
1560                     get_default_sort_order(), sort_order + 1) < 0) {
1561                error("Not enough memory to set up --sort");
1562                return -ENOMEM;
1563        }
1564
1565        sort_order = new_sort_order;
1566        return 0;
1567}
1568
1569static int __setup_sorting(void)
1570{
1571        char *tmp, *tok, *str;
1572        const char *sort_keys;
1573        int ret = 0;
1574
1575        ret = setup_sort_order();
1576        if (ret)
1577                return ret;
1578
1579        sort_keys = sort_order;
1580        if (sort_keys == NULL) {
1581                if (is_strict_order(field_order)) {
1582                        /*
1583                         * If user specified field order but no sort order,
1584                         * we'll honor it and not add default sort orders.
1585                         */
1586                        return 0;
1587                }
1588
1589                sort_keys = get_default_sort_order();
1590        }
1591
1592        str = strdup(sort_keys);
1593        if (str == NULL) {
1594                error("Not enough memory to setup sort keys");
1595                return -ENOMEM;
1596        }
1597
1598        for (tok = strtok_r(str, ", ", &tmp);
1599                        tok; tok = strtok_r(NULL, ", ", &tmp)) {
1600                ret = sort_dimension__add(tok);
1601                if (ret == -EINVAL) {
1602                        error("Invalid --sort key: `%s'", tok);
1603                        break;
1604                } else if (ret == -ESRCH) {
1605                        error("Unknown --sort key: `%s'", tok);
1606                        break;
1607                }
1608        }
1609
1610        free(str);
1611        return ret;
1612}
1613
1614void perf_hpp__set_elide(int idx, bool elide)
1615{
1616        struct perf_hpp_fmt *fmt;
1617        struct hpp_sort_entry *hse;
1618
1619        perf_hpp__for_each_format(fmt) {
1620                if (!perf_hpp__is_sort_entry(fmt))
1621                        continue;
1622
1623                hse = container_of(fmt, struct hpp_sort_entry, hpp);
1624                if (hse->se->se_width_idx == idx) {
1625                        fmt->elide = elide;
1626                        break;
1627                }
1628        }
1629}
1630
1631static bool __get_elide(struct strlist *list, const char *list_name, FILE *fp)
1632{
1633        if (list && strlist__nr_entries(list) == 1) {
1634                if (fp != NULL)
1635                        fprintf(fp, "# %s: %s\n", list_name,
1636                                strlist__entry(list, 0)->s);
1637                return true;
1638        }
1639        return false;
1640}
1641
1642static bool get_elide(int idx, FILE *output)
1643{
1644        switch (idx) {
1645        case HISTC_SYMBOL:
1646                return __get_elide(symbol_conf.sym_list, "symbol", output);
1647        case HISTC_DSO:
1648                return __get_elide(symbol_conf.dso_list, "dso", output);
1649        case HISTC_COMM:
1650                return __get_elide(symbol_conf.comm_list, "comm", output);
1651        default:
1652                break;
1653        }
1654
1655        if (sort__mode != SORT_MODE__BRANCH)
1656                return false;
1657
1658        switch (idx) {
1659        case HISTC_SYMBOL_FROM:
1660                return __get_elide(symbol_conf.sym_from_list, "sym_from", output);
1661        case HISTC_SYMBOL_TO:
1662                return __get_elide(symbol_conf.sym_to_list, "sym_to", output);
1663        case HISTC_DSO_FROM:
1664                return __get_elide(symbol_conf.dso_from_list, "dso_from", output);
1665        case HISTC_DSO_TO:
1666                return __get_elide(symbol_conf.dso_to_list, "dso_to", output);
1667        default:
1668                break;
1669        }
1670
1671        return false;
1672}
1673
1674void sort__setup_elide(FILE *output)
1675{
1676        struct perf_hpp_fmt *fmt;
1677        struct hpp_sort_entry *hse;
1678
1679        perf_hpp__for_each_format(fmt) {
1680                if (!perf_hpp__is_sort_entry(fmt))
1681                        continue;
1682
1683                hse = container_of(fmt, struct hpp_sort_entry, hpp);
1684                fmt->elide = get_elide(hse->se->se_width_idx, output);
1685        }
1686
1687        /*
1688         * It makes no sense to elide all of sort entries.
1689         * Just revert them to show up again.
1690         */
1691        perf_hpp__for_each_format(fmt) {
1692                if (!perf_hpp__is_sort_entry(fmt))
1693                        continue;
1694
1695                if (!fmt->elide)
1696                        return;
1697        }
1698
1699        perf_hpp__for_each_format(fmt) {
1700                if (!perf_hpp__is_sort_entry(fmt))
1701                        continue;
1702
1703                fmt->elide = false;
1704        }
1705}
1706
1707static int output_field_add(char *tok)
1708{
1709        unsigned int i;
1710
1711        for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) {
1712                struct sort_dimension *sd = &common_sort_dimensions[i];
1713
1714                if (strncasecmp(tok, sd->name, strlen(tok)))
1715                        continue;
1716
1717                return __sort_dimension__add_output(sd);
1718        }
1719
1720        for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) {
1721                struct hpp_dimension *hd = &hpp_sort_dimensions[i];
1722
1723                if (strncasecmp(tok, hd->name, strlen(tok)))
1724                        continue;
1725
1726                return __hpp_dimension__add_output(hd);
1727        }
1728
1729        for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) {
1730                struct sort_dimension *sd = &bstack_sort_dimensions[i];
1731
1732                if (strncasecmp(tok, sd->name, strlen(tok)))
1733                        continue;
1734
1735                return __sort_dimension__add_output(sd);
1736        }
1737
1738        for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) {
1739                struct sort_dimension *sd = &memory_sort_dimensions[i];
1740
1741                if (strncasecmp(tok, sd->name, strlen(tok)))
1742                        continue;
1743
1744                return __sort_dimension__add_output(sd);
1745        }
1746
1747        return -ESRCH;
1748}
1749
1750static void reset_dimensions(void)
1751{
1752        unsigned int i;
1753
1754        for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++)
1755                common_sort_dimensions[i].taken = 0;
1756
1757        for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++)
1758                hpp_sort_dimensions[i].taken = 0;
1759
1760        for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++)
1761                bstack_sort_dimensions[i].taken = 0;
1762
1763        for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++)
1764                memory_sort_dimensions[i].taken = 0;
1765}
1766
1767bool is_strict_order(const char *order)
1768{
1769        return order && (*order != '+');
1770}
1771
1772static int __setup_output_field(void)
1773{
1774        char *tmp, *tok, *str, *strp;
1775        int ret = -EINVAL;
1776
1777        if (field_order == NULL)
1778                return 0;
1779
1780        reset_dimensions();
1781
1782        strp = str = strdup(field_order);
1783        if (str == NULL) {
1784                error("Not enough memory to setup output fields");
1785                return -ENOMEM;
1786        }
1787
1788        if (!is_strict_order(field_order))
1789                strp++;
1790
1791        if (!strlen(strp)) {
1792                error("Invalid --fields key: `+'");
1793                goto out;
1794        }
1795
1796        for (tok = strtok_r(strp, ", ", &tmp);
1797                        tok; tok = strtok_r(NULL, ", ", &tmp)) {
1798                ret = output_field_add(tok);
1799                if (ret == -EINVAL) {
1800                        error("Invalid --fields key: `%s'", tok);
1801                        break;
1802                } else if (ret == -ESRCH) {
1803                        error("Unknown --fields key: `%s'", tok);
1804                        break;
1805                }
1806        }
1807
1808out:
1809        free(str);
1810        return ret;
1811}
1812
1813int setup_sorting(void)
1814{
1815        int err;
1816
1817        err = __setup_sorting();
1818        if (err < 0)
1819                return err;
1820
1821        if (parent_pattern != default_parent_pattern) {
1822                err = sort_dimension__add("parent");
1823                if (err < 0)
1824                        return err;
1825        }
1826
1827        reset_dimensions();
1828
1829        /*
1830         * perf diff doesn't use default hpp output fields.
1831         */
1832        if (sort__mode != SORT_MODE__DIFF)
1833                perf_hpp__init();
1834
1835        err = __setup_output_field();
1836        if (err < 0)
1837                return err;
1838
1839        /* copy sort keys to output fields */
1840        perf_hpp__setup_output_field();
1841        /* and then copy output fields to sort keys */
1842        perf_hpp__append_sort_keys();
1843
1844        return 0;
1845}
1846
1847void reset_output_field(void)
1848{
1849        sort__need_collapse = 0;
1850        sort__has_parent = 0;
1851        sort__has_sym = 0;
1852        sort__has_dso = 0;
1853
1854        field_order = NULL;
1855        sort_order = NULL;
1856
1857        reset_dimensions();
1858        perf_hpp__reset_output_field();
1859}
1860