| /* |
| * pfmlib_common.c: set of functions common to all PMU models |
| * |
| * Copyright (c) 2009 Google, Inc |
| * Contributed by Stephane Eranian <eranian@gmail.com> |
| * |
| * Based on: |
| * Copyright (c) 2001-2006 Hewlett-Packard Development Company, L.P. |
| * Contributed by Stephane Eranian <eranian@hpl.hp.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. |
| */ |
| #include <sys/types.h> |
| #include <ctype.h> |
| #include <string.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <stdarg.h> |
| #include <limits.h> |
| |
| #include <perfmon/pfmlib.h> |
| |
| #include "pfmlib_priv.h" |
| |
| static pfmlib_pmu_t *pfmlib_pmus[]= |
| { |
| |
| #ifdef CONFIG_PFMLIB_ARCH_IA64 |
| #if 0 |
| &montecito_support, |
| &itanium2_support, |
| &itanium_support, |
| &generic_ia64_support, /* must always be last for IA-64 */ |
| #endif |
| #endif |
| |
| #ifdef CONFIG_PFMLIB_ARCH_I386 |
| /* 32-bit only processors */ |
| &intel_pii_support, |
| &intel_ppro_support, |
| &intel_p6_support, |
| &intel_pm_support, |
| &intel_coreduo_support, |
| #endif |
| |
| #ifdef CONFIG_PFMLIB_ARCH_X86 |
| /* 32 and 64 bit processors */ |
| &netburst_support, |
| &netburst_p_support, |
| &amd64_k7_support, |
| &amd64_k8_revb_support, |
| &amd64_k8_revc_support, |
| &amd64_k8_revd_support, |
| &amd64_k8_reve_support, |
| &amd64_k8_revf_support, |
| &amd64_k8_revg_support, |
| &amd64_fam10h_barcelona_support, |
| &amd64_fam10h_shanghai_support, |
| &amd64_fam10h_istanbul_support, |
| &amd64_fam11h_turion_support, |
| &amd64_fam12h_llano_support, |
| &amd64_fam14h_bobcat_support, |
| &amd64_fam15h_interlagos_support, |
| &amd64_fam15h_nb_support, |
| &intel_core_support, |
| &intel_atom_support, |
| &intel_nhm_support, |
| &intel_nhm_ex_support, |
| &intel_nhm_unc_support, |
| &intel_wsm_sp_support, |
| &intel_wsm_dp_support, |
| &intel_wsm_unc_support, |
| &intel_snb_support, |
| &intel_snb_unc_cbo0_support, |
| &intel_snb_unc_cbo1_support, |
| &intel_snb_unc_cbo2_support, |
| &intel_snb_unc_cbo3_support, |
| &intel_snb_ep_support, |
| &intel_ivb_support, |
| &intel_ivb_unc_cbo0_support, |
| &intel_ivb_unc_cbo1_support, |
| &intel_ivb_unc_cbo2_support, |
| &intel_ivb_unc_cbo3_support, |
| &intel_ivb_ep_support, |
| &intel_hsw_support, |
| &intel_hsw_ep_support, |
| &intel_bdw_support, |
| &intel_rapl_support, |
| &intel_snbep_unc_cb0_support, |
| &intel_snbep_unc_cb1_support, |
| &intel_snbep_unc_cb2_support, |
| &intel_snbep_unc_cb3_support, |
| &intel_snbep_unc_cb4_support, |
| &intel_snbep_unc_cb5_support, |
| &intel_snbep_unc_cb6_support, |
| &intel_snbep_unc_cb7_support, |
| &intel_snbep_unc_ha_support, |
| &intel_snbep_unc_imc0_support, |
| &intel_snbep_unc_imc1_support, |
| &intel_snbep_unc_imc2_support, |
| &intel_snbep_unc_imc3_support, |
| &intel_snbep_unc_pcu_support, |
| &intel_snbep_unc_qpi0_support, |
| &intel_snbep_unc_qpi1_support, |
| &intel_snbep_unc_ubo_support, |
| &intel_snbep_unc_r2pcie_support, |
| &intel_snbep_unc_r3qpi0_support, |
| &intel_snbep_unc_r3qpi1_support, |
| &intel_knc_support, |
| &intel_slm_support, |
| &intel_ivbep_unc_cb0_support, |
| &intel_ivbep_unc_cb1_support, |
| &intel_ivbep_unc_cb2_support, |
| &intel_ivbep_unc_cb3_support, |
| &intel_ivbep_unc_cb4_support, |
| &intel_ivbep_unc_cb5_support, |
| &intel_ivbep_unc_cb6_support, |
| &intel_ivbep_unc_cb7_support, |
| &intel_ivbep_unc_cb8_support, |
| &intel_ivbep_unc_cb9_support, |
| &intel_ivbep_unc_cb10_support, |
| &intel_ivbep_unc_cb11_support, |
| &intel_ivbep_unc_cb12_support, |
| &intel_ivbep_unc_cb13_support, |
| &intel_ivbep_unc_cb14_support, |
| &intel_ivbep_unc_ha0_support, |
| &intel_ivbep_unc_ha1_support, |
| &intel_ivbep_unc_imc0_support, |
| &intel_ivbep_unc_imc1_support, |
| &intel_ivbep_unc_imc2_support, |
| &intel_ivbep_unc_imc3_support, |
| &intel_ivbep_unc_imc4_support, |
| &intel_ivbep_unc_imc5_support, |
| &intel_ivbep_unc_imc6_support, |
| &intel_ivbep_unc_imc7_support, |
| &intel_ivbep_unc_pcu_support, |
| &intel_ivbep_unc_qpi0_support, |
| &intel_ivbep_unc_qpi1_support, |
| &intel_ivbep_unc_qpi2_support, |
| &intel_ivbep_unc_ubo_support, |
| &intel_ivbep_unc_r2pcie_support, |
| &intel_ivbep_unc_r3qpi0_support, |
| &intel_ivbep_unc_r3qpi1_support, |
| &intel_ivbep_unc_r3qpi2_support, |
| &intel_ivbep_unc_irp_support, |
| &intel_x86_arch_support, /* must always be last for x86 */ |
| #endif |
| |
| #ifdef CONFIG_PFMLIB_ARCH_MIPS |
| &mips_74k_support, |
| #endif |
| |
| #ifdef CONFIG_PFMLIB_ARCH_SICORTEX |
| &sicortex_support, |
| #endif |
| |
| #ifdef CONFIG_PFMLIB_ARCH_POWERPC |
| &power4_support, |
| &ppc970_support, |
| &ppc970mp_support, |
| &power5_support, |
| &power5p_support, |
| &power6_support, |
| &power7_support, |
| &power8_support, |
| &torrent_support, |
| #endif |
| |
| #ifdef CONFIG_PFMLIB_ARCH_SPARC |
| &sparc_ultra12_support, |
| &sparc_ultra3_support, |
| &sparc_ultra3i_support, |
| &sparc_ultra3plus_support, |
| &sparc_ultra4plus_support, |
| &sparc_niagara1_support, |
| &sparc_niagara2_support, |
| #endif |
| |
| #ifdef CONFIG_PFMLIB_CELL |
| &cell_support, |
| #endif |
| |
| #ifdef CONFIG_PFMLIB_ARCH_ARM |
| &arm_cortex_a7_support, |
| &arm_cortex_a8_support, |
| &arm_cortex_a9_support, |
| &arm_cortex_a15_support, |
| &arm_1176_support, |
| &arm_qcom_krait_support, |
| &arm_cortex_a57_support, |
| &arm_cortex_a53_support, |
| &arm_xgene_support, |
| #endif |
| #ifdef CONFIG_PFMLIB_ARCH_ARM64 |
| &arm_cortex_a57_support, |
| &arm_cortex_a53_support, |
| &arm_xgene_support, |
| #endif |
| |
| #ifdef CONFIG_PFMLIB_ARCH_S390X |
| &s390x_cpum_cf_support, |
| &s390x_cpum_sf_support, |
| #endif |
| #ifdef __linux__ |
| &perf_event_support, |
| &perf_event_raw_support, |
| #endif |
| }; |
| #define PFMLIB_NUM_PMUS (int)(sizeof(pfmlib_pmus)/sizeof(pfmlib_pmu_t *)) |
| |
| static pfmlib_os_t pfmlib_os_none; |
| pfmlib_os_t *pfmlib_os = &pfmlib_os_none; |
| |
| static pfmlib_os_t *pfmlib_oses[]={ |
| &pfmlib_os_none, |
| #ifdef __linux__ |
| &pfmlib_os_perf, |
| &pfmlib_os_perf_ext, |
| #endif |
| }; |
| #define PFMLIB_NUM_OSES (int)(sizeof(pfmlib_oses)/sizeof(pfmlib_os_t *)) |
| |
| /* |
| * Mapping table from PMU index to pfmlib_pmu_t |
| * table is populated from pfmlib_pmus[] when the library |
| * is initialized. |
| * |
| * Some entries can be NULL if PMU is not implemented on host |
| * architecture or if the initialization failed. |
| */ |
| static pfmlib_pmu_t *pfmlib_pmus_map[PFM_PMU_MAX]; |
| |
| |
| #define pfmlib_for_each_pmu_event(p, e) \ |
| for(e=(p)->get_event_first((p)); e != -1; e = (p)->get_event_next((p), e)) |
| |
| #define for_each_pmu_event_attr(u, i) \ |
| for((u)=0; (u) < (i)->nattrs; (u) = (u)+1) |
| |
| #define pfmlib_for_each_pmu(x) \ |
| for((x)= 0 ; (x) < PFMLIB_NUM_PMUS; (x)++) |
| |
| #define pfmlib_for_each_pmu(x) \ |
| for((x)= 0 ; (x) < PFMLIB_NUM_PMUS; (x)++) |
| |
| #define pfmlib_for_each_os(x) \ |
| for((x)= 0 ; (x) < PFMLIB_NUM_OSES; (x)++) |
| |
| pfmlib_config_t pfm_cfg; |
| |
| void |
| __pfm_dbprintf(const char *fmt, ...) |
| { |
| va_list ap; |
| |
| if (pfm_cfg.debug == 0) |
| return; |
| |
| va_start(ap, fmt); |
| vfprintf(pfm_cfg.fp, fmt, ap); |
| va_end(ap); |
| } |
| |
| void |
| __pfm_vbprintf(const char *fmt, ...) |
| { |
| va_list ap; |
| |
| if (pfm_cfg.verbose == 0) |
| return; |
| |
| va_start(ap, fmt); |
| vfprintf(pfm_cfg.fp, fmt, ap); |
| va_end(ap); |
| } |
| |
| /* |
| * pfmlib_getl: our own equivalent to GNU getline() extension. |
| * This avoids a dependency on having a C library with |
| * support for getline(). |
| */ |
| int |
| pfmlib_getl(char **buffer, size_t *len, FILE *fp) |
| { |
| #define GETL_DFL_LEN 32 |
| char *b; |
| int c; |
| size_t maxsz, maxi, d, i = 0; |
| |
| if (!len || !fp || !buffer) |
| return -1; |
| |
| b = *buffer; |
| |
| if (!b) |
| *len = 0; |
| |
| maxsz = *len; |
| maxi = maxsz - 2; |
| |
| while ((c = fgetc(fp)) != EOF) { |
| if (maxsz == 0 || i == maxi) { |
| if (maxsz == 0) |
| maxsz = GETL_DFL_LEN; |
| else |
| maxsz <<= 1; |
| |
| if (*buffer) |
| d = &b[i] - *buffer; |
| else |
| d = 0; |
| |
| *buffer = realloc(*buffer, maxsz); |
| if (!*buffer) |
| return -1; |
| |
| b = *buffer + d; |
| maxi = maxsz - d - 2; |
| i = 0; |
| *len = maxsz; |
| } |
| b[i++] = c; |
| if (c == '\n') |
| break; |
| } |
| b[i] = '\0'; |
| return c != EOF ? 0 : -1; |
| } |
| |
| |
| |
| /* |
| * append fmt+args to str such that the string is no |
| * more than max characters incl. null termination |
| */ |
| void |
| pfmlib_strconcat(char *str, size_t max, const char *fmt, ...) |
| { |
| va_list ap; |
| size_t len, todo; |
| |
| len = strlen(str); |
| todo = max - strlen(str); |
| va_start(ap, fmt); |
| vsnprintf(str+len, todo, fmt, ap); |
| va_end(ap); |
| } |
| |
| /* |
| * compact all pattrs starting from index i |
| */ |
| void |
| pfmlib_compact_pattrs(pfmlib_event_desc_t *e, int i) |
| { |
| int j; |
| |
| for (j = i+1; j < e->npattrs; j++) |
| e->pattrs[j - 1] = e->pattrs[j]; |
| |
| e->npattrs--; |
| } |
| |
| static void |
| pfmlib_compact_attrs(pfmlib_event_desc_t *e, int i) |
| { |
| int j; |
| |
| for (j = i+1; j < e->nattrs; j++) |
| e->attrs[j - 1] = e->attrs[j]; |
| |
| e->nattrs--; |
| } |
| |
| /* |
| * 0 : different attribute |
| * 1 : exactly same attribute (duplicate can be removed) |
| * -1 : same attribute but value differ, this is an error |
| */ |
| static inline int |
| pfmlib_same_attr(pfmlib_event_desc_t *d, int i, int j) |
| { |
| pfm_event_attr_info_t *a1, *a2; |
| pfmlib_attr_t *b1, *b2; |
| |
| a1 = attr(d, i); |
| a2 = attr(d, j); |
| |
| b1 = d->attrs+i; |
| b2 = d->attrs+j; |
| |
| if (a1->idx == a2->idx |
| && a1->type == a2->type |
| && a1->ctrl == a2->ctrl) { |
| if (b1->ival == b2->ival) |
| return 1; |
| return -1; |
| |
| } |
| return 0; |
| } |
| |
| static inline int |
| pfmlib_pmu_active(pfmlib_pmu_t *pmu) |
| { |
| return !!(pmu->flags & PFMLIB_PMU_FL_ACTIVE); |
| } |
| |
| static inline int |
| pfmlib_pmu_initialized(pfmlib_pmu_t *pmu) |
| { |
| return !!(pmu->flags & PFMLIB_PMU_FL_INIT); |
| } |
| |
| static inline pfm_pmu_t |
| idx2pmu(int idx) |
| { |
| return (pfm_pmu_t)(idx >> PFMLIB_PMU_SHIFT) & PFMLIB_PMU_MASK; |
| } |
| |
| static inline pfmlib_pmu_t * |
| pmu2pmuidx(pfm_pmu_t pmu) |
| { |
| /* pfm_pmu_t is unsigned int enum, so |
| * just need to check for upper bound |
| */ |
| if (pmu >= PFM_PMU_MAX) |
| return NULL; |
| |
| return pfmlib_pmus_map[pmu]; |
| } |
| |
| /* |
| * external opaque idx -> PMU + internal idx |
| */ |
| static pfmlib_pmu_t * |
| pfmlib_idx2pidx(int idx, int *pidx) |
| { |
| pfmlib_pmu_t *pmu; |
| pfm_pmu_t pmu_id; |
| |
| if (PFMLIB_INITIALIZED() == 0) |
| return NULL; |
| |
| if (idx < 0) |
| return NULL; |
| |
| pmu_id = idx2pmu(idx); |
| |
| pmu = pmu2pmuidx(pmu_id); |
| if (!pmu) |
| return NULL; |
| |
| *pidx = idx & PFMLIB_PMU_PIDX_MASK; |
| |
| if (!pmu->event_is_valid(pmu, *pidx)) |
| return NULL; |
| |
| return pmu; |
| } |
| |
| static pfmlib_os_t * |
| pfmlib_find_os(pfm_os_t id) |
| { |
| int o; |
| pfmlib_os_t *os; |
| |
| pfmlib_for_each_os(o) { |
| os = pfmlib_oses[o]; |
| if (os->id == id && (os->flags & PFMLIB_OS_FL_ACTIVATED)) |
| return os; |
| } |
| return NULL; |
| } |
| |
| size_t |
| pfmlib_check_struct(void *st, size_t usz, size_t refsz, size_t sz) |
| { |
| size_t rsz = sz; |
| |
| /* |
| * if user size is zero, then use ABI0 size |
| */ |
| if (usz == 0) |
| usz = refsz; |
| |
| /* |
| * cannot be smaller than ABI0 size |
| */ |
| if (usz < refsz) { |
| DPRINT("pfmlib_check_struct: user size too small %zu\n", usz); |
| return 0; |
| } |
| |
| /* |
| * if bigger than current ABI, then check that none |
| * of the extra bits are set. This is to avoid mistake |
| * by caller assuming the library set those bits. |
| */ |
| if (usz > sz) { |
| char *addr = (char *)st + sz; |
| char *end = (char *)st + usz; |
| while (addr != end) { |
| if (*addr++) { |
| DPRINT("pfmlib_check_struct: invalid extra bits\n"); |
| return 0; |
| } |
| } |
| } |
| return rsz; |
| } |
| |
| /* |
| * check environment variables for: |
| * LIBPFM_VERBOSE : enable verbose output (must be 1) |
| * LIBPFM_DEBUG : enable debug output (must be 1) |
| */ |
| static void |
| pfmlib_init_env(void) |
| { |
| char *str; |
| |
| pfm_cfg.fp = stderr; |
| |
| str = getenv("LIBPFM_VERBOSE"); |
| if (str && isdigit((int)*str)) |
| pfm_cfg.verbose = *str - '0'; |
| |
| str = getenv("LIBPFM_DEBUG"); |
| if (str && isdigit((int)*str)) |
| pfm_cfg.debug = *str - '0'; |
| |
| str = getenv("LIBPFM_DEBUG_STDOUT"); |
| if (str) |
| pfm_cfg.fp = stdout; |
| |
| pfm_cfg.forced_pmu = getenv("LIBPFM_FORCE_PMU"); |
| |
| str = getenv("LIBPFM_ENCODE_INACTIVE"); |
| if (str) |
| pfm_cfg.inactive = 1; |
| |
| str = getenv("LIBPFM_DISABLED_PMUS"); |
| if (str) |
| pfm_cfg.blacklist_pmus = str; |
| } |
| |
| static int |
| pfmlib_pmu_sanity_checks(pfmlib_pmu_t *p) |
| { |
| /* |
| * check event can be encoded |
| */ |
| if (p->pme_count >= (1<< PFMLIB_PMU_SHIFT)) { |
| DPRINT("too many events for %s\n", p->desc); |
| return PFM_ERR_NOTSUPP; |
| } |
| |
| if (p->max_encoding > PFMLIB_MAX_ENCODING) { |
| DPRINT("max encoding too high (%d > %d) for %s\n", |
| p->max_encoding, PFMLIB_MAX_ENCODING, p->desc); |
| return PFM_ERR_NOTSUPP; |
| } |
| |
| return PFM_SUCCESS; |
| } |
| |
| int |
| pfmlib_build_fstr(pfmlib_event_desc_t *e, char **fstr) |
| { |
| /* nothing to do */ |
| if (!fstr) |
| return PFM_SUCCESS; |
| |
| *fstr = malloc(strlen(e->fstr) + 2 + strlen(e->pmu->name) + 1); |
| if (*fstr) |
| sprintf(*fstr, "%s::%s", e->pmu->name, e->fstr); |
| |
| return *fstr ? PFM_SUCCESS : PFM_ERR_NOMEM; |
| } |
| |
| static int |
| pfmlib_pmu_activate(pfmlib_pmu_t *p) |
| { |
| int ret; |
| |
| if (p->pmu_init) { |
| ret = p->pmu_init(p); |
| if (ret != PFM_SUCCESS) |
| return ret; |
| } |
| |
| p->flags |= PFMLIB_PMU_FL_ACTIVE; |
| |
| DPRINT("activated %s\n", p->desc); |
| |
| return PFM_SUCCESS; |
| } |
| |
| static inline int |
| pfmlib_match_forced_pmu(const char *name) |
| { |
| const char *p; |
| size_t l; |
| |
| /* skip any lower level specifier */ |
| p = strchr(pfm_cfg.forced_pmu, ','); |
| if (p) |
| l = p - pfm_cfg.forced_pmu; |
| else |
| l = strlen(pfm_cfg.forced_pmu); |
| |
| return !strncasecmp(name, pfm_cfg.forced_pmu, l); |
| } |
| |
| static int |
| pfmlib_is_blacklisted_pmu(pfmlib_pmu_t *p) |
| { |
| if (!pfm_cfg.blacklist_pmus) |
| return 0; |
| |
| /* |
| * scan list for matching PMU names, we accept substrings. |
| * for instance: snbep does match snbep* |
| */ |
| char *q, buffer[strlen(pfm_cfg.blacklist_pmus) + 1]; |
| |
| strcpy (buffer, pfm_cfg.blacklist_pmus); |
| for (q = strtok (buffer, ","); q != NULL; q = strtok (NULL, ",")) { |
| if (strstr (p->name, q) != NULL) { |
| return 1; |
| } |
| } |
| return 0; |
| } |
| |
| static int |
| pfmlib_init_pmus(void) |
| { |
| pfmlib_pmu_t *p; |
| int i, ret; |
| int nsuccess = 0; |
| |
| /* |
| * activate all detected PMUs |
| * when forced, only the designated PMU |
| * is setup and activated |
| */ |
| pfmlib_for_each_pmu(i) { |
| |
| p = pfmlib_pmus[i]; |
| |
| DPRINT("trying %s\n", p->desc); |
| |
| ret = PFM_SUCCESS; |
| |
| if (!pfm_cfg.forced_pmu) |
| ret = p->pmu_detect(p); |
| else if (!pfmlib_match_forced_pmu(p->name)) |
| ret = PFM_ERR_NOTSUPP; |
| |
| /* |
| * basic checks |
| * failure causes PMU to not be available |
| */ |
| if (pfmlib_pmu_sanity_checks(p) != PFM_SUCCESS) |
| continue; |
| |
| if (pfmlib_is_blacklisted_pmu(p)) { |
| DPRINT("%d PMU blacklisted, skipping initialization\n"); |
| continue; |
| } |
| p->flags |= PFMLIB_PMU_FL_INIT; |
| |
| /* |
| * populate mapping table |
| */ |
| pfmlib_pmus_map[p->pmu] = p; |
| |
| if (ret != PFM_SUCCESS) |
| continue; |
| |
| /* |
| * check if exported by OS if needed |
| */ |
| if (p->os_detect[pfmlib_os->id]) { |
| ret = p->os_detect[pfmlib_os->id](p); |
| if (ret != PFM_SUCCESS) { |
| DPRINT("%s PMU not exported by OS\n", p->name); |
| continue; |
| } |
| } |
| |
| ret = pfmlib_pmu_activate(p); |
| if (ret == PFM_SUCCESS) |
| nsuccess++; |
| |
| if (pfm_cfg.forced_pmu) { |
| __pfm_vbprintf("PMU forced to %s (%s) : %s\n", |
| p->name, |
| p->desc, |
| ret == PFM_SUCCESS ? "success" : "failure"); |
| return ret; |
| } |
| } |
| DPRINT("%d PMU detected out of %d supported\n", nsuccess, PFMLIB_NUM_PMUS); |
| return PFM_SUCCESS; |
| } |
| |
| static void |
| pfmlib_init_os(void) |
| { |
| int o; |
| pfmlib_os_t *os; |
| |
| pfmlib_for_each_os(o) { |
| os = pfmlib_oses[o]; |
| |
| if (!os->detect) |
| continue; |
| |
| if (os->detect(os) != PFM_SUCCESS) |
| continue; |
| |
| if (os != &pfmlib_os_none && pfmlib_os == &pfmlib_os_none) |
| pfmlib_os = os; |
| |
| DPRINT("OS layer %s activated\n", os->name); |
| os->flags = PFMLIB_OS_FL_ACTIVATED; |
| } |
| DPRINT("default OS layer: %s\n", pfmlib_os->name); |
| } |
| |
| int |
| pfm_initialize(void) |
| { |
| int ret; |
| /* |
| * not atomic |
| */ |
| if (pfm_cfg.initdone) |
| return PFM_SUCCESS; |
| |
| /* |
| * generic sanity checks |
| */ |
| if (PFM_PMU_MAX & (~PFMLIB_PMU_MASK)) { |
| DPRINT("PFM_PMU_MAX exceeds PFMLIB_PMU_MASK\n"); |
| return PFM_ERR_NOTSUPP; |
| } |
| |
| pfmlib_init_env(); |
| |
| /* must be done before pfmlib_init_pmus() */ |
| pfmlib_init_os(); |
| |
| ret = pfmlib_init_pmus(); |
| if (ret != PFM_SUCCESS) |
| return ret; |
| |
| |
| pfm_cfg.initdone = 1; |
| |
| return ret; |
| } |
| |
| void |
| pfm_terminate(void) |
| { |
| pfmlib_pmu_t *pmu; |
| int i; |
| |
| if (PFMLIB_INITIALIZED() == 0) |
| return; |
| |
| pfmlib_for_each_pmu(i) { |
| pmu = pfmlib_pmus[i]; |
| if (!pfmlib_pmu_active(pmu)) |
| continue; |
| if (pmu->pmu_terminate) |
| pmu->pmu_terminate(pmu); |
| } |
| pfm_cfg.initdone = 0; |
| } |
| |
| int |
| pfm_find_event(const char *str) |
| { |
| pfmlib_event_desc_t e; |
| int ret; |
| |
| if (PFMLIB_INITIALIZED() == 0) |
| return PFM_ERR_NOINIT; |
| |
| if (!str) |
| return PFM_ERR_INVAL; |
| |
| memset(&e, 0, sizeof(e)); |
| |
| ret = pfmlib_parse_event(str, &e); |
| if (ret == PFM_SUCCESS) |
| return pfmlib_pidx2idx(e.pmu, e.event); |
| |
| return ret; |
| } |
| |
| static int |
| pfmlib_sanitize_event(pfmlib_event_desc_t *d) |
| { |
| int i, j, ret; |
| |
| /* |
| * fail if duplicate attributes are found |
| */ |
| for(i=0; i < d->nattrs; i++) { |
| for(j=i+1; j < d->nattrs; j++) { |
| ret = pfmlib_same_attr(d, i, j); |
| if (ret == 1) |
| pfmlib_compact_attrs(d, j); |
| else if (ret == -1) |
| return PFM_ERR_ATTR_SET; |
| } |
| } |
| return PFM_SUCCESS; |
| } |
| |
| static int |
| pfmlib_parse_event_attr(char *str, pfmlib_event_desc_t *d) |
| { |
| pfm_event_attr_info_t *ainfo; |
| char *s, *p, *q, *endptr; |
| char yes[2] = "y"; |
| pfm_attr_t type; |
| int aidx = 0, has_val, has_raw_um = 0, has_um = 0; |
| int ret = PFM_ERR_INVAL; |
| |
| s = str; |
| |
| while(s) { |
| p = strchr(s, PFMLIB_ATTR_DELIM); |
| if (p) |
| *p++ = '\0'; |
| |
| q = strchr(s, '='); |
| if (q) |
| *q++ = '\0'; |
| |
| has_val = !!q; |
| |
| /* |
| * check for raw umasks in hexdecimal only |
| */ |
| if (*s == '0' && tolower(*(s+1)) == 'x') { |
| char *endptr = NULL; |
| |
| /* can only have one raw umask */ |
| if (has_raw_um || has_um) { |
| DPRINT("cannot mix raw umask with umask\n"); |
| return PFM_ERR_ATTR; |
| } |
| if (!(d->pmu->flags & PFMLIB_PMU_FL_RAW_UMASK)) { |
| DPRINT("PMU %s does not support RAW umasks\n", d->pmu->name); |
| return PFM_ERR_ATTR; |
| } |
| |
| /* we have reserved an entry at the end of pattrs */ |
| aidx = d->npattrs; |
| ainfo = d->pattrs + aidx; |
| |
| ainfo->name = "RAW_UMASK"; |
| ainfo->type = PFM_ATTR_RAW_UMASK; |
| ainfo->ctrl = PFM_ATTR_CTRL_PMU; |
| ainfo->idx = strtoul(s, &endptr, 0); |
| ainfo->equiv= NULL; |
| if (*endptr) { |
| DPRINT("raw umask (%s) is not a number\n"); |
| return PFM_ERR_ATTR; |
| } |
| |
| has_raw_um = 1; |
| |
| goto found_attr; |
| } |
| |
| for(aidx = 0; aidx < d->npattrs; aidx++) { |
| if (!strcasecmp(d->pattrs[aidx].name, s)) { |
| ainfo = d->pattrs + aidx; |
| /* disambiguate modifier and umask |
| * with the same name : snb::L2_LINES_IN:I:I=1 |
| */ |
| if (has_val && ainfo->type == PFM_ATTR_UMASK) |
| continue; |
| goto found_attr; |
| } |
| } |
| DPRINT("cannot find attribute %s\n", s); |
| return PFM_ERR_ATTR; |
| found_attr: |
| type = ainfo->type; |
| |
| if (type == PFM_ATTR_UMASK) { |
| has_um = 1; |
| if (has_raw_um) { |
| DPRINT("cannot mix raw umask with umask\n"); |
| return PFM_ERR_ATTR; |
| } |
| } |
| |
| if (ainfo->equiv) { |
| char *z; |
| |
| /* cannot have equiv for attributes with value */ |
| if (has_val) |
| return PFM_ERR_ATTR_VAL; |
| |
| /* copy because it is const */ |
| z = strdup(ainfo->equiv); |
| if (!z) |
| return PFM_ERR_NOMEM; |
| |
| ret = pfmlib_parse_event_attr(z, d); |
| |
| free(z); |
| |
| if (ret != PFM_SUCCESS) |
| return ret; |
| s = p; |
| continue; |
| } |
| /* |
| * we tolerate missing value for boolean attributes. |
| * Presence of the attribute is equivalent to |
| * attr=1, i.e., attribute is set |
| */ |
| if (type != PFM_ATTR_UMASK && type != PFM_ATTR_RAW_UMASK && !has_val) { |
| if (type != PFM_ATTR_MOD_BOOL) |
| return PFM_ERR_ATTR_VAL; |
| s = yes; /* no const */ |
| goto handle_bool; |
| } |
| |
| d->attrs[d->nattrs].ival = 0; |
| if ((type == PFM_ATTR_UMASK || type == PFM_ATTR_RAW_UMASK) && has_val) |
| return PFM_ERR_ATTR_VAL; |
| |
| if (has_val) { |
| s = q; |
| handle_bool: |
| ret = PFM_ERR_ATTR_VAL; |
| if (!strlen(s)) |
| goto error; |
| if (d->nattrs == PFMLIB_MAX_ATTRS) { |
| DPRINT("too many attributes\n"); |
| ret = PFM_ERR_TOOMANY; |
| goto error; |
| } |
| |
| endptr = NULL; |
| switch(type) { |
| case PFM_ATTR_MOD_BOOL: |
| if (strlen(s) > 1) |
| goto error; |
| |
| if (tolower((int)*s) == 'y' |
| || tolower((int)*s) == 't' || *s == '1') |
| d->attrs[d->nattrs].ival = 1; |
| else if (tolower((int)*s) == 'n' |
| || tolower((int)*s) == 'f' || *s == '0') |
| d->attrs[d->nattrs].ival = 0; |
| else |
| goto error; |
| break; |
| case PFM_ATTR_MOD_INTEGER: |
| d->attrs[d->nattrs].ival = strtoull(s, &endptr, 0); |
| if (*endptr != '\0') |
| goto error; |
| break; |
| default: |
| goto error; |
| } |
| } |
| d->attrs[d->nattrs].id = aidx; |
| d->nattrs++; |
| s = p; |
| } |
| ret = PFM_SUCCESS; |
| error: |
| return ret; |
| } |
| |
| static int |
| pfmlib_build_event_pattrs(pfmlib_event_desc_t *e) |
| { |
| pfmlib_pmu_t *pmu; |
| pfmlib_os_t *os; |
| int i, ret, pmu_nattrs = 0, os_nattrs = 0; |
| int npattrs; |
| |
| /* |
| * cannot satisfy request for an OS that was not activated |
| */ |
| os = pfmlib_find_os(e->osid); |
| if (!os) |
| return PFM_ERR_NOTSUPP; |
| |
| pmu = e->pmu; |
| |
| /* get actual PMU number of attributes for the event */ |
| if (pmu->get_event_nattrs) |
| pmu_nattrs = pmu->get_event_nattrs(pmu, e->event); |
| if (os && os->get_os_nattrs) |
| os_nattrs += os->get_os_nattrs(os, e); |
| |
| npattrs = pmu_nattrs + os_nattrs; |
| |
| /* |
| * add extra entry for raw umask, if supported |
| */ |
| if (pmu->flags & PFMLIB_PMU_FL_RAW_UMASK) |
| npattrs++; |
| |
| if (npattrs) { |
| e->pattrs = malloc(npattrs * sizeof(*e->pattrs)); |
| if (!e->pattrs) |
| return PFM_ERR_NOMEM; |
| } |
| |
| /* collect all actual PMU attrs */ |
| for(i = 0; i < pmu_nattrs; i++) { |
| ret = pmu->get_event_attr_info(pmu, e->event, i, e->pattrs+i); |
| if (ret != PFM_SUCCESS) |
| goto error; |
| } |
| e->npattrs = pmu_nattrs; |
| |
| if (os_nattrs) { |
| if (e->osid == os->id && os->get_os_attr_info) { |
| os->get_os_attr_info(os, e); |
| /* |
| * check for conflicts between HW and OS attributes |
| */ |
| if (pmu->validate_pattrs[e->osid]) |
| pmu->validate_pattrs[e->osid](pmu, e); |
| } |
| } |
| for (i = 0; i < e->npattrs; i++) |
| DPRINT("%d %d %d %d %d %s\n", e->event, i, e->pattrs[i].type, e->pattrs[i].ctrl, e->pattrs[i].idx, e->pattrs[i].name); |
| |
| return PFM_SUCCESS; |
| error: |
| free(e->pattrs); |
| e->pattrs = NULL; |
| return ret; |
| } |
| |
| void |
| pfmlib_release_event(pfmlib_event_desc_t *e) |
| { |
| free(e->pattrs); |
| e->pattrs = NULL; |
| } |
| |
| static int |
| match_event(void *this, pfmlib_event_desc_t *d, const char *e, const char *s) |
| { |
| return strcasecmp(e, s); |
| } |
| |
| static int |
| pfmlib_parse_equiv_event(const char *event, pfmlib_event_desc_t *d) |
| { |
| pfmlib_pmu_t *pmu = d->pmu; |
| pfm_event_info_t einfo; |
| int (*match)(void *this, pfmlib_event_desc_t *d, const char *e, const char *s); |
| char *str, *s, *p; |
| int i; |
| int ret; |
| |
| /* |
| * create copy because string is const |
| */ |
| s = str = strdup(event); |
| if (!str) |
| return PFM_ERR_NOMEM; |
| |
| p = strchr(s, PFMLIB_ATTR_DELIM); |
| if (p) |
| *p++ = '\0'; |
| |
| match = pmu->match_event ? pmu->match_event : match_event; |
| |
| pfmlib_for_each_pmu_event(pmu, i) { |
| ret = pmu->get_event_info(pmu, i, &einfo); |
| if (ret != PFM_SUCCESS) |
| goto error; |
| if (!match(pmu, d, einfo.name, s)) |
| goto found; |
| } |
| free(str); |
| return PFM_ERR_NOTFOUND; |
| found: |
| d->pmu = pmu; |
| d->event = i; /* private index */ |
| |
| /* |
| * build_event_pattrs and parse_event_attr |
| * cannot be factorized with pfmlib_parse_event() |
| * because equivalent event may add its own attributes |
| */ |
| ret = pfmlib_build_event_pattrs(d); |
| if (ret != PFM_SUCCESS) |
| goto error; |
| |
| ret = pfmlib_parse_event_attr(p, d); |
| if (ret == PFM_SUCCESS) |
| ret = pfmlib_sanitize_event(d); |
| error: |
| free(str); |
| |
| if (ret != PFM_SUCCESS) |
| pfmlib_release_event(d); |
| |
| return ret; |
| } |
| |
| int |
| pfmlib_parse_event(const char *event, pfmlib_event_desc_t *d) |
| { |
| pfm_event_info_t einfo; |
| char *str, *s, *p; |
| pfmlib_pmu_t *pmu; |
| int (*match)(void *this, pfmlib_event_desc_t *d, const char *e, const char *s); |
| const char *pname = NULL; |
| int i, j, ret; |
| |
| /* |
| * create copy because string is const |
| */ |
| s = str = strdup(event); |
| if (!str) |
| return PFM_ERR_NOMEM; |
| |
| /* |
| * ignore everything passed after a comma |
| * (simplify dealing with const event list) |
| * |
| * safe to do before pname, because now |
| * PMU name cannot have commas in them. |
| */ |
| p = strchr(s, PFMLIB_EVENT_DELIM); |
| if (p) |
| *p = '\0'; |
| |
| /* check for optional PMU name */ |
| p = strstr(s, PFMLIB_PMU_DELIM); |
| if (p) { |
| *p = '\0'; |
| pname = s; |
| s = p + strlen(PFMLIB_PMU_DELIM); |
| } |
| p = strchr(s, PFMLIB_ATTR_DELIM); |
| if (p) |
| *p++ = '\0'; |
| /* |
| * for each pmu |
| */ |
| pfmlib_for_each_pmu(j) { |
| pmu = pfmlib_pmus[j]; |
| /* |
| * if no explicit PMU name is given, then |
| * only look for active PMU models |
| */ |
| if (!pname && !pfmlib_pmu_active(pmu)) |
| continue; |
| /* |
| * check for requested PMU name, |
| */ |
| if (pname && strcasecmp(pname, pmu->name)) |
| continue; |
| /* |
| * only allow event on inactive PMU if enabled via |
| * environement variable |
| */ |
| if (pname && !pfmlib_pmu_active(pmu) && !pfm_cfg.inactive) |
| continue; |
| |
| match = pmu->match_event ? pmu->match_event : match_event; |
| /* |
| * for each event |
| */ |
| pfmlib_for_each_pmu_event(pmu, i) { |
| ret = pmu->get_event_info(pmu, i, &einfo); |
| if (ret != PFM_SUCCESS) |
| goto error; |
| if (!match(pmu, d, einfo.name, s)) |
| goto found; |
| } |
| } |
| free(str); |
| return PFM_ERR_NOTFOUND; |
| found: |
| d->pmu = pmu; |
| /* |
| * handle equivalence |
| */ |
| if (einfo.equiv) { |
| ret = pfmlib_parse_equiv_event(einfo.equiv, d); |
| if (ret != PFM_SUCCESS) |
| goto error; |
| } else { |
| d->event = i; /* private index */ |
| |
| ret = pfmlib_build_event_pattrs(d); |
| if (ret != PFM_SUCCESS) |
| goto error; |
| } |
| /* |
| * parse attributes from original event |
| */ |
| ret = pfmlib_parse_event_attr(p, d); |
| if (ret == PFM_SUCCESS) |
| ret = pfmlib_sanitize_event(d); |
| |
| for (i = 0; i < d->nattrs; i++) { |
| pfm_event_attr_info_t *a = attr(d, i); |
| if (a->type != PFM_ATTR_RAW_UMASK) |
| DPRINT("%d %d %d %s\n", d->event, i, a->idx, d->pattrs[d->attrs[i].id].name); |
| else |
| DPRINT("%d %d RAW_UMASK (0x%x)\n", d->event, i, a->idx); |
| } |
| error: |
| free(str); |
| if (ret != PFM_SUCCESS) |
| pfmlib_release_event(d); |
| return ret; |
| } |
| |
| /* sorry, only English supported at this point! */ |
| static const char *pfmlib_err_list[]= |
| { |
| "success", |
| "not supported", |
| "invalid parameters", |
| "pfmlib not initialized", |
| "event not found", |
| "invalid combination of model specific features", |
| "invalid or missing unit mask", |
| "out of memory", |
| "invalid event attribute", |
| "invalid event attribute value", |
| "attribute value already set", |
| "too many parameters", |
| "parameter is too small", |
| }; |
| static int pfmlib_err_count = (int)sizeof(pfmlib_err_list)/sizeof(char *); |
| |
| const char * |
| pfm_strerror(int code) |
| { |
| code = -code; |
| if (code <0 || code >= pfmlib_err_count) |
| return "unknown error code"; |
| |
| return pfmlib_err_list[code]; |
| } |
| |
| int |
| pfm_get_version(void) |
| { |
| return LIBPFM_VERSION; |
| } |
| |
| int |
| pfm_get_event_next(int idx) |
| { |
| pfmlib_pmu_t *pmu; |
| int pidx; |
| |
| pmu = pfmlib_idx2pidx(idx, &pidx); |
| if (!pmu) |
| return -1; |
| |
| pidx = pmu->get_event_next(pmu, pidx); |
| return pidx == -1 ? -1 : pfmlib_pidx2idx(pmu, pidx); |
| } |
| |
| int |
| pfm_get_os_event_encoding(const char *str, int dfl_plm, pfm_os_t uos, void *args) |
| { |
| pfmlib_os_t *os; |
| |
| if (PFMLIB_INITIALIZED() == 0) |
| return PFM_ERR_NOINIT; |
| |
| if (!(args && str)) |
| return PFM_ERR_INVAL; |
| |
| if (dfl_plm & ~(PFM_PLM_ALL)) |
| return PFM_ERR_INVAL; |
| |
| os = pfmlib_find_os(uos); |
| if (!os) |
| return PFM_ERR_NOTSUPP; |
| |
| return os->encode(os, str, dfl_plm, args); |
| } |
| |
| /* |
| * old API maintained for backward compatibility with existing apps |
| * prefer pfm_get_os_event_encoding() |
| */ |
| int |
| pfm_get_event_encoding(const char *str, int dfl_plm, char **fstr, int *idx, uint64_t **codes, int *count) |
| { |
| pfm_pmu_encode_arg_t arg; |
| int ret; |
| |
| if (!(str && codes && count)) |
| return PFM_ERR_INVAL; |
| |
| if ((*codes && !*count) || (!*codes && *count)) |
| return PFM_ERR_INVAL; |
| |
| memset(&arg, 0, sizeof(arg)); |
| |
| arg.fstr = fstr; |
| arg.codes = *codes; |
| arg.count = *count; |
| arg.size = sizeof(arg); |
| |
| /* |
| * request RAW PMU encoding |
| */ |
| ret = pfm_get_os_event_encoding(str, dfl_plm, PFM_OS_NONE, &arg); |
| if (ret != PFM_SUCCESS) |
| return ret; |
| |
| /* handle the case where the array was allocated */ |
| *codes = arg.codes; |
| *count = arg.count; |
| |
| if (idx) |
| *idx = arg.idx; |
| |
| return PFM_SUCCESS; |
| } |
| |
| static int |
| pfmlib_check_event_pattrs(pfmlib_pmu_t *pmu, int pidx, pfm_os_t osid, FILE *fp) |
| { |
| pfmlib_event_desc_t e; |
| int i, j, ret; |
| |
| memset(&e, 0, sizeof(e)); |
| e.event = pidx; |
| e.osid = osid; |
| e.pmu = pmu; |
| |
| ret = pfmlib_build_event_pattrs(&e); |
| if (ret != PFM_SUCCESS) { |
| fprintf(fp, "invalid pattrs for event %d\n", pidx); |
| return ret; |
| } |
| |
| ret = PFM_ERR_ATTR; |
| |
| for (i = 0; i < e.npattrs; i++) { |
| for (j = i+1; j < e.npattrs; j++) { |
| if (!strcmp(e.pattrs[i].name, e.pattrs[j].name)) { |
| fprintf(fp, "event %d duplicate pattrs %s\n", pidx, e.pattrs[i].name); |
| goto error; |
| } |
| } |
| } |
| ret = PFM_SUCCESS; |
| error: |
| /* |
| * release resources allocated for event |
| */ |
| pfmlib_release_event(&e); |
| return ret; |
| } |
| |
| static int |
| pfmlib_validate_encoding(char *buf, int plm) |
| { |
| uint64_t *codes = NULL; |
| int count = 0, ret; |
| |
| ret = pfm_get_event_encoding(buf, plm, NULL, NULL, &codes, &count); |
| if (ret != PFM_SUCCESS) { |
| int i; |
| DPRINT("%s ", buf); |
| for(i=0; i < count; i++) |
| __pfm_dbprintf(" %#"PRIx64, codes[i]); |
| __pfm_dbprintf("\n"); |
| } |
| if (codes) |
| free(codes); |
| |
| return ret; |
| } |
| |
| static int |
| pfmlib_pmu_validate_encoding(pfmlib_pmu_t *pmu, FILE *fp) |
| { |
| pfm_event_info_t einfo; |
| pfm_event_attr_info_t ainfo; |
| char *buf; |
| size_t maxlen = 0, len; |
| int i, u, n = 0, um; |
| int ret, retval = PFM_SUCCESS; |
| |
| pfmlib_for_each_pmu_event(pmu, i) { |
| ret = pmu->get_event_info(pmu, i, &einfo); |
| if (ret != PFM_SUCCESS) |
| return ret; |
| |
| ret = pfmlib_check_event_pattrs(pmu, i, PFM_OS_NONE, fp); |
| if (ret != PFM_SUCCESS) |
| return ret; |
| |
| len = strlen(einfo.name); |
| if (len > maxlen) |
| maxlen = len; |
| |
| for_each_pmu_event_attr(u, &einfo) { |
| ret = pmu->get_event_attr_info(pmu, i, u, &ainfo); |
| if (ret != PFM_SUCCESS) |
| return ret; |
| |
| if (ainfo.type != PFM_ATTR_UMASK) |
| continue; |
| |
| len = strlen(einfo.name) + strlen(ainfo.name); |
| if (len > maxlen) |
| maxlen = len; |
| } |
| } |
| /* 2 = ::, 1=:, 1=eol */ |
| maxlen += strlen(pmu->name) + 2 + 1 + 1; |
| buf = malloc(maxlen); |
| if (!buf) |
| return PFM_ERR_NOMEM; |
| |
| pfmlib_for_each_pmu_event(pmu, i) { |
| ret = pmu->get_event_info(pmu, i, &einfo); |
| if (ret != PFM_SUCCESS) { |
| retval = ret; |
| continue; |
| } |
| |
| um = 0; |
| for_each_pmu_event_attr(u, &einfo) { |
| ret = pmu->get_event_attr_info(pmu, i, u, &ainfo); |
| if (ret != PFM_SUCCESS) { |
| retval = ret; |
| continue; |
| } |
| |
| if (ainfo.type != PFM_ATTR_UMASK) |
| continue; |
| |
| /* |
| * XXX: some events may require more than one umasks to encode |
| */ |
| sprintf(buf, "%s::%s:%s", pmu->name, einfo.name, ainfo.name); |
| ret = pfmlib_validate_encoding(buf, PFM_PLM3|PFM_PLM0); |
| if (ret != PFM_SUCCESS) { |
| if (pmu->can_auto_encode) { |
| if (!pmu->can_auto_encode(pmu, i, u)) |
| continue; |
| } |
| /* |
| * some PMU may not support raw encoding |
| */ |
| if (ret != PFM_ERR_NOTSUPP) { |
| fprintf(fp, "cannot encode event %s : %s\n", buf, pfm_strerror(ret)); |
| retval = ret; |
| } |
| continue; |
| } |
| um++; |
| } |
| if (um == 0) { |
| sprintf(buf, "%s::%s", pmu->name, einfo.name); |
| ret = pfmlib_validate_encoding(buf, PFM_PLM3|PFM_PLM0); |
| if (ret != PFM_SUCCESS) { |
| if (pmu->can_auto_encode) { |
| if (!pmu->can_auto_encode(pmu, i, u)) |
| continue; |
| } |
| if (ret != PFM_ERR_NOTSUPP) { |
| fprintf(fp, "cannot encode event %s : %s\n", buf, pfm_strerror(ret)); |
| retval = ret; |
| } |
| continue; |
| } |
| } |
| n++; |
| } |
| free(buf); |
| |
| return retval; |
| } |
| |
| int |
| pfm_pmu_validate(pfm_pmu_t pmu_id, FILE *fp) |
| { |
| pfmlib_pmu_t *pmu, *pmx; |
| int nos = 0; |
| int i, ret; |
| |
| if (fp == NULL) |
| return PFM_ERR_INVAL; |
| |
| pmu = pmu2pmuidx(pmu_id); |
| if (!pmu) |
| return PFM_ERR_INVAL; |
| |
| |
| if (!pfmlib_pmu_initialized(pmu)) { |
| fprintf(fp, "pmu: %s :: initialization failed\n", pmu->name); |
| return PFM_ERR_INVAL; |
| } |
| |
| if (!pmu->name) { |
| fprintf(fp, "pmu id: %d :: no name\n", pmu->pmu); |
| return PFM_ERR_INVAL; |
| } |
| |
| if (!pmu->desc) { |
| fprintf(fp, "pmu: %s :: no description\n", pmu->name); |
| return PFM_ERR_INVAL; |
| } |
| |
| if (pmu->pmu >= PFM_PMU_MAX) { |
| fprintf(fp, "pmu: %s :: invalid PMU id\n", pmu->name); |
| return PFM_ERR_INVAL; |
| } |
| |
| if (pmu->max_encoding >= PFMLIB_MAX_ENCODING) { |
| fprintf(fp, "pmu: %s :: max encoding too high\n", pmu->name); |
| return PFM_ERR_INVAL; |
| } |
| |
| if (pfmlib_pmu_active(pmu) && !pmu->pme_count) { |
| fprintf(fp, "pmu: %s :: no events\n", pmu->name); |
| return PFM_ERR_INVAL; |
| } |
| if (!pmu->pmu_detect) { |
| fprintf(fp, "pmu: %s :: missing pmu_detect callback\n", pmu->name); |
| return PFM_ERR_INVAL; |
| } |
| if (!pmu->get_event_first) { |
| fprintf(fp, "pmu: %s :: missing get_event_first callback\n", pmu->name); |
| return PFM_ERR_INVAL; |
| } |
| if (!pmu->get_event_next) { |
| fprintf(fp, "pmu: %s :: missing get_event_next callback\n", pmu->name); |
| return PFM_ERR_INVAL; |
| } |
| if (!pmu->get_event_info) { |
| fprintf(fp, "pmu: %s :: missing get_event_info callback\n", pmu->name); |
| return PFM_ERR_INVAL; |
| } |
| if (!pmu->get_event_attr_info) { |
| fprintf(fp, "pmu: %s :: missing get_event_attr_info callback\n", pmu->name); |
| return PFM_ERR_INVAL; |
| } |
| for (i = PFM_OS_NONE; i < PFM_OS_MAX; i++) { |
| if (pmu->get_event_encoding[i]) |
| nos++; |
| } |
| if (!nos) { |
| fprintf(fp, "pmu: %s :: no os event encoding callback\n", pmu->name); |
| return PFM_ERR_INVAL; |
| } |
| if (!pmu->max_encoding) { |
| fprintf(fp, "pmu: %s :: max_encoding is zero\n", pmu->name); |
| return PFM_ERR_INVAL; |
| } |
| |
| /* look for duplicate names, id */ |
| pfmlib_for_each_pmu(i) { |
| pmx = pfmlib_pmus[i]; |
| if (!pfmlib_pmu_active(pmx)) |
| continue; |
| if (pmx == pmu) |
| continue; |
| if (!strcasecmp(pmx->name, pmu->name)) { |
| fprintf(fp, "pmu: %s :: duplicate name\n", pmu->name); |
| return PFM_ERR_INVAL; |
| } |
| if (pmx->pmu == pmu->pmu) { |
| fprintf(fp, "pmu: %s :: duplicate id\n", pmu->name); |
| return PFM_ERR_INVAL; |
| } |
| } |
| |
| if (pmu->validate_table) { |
| ret = pmu->validate_table(pmu, fp); |
| if (ret != PFM_SUCCESS) |
| return ret; |
| } |
| return pfmlib_pmu_validate_encoding(pmu, fp); |
| } |
| |
| int |
| pfm_get_event_info(int idx, pfm_os_t os, pfm_event_info_t *uinfo) |
| { |
| pfm_event_info_t info; |
| pfmlib_event_desc_t e; |
| pfmlib_pmu_t *pmu; |
| size_t sz = sizeof(info); |
| int pidx, ret; |
| |
| if (!PFMLIB_INITIALIZED()) |
| return PFM_ERR_NOINIT; |
| |
| if (os >= PFM_OS_MAX) |
| return PFM_ERR_INVAL; |
| |
| pmu = pfmlib_idx2pidx(idx, &pidx); |
| if (!pmu) |
| return PFM_ERR_INVAL; |
| |
| if (!uinfo) |
| return PFM_ERR_INVAL; |
| |
| sz = pfmlib_check_struct(uinfo, uinfo->size, PFM_EVENT_INFO_ABI0, sz); |
| if (!sz) |
| return PFM_ERR_INVAL; |
| |
| memset(&info, 0, sizeof(info)); |
| |
| info.size = sz; |
| |
| /* default data type is uint64 */ |
| info.dtype = PFM_DTYPE_UINT64; |
| |
| /* reset flags */ |
| info.is_precise = 0; |
| |
| ret = pmu->get_event_info(pmu, pidx, &info); |
| if (ret != PFM_SUCCESS) |
| return ret; |
| |
| info.pmu = pmu->pmu; |
| info.idx = idx; |
| |
| memset(&e, 0, sizeof(e)); |
| e.event = pidx; |
| e.osid = os; |
| e.pmu = pmu; |
| |
| ret = pfmlib_build_event_pattrs(&e); |
| if (ret == PFM_SUCCESS) { |
| info.nattrs = e.npattrs; |
| memcpy(uinfo, &info, sz); |
| } |
| |
| pfmlib_release_event(&e); |
| return ret; |
| } |
| |
| int |
| pfm_get_event_attr_info(int idx, int attr_idx, pfm_os_t os, pfm_event_attr_info_t *uinfo) |
| { |
| pfm_event_attr_info_t info; |
| pfmlib_event_desc_t e; |
| pfmlib_pmu_t *pmu; |
| size_t sz = sizeof(info); |
| int pidx, ret; |
| |
| if (!PFMLIB_INITIALIZED()) |
| return PFM_ERR_NOINIT; |
| |
| if (attr_idx < 0) |
| return PFM_ERR_INVAL; |
| |
| if (os >= PFM_OS_MAX) |
| return PFM_ERR_INVAL; |
| |
| pmu = pfmlib_idx2pidx(idx, &pidx); |
| if (!pmu) |
| return PFM_ERR_INVAL; |
| |
| if (!uinfo) |
| return PFM_ERR_INVAL; |
| |
| sz = pfmlib_check_struct(uinfo, uinfo->size, PFM_ATTR_INFO_ABI0, sz); |
| if (!sz) |
| return PFM_ERR_INVAL; |
| |
| memset(&e, 0, sizeof(e)); |
| e.event = pidx; |
| e.osid = os; |
| e.pmu = pmu; |
| |
| ret = pfmlib_build_event_pattrs(&e); |
| if (ret != PFM_SUCCESS) |
| return ret; |
| |
| ret = PFM_ERR_INVAL; |
| |
| if (attr_idx >= e.npattrs) |
| goto error; |
| |
| /* |
| * copy event_attr_info |
| */ |
| info = e.pattrs[attr_idx]; |
| |
| /* |
| * rewrite size to reflect what we are returning |
| */ |
| info.size = sz; |
| /* |
| * info.idx = private, namespace specific index, |
| * should not be visible externally, so override |
| * with public index |
| */ |
| info.idx = attr_idx; |
| |
| memcpy(uinfo, &info, sz); |
| |
| ret = PFM_SUCCESS; |
| error: |
| pfmlib_release_event(&e); |
| return ret; |
| } |
| |
| int |
| pfm_get_pmu_info(pfm_pmu_t pmuid, pfm_pmu_info_t *uinfo) |
| { |
| pfm_pmu_info_t info; |
| pfmlib_pmu_t *pmu; |
| size_t sz = sizeof(info); |
| int pidx; |
| |
| if (!PFMLIB_INITIALIZED()) |
| return PFM_ERR_NOINIT; |
| |
| if (pmuid >= PFM_PMU_MAX) |
| return PFM_ERR_INVAL; |
| |
| if (!uinfo) |
| return PFM_ERR_INVAL; |
| |
| sz = pfmlib_check_struct(uinfo, uinfo->size, PFM_PMU_INFO_ABI0, sz); |
| if (!sz) |
| return PFM_ERR_INVAL; |
| |
| pmu = pfmlib_pmus_map[pmuid]; |
| if (!pmu) |
| return PFM_ERR_NOTSUPP; |
| |
| info.name = pmu->name; |
| info.desc = pmu->desc; |
| info.pmu = pmuid; |
| info.size = sz; |
| |
| info.max_encoding = pmu->max_encoding; |
| info.num_cntrs = pmu->num_cntrs; |
| info.num_fixed_cntrs = pmu->num_fixed_cntrs; |
| |
| pidx = pmu->get_event_first(pmu); |
| if (pidx == -1) |
| info.first_event = -1; |
| else |
| info.first_event = pfmlib_pidx2idx(pmu, pidx); |
| |
| /* |
| * XXX: pme_count only valid when PMU is detected |
| */ |
| info.is_present = pfmlib_pmu_active(pmu); |
| info.is_dfl = !!(pmu->flags & PFMLIB_PMU_FL_ARCH_DFL); |
| info.type = pmu->type; |
| |
| if (pmu->get_num_events) |
| info.nevents = pmu->get_num_events(pmu); |
| else |
| info.nevents = pmu->pme_count; |
| |
| memcpy(uinfo, &info, sz); |
| |
| return PFM_SUCCESS; |
| } |
| |
| pfmlib_pmu_t * |
| pfmlib_get_pmu_by_type(pfm_pmu_type_t t) |
| { |
| pfmlib_pmu_t *pmu; |
| int i; |
| |
| pfmlib_for_each_pmu(i) { |
| pmu = pfmlib_pmus[i]; |
| |
| if (!pfmlib_pmu_active(pmu)) |
| continue; |
| |
| /* first match */ |
| if (pmu->type != t) |
| continue; |
| |
| return pmu; |
| } |
| return NULL; |
| } |
| |
| static int |
| pfmlib_compare_attr_id(const void *a, const void *b) |
| { |
| const pfmlib_attr_t *t1 = a; |
| const pfmlib_attr_t *t2 = b; |
| |
| if (t1->id < t2->id) |
| return -1; |
| return t1->id == t2->id ? 0 : 1; |
| } |
| |
| void |
| pfmlib_sort_attr(pfmlib_event_desc_t *e) |
| { |
| qsort(e->attrs, e->nattrs, sizeof(pfmlib_attr_t), pfmlib_compare_attr_id); |
| } |
| |
| static int |
| pfmlib_raw_pmu_encode(void *this, const char *str, int dfl_plm, void *data) |
| { |
| pfm_pmu_encode_arg_t arg; |
| pfm_pmu_encode_arg_t *uarg = data; |
| |
| pfmlib_pmu_t *pmu; |
| pfmlib_event_desc_t e; |
| size_t sz = sizeof(arg); |
| int ret, i; |
| |
| sz = pfmlib_check_struct(uarg, uarg->size, PFM_RAW_ENCODE_ABI0, sz); |
| if (!sz) |
| return PFM_ERR_INVAL; |
| |
| memset(&arg, 0, sizeof(arg)); |
| |
| /* |
| * get input data |
| */ |
| memcpy(&arg, uarg, sz); |
| |
| memset(&e, 0, sizeof(e)); |
| |
| e.osid = PFM_OS_NONE; |
| e.dfl_plm = dfl_plm; |
| |
| ret = pfmlib_parse_event(str, &e); |
| if (ret != PFM_SUCCESS) |
| return ret; |
| |
| pmu = e.pmu; |
| |
| if (!pmu->get_event_encoding[PFM_OS_NONE]) { |
| DPRINT("PMU %s does not support PFM_OS_NONE\n", pmu->name); |
| return PFM_ERR_NOTSUPP; |
| } |
| |
| ret = pmu->get_event_encoding[PFM_OS_NONE](pmu, &e); |
| if (ret != PFM_SUCCESS) |
| goto error; |
| /* |
| * return opaque event identifier |
| */ |
| arg.idx = pfmlib_pidx2idx(e.pmu, e.event); |
| |
| if (arg.codes == NULL) { |
| ret = PFM_ERR_NOMEM; |
| arg.codes = malloc(sizeof(uint64_t) * e.count); |
| if (!arg.codes) |
| goto error_fstr; |
| } else if (arg.count < e.count) { |
| ret = PFM_ERR_TOOSMALL; |
| goto error_fstr; |
| } |
| |
| arg.count = e.count; |
| |
| for (i = 0; i < e.count; i++) |
| arg.codes[i] = e.codes[i]; |
| |
| if (arg.fstr) { |
| ret = pfmlib_build_fstr(&e, arg.fstr); |
| if (ret != PFM_SUCCESS) |
| goto error; |
| } |
| |
| ret = PFM_SUCCESS; |
| |
| /* copy out results */ |
| memcpy(uarg, &arg, sz); |
| |
| error_fstr: |
| if (ret != PFM_SUCCESS) |
| free(arg.fstr); |
| error: |
| /* |
| * release resources allocated for event |
| */ |
| pfmlib_release_event(&e); |
| return ret; |
| } |
| |
| static int |
| pfmlib_raw_pmu_detect(void *this) |
| { |
| return PFM_SUCCESS; |
| } |
| |
| static pfmlib_os_t pfmlib_os_none= { |
| .name = "No OS (raw PMU)", |
| .id = PFM_OS_NONE, |
| .flags = PFMLIB_OS_FL_ACTIVATED, |
| .encode = pfmlib_raw_pmu_encode, |
| .detect = pfmlib_raw_pmu_detect, |
| }; |