[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