xref: /illumos-gate/usr/src/lib/libcpc/i386/event_pentium.c (revision 45ede40b2394db7967e59f19288fae9b62efd4aa)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 /*
27  * Routines to capture processor-dependencies in event specification.
28  */
29 
30 #include <sys/types.h>
31 #include <string.h>
32 #include <strings.h>
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <libintl.h>
36 #include <assert.h>
37 #include <errno.h>
38 
39 #include "libcpc.h"
40 #include "libcpc_impl.h"
41 
42 /*
43  * Event specifications for Pentium performance counters are based
44  * on the content of a getsubopt-like string.
45  * The string should contain something that looks like this:
46  *
47  *	pic0=<eventspec>,pic1=<eventspec>
48  *		[,cmask0=<maskspec>][,cmask1=<maskspec>]
49  *		[,umask0=<maskspec>][,umask1=<maskspec>]
50  *		[,inv[0|1]][,noedge[0|1]]
51  *		[,sys[0|1]][,nouser[0|1]]
52  *
53  * For example:
54  *	pic0=data_mem_refs,pic1=l2_ld,sys
55  * or
56  *	pic0=l2_ld,pic1=bus_drdy_clocks,umask1=0x20,nouser1
57  *
58  * By default, user event counting is enabled, system event counting
59  * is disabled.
60  *
61  * Note that Pentium and Pentium Pro have different event specifications.
62  *
63  * The two events must be named.  The names can be ascii or
64  * a decimal, octal or hexadecimal number as parsed by strtol(3C).
65  *
66  * The routine counts the number of errors encountered while parsing
67  * the string, if no errors are encountered, the event handle is
68  * returned.
69  */
70 
71 const char *
72 cpc_getusage(int cpuver)
73 {
74 	switch (cpuver) {
75 	case CPC_PENTIUM_PRO_MMX:
76 	case CPC_PENTIUM_PRO:
77 		return ("pic0=<event0>,pic1=<event1> "
78 		    "[,sys[0|1]] "
79 		    "[,nouser[0|1]] "
80 		    "[,noedge[0|1]] "
81 		    "[,pc[0|1]] "
82 		    "[,int[0|1]] "
83 		    "[,inv[0|1]] "
84 		    "[,cmask[0|1]=<maskspec>] "
85 		    "[,umask[0|1]=<maskspec>] ");
86 	case CPC_PENTIUM_MMX:
87 	case CPC_PENTIUM:
88 		return ("pic0=<event0>,pic1=<event1> "
89 		    "[,sys[0|1]] "
90 		    "[,nouser[0|1]] "
91 		    "[,noedge[0|1]] "
92 		    "[,pc[0|1]]");
93 	default:
94 		return (NULL);
95 	}
96 }
97 
98 struct keyval {
99 	char *kv_token;
100 	int (*kv_action)(const char *,
101 	    const struct keyval *, int, char *, uint32_t *);
102 	uint_t kv_regno;
103 	uint32_t kv_mask;
104 	int kv_shift;
105 };
106 
107 /*ARGSUSED*/
108 static int
109 eightbits(const char *fn,
110     const struct keyval *kv, int cpuver, char *value, uint32_t *bits)
111 {
112 	char *eptr = NULL;
113 	long l;
114 
115 	if (value == NULL) {
116 		__cpc_error(fn, gettext("missing '%s' value\n"),
117 		    kv->kv_token);
118 		return (-1);
119 	}
120 	l = strtol(value, &eptr, 0);
121 	if (value == eptr || l < 0 || l > UINT8_MAX) {
122 		__cpc_error(fn, gettext("bad '%s' value\n"), kv->kv_token);
123 		return (-1);
124 	}
125 	bits[kv->kv_regno] |= ((uint8_t)l & kv->kv_mask) << kv->kv_shift;
126 	return (0);
127 }
128 
129 static int
130 picbits(const char *fn,
131     const struct keyval *kv, int cpuver, char *value, uint32_t *bits)
132 {
133 	uint8_t val8;
134 	uint_t regno;
135 
136 	regno = strcmp(kv->kv_token, "pic0") == 0 ? 0 : 1;
137 
138 	if (value == NULL) {
139 		__cpc_error(fn, gettext("missing '%s' value\n"),
140 		    kv->kv_token);
141 		return (-1);
142 	}
143 
144 	if (__cpc_name_to_reg(cpuver, regno, value, &val8) != 0) {
145 		switch (cpuver) {
146 		case CPC_PENTIUM_PRO_MMX:
147 		case CPC_PENTIUM_PRO:
148 			assert(kv->kv_regno == regno);
149 			__cpc_error(fn, gettext(
150 			    "PerfCtr%d cannot measure '%s' on this cpu\n"),
151 			    regno, value);
152 			break;
153 		case CPC_PENTIUM_MMX:
154 		case CPC_PENTIUM:
155 			assert(kv->kv_regno == 0);
156 			__cpc_error(fn, gettext(
157 			    "CTR%d cannot measure '%s' on this cpu\n"),
158 			    regno, value);
159 			break;
160 		}
161 		return (-1);
162 	}
163 	bits[kv->kv_regno] |= (val8 & kv->kv_mask) << kv->kv_shift;
164 	return (0);
165 }
166 
167 /*ARGSUSED2*/
168 static int
169 bitclr(const char *fn,
170     const struct keyval *kv, int cpuver, char *value, uint32_t *bits)
171 {
172 	if (value != NULL) {
173 		__cpc_error(fn, gettext("bad arg to '%s'\n"), kv->kv_token);
174 		return (-1);
175 	}
176 	bits[kv->kv_regno] &= ~(kv->kv_mask << kv->kv_shift);
177 	return (0);
178 }
179 
180 /*ARGSUSED2*/
181 static int
182 bitset(const char *fn,
183     const struct keyval *kv, int cpuver, char *value, uint32_t *bits)
184 {
185 	if (value != NULL) {
186 		__cpc_error(fn, gettext("bad arg to '%s'\n"), kv->kv_token);
187 		return (-1);
188 	}
189 	bits[kv->kv_regno] |= (kv->kv_mask << kv->kv_shift);
190 	return (0);
191 }
192 
193 static int
194 nextpair(const char *fn,
195     const struct keyval *kv, int cpuver, char *value, uint32_t *bits)
196 {
197 	int rv;
198 
199 	if (value != NULL) {
200 		__cpc_error(fn, gettext("bad arg to '%s'\n"), kv->kv_token);
201 		return (-1);
202 	}
203 	kv++;
204 	if ((rv = kv->kv_action(fn, kv, cpuver, value, bits)) != 0)
205 		return (rv);
206 	kv++;
207 	return (kv->kv_action(fn, kv, cpuver, value, bits));
208 }
209 
210 /*
211  * This token table must match the keyval tables below.
212  */
213 
214 static char * const tokens[] = {
215 #define	D_pic0		0
216 	"pic0",			/* takes a valid event name */
217 #define	D_pic1		1
218 	"pic1",			/* takes a valid event name */
219 #define	D_nouser	2
220 	"nouser",		/* disables user counts */
221 #define	D_nouser0	3
222 	"nouser0",
223 #define	D_nouser1	4
224 	"nouser1",
225 #define	D_sys		5
226 	"sys",			/* enables system counts */
227 #define	D_sys0		6
228 	"sys0",
229 #define	D_sys1		7
230 	"sys1",
231 #define	D_noedge	8
232 	"noedge",		/* disable edge detect */
233 #define	D_noedge0	9
234 	"noedge0",
235 #define	D_noedge1	10
236 	"noedge1",
237 #define	D_pc		11
238 	"pc",			/* sets pin control high */
239 #define	D_pc0		12
240 	"pc0",
241 #define	D_pc1		13
242 	"pc1",
243 
244 /*
245  * These additional keywords are for Pentium Pro / Pentium II machines.
246  */
247 #define	D_int		14
248 	"int",			/* enable interrupt on counter overflow */
249 #define	D_int0		15
250 	"int0",
251 #define	D_int1		16
252 	"int1",
253 #define	D_inv		17
254 	"inv",			/* invert cmask comparison */
255 #define	D_inv0		18
256 	"inv0",
257 #define	D_inv1		19
258 	"inv1",
259 #define	D_umask0	20
260 	"umask0",		/* PerfCtr0 unit mask */
261 #define	D_umask1	21
262 	"umask1",		/* PerfCtr1 unit mask */
263 #define	D_cmask0	22
264 	"cmask0",		/* PerfCtr0 counter mask */
265 #define	D_cmask1	23
266 	"cmask1",		/* PerfCtr1 counter mask */
267 	NULL
268 };
269 
270 static const struct keyval p6_keyvals[] = {
271 	{ "pic0",	picbits,	0,
272 		CPC_P6_PES_PIC0_MASK,	0 },
273 	{ "pic1",	picbits,	1,
274 		CPC_P6_PES_PIC1_MASK,	0 },
275 	{ "nouser",	nextpair },
276 	{ "nouser0",	bitclr,		0,
277 		UINT32_C(1),		CPC_P6_PES_USR },
278 	{ "nouser1",	bitclr,		1,
279 		UINT32_C(1),		CPC_P6_PES_USR },
280 	{ "sys",	nextpair },
281 	{ "sys0",	bitset,		0,
282 		UINT32_C(1),		CPC_P6_PES_OS },
283 	{ "sys1",	bitset,		1,
284 		UINT32_C(1),		CPC_P6_PES_OS },
285 	{ "noedge",	nextpair },
286 	{ "noedge0",	bitclr,		0,
287 		UINT32_C(1),		CPC_P6_PES_E },
288 	{ "noedge1",	bitclr,		1,
289 		UINT32_C(1),		CPC_P6_PES_E },
290 	{ "pc",		nextpair },
291 	{ "pc0",	bitset,		0,
292 		UINT32_C(1),		CPC_P6_PES_PC },
293 	{ "pc1",	bitset,		1,
294 		UINT32_C(1),		CPC_P6_PES_PC },
295 	{ "int",	nextpair },
296 	{ "int0",	bitset,		0,
297 		UINT32_C(1),		CPC_P6_PES_INT },
298 	{ "int1",	bitset,		1,
299 		UINT32_C(1),		CPC_P6_PES_INT },
300 	{ "inv",	nextpair },
301 	{ "inv0",	bitset,		0,
302 		UINT32_C(1),		CPC_P6_PES_INV },
303 	{ "inv1",	bitset,		1,
304 		UINT32_C(1),		CPC_P6_PES_INV },
305 	{ "umask0",	eightbits,	0,
306 		CPC_P6_PES_UMASK_MASK,	CPC_P6_PES_UMASK_SHIFT },
307 	{ "umask1",	eightbits,	1,
308 		CPC_P6_PES_UMASK_MASK,	CPC_P6_PES_UMASK_SHIFT },
309 	{ "cmask0",	eightbits,	0,
310 		CPC_P6_PES_CMASK_MASK,	CPC_P6_PES_CMASK_SHIFT },
311 	{ "cmask1",	eightbits,	1,
312 		CPC_P6_PES_CMASK_MASK,	CPC_P6_PES_CMASK_SHIFT },
313 };
314 
315 /*
316  * Note that this table -must- be an identically indexed
317  * subset of p6_keyvals.
318  */
319 static const struct keyval p5_keyvals[] = {
320 	{ "pic0",	picbits,	0,
321 		CPC_P5_CESR_ES0_MASK,	CPC_P5_CESR_ES0_SHIFT },
322 	{ "pic1",	picbits,	0,
323 		CPC_P5_CESR_ES1_MASK,	CPC_P5_CESR_ES1_SHIFT },
324 	{ "nouser",	nextpair },
325 	{ "nouser0",	bitclr,		0,
326 		UINT32_C(1),		CPC_P5_CESR_USR0 },
327 	{ "nouser1",	bitclr,		0,
328 		UINT32_C(1),		CPC_P5_CESR_USR1 },
329 	{ "sys",	nextpair },
330 	{ "sys0",	bitset,		0,
331 		UINT32_C(1),		CPC_P5_CESR_OS0 },
332 	{ "sys1",	bitset,		0,
333 		UINT32_C(1),		CPC_P5_CESR_OS1 },
334 	{ "noedge",	nextpair },
335 	{ "noedge0",	bitset,		0,
336 		UINT32_C(1),		CPC_P5_CESR_CLK0 },
337 	{ "noedge1",	bitset,		0,
338 		UINT32_C(1),		CPC_P5_CESR_CLK1 },
339 	{ "pc",		nextpair },
340 	{ "pc0",	bitset,		0,
341 		UINT32_C(1),		CPC_P5_CESR_PC0 },
342 	{ "pc1",	bitset,		0,
343 		UINT32_C(1),		CPC_P5_CESR_PC1 },
344 };
345 
346 #if !defined(NDEBUG)
347 #pragma	init(__tablecheck)
348 
349 static void
350 __tablecheck(void)
351 {
352 	uint_t ntokens = sizeof (tokens) / sizeof (tokens[0]) - 1;
353 	uint_t p6_nkeys = sizeof (p6_keyvals) / sizeof (p6_keyvals[0]);
354 	uint_t p5_nkeys = sizeof (p5_keyvals) / sizeof (p5_keyvals[0]);
355 	uint_t n;
356 
357 	assert(ntokens == p6_nkeys);
358 	for (n = 0; n < ntokens; n++)
359 		assert(strcmp(tokens[n], p6_keyvals[n].kv_token) == 0);
360 	assert(p6_nkeys >= p5_nkeys);
361 	for (n = 0; n < p5_nkeys; n++)
362 		assert(strcmp(tokens[n], p5_keyvals[n].kv_token) == 0);
363 }
364 
365 #endif	/* !NDEBUG */
366 
367 int
368 cpc_strtoevent(int cpuver, const char *spec, cpc_event_t *event)
369 {
370 	static const char fn[] = "strtoevent";
371 	char *value;
372 	char *pic[2];
373 	char *opts;
374 	int errcnt = 0;
375 	uint_t ntokens;
376 	const struct keyval *keyvals;
377 	uint32_t *bits;
378 
379 	if (spec == NULL)
380 		return (errcnt = 1);
381 
382 	bzero(event, sizeof (*event));
383 	switch (event->ce_cpuver = cpuver) {
384 	case CPC_PENTIUM_PRO_MMX:
385 	case CPC_PENTIUM_PRO:
386 		keyvals = p6_keyvals;
387 		ntokens = sizeof (p6_keyvals) / sizeof (p6_keyvals[0]);
388 		bits = &event->ce_pes[0];
389 		bits[0] = bits[1] =
390 		    (1u << CPC_P6_PES_USR) | (1u << CPC_P6_PES_E);
391 		break;
392 	case CPC_PENTIUM_MMX:
393 	case CPC_PENTIUM:
394 		keyvals = p5_keyvals;
395 		ntokens = sizeof (p5_keyvals) / sizeof (p5_keyvals[0]);
396 		bits = &event->ce_cesr;
397 		bits[0] =
398 		    (1u << CPC_P5_CESR_USR0) | (1u << CPC_P5_CESR_USR1);
399 		break;
400 	default:
401 		return (errcnt = 1);
402 	}
403 
404 	pic[0] = pic[1] = NULL;
405 
406 	opts = strdupa(spec);
407 	while (*opts != '\0') {
408 		const struct keyval *kv;
409 		int idx = getsubopt(&opts, tokens, &value);
410 
411 		if (idx >= 0 && idx < ntokens) {
412 			kv = &keyvals[idx];
413 			if (kv->kv_action(fn, kv, cpuver, value, bits) != 0) {
414 				errcnt++;
415 				break;
416 			}
417 
418 			if (idx == D_pic0) {
419 				if (pic[0] != NULL) {
420 					__cpc_error(fn,
421 					    "repeated '%s' token\n",
422 					    tokens[idx]);
423 					errcnt++;
424 					break;
425 				}
426 				pic[0] = value;
427 			} else if (idx == D_pic1) {
428 				if (pic[1] != NULL) {
429 					__cpc_error(fn,
430 					    "repeated '%s' token\n",
431 					    tokens[idx]);
432 					errcnt++;
433 					break;
434 				}
435 				pic[1] = value;
436 			}
437 		} else if (idx == -1) {
438 			/*
439 			 * The token given wasn't recognized.
440 			 * See if it was an implicit pic specification..
441 			 */
442 			if (pic[0] == NULL) {
443 				kv = &keyvals[D_pic0];
444 				if (kv->kv_action(fn,
445 				    kv, cpuver, value, bits) != 0) {
446 					errcnt++;
447 					break;
448 				}
449 				pic[0] = value;
450 			} else if (pic[1] == NULL) {
451 				kv = &keyvals[D_pic1];
452 				if (kv->kv_action(fn,
453 				    kv, cpuver, value, bits) != 0) {
454 					errcnt++;
455 					break;
456 				}
457 				pic[1] = value;
458 			} else {
459 				__cpc_error(fn,
460 				    gettext("bad token '%s'\n"), value);
461 				errcnt++;
462 				break;
463 			}
464 		} else {
465 			if (idx >= 0 &&
466 			    idx < sizeof (tokens) / sizeof (tokens[0]))
467 				__cpc_error(fn,
468 				    gettext("bad token '%s'\n"), tokens[idx]);
469 			else
470 				__cpc_error(fn, gettext("bad token\n"));
471 			errcnt++;
472 			break;
473 		}
474 	}
475 
476 	if (pic[0] == NULL || pic[1] == NULL) {
477 		__cpc_error(fn, gettext("two events must be specified\n"));
478 		errcnt++;
479 	}
480 
481 	return (errcnt);
482 }
483 
484 /*
485  * Return a printable description of the control registers.
486  *
487  * This routine should always succeed (notwithstanding heap problems),
488  * but may not be able to correctly decode the registers, if, for
489  * example, a new processor is under test.
490  *
491  * The caller is responsible for free(3c)ing the string returned.
492  */
493 
494 static void
495 flagstostr(char *buf, int flag0, int flag1, int defvalue, char *tok)
496 {
497 	buf += strlen(buf);
498 	if (flag0 != defvalue) {
499 		if (flag1 != defvalue)
500 			(void) sprintf(buf, ",%s", tok);
501 		else
502 			(void) sprintf(buf, ",%s0", tok);
503 	} else {
504 		if (flag1 != defvalue)
505 			(void) sprintf(buf, ",%s1", tok);
506 	}
507 }
508 
509 static void
510 masktostr(char *buf, uint8_t bits, char *tok)
511 {
512 	if (bits != 0) {
513 		buf += strlen(buf);
514 		(void) sprintf(buf, ",%s=0x%x", tok, bits);
515 	}
516 }
517 
518 static char *
519 val8tostr(uint8_t bits)
520 {
521 	char buf[2 + 2 + 1];	/* 0x %2x \0 */
522 	(void) snprintf(buf, sizeof (buf), "0x%x", bits);
523 	return (strdup(buf));
524 }
525 
526 static char *
527 regtostr(int cpuver, int regno, uint8_t bits)
528 {
529 	const char *sname;
530 
531 	if ((sname = __cpc_reg_to_name(cpuver, regno, bits)) != NULL)
532 		return (strdup(sname));
533 	return (val8tostr(bits));
534 }
535 
536 struct xpes {
537 	uint8_t cmask, umask, evsel;
538 	int usr, sys, edge, inv, irupt, pc;
539 };
540 
541 /*ARGSUSED1*/
542 static void
543 unmake_pes(uint32_t pes, int cpuver, struct xpes *xpes)
544 {
545 	xpes->cmask = (uint8_t)(pes >> CPC_P6_PES_CMASK_SHIFT);
546 	xpes->pc = (pes >> CPC_P6_PES_PC) & 1u;
547 	xpes->inv = (pes >> CPC_P6_PES_INV) & 1u;
548 	xpes->irupt = (pes >> CPC_P6_PES_INT) & 1u;
549 	xpes->edge = (pes >> CPC_P6_PES_E) & 1u;
550 	xpes->sys = (pes >> CPC_P6_PES_OS) & 1u;
551 	xpes->usr = (pes >> CPC_P6_PES_USR) & 1u;
552 	xpes->umask = (uint8_t)(pes >> CPC_P6_PES_UMASK_SHIFT);
553 	xpes->evsel = (uint8_t)pes;
554 }
555 
556 struct xcesr {
557 	uint8_t evsel[2];
558 	int usr[2], sys[2], clk[2], pc[2];
559 };
560 
561 /*ARGSUSED1*/
562 static void
563 unmake_cesr(uint32_t cesr, int cpuver, struct xcesr *xcesr)
564 {
565 	xcesr->evsel[0] = (cesr >> CPC_P5_CESR_ES0_SHIFT) &
566 	    CPC_P5_CESR_ES0_MASK;
567 	xcesr->evsel[1] = (cesr >> CPC_P5_CESR_ES1_SHIFT) &
568 	    CPC_P5_CESR_ES1_MASK;
569 	xcesr->usr[0] = (cesr >> CPC_P5_CESR_USR0) & 1u;
570 	xcesr->usr[1] = (cesr >> CPC_P5_CESR_USR1) & 1u;
571 	xcesr->sys[0] = (cesr >> CPC_P5_CESR_OS0) & 1u;
572 	xcesr->sys[1] = (cesr >> CPC_P5_CESR_OS1) & 1u;
573 	xcesr->clk[0] = (cesr >> CPC_P5_CESR_CLK0) & 1u;
574 	xcesr->clk[1] = (cesr >> CPC_P5_CESR_CLK1) & 1u;
575 	xcesr->pc[0] = (cesr >> CPC_P5_CESR_PC0) & 1u;
576 	xcesr->pc[1] = (cesr >> CPC_P5_CESR_PC1) & 1u;
577 	/*
578 	 * If usr and sys are both disabled, the counter is disabled.
579 	 */
580 	if (xcesr->usr[0] == 0 && xcesr->sys[0] == 0)
581 		xcesr->clk[0] = 0;
582 	if (xcesr->usr[1] == 0 && xcesr->sys[1] == 0)
583 		xcesr->clk[1] = 0;
584 }
585 
586 char *
587 cpc_eventtostr(cpc_event_t *event)
588 {
589 	char *pic[2];
590 	char buffer[1024];
591 	int cpuver = event->ce_cpuver;
592 
593 	switch (cpuver) {
594 	case CPC_PENTIUM_PRO_MMX:
595 	case CPC_PENTIUM_PRO:
596 	{
597 		struct xpes xpes[2];
598 
599 		unmake_pes(event->ce_pes[0], cpuver, &xpes[0]);
600 		if ((pic[0] = regtostr(cpuver, 0, xpes[0].evsel)) == NULL)
601 			return (NULL);
602 
603 		unmake_pes(event->ce_pes[1], cpuver, &xpes[1]);
604 		if ((pic[1] = regtostr(cpuver, 1, xpes[1].evsel)) == NULL) {
605 			free(pic[0]);
606 			return (NULL);
607 		}
608 		(void) snprintf(buffer, sizeof (buffer), "%s=%s,%s=%s",
609 		    tokens[D_pic0], pic[0], tokens[D_pic1], pic[1]);
610 		free(pic[1]);
611 		free(pic[0]);
612 		masktostr(buffer, xpes[0].cmask, tokens[D_cmask0]);
613 		masktostr(buffer, xpes[1].cmask, tokens[D_cmask1]);
614 		masktostr(buffer, xpes[0].umask, tokens[D_umask0]);
615 		masktostr(buffer, xpes[1].umask, tokens[D_umask1]);
616 		flagstostr(buffer,
617 		    xpes[0].usr, xpes[1].usr, 1, tokens[D_nouser]);
618 		flagstostr(buffer,
619 		    xpes[0].sys, xpes[1].sys, 0, tokens[D_sys]);
620 		flagstostr(buffer,
621 		    xpes[0].edge, xpes[1].edge, 1, tokens[D_noedge]);
622 		flagstostr(buffer,
623 		    xpes[0].irupt, xpes[1].irupt, 0, tokens[D_int]);
624 		flagstostr(buffer,
625 		    xpes[0].inv, xpes[1].inv, 0, tokens[D_inv]);
626 		flagstostr(buffer,
627 		    xpes[0].pc, xpes[1].pc, 0, tokens[D_pc]);
628 		break;
629 	}
630 	case CPC_PENTIUM_MMX:
631 	case CPC_PENTIUM:
632 	{
633 		struct xcesr xcesr;
634 
635 		unmake_cesr(event->ce_cesr, cpuver, &xcesr);
636 		if ((pic[0] = regtostr(cpuver, 0, xcesr.evsel[0])) == NULL)
637 			return (NULL);
638 		if ((pic[1] = regtostr(cpuver, 1, xcesr.evsel[1])) == NULL) {
639 			free(pic[0]);
640 			return (NULL);
641 		}
642 		(void) snprintf(buffer, sizeof (buffer), "%s=%s,%s=%s",
643 		    tokens[D_pic0], pic[0], tokens[D_pic1], pic[1]);
644 		free(pic[1]);
645 		free(pic[0]);
646 		flagstostr(buffer,
647 		    xcesr.usr[0], xcesr.usr[1], 1, tokens[D_nouser]);
648 		flagstostr(buffer,
649 		    xcesr.sys[0], xcesr.sys[1], 0, tokens[D_sys]);
650 		flagstostr(buffer,
651 		    xcesr.clk[0], xcesr.clk[1], 0, tokens[D_noedge]);
652 		flagstostr(buffer,
653 		    xcesr.pc[0], xcesr.pc[1], 0, tokens[D_pc]);
654 		break;
655 	}
656 	default:
657 		return (NULL);
658 	}
659 	return (strdup(buffer));
660 }
661 
662 /*
663  * Utility operations on events
664  */
665 void
666 cpc_event_accum(cpc_event_t *accum, cpc_event_t *event)
667 {
668 	if (accum->ce_hrt < event->ce_hrt)
669 		accum->ce_hrt = event->ce_hrt;
670 	accum->ce_tsc += event->ce_tsc;
671 	accum->ce_pic[0] += event->ce_pic[0];
672 	accum->ce_pic[1] += event->ce_pic[1];
673 }
674 
675 void
676 cpc_event_diff(cpc_event_t *diff, cpc_event_t *left, cpc_event_t *right)
677 {
678 	diff->ce_hrt = left->ce_hrt;
679 	diff->ce_tsc = left->ce_tsc - right->ce_tsc;
680 	diff->ce_pic[0] = left->ce_pic[0] - right->ce_pic[0];
681 	diff->ce_pic[1] = left->ce_pic[1] - right->ce_pic[1];
682 }
683 
684 /*
685  * Given a cpc_event_t and cpc_bind_event() flags,
686  * translate the cpc_event_t into the cpc_set_t format.
687  *
688  * Returns NULL on failure.
689  */
690 cpc_set_t *
691 __cpc_eventtoset(cpc_t *cpc, cpc_event_t *event, int iflags)
692 {
693 	cpc_set_t	*set;
694 	int		cpuver = event->ce_cpuver;
695 	char		*pic[2];
696 	int		flags[2] = { 0, 0 };
697 	int		i;
698 	int		j;
699 	int		nattrs;
700 	cpc_attr_t	*attr;
701 	int		intr;
702 
703 	if ((set = cpc_set_create(cpc)) == NULL) {
704 		return (NULL);
705 	}
706 
707 	if (iflags & CPC_BIND_EMT_OVF)
708 		flags[0] = flags[1] = CPC_OVF_NOTIFY_EMT;
709 
710 	switch (cpuver) {
711 	case CPC_PENTIUM_PRO_MMX:
712 	case CPC_PENTIUM_PRO:
713 	{
714 		struct xpes xpes[2];
715 
716 		for (i = 0; i < 2; i++) {
717 			intr = 0;
718 			nattrs = j = 1;
719 			unmake_pes(event->ce_pes[i], cpuver, &xpes[i]);
720 			if ((pic[i] = regtostr(cpuver, i,
721 			    xpes[i].evsel)) == NULL) {
722 				(void) cpc_set_destroy(cpc, set);
723 				return (NULL);
724 			}
725 			if (xpes[i].usr == 1)
726 				flags[i] |= CPC_COUNT_USER;
727 			if (xpes[i].sys == 1)
728 				flags[i] |= CPC_COUNT_SYSTEM;
729 			if (xpes[i].irupt == 1) {
730 				nattrs++;
731 				intr = 1;
732 			}
733 
734 			if (xpes[i].cmask)
735 				nattrs++;
736 			if (xpes[i].umask)
737 				nattrs++;
738 			if (xpes[i].inv)
739 				nattrs++;
740 			if (xpes[i].pc)
741 				nattrs++;
742 			if (xpes[i].edge == 0)
743 				nattrs++;
744 
745 			if ((attr = (cpc_attr_t *)malloc(nattrs *
746 			    sizeof (cpc_attr_t))) == NULL) {
747 				(void) cpc_set_destroy(cpc, set);
748 				errno = ENOMEM;
749 				return (NULL);
750 			}
751 
752 			/*
753 			 * Ensure that pic[0] in the cpc_event_t is bound to
754 			 * physical pic0.
755 			 */
756 			attr[0].ca_name = "picnum";
757 			attr[0].ca_val = i;
758 
759 			if (intr) {
760 				attr[j].ca_name = "int";
761 				attr[j].ca_val = 1;
762 				j++;
763 			}
764 			if (xpes[i].cmask) {
765 				attr[j].ca_name = "cmask";
766 				attr[j].ca_val = xpes[i].cmask;
767 				j++;
768 			}
769 			if (xpes[i].umask) {
770 				attr[j].ca_name = "umask";
771 				attr[j].ca_val = xpes[i].umask;
772 				j++;
773 			}
774 			if (xpes[i].inv) {
775 				attr[j].ca_name = "inv";
776 				attr[j].ca_val = 1;
777 				j++;
778 			}
779 			if (xpes[i].pc) {
780 				attr[j].ca_name = "pc";
781 				attr[j].ca_val = 1;
782 				j++;
783 			}
784 			if (xpes[i].edge == 0) {
785 				attr[j].ca_name = "noedge";
786 				attr[j].ca_val = 1;
787 				j++;
788 			}
789 
790 			if (cpc_set_add_request(cpc, set, pic[i],
791 			    event->ce_pic[i], flags[i], nattrs, attr) == -1) {
792 				(void) cpc_set_destroy(cpc, set);
793 				free(pic[i]);
794 				free(attr);
795 				return (NULL);
796 			}
797 			free(pic[i]);
798 			free(attr);
799 		}
800 	}
801 	break;
802 	case CPC_PENTIUM_MMX:
803 	case CPC_PENTIUM:
804 	{
805 		struct xcesr xcesr;
806 		unmake_cesr(event->ce_cesr, cpuver, &xcesr);
807 
808 		for (i = 0; i < 2; i++) {
809 			nattrs = j = 1;
810 
811 			if ((pic[i] = regtostr(cpuver, i, xcesr.evsel[i]))
812 			    == NULL) {
813 				(void) cpc_set_destroy(cpc, set);
814 				return (NULL);
815 			}
816 
817 			if (xcesr.usr[i] == 1)
818 				flags[i] |= CPC_COUNT_USER;
819 			if (xcesr.sys[i] == 1)
820 				flags[i] |= CPC_COUNT_SYSTEM;
821 			if (xcesr.clk[i] == 1)
822 				nattrs++;
823 			if (xcesr.pc[i] == 1)
824 				nattrs++;
825 
826 			if ((attr = (cpc_attr_t *)malloc(nattrs *
827 			    sizeof (cpc_attr_t))) == NULL) {
828 				(void) cpc_set_destroy(cpc, set);
829 				errno = ENOMEM;
830 				return (NULL);
831 			}
832 
833 			/*
834 			 * Ensure that pic[0] in the cpc_event_t is bound to
835 			 * physical pic0.
836 			 */
837 			attr[0].ca_name = "picnum";
838 			attr[0].ca_val = i;
839 
840 			if (xcesr.clk[i] == 1) {
841 				attr[j].ca_name = "noedge";
842 				attr[j].ca_val = 1;
843 				j++;
844 			}
845 
846 			if (xcesr.pc[i] == 1) {
847 				attr[j].ca_name = "pc";
848 				attr[j].ca_val = 1;
849 				j++;
850 			}
851 
852 			if (cpc_set_add_request(cpc, set, pic[i],
853 			    event->ce_pic[i], flags[i], nattrs, attr) == -1) {
854 				(void) cpc_set_destroy(cpc, set);
855 				free(pic[i]);
856 				free(attr);
857 				return (NULL);
858 			}
859 
860 			free(pic[i]);
861 			free(attr);
862 		}
863 	}
864 	break;
865 	default:
866 		(void) cpc_set_destroy(cpc, set);
867 		return (NULL);
868 	}
869 
870 	return (set);
871 }
872