diff options
Diffstat (limited to 'tools/perf/util/annotate.c')
-rw-r--r-- | tools/perf/util/annotate.c | 337 |
1 files changed, 212 insertions, 125 deletions
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index f91775b4bc3c..28cd6a17491b 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c | |||
@@ -49,6 +49,7 @@ struct annotation_options annotation__default_options = { | |||
49 | .jump_arrows = true, | 49 | .jump_arrows = true, |
50 | .annotate_src = true, | 50 | .annotate_src = true, |
51 | .offset_level = ANNOTATION__OFFSET_JUMP_TARGETS, | 51 | .offset_level = ANNOTATION__OFFSET_JUMP_TARGETS, |
52 | .percent_type = PERCENT_PERIOD_LOCAL, | ||
52 | }; | 53 | }; |
53 | 54 | ||
54 | static regex_t file_lineno; | 55 | static regex_t file_lineno; |
@@ -245,8 +246,14 @@ find_target: | |||
245 | 246 | ||
246 | indirect_call: | 247 | indirect_call: |
247 | tok = strchr(endptr, '*'); | 248 | tok = strchr(endptr, '*'); |
248 | if (tok != NULL) | 249 | if (tok != NULL) { |
249 | ops->target.addr = strtoull(tok + 1, NULL, 16); | 250 | endptr++; |
251 | |||
252 | /* Indirect call can use a non-rip register and offset: callq *0x8(%rbx). | ||
253 | * Do not parse such instruction. */ | ||
254 | if (strstr(endptr, "(%r") == NULL) | ||
255 | ops->target.addr = strtoull(endptr, NULL, 16); | ||
256 | } | ||
250 | goto find_target; | 257 | goto find_target; |
251 | } | 258 | } |
252 | 259 | ||
@@ -275,7 +282,19 @@ bool ins__is_call(const struct ins *ins) | |||
275 | return ins->ops == &call_ops || ins->ops == &s390_call_ops; | 282 | return ins->ops == &call_ops || ins->ops == &s390_call_ops; |
276 | } | 283 | } |
277 | 284 | ||
278 | static int jump__parse(struct arch *arch __maybe_unused, struct ins_operands *ops, struct map_symbol *ms) | 285 | /* |
286 | * Prevents from matching commas in the comment section, e.g.: | ||
287 | * ffff200008446e70: b.cs ffff2000084470f4 <generic_exec_single+0x314> // b.hs, b.nlast | ||
288 | */ | ||
289 | static inline const char *validate_comma(const char *c, struct ins_operands *ops) | ||
290 | { | ||
291 | if (ops->raw_comment && c > ops->raw_comment) | ||
292 | return NULL; | ||
293 | |||
294 | return c; | ||
295 | } | ||
296 | |||
297 | static int jump__parse(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms) | ||
279 | { | 298 | { |
280 | struct map *map = ms->map; | 299 | struct map *map = ms->map; |
281 | struct symbol *sym = ms->sym; | 300 | struct symbol *sym = ms->sym; |
@@ -284,6 +303,10 @@ static int jump__parse(struct arch *arch __maybe_unused, struct ins_operands *op | |||
284 | }; | 303 | }; |
285 | const char *c = strchr(ops->raw, ','); | 304 | const char *c = strchr(ops->raw, ','); |
286 | u64 start, end; | 305 | u64 start, end; |
306 | |||
307 | ops->raw_comment = strchr(ops->raw, arch->objdump.comment_char); | ||
308 | c = validate_comma(c, ops); | ||
309 | |||
287 | /* | 310 | /* |
288 | * Examples of lines to parse for the _cpp_lex_token@@Base | 311 | * Examples of lines to parse for the _cpp_lex_token@@Base |
289 | * function: | 312 | * function: |
@@ -303,6 +326,7 @@ static int jump__parse(struct arch *arch __maybe_unused, struct ins_operands *op | |||
303 | ops->target.addr = strtoull(c, NULL, 16); | 326 | ops->target.addr = strtoull(c, NULL, 16); |
304 | if (!ops->target.addr) { | 327 | if (!ops->target.addr) { |
305 | c = strchr(c, ','); | 328 | c = strchr(c, ','); |
329 | c = validate_comma(c, ops); | ||
306 | if (c++ != NULL) | 330 | if (c++ != NULL) |
307 | ops->target.addr = strtoull(c, NULL, 16); | 331 | ops->target.addr = strtoull(c, NULL, 16); |
308 | } | 332 | } |
@@ -360,9 +384,12 @@ static int jump__scnprintf(struct ins *ins, char *bf, size_t size, | |||
360 | return scnprintf(bf, size, "%-6s %s", ins->name, ops->target.sym->name); | 384 | return scnprintf(bf, size, "%-6s %s", ins->name, ops->target.sym->name); |
361 | 385 | ||
362 | c = strchr(ops->raw, ','); | 386 | c = strchr(ops->raw, ','); |
387 | c = validate_comma(c, ops); | ||
388 | |||
363 | if (c != NULL) { | 389 | if (c != NULL) { |
364 | const char *c2 = strchr(c + 1, ','); | 390 | const char *c2 = strchr(c + 1, ','); |
365 | 391 | ||
392 | c2 = validate_comma(c2, ops); | ||
366 | /* check for 3-op insn */ | 393 | /* check for 3-op insn */ |
367 | if (c2 != NULL) | 394 | if (c2 != NULL) |
368 | c = c2; | 395 | c = c2; |
@@ -1108,7 +1135,7 @@ annotation_line__new(struct annotate_args *args, size_t privsize) | |||
1108 | if (perf_evsel__is_group_event(evsel)) | 1135 | if (perf_evsel__is_group_event(evsel)) |
1109 | nr = evsel->nr_members; | 1136 | nr = evsel->nr_members; |
1110 | 1137 | ||
1111 | size += sizeof(al->samples[0]) * nr; | 1138 | size += sizeof(al->data[0]) * nr; |
1112 | 1139 | ||
1113 | al = zalloc(size); | 1140 | al = zalloc(size); |
1114 | if (al) { | 1141 | if (al) { |
@@ -1117,7 +1144,7 @@ annotation_line__new(struct annotate_args *args, size_t privsize) | |||
1117 | al->offset = args->offset; | 1144 | al->offset = args->offset; |
1118 | al->line = strdup(args->line); | 1145 | al->line = strdup(args->line); |
1119 | al->line_nr = args->line_nr; | 1146 | al->line_nr = args->line_nr; |
1120 | al->samples_nr = nr; | 1147 | al->data_nr = nr; |
1121 | } | 1148 | } |
1122 | 1149 | ||
1123 | return al; | 1150 | return al; |
@@ -1297,7 +1324,8 @@ static int disasm_line__print(struct disasm_line *dl, u64 start, int addr_fmt_wi | |||
1297 | static int | 1324 | static int |
1298 | annotation_line__print(struct annotation_line *al, struct symbol *sym, u64 start, | 1325 | annotation_line__print(struct annotation_line *al, struct symbol *sym, u64 start, |
1299 | struct perf_evsel *evsel, u64 len, int min_pcnt, int printed, | 1326 | struct perf_evsel *evsel, u64 len, int min_pcnt, int printed, |
1300 | int max_lines, struct annotation_line *queue, int addr_fmt_width) | 1327 | int max_lines, struct annotation_line *queue, int addr_fmt_width, |
1328 | int percent_type) | ||
1301 | { | 1329 | { |
1302 | struct disasm_line *dl = container_of(al, struct disasm_line, al); | 1330 | struct disasm_line *dl = container_of(al, struct disasm_line, al); |
1303 | static const char *prev_line; | 1331 | static const char *prev_line; |
@@ -1309,15 +1337,18 @@ annotation_line__print(struct annotation_line *al, struct symbol *sym, u64 start | |||
1309 | const char *color; | 1337 | const char *color; |
1310 | struct annotation *notes = symbol__annotation(sym); | 1338 | struct annotation *notes = symbol__annotation(sym); |
1311 | 1339 | ||
1312 | for (i = 0; i < al->samples_nr; i++) { | 1340 | for (i = 0; i < al->data_nr; i++) { |
1313 | struct annotation_data *sample = &al->samples[i]; | 1341 | double percent; |
1314 | 1342 | ||
1315 | if (sample->percent > max_percent) | 1343 | percent = annotation_data__percent(&al->data[i], |
1316 | max_percent = sample->percent; | 1344 | percent_type); |
1345 | |||
1346 | if (percent > max_percent) | ||
1347 | max_percent = percent; | ||
1317 | } | 1348 | } |
1318 | 1349 | ||
1319 | if (al->samples_nr > nr_percent) | 1350 | if (al->data_nr > nr_percent) |
1320 | nr_percent = al->samples_nr; | 1351 | nr_percent = al->data_nr; |
1321 | 1352 | ||
1322 | if (max_percent < min_pcnt) | 1353 | if (max_percent < min_pcnt) |
1323 | return -1; | 1354 | return -1; |
@@ -1330,7 +1361,8 @@ annotation_line__print(struct annotation_line *al, struct symbol *sym, u64 start | |||
1330 | if (queue == al) | 1361 | if (queue == al) |
1331 | break; | 1362 | break; |
1332 | annotation_line__print(queue, sym, start, evsel, len, | 1363 | annotation_line__print(queue, sym, start, evsel, len, |
1333 | 0, 0, 1, NULL, addr_fmt_width); | 1364 | 0, 0, 1, NULL, addr_fmt_width, |
1365 | percent_type); | ||
1334 | } | 1366 | } |
1335 | } | 1367 | } |
1336 | 1368 | ||
@@ -1351,18 +1383,20 @@ annotation_line__print(struct annotation_line *al, struct symbol *sym, u64 start | |||
1351 | } | 1383 | } |
1352 | 1384 | ||
1353 | for (i = 0; i < nr_percent; i++) { | 1385 | for (i = 0; i < nr_percent; i++) { |
1354 | struct annotation_data *sample = &al->samples[i]; | 1386 | struct annotation_data *data = &al->data[i]; |
1387 | double percent; | ||
1355 | 1388 | ||
1356 | color = get_percent_color(sample->percent); | 1389 | percent = annotation_data__percent(data, percent_type); |
1390 | color = get_percent_color(percent); | ||
1357 | 1391 | ||
1358 | if (symbol_conf.show_total_period) | 1392 | if (symbol_conf.show_total_period) |
1359 | color_fprintf(stdout, color, " %11" PRIu64, | 1393 | color_fprintf(stdout, color, " %11" PRIu64, |
1360 | sample->he.period); | 1394 | data->he.period); |
1361 | else if (symbol_conf.show_nr_samples) | 1395 | else if (symbol_conf.show_nr_samples) |
1362 | color_fprintf(stdout, color, " %7" PRIu64, | 1396 | color_fprintf(stdout, color, " %7" PRIu64, |
1363 | sample->he.nr_samples); | 1397 | data->he.nr_samples); |
1364 | else | 1398 | else |
1365 | color_fprintf(stdout, color, " %7.2f", sample->percent); | 1399 | color_fprintf(stdout, color, " %7.2f", percent); |
1366 | } | 1400 | } |
1367 | 1401 | ||
1368 | printf(" : "); | 1402 | printf(" : "); |
@@ -1621,6 +1655,7 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args) | |||
1621 | char symfs_filename[PATH_MAX]; | 1655 | char symfs_filename[PATH_MAX]; |
1622 | struct kcore_extract kce; | 1656 | struct kcore_extract kce; |
1623 | bool delete_extract = false; | 1657 | bool delete_extract = false; |
1658 | bool decomp = false; | ||
1624 | int stdout_fd[2]; | 1659 | int stdout_fd[2]; |
1625 | int lineno = 0; | 1660 | int lineno = 0; |
1626 | int nline; | 1661 | int nline; |
@@ -1654,6 +1689,7 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args) | |||
1654 | tmp, sizeof(tmp)) < 0) | 1689 | tmp, sizeof(tmp)) < 0) |
1655 | goto out; | 1690 | goto out; |
1656 | 1691 | ||
1692 | decomp = true; | ||
1657 | strcpy(symfs_filename, tmp); | 1693 | strcpy(symfs_filename, tmp); |
1658 | } | 1694 | } |
1659 | 1695 | ||
@@ -1740,7 +1776,7 @@ out_free_command: | |||
1740 | out_remove_tmp: | 1776 | out_remove_tmp: |
1741 | close(stdout_fd[0]); | 1777 | close(stdout_fd[0]); |
1742 | 1778 | ||
1743 | if (dso__needs_decompress(dso)) | 1779 | if (decomp) |
1744 | unlink(symfs_filename); | 1780 | unlink(symfs_filename); |
1745 | 1781 | ||
1746 | if (delete_extract) | 1782 | if (delete_extract) |
@@ -1753,34 +1789,45 @@ out_close_stdout: | |||
1753 | goto out_free_command; | 1789 | goto out_free_command; |
1754 | } | 1790 | } |
1755 | 1791 | ||
1756 | static void calc_percent(struct sym_hist *hist, | 1792 | static void calc_percent(struct sym_hist *sym_hist, |
1757 | struct annotation_data *sample, | 1793 | struct hists *hists, |
1794 | struct annotation_data *data, | ||
1758 | s64 offset, s64 end) | 1795 | s64 offset, s64 end) |
1759 | { | 1796 | { |
1760 | unsigned int hits = 0; | 1797 | unsigned int hits = 0; |
1761 | u64 period = 0; | 1798 | u64 period = 0; |
1762 | 1799 | ||
1763 | while (offset < end) { | 1800 | while (offset < end) { |
1764 | hits += hist->addr[offset].nr_samples; | 1801 | hits += sym_hist->addr[offset].nr_samples; |
1765 | period += hist->addr[offset].period; | 1802 | period += sym_hist->addr[offset].period; |
1766 | ++offset; | 1803 | ++offset; |
1767 | } | 1804 | } |
1768 | 1805 | ||
1769 | if (hist->nr_samples) { | 1806 | if (sym_hist->nr_samples) { |
1770 | sample->he.period = period; | 1807 | data->he.period = period; |
1771 | sample->he.nr_samples = hits; | 1808 | data->he.nr_samples = hits; |
1772 | sample->percent = 100.0 * hits / hist->nr_samples; | 1809 | data->percent[PERCENT_HITS_LOCAL] = 100.0 * hits / sym_hist->nr_samples; |
1773 | } | 1810 | } |
1811 | |||
1812 | if (hists->stats.nr_non_filtered_samples) | ||
1813 | data->percent[PERCENT_HITS_GLOBAL] = 100.0 * hits / hists->stats.nr_non_filtered_samples; | ||
1814 | |||
1815 | if (sym_hist->period) | ||
1816 | data->percent[PERCENT_PERIOD_LOCAL] = 100.0 * period / sym_hist->period; | ||
1817 | |||
1818 | if (hists->stats.total_period) | ||
1819 | data->percent[PERCENT_PERIOD_GLOBAL] = 100.0 * period / hists->stats.total_period; | ||
1774 | } | 1820 | } |
1775 | 1821 | ||
1776 | static void annotation__calc_percent(struct annotation *notes, | 1822 | static void annotation__calc_percent(struct annotation *notes, |
1777 | struct perf_evsel *evsel, s64 len) | 1823 | struct perf_evsel *leader, s64 len) |
1778 | { | 1824 | { |
1779 | struct annotation_line *al, *next; | 1825 | struct annotation_line *al, *next; |
1826 | struct perf_evsel *evsel; | ||
1780 | 1827 | ||
1781 | list_for_each_entry(al, ¬es->src->source, node) { | 1828 | list_for_each_entry(al, ¬es->src->source, node) { |
1782 | s64 end; | 1829 | s64 end; |
1783 | int i; | 1830 | int i = 0; |
1784 | 1831 | ||
1785 | if (al->offset == -1) | 1832 | if (al->offset == -1) |
1786 | continue; | 1833 | continue; |
@@ -1788,14 +1835,17 @@ static void annotation__calc_percent(struct annotation *notes, | |||
1788 | next = annotation_line__next(al, ¬es->src->source); | 1835 | next = annotation_line__next(al, ¬es->src->source); |
1789 | end = next ? next->offset : len; | 1836 | end = next ? next->offset : len; |
1790 | 1837 | ||
1791 | for (i = 0; i < al->samples_nr; i++) { | 1838 | for_each_group_evsel(evsel, leader) { |
1792 | struct annotation_data *sample; | 1839 | struct hists *hists = evsel__hists(evsel); |
1793 | struct sym_hist *hist; | 1840 | struct annotation_data *data; |
1841 | struct sym_hist *sym_hist; | ||
1794 | 1842 | ||
1795 | hist = annotation__histogram(notes, evsel->idx + i); | 1843 | BUG_ON(i >= al->data_nr); |
1796 | sample = &al->samples[i]; | ||
1797 | 1844 | ||
1798 | calc_percent(hist, sample, al->offset, end); | 1845 | sym_hist = annotation__histogram(notes, evsel->idx); |
1846 | data = &al->data[i++]; | ||
1847 | |||
1848 | calc_percent(sym_hist, hists, data, al->offset, end); | ||
1799 | } | 1849 | } |
1800 | } | 1850 | } |
1801 | } | 1851 | } |
@@ -1846,7 +1896,8 @@ int symbol__annotate(struct symbol *sym, struct map *map, | |||
1846 | return symbol__disassemble(sym, &args); | 1896 | return symbol__disassemble(sym, &args); |
1847 | } | 1897 | } |
1848 | 1898 | ||
1849 | static void insert_source_line(struct rb_root *root, struct annotation_line *al) | 1899 | static void insert_source_line(struct rb_root *root, struct annotation_line *al, |
1900 | struct annotation_options *opts) | ||
1850 | { | 1901 | { |
1851 | struct annotation_line *iter; | 1902 | struct annotation_line *iter; |
1852 | struct rb_node **p = &root->rb_node; | 1903 | struct rb_node **p = &root->rb_node; |
@@ -1859,8 +1910,10 @@ static void insert_source_line(struct rb_root *root, struct annotation_line *al) | |||
1859 | 1910 | ||
1860 | ret = strcmp(iter->path, al->path); | 1911 | ret = strcmp(iter->path, al->path); |
1861 | if (ret == 0) { | 1912 | if (ret == 0) { |
1862 | for (i = 0; i < al->samples_nr; i++) | 1913 | for (i = 0; i < al->data_nr; i++) { |
1863 | iter->samples[i].percent_sum += al->samples[i].percent; | 1914 | iter->data[i].percent_sum += annotation_data__percent(&al->data[i], |
1915 | opts->percent_type); | ||
1916 | } | ||
1864 | return; | 1917 | return; |
1865 | } | 1918 | } |
1866 | 1919 | ||
@@ -1870,8 +1923,10 @@ static void insert_source_line(struct rb_root *root, struct annotation_line *al) | |||
1870 | p = &(*p)->rb_right; | 1923 | p = &(*p)->rb_right; |
1871 | } | 1924 | } |
1872 | 1925 | ||
1873 | for (i = 0; i < al->samples_nr; i++) | 1926 | for (i = 0; i < al->data_nr; i++) { |
1874 | al->samples[i].percent_sum = al->samples[i].percent; | 1927 | al->data[i].percent_sum = annotation_data__percent(&al->data[i], |
1928 | opts->percent_type); | ||
1929 | } | ||
1875 | 1930 | ||
1876 | rb_link_node(&al->rb_node, parent, p); | 1931 | rb_link_node(&al->rb_node, parent, p); |
1877 | rb_insert_color(&al->rb_node, root); | 1932 | rb_insert_color(&al->rb_node, root); |
@@ -1881,10 +1936,10 @@ static int cmp_source_line(struct annotation_line *a, struct annotation_line *b) | |||
1881 | { | 1936 | { |
1882 | int i; | 1937 | int i; |
1883 | 1938 | ||
1884 | for (i = 0; i < a->samples_nr; i++) { | 1939 | for (i = 0; i < a->data_nr; i++) { |
1885 | if (a->samples[i].percent_sum == b->samples[i].percent_sum) | 1940 | if (a->data[i].percent_sum == b->data[i].percent_sum) |
1886 | continue; | 1941 | continue; |
1887 | return a->samples[i].percent_sum > b->samples[i].percent_sum; | 1942 | return a->data[i].percent_sum > b->data[i].percent_sum; |
1888 | } | 1943 | } |
1889 | 1944 | ||
1890 | return 0; | 1945 | return 0; |
@@ -1949,8 +2004,8 @@ static void print_summary(struct rb_root *root, const char *filename) | |||
1949 | int i; | 2004 | int i; |
1950 | 2005 | ||
1951 | al = rb_entry(node, struct annotation_line, rb_node); | 2006 | al = rb_entry(node, struct annotation_line, rb_node); |
1952 | for (i = 0; i < al->samples_nr; i++) { | 2007 | for (i = 0; i < al->data_nr; i++) { |
1953 | percent = al->samples[i].percent_sum; | 2008 | percent = al->data[i].percent_sum; |
1954 | color = get_percent_color(percent); | 2009 | color = get_percent_color(percent); |
1955 | color_fprintf(stdout, color, " %7.2f", percent); | 2010 | color_fprintf(stdout, color, " %7.2f", percent); |
1956 | 2011 | ||
@@ -2029,10 +2084,12 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, | |||
2029 | evsel_name = buf; | 2084 | evsel_name = buf; |
2030 | } | 2085 | } |
2031 | 2086 | ||
2032 | graph_dotted_len = printf(" %-*.*s| Source code & Disassembly of %s for %s (%" PRIu64 " samples)\n", | 2087 | graph_dotted_len = printf(" %-*.*s| Source code & Disassembly of %s for %s (%" PRIu64 " samples, " |
2088 | "percent: %s)\n", | ||
2033 | width, width, symbol_conf.show_total_period ? "Period" : | 2089 | width, width, symbol_conf.show_total_period ? "Period" : |
2034 | symbol_conf.show_nr_samples ? "Samples" : "Percent", | 2090 | symbol_conf.show_nr_samples ? "Samples" : "Percent", |
2035 | d_filename, evsel_name, h->nr_samples); | 2091 | d_filename, evsel_name, h->nr_samples, |
2092 | percent_type_str(opts->percent_type)); | ||
2036 | 2093 | ||
2037 | printf("%-*.*s----\n", | 2094 | printf("%-*.*s----\n", |
2038 | graph_dotted_len, graph_dotted_len, graph_dotted_line); | 2095 | graph_dotted_len, graph_dotted_len, graph_dotted_line); |
@@ -2052,7 +2109,7 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, | |||
2052 | 2109 | ||
2053 | err = annotation_line__print(pos, sym, start, evsel, len, | 2110 | err = annotation_line__print(pos, sym, start, evsel, len, |
2054 | opts->min_pcnt, printed, opts->max_lines, | 2111 | opts->min_pcnt, printed, opts->max_lines, |
2055 | queue, addr_fmt_width); | 2112 | queue, addr_fmt_width, opts->percent_type); |
2056 | 2113 | ||
2057 | switch (err) { | 2114 | switch (err) { |
2058 | case 0: | 2115 | case 0: |
@@ -2129,10 +2186,11 @@ static void FILE__write_graph(void *fp, int graph) | |||
2129 | fputs(s, fp); | 2186 | fputs(s, fp); |
2130 | } | 2187 | } |
2131 | 2188 | ||
2132 | int symbol__annotate_fprintf2(struct symbol *sym, FILE *fp) | 2189 | static int symbol__annotate_fprintf2(struct symbol *sym, FILE *fp, |
2190 | struct annotation_options *opts) | ||
2133 | { | 2191 | { |
2134 | struct annotation *notes = symbol__annotation(sym); | 2192 | struct annotation *notes = symbol__annotation(sym); |
2135 | struct annotation_write_ops ops = { | 2193 | struct annotation_write_ops wops = { |
2136 | .first_line = true, | 2194 | .first_line = true, |
2137 | .obj = fp, | 2195 | .obj = fp, |
2138 | .set_color = FILE__set_color, | 2196 | .set_color = FILE__set_color, |
@@ -2146,15 +2204,16 @@ int symbol__annotate_fprintf2(struct symbol *sym, FILE *fp) | |||
2146 | list_for_each_entry(al, ¬es->src->source, node) { | 2204 | list_for_each_entry(al, ¬es->src->source, node) { |
2147 | if (annotation_line__filter(al, notes)) | 2205 | if (annotation_line__filter(al, notes)) |
2148 | continue; | 2206 | continue; |
2149 | annotation_line__write(al, notes, &ops); | 2207 | annotation_line__write(al, notes, &wops, opts); |
2150 | fputc('\n', fp); | 2208 | fputc('\n', fp); |
2151 | ops.first_line = false; | 2209 | wops.first_line = false; |
2152 | } | 2210 | } |
2153 | 2211 | ||
2154 | return 0; | 2212 | return 0; |
2155 | } | 2213 | } |
2156 | 2214 | ||
2157 | int map_symbol__annotation_dump(struct map_symbol *ms, struct perf_evsel *evsel) | 2215 | int map_symbol__annotation_dump(struct map_symbol *ms, struct perf_evsel *evsel, |
2216 | struct annotation_options *opts) | ||
2158 | { | 2217 | { |
2159 | const char *ev_name = perf_evsel__name(evsel); | 2218 | const char *ev_name = perf_evsel__name(evsel); |
2160 | char buf[1024]; | 2219 | char buf[1024]; |
@@ -2176,7 +2235,7 @@ int map_symbol__annotation_dump(struct map_symbol *ms, struct perf_evsel *evsel) | |||
2176 | 2235 | ||
2177 | fprintf(fp, "%s() %s\nEvent: %s\n\n", | 2236 | fprintf(fp, "%s() %s\nEvent: %s\n\n", |
2178 | ms->sym->name, ms->map->dso->long_name, ev_name); | 2237 | ms->sym->name, ms->map->dso->long_name, ev_name); |
2179 | symbol__annotate_fprintf2(ms->sym, fp); | 2238 | symbol__annotate_fprintf2(ms->sym, fp, opts); |
2180 | 2239 | ||
2181 | fclose(fp); | 2240 | fclose(fp); |
2182 | err = 0; | 2241 | err = 0; |
@@ -2346,7 +2405,8 @@ void annotation__update_column_widths(struct annotation *notes) | |||
2346 | } | 2405 | } |
2347 | 2406 | ||
2348 | static void annotation__calc_lines(struct annotation *notes, struct map *map, | 2407 | static void annotation__calc_lines(struct annotation *notes, struct map *map, |
2349 | struct rb_root *root) | 2408 | struct rb_root *root, |
2409 | struct annotation_options *opts) | ||
2350 | { | 2410 | { |
2351 | struct annotation_line *al; | 2411 | struct annotation_line *al; |
2352 | struct rb_root tmp_root = RB_ROOT; | 2412 | struct rb_root tmp_root = RB_ROOT; |
@@ -2355,13 +2415,14 @@ static void annotation__calc_lines(struct annotation *notes, struct map *map, | |||
2355 | double percent_max = 0.0; | 2415 | double percent_max = 0.0; |
2356 | int i; | 2416 | int i; |
2357 | 2417 | ||
2358 | for (i = 0; i < al->samples_nr; i++) { | 2418 | for (i = 0; i < al->data_nr; i++) { |
2359 | struct annotation_data *sample; | 2419 | double percent; |
2360 | 2420 | ||
2361 | sample = &al->samples[i]; | 2421 | percent = annotation_data__percent(&al->data[i], |
2422 | opts->percent_type); | ||
2362 | 2423 | ||
2363 | if (sample->percent > percent_max) | 2424 | if (percent > percent_max) |
2364 | percent_max = sample->percent; | 2425 | percent_max = percent; |
2365 | } | 2426 | } |
2366 | 2427 | ||
2367 | if (percent_max <= 0.5) | 2428 | if (percent_max <= 0.5) |
@@ -2369,18 +2430,19 @@ static void annotation__calc_lines(struct annotation *notes, struct map *map, | |||
2369 | 2430 | ||
2370 | al->path = get_srcline(map->dso, notes->start + al->offset, NULL, | 2431 | al->path = get_srcline(map->dso, notes->start + al->offset, NULL, |
2371 | false, true, notes->start + al->offset); | 2432 | false, true, notes->start + al->offset); |
2372 | insert_source_line(&tmp_root, al); | 2433 | insert_source_line(&tmp_root, al, opts); |
2373 | } | 2434 | } |
2374 | 2435 | ||
2375 | resort_source_line(root, &tmp_root); | 2436 | resort_source_line(root, &tmp_root); |
2376 | } | 2437 | } |
2377 | 2438 | ||
2378 | static void symbol__calc_lines(struct symbol *sym, struct map *map, | 2439 | static void symbol__calc_lines(struct symbol *sym, struct map *map, |
2379 | struct rb_root *root) | 2440 | struct rb_root *root, |
2441 | struct annotation_options *opts) | ||
2380 | { | 2442 | { |
2381 | struct annotation *notes = symbol__annotation(sym); | 2443 | struct annotation *notes = symbol__annotation(sym); |
2382 | 2444 | ||
2383 | annotation__calc_lines(notes, map, root); | 2445 | annotation__calc_lines(notes, map, root, opts); |
2384 | } | 2446 | } |
2385 | 2447 | ||
2386 | int symbol__tty_annotate2(struct symbol *sym, struct map *map, | 2448 | int symbol__tty_annotate2(struct symbol *sym, struct map *map, |
@@ -2389,7 +2451,7 @@ int symbol__tty_annotate2(struct symbol *sym, struct map *map, | |||
2389 | { | 2451 | { |
2390 | struct dso *dso = map->dso; | 2452 | struct dso *dso = map->dso; |
2391 | struct rb_root source_line = RB_ROOT; | 2453 | struct rb_root source_line = RB_ROOT; |
2392 | struct annotation *notes = symbol__annotation(sym); | 2454 | struct hists *hists = evsel__hists(evsel); |
2393 | char buf[1024]; | 2455 | char buf[1024]; |
2394 | 2456 | ||
2395 | if (symbol__annotate2(sym, map, evsel, opts, NULL) < 0) | 2457 | if (symbol__annotate2(sym, map, evsel, opts, NULL) < 0) |
@@ -2397,13 +2459,14 @@ int symbol__tty_annotate2(struct symbol *sym, struct map *map, | |||
2397 | 2459 | ||
2398 | if (opts->print_lines) { | 2460 | if (opts->print_lines) { |
2399 | srcline_full_filename = opts->full_path; | 2461 | srcline_full_filename = opts->full_path; |
2400 | symbol__calc_lines(sym, map, &source_line); | 2462 | symbol__calc_lines(sym, map, &source_line, opts); |
2401 | print_summary(&source_line, dso->long_name); | 2463 | print_summary(&source_line, dso->long_name); |
2402 | } | 2464 | } |
2403 | 2465 | ||
2404 | annotation__scnprintf_samples_period(notes, buf, sizeof(buf), evsel); | 2466 | hists__scnprintf_title(hists, buf, sizeof(buf)); |
2405 | fprintf(stdout, "%s\n%s() %s\n", buf, sym->name, dso->long_name); | 2467 | fprintf(stdout, "%s, [percent: %s]\n%s() %s\n", |
2406 | symbol__annotate_fprintf2(sym, stdout); | 2468 | buf, percent_type_str(opts->percent_type), sym->name, dso->long_name); |
2469 | symbol__annotate_fprintf2(sym, stdout, opts); | ||
2407 | 2470 | ||
2408 | annotated_source__purge(symbol__annotation(sym)->src); | 2471 | annotated_source__purge(symbol__annotation(sym)->src); |
2409 | 2472 | ||
@@ -2424,7 +2487,7 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, | |||
2424 | 2487 | ||
2425 | if (opts->print_lines) { | 2488 | if (opts->print_lines) { |
2426 | srcline_full_filename = opts->full_path; | 2489 | srcline_full_filename = opts->full_path; |
2427 | symbol__calc_lines(sym, map, &source_line); | 2490 | symbol__calc_lines(sym, map, &source_line, opts); |
2428 | print_summary(&source_line, dso->long_name); | 2491 | print_summary(&source_line, dso->long_name); |
2429 | } | 2492 | } |
2430 | 2493 | ||
@@ -2441,14 +2504,21 @@ bool ui__has_annotation(void) | |||
2441 | } | 2504 | } |
2442 | 2505 | ||
2443 | 2506 | ||
2444 | double annotation_line__max_percent(struct annotation_line *al, struct annotation *notes) | 2507 | static double annotation_line__max_percent(struct annotation_line *al, |
2508 | struct annotation *notes, | ||
2509 | unsigned int percent_type) | ||
2445 | { | 2510 | { |
2446 | double percent_max = 0.0; | 2511 | double percent_max = 0.0; |
2447 | int i; | 2512 | int i; |
2448 | 2513 | ||
2449 | for (i = 0; i < notes->nr_events; i++) { | 2514 | for (i = 0; i < notes->nr_events; i++) { |
2450 | if (al->samples[i].percent > percent_max) | 2515 | double percent; |
2451 | percent_max = al->samples[i].percent; | 2516 | |
2517 | percent = annotation_data__percent(&al->data[i], | ||
2518 | percent_type); | ||
2519 | |||
2520 | if (percent > percent_max) | ||
2521 | percent_max = percent; | ||
2452 | } | 2522 | } |
2453 | 2523 | ||
2454 | return percent_max; | 2524 | return percent_max; |
@@ -2487,7 +2557,7 @@ call_like: | |||
2487 | 2557 | ||
2488 | static void __annotation_line__write(struct annotation_line *al, struct annotation *notes, | 2558 | static void __annotation_line__write(struct annotation_line *al, struct annotation *notes, |
2489 | bool first_line, bool current_entry, bool change_color, int width, | 2559 | bool first_line, bool current_entry, bool change_color, int width, |
2490 | void *obj, | 2560 | void *obj, unsigned int percent_type, |
2491 | int (*obj__set_color)(void *obj, int color), | 2561 | int (*obj__set_color)(void *obj, int color), |
2492 | void (*obj__set_percent_color)(void *obj, double percent, bool current), | 2562 | void (*obj__set_percent_color)(void *obj, double percent, bool current), |
2493 | int (*obj__set_jumps_percent_color)(void *obj, int nr, bool current), | 2563 | int (*obj__set_jumps_percent_color)(void *obj, int nr, bool current), |
@@ -2495,7 +2565,7 @@ static void __annotation_line__write(struct annotation_line *al, struct annotati | |||
2495 | void (*obj__write_graph)(void *obj, int graph)) | 2565 | void (*obj__write_graph)(void *obj, int graph)) |
2496 | 2566 | ||
2497 | { | 2567 | { |
2498 | double percent_max = annotation_line__max_percent(al, notes); | 2568 | double percent_max = annotation_line__max_percent(al, notes, percent_type); |
2499 | int pcnt_width = annotation__pcnt_width(notes), | 2569 | int pcnt_width = annotation__pcnt_width(notes), |
2500 | cycles_width = annotation__cycles_width(notes); | 2570 | cycles_width = annotation__cycles_width(notes); |
2501 | bool show_title = false; | 2571 | bool show_title = false; |
@@ -2514,15 +2584,18 @@ static void __annotation_line__write(struct annotation_line *al, struct annotati | |||
2514 | int i; | 2584 | int i; |
2515 | 2585 | ||
2516 | for (i = 0; i < notes->nr_events; i++) { | 2586 | for (i = 0; i < notes->nr_events; i++) { |
2517 | obj__set_percent_color(obj, al->samples[i].percent, current_entry); | 2587 | double percent; |
2588 | |||
2589 | percent = annotation_data__percent(&al->data[i], percent_type); | ||
2590 | |||
2591 | obj__set_percent_color(obj, percent, current_entry); | ||
2518 | if (notes->options->show_total_period) { | 2592 | if (notes->options->show_total_period) { |
2519 | obj__printf(obj, "%11" PRIu64 " ", al->samples[i].he.period); | 2593 | obj__printf(obj, "%11" PRIu64 " ", al->data[i].he.period); |
2520 | } else if (notes->options->show_nr_samples) { | 2594 | } else if (notes->options->show_nr_samples) { |
2521 | obj__printf(obj, "%6" PRIu64 " ", | 2595 | obj__printf(obj, "%6" PRIu64 " ", |
2522 | al->samples[i].he.nr_samples); | 2596 | al->data[i].he.nr_samples); |
2523 | } else { | 2597 | } else { |
2524 | obj__printf(obj, "%6.2f ", | 2598 | obj__printf(obj, "%6.2f ", percent); |
2525 | al->samples[i].percent); | ||
2526 | } | 2599 | } |
2527 | } | 2600 | } |
2528 | } else { | 2601 | } else { |
@@ -2640,13 +2713,15 @@ print_addr: | |||
2640 | } | 2713 | } |
2641 | 2714 | ||
2642 | void annotation_line__write(struct annotation_line *al, struct annotation *notes, | 2715 | void annotation_line__write(struct annotation_line *al, struct annotation *notes, |
2643 | struct annotation_write_ops *ops) | 2716 | struct annotation_write_ops *wops, |
2717 | struct annotation_options *opts) | ||
2644 | { | 2718 | { |
2645 | __annotation_line__write(al, notes, ops->first_line, ops->current_entry, | 2719 | __annotation_line__write(al, notes, wops->first_line, wops->current_entry, |
2646 | ops->change_color, ops->width, ops->obj, | 2720 | wops->change_color, wops->width, wops->obj, |
2647 | ops->set_color, ops->set_percent_color, | 2721 | opts->percent_type, |
2648 | ops->set_jumps_percent_color, ops->printf, | 2722 | wops->set_color, wops->set_percent_color, |
2649 | ops->write_graph); | 2723 | wops->set_jumps_percent_color, wops->printf, |
2724 | wops->write_graph); | ||
2650 | } | 2725 | } |
2651 | 2726 | ||
2652 | int symbol__annotate2(struct symbol *sym, struct map *map, struct perf_evsel *evsel, | 2727 | int symbol__annotate2(struct symbol *sym, struct map *map, struct perf_evsel *evsel, |
@@ -2688,46 +2763,6 @@ out_free_offsets: | |||
2688 | return -1; | 2763 | return -1; |
2689 | } | 2764 | } |
2690 | 2765 | ||
2691 | int __annotation__scnprintf_samples_period(struct annotation *notes, | ||
2692 | char *bf, size_t size, | ||
2693 | struct perf_evsel *evsel, | ||
2694 | bool show_freq) | ||
2695 | { | ||
2696 | const char *ev_name = perf_evsel__name(evsel); | ||
2697 | char buf[1024], ref[30] = " show reference callgraph, "; | ||
2698 | char sample_freq_str[64] = ""; | ||
2699 | unsigned long nr_samples = 0; | ||
2700 | int nr_members = 1; | ||
2701 | bool enable_ref = false; | ||
2702 | u64 nr_events = 0; | ||
2703 | char unit; | ||
2704 | int i; | ||
2705 | |||
2706 | if (perf_evsel__is_group_event(evsel)) { | ||
2707 | perf_evsel__group_desc(evsel, buf, sizeof(buf)); | ||
2708 | ev_name = buf; | ||
2709 | nr_members = evsel->nr_members; | ||
2710 | } | ||
2711 | |||
2712 | for (i = 0; i < nr_members; i++) { | ||
2713 | struct sym_hist *ah = annotation__histogram(notes, evsel->idx + i); | ||
2714 | |||
2715 | nr_samples += ah->nr_samples; | ||
2716 | nr_events += ah->period; | ||
2717 | } | ||
2718 | |||
2719 | if (symbol_conf.show_ref_callgraph && strstr(ev_name, "call-graph=no")) | ||
2720 | enable_ref = true; | ||
2721 | |||
2722 | if (show_freq) | ||
2723 | scnprintf(sample_freq_str, sizeof(sample_freq_str), " %d Hz,", evsel->attr.sample_freq); | ||
2724 | |||
2725 | nr_samples = convert_unit(nr_samples, &unit); | ||
2726 | return scnprintf(bf, size, "Samples: %lu%c of event%s '%s',%s%sEvent count (approx.): %" PRIu64, | ||
2727 | nr_samples, unit, evsel->nr_members > 1 ? "s" : "", | ||
2728 | ev_name, sample_freq_str, enable_ref ? ref : " ", nr_events); | ||
2729 | } | ||
2730 | |||
2731 | #define ANNOTATION__CFG(n) \ | 2766 | #define ANNOTATION__CFG(n) \ |
2732 | { .name = #n, .value = &annotation__default_options.n, } | 2767 | { .name = #n, .value = &annotation__default_options.n, } |
2733 | 2768 | ||
@@ -2792,3 +2827,55 @@ void annotation_config__init(void) | |||
2792 | annotation__default_options.show_total_period = symbol_conf.show_total_period; | 2827 | annotation__default_options.show_total_period = symbol_conf.show_total_period; |
2793 | annotation__default_options.show_nr_samples = symbol_conf.show_nr_samples; | 2828 | annotation__default_options.show_nr_samples = symbol_conf.show_nr_samples; |
2794 | } | 2829 | } |
2830 | |||
2831 | static unsigned int parse_percent_type(char *str1, char *str2) | ||
2832 | { | ||
2833 | unsigned int type = (unsigned int) -1; | ||
2834 | |||
2835 | if (!strcmp("period", str1)) { | ||
2836 | if (!strcmp("local", str2)) | ||
2837 | type = PERCENT_PERIOD_LOCAL; | ||
2838 | else if (!strcmp("global", str2)) | ||
2839 | type = PERCENT_PERIOD_GLOBAL; | ||
2840 | } | ||
2841 | |||
2842 | if (!strcmp("hits", str1)) { | ||
2843 | if (!strcmp("local", str2)) | ||
2844 | type = PERCENT_HITS_LOCAL; | ||
2845 | else if (!strcmp("global", str2)) | ||
2846 | type = PERCENT_HITS_GLOBAL; | ||
2847 | } | ||
2848 | |||
2849 | return type; | ||
2850 | } | ||
2851 | |||
2852 | int annotate_parse_percent_type(const struct option *opt, const char *_str, | ||
2853 | int unset __maybe_unused) | ||
2854 | { | ||
2855 | struct annotation_options *opts = opt->value; | ||
2856 | unsigned int type; | ||
2857 | char *str1, *str2; | ||
2858 | int err = -1; | ||
2859 | |||
2860 | str1 = strdup(_str); | ||
2861 | if (!str1) | ||
2862 | return -ENOMEM; | ||
2863 | |||
2864 | str2 = strchr(str1, '-'); | ||
2865 | if (!str2) | ||
2866 | goto out; | ||
2867 | |||
2868 | *str2++ = 0; | ||
2869 | |||
2870 | type = parse_percent_type(str1, str2); | ||
2871 | if (type == (unsigned int) -1) | ||
2872 | type = parse_percent_type(str2, str1); | ||
2873 | if (type != (unsigned int) -1) { | ||
2874 | opts->percent_type = type; | ||
2875 | err = 0; | ||
2876 | } | ||
2877 | |||
2878 | out: | ||
2879 | free(str1); | ||
2880 | return err; | ||
2881 | } | ||