Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/tools/perf/util/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 105 kB image not shown  

SSL sort.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0
#include <errno.h>
#include <inttypes.h>
#include <regex.h>
#include <stdlib.h>
#include <linux/mman.h>
#include <linux/time64.h>
#include "debug.h"
#include "dso.h"
#include "sort.h"
#include "hist.h"
#include "cacheline.h"
#include "comm.h"
#include "map.h"
#include "maps.h"
#include "symbol.h"
#include "map_symbol.h"
#include "branch.h"
#include "thread.h"
#include "evsel.h"
#include "evlist.h"
#include "srcline.h"
#include "strlist.h"
#include "strbuf.h"
#include "mem-events.h"
#include "mem-info.h"
#include "annotate.h"
#include "annotate-data.h"
#include "event.h"
#include "time-utils.h"
#include "cgroup.h"
#include "machine.h"
#include "trace-event.h"
#include <linux/kernel.h>
#include <linux/string.h>

#ifdef HAVE_LIBTRACEEVENT
#include <event-parse.h>
#endif

regex_t  parent_regex;
const char default_parent_pattern[] = "^sys_|^do_page_fault";
const char *parent_pattern = default_parent_pattern;
const char *default_sort_order = "comm,dso,symbol";
const char default_branch_sort_order[] = "comm,dso_from,symbol_from,symbol_to,cycles";
const char default_mem_sort_order[] = "local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked,blocked,local_ins_lat,local_p_stage_cyc";
const char default_top_sort_order[] = "dso,symbol";
const char default_diff_sort_order[] = "dso,symbol";
const char default_tracepoint_sort_order[] = "trace";
const char *sort_order;
const char *field_order;
regex_t  ignore_callees_regex;
int  have_ignore_callees = 0;
enum sort_mode sort__mode = SORT_MODE__NORMAL;
static const char *const dynamic_headers[] = {"local_ins_lat""ins_lat""local_p_stage_cyc""p_stage_cyc"};
static const char *const arch_specific_sort_keys[] = {"local_p_stage_cyc""p_stage_cyc"};

/*
 * Some architectures have Adjacent Cacheline Prefetch feature, which
 * behaves like the cacheline size is doubled. Enable this flag to
 * check things in double cacheline granularity.
 */

bool chk_double_cl;

/*
 * Replaces all occurrences of a char used with the:
 *
 * -t, --field-separator
 *
 * option, that uses a special separator character and don't pad with spaces,
 * replacing all occurrences of this separator in symbol names (and other
 * output) with a '.' character, that thus it's the only non valid separator.
*/

static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...)
{
 int n;
 va_list ap;

 va_start(ap, fmt);
 n = vsnprintf(bf, size, fmt, ap);
 if (symbol_conf.field_sep && n > 0) {
  char *sep = bf;

  while (1) {
   sep = strchr(sep, *symbol_conf.field_sep);
   if (sep == NULL)
    break;
   *sep = '.';
  }
 }
 va_end(ap);

 if (n >= (int)size)
  return size - 1;
 return n;
}

static int64_t cmp_null(const void *l, const void *r)
{
 if (!l && !r)
  return 0;
 else if (!l)
  return -1;
 else
  return 1;
}

/* --sort pid */

static int64_t
sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
{
 return thread__tid(right->thread) - thread__tid(left->thread);
}

static int hist_entry__thread_snprintf(struct hist_entry *he, char *bf,
           size_t size, unsigned int width)
{
 const char *comm = thread__comm_str(he->thread);

 width = max(7U, width) - 8;
 return repsep_snprintf(bf, size, "%7d:%-*.*s", thread__tid(he->thread),
          width, width, comm ?: "");
}

static int hist_entry__thread_filter(struct hist_entry *he, int type, const void *arg)
{
 const struct thread *th = arg;

 if (type != HIST_FILTER__THREAD)
  return -1;

 return th && !RC_CHK_EQUAL(he->thread, th);
}

struct sort_entry sort_thread = {
 .se_header = " Pid:Command",
 .se_cmp  = sort__thread_cmp,
 .se_snprintf = hist_entry__thread_snprintf,
 .se_filter = hist_entry__thread_filter,
 .se_width_idx = HISTC_THREAD,
};

/* --sort tgid */

static int64_t
sort__tgid_cmp(struct hist_entry *left, struct hist_entry *right)
{
 return thread__pid(right->thread) - thread__pid(left->thread);
}

static int hist_entry__tgid_snprintf(struct hist_entry *he, char *bf,
           size_t size, unsigned int width)
{
 int tgid = thread__pid(he->thread);
 const char *comm = NULL;

 /* display comm of the thread-group leader */
 if (thread__pid(he->thread) == thread__tid(he->thread)) {
  comm = thread__comm_str(he->thread);
 } else {
  struct maps *maps = thread__maps(he->thread);
  struct thread *leader = machine__find_thread(maps__machine(maps),
            tgid, tgid);
  if (leader) {
   comm = thread__comm_str(leader);
   thread__put(leader);
  }
 }
 width = max(7U, width) - 8;
 return repsep_snprintf(bf, size, "%7d:%-*.*s", tgid, width, width, comm ?: "");
}

struct sort_entry sort_tgid = {
 .se_header = " Tgid:Command",
 .se_cmp  = sort__tgid_cmp,
 .se_snprintf = hist_entry__tgid_snprintf,
 .se_width_idx = HISTC_TGID,
};

/* --sort simd */

static int64_t
sort__simd_cmp(struct hist_entry *left, struct hist_entry *right)
{
 if (left->simd_flags.arch != right->simd_flags.arch)
  return (int64_t) left->simd_flags.arch - right->simd_flags.arch;

 return (int64_t) left->simd_flags.pred - right->simd_flags.pred;
}

static const char *hist_entry__get_simd_name(struct simd_flags *simd_flags)
{
 u64 arch = simd_flags->arch;

 if (arch & SIMD_OP_FLAGS_ARCH_SVE)
  return "SVE";
 else
  return "n/a";
}

static int hist_entry__simd_snprintf(struct hist_entry *he, char *bf,
         size_t size, unsigned int width __maybe_unused)
{
 const char *name;

 if (!he->simd_flags.arch)
  return repsep_snprintf(bf, size, "");

 name = hist_entry__get_simd_name(&he->simd_flags);

 if (he->simd_flags.pred & SIMD_OP_FLAGS_PRED_EMPTY)
  return repsep_snprintf(bf, size, "[e] %s", name);
 else if (he->simd_flags.pred & SIMD_OP_FLAGS_PRED_PARTIAL)
  return repsep_snprintf(bf, size, "[p] %s", name);

 return repsep_snprintf(bf, size, "[.] %s", name);
}

struct sort_entry sort_simd = {
 .se_header = "Simd ",
 .se_cmp  = sort__simd_cmp,
 .se_snprintf = hist_entry__simd_snprintf,
 .se_width_idx = HISTC_SIMD,
};

/* --sort comm */

/*
 * We can't use pointer comparison in functions below,
 * because it gives different results based on pointer
 * values, which could break some sorting assumptions.
 */

static int64_t
sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
{
 return strcmp(comm__str(right->comm), comm__str(left->comm));
}

static int64_t
sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
{
 return strcmp(comm__str(right->comm), comm__str(left->comm));
}

static int64_t
sort__comm_sort(struct hist_entry *left, struct hist_entry *right)
{
 return strcmp(comm__str(right->comm), comm__str(left->comm));
}

static int hist_entry__comm_snprintf(struct hist_entry *he, char *bf,
         size_t size, unsigned int width)
{
 return repsep_snprintf(bf, size, "%-*.*s", width, width, comm__str(he->comm));
}

struct sort_entry sort_comm = {
 .se_header = "Command",
 .se_cmp  = sort__comm_cmp,
 .se_collapse = sort__comm_collapse,
 .se_sort = sort__comm_sort,
 .se_snprintf = hist_entry__comm_snprintf,
 .se_filter = hist_entry__thread_filter,
 .se_width_idx = HISTC_COMM,
};

/* --sort dso */

static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r)
{
 struct dso *dso_l = map_l ? map__dso(map_l) : NULL;
 struct dso *dso_r = map_r ? map__dso(map_r) : NULL;
 const char *dso_name_l, *dso_name_r;

 if (!dso_l || !dso_r)
  return cmp_null(dso_r, dso_l);

 if (verbose > 0) {
  dso_name_l = dso__long_name(dso_l);
  dso_name_r = dso__long_name(dso_r);
 } else {
  dso_name_l = dso__short_name(dso_l);
  dso_name_r = dso__short_name(dso_r);
 }

 return strcmp(dso_name_l, dso_name_r);
}

static int64_t
sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
{
 return _sort__dso_cmp(right->ms.map, left->ms.map);
}

static int _hist_entry__dso_snprintf(struct map *map, char *bf,
         size_t size, unsigned int width)
{
 const struct dso *dso = map ? map__dso(map) : NULL;
 const char *dso_name = "[unknown]";

 if (dso)
  dso_name = verbose > 0 ? dso__long_name(dso) : dso__short_name(dso);

 return repsep_snprintf(bf, size, "%-*.*s", width, width, dso_name);
}

static int hist_entry__dso_snprintf(struct hist_entry *he, char *bf,
        size_t size, unsigned int width)
{
 return _hist_entry__dso_snprintf(he->ms.map, bf, size, width);
}

static int hist_entry__dso_filter(struct hist_entry *he, int type, const void *arg)
{
 const struct dso *dso = arg;

 if (type != HIST_FILTER__DSO)
  return -1;

 return dso && (!he->ms.map || map__dso(he->ms.map) != dso);
}

struct sort_entry sort_dso = {
 .se_header = "Shared Object",
 .se_cmp  = sort__dso_cmp,
 .se_snprintf = hist_entry__dso_snprintf,
 .se_filter = hist_entry__dso_filter,
 .se_width_idx = HISTC_DSO,
};

