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