[PATCH 3/4] perf c2c: Add stdio support for the function view

Jiebin Sun <[email protected]>
Newsgroups org.kernel.vger.linux-perf-users,org.kernel.vger.linux-kernel
Message-ID <c53d3eb3a036c250702af363b6e7ce3307aa1571.1787283281.git.jiebin.sun@intel.com>
The function view is currently TUI-only, so it cannot be used by builds
without SLANG support, when output is piped, or from a script. Add a
--function option that prints the fully expanded three-level hierarchy to
stdout.

Keep the stdio renderer in builtin-c2c.c and reuse the common function-view
model introduced by the merged series. Export only the coalescing-field
capability check from the model, preserving the util/UI boundary and
leaving the TUI object in libperf-ui.a.

Stop padding the final identity column in symbol_view_entry(). The generic
formatter pads non-final columns but deliberately leaves the final column
unpadded, avoiding trailing whitespace in function-view table rows. The TUI
remains unchanged because its browser clears the rest of each rendered row.

--function implies --stdio and is rejected together with --stats. Validate
the iaddr requirement before processing events. Return function-view build
failures from the report command, and preserve TUI browser errors when
converting the display helpers to return a status.

Signed-off-by: Jiebin Sun <[email protected]>
Cc: Dapeng Mi <[email protected]>
Cc: Ian Rogers <[email protected]>
Cc: James Clark <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Thomas Falcon <[email protected]>
Reviewed-by: Tianyou Li <[email protected]>
Reviewed-by: Wangyang Guo <[email protected]>
---
 tools/perf/Documentation/perf-c2c.txt | 12 +++-
 tools/perf/builtin-c2c.c              | 85 ++++++++++++++++++++++++---
 tools/perf/util/c2c-function.c        |  9 +--
 tools/perf/util/c2c.h                 |  1 +
 4 files changed, 90 insertions(+), 17 deletions(-)

diff --git a/tools/perf/Documentation/perf-c2c.txt b/tools/perf/Documentation/perf-c2c.txt
index 9e58a51c55de..7a0cf31be7ed 100644
--- a/tools/perf/Documentation/perf-c2c.txt
+++ b/tools/perf/Documentation/perf-c2c.txt
@@ -107,6 +107,10 @@ REPORT OPTIONS
 --stats::
 	Display only statistic tables and force stdio mode.
 
+--function::
+	Display the function view and force stdio mode.  This requires `iaddr`
+	in the cacheline coalescing fields and cannot be used with `--stats`.
+
 --full-symbols::
 	Display full length of symbols.
 
@@ -360,6 +364,10 @@ Following tables are displayed:
   Shared Cache Line Distribution Pareto
   - list of all accessed offsets for each cacheline
 
+With `--function`, the cacheline and Pareto tables are replaced by a fully
+expanded Shared Data Functions Table.  Its three levels are the read-side
+function, contending writer, and shared cacheline, as detailed below.
+
 TUI OUTPUT
 ----------
 The TUI output provides interactive interface to navigate
@@ -374,8 +382,8 @@ Verbose mode also includes code addresses in function rows, and code addresses
 remain available in the per-cacheline detail view ('d').
 
 The function view requires `iaddr` in the cacheline coalescing fields.  If
-`--coalesce` omits it, TAB reports that the view is unavailable rather than
-attributing already-coalesced samples to an arbitrary function.
+`--coalesce` omits it, TAB or `--function` reports that the view is unavailable
+rather than attributing already-coalesced samples to an arbitrary function.
 
   Level 1: the read-side function, sorted by Cycles % (estimated load
            cycles: HITM, peer-snoop and other-load cycles)
diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c
index 715b75d42f2a..cce76a1e2ff5 100644
--- a/tools/perf/builtin-c2c.c
+++ b/tools/perf/builtin-c2c.c
@@ -73,6 +73,7 @@ struct perf_c2c {
 	bool			 show_all;
 	bool			 use_stdio;
 	bool			 stats_only;
+	bool			 function_view;
 	bool			 symbol_full;
 	bool			 stitch_lbr;
 
@@ -2530,7 +2531,48 @@ static void print_c2c_info(FILE *out, struct perf_session *session)
 	fprintf(out, "  Cacheline data grouping           : %s\n", c2c.cl_sort);
 }
 
