/* * Copyright (c) 2005-2006 Hewlett-Packard Development Company, L.P. * Copyright (c) 2006 IBM Corp. * Contributed by Kevin Corry * * 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. * * pfmlib_intel_netburst.c * * Support for the Pentium4/Xeon/EM64T processor family (family=15). */ /* private headers */ #include "pfmlib_priv.h" #include "pfmlib_intel_netburst_priv.h" #include "pfmlib_intel_x86_priv.h" #include "events/intel_netburst_events.h" const pfmlib_attr_desc_t netburst_mods[]={ PFM_ATTR_B("u", "monitor at priv level 1, 2, 3"), /* monitor priv level 1, 2, 3 */ PFM_ATTR_B("k", "monitor at priv level 0"), /* monitor priv level 0 */ PFM_ATTR_B("cmpl", "complement"), /* set: <=, clear: > */ PFM_ATTR_B("e", "edge"), /* edge */ PFM_ATTR_I("thr", "event threshold in range [0-15]"), /* threshold */ }; #define NETBURST_MODS_COUNT (sizeof(netburst_mods)/sizeof(pfmlib_attr_desc_t)) extern pfmlib_pmu_t netburst_support; static inline int netburst_get_numasks(int pidx) { int i = 0; /* * name = NULL is end-marker */ while (netburst_events[pidx].event_masks[i].name) i++; return i; } static void netburst_display_reg(pfmlib_event_desc_t *e) { netburst_escr_value_t escr; netburst_cccr_value_t cccr; escr.val = e->codes[0]; cccr.val = e->codes[1]; __pfm_vbprintf("[0x%"PRIx64" 0x%"PRIx64" 0x%"PRIx64" usr=%d os=%d tag_ena=%d tag_val=%d " "evmask=0x%x evsel=0x%x escr_sel=0x%x comp=%d cmpl=%d thr=%d e=%d", escr, cccr, e->codes[2], /* perf_event code */ escr.bits.t0_usr, /* t1 is identical */ escr.bits.t0_os, /* t1 is identical */ escr.bits.tag_enable, escr.bits.tag_value, escr.bits.event_mask, escr.bits.event_select, cccr.bits.escr_select, cccr.bits.compare, cccr.bits.complement, cccr.bits.threshold, cccr.bits.edge); __pfm_vbprintf("] %s\n", e->fstr); } static int netburst_add_defaults(pfmlib_event_desc_t *e, unsigned int *evmask) { int i, n; n = netburst_get_numasks(e->event); for (i = 0; i < n; i++) { if (netburst_events[e->event].event_masks[i].flags & NETBURST_FL_DFL) goto found; } return PFM_ERR_ATTR; found: *evmask = 1 << netburst_events[e->event].event_masks[i].bit; n = e->nattrs; e->attrs[n].id = i; e->attrs[n].ival = i; e->nattrs = n+1; return PFM_SUCCESS; } int pfm_netburst_get_encoding(void *this, pfmlib_event_desc_t *e) { pfmlib_event_attr_info_t *a; netburst_escr_value_t escr; netburst_cccr_value_t cccr; unsigned int evmask = 0; unsigned int plmmsk = 0; int umask_done = 0; const char *n; int k, id, bit, ret; int tag_enable = 0, tag_value = 0; e->fstr[0] = '\0'; escr.val = 0; cccr.val = 0; 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) { bit = netburst_events[e->event].event_masks[a->idx].bit; n = netburst_events[e->event].event_masks[a->idx].name; /* * umask combination seems possible, although it does * not always make sense, e.g., BOGUS vs. NBOGUS */ if (bit < EVENT_MASK_BITS && n) { evmask |= (1 << bit); } else if (bit >= EVENT_MASK_BITS && n) { tag_value |= (1 << (bit - EVENT_MASK_BITS)); tag_enable = 1; } umask_done = 1; } else if (a->type == PFM_ATTR_RAW_UMASK) { /* should not happen */ return PFM_ERR_ATTR; } else { uint64_t ival = e->attrs[k].ival; switch (a->idx) { case NETBURST_ATTR_U: escr.bits.t1_usr = !!ival; escr.bits.t0_usr = !!ival; plmmsk |= _NETBURST_ATTR_U; break; case NETBURST_ATTR_K: escr.bits.t1_os = !!ival; escr.bits.t0_os = !!ival; plmmsk |= _NETBURST_ATTR_K; break; case NETBURST_ATTR_E: if (ival) { cccr.bits.compare = 1; cccr.bits.edge = 1; } break; case NETBURST_ATTR_C: if (ival) { cccr.bits.compare = 1; cccr.bits.complement = 1; } break; case NETBURST_ATTR_T: if (ival > 15) return PFM_ERR_ATTR_VAL; if (ival) { cccr.bits.compare = 1; cccr.bits.threshold = ival; } break; default: return PFM_ERR_ATTR; } } } /* * handle case where no priv level mask was passed. * then we use the dfl_plm */ if (!(plmmsk & (_NETBURST_ATTR_K|_NETBURST_ATTR_U))) { if (e->dfl_plm & PFM_PLM0) { escr.bits.t1_os = 1; escr.bits.t0_os = 1; } if (e->dfl_plm & PFM_PLM3) { escr.bits.t1_usr = 1; escr.bits.t0_usr = 1; } } if (!umask_done) { ret = netburst_add_defaults(e, &evmask); if (ret != PFM_SUCCESS) return ret; } escr.bits.tag_enable = tag_enable; escr.bits.tag_value = tag_value; escr.bits.event_mask = evmask; escr.bits.event_select = netburst_events[e->event].event_select; cccr.bits.enable = 1; cccr.bits.escr_select = netburst_events[e->event].escr_select; cccr.bits.active_thread = 3; if (e->event == PME_REPLAY_EVENT) escr.bits.event_mask &= P4_REPLAY_REAL_MASK; /* remove virtual mask bits */ /* * reorder all the attributes such that the fstr appears always * the same regardless of how the attributes were submitted. */ evt_strcat(e->fstr, "%s", netburst_events[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) { id = e->attrs[k].id; evt_strcat(e->fstr, ":%s", netburst_events[e->event].event_masks[id].name); } } evt_strcat(e->fstr, ":%s=%lu", netburst_mods[NETBURST_ATTR_K].name, escr.bits.t0_os); evt_strcat(e->fstr, ":%s=%lu", netburst_mods[NETBURST_ATTR_U].name, escr.bits.t0_usr); evt_strcat(e->fstr, ":%s=%lu", netburst_mods[NETBURST_ATTR_E].name, cccr.bits.edge); evt_strcat(e->fstr, ":%s=%lu", netburst_mods[NETBURST_ATTR_C].name, cccr.bits.complement); evt_strcat(e->fstr, ":%s=%lu", netburst_mods[NETBURST_ATTR_T].name, cccr.bits.threshold); e->count = 2; e->codes[0] = escr.val; e->codes[1] = cccr.val; netburst_display_reg(e); return PFM_SUCCESS; } static int pfm_netburst_detect(void *this) { int ret; int model; ret = pfm_intel_x86_detect(); if (ret != PFM_SUCCESS) return ret; if (pfm_intel_x86_cfg.family != 15) return PFM_ERR_NOTSUPP; model = pfm_intel_x86_cfg.model; if (model == 3 || model == 4 || model == 6) return PFM_ERR_NOTSUPP; return PFM_SUCCESS; } static int pfm_netburst_detect_prescott(void *this) { int ret; int model; ret = pfm_intel_x86_detect(); if (ret != PFM_SUCCESS) return ret; if (pfm_intel_x86_cfg.family != 15) return PFM_ERR_NOTSUPP; /* * prescott has one more event (instr_completed) */ model = pfm_intel_x86_cfg.model; if (model != 3 && model != 4 && model != 6) return PFM_ERR_NOTSUPP; return PFM_SUCCESS; } static int pfm_netburst_get_event_first(void *this) { pfmlib_pmu_t *p = this; return p->pme_count ? 0 : -1; } static int pfm_netburst_get_event_next(void *this, int idx) { pfmlib_pmu_t *p = this; if (idx >= (p->pme_count-1)) return -1; return idx+1; } static int pfm_netburst_event_is_valid(void *this, int pidx) { pfmlib_pmu_t *p = this; return pidx >= 0 && pidx < p->pme_count; } static int pfm_netburst_get_event_attr_info(void *this, int pidx, int attr_idx, pfmlib_event_attr_info_t *info) { const netburst_entry_t *pe = this_pe(this); int numasks, idx; numasks = netburst_get_numasks(pidx); if (attr_idx < numasks) { //idx = pfm_intel_x86_attr2umask(this, pidx, attr_idx); idx = attr_idx; info->name = pe[pidx].event_masks[idx].name; info->desc = pe[pidx].event_masks[idx].desc; info->equiv= NULL; info->code = pe[pidx].event_masks[idx].bit; info->type = PFM_ATTR_UMASK; info->is_dfl = !!(pe[pidx].event_masks[idx].flags & NETBURST_FL_DFL); } else { idx = attr_idx - numasks; info->name = netburst_mods[idx].name; info->desc = netburst_mods[idx].desc; info->equiv= NULL; info->code = idx; info->type = netburst_mods[idx].type; info->is_dfl = 0; } info->ctrl = PFM_ATTR_CTRL_PMU; info->idx = idx; /* namespace specific index */ info->dfl_val64 = 0; return PFM_SUCCESS; } static int pfm_netburst_get_event_info(void *this, int idx, pfm_event_info_t *info) { const netburst_entry_t *pe = this_pe(this); pfmlib_pmu_t *pmu = this; /* * pmu and idx filled out by caller */ info->name = pe[idx].name; info->desc = pe[idx].desc; info->code = pe[idx].event_select | (pe[idx].escr_select << 8); info->equiv = NULL; info->idx = idx; /* private index */ info->pmu = pmu->pmu; info->nattrs = netburst_get_numasks(idx); info->nattrs += NETBURST_MODS_COUNT; return PFM_SUCCESS; } static int pfm_netburst_validate_table(void *this, FILE *fp) { pfmlib_pmu_t *pmu = this; const netburst_entry_t *pe = netburst_events; const char *name = pmu->name; int i, j, noname, ndfl; int error = 0; 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++; } noname = ndfl = 0; /* name = NULL is end-marker, veryfy there is at least one */ for(j= 0; j < EVENT_MASK_BITS; j++) { if (!pe[i].event_masks[j].name) noname++; if (pe[i].event_masks[j].name) { if (!pe[i].event_masks[j].desc) { fprintf(fp, "pmu: %s event%d:%s umask%d: %s :: no description\n", name, i, pe[i].name, j, pe[i].event_masks[j].name); error++; } if (pe[i].event_masks[j].bit >= (EVENT_MASK_BITS+4)) { fprintf(fp, "pmu: %s event%d:%s umask%d: %s :: invalid bit field\n", name, i, pe[i].name, j, pe[i].event_masks[j].name); error++; } if (pe[i].event_masks[j].flags & NETBURST_FL_DFL) ndfl++; } } if (ndfl > 1) { fprintf(fp, "pmu: %s event%d:%s :: more than one default umask\n", name, i, pe[i].name); error++; } if (!noname) { fprintf(fp, "pmu: %s event%d:%s :: no event mask end-marker\n", name, i, pe[i].name); error++; } } return error ? PFM_ERR_INVAL : PFM_SUCCESS; } static unsigned int pfm_netburst_get_event_nattrs(void *this, int pidx) { unsigned int nattrs; nattrs = netburst_get_numasks(pidx); nattrs += NETBURST_MODS_COUNT; return nattrs; } pfmlib_pmu_t netburst_support = { .desc = "Pentium4", .name = "netburst", .pmu = PFM_PMU_INTEL_NETBURST, .pme_count = LIBPFM_ARRAY_SIZE(netburst_events) - 1, .type = PFM_PMU_TYPE_CORE, .supported_plm = INTEL_X86_PLM, .atdesc = netburst_mods, .pe = netburst_events, .max_encoding = 3, .num_cntrs = 18, .pmu_detect = pfm_netburst_detect, .get_event_encoding[PFM_OS_NONE] = pfm_netburst_get_encoding, PFMLIB_ENCODE_PERF(pfm_netburst_get_perf_encoding), .get_event_first = pfm_netburst_get_event_first, .get_event_next = pfm_netburst_get_event_next, .event_is_valid = pfm_netburst_event_is_valid, .validate_table = pfm_netburst_validate_table, .get_event_info = pfm_netburst_get_event_info, .get_event_attr_info = pfm_netburst_get_event_attr_info, .get_event_nattrs = pfm_netburst_get_event_nattrs, PFMLIB_VALID_PERF_PATTRS(pfm_netburst_perf_validate_pattrs), }; pfmlib_pmu_t netburst_p_support = { .desc = "Pentium4 (Prescott)", .name = "netburst_p", .pmu = PFM_PMU_INTEL_NETBURST_P, .pme_count = LIBPFM_ARRAY_SIZE(netburst_events), .type = PFM_PMU_TYPE_CORE, .supported_plm = INTEL_X86_PLM, .atdesc = netburst_mods, .pe = netburst_events, .max_encoding = 3, .num_cntrs = 18, .pmu_detect = pfm_netburst_detect_prescott, .get_event_encoding[PFM_OS_NONE] = pfm_netburst_get_encoding, PFMLIB_ENCODE_PERF(pfm_netburst_get_perf_encoding), .get_event_first = pfm_netburst_get_event_first, .get_event_next = pfm_netburst_get_event_next, .event_is_valid = pfm_netburst_event_is_valid, .validate_table = pfm_netburst_validate_table, .get_event_info = pfm_netburst_get_event_info, .get_event_attr_info = pfm_netburst_get_event_attr_info, .get_event_nattrs = pfm_netburst_get_event_nattrs, PFMLIB_VALID_PERF_PATTRS(pfm_netburst_perf_validate_pattrs), };