10a507af9SJiri Olsa // SPDX-License-Identifier: GPL-2.0
20a507af9SJiri Olsa #include <linux/compiler.h>
30a507af9SJiri Olsa #include <string.h>
40a507af9SJiri Olsa #include <perf/cpumap.h>
50a507af9SJiri Olsa #include <perf/evlist.h>
60a507af9SJiri Olsa #include "metricgroup.h"
70a507af9SJiri Olsa #include "tests.h"
80a507af9SJiri Olsa #include "pmu-events/pmu-events.h"
90a507af9SJiri Olsa #include "evlist.h"
100a507af9SJiri Olsa #include "rblist.h"
110a507af9SJiri Olsa #include "debug.h"
120a507af9SJiri Olsa #include "expr.h"
130a507af9SJiri Olsa #include "stat.h"
146081e876SJin Yao #include "pmus.h"
150a507af9SJiri Olsa
160a507af9SJiri Olsa struct value {
170a507af9SJiri Olsa const char *event;
180a507af9SJiri Olsa u64 val;
190a507af9SJiri Olsa };
200a507af9SJiri Olsa
find_value(const char * name,struct value * values)210a507af9SJiri Olsa static u64 find_value(const char *name, struct value *values)
220a507af9SJiri Olsa {
230a507af9SJiri Olsa struct value *v = values;
240a507af9SJiri Olsa
250a507af9SJiri Olsa while (v->event) {
260a507af9SJiri Olsa if (!strcmp(name, v->event))
270a507af9SJiri Olsa return v->val;
280a507af9SJiri Olsa v++;
2981db00a4SJiapeng Chong }
300a507af9SJiri Olsa return 0;
310a507af9SJiri Olsa }
320a507af9SJiri Olsa
load_runtime_stat(struct evlist * evlist,struct value * vals)33cc26ffaaSIan Rogers static void load_runtime_stat(struct evlist *evlist, struct value *vals)
340a507af9SJiri Olsa {
350a507af9SJiri Olsa struct evsel *evsel;
360a507af9SJiri Olsa u64 count;
370a507af9SJiri Olsa
3837cc8ad7SIan Rogers evlist__alloc_aggr_stats(evlist, 1);
390a507af9SJiri Olsa evlist__for_each_entry(evlist, evsel) {
400a507af9SJiri Olsa count = find_value(evsel->name, vals);
41*2a939c86SIan Rogers evsel->supported = true;
4237cc8ad7SIan Rogers evsel->stats->aggr->counts.val = count;
43ce1d3bc2SArnaldo Carvalho de Melo if (evsel__name_is(evsel, "duration_time"))
447efce5c2SJohn Garry update_stats(&walltime_nsecs_stats, count);
450a507af9SJiri Olsa }
460a507af9SJiri Olsa }
470a507af9SJiri Olsa
compute_single(struct rblist * metric_events,struct evlist * evlist,const char * name)480a507af9SJiri Olsa static double compute_single(struct rblist *metric_events, struct evlist *evlist,
49cc26ffaaSIan Rogers const char *name)
500a507af9SJiri Olsa {
51b81ef466SJiri Olsa struct metric_expr *mexp;
520a507af9SJiri Olsa struct metric_event *me;
53b81ef466SJiri Olsa struct evsel *evsel;
540a507af9SJiri Olsa
55b81ef466SJiri Olsa evlist__for_each_entry(evlist, evsel) {
560a507af9SJiri Olsa me = metricgroup__lookup(metric_events, evsel, false);
570a507af9SJiri Olsa if (me != NULL) {
58b81ef466SJiri Olsa list_for_each_entry (mexp, &me->head, nd) {
59b81ef466SJiri Olsa if (strcmp(mexp->metric_name, name))
60b81ef466SJiri Olsa continue;
61cc26ffaaSIan Rogers return test_generic_metric(mexp, 0);
620a507af9SJiri Olsa }
63b81ef466SJiri Olsa }
64b81ef466SJiri Olsa }
650a507af9SJiri Olsa return 0.;
660a507af9SJiri Olsa }
670a507af9SJiri Olsa
__compute_metric(const char * name,struct value * vals,const char * name1,double * ratio1,const char * name2,double * ratio2)68dfce77c5SJiri Olsa static int __compute_metric(const char *name, struct value *vals,
69dfce77c5SJiri Olsa const char *name1, double *ratio1,
70dfce77c5SJiri Olsa const char *name2, double *ratio2)
710a507af9SJiri Olsa {
720a507af9SJiri Olsa struct rblist metric_events = {
730a507af9SJiri Olsa .nr_entries = 0,
740a507af9SJiri Olsa };
75f8ea2c15SIan Rogers const struct pmu_metrics_table *pme_test;
760a507af9SJiri Olsa struct perf_cpu_map *cpus;
770a507af9SJiri Olsa struct evlist *evlist;
780a507af9SJiri Olsa int err;
790a507af9SJiri Olsa
800a507af9SJiri Olsa /*
810a507af9SJiri Olsa * We need to prepare evlist for stat mode running on CPU 0
820a507af9SJiri Olsa * because that's where all the stats are going to be created.
830a507af9SJiri Olsa */
840a507af9SJiri Olsa evlist = evlist__new();
850a507af9SJiri Olsa if (!evlist)
860a507af9SJiri Olsa return -ENOMEM;
870a507af9SJiri Olsa
880a507af9SJiri Olsa cpus = perf_cpu_map__new("0");
89f5a56570SNamhyung Kim if (!cpus) {
90f5a56570SNamhyung Kim evlist__delete(evlist);
910a507af9SJiri Olsa return -ENOMEM;
92f5a56570SNamhyung Kim }
930a507af9SJiri Olsa
940a507af9SJiri Olsa perf_evlist__set_maps(&evlist->core, cpus, NULL);
950a507af9SJiri Olsa
960a507af9SJiri Olsa /* Parse the metric into metric_events list. */
9796d2a746SIan Rogers pme_test = find_core_metrics_table("testarch", "testcpu");
98eeac7730SIan Rogers err = metricgroup__parse_groups_test(evlist, pme_test, name,
990a507af9SJiri Olsa &metric_events);
100f6fb0960SJiri Olsa if (err)
101f5a56570SNamhyung Kim goto out;
1020a507af9SJiri Olsa
1031f297a6eSNamhyung Kim err = evlist__alloc_stats(/*config=*/NULL, evlist, /*alloc_raw=*/false);
104f5a56570SNamhyung Kim if (err)
105f5a56570SNamhyung Kim goto out;
1060a507af9SJiri Olsa
1070a507af9SJiri Olsa /* Load the runtime stats with given numbers for events. */
108cc26ffaaSIan Rogers load_runtime_stat(evlist, vals);
1090a507af9SJiri Olsa
1100a507af9SJiri Olsa /* And execute the metric */
111dfce77c5SJiri Olsa if (name1 && ratio1)
112cc26ffaaSIan Rogers *ratio1 = compute_single(&metric_events, evlist, name1);
113dfce77c5SJiri Olsa if (name2 && ratio2)
114cc26ffaaSIan Rogers *ratio2 = compute_single(&metric_events, evlist, name2);
1150a507af9SJiri Olsa
116f5a56570SNamhyung Kim out:
1174d39c89fSIngo Molnar /* ... cleanup. */
1180a507af9SJiri Olsa metricgroup__rblist_exit(&metric_events);
11953f5e908SArnaldo Carvalho de Melo evlist__free_stats(evlist);
1200a507af9SJiri Olsa perf_cpu_map__put(cpus);
1210a507af9SJiri Olsa evlist__delete(evlist);
122f5a56570SNamhyung Kim return err;
1230a507af9SJiri Olsa }
1240a507af9SJiri Olsa
compute_metric(const char * name,struct value * vals,double * ratio)125dfce77c5SJiri Olsa static int compute_metric(const char *name, struct value *vals, double *ratio)
126dfce77c5SJiri Olsa {
127dfce77c5SJiri Olsa return __compute_metric(name, vals, name, ratio, NULL, NULL);
128dfce77c5SJiri Olsa }
129dfce77c5SJiri Olsa
compute_metric_group(const char * name,struct value * vals,const char * name1,double * ratio1,const char * name2,double * ratio2)130dfce77c5SJiri Olsa static int compute_metric_group(const char *name, struct value *vals,
131dfce77c5SJiri Olsa const char *name1, double *ratio1,
132dfce77c5SJiri Olsa const char *name2, double *ratio2)
133dfce77c5SJiri Olsa {
134dfce77c5SJiri Olsa return __compute_metric(name, vals, name1, ratio1, name2, ratio2);
135dfce77c5SJiri Olsa }
136dfce77c5SJiri Olsa
test_ipc(void)1370a507af9SJiri Olsa static int test_ipc(void)
1380a507af9SJiri Olsa {
1390a507af9SJiri Olsa double ratio;
1400a507af9SJiri Olsa struct value vals[] = {
1410a507af9SJiri Olsa { .event = "inst_retired.any", .val = 300 },
1420a507af9SJiri Olsa { .event = "cpu_clk_unhalted.thread", .val = 200 },
1430a507af9SJiri Olsa { .event = NULL, },
1440a507af9SJiri Olsa };
1450a507af9SJiri Olsa
1460a507af9SJiri Olsa TEST_ASSERT_VAL("failed to compute metric",
1470a507af9SJiri Olsa compute_metric("IPC", vals, &ratio) == 0);
1480a507af9SJiri Olsa
1490a507af9SJiri Olsa TEST_ASSERT_VAL("IPC failed, wrong ratio",
1500a507af9SJiri Olsa ratio == 1.5);
1510a507af9SJiri Olsa return 0;
1520a507af9SJiri Olsa }
1530a507af9SJiri Olsa
test_frontend(void)154218ca91dSJiri Olsa static int test_frontend(void)
155218ca91dSJiri Olsa {
156218ca91dSJiri Olsa double ratio;
157218ca91dSJiri Olsa struct value vals[] = {
158218ca91dSJiri Olsa { .event = "idq_uops_not_delivered.core", .val = 300 },
159218ca91dSJiri Olsa { .event = "cpu_clk_unhalted.thread", .val = 200 },
160218ca91dSJiri Olsa { .event = "cpu_clk_unhalted.one_thread_active", .val = 400 },
161218ca91dSJiri Olsa { .event = "cpu_clk_unhalted.ref_xclk", .val = 600 },
162218ca91dSJiri Olsa { .event = NULL, },
163218ca91dSJiri Olsa };
164218ca91dSJiri Olsa
165218ca91dSJiri Olsa TEST_ASSERT_VAL("failed to compute metric",
166218ca91dSJiri Olsa compute_metric("Frontend_Bound_SMT", vals, &ratio) == 0);
167218ca91dSJiri Olsa
168218ca91dSJiri Olsa TEST_ASSERT_VAL("Frontend_Bound_SMT failed, wrong ratio",
169218ca91dSJiri Olsa ratio == 0.45);
170218ca91dSJiri Olsa return 0;
171218ca91dSJiri Olsa }
172218ca91dSJiri Olsa
test_cache_miss_cycles(void)17355f30d68SJiri Olsa static int test_cache_miss_cycles(void)
17455f30d68SJiri Olsa {
17555f30d68SJiri Olsa double ratio;
17655f30d68SJiri Olsa struct value vals[] = {
17755f30d68SJiri Olsa { .event = "l1d-loads-misses", .val = 300 },
17855f30d68SJiri Olsa { .event = "l1i-loads-misses", .val = 200 },
17955f30d68SJiri Olsa { .event = "inst_retired.any", .val = 400 },
18055f30d68SJiri Olsa { .event = NULL, },
18155f30d68SJiri Olsa };
18255f30d68SJiri Olsa
18355f30d68SJiri Olsa TEST_ASSERT_VAL("failed to compute metric",
18455f30d68SJiri Olsa compute_metric("cache_miss_cycles", vals, &ratio) == 0);
18555f30d68SJiri Olsa
18655f30d68SJiri Olsa TEST_ASSERT_VAL("cache_miss_cycles failed, wrong ratio",
18755f30d68SJiri Olsa ratio == 1.25);
18855f30d68SJiri Olsa return 0;
18955f30d68SJiri Olsa }
19055f30d68SJiri Olsa
1915a606f3bSJiri Olsa
1925a606f3bSJiri Olsa /*
1935a606f3bSJiri Olsa * DCache_L2_All_Hits = l2_rqsts.demand_data_rd_hit + l2_rqsts.pf_hit + l2_rqsts.rfo_hi
1945a606f3bSJiri Olsa * DCache_L2_All_Miss = max(l2_rqsts.all_demand_data_rd - l2_rqsts.demand_data_rd_hit, 0) +
1955a606f3bSJiri Olsa * l2_rqsts.pf_miss + l2_rqsts.rfo_miss
1965a606f3bSJiri Olsa * DCache_L2_All = dcache_l2_all_hits + dcache_l2_all_miss
1975a606f3bSJiri Olsa * DCache_L2_Hits = d_ratio(dcache_l2_all_hits, dcache_l2_all)
1985a606f3bSJiri Olsa * DCache_L2_Misses = d_ratio(dcache_l2_all_miss, dcache_l2_all)
1995a606f3bSJiri Olsa *
2005a606f3bSJiri Olsa * l2_rqsts.demand_data_rd_hit = 100
2015a606f3bSJiri Olsa * l2_rqsts.pf_hit = 200
2025a606f3bSJiri Olsa * l2_rqsts.rfo_hi = 300
2035a606f3bSJiri Olsa * l2_rqsts.all_demand_data_rd = 400
2045a606f3bSJiri Olsa * l2_rqsts.pf_miss = 500
2055a606f3bSJiri Olsa * l2_rqsts.rfo_miss = 600
2065a606f3bSJiri Olsa *
2075a606f3bSJiri Olsa * DCache_L2_All_Hits = 600
2085a606f3bSJiri Olsa * DCache_L2_All_Miss = MAX(400 - 100, 0) + 500 + 600 = 1400
2095a606f3bSJiri Olsa * DCache_L2_All = 600 + 1400 = 2000
2105a606f3bSJiri Olsa * DCache_L2_Hits = 600 / 2000 = 0.3
2115a606f3bSJiri Olsa * DCache_L2_Misses = 1400 / 2000 = 0.7
2125a606f3bSJiri Olsa */
test_dcache_l2(void)2135a606f3bSJiri Olsa static int test_dcache_l2(void)
2145a606f3bSJiri Olsa {
2155a606f3bSJiri Olsa double ratio;
2165a606f3bSJiri Olsa struct value vals[] = {
2175a606f3bSJiri Olsa { .event = "l2_rqsts.demand_data_rd_hit", .val = 100 },
2185a606f3bSJiri Olsa { .event = "l2_rqsts.pf_hit", .val = 200 },
2195a606f3bSJiri Olsa { .event = "l2_rqsts.rfo_hit", .val = 300 },
2205a606f3bSJiri Olsa { .event = "l2_rqsts.all_demand_data_rd", .val = 400 },
2215a606f3bSJiri Olsa { .event = "l2_rqsts.pf_miss", .val = 500 },
2225a606f3bSJiri Olsa { .event = "l2_rqsts.rfo_miss", .val = 600 },
2235a606f3bSJiri Olsa { .event = NULL, },
2245a606f3bSJiri Olsa };
2255a606f3bSJiri Olsa
2265a606f3bSJiri Olsa TEST_ASSERT_VAL("failed to compute metric",
2275a606f3bSJiri Olsa compute_metric("DCache_L2_Hits", vals, &ratio) == 0);
2285a606f3bSJiri Olsa
2295a606f3bSJiri Olsa TEST_ASSERT_VAL("DCache_L2_Hits failed, wrong ratio",
2305a606f3bSJiri Olsa ratio == 0.3);
2315a606f3bSJiri Olsa
2325a606f3bSJiri Olsa TEST_ASSERT_VAL("failed to compute metric",
2335a606f3bSJiri Olsa compute_metric("DCache_L2_Misses", vals, &ratio) == 0);
2345a606f3bSJiri Olsa
2355a606f3bSJiri Olsa TEST_ASSERT_VAL("DCache_L2_Misses failed, wrong ratio",
2365a606f3bSJiri Olsa ratio == 0.7);
2375a606f3bSJiri Olsa return 0;
2385a606f3bSJiri Olsa }
2395a606f3bSJiri Olsa
test_recursion_fail(void)240f6fb0960SJiri Olsa static int test_recursion_fail(void)
241f6fb0960SJiri Olsa {
242f6fb0960SJiri Olsa double ratio;
243f6fb0960SJiri Olsa struct value vals[] = {
244f6fb0960SJiri Olsa { .event = "inst_retired.any", .val = 300 },
245f6fb0960SJiri Olsa { .event = "cpu_clk_unhalted.thread", .val = 200 },
246f6fb0960SJiri Olsa { .event = NULL, },
247f6fb0960SJiri Olsa };
248f6fb0960SJiri Olsa
249f6fb0960SJiri Olsa TEST_ASSERT_VAL("failed to find recursion",
250f6fb0960SJiri Olsa compute_metric("M1", vals, &ratio) == -1);
251f6fb0960SJiri Olsa
252f6fb0960SJiri Olsa TEST_ASSERT_VAL("failed to find recursion",
253f6fb0960SJiri Olsa compute_metric("M3", vals, &ratio) == -1);
254f6fb0960SJiri Olsa return 0;
255f6fb0960SJiri Olsa }
256f6fb0960SJiri Olsa
test_memory_bandwidth(void)2577efce5c2SJohn Garry static int test_memory_bandwidth(void)
2587efce5c2SJohn Garry {
2597efce5c2SJohn Garry double ratio;
2607efce5c2SJohn Garry struct value vals[] = {
2617efce5c2SJohn Garry { .event = "l1d.replacement", .val = 4000000 },
2627efce5c2SJohn Garry { .event = "duration_time", .val = 200000000 },
2637efce5c2SJohn Garry { .event = NULL, },
2647efce5c2SJohn Garry };
2657efce5c2SJohn Garry
2667efce5c2SJohn Garry TEST_ASSERT_VAL("failed to compute metric",
2677efce5c2SJohn Garry compute_metric("L1D_Cache_Fill_BW", vals, &ratio) == 0);
2687efce5c2SJohn Garry TEST_ASSERT_VAL("L1D_Cache_Fill_BW, wrong ratio",
2697efce5c2SJohn Garry 1.28 == ratio);
2707efce5c2SJohn Garry
2717efce5c2SJohn Garry return 0;
2727efce5c2SJohn Garry }
2737efce5c2SJohn Garry
test_metric_group(void)274dfce77c5SJiri Olsa static int test_metric_group(void)
275dfce77c5SJiri Olsa {
276dfce77c5SJiri Olsa double ratio1, ratio2;
277dfce77c5SJiri Olsa struct value vals[] = {
278dfce77c5SJiri Olsa { .event = "cpu_clk_unhalted.thread", .val = 200 },
279dfce77c5SJiri Olsa { .event = "l1d-loads-misses", .val = 300 },
280dfce77c5SJiri Olsa { .event = "l1i-loads-misses", .val = 200 },
281dfce77c5SJiri Olsa { .event = "inst_retired.any", .val = 400 },
282dfce77c5SJiri Olsa { .event = NULL, },
283dfce77c5SJiri Olsa };
284dfce77c5SJiri Olsa
285dfce77c5SJiri Olsa TEST_ASSERT_VAL("failed to find recursion",
286dfce77c5SJiri Olsa compute_metric_group("group1", vals,
287dfce77c5SJiri Olsa "IPC", &ratio1,
288dfce77c5SJiri Olsa "cache_miss_cycles", &ratio2) == 0);
289dfce77c5SJiri Olsa
290dfce77c5SJiri Olsa TEST_ASSERT_VAL("group IPC failed, wrong ratio",
291dfce77c5SJiri Olsa ratio1 == 2.0);
292dfce77c5SJiri Olsa
293dfce77c5SJiri Olsa TEST_ASSERT_VAL("group cache_miss_cycles failed, wrong ratio",
294dfce77c5SJiri Olsa ratio2 == 1.25);
295dfce77c5SJiri Olsa return 0;
296dfce77c5SJiri Olsa }
297dfce77c5SJiri Olsa
test__parse_metric(struct test_suite * test __maybe_unused,int subtest __maybe_unused)29833f44bfdSIan Rogers static int test__parse_metric(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
2990a507af9SJiri Olsa {
3000a507af9SJiri Olsa TEST_ASSERT_VAL("IPC failed", test_ipc() == 0);
301218ca91dSJiri Olsa TEST_ASSERT_VAL("frontend failed", test_frontend() == 0);
3025a606f3bSJiri Olsa TEST_ASSERT_VAL("DCache_L2 failed", test_dcache_l2() == 0);
303f6fb0960SJiri Olsa TEST_ASSERT_VAL("recursion fail failed", test_recursion_fail() == 0);
3047efce5c2SJohn Garry TEST_ASSERT_VAL("Memory bandwidth", test_memory_bandwidth() == 0);
3056081e876SJin Yao TEST_ASSERT_VAL("cache_miss_cycles failed", test_cache_miss_cycles() == 0);
3066081e876SJin Yao TEST_ASSERT_VAL("test metric group", test_metric_group() == 0);
3076081e876SJin Yao return 0;
3086081e876SJin Yao }
3096081e876SJin Yao
3100a507af9SJiri Olsa DEFINE_SUITE("Parse and process metrics", parse_metric);
3110a507af9SJiri Olsa