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]
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.
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
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
;
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 */
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 */
88 #ifndef UMEM_STANDALONE
89 static arg_process_t umem_backend_process
;
90 static arg_process_t umem_allocator_process
;
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)",
105 &umem_backend_process
107 { "allocator", "Evolving", ITEM_SPECIAL
,
108 "=best, =first, =next, or =instant",
110 &umem_allocator_process
114 { "concurrency", "Private", ITEM_UINT
,
116 NULL
, 0, &umem_max_ncpus
118 { "max_contention", "Private", ITEM_UINT
,
119 "Maximum contention in a reap interval before the depot is "
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",
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",
140 &umem_size_tempval
, &umem_size_process
142 { "size_remove", "Private", ITEM_SPECIAL
,
143 "remove a size from the cache size table",
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
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",
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 "
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",
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
,
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)",
217 { "mtbf", "Private", ITEM_UINT
,
218 "=mtbf, the mean time between injected failures. Works best "
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",
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.",
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 "
259 &umem_failure_log_size
, &umem_log_process
262 { "slab", "Private", ITEM_SPECIAL
,
263 "Every slab created will be entered into this log.",
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
;
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
},
286 static umem_envvar_t
*env_current
;
287 #define CURRENT (env_current->env_name)
290 empty(const char *str
)
294 while ((c
= *str
) != '\0' && isspace(c
))
297 return (*str
== '\0');
301 item_uint_process(const umem_env_item_t
*item
, const char *item_arg
)
310 if (empty(item_arg
)) {
314 result
= strtoul(item_arg
, &endptr
, 10);
316 if (result
== ULONG_MAX
&& errno
== ERANGE
) {
324 if ((uint_t
)result
!= result
)
327 (*item
->item_uint_target
) = (uint_t
)result
;
328 return (ARG_SUCCESS
);
331 log_message("%s: %s: not a number\n", CURRENT
, item
->item_name
);
335 log_message("%s: %s: overflowed\n", CURRENT
, item
->item_name
);
340 item_size_process(const umem_env_item_t
*item
, const char *item_arg
)
353 result_arg
= strtoul(item_arg
, &endptr
, 10);
355 if (result_arg
== ULONG_MAX
&& errno
== ERANGE
) {
367 if (result
< result_arg
)
373 if (result
< result_arg
)
379 if (result
< result_arg
)
385 if (result
< result_arg
)
387 endptr
++; /* skip over the size character */
390 break; /* handled later */
396 (*item
->item_size_target
) = result
;
397 return (ARG_SUCCESS
);
400 log_message("%s: %s: not a number\n", CURRENT
, item
->item_name
);
404 log_message("%s: %s: overflowed\n", CURRENT
, item
->item_name
);
409 umem_log_process(const umem_env_item_t
*item
, const char *item_arg
)
411 if (item_arg
!= NULL
) {
413 ret
= item_size_process(item
, item_arg
);
414 if (ret
!= ARG_SUCCESS
)
417 if (*item
->item_size_target
== 0)
418 return (ARG_SUCCESS
);
420 *item
->item_size_target
= 64*1024;
423 return (ARG_SUCCESS
);
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);
436 if (strcmp(name
, "size_clear") == 0) {
437 if (item_arg
!= NULL
) {
438 log_message("%s: %s: does not take a value. ignored\n",
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
;
449 log_message("%s: %s: internally unrecognized\n",
450 CURRENT
, name
, name
, name
);
454 if (item_arg
== NULL
) {
455 log_message("%s: %s: requires a value. ignored\n",
460 ret
= item_size_process(item
, item_arg
);
461 if (ret
!= ARG_SUCCESS
)
464 result
= *item
->item_size_target
;
466 return (ARG_SUCCESS
);
469 #ifndef UMEM_STANDALONE
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
)
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
;
485 return (ARG_SUCCESS
);
488 log_message("%s: %s: must be %s=sbrk or %s=mmap\n",
489 CURRENT
, name
, name
, name
);
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
)
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)
513 return (ARG_SUCCESS
);
516 log_message("%s: %s: must be %s=best, %s=next or %s=first\n",
517 CURRENT
, name
, name
, name
, name
);
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
) {
544 switch (item
->item_type
) {
547 if (item_arg
!= NULL
) {
548 log_message("%s: %s: does not take a value. ignored\n",
549 CURRENT
, item
->item_name
);
557 processor
= item_uint_process
;
562 processor
= item_size_process
;
566 processor
= item
->item_special
;
570 log_message("%s: %s: Invalid type. Ignored\n",
571 CURRENT
, item
->item_name
);
575 if (arg_required
&& item_arg
== NULL
) {
576 log_message("%s: %s: Required value missing\n",
577 CURRENT
, item
->item_name
);
581 if (item_arg
!= NULL
|| item
->item_type
== ITEM_SPECIAL
) {
582 if (processor(item
, item_arg
) != ARG_SUCCESS
)
586 if (item
->item_flag_target
) {
587 if (item
->item_type
== ITEM_CLEARFLAG
)
588 (*item
->item_flag_target
) &= ~item
->item_flag_value
;
590 (*item
->item_flag_target
) |= item
->item_flag_value
;
598 #define ENV_SHORT_BYTES 10 /* bytes to print on error */
600 umem_process_value(umem_env_item_t
*item_list
, const char *beg
, const char *end
)
602 char buf
[UMEM_ENV_ITEM_MAX
];
607 while (beg
< end
&& isspace(*beg
))
610 while (beg
< end
&& isspace(*(end
- 1)))
614 log_message("%s: empty option\n", CURRENT
);
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
,
633 (void) strncpy(buf
, beg
, count
);
636 argptr
= strchr(buf
, '=');
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
);
647 log_message("%s: '%s' not recognized\n", CURRENT
, buf
);
652 umem_setup_envvars(int invalid
)
654 umem_envvar_t
*cur_env
;
655 static volatile enum {
662 } state
= STATE_START
;
663 #ifndef UMEM_STANDALONE
670 * One of the calls below invoked malloc() recursively. We
671 * remove any partial results and return.
676 where
= "before getenv(3C) calls -- "
677 "getenv(3C) results ignored.";
680 where
= "during getenv(3C) calls -- "
681 "getenv(3C) results ignored.";
684 where
= "during dlopen(3C) call -- "
685 "_umem_*() results ignored.";
688 where
= "during dlsym(3C) call -- "
689 "_umem_*() results ignored.";
692 where
= "during _umem_*() call -- "
693 "_umem_*() results ignored.";
696 where
= "after dlsym() or _umem_*() calls.";
699 where
= "at unknown point -- "
700 "_umem_*() results ignored.";
704 log_message("recursive allocation %s\n", where
);
706 for (cur_env
= umem_envvars
; cur_env
->env_name
!= NULL
;
708 if (state
== STATE_GETENV
)
709 cur_env
->env_getenv_result
= NULL
;
710 if (state
!= STATE_DONE
)
711 cur_env
->env_func_result
= NULL
;
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
;
733 const char *(*func
)(void);
737 func
= (const char *(*)(void))dlsym(h
,
740 if (state
== STATE_DONE
)
741 break; /* recursed */
746 if (state
== STATE_DONE
)
747 break; /* recursed */
748 cur_env
->env_func_result
= value
;
753 (void) dlerror(); /* snarf dlerror() */
755 #endif /* UMEM_STANDALONE */
761 * Process the environment variables.
764 umem_process_envvars(void)
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
;
775 value
= cur_env
->env_func_result
;
777 /* ignore if missing or empty */
781 for (end
= value
; *end
!= '\0'; value
= next
) {
782 end
= strchr(value
, ',');
784 next
= end
+ 1; /* skip the comma */
786 next
= end
= value
+ strlen(value
);
788 umem_process_value(cur_env
->env_item_list
, value
, end
);