aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/util
diff options
context:
space:
mode:
authorIngo Molnar <mingo@elte.hu>2012-03-12 15:46:35 -0400
committerIngo Molnar <mingo@elte.hu>2012-03-12 15:47:05 -0400
commitbea95c152dee1791dd02cbc708afbb115bb00f9a (patch)
treeaf9994c42c5fdd81ba3dadd7b812e2fa85273353 /tools/perf/util
parentf9b4eeb809c6d031cc9561cc34dd691701cb2c2a (diff)
parent24bff2dc0f77b1f186b7bdf30060caf3df191a68 (diff)
Merge branch 'perf/hw-branch-sampling' into perf/core
Merge reason: The 'perf record -b' hardware branch sampling feature is ready for upstream. Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'tools/perf/util')
-rw-r--r--tools/perf/util/event.h1
-rw-r--r--tools/perf/util/evsel.c14
-rw-r--r--tools/perf/util/header.c207
-rw-r--r--tools/perf/util/header.h2
-rw-r--r--tools/perf/util/hist.c122
-rw-r--r--tools/perf/util/hist.h11
-rw-r--r--tools/perf/util/session.c77
-rw-r--r--tools/perf/util/session.h4
-rw-r--r--tools/perf/util/sort.c287
-rw-r--r--tools/perf/util/sort.h11
-rw-r--r--tools/perf/util/symbol.h20
-rw-r--r--tools/perf/util/ui/browsers/hists.c102
12 files changed, 714 insertions, 144 deletions
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index cbdeaad9c5e5..1b197280c621 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -81,6 +81,7 @@ struct perf_sample {
81 u32 raw_size; 81 u32 raw_size;
82 void *raw_data; 82 void *raw_data;
83 struct ip_callchain *callchain; 83 struct ip_callchain *callchain;
84 struct branch_stack *branch_stack;
84}; 85};
85 86
86#define BUILD_ID_SIZE 20 87#define BUILD_ID_SIZE 20
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index 302d49a9f985..f421f7cbc0d3 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -126,6 +126,10 @@ void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts)
126 attr->watermark = 0; 126 attr->watermark = 0;
127 attr->wakeup_events = 1; 127 attr->wakeup_events = 1;
128 } 128 }
129 if (opts->branch_stack) {
130 attr->sample_type |= PERF_SAMPLE_BRANCH_STACK;
131 attr->branch_sample_type = opts->branch_stack;
132 }
129 133
130 attr->mmap = track; 134 attr->mmap = track;
131 attr->comm = track; 135 attr->comm = track;
@@ -576,6 +580,16 @@ int perf_event__parse_sample(const union perf_event *event, u64 type,
576 data->raw_data = (void *) pdata; 580 data->raw_data = (void *) pdata;
577 } 581 }
578 582
583 if (type & PERF_SAMPLE_BRANCH_STACK) {
584 u64 sz;
585
586 data->branch_stack = (struct branch_stack *)array;
587 array++; /* nr */
588
589 sz = data->branch_stack->nr * sizeof(struct branch_entry);
590 sz /= sizeof(u64);
591 array += sz;
592 }
579 return 0; 593 return 0;
580} 594}
581 595
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 9f867d96c6a5..0d9b6da86a39 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -1023,6 +1023,12 @@ write_it:
1023 return do_write_string(fd, buffer); 1023 return do_write_string(fd, buffer);
1024} 1024}
1025 1025
1026static int write_branch_stack(int fd __used, struct perf_header *h __used,
1027 struct perf_evlist *evlist __used)
1028{
1029 return 0;
1030}
1031
1026static void print_hostname(struct perf_header *ph, int fd, FILE *fp) 1032static void print_hostname(struct perf_header *ph, int fd, FILE *fp)
1027{ 1033{
1028 char *str = do_read_string(fd, ph); 1034 char *str = do_read_string(fd, ph);
@@ -1144,8 +1150,9 @@ static void print_event_desc(struct perf_header *ph, int fd, FILE *fp)
1144 uint64_t id; 1150 uint64_t id;
1145 void *buf = NULL; 1151 void *buf = NULL;
1146 char *str; 1152 char *str;
1147 u32 nre, sz, nr, i, j, msz; 1153 u32 nre, sz, nr, i, j;
1148 int ret; 1154 ssize_t ret;
1155 size_t msz;
1149 1156
1150 /* number of events */ 1157 /* number of events */
1151 ret = read(fd, &nre, sizeof(nre)); 1158 ret = read(fd, &nre, sizeof(nre));
@@ -1162,25 +1169,23 @@ static void print_event_desc(struct perf_header *ph, int fd, FILE *fp)
1162 if (ph->needs_swap) 1169 if (ph->needs_swap)
1163 sz = bswap_32(sz); 1170 sz = bswap_32(sz);
1164 1171
1165 /*
1166 * ensure it is at least to our ABI rev
1167 */
1168 if (sz < (u32)sizeof(attr))
1169 goto error;
1170
1171 memset(&attr, 0, sizeof(attr)); 1172 memset(&attr, 0, sizeof(attr));
1172 1173
1173 /* read entire region to sync up to next field */ 1174 /* buffer to hold on file attr struct */
1174 buf = malloc(sz); 1175 buf = malloc(sz);
1175 if (!buf) 1176 if (!buf)
1176 goto error; 1177 goto error;
1177 1178
1178 msz = sizeof(attr); 1179 msz = sizeof(attr);
1179 if (sz < msz) 1180 if (sz < (ssize_t)msz)
1180 msz = sz; 1181 msz = sz;
1181 1182
1182 for (i = 0 ; i < nre; i++) { 1183 for (i = 0 ; i < nre; i++) {
1183 1184
1185 /*
1186 * must read entire on-file attr struct to
1187 * sync up with layout.
1188 */
1184 ret = read(fd, buf, sz); 1189 ret = read(fd, buf, sz);
1185 if (ret != (ssize_t)sz) 1190 if (ret != (ssize_t)sz)
1186 goto error; 1191 goto error;
@@ -1316,6 +1321,12 @@ static void print_cpuid(struct perf_header *ph, int fd, FILE *fp)
1316 free(str); 1321 free(str);
1317} 1322}
1318 1323
1324static void print_branch_stack(struct perf_header *ph __used, int fd __used,
1325 FILE *fp)
1326{
1327 fprintf(fp, "# contains samples with branch stack\n");
1328}
1329
1319static int __event_process_build_id(struct build_id_event *bev, 1330static int __event_process_build_id(struct build_id_event *bev,
1320 char *filename, 1331 char *filename,
1321 struct perf_session *session) 1332 struct perf_session *session)
@@ -1520,6 +1531,7 @@ static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = {
1520 FEAT_OPA(HEADER_CMDLINE, cmdline), 1531 FEAT_OPA(HEADER_CMDLINE, cmdline),
1521 FEAT_OPF(HEADER_CPU_TOPOLOGY, cpu_topology), 1532 FEAT_OPF(HEADER_CPU_TOPOLOGY, cpu_topology),
1522 FEAT_OPF(HEADER_NUMA_TOPOLOGY, numa_topology), 1533 FEAT_OPF(HEADER_NUMA_TOPOLOGY, numa_topology),
1534 FEAT_OPA(HEADER_BRANCH_STACK, branch_stack),
1523}; 1535};
1524 1536
1525struct header_print_data { 1537struct header_print_data {
@@ -1804,35 +1816,101 @@ out_free:
1804 return err; 1816 return err;
1805} 1817}
1806 1818
1807static int check_magic_endian(u64 *magic, struct perf_file_header *header, 1819static const int attr_file_abi_sizes[] = {
1808 struct perf_header *ph) 1820 [0] = PERF_ATTR_SIZE_VER0,
1821 [1] = PERF_ATTR_SIZE_VER1,
1822 0,
1823};
1824
1825/*
1826 * In the legacy file format, the magic number is not used to encode endianness.
1827 * hdr_sz was used to encode endianness. But given that hdr_sz can vary based
1828 * on ABI revisions, we need to try all combinations for all endianness to
1829 * detect the endianness.
1830 */
1831static int try_all_file_abis(uint64_t hdr_sz, struct perf_header *ph)
1809{ 1832{
1810 int ret; 1833 uint64_t ref_size, attr_size;
1834 int i;
1811 1835
1812 /* check for legacy format */ 1836 for (i = 0 ; attr_file_abi_sizes[i]; i++) {
1813 ret = memcmp(magic, __perf_magic1, sizeof(*magic)); 1837 ref_size = attr_file_abi_sizes[i]
1814 if (ret == 0) { 1838 + sizeof(struct perf_file_section);
1815 pr_debug("legacy perf.data format\n"); 1839 if (hdr_sz != ref_size) {
1816 if (!header) 1840 attr_size = bswap_64(hdr_sz);
1817 return -1; 1841 if (attr_size != ref_size)
1842 continue;
1818 1843
1819 if (header->attr_size != sizeof(struct perf_file_attr)) { 1844 ph->needs_swap = true;
1820 u64 attr_size = bswap_64(header->attr_size); 1845 }
1846 pr_debug("ABI%d perf.data file detected, need_swap=%d\n",
1847 i,
1848 ph->needs_swap);
1849 return 0;
1850 }
1851 /* could not determine endianness */
1852 return -1;
1853}
1821 1854
1822 if (attr_size != sizeof(struct perf_file_attr)) 1855#define PERF_PIPE_HDR_VER0 16
1823 return -1; 1856
1857static const size_t attr_pipe_abi_sizes[] = {
1858 [0] = PERF_PIPE_HDR_VER0,
1859 0,
1860};
1861
1862/*
1863 * In the legacy pipe format, there is an implicit assumption that endiannesss
1864 * between host recording the samples, and host parsing the samples is the
1865 * same. This is not always the case given that the pipe output may always be
1866 * redirected into a file and analyzed on a different machine with possibly a
1867 * different endianness and perf_event ABI revsions in the perf tool itself.
1868 */
1869static int try_all_pipe_abis(uint64_t hdr_sz, struct perf_header *ph)
1870{
1871 u64 attr_size;
1872 int i;
1873
1874 for (i = 0 ; attr_pipe_abi_sizes[i]; i++) {
1875 if (hdr_sz != attr_pipe_abi_sizes[i]) {
1876 attr_size = bswap_64(hdr_sz);
1877 if (attr_size != hdr_sz)
1878 continue;
1824 1879
1825 ph->needs_swap = true; 1880 ph->needs_swap = true;
1826 } 1881 }
1882 pr_debug("Pipe ABI%d perf.data file detected\n", i);
1827 return 0; 1883 return 0;
1828 } 1884 }
1885 return -1;
1886}
1887
1888static int check_magic_endian(u64 magic, uint64_t hdr_sz,
1889 bool is_pipe, struct perf_header *ph)
1890{
1891 int ret;
1892
1893 /* check for legacy format */
1894 ret = memcmp(&magic, __perf_magic1, sizeof(magic));
1895 if (ret == 0) {
1896 pr_debug("legacy perf.data format\n");
1897 if (is_pipe)
1898 return try_all_pipe_abis(hdr_sz, ph);
1899
1900 return try_all_file_abis(hdr_sz, ph);
1901 }
1902 /*
1903 * the new magic number serves two purposes:
1904 * - unique number to identify actual perf.data files
1905 * - encode endianness of file
1906 */
1829 1907
1830 /* check magic number with same endianness */ 1908 /* check magic number with one endianness */
1831 if (*magic == __perf_magic2) 1909 if (magic == __perf_magic2)
1832 return 0; 1910 return 0;
1833 1911
1834 /* check magic number but opposite endianness */ 1912 /* check magic number with opposite endianness */
1835 if (*magic != __perf_magic2_sw) 1913 if (magic != __perf_magic2_sw)
1836 return -1; 1914 return -1;
1837 1915
1838 ph->needs_swap = true; 1916 ph->needs_swap = true;
@@ -1851,8 +1929,11 @@ int perf_file_header__read(struct perf_file_header *header,
1851 if (ret <= 0) 1929 if (ret <= 0)
1852 return -1; 1930 return -1;
1853 1931
1854 if (check_magic_endian(&header->magic, header, ph) < 0) 1932 if (check_magic_endian(header->magic,
1933 header->attr_size, false, ph) < 0) {
1934 pr_debug("magic/endian check failed\n");
1855 return -1; 1935 return -1;
1936 }
1856 1937
1857 if (ph->needs_swap) { 1938 if (ph->needs_swap) {
1858 mem_bswap_64(header, offsetof(struct perf_file_header, 1939 mem_bswap_64(header, offsetof(struct perf_file_header,
@@ -1939,21 +2020,17 @@ static int perf_file_header__read_pipe(struct perf_pipe_file_header *header,
1939 if (ret <= 0) 2020 if (ret <= 0)
1940 return -1; 2021 return -1;
1941 2022
1942 if (check_magic_endian(&header->magic, NULL, ph) < 0) 2023 if (check_magic_endian(header->magic, header->size, true, ph) < 0) {
2024 pr_debug("endian/magic failed\n");
1943 return -1; 2025 return -1;
2026 }
2027
2028 if (ph->needs_swap)
2029 header->size = bswap_64(header->size);
1944 2030
1945 if (repipe && do_write(STDOUT_FILENO, header, sizeof(*header)) < 0) 2031 if (repipe && do_write(STDOUT_FILENO, header, sizeof(*header)) < 0)
1946 return -1; 2032 return -1;
1947 2033
1948 if (header->size != sizeof(*header)) {
1949 u64 size = bswap_64(header->size);
1950
1951 if (size != sizeof(*header))
1952 return -1;
1953
1954 ph->needs_swap = true;
1955 }
1956
1957 return 0; 2034 return 0;
1958} 2035}
1959 2036
@@ -1973,6 +2050,52 @@ static int perf_header__read_pipe(struct perf_session *session, int fd)
1973 return 0; 2050 return 0;
1974} 2051}
1975 2052
2053static int read_attr(int fd, struct perf_header *ph,
2054 struct perf_file_attr *f_attr)
2055{
2056 struct perf_event_attr *attr = &f_attr->attr;
2057 size_t sz, left;
2058 size_t our_sz = sizeof(f_attr->attr);
2059 int ret;
2060
2061 memset(f_attr, 0, sizeof(*f_attr));
2062
2063 /* read minimal guaranteed structure */
2064 ret = readn(fd, attr, PERF_ATTR_SIZE_VER0);
2065 if (ret <= 0) {
2066 pr_debug("cannot read %d bytes of header attr\n",
2067 PERF_ATTR_SIZE_VER0);
2068 return -1;
2069 }
2070
2071 /* on file perf_event_attr size */
2072 sz = attr->size;
2073
2074 if (ph->needs_swap)
2075 sz = bswap_32(sz);
2076
2077 if (sz == 0) {
2078 /* assume ABI0 */
2079 sz = PERF_ATTR_SIZE_VER0;
2080 } else if (sz > our_sz) {
2081 pr_debug("file uses a more recent and unsupported ABI"
2082 " (%zu bytes extra)\n", sz - our_sz);
2083 return -1;
2084 }
2085 /* what we have not yet read and that we know about */
2086 left = sz - PERF_ATTR_SIZE_VER0;
2087 if (left) {
2088 void *ptr = attr;
2089 ptr += PERF_ATTR_SIZE_VER0;
2090
2091 ret = readn(fd, ptr, left);
2092 }
2093 /* read perf_file_section, ids are read in caller */
2094 ret = readn(fd, &f_attr->ids, sizeof(f_attr->ids));
2095
2096 return ret <= 0 ? -1 : 0;
2097}
2098
1976int perf_session__read_header(struct perf_session *session, int fd) 2099int perf_session__read_header(struct perf_session *session, int fd)
1977{ 2100{
1978 struct perf_header *header = &session->header; 2101 struct perf_header *header = &session->header;
@@ -1988,19 +2111,17 @@ int perf_session__read_header(struct perf_session *session, int fd)
1988 if (session->fd_pipe) 2111 if (session->fd_pipe)
1989 return perf_header__read_pipe(session, fd); 2112 return perf_header__read_pipe(session, fd);
1990 2113
1991 if (perf_file_header__read(&f_header, header, fd) < 0) { 2114 if (perf_file_header__read(&f_header, header, fd) < 0)
1992 pr_debug("incompatible file format\n");
1993 return -EINVAL; 2115 return -EINVAL;
1994 }
1995 2116
1996 nr_attrs = f_header.attrs.size / sizeof(f_attr); 2117 nr_attrs = f_header.attrs.size / f_header.attr_size;
1997 lseek(fd, f_header.attrs.offset, SEEK_SET); 2118 lseek(fd, f_header.attrs.offset, SEEK_SET);
1998 2119
1999 for (i = 0; i < nr_attrs; i++) { 2120 for (i = 0; i < nr_attrs; i++) {
2000 struct perf_evsel *evsel; 2121 struct perf_evsel *evsel;
2001 off_t tmp; 2122 off_t tmp;
2002 2123
2003 if (readn(fd, &f_attr, sizeof(f_attr)) <= 0) 2124 if (read_attr(fd, header, &f_attr) < 0)
2004 goto out_errno; 2125 goto out_errno;
2005 2126
2006 if (header->needs_swap) 2127 if (header->needs_swap)
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h
index e68f617d082f..21a6be09c129 100644
--- a/tools/perf/util/header.h
+++ b/tools/perf/util/header.h
@@ -27,7 +27,7 @@ enum {
27 HEADER_EVENT_DESC, 27 HEADER_EVENT_DESC,
28 HEADER_CPU_TOPOLOGY, 28 HEADER_CPU_TOPOLOGY,
29 HEADER_NUMA_TOPOLOGY, 29 HEADER_NUMA_TOPOLOGY,
30 30 HEADER_BRANCH_STACK,
31 HEADER_LAST_FEATURE, 31 HEADER_LAST_FEATURE,
32 HEADER_FEAT_BITS = 256, 32 HEADER_FEAT_BITS = 256,
33}; 33};
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index 6f505d1abac7..8380c3db1c92 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -50,21 +50,25 @@ static void hists__reset_col_len(struct hists *hists)
50 hists__set_col_len(hists, col, 0); 50 hists__set_col_len(hists, col, 0);
51} 51}
52 52
53static void hists__set_unres_dso_col_len(struct hists *hists, int dso)
54{
55 const unsigned int unresolved_col_width = BITS_PER_LONG / 4;
56
57 if (hists__col_len(hists, dso) < unresolved_col_width &&
58 !symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
59 !symbol_conf.dso_list)
60 hists__set_col_len(hists, dso, unresolved_col_width);
61}
62
53static void hists__calc_col_len(struct hists *hists, struct hist_entry *h) 63static void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
54{ 64{
65 const unsigned int unresolved_col_width = BITS_PER_LONG / 4;
55 u16 len; 66 u16 len;
56 67
57 if (h->ms.sym) 68 if (h->ms.sym)
58 hists__new_col_len(hists, HISTC_SYMBOL, h->ms.sym->namelen); 69 hists__new_col_len(hists, HISTC_SYMBOL, h->ms.sym->namelen + 4);
59 else { 70 else
60 const unsigned int unresolved_col_width = BITS_PER_LONG / 4; 71 hists__set_unres_dso_col_len(hists, HISTC_DSO);
61
62 if (hists__col_len(hists, HISTC_DSO) < unresolved_col_width &&
63 !symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
64 !symbol_conf.dso_list)
65 hists__set_col_len(hists, HISTC_DSO,
66 unresolved_col_width);
67 }
68 72
69 len = thread__comm_len(h->thread); 73 len = thread__comm_len(h->thread);
70 if (hists__new_col_len(hists, HISTC_COMM, len)) 74 if (hists__new_col_len(hists, HISTC_COMM, len))
@@ -74,6 +78,37 @@ static void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
74 len = dso__name_len(h->ms.map->dso); 78 len = dso__name_len(h->ms.map->dso);
75 hists__new_col_len(hists, HISTC_DSO, len); 79 hists__new_col_len(hists, HISTC_DSO, len);
76 } 80 }
81
82 if (h->branch_info) {
83 int symlen;
84 /*
85 * +4 accounts for '[x] ' priv level info
86 * +2 account of 0x prefix on raw addresses
87 */
88 if (h->branch_info->from.sym) {
89 symlen = (int)h->branch_info->from.sym->namelen + 4;
90 hists__new_col_len(hists, HISTC_SYMBOL_FROM, symlen);
91
92 symlen = dso__name_len(h->branch_info->from.map->dso);
93 hists__new_col_len(hists, HISTC_DSO_FROM, symlen);
94 } else {
95 symlen = unresolved_col_width + 4 + 2;
96 hists__new_col_len(hists, HISTC_SYMBOL_FROM, symlen);
97 hists__set_unres_dso_col_len(hists, HISTC_DSO_FROM);
98 }
99
100 if (h->branch_info->to.sym) {
101 symlen = (int)h->branch_info->to.sym->namelen + 4;
102 hists__new_col_len(hists, HISTC_SYMBOL_TO, symlen);
103
104 symlen = dso__name_len(h->branch_info->to.map->dso);
105 hists__new_col_len(hists, HISTC_DSO_TO, symlen);
106 } else {
107 symlen = unresolved_col_width + 4 + 2;
108 hists__new_col_len(hists, HISTC_SYMBOL_TO, symlen);
109 hists__set_unres_dso_col_len(hists, HISTC_DSO_TO);
110 }
111 }
77} 112}
78 113
79static void hist_entry__add_cpumode_period(struct hist_entry *he, 114static void hist_entry__add_cpumode_period(struct hist_entry *he,
@@ -195,26 +230,14 @@ static u8 symbol__parent_filter(const struct symbol *parent)
195 return 0; 230 return 0;
196} 231}
197 232
198struct hist_entry *__hists__add_entry(struct hists *hists, 233static struct hist_entry *add_hist_entry(struct hists *hists,
234 struct hist_entry *entry,
199 struct addr_location *al, 235 struct addr_location *al,
200 struct symbol *sym_parent, u64 period) 236 u64 period)
201{ 237{
202 struct rb_node **p; 238 struct rb_node **p;
203 struct rb_node *parent = NULL; 239 struct rb_node *parent = NULL;
204 struct hist_entry *he; 240 struct hist_entry *he;
205 struct hist_entry entry = {
206 .thread = al->thread,
207 .ms = {
208 .map = al->map,
209 .sym = al->sym,
210 },
211 .cpu = al->cpu,
212 .ip = al->addr,
213 .level = al->level,
214 .period = period,
215 .parent = sym_parent,
216 .filtered = symbol__parent_filter(sym_parent),
217 };
218 int cmp; 241 int cmp;
219 242
220 pthread_mutex_lock(&hists->lock); 243 pthread_mutex_lock(&hists->lock);
@@ -225,7 +248,7 @@ struct hist_entry *__hists__add_entry(struct hists *hists,
225 parent = *p; 248 parent = *p;
226 he = rb_entry(parent, struct hist_entry, rb_node_in); 249 he = rb_entry(parent, struct hist_entry, rb_node_in);
227 250
228 cmp = hist_entry__cmp(&entry, he); 251 cmp = hist_entry__cmp(entry, he);
229 252
230 if (!cmp) { 253 if (!cmp) {
231 he->period += period; 254 he->period += period;
@@ -239,7 +262,7 @@ struct hist_entry *__hists__add_entry(struct hists *hists,
239 p = &(*p)->rb_right; 262 p = &(*p)->rb_right;
240 } 263 }
241 264
242 he = hist_entry__new(&entry); 265 he = hist_entry__new(entry);
243 if (!he) 266 if (!he)
244 goto out_unlock; 267 goto out_unlock;
245 268
@@ -252,6 +275,51 @@ out_unlock:
252 return he; 275 return he;
253} 276}
254 277
278struct hist_entry *__hists__add_branch_entry(struct hists *self,
279 struct addr_location *al,
280 struct symbol *sym_parent,
281 struct branch_info *bi,
282 u64 period)
283{
284 struct hist_entry entry = {
285 .thread = al->thread,
286 .ms = {
287 .map = bi->to.map,
288 .sym = bi->to.sym,
289 },
290 .cpu = al->cpu,
291 .ip = bi->to.addr,
292 .level = al->level,
293 .period = period,
294 .parent = sym_parent,
295 .filtered = symbol__parent_filter(sym_parent),
296 .branch_info = bi,
297 };
298
299 return add_hist_entry(self, &entry, al, period);
300}
301
302struct hist_entry *__hists__add_entry(struct hists *self,
303 struct addr_location *al,
304 struct symbol *sym_parent, u64 period)
305{
306 struct hist_entry entry = {
307 .thread = al->thread,
308 .ms = {
309 .map = al->map,
310 .sym = al->sym,
311 },
312 .cpu = al->cpu,
313 .ip = al->addr,
314 .level = al->level,
315 .period = period,
316 .parent = sym_parent,
317 .filtered = symbol__parent_filter(sym_parent),
318 };
319
320 return add_hist_entry(self, &entry, al, period);
321}
322
255int64_t 323int64_t
256hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) 324hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
257{ 325{
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
index 48e5acd1e862..9413f3e31fea 100644
--- a/tools/perf/util/hist.h
+++ b/tools/perf/util/hist.h
@@ -42,6 +42,11 @@ enum hist_column {
42 HISTC_COMM, 42 HISTC_COMM,
43 HISTC_PARENT, 43 HISTC_PARENT,
44 HISTC_CPU, 44 HISTC_CPU,
45 HISTC_MISPREDICT,
46 HISTC_SYMBOL_FROM,
47 HISTC_SYMBOL_TO,
48 HISTC_DSO_FROM,
49 HISTC_DSO_TO,
45 HISTC_NR_COLS, /* Last entry */ 50 HISTC_NR_COLS, /* Last entry */
46}; 51};
47 52
@@ -74,6 +79,12 @@ int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size,
74 struct hists *hists); 79 struct hists *hists);
75void hist_entry__free(struct hist_entry *); 80void hist_entry__free(struct hist_entry *);
76 81
82struct hist_entry *__hists__add_branch_entry(struct hists *self,
83 struct addr_location *al,
84 struct symbol *sym_parent,
85 struct branch_info *bi,
86 u64 period);
87
77void hists__output_resort(struct hists *self); 88void hists__output_resort(struct hists *self);
78void hists__output_resort_threaded(struct hists *hists); 89void hists__output_resort_threaded(struct hists *hists);
79void hists__collapse_resort(struct hists *self); 90void hists__collapse_resort(struct hists *self);
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 9f833cf9c6a9..002ebbf59f48 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -24,7 +24,7 @@ static int perf_session__open(struct perf_session *self, bool force)
24 self->fd = STDIN_FILENO; 24 self->fd = STDIN_FILENO;
25 25
26 if (perf_session__read_header(self, self->fd) < 0) 26 if (perf_session__read_header(self, self->fd) < 0)
27 pr_err("incompatible file format"); 27 pr_err("incompatible file format (rerun with -v to learn more)");
28 28
29 return 0; 29 return 0;
30 } 30 }
@@ -56,7 +56,7 @@ static int perf_session__open(struct perf_session *self, bool force)
56 } 56 }
57 57
58 if (perf_session__read_header(self, self->fd) < 0) { 58 if (perf_session__read_header(self, self->fd) < 0) {
59 pr_err("incompatible file format"); 59 pr_err("incompatible file format (rerun with -v to learn more)");
60 goto out_close; 60 goto out_close;
61 } 61 }
62 62
@@ -229,6 +229,64 @@ static bool symbol__match_parent_regex(struct symbol *sym)
229 return 0; 229 return 0;
230} 230}
231 231
232static const u8 cpumodes[] = {
233 PERF_RECORD_MISC_USER,
234 PERF_RECORD_MISC_KERNEL,
235 PERF_RECORD_MISC_GUEST_USER,
236 PERF_RECORD_MISC_GUEST_KERNEL
237};
238#define NCPUMODES (sizeof(cpumodes)/sizeof(u8))
239
240static void ip__resolve_ams(struct machine *self, struct thread *thread,
241 struct addr_map_symbol *ams,
242 u64 ip)
243{
244 struct addr_location al;
245 size_t i;
246 u8 m;
247
248 memset(&al, 0, sizeof(al));
249
250 for (i = 0; i < NCPUMODES; i++) {
251 m = cpumodes[i];
252 /*
253 * We cannot use the header.misc hint to determine whether a
254 * branch stack address is user, kernel, guest, hypervisor.
255 * Branches may straddle the kernel/user/hypervisor boundaries.
256 * Thus, we have to try consecutively until we find a match
257 * or else, the symbol is unknown
258 */
259 thread__find_addr_location(thread, self, m, MAP__FUNCTION,
260 ip, &al, NULL);
261 if (al.sym)
262 goto found;
263 }
264found:
265 ams->addr = ip;
266 ams->al_addr = al.addr;
267 ams->sym = al.sym;
268 ams->map = al.map;
269}
270
271struct branch_info *machine__resolve_bstack(struct machine *self,
272 struct thread *thr,
273 struct branch_stack *bs)
274{
275 struct branch_info *bi;
276 unsigned int i;
277
278 bi = calloc(bs->nr, sizeof(struct branch_info));
279 if (!bi)
280 return NULL;
281
282 for (i = 0; i < bs->nr; i++) {
283 ip__resolve_ams(self, thr, &bi[i].to, bs->entries[i].to);
284 ip__resolve_ams(self, thr, &bi[i].from, bs->entries[i].from);
285 bi[i].flags = bs->entries[i].flags;
286 }
287 return bi;
288}
289
232int machine__resolve_callchain(struct machine *self, struct perf_evsel *evsel, 290int machine__resolve_callchain(struct machine *self, struct perf_evsel *evsel,
233 struct thread *thread, 291 struct thread *thread,
234 struct ip_callchain *chain, 292 struct ip_callchain *chain,
@@ -697,6 +755,18 @@ static void callchain__printf(struct perf_sample *sample)
697 i, sample->callchain->ips[i]); 755 i, sample->callchain->ips[i]);
698} 756}
699 757
758static void branch_stack__printf(struct perf_sample *sample)
759{
760 uint64_t i;
761
762 printf("... branch stack: nr:%" PRIu64 "\n", sample->branch_stack->nr);
763
764 for (i = 0; i < sample->branch_stack->nr; i++)
765 printf("..... %2"PRIu64": %016" PRIx64 " -> %016" PRIx64 "\n",
766 i, sample->branch_stack->entries[i].from,
767 sample->branch_stack->entries[i].to);
768}
769
700static void perf_session__print_tstamp(struct perf_session *session, 770static void perf_session__print_tstamp(struct perf_session *session,
701 union perf_event *event, 771 union perf_event *event,
702 struct perf_sample *sample) 772 struct perf_sample *sample)
@@ -744,6 +814,9 @@ static void dump_sample(struct perf_session *session, union perf_event *event,
744 814
745 if (session->sample_type & PERF_SAMPLE_CALLCHAIN) 815 if (session->sample_type & PERF_SAMPLE_CALLCHAIN)
746 callchain__printf(sample); 816 callchain__printf(sample);
817
818 if (session->sample_type & PERF_SAMPLE_BRANCH_STACK)
819 branch_stack__printf(sample);
747} 820}
748 821
749static struct machine * 822static struct machine *
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
index c8d90178e7de..7a5434c00565 100644
--- a/tools/perf/util/session.h
+++ b/tools/perf/util/session.h
@@ -73,6 +73,10 @@ int perf_session__resolve_callchain(struct perf_session *self, struct perf_evsel
73 struct ip_callchain *chain, 73 struct ip_callchain *chain,
74 struct symbol **parent); 74 struct symbol **parent);
75 75
76struct branch_info *machine__resolve_bstack(struct machine *self,
77 struct thread *thread,
78 struct branch_stack *bs);
79
76bool perf_session__has_traces(struct perf_session *self, const char *msg); 80bool perf_session__has_traces(struct perf_session *self, const char *msg);
77 81
78void mem_bswap_64(void *src, int byte_size); 82void mem_bswap_64(void *src, int byte_size);
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
index 16da30d8d765..88dbcf6f9575 100644
--- a/tools/perf/util/sort.c
+++ b/tools/perf/util/sort.c
@@ -8,6 +8,7 @@ const char default_sort_order[] = "comm,dso,symbol";
8const char *sort_order = default_sort_order; 8const char *sort_order = default_sort_order;
9int sort__need_collapse = 0; 9int sort__need_collapse = 0;
10int sort__has_parent = 0; 10int sort__has_parent = 0;
11int sort__branch_mode = -1; /* -1 = means not set */
11 12
12enum sort_type sort__first_dimension; 13enum sort_type sort__first_dimension;
13 14
@@ -94,6 +95,26 @@ static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf,
94 return repsep_snprintf(bf, size, "%*s", width, self->thread->comm); 95 return repsep_snprintf(bf, size, "%*s", width, self->thread->comm);
95} 96}
96 97
98static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r)
99{
100 struct dso *dso_l = map_l ? map_l->dso : NULL;
101 struct dso *dso_r = map_r ? map_r->dso : NULL;
102 const char *dso_name_l, *dso_name_r;
103
104 if (!dso_l || !dso_r)
105 return cmp_null(dso_l, dso_r);
106
107 if (verbose) {
108 dso_name_l = dso_l->long_name;
109 dso_name_r = dso_r->long_name;
110 } else {
111 dso_name_l = dso_l->short_name;
112 dso_name_r = dso_r->short_name;
113 }
114
115 return strcmp(dso_name_l, dso_name_r);
116}
117
97struct sort_entry sort_comm = { 118struct sort_entry sort_comm = {
98 .se_header = "Command", 119 .se_header = "Command",
99 .se_cmp = sort__comm_cmp, 120 .se_cmp = sort__comm_cmp,
@@ -107,36 +128,74 @@ struct sort_entry sort_comm = {
107static int64_t 128static int64_t
108sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) 129sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
109{ 130{
110 struct dso *dso_l = left->ms.map ? left->ms.map->dso : NULL; 131 return _sort__dso_cmp(left->ms.map, right->ms.map);
111 struct dso *dso_r = right->ms.map ? right->ms.map->dso : NULL; 132}
112 const char *dso_name_l, *dso_name_r;
113 133
114 if (!dso_l || !dso_r)
115 return cmp_null(dso_l, dso_r);
116 134
117 if (verbose) { 135static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r,
118 dso_name_l = dso_l->long_name; 136 u64 ip_l, u64 ip_r)
119 dso_name_r = dso_r->long_name; 137{
120 } else { 138 if (!sym_l || !sym_r)
121 dso_name_l = dso_l->short_name; 139 return cmp_null(sym_l, sym_r);
122 dso_name_r = dso_r->short_name; 140
141 if (sym_l == sym_r)
142 return 0;
143
144 if (sym_l)
145 ip_l = sym_l->start;
146 if (sym_r)
147 ip_r = sym_r->start;
148
149 return (int64_t)(ip_r - ip_l);
150}
151
152static int _hist_entry__dso_snprintf(struct map *map, char *bf,
153 size_t size, unsigned int width)
154{
155 if (map && map->dso) {
156 const char *dso_name = !verbose ? map->dso->short_name :
157 map->dso->long_name;
158 return repsep_snprintf(bf, size, "%-*s", width, dso_name);
123 } 159 }
124 160
125 return strcmp(dso_name_l, dso_name_r); 161 return repsep_snprintf(bf, size, "%-*s", width, "[unknown]");
126} 162}
127 163
128static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf, 164static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf,
129 size_t size, unsigned int width) 165 size_t size, unsigned int width)
130{ 166{
131 if (self->ms.map && self->ms.map->dso) { 167 return _hist_entry__dso_snprintf(self->ms.map, bf, size, width);
132 const char *dso_name = !verbose ? self->ms.map->dso->short_name : 168}
133 self->ms.map->dso->long_name; 169
134 return repsep_snprintf(bf, size, "%-*s", width, dso_name); 170static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym,
171 u64 ip, char level, char *bf, size_t size,
172 unsigned int width __used)
173{
174 size_t ret = 0;
175
176 if (verbose) {
177 char o = map ? dso__symtab_origin(map->dso) : '!';
178 ret += repsep_snprintf(bf, size, "%-#*llx %c ",
179 BITS_PER_LONG / 4, ip, o);
135 } 180 }
136 181
137 return repsep_snprintf(bf, size, "%-*s", width, "[unknown]"); 182 ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level);
183 if (sym)
184 ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
185 width - ret,
186 sym->name);
187 else {
188 size_t len = BITS_PER_LONG / 4;
189 ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx",
190 len, ip);
191 ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
192 width - ret, "");
193 }
194
195 return ret;
138} 196}
139 197
198
140struct sort_entry sort_dso = { 199struct sort_entry sort_dso = {
141 .se_header = "Shared Object", 200 .se_header = "Shared Object",
142 .se_cmp = sort__dso_cmp, 201 .se_cmp = sort__dso_cmp,
@@ -144,8 +203,14 @@ struct sort_entry sort_dso = {
144 .se_width_idx = HISTC_DSO, 203 .se_width_idx = HISTC_DSO,
145}; 204};
146 205
147/* --sort symbol */ 206static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
207 size_t size, unsigned int width __used)
208{
209 return _hist_entry__sym_snprintf(self->ms.map, self->ms.sym, self->ip,
210 self->level, bf, size, width);
211}
148 212
213/* --sort symbol */
149static int64_t 214static int64_t
150sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) 215sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
151{ 216{
@@ -163,31 +228,7 @@ sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
163 ip_l = left->ms.sym->start; 228 ip_l = left->ms.sym->start;
164 ip_r = right->ms.sym->start; 229 ip_r = right->ms.sym->start;
165 230
166 return (int64_t)(ip_r - ip_l); 231 return _sort__sym_cmp(left->ms.sym, right->ms.sym, ip_l, ip_r);
167}
168
169static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
170 size_t size, unsigned int width __used)
171{
172 size_t ret = 0;
173
174 if (verbose) {
175 char o = self->ms.map ? dso__symtab_origin(self->ms.map->dso) : '!';
176 ret += repsep_snprintf(bf, size, "%-#*llx %c ",
177 BITS_PER_LONG / 4, self->ip, o);
178 }
179
180 if (!sort_dso.elide)
181 ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", self->level);
182
183 if (self->ms.sym)
184 ret += repsep_snprintf(bf + ret, size - ret, "%s",
185 self->ms.sym->name);
186 else
187 ret += repsep_snprintf(bf + ret, size - ret, "%-#*llx",
188 BITS_PER_LONG / 4, self->ip);
189
190 return ret;
191} 232}
192 233
193struct sort_entry sort_sym = { 234struct sort_entry sort_sym = {
@@ -246,19 +287,155 @@ struct sort_entry sort_cpu = {
246 .se_width_idx = HISTC_CPU, 287 .se_width_idx = HISTC_CPU,
247}; 288};
248 289
290static int64_t
291sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right)
292{
293 return _sort__dso_cmp(left->branch_info->from.map,
294 right->branch_info->from.map);
295}
296
297static int hist_entry__dso_from_snprintf(struct hist_entry *self, char *bf,
298 size_t size, unsigned int width)
299{
300 return _hist_entry__dso_snprintf(self->branch_info->from.map,
301 bf, size, width);
302}
303
304struct sort_entry sort_dso_from = {
305 .se_header = "Source Shared Object",
306 .se_cmp = sort__dso_from_cmp,
307 .se_snprintf = hist_entry__dso_from_snprintf,
308 .se_width_idx = HISTC_DSO_FROM,
309};
310
311static int64_t
312sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right)
313{
314 return _sort__dso_cmp(left->branch_info->to.map,
315 right->branch_info->to.map);
316}
317
318static int hist_entry__dso_to_snprintf(struct hist_entry *self, char *bf,
319 size_t size, unsigned int width)
320{
321 return _hist_entry__dso_snprintf(self->branch_info->to.map,
322 bf, size, width);
323}
324
325static int64_t
326sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right)
327{
328 struct addr_map_symbol *from_l = &left->branch_info->from;
329 struct addr_map_symbol *from_r = &right->branch_info->from;
330
331 if (!from_l->sym && !from_r->sym)
332 return right->level - left->level;
333
334 return _sort__sym_cmp(from_l->sym, from_r->sym, from_l->addr,
335 from_r->addr);
336}
337
338static int64_t
339sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right)
340{
341 struct addr_map_symbol *to_l = &left->branch_info->to;
342 struct addr_map_symbol *to_r = &right->branch_info->to;
343
344 if (!to_l->sym && !to_r->sym)
345 return right->level - left->level;
346
347 return _sort__sym_cmp(to_l->sym, to_r->sym, to_l->addr, to_r->addr);
348}
349
350static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf,
351 size_t size, unsigned int width __used)
352{
353 struct addr_map_symbol *from = &self->branch_info->from;
354 return _hist_entry__sym_snprintf(from->map, from->sym, from->addr,
355 self->level, bf, size, width);
356
357}
358
359static int hist_entry__sym_to_snprintf(struct hist_entry *self, char *bf,
360 size_t size, unsigned int width __used)
361{
362 struct addr_map_symbol *to = &self->branch_info->to;
363 return _hist_entry__sym_snprintf(to->map, to->sym, to->addr,
364 self->level, bf, size, width);
365
366}
367
368struct sort_entry sort_dso_to = {
369 .se_header = "Target Shared Object",
370 .se_cmp = sort__dso_to_cmp,
371 .se_snprintf = hist_entry__dso_to_snprintf,
372 .se_width_idx = HISTC_DSO_TO,
373};
374
375struct sort_entry sort_sym_from = {
376 .se_header = "Source Symbol",
377 .se_cmp = sort__sym_from_cmp,
378 .se_snprintf = hist_entry__sym_from_snprintf,
379 .se_width_idx = HISTC_SYMBOL_FROM,
380};
381
382struct sort_entry sort_sym_to = {
383 .se_header = "Target Symbol",
384 .se_cmp = sort__sym_to_cmp,
385 .se_snprintf = hist_entry__sym_to_snprintf,
386 .se_width_idx = HISTC_SYMBOL_TO,
387};
388
389static int64_t
390sort__mispredict_cmp(struct hist_entry *left, struct hist_entry *right)
391{
392 const unsigned char mp = left->branch_info->flags.mispred !=
393 right->branch_info->flags.mispred;
394 const unsigned char p = left->branch_info->flags.predicted !=
395 right->branch_info->flags.predicted;
396
397 return mp || p;
398}
399
400static int hist_entry__mispredict_snprintf(struct hist_entry *self, char *bf,
401 size_t size, unsigned int width){
402 static const char *out = "N/A";
403
404 if (self->branch_info->flags.predicted)
405 out = "N";
406 else if (self->branch_info->flags.mispred)
407 out = "Y";
408
409 return repsep_snprintf(bf, size, "%-*s", width, out);
410}
411
412struct sort_entry sort_mispredict = {
413 .se_header = "Branch Mispredicted",
414 .se_cmp = sort__mispredict_cmp,
415 .se_snprintf = hist_entry__mispredict_snprintf,
416 .se_width_idx = HISTC_MISPREDICT,
417};
418
249struct sort_dimension { 419struct sort_dimension {
250 const char *name; 420 const char *name;
251 struct sort_entry *entry; 421 struct sort_entry *entry;
252 int taken; 422 int taken;
253}; 423};
254 424
425#define DIM(d, n, func) [d] = { .name = n, .entry = &(func) }
426
255static struct sort_dimension sort_dimensions[] = { 427static struct sort_dimension sort_dimensions[] = {
256 { .name = "pid", .entry = &sort_thread, }, 428 DIM(SORT_PID, "pid", sort_thread),
257 { .name = "comm", .entry = &sort_comm, }, 429 DIM(SORT_COMM, "comm", sort_comm),
258 { .name = "dso", .entry = &sort_dso, }, 430 DIM(SORT_DSO, "dso", sort_dso),
259 { .name = "symbol", .entry = &sort_sym, }, 431 DIM(SORT_DSO_FROM, "dso_from", sort_dso_from),
260 { .name = "parent", .entry = &sort_parent, }, 432 DIM(SORT_DSO_TO, "dso_to", sort_dso_to),
261 { .name = "cpu", .entry = &sort_cpu, }, 433 DIM(SORT_SYM, "symbol", sort_sym),
434 DIM(SORT_SYM_TO, "symbol_from", sort_sym_from),
435 DIM(SORT_SYM_FROM, "symbol_to", sort_sym_to),
436 DIM(SORT_PARENT, "parent", sort_parent),
437 DIM(SORT_CPU, "cpu", sort_cpu),
438 DIM(SORT_MISPREDICT, "mispredict", sort_mispredict),
262}; 439};
263 440
264int sort_dimension__add(const char *tok) 441int sort_dimension__add(const char *tok)
@@ -270,7 +447,6 @@ int sort_dimension__add(const char *tok)
270 447
271 if (strncasecmp(tok, sd->name, strlen(tok))) 448 if (strncasecmp(tok, sd->name, strlen(tok)))
272 continue; 449 continue;
273
274 if (sd->entry == &sort_parent) { 450 if (sd->entry == &sort_parent) {
275 int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED); 451 int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED);
276 if (ret) { 452 if (ret) {
@@ -302,6 +478,16 @@ int sort_dimension__add(const char *tok)
302 sort__first_dimension = SORT_PARENT; 478 sort__first_dimension = SORT_PARENT;
303 else if (!strcmp(sd->name, "cpu")) 479 else if (!strcmp(sd->name, "cpu"))
304 sort__first_dimension = SORT_CPU; 480 sort__first_dimension = SORT_CPU;
481 else if (!strcmp(sd->name, "symbol_from"))
482 sort__first_dimension = SORT_SYM_FROM;
483 else if (!strcmp(sd->name, "symbol_to"))
484 sort__first_dimension = SORT_SYM_TO;
485 else if (!strcmp(sd->name, "dso_from"))
486 sort__first_dimension = SORT_DSO_FROM;
487 else if (!strcmp(sd->name, "dso_to"))
488 sort__first_dimension = SORT_DSO_TO;
489 else if (!strcmp(sd->name, "mispredict"))
490 sort__first_dimension = SORT_MISPREDICT;
305 } 491 }
306 492
307 list_add_tail(&sd->entry->list, &hist_entry__sort_list); 493 list_add_tail(&sd->entry->list, &hist_entry__sort_list);
@@ -309,7 +495,6 @@ int sort_dimension__add(const char *tok)
309 495
310 return 0; 496 return 0;
311 } 497 }
312
313 return -ESRCH; 498 return -ESRCH;
314} 499}
315 500
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h
index 3f67ae395752..472aa5a63a58 100644
--- a/tools/perf/util/sort.h
+++ b/tools/perf/util/sort.h
@@ -31,11 +31,16 @@ extern const char *parent_pattern;
31extern const char default_sort_order[]; 31extern const char default_sort_order[];
32extern int sort__need_collapse; 32extern int sort__need_collapse;
33extern int sort__has_parent; 33extern int sort__has_parent;
34extern int sort__branch_mode;
34extern char *field_sep; 35extern char *field_sep;
35extern struct sort_entry sort_comm; 36extern struct sort_entry sort_comm;
36extern struct sort_entry sort_dso; 37extern struct sort_entry sort_dso;
37extern struct sort_entry sort_sym; 38extern struct sort_entry sort_sym;
38extern struct sort_entry sort_parent; 39extern struct sort_entry sort_parent;
40extern struct sort_entry sort_dso_from;
41extern struct sort_entry sort_dso_to;
42extern struct sort_entry sort_sym_from;
43extern struct sort_entry sort_sym_to;
39extern enum sort_type sort__first_dimension; 44extern enum sort_type sort__first_dimension;
40 45
41/** 46/**
@@ -72,6 +77,7 @@ struct hist_entry {
72 struct hist_entry *pair; 77 struct hist_entry *pair;
73 struct rb_root sorted_chain; 78 struct rb_root sorted_chain;
74 }; 79 };
80 struct branch_info *branch_info;
75 struct callchain_root callchain[0]; 81 struct callchain_root callchain[0];
76}; 82};
77 83
@@ -82,6 +88,11 @@ enum sort_type {
82 SORT_SYM, 88 SORT_SYM,
83 SORT_PARENT, 89 SORT_PARENT,
84 SORT_CPU, 90 SORT_CPU,
91 SORT_DSO_FROM,
92 SORT_DSO_TO,
93 SORT_SYM_FROM,
94 SORT_SYM_TO,
95 SORT_MISPREDICT,
85}; 96};
86 97
87/* 98/*
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 2a683d4fc918..ac49ef208a5f 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -5,6 +5,7 @@
5#include <stdbool.h> 5#include <stdbool.h>
6#include <stdint.h> 6#include <stdint.h>
7#include "map.h" 7#include "map.h"
8#include "../perf.h"
8#include <linux/list.h> 9#include <linux/list.h>
9#include <linux/rbtree.h> 10#include <linux/rbtree.h>
10#include <stdio.h> 11#include <stdio.h>
@@ -96,7 +97,11 @@ struct symbol_conf {
96 *col_width_list_str; 97 *col_width_list_str;
97 struct strlist *dso_list, 98 struct strlist *dso_list,
98 *comm_list, 99 *comm_list,
99 *sym_list; 100 *sym_list,
101 *dso_from_list,
102 *dso_to_list,
103 *sym_from_list,
104 *sym_to_list;
100 const char *symfs; 105 const char *symfs;
101}; 106};
102 107
@@ -120,6 +125,19 @@ struct map_symbol {
120 bool has_children; 125 bool has_children;
121}; 126};
122 127
128struct addr_map_symbol {
129 struct map *map;
130 struct symbol *sym;
131 u64 addr;
132 u64 al_addr;
133};
134
135struct branch_info {
136 struct addr_map_symbol from;
137 struct addr_map_symbol to;
138 struct branch_flags flags;
139};
140
123struct addr_location { 141struct addr_location {
124 struct thread *thread; 142 struct thread *thread;
125 struct map *map; 143 struct map *map;
diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c
index bfba0490c098..de8ece8bcce3 100644
--- a/tools/perf/util/ui/browsers/hists.c
+++ b/tools/perf/util/ui/browsers/hists.c
@@ -805,8 +805,11 @@ static struct hist_browser *hist_browser__new(struct hists *hists)
805 self->hists = hists; 805 self->hists = hists;
806 self->b.refresh = hist_browser__refresh; 806 self->b.refresh = hist_browser__refresh;
807 self->b.seek = ui_browser__hists_seek; 807 self->b.seek = ui_browser__hists_seek;
808 self->b.use_navkeypressed = true, 808 self->b.use_navkeypressed = true;
809 self->has_symbols = sort_sym.list.next != NULL; 809 if (sort__branch_mode == 1)
810 self->has_symbols = sort_sym_from.list.next != NULL;
811 else
812 self->has_symbols = sort_sym.list.next != NULL;
810 } 813 }
811 814
812 return self; 815 return self;
@@ -853,6 +856,16 @@ static int hists__browser_title(struct hists *self, char *bf, size_t size,
853 return printed; 856 return printed;
854} 857}
855 858
859static inline void free_popup_options(char **options, int n)
860{
861 int i;
862
863 for (i = 0; i < n; ++i) {
864 free(options[i]);
865 options[i] = NULL;
866 }
867}
868
856static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, 869static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
857 const char *helpline, const char *ev_name, 870 const char *helpline, const char *ev_name,
858 bool left_exits, 871 bool left_exits,
@@ -861,7 +874,10 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
861{ 874{
862 struct hists *self = &evsel->hists; 875 struct hists *self = &evsel->hists;
863 struct hist_browser *browser = hist_browser__new(self); 876 struct hist_browser *browser = hist_browser__new(self);
877 struct branch_info *bi;
864 struct pstack *fstack; 878 struct pstack *fstack;
879 char *options[16];
880 int nr_options = 0;
865 int key = -1; 881 int key = -1;
866 882
867 if (browser == NULL) 883 if (browser == NULL)
@@ -873,13 +889,16 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
873 889
874 ui_helpline__push(helpline); 890 ui_helpline__push(helpline);
875 891
892 memset(options, 0, sizeof(options));
893
876 while (1) { 894 while (1) {
877 const struct thread *thread = NULL; 895 const struct thread *thread = NULL;
878 const struct dso *dso = NULL; 896 const struct dso *dso = NULL;
879 char *options[16]; 897 int choice = 0,
880 int nr_options = 0, choice = 0, i,
881 annotate = -2, zoom_dso = -2, zoom_thread = -2, 898 annotate = -2, zoom_dso = -2, zoom_thread = -2,
882 browse_map = -2; 899 annotate_f = -2, annotate_t = -2, browse_map = -2;
900
901 nr_options = 0;
883 902
884 key = hist_browser__run(browser, ev_name, timer, arg, delay_secs); 903 key = hist_browser__run(browser, ev_name, timer, arg, delay_secs);
885 904
@@ -887,7 +906,6 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
887 thread = hist_browser__selected_thread(browser); 906 thread = hist_browser__selected_thread(browser);
888 dso = browser->selection->map ? browser->selection->map->dso : NULL; 907 dso = browser->selection->map ? browser->selection->map->dso : NULL;
889 } 908 }
890
891 switch (key) { 909 switch (key) {
892 case K_TAB: 910 case K_TAB:
893 case K_UNTAB: 911 case K_UNTAB:
@@ -902,7 +920,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
902 if (!browser->has_symbols) { 920 if (!browser->has_symbols) {
903 ui_browser__warning(&browser->b, delay_secs * 2, 921 ui_browser__warning(&browser->b, delay_secs * 2,
904 "Annotation is only available for symbolic views, " 922 "Annotation is only available for symbolic views, "
905 "include \"sym\" in --sort to use it."); 923 "include \"sym*\" in --sort to use it.");
906 continue; 924 continue;
907 } 925 }
908 926
@@ -972,12 +990,34 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
972 if (!browser->has_symbols) 990 if (!browser->has_symbols)
973 goto add_exit_option; 991 goto add_exit_option;
974 992
975 if (browser->selection != NULL && 993 if (sort__branch_mode == 1) {
976 browser->selection->sym != NULL && 994 bi = browser->he_selection->branch_info;
977 !browser->selection->map->dso->annotate_warned && 995 if (browser->selection != NULL &&
978 asprintf(&options[nr_options], "Annotate %s", 996 bi &&
979 browser->selection->sym->name) > 0) 997 bi->from.sym != NULL &&
980 annotate = nr_options++; 998 !bi->from.map->dso->annotate_warned &&
999 asprintf(&options[nr_options], "Annotate %s",
1000 bi->from.sym->name) > 0)
1001 annotate_f = nr_options++;
1002
1003 if (browser->selection != NULL &&
1004 bi &&
1005 bi->to.sym != NULL &&
1006 !bi->to.map->dso->annotate_warned &&
1007 (bi->to.sym != bi->from.sym ||
1008 bi->to.map->dso != bi->from.map->dso) &&
1009 asprintf(&options[nr_options], "Annotate %s",
1010 bi->to.sym->name) > 0)
1011 annotate_t = nr_options++;
1012 } else {
1013
1014 if (browser->selection != NULL &&
1015 browser->selection->sym != NULL &&
1016 !browser->selection->map->dso->annotate_warned &&
1017 asprintf(&options[nr_options], "Annotate %s",
1018 browser->selection->sym->name) > 0)
1019 annotate = nr_options++;
1020 }
981 1021
982 if (thread != NULL && 1022 if (thread != NULL &&
983 asprintf(&options[nr_options], "Zoom %s %s(%d) thread", 1023 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
@@ -998,25 +1038,39 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
998 browse_map = nr_options++; 1038 browse_map = nr_options++;
999add_exit_option: 1039add_exit_option:
1000 options[nr_options++] = (char *)"Exit"; 1040 options[nr_options++] = (char *)"Exit";
1001 1041retry_popup_menu:
1002 choice = ui__popup_menu(nr_options, options); 1042 choice = ui__popup_menu(nr_options, options);
1003 1043
1004 for (i = 0; i < nr_options - 1; ++i)
1005 free(options[i]);
1006
1007 if (choice == nr_options - 1) 1044 if (choice == nr_options - 1)
1008 break; 1045 break;
1009 1046
1010 if (choice == -1) 1047 if (choice == -1) {
1048 free_popup_options(options, nr_options - 1);
1011 continue; 1049 continue;
1050 }
1012 1051
1013 if (choice == annotate) { 1052 if (choice == annotate || choice == annotate_t || choice == annotate_f) {
1014 struct hist_entry *he; 1053 struct hist_entry *he;
1015 int err; 1054 int err;
1016do_annotate: 1055do_annotate:
1017 he = hist_browser__selected_entry(browser); 1056 he = hist_browser__selected_entry(browser);
1018 if (he == NULL) 1057 if (he == NULL)
1019 continue; 1058 continue;
1059
1060 /*
1061 * we stash the branch_info symbol + map into the
1062 * the ms so we don't have to rewrite all the annotation
1063 * code to use branch_info.
1064 * in branch mode, the ms struct is not used
1065 */
1066 if (choice == annotate_f) {
1067 he->ms.sym = he->branch_info->from.sym;
1068 he->ms.map = he->branch_info->from.map;
1069 } else if (choice == annotate_t) {
1070 he->ms.sym = he->branch_info->to.sym;
1071 he->ms.map = he->branch_info->to.map;
1072 }
1073
1020 /* 1074 /*
1021 * Don't let this be freed, say, by hists__decay_entry. 1075 * Don't let this be freed, say, by hists__decay_entry.
1022 */ 1076 */
@@ -1024,9 +1078,18 @@ do_annotate:
1024 err = hist_entry__tui_annotate(he, evsel->idx, 1078 err = hist_entry__tui_annotate(he, evsel->idx,
1025 timer, arg, delay_secs); 1079 timer, arg, delay_secs);
1026 he->used = false; 1080 he->used = false;
1081 /*
1082 * offer option to annotate the other branch source or target
1083 * (if they exists) when returning from annotate
1084 */
1085 if ((err == 'q' || err == CTRL('c'))
1086 && annotate_t != -2 && annotate_f != -2)
1087 goto retry_popup_menu;
1088
1027 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries); 1089 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1028 if (err) 1090 if (err)
1029 ui_browser__handle_resize(&browser->b); 1091 ui_browser__handle_resize(&browser->b);
1092
1030 } else if (choice == browse_map) 1093 } else if (choice == browse_map)
1031 map__browse(browser->selection->map); 1094 map__browse(browser->selection->map);
1032 else if (choice == zoom_dso) { 1095 else if (choice == zoom_dso) {
@@ -1072,6 +1135,7 @@ out_free_stack:
1072 pstack__delete(fstack); 1135 pstack__delete(fstack);
1073out: 1136out:
1074 hist_browser__delete(browser); 1137 hist_browser__delete(browser);
1138 free_popup_options(options, nr_options - 1);
1075 return key; 1139 return key;
1076} 1140}
1077 1141