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