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 2005 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 <ctype.h> 30 #include <errno.h> 31 #include <limits.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <dlfcn.h> 35 36 #include "umem_base.h" 37 #include "vmem_base.h" 38 39 /* 40 * A umem environment variable, like UMEM_DEBUG, is set to a series 41 * of items, seperated by ',': 42 * 43 * UMEM_DEBUG="audit=10,guards,firewall=512" 44 * 45 * This structure describes items. Each item has a name, type, and 46 * description. During processing, an item read from the user may 47 * be either "valid" or "invalid". 48 * 49 * A valid item has an argument, if required, and it is of the right 50 * form (doesn't overflow, doesn't contain any unexpected characters). 51 * 52 * If the item is valid, item_flag_target != NULL, and: 53 * type is not CLEARFLAG, then (*item_flag_target) |= item_flag_value 54 * type is CLEARFLAG, then (*item_flag_target) &= ~item_flag_value 55 */ 56 57 #define UMEM_ENV_ITEM_MAX 512 58 59 struct umem_env_item; 60 61 typedef int arg_process_t(const struct umem_env_item *item, const char *value); 62 #define ARG_SUCCESS 0 /* processing successful */ 63 #define ARG_BAD 1 /* argument had a bad value */ 64 65 typedef struct umem_env_item { 66 const char *item_name; /* tag in environment variable */ 67 const char *item_interface_stability; 68 enum { 69 ITEM_INVALID, 70 ITEM_FLAG, /* only a flag. No argument allowed */ 71 ITEM_CLEARFLAG, /* only a flag, but clear instead of set */ 72 ITEM_OPTUINT, /* optional integer argument */ 73 ITEM_UINT, /* required integer argument */ 74 ITEM_OPTSIZE, /* optional size_t argument */ 75 ITEM_SIZE, /* required size_t argument */ 76 ITEM_SPECIAL /* special argument processing */ 77 } item_type; 78 const char *item_description; 79 uint_t *item_flag_target; /* the variable containing the flag */ 80 uint_t item_flag_value; /* the value to OR in */ 81 uint_t *item_uint_target; /* the variable to hold the integer */ 82 size_t *item_size_target; 83 arg_process_t *item_special; /* callback for special handling */ 84 } umem_env_item_t; 85 86 #ifndef UMEM_STANDALONE 87 static arg_process_t umem_backend_process; 88 #endif 89 90 static arg_process_t umem_log_process; 91 92 const char *____umem_environ_msg_options = "-- UMEM_OPTIONS --"; 93 94 static umem_env_item_t umem_options_items[] = { 95 #ifndef UMEM_STANDALONE 96 { "backend", "Evolving", ITEM_SPECIAL, 97 "=sbrk for sbrk(2), =mmap for mmap(2)", 98 NULL, 0, NULL, NULL, 99 &umem_backend_process 100 }, 101 #endif 102 103 { "concurrency", "Private", ITEM_UINT, 104 "Max concurrency", 105 NULL, 0, &umem_max_ncpus 106 }, 107 { "max_contention", "Private", ITEM_UINT, 108 "Maximum contention in a reap interval before the depot is " 109 "resized.", 110 NULL, 0, &umem_depot_contention 111 }, 112 { "nomagazines", "Private", ITEM_FLAG, 113 "no caches will be multithreaded, and no caching will occur.", 114 &umem_flags, UMF_NOMAGAZINE 115 }, 116 { "reap_interval", "Private", ITEM_UINT, 117 "Minimum time between reaps and updates, in seconds.", 118 NULL, 0, &umem_reap_interval 119 }, 120 121 #ifndef UMEM_STANDALONE 122 { "sbrk_pagesize", "Private", ITEM_SIZE, 123 "The preferred page size for the sbrk(2) heap.", 124 NULL, 0, NULL, &vmem_sbrk_pagesize 125 }, 126 #endif 127 128 { NULL, "-- end of UMEM_OPTIONS --", ITEM_INVALID } 129 }; 130 131 const char *____umem_environ_msg_debug = "-- UMEM_DEBUG --"; 132 133 static umem_env_item_t umem_debug_items[] = { 134 { "default", "Unstable", ITEM_FLAG, 135 "audit,contents,guards", 136 &umem_flags, 137 UMF_AUDIT | UMF_CONTENTS | UMF_DEADBEEF | UMF_REDZONE 138 }, 139 { "audit", "Unstable", ITEM_OPTUINT, 140 "Enable auditing. optionally =frames to set the number of " 141 "stored stack frames", 142 &umem_flags, UMF_AUDIT, &umem_stack_depth 143 }, 144 { "contents", "Unstable", ITEM_OPTSIZE, 145 "Enable contents storing. UMEM_LOGGING=contents also " 146 "required. optionally =bytes to set the number of stored " 147 "bytes", 148 &umem_flags, UMF_CONTENTS, NULL, &umem_content_maxsave 149 }, 150 { "guards", "Unstable", ITEM_FLAG, 151 "Enables guards and special patterns", 152 &umem_flags, UMF_DEADBEEF | UMF_REDZONE 153 }, 154 { "verbose", "Unstable", ITEM_FLAG, 155 "Enables writing error messages to stderr", 156 &umem_output, 1 157 }, 158 159 { "nosignal", "Private", ITEM_FLAG, 160 "Abort if called from a signal handler. Turns on 'audit'. " 161 "Note that this is not always a bug.", 162 &umem_flags, UMF_AUDIT | UMF_CHECKSIGNAL 163 }, 164 { "firewall", "Private", ITEM_SIZE, 165 "=minbytes. Every object >= minbytes in size will have its " 166 "end against an unmapped page", 167 &umem_flags, UMF_FIREWALL, NULL, &umem_minfirewall 168 }, 169 { "lite", "Private", ITEM_FLAG, 170 "debugging-lite", 171 &umem_flags, UMF_LITE 172 }, 173 { "maxverify", "Private", ITEM_SIZE, 174 "=maxbytes, Maximum bytes to check when 'guards' is active. " 175 "Normally all bytes are checked.", 176 NULL, 0, NULL, &umem_maxverify 177 }, 178 { "noabort", "Private", ITEM_CLEARFLAG, 179 "umem will not abort when a recoverable error occurs " 180 "(i.e. double frees, certain kinds of corruption)", 181 &umem_abort, 1 182 }, 183 { "mtbf", "Private", ITEM_UINT, 184 "=mtbf, the mean time between injected failures. Works best " 185 "if prime.\n", 186 NULL, 0, &umem_mtbf 187 }, 188 { "random", "Private", ITEM_FLAG, 189 "randomize flags on a per-cache basis", 190 &umem_flags, UMF_RANDOMIZE 191 }, 192 { "allverbose", "Private", ITEM_FLAG, 193 "Enables writing all logged messages to stderr", 194 &umem_output, 2 195 }, 196 197 { NULL, "-- end of UMEM_DEBUG --", ITEM_INVALID } 198 }; 199 200 const char *____umem_environ_msg_logging = "-- UMEM_LOGGING --"; 201 202 static umem_env_item_t umem_logging_items[] = { 203 { "transaction", "Unstable", ITEM_SPECIAL, 204 "If 'audit' is set in UMEM_DEBUG, the audit structures " 205 "from previous transactions are entered into this log.", 206 NULL, 0, NULL, 207 &umem_transaction_log_size, &umem_log_process 208 }, 209 { "contents", "Unstable", ITEM_SPECIAL, 210 "If 'audit' is set in UMEM_DEBUG, the contents of objects " 211 "are recorded in this log as they are freed. If the " 212 "'contents' option is not set in UMEM_DEBUG, the first " 213 "256 bytes of each freed buffer will be saved.", 214 &umem_flags, UMF_CONTENTS, NULL, 215 &umem_content_log_size, &umem_log_process 216 }, 217 { "fail", "Unstable", ITEM_SPECIAL, 218 "Records are entered into this log for every failed " 219 "allocation.", 220 NULL, 0, NULL, 221 &umem_failure_log_size, &umem_log_process 222 }, 223 224 { "slab", "Private", ITEM_SPECIAL, 225 "Every slab created will be entered into this log.", 226 NULL, 0, NULL, 227 &umem_slab_log_size, &umem_log_process 228 }, 229 230 { NULL, "-- end of UMEM_LOGGING --", ITEM_INVALID } 231 }; 232 233 typedef struct umem_envvar { 234 const char *env_name; 235 const char *env_func; 236 umem_env_item_t *env_item_list; 237 const char *env_getenv_result; 238 const char *env_func_result; 239 } umem_envvar_t; 240 241 static umem_envvar_t umem_envvars[] = { 242 { "UMEM_DEBUG", "_umem_debug_init", umem_debug_items }, 243 { "UMEM_OPTIONS", "_umem_options_init", umem_options_items }, 244 { "UMEM_LOGGING", "_umem_logging_init", umem_logging_items }, 245 { NULL, NULL, NULL } 246 }; 247 248 static umem_envvar_t *env_current; 249 #define CURRENT (env_current->env_name) 250 251 static int 252 empty(const char *str) 253 { 254 char c; 255 256 while ((c = *str) != '\0' && isspace(c)) 257 str++; 258 259 return (*str == '\0'); 260 } 261 262 static int 263 item_uint_process(const umem_env_item_t *item, const char *item_arg) 264 { 265 ulong_t result; 266 char *endptr = ""; 267 int olderrno; 268 269 olderrno = errno; 270 errno = 0; 271 272 if (empty(item_arg)) { 273 goto badnumber; 274 } 275 276 result = strtoul(item_arg, &endptr, 10); 277 278 if (result == ULONG_MAX && errno == ERANGE) { 279 errno = olderrno; 280 goto overflow; 281 } 282 errno = olderrno; 283 284 if (*endptr != '\0') 285 goto badnumber; 286 if ((uint_t)result != result) 287 goto overflow; 288 289 (*item->item_uint_target) = (uint_t)result; 290 return (ARG_SUCCESS); 291 292 badnumber: 293 log_message("%s: %s: not a number\n", CURRENT, item->item_name); 294 return (ARG_BAD); 295 296 overflow: 297 log_message("%s: %s: overflowed\n", CURRENT, item->item_name); 298 return (ARG_BAD); 299 } 300 301 static int 302 item_size_process(const umem_env_item_t *item, const char *item_arg) 303 { 304 ulong_t result; 305 ulong_t result_arg; 306 char *endptr = ""; 307 int olderrno; 308 309 if (empty(item_arg)) 310 goto badnumber; 311 312 olderrno = errno; 313 errno = 0; 314 315 result_arg = strtoul(item_arg, &endptr, 10); 316 317 if (result_arg == ULONG_MAX && errno == ERANGE) { 318 errno = olderrno; 319 goto overflow; 320 } 321 errno = olderrno; 322 323 result = result_arg; 324 325 switch (*endptr) { 326 case 't': 327 case 'T': 328 result *= 1024; 329 if (result < result_arg) 330 goto overflow; 331 /*FALLTHRU*/ 332 case 'g': 333 case 'G': 334 result *= 1024; 335 if (result < result_arg) 336 goto overflow; 337 /*FALLTHRU*/ 338 case 'm': 339 case 'M': 340 result *= 1024; 341 if (result < result_arg) 342 goto overflow; 343 /*FALLTHRU*/ 344 case 'k': 345 case 'K': 346 result *= 1024; 347 if (result < result_arg) 348 goto overflow; 349 endptr++; /* skip over the size character */ 350 break; 351 default: 352 break; /* handled later */ 353 } 354 355 if (*endptr != '\0') 356 goto badnumber; 357 358 (*item->item_size_target) = result; 359 return (ARG_SUCCESS); 360 361 badnumber: 362 log_message("%s: %s: not a number\n", CURRENT, item->item_name); 363 return (ARG_BAD); 364 365 overflow: 366 log_message("%s: %s: overflowed\n", CURRENT, item->item_name); 367 return (ARG_BAD); 368 } 369 370 static int 371 umem_log_process(const umem_env_item_t *item, const char *item_arg) 372 { 373 if (item_arg != NULL) { 374 int ret; 375 ret = item_size_process(item, item_arg); 376 if (ret != ARG_SUCCESS) 377 return (ret); 378 379 if (*item->item_size_target == 0) 380 return (ARG_SUCCESS); 381 } else 382 *item->item_size_target = 64*1024; 383 384 umem_logging = 1; 385 return (ARG_SUCCESS); 386 } 387 388 #ifndef UMEM_STANDALONE 389 static int 390 umem_backend_process(const umem_env_item_t *item, const char *item_arg) 391 { 392 const char *name = item->item_name; 393 394 if (item_arg == NULL) 395 goto fail; 396 397 if (strcmp(item_arg, "sbrk") == 0) 398 vmem_backend |= VMEM_BACKEND_SBRK; 399 else if (strcmp(item_arg, "mmap") == 0) 400 vmem_backend |= VMEM_BACKEND_MMAP; 401 else 402 goto fail; 403 404 return (ARG_SUCCESS); 405 406 fail: 407 log_message("%s: %s: must be %s=sbrk or %s=mmap\n", 408 CURRENT, name, name, name); 409 return (ARG_BAD); 410 } 411 #endif 412 413 static int 414 process_item(const umem_env_item_t *item, const char *item_arg) 415 { 416 int arg_required = 0; 417 arg_process_t *processor; 418 419 switch (item->item_type) { 420 case ITEM_FLAG: 421 case ITEM_CLEARFLAG: 422 case ITEM_OPTUINT: 423 case ITEM_OPTSIZE: 424 case ITEM_SPECIAL: 425 arg_required = 0; 426 break; 427 428 case ITEM_UINT: 429 case ITEM_SIZE: 430 arg_required = 1; 431 break; 432 } 433 434 switch (item->item_type) { 435 case ITEM_FLAG: 436 case ITEM_CLEARFLAG: 437 if (item_arg != NULL) { 438 log_message("%s: %s: does not take a value. ignored\n", 439 CURRENT, item->item_name); 440 return (1); 441 } 442 processor = NULL; 443 break; 444 445 case ITEM_UINT: 446 case ITEM_OPTUINT: 447 processor = item_uint_process; 448 break; 449 450 case ITEM_SIZE: 451 case ITEM_OPTSIZE: 452 processor = item_size_process; 453 break; 454 455 case ITEM_SPECIAL: 456 processor = item->item_special; 457 break; 458 459 default: 460 log_message("%s: %s: Invalid type. Ignored\n", 461 CURRENT, item->item_name); 462 return (1); 463 } 464 465 if (arg_required && item_arg == NULL) { 466 log_message("%s: %s: Required value missing\n", 467 CURRENT, item->item_name); 468 goto invalid; 469 } 470 471 if (item_arg != NULL || item->item_type == ITEM_SPECIAL) { 472 if (processor(item, item_arg) != ARG_SUCCESS) 473 goto invalid; 474 } 475 476 if (item->item_flag_target) { 477 if (item->item_type == ITEM_CLEARFLAG) 478 (*item->item_flag_target) &= ~item->item_flag_value; 479 else 480 (*item->item_flag_target) |= item->item_flag_value; 481 } 482 return (0); 483 484 invalid: 485 return (1); 486 } 487 488 #define ENV_SHORT_BYTES 10 /* bytes to print on error */ 489 void 490 umem_process_value(umem_env_item_t *item_list, const char *beg, const char *end) 491 { 492 char buf[UMEM_ENV_ITEM_MAX]; 493 char *argptr; 494 495 size_t count; 496 497 while (beg < end && isspace(*beg)) 498 beg++; 499 500 while (beg < end && isspace(*(end - 1))) 501 end--; 502 503 if (beg >= end) { 504 log_message("%s: empty option\n", CURRENT); 505 return; 506 } 507 508 count = end - beg; 509 510 if (count + 1 > sizeof (buf)) { 511 char outbuf[ENV_SHORT_BYTES + 1]; 512 /* 513 * Have to do this, since sprintf("%10s",...) calls malloc() 514 */ 515 (void) strncpy(outbuf, beg, ENV_SHORT_BYTES); 516 outbuf[ENV_SHORT_BYTES] = 0; 517 518 log_message("%s: argument \"%s...\" too long\n", CURRENT, 519 outbuf); 520 return; 521 } 522 523 (void) strncpy(buf, beg, count); 524 buf[count] = 0; 525 526 argptr = strchr(buf, '='); 527 528 if (argptr != NULL) 529 *argptr++ = 0; 530 531 for (; item_list->item_name != NULL; item_list++) { 532 if (strcmp(buf, item_list->item_name) == 0) { 533 (void) process_item(item_list, argptr); 534 return; 535 } 536 } 537 log_message("%s: '%s' not recognized\n", CURRENT, buf); 538 } 539 540 /*ARGSUSED*/ 541 void 542 umem_setup_envvars(int invalid) 543 { 544 umem_envvar_t *cur_env; 545 static volatile enum { 546 STATE_START, 547 STATE_GETENV, 548 STATE_DLOPEN, 549 STATE_DLSYM, 550 STATE_FUNC, 551 STATE_DONE 552 } state = STATE_START; 553 #ifndef UMEM_STANDALONE 554 void *h; 555 #endif 556 557 if (invalid) { 558 const char *where; 559 /* 560 * One of the calls below invoked malloc() recursively. We 561 * remove any partial results and return. 562 */ 563 564 switch (state) { 565 case STATE_START: 566 where = "before getenv(3C) calls -- " 567 "getenv(3C) results ignored."; 568 break; 569 case STATE_GETENV: 570 where = "during getenv(3C) calls -- " 571 "getenv(3C) results ignored."; 572 break; 573 case STATE_DLOPEN: 574 where = "during dlopen(3C) call -- " 575 "_umem_*() results ignored."; 576 break; 577 case STATE_DLSYM: 578 where = "during dlsym(3C) call -- " 579 "_umem_*() results ignored."; 580 break; 581 case STATE_FUNC: 582 where = "during _umem_*() call -- " 583 "_umem_*() results ignored."; 584 break; 585 case STATE_DONE: 586 where = "after dlsym() or _umem_*() calls."; 587 break; 588 default: 589 where = "at unknown point -- " 590 "_umem_*() results ignored."; 591 break; 592 } 593 594 log_message("recursive allocation %s\n", where); 595 596 for (cur_env = umem_envvars; cur_env->env_name != NULL; 597 cur_env++) { 598 if (state == STATE_GETENV) 599 cur_env->env_getenv_result = NULL; 600 if (state != STATE_DONE) 601 cur_env->env_func_result = NULL; 602 } 603 604 state = STATE_DONE; 605 return; 606 } 607 608 state = STATE_GETENV; 609 610 for (cur_env = umem_envvars; cur_env->env_name != NULL; cur_env++) { 611 cur_env->env_getenv_result = getenv(cur_env->env_name); 612 if (state == STATE_DONE) 613 return; /* recursed */ 614 } 615 616 #ifndef UMEM_STANDALONE 617 state = STATE_DLOPEN; 618 619 /* get a handle to the "a.out" object */ 620 if ((h = dlopen(0, RTLD_FIRST | RTLD_LAZY)) != NULL) { 621 for (cur_env = umem_envvars; cur_env->env_name != NULL; 622 cur_env++) { 623 const char *(*func)(void); 624 const char *value; 625 626 state = STATE_DLSYM; 627 func = (const char *(*)(void))dlsym(h, 628 cur_env->env_func); 629 630 if (state == STATE_DONE) 631 break; /* recursed */ 632 633 state = STATE_FUNC; 634 if (func != NULL) { 635 value = func(); 636 if (state == STATE_DONE) 637 break; /* recursed */ 638 cur_env->env_func_result = value; 639 } 640 } 641 (void) dlclose(h); 642 } else { 643 (void) dlerror(); /* snarf dlerror() */ 644 } 645 #endif /* UMEM_STANDALONE */ 646 647 state = STATE_DONE; 648 } 649 650 /* 651 * Process the environment variables. 652 */ 653 void 654 umem_process_envvars(void) 655 { 656 const char *value; 657 const char *end, *next; 658 umem_envvar_t *cur_env; 659 660 for (cur_env = umem_envvars; cur_env->env_name != NULL; cur_env++) { 661 env_current = cur_env; 662 663 value = cur_env->env_getenv_result; 664 if (value == NULL) 665 value = cur_env->env_func_result; 666 667 /* ignore if missing or empty */ 668 if (value == NULL) 669 continue; 670 671 for (end = value; *end != '\0'; value = next) { 672 end = strchr(value, ','); 673 if (end != NULL) 674 next = end + 1; /* skip the comma */ 675 else 676 next = end = value + strlen(value); 677 678 umem_process_value(cur_env->env_item_list, value, end); 679 } 680 } 681 } 682