xref: /illumos-gate/usr/src/cmd/cpc/common/strtoset.c (revision 355b4669e025ff377602b6fc7caaf30dbc218371)
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 #include <stdio.h>
30 #include <stdlib.h>
31 #include <alloca.h>
32 #include <errno.h>
33 #include <libintl.h>
34 
35 #include "libcpc.h"
36 
37 /*
38  * Takes a string and converts it to a cpc_set_t.
39  *
40  * While processing the string using getsubopt(), we will use an array of
41  * requests to hold the data, and a proprietary representation of attributes
42  * which allow us to avoid a realloc()/bcopy() dance every time we come across
43  * a new attribute.
44  *
45  * Not until after the string has been processed in its entirety do we
46  * allocate and specify a request set properly.
47  */
48 
49 /*
50  * Leave enough room in token strings for picn, nousern, or sysn where n is
51  * picnum.
52  */
53 #define	TOK_SIZE	10
54 
55 typedef struct __tmp_attr {
56 	char			*name;
57 	uint64_t		val;
58 	struct __tmp_attr	*next;
59 } tmp_attr_t;
60 
61 typedef struct __tok_info {
62 	char			*name;
63 	int			picnum;
64 } tok_info_t;
65 
66 typedef struct __request_t {
67 	char			cr_event[CPC_MAX_EVENT_LEN];
68 	uint_t			cr_flags;
69 	uint_t			cr_nattrs;	/* # CPU-specific attrs */
70 } request_t;
71 
72 static void strtoset_cleanup(void);
73 static void smt_special(int picnum);
74 static void *emalloc(size_t n);
75 
76 /*
77  * Clients of cpc_strtoset may set this to specify an error handler during
78  * string parsing.
79  */
80 cpc_errhndlr_t		*strtoset_errfn = NULL;
81 
82 static request_t	*reqs;
83 static int		nreqs;
84 static int		ncounters;
85 
86 static tmp_attr_t	**attrs;
87 static int		ntoks;
88 static char		**toks;
89 static tok_info_t	*tok_info;
90 static int		(*(*tok_funcs))(int, char *);
91 static char		**attrlist;	/* array of ptrs to toks in attrlistp */
92 static int		nattrs;
93 static cpc_t		*cpc;
94 static int		found;
95 
96 static void
97 strtoset_err(const char *fmt, ...)
98 {
99 	va_list ap;
100 
101 	if (strtoset_errfn == NULL)
102 		return;
103 
104 	va_start(ap, fmt);
105 	(*strtoset_errfn)("cpc_strtoset", -1, fmt, ap);
106 	va_end(ap);
107 }
108 
109 /*ARGSUSED*/
110 static void
111 event_walker(void *arg, uint_t picno, const char *event)
112 {
113 	if (strncmp(arg, event, CPC_MAX_EVENT_LEN) == 0)
114 		found = 1;
115 }
116 
117 static int
118 event_valid(int picnum, char *event)
119 {
120 	found = 0;
121 	cpc_walk_events_pic(cpc, picnum, event, event_walker);
122 	return (found);
123 }
124 
125 /*
126  * An unknown token was encountered; check here if it is an implicit event
127  * name. We allow users to omit the "picn=" portion of the event spec, and
128  * assign such events to available pics in order they are returned from
129  * getsubopt(3C). We start our search for an available pic _after_ the highest
130  * picnum to be assigned. This ensures that the event spec can never be out of
131  * order; i.e. if the event string is "eventa,eventb" we must ensure that the
132  * picnum counting eventa is less than the picnum counting eventb.
133  */
134 static int
135 find_event(char *event)
136 {
137 	int i;
138 
139 	/*
140 	 * Find the first unavailable pic, after which we must start our search.
141 	 */
142 	for (i = ncounters - 1; i >= 0; i--) {
143 		if (reqs[i].cr_event[0] != '\0')
144 			break;
145 	}
146 	/*
147 	 * If the last counter has been assigned, we cannot place this event.
148 	 */
149 	if (i == ncounters - 1)
150 		return (0);
151 
152 	/*
153 	 * If none of the counters have been assigned yet, i is -1 and we will
154 	 * begin our search at 0. Else we begin our search at the counter after
155 	 * the last one currently assigned.
156 	 */
157 	i++;
158 
159 	for (; i < ncounters; i++) {
160 		if (event_valid(i, event) == 0)
161 			continue;
162 
163 		nreqs++;
164 		(void) strncpy(reqs[i].cr_event, event, CPC_MAX_EVENT_LEN);
165 		return (1);
166 	}
167 
168 	return (0);
169 }
170 
171 static int
172 pic(int tok, char *val)
173 {
174 	int picnum = tok_info[tok].picnum;
175 	/*
176 	 * Make sure the each pic only appears in the spec once.
177 	 */
178 	if (reqs[picnum].cr_event[0] != '\0') {
179 		strtoset_err(gettext("repeated 'pic%d' token\n"), picnum);
180 		return (-1);
181 	}
182 
183 	if (val == NULL || val[0] == '\0') {
184 		strtoset_err(gettext("missing 'pic%d' value\n"), picnum);
185 		return (-1);
186 	}
187 
188 	if (event_valid(picnum, val) == 0) {
189 		strtoset_err(gettext("pic%d cannot measure event '%s' on this "
190 		    "cpu\n"), picnum, val);
191 		return (-1);
192 	}
193 
194 	nreqs++;
195 	(void) strncpy(reqs[picnum].cr_event, val, CPC_MAX_EVENT_LEN);
196 	return (0);
197 }
198 
199 /*
200  * We explicitly ignore any value provided for these tokens, as their
201  * mere presence signals us to turn on or off the relevant flags.
202  */
203 /*ARGSUSED*/
204 static int
205 flag(int tok, char *val)
206 {
207 	int i;
208 	int picnum = tok_info[tok].picnum;
209 
210 	/*
211 	 * If picnum is -1, this flag should be applied to all reqs.
212 	 */
213 	for (i = (picnum == -1) ? 0 : picnum; i < ncounters; i++) {
214 		if (strcmp(tok_info[tok].name, "nouser") == 0)
215 			reqs[i].cr_flags &= ~CPC_COUNT_USER;
216 		else if (strcmp(tok_info[tok].name, "sys") == 0)
217 			reqs[i].cr_flags |= CPC_COUNT_SYSTEM;
218 		else
219 			return (-1);
220 
221 		if (picnum != -1)
222 			break;
223 	}
224 
225 	return (0);
226 }
227 
228 static int
229 doattr(int tok, char *val)
230 {
231 	int		i;
232 	int		picnum = tok_info[tok].picnum;
233 	tmp_attr_t	*tmp;
234 	char		*endptr;
235 
236 	/*
237 	 * If picnum is -1, this attribute should be applied to all reqs.
238 	 */
239 	for (i = (picnum == -1) ? 0 : picnum; i < ncounters; i++) {
240 		tmp = (tmp_attr_t *)emalloc(sizeof (tmp_attr_t));
241 		tmp->name = tok_info[tok].name;
242 		if (val != NULL) {
243 			tmp->val = strtoll(val, &endptr, 0);
244 			if (endptr == val) {
245 				strtoset_err(gettext("invalid value '%s' for "
246 				    "attribute '%s'\n"), val, tmp->name);
247 				free(tmp);
248 				return (-1);
249 			}
250 		} else
251 			/*
252 			 * No value was provided for this attribute,
253 			 * so specify a default value of 1.
254 			 */
255 			tmp->val = 1;
256 
257 		tmp->next = attrs[i];
258 		attrs[i] = tmp;
259 		reqs[i].cr_nattrs++;
260 
261 		if (picnum != -1)
262 			break;
263 	}
264 
265 	return (0);
266 }
267 
268 /*ARGSUSED*/
269 static void
270 attr_count_walker(void *arg, const char *attr)
271 {
272 	/*
273 	 * We don't allow picnum to be specified by the user.
274 	 */
275 	if (strncmp(attr, "picnum", 7) == 0)
276 		return;
277 	(*(int *)arg)++;
278 }
279 
280 static int
281 cpc_count_attrs(cpc_t *cpc)
282 {
283 	int nattrs = 0;
284 
285 	cpc_walk_attrs(cpc, &nattrs, attr_count_walker);
286 
287 	return (nattrs);
288 }
289 
290 static void
291 attr_walker(void *arg, const char *attr)
292 {
293 	int *i = arg;
294 
295 	if (strncmp(attr, "picnum", 7) == 0)
296 		return;
297 
298 	if ((attrlist[(*i)++] = strdup(attr)) == NULL) {
299 		strtoset_err(gettext("no memory available\n"));
300 		exit(0);
301 	}
302 }
303 
304 cpc_set_t *
305 cpc_strtoset(cpc_t *cpcin, const char *spec, int smt)
306 {
307 	cpc_set_t		*set;
308 	cpc_attr_t		*req_attrs;
309 	tmp_attr_t		*tmp;
310 	size_t			toklen;
311 	int			i;
312 	int			j;
313 	int			x;
314 	char			*opts;
315 	char			*val;
316 
317 	cpc = cpcin;
318 	nattrs = 0;
319 
320 	ncounters = cpc_npic(cpc);
321 
322 	reqs = (request_t *)emalloc(ncounters * sizeof (request_t));
323 
324 	attrs = (tmp_attr_t **)emalloc(ncounters * sizeof (tmp_attr_t *));
325 
326 	for (i = 0; i < ncounters; i++) {
327 		reqs[i].cr_event[0] = '\0';
328 		reqs[i].cr_flags = CPC_COUNT_USER;
329 		/*
330 		 * Each pic will have at least one attribute: the physical pic
331 		 * assignment via the "picnum" attribute. Set that up here for
332 		 * each request.
333 		 */
334 		reqs[i].cr_nattrs = 1;
335 		attrs[i] = emalloc(sizeof (tmp_attr_t));
336 		attrs[i]->name = "picnum";
337 		attrs[i]->val = i;
338 		attrs[i]->next = NULL;
339 	}
340 
341 	/*
342 	 * Build up a list of acceptable tokens.
343 	 *
344 	 * Permitted tokens are
345 	 * picn=event
346 	 * nousern
347 	 * sysn
348 	 * attrn=val
349 	 * nouser
350 	 * sys
351 	 * attr=val
352 	 *
353 	 * Where n is a counter number, and attr is any attribute supported by
354 	 * the current processor.
355 	 *
356 	 * If a token appears without a counter number, it applies to all
357 	 * counters in the request set.
358 	 *
359 	 * The number of tokens is:
360 	 *
361 	 * picn: ncounters
362 	 * generic flags: 2 * ncounters (nouser, sys)
363 	 * attrs: nattrs * ncounters
364 	 * attrs with no picnum: nattrs
365 	 * generic flags with no picnum: 2 (nouser, sys)
366 	 * NULL token to signify end of list to getsubopt(3C).
367 	 *
368 	 * Matching each token's index in the token table is a function which
369 	 * process that token; these are in tok_funcs.
370 	 */
371 
372 	/*
373 	 * Count the number of valid attributes.
374 	 * Set up the attrlist array to point to the attributes in attrlistp.
375 	 */
376 	nattrs = cpc_count_attrs(cpc);
377 	attrlist = (char **)emalloc(nattrs * sizeof (char *));
378 
379 	i = 0;
380 	cpc_walk_attrs(cpc, &i, attr_walker);
381 
382 	ntoks = ncounters + (2 * ncounters) + (nattrs * ncounters) + nattrs + 3;
383 	toks = (char **)emalloc(ntoks * sizeof (char *));
384 	tok_info = (tok_info_t *)emalloc(ntoks * sizeof (tok_info_t));
385 
386 	tok_funcs = (int (**)(int, char *))emalloc(ntoks *
387 	    sizeof (int (*)(char *)));
388 
389 	for (i = 0; i < ntoks; i++) {
390 		toks[i] = NULL;
391 		tok_funcs[i] = NULL;
392 	}
393 
394 	x = 0;
395 	for (i = 0; i < ncounters; i++) {
396 		toks[x] = (char *)emalloc(TOK_SIZE);
397 		(void) snprintf(toks[x], TOK_SIZE, "pic%d", i);
398 		tok_info[x].name = "pic";
399 		tok_info[i].picnum = i;
400 		tok_funcs[x] = pic;
401 		x++;
402 	}
403 
404 	for (i = 0; i < ncounters; i++) {
405 		toks[x] = (char *)emalloc(TOK_SIZE);
406 		(void) snprintf(toks[x], TOK_SIZE, "nouser%d", i);
407 		tok_info[x].name = "nouser";
408 		tok_info[x].picnum = i;
409 		tok_funcs[x] = flag;
410 		x++;
411 	}
412 
413 	for (i = 0; i < ncounters; i++) {
414 		toks[x] = (char *)emalloc(TOK_SIZE);
415 		(void) snprintf(toks[x], TOK_SIZE, "sys%d", i);
416 		tok_info[x].name = "sys";
417 		tok_info[x].picnum = i;
418 		tok_funcs[x] = flag;
419 		x++;
420 	}
421 	for (j = 0; j < nattrs; j++) {
422 		toklen = strlen(attrlist[j]) + 3;
423 		for (i = 0; i < ncounters; i++) {
424 			toks[x] = (char *)emalloc(toklen);
425 			(void) snprintf(toks[x], toklen, "%s%d", attrlist[j],
426 			    i);
427 			tok_info[x].name = attrlist[j];
428 			tok_info[x].picnum = i;
429 			tok_funcs[x] = doattr;
430 			x++;
431 		}
432 
433 		/*
434 		 * Now create a token for this attribute with no picnum; if used
435 		 * it will be applied to all reqs.
436 		 */
437 		toks[x] = (char *)emalloc(toklen);
438 		(void) snprintf(toks[x], toklen, "%s", attrlist[j]);
439 		tok_info[x].name = attrlist[j];
440 		tok_info[x].picnum = -1;
441 		tok_funcs[x] = doattr;
442 		x++;
443 	}
444 
445 	toks[x] = "nouser";
446 	tok_info[x].name = "nouser";
447 	tok_info[x].picnum = -1;
448 	tok_funcs[x] = flag;
449 	x++;
450 
451 	toks[x] = "sys";
452 	tok_info[x].name = "sys";
453 	tok_info[x].picnum = -1;
454 	tok_funcs[x] = flag;
455 	x++;
456 
457 	toks[x] = NULL;
458 
459 	opts = strcpy(alloca(strlen(spec) + 1), spec);
460 	while (*opts != '\0') {
461 		int idx = getsubopt(&opts, toks, &val);
462 
463 		if (idx == -1) {
464 			if (find_event(val) == 0) {
465 				strtoset_err(gettext("bad token '%s'\n"), val);
466 				goto inval;
467 			} else
468 				continue;
469 		}
470 
471 		if (tok_funcs[idx](idx, val) == -1)
472 			goto inval;
473 	}
474 
475 	/*
476 	 * The string has been processed. Now count how many PICs were used,
477 	 * create a request set, and specify each request properly.
478 	 */
479 
480 	if ((set = cpc_set_create(cpc)) == NULL) {
481 		strtoset_err(gettext("no memory available\n"));
482 		exit(0);
483 	}
484 
485 	for (i = 0; i < ncounters; i++) {
486 		if (reqs[i].cr_event[0] == '\0')
487 			continue;
488 
489 		/*
490 		 * If the caller wishes to measure events on the physical CPU,
491 		 * we need to add SMT attributes to each request.
492 		 */
493 		if (smt)
494 			smt_special(i);
495 
496 		req_attrs = (cpc_attr_t *)emalloc(reqs[i].cr_nattrs *
497 		    sizeof (cpc_attr_t));
498 
499 		j = 0;
500 		for (tmp = attrs[i]; tmp != NULL; tmp = tmp->next) {
501 			req_attrs[j].ca_name = tmp->name;
502 			req_attrs[j].ca_val = tmp->val;
503 			j++;
504 		}
505 
506 		if (cpc_set_add_request(cpc, set, reqs[i].cr_event, 0,
507 		    reqs[i].cr_flags, reqs[i].cr_nattrs, req_attrs) == -1) {
508 			free(req_attrs);
509 			(void) cpc_set_destroy(cpc, set);
510 			strtoset_err(
511 				gettext("cpc_set_add_request() failed: %s\n"),
512 				    strerror(errno));
513 			goto inval;
514 		}
515 
516 		free(req_attrs);
517 	}
518 
519 	strtoset_cleanup();
520 
521 	return (set);
522 
523 inval:
524 	strtoset_cleanup();
525 	errno = EINVAL;
526 	return (NULL);
527 }
528 
529 static void
530 strtoset_cleanup(void)
531 {
532 	int		i;
533 	tmp_attr_t	*tmp, *p;
534 
535 	for (i = 0; i < nattrs; i++)
536 		free(attrlist[i]);
537 	free(attrlist);
538 
539 	for (i = 0; i < ncounters; i++) {
540 		for (tmp = attrs[i]; tmp != NULL; tmp = p) {
541 			p = tmp->next;
542 			free(tmp);
543 		}
544 	}
545 	free(attrs);
546 
547 	for (i = 0; i < ntoks - 3; i++)
548 		/*
549 		 * We free all but the last three tokens: "nouser", "sys", NULL
550 		 */
551 		free(toks[i]);
552 	free(toks);
553 	free(reqs);
554 	free(tok_info);
555 	free(tok_funcs);
556 }
557 
558 /*
559  * The following is called to modify requests so that they count events on
560  * behalf of a physical processor, instead of a logical processor. It duplicates
561  * the request flags for the sibling processor (i.e. if the request counts user
562  * events, add an attribute to count user events on the sibling processor also).
563  */
564 static void
565 smt_special(int picnum)
566 {
567 	tmp_attr_t *attr;
568 
569 	if (reqs[picnum].cr_flags & CPC_COUNT_USER) {
570 		attr = (tmp_attr_t *)emalloc(sizeof (tmp_attr_t));
571 		attr->name = "count_sibling_usr";
572 		attr->val = 1;
573 		attr->next = attrs[picnum];
574 		attrs[picnum] = attr;
575 		reqs[picnum].cr_nattrs++;
576 	}
577 
578 	if (reqs[picnum].cr_flags & CPC_COUNT_SYSTEM) {
579 		attr = (tmp_attr_t *)emalloc(sizeof (tmp_attr_t));
580 		attr->name = "count_sibling_sys";
581 		attr->val = 1;
582 		attr->next = attrs[picnum];
583 		attrs[picnum] = attr;
584 		reqs[picnum].cr_nattrs++;
585 	}
586 }
587 
588 /*
589  * If we ever fail to get memory, we print an error message and exit.
590  */
591 static void *
592 emalloc(size_t n)
593 {
594 	void *p = malloc(n);
595 
596 	if (p == NULL) {
597 		strtoset_err(gettext("no memory available\n"));
598 		exit(0);
599 	}
600 
601 	return (p);
602 }
603