Commit a0ac7b3c authored by Ingo Molnar's avatar Ingo Molnar

Merge tag 'perf-core-for-mingo-4.17-20180323' of...

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: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents 7054e4e0 980b68ec
...@@ -55,6 +55,9 @@ OPTIONS ...@@ -55,6 +55,9 @@ OPTIONS
--vmlinux=<file>:: --vmlinux=<file>::
vmlinux pathname. vmlinux pathname.
--ignore-vmlinux::
Ignore vmlinux files.
-m:: -m::
--modules:: --modules::
Load module symbols. WARNING: use only with -k and LIVE kernel. Load module symbols. WARNING: use only with -k and LIVE kernel.
...@@ -69,6 +72,8 @@ OPTIONS ...@@ -69,6 +72,8 @@ OPTIONS
--stdio:: Use the stdio interface. --stdio:: Use the stdio interface.
--stdio2:: Use the stdio2 interface, non-interactive, uses the TUI formatting.
--stdio-color=<mode>:: --stdio-color=<mode>::
'always', 'never' or 'auto', allowing configuring color output 'always', 'never' or 'auto', allowing configuring color output
via the command line, in addition to via "color.ui" .perfconfig. via the command line, in addition to via "color.ui" .perfconfig.
......
...@@ -296,6 +296,9 @@ OPTIONS ...@@ -296,6 +296,9 @@ OPTIONS
--vmlinux=<file>:: --vmlinux=<file>::
vmlinux pathname vmlinux pathname
--ignore-vmlinux::
Ignore vmlinux files.
--kallsyms=<file>:: --kallsyms=<file>::
kallsyms pathname kallsyms pathname
......
...@@ -2,9 +2,10 @@ ...@@ -2,9 +2,10 @@
#include <linux/compiler.h> #include <linux/compiler.h>
static int s390_call__parse(struct arch *arch, struct ins_operands *ops, static int s390_call__parse(struct arch *arch, struct ins_operands *ops,
struct map *map) struct map_symbol *ms)
{ {
char *endptr, *tok, *name; char *endptr, *tok, *name;
struct map *map = ms->map;
struct addr_map_symbol target = { struct addr_map_symbol target = {
.map = map, .map = map,
}; };
...@@ -54,7 +55,7 @@ static struct ins_ops s390_call_ops = { ...@@ -54,7 +55,7 @@ static struct ins_ops s390_call_ops = {
static int s390_mov__parse(struct arch *arch __maybe_unused, static int s390_mov__parse(struct arch *arch __maybe_unused,
struct ins_operands *ops, struct ins_operands *ops,
struct map *map __maybe_unused) struct map_symbol *ms __maybe_unused)
{ {
char *s = strchr(ops->raw, ','), *target, *endptr; char *s = strchr(ops->raw, ','), *target, *endptr;
......
...@@ -40,7 +40,7 @@ ...@@ -40,7 +40,7 @@
struct perf_annotate { struct perf_annotate {
struct perf_tool tool; struct perf_tool tool;
struct perf_session *session; struct perf_session *session;
bool use_tui, use_stdio, use_gtk; bool use_tui, use_stdio, use_stdio2, use_gtk;
bool full_paths; bool full_paths;
bool print_line; bool print_line;
bool skip_missing; bool skip_missing;
...@@ -202,6 +202,11 @@ static int process_branch_callback(struct perf_evsel *evsel, ...@@ -202,6 +202,11 @@ static int process_branch_callback(struct perf_evsel *evsel,
return ret; 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, static int perf_evsel__add_sample(struct perf_evsel *evsel,
struct perf_sample *sample, struct perf_sample *sample,
struct addr_location *al, struct addr_location *al,
...@@ -212,7 +217,7 @@ static int perf_evsel__add_sample(struct perf_evsel *evsel, ...@@ -212,7 +217,7 @@ static int perf_evsel__add_sample(struct perf_evsel *evsel,
struct hist_entry *he; struct hist_entry *he;
int ret; int ret;
if ((!ann->has_br_stack || !ui__has_annotation()) && if ((!ann->has_br_stack || !has_annotation(ann)) &&
ann->sym_hist_filter != NULL && ann->sym_hist_filter != NULL &&
(al->sym == NULL || (al->sym == NULL ||
strcmp(ann->sym_hist_filter, al->sym->name) != 0)) { strcmp(ann->sym_hist_filter, al->sym->name) != 0)) {
...@@ -236,7 +241,7 @@ static int perf_evsel__add_sample(struct perf_evsel *evsel, ...@@ -236,7 +241,7 @@ static int perf_evsel__add_sample(struct perf_evsel *evsel,
*/ */
process_branch_stack(sample->branch_stack, al, sample); 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); return process_branch_callback(evsel, sample, al, ann, machine);
he = hists__add_entry(hists, al, NULL, NULL, NULL, sample, true); 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, ...@@ -282,8 +287,11 @@ static int hist_entry__tty_annotate(struct hist_entry *he,
struct perf_evsel *evsel, struct perf_evsel *evsel,
struct perf_annotate *ann) struct perf_annotate *ann)
{ {
if (!ann->use_stdio2)
return symbol__tty_annotate(he->ms.sym, he->ms.map, evsel, return symbol__tty_annotate(he->ms.sym, he->ms.map, evsel,
ann->print_line, ann->full_paths, 0, 0); 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, static void hists__find_annotations(struct hists *hists,
...@@ -487,6 +495,9 @@ int cmd_annotate(int argc, const char **argv) ...@@ -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, "gtk", &annotate.use_gtk, "Use the GTK interface"),
OPT_BOOLEAN(0, "tui", &annotate.use_tui, "Use the TUI 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, "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, OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
"file", "vmlinux pathname"), "file", "vmlinux pathname"),
OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules, OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
...@@ -563,13 +574,15 @@ int cmd_annotate(int argc, const char **argv) ...@@ -563,13 +574,15 @@ int cmd_annotate(int argc, const char **argv)
if (ret < 0) if (ret < 0)
goto out_delete; goto out_delete;
annotation_config__init();
symbol_conf.try_vmlinux_path = true; symbol_conf.try_vmlinux_path = true;
ret = symbol__init(&annotate.session->header.env); ret = symbol__init(&annotate.session->header.env);
if (ret < 0) if (ret < 0)
goto out_delete; goto out_delete;
if (annotate.use_stdio) if (annotate.use_stdio || annotate.use_stdio2)
use_browser = 0; use_browser = 0;
else if (annotate.use_tui) else if (annotate.use_tui)
use_browser = 1; use_browser = 1;
...@@ -578,7 +591,7 @@ int cmd_annotate(int argc, const char **argv) ...@@ -578,7 +591,7 @@ int cmd_annotate(int argc, const char **argv)
setup_browser(true); 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; sort__mode = SORT_MODE__BRANCH;
if (setup_sorting(annotate.session->evlist) < 0) if (setup_sorting(annotate.session->evlist) < 0)
usage_with_options(annotate_usage, options); usage_with_options(annotate_usage, options);
......
...@@ -1018,6 +1018,8 @@ int cmd_report(int argc, const char **argv) ...@@ -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_BOOLEAN(0, "mmaps", &report.mmaps_mode, "Display recorded tasks memory maps"),
OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
"file", "vmlinux pathname"), "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, OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name,
"file", "kallsyms pathname"), "file", "kallsyms pathname"),
OPT_BOOLEAN('f', "force", &symbol_conf.force, "don't complain, do it"), OPT_BOOLEAN('f', "force", &symbol_conf.force, "don't complain, do it"),
...@@ -1340,6 +1342,7 @@ int cmd_report(int argc, const char **argv) ...@@ -1340,6 +1342,7 @@ int cmd_report(int argc, const char **argv)
symbol_conf.priv_size += sizeof(u32); symbol_conf.priv_size += sizeof(u32);
symbol_conf.sort_by_name = true; symbol_conf.sort_by_name = true;
} }
annotation_config__init();
} }
if (symbol__init(&session->header.env) < 0) if (symbol__init(&session->header.env) < 0)
......
...@@ -1493,6 +1493,8 @@ int cmd_top(int argc, const char **argv) ...@@ -1493,6 +1493,8 @@ int cmd_top(int argc, const char **argv)
if (status < 0) if (status < 0)
goto out_delete_evlist; goto out_delete_evlist;
annotation_config__init();
symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL);
if (symbol__init(NULL) < 0) if (symbol__init(NULL) < 0)
return -1; return -1;
......
...@@ -56,12 +56,17 @@ void ui_browser__write_nstring(struct ui_browser *browser __maybe_unused, const ...@@ -56,12 +56,17 @@ void ui_browser__write_nstring(struct ui_browser *browser __maybe_unused, const
slsmg_write_nstring(msg, width); 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, ...) void ui_browser__printf(struct ui_browser *browser __maybe_unused, const char *fmt, ...)
{ {
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
slsmg_vprintf(fmt, args); ui_browser__vprintf(browser, fmt, args);
va_end(args); va_end(args);
} }
...@@ -779,6 +784,4 @@ void ui_browser__init(void) ...@@ -779,6 +784,4 @@ void ui_browser__init(void)
struct ui_browser_colorset *c = &ui_browser__colorsets[i++]; struct ui_browser_colorset *c = &ui_browser__colorsets[i++];
sltt_set_color(c->colorset, c->name, c->fg, c->bg); sltt_set_color(c->colorset, c->name, c->fg, c->bg);
} }
annotate_browser__init();
} }
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
#define _PERF_UI_BROWSER_H_ 1 #define _PERF_UI_BROWSER_H_ 1
#include <linux/types.h> #include <linux/types.h>
#include <stdarg.h>
#define HE_COLORSET_TOP 50 #define HE_COLORSET_TOP 50
#define HE_COLORSET_MEDIUM 51 #define HE_COLORSET_MEDIUM 51
...@@ -40,6 +41,7 @@ void ui_browser__reset_index(struct ui_browser *browser); ...@@ -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__gotorc(struct ui_browser *browser, int y, int x);
void ui_browser__write_nstring(struct ui_browser *browser, const char *msg, void ui_browser__write_nstring(struct ui_browser *browser, const char *msg,
unsigned int width); 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__printf(struct ui_browser *browser, const char *fmt, ...);
void ui_browser__write_graph(struct ui_browser *browser, int graph); void ui_browser__write_graph(struct ui_browser *browser, int graph);
void __ui_browser__line_arrow(struct ui_browser *browser, unsigned int column, 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 ...@@ -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); unsigned int ui_browser__list_head_refresh(struct ui_browser *browser);
void ui_browser__init(void); void ui_browser__init(void);
void annotate_browser__init(void);
#endif /* _PERF_UI_BROWSER_H_ */ #endif /* _PERF_UI_BROWSER_H_ */
...@@ -9,7 +9,6 @@ ...@@ -9,7 +9,6 @@
#include "../../util/sort.h" #include "../../util/sort.h"
#include "../../util/symbol.h" #include "../../util/symbol.h"
#include "../../util/evsel.h" #include "../../util/evsel.h"
#include "../../util/config.h"
#include "../../util/evlist.h" #include "../../util/evlist.h"
#include <inttypes.h> #include <inttypes.h>
#include <pthread.h> #include <pthread.h>
...@@ -22,28 +21,6 @@ struct disasm_line_samples { ...@@ -22,28 +21,6 @@ struct disasm_line_samples {
struct sym_hist_entry he; 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 arch;
struct annotate_browser { struct annotate_browser {
...@@ -51,245 +28,98 @@ struct annotate_browser { ...@@ -51,245 +28,98 @@ struct annotate_browser {
struct rb_root entries; struct rb_root entries;
struct rb_node *curr_hot; struct rb_node *curr_hot;
struct annotation_line *selection; struct annotation_line *selection;
struct annotation_line **offsets;
struct arch *arch; 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 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]; 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; struct map_symbol *ms = browser->priv;
return symbol__annotation(ms->sym);
ptr = container_of(al, struct disasm_line, al);
return ptr - sizeof(struct browser_line);
} }
static bool disasm_line__filter(struct ui_browser *browser __maybe_unused, static bool disasm_line__filter(struct ui_browser *browser, void *entry)
void *entry)
{ {
if (annotate_browser__opts.hide_src_code) { struct annotation *notes = browser__annotation(browser);
struct annotation_line *al = list_entry(entry, struct annotation_line, node); struct annotation_line *al = list_entry(entry, struct annotation_line, node);
return annotation_line__filter(al, notes);
return al->offset == -1;
}
return false;
} }
static int annotate_browser__jumps_percent_color(struct annotate_browser *browser, static int ui_browser__jumps_percent_color(struct ui_browser *browser, int nr, bool current)
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; return HE_COLORSET_SELECTED;
if (nr == browser->max_jump_sources) if (nr == notes->max_jump_sources)
return HE_COLORSET_TOP; return HE_COLORSET_TOP;
if (nr > 1) if (nr > 1)
return HE_COLORSET_MEDIUM; return HE_COLORSET_MEDIUM;
return HE_COLORSET_NORMAL; return HE_COLORSET_NORMAL;
} }
static int annotate_browser__set_jumps_percent_color(struct annotate_browser *browser, static int ui_browser__set_jumps_percent_color(void *browser, int nr, bool current)
int nr, bool current)
{ {
int color = annotate_browser__jumps_percent_color(browser, nr, current); int color = ui_browser__jumps_percent_color(browser, nr, current);
return ui_browser__set_color(&browser->b, color); 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, static void annotate_browser__set_percent_color(void *browser, double percent, bool current)
char *bf, size_t size)
{ {
if (dl->ins.ops && dl->ins.ops->scnprintf) { ui_browser__set_percent_color(browser, percent, current);
if (ins__is_jump(&dl->ins)) { }
bool fwd = dl->ops.target.offset > dl->al.offset;
static void annotate_browser__printf(void *browser, const char *fmt, ...)
ui_browser__write_graph(browser, fwd ? SLSMG_DARROW_CHAR : {
SLSMG_UARROW_CHAR); va_list args;
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);
}
disasm_line__scnprintf(dl, bf, size, !annotate_browser__opts.use_offset); 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) 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 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 annotation_line *al = list_entry(entry, struct annotation_line, node);
struct browser_line *bl = browser_line(al); struct annotation_write_ops ops = {
bool current_entry = ui_browser__is_current_entry(browser, row); .first_line = row == 0,
bool change_color = (!annotate_browser__opts.hide_src_code && .current_entry = ui_browser__is_current_entry(browser, row),
(!current_entry || (browser->use_navkeypressed && .change_color = (!notes->options->hide_src_code &&
!browser->navkeypressed))); (!ops.current_entry ||
int width = browser->width, printed; (browser->use_navkeypressed &&
int i, pcnt_width = annotate_browser__pcnt_width(ab), !browser->navkeypressed))),
cycles_width = annotate_browser__cycles_width(ab); .width = browser->width,
double percent_max = 0.0; .obj = browser,
char bf[256]; .set_color = annotate_browser__set_color,
bool show_title = false; .set_percent_color = annotate_browser__set_percent_color,
.set_jumps_percent_color = ui_browser__set_jumps_percent_color,
for (i = 0; i < ab->nr_events; i++) { .printf = annotate_browser__printf,
if (al->samples[i].percent > percent_max) .write_graph = annotate_browser__write_graph,
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(' ');
/* The scroll bar isn't being used */ /* The scroll bar isn't being used */
if (!browser->navkeypressed) if (!browser->navkeypressed)
width += 1; ops.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, " ");
}
}
if (change_color) annotation_line__write(al, notes, &ops);
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);
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; 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) static bool is_fused(struct annotate_browser *ab, struct disasm_line *cursor)
{ {
struct disasm_line *pos = list_prev_entry(cursor, al.node); 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) ...@@ -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 annotate_browser *ab = container_of(browser, struct annotate_browser, b);
struct disasm_line *cursor = disasm_line(ab->selection); struct disasm_line *cursor = disasm_line(ab->selection);
struct annotation_line *target; struct annotation_line *target;
struct browser_line *btarget, *bcursor;
unsigned int from, to; unsigned int from, to;
struct map_symbol *ms = ab->b.priv; struct map_symbol *ms = ab->b.priv;
struct symbol *sym = ms->sym; struct symbol *sym = ms->sym;
u8 pcnt_width = annotate_browser__pcnt_width(ab); struct annotation *notes = symbol__annotation(sym);
int width = 0; u8 pcnt_width = annotation__pcnt_width(notes);
int width;
/* PLT symbols contain external offsets */ /* PLT symbols contain external offsets */
if (strstr(sym->name, "@plt")) if (strstr(sym->name, "@plt"))
return; return;
if (!disasm_line__is_valid_jump(cursor, sym)) if (!disasm_line__is_valid_local_jump(cursor, sym))
return; return;
/* /*
...@@ -348,35 +178,31 @@ static void annotate_browser__draw_current_jump(struct ui_browser *browser) ...@@ -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 * name right after the '<' token and probably treating this like a
* 'call' instruction. * 'call' instruction.
*/ */
target = ab->offsets[cursor->ops.target.offset]; target = notes->offsets[cursor->ops.target.offset];
if (target == NULL) { 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); cursor->ops.target.offset);
return; return;
} }
bcursor = browser_line(&cursor->al); if (notes->options->hide_src_code) {
btarget = browser_line(target); from = cursor->al.idx_asm;
to = target->idx_asm;
if (annotate_browser__opts.hide_src_code) {
from = bcursor->idx_asm;
to = btarget->idx_asm;
} else { } else {
from = (u64)bcursor->idx; from = (u64)cursor->al.idx;
to = (u64)btarget->idx; to = (u64)target->idx;
} }
if (ab->have_cycles) width = annotation__cycles_width(notes);
width = IPC_WIDTH + CYCLES_WIDTH;
ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS); ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS);
__ui_browser__line_arrow(browser, __ui_browser__line_arrow(browser,
pcnt_width + 2 + ab->addr_width + width, pcnt_width + 2 + notes->widths.addr + width,
from, to); from, to);
if (is_fused(ab, cursor)) { if (is_fused(ab, cursor)) {
ui_browser__mark_fused(browser, ui_browser__mark_fused(browser,
pcnt_width + 3 + ab->addr_width + width, pcnt_width + 3 + notes->widths.addr + width,
from - 1, from - 1,
to > from ? true : false); to > from ? true : false);
} }
...@@ -384,11 +210,11 @@ static void annotate_browser__draw_current_jump(struct ui_browser *browser) ...@@ -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) 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 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); annotate_browser__draw_current_jump(browser);
ui_browser__set_color(browser, HE_COLORSET_NORMAL); 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 ...@@ -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, static void annotate_browser__set_top(struct annotate_browser *browser,
struct annotation_line *pos, u32 idx) struct annotation_line *pos, u32 idx)
{ {
struct annotation *notes = browser__annotation(&browser->b);
unsigned back; unsigned back;
ui_browser__refresh_dimensions(&browser->b); ui_browser__refresh_dimensions(&browser->b);
...@@ -439,7 +266,7 @@ static void annotate_browser__set_top(struct annotate_browser *browser, ...@@ -439,7 +266,7 @@ static void annotate_browser__set_top(struct annotate_browser *browser,
while (browser->b.top_idx != 0 && back != 0) { while (browser->b.top_idx != 0 && back != 0) {
pos = list_entry(pos->node.prev, struct annotation_line, node); 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; continue;
--browser->b.top_idx; --browser->b.top_idx;
...@@ -453,16 +280,12 @@ static void annotate_browser__set_top(struct annotate_browser *browser, ...@@ -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, static void annotate_browser__set_rb_top(struct annotate_browser *browser,
struct rb_node *nd) struct rb_node *nd)
{ {
struct browser_line *bpos; struct annotation *notes = browser__annotation(&browser->b);
struct annotation_line *pos; struct annotation_line * pos = rb_entry(nd, struct annotation_line, rb_node);
u32 idx; u32 idx = pos->idx;
pos = rb_entry(nd, struct annotation_line, rb_node); if (notes->options->hide_src_code)
bpos = browser_line(pos); idx = pos->idx_asm;
idx = bpos->idx;
if (annotate_browser__opts.hide_src_code)
idx = bpos->idx_asm;
annotate_browser__set_top(browser, pos, idx); annotate_browser__set_top(browser, pos, idx);
browser->curr_hot = nd; browser->curr_hot = nd;
} }
...@@ -510,47 +333,47 @@ static void annotate_browser__calc_percent(struct annotate_browser *browser, ...@@ -510,47 +333,47 @@ static void annotate_browser__calc_percent(struct annotate_browser *browser,
static bool annotate_browser__toggle_source(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 annotation_line *al;
struct browser_line *bl;
off_t offset = browser->b.index - browser->b.top_idx; off_t offset = browser->b.index - browser->b.top_idx;
browser->b.seek(&browser->b, offset, SEEK_CUR); browser->b.seek(&browser->b, offset, SEEK_CUR);
al = list_entry(browser->b.top, struct annotation_line, node); al = list_entry(browser->b.top, struct annotation_line, node);
bl = browser_line(al);
if (annotate_browser__opts.hide_src_code) { if (notes->options->hide_src_code) {
if (bl->idx_asm < offset) if (al->idx_asm < offset)
offset = bl->idx; offset = al->idx;
browser->b.nr_entries = browser->nr_entries; browser->b.nr_entries = notes->nr_entries;
annotate_browser__opts.hide_src_code = false; notes->options->hide_src_code = false;
browser->b.seek(&browser->b, -offset, SEEK_CUR); browser->b.seek(&browser->b, -offset, SEEK_CUR);
browser->b.top_idx = bl->idx - offset; browser->b.top_idx = al->idx - offset;
browser->b.index = bl->idx; browser->b.index = al->idx;
} else { } else {
if (bl->idx_asm < 0) { if (al->idx_asm < 0) {
ui_helpline__puts("Only available for assembly lines."); ui_helpline__puts("Only available for assembly lines.");
browser->b.seek(&browser->b, -offset, SEEK_CUR); browser->b.seek(&browser->b, -offset, SEEK_CUR);
return false; return false;
} }
if (bl->idx_asm < offset) if (al->idx_asm < offset)
offset = bl->idx_asm; offset = al->idx_asm;
browser->b.nr_entries = browser->nr_asm_entries; browser->b.nr_entries = notes->nr_asm_entries;
annotate_browser__opts.hide_src_code = true; notes->options->hide_src_code = true;
browser->b.seek(&browser->b, -offset, SEEK_CUR); browser->b.seek(&browser->b, -offset, SEEK_CUR);
browser->b.top_idx = bl->idx_asm - offset; browser->b.top_idx = al->idx_asm - offset;
browser->b.index = bl->idx_asm; browser->b.index = al->idx_asm;
} }
return true; 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); struct annotation *notes = browser__annotation(browser);
browser->b.nr_entries = browser->nr_asm_entries; ui_browser__reset_index(browser);
browser->nr_entries = notes->nr_asm_entries;
} }
#define SYM_TITLE_MAX_SIZE (PATH_MAX + 64) #define SYM_TITLE_MAX_SIZE (PATH_MAX + 64)
...@@ -561,6 +384,15 @@ static int sym_title(struct symbol *sym, struct map *map, char *title, ...@@ -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); 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, static bool annotate_browser__callq(struct annotate_browser *browser,
struct perf_evsel *evsel, struct perf_evsel *evsel,
struct hist_browser_timer *hbt) struct hist_browser_timer *hbt)
...@@ -570,9 +402,6 @@ static bool annotate_browser__callq(struct annotate_browser *browser, ...@@ -570,9 +402,6 @@ static bool annotate_browser__callq(struct annotate_browser *browser,
struct annotation *notes; struct annotation *notes;
char title[SYM_TITLE_MAX_SIZE]; char title[SYM_TITLE_MAX_SIZE];
if (!ins__is_call(&dl->ins))
return false;
if (!dl->ops.target.sym) { if (!dl->ops.target.sym) {
ui_helpline__puts("The called function was not found."); ui_helpline__puts("The called function was not found.");
return true; return true;
...@@ -599,23 +428,23 @@ static ...@@ -599,23 +428,23 @@ static
struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser, struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
s64 offset, s64 *idx) s64 offset, s64 *idx)
{ {
struct map_symbol *ms = browser->b.priv; struct annotation *notes = browser__annotation(&browser->b);
struct symbol *sym = ms->sym;
struct annotation *notes = symbol__annotation(sym);
struct disasm_line *pos; struct disasm_line *pos;
*idx = 0; *idx = 0;
list_for_each_entry(pos, &notes->src->source, al.node) { list_for_each_entry(pos, &notes->src->source, al.node) {
if (pos->al.offset == offset) if (pos->al.offset == offset)
return pos; return pos;
if (!disasm_line__filter(&browser->b, &pos->al.node)) if (!annotation_line__filter(&pos->al, notes))
++*idx; ++*idx;
} }
return NULL; 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); struct disasm_line *dl = disasm_line(browser->selection);
u64 offset; u64 offset;
...@@ -624,6 +453,11 @@ static bool annotate_browser__jump(struct annotate_browser *browser) ...@@ -624,6 +453,11 @@ static bool annotate_browser__jump(struct annotate_browser *browser)
if (!ins__is_jump(&dl->ins)) if (!ins__is_jump(&dl->ins))
return false; return false;
if (dl->ops.target.outside) {
annotate_browser__callq(browser, evsel, hbt);
return true;
}
offset = dl->ops.target.offset; offset = dl->ops.target.offset;
dl = annotate_browser__find_offset(browser, offset, &idx); dl = annotate_browser__find_offset(browser, offset, &idx);
if (dl == NULL) { if (dl == NULL) {
...@@ -640,14 +474,12 @@ static ...@@ -640,14 +474,12 @@ static
struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser, struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser,
char *s, s64 *idx) char *s, s64 *idx)
{ {
struct map_symbol *ms = browser->b.priv; struct annotation *notes = browser__annotation(&browser->b);
struct symbol *sym = ms->sym;
struct annotation *notes = symbol__annotation(sym);
struct annotation_line *al = browser->selection; struct annotation_line *al = browser->selection;
*idx = browser->b.index; *idx = browser->b.index;
list_for_each_entry_continue(al, &notes->src->source, node) { 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; continue;
++*idx; ++*idx;
...@@ -679,14 +511,12 @@ static ...@@ -679,14 +511,12 @@ static
struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser, struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
char *s, s64 *idx) char *s, s64 *idx)
{ {
struct map_symbol *ms = browser->b.priv; struct annotation *notes = browser__annotation(&browser->b);
struct symbol *sym = ms->sym;
struct annotation *notes = symbol__annotation(sym);
struct annotation_line *al = browser->selection; struct annotation_line *al = browser->selection;
*idx = browser->b.index; *idx = browser->b.index;
list_for_each_entry_continue_reverse(al, &notes->src->source, node) { 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; continue;
--*idx; --*idx;
...@@ -762,19 +592,6 @@ bool annotate_browser__continue_search_reverse(struct annotate_browser *browser, ...@@ -762,19 +592,6 @@ bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
return __annotate_browser__search_reverse(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, static int annotate_browser__run(struct annotate_browser *browser,
struct perf_evsel *evsel, struct perf_evsel *evsel,
struct hist_browser_timer *hbt) struct hist_browser_timer *hbt)
...@@ -782,6 +599,7 @@ static int annotate_browser__run(struct annotate_browser *browser, ...@@ -782,6 +599,7 @@ static int annotate_browser__run(struct annotate_browser *browser,
struct rb_node *nd = NULL; struct rb_node *nd = NULL;
struct map_symbol *ms = browser->b.priv; struct map_symbol *ms = browser->b.priv;
struct symbol *sym = ms->sym; struct symbol *sym = ms->sym;
struct annotation *notes = symbol__annotation(ms->sym);
const char *help = "Press 'h' for help on key bindings"; const char *help = "Press 'h' for help on key bindings";
int delay_secs = hbt ? hbt->refresh : 0; int delay_secs = hbt ? hbt->refresh : 0;
int key; int key;
...@@ -856,6 +674,7 @@ static int annotate_browser__run(struct annotate_browser *browser, ...@@ -856,6 +674,7 @@ static int annotate_browser__run(struct annotate_browser *browser,
"t Circulate percent, total period, samples view\n" "t Circulate percent, total period, samples view\n"
"/ Search string\n" "/ Search string\n"
"k Toggle line numbers\n" "k Toggle line numbers\n"
"P Print to [symbol_name].annotation file.\n"
"r Run available scripts\n" "r Run available scripts\n"
"? Search string backwards\n"); "? Search string backwards\n");
continue; continue;
...@@ -865,8 +684,7 @@ static int annotate_browser__run(struct annotate_browser *browser, ...@@ -865,8 +684,7 @@ static int annotate_browser__run(struct annotate_browser *browser,
continue; continue;
} }
case 'k': case 'k':
annotate_browser__opts.show_linenr = notes->options->show_linenr = !notes->options->show_linenr;
!annotate_browser__opts.show_linenr;
break; break;
case 'H': case 'H':
nd = browser->curr_hot; nd = browser->curr_hot;
...@@ -876,15 +694,15 @@ static int annotate_browser__run(struct annotate_browser *browser, ...@@ -876,15 +694,15 @@ static int annotate_browser__run(struct annotate_browser *browser,
ui_helpline__puts(help); ui_helpline__puts(help);
continue; continue;
case 'o': case 'o':
annotate_browser__opts.use_offset = !annotate_browser__opts.use_offset; notes->options->use_offset = !notes->options->use_offset;
annotate_browser__update_addr_width(browser); annotation__update_column_widths(notes);
continue; continue;
case 'j': case 'j':
annotate_browser__opts.jump_arrows = !annotate_browser__opts.jump_arrows; notes->options->jump_arrows = !notes->options->jump_arrows;
continue; continue;
case 'J': case 'J':
annotate_browser__opts.show_nr_jumps = !annotate_browser__opts.show_nr_jumps; notes->options->show_nr_jumps = !notes->options->show_nr_jumps;
annotate_browser__update_addr_width(browser); annotation__update_column_widths(notes);
continue; continue;
case '/': case '/':
if (annotate_browser__search(browser, delay_secs)) { if (annotate_browser__search(browser, delay_secs)) {
...@@ -910,7 +728,7 @@ static int annotate_browser__run(struct annotate_browser *browser, ...@@ -910,7 +728,7 @@ static int annotate_browser__run(struct annotate_browser *browser,
browser->b.height, browser->b.height,
browser->b.index, browser->b.index,
browser->b.top_idx, browser->b.top_idx,
browser->nr_asm_entries); notes->nr_asm_entries);
} }
continue; continue;
case K_ENTER: case K_ENTER:
...@@ -926,22 +744,25 @@ static int annotate_browser__run(struct annotate_browser *browser, ...@@ -926,22 +744,25 @@ static int annotate_browser__run(struct annotate_browser *browser,
goto show_sup_ins; goto show_sup_ins;
else if (ins__is_ret(&dl->ins)) else if (ins__is_ret(&dl->ins))
goto out; goto out;
else if (!(annotate_browser__jump(browser) || else if (!(annotate_browser__jump(browser, evsel, hbt) ||
annotate_browser__callq(browser, evsel, hbt))) { annotate_browser__callq(browser, evsel, hbt))) {
show_sup_ins: show_sup_ins:
ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions."); ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
} }
continue; continue;
} }
case 'P':
map_symbol__annotation_dump(ms, evsel);
continue;
case 't': case 't':
if (annotate_browser__opts.show_total_period) { if (notes->options->show_total_period) {
annotate_browser__opts.show_total_period = false; notes->options->show_total_period = false;
annotate_browser__opts.show_nr_samples = true; notes->options->show_nr_samples = true;
} else if (annotate_browser__opts.show_nr_samples) } else if (notes->options->show_nr_samples)
annotate_browser__opts.show_nr_samples = false; notes->options->show_nr_samples = false;
else else
annotate_browser__opts.show_total_period = true; notes->options->show_total_period = true;
annotate_browser__update_addr_width(browser); annotation__update_column_widths(notes);
continue; continue;
case K_LEFT: case K_LEFT:
case K_ESC: case K_ESC:
...@@ -963,12 +784,6 @@ static int annotate_browser__run(struct annotate_browser *browser, ...@@ -963,12 +784,6 @@ static int annotate_browser__run(struct annotate_browser *browser,
int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel, int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
struct hist_browser_timer *hbt) 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); 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, ...@@ -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); 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, int symbol__tui_annotate(struct symbol *sym, struct map *map,
struct perf_evsel *evsel, struct perf_evsel *evsel,
struct hist_browser_timer *hbt) struct hist_browser_timer *hbt)
{ {
struct annotation_line *al; struct annotation *notes = symbol__annotation(sym);
struct annotation *notes;
size_t size;
struct map_symbol ms = { struct map_symbol ms = {
.map = map, .map = map,
.sym = sym, .sym = sym,
...@@ -1120,26 +817,14 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, ...@@ -1120,26 +817,14 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map,
}, },
}; };
int ret = -1, err; int ret = -1, err;
int nr_pcnt = 1;
if (sym == NULL) if (sym == NULL)
return -1; return -1;
size = symbol__size(sym);
if (map->dso->annotate_warned) if (map->dso->annotate_warned)
return -1; return -1;
browser.offsets = zalloc(size * sizeof(struct annotation_line *)); err = symbol__annotate2(sym, map, evsel, &annotation__default_options, &browser.arch);
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);
if (err) { if (err) {
char msg[BUFSIZ]; char msg[BUFSIZ];
symbol__strerror_disassemble(sym, map, err, msg, sizeof(msg)); symbol__strerror_disassemble(sym, map, err, msg, sizeof(msg));
...@@ -1147,110 +832,21 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, ...@@ -1147,110 +832,21 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map,
goto out_free_offsets; goto out_free_offsets;
} }
symbol__calc_percent(sym, evsel);
ui_helpline__push("Press ESC to exit"); ui_helpline__push("Press ESC to exit");
notes = symbol__annotation(sym); browser.b.width = notes->max_line_len;
browser.start = map__rip_2objdump(map, sym->start); browser.b.nr_entries = notes->nr_entries;
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.entries = &notes->src->source, browser.b.entries = &notes->src->source,
browser.b.width += 18; /* Percentage */ browser.b.width += 18; /* Percentage */
if (annotate_browser__opts.hide_src_code) if (notes->options->hide_src_code)
annotate_browser__init_asm_mode(&browser); ui_browser__init_asm_mode(&browser.b);
annotate_browser__update_addr_width(&browser);
ret = annotate_browser__run(&browser, evsel, hbt); ret = annotate_browser__run(&browser, evsel, hbt);
annotated_source__purge(notes->src); annotated_source__purge(notes->src);
out_free_offsets: out_free_offsets:
free(browser.offsets); zfree(&notes->offsets);
return ret; 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);
}
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "sort.h" #include "sort.h"
#include "build-id.h" #include "build-id.h"
#include "color.h" #include "color.h"
#include "config.h"
#include "cache.h" #include "cache.h"
#include "symbol.h" #include "symbol.h"
#include "debug.h" #include "debug.h"
...@@ -27,8 +28,25 @@ ...@@ -27,8 +28,25 @@
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/kernel.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" #include "sane_ctype.h"
struct annotation_options annotation__default_options = {
.use_offset = true,
.jump_arrows = true,
};
const char *disassembler_style; const char *disassembler_style;
const char *objdump_path; const char *objdump_path;
static regex_t file_lineno; static regex_t file_lineno;
...@@ -184,9 +202,10 @@ bool ins__is_fused(struct arch *arch, const char *ins1, const char *ins2) ...@@ -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); 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; char *endptr, *tok, *name;
struct map *map = ms->map;
struct addr_map_symbol target = { struct addr_map_symbol target = {
.map = map, .map = map,
}; };
...@@ -254,11 +273,26 @@ bool ins__is_call(const struct ins *ins) ...@@ -254,11 +273,26 @@ bool ins__is_call(const struct ins *ins)
return ins->ops == &call_ops || ins->ops == &s390_call_ops; 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, ','); 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.: * skip over possible up to 2 operands to get to address, e.g.:
* tbnz w0, #26, ffff0000083cd190 <security_file_permission+0xd0> * 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 ...@@ -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); ops->target.addr = strtoull(ops->raw, NULL, 16);
} }
if (s++ != NULL) { target.addr = map__objdump_2mem(map, ops->target.addr);
ops->target.offset = strtoull(s, NULL, 16); 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; ops->target.offset_avail = true;
} else { } else {
ops->target.offset_avail = false; ops->target.offset_avail = false;
...@@ -287,11 +349,15 @@ static int jump__parse(struct arch *arch __maybe_unused, struct ins_operands *op ...@@ -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, static int jump__scnprintf(struct ins *ins, char *bf, size_t size,
struct ins_operands *ops) struct ins_operands *ops)
{ {
const char *c = strchr(ops->raw, ','); const char *c;
if (!ops->target.addr || ops->target.offset < 0) if (!ops->target.addr || ops->target.offset < 0)
return ins__raw_scnprintf(ins, bf, size, ops); 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) { if (c != NULL) {
const char *c2 = strchr(c + 1, ','); const char *c2 = strchr(c + 1, ',');
...@@ -347,7 +413,7 @@ static int comment__symbol(char *raw, char *comment, u64 *addrp, char **namep) ...@@ -347,7 +413,7 @@ static int comment__symbol(char *raw, char *comment, u64 *addrp, char **namep)
return 0; 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)); ops->locked.ops = zalloc(sizeof(*ops->locked.ops));
if (ops->locked.ops == NULL) if (ops->locked.ops == NULL)
...@@ -362,7 +428,7 @@ static int lock__parse(struct arch *arch, struct ins_operands *ops, struct map * ...@@ -362,7 +428,7 @@ static int lock__parse(struct arch *arch, struct ins_operands *ops, struct map *
goto out_free_ops; goto out_free_ops;
if (ops->locked.ins.ops->parse && 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; goto out_free_ops;
return 0; return 0;
...@@ -405,7 +471,7 @@ static struct ins_ops lock_ops = { ...@@ -405,7 +471,7 @@ static struct ins_ops lock_ops = {
.scnprintf = lock__scnprintf, .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; char *s = strchr(ops->raw, ','), *target, *comment, prev;
...@@ -466,7 +532,7 @@ static struct ins_ops mov_ops = { ...@@ -466,7 +532,7 @@ static struct ins_ops mov_ops = {
.scnprintf = mov__scnprintf, .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; char *target, *comment, *s, prev;
...@@ -833,6 +899,66 @@ int addr_map_symbol__account_cycles(struct addr_map_symbol *ams, ...@@ -833,6 +899,66 @@ int addr_map_symbol__account_cycles(struct addr_map_symbol *ams,
return err; 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 addr_map_symbol__inc_samples(struct addr_map_symbol *ams, struct perf_sample *sample,
int evidx) int evidx)
{ {
...@@ -845,14 +971,14 @@ int hist_entry__inc_addr_samples(struct hist_entry *he, struct perf_sample *samp ...@@ -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); 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); dl->ins.ops = ins__find(arch, dl->ins.name);
if (!dl->ins.ops) if (!dl->ins.ops)
return; 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; dl->ins.ops = NULL;
} }
...@@ -889,7 +1015,7 @@ static int disasm_line__parse(char *line, const char **namep, char **rawp) ...@@ -889,7 +1015,7 @@ static int disasm_line__parse(char *line, const char **namep, char **rawp)
struct annotate_args { struct annotate_args {
size_t privsize; size_t privsize;
struct arch *arch; struct arch *arch;
struct map *map; struct map_symbol ms;
struct perf_evsel *evsel; struct perf_evsel *evsel;
s64 offset; s64 offset;
char *line; char *line;
...@@ -971,7 +1097,7 @@ static struct disasm_line *disasm_line__new(struct annotate_args *args) ...@@ -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) if (disasm_line__parse(dl->al.line, &dl->ins.name, &dl->ops.raw) < 0)
goto out_free_line; 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, ...@@ -1229,7 +1355,7 @@ static int symbol__parse_objdump_line(struct symbol *sym, FILE *file,
struct annotate_args *args, struct annotate_args *args,
int *line_nr) int *line_nr)
{ {
struct map *map = args->map; struct map *map = args->ms.map;
struct annotation *notes = symbol__annotation(sym); struct annotation *notes = symbol__annotation(sym);
struct disasm_line *dl; struct disasm_line *dl;
char *line = NULL, *parsed_line, *tmp, *tmp2; char *line = NULL, *parsed_line, *tmp, *tmp2;
...@@ -1276,6 +1402,7 @@ static int symbol__parse_objdump_line(struct symbol *sym, FILE *file, ...@@ -1276,6 +1402,7 @@ static int symbol__parse_objdump_line(struct symbol *sym, FILE *file,
args->offset = offset; args->offset = offset;
args->line = parsed_line; args->line = parsed_line;
args->line_nr = *line_nr; args->line_nr = *line_nr;
args->ms.sym = sym;
dl = disasm_line__new(args); dl = disasm_line__new(args);
free(line); free(line);
...@@ -1284,7 +1411,7 @@ static int symbol__parse_objdump_line(struct symbol *sym, FILE *file, ...@@ -1284,7 +1411,7 @@ static int symbol__parse_objdump_line(struct symbol *sym, FILE *file,
if (dl == NULL) if (dl == NULL)
return -1; return -1;
if (!disasm_line__has_offset(dl)) { if (!disasm_line__has_local_offset(dl)) {
dl->ops.target.offset = dl->ops.target.addr - dl->ops.target.offset = dl->ops.target.addr -
map__rip_2objdump(map, sym->start); map__rip_2objdump(map, sym->start);
dl->ops.target.offset_avail = true; dl->ops.target.offset_avail = true;
...@@ -1428,7 +1555,7 @@ static int dso__disassemble_filename(struct dso *dso, char *filename, size_t fil ...@@ -1428,7 +1555,7 @@ static int dso__disassemble_filename(struct dso *dso, char *filename, size_t fil
static int symbol__disassemble(struct symbol *sym, struct annotate_args *args) 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; struct dso *dso = map->dso;
char *command; char *command;
FILE *file; FILE *file;
...@@ -1627,7 +1754,6 @@ int symbol__annotate(struct symbol *sym, struct map *map, ...@@ -1627,7 +1754,6 @@ int symbol__annotate(struct symbol *sym, struct map *map,
{ {
struct annotate_args args = { struct annotate_args args = {
.privsize = privsize, .privsize = privsize,
.map = map,
.evsel = evsel, .evsel = evsel,
}; };
struct perf_env *env = perf_evsel__env(evsel); struct perf_env *env = perf_evsel__env(evsel);
...@@ -1653,6 +1779,9 @@ int symbol__annotate(struct symbol *sym, struct map *map, ...@@ -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); return symbol__disassemble(sym, &args);
} }
...@@ -1893,6 +2022,103 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, ...@@ -1893,6 +2022,103 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map,
return more; 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) void symbol__annotate_zero_histogram(struct symbol *sym, int evidx)
{ {
struct annotation *notes = symbol__annotation(sym); struct annotation *notes = symbol__annotation(sym);
...@@ -1952,8 +2178,109 @@ size_t disasm__fprintf(struct list_head *head, FILE *fp) ...@@ -1952,8 +2178,109 @@ size_t disasm__fprintf(struct list_head *head, FILE *fp)
return printed; 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, 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 annotation_line *al;
struct rb_root tmp_root = RB_ROOT; struct rb_root tmp_root = RB_ROOT;
...@@ -1974,8 +2301,8 @@ static void annotation__calc_lines(struct annotation *notes, struct map *map, ...@@ -1974,8 +2301,8 @@ static void annotation__calc_lines(struct annotation *notes, struct map *map,
if (percent_max <= 0.5) if (percent_max <= 0.5)
continue; continue;
al->path = get_srcline(map->dso, start + al->offset, NULL, al->path = get_srcline(map->dso, notes->start + al->offset, NULL,
false, true, start + al->offset); false, true, notes->start + al->offset);
insert_source_line(&tmp_root, al); insert_source_line(&tmp_root, al);
} }
...@@ -1986,9 +2313,40 @@ static void symbol__calc_lines(struct symbol *sym, struct map *map, ...@@ -1986,9 +2313,40 @@ static void symbol__calc_lines(struct symbol *sym, struct map *map,
struct rb_root *root) struct rb_root *root)
{ {
struct annotation *notes = symbol__annotation(sym); 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, int symbol__tty_annotate(struct symbol *sym, struct map *map,
...@@ -2021,3 +2379,276 @@ bool ui__has_annotation(void) ...@@ -2021,3 +2379,276 @@ bool ui__has_annotation(void)
{ {
return use_browser == 1 && perf_hpp_list.sym; 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;
}
...@@ -28,6 +28,7 @@ struct ins_operands { ...@@ -28,6 +28,7 @@ struct ins_operands {
u64 addr; u64 addr;
s64 offset; s64 offset;
bool offset_avail; bool offset_avail;
bool outside;
} target; } target;
union { union {
struct { struct {
...@@ -46,7 +47,7 @@ struct arch; ...@@ -46,7 +47,7 @@ struct arch;
struct ins_ops { struct ins_ops {
void (*free)(struct ins_operands *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, int (*scnprintf)(struct ins *ins, char *bf, size_t size,
struct ins_operands *ops); struct ins_operands *ops);
}; };
...@@ -58,6 +59,21 @@ bool ins__is_lock(const struct ins *ins); ...@@ -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); 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); 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 annotation;
struct sym_hist_entry { struct sym_hist_entry {
...@@ -77,10 +93,13 @@ struct annotation_line { ...@@ -77,10 +93,13 @@ struct annotation_line {
s64 offset; s64 offset;
char *line; char *line;
int line_nr; int line_nr;
int jump_sources;
float ipc; float ipc;
u64 cycles; u64 cycles;
size_t privsize; size_t privsize;
char *path; char *path;
u32 idx;
int idx_asm;
int samples_nr; int samples_nr;
struct annotation_data samples[0]; struct annotation_data samples[0];
}; };
...@@ -98,14 +117,40 @@ static inline struct disasm_line *disasm_line(struct annotation_line *al) ...@@ -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; 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); void disasm_line__free(struct disasm_line *dl);
struct annotation_line * struct annotation_line *
annotation_line__next(struct annotation_line *pos, struct list_head *head); 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); 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); size_t disasm__fprintf(struct list_head *head, FILE *fp);
void symbol__calc_percent(struct symbol *sym, struct perf_evsel *evsel); void symbol__calc_percent(struct symbol *sym, struct perf_evsel *evsel);
...@@ -151,9 +196,47 @@ struct annotated_source { ...@@ -151,9 +196,47 @@ struct annotated_source {
struct annotation { struct annotation {
pthread_mutex_t lock; pthread_mutex_t lock;
u64 max_coverage; 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; 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) static inline struct sym_hist *annotation__histogram(struct annotation *notes, int idx)
{ {
return (((void *)&notes->src->histograms) + return (((void *)&notes->src->histograms) +
...@@ -181,6 +264,10 @@ void symbol__annotate_zero_histograms(struct symbol *sym); ...@@ -181,6 +264,10 @@ void symbol__annotate_zero_histograms(struct symbol *sym);
int symbol__annotate(struct symbol *sym, struct map *map, int symbol__annotate(struct symbol *sym, struct map *map,
struct perf_evsel *evsel, size_t privsize, struct perf_evsel *evsel, size_t privsize,
struct arch **parch); 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 { enum symbol_disassemble_errno {
SYMBOL_ANNOTATE_ERRNO__SUCCESS = 0, SYMBOL_ANNOTATE_ERRNO__SUCCESS = 0,
...@@ -205,16 +292,23 @@ int symbol__strerror_disassemble(struct symbol *sym, struct map *map, ...@@ -205,16 +292,23 @@ int symbol__strerror_disassemble(struct symbol *sym, struct map *map,
int symbol__annotate_printf(struct symbol *sym, struct map *map, int symbol__annotate_printf(struct symbol *sym, struct map *map,
struct perf_evsel *evsel, bool full_paths, struct perf_evsel *evsel, bool full_paths,
int min_pcnt, int max_lines, int context); 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_zero_histogram(struct symbol *sym, int evidx);
void symbol__annotate_decay_histogram(struct symbol *sym, int evidx); void symbol__annotate_decay_histogram(struct symbol *sym, int evidx);
void annotated_source__purge(struct annotated_source *as); 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); bool ui__has_annotation(void);
int symbol__tty_annotate(struct symbol *sym, struct map *map, int symbol__tty_annotate(struct symbol *sym, struct map *map,
struct perf_evsel *evsel, bool print_lines, struct perf_evsel *evsel, bool print_lines,
bool full_paths, int min_pcnt, int max_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 #ifdef HAVE_SLANG_SUPPORT
int symbol__tui_annotate(struct symbol *sym, struct map *map, int symbol__tui_annotate(struct symbol *sym, struct map *map,
struct perf_evsel *evsel, struct perf_evsel *evsel,
...@@ -232,4 +326,6 @@ static inline int symbol__tui_annotate(struct symbol *sym __maybe_unused, ...@@ -232,4 +326,6 @@ static inline int symbol__tui_annotate(struct symbol *sym __maybe_unused,
extern const char *disassembler_style; extern const char *disassembler_style;
void annotation_config__init(void);
#endif /* __PERF_ANNOTATE_H */ #endif /* __PERF_ANNOTATE_H */
...@@ -1004,8 +1004,10 @@ static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist, ...@@ -1004,8 +1004,10 @@ static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist,
return PyErr_NoMemory(); return PyErr_NoMemory();
evsel = perf_evlist__event2evsel(evlist, event); evsel = perf_evlist__event2evsel(evlist, event);
if (!evsel) if (!evsel) {
Py_INCREF(Py_None);
return Py_None; return Py_None;
}
pevent->evsel = evsel; pevent->evsel = evsel;
......
...@@ -236,7 +236,8 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg, ...@@ -236,7 +236,8 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
if (err) if (err)
goto out; 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; goto out;
err = dwfl_getthread_frames(ui->dwfl, thread->tid, frame_callback, ui); err = dwfl_getthread_frames(ui->dwfl, thread->tid, frame_callback, ui);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment