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