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 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 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 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 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 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 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 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 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 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 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 * 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 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 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 * 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