[PATCH v5] perf tools c2c: Add annotation support to perf c2c report

Tianyou Li posted 1 patch 4 months, 1 week ago
There is a newer version of this series
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(-)
[PATCH v5] perf tools c2c: Add annotation support to perf c2c report
Posted by Tianyou Li 4 months, 1 week ago
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
Re: [PATCH v5] perf tools c2c: Add annotation support to perf c2c report
Posted by Namhyung Kim 4 months, 1 week ago
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
>
Re: [PATCH v5] perf tools c2c: Add annotation support to perf c2c report
Posted by Ravi Bangoria 4 months ago
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
Re: [PATCH v5] perf tools c2c: Add annotation support to perf c2c report
Posted by Li, Tianyou 4 months ago
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
>
Re: [PATCH v5] perf tools c2c: Add annotation support to perf c2c report
Posted by Li, Tianyou 4 months ago
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
>>
Re: [PATCH v5] perf tools c2c: Add annotation support to perf c2c report
Posted by Namhyung Kim 4 months ago
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

Re: [PATCH v5] perf tools c2c: Add annotation support to perf c2c report
Posted by Li, Tianyou 4 months ago
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
>
>
[PATCH v6 1/3] perf tools c2c: Add annotation support to perf c2c report
Posted by Tianyou Li 4 months ago
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
[PATCH v6 1/3] perf tools c2c: Add annotation support to perf c2c report
Posted by Tianyou Li 4 months ago
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
[PATCH v6 2/3] perf tools annotate: Fix a crash/hang when switch disassemble and source view
Posted by Tianyou Li 4 months ago
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
[PATCH v6 2/3] perf tools annotate: Fix a crash/hang when switch disassemble and source view
Posted by Tianyou Li 4 months ago
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
[PATCH v6 3/3] perf tools c2c: Highlight the contention line in the annotate browser
Posted by Tianyou Li 4 months ago
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
[PATCH v6 3/3] perf tools c2c: Highlight the contention line in the annotate browser
Posted by Tianyou Li 4 months ago
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
Re: [PATCH v6 3/3] perf tools c2c: Highlight the contention line in the annotate browser
Posted by Namhyung Kim 3 months, 4 weeks ago
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
>
[PATCH v7 1/2] perf tools c2c: Add annotation support to perf c2c report
Posted by Tianyou Li 3 months, 4 weeks ago
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
Re: [PATCH v7 1/2] perf tools c2c: Add annotation support to perf c2c report
Posted by Ravi Bangoria 3 months, 3 weeks ago
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
Re: [PATCH v7 1/2] perf tools c2c: Add annotation support to perf c2c report
Posted by Li, Tianyou 3 months, 3 weeks ago
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
>
[PATCH v8 1/2] perf tools c2c: Add annotation support to perf c2c report
Posted by Tianyou Li 3 months, 3 weeks ago
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
Re: [PATCH v8 1/2] perf tools c2c: Add annotation support to perf c2c report
Posted by Namhyung Kim 3 months, 2 weeks ago
On Mon, 13 Oct 2025 22:48:10 +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.
> 
> [...]

Applied to perf-tools-next, thanks!

Best regards,
Namhyung
[PATCH v8 2/2] perf tools c2c: Highlight the contention line in the annotate browser
Posted by Tianyou Li 3 months, 3 weeks ago
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
[PATCH v7 2/2] perf tools c2c: Highlight the contention line in the annotate browser
Posted by Tianyou Li 3 months, 4 weeks ago
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
[PATCH v6] perf tools c2c: Add annotation support to perf c2c report
Posted by Tianyou Li 4 months ago
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
Re: [PATCH v6] perf tools c2c: Add annotation support to perf c2c report
Posted by Ravi Bangoria 4 months ago
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>
Re: [PATCH v6] perf tools c2c: Add annotation support to perf c2c report
Posted by Li, Tianyou 4 months ago
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>
>