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