Merge branch 'perf/urgent' into perf/core
authorArnaldo Carvalho de Melo <acme@redhat.com>
Thu, 22 Mar 2012 18:09:08 +0000 (15:09 -0300)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Thu, 22 Mar 2012 18:09:08 +0000 (15:09 -0300)
Merge Reason: to pick the fix:

 commit e7f01d1
     perf tools: Use scnprintf where applicable

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

1  2 
tools/perf/Makefile
tools/perf/perf.h
tools/perf/util/header.c
tools/perf/util/hist.c
tools/perf/util/parse-events.c
tools/perf/util/sort.c
tools/perf/util/ui/browsers/hists.c

diff --combined tools/perf/Makefile
@@@ -15,16 -15,6 +15,16 @@@ endi
  
  # Define V to have a more verbose compile.
  #
 +# Define O to save output files in a separate directory.
 +#
 +# Define ARCH as name of target architecture if you want cross-builds.
 +#
 +# Define CROSS_COMPILE as prefix name of compiler if you want cross-builds.
 +#
 +# Define NO_LIBPERL to disable perl script extension.
 +#
 +# Define NO_LIBPYTHON to disable python script extension.
 +#
  # Define PYTHON to point to the python binary if the default
  # `python' is not correct; for example: PYTHON=python2
  #
  # Define NO_DWARF if you do not want debug-info analysis feature at all.
  #
  # Define WERROR=0 to disable treating any warnings as errors.
 +#
 +# Define NO_NEWT if you do not want TUI support.
 +#
 +# Define NO_DEMANGLE if you do not want C++ symbol demangling.
  
  $(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE
        @$(SHELL_PATH) util/PERF-VERSION-GEN $(OUTPUT)
@@@ -61,8 -47,6 +61,8 @@@ ARCH ?= $(shell echo $(uname_M) | sed -
  
  CC = $(CROSS_COMPILE)gcc
  AR = $(CROSS_COMPILE)ar
 +FLEX = $(CROSS_COMPILE)flex
 +BISON= $(CROSS_COMPILE)bison
  
  # Additional ARCH settings for x86
  ifeq ($(ARCH),i386)
@@@ -77,7 -61,7 +77,7 @@@ ifeq ($(ARCH),x86_64
        ifeq (${IS_X86_64}, 1)
                RAW_ARCH := x86_64
                ARCH_CFLAGS := -DARCH_X86_64
 -              ARCH_INCLUDE = ../../arch/x86/lib/memcpy_64.S
 +              ARCH_INCLUDE = ../../arch/x86/lib/memcpy_64.S ../../arch/x86/lib/memset_64.S
        endif
  endif
  
@@@ -199,10 -183,7 +199,10 @@@ SCRIPT_SH += perf-archive.s
  grep-libs = $(filter -l%,$(1))
  strip-libs = $(filter-out -l%,$(1))
  
 -$(OUTPUT)python/perf.so: $(PYRF_OBJS)
 +PYTHON_EXT_SRCS := $(shell grep -v ^\# util/python-ext-sources)
 +PYTHON_EXT_DEPS := util/python-ext-sources util/setup.py
 +
 +$(OUTPUT)python/perf.so: $(PYRF_OBJS) $(PYTHON_EXT_SRCS) $(PYTHON_EXT_DEPS)
        $(QUIET_GEN)CFLAGS='$(BASIC_CFLAGS)' $(PYTHON_WORD) util/setup.py \
          --quiet build_ext; \
        mkdir -p $(OUTPUT)python && \
@@@ -268,6 -249,8 +268,8 @@@ LIB_H += util/include/asm/uaccess.
  LIB_H += util/include/dwarf-regs.h
  LIB_H += util/include/asm/dwarf2.h
  LIB_H += util/include/asm/cpufeature.h
+ LIB_H += util/include/asm/unistd_32.h
+ LIB_H += util/include/asm/unistd_64.h
  LIB_H += perf.h
  LIB_H += util/annotate.h
  LIB_H += util/cache.h
@@@ -275,8 -258,6 +277,8 @@@ LIB_H += util/callchain.
  LIB_H += util/build-id.h
  LIB_H += util/debug.h
  LIB_H += util/debugfs.h
 +LIB_H += util/sysfs.h
 +LIB_H += util/pmu.h
  LIB_H += util/event.h
  LIB_H += util/evsel.h
  LIB_H += util/evlist.h
@@@ -323,8 -304,6 +325,8 @@@ LIB_OBJS += $(OUTPUT)util/build-id.
  LIB_OBJS += $(OUTPUT)util/config.o
  LIB_OBJS += $(OUTPUT)util/ctype.o
  LIB_OBJS += $(OUTPUT)util/debugfs.o
 +LIB_OBJS += $(OUTPUT)util/sysfs.o
 +LIB_OBJS += $(OUTPUT)util/pmu.o
  LIB_OBJS += $(OUTPUT)util/environment.o
  LIB_OBJS += $(OUTPUT)util/event.o
  LIB_OBJS += $(OUTPUT)util/evlist.o
@@@ -361,10 -340,6 +363,10 @@@ LIB_OBJS += $(OUTPUT)util/session.
  LIB_OBJS += $(OUTPUT)util/thread.o
  LIB_OBJS += $(OUTPUT)util/thread_map.o
  LIB_OBJS += $(OUTPUT)util/trace-event-parse.o
 +LIB_OBJS += $(OUTPUT)util/parse-events-flex.o
 +LIB_OBJS += $(OUTPUT)util/parse-events-bison.o
 +LIB_OBJS += $(OUTPUT)util/pmu-flex.o
 +LIB_OBJS += $(OUTPUT)util/pmu-bison.o
  LIB_OBJS += $(OUTPUT)util/trace-event-read.o
  LIB_OBJS += $(OUTPUT)util/trace-event-info.o
  LIB_OBJS += $(OUTPUT)util/trace-event-scripting.o
@@@ -386,10 -361,8 +388,10 @@@ BUILTIN_OBJS += $(OUTPUT)bench/sched-me
  BUILTIN_OBJS += $(OUTPUT)bench/sched-pipe.o
  ifeq ($(RAW_ARCH),x86_64)
  BUILTIN_OBJS += $(OUTPUT)bench/mem-memcpy-x86-64-asm.o
 +BUILTIN_OBJS += $(OUTPUT)bench/mem-memset-x86-64-asm.o
  endif
  BUILTIN_OBJS += $(OUTPUT)bench/mem-memcpy.o
 +BUILTIN_OBJS += $(OUTPUT)bench/mem-memset.o
  
  BUILTIN_OBJS += $(OUTPUT)builtin-diff.o
  BUILTIN_OBJS += $(OUTPUT)builtin-evlist.o
@@@ -507,20 -480,6 +509,20 @@@ els
        endif
  endif
  
 +ifdef NO_GTK2
 +      BASIC_CFLAGS += -DNO_GTK2
 +else
 +      FLAGS_GTK2=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) $(shell pkg-config --libs --cflags gtk+-2.0)
 +      ifneq ($(call try-cc,$(SOURCE_GTK2),$(FLAGS_GTK2)),y)
 +              msg := $(warning GTK2 not found, disables GTK2 support. Please install gtk2-devel or libgtk2.0-dev);
 +              BASIC_CFLAGS += -DNO_GTK2_SUPPORT
 +      else
 +              BASIC_CFLAGS += $(shell pkg-config --cflags gtk+-2.0)
 +              EXTLIBS += $(shell pkg-config --libs gtk+-2.0)
 +              LIB_OBJS += $(OUTPUT)util/gtk/browser.o
 +      endif
 +endif
 +
  ifdef NO_LIBPERL
        BASIC_CFLAGS += -DNO_LIBPERL
  else
@@@ -667,8 -626,6 +669,8 @@@ ifndef 
        QUIET_LINK     = @echo '   ' LINK $@;
        QUIET_MKDIR    = @echo '   ' MKDIR $@;
        QUIET_GEN      = @echo '   ' GEN $@;
 +      QUIET_FLEX     = @echo '   ' FLEX $@;
 +      QUIET_BISON    = @echo '   ' BISON $@;
  endif
  endif
  
@@@ -749,19 -706,12 +751,19 @@@ $(OUTPUT)perf.o perf.spec 
        $(SCRIPTS) \
        : $(OUTPUT)PERF-VERSION-FILE
  
 +.SUFFIXES:
 +.SUFFIXES: .o .c .S .s
 +
  $(OUTPUT)%.o: %.c $(OUTPUT)PERF-CFLAGS
        $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $<
 +$(OUTPUT)%.i: %.c $(OUTPUT)PERF-CFLAGS
 +      $(QUIET_CC)$(CC) -o $@ -E $(ALL_CFLAGS) $<
  $(OUTPUT)%.s: %.c $(OUTPUT)PERF-CFLAGS
 -      $(QUIET_CC)$(CC) -S $(ALL_CFLAGS) $<
 +      $(QUIET_CC)$(CC) -o $@ -S $(ALL_CFLAGS) $<
  $(OUTPUT)%.o: %.S
        $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $<
 +$(OUTPUT)%.s: %.S
 +      $(QUIET_CC)$(CC) -o $@ -E $(ALL_CFLAGS) $<
  
  $(OUTPUT)util/exec_cmd.o: util/exec_cmd.c $(OUTPUT)PERF-CFLAGS
        $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \
@@@ -788,12 -738,6 +790,12 @@@ $(OUTPUT)util/ui/browsers/map.o: util/u
  $(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS
        $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $<
  
 +$(OUTPUT)util/parse-events-flex.o: util/parse-events-flex.c $(OUTPUT)PERF-CFLAGS
 +      $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -Wno-redundant-decls -Wno-switch-default -Wno-unused-function $<
 +
 +$(OUTPUT)util/pmu-flex.o: util/pmu-flex.c $(OUTPUT)PERF-CFLAGS
 +      $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -Wno-redundant-decls -Wno-switch-default -Wno-unused-function $<
 +
  $(OUTPUT)util/scripting-engines/trace-event-perl.o: util/scripting-engines/trace-event-perl.c $(OUTPUT)PERF-CFLAGS
        $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow $<
  
@@@ -830,8 -774,6 +832,8 @@@ help
        @echo '  html           - make html documentation'
        @echo '  info           - make GNU info documentation (access with info <foo>)'
        @echo '  pdf            - make pdf documentation'
 +      @echo '  event-parser   - make event parser code'
 +      @echo '  pmu-parser     - make pmu format parser code'
        @echo '  TAGS           - use etags to make tag information for source browsing'
        @echo '  tags           - use ctags to make tag information for source browsing'
        @echo '  cscope - use cscope to make interactive browsing database'
        @echo '  quick-install-html     - install the html documentation quickly'
        @echo ''
        @echo 'Perf maintainer targets:'
 -      @echo '  distclean              - alias to clean'
        @echo '  clean                  - clean all binary objects and build output'
  
  doc:
@@@ -881,14 -824,6 +883,14 @@@ cscope
        $(RM) cscope*
        $(FIND) . -name '*.[hcS]' -print | xargs cscope -b
  
 +event-parser:
 +      $(QUIET_BISON)$(BISON) -v util/parse-events.y -d -o util/parse-events-bison.c
 +      $(QUIET_FLEX)$(FLEX) --header-file=util/parse-events-flex.h -t util/parse-events.l > util/parse-events-flex.c
 +
 +pmu-parser:
 +      $(QUIET_BISON)$(BISON) -v util/pmu.y -d -o util/pmu-bison.c
 +      $(QUIET_FLEX)$(FLEX) --header-file=util/pmu-flex.h -t util/pmu.l > util/pmu-flex.c
 +
  ### Detect prefix changes
  TRACK_CFLAGS = $(subst ','\'',$(ALL_CFLAGS)):\
               $(bindir_SQ):$(perfexecdir_SQ):$(template_dir_SQ):$(prefix_SQ)
diff --combined tools/perf/perf.h
@@@ -10,6 -10,9 +10,9 @@@ void get_term_dimensions(struct winsiz
  #define rmb()         asm volatile("lock; addl $0,0(%%esp)" ::: "memory")
  #define cpu_relax()   asm volatile("rep; nop" ::: "memory");
  #define CPUINFO_PROC  "model name"
+ #ifndef __NR_perf_event_open
+ # define __NR_perf_event_open 336
+ #endif
  #endif
  
  #if defined(__x86_64__)
@@@ -17,6 -20,9 +20,9 @@@
  #define rmb()         asm volatile("lfence" ::: "memory")
  #define cpu_relax()   asm volatile("rep; nop" ::: "memory");
  #define CPUINFO_PROC  "model name"
+ #ifndef __NR_perf_event_open
+ # define __NR_perf_event_open 298
+ #endif
  #endif
  
  #ifdef __powerpc__
@@@ -167,6 -173,7 +173,6 @@@ sys_perf_event_open(struct perf_event_a
                      pid_t pid, int cpu, int group_fd,
                      unsigned long flags)
  {
 -      attr->size = sizeof(*attr);
        return syscall(__NR_perf_event_open, attr, pid, cpu,
                       group_fd, flags);
  }
@@@ -179,32 -186,14 +185,32 @@@ struct ip_callchain 
        u64 ips[0];
  };
  
 +struct branch_flags {
 +      u64 mispred:1;
 +      u64 predicted:1;
 +      u64 reserved:62;
 +};
 +
 +struct branch_entry {
 +      u64                             from;
 +      u64                             to;
 +      struct branch_flags flags;
 +};
 +
 +struct branch_stack {
 +      u64                             nr;
 +      struct branch_entry     entries[0];
 +};
 +
  extern bool perf_host, perf_guest;
  extern const char perf_version_string[];
  
  void pthread__unblock_sigwinch(void);
  
  struct perf_record_opts {
 -      pid_t        target_pid;
 -      pid_t        target_tid;
 +      const char   *target_pid;
 +      const char   *target_tid;
 +      uid_t        uid;
        bool         call_graph;
        bool         group;
        bool         inherit_stat;
        bool         raw_samples;
        bool         sample_address;
        bool         sample_time;
 -      bool         sample_id_all_avail;
 +      bool         sample_id_all_missing;
        bool         exclude_guest_missing;
        bool         system_wide;
        bool         period;
        unsigned int freq;
        unsigned int mmap_pages;
        unsigned int user_freq;
 +      int          branch_stack;
        u64          default_interval;
        u64          user_interval;
        const char   *cpu_list;
diff --combined tools/perf/util/header.c
@@@ -63,20 -63,9 +63,20 @@@ char *perf_header__find_event(u64 id
        return NULL;
  }
  
 -static const char *__perf_magic = "PERFFILE";
 +/*
 + * magic2 = "PERFILE2"
 + * must be a numerical value to let the endianness
 + * determine the memory layout. That way we are able
 + * to detect endianness when reading the perf.data file
 + * back.
 + *
 + * we check for legacy (PERFFILE) format.
 + */
 +static const char *__perf_magic1 = "PERFFILE";
 +static const u64 __perf_magic2    = 0x32454c4946524550ULL;
 +static const u64 __perf_magic2_sw = 0x50455246494c4532ULL;
  
 -#define PERF_MAGIC    (*(u64 *)__perf_magic)
 +#define PERF_MAGIC    __perf_magic2
  
  struct perf_file_attr {
        struct perf_event_attr  attr;
@@@ -291,7 -280,7 +291,7 @@@ int build_id_cache__add_s(const char *s
        if (realname == NULL || filename == NULL || linkname == NULL)
                goto out_free;
  
-       len = snprintf(filename, size, "%s%s%s",
+       len = scnprintf(filename, size, "%s%s%s",
                       debugdir, is_kallsyms ? "/" : "", realname);
        if (mkdir_p(filename, 0755))
                goto out_free;
                        goto out_free;
        }
  
-       len = snprintf(linkname, size, "%s/.build-id/%.2s",
+       len = scnprintf(linkname, size, "%s/.build-id/%.2s",
                       debugdir, sbuild_id);
  
        if (access(linkname, X_OK) && mkdir_p(linkname, 0755))
@@@ -1023,12 -1012,6 +1023,12 @@@ write_it
        return do_write_string(fd, buffer);
  }
  
 +static int write_branch_stack(int fd __used, struct perf_header *h __used,
 +                     struct perf_evlist *evlist __used)
 +{
 +      return 0;
 +}
 +
  static void print_hostname(struct perf_header *ph, int fd, FILE *fp)
  {
        char *str = do_read_string(fd, ph);
@@@ -1150,9 -1133,8 +1150,9 @@@ static void print_event_desc(struct per
        uint64_t id;
        void *buf = NULL;
        char *str;
 -      u32 nre, sz, nr, i, j, msz;
 -      int ret;
 +      u32 nre, sz, nr, i, j;
 +      ssize_t ret;
 +      size_t msz;
  
        /* number of events */
        ret = read(fd, &nre, sizeof(nre));
        if (ph->needs_swap)
                sz = bswap_32(sz);
  
 -      /*
 -       * ensure it is at least to our ABI rev
 -       */
 -      if (sz < (u32)sizeof(attr))
 -              goto error;
 -
        memset(&attr, 0, sizeof(attr));
  
 -      /* read entire region to sync up to next field */
 +      /* buffer to hold on file attr struct */
        buf = malloc(sz);
        if (!buf)
                goto error;
  
        msz = sizeof(attr);
 -      if (sz < msz)
 +      if (sz < (ssize_t)msz)
                msz = sz;
  
        for (i = 0 ; i < nre; i++) {
  
 +              /*
 +               * must read entire on-file attr struct to
 +               * sync up with layout.
 +               */
                ret = read(fd, buf, sz);
                if (ret != (ssize_t)sz)
                        goto error;
@@@ -1321,204 -1305,25 +1321,204 @@@ static void print_cpuid(struct perf_hea
        free(str);
  }
  
 +static void print_branch_stack(struct perf_header *ph __used, int fd __used,
 +                             FILE *fp)
 +{
 +      fprintf(fp, "# contains samples with branch stack\n");
 +}
 +
 +static int __event_process_build_id(struct build_id_event *bev,
 +                                  char *filename,
 +                                  struct perf_session *session)
 +{
 +      int err = -1;
 +      struct list_head *head;
 +      struct machine *machine;
 +      u16 misc;
 +      struct dso *dso;
 +      enum dso_kernel_type dso_type;
 +
 +      machine = perf_session__findnew_machine(session, bev->pid);
 +      if (!machine)
 +              goto out;
 +
 +      misc = bev->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
 +
 +      switch (misc) {
 +      case PERF_RECORD_MISC_KERNEL:
 +              dso_type = DSO_TYPE_KERNEL;
 +              head = &machine->kernel_dsos;
 +              break;
 +      case PERF_RECORD_MISC_GUEST_KERNEL:
 +              dso_type = DSO_TYPE_GUEST_KERNEL;
 +              head = &machine->kernel_dsos;
 +              break;
 +      case PERF_RECORD_MISC_USER:
 +      case PERF_RECORD_MISC_GUEST_USER:
 +              dso_type = DSO_TYPE_USER;
 +              head = &machine->user_dsos;
 +              break;
 +      default:
 +              goto out;
 +      }
 +
 +      dso = __dsos__findnew(head, filename);
 +      if (dso != NULL) {
 +              char sbuild_id[BUILD_ID_SIZE * 2 + 1];
 +
 +              dso__set_build_id(dso, &bev->build_id);
 +
 +              if (filename[0] == '[')
 +                      dso->kernel = dso_type;
 +
 +              build_id__sprintf(dso->build_id, sizeof(dso->build_id),
 +                                sbuild_id);
 +              pr_debug("build id event received for %s: %s\n",
 +                       dso->long_name, sbuild_id);
 +      }
 +
 +      err = 0;
 +out:
 +      return err;
 +}
 +
 +static int perf_header__read_build_ids_abi_quirk(struct perf_header *header,
 +                                               int input, u64 offset, u64 size)
 +{
 +      struct perf_session *session = container_of(header, struct perf_session, header);
 +      struct {
 +              struct perf_event_header   header;
 +              u8                         build_id[ALIGN(BUILD_ID_SIZE, sizeof(u64))];
 +              char                       filename[0];
 +      } old_bev;
 +      struct build_id_event bev;
 +      char filename[PATH_MAX];
 +      u64 limit = offset + size;
 +
 +      while (offset < limit) {
 +              ssize_t len;
 +
 +              if (read(input, &old_bev, sizeof(old_bev)) != sizeof(old_bev))
 +                      return -1;
 +
 +              if (header->needs_swap)
 +                      perf_event_header__bswap(&old_bev.header);
 +
 +              len = old_bev.header.size - sizeof(old_bev);
 +              if (read(input, filename, len) != len)
 +                      return -1;
 +
 +              bev.header = old_bev.header;
 +
 +              /*
 +               * As the pid is the missing value, we need to fill
 +               * it properly. The header.misc value give us nice hint.
 +               */
 +              bev.pid = HOST_KERNEL_ID;
 +              if (bev.header.misc == PERF_RECORD_MISC_GUEST_USER ||
 +                  bev.header.misc == PERF_RECORD_MISC_GUEST_KERNEL)
 +                      bev.pid = DEFAULT_GUEST_KERNEL_ID;
 +
 +              memcpy(bev.build_id, old_bev.build_id, sizeof(bev.build_id));
 +              __event_process_build_id(&bev, filename, session);
 +
 +              offset += bev.header.size;
 +      }
 +
 +      return 0;
 +}
 +
 +static int perf_header__read_build_ids(struct perf_header *header,
 +                                     int input, u64 offset, u64 size)
 +{
 +      struct perf_session *session = container_of(header, struct perf_session, header);
 +      struct build_id_event bev;
 +      char filename[PATH_MAX];
 +      u64 limit = offset + size, orig_offset = offset;
 +      int err = -1;
 +
 +      while (offset < limit) {
 +              ssize_t len;
 +
 +              if (read(input, &bev, sizeof(bev)) != sizeof(bev))
 +                      goto out;
 +
 +              if (header->needs_swap)
 +                      perf_event_header__bswap(&bev.header);
 +
 +              len = bev.header.size - sizeof(bev);
 +              if (read(input, filename, len) != len)
 +                      goto out;
 +              /*
 +               * The a1645ce1 changeset:
 +               *
 +               * "perf: 'perf kvm' tool for monitoring guest performance from host"
 +               *
 +               * Added a field to struct build_id_event that broke the file
 +               * format.
 +               *
 +               * Since the kernel build-id is the first entry, process the
 +               * table using the old format if the well known
 +               * '[kernel.kallsyms]' string for the kernel build-id has the
 +               * first 4 characters chopped off (where the pid_t sits).
 +               */
 +              if (memcmp(filename, "nel.kallsyms]", 13) == 0) {
 +                      if (lseek(input, orig_offset, SEEK_SET) == (off_t)-1)
 +                              return -1;
 +                      return perf_header__read_build_ids_abi_quirk(header, input, offset, size);
 +              }
 +
 +              __event_process_build_id(&bev, filename, session);
 +
 +              offset += bev.header.size;
 +      }
 +      err = 0;
 +out:
 +      return err;
 +}
 +
 +static int process_trace_info(struct perf_file_section *section __unused,
 +                            struct perf_header *ph __unused,
 +                            int feat __unused, int fd)
 +{
 +      trace_report(fd, false);
 +      return 0;
 +}
 +
 +static int process_build_id(struct perf_file_section *section,
 +                          struct perf_header *ph,
 +                          int feat __unused, int fd)
 +{
 +      if (perf_header__read_build_ids(ph, fd, section->offset, section->size))
 +              pr_debug("Failed to read buildids, continuing...\n");
 +      return 0;
 +}
 +
  struct feature_ops {
        int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist);
        void (*print)(struct perf_header *h, int fd, FILE *fp);
 +      int (*process)(struct perf_file_section *section,
 +                     struct perf_header *h, int feat, int fd);
        const char *name;
        bool full_only;
  };
  
  #define FEAT_OPA(n, func) \
        [n] = { .name = #n, .write = write_##func, .print = print_##func }
 +#define FEAT_OPP(n, func) \
 +      [n] = { .name = #n, .write = write_##func, .print = print_##func, \
 +              .process = process_##func }
  #define FEAT_OPF(n, func) \
 -      [n] = { .name = #n, .write = write_##func, .print = print_##func, .full_only = true }
 +      [n] = { .name = #n, .write = write_##func, .print = print_##func, \
 +              .full_only = true }
  
  /* feature_ops not implemented: */
  #define print_trace_info              NULL
  #define print_build_id                        NULL
  
  static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = {
 -      FEAT_OPA(HEADER_TRACE_INFO,     trace_info),
 -      FEAT_OPA(HEADER_BUILD_ID,       build_id),
 +      FEAT_OPP(HEADER_TRACE_INFO,     trace_info),
 +      FEAT_OPP(HEADER_BUILD_ID,       build_id),
        FEAT_OPA(HEADER_HOSTNAME,       hostname),
        FEAT_OPA(HEADER_OSRELEASE,      osrelease),
        FEAT_OPA(HEADER_VERSION,        version),
        FEAT_OPA(HEADER_CMDLINE,        cmdline),
        FEAT_OPF(HEADER_CPU_TOPOLOGY,   cpu_topology),
        FEAT_OPF(HEADER_NUMA_TOPOLOGY,  numa_topology),
 +      FEAT_OPA(HEADER_BRANCH_STACK,   branch_stack),
  };
  
  struct header_print_data {
@@@ -1816,128 -1620,24 +1816,128 @@@ out_free
        return err;
  }
  
 +static const int attr_file_abi_sizes[] = {
 +      [0] = PERF_ATTR_SIZE_VER0,
 +      [1] = PERF_ATTR_SIZE_VER1,
 +      0,
 +};
 +
 +/*
 + * In the legacy file format, the magic number is not used to encode endianness.
 + * hdr_sz was used to encode endianness. But given that hdr_sz can vary based
 + * on ABI revisions, we need to try all combinations for all endianness to
 + * detect the endianness.
 + */
 +static int try_all_file_abis(uint64_t hdr_sz, struct perf_header *ph)
 +{
 +      uint64_t ref_size, attr_size;
 +      int i;
 +
 +      for (i = 0 ; attr_file_abi_sizes[i]; i++) {
 +              ref_size = attr_file_abi_sizes[i]
 +                       + sizeof(struct perf_file_section);
 +              if (hdr_sz != ref_size) {
 +                      attr_size = bswap_64(hdr_sz);
 +                      if (attr_size != ref_size)
 +                              continue;
 +
 +                      ph->needs_swap = true;
 +              }
 +              pr_debug("ABI%d perf.data file detected, need_swap=%d\n",
 +                       i,
 +                       ph->needs_swap);
 +              return 0;
 +      }
 +      /* could not determine endianness */
 +      return -1;
 +}
 +
 +#define PERF_PIPE_HDR_VER0    16
 +
 +static const size_t attr_pipe_abi_sizes[] = {
 +      [0] = PERF_PIPE_HDR_VER0,
 +      0,
 +};
 +
 +/*
 + * In the legacy pipe format, there is an implicit assumption that endiannesss
 + * between host recording the samples, and host parsing the samples is the
 + * same. This is not always the case given that the pipe output may always be
 + * redirected into a file and analyzed on a different machine with possibly a
 + * different endianness and perf_event ABI revsions in the perf tool itself.
 + */
 +static int try_all_pipe_abis(uint64_t hdr_sz, struct perf_header *ph)
 +{
 +      u64 attr_size;
 +      int i;
 +
 +      for (i = 0 ; attr_pipe_abi_sizes[i]; i++) {
 +              if (hdr_sz != attr_pipe_abi_sizes[i]) {
 +                      attr_size = bswap_64(hdr_sz);
 +                      if (attr_size != hdr_sz)
 +                              continue;
 +
 +                      ph->needs_swap = true;
 +              }
 +              pr_debug("Pipe ABI%d perf.data file detected\n", i);
 +              return 0;
 +      }
 +      return -1;
 +}
 +
 +static int check_magic_endian(u64 magic, uint64_t hdr_sz,
 +                            bool is_pipe, struct perf_header *ph)
 +{
 +      int ret;
 +
 +      /* check for legacy format */
 +      ret = memcmp(&magic, __perf_magic1, sizeof(magic));
 +      if (ret == 0) {
 +              pr_debug("legacy perf.data format\n");
 +              if (is_pipe)
 +                      return try_all_pipe_abis(hdr_sz, ph);
 +
 +              return try_all_file_abis(hdr_sz, ph);
 +      }
 +      /*
 +       * the new magic number serves two purposes:
 +       * - unique number to identify actual perf.data files
 +       * - encode endianness of file
 +       */
 +
 +      /* check magic number with one endianness */
 +      if (magic == __perf_magic2)
 +              return 0;
 +
 +      /* check magic number with opposite endianness */
 +      if (magic != __perf_magic2_sw)
 +              return -1;
 +
 +      ph->needs_swap = true;
 +
 +      return 0;
 +}
 +
  int perf_file_header__read(struct perf_file_header *header,
                           struct perf_header *ph, int fd)
  {
 +      int ret;
 +
        lseek(fd, 0, SEEK_SET);
  
 -      if (readn(fd, header, sizeof(*header)) <= 0 ||
 -          memcmp(&header->magic, __perf_magic, sizeof(header->magic)))
 +      ret = readn(fd, header, sizeof(*header));
 +      if (ret <= 0)
                return -1;
  
 -      if (header->attr_size != sizeof(struct perf_file_attr)) {
 -              u64 attr_size = bswap_64(header->attr_size);
 -
 -              if (attr_size != sizeof(struct perf_file_attr))
 -                      return -1;
 +      if (check_magic_endian(header->magic,
 +                             header->attr_size, false, ph) < 0) {
 +              pr_debug("magic/endian check failed\n");
 +              return -1;
 +      }
  
 +      if (ph->needs_swap) {
                mem_bswap_64(header, offsetof(struct perf_file_header,
 -                                          adds_features));
 -              ph->needs_swap = true;
 +                           adds_features));
        }
  
        if (header->size != sizeof(*header)) {
        return 0;
  }
  
 -static int __event_process_build_id(struct build_id_event *bev,
 -                                  char *filename,
 -                                  struct perf_session *session)
 -{
 -      int err = -1;
 -      struct list_head *head;
 -      struct machine *machine;
 -      u16 misc;
 -      struct dso *dso;
 -      enum dso_kernel_type dso_type;
 -
 -      machine = perf_session__findnew_machine(session, bev->pid);
 -      if (!machine)
 -              goto out;
 -
 -      misc = bev->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
 -
 -      switch (misc) {
 -      case PERF_RECORD_MISC_KERNEL:
 -              dso_type = DSO_TYPE_KERNEL;
 -              head = &machine->kernel_dsos;
 -              break;
 -      case PERF_RECORD_MISC_GUEST_KERNEL:
 -              dso_type = DSO_TYPE_GUEST_KERNEL;
 -              head = &machine->kernel_dsos;
 -              break;
 -      case PERF_RECORD_MISC_USER:
 -      case PERF_RECORD_MISC_GUEST_USER:
 -              dso_type = DSO_TYPE_USER;
 -              head = &machine->user_dsos;
 -              break;
 -      default:
 -              goto out;
 -      }
 -
 -      dso = __dsos__findnew(head, filename);
 -      if (dso != NULL) {
 -              char sbuild_id[BUILD_ID_SIZE * 2 + 1];
 -
 -              dso__set_build_id(dso, &bev->build_id);
 -
 -              if (filename[0] == '[')
 -                      dso->kernel = dso_type;
 -
 -              build_id__sprintf(dso->build_id, sizeof(dso->build_id),
 -                                sbuild_id);
 -              pr_debug("build id event received for %s: %s\n",
 -                       dso->long_name, sbuild_id);
 -      }
 -
 -      err = 0;
 -out:
 -      return err;
 -}
 -
 -static int perf_header__read_build_ids_abi_quirk(struct perf_header *header,
 -                                               int input, u64 offset, u64 size)
 -{
 -      struct perf_session *session = container_of(header, struct perf_session, header);
 -      struct {
 -              struct perf_event_header   header;
 -              u8                         build_id[ALIGN(BUILD_ID_SIZE, sizeof(u64))];
 -              char                       filename[0];
 -      } old_bev;
 -      struct build_id_event bev;
 -      char filename[PATH_MAX];
 -      u64 limit = offset + size;
 -
 -      while (offset < limit) {
 -              ssize_t len;
 -
 -              if (read(input, &old_bev, sizeof(old_bev)) != sizeof(old_bev))
 -                      return -1;
 -
 -              if (header->needs_swap)
 -                      perf_event_header__bswap(&old_bev.header);
 -
 -              len = old_bev.header.size - sizeof(old_bev);
 -              if (read(input, filename, len) != len)
 -                      return -1;
 -
 -              bev.header = old_bev.header;
 -
 -              /*
 -               * As the pid is the missing value, we need to fill
 -               * it properly. The header.misc value give us nice hint.
 -               */
 -              bev.pid = HOST_KERNEL_ID;
 -              if (bev.header.misc == PERF_RECORD_MISC_GUEST_USER ||
 -                  bev.header.misc == PERF_RECORD_MISC_GUEST_KERNEL)
 -                      bev.pid = DEFAULT_GUEST_KERNEL_ID;
 -
 -              memcpy(bev.build_id, old_bev.build_id, sizeof(bev.build_id));
 -              __event_process_build_id(&bev, filename, session);
 -
 -              offset += bev.header.size;
 -      }
 -
 -      return 0;
 -}
 -
 -static int perf_header__read_build_ids(struct perf_header *header,
 -                                     int input, u64 offset, u64 size)
 -{
 -      struct perf_session *session = container_of(header, struct perf_session, header);
 -      struct build_id_event bev;
 -      char filename[PATH_MAX];
 -      u64 limit = offset + size, orig_offset = offset;
 -      int err = -1;
 -
 -      while (offset < limit) {
 -              ssize_t len;
 -
 -              if (read(input, &bev, sizeof(bev)) != sizeof(bev))
 -                      goto out;
 -
 -              if (header->needs_swap)
 -                      perf_event_header__bswap(&bev.header);
 -
 -              len = bev.header.size - sizeof(bev);
 -              if (read(input, filename, len) != len)
 -                      goto out;
 -              /*
 -               * The a1645ce1 changeset:
 -               *
 -               * "perf: 'perf kvm' tool for monitoring guest performance from host"
 -               *
 -               * Added a field to struct build_id_event that broke the file
 -               * format.
 -               *
 -               * Since the kernel build-id is the first entry, process the
 -               * table using the old format if the well known
 -               * '[kernel.kallsyms]' string for the kernel build-id has the
 -               * first 4 characters chopped off (where the pid_t sits).
 -               */
 -              if (memcmp(filename, "nel.kallsyms]", 13) == 0) {
 -                      if (lseek(input, orig_offset, SEEK_SET) == (off_t)-1)
 -                              return -1;
 -                      return perf_header__read_build_ids_abi_quirk(header, input, offset, size);
 -              }
 -
 -              __event_process_build_id(&bev, filename, session);
 -
 -              offset += bev.header.size;
 -      }
 -      err = 0;
 -out:
 -      return err;
 -}
 -
  static int perf_file_section__process(struct perf_file_section *section,
                                      struct perf_header *ph,
                                      int feat, int fd, void *data __used)
                return 0;
        }
  
 -      switch (feat) {
 -      case HEADER_TRACE_INFO:
 -              trace_report(fd, false);
 -              break;
 -      case HEADER_BUILD_ID:
 -              if (perf_header__read_build_ids(ph, fd, section->offset, section->size))
 -                      pr_debug("Failed to read buildids, continuing...\n");
 -              break;
 -      default:
 -              break;
 -      }
 +      if (!feat_ops[feat].process)
 +              return 0;
  
 -      return 0;
 +      return feat_ops[feat].process(section, ph, feat, fd);
  }
  
  static int perf_file_header__read_pipe(struct perf_pipe_file_header *header,
                                       struct perf_header *ph, int fd,
                                       bool repipe)
  {
 -      if (readn(fd, header, sizeof(*header)) <= 0 ||
 -          memcmp(&header->magic, __perf_magic, sizeof(header->magic)))
 -              return -1;
 +      int ret;
  
 -      if (repipe && do_write(STDOUT_FILENO, header, sizeof(*header)) < 0)
 +      ret = readn(fd, header, sizeof(*header));
 +      if (ret <= 0)
                return -1;
  
 -      if (header->size != sizeof(*header)) {
 -              u64 size = bswap_64(header->size);
 +      if (check_magic_endian(header->magic, header->size, true, ph) < 0) {
 +              pr_debug("endian/magic failed\n");
 +              return -1;
 +      }
  
 -              if (size != sizeof(*header))
 -                      return -1;
 +      if (ph->needs_swap)
 +              header->size = bswap_64(header->size);
  
 -              ph->needs_swap = true;
 -      }
 +      if (repipe && do_write(STDOUT_FILENO, header, sizeof(*header)) < 0)
 +              return -1;
  
        return 0;
  }
@@@ -2050,52 -1908,6 +2050,52 @@@ static int perf_header__read_pipe(struc
        return 0;
  }
  
 +static int read_attr(int fd, struct perf_header *ph,
 +                   struct perf_file_attr *f_attr)
 +{
 +      struct perf_event_attr *attr = &f_attr->attr;
 +      size_t sz, left;
 +      size_t our_sz = sizeof(f_attr->attr);
 +      int ret;
 +
 +      memset(f_attr, 0, sizeof(*f_attr));
 +
 +      /* read minimal guaranteed structure */
 +      ret = readn(fd, attr, PERF_ATTR_SIZE_VER0);
 +      if (ret <= 0) {
 +              pr_debug("cannot read %d bytes of header attr\n",
 +                       PERF_ATTR_SIZE_VER0);
 +              return -1;
 +      }
 +
 +      /* on file perf_event_attr size */
 +      sz = attr->size;
 +
 +      if (ph->needs_swap)
 +              sz = bswap_32(sz);
 +
 +      if (sz == 0) {
 +              /* assume ABI0 */
 +              sz =  PERF_ATTR_SIZE_VER0;
 +      } else if (sz > our_sz) {
 +              pr_debug("file uses a more recent and unsupported ABI"
 +                       " (%zu bytes extra)\n", sz - our_sz);
 +              return -1;
 +      }
 +      /* what we have not yet read and that we know about */
 +      left = sz - PERF_ATTR_SIZE_VER0;
 +      if (left) {
 +              void *ptr = attr;
 +              ptr += PERF_ATTR_SIZE_VER0;
 +
 +              ret = readn(fd, ptr, left);
 +      }
 +      /* read perf_file_section, ids are read in caller */
 +      ret = readn(fd, &f_attr->ids, sizeof(f_attr->ids));
 +
 +      return ret <= 0 ? -1 : 0;
 +}
 +
  int perf_session__read_header(struct perf_session *session, int fd)
  {
        struct perf_header *header = &session->header;
        if (session->fd_pipe)
                return perf_header__read_pipe(session, fd);
  
 -      if (perf_file_header__read(&f_header, header, fd) < 0) {
 -              pr_debug("incompatible file format\n");
 +      if (perf_file_header__read(&f_header, header, fd) < 0)
                return -EINVAL;
 -      }
  
 -      nr_attrs = f_header.attrs.size / sizeof(f_attr);
 +      nr_attrs = f_header.attrs.size / f_header.attr_size;
        lseek(fd, f_header.attrs.offset, SEEK_SET);
  
        for (i = 0; i < nr_attrs; i++) {
                struct perf_evsel *evsel;
                off_t tmp;
  
 -              if (readn(fd, &f_attr, sizeof(f_attr)) <= 0)
 +              if (read_attr(fd, header, &f_attr) < 0)
                        goto out_errno;
  
                if (header->needs_swap)
diff --combined tools/perf/util/hist.c
@@@ -10,14 -10,11 +10,14 @@@ static bool hists__filter_entry_by_dso(
                                       struct hist_entry *he);
  static bool hists__filter_entry_by_thread(struct hists *hists,
                                          struct hist_entry *he);
 +static bool hists__filter_entry_by_symbol(struct hists *hists,
 +                                        struct hist_entry *he);
  
  enum hist_filter {
        HIST_FILTER__DSO,
        HIST_FILTER__THREAD,
        HIST_FILTER__PARENT,
 +      HIST_FILTER__SYMBOL,
  };
  
  struct callchain_param        callchain_param = {
@@@ -53,25 -50,21 +53,25 @@@ static void hists__reset_col_len(struc
                hists__set_col_len(hists, col, 0);
  }
  
 +static void hists__set_unres_dso_col_len(struct hists *hists, int dso)
 +{
 +      const unsigned int unresolved_col_width = BITS_PER_LONG / 4;
 +
 +      if (hists__col_len(hists, dso) < unresolved_col_width &&
 +          !symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
 +          !symbol_conf.dso_list)
 +              hists__set_col_len(hists, dso, unresolved_col_width);
 +}
 +
  static void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
  {
 +      const unsigned int unresolved_col_width = BITS_PER_LONG / 4;
        u16 len;
  
        if (h->ms.sym)
 -              hists__new_col_len(hists, HISTC_SYMBOL, h->ms.sym->namelen);
 -      else {
 -              const unsigned int unresolved_col_width = BITS_PER_LONG / 4;
 -
 -              if (hists__col_len(hists, HISTC_DSO) < unresolved_col_width &&
 -                  !symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
 -                  !symbol_conf.dso_list)
 -                      hists__set_col_len(hists, HISTC_DSO,
 -                                         unresolved_col_width);
 -      }
 +              hists__new_col_len(hists, HISTC_SYMBOL, h->ms.sym->namelen + 4);
 +      else
 +              hists__set_unres_dso_col_len(hists, HISTC_DSO);
  
        len = thread__comm_len(h->thread);
        if (hists__new_col_len(hists, HISTC_COMM, len))
                len = dso__name_len(h->ms.map->dso);
                hists__new_col_len(hists, HISTC_DSO, len);
        }
 +
 +      if (h->branch_info) {
 +              int symlen;
 +              /*
 +               * +4 accounts for '[x] ' priv level info
 +               * +2 account of 0x prefix on raw addresses
 +               */
 +              if (h->branch_info->from.sym) {
 +                      symlen = (int)h->branch_info->from.sym->namelen + 4;
 +                      hists__new_col_len(hists, HISTC_SYMBOL_FROM, symlen);
 +
 +                      symlen = dso__name_len(h->branch_info->from.map->dso);
 +                      hists__new_col_len(hists, HISTC_DSO_FROM, symlen);
 +              } else {
 +                      symlen = unresolved_col_width + 4 + 2;
 +                      hists__new_col_len(hists, HISTC_SYMBOL_FROM, symlen);
 +                      hists__set_unres_dso_col_len(hists, HISTC_DSO_FROM);
 +              }
 +
 +              if (h->branch_info->to.sym) {
 +                      symlen = (int)h->branch_info->to.sym->namelen + 4;
 +                      hists__new_col_len(hists, HISTC_SYMBOL_TO, symlen);
 +
 +                      symlen = dso__name_len(h->branch_info->to.map->dso);
 +                      hists__new_col_len(hists, HISTC_DSO_TO, symlen);
 +              } else {
 +                      symlen = unresolved_col_width + 4 + 2;
 +                      hists__new_col_len(hists, HISTC_SYMBOL_TO, symlen);
 +                      hists__set_unres_dso_col_len(hists, HISTC_DSO_TO);
 +              }
 +      }
  }
  
  static void hist_entry__add_cpumode_period(struct hist_entry *he,
@@@ -233,14 -195,26 +233,14 @@@ static u8 symbol__parent_filter(const s
        return 0;
  }
  
 -struct hist_entry *__hists__add_entry(struct hists *hists,
 +static struct hist_entry *add_hist_entry(struct hists *hists,
 +                                    struct hist_entry *entry,
                                      struct addr_location *al,
 -                                    struct symbol *sym_parent, u64 period)
 +                                    u64 period)
  {
        struct rb_node **p;
        struct rb_node *parent = NULL;
        struct hist_entry *he;
 -      struct hist_entry entry = {
 -              .thread = al->thread,
 -              .ms = {
 -                      .map    = al->map,
 -                      .sym    = al->sym,
 -              },
 -              .cpu    = al->cpu,
 -              .ip     = al->addr,
 -              .level  = al->level,
 -              .period = period,
 -              .parent = sym_parent,
 -              .filtered = symbol__parent_filter(sym_parent),
 -      };
        int cmp;
  
        pthread_mutex_lock(&hists->lock);
                parent = *p;
                he = rb_entry(parent, struct hist_entry, rb_node_in);
  
 -              cmp = hist_entry__cmp(&entry, he);
 +              cmp = hist_entry__cmp(entry, he);
  
                if (!cmp) {
                        he->period += period;
                        p = &(*p)->rb_right;
        }
  
 -      he = hist_entry__new(&entry);
 +      he = hist_entry__new(entry);
        if (!he)
                goto out_unlock;
  
@@@ -278,51 -252,6 +278,51 @@@ out_unlock
        return he;
  }
  
 +struct hist_entry *__hists__add_branch_entry(struct hists *self,
 +                                           struct addr_location *al,
 +                                           struct symbol *sym_parent,
 +                                           struct branch_info *bi,
 +                                           u64 period)
 +{
 +      struct hist_entry entry = {
 +              .thread = al->thread,
 +              .ms = {
 +                      .map    = bi->to.map,
 +                      .sym    = bi->to.sym,
 +              },
 +              .cpu    = al->cpu,
 +              .ip     = bi->to.addr,
 +              .level  = al->level,
 +              .period = period,
 +              .parent = sym_parent,
 +              .filtered = symbol__parent_filter(sym_parent),
 +              .branch_info = bi,
 +      };
 +
 +      return add_hist_entry(self, &entry, al, period);
 +}
 +
 +struct hist_entry *__hists__add_entry(struct hists *self,
 +                                    struct addr_location *al,
 +                                    struct symbol *sym_parent, u64 period)
 +{
 +      struct hist_entry entry = {
 +              .thread = al->thread,
 +              .ms = {
 +                      .map    = al->map,
 +                      .sym    = al->sym,
 +              },
 +              .cpu    = al->cpu,
 +              .ip     = al->addr,
 +              .level  = al->level,
 +              .period = period,
 +              .parent = sym_parent,
 +              .filtered = symbol__parent_filter(sym_parent),
 +      };
 +
 +      return add_hist_entry(self, &entry, al, period);
 +}
 +
  int64_t
  hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
  {
@@@ -423,7 -352,6 +423,7 @@@ static void hists__apply_filters(struc
  {
        hists__filter_entry_by_dso(hists, he);
        hists__filter_entry_by_thread(hists, he);
 +      hists__filter_entry_by_symbol(hists, he);
  }
  
  static void __hists__collapse_resort(struct hists *hists, bool threaded)
@@@ -840,7 -768,7 +840,7 @@@ static int hist_entry__pcnt_snprintf(st
                                                     sep ? "%.2f" : "   %6.2f%%",
                                                     (period * 100.0) / total);
                else
-                       ret = snprintf(s, size, sep ? "%.2f" : "   %6.2f%%",
+                       ret = scnprintf(s, size, sep ? "%.2f" : "   %6.2f%%",
                                       (period * 100.0) / total);
                if (symbol_conf.show_cpu_utilization) {
                        ret += percent_color_snprintf(s + ret, size - ret,
                        }
                }
        } else
-               ret = snprintf(s, size, sep ? "%" PRIu64 : "%12" PRIu64 " ", period);
+               ret = scnprintf(s, size, sep ? "%" PRIu64 : "%12" PRIu64 " ", period);
  
        if (symbol_conf.show_nr_samples) {
                if (sep)
-                       ret += snprintf(s + ret, size - ret, "%c%" PRIu64, *sep, nr_events);
+                       ret += scnprintf(s + ret, size - ret, "%c%" PRIu64, *sep, nr_events);
                else
-                       ret += snprintf(s + ret, size - ret, "%11" PRIu64, nr_events);
+                       ret += scnprintf(s + ret, size - ret, "%11" PRIu64, nr_events);
        }
  
        if (symbol_conf.show_total_period) {
                if (sep)
-                       ret += snprintf(s + ret, size - ret, "%c%" PRIu64, *sep, period);
+                       ret += scnprintf(s + ret, size - ret, "%c%" PRIu64, *sep, period);
                else
-                       ret += snprintf(s + ret, size - ret, " %12" PRIu64, period);
+                       ret += scnprintf(s + ret, size - ret, " %12" PRIu64, period);
        }
  
        if (pair_hists) {
                diff = new_percent - old_percent;
  
                if (fabs(diff) >= 0.01)
-                       snprintf(bf, sizeof(bf), "%+4.2F%%", diff);
+                       ret += scnprintf(bf, sizeof(bf), "%+4.2F%%", diff);
                else
-                       snprintf(bf, sizeof(bf), " ");
+                       ret += scnprintf(bf, sizeof(bf), " ");
  
                if (sep)
-                       ret += snprintf(s + ret, size - ret, "%c%s", *sep, bf);
+                       ret += scnprintf(s + ret, size - ret, "%c%s", *sep, bf);
                else
-                       ret += snprintf(s + ret, size - ret, "%11.11s", bf);
+                       ret += scnprintf(s + ret, size - ret, "%11.11s", bf);
  
                if (show_displacement) {
                        if (displacement)
-                               snprintf(bf, sizeof(bf), "%+4ld", displacement);
+                               ret += scnprintf(bf, sizeof(bf), "%+4ld", displacement);
                        else
-                               snprintf(bf, sizeof(bf), " ");
+                               ret += scnprintf(bf, sizeof(bf), " ");
  
                        if (sep)
-                               ret += snprintf(s + ret, size - ret, "%c%s", *sep, bf);
+                               ret += scnprintf(s + ret, size - ret, "%c%s", *sep, bf);
                        else
-                               ret += snprintf(s + ret, size - ret, "%6.6s", bf);
+                               ret += scnprintf(s + ret, size - ret, "%6.6s", bf);
                }
        }
  
@@@ -927,7 -855,7 +927,7 @@@ int hist_entry__snprintf(struct hist_en
                if (se->elide)
                        continue;
  
-               ret += snprintf(s + ret, size - ret, "%s", sep ?: "  ");
+               ret += scnprintf(s + ret, size - ret, "%s", sep ?: "  ");
                ret += se->se_snprintf(he, s + ret, size - ret,
                                       hists__col_len(hists, se->se_width_idx));
        }
@@@ -1251,37 -1179,6 +1251,37 @@@ void hists__filter_by_thread(struct his
        }
  }
  
 +static bool hists__filter_entry_by_symbol(struct hists *hists,
 +                                        struct hist_entry *he)
 +{
 +      if (hists->symbol_filter_str != NULL &&
 +          (!he->ms.sym || strstr(he->ms.sym->name,
 +                                 hists->symbol_filter_str) == NULL)) {
 +              he->filtered |= (1 << HIST_FILTER__SYMBOL);
 +              return true;
 +      }
 +
 +      return false;
 +}
 +
 +void hists__filter_by_symbol(struct hists *hists)
 +{
 +      struct rb_node *nd;
 +
 +      hists->nr_entries = hists->stats.total_period = 0;
 +      hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0;
 +      hists__reset_col_len(hists);
 +
 +      for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
 +              struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
 +
 +              if (hists__filter_entry_by_symbol(hists, h))
 +                      continue;
 +
 +              hists__remove_entry_filter(hists, h, HIST_FILTER__SYMBOL);
 +      }
 +}
 +
  int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 ip)
  {
        return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evidx, ip);
  #include "cache.h"
  #include "header.h"
  #include "debugfs.h"
 +#include "parse-events-flex.h"
 +#include "pmu.h"
 +
 +#define MAX_NAME_LEN 100
  
  struct event_symbol {
        u8              type;
        const char      *alias;
  };
  
 -enum event_result {
 -      EVT_FAILED,
 -      EVT_HANDLED,
 -      EVT_HANDLED_ALL
 -};
 +int parse_events_parse(struct list_head *list, int *idx);
  
  #define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x
  #define CSW(x) .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_##x
@@@ -165,7 -165,7 +165,7 @@@ struct tracepoint_path *tracepoint_id_t
        struct tracepoint_path *path = NULL;
        DIR *sys_dir, *evt_dir;
        struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent;
-       char id_buf[4];
+       char id_buf[24];
        int fd;
        u64 id;
        char evt_path[MAXPATHLEN];
@@@ -354,24 -354,7 +354,24 @@@ const char *__event_name(int type, u64 
        return "unknown";
  }
  
 -static int parse_aliases(const char **str, const char *names[][MAX_ALIASES], int size)
 +static int add_event(struct list_head *list, int *idx,
 +                   struct perf_event_attr *attr, char *name)
 +{
 +      struct perf_evsel *evsel;
 +
 +      event_attr_init(attr);
 +
 +      evsel = perf_evsel__new(attr, (*idx)++);
 +      if (!evsel)
 +              return -ENOMEM;
 +
 +      list_add_tail(&evsel->node, list);
 +
 +      evsel->name = strdup(name);
 +      return 0;
 +}
 +
 +static int parse_aliases(char *str, const char *names[][MAX_ALIASES], int size)
  {
        int i, j;
        int n, longest = -1;
        for (i = 0; i < size; i++) {
                for (j = 0; j < MAX_ALIASES && names[i][j]; j++) {
                        n = strlen(names[i][j]);
 -                      if (n > longest && !strncasecmp(*str, names[i][j], n))
 +                      if (n > longest && !strncasecmp(str, names[i][j], n))
                                longest = n;
                }
 -              if (longest > 0) {
 -                      *str += longest;
 +              if (longest > 0)
                        return i;
 -              }
        }
  
        return -1;
  }
  
 -static enum event_result
 -parse_generic_hw_event(const char **str, struct perf_event_attr *attr)
 +int parse_events_add_cache(struct list_head *list, int *idx,
 +                         char *type, char *op_result1, char *op_result2)
  {
 -      const char *s = *str;
 +      struct perf_event_attr attr;
 +      char name[MAX_NAME_LEN];
        int cache_type = -1, cache_op = -1, cache_result = -1;
 +      char *op_result[2] = { op_result1, op_result2 };
 +      int i, n;
  
 -      cache_type = parse_aliases(&s, hw_cache, PERF_COUNT_HW_CACHE_MAX);
        /*
         * No fallback - if we cannot get a clear cache type
         * then bail out:
         */
 +      cache_type = parse_aliases(type, hw_cache,
 +                                 PERF_COUNT_HW_CACHE_MAX);
        if (cache_type == -1)
 -              return EVT_FAILED;
 +              return -EINVAL;
  
 -      while ((cache_op == -1 || cache_result == -1) && *s == '-') {
 -              ++s;
 +      n = snprintf(name, MAX_NAME_LEN, "%s", type);
 +
 +      for (i = 0; (i < 2) && (op_result[i]); i++) {
 +              char *str = op_result[i];
 +
 +              snprintf(name + n, MAX_NAME_LEN - n, "-%s\n", str);
  
                if (cache_op == -1) {
 -                      cache_op = parse_aliases(&s, hw_cache_op,
 -                                              PERF_COUNT_HW_CACHE_OP_MAX);
 +                      cache_op = parse_aliases(str, hw_cache_op,
 +                                               PERF_COUNT_HW_CACHE_OP_MAX);
                        if (cache_op >= 0) {
                                if (!is_cache_op_valid(cache_type, cache_op))
 -                                      return EVT_FAILED;
 +                                      return -EINVAL;
                                continue;
                        }
                }
  
                if (cache_result == -1) {
 -                      cache_result = parse_aliases(&s, hw_cache_result,
 +                      cache_result = parse_aliases(str, hw_cache_result,
                                                PERF_COUNT_HW_CACHE_RESULT_MAX);
                        if (cache_result >= 0)
                                continue;
                }
 -
 -              /*
 -               * Can't parse this as a cache op or result, so back up
 -               * to the '-'.
 -               */
 -              --s;
 -              break;
        }
  
        /*
        if (cache_result == -1)
                cache_result = PERF_COUNT_HW_CACHE_RESULT_ACCESS;
  
 -      attr->config = cache_type | (cache_op << 8) | (cache_result << 16);
 -      attr->type = PERF_TYPE_HW_CACHE;
 -
 -      *str = s;
 -      return EVT_HANDLED;
 +      memset(&attr, 0, sizeof(attr));
 +      attr.config = cache_type | (cache_op << 8) | (cache_result << 16);
 +      attr.type = PERF_TYPE_HW_CACHE;
 +      return add_event(list, idx, &attr, name);
  }
  
 -static enum event_result
 -parse_single_tracepoint_event(char *sys_name,
 -                            const char *evt_name,
 -                            unsigned int evt_length,
 -                            struct perf_event_attr *attr,
 -                            const char **strp)
 +static int add_tracepoint(struct list_head *list, int *idx,
 +                        char *sys_name, char *evt_name)
  {
 +      struct perf_event_attr attr;
 +      char name[MAX_NAME_LEN];
        char evt_path[MAXPATHLEN];
        char id_buf[4];
        u64 id;
  
        fd = open(evt_path, O_RDONLY);
        if (fd < 0)
 -              return EVT_FAILED;
 +              return -1;
  
        if (read(fd, id_buf, sizeof(id_buf)) < 0) {
                close(fd);
 -              return EVT_FAILED;
 +              return -1;
        }
  
        close(fd);
        id = atoll(id_buf);
 -      attr->config = id;
 -      attr->type = PERF_TYPE_TRACEPOINT;
 -      *strp += strlen(sys_name) + evt_length + 1; /* + 1 for the ':' */
 -
 -      attr->sample_type |= PERF_SAMPLE_RAW;
 -      attr->sample_type |= PERF_SAMPLE_TIME;
 -      attr->sample_type |= PERF_SAMPLE_CPU;
  
 -      attr->sample_period = 1;
 +      memset(&attr, 0, sizeof(attr));
 +      attr.config = id;
 +      attr.type = PERF_TYPE_TRACEPOINT;
 +      attr.sample_type |= PERF_SAMPLE_RAW;
 +      attr.sample_type |= PERF_SAMPLE_TIME;
 +      attr.sample_type |= PERF_SAMPLE_CPU;
 +      attr.sample_period = 1;
  
 -
 -      return EVT_HANDLED;
 +      snprintf(name, MAX_NAME_LEN, "%s:%s", sys_name, evt_name);
 +      return add_event(list, idx, &attr, name);
  }
  
 -/* sys + ':' + event + ':' + flags*/
 -#define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128)
 -static enum event_result
 -parse_multiple_tracepoint_event(struct perf_evlist *evlist, char *sys_name,
 -                              const char *evt_exp, char *flags)
 +static int add_tracepoint_multi(struct list_head *list, int *idx,
 +                              char *sys_name, char *evt_name)
  {
        char evt_path[MAXPATHLEN];
        struct dirent *evt_ent;
        DIR *evt_dir;
 +      int ret = 0;
  
        snprintf(evt_path, MAXPATHLEN, "%s/%s", tracing_events_path, sys_name);
        evt_dir = opendir(evt_path);
 -
        if (!evt_dir) {
                perror("Can't open event dir");
 -              return EVT_FAILED;
 +              return -1;
        }
  
 -      while ((evt_ent = readdir(evt_dir))) {
 -              char event_opt[MAX_EVOPT_LEN + 1];
 -              int len;
 -
 +      while (!ret && (evt_ent = readdir(evt_dir))) {
                if (!strcmp(evt_ent->d_name, ".")
                    || !strcmp(evt_ent->d_name, "..")
                    || !strcmp(evt_ent->d_name, "enable")
                    || !strcmp(evt_ent->d_name, "filter"))
                        continue;
  
 -              if (!strglobmatch(evt_ent->d_name, evt_exp))
 +              if (!strglobmatch(evt_ent->d_name, evt_name))
                        continue;
  
 -              len = snprintf(event_opt, MAX_EVOPT_LEN, "%s:%s%s%s", sys_name,
 -                             evt_ent->d_name, flags ? ":" : "",
 -                             flags ?: "");
 -              if (len < 0)
 -                      return EVT_FAILED;
 -
 -              if (parse_events(evlist, event_opt, 0))
 -                      return EVT_FAILED;
 +              ret = add_tracepoint(list, idx, sys_name, evt_ent->d_name);
        }
  
 -      return EVT_HANDLED_ALL;
 +      return ret;
  }
  
 -static enum event_result
 -parse_tracepoint_event(struct perf_evlist *evlist, const char **strp,
 -                     struct perf_event_attr *attr)
 +int parse_events_add_tracepoint(struct list_head *list, int *idx,
 +                              char *sys, char *event)
  {
 -      const char *evt_name;
 -      char *flags = NULL, *comma_loc;
 -      char sys_name[MAX_EVENT_LENGTH];
 -      unsigned int sys_length, evt_length;
 +      int ret;
  
 -      if (debugfs_valid_mountpoint(tracing_events_path))
 -              return 0;
 -
 -      evt_name = strchr(*strp, ':');
 -      if (!evt_name)
 -              return EVT_FAILED;
 -
 -      sys_length = evt_name - *strp;
 -      if (sys_length >= MAX_EVENT_LENGTH)
 -              return 0;
 +      ret = debugfs_valid_mountpoint(tracing_events_path);
 +      if (ret)
 +              return ret;
  
 -      strncpy(sys_name, *strp, sys_length);
 -      sys_name[sys_length] = '\0';
 -      evt_name = evt_name + 1;
 -
 -      comma_loc = strchr(evt_name, ',');
 -      if (comma_loc) {
 -              /* take the event name up to the comma */
 -              evt_name = strndup(evt_name, comma_loc - evt_name);
 -      }
 -      flags = strchr(evt_name, ':');
 -      if (flags) {
 -              /* split it out: */
 -              evt_name = strndup(evt_name, flags - evt_name);
 -              flags++;
 -      }
 -
 -      evt_length = strlen(evt_name);
 -      if (evt_length >= MAX_EVENT_LENGTH)
 -              return EVT_FAILED;
 -      if (strpbrk(evt_name, "*?")) {
 -              *strp += strlen(sys_name) + evt_length + 1; /* 1 == the ':' */
 -              return parse_multiple_tracepoint_event(evlist, sys_name,
 -                                                     evt_name, flags);
 -      } else {
 -              return parse_single_tracepoint_event(sys_name, evt_name,
 -                                                   evt_length, attr, strp);
 -      }
 +      return strpbrk(event, "*?") ?
 +             add_tracepoint_multi(list, idx, sys, event) :
 +             add_tracepoint(list, idx, sys, event);
  }
  
 -static enum event_result
 -parse_breakpoint_type(const char *type, const char **strp,
 -                    struct perf_event_attr *attr)
 +static int
 +parse_breakpoint_type(const char *type, struct perf_event_attr *attr)
  {
        int i;
  
        for (i = 0; i < 3; i++) {
 -              if (!type[i])
 +              if (!type || !type[i])
                        break;
  
                switch (type[i]) {
                        attr->bp_type |= HW_BREAKPOINT_X;
                        break;
                default:
 -                      return EVT_FAILED;
 +                      return -EINVAL;
                }
        }
 +
        if (!attr->bp_type) /* Default */
                attr->bp_type = HW_BREAKPOINT_R | HW_BREAKPOINT_W;
  
 -      *strp = type + i;
 -
 -      return EVT_HANDLED;
 +      return 0;
  }
  
 -static enum event_result
 -parse_breakpoint_event(const char **strp, struct perf_event_attr *attr)
 +int parse_events_add_breakpoint(struct list_head *list, int *idx,
 +                              void *ptr, char *type)
  {
 -      const char *target;
 -      const char *type;
 -      char *endaddr;
 -      u64 addr;
 -      enum event_result err;
 -
 -      target = strchr(*strp, ':');
 -      if (!target)
 -              return EVT_FAILED;
 -
 -      if (strncmp(*strp, "mem", target - *strp) != 0)
 -              return EVT_FAILED;
 -
 -      target++;
 -
 -      addr = strtoull(target, &endaddr, 0);
 -      if (target == endaddr)
 -              return EVT_FAILED;
 -
 -      attr->bp_addr = addr;
 -      *strp = endaddr;
 +      struct perf_event_attr attr;
 +      char name[MAX_NAME_LEN];
  
 -      type = strchr(target, ':');
 +      memset(&attr, 0, sizeof(attr));
 +      attr.bp_addr = (u64) ptr;
  
 -      /* If no type is defined, just rw as default */
 -      if (!type) {
 -              attr->bp_type = HW_BREAKPOINT_R | HW_BREAKPOINT_W;
 -      } else {
 -              err = parse_breakpoint_type(++type, strp, attr);
 -              if (err == EVT_FAILED)
 -                      return EVT_FAILED;
 -      }
 +      if (parse_breakpoint_type(type, &attr))
 +              return -EINVAL;
  
        /*
         * We should find a nice way to override the access length
         * Provide some defaults for now
         */
 -      if (attr->bp_type == HW_BREAKPOINT_X)
 -              attr->bp_len = sizeof(long);
 +      if (attr.bp_type == HW_BREAKPOINT_X)
 +              attr.bp_len = sizeof(long);
        else
 -              attr->bp_len = HW_BREAKPOINT_LEN_4;
 +              attr.bp_len = HW_BREAKPOINT_LEN_4;
  
 -      attr->type = PERF_TYPE_BREAKPOINT;
 +      attr.type = PERF_TYPE_BREAKPOINT;
  
 -      return EVT_HANDLED;
 +      snprintf(name, MAX_NAME_LEN, "mem:%p:%s", ptr, type ? type : "rw");
 +      return add_event(list, idx, &attr, name);
  }
  
 -static int check_events(const char *str, unsigned int i)
 +static int config_term(struct perf_event_attr *attr,
 +                     struct parse_events__term *term)
  {
 -      int n;
 -
 -      n = strlen(event_symbols[i].symbol);
 -      if (!strncasecmp(str, event_symbols[i].symbol, n))
 -              return n;
 -
 -      n = strlen(event_symbols[i].alias);
 -      if (n) {
 -              if (!strncasecmp(str, event_symbols[i].alias, n))
 -                      return n;
 +      switch (term->type) {
 +      case PARSE_EVENTS__TERM_TYPE_CONFIG:
 +              attr->config = term->val.num;
 +              break;
 +      case PARSE_EVENTS__TERM_TYPE_CONFIG1:
 +              attr->config1 = term->val.num;
 +              break;
 +      case PARSE_EVENTS__TERM_TYPE_CONFIG2:
 +              attr->config2 = term->val.num;
 +              break;
 +      case PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD:
 +              attr->sample_period = term->val.num;
 +              break;
 +      case PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE:
 +              /*
 +               * TODO uncomment when the field is available
 +               * attr->branch_sample_type = term->val.num;
 +               */
 +              break;
 +      default:
 +              return -EINVAL;
        }
        return 0;
  }
  
 -static enum event_result
 -parse_symbolic_event(const char **strp, struct perf_event_attr *attr)
 +static int config_attr(struct perf_event_attr *attr,
 +                     struct list_head *head, int fail)
  {
 -      const char *str = *strp;
 -      unsigned int i;
 -      int n;
 -
 -      for (i = 0; i < ARRAY_SIZE(event_symbols); i++) {
 -              n = check_events(str, i);
 -              if (n > 0) {
 -                      attr->type = event_symbols[i].type;
 -                      attr->config = event_symbols[i].config;
 -                      *strp = str + n;
 -                      return EVT_HANDLED;
 -              }
 -      }
 -      return EVT_FAILED;
 +      struct parse_events__term *term;
 +
 +      list_for_each_entry(term, head, list)
 +              if (config_term(attr, term) && fail)
 +                      return -EINVAL;
 +
 +      return 0;
  }
  
 -static enum event_result
 -parse_raw_event(const char **strp, struct perf_event_attr *attr)
 +int parse_events_add_numeric(struct list_head *list, int *idx,
 +                           unsigned long type, unsigned long config,
 +                           struct list_head *head_config)
  {
 -      const char *str = *strp;
 -      u64 config;
 -      int n;
 -
 -      if (*str != 'r')
 -              return EVT_FAILED;
 -      n = hex2u64(str + 1, &config);
 -      if (n > 0) {
 -              const char *end = str + n + 1;
 -              if (*end != '\0' && *end != ',' && *end != ':')
 -                      return EVT_FAILED;
 -
 -              *strp = end;
 -              attr->type = PERF_TYPE_RAW;
 -              attr->config = config;
 -              return EVT_HANDLED;
 -      }
 -      return EVT_FAILED;
 +      struct perf_event_attr attr;
 +
 +      memset(&attr, 0, sizeof(attr));
 +      attr.type = type;
 +      attr.config = config;
 +
 +      if (head_config &&
 +          config_attr(&attr, head_config, 1))
 +              return -EINVAL;
 +
 +      return add_event(list, idx, &attr,
 +                       (char *) __event_name(type, config));
  }
  
 -static enum event_result
 -parse_numeric_event(const char **strp, struct perf_event_attr *attr)
 +int parse_events_add_pmu(struct list_head *list, int *idx,
 +                       char *name, struct list_head *head_config)
  {
 -      const char *str = *strp;
 -      char *endp;
 -      unsigned long type;
 -      u64 config;
 -
 -      type = strtoul(str, &endp, 0);
 -      if (endp > str && type < PERF_TYPE_MAX && *endp == ':') {
 -              str = endp + 1;
 -              config = strtoul(str, &endp, 0);
 -              if (endp > str) {
 -                      attr->type = type;
 -                      attr->config = config;
 -                      *strp = endp;
 -                      return EVT_HANDLED;
 -              }
 -      }
 -      return EVT_FAILED;
 +      struct perf_event_attr attr;
 +      struct perf_pmu *pmu;
 +
 +      pmu = perf_pmu__find(name);
 +      if (!pmu)
 +              return -EINVAL;
 +
 +      memset(&attr, 0, sizeof(attr));
 +
 +      /*
 +       * Configure hardcoded terms first, no need to check
 +       * return value when called with fail == 0 ;)
 +       */
 +      config_attr(&attr, head_config, 0);
 +
 +      if (perf_pmu__config(pmu, &attr, head_config))
 +              return -EINVAL;
 +
 +      return add_event(list, idx, &attr, (char *) "pmu");
  }
  
 -static int
 -parse_event_modifier(const char **strp, struct perf_event_attr *attr)
 +int parse_events_modifier(struct list_head *list, char *str)
  {
 -      const char *str = *strp;
 +      struct perf_evsel *evsel;
        int exclude = 0, exclude_GH = 0;
        int eu = 0, ek = 0, eh = 0, eH = 0, eG = 0, precise = 0;
  
 -      if (!*str)
 +      if (str == NULL)
                return 0;
  
 -      if (*str == ',')
 -              return 0;
 -
 -      if (*str++ != ':')
 -              return -1;
 -
        while (*str) {
                if (*str == 'u') {
                        if (!exclude)
  
                ++str;
        }
 -      if (str < *strp + 2)
 -              return -1;
  
 -      *strp = str;
 +      /*
 +       * precise ip:
 +       *
 +       *  0 - SAMPLE_IP can have arbitrary skid
 +       *  1 - SAMPLE_IP must have constant skid
 +       *  2 - SAMPLE_IP requested to have 0 skid
 +       *  3 - SAMPLE_IP must have 0 skid
 +       *
 +       *  See also PERF_RECORD_MISC_EXACT_IP
 +       */
 +      if (precise > 3)
 +              return -EINVAL;
  
 -      attr->exclude_user   = eu;
 -      attr->exclude_kernel = ek;
 -      attr->exclude_hv     = eh;
 -      attr->precise_ip     = precise;
 -      attr->exclude_host   = eH;
 -      attr->exclude_guest  = eG;
 +      list_for_each_entry(evsel, list, node) {
 +              evsel->attr.exclude_user   = eu;
 +              evsel->attr.exclude_kernel = ek;
 +              evsel->attr.exclude_hv     = eh;
 +              evsel->attr.precise_ip     = precise;
 +              evsel->attr.exclude_host   = eH;
 +              evsel->attr.exclude_guest  = eG;
 +      }
  
        return 0;
  }
  
 -/*
 - * Each event can have multiple symbolic names.
 - * Symbolic names are (almost) exactly matched.
 - */
 -static enum event_result
 -parse_event_symbols(struct perf_evlist *evlist, const char **str,
 -                  struct perf_event_attr *attr)
 +int parse_events(struct perf_evlist *evlist, const char *str, int unset __used)
  {
 -      enum event_result ret;
 +      struct perf_evsel *evsel, *h;
 +      LIST_HEAD(list);
 +      YY_BUFFER_STATE buffer;
 +      int ret, idx = evlist->nr_entries;
  
 -      ret = parse_tracepoint_event(evlist, str, attr);
 -      if (ret != EVT_FAILED)
 -              goto modifier;
 +      buffer = parse_events__scan_string(str);
  
 -      ret = parse_raw_event(str, attr);
 -      if (ret != EVT_FAILED)
 -              goto modifier;
 +      ret = parse_events_parse(&list, &idx);
  
 -      ret = parse_numeric_event(str, attr);
 -      if (ret != EVT_FAILED)
 -              goto modifier;
 +      parse_events__flush_buffer(buffer);
 +      parse_events__delete_buffer(buffer);
  
 -      ret = parse_symbolic_event(str, attr);
 -      if (ret != EVT_FAILED)
 -              goto modifier;
 -
 -      ret = parse_generic_hw_event(str, attr);
 -      if (ret != EVT_FAILED)
 -              goto modifier;
 +      if (!ret) {
 +              int entries = idx - evlist->nr_entries;
 +              perf_evlist__splice_list_tail(evlist, &list, entries);
 +              return 0;
 +      }
  
 -      ret = parse_breakpoint_event(str, attr);
 -      if (ret != EVT_FAILED)
 -              goto modifier;
 +      list_for_each_entry_safe(evsel, h, &list, node)
 +              perf_evsel__delete(evsel);
  
 -      fprintf(stderr, "invalid or unsupported event: '%s'\n", *str);
 +      fprintf(stderr, "invalid or unsupported event: '%s'\n", str);
        fprintf(stderr, "Run 'perf list' for a list of valid events\n");
        return ret;
  }
  
 -int parse_events(struct perf_evlist *evlist , const char *str, int unset __used)
 -{
 -      struct perf_event_attr attr;
 -      enum event_result ret;
 -      const char *ostr;
 -
 -      for (;;) {
 -              ostr = str;
 -              memset(&attr, 0, sizeof(attr));
 -              event_attr_init(&attr);
 -              ret = parse_event_symbols(evlist, &str, &attr);
 -              if (ret == EVT_FAILED)
 -                      return -1;
 -
 -              if (!(*str == 0 || *str == ',' || isspace(*str)))
 -                      return -1;
 -
 -              if (ret != EVT_HANDLED_ALL) {
 -                      struct perf_evsel *evsel;
 -                      evsel = perf_evsel__new(&attr, evlist->nr_entries);
 -                      if (evsel == NULL)
 -                              return -1;
 -                      perf_evlist__add(evlist, evsel);
 -
 -                      evsel->name = calloc(str - ostr + 1, 1);
 -                      if (!evsel->name)
 -                              return -1;
 -                      strncpy(evsel->name, ostr, str - ostr);
 -              }
 -
 -              if (*str == 0)
 -                      break;
 -              if (*str == ',')
 -                      ++str;
 -              while (isspace(*str))
 -                      ++str;
 -      }
 -
 -      return 0;
 -}
 -
  int parse_events_option(const struct option *opt, const char *str,
                        int unset __used)
  {
@@@ -934,6 -1052,8 +934,6 @@@ int print_hwcache_events(const char *ev
        return printed;
  }
  
 -#define MAX_NAME_LEN 100
 -
  /*
   * Print the help text for the event symbols:
   */
@@@ -982,12 -1102,8 +982,12 @@@ void print_events(const char *event_glo
  
        printf("\n");
        printf("  %-50s [%s]\n",
 -              "rNNN (see 'perf list --help' on how to encode it)",
 +             "rNNN",
 +             event_type_descriptors[PERF_TYPE_RAW]);
 +      printf("  %-50s [%s]\n",
 +             "cpu/t1=v1[,t2=v2,t3 ...]/modifier",
               event_type_descriptors[PERF_TYPE_RAW]);
 +      printf("   (see 'perf list --help' on how to encode it)\n");
        printf("\n");
  
        printf("  %-50s [%s]\n",
  
        print_tracepoint_events(NULL, NULL);
  }
 +
 +int parse_events__is_hardcoded_term(struct parse_events__term *term)
 +{
 +      return term->type <= PARSE_EVENTS__TERM_TYPE_HARDCODED_MAX;
 +}
 +
 +int parse_events__new_term(struct parse_events__term **_term, int type,
 +                         char *config, char *str, long num)
 +{
 +      struct parse_events__term *term;
 +
 +      term = zalloc(sizeof(*term));
 +      if (!term)
 +              return -ENOMEM;
 +
 +      INIT_LIST_HEAD(&term->list);
 +      term->type = type;
 +      term->config = config;
 +
 +      switch (type) {
 +      case PARSE_EVENTS__TERM_TYPE_CONFIG:
 +      case PARSE_EVENTS__TERM_TYPE_CONFIG1:
 +      case PARSE_EVENTS__TERM_TYPE_CONFIG2:
 +      case PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD:
 +      case PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE:
 +      case PARSE_EVENTS__TERM_TYPE_NUM:
 +              term->val.num = num;
 +              break;
 +      case PARSE_EVENTS__TERM_TYPE_STR:
 +              term->val.str = str;
 +              break;
 +      default:
 +              return -EINVAL;
 +      }
 +
 +      *_term = term;
 +      return 0;
 +}
 +
 +void parse_events__free_terms(struct list_head *terms)
 +{
 +      struct parse_events__term *term, *h;
 +
 +      list_for_each_entry_safe(term, h, terms, list)
 +              free(term);
 +
 +      free(terms);
 +}
diff --combined tools/perf/util/sort.c
@@@ -8,7 -8,6 +8,7 @@@ const char       default_sort_order[] = "comm
  const char    *sort_order = default_sort_order;
  int           sort__need_collapse = 0;
  int           sort__has_parent = 0;
 +int           sort__branch_mode = -1; /* -1 = means not set */
  
  enum sort_type        sort__first_dimension;
  
@@@ -34,6 -33,9 +34,9 @@@ static int repsep_snprintf(char *bf, si
                }
        }
        va_end(ap);
+       if (n >= (int)size)
+               return size - 1;
        return n;
  }
  
@@@ -95,26 -97,6 +98,26 @@@ static int hist_entry__comm_snprintf(st
        return repsep_snprintf(bf, size, "%*s", width, self->thread->comm);
  }
  
 +static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r)
 +{
 +      struct dso *dso_l = map_l ? map_l->dso : NULL;
 +      struct dso *dso_r = map_r ? map_r->dso : NULL;
 +      const char *dso_name_l, *dso_name_r;
 +
 +      if (!dso_l || !dso_r)
 +              return cmp_null(dso_l, dso_r);
 +
 +      if (verbose) {
 +              dso_name_l = dso_l->long_name;
 +              dso_name_r = dso_r->long_name;
 +      } else {
 +              dso_name_l = dso_l->short_name;
 +              dso_name_r = dso_r->short_name;
 +      }
 +
 +      return strcmp(dso_name_l, dso_name_r);
 +}
 +
  struct sort_entry sort_comm = {
        .se_header      = "Command",
        .se_cmp         = sort__comm_cmp,
  static int64_t
  sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
  {
 -      struct dso *dso_l = left->ms.map ? left->ms.map->dso : NULL;
 -      struct dso *dso_r = right->ms.map ? right->ms.map->dso : NULL;
 -      const char *dso_name_l, *dso_name_r;
 +      return _sort__dso_cmp(left->ms.map, right->ms.map);
 +}
  
 -      if (!dso_l || !dso_r)
 -              return cmp_null(dso_l, dso_r);
  
 -      if (verbose) {
 -              dso_name_l = dso_l->long_name;
 -              dso_name_r = dso_r->long_name;
 -      } else {
 -              dso_name_l = dso_l->short_name;
 -              dso_name_r = dso_r->short_name;
 +static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r,
 +                            u64 ip_l, u64 ip_r)
 +{
 +      if (!sym_l || !sym_r)
 +              return cmp_null(sym_l, sym_r);
 +
 +      if (sym_l == sym_r)
 +              return 0;
 +
 +      if (sym_l)
 +              ip_l = sym_l->start;
 +      if (sym_r)
 +              ip_r = sym_r->start;
 +
 +      return (int64_t)(ip_r - ip_l);
 +}
 +
 +static int _hist_entry__dso_snprintf(struct map *map, char *bf,
 +                                   size_t size, unsigned int width)
 +{
 +      if (map && map->dso) {
 +              const char *dso_name = !verbose ? map->dso->short_name :
 +                      map->dso->long_name;
 +              return repsep_snprintf(bf, size, "%-*s", width, dso_name);
        }
  
 -      return strcmp(dso_name_l, dso_name_r);
 +      return repsep_snprintf(bf, size, "%-*s", width, "[unknown]");
  }
  
  static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf,
                                    size_t size, unsigned int width)
  {
 -      if (self->ms.map && self->ms.map->dso) {
 -              const char *dso_name = !verbose ? self->ms.map->dso->short_name :
 -                                                self->ms.map->dso->long_name;
 -              return repsep_snprintf(bf, size, "%-*s", width, dso_name);
 +      return _hist_entry__dso_snprintf(self->ms.map, bf, size, width);
 +}
 +
 +static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym,
 +                                   u64 ip, char level, char *bf, size_t size,
 +                                   unsigned int width __used)
 +{
 +      size_t ret = 0;
 +
 +      if (verbose) {
 +              char o = map ? dso__symtab_origin(map->dso) : '!';
 +              ret += repsep_snprintf(bf, size, "%-#*llx %c ",
 +                                     BITS_PER_LONG / 4, ip, o);
        }
  
 -      return repsep_snprintf(bf, size, "%-*s", width, "[unknown]");
 +      ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level);
 +      if (sym)
 +              ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
 +                                     width - ret,
 +                                     sym->name);
 +      else {
 +              size_t len = BITS_PER_LONG / 4;
 +              ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx",
 +                                     len, ip);
 +              ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
 +                                     width - ret, "");
 +      }
 +
 +      return ret;
  }
  
 +
  struct sort_entry sort_dso = {
        .se_header      = "Shared Object",
        .se_cmp         = sort__dso_cmp,
        .se_width_idx   = HISTC_DSO,
  };
  
 -/* --sort symbol */
 +static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
 +                                  size_t size, unsigned int width __used)
 +{
 +      return _hist_entry__sym_snprintf(self->ms.map, self->ms.sym, self->ip,
 +                                       self->level, bf, size, width);
 +}
  
 +/* --sort symbol */
  static int64_t
  sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
  {
        ip_l = left->ms.sym->start;
        ip_r = right->ms.sym->start;
  
 -      return (int64_t)(ip_r - ip_l);
 -}
 -
 -static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
 -                                  size_t size, unsigned int width __used)
 -{
 -      size_t ret = 0;
 -
 -      if (verbose) {
 -              char o = self->ms.map ? dso__symtab_origin(self->ms.map->dso) : '!';
 -              ret += repsep_snprintf(bf, size, "%-#*llx %c ",
 -                                     BITS_PER_LONG / 4, self->ip, o);
 -      }
 -
 -      if (!sort_dso.elide)
 -              ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", self->level);
 -
 -      if (self->ms.sym)
 -              ret += repsep_snprintf(bf + ret, size - ret, "%s",
 -                                     self->ms.sym->name);
 -      else
 -              ret += repsep_snprintf(bf + ret, size - ret, "%-#*llx",
 -                                     BITS_PER_LONG / 4, self->ip);
 -
 -      return ret;
 +      return _sort__sym_cmp(left->ms.sym, right->ms.sym, ip_l, ip_r);
  }
  
  struct sort_entry sort_sym = {
@@@ -287,155 -249,19 +290,155 @@@ struct sort_entry sort_cpu = 
        .se_width_idx   = HISTC_CPU,
  };
  
 +static int64_t
 +sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right)
 +{
 +      return _sort__dso_cmp(left->branch_info->from.map,
 +                            right->branch_info->from.map);
 +}
 +
 +static int hist_entry__dso_from_snprintf(struct hist_entry *self, char *bf,
 +                                  size_t size, unsigned int width)
 +{
 +      return _hist_entry__dso_snprintf(self->branch_info->from.map,
 +                                       bf, size, width);
 +}
 +
 +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_width_idx   = HISTC_DSO_FROM,
 +};
 +
 +static int64_t
 +sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right)
 +{
 +      return _sort__dso_cmp(left->branch_info->to.map,
 +                            right->branch_info->to.map);
 +}
 +
 +static int hist_entry__dso_to_snprintf(struct hist_entry *self, char *bf,
 +                                     size_t size, unsigned int width)
 +{
 +      return _hist_entry__dso_snprintf(self->branch_info->to.map,
 +                                       bf, size, width);
 +}
 +
 +static int64_t
 +sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right)
 +{
 +      struct addr_map_symbol *from_l = &left->branch_info->from;
 +      struct addr_map_symbol *from_r = &right->branch_info->from;
 +
 +      if (!from_l->sym && !from_r->sym)
 +              return right->level - left->level;
 +
 +      return _sort__sym_cmp(from_l->sym, from_r->sym, from_l->addr,
 +                           from_r->addr);
 +}
 +
 +static int64_t
 +sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right)
 +{
 +      struct addr_map_symbol *to_l = &left->branch_info->to;
 +      struct addr_map_symbol *to_r = &right->branch_info->to;
 +
 +      if (!to_l->sym && !to_r->sym)
 +              return right->level - left->level;
 +
 +      return _sort__sym_cmp(to_l->sym, to_r->sym, to_l->addr, to_r->addr);
 +}
 +
 +static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf,
 +                                  size_t size, unsigned int width __used)
 +{
 +      struct addr_map_symbol *from = &self->branch_info->from;
 +      return _hist_entry__sym_snprintf(from->map, from->sym, from->addr,
 +                                       self->level, bf, size, width);
 +
 +}
 +
 +static int hist_entry__sym_to_snprintf(struct hist_entry *self, char *bf,
 +                                  size_t size, unsigned int width __used)
 +{
 +      struct addr_map_symbol *to = &self->branch_info->to;
 +      return _hist_entry__sym_snprintf(to->map, to->sym, to->addr,
 +                                       self->level, bf, size, width);
 +
 +}
 +
 +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_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_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_width_idx   = HISTC_SYMBOL_TO,
 +};
 +
 +static int64_t
 +sort__mispredict_cmp(struct hist_entry *left, struct hist_entry *right)
 +{
 +      const unsigned char mp = left->branch_info->flags.mispred !=
 +                                      right->branch_info->flags.mispred;
 +      const unsigned char p = left->branch_info->flags.predicted !=
 +                                      right->branch_info->flags.predicted;
 +
 +      return mp || p;
 +}
 +
 +static int hist_entry__mispredict_snprintf(struct hist_entry *self, char *bf,
 +                                  size_t size, unsigned int width){
 +      static const char *out = "N/A";
 +
 +      if (self->branch_info->flags.predicted)
 +              out = "N";
 +      else if (self->branch_info->flags.mispred)
 +              out = "Y";
 +
 +      return repsep_snprintf(bf, size, "%-*s", width, out);
 +}
 +
 +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,
 +};
 +
  struct sort_dimension {
        const char              *name;
        struct sort_entry       *entry;
        int                     taken;
  };
  
 +#define DIM(d, n, func) [d] = { .name = n, .entry = &(func) }
 +
  static struct sort_dimension sort_dimensions[] = {
 -      { .name = "pid",        .entry = &sort_thread,  },
 -      { .name = "comm",       .entry = &sort_comm,    },
 -      { .name = "dso",        .entry = &sort_dso,     },
 -      { .name = "symbol",     .entry = &sort_sym,     },
 -      { .name = "parent",     .entry = &sort_parent,  },
 -      { .name = "cpu",        .entry = &sort_cpu,     },
 +      DIM(SORT_PID, "pid", sort_thread),
 +      DIM(SORT_COMM, "comm", sort_comm),
 +      DIM(SORT_DSO, "dso", sort_dso),
 +      DIM(SORT_DSO_FROM, "dso_from", sort_dso_from),
 +      DIM(SORT_DSO_TO, "dso_to", sort_dso_to),
 +      DIM(SORT_SYM, "symbol", sort_sym),
 +      DIM(SORT_SYM_TO, "symbol_from", sort_sym_from),
 +      DIM(SORT_SYM_FROM, "symbol_to", sort_sym_to),
 +      DIM(SORT_PARENT, "parent", sort_parent),
 +      DIM(SORT_CPU, "cpu", sort_cpu),
 +      DIM(SORT_MISPREDICT, "mispredict", sort_mispredict),
  };
  
  int sort_dimension__add(const char *tok)
  
                if (strncasecmp(tok, sd->name, strlen(tok)))
                        continue;
 -
                if (sd->entry == &sort_parent) {
                        int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED);
                        if (ret) {
                                sort__first_dimension = SORT_PARENT;
                        else if (!strcmp(sd->name, "cpu"))
                                sort__first_dimension = SORT_CPU;
 +                      else if (!strcmp(sd->name, "symbol_from"))
 +                              sort__first_dimension = SORT_SYM_FROM;
 +                      else if (!strcmp(sd->name, "symbol_to"))
 +                              sort__first_dimension = SORT_SYM_TO;
 +                      else if (!strcmp(sd->name, "dso_from"))
 +                              sort__first_dimension = SORT_DSO_FROM;
 +                      else if (!strcmp(sd->name, "dso_to"))
 +                              sort__first_dimension = SORT_DSO_TO;
 +                      else if (!strcmp(sd->name, "mispredict"))
 +                              sort__first_dimension = SORT_MISPREDICT;
                }
  
                list_add_tail(&sd->entry->list, &hist_entry__sort_list);
  
                return 0;
        }
 -
        return -ESRCH;
  }
  
