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 /* used for matching against events from generated pmu-events.c */
18 struct pmu_event event
;
20 /* used for matching against event aliases */
21 /* extra events for aliases */
22 const char *alias_str
;
25 * Note: For when PublicDescription does not exist in the JSON, we
26 * will have no long_desc in pmu_event.long_desc, but long_desc may
27 * be set in the alias.
29 const char *alias_long_desc
;
32 static struct perf_pmu_test_event test_cpu_events
[] = {
35 .name
= "bp_l1_btb_correct",
36 .event
= "event=0x8a",
37 .desc
= "L1 BTB Correction",
40 .alias_str
= "event=0x8a",
41 .alias_long_desc
= "L1 BTB Correction",
45 .name
= "bp_l2_btb_correct",
46 .event
= "event=0x8b",
47 .desc
= "L2 BTB Correction",
50 .alias_str
= "event=0x8b",
51 .alias_long_desc
= "L2 BTB Correction",
55 .name
= "segment_reg_loads.any",
56 .event
= "umask=0x80,period=200000,event=0x6",
57 .desc
= "Number of segment register loads",
60 .alias_str
= "umask=0x80,(null)=0x30d40,event=0x6",
61 .alias_long_desc
= "Number of segment register loads",
65 .name
= "dispatch_blocked.any",
66 .event
= "umask=0x20,period=200000,event=0x9",
67 .desc
= "Memory cluster signals to block micro-op dispatch for any reason",
70 .alias_str
= "umask=0x20,(null)=0x30d40,event=0x9",
71 .alias_long_desc
= "Memory cluster signals to block micro-op dispatch for any reason",
76 .event
= "umask=0x0,period=200000,event=0x3a",
77 .desc
= "Number of Enhanced Intel SpeedStep(R) Technology (EIST) transitions",
80 .alias_str
= "umask=0,(null)=0x30d40,event=0x3a",
81 .alias_long_desc
= "Number of Enhanced Intel SpeedStep(R) Technology (EIST) transitions",
85 .name
= "l3_cache_rd",
86 .event
= "event=0x40",
87 .desc
= "L3 cache access, read",
88 .long_desc
= "Attributable Level 3 cache access, read",
91 .alias_str
= "event=0x40",
92 .alias_long_desc
= "Attributable Level 3 cache access, read",
101 static struct perf_pmu_test_event test_uncore_events
[] = {
104 .name
= "uncore_hisi_ddrc.flux_wcmd",
105 .event
= "event=0x2",
106 .desc
= "DDRC write commands. Unit: hisi_sccl,ddrc ",
108 .long_desc
= "DDRC write commands",
109 .pmu
= "hisi_sccl,ddrc",
111 .alias_str
= "event=0x2",
112 .alias_long_desc
= "DDRC write commands",
116 .name
= "unc_cbo_xsnp_response.miss_eviction",
117 .event
= "umask=0x81,event=0x22",
118 .desc
= "Unit: uncore_cbox A cross-core snoop resulted from L3 Eviction which misses in some processor core",
120 .long_desc
= "A cross-core snoop resulted from L3 Eviction which misses in some processor core",
121 .pmu
= "uncore_cbox",
123 .alias_str
= "umask=0x81,event=0x22",
124 .alias_long_desc
= "A cross-core snoop resulted from L3 Eviction which misses in some processor core",
133 const int total_test_events_size
= ARRAY_SIZE(test_uncore_events
);
135 static bool is_same(const char *reference
, const char *test
)
137 if (!reference
&& !test
)
140 if (reference
&& !test
)
143 if (!reference
&& test
)
146 return !strcmp(reference
, test
);
149 static struct pmu_events_map
*__test_pmu_get_events_map(void)
151 struct pmu_events_map
*map
;
153 for (map
= &pmu_events_map
[0]; map
->cpuid
; map
++) {
154 if (!strcmp(map
->cpuid
, "testcpu"))
158 pr_err("could not find test events map\n");
163 /* Verify generated events from pmu-events.c is as expected */
164 static int test_pmu_event_table(void)
166 struct pmu_events_map
*map
= __test_pmu_get_events_map();
167 struct pmu_event
*table
;
168 int map_events
= 0, expected_events
;
170 /* ignore 2x sentinels */
171 expected_events
= ARRAY_SIZE(test_cpu_events
) +
172 ARRAY_SIZE(test_uncore_events
) - 2;
177 for (table
= map
->table
; table
->name
; table
++) {
178 struct perf_pmu_test_event
*test
;
179 struct pmu_event
*te
;
183 test
= &test_uncore_events
[0];
185 test
= &test_cpu_events
[0];
189 for (; te
->name
; test
++, te
= &test
->event
) {
190 if (strcmp(table
->name
, te
->name
))
195 if (!is_same(table
->desc
, te
->desc
)) {
196 pr_debug2("testing event table %s: mismatched desc, %s vs %s\n",
197 table
->name
, table
->desc
, te
->desc
);
201 if (!is_same(table
->topic
, te
->topic
)) {
202 pr_debug2("testing event table %s: mismatched topic, %s vs %s\n",
203 table
->name
, table
->topic
,
208 if (!is_same(table
->long_desc
, te
->long_desc
)) {
209 pr_debug2("testing event table %s: mismatched long_desc, %s vs %s\n",
210 table
->name
, table
->long_desc
,
215 if (!is_same(table
->unit
, te
->unit
)) {
216 pr_debug2("testing event table %s: mismatched unit, %s vs %s\n",
217 table
->name
, table
->unit
,
222 if (!is_same(table
->perpkg
, te
->perpkg
)) {
223 pr_debug2("testing event table %s: mismatched perpkg, %s vs %s\n",
224 table
->name
, table
->perpkg
,
229 if (!is_same(table
->metric_expr
, te
->metric_expr
)) {
230 pr_debug2("testing event table %s: mismatched metric_expr, %s vs %s\n",
231 table
->name
, table
->metric_expr
,
236 if (!is_same(table
->metric_name
, te
->metric_name
)) {
237 pr_debug2("testing event table %s: mismatched metric_name, %s vs %s\n",
238 table
->name
, table
->metric_name
,
243 if (!is_same(table
->deprecated
, te
->deprecated
)) {
244 pr_debug2("testing event table %s: mismatched deprecated, %s vs %s\n",
245 table
->name
, table
->deprecated
,
250 pr_debug("testing event table %s: pass\n", table
->name
);
254 pr_err("testing event table: could not find event %s\n",
260 if (map_events
!= expected_events
) {
261 pr_err("testing event table: found %d, but expected %d\n",
262 map_events
, expected_events
);
269 static struct perf_pmu_alias
*find_alias(const char *test_event
, struct list_head
*aliases
)
271 struct perf_pmu_alias
*alias
;
273 list_for_each_entry(alias
, aliases
, list
)
274 if (!strcmp(test_event
, alias
->name
))
280 /* Verify aliases are as expected */
281 static int __test__pmu_event_aliases(char *pmu_name
, int *count
)
283 struct perf_pmu_test_event
*test
;
284 struct pmu_event
*te
;
285 struct perf_pmu
*pmu
;
288 bool use_uncore_table
;
289 struct pmu_events_map
*map
= __test_pmu_get_events_map();
290 struct perf_pmu_alias
*a
, *tmp
;
295 if (is_pmu_core(pmu_name
)) {
296 test
= &test_cpu_events
[0];
297 use_uncore_table
= false;
299 test
= &test_uncore_events
[0];
300 use_uncore_table
= true;
303 pmu
= zalloc(sizeof(*pmu
));
307 pmu
->name
= pmu_name
;
309 pmu_add_cpu_aliases_map(&aliases
, pmu
, map
);
311 for (te
= &test
->event
; te
->name
; test
++, te
= &test
->event
) {
312 struct perf_pmu_alias
*alias
= find_alias(te
->name
, &aliases
);
315 bool uncore_match
= pmu_uncore_alias_match(pmu_name
,
318 if (use_uncore_table
&& !uncore_match
) {
319 pr_debug3("testing aliases PMU %s: skip matching alias %s\n",
324 pr_debug2("testing aliases PMU %s: no alias, alias_table->name=%s\n",
330 if (!is_same(alias
->desc
, te
->desc
)) {
331 pr_debug2("testing aliases PMU %s: mismatched desc, %s vs %s\n",
332 pmu_name
, alias
->desc
, te
->desc
);
337 if (!is_same(alias
->long_desc
, test
->alias_long_desc
)) {
338 pr_debug2("testing aliases PMU %s: mismatched long_desc, %s vs %s\n",
339 pmu_name
, alias
->long_desc
,
340 test
->alias_long_desc
);
345 if (!is_same(alias
->str
, test
->alias_str
)) {
346 pr_debug2("testing aliases PMU %s: mismatched str, %s vs %s\n",
347 pmu_name
, alias
->str
, test
->alias_str
);
352 if (!is_same(alias
->topic
, te
->topic
)) {
353 pr_debug2("testing aliases PMU %s: mismatched topic, %s vs %s\n",
354 pmu_name
, alias
->topic
, te
->topic
);
360 pr_debug2("testing aliases PMU %s: matched event %s\n",
361 pmu_name
, alias
->name
);
364 list_for_each_entry_safe(a
, tmp
, &aliases
, list
) {
366 perf_pmu_free_alias(a
);
373 /* Test that aliases generated are as expected */
374 static int test_aliases(void)
376 struct perf_pmu
*pmu
= NULL
;
378 while ((pmu
= perf_pmu__scan(pmu
)) != NULL
) {
381 if (list_empty(&pmu
->format
)) {
382 pr_debug2("skipping testing PMU %s\n", pmu
->name
);
386 if (__test__pmu_event_aliases(pmu
->name
, &count
)) {
387 pr_debug("testing PMU %s aliases: failed\n", pmu
->name
);
392 pr_debug3("testing PMU %s aliases: no events to match\n",
395 pr_debug("testing PMU %s aliases: pass\n", pmu
->name
);
401 static bool is_number(const char *str
)
407 v
= strtod(str
, &end_ptr
);
408 (void)v
; // We're not interested in this value, only if it is valid
409 return errno
== 0 && end_ptr
!= str
;
412 static int check_parse_id(const char *id
, struct parse_events_error
*error
,
413 struct perf_pmu
*fake_pmu
)
415 struct evlist
*evlist
;
418 /* Numbers are always valid. */
422 evlist
= evlist__new();
425 ret
= __parse_events(evlist
, id
, error
, fake_pmu
);
426 evlist__delete(evlist
);
430 static int check_parse_cpu(const char *id
, bool same_cpu
, struct pmu_event
*pe
)
432 struct parse_events_error error
= { .idx
= 0, };
434 int ret
= check_parse_id(id
, &error
, NULL
);
435 if (ret
&& same_cpu
) {
436 pr_warning("Parse event failed metric '%s' id '%s' expr '%s'\n",
437 pe
->metric_name
, id
, pe
->metric_expr
);
438 pr_warning("Error string '%s' help '%s'\n", error
.str
,
441 pr_debug3("Parse event failed, but for an event that may not be supported by this CPU.\nid '%s' metric '%s' expr '%s'\n",
442 id
, pe
->metric_name
, pe
->metric_expr
);
447 free(error
.first_str
);
448 free(error
.first_help
);
452 static int check_parse_fake(const char *id
)
454 struct parse_events_error error
= { .idx
= 0, };
455 int ret
= check_parse_id(id
, &error
, &perf_pmu__fake
);
459 free(error
.first_str
);
460 free(error
.first_help
);
464 static void expr_failure(const char *msg
,
465 const struct pmu_events_map
*map
,
466 const struct pmu_event
*pe
)
468 pr_debug("%s for map %s %s %s\n",
469 msg
, map
->cpuid
, map
->version
, map
->type
);
470 pr_debug("On metric %s\n", pe
->metric_name
);
471 pr_debug("On expression %s\n", pe
->metric_expr
);
474 static int test_parsing(void)
476 struct pmu_events_map
*cpus_map
= perf_pmu__find_map(NULL
);
477 struct pmu_events_map
*map
;
478 struct pmu_event
*pe
;
481 struct expr_parse_ctx ctx
;
486 map
= &pmu_events_map
[i
++];
491 struct hashmap_entry
*cur
;
494 pe
= &map
->table
[j
++];
495 if (!pe
->name
&& !pe
->metric_group
&& !pe
->metric_name
)
497 if (!pe
->metric_expr
)
499 expr__ctx_init(&ctx
);
500 if (expr__find_other(pe
->metric_expr
, NULL
, &ctx
, 0)
502 expr_failure("Parse other failed", map
, pe
);
508 * Add all ids with a made up value. The value may
509 * trigger divide by zero when subtracted and so try to
513 hashmap__for_each_entry((&ctx
.ids
), cur
, bkt
)
514 expr__add_id_val(&ctx
, strdup(cur
->key
), k
++);
516 hashmap__for_each_entry((&ctx
.ids
), cur
, bkt
) {
517 if (check_parse_cpu(cur
->key
, map
== cpus_map
,
522 if (expr__parse(&result
, &ctx
, pe
->metric_expr
, 0)) {
523 expr_failure("Parse failed", map
, pe
);
526 expr__ctx_clear(&ctx
);
529 /* TODO: fail when not ok */
530 return ret
== 0 ? TEST_OK
: TEST_SKIP
;
537 static struct test_metric metrics
[] = {
538 { "(unc_p_power_state_occupancy.cores_c0 / unc_p_clockticks) * 100." },
539 { "imx8_ddr0@read\\-cycles@ * 4 * 4", },
540 { "imx8_ddr0@axid\\-read\\,axi_mask\\=0xffff\\,axi_id\\=0x0000@ * 4", },
541 { "(cstate_pkg@c2\\-residency@ / msr@tsc@) * 100", },
542 { "(imx8_ddr0@read\\-cycles@ + imx8_ddr0@write\\-cycles@)", },
545 static int metric_parse_fake(const char *str
)
547 struct expr_parse_ctx ctx
;
548 struct hashmap_entry
*cur
;
554 pr_debug("parsing '%s'\n", str
);
556 expr__ctx_init(&ctx
);
557 if (expr__find_other(str
, NULL
, &ctx
, 0) < 0) {
558 pr_err("expr__find_other failed\n");
563 * Add all ids with a made up value. The value may
564 * trigger divide by zero when subtracted and so try to
568 hashmap__for_each_entry((&ctx
.ids
), cur
, bkt
)
569 expr__add_id_val(&ctx
, strdup(cur
->key
), i
++);
571 hashmap__for_each_entry((&ctx
.ids
), cur
, bkt
) {
572 if (check_parse_fake(cur
->key
)) {
573 pr_err("check_parse_fake failed\n");
578 if (expr__parse(&result
, &ctx
, str
, 0))
579 pr_err("expr__parse failed\n");
584 expr__ctx_clear(&ctx
);
589 * Parse all the metrics for current architecture,
590 * or all defined cpus via the 'fake_pmu'
593 static int test_parsing_fake(void)
595 struct pmu_events_map
*map
;
596 struct pmu_event
*pe
;
600 for (i
= 0; i
< ARRAY_SIZE(metrics
); i
++) {
601 err
= metric_parse_fake(metrics
[i
].str
);
608 map
= &pmu_events_map
[i
++];
613 pe
= &map
->table
[j
++];
614 if (!pe
->name
&& !pe
->metric_group
&& !pe
->metric_name
)
616 if (!pe
->metric_expr
)
618 err
= metric_parse_fake(pe
->metric_expr
);
627 static const struct {
630 } pmu_events_testcase_table
[] = {
632 .func
= test_pmu_event_table
,
633 .desc
= "PMU event table sanity",
636 .func
= test_aliases
,
637 .desc
= "PMU event map aliases",
640 .func
= test_parsing
,
641 .desc
= "Parsing of PMU event table metrics",
644 .func
= test_parsing_fake
,
645 .desc
= "Parsing of PMU event table metrics with fake PMUs",
649 const char *test__pmu_events_subtest_get_desc(int subtest
)
652 subtest
>= (int)ARRAY_SIZE(pmu_events_testcase_table
))
654 return pmu_events_testcase_table
[subtest
].desc
;
657 const char *test__pmu_events_subtest_skip_reason(int subtest
)
660 subtest
>= (int)ARRAY_SIZE(pmu_events_testcase_table
))
662 if (pmu_events_testcase_table
[subtest
].func
!= test_parsing
)
664 return "some metrics failed";
667 int test__pmu_events_subtest_get_nr(void)
669 return (int)ARRAY_SIZE(pmu_events_testcase_table
);
672 int test__pmu_events(struct test
*test __maybe_unused
, int subtest
)
675 subtest
>= (int)ARRAY_SIZE(pmu_events_testcase_table
))
677 return pmu_events_testcase_table
[subtest
].func();