/* --sort symbol */

static int64_t _sort__addr_cmp(u64 left_ip, u64 right_ip)
{
 return (int64_t)(right_ip - left_ip);
}

int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r)
{
 if (!sym_l || !sym_r)
  return cmp_null(sym_l, sym_r);

 if (sym_l == sym_r)
  return 0;

 if (sym_l->inlined || sym_r->inlined) {
  int ret = strcmp(sym_l->name, sym_r->name);

  if (ret)
   return ret;
  if ((sym_l->start <= sym_r->end) && (sym_l->end >= sym_r->start))
   return 0;
 }

 if (sym_l->start != sym_r->start)
  return (int64_t)(sym_r->start - sym_l->start);

 return (int64_t)(sym_r->end - sym_l->end);
}

static int64_t
sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
{
 int64_t ret;

 if (!left->ms.sym && !right->ms.sym)
  return _sort__addr_cmp(left->ip, right->ip);

 /*
 * comparing symbol address alone is not enough since it's a
 * relative address within a dso.
 */

 if (!hists__has(left->hists, dso)) {
  ret = sort__dso_cmp(left, right);
  if (ret != 0)
   return ret;
 }

 return _sort__sym_cmp(left->ms.sym, right->ms.sym);
}

static int64_t
sort__sym_sort(struct hist_entry *left, struct hist_entry *right)
{
 if (!left->ms.sym || !right->ms.sym)
  return cmp_null(left->ms.sym, right->ms.sym);

 return strcmp(right->ms.sym->name, left->ms.sym->name);
}

static int _hist_entry__sym_snprintf(struct map_symbol *ms,
         u64 ip, char level, char *bf, size_t size,
         unsigned int width)
{
 struct symbol *sym = ms->sym;
 struct map *map = ms->map;
 size_t ret = 0;

 if (verbose > 0) {
  struct dso *dso = map ? map__dso(map) : NULL;
  char o = dso ? dso__symtab_origin(dso) : '!';
  u64 rip = ip;

  if (dso && dso__kernel(dso) && dso__adjust_symbols(dso))
   rip = map__unmap_ip(map, ip);

  ret += repsep_snprintf(bf, size, "%-#*llx %c ",
           BITS_PER_LONG / 4 + 2, rip, o);
 }

 ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level);
 if (sym && map) {
  if (sym->type == STT_OBJECT) {
   ret += repsep_snprintf(bf + ret, size - ret, "%s", sym->name);
   ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx",
     ip - map__unmap_ip(map, sym->start));
  } else {
   ret += repsep_snprintf(bf + ret, size - ret, "%.*s",
            width - ret,
            sym->name);
   if (sym->inlined)
    ret += repsep_snprintf(bf + ret, size - ret,
             " (inlined)");
  }
 } else {
  size_t len = BITS_PER_LONG / 4;
  ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx",
           len, ip);
 }

 return ret;
}

int hist_entry__sym_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width)
{
 return _hist_entry__sym_snprintf(&he->ms, he->ip,
      he->level, bf, size, width);
}

static int hist_entry__sym_filter(struct hist_entry *he, int type, const void *arg)
{
 const char *sym = arg;

 if (type != HIST_FILTER__SYMBOL)
  return -1;

 return sym && (!he->ms.sym || !strstr(he->ms.sym->name, sym));
}

struct sort_entry sort_sym = {
 .se_header = "Symbol",
 .se_cmp  = sort__sym_cmp,
 .se_sort = sort__sym_sort,
 .se_snprintf = hist_entry__sym_snprintf,
 .se_filter = hist_entry__sym_filter,
 .se_width_idx = HISTC_SYMBOL,
};

/* --sort symoff */

static int64_t
sort__symoff_cmp(struct hist_entry *left, struct hist_entry *right)
{
 int64_t ret;

 ret = sort__sym_cmp(left, right);
 if (ret)
  return ret;

 return left->ip - right->ip;
}

static int64_t
sort__symoff_sort(struct hist_entry *left, struct hist_entry *right)
{
 int64_t ret;

 ret = sort__sym_sort(left, right);
 if (ret)
  return ret;

 return left->ip - right->ip;
}

static int
hist_entry__symoff_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width)
{
 struct symbol *sym = he->ms.sym;

 if (sym == NULL)
  return repsep_snprintf(bf, size, "[%c] %-#.*llx", he->level, width - 4, he->ip);

 return repsep_snprintf(bf, size, "[%c] %s+0x%llx", he->level, sym->name, he->ip - sym->start);
}

struct sort_entry sort_sym_offset = {
 .se_header = "Symbol Offset",
 .se_cmp  = sort__symoff_cmp,
 .se_sort = sort__symoff_sort,
 .se_snprintf = hist_entry__symoff_snprintf,
 .se_filter = hist_entry__sym_filter,
 .se_width_idx = HISTC_SYMBOL_OFFSET,
};

/* --sort srcline */

char *hist_entry__srcline(struct hist_entry *he)
{
 return map__srcline(he->ms.map, he->ip, he->ms.sym);
}

static int64_t
sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right)
{
 int64_t ret;

 ret = _sort__addr_cmp(left->ip, right->ip);
 if (ret)
  return ret;

 return sort__dso_cmp(left, right);
}

static int64_t
sort__srcline_collapse(struct hist_entry *left, struct hist_entry *right)
{
 if (!left->srcline)
  left->srcline = hist_entry__srcline(left);
 if (!right->srcline)
  right->srcline = hist_entry__srcline(right);

 return strcmp(right->srcline, left->srcline);
}

static int64_t
sort__srcline_sort(struct hist_entry *left, struct hist_entry *right)
{
 return sort__srcline_collapse(left, right);
}

static void
sort__srcline_init(struct hist_entry *he)
{
 if (!he->srcline)
  he->srcline = hist_entry__srcline(he);
}

static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf,
     size_t size, unsigned int width)
{
 return repsep_snprintf(bf, size, "%-.*s", width, he->srcline);
}

struct sort_entry sort_srcline = {
 .se_header = "Source:Line",
 .se_cmp  = sort__srcline_cmp,
 .se_collapse = sort__srcline_collapse,
 .se_sort = sort__srcline_sort,
 .se_init = sort__srcline_init,
 .se_snprintf = hist_entry__srcline_snprintf,
 .se_width_idx = HISTC_SRCLINE,
};

/* --sort srcline_from */

static char *addr_map_symbol__srcline(struct addr_map_symbol *ams)
{
 return map__srcline(ams->ms.map, ams->al_addr, ams->ms.sym);
}

static int64_t
sort__srcline_from_cmp(struct hist_entry *left, struct hist_entry *right)
{
 return left->branch_info->from.addr - right->branch_info->from.addr;
}

static int64_t
sort__srcline_from_collapse(struct hist_entry *left, struct hist_entry *right)
{
 if (!left->branch_info->srcline_from)
  left->branch_info->srcline_from = addr_map_symbol__srcline(&left->branch_info->from);

 if (!right->branch_info->srcline_from)
  right->branch_info->srcline_from = addr_map_symbol__srcline(&right->branch_info->from);

 return strcmp(right->branch_info->srcline_from, left->branch_info->srcline_from);
}

static int64_t
sort__srcline_from_sort(struct hist_entry *left, struct hist_entry *right)
{
 return sort__srcline_from_collapse(left, right);
}

static void sort__srcline_from_init(struct hist_entry *he)
{
 if (!he->branch_info->srcline_from)
  he->branch_info->srcline_from = addr_map_symbol__srcline(&he->branch_info->from);
}

static int hist_entry__srcline_from_snprintf(struct hist_entry *he, char *bf,
     size_t size, unsigned int width)
{
 return repsep_snprintf(bf, size, "%-*.*s", width, width, he->branch_info->srcline_from);
}

struct sort_entry sort_srcline_from = {
 .se_header = "From Source:Line",
 .se_cmp  = sort__srcline_from_cmp,
 .se_collapse = sort__srcline_from_collapse,
 .se_sort = sort__srcline_from_sort,
 .se_init = sort__srcline_from_init,
 .se_snprintf = hist_entry__srcline_from_snprintf,
 .se_width_idx = HISTC_SRCLINE_FROM,
};

/* --sort srcline_to */

static int64_t
sort__srcline_to_cmp(struct hist_entry *left, struct hist_entry *right)
{
 return left->branch_info->to.addr - right->branch_info->to.addr;
}

static int64_t
sort__srcline_to_collapse(struct hist_entry *left, struct hist_entry *right)
{
 if (!left->branch_info->srcline_to)
  left->branch_info->srcline_to = addr_map_symbol__srcline(&left->branch_info->to);

 if (!right->branch_info->srcline_to)
  right->branch_info->srcline_to = addr_map_symbol__srcline(&right->branch_info->to);

 return strcmp(right->branch_info->srcline_to, left->branch_info->srcline_to);
}

static int64_t
sort__srcline_to_sort(struct hist_entry *left, struct hist_entry *right)
{
 return sort__srcline_to_collapse(left, right);
}

static void sort__srcline_to_init(struct hist_entry *he)
{
 if (!he->branch_info->srcline_to)
  he->branch_info->srcline_to = addr_map_symbol__srcline(&he->branch_info->to);
}

static int hist_entry__srcline_to_snprintf(struct hist_entry *he, char *bf,
     size_t size, unsigned int width)
{
 return repsep_snprintf(bf, size, "%-*.*s", width, width, he->branch_info->srcline_to);
}

struct sort_entry sort_srcline_to = {
 .se_header = "To Source:Line",
 .se_cmp  = sort__srcline_to_cmp,
 .se_collapse = sort__srcline_to_collapse,
 .se_sort = sort__srcline_to_sort,
 .se_init = sort__srcline_to_init,
 .se_snprintf = hist_entry__srcline_to_snprintf,
 .se_width_idx = HISTC_SRCLINE_TO,
};

