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