| /* |
| * 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 <eranian@gmail.com> |
| * |
| * Based on: |
| * Copyright (c) 2005-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 <string.h> |
| #include <stdlib.h> |
| |
| /* 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; |
| } |
| 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; |
| pfm_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, pfm_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; |
| } |