| /* | 
 |  * validate.c - validate event tables + encodings | 
 |  * | 
 |  * Copyright (c) 2010 Google, Inc | 
 |  * Contributed by Stephane Eranian <eranian@gmail.com> | 
 |  * | 
 |  * Permission is hereby granted, free of charge, to any person obtaining a copy | 
 |  * of this software and associated documentation files (the "Software"), to deal | 
 |  * in the Software without restriction, including without limitation the rights | 
 |  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies | 
 |  * of the Software, and to permit persons to whom the Software is furnished to do so, | 
 |  * subject to the following conditions: | 
 |  * | 
 |  * The above copyright notice and this permission notice shall be included in all | 
 |  * copies or substantial portions of the Software. | 
 |  * | 
 |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | 
 |  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A | 
 |  * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | 
 |  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF | 
 |  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE | 
 |  * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | 
 |  * | 
 |  * This file is part of libpfm, a performance monitoring support library for | 
 |  * applications on Linux. | 
 |  */ | 
 | #include <sys/types.h> | 
 | #include <stdio.h> | 
 | #include <stdlib.h> | 
 | #include <inttypes.h> | 
 | #include <stdarg.h> | 
 | #include <errno.h> | 
 | #include <unistd.h> | 
 | #include <string.h> | 
 | #include <perfmon/err.h> | 
 |  | 
 | #include <perfmon/pfmlib.h> | 
 | #ifdef __linux__ | 
 | #include <perfmon/pfmlib_perf_event.h> | 
 | #endif | 
 |  | 
 | #define __weak_func	__attribute__((weak)) | 
 |  | 
 | #ifdef PFMLIB_WINDOWS | 
 | int set_env_var(const char *var, const char *value, int ov) | 
 | { | 
 | 	size_t len; | 
 | 	char *str; | 
 | 	int ret; | 
 |  | 
 | 	len = strlen(var) + 1 + strlen(value) + 1; | 
 |  | 
 | 	str = malloc(len); | 
 | 	if (!str) | 
 | 		return PFM_ERR_NOMEM; | 
 |  | 
 | 	sprintf(str, "%s=%s", var, value); | 
 |  | 
 | 	ret = putenv(str); | 
 |  | 
 | 	free(str); | 
 |  | 
 | 	return ret ? PFM_ERR_INVAL : PFM_SUCCESS; | 
 | } | 
 | #else | 
 | static inline int | 
 | set_env_var(const char *var, const char *value, int ov) | 
 | { | 
 | 	return setenv(var, value, ov); | 
 | } | 
 | #endif | 
 |  | 
 | __weak_func int validate_arch(FILE *fp) | 
 | { | 
 | 	return 0; | 
 | } | 
 |  | 
 | static struct { | 
 | 	int valid_intern; | 
 | 	int valid_arch; | 
 | } options; | 
 |  | 
 | static void | 
 | usage(void) | 
 | { | 
 | 	printf("validate [-c] [-a] [-A]\n" | 
 | 		"-c\trun the library validate events\n" | 
 | 		"-a\trun architecture specific event tests\n" | 
 | 		"-A\trun all tests\n" | 
 | 		"-h\tget help\n"); | 
 | } | 
 |  | 
 | static int | 
 | validate_event_tables(void) | 
 | { | 
 | 	pfm_pmu_info_t pinfo; | 
 | 	int i, ret, errors = 0; | 
 |  | 
 | 	memset(&pinfo, 0, sizeof(pinfo)); | 
 |  | 
 | 	pinfo.size = sizeof(pinfo); | 
 |  | 
 | 	pfm_for_all_pmus(i) { | 
 | 		ret = pfm_get_pmu_info(i, &pinfo); | 
 | 		if (ret != PFM_SUCCESS) | 
 | 			continue; | 
 |  | 
 | 		printf("\tchecking %s (%d events): ", pinfo.name, pinfo.nevents); | 
 | 		fflush(stdout); | 
 |  | 
 | 		ret = pfm_pmu_validate(i, stdout); | 
 | 		if (ret != PFM_SUCCESS && ret != PFM_ERR_NOTSUPP) { | 
 | 			printf("Failed\n"); | 
 | 			errors++; | 
 | 		} else if (ret == PFM_ERR_NOTSUPP) { | 
 | 			printf("N/A\n"); | 
 | 		} else { | 
 | 			printf("Passed\n"); | 
 | 		} | 
 | 	} | 
 | 	return errors; | 
 | } | 
 |  | 
 | #if __WORDSIZE == 64 | 
 | #define STRUCT_MULT	8 | 
 | #else | 
 | #define STRUCT_MULT	4 | 
 | #endif | 
 |  | 
 | #define MAX_FIELDS 32 | 
 | typedef struct { | 
 | 		const char *name; | 
 | 		size_t sz; | 
 | } field_desc_t; | 
 |  | 
 | typedef struct { | 
 | 	const char *name; | 
 | 	size_t sz; | 
 | 	size_t bitfield_sz; | 
 | 	size_t abi_sz; | 
 | 	field_desc_t fields[MAX_FIELDS]; | 
 | } struct_desc_t; | 
 |  | 
 | #define LAST_STRUCT { .name = NULL, } | 
 |  | 
 | #define FIELD(n, st) \ | 
 | 	{ .name = #n, \ | 
 | 	  .sz   = sizeof(((st *)(0))->n), \ | 
 | 	} | 
 | #define LAST_FIELD { .name = NULL, } | 
 |  | 
 | static const struct_desc_t pfmlib_structs[]={ | 
 | 	{ | 
 | 	 .name = "pfm_pmu_info_t", | 
 | 	 .sz   = sizeof(pfm_pmu_info_t), | 
 | 	 .bitfield_sz = 4, | 
 | 	 .abi_sz = PFM_PMU_INFO_ABI0, | 
 | 	 .fields= { | 
 | 		FIELD(name, pfm_pmu_info_t), | 
 | 		FIELD(desc, pfm_pmu_info_t), | 
 | 		FIELD(size, pfm_pmu_info_t), | 
 | 		FIELD(pmu, pfm_pmu_info_t), | 
 | 		FIELD(type, pfm_pmu_info_t), | 
 | 		FIELD(nevents, pfm_pmu_info_t), | 
 | 		FIELD(first_event, pfm_pmu_info_t), | 
 | 		FIELD(max_encoding, pfm_pmu_info_t), | 
 | 		FIELD(num_cntrs, pfm_pmu_info_t), | 
 | 		FIELD(num_fixed_cntrs, pfm_pmu_info_t), | 
 | 		LAST_FIELD | 
 | 	 }, | 
 | 	}, | 
 | 	{ | 
 | 	 .name = "pfm_event_info_t", | 
 | 	 .sz   = sizeof(pfm_event_info_t), | 
 | 	 .bitfield_sz = 4, | 
 | 	 .abi_sz = PFM_EVENT_INFO_ABI0, | 
 | 	 .fields= { | 
 | 		FIELD(name, pfm_event_info_t), | 
 | 		FIELD(desc, pfm_event_info_t), | 
 | 		FIELD(equiv, pfm_event_info_t), | 
 | 		FIELD(size, pfm_event_info_t), | 
 | 		FIELD(code, pfm_event_info_t), | 
 | 		FIELD(pmu, pfm_event_info_t), | 
 | 		FIELD(dtype, pfm_event_info_t), | 
 | 		FIELD(idx, pfm_event_info_t), | 
 | 		FIELD(nattrs, pfm_event_info_t), | 
 | 		FIELD(reserved, pfm_event_info_t), | 
 | 		LAST_FIELD | 
 | 	 }, | 
 | 	}, | 
 | 	{ | 
 | 	 .name = "pfm_event_attr_info_t", | 
 | 	 .sz   = sizeof(pfm_event_attr_info_t), | 
 | 	 .bitfield_sz = 4+8, | 
 | 	 .abi_sz = PFM_ATTR_INFO_ABI0, | 
 | 	 .fields= { | 
 | 		FIELD(name, pfm_event_attr_info_t), | 
 | 		FIELD(desc, pfm_event_attr_info_t), | 
 | 		FIELD(equiv, pfm_event_attr_info_t), | 
 | 		FIELD(size, pfm_event_attr_info_t), | 
 | 		FIELD(code, pfm_event_attr_info_t), | 
 | 		FIELD(type, pfm_event_attr_info_t), | 
 | 		FIELD(idx, pfm_event_attr_info_t), | 
 | 		FIELD(ctrl, pfm_event_attr_info_t), | 
 | 		LAST_FIELD | 
 | 	 }, | 
 | 	}, | 
 | 	{ | 
 | 	 .name = "pfm_pmu_encode_arg_t", | 
 | 	 .sz   = sizeof(pfm_pmu_encode_arg_t), | 
 | 	 .abi_sz = PFM_RAW_ENCODE_ABI0, | 
 | 	 .fields= { | 
 | 		FIELD(codes, pfm_pmu_encode_arg_t), | 
 | 		FIELD(fstr, pfm_pmu_encode_arg_t), | 
 | 		FIELD(size, pfm_pmu_encode_arg_t), | 
 | 		FIELD(count, pfm_pmu_encode_arg_t), | 
 | 		FIELD(idx, pfm_pmu_encode_arg_t), | 
 | 		LAST_FIELD | 
 | 	 }, | 
 | 	}, | 
 | #ifdef __linux__ | 
 | 	{ | 
 | 	 .name = "pfm_perf_encode_arg_t", | 
 | 	 .sz   = sizeof(pfm_perf_encode_arg_t), | 
 | 	 .bitfield_sz = 0, | 
 | 	 .abi_sz = PFM_PERF_ENCODE_ABI0, | 
 | 	 .fields= { | 
 | 		FIELD(attr, pfm_perf_encode_arg_t), | 
 | 		FIELD(fstr, pfm_perf_encode_arg_t), | 
 | 		FIELD(size, pfm_perf_encode_arg_t), | 
 | 		FIELD(idx, pfm_perf_encode_arg_t), | 
 | 		FIELD(cpu, pfm_perf_encode_arg_t), | 
 | 		FIELD(flags, pfm_perf_encode_arg_t), | 
 | 		FIELD(pad0, pfm_perf_encode_arg_t), | 
 | 		LAST_FIELD | 
 | 	 }, | 
 | 	}, | 
 | #endif | 
 | 	LAST_STRUCT | 
 | }; | 
 |  | 
 | static int | 
 | validate_structs(void) | 
 | { | 
 |  | 
 | 	const struct_desc_t *d; | 
 | 	const field_desc_t *f; | 
 | 	size_t sz; | 
 | 	int errors = 0; | 
 | 	int abi = LIBPFM_ABI_VERSION; | 
 |  | 
 | 	printf("\tlibpfm ABI version : %d\n", abi); | 
 | 	for (d = pfmlib_structs; d->name; d++) { | 
 |  | 
 | 		printf("\t%s : ", d->name); | 
 |  | 
 | 		if (d->abi_sz != d->sz) { | 
 | 			printf("struct size does not correspond to ABI size %zu vs. %zu)\n", d->abi_sz, d->sz); | 
 | 			errors++; | 
 | 		} | 
 |  | 
 | 		if (d->sz % STRUCT_MULT) { | 
 | 			printf("Failed (wrong mult size=%zu)\n", d->sz); | 
 | 			errors++; | 
 | 		} | 
 |  | 
 | 		sz = d->bitfield_sz; | 
 | 		for (f = d->fields; f->name; f++) { | 
 | 			sz += f->sz; | 
 | 		} | 
 |  | 
 | 		if (sz != d->sz) { | 
 | 			printf("Failed (invisible padding of %zu bytes)\n", d->sz - sz); | 
 | 			errors++; | 
 | 			continue; | 
 | 		} | 
 | 		printf("Passed\n"); | 
 | 		 | 
 | 	} | 
 | 	return errors; | 
 | } | 
 |  | 
 | int | 
 | main(int argc, char **argv) | 
 | { | 
 | 	int ret, c, errors = 0; | 
 |  | 
 |  | 
 | 	while ((c=getopt(argc, argv,"hcaA")) != -1) { | 
 | 		switch(c) { | 
 | 			case 'c': | 
 | 				options.valid_intern = 1; | 
 | 				break; | 
 | 			case 'a': | 
 | 				options.valid_arch = 1; | 
 | 				break; | 
 | 			case 'A': | 
 | 				options.valid_arch = 1; | 
 | 				options.valid_intern = 1; | 
 | 				break; | 
 | 			case 'h': | 
 | 				usage(); | 
 | 				exit(0); | 
 | 			default: | 
 | 				errx(1, "unknown option error"); | 
 | 		} | 
 | 	} | 
 | 	/* to allow encoding of events from non detected PMU models */ | 
 | 	ret = set_env_var("LIBPFM_ENCODE_INACTIVE", "1", 1); | 
 | 	if (ret != PFM_SUCCESS) | 
 | 		errx(1, "cannot force inactive encoding"); | 
 |  | 
 | 	ret = pfm_initialize(); | 
 | 	if (ret != PFM_SUCCESS) | 
 | 		errx(1, "cannot initialize libpfm: %s", pfm_strerror(ret)); | 
 |  | 
 | 	/* run everything by default */ | 
 | 	if (!(options.valid_intern || options.valid_arch)) { | 
 | 		options.valid_intern = 1; | 
 | 		options.valid_arch = 1; | 
 | 	} | 
 |  | 
 | 	printf("Libpfm structure tests:\n"); | 
 | 	errors += validate_structs(); | 
 |  | 
 | 	if (options.valid_intern) { | 
 | 		printf("Libpfm internal table tests:\n"); | 
 | 		errors += validate_event_tables(); | 
 | 	} | 
 |  | 
 | 	if (options.valid_arch) { | 
 | 		printf("Architecture specific tests:\n"); | 
 | 		errors += validate_arch(stderr); | 
 | 	} | 
 |  | 
 | 	pfm_terminate(); | 
 |  | 
 | 	if (errors) | 
 | 		printf("Total %d errors\n", errors); | 
 | 	else | 
 | 		printf("All tests passed\n"); | 
 |  | 
 | 	return errors; | 
 | } |