xref: /openbmc/linux/tools/perf/tests/parse-metric.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
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