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