/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 2012 Joyent, Inc. All rights reserved.
* Copyright (c) 2015 by Delphix. All rights reserved.
*/
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
#include "umem_base.h"
#include "vmem_base.h"
/*
* A umem environment variable, like UMEM_DEBUG, is set to a series
* of items, seperated by ',':
*
* UMEM_DEBUG="audit=10,guards,firewall=512"
*
* This structure describes items. Each item has a name, type, and
* description. During processing, an item read from the user may
* be either "valid" or "invalid".
*
* A valid item has an argument, if required, and it is of the right
* form (doesn't overflow, doesn't contain any unexpected characters).
*
* If the item is valid, item_flag_target != NULL, and:
* type is not CLEARFLAG, then (*item_flag_target) |= item_flag_value
* type is CLEARFLAG, then (*item_flag_target) &= ~item_flag_value
*/
struct umem_env_item;
typedef struct umem_env_item {
const char *item_interface_stability;
enum {
} item_type;
const char *item_description;
#ifndef UMEM_STANDALONE
#endif
#ifndef UMEM_STANDALONE
"=sbrk for sbrk(2), =mmap for mmap(2)",
},
"=best, =first, =next, or =instant",
},
#endif
"Max concurrency",
NULL, 0, &umem_max_ncpus
},
"Maximum contention in a reap interval before the depot is "
"resized.",
},
"no caches will be multithreaded, and no caching will occur.",
},
"Minimum time between reaps and updates, in seconds.",
NULL, 0, &umem_reap_interval
},
"add a size to the cache size table",
},
"clear all but the largest size from the cache size table",
},
"remove a size from the cache size table",
},
#ifndef UMEM_STANDALONE
"The minimum allocation chunk for the sbrk(2) heap.",
},
"The preferred page size for the sbrk(2) heap.",
},
#endif
"Size (in bytes) of per-thread allocation cache",
},
};
"audit,contents,guards",
},
"Enable auditing. optionally =frames to set the number of "
"stored stack frames",
},
"Enable contents storing. UMEM_LOGGING=contents also "
"required. optionally =bytes to set the number of stored "
"bytes",
},
"Enables guards and special patterns",
},
"Enables writing error messages to stderr",
&umem_output, 1
},
"Abort if called from a signal handler. Turns on 'audit'. "
"Note that this is not always a bug.",
},
"=minbytes. Every object >= minbytes in size will have its "
"end against an unmapped page",
},
"debugging-lite",
},
"=maxbytes, Maximum bytes to check when 'guards' is active. "
"Normally all bytes are checked.",
},
"umem will not abort when a recoverable error occurs "
"(i.e. double frees, certain kinds of corruption)",
&umem_abort, 1
},
"=mtbf, the mean time between injected failures. Works best "
"if prime.\n",
},
"randomize flags on a per-cache basis",
},
"Enables writing all logged messages to stderr",
&umem_output, 2
},
"Abort if an allocation would return null",
},
};
"If 'audit' is set in UMEM_DEBUG, the audit structures "
"from previous transactions are entered into this log.",
},
"If 'audit' is set in UMEM_DEBUG, the contents of objects "
"are recorded in this log as they are freed. If the "
"'contents' option is not set in UMEM_DEBUG, the first "
"256 bytes of each freed buffer will be saved.",
},
"Records are entered into this log for every failed "
"allocation.",
},
"Every slab created will be entered into this log.",
},
};
typedef struct umem_envvar {
const char *env_name;
const char *env_func;
const char *env_getenv_result;
const char *env_func_result;
};
static int
{
char c;
str++;
return (*str == '\0');
}
static int
{
int olderrno;
errno = 0;
goto badnumber;
}
goto overflow;
}
if (*endptr != '\0')
goto badnumber;
goto overflow;
return (ARG_SUCCESS);
return (ARG_BAD);
return (ARG_BAD);
}
static int
{
int olderrno;
goto badnumber;
errno = 0;
goto overflow;
}
result = result_arg;
switch (*endptr) {
case 't':
case 'T':
result *= 1024;
if (result < result_arg)
goto overflow;
/*FALLTHRU*/
case 'g':
case 'G':
result *= 1024;
if (result < result_arg)
goto overflow;
/*FALLTHRU*/
case 'm':
case 'M':
result *= 1024;
if (result < result_arg)
goto overflow;
/*FALLTHRU*/
case 'k':
case 'K':
result *= 1024;
if (result < result_arg)
goto overflow;
endptr++; /* skip over the size character */
break;
default:
break; /* handled later */
}
if (*endptr != '\0')
goto badnumber;
return (ARG_SUCCESS);
return (ARG_BAD);
return (ARG_BAD);
}
static int
{
int ret;
if (ret != ARG_SUCCESS)
return (ret);
if (*item->item_size_target == 0)
return (ARG_SUCCESS);
} else
umem_logging = 1;
return (ARG_SUCCESS);
}
static int
{
void (*action_func)(size_t);
int ret;
log_message("%s: %s: does not take a value. ignored\n",
return (ARG_BAD);
}
return (ARG_SUCCESS);
} else {
log_message("%s: %s: internally unrecognized\n",
return (ARG_BAD);
}
log_message("%s: %s: requires a value. ignored\n",
return (ARG_BAD);
}
if (ret != ARG_SUCCESS)
return (ret);
return (ARG_SUCCESS);
}
#ifndef UMEM_STANDALONE
static int
{
goto fail;
else
goto fail;
return (ARG_SUCCESS);
fail:
log_message("%s: %s: must be %s=sbrk or %s=mmap\n",
return (ARG_BAD);
}
static int
{
goto fail;
vmem_allocator = 0;
else
goto fail;
return (ARG_SUCCESS);
fail:
log_message("%s: %s: must be %s=best, %s=next or %s=first\n",
return (ARG_BAD);
}
#endif
static int
{
int arg_required = 0;
case ITEM_FLAG:
case ITEM_CLEARFLAG:
case ITEM_OPTUINT:
case ITEM_OPTSIZE:
case ITEM_SPECIAL:
arg_required = 0;
break;
case ITEM_UINT:
case ITEM_SIZE:
arg_required = 1;
break;
}
case ITEM_FLAG:
case ITEM_CLEARFLAG:
log_message("%s: %s: does not take a value. ignored\n",
return (1);
}
break;
case ITEM_UINT:
case ITEM_OPTUINT:
break;
case ITEM_SIZE:
case ITEM_OPTSIZE:
break;
case ITEM_SPECIAL:
break;
default:
log_message("%s: %s: Invalid type. Ignored\n",
return (1);
}
log_message("%s: %s: Required value missing\n",
goto invalid;
}
goto invalid;
}
if (item->item_flag_target) {
else
}
return (0);
return (1);
}
void
{
char *argptr;
beg++;
end--;
return;
}
/*
* Have to do this, since sprintf("%10s",...) calls malloc()
*/
outbuf[ENV_SHORT_BYTES] = 0;
outbuf);
return;
}
*argptr++ = 0;
return;
}
}
}
/*ARGSUSED*/
void
{
static volatile enum {
#ifndef UMEM_STANDALONE
void *h;
#endif
if (invalid) {
const char *where;
/*
* One of the calls below invoked malloc() recursively. We
* remove any partial results and return.
*/
switch (state) {
case STATE_START:
where = "before getenv(3C) calls -- "
"getenv(3C) results ignored.";
break;
case STATE_GETENV:
where = "during getenv(3C) calls -- "
"getenv(3C) results ignored.";
break;
case STATE_DLOPEN:
where = "during dlopen(3C) call -- "
"_umem_*() results ignored.";
break;
case STATE_DLSYM:
where = "during dlsym(3C) call -- "
"_umem_*() results ignored.";
break;
case STATE_FUNC:
where = "during _umem_*() call -- "
"_umem_*() results ignored.";
break;
case STATE_DONE:
where = "after dlsym() or _umem_*() calls.";
break;
default:
where = "at unknown point -- "
"_umem_*() results ignored.";
break;
}
cur_env++) {
if (state == STATE_GETENV)
if (state != STATE_DONE)
}
state = STATE_DONE;
return;
}
if (state == STATE_DONE)
return; /* recursed */
}
#ifndef UMEM_STANDALONE
/* get a handle to the "a.out" object */
cur_env++) {
const char *(*func)(void);
const char *value;
state = STATE_DLSYM;
if (state == STATE_DONE)
break; /* recursed */
state = STATE_FUNC;
if (state == STATE_DONE)
break; /* recursed */
}
}
(void) dlclose(h);
} else {
(void) dlerror(); /* snarf dlerror() */
}
#endif /* UMEM_STANDALONE */
state = STATE_DONE;
}
/*
* Process the environment variables.
*/
void
umem_process_envvars(void)
{
const char *value;
/* ignore if missing or empty */
continue;
else
}
}
}