1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include "util/cputopo.h" |
3 | #include "util/debug.h" |
4 | #include "util/expr.h" |
5 | #include "util/hashmap.h" |
6 | #include "util/header.h" |
7 | #include "util/smt.h" |
8 | #include "tests.h" |
9 | #include <math.h> |
10 | #include <stdlib.h> |
11 | #include <string.h> |
12 | #include <string2.h> |
13 | #include <linux/zalloc.h> |
14 | |
15 | static int test_ids_union(void) |
16 | { |
17 | struct hashmap *ids1, *ids2; |
18 | |
19 | /* Empty union. */ |
20 | ids1 = ids__new(); |
21 | TEST_ASSERT_VAL("ids__new" , ids1); |
22 | ids2 = ids__new(); |
23 | TEST_ASSERT_VAL("ids__new" , ids2); |
24 | |
25 | ids1 = ids__union(ids1, ids2); |
26 | TEST_ASSERT_EQUAL("union" , (int)hashmap__size(ids1), 0); |
27 | |
28 | /* Union {foo, bar} against {}. */ |
29 | ids2 = ids__new(); |
30 | TEST_ASSERT_VAL("ids__new" , ids2); |
31 | |
32 | TEST_ASSERT_EQUAL("ids__insert" , ids__insert(ids1, strdup("foo" )), 0); |
33 | TEST_ASSERT_EQUAL("ids__insert" , ids__insert(ids1, strdup("bar" )), 0); |
34 | |
35 | ids1 = ids__union(ids1, ids2); |
36 | TEST_ASSERT_EQUAL("union" , (int)hashmap__size(ids1), 2); |
37 | |
38 | /* Union {foo, bar} against {foo}. */ |
39 | ids2 = ids__new(); |
40 | TEST_ASSERT_VAL("ids__new" , ids2); |
41 | TEST_ASSERT_EQUAL("ids__insert" , ids__insert(ids2, strdup("foo" )), 0); |
42 | |
43 | ids1 = ids__union(ids1, ids2); |
44 | TEST_ASSERT_EQUAL("union" , (int)hashmap__size(ids1), 2); |
45 | |
46 | /* Union {foo, bar} against {bar,baz}. */ |
47 | ids2 = ids__new(); |
48 | TEST_ASSERT_VAL("ids__new" , ids2); |
49 | TEST_ASSERT_EQUAL("ids__insert" , ids__insert(ids2, strdup("bar" )), 0); |
50 | TEST_ASSERT_EQUAL("ids__insert" , ids__insert(ids2, strdup("baz" )), 0); |
51 | |
52 | ids1 = ids__union(ids1, ids2); |
53 | TEST_ASSERT_EQUAL("union" , (int)hashmap__size(ids1), 3); |
54 | |
55 | ids__free(ids1); |
56 | |
57 | return 0; |
58 | } |
59 | |
60 | static int test(struct expr_parse_ctx *ctx, const char *e, double val2) |
61 | { |
62 | double val; |
63 | |
64 | if (expr__parse(&val, ctx, e)) |
65 | TEST_ASSERT_VAL("parse test failed" , 0); |
66 | TEST_ASSERT_VAL("unexpected value" , val == val2); |
67 | return 0; |
68 | } |
69 | |
70 | static int test__expr(struct test_suite *t __maybe_unused, int subtest __maybe_unused) |
71 | { |
72 | struct expr_id_data *val_ptr; |
73 | const char *p; |
74 | double val, num_cpus_online, num_cpus, num_cores, num_dies, num_packages; |
75 | int ret; |
76 | struct expr_parse_ctx *ctx; |
77 | bool is_intel = false; |
78 | char strcmp_cpuid_buf[256]; |
79 | struct perf_pmu *pmu = perf_pmus__find_core_pmu(); |
80 | char *cpuid = perf_pmu__getcpuid(pmu); |
81 | char *escaped_cpuid1, *escaped_cpuid2; |
82 | |
83 | TEST_ASSERT_VAL("get_cpuid" , cpuid); |
84 | is_intel = strstr(cpuid, "Intel" ) != NULL; |
85 | |
86 | TEST_ASSERT_EQUAL("ids_union" , test_ids_union(), 0); |
87 | |
88 | ctx = expr__ctx_new(); |
89 | TEST_ASSERT_VAL("expr__ctx_new" , ctx); |
90 | expr__add_id_val(ctx, strdup("FOO" ), 1); |
91 | expr__add_id_val(ctx, strdup("BAR" ), 2); |
92 | |
93 | ret = test(ctx, e: "1+1" , val2: 2); |
94 | ret |= test(ctx, e: "FOO+BAR" , val2: 3); |
95 | ret |= test(ctx, e: "(BAR/2)%2" , val2: 1); |
96 | ret |= test(ctx, e: "1 - -4" , val2: 5); |
97 | ret |= test(ctx, e: "(FOO-1)*2 + (BAR/2)%2 - -4" , val2: 5); |
98 | ret |= test(ctx, e: "1-1 | 1" , val2: 1); |
99 | ret |= test(ctx, e: "1-1 & 1" , val2: 0); |
100 | ret |= test(ctx, e: "min(1,2) + 1" , val2: 2); |
101 | ret |= test(ctx, e: "max(1,2) + 1" , val2: 3); |
102 | ret |= test(ctx, e: "1+1 if 3*4 else 0" , val2: 2); |
103 | ret |= test(ctx, e: "100 if 1 else 200 if 1 else 300" , val2: 100); |
104 | ret |= test(ctx, e: "100 if 0 else 200 if 1 else 300" , val2: 200); |
105 | ret |= test(ctx, e: "100 if 1 else 200 if 0 else 300" , val2: 100); |
106 | ret |= test(ctx, e: "100 if 0 else 200 if 0 else 300" , val2: 300); |
107 | ret |= test(ctx, e: "1.1 + 2.1" , val2: 3.2); |
108 | ret |= test(ctx, e: ".1 + 2." , val2: 2.1); |
109 | ret |= test(ctx, e: "d_ratio(1, 2)" , val2: 0.5); |
110 | ret |= test(ctx, e: "d_ratio(2.5, 0)" , val2: 0); |
111 | ret |= test(ctx, e: "1.1 < 2.2" , val2: 1); |
112 | ret |= test(ctx, e: "2.2 > 1.1" , val2: 1); |
113 | ret |= test(ctx, e: "1.1 < 1.1" , val2: 0); |
114 | ret |= test(ctx, e: "2.2 > 2.2" , val2: 0); |
115 | ret |= test(ctx, e: "2.2 < 1.1" , val2: 0); |
116 | ret |= test(ctx, e: "1.1 > 2.2" , val2: 0); |
117 | ret |= test(ctx, e: "1.1e10 < 1.1e100" , val2: 1); |
118 | ret |= test(ctx, e: "1.1e2 > 1.1e-2" , val2: 1); |
119 | |
120 | if (ret) { |
121 | expr__ctx_free(ctx); |
122 | return ret; |
123 | } |
124 | |
125 | p = "FOO/0" ; |
126 | ret = expr__parse(&val, ctx, p); |
127 | TEST_ASSERT_VAL("division by zero" , ret == 0); |
128 | TEST_ASSERT_VAL("division by zero" , isnan(val)); |
129 | |
130 | p = "BAR/" ; |
131 | ret = expr__parse(&val, ctx, p); |
132 | TEST_ASSERT_VAL("missing operand" , ret == -1); |
133 | |
134 | expr__ctx_clear(ctx); |
135 | TEST_ASSERT_VAL("find ids" , |
136 | expr__find_ids("FOO + BAR + BAZ + BOZO" , "FOO" , |
137 | ctx) == 0); |
138 | TEST_ASSERT_VAL("find ids" , hashmap__size(ctx->ids) == 3); |
139 | TEST_ASSERT_VAL("find ids" , hashmap__find(ctx->ids, "BAR" , &val_ptr)); |
140 | TEST_ASSERT_VAL("find ids" , hashmap__find(ctx->ids, "BAZ" , &val_ptr)); |
141 | TEST_ASSERT_VAL("find ids" , hashmap__find(ctx->ids, "BOZO" , &val_ptr)); |
142 | |
143 | expr__ctx_clear(ctx); |
144 | ctx->sctx.runtime = 3; |
145 | TEST_ASSERT_VAL("find ids" , |
146 | expr__find_ids("EVENT1\\,param\\=?@ + EVENT2\\,param\\=?@" , |
147 | NULL, ctx) == 0); |
148 | TEST_ASSERT_VAL("find ids" , hashmap__size(ctx->ids) == 2); |
149 | TEST_ASSERT_VAL("find ids" , hashmap__find(ctx->ids, "EVENT1,param=3@" , &val_ptr)); |
150 | TEST_ASSERT_VAL("find ids" , hashmap__find(ctx->ids, "EVENT2,param=3@" , &val_ptr)); |
151 | |
152 | expr__ctx_clear(ctx); |
153 | TEST_ASSERT_VAL("find ids" , |
154 | expr__find_ids("dash\\-event1 - dash\\-event2" , |
155 | NULL, ctx) == 0); |
156 | TEST_ASSERT_VAL("find ids" , hashmap__size(ctx->ids) == 2); |
157 | TEST_ASSERT_VAL("find ids" , hashmap__find(ctx->ids, "dash-event1" , &val_ptr)); |
158 | TEST_ASSERT_VAL("find ids" , hashmap__find(ctx->ids, "dash-event2" , &val_ptr)); |
159 | |
160 | /* Only EVENT1 or EVENT2 need be measured depending on the value of smt_on. */ |
161 | { |
162 | bool smton = smt_on(); |
163 | bool corewide = core_wide(/*system_wide=*/false, |
164 | /*user_requested_cpus=*/false); |
165 | |
166 | expr__ctx_clear(ctx); |
167 | TEST_ASSERT_VAL("find ids" , |
168 | expr__find_ids("EVENT1 if #smt_on else EVENT2" , |
169 | NULL, ctx) == 0); |
170 | TEST_ASSERT_VAL("find ids" , hashmap__size(ctx->ids) == 1); |
171 | TEST_ASSERT_VAL("find ids" , hashmap__find(ctx->ids, |
172 | smton ? "EVENT1" : "EVENT2" , |
173 | &val_ptr)); |
174 | |
175 | expr__ctx_clear(ctx); |
176 | TEST_ASSERT_VAL("find ids" , |
177 | expr__find_ids("EVENT1 if #core_wide else EVENT2" , |
178 | NULL, ctx) == 0); |
179 | TEST_ASSERT_VAL("find ids" , hashmap__size(ctx->ids) == 1); |
180 | TEST_ASSERT_VAL("find ids" , hashmap__find(ctx->ids, |
181 | corewide ? "EVENT1" : "EVENT2" , |
182 | &val_ptr)); |
183 | |
184 | } |
185 | /* The expression is a constant 1.0 without needing to evaluate EVENT1. */ |
186 | expr__ctx_clear(ctx); |
187 | TEST_ASSERT_VAL("find ids" , |
188 | expr__find_ids("1.0 if EVENT1 > 100.0 else 1.0" , |
189 | NULL, ctx) == 0); |
190 | TEST_ASSERT_VAL("find ids" , hashmap__size(ctx->ids) == 0); |
191 | |
192 | /* The expression is a constant 0.0 without needing to evaluate EVENT1. */ |
193 | expr__ctx_clear(ctx); |
194 | TEST_ASSERT_VAL("find ids" , |
195 | expr__find_ids("0 & EVENT1 > 0" , NULL, ctx) == 0); |
196 | TEST_ASSERT_VAL("find ids" , hashmap__size(ctx->ids) == 0); |
197 | expr__ctx_clear(ctx); |
198 | TEST_ASSERT_VAL("find ids" , |
199 | expr__find_ids("EVENT1 > 0 & 0" , NULL, ctx) == 0); |
200 | TEST_ASSERT_VAL("find ids" , hashmap__size(ctx->ids) == 0); |
201 | expr__ctx_clear(ctx); |
202 | TEST_ASSERT_VAL("find ids" , |
203 | expr__find_ids("1 & EVENT1 > 0" , NULL, ctx) == 0); |
204 | TEST_ASSERT_VAL("find ids" , hashmap__size(ctx->ids) == 1); |
205 | TEST_ASSERT_VAL("find ids" , hashmap__find(ctx->ids, "EVENT1" , &val_ptr)); |
206 | expr__ctx_clear(ctx); |
207 | TEST_ASSERT_VAL("find ids" , |
208 | expr__find_ids("EVENT1 > 0 & 1" , NULL, ctx) == 0); |
209 | TEST_ASSERT_VAL("find ids" , hashmap__size(ctx->ids) == 1); |
210 | TEST_ASSERT_VAL("find ids" , hashmap__find(ctx->ids, "EVENT1" , &val_ptr)); |
211 | |
212 | /* The expression is a constant 1.0 without needing to evaluate EVENT1. */ |
213 | expr__ctx_clear(ctx); |
214 | TEST_ASSERT_VAL("find ids" , |
215 | expr__find_ids("1 | EVENT1 > 0" , NULL, ctx) == 0); |
216 | TEST_ASSERT_VAL("find ids" , hashmap__size(ctx->ids) == 0); |
217 | expr__ctx_clear(ctx); |
218 | TEST_ASSERT_VAL("find ids" , |
219 | expr__find_ids("EVENT1 > 0 | 1" , NULL, ctx) == 0); |
220 | TEST_ASSERT_VAL("find ids" , hashmap__size(ctx->ids) == 0); |
221 | expr__ctx_clear(ctx); |
222 | TEST_ASSERT_VAL("find ids" , |
223 | expr__find_ids("0 | EVENT1 > 0" , NULL, ctx) == 0); |
224 | TEST_ASSERT_VAL("find ids" , hashmap__size(ctx->ids) == 1); |
225 | TEST_ASSERT_VAL("find ids" , hashmap__find(ctx->ids, "EVENT1" , &val_ptr)); |
226 | expr__ctx_clear(ctx); |
227 | TEST_ASSERT_VAL("find ids" , |
228 | expr__find_ids("EVENT1 > 0 | 0" , NULL, ctx) == 0); |
229 | TEST_ASSERT_VAL("find ids" , hashmap__size(ctx->ids) == 1); |
230 | TEST_ASSERT_VAL("find ids" , hashmap__find(ctx->ids, "EVENT1" , &val_ptr)); |
231 | |
232 | /* Test toplogy constants appear well ordered. */ |
233 | expr__ctx_clear(ctx); |
234 | TEST_ASSERT_VAL("#num_cpus_online" , |
235 | expr__parse(&num_cpus_online, ctx, "#num_cpus_online" ) == 0); |
236 | TEST_ASSERT_VAL("#num_cpus" , expr__parse(&num_cpus, ctx, "#num_cpus" ) == 0); |
237 | TEST_ASSERT_VAL("#num_cpus >= #num_cpus_online" , num_cpus >= num_cpus_online); |
238 | TEST_ASSERT_VAL("#num_cores" , expr__parse(&num_cores, ctx, "#num_cores" ) == 0); |
239 | TEST_ASSERT_VAL("#num_cpus >= #num_cores" , num_cpus >= num_cores); |
240 | TEST_ASSERT_VAL("#num_dies" , expr__parse(&num_dies, ctx, "#num_dies" ) == 0); |
241 | TEST_ASSERT_VAL("#num_cores >= #num_dies" , num_cores >= num_dies); |
242 | TEST_ASSERT_VAL("#num_packages" , expr__parse(&num_packages, ctx, "#num_packages" ) == 0); |
243 | |
244 | if (num_dies) // Some platforms do not have CPU die support, for example s390 |
245 | TEST_ASSERT_VAL("#num_dies >= #num_packages" , num_dies >= num_packages); |
246 | |
247 | TEST_ASSERT_VAL("#system_tsc_freq" , expr__parse(&val, ctx, "#system_tsc_freq" ) == 0); |
248 | if (is_intel) |
249 | TEST_ASSERT_VAL("#system_tsc_freq > 0" , val > 0); |
250 | else |
251 | TEST_ASSERT_VAL("#system_tsc_freq == 0" , fpclassify(val) == FP_ZERO); |
252 | |
253 | /* |
254 | * Source count returns the number of events aggregating in a leader |
255 | * event including the leader. Check parsing yields an id. |
256 | */ |
257 | expr__ctx_clear(ctx); |
258 | TEST_ASSERT_VAL("source count" , |
259 | expr__find_ids("source_count(EVENT1)" , |
260 | NULL, ctx) == 0); |
261 | TEST_ASSERT_VAL("source count" , hashmap__size(ctx->ids) == 1); |
262 | TEST_ASSERT_VAL("source count" , hashmap__find(ctx->ids, "EVENT1" , &val_ptr)); |
263 | |
264 | |
265 | /* Test no cpuid match */ |
266 | ret = test(ctx, e: "strcmp_cpuid_str(0x0)" , val2: 0); |
267 | |
268 | /* |
269 | * Test cpuid match with current cpuid. Special chars have to be |
270 | * escaped. |
271 | */ |
272 | escaped_cpuid1 = strreplace_chars('-', cpuid, "\\-" ); |
273 | free(cpuid); |
274 | escaped_cpuid2 = strreplace_chars(',', escaped_cpuid1, "\\," ); |
275 | free(escaped_cpuid1); |
276 | escaped_cpuid1 = strreplace_chars('=', escaped_cpuid2, "\\=" ); |
277 | free(escaped_cpuid2); |
278 | scnprintf(strcmp_cpuid_buf, sizeof(strcmp_cpuid_buf), |
279 | "strcmp_cpuid_str(%s)" , escaped_cpuid1); |
280 | free(escaped_cpuid1); |
281 | ret |= test(ctx, e: strcmp_cpuid_buf, val2: 1); |
282 | |
283 | /* has_event returns 1 when an event exists. */ |
284 | expr__add_id_val(ctx, strdup("cycles" ), 2); |
285 | ret |= test(ctx, e: "has_event(cycles)" , val2: 1); |
286 | |
287 | expr__ctx_free(ctx); |
288 | |
289 | return ret; |
290 | } |
291 | |
292 | DEFINE_SUITE("Simple expression parser" , expr); |
293 | |