@@@ -805,11 -805,8 +805,11 @@@ static struct hist_browser *hist_browse
                self->hists = hists;
                self->b.refresh = hist_browser__refresh;
                self->b.seek = ui_browser__hists_seek;
 -              self->b.use_navkeypressed = true,
 -              self->has_symbols = sort_sym.list.next != NULL;
 +              self->b.use_navkeypressed = true;
 +              if (sort__branch_mode == 1)
 +                      self->has_symbols = sort_sym_from.list.next != NULL;
 +              else
 +                      self->has_symbols = sort_sym.list.next != NULL;
        }
  
        return self;
@@@ -840,32 -837,19 +840,32 @@@ static int hists__browser_title(struct 
        unsigned long nr_events = self->stats.nr_events[PERF_RECORD_SAMPLE];
  
        nr_events = convert_unit(nr_events, &unit);
-       printed = snprintf(bf, size, "Events: %lu%c %s", nr_events, unit, ev_name);
+       printed = scnprintf(bf, size, "Events: %lu%c %s", nr_events, unit, ev_name);
  
 +      if (self->uid_filter_str)
 +              printed += snprintf(bf + printed, size - printed,
 +                                  ", UID: %s", self->uid_filter_str);
        if (thread)
-               printed += snprintf(bf + printed, size - printed,
+               printed += scnprintf(bf + printed, size - printed,
                                    ", Thread: %s(%d)",
                                    (thread->comm_set ? thread->comm : ""),
                                    thread->pid);
        if (dso)
-               printed += snprintf(bf + printed, size - printed,
+               printed += scnprintf(bf + printed, size - printed,
                                    ", DSO: %s", dso->short_name);
        return printed;
  }
  
 +static inline void free_popup_options(char **options, int n)
 +{
 +      int i;
 +
 +      for (i = 0; i < n; ++i) {
 +              free(options[i]);
 +              options[i] = NULL;
 +      }
 +}
 +
  static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
                                    const char *helpline, const char *ev_name,
                                    bool left_exits,
  {
        struct hists *self = &evsel->hists;
        struct hist_browser *browser = hist_browser__new(self);
 +      struct branch_info *bi;
        struct pstack *fstack;
 +      char *options[16];
 +      int nr_options = 0;
        int key = -1;
 +      char buf[64];
  
        if (browser == NULL)
                return -1;
  
        ui_helpline__push(helpline);
  
 +      memset(options, 0, sizeof(options));
 +
        while (1) {
                const struct thread *thread = NULL;
                const struct dso *dso = NULL;
 -              char *options[16];
 -              int nr_options = 0, choice = 0, i,
 +              int choice = 0,
                    annotate = -2, zoom_dso = -2, zoom_thread = -2,
 -                  browse_map = -2;
 +                  annotate_f = -2, annotate_t = -2, browse_map = -2;
 +
 +              nr_options = 0;
  
                key = hist_browser__run(browser, ev_name, timer, arg, delay_secs);
  
                        thread = hist_browser__selected_thread(browser);
                        dso = browser->selection->map ? browser->selection->map->dso : NULL;
                }
 -
                switch (key) {
                case K_TAB:
                case K_UNTAB:
                        if (!browser->has_symbols) {
                                ui_browser__warning(&browser->b, delay_secs * 2,
                        "Annotation is only available for symbolic views, "
 -                      "include \"sym\" in --sort to use it.");
 +                      "include \"sym*\" in --sort to use it.");
                                continue;
                        }
  
                        goto zoom_dso;
                case 't':
                        goto zoom_thread;
 +              case 's':
 +                      if (ui_browser__input_window("Symbol to show",
 +                                      "Please enter the name of symbol you want to see",
 +                                      buf, "ENTER: OK, ESC: Cancel",
 +                                      delay_secs * 2) == K_ENTER) {
 +                              self->symbol_filter_str = *buf ? buf : NULL;
 +                              hists__filter_by_symbol(self);
 +                              hist_browser__reset(browser);
 +                      }
 +                      continue;
                case K_F1:
                case 'h':
                case '?':
                                        "C             Collapse all callchains\n"
                                        "E             Expand all callchains\n"
                                        "d             Zoom into current DSO\n"
 -                                      "t             Zoom into current Thread");
 +                                      "t             Zoom into current Thread\n"
 +                                      "s             Filter symbol by name");
                        continue;
                case K_ENTER:
                case K_RIGHT:
                if (!browser->has_symbols)
                        goto add_exit_option;
  
 -              if (browser->selection != NULL &&
 -                  browser->selection->sym != NULL &&
 -                  !browser->selection->map->dso->annotate_warned &&
 -                  asprintf(&options[nr_options], "Annotate %s",
 -                           browser->selection->sym->name) > 0)
 -                      annotate = nr_options++;
 +              if (sort__branch_mode == 1) {
 +                      bi = browser->he_selection->branch_info;
 +                      if (browser->selection != NULL &&
 +                          bi &&
 +                          bi->from.sym != NULL &&
 +                          !bi->from.map->dso->annotate_warned &&
 +                              asprintf(&options[nr_options], "Annotate %s",
 +                                       bi->from.sym->name) > 0)
 +                              annotate_f = nr_options++;
 +
 +                      if (browser->selection != NULL &&
 +                          bi &&
 +                          bi->to.sym != NULL &&
 +                          !bi->to.map->dso->annotate_warned &&
 +                          (bi->to.sym != bi->from.sym ||
 +                           bi->to.map->dso != bi->from.map->dso) &&
 +                              asprintf(&options[nr_options], "Annotate %s",
 +                                       bi->to.sym->name) > 0)
 +                              annotate_t = nr_options++;
 +              } else {
 +
 +                      if (browser->selection != NULL &&
 +                          browser->selection->sym != NULL &&
 +                          !browser->selection->map->dso->annotate_warned &&
 +                              asprintf(&options[nr_options], "Annotate %s",
 +                                       browser->selection->sym->name) > 0)
 +                              annotate = nr_options++;
 +              }
  
                if (thread != NULL &&
                    asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
                        browse_map = nr_options++;
  add_exit_option:
                options[nr_options++] = (char *)"Exit";
 -
 +retry_popup_menu:
                choice = ui__popup_menu(nr_options, options);
  
                if (choice == nr_options - 1)
                        break;
  
 -              if (choice == -1)
 +              if (choice == -1) {
 +                      free_popup_options(options, nr_options - 1);
                        continue;
 +              }
  
 -              if (choice == annotate) {
 +              if (choice == annotate || choice == annotate_t || choice == annotate_f) {
                        struct hist_entry *he;
                        int err;
  do_annotate:
                        he = hist_browser__selected_entry(browser);
                        if (he == NULL)
                                continue;
 +
 +                      /*
 +                       * we stash the branch_info symbol + map into the
 +                       * the ms so we don't have to rewrite all the annotation
 +                       * code to use branch_info.
 +                       * in branch mode, the ms struct is not used
 +                       */
 +                      if (choice == annotate_f) {
 +                              he->ms.sym = he->branch_info->from.sym;
 +                              he->ms.map = he->branch_info->from.map;
 +                      }  else if (choice == annotate_t) {
 +                              he->ms.sym = he->branch_info->to.sym;
 +                              he->ms.map = he->branch_info->to.map;
 +                      }
 +
                        /*
                         * Don't let this be freed, say, by hists__decay_entry.
                         */
                        err = hist_entry__tui_annotate(he, evsel->idx,
                                                       timer, arg, delay_secs);
                        he->used = false;
 +                      /*
 +                       * offer option to annotate the other branch source or target
 +                       * (if they exists) when returning from annotate
 +                       */
 +                      if ((err == 'q' || err == CTRL('c'))
 +                          && annotate_t != -2 && annotate_f != -2)
 +                              goto retry_popup_menu;
 +
                        ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
                        if (err)
                                ui_browser__handle_resize(&browser->b);
 +
                } else if (choice == browse_map)
                        map__browse(browser->selection->map);
                else if (choice == zoom_dso) {
@@@ -1147,7 -1069,6 +1147,7 @@@ out_free_stack
        pstack__delete(fstack);
  out:
        hist_browser__delete(browser);
 +      free_popup_options(options, nr_options - 1);
        return key;
  }
  
@@@ -1174,7 -1095,7 +1174,7 @@@ static void perf_evsel_menu__write(stru
                                                       HE_COLORSET_NORMAL);
  
        nr_events = convert_unit(nr_events, &unit);
-       printed = snprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
+       printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
                           unit, unit == ' ' ? "" : " ", ev_name);
        slsmg_printf("%s", bf);
  
                if (!current_entry)
                        ui_browser__set_color(browser, HE_COLORSET_TOP);
                nr_events = convert_unit(nr_events, &unit);
-               snprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!", nr_events,
-                        unit, unit == ' ' ? "" : " ");
+               printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
+                                    nr_events, unit, unit == ' ' ? "" : " ");
                warn = bf;
        }