* [PATCH 3/4] perf c2c: Add stdio support for the function view
2026-08-21 5:56 [PATCH 0/4] perf c2c: Add function-view stdio support and coverage Jiebin Sun
2026-08-21 5:56 ` [PATCH 1/4] perf c2c: Fix documented default coalesce fields Jiebin Sun
2026-08-21 5:56 ` [PATCH 2/4] perf tests c2c: Report skip when the workload fails Jiebin Sun
@ 2026-08-21 5:56 ` Jiebin Sun
2026-08-21 5:58 ` sashiko-bot
2026-08-21 5:56 ` [PATCH 4/4] perf tests c2c: Add function view stdio coverage Jiebin Sun
3 siblings, 1 reply; 9+ messages in thread
From: Jiebin Sun @ 2026-08-21 5:56 UTC (permalink / raw)
To: namhyung
Cc: acme, mingo, peterz, adrian.hunter, alexander.shishkin, irogers,
james.clark, jolsa, mark.rutland, dapeng1.mi, thomas.falcon,
tianyou.li, wangyang.guo, jiebin.sun, linux-perf-users,
linux-kernel
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 <jiebin.sun@intel.com>
Cc: Dapeng Mi <dapeng1.mi@linux.intel.com>
Cc: Ian Rogers <irogers@google.com>
Cc: James Clark <james.clark@linaro.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Thomas Falcon <thomas.falcon@intel.com>
Reviewed-by: Tianyou Li <tianyou.li@intel.com>
Reviewed-by: Wangyang Guo <wangyang.guo@intel.com>
---
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
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 4/4] perf tests c2c: Add function view stdio coverage
2026-08-21 5:56 [PATCH 0/4] perf c2c: Add function-view stdio support and coverage Jiebin Sun
` (2 preceding siblings ...)
2026-08-21 5:56 ` [PATCH 3/4] perf c2c: Add stdio support for the function view Jiebin Sun
@ 2026-08-21 5:56 ` Jiebin Sun
2026-08-21 5:58 ` sashiko-bot
3 siblings, 1 reply; 9+ messages in thread
From: Jiebin Sun @ 2026-08-21 5:56 UTC (permalink / raw)
To: namhyung
Cc: acme, mingo, peterz, adrian.hunter, alexander.shishkin, irogers,
james.clark, jolsa, mark.rutland, dapeng1.mi, thomas.falcon,
tianyou.li, wangyang.guo, jiebin.sun, linux-perf-users,
linux-kernel
Exercise the function view without driving a terminal now that it has
a stdio path. Keep the existing datasym record/report coverage and make a
separate recording of the contended locks used by the futex hash benchmark.
Always check the function table headers, table replacement, missing-iaddr
diagnostic, and conflicting options. A machine can support c2c recording
without capturing a contended sample, so report a skip when the hierarchy
is empty rather than treating hardware sampling variance as a failure.
When samples are available, check generic row shapes for all three
hierarchy levels, their expanded fold signs, cacheline addresses, and the
absence of trailing whitespace in the table body.
Signed-off-by: Jiebin Sun <jiebin.sun@intel.com>
Cc: Dapeng Mi <dapeng1.mi@linux.intel.com>
Cc: Ian Rogers <irogers@google.com>
Cc: James Clark <james.clark@linaro.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Thomas Falcon <thomas.falcon@intel.com>
Reviewed-by: Tianyou Li <tianyou.li@intel.com>
Reviewed-by: Wangyang Guo <wangyang.guo@intel.com>
---
tools/perf/tests/shell/c2c.sh | 113 ++++++++++++++++++++++++++++++++++
1 file changed, 113 insertions(+)
diff --git a/tools/perf/tests/shell/c2c.sh b/tools/perf/tests/shell/c2c.sh
index f5f223cbf9cc..cf76359d19cc 100755
--- a/tools/perf/tests/shell/c2c.sh
+++ b/tools/perf/tests/shell/c2c.sh
@@ -6,10 +6,15 @@ set -e
err=0
perfdata=$(mktemp /tmp/__perf_c2c_test.perf.data.XXXXX)
+funcdata=$(mktemp /tmp/__perf_c2c_function_test.perf.data.XXXXX)
+perfout=$(mktemp /tmp/__perf_c2c_test.output.XXXXX)
cleanup() {
rm -f "${perfdata}"
rm -f "${perfdata}".old
+ rm -f "${funcdata}"
+ rm -f "${funcdata}".old
+ rm -f "${perfout}"
trap - EXIT TERM INT
}
@@ -58,6 +63,114 @@ test_c2c_record_report() {
echo "c2c record and report test [Success]"
}
+test_c2c_function_report() {
+ echo "c2c function stdio report test"
+
+ if perf c2c report -i "${perfdata}" --function -c pid > "${perfout}" 2>&1 ; then
+ echo "c2c function stdio report test [Failed: report accepted missing iaddr]"
+ err=1
+ return
+ fi
+ if ! grep -Fq "The function view requires iaddr in --coalesce." "${perfout}" ; then
+ echo "c2c function stdio report test [Failed: missing iaddr diagnostic]"
+ cat "${perfout}"
+ err=1
+ return
+ fi
+ if grep -Fq "Shared Data Functions Table" "${perfout}" ; then
+ echo "c2c function stdio report test [Failed: partial report on missing iaddr]"
+ cat "${perfout}"
+ err=1
+ return
+ fi
+
+ if perf c2c report -i "${perfdata}" --function --stats > "${perfout}" 2>&1 ; then
+ echo "c2c function stdio report test [Failed: accepted conflicting options]"
+ err=1
+ return
+ fi
+ if ! grep -Fq -- "--stats and --function cannot be used together." "${perfout}" ; then
+ echo "c2c function stdio report test [Failed: missing conflict diagnostic]"
+ cat "${perfout}"
+ err=1
+ return
+ fi
+
+ # Exercise contended futex hash-bucket locks so the function view can get
+ # reader, writer, and cacheline rows without changing the original test.
+ if ! perf c2c record -o "${funcdata}" -- \
+ perf bench futex hash -t 4 -r 1 -s > /dev/null 2>&1 ; then
+ echo "c2c function stdio report test [Skipped: recording failed]"
+ err=2
+ return
+ fi
+
+ if ! perf c2c report -i "${funcdata}" --function > "${perfout}" 2>&1 ; then
+ echo "c2c function stdio report test [Failed: report failed]"
+ cat "${perfout}"
+ err=1
+ return
+ fi
+
+ for expected in "Shared Data Functions Table" \
+ "# Cycles Store" \
+ "# % count Function / Contending function / Cacheline" \
+ "# ......... ......." ; do
+ if ! grep -Fq "${expected}" "${perfout}" ; then
+ echo "c2c function stdio report test [Failed: missing '${expected}']"
+ cat "${perfout}"
+ err=1
+ return
+ fi
+ done
+ for unexpected in "Shared Data Cache Line Table" \
+ "Shared Cache Line Distribution Pareto" ; do
+ if grep -Fq "${unexpected}" "${perfout}" ; then
+ echo "c2c function stdio report test [Failed: found '${unexpected}']"
+ cat "${perfout}"
+ err=1
+ return
+ fi
+ done
+
+ if ! awk '/^# \.+/ { body = 1; next }
+ body && !/^#/ && NF { found = 1 }
+ END { exit !found }' "${perfout}" ; then
+ echo "c2c function stdio report test [Skipped: no contended samples]"
+ err=2
+ return
+ fi
+
+ # The spaces intentionally verify per-level indentation and expanded
+ # fold-sign placement without depending on symbol names.
+ for expected in \
+ '^ - +[0-9]+\.[0-9]+% +[0-9]+ - ' \
+ '^ +[0-9]+ - ' \
+ '^ +[0-9]+ 0x[[:xdigit:]]+$' ; do
+ if ! grep -Eq "${expected}" "${perfout}" ; then
+ echo "c2c function stdio report test [Failed: missing hierarchy row]"
+ echo " ${expected}"
+ cat "${perfout}"
+ err=1
+ return
+ fi
+ done
+
+ if awk '/^# \.+/ { body = 1; next }
+ body && /[[:blank:]]$/ { found = 1 }
+ END { exit !found }' "${perfout}" ; then
+ echo "c2c function stdio report test [Failed: trailing whitespace in table body]"
+ cat "${perfout}"
+ err=1
+ return
+ fi
+
+ echo "c2c function stdio report test [Success]"
+}
+
test_c2c_record_report
+if [ "${err}" -eq 0 ]; then
+ test_c2c_function_report
+fi
cleanup
exit $err
--
2.52.0
^ permalink raw reply related [flat|nested] 9+ messages in thread