linux-perf-users.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] perf tools c2c: Add annotation support to perf c2c report
@ 2025-08-01  7:51 Tianyou Li
  2025-08-19  8:00 ` [PATCH v2] " Tianyou Li
  0 siblings, 1 reply; 36+ messages in thread
From: Tianyou Li @ 2025-08-01  7:51 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim
  Cc: Mark Rutland, Alexander Shishkin, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Kan Liang, tianyou.li, wangyang.guo, pan.deng,
	zhiguo.zhou, jiebin.sun, thomas.falcon, dapeng1.mi,
	linux-perf-users, linux-kernel

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          | 120 ++++++++++++++++++++++++++++--
 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, 149 insertions(+), 17 deletions(-)

diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 9833c2c82a2f..5d52d6a461b3 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 e2e257bcc461..ccdac419df0e 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;
@@ -223,6 +226,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)
 {
@@ -332,6 +341,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);
@@ -369,6 +379,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);
@@ -2598,6 +2609,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;
@@ -2609,6 +2642,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)
@@ -2643,6 +2677,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 '?':
@@ -2981,6 +3018,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 = {
@@ -2998,6 +3040,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"),
@@ -3025,12 +3069,20 @@ 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()
 	};
 	int err = 0;
 	const char *output_str, *sort_str = NULL;
 
+	annotation_options__init();
+
 	argc = parse_options(argc, argv, options, report_c2c_usage,
 			     PARSE_OPT_STOP_AT_NON_OPTION);
 	if (argc)
@@ -3043,6 +3095,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;
@@ -3117,6 +3199,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(&session->header.env) < 0)
 		goto out_mem2node;
 
@@ -3126,11 +3240,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);
@@ -3201,6 +3310,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 ab776b1ed2d5..64f0a8891294 100644
--- a/tools/perf/ui/browsers/annotate.c
+++ b/tools/perf/ui/browsers/annotate.c
@@ -483,7 +483,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;
@@ -740,6 +740,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;
@@ -959,27 +964,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,
@@ -1013,6 +1019,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 d26b925e3d7f..d9ae0e5d8205 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 264a212b47df..54b7759b1930 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -2542,7 +2542,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 bbb89b32f398..75170bd429a7 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.
@@ -472,7 +474,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 c64254088fc7..0b8bd9585127 100644
--- a/tools/perf/util/hist.h
+++ b/tools/perf/util/hist.h
@@ -712,11 +712,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


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v2] perf tools c2c: Add annotation support to perf c2c report
  2025-08-01  7:51 [PATCH] perf tools c2c: Add annotation support to perf c2c report Tianyou Li
@ 2025-08-19  8:00 ` Tianyou Li
  2025-09-03 13:50   ` Arnaldo Carvalho de Melo
  0 siblings, 1 reply; 36+ messages in thread
From: Tianyou Li @ 2025-08-19  8:00 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim
  Cc: Mark Rutland, Alexander Shishkin, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Kan Liang, tianyou.li, wangyang.guo, pan.deng,
	zhiguo.zhou, jiebin.sun, thomas.falcon, dapeng1.mi,
	linux-perf-users, linux-kernel

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


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* Re: [PATCH v2] perf tools c2c: Add annotation support to perf c2c report
  2025-08-19  8:00 ` [PATCH v2] " Tianyou Li
@ 2025-09-03 13:50   ` Arnaldo Carvalho de Melo
  2025-09-07 14:53     ` Li, Tianyou
  2025-09-07 15:25     ` [PATCH v3] " Tianyou Li
  0 siblings, 2 replies; 36+ messages in thread
From: Arnaldo Carvalho de Melo @ 2025-09-03 13:50 UTC (permalink / raw)
  To: Tianyou Li
  Cc: Peter Zijlstra, Ingo Molnar, Namhyung Kim, Mark Rutland,
	Alexander Shishkin, Jiri Olsa, Ian Rogers, Adrian Hunter,
	Kan Liang, wangyang.guo, pan.deng, zhiguo.zhou, jiebin.sun,
	thomas.falcon, dapeng1.mi, linux-perf-users, linux-kernel

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
> 

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v2] perf tools c2c: Add annotation support to perf c2c report
  2025-09-03 13:50   ` Arnaldo Carvalho de Melo
@ 2025-09-07 14:53     ` Li, Tianyou
  2025-09-07 15:25     ` [PATCH v3] " Tianyou Li
  1 sibling, 0 replies; 36+ messages in thread
From: Li, Tianyou @ 2025-09-07 14:53 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Peter Zijlstra, Ingo Molnar, Namhyung Kim, Mark Rutland,
	Alexander Shishkin, Jiri Olsa, Ian Rogers, Adrian Hunter,
	Kan Liang, wangyang.guo, pan.deng, zhiguo.zhou, jiebin.sun,
	thomas.falcon, dapeng1.mi, linux-perf-users, linux-kernel


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

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [PATCH v3] perf tools c2c: Add annotation support to perf c2c report
  2025-09-03 13:50   ` Arnaldo Carvalho de Melo
  2025-09-07 14:53     ` Li, Tianyou
@ 2025-09-07 15:25     ` Tianyou Li
  2025-09-11 21:39       ` Namhyung Kim
  1 sibling, 1 reply; 36+ messages in thread
From: Tianyou Li @ 2025-09-07 15:25 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim
  Cc: Mark Rutland, Alexander Shishkin, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Kan Liang, tianyou.li, wangyang.guo, pan.deng,
	zhiguo.zhou, jiebin.sun, thomas.falcon, dapeng1.mi,
	linux-perf-users, linux-kernel

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


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* Re: [PATCH v3] perf tools c2c: Add annotation support to perf c2c report
  2025-09-07 15:25     ` [PATCH v3] " Tianyou Li
@ 2025-09-11 21:39       ` Namhyung Kim
  2025-09-12 15:20         ` Li, Tianyou
  0 siblings, 1 reply; 36+ messages in thread
From: Namhyung Kim @ 2025-09-11 21:39 UTC (permalink / raw)
  To: Tianyou Li
  Cc: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Mark Rutland, Alexander Shishkin, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Kan Liang, wangyang.guo, pan.deng, zhiguo.zhou,
	jiebin.sun, thomas.falcon, dapeng1.mi, linux-perf-users,
	linux-kernel

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
> 

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v3] perf tools c2c: Add annotation support to perf c2c report
  2025-09-11 21:39       ` Namhyung Kim
@ 2025-09-12 15:20         ` Li, Tianyou
  2025-09-17  6:34           ` Namhyung Kim
  0 siblings, 1 reply; 36+ messages in thread
From: Li, Tianyou @ 2025-09-12 15:20 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Mark Rutland, Alexander Shishkin, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Kan Liang, wangyang.guo, pan.deng, zhiguo.zhou,
	jiebin.sun, thomas.falcon, dapeng1.mi, linux-perf-users,
	linux-kernel


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

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v3] perf tools c2c: Add annotation support to perf c2c report
  2025-09-12 15:20         ` Li, Tianyou
@ 2025-09-17  6:34           ` Namhyung Kim
  2025-09-24  7:33             ` Li, Tianyou
  2025-09-28  9:02             ` [PATCH v4] " Tianyou Li
  0 siblings, 2 replies; 36+ messages in thread
From: Namhyung Kim @ 2025-09-17  6:34 UTC (permalink / raw)
  To: Li, Tianyou
  Cc: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Mark Rutland, Alexander Shishkin, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Kan Liang, wangyang.guo, pan.deng, zhiguo.zhou,
	jiebin.sun, thomas.falcon, dapeng1.mi, linux-perf-users,
	linux-kernel

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

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v3] perf tools c2c: Add annotation support to perf c2c report
  2025-09-17  6:34           ` Namhyung Kim
@ 2025-09-24  7:33             ` Li, Tianyou
  2025-09-28  9:02             ` [PATCH v4] " Tianyou Li
  1 sibling, 0 replies; 36+ messages in thread
From: Li, Tianyou @ 2025-09-24  7:33 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Mark Rutland, Alexander Shishkin, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Kan Liang, wangyang.guo, pan.deng, zhiguo.zhou,
	jiebin.sun, thomas.falcon, dapeng1.mi, linux-perf-users,
	linux-kernel


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

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v4] perf tools c2c: Add annotation support to perf c2c report
  2025-09-28  9:02             ` [PATCH v4] " Tianyou Li
@ 2025-09-28  8:16               ` Li, Tianyou
  2025-09-29  8:07                 ` Namhyung Kim
  0 siblings, 1 reply; 36+ messages in thread
From: Li, Tianyou @ 2025-09-28  8:16 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim
  Cc: Mark Rutland, Alexander Shishkin, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Kan Liang, wangyang.guo, pan.deng, zhiguo.zhou,
	jiebin.sun, thomas.falcon, dapeng1.mi, linux-perf-users,
	linux-kernel

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

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [PATCH v4] perf tools c2c: Add annotation support to perf c2c report
  2025-09-17  6:34           ` Namhyung Kim
  2025-09-24  7:33             ` Li, Tianyou
@ 2025-09-28  9:02             ` Tianyou Li
  2025-09-28  8:16               ` Li, Tianyou
  1 sibling, 1 reply; 36+ messages in thread
From: Tianyou Li @ 2025-09-28  9:02 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim
  Cc: Mark Rutland, Alexander Shishkin, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Kan Liang, tianyou.li, wangyang.guo, pan.deng,
	zhiguo.zhou, jiebin.sun, thomas.falcon, dapeng1.mi,
	linux-perf-users, linux-kernel

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


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* Re: [PATCH v4] perf tools c2c: Add annotation support to perf c2c report
  2025-09-28  8:16               ` Li, Tianyou
@ 2025-09-29  8:07                 ` Namhyung Kim
  2025-09-30 11:41                   ` Li, Tianyou
  2025-09-30 12:39                   ` [PATCH v5] " Tianyou Li
  0 siblings, 2 replies; 36+ messages in thread
From: Namhyung Kim @ 2025-09-29  8:07 UTC (permalink / raw)
  To: Li, Tianyou
  Cc: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Mark Rutland, Alexander Shishkin, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Kan Liang, wangyang.guo, pan.deng, zhiguo.zhou,
	jiebin.sun, thomas.falcon, dapeng1.mi, linux-perf-users,
	linux-kernel

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

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v4] perf tools c2c: Add annotation support to perf c2c report
  2025-09-29  8:07                 ` Namhyung Kim
@ 2025-09-30 11:41                   ` Li, Tianyou
  2025-09-30 12:39                   ` [PATCH v5] " Tianyou Li
  1 sibling, 0 replies; 36+ messages in thread
From: Li, Tianyou @ 2025-09-30 11:41 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Mark Rutland, Alexander Shishkin, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Kan Liang, wangyang.guo, pan.deng, zhiguo.zhou,
	jiebin.sun, thomas.falcon, dapeng1.mi, linux-perf-users,
	linux-kernel


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

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [PATCH v5] perf tools c2c: Add annotation support to perf c2c report
  2025-09-29  8:07                 ` Namhyung Kim
  2025-09-30 11:41                   ` Li, Tianyou
@ 2025-09-30 12:39                   ` Tianyou Li
  2025-10-03  5:05                     ` Namhyung Kim
  1 sibling, 1 reply; 36+ messages in thread
From: Tianyou Li @ 2025-09-30 12:39 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim
  Cc: Mark Rutland, Alexander Shishkin, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Kan Liang, tianyou.li, wangyang.guo, pan.deng,
	zhiguo.zhou, jiebin.sun, thomas.falcon, dapeng1.mi,
	linux-perf-users, linux-kernel

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


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* Re: [PATCH v5] perf tools c2c: Add annotation support to perf c2c report
  2025-09-30 12:39                   ` [PATCH v5] " Tianyou Li
