Files
android_kernel_xiaomi_sm8450/tools/perf/tests/parse-metric.c
Jiri Olsa f6fb0960f9 perf metric: Add recursion check when processing nested metrics
Keeping the stack of nested metrics via 'struct expr_id' objects
and checking if we are in recursion via already processed metric.

The stack is implemented as static array within the struct egroup
with 100 entries, which should be enough nesting depth for any
metric we have or plan to have at the moment.

Adding test that simulates the recursion and checks we can
detect it.

Committer notes:

Bumped RECURSION_ID_MAX to 1000 as per Jiri's reply to Paul Clark on the
patch series e-mail discussion.

Fixed these:

  tests/parse-metric.c:308:7: error: missing field 'val' initializer [-Werror,-Wmissing-field-initializers]
                  { 0 },
                      ^

  util/metricgroup.c:924:28: error: missing field 'parent' initializer [-Werror,-Wmissing-field-initializers]
          struct expr_ids ids = { 0 };
                                    ^
  util/metricgroup.c:924:26: error: suggest braces around initialization of subobject [-Werror,-Wmissing-braces]
          struct expr_ids ids = { 0 };
                                  ^
                                  {}
  util/metricgroup.c:924:26: error: suggest braces around initialization of subobject [-Werror,-Wmissing-braces]
          struct expr_ids ids = { 0 };
                                  ^
                                  {}
  util/metricgroup.c:924:28: error: missing field 'cnt' initializer [-Werror,-Wmissing-field-initializers]
          struct expr_ids ids = { 0 };
                                    ^

Signed-off-by: Jiri Olsa <jolsa@kernel.org>
Reviewed-by: Kajol Jain <kjain@linux.ibm.com>
Acked-by: Ian Rogers <irogers@google.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: John Garry <john.garry@huawei.com>
Cc: Michael Petlan <mpetlan@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Clarke <pc@us.ibm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lore.kernel.org/lkml/20200719181320.785305-16-jolsa@kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-07-30 07:01:49 -03:00

305 lines
7.7 KiB
C

