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.
27 #pragma ident "%Z%%M% %I% %E% SMI"
35 #include "umem_base.h"
36 #include "vmem_base.h"
39 * A umem environment variable, like UMEM_DEBUG, is set to a series
40 * of items, seperated by ',':
42 * UMEM_DEBUG="audit=10,guards,firewall=512"
44 * This structure describes items. Each item has a name, type, and
45 * description. During processing, an item read from the user may
46 * be either "valid" or "invalid".
48 * A valid item has an argument, if required, and it is of the right
49 * form (doesn't overflow, doesn't contain any unexpected characters).
51 * If the item is valid, item_flag_target != NULL, and:
52 * type is not CLEARFLAG, then (*item_flag_target) |= item_flag_value
53 * type is CLEARFLAG, then (*item_flag_target) &= ~item_flag_value
56 #define UMEM_ENV_ITEM_MAX 512
60 typedef int arg_process_t(const struct umem_env_item
*item
, const char *value
);
61 #define ARG_SUCCESS 0 /* processing successful */
62 #define ARG_BAD 1 /* argument had a bad value */
64 typedef struct umem_env_item
{
65 const char *item_name
; /* tag in environment variable */
66 const char *item_interface_stability
;
69 ITEM_FLAG
, /* only a flag. No argument allowed */
70 ITEM_CLEARFLAG
, /* only a flag, but clear instead of set */
71 ITEM_OPTUINT
, /* optional integer argument */
72 ITEM_UINT
, /* required integer argument */
73 ITEM_OPTSIZE
, /* optional size_t argument */
74 ITEM_SIZE
, /* required size_t argument */
75 ITEM_SPECIAL
/* special argument processing */
77 const char *item_description
;
78 uint_t
*item_flag_target
; /* the variable containing the flag */
79 uint_t item_flag_value
; /* the value to OR in */
80 uint_t
*item_uint_target
; /* the variable to hold the integer */
81 size_t *item_size_target
;
82 arg_process_t
*item_special
; /* callback for special handling */
85 #ifndef UMEM_STANDALONE
86 static arg_process_t umem_backend_process
;
89 static arg_process_t umem_log_process
;
91 static size_t umem_size_tempval
;
92 static arg_process_t umem_size_process
;
94 const char *____umem_environ_msg_options
= "-- UMEM_OPTIONS --";
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)",
101 &umem_backend_process
105 { "concurrency", "Private", ITEM_UINT
,
107 NULL
, 0, &umem_max_ncpus
109 { "max_contention", "Private", ITEM_UINT
,
110 "Maximum contention in a reap interval before the depot is "
112 NULL
, 0, &umem_depot_contention
114 { "nomagazines", "Private", ITEM_FLAG
,
115 "no caches will be multithreaded, and no caching will occur.",
116 &umem_flags
, UMF_NOMAGAZINE
118 { "reap_interval", "Private", ITEM_UINT
,
119 "Minimum time between reaps and updates, in seconds.",
120 NULL
, 0, &umem_reap_interval
123 { "size_add", "Private", ITEM_SPECIAL
,
124 "add a size to the cache size table",
126 &umem_size_tempval
, &umem_size_process
128 { "size_clear", "Private", ITEM_SPECIAL
,
129 "clear all but the largest size from the cache size table",
131 &umem_size_tempval
, &umem_size_process
133 { "size_remove", "Private", ITEM_SPECIAL
,
134 "remove a size from the cache size table",
136 &umem_size_tempval
, &umem_size_process
139 #ifndef UMEM_STANDALONE
140 { "sbrk_minalloc", "Private", ITEM_SIZE
,
141 "The minimum allocation chunk for the sbrk(2) heap.",
142 NULL
, 0, NULL
, &vmem_sbrk_minalloc
144 { "sbrk_pagesize", "Private", ITEM_SIZE
,
145 "The preferred page size for the sbrk(2) heap.",
146 NULL
, 0, NULL
, &vmem_sbrk_pagesize
150 { NULL
, "-- end of UMEM_OPTIONS --", ITEM_INVALID
}
153 const char *____umem_environ_msg_debug
= "-- UMEM_DEBUG --";
155 static umem_env_item_t umem_debug_items
[] = {
156 { "default", "Unstable", ITEM_FLAG
,
157 "audit,contents,guards",
159 UMF_AUDIT
| UMF_CONTENTS
| UMF_DEADBEEF
| UMF_REDZONE
161 { "audit", "Unstable", ITEM_OPTUINT
,
162 "Enable auditing. optionally =frames to set the number of "
163 "stored stack frames",
164 &umem_flags
, UMF_AUDIT
, &umem_stack_depth
166 { "contents", "Unstable", ITEM_OPTSIZE
,
167 "Enable contents storing. UMEM_LOGGING=contents also "
168 "required. optionally =bytes to set the number of stored "
170 &umem_flags
, UMF_CONTENTS
, NULL
, &umem_content_maxsave
172 { "guards", "Unstable", ITEM_FLAG
,
173 "Enables guards and special patterns",
174 &umem_flags
, UMF_DEADBEEF
| UMF_REDZONE
176 { "verbose", "Unstable", ITEM_FLAG
,
177 "Enables writing error messages to stderr",
181 { "nosignal", "Private", ITEM_FLAG
,
182 "Abort if called from a signal handler. Turns on 'audit'. "
183 "Note that this is not always a bug.",
184 &umem_flags
, UMF_AUDIT
| UMF_CHECKSIGNAL
186 { "firewall", "Private", ITEM_SIZE
,
187 "=minbytes. Every object >= minbytes in size will have its "
188 "end against an unmapped page",
189 &umem_flags
, UMF_FIREWALL
, NULL
, &umem_minfirewall
191 { "lite", "Private", ITEM_FLAG
,
193 &umem_flags
, UMF_LITE
195 { "maxverify", "Private", ITEM_SIZE
,
196 "=maxbytes, Maximum bytes to check when 'guards' is active. "
197 "Normally all bytes are checked.",
198 NULL
, 0, NULL
, &umem_maxverify
200 { "noabort", "Private", ITEM_CLEARFLAG
,
201 "umem will not abort when a recoverable error occurs "
202 "(i.e. double frees, certain kinds of corruption)",
205 { "mtbf", "Private", ITEM_UINT
,
206 "=mtbf, the mean time between injected failures. Works best "
210 { "random", "Private", ITEM_FLAG
,
211 "randomize flags on a per-cache basis",
212 &umem_flags
, UMF_RANDOMIZE
214 { "allverbose", "Private", ITEM_FLAG
,
215 "Enables writing all logged messages to stderr",
219 { NULL
, "-- end of UMEM_DEBUG --", ITEM_INVALID
}
222 const char *____umem_environ_msg_logging
= "-- UMEM_LOGGING --";
224 static umem_env_item_t umem_logging_items
[] = {
225 { "transaction", "Unstable", ITEM_SPECIAL
,
226 "If 'audit' is set in UMEM_DEBUG, the audit structures "
227 "from previous transactions are entered into this log.",
229 &umem_transaction_log_size
, &umem_log_process
231 { "contents", "Unstable", ITEM_SPECIAL
,
232 "If 'audit' is set in UMEM_DEBUG, the contents of objects "
233 "are recorded in this log as they are freed. If the "
234 "'contents' option is not set in UMEM_DEBUG, the first "
235 "256 bytes of each freed buffer will be saved.",
236 &umem_flags
, UMF_CONTENTS
, NULL
,
237 &umem_content_log_size
, &umem_log_process
239 { "fail", "Unstable", ITEM_SPECIAL
,
240 "Records are entered into this log for every failed "
243 &umem_failure_log_size
, &umem_log_process
246 { "slab", "Private", ITEM_SPECIAL
,
247 "Every slab created will be entered into this log.",
249 &umem_slab_log_size
, &umem_log_process
252 { NULL
, "-- end of UMEM_LOGGING --", ITEM_INVALID
}
255 typedef struct umem_envvar
{
256 const char *env_name
;
257 const char *env_func
;
258 umem_env_item_t
*env_item_list
;
259 const char *env_getenv_result
;
260 const char *env_func_result
;
263 static umem_envvar_t umem_envvars
[] = {
264 { "UMEM_DEBUG", "_umem_debug_init", umem_debug_items
},
265 { "UMEM_OPTIONS", "_umem_options_init", umem_options_items
},
266 { "UMEM_LOGGING", "_umem_logging_init", umem_logging_items
},
270 static umem_envvar_t
*env_current
;
271 #define CURRENT (env_current->env_name)
274 empty(const char *str
)
278 while ((c
= *str
) != '\0' && isspace(c
))
281 return (*str
== '\0');
285 item_uint_process(const umem_env_item_t
*item
, const char *item_arg
)
294 if (empty(item_arg
)) {
298 result
= strtoul(item_arg
, &endptr
, 10);
300 if (result
== ULONG_MAX
&& errno
== ERANGE
) {
308 if ((uint_t
)result
!= result
)
311 (*item
->item_uint_target
) = (uint_t
)result
;
312 return (ARG_SUCCESS
);
315 log_message("%s: %s: not a number\n", CURRENT
, item
->item_name
);
319 log_message("%s: %s: overflowed\n", CURRENT
, item
->item_name
);
324 item_size_process(const umem_env_item_t
*item
, const char *item_arg
)
337 result_arg
= strtoul(item_arg
, &endptr
, 10);
339 if (result_arg
== ULONG_MAX
&& errno
== ERANGE
) {
351 if (result
< result_arg
)
357 if (result
< result_arg
)
363 if (result
< result_arg
)
369 if (result
< result_arg
)
371 endptr
++; /* skip over the size character */
374 break; /* handled later */
380 (*item
->item_size_target
) = result
;
381 return (ARG_SUCCESS
);
384 log_message("%s: %s: not a number\n", CURRENT
, item
->item_name
);
388 log_message("%s: %s: overflowed\n", CURRENT
, item
->item_name
);
393 umem_log_process(const umem_env_item_t
*item
, const char *item_arg
)
395 if (item_arg
!= NULL
) {
397 ret
= item_size_process(item
, item_arg
);
398 if (ret
!= ARG_SUCCESS
)
401 if (*item
->item_size_target
== 0)
402 return (ARG_SUCCESS
);
404 *item
->item_size_target
= 64*1024;
407 return (ARG_SUCCESS
);
411 umem_size_process(const umem_env_item_t
*item
, const char *item_arg
)
413 const char *name
= item
->item_name
;
414 void (*action_func
)(size_t);
420 if (strcmp(name
, "size_clear") == 0) {
421 if (item_arg
!= NULL
) {
422 log_message("%s: %s: does not take a value. ignored\n",
426 umem_alloc_sizes_clear();
427 return (ARG_SUCCESS
);
428 } else if (strcmp(name
, "size_add") == 0) {
429 action_func
= umem_alloc_sizes_add
;
430 } else if (strcmp(name
, "size_remove") == 0) {
431 action_func
= umem_alloc_sizes_remove
;
433 log_message("%s: %s: internally unrecognized\n",
434 CURRENT
, name
, name
, name
);
438 if (item_arg
== NULL
) {
439 log_message("%s: %s: requires a value. ignored\n",
444 ret
= item_size_process(item
, item_arg
);
445 if (ret
!= ARG_SUCCESS
)
448 result
= *item
->item_size_target
;
450 return (ARG_SUCCESS
);
453 #ifndef UMEM_STANDALONE
455 umem_backend_process(const umem_env_item_t
*item
, const char *item_arg
)
457 const char *name
= item
->item_name
;
459 if (item_arg
== NULL
)
462 if (strcmp(item_arg
, "sbrk") == 0)
463 vmem_backend
|= VMEM_BACKEND_SBRK
;
464 else if (strcmp(item_arg
, "mmap") == 0)
465 vmem_backend
|= VMEM_BACKEND_MMAP
;
469 return (ARG_SUCCESS
);
472 log_message("%s: %s: must be %s=sbrk or %s=mmap\n",
473 CURRENT
, name
, name
, name
);
479 process_item(const umem_env_item_t
*item
, const char *item_arg
)
481 int arg_required
= 0;
482 arg_process_t
*processor
;
484 switch (item
->item_type
) {
499 switch (item
->item_type
) {
502 if (item_arg
!= NULL
) {
503 log_message("%s: %s: does not take a value. ignored\n",
504 CURRENT
, item
->item_name
);
512 processor
= item_uint_process
;
517 processor
= item_size_process
;
521 processor
= item
->item_special
;
525 log_message("%s: %s: Invalid type. Ignored\n",
526 CURRENT
, item
->item_name
);
530 if (arg_required
&& item_arg
== NULL
) {
531 log_message("%s: %s: Required value missing\n",
532 CURRENT
, item
->item_name
);
536 if (item_arg
!= NULL
|| item
->item_type
== ITEM_SPECIAL
) {
537 if (processor(item
, item_arg
) != ARG_SUCCESS
)
541 if (item
->item_flag_target
) {
542 if (item
->item_type
== ITEM_CLEARFLAG
)
543 (*item
->item_flag_target
) &= ~item
->item_flag_value
;
545 (*item
->item_flag_target
) |= item
->item_flag_value
;
553 #define ENV_SHORT_BYTES 10 /* bytes to print on error */
555 umem_process_value(umem_env_item_t
*item_list
, const char *beg
, const char *end
)
557 char buf
[UMEM_ENV_ITEM_MAX
];
562 while (beg
< end
&& isspace(*beg
))
565 while (beg
< end
&& isspace(*(end
- 1)))
569 log_message("%s: empty option\n", CURRENT
);
575 if (count
+ 1 > sizeof (buf
)) {
576 char outbuf
[ENV_SHORT_BYTES
+ 1];
578 * Have to do this, since sprintf("%10s",...) calls malloc()
580 (void) strncpy(outbuf
, beg
, ENV_SHORT_BYTES
);
581 outbuf
[ENV_SHORT_BYTES
] = 0;
583 log_message("%s: argument \"%s...\" too long\n", CURRENT
,
588 (void) strncpy(buf
, beg
, count
);
591 argptr
= strchr(buf
, '=');
596 for (; item_list
->item_name
!= NULL
; item_list
++) {
597 if (strcmp(buf
, item_list
->item_name
) == 0) {
598 (void) process_item(item_list
, argptr
);
602 log_message("%s: '%s' not recognized\n", CURRENT
, buf
);
607 umem_setup_envvars(int invalid
)
609 umem_envvar_t
*cur_env
;
610 static volatile enum {
617 } state
= STATE_START
;
618 #ifndef UMEM_STANDALONE
625 * One of the calls below invoked malloc() recursively. We
626 * remove any partial results and return.
631 where
= "before getenv(3C) calls -- "
632 "getenv(3C) results ignored.";
635 where
= "during getenv(3C) calls -- "
636 "getenv(3C) results ignored.";
639 where
= "during dlopen(3C) call -- "
640 "_umem_*() results ignored.";
643 where
= "during dlsym(3C) call -- "
644 "_umem_*() results ignored.";
647 where
= "during _umem_*() call -- "
648 "_umem_*() results ignored.";
651 where
= "after dlsym() or _umem_*() calls.";
654 where
= "at unknown point -- "
655 "_umem_*() results ignored.";
659 log_message("recursive allocation %s\n", where
);
661 for (cur_env
= umem_envvars
; cur_env
->env_name
!= NULL
;
663 if (state
== STATE_GETENV
)
664 cur_env
->env_getenv_result
= NULL
;
665 if (state
!= STATE_DONE
)
666 cur_env
->env_func_result
= NULL
;
673 state
= STATE_GETENV
;
675 for (cur_env
= umem_envvars
; cur_env
->env_name
!= NULL
; cur_env
++) {
676 cur_env
->env_getenv_result
= getenv(cur_env
->env_name
);
677 if (state
== STATE_DONE
)
678 return; /* recursed */
681 #ifndef UMEM_STANDALONE
682 state
= STATE_DLOPEN
;
684 /* get a handle to the "a.out" object */
685 if ((h
= dlopen(0, RTLD_FIRST
| RTLD_LAZY
)) != NULL
) {
686 for (cur_env
= umem_envvars
; cur_env
->env_name
!= NULL
;
688 const char *(*func
)(void);
692 func
= (const char *(*)(void))dlsym(h
,
695 if (state
== STATE_DONE
)
696 break; /* recursed */
701 if (state
== STATE_DONE
)
702 break; /* recursed */
703 cur_env
->env_func_result
= value
;
708 (void) dlerror(); /* snarf dlerror() */
710 #endif /* UMEM_STANDALONE */
716 * Process the environment variables.
719 umem_process_envvars(void)
722 const char *end
, *next
;
723 umem_envvar_t
*cur_env
;
725 for (cur_env
= umem_envvars
; cur_env
->env_name
!= NULL
; cur_env
++) {
726 env_current
= cur_env
;
728 value
= cur_env
->env_getenv_result
;
730 value
= cur_env
->env_func_result
;
732 /* ignore if missing or empty */
736 for (end
= value
; *end
!= '\0'; value
= next
) {
737 end
= strchr(value
, ',');
739 next
= end
+ 1; /* skip the comma */
741 next
= end
= value
+ strlen(value
);
743 umem_process_value(cur_env
->env_item_list
, value
, end
);