mirror of
https://github.com/torvalds/linux.git
synced 2026-09-23 05:04:02 +02:00
perf c2c: add HPP list parsing for function view columns
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 <jiebin.sun@intel.com> Reviewed-by: Tianyou Li <tianyou.li@intel.com> Reviewed-by: Wangyang Guo <wangyang.guo@intel.com> Reviewed-by: Ian Rogers <irogers@google.com> Cc: Dapeng Mi <dapeng1.mi@linux.intel.com> Cc: James Clark <james.clark@linaro.org> Cc: Thomas Falcon <thomas.falcon@intel.com> Signed-off-by: Namhyung Kim <namhyung@kernel.org>
This commit is contained in:
parent
1b4eb7a2dd
commit
e888662432
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user