xref: /titanic_50/usr/src/lib/libcpc/sparc/event_ultra.c (revision 6a634c9dca3093f3922e4b7ab826d7bdf17bf78e)
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) 1999, 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 
38 #include "libcpc.h"
39 #include "libcpc_impl.h"
40 
41 /*
42  * Event specifications for UltraSPARC performance counters are based
43  * on the content of a getsubopt-like string.
44  * The string should contain something that looks like this:
45  *
46  *	pic0=<eventspec>,pic1=<eventspec>
47  *		[,nouser][,sys]
48  *
49  * For example:
50  *	pic1=0x4,pic0=Instr_cnt
51  * or
52  *	pic0=Instr_cnt,pic1=Cycle_cnt,nouser,sys
53  *
54  * The two events must be named.  The names can be ascii or
55  * a decimal, octal or hexadecimal number as parsed by strtol(3C).
56  *
57  * By default, user event counting is enabled, system event counting
58  * is disabled.
59  *
60  * The routine counts the number of errors encountered while parsing
61  * the string, if no errors are encountered, the event handle is
62  * returned.
63  */
64 
65 const char *
cpc_getusage(int cpuver)66 cpc_getusage(int cpuver)
67 {
68 	switch (cpuver) {
69 	case CPC_ULTRA1:
70 	case CPC_ULTRA2:
71 	case CPC_ULTRA3:
72 	case CPC_ULTRA3_PLUS:
73 	case CPC_ULTRA3_I:
74 	case CPC_ULTRA4_PLUS:
75 		return ("pic0=<event0>,pic1=<event1> "
76 		    "[,sys] "
77 		    "[,nouser]");
78 	default:
79 		return (NULL);
80 	}
81 }
82 
83 /*
84  * This private structure is used to build tables that correspond
85  * to the bit patterns in the control registers of the processor.
86  */
87 struct keyval {
88 	char *kv_token;
89 	int (*kv_action)(const char *,
90 	    const struct keyval *, int, char *, uint64_t *);
91 	uint64_t kv_mask;
92 	int kv_shift;
93 };
94 
95 static int
picbits(const char * fn,const struct keyval * kv,int cpuver,char * value,uint64_t * bits)96 picbits(const char *fn,
97     const struct keyval *kv, int cpuver, char *value, uint64_t *bits)
98 {
99 	uint8_t val8;
100 	uint_t regno;
101 
102 	regno = strcmp(kv->kv_token, "pic0") == 0 ? 0 : 1;
103 
104 	if (value == NULL) {
105 		__cpc_error(fn, gettext("missing '%s' value\n"),
106 		    kv->kv_token);
107 		return (-1);
108 	}
109 	if (__cpc_name_to_reg(cpuver, regno, value, &val8) != 0) {
110 		__cpc_error(fn, gettext("%%pic%d cannot measure "
111 		    "event '%s' on this cpu\n"), regno, value);
112 		return (-1);
113 	}
114 	*bits |= (((uint64_t)val8 & kv->kv_mask) << kv->kv_shift);
115 	return (0);
116 }
117 
118 /*ARGSUSED*/
119 static int
bitclr(const char * fn,const struct keyval * kv,int cpuver,char * value,uint64_t * bits)120 bitclr(const char *fn,
121     const struct keyval *kv, int cpuver, char *value, uint64_t *bits)
122 {
123 	if (value != NULL) {
124 		__cpc_error(fn, gettext("bad arg to '%s'\n"), kv->kv_token);
125 		return (-1);
126 	}
127 	*bits &= ~(kv->kv_mask << kv->kv_shift);
128 	return (0);
129 }
130 
131 /*ARGSUSED*/
132 static int
bitset(const char * fn,const struct keyval * kv,int cpuver,char * value,uint64_t * bits)133 bitset(const char *fn,
134     const struct keyval *kv, int cpuver, char *value, uint64_t *bits)
135 {
136 	if (value != NULL) {
137 		__cpc_error(fn, gettext("bad arg to '%s'\n"), kv->kv_token);
138 		return (-1);
139 	}
140 	*bits |= (kv->kv_mask << kv->kv_shift);
141 	return (0);
142 }
143 
144 /*
145  * This token table must match the keyval tables below.
146  */
147 
148 static char * const tokens[] = {
149 #define	D_pic0		0
150 	"pic0",			/* takes a valid event name */
151 #define	D_pic1		1
152 	"pic1",			/* takes a valid event name */
153 #define	D_nouser	2
154 	"nouser",		/* disables user counts */
155 #define	D_sys		3
156 	"sys",			/* enables system counts */
157 	NULL
158 };
159 
160 static const struct keyval us2_keyvals[] = {
161 	{ "pic0",   picbits,
162 		CPC_ULTRA2_PCR_PIC0_MASK,	CPC_ULTRA_PCR_PIC0_SHIFT },
163 	{ "pic1",   picbits,
164 		CPC_ULTRA2_PCR_PIC1_MASK,	CPC_ULTRA_PCR_PIC1_SHIFT },
165 	{ "nouser", bitclr,
166 		UINT64_C(1),			CPC_ULTRA_PCR_USR },
167 	{ "sys",    bitset,
168 		UINT64_C(1),			CPC_ULTRA_PCR_SYS },
169 };
170 
171 static const struct keyval us3_keyvals[] = {
172 	{ "pic0",   picbits,
173 		CPC_ULTRA3_PCR_PIC0_MASK,	CPC_ULTRA_PCR_PIC0_SHIFT },
174 	{ "pic1",   picbits,
175 		CPC_ULTRA3_PCR_PIC1_MASK,	CPC_ULTRA_PCR_PIC1_SHIFT },
176 	{ "nouser", bitclr,
177 		UINT64_C(1),			CPC_ULTRA_PCR_USR },
178 	{ "sys",    bitset,
179 		UINT64_C(1),			CPC_ULTRA_PCR_SYS },
180 };
181 
182 #if !defined(NDEBUG)
183 #pragma	init(__tablecheck)
184 
185 static void
__tablecheck(void)186 __tablecheck(void)
187 {
188 	uint_t ntokens = sizeof (tokens) / sizeof (tokens[0]) - 1;
189 	uint_t us3_nkeys = sizeof (us3_keyvals) / sizeof (us3_keyvals[0]);
190 	uint_t us2_nkeys = sizeof (us2_keyvals) / sizeof (us2_keyvals[0]);
191 	uint_t n;
192 
193 	assert(ntokens == us3_nkeys);
194 	for (n = 0; n < ntokens; n++)
195 		assert(strcmp(tokens[n], us3_keyvals[n].kv_token) == 0);
196 	assert(us3_nkeys >= us2_nkeys);
197 	for (n = 0; n < us2_nkeys; n++)
198 		assert(strcmp(tokens[n], us2_keyvals[n].kv_token) == 0);
199 }
200 
201 #endif	/* !NDEBUG */
202 
203 int
cpc_strtoevent(int cpuver,const char * spec,cpc_event_t * event)204 cpc_strtoevent(int cpuver, const char *spec, cpc_event_t *event)
205 {
206 	static const char fn[] = "strtoevent";
207 	char *value;
208 	char *pic[2];
209 	char *opts;
210 	int errcnt = 0;
211 	uint_t ntokens;
212 	const struct keyval *keyvals;
213 	uint64_t *bits;
214 
215 	if (spec == NULL)
216 		return (errcnt = 1);
217 
218 	bzero(event, sizeof (*event));
219 	switch (event->ce_cpuver = cpuver) {
220 	case CPC_ULTRA1:
221 	case CPC_ULTRA2:
222 		keyvals = us2_keyvals;
223 		ntokens = sizeof (us2_keyvals) / sizeof (us2_keyvals[0]);
224 		bits = &event->ce_pcr;
225 		*bits = UINT64_C(1) << CPC_ULTRA_PCR_USR;
226 		break;
227 	case CPC_ULTRA3:
228 	case CPC_ULTRA3_PLUS:
229 	case CPC_ULTRA3_I:
230 	case CPC_ULTRA4_PLUS:
231 		keyvals = us3_keyvals;
232 		ntokens = sizeof (us3_keyvals) / sizeof	(us3_keyvals[0]);
233 		bits = &event->ce_pcr;
234 		*bits = UINT64_C(1) << CPC_ULTRA_PCR_USR;
235 		break;
236 	default:
237 		return (errcnt = 1);
238 	}
239 
240 	pic[0] = pic[1] = NULL;
241 
242 	opts = strdupa(spec);
243 	while (*opts != '\0') {
244 		const struct keyval *kv;
245 		int idx = getsubopt(&opts, tokens, &value);
246 
247 		if (idx >= 0 && idx < ntokens) {
248 			kv = &keyvals[idx];
249 			if (kv->kv_action(fn, kv, cpuver, value, bits) != 0) {
250 				errcnt++;
251 				break;
252 			}
253 
254 			if (idx == D_pic0) {
255 				if (pic[0] != NULL) {
256 					__cpc_error(fn,
257 					    "repeated '%s' token\n",
258 					    tokens[idx]);
259 					errcnt++;
260 					break;
261 				}
262 				pic[0] = value;
263 			} else if (idx == D_pic1) {
264 				if (pic[1] != NULL) {
265 					__cpc_error(fn,
266 					    "repeated '%s' token\n",
267 					    tokens[idx]);
268 					errcnt++;
269 					break;
270 				}
271 				pic[1] = value;
272 			}
273 		} else if (idx == -1) {
274 			/*
275 			 * The token given wasn't recognized.
276 			 * See if it was an implicit pic specification..
277 			 */
278 			if (pic[0] == NULL) {
279 				kv = &keyvals[D_pic0];
280 				if (kv->kv_action(fn,
281 				    kv, cpuver, value, bits) != 0) {
282 					errcnt++;
283 					break;
284 				}
285 				pic[0] = value;
286 			} else if (pic[1] == NULL) {
287 				kv = &keyvals[D_pic1];
288 				if (kv->kv_action(fn,
289 				    kv, cpuver, value, bits) != 0) {
290 					errcnt++;
291 					break;
292 				}
293 				pic[1] = value;
294 			} else {
295 				__cpc_error(fn,
296 				    gettext("bad token '%s'\n"), value);
297 				errcnt++;
298 				break;
299 			}
300 		} else {
301 			if (idx >= 0 &&
302 			    idx < sizeof (tokens) / sizeof (tokens[0]))
303 				__cpc_error(fn,
304 				    gettext("bad token '%s'\n"), tokens[idx]);
305 			else
306 				__cpc_error(fn, gettext("bad token\n"));
307 			errcnt++;
308 			break;
309 		}
310 	}
311 
312 	if (pic[0] == NULL || pic[1] == NULL) {
313 		__cpc_error(fn, gettext("two events must be specified\n"));
314 		errcnt++;
315 	}
316 
317 	return (errcnt);
318 }
319 
320 /*
321  * Return a printable description of the control registers.
322  *
323  * This routine should always succeed (notwithstanding heap problems),
324  * but may not be able to correctly decode the registers, if, for
325  * example, a new processor is under test.
326  *
327  * The caller is responsible for free(3c)ing the string returned.
328  */
329 
330 static char *
val8tostr(uint8_t bits)331 val8tostr(uint8_t bits)
332 {
333 	char buf[2 + 2 + 1];	/* 0x %2x \0 */
334 	(void) snprintf(buf, sizeof (buf), "0x%x", bits);
335 	return (strdup(buf));
336 }
337 
338 static char *
regtostr(int cpuver,int regno,uint8_t bits)339 regtostr(int cpuver, int regno, uint8_t bits)
340 {
341 	const char *sname;
342 
343 	if ((sname = __cpc_reg_to_name(cpuver, regno, bits)) != NULL)
344 		return (strdup(sname));
345 	return (val8tostr(bits));
346 }
347 
348 struct xpcr {
349 	uint8_t pic[2];
350 	int usr, sys;
351 };
352 
353 static void
unmake_pcr(uint64_t pcr,int cpuver,struct xpcr * xpcr)354 unmake_pcr(uint64_t pcr, int cpuver, struct xpcr *xpcr)
355 {
356 	const struct keyval *kv;
357 
358 	switch (cpuver) {
359 	case CPC_ULTRA1:
360 	case CPC_ULTRA2:
361 	default:
362 		kv = us2_keyvals;
363 		break;
364 	case CPC_ULTRA3:
365 	case CPC_ULTRA3_PLUS:
366 	case CPC_ULTRA3_I:
367 	case CPC_ULTRA4_PLUS:
368 		kv = us3_keyvals;
369 		break;
370 	}
371 	xpcr->pic[0] = (uint8_t)((pcr >> kv[D_pic0].kv_shift) &
372 	    kv[D_pic0].kv_mask);
373 	xpcr->pic[1] = (uint8_t)((pcr >> kv[D_pic1].kv_shift) &
374 	    kv[D_pic1].kv_mask);
375 	xpcr->usr = (pcr >> kv[D_nouser].kv_shift) &
376 	    kv[D_nouser].kv_mask;
377 	xpcr->sys = (pcr >> kv[D_sys].kv_shift) &
378 	    kv[D_sys].kv_mask;
379 }
380 
381 char *
cpc_eventtostr(cpc_event_t * event)382 cpc_eventtostr(cpc_event_t *event)
383 {
384 	struct xpcr xpcr;
385 	char *pic[2];
386 	char buffer[1024];
387 
388 	switch (event->ce_cpuver) {
389 	case CPC_ULTRA1:
390 	case CPC_ULTRA2:
391 	case CPC_ULTRA3:
392 	case CPC_ULTRA3_PLUS:
393 	case CPC_ULTRA3_I:
394 	case CPC_ULTRA4_PLUS:
395 		break;
396 	default:
397 		return (NULL);
398 	}
399 
400 	unmake_pcr(event->ce_pcr, event->ce_cpuver, &xpcr);
401 	if ((pic[0] = regtostr(event->ce_cpuver, 0, xpcr.pic[0])) == NULL)
402 		return (NULL);
403 	if ((pic[1] = regtostr(event->ce_cpuver, 1, xpcr.pic[1])) == NULL) {
404 		free(pic[0]);
405 		return (NULL);
406 	}
407 
408 	(void) snprintf(buffer, sizeof (buffer), "%s=%s,%s=%s",
409 	    tokens[D_pic0], pic[0], tokens[D_pic1], pic[1]);
410 
411 	free(pic[1]);
412 	free(pic[0]);
413 
414 	if (!xpcr.usr)
415 		(void) strcat(strcat(buffer, ","), tokens[D_nouser]);
416 	if (xpcr.sys)
417 		(void) strcat(strcat(buffer, ","), tokens[D_sys]);
418 
419 	return (strdup(buffer));
420 }
421 
422 /*
423  * Utility operations on events
424  */
425 void
cpc_event_accum(cpc_event_t * accum,cpc_event_t * event)426 cpc_event_accum(cpc_event_t *accum, cpc_event_t *event)
427 {
428 	if (accum->ce_hrt < event->ce_hrt)
429 		accum->ce_hrt = event->ce_hrt;
430 	accum->ce_tick += event->ce_tick;
431 	accum->ce_pic[0] += event->ce_pic[0];
432 	accum->ce_pic[1] += event->ce_pic[1];
433 }
434 
435 void
cpc_event_diff(cpc_event_t * diff,cpc_event_t * left,cpc_event_t * right)436 cpc_event_diff(cpc_event_t *diff, cpc_event_t *left, cpc_event_t *right)
437 {
438 	diff->ce_hrt = left->ce_hrt;
439 	diff->ce_tick = left->ce_tick - right->ce_tick;
440 	diff->ce_pic[0] = left->ce_pic[0] - right->ce_pic[0];
441 	diff->ce_pic[1] = left->ce_pic[1] - right->ce_pic[1];
442 }
443 
444 /*
445  * Given a cpc_event_t and cpc_bind_event() flags, translate the event into the
446  * cpc_set_t format.
447  *
448  * Returns NULL on failure.
449  */
450 cpc_set_t *
__cpc_eventtoset(cpc_t * cpc,cpc_event_t * event,int iflags)451 __cpc_eventtoset(cpc_t *cpc, cpc_event_t *event, int iflags)
452 {
453 	cpc_set_t	*set = NULL;
454 	struct xpcr	xpcr;
455 	char		*pic[2];
456 	uint32_t	flag = 0;
457 	cpc_attr_t	attr = { "picnum", 0 };
458 
459 	switch (event->ce_cpuver) {
460 	case CPC_ULTRA1:
461 	case CPC_ULTRA2:
462 	case CPC_ULTRA3:
463 	case CPC_ULTRA3_PLUS:
464 	case CPC_ULTRA3_I:
465 	case CPC_ULTRA4_PLUS:
466 		break;
467 	default:
468 		return (NULL);
469 	}
470 
471 	unmake_pcr(event->ce_pcr, event->ce_cpuver, &xpcr);
472 	if ((pic[0] = regtostr(event->ce_cpuver, 0, xpcr.pic[0])) == NULL)
473 		return (NULL);
474 	if ((pic[1] = regtostr(event->ce_cpuver, 1, xpcr.pic[1])) == NULL) {
475 		free(pic[0]);
476 		return (NULL);
477 	}
478 
479 	if (xpcr.usr)
480 		flag |= CPC_COUNT_USER;
481 	if (xpcr.sys)
482 		flag |= CPC_COUNT_SYSTEM;
483 
484 	if (iflags & CPC_BIND_EMT_OVF)
485 		flag |= CPC_OVF_NOTIFY_EMT;
486 
487 	if ((set = cpc_set_create(cpc)) == NULL)
488 		goto bad;
489 
490 	if (cpc_set_add_request(cpc, set, pic[0], event->ce_pic[0], flag,
491 	    1, &attr) != 0)
492 		goto bad;
493 
494 	attr.ca_val = 1;
495 	if (cpc_set_add_request(cpc, set, pic[1], event->ce_pic[1], flag,
496 	    1, &attr) != 1)
497 		goto bad;
498 
499 	free(pic[0]);
500 	free(pic[1]);
501 
502 	return (set);
503 
504 bad:
505 	if (set != NULL)
506 		(void) cpc_set_destroy(cpc, set);
507 	free(pic[0]);
508 	free(pic[1]);
509 	return (NULL);
510 }
511