perf metric: Rename expr__add_id() to expr__add_val()
[linux-2.6-microblaze.git] / tools / perf / tests / pmu-events.c
1 // SPDX-License-Identifier: GPL-2.0
2 #include "math.h"
3 #include "parse-events.h"
4 #include "pmu.h"
5 #include "tests.h"
6 #include <errno.h>
7 #include <stdio.h>
8 #include <linux/kernel.h>
9 #include <linux/zalloc.h>
10 #include "debug.h"
11 #include "../pmu-events/pmu-events.h"
12 #include "util/evlist.h"
13 #include "util/expr.h"
14 #include "util/parse-events.h"
15
16 struct perf_pmu_test_event {
17         struct pmu_event event;
18
19         /* extra events for aliases */
20         const char *alias_str;
21
22         /*
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.
26          */
27         const char *alias_long_desc;
28 };
29
30 static struct perf_pmu_test_event test_cpu_events[] = {
31         {
32                 .event = {
33                         .name = "bp_l1_btb_correct",
34                         .event = "event=0x8a",
35                         .desc = "L1 BTB Correction",
36                         .topic = "branch",
37                 },
38                 .alias_str = "event=0x8a",
39                 .alias_long_desc = "L1 BTB Correction",
40         },
41         {
42                 .event = {
43                         .name = "bp_l2_btb_correct",
44                         .event = "event=0x8b",
45                         .desc = "L2 BTB Correction",
46                         .topic = "branch",
47                 },
48                 .alias_str = "event=0x8b",
49                 .alias_long_desc = "L2 BTB Correction",
50         },
51         {
52                 .event = {
53                         .name = "segment_reg_loads.any",
54                         .event = "umask=0x80,period=200000,event=0x6",
55                         .desc = "Number of segment register loads",
56                         .topic = "other",
57                 },
58                 .alias_str = "umask=0x80,(null)=0x30d40,event=0x6",
59                 .alias_long_desc = "Number of segment register loads",
60         },
61         {
62                 .event = {
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",
66                         .topic = "other",
67                 },
68                 .alias_str = "umask=0x20,(null)=0x30d40,event=0x9",
69                 .alias_long_desc = "Memory cluster signals to block micro-op dispatch for any reason",
70         },
71         {
72                 .event = {
73                         .name = "eist_trans",
74                         .event = "umask=0x0,period=200000,event=0x3a",
75                         .desc = "Number of Enhanced Intel SpeedStep(R) Technology (EIST) transitions",
76                         .topic = "other",
77                 },
78                 .alias_str = "umask=0,(null)=0x30d40,event=0x3a",
79                 .alias_long_desc = "Number of Enhanced Intel SpeedStep(R) Technology (EIST) transitions",
80         },
81         { /* sentinel */
82                 .event = {
83                         .name = NULL,
84                 },
85         },
86 };
87
88 static struct perf_pmu_test_event test_uncore_events[] = {
89         {
90                 .event = {
91                         .name = "uncore_hisi_ddrc.flux_wcmd",
92                         .event = "event=0x2",
93                         .desc = "DDRC write commands. Unit: hisi_sccl,ddrc ",
94                         .topic = "uncore",
95                         .long_desc = "DDRC write commands",
96                         .pmu = "hisi_sccl,ddrc",
97                 },
98                 .alias_str = "event=0x2",
99                 .alias_long_desc = "DDRC write commands",
100         },
101         {
102                 .event = {
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",
106                         .topic = "uncore",
107                         .long_desc = "A cross-core snoop resulted from L3 Eviction which misses in some processor core",
108                         .pmu = "uncore_cbox",
109                 },
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",
112         },
113         { /* sentinel */
114                 .event = {
115                         .name = NULL,
116                 },
117         }
118 };
119
120 const int total_test_events_size = ARRAY_SIZE(test_uncore_events);
121
122 static bool is_same(const char *reference, const char *test)
123 {
124         if (!reference && !test)
125                 return true;
126
127         if (reference && !test)
128                 return false;
129
130         if (!reference && test)
131                 return false;
132
133         return !strcmp(reference, test);
134 }
135
136 static struct pmu_events_map *__test_pmu_get_events_map(void)
137 {
138         struct pmu_events_map *map;
139
140         for (map = &pmu_events_map[0]; map->cpuid; map++) {
141                 if (!strcmp(map->cpuid, "testcpu"))
142                         return map;
143         }
144
145         pr_err("could not find test events map\n");
146
147         return NULL;
148 }
149
150 /* Verify generated events from pmu-events.c is as expected */
151 static int test_pmu_event_table(void)
152 {
153         struct pmu_events_map *map = __test_pmu_get_events_map();
154         struct pmu_event *table;
155         int map_events = 0, expected_events;
156
157         /* ignore 2x sentinels */
158         expected_events = ARRAY_SIZE(test_cpu_events) +
159                           ARRAY_SIZE(test_uncore_events) - 2;
160
161         if (!map)
162                 return -1;
163
164         for (table = map->table; table->name; table++) {
165                 struct perf_pmu_test_event *test;
166                 struct pmu_event *te;
167                 bool found = false;
168
169                 if (table->pmu)
170                         test = &test_uncore_events[0];
171                 else
172                         test = &test_cpu_events[0];
173
174                 te = &test->event;
175
176                 for (; te->name; test++, te = &test->event) {
177                         if (strcmp(table->name, te->name))
178                                 continue;
179                         found = true;
180                         map_events++;
181
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);
185                                 return -1;
186                         }
187
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,
191                                           te->topic);
192                                 return -1;
193                         }
194
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,
198                                           te->long_desc);
199                                 return -1;
200                         }
201
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,
205                                           te->unit);
206                                 return -1;
207                         }
208
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,
212                                           te->perpkg);
213                                 return -1;
214                         }
215
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,
219                                           te->metric_expr);
220                                 return -1;
221                         }
222
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,
226                                           te->metric_name);
227                                 return -1;
228                         }
229
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,
233                                           te->deprecated);
234                                 return -1;
235                         }
236
237                         pr_debug("testing event table %s: pass\n", table->name);
238                 }
239
240                 if (!found) {
241                         pr_err("testing event table: could not find event %s\n",
242                                table->name);
243                         return -1;
244                 }
245         }
246
247         if (map_events != expected_events) {
248                 pr_err("testing event table: found %d, but expected %d\n",
249                        map_events, expected_events);
250                 return -1;
251         }
252
253         return 0;
254 }
255
256 static struct perf_pmu_alias *find_alias(const char *test_event, struct list_head *aliases)
257 {
258         struct perf_pmu_alias *alias;
259
260         list_for_each_entry(alias, aliases, list)
261                 if (!strcmp(test_event, alias->name))
262                         return alias;
263
264         return NULL;
265 }
266
267 /* Verify aliases are as expected */
268 static int __test__pmu_event_aliases(char *pmu_name, int *count)
269 {
270         struct perf_pmu_test_event *test;
271         struct pmu_event *te;
272         struct perf_pmu *pmu;
273         LIST_HEAD(aliases);
274         int res = 0;
275         bool use_uncore_table;
276         struct pmu_events_map *map = __test_pmu_get_events_map();
277
278         if (!map)
279                 return -1;
280
281         if (is_pmu_core(pmu_name)) {
282                 test = &test_cpu_events[0];
283                 use_uncore_table = false;
284         } else {
285                 test = &test_uncore_events[0];
286                 use_uncore_table = true;
287         }
288
289         pmu = zalloc(sizeof(*pmu));
290         if (!pmu)
291                 return -1;
292
293         pmu->name = pmu_name;
294
295         pmu_add_cpu_aliases_map(&aliases, pmu, map);
296
297         for (te = &test->event; te->name; test++, te = &test->event) {
298                 struct perf_pmu_alias *alias = find_alias(te->name, &aliases);
299
300                 if (!alias) {
301                         bool uncore_match = pmu_uncore_alias_match(pmu_name,
302                                                                    te->pmu);
303
304                         if (use_uncore_table && !uncore_match) {
305                                 pr_debug3("testing aliases PMU %s: skip matching alias %s\n",
306                                           pmu_name, te->name);
307                                 continue;
308                         }
309
310                         pr_debug2("testing aliases PMU %s: no alias, alias_table->name=%s\n",
311                                   pmu_name, te->name);
312                         res = -1;
313                         break;
314                 }
315
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);
319                         res = -1;
320                         break;
321                 }
322
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);
327                         res = -1;
328                         break;
329                 }
330
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);
334                         res = -1;
335                         break;
336                 }
337
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);
341                         res = -1;
342                         break;
343                 }
344
345                 (*count)++;
346                 pr_debug2("testing aliases PMU %s: matched event %s\n",
347                           pmu_name, alias->name);
348         }
349
350         free(pmu);
351         return res;
352 }
353
354
355 static int test_aliases(void)
356 {
357         struct perf_pmu *pmu = NULL;
358
359         while ((pmu = perf_pmu__scan(pmu)) != NULL) {
360                 int count = 0;
361
362                 if (list_empty(&pmu->format)) {
363                         pr_debug2("skipping testing PMU %s\n", pmu->name);
364                         continue;
365                 }
366
367                 if (__test__pmu_event_aliases(pmu->name, &count)) {
368                         pr_debug("testing PMU %s aliases: failed\n", pmu->name);
369                         return -1;
370                 }
371
372                 if (count == 0)
373                         pr_debug3("testing PMU %s aliases: no events to match\n",
374                                   pmu->name);
375                 else
376                         pr_debug("testing PMU %s aliases: pass\n", pmu->name);
377         }
378
379         return 0;
380 }
381
382 static bool is_number(const char *str)
383 {
384         char *end_ptr;
385         double v;
386
387         errno = 0;
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;
391 }
392
393 static int check_parse_id(const char *id, struct parse_events_error *error,
394                           struct perf_pmu *fake_pmu)
395 {
396         struct evlist *evlist;
397         int ret;
398
399         /* Numbers are always valid. */
400         if (is_number(id))
401                 return 0;
402
403         evlist = evlist__new();
404         if (!evlist)
405                 return -ENOMEM;
406         ret = __parse_events(evlist, id, error, fake_pmu);
407         evlist__delete(evlist);
408         return ret;
409 }
410
411 static int check_parse_cpu(const char *id, bool same_cpu, struct pmu_event *pe)
412 {
413         struct parse_events_error error = { .idx = 0, };
414
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,
420                         error.help);
421         } else if (ret) {
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);
424                 ret = 0;
425         }
426         free(error.str);
427         free(error.help);
428         free(error.first_str);
429         free(error.first_help);
430         return ret;
431 }
432
433 static int check_parse_fake(const char *id)
434 {
435         struct parse_events_error error = { .idx = 0, };
436         int ret = check_parse_id(id, &error, &perf_pmu__fake);
437
438         free(error.str);
439         free(error.help);
440         free(error.first_str);
441         free(error.first_help);
442         return ret;
443 }
444
445 static void expr_failure(const char *msg,
446                          const struct pmu_events_map *map,
447                          const struct pmu_event *pe)
448 {
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);
453 }
454
455 static int test_parsing(void)
456 {
457         struct pmu_events_map *cpus_map = perf_pmu__find_map(NULL);
458         struct pmu_events_map *map;
459         struct pmu_event *pe;
460         int i, j, k;
461         int ret = 0;
462         struct expr_parse_ctx ctx;
463         double result;
464
465         i = 0;
466         for (;;) {
467                 map = &pmu_events_map[i++];
468                 if (!map->table)
469                         break;
470                 j = 0;
471                 for (;;) {
472                         struct hashmap_entry *cur;
473                         size_t bkt;
474
475                         pe = &map->table[j++];
476                         if (!pe->name && !pe->metric_group && !pe->metric_name)
477                                 break;
478                         if (!pe->metric_expr)
479                                 continue;
480                         expr__ctx_init(&ctx);
481                         if (expr__find_other(pe->metric_expr, NULL, &ctx, 0)
482                                   < 0) {
483                                 expr_failure("Parse other failed", map, pe);
484                                 ret++;
485                                 continue;
486                         }
487
488                         /*
489                          * Add all ids with a made up value. The value may
490                          * trigger divide by zero when subtracted and so try to
491                          * make them unique.
492                          */
493                         k = 1;
494                         hashmap__for_each_entry((&ctx.ids), cur, bkt)
495                                 expr__add_id_val(&ctx, strdup(cur->key), k++);
496
497                         hashmap__for_each_entry((&ctx.ids), cur, bkt) {
498                                 if (check_parse_cpu(cur->key, map == cpus_map,
499                                                    pe))
500                                         ret++;
501                         }
502
503                         if (expr__parse(&result, &ctx, pe->metric_expr, 0)) {
504                                 expr_failure("Parse failed", map, pe);
505                                 ret++;
506                         }
507                         expr__ctx_clear(&ctx);
508                 }
509         }
510         /* TODO: fail when not ok */
511         return ret == 0 ? TEST_OK : TEST_SKIP;
512 }
513
514 struct test_metric {
515         const char *str;
516 };
517
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@)", },
524 };
525
526 static int metric_parse_fake(const char *str)
527 {
528         struct expr_parse_ctx ctx;
529         struct hashmap_entry *cur;
530         double result;
531         int ret = -1;
532         size_t bkt;
533         int i;
534
535         pr_debug("parsing '%s'\n", str);
536
537         expr__ctx_init(&ctx);
538         if (expr__find_other(str, NULL, &ctx, 0) < 0) {
539                 pr_err("expr__find_other failed\n");
540                 return -1;
541         }
542
543         /*
544          * Add all ids with a made up value. The value may
545          * trigger divide by zero when subtracted and so try to
546          * make them unique.
547          */
548         i = 1;
549         hashmap__for_each_entry((&ctx.ids), cur, bkt)
550                 expr__add_id_val(&ctx, strdup(cur->key), i++);
551
552         hashmap__for_each_entry((&ctx.ids), cur, bkt) {
553                 if (check_parse_fake(cur->key)) {
554                         pr_err("check_parse_fake failed\n");
555                         goto out;
556                 }
557         }
558
559         if (expr__parse(&result, &ctx, str, 1))
560                 pr_err("expr__parse failed\n");
561         else
562                 ret = 0;
563
564 out:
565         expr__ctx_clear(&ctx);
566         return ret;
567 }
568
569 /*
570  * Parse all the metrics for current architecture,
571  * or all defined cpus via the 'fake_pmu'
572  * in parse_events.
573  */
574 static int test_parsing_fake(void)
575 {
576         struct pmu_events_map *map;
577         struct pmu_event *pe;
578         unsigned int i, j;
579         int err = 0;
580
581         for (i = 0; i < ARRAY_SIZE(metrics); i++) {
582                 err = metric_parse_fake(metrics[i].str);
583                 if (err)
584                         return err;
585         }
586
587         i = 0;
588         for (;;) {
589                 map = &pmu_events_map[i++];
590                 if (!map->table)
591                         break;
592                 j = 0;
593                 for (;;) {
594                         pe = &map->table[j++];
595                         if (!pe->name && !pe->metric_group && !pe->metric_name)
596                                 break;
597                         if (!pe->metric_expr)
598                                 continue;
599                         err = metric_parse_fake(pe->metric_expr);
600                         if (err)
601                                 return err;
602                 }
603         }
604
605         return 0;
606 }
607
608 static const struct {
609         int (*func)(void);
610         const char *desc;
611 } pmu_events_testcase_table[] = {
612         {
613                 .func = test_pmu_event_table,
614                 .desc = "PMU event table sanity",
615         },
616         {
617                 .func = test_aliases,
618                 .desc = "PMU event map aliases",
619         },
620         {
621                 .func = test_parsing,
622                 .desc = "Parsing of PMU event table metrics",
623         },
624         {
625                 .func = test_parsing_fake,
626                 .desc = "Parsing of PMU event table metrics with fake PMUs",
627         },
628 };
629
630 const char *test__pmu_events_subtest_get_desc(int subtest)
631 {
632         if (subtest < 0 ||
633             subtest >= (int)ARRAY_SIZE(pmu_events_testcase_table))
634                 return NULL;
635         return pmu_events_testcase_table[subtest].desc;
636 }
637
638 const char *test__pmu_events_subtest_skip_reason(int subtest)
639 {
640         if (subtest < 0 ||
641             subtest >= (int)ARRAY_SIZE(pmu_events_testcase_table))
642                 return NULL;
643         if (pmu_events_testcase_table[subtest].func != test_parsing)
644                 return NULL;
645         return "some metrics failed";
646 }
647
648 int test__pmu_events_subtest_get_nr(void)
649 {
650         return (int)ARRAY_SIZE(pmu_events_testcase_table);
651 }
652
653 int test__pmu_events(struct test *test __maybe_unused, int subtest)
654 {
655         if (subtest < 0 ||
656             subtest >= (int)ARRAY_SIZE(pmu_events_testcase_table))
657                 return TEST_FAIL;
658         return pmu_events_testcase_table[subtest].func();
659 }