@ 2025-10-03  5:05                     ` Namhyung Kim
  2025-10-03 11:44                       ` Li, Tianyou
  2025-10-06 10:54                       ` [PATCH v5] perf tools c2c: Add annotation support to perf c2c report Ravi Bangoria
  0 siblings, 2 replies; 36+ messages in thread
From: Namhyung Kim @ 2025-10-03  5:05 UTC (permalink / raw)
  To: Tianyou Li
  Cc: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Mark Rutland, Alexander Shishkin, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Kan Liang, wangyang.guo, pan.deng, zhiguo.zhou,
	jiebin.sun, thomas.falcon, dapeng1.mi, linux-perf-users,
	linux-kernel

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
> 

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v5] perf tools c2c: Add annotation support to perf c2c report
  2025-10-03  5:05                     ` Namhyung Kim
@ 2025-10-03 11:44                       ` Li, Tianyou
  2025-10-07  8:23                         ` Namhyung Kim
  2025-10-06 10:54                       ` [PATCH v5] perf tools c2c: Add annotation support to perf c2c report Ravi Bangoria
  1 sibling, 1 reply; 36+ messages in thread
From: Li, Tianyou @ 2025-10-03 11:44 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Mark Rutland, Alexander Shishkin, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Kan Liang, wangyang.guo, pan.deng, zhiguo.zhou,
	jiebin.sun, thomas.falcon, dapeng1.mi, linux-perf-users,
	linux-kernel

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

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v5] perf tools c2c: Add annotation support to perf c2c report
  2025-10-03  5:05                     ` Namhyung Kim
  2025-10-03 11:44                       ` Li, Tianyou
@ 2025-10-06 10:54                       ` Ravi Bangoria
  2025-10-09  5:34                         ` Li, Tianyou
  1 sibling, 1 reply; 36+ messages in thread
From: Ravi Bangoria @ 2025-10-06 10:54 UTC (permalink / raw)
  To: Tianyou Li
  Cc: Namhyung Kim, Peter Zijlstra, Ingo Molnar,
	Arnaldo Carvalho de Melo, Mark Rutland, Alexander Shishkin,
	Jiri Olsa, Ian Rogers, Adrian Hunter, Kan Liang, wangyang.guo,
	pan.deng, zhiguo.zhou, jiebin.sun, thomas.falcon, dapeng1.mi,
	linux-perf-users, linux-kernel, Ravi Bangoria

Hi Tianyou,

I tested this on an AMD machine and it's working fine. Few minor issues
I would suggest to address.

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

1. Annotate browser title always shows "Samples: 0", which is misleading:

   Samples: 0  of event 'ibs_op//', 100000 Hz, Event count (approx.): 0
            ^                                                         ^
             `--- this                                                 `--- and this

2. Comment from v3:

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

   It's ideal if we can show sample count/overhead along with the
   instruction (because that's what users are used to seeing with
   perf-report->annotate output). I haven't checked the code but is
   it possible to highlight the instruction by default (Annotate+TAB
   OR Set the font color to red etc.)? Waiting for user to press the
   TAB seems counterintuitive esp when instructions are not tagged
   with overhead.

        |1340: > callq   0x10f0 <_init+0xf0>
        |1345: v jmp     0x1376 <thread+0x12d>
        |1347:   cmpl    $0x1, -0xb0(%rbp)
        |134e: v jne     0x1364 <thread+0x11b>
        |1350:   movq    0x2d29(%rip), %rax  # 0x4080 <data>   <=====  Highlight it by default
        |1357:   addq    $0x1, %rax
        |135b:   movq    %rax, 0x2d1e(%rip)  # 0x4080 <data>
        |1362: v jmp     0x1376 <thread+0x12d>

These are not blockers if Namhyung / Arnaldo are okay with current design.

Thanks,
Ravi

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v5] perf tools c2c: Add annotation support to perf c2c report
  2025-10-03 11:44                       ` Li, Tianyou
@ 2025-10-07  8:23                         ` Namhyung Kim
  2025-10-09  3:47                           ` Li, Tianyou
                                             ` (7 more replies)
  0 siblings, 8 replies; 36+ messages in thread
From: Namhyung Kim @ 2025-10-07  8:23 UTC (permalink / raw)
  To: Li, Tianyou
  Cc: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Mark Rutland, Alexander Shishkin, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Kan Liang, wangyang.guo, pan.deng, zhiguo.zhou,
	jiebin.sun, thomas.falcon, dapeng1.mi, linux-perf-users,
	linux-kernel

Hello,

On Fri, Oct 03, 2025 at 07:44:34PM +0800, Li, Tianyou wrote:
> 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>
> > > ---
[SNIP]
> > > @@ -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?

I meant you have 'iaddr' in the default sort keys and it will include
'symbol' in the output.  So annotation will be enabled by default.

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

Yes, please.

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

Thanks for your understanding!
Namhyung


^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v5] perf tools c2c: Add annotation support to perf c2c report
  2025-10-07  8:23                         ` Namhyung Kim
@ 2025-10-09  3:47                           ` Li, Tianyou
  2025-10-09  4:28                           ` [PATCH v6] " Tianyou Li
                                             ` (6 subsequent siblings)
  7 siblings, 0 replies; 36+ messages in thread
From: Li, Tianyou @ 2025-10-09  3:47 UTC (permalink / raw)
  To: Namhyung Kim
  Cc: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Mark Rutland, Alexander Shishkin, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Kan Liang, wangyang.guo, pan.deng, zhiguo.zhou,
	jiebin.sun, thomas.falcon, dapeng1.mi, linux-perf-users,
	linux-kernel

Hi Namhyung,

Sorry for the delayed response, just came back from vacation. According 
to your review comments, I have sent patch v6 for your kindly review. 
Appreciated for your time.

Patch v6 rebased with latest perf-tools-next, removed the INIT_ADDR 
support, keeps the changes inside of the builtin-c2c.c, along with other 
code suggestions you've made. Basically split the annotate changes from 
the c2c changes.

I will work on the annotate changes according you and Ravi's 
suggestions, hopefully I could send that patch soon.

Regards,

Tianyou

On 10/7/2025 4:23 PM, Namhyung Kim wrote:
> Hello,
>
> On Fri, Oct 03, 2025 at 07:44:34PM +0800, Li, Tianyou wrote:
>> 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>
>>>> ---
> [SNIP]
>>>> @@ -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?
> I meant you have 'iaddr' in the default sort keys and it will include
> 'symbol' in the output.  So annotation will be enabled by default.
>
>>
>>>>    		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.
> Yes, please.
>
>>
>>>>    	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.
> Thanks for your understanding!
> Namhyung
>
>

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [PATCH v6] perf tools c2c: Add annotation support to perf c2c report
  2025-10-07  8:23                         ` Namhyung Kim
  2025-10-09  3:47                           ` Li, Tianyou
@ 2025-10-09  4:28                           ` Tianyou Li
  2025-10-10  5:56                             ` Ravi Bangoria
  2025-10-10  8:33                           ` [PATCH v6 1/3] " Tianyou Li
                                             ` (5 subsequent siblings)
  7 siblings, 1 reply; 36+ messages in thread
From: Tianyou Li @ 2025-10-09  4:28 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim
  Cc: Mark Rutland, Alexander Shishkin, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Kan Liang, tianyou.li, wangyang.guo, pan.deng,
	zhiguo.zhou, jiebin.sun, thomas.falcon, dapeng1.mi,
	linux-perf-users, linux-kernel

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.

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/Documentation/perf-c2c.txt |   7 ++
 tools/perf/builtin-c2c.c              | 137 +++++++++++++++++++++++++-
 2 files changed, 139 insertions(+), 5 deletions(-)

diff --git a/tools/perf/Documentation/perf-c2c.txt b/tools/perf/Documentation/perf-c2c.txt
index f4af2dd6ab31..40b0f71a2c44 100644
--- a/tools/perf/Documentation/perf-c2c.txt
+++ b/tools/perf/Documentation/perf-c2c.txt
@@ -143,6 +143,13 @@ REPORT OPTIONS
 	feature, which causes cacheline sharing to behave like the cacheline
 	size is doubled.
 
+-M::
+--disassembler-style=::
+	Set disassembler style for objdump.
+
+--objdump=<path>::
+        Path to objdump binary.
+
 C2C RECORD
 ----------
 The perf c2c record command setup options related to HITM cacheline analysis
diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c
index 9e9ff471ddd1..878913115b45 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 || dim == &dim_iaddr)
+		hpp_list->sym = 1;
+
 	perf_hpp_list__register_sort_field(hpp_list, &c2c_fmt->fmt);
 	return 0;
 }
@@ -2549,7 +2563,56 @@ 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)
+{
+	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 annotated_source *src = NULL;
+	struct c2c_hist_entry *c2c_he = NULL;
+
+	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;
+	}
+
+	c2c_he = container_of(he, struct c2c_hist_entry, he);
+	return hist_entry__tui_annotate(he, c2c_he->evsel, NULL);
+}
+
 static void c2c_browser__update_nr_entries(struct hist_browser *hb)
 {
 	u64 nr_entries = 0;
@@ -2617,6 +2680,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 +2715,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 '?':
@@ -3006,6 +3073,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;
 	const struct option options[] = {
 	OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
 		   "file", "vmlinux pathname"),
@@ -3033,6 +3101,10 @@ 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_PARENT(c2c_options),
 	OPT_END()
 	};
@@ -3040,6 +3112,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 +3130,27 @@ 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 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;
+		}
+	}
+
 	err = symbol__validate_sym_arguments();
 	if (err)
 		goto out;
@@ -3126,6 +3225,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 +3266,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 +3336,7 @@ static int perf_c2c__report(int argc, const char **argv)
 out_session:
 	perf_session__delete(session);
 out:
+	annotation_options__exit();
 	return err;
 }
 
-- 
2.47.1


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* Re: [PATCH v5] perf tools c2c: Add annotation support to perf c2c report
  2025-10-06 10:54                       ` [PATCH v5] perf tools c2c: Add annotation support to perf c2c report Ravi Bangoria