-static void perf_c2c__hists_fprintf(FILE *out, struct perf_session *session)
+static void c2c_function__unfold_all(struct rb_root_cached *root)
+{
+	struct rb_node *nd;
+
+	for (nd = rb_first_cached(root); nd; nd = rb_next(nd)) {
+		struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
+
+		if (!he->has_children)
+			continue;
+		he->unfolded = true;
+		c2c_function__unfold_all(&he->hroot_out);
+	}
+}
+
+static int perf_c2c__function_fprintf(FILE *out)
+{
+	bool saved_use_callchain = symbol_conf.use_callchain;
+	struct hists *hists;
+	int ret;
+
+	/* Function-view entries aggregate samples and never display callchains. */
+	symbol_conf.use_callchain = false;
+	ret = c2c_function__build(&c2c.hists, c2c.cl_sort, c2c.symbol_full,
+				  &hists);
+	if (ret) {
+		if (ret == -EOPNOTSUPP)
+			pr_err("The function view requires iaddr in --coalesce.\n");
+		else
+			pr_err("Failed to build function view hierarchy (ret=%d)\n", ret);
+		goto out;
+	}
+
+	/* Match fold signs to hists__fprintf()'s forced child traversal. */
+	c2c_function__unfold_all(&hists->entries);
+	hists__fprintf(hists, true, 0, 0, 0, out, true);
+	c2c_function__reset();
+out:
+	symbol_conf.use_callchain = saved_use_callchain;
+	return ret;
+}
+
+static int perf_c2c__hists_fprintf(FILE *out, struct perf_session *session)
 {
 	setup_pager();
 
@@ -2541,7 +2583,17 @@ static void perf_c2c__hists_fprintf(FILE *out, struct perf_session *session)
 	print_c2c_info(out, session);
 
 	if (c2c.stats_only)
-		return;
+		return 0;
+
+	if (c2c.function_view) {
+		fprintf(out, "\n");
+		fprintf(out, "=================================================\n");
+		fprintf(out, "           Shared Data Functions Table\n");
+		fprintf(out, "=================================================\n");
+		fprintf(out, "#\n");
+
+		return perf_c2c__function_fprintf(out);
+	}
 
 	fprintf(out, "\n");
 	fprintf(out, "=================================================\n");
@@ -2558,6 +2610,7 @@ static void perf_c2c__hists_fprintf(FILE *out, struct perf_session *session)
 	fprintf(out, "#\n");
 
 	print_pareto(out, perf_session__env(session));
+	return 0;
 }
 
 #ifdef HAVE_SLANG_SUPPORT
@@ -2794,18 +2847,18 @@ static int perf_c2c__hists_browse(struct hists *hists)
 	return 0;
 }
 
-static void perf_c2c_display(struct perf_session *session)
+static int perf_c2c_display(struct perf_session *session)
 {
 	if (use_browser == 0)
-		perf_c2c__hists_fprintf(stdout, session);
-	else
-		perf_c2c__hists_browse(&c2c.hists.hists);
+		return perf_c2c__hists_fprintf(stdout, session);
+
+	return perf_c2c__hists_browse(&c2c.hists.hists);
 }
 #else
-static void perf_c2c_display(struct perf_session *session)
+static int perf_c2c_display(struct perf_session *session)
 {
 	use_browser = 0;
-	perf_c2c__hists_fprintf(stdout, session);
+	return perf_c2c__hists_fprintf(stdout, session);
 }
 #endif /* HAVE_SLANG_SUPPORT */
 
@@ -3081,6 +3134,8 @@ static int perf_c2c__report(int argc, const char **argv)
 	OPT_BOOLEAN(0, "stdio", &c2c.use_stdio, "Use the stdio interface"),
 	OPT_BOOLEAN(0, "stats", &c2c.stats_only,
 		    "Display only statistic tables (implies --stdio)"),
