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) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <errno.h>
29 #include <libintl.h>
30
31 #include "libcpc.h"
32
33 /*
34 * Takes a string and converts it to a cpc_set_t.
35 *
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.
40 *
41 * Not until after the string has been processed in its entirety do we
42 * allocate and specify a request set properly.
43 */
44
45 /*
46 * Leave enough room in token strings for picn, nousern, or sysn where n is
47 * picnum.
48 */
49 #define TOK_SIZE 10
50
51 typedef struct __tmp_attr {
52 char *name;
53 uint64_t val;
54 struct __tmp_attr *next;
55 } tmp_attr_t;
56
57 typedef struct __tok_info {
58 char *name;
59 int picnum;
60 } tok_info_t;
61
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;
67
68 static void strtoset_cleanup(void);
69 static void smt_special(int picnum);
70 static void *emalloc(size_t n);
71
72 /*
73 * Clients of cpc_strtoset may set this to specify an error handler during
74 * string parsing.
75 */
76 cpc_errhndlr_t *strtoset_errfn = NULL;
77
78 static request_t *reqs;
79 static int nreqs;
80 static int ncounters;
81
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;
91
92 static void
strtoset_err(const char * fmt,...)93 strtoset_err(const char *fmt, ...)
94 {
95 va_list ap;
96
97 if (strtoset_errfn == NULL)
98 return;
99
100 va_start(ap, fmt);
101 (*strtoset_errfn)("cpc_strtoset", -1, fmt, ap);
102 va_end(ap);
103 }
104
105 /*ARGSUSED*/
106 static void
event_walker(void * arg,uint_t picno,const char * event)107 event_walker(void *arg, uint_t picno, const char *event)
108 {
109 if (strncmp(arg, event, CPC_MAX_EVENT_LEN) == 0)
110 found = 1;
111 }
112
113 static int
event_valid(int picnum,char * event)114 event_valid(int picnum, char *event)
115 {
116 char *end_event;
117 int err;
118
119 found = 0;
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.
134 * Check the second argument of strtol() to ensure invalid events
135 * beginning with number do not go through.
136 */
137 err = errno;
138 errno = 0;
139 (void) strtol(event, &end_event, 0);
140 if ((errno == 0) && (*end_event == '\0')) {
141 /*
142 * Success - this is a valid raw code in hex, decimal, or octal.
143 */
144 errno = err;
145 return (1);
146 }
147
148 errno = err;
149 return (0);
150 }
151
152 /*
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.
160 */
161 static int
find_event(char * event)162 find_event(char *event)
163 {
164 int i;
165
166 /*
167 * Event names cannot have '=' in them. If present here, it means we
168 * have encountered an unknown token (foo=bar, for example).
169 */
170 if (strchr(event, '=') != NULL)
171 return (0);
172
173 /*
174 * Find the first unavailable pic, after which we must start our search.
175 */
176 for (i = ncounters - 1; i >= 0; i--) {
177 if (reqs[i].cr_event[0] != '\0')
178 break;
179 }
180 /*
181 * If the last counter has been assigned, we cannot place this event.
182 */
183 if (i == ncounters - 1)
184 return (0);
185
186 /*
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.
190 */
191 i++;
192
193 for (; i < ncounters; i++) {
194 if (event_valid(i, event) == 0)
195 continue;
196
197 nreqs++;
198 (void) strncpy(reqs[i].cr_event, event, CPC_MAX_EVENT_LEN);
199 return (1);
200 }
201
202 return (0);
203 }
204
205 static int
pic(int tok,char * val)206 pic(int tok, char *val)
207 {
208 int picnum = tok_info[tok].picnum;
209 /*
210 * Make sure the each pic only appears in the spec once.
211 */
212 if (reqs[picnum].cr_event[0] != '\0') {
213 strtoset_err(gettext("repeated 'pic%d' token\n"), picnum);
214 return (-1);
215 }
216
217 if (val == NULL || val[0] == '\0') {
218 strtoset_err(gettext("missing 'pic%d' value\n"), picnum);
219 return (-1);
220 }
221
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);
226 }
227
228 nreqs++;
229 (void) strncpy(reqs[picnum].cr_event, val, CPC_MAX_EVENT_LEN);
230 return (0);
231 }
232
233 /*
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.
236 */
237 /*ARGSUSED*/
238 static int
flag(int tok,char * val)239 flag(int tok, char *val)
240 {
241 int i;
242 int picnum = tok_info[tok].picnum;
243
244 /*
245 * If picnum is -1, this flag should be applied to all reqs.
246 */
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);
254
255 if (picnum != -1)
256 break;
257 }
258
259 return (0);
260 }
261
262 static int
doattr(int tok,char * val)263 doattr(int tok, char *val)
264 {
265 int i;
266 int picnum = tok_info[tok].picnum;
267 tmp_attr_t *tmp;
268 char *endptr;
269
270 /*
271 * If picnum is -1, this attribute should be applied to all reqs.
272 */
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);
283 }
284 } else
285 /*
286 * No value was provided for this attribute,
287 * so specify a default value of 1.
288 */
289 tmp->val = 1;
290
291 tmp->next = attrs[i];
292 attrs[i] = tmp;
293 reqs[i].cr_nattrs++;
294
295 if (picnum != -1)
296 break;
297 }
298
299 return (0);
300 }
301
302 /*ARGSUSED*/
303 static void
attr_count_walker(void * arg,const char * attr)304 attr_count_walker(void *arg, const char *attr)
305 {
306 /*
307 * We don't allow picnum to be specified by the user.
308 */
309 if (strncmp(attr, "picnum", 7) == 0)
310 return;
311 (*(int *)arg)++;
312 }
313
314 static int
cpc_count_attrs(cpc_t * cpc)315 cpc_count_attrs(cpc_t *cpc)
316 {
317 int nattrs = 0;
318
319 cpc_walk_attrs(cpc, &nattrs, attr_count_walker);
320
321 return (nattrs);
322 }
323
324 static void
attr_walker(void * arg,const char * attr)325 attr_walker(void *arg, const char *attr)
326 {
327 int *i = arg;
328
329 if (strncmp(attr, "picnum", 7) == 0)
330 return;
331
332 if ((attrlist[(*i)++] = strdup(attr)) == NULL) {
333 strtoset_err(gettext("no memory available\n"));
334 exit(0);
335 }
336 }
337
338 cpc_set_t *
cpc_strtoset(cpc_t * cpcin,const char * spec,int smt)339 cpc_strtoset(cpc_t *cpcin, const char *spec, int smt)
340 {
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;
350
351 cpc = cpcin;
352 nattrs = 0;
353
354 ncounters = cpc_npic(cpc);
355
356 reqs = (request_t *)emalloc(ncounters * sizeof (request_t));
357
358 attrs = (tmp_attr_t **)emalloc(ncounters * sizeof (tmp_attr_t *));
359
360 for (i = 0; i < ncounters; i++) {
361 reqs[i].cr_event[0] = '\0';
362 reqs[i].cr_flags = CPC_COUNT_USER;
363 /*
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.
367 */
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;
373 }
374
375 /*
376 * Build up a list of acceptable tokens.
377 *
378 * Permitted tokens are
379 * picn=event
380 * nousern
381 * sysn
382 * attrn=val
383 * nouser
384 * sys
385 * attr=val
386 *
387 * Where n is a counter number, and attr is any attribute supported by
388 * the current processor.
389 *
390 * If a token appears without a counter number, it applies to all
391 * counters in the request set.
392 *
393 * The number of tokens is:
394 *
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).
401 *
402 * Matching each token's index in the token table is a function which
403 * process that token; these are in tok_funcs.
404 */
405
406 /*
407 * Count the number of valid attributes.
408 * Set up the attrlist array to point to the attributes in attrlistp.
409 */
410 nattrs = cpc_count_attrs(cpc);
411 attrlist = (char **)emalloc(nattrs * sizeof (char *));
412
413 i = 0;
414 cpc_walk_attrs(cpc, &i, attr_walker);
415
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));
419
420 tok_funcs = (int (**)(int, char *))emalloc(ntoks *
421 sizeof (int (*)(char *)));
422
423 for (i = 0; i < ntoks; i++) {
424 toks[i] = NULL;
425 tok_funcs[i] = NULL;
426 }
427
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++;
436 }
437
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++;
445 }
446
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++;
454 }
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],
460 i);
461 tok_info[x].name = attrlist[j];
462 tok_info[x].picnum = i;
463 tok_funcs[x] = doattr;
464 x++;
465 }
466
467 /*
468 * Now create a token for this attribute with no picnum; if used
469 * it will be applied to all reqs.
470 */
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++;
477 }
478
479 toks[x] = "nouser";
480 tok_info[x].name = "nouser";
481 tok_info[x].picnum = -1;
482 tok_funcs[x] = flag;
483 x++;
484
485 toks[x] = "sys";
486 tok_info[x].name = "sys";
487 tok_info[x].picnum = -1;
488 tok_funcs[x] = flag;
489 x++;
490
491 toks[x] = NULL;
492
493 opts = strdupa(spec);
494 while (*opts != '\0') {
495 int idx = getsubopt(&opts, toks, &val);
496
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;
503 }
504
505 if (tok_funcs[idx](idx, val) == -1)
506 goto inval;
507 }
508
509 /*
510 * The string has been processed. Now count how many PICs were used,
511 * create a request set, and specify each request properly.
512 */
513
514 if ((set = cpc_set_create(cpc)) == NULL) {
515 strtoset_err(gettext("no memory available\n"));
516 exit(0);
517 }
518
519 for (i = 0; i < ncounters; i++) {
520 if (reqs[i].cr_event[0] == '\0')
521 continue;
522
523 /*
524 * If the caller wishes to measure events on the physical CPU,
525 * we need to add SMT attributes to each request.
526 */
527 if (smt)
528 smt_special(i);
529
530 req_attrs = (cpc_attr_t *)emalloc(reqs[i].cr_nattrs *
531 sizeof (cpc_attr_t));
532
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++;
538 }
539
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;
548 }
549
550 free(req_attrs);
551 }
552
553 strtoset_cleanup();
554
555 return (set);
556
557 inval:
558 strtoset_cleanup();
559 errno = EINVAL;
560 return (NULL);
561 }
562
563 static void
strtoset_cleanup(void)564 strtoset_cleanup(void)
565 {
566 int i;
567 tmp_attr_t *tmp, *p;
568
569 for (i = 0; i < nattrs; i++)
570 free(attrlist[i]);
571 free(attrlist);
572
573 for (i = 0; i < ncounters; i++) {
574 for (tmp = attrs[i]; tmp != NULL; tmp = p) {
575 p = tmp->next;
576 free(tmp);
577 }
578 }
579 free(attrs);
580
581 for (i = 0; i < ntoks - 3; i++)
582 /*
583 * We free all but the last three tokens: "nouser", "sys", NULL
584 */
585 free(toks[i]);
586 free(toks);
587 free(reqs);
588 free(tok_info);
589 free(tok_funcs);
590 }
591
592 /*
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).
597 */
598 static void
smt_special(int picnum)599 smt_special(int picnum)
600 {
601 tmp_attr_t *attr;
602
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++;
610 }
611
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++;
619 }
620 }
621
622 /*
623 * If we ever fail to get memory, we print an error message and exit.
624 */
625 static void *
emalloc(size_t n)626 emalloc(size_t n)
627 {
628 void *p = malloc(n);
629
630 if (p == NULL) {
631 strtoset_err(gettext("no memory available\n"));
632 exit(0);
633 }
634
635 return (p);
636 }
637