@ 2025-10-09  5:34                         ` Li, Tianyou
  0 siblings, 0 replies; 36+ messages in thread
From: Li, Tianyou @ 2025-10-09  5:34 UTC (permalink / raw)
  To: Ravi Bangoria
  Cc: Namhyung Kim, Peter Zijlstra, Ingo Molnar,
	Arnaldo Carvalho de Melo, Mark Rutland, Alexander Shishkin,
	Jiri Olsa, Ian Rogers, Adrian Hunter, Kan Liang, wangyang.guo,
	pan.deng, zhiguo.zhou, jiebin.sun, thomas.falcon, dapeng1.mi,
	linux-perf-users, linux-kernel

Hi Ravi,


Very appreciated for your testing. Could I add you to the 'Tested-by' 
for my next revision?


I am working on the annotate changes. Hope I can get your attention 
again in the next revision. Thanks.


Regards,

Tianyou


On 10/6/2025 6:54 PM, Ravi Bangoria wrote:
> Hi Tianyou,
>
> I tested this on an AMD machine and it's working fine. Few minor issues
> I would suggest to address.
>
>>> 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.
> 1. Annotate browser title always shows "Samples: 0", which is misleading:
>
>     Samples: 0  of event 'ibs_op//', 100000 Hz, Event count (approx.): 0
>              ^                                                         ^
>               `--- this                                                 `--- and this
>
> 2. Comment from v3:
>
>     > 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.
>
>     It's ideal if we can show sample count/overhead along with the
>     instruction (because that's what users are used to seeing with
>     perf-report->annotate output). I haven't checked the code but is
>     it possible to highlight the instruction by default (Annotate+TAB
>     OR Set the font color to red etc.)? Waiting for user to press the
>     TAB seems counterintuitive esp when instructions are not tagged
>     with overhead.
>
>          |1340: > callq   0x10f0 <_init+0xf0>
>          |1345: v jmp     0x1376 <thread+0x12d>
>          |1347:   cmpl    $0x1, -0xb0(%rbp)
>          |134e: v jne     0x1364 <thread+0x11b>
>          |1350:   movq    0x2d29(%rip), %rax  # 0x4080 <data>   <=====  Highlight it by default
>          |1357:   addq    $0x1, %rax
>          |135b:   movq    %rax, 0x2d1e(%rip)  # 0x4080 <data>
>          |1362: v jmp     0x1376 <thread+0x12d>
>
> These are not blockers if Namhyung / Arnaldo are okay with current design.
>
> Thanks,
> Ravi
>

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v6] perf tools c2c: Add annotation support to perf c2c report
  2025-10-09  4:28                           ` [PATCH v6] " Tianyou Li
@ 2025-10-10  5:56                             ` Ravi Bangoria
  2025-10-10  7:49                               ` Li, Tianyou
  0 siblings, 1 reply; 36+ messages in thread
From: Ravi Bangoria @ 2025-10-10  5:56 UTC (permalink / raw)
  To: Tianyou Li, Arnaldo Carvalho de Melo, Namhyung Kim
  Cc: Peter Zijlstra, Ingo Molnar, Mark Rutland, Alexander Shishkin,
	Jiri Olsa, Ian Rogers, Adrian Hunter, Kan Liang, wangyang.guo,
	pan.deng, zhiguo.zhou, jiebin.sun, thomas.falcon, dapeng1.mi,
	linux-perf-users, linux-kernel

On 09-Oct-25 9:58 AM, 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.
> 
> 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>

Works fine on AMD.

Tested-by: Ravi Bangoria <ravi.bangoria@amd.com>

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v6] perf tools c2c: Add annotation support to perf c2c report
  2025-10-10  5:56                             ` Ravi Bangoria
@ 2025-10-10  7:49                               ` Li, Tianyou
  0 siblings, 0 replies; 36+ messages in thread
From: Li, Tianyou @ 2025-10-10  7:49 UTC (permalink / raw)
  To: Ravi Bangoria, Arnaldo Carvalho de Melo, Namhyung Kim
  Cc: Peter Zijlstra, Ingo Molnar, Mark Rutland, Alexander Shishkin,
	Jiri Olsa, Ian Rogers, Adrian Hunter, Kan Liang, wangyang.guo,
	pan.deng, zhiguo.zhou, jiebin.sun, thomas.falcon, dapeng1.mi,
	linux-perf-users, linux-kernel

Thanks Ravi. I resent the patches and updated the 'Tested-by' tag. Very 
appreciated.


Regards,

Tianyou

On 10/10/2025 1:56 PM, Ravi Bangoria wrote:
> On 09-Oct-25 9:58 AM, 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.
>>
>> 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>
> Works fine on AMD.
>
> Tested-by: Ravi Bangoria <ravi.bangoria@amd.com>
>

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [PATCH v6 1/3] perf tools c2c: Add annotation support to perf c2c report
  2025-10-07  8:23                         ` Namhyung Kim
  2025-10-09  3:47                           ` Li, Tianyou
  2025-10-09  4:28                           ` [PATCH v6] " Tianyou Li
@ 2025-10-10  8:33                           ` Tianyou Li
  2025-10-10  8:33                           ` [PATCH v6 2/3] perf tools annotate: Fix a crash/hang when switch disassemble and source view Tianyou Li
                                             ` (4 subsequent siblings)
  7 siblings, 0 replies; 36+ messages in thread
From: Tianyou Li @ 2025-10-10  8:33 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim
  Cc: Mark Rutland, Alexander Shishkin, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Kan Liang, Ravi Bangoria, tianyou.li, wangyang.guo,
	pan.deng, zhiguo.zhou, jiebin.sun, thomas.falcon, dapeng1.mi,
	linux-perf-users, linux-kernel

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.

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/Documentation/perf-c2c.txt |   7 ++
 tools/perf/builtin-c2c.c              | 137 +++++++++++++++++++++++++-
 2 files changed, 139 insertions(+), 5 deletions(-)

diff --git a/tools/perf/Documentation/perf-c2c.txt b/tools/perf/Documentation/perf-c2c.txt
index f4af2dd6ab31..40b0f71a2c44 100644
--- a/tools/perf/Documentation/perf-c2c.txt
+++ b/tools/perf/Documentation/perf-c2c.txt
@@ -143,6 +143,13 @@ REPORT OPTIONS
 	feature, which causes cacheline sharing to behave like the cacheline
 	size is doubled.
 
+-M::
+--disassembler-style=::
+	Set disassembler style for objdump.
+
+--objdump=<path>::
+        Path to objdump binary.
+
 C2C RECORD
 ----------
 The perf c2c record command setup options related to HITM cacheline analysis
diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c
index 9e9ff471ddd1..878913115b45 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 || dim == &dim_iaddr)
+		hpp_list->sym = 1;
+
 	perf_hpp_list__register_sort_field(hpp_list, &c2c_fmt->fmt);
 	return 0;
 }
@@ -2549,7 +2563,56 @@ 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)
+{
+	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 annotated_source *src = NULL;
+	struct c2c_hist_entry *c2c_he = NULL;
+
+	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;
+	}
+
+	c2c_he = container_of(he, struct c2c_hist_entry, he);
+	return hist_entry__tui_annotate(he, c2c_he->evsel, NULL);
+}
+
 static void c2c_browser__update_nr_entries(struct hist_browser *hb)
 {
 	u64 nr_entries = 0;
@@ -2617,6 +2680,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 +2715,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 '?':
@@ -3006,6 +3073,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;
 	const struct option options[] = {
 	OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
 		   "file", "vmlinux pathname"),
@@ -3033,6 +3101,10 @@ 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_PARENT(c2c_options),
 	OPT_END()
 	};
@@ -3040,6 +3112,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 +3130,27 @@ 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 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;
+		}
+	}
+
 	err = symbol__validate_sym_arguments();
 	if (err)
 		goto out;
@@ -3126,6 +3225,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 +3266,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 +3336,7 @@ static int perf_c2c__report(int argc, const char **argv)
 out_session:
 	perf_session__delete(session);
 out:
+	annotation_options__exit();
 	return err;
 }
 
-- 
2.47.1


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v6 2/3] perf tools annotate: Fix a crash/hang when switch disassemble and source view
  2025-10-07  8:23                         ` Namhyung Kim
                                             ` (2 preceding siblings ...)
  2025-10-10  8:33                           ` [PATCH v6 1/3] " Tianyou Li
@ 2025-10-10  8:33                           ` Tianyou Li
  2025-10-10  8:33                           ` [PATCH v6 3/3] perf tools c2c: Highlight the contention line in the annotate browser Tianyou Li
                                             ` (3 subsequent siblings)
  7 siblings, 0 replies; 36+ messages in thread
From: Tianyou Li @ 2025-10-10  8:33 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim
  Cc: Mark Rutland, Alexander Shishkin, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Kan Liang, Ravi Bangoria, tianyou.li, wangyang.guo,
	pan.deng, zhiguo.zhou, jiebin.sun, thomas.falcon, dapeng1.mi,
	linux-perf-users, linux-kernel

When perf report with annotation, press 'TAB' to navigate among hot lines,
press 's' to switch on the source view, then press 'TAB' again, the perf
report could crash or hang. The 'nd' and 'browser->curr_hot' need to be
updated when switch the disassemble and source view.

Signed-off-by: Tianyou Li <tianyou.li@intel.com>
---
 tools/perf/ui/browsers/annotate.c | 35 ++++++++++++++++++++++++++++++-
 1 file changed, 34 insertions(+), 1 deletion(-)

diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c
index 8fe699f98542..32da310b3b62 100644
--- a/tools/perf/ui/browsers/annotate.c
+++ b/tools/perf/ui/browsers/annotate.c
@@ -852,6 +852,30 @@ static void annotate_browser__debuginfo_warning(struct annotate_browser *browser
 	}
 }
 
+static u32 rb_node__get_idx_asm(struct rb_node *nd)
+{
+	struct annotation_line *al = NULL;
+
+	if (nd == NULL)
+		return 0;
+
+	al = rb_entry(nd, struct annotation_line, rb_node);
+
+	return al->idx_asm;
+}
+
+static struct rb_node *annotate_browser__rb_node_by_idx_asm(struct annotate_browser *browser,
+									u32 idx_asm)
+{
+	struct annotation_line *al = NULL;
+
+	if (idx_asm == 0)
+		return NULL;
+
+	al = annotate_browser__find_new_asm_line(browser, idx_asm);
+	return al ? &al->rb_node : NULL;
+}
+
 static int annotate_browser__run(struct annotate_browser *browser,
 				 struct evsel *evsel,
 				 struct hist_browser_timer *hbt)