static int hist_entry__sym_ipc_snprintf(struct hist_entry *he, char *bf,
     size_t size, unsigned int width)
{

 struct symbol *sym = he->ms.sym;
 struct annotated_branch *branch;
 double ipc = 0.0, coverage = 0.0;
 char tmp[64];

 if (!sym)
  return repsep_snprintf(bf, size, "%-*s", width, "-");

 branch = symbol__annotation(sym)->branch;

 if (branch && branch->hit_cycles)
  ipc = branch->hit_insn / ((double)branch->hit_cycles);

 if (branch && branch->total_insn) {
  coverage = branch->cover_insn * 100.0 /
   ((double)branch->total_insn);
 }

 snprintf(tmp, sizeof(tmp), "%-5.2f [%5.1f%%]", ipc, coverage);
 return repsep_snprintf(bf, size, "%-*s", width, tmp);
}

struct sort_entry sort_sym_ipc = {
 .se_header = "IPC [IPC Coverage]",
 .se_cmp  = sort__sym_cmp,
 .se_snprintf = hist_entry__sym_ipc_snprintf,
 .se_width_idx = HISTC_SYMBOL_IPC,
};

static int hist_entry__sym_ipc_null_snprintf(struct hist_entry *he
          __maybe_unused,
          char *bf, size_t size,
          unsigned int width)
{
 char tmp[64];

 snprintf(tmp, sizeof(tmp), "%-5s %2s""-""-");
 return repsep_snprintf(bf, size, "%-*s", width, tmp);
}

struct sort_entry sort_sym_ipc_null = {
 .se_header = "IPC [IPC Coverage]",
 .se_cmp  = sort__sym_cmp,
 .se_snprintf = hist_entry__sym_ipc_null_snprintf,
 .se_width_idx = HISTC_SYMBOL_IPC,
};

/* --sort callchain_branch_predicted */

static int64_t
sort__callchain_branch_predicted_cmp(struct hist_entry *left __maybe_unused,
         struct hist_entry *right __maybe_unused)
{
 return 0;
}

static int hist_entry__callchain_branch_predicted_snprintf(
 struct hist_entry *he, char *bf, size_t size, unsigned int width)
{
 u64 branch_count, predicted_count;
 double percent = 0.0;
 char str[32];

 callchain_branch_counts(he->callchain, &branch_count,
    &predicted_count, NULL, NULL);

 if (branch_count)
  percent = predicted_count * 100.0 / branch_count;

 snprintf(str, sizeof(str), "%.1f%%", percent);
 return repsep_snprintf(bf, size, "%-*.*s", width, width, str);
}

struct sort_entry sort_callchain_branch_predicted = {
 .se_header = "Predicted",
 .se_cmp  = sort__callchain_branch_predicted_cmp,
 .se_snprintf = hist_entry__callchain_branch_predicted_snprintf,
 .se_width_idx = HISTC_CALLCHAIN_BRANCH_PREDICTED,
};

/* --sort callchain_branch_abort */

static int64_t
sort__callchain_branch_abort_cmp(struct hist_entry *left __maybe_unused,
     struct hist_entry *right __maybe_unused)
{
 return 0;
}

static int hist_entry__callchain_branch_abort_snprintf(struct hist_entry *he,
             char *bf, size_t size,
             unsigned int width)
{
 u64 branch_count, abort_count;
 char str[32];

 callchain_branch_counts(he->callchain, &branch_count,
    NULL, &abort_count, NULL);

 snprintf(str, sizeof(str), "%" PRId64, abort_count);
 return repsep_snprintf(bf, size, "%-*.*s", width, width, str);
}

struct sort_entry sort_callchain_branch_abort = {
 .se_header = "Abort",
 .se_cmp  = sort__callchain_branch_abort_cmp,
 .se_snprintf = hist_entry__callchain_branch_abort_snprintf,
 .se_width_idx = HISTC_CALLCHAIN_BRANCH_ABORT,
};

/* --sort callchain_branch_cycles */

static int64_t
sort__callchain_branch_cycles_cmp(struct hist_entry *left __maybe_unused,
      struct hist_entry *right __maybe_unused)
{
 return 0;
}

static int hist_entry__callchain_branch_cycles_snprintf(struct hist_entry *he,
       char *bf, size_t size,
       unsigned int width)
{
 u64 branch_count, cycles_count, cycles = 0;
 char str[32];

 callchain_branch_counts(he->callchain, &branch_count,
    NULL, NULL, &cycles_count);

 if (branch_count)
  cycles = cycles_count / branch_count;

 snprintf(str, sizeof(str), "%" PRId64 "", cycles);
 return repsep_snprintf(bf, size, "%-*.*s", width, width, str);
}

struct sort_entry sort_callchain_branch_cycles = {
 .se_header = "Cycles",
 .se_cmp  = sort__callchain_branch_cycles_cmp,
 .se_snprintf = hist_entry__callchain_branch_cycles_snprintf,
 .se_width_idx = HISTC_CALLCHAIN_BRANCH_CYCLES,
};

/* --sort srcfile */

static char no_srcfile[1];

static char *hist_entry__get_srcfile(struct hist_entry *e)
{
 char *sf, *p;
 struct map *map = e->ms.map;

 if (!map)
  return no_srcfile;

 sf = __get_srcline(map__dso(map), map__rip_2objdump(map, e->ip),
    e->ms.sym, falsetruetrue, e->ip);
 if (sf == SRCLINE_UNKNOWN)
  return no_srcfile;
 p = strchr(sf, ':');
 if (p && *sf) {
  *p = 0;
  return sf;
 }
 free(sf);
 return no_srcfile;
}

static int64_t
sort__srcfile_cmp(struct hist_entry *left, struct hist_entry *right)
{
 return sort__srcline_cmp(left, right);
}

static int64_t
sort__srcfile_collapse(struct hist_entry *left, struct hist_entry *right)
{
 if (!left->srcfile)
  left->srcfile = hist_entry__get_srcfile(left);
 if (!right->srcfile)
  right->srcfile = hist_entry__get_srcfile(right);

 return strcmp(right->srcfile, left->srcfile);
}

static int64_t
sort__srcfile_sort(struct hist_entry *left, struct hist_entry *right)
{
 return sort__srcfile_collapse(left, right);
}

static void sort__srcfile_init(struct hist_entry *he)
{
 if (!he->srcfile)
  he->srcfile = hist_entry__get_srcfile(he);
}

static int hist_entry__srcfile_snprintf(struct hist_entry *he, char *bf,
     size_t size, unsigned int width)
{
 return repsep_snprintf(bf, size, "%-.*s", width, he->srcfile);
}

struct sort_entry sort_srcfile = {
 .se_header = "Source File",
 .se_cmp  = sort__srcfile_cmp,
 .se_collapse = sort__srcfile_collapse,
 .se_sort = sort__srcfile_sort,
 .se_init = sort__srcfile_init,
 .se_snprintf = hist_entry__srcfile_snprintf,
 .se_width_idx = HISTC_SRCFILE,
};

/* --sort parent */

static int64_t
sort__parent_cmp(struct hist_entry *left, struct hist_entry *right)
{
 struct symbol *sym_l = left->parent;
 struct symbol *sym_r = right->parent;

 if (!sym_l || !sym_r)
  return cmp_null(sym_l, sym_r);

 return strcmp(sym_r->name, sym_l->name);
}

static int hist_entry__parent_snprintf(struct hist_entry *he, char *bf,
           size_t size, unsigned int width)
{
 return repsep_snprintf(bf, size, "%-*.*s", width, width,
         he->parent ? he->parent->name : "[other]");
}

struct sort_entry sort_parent = {
 .se_header = "Parent symbol",
 .se_cmp  = sort__parent_cmp,
 .se_snprintf = hist_entry__parent_snprintf,
 .se_width_idx = HISTC_PARENT,
};

/* --sort cpu */

static int64_t
sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right)
{
 return right->cpu - left->cpu;
}

static int hist_entry__cpu_snprintf(struct hist_entry *he, char *bf,
        size_t size, unsigned int width)
{
 return repsep_snprintf(bf, size, "%*.*d", width, width, he->cpu);
}

struct sort_entry sort_cpu = {
 .se_header      = "CPU",
 .se_cmp         = sort__cpu_cmp,
 .se_snprintf    = hist_entry__cpu_snprintf,
 .se_width_idx = HISTC_CPU,
};

/* --sort parallelism */

static int64_t
sort__parallelism_cmp(struct hist_entry *left, struct hist_entry *right)
{
 return right->parallelism - left->parallelism;
}

static int hist_entry__parallelism_filter(struct hist_entry *he, int type, const void *arg)
{
 const unsigned long *parallelism_filter = arg;

 if (type != HIST_FILTER__PARALLELISM)
  return -1;

 return test_bit(he->parallelism, parallelism_filter);
}

static int hist_entry__parallelism_snprintf(struct hist_entry *he, char *bf,
        size_t size, unsigned int width)
{
 return repsep_snprintf(bf, size, "%*d", width, he->parallelism);
}

struct sort_entry sort_parallelism = {
 .se_header      = "Parallelism",
 .se_cmp         = sort__parallelism_cmp,
 .se_filter = hist_entry__parallelism_filter,
 .se_snprintf    = hist_entry__parallelism_snprintf,
 .se_width_idx = HISTC_PARALLELISM,
};

/* --sort cgroup_id */

static int64_t _sort__cgroup_dev_cmp(u64 left_dev, u64 right_dev)
{
 return (int64_t)(right_dev - left_dev);
}

static int64_t _sort__cgroup_inode_cmp(u64 left_ino, u64 right_ino)
{
 return (int64_t)(right_ino - left_ino);
}

static int64_t
sort__cgroup_id_cmp(struct hist_entry *left, struct hist_entry *right)
{
 int64_t ret;

 ret = _sort__cgroup_dev_cmp(right->cgroup_id.dev, left->cgroup_id.dev);
 if (ret != 0)
  return ret;

 return _sort__cgroup_inode_cmp(right->cgroup_id.ino,
           left->cgroup_id.ino);
}

