xref: /openbmc/linux/tools/perf/tests/parse-metric.c (revision 59b4412f)
1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/compiler.h>
3 #include <string.h>
4 #include <perf/cpumap.h>
5 #include <perf/evlist.h>
6 #include "metricgroup.h"
7 #include "tests.h"
8 #include "pmu-events/pmu-events.h"
9 #include "evlist.h"
10 #include "rblist.h"
11 #include "debug.h"
12 #include "expr.h"
13 #include "stat.h"
14 
15 static struct pmu_event pme_test[] = {
16 {
17 	.metric_expr	= "inst_retired.any / cpu_clk_unhalted.thread",
18 	.metric_name	= "IPC",
19 },
20 {
21 	.metric_expr	= "idq_uops_not_delivered.core / (4 * (( ( cpu_clk_unhalted.thread / 2 ) * "
22 			  "( 1 + cpu_clk_unhalted.one_thread_active / cpu_clk_unhalted.ref_xclk ) )))",
23 	.metric_name	= "Frontend_Bound_SMT",
24 },
25 };
26 
27 static struct pmu_events_map map = {
28 	.cpuid		= "test",
29 	.version	= "1",
30 	.type		= "core",
31 	.table		= pme_test,
32 };
33 
34 struct value {
35 	const char	*event;
36 	u64		 val;
37 };
38 
39 static u64 find_value(const char *name, struct value *values)
40 {
41 	struct value *v = values;
42 
43 	while (v->event) {
44 		if (!strcmp(name, v->event))
45 			return v->val;
46 		v++;
47 	};
48 	return 0;
49 }
50 
51 static void load_runtime_stat(struct runtime_stat *st, struct evlist *evlist,
52 			      struct value *vals)
53 {
54 	struct evsel *evsel;
55 	u64 count;
56 
57 	evlist__for_each_entry(evlist, evsel) {
58 		count = find_value(evsel->name, vals);
59 		perf_stat__update_shadow_stats(evsel, count, 0, st);
60 	}
61 }
62 
63 static double compute_single(struct rblist *metric_events, struct evlist *evlist,
64 			     struct runtime_stat *st)
65 {
66 	struct evsel *evsel = evlist__first(evlist);
67 	struct metric_event *me;
68 
69 	me = metricgroup__lookup(metric_events, evsel, false);
70 	if (me != NULL) {
71 		struct metric_expr *mexp;
72 
73 		mexp = list_first_entry(&me->head, struct metric_expr, nd);
74 		return test_generic_metric(mexp, 0, st);
75 	}
76 	return 0.;
77 }
78 
79 static int compute_metric(const char *name, struct value *vals, double *ratio)
80 {
81 	struct rblist metric_events = {
82 		.nr_entries = 0,
83 	};
84 	struct perf_cpu_map *cpus;
85 	struct runtime_stat st;
86 	struct evlist *evlist;
87 	int err;
88 
89 	/*
90 	 * We need to prepare evlist for stat mode running on CPU 0
91 	 * because that's where all the stats are going to be created.
92 	 */
93 	evlist = evlist__new();
94 	if (!evlist)
95 		return -ENOMEM;
96 
97 	cpus = perf_cpu_map__new("0");
98 	if (!cpus)
99 		return -ENOMEM;
100 
101 	perf_evlist__set_maps(&evlist->core, cpus, NULL);
102 
103 	/* Parse the metric into metric_events list. */
104 	err = metricgroup__parse_groups_test(evlist, &map, name,
105 					     false, false,
106 					     &metric_events);
107 
108 	TEST_ASSERT_VAL("failed to parse metric", err == 0);
109 
110 	if (perf_evlist__alloc_stats(evlist, false))
111 		return -1;
112 
113 	/* Load the runtime stats with given numbers for events. */
114 	runtime_stat__init(&st);
115 	load_runtime_stat(&st, evlist, vals);
116 
117 	/* And execute the metric */
118 	*ratio = compute_single(&metric_events, evlist, &st);
119 
120 	/* ... clenup. */
121 	metricgroup__rblist_exit(&metric_events);
122 	runtime_stat__exit(&st);
123 	perf_evlist__free_stats(evlist);
124 	perf_cpu_map__put(cpus);
125 	evlist__delete(evlist);
126 	return 0;
127 }
128 
129 static int test_ipc(void)
130 {
131 	double ratio;
132 	struct value vals[] = {
133 		{ .event = "inst_retired.any",        .val = 300 },
134 		{ .event = "cpu_clk_unhalted.thread", .val = 200 },
135 		{ .event = NULL, },
136 	};
137 
138 	TEST_ASSERT_VAL("failed to compute metric",
139 			compute_metric("IPC", vals, &ratio) == 0);
140 
141 	TEST_ASSERT_VAL("IPC failed, wrong ratio",
142 			ratio == 1.5);
143 	return 0;
144 }
145 
146 static int test_frontend(void)
147 {
148 	double ratio;
149 	struct value vals[] = {
150 		{ .event = "idq_uops_not_delivered.core",        .val = 300 },
151 		{ .event = "cpu_clk_unhalted.thread",            .val = 200 },
152 		{ .event = "cpu_clk_unhalted.one_thread_active", .val = 400 },
153 		{ .event = "cpu_clk_unhalted.ref_xclk",          .val = 600 },
154 		{ .event = NULL, },
155 	};
156 
157 	TEST_ASSERT_VAL("failed to compute metric",
158 			compute_metric("Frontend_Bound_SMT", vals, &ratio) == 0);
159 
160 	TEST_ASSERT_VAL("Frontend_Bound_SMT failed, wrong ratio",
161 			ratio == 0.45);
162 	return 0;
163 }
164 
165 int test__parse_metric(struct test *test __maybe_unused, int subtest __maybe_unused)
166 {
167 	TEST_ASSERT_VAL("IPC failed", test_ipc() == 0);
168 	TEST_ASSERT_VAL("frontend failed", test_frontend() == 0);
169 	return 0;
170 }
171