Re: [PATCH v9 4/9] perf c2c: add HPP list parsing for function view columns
Ian Rogers <[email protected]>
| Newsgroups | org.kernel.vger.linux-perf-users,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <CAP-5=fUDH2mimHTKU+kgwxTq_wqoxwmoTbak43VaCkfPg=XEGw@mail.gmail.com> |
On Mon, Aug 17, 2026 at 2:40 AM Jiebin Sun <[email protected]> wrote: > > Add the parser that builds the function view's local HPP output and > sort lists from field strings. This includes dimension lookup, comparator > wrappers, c2c_fmt allocation, and the initialization entry points used by > the hierarchy builder. > > The generic perf_hpp__setup_output_field() registers formats on the global > perf_hpp_list. Using it here would leave the function view's local list > without output columns and modify the cacheline view's list instead. Add > c2c_function_hists__setup_output_field() to append sort keys to the local > output list. > > Signed-off-by: Jiebin Sun <[email protected]> > Cc: Adrian Hunter <[email protected]> > Cc: Alexander Shishkin <[email protected]> > Cc: Arnaldo Carvalho de Melo <[email protected]> > Cc: Dapeng Mi <[email protected]> > Cc: Ian Rogers <[email protected]> > Cc: Ingo Molnar <[email protected]> > Cc: James Clark <[email protected]> > Cc: Jiri Olsa <[email protected]> > Cc: Mark Rutland <[email protected]> > Cc: Namhyung Kim <[email protected]> > Cc: Peter Zijlstra <[email protected]> > Cc: Thomas Falcon <[email protected]> > Reviewed-by: Tianyou Li <[email protected]> > Reviewed-by: Wangyang Guo <[email protected]> Reviewed-by: Ian Rogers <[email protected]> Thanks, Ian > --- > tools/perf/util/c2c-function.c | 269 ++++++++++++++++++++++++++++++++- > 1 file changed, 266 insertions(+), 3 deletions(-) > > diff --git a/tools/perf/util/c2c-function.c b/tools/perf/util/c2c-function.c > index 7fce415c0f07..d27544016660 100644 > --- a/tools/perf/util/c2c-function.c > +++ b/tools/perf/util/c2c-function.c > @@ -23,6 +23,7 @@ > #include "addr_location.h" > #include "c2c.h" > #include "cacheline.h" > +#include "debug.h" > #include "hist.h" > #include "map.h" > #include "mem-events.h" > @@ -132,8 +133,8 @@ static int c2c_width(struct perf_hpp_fmt *fmt, > dim->width; > } > > -static int __maybe_unused c2c_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, > - struct hists *hists, int line, int *span) > +static int c2c_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, > + struct hists *hists, int line, int *span) > { > struct c2c_fmt *c2c_fmt; > struct c2c_dimension *dim; > @@ -411,9 +412,271 @@ static struct c2c_dimension dim_symbol_view = { > .width = SYMBOL_WIDTH, > }; > > -static struct c2c_dimension *function_view_dimensions[] __maybe_unused = { > +static struct c2c_dimension *function_view_dimensions[] = { > &dim_cycles_percent, > &dim_total_stores, > &dim_symbol_view, > NULL, > }; > + > +static struct c2c_dimension *get_function_dimension(const char *name) > +{ > + unsigned int i; > + > + for (i = 0; function_view_dimensions[i]; i++) { > + struct c2c_dimension *dim = function_view_dimensions[i]; > + > + if (!strcmp(dim->name, name)) > + return dim; > + } > + > + return NULL; > +} > + > +/* Wrappers so sort_entry-backed dimensions sort/collapse via their se. */ > +static int64_t c2c_se_cmp(struct perf_hpp_fmt *fmt, > + struct hist_entry *a, struct hist_entry *b) > +{ > + struct c2c_fmt *c2c_fmt = container_of(fmt, struct c2c_fmt, fmt); > + struct c2c_dimension *dim = c2c_fmt->dim; > + > + return dim->se->se_cmp(a, b); > +} > + > +static int64_t c2c_se_collapse(struct perf_hpp_fmt *fmt, > + struct hist_entry *a, struct hist_entry *b) > +{ > + struct c2c_fmt *c2c_fmt = container_of(fmt, struct c2c_fmt, fmt); > + struct c2c_dimension *dim = c2c_fmt->dim; > + int64_t (*collapse_fn)(struct hist_entry *a, struct hist_entry *b); > + > + collapse_fn = dim->se->se_collapse ?: dim->se->se_cmp; > + return collapse_fn(a, b); > +} > + > +static int64_t c2c_se_sort(struct perf_hpp_fmt *fmt, > + struct hist_entry *a, struct hist_entry *b) > +{ > + struct c2c_fmt *c2c_fmt = container_of(fmt, struct c2c_fmt, fmt); > + struct c2c_dimension *dim = c2c_fmt->dim; > + int64_t (*sort_fn)(struct hist_entry *a, struct hist_entry *b); > + > + sort_fn = dim->se->se_sort ?: dim->se->se_cmp; > + return sort_fn(a, b); > +} > + > +/* > + * Build the c2c_fmt for @name. Returns: > + * 0 and *fmtp set on success; > + * -ENOENT and *fmtp = NULL if @name is not a function-view dimension; > + * -ENOMEM if allocation failed (distinct from -ENOENT so > + * the caller does not misreport it as an > + * "invalid field"). > + */ > +static int get_function_format(const char *name, struct c2c_fmt **fmtp) > +{ > + struct c2c_dimension *dim = get_function_dimension(name); > + struct c2c_fmt *c2c_fmt; > + struct perf_hpp_fmt *fmt; > + > + *fmtp = NULL; > + > + if (!dim) > + return -ENOENT; > + > + c2c_fmt = zalloc(sizeof(*c2c_fmt)); > + if (!c2c_fmt) > + return -ENOMEM; > + > + fmt = &c2c_fmt->fmt; > + > + c2c_fmt->dim = dim; > + INIT_LIST_HEAD(&fmt->list); > + INIT_LIST_HEAD(&fmt->sort_list); > + > + fmt->cmp = dim->se ? c2c_se_cmp : dim->cmp; > + fmt->sort = dim->se ? c2c_se_sort : dim->cmp; > + fmt->color = dim->color; > + fmt->entry = dim->entry; > + fmt->header = c2c_header; > + fmt->width = c2c_width; > + fmt->collapse = dim->se ? c2c_se_collapse : dim->cmp; > + fmt->equal = c2c_fmt_equal; > + fmt->free = c2c_fmt_free; > + > + *fmtp = c2c_fmt; > + return 0; > +} > + > +static int > +c2c_function_hists__init_output(struct perf_hpp_list *hpp_list, char *name, > + struct perf_env *env __maybe_unused) > +{ > + struct c2c_fmt *c2c_fmt; > + int ret; > + > + ret = get_function_format(name, &c2c_fmt); > + if (ret == -ENOMEM) > + return ret; > + /* The function view only accepts its own dimensions. */ > + if (ret == -ENOENT) > + return -EINVAL; > + > + /* > + * Mark symbol-backed columns so hists__has(hists, sym) is correct. > + * Only dim_symbol_view carries a sort_entry (.se); the function > + * view's field strings are fixed and always include symbol_view, so > + * this single check is sufficient (unlike the user-configurable > + * cacheline view, which must also test dim_iaddr). > + */ > + if (c2c_fmt->dim->se == &sort_sym) > + hpp_list->sym = 1; > + > + perf_hpp_list__column_register(hpp_list, &c2c_fmt->fmt); > + return 0; > +} > + > +static int > +c2c_function_hists__init_sort(struct perf_hpp_list *hpp_list, char *name, > + struct perf_env *env __maybe_unused) > +{ > + struct c2c_fmt *c2c_fmt; > + int ret; > + > + ret = get_function_format(name, &c2c_fmt); > + if (ret == -ENOMEM) > + return ret; > + /* The function view only accepts its own dimensions. */ > + if (ret == -ENOENT) > + return -EINVAL; > + > + /* Mark symbol-backed sort keys so hists__has(hists, sym) is correct. */ > + if (c2c_fmt->dim->se == &sort_sym) > + hpp_list->sym = 1; > + > + perf_hpp_list__register_sort_field(hpp_list, &c2c_fmt->fmt); > + return 0; > +} > + > +typedef int (*hpp_list_add_fn)(struct perf_hpp_list *hpp_list, char *name, > + struct perf_env *env); > + > +static int function_hpp_list__add_tokens(struct perf_hpp_list *hpp_list, char *list, > + struct perf_env *env, hpp_list_add_fn add) > +{ > + char *tok, *tmp; > + int ret; > + > + if (!list) > + return 0; > + > + for (tok = strtok_r(list, ", ", &tmp); tok; tok = strtok_r(NULL, ", ", &tmp)) { > + ret = add(hpp_list, tok, env); > + if (ret) { > + if (ret == -EINVAL || ret == -ESRCH) > + pr_err("Invalid c2c function-view field: %s\n", tok); > + return ret; > + } > + } > + return 0; > +} > + > +/* > + * Append the function view's sort keys to its own output fields, mirroring > + * perf_hpp__setup_output_field() but on the local @list. The shared helper > + * registers onto the global perf_hpp_list, which would leave this local list > + * without output columns, so the function view keeps its own copy here. > + */ > +static void c2c_function_hists__setup_output_field(struct perf_hpp_list *list) > +{ > + struct perf_hpp_fmt *fmt; > + > + perf_hpp_list__for_each_sort_list(list, fmt) { > + struct perf_hpp_fmt *pos; > + > + if (!fmt->entry && !fmt->color) > + continue; > + > + perf_hpp_list__for_each_format(list, pos) { > + if (c2c_fmt_equal(fmt, pos)) > + goto next; > + } > + > + perf_hpp_list__column_register(list, fmt); > +next: > + continue; > + } > +} > + > +static int > +function_hpp_list__parse(struct perf_hpp_list *hpp_list, > + const char *output_str, > + const char *sort_str, > + struct perf_env *env) > +{ > + char *output = output_str ? strdup(output_str) : NULL; > + char *sort = sort_str ? strdup(sort_str) : NULL; > + int ret = 0; > + > + if ((output_str && !output) || (sort_str && !sort)) { > + ret = -ENOMEM; > + goto out; > + } > + > + ret = function_hpp_list__add_tokens(hpp_list, output, env, > + c2c_function_hists__init_output); > + if (ret) > + goto out; > + > + ret = function_hpp_list__add_tokens(hpp_list, sort, env, > + c2c_function_hists__init_sort); > + if (ret) > + goto out; > + > + c2c_function_hists__setup_output_field(hpp_list); > +out: > + if (ret) > + perf_hpp__reset_output_field(hpp_list); > + free(output); > + free(sort); > + return ret; > +} > + > +static int __maybe_unused > +c2c_function_hists__init(struct c2c_hists *hists, > + const char *sort, > + int nr_header_lines, > + struct perf_env *env) > +{ > + __hists__init(&hists->hists, &hists->list); > + > + perf_hpp_list__init(&hists->list); > + > + hists->list.nr_header_lines = nr_header_lines; > + > + return function_hpp_list__parse(&hists->list, /*output=*/NULL, sort, env); > +} > + > +static int __maybe_unused > +c2c_function_hists__reinit(struct c2c_hists *c2c_hists, > + const char *output, > + const char *sort, > + struct perf_env *env) > +{ > + int nr_header_lines = c2c_hists->list.nr_header_lines; > + > + perf_hpp__reset_output_field(&c2c_hists->list); > + > + /* Clear stale state flags so a different output/sort set starts fresh. */ > + c2c_hists->list.need_collapse = 0; > + c2c_hists->list.parent = 0; > + c2c_hists->list.sym = 0; > + c2c_hists->list.dso = 0; > + c2c_hists->list.socket = 0; > + c2c_hists->list.thread = 0; > + c2c_hists->list.comm = 0; > + c2c_hists->list.comm_nodigit = 0; > + c2c_hists->list.nr_header_lines = nr_header_lines; > + > + return function_hpp_list__parse(&c2c_hists->list, output, sort, env); > +} > -- > 2.52.0 >