|  | /* | 
|  | * 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; | 
|  | } |