static int hist_entry__cgroup_id_snprintf(struct hist_entry *he,
       char *bf, size_t size,
       unsigned int width __maybe_unused)
{
 return repsep_snprintf(bf, size, "%lu/0x%lx", he->cgroup_id.dev,
          he->cgroup_id.ino);
}

struct sort_entry sort_cgroup_id = {
 .se_header      = "cgroup id (dev/inode)",
 .se_cmp         = sort__cgroup_id_cmp,
 .se_snprintf    = hist_entry__cgroup_id_snprintf,
 .se_width_idx = HISTC_CGROUP_ID,
};

/* --sort cgroup */

static int64_t
sort__cgroup_cmp(struct hist_entry *left, struct hist_entry *right)
{
 return right->cgroup - left->cgroup;
}

static int hist_entry__cgroup_snprintf(struct hist_entry *he,
           char *bf, size_t size,
           unsigned int width __maybe_unused)
{
 const char *cgrp_name = "N/A";

 if (he->cgroup) {
  struct cgroup *cgrp = cgroup__find(maps__machine(he->ms.maps)->env,
         he->cgroup);
  if (cgrp != NULL)
   cgrp_name = cgrp->name;
  else
   cgrp_name = "unknown";
 }

 return repsep_snprintf(bf, size, "%s", cgrp_name);
}

struct sort_entry sort_cgroup = {
 .se_header      = "Cgroup",
 .se_cmp         = sort__cgroup_cmp,
 .se_snprintf    = hist_entry__cgroup_snprintf,
 .se_width_idx = HISTC_CGROUP,
};

/* --sort socket */

static int64_t
sort__socket_cmp(struct hist_entry *left, struct hist_entry *right)
{
 return right->socket - left->socket;
}

static int hist_entry__socket_snprintf(struct hist_entry *he, char *bf,
        size_t size, unsigned int width)
{
 return repsep_snprintf(bf, size, "%*.*d", width, width-3, he->socket);
}

static int hist_entry__socket_filter(struct hist_entry *he, int type, const void *arg)
{
 int sk = *(const int *)arg;

 if (type != HIST_FILTER__SOCKET)
  return -1;

 return sk >= 0 && he->socket != sk;
}

struct sort_entry sort_socket = {
 .se_header      = "Socket",
 .se_cmp         = sort__socket_cmp,
 .se_snprintf    = hist_entry__socket_snprintf,
 .se_filter      = hist_entry__socket_filter,
 .se_width_idx = HISTC_SOCKET,
};

/* --sort time */

static int64_t
sort__time_cmp(struct hist_entry *left, struct hist_entry *right)
{
 return right->time - left->time;
}

static int hist_entry__time_snprintf(struct hist_entry *he, char *bf,
        size_t size, unsigned int width)
{
 char he_time[32];

 if (symbol_conf.nanosecs)
  timestamp__scnprintf_nsec(he->time, he_time,
       sizeof(he_time));
 else
  timestamp__scnprintf_usec(he->time, he_time,
       sizeof(he_time));

 return repsep_snprintf(bf, size, "%-.*s", width, he_time);
}

struct sort_entry sort_time = {
 .se_header      = "Time",
 .se_cmp         = sort__time_cmp,
 .se_snprintf    = hist_entry__time_snprintf,
 .se_width_idx = HISTC_TIME,
};

/* --sort trace */

#ifdef HAVE_LIBTRACEEVENT
static char *get_trace_output(struct hist_entry *he)
{
 struct trace_seq seq;
 struct evsel *evsel;
 struct tep_record rec = {
  .data = he->raw_data,
  .size = he->raw_size,
 };
 struct tep_event *tp_format;

 evsel = hists_to_evsel(he->hists);

 trace_seq_init(&seq);
 tp_format = evsel__tp_format(evsel);
 if (tp_format) {
  if (symbol_conf.raw_trace)
   tep_print_fields(&seq, he->raw_data, he->raw_size, tp_format);
  else
   tep_print_event(tp_format->tep, &seq, &rec, "%s", TEP_PRINT_INFO);
 }

 /*
 * Trim the buffer, it starts at 4KB and we're not going to
 * add anything more to this buffer.
 */

 return realloc(seq.buffer, seq.len + 1);
}

static int64_t
sort__trace_cmp(struct hist_entry *left, struct hist_entry *right)
{
 struct evsel *evsel;

 evsel = hists_to_evsel(left->hists);
 if (evsel->core.attr.type != PERF_TYPE_TRACEPOINT)
  return 0;

 if (left->trace_output == NULL)
  left->trace_output = get_trace_output(left);
 if (right->trace_output == NULL)
  right->trace_output = get_trace_output(right);

 return strcmp(right->trace_output, left->trace_output);
}

static int hist_entry__trace_snprintf(struct hist_entry *he, char *bf,
        size_t size, unsigned int width)
{
 struct evsel *evsel;

 evsel = hists_to_evsel(he->hists);
 if (evsel->core.attr.type != PERF_TYPE_TRACEPOINT)
  return scnprintf(bf, size, "%-.*s", width, "N/A");

 if (he->trace_output == NULL)
  he->trace_output = get_trace_output(he);
 return repsep_snprintf(bf, size, "%-.*s", width, he->trace_output);
}

struct sort_entry sort_trace = {
 .se_header      = "Trace output",
 .se_cmp         = sort__trace_cmp,
 .se_snprintf    = hist_entry__trace_snprintf,
 .se_width_idx = HISTC_TRACE,
};
#endif /* HAVE_LIBTRACEEVENT */

/* sort keys for branch stacks */

static int64_t
sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right)
{
 if (!left->branch_info || !right->branch_info)
  return cmp_null(left->branch_info, right->branch_info);

 return _sort__dso_cmp(left->branch_info->from.ms.map,
         right->branch_info->from.ms.map);
}

static int hist_entry__dso_from_snprintf(struct hist_entry *he, char *bf,
        size_t size, unsigned int width)
{
 if (he->branch_info)
  return _hist_entry__dso_snprintf(he->branch_info->from.ms.map,
       bf, size, width);
 else
  return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
}

static int hist_entry__dso_from_filter(struct hist_entry *he, int type,
           const void *arg)
{
 const struct dso *dso = arg;

 if (type != HIST_FILTER__DSO)
  return -1;

 return dso && (!he->branch_info || !he->branch_info->from.ms.map ||
  map__dso(he->branch_info->from.ms.map) != dso);
}

static int64_t
sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right)
{
 if (!left->branch_info || !right->branch_info)
  return cmp_null(left->branch_info, right->branch_info);

 return _sort__dso_cmp(left->branch_info->to.ms.map,
         right->branch_info->to.ms.map);
}

static int hist_entry__dso_to_snprintf(struct hist_entry *he, char *bf,
           size_t size, unsigned int width)
{
 if (he->branch_info)
  return _hist_entry__dso_snprintf(he->branch_info->to.ms.map,
       bf, size, width);
 else
  return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
}

static int hist_entry__dso_to_filter(struct hist_entry *he, int type,
         const void *arg)
{
 const struct dso *dso = arg;

 if (type != HIST_FILTER__DSO)
  return -1;

 return dso && (!he->branch_info || !he->branch_info->to.ms.map ||
  map__dso(he->branch_info->to.ms.map) != dso);
}

static int64_t
sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right)
{
 struct addr_map_symbol *from_l, *from_r;

 if (!left->branch_info || !right->branch_info)
  return cmp_null(left->branch_info, right->branch_info);

 from_l = &left->branch_info->from;
 from_r = &right->branch_info->from;

 if (!from_l->ms.sym && !from_r->ms.sym)
  return _sort__addr_cmp(from_l->addr, from_r->addr);

 return _sort__sym_cmp(from_l->ms.sym, from_r->ms.sym);
}

static int64_t
sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right)
{
 struct addr_map_symbol *to_l, *to_r;

 if (!left->branch_info || !right->branch_info)
  return cmp_null(left->branch_info, right->branch_info);

 to_l = &left->branch_info->to;
 to_r = &right->branch_info->to;

 if (!to_l->ms.sym && !to_r->ms.sym)
  return _sort__addr_cmp(to_l->addr, to_r->addr);

 return _sort__sym_cmp(to_l->ms.sym, to_r->ms.sym);
}

static int hist_entry__sym_from_snprintf(struct hist_entry *he, char *bf,
      size_t size, unsigned int width)
{
 if (he->branch_info) {
  struct addr_map_symbol *from = &he->branch_info->from;

  return _hist_entry__sym_snprintf(&from->ms, from->al_addr,
       from->al_level, bf, size, width);
 }

 return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
}

static int hist_entry__sym_to_snprintf(struct hist_entry *he, char *bf,
           size_t size, unsigned int width)
{
 if (he->branch_info) {
  struct addr_map_symbol *to = &he->branch_info->to;

  return _hist_entry__sym_snprintf(&to->ms, to->al_addr,
       to->al_level, bf, size, width);
 }

 return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
}

static int hist_entry__sym_from_filter(struct hist_entry *he, int type,
           const void *arg)
{
 const char *sym = arg;

 if (type != HIST_FILTER__SYMBOL)
  return -1;

 return sym && !(he->branch_info && he->branch_info->from.ms.sym &&
   strstr(he->branch_info->from.ms.sym->name, sym));
}

static int hist_entry__sym_to_filter(struct hist_entry *he, int type,
           const void *arg)
{
 const char *sym = arg;

 if (type != HIST_FILTER__SYMBOL)
  return -1;

 return sym && !(he->branch_info && he->branch_info->to.ms.sym &&
          strstr(he->branch_info->to.ms.sym->name, sym));
}

