tools/perf/builtin-annotate.c | 2 +- tools/perf/builtin-c2c.c | 124 ++++++++++++++++++++++++++++-- tools/perf/ui/browsers/annotate.c | 30 ++++++-- tools/perf/ui/browsers/hists.c | 2 +- tools/perf/util/annotate.c | 2 +- tools/perf/util/annotate.h | 4 +- tools/perf/util/hist.h | 6 +- 7 files changed, 153 insertions(+), 17 deletions(-)
Perf c2c report currently specified the code address and source:line
information in the cacheline browser, while it is lack of annotation
support like perf report to directly show the disassembly code for
the particular symbol shared that same cacheline. This patches add
a key 'a' binding to the cacheline browser which reuse the annotation
browser to show the disassembly view for easier analysis of cacheline
contentions. By default, the 'TAB' key navigate to the code address
where the contentions detected.
Signed-off-by: Tianyou Li <tianyou.li@intel.com>
Reviewed-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
Reviewed-by: Thomas Falcon <thomas.falcon@intel.com>
Reviewed-by: Jiebin Sun <jiebin.sun@intel.com>
Reviewed-by: Pan Deng <pan.deng@intel.com>
Reviewed-by: Zhiguo Zhou <zhiguo.zhou@intel.com>
Reviewed-by: Wangyang Guo <wangyang.guo@intel.com>
---
tools/perf/builtin-annotate.c | 2 +-
tools/perf/builtin-c2c.c | 124 ++++++++++++++++++++++++++++--
tools/perf/ui/browsers/annotate.c | 30 ++++++--
tools/perf/ui/browsers/hists.c | 2 +-
tools/perf/util/annotate.c | 2 +-
tools/perf/util/annotate.h | 4 +-
tools/perf/util/hist.h | 6 +-
7 files changed, 153 insertions(+), 17 deletions(-)
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 5d57d2913f3d..8c896fbe76b7 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -519,7 +519,7 @@ static void hists__find_annotations(struct hists *hists,
/* skip missing symbols */
nd = rb_next(nd);
} else if (use_browser == 1) {
- key = hist_entry__tui_annotate(he, evsel, NULL);
+ key = hist_entry__tui_annotate(he, evsel, NULL, NO_INITIAL_IP);
switch (key) {
case -1:
diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c
index 9e9ff471ddd1..f753ec50b967 100644
--- a/tools/perf/builtin-c2c.c
+++ b/tools/perf/builtin-c2c.c
@@ -45,6 +45,8 @@
#include "pmus.h"
#include "string2.h"
#include "util/util.h"
+#include "util/symbol.h"
+#include "util/annotate.h"
struct c2c_hists {
struct hists hists;
@@ -62,6 +64,7 @@ struct compute_stats {
struct c2c_hist_entry {
struct c2c_hists *hists;
+ struct evsel *evsel;
struct c2c_stats stats;
unsigned long *cpuset;
unsigned long *nodeset;
@@ -225,6 +228,12 @@ he__get_c2c_hists(struct hist_entry *he,
return hists;
}
+static void c2c_he__set_evsel(struct c2c_hist_entry *c2c_he,
+ struct evsel *evsel)
+{
+ c2c_he->evsel = evsel;
+}
+
static void c2c_he__set_cpu(struct c2c_hist_entry *c2c_he,
struct perf_sample *sample)
{
@@ -334,6 +343,7 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused,
c2c_he__set_cpu(c2c_he, sample);
c2c_he__set_node(c2c_he, sample);
+ c2c_he__set_evsel(c2c_he, evsel);
hists__inc_nr_samples(&c2c_hists->hists, he->filtered);
ret = hist_entry__append_callchain(he, sample);
@@ -371,6 +381,7 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused,
c2c_he__set_cpu(c2c_he, sample);
c2c_he__set_node(c2c_he, sample);
+ c2c_he__set_evsel(c2c_he, evsel);
hists__inc_nr_samples(&c2c_hists->hists, he->filtered);
ret = hist_entry__append_callchain(he, sample);
@@ -2606,6 +2617,28 @@ c2c_cacheline_browser__new(struct hists *hists, struct hist_entry *he)
return browser;
}
+static int perf_c2c__toggle_annotation(struct hist_browser *browser)
+{
+ struct hist_entry *he = browser->he_selection;
+ struct symbol *sym = NULL;
+ struct c2c_hist_entry *c2c_he = NULL;
+
+ if (!he) {
+ ui_browser__help_window(&browser->b, "No entry selected for annotation");
+ return 0;
+ }
+ sym = (&he->ms)->sym;
+
+ if (sym == NULL) {
+ ui_browser__help_window(&browser->b, "Can not annotate, no symbol found");
+ return 0;
+ }
+
+ symbol__hists(sym, 0);
+ c2c_he = container_of(he, struct c2c_hist_entry, he);
+ return hist_entry__tui_annotate(he, c2c_he->evsel, NULL, he->ip);
+}
+
static int perf_c2c__browse_cacheline(struct hist_entry *he)
{
struct c2c_hist_entry *c2c_he;
@@ -2617,6 +2650,7 @@ static int perf_c2c__browse_cacheline(struct hist_entry *he)
" ENTER Toggle callchains (if present) \n"
" n Toggle Node details info \n"
" s Toggle full length of symbol and source line columns \n"
+ " a Toggle annotation view \n"
" q Return back to cacheline list \n";
if (!he)
@@ -2651,6 +2685,9 @@ static int perf_c2c__browse_cacheline(struct hist_entry *he)
c2c.node_info = (c2c.node_info + 1) % 3;
setup_nodes_header();
break;
+ case 'a':
+ perf_c2c__toggle_annotation(browser);
+ break;
case 'q':
goto out;
case '?':
@@ -2989,6 +3026,11 @@ static int setup_coalesce(const char *coalesce, bool no_source)
return 0;
}
+static bool perf_c2c__has_annotation(void)
+{
+ return use_browser == 1;
+}
+
static int perf_c2c__report(int argc, const char **argv)
{
struct itrace_synth_opts itrace_synth_opts = {
@@ -3006,6 +3048,8 @@ static int perf_c2c__report(int argc, const char **argv)
const char *display = NULL;
const char *coalesce = NULL;
bool no_source = false;
+ const char *disassembler_style = NULL, *objdump_path = NULL, *addr2line_path = NULL;
+
const struct option options[] = {
OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
"file", "vmlinux pathname"),
@@ -3033,6 +3077,12 @@ static int perf_c2c__report(int argc, const char **argv)
OPT_BOOLEAN(0, "stitch-lbr", &c2c.stitch_lbr,
"Enable LBR callgraph stitching approach"),
OPT_BOOLEAN(0, "double-cl", &chk_double_cl, "Detect adjacent cacheline false sharing"),
+ OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style",
+ "Specify disassembler style (e.g. -M intel for intel syntax)"),
+ OPT_STRING(0, "objdump", &objdump_path, "path",
+ "objdump binary to use for disassembly and annotations"),
+ OPT_STRING(0, "addr2line", &addr2line_path, "path",
+ "addr2line binary to use for line numbers"),
OPT_PARENT(c2c_options),
OPT_END()
};
@@ -3040,6 +3090,12 @@ static int perf_c2c__report(int argc, const char **argv)
const char *output_str, *sort_str = NULL;
struct perf_env *env;
+ annotation_options__init();
+
+ err = hists__init();
+ if (err < 0)
+ goto out;
+
argc = parse_options(argc, argv, options, report_c2c_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
if (argc)
@@ -3052,6 +3108,36 @@ static int perf_c2c__report(int argc, const char **argv)
if (c2c.stats_only)
c2c.use_stdio = true;
+ /**
+ * Annotation related options
+ * disassembler_style, objdump_path, addr2line_path
+ * are set in the c2c_options, so we can use them here.
+ */
+ if (disassembler_style) {
+ annotate_opts.disassembler_style = strdup(disassembler_style);
+ if (!annotate_opts.disassembler_style) {
+ err = -ENOMEM;
+ pr_err("Failed to allocate memory for annotation options\n");
+ goto out;
+ }
+ }
+ if (objdump_path) {
+ annotate_opts.objdump_path = strdup(objdump_path);
+ if (!annotate_opts.objdump_path) {
+ err = -ENOMEM;
+ pr_err("Failed to allocate memory for annotation options\n");
+ goto out;
+ }
+ }
+ if (addr2line_path) {
+ symbol_conf.addr2line_path = strdup(addr2line_path);
+ if (!symbol_conf.addr2line_path) {
+ err = -ENOMEM;
+ pr_err("Failed to allocate memory for annotation options\n");
+ goto out;
+ }
+ }
+
err = symbol__validate_sym_arguments();
if (err)
goto out;
@@ -3126,6 +3212,38 @@ static int perf_c2c__report(int argc, const char **argv)
if (err)
goto out_mem2node;
+ if (c2c.use_stdio)
+ use_browser = 0;
+ else
+ use_browser = 1;
+
+ /*
+ * Only in the TUI browser we are doing integrated annotation,
+ * so don't allocate extra space that won't be used in the stdio
+ * implementation.
+ */
+ if (perf_c2c__has_annotation()) {
+ int ret = symbol__annotation_init();
+
+ if (ret < 0)
+ goto out_mem2node;
+ /*
+ * For searching by name on the "Browse map details".
+ * providing it only in verbose mode not to bloat too
+ * much struct symbol.
+ */
+ if (verbose > 0) {
+ /*
+ * XXX: Need to provide a less kludgy way to ask for
+ * more space per symbol, the u32 is for the index on
+ * the ui browser.
+ * See symbol__browser_index.
+ */
+ symbol_conf.priv_size += sizeof(u32);
+ }
+ annotation_config__init();
+ }
+
if (symbol__init(env) < 0)
goto out_mem2node;
@@ -3135,11 +3253,6 @@ static int perf_c2c__report(int argc, const char **argv)
goto out_mem2node;
}
- if (c2c.use_stdio)
- use_browser = 0;
- else
- use_browser = 1;
-
setup_browser(false);
err = perf_session__process_events(session);
@@ -3210,6 +3323,7 @@ static int perf_c2c__report(int argc, const char **argv)
out_session:
perf_session__delete(session);
out:
+ annotation_options__exit();
return err;
}
diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c
index 183902dac042..7eb659c76b53 100644
--- a/tools/perf/ui/browsers/annotate.c
+++ b/tools/perf/ui/browsers/annotate.c
@@ -557,7 +557,7 @@ static bool annotate_browser__callq(struct annotate_browser *browser,
target_ms.map = ms->map;
target_ms.sym = dl->ops.target.sym;
annotation__unlock(notes);
- symbol__tui_annotate(&target_ms, evsel, hbt);
+ symbol__tui_annotate(&target_ms, evsel, hbt, NO_INITIAL_IP);
sym_title(ms->sym, ms->map, title, sizeof(title), annotate_opts.percent_type);
ui_browser__show_title(&browser->b, title);
return true;
@@ -814,6 +814,11 @@ static int annotate_browser__run(struct annotate_browser *browser,
annotate_browser__calc_percent(browser, evsel);
+ if (browser->curr_hot == NULL && browser->selection) {
+ disasm_rb_tree__insert(browser, browser->selection);
+ browser->curr_hot = rb_last(&browser->entries);
+ }
+
if (browser->curr_hot) {
annotate_browser__set_rb_top(browser, browser->curr_hot);
browser->b.navkeypressed = false;
@@ -1033,27 +1038,28 @@ static int annotate_browser__run(struct annotate_browser *browser,
}
int map_symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel,
- struct hist_browser_timer *hbt)
+ struct hist_browser_timer *hbt, u64 init_ip)
{
- return symbol__tui_annotate(ms, evsel, hbt);
+ return symbol__tui_annotate(ms, evsel, hbt, init_ip);
}
int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel,
- struct hist_browser_timer *hbt)
+ struct hist_browser_timer *hbt, u64 init_ip)
{
/* reset abort key so that it can get Ctrl-C as a key */
SLang_reset_tty();
SLang_init_tty(0, 0, 0);
SLtty_set_suspend_state(true);
- return map_symbol__tui_annotate(&he->ms, evsel, hbt);
+ return map_symbol__tui_annotate(&he->ms, evsel, hbt, init_ip);
}
int symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel,
- struct hist_browser_timer *hbt)
+ struct hist_browser_timer *hbt, u64 init_ip)
{
struct symbol *sym = ms->sym;
struct annotation *notes = symbol__annotation(sym);
+ struct disasm_line *dl = NULL;
struct annotate_browser browser = {
.b = {
.refresh = annotate_browser__refresh,
@@ -1093,6 +1099,18 @@ int symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel,
}
}
+ /*
+ * If init_ip is set, it means that there should be a line
+ * intentionally selected, not based on the percentages
+ * which caculated by the event sampling. In this case, we
+ * convey this information into the browser selection, where
+ * the selection in other cases should be empty.
+ */
+ if (init_ip != NO_INITIAL_IP) {
+ dl = find_disasm_line(sym, init_ip, false);
+ browser.selection = &dl->al;
+ }
+
ui_helpline__push("Press ESC to exit");
browser.b.width = notes->src->widths.max_line_len;
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c
index d9d3fb44477a..eec1b5c12a28 100644
--- a/tools/perf/ui/browsers/hists.c
+++ b/tools/perf/ui/browsers/hists.c
@@ -2484,7 +2484,7 @@ do_annotate(struct hist_browser *browser, struct popup_action *act)
else
evsel = hists_to_evsel(browser->hists);
- err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
+ err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt, NO_INITIAL_IP);
he = hist_browser__selected_entry(browser);
/*
* offer option to annotate the other branch source or target
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index 0dd475a744b6..682100196134 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -2544,7 +2544,7 @@ int annotate_get_insn_location(struct arch *arch, struct disasm_line *dl,
return 0;
}
-static struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip,
+struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip,
bool allow_update)
{
struct disasm_line *dl;
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h
index 8b5131d257b0..c4c897745698 100644
--- a/tools/perf/util/annotate.h
+++ b/tools/perf/util/annotate.h
@@ -170,6 +170,8 @@ static inline struct disasm_line *disasm_line(struct annotation_line *al)
return al ? container_of(al, struct disasm_line, al) : NULL;
}
+struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip,
+ bool allow_update);
/*
* Is this offset in the same function as the line it is used?
* asm functions jump to other functions, for instance.
@@ -473,7 +475,7 @@ int hist_entry__tty_annotate2(struct hist_entry *he, struct evsel *evsel);
#ifdef HAVE_SLANG_SUPPORT
int symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel,
- struct hist_browser_timer *hbt);
+ struct hist_browser_timer *hbt, u64 init_ip);
#else
static inline int symbol__tui_annotate(struct map_symbol *ms __maybe_unused,
struct evsel *evsel __maybe_unused,
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
index 70438d03ca9c..aca1e3151bcc 100644
--- a/tools/perf/util/hist.h
+++ b/tools/perf/util/hist.h
@@ -713,11 +713,13 @@ struct block_hist {
#include "../ui/keysyms.h"
void attr_to_script(char *buf, struct perf_event_attr *attr);
+#define NO_INITIAL_IP 0
+
int map_symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel,
- struct hist_browser_timer *hbt);
+ struct hist_browser_timer *hbt, u64 init_ip);
int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel,
- struct hist_browser_timer *hbt);
+ struct hist_browser_timer *hbt, u64 init_ip);
int evlist__tui_browse_hists(struct evlist *evlist, const char *help, struct hist_browser_timer *hbt,
float min_pcnt, struct perf_env *env, bool warn_lost_event);
--
2.47.1
On Tue, Aug 19, 2025 at 04:00:14PM +0800, Tianyou Li wrote: > Perf c2c report currently specified the code address and source:line > information in the cacheline browser, while it is lack of annotation > support like perf report to directly show the disassembly code for > the particular symbol shared that same cacheline. This patches add > a key 'a' binding to the cacheline browser which reuse the annotation > browser to show the disassembly view for easier analysis of cacheline > contentions. By default, the 'TAB' key navigate to the code address > where the contentions detected. There were changes in that codebase recently, can you please consider rebasing your work? This is something I really want to see in place, almost did it myself at some point :-) Please use what is in: https://git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next.git/log/?h=tmp.perf-tools-next - Arnaldo > Signed-off-by: Tianyou Li <tianyou.li@intel.com> > Reviewed-by: Dapeng Mi <dapeng1.mi@linux.intel.com> > Reviewed-by: Thomas Falcon <thomas.falcon@intel.com> > Reviewed-by: Jiebin Sun <jiebin.sun@intel.com> > Reviewed-by: Pan Deng <pan.deng@intel.com> > Reviewed-by: Zhiguo Zhou <zhiguo.zhou@intel.com> > Reviewed-by: Wangyang Guo <wangyang.guo@intel.com> > --- > tools/perf/builtin-annotate.c | 2 +- > tools/perf/builtin-c2c.c | 124 ++++++++++++++++++++++++++++-- > tools/perf/ui/browsers/annotate.c | 30 ++++++-- > tools/perf/ui/browsers/hists.c | 2 +- > tools/perf/util/annotate.c | 2 +- > tools/perf/util/annotate.h | 4 +- > tools/perf/util/hist.h | 6 +- > 7 files changed, 153 insertions(+), 17 deletions(-) > > diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c > index 5d57d2913f3d..8c896fbe76b7 100644 > --- a/tools/perf/builtin-annotate.c > +++ b/tools/perf/builtin-annotate.c > @@ -519,7 +519,7 @@ static void hists__find_annotations(struct hists *hists, > /* skip missing symbols */ > nd = rb_next(nd); > } else if (use_browser == 1) { > - key = hist_entry__tui_annotate(he, evsel, NULL); > + key = hist_entry__tui_annotate(he, evsel, NULL, NO_INITIAL_IP); > > switch (key) { > case -1: > diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c > index 9e9ff471ddd1..f753ec50b967 100644 > --- a/tools/perf/builtin-c2c.c > +++ b/tools/perf/builtin-c2c.c > @@ -45,6 +45,8 @@ > #include "pmus.h" > #include "string2.h" > #include "util/util.h" > +#include "util/symbol.h" > +#include "util/annotate.h" > > struct c2c_hists { > struct hists hists; > @@ -62,6 +64,7 @@ struct compute_stats { > > struct c2c_hist_entry { > struct c2c_hists *hists; > + struct evsel *evsel; > struct c2c_stats stats; > unsigned long *cpuset; > unsigned long *nodeset; > @@ -225,6 +228,12 @@ he__get_c2c_hists(struct hist_entry *he, > return hists; > } > > +static void c2c_he__set_evsel(struct c2c_hist_entry *c2c_he, > + struct evsel *evsel) > +{ > + c2c_he->evsel = evsel; > +} > + > static void c2c_he__set_cpu(struct c2c_hist_entry *c2c_he, > struct perf_sample *sample) > { > @@ -334,6 +343,7 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused, > > c2c_he__set_cpu(c2c_he, sample); > c2c_he__set_node(c2c_he, sample); > + c2c_he__set_evsel(c2c_he, evsel); > > hists__inc_nr_samples(&c2c_hists->hists, he->filtered); > ret = hist_entry__append_callchain(he, sample); > @@ -371,6 +381,7 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused, > > c2c_he__set_cpu(c2c_he, sample); > c2c_he__set_node(c2c_he, sample); > + c2c_he__set_evsel(c2c_he, evsel); > > hists__inc_nr_samples(&c2c_hists->hists, he->filtered); > ret = hist_entry__append_callchain(he, sample); > @@ -2606,6 +2617,28 @@ c2c_cacheline_browser__new(struct hists *hists, struct hist_entry *he) > return browser; > } > > +static int perf_c2c__toggle_annotation(struct hist_browser *browser) > +{ > + struct hist_entry *he = browser->he_selection; > + struct symbol *sym = NULL; > + struct c2c_hist_entry *c2c_he = NULL; > + > + if (!he) { > + ui_browser__help_window(&browser->b, "No entry selected for annotation"); > + return 0; > + } > + sym = (&he->ms)->sym; > + > + if (sym == NULL) { > + ui_browser__help_window(&browser->b, "Can not annotate, no symbol found"); > + return 0; > + } > + > + symbol__hists(sym, 0); > + c2c_he = container_of(he, struct c2c_hist_entry, he); > + return hist_entry__tui_annotate(he, c2c_he->evsel, NULL, he->ip); > +} > + > static int perf_c2c__browse_cacheline(struct hist_entry *he) > { > struct c2c_hist_entry *c2c_he; > @@ -2617,6 +2650,7 @@ static int perf_c2c__browse_cacheline(struct hist_entry *he) > " ENTER Toggle callchains (if present) \n" > " n Toggle Node details info \n" > " s Toggle full length of symbol and source line columns \n" > + " a Toggle annotation view \n" > " q Return back to cacheline list \n"; > > if (!he) > @@ -2651,6 +2685,9 @@ static int perf_c2c__browse_cacheline(struct hist_entry *he) > c2c.node_info = (c2c.node_info + 1) % 3; > setup_nodes_header(); > break; > + case 'a': > + perf_c2c__toggle_annotation(browser); > + break; > case 'q': > goto out; > case '?': > @@ -2989,6 +3026,11 @@ static int setup_coalesce(const char *coalesce, bool no_source) > return 0; > } > > +static bool perf_c2c__has_annotation(void) > +{ > + return use_browser == 1; > +} > + > static int perf_c2c__report(int argc, const char **argv) > { > struct itrace_synth_opts itrace_synth_opts = { > @@ -3006,6 +3048,8 @@ static int perf_c2c__report(int argc, const char **argv) > const char *display = NULL; > const char *coalesce = NULL; > bool no_source = false; > + const char *disassembler_style = NULL, *objdump_path = NULL, *addr2line_path = NULL; > + > const struct option options[] = { > OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, > "file", "vmlinux pathname"), > @@ -3033,6 +3077,12 @@ static int perf_c2c__report(int argc, const char **argv) > OPT_BOOLEAN(0, "stitch-lbr", &c2c.stitch_lbr, > "Enable LBR callgraph stitching approach"), > OPT_BOOLEAN(0, "double-cl", &chk_double_cl, "Detect adjacent cacheline false sharing"), > + OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style", > + "Specify disassembler style (e.g. -M intel for intel syntax)"), > + OPT_STRING(0, "objdump", &objdump_path, "path", > + "objdump binary to use for disassembly and annotations"), > + OPT_STRING(0, "addr2line", &addr2line_path, "path", > + "addr2line binary to use for line numbers"), > OPT_PARENT(c2c_options), > OPT_END() > }; > @@ -3040,6 +3090,12 @@ static int perf_c2c__report(int argc, const char **argv) > const char *output_str, *sort_str = NULL; > struct perf_env *env; > > + annotation_options__init(); > + > + err = hists__init(); > + if (err < 0) > + goto out; > + > argc = parse_options(argc, argv, options, report_c2c_usage, > PARSE_OPT_STOP_AT_NON_OPTION); > if (argc) > @@ -3052,6 +3108,36 @@ static int perf_c2c__report(int argc, const char **argv) > if (c2c.stats_only) > c2c.use_stdio = true; > > + /** > + * Annotation related options > + * disassembler_style, objdump_path, addr2line_path > + * are set in the c2c_options, so we can use them here. > + */ > + if (disassembler_style) { > + annotate_opts.disassembler_style = strdup(disassembler_style); > + if (!annotate_opts.disassembler_style) { > + err = -ENOMEM; > + pr_err("Failed to allocate memory for annotation options\n"); > + goto out; > + } > + } > + if (objdump_path) { > + annotate_opts.objdump_path = strdup(objdump_path); > + if (!annotate_opts.objdump_path) { > + err = -ENOMEM; > + pr_err("Failed to allocate memory for annotation options\n"); > + goto out; > + } > + } > + if (addr2line_path) { > + symbol_conf.addr2line_path = strdup(addr2line_path); > + if (!symbol_conf.addr2line_path) { > + err = -ENOMEM; > + pr_err("Failed to allocate memory for annotation options\n"); > + goto out; > + } > + } > + > err = symbol__validate_sym_arguments(); > if (err) > goto out; > @@ -3126,6 +3212,38 @@ static int perf_c2c__report(int argc, const char **argv) > if (err) > goto out_mem2node; > > + if (c2c.use_stdio) > + use_browser = 0; > + else > + use_browser = 1; > + > + /* > + * Only in the TUI browser we are doing integrated annotation, > + * so don't allocate extra space that won't be used in the stdio > + * implementation. > + */ > + if (perf_c2c__has_annotation()) { > + int ret = symbol__annotation_init(); > + > + if (ret < 0) > + goto out_mem2node; > + /* > + * For searching by name on the "Browse map details". > + * providing it only in verbose mode not to bloat too > + * much struct symbol. > + */ > + if (verbose > 0) { > + /* > + * XXX: Need to provide a less kludgy way to ask for > + * more space per symbol, the u32 is for the index on > + * the ui browser. > + * See symbol__browser_index. > + */ > + symbol_conf.priv_size += sizeof(u32); > + } > + annotation_config__init(); > + } > + > if (symbol__init(env) < 0) > goto out_mem2node; > > @@ -3135,11 +3253,6 @@ static int perf_c2c__report(int argc, const char **argv) > goto out_mem2node; > } > > - if (c2c.use_stdio) > - use_browser = 0; > - else > - use_browser = 1; > - > setup_browser(false); > > err = perf_session__process_events(session); > @@ -3210,6 +3323,7 @@ static int perf_c2c__report(int argc, const char **argv) > out_session: > perf_session__delete(session); > out: > + annotation_options__exit(); > return err; > } > > diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c > index 183902dac042..7eb659c76b53 100644 > --- a/tools/perf/ui/browsers/annotate.c > +++ b/tools/perf/ui/browsers/annotate.c > @@ -557,7 +557,7 @@ static bool annotate_browser__callq(struct annotate_browser *browser, > target_ms.map = ms->map; > target_ms.sym = dl->ops.target.sym; > annotation__unlock(notes); > - symbol__tui_annotate(&target_ms, evsel, hbt); > + symbol__tui_annotate(&target_ms, evsel, hbt, NO_INITIAL_IP); > sym_title(ms->sym, ms->map, title, sizeof(title), annotate_opts.percent_type); > ui_browser__show_title(&browser->b, title); > return true; > @@ -814,6 +814,11 @@ static int annotate_browser__run(struct annotate_browser *browser, > > annotate_browser__calc_percent(browser, evsel); > > + if (browser->curr_hot == NULL && browser->selection) { > + disasm_rb_tree__insert(browser, browser->selection); > + browser->curr_hot = rb_last(&browser->entries); > + } > + > if (browser->curr_hot) { > annotate_browser__set_rb_top(browser, browser->curr_hot); > browser->b.navkeypressed = false; > @@ -1033,27 +1038,28 @@ static int annotate_browser__run(struct annotate_browser *browser, > } > > int map_symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel, > - struct hist_browser_timer *hbt) > + struct hist_browser_timer *hbt, u64 init_ip) > { > - return symbol__tui_annotate(ms, evsel, hbt); > + return symbol__tui_annotate(ms, evsel, hbt, init_ip); > } > > int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel, > - struct hist_browser_timer *hbt) > + struct hist_browser_timer *hbt, u64 init_ip) > { > /* reset abort key so that it can get Ctrl-C as a key */ > SLang_reset_tty(); > SLang_init_tty(0, 0, 0); > SLtty_set_suspend_state(true); > > - return map_symbol__tui_annotate(&he->ms, evsel, hbt); > + return map_symbol__tui_annotate(&he->ms, evsel, hbt, init_ip); > } > > int symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel, > - struct hist_browser_timer *hbt) > + struct hist_browser_timer *hbt, u64 init_ip) > { > struct symbol *sym = ms->sym; > struct annotation *notes = symbol__annotation(sym); > + struct disasm_line *dl = NULL; > struct annotate_browser browser = { > .b = { > .refresh = annotate_browser__refresh, > @@ -1093,6 +1099,18 @@ int symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel, > } > } > > + /* > + * If init_ip is set, it means that there should be a line > + * intentionally selected, not based on the percentages > + * which caculated by the event sampling. In this case, we > + * convey this information into the browser selection, where > + * the selection in other cases should be empty. > + */ > + if (init_ip != NO_INITIAL_IP) { > + dl = find_disasm_line(sym, init_ip, false); > + browser.selection = &dl->al; > + } > + > ui_helpline__push("Press ESC to exit"); > > browser.b.width = notes->src->widths.max_line_len; > diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c > index d9d3fb44477a..eec1b5c12a28 100644 > --- a/tools/perf/ui/browsers/hists.c > +++ b/tools/perf/ui/browsers/hists.c > @@ -2484,7 +2484,7 @@ do_annotate(struct hist_browser *browser, struct popup_action *act) > else > evsel = hists_to_evsel(browser->hists); > > - err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt); > + err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt, NO_INITIAL_IP); > he = hist_browser__selected_entry(browser); > /* > * offer option to annotate the other branch source or target > diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c > index 0dd475a744b6..682100196134 100644 > --- a/tools/perf/util/annotate.c > +++ b/tools/perf/util/annotate.c > @@ -2544,7 +2544,7 @@ int annotate_get_insn_location(struct arch *arch, struct disasm_line *dl, > return 0; > } > > -static struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip, > +struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip, > bool allow_update) > { > struct disasm_line *dl; > diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h > index 8b5131d257b0..c4c897745698 100644 > --- a/tools/perf/util/annotate.h > +++ b/tools/perf/util/annotate.h > @@ -170,6 +170,8 @@ static inline struct disasm_line *disasm_line(struct annotation_line *al) > return al ? container_of(al, struct disasm_line, al) : NULL; > } > > +struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip, > + bool allow_update); > /* > * Is this offset in the same function as the line it is used? > * asm functions jump to other functions, for instance. > @@ -473,7 +475,7 @@ int hist_entry__tty_annotate2(struct hist_entry *he, struct evsel *evsel); > > #ifdef HAVE_SLANG_SUPPORT > int symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel, > - struct hist_browser_timer *hbt); > + struct hist_browser_timer *hbt, u64 init_ip); > #else > static inline int symbol__tui_annotate(struct map_symbol *ms __maybe_unused, > struct evsel *evsel __maybe_unused, > diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h > index 70438d03ca9c..aca1e3151bcc 100644 > --- a/tools/perf/util/hist.h > +++ b/tools/perf/util/hist.h > @@ -713,11 +713,13 @@ struct block_hist { > #include "../ui/keysyms.h" > void attr_to_script(char *buf, struct perf_event_attr *attr); > > +#define NO_INITIAL_IP 0 > + > int map_symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel, > - struct hist_browser_timer *hbt); > + struct hist_browser_timer *hbt, u64 init_ip); > > int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel, > - struct hist_browser_timer *hbt); > + struct hist_browser_timer *hbt, u64 init_ip); > > int evlist__tui_browse_hists(struct evlist *evlist, const char *help, struct hist_browser_timer *hbt, > float min_pcnt, struct perf_env *env, bool warn_lost_event); > -- > 2.47.1 >
On 9/3/2025 9:50 PM, Arnaldo Carvalho de Melo wrote: > On Tue, Aug 19, 2025 at 04:00:14PM +0800, Tianyou Li wrote: >> Perf c2c report currently specified the code address and source:line >> information in the cacheline browser, while it is lack of annotation >> support like perf report to directly show the disassembly code for >> the particular symbol shared that same cacheline. This patches add >> a key 'a' binding to the cacheline browser which reuse the annotation >> browser to show the disassembly view for easier analysis of cacheline >> contentions. By default, the 'TAB' key navigate to the code address >> where the contentions detected. > There were changes in that codebase recently, can you please consider > rebasing your work? > > This is something I really want to see in place, almost did it myself at > some point :-) > > Please use what is in: > > https://git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next.git/log/?h=tmp.perf-tools-next Thanks Arnaldo for your comments. Sorry for the late response due to other errands. I have rebased the code on top of tmp.perf-tools-next branch, sent out as v3 patch. Looking forward for your review comments, appreciated. Regards, Tianyou > - Arnaldo > >> Signed-off-by: Tianyou Li <tianyou.li@intel.com> >> Reviewed-by: Dapeng Mi <dapeng1.mi@linux.intel.com> >> Reviewed-by: Thomas Falcon <thomas.falcon@intel.com> >> Reviewed-by: Jiebin Sun <jiebin.sun@intel.com> >> Reviewed-by: Pan Deng <pan.deng@intel.com> >> Reviewed-by: Zhiguo Zhou <zhiguo.zhou@intel.com> >> Reviewed-by: Wangyang Guo <wangyang.guo@intel.com> >> --- >> tools/perf/builtin-annotate.c | 2 +- >> tools/perf/builtin-c2c.c | 124 ++++++++++++++++++++++++++++-- >> tools/perf/ui/browsers/annotate.c | 30 ++++++-- >> tools/perf/ui/browsers/hists.c | 2 +- >> tools/perf/util/annotate.c | 2 +- >> tools/perf/util/annotate.h | 4 +- >> tools/perf/util/hist.h | 6 +- >> 7 files changed, 153 insertions(+), 17 deletions(-) >> >> diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c >> index 5d57d2913f3d..8c896fbe76b7 100644 >> --- a/tools/perf/builtin-annotate.c >> +++ b/tools/perf/builtin-annotate.c >> @@ -519,7 +519,7 @@ static void hists__find_annotations(struct hists *hists, >> /* skip missing symbols */ >> nd = rb_next(nd); >> } else if (use_browser == 1) { >> - key = hist_entry__tui_annotate(he, evsel, NULL); >> + key = hist_entry__tui_annotate(he, evsel, NULL, NO_INITIAL_IP); >> >> switch (key) { >> case -1: >> diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c >> index 9e9ff471ddd1..f753ec50b967 100644 >> --- a/tools/perf/builtin-c2c.c >> +++ b/tools/perf/builtin-c2c.c >> @@ -45,6 +45,8 @@ >> #include "pmus.h" >> #include "string2.h" >> #include "util/util.h" >> +#include "util/symbol.h" >> +#include "util/annotate.h" >> >> struct c2c_hists { >> struct hists hists; >> @@ -62,6 +64,7 @@ struct compute_stats { >> >> struct c2c_hist_entry { >> struct c2c_hists *hists; >> + struct evsel *evsel; >> struct c2c_stats stats; >> unsigned long *cpuset; >> unsigned long *nodeset; >> @@ -225,6 +228,12 @@ he__get_c2c_hists(struct hist_entry *he, >> return hists; >> } >> >> +static void c2c_he__set_evsel(struct c2c_hist_entry *c2c_he, >> + struct evsel *evsel) >> +{ >> + c2c_he->evsel = evsel; >> +} >> + >> static void c2c_he__set_cpu(struct c2c_hist_entry *c2c_he, >> struct perf_sample *sample) >> { >> @@ -334,6 +343,7 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused, >> >> c2c_he__set_cpu(c2c_he, sample); >> c2c_he__set_node(c2c_he, sample); >> + c2c_he__set_evsel(c2c_he, evsel); >> >> hists__inc_nr_samples(&c2c_hists->hists, he->filtered); >> ret = hist_entry__append_callchain(he, sample); >> @@ -371,6 +381,7 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused, >> >> c2c_he__set_cpu(c2c_he, sample); >> c2c_he__set_node(c2c_he, sample); >> + c2c_he__set_evsel(c2c_he, evsel); >> >> hists__inc_nr_samples(&c2c_hists->hists, he->filtered); >> ret = hist_entry__append_callchain(he, sample); >> @@ -2606,6 +2617,28 @@ c2c_cacheline_browser__new(struct hists *hists, struct hist_entry *he) >> return browser; >> } >> >> +static int perf_c2c__toggle_annotation(struct hist_browser *browser) >> +{ >> + struct hist_entry *he = browser->he_selection; >> + struct symbol *sym = NULL; >> + struct c2c_hist_entry *c2c_he = NULL; >> + >> + if (!he) { >> + ui_browser__help_window(&browser->b, "No entry selected for annotation"); >> + return 0; >> + } >> + sym = (&he->ms)->sym; >> + >> + if (sym == NULL) { >> + ui_browser__help_window(&browser->b, "Can not annotate, no symbol found"); >> + return 0; >> + } >> + >> + symbol__hists(sym, 0); >> + c2c_he = container_of(he, struct c2c_hist_entry, he); >> + return hist_entry__tui_annotate(he, c2c_he->evsel, NULL, he->ip); >> +} >> + >> static int perf_c2c__browse_cacheline(struct hist_entry *he) >> { >> struct c2c_hist_entry *c2c_he; >> @@ -2617,6 +2650,7 @@ static int perf_c2c__browse_cacheline(struct hist_entry *he) >> " ENTER Toggle callchains (if present) \n" >> " n Toggle Node details info \n" >> " s Toggle full length of symbol and source line columns \n" >> + " a Toggle annotation view \n" >> " q Return back to cacheline list \n"; >> >> if (!he) >> @@ -2651,6 +2685,9 @@ static int perf_c2c__browse_cacheline(struct hist_entry *he) >> c2c.node_info = (c2c.node_info + 1) % 3; >> setup_nodes_header(); >> break; >> + case 'a': >> + perf_c2c__toggle_annotation(browser); >> + break; >> case 'q': >> goto out; >> case '?': >> @@ -2989,6 +3026,11 @@ static int setup_coalesce(const char *coalesce, bool no_source) >> return 0; >> } >> >> +static bool perf_c2c__has_annotation(void) >> +{ >> + return use_browser == 1; >> +} >> + >> static int perf_c2c__report(int argc, const char **argv) >> { >> struct itrace_synth_opts itrace_synth_opts = { >> @@ -3006,6 +3048,8 @@ static int perf_c2c__report(int argc, const char **argv) >> const char *display = NULL; >> const char *coalesce = NULL; >> bool no_source = false; >> + const char *disassembler_style = NULL, *objdump_path = NULL, *addr2line_path = NULL; >> + >> const struct option options[] = { >> OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, >> "file", "vmlinux pathname"), >> @@ -3033,6 +3077,12 @@ static int perf_c2c__report(int argc, const char **argv) >> OPT_BOOLEAN(0, "stitch-lbr", &c2c.stitch_lbr, >> "Enable LBR callgraph stitching approach"), >> OPT_BOOLEAN(0, "double-cl", &chk_double_cl, "Detect adjacent cacheline false sharing"), >> + OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style", >> + "Specify disassembler style (e.g. -M intel for intel syntax)"), >> + OPT_STRING(0, "objdump", &objdump_path, "path", >> + "objdump binary to use for disassembly and annotations"), >> + OPT_STRING(0, "addr2line", &addr2line_path, "path", >> + "addr2line binary to use for line numbers"), >> OPT_PARENT(c2c_options), >> OPT_END() >> }; >> @@ -3040,6 +3090,12 @@ static int perf_c2c__report(int argc, const char **argv) >> const char *output_str, *sort_str = NULL; >> struct perf_env *env; >> >> + annotation_options__init(); >> + >> + err = hists__init(); >> + if (err < 0) >> + goto out; >> + >> argc = parse_options(argc, argv, options, report_c2c_usage, >> PARSE_OPT_STOP_AT_NON_OPTION); >> if (argc) >> @@ -3052,6 +3108,36 @@ static int perf_c2c__report(int argc, const char **argv) >> if (c2c.stats_only) >> c2c.use_stdio = true; >> >> + /** >> + * Annotation related options >> + * disassembler_style, objdump_path, addr2line_path >> + * are set in the c2c_options, so we can use them here. >> + */ >> + if (disassembler_style) { >> + annotate_opts.disassembler_style = strdup(disassembler_style); >> + if (!annotate_opts.disassembler_style) { >> + err = -ENOMEM; >> + pr_err("Failed to allocate memory for annotation options\n"); >> + goto out; >> + } >> + } >> + if (objdump_path) { >> + annotate_opts.objdump_path = strdup(objdump_path); >> + if (!annotate_opts.objdump_path) { >> + err = -ENOMEM; >> + pr_err("Failed to allocate memory for annotation options\n"); >> + goto out; >> + } >> + } >> + if (addr2line_path) { >> + symbol_conf.addr2line_path = strdup(addr2line_path); >> + if (!symbol_conf.addr2line_path) { >> + err = -ENOMEM; >> + pr_err("Failed to allocate memory for annotation options\n"); >> + goto out; >> + } >> + } >> + >> err = symbol__validate_sym_arguments(); >> if (err) >> goto out; >> @@ -3126,6 +3212,38 @@ static int perf_c2c__report(int argc, const char **argv) >> if (err) >> goto out_mem2node; >> >> + if (c2c.use_stdio) >> + use_browser = 0; >> + else >> + use_browser = 1; >> + >> + /* >> + * Only in the TUI browser we are doing integrated annotation, >> + * so don't allocate extra space that won't be used in the stdio >> + * implementation. >> + */ >> + if (perf_c2c__has_annotation()) { >> + int ret = symbol__annotation_init(); >> + >> + if (ret < 0) >> + goto out_mem2node; >> + /* >> + * For searching by name on the "Browse map details". >> + * providing it only in verbose mode not to bloat too >> + * much struct symbol. >> + */ >> + if (verbose > 0) { >> + /* >> + * XXX: Need to provide a less kludgy way to ask for >> + * more space per symbol, the u32 is for the index on >> + * the ui browser. >> + * See symbol__browser_index. >> + */ >> + symbol_conf.priv_size += sizeof(u32); >> + } >> + annotation_config__init(); >> + } >> + >> if (symbol__init(env) < 0) >> goto out_mem2node; >> >> @@ -3135,11 +3253,6 @@ static int perf_c2c__report(int argc, const char **argv) >> goto out_mem2node; >> } >> >> - if (c2c.use_stdio) >> - use_browser = 0; >> - else >> - use_browser = 1; >> - >> setup_browser(false); >> >> err = perf_session__process_events(session); >> @@ -3210,6 +3323,7 @@ static int perf_c2c__report(int argc, const char **argv) >> out_session: >> perf_session__delete(session); >> out: >> + annotation_options__exit(); >> return err; >> } >> >> diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c >> index 183902dac042..7eb659c76b53 100644 >> --- a/tools/perf/ui/browsers/annotate.c >> +++ b/tools/perf/ui/browsers/annotate.c >> @@ -557,7 +557,7 @@ static bool annotate_browser__callq(struct annotate_browser *browser, >> target_ms.map = ms->map; >> target_ms.sym = dl->ops.target.sym; >> annotation__unlock(notes); >> - symbol__tui_annotate(&target_ms, evsel, hbt); >> + symbol__tui_annotate(&target_ms, evsel, hbt, NO_INITIAL_IP); >> sym_title(ms->sym, ms->map, title, sizeof(title), annotate_opts.percent_type); >> ui_browser__show_title(&browser->b, title); >> return true; >> @@ -814,6 +814,11 @@ static int annotate_browser__run(struct annotate_browser *browser, >> >> annotate_browser__calc_percent(browser, evsel); >> >> + if (browser->curr_hot == NULL && browser->selection) { >> + disasm_rb_tree__insert(browser, browser->selection); >> + browser->curr_hot = rb_last(&browser->entries); >> + } >> + >> if (browser->curr_hot) { >> annotate_browser__set_rb_top(browser, browser->curr_hot); >> browser->b.navkeypressed = false; >> @@ -1033,27 +1038,28 @@ static int annotate_browser__run(struct annotate_browser *browser, >> } >> >> int map_symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel, >> - struct hist_browser_timer *hbt) >> + struct hist_browser_timer *hbt, u64 init_ip) >> { >> - return symbol__tui_annotate(ms, evsel, hbt); >> + return symbol__tui_annotate(ms, evsel, hbt, init_ip); >> } >> >> int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel, >> - struct hist_browser_timer *hbt) >> + struct hist_browser_timer *hbt, u64 init_ip) >> { >> /* reset abort key so that it can get Ctrl-C as a key */ >> SLang_reset_tty(); >> SLang_init_tty(0, 0, 0); >> SLtty_set_suspend_state(true); >> >> - return map_symbol__tui_annotate(&he->ms, evsel, hbt); >> + return map_symbol__tui_annotate(&he->ms, evsel, hbt, init_ip); >> } >> >> int symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel, >> - struct hist_browser_timer *hbt) >> + struct hist_browser_timer *hbt, u64 init_ip) >> { >> struct symbol *sym = ms->sym; >> struct annotation *notes = symbol__annotation(sym); >> + struct disasm_line *dl = NULL; >> struct annotate_browser browser = { >> .b = { >> .refresh = annotate_browser__refresh, >> @@ -1093,6 +1099,18 @@ int symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel, >> } >> } >> >> + /* >> + * If init_ip is set, it means that there should be a line >> + * intentionally selected, not based on the percentages >> + * which caculated by the event sampling. In this case, we >> + * convey this information into the browser selection, where >> + * the selection in other cases should be empty. >> + */ >> + if (init_ip != NO_INITIAL_IP) { >> + dl = find_disasm_line(sym, init_ip, false); >> + browser.selection = &dl->al; >> + } >> + >> ui_helpline__push("Press ESC to exit"); >> >> browser.b.width = notes->src->widths.max_line_len; >> diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c >> index d9d3fb44477a..eec1b5c12a28 100644 >> --- a/tools/perf/ui/browsers/hists.c >> +++ b/tools/perf/ui/browsers/hists.c >> @@ -2484,7 +2484,7 @@ do_annotate(struct hist_browser *browser, struct popup_action *act) >> else >> evsel = hists_to_evsel(browser->hists); >> >> - err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt); >> + err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt, NO_INITIAL_IP); >> he = hist_browser__selected_entry(browser); >> /* >> * offer option to annotate the other branch source or target >> diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c >> index 0dd475a744b6..682100196134 100644 >> --- a/tools/perf/util/annotate.c >> +++ b/tools/perf/util/annotate.c >> @@ -2544,7 +2544,7 @@ int annotate_get_insn_location(struct arch *arch, struct disasm_line *dl, >> return 0; >> } >> >> -static struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip, >> +struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip, >> bool allow_update) >> { >> struct disasm_line *dl; >> diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h >> index 8b5131d257b0..c4c897745698 100644 >> --- a/tools/perf/util/annotate.h >> +++ b/tools/perf/util/annotate.h >> @@ -170,6 +170,8 @@ static inline struct disasm_line *disasm_line(struct annotation_line *al) >> return al ? container_of(al, struct disasm_line, al) : NULL; >> } >> >> +struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip, >> + bool allow_update); >> /* >> * Is this offset in the same function as the line it is used? >> * asm functions jump to other functions, for instance. >> @@ -473,7 +475,7 @@ int hist_entry__tty_annotate2(struct hist_entry *he, struct evsel *evsel); >> >> #ifdef HAVE_SLANG_SUPPORT >> int symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel, >> - struct hist_browser_timer *hbt); >> + struct hist_browser_timer *hbt, u64 init_ip); >> #else >> static inline int symbol__tui_annotate(struct map_symbol *ms __maybe_unused, >> struct evsel *evsel __maybe_unused, >> diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h >> index 70438d03ca9c..aca1e3151bcc 100644 >> --- a/tools/perf/util/hist.h >> +++ b/tools/perf/util/hist.h >> @@ -713,11 +713,13 @@ struct block_hist { >> #include "../ui/keysyms.h" >> void attr_to_script(char *buf, struct perf_event_attr *attr); >> >> +#define NO_INITIAL_IP 0 >> + >> int map_symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel, >> - struct hist_browser_timer *hbt); >> + struct hist_browser_timer *hbt, u64 init_ip); >> >> int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel, >> - struct hist_browser_timer *hbt); >> + struct hist_browser_timer *hbt, u64 init_ip); >> >> int evlist__tui_browse_hists(struct evlist *evlist, const char *help, struct hist_browser_timer *hbt, >> float min_pcnt, struct perf_env *env, bool warn_lost_event); >> -- >> 2.47.1 >>
Perf c2c report currently specified the code address and source:line
information in the cacheline browser, while it is lack of annotation
support like perf report to directly show the disassembly code for
the particular symbol shared that same cacheline. This patches add
a key 'a' binding to the cacheline browser which reuse the annotation
browser to show the disassembly view for easier analysis of cacheline
contentions. By default, the 'TAB' key navigate to the code address
where the contentions detected.
Signed-off-by: Tianyou Li <tianyou.li@intel.com>
Reviewed-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
Reviewed-by: Thomas Falcon <thomas.falcon@intel.com>
Reviewed-by: Jiebin Sun <jiebin.sun@intel.com>
Reviewed-by: Pan Deng <pan.deng@intel.com>
Reviewed-by: Zhiguo Zhou <zhiguo.zhou@intel.com>
Reviewed-by: Wangyang Guo <wangyang.guo@intel.com>
---
tools/perf/builtin-annotate.c | 2 +-
tools/perf/builtin-c2c.c | 124 ++++++++++++++++++++++++++++--
tools/perf/ui/browsers/annotate.c | 40 +++++++++-
tools/perf/ui/browsers/hists.c | 2 +-
tools/perf/util/annotate.c | 2 +-
tools/perf/util/annotate.h | 2 +
tools/perf/util/hist.h | 6 +-
7 files changed, 164 insertions(+), 14 deletions(-)
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 646f43b0f7c4..f977e97a9c96 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -519,7 +519,7 @@ static void hists__find_annotations(struct hists *hists,
/* skip missing symbols */
nd = rb_next(nd);
} else if (use_browser == 1) {
- key = hist_entry__tui_annotate(he, evsel, NULL);
+ key = hist_entry__tui_annotate(he, evsel, NULL, NO_INITIAL_IP);
switch (key) {
case -1:
diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c
index 9e9ff471ddd1..f5702d218490 100644
--- a/tools/perf/builtin-c2c.c
+++ b/tools/perf/builtin-c2c.c
@@ -45,6 +45,8 @@
#include "pmus.h"
#include "string2.h"
#include "util/util.h"
+#include "util/symbol.h"
+#include "util/annotate.h"
struct c2c_hists {
struct hists hists;
@@ -62,6 +64,7 @@ struct compute_stats {
struct c2c_hist_entry {
struct c2c_hists *hists;
+ struct evsel *evsel;
struct c2c_stats stats;
unsigned long *cpuset;
unsigned long *nodeset;
@@ -225,6 +228,12 @@ he__get_c2c_hists(struct hist_entry *he,
return hists;
}
+static void c2c_he__set_evsel(struct c2c_hist_entry *c2c_he,
+ struct evsel *evsel)
+{
+ c2c_he->evsel = evsel;
+}
+
static void c2c_he__set_cpu(struct c2c_hist_entry *c2c_he,
struct perf_sample *sample)
{
@@ -334,6 +343,7 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused,
c2c_he__set_cpu(c2c_he, sample);
c2c_he__set_node(c2c_he, sample);
+ c2c_he__set_evsel(c2c_he, evsel);
hists__inc_nr_samples(&c2c_hists->hists, he->filtered);
ret = hist_entry__append_callchain(he, sample);
@@ -371,6 +381,7 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused,
c2c_he__set_cpu(c2c_he, sample);
c2c_he__set_node(c2c_he, sample);
+ c2c_he__set_evsel(c2c_he, evsel);
hists__inc_nr_samples(&c2c_hists->hists, he->filtered);
ret = hist_entry__append_callchain(he, sample);
@@ -2550,6 +2561,29 @@ static void perf_c2c__hists_fprintf(FILE *out, struct perf_session *session)
}
#ifdef HAVE_SLANG_SUPPORT
+
+static int perf_c2c__toggle_annotation(struct hist_browser *browser)
+{
+ struct hist_entry *he = browser->he_selection;
+ struct symbol *sym = NULL;
+ struct c2c_hist_entry *c2c_he = NULL;
+
+ if (!he) {
+ ui_browser__help_window(&browser->b, "No entry selected for annotation");
+ return 0;
+ }
+ sym = (&he->ms)->sym;
+
+ if (sym == NULL) {
+ ui_browser__help_window(&browser->b, "Can not annotate, no symbol found");
+ return 0;
+ }
+
+ symbol__hists(sym, 0);
+ c2c_he = container_of(he, struct c2c_hist_entry, he);
+ return hist_entry__tui_annotate(he, c2c_he->evsel, NULL, he->ip);
+}
+
static void c2c_browser__update_nr_entries(struct hist_browser *hb)
{
u64 nr_entries = 0;
@@ -2617,6 +2651,7 @@ static int perf_c2c__browse_cacheline(struct hist_entry *he)
" ENTER Toggle callchains (if present) \n"
" n Toggle Node details info \n"
" s Toggle full length of symbol and source line columns \n"
+ " a Toggle annotation view \n"
" q Return back to cacheline list \n";
if (!he)
@@ -2651,6 +2686,9 @@ static int perf_c2c__browse_cacheline(struct hist_entry *he)
c2c.node_info = (c2c.node_info + 1) % 3;
setup_nodes_header();
break;
+ case 'a':
+ perf_c2c__toggle_annotation(browser);
+ break;
case 'q':
goto out;
case '?':
@@ -2989,6 +3027,11 @@ static int setup_coalesce(const char *coalesce, bool no_source)
return 0;
}
+static bool perf_c2c__has_annotation(void)
+{
+ return use_browser == 1;
+}
+
static int perf_c2c__report(int argc, const char **argv)
{
struct itrace_synth_opts itrace_synth_opts = {
@@ -3006,6 +3049,7 @@ static int perf_c2c__report(int argc, const char **argv)
const char *display = NULL;
const char *coalesce = NULL;
bool no_source = false;
+ const char *disassembler_style = NULL, *objdump_path = NULL, *addr2line_path = NULL;
const struct option options[] = {
OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
"file", "vmlinux pathname"),
@@ -3033,6 +3077,12 @@ static int perf_c2c__report(int argc, const char **argv)
OPT_BOOLEAN(0, "stitch-lbr", &c2c.stitch_lbr,
"Enable LBR callgraph stitching approach"),
OPT_BOOLEAN(0, "double-cl", &chk_double_cl, "Detect adjacent cacheline false sharing"),
+ OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style",
+ "Specify disassembler style (e.g. -M intel for intel syntax)"),
+ OPT_STRING(0, "objdump", &objdump_path, "path",
+ "objdump binary to use for disassembly and annotations"),
+ OPT_STRING(0, "addr2line", &addr2line_path, "path",
+ "addr2line binary to use for line numbers"),
OPT_PARENT(c2c_options),
OPT_END()
};
@@ -3040,6 +3090,12 @@ static int perf_c2c__report(int argc, const char **argv)
const char *output_str, *sort_str = NULL;
struct perf_env *env;
+ annotation_options__init();
+
+ err = hists__init();
+ if (err < 0)
+ goto out;
+
argc = parse_options(argc, argv, options, report_c2c_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
if (argc)
@@ -3052,6 +3108,36 @@ static int perf_c2c__report(int argc, const char **argv)
if (c2c.stats_only)
c2c.use_stdio = true;
+ /**
+ * Annotation related options
+ * disassembler_style, objdump_path, addr2line_path
+ * are set in the c2c_options, so we can use them here.
+ */
+ if (disassembler_style) {
+ annotate_opts.disassembler_style = strdup(disassembler_style);
+ if (!annotate_opts.disassembler_style) {
+ err = -ENOMEM;
+ pr_err("Failed to allocate memory for annotation options\n");
+ goto out;
+ }
+ }
+ if (objdump_path) {
+ annotate_opts.objdump_path = strdup(objdump_path);
+ if (!annotate_opts.objdump_path) {
+ err = -ENOMEM;
+ pr_err("Failed to allocate memory for annotation options\n");
+ goto out;
+ }
+ }
+ if (addr2line_path) {
+ symbol_conf.addr2line_path = strdup(addr2line_path);
+ if (!symbol_conf.addr2line_path) {
+ err = -ENOMEM;
+ pr_err("Failed to allocate memory for annotation options\n");
+ goto out;
+ }
+ }
+
err = symbol__validate_sym_arguments();
if (err)
goto out;
@@ -3126,6 +3212,38 @@ static int perf_c2c__report(int argc, const char **argv)
if (err)
goto out_mem2node;
+ if (c2c.use_stdio)
+ use_browser = 0;
+ else
+ use_browser = 1;
+
+ /*
+ * Only in the TUI browser we are doing integrated annotation,
+ * so don't allocate extra space that won't be used in the stdio
+ * implementation.
+ */
+ if (perf_c2c__has_annotation()) {
+ int ret = symbol__annotation_init();
+
+ if (ret < 0)
+ goto out_mem2node;
+ /*
+ * For searching by name on the "Browse map details".
+ * providing it only in verbose mode not to bloat too
+ * much struct symbol.
+ */
+ if (verbose > 0) {
+ /*
+ * XXX: Need to provide a less kludgy way to ask for
+ * more space per symbol, the u32 is for the index on
+ * the ui browser.
+ * See symbol__browser_index.
+ */
+ symbol_conf.priv_size += sizeof(u32);
+ }
+ annotation_config__init();
+ }
+
if (symbol__init(env) < 0)
goto out_mem2node;
@@ -3135,11 +3253,6 @@ static int perf_c2c__report(int argc, const char **argv)
goto out_mem2node;
}
- if (c2c.use_stdio)
- use_browser = 0;
- else
- use_browser = 1;
-
setup_browser(false);
err = perf_session__process_events(session);
@@ -3210,6 +3323,7 @@ static int perf_c2c__report(int argc, const char **argv)
out_session:
perf_session__delete(session);
out:
+ annotation_options__exit();
return err;
}
diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c
index b770a8d4623e..c2afa3624917 100644
--- a/tools/perf/ui/browsers/annotate.c
+++ b/tools/perf/ui/browsers/annotate.c
@@ -592,7 +592,7 @@ static bool annotate_browser__callq(struct annotate_browser *browser,
target_ms.map = ms->map;
target_ms.sym = dl->ops.target.sym;
annotation__unlock(notes);
- __hist_entry__tui_annotate(browser->he, &target_ms, evsel, hbt);
+ __hist_entry__tui_annotate(browser->he, &target_ms, evsel, hbt, NO_INITIAL_IP);
sym_title(ms->sym, ms->map, title, sizeof(title), annotate_opts.percent_type);
ui_browser__show_title(&browser->b, title);
return true;
@@ -854,6 +854,7 @@ static int annotate_browser__run(struct annotate_browser *browser,
const char *help = "Press 'h' for help on key bindings";
int delay_secs = hbt ? hbt->refresh : 0;
char *br_cntr_text = NULL;
+ u64 init_ip = 0;
char title[256];
int key;
@@ -863,6 +864,13 @@ static int annotate_browser__run(struct annotate_browser *browser,
annotate_browser__calc_percent(browser, evsel);
+ /* the selection are intentionally even not from the sample percentage */
+ if (browser->entries.rb_node == NULL && browser->selection) {
+ init_ip = sym->start + browser->selection->offset;
+ disasm_rb_tree__insert(browser, browser->selection);
+ browser->curr_hot = rb_last(&browser->entries);
+ }
+
if (browser->curr_hot) {
annotate_browser__set_rb_top(browser, browser->curr_hot);
browser->b.navkeypressed = false;
@@ -963,6 +971,17 @@ static int annotate_browser__run(struct annotate_browser *browser,
ui_helpline__puts(help);
annotate__scnprintf_title(hists, title, sizeof(title));
annotate_browser__show(&browser->b, title, help);
+ /* Previous RB tree may not valid, need refresh according to new entries*/
+ if (init_ip != 0) {
+ struct disasm_line *dl = find_disasm_line(sym, init_ip, true);
+ browser->curr_hot = NULL;
+ if (dl != NULL) {
+ browser->entries.rb_node = NULL;
+ disasm_rb_tree__insert(browser, &dl->al);
+ browser->curr_hot = rb_last(&browser->entries);
+ }
+ nd = browser->curr_hot;
+ }
continue;
case 'o':
annotate_opts.use_offset = !annotate_opts.use_offset;
@@ -1096,22 +1115,23 @@ static int annotate_browser__run(struct annotate_browser *browser,
}
int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel,
- struct hist_browser_timer *hbt)
+ struct hist_browser_timer *hbt, u64 init_ip)
{
/* reset abort key so that it can get Ctrl-C as a key */
SLang_reset_tty();
SLang_init_tty(0, 0, 0);
SLtty_set_suspend_state(true);
- return __hist_entry__tui_annotate(he, &he->ms, evsel, hbt);
+ return __hist_entry__tui_annotate(he, &he->ms, evsel, hbt, init_ip);
}
int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms,
struct evsel *evsel,
- struct hist_browser_timer *hbt)
+ struct hist_browser_timer *hbt, u64 init_ip)
{
struct symbol *sym = ms->sym;
struct annotation *notes = symbol__annotation(sym);
+ struct disasm_line *dl = NULL;
struct annotate_browser browser = {
.b = {
.refresh = annotate_browser__refresh,
@@ -1163,6 +1183,18 @@ int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms,
browser.he = &annotate_he;
}
+ /*
+ * If init_ip is set, it means that there should be a line
+ * intentionally selected, not based on the percentages
+ * which caculated by the event sampling. In this case, we
+ * convey this information into the browser selection, where
+ * the selection in other cases should be empty.
+ */
+ if (init_ip != NO_INITIAL_IP) {
+ dl = find_disasm_line(sym, init_ip, false);
+ browser.selection = &dl->al;
+ }
+
ui_helpline__push("Press ESC to exit");
if (annotate_opts.code_with_type) {
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c
index 487c0b08c003..3675a703de11 100644
--- a/tools/perf/ui/browsers/hists.c
+++ b/tools/perf/ui/browsers/hists.c
@@ -2485,7 +2485,7 @@ do_annotate(struct hist_browser *browser, struct popup_action *act)
evsel = hists_to_evsel(browser->hists);
he = hist_browser__selected_entry(browser);
- err = __hist_entry__tui_annotate(he, &act->ms, evsel, browser->hbt);
+ err = __hist_entry__tui_annotate(he, &act->ms, evsel, browser->hbt, NO_INITIAL_IP);
/*
* offer option to annotate the other branch source or target
* (if they exists) when returning from annotate
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index c9b220d9f924..937effbeda69 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -2622,7 +2622,7 @@ int annotate_get_insn_location(struct arch *arch, struct disasm_line *dl,
return 0;
}
-static struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip,
+struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip,
bool allow_update)
{
struct disasm_line *dl;
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h
index eaf6c8aa7f47..bbe67588bbdd 100644
--- a/tools/perf/util/annotate.h
+++ b/tools/perf/util/annotate.h
@@ -170,6 +170,8 @@ static inline struct disasm_line *disasm_line(struct annotation_line *al)
return al ? container_of(al, struct disasm_line, al) : NULL;
}
+struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip, bool allow_update);
+
/*
* Is this offset in the same function as the line it is used?
* asm functions jump to other functions, for instance.
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
index c64005278687..e544e1795f19 100644
--- a/tools/perf/util/hist.h
+++ b/tools/perf/util/hist.h
@@ -713,12 +713,14 @@ struct block_hist {
#include "../ui/keysyms.h"
void attr_to_script(char *buf, struct perf_event_attr *attr);
+#define NO_INITIAL_IP 0
+
int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms,
struct evsel *evsel,
- struct hist_browser_timer *hbt);
+ struct hist_browser_timer *hbt, u64 init_ip);
int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel,
- struct hist_browser_timer *hbt);
+ struct hist_browser_timer *hbt, u64 init_ip);
int evlist__tui_browse_hists(struct evlist *evlist, const char *help, struct hist_browser_timer *hbt,
float min_pcnt, struct perf_env *env, bool warn_lost_event);
--
2.47.1
Hello, On Sun, Sep 07, 2025 at 11:25:10PM +0800, Tianyou Li wrote: > Perf c2c report currently specified the code address and source:line > information in the cacheline browser, while it is lack of annotation > support like perf report to directly show the disassembly code for > the particular symbol shared that same cacheline. This patches add > a key 'a' binding to the cacheline browser which reuse the annotation > browser to show the disassembly view for easier analysis of cacheline > contentions. By default, the 'TAB' key navigate to the code address > where the contentions detected. > > Signed-off-by: Tianyou Li <tianyou.li@intel.com> > Reviewed-by: Dapeng Mi <dapeng1.mi@linux.intel.com> > Reviewed-by: Thomas Falcon <thomas.falcon@intel.com> > Reviewed-by: Jiebin Sun <jiebin.sun@intel.com> > Reviewed-by: Pan Deng <pan.deng@intel.com> > Reviewed-by: Zhiguo Zhou <zhiguo.zhou@intel.com> > Reviewed-by: Wangyang Guo <wangyang.guo@intel.com> > --- > tools/perf/builtin-annotate.c | 2 +- > tools/perf/builtin-c2c.c | 124 ++++++++++++++++++++++++++++-- > tools/perf/ui/browsers/annotate.c | 40 +++++++++- > tools/perf/ui/browsers/hists.c | 2 +- > tools/perf/util/annotate.c | 2 +- > tools/perf/util/annotate.h | 2 + > tools/perf/util/hist.h | 6 +- > 7 files changed, 164 insertions(+), 14 deletions(-) > > diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c > index 646f43b0f7c4..f977e97a9c96 100644 > --- a/tools/perf/builtin-annotate.c > +++ b/tools/perf/builtin-annotate.c > @@ -519,7 +519,7 @@ static void hists__find_annotations(struct hists *hists, > /* skip missing symbols */ > nd = rb_next(nd); > } else if (use_browser == 1) { > - key = hist_entry__tui_annotate(he, evsel, NULL); > + key = hist_entry__tui_annotate(he, evsel, NULL, NO_INITIAL_IP); > > switch (key) { > case -1: > diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c > index 9e9ff471ddd1..f5702d218490 100644 > --- a/tools/perf/builtin-c2c.c > +++ b/tools/perf/builtin-c2c.c > @@ -45,6 +45,8 @@ > #include "pmus.h" > #include "string2.h" > #include "util/util.h" > +#include "util/symbol.h" > +#include "util/annotate.h" > > struct c2c_hists { > struct hists hists; > @@ -62,6 +64,7 @@ struct compute_stats { > > struct c2c_hist_entry { > struct c2c_hists *hists; > + struct evsel *evsel; > struct c2c_stats stats; > unsigned long *cpuset; > unsigned long *nodeset; > @@ -225,6 +228,12 @@ he__get_c2c_hists(struct hist_entry *he, > return hists; > } > > +static void c2c_he__set_evsel(struct c2c_hist_entry *c2c_he, > + struct evsel *evsel) > +{ > + c2c_he->evsel = evsel; > +} > + > static void c2c_he__set_cpu(struct c2c_hist_entry *c2c_he, > struct perf_sample *sample) > { > @@ -334,6 +343,7 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused, > > c2c_he__set_cpu(c2c_he, sample); > c2c_he__set_node(c2c_he, sample); > + c2c_he__set_evsel(c2c_he, evsel); > > hists__inc_nr_samples(&c2c_hists->hists, he->filtered); > ret = hist_entry__append_callchain(he, sample); > @@ -371,6 +381,7 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused, > > c2c_he__set_cpu(c2c_he, sample); > c2c_he__set_node(c2c_he, sample); > + c2c_he__set_evsel(c2c_he, evsel); > > hists__inc_nr_samples(&c2c_hists->hists, he->filtered); > ret = hist_entry__append_callchain(he, sample); > @@ -2550,6 +2561,29 @@ static void perf_c2c__hists_fprintf(FILE *out, struct perf_session *session) > } > > #ifdef HAVE_SLANG_SUPPORT > + > +static int perf_c2c__toggle_annotation(struct hist_browser *browser) > +{ > + struct hist_entry *he = browser->he_selection; > + struct symbol *sym = NULL; > + struct c2c_hist_entry *c2c_he = NULL; > + > + if (!he) { > + ui_browser__help_window(&browser->b, "No entry selected for annotation"); > + return 0; > + } > + sym = (&he->ms)->sym; > + > + if (sym == NULL) { > + ui_browser__help_window(&browser->b, "Can not annotate, no symbol found"); > + return 0; > + } > + > + symbol__hists(sym, 0); > + c2c_he = container_of(he, struct c2c_hist_entry, he); > + return hist_entry__tui_annotate(he, c2c_he->evsel, NULL, he->ip); > +} > + > static void c2c_browser__update_nr_entries(struct hist_browser *hb) > { > u64 nr_entries = 0; > @@ -2617,6 +2651,7 @@ static int perf_c2c__browse_cacheline(struct hist_entry *he) > " ENTER Toggle callchains (if present) \n" > " n Toggle Node details info \n" > " s Toggle full length of symbol and source line columns \n" > + " a Toggle annotation view \n" > " q Return back to cacheline list \n"; > > if (!he) > @@ -2651,6 +2686,9 @@ static int perf_c2c__browse_cacheline(struct hist_entry *he) > c2c.node_info = (c2c.node_info + 1) % 3; > setup_nodes_header(); > break; > + case 'a': > + perf_c2c__toggle_annotation(browser); > + break; > case 'q': > goto out; > case '?': > @@ -2989,6 +3027,11 @@ static int setup_coalesce(const char *coalesce, bool no_source) > return 0; > } > > +static bool perf_c2c__has_annotation(void) > +{ > + return use_browser == 1; > +} Can you just use ui__has_annotation()? It should make sure if he->ms.sym is valid which means you have 'sym' sort key. Thanks, Namhyung > + > static int perf_c2c__report(int argc, const char **argv) > { > struct itrace_synth_opts itrace_synth_opts = { > @@ -3006,6 +3049,7 @@ static int perf_c2c__report(int argc, const char **argv) > const char *display = NULL; > const char *coalesce = NULL; > bool no_source = false; > + const char *disassembler_style = NULL, *objdump_path = NULL, *addr2line_path = NULL; > const struct option options[] = { > OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, > "file", "vmlinux pathname"), > @@ -3033,6 +3077,12 @@ static int perf_c2c__report(int argc, const char **argv) > OPT_BOOLEAN(0, "stitch-lbr", &c2c.stitch_lbr, > "Enable LBR callgraph stitching approach"), > OPT_BOOLEAN(0, "double-cl", &chk_double_cl, "Detect adjacent cacheline false sharing"), > + OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style", > + "Specify disassembler style (e.g. -M intel for intel syntax)"), > + OPT_STRING(0, "objdump", &objdump_path, "path", > + "objdump binary to use for disassembly and annotations"), > + OPT_STRING(0, "addr2line", &addr2line_path, "path", > + "addr2line binary to use for line numbers"), > OPT_PARENT(c2c_options), > OPT_END() > }; > @@ -3040,6 +3090,12 @@ static int perf_c2c__report(int argc, const char **argv) > const char *output_str, *sort_str = NULL; > struct perf_env *env; > > + annotation_options__init(); > + > + err = hists__init(); > + if (err < 0) > + goto out; > + > argc = parse_options(argc, argv, options, report_c2c_usage, > PARSE_OPT_STOP_AT_NON_OPTION); > if (argc) > @@ -3052,6 +3108,36 @@ static int perf_c2c__report(int argc, const char **argv) > if (c2c.stats_only) > c2c.use_stdio = true; > > + /** > + * Annotation related options > + * disassembler_style, objdump_path, addr2line_path > + * are set in the c2c_options, so we can use them here. > + */ > + if (disassembler_style) { > + annotate_opts.disassembler_style = strdup(disassembler_style); > + if (!annotate_opts.disassembler_style) { > + err = -ENOMEM; > + pr_err("Failed to allocate memory for annotation options\n"); > + goto out; > + } > + } > + if (objdump_path) { > + annotate_opts.objdump_path = strdup(objdump_path); > + if (!annotate_opts.objdump_path) { > + err = -ENOMEM; > + pr_err("Failed to allocate memory for annotation options\n"); > + goto out; > + } > + } > + if (addr2line_path) { > + symbol_conf.addr2line_path = strdup(addr2line_path); > + if (!symbol_conf.addr2line_path) { > + err = -ENOMEM; > + pr_err("Failed to allocate memory for annotation options\n"); > + goto out; > + } > + } > + > err = symbol__validate_sym_arguments(); > if (err) > goto out; > @@ -3126,6 +3212,38 @@ static int perf_c2c__report(int argc, const char **argv) > if (err) > goto out_mem2node; > > + if (c2c.use_stdio) > + use_browser = 0; > + else > + use_browser = 1; > + > + /* > + * Only in the TUI browser we are doing integrated annotation, > + * so don't allocate extra space that won't be used in the stdio > + * implementation. > + */ > + if (perf_c2c__has_annotation()) { > + int ret = symbol__annotation_init(); > + > + if (ret < 0) > + goto out_mem2node; > + /* > + * For searching by name on the "Browse map details". > + * providing it only in verbose mode not to bloat too > + * much struct symbol. > + */ > + if (verbose > 0) { > + /* > + * XXX: Need to provide a less kludgy way to ask for > + * more space per symbol, the u32 is for the index on > + * the ui browser. > + * See symbol__browser_index. > + */ > + symbol_conf.priv_size += sizeof(u32); > + } > + annotation_config__init(); > + } > + > if (symbol__init(env) < 0) > goto out_mem2node; > > @@ -3135,11 +3253,6 @@ static int perf_c2c__report(int argc, const char **argv) > goto out_mem2node; > } > > - if (c2c.use_stdio) > - use_browser = 0; > - else > - use_browser = 1; > - > setup_browser(false); > > err = perf_session__process_events(session); > @@ -3210,6 +3323,7 @@ static int perf_c2c__report(int argc, const char **argv) > out_session: > perf_session__delete(session); > out: > + annotation_options__exit(); > return err; > } > > diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c > index b770a8d4623e..c2afa3624917 100644 > --- a/tools/perf/ui/browsers/annotate.c > +++ b/tools/perf/ui/browsers/annotate.c > @@ -592,7 +592,7 @@ static bool annotate_browser__callq(struct annotate_browser *browser, > target_ms.map = ms->map; > target_ms.sym = dl->ops.target.sym; > annotation__unlock(notes); > - __hist_entry__tui_annotate(browser->he, &target_ms, evsel, hbt); > + __hist_entry__tui_annotate(browser->he, &target_ms, evsel, hbt, NO_INITIAL_IP); > sym_title(ms->sym, ms->map, title, sizeof(title), annotate_opts.percent_type); > ui_browser__show_title(&browser->b, title); > return true; > @@ -854,6 +854,7 @@ static int annotate_browser__run(struct annotate_browser *browser, > const char *help = "Press 'h' for help on key bindings"; > int delay_secs = hbt ? hbt->refresh : 0; > char *br_cntr_text = NULL; > + u64 init_ip = 0; > char title[256]; > int key; > > @@ -863,6 +864,13 @@ static int annotate_browser__run(struct annotate_browser *browser, > > annotate_browser__calc_percent(browser, evsel); > > + /* the selection are intentionally even not from the sample percentage */ > + if (browser->entries.rb_node == NULL && browser->selection) { > + init_ip = sym->start + browser->selection->offset; > + disasm_rb_tree__insert(browser, browser->selection); > + browser->curr_hot = rb_last(&browser->entries); > + } > + > if (browser->curr_hot) { > annotate_browser__set_rb_top(browser, browser->curr_hot); > browser->b.navkeypressed = false; > @@ -963,6 +971,17 @@ static int annotate_browser__run(struct annotate_browser *browser, > ui_helpline__puts(help); > annotate__scnprintf_title(hists, title, sizeof(title)); > annotate_browser__show(&browser->b, title, help); > + /* Previous RB tree may not valid, need refresh according to new entries*/ > + if (init_ip != 0) { > + struct disasm_line *dl = find_disasm_line(sym, init_ip, true); > + browser->curr_hot = NULL; > + if (dl != NULL) { > + browser->entries.rb_node = NULL; > + disasm_rb_tree__insert(browser, &dl->al); > + browser->curr_hot = rb_last(&browser->entries); > + } > + nd = browser->curr_hot; > + } > continue; > case 'o': > annotate_opts.use_offset = !annotate_opts.use_offset; > @@ -1096,22 +1115,23 @@ static int annotate_browser__run(struct annotate_browser *browser, > } > > int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel, > - struct hist_browser_timer *hbt) > + struct hist_browser_timer *hbt, u64 init_ip) > { > /* reset abort key so that it can get Ctrl-C as a key */ > SLang_reset_tty(); > SLang_init_tty(0, 0, 0); > SLtty_set_suspend_state(true); > > - return __hist_entry__tui_annotate(he, &he->ms, evsel, hbt); > + return __hist_entry__tui_annotate(he, &he->ms, evsel, hbt, init_ip); > } > > int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms, > struct evsel *evsel, > - struct hist_browser_timer *hbt) > + struct hist_browser_timer *hbt, u64 init_ip) > { > struct symbol *sym = ms->sym; > struct annotation *notes = symbol__annotation(sym); > + struct disasm_line *dl = NULL; > struct annotate_browser browser = { > .b = { > .refresh = annotate_browser__refresh, > @@ -1163,6 +1183,18 @@ int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms, > browser.he = &annotate_he; > } > > + /* > + * If init_ip is set, it means that there should be a line > + * intentionally selected, not based on the percentages > + * which caculated by the event sampling. In this case, we > + * convey this information into the browser selection, where > + * the selection in other cases should be empty. > + */ > + if (init_ip != NO_INITIAL_IP) { > + dl = find_disasm_line(sym, init_ip, false); > + browser.selection = &dl->al; > + } > + > ui_helpline__push("Press ESC to exit"); > > if (annotate_opts.code_with_type) { > diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c > index 487c0b08c003..3675a703de11 100644 > --- a/tools/perf/ui/browsers/hists.c > +++ b/tools/perf/ui/browsers/hists.c > @@ -2485,7 +2485,7 @@ do_annotate(struct hist_browser *browser, struct popup_action *act) > evsel = hists_to_evsel(browser->hists); > > he = hist_browser__selected_entry(browser); > - err = __hist_entry__tui_annotate(he, &act->ms, evsel, browser->hbt); > + err = __hist_entry__tui_annotate(he, &act->ms, evsel, browser->hbt, NO_INITIAL_IP); > /* > * offer option to annotate the other branch source or target > * (if they exists) when returning from annotate > diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c > index c9b220d9f924..937effbeda69 100644 > --- a/tools/perf/util/annotate.c > +++ b/tools/perf/util/annotate.c > @@ -2622,7 +2622,7 @@ int annotate_get_insn_location(struct arch *arch, struct disasm_line *dl, > return 0; > } > > -static struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip, > +struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip, > bool allow_update) > { > struct disasm_line *dl; > diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h > index eaf6c8aa7f47..bbe67588bbdd 100644 > --- a/tools/perf/util/annotate.h > +++ b/tools/perf/util/annotate.h > @@ -170,6 +170,8 @@ static inline struct disasm_line *disasm_line(struct annotation_line *al) > return al ? container_of(al, struct disasm_line, al) : NULL; > } > > +struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip, bool allow_update); > + > /* > * Is this offset in the same function as the line it is used? > * asm functions jump to other functions, for instance. > diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h > index c64005278687..e544e1795f19 100644 > --- a/tools/perf/util/hist.h > +++ b/tools/perf/util/hist.h > @@ -713,12 +713,14 @@ struct block_hist { > #include "../ui/keysyms.h" > void attr_to_script(char *buf, struct perf_event_attr *attr); > > +#define NO_INITIAL_IP 0 > + > int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms, > struct evsel *evsel, > - struct hist_browser_timer *hbt); > + struct hist_browser_timer *hbt, u64 init_ip); > > int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel, > - struct hist_browser_timer *hbt); > + struct hist_browser_timer *hbt, u64 init_ip); > > int evlist__tui_browse_hists(struct evlist *evlist, const char *help, struct hist_browser_timer *hbt, > float min_pcnt, struct perf_env *env, bool warn_lost_event); > -- > 2.47.1 >
On 9/12/2025 5:39 AM, Namhyung Kim wrote: > Hello, > > On Sun, Sep 07, 2025 at 11:25:10PM +0800, Tianyou Li wrote: >> Perf c2c report currently specified the code address and source:line >> information in the cacheline browser, while it is lack of annotation >> support like perf report to directly show the disassembly code for >> the particular symbol shared that same cacheline. This patches add >> a key 'a' binding to the cacheline browser which reuse the annotation >> browser to show the disassembly view for easier analysis of cacheline >> contentions. By default, the 'TAB' key navigate to the code address >> where the contentions detected. >> >> Signed-off-by: Tianyou Li <tianyou.li@intel.com> >> Reviewed-by: Dapeng Mi <dapeng1.mi@linux.intel.com> >> Reviewed-by: Thomas Falcon <thomas.falcon@intel.com> >> Reviewed-by: Jiebin Sun <jiebin.sun@intel.com> >> Reviewed-by: Pan Deng <pan.deng@intel.com> >> Reviewed-by: Zhiguo Zhou <zhiguo.zhou@intel.com> >> Reviewed-by: Wangyang Guo <wangyang.guo@intel.com> >> --- >> tools/perf/builtin-annotate.c | 2 +- >> tools/perf/builtin-c2c.c | 124 ++++++++++++++++++++++++++++-- >> tools/perf/ui/browsers/annotate.c | 40 +++++++++- >> tools/perf/ui/browsers/hists.c | 2 +- >> tools/perf/util/annotate.c | 2 +- >> tools/perf/util/annotate.h | 2 + >> tools/perf/util/hist.h | 6 +- >> 7 files changed, 164 insertions(+), 14 deletions(-) >> >> diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c >> index 646f43b0f7c4..f977e97a9c96 100644 >> --- a/tools/perf/builtin-annotate.c >> +++ b/tools/perf/builtin-annotate.c >> @@ -519,7 +519,7 @@ static void hists__find_annotations(struct hists *hists, >> /* skip missing symbols */ >> nd = rb_next(nd); >> } else if (use_browser == 1) { >> - key = hist_entry__tui_annotate(he, evsel, NULL); >> + key = hist_entry__tui_annotate(he, evsel, NULL, NO_INITIAL_IP); >> >> switch (key) { >> case -1: >> diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c >> index 9e9ff471ddd1..f5702d218490 100644 >> --- a/tools/perf/builtin-c2c.c >> +++ b/tools/perf/builtin-c2c.c >> @@ -45,6 +45,8 @@ >> #include "pmus.h" >> #include "string2.h" >> #include "util/util.h" >> +#include "util/symbol.h" >> +#include "util/annotate.h" >> >> struct c2c_hists { >> struct hists hists; >> @@ -62,6 +64,7 @@ struct compute_stats { >> >> struct c2c_hist_entry { >> struct c2c_hists *hists; >> + struct evsel *evsel; >> struct c2c_stats stats; >> unsigned long *cpuset; >> unsigned long *nodeset; >> @@ -225,6 +228,12 @@ he__get_c2c_hists(struct hist_entry *he, >> return hists; >> } >> >> +static void c2c_he__set_evsel(struct c2c_hist_entry *c2c_he, >> + struct evsel *evsel) >> +{ >> + c2c_he->evsel = evsel; >> +} >> + >> static void c2c_he__set_cpu(struct c2c_hist_entry *c2c_he, >> struct perf_sample *sample) >> { >> @@ -334,6 +343,7 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused, >> >> c2c_he__set_cpu(c2c_he, sample); >> c2c_he__set_node(c2c_he, sample); >> + c2c_he__set_evsel(c2c_he, evsel); >> >> hists__inc_nr_samples(&c2c_hists->hists, he->filtered); >> ret = hist_entry__append_callchain(he, sample); >> @@ -371,6 +381,7 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused, >> >> c2c_he__set_cpu(c2c_he, sample); >> c2c_he__set_node(c2c_he, sample); >> + c2c_he__set_evsel(c2c_he, evsel); >> >> hists__inc_nr_samples(&c2c_hists->hists, he->filtered); >> ret = hist_entry__append_callchain(he, sample); >> @@ -2550,6 +2561,29 @@ static void perf_c2c__hists_fprintf(FILE *out, struct perf_session *session) >> } >> >> #ifdef HAVE_SLANG_SUPPORT >> + >> +static int perf_c2c__toggle_annotation(struct hist_browser *browser) >> +{ >> + struct hist_entry *he = browser->he_selection; >> + struct symbol *sym = NULL; >> + struct c2c_hist_entry *c2c_he = NULL; >> + >> + if (!he) { >> + ui_browser__help_window(&browser->b, "No entry selected for annotation"); >> + return 0; >> + } >> + sym = (&he->ms)->sym; >> + >> + if (sym == NULL) { >> + ui_browser__help_window(&browser->b, "Can not annotate, no symbol found"); >> + return 0; >> + } >> + >> + symbol__hists(sym, 0); >> + c2c_he = container_of(he, struct c2c_hist_entry, he); >> + return hist_entry__tui_annotate(he, c2c_he->evsel, NULL, he->ip); >> +} >> + >> static void c2c_browser__update_nr_entries(struct hist_browser *hb) >> { >> u64 nr_entries = 0; >> @@ -2617,6 +2651,7 @@ static int perf_c2c__browse_cacheline(struct hist_entry *he) >> " ENTER Toggle callchains (if present) \n" >> " n Toggle Node details info \n" >> " s Toggle full length of symbol and source line columns \n" >> + " a Toggle annotation view \n" >> " q Return back to cacheline list \n"; >> >> if (!he) >> @@ -2651,6 +2686,9 @@ static int perf_c2c__browse_cacheline(struct hist_entry *he) >> c2c.node_info = (c2c.node_info + 1) % 3; >> setup_nodes_header(); >> break; >> + case 'a': >> + perf_c2c__toggle_annotation(browser); >> + break; >> case 'q': >> goto out; >> case '?': >> @@ -2989,6 +3027,11 @@ static int setup_coalesce(const char *coalesce, bool no_source) >> return 0; >> } >> >> +static bool perf_c2c__has_annotation(void) >> +{ >> + return use_browser == 1; >> +} > Can you just use ui__has_annotation()? It should make sure if > he->ms.sym is valid which means you have 'sym' sort key. > > Thanks, > Namhyung Thanks Namhyung for your time to review the patch. ui__has_annotation() use global perf_hpp_list while we use c2c.hists.list in builtin-c2c.c. Per my understanding, the he->ms.sym was initialized through the call chain of hists__add_entry_ops -> __hists__add_entry -> hists__findnew_entry -> hist_entry__init. Could you please kindly let me know in which case we have an invalid he->ms.sym where I should code a fix? Thanks. Regards, Tianyou >> + >> static int perf_c2c__report(int argc, const char **argv) >> { >> struct itrace_synth_opts itrace_synth_opts = { >> @@ -3006,6 +3049,7 @@ static int perf_c2c__report(int argc, const char **argv) >> const char *display = NULL; >> const char *coalesce = NULL; >> bool no_source = false; >> + const char *disassembler_style = NULL, *objdump_path = NULL, *addr2line_path = NULL; >> const struct option options[] = { >> OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, >> "file", "vmlinux pathname"), >> @@ -3033,6 +3077,12 @@ static int perf_c2c__report(int argc, const char **argv) >> OPT_BOOLEAN(0, "stitch-lbr", &c2c.stitch_lbr, >> "Enable LBR callgraph stitching approach"), >> OPT_BOOLEAN(0, "double-cl", &chk_double_cl, "Detect adjacent cacheline false sharing"), >> + OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style", >> + "Specify disassembler style (e.g. -M intel for intel syntax)"), >> + OPT_STRING(0, "objdump", &objdump_path, "path", >> + "objdump binary to use for disassembly and annotations"), >> + OPT_STRING(0, "addr2line", &addr2line_path, "path", >> + "addr2line binary to use for line numbers"), >> OPT_PARENT(c2c_options), >> OPT_END() >> }; >> @@ -3040,6 +3090,12 @@ static int perf_c2c__report(int argc, const char **argv) >> const char *output_str, *sort_str = NULL; >> struct perf_env *env; >> >> + annotation_options__init(); >> + >> + err = hists__init(); >> + if (err < 0) >> + goto out; >> + >> argc = parse_options(argc, argv, options, report_c2c_usage, >> PARSE_OPT_STOP_AT_NON_OPTION); >> if (argc) >> @@ -3052,6 +3108,36 @@ static int perf_c2c__report(int argc, const char **argv) >> if (c2c.stats_only) >> c2c.use_stdio = true; >> >> + /** >> + * Annotation related options >> + * disassembler_style, objdump_path, addr2line_path >> + * are set in the c2c_options, so we can use them here. >> + */ >> + if (disassembler_style) { >> + annotate_opts.disassembler_style = strdup(disassembler_style); >> + if (!annotate_opts.disassembler_style) { >> + err = -ENOMEM; >> + pr_err("Failed to allocate memory for annotation options\n"); >> + goto out; >> + } >> + } >> + if (objdump_path) { >> + annotate_opts.objdump_path = strdup(objdump_path); >> + if (!annotate_opts.objdump_path) { >> + err = -ENOMEM; >> + pr_err("Failed to allocate memory for annotation options\n"); >> + goto out; >> + } >> + } >> + if (addr2line_path) { >> + symbol_conf.addr2line_path = strdup(addr2line_path); >> + if (!symbol_conf.addr2line_path) { >> + err = -ENOMEM; >> + pr_err("Failed to allocate memory for annotation options\n"); >> + goto out; >> + } >> + } >> + >> err = symbol__validate_sym_arguments(); >> if (err) >> goto out; >> @@ -3126,6 +3212,38 @@ static int perf_c2c__report(int argc, const char **argv) >> if (err) >> goto out_mem2node; >> >> + if (c2c.use_stdio) >> + use_browser = 0; >> + else >> + use_browser = 1; >> + >> + /* >> + * Only in the TUI browser we are doing integrated annotation, >> + * so don't allocate extra space that won't be used in the stdio >> + * implementation. >> + */ >> + if (perf_c2c__has_annotation()) { >> + int ret = symbol__annotation_init(); >> + >> + if (ret < 0) >> + goto out_mem2node; >> + /* >> + * For searching by name on the "Browse map details". >> + * providing it only in verbose mode not to bloat too >> + * much struct symbol. >> + */ >> + if (verbose > 0) { >> + /* >> + * XXX: Need to provide a less kludgy way to ask for >> + * more space per symbol, the u32 is for the index on >> + * the ui browser. >> + * See symbol__browser_index. >> + */ >> + symbol_conf.priv_size += sizeof(u32); >> + } >> + annotation_config__init(); >> + } >> + >> if (symbol__init(env) < 0) >> goto out_mem2node; >> >> @@ -3135,11 +3253,6 @@ static int perf_c2c__report(int argc, const char **argv) >> goto out_mem2node; >> } >> >> - if (c2c.use_stdio) >> - use_browser = 0; >> - else >> - use_browser = 1; >> - >> setup_browser(false); >> >> err = perf_session__process_events(session); >> @@ -3210,6 +3323,7 @@ static int perf_c2c__report(int argc, const char **argv) >> out_session: >> perf_session__delete(session); >> out: >> + annotation_options__exit(); >> return err; >> } >> >> diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c >> index b770a8d4623e..c2afa3624917 100644 >> --- a/tools/perf/ui/browsers/annotate.c >> +++ b/tools/perf/ui/browsers/annotate.c >> @@ -592,7 +592,7 @@ static bool annotate_browser__callq(struct annotate_browser *browser, >> target_ms.map = ms->map; >> target_ms.sym = dl->ops.target.sym; >> annotation__unlock(notes); >> - __hist_entry__tui_annotate(browser->he, &target_ms, evsel, hbt); >> + __hist_entry__tui_annotate(browser->he, &target_ms, evsel, hbt, NO_INITIAL_IP); >> sym_title(ms->sym, ms->map, title, sizeof(title), annotate_opts.percent_type); >> ui_browser__show_title(&browser->b, title); >> return true; >> @@ -854,6 +854,7 @@ static int annotate_browser__run(struct annotate_browser *browser, >> const char *help = "Press 'h' for help on key bindings"; >> int delay_secs = hbt ? hbt->refresh : 0; >> char *br_cntr_text = NULL; >> + u64 init_ip = 0; >> char title[256]; >> int key; >> >> @@ -863,6 +864,13 @@ static int annotate_browser__run(struct annotate_browser *browser, >> >> annotate_browser__calc_percent(browser, evsel); >> >> + /* the selection are intentionally even not from the sample percentage */ >> + if (browser->entries.rb_node == NULL && browser->selection) { >> + init_ip = sym->start + browser->selection->offset; >> + disasm_rb_tree__insert(browser, browser->selection); >> + browser->curr_hot = rb_last(&browser->entries); >> + } >> + >> if (browser->curr_hot) { >> annotate_browser__set_rb_top(browser, browser->curr_hot); >> browser->b.navkeypressed = false; >> @@ -963,6 +971,17 @@ static int annotate_browser__run(struct annotate_browser *browser, >> ui_helpline__puts(help); >> annotate__scnprintf_title(hists, title, sizeof(title)); >> annotate_browser__show(&browser->b, title, help); >> + /* Previous RB tree may not valid, need refresh according to new entries*/ >> + if (init_ip != 0) { >> + struct disasm_line *dl = find_disasm_line(sym, init_ip, true); >> + browser->curr_hot = NULL; >> + if (dl != NULL) { >> + browser->entries.rb_node = NULL; >> + disasm_rb_tree__insert(browser, &dl->al); >> + browser->curr_hot = rb_last(&browser->entries); >> + } >> + nd = browser->curr_hot; >> + } >> continue; >> case 'o': >> annotate_opts.use_offset = !annotate_opts.use_offset; >> @@ -1096,22 +1115,23 @@ static int annotate_browser__run(struct annotate_browser *browser, >> } >> >> int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel, >> - struct hist_browser_timer *hbt) >> + struct hist_browser_timer *hbt, u64 init_ip) >> { >> /* reset abort key so that it can get Ctrl-C as a key */ >> SLang_reset_tty(); >> SLang_init_tty(0, 0, 0); >> SLtty_set_suspend_state(true); >> >> - return __hist_entry__tui_annotate(he, &he->ms, evsel, hbt); >> + return __hist_entry__tui_annotate(he, &he->ms, evsel, hbt, init_ip); >> } >> >> int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms, >> struct evsel *evsel, >> - struct hist_browser_timer *hbt) >> + struct hist_browser_timer *hbt, u64 init_ip) >> { >> struct symbol *sym = ms->sym; >> struct annotation *notes = symbol__annotation(sym); >> + struct disasm_line *dl = NULL; >> struct annotate_browser browser = { >> .b = { >> .refresh = annotate_browser__refresh, >> @@ -1163,6 +1183,18 @@ int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms, >> browser.he = &annotate_he; >> } >> >> + /* >> + * If init_ip is set, it means that there should be a line >> + * intentionally selected, not based on the percentages >> + * which caculated by the event sampling. In this case, we >> + * convey this information into the browser selection, where >> + * the selection in other cases should be empty. >> + */ >> + if (init_ip != NO_INITIAL_IP) { >> + dl = find_disasm_line(sym, init_ip, false); >> + browser.selection = &dl->al; >> + } >> + >> ui_helpline__push("Press ESC to exit"); >> >> if (annotate_opts.code_with_type) { >> diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c >> index 487c0b08c003..3675a703de11 100644 >> --- a/tools/perf/ui/browsers/hists.c >> +++ b/tools/perf/ui/browsers/hists.c >> @@ -2485,7 +2485,7 @@ do_annotate(struct hist_browser *browser, struct popup_action *act) >> evsel = hists_to_evsel(browser->hists); >> >> he = hist_browser__selected_entry(browser); >> - err = __hist_entry__tui_annotate(he, &act->ms, evsel, browser->hbt); >> + err = __hist_entry__tui_annotate(he, &act->ms, evsel, browser->hbt, NO_INITIAL_IP); >> /* >> * offer option to annotate the other branch source or target >> * (if they exists) when returning from annotate >> diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c >> index c9b220d9f924..937effbeda69 100644 >> --- a/tools/perf/util/annotate.c >> +++ b/tools/perf/util/annotate.c >> @@ -2622,7 +2622,7 @@ int annotate_get_insn_location(struct arch *arch, struct disasm_line *dl, >> return 0; >> } >> >> -static struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip, >> +struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip, >> bool allow_update) >> { >> struct disasm_line *dl; >> diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h >> index eaf6c8aa7f47..bbe67588bbdd 100644 >> --- a/tools/perf/util/annotate.h >> +++ b/tools/perf/util/annotate.h >> @@ -170,6 +170,8 @@ static inline struct disasm_line *disasm_line(struct annotation_line *al) >> return al ? container_of(al, struct disasm_line, al) : NULL; >> } >> >> +struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip, bool allow_update); >> + >> /* >> * Is this offset in the same function as the line it is used? >> * asm functions jump to other functions, for instance. >> diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h >> index c64005278687..e544e1795f19 100644 >> --- a/tools/perf/util/hist.h >> +++ b/tools/perf/util/hist.h >> @@ -713,12 +713,14 @@ struct block_hist { >> #include "../ui/keysyms.h" >> void attr_to_script(char *buf, struct perf_event_attr *attr); >> >> +#define NO_INITIAL_IP 0 >> + >> int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms, >> struct evsel *evsel, >> - struct hist_browser_timer *hbt); >> + struct hist_browser_timer *hbt, u64 init_ip); >> >> int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel, >> - struct hist_browser_timer *hbt); >> + struct hist_browser_timer *hbt, u64 init_ip); >> >> int evlist__tui_browse_hists(struct evlist *evlist, const char *help, struct hist_browser_timer *hbt, >> float min_pcnt, struct perf_env *env, bool warn_lost_event); >> -- >> 2.47.1 >>
On Fri, Sep 12, 2025 at 11:20:29PM +0800, Li, Tianyou wrote: > > On 9/12/2025 5:39 AM, Namhyung Kim wrote: > > Hello, > > > > On Sun, Sep 07, 2025 at 11:25:10PM +0800, Tianyou Li wrote: > > > Perf c2c report currently specified the code address and source:line > > > information in the cacheline browser, while it is lack of annotation > > > support like perf report to directly show the disassembly code for > > > the particular symbol shared that same cacheline. This patches add > > > a key 'a' binding to the cacheline browser which reuse the annotation > > > browser to show the disassembly view for easier analysis of cacheline > > > contentions. By default, the 'TAB' key navigate to the code address > > > where the contentions detected. > > > > > > Signed-off-by: Tianyou Li <tianyou.li@intel.com> > > > Reviewed-by: Dapeng Mi <dapeng1.mi@linux.intel.com> > > > Reviewed-by: Thomas Falcon <thomas.falcon@intel.com> > > > Reviewed-by: Jiebin Sun <jiebin.sun@intel.com> > > > Reviewed-by: Pan Deng <pan.deng@intel.com> > > > Reviewed-by: Zhiguo Zhou <zhiguo.zhou@intel.com> > > > Reviewed-by: Wangyang Guo <wangyang.guo@intel.com> > > > --- > > > tools/perf/builtin-annotate.c | 2 +- > > > tools/perf/builtin-c2c.c | 124 ++++++++++++++++++++++++++++-- > > > tools/perf/ui/browsers/annotate.c | 40 +++++++++- > > > tools/perf/ui/browsers/hists.c | 2 +- > > > tools/perf/util/annotate.c | 2 +- > > > tools/perf/util/annotate.h | 2 + > > > tools/perf/util/hist.h | 6 +- > > > 7 files changed, 164 insertions(+), 14 deletions(-) > > > > > > diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c > > > index 646f43b0f7c4..f977e97a9c96 100644 > > > --- a/tools/perf/builtin-annotate.c > > > +++ b/tools/perf/builtin-annotate.c > > > @@ -519,7 +519,7 @@ static void hists__find_annotations(struct hists *hists, > > > /* skip missing symbols */ > > > nd = rb_next(nd); > > > } else if (use_browser == 1) { > > > - key = hist_entry__tui_annotate(he, evsel, NULL); > > > + key = hist_entry__tui_annotate(he, evsel, NULL, NO_INITIAL_IP); > > > switch (key) { > > > case -1: > > > diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c > > > index 9e9ff471ddd1..f5702d218490 100644 > > > --- a/tools/perf/builtin-c2c.c > > > +++ b/tools/perf/builtin-c2c.c > > > @@ -45,6 +45,8 @@ > > > #include "pmus.h" > > > #include "string2.h" > > > #include "util/util.h" > > > +#include "util/symbol.h" > > > +#include "util/annotate.h" > > > struct c2c_hists { > > > struct hists hists; > > > @@ -62,6 +64,7 @@ struct compute_stats { > > > struct c2c_hist_entry { > > > struct c2c_hists *hists; > > > + struct evsel *evsel; > > > struct c2c_stats stats; > > > unsigned long *cpuset; > > > unsigned long *nodeset; > > > @@ -225,6 +228,12 @@ he__get_c2c_hists(struct hist_entry *he, > > > return hists; > > > } > > > +static void c2c_he__set_evsel(struct c2c_hist_entry *c2c_he, > > > + struct evsel *evsel) > > > +{ > > > + c2c_he->evsel = evsel; > > > +} > > > + > > > static void c2c_he__set_cpu(struct c2c_hist_entry *c2c_he, > > > struct perf_sample *sample) > > > { > > > @@ -334,6 +343,7 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused, > > > c2c_he__set_cpu(c2c_he, sample); > > > c2c_he__set_node(c2c_he, sample); > > > + c2c_he__set_evsel(c2c_he, evsel); > > > hists__inc_nr_samples(&c2c_hists->hists, he->filtered); > > > ret = hist_entry__append_callchain(he, sample); > > > @@ -371,6 +381,7 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused, > > > c2c_he__set_cpu(c2c_he, sample); > > > c2c_he__set_node(c2c_he, sample); > > > + c2c_he__set_evsel(c2c_he, evsel); > > > hists__inc_nr_samples(&c2c_hists->hists, he->filtered); > > > ret = hist_entry__append_callchain(he, sample); > > > @@ -2550,6 +2561,29 @@ static void perf_c2c__hists_fprintf(FILE *out, struct perf_session *session) > > > } > > > #ifdef HAVE_SLANG_SUPPORT > > > + > > > +static int perf_c2c__toggle_annotation(struct hist_browser *browser) > > > +{ > > > + struct hist_entry *he = browser->he_selection; > > > + struct symbol *sym = NULL; > > > + struct c2c_hist_entry *c2c_he = NULL; > > > + > > > + if (!he) { > > > + ui_browser__help_window(&browser->b, "No entry selected for annotation"); > > > + return 0; > > > + } > > > + sym = (&he->ms)->sym; > > > + > > > + if (sym == NULL) { > > > + ui_browser__help_window(&browser->b, "Can not annotate, no symbol found"); > > > + return 0; > > > + } > > > + > > > + symbol__hists(sym, 0); > > > + c2c_he = container_of(he, struct c2c_hist_entry, he); > > > + return hist_entry__tui_annotate(he, c2c_he->evsel, NULL, he->ip); > > > +} > > > + > > > static void c2c_browser__update_nr_entries(struct hist_browser *hb) > > > { > > > u64 nr_entries = 0; > > > @@ -2617,6 +2651,7 @@ static int perf_c2c__browse_cacheline(struct hist_entry *he) > > > " ENTER Toggle callchains (if present) \n" > > > " n Toggle Node details info \n" > > > " s Toggle full length of symbol and source line columns \n" > > > + " a Toggle annotation view \n" > > > " q Return back to cacheline list \n"; > > > if (!he) > > > @@ -2651,6 +2686,9 @@ static int perf_c2c__browse_cacheline(struct hist_entry *he) > > > c2c.node_info = (c2c.node_info + 1) % 3; > > > setup_nodes_header(); > > > break; > > > + case 'a': > > > + perf_c2c__toggle_annotation(browser); > > > + break; > > > case 'q': > > > goto out; > > > case '?': > > > @@ -2989,6 +3027,11 @@ static int setup_coalesce(const char *coalesce, bool no_source) > > > return 0; > > > } > > > +static bool perf_c2c__has_annotation(void) > > > +{ > > > + return use_browser == 1; > > > +} > > Can you just use ui__has_annotation()? It should make sure if > > he->ms.sym is valid which means you have 'sym' sort key. > > > > Thanks, > > Namhyung > > Thanks Namhyung for your time to review the patch. ui__has_annotation() use > global perf_hpp_list while we use c2c.hists.list in builtin-c2c.c. I see. Thanks for the explanation. Ideally it'd check if c2c.hists.list.sym is set but I'm not sure the sort key setup code correctly updates it or not. > > Per my understanding, the he->ms.sym was initialized through the call chain > of hists__add_entry_ops -> __hists__add_entry -> hists__findnew_entry -> > hist_entry__init. > > Could you please kindly let me know in which case we have an invalid > he->ms.sym where I should code a fix? Thanks. It may have some value, but it'd be consistent only if all hist entries merged to it (according to the sort keys) has the same symbol info. And it's guaranteed only if you have 'symbol' in the sort key. Otherwise an entry can have a random symbol (at the time of the first sample) which won't represent the whole overhead for the entry. And I'm afraid that the result can be misleading. I haven't looked at the c2c code deeply if it's always guaranteed to have the 'symbol' sort key though. Thanks, Namhyung > > > + > > > static int perf_c2c__report(int argc, const char **argv) > > > { > > > struct itrace_synth_opts itrace_synth_opts = { > > > @@ -3006,6 +3049,7 @@ static int perf_c2c__report(int argc, const char **argv) > > > const char *display = NULL; > > > const char *coalesce = NULL; > > > bool no_source = false; > > > + const char *disassembler_style = NULL, *objdump_path = NULL, *addr2line_path = NULL; > > > const struct option options[] = { > > > OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, > > > "file", "vmlinux pathname"), > > > @@ -3033,6 +3077,12 @@ static int perf_c2c__report(int argc, const char **argv) > > > OPT_BOOLEAN(0, "stitch-lbr", &c2c.stitch_lbr, > > > "Enable LBR callgraph stitching approach"), > > > OPT_BOOLEAN(0, "double-cl", &chk_double_cl, "Detect adjacent cacheline false sharing"), > > > + OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style", > > > + "Specify disassembler style (e.g. -M intel for intel syntax)"), > > > + OPT_STRING(0, "objdump", &objdump_path, "path", > > > + "objdump binary to use for disassembly and annotations"), > > > + OPT_STRING(0, "addr2line", &addr2line_path, "path", > > > + "addr2line binary to use for line numbers"), > > > OPT_PARENT(c2c_options), > > > OPT_END() > > > }; > > > @@ -3040,6 +3090,12 @@ static int perf_c2c__report(int argc, const char **argv) > > > const char *output_str, *sort_str = NULL; > > > struct perf_env *env; > > > + annotation_options__init(); > > > + > > > + err = hists__init(); > > > + if (err < 0) > > > + goto out; > > > + > > > argc = parse_options(argc, argv, options, report_c2c_usage, > > > PARSE_OPT_STOP_AT_NON_OPTION); > > > if (argc) > > > @@ -3052,6 +3108,36 @@ static int perf_c2c__report(int argc, const char **argv) > > > if (c2c.stats_only) > > > c2c.use_stdio = true; > > > + /** > > > + * Annotation related options > > > + * disassembler_style, objdump_path, addr2line_path > > > + * are set in the c2c_options, so we can use them here. > > > + */ > > > + if (disassembler_style) { > > > + annotate_opts.disassembler_style = strdup(disassembler_style); > > > + if (!annotate_opts.disassembler_style) { > > > + err = -ENOMEM; > > > + pr_err("Failed to allocate memory for annotation options\n"); > > > + goto out; > > > + } > > > + } > > > + if (objdump_path) { > > > + annotate_opts.objdump_path = strdup(objdump_path); > > > + if (!annotate_opts.objdump_path) { > > > + err = -ENOMEM; > > > + pr_err("Failed to allocate memory for annotation options\n"); > > > + goto out; > > > + } > > > + } > > > + if (addr2line_path) { > > > + symbol_conf.addr2line_path = strdup(addr2line_path); > > > + if (!symbol_conf.addr2line_path) { > > > + err = -ENOMEM; > > > + pr_err("Failed to allocate memory for annotation options\n"); > > > + goto out; > > > + } > > > + } > > > + > > > err = symbol__validate_sym_arguments(); > > > if (err) > > > goto out; > > > @@ -3126,6 +3212,38 @@ static int perf_c2c__report(int argc, const char **argv) > > > if (err) > > > goto out_mem2node; > > > + if (c2c.use_stdio) > > > + use_browser = 0; > > > + else > > > + use_browser = 1; > > > + > > > + /* > > > + * Only in the TUI browser we are doing integrated annotation, > > > + * so don't allocate extra space that won't be used in the stdio > > > + * implementation. > > > + */ > > > + if (perf_c2c__has_annotation()) { > > > + int ret = symbol__annotation_init(); > > > + > > > + if (ret < 0) > > > + goto out_mem2node; > > > + /* > > > + * For searching by name on the "Browse map details". > > > + * providing it only in verbose mode not to bloat too > > > + * much struct symbol. > > > + */ > > > + if (verbose > 0) { > > > + /* > > > + * XXX: Need to provide a less kludgy way to ask for > > > + * more space per symbol, the u32 is for the index on > > > + * the ui browser. > > > + * See symbol__browser_index. > > > + */ > > > + symbol_conf.priv_size += sizeof(u32); > > > + } > > > + annotation_config__init(); > > > + } > > > + > > > if (symbol__init(env) < 0) > > > goto out_mem2node; > > > @@ -3135,11 +3253,6 @@ static int perf_c2c__report(int argc, const char **argv) > > > goto out_mem2node; > > > } > > > - if (c2c.use_stdio) > > > - use_browser = 0; > > > - else > > > - use_browser = 1; > > > - > > > setup_browser(false); > > > err = perf_session__process_events(session); > > > @@ -3210,6 +3323,7 @@ static int perf_c2c__report(int argc, const char **argv) > > > out_session: > > > perf_session__delete(session); > > > out: > > > + annotation_options__exit(); > > > return err; > > > } > > > diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c > > > index b770a8d4623e..c2afa3624917 100644 > > > --- a/tools/perf/ui/browsers/annotate.c > > > +++ b/tools/perf/ui/browsers/annotate.c > > > @@ -592,7 +592,7 @@ static bool annotate_browser__callq(struct annotate_browser *browser, > > > target_ms.map = ms->map; > > > target_ms.sym = dl->ops.target.sym; > > > annotation__unlock(notes); > > > - __hist_entry__tui_annotate(browser->he, &target_ms, evsel, hbt); > > > + __hist_entry__tui_annotate(browser->he, &target_ms, evsel, hbt, NO_INITIAL_IP); > > > sym_title(ms->sym, ms->map, title, sizeof(title), annotate_opts.percent_type); > > > ui_browser__show_title(&browser->b, title); > > > return true; > > > @@ -854,6 +854,7 @@ static int annotate_browser__run(struct annotate_browser *browser, > > > const char *help = "Press 'h' for help on key bindings"; > > > int delay_secs = hbt ? hbt->refresh : 0; > > > char *br_cntr_text = NULL; > > > + u64 init_ip = 0; > > > char title[256]; > > > int key; > > > @@ -863,6 +864,13 @@ static int annotate_browser__run(struct annotate_browser *browser, > > > annotate_browser__calc_percent(browser, evsel); > > > + /* the selection are intentionally even not from the sample percentage */ > > > + if (browser->entries.rb_node == NULL && browser->selection) { > > > + init_ip = sym->start + browser->selection->offset; > > > + disasm_rb_tree__insert(browser, browser->selection); > > > + browser->curr_hot = rb_last(&browser->entries); > > > + } > > > + > > > if (browser->curr_hot) { > > > annotate_browser__set_rb_top(browser, browser->curr_hot); > > > browser->b.navkeypressed = false; > > > @@ -963,6 +971,17 @@ static int annotate_browser__run(struct annotate_browser *browser, > > > ui_helpline__puts(help); > > > annotate__scnprintf_title(hists, title, sizeof(title)); > > > annotate_browser__show(&browser->b, title, help); > > > + /* Previous RB tree may not valid, need refresh according to new entries*/ > > > + if (init_ip != 0) { > > > + struct disasm_line *dl = find_disasm_line(sym, init_ip, true); > > > + browser->curr_hot = NULL; > > > + if (dl != NULL) { > > > + browser->entries.rb_node = NULL; > > > + disasm_rb_tree__insert(browser, &dl->al); > > > + browser->curr_hot = rb_last(&browser->entries); > > > + } > > > + nd = browser->curr_hot; > > > + } > > > continue; > > > case 'o': > > > annotate_opts.use_offset = !annotate_opts.use_offset; > > > @@ -1096,22 +1115,23 @@ static int annotate_browser__run(struct annotate_browser *browser, > > > } > > > int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel, > > > - struct hist_browser_timer *hbt) > > > + struct hist_browser_timer *hbt, u64 init_ip) > > > { > > > /* reset abort key so that it can get Ctrl-C as a key */ > > > SLang_reset_tty(); > > > SLang_init_tty(0, 0, 0); > > > SLtty_set_suspend_state(true); > > > - return __hist_entry__tui_annotate(he, &he->ms, evsel, hbt); > > > + return __hist_entry__tui_annotate(he, &he->ms, evsel, hbt, init_ip); > > > } > > > int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms, > > > struct evsel *evsel, > > > - struct hist_browser_timer *hbt) > > > + struct hist_browser_timer *hbt, u64 init_ip) > > > { > > > struct symbol *sym = ms->sym; > > > struct annotation *notes = symbol__annotation(sym); > > > + struct disasm_line *dl = NULL; > > > struct annotate_browser browser = { > > > .b = { > > > .refresh = annotate_browser__refresh, > > > @@ -1163,6 +1183,18 @@ int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms, > > > browser.he = &annotate_he; > > > } > > > + /* > > > + * If init_ip is set, it means that there should be a line > > > + * intentionally selected, not based on the percentages > > > + * which caculated by the event sampling. In this case, we > > > + * convey this information into the browser selection, where > > > + * the selection in other cases should be empty. > > > + */ > > > + if (init_ip != NO_INITIAL_IP) { > > > + dl = find_disasm_line(sym, init_ip, false); > > > + browser.selection = &dl->al; > > > + } > > > + > > > ui_helpline__push("Press ESC to exit"); > > > if (annotate_opts.code_with_type) { > > > diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c > > > index 487c0b08c003..3675a703de11 100644 > > > --- a/tools/perf/ui/browsers/hists.c > > > +++ b/tools/perf/ui/browsers/hists.c > > > @@ -2485,7 +2485,7 @@ do_annotate(struct hist_browser *browser, struct popup_action *act) > > > evsel = hists_to_evsel(browser->hists); > > > he = hist_browser__selected_entry(browser); > > > - err = __hist_entry__tui_annotate(he, &act->ms, evsel, browser->hbt); > > > + err = __hist_entry__tui_annotate(he, &act->ms, evsel, browser->hbt, NO_INITIAL_IP); > > > /* > > > * offer option to annotate the other branch source or target > > > * (if they exists) when returning from annotate > > > diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c > > > index c9b220d9f924..937effbeda69 100644 > > > --- a/tools/perf/util/annotate.c > > > +++ b/tools/perf/util/annotate.c > > > @@ -2622,7 +2622,7 @@ int annotate_get_insn_location(struct arch *arch, struct disasm_line *dl, > > > return 0; > > > } > > > -static struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip, > > > +struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip, > > > bool allow_update) > > > { > > > struct disasm_line *dl; > > > diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h > > > index eaf6c8aa7f47..bbe67588bbdd 100644 > > > --- a/tools/perf/util/annotate.h > > > +++ b/tools/perf/util/annotate.h > > > @@ -170,6 +170,8 @@ static inline struct disasm_line *disasm_line(struct annotation_line *al) > > > return al ? container_of(al, struct disasm_line, al) : NULL; > > > } > > > +struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip, bool allow_update); > > > + > > > /* > > > * Is this offset in the same function as the line it is used? > > > * asm functions jump to other functions, for instance. > > > diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h > > > index c64005278687..e544e1795f19 100644 > > > --- a/tools/perf/util/hist.h > > > +++ b/tools/perf/util/hist.h > > > @@ -713,12 +713,14 @@ struct block_hist { > > > #include "../ui/keysyms.h" > > > void attr_to_script(char *buf, struct perf_event_attr *attr); > > > +#define NO_INITIAL_IP 0 > > > + > > > int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms, > > > struct evsel *evsel, > > > - struct hist_browser_timer *hbt); > > > + struct hist_browser_timer *hbt, u64 init_ip); > > > int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel, > > > - struct hist_browser_timer *hbt); > > > + struct hist_browser_timer *hbt, u64 init_ip); > > > int evlist__tui_browse_hists(struct evlist *evlist, const char *help, struct hist_browser_timer *hbt, > > > float min_pcnt, struct perf_env *env, bool warn_lost_event); > > > -- > > > 2.47.1 > > >
On 9/17/2025 2:34 PM, Namhyung Kim wrote: > On Fri, Sep 12, 2025 at 11:20:29PM +0800, Li, Tianyou wrote: >> On 9/12/2025 5:39 AM, Namhyung Kim wrote: >>> Hello, >>> >>> On Sun, Sep 07, 2025 at 11:25:10PM +0800, Tianyou Li wrote: >>>> Perf c2c report currently specified the code address and source:line >>>> information in the cacheline browser, while it is lack of annotation >>>> support like perf report to directly show the disassembly code for >>>> the particular symbol shared that same cacheline. This patches add >>>> a key 'a' binding to the cacheline browser which reuse the annotation >>>> browser to show the disassembly view for easier analysis of cacheline >>>> contentions. By default, the 'TAB' key navigate to the code address >>>> where the contentions detected. >>>> >>>> Signed-off-by: Tianyou Li <tianyou.li@intel.com> >>>> Reviewed-by: Dapeng Mi <dapeng1.mi@linux.intel.com> >>>> Reviewed-by: Thomas Falcon <thomas.falcon@intel.com> >>>> Reviewed-by: Jiebin Sun <jiebin.sun@intel.com> >>>> Reviewed-by: Pan Deng <pan.deng@intel.com> >>>> Reviewed-by: Zhiguo Zhou <zhiguo.zhou@intel.com> >>>> Reviewed-by: Wangyang Guo <wangyang.guo@intel.com> >>>> --- >>>> tools/perf/builtin-annotate.c | 2 +- >>>> tools/perf/builtin-c2c.c | 124 ++++++++++++++++++++++++++++-- >>>> tools/perf/ui/browsers/annotate.c | 40 +++++++++- >>>> tools/perf/ui/browsers/hists.c | 2 +- >>>> tools/perf/util/annotate.c | 2 +- >>>> tools/perf/util/annotate.h | 2 + >>>> tools/perf/util/hist.h | 6 +- >>>> 7 files changed, 164 insertions(+), 14 deletions(-) >>>> >>>> diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c >>>> index 646f43b0f7c4..f977e97a9c96 100644 >>>> --- a/tools/perf/builtin-annotate.c >>>> +++ b/tools/perf/builtin-annotate.c >>>> @@ -519,7 +519,7 @@ static void hists__find_annotations(struct hists *hists, >>>> /* skip missing symbols */ >>>> nd = rb_next(nd); >>>> } else if (use_browser == 1) { >>>> - key = hist_entry__tui_annotate(he, evsel, NULL); >>>> + key = hist_entry__tui_annotate(he, evsel, NULL, NO_INITIAL_IP); >>>> switch (key) { >>>> case -1: >>>> diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c >>>> index 9e9ff471ddd1..f5702d218490 100644 >>>> --- a/tools/perf/builtin-c2c.c >>>> +++ b/tools/perf/builtin-c2c.c >>>> @@ -45,6 +45,8 @@ >>>> #include "pmus.h" >>>> #include "string2.h" >>>> #include "util/util.h" >>>> +#include "util/symbol.h" >>>> +#include "util/annotate.h" >>>> struct c2c_hists { >>>> struct hists hists; >>>> @@ -62,6 +64,7 @@ struct compute_stats { >>>> struct c2c_hist_entry { >>>> struct c2c_hists *hists; >>>> + struct evsel *evsel; >>>> struct c2c_stats stats; >>>> unsigned long *cpuset; >>>> unsigned long *nodeset; >>>> @@ -225,6 +228,12 @@ he__get_c2c_hists(struct hist_entry *he, >>>> return hists; >>>> } >>>> +static void c2c_he__set_evsel(struct c2c_hist_entry *c2c_he, >>>> + struct evsel *evsel) >>>> +{ >>>> + c2c_he->evsel = evsel; >>>> +} >>>> + >>>> static void c2c_he__set_cpu(struct c2c_hist_entry *c2c_he, >>>> struct perf_sample *sample) >>>> { >>>> @@ -334,6 +343,7 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused, >>>> c2c_he__set_cpu(c2c_he, sample); >>>> c2c_he__set_node(c2c_he, sample); >>>> + c2c_he__set_evsel(c2c_he, evsel); >>>> hists__inc_nr_samples(&c2c_hists->hists, he->filtered); >>>> ret = hist_entry__append_callchain(he, sample); >>>> @@ -371,6 +381,7 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused, >>>> c2c_he__set_cpu(c2c_he, sample); >>>> c2c_he__set_node(c2c_he, sample); >>>> + c2c_he__set_evsel(c2c_he, evsel); >>>> hists__inc_nr_samples(&c2c_hists->hists, he->filtered); >>>> ret = hist_entry__append_callchain(he, sample); >>>> @@ -2550,6 +2561,29 @@ static void perf_c2c__hists_fprintf(FILE *out, struct perf_session *session) >>>> } >>>> #ifdef HAVE_SLANG_SUPPORT >>>> + >>>> +static int perf_c2c__toggle_annotation(struct hist_browser *browser) >>>> +{ >>>> + struct hist_entry *he = browser->he_selection; >>>> + struct symbol *sym = NULL; >>>> + struct c2c_hist_entry *c2c_he = NULL; >>>> + >>>> + if (!he) { >>>> + ui_browser__help_window(&browser->b, "No entry selected for annotation"); >>>> + return 0; >>>> + } >>>> + sym = (&he->ms)->sym; >>>> + >>>> + if (sym == NULL) { >>>> + ui_browser__help_window(&browser->b, "Can not annotate, no symbol found"); >>>> + return 0; >>>> + } >>>> + >>>> + symbol__hists(sym, 0); >>>> + c2c_he = container_of(he, struct c2c_hist_entry, he); >>>> + return hist_entry__tui_annotate(he, c2c_he->evsel, NULL, he->ip); >>>> +} >>>> + >>>> static void c2c_browser__update_nr_entries(struct hist_browser *hb) >>>> { >>>> u64 nr_entries = 0; >>>> @@ -2617,6 +2651,7 @@ static int perf_c2c__browse_cacheline(struct hist_entry *he) >>>> " ENTER Toggle callchains (if present) \n" >>>> " n Toggle Node details info \n" >>>> " s Toggle full length of symbol and source line columns \n" >>>> + " a Toggle annotation view \n" >>>> " q Return back to cacheline list \n"; >>>> if (!he) >>>> @@ -2651,6 +2686,9 @@ static int perf_c2c__browse_cacheline(struct hist_entry *he) >>>> c2c.node_info = (c2c.node_info + 1) % 3; >>>> setup_nodes_header(); >>>> break; >>>> + case 'a': >>>> + perf_c2c__toggle_annotation(browser); >>>> + break; >>>> case 'q': >>>> goto out; >>>> case '?': >>>> @@ -2989,6 +3027,11 @@ static int setup_coalesce(const char *coalesce, bool no_source) >>>> return 0; >>>> } >>>> +static bool perf_c2c__has_annotation(void) >>>> +{ >>>> + return use_browser == 1; >>>> +} >>> Can you just use ui__has_annotation()? It should make sure if >>> he->ms.sym is valid which means you have 'sym' sort key. >>> >>> Thanks, >>> Namhyung >> Thanks Namhyung for your time to review the patch. ui__has_annotation() use >> global perf_hpp_list while we use c2c.hists.list in builtin-c2c.c. > I see. Thanks for the explanation. Ideally it'd check if > c2c.hists.list.sym is set but I'm not sure the sort key setup code > correctly updates it or not. > >> Per my understanding, the he->ms.sym was initialized through the call chain >> of hists__add_entry_ops -> __hists__add_entry -> hists__findnew_entry -> >> hist_entry__init. >> >> Could you please kindly let me know in which case we have an invalid >> he->ms.sym where I should code a fix? Thanks. > It may have some value, but it'd be consistent only if all hist entries > merged to it (according to the sort keys) has the same symbol info. And > it's guaranteed only if you have 'symbol' in the sort key. > > Otherwise an entry can have a random symbol (at the time of the first > sample) which won't represent the whole overhead for the entry. And I'm > afraid that the result can be misleading. > > I haven't looked at the c2c code deeply if it's always guaranteed to > have the 'symbol' sort key though. > > Thanks, > Namhyung Very appreciated for your response and explanation, sorry for the delayed response. When cacheline browser created, it will setup the dimensions which include the dim_symbol where sort_entry set to sort_sym. The hist_entry of c2c_cacheline_browser contains the symbol information that need to be displayed in the TUI. The ip addr also displayed as the code address. In the annotation browser, we did not need to show the overhead of particular event type, or switch among events. The only selection was set to the ip addr where the hist entry indicated in the cacheline browser. The hist_entry was correctly initialized with .ms field by addr_location when the mem_info can be successfully resolved through addr_location. I did not find the 'symbol' sort key has already guaranteed but in the c2c scenario it might not be necessary since we just need to highlight the only code line that where the code address indicated by the cacheline browser's hist entry, no overhead percentage calculation is needed. Please kindly allow me to know your thoughts. Looking forward to hearing from you. Thanks. Regards, Tianyou >>>> + >>>> static int perf_c2c__report(int argc, const char **argv) >>>> { >>>> struct itrace_synth_opts itrace_synth_opts = { >>>> @@ -3006,6 +3049,7 @@ static int perf_c2c__report(int argc, const char **argv) >>>> const char *display = NULL; >>>> const char *coalesce = NULL; >>>> bool no_source = false; >>>> + const char *disassembler_style = NULL, *objdump_path = NULL, *addr2line_path = NULL; >>>> const struct option options[] = { >>>> OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, >>>> "file", "vmlinux pathname"), >>>> @@ -3033,6 +3077,12 @@ static int perf_c2c__report(int argc, const char **argv) >>>> OPT_BOOLEAN(0, "stitch-lbr", &c2c.stitch_lbr, >>>> "Enable LBR callgraph stitching approach"), >>>> OPT_BOOLEAN(0, "double-cl", &chk_double_cl, "Detect adjacent cacheline false sharing"), >>>> + OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style", >>>> + "Specify disassembler style (e.g. -M intel for intel syntax)"), >>>> + OPT_STRING(0, "objdump", &objdump_path, "path", >>>> + "objdump binary to use for disassembly and annotations"), >>>> + OPT_STRING(0, "addr2line", &addr2line_path, "path", >>>> + "addr2line binary to use for line numbers"), >>>> OPT_PARENT(c2c_options), >>>> OPT_END() >>>> }; >>>> @@ -3040,6 +3090,12 @@ static int perf_c2c__report(int argc, const char **argv) >>>> const char *output_str, *sort_str = NULL; >>>> struct perf_env *env; >>>> + annotation_options__init(); >>>> + >>>> + err = hists__init(); >>>> + if (err < 0) >>>> + goto out; >>>> + >>>> argc = parse_options(argc, argv, options, report_c2c_usage, >>>> PARSE_OPT_STOP_AT_NON_OPTION); >>>> if (argc) >>>> @@ -3052,6 +3108,36 @@ static int perf_c2c__report(int argc, const char **argv) >>>> if (c2c.stats_only) >>>> c2c.use_stdio = true; >>>> + /** >>>> + * Annotation related options >>>> + * disassembler_style, objdump_path, addr2line_path >>>> + * are set in the c2c_options, so we can use them here. >>>> + */ >>>> + if (disassembler_style) { >>>> + annotate_opts.disassembler_style = strdup(disassembler_style); >>>> + if (!annotate_opts.disassembler_style) { >>>> + err = -ENOMEM; >>>> + pr_err("Failed to allocate memory for annotation options\n"); >>>> + goto out; >>>> + } >>>> + } >>>> + if (objdump_path) { >>>> + annotate_opts.objdump_path = strdup(objdump_path); >>>> + if (!annotate_opts.objdump_path) { >>>> + err = -ENOMEM; >>>> + pr_err("Failed to allocate memory for annotation options\n"); >>>> + goto out; >>>> + } >>>> + } >>>> + if (addr2line_path) { >>>> + symbol_conf.addr2line_path = strdup(addr2line_path); >>>> + if (!symbol_conf.addr2line_path) { >>>> + err = -ENOMEM; >>>> + pr_err("Failed to allocate memory for annotation options\n"); >>>> + goto out; >>>> + } >>>> + } >>>> + >>>> err = symbol__validate_sym_arguments(); >>>> if (err) >>>> goto out; >>>> @@ -3126,6 +3212,38 @@ static int perf_c2c__report(int argc, const char **argv) >>>> if (err) >>>> goto out_mem2node; >>>> + if (c2c.use_stdio) >>>> + use_browser = 0; >>>> + else >>>> + use_browser = 1; >>>> + >>>> + /* >>>> + * Only in the TUI browser we are doing integrated annotation, >>>> + * so don't allocate extra space that won't be used in the stdio >>>> + * implementation. >>>> + */ >>>> + if (perf_c2c__has_annotation()) { >>>> + int ret = symbol__annotation_init(); >>>> + >>>> + if (ret < 0) >>>> + goto out_mem2node; >>>> + /* >>>> + * For searching by name on the "Browse map details". >>>> + * providing it only in verbose mode not to bloat too >>>> + * much struct symbol. >>>> + */ >>>> + if (verbose > 0) { >>>> + /* >>>> + * XXX: Need to provide a less kludgy way to ask for >>>> + * more space per symbol, the u32 is for the index on >>>> + * the ui browser. >>>> + * See symbol__browser_index. >>>> + */ >>>> + symbol_conf.priv_size += sizeof(u32); >>>> + } >>>> + annotation_config__init(); >>>> + } >>>> + >>>> if (symbol__init(env) < 0) >>>> goto out_mem2node; >>>> @@ -3135,11 +3253,6 @@ static int perf_c2c__report(int argc, const char **argv) >>>> goto out_mem2node; >>>> } >>>> - if (c2c.use_stdio) >>>> - use_browser = 0; >>>> - else >>>> - use_browser = 1; >>>> - >>>> setup_browser(false); >>>> err = perf_session__process_events(session); >>>> @@ -3210,6 +3323,7 @@ static int perf_c2c__report(int argc, const char **argv) >>>> out_session: >>>> perf_session__delete(session); >>>> out: >>>> + annotation_options__exit(); >>>> return err; >>>> } >>>> diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c >>>> index b770a8d4623e..c2afa3624917 100644 >>>> --- a/tools/perf/ui/browsers/annotate.c >>>> +++ b/tools/perf/ui/browsers/annotate.c >>>> @@ -592,7 +592,7 @@ static bool annotate_browser__callq(struct annotate_browser *browser, >>>> target_ms.map = ms->map; >>>> target_ms.sym = dl->ops.target.sym; >>>> annotation__unlock(notes); >>>> - __hist_entry__tui_annotate(browser->he, &target_ms, evsel, hbt); >>>> + __hist_entry__tui_annotate(browser->he, &target_ms, evsel, hbt, NO_INITIAL_IP); >>>> sym_title(ms->sym, ms->map, title, sizeof(title), annotate_opts.percent_type); >>>> ui_browser__show_title(&browser->b, title); >>>> return true; >>>> @@ -854,6 +854,7 @@ static int annotate_browser__run(struct annotate_browser *browser, >>>> const char *help = "Press 'h' for help on key bindings"; >>>> int delay_secs = hbt ? hbt->refresh : 0; >>>> char *br_cntr_text = NULL; >>>> + u64 init_ip = 0; >>>> char title[256]; >>>> int key; >>>> @@ -863,6 +864,13 @@ static int annotate_browser__run(struct annotate_browser *browser, >>>> annotate_browser__calc_percent(browser, evsel); >>>> + /* the selection are intentionally even not from the sample percentage */ >>>> + if (browser->entries.rb_node == NULL && browser->selection) { >>>> + init_ip = sym->start + browser->selection->offset; >>>> + disasm_rb_tree__insert(browser, browser->selection); >>>> + browser->curr_hot = rb_last(&browser->entries); >>>> + } >>>> + >>>> if (browser->curr_hot) { >>>> annotate_browser__set_rb_top(browser, browser->curr_hot); >>>> browser->b.navkeypressed = false; >>>> @@ -963,6 +971,17 @@ static int annotate_browser__run(struct annotate_browser *browser, >>>> ui_helpline__puts(help); >>>> annotate__scnprintf_title(hists, title, sizeof(title)); >>>> annotate_browser__show(&browser->b, title, help); >>>> + /* Previous RB tree may not valid, need refresh according to new entries*/ >>>> + if (init_ip != 0) { >>>> + struct disasm_line *dl = find_disasm_line(sym, init_ip, true); >>>> + browser->curr_hot = NULL; >>>> + if (dl != NULL) { >>>> + browser->entries.rb_node = NULL; >>>> + disasm_rb_tree__insert(browser, &dl->al); >>>> + browser->curr_hot = rb_last(&browser->entries); >>>> + } >>>> + nd = browser->curr_hot; >>>> + } >>>> continue; >>>> case 'o': >>>> annotate_opts.use_offset = !annotate_opts.use_offset; >>>> @@ -1096,22 +1115,23 @@ static int annotate_browser__run(struct annotate_browser *browser, >>>> } >>>> int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel, >>>> - struct hist_browser_timer *hbt) >>>> + struct hist_browser_timer *hbt, u64 init_ip) >>>> { >>>> /* reset abort key so that it can get Ctrl-C as a key */ >>>> SLang_reset_tty(); >>>> SLang_init_tty(0, 0, 0); >>>> SLtty_set_suspend_state(true); >>>> - return __hist_entry__tui_annotate(he, &he->ms, evsel, hbt); >>>> + return __hist_entry__tui_annotate(he, &he->ms, evsel, hbt, init_ip); >>>> } >>>> int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms, >>>> struct evsel *evsel, >>>> - struct hist_browser_timer *hbt) >>>> + struct hist_browser_timer *hbt, u64 init_ip) >>>> { >>>> struct symbol *sym = ms->sym; >>>> struct annotation *notes = symbol__annotation(sym); >>>> + struct disasm_line *dl = NULL; >>>> struct annotate_browser browser = { >>>> .b = { >>>> .refresh = annotate_browser__refresh, >>>> @@ -1163,6 +1183,18 @@ int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms, >>>> browser.he = &annotate_he; >>>> } >>>> + /* >>>> + * If init_ip is set, it means that there should be a line >>>> + * intentionally selected, not based on the percentages >>>> + * which caculated by the event sampling. In this case, we >>>> + * convey this information into the browser selection, where >>>> + * the selection in other cases should be empty. >>>> + */ >>>> + if (init_ip != NO_INITIAL_IP) { >>>> + dl = find_disasm_line(sym, init_ip, false); >>>> + browser.selection = &dl->al; >>>> + } >>>> + >>>> ui_helpline__push("Press ESC to exit"); >>>> if (annotate_opts.code_with_type) { >>>> diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c >>>> index 487c0b08c003..3675a703de11 100644 >>>> --- a/tools/perf/ui/browsers/hists.c >>>> +++ b/tools/perf/ui/browsers/hists.c >>>> @@ -2485,7 +2485,7 @@ do_annotate(struct hist_browser *browser, struct popup_action *act) >>>> evsel = hists_to_evsel(browser->hists); >>>> he = hist_browser__selected_entry(browser); >>>> - err = __hist_entry__tui_annotate(he, &act->ms, evsel, browser->hbt); >>>> + err = __hist_entry__tui_annotate(he, &act->ms, evsel, browser->hbt, NO_INITIAL_IP); >>>> /* >>>> * offer option to annotate the other branch source or target >>>> * (if they exists) when returning from annotate >>>> diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c >>>> index c9b220d9f924..937effbeda69 100644 >>>> --- a/tools/perf/util/annotate.c >>>> +++ b/tools/perf/util/annotate.c >>>> @@ -2622,7 +2622,7 @@ int annotate_get_insn_location(struct arch *arch, struct disasm_line *dl, >>>> return 0; >>>> } >>>> -static struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip, >>>> +struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip, >>>> bool allow_update) >>>> { >>>> struct disasm_line *dl; >>>> diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h >>>> index eaf6c8aa7f47..bbe67588bbdd 100644 >>>> --- a/tools/perf/util/annotate.h >>>> +++ b/tools/perf/util/annotate.h >>>> @@ -170,6 +170,8 @@ static inline struct disasm_line *disasm_line(struct annotation_line *al) >>>> return al ? container_of(al, struct disasm_line, al) : NULL; >>>> } >>>> +struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip, bool allow_update); >>>> + >>>> /* >>>> * Is this offset in the same function as the line it is used? >>>> * asm functions jump to other functions, for instance. >>>> diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h >>>> index c64005278687..e544e1795f19 100644 >>>> --- a/tools/perf/util/hist.h >>>> +++ b/tools/perf/util/hist.h >>>> @@ -713,12 +713,14 @@ struct block_hist { >>>> #include "../ui/keysyms.h" >>>> void attr_to_script(char *buf, struct perf_event_attr *attr); >>>> +#define NO_INITIAL_IP 0 >>>> + >>>> int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms, >>>> struct evsel *evsel, >>>> - struct hist_browser_timer *hbt); >>>> + struct hist_browser_timer *hbt, u64 init_ip); >>>> int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel, >>>> - struct hist_browser_timer *hbt); >>>> + struct hist_browser_timer *hbt, u64 init_ip); >>>> int evlist__tui_browse_hists(struct evlist *evlist, const char *help, struct hist_browser_timer *hbt, >>>> float min_pcnt, struct perf_env *env, bool warn_lost_event); >>>> -- >>>> 2.47.1 >>>>
Perf c2c report currently specified the code address and source:line
information in the cacheline browser, while it is lack of annotation
support like perf report to directly show the disassembly code for
the particular symbol shared that same cacheline. This patches add
a key 'a' binding to the cacheline browser which reuse the annotation
browser to show the disassembly view for easier analysis of cacheline
contentions. By default, the 'TAB' key navigate to the code address
where the contentions detected.
Signed-off-by: Tianyou Li <tianyou.li@intel.com>
Reviewed-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
Reviewed-by: Thomas Falcon <thomas.falcon@intel.com>
Reviewed-by: Jiebin Sun <jiebin.sun@intel.com>
Reviewed-by: Pan Deng <pan.deng@intel.com>
Reviewed-by: Zhiguo Zhou <zhiguo.zhou@intel.com>
Reviewed-by: Wangyang Guo <wangyang.guo@intel.com>
---
tools/perf/builtin-annotate.c | 2 +-
tools/perf/builtin-c2c.c | 130 ++++++++++++++++++++++++++++--
tools/perf/ui/browsers/annotate.c | 41 +++++++++-
tools/perf/ui/browsers/hists.c | 2 +-
tools/perf/util/annotate.c | 2 +-
tools/perf/util/annotate.h | 2 +
tools/perf/util/hist.h | 6 +-
7 files changed, 171 insertions(+), 14 deletions(-)
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 646f43b0f7c4..f977e97a9c96 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -519,7 +519,7 @@ static void hists__find_annotations(struct hists *hists,
/* skip missing symbols */
nd = rb_next(nd);
} else if (use_browser == 1) {
- key = hist_entry__tui_annotate(he, evsel, NULL);
+ key = hist_entry__tui_annotate(he, evsel, NULL, NO_INITIAL_IP);
switch (key) {
case -1:
diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c
index 9e9ff471ddd1..bf2136d062ef 100644
--- a/tools/perf/builtin-c2c.c
+++ b/tools/perf/builtin-c2c.c
@@ -45,6 +45,8 @@
#include "pmus.h"
#include "string2.h"
#include "util/util.h"
+#include "util/symbol.h"
+#include "util/annotate.h"
struct c2c_hists {
struct hists hists;
@@ -62,6 +64,7 @@ struct compute_stats {
struct c2c_hist_entry {
struct c2c_hists *hists;
+ struct evsel *evsel;
struct c2c_stats stats;
unsigned long *cpuset;
unsigned long *nodeset;
@@ -225,6 +228,12 @@ he__get_c2c_hists(struct hist_entry *he,
return hists;
}
+static void c2c_he__set_evsel(struct c2c_hist_entry *c2c_he,
+ struct evsel *evsel)
+{
+ c2c_he->evsel = evsel;
+}
+
static void c2c_he__set_cpu(struct c2c_hist_entry *c2c_he,
struct perf_sample *sample)
{
@@ -334,6 +343,7 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused,
c2c_he__set_cpu(c2c_he, sample);
c2c_he__set_node(c2c_he, sample);
+ c2c_he__set_evsel(c2c_he, evsel);
hists__inc_nr_samples(&c2c_hists->hists, he->filtered);
ret = hist_entry__append_callchain(he, sample);
@@ -371,6 +381,7 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused,
c2c_he__set_cpu(c2c_he, sample);
c2c_he__set_node(c2c_he, sample);
+ c2c_he__set_evsel(c2c_he, evsel);
hists__inc_nr_samples(&c2c_hists->hists, he->filtered);
ret = hist_entry__append_callchain(he, sample);
@@ -2550,6 +2561,35 @@ static void perf_c2c__hists_fprintf(FILE *out, struct perf_session *session)
}
#ifdef HAVE_SLANG_SUPPORT
+
+static int perf_c2c__toggle_annotation(struct hist_browser *browser)
+{
+ struct hist_entry *he = browser->he_selection;
+ struct symbol *sym = NULL;
+ struct c2c_hist_entry *c2c_he = NULL;
+ struct annotated_source *src = NULL;
+
+ if (he == NULL) {
+ ui_browser__help_window(&browser->b, "No entry selected for annotation");
+ return 0;
+ }
+ sym = (&he->ms)->sym;
+
+ if (sym == NULL) {
+ ui_browser__help_window(&browser->b, "Can not annotate, no symbol found");
+ return 0;
+ }
+
+ src = symbol__hists(sym, 0);
+ if (src == NULL) {
+ ui_browser__help_window(&browser->b, "Failed to initialize annotation source");
+ return 0;
+ }
+
+ c2c_he = container_of(he, struct c2c_hist_entry, he);
+ return hist_entry__tui_annotate(he, c2c_he->evsel, NULL, he->ip);
+}
+
static void c2c_browser__update_nr_entries(struct hist_browser *hb)
{
u64 nr_entries = 0;
@@ -2617,6 +2657,7 @@ static int perf_c2c__browse_cacheline(struct hist_entry *he)
" ENTER Toggle callchains (if present) \n"
" n Toggle Node details info \n"
" s Toggle full length of symbol and source line columns \n"
+ " a Toggle annotation view \n"
" q Return back to cacheline list \n";
if (!he)
@@ -2651,6 +2692,9 @@ static int perf_c2c__browse_cacheline(struct hist_entry *he)
c2c.node_info = (c2c.node_info + 1) % 3;
setup_nodes_header();
break;
+ case 'a':
+ perf_c2c__toggle_annotation(browser);
+ break;
case 'q':
goto out;
case '?':
@@ -2989,6 +3033,11 @@ static int setup_coalesce(const char *coalesce, bool no_source)
return 0;
}
+static bool perf_c2c__has_annotation(void)
+{
+ return use_browser == 1;
+}
+
static int perf_c2c__report(int argc, const char **argv)
{
struct itrace_synth_opts itrace_synth_opts = {
@@ -3006,6 +3055,7 @@ static int perf_c2c__report(int argc, const char **argv)
const char *display = NULL;
const char *coalesce = NULL;
bool no_source = false;
+ const char *disassembler_style = NULL, *objdump_path = NULL, *addr2line_path = NULL;
const struct option options[] = {
OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
"file", "vmlinux pathname"),
@@ -3033,6 +3083,12 @@ static int perf_c2c__report(int argc, const char **argv)
OPT_BOOLEAN(0, "stitch-lbr", &c2c.stitch_lbr,
"Enable LBR callgraph stitching approach"),
OPT_BOOLEAN(0, "double-cl", &chk_double_cl, "Detect adjacent cacheline false sharing"),
+ OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style",
+ "Specify disassembler style (e.g. -M intel for intel syntax)"),
+ OPT_STRING(0, "objdump", &objdump_path, "path",
+ "objdump binary to use for disassembly and annotations"),
+ OPT_STRING(0, "addr2line", &addr2line_path, "path",
+ "addr2line binary to use for line numbers"),
OPT_PARENT(c2c_options),
OPT_END()
};
@@ -3040,6 +3096,12 @@ static int perf_c2c__report(int argc, const char **argv)
const char *output_str, *sort_str = NULL;
struct perf_env *env;
+ annotation_options__init();
+
+ err = hists__init();
+ if (err < 0)
+ goto out;
+
argc = parse_options(argc, argv, options, report_c2c_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
if (argc)
@@ -3052,6 +3114,36 @@ static int perf_c2c__report(int argc, const char **argv)
if (c2c.stats_only)
c2c.use_stdio = true;
+ /**
+ * Annotation related options
+ * disassembler_style, objdump_path, addr2line_path
+ * are set in the c2c_options, so we can use them here.
+ */
+ if (disassembler_style) {
+ annotate_opts.disassembler_style = strdup(disassembler_style);
+ if (!annotate_opts.disassembler_style) {
+ err = -ENOMEM;
+ pr_err("Failed to allocate memory for annotation options\n");
+ goto out;
+ }
+ }
+ if (objdump_path) {
+ annotate_opts.objdump_path = strdup(objdump_path);
+ if (!annotate_opts.objdump_path) {
+ err = -ENOMEM;
+ pr_err("Failed to allocate memory for annotation options\n");
+ goto out;
+ }
+ }
+ if (addr2line_path) {
+ symbol_conf.addr2line_path = strdup(addr2line_path);
+ if (!symbol_conf.addr2line_path) {
+ err = -ENOMEM;
+ pr_err("Failed to allocate memory for annotation options\n");
+ goto out;
+ }
+ }
+
err = symbol__validate_sym_arguments();
if (err)
goto out;
@@ -3126,6 +3218,38 @@ static int perf_c2c__report(int argc, const char **argv)
if (err)
goto out_mem2node;
+ if (c2c.use_stdio)
+ use_browser = 0;
+ else
+ use_browser = 1;
+
+ /*
+ * Only in the TUI browser we are doing integrated annotation,
+ * so don't allocate extra space that won't be used in the stdio
+ * implementation.
+ */
+ if (perf_c2c__has_annotation()) {
+ int ret = symbol__annotation_init();
+
+ if (ret < 0)
+ goto out_mem2node;
+ /*
+ * For searching by name on the "Browse map details".
+ * providing it only in verbose mode not to bloat too
+ * much struct symbol.
+ */
+ if (verbose > 0) {
+ /*
+ * XXX: Need to provide a less kludgy way to ask for
+ * more space per symbol, the u32 is for the index on
+ * the ui browser.
+ * See symbol__browser_index.
+ */
+ symbol_conf.priv_size += sizeof(u32);
+ }
+ annotation_config__init();
+ }
+
if (symbol__init(env) < 0)
goto out_mem2node;
@@ -3135,11 +3259,6 @@ static int perf_c2c__report(int argc, const char **argv)
goto out_mem2node;
}
- if (c2c.use_stdio)
- use_browser = 0;
- else
- use_browser = 1;
-
setup_browser(false);
err = perf_session__process_events(session);
@@ -3210,6 +3329,7 @@ static int perf_c2c__report(int argc, const char **argv)
out_session:
perf_session__delete(session);
out:
+ annotation_options__exit();
return err;
}
diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c
index 8fe699f98542..63d0e28fb991 100644
--- a/tools/perf/ui/browsers/annotate.c
+++ b/tools/perf/ui/browsers/annotate.c
@@ -605,7 +605,7 @@ static bool annotate_browser__callq(struct annotate_browser *browser,
target_ms.map = ms->map;
target_ms.sym = dl->ops.target.sym;
annotation__unlock(notes);
- __hist_entry__tui_annotate(browser->he, &target_ms, evsel, hbt);
+ __hist_entry__tui_annotate(browser->he, &target_ms, evsel, hbt, NO_INITIAL_IP);
/*
* The annotate_browser above changed the title with the target function
@@ -864,6 +864,7 @@ static int annotate_browser__run(struct annotate_browser *browser,
const char *help = "Press 'h' for help on key bindings";
int delay_secs = hbt ? hbt->refresh : 0;
char *br_cntr_text = NULL;
+ u64 init_ip = NO_INITIAL_IP;
char title[256];
int key;
@@ -873,6 +874,13 @@ static int annotate_browser__run(struct annotate_browser *browser,
annotate_browser__calc_percent(browser, evsel);
+ /* the selection are intentionally even not from the sample percentage */
+ if (browser->entries.rb_node == NULL && browser->selection) {
+ init_ip = sym->start + browser->selection->offset;
+ disasm_rb_tree__insert(browser, browser->selection);
+ browser->curr_hot = rb_last(&browser->entries);
+ }
+
if (browser->curr_hot) {
annotate_browser__set_rb_top(browser, browser->curr_hot);
browser->b.navkeypressed = false;
@@ -973,6 +981,18 @@ static int annotate_browser__run(struct annotate_browser *browser,
ui_helpline__puts(help);
annotate__scnprintf_title(hists, title, sizeof(title));
annotate_browser__show(browser, title, help);
+ /* Previous RB tree may not valid, need refresh according to new entries*/
+ if (init_ip != NO_INITIAL_IP) {
+ struct disasm_line *dl = find_disasm_line(sym, init_ip, true);
+
+ browser->curr_hot = NULL;
+ browser->entries.rb_node = NULL;
+ if (dl != NULL) {
+ disasm_rb_tree__insert(browser, &dl->al);
+ browser->curr_hot = rb_last(&browser->entries);
+ }
+ nd = browser->curr_hot;
+ }
continue;
case 'o':
annotate_opts.use_offset = !annotate_opts.use_offset;
@@ -1106,22 +1126,23 @@ static int annotate_browser__run(struct annotate_browser *browser,
}
int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel,
- struct hist_browser_timer *hbt)
+ struct hist_browser_timer *hbt, u64 init_ip)
{
/* reset abort key so that it can get Ctrl-C as a key */
SLang_reset_tty();
SLang_init_tty(0, 0, 0);
SLtty_set_suspend_state(true);
- return __hist_entry__tui_annotate(he, &he->ms, evsel, hbt);
+ return __hist_entry__tui_annotate(he, &he->ms, evsel, hbt, init_ip);
}
int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms,
struct evsel *evsel,
- struct hist_browser_timer *hbt)
+ struct hist_browser_timer *hbt, u64 init_ip)
{
struct symbol *sym = ms->sym;
struct annotation *notes = symbol__annotation(sym);
+ struct disasm_line *dl = NULL;
struct annotate_browser browser = {
.b = {
.refresh = annotate_browser__refresh,
@@ -1173,6 +1194,18 @@ int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms,
browser.he = &annotate_he;
}
+ /*
+ * If init_ip is set, it means that there should be a line
+ * intentionally selected, not based on the percentages
+ * which caculated by the event sampling. In this case, we
+ * convey this information into the browser selection, where
+ * the selection in other cases should be empty.
+ */
+ if (init_ip != NO_INITIAL_IP) {
+ dl = find_disasm_line(sym, init_ip, false);
+ browser.selection = &dl->al;
+ }
+
ui_helpline__push("Press ESC to exit");
if (annotate_opts.code_with_type) {
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c
index 487c0b08c003..3675a703de11 100644
--- a/tools/perf/ui/browsers/hists.c
+++ b/tools/perf/ui/browsers/hists.c
@@ -2485,7 +2485,7 @@ do_annotate(struct hist_browser *browser, struct popup_action *act)
evsel = hists_to_evsel(browser->hists);
he = hist_browser__selected_entry(browser);
- err = __hist_entry__tui_annotate(he, &act->ms, evsel, browser->hbt);
+ err = __hist_entry__tui_annotate(he, &act->ms, evsel, browser->hbt, NO_INITIAL_IP);
/*
* offer option to annotate the other branch source or target
* (if they exists) when returning from annotate
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index c9b220d9f924..937effbeda69 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -2622,7 +2622,7 @@ int annotate_get_insn_location(struct arch *arch, struct disasm_line *dl,
return 0;
}
-static struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip,
+struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip,
bool allow_update)
{
struct disasm_line *dl;
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h
index eaf6c8aa7f47..bbe67588bbdd 100644
--- a/tools/perf/util/annotate.h
+++ b/tools/perf/util/annotate.h
@@ -170,6 +170,8 @@ static inline struct disasm_line *disasm_line(struct annotation_line *al)
return al ? container_of(al, struct disasm_line, al) : NULL;
}
+struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip, bool allow_update);
+
/*
* Is this offset in the same function as the line it is used?
* asm functions jump to other functions, for instance.
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
index c64005278687..e544e1795f19 100644
--- a/tools/perf/util/hist.h
+++ b/tools/perf/util/hist.h
@@ -713,12 +713,14 @@ struct block_hist {
#include "../ui/keysyms.h"
void attr_to_script(char *buf, struct perf_event_attr *attr);
+#define NO_INITIAL_IP 0
+
int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms,
struct evsel *evsel,
- struct hist_browser_timer *hbt);
+ struct hist_browser_timer *hbt, u64 init_ip);
int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel,
- struct hist_browser_timer *hbt);
+ struct hist_browser_timer *hbt, u64 init_ip);
int evlist__tui_browse_hists(struct evlist *evlist, const char *help, struct hist_browser_timer *hbt,
float min_pcnt, struct perf_env *env, bool warn_lost_event);
--
2.47.1
Rebased with latest perf-tools-next. Looking forward to your review comments. Thanks. Regards, Tianyou On 9/28/2025 5:02 PM, Tianyou Li wrote: > Perf c2c report currently specified the code address and source:line > information in the cacheline browser, while it is lack of annotation > support like perf report to directly show the disassembly code for > the particular symbol shared that same cacheline. This patches add > a key 'a' binding to the cacheline browser which reuse the annotation > browser to show the disassembly view for easier analysis of cacheline > contentions. By default, the 'TAB' key navigate to the code address > where the contentions detected. > > Signed-off-by: Tianyou Li <tianyou.li@intel.com> > Reviewed-by: Dapeng Mi <dapeng1.mi@linux.intel.com> > Reviewed-by: Thomas Falcon <thomas.falcon@intel.com> > Reviewed-by: Jiebin Sun <jiebin.sun@intel.com> > Reviewed-by: Pan Deng <pan.deng@intel.com> > Reviewed-by: Zhiguo Zhou <zhiguo.zhou@intel.com> > Reviewed-by: Wangyang Guo <wangyang.guo@intel.com> > --- > tools/perf/builtin-annotate.c | 2 +- > tools/perf/builtin-c2c.c | 130 ++++++++++++++++++++++++++++-- > tools/perf/ui/browsers/annotate.c | 41 +++++++++- > tools/perf/ui/browsers/hists.c | 2 +- > tools/perf/util/annotate.c | 2 +- > tools/perf/util/annotate.h | 2 + > tools/perf/util/hist.h | 6 +- > 7 files changed, 171 insertions(+), 14 deletions(-) > > diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c > index 646f43b0f7c4..f977e97a9c96 100644 > --- a/tools/perf/builtin-annotate.c > +++ b/tools/perf/builtin-annotate.c > @@ -519,7 +519,7 @@ static void hists__find_annotations(struct hists *hists, > /* skip missing symbols */ > nd = rb_next(nd); > } else if (use_browser == 1) { > - key = hist_entry__tui_annotate(he, evsel, NULL); > + key = hist_entry__tui_annotate(he, evsel, NULL, NO_INITIAL_IP); > > switch (key) { > case -1: > diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c > index 9e9ff471ddd1..bf2136d062ef 100644 > --- a/tools/perf/builtin-c2c.c > +++ b/tools/perf/builtin-c2c.c > @@ -45,6 +45,8 @@ > #include "pmus.h" > #include "string2.h" > #include "util/util.h" > +#include "util/symbol.h" > +#include "util/annotate.h" > > struct c2c_hists { > struct hists hists; > @@ -62,6 +64,7 @@ struct compute_stats { > > struct c2c_hist_entry { > struct c2c_hists *hists; > + struct evsel *evsel; > struct c2c_stats stats; > unsigned long *cpuset; > unsigned long *nodeset; > @@ -225,6 +228,12 @@ he__get_c2c_hists(struct hist_entry *he, > return hists; > } > > +static void c2c_he__set_evsel(struct c2c_hist_entry *c2c_he, > + struct evsel *evsel) > +{ > + c2c_he->evsel = evsel; > +} > + > static void c2c_he__set_cpu(struct c2c_hist_entry *c2c_he, > struct perf_sample *sample) > { > @@ -334,6 +343,7 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused, > > c2c_he__set_cpu(c2c_he, sample); > c2c_he__set_node(c2c_he, sample); > + c2c_he__set_evsel(c2c_he, evsel); > > hists__inc_nr_samples(&c2c_hists->hists, he->filtered); > ret = hist_entry__append_callchain(he, sample); > @@ -371,6 +381,7 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused, > > c2c_he__set_cpu(c2c_he, sample); > c2c_he__set_node(c2c_he, sample); > + c2c_he__set_evsel(c2c_he, evsel); > > hists__inc_nr_samples(&c2c_hists->hists, he->filtered); > ret = hist_entry__append_callchain(he, sample); > @@ -2550,6 +2561,35 @@ static void perf_c2c__hists_fprintf(FILE *out, struct perf_session *session) > } > > #ifdef HAVE_SLANG_SUPPORT > + > +static int perf_c2c__toggle_annotation(struct hist_browser *browser) > +{ > + struct hist_entry *he = browser->he_selection; > + struct symbol *sym = NULL; > + struct c2c_hist_entry *c2c_he = NULL; > + struct annotated_source *src = NULL; > + > + if (he == NULL) { > + ui_browser__help_window(&browser->b, "No entry selected for annotation"); > + return 0; > + } > + sym = (&he->ms)->sym; > + > + if (sym == NULL) { > + ui_browser__help_window(&browser->b, "Can not annotate, no symbol found"); > + return 0; > + } > + > + src = symbol__hists(sym, 0); > + if (src == NULL) { > + ui_browser__help_window(&browser->b, "Failed to initialize annotation source"); > + return 0; > + } > + > + c2c_he = container_of(he, struct c2c_hist_entry, he); > + return hist_entry__tui_annotate(he, c2c_he->evsel, NULL, he->ip); > +} > + > static void c2c_browser__update_nr_entries(struct hist_browser *hb) > { > u64 nr_entries = 0; > @@ -2617,6 +2657,7 @@ static int perf_c2c__browse_cacheline(struct hist_entry *he) > " ENTER Toggle callchains (if present) \n" > " n Toggle Node details info \n" > " s Toggle full length of symbol and source line columns \n" > + " a Toggle annotation view \n" > " q Return back to cacheline list \n"; > > if (!he) > @@ -2651,6 +2692,9 @@ static int perf_c2c__browse_cacheline(struct hist_entry *he) > c2c.node_info = (c2c.node_info + 1) % 3; > setup_nodes_header(); > break; > + case 'a': > + perf_c2c__toggle_annotation(browser); > + break; > case 'q': > goto out; > case '?': > @@ -2989,6 +3033,11 @@ static int setup_coalesce(const char *coalesce, bool no_source) > return 0; > } > > +static bool perf_c2c__has_annotation(void) > +{ > + return use_browser == 1; > +} > + > static int perf_c2c__report(int argc, const char **argv) > { > struct itrace_synth_opts itrace_synth_opts = { > @@ -3006,6 +3055,7 @@ static int perf_c2c__report(int argc, const char **argv) > const char *display = NULL; > const char *coalesce = NULL; > bool no_source = false; > + const char *disassembler_style = NULL, *objdump_path = NULL, *addr2line_path = NULL; > const struct option options[] = { > OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, > "file", "vmlinux pathname"), > @@ -3033,6 +3083,12 @@ static int perf_c2c__report(int argc, const char **argv) > OPT_BOOLEAN(0, "stitch-lbr", &c2c.stitch_lbr, > "Enable LBR callgraph stitching approach"), > OPT_BOOLEAN(0, "double-cl", &chk_double_cl, "Detect adjacent cacheline false sharing"), > + OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style", > + "Specify disassembler style (e.g. -M intel for intel syntax)"), > + OPT_STRING(0, "objdump", &objdump_path, "path", > + "objdump binary to use for disassembly and annotations"), > + OPT_STRING(0, "addr2line", &addr2line_path, "path", > + "addr2line binary to use for line numbers"), > OPT_PARENT(c2c_options), > OPT_END() > }; > @@ -3040,6 +3096,12 @@ static int perf_c2c__report(int argc, const char **argv) > const char *output_str, *sort_str = NULL; > struct perf_env *env; > > + annotation_options__init(); > + > + err = hists__init(); > + if (err < 0) > + goto out; > + > argc = parse_options(argc, argv, options, report_c2c_usage, > PARSE_OPT_STOP_AT_NON_OPTION); > if (argc) > @@ -3052,6 +3114,36 @@ static int perf_c2c__report(int argc, const char **argv) > if (c2c.stats_only) > c2c.use_stdio = true; > > + /** > + * Annotation related options > + * disassembler_style, objdump_path, addr2line_path > + * are set in the c2c_options, so we can use them here. > + */ > + if (disassembler_style) { > + annotate_opts.disassembler_style = strdup(disassembler_style); > + if (!annotate_opts.disassembler_style) { > + err = -ENOMEM; > + pr_err("Failed to allocate memory for annotation options\n"); > + goto out; > + } > + } > + if (objdump_path) { > + annotate_opts.objdump_path = strdup(objdump_path); > + if (!annotate_opts.objdump_path) { > + err = -ENOMEM; > + pr_err("Failed to allocate memory for annotation options\n"); > + goto out; > + } > + } > + if (addr2line_path) { > + symbol_conf.addr2line_path = strdup(addr2line_path); > + if (!symbol_conf.addr2line_path) { > + err = -ENOMEM; > + pr_err("Failed to allocate memory for annotation options\n"); > + goto out; > + } > + } > + > err = symbol__validate_sym_arguments(); > if (err) > goto out; > @@ -3126,6 +3218,38 @@ static int perf_c2c__report(int argc, const char **argv) > if (err) > goto out_mem2node; > > + if (c2c.use_stdio) > + use_browser = 0; > + else > + use_browser = 1; > + > + /* > + * Only in the TUI browser we are doing integrated annotation, > + * so don't allocate extra space that won't be used in the stdio > + * implementation. > + */ > + if (perf_c2c__has_annotation()) { > + int ret = symbol__annotation_init(); > + > + if (ret < 0) > + goto out_mem2node; > + /* > + * For searching by name on the "Browse map details". > + * providing it only in verbose mode not to bloat too > + * much struct symbol. > + */ > + if (verbose > 0) { > + /* > + * XXX: Need to provide a less kludgy way to ask for > + * more space per symbol, the u32 is for the index on > + * the ui browser. > + * See symbol__browser_index. > + */ > + symbol_conf.priv_size += sizeof(u32); > + } > + annotation_config__init(); > + } > + > if (symbol__init(env) < 0) > goto out_mem2node; > > @@ -3135,11 +3259,6 @@ static int perf_c2c__report(int argc, const char **argv) > goto out_mem2node; > } > > - if (c2c.use_stdio) > - use_browser = 0; > - else > - use_browser = 1; > - > setup_browser(false); > > err = perf_session__process_events(session); > @@ -3210,6 +3329,7 @@ static int perf_c2c__report(int argc, const char **argv) > out_session: > perf_session__delete(session); > out: > + annotation_options__exit(); > return err; > } > > diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c > index 8fe699f98542..63d0e28fb991 100644 > --- a/tools/perf/ui/browsers/annotate.c > +++ b/tools/perf/ui/browsers/annotate.c > @@ -605,7 +605,7 @@ static bool annotate_browser__callq(struct annotate_browser *browser, > target_ms.map = ms->map; > target_ms.sym = dl->ops.target.sym; > annotation__unlock(notes); > - __hist_entry__tui_annotate(browser->he, &target_ms, evsel, hbt); > + __hist_entry__tui_annotate(browser->he, &target_ms, evsel, hbt, NO_INITIAL_IP); > > /* > * The annotate_browser above changed the title with the target function > @@ -864,6 +864,7 @@ static int annotate_browser__run(struct annotate_browser *browser, > const char *help = "Press 'h' for help on key bindings"; > int delay_secs = hbt ? hbt->refresh : 0; > char *br_cntr_text = NULL; > + u64 init_ip = NO_INITIAL_IP; > char title[256]; > int key; > > @@ -873,6 +874,13 @@ static int annotate_browser__run(struct annotate_browser *browser, > > annotate_browser__calc_percent(browser, evsel); > > + /* the selection are intentionally even not from the sample percentage */ > + if (browser->entries.rb_node == NULL && browser->selection) { > + init_ip = sym->start + browser->selection->offset; > + disasm_rb_tree__insert(browser, browser->selection); > + browser->curr_hot = rb_last(&browser->entries); > + } > + > if (browser->curr_hot) { > annotate_browser__set_rb_top(browser, browser->curr_hot); > browser->b.navkeypressed = false; > @@ -973,6 +981,18 @@ static int annotate_browser__run(struct annotate_browser *browser, > ui_helpline__puts(help); > annotate__scnprintf_title(hists, title, sizeof(title)); > annotate_browser__show(browser, title, help); > + /* Previous RB tree may not valid, need refresh according to new entries*/ > + if (init_ip != NO_INITIAL_IP) { > + struct disasm_line *dl = find_disasm_line(sym, init_ip, true); > + > + browser->curr_hot = NULL; > + browser->entries.rb_node = NULL; > + if (dl != NULL) { > + disasm_rb_tree__insert(browser, &dl->al); > + browser->curr_hot = rb_last(&browser->entries); > + } > + nd = browser->curr_hot; > + } > continue; > case 'o': > annotate_opts.use_offset = !annotate_opts.use_offset; > @@ -1106,22 +1126,23 @@ static int annotate_browser__run(struct annotate_browser *browser, > } > > int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel, > - struct hist_browser_timer *hbt) > + struct hist_browser_timer *hbt, u64 init_ip) > { > /* reset abort key so that it can get Ctrl-C as a key */ > SLang_reset_tty(); > SLang_init_tty(0, 0, 0); > SLtty_set_suspend_state(true); > > - return __hist_entry__tui_annotate(he, &he->ms, evsel, hbt); > + return __hist_entry__tui_annotate(he, &he->ms, evsel, hbt, init_ip); > } > > int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms, > struct evsel *evsel, > - struct hist_browser_timer *hbt) > + struct hist_browser_timer *hbt, u64 init_ip) > { > struct symbol *sym = ms->sym; > struct annotation *notes = symbol__annotation(sym); > + struct disasm_line *dl = NULL; > struct annotate_browser browser = { > .b = { > .refresh = annotate_browser__refresh, > @@ -1173,6 +1194,18 @@ int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms, > browser.he = &annotate_he; > } > > + /* > + * If init_ip is set, it means that there should be a line > + * intentionally selected, not based on the percentages > + * which caculated by the event sampling. In this case, we > + * convey this information into the browser selection, where > + * the selection in other cases should be empty. > + */ > + if (init_ip != NO_INITIAL_IP) { > + dl = find_disasm_line(sym, init_ip, false); > + browser.selection = &dl->al; > + } > + > ui_helpline__push("Press ESC to exit"); > > if (annotate_opts.code_with_type) { > diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c > index 487c0b08c003..3675a703de11 100644 > --- a/tools/perf/ui/browsers/hists.c > +++ b/tools/perf/ui/browsers/hists.c > @@ -2485,7 +2485,7 @@ do_annotate(struct hist_browser *browser, struct popup_action *act) > evsel = hists_to_evsel(browser->hists); > > he = hist_browser__selected_entry(browser); > - err = __hist_entry__tui_annotate(he, &act->ms, evsel, browser->hbt); > + err = __hist_entry__tui_annotate(he, &act->ms, evsel, browser->hbt, NO_INITIAL_IP); > /* > * offer option to annotate the other branch source or target > * (if they exists) when returning from annotate > diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c > index c9b220d9f924..937effbeda69 100644 > --- a/tools/perf/util/annotate.c > +++ b/tools/perf/util/annotate.c > @@ -2622,7 +2622,7 @@ int annotate_get_insn_location(struct arch *arch, struct disasm_line *dl, > return 0; > } > > -static struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip, > +struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip, > bool allow_update) > { > struct disasm_line *dl; > diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h > index eaf6c8aa7f47..bbe67588bbdd 100644 > --- a/tools/perf/util/annotate.h > +++ b/tools/perf/util/annotate.h > @@ -170,6 +170,8 @@ static inline struct disasm_line *disasm_line(struct annotation_line *al) > return al ? container_of(al, struct disasm_line, al) : NULL; > } > > +struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip, bool allow_update); > + > /* > * Is this offset in the same function as the line it is used? > * asm functions jump to other functions, for instance. > diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h > index c64005278687..e544e1795f19 100644 > --- a/tools/perf/util/hist.h > +++ b/tools/perf/util/hist.h > @@ -713,12 +713,14 @@ struct block_hist { > #include "../ui/keysyms.h" > void attr_to_script(char *buf, struct perf_event_attr *attr); > > +#define NO_INITIAL_IP 0 > + > int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms, > struct evsel *evsel, > - struct hist_browser_timer *hbt); > + struct hist_browser_timer *hbt, u64 init_ip); > > int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel, > - struct hist_browser_timer *hbt); > + struct hist_browser_timer *hbt, u64 init_ip); > > int evlist__tui_browse_hists(struct evlist *evlist, const char *help, struct hist_browser_timer *hbt, > float min_pcnt, struct perf_env *env, bool warn_lost_event);
Hello, On Sun, Sep 28, 2025 at 04:16:16PM +0800, Li, Tianyou wrote: > Rebased with latest perf-tools-next. Looking forward to your review > comments. Thanks. Sorry for the delay, I was on vacation. > On 9/28/2025 5:02 PM, Tianyou Li wrote: > > Perf c2c report currently specified the code address and source:line > > information in the cacheline browser, while it is lack of annotation > > support like perf report to directly show the disassembly code for > > the particular symbol shared that same cacheline. This patches add > > a key 'a' binding to the cacheline browser which reuse the annotation > > browser to show the disassembly view for easier analysis of cacheline > > contentions. By default, the 'TAB' key navigate to the code address > > where the contentions detected. > > > > Signed-off-by: Tianyou Li <tianyou.li@intel.com> > > Reviewed-by: Dapeng Mi <dapeng1.mi@linux.intel.com> > > Reviewed-by: Thomas Falcon <thomas.falcon@intel.com> > > Reviewed-by: Jiebin Sun <jiebin.sun@intel.com> > > Reviewed-by: Pan Deng <pan.deng@intel.com> > > Reviewed-by: Zhiguo Zhou <zhiguo.zhou@intel.com> > > Reviewed-by: Wangyang Guo <wangyang.guo@intel.com> > > --- > > tools/perf/builtin-annotate.c | 2 +- > > tools/perf/builtin-c2c.c | 130 ++++++++++++++++++++++++++++-- > > tools/perf/ui/browsers/annotate.c | 41 +++++++++- > > tools/perf/ui/browsers/hists.c | 2 +- > > tools/perf/util/annotate.c | 2 +- > > tools/perf/util/annotate.h | 2 + > > tools/perf/util/hist.h | 6 +- > > 7 files changed, 171 insertions(+), 14 deletions(-) > > > > diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c > > index 646f43b0f7c4..f977e97a9c96 100644 > > --- a/tools/perf/builtin-annotate.c > > +++ b/tools/perf/builtin-annotate.c > > @@ -519,7 +519,7 @@ static void hists__find_annotations(struct hists *hists, > > /* skip missing symbols */ > > nd = rb_next(nd); > > } else if (use_browser == 1) { > > - key = hist_entry__tui_annotate(he, evsel, NULL); > > + key = hist_entry__tui_annotate(he, evsel, NULL, NO_INITIAL_IP); > > switch (key) { > > case -1: > > diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c > > index 9e9ff471ddd1..bf2136d062ef 100644 > > --- a/tools/perf/builtin-c2c.c > > +++ b/tools/perf/builtin-c2c.c > > @@ -45,6 +45,8 @@ > > #include "pmus.h" > > #include "string2.h" > > #include "util/util.h" > > +#include "util/symbol.h" > > +#include "util/annotate.h" > > struct c2c_hists { > > struct hists hists; > > @@ -62,6 +64,7 @@ struct compute_stats { > > struct c2c_hist_entry { > > struct c2c_hists *hists; > > + struct evsel *evsel; > > struct c2c_stats stats; > > unsigned long *cpuset; > > unsigned long *nodeset; > > @@ -225,6 +228,12 @@ he__get_c2c_hists(struct hist_entry *he, > > return hists; > > } > > +static void c2c_he__set_evsel(struct c2c_hist_entry *c2c_he, > > + struct evsel *evsel) > > +{ > > + c2c_he->evsel = evsel; > > +} > > + > > static void c2c_he__set_cpu(struct c2c_hist_entry *c2c_he, > > struct perf_sample *sample) > > { > > @@ -334,6 +343,7 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused, > > c2c_he__set_cpu(c2c_he, sample); > > c2c_he__set_node(c2c_he, sample); > > + c2c_he__set_evsel(c2c_he, evsel); > > hists__inc_nr_samples(&c2c_hists->hists, he->filtered); > > ret = hist_entry__append_callchain(he, sample); > > @@ -371,6 +381,7 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused, > > c2c_he__set_cpu(c2c_he, sample); > > c2c_he__set_node(c2c_he, sample); > > + c2c_he__set_evsel(c2c_he, evsel); > > hists__inc_nr_samples(&c2c_hists->hists, he->filtered); > > ret = hist_entry__append_callchain(he, sample); > > @@ -2550,6 +2561,35 @@ static void perf_c2c__hists_fprintf(FILE *out, struct perf_session *session) > > } > > #ifdef HAVE_SLANG_SUPPORT > > + > > +static int perf_c2c__toggle_annotation(struct hist_browser *browser) > > +{ > > + struct hist_entry *he = browser->he_selection; > > + struct symbol *sym = NULL; > > + struct c2c_hist_entry *c2c_he = NULL; > > + struct annotated_source *src = NULL; > > + > > + if (he == NULL) { > > + ui_browser__help_window(&browser->b, "No entry selected for annotation"); > > + return 0; > > + } > > + sym = (&he->ms)->sym; > > + > > + if (sym == NULL) { > > + ui_browser__help_window(&browser->b, "Can not annotate, no symbol found"); > > + return 0; > > + } > > + > > + src = symbol__hists(sym, 0); > > + if (src == NULL) { > > + ui_browser__help_window(&browser->b, "Failed to initialize annotation source"); > > + return 0; > > + } > > + > > + c2c_he = container_of(he, struct c2c_hist_entry, he); > > + return hist_entry__tui_annotate(he, c2c_he->evsel, NULL, he->ip); I'm skeptical about using he->ip. An hist_entry can collapse multiple samples with different IP in a symbol (even if hpp_list has symbol sort key). That means he->ip cannot represent the entry is from the specific point in the function. This might lead users to an inaccurate place in the annotation browser. I'd recommend not passing IP. > > +} > > + > > static void c2c_browser__update_nr_entries(struct hist_browser *hb) > > { > > u64 nr_entries = 0; > > @@ -2617,6 +2657,7 @@ static int perf_c2c__browse_cacheline(struct hist_entry *he) > > " ENTER Toggle callchains (if present) \n" > > " n Toggle Node details info \n" > > " s Toggle full length of symbol and source line columns \n" > > + " a Toggle annotation view \n" > > " q Return back to cacheline list \n"; > > if (!he) > > @@ -2651,6 +2692,9 @@ static int perf_c2c__browse_cacheline(struct hist_entry *he) > > c2c.node_info = (c2c.node_info + 1) % 3; > > setup_nodes_header(); > > break; > > + case 'a': > > + perf_c2c__toggle_annotation(browser); > > + break; > > case 'q': > > goto out; > > case '?': > > @@ -2989,6 +3033,11 @@ static int setup_coalesce(const char *coalesce, bool no_source) > > return 0; > > } > > +static bool perf_c2c__has_annotation(void) > > +{ > > + return use_browser == 1; Please check if it has symbol dimension in the c2c_hists->list like in the ui__has_annotation(). Maybe you need to add this in the c2c_hists__init_sort(). if (dim == &dim_symbol) hpp_list->sym = 1; Thanks, Namhyung > > +} > > + > > static int perf_c2c__report(int argc, const char **argv) > > { > > struct itrace_synth_opts itrace_synth_opts = { > > @@ -3006,6 +3055,7 @@ static int perf_c2c__report(int argc, const char **argv) > > const char *display = NULL; > > const char *coalesce = NULL; > > bool no_source = false; > > + const char *disassembler_style = NULL, *objdump_path = NULL, *addr2line_path = NULL; > > const struct option options[] = { > > OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, > > "file", "vmlinux pathname"), > > @@ -3033,6 +3083,12 @@ static int perf_c2c__report(int argc, const char **argv) > > OPT_BOOLEAN(0, "stitch-lbr", &c2c.stitch_lbr, > > "Enable LBR callgraph stitching approach"), > > OPT_BOOLEAN(0, "double-cl", &chk_double_cl, "Detect adjacent cacheline false sharing"), > > + OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style", > > + "Specify disassembler style (e.g. -M intel for intel syntax)"), > > + OPT_STRING(0, "objdump", &objdump_path, "path", > > + "objdump binary to use for disassembly and annotations"), > > + OPT_STRING(0, "addr2line", &addr2line_path, "path", > > + "addr2line binary to use for line numbers"), > > OPT_PARENT(c2c_options), > > OPT_END() > > }; > > @@ -3040,6 +3096,12 @@ static int perf_c2c__report(int argc, const char **argv) > > const char *output_str, *sort_str = NULL; > > struct perf_env *env; > > + annotation_options__init(); > > + > > + err = hists__init(); > > + if (err < 0) > > + goto out; > > + > > argc = parse_options(argc, argv, options, report_c2c_usage, > > PARSE_OPT_STOP_AT_NON_OPTION); > > if (argc) > > @@ -3052,6 +3114,36 @@ static int perf_c2c__report(int argc, const char **argv) > > if (c2c.stats_only) > > c2c.use_stdio = true; > > + /** > > + * Annotation related options > > + * disassembler_style, objdump_path, addr2line_path > > + * are set in the c2c_options, so we can use them here. > > + */ > > + if (disassembler_style) { > > + annotate_opts.disassembler_style = strdup(disassembler_style); > > + if (!annotate_opts.disassembler_style) { > > + err = -ENOMEM; > > + pr_err("Failed to allocate memory for annotation options\n"); > > + goto out; > > + } > > + } > > + if (objdump_path) { > > + annotate_opts.objdump_path = strdup(objdump_path); > > + if (!annotate_opts.objdump_path) { > > + err = -ENOMEM; > > + pr_err("Failed to allocate memory for annotation options\n"); > > + goto out; > > + } > > + } > > + if (addr2line_path) { > > + symbol_conf.addr2line_path = strdup(addr2line_path); > > + if (!symbol_conf.addr2line_path) { > > + err = -ENOMEM; > > + pr_err("Failed to allocate memory for annotation options\n"); > > + goto out; > > + } > > + } > > + > > err = symbol__validate_sym_arguments(); > > if (err) > > goto out; > > @@ -3126,6 +3218,38 @@ static int perf_c2c__report(int argc, const char **argv) > > if (err) > > goto out_mem2node; > > + if (c2c.use_stdio) > > + use_browser = 0; > > + else > > + use_browser = 1; > > + > > + /* > > + * Only in the TUI browser we are doing integrated annotation, > > + * so don't allocate extra space that won't be used in the stdio > > + * implementation. > > + */ > > + if (perf_c2c__has_annotation()) { > > + int ret = symbol__annotation_init(); > > + > > + if (ret < 0) > > + goto out_mem2node; > > + /* > > + * For searching by name on the "Browse map details". > > + * providing it only in verbose mode not to bloat too > > + * much struct symbol. > > + */ > > + if (verbose > 0) { > > + /* > > + * XXX: Need to provide a less kludgy way to ask for > > + * more space per symbol, the u32 is for the index on > > + * the ui browser. > > + * See symbol__browser_index. > > + */ > > + symbol_conf.priv_size += sizeof(u32); > > + } > > + annotation_config__init(); > > + } > > + > > if (symbol__init(env) < 0) > > goto out_mem2node; > > @@ -3135,11 +3259,6 @@ static int perf_c2c__report(int argc, const char **argv) > > goto out_mem2node; > > } > > - if (c2c.use_stdio) > > - use_browser = 0; > > - else > > - use_browser = 1; > > - > > setup_browser(false); > > err = perf_session__process_events(session); > > @@ -3210,6 +3329,7 @@ static int perf_c2c__report(int argc, const char **argv) > > out_session: > > perf_session__delete(session); > > out: > > + annotation_options__exit(); > > return err; > > } > > diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c > > index 8fe699f98542..63d0e28fb991 100644 > > --- a/tools/perf/ui/browsers/annotate.c > > +++ b/tools/perf/ui/browsers/annotate.c > > @@ -605,7 +605,7 @@ static bool annotate_browser__callq(struct annotate_browser *browser, > > target_ms.map = ms->map; > > target_ms.sym = dl->ops.target.sym; > > annotation__unlock(notes); > > - __hist_entry__tui_annotate(browser->he, &target_ms, evsel, hbt); > > + __hist_entry__tui_annotate(browser->he, &target_ms, evsel, hbt, NO_INITIAL_IP); > > /* > > * The annotate_browser above changed the title with the target function > > @@ -864,6 +864,7 @@ static int annotate_browser__run(struct annotate_browser *browser, > > const char *help = "Press 'h' for help on key bindings"; > > int delay_secs = hbt ? hbt->refresh : 0; > > char *br_cntr_text = NULL; > > + u64 init_ip = NO_INITIAL_IP; > > char title[256]; > > int key; > > @@ -873,6 +874,13 @@ static int annotate_browser__run(struct annotate_browser *browser, > > annotate_browser__calc_percent(browser, evsel); > > + /* the selection are intentionally even not from the sample percentage */ > > + if (browser->entries.rb_node == NULL && browser->selection) { > > + init_ip = sym->start + browser->selection->offset; > > + disasm_rb_tree__insert(browser, browser->selection); > > + browser->curr_hot = rb_last(&browser->entries); > > + } > > + > > if (browser->curr_hot) { > > annotate_browser__set_rb_top(browser, browser->curr_hot); > > browser->b.navkeypressed = false; > > @@ -973,6 +981,18 @@ static int annotate_browser__run(struct annotate_browser *browser, > > ui_helpline__puts(help); > > annotate__scnprintf_title(hists, title, sizeof(title)); > > annotate_browser__show(browser, title, help); > > + /* Previous RB tree may not valid, need refresh according to new entries*/ > > + if (init_ip != NO_INITIAL_IP) { > > + struct disasm_line *dl = find_disasm_line(sym, init_ip, true); > > + > > + browser->curr_hot = NULL; > > + browser->entries.rb_node = NULL; > > + if (dl != NULL) { > > + disasm_rb_tree__insert(browser, &dl->al); > > + browser->curr_hot = rb_last(&browser->entries); > > + } > > + nd = browser->curr_hot; > > + } > > continue; > > case 'o': > > annotate_opts.use_offset = !annotate_opts.use_offset; > > @@ -1106,22 +1126,23 @@ static int annotate_browser__run(struct annotate_browser *browser, > > } > > int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel, > > - struct hist_browser_timer *hbt) > > + struct hist_browser_timer *hbt, u64 init_ip) > > { > > /* reset abort key so that it can get Ctrl-C as a key */ > > SLang_reset_tty(); > > SLang_init_tty(0, 0, 0); > > SLtty_set_suspend_state(true); > > - return __hist_entry__tui_annotate(he, &he->ms, evsel, hbt); > > + return __hist_entry__tui_annotate(he, &he->ms, evsel, hbt, init_ip); > > } > > int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms, > > struct evsel *evsel, > > - struct hist_browser_timer *hbt) > > + struct hist_browser_timer *hbt, u64 init_ip) > > { > > struct symbol *sym = ms->sym; > > struct annotation *notes = symbol__annotation(sym); > > + struct disasm_line *dl = NULL; > > struct annotate_browser browser = { > > .b = { > > .refresh = annotate_browser__refresh, > > @@ -1173,6 +1194,18 @@ int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms, > > browser.he = &annotate_he; > > } > > + /* > > + * If init_ip is set, it means that there should be a line > > + * intentionally selected, not based on the percentages > > + * which caculated by the event sampling. In this case, we > > + * convey this information into the browser selection, where > > + * the selection in other cases should be empty. > > + */ > > + if (init_ip != NO_INITIAL_IP) { > > + dl = find_disasm_line(sym, init_ip, false); > > + browser.selection = &dl->al; > > + } > > + > > ui_helpline__push("Press ESC to exit"); > > if (annotate_opts.code_with_type) { > > diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c > > index 487c0b08c003..3675a703de11 100644 > > --- a/tools/perf/ui/browsers/hists.c > > +++ b/tools/perf/ui/browsers/hists.c > > @@ -2485,7 +2485,7 @@ do_annotate(struct hist_browser *browser, struct popup_action *act) > > evsel = hists_to_evsel(browser->hists); > > he = hist_browser__selected_entry(browser); > > - err = __hist_entry__tui_annotate(he, &act->ms, evsel, browser->hbt); > > + err = __hist_entry__tui_annotate(he, &act->ms, evsel, browser->hbt, NO_INITIAL_IP); > > /* > > * offer option to annotate the other branch source or target > > * (if they exists) when returning from annotate > > diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c > > index c9b220d9f924..937effbeda69 100644 > > --- a/tools/perf/util/annotate.c > > +++ b/tools/perf/util/annotate.c > > @@ -2622,7 +2622,7 @@ int annotate_get_insn_location(struct arch *arch, struct disasm_line *dl, > > return 0; > > } > > -static struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip, > > +struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip, > > bool allow_update) > > { > > struct disasm_line *dl; > > diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h > > index eaf6c8aa7f47..bbe67588bbdd 100644 > > --- a/tools/perf/util/annotate.h > > +++ b/tools/perf/util/annotate.h > > @@ -170,6 +170,8 @@ static inline struct disasm_line *disasm_line(struct annotation_line *al) > > return al ? container_of(al, struct disasm_line, al) : NULL; > > } > > +struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip, bool allow_update); > > + > > /* > > * Is this offset in the same function as the line it is used? > > * asm functions jump to other functions, for instance. > > diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h > > index c64005278687..e544e1795f19 100644 > > --- a/tools/perf/util/hist.h > > +++ b/tools/perf/util/hist.h > > @@ -713,12 +713,14 @@ struct block_hist { > > #include "../ui/keysyms.h" > > void attr_to_script(char *buf, struct perf_event_attr *attr); > > +#define NO_INITIAL_IP 0 > > + > > int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms, > > struct evsel *evsel, > > - struct hist_browser_timer *hbt); > > + struct hist_browser_timer *hbt, u64 init_ip); > > int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel, > > - struct hist_browser_timer *hbt); > > + struct hist_browser_timer *hbt, u64 init_ip); > > int evlist__tui_browse_hists(struct evlist *evlist, const char *help, struct hist_browser_timer *hbt, > > float min_pcnt, struct perf_env *env, bool warn_lost_event);
On 9/29/2025 4:07 PM, Namhyung Kim wrote: > Hello, > > On Sun, Sep 28, 2025 at 04:16:16PM +0800, Li, Tianyou wrote: >> Rebased with latest perf-tools-next. Looking forward to your review >> comments. Thanks. > Sorry for the delay, I was on vacation. Very appreciated for your time to review the patch. Believes you have a great vacation. > >> On 9/28/2025 5:02 PM, Tianyou Li wrote: >>> Perf c2c report currently specified the code address and source:line >>> information in the cacheline browser, while it is lack of annotation >>> support like perf report to directly show the disassembly code for >>> the particular symbol shared that same cacheline. This patches add >>> a key 'a' binding to the cacheline browser which reuse the annotation >>> browser to show the disassembly view for easier analysis of cacheline >>> contentions. By default, the 'TAB' key navigate to the code address >>> where the contentions detected. >>> >>> Signed-off-by: Tianyou Li <tianyou.li@intel.com> >>> Reviewed-by: Dapeng Mi <dapeng1.mi@linux.intel.com> >>> Reviewed-by: Thomas Falcon <thomas.falcon@intel.com> >>> Reviewed-by: Jiebin Sun <jiebin.sun@intel.com> >>> Reviewed-by: Pan Deng <pan.deng@intel.com> >>> Reviewed-by: Zhiguo Zhou <zhiguo.zhou@intel.com> >>> Reviewed-by: Wangyang Guo <wangyang.guo@intel.com> >>> --- >>> tools/perf/builtin-annotate.c | 2 +- >>> tools/perf/builtin-c2c.c | 130 ++++++++++++++++++++++++++++-- >>> tools/perf/ui/browsers/annotate.c | 41 +++++++++- >>> tools/perf/ui/browsers/hists.c | 2 +- >>> tools/perf/util/annotate.c | 2 +- >>> tools/perf/util/annotate.h | 2 + >>> tools/perf/util/hist.h | 6 +- >>> 7 files changed, 171 insertions(+), 14 deletions(-) >>> >>> diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c >>> index 646f43b0f7c4..f977e97a9c96 100644 >>> --- a/tools/perf/builtin-annotate.c >>> +++ b/tools/perf/builtin-annotate.c >>> @@ -519,7 +519,7 @@ static void hists__find_annotations(struct hists *hists, >>> /* skip missing symbols */ >>> nd = rb_next(nd); >>> } else if (use_browser == 1) { >>> - key = hist_entry__tui_annotate(he, evsel, NULL); >>> + key = hist_entry__tui_annotate(he, evsel, NULL, NO_INITIAL_IP); >>> switch (key) { >>> case -1: >>> diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c >>> index 9e9ff471ddd1..bf2136d062ef 100644 >>> --- a/tools/perf/builtin-c2c.c >>> +++ b/tools/perf/builtin-c2c.c >>> @@ -45,6 +45,8 @@ >>> #include "pmus.h" >>> #include "string2.h" >>> #include "util/util.h" >>> +#include "util/symbol.h" >>> +#include "util/annotate.h" >>> struct c2c_hists { >>> struct hists hists; >>> @@ -62,6 +64,7 @@ struct compute_stats { >>> struct c2c_hist_entry { >>> struct c2c_hists *hists; >>> + struct evsel *evsel; >>> struct c2c_stats stats; >>> unsigned long *cpuset; >>> unsigned long *nodeset; >>> @@ -225,6 +228,12 @@ he__get_c2c_hists(struct hist_entry *he, >>> return hists; >>> } >>> +static void c2c_he__set_evsel(struct c2c_hist_entry *c2c_he, >>> + struct evsel *evsel) >>> +{ >>> + c2c_he->evsel = evsel; >>> +} >>> + >>> static void c2c_he__set_cpu(struct c2c_hist_entry *c2c_he, >>> struct perf_sample *sample) >>> { >>> @@ -334,6 +343,7 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused, >>> c2c_he__set_cpu(c2c_he, sample); >>> c2c_he__set_node(c2c_he, sample); >>> + c2c_he__set_evsel(c2c_he, evsel); >>> hists__inc_nr_samples(&c2c_hists->hists, he->filtered); >>> ret = hist_entry__append_callchain(he, sample); >>> @@ -371,6 +381,7 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused, >>> c2c_he__set_cpu(c2c_he, sample); >>> c2c_he__set_node(c2c_he, sample); >>> + c2c_he__set_evsel(c2c_he, evsel); >>> hists__inc_nr_samples(&c2c_hists->hists, he->filtered); >>> ret = hist_entry__append_callchain(he, sample); >>> @@ -2550,6 +2561,35 @@ static void perf_c2c__hists_fprintf(FILE *out, struct perf_session *session) >>> } >>> #ifdef HAVE_SLANG_SUPPORT >>> + >>> +static int perf_c2c__toggle_annotation(struct hist_browser *browser) >>> +{ >>> + struct hist_entry *he = browser->he_selection; >>> + struct symbol *sym = NULL; >>> + struct c2c_hist_entry *c2c_he = NULL; >>> + struct annotated_source *src = NULL; >>> + >>> + if (he == NULL) { >>> + ui_browser__help_window(&browser->b, "No entry selected for annotation"); >>> + return 0; >>> + } >>> + sym = (&he->ms)->sym; >>> + >>> + if (sym == NULL) { >>> + ui_browser__help_window(&browser->b, "Can not annotate, no symbol found"); >>> + return 0; >>> + } >>> + >>> + src = symbol__hists(sym, 0); >>> + if (src == NULL) { >>> + ui_browser__help_window(&browser->b, "Failed to initialize annotation source"); >>> + return 0; >>> + } >>> + >>> + c2c_he = container_of(he, struct c2c_hist_entry, he); >>> + return hist_entry__tui_annotate(he, c2c_he->evsel, NULL, he->ip); > I'm skeptical about using he->ip. An hist_entry can collapse multiple > samples with different IP in a symbol (even if hpp_list has symbol sort > key). That means he->ip cannot represent the entry is from the specific > point in the function. This might lead users to an inaccurate place in > the annotation browser. I'd recommend not passing IP. Understood. Thanks. What if we use cacheline browser's he->mem_info to get the al_addr? I will change the code accordingly for your review. >>> +} >>> + >>> static void c2c_browser__update_nr_entries(struct hist_browser *hb) >>> { >>> u64 nr_entries = 0; >>> @@ -2617,6 +2657,7 @@ static int perf_c2c__browse_cacheline(struct hist_entry *he) >>> " ENTER Toggle callchains (if present) \n" >>> " n Toggle Node details info \n" >>> " s Toggle full length of symbol and source line columns \n" >>> + " a Toggle annotation view \n" >>> " q Return back to cacheline list \n"; >>> if (!he) >>> @@ -2651,6 +2692,9 @@ static int perf_c2c__browse_cacheline(struct hist_entry *he) >>> c2c.node_info = (c2c.node_info + 1) % 3; >>> setup_nodes_header(); >>> break; >>> + case 'a': >>> + perf_c2c__toggle_annotation(browser); >>> + break; >>> case 'q': >>> goto out; >>> case '?': >>> @@ -2989,6 +3033,11 @@ static int setup_coalesce(const char *coalesce, bool no_source) >>> return 0; >>> } >>> +static bool perf_c2c__has_annotation(void) >>> +{ >>> + return use_browser == 1; > Please check if it has symbol dimension in the c2c_hists->list like in > the ui__has_annotation(). Maybe you need to add this in the > c2c_hists__init_sort(). > > if (dim == &dim_symbol) > hpp_list->sym = 1; > > Thanks, > Namhyung Agreed, will do. I am thinking to add the 'symbol' sort key into cl_resort so it will not impact the 'dcacheline' sort key in the c2c_hists__init and the cl_sort sort key which also controls the cl output. In this way it may need to have a hpp_list parameter to indicate the caller from c2c_browser or cacheline_browser. >>> +} >>> + >>> static int perf_c2c__report(int argc, const char **argv) >>> { >>> struct itrace_synth_opts itrace_synth_opts = { >>> @@ -3006,6 +3055,7 @@ static int perf_c2c__report(int argc, const char **argv) >>> const char *display = NULL; >>> const char *coalesce = NULL; >>> bool no_source = false; >>> + const char *disassembler_style = NULL, *objdump_path = NULL, *addr2line_path = NULL; >>> const struct option options[] = { >>> OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, >>> "file", "vmlinux pathname"), >>> @@ -3033,6 +3083,12 @@ static int perf_c2c__report(int argc, const char **argv) >>> OPT_BOOLEAN(0, "stitch-lbr", &c2c.stitch_lbr, >>> "Enable LBR callgraph stitching approach"), >>> OPT_BOOLEAN(0, "double-cl", &chk_double_cl, "Detect adjacent cacheline false sharing"), >>> + OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style", >>> + "Specify disassembler style (e.g. -M intel for intel syntax)"), >>> + OPT_STRING(0, "objdump", &objdump_path, "path", >>> + "objdump binary to use for disassembly and annotations"), >>> + OPT_STRING(0, "addr2line", &addr2line_path, "path", >>> + "addr2line binary to use for line numbers"), >>> OPT_PARENT(c2c_options), >>> OPT_END() >>> }; >>> @@ -3040,6 +3096,12 @@ static int perf_c2c__report(int argc, const char **argv) >>> const char *output_str, *sort_str = NULL; >>> struct perf_env *env; >>> + annotation_options__init(); >>> + >>> + err = hists__init(); >>> + if (err < 0) >>> + goto out; >>> + >>> argc = parse_options(argc, argv, options, report_c2c_usage, >>> PARSE_OPT_STOP_AT_NON_OPTION); >>> if (argc) >>> @@ -3052,6 +3114,36 @@ static int perf_c2c__report(int argc, const char **argv) >>> if (c2c.stats_only) >>> c2c.use_stdio = true; >>> + /** >>> + * Annotation related options >>> + * disassembler_style, objdump_path, addr2line_path >>> + * are set in the c2c_options, so we can use them here. >>> + */ >>> + if (disassembler_style) { >>> + annotate_opts.disassembler_style = strdup(disassembler_style); >>> + if (!annotate_opts.disassembler_style) { >>> + err = -ENOMEM; >>> + pr_err("Failed to allocate memory for annotation options\n"); >>> + goto out; >>> + } >>> + } >>> + if (objdump_path) { >>> + annotate_opts.objdump_path = strdup(objdump_path); >>> + if (!annotate_opts.objdump_path) { >>> + err = -ENOMEM; >>> + pr_err("Failed to allocate memory for annotation options\n"); >>> + goto out; >>> + } >>> + } >>> + if (addr2line_path) { >>> + symbol_conf.addr2line_path = strdup(addr2line_path); >>> + if (!symbol_conf.addr2line_path) { >>> + err = -ENOMEM; >>> + pr_err("Failed to allocate memory for annotation options\n"); >>> + goto out; >>> + } >>> + } >>> + >>> err = symbol__validate_sym_arguments(); >>> if (err) >>> goto out; >>> @@ -3126,6 +3218,38 @@ static int perf_c2c__report(int argc, const char **argv) >>> if (err) >>> goto out_mem2node; >>> + if (c2c.use_stdio) >>> + use_browser = 0; >>> + else >>> + use_browser = 1; >>> + >>> + /* >>> + * Only in the TUI browser we are doing integrated annotation, >>> + * so don't allocate extra space that won't be used in the stdio >>> + * implementation. >>> + */ >>> + if (perf_c2c__has_annotation()) { >>> + int ret = symbol__annotation_init(); >>> + >>> + if (ret < 0) >>> + goto out_mem2node; >>> + /* >>> + * For searching by name on the "Browse map details". >>> + * providing it only in verbose mode not to bloat too >>> + * much struct symbol. >>> + */ >>> + if (verbose > 0) { >>> + /* >>> + * XXX: Need to provide a less kludgy way to ask for >>> + * more space per symbol, the u32 is for the index on >>> + * the ui browser. >>> + * See symbol__browser_index. >>> + */ >>> + symbol_conf.priv_size += sizeof(u32); >>> + } >>> + annotation_config__init(); >>> + } >>> + >>> if (symbol__init(env) < 0) >>> goto out_mem2node; >>> @@ -3135,11 +3259,6 @@ static int perf_c2c__report(int argc, const char **argv) >>> goto out_mem2node; >>> } >>> - if (c2c.use_stdio) >>> - use_browser = 0; >>> - else >>> - use_browser = 1; >>> - >>> setup_browser(false); >>> err = perf_session__process_events(session); >>> @@ -3210,6 +3329,7 @@ static int perf_c2c__report(int argc, const char **argv) >>> out_session: >>> perf_session__delete(session); >>> out: >>> + annotation_options__exit(); >>> return err; >>> } >>> diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c >>> index 8fe699f98542..63d0e28fb991 100644 >>> --- a/tools/perf/ui/browsers/annotate.c >>> +++ b/tools/perf/ui/browsers/annotate.c >>> @@ -605,7 +605,7 @@ static bool annotate_browser__callq(struct annotate_browser *browser, >>> target_ms.map = ms->map; >>> target_ms.sym = dl->ops.target.sym; >>> annotation__unlock(notes); >>> - __hist_entry__tui_annotate(browser->he, &target_ms, evsel, hbt); >>> + __hist_entry__tui_annotate(browser->he, &target_ms, evsel, hbt, NO_INITIAL_IP); >>> /* >>> * The annotate_browser above changed the title with the target function >>> @@ -864,6 +864,7 @@ static int annotate_browser__run(struct annotate_browser *browser, >>> const char *help = "Press 'h' for help on key bindings"; >>> int delay_secs = hbt ? hbt->refresh : 0; >>> char *br_cntr_text = NULL; >>> + u64 init_ip = NO_INITIAL_IP; >>> char title[256]; >>> int key; >>> @@ -873,6 +874,13 @@ static int annotate_browser__run(struct annotate_browser *browser, >>> annotate_browser__calc_percent(browser, evsel); >>> + /* the selection are intentionally even not from the sample percentage */ >>> + if (browser->entries.rb_node == NULL && browser->selection) { >>> + init_ip = sym->start + browser->selection->offset; >>> + disasm_rb_tree__insert(browser, browser->selection); >>> + browser->curr_hot = rb_last(&browser->entries); >>> + } >>> + >>> if (browser->curr_hot) { >>> annotate_browser__set_rb_top(browser, browser->curr_hot); >>> browser->b.navkeypressed = false; >>> @@ -973,6 +981,18 @@ static int annotate_browser__run(struct annotate_browser *browser, >>> ui_helpline__puts(help); >>> annotate__scnprintf_title(hists, title, sizeof(title)); >>> annotate_browser__show(browser, title, help); >>> + /* Previous RB tree may not valid, need refresh according to new entries*/ >>> + if (init_ip != NO_INITIAL_IP) { >>> + struct disasm_line *dl = find_disasm_line(sym, init_ip, true); >>> + >>> + browser->curr_hot = NULL; >>> + browser->entries.rb_node = NULL; >>> + if (dl != NULL) { >>> + disasm_rb_tree__insert(browser, &dl->al); >>> + browser->curr_hot = rb_last(&browser->entries); >>> + } >>> + nd = browser->curr_hot; >>> + } >>> continue; >>> case 'o': >>> annotate_opts.use_offset = !annotate_opts.use_offset; >>> @@ -1106,22 +1126,23 @@ static int annotate_browser__run(struct annotate_browser *browser, >>> } >>> int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel, >>> - struct hist_browser_timer *hbt) >>> + struct hist_browser_timer *hbt, u64 init_ip) >>> { >>> /* reset abort key so that it can get Ctrl-C as a key */ >>> SLang_reset_tty(); >>> SLang_init_tty(0, 0, 0); >>> SLtty_set_suspend_state(true); >>> - return __hist_entry__tui_annotate(he, &he->ms, evsel, hbt); >>> + return __hist_entry__tui_annotate(he, &he->ms, evsel, hbt, init_ip); >>> } >>> int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms, >>> struct evsel *evsel, >>> - struct hist_browser_timer *hbt) >>> + struct hist_browser_timer *hbt, u64 init_ip) >>> { >>> struct symbol *sym = ms->sym; >>> struct annotation *notes = symbol__annotation(sym); >>> + struct disasm_line *dl = NULL; >>> struct annotate_browser browser = { >>> .b = { >>> .refresh = annotate_browser__refresh, >>> @@ -1173,6 +1194,18 @@ int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms, >>> browser.he = &annotate_he; >>> } >>> + /* >>> + * If init_ip is set, it means that there should be a line >>> + * intentionally selected, not based on the percentages >>> + * which caculated by the event sampling. In this case, we >>> + * convey this information into the browser selection, where >>> + * the selection in other cases should be empty. >>> + */ >>> + if (init_ip != NO_INITIAL_IP) { >>> + dl = find_disasm_line(sym, init_ip, false); >>> + browser.selection = &dl->al; >>> + } >>> + >>> ui_helpline__push("Press ESC to exit"); >>> if (annotate_opts.code_with_type) { >>> diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c >>> index 487c0b08c003..3675a703de11 100644 >>> --- a/tools/perf/ui/browsers/hists.c >>> +++ b/tools/perf/ui/browsers/hists.c >>> @@ -2485,7 +2485,7 @@ do_annotate(struct hist_browser *browser, struct popup_action *act) >>> evsel = hists_to_evsel(browser->hists); >>> he = hist_browser__selected_entry(browser); >>> - err = __hist_entry__tui_annotate(he, &act->ms, evsel, browser->hbt); >>> + err = __hist_entry__tui_annotate(he, &act->ms, evsel, browser->hbt, NO_INITIAL_IP); >>> /* >>> * offer option to annotate the other branch source or target >>> * (if they exists) when returning from annotate >>> diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c >>> index c9b220d9f924..937effbeda69 100644 >>> --- a/tools/perf/util/annotate.c >>> +++ b/tools/perf/util/annotate.c >>> @@ -2622,7 +2622,7 @@ int annotate_get_insn_location(struct arch *arch, struct disasm_line *dl, >>> return 0; >>> } >>> -static struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip, >>> +struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip, >>> bool allow_update) >>> { >>> struct disasm_line *dl; >>> diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h >>> index eaf6c8aa7f47..bbe67588bbdd 100644 >>> --- a/tools/perf/util/annotate.h >>> +++ b/tools/perf/util/annotate.h >>> @@ -170,6 +170,8 @@ static inline struct disasm_line *disasm_line(struct annotation_line *al) >>> return al ? container_of(al, struct disasm_line, al) : NULL; >>> } >>> +struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip, bool allow_update); >>> + >>> /* >>> * Is this offset in the same function as the line it is used? >>> * asm functions jump to other functions, for instance. >>> diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h >>> index c64005278687..e544e1795f19 100644 >>> --- a/tools/perf/util/hist.h >>> +++ b/tools/perf/util/hist.h >>> @@ -713,12 +713,14 @@ struct block_hist { >>> #include "../ui/keysyms.h" >>> void attr_to_script(char *buf, struct perf_event_attr *attr); >>> +#define NO_INITIAL_IP 0 >>> + >>> int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms, >>> struct evsel *evsel, >>> - struct hist_browser_timer *hbt); >>> + struct hist_browser_timer *hbt, u64 init_ip); >>> int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel, >>> - struct hist_browser_timer *hbt); >>> + struct hist_browser_timer *hbt, u64 init_ip); >>> int evlist__tui_browse_hists(struct evlist *evlist, const char *help, struct hist_browser_timer *hbt, >>> float min_pcnt, struct perf_env *env, bool warn_lost_event);
Perf c2c report currently specified the code address and source:line
information in the cacheline browser, while it is lack of annotation
support like perf report to directly show the disassembly code for
the particular symbol shared that same cacheline. This patches add
a key 'a' binding to the cacheline browser which reuse the annotation
browser to show the disassembly view for easier analysis of cacheline
contentions. By default, the 'TAB' key navigate to the code address
where the contentions detected.
Signed-off-by: Tianyou Li <tianyou.li@intel.com>
Reviewed-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
Reviewed-by: Thomas Falcon <thomas.falcon@intel.com>
Reviewed-by: Jiebin Sun <jiebin.sun@intel.com>
Reviewed-by: Pan Deng <pan.deng@intel.com>
Reviewed-by: Zhiguo Zhou <zhiguo.zhou@intel.com>
Reviewed-by: Wangyang Guo <wangyang.guo@intel.com>
---
tools/perf/builtin-annotate.c | 2 +-
tools/perf/builtin-c2c.c | 160 ++++++++++++++++++++++++++++--
tools/perf/ui/browsers/annotate.c | 41 +++++++-
tools/perf/ui/browsers/hists.c | 2 +-
tools/perf/util/annotate.c | 2 +-
tools/perf/util/annotate.h | 2 +
tools/perf/util/hist.h | 6 +-
7 files changed, 200 insertions(+), 15 deletions(-)
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 646f43b0f7c4..d89796648bec 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -519,7 +519,7 @@ static void hists__find_annotations(struct hists *hists,
/* skip missing symbols */
nd = rb_next(nd);
} else if (use_browser == 1) {
- key = hist_entry__tui_annotate(he, evsel, NULL);
+ key = hist_entry__tui_annotate(he, evsel, NULL, NO_INITIAL_AL_ADDR);
switch (key) {
case -1:
diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c
index 9e9ff471ddd1..7989fc46516e 100644
--- a/tools/perf/builtin-c2c.c
+++ b/tools/perf/builtin-c2c.c
@@ -45,6 +45,8 @@
#include "pmus.h"
#include "string2.h"
#include "util/util.h"
+#include "util/symbol.h"
+#include "util/annotate.h"
struct c2c_hists {
struct hists hists;
@@ -62,6 +64,7 @@ struct compute_stats {
struct c2c_hist_entry {
struct c2c_hists *hists;
+ struct evsel *evsel;
struct c2c_stats stats;
unsigned long *cpuset;
unsigned long *nodeset;
@@ -225,6 +228,12 @@ he__get_c2c_hists(struct hist_entry *he,
return hists;
}
+static void c2c_he__set_evsel(struct c2c_hist_entry *c2c_he,
+ struct evsel *evsel)
+{
+ c2c_he->evsel = evsel;
+}
+
static void c2c_he__set_cpu(struct c2c_hist_entry *c2c_he,
struct perf_sample *sample)
{
@@ -334,6 +343,7 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused,
c2c_he__set_cpu(c2c_he, sample);
c2c_he__set_node(c2c_he, sample);
+ c2c_he__set_evsel(c2c_he, evsel);
hists__inc_nr_samples(&c2c_hists->hists, he->filtered);
ret = hist_entry__append_callchain(he, sample);
@@ -371,6 +381,7 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused,
c2c_he__set_cpu(c2c_he, sample);
c2c_he__set_node(c2c_he, sample);
+ c2c_he__set_evsel(c2c_he, evsel);
hists__inc_nr_samples(&c2c_hists->hists, he->filtered);
ret = hist_entry__append_callchain(he, sample);
@@ -1997,6 +2008,9 @@ static int c2c_hists__init_sort(struct perf_hpp_list *hpp_list, char *name, stru
if (dim == &dim_dso)
hpp_list->dso = 1;
+ if (dim == &dim_symbol)
+ hpp_list->sym = 1;
+
perf_hpp_list__register_sort_field(hpp_list, &c2c_fmt->fmt);
return 0;
}
@@ -2549,7 +2563,65 @@ static void perf_c2c__hists_fprintf(FILE *out, struct perf_session *session)
print_pareto(out, perf_session__env(session));
}
+/*
+ * Return true if annotation is possible. When list is NULL,
+ * it means that we are called at the c2c_browser level,
+ * in that case we allow annotation to be initialized.When list
+ * is non-NULL, it means that we are called at the cacheline_browser
+ * level, in that case we allow annotation only if use_browser
+ * is set and symbol information is available.
+ */
+static bool perf_c2c__has_annotation(struct perf_hpp_list *list)
+{
+ bool has_annotation = false;
+
+ if (list == NULL)
+ has_annotation = use_browser == 1;
+ else
+ has_annotation = use_browser == 1 && list->sym;
+
+ return has_annotation;
+}
+
#ifdef HAVE_SLANG_SUPPORT
+
+static int perf_c2c__toggle_annotation(struct hist_browser *browser)
+{
+ struct hist_entry *he = browser->he_selection;
+ struct symbol *sym = NULL;
+ struct c2c_hist_entry *c2c_he = NULL;
+ struct annotated_source *src = NULL;
+ u64 addr = 0;
+
+ if (!perf_c2c__has_annotation(he->hists->hpp_list)) {
+ ui_browser__help_window(&browser->b, "No annotation support");
+ return 0;
+ }
+
+ if (he == NULL) {
+ ui_browser__help_window(&browser->b, "No entry selected for annotation");
+ return 0;
+ }
+ sym = (&he->ms)->sym;
+
+ if (sym == NULL) {
+ ui_browser__help_window(&browser->b, "Can not annotate, no symbol found");
+ return 0;
+ }
+
+ src = symbol__hists(sym, 0);
+ if (src == NULL) {
+ ui_browser__help_window(&browser->b, "Failed to initialize annotation source");
+ return 0;
+ }
+
+ if (he->mem_info)
+ addr = mem_info__iaddr(he->mem_info)->al_addr;
+
+ c2c_he = container_of(he, struct c2c_hist_entry, he);
+ return hist_entry__tui_annotate(he, c2c_he->evsel, NULL, addr);
+}
+
static void c2c_browser__update_nr_entries(struct hist_browser *hb)
{
u64 nr_entries = 0;
@@ -2617,6 +2689,7 @@ static int perf_c2c__browse_cacheline(struct hist_entry *he)
" ENTER Toggle callchains (if present) \n"
" n Toggle Node details info \n"
" s Toggle full length of symbol and source line columns \n"
+ " a Toggle annotation view \n"
" q Return back to cacheline list \n";
if (!he)
@@ -2651,6 +2724,9 @@ static int perf_c2c__browse_cacheline(struct hist_entry *he)
c2c.node_info = (c2c.node_info + 1) % 3;
setup_nodes_header();
break;
+ case 'a':
+ perf_c2c__toggle_annotation(browser);
+ break;
case 'q':
goto out;
case '?':
@@ -2980,7 +3056,8 @@ static int setup_coalesce(const char *coalesce, bool no_source)
else if (c2c.display == DISPLAY_SNP_PEER)
sort_str = "tot_peer";
- if (asprintf(&c2c.cl_resort, "offset,%s", sort_str) < 0)
+ /* add 'symbol' sort key to make sure hpp_list->sym get updated */
+ if (asprintf(&c2c.cl_resort, "offset,%s,symbol", sort_str) < 0)
return -ENOMEM;
pr_debug("coalesce sort fields: %s\n", c2c.cl_sort);
@@ -3006,6 +3083,7 @@ static int perf_c2c__report(int argc, const char **argv)
const char *display = NULL;
const char *coalesce = NULL;
bool no_source = false;
+ const char *disassembler_style = NULL, *objdump_path = NULL, *addr2line_path = NULL;
const struct option options[] = {
OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
"file", "vmlinux pathname"),
@@ -3033,6 +3111,12 @@ static int perf_c2c__report(int argc, const char **argv)
OPT_BOOLEAN(0, "stitch-lbr", &c2c.stitch_lbr,
"Enable LBR callgraph stitching approach"),
OPT_BOOLEAN(0, "double-cl", &chk_double_cl, "Detect adjacent cacheline false sharing"),
+ OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style",
+ "Specify disassembler style (e.g. -M intel for intel syntax)"),
+ OPT_STRING(0, "objdump", &objdump_path, "path",
+ "objdump binary to use for disassembly and annotations"),
+ OPT_STRING(0, "addr2line", &addr2line_path, "path",
+ "addr2line binary to use for line numbers"),
OPT_PARENT(c2c_options),
OPT_END()
};
@@ -3040,6 +3124,12 @@ static int perf_c2c__report(int argc, const char **argv)
const char *output_str, *sort_str = NULL;
struct perf_env *env;
+ annotation_options__init();
+
+ err = hists__init();
+ if (err < 0)
+ goto out;
+
argc = parse_options(argc, argv, options, report_c2c_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
if (argc)
@@ -3052,6 +3142,36 @@ static int perf_c2c__report(int argc, const char **argv)
if (c2c.stats_only)
c2c.use_stdio = true;
+ /**
+ * Annotation related options
+ * disassembler_style, objdump_path, addr2line_path
+ * are set in the c2c_options, so we can use them here.
+ */
+ if (disassembler_style) {
+ annotate_opts.disassembler_style = strdup(disassembler_style);
+ if (!annotate_opts.disassembler_style) {
+ err = -ENOMEM;
+ pr_err("Failed to allocate memory for annotation options\n");
+ goto out;
+ }
+ }
+ if (objdump_path) {
+ annotate_opts.objdump_path = strdup(objdump_path);
+ if (!annotate_opts.objdump_path) {
+ err = -ENOMEM;
+ pr_err("Failed to allocate memory for annotation options\n");
+ goto out;
+ }
+ }
+ if (addr2line_path) {
+ symbol_conf.addr2line_path = strdup(addr2line_path);
+ if (!symbol_conf.addr2line_path) {
+ err = -ENOMEM;
+ pr_err("Failed to allocate memory for annotation options\n");
+ goto out;
+ }
+ }
+
err = symbol__validate_sym_arguments();
if (err)
goto out;
@@ -3126,6 +3246,38 @@ static int perf_c2c__report(int argc, const char **argv)
if (err)
goto out_mem2node;
+ if (c2c.use_stdio)
+ use_browser = 0;
+ else
+ use_browser = 1;
+
+ /*
+ * Only in the TUI browser we are doing integrated annotation,
+ * so don't allocate extra space that won't be used in the stdio
+ * implementation.
+ */
+ if (perf_c2c__has_annotation(NULL)) {
+ int ret = symbol__annotation_init();
+
+ if (ret < 0)
+ goto out_mem2node;
+ /*
+ * For searching by name on the "Browse map details".
+ * providing it only in verbose mode not to bloat too
+ * much struct symbol.
+ */
+ if (verbose > 0) {
+ /*
+ * XXX: Need to provide a less kludgy way to ask for
+ * more space per symbol, the u32 is for the index on
+ * the ui browser.
+ * See symbol__browser_index.
+ */
+ symbol_conf.priv_size += sizeof(u32);
+ }
+ annotation_config__init();
+ }
+
if (symbol__init(env) < 0)
goto out_mem2node;
@@ -3135,11 +3287,6 @@ static int perf_c2c__report(int argc, const char **argv)
goto out_mem2node;
}
- if (c2c.use_stdio)
- use_browser = 0;
- else
- use_browser = 1;
-
setup_browser(false);
err = perf_session__process_events(session);
@@ -3210,6 +3357,7 @@ static int perf_c2c__report(int argc, const char **argv)
out_session:
perf_session__delete(session);
out:
+ annotation_options__exit();
return err;
}
diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c
index 8fe699f98542..a9d56e67454d 100644
--- a/tools/perf/ui/browsers/annotate.c
+++ b/tools/perf/ui/browsers/annotate.c
@@ -605,7 +605,7 @@ static bool annotate_browser__callq(struct annotate_browser *browser,
target_ms.map = ms->map;
target_ms.sym = dl->ops.target.sym;
annotation__unlock(notes);
- __hist_entry__tui_annotate(browser->he, &target_ms, evsel, hbt);
+ __hist_entry__tui_annotate(browser->he, &target_ms, evsel, hbt, NO_INITIAL_AL_ADDR);
/*
* The annotate_browser above changed the title with the target function
@@ -864,6 +864,7 @@ static int annotate_browser__run(struct annotate_browser *browser,
const char *help = "Press 'h' for help on key bindings";
int delay_secs = hbt ? hbt->refresh : 0;
char *br_cntr_text = NULL;
+ u64 init_al_addr = NO_INITIAL_AL_ADDR;
char title[256];
int key;
@@ -873,6 +874,13 @@ static int annotate_browser__run(struct annotate_browser *browser,
annotate_browser__calc_percent(browser, evsel);
+ /* the selection are intentionally even not from the sample percentage */
+ if (browser->entries.rb_node == NULL && browser->selection) {
+ init_al_addr = sym->start + browser->selection->offset;
+ disasm_rb_tree__insert(browser, browser->selection);
+ browser->curr_hot = rb_last(&browser->entries);
+ }
+
if (browser->curr_hot) {
annotate_browser__set_rb_top(browser, browser->curr_hot);
browser->b.navkeypressed = false;
@@ -973,6 +981,18 @@ static int annotate_browser__run(struct annotate_browser *browser,
ui_helpline__puts(help);
annotate__scnprintf_title(hists, title, sizeof(title));
annotate_browser__show(browser, title, help);
+ /* Previous RB tree may not valid, need refresh according to new entries*/
+ if (init_al_addr != NO_INITIAL_AL_ADDR) {
+ struct disasm_line *dl = find_disasm_line(sym, init_al_addr, true);
+
+ browser->curr_hot = NULL;
+ browser->entries.rb_node = NULL;
+ if (dl != NULL) {
+ disasm_rb_tree__insert(browser, &dl->al);
+ browser->curr_hot = rb_last(&browser->entries);
+ }
+ nd = browser->curr_hot;
+ }
continue;
case 'o':
annotate_opts.use_offset = !annotate_opts.use_offset;
@@ -1106,22 +1126,23 @@ static int annotate_browser__run(struct annotate_browser *browser,
}
int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel,
- struct hist_browser_timer *hbt)
+ struct hist_browser_timer *hbt, u64 init_al_addr)
{
/* reset abort key so that it can get Ctrl-C as a key */
SLang_reset_tty();
SLang_init_tty(0, 0, 0);
SLtty_set_suspend_state(true);
- return __hist_entry__tui_annotate(he, &he->ms, evsel, hbt);
+ return __hist_entry__tui_annotate(he, &he->ms, evsel, hbt, init_al_addr);
}
int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms,
struct evsel *evsel,
- struct hist_browser_timer *hbt)
+ struct hist_browser_timer *hbt, u64 init_al_addr)
{
struct symbol *sym = ms->sym;
struct annotation *notes = symbol__annotation(sym);
+ struct disasm_line *dl = NULL;
struct annotate_browser browser = {
.b = {
.refresh = annotate_browser__refresh,
@@ -1173,6 +1194,18 @@ int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms,
browser.he = &annotate_he;
}
+ /*
+ * If init_al_addr is set, it means that there should be a line
+ * intentionally selected, not based on the percentages
+ * which caculated by the event sampling. In this case, we
+ * convey this information into the browser selection, where
+ * the selection in other cases should be empty.
+ */
+ if (init_al_addr != NO_INITIAL_AL_ADDR) {
+ dl = find_disasm_line(sym, init_al_addr, false);
+ browser.selection = &dl->al;
+ }
+
ui_helpline__push("Press ESC to exit");
if (annotate_opts.code_with_type) {
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c
index 487c0b08c003..c34ddc4ca13f 100644
--- a/tools/perf/ui/browsers/hists.c
+++ b/tools/perf/ui/browsers/hists.c
@@ -2485,7 +2485,7 @@ do_annotate(struct hist_browser *browser, struct popup_action *act)
evsel = hists_to_evsel(browser->hists);
he = hist_browser__selected_entry(browser);
- err = __hist_entry__tui_annotate(he, &act->ms, evsel, browser->hbt);
+ err = __hist_entry__tui_annotate(he, &act->ms, evsel, browser->hbt, NO_INITIAL_AL_ADDR);
/*
* offer option to annotate the other branch source or target
* (if they exists) when returning from annotate
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index c9b220d9f924..937effbeda69 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -2622,7 +2622,7 @@ int annotate_get_insn_location(struct arch *arch, struct disasm_line *dl,
return 0;
}
-static struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip,
+struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip,
bool allow_update)
{
struct disasm_line *dl;
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h
index eaf6c8aa7f47..bbe67588bbdd 100644
--- a/tools/perf/util/annotate.h
+++ b/tools/perf/util/annotate.h
@@ -170,6 +170,8 @@ static inline struct disasm_line *disasm_line(struct annotation_line *al)
return al ? container_of(al, struct disasm_line, al) : NULL;
}
+struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip, bool allow_update);
+
/*
* Is this offset in the same function as the line it is used?
* asm functions jump to other functions, for instance.
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
index c64005278687..7afa50aa5cbb 100644
--- a/tools/perf/util/hist.h
+++ b/tools/perf/util/hist.h
@@ -713,12 +713,14 @@ struct block_hist {
#include "../ui/keysyms.h"
void attr_to_script(char *buf, struct perf_event_attr *attr);
+#define NO_INITIAL_AL_ADDR 0
+
int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms,
struct evsel *evsel,
- struct hist_browser_timer *hbt);
+ struct hist_browser_timer *hbt, u64 init_al_addr);
int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel,
- struct hist_browser_timer *hbt);
+ struct hist_browser_timer *hbt, u64 init_al_addr);
int evlist__tui_browse_hists(struct evlist *evlist, const char *help, struct hist_browser_timer *hbt,
float min_pcnt, struct perf_env *env, bool warn_lost_event);
--
2.47.1
Hello, On Tue, Sep 30, 2025 at 08:39:00PM +0800, Tianyou Li wrote: > Perf c2c report currently specified the code address and source:line > information in the cacheline browser, while it is lack of annotation > support like perf report to directly show the disassembly code for > the particular symbol shared that same cacheline. This patches add > a key 'a' binding to the cacheline browser which reuse the annotation > browser to show the disassembly view for easier analysis of cacheline > contentions. By default, the 'TAB' key navigate to the code address > where the contentions detected. > > Signed-off-by: Tianyou Li <tianyou.li@intel.com> > Reviewed-by: Dapeng Mi <dapeng1.mi@linux.intel.com> > Reviewed-by: Thomas Falcon <thomas.falcon@intel.com> > Reviewed-by: Jiebin Sun <jiebin.sun@intel.com> > Reviewed-by: Pan Deng <pan.deng@intel.com> > Reviewed-by: Zhiguo Zhou <zhiguo.zhou@intel.com> > Reviewed-by: Wangyang Guo <wangyang.guo@intel.com> > --- > tools/perf/builtin-annotate.c | 2 +- > tools/perf/builtin-c2c.c | 160 ++++++++++++++++++++++++++++-- > tools/perf/ui/browsers/annotate.c | 41 +++++++- > tools/perf/ui/browsers/hists.c | 2 +- > tools/perf/util/annotate.c | 2 +- > tools/perf/util/annotate.h | 2 + > tools/perf/util/hist.h | 6 +- > 7 files changed, 200 insertions(+), 15 deletions(-) > > diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c > index 646f43b0f7c4..d89796648bec 100644 > --- a/tools/perf/builtin-annotate.c > +++ b/tools/perf/builtin-annotate.c > @@ -519,7 +519,7 @@ static void hists__find_annotations(struct hists *hists, > /* skip missing symbols */ > nd = rb_next(nd); > } else if (use_browser == 1) { > - key = hist_entry__tui_annotate(he, evsel, NULL); > + key = hist_entry__tui_annotate(he, evsel, NULL, NO_INITIAL_AL_ADDR); > > switch (key) { > case -1: > diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c > index 9e9ff471ddd1..7989fc46516e 100644 > --- a/tools/perf/builtin-c2c.c > +++ b/tools/perf/builtin-c2c.c > @@ -45,6 +45,8 @@ > #include "pmus.h" > #include "string2.h" > #include "util/util.h" > +#include "util/symbol.h" > +#include "util/annotate.h" > > struct c2c_hists { > struct hists hists; > @@ -62,6 +64,7 @@ struct compute_stats { > > struct c2c_hist_entry { > struct c2c_hists *hists; > + struct evsel *evsel; > struct c2c_stats stats; > unsigned long *cpuset; > unsigned long *nodeset; > @@ -225,6 +228,12 @@ he__get_c2c_hists(struct hist_entry *he, > return hists; > } > > +static void c2c_he__set_evsel(struct c2c_hist_entry *c2c_he, > + struct evsel *evsel) > +{ > + c2c_he->evsel = evsel; > +} > + > static void c2c_he__set_cpu(struct c2c_hist_entry *c2c_he, > struct perf_sample *sample) > { > @@ -334,6 +343,7 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused, > > c2c_he__set_cpu(c2c_he, sample); > c2c_he__set_node(c2c_he, sample); > + c2c_he__set_evsel(c2c_he, evsel); > > hists__inc_nr_samples(&c2c_hists->hists, he->filtered); > ret = hist_entry__append_callchain(he, sample); > @@ -371,6 +381,7 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused, > > c2c_he__set_cpu(c2c_he, sample); > c2c_he__set_node(c2c_he, sample); > + c2c_he__set_evsel(c2c_he, evsel); > > hists__inc_nr_samples(&c2c_hists->hists, he->filtered); > ret = hist_entry__append_callchain(he, sample); > @@ -1997,6 +2008,9 @@ static int c2c_hists__init_sort(struct perf_hpp_list *hpp_list, char *name, stru > if (dim == &dim_dso) > hpp_list->dso = 1; > > + if (dim == &dim_symbol) > + hpp_list->sym = 1; > + > perf_hpp_list__register_sort_field(hpp_list, &c2c_fmt->fmt); > return 0; > } > @@ -2549,7 +2563,65 @@ static void perf_c2c__hists_fprintf(FILE *out, struct perf_session *session) > print_pareto(out, perf_session__env(session)); > } > > +/* > + * Return true if annotation is possible. When list is NULL, > + * it means that we are called at the c2c_browser level, > + * in that case we allow annotation to be initialized.When list > + * is non-NULL, it means that we are called at the cacheline_browser > + * level, in that case we allow annotation only if use_browser > + * is set and symbol information is available. > + */ > +static bool perf_c2c__has_annotation(struct perf_hpp_list *list) > +{ > + bool has_annotation = false; > + > + if (list == NULL) > + has_annotation = use_browser == 1; > + else > + has_annotation = use_browser == 1 && list->sym; > + > + return has_annotation; Nit: it would be simpler this way. if (use_browser != 1) return false; return !list || list->sym; > +} > + > #ifdef HAVE_SLANG_SUPPORT > + > +static int perf_c2c__toggle_annotation(struct hist_browser *browser) > +{ > + struct hist_entry *he = browser->he_selection; > + struct symbol *sym = NULL; > + struct c2c_hist_entry *c2c_he = NULL; > + struct annotated_source *src = NULL; > + u64 addr = 0; u64 addr = NO_INITIAL_AL_ADDR; > + > + if (!perf_c2c__has_annotation(he->hists->hpp_list)) { > + ui_browser__help_window(&browser->b, "No annotation support"); > + return 0; > + } > + > + if (he == NULL) { > + ui_browser__help_window(&browser->b, "No entry selected for annotation"); > + return 0; > + } > + sym = (&he->ms)->sym; sym = he->ms.sym; > + > + if (sym == NULL) { > + ui_browser__help_window(&browser->b, "Can not annotate, no symbol found"); > + return 0; > + } > + > + src = symbol__hists(sym, 0); > + if (src == NULL) { > + ui_browser__help_window(&browser->b, "Failed to initialize annotation source"); > + return 0; > + } > + > + if (he->mem_info) > + addr = mem_info__iaddr(he->mem_info)->al_addr; > + > + c2c_he = container_of(he, struct c2c_hist_entry, he); > + return hist_entry__tui_annotate(he, c2c_he->evsel, NULL, addr); > +} > + > static void c2c_browser__update_nr_entries(struct hist_browser *hb) > { > u64 nr_entries = 0; > @@ -2617,6 +2689,7 @@ static int perf_c2c__browse_cacheline(struct hist_entry *he) > " ENTER Toggle callchains (if present) \n" > " n Toggle Node details info \n" > " s Toggle full length of symbol and source line columns \n" > + " a Toggle annotation view \n" > " q Return back to cacheline list \n"; > > if (!he) > @@ -2651,6 +2724,9 @@ static int perf_c2c__browse_cacheline(struct hist_entry *he) > c2c.node_info = (c2c.node_info + 1) % 3; > setup_nodes_header(); > break; > + case 'a': > + perf_c2c__toggle_annotation(browser); > + break; > case 'q': > goto out; > case '?': > @@ -2980,7 +3056,8 @@ static int setup_coalesce(const char *coalesce, bool no_source) > else if (c2c.display == DISPLAY_SNP_PEER) > sort_str = "tot_peer"; > > - if (asprintf(&c2c.cl_resort, "offset,%s", sort_str) < 0) > + /* add 'symbol' sort key to make sure hpp_list->sym get updated */ > + if (asprintf(&c2c.cl_resort, "offset,%s,symbol", sort_str) < 0) I think it's better to just process the input rather than enforcing it. It seems the default value will have 'iaddr' and so 'symbol as well. > return -ENOMEM; > > pr_debug("coalesce sort fields: %s\n", c2c.cl_sort); > @@ -3006,6 +3083,7 @@ static int perf_c2c__report(int argc, const char **argv) > const char *display = NULL; > const char *coalesce = NULL; > bool no_source = false; > + const char *disassembler_style = NULL, *objdump_path = NULL, *addr2line_path = NULL; > const struct option options[] = { > OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, > "file", "vmlinux pathname"), > @@ -3033,6 +3111,12 @@ static int perf_c2c__report(int argc, const char **argv) > OPT_BOOLEAN(0, "stitch-lbr", &c2c.stitch_lbr, > "Enable LBR callgraph stitching approach"), > OPT_BOOLEAN(0, "double-cl", &chk_double_cl, "Detect adjacent cacheline false sharing"), > + OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style", > + "Specify disassembler style (e.g. -M intel for intel syntax)"), > + OPT_STRING(0, "objdump", &objdump_path, "path", > + "objdump binary to use for disassembly and annotations"), Please update documentation with the new options. > + OPT_STRING(0, "addr2line", &addr2line_path, "path", > + "addr2line binary to use for line numbers"), Do you really need this? > OPT_PARENT(c2c_options), > OPT_END() > }; > @@ -3040,6 +3124,12 @@ static int perf_c2c__report(int argc, const char **argv) > const char *output_str, *sort_str = NULL; > struct perf_env *env; > > + annotation_options__init(); > + > + err = hists__init(); > + if (err < 0) > + goto out; > + > argc = parse_options(argc, argv, options, report_c2c_usage, > PARSE_OPT_STOP_AT_NON_OPTION); > if (argc) > @@ -3052,6 +3142,36 @@ static int perf_c2c__report(int argc, const char **argv) > if (c2c.stats_only) > c2c.use_stdio = true; > > + /** > + * Annotation related options > + * disassembler_style, objdump_path, addr2line_path > + * are set in the c2c_options, so we can use them here. > + */ > + if (disassembler_style) { > + annotate_opts.disassembler_style = strdup(disassembler_style); > + if (!annotate_opts.disassembler_style) { > + err = -ENOMEM; > + pr_err("Failed to allocate memory for annotation options\n"); > + goto out; > + } > + } > + if (objdump_path) { > + annotate_opts.objdump_path = strdup(objdump_path); > + if (!annotate_opts.objdump_path) { > + err = -ENOMEM; > + pr_err("Failed to allocate memory for annotation options\n"); > + goto out; > + } > + } > + if (addr2line_path) { > + symbol_conf.addr2line_path = strdup(addr2line_path); > + if (!symbol_conf.addr2line_path) { > + err = -ENOMEM; > + pr_err("Failed to allocate memory for annotation options\n"); > + goto out; > + } > + } > + > err = symbol__validate_sym_arguments(); > if (err) > goto out; > @@ -3126,6 +3246,38 @@ static int perf_c2c__report(int argc, const char **argv) > if (err) > goto out_mem2node; > > + if (c2c.use_stdio) > + use_browser = 0; > + else > + use_browser = 1; > + > + /* > + * Only in the TUI browser we are doing integrated annotation, > + * so don't allocate extra space that won't be used in the stdio > + * implementation. > + */ > + if (perf_c2c__has_annotation(NULL)) { > + int ret = symbol__annotation_init(); > + > + if (ret < 0) > + goto out_mem2node; > + /* > + * For searching by name on the "Browse map details". > + * providing it only in verbose mode not to bloat too > + * much struct symbol. > + */ > + if (verbose > 0) { > + /* > + * XXX: Need to provide a less kludgy way to ask for > + * more space per symbol, the u32 is for the index on > + * the ui browser. > + * See symbol__browser_index. > + */ > + symbol_conf.priv_size += sizeof(u32); > + } > + annotation_config__init(); > + } > + > if (symbol__init(env) < 0) > goto out_mem2node; > > @@ -3135,11 +3287,6 @@ static int perf_c2c__report(int argc, const char **argv) > goto out_mem2node; > } > > - if (c2c.use_stdio) > - use_browser = 0; > - else > - use_browser = 1; > - > setup_browser(false); > > err = perf_session__process_events(session); > @@ -3210,6 +3357,7 @@ static int perf_c2c__report(int argc, const char **argv) > out_session: > perf_session__delete(session); > out: > + annotation_options__exit(); > return err; > } > > diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c > index 8fe699f98542..a9d56e67454d 100644 > --- a/tools/perf/ui/browsers/annotate.c > +++ b/tools/perf/ui/browsers/annotate.c > @@ -605,7 +605,7 @@ static bool annotate_browser__callq(struct annotate_browser *browser, > target_ms.map = ms->map; > target_ms.sym = dl->ops.target.sym; > annotation__unlock(notes); > - __hist_entry__tui_annotate(browser->he, &target_ms, evsel, hbt); > + __hist_entry__tui_annotate(browser->he, &target_ms, evsel, hbt, NO_INITIAL_AL_ADDR); > > /* > * The annotate_browser above changed the title with the target function > @@ -864,6 +864,7 @@ static int annotate_browser__run(struct annotate_browser *browser, > const char *help = "Press 'h' for help on key bindings"; > int delay_secs = hbt ? hbt->refresh : 0; > char *br_cntr_text = NULL; > + u64 init_al_addr = NO_INITIAL_AL_ADDR; > char title[256]; > int key; > > @@ -873,6 +874,13 @@ static int annotate_browser__run(struct annotate_browser *browser, > > annotate_browser__calc_percent(browser, evsel); > > + /* the selection are intentionally even not from the sample percentage */ > + if (browser->entries.rb_node == NULL && browser->selection) { > + init_al_addr = sym->start + browser->selection->offset; > + disasm_rb_tree__insert(browser, browser->selection); > + browser->curr_hot = rb_last(&browser->entries); > + } > + > if (browser->curr_hot) { > annotate_browser__set_rb_top(browser, browser->curr_hot); > browser->b.navkeypressed = false; > @@ -973,6 +981,18 @@ static int annotate_browser__run(struct annotate_browser *browser, > ui_helpline__puts(help); > annotate__scnprintf_title(hists, title, sizeof(title)); > annotate_browser__show(browser, title, help); > + /* Previous RB tree may not valid, need refresh according to new entries*/ > + if (init_al_addr != NO_INITIAL_AL_ADDR) { > + struct disasm_line *dl = find_disasm_line(sym, init_al_addr, true); > + > + browser->curr_hot = NULL; > + browser->entries.rb_node = NULL; > + if (dl != NULL) { > + disasm_rb_tree__insert(browser, &dl->al); > + browser->curr_hot = rb_last(&browser->entries); > + } > + nd = browser->curr_hot; > + } Can you please split annotate changes from c2c change? I think you can start with annotation support in c2c. And add INITIAL_ADDR later so that we can discuss the issue separately. Maybe we don't need the ADDR change. Do you have any concrete usecase where default annotate is not enough for c2c? Thanks, Namhyung > continue; > case 'o': > annotate_opts.use_offset = !annotate_opts.use_offset; > @@ -1106,22 +1126,23 @@ static int annotate_browser__run(struct annotate_browser *browser, > } > > int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel, > - struct hist_browser_timer *hbt) > + struct hist_browser_timer *hbt, u64 init_al_addr) > { > /* reset abort key so that it can get Ctrl-C as a key */ > SLang_reset_tty(); > SLang_init_tty(0, 0, 0); > SLtty_set_suspend_state(true); > > - return __hist_entry__tui_annotate(he, &he->ms, evsel, hbt); > + return __hist_entry__tui_annotate(he, &he->ms, evsel, hbt, init_al_addr); > } > > int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms, > struct evsel *evsel, > - struct hist_browser_timer *hbt) > + struct hist_browser_timer *hbt, u64 init_al_addr) > { > struct symbol *sym = ms->sym; > struct annotation *notes = symbol__annotation(sym); > + struct disasm_line *dl = NULL; > struct annotate_browser browser = { > .b = { > .refresh = annotate_browser__refresh, > @@ -1173,6 +1194,18 @@ int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms, > browser.he = &annotate_he; > } > > + /* > + * If init_al_addr is set, it means that there should be a line > + * intentionally selected, not based on the percentages > + * which caculated by the event sampling. In this case, we > + * convey this information into the browser selection, where > + * the selection in other cases should be empty. > + */ > + if (init_al_addr != NO_INITIAL_AL_ADDR) { > + dl = find_disasm_line(sym, init_al_addr, false); > + browser.selection = &dl->al; > + } > + > ui_helpline__push("Press ESC to exit"); > > if (annotate_opts.code_with_type) { > diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c > index 487c0b08c003..c34ddc4ca13f 100644 > --- a/tools/perf/ui/browsers/hists.c > +++ b/tools/perf/ui/browsers/hists.c > @@ -2485,7 +2485,7 @@ do_annotate(struct hist_browser *browser, struct popup_action *act) > evsel = hists_to_evsel(browser->hists); > > he = hist_browser__selected_entry(browser); > - err = __hist_entry__tui_annotate(he, &act->ms, evsel, browser->hbt); > + err = __hist_entry__tui_annotate(he, &act->ms, evsel, browser->hbt, NO_INITIAL_AL_ADDR); > /* > * offer option to annotate the other branch source or target > * (if they exists) when returning from annotate > diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c > index c9b220d9f924..937effbeda69 100644 > --- a/tools/perf/util/annotate.c > +++ b/tools/perf/util/annotate.c > @@ -2622,7 +2622,7 @@ int annotate_get_insn_location(struct arch *arch, struct disasm_line *dl, > return 0; > } > > -static struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip, > +struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip, > bool allow_update) > { > struct disasm_line *dl; > diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h > index eaf6c8aa7f47..bbe67588bbdd 100644 > --- a/tools/perf/util/annotate.h > +++ b/tools/perf/util/annotate.h > @@ -170,6 +170,8 @@ static inline struct disasm_line *disasm_line(struct annotation_line *al) > return al ? container_of(al, struct disasm_line, al) : NULL; > } > > +struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip, bool allow_update); > + > /* > * Is this offset in the same function as the line it is used? > * asm functions jump to other functions, for instance. > diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h > index c64005278687..7afa50aa5cbb 100644 > --- a/tools/perf/util/hist.h > +++ b/tools/perf/util/hist.h > @@ -713,12 +713,14 @@ struct block_hist { > #include "../ui/keysyms.h" > void attr_to_script(char *buf, struct perf_event_attr *attr); > > +#define NO_INITIAL_AL_ADDR 0 > + > int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms, > struct evsel *evsel, > - struct hist_browser_timer *hbt); > + struct hist_browser_timer *hbt, u64 init_al_addr); > > int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel, > - struct hist_browser_timer *hbt); > + struct hist_browser_timer *hbt, u64 init_al_addr); > > int evlist__tui_browse_hists(struct evlist *evlist, const char *help, struct hist_browser_timer *hbt, > float min_pcnt, struct perf_env *env, bool warn_lost_event); > -- > 2.47.1 >
Hi Namhyung, Appreciated for your review comments. Sorry for the delayed response. I am on National Holiday so check email late. My response inlined for your consideration. Regards, Tianyou On 10/3/2025 1:05 PM, Namhyung Kim wrote: > Hello, > > On Tue, Sep 30, 2025 at 08:39:00PM +0800, Tianyou Li wrote: >> Perf c2c report currently specified the code address and source:line >> information in the cacheline browser, while it is lack of annotation >> support like perf report to directly show the disassembly code for >> the particular symbol shared that same cacheline. This patches add >> a key 'a' binding to the cacheline browser which reuse the annotation >> browser to show the disassembly view for easier analysis of cacheline >> contentions. By default, the 'TAB' key navigate to the code address >> where the contentions detected. >> >> Signed-off-by: Tianyou Li <tianyou.li@intel.com> >> Reviewed-by: Dapeng Mi <dapeng1.mi@linux.intel.com> >> Reviewed-by: Thomas Falcon <thomas.falcon@intel.com> >> Reviewed-by: Jiebin Sun <jiebin.sun@intel.com> >> Reviewed-by: Pan Deng <pan.deng@intel.com> >> Reviewed-by: Zhiguo Zhou <zhiguo.zhou@intel.com> >> Reviewed-by: Wangyang Guo <wangyang.guo@intel.com> >> --- >> tools/perf/builtin-annotate.c | 2 +- >> tools/perf/builtin-c2c.c | 160 ++++++++++++++++++++++++++++-- >> tools/perf/ui/browsers/annotate.c | 41 +++++++- >> tools/perf/ui/browsers/hists.c | 2 +- >> tools/perf/util/annotate.c | 2 +- >> tools/perf/util/annotate.h | 2 + >> tools/perf/util/hist.h | 6 +- >> 7 files changed, 200 insertions(+), 15 deletions(-) >> >> diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c >> index 646f43b0f7c4..d89796648bec 100644 >> --- a/tools/perf/builtin-annotate.c >> +++ b/tools/perf/builtin-annotate.c >> @@ -519,7 +519,7 @@ static void hists__find_annotations(struct hists *hists, >> /* skip missing symbols */ >> nd = rb_next(nd); >> } else if (use_browser == 1) { >> - key = hist_entry__tui_annotate(he, evsel, NULL); >> + key = hist_entry__tui_annotate(he, evsel, NULL, NO_INITIAL_AL_ADDR); >> >> switch (key) { >> case -1: >> diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c >> index 9e9ff471ddd1..7989fc46516e 100644 >> --- a/tools/perf/builtin-c2c.c >> +++ b/tools/perf/builtin-c2c.c >> @@ -45,6 +45,8 @@ >> #include "pmus.h" >> #include "string2.h" >> #include "util/util.h" >> +#include "util/symbol.h" >> +#include "util/annotate.h" >> >> struct c2c_hists { >> struct hists hists; >> @@ -62,6 +64,7 @@ struct compute_stats { >> >> struct c2c_hist_entry { >> struct c2c_hists *hists; >> + struct evsel *evsel; >> struct c2c_stats stats; >> unsigned long *cpuset; >> unsigned long *nodeset; >> @@ -225,6 +228,12 @@ he__get_c2c_hists(struct hist_entry *he, >> return hists; >> } >> >> +static void c2c_he__set_evsel(struct c2c_hist_entry *c2c_he, >> + struct evsel *evsel) >> +{ >> + c2c_he->evsel = evsel; >> +} >> + >> static void c2c_he__set_cpu(struct c2c_hist_entry *c2c_he, >> struct perf_sample *sample) >> { >> @@ -334,6 +343,7 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused, >> >> c2c_he__set_cpu(c2c_he, sample); >> c2c_he__set_node(c2c_he, sample); >> + c2c_he__set_evsel(c2c_he, evsel); >> >> hists__inc_nr_samples(&c2c_hists->hists, he->filtered); >> ret = hist_entry__append_callchain(he, sample); >> @@ -371,6 +381,7 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused, >> >> c2c_he__set_cpu(c2c_he, sample); >> c2c_he__set_node(c2c_he, sample); >> + c2c_he__set_evsel(c2c_he, evsel); >> >> hists__inc_nr_samples(&c2c_hists->hists, he->filtered); >> ret = hist_entry__append_callchain(he, sample); >> @@ -1997,6 +2008,9 @@ static int c2c_hists__init_sort(struct perf_hpp_list *hpp_list, char *name, stru >> if (dim == &dim_dso) >> hpp_list->dso = 1; >> >> + if (dim == &dim_symbol) >> + hpp_list->sym = 1; >> + >> perf_hpp_list__register_sort_field(hpp_list, &c2c_fmt->fmt); >> return 0; >> } >> @@ -2549,7 +2563,65 @@ static void perf_c2c__hists_fprintf(FILE *out, struct perf_session *session) >> print_pareto(out, perf_session__env(session)); >> } >> >> +/* >> + * Return true if annotation is possible. When list is NULL, >> + * it means that we are called at the c2c_browser level, >> + * in that case we allow annotation to be initialized.When list >> + * is non-NULL, it means that we are called at the cacheline_browser >> + * level, in that case we allow annotation only if use_browser >> + * is set and symbol information is available. >> + */ >> +static bool perf_c2c__has_annotation(struct perf_hpp_list *list) >> +{ >> + bool has_annotation = false; >> + >> + if (list == NULL) >> + has_annotation = use_browser == 1; >> + else >> + has_annotation = use_browser == 1 && list->sym; >> + >> + return has_annotation; > Nit: it would be simpler this way. > > if (use_browser != 1) > return false; > return !list || list->sym; Nice, the code looks more concise. Will update it in patch v6. Thanks. >> +} >> + >> #ifdef HAVE_SLANG_SUPPORT >> + >> +static int perf_c2c__toggle_annotation(struct hist_browser *browser) >> +{ >> + struct hist_entry *he = browser->he_selection; >> + struct symbol *sym = NULL; >> + struct c2c_hist_entry *c2c_he = NULL; >> + struct annotated_source *src = NULL; >> + u64 addr = 0; > u64 addr = NO_INITIAL_AL_ADDR; > >> + >> + if (!perf_c2c__has_annotation(he->hists->hpp_list)) { >> + ui_browser__help_window(&browser->b, "No annotation support"); >> + return 0; >> + } >> + >> + if (he == NULL) { >> + ui_browser__help_window(&browser->b, "No entry selected for annotation"); >> + return 0; >> + } >> + sym = (&he->ms)->sym; > sym = he->ms.sym; My mistake, thanks for pointing out. Will update in patch v6. Thanks. >> + >> + if (sym == NULL) { >> + ui_browser__help_window(&browser->b, "Can not annotate, no symbol found"); >> + return 0; >> + } >> + >> + src = symbol__hists(sym, 0); >> + if (src == NULL) { >> + ui_browser__help_window(&browser->b, "Failed to initialize annotation source"); >> + return 0; >> + } >> + >> + if (he->mem_info) >> + addr = mem_info__iaddr(he->mem_info)->al_addr; >> + >> + c2c_he = container_of(he, struct c2c_hist_entry, he); >> + return hist_entry__tui_annotate(he, c2c_he->evsel, NULL, addr); >> +} >> + >> static void c2c_browser__update_nr_entries(struct hist_browser *hb) >> { >> u64 nr_entries = 0; >> @@ -2617,6 +2689,7 @@ static int perf_c2c__browse_cacheline(struct hist_entry *he) >> " ENTER Toggle callchains (if present) \n" >> " n Toggle Node details info \n" >> " s Toggle full length of symbol and source line columns \n" >> + " a Toggle annotation view \n" >> " q Return back to cacheline list \n"; >> >> if (!he) >> @@ -2651,6 +2724,9 @@ static int perf_c2c__browse_cacheline(struct hist_entry *he) >> c2c.node_info = (c2c.node_info + 1) % 3; >> setup_nodes_header(); >> break; >> + case 'a': >> + perf_c2c__toggle_annotation(browser); >> + break; >> case 'q': >> goto out; >> case '?': >> @@ -2980,7 +3056,8 @@ static int setup_coalesce(const char *coalesce, bool no_source) >> else if (c2c.display == DISPLAY_SNP_PEER) >> sort_str = "tot_peer"; >> >> - if (asprintf(&c2c.cl_resort, "offset,%s", sort_str) < 0) >> + /* add 'symbol' sort key to make sure hpp_list->sym get updated */ >> + if (asprintf(&c2c.cl_resort, "offset,%s,symbol", sort_str) < 0) > I think it's better to just process the input rather than enforcing it. > It seems the default value will have 'iaddr' and so 'symbol as well. Sorry I am not so clear about 'so symbol as well'. Did you mean we can check the 'dim == &dim_iaddr' instead of 'dim == &dim_symbol' to make sure hpp_list->sym = 1? If so, do we need to check the coalesce set to default 'iaddr' or not, otherwise we need to append the 'iaddr' in addition to the user specific one? > >> return -ENOMEM; >> >> pr_debug("coalesce sort fields: %s\n", c2c.cl_sort); >> @@ -3006,6 +3083,7 @@ static int perf_c2c__report(int argc, const char **argv) >> const char *display = NULL; >> const char *coalesce = NULL; >> bool no_source = false; >> + const char *disassembler_style = NULL, *objdump_path = NULL, *addr2line_path = NULL; >> const struct option options[] = { >> OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, >> "file", "vmlinux pathname"), >> @@ -3033,6 +3111,12 @@ static int perf_c2c__report(int argc, const char **argv) >> OPT_BOOLEAN(0, "stitch-lbr", &c2c.stitch_lbr, >> "Enable LBR callgraph stitching approach"), >> OPT_BOOLEAN(0, "double-cl", &chk_double_cl, "Detect adjacent cacheline false sharing"), >> + OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style", >> + "Specify disassembler style (e.g. -M intel for intel syntax)"), >> + OPT_STRING(0, "objdump", &objdump_path, "path", >> + "objdump binary to use for disassembly and annotations"), > Please update documentation with the new options. Noted, will do in patch v6. > >> + OPT_STRING(0, "addr2line", &addr2line_path, "path", >> + "addr2line binary to use for line numbers"), > Do you really need this? In my use scenarios of c2c tool, I did not use this addr2line tool. If this was not quite necessary, I will remove it from patch v6. > >> OPT_PARENT(c2c_options), >> OPT_END() >> }; >> @@ -3040,6 +3124,12 @@ static int perf_c2c__report(int argc, const char **argv) >> const char *output_str, *sort_str = NULL; >> struct perf_env *env; >> >> + annotation_options__init(); >> + >> + err = hists__init(); >> + if (err < 0) >> + goto out; >> + >> argc = parse_options(argc, argv, options, report_c2c_usage, >> PARSE_OPT_STOP_AT_NON_OPTION); >> if (argc) >> @@ -3052,6 +3142,36 @@ static int perf_c2c__report(int argc, const char **argv) >> if (c2c.stats_only) >> c2c.use_stdio = true; >> >> + /** >> + * Annotation related options >> + * disassembler_style, objdump_path, addr2line_path >> + * are set in the c2c_options, so we can use them here. >> + */ >> + if (disassembler_style) { >> + annotate_opts.disassembler_style = strdup(disassembler_style); >> + if (!annotate_opts.disassembler_style) { >> + err = -ENOMEM; >> + pr_err("Failed to allocate memory for annotation options\n"); >> + goto out; >> + } >> + } >> + if (objdump_path) { >> + annotate_opts.objdump_path = strdup(objdump_path); >> + if (!annotate_opts.objdump_path) { >> + err = -ENOMEM; >> + pr_err("Failed to allocate memory for annotation options\n"); >> + goto out; >> + } >> + } >> + if (addr2line_path) { >> + symbol_conf.addr2line_path = strdup(addr2line_path); >> + if (!symbol_conf.addr2line_path) { >> + err = -ENOMEM; >> + pr_err("Failed to allocate memory for annotation options\n"); >> + goto out; >> + } >> + } >> + >> err = symbol__validate_sym_arguments(); >> if (err) >> goto out; >> @@ -3126,6 +3246,38 @@ static int perf_c2c__report(int argc, const char **argv) >> if (err) >> goto out_mem2node; >> >> + if (c2c.use_stdio) >> + use_browser = 0; >> + else >> + use_browser = 1; >> + >> + /* >> + * Only in the TUI browser we are doing integrated annotation, >> + * so don't allocate extra space that won't be used in the stdio >> + * implementation. >> + */ >> + if (perf_c2c__has_annotation(NULL)) { >> + int ret = symbol__annotation_init(); >> + >> + if (ret < 0) >> + goto out_mem2node; >> + /* >> + * For searching by name on the "Browse map details". >> + * providing it only in verbose mode not to bloat too >> + * much struct symbol. >> + */ >> + if (verbose > 0) { >> + /* >> + * XXX: Need to provide a less kludgy way to ask for >> + * more space per symbol, the u32 is for the index on >> + * the ui browser. >> + * See symbol__browser_index. >> + */ >> + symbol_conf.priv_size += sizeof(u32); >> + } >> + annotation_config__init(); >> + } >> + >> if (symbol__init(env) < 0) >> goto out_mem2node; >> >> @@ -3135,11 +3287,6 @@ static int perf_c2c__report(int argc, const char **argv) >> goto out_mem2node; >> } >> >> - if (c2c.use_stdio) >> - use_browser = 0; >> - else >> - use_browser = 1; >> - >> setup_browser(false); >> >> err = perf_session__process_events(session); >> @@ -3210,6 +3357,7 @@ static int perf_c2c__report(int argc, const char **argv) >> out_session: >> perf_session__delete(session); >> out: >> + annotation_options__exit(); >> return err; >> } >> >> diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c >> index 8fe699f98542..a9d56e67454d 100644 >> --- a/tools/perf/ui/browsers/annotate.c >> +++ b/tools/perf/ui/browsers/annotate.c >> @@ -605,7 +605,7 @@ static bool annotate_browser__callq(struct annotate_browser *browser, >> target_ms.map = ms->map; >> target_ms.sym = dl->ops.target.sym; >> annotation__unlock(notes); >> - __hist_entry__tui_annotate(browser->he, &target_ms, evsel, hbt); >> + __hist_entry__tui_annotate(browser->he, &target_ms, evsel, hbt, NO_INITIAL_AL_ADDR); >> >> /* >> * The annotate_browser above changed the title with the target function >> @@ -864,6 +864,7 @@ static int annotate_browser__run(struct annotate_browser *browser, >> const char *help = "Press 'h' for help on key bindings"; >> int delay_secs = hbt ? hbt->refresh : 0; >> char *br_cntr_text = NULL; >> + u64 init_al_addr = NO_INITIAL_AL_ADDR; >> char title[256]; >> int key; >> >> @@ -873,6 +874,13 @@ static int annotate_browser__run(struct annotate_browser *browser, >> >> annotate_browser__calc_percent(browser, evsel); >> >> + /* the selection are intentionally even not from the sample percentage */ >> + if (browser->entries.rb_node == NULL && browser->selection) { >> + init_al_addr = sym->start + browser->selection->offset; >> + disasm_rb_tree__insert(browser, browser->selection); >> + browser->curr_hot = rb_last(&browser->entries); >> + } >> + >> if (browser->curr_hot) { >> annotate_browser__set_rb_top(browser, browser->curr_hot); >> browser->b.navkeypressed = false; >> @@ -973,6 +981,18 @@ static int annotate_browser__run(struct annotate_browser *browser, >> ui_helpline__puts(help); >> annotate__scnprintf_title(hists, title, sizeof(title)); >> annotate_browser__show(browser, title, help); >> + /* Previous RB tree may not valid, need refresh according to new entries*/ >> + if (init_al_addr != NO_INITIAL_AL_ADDR) { >> + struct disasm_line *dl = find_disasm_line(sym, init_al_addr, true); >> + >> + browser->curr_hot = NULL; >> + browser->entries.rb_node = NULL; >> + if (dl != NULL) { >> + disasm_rb_tree__insert(browser, &dl->al); >> + browser->curr_hot = rb_last(&browser->entries); >> + } >> + nd = browser->curr_hot; >> + } > Can you please split annotate changes from c2c change? I think you can > start with annotation support in c2c. And add INITIAL_ADDR later so > that we can discuss the issue separately. Maybe we don't need the ADDR > change. Do you have any concrete usecase where default annotate is not > enough for c2c? Sure, I will split the patch into 2 patches. I use c2c extensively for my day-to-day performance work, the INITIAL_ADDR would be very helpful to located to the code where the iaddr was indicated in the cacheline browser. Otherwise, probably I need to copy the iaddr from the cacheline browser, get into the annotation browser, press 'o' to show the view with addresses in disassemble view, and manually find the iaddr match since the search only match string for disassembly code. The code highlight with INITIAL_ADDR would quickly allow me to navigate the contended lines of code from different functions showed in the cacheline browser, plus with 's' and 'T', I can get to the point more conveniently. Agreed to discuss it separately, looking forward to hearing your thoughts. Regards, Tianyou > Thanks, > Namhyung > >> continue; >> case 'o': >> annotate_opts.use_offset = !annotate_opts.use_offset; >> @@ -1106,22 +1126,23 @@ static int annotate_browser__run(struct annotate_browser *browser, >> } >> >> int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel, >> - struct hist_browser_timer *hbt) >> + struct hist_browser_timer *hbt, u64 init_al_addr) >> { >> /* reset abort key so that it can get Ctrl-C as a key */ >> SLang_reset_tty(); >> SLang_init_tty(0, 0, 0); >> SLtty_set_suspend_state(true); >> >> - return __hist_entry__tui_annotate(he, &he->ms, evsel, hbt); >> + return __hist_entry__tui_annotate(he, &he->ms, evsel, hbt, init_al_addr); >> } >> >> int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms, >> struct evsel *evsel, >> - struct hist_browser_timer *hbt) >> + struct hist_browser_timer *hbt, u64 init_al_addr) >> { >> struct symbol *sym = ms->sym; >> struct annotation *notes = symbol__annotation(sym); >> + struct disasm_line *dl = NULL; >> struct annotate_browser browser = { >> .b = { >> .refresh = annotate_browser__refresh, >> @@ -1173,6 +1194,18 @@ int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms, >> browser.he = &annotate_he; >> } >> >> + /* >> + * If init_al_addr is set, it means that there should be a line >> + * intentionally selected, not based on the percentages >> + * which caculated by the event sampling. In this case, we >> + * convey this information into the browser selection, where >> + * the selection in other cases should be empty. >> + */ >> + if (init_al_addr != NO_INITIAL_AL_ADDR) { >> + dl = find_disasm_line(sym, init_al_addr, false); >> + browser.selection = &dl->al; >> + } >> + >> ui_helpline__push("Press ESC to exit"); >> >> if (annotate_opts.code_with_type) { >> diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c >> index 487c0b08c003..c34ddc4ca13f 100644 >> --- a/tools/perf/ui/browsers/hists.c >> +++ b/tools/perf/ui/browsers/hists.c >> @@ -2485,7 +2485,7 @@ do_annotate(struct hist_browser *browser, struct popup_action *act) >> evsel = hists_to_evsel(browser->hists); >> >> he = hist_browser__selected_entry(browser); >> - err = __hist_entry__tui_annotate(he, &act->ms, evsel, browser->hbt); >> + err = __hist_entry__tui_annotate(he, &act->ms, evsel, browser->hbt, NO_INITIAL_AL_ADDR); >> /* >> * offer option to annotate the other branch source or target >> * (if they exists) when returning from annotate >> diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c >> index c9b220d9f924..937effbeda69 100644 >> --- a/tools/perf/util/annotate.c >> +++ b/tools/perf/util/annotate.c >> @@ -2622,7 +2622,7 @@ int annotate_get_insn_location(struct arch *arch, struct disasm_line *dl, >> return 0; >> } >> >> -static struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip, >> +struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip, >> bool allow_update) >> { >> struct disasm_line *dl; >> diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h >> index eaf6c8aa7f47..bbe67588bbdd 100644 >> --- a/tools/perf/util/annotate.h >> +++ b/tools/perf/util/annotate.h >> @@ -170,6 +170,8 @@ static inline struct disasm_line *disasm_line(struct annotation_line *al) >> return al ? container_of(al, struct disasm_line, al) : NULL; >> } >> >> +struct disasm_line *find_disasm_line(struct symbol *sym, u64 ip, bool allow_update); >> + >> /* >> * Is this offset in the same function as the line it is used? >> * asm functions jump to other functions, for instance. >> diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h >> index c64005278687..7afa50aa5cbb 100644 >> --- a/tools/perf/util/hist.h >> +++ b/tools/perf/util/hist.h >> @@ -713,12 +713,14 @@ struct block_hist { >> #include "../ui/keysyms.h" >> void attr_to_script(char *buf, struct perf_event_attr *attr); >> >> +#define NO_INITIAL_AL_ADDR 0 >> + >> int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms, >> struct evsel *evsel, >> - struct hist_browser_timer *hbt); >> + struct hist_browser_timer *hbt, u64 init_al_addr); >> >> int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel, >> - struct hist_browser_timer *hbt); >> + struct hist_browser_timer *hbt, u64 init_al_addr); >> >> int evlist__tui_browse_hists(struct evlist *evlist, const char *help, struct hist_browser_timer *hbt, >> float min_pcnt, struct perf_env *env, bool warn_lost_event); >> -- >> 2.47.1 >>
© 2016 - 2025 Red Hat, Inc.