root/trunk/envvar.c

Revision 2, 16.1 kB (checked in by wez, 8 years ago)

Initial revision

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