struct sort_entry sort_dso_from = {
 .se_header = "Source Shared Object",
 .se_cmp  = sort__dso_from_cmp,
 .se_snprintf = hist_entry__dso_from_snprintf,
 .se_filter = hist_entry__dso_from_filter,
 .se_width_idx = HISTC_DSO_FROM,
};

struct sort_entry sort_dso_to = {
 .se_header = "Target Shared Object",
 .se_cmp  = sort__dso_to_cmp,
 .se_snprintf = hist_entry__dso_to_snprintf,
 .se_filter = hist_entry__dso_to_filter,
 .se_width_idx = HISTC_DSO_TO,
};

struct sort_entry sort_sym_from = {
 .se_header = "Source Symbol",
 .se_cmp  = sort__sym_from_cmp,
 .se_snprintf = hist_entry__sym_from_snprintf,
 .se_filter = hist_entry__sym_from_filter,
 .se_width_idx = HISTC_SYMBOL_FROM,
};

struct sort_entry sort_sym_to = {
 .se_header = "Target Symbol",
 .se_cmp  = sort__sym_to_cmp,
 .se_snprintf = hist_entry__sym_to_snprintf,
 .se_filter = hist_entry__sym_to_filter,
 .se_width_idx = HISTC_SYMBOL_TO,
};

static int _hist_entry__addr_snprintf(struct map_symbol *ms,
         u64 ip, char level, char *bf, size_t size,
         unsigned int width)
{
 struct symbol *sym = ms->sym;
 struct map *map = ms->map;
 size_t ret = 0, offs;

 ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level);
 if (sym && map) {
  if (sym->type == STT_OBJECT) {
   ret += repsep_snprintf(bf + ret, size - ret, "%s", sym->name);
   ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx",
     ip - map__unmap_ip(map, sym->start));
  } else {
   ret += repsep_snprintf(bf + ret, size - ret, "%.*s",
            width - ret,
            sym->name);
   offs = ip - sym->start;
   if (offs)
    ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx", offs);
  }
 } else {
  size_t len = BITS_PER_LONG / 4;
  ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx",
           len, ip);
 }

 return ret;
}

static int hist_entry__addr_from_snprintf(struct hist_entry *he, char *bf,
      size_t size, unsigned int width)
{
 if (he->branch_info) {
  struct addr_map_symbol *from = &he->branch_info->from;

  return _hist_entry__addr_snprintf(&from->ms, from->al_addr,
       he->level, bf, size, width);
 }

 return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
}

static int hist_entry__addr_to_snprintf(struct hist_entry *he, char *bf,
           size_t size, unsigned int width)
{
 if (he->branch_info) {
  struct addr_map_symbol *to = &he->branch_info->to;

  return _hist_entry__addr_snprintf(&to->ms, to->al_addr,
       he->level, bf, size, width);
 }

 return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
}

static int64_t
sort__addr_from_cmp(struct hist_entry *left, struct hist_entry *right)
{
 struct addr_map_symbol *from_l;
 struct addr_map_symbol *from_r;
 int64_t ret;

 if (!left->branch_info || !right->branch_info)
  return cmp_null(left->branch_info, right->branch_info);

 from_l = &left->branch_info->from;
 from_r = &right->branch_info->from;

 /*
 * comparing symbol address alone is not enough since it's a
 * relative address within a dso.
 */

 ret = _sort__dso_cmp(from_l->ms.map, from_r->ms.map);
 if (ret != 0)
  return ret;

 return _sort__addr_cmp(from_l->addr, from_r->addr);
}

static int64_t
sort__addr_to_cmp(struct hist_entry *left, struct hist_entry *right)
{
 struct addr_map_symbol *to_l;
 struct addr_map_symbol *to_r;
 int64_t ret;

 if (!left->branch_info || !right->branch_info)
  return cmp_null(left->branch_info, right->branch_info);

 to_l = &left->branch_info->to;
 to_r = &right->branch_info->to;

 /*
 * comparing symbol address alone is not enough since it's a
 * relative address within a dso.
 */

 ret = _sort__dso_cmp(to_l->ms.map, to_r->ms.map);
 if (ret != 0)
  return ret;

 return _sort__addr_cmp(to_l->addr, to_r->addr);
}

struct sort_entry sort_addr_from = {
 .se_header = "Source Address",
 .se_cmp  = sort__addr_from_cmp,
 .se_snprintf = hist_entry__addr_from_snprintf,
 .se_filter = hist_entry__sym_from_filter, /* shared with sym_from */
 .se_width_idx = HISTC_ADDR_FROM,
};

struct sort_entry sort_addr_to = {
 .se_header = "Target Address",
 .se_cmp  = sort__addr_to_cmp,
 .se_snprintf = hist_entry__addr_to_snprintf,
 .se_filter = hist_entry__sym_to_filter, /* shared with sym_to */
 .se_width_idx = HISTC_ADDR_TO,
};


static int64_t
sort__mispredict_cmp(struct hist_entry *left, struct hist_entry *right)
{
 unsigned char mp, p;

 if (!left->branch_info || !right->branch_info)
  return cmp_null(left->branch_info, right->branch_info);

 mp = left->branch_info->flags.mispred != right->branch_info->flags.mispred;
 p  = left->branch_info->flags.predicted != right->branch_info->flags.predicted;
 return mp || p;
}

static int hist_entry__mispredict_snprintf(struct hist_entry *he, char *bf,
        size_t size, unsigned int width){
 static const char *out = "N/A";

 if (he->branch_info) {
  if (he->branch_info->flags.predicted)
   out = "N";
  else if (he->branch_info->flags.mispred)
   out = "Y";
 }

 return repsep_snprintf(bf, size, "%-*.*s", width, width, out);
}

static int64_t
sort__cycles_cmp(struct hist_entry *left, struct hist_entry *right)
{
 if (!left->branch_info || !right->branch_info)
  return cmp_null(left->branch_info, right->branch_info);

 return left->branch_info->flags.cycles -
  right->branch_info->flags.cycles;
}

static int hist_entry__cycles_snprintf(struct hist_entry *he, char *bf,
        size_t size, unsigned int width)
{
 if (!he->branch_info)
  return scnprintf(bf, size, "%-.*s", width, "N/A");
 if (he->branch_info->flags.cycles == 0)
  return repsep_snprintf(bf, size, "%-*s", width, "-");
 return repsep_snprintf(bf, size, "%-*hd", width,
          he->branch_info->flags.cycles);
}

struct sort_entry sort_cycles = {
 .se_header = "Basic Block Cycles",
 .se_cmp  = sort__cycles_cmp,
 .se_snprintf = hist_entry__cycles_snprintf,
 .se_width_idx = HISTC_CYCLES,
};

/* --sort daddr_sym */
int64_t
sort__daddr_cmp(struct hist_entry *left, struct hist_entry *right)
{
 uint64_t l = 0, r = 0;

 if (left->mem_info)
  l = mem_info__daddr(left->mem_info)->addr;
 if (right->mem_info)
  r = mem_info__daddr(right->mem_info)->addr;

 return (int64_t)(r - l);
}

static int hist_entry__daddr_snprintf(struct hist_entry *he, char *bf,
        size_t size, unsigned int width)
{
 uint64_t addr = 0;
 struct map_symbol *ms = NULL;

 if (he->mem_info) {
  addr = mem_info__daddr(he->mem_info)->addr;
  ms = &mem_info__daddr(he->mem_info)->ms;
 }
 return _hist_entry__sym_snprintf(ms, addr, he->level, bf, size, width);
}

int64_t
sort__iaddr_cmp(struct hist_entry *left, struct hist_entry *right)
{
 uint64_t l = 0, r = 0;

 if (left->mem_info)
  l = mem_info__iaddr(left->mem_info)->addr;
 if (right->mem_info)
  r = mem_info__iaddr(right->mem_info)->addr;

 return (int64_t)(r - l);
}

static int hist_entry__iaddr_snprintf(struct hist_entry *he, char *bf,
        size_t size, unsigned int width)
{
 uint64_t addr = 0;
 struct map_symbol *ms = NULL;

 if (he->mem_info) {
  addr = mem_info__iaddr(he->mem_info)->addr;
  ms   = &mem_info__iaddr(he->mem_info)->ms;
 }
 return _hist_entry__sym_snprintf(ms, addr, he->level, bf, size, width);
}

static int64_t
sort__dso_daddr_cmp(struct hist_entry *left, struct hist_entry *right)
{
 struct map *map_l = NULL;
 struct map *map_r = NULL;

 if (left->mem_info)
  map_l = mem_info__daddr(left->mem_info)->ms.map;
 if (right->mem_info)
  map_r = mem_info__daddr(right->mem_info)->ms.map;

 return _sort__dso_cmp(map_l, map_r);
}

static int hist_entry__dso_daddr_snprintf(struct hist_entry *he, char *bf,
        size_t size, unsigned int width)
{
 struct map *map = NULL;

 if (he->mem_info)
  map = mem_info__daddr(he->mem_info)->ms.map;

 return _hist_entry__dso_snprintf(map, bf, size, width);
}

static int64_t
sort__locked_cmp(struct hist_entry *left, struct hist_entry *right)
{
 union perf_mem_data_src data_src_l;
 union perf_mem_data_src data_src_r;

 if (left->mem_info)
  data_src_l = *mem_info__data_src(left->mem_info);
 else
  data_src_l.mem_lock = PERF_MEM_LOCK_NA;

 if (right->mem_info)
  data_src_r = *mem_info__data_src(right->mem_info);
 else
  data_src_r.mem_lock = PERF_MEM_LOCK_NA;

 return (int64_t)(data_src_r.mem_lock - data_src_l.mem_lock);
}

static int hist_entry__locked_snprintf(struct hist_entry *he, char *bf,
        size_t size, unsigned int width)
{
 char out[10];

 perf_mem__lck_scnprintf(out, sizeof(out), he->mem_info);
 return repsep_snprintf(bf, size, "%.*s", width, out);
}

