|  | /* | 
|  | * showevtinfo.c - show event information | 
|  | * | 
|  | * 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 <regex.h> | 
|  | #include <perfmon/err.h> | 
|  |  | 
|  | #include <perfmon/pfmlib.h> | 
|  |  | 
|  | #define MAXBUF		1024 | 
|  | #define COMBO_MAX	18 | 
|  |  | 
|  | static struct { | 
|  | int compact; | 
|  | int sort; | 
|  | int encode; | 
|  | int combo; | 
|  | int combo_lim; | 
|  | int desc; | 
|  | char *csv_sep; | 
|  | pfm_event_info_t efilter; | 
|  | pfm_event_attr_info_t ufilter; | 
|  | pfm_os_t os; | 
|  | uint64_t mask; | 
|  | } options; | 
|  |  | 
|  | typedef struct { | 
|  | uint64_t code; | 
|  | int idx; | 
|  | } code_info_t; | 
|  |  | 
|  | static void show_event_info_compact(pfm_event_info_t *info); | 
|  |  | 
|  | static const char *srcs[PFM_ATTR_CTRL_MAX]={ | 
|  | [PFM_ATTR_CTRL_UNKNOWN] = "???", | 
|  | [PFM_ATTR_CTRL_PMU] = "PMU", | 
|  | [PFM_ATTR_CTRL_PERF_EVENT] = "perf_event", | 
|  | }; | 
|  |  | 
|  | #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 | 
|  |  | 
|  | static int | 
|  | event_has_pname(char *s) | 
|  | { | 
|  | char *p; | 
|  | return (p = strchr(s, ':')) && *(p+1) == ':'; | 
|  | } | 
|  |  | 
|  | static int | 
|  | print_codes(char *buf, int plm, int max_encoding) | 
|  | { | 
|  | uint64_t *codes = NULL; | 
|  | int j, ret, count = 0; | 
|  |  | 
|  | ret = pfm_get_event_encoding(buf, PFM_PLM0|PFM_PLM3, NULL, NULL, &codes, &count); | 
|  | if (ret != PFM_SUCCESS) { | 
|  | if (ret == PFM_ERR_NOTFOUND) | 
|  | errx(1, "encoding failed, try setting env variable LIBPFM_ENCODE_INACTIVE=1"); | 
|  | return -1; | 
|  | } | 
|  | for(j = 0; j < max_encoding; j++) { | 
|  | if (j < count) | 
|  | printf("0x%"PRIx64, codes[j]); | 
|  | printf("%s", options.csv_sep); | 
|  | } | 
|  | free(codes); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int | 
|  | check_valid(char *buf, int plm) | 
|  | { | 
|  | uint64_t *codes = NULL; | 
|  | int ret, count = 0; | 
|  |  | 
|  | ret = pfm_get_event_encoding(buf, PFM_PLM0|PFM_PLM3, NULL, NULL, &codes, &count); | 
|  | if (ret != PFM_SUCCESS) | 
|  | return -1; | 
|  | free(codes); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int | 
|  | match_ufilters(pfm_event_attr_info_t *info) | 
|  | { | 
|  | uint32_t ufilter1 = 0; | 
|  | uint32_t ufilter2 = 0; | 
|  |  | 
|  | if (options.ufilter.is_dfl) | 
|  | ufilter1 |= 0x1; | 
|  |  | 
|  | if (info->is_dfl) | 
|  | ufilter2 |= 0x1; | 
|  |  | 
|  | if (options.ufilter.is_precise) | 
|  | ufilter1 |= 0x2; | 
|  |  | 
|  | if (info->is_precise) | 
|  | ufilter2 |= 0x2; | 
|  |  | 
|  | if (!ufilter1) | 
|  | return 1; | 
|  |  | 
|  | /* at least one filter matches */ | 
|  | return ufilter1 & ufilter2; | 
|  | } | 
|  |  | 
|  | static int | 
|  | match_efilters(pfm_event_info_t *info) | 
|  | { | 
|  | pfm_event_attr_info_t ainfo; | 
|  | int n = 0; | 
|  | int i, ret; | 
|  |  | 
|  | if (options.efilter.is_precise && !info->is_precise) | 
|  | return 0; | 
|  |  | 
|  | memset(&ainfo, 0, sizeof(ainfo)); | 
|  | ainfo.size = sizeof(ainfo); | 
|  |  | 
|  | pfm_for_each_event_attr(i, info) { | 
|  | ret = pfm_get_event_attr_info(info->idx, i, options.os, &ainfo); | 
|  | if (ret != PFM_SUCCESS) | 
|  | continue; | 
|  | if (match_ufilters(&ainfo)) | 
|  | return 1; | 
|  | if (ainfo.type == PFM_ATTR_UMASK) | 
|  | n++; | 
|  | } | 
|  | return n ? 0 : 1; | 
|  | } | 
|  |  | 
|  | static void | 
|  | show_event_info_combo(pfm_event_info_t *info) | 
|  | { | 
|  | pfm_event_attr_info_t *ainfo; | 
|  | pfm_pmu_info_t pinfo; | 
|  | char buf[MAXBUF]; | 
|  | size_t len; | 
|  | int numasks = 0; | 
|  | int i, j, ret; | 
|  | uint64_t total, m, u; | 
|  |  | 
|  | memset(&pinfo, 0, sizeof(pinfo)); | 
|  |  | 
|  | pinfo.size = sizeof(pinfo); | 
|  |  | 
|  | ret = pfm_get_pmu_info(info->pmu, &pinfo); | 
|  | if (ret != PFM_SUCCESS) | 
|  | errx(1, "cannot get PMU info"); | 
|  |  | 
|  | ainfo = calloc(info->nattrs, sizeof(*ainfo)); | 
|  | if (!ainfo) | 
|  | err(1, "event %s : ", info->name); | 
|  |  | 
|  | /* | 
|  | * extract attribute information and count number | 
|  | * of umasks | 
|  | * | 
|  | * we cannot just drop non umasks because we need | 
|  | * to keep attributes in order for the enumeration | 
|  | * of 2^n | 
|  | */ | 
|  | pfm_for_each_event_attr(i, info) { | 
|  | ainfo[i].size = sizeof(*ainfo); | 
|  |  | 
|  | ret = pfm_get_event_attr_info(info->idx, i, options.os, &ainfo[i]); | 
|  | if (ret != PFM_SUCCESS) | 
|  | errx(1, "cannot get attribute info: %s", pfm_strerror(ret)); | 
|  |  | 
|  | if (ainfo[i].type == PFM_ATTR_UMASK) | 
|  | numasks++; | 
|  | } | 
|  | if (numasks > options.combo_lim) { | 
|  | warnx("event %s has too many umasks to print all combinations, dropping to simple enumeration", info->name); | 
|  | free(ainfo); | 
|  | show_event_info_compact(info); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (numasks) { | 
|  | if (info->nattrs > (int)((sizeof(total)<<3))) { | 
|  | warnx("too many umasks, cannot show all combinations for event %s", info->name); | 
|  | goto end; | 
|  | } | 
|  | total = 1ULL << info->nattrs; | 
|  |  | 
|  | for (u = 1; u < total; u++) { | 
|  | len = sizeof(buf); | 
|  | len -= snprintf(buf, len, "%s::%s", pinfo.name, info->name); | 
|  | if (len <= 0) { | 
|  | warnx("event name too long%s", info->name); | 
|  | goto end; | 
|  | } | 
|  | for(m = u, j = 0; m; m >>=1, j++) { | 
|  | if (m & 0x1ULL) { | 
|  | /* we have hit a non umasks attribute, skip */ | 
|  | if (ainfo[j].type != PFM_ATTR_UMASK) | 
|  | break; | 
|  |  | 
|  | if (len < (1 + strlen(ainfo[j].name))) { | 
|  | warnx("umasks combination too long for event %s", buf); | 
|  | break; | 
|  | } | 
|  | strncat(buf, ":", len-1);buf[len-1] = '\0'; len--; | 
|  | strncat(buf, ainfo[j].name, len-1);buf[len-1] = '\0'; | 
|  | len -= strlen(ainfo[j].name); | 
|  | } | 
|  | } | 
|  | /* if found a valid umask combination, check encoding */ | 
|  | if (m == 0) { | 
|  | if (options.encode) | 
|  | ret = print_codes(buf, PFM_PLM0|PFM_PLM3, pinfo.max_encoding); | 
|  | else | 
|  | ret = check_valid(buf, PFM_PLM0|PFM_PLM3); | 
|  | if (!ret) | 
|  | printf("%s\n", buf); | 
|  | } | 
|  | } | 
|  | } else { | 
|  | snprintf(buf, sizeof(buf)-1, "%s::%s", pinfo.name, info->name); | 
|  | buf[sizeof(buf)-1] = '\0'; | 
|  |  | 
|  | ret = options.encode ? print_codes(buf, PFM_PLM0|PFM_PLM3, pinfo.max_encoding) : 0; | 
|  | if (!ret) | 
|  | printf("%s\n", buf); | 
|  | } | 
|  | end: | 
|  | free(ainfo); | 
|  | } | 
|  |  | 
|  | static void | 
|  | show_event_info_compact(pfm_event_info_t *info) | 
|  | { | 
|  | pfm_event_attr_info_t ainfo; | 
|  | pfm_pmu_info_t pinfo; | 
|  | char buf[MAXBUF]; | 
|  | int i, ret, um = 0; | 
|  |  | 
|  | memset(&ainfo, 0, sizeof(ainfo)); | 
|  | memset(&pinfo, 0, sizeof(pinfo)); | 
|  |  | 
|  | pinfo.size = sizeof(pinfo); | 
|  | ainfo.size = sizeof(ainfo); | 
|  |  | 
|  | ret = pfm_get_pmu_info(info->pmu, &pinfo); | 
|  | if (ret != PFM_SUCCESS) | 
|  | errx(1, "cannot get pmu info: %s", pfm_strerror(ret)); | 
|  |  | 
|  | pfm_for_each_event_attr(i, info) { | 
|  | ret = pfm_get_event_attr_info(info->idx, i, options.os, &ainfo); | 
|  | if (ret != PFM_SUCCESS) | 
|  | errx(1, "cannot get attribute info: %s", pfm_strerror(ret)); | 
|  |  | 
|  | if (ainfo.type != PFM_ATTR_UMASK) | 
|  | continue; | 
|  |  | 
|  | if (!match_ufilters(&ainfo)) | 
|  | continue; | 
|  |  | 
|  | snprintf(buf, sizeof(buf)-1, "%s::%s:%s", pinfo.name, info->name, ainfo.name); | 
|  | buf[sizeof(buf)-1] = '\0'; | 
|  |  | 
|  | ret = 0; | 
|  | if (options.encode) { | 
|  | ret = print_codes(buf, PFM_PLM0|PFM_PLM3, pinfo.max_encoding); | 
|  | } | 
|  | if (!ret) { | 
|  | printf("%s", buf); | 
|  | if (options.desc) { | 
|  | printf("%s", options.csv_sep); | 
|  | printf("\"%s. %s.\"", info->desc, ainfo.desc); | 
|  | } | 
|  | putchar('\n'); | 
|  | } | 
|  | um++; | 
|  | } | 
|  | if (um == 0) { | 
|  | if (!match_efilters(info)) | 
|  | return; | 
|  |  | 
|  | snprintf(buf, sizeof(buf)-1, "%s::%s", pinfo.name, info->name); | 
|  | buf[sizeof(buf)-1] = '\0'; | 
|  | if (options.encode) { | 
|  | ret = print_codes(buf, PFM_PLM0|PFM_PLM3, pinfo.max_encoding); | 
|  | if (ret) | 
|  | return; | 
|  | } | 
|  | printf("%s", buf); | 
|  | if (options.desc) { | 
|  | printf("%s", options.csv_sep); | 
|  | printf("\"%s.\"", info->desc); | 
|  | } | 
|  | putchar('\n'); | 
|  | } | 
|  | } | 
|  |  | 
|  | int compare_codes(const void *a, const void *b) | 
|  | { | 
|  | const code_info_t *aa = a; | 
|  | const code_info_t *bb = b; | 
|  | uint64_t m = options.mask; | 
|  |  | 
|  | if ((aa->code & m) < (bb->code &m)) | 
|  | return -1; | 
|  | if ((aa->code & m) == (bb->code & m)) | 
|  | return 0; | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static void | 
|  | print_event_flags(pfm_event_info_t *info) | 
|  | { | 
|  | int n = 0; | 
|  |  | 
|  | if (info->is_precise) { | 
|  | printf("[precise] "); | 
|  | n++; | 
|  | } | 
|  | if (!n) | 
|  | printf("None"); | 
|  | } | 
|  |  | 
|  | static void | 
|  | print_attr_flags(pfm_event_attr_info_t *info) | 
|  | { | 
|  | int n = 0; | 
|  |  | 
|  | if (info->is_dfl) { | 
|  | printf("[default] "); | 
|  | n++; | 
|  | } | 
|  |  | 
|  | if (info->is_precise) { | 
|  | printf("[precise] "); | 
|  | n++; | 
|  | } | 
|  |  | 
|  | if (!n) | 
|  | printf("None "); | 
|  | } | 
|  |  | 
|  |  | 
|  | static void | 
|  | show_event_info(pfm_event_info_t *info) | 
|  | { | 
|  | pfm_event_attr_info_t ainfo; | 
|  | pfm_pmu_info_t pinfo; | 
|  | int mod = 0, um = 0; | 
|  | int i, ret; | 
|  | const char *src; | 
|  |  | 
|  | memset(&ainfo, 0, sizeof(ainfo)); | 
|  | memset(&pinfo, 0, sizeof(pinfo)); | 
|  |  | 
|  | pinfo.size = sizeof(pinfo); | 
|  | ainfo.size = sizeof(ainfo); | 
|  |  | 
|  | if (!match_efilters(info)) | 
|  | return; | 
|  | ret = pfm_get_pmu_info(info->pmu, &pinfo); | 
|  | if (ret) | 
|  | errx(1, "cannot get pmu info: %s", pfm_strerror(ret)); | 
|  |  | 
|  | printf("#-----------------------------\n" | 
|  | "IDX	 : %d\n" | 
|  | "PMU name : %s (%s)\n" | 
|  | "Name     : %s\n" | 
|  | "Equiv	 : %s\n", | 
|  | info->idx, | 
|  | pinfo.name, | 
|  | pinfo.desc, | 
|  | info->name, | 
|  | info->equiv ? info->equiv : "None"); | 
|  |  | 
|  | printf("Flags    : "); | 
|  | print_event_flags(info); | 
|  | putchar('\n'); | 
|  |  | 
|  | printf("Desc     : %s\n", info->desc ? info->desc : "no description available"); | 
|  | printf("Code     : 0x%"PRIx64"\n", info->code); | 
|  |  | 
|  | pfm_for_each_event_attr(i, info) { | 
|  | ret = pfm_get_event_attr_info(info->idx, i, options.os, &ainfo); | 
|  | if (ret != PFM_SUCCESS) | 
|  | errx(1, "cannot retrieve event %s attribute info: %s", info->name, pfm_strerror(ret)); | 
|  |  | 
|  | if (ainfo.ctrl >= PFM_ATTR_CTRL_MAX) { | 
|  | warnx("event: %s has unsupported attribute source %d", info->name, ainfo.ctrl); | 
|  | ainfo.ctrl = PFM_ATTR_CTRL_UNKNOWN; | 
|  | } | 
|  | src = srcs[ainfo.ctrl]; | 
|  | switch(ainfo.type) { | 
|  | case PFM_ATTR_UMASK: | 
|  | if (!match_ufilters(&ainfo)) | 
|  | continue; | 
|  |  | 
|  | printf("Umask-%02u : 0x%02"PRIx64" : %s : [%s] : ", | 
|  | um, | 
|  | ainfo.code, | 
|  | src, | 
|  | ainfo.name); | 
|  |  | 
|  | print_attr_flags(&ainfo); | 
|  |  | 
|  | putchar(':'); | 
|  |  | 
|  | if (ainfo.equiv) | 
|  | printf(" Alias to %s", ainfo.equiv); | 
|  | else | 
|  | printf(" %s", ainfo.desc); | 
|  |  | 
|  | putchar('\n'); | 
|  | um++; | 
|  | break; | 
|  | case PFM_ATTR_MOD_BOOL: | 
|  | printf("Modif-%02u : 0x%02"PRIx64" : %s : [%s] : %s (boolean)\n", mod, ainfo.code, src, ainfo.name, ainfo.desc); | 
|  | mod++; | 
|  | break; | 
|  | case PFM_ATTR_MOD_INTEGER: | 
|  | printf("Modif-%02u : 0x%02"PRIx64" : %s : [%s] : %s (integer)\n", mod, ainfo.code, src, ainfo.name, ainfo.desc); | 
|  | mod++; | 
|  | break; | 
|  | default: | 
|  | printf("Attr-%02u  : 0x%02"PRIx64" : %s : [%s] : %s\n", i, ainfo.code, ainfo.name, src, ainfo.desc); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | static int | 
|  | show_info(char *event, regex_t *preg) | 
|  | { | 
|  | pfm_pmu_info_t pinfo; | 
|  | pfm_event_info_t info; | 
|  | int i, j, ret, match = 0, pname; | 
|  | size_t len, l = 0; | 
|  | char *fullname = NULL; | 
|  |  | 
|  | memset(&pinfo, 0, sizeof(pinfo)); | 
|  | memset(&info, 0, sizeof(info)); | 
|  |  | 
|  | pinfo.size = sizeof(pinfo); | 
|  | info.size = sizeof(info); | 
|  |  | 
|  | pname = event_has_pname(event); | 
|  |  | 
|  | /* | 
|  | * scan all supported events, incl. those | 
|  | * from undetected PMU models | 
|  | */ | 
|  | pfm_for_all_pmus(j) { | 
|  |  | 
|  | ret = pfm_get_pmu_info(j, &pinfo); | 
|  | if (ret != PFM_SUCCESS) | 
|  | continue; | 
|  |  | 
|  | /* no pmu prefix, just look for detected PMU models */ | 
|  | if (!pname && !pinfo.is_present) | 
|  | continue; | 
|  |  | 
|  | for (i = pinfo.first_event; i != -1; i = pfm_get_event_next(i)) { | 
|  | ret = pfm_get_event_info(i, options.os, &info); | 
|  | if (ret != PFM_SUCCESS) | 
|  | errx(1, "cannot get event info: %s", pfm_strerror(ret)); | 
|  |  | 
|  | len = strlen(info.name) + strlen(pinfo.name) + 1 + 2; | 
|  | if (len > l) { | 
|  | l = len; | 
|  | fullname = realloc(fullname, l); | 
|  | if (!fullname) | 
|  | err(1, "cannot allocate memory"); | 
|  | } | 
|  | sprintf(fullname, "%s::%s", pinfo.name, info.name); | 
|  |  | 
|  | if (regexec(preg, fullname, 0, NULL, 0) == 0) { | 
|  | if (options.compact) | 
|  | if (options.combo) | 
|  | show_event_info_combo(&info); | 
|  | else | 
|  | show_event_info_compact(&info); | 
|  | else | 
|  | show_event_info(&info); | 
|  | match++; | 
|  | } | 
|  | } | 
|  | } | 
|  | if (fullname) | 
|  | free(fullname); | 
|  |  | 
|  | return match; | 
|  | } | 
|  |  | 
|  | static int | 
|  | show_info_sorted(char *event, regex_t *preg) | 
|  | { | 
|  | pfm_pmu_info_t pinfo; | 
|  | pfm_event_info_t info; | 
|  | unsigned int j; | 
|  | int i, ret, n, match = 0; | 
|  | size_t len, l = 0; | 
|  | char *fullname = NULL; | 
|  | code_info_t *codes; | 
|  |  | 
|  | memset(&pinfo, 0, sizeof(pinfo)); | 
|  | memset(&info, 0, sizeof(info)); | 
|  |  | 
|  | pinfo.size = sizeof(pinfo); | 
|  | info.size = sizeof(info); | 
|  |  | 
|  | pfm_for_all_pmus(j) { | 
|  |  | 
|  | ret = pfm_get_pmu_info(j, &pinfo); | 
|  | if (ret != PFM_SUCCESS) | 
|  | continue; | 
|  |  | 
|  | codes = malloc(pinfo.nevents * sizeof(*codes)); | 
|  | if (!codes) | 
|  | err(1, "cannot allocate memory\n"); | 
|  |  | 
|  | /* scans all supported events */ | 
|  | n = 0; | 
|  | for (i = pinfo.first_event; i != -1; i = pfm_get_event_next(i)) { | 
|  |  | 
|  | ret = pfm_get_event_info(i, options.os, &info); | 
|  | if (ret != PFM_SUCCESS) | 
|  | errx(1, "cannot get event info: %s", pfm_strerror(ret)); | 
|  |  | 
|  | if (info.pmu != j) | 
|  | continue; | 
|  |  | 
|  | codes[n].idx = info.idx; | 
|  | codes[n].code = info.code; | 
|  | n++; | 
|  | } | 
|  | qsort(codes, n, sizeof(*codes), compare_codes); | 
|  | for(i=0; i < n; i++) { | 
|  | ret = pfm_get_event_info(codes[i].idx, options.os, &info); | 
|  | if (ret != PFM_SUCCESS) | 
|  | errx(1, "cannot get event info: %s", pfm_strerror(ret)); | 
|  |  | 
|  | len = strlen(info.name) + strlen(pinfo.name) + 1 + 2; | 
|  | if (len > l) { | 
|  | l = len; | 
|  | fullname = realloc(fullname, l); | 
|  | if (!fullname) | 
|  | err(1, "cannot allocate memory"); | 
|  | } | 
|  | sprintf(fullname, "%s::%s", pinfo.name, info.name); | 
|  |  | 
|  | if (regexec(preg, fullname, 0, NULL, 0) == 0) { | 
|  | if (options.compact) | 
|  | show_event_info_compact(&info); | 
|  | else | 
|  | show_event_info(&info); | 
|  | match++; | 
|  | } | 
|  | } | 
|  | free(codes); | 
|  | } | 
|  | if (fullname) | 
|  | free(fullname); | 
|  |  | 
|  | return match; | 
|  | } | 
|  |  | 
|  | static void | 
|  | usage(void) | 
|  | { | 
|  | printf("showevtinfo [-L] [-E] [-h] [-s] [-m mask]\n" | 
|  | "-L\t\tlist one event per line (compact mode)\n" | 
|  | "-E\t\tlist one event per line with encoding (compact mode)\n" | 
|  | "-M\t\tdisplay all valid unit masks combination (use with -L or -E)\n" | 
|  | "-h\t\tget help\n" | 
|  | "-s\t\tsort event by PMU and by code based on -m mask\n" | 
|  | "-l\t\tmaximum number of umasks to list all combinations (default: %d)\n" | 
|  | "-F\t\tshow only events and attributes with certain flags (precise,...)\n" | 
|  | "-m mask\t\thexadecimal event code mask, bits to match when sorting\n" | 
|  | "-x sep\t\tuse sep as field separator in compact mode\n" | 
|  | "-D\t\t\tprint event description in compact mode\n" | 
|  | "-O os\t\tshow attributes for the specific operating system\n", | 
|  | COMBO_MAX); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * keep: [pmu::]event | 
|  | * drop everything else | 
|  | */ | 
|  | static void | 
|  | drop_event_attributes(char *str) | 
|  | { | 
|  | char *p; | 
|  |  | 
|  | p = strchr(str, ':'); | 
|  | if (!p) | 
|  | return; | 
|  |  | 
|  | str = p+1; | 
|  | /* keep PMU name */ | 
|  | if (*str == ':') | 
|  | str++; | 
|  |  | 
|  | /* stop string at 1st attribute */ | 
|  | p = strchr(str, ':'); | 
|  | if (p) | 
|  | *p = '\0'; | 
|  | } | 
|  |  | 
|  | #define EVENT_FLAGS(n, f, l) { .name = n, .ebit = f, .ubit = l } | 
|  | struct attr_flags { | 
|  | const char *name; | 
|  | int ebit; /* bit position in pfm_event_info_t.flags, -1 means ignore */ | 
|  | int ubit; /*  bit position in pfm_event_attr_info_t.flags, -1 means ignore */ | 
|  | }; | 
|  |  | 
|  | static const struct attr_flags  event_flags[]={ | 
|  | EVENT_FLAGS("precise", 0, 1), | 
|  | EVENT_FLAGS("pebs", 0, 1), | 
|  | EVENT_FLAGS("default", -1, 0), | 
|  | EVENT_FLAGS("dfl", -1, 0), | 
|  | EVENT_FLAGS(NULL, 0, 0) | 
|  | }; | 
|  |  | 
|  | static void | 
|  | parse_filters(char *arg) | 
|  | { | 
|  | const struct attr_flags *attr; | 
|  | char *p; | 
|  |  | 
|  | while (arg) { | 
|  | p = strchr(arg, ','); | 
|  | if (p) | 
|  | *p++ = 0; | 
|  |  | 
|  | for (attr = event_flags; attr->name; attr++) { | 
|  | if (!strcasecmp(attr->name, arg)) { | 
|  | switch(attr->ebit) { | 
|  | case 0: | 
|  | options.efilter.is_precise = 1; | 
|  | break; | 
|  | case -1: | 
|  | break; | 
|  | default: | 
|  | errx(1, "unknown event flag %d", attr->ebit); | 
|  | } | 
|  | switch (attr->ubit) { | 
|  | case 0: | 
|  | options.ufilter.is_dfl = 1; | 
|  | break; | 
|  | case 1: | 
|  | options.ufilter.is_precise = 1; | 
|  | break; | 
|  | case -1: | 
|  | break; | 
|  | default: | 
|  | errx(1, "unknown umaks flag %d", attr->ubit); | 
|  | } | 
|  | break; | 
|  | } | 
|  | } | 
|  | arg = p; | 
|  | } | 
|  | } | 
|  |  | 
|  | static const struct { | 
|  | char *name; | 
|  | pfm_os_t os; | 
|  | } supported_oses[]={ | 
|  | { .name = "none", .os = PFM_OS_NONE }, | 
|  | { .name = "raw", .os = PFM_OS_NONE }, | 
|  | { .name = "pmu", .os = PFM_OS_NONE }, | 
|  |  | 
|  | { .name = "perf", .os = PFM_OS_PERF_EVENT}, | 
|  | { .name = "perf_ext", .os = PFM_OS_PERF_EVENT_EXT}, | 
|  | { .name = NULL, } | 
|  | }; | 
|  |  | 
|  | static const char *pmu_types[]={ | 
|  | "unknown type", | 
|  | "core", | 
|  | "uncore", | 
|  | "OS generic", | 
|  | }; | 
|  |  | 
|  | static void | 
|  | setup_os(char *ostr) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | for (i = 0; supported_oses[i].name; i++) { | 
|  | if (!strcmp(supported_oses[i].name, ostr)) { | 
|  | options.os = supported_oses[i].os; | 
|  | return; | 
|  | } | 
|  | } | 
|  | fprintf(stderr, "unknown OS layer %s, choose from:", ostr); | 
|  | for (i = 0; supported_oses[i].name; i++) { | 
|  | if (i) | 
|  | fputc(',', stderr); | 
|  | fprintf(stderr, " %s", supported_oses[i].name); | 
|  | } | 
|  | fputc('\n', stderr); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | int | 
|  | main(int argc, char **argv) | 
|  | { | 
|  | static char *argv_all[2] = { ".*", NULL }; | 
|  | pfm_pmu_info_t pinfo; | 
|  | char *endptr = NULL; | 
|  | char default_sep[2] = "\t"; | 
|  | char *ostr = NULL; | 
|  | char **args; | 
|  | int i, match; | 
|  | regex_t preg; | 
|  | int ret, c; | 
|  |  | 
|  | memset(&pinfo, 0, sizeof(pinfo)); | 
|  |  | 
|  | pinfo.size = sizeof(pinfo); | 
|  |  | 
|  | while ((c=getopt(argc, argv,"hELsm:Ml:F:x:DO:")) != -1) { | 
|  | switch(c) { | 
|  | case 'L': | 
|  | options.compact = 1; | 
|  | break; | 
|  | case 'F': | 
|  | parse_filters(optarg); | 
|  | break; | 
|  | case 'E': | 
|  | options.compact = 1; | 
|  | options.encode = 1; | 
|  | break; | 
|  | case 'M': | 
|  | options.combo = 1; | 
|  | break; | 
|  | case 's': | 
|  | options.sort = 1; | 
|  | break; | 
|  | case 'D': | 
|  | options.desc = 1; | 
|  | break; | 
|  | case 'l': | 
|  | options.combo_lim = atoi(optarg); | 
|  | break; | 
|  | case 'x': | 
|  | options.csv_sep = optarg; | 
|  | break; | 
|  | case 'O': | 
|  | ostr = optarg; | 
|  | break; | 
|  | case 'm': | 
|  | options.mask = strtoull(optarg, &endptr, 16); | 
|  | if (*endptr) | 
|  | errx(1, "mask must be in hexadecimal\n"); | 
|  | 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)); | 
|  |  | 
|  | if (options.mask == 0) | 
|  | options.mask = ~0; | 
|  |  | 
|  | if (optind == argc) { | 
|  | args = argv_all; | 
|  | } else { | 
|  | args = argv + optind; | 
|  | } | 
|  | if (!options.csv_sep) | 
|  | options.csv_sep = default_sep; | 
|  |  | 
|  | /* avoid combinatorial explosion */ | 
|  | if (options.combo_lim == 0) | 
|  | options.combo_lim = COMBO_MAX; | 
|  |  | 
|  | if (ostr) | 
|  | setup_os(ostr); | 
|  | else | 
|  | options.os = PFM_OS_NONE; | 
|  |  | 
|  | if (!options.compact) { | 
|  | int total_supported_events = 0; | 
|  | int total_available_events = 0; | 
|  |  | 
|  | printf("Supported PMU models:\n"); | 
|  | pfm_for_all_pmus(i) { | 
|  | ret = pfm_get_pmu_info(i, &pinfo); | 
|  | if (ret != PFM_SUCCESS) | 
|  | continue; | 
|  |  | 
|  | printf("\t[%d, %s, \"%s\"]\n", i, pinfo.name,  pinfo.desc); | 
|  | } | 
|  |  | 
|  | printf("Detected PMU models:\n"); | 
|  | pfm_for_all_pmus(i) { | 
|  | ret = pfm_get_pmu_info(i, &pinfo); | 
|  | if (ret != PFM_SUCCESS) | 
|  | continue; | 
|  |  | 
|  | if (pinfo.is_present) { | 
|  | if (pinfo.type >= PFM_PMU_TYPE_MAX) | 
|  | pinfo.type = PFM_PMU_TYPE_UNKNOWN; | 
|  |  | 
|  | printf("\t[%d, %s, \"%s\", %d events, %d max encoding, %d counters, %s PMU]\n", | 
|  | i, | 
|  | pinfo.name, | 
|  | pinfo.desc, | 
|  | pinfo.nevents, | 
|  | pinfo.max_encoding, | 
|  | pinfo.num_cntrs + pinfo.num_fixed_cntrs, | 
|  | pmu_types[pinfo.type]); | 
|  |  | 
|  | total_supported_events += pinfo.nevents; | 
|  | } | 
|  | total_available_events += pinfo.nevents; | 
|  | } | 
|  | printf("Total events: %d available, %d supported\n", total_available_events, total_supported_events); | 
|  | } | 
|  |  | 
|  | while(*args) { | 
|  | /* drop umasks and modifiers */ | 
|  | drop_event_attributes(*args); | 
|  | if (regcomp(&preg, *args, REG_ICASE)) | 
|  | errx(1, "error in regular expression for event \"%s\"", *argv); | 
|  |  | 
|  | if (options.sort) | 
|  | match = show_info_sorted(*args, &preg); | 
|  | else | 
|  | match = show_info(*args, &preg); | 
|  |  | 
|  | if (match == 0) | 
|  | errx(1, "event %s not found", *args); | 
|  |  | 
|  | args++; | 
|  | } | 
|  |  | 
|  | regfree(&preg); | 
|  |  | 
|  | pfm_terminate(); | 
|  |  | 
|  | return 0; | 
|  | } |