Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.5.6.
  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
 16struct 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
 30static 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
 88static 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
120const int total_test_events_size = ARRAY_SIZE(test_uncore_events);
121
122static 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
136static 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 */
151static 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
256static 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 */
268static 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	struct perf_pmu_alias *a, *tmp;
278
279	if (!map)
280		return -1;
281
282	if (is_pmu_core(pmu_name)) {
283		test = &test_cpu_events[0];
284		use_uncore_table = false;
285	} else {
286		test = &test_uncore_events[0];
287		use_uncore_table = true;
288	}
289
290	pmu = zalloc(sizeof(*pmu));
291	if (!pmu)
292		return -1;
293
294	pmu->name = pmu_name;
295
296	pmu_add_cpu_aliases_map(&aliases, pmu, map);
297
298	for (te = &test->event; te->name; test++, te = &test->event) {
299		struct perf_pmu_alias *alias = find_alias(te->name, &aliases);
300
301		if (!alias) {
302			bool uncore_match = pmu_uncore_alias_match(pmu_name,
303								   te->pmu);
304
305			if (use_uncore_table && !uncore_match) {
306				pr_debug3("testing aliases PMU %s: skip matching alias %s\n",
307					  pmu_name, te->name);
308				continue;
309			}
310
311			pr_debug2("testing aliases PMU %s: no alias, alias_table->name=%s\n",
312				  pmu_name, te->name);
313			res = -1;
314			break;
315		}
316
317		if (!is_same(alias->desc, te->desc)) {
318			pr_debug2("testing aliases PMU %s: mismatched desc, %s vs %s\n",
319				  pmu_name, alias->desc, te->desc);
320			res = -1;
321			break;
322		}
323
324		if (!is_same(alias->long_desc, test->alias_long_desc)) {
325			pr_debug2("testing aliases PMU %s: mismatched long_desc, %s vs %s\n",
326				  pmu_name, alias->long_desc,
327				  test->alias_long_desc);
328			res = -1;
329			break;
330		}
331
332		if (!is_same(alias->str, test->alias_str)) {
333			pr_debug2("testing aliases PMU %s: mismatched str, %s vs %s\n",
334				  pmu_name, alias->str, test->alias_str);
335			res = -1;
336			break;
337		}
338
339		if (!is_same(alias->topic, te->topic)) {
340			pr_debug2("testing aliases PMU %s: mismatched topic, %s vs %s\n",
341				  pmu_name, alias->topic, te->topic);
342			res = -1;
343			break;
344		}
345
346		(*count)++;
347		pr_debug2("testing aliases PMU %s: matched event %s\n",
348			  pmu_name, alias->name);
349	}
350
351	list_for_each_entry_safe(a, tmp, &aliases, list) {
352		list_del(&a->list);
353		perf_pmu_free_alias(a);
354	}
355	free(pmu);
356	return res;
357}
358
359
360static int test_aliases(void)
361{
362	struct perf_pmu *pmu = NULL;
363
364	while ((pmu = perf_pmu__scan(pmu)) != NULL) {
365		int count = 0;
366
367		if (list_empty(&pmu->format)) {
368			pr_debug2("skipping testing PMU %s\n", pmu->name);
369			continue;
370		}
371
372		if (__test__pmu_event_aliases(pmu->name, &count)) {
373			pr_debug("testing PMU %s aliases: failed\n", pmu->name);
374			return -1;
375		}
376
377		if (count == 0)
378			pr_debug3("testing PMU %s aliases: no events to match\n",
379				  pmu->name);
380		else
381			pr_debug("testing PMU %s aliases: pass\n", pmu->name);
382	}
383
384	return 0;
385}
386
387static bool is_number(const char *str)
388{
389	char *end_ptr;
390	double v;
391
392	errno = 0;
393	v = strtod(str, &end_ptr);
394	(void)v; // We're not interested in this value, only if it is valid
395	return errno == 0 && end_ptr != str;
396}
397
398static int check_parse_id(const char *id, struct parse_events_error *error,
399			  struct perf_pmu *fake_pmu)
400{
401	struct evlist *evlist;
402	int ret;
403
404	/* Numbers are always valid. */
405	if (is_number(id))
406		return 0;
407
408	evlist = evlist__new();
409	if (!evlist)
410		return -ENOMEM;
411	ret = __parse_events(evlist, id, error, fake_pmu);
412	evlist__delete(evlist);
413	return ret;
414}
415
416static int check_parse_cpu(const char *id, bool same_cpu, struct pmu_event *pe)
417{
418	struct parse_events_error error = { .idx = 0, };
419
420	int ret = check_parse_id(id, &error, NULL);
421	if (ret && same_cpu) {
422		pr_warning("Parse event failed metric '%s' id '%s' expr '%s'\n",
423			pe->metric_name, id, pe->metric_expr);
424		pr_warning("Error string '%s' help '%s'\n", error.str,
425			error.help);
426	} else if (ret) {
427		pr_debug3("Parse event failed, but for an event that may not be supported by this CPU.\nid '%s' metric '%s' expr '%s'\n",
428			  id, pe->metric_name, pe->metric_expr);
429		ret = 0;
430	}
431	free(error.str);
432	free(error.help);
433	free(error.first_str);
434	free(error.first_help);
435	return ret;
436}
437
438static int check_parse_fake(const char *id)
439{
440	struct parse_events_error error = { .idx = 0, };
441	int ret = check_parse_id(id, &error, &perf_pmu__fake);
442
443	free(error.str);
444	free(error.help);
445	free(error.first_str);
446	free(error.first_help);
447	return ret;
448}
449
450static void expr_failure(const char *msg,
451			 const struct pmu_events_map *map,
452			 const struct pmu_event *pe)
453{
454	pr_debug("%s for map %s %s %s\n",
455		msg, map->cpuid, map->version, map->type);
456	pr_debug("On metric %s\n", pe->metric_name);
457	pr_debug("On expression %s\n", pe->metric_expr);
458}
459
460static int test_parsing(void)
461{
462	struct pmu_events_map *cpus_map = perf_pmu__find_map(NULL);
463	struct pmu_events_map *map;
464	struct pmu_event *pe;
465	int i, j, k;
466	int ret = 0;
467	struct expr_parse_ctx ctx;
468	double result;
469
470	i = 0;
471	for (;;) {
472		map = &pmu_events_map[i++];
473		if (!map->table)
474			break;
475		j = 0;
476		for (;;) {
477			struct hashmap_entry *cur;
478			size_t bkt;
479
480			pe = &map->table[j++];
481			if (!pe->name && !pe->metric_group && !pe->metric_name)
482				break;
483			if (!pe->metric_expr)
484				continue;
485			expr__ctx_init(&ctx);
486			if (expr__find_other(pe->metric_expr, NULL, &ctx, 0)
487				  < 0) {
488				expr_failure("Parse other failed", map, pe);
489				ret++;
490				continue;
491			}
492
493			/*
494			 * Add all ids with a made up value. The value may
495			 * trigger divide by zero when subtracted and so try to
496			 * make them unique.
497			 */
498			k = 1;
499			hashmap__for_each_entry((&ctx.ids), cur, bkt)
500				expr__add_id_val(&ctx, strdup(cur->key), k++);
501
502			hashmap__for_each_entry((&ctx.ids), cur, bkt) {
503				if (check_parse_cpu(cur->key, map == cpus_map,
504						   pe))
505					ret++;
506			}
507
508			if (expr__parse(&result, &ctx, pe->metric_expr, 0)) {
509				expr_failure("Parse failed", map, pe);
510				ret++;
511			}
512			expr__ctx_clear(&ctx);
513		}
514	}
515	/* TODO: fail when not ok */
516	return ret == 0 ? TEST_OK : TEST_SKIP;
517}
518
519struct test_metric {
520	const char *str;
521};
522
523static struct test_metric metrics[] = {
524	{ "(unc_p_power_state_occupancy.cores_c0 / unc_p_clockticks) * 100." },
525	{ "imx8_ddr0@read\\-cycles@ * 4 * 4", },
526	{ "imx8_ddr0@axid\\-read\\,axi_mask\\=0xffff\\,axi_id\\=0x0000@ * 4", },
527	{ "(cstate_pkg@c2\\-residency@ / msr@tsc@) * 100", },
528	{ "(imx8_ddr0@read\\-cycles@ + imx8_ddr0@write\\-cycles@)", },
529};
530
531static int metric_parse_fake(const char *str)
532{
533	struct expr_parse_ctx ctx;
534	struct hashmap_entry *cur;
535	double result;
536	int ret = -1;
537	size_t bkt;
538	int i;
539
540	pr_debug("parsing '%s'\n", str);
541
542	expr__ctx_init(&ctx);
543	if (expr__find_other(str, NULL, &ctx, 0) < 0) {
544		pr_err("expr__find_other failed\n");
545		return -1;
546	}
547
548	/*
549	 * Add all ids with a made up value. The value may
550	 * trigger divide by zero when subtracted and so try to
551	 * make them unique.
552	 */
553	i = 1;
554	hashmap__for_each_entry((&ctx.ids), cur, bkt)
555		expr__add_id_val(&ctx, strdup(cur->key), i++);
556
557	hashmap__for_each_entry((&ctx.ids), cur, bkt) {
558		if (check_parse_fake(cur->key)) {
559			pr_err("check_parse_fake failed\n");
560			goto out;
561		}
562	}
563
564	if (expr__parse(&result, &ctx, str, 1))
565		pr_err("expr__parse failed\n");
566	else
567		ret = 0;
568
569out:
570	expr__ctx_clear(&ctx);
571	return ret;
572}
573
574/*
575 * Parse all the metrics for current architecture,
576 * or all defined cpus via the 'fake_pmu'
577 * in parse_events.
578 */
579static int test_parsing_fake(void)
580{
581	struct pmu_events_map *map;
582	struct pmu_event *pe;
583	unsigned int i, j;
584	int err = 0;
585
586	for (i = 0; i < ARRAY_SIZE(metrics); i++) {
587		err = metric_parse_fake(metrics[i].str);
588		if (err)
589			return err;
590	}
591
592	i = 0;
593	for (;;) {
594		map = &pmu_events_map[i++];
595		if (!map->table)
596			break;
597		j = 0;
598		for (;;) {
599			pe = &map->table[j++];
600			if (!pe->name && !pe->metric_group && !pe->metric_name)
601				break;
602			if (!pe->metric_expr)
603				continue;
604			err = metric_parse_fake(pe->metric_expr);
605			if (err)
606				return err;
607		}
608	}
609
610	return 0;
611}
612
613static const struct {
614	int (*func)(void);
615	const char *desc;
616} pmu_events_testcase_table[] = {
617	{
618		.func = test_pmu_event_table,
619		.desc = "PMU event table sanity",
620	},
621	{
622		.func = test_aliases,
623		.desc = "PMU event map aliases",
624	},
625	{
626		.func = test_parsing,
627		.desc = "Parsing of PMU event table metrics",
628	},
629	{
630		.func = test_parsing_fake,
631		.desc = "Parsing of PMU event table metrics with fake PMUs",
632	},
633};
634
635const char *test__pmu_events_subtest_get_desc(int subtest)
636{
637	if (subtest < 0 ||
638	    subtest >= (int)ARRAY_SIZE(pmu_events_testcase_table))
639		return NULL;
640	return pmu_events_testcase_table[subtest].desc;
641}
642
643const char *test__pmu_events_subtest_skip_reason(int subtest)
644{
645	if (subtest < 0 ||
646	    subtest >= (int)ARRAY_SIZE(pmu_events_testcase_table))
647		return NULL;
648	if (pmu_events_testcase_table[subtest].func != test_parsing)
649		return NULL;
650	return "some metrics failed";
651}
652
653int test__pmu_events_subtest_get_nr(void)
654{
655	return (int)ARRAY_SIZE(pmu_events_testcase_table);
656}
657
658int test__pmu_events(struct test *test __maybe_unused, int subtest)
659{
660	if (subtest < 0 ||
661	    subtest >= (int)ARRAY_SIZE(pmu_events_testcase_table))
662		return TEST_FAIL;
663	return pmu_events_testcase_table[subtest].func();
664}