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 <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_DLSYM, 549 STATE_FUNC, 550 STATE_DONE 551 } state = STATE_START; 552 #ifndef UMEM_STANDALONE 553 void *h; 554 #endif 555 556 if (invalid) { 557 const char *where; 558 /* 559 * One of the calls below invoked malloc() recursively. We 560 * remove any partial results and return. 561 */ 562 563 switch (state) { 564 case STATE_START: 565 where = "before getenv(3C) calls -- " 566 "getenv(3C) results ignored."; 567 break; 568 case STATE_GETENV: 569 where = "during getenv(3C) calls -- " 570 "getenv(3C) results ignored."; 571 break; 572 case STATE_DLSYM: 573 where = "during dlsym(3C) call -- " 574 "_umem_*() results ignored."; 575 break; 576 case STATE_FUNC: 577 where = "during _umem_*() call -- " 578 "_umem_*() results ignored."; 579 break; 580 case STATE_DONE: 581 where = "after dlsym() or _umem_*() calls."; 582 break; 583 default: 584 where = "at unknown point -- " 585 "_umem_*() results ignored."; 586 break; 587 } 588 589 log_message("recursive allocation %s\n", where); 590 591 for (cur_env = umem_envvars; cur_env->env_name != NULL; 592 cur_env++) { 593 if (state == STATE_GETENV) 594 cur_env->env_getenv_result = NULL; 595 if (state != STATE_DONE) 596 cur_env->env_func_result = NULL; 597 } 598 599 state = STATE_DONE; 600 return; 601 } 602 603 state = STATE_GETENV; 604 605 for (cur_env = umem_envvars; cur_env->env_name != NULL; cur_env++) { 606 cur_env->env_getenv_result = getenv(cur_env->env_name); 607 if (state == STATE_DONE) 608 return; /* recursed */ 609 } 610 611 #ifndef UMEM_STANDALONE 612 /* get a handle to the "a.out" object */ 613 if ((h = dlopen(0, RTLD_FIRST | RTLD_LAZY)) != NULL) { 614 for (cur_env = umem_envvars; cur_env->env_name != NULL; 615 cur_env++) { 616 const char *(*func)(void); 617 const char *value; 618 619 state = STATE_DLSYM; 620 func = (const char *(*)(void))dlsym(h, 621 cur_env->env_func); 622 623 if (state == STATE_DONE) 624 break; /* recursed */ 625 626 state = STATE_FUNC; 627 if (func != NULL) { 628 value = func(); 629 if (state == STATE_DONE) 630 break; /* recursed */ 631 cur_env->env_func_result = value; 632 } 633 } 634 (void) dlclose(h); 635 } else { 636 (void) dlerror(); /* snarf dlerror() */ 637 } 638 #endif /* UMEM_STANDALONE */ 639 640 state = STATE_DONE; 641 } 642 643 /* 644 * Process the environment variables. 645 */ 646 void 647 umem_process_envvars(void) 648 { 649 const char *value; 650 const char *end, *next; 651 umem_envvar_t *cur_env; 652 653 for (cur_env = umem_envvars; cur_env->env_name != NULL; cur_env++) { 654 env_current = cur_env; 655 656 value = cur_env->env_getenv_result; 657 if (value == NULL) 658 value = cur_env->env_func_result; 659 660 /* ignore if missing or empty */ 661 if (value == NULL) 662 continue; 663 664 for (end = value; *end != '\0'; value = next) { 665 end = strchr(value, ','); 666 if (end != NULL) 667 next = end + 1; /* skip the comma */ 668 else 669 next = end = value + strlen(value); 670 671 umem_process_value(cur_env->env_item_list, value, end); 672 } 673 } 674 } 675