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