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