summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIngo Molnar <mingo@kernel.org>2018-03-25 10:38:15 +0200
committerIngo Molnar <mingo@kernel.org>2018-03-25 10:38:15 +0200
commita0ac7b3ca941fbbf96b48df7de6542d18d8d92f3 (patch)
tree2185281981513d704dcde213b4225848ae7a8b0f
parent7054e4e0b165ba74562adef96a6b1c53fb9600a4 (diff)
parent980b68ec0694f250e967cb18c5705ef5de10fdd5 (diff)
downloadlwn-a0ac7b3ca941fbbf96b48df7de6542d18d8d92f3.tar.gz
lwn-a0ac7b3ca941fbbf96b48df7de6542d18d8d92f3.zip
Merge tag 'perf-core-for-mingo-4.17-20180323' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core
Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo: - Move non-TUI specific annotation routines out of the TUI browser so that it can be used in other UIs, and to demonstrate that introduce a 'perf annotate --stdio2' option that will apply those formatting routines to provide a non-interactive annotation mode (Arnaldo Carvalho de Melo) - Add 'P' hotkey to the annotation TUI, so dump the current annotated symbol to a file, easing report thru e-mail, by getting rid of the spaces + right hand side scrollbar chars (Arnaldo Carvalho de Melo) - Support --ignore-vmlinux to 'perf report' and 'perf annotate', that was already present in 'perf top', to use /proc/{kcore,kallsyms}, allowing to see what is in fact running (patched stuff, alternatives, ftrace, etc), not the initial state of the kernel (vmlinux) (Arnaldo Carvalho de Melo) - Support 'jump' instructions to a different function, treating them as 'call' instructions (Arnaldo Carvalho de Melo) - Fix some jump artifacts when using vmlinux + ASM functions, where the ELF symtab for instance, for entry_SYSCALL_64 includes that and what comes after the 'syscall_return_via_sysret' label, but the objdump -dS prints the jump targets + offsets using the syscall_return_via_sysret address, which was confusing 'perf annotate'. See the cset comments for further info (Arnaldo Carvalho de Melo) - Report error from dwfl_attach_state() in the unwind code (Martin Vuille) - Reference Py_None before returning it in the python extension (Petr Machata) Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r--tools/perf/Documentation/perf-annotate.txt5
-rw-r--r--tools/perf/Documentation/perf-report.txt3
-rw-r--r--tools/perf/arch/s390/annotate/instructions.c5
-rw-r--r--tools/perf/builtin-annotate.c27
-rw-r--r--tools/perf/builtin-report.c3
-rw-r--r--tools/perf/builtin-top.c2
-rw-r--r--tools/perf/ui/browser.c9
-rw-r--r--tools/perf/ui/browser.h3
-rw-r--r--tools/perf/ui/browsers/annotate.c686
-rw-r--r--tools/perf/util/annotate.c679
-rw-r--r--tools/perf/util/annotate.h102
-rw-r--r--tools/perf/util/python.c4
-rw-r--r--tools/perf/util/unwind-libdw.c3
13 files changed, 944 insertions, 587 deletions
diff --git a/tools/perf/Documentation/perf-annotate.txt b/tools/perf/Documentation/perf-annotate.txt
index 292809c3c0ca..749cc6055dac 100644
--- a/tools/perf/Documentation/perf-annotate.txt
+++ b/tools/perf/Documentation/perf-annotate.txt
@@ -55,6 +55,9 @@ OPTIONS
--vmlinux=<file>::
vmlinux pathname.
+--ignore-vmlinux::
+ Ignore vmlinux files.
+
-m::
--modules::
Load module symbols. WARNING: use only with -k and LIVE kernel.
@@ -69,6 +72,8 @@ OPTIONS
--stdio:: Use the stdio interface.
+--stdio2:: Use the stdio2 interface, non-interactive, uses the TUI formatting.
+
--stdio-color=<mode>::
'always', 'never' or 'auto', allowing configuring color output
via the command line, in addition to via "color.ui" .perfconfig.
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt
index cba16d8a970e..e1a660e60849 100644
--- a/tools/perf/Documentation/perf-report.txt
+++ b/tools/perf/Documentation/perf-report.txt
@@ -296,6 +296,9 @@ OPTIONS
--vmlinux=<file>::
vmlinux pathname
+--ignore-vmlinux::
+ Ignore vmlinux files.
+
--kallsyms=<file>::
kallsyms pathname
diff --git a/tools/perf/arch/s390/annotate/instructions.c b/tools/perf/arch/s390/annotate/instructions.c
index 46c21831f2ac..cee4e2f7c057 100644
--- a/tools/perf/arch/s390/annotate/instructions.c
+++ b/tools/perf/arch/s390/annotate/instructions.c
@@ -2,9 +2,10 @@
#include <linux/compiler.h>
static int s390_call__parse(struct arch *arch, struct ins_operands *ops,
- struct map *map)
+ struct map_symbol *ms)
{
char *endptr, *tok, *name;
+ struct map *map = ms->map;
struct addr_map_symbol target = {
.map = map,
};
@@ -54,7 +55,7 @@ static struct ins_ops s390_call_ops = {
static int s390_mov__parse(struct arch *arch __maybe_unused,
struct ins_operands *ops,
- struct map *map __maybe_unused)
+ struct map_symbol *ms __maybe_unused)
{
char *s = strchr(ops->raw, ','), *target, *endptr;
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index ead6ae4549e5..51709a961496 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -40,7 +40,7 @@
struct perf_annotate {
struct perf_tool tool;
struct perf_session *session;
- bool use_tui, use_stdio, use_gtk;
+ bool use_tui, use_stdio, use_stdio2, use_gtk;
bool full_paths;
bool print_line;
bool skip_missing;
@@ -202,6 +202,11 @@ static int process_branch_callback(struct perf_evsel *evsel,
return ret;
}
+static bool has_annotation(struct perf_annotate *ann)
+{
+ return ui__has_annotation() || ann->use_stdio2;
+}
+
static int perf_evsel__add_sample(struct perf_evsel *evsel,
struct perf_sample *sample,
struct addr_location *al,
@@ -212,7 +217,7 @@ static int perf_evsel__add_sample(struct perf_evsel *evsel,
struct hist_entry *he;
int ret;
- if ((!ann->has_br_stack || !ui__has_annotation()) &&
+ if ((!ann->has_br_stack || !has_annotation(ann)) &&
ann->sym_hist_filter != NULL &&
(al->sym == NULL ||
strcmp(ann->sym_hist_filter, al->sym->name) != 0)) {
@@ -236,7 +241,7 @@ static int perf_evsel__add_sample(struct perf_evsel *evsel,
*/
process_branch_stack(sample->branch_stack, al, sample);
- if (ann->has_br_stack && ui__has_annotation())
+ if (ann->has_br_stack && has_annotation(ann))
return process_branch_callback(evsel, sample, al, ann, machine);
he = hists__add_entry(hists, al, NULL, NULL, NULL, sample, true);
@@ -282,8 +287,11 @@ static int hist_entry__tty_annotate(struct hist_entry *he,
struct perf_evsel *evsel,
struct perf_annotate *ann)
{
- return symbol__tty_annotate(he->ms.sym, he->ms.map, evsel,
- ann->print_line, ann->full_paths, 0, 0);
+ if (!ann->use_stdio2)
+ return symbol__tty_annotate(he->ms.sym, he->ms.map, evsel,
+ ann->print_line, ann->full_paths, 0, 0);
+ return symbol__tty_annotate2(he->ms.sym, he->ms.map, evsel,
+ ann->print_line, ann->full_paths);
}
static void hists__find_annotations(struct hists *hists,
@@ -487,6 +495,9 @@ int cmd_annotate(int argc, const char **argv)
OPT_BOOLEAN(0, "gtk", &annotate.use_gtk, "Use the GTK interface"),
OPT_BOOLEAN(0, "tui", &annotate.use_tui, "Use the TUI interface"),
OPT_BOOLEAN(0, "stdio", &annotate.use_stdio, "Use the stdio interface"),
+ OPT_BOOLEAN(0, "stdio2", &annotate.use_stdio2, "Use the stdio interface"),
+ OPT_BOOLEAN(0, "ignore-vmlinux", &symbol_conf.ignore_vmlinux,
+ "don't load vmlinux even if found"),
OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
"file", "vmlinux pathname"),
OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
@@ -563,13 +574,15 @@ int cmd_annotate(int argc, const char **argv)
if (ret < 0)
goto out_delete;
+ annotation_config__init();
+
symbol_conf.try_vmlinux_path = true;
ret = symbol__init(&annotate.session->header.env);
if (ret < 0)
goto out_delete;
- if (annotate.use_stdio)
+ if (annotate.use_stdio || annotate.use_stdio2)
use_browser = 0;
else if (annotate.use_tui)
use_browser = 1;
@@ -578,7 +591,7 @@ int cmd_annotate(int argc, const char **argv)
setup_browser(true);
- if (use_browser == 1 && annotate.has_br_stack) {
+ if ((use_browser == 1 || annotate.use_stdio2) && annotate.has_br_stack) {
sort__mode = SORT_MODE__BRANCH;
if (setup_sorting(annotate.session->evlist) < 0)
usage_with_options(annotate_usage, options);
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 91da12975642..0f198f6d9b77 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -1018,6 +1018,8 @@ int cmd_report(int argc, const char **argv)
OPT_BOOLEAN(0, "mmaps", &report.mmaps_mode, "Display recorded tasks memory maps"),
OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
"file", "vmlinux pathname"),
+ OPT_BOOLEAN(0, "ignore-vmlinux", &symbol_conf.ignore_vmlinux,
+ "don't load vmlinux even if found"),
OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name,
"file", "kallsyms pathname"),
OPT_BOOLEAN('f', "force", &symbol_conf.force, "don't complain, do it"),
@@ -1340,6 +1342,7 @@ repeat:
symbol_conf.priv_size += sizeof(u32);
symbol_conf.sort_by_name = true;
}
+ annotation_config__init();
}
if (symbol__init(&session->header.env) < 0)
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index 113c298ed38b..f39bd60d2708 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -1493,6 +1493,8 @@ int cmd_top(int argc, const char **argv)
if (status < 0)
goto out_delete_evlist;
+ annotation_config__init();
+
symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL);
if (symbol__init(NULL) < 0)
return -1;
diff --git a/tools/perf/ui/browser.c b/tools/perf/ui/browser.c
index 63399af3049f..9f6ce29b83b4 100644
--- a/tools/perf/ui/browser.c
+++ b/tools/perf/ui/browser.c
@@ -56,12 +56,17 @@ void ui_browser__write_nstring(struct ui_browser *browser __maybe_unused, const
slsmg_write_nstring(msg, width);
}
+void ui_browser__vprintf(struct ui_browser *browser __maybe_unused, const char *fmt, va_list args)
+{
+ slsmg_vprintf(fmt, args);
+}
+
void ui_browser__printf(struct ui_browser *browser __maybe_unused, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
- slsmg_vprintf(fmt, args);
+ ui_browser__vprintf(browser, fmt, args);
va_end(args);
}
@@ -779,6 +784,4 @@ void ui_browser__init(void)
struct ui_browser_colorset *c = &ui_browser__colorsets[i++];
sltt_set_color(c->colorset, c->name, c->fg, c->bg);
}
-
- annotate_browser__init();
}
diff --git a/tools/perf/ui/browser.h b/tools/perf/ui/browser.h
index 03e1734412b9..70057178ee34 100644
--- a/tools/perf/ui/browser.h
+++ b/tools/perf/ui/browser.h
@@ -3,6 +3,7 @@
#define _PERF_UI_BROWSER_H_ 1
#include <linux/types.h>
+#include <stdarg.h>
#define HE_COLORSET_TOP 50
#define HE_COLORSET_MEDIUM 51
@@ -40,6 +41,7 @@ void ui_browser__reset_index(struct ui_browser *browser);
void ui_browser__gotorc(struct ui_browser *browser, int y, int x);
void ui_browser__write_nstring(struct ui_browser *browser, const char *msg,
unsigned int width);
+void ui_browser__vprintf(struct ui_browser *browser, const char *fmt, va_list args);
void ui_browser__printf(struct ui_browser *browser, const char *fmt, ...);
void ui_browser__write_graph(struct ui_browser *browser, int graph);
void __ui_browser__line_arrow(struct ui_browser *browser, unsigned int column,
@@ -77,5 +79,4 @@ void ui_browser__list_head_seek(struct ui_browser *browser, off_t offset, int wh
unsigned int ui_browser__list_head_refresh(struct ui_browser *browser);
void ui_browser__init(void);
-void annotate_browser__init(void);
#endif /* _PERF_UI_BROWSER_H_ */
diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c
index 618edf96353c..c02fb437ac8e 100644
--- a/tools/perf/ui/browsers/annotate.c
+++ b/tools/perf/ui/browsers/annotate.c
@@ -9,7 +9,6 @@
#include "../../util/sort.h"
#include "../../util/symbol.h"
#include "../../util/evsel.h"
-#include "../../util/config.h"
#include "../../util/evlist.h"
#include <inttypes.h>
#include <pthread.h>
@@ -22,28 +21,6 @@ struct disasm_line_samples {
struct sym_hist_entry he;
};
-#define IPC_WIDTH 6
-#define CYCLES_WIDTH 6
-
-struct browser_line {
- u32 idx;
- int idx_asm;
- int jump_sources;
-};
-
-static struct annotate_browser_opt {
- bool hide_src_code,
- use_offset,
- jump_arrows,
- show_linenr,
- show_nr_jumps,
- show_nr_samples,
- show_total_period;
-} annotate_browser__opts = {
- .use_offset = true,
- .jump_arrows = true,
-};
-
struct arch;
struct annotate_browser {
@@ -51,245 +28,98 @@ struct annotate_browser {
struct rb_root entries;
struct rb_node *curr_hot;
struct annotation_line *selection;
- struct annotation_line **offsets;
struct arch *arch;
- int nr_events;
- u64 start;
- int nr_asm_entries;
- int nr_entries;
- int max_jump_sources;
- int nr_jumps;
bool searching_backwards;
- bool have_cycles;
- u8 addr_width;
- u8 jumps_width;
- u8 target_width;
- u8 min_addr_width;
- u8 max_addr_width;
char search_bf[128];
};
-static inline struct browser_line *browser_line(struct annotation_line *al)
+static inline struct annotation *browser__annotation(struct ui_browser *browser)
{
- void *ptr = al;
-
- ptr = container_of(al, struct disasm_line, al);
- return ptr - sizeof(struct browser_line);
+ struct map_symbol *ms = browser->priv;
+ return symbol__annotation(ms->sym);
}
-static bool disasm_line__filter(struct ui_browser *browser __maybe_unused,
- void *entry)
+static bool disasm_line__filter(struct ui_browser *browser, void *entry)
{
- if (annotate_browser__opts.hide_src_code) {
- struct annotation_line *al = list_entry(entry, struct annotation_line, node);
-
- return al->offset == -1;
- }
-
- return false;
+ struct annotation *notes = browser__annotation(browser);
+ struct annotation_line *al = list_entry(entry, struct annotation_line, node);
+ return annotation_line__filter(al, notes);
}
-static int annotate_browser__jumps_percent_color(struct annotate_browser *browser,
- int nr, bool current)
+static int ui_browser__jumps_percent_color(struct ui_browser *browser, int nr, bool current)
{
- if (current && (!browser->b.use_navkeypressed || browser->b.navkeypressed))
+ struct annotation *notes = browser__annotation(browser);
+
+ if (current && (!browser->use_navkeypressed || browser->navkeypressed))
return HE_COLORSET_SELECTED;
- if (nr == browser->max_jump_sources)
+ if (nr == notes->max_jump_sources)
return HE_COLORSET_TOP;
if (nr > 1)
return HE_COLORSET_MEDIUM;
return HE_COLORSET_NORMAL;
}
-static int annotate_browser__set_jumps_percent_color(struct annotate_browser *browser,
- int nr, bool current)
+static int ui_browser__set_jumps_percent_color(void *browser, int nr, bool current)
{
- int color = annotate_browser__jumps_percent_color(browser, nr, current);
- return ui_browser__set_color(&browser->b, color);
+ int color = ui_browser__jumps_percent_color(browser, nr, current);
+ return ui_browser__set_color(browser, color);
}
-static int annotate_browser__pcnt_width(struct annotate_browser *ab)
+static int annotate_browser__set_color(void *browser, int color)
{
- return (annotate_browser__opts.show_total_period ? 12 : 7) * ab->nr_events;
+ return ui_browser__set_color(browser, color);
}
-static int annotate_browser__cycles_width(struct annotate_browser *ab)
+static void annotate_browser__write_graph(void *browser, int graph)
{
- return ab->have_cycles ? IPC_WIDTH + CYCLES_WIDTH : 0;
+ ui_browser__write_graph(browser, graph);
}
-static void disasm_line__write(struct disasm_line *dl, struct ui_browser *browser,
- char *bf, size_t size)
+static void annotate_browser__set_percent_color(void *browser, double percent, bool current)
{
- if (dl->ins.ops && dl->ins.ops->scnprintf) {
- if (ins__is_jump(&dl->ins)) {
- bool fwd = dl->ops.target.offset > dl->al.offset;
-
- ui_browser__write_graph(browser, fwd ? SLSMG_DARROW_CHAR :
- SLSMG_UARROW_CHAR);
- SLsmg_write_char(' ');
- } else if (ins__is_call(&dl->ins)) {
- ui_browser__write_graph(browser, SLSMG_RARROW_CHAR);
- SLsmg_write_char(' ');
- } else if (ins__is_ret(&dl->ins)) {
- ui_browser__write_graph(browser, SLSMG_LARROW_CHAR);
- SLsmg_write_char(' ');
- } else {
- ui_browser__write_nstring(browser, " ", 2);
- }
- } else {
- ui_browser__write_nstring(browser, " ", 2);
- }
+ ui_browser__set_percent_color(browser, percent, current);
+}
- disasm_line__scnprintf(dl, bf, size, !annotate_browser__opts.use_offset);
+static void annotate_browser__printf(void *browser, const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ ui_browser__vprintf(browser, fmt, args);
+ va_end(args);
}
static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
{
struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
+ struct annotation *notes = browser__annotation(browser);
struct annotation_line *al = list_entry(entry, struct annotation_line, node);
- struct browser_line *bl = browser_line(al);
- bool current_entry = ui_browser__is_current_entry(browser, row);
- bool change_color = (!annotate_browser__opts.hide_src_code &&
- (!current_entry || (browser->use_navkeypressed &&
- !browser->navkeypressed)));
- int width = browser->width, printed;
- int i, pcnt_width = annotate_browser__pcnt_width(ab),
- cycles_width = annotate_browser__cycles_width(ab);
- double percent_max = 0.0;
- char bf[256];
- bool show_title = false;
-
- for (i = 0; i < ab->nr_events; i++) {
- if (al->samples[i].percent > percent_max)
- percent_max = al->samples[i].percent;
- }
-
- if ((row == 0) && (al->offset == -1 || percent_max == 0.0)) {
- if (ab->have_cycles) {
- if (al->ipc == 0.0 && al->cycles == 0)
- show_title = true;
- } else
- show_title = true;
- }
-
- if (al->offset != -1 && percent_max != 0.0) {
- for (i = 0; i < ab->nr_events; i++) {
- ui_browser__set_percent_color(browser,
- al->samples[i].percent,
- current_entry);
- if (annotate_browser__opts.show_total_period) {
- ui_browser__printf(browser, "%11" PRIu64 " ",
- al->samples[i].he.period);
- } else if (annotate_browser__opts.show_nr_samples) {
- ui_browser__printf(browser, "%6" PRIu64 " ",
- al->samples[i].he.nr_samples);
- } else {
- ui_browser__printf(browser, "%6.2f ",
- al->samples[i].percent);
- }
- }
- } else {
- ui_browser__set_percent_color(browser, 0, current_entry);
-
- if (!show_title)
- ui_browser__write_nstring(browser, " ", pcnt_width);
- else {
- ui_browser__printf(browser, "%*s", pcnt_width,
- annotate_browser__opts.show_total_period ? "Period" :
- annotate_browser__opts.show_nr_samples ? "Samples" : "Percent");
- }
- }
- if (ab->have_cycles) {
- if (al->ipc)
- ui_browser__printf(browser, "%*.2f ", IPC_WIDTH - 1, al->ipc);
- else if (!show_title)
- ui_browser__write_nstring(browser, " ", IPC_WIDTH);
- else
- ui_browser__printf(browser, "%*s ", IPC_WIDTH - 1, "IPC");
-
- if (al->cycles)
- ui_browser__printf(browser, "%*" PRIu64 " ",
- CYCLES_WIDTH - 1, al->cycles);
- else if (!show_title)
- ui_browser__write_nstring(browser, " ", CYCLES_WIDTH);
- else
- ui_browser__printf(browser, "%*s ", CYCLES_WIDTH - 1, "Cycle");
- }
-
- SLsmg_write_char(' ');
+ struct annotation_write_ops ops = {
+ .first_line = row == 0,
+ .current_entry = ui_browser__is_current_entry(browser, row),
+ .change_color = (!notes->options->hide_src_code &&
+ (!ops.current_entry ||
+ (browser->use_navkeypressed &&
+ !browser->navkeypressed))),
+ .width = browser->width,
+ .obj = browser,
+ .set_color = annotate_browser__set_color,
+ .set_percent_color = annotate_browser__set_percent_color,
+ .set_jumps_percent_color = ui_browser__set_jumps_percent_color,
+ .printf = annotate_browser__printf,
+ .write_graph = annotate_browser__write_graph,
+ };
/* The scroll bar isn't being used */
if (!browser->navkeypressed)
- width += 1;
-
- if (!*al->line)
- ui_browser__write_nstring(browser, " ", width - pcnt_width - cycles_width);
- else if (al->offset == -1) {
- if (al->line_nr && annotate_browser__opts.show_linenr)
- printed = scnprintf(bf, sizeof(bf), "%-*d ",
- ab->addr_width + 1, al->line_nr);
- else
- printed = scnprintf(bf, sizeof(bf), "%*s ",
- ab->addr_width, " ");
- ui_browser__write_nstring(browser, bf, printed);
- ui_browser__write_nstring(browser, al->line, width - printed - pcnt_width - cycles_width + 1);
- } else {
- u64 addr = al->offset;
- int color = -1;
-
- if (!annotate_browser__opts.use_offset)
- addr += ab->start;
-
- if (!annotate_browser__opts.use_offset) {
- printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr);
- } else {
- if (bl->jump_sources) {
- if (annotate_browser__opts.show_nr_jumps) {
- int prev;
- printed = scnprintf(bf, sizeof(bf), "%*d ",
- ab->jumps_width,
- bl->jump_sources);
- prev = annotate_browser__set_jumps_percent_color(ab, bl->jump_sources,
- current_entry);
- ui_browser__write_nstring(browser, bf, printed);
- ui_browser__set_color(browser, prev);
- }
-
- printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ",
- ab->target_width, addr);
- } else {
- printed = scnprintf(bf, sizeof(bf), "%*s ",
- ab->addr_width, " ");
- }
- }
+ ops.width += 1;
- if (change_color)
- color = ui_browser__set_color(browser, HE_COLORSET_ADDR);
- ui_browser__write_nstring(browser, bf, printed);
- if (change_color)
- ui_browser__set_color(browser, color);
+ annotation_line__write(al, notes, &ops);
- disasm_line__write(disasm_line(al), browser, bf, sizeof(bf));
-
- ui_browser__write_nstring(browser, bf, width - pcnt_width - cycles_width - 3 - printed);
- }
-
- if (current_entry)
+ if (ops.current_entry)
ab->selection = al;
}
-static bool disasm_line__is_valid_jump(struct disasm_line *dl, struct symbol *sym)
-{
- if (!dl || !dl->ins.ops || !ins__is_jump(&dl->ins)
- || !disasm_line__has_offset(dl)
- || dl->ops.target.offset < 0
- || dl->ops.target.offset >= (s64)symbol__size(sym))
- return false;
-
- return true;
-}
-
static bool is_fused(struct annotate_browser *ab, struct disasm_line *cursor)
{
struct disasm_line *pos = list_prev_entry(cursor, al.node);
@@ -314,18 +144,18 @@ static void annotate_browser__draw_current_jump(struct ui_browser *browser)
struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
struct disasm_line *cursor = disasm_line(ab->selection);
struct annotation_line *target;
- struct browser_line *btarget, *bcursor;
unsigned int from, to;
struct map_symbol *ms = ab->b.priv;
struct symbol *sym = ms->sym;
- u8 pcnt_width = annotate_browser__pcnt_width(ab);
- int width = 0;
+ struct annotation *notes = symbol__annotation(sym);
+ u8 pcnt_width = annotation__pcnt_width(notes);
+ int width;
/* PLT symbols contain external offsets */
if (strstr(sym->name, "@plt"))
return;
- if (!disasm_line__is_valid_jump(cursor, sym))
+ if (!disasm_line__is_valid_local_jump(cursor, sym))
return;
/*
@@ -348,35 +178,31 @@ static void annotate_browser__draw_current_jump(struct ui_browser *browser)
* name right after the '<' token and probably treating this like a
* 'call' instruction.
*/
- target = ab->offsets[cursor->ops.target.offset];
+ target = notes->offsets[cursor->ops.target.offset];
if (target == NULL) {
- ui_helpline__printf("WARN: jump target inconsistency, press 'o', ab->offsets[%#x] = NULL\n",
+ ui_helpline__printf("WARN: jump target inconsistency, press 'o', notes->offsets[%#x] = NULL\n",
cursor->ops.target.offset);
return;
}
- bcursor = browser_line(&cursor->al);
- btarget = browser_line(target);
-
- if (annotate_browser__opts.hide_src_code) {
- from = bcursor->idx_asm;
- to = btarget->idx_asm;
+ if (notes->options->hide_src_code) {
+ from = cursor->al.idx_asm;
+ to = target->idx_asm;
} else {
- from = (u64)bcursor->idx;
- to = (u64)btarget->idx;
+ from = (u64)cursor->al.idx;
+ to = (u64)target->idx;
}
- if (ab->have_cycles)
- width = IPC_WIDTH + CYCLES_WIDTH;
+ width = annotation__cycles_width(notes);
ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS);
__ui_browser__line_arrow(browser,
- pcnt_width + 2 + ab->addr_width + width,
+ pcnt_width + 2 + notes->widths.addr + width,
from, to);
if (is_fused(ab, cursor)) {
ui_browser__mark_fused(browser,
- pcnt_width + 3 + ab->addr_width + width,
+ pcnt_width + 3 + notes->widths.addr + width,
from - 1,
to > from ? true : false);
}
@@ -384,11 +210,11 @@ static void annotate_browser__draw_current_jump(struct ui_browser *browser)
static unsigned int annotate_browser__refresh(struct ui_browser *browser)
{
- struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
+ struct annotation *notes = browser__annotation(browser);
int ret = ui_browser__list_head_refresh(browser);
- int pcnt_width = annotate_browser__pcnt_width(ab);
+ int pcnt_width = annotation__pcnt_width(notes);
- if (annotate_browser__opts.jump_arrows)
+ if (notes->options->jump_arrows)
annotate_browser__draw_current_jump(browser);
ui_browser__set_color(browser, HE_COLORSET_NORMAL);
@@ -430,6 +256,7 @@ static void disasm_rb_tree__insert(struct rb_root *root, struct annotation_line
static void annotate_browser__set_top(struct annotate_browser *browser,
struct annotation_line *pos, u32 idx)
{
+ struct annotation *notes = browser__annotation(&browser->b);
unsigned back;
ui_browser__refresh_dimensions(&browser->b);
@@ -439,7 +266,7 @@ static void annotate_browser__set_top(struct annotate_browser *browser,
while (browser->b.top_idx != 0 && back != 0) {
pos = list_entry(pos->node.prev, struct annotation_line, node);
- if (disasm_line__filter(&browser->b, &pos->node))
+ if (annotation_line__filter(pos, notes))
continue;
--browser->b.top_idx;
@@ -453,16 +280,12 @@ static void annotate_browser__set_top(struct annotate_browser *browser,
static void annotate_browser__set_rb_top(struct annotate_browser *browser,
struct rb_node *nd)
{
- struct browser_line *bpos;
- struct annotation_line *pos;
- u32 idx;
-
- pos = rb_entry(nd, struct annotation_line, rb_node);
- bpos = browser_line(pos);
+ struct annotation *notes = browser__annotation(&browser->b);
+ struct annotation_line * pos = rb_entry(nd, struct annotation_line, rb_node);
+ u32 idx = pos->idx;
- idx = bpos->idx;
- if (annotate_browser__opts.hide_src_code)
- idx = bpos->idx_asm;
+ if (notes->options->hide_src_code)
+ idx = pos->idx_asm;
annotate_browser__set_top(browser, pos, idx);
browser->curr_hot = nd;
}
@@ -510,47 +333,47 @@ static void annotate_browser__calc_percent(struct annotate_browser *browser,
static bool annotate_browser__toggle_source(struct annotate_browser *browser)
{
+ struct annotation *notes = browser__annotation(&browser->b);
struct annotation_line *al;
- struct browser_line *bl;
off_t offset = browser->b.index - browser->b.top_idx;
browser->b.seek(&browser->b, offset, SEEK_CUR);
al = list_entry(browser->b.top, struct annotation_line, node);
- bl = browser_line(al);
- if (annotate_browser__opts.hide_src_code) {
- if (bl->idx_asm < offset)
- offset = bl->idx;
+ if (notes->options->hide_src_code) {
+ if (al->idx_asm < offset)
+ offset = al->idx;
- browser->b.nr_entries = browser->nr_entries;
- annotate_browser__opts.hide_src_code = false;
+ browser->b.nr_entries = notes->nr_entries;
+ notes->options->hide_src_code = false;
browser->b.seek(&browser->b, -offset, SEEK_CUR);
- browser->b.top_idx = bl->idx - offset;
- browser->b.index = bl->idx;
+ browser->b.top_idx = al->idx - offset;
+ browser->b.index = al->idx;
} else {
- if (bl->idx_asm < 0) {
+ if (al->idx_asm < 0) {
ui_helpline__puts("Only available for assembly lines.");
browser->b.seek(&browser->b, -offset, SEEK_CUR);
return false;
}
- if (bl->idx_asm < offset)
- offset = bl->idx_asm;
+ if (al->idx_asm < offset)
+ offset = al->idx_asm;
- browser->b.nr_entries = browser->nr_asm_entries;
- annotate_browser__opts.hide_src_code = true;
+ browser->b.nr_entries = notes->nr_asm_entries;
+ notes->options->hide_src_code = true;
browser->b.seek(&browser->b, -offset, SEEK_CUR);
- browser->b.top_idx = bl->idx_asm - offset;
- browser->b.index = bl->idx_asm;
+ browser->b.top_idx = al->idx_asm - offset;
+ browser->b.index = al->idx_asm;
}
return true;
}
-static void annotate_browser__init_asm_mode(struct annotate_browser *browser)
+static void ui_browser__init_asm_mode(struct ui_browser *browser)
{
- ui_browser__reset_index(&browser->b);
- browser->b.nr_entries = browser->nr_asm_entries;
+ struct annotation *notes = browser__annotation(browser);
+ ui_browser__reset_index(browser);
+ browser->nr_entries = notes->nr_asm_entries;
}
#define SYM_TITLE_MAX_SIZE (PATH_MAX + 64)
@@ -561,6 +384,15 @@ static int sym_title(struct symbol *sym, struct map *map, char *title,
return snprintf(title, sz, "%s %s", sym->name, map->dso->long_name);
}
+/*
+ * This can be called from external jumps, i.e. jumps from one functon
+ * to another, like from the kernel's entry_SYSCALL_64 function to the
+ * swapgs_restore_regs_and_return_to_usermode() function.
+ *
+ * So all we check here is that dl->ops.target.sym is set, if it is, just
+ * go to that function and when exiting from its disassembly, come back
+ * to the calling function.
+ */
static bool annotate_browser__callq(struct annotate_browser *browser,
struct perf_evsel *evsel,
struct hist_browser_timer *hbt)
@@ -570,9 +402,6 @@ static bool annotate_browser__callq(struct annotate_browser *browser,
struct annotation *notes;
char title[SYM_TITLE_MAX_SIZE];
- if (!ins__is_call(&dl->ins))
- return false;
-
if (!dl->ops.target.sym) {
ui_helpline__puts("The called function was not found.");
return true;
@@ -599,23 +428,23 @@ static
struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
s64 offset, s64 *idx)
{
- struct map_symbol *ms = browser->b.priv;
- struct symbol *sym = ms->sym;
- struct annotation *notes = symbol__annotation(sym);
+ struct annotation *notes = browser__annotation(&browser->b);
struct disasm_line *pos;
*idx = 0;
list_for_each_entry(pos, &notes->src->source, al.node) {
if (pos->al.offset == offset)
return pos;
- if (!disasm_line__filter(&browser->b, &pos->al.node))
+ if (!annotation_line__filter(&pos->al, notes))
++*idx;
}
return NULL;
}
-static bool annotate_browser__jump(struct annotate_browser *browser)
+static bool annotate_browser__jump(struct annotate_browser *browser,
+ struct perf_evsel *evsel,
+ struct hist_browser_timer *hbt)
{
struct disasm_line *dl = disasm_line(browser->selection);
u64 offset;
@@ -624,6 +453,11 @@ static bool annotate_browser__jump(struct annotate_browser *browser)
if (!ins__is_jump(&dl->ins))
return false;
+ if (dl->ops.target.outside) {
+ annotate_browser__callq(browser, evsel, hbt);
+ return true;
+ }
+
offset = dl->ops.target.offset;
dl = annotate_browser__find_offset(browser, offset, &idx);
if (dl == NULL) {
@@ -640,14 +474,12 @@ static
struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser,
char *s, s64 *idx)
{
- struct map_symbol *ms = browser->b.priv;
- struct symbol *sym = ms->sym;
- struct annotation *notes = symbol__annotation(sym);
+ struct annotation *notes = browser__annotation(&browser->b);
struct annotation_line *al = browser->selection;
*idx = browser->b.index;
list_for_each_entry_continue(al, &notes->src->source, node) {
- if (disasm_line__filter(&browser->b, &al->node))
+ if (annotation_line__filter(al, notes))
continue;
++*idx;
@@ -679,14 +511,12 @@ static
struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
char *s, s64 *idx)
{
- struct map_symbol *ms = browser->b.priv;
- struct symbol *sym = ms->sym;
- struct annotation *notes = symbol__annotation(sym);
+ struct annotation *notes = browser__annotation(&browser->b);
struct annotation_line *al = browser->selection;
*idx = browser->b.index;
list_for_each_entry_continue_reverse(al, &notes->src->source, node) {
- if (disasm_line__filter(&browser->b, &al->node))
+ if (annotation_line__filter(al, notes))
continue;
--*idx;
@@ -762,19 +592,6 @@ bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
return __annotate_browser__search_reverse(browser);
}
-static void annotate_browser__update_addr_width(struct annotate_browser *browser)
-{
- if (annotate_browser__opts.use_offset)
- browser->target_width = browser->min_addr_width;
- else
- browser->target_width = browser->max_addr_width;
-
- browser->addr_width = browser->target_width;
-
- if (annotate_browser__opts.show_nr_jumps)
- browser->addr_width += browser->jumps_width + 1;
-}
-
static int annotate_browser__run(struct annotate_browser *browser,
struct perf_evsel *evsel,
struct hist_browser_timer *hbt)
@@ -782,6 +599,7 @@ static int annotate_browser__run(struct annotate_browser *browser,
struct rb_node *nd = NULL;
struct map_symbol *ms = browser->b.priv;
struct symbol *sym = ms->sym;
+ struct annotation *notes = symbol__annotation(ms->sym);
const char *help = "Press 'h' for help on key bindings";
int delay_secs = hbt ? hbt->refresh : 0;
int key;
@@ -856,6 +674,7 @@ static int annotate_browser__run(struct annotate_browser *browser,
"t Circulate percent, total period, samples view\n"
"/ Search string\n"
"k Toggle line numbers\n"
+ "P Print to [symbol_name].annotation file.\n"
"r Run available scripts\n"
"? Search string backwards\n");
continue;
@@ -865,8 +684,7 @@ static int annotate_browser__run(struct annotate_browser *browser,
continue;
}
case 'k':
- annotate_browser__opts.show_linenr =
- !annotate_browser__opts.show_linenr;
+ notes->options->show_linenr = !notes->options->show_linenr;
break;
case 'H':
nd = browser->curr_hot;
@@ -876,15 +694,15 @@ static int annotate_browser__run(struct annotate_browser *browser,
ui_helpline__puts(help);
continue;
case 'o':
- annotate_browser__opts.use_offset = !annotate_browser__opts.use_offset;
- annotate_browser__update_addr_width(browser);
+ notes->options->use_offset = !notes->options->use_offset;
+ annotation__update_column_widths(notes);
continue;
case 'j':
- annotate_browser__opts.jump_arrows = !annotate_browser__opts.jump_arrows;
+ notes->options->jump_arrows = !notes->options->jump_arrows;
continue;
case 'J':
- annotate_browser__opts.show_nr_jumps = !annotate_browser__opts.show_nr_jumps;
- annotate_browser__update_addr_width(browser);
+ notes->options->show_nr_jumps = !notes->options->show_nr_jumps;
+ annotation__update_column_widths(notes);
continue;
case '/':
if (annotate_browser__search(browser, delay_secs)) {
@@ -910,7 +728,7 @@ show_help:
browser->b.height,
browser->b.index,
browser->b.top_idx,
- browser->nr_asm_entries);
+ notes->nr_asm_entries);
}
continue;
case K_ENTER:
@@ -926,22 +744,25 @@ show_help:
goto show_sup_ins;
else if (ins__is_ret(&dl->ins))
goto out;
- else if (!(annotate_browser__jump(browser) ||
+ else if (!(annotate_browser__jump(browser, evsel, hbt) ||
annotate_browser__callq(browser, evsel, hbt))) {
show_sup_ins:
ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
}
continue;
}
+ case 'P':
+ map_symbol__annotation_dump(ms, evsel);
+ continue;
case 't':
- if (annotate_browser__opts.show_total_period) {
- annotate_browser__opts.show_total_period = false;
- annotate_browser__opts.show_nr_samples = true;
- } else if (annotate_browser__opts.show_nr_samples)
- annotate_browser__opts.show_nr_samples = false;
+ if (notes->options->show_total_period) {
+ notes->options->show_total_period = false;
+ notes->options->show_nr_samples = true;
+ } else if (notes->options->show_nr_samples)
+ notes->options->show_nr_samples = false;
else
- annotate_browser__opts.show_total_period = true;
- annotate_browser__update_addr_width(browser);
+ notes->options->show_total_period = true;
+ annotation__update_column_widths(notes);
continue;
case K_LEFT:
case K_ESC:
@@ -963,12 +784,6 @@ out:
int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
struct hist_browser_timer *hbt)
{
- /* Set default value for show_total_period and show_nr_samples */
- annotate_browser__opts.show_total_period =
- symbol_conf.show_total_period;
- annotate_browser__opts.show_nr_samples =
- symbol_conf.show_nr_samples;
-
return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt);
}
@@ -982,129 +797,11 @@ int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
return map_symbol__tui_annotate(&he->ms, evsel, hbt);
}
-
-static unsigned count_insn(struct annotate_browser *browser, u64 start, u64 end)
-{
- unsigned n_insn = 0;
- u64 offset;
-
- for (offset = start; offset <= end; offset++) {
- if (browser->offsets[offset])
- n_insn++;
- }
- return n_insn;
-}
-
-static void count_and_fill(struct annotate_browser *browser, u64 start, u64 end,
- struct cyc_hist *ch)
-{
- unsigned n_insn;
- u64 offset;
-
- n_insn = count_insn(browser, start, end);
- if (n_insn && ch->num && ch->cycles) {
- float ipc = n_insn / ((double)ch->cycles / (double)ch->num);
-
- /* Hide data when there are too many overlaps. */
- if (ch->reset >= 0x7fff || ch->reset >= ch->num / 2)
- return;
-
- for (offset = start; offset <= end; offset++) {
- struct annotation_line *al = browser->offsets[offset];
-
- if (al)
- al->ipc = ipc;
- }
- }
-}
-
-/*
- * This should probably be in util/annotate.c to share with the tty
- * annotate, but right now we need the per byte offsets arrays,
- * which are only here.
- */
-static void annotate__compute_ipc(struct annotate_browser *browser, size_t size,
- struct symbol *sym)
-{
- u64 offset;
- struct annotation *notes = symbol__annotation(sym);
-
- if (!notes->src || !notes->src->cycles_hist)
- return;
-
- pthread_mutex_lock(&notes->lock);
- for (offset = 0; offset < size; ++offset) {
- struct cyc_hist *ch;
-
- ch = &notes->src->cycles_hist[offset];
- if (ch && ch->cycles) {
- struct annotation_line *al;
-
- if (ch->have_start)
- count_and_fill(browser, ch->start, offset, ch);
- al = browser->offsets[offset];
- if (al && ch->num_aggr)
- al->cycles = ch->cycles_aggr / ch->num_aggr;
- browser->have_cycles = true;
- }
- }
- pthread_mutex_unlock(&notes->lock);
-}
-
-static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
- size_t size)
-{
- u64 offset;
- struct map_symbol *ms = browser->b.priv;
- struct symbol *sym = ms->sym;
-
- /* PLT symbols contain external offsets */
- if (strstr(sym->name, "@plt"))
- return;
-
- for (offset = 0; offset < size; ++offset) {
- struct annotation_line *al = browser->offsets[offset];
- struct disasm_line *dl;
- struct browser_line *blt;
-
- dl = disasm_line(al);
-
- if (!disasm_line__is_valid_jump(dl, sym))
- continue;
-
- al = browser->offsets[dl->ops.target.offset];
-
- /*
- * FIXME: Oops, no jump target? Buggy disassembler? Or do we
- * have to adjust to the previous offset?
- */
- if (al == NULL)
- continue;
-
- blt = browser_line(al);
- if (++blt->jump_sources > browser->max_jump_sources)
- browser->max_jump_sources = blt->jump_sources;
-
- ++browser->nr_jumps;
- }
-}
-
-static inline int width_jumps(int n)
-{
- if (n >= 100)
- return 5;
- if (n / 10)
- return 2;
- return 1;
-}
-
int symbol__tui_annotate(struct symbol *sym, struct map *map,
struct perf_evsel *evsel,
struct hist_browser_timer *hbt)
{
- struct annotation_line *al;
- struct annotation *notes;
- size_t size;
+ struct annotation *notes = symbol__annotation(sym);
struct map_symbol ms = {
.map = map,
.sym = sym,
@@ -1120,26 +817,14 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map,
},
};
int ret = -1, err;
- int nr_pcnt = 1;
if (sym == NULL)
return -1;
- size = symbol__size(sym);
-
if (map->dso->annotate_warned)
return -1;
- browser.offsets = zalloc(size * sizeof(struct annotation_line *));
- if (browser.offsets == NULL) {
- ui__error("Not enough memory!");
- return -1;
- }
-
- if (perf_evsel__is_group_event(evsel))
- nr_pcnt = evsel->nr_members;
-
- err = symbol__annotate(sym, map, evsel, sizeof(struct browser_line), &browser.arch);
+ err = symbol__annotate2(sym, map, evsel, &annotation__default_options, &browser.arch);
if (err) {
char msg[BUFSIZ];
symbol__strerror_disassemble(sym, map, err, msg, sizeof(msg));
@@ -1147,110 +832,21 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map,
goto out_free_offsets;
}
- symbol__calc_percent(sym, evsel);
-
ui_helpline__push("Press ESC to exit");
- notes = symbol__annotation(sym);
- browser.start = map__rip_2objdump(map, sym->start);
-
- list_for_each_entry(al, &notes->src->source, node) {
- struct browser_line *bpos;
- size_t line_len = strlen(al->line);
-
- if (browser.b.width < line_len)
- browser.b.width = line_len;
- bpos = browser_line(al);
- bpos->idx = browser.nr_entries++;
- if (al->offset != -1) {
- bpos->idx_asm = browser.nr_asm_entries++;
- /*
- * FIXME: short term bandaid to cope with assembly
- * routines that comes with labels in the same column
- * as the address in objdump, sigh.
- *
- * E.g. copy_user_generic_unrolled
- */
- if (al->offset < (s64)size)
- browser.offsets[al->offset] = al;
- } else
- bpos->idx_asm = -1;
- }
-
- annotate_browser__mark_jump_targets(&browser, size);
- annotate__compute_ipc(&browser, size, sym);
-
- browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
- browser.max_addr_width = hex_width(sym->end);
- browser.jumps_width = width_jumps(browser.max_jump_sources);
- browser.nr_events = nr_pcnt;
- browser.b.nr_entries = browser.nr_entries;
+ browser.b.width = notes->max_line_len;
+ browser.b.nr_entries = notes->nr_entries;
browser.b.entries = &notes->src->source,
browser.b.width += 18; /* Percentage */
- if (annotate_browser__opts.hide_src_code)
- annotate_browser__init_asm_mode(&browser);
-
- annotate_browser__update_addr_width(&browser);
+ if (notes->options->hide_src_code)
+ ui_browser__init_asm_mode(&browser.b);
ret = annotate_browser__run(&browser, evsel, hbt);
annotated_source__purge(notes->src);
out_free_offsets:
- free(browser.offsets);
+ zfree(&notes->offsets);
return ret;
}
-
-#define ANNOTATE_CFG(n) \
- { .name = #n, .value = &annotate_browser__opts.n, }
-
-/*
- * Keep the entries sorted, they are bsearch'ed
- */
-static struct annotate_config {
- const char *name;
- bool *value;
-} annotate__configs[] = {
- ANNOTATE_CFG(hide_src_code),
- ANNOTATE_CFG(jump_arrows),
- ANNOTATE_CFG(show_linenr),
- ANNOTATE_CFG(show_nr_jumps),
- ANNOTATE_CFG(show_nr_samples),
- ANNOTATE_CFG(show_total_period),
- ANNOTATE_CFG(use_offset),
-};
-
-#undef ANNOTATE_CFG
-
-static int annotate_config__cmp(const void *name, const void *cfgp)
-{
- const struct annotate_config *cfg = cfgp;
-
- return strcmp(name, cfg->name);
-}
-
-static int annotate__config(const char *var, const char *value,
- void *data __maybe_unused)
-{
- struct annotate_config *cfg;
- const char *name;
-
- if (!strstarts(var, "annotate."))
- return 0;
-
- name = var + 9;
- cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
- sizeof(struct annotate_config), annotate_config__cmp);
-
- if (cfg == NULL)
- ui__warning("%s variable unknown, ignoring...", var);
- else
- *cfg->value = perf_config_bool(name, value);
- return 0;
-}
-
-void annotate_browser__init(void)
-{
- perf_config(annotate__config, NULL);
-}
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index 535357c6ce02..3a428d7c59b9 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -14,6 +14,7 @@
#include "sort.h"
#include "build-id.h"
#include "color.h"
+#include "config.h"
#include "cache.h"
#include "symbol.h"
#include "debug.h"
@@ -27,8 +28,25 @@
#include <linux/bitops.h>
#include <linux/kernel.h>
+/* FIXME: For the HE_COLORSET */
+#include "ui/browser.h"
+
+/*
+ * FIXME: Using the same values as slang.h,
+ * but that header may not be available everywhere
+ */
+#define LARROW_CHAR ((unsigned char)',')
+#define RARROW_CHAR ((unsigned char)'+')
+#define DARROW_CHAR ((unsigned char)'.')
+#define UARROW_CHAR ((unsigned char)'-')
+
#include "sane_ctype.h"
+struct annotation_options annotation__default_options = {
+ .use_offset = true,
+ .jump_arrows = true,
+};
+
const char *disassembler_style;
const char *objdump_path;
static regex_t file_lineno;
@@ -184,9 +202,10 @@ bool ins__is_fused(struct arch *arch, const char *ins1, const char *ins2)
return arch->ins_is_fused(arch, ins1, ins2);
}
-static int call__parse(struct arch *arch, struct ins_operands *ops, struct map *map)
+static int call__parse(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms)
{
char *endptr, *tok, *name;
+ struct map *map = ms->map;
struct addr_map_symbol target = {
.map = map,
};
@@ -254,11 +273,26 @@ bool ins__is_call(const struct ins *ins)
return ins->ops == &call_ops || ins->ops == &s390_call_ops;
}
-static int jump__parse(struct arch *arch __maybe_unused, struct ins_operands *ops, struct map *map __maybe_unused)
+static int jump__parse(struct arch *arch __maybe_unused, struct ins_operands *ops, struct map_symbol *ms)
{
- const char *s = strchr(ops->raw, '+');
+ struct map *map = ms->map;
+ struct symbol *sym = ms->sym;
+ struct addr_map_symbol target = {
+ .map = map,
+ };
const char *c = strchr(ops->raw, ',');
-
+ u64 start, end;
+ /*
+ * Examples of lines to parse for the _cpp_lex_token@@Base
+ * function:
+ *
+ * 1159e6c: jne 115aa32 <_cpp_lex_token@@Base+0xf92>
+ * 1159e8b: jne c469be <cpp_named_operator2name@@Base+0xa72>
+ *
+ * The first is a jump to an offset inside the same function,
+ * the second is to another function, i.e. that 0xa72 is an
+ * offset in the cpp_named_operator2name@@base function.
+ */
/*
* skip over possible up to 2 operands to get to address, e.g.:
* tbnz w0, #26, ffff0000083cd190 <security_file_permission+0xd0>
@@ -274,8 +308,36 @@ static int jump__parse(struct arch *arch __maybe_unused, struct ins_operands *op
ops->target.addr = strtoull(ops->raw, NULL, 16);
}
- if (s++ != NULL) {
- ops->target.offset = strtoull(s, NULL, 16);
+ target.addr = map__objdump_2mem(map, ops->target.addr);
+ start = map->unmap_ip(map, sym->start),
+ end = map->unmap_ip(map, sym->end);
+
+ ops->target.outside = target.addr < start || target.addr > end;
+
+ /*
+ * FIXME: things like this in _cpp_lex_token (gcc's cc1 program):
+
+ cpp_named_operator2name@@Base+0xa72
+
+ * Point to a place that is after the cpp_named_operator2name
+ * boundaries, i.e. in the ELF symbol table for cc1
+ * cpp_named_operator2name is marked as being 32-bytes long, but it in
+ * fact is much larger than that, so we seem to need a symbols__find()
+ * routine that looks for >= current->start and < next_symbol->start,
+ * possibly just for C++ objects?
+ *
+ * For now lets just make some progress by marking jumps to outside the
+ * current function as call like.
+ *
+ * Actual navigation will come next, with further understanding of how
+ * the symbol searching and disassembly should be done.
+ */
+ if (map_groups__find_ams(&target) == 0 &&
+ map__rip_2objdump(target.map, map->map_ip(target.map, target.addr)) == ops->target.addr)
+ ops->target.sym = target.sym;
+
+ if (!ops->target.outside) {
+ ops->target.offset = target.addr - start;
ops->target.offset_avail = true;
} else {
ops->target.offset_avail = false;
@@ -287,11 +349,15 @@ static int jump__parse(struct arch *arch __maybe_unused, struct ins_operands *op
static int jump__scnprintf(struct ins *ins, char *bf, size_t size,
struct ins_operands *ops)
{
- const char *c = strchr(ops->raw, ',');
+ const char *c;
if (!ops->target.addr || ops->target.offset < 0)
return ins__raw_scnprintf(ins, bf, size, ops);
+ if (ops->target.outside && ops->target.sym != NULL)
+ return scnprintf(bf, size, "%-6s %s", ins->name, ops->target.sym->name);
+
+ c = strchr(ops->raw, ',');
if (c != NULL) {
const char *c2 = strchr(c + 1, ',');
@@ -347,7 +413,7 @@ static int comment__symbol(char *raw, char *comment, u64 *addrp, char **namep)
return 0;
}
-static int lock__parse(struct arch *arch, struct ins_operands *ops, struct map *map)
+static int lock__parse(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms)
{
ops->locked.ops = zalloc(sizeof(*ops->locked.ops));
if (ops->locked.ops == NULL)
@@ -362,7 +428,7 @@ static int lock__parse(struct arch *arch, struct ins_operands *ops, struct map *
goto out_free_ops;
if (ops->locked.ins.ops->parse &&
- ops->locked.ins.ops->parse(arch, ops->locked.ops, map) < 0)
+ ops->locked.ins.ops->parse(arch, ops->locked.ops, ms) < 0)
goto out_free_ops;
return 0;
@@ -405,7 +471,7 @@ static struct ins_ops lock_ops = {
.scnprintf = lock__scnprintf,
};
-static int mov__parse(struct arch *arch, struct ins_operands *ops, struct map *map __maybe_unused)
+static int mov__parse(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms __maybe_unused)
{
char *s = strchr(ops->raw, ','), *target, *comment, prev;
@@ -466,7 +532,7 @@ static struct ins_ops mov_ops = {
.scnprintf = mov__scnprintf,
};
-static int dec__parse(struct arch *arch __maybe_unused, struct ins_operands *ops, struct map *map __maybe_unused)
+static int dec__parse(struct arch *arch __maybe_unused, struct ins_operands *ops, struct map_symbol *ms __maybe_unused)
{
char *target, *comment, *s, prev;
@@ -833,6 +899,66 @@ int addr_map_symbol__account_cycles(struct addr_map_symbol *ams,
return err;
}
+static unsigned annotation__count_insn(struct annotation *notes, u64 start, u64 end)
+{
+ unsigned n_insn = 0;
+ u64 offset;
+
+ for (offset = start; offset <= end; offset++) {
+ if (notes->offsets[offset])
+ n_insn++;
+ }
+ return n_insn;
+}
+
+static void annotation__count_and_fill(struct annotation *notes, u64 start, u64 end, struct cyc_hist *ch)
+{
+ unsigned n_insn;
+ u64 offset;
+
+ n_insn = annotation__count_insn(notes, start, end);
+ if (n_insn && ch->num && ch->cycles) {
+ float ipc = n_insn / ((double)ch->cycles / (double)ch->num);
+
+ /* Hide data when there are too many overlaps. */
+ if (ch->reset >= 0x7fff || ch->reset >= ch->num / 2)
+ return;
+
+ for (offset = start; offset <= end; offset++) {
+ struct annotation_line *al = notes->offsets[offset];
+
+ if (al)
+ al->ipc = ipc;
+ }
+ }
+}
+
+void annotation__compute_ipc(struct annotation *notes, size_t size)
+{
+ u64 offset;
+
+ if (!notes->src || !notes->src->cycles_hist)
+ return;
+
+ pthread_mutex_lock(&notes->lock);
+ for (offset = 0; offset < size; ++offset) {
+ struct cyc_hist *ch;
+
+ ch = &notes->src->cycles_hist[offset];
+ if (ch && ch->cycles) {
+ struct annotation_line *al;
+
+ if (ch->have_start)
+ annotation__count_and_fill(notes, ch->start, offset, ch);
+ al = notes->offsets[offset];
+ if (al && ch->num_aggr)
+ al->cycles = ch->cycles_aggr / ch->num_aggr;
+ notes->have_cycles = true;
+ }
+ }
+ pthread_mutex_unlock(&notes->lock);
+}
+
int addr_map_symbol__inc_samples(struct addr_map_symbol *ams, struct perf_sample *sample,
int evidx)
{
@@ -845,14 +971,14 @@ int hist_entry__inc_addr_samples(struct hist_entry *he, struct perf_sample *samp
return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evidx, ip, sample);
}
-static void disasm_line__init_ins(struct disasm_line *dl, struct arch *arch, struct map *map)
+static void disasm_line__init_ins(struct disasm_line *dl, struct arch *arch, struct map_symbol *ms)
{
dl->ins.ops = ins__find(arch, dl->ins.name);
if (!dl->ins.ops)
return;
- if (dl->ins.ops->parse && dl->ins.ops->parse(arch, &dl->ops, map) < 0)
+ if (dl->ins.ops->parse && dl->ins.ops->parse(arch, &dl->ops, ms) < 0)
dl->ins.ops = NULL;
}
@@ -889,7 +1015,7 @@ out_free_name:
struct annotate_args {
size_t privsize;
struct arch *arch;
- struct map *map;
+ struct map_symbol ms;
struct perf_evsel *evsel;
s64 offset;
char *line;
@@ -971,7 +1097,7 @@ static struct disasm_line *disasm_line__new(struct annotate_args *args)
if (disasm_line__parse(dl->al.line, &dl->ins.name, &dl->ops.raw) < 0)
goto out_free_line;
- disasm_line__init_ins(dl, args->arch, args->map);
+ disasm_line__init_ins(dl, args->arch, &args->ms);
}
}
@@ -1229,7 +1355,7 @@ static int symbol__parse_objdump_line(struct symbol *sym, FILE *file,
struct annotate_args *args,
int *line_nr)
{
- struct map *map = args->map;
+ struct map *map = args->ms.map;
struct annotation *notes = symbol__annotation(sym);
struct disasm_line *dl;
char *line = NULL, *parsed_line, *tmp, *tmp2;
@@ -1276,6 +1402,7 @@ static int symbol__parse_objdump_line(struct symbol *sym, FILE *file,
args->offset = offset;
args->line = parsed_line;
args->line_nr = *line_nr;
+ args->ms.sym = sym;
dl = disasm_line__new(args);
free(line);
@@ -1284,7 +1411,7 @@ static int symbol__parse_objdump_line(struct symbol *sym, FILE *file,
if (dl == NULL)
return -1;
- if (!disasm_line__has_offset(dl)) {
+ if (!disasm_line__has_local_offset(dl)) {
dl->ops.target.offset = dl->ops.target.addr -
map__rip_2objdump(map, sym->start);
dl->ops.target.offset_avail = true;
@@ -1428,7 +1555,7 @@ fallback:
static int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
{
- struct map *map = args->map;
+ struct map *map = args->ms.map;
struct dso *dso = map->dso;
char *command;
FILE *file;
@@ -1627,7 +1754,6 @@ int symbol__annotate(struct symbol *sym, struct map *map,
{
struct annotate_args args = {
.privsize = privsize,
- .map = map,
.evsel = evsel,
};
struct perf_env *env = perf_evsel__env(evsel);
@@ -1653,6 +1779,9 @@ int symbol__annotate(struct symbol *sym, struct map *map,
}
}
+ args.ms.map = map;
+ args.ms.sym = sym;
+
return symbol__disassemble(sym, &args);
}
@@ -1893,6 +2022,103 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map,
return more;
}
+static void FILE__set_percent_color(void *fp __maybe_unused,
+ double percent __maybe_unused,
+ bool current __maybe_unused)
+{
+}
+
+static int FILE__set_jumps_percent_color(void *fp __maybe_unused,
+ int nr __maybe_unused, bool current __maybe_unused)
+{
+ return 0;
+}
+
+static int FILE__set_color(void *fp __maybe_unused, int color __maybe_unused)
+{
+ return 0;
+}
+
+static void FILE__printf(void *fp, const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ vfprintf(fp, fmt, args);
+ va_end(args);
+}
+
+static void FILE__write_graph(void *fp, int graph)
+{
+ const char *s;
+ switch (graph) {
+
+ case DARROW_CHAR: s = "↓"; break;
+ case UARROW_CHAR: s = "↑"; break;
+ case LARROW_CHAR: s = "←"; break;
+ case RARROW_CHAR: s = "→"; break;
+ default: s = "?"; break;
+ }
+
+ fputs(s, fp);
+}
+
+int symbol__annotate_fprintf2(struct symbol *sym, FILE *fp)
+{
+ struct annotation *notes = symbol__annotation(sym);
+ struct annotation_write_ops ops = {
+ .first_line = true,
+ .obj = fp,
+ .set_color = FILE__set_color,
+ .set_percent_color = FILE__set_percent_color,
+ .set_jumps_percent_color = FILE__set_jumps_percent_color,
+ .printf = FILE__printf,
+ .write_graph = FILE__write_graph,
+ };
+ struct annotation_line *al;
+
+ list_for_each_entry(al, &notes->src->source, node) {
+ if (annotation_line__filter(al, notes))
+ continue;
+ annotation_line__write(al, notes, &ops);
+ fputc('\n', fp);
+ ops.first_line = false;
+ }
+
+ return 0;
+}
+
+int map_symbol__annotation_dump(struct map_symbol *ms, struct perf_evsel *evsel)
+{
+ const char *ev_name = perf_evsel__name(evsel);
+ char buf[1024];
+ char *filename;
+ int err = -1;
+ FILE *fp;
+
+ if (asprintf(&filename, "%s.annotation", ms->sym->name) < 0)
+ return -1;
+
+ fp = fopen(filename, "w");
+ if (fp == NULL)
+ goto out_free_filename;
+
+ if (perf_evsel__is_group_event(evsel)) {
+ perf_evsel__group_desc(evsel, buf, sizeof(buf));
+ ev_name = buf;
+ }
+
+ fprintf(fp, "%s() %s\nEvent: %s\n\n",
+ ms->sym->name, ms->map->dso->long_name, ev_name);
+ symbol__annotate_fprintf2(ms->sym, fp);
+
+ fclose(fp);
+ err = 0;
+out_free_filename:
+ free(filename);
+ return err;
+}
+
void symbol__annotate_zero_histogram(struct symbol *sym, int evidx)
{
struct annotation *notes = symbol__annotation(sym);
@@ -1952,8 +2178,109 @@ size_t disasm__fprintf(struct list_head *head, FILE *fp)
return printed;
}
+bool disasm_line__is_valid_local_jump(struct disasm_line *dl, struct symbol *sym)
+{
+ if (!dl || !dl->ins.ops || !ins__is_jump(&dl->ins) ||
+ !disasm_line__has_local_offset(dl) || dl->ops.target.offset < 0 ||
+ dl->ops.target.offset >= (s64)symbol__size(sym))
+ return false;
+
+ return true;
+}
+
+void annotation__mark_jump_targets(struct annotation *notes, struct symbol *sym)
+{
+ u64 offset, size = symbol__size(sym);
+
+ /* PLT symbols contain external offsets */
+ if (strstr(sym->name, "@plt"))
+ return;
+
+ for (offset = 0; offset < size; ++offset) {
+ struct annotation_line *al = notes->offsets[offset];
+ struct disasm_line *dl;
+
+ dl = disasm_line(al);
+
+ if (!disasm_line__is_valid_local_jump(dl, sym))
+ continue;
+
+ al = notes->offsets[dl->ops.target.offset];
+
+ /*
+ * FIXME: Oops, no jump target? Buggy disassembler? Or do we
+ * have to adjust to the previous offset?
+ */
+ if (al == NULL)
+ continue;
+
+ if (++al->jump_sources > notes->max_jump_sources)
+ notes->max_jump_sources = al->jump_sources;
+
+ ++notes->nr_jumps;
+ }
+}
+
+void annotation__set_offsets(struct annotation *notes, s64 size)
+{
+ struct annotation_line *al;
+
+ notes->max_line_len = 0;
+
+ list_for_each_entry(al, &notes->src->source, node) {
+ size_t line_len = strlen(al->line);
+
+ if (notes->max_line_len < line_len)
+ notes->max_line_len = line_len;
+ al->idx = notes->nr_entries++;
+ if (al->offset != -1) {
+ al->idx_asm = notes->nr_asm_entries++;
+ /*
+ * FIXME: short term bandaid to cope with assembly
+ * routines that comes with labels in the same column
+ * as the address in objdump, sigh.
+ *
+ * E.g. copy_user_generic_unrolled
+ */
+ if (al->offset < size)
+ notes->offsets[al->offset] = al;
+ } else
+ al->idx_asm = -1;
+ }
+}
+
+static inline int width_jumps(int n)
+{
+ if (n >= 100)
+ return 5;
+ if (n / 10)
+ return 2;
+ return 1;
+}
+
+void annotation__init_column_widths(struct annotation *notes, struct symbol *sym)
+{
+ notes->widths.addr = notes->widths.target =
+ notes->widths.min_addr = hex_width(symbol__size(sym));
+ notes->widths.max_addr = hex_width(sym->end);
+ notes->widths.jumps = width_jumps(notes->max_jump_sources);
+}
+
+void annotation__update_column_widths(struct annotation *notes)
+{
+ if (notes->options->use_offset)
+ notes->widths.target = notes->widths.min_addr;
+ else
+ notes->widths.target = notes->widths.max_addr;
+
+ notes->widths.addr = notes->widths.target;
+
+ if (notes->options->show_nr_jumps)
+ notes->widths.addr += notes->widths.jumps + 1;
+}
+
static void annotation__calc_lines(struct annotation *notes, struct map *map,
- struct rb_root *root, u64 start)
+ struct rb_root *root)
{
struct annotation_line *al;
struct rb_root tmp_root = RB_ROOT;
@@ -1974,8 +2301,8 @@ static void annotation__calc_lines(struct annotation *notes, struct map *map,
if (percent_max <= 0.5)
continue;
- al->path = get_srcline(map->dso, start + al->offset, NULL,
- false, true, start + al->offset);
+ al->path = get_srcline(map->dso, notes->start + al->offset, NULL,
+ false, true, notes->start + al->offset);
insert_source_line(&tmp_root, al);
}
@@ -1986,9 +2313,40 @@ static void symbol__calc_lines(struct symbol *sym, struct map *map,
struct rb_root *root)
{
struct annotation *notes = symbol__annotation(sym);
- u64 start = map__rip_2objdump(map, sym->start);
- annotation__calc_lines(notes, map, root, start);
+ annotation__calc_lines(notes, map, root);
+}
+
+int symbol__tty_annotate2(struct symbol *sym, struct map *map,
+ struct perf_evsel *evsel, bool print_lines,
+ bool full_paths)
+{
+ struct dso *dso = map->dso;
+ struct rb_root source_line = RB_ROOT;
+ struct annotation_options opts = annotation__default_options;
+ const char *ev_name = perf_evsel__name(evsel);
+ char buf[1024];
+
+ if (symbol__annotate2(sym, map, evsel, &opts, NULL) < 0)
+ return -1;
+
+ if (print_lines) {
+ srcline_full_filename = full_paths;
+ symbol__calc_lines(sym, map, &source_line);
+ print_summary(&source_line, dso->long_name);
+ }
+
+ if (perf_evsel__is_group_event(evsel)) {
+ perf_evsel__group_desc(evsel, buf, sizeof(buf));
+ ev_name = buf;
+ }
+
+ fprintf(stdout, "%s() %s\nEvent: %s\n\n", sym->name, dso->long_name, ev_name);
+ symbol__annotate_fprintf2(sym, stdout);
+
+ annotated_source__purge(symbol__annotation(sym)->src);
+
+ return 0;
}
int symbol__tty_annotate(struct symbol *sym, struct map *map,
@@ -2021,3 +2379,276 @@ bool ui__has_annotation(void)
{
return use_browser == 1 && perf_hpp_list.sym;
}
+
+
+double annotation_line__max_percent(struct annotation_line *al, struct annotation *notes)
+{
+ double percent_max = 0.0;
+ int i;
+
+ for (i = 0; i < notes->nr_events; i++) {
+ if (al->samples[i].percent > percent_max)
+ percent_max = al->samples[i].percent;
+ }
+
+ return percent_max;
+}
+
+static void disasm_line__write(struct disasm_line *dl, struct annotation *notes,
+ void *obj, char *bf, size_t size,
+ void (*obj__printf)(void *obj, const char *fmt, ...),
+ void (*obj__write_graph)(void *obj, int graph))
+{
+ if (dl->ins.ops && dl->ins.ops->scnprintf) {
+ if (ins__is_jump(&dl->ins)) {
+ bool fwd;
+
+ if (dl->ops.target.outside)
+ goto call_like;
+ fwd = dl->ops.target.offset > dl->al.offset;
+ obj__write_graph(obj, fwd ? DARROW_CHAR : UARROW_CHAR);
+ obj__printf(obj, " ");
+ } else if (ins__is_call(&dl->ins)) {
+call_like:
+ obj__write_graph(obj, RARROW_CHAR);
+ obj__printf(obj, " ");
+ } else if (ins__is_ret(&dl->ins)) {
+ obj__write_graph(obj, LARROW_CHAR);
+ obj__printf(obj, " ");
+ } else {
+ obj__printf(obj, " ");
+ }
+ } else {
+ obj__printf(obj, " ");
+ }
+
+ disasm_line__scnprintf(dl, bf, size, !notes->options->use_offset);
+}
+
+static void __annotation_line__write(struct annotation_line *al, struct annotation *notes,
+ bool first_line, bool current_entry, bool change_color, int width,
+ void *obj,
+ int (*obj__set_color)(void *obj, int color),
+ void (*obj__set_percent_color)(void *obj, double percent, bool current),
+ int (*obj__set_jumps_percent_color)(void *obj, int nr, bool current),
+ void (*obj__printf)(void *obj, const char *fmt, ...),
+ void (*obj__write_graph)(void *obj, int graph))
+
+{
+ double percent_max = annotation_line__max_percent(al, notes);
+ int pcnt_width = annotation__pcnt_width(notes),
+ cycles_width = annotation__cycles_width(notes);
+ bool show_title = false;
+ char bf[256];
+ int printed;
+
+ if (first_line && (al->offset == -1 || percent_max == 0.0)) {
+ if (notes->have_cycles) {
+ if (al->ipc == 0.0 && al->cycles == 0)
+ show_title = true;
+ } else
+ show_title = true;
+ }
+
+ if (al->offset != -1 && percent_max != 0.0) {
+ int i;
+
+ for (i = 0; i < notes->nr_events; i++) {
+ obj__set_percent_color(obj, al->samples[i].percent, current_entry);
+ if (notes->options->show_total_period) {
+ obj__printf(obj, "%11" PRIu64 " ", al->samples[i].he.period);
+ } else if (notes->options->show_nr_samples) {
+ obj__printf(obj, "%6" PRIu64 " ",
+ al->samples[i].he.nr_samples);
+ } else {
+ obj__printf(obj, "%6.2f ",
+ al->samples[i].percent);
+ }
+ }
+ } else {
+ obj__set_percent_color(obj, 0, current_entry);
+
+ if (!show_title)
+ obj__printf(obj, "%-*s", pcnt_width, " ");
+ else {
+ obj__printf(obj, "%-*s", pcnt_width,
+ notes->options->show_total_period ? "Period" :
+ notes->options->show_nr_samples ? "Samples" : "Percent");
+ }
+ }
+
+ if (notes->have_cycles) {
+ if (al->ipc)
+ obj__printf(obj, "%*.2f ", ANNOTATION__IPC_WIDTH - 1, al->ipc);
+ else if (!show_title)
+ obj__printf(obj, "%*s", ANNOTATION__IPC_WIDTH, " ");
+ else
+ obj__printf(obj, "%*s ", ANNOTATION__IPC_WIDTH - 1, "IPC");
+
+ if (al->cycles)
+ obj__printf(obj, "%*" PRIu64 " ",
+ ANNOTATION__CYCLES_WIDTH - 1, al->cycles);
+ else if (!show_title)
+ obj__printf(obj, "%*s", ANNOTATION__CYCLES_WIDTH, " ");
+ else
+ obj__printf(obj, "%*s ", ANNOTATION__CYCLES_WIDTH - 1, "Cycle");
+ }
+
+ obj__printf(obj, " ");
+
+ if (!*al->line)
+ obj__printf(obj, "%-*s", width - pcnt_width - cycles_width, " ");
+ else if (al->offset == -1) {
+ if (al->line_nr && notes->options->show_linenr)
+ printed = scnprintf(bf, sizeof(bf), "%-*d ", notes->widths.addr + 1, al->line_nr);
+ else
+ printed = scnprintf(bf, sizeof(bf), "%-*s ", notes->widths.addr, " ");
+ obj__printf(obj, bf);
+ obj__printf(obj, "%-*s", width - printed - pcnt_width - cycles_width + 1, al->line);
+ } else {
+ u64 addr = al->offset;
+ int color = -1;
+
+ if (!notes->options->use_offset)
+ addr += notes->start;
+
+ if (!notes->options->use_offset) {
+ printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr);
+ } else {
+ if (al->jump_sources) {
+ if (notes->options->show_nr_jumps) {
+ int prev;
+ printed = scnprintf(bf, sizeof(bf), "%*d ",
+ notes->widths.jumps,
+ al->jump_sources);
+ prev = obj__set_jumps_percent_color(obj, al->jump_sources,
+ current_entry);
+ obj__printf(obj, bf);
+ obj__set_color(obj, prev);
+ }
+
+ printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ",
+ notes->widths.target, addr);
+ } else {
+ printed = scnprintf(bf, sizeof(bf), "%-*s ",
+ notes->widths.addr, " ");
+ }
+ }
+
+ if (change_color)
+ color = obj__set_color(obj, HE_COLORSET_ADDR);
+ obj__printf(obj, bf);
+ if (change_color)
+ obj__set_color(obj, color);
+
+ disasm_line__write(disasm_line(al), notes, obj, bf, sizeof(bf), obj__printf, obj__write_graph);
+
+ obj__printf(obj, "%-*s", width - pcnt_width - cycles_width - 3 - printed, bf);
+ }
+
+}
+
+void annotation_line__write(struct annotation_line *al, struct annotation *notes,
+ struct annotation_write_ops *ops)
+{
+ __annotation_line__write(al, notes, ops->first_line, ops->current_entry,
+ ops->change_color, ops->width, ops->obj,
+ ops->set_color, ops->set_percent_color,
+ ops->set_jumps_percent_color, ops->printf,
+ ops->write_graph);
+}
+
+int symbol__annotate2(struct symbol *sym, struct map *map, struct perf_evsel *evsel,
+ struct annotation_options *options, struct arch **parch)
+{
+ struct annotation *notes = symbol__annotation(sym);
+ size_t size = symbol__size(sym);
+ int nr_pcnt = 1, err;
+
+ notes->offsets = zalloc(size * sizeof(struct annotation_line *));
+ if (notes->offsets == NULL)
+ return -1;
+
+ if (perf_evsel__is_group_event(evsel))
+ nr_pcnt = evsel->nr_members;
+
+ err = symbol__annotate(sym, map, evsel, 0, parch);
+ if (err)
+ goto out_free_offsets;
+
+ notes->options = options;
+
+ symbol__calc_percent(sym, evsel);
+
+ notes->start = map__rip_2objdump(map, sym->start);
+
+ annotation__set_offsets(notes, size);
+ annotation__mark_jump_targets(notes, sym);
+ annotation__compute_ipc(notes, size);
+ annotation__init_column_widths(notes, sym);
+ notes->nr_events = nr_pcnt;
+
+ annotation__update_column_widths(notes);
+
+ return 0;
+
+out_free_offsets:
+ zfree(&notes->offsets);
+ return -1;
+}
+
+#define ANNOTATION__CFG(n) \
+ { .name = #n, .value = &annotation__default_options.n, }
+
+/*
+ * Keep the entries sorted, they are bsearch'ed
+ */
+static struct annotation_config {
+ const char *name;
+ bool *value;
+} annotation__configs[] = {
+ ANNOTATION__CFG(hide_src_code),
+ ANNOTATION__CFG(jump_arrows),
+ ANNOTATION__CFG(show_linenr),
+ ANNOTATION__CFG(show_nr_jumps),
+ ANNOTATION__CFG(show_nr_samples),
+ ANNOTATION__CFG(show_total_period),
+ ANNOTATION__CFG(use_offset),
+};
+
+#undef ANNOTATION__CFG
+
+static int annotation_config__cmp(const void *name, const void *cfgp)
+{
+ const struct annotation_config *cfg = cfgp;
+
+ return strcmp(name, cfg->name);
+}
+
+static int annotation__config(const char *var, const char *value,
+ void *data __maybe_unused)
+{
+ struct annotation_config *cfg;
+ const char *name;
+
+ if (!strstarts(var, "annotate."))
+ return 0;
+
+ name = var + 9;
+ cfg = bsearch(name, annotation__configs, ARRAY_SIZE(annotation__configs),
+ sizeof(struct annotation_config), annotation_config__cmp);
+
+ if (cfg == NULL)
+ pr_debug("%s variable unknown, ignoring...", var);
+ else
+ *cfg->value = perf_config_bool(name, value);
+ return 0;
+}
+
+void annotation_config__init(void)
+{
+ perf_config(annotation__config, NULL);
+
+ annotation__default_options.show_total_period = symbol_conf.show_total_period;
+ annotation__default_options.show_nr_samples = symbol_conf.show_nr_samples;
+}
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h
index 7e914e834101..ff7e3df31efa 100644
--- a/tools/perf/util/annotate.h
+++ b/tools/perf/util/annotate.h
@@ -28,6 +28,7 @@ struct ins_operands {
u64 addr;
s64 offset;
bool offset_avail;
+ bool outside;
} target;
union {
struct {
@@ -46,7 +47,7 @@ struct arch;
struct ins_ops {
void (*free)(struct ins_operands *ops);
- int (*parse)(struct arch *arch, struct ins_operands *ops, struct map *map);
+ int (*parse)(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms);
int (*scnprintf)(struct ins *ins, char *bf, size_t size,
struct ins_operands *ops);
};
@@ -58,6 +59,21 @@ bool ins__is_lock(const struct ins *ins);
int ins__scnprintf(struct ins *ins, char *bf, size_t size, struct ins_operands *ops);
bool ins__is_fused(struct arch *arch, const char *ins1, const char *ins2);
+#define ANNOTATION__IPC_WIDTH 6
+#define ANNOTATION__CYCLES_WIDTH 6
+
+struct annotation_options {
+ bool hide_src_code,
+ use_offset,
+ jump_arrows,
+ show_linenr,
+ show_nr_jumps,
+ show_nr_samples,
+ show_total_period;
+};
+
+extern struct annotation_options annotation__default_options;
+
struct annotation;
struct sym_hist_entry {
@@ -77,10 +93,13 @@ struct annotation_line {
s64 offset;
char *line;
int line_nr;
+ int jump_sources;
float ipc;
u64 cycles;
size_t privsize;
char *path;
+ u32 idx;
+ int idx_asm;
int samples_nr;
struct annotation_data samples[0];
};
@@ -98,14 +117,40 @@ static inline struct disasm_line *disasm_line(struct annotation_line *al)
return al ? container_of(al, struct disasm_line, al) : NULL;
}
-static inline bool disasm_line__has_offset(const struct disasm_line *dl)
+/*
+ * Is this offset in the same function as the line it is used?
+ * asm functions jump to other functions, for instance.
+ */
+static inline bool disasm_line__has_local_offset(const struct disasm_line *dl)
{
- return dl->ops.target.offset_avail;
+ return dl->ops.target.offset_avail && !dl->ops.target.outside;
}
+/*
+ * Can we draw an arrow from the jump to its target, for instance? I.e.
+ * is the jump and its target in the same function?
+ */
+bool disasm_line__is_valid_local_jump(struct disasm_line *dl, struct symbol *sym);
+
void disasm_line__free(struct disasm_line *dl);
struct annotation_line *
annotation_line__next(struct annotation_line *pos, struct list_head *head);
+
+struct annotation_write_ops {
+ bool first_line, current_entry, change_color;
+ int width;
+ void *obj;
+ int (*set_color)(void *obj, int color);
+ void (*set_percent_color)(void *obj, double percent, bool current);
+ int (*set_jumps_percent_color)(void *obj, int nr, bool current);
+ void (*printf)(void *obj, const char *fmt, ...);
+ void (*write_graph)(void *obj, int graph);
+};
+
+double annotation_line__max_percent(struct annotation_line *al, struct annotation *notes);
+void annotation_line__write(struct annotation_line *al, struct annotation *notes,
+ struct annotation_write_ops *ops);
+
int disasm_line__scnprintf(struct disasm_line *dl, char *bf, size_t size, bool raw);
size_t disasm__fprintf(struct list_head *head, FILE *fp);
void symbol__calc_percent(struct symbol *sym, struct perf_evsel *evsel);
@@ -151,9 +196,47 @@ struct annotated_source {
struct annotation {
pthread_mutex_t lock;
u64 max_coverage;
+ u64 start;
+ struct annotation_options *options;
+ struct annotation_line **offsets;
+ int nr_events;
+ int nr_jumps;
+ int max_jump_sources;
+ int nr_entries;
+ int nr_asm_entries;
+ u16 max_line_len;
+ struct {
+ u8 addr;
+ u8 jumps;
+ u8 target;
+ u8 min_addr;
+ u8 max_addr;
+ } widths;
+ bool have_cycles;
struct annotated_source *src;
};
+static inline int annotation__cycles_width(struct annotation *notes)
+{
+ return notes->have_cycles ? ANNOTATION__IPC_WIDTH + ANNOTATION__CYCLES_WIDTH : 0;
+}
+
+static inline int annotation__pcnt_width(struct annotation *notes)
+{
+ return (notes->options->show_total_period ? 12 : 7) * notes->nr_events;
+}
+
+static inline bool annotation_line__filter(struct annotation_line *al, struct annotation *notes)
+{
+ return notes->options->hide_src_code && al->offset == -1;
+}
+
+void annotation__set_offsets(struct annotation *notes, s64 size);
+void annotation__compute_ipc(struct annotation *notes, size_t size);
+void annotation__mark_jump_targets(struct annotation *notes, struct symbol *sym);
+void annotation__update_column_widths(struct annotation *notes);
+void annotation__init_column_widths(struct annotation *notes, struct symbol *sym);
+
static inline struct sym_hist *annotation__histogram(struct annotation *notes, int idx)
{
return (((void *)&notes->src->histograms) +
@@ -181,6 +264,10 @@ void symbol__annotate_zero_histograms(struct symbol *sym);
int symbol__annotate(struct symbol *sym, struct map *map,
struct perf_evsel *evsel, size_t privsize,
struct arch **parch);
+int symbol__annotate2(struct symbol *sym, struct map *map,
+ struct perf_evsel *evsel,
+ struct annotation_options *options,
+ struct arch **parch);
enum symbol_disassemble_errno {
SYMBOL_ANNOTATE_ERRNO__SUCCESS = 0,
@@ -205,16 +292,23 @@ int symbol__strerror_disassemble(struct symbol *sym, struct map *map,
int symbol__annotate_printf(struct symbol *sym, struct map *map,
struct perf_evsel *evsel, bool full_paths,
int min_pcnt, int max_lines, int context);
+int symbol__annotate_fprintf2(struct symbol *sym, FILE *fp);
void symbol__annotate_zero_histogram(struct symbol *sym, int evidx);
void symbol__annotate_decay_histogram(struct symbol *sym, int evidx);
void annotated_source__purge(struct annotated_source *as);
+int map_symbol__annotation_dump(struct map_symbol *ms, struct perf_evsel *evsel);
+
bool ui__has_annotation(void);
int symbol__tty_annotate(struct symbol *sym, struct map *map,
struct perf_evsel *evsel, bool print_lines,
bool full_paths, int min_pcnt, int max_lines);
+int symbol__tty_annotate2(struct symbol *sym, struct map *map,
+ struct perf_evsel *evsel, bool print_lines,
+ bool full_paths);
+
#ifdef HAVE_SLANG_SUPPORT
int symbol__tui_annotate(struct symbol *sym, struct map *map,
struct perf_evsel *evsel,
@@ -232,4 +326,6 @@ static inline int symbol__tui_annotate(struct symbol *sym __maybe_unused,
extern const char *disassembler_style;
+void annotation_config__init(void);
+
#endif /* __PERF_ANNOTATE_H */
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c
index b956868fd445..863b61478edd 100644
--- a/tools/perf/util/python.c
+++ b/tools/perf/util/python.c
@@ -1004,8 +1004,10 @@ static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist,
return PyErr_NoMemory();
evsel = perf_evlist__event2evsel(evlist, event);
- if (!evsel)
+ if (!evsel) {
+ Py_INCREF(Py_None);
return Py_None;
+ }
pevent->evsel = evsel;
diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c
index 8e969f28cc59..7bdd239c795c 100644
--- a/tools/perf/util/unwind-libdw.c
+++ b/tools/perf/util/unwind-libdw.c
@@ -236,7 +236,8 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
if (err)
goto out;
- if (!dwfl_attach_state(ui->dwfl, EM_NONE, thread->tid, &callbacks, ui))
+ err = !dwfl_attach_state(ui->dwfl, EM_NONE, thread->tid, &callbacks, ui);
+ if (err)
goto out;
err = dwfl_getthread_frames(ui->dwfl, thread->tid, frame_callback, ui);