+	OPT_BOOLEAN(0, "function", &c2c.function_view,
+		    "Display the function view (implies --stdio)"),
 	OPT_BOOLEAN(0, "full-symbols", &c2c.symbol_full,
 		    "Display full length of symbols"),
 	OPT_BOOLEAN(0, "no-source", &no_source,
@@ -3119,6 +3174,11 @@ static int perf_c2c__report(int argc, const char **argv)
 			     PARSE_OPT_STOP_AT_NON_OPTION);
 	if (argc)
 		usage_with_options(report_c2c_usage, options);
+	if (c2c.stats_only && c2c.function_view) {
+		pr_err("--stats and --function cannot be used together.\n");
+		err = -EINVAL;
+		goto out;
+	}
 
 #ifndef HAVE_SLANG_SUPPORT
 	c2c.use_stdio = true;
@@ -3126,6 +3186,8 @@ static int perf_c2c__report(int argc, const char **argv)
 
 	if (c2c.stats_only)
 		c2c.use_stdio = true;
+	if (c2c.function_view)
+		c2c.use_stdio = true;
 
 	/**
 	 * Annotation related options disassembler_style, objdump_path are set
@@ -3199,6 +3261,11 @@ static int perf_c2c__report(int argc, const char **argv)
 		pr_debug("Failed to initialize hists\n");
 		goto out_session;
 	}
+	if (c2c.function_view && !c2c_function__has_iaddr(c2c.cl_sort)) {
+		pr_err("The function view requires iaddr in --coalesce.\n");
+		err = -EINVAL;
+		goto out_session;
+	}
 
 	err = c2c_hists__init(&c2c.hists, "dcacheline", 2, perf_session__env(session));
 	if (err) {
@@ -3332,7 +3399,7 @@ static int perf_c2c__report(int argc, const char **argv)
 		goto out_mem2node;
 	}
 
-	perf_c2c_display(session);
+	err = perf_c2c_display(session);
 
 out_mem2node:
 	mem2node__exit(&c2c.mem2node);
diff --git a/tools/perf/util/c2c-function.c b/tools/perf/util/c2c-function.c
index 5b6a06a5a067..d410196d0429 100644
--- a/tools/perf/util/c2c-function.c
+++ b/tools/perf/util/c2c-function.c
@@ -305,8 +305,8 @@ symbol_view_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
 		char symbuf[32];
 
 		scnprintf(symbuf, sizeof(symbuf), "0x%" PRIx64, addr);
-		ret += scnprintf(hpp->buf + ret, hpp->size - ret, "%-*.*s",
-				 text_width, text_width, symbuf);
+		ret += scnprintf(hpp->buf + ret, hpp->size - ret, "%.*s",
+				 text_width, symbuf);
 	} else {
 		/* Level 1 and level 2 are both functions. */
 		size_t cell_size;
@@ -331,9 +331,6 @@ symbol_view_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
 			len = min_t(size_t, len, cell_size - 1);
 
 		ret += len;
-		if (len < text_width)
-			ret += scnprintf(hpp->buf + ret, hpp->size - ret, "%*s",
-					 text_width - len, "");
 	}
 
 	return ret;
@@ -1519,7 +1516,7 @@ void c2c_function__reset(void)
 	symbol_conf.use_callchain = saved_use_callchain;
 }
 
-static bool c2c_function__has_iaddr(const char *cl_sort)
+bool c2c_function__has_iaddr(const char *cl_sort)
 {
 	const char *field = cl_sort;
 
diff --git a/tools/perf/util/c2c.h b/tools/perf/util/c2c.h
index 53f024e25d99..198032ec7c87 100644
--- a/tools/perf/util/c2c.h
+++ b/tools/perf/util/c2c.h
@@ -107,6 +107,7 @@ bool c2c_fmt_equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b);
 int c2c_function__build(struct c2c_hists *cl_hists, const char *cl_sort,
 			bool symbol_full, struct hists **hists);
 void c2c_function__reset(void);
+bool c2c_function__has_iaddr(const char *cl_sort);
 /* Valid only between a successful build and c2c_function__reset(). */
 struct hist_entry *c2c_function__find_cacheline(struct hist_entry *he);
 
-- 
2.52.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.