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