/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
*/
/*
* Routines to capture processor-dependencies in event specification.
*/
#include <sys/types.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <stdio.h>
#include <libintl.h>
#include <assert.h>
#include <errno.h>
#include "libcpc.h"
#include "libcpc_impl.h"
/*
* Event specifications for Pentium performance counters are based
* on the content of a getsubopt-like string.
* The string should contain something that looks like this:
*
* pic0=<eventspec>,pic1=<eventspec>
* [,cmask0=<maskspec>][,cmask1=<maskspec>]
* [,umask0=<maskspec>][,umask1=<maskspec>]
* [,inv[0|1]][,noedge[0|1]]
* [,sys[0|1]][,nouser[0|1]]
*
* For example:
* pic0=data_mem_refs,pic1=l2_ld,sys
* or
* pic0=l2_ld,pic1=bus_drdy_clocks,umask1=0x20,nouser1
*
* By default, user event counting is enabled, system event counting
* is disabled.
*
* Note that Pentium and Pentium Pro have different event specifications.
*
* The two events must be named. The names can be ascii or
* a decimal, octal or hexadecimal number as parsed by strtol(3C).
*
* The routine counts the number of errors encountered while parsing
* the string, if no errors are encountered, the event handle is
* returned.
*/
const char *
cpc_getusage(int cpuver)
{
switch (cpuver) {
case CPC_PENTIUM_PRO_MMX:
case CPC_PENTIUM_PRO:
return ("pic0=<event0>,pic1=<event1> "
"[,sys[0|1]] "
"[,nouser[0|1]] "
"[,noedge[0|1]] "
"[,pc[0|1]] "
"[,int[0|1]] "
"[,inv[0|1]] "
"[,cmask[0|1]=<maskspec>] "
"[,umask[0|1]=<maskspec>] ");
case CPC_PENTIUM_MMX:
case CPC_PENTIUM:
return ("pic0=<event0>,pic1=<event1> "
"[,sys[0|1]] "
"[,nouser[0|1]] "
"[,noedge[0|1]] "
"[,pc[0|1]]");
default:
return (NULL);
}
}
struct keyval {
char *kv_token;
int (*kv_action)(const char *,
const struct keyval *, int, char *, uint32_t *);
uint_t kv_regno;
uint32_t kv_mask;
int kv_shift;
};
/*ARGSUSED*/
static int
eightbits(const char *fn,
const struct keyval *kv, int cpuver, char *value, uint32_t *bits)
{
char *eptr = NULL;
long l;
if (value == NULL) {
__cpc_error(fn, gettext("missing '%s' value\n"),
kv->kv_token);
return (-1);
}
l = strtol(value, &eptr, 0);
if (value == eptr || l < 0 || l > UINT8_MAX) {
__cpc_error(fn, gettext("bad '%s' value\n"), kv->kv_token);
return (-1);
}
bits[kv->kv_regno] |= ((uint8_t)l & kv->kv_mask) << kv->kv_shift;
return (0);
}
static int
picbits(const char *fn,
const struct keyval *kv, int cpuver, char *value, uint32_t *bits)
{
uint8_t val8;
uint_t regno;
regno = strcmp(kv->kv_token, "pic0") == 0 ? 0 : 1;
if (value == NULL) {
__cpc_error(fn, gettext("missing '%s' value\n"),
kv->kv_token);
return (-1);
}
if (__cpc_name_to_reg(cpuver, regno, value, &val8) != 0) {
switch (cpuver) {
case CPC_PENTIUM_PRO_MMX:
case CPC_PENTIUM_PRO:
assert(kv->kv_regno == regno);
__cpc_error(fn, gettext(
"PerfCtr%d cannot measure '%s' on this cpu\n"),
regno, value);
break;
case CPC_PENTIUM_MMX:
case CPC_PENTIUM:
assert(kv->kv_regno == 0);
__cpc_error(fn, gettext(
"CTR%d cannot measure '%s' on this cpu\n"),
regno, value);
break;
}
return (-1);
}
bits[kv->kv_regno] |= (val8 & kv->kv_mask) << kv->kv_shift;
return (0);
}
/*ARGSUSED2*/
static int
bitclr(const char *fn,
const struct keyval *kv, int cpuver, char *value, uint32_t *bits)
{
if (value != NULL) {
__cpc_error(fn, gettext("bad arg to '%s'\n"), kv->kv_token);
return (-1);
}
bits[kv->kv_regno] &= ~(kv->kv_mask << kv->kv_shift);
return (0);
}
/*ARGSUSED2*/
static int
bitset(const char *fn,
const struct keyval *kv, int cpuver, char *value, uint32_t *bits)
{
if (value != NULL) {
__cpc_error(fn, gettext("bad arg to '%s'\n"), kv->kv_token);
return (-1);
}
bits[kv->kv_regno] |= (kv->kv_mask << kv->kv_shift);
return (0);
}
static int
nextpair(const char *fn,
const struct keyval *kv, int cpuver, char *value, uint32_t *bits)
{
int rv;
if (value != NULL) {
__cpc_error(fn, gettext("bad arg to '%s'\n"), kv->kv_token);
return (-1);
}
kv++;
if ((rv = kv->kv_action(fn, kv, cpuver, value, bits)) != 0)
return (rv);
kv++;
return (kv->kv_action(fn, kv, cpuver, value, bits));
}
/*
* This token table must match the keyval tables below.
*/
static char * const tokens[] = {
#define D_pic0 0
"pic0", /* takes a valid event name */
#define D_pic1 1
"pic1", /* takes a valid event name */
#define D_nouser 2
"nouser", /* disables user counts */
#define D_nouser0 3
"nouser0",
#define D_nouser1 4
"nouser1",
#define D_sys 5
"sys", /* enables system counts */
#define D_sys0 6
"sys0",
#define D_sys1 7
"sys1",
#define D_noedge 8
"noedge", /* disable edge detect */
#define D_noedge0 9
"noedge0",
#define D_noedge1 10
"noedge1",
#define D_pc 11
"pc", /* sets pin control high */
#define D_pc0 12
"pc0",
#define D_pc1 13
"pc1",
/*
* These additional keywords are for Pentium Pro / Pentium II machines.
*/
#define D_int 14
"int", /* enable interrupt on counter overflow */
#define D_int0 15
"int0",
#define D_int1 16
"int1",
#define D_inv 17
"inv", /* invert cmask comparison */
#define D_inv0 18
"inv0",
#define D_inv1 19
"inv1",
#define D_umask0 20
"umask0", /* PerfCtr0 unit mask */
#define D_umask1 21
"umask1", /* PerfCtr1 unit mask */
#define D_cmask0 22
"cmask0", /* PerfCtr0 counter mask */
#define D_cmask1 23
"cmask1", /* PerfCtr1 counter mask */
NULL
};
static const struct keyval p6_keyvals[] = {
{ "pic0", picbits, 0,
CPC_P6_PES_PIC0_MASK, 0 },
{ "pic1", picbits, 1,
CPC_P6_PES_PIC1_MASK, 0 },
{ "nouser", nextpair },
{ "nouser0", bitclr, 0,
UINT32_C(1), CPC_P6_PES_USR },
{ "nouser1", bitclr, 1,
UINT32_C(1), CPC_P6_PES_USR },
{ "sys", nextpair },
{ "sys0", bitset, 0,
UINT32_C(1), CPC_P6_PES_OS },
{ "sys1", bitset, 1,
UINT32_C(1), CPC_P6_PES_OS },
{ "noedge", nextpair },
{ "noedge0", bitclr, 0,
UINT32_C(1), CPC_P6_PES_E },
{ "noedge1", bitclr, 1,
UINT32_C(1), CPC_P6_PES_E },
{ "pc", nextpair },
{ "pc0", bitset, 0,
UINT32_C(1), CPC_P6_PES_PC },
{ "pc1", bitset, 1,
UINT32_C(1), CPC_P6_PES_PC },
{ "int", nextpair },
{ "int0", bitset, 0,
UINT32_C(1), CPC_P6_PES_INT },
{ "int1", bitset, 1,
UINT32_C(1), CPC_P6_PES_INT },
{ "inv", nextpair },
{ "inv0", bitset, 0,
UINT32_C(1), CPC_P6_PES_INV },
{ "inv1", bitset, 1,
UINT32_C(1), CPC_P6_PES_INV },
{ "umask0", eightbits, 0,
CPC_P6_PES_UMASK_MASK, CPC_P6_PES_UMASK_SHIFT },
{ "umask1", eightbits, 1,
CPC_P6_PES_UMASK_MASK, CPC_P6_PES_UMASK_SHIFT },
{ "cmask0", eightbits, 0,
CPC_P6_PES_CMASK_MASK, CPC_P6_PES_CMASK_SHIFT },
{ "cmask1", eightbits, 1,
CPC_P6_PES_CMASK_MASK, CPC_P6_PES_CMASK_SHIFT },
};
/*
* Note that this table -must- be an identically indexed
* subset of p6_keyvals.
*/
static const struct keyval p5_keyvals[] = {
{ "pic0", picbits, 0,
CPC_P5_CESR_ES0_MASK, CPC_P5_CESR_ES0_SHIFT },
{ "pic1", picbits, 0,
CPC_P5_CESR_ES1_MASK, CPC_P5_CESR_ES1_SHIFT },
{ "nouser", nextpair },
{ "nouser0", bitclr, 0,
UINT32_C(1), CPC_P5_CESR_USR0 },
{ "nouser1", bitclr, 0,
UINT32_C(1), CPC_P5_CESR_USR1 },
{ "sys", nextpair },
{ "sys0", bitset, 0,
UINT32_C(1), CPC_P5_CESR_OS0 },
{ "sys1", bitset, 0,
UINT32_C(1), CPC_P5_CESR_OS1 },
{ "noedge", nextpair },
{ "noedge0", bitset, 0,
UINT32_C(1), CPC_P5_CESR_CLK0 },
{ "noedge1", bitset, 0,
UINT32_C(1), CPC_P5_CESR_CLK1 },
{ "pc", nextpair },
{ "pc0", bitset, 0,
UINT32_C(1), CPC_P5_CESR_PC0 },
{ "pc1", bitset, 0,
UINT32_C(1), CPC_P5_CESR_PC1 },
};
#if !defined(NDEBUG)
#pragma init(__tablecheck)
static void
__tablecheck(void)
{
uint_t ntokens = sizeof (tokens) / sizeof (tokens[0]) - 1;
uint_t p6_nkeys = sizeof (p6_keyvals) / sizeof (p6_keyvals[0]);
uint_t p5_nkeys = sizeof (p5_keyvals) / sizeof (p5_keyvals[0]);
uint_t n;
assert(ntokens == p6_nkeys);
for (n = 0; n < ntokens; n++)
assert(strcmp(tokens[n], p6_keyvals[n].kv_token) == 0);
assert(p6_nkeys >= p5_nkeys);
for (n = 0; n < p5_nkeys; n++)
assert(strcmp(tokens[n], p5_keyvals[n].kv_token) == 0);
}
#endif /* !NDEBUG */
int
cpc_strtoevent(int cpuver, const char *spec, cpc_event_t *event)
{
static const char fn[] = "strtoevent";
char *value;
char *pic[2];
char *opts;
int errcnt = 0;
uint_t ntokens;
const struct keyval *keyvals;
uint32_t *bits;
if (spec == NULL)
return (errcnt = 1);
bzero(event, sizeof (*event));
switch (event->ce_cpuver = cpuver) {
case CPC_PENTIUM_PRO_MMX:
case CPC_PENTIUM_PRO:
keyvals = p6_keyvals;
ntokens = sizeof (p6_keyvals) / sizeof (p6_keyvals[0]);
bits = &event->ce_pes[0];
bits[0] = bits[1] =
(1u << CPC_P6_PES_USR) | (1u << CPC_P6_PES_E);
break;
case CPC_PENTIUM_MMX:
case CPC_PENTIUM:
keyvals = p5_keyvals;
ntokens = sizeof (p5_keyvals) / sizeof (p5_keyvals[0]);
bits = &event->ce_cesr;
bits[0] =
(1u << CPC_P5_CESR_USR0) | (1u << CPC_P5_CESR_USR1);
break;
default:
return (errcnt = 1);
}
pic[0] = pic[1] = NULL;
opts = strdupa(spec);
while (*opts != '\0') {
const struct keyval *kv;
int idx = getsubopt(&opts, tokens, &value);
if (idx >= 0 && idx < ntokens) {
kv = &keyvals[idx];
if (kv->kv_action(fn, kv, cpuver, value, bits) != 0) {
errcnt++;
break;
}
if (idx == D_pic0) {
if (pic[0] != NULL) {
__cpc_error(fn,
"repeated '%s' token\n",
tokens[idx]);
errcnt++;
break;
}
pic[0] = value;
} else if (idx == D_pic1) {
if (pic[1] != NULL) {
__cpc_error(fn,
"repeated '%s' token\n",
tokens[idx]);
errcnt++;
break;
}
pic[1] = value;
}
} else if (idx == -1) {
/*
* The token given wasn't recognized.
* See if it was an implicit pic specification..
*/
if (pic[0] == NULL) {
kv = &keyvals[D_pic0];
if (kv->kv_action(fn,
kv, cpuver, value, bits) != 0) {
errcnt++;
break;
}
pic[0] = value;
} else if (pic[1] == NULL) {
kv = &keyvals[D_pic1];
if (kv->kv_action(fn,
kv, cpuver, value, bits) != 0) {
errcnt++;
break;
}
pic[1] = value;
} else {
__cpc_error(fn,
gettext("bad token '%s'\n"), value);
errcnt++;
break;
}
} else {
if (idx >= 0 &&
idx < sizeof (tokens) / sizeof (tokens[0]))
__cpc_error(fn,
gettext("bad token '%s'\n"), tokens[idx]);
else
__cpc_error(fn, gettext("bad token\n"));
errcnt++;
break;
}
}
if (pic[0] == NULL || pic[1] == NULL) {
__cpc_error(fn, gettext("two events must be specified\n"));
errcnt++;
}
return (errcnt);
}
/*
* Return a printable description of the control registers.
*
* This routine should always succeed (notwithstanding heap problems),
* but may not be able to correctly decode the registers, if, for
* example, a new processor is under test.
*
* The caller is responsible for free(3c)ing the string returned.
*/
static void
flagstostr(char *buf, int flag0, int flag1, int defvalue, char *tok)
{
buf += strlen(buf);
if (flag0 != defvalue) {
if (flag1 != defvalue)
(void) sprintf(buf, ",%s", tok);
else
(void) sprintf(buf, ",%s0", tok);
} else {
if (flag1 != defvalue)
(void) sprintf(buf, ",%s1", tok);
}
}
static void
masktostr(char *buf, uint8_t bits, char *tok)
{
if (bits != 0) {
buf += strlen(buf);
(void) sprintf(buf, ",%s=0x%x", tok, bits);
}
}
static char *
val8tostr(uint8_t bits)
{
char buf[2 + 2 + 1]; /* 0x %2x \0 */
(void) snprintf(buf, sizeof (buf), "0x%x", bits);
return (strdup(buf));
}
static char *
regtostr(int cpuver, int regno, uint8_t bits)
{
const char *sname;
if ((sname = __cpc_reg_to_name(cpuver, regno, bits)) != NULL)
return (strdup(sname));
return (val8tostr(bits));
}
struct xpes {
uint8_t cmask, umask, evsel;
int usr, sys, edge, inv, irupt, pc;
};
/*ARGSUSED1*/
static void
unmake_pes(uint32_t pes, int cpuver, struct xpes *xpes)
{
xpes->cmask = (uint8_t)(pes >> CPC_P6_PES_CMASK_SHIFT);
xpes->pc = (pes >> CPC_P6_PES_PC) & 1u;
xpes->inv = (pes >> CPC_P6_PES_INV) & 1u;
xpes->irupt = (pes >> CPC_P6_PES_INT) & 1u;
xpes->edge = (pes >> CPC_P6_PES_E) & 1u;
xpes->sys = (pes >> CPC_P6_PES_OS) & 1u;
xpes->usr = (pes >> CPC_P6_PES_USR) & 1u;
xpes->umask = (uint8_t)(pes >> CPC_P6_PES_UMASK_SHIFT);
xpes->evsel = (uint8_t)pes;
}
struct xcesr {
uint8_t evsel[2];
int usr[2], sys[2], clk[2], pc[2];
};
/*ARGSUSED1*/
static void
unmake_cesr(uint32_t cesr, int cpuver, struct xcesr *xcesr)
{
xcesr->evsel[0] = (cesr >> CPC_P5_CESR_ES0_SHIFT) &
CPC_P5_CESR_ES0_MASK;
xcesr->evsel[1] = (cesr >> CPC_P5_CESR_ES1_SHIFT) &
CPC_P5_CESR_ES1_MASK;
xcesr->usr[0] = (cesr >> CPC_P5_CESR_USR0) & 1u;
xcesr->usr[1] = (cesr >> CPC_P5_CESR_USR1) & 1u;
xcesr->sys[0] = (cesr >> CPC_P5_CESR_OS0) & 1u;
xcesr->sys[1] = (cesr >> CPC_P5_CESR_OS1) & 1u;
xcesr->clk[0] = (cesr >> CPC_P5_CESR_CLK0) & 1u;
xcesr->clk[1] = (cesr >> CPC_P5_CESR_CLK1) & 1u;
xcesr->pc[0] = (cesr >> CPC_P5_CESR_PC0) & 1u;
xcesr->pc[1] = (cesr >> CPC_P5_CESR_PC1) & 1u;
/*
* If usr and sys are both disabled, the counter is disabled.
*/
if (xcesr->usr[0] == 0 && xcesr->sys[0] == 0)
xcesr->clk[0] = 0;
if (xcesr->usr[1] == 0 && xcesr->sys[1] == 0)
xcesr->clk[1] = 0;
}
char *
cpc_eventtostr(cpc_event_t *event)
{
char *pic[2];
char buffer[1024];
int cpuver = event->ce_cpuver;
switch (cpuver) {
case CPC_PENTIUM_PRO_MMX:
case CPC_PENTIUM_PRO:
{
struct xpes xpes[2];
unmake_pes(event->ce_pes[0], cpuver, &xpes[0]);
if ((pic[0] = regtostr(cpuver, 0, xpes[0].evsel)) == NULL)
return (NULL);
unmake_pes(event->ce_pes[1], cpuver, &xpes[1]);
if ((pic[1] = regtostr(cpuver, 1, xpes[1].evsel)) == NULL) {
free(pic[0]);
return (NULL);
}
(void) snprintf(buffer, sizeof (buffer), "%s=%s,%s=%s",
tokens[D_pic0], pic[0], tokens[D_pic1], pic[1]);
free(pic[1]);
free(pic[0]);
masktostr(buffer, xpes[0].cmask, tokens[D_cmask0]);
masktostr(buffer, xpes[1].cmask, tokens[D_cmask1]);
masktostr(buffer, xpes[0].umask, tokens[D_umask0]);
masktostr(buffer, xpes[1].umask, tokens[D_umask1]);
flagstostr(buffer,
xpes[0].usr, xpes[1].usr, 1, tokens[D_nouser]);
flagstostr(buffer,
xpes[0].sys, xpes[1].sys, 0, tokens[D_sys]);
flagstostr(buffer,
xpes[0].edge, xpes[1].edge, 1, tokens[D_noedge]);
flagstostr(buffer,
xpes[0].irupt, xpes[1].irupt, 0, tokens[D_int]);
flagstostr(buffer,
xpes[0].inv, xpes[1].inv, 0, tokens[D_inv]);
flagstostr(buffer,
xpes[0].pc, xpes[1].pc, 0, tokens[D_pc]);
break;
}
case CPC_PENTIUM_MMX:
case CPC_PENTIUM:
{
struct xcesr xcesr;
unmake_cesr(event->ce_cesr, cpuver, &xcesr);
if ((pic[0] = regtostr(cpuver, 0, xcesr.evsel[0])) == NULL)
return (NULL);
if ((pic[1] = regtostr(cpuver, 1, xcesr.evsel[1])) == NULL) {
free(pic[0]);
return (NULL);
}
(void) snprintf(buffer, sizeof (buffer), "%s=%s,%s=%s",
tokens[D_pic0], pic[0], tokens[D_pic1], pic[1]);
free(pic[1]);
free(pic[0]);
flagstostr(buffer,
xcesr.usr[0], xcesr.usr[1], 1, tokens[D_nouser]);
flagstostr(buffer,
xcesr.sys[0], xcesr.sys[1], 0, tokens[D_sys]);
flagstostr(buffer,
xcesr.clk[0], xcesr.clk[1], 0, tokens[D_noedge]);
flagstostr(buffer,
xcesr.pc[0], xcesr.pc[1], 0, tokens[D_pc]);
break;
}
default:
return (NULL);
}
return (strdup(buffer));
}
/*
* Utility operations on events
*/
void
cpc_event_accum(cpc_event_t *accum, cpc_event_t *event)
{
if (accum->ce_hrt < event->ce_hrt)
accum->ce_hrt = event->ce_hrt;
accum->ce_tsc += event->ce_tsc;
accum->ce_pic[0] += event->ce_pic[0];
accum->ce_pic[1] += event->ce_pic[1];
}
void
cpc_event_diff(cpc_event_t *diff, cpc_event_t *left, cpc_event_t *right)
{
diff->ce_hrt = left->ce_hrt;
diff->ce_tsc = left->ce_tsc - right->ce_tsc;
diff->ce_pic[0] = left->ce_pic[0] - right->ce_pic[0];
diff->ce_pic[1] = left->ce_pic[1] - right->ce_pic[1];
}
/*
* Given a cpc_event_t and cpc_bind_event() flags,
* translate the cpc_event_t into the cpc_set_t format.
*
* Returns NULL on failure.
*/
cpc_set_t *
__cpc_eventtoset(cpc_t *cpc, cpc_event_t *event, int iflags)
{
cpc_set_t *set;
int cpuver = event->ce_cpuver;
char *pic[2];
int flags[2] = { 0, 0 };
int i;
int j;
int nattrs;
cpc_attr_t *attr;
int intr;
if ((set = cpc_set_create(cpc)) == NULL) {
return (NULL);
}
if (iflags & CPC_BIND_EMT_OVF)
flags[0] = flags[1] = CPC_OVF_NOTIFY_EMT;
switch (cpuver) {
case CPC_PENTIUM_PRO_MMX:
case CPC_PENTIUM_PRO:
{
struct xpes xpes[2];
for (i = 0; i < 2; i++) {
intr = 0;
nattrs = j = 1;
unmake_pes(event->ce_pes[i], cpuver, &xpes[i]);
if ((pic[i] = regtostr(cpuver, i,
xpes[i].evsel)) == NULL) {
(void) cpc_set_destroy(cpc, set);
return (NULL);
}
if (xpes[i].usr == 1)
flags[i] |= CPC_COUNT_USER;
if (xpes[i].sys == 1)
flags[i] |= CPC_COUNT_SYSTEM;
if (xpes[i].irupt == 1) {
nattrs++;
intr = 1;
}
if (xpes[i].cmask)
nattrs++;
if (xpes[i].umask)
nattrs++;
if (xpes[i].inv)
nattrs++;
if (xpes[i].pc)
nattrs++;
if (xpes[i].edge == 0)
nattrs++;
if ((attr = (cpc_attr_t *)malloc(nattrs *
sizeof (cpc_attr_t))) == NULL) {
(void) cpc_set_destroy(cpc, set);
errno = ENOMEM;
return (NULL);
}
/*
* Ensure that pic[0] in the cpc_event_t is bound to
* physical pic0.
*/
attr[0].ca_name = "picnum";
attr[0].ca_val = i;
if (intr) {
attr[j].ca_name = "int";
attr[j].ca_val = 1;
j++;
}
if (xpes[i].cmask) {
attr[j].ca_name = "cmask";
attr[j].ca_val = xpes[i].cmask;
j++;
}
if (xpes[i].umask) {
attr[j].ca_name = "umask";
attr[j].ca_val = xpes[i].umask;
j++;
}
if (xpes[i].inv) {
attr[j].ca_name = "inv";
attr[j].ca_val = 1;
j++;
}
if (xpes[i].pc) {
attr[j].ca_name = "pc";
attr[j].ca_val = 1;
j++;
}
if (xpes[i].edge == 0) {
attr[j].ca_name = "noedge";
attr[j].ca_val = 1;
j++;
}
if (cpc_set_add_request(cpc, set, pic[i],
event->ce_pic[i], flags[i], nattrs, attr) == -1) {
(void) cpc_set_destroy(cpc, set);
free(pic[i]);
free(attr);
return (NULL);
}
free(pic[i]);
free(attr);
}
}
break;
case CPC_PENTIUM_MMX:
case CPC_PENTIUM:
{
struct xcesr xcesr;
unmake_cesr(event->ce_cesr, cpuver, &xcesr);
for (i = 0; i < 2; i++) {
nattrs = j = 1;
if ((pic[i] = regtostr(cpuver, i, xcesr.evsel[i]))
== NULL) {
(void) cpc_set_destroy(cpc, set);
return (NULL);
}
if (xcesr.usr[i] == 1)
flags[i] |= CPC_COUNT_USER;
if (xcesr.sys[i] == 1)
flags[i] |= CPC_COUNT_SYSTEM;
if (xcesr.clk[i] == 1)
nattrs++;
if (xcesr.pc[i] == 1)
nattrs++;
if ((attr = (cpc_attr_t *)malloc(nattrs *
sizeof (cpc_attr_t))) == NULL) {
(void) cpc_set_destroy(cpc, set);
errno = ENOMEM;
return (NULL);
}
/*
* Ensure that pic[0] in the cpc_event_t is bound to
* physical pic0.
*/
attr[0].ca_name = "picnum";
attr[0].ca_val = i;
if (xcesr.clk[i] == 1) {
attr[j].ca_name = "noedge";
attr[j].ca_val = 1;
j++;
}
if (xcesr.pc[i] == 1) {
attr[j].ca_name = "pc";
attr[j].ca_val = 1;
j++;
}
if (cpc_set_add_request(cpc, set, pic[i],
event->ce_pic[i], flags[i], nattrs, attr) == -1) {
(void) cpc_set_destroy(cpc, set);
free(pic[i]);
free(attr);
return (NULL);
}
free(pic[i]);
free(attr);
}
}
break;
default:
(void) cpc_set_destroy(cpc, set);
return (NULL);
}
return (set);
}