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