dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / cpc / common / strtoset.c
blobdda5a227a023721301e760db9d5b86b5c8bf77cc
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <errno.h>
29 #include <libintl.h>
31 #include "libcpc.h"
34 * Takes a string and converts it to a cpc_set_t.
36 * While processing the string using getsubopt(), we will use an array of
37 * requests to hold the data, and a proprietary representation of attributes
38 * which allow us to avoid a realloc()/bcopy() dance every time we come across
39 * a new attribute.
41 * Not until after the string has been processed in its entirety do we
42 * allocate and specify a request set properly.
46 * Leave enough room in token strings for picn, nousern, or sysn where n is
47 * picnum.
49 #define TOK_SIZE 10
51 typedef struct __tmp_attr {
52 char *name;
53 uint64_t val;
54 struct __tmp_attr *next;
55 } tmp_attr_t;
57 typedef struct __tok_info {
58 char *name;
59 int picnum;
60 } tok_info_t;
62 typedef struct __request_t {
63 char cr_event[CPC_MAX_EVENT_LEN];
64 uint_t cr_flags;
65 uint_t cr_nattrs; /* # CPU-specific attrs */
66 } request_t;
68 static void strtoset_cleanup(void);
69 static void smt_special(int picnum);
70 static void *emalloc(size_t n);
73 * Clients of cpc_strtoset may set this to specify an error handler during
74 * string parsing.
76 cpc_errhndlr_t *strtoset_errfn = NULL;
78 static request_t *reqs;
79 static int nreqs;
80 static int ncounters;
82 static tmp_attr_t **attrs;
83 static int ntoks;
84 static char **toks;
85 static tok_info_t *tok_info;
86 static int (*(*tok_funcs))(int, char *);
87 static char **attrlist; /* array of ptrs to toks in attrlistp */
88 static int nattrs;
89 static cpc_t *cpc;
90 static int found;
92 static void
93 strtoset_err(const char *fmt, ...)
95 va_list ap;
97 if (strtoset_errfn == NULL)
98 return;
100 va_start(ap, fmt);
101 (*strtoset_errfn)("cpc_strtoset", -1, fmt, ap);
102 va_end(ap);
105 /*ARGSUSED*/
106 static void
107 event_walker(void *arg, uint_t picno, const char *event)
109 if (strncmp(arg, event, CPC_MAX_EVENT_LEN) == 0)
110 found = 1;
113 static int
114 event_valid(int picnum, char *event)
116 char *end_event;
117 int err;
119 found = 0;
121 cpc_walk_events_pic(cpc, picnum, event, event_walker);
123 if (found)
124 return (1);
126 cpc_walk_generic_events_pic(cpc, picnum, event, event_walker);
128 if (found)
129 return (1);
132 * Before assuming this is an invalid event, see if we have been given
133 * a raw event code.
134 * Check the second argument of strtol() to ensure invalid events
135 * beginning with number do not go through.
137 err = errno;
138 errno = 0;
139 (void) strtol(event, &end_event, 0);
140 if ((errno == 0) && (*end_event == '\0')) {
142 * Success - this is a valid raw code in hex, decimal, or octal.
144 errno = err;
145 return (1);
148 errno = err;
149 return (0);
153 * An unknown token was encountered; check here if it is an implicit event
154 * name. We allow users to omit the "picn=" portion of the event spec, and
155 * assign such events to available pics in order they are returned from
156 * getsubopt(3C). We start our search for an available pic _after_ the highest
157 * picnum to be assigned. This ensures that the event spec can never be out of
158 * order; i.e. if the event string is "eventa,eventb" we must ensure that the
159 * picnum counting eventa is less than the picnum counting eventb.
161 static int
162 find_event(char *event)
164 int i;
167 * Event names cannot have '=' in them. If present here, it means we
168 * have encountered an unknown token (foo=bar, for example).
170 if (strchr(event, '=') != NULL)
171 return (0);
174 * Find the first unavailable pic, after which we must start our search.
176 for (i = ncounters - 1; i >= 0; i--) {
177 if (reqs[i].cr_event[0] != '\0')
178 break;
181 * If the last counter has been assigned, we cannot place this event.
183 if (i == ncounters - 1)
184 return (0);
187 * If none of the counters have been assigned yet, i is -1 and we will
188 * begin our search at 0. Else we begin our search at the counter after
189 * the last one currently assigned.
191 i++;
193 for (; i < ncounters; i++) {
194 if (event_valid(i, event) == 0)
195 continue;
197 nreqs++;
198 (void) strncpy(reqs[i].cr_event, event, CPC_MAX_EVENT_LEN);
199 return (1);
202 return (0);
205 static int
206 pic(int tok, char *val)
208 int picnum = tok_info[tok].picnum;
210 * Make sure the each pic only appears in the spec once.
212 if (reqs[picnum].cr_event[0] != '\0') {
213 strtoset_err(gettext("repeated 'pic%d' token\n"), picnum);
214 return (-1);
217 if (val == NULL || val[0] == '\0') {
218 strtoset_err(gettext("missing 'pic%d' value\n"), picnum);
219 return (-1);
222 if (event_valid(picnum, val) == 0) {
223 strtoset_err(gettext("pic%d cannot measure event '%s' on this "
224 "cpu\n"), picnum, val);
225 return (-1);
228 nreqs++;
229 (void) strncpy(reqs[picnum].cr_event, val, CPC_MAX_EVENT_LEN);
230 return (0);
234 * We explicitly ignore any value provided for these tokens, as their
235 * mere presence signals us to turn on or off the relevant flags.
237 /*ARGSUSED*/
238 static int
239 flag(int tok, char *val)
241 int i;
242 int picnum = tok_info[tok].picnum;
245 * If picnum is -1, this flag should be applied to all reqs.
247 for (i = (picnum == -1) ? 0 : picnum; i < ncounters; i++) {
248 if (strcmp(tok_info[tok].name, "nouser") == 0)
249 reqs[i].cr_flags &= ~CPC_COUNT_USER;
250 else if (strcmp(tok_info[tok].name, "sys") == 0)
251 reqs[i].cr_flags |= CPC_COUNT_SYSTEM;
252 else
253 return (-1);
255 if (picnum != -1)
256 break;
259 return (0);
262 static int
263 doattr(int tok, char *val)
265 int i;
266 int picnum = tok_info[tok].picnum;
267 tmp_attr_t *tmp;
268 char *endptr;
271 * If picnum is -1, this attribute should be applied to all reqs.
273 for (i = (picnum == -1) ? 0 : picnum; i < ncounters; i++) {
274 tmp = (tmp_attr_t *)emalloc(sizeof (tmp_attr_t));
275 tmp->name = tok_info[tok].name;
276 if (val != NULL) {
277 tmp->val = strtoll(val, &endptr, 0);
278 if (endptr == val) {
279 strtoset_err(gettext("invalid value '%s' for "
280 "attribute '%s'\n"), val, tmp->name);
281 free(tmp);
282 return (-1);
284 } else
286 * No value was provided for this attribute,
287 * so specify a default value of 1.
289 tmp->val = 1;
291 tmp->next = attrs[i];
292 attrs[i] = tmp;
293 reqs[i].cr_nattrs++;
295 if (picnum != -1)
296 break;
299 return (0);
302 /*ARGSUSED*/
303 static void
304 attr_count_walker(void *arg, const char *attr)
307 * We don't allow picnum to be specified by the user.
309 if (strncmp(attr, "picnum", 7) == 0)
310 return;
311 (*(int *)arg)++;
314 static int
315 cpc_count_attrs(cpc_t *cpc)
317 int nattrs = 0;
319 cpc_walk_attrs(cpc, &nattrs, attr_count_walker);
321 return (nattrs);
324 static void
325 attr_walker(void *arg, const char *attr)
327 int *i = arg;
329 if (strncmp(attr, "picnum", 7) == 0)
330 return;
332 if ((attrlist[(*i)++] = strdup(attr)) == NULL) {
333 strtoset_err(gettext("no memory available\n"));
334 exit(0);
338 cpc_set_t *
339 cpc_strtoset(cpc_t *cpcin, const char *spec, int smt)
341 cpc_set_t *set;
342 cpc_attr_t *req_attrs;
343 tmp_attr_t *tmp;
344 size_t toklen;
345 int i;
346 int j;
347 int x;
348 char *opts;
349 char *val;
351 cpc = cpcin;
352 nattrs = 0;
354 ncounters = cpc_npic(cpc);
356 reqs = (request_t *)emalloc(ncounters * sizeof (request_t));
358 attrs = (tmp_attr_t **)emalloc(ncounters * sizeof (tmp_attr_t *));
360 for (i = 0; i < ncounters; i++) {
361 reqs[i].cr_event[0] = '\0';
362 reqs[i].cr_flags = CPC_COUNT_USER;
364 * Each pic will have at least one attribute: the physical pic
365 * assignment via the "picnum" attribute. Set that up here for
366 * each request.
368 reqs[i].cr_nattrs = 1;
369 attrs[i] = emalloc(sizeof (tmp_attr_t));
370 attrs[i]->name = "picnum";
371 attrs[i]->val = i;
372 attrs[i]->next = NULL;
376 * Build up a list of acceptable tokens.
378 * Permitted tokens are
379 * picn=event
380 * nousern
381 * sysn
382 * attrn=val
383 * nouser
384 * sys
385 * attr=val
387 * Where n is a counter number, and attr is any attribute supported by
388 * the current processor.
390 * If a token appears without a counter number, it applies to all
391 * counters in the request set.
393 * The number of tokens is:
395 * picn: ncounters
396 * generic flags: 2 * ncounters (nouser, sys)
397 * attrs: nattrs * ncounters
398 * attrs with no picnum: nattrs
399 * generic flags with no picnum: 2 (nouser, sys)
400 * NULL token to signify end of list to getsubopt(3C).
402 * Matching each token's index in the token table is a function which
403 * process that token; these are in tok_funcs.
407 * Count the number of valid attributes.
408 * Set up the attrlist array to point to the attributes in attrlistp.
410 nattrs = cpc_count_attrs(cpc);
411 attrlist = (char **)emalloc(nattrs * sizeof (char *));
413 i = 0;
414 cpc_walk_attrs(cpc, &i, attr_walker);
416 ntoks = ncounters + (2 * ncounters) + (nattrs * ncounters) + nattrs + 3;
417 toks = (char **)emalloc(ntoks * sizeof (char *));
418 tok_info = (tok_info_t *)emalloc(ntoks * sizeof (tok_info_t));
420 tok_funcs = (int (**)(int, char *))emalloc(ntoks *
421 sizeof (int (*)(char *)));
423 for (i = 0; i < ntoks; i++) {
424 toks[i] = NULL;
425 tok_funcs[i] = NULL;
428 x = 0;
429 for (i = 0; i < ncounters; i++) {
430 toks[x] = (char *)emalloc(TOK_SIZE);
431 (void) snprintf(toks[x], TOK_SIZE, "pic%d", i);
432 tok_info[x].name = "pic";
433 tok_info[i].picnum = i;
434 tok_funcs[x] = pic;
435 x++;
438 for (i = 0; i < ncounters; i++) {
439 toks[x] = (char *)emalloc(TOK_SIZE);
440 (void) snprintf(toks[x], TOK_SIZE, "nouser%d", i);
441 tok_info[x].name = "nouser";
442 tok_info[x].picnum = i;
443 tok_funcs[x] = flag;
444 x++;
447 for (i = 0; i < ncounters; i++) {
448 toks[x] = (char *)emalloc(TOK_SIZE);
449 (void) snprintf(toks[x], TOK_SIZE, "sys%d", i);
450 tok_info[x].name = "sys";
451 tok_info[x].picnum = i;
452 tok_funcs[x] = flag;
453 x++;
455 for (j = 0; j < nattrs; j++) {
456 toklen = strlen(attrlist[j]) + 3;
457 for (i = 0; i < ncounters; i++) {
458 toks[x] = (char *)emalloc(toklen);
459 (void) snprintf(toks[x], toklen, "%s%d", attrlist[j],
461 tok_info[x].name = attrlist[j];
462 tok_info[x].picnum = i;
463 tok_funcs[x] = doattr;
464 x++;
468 * Now create a token for this attribute with no picnum; if used
469 * it will be applied to all reqs.
471 toks[x] = (char *)emalloc(toklen);
472 (void) snprintf(toks[x], toklen, "%s", attrlist[j]);
473 tok_info[x].name = attrlist[j];
474 tok_info[x].picnum = -1;
475 tok_funcs[x] = doattr;
476 x++;
479 toks[x] = "nouser";
480 tok_info[x].name = "nouser";
481 tok_info[x].picnum = -1;
482 tok_funcs[x] = flag;
483 x++;
485 toks[x] = "sys";
486 tok_info[x].name = "sys";
487 tok_info[x].picnum = -1;
488 tok_funcs[x] = flag;
489 x++;
491 toks[x] = NULL;
493 opts = strdupa(spec);
494 while (*opts != '\0') {
495 int idx = getsubopt(&opts, toks, &val);
497 if (idx == -1) {
498 if (find_event(val) == 0) {
499 strtoset_err(gettext("bad token '%s'\n"), val);
500 goto inval;
501 } else
502 continue;
505 if (tok_funcs[idx](idx, val) == -1)
506 goto inval;
510 * The string has been processed. Now count how many PICs were used,
511 * create a request set, and specify each request properly.
514 if ((set = cpc_set_create(cpc)) == NULL) {
515 strtoset_err(gettext("no memory available\n"));
516 exit(0);
519 for (i = 0; i < ncounters; i++) {
520 if (reqs[i].cr_event[0] == '\0')
521 continue;
524 * If the caller wishes to measure events on the physical CPU,
525 * we need to add SMT attributes to each request.
527 if (smt)
528 smt_special(i);
530 req_attrs = (cpc_attr_t *)emalloc(reqs[i].cr_nattrs *
531 sizeof (cpc_attr_t));
533 j = 0;
534 for (tmp = attrs[i]; tmp != NULL; tmp = tmp->next) {
535 req_attrs[j].ca_name = tmp->name;
536 req_attrs[j].ca_val = tmp->val;
537 j++;
540 if (cpc_set_add_request(cpc, set, reqs[i].cr_event, 0,
541 reqs[i].cr_flags, reqs[i].cr_nattrs, req_attrs) == -1) {
542 free(req_attrs);
543 (void) cpc_set_destroy(cpc, set);
544 strtoset_err(
545 gettext("cpc_set_add_request() failed: %s\n"),
546 strerror(errno));
547 goto inval;
550 free(req_attrs);
553 strtoset_cleanup();
555 return (set);
557 inval:
558 strtoset_cleanup();
559 errno = EINVAL;
560 return (NULL);
563 static void
564 strtoset_cleanup(void)
566 int i;
567 tmp_attr_t *tmp, *p;
569 for (i = 0; i < nattrs; i++)
570 free(attrlist[i]);
571 free(attrlist);
573 for (i = 0; i < ncounters; i++) {
574 for (tmp = attrs[i]; tmp != NULL; tmp = p) {
575 p = tmp->next;
576 free(tmp);
579 free(attrs);
581 for (i = 0; i < ntoks - 3; i++)
583 * We free all but the last three tokens: "nouser", "sys", NULL
585 free(toks[i]);
586 free(toks);
587 free(reqs);
588 free(tok_info);
589 free(tok_funcs);
593 * The following is called to modify requests so that they count events on
594 * behalf of a physical processor, instead of a logical processor. It duplicates
595 * the request flags for the sibling processor (i.e. if the request counts user
596 * events, add an attribute to count user events on the sibling processor also).
598 static void
599 smt_special(int picnum)
601 tmp_attr_t *attr;
603 if (reqs[picnum].cr_flags & CPC_COUNT_USER) {
604 attr = (tmp_attr_t *)emalloc(sizeof (tmp_attr_t));
605 attr->name = "count_sibling_usr";
606 attr->val = 1;
607 attr->next = attrs[picnum];
608 attrs[picnum] = attr;
609 reqs[picnum].cr_nattrs++;
612 if (reqs[picnum].cr_flags & CPC_COUNT_SYSTEM) {
613 attr = (tmp_attr_t *)emalloc(sizeof (tmp_attr_t));
614 attr->name = "count_sibling_sys";
615 attr->val = 1;
616 attr->next = attrs[picnum];
617 attrs[picnum] = attr;
618 reqs[picnum].cr_nattrs++;
623 * If we ever fail to get memory, we print an error message and exit.
625 static void *
626 emalloc(size_t n)
628 void *p = malloc(n);
630 if (p == NULL) {
631 strtoset_err(gettext("no memory available\n"));
632 exit(0);
635 return (p);