1 // SPDX-License-Identifier: GPL-2.0
3 #include "parse-events.h"
8 #include <linux/kernel.h>
9 #include <linux/zalloc.h>
11 #include "../pmu-events/pmu-events.h"
12 #include "util/evlist.h"
13 #include "util/expr.h"
14 #include "util/parse-events.h"
16 struct perf_pmu_test_event
{
17 struct pmu_event event
;
19 /* extra events for aliases */
20 const char *alias_str
;
23 * Note: For when PublicDescription does not exist in the JSON, we
24 * will have no long_desc in pmu_event.long_desc, but long_desc may
25 * be set in the alias.
27 const char *alias_long_desc
;
30 static struct perf_pmu_test_event test_cpu_events
[] = {
33 .name
= "bp_l1_btb_correct",
34 .event
= "event=0x8a",
35 .desc
= "L1 BTB Correction",
38 .alias_str
= "event=0x8a",
39 .alias_long_desc
= "L1 BTB Correction",
43 .name
= "bp_l2_btb_correct",
44 .event
= "event=0x8b",
45 .desc
= "L2 BTB Correction",
48 .alias_str
= "event=0x8b",
49 .alias_long_desc
= "L2 BTB Correction",
53 .name
= "segment_reg_loads.any",
54 .event
= "umask=0x80,period=200000,event=0x6",
55 .desc
= "Number of segment register loads",
58 .alias_str
= "umask=0x80,(null)=0x30d40,event=0x6",
59 .alias_long_desc
= "Number of segment register loads",
63 .name
= "dispatch_blocked.any",
64 .event
= "umask=0x20,period=200000,event=0x9",
65 .desc
= "Memory cluster signals to block micro-op dispatch for any reason",
68 .alias_str
= "umask=0x20,(null)=0x30d40,event=0x9",
69 .alias_long_desc
= "Memory cluster signals to block micro-op dispatch for any reason",
74 .event
= "umask=0x0,period=200000,event=0x3a",
75 .desc
= "Number of Enhanced Intel SpeedStep(R) Technology (EIST) transitions",
78 .alias_str
= "umask=0,(null)=0x30d40,event=0x3a",
79 .alias_long_desc
= "Number of Enhanced Intel SpeedStep(R) Technology (EIST) transitions",
88 static struct perf_pmu_test_event test_uncore_events
[] = {
91 .name
= "uncore_hisi_ddrc.flux_wcmd",
93 .desc
= "DDRC write commands. Unit: hisi_sccl,ddrc ",
95 .long_desc
= "DDRC write commands",
96 .pmu
= "hisi_sccl,ddrc",
98 .alias_str
= "event=0x2",
99 .alias_long_desc
= "DDRC write commands",
103 .name
= "unc_cbo_xsnp_response.miss_eviction",
104 .event
= "umask=0x81,event=0x22",
105 .desc
= "Unit: uncore_cbox A cross-core snoop resulted from L3 Eviction which misses in some processor core",
107 .long_desc
= "A cross-core snoop resulted from L3 Eviction which misses in some processor core",
108 .pmu
= "uncore_cbox",
110 .alias_str
= "umask=0x81,event=0x22",
111 .alias_long_desc
= "A cross-core snoop resulted from L3 Eviction which misses in some processor core",
120 const int total_test_events_size
= ARRAY_SIZE(test_uncore_events
);
122 static bool is_same(const char *reference
, const char *test
)
124 if (!reference
&& !test
)
127 if (reference
&& !test
)
130 if (!reference
&& test
)
133 return !strcmp(reference
, test
);
136 static struct pmu_events_map
*__test_pmu_get_events_map(void)
138 struct pmu_events_map
*map
;
140 for (map
= &pmu_events_map
[0]; map
->cpuid
; map
++) {
141 if (!strcmp(map
->cpuid
, "testcpu"))
145 pr_err("could not find test events map\n");
150 /* Verify generated events from pmu-events.c is as expected */
151 static int test_pmu_event_table(void)
153 struct pmu_events_map
*map
= __test_pmu_get_events_map();
154 struct pmu_event
*table
;
155 int map_events
= 0, expected_events
;
157 /* ignore 2x sentinels */
158 expected_events
= ARRAY_SIZE(test_cpu_events
) +
159 ARRAY_SIZE(test_uncore_events
) - 2;
164 for (table
= map
->table
; table
->name
; table
++) {
165 struct perf_pmu_test_event
*test
;
166 struct pmu_event
*te
;
170 test
= &test_uncore_events
[0];
172 test
= &test_cpu_events
[0];
176 for (; te
->name
; test
++, te
= &test
->event
) {
177 if (strcmp(table
->name
, te
->name
))
182 if (!is_same(table
->desc
, te
->desc
)) {
183 pr_debug2("testing event table %s: mismatched desc, %s vs %s\n",
184 table
->name
, table
->desc
, te
->desc
);
188 if (!is_same(table
->topic
, te
->topic
)) {
189 pr_debug2("testing event table %s: mismatched topic, %s vs %s\n",
190 table
->name
, table
->topic
,
195 if (!is_same(table
->long_desc
, te
->long_desc
)) {
196 pr_debug2("testing event table %s: mismatched long_desc, %s vs %s\n",
197 table
->name
, table
->long_desc
,
202 if (!is_same(table
->unit
, te
->unit
)) {
203 pr_debug2("testing event table %s: mismatched unit, %s vs %s\n",
204 table
->name
, table
->unit
,
209 if (!is_same(table
->perpkg
, te
->perpkg
)) {
210 pr_debug2("testing event table %s: mismatched perpkg, %s vs %s\n",
211 table
->name
, table
->perpkg
,
216 if (!is_same(table
->metric_expr
, te
->metric_expr
)) {
217 pr_debug2("testing event table %s: mismatched metric_expr, %s vs %s\n",
218 table
->name
, table
->metric_expr
,
223 if (!is_same(table
->metric_name
, te
->metric_name
)) {
224 pr_debug2("testing event table %s: mismatched metric_name, %s vs %s\n",
225 table
->name
, table
->metric_name
,
230 if (!is_same(table
->deprecated
, te
->deprecated
)) {
231 pr_debug2("testing event table %s: mismatched deprecated, %s vs %s\n",
232 table
->name
, table
->deprecated
,
237 pr_debug("testing event table %s: pass\n", table
->name
);
241 pr_err("testing event table: could not find event %s\n",
247 if (map_events
!= expected_events
) {
248 pr_err("testing event table: found %d, but expected %d\n",
249 map_events
, expected_events
);
256 static struct perf_pmu_alias
*find_alias(const char *test_event
, struct list_head
*aliases
)
258 struct perf_pmu_alias
*alias
;
260 list_for_each_entry(alias
, aliases
, list
)
261 if (!strcmp(test_event
, alias
->name
))
267 /* Verify aliases are as expected */
268 static int __test__pmu_event_aliases(char *pmu_name
, int *count
)
270 struct perf_pmu_test_event
*test
;
271 struct pmu_event
*te
;
272 struct perf_pmu
*pmu
;
275 bool use_uncore_table
;
276 struct pmu_events_map
*map
= __test_pmu_get_events_map();
281 if (is_pmu_core(pmu_name
)) {
282 test
= &test_cpu_events
[0];
283 use_uncore_table
= false;
285 test
= &test_uncore_events
[0];
286 use_uncore_table
= true;
289 pmu
= zalloc(sizeof(*pmu
));
293 pmu
->name
= pmu_name
;
295 pmu_add_cpu_aliases_map(&aliases
, pmu
, map
);
297 for (te
= &test
->event
; te
->name
; test
++, te
= &test
->event
) {
298 struct perf_pmu_alias
*alias
= find_alias(te
->name
, &aliases
);
301 bool uncore_match
= pmu_uncore_alias_match(pmu_name
,
304 if (use_uncore_table
&& !uncore_match
) {
305 pr_debug3("testing aliases PMU %s: skip matching alias %s\n",
310 pr_debug2("testing aliases PMU %s: no alias, alias_table->name=%s\n",
316 if (!is_same(alias
->desc
, te
->desc
)) {
317 pr_debug2("testing aliases PMU %s: mismatched desc, %s vs %s\n",
318 pmu_name
, alias
->desc
, te
->desc
);
323 if (!is_same(alias
->long_desc
, test
->alias_long_desc
)) {
324 pr_debug2("testing aliases PMU %s: mismatched long_desc, %s vs %s\n",
325 pmu_name
, alias
->long_desc
,
326 test
->alias_long_desc
);
331 if (!is_same(alias
->str
, test
->alias_str
)) {
332 pr_debug2("testing aliases PMU %s: mismatched str, %s vs %s\n",
333 pmu_name
, alias
->str
, test
->alias_str
);
338 if (!is_same(alias
->topic
, te
->topic
)) {
339 pr_debug2("testing aliases PMU %s: mismatched topic, %s vs %s\n",
340 pmu_name
, alias
->topic
, te
->topic
);
346 pr_debug2("testing aliases PMU %s: matched event %s\n",
347 pmu_name
, alias
->name
);
355 static int test_aliases(void)
357 struct perf_pmu
*pmu
= NULL
;
359 while ((pmu
= perf_pmu__scan(pmu
)) != NULL
) {
362 if (list_empty(&pmu
->format
)) {
363 pr_debug2("skipping testing PMU %s\n", pmu
->name
);
367 if (__test__pmu_event_aliases(pmu
->name
, &count
)) {
368 pr_debug("testing PMU %s aliases: failed\n", pmu
->name
);
373 pr_debug3("testing PMU %s aliases: no events to match\n",
376 pr_debug("testing PMU %s aliases: pass\n", pmu
->name
);
382 static bool is_number(const char *str
)
388 v
= strtod(str
, &end_ptr
);
389 (void)v
; // We're not interested in this value, only if it is valid
390 return errno
== 0 && end_ptr
!= str
;
393 static int check_parse_id(const char *id
, struct parse_events_error
*error
,
394 struct perf_pmu
*fake_pmu
)
396 struct evlist
*evlist
;
399 /* Numbers are always valid. */
403 evlist
= evlist__new();
406 ret
= __parse_events(evlist
, id
, error
, fake_pmu
);
407 evlist__delete(evlist
);
411 static int check_parse_cpu(const char *id
, bool same_cpu
, struct pmu_event
*pe
)
413 struct parse_events_error error
= { .idx
= 0, };
415 int ret
= check_parse_id(id
, &error
, NULL
);
416 if (ret
&& same_cpu
) {
417 pr_warning("Parse event failed metric '%s' id '%s' expr '%s'\n",
418 pe
->metric_name
, id
, pe
->metric_expr
);
419 pr_warning("Error string '%s' help '%s'\n", error
.str
,
422 pr_debug3("Parse event failed, but for an event that may not be supported by this CPU.\nid '%s' metric '%s' expr '%s'\n",
423 id
, pe
->metric_name
, pe
->metric_expr
);
428 free(error
.first_str
);
429 free(error
.first_help
);
433 static int check_parse_fake(const char *id
)
435 struct parse_events_error error
= { .idx
= 0, };
436 int ret
= check_parse_id(id
, &error
, &perf_pmu__fake
);
440 free(error
.first_str
);
441 free(error
.first_help
);
445 static void expr_failure(const char *msg
,
446 const struct pmu_events_map
*map
,
447 const struct pmu_event
*pe
)
449 pr_debug("%s for map %s %s %s\n",
450 msg
, map
->cpuid
, map
->version
, map
->type
);
451 pr_debug("On metric %s\n", pe
->metric_name
);
452 pr_debug("On expression %s\n", pe
->metric_expr
);
455 static int test_parsing(void)
457 struct pmu_events_map
*cpus_map
= perf_pmu__find_map(NULL
);
458 struct pmu_events_map
*map
;
459 struct pmu_event
*pe
;
462 struct expr_parse_ctx ctx
;
467 map
= &pmu_events_map
[i
++];
472 struct hashmap_entry
*cur
;
475 pe
= &map
->table
[j
++];
476 if (!pe
->name
&& !pe
->metric_group
&& !pe
->metric_name
)
478 if (!pe
->metric_expr
)
480 expr__ctx_init(&ctx
);
481 if (expr__find_other(pe
->metric_expr
, NULL
, &ctx
, 0)
483 expr_failure("Parse other failed", map
, pe
);
489 * Add all ids with a made up value. The value may
490 * trigger divide by zero when subtracted and so try to
494 hashmap__for_each_entry((&ctx
.ids
), cur
, bkt
)
495 expr__add_id_val(&ctx
, strdup(cur
->key
), k
++);
497 hashmap__for_each_entry((&ctx
.ids
), cur
, bkt
) {
498 if (check_parse_cpu(cur
->key
, map
== cpus_map
,
503 if (expr__parse(&result
, &ctx
, pe
->metric_expr
, 0)) {
504 expr_failure("Parse failed", map
, pe
);
507 expr__ctx_clear(&ctx
);
510 /* TODO: fail when not ok */
511 return ret
== 0 ? TEST_OK
: TEST_SKIP
;
518 static struct test_metric metrics
[] = {
519 { "(unc_p_power_state_occupancy.cores_c0 / unc_p_clockticks) * 100." },
520 { "imx8_ddr0@read\\-cycles@ * 4 * 4", },
521 { "imx8_ddr0@axid\\-read\\,axi_mask\\=0xffff\\,axi_id\\=0x0000@ * 4", },
522 { "(cstate_pkg@c2\\-residency@ / msr@tsc@) * 100", },
523 { "(imx8_ddr0@read\\-cycles@ + imx8_ddr0@write\\-cycles@)", },
526 static int metric_parse_fake(const char *str
)
528 struct expr_parse_ctx ctx
;
529 struct hashmap_entry
*cur
;
535 pr_debug("parsing '%s'\n", str
);
537 expr__ctx_init(&ctx
);
538 if (expr__find_other(str
, NULL
, &ctx
, 0) < 0) {
539 pr_err("expr__find_other failed\n");
544 * Add all ids with a made up value. The value may
545 * trigger divide by zero when subtracted and so try to
549 hashmap__for_each_entry((&ctx
.ids
), cur
, bkt
)
550 expr__add_id_val(&ctx
, strdup(cur
->key
), i
++);
552 hashmap__for_each_entry((&ctx
.ids
), cur
, bkt
) {
553 if (check_parse_fake(cur
->key
)) {
554 pr_err("check_parse_fake failed\n");
559 if (expr__parse(&result
, &ctx
, str
, 1))
560 pr_err("expr__parse failed\n");
565 expr__ctx_clear(&ctx
);
570 * Parse all the metrics for current architecture,
571 * or all defined cpus via the 'fake_pmu'
574 static int test_parsing_fake(void)
576 struct pmu_events_map
*map
;
577 struct pmu_event
*pe
;
581 for (i
= 0; i
< ARRAY_SIZE(metrics
); i
++) {
582 err
= metric_parse_fake(metrics
[i
].str
);
589 map
= &pmu_events_map
[i
++];
594 pe
= &map
->table
[j
++];
595 if (!pe
->name
&& !pe
->metric_group
&& !pe
->metric_name
)
597 if (!pe
->metric_expr
)
599 err
= metric_parse_fake(pe
->metric_expr
);
608 static const struct {
611 } pmu_events_testcase_table
[] = {
613 .func
= test_pmu_event_table
,
614 .desc
= "PMU event table sanity",
617 .func
= test_aliases
,
618 .desc
= "PMU event map aliases",
621 .func
= test_parsing
,
622 .desc
= "Parsing of PMU event table metrics",
625 .func
= test_parsing_fake
,
626 .desc
= "Parsing of PMU event table metrics with fake PMUs",
630 const char *test__pmu_events_subtest_get_desc(int subtest
)
633 subtest
>= (int)ARRAY_SIZE(pmu_events_testcase_table
))
635 return pmu_events_testcase_table
[subtest
].desc
;
638 const char *test__pmu_events_subtest_skip_reason(int subtest
)
641 subtest
>= (int)ARRAY_SIZE(pmu_events_testcase_table
))
643 if (pmu_events_testcase_table
[subtest
].func
!= test_parsing
)
645 return "some metrics failed";
648 int test__pmu_events_subtest_get_nr(void)
650 return (int)ARRAY_SIZE(pmu_events_testcase_table
);
653 int test__pmu_events(struct test
*test __maybe_unused
, int subtest
)
656 subtest
>= (int)ARRAY_SIZE(pmu_events_testcase_table
))
658 return pmu_events_testcase_table
[subtest
].func();