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