@@ -969,8 +993,17 @@ static int annotate_browser__run(struct annotate_browser *browser,
 			nd = browser->curr_hot;
 			break;
 		case 's':
-			if (annotate_browser__toggle_source(browser, evsel))
+			u32 idx_asm_nd = rb_node__get_idx_asm(nd);
+			u32 idx_asm_curr_hot = rb_node__get_idx_asm(browser->curr_hot);
+
+			if (annotate_browser__toggle_source(browser, evsel)) {
 				ui_helpline__puts(help);
+				/* Update the annotation browser's rb_tree, and reset the nd */
+				annotate_browser__calc_percent(browser, evsel);
+				nd = annotate_browser__rb_node_by_idx_asm(browser, idx_asm_nd);
+				browser->curr_hot = annotate_browser__rb_node_by_idx_asm(browser,
+							idx_asm_curr_hot);
+			}
 			annotate__scnprintf_title(hists, title, sizeof(title));
 			annotate_browser__show(browser, title, help);
 			continue;
-- 
2.47.1


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v6 3/3] perf tools c2c: Highlight the contention line in the annotate browser
  2025-10-07  8:23                         ` Namhyung Kim
                                             ` (3 preceding siblings ...)
  2025-10-10  8:33                           ` [PATCH v6 2/3] perf tools annotate: Fix a crash/hang when switch disassemble and source view Tianyou Li
@ 2025-10-10  8:33                           ` Tianyou Li
  2025-10-10  8:35                           ` [PATCH v6 1/3] perf tools c2c: Add annotation support to perf c2c report Tianyou Li
                                             ` (2 subsequent siblings)
  7 siblings, 0 replies; 36+ messages in thread
From: Tianyou Li @ 2025-10-10  8:33 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim
  Cc: Mark Rutland, Alexander Shishkin, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Kan Liang, Ravi Bangoria, tianyou.li, wangyang.guo,
	pan.deng, zhiguo.zhou, jiebin.sun, thomas.falcon, dapeng1.mi,
	linux-perf-users, linux-kernel

Add support to highlight the contention line in the annotate browser,
use 'TAB'/'UNTAB' to refocus to the contention line.

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          |  6 +++-
 tools/perf/ui/browsers/annotate.c | 52 ++++++++++++++++++++++++++++---
 tools/perf/ui/browsers/hists.c    |  2 +-
 tools/perf/util/hist.h            |  6 ++--
 5 files changed, 59 insertions(+), 9 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 878913115b45..7ee3c8a3f66c 100644
--- a/tools/perf/builtin-c2c.c
+++ b/tools/perf/builtin-c2c.c
@@ -2586,6 +2586,7 @@ static int perf_c2c__toggle_annotation(struct hist_browser *browser)
 	struct symbol *sym = NULL;
 	struct annotated_source *src = NULL;
 	struct c2c_hist_entry *c2c_he = NULL;
+	u64 al_addr = NO_INITIAL_AL_ADDR;
 
 	if (!perf_c2c__has_annotation(he->hists->hpp_list)) {
 		ui_browser__help_window(&browser->b, "No annotation support");
@@ -2609,8 +2610,11 @@ static int perf_c2c__toggle_annotation(struct hist_browser *browser)
 		return 0;
 	}
 
+	if (he->mem_info)
+		al_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);
+	return hist_entry__tui_annotate(he, c2c_he->evsel, NULL, al_addr);
 }
 
 static void c2c_browser__update_nr_entries(struct hist_browser *hb)
diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c
index 32da310b3b62..1de9bb88c379 100644
--- a/tools/perf/ui/browsers/annotate.c
+++ b/tools/perf/ui/browsers/annotate.c
@@ -300,6 +300,13 @@ static void disasm_rb_tree__insert(struct annotate_browser *browser,
 	rb_insert_color(&al->rb_node, root);
 }
 