static int64_t
sort__tlb_cmp(struct hist_entry *left, struct hist_entry *right)
{
 union perf_mem_data_src data_src_l;
 union perf_mem_data_src data_src_r;

 if (left->mem_info)
  data_src_l = *mem_info__data_src(left->mem_info);
 else
  data_src_l.mem_dtlb = PERF_MEM_TLB_NA;

 if (right->mem_info)
  data_src_r = *mem_info__data_src(right->mem_info);
 else
  data_src_r.mem_dtlb = PERF_MEM_TLB_NA;

 return (int64_t)(data_src_r.mem_dtlb - data_src_l.mem_dtlb);
}

static int hist_entry__tlb_snprintf(struct hist_entry *he, char *bf,
        size_t size, unsigned int width)
{
 char out[64];

 perf_mem__tlb_scnprintf(out, sizeof(out), he->mem_info);
 return repsep_snprintf(bf, size, "%-*s", width, out);
}

static int64_t
sort__lvl_cmp(struct hist_entry *left, struct hist_entry *right)
{
 union perf_mem_data_src data_src_l;
 union perf_mem_data_src data_src_r;

 if (left->mem_info)
  data_src_l = *mem_info__data_src(left->mem_info);
 else
  data_src_l.mem_lvl = PERF_MEM_LVL_NA;

 if (right->mem_info)
  data_src_r = *mem_info__data_src(right->mem_info);
 else
  data_src_r.mem_lvl = PERF_MEM_LVL_NA;

 return (int64_t)(data_src_r.mem_lvl - data_src_l.mem_lvl);
}

static int hist_entry__lvl_snprintf(struct hist_entry *he, char *bf,
        size_t size, unsigned int width)
{
 char out[64];

 perf_mem__lvl_scnprintf(out, sizeof(out), he->mem_info);
 return repsep_snprintf(bf, size, "%-*s", width, out);
}

static int64_t
sort__snoop_cmp(struct hist_entry *left, struct hist_entry *right)
{
 union perf_mem_data_src data_src_l;
 union perf_mem_data_src data_src_r;

 if (left->mem_info)
  data_src_l = *mem_info__data_src(left->mem_info);
 else
  data_src_l.mem_snoop = PERF_MEM_SNOOP_NA;

 if (right->mem_info)
  data_src_r = *mem_info__data_src(right->mem_info);
 else
  data_src_r.mem_snoop = PERF_MEM_SNOOP_NA;

 return (int64_t)(data_src_r.mem_snoop - data_src_l.mem_snoop);
}

static int hist_entry__snoop_snprintf(struct hist_entry *he, char *bf,
        size_t size, unsigned int width)
{
 char out[64];

 perf_mem__snp_scnprintf(out, sizeof(out), he->mem_info);
 return repsep_snprintf(bf, size, "%-*s", width, out);
}

int64_t
sort__dcacheline_cmp(struct hist_entry *left, struct hist_entry *right)
{
 u64 l, r;
 struct map *l_map, *r_map;
 struct dso *l_dso, *r_dso;
 int rc;

 if (!left->mem_info)  return -1;
 if (!right->mem_info) return 1;

 /* group event types together */
 if (left->cpumode > right->cpumode) return -1;
 if (left->cpumode < right->cpumode) return 1;

 l_map = mem_info__daddr(left->mem_info)->ms.map;
 r_map = mem_info__daddr(right->mem_info)->ms.map;

 /* if both are NULL, jump to sort on al_addr instead */
 if (!l_map && !r_map)
  goto addr;

 if (!l_map) return -1;
 if (!r_map) return 1;

 l_dso = map__dso(l_map);
 r_dso = map__dso(r_map);
 rc = dso__cmp_id(l_dso, r_dso);
 if (rc)
  return rc;
 /*
 * Addresses with no major/minor numbers or build ID are assumed to be
 * anonymous in userspace.  Sort those on pid then address.
 *
 * The kernel and non-zero major/minor mapped areas are
 * assumed to be unity mapped.  Sort those on address.
 */

 if (left->cpumode != PERF_RECORD_MISC_KERNEL && (map__flags(l_map) & MAP_SHARED) == 0) {
  const struct dso_id *dso_id = dso__id_const(l_dso);

  if (!dso_id->mmap2_valid)
   dso_id = dso__id_const(r_dso);

  if (!build_id__is_defined(&dso_id->build_id) &&
      (!dso_id->mmap2_valid || (dso_id->maj == 0 && dso_id->min == 0))) {
   /* userspace anonymous */

   if (thread__pid(left->thread) > thread__pid(right->thread))
    return -1;
   if (thread__pid(left->thread) < thread__pid(right->thread))
    return 1;
  }
 }

addr:
 /* al_addr does all the right addr - start + offset calculations */
 l = cl_address(mem_info__daddr(left->mem_info)->al_addr, chk_double_cl);
 r = cl_address(mem_info__daddr(right->mem_info)->al_addr, chk_double_cl);

 if (l > r) return -1;
 if (l < r) return 1;

 return 0;
}

static int hist_entry__dcacheline_snprintf(struct hist_entry *he, char *bf,
       size_t size, unsigned int width)
{

 uint64_t addr = 0;
 struct map_symbol *ms = NULL;
 char level = he->level;

 if (he->mem_info) {
  struct map *map = mem_info__daddr(he->mem_info)->ms.map;
  struct dso *dso = map ? map__dso(map) : NULL;
  const struct dso_id *dso_id = dso ? dso__id_const(dso) : &dso_id_empty;

  addr = cl_address(mem_info__daddr(he->mem_info)->al_addr, chk_double_cl);
  ms = &mem_info__daddr(he->mem_info)->ms;

  /* print [s] for shared data mmaps */
  if ((he->cpumode != PERF_RECORD_MISC_KERNEL) &&
       map && !(map__prot(map) & PROT_EXEC) &&
       (map__flags(map) & MAP_SHARED) &&
       (!dso_id->mmap2_valid || (dso_id->maj == 0 && dso_id->min == 0)))
   level = 's';
  else if (!map)
   level = 'X';
 }
 return _hist_entry__sym_snprintf(ms, addr, level, bf, size, width);
}

struct sort_entry sort_mispredict = {
 .se_header = "Branch Mispredicted",
 .se_cmp  = sort__mispredict_cmp,
 .se_snprintf = hist_entry__mispredict_snprintf,
 .se_width_idx = HISTC_MISPREDICT,
};

static int64_t
sort__weight_cmp(struct hist_entry *left, struct hist_entry *right)
{
 return left->weight - right->weight;
}

static int hist_entry__local_weight_snprintf(struct hist_entry *he, char *bf,
        size_t size, unsigned int width)
{
 return repsep_snprintf(bf, size, "%-*llu", width, he->weight);
}

struct sort_entry sort_local_weight = {
 .se_header = "Local Weight",
 .se_cmp  = sort__weight_cmp,
 .se_snprintf = hist_entry__local_weight_snprintf,
 .se_width_idx = HISTC_LOCAL_WEIGHT,
};

static int hist_entry__global_weight_snprintf(struct hist_entry *he, char *bf,
           size_t size, unsigned int width)
{
 return repsep_snprintf(bf, size, "%-*llu", width,
          he->weight * he->stat.nr_events);
}

struct sort_entry sort_global_weight = {
 .se_header = "Weight",
 .se_cmp  = sort__weight_cmp,
 .se_snprintf = hist_entry__global_weight_snprintf,
 .se_width_idx = HISTC_GLOBAL_WEIGHT,
};

static int64_t
sort__ins_lat_cmp(struct hist_entry *left, struct hist_entry *right)
{
 return left->ins_lat - right->ins_lat;
}

static int hist_entry__local_ins_lat_snprintf(struct hist_entry *he, char *bf,
           size_t size, unsigned int width)
{
 return repsep_snprintf(bf, size, "%-*u", width, he->ins_lat);
}

struct sort_entry sort_local_ins_lat = {
 .se_header = "Local INSTR Latency",
 .se_cmp  = sort__ins_lat_cmp,
 .se_snprintf = hist_entry__local_ins_lat_snprintf,
 .se_width_idx = HISTC_LOCAL_INS_LAT,
};

static int hist_entry__global_ins_lat_snprintf(struct hist_entry *he, char *bf,
            size_t size, unsigned int width)
{
 return repsep_snprintf(bf, size, "%-*u", width,
          he->ins_lat * he->stat.nr_events);
}

struct sort_entry sort_global_ins_lat = {
 .se_header = "INSTR Latency",
 .se_cmp  = sort__ins_lat_cmp,
 .se_snprintf = hist_entry__global_ins_lat_snprintf,
 .se_width_idx = HISTC_GLOBAL_INS_LAT,
};

static int64_t
sort__p_stage_cyc_cmp(struct hist_entry *left, struct hist_entry *right)
{
 return left->weight3 - right->weight3;
}

static int hist_entry__global_p_stage_cyc_snprintf(struct hist_entry *he, char *bf,
     size_t size, unsigned int width)
{
 return repsep_snprintf(bf, size, "%-*u", width, he->weight3 * he->stat.nr_events);
}


static int hist_entry__p_stage_cyc_snprintf(struct hist_entry *he, char *bf,
     size_t size, unsigned int width)
{
 return repsep_snprintf(bf, size, "%-*u", width, he->weight3);
}

struct sort_entry sort_local_p_stage_cyc = {
 .se_header      = "Local Pipeline Stage Cycle",
 .se_cmp         = sort__p_stage_cyc_cmp,
 .se_snprintf = hist_entry__p_stage_cyc_snprintf,
 .se_width_idx = HISTC_LOCAL_P_STAGE_CYC,
};

struct sort_entry sort_global_p_stage_cyc = {
 .se_header      = "Pipeline Stage Cycle",
 .se_cmp         = sort__p_stage_cyc_cmp,
 .se_snprintf    = hist_entry__global_p_stage_cyc_snprintf,
 .se_width_idx   = HISTC_GLOBAL_P_STAGE_CYC,
};