// SPDX-License-Identifier: GPL-2.0
#include <linux/compiler.h>
#include <string.h>
#include <perf/cpumap.h>
#include <perf/evlist.h>
#include "metricgroup.h"
#include "tests.h"
#include "pmu-events/pmu-events.h"
#include "evlist.h"
#include "rblist.h"
#include "debug.h"
#include "expr.h"
#include "stat.h"
#include <perf/cpumap.h>
#include <perf/evlist.h>
static struct pmu_event pme_test[] = {
{
.metric_expr = "inst_retired.any / cpu_clk_unhalted.thread",
.metric_name = "IPC",
},
{
.metric_expr = "idq_uops_not_delivered.core / (4 * (( ( cpu_clk_unhalted.thread / 2 ) * "
"( 1 + cpu_clk_unhalted.one_thread_active / cpu_clk_unhalted.ref_xclk ) )))",
.metric_name = "Frontend_Bound_SMT",
},
{
.metric_expr = "l1d\\-loads\\-misses / inst_retired.any",
.metric_name = "dcache_miss_cpi",
},
{
.metric_expr = "l1i\\-loads\\-misses / inst_retired.any",
.metric_name = "icache_miss_cycles",
},
{
.metric_expr = "(dcache_miss_cpi + icache_miss_cycles)",
.metric_name = "cache_miss_cycles",
},
{
.metric_expr = "l2_rqsts.demand_data_rd_hit + l2_rqsts.pf_hit + l2_rqsts.rfo_hit",
.metric_name = "DCache_L2_All_Hits",
},
{
.metric_expr = "max(l2_rqsts.all_demand_data_rd - l2_rqsts.demand_data_rd_hit, 0) + "
"l2_rqsts.pf_miss + l2_rqsts.rfo_miss",
.metric_name = "DCache_L2_All_Miss",
},
{
.metric_expr = "dcache_l2_all_hits + dcache_l2_all_miss",
.metric_name = "DCache_L2_All",
},
{
.metric_expr = "d_ratio(dcache_l2_all_hits, dcache_l2_all)",
.metric_name = "DCache_L2_Hits",
},
{
.metric_expr = "d_ratio(dcache_l2_all_miss, dcache_l2_all)",
.metric_name = "DCache_L2_Misses",
},
{
.metric_expr = "ipc + m2",
.metric_name = "M1",
},
{
.metric_expr = "ipc + m1",
.metric_name = "M2",
},
{
.metric_expr = "1/m3",
.metric_name = "M3",
}
};
static struct pmu_events_map map = {
.cpuid = "test",
.version = "1",
.type = "core",
.table = pme_test,
};
struct value {
const char *event;
u64 val;
};
static u64 find_value(const char *name, struct value *values)
{
struct value *v = values;
while (v->event) {
if (!strcmp(name, v->event))
return v->val;
v++;
};
return 0;
}
static void load_runtime_stat(struct runtime_stat *st, struct evlist *evlist,
struct value *vals)
{
struct evsel *evsel;
u64 count;
evlist__for_each_entry(evlist, evsel) {
count = find_value(evsel->name, vals);
perf_stat__update_shadow_stats(evsel, count, 0, st);
}
}
static double compute_single(struct rblist *metric_events, struct evlist *evlist,
struct runtime_stat *st)
{
struct evsel *evsel = evlist__first(evlist);
struct metric_event *me;
me = metricgroup__lookup(metric_events, evsel, false);
if (me != NULL) {
struct metric_expr *mexp;
mexp = list_first_entry(&me->head, struct metric_expr, nd);
return test_generic_metric(mexp, 0, st);
}
return 0.;
}
static int compute_metric(const char *name, struct value *vals, double *ratio)
{
struct rblist metric_events = {
.nr_entries = 0,
};
struct perf_cpu_map *cpus;
struct runtime_stat st;
struct evlist *evlist;
int err;
/*
* We need to prepare evlist for stat mode running on CPU 0
* because that's where all the stats are going to be created.
*/
evlist = evlist__new();
if (!evlist)
return -ENOMEM;
cpus = perf_cpu_map__new("0");
if (!cpus)
return -ENOMEM;
perf_evlist__set_maps(&evlist->core, cpus, NULL);
/* Parse the metric into metric_events list. */
err = metricgroup__parse_groups_test(evlist, &map, name,
false, false,
&metric_events);
if (err)
return err;
if (perf_evlist__alloc_stats(evlist, false))
return -1;
/* Load the runtime stats with given numbers for events. */
runtime_stat__init(&st);
load_runtime_stat(&st, evlist, vals);
/* And execute the metric */
*ratio = compute_single(&metric_events, evlist, &st);
/* ... clenup. */
metricgroup__rblist_exit(&metric_events);
runtime_stat__exit(&st);
perf_evlist__free_stats(evlist);
perf_cpu_map__put(cpus);
evlist__delete(evlist);
return 0;
}
static int test_ipc(void)
{
double ratio;
struct value vals[] = {
{ .event = "inst_retired.any", .val = 300 },
{ .event = "cpu_clk_unhalted.thread", .val = 200 },
{ .event = NULL, },
};
TEST_ASSERT_VAL("failed to compute metric",
compute_metric("IPC", vals, &ratio) == 0);
TEST_ASSERT_VAL("IPC failed, wrong ratio",
ratio == 1.5);
return 0;
}
static int test_frontend(void)
{
double ratio;
struct value vals[] = {
{ .event = "idq_uops_not_delivered.core", .val = 300 },
{ .event = "cpu_clk_unhalted.thread", .val = 200 },
{ .event = "cpu_clk_unhalted.one_thread_active", .val = 400 },
{ .event = "cpu_clk_unhalted.ref_xclk", .val = 600 },
{ .event = NULL, },
};
TEST_ASSERT_VAL("failed to compute metric",
compute_metric("Frontend_Bound_SMT", vals, &ratio) == 0);
TEST_ASSERT_VAL("Frontend_Bound_SMT failed, wrong ratio",
ratio == 0.45);
return 0;
}
static int test_cache_miss_cycles(void)
{
double ratio;
struct value vals[] = {
{ .event = "l1d-loads-misses", .val = 300 },
{ .event = "l1i-loads-misses", .val = 200 },
{ .event = "inst_retired.any", .val = 400 },
{ .event = NULL, },
};
TEST_ASSERT_VAL("failed to compute metric",
compute_metric("cache_miss_cycles", vals, &ratio) == 0);
TEST_ASSERT_VAL("cache_miss_cycles failed, wrong ratio",
ratio == 1.25);
return 0;
}
/*
* DCache_L2_All_Hits = l2_rqsts.demand_data_rd_hit + l2_rqsts.pf_hit + l2_rqsts.rfo_hi
* DCache_L2_All_Miss = max(l2_rqsts.all_demand_data_rd - l2_rqsts.demand_data_rd_hit, 0) +
* l2_rqsts.pf_miss + l2_rqsts.rfo_miss
* DCache_L2_All = dcache_l2_all_hits + dcache_l2_all_miss
* DCache_L2_Hits = d_ratio(dcache_l2_all_hits, dcache_l2_all)
* DCache_L2_Misses = d_ratio(dcache_l2_all_miss, dcache_l2_all)
*
* l2_rqsts.demand_data_rd_hit = 100
* l2_rqsts.pf_hit = 200
* l2_rqsts.rfo_hi = 300
* l2_rqsts.all_demand_data_rd = 400
* l2_rqsts.pf_miss = 500
* l2_rqsts.rfo_miss = 600
*
* DCache_L2_All_Hits = 600
* DCache_L2_All_Miss = MAX(400 - 100, 0) + 500 + 600 = 1400
* DCache_L2_All = 600 + 1400 = 2000
* DCache_L2_Hits = 600 / 2000 = 0.3
* DCache_L2_Misses = 1400 / 2000 = 0.7
*/
static int test_dcache_l2(void)
{
double ratio;
struct value vals[] = {
{ .event = "l2_rqsts.demand_data_rd_hit", .val = 100 },
{ .event = "l2_rqsts.pf_hit", .val = 200 },
{ .event = "l2_rqsts.rfo_hit", .val = 300 },
{ .event = "l2_rqsts.all_demand_data_rd", .val = 400 },
{ .event = "l2_rqsts.pf_miss", .val = 500 },
{ .event = "l2_rqsts.rfo_miss", .val = 600 },
{ .event = NULL, },
};
TEST_ASSERT_VAL("failed to compute metric",
compute_metric("DCache_L2_Hits", vals, &ratio) == 0);
TEST_ASSERT_VAL("DCache_L2_Hits failed, wrong ratio",
ratio == 0.3);
TEST_ASSERT_VAL("failed to compute metric",
compute_metric("DCache_L2_Misses", vals, &ratio) == 0);
TEST_ASSERT_VAL("DCache_L2_Misses failed, wrong ratio",
ratio == 0.7);
return 0;
}
static int test_recursion_fail(void)
{
double ratio;
struct value vals[] = {
{ .event = "inst_retired.any", .val = 300 },
{ .event = "cpu_clk_unhalted.thread", .val = 200 },
{ .event = NULL, },
};
TEST_ASSERT_VAL("failed to find recursion",
compute_metric("M1", vals, &ratio) == -1);
TEST_ASSERT_VAL("failed to find recursion",
compute_metric("M3", vals, &ratio) == -1);
return 0;
}
int test__parse_metric(struct test *test __maybe_unused, int subtest __maybe_unused)
{
TEST_ASSERT_VAL("IPC failed", test_ipc() == 0);
TEST_ASSERT_VAL("frontend failed", test_frontend() == 0);
TEST_ASSERT_VAL("cache_miss_cycles failed", test_cache_miss_cycles() == 0);
TEST_ASSERT_VAL("DCache_L2 failed", test_dcache_l2() == 0);
TEST_ASSERT_VAL("recursion fail failed", test_recursion_fail() == 0);
return 0;
}