+static void disasm_rb_tree__insert_if_empty(struct annotate_browser *browser,
+				struct annotation_line *al)
+{
+	if (rb_first(&browser->entries) == NULL)
+		disasm_rb_tree__insert(browser, al);
+}
+
 static void annotate_browser__set_top(struct annotate_browser *browser,
 				      struct annotation_line *pos, u32 idx)
 {
@@ -396,6 +403,22 @@ static struct annotation_line *annotate_browser__find_new_asm_line(
 	return NULL;
 }
 
+static struct annotation_line *annotate_browser__find_al_addr(struct annotate_browser *browser,
+						     u64 al_addr)
+{
+	struct symbol *sym = browser->he->ms.sym;
+	struct list_head *head = browser->b.entries;
+	struct annotation_line *al;
+
+	/* find an annotation line in the new list with the same al_addr */
+	list_for_each_entry(al, head, node) {
+		if (sym->start + al->offset == al_addr)
+			return al;
+	}
+	/* There are no asm lines */
+	return NULL;
+}
+
 static struct annotation_line *annotate_browser__find_next_asm_line(
 					struct annotate_browser *browser,
 					struct annotation_line *al)
@@ -605,7 +628,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
@@ -897,6 +920,12 @@ static int annotate_browser__run(struct annotate_browser *browser,
 
 	annotate_browser__calc_percent(browser, evsel);
 
+	if (browser->curr_hot == NULL && browser->selection != NULL) {
+		disasm_rb_tree__insert_if_empty(browser, browser->selection);
+		browser->curr_hot = rb_first(&browser->entries);
+		browser->b.use_navkeypressed = false;
+	}
+
 	if (browser->curr_hot) {
 		annotate_browser__set_rb_top(browser, browser->curr_hot);
 		browser->b.navkeypressed = false;
@@ -1003,6 +1032,8 @@ static int annotate_browser__run(struct annotate_browser *browser,
 				nd = annotate_browser__rb_node_by_idx_asm(browser, idx_asm_nd);
 				browser->curr_hot = annotate_browser__rb_node_by_idx_asm(browser,
 							idx_asm_curr_hot);
+				disasm_rb_tree__insert_if_empty(browser,
+					rb_entry(nd, struct annotation_line, rb_node));
 			}
 			annotate__scnprintf_title(hists, title, sizeof(title));
 			annotate_browser__show(browser, title, help);
@@ -1139,19 +1170,19 @@ 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 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, 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 al_addr)
 {
 	struct symbol *sym = ms->sym;
 	struct annotation *notes = symbol__annotation(sym);
@@ -1221,6 +1252,19 @@ int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms,
 	if (annotate_opts.hide_src_code)
 		ui_browser__init_asm_mode(&browser.b);
 
+	/*
+	 * If 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 (al_addr != NO_INITIAL_AL_ADDR) {
+		struct annotation_line *al = annotate_browser__find_al_addr(&browser, al_addr);
+
+		browser.selection = al;
+	}
+
 	ret = annotate_browser__run(&browser, evsel, hbt);
 
 	debuginfo__delete(browser.dbg);
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/hist.h b/tools/perf/util/hist.h
index c64005278687..9542cf43bd2a 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 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 ad_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


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v6 1/3] perf tools c2c: Add annotation support to perf c2c report
  2025-10-07  8:23                         ` Namhyung Kim
                                             ` (4 preceding siblings ...)
  2025-10-10  8:33                           ` [PATCH v6 3/3] perf tools c2c: Highlight the contention line in the annotate browser Tianyou Li
@ 2025-10-10  8:35                           ` Tianyou Li
  2025-10-10  8:35                           ` [PATCH v6 2/3] perf tools annotate: Fix a crash/hang when switch disassemble and source view Tianyou Li
  2025-10-10  8:35                           ` [PATCH v6 3/3] perf tools c2c: Highlight the contention line in the annotate browser Tianyou Li
  7 siblings, 0 replies; 36+ messages in thread
From: Tianyou Li @ 2025-10-10  8:35 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim
  Cc: Mark Rutland, Alexander Shishkin, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Kan Liang, Ravi Bangoria, tianyou.li, wangyang.guo,
	pan.deng, zhiguo.zhou, jiebin.sun, thomas.falcon, dapeng1.mi,
	linux-perf-users, linux-kernel

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.

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>
Tested-by: Ravi Bangoria <ravi.bangoria@amd.com>
---
 tools/perf/Documentation/perf-c2c.txt |   7 ++
 tools/perf/builtin-c2c.c              | 137 +++++++++++++++++++++++++-
 2 files changed, 139 insertions(+), 5 deletions(-)

diff --git a/tools/perf/Documentation/perf-c2c.txt b/tools/perf/Documentation/perf-c2c.txt
index f4af2dd6ab31..40b0f71a2c44 100644
--- a/tools/perf/Documentation/perf-c2c.txt
+++ b/tools/perf/Documentation/perf-c2c.txt
@@ -143,6 +143,13 @@ REPORT OPTIONS
 	feature, which causes cacheline sharing to behave like the cacheline
 	size is doubled.
 
+-M::
+--disassembler-style=::
+	Set disassembler style for objdump.
+
+--objdump=<path>::
+        Path to objdump binary.
+
 C2C RECORD
 ----------
 The perf c2c record command setup options related to HITM cacheline analysis
diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c
index 9e9ff471ddd1..878913115b45 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 || dim == &dim_iaddr)
+		hpp_list->sym = 1;
+
 	perf_hpp_list__register_sort_field(hpp_list, &c2c_fmt->fmt);
 	return 0;
 }
@@ -2549,7 +2563,56 @@ 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)
+{
+	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 annotated_source *src = NULL;
+	struct c2c_hist_entry *c2c_he = NULL;
+
+	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;
+	}
+
+	c2c_he = container_of(he, struct c2c_hist_entry, he);
+	return hist_entry__tui_annotate(he, c2c_he->evsel, NULL);
+}
+
 static void c2c_browser__update_nr_entries(struct hist_browser *hb)
 {
 	u64 nr_entries = 0;
@@ -2617,6 +2680,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 +2715,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 '?':
@@ -3006,6 +3073,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;
 	const struct option options[] = {
 	OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
 		   "file", "vmlinux pathname"),
@@ -3033,6 +3101,10 @@ 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_PARENT(c2c_options),
 	OPT_END()
 	};
@@ -3040,6 +3112,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 +3130,27 @@ 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 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;
+		}
+	}
+
 	err = symbol__validate_sym_arguments();
 	if (err)
 		goto out;
@@ -3126,6 +3225,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 +3266,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 +3336,7 @@ static int perf_c2c__report(int argc, const char **argv)
 out_session:
 	perf_session__delete(session);
 out:
+	annotation_options__exit();
 	return err;
 }
 
-- 
2.47.1


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v6 2/3] perf tools annotate: Fix a crash/hang when switch disassemble and source view
  2025-10-07  8:23                         ` Namhyung Kim
                                             ` (5 preceding siblings ...)
  2025-10-10  8:35                           ` [PATCH v6 1/3] perf tools c2c: Add annotation support to perf c2c report Tianyou Li
@ 2025-10-10  8:35                           ` Tianyou Li
  2025-10-10  8:35                           ` [PATCH v6 3/3] perf tools c2c: Highlight the contention line in the annotate browser Tianyou Li
  7 siblings, 0 replies; 36+ messages in thread
From: Tianyou Li @ 2025-10-10  8:35 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim
  Cc: Mark Rutland, Alexander Shishkin, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Kan Liang, Ravi Bangoria, tianyou.li, wangyang.guo,
	pan.deng, zhiguo.zhou, jiebin.sun, thomas.falcon, dapeng1.mi,
	linux-perf-users, linux-kernel

When perf report with annotation, press 'TAB' to navigate among hot lines,
press 's' to switch on the source view, then press 'TAB' again, the perf
report could crash or hang. The 'nd' and 'browser->curr_hot' need to be
updated when switch the disassemble and source view.

Signed-off-by: Tianyou Li <tianyou.li@intel.com>
---
 tools/perf/ui/browsers/annotate.c | 35 ++++++++++++++++++++++++++++++-
 1 file changed, 34 insertions(+), 1 deletion(-)

diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c
index 8fe699f98542..32da310b3b62 100644
--- a/tools/perf/ui/browsers/annotate.c
+++ b/tools/perf/ui/browsers/annotate.c
@@ -852,6 +852,30 @@ static void annotate_browser__debuginfo_warning(struct annotate_browser *browser
 	}
 }
 
+static u32 rb_node__get_idx_asm(struct rb_node *nd)
+{
+	struct annotation_line *al = NULL;
+
+	if (nd == NULL)
+		return 0;
+
+	al = rb_entry(nd, struct annotation_line, rb_node);
+
+	return al->idx_asm;
+}
+
+static struct rb_node *annotate_browser__rb_node_by_idx_asm(struct annotate_browser *browser,
+									u32 idx_asm)
+{
+	struct annotation_line *al = NULL;
+
+	if (idx_asm == 0)
+		return NULL;
+
+	al = annotate_browser__find_new_asm_line(browser, idx_asm);
+	return al ? &al->rb_node : NULL;
+}
+
 static int annotate_browser__run(struct annotate_browser *browser,
 				 struct evsel *evsel,
 				 struct hist_browser_timer *hbt)
@@ -969,8 +993,17 @@ static int annotate_browser__run(struct annotate_browser *browser,
 			nd = browser->curr_hot;
 			break;
 		case 's':
-			if (annotate_browser__toggle_source(browser, evsel))
+			u32 idx_asm_nd = rb_node__get_idx_asm(nd);
+			u32 idx_asm_curr_hot = rb_node__get_idx_asm(browser->curr_hot);
+
+			if (annotate_browser__toggle_source(browser, evsel)) {
 				ui_helpline__puts(help);
+				/* Update the annotation browser's rb_tree, and reset the nd */
+				annotate_browser__calc_percent(browser, evsel);
+				nd = annotate_browser__rb_node_by_idx_asm(browser, idx_asm_nd);
+				browser->curr_hot = annotate_browser__rb_node_by_idx_asm(browser,
+							idx_asm_curr_hot);
+			}
 			annotate__scnprintf_title(hists, title, sizeof(title));
 			annotate_browser__show(browser, title, help);
 			continue;
-- 
2.47.1


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v6 3/3] perf tools c2c: Highlight the contention line in the annotate browser
  2025-10-07  8:23                         ` Namhyung Kim
                                             ` (6 preceding siblings ...)
  2025-10-10  8:35                           ` [PATCH v6 2/3] perf tools annotate: Fix a crash/hang when switch disassemble and source view Tianyou Li
@ 2025-10-10  8:35                           ` Tianyou Li
  2025-10-10 13:08                             ` Namhyung Kim
  7 siblings, 1 reply; 36+ messages in thread
From: Tianyou Li @ 2025-10-10  8:35 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim
  Cc: Mark Rutland, Alexander Shishkin, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Kan Liang, Ravi Bangoria, tianyou.li, wangyang.guo,
	pan.deng, zhiguo.zhou, jiebin.sun, thomas.falcon, dapeng1.mi,
	linux-perf-users, linux-kernel

Add support to highlight the contention line in the annotate browser,
use 'TAB'/'UNTAB' to refocus to the contention line.

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>
Tested-by: Ravi Bangoria <ravi.bangoria@amd.com>
---
 tools/perf/builtin-annotate.c     |  2 +-
 tools/perf/builtin-c2c.c          |  6 +++-
 tools/perf/ui/browsers/annotate.c | 52 ++++++++++++++++++++++++++++---
 tools/perf/ui/browsers/hists.c    |  2 +-
 tools/perf/util/hist.h            |  6 ++--
 5 files changed, 59 insertions(+), 9 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 878913115b45..7ee3c8a3f66c 100644
--- a/tools/perf/builtin-c2c.c
+++ b/tools/perf/builtin-c2c.c
@@ -2586,6 +2586,7 @@ static int perf_c2c__toggle_annotation(struct hist_browser *browser)
 	struct symbol *sym = NULL;
 	struct annotated_source *src = NULL;
 	struct c2c_hist_entry *c2c_he = NULL;
+	u64 al_addr = NO_INITIAL_AL_ADDR;
 
 	if (!perf_c2c__has_annotation(he->hists->hpp_list)) {
 		ui_browser__help_window(&browser->b, "No annotation support");
@@ -2609,8 +2610,11 @@ static int perf_c2c__toggle_annotation(struct hist_browser *browser)
 		return 0;
 	}
 
+	if (he->mem_info)
+		al_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);
+	return hist_entry__tui_annotate(he, c2c_he->evsel, NULL, al_addr);
 }
 
 static void c2c_browser__update_nr_entries(struct hist_browser *hb)
diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c
index 32da310b3b62..1de9bb88c379 100644
--- a/tools/perf/ui/browsers/annotate.c
+++ b/tools/perf/ui/browsers/annotate.c
@@ -300,6 +300,13 @@ static void disasm_rb_tree__insert(struct annotate_browser *browser,
 	rb_insert_color(&al->rb_node, root);
 }
 
+static void disasm_rb_tree__insert_if_empty(struct annotate_browser *browser,
+				struct annotation_line *al)
+{
+	if (rb_first(&browser->entries) == NULL)
+		disasm_rb_tree__insert(browser, al);
+}
+
 static void annotate_browser__set_top(struct annotate_browser *browser,
 				      struct annotation_line *pos, u32 idx)
 {
@@ -396,6 +403,22 @@ static struct annotation_line *annotate_browser__find_new_asm_line(
 	return NULL;
 }
 
+static struct annotation_line *annotate_browser__find_al_addr(struct annotate_browser *browser,
+						     u64 al_addr)
+{
+	struct symbol *sym = browser->he->ms.sym;
+	struct list_head *head = browser->b.entries;
+	struct annotation_line *al;
+
+	/* find an annotation line in the new list with the same al_addr */
+	list_for_each_entry(al, head, node) {
+		if (sym->start + al->offset == al_addr)
+			return al;
+	}
+	/* There are no asm lines */
+	return NULL;
+}
+
 static struct annotation_line *annotate_browser__find_next_asm_line(
 					struct annotate_browser *browser,
 					struct annotation_line *al)
@@ -605,7 +628,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
@@ -897,6 +920,12 @@ static int annotate_browser__run(struct annotate_browser *browser,
 
 	annotate_browser__calc_percent(browser, evsel);
 
+	if (browser->curr_hot == NULL && browser->selection != NULL) {
+		disasm_rb_tree__insert_if_empty(browser, browser->selection);
+		browser->curr_hot = rb_first(&browser->entries);
+		browser->b.use_navkeypressed = false;
+	}
+
 	if (browser->curr_hot) {
 		annotate_browser__set_rb_top(browser, browser->curr_hot);
 		browser->b.navkeypressed = false;
@@ -1003,6 +1032,8 @@ static int annotate_browser__run(struct annotate_browser *browser,
 				nd = annotate_browser__rb_node_by_idx_asm(browser, idx_asm_nd);
 				browser->curr_hot = annotate_browser__rb_node_by_idx_asm(browser,
 							idx_asm_curr_hot);
+				disasm_rb_tree__insert_if_empty(browser,
+					rb_entry(nd, struct annotation_line, rb_node));
 			}
 			annotate__scnprintf_title(hists, title, sizeof(title));
 			annotate_browser__show(browser, title, help);
@@ -1139,19 +1170,19 @@ 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 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, 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 al_addr)
 {
 	struct symbol *sym = ms->sym;
 	struct annotation *notes = symbol__annotation(sym);
@@ -1221,6 +1252,19 @@ int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms,
 	if (annotate_opts.hide_src_code)
 		ui_browser__init_asm_mode(&browser.b);
 
+	/*
+	 * If 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 (al_addr != NO_INITIAL_AL_ADDR) {
+		struct annotation_line *al = annotate_browser__find_al_addr(&browser, al_addr);
+
+		browser.selection = al;
+	}
+
 	ret = annotate_browser__run(&browser, evsel, hbt);
 
 	debuginfo__delete(browser.dbg);
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/hist.h b/tools/perf/util/hist.h
index c64005278687..9542cf43bd2a 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 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 ad_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


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* Re: [PATCH v6 3/3] perf tools c2c: Highlight the contention line in the annotate browser
  2025-10-10  8:35                           ` [PATCH v6 3/3] perf tools c2c: Highlight the contention line in the annotate browser Tianyou Li
@ 2025-10-10 13:08                             ` Namhyung Kim
  2025-10-11  8:16                               ` [PATCH v7 1/2] perf tools c2c: Add annotation support to perf c2c report Tianyou Li
  2025-10-11  8:16                               ` [PATCH v7 " Tianyou Li
  0 siblings, 2 replies; 36+ messages in thread
From: Namhyung Kim @ 2025-10-10 13:08 UTC (permalink / raw)
  To: Tianyou Li
  Cc: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Mark Rutland, Alexander Shishkin, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Kan Liang, Ravi Bangoria, wangyang.guo, pan.deng,
	zhiguo.zhou, jiebin.sun, thomas.falcon, dapeng1.mi,
	linux-perf-users, linux-kernel

Hello,

On Fri, Oct 10, 2025 at 04:35:49PM +0800, Tianyou Li wrote:
> Add support to highlight the contention line in the annotate browser,
> use 'TAB'/'UNTAB' to refocus to the contention line.
> 
> 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>
> Tested-by: Ravi Bangoria <ravi.bangoria@amd.com>
> ---
>  tools/perf/builtin-annotate.c     |  2 +-
>  tools/perf/builtin-c2c.c          |  6 +++-
>  tools/perf/ui/browsers/annotate.c | 52 ++++++++++++++++++++++++++++---
>  tools/perf/ui/browsers/hists.c    |  2 +-
>  tools/perf/util/hist.h            |  6 ++--
>  5 files changed, 59 insertions(+), 9 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);

I think it's too long.  How about NO_ADDR or ZERO_ADDR?

>  
>  			switch (key) {
>  			case -1:
> diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c
> index 878913115b45..7ee3c8a3f66c 100644
> --- a/tools/perf/builtin-c2c.c
> +++ b/tools/perf/builtin-c2c.c
> @@ -2586,6 +2586,7 @@ static int perf_c2c__toggle_annotation(struct hist_browser *browser)
>  	struct symbol *sym = NULL;
>  	struct annotated_source *src = NULL;
>  	struct c2c_hist_entry *c2c_he = NULL;
> +	u64 al_addr = NO_INITIAL_AL_ADDR;
>  
>  	if (!perf_c2c__has_annotation(he->hists->hpp_list)) {
>  		ui_browser__help_window(&browser->b, "No annotation support");
> @@ -2609,8 +2610,11 @@ static int perf_c2c__toggle_annotation(struct hist_browser *browser)
>  		return 0;
>  	}
>  
> +	if (he->mem_info)
> +		al_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);
> +	return hist_entry__tui_annotate(he, c2c_he->evsel, NULL, al_addr);

I think it's better to add sample info to annotation like Ravi said.
How about adding this? (not tested)

--- a/tools/perf/builtin-c2c.c
+++ b/tools/perf/builtin-c2c.c
@@ -336,6 +336,8 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused,
        c2c_he__set_node(c2c_he, sample);
 
        hists__inc_nr_samples(&c2c_hists->hists, he->filtered);
+       if (perf_c2c__has_annotation(c2c_hists->hists.hpp_list))
+               addr_map_symbol__inc_samples(mem_info__iaddr(mi), sample, evsel);
        ret = hist_entry__append_callchain(he, sample);
 
        if (!ret) {


>  }
>  
>  static void c2c_browser__update_nr_entries(struct hist_browser *hb)
> diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c
> index 32da310b3b62..1de9bb88c379 100644
> --- a/tools/perf/ui/browsers/annotate.c
> +++ b/tools/perf/ui/browsers/annotate.c
> @@ -300,6 +300,13 @@ static void disasm_rb_tree__insert(struct annotate_browser *browser,
>  	rb_insert_color(&al->rb_node, root);
>  }
>  
> +static void disasm_rb_tree__insert_if_empty(struct annotate_browser *browser,
> +				struct annotation_line *al)
> +{
> +	if (rb_first(&browser->entries) == NULL)
> +		disasm_rb_tree__insert(browser, al);
> +}
> +
>  static void annotate_browser__set_top(struct annotate_browser *browser,
>  				      struct annotation_line *pos, u32 idx)
>  {
> @@ -396,6 +403,22 @@ static struct annotation_line *annotate_browser__find_new_asm_line(
>  	return NULL;
>  }
>  
> +static struct annotation_line *annotate_browser__find_al_addr(struct annotate_browser *browser,
> +						     u64 al_addr)

We have annotated_source__get_line().


> +{
> +	struct symbol *sym = browser->he->ms.sym;
> +	struct list_head *head = browser->b.entries;
> +	struct annotation_line *al;
> +
> +	/* find an annotation line in the new list with the same al_addr */
> +	list_for_each_entry(al, head, node) {
> +		if (sym->start + al->offset == al_addr)
> +			return al;
> +	}
> +	/* There are no asm lines */
> +	return NULL;
> +}
> +
>  static struct annotation_line *annotate_browser__find_next_asm_line(
>  					struct annotate_browser *browser,
>  					struct annotation_line *al)
> @@ -605,7 +628,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
> @@ -897,6 +920,12 @@ static int annotate_browser__run(struct annotate_browser *browser,
>  
>  	annotate_browser__calc_percent(browser, evsel);
>  
> +	if (browser->curr_hot == NULL && browser->selection != NULL) {
> +		disasm_rb_tree__insert_if_empty(browser, browser->selection);
> +		browser->curr_hot = rb_first(&browser->entries);
> +		browser->b.use_navkeypressed = false;
> +	}

Then it can be like this.

	if (browser->selection != NULL)
		browser->curr_hot = &selection->rb_node;

> +
>  	if (browser->curr_hot) {
>  		annotate_browser__set_rb_top(browser, browser->curr_hot);
>  		browser->b.navkeypressed = false;
> @@ -1003,6 +1032,8 @@ static int annotate_browser__run(struct annotate_browser *browser,
>  				nd = annotate_browser__rb_node_by_idx_asm(browser, idx_asm_nd);
>  				browser->curr_hot = annotate_browser__rb_node_by_idx_asm(browser,
>  							idx_asm_curr_hot);
> +				disasm_rb_tree__insert_if_empty(browser,
> +					rb_entry(nd, struct annotation_line, rb_node));

I feel like it should call annotate_browser__calc_percent() after
annotate_browser__toggle_source().  Then just updating curr_hot would
work.

Thanks,
Namhyung


>  			}
>  			annotate__scnprintf_title(hists, title, sizeof(title));
>  			annotate_browser__show(browser, title, help);
> @@ -1139,19 +1170,19 @@ 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 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, 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 al_addr)
>  {
>  	struct symbol *sym = ms->sym;
>  	struct annotation *notes = symbol__annotation(sym);
> @@ -1221,6 +1252,19 @@ int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms,
>  	if (annotate_opts.hide_src_code)
>  		ui_browser__init_asm_mode(&browser.b);
>  
> +	/*
> +	 * If 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 (al_addr != NO_INITIAL_AL_ADDR) {
> +		struct annotation_line *al = annotate_browser__find_al_addr(&browser, al_addr);
> +
> +		browser.selection = al;
> +	}
> +
>  	ret = annotate_browser__run(&browser, evsel, hbt);
>  
>  	debuginfo__delete(browser.dbg);
> 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/hist.h b/tools/perf/util/hist.h
> index c64005278687..9542cf43bd2a 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 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 ad_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
> 

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [PATCH v7 1/2] perf tools c2c: Add annotation support to perf c2c report
  2025-10-10 13:08                             ` Namhyung Kim
@ 2025-10-11  8:16                               ` Tianyou Li
  2025-10-13  8:52                                 ` Ravi Bangoria
  2025-10-11  8:16                               ` [PATCH v7 " Tianyou Li
  1 sibling, 1 reply; 36+ messages in thread
From: Tianyou Li @ 2025-10-11  8:16 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim
  Cc: Mark Rutland, Alexander Shishkin, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Kan Liang, Ravi Bangoria, tianyou.li, wangyang.guo,
	pan.deng, zhiguo.zhou, jiebin.sun, thomas.falcon, dapeng1.mi,
	linux-perf-users, linux-kernel

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.

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>
Tested-by: Ravi Bangoria <ravi.bangoria@amd.com>
---
 tools/perf/Documentation/perf-c2c.txt |   7 ++
 tools/perf/builtin-c2c.c              | 139 +++++++++++++++++++++++++-
 2 files changed, 141 insertions(+), 5 deletions(-)

diff --git a/tools/perf/Documentation/perf-c2c.txt b/tools/perf/Documentation/perf-c2c.txt
index f4af2dd6ab31..40b0f71a2c44 100644
--- a/tools/perf/Documentation/perf-c2c.txt
+++ b/tools/perf/Documentation/perf-c2c.txt
@@ -143,6 +143,13 @@ REPORT OPTIONS
 	feature, which causes cacheline sharing to behave like the cacheline
 	size is doubled.
 
+-M::
+--disassembler-style=::
+	Set disassembler style for objdump.
+
+--objdump=<path>::
+        Path to objdump binary.
+
 C2C RECORD
 ----------
 The perf c2c record command setup options related to HITM cacheline analysis
diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c
index 9e9ff471ddd1..e4841cce55e6 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)
 {
@@ -275,6 +284,21 @@ static void compute_stats(struct c2c_hist_entry *c2c_he,
 		update_stats(&cstats->load, weight);
 }
 
+/*
+ * 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)
+{
+	if (use_browser != 1)
+		return false;
+	return !list || list->sym;
+}
+
 static int process_sample_event(const struct perf_tool *tool __maybe_unused,
 				union perf_event *event,
 				struct perf_sample *sample,
@@ -334,8 +358,11 @@ 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);
+	if (perf_c2c__has_annotation(NULL))
+		addr_map_symbol__inc_samples(mem_info__iaddr(mi), sample, evsel);
 	ret = hist_entry__append_callchain(he, sample);
 
 	if (!ret) {
@@ -371,6 +398,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 +2025,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 || dim == &dim_iaddr)
+		hpp_list->sym = 1;
+
 	perf_hpp_list__register_sort_field(hpp_list, &c2c_fmt->fmt);
 	return 0;
 }
@@ -2550,6 +2581,40 @@ 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 annotated_source *src = NULL;
+	struct c2c_hist_entry *c2c_he = NULL;
+
+	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;
+	}
+
+	c2c_he = container_of(he, struct c2c_hist_entry, he);
+	return hist_entry__tui_annotate(he, c2c_he->evsel, NULL);
+}
+
 static void c2c_browser__update_nr_entries(struct hist_browser *hb)
 {
 	u64 nr_entries = 0;
@@ -2617,6 +2682,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 +2717,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 '?':
@@ -3006,6 +3075,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;
 	const struct option options[] = {
 	OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
 		   "file", "vmlinux pathname"),
@@ -3033,6 +3103,10 @@ 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_PARENT(c2c_options),
 	OPT_END()
 	};
@@ -3040,6 +3114,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 +3132,27 @@ 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 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;
+		}
+	}
+
 	err = symbol__validate_sym_arguments();
 	if (err)
 		goto out;
@@ -3126,6 +3227,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 +3268,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 +3338,7 @@ static int perf_c2c__report(int argc, const char **argv)
 out_session:
 	perf_session__delete(session);
 out:
+	annotation_options__exit();
 	return err;
 }
 
-- 
2.47.1


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v7 2/2] perf tools c2c: Highlight the contention line in the annotate browser
  2025-10-10 13:08                             ` Namhyung Kim
  2025-10-11  8:16                               ` [PATCH v7 1/2] perf tools c2c: Add annotation support to perf c2c report Tianyou Li
@ 2025-10-11  8:16                               ` Tianyou Li
  1 sibling, 0 replies; 36+ messages in thread
From: Tianyou Li @ 2025-10-11  8:16 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim
  Cc: Mark Rutland, Alexander Shishkin, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Kan Liang, Ravi Bangoria, tianyou.li, wangyang.guo,
	pan.deng, zhiguo.zhou, jiebin.sun, thomas.falcon, dapeng1.mi,
	linux-perf-users, linux-kernel

Add support to highlight the contention line in the annotate browser,
use 'TAB'/'UNTAB' to refocus to the contention line.

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>
Tested-by: Ravi Bangoria <ravi.bangoria@amd.com>
---
 tools/perf/builtin-annotate.c     |  2 +-
 tools/perf/builtin-c2c.c          |  6 +++-
 tools/perf/ui/browsers/annotate.c | 48 ++++++++++++++++++++++++++++---
 tools/perf/ui/browsers/hists.c    |  2 +-
 tools/perf/util/hist.h            |  6 ++--
 5 files changed, 55 insertions(+), 9 deletions(-)

diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 646f43b0f7c4..112b15952016 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_ADDR);
 
 			switch (key) {
 			case -1:
diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c
index e4841cce55e6..1946c3409e9f 100644
--- a/tools/perf/builtin-c2c.c
+++ b/tools/perf/builtin-c2c.c
@@ -2588,6 +2588,7 @@ static int perf_c2c__toggle_annotation(struct hist_browser *browser)
 	struct symbol *sym = NULL;
 	struct annotated_source *src = NULL;
 	struct c2c_hist_entry *c2c_he = NULL;
+	u64 al_addr = NO_ADDR;
 
 	if (!perf_c2c__has_annotation(he->hists->hpp_list)) {
 		ui_browser__help_window(&browser->b, "No annotation support");
@@ -2611,8 +2612,11 @@ static int perf_c2c__toggle_annotation(struct hist_browser *browser)
 		return 0;
 	}
 
+	if (he->mem_info)
+		al_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);
+	return hist_entry__tui_annotate(he, c2c_he->evsel, NULL, al_addr);
 }
 
 static void c2c_browser__update_nr_entries(struct hist_browser *hb)
diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c
index 8fe699f98542..112fe6ad112e 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_ADDR);
 
 	/*
 	 * The annotate_browser above changed the title with the target function
@@ -852,6 +852,16 @@ static void annotate_browser__debuginfo_warning(struct annotate_browser *browser
 	}
 }
 
+static s64 annotate_browser__curr_hot_offset(struct annotate_browser *browser)
+{
+	struct annotation_line *al = NULL;
+
+	if (browser->curr_hot)
+		al = rb_entry(browser->curr_hot, struct annotation_line, rb_node);
+
+	return al ? al->offset : 0;
+}
+
 static int annotate_browser__run(struct annotate_browser *browser,
 				 struct evsel *evsel,
 				 struct hist_browser_timer *hbt)
@@ -873,6 +883,11 @@ static int annotate_browser__run(struct annotate_browser *browser,
 
 	annotate_browser__calc_percent(browser, evsel);
 
+	if (browser->selection != NULL) {
+		browser->curr_hot = &browser->selection->rb_node;
+		browser->b.use_navkeypressed = false;
+	}
+
 	if (browser->curr_hot) {
 		annotate_browser__set_rb_top(browser, browser->curr_hot);
 		browser->b.navkeypressed = false;
@@ -969,8 +984,19 @@ static int annotate_browser__run(struct annotate_browser *browser,
 			nd = browser->curr_hot;
 			break;
 		case 's':
+			struct annotation_line *al = NULL;
+			s64 offset = annotate_browser__curr_hot_offset(browser);
+
 			if (annotate_browser__toggle_source(browser, evsel))
 				ui_helpline__puts(help);
+
+			/* Update the annotation browser's rb_tree, and reset the nd */
+			annotate_browser__calc_percent(browser, evsel);
+			/* Try to find the same asm line as before */
+			al = annotated_source__get_line(notes->src, offset);
+			browser->curr_hot = al ? &al->rb_node : NULL;
+			nd = browser->curr_hot;
+
 			annotate__scnprintf_title(hists, title, sizeof(title));
 			annotate_browser__show(browser, title, help);
 			continue;