struct sort_entry sort_mem_daddr_sym = {
 .se_header = "Data Symbol",
 .se_cmp  = sort__daddr_cmp,
 .se_snprintf = hist_entry__daddr_snprintf,
 .se_width_idx = HISTC_MEM_DADDR_SYMBOL,
};

struct sort_entry sort_mem_iaddr_sym = {
 .se_header = "Code Symbol",
 .se_cmp  = sort__iaddr_cmp,
 .se_snprintf = hist_entry__iaddr_snprintf,
 .se_width_idx = HISTC_MEM_IADDR_SYMBOL,
};

struct sort_entry sort_mem_daddr_dso = {
 .se_header = "Data Object",
 .se_cmp  = sort__dso_daddr_cmp,
 .se_snprintf = hist_entry__dso_daddr_snprintf,
 .se_width_idx = HISTC_MEM_DADDR_DSO,
};

struct sort_entry sort_mem_locked = {
 .se_header = "Locked",
 .se_cmp  = sort__locked_cmp,
 .se_snprintf = hist_entry__locked_snprintf,
 .se_width_idx = HISTC_MEM_LOCKED,
};

struct sort_entry sort_mem_tlb = {
 .se_header = "TLB access",
 .se_cmp  = sort__tlb_cmp,
 .se_snprintf = hist_entry__tlb_snprintf,
 .se_width_idx = HISTC_MEM_TLB,
};

struct sort_entry sort_mem_lvl = {
 .se_header = "Memory access",
 .se_cmp  = sort__lvl_cmp,
 .se_snprintf = hist_entry__lvl_snprintf,
 .se_width_idx = HISTC_MEM_LVL,
};

struct sort_entry sort_mem_snoop = {
 .se_header = "Snoop",
 .se_cmp  = sort__snoop_cmp,
 .se_snprintf = hist_entry__snoop_snprintf,
 .se_width_idx = HISTC_MEM_SNOOP,
};

struct sort_entry sort_mem_dcacheline = {
 .se_header = "Data Cacheline",
 .se_cmp  = sort__dcacheline_cmp,
 .se_snprintf = hist_entry__dcacheline_snprintf,
 .se_width_idx = HISTC_MEM_DCACHELINE,
};

static int64_t
sort__blocked_cmp(struct hist_entry *left, struct hist_entry *right)
{
 union perf_mem_data_src data_src_l;
 union perf_mem_data_src data_src_r;

 if (left->mem_info)
  data_src_l = *mem_info__data_src(left->mem_info);
 else
  data_src_l.mem_blk = PERF_MEM_BLK_NA;

 if (right->mem_info)
  data_src_r = *mem_info__data_src(right->mem_info);
 else
  data_src_r.mem_blk = PERF_MEM_BLK_NA;

 return (int64_t)(data_src_r.mem_blk - data_src_l.mem_blk);
}

static int hist_entry__blocked_snprintf(struct hist_entry *he, char *bf,
     size_t size, unsigned int width)
{
 char out[16];

 perf_mem__blk_scnprintf(out, sizeof(out), he->mem_info);
 return repsep_snprintf(bf, size, "%.*s", width, out);
}

struct sort_entry sort_mem_blocked = {
 .se_header = "Blocked",
 .se_cmp  = sort__blocked_cmp,
 .se_snprintf = hist_entry__blocked_snprintf,
 .se_width_idx = HISTC_MEM_BLOCKED,
};

static int64_t
sort__phys_daddr_cmp(struct hist_entry *left, struct hist_entry *right)
{
 uint64_t l = 0, r = 0;

 if (left->mem_info)
  l = mem_info__daddr(left->mem_info)->phys_addr;
 if (right->mem_info)
  r = mem_info__daddr(right->mem_info)->phys_addr;

 return (int64_t)(r - l);
}

static int hist_entry__phys_daddr_snprintf(struct hist_entry *he, char *bf,
        size_t size, unsigned int width)
{
 uint64_t addr = 0;
 size_t ret = 0;
 size_t len = BITS_PER_LONG / 4;

 addr = mem_info__daddr(he->mem_info)->phys_addr;

 ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", he->level);

 ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx", len, addr);

 ret += repsep_snprintf(bf + ret, size - ret, "%-*s", width - ret, "");

 if (ret > width)
  bf[width] = '\0';

 return width;
}

struct sort_entry sort_mem_phys_daddr = {
 .se_header = "Data Physical Address",
 .se_cmp  = sort__phys_daddr_cmp,
 .se_snprintf = hist_entry__phys_daddr_snprintf,
 .se_width_idx = HISTC_MEM_PHYS_DADDR,
};

static int64_t
sort__data_page_size_cmp(struct hist_entry *left, struct hist_entry *right)
{
 uint64_t l = 0, r = 0;

 if (left->mem_info)
  l = mem_info__daddr(left->mem_info)->data_page_size;
 if (right->mem_info)
  r = mem_info__daddr(right->mem_info)->data_page_size;

 return (int64_t)(r - l);
}

static int hist_entry__data_page_size_snprintf(struct hist_entry *he, char *bf,
       size_t size, unsigned int width)
{
 char str[PAGE_SIZE_NAME_LEN];

 return repsep_snprintf(bf, size, "%-*s", width,
   get_page_size_name(mem_info__daddr(he->mem_info)->data_page_size, str));
}

struct sort_entry sort_mem_data_page_size = {
 .se_header = "Data Page Size",
 .se_cmp  = sort__data_page_size_cmp,
 .se_snprintf = hist_entry__data_page_size_snprintf,
 .se_width_idx = HISTC_MEM_DATA_PAGE_SIZE,
};

static int64_t
sort__code_page_size_cmp(struct hist_entry *left, struct hist_entry *right)
{
 uint64_t l = left->code_page_size;
 uint64_t r = right->code_page_size;

 return (int64_t)(r - l);
}

static int hist_entry__code_page_size_snprintf(struct hist_entry *he, char *bf,
       size_t size, unsigned int width)
{
 char str[PAGE_SIZE_NAME_LEN];

 return repsep_snprintf(bf, size, "%-*s", width,
          get_page_size_name(he->code_page_size, str));
}

struct sort_entry sort_code_page_size = {
 .se_header = "Code Page Size",
 .se_cmp  = sort__code_page_size_cmp,
 .se_snprintf = hist_entry__code_page_size_snprintf,
 .se_width_idx = HISTC_CODE_PAGE_SIZE,
};

static int64_t
sort__abort_cmp(struct hist_entry *left, struct hist_entry *right)
{
 if (!left->branch_info || !right->branch_info)
  return cmp_null(left->branch_info, right->branch_info);

 return left->branch_info->flags.abort !=
  right->branch_info->flags.abort;
}

static int hist_entry__abort_snprintf(struct hist_entry *he, char *bf,
        size_t size, unsigned int width)
{
 static const char *out = "N/A";

 if (he->branch_info) {
  if (he->branch_info->flags.abort)
   out = "A";
  else
   out = ".";
 }

 return repsep_snprintf(bf, size, "%-*s", width, out);
}

struct sort_entry sort_abort = {
 .se_header = "Transaction abort",
 .se_cmp  = sort__abort_cmp,
 .se_snprintf = hist_entry__abort_snprintf,
 .se_width_idx = HISTC_ABORT,
};

static int64_t
sort__in_tx_cmp(struct hist_entry *left, struct hist_entry *right)
{
 if (!left->branch_info || !right->branch_info)
  return cmp_null(left->branch_info, right->branch_info);

 return left->branch_info->flags.in_tx !=
  right->branch_info->flags.in_tx;
}

static int hist_entry__in_tx_snprintf(struct hist_entry *he, char *bf,
        size_t size, unsigned int width)
{
 static const char *out = "N/A";

 if (he->branch_info) {
  if (he->branch_info->flags.in_tx)
   out = "T";
  else
   out = ".";
 }

 return repsep_snprintf(bf, size, "%-*s", width, out);
}

struct sort_entry sort_in_tx = {
 .se_header = "Branch in transaction",
 .se_cmp  = sort__in_tx_cmp,
 .se_snprintf = hist_entry__in_tx_snprintf,
 .se_width_idx = HISTC_IN_TX,
};

static int64_t
sort__transaction_cmp(struct hist_entry *left, struct hist_entry *right)
{
 return left->transaction - right->transaction;
}

static inline char *add_str(char *p, const char *str)
{
 strcpy(p, str);
 return p + strlen(str);
}

static struct txbit {
 unsigned flag;
 const char *name;
 int skip_for_len;
} txbits[] = {
 { PERF_TXN_ELISION,        "EL ",        0 },
 { PERF_TXN_TRANSACTION,    "TX ",        1 },
 { PERF_TXN_SYNC,           "SYNC ",      1 },
 { PERF_TXN_ASYNC,          "ASYNC ",     0 },
 { PERF_TXN_RETRY,          "RETRY ",     0 },
 { PERF_TXN_CONFLICT,       "CON ",       0 },
 { PERF_TXN_CAPACITY_WRITE, "CAP-WRITE ", 1 },
 { PERF_TXN_CAPACITY_READ,  "CAP-READ ",  0 },
 { 0, NULL, 0 }
};

int hist_entry__transaction_len(void)
{
 int i;
 int len = 0;

 for (i = 0; txbits[i].name; i++) {
  if (!txbits[i].skip_for_len)
   len += strlen(txbits[i].name);
 }
 len += 4; /* :XX<space> */
 return len;
}

static int hist_entry__transaction_snprintf(struct hist_entry *he, char *bf,
         size_t size, unsigned int width)
{
 u64 t = he->transaction;
 char buf[128];
 char *p = buf;
 int i;

 buf[0] = 0;
 for (i = 0; txbits[i].name; i++)
  if (txbits[i].flag & t)
   p = add_str(p, txbits[i].name);
 if (t && !(t & (PERF_TXN_SYNC|PERF_TXN_ASYNC)))
  p = add_str(p, "NEITHER ");
 if (t & PERF_TXN_ABORT_MASK) {
  sprintf(p, ":%" PRIx64,
   (t & PERF_TXN_ABORT_MASK) >>
   PERF_TXN_ABORT_SHIFT);
  p += strlen(p);
 }

 return repsep_snprintf(bf, size, "%-*s", width, buf);
}

