perf/core improvements and fixes:

- 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>
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEELb9bqkb7Te0zijNb1lAW81NSqkAFAlq1YC8ACgkQ1lAW81NS
 qkDgThAAtnpGECjgPFJMqtLQeaW98dWxNz2c/DShWcLClESaKKHf8H0ImEz+mkG3
 ehOg5VxfvutzBP51MmMTGNuS5vvFBhE9Z0+chqAHfIfLL2yxfbBKNQln82ja3QKd
 0EPauXbCt55un4vQBEQNr3uD6OMDIBmRBLshdMGFqzRBY8LvbO33hmjlBVjzRd+d
 FSdCA0p4C+YSSeGHi9496Ru4Mb5Pch5rxzBSDCw3/+Sk1Ixxe2qSyWS5iCFCrmrq
 33+Z8+QNRPBn3D+SRTqTXplSsexe2PgfGaktvpYvPY2lEq3slhIf7wGLWpPSkP6z
 520PZOdWm4bSfklSHl3Qa2XaJRKumkZxKGBFjbWZWcPuwa1+6ZtkCIaJz498hHDd
 xS3pVjBWQyU7NVqdTq78rCWT0mfgtpXxgblm/38lT7GM+5Z3HjqUgzMjt6TvNqaM
 lvGz3u9EgtkCRlOtE4XRfxFTdpeIKdDdPjqmtGsnRxp2Cfr3O/r7xxzLWbIUfG8C
 vAjkPHNB2bxt7eXiP1z/YDJnvlBvQCWIILYgRRmlDCtCvY7DjNxhwirBvfAx8I1p
 HICyC59+IJqVMlMjSPnON63pS2P6uYIW1CF0yEno61dTVVyF5qugRQ8cwEwB7TYi
 jRTU3C6N8azqmPUGJR5Sd8y3A8dZIzcefWVlwv2ZZC4DHIS+WYs=
 =0yaC
 -----END PGP SIGNATURE-----

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>
This commit is contained in:
Ingo Molnar 2018-03-25 10:38:15 +02:00
commit a0ac7b3ca9
13 changed files with 943 additions and 586 deletions

View File

@ -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.

View File

@ -296,6 +296,9 @@ OPTIONS
--vmlinux=<file>::
vmlinux pathname
--ignore-vmlinux::
Ignore vmlinux files.
--kallsyms=<file>::
kallsyms pathname

View File

@ -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;

View File

@ -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);

View File

@ -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 @@ int cmd_report(int argc, const char **argv)
symbol_conf.priv_size += sizeof(u32);
symbol_conf.sort_by_name = true;
}
annotation_config__init();
}
if (symbol__init(&session->header.env) < 0)

View File

@ -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;

View File

@ -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();
}

View File

@ -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_ */

View File

@ -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__set_percent_color(browser, percent, current);
}
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);
}
static void annotate_browser__printf(void *browser, const char *fmt, ...)
{
va_list args;
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)
{
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;
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;
annotation_line__write(al, notes, &ops);
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)
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;
}
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;
struct annotation *notes = browser__annotation(&browser->b);
struct annotation_line * pos = rb_entry(nd, struct annotation_line, rb_node);
u32 idx = pos->idx;
pos = rb_entry(nd, struct annotation_line, rb_node);
bpos = browser_line(pos);
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 @@ static int annotate_browser__run(struct annotate_browser *browser,
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 @@ static int annotate_browser__run(struct annotate_browser *browser,
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 @@ static int annotate_browser__run(struct annotate_browser *browser,
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);
}

View File

@ -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 @@ static int disasm_line__parse(char *line, const char **namep, char **rawp)
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 @@ static int dso__disassemble_filename(struct dso *dso, char *filename, size_t fil
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;
}

View File

@ -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 */

View File

@ -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;

View File

@ -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);