@@ -1106,19 +1132,19 @@ 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 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, 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 al_addr)
 {
 	struct symbol *sym = ms->sym;
 	struct annotation *notes = symbol__annotation(sym);
@@ -1188,6 +1214,20 @@ int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms,
 	if (annotate_opts.hide_src_code)
 		ui_browser__init_asm_mode(&browser.b);
 
+	/*
+	 * If 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 (al_addr != NO_ADDR) {
+		struct annotation_line *al = annotated_source__get_line(notes->src,
+			al_addr - sym->start);
+
+		browser.selection = al;
+	}
+
 	ret = annotate_browser__run(&browser, evsel, hbt);
 
 	debuginfo__delete(browser.dbg);
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c
index 487c0b08c003..08fecbe28a52 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_ADDR);
 	/*
 	 * offer option to annotate the other branch source or target
 	 * (if they exists) when returning from annotate
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
index c64005278687..6795816eee85 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_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 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 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


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* Re: [PATCH v7 1/2] perf tools c2c: Add annotation support to perf c2c report
  2025-10-11  8:16                               ` [PATCH v7 1/2] perf tools c2c: Add annotation support to perf c2c report Tianyou Li
@ 2025-10-13  8:52                                 ` Ravi Bangoria
  2025-10-13 13:43                                   ` Li, Tianyou
                                                     ` (2 more replies)
  0 siblings, 3 replies; 36+ messages in thread
From: Ravi Bangoria @ 2025-10-13  8:52 UTC (permalink / raw)
  To: Tianyou Li, Namhyung Kim, Arnaldo Carvalho de Melo
  Cc: Peter Zijlstra, Ingo Molnar, Mark Rutland, Alexander Shishkin,
	Jiri Olsa, Ian Rogers, Adrian Hunter, Kan Liang, wangyang.guo,
	pan.deng, zhiguo.zhou, jiebin.sun, thomas.falcon, dapeng1.mi,
	linux-perf-users, linux-kernel, Ravi Bangoria

On 11-Oct-25 1:46 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.

I still see annotate browser title with samples/event count as 0.
Something like below (lightly tested) should fix it. Can you please
check?


--- a/tools/perf/builtin-c2c.c
+++ b/tools/perf/builtin-c2c.c
@@ -299,6 +299,18 @@ static bool perf_c2c__has_annotation(struct perf_hpp_list *list)
 	return !list || list->sym;
 }
 
+static void perf_c2c__evsel_hists_inc_stats(struct evsel *evsel,
+					    struct hist_entry *he,
+					    struct perf_sample *sample)
+{
+	struct hists *evsel_hists = evsel__hists(evsel);
+
+	hists__inc_nr_samples(evsel_hists, he->filtered);
+	evsel_hists->stats.total_period += sample->period;
+	if (!he->filtered)
+		evsel_hists->stats.total_non_filtered_period += sample->period;
+}
+
 static int process_sample_event(const struct perf_tool *tool __maybe_unused,
 				union perf_event *event,
 				struct perf_sample *sample,
@@ -363,6 +375,9 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused,
 	hists__inc_nr_samples(&c2c_hists->hists, he->filtered);
 	if (perf_c2c__has_annotation(NULL))
 		addr_map_symbol__inc_samples(mem_info__iaddr(mi), sample, evsel);
+
+	perf_c2c__evsel_hists_inc_stats(evsel, he, sample);
+
 	ret = hist_entry__append_callchain(he, sample);
 
 	if (!ret) {

Thanks,
Ravi

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v7 1/2] perf tools c2c: Add annotation support to perf c2c report
  2025-10-13  8:52                                 ` Ravi Bangoria
@ 2025-10-13 13:43                                   ` Li, Tianyou
  2025-10-13 14:48                                   ` [PATCH v8 " Tianyou Li
  2025-10-13 14:48                                   ` [PATCH v8 2/2] perf tools c2c: Highlight the contention line in the annotate browser Tianyou Li
  2 siblings, 0 replies; 36+ messages in thread
From: Li, Tianyou @ 2025-10-13 13:43 UTC (permalink / raw)
  To: Ravi Bangoria, Namhyung Kim, Arnaldo Carvalho de Melo
  Cc: Peter Zijlstra, Ingo Molnar, Mark Rutland, Alexander Shishkin,
	Jiri Olsa, Ian Rogers, Adrian Hunter, Kan Liang, wangyang.guo,
	pan.deng, zhiguo.zhou, jiebin.sun, thomas.falcon, dapeng1.mi,
	linux-perf-users, linux-kernel

Hi Ravi,

Appreciated for your testing and the code. It works like magic. Learnt.

I compared the sample count, percentage and period by 't' in annotate 
browser, all numbers in perf c2c report aligned with perf report.

I am curious if we should update the evsel_hists->stats when 
perf_c2c__has_annotation return true. I will send out patch v8 soon.

Regards,

Tianyou

On 10/13/2025 4:52 PM, Ravi Bangoria wrote:
> On 11-Oct-25 1:46 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.
> I still see annotate browser title with samples/event count as 0.
> Something like below (lightly tested) should fix it. Can you please
> check?
>
>
> --- a/tools/perf/builtin-c2c.c
> +++ b/tools/perf/builtin-c2c.c
> @@ -299,6 +299,18 @@ static bool perf_c2c__has_annotation(struct perf_hpp_list *list)
>   	return !list || list->sym;
>   }
>   
> +static void perf_c2c__evsel_hists_inc_stats(struct evsel *evsel,
> +					    struct hist_entry *he,
> +					    struct perf_sample *sample)
> +{
> +	struct hists *evsel_hists = evsel__hists(evsel);
> +
> +	hists__inc_nr_samples(evsel_hists, he->filtered);
> +	evsel_hists->stats.total_period += sample->period;
> +	if (!he->filtered)
> +		evsel_hists->stats.total_non_filtered_period += sample->period;
> +}
> +
>   static int process_sample_event(const struct perf_tool *tool __maybe_unused,
>   				union perf_event *event,
>   				struct perf_sample *sample,
> @@ -363,6 +375,9 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused,
>   	hists__inc_nr_samples(&c2c_hists->hists, he->filtered);
>   	if (perf_c2c__has_annotation(NULL))
>   		addr_map_symbol__inc_samples(mem_info__iaddr(mi), sample, evsel);
> +
> +	perf_c2c__evsel_hists_inc_stats(evsel, he, sample);
> +
>   	ret = hist_entry__append_callchain(he, sample);
>   
>   	if (!ret) {
>
> Thanks,
> Ravi
>

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [PATCH v8 1/2] perf tools c2c: Add annotation support to perf c2c report
  2025-10-13  8:52                                 ` Ravi Bangoria
  2025-10-13 13:43                                   ` Li, Tianyou
@ 2025-10-13 14:48                                   ` Tianyou Li
  2025-10-13 14:48                                   ` [PATCH v8 2/2] perf tools c2c: Highlight the contention line in the annotate browser Tianyou Li
  2 siblings, 0 replies; 36+ messages in thread
From: Tianyou Li @ 2025-10-13 14:48 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim
  Cc: Mark Rutland, Alexander Shishkin, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Kan Liang, Ravi Bangoria, tianyou.li, wangyang.guo,
	pan.deng, zhiguo.zhou, jiebin.sun, thomas.falcon, dapeng1.mi,
	linux-perf-users, linux-kernel

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.

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>
Tested-by: Ravi Bangoria <ravi.bangoria@amd.com>
---
 tools/perf/Documentation/perf-c2c.txt |   7 ++
 tools/perf/builtin-c2c.c              | 155 +++++++++++++++++++++++++-
 2 files changed, 157 insertions(+), 5 deletions(-)

diff --git a/tools/perf/Documentation/perf-c2c.txt b/tools/perf/Documentation/perf-c2c.txt
index f4af2dd6ab31..40b0f71a2c44 100644
--- a/tools/perf/Documentation/perf-c2c.txt
+++ b/tools/perf/Documentation/perf-c2c.txt
@@ -143,6 +143,13 @@ REPORT OPTIONS
 	feature, which causes cacheline sharing to behave like the cacheline
 	size is doubled.
 
+-M::
+--disassembler-style=::
+	Set disassembler style for objdump.
+
+--objdump=<path>::
+        Path to objdump binary.
+
 C2C RECORD
 ----------
 The perf c2c record command setup options related to HITM cacheline analysis
diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c
index 9e9ff471ddd1..a37e886ff3d7 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)
 {
@@ -275,6 +284,33 @@ static void compute_stats(struct c2c_hist_entry *c2c_he,
 		update_stats(&cstats->load, weight);
 }
 
+/*
+ * 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)
+{
+	if (use_browser != 1)
+		return false;
+	return !list || list->sym;
+}
+
+static void perf_c2c__evsel_hists_inc_stats(struct evsel *evsel,
+					    struct hist_entry *he,
+					    struct perf_sample *sample)
+{
+	struct hists *evsel_hists = evsel__hists(evsel);
+
+	hists__inc_nr_samples(evsel_hists, he->filtered);
+	evsel_hists->stats.total_period += sample->period;
+	if (!he->filtered)
+		evsel_hists->stats.total_non_filtered_period += sample->period;
+}
+
 static int process_sample_event(const struct perf_tool *tool __maybe_unused,
 				union perf_event *event,
 				struct perf_sample *sample,
@@ -334,8 +370,15 @@ 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);
+
+	if (perf_c2c__has_annotation(NULL)) {
+		perf_c2c__evsel_hists_inc_stats(evsel, he, sample);
+		addr_map_symbol__inc_samples(mem_info__iaddr(mi), sample, evsel);
+	}
+
 	ret = hist_entry__append_callchain(he, sample);
 
 	if (!ret) {
@@ -371,6 +414,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 +2041,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 || dim == &dim_iaddr)
+		hpp_list->sym = 1;
+
 	perf_hpp_list__register_sort_field(hpp_list, &c2c_fmt->fmt);
 	return 0;
 }
@@ -2550,6 +2597,40 @@ 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 annotated_source *src = NULL;
+	struct c2c_hist_entry *c2c_he = NULL;
+
+	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;
+	}
+
+	c2c_he = container_of(he, struct c2c_hist_entry, he);
+	return hist_entry__tui_annotate(he, c2c_he->evsel, NULL);
+}
+
 static void c2c_browser__update_nr_entries(struct hist_browser *hb)
 {
 	u64 nr_entries = 0;
@@ -2617,6 +2698,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 +2733,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 '?':
@@ -3006,6 +3091,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;
 	const struct option options[] = {
 	OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
 		   "file", "vmlinux pathname"),
@@ -3033,6 +3119,10 @@ 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_PARENT(c2c_options),
 	OPT_END()
 	};
@@ -3040,6 +3130,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 +3148,27 @@ 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 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;
+		}
+	}
+
 	err = symbol__validate_sym_arguments();
 	if (err)
 		goto out;
@@ -3126,6 +3243,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 +3284,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 +3354,7 @@ static int perf_c2c__report(int argc, const char **argv)
 out_session:
 	perf_session__delete(session);
 out:
+	annotation_options__exit();
 	return err;
 }
 
-- 
2.47.1


^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v8 2/2] perf tools c2c: Highlight the contention line in the annotate browser
  2025-10-13  8:52                                 ` Ravi Bangoria
  2025-10-13 13:43                                   ` Li, Tianyou
  2025-10-13 14:48                                   ` [PATCH v8 " Tianyou Li
@ 2025-10-13 14:48                                   ` Tianyou Li
  2 siblings, 0 replies; 36+ messages in thread
From: Tianyou Li @ 2025-10-13 14:48 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Namhyung Kim
  Cc: Mark Rutland, Alexander Shishkin, Jiri Olsa, Ian Rogers,
	Adrian Hunter, Kan Liang, Ravi Bangoria, tianyou.li, wangyang.guo,
	pan.deng, zhiguo.zhou, jiebin.sun, thomas.falcon, dapeng1.mi,
	linux-perf-users, linux-kernel

Add support to highlight the contention line in the annotate browser,
use 'TAB'/'UNTAB' to refocus to the contention line.

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>
Tested-by: Ravi Bangoria <ravi.bangoria@amd.com>
---
 tools/perf/builtin-annotate.c     |  2 +-
 tools/perf/builtin-c2c.c          |  6 +++-
 tools/perf/ui/browsers/annotate.c | 48 ++++++++++++++++++++++++++++---
 tools/perf/ui/browsers/hists.c    |  2 +-
 tools/perf/util/hist.h            |  6 ++--
 5 files changed, 55 insertions(+), 9 deletions(-)

diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 646f43b0f7c4..112b15952016 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_ADDR);
 
 			switch (key) {
 			case -1:
diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c
index a37e886ff3d7..14c3823f8fed 100644
--- a/tools/perf/builtin-c2c.c
+++ b/tools/perf/builtin-c2c.c
@@ -2604,6 +2604,7 @@ static int perf_c2c__toggle_annotation(struct hist_browser *browser)
 	struct symbol *sym = NULL;
 	struct annotated_source *src = NULL;
 	struct c2c_hist_entry *c2c_he = NULL;
+	u64 al_addr = NO_ADDR;
 
 	if (!perf_c2c__has_annotation(he->hists->hpp_list)) {
 		ui_browser__help_window(&browser->b, "No annotation support");
@@ -2627,8 +2628,11 @@ static int perf_c2c__toggle_annotation(struct hist_browser *browser)
 		return 0;
 	}
 
+	if (he->mem_info)
+		al_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);
+	return hist_entry__tui_annotate(he, c2c_he->evsel, NULL, al_addr);
 }
 
 static void c2c_browser__update_nr_entries(struct hist_browser *hb)
diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c
index 8fe699f98542..112fe6ad112e 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_ADDR);
 
 	/*
 	 * The annotate_browser above changed the title with the target function
@@ -852,6 +852,16 @@ static void annotate_browser__debuginfo_warning(struct annotate_browser *browser
 	}
 }
 
+static s64 annotate_browser__curr_hot_offset(struct annotate_browser *browser)
+{
+	struct annotation_line *al = NULL;
+
+	if (browser->curr_hot)
+		al = rb_entry(browser->curr_hot, struct annotation_line, rb_node);
+
+	return al ? al->offset : 0;
+}
+
 static int annotate_browser__run(struct annotate_browser *browser,
 				 struct evsel *evsel,
 				 struct hist_browser_timer *hbt)
@@ -873,6 +883,11 @@ static int annotate_browser__run(struct annotate_browser *browser,
 
 	annotate_browser__calc_percent(browser, evsel);
 
+	if (browser->selection != NULL) {
+		browser->curr_hot = &browser->selection->rb_node;
+		browser->b.use_navkeypressed = false;
+	}
+
 	if (browser->curr_hot) {
 		annotate_browser__set_rb_top(browser, browser->curr_hot);
 		browser->b.navkeypressed = false;
@@ -969,8 +984,19 @@ static int annotate_browser__run(struct annotate_browser *browser,
 			nd = browser->curr_hot;
 			break;
 		case 's':
+			struct annotation_line *al = NULL;
+			s64 offset = annotate_browser__curr_hot_offset(browser);
+
 			if (annotate_browser__toggle_source(browser, evsel))
 				ui_helpline__puts(help);
+
+			/* Update the annotation browser's rb_tree, and reset the nd */
+			annotate_browser__calc_percent(browser, evsel);
+			/* Try to find the same asm line as before */
+			al = annotated_source__get_line(notes->src, offset);
+			browser->curr_hot = al ? &al->rb_node : NULL;
+			nd = browser->curr_hot;
+
 			annotate__scnprintf_title(hists, title, sizeof(title));
 			annotate_browser__show(browser, title, help);
 			continue;
@@ -1106,19 +1132,19 @@ 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 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, 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 al_addr)
 {
 	struct symbol *sym = ms->sym;
 	struct annotation *notes = symbol__annotation(sym);
@@ -1188,6 +1214,20 @@ int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms,
 	if (annotate_opts.hide_src_code)
 		ui_browser__init_asm_mode(&browser.b);
 
+	/*
+	 * If 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 (al_addr != NO_ADDR) {
+		struct annotation_line *al = annotated_source__get_line(notes->src,
+			al_addr - sym->start);
+
+		browser.selection = al;
+	}
+
 	ret = annotate_browser__run(&browser, evsel, hbt);
 
 	debuginfo__delete(browser.dbg);
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c
index 487c0b08c003..08fecbe28a52 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_ADDR);
 	/*
 	 * offer option to annotate the other branch source or target
 	 * (if they exists) when returning from annotate
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
index c64005278687..6795816eee85 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_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 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 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


^ permalink raw reply related	[flat|nested] 36+ messages in thread

end of thread, other threads:[~2025-10-13 13:56 UTC | newest]

Thread overview: 36+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-08-01  7:51 [PATCH] perf tools c2c: Add annotation support to perf c2c report Tianyou Li
2025-08-19  8:00 ` [PATCH v2] " Tianyou Li
2025-09-03 13:50   ` Arnaldo Carvalho de Melo
2025-09-07 14:53     ` Li, Tianyou
2025-09-07 15:25     ` [PATCH v3] " Tianyou Li
2025-09-11 21:39       ` Namhyung Kim
2025-09-12 15:20         ` Li, Tianyou
2025-09-17  6:34           ` Namhyung Kim
2025-09-24  7:33             ` Li, Tianyou
2025-09-28  9:02             ` [PATCH v4] " Tianyou Li
2025-09-28  8:16               ` Li, Tianyou
2025-09-29  8:07                 ` Namhyung Kim
2025-09-30 11:41                   ` Li, Tianyou
2025-09-30 12:39                   ` [PATCH v5] " Tianyou Li
2025-10-03  5:05                     ` Namhyung Kim
2025-10-03 11:44                       ` Li, Tianyou
2025-10-07  8:23                         ` Namhyung Kim
2025-10-09  3:47                           ` Li, Tianyou
2025-10-09  4:28                           ` [PATCH v6] " Tianyou Li
2025-10-10  5:56                             ` Ravi Bangoria
2025-10-10  7:49                               ` Li, Tianyou
2025-10-10  8:33                           ` [PATCH v6 1/3] " Tianyou Li
2025-10-10  8:33                           ` [PATCH v6 2/3] perf tools annotate: Fix a crash/hang when switch disassemble and source view Tianyou Li
2025-10-10  8:33                           ` [PATCH v6 3/3] perf tools c2c: Highlight the contention line in the annotate browser Tianyou Li
2025-10-10  8:35                           ` [PATCH v6 1/3] perf tools c2c: Add annotation support to perf c2c report Tianyou Li
2025-10-10  8:35                           ` [PATCH v6 2/3] perf tools annotate: Fix a crash/hang when switch disassemble and source view Tianyou Li
2025-10-10  8:35                           ` [PATCH v6 3/3] perf tools c2c: Highlight the contention line in the annotate browser Tianyou Li
2025-10-10 13:08                             ` Namhyung Kim
2025-10-11  8:16                               ` [PATCH v7 1/2] perf tools c2c: Add annotation support to perf c2c report Tianyou Li
2025-10-13  8:52                                 ` Ravi Bangoria
2025-10-13 13:43                                   ` Li, Tianyou
2025-10-13 14:48                                   ` [PATCH v8 " Tianyou Li
2025-10-13 14:48                                   ` [PATCH v8 2/2] perf tools c2c: Highlight the contention line in the annotate browser Tianyou Li
2025-10-11  8:16                               ` [PATCH v7 " Tianyou Li
2025-10-06 10:54                       ` [PATCH v5] perf tools c2c: Add annotation support to perf c2c report Ravi Bangoria
2025-10-09  5:34                         ` Li, Tianyou

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).