6baff670525c7c4ea321a68d43eaef015d580d8d
[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, bool same_cpu, struct pmu_event *pe)
394 {
395         struct parse_events_error error;
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         memset(&error, 0, sizeof(error));
405         ret = parse_events(evlist, id, &error);
406         if (ret && same_cpu) {
407                 pr_warning("Parse event failed metric '%s' id '%s' expr '%s'\n",
408                         pe->metric_name, id, pe->metric_expr);
409                 pr_warning("Error string '%s' help '%s'\n", error.str,
410                         error.help);
411         } else if (ret) {
412                 pr_debug3("Parse event failed, but for an event that may not be supported by this CPU.\nid '%s' metric '%s' expr '%s'\n",
413                           id, pe->metric_name, pe->metric_expr);
414                 ret = 0;
415         }
416         evlist__delete(evlist);
417         free(error.str);
418         free(error.help);
419         free(error.first_str);
420         free(error.first_help);
421         return ret;
422 }
423
424 static void expr_failure(const char *msg,
425                          const struct pmu_events_map *map,
426                          const struct pmu_event *pe)
427 {
428         pr_debug("%s for map %s %s %s\n",
429                 msg, map->cpuid, map->version, map->type);
430         pr_debug("On metric %s\n", pe->metric_name);
431         pr_debug("On expression %s\n", pe->metric_expr);
432 }
433
434 static int test_parsing(void)
435 {
436         struct pmu_events_map *cpus_map = perf_pmu__find_map(NULL);
437         struct pmu_events_map *map;
438         struct pmu_event *pe;
439         int i, j, k;
440         const char **ids;
441         int idnum;
442         int ret = 0;
443         struct expr_parse_ctx ctx;
444         double result;
445
446         i = 0;
447         for (;;) {
448                 map = &pmu_events_map[i++];
449                 if (!map->table)
450                         break;
451                 j = 0;
452                 for (;;) {
453                         pe = &map->table[j++];
454                         if (!pe->name && !pe->metric_group && !pe->metric_name)
455                                 break;
456                         if (!pe->metric_expr)
457                                 continue;
458                         if (expr__find_other(pe->metric_expr, NULL,
459                                                 &ids, &idnum, 0) < 0) {
460                                 expr_failure("Parse other failed", map, pe);
461                                 ret++;
462                                 continue;
463                         }
464                         expr__ctx_init(&ctx);
465
466                         /*
467                          * Add all ids with a made up value. The value may
468                          * trigger divide by zero when subtracted and so try to
469                          * make them unique.
470                          */
471                         for (k = 0; k < idnum; k++)
472                                 expr__add_id(&ctx, ids[k], k + 1);
473
474                         for (k = 0; k < idnum; k++) {
475                                 if (check_parse_id(ids[k], map == cpus_map, pe))
476                                         ret++;
477                         }
478
479                         if (expr__parse(&result, &ctx, pe->metric_expr, 0)) {
480                                 expr_failure("Parse failed", map, pe);
481                                 ret++;
482                         }
483                         for (k = 0; k < idnum; k++)
484                                 zfree(&ids[k]);
485                         free(ids);
486                 }
487         }
488         /* TODO: fail when not ok */
489         return ret == 0 ? TEST_OK : TEST_SKIP;
490 }
491
492 static const struct {
493         int (*func)(void);
494         const char *desc;
495 } pmu_events_testcase_table[] = {
496         {
497                 .func = test_pmu_event_table,
498                 .desc = "PMU event table sanity",
499         },
500         {
501                 .func = test_aliases,
502                 .desc = "PMU event map aliases",
503         },
504         {
505                 .func = test_parsing,
506                 .desc = "Parsing of PMU event table metrics",
507         },
508 };
509
510 const char *test__pmu_events_subtest_get_desc(int subtest)
511 {
512         if (subtest < 0 ||
513             subtest >= (int)ARRAY_SIZE(pmu_events_testcase_table))
514                 return NULL;
515         return pmu_events_testcase_table[subtest].desc;
516 }
517
518 const char *test__pmu_events_subtest_skip_reason(int subtest)
519 {
520         if (subtest < 0 ||
521             subtest >= (int)ARRAY_SIZE(pmu_events_testcase_table))
522                 return NULL;
523         if (pmu_events_testcase_table[subtest].func != test_parsing)
524                 return NULL;
525         return "some metrics failed";
526 }
527
528 int test__pmu_events_subtest_get_nr(void)
529 {
530         return (int)ARRAY_SIZE(pmu_events_testcase_table);
531 }
532
533 int test__pmu_events(struct test *test __maybe_unused, int subtest)
534 {
535         if (subtest < 0 ||
536             subtest >= (int)ARRAY_SIZE(pmu_events_testcase_table))
537                 return TEST_FAIL;
538         return pmu_events_testcase_table[subtest].func();
539 }