struct sort_entry sort_transaction = {
 .se_header = "Transaction ",
 .se_cmp  = sort__transaction_cmp,
 .se_snprintf = hist_entry__transaction_snprintf,
 .se_width_idx = HISTC_TRANSACTION,
};

/* --sort symbol_size */

static int64_t _sort__sym_size_cmp(struct symbol *sym_l, struct symbol *sym_r)
{
 int64_t size_l = sym_l != NULL ? symbol__size(sym_l) : 0;
 int64_t size_r = sym_r != NULL ? symbol__size(sym_r) : 0;

 return size_l < size_r ? -1 :
  size_l == size_r ? 0 : 1;
}

static int64_t
sort__sym_size_cmp(struct hist_entry *left, struct hist_entry *right)
{
 return _sort__sym_size_cmp(right->ms.sym, left->ms.sym);
}

static int _hist_entry__sym_size_snprintf(struct symbol *sym, char *bf,
       size_t bf_size, unsigned int width)
{
 if (sym)
  return repsep_snprintf(bf, bf_size, "%*d", width, symbol__size(sym));

 return repsep_snprintf(bf, bf_size, "%*s", width, "unknown");
}

static int hist_entry__sym_size_snprintf(struct hist_entry *he, char *bf,
      size_t size, unsigned int width)
{
 return _hist_entry__sym_size_snprintf(he->ms.sym, bf, size, width);
}

struct sort_entry sort_sym_size = {
 .se_header = "Symbol size",
 .se_cmp  = sort__sym_size_cmp,
 .se_snprintf = hist_entry__sym_size_snprintf,
 .se_width_idx = HISTC_SYM_SIZE,
};

/* --sort dso_size */

static int64_t _sort__dso_size_cmp(struct map *map_l, struct map *map_r)
{
 int64_t size_l = map_l != NULL ? map__size(map_l) : 0;
 int64_t size_r = map_r != NULL ? map__size(map_r) : 0;

 return size_l < size_r ? -1 :
  size_l == size_r ? 0 : 1;
}

static int64_t
sort__dso_size_cmp(struct hist_entry *left, struct hist_entry *right)
{
 return _sort__dso_size_cmp(right->ms.map, left->ms.map);
}

static int _hist_entry__dso_size_snprintf(struct map *map, char *bf,
       size_t bf_size, unsigned int width)
{
 if (map && map__dso(map))
  return repsep_snprintf(bf, bf_size, "%*d", width, map__size(map));

 return repsep_snprintf(bf, bf_size, "%*s", width, "unknown");
}

static int hist_entry__dso_size_snprintf(struct hist_entry *he, char *bf,
      size_t size, unsigned int width)
{
 return _hist_entry__dso_size_snprintf(he->ms.map, bf, size, width);
}

struct sort_entry sort_dso_size = {
 .se_header = "DSO size",
 .se_cmp  = sort__dso_size_cmp,
 .se_snprintf = hist_entry__dso_size_snprintf,
 .se_width_idx = HISTC_DSO_SIZE,
};

/* --sort addr */

static int64_t
sort__addr_cmp(struct hist_entry *left, struct hist_entry *right)
{
 u64 left_ip = left->ip;
 u64 right_ip = right->ip;
 struct map *left_map = left->ms.map;
 struct map *right_map = right->ms.map;

 if (left_map)
  left_ip = map__unmap_ip(left_map, left_ip);
 if (right_map)
  right_ip = map__unmap_ip(right_map, right_ip);

 return _sort__addr_cmp(left_ip, right_ip);
}

static int hist_entry__addr_snprintf(struct hist_entry *he, char *bf,
         size_t size, unsigned int width)
{
 u64 ip = he->ip;
 struct map *map = he->ms.map;

 if (map)
  ip = map__unmap_ip(map, ip);

 return repsep_snprintf(bf, size, "%-#*llx", width, ip);
}

struct sort_entry sort_addr = {
 .se_header = "Address",
 .se_cmp  = sort__addr_cmp,
 .se_snprintf = hist_entry__addr_snprintf,
 .se_width_idx = HISTC_ADDR,
};

/* --sort type */

struct annotated_data_type unknown_type = {
 .self = {
  .type_name = (char *)"(unknown)",
  .children = LIST_HEAD_INIT(unknown_type.self.children),
 },
};

static int64_t
sort__type_cmp(struct hist_entry *left, struct hist_entry *right)
{
 return sort__addr_cmp(left, right);
}

static void sort__type_init(struct hist_entry *he)
{
 if (he->mem_type)
  return;

 he->mem_type = hist_entry__get_data_type(he);
 if (he->mem_type == NULL) {
  he->mem_type = &unknown_type;
  he->mem_type_off = 0;
 }
}

static int64_t
sort__type_collapse(struct hist_entry *left, struct hist_entry *right)
{
 struct annotated_data_type *left_type = left->mem_type;
 struct annotated_data_type *right_type = right->mem_type;

 if (!left_type) {
  sort__type_init(left);
  left_type = left->mem_type;
 }

 if (!right_type) {
  sort__type_init(right);
  right_type = right->mem_type;
 }

 return strcmp(left_type->self.type_name, right_type->self.type_name);
}

static int64_t
sort__type_sort(struct hist_entry *left, struct hist_entry *right)
{
 return sort__type_collapse(left, right);
}

static int hist_entry__type_snprintf(struct hist_entry *he, char *bf,
         size_t size, unsigned int width)
{
 return repsep_snprintf(bf, size, "%-*s", width, he->mem_type->self.type_name);
}

struct sort_entry sort_type = {
 .se_header = "Data Type",
 .se_cmp  = sort__type_cmp,
 .se_collapse = sort__type_collapse,
 .se_sort = sort__type_sort,
 .se_init = sort__type_init,
 .se_snprintf = hist_entry__type_snprintf,
 .se_width_idx = HISTC_TYPE,
};

/* --sort typeoff */

static int64_t
sort__typeoff_sort(struct hist_entry *left, struct hist_entry *right)
{
 struct annotated_data_type *left_type = left->mem_type;
 struct annotated_data_type *right_type = right->mem_type;
 int64_t ret;

 if (!left_type) {
  sort__type_init(left);
  left_type = left->mem_type;
 }

 if (!right_type) {
  sort__type_init(right);
  right_type = right->mem_type;
 }

 ret = strcmp(left_type->self.type_name, right_type->self.type_name);
 if (ret)
  return ret;
 return left->mem_type_off - right->mem_type_off;
}

static int hist_entry__typeoff_snprintf(struct hist_entry *he, char *bf,
         size_t size, unsigned int width __maybe_unused)
{
 struct annotated_data_type *he_type = he->mem_type;
 char buf[4096];

 if (he_type == &unknown_type || he_type == &stackop_type ||
     he_type == &canary_type)
  return repsep_snprintf(bf, size, "%s", he_type->self.type_name);

 if (!annotated_data_type__get_member_name(he_type, buf, sizeof(buf),
        he->mem_type_off))
  scnprintf(buf, sizeof(buf), "no field");

 return repsep_snprintf(bf, size, "%s +%#x (%s)", he_type->self.type_name,
          he->mem_type_off, buf);
}

struct sort_entry sort_type_offset = {
 .se_header = "Data Type Offset",
 .se_cmp  = sort__type_cmp,
 .se_collapse = sort__typeoff_sort,
 .se_sort = sort__typeoff_sort,
 .se_init = sort__type_init,
 .se_snprintf = hist_entry__typeoff_snprintf,
 .se_width_idx = HISTC_TYPE_OFFSET,
};

/* --sort typecln */

/* TODO: use actual value in the system */
#define TYPE_CACHELINE_SIZE  64

static int64_t
sort__typecln_sort(struct hist_entry *left, struct hist_entry *right)
{
 struct annotated_data_type *left_type = left->mem_type;
 struct annotated_data_type *right_type = right->mem_type;
 int64_t left_cln, right_cln;
 int64_t ret;

 if (!left_type) {
  sort__type_init(left);
  left_type = left->mem_type;
 }

 if (!right_type) {
  sort__type_init(right);
  right_type = right->mem_type;
 }

 ret = strcmp(left_type->self.type_name, right_type->self.type_name);
 if (ret)
  return ret;

 left_cln = left->mem_type_off / TYPE_CACHELINE_SIZE;
 right_cln = right->mem_type_off / TYPE_CACHELINE_SIZE;
 return left_cln - right_cln;
}

static int hist_entry__typecln_snprintf(struct hist_entry *he, char *bf,
         size_t size, unsigned int width __maybe_unused)
{
 struct annotated_data_type *he_type = he->mem_type;

 return repsep_snprintf(bf, size, "%s: cache-line %d", he_type->self.type_name,
          he->mem_type_off / TYPE_CACHELINE_SIZE);
}

struct sort_entry sort_type_cacheline = {
 .se_header = "Data Type Cacheline",
 .se_cmp  = sort__type_cmp,
 .se_collapse = sort__typecln_sort,
 .se_sort = sort__typecln_sort,
 .se_init = sort__type_init,
 .se_snprintf = hist_entry__typecln_snprintf,
 .se_width_idx = HISTC_TYPE_CACHELINE,
};


struct sort_dimension {
 const char  *name;
 struct sort_entry *entry;
 int   taken;
};

--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=93 H=93 G=92

¤ Dauer der Verarbeitung: 0.72 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Versionsinformation zu Columbo

Bemerkung:

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Anfrage:

Dauer der Verarbeitung:

Sekunden

sprechenden Kalenders