/* * pfmlib_amd64.c : support for the AMD64 architected PMU * (for both 64 and 32 bit modes) * * Copyright (c) 2009 Google, Inc * Contributed by Stephane Eranian * * Based on: * Copyright (c) 2005-2006 Hewlett-Packard Development Company, L.P. * Contributed by Stephane Eranian * * 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 #include #include /* private headers */ #include "pfmlib_priv.h" /* library private */ #include "pfmlib_amd64_priv.h" /* architecture private */ const pfmlib_attr_desc_t amd64_mods[]={ PFM_ATTR_B("k", "monitor at priv level 0"), /* monitor priv level 0 */ PFM_ATTR_B("u", "monitor at priv level 1, 2, 3"), /* monitor priv level 1, 2, 3 */ PFM_ATTR_B("e", "edge level"), /* edge */ PFM_ATTR_B("i", "invert"), /* invert */ PFM_ATTR_I("c", "counter-mask in range [0-255]"), /* counter-mask */ PFM_ATTR_B("h", "monitor in hypervisor"), /* monitor in hypervisor*/ PFM_ATTR_B("g", "measure in guest"), /* monitor in guest */ PFM_ATTR_NULL /* end-marker to avoid exporting number of entries */ }; pfmlib_pmu_t amd64_support; pfm_amd64_config_t pfm_amd64_cfg; static int amd64_num_mods(void *this, int idx) { const amd64_entry_t *pe = this_pe(this); unsigned int mask; mask = pe[idx].modmsk; return pfmlib_popcnt(mask); } static inline int amd64_eflag(void *this, int idx, int flag) { const amd64_entry_t *pe = this_pe(this); return !!(pe[idx].flags & flag); } static inline int amd64_uflag(void *this, int idx, int attr, int flag) { const amd64_entry_t *pe = this_pe(this); return !!(pe[idx].umasks[attr].uflags & flag); } static inline int amd64_event_ibsfetch(void *this, int idx) { return amd64_eflag(this, idx, AMD64_FL_IBSFE); } static inline int amd64_event_ibsop(void *this, int idx) { return amd64_eflag(this, idx, AMD64_FL_IBSOP); } static inline int amd64_from_rev(unsigned int flags) { return ((flags) >> 8) & 0xff; } static inline int amd64_till_rev(unsigned int flags) { int till = (((flags)>>16) & 0xff); if (!till) return 0xff; return till; } static void amd64_get_revision(pfm_amd64_config_t *cfg) { pfm_pmu_t rev = PFM_PMU_NONE; if (cfg->family == 6) { cfg->revision = PFM_PMU_AMD64_K7; return; } if (cfg->family == 15) { switch (cfg->model >> 4) { case 0: if (cfg->model == 5 && cfg->stepping < 2) { rev = PFM_PMU_AMD64_K8_REVB; break; } if (cfg->model == 4 && cfg->stepping == 0) { rev = PFM_PMU_AMD64_K8_REVB; break; } rev = PFM_PMU_AMD64_K8_REVC; break; case 1: rev = PFM_PMU_AMD64_K8_REVD; break; case 2: case 3: rev = PFM_PMU_AMD64_K8_REVE; break; case 4: case 5: case 0xc: rev = PFM_PMU_AMD64_K8_REVF; break; case 6: case 7: case 8: rev = PFM_PMU_AMD64_K8_REVG; break; default: rev = PFM_PMU_AMD64_K8_REVB; } } else if (cfg->family == 16) { /* family 10h */ switch (cfg->model) { case 4: case 5: case 6: rev = PFM_PMU_AMD64_FAM10H_SHANGHAI; break; case 8: case 9: rev = PFM_PMU_AMD64_FAM10H_ISTANBUL; break; default: rev = PFM_PMU_AMD64_FAM10H_BARCELONA; } } else if (cfg->family == 17) { /* family 11h */ switch (cfg->model) { default: rev = PFM_PMU_AMD64_FAM11H_TURION; } } else if (cfg->family == 18) { /* family 12h */ switch (cfg->model) { default: rev = PFM_PMU_AMD64_FAM12H_LLANO; } } else if (cfg->family == 20) { /* family 14h */ switch (cfg->model) { default: rev = PFM_PMU_AMD64_FAM14H_BOBCAT; } } else if (cfg->family == 21) { /* family 15h */ rev = PFM_PMU_AMD64_FAM15H_INTERLAGOS; } else if (cfg->family == 23) { /* family 17h */ rev = PFM_PMU_AMD64_FAM17H; } else if (cfg->family == 22) { /* family 16h */ rev = PFM_PMU_AMD64_FAM16H; } cfg->revision = rev; } /* * .byte 0x53 == push ebx. it's universal for 32 and 64 bit * .byte 0x5b == pop ebx. * Some gcc's (4.1.2 on Core2) object to pairing push/pop and ebx in 64 bit mode. * Using the opcode directly avoids this problem. */ static inline void cpuid(unsigned int op, unsigned int *a, unsigned int *b, unsigned int *c, unsigned int *d) { __asm__ __volatile__ (".byte 0x53\n\tcpuid\n\tmovl %%ebx, %%esi\n\t.byte 0x5b" : "=a" (*a), "=S" (*b), "=c" (*c), "=d" (*d) : "a" (op)); } static int amd64_event_valid(void *this, int i) { const amd64_entry_t *pe = this_pe(this); pfmlib_pmu_t *pmu = this; int flags; flags = pe[i].flags; if (pmu->pmu_rev < amd64_from_rev(flags)) return 0; if (pmu->pmu_rev > amd64_till_rev(flags)) return 0; /* no restrictions or matches restrictions */ return 1; } static int amd64_umask_valid(void *this, int i, int attr) { pfmlib_pmu_t *pmu = this; const amd64_entry_t *pe = this_pe(this); int flags; flags = pe[i].umasks[attr].uflags; if (pmu->pmu_rev < amd64_from_rev(flags)) return 0; if (pmu->pmu_rev > amd64_till_rev(flags)) return 0; /* no restrictions or matches restrictions */ return 1; } static unsigned int amd64_num_umasks(void *this, int pidx) { const amd64_entry_t *pe = this_pe(this); unsigned int i, n = 0; /* unit masks + modifiers */ for (i = 0; i < pe[pidx].numasks; i++) if (amd64_umask_valid(this, pidx, i)) n++; return n; } static int amd64_get_umask(void *this, int pidx, int attr_idx) { const amd64_entry_t *pe = this_pe(this); unsigned int i; int n; for (i=0, n = 0; i < pe[pidx].numasks; i++) { if (!amd64_umask_valid(this, pidx, i)) continue; if (n++ == attr_idx) return i; } return -1; } static inline int amd64_attr2mod(void *this, int pidx, int attr_idx) { const amd64_entry_t *pe = this_pe(this); size_t x; int n; n = attr_idx - amd64_num_umasks(this, pidx); pfmlib_for_each_bit(x, pe[pidx].modmsk) { if (n == 0) break; n--; } return x; } void amd64_display_reg(void *this, pfmlib_event_desc_t *e, pfm_amd64_reg_t reg) { pfmlib_pmu_t *pmu = this; if (IS_FAMILY_10H(pmu) || IS_FAMILY_15H(pmu)) __pfm_vbprintf("[0x%"PRIx64" event_sel=0x%x umask=0x%x os=%d usr=%d en=%d int=%d inv=%d edge=%d cnt_mask=%d guest=%d host=%d] %s\n", reg.val, reg.sel_event_mask | (reg.sel_event_mask2 << 8), reg.sel_unit_mask, reg.sel_os, reg.sel_usr, reg.sel_en, reg.sel_int, reg.sel_inv, reg.sel_edge, reg.sel_cnt_mask, reg.sel_guest, reg.sel_host, e->fstr); else __pfm_vbprintf("[0x%"PRIx64" event_sel=0x%x umask=0x%x os=%d usr=%d en=%d int=%d inv=%d edge=%d cnt_mask=%d] %s\n", reg.val, reg.sel_event_mask, reg.sel_unit_mask, reg.sel_os, reg.sel_usr, reg.sel_en, reg.sel_int, reg.sel_inv, reg.sel_edge, reg.sel_cnt_mask, e->fstr); } int pfm_amd64_detect(void *this) { unsigned int a, b, c, d; char buffer[128]; if (pfm_amd64_cfg.family) return PFM_SUCCESS; cpuid(0, &a, &b, &c, &d); strncpy(&buffer[0], (char *)(&b), 4); strncpy(&buffer[4], (char *)(&d), 4); strncpy(&buffer[8], (char *)(&c), 4); buffer[12] = '\0'; if (strcmp(buffer, "AuthenticAMD")) return PFM_ERR_NOTSUPP; cpuid(1, &a, &b, &c, &d); pfm_amd64_cfg.family = (a >> 8) & 0x0000000f; // bits 11 - 8 pfm_amd64_cfg.model = (a >> 4) & 0x0000000f; // Bits 7 - 4 if (pfm_amd64_cfg.family == 0xf) { pfm_amd64_cfg.family += (a >> 20) & 0x000000ff; // Extended family pfm_amd64_cfg.model |= (a >> 12) & 0x000000f0; // Extended model } pfm_amd64_cfg.stepping= a & 0x0000000f; // bits 3 - 0 amd64_get_revision(&pfm_amd64_cfg); if (pfm_amd64_cfg.revision == PFM_PMU_NONE) return PFM_ERR_NOTSUPP; return PFM_SUCCESS; } int pfm_amd64_family_detect(void *this) { struct pfmlib_pmu *pmu = this; int ret; ret = pfm_amd64_detect(this); if (ret != PFM_SUCCESS) return ret; ret = pfm_amd64_cfg.revision; return ret == pmu->cpu_family ? PFM_SUCCESS : PFM_ERR_NOTSUPP; } static int amd64_add_defaults(void *this, pfmlib_event_desc_t *e, unsigned int msk, uint64_t *umask) { const amd64_entry_t *ent, *pe = this_pe(this); unsigned int i; int j, k, added, omit, numasks_grp; int idx; k = e->nattrs; ent = pe+e->event; for(i=0; msk; msk >>=1, i++) { if (!(msk & 0x1)) continue; added = omit = numasks_grp = 0; for (j = 0; j < e->npattrs; j++) { if (e->pattrs[j].ctrl != PFM_ATTR_CTRL_PMU) continue; if (e->pattrs[j].type != PFM_ATTR_UMASK) continue; idx = e->pattrs[j].idx; if (ent->umasks[idx].grpid != i) continue; /* number of umasks in this group */ numasks_grp++; if (amd64_uflag(this, e->event, idx, AMD64_FL_DFL)) { DPRINT("added default for %s j=%d idx=%d\n", ent->umasks[idx].uname, j, idx); *umask |= ent->umasks[idx].ucode; e->attrs[k].id = j; /* pattrs index */ e->attrs[k].ival = 0; k++; added++; } if (amd64_uflag(this, e->event, idx, AMD64_FL_OMIT)) omit++; } /* * fail if no default was found AND at least one umasks cannot be omitted * in the group */ if (!added && omit != numasks_grp) { DPRINT("no default found for event %s unit mask group %d\n", ent->name, i); return PFM_ERR_UMASK; } } e->nattrs = k; return PFM_SUCCESS; } int pfm_amd64_get_encoding(void *this, pfmlib_event_desc_t *e) { const amd64_entry_t *pe = this_pe(this); pfm_amd64_reg_t reg; pfmlib_event_attr_info_t *a; uint64_t umask = 0; unsigned int plmmsk = 0; int k, ret, grpid; unsigned int grpmsk, ugrpmsk = 0; int grpcounts[AMD64_MAX_GRP]; int ncombo[AMD64_MAX_GRP]; memset(grpcounts, 0, sizeof(grpcounts)); memset(ncombo, 0, sizeof(ncombo)); e->fstr[0] = '\0'; reg.val = 0; /* assume reserved bits are zerooed */ grpmsk = (1 << pe[e->event].ngrp)-1; if (amd64_event_ibsfetch(this, e->event)) reg.ibsfetch.en = 1; else if (amd64_event_ibsop(this, e->event)) reg.ibsop.en = 1; else { reg.sel_event_mask = pe[e->event].code; reg.sel_event_mask2 = pe[e->event].code >> 8; reg.sel_en = 1; /* force enable */ reg.sel_int = 1; /* force APIC */ } for(k=0; k < e->nattrs; k++) { a = attr(e, k); if (a->ctrl != PFM_ATTR_CTRL_PMU) continue; if (a->type == PFM_ATTR_UMASK) { grpid = pe[e->event].umasks[a->idx].grpid; ++grpcounts[grpid]; /* * upper layer has removed duplicates * so if we come here more than once, it is for two * diinct umasks */ if (amd64_uflag(this, e->event, a->idx, AMD64_FL_NCOMBO)) ncombo[grpid] = 1; /* * if more than one umask in this group but one is marked * with ncombo, then fail. It is okay to combine umask within * a group as long as none is tagged with NCOMBO */ if (grpcounts[grpid] > 1 && ncombo[grpid]) { DPRINT("event does not support unit mask combination within a group\n"); return PFM_ERR_FEATCOMB; } umask |= pe[e->event].umasks[a->idx].ucode; ugrpmsk |= 1 << pe[e->event].umasks[a->idx].grpid; } else if (a->type == PFM_ATTR_RAW_UMASK) { /* there can only be one RAW_UMASK per event */ /* sanity checks */ if (a->idx & ~0xff) { DPRINT("raw umask is invalid\n"); return PFM_ERR_ATTR; } /* override umask */ umask = a->idx & 0xff; ugrpmsk = grpmsk; } else { /* modifiers */ uint64_t ival = e->attrs[k].ival; switch(a->idx) { //amd64_attr2mod(this, e->osid, e->event, a->idx)) { case AMD64_ATTR_I: /* invert */ reg.sel_inv = !!ival; break; case AMD64_ATTR_E: /* edge */ reg.sel_edge = !!ival; break; case AMD64_ATTR_C: /* counter-mask */ if (ival > 255) return PFM_ERR_ATTR_VAL; reg.sel_cnt_mask = ival; break; case AMD64_ATTR_U: /* USR */ reg.sel_usr = !!ival; plmmsk |= _AMD64_ATTR_U; break; case AMD64_ATTR_K: /* OS */ reg.sel_os = !!ival; plmmsk |= _AMD64_ATTR_K; break; case AMD64_ATTR_G: /* GUEST */ reg.sel_guest = !!ival; plmmsk |= _AMD64_ATTR_G; break; case AMD64_ATTR_H: /* HOST */ reg.sel_host = !!ival; plmmsk |= _AMD64_ATTR_H; break; } } } /* * handle case where no priv level mask was passed. * then we use the dfl_plm */ if (!(plmmsk & (_AMD64_ATTR_K|_AMD64_ATTR_U|_AMD64_ATTR_H))) { if (e->dfl_plm & PFM_PLM0) reg.sel_os = 1; if (e->dfl_plm & PFM_PLM3) reg.sel_usr = 1; if ((IS_FAMILY_10H(this) || IS_FAMILY_15H(this)) && e->dfl_plm & PFM_PLMH) reg.sel_host = 1; } /* * check that there is at least of unit mask in each unit * mask group */ if (ugrpmsk != grpmsk) { ugrpmsk ^= grpmsk; ret = amd64_add_defaults(this, e, ugrpmsk, &umask); if (ret != PFM_SUCCESS) return ret; } reg.sel_unit_mask = umask; e->codes[0] = reg.val; e->count = 1; /* * reorder all the attributes such that the fstr appears always * the same regardless of how the attributes were submitted. */ evt_strcat(e->fstr, "%s", pe[e->event].name); pfmlib_sort_attr(e); for (k = 0; k < e->nattrs; k++) { a = attr(e, k); if (a->ctrl != PFM_ATTR_CTRL_PMU) continue; if (a->type == PFM_ATTR_UMASK) evt_strcat(e->fstr, ":%s", pe[e->event].umasks[a->idx].uname); else if (a->type == PFM_ATTR_RAW_UMASK) evt_strcat(e->fstr, ":0x%x", a->idx); } for (k = 0; k < e->npattrs; k++) { int idx; if (e->pattrs[k].ctrl != PFM_ATTR_CTRL_PMU) continue; if (e->pattrs[k].type == PFM_ATTR_UMASK) continue; idx = e->pattrs[k].idx; switch(idx) { case AMD64_ATTR_K: evt_strcat(e->fstr, ":%s=%lu", amd64_mods[idx].name, reg.sel_os); break; case AMD64_ATTR_U: evt_strcat(e->fstr, ":%s=%lu", amd64_mods[idx].name, reg.sel_usr); break; case AMD64_ATTR_E: evt_strcat(e->fstr, ":%s=%lu", amd64_mods[idx].name, reg.sel_edge); break; case AMD64_ATTR_I: evt_strcat(e->fstr, ":%s=%lu", amd64_mods[idx].name, reg.sel_inv); break; case AMD64_ATTR_C: evt_strcat(e->fstr, ":%s=%lu", amd64_mods[idx].name, reg.sel_cnt_mask); break; case AMD64_ATTR_H: evt_strcat(e->fstr, ":%s=%lu", amd64_mods[idx].name, reg.sel_host); break; case AMD64_ATTR_G: evt_strcat(e->fstr, ":%s=%lu", amd64_mods[idx].name, reg.sel_guest); break; } } amd64_display_reg(this, e, reg); return PFM_SUCCESS; } int pfm_amd64_get_event_first(void *this) { pfmlib_pmu_t *pmu = this; int idx; for(idx=0; idx < pmu->pme_count; idx++) if (amd64_event_valid(this, idx)) return idx; return -1; } int pfm_amd64_get_event_next(void *this, int idx) { pfmlib_pmu_t *pmu = this; /* basic validity checks on idx down by caller */ if (idx >= (pmu->pme_count-1)) return -1; /* validate event fo this host PMU */ if (!amd64_event_valid(this, idx)) return -1; for(++idx; idx < pmu->pme_count; idx++) { if (amd64_event_valid(this, idx)) return idx; } return -1; } int pfm_amd64_event_is_valid(void *this, int pidx) { pfmlib_pmu_t *pmu = this; if (pidx < 0 || pidx >= pmu->pme_count) return 0; /* valid revision */ return amd64_event_valid(this, pidx); } int pfm_amd64_get_event_attr_info(void *this, int pidx, int attr_idx, pfmlib_event_attr_info_t *info) { const amd64_entry_t *pe = this_pe(this); int numasks, idx; numasks = amd64_num_umasks(this, pidx); if (attr_idx < numasks) { idx = amd64_get_umask(this, pidx, attr_idx); if (idx == -1) return PFM_ERR_ATTR; info->name = pe[pidx].umasks[idx].uname; info->desc = pe[pidx].umasks[idx].udesc; info->code = pe[pidx].umasks[idx].ucode; info->type = PFM_ATTR_UMASK; info->is_dfl = amd64_uflag(this, pidx, idx, AMD64_FL_DFL); } else { idx = amd64_attr2mod(this, pidx, attr_idx); info->name = amd64_mods[idx].name; info->desc = amd64_mods[idx].desc; info->type = amd64_mods[idx].type; info->code = idx; info->is_dfl = 0; } info->is_precise = 0; info->equiv = NULL; info->ctrl = PFM_ATTR_CTRL_PMU; info->idx = idx; /* namespace specific index */ info->dfl_val64 = 0; return PFM_SUCCESS; } int pfm_amd64_get_event_info(void *this, int idx, pfm_event_info_t *info) { pfmlib_pmu_t *pmu = this; const amd64_entry_t *pe = this_pe(this); info->name = pe[idx].name; info->desc = pe[idx].desc; info->equiv = NULL; info->code = pe[idx].code; info->idx = idx; info->pmu = pmu->pmu; info->is_precise = 0; info->nattrs = amd64_num_umasks(this, idx); info->nattrs += amd64_num_mods(this, idx); return PFM_SUCCESS; } int pfm_amd64_validate_table(void *this, FILE *fp) { pfmlib_pmu_t *pmu = this; const amd64_entry_t *pe = this_pe(this); const char *name = pmu->name; unsigned int j, k; int i, ndfl; int error = 0; if (!pmu->atdesc) { fprintf(fp, "pmu: %s missing attr_desc\n", pmu->name); error++; } if (!pmu->supported_plm && pmu->type == PFM_PMU_TYPE_CORE) { fprintf(fp, "pmu: %s supported_plm not set\n", pmu->name); error++; } for(i=0; i < pmu->pme_count; i++) { if (!pe[i].name) { fprintf(fp, "pmu: %s event%d: :: no name (prev event was %s)\n", pmu->name, i, i > 1 ? pe[i-1].name : "??"); error++; } if (!pe[i].desc) { fprintf(fp, "pmu: %s event%d: %s :: no description\n", name, i, pe[i].name); error++; } if (pe[i].numasks && pe[i].umasks == NULL) { fprintf(fp, "pmu: %s event%d: %s :: numasks but no umasks\n", pmu->name, i, pe[i].name); error++; } if (pe[i].numasks == 0 && pe[i].umasks) { fprintf(fp, "pmu: %s event%d: %s :: numasks=0 but umasks defined\n", pmu->name, i, pe[i].name); error++; } if (pe[i].numasks && pe[i].ngrp == 0) { fprintf(fp, "pmu: %s event%d: %s :: ngrp cannot be zero\n", name, i, pe[i].name); error++; } if (pe[i].numasks == 0 && pe[i].ngrp) { fprintf(fp, "pmu: %s event%d: %s :: ngrp must be zero\n", name, i, pe[i].name); error++; } if (pe[i].ngrp >= AMD64_MAX_GRP) { fprintf(fp, "pmu: %s event%d: %s :: ngrp too big (max=%d)\n", name, i, pe[i].name, AMD64_MAX_GRP); error++; } for(ndfl = 0, j= 0; j < pe[i].numasks; j++) { if (!pe[i].umasks[j].uname) { fprintf(fp, "pmu: %s event%d: %s umask%d :: no name\n", pmu->name, i, pe[i].name, j); error++; } if (!pe[i].umasks[j].udesc) { fprintf(fp, "pmu: %s event%d:%s umask%d: %s :: no description\n", name, i, pe[i].name, j, pe[i].umasks[j].uname); error++; } if (pe[i].ngrp && pe[i].umasks[j].grpid >= pe[i].ngrp) { fprintf(fp, "pmu: %s event%d: %s umask%d: %s :: invalid grpid %d (must be < %d)\n", name, i, pe[i].name, j, pe[i].umasks[j].uname, pe[i].umasks[j].grpid, pe[i].ngrp); error++; } if (pe[i].umasks[j].uflags & AMD64_FL_DFL) { for(k=0; k < j; k++) if ((pe[i].umasks[k].uflags == pe[i].umasks[j].uflags) && (pe[i].umasks[k].grpid == pe[i].umasks[j].grpid)) ndfl++; if (pe[i].numasks == 1) ndfl = 1; } } if (pe[i].numasks > 1 && ndfl) { fprintf(fp, "pmu: %s event%d: %s :: more than one default unit mask with same code\n", name, i, pe[i].name); error++; } /* if only one umask, then ought to be default */ if (pe[i].numasks == 1 && ndfl != 1) { fprintf(fp, "pmu: %s event%d: %s, only one umask but no default\n", pmu->name, i, pe[i].name); error++; } if (pe[i].flags & AMD64_FL_NCOMBO) { fprintf(fp, "pmu: %s event%d: %s :: NCOMBO is unit mask only flag\n", name, i, pe[i].name); error++; } for(j=0; j < pe[i].numasks; j++) { if (pe[i].umasks[j].uflags & AMD64_FL_NCOMBO) continue; for(k=j+1; k < pe[i].numasks; k++) { if (pe[i].umasks[k].uflags & AMD64_FL_NCOMBO) continue; if ((pe[i].umasks[j].ucode & pe[i].umasks[k].ucode)) { fprintf(fp, "pmu: %s event%d: %s :: umask %s and %s have overlapping code bits\n", name, i, pe[i].name, pe[i].umasks[j].uname, pe[i].umasks[k].uname); error++; } } } } return error ? PFM_ERR_INVAL : PFM_SUCCESS; } unsigned int pfm_amd64_get_event_nattrs(void *this, int pidx) { unsigned int nattrs; nattrs = amd64_num_umasks(this, pidx); nattrs += amd64_num_mods(this, pidx); return nattrs; } int pfm_amd64_get_num_events(void *this) { pfmlib_pmu_t *pmu = this; int i, num = 0; /* * count actual number of events for specific PMU. * Table may contain more events for the family than * what a specific model actually supports. */ for (i = 0; i < pmu->pme_count; i++) if (amd64_event_valid(this, i)) num++; return num; }