mirror of https://gitee.com/openkylin/linux.git
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:
commit
a0ac7b3ca9
|
@ -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.
|
||||
|
|
|
@ -296,6 +296,9 @@ OPTIONS
|
|||
--vmlinux=<file>::
|
||||
vmlinux pathname
|
||||
|
||||
--ignore-vmlinux::
|
||||
Ignore vmlinux files.
|
||||
|
||||
--kallsyms=<file>::
|
||||
kallsyms pathname
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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, ¬es->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, ¬es->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, ¬es->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(¬es->lock);
|
||||
for (offset = 0; offset < size; ++offset) {
|
||||
struct cyc_hist *ch;
|
||||
|
||||
ch = ¬es->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(¬es->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, ¬es->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 = ¬es->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(¬es->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);
|
||||
}
|
||||
|
|
|
@ -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(¬es->lock);
|
||||
for (offset = 0; offset < size; ++offset) {
|
||||
struct cyc_hist *ch;
|
||||
|
||||
ch = ¬es->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(¬es->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, ¬es->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, ¬es->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(¬es->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 {
|
|||
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 *)¬es->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 */
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue