diff options
Diffstat (limited to 'erts/emulator/beam/erl_alloc.c')
-rw-r--r-- | erts/emulator/beam/erl_alloc.c | 3157 |
1 files changed, 3157 insertions, 0 deletions
diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c new file mode 100644 index 0000000000..b853ec0f01 --- /dev/null +++ b/erts/emulator/beam/erl_alloc.c @@ -0,0 +1,3157 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2002-2009. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + + +/* + * Description: Management of memory allocators. + * + * Author: Rickard Green + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#define ERTS_ALLOC_C__ +#define ERTS_ALC_INTERNAL__ +#include "sys.h" +#define ERL_THREADS_EMU_INTERNAL__ +#include "erl_threads.h" +#include "global.h" +#include "erl_db.h" +#include "erl_binary.h" +#include "erl_bits.h" +#include "erl_instrument.h" +#include "erl_mseg.h" +#ifdef ELIB_ALLOC_IS_CLIB +#include "erl_version.h" +#endif +#include "erl_monitors.h" +#include "erl_bif_timer.h" +#if defined(ERTS_ALC_T_DRV_SEL_D_STATE) || defined(ERTS_ALC_T_DRV_EV_D_STATE) +#include "erl_check_io.h" +#endif + +#define GET_ERL_GF_ALLOC_IMPL +#include "erl_goodfit_alloc.h" +#define GET_ERL_BF_ALLOC_IMPL +#include "erl_bestfit_alloc.h" +#define GET_ERL_AF_ALLOC_IMPL +#include "erl_afit_alloc.h" + +#define ERTS_ALC_DEFAULT_MAX_THR_PREF 16 + +#if defined(SMALL_MEMORY) || defined(PURIFY) || defined(VALGRIND) +#define AU_ALLOC_DEFAULT_ENABLE(X) 0 +#else +#define AU_ALLOC_DEFAULT_ENABLE(X) (X) +#endif + +#ifdef DEBUG +static Uint install_debug_functions(void); +#endif +extern void elib_ensure_initialized(void); + +ErtsAllocatorFunctions_t erts_allctrs[ERTS_ALC_A_MAX+1]; +ErtsAllocatorInfo_t erts_allctrs_info[ERTS_ALC_A_MAX+1]; +ErtsAllocatorThrSpec_t erts_allctr_thr_spec[ERTS_ALC_A_MAX+1]; + +#define ERTS_MIN(A, B) ((A) < (B) ? (A) : (B)) +#define ERTS_MAX(A, B) ((A) > (B) ? (A) : (B)) + +typedef union { + GFAllctr_t gfa; + char align_gfa[ERTS_ALC_CACHE_LINE_ALIGN_SIZE(sizeof(GFAllctr_t))]; + BFAllctr_t bfa; + char align_bfa[ERTS_ALC_CACHE_LINE_ALIGN_SIZE(sizeof(BFAllctr_t))]; + AFAllctr_t afa; + char align_afa[ERTS_ALC_CACHE_LINE_ALIGN_SIZE(sizeof(AFAllctr_t))]; +} ErtsAllocatorState_t; + +static ErtsAllocatorState_t sl_alloc_state; +static ErtsAllocatorState_t std_alloc_state; +static ErtsAllocatorState_t ll_alloc_state; +static ErtsAllocatorState_t temp_alloc_state; +static ErtsAllocatorState_t eheap_alloc_state; +static ErtsAllocatorState_t binary_alloc_state; +static ErtsAllocatorState_t ets_alloc_state; +static ErtsAllocatorState_t driver_alloc_state; + +ErtsAlcType_t erts_fix_core_allocator_ix; +#ifdef ERTS_ALC_N_MIN_A_FIXED_SIZE +static void *(*fix_core_allocator)(ErtsAlcType_t, void *, Uint); +static void *fix_core_extra; +static void *fix_core_alloc(Uint size) +{ + void *res; + res = (*fix_core_allocator)(ERTS_ALC_T_UNDEF, fix_core_extra, size); + if (erts_mtrace_enabled) + erts_mtrace_crr_alloc(res, + ERTS_ALC_A_FIXED_SIZE, + erts_fix_core_allocator_ix, + size); + return res; +} +#endif + +enum allctr_type { + GOODFIT, + BESTFIT, + AFIT +}; + +struct au_init { + int enable; + int thr_spec; + enum allctr_type atype; + struct { + AllctrInit_t util; + GFAllctrInit_t gf; + BFAllctrInit_t bf; + AFAllctrInit_t af; + } init; + struct { + int mmbcs; + int lmbcs; + int smbcs; + int mmmbc; + } default_; +}; + +#define DEFAULT_ALLCTR_INIT { \ + ERTS_DEFAULT_ALLCTR_INIT, \ + ERTS_DEFAULT_GF_ALLCTR_INIT, \ + ERTS_DEFAULT_BF_ALLCTR_INIT, \ + ERTS_DEFAULT_AF_ALLCTR_INIT \ +} + +typedef struct { + int erts_alloc_config; +#if HAVE_ERTS_MSEG + ErtsMsegInit_t mseg; +#endif + int trim_threshold; + int top_pad; + AlcUInit_t alloc_util; + struct { + int stat; + int map; + char *mtrace; + char *nodename; + } instr; + struct au_init sl_alloc; + struct au_init std_alloc; + struct au_init ll_alloc; + struct au_init temp_alloc; + struct au_init eheap_alloc; + struct au_init binary_alloc; + struct au_init ets_alloc; + struct au_init driver_alloc; +} erts_alc_hndl_args_init_t; + +#define ERTS_AU_INIT__ {0, 0, GOODFIT, DEFAULT_ALLCTR_INIT, {1,1,1,1}} + +#define SET_DEFAULT_ALLOC_OPTS(IP) \ +do { \ + struct au_init aui__ = ERTS_AU_INIT__; \ + sys_memcpy((void *) (IP), (void *) &aui__, sizeof(struct au_init)); \ +} while (0) + +static void +set_default_sl_alloc_opts(struct au_init *ip) +{ + SET_DEFAULT_ALLOC_OPTS(ip); + ip->enable = AU_ALLOC_DEFAULT_ENABLE(1); + ip->thr_spec = 1; + ip->atype = GOODFIT; + ip->init.util.name_prefix = "sl_"; + ip->init.util.mmmbc = 5; + ip->init.util.alloc_no = ERTS_ALC_A_SHORT_LIVED; +#ifndef SMALL_MEMORY + ip->init.util.mmbcs = 128*1024; /* Main carrier size */ +#else + ip->init.util.mmbcs = 32*1024; /* Main carrier size */ +#endif + ip->init.util.ts = ERTS_ALC_MTA_SHORT_LIVED; + ip->init.util.rsbcst = 80; +} + +static void +set_default_std_alloc_opts(struct au_init *ip) +{ + SET_DEFAULT_ALLOC_OPTS(ip); + ip->enable = AU_ALLOC_DEFAULT_ENABLE(1); + ip->thr_spec = 1; + ip->atype = BESTFIT; + ip->init.util.name_prefix = "std_"; + ip->init.util.mmmbc = 5; + ip->init.util.alloc_no = ERTS_ALC_A_STANDARD; +#ifndef SMALL_MEMORY + ip->init.util.mmbcs = 128*1024; /* Main carrier size */ +#else + ip->init.util.mmbcs = 32*1024; /* Main carrier size */ +#endif + ip->init.util.ts = ERTS_ALC_MTA_STANDARD; +} + +static void +set_default_ll_alloc_opts(struct au_init *ip) +{ + SET_DEFAULT_ALLOC_OPTS(ip); + ip->enable = AU_ALLOC_DEFAULT_ENABLE(1); + ip->thr_spec = 0; + ip->atype = BESTFIT; + ip->init.bf.ao = 1; + ip->init.util.ramv = 0; + ip->init.util.mmsbc = 0; + ip->init.util.mmmbc = 0; + ip->init.util.sbct = ~((Uint) 0); + ip->init.util.name_prefix = "ll_"; + ip->init.util.alloc_no = ERTS_ALC_A_LONG_LIVED; +#ifndef SMALL_MEMORY + ip->init.util.mmbcs = 2*1024*1024; /* Main carrier size */ +#else + ip->init.util.mmbcs = 1*1024*1024; /* Main carrier size */ +#endif + ip->init.util.ts = ERTS_ALC_MTA_LONG_LIVED; + ip->init.util.asbcst = 0; + ip->init.util.rsbcst = 0; + ip->init.util.rsbcmt = 0; + ip->init.util.rmbcmt = 0; +} + +static void +set_default_temp_alloc_opts(struct au_init *ip) +{ + SET_DEFAULT_ALLOC_OPTS(ip); + ip->enable = AU_ALLOC_DEFAULT_ENABLE(1); + ip->thr_spec = 1; + ip->atype = AFIT; + ip->init.util.name_prefix = "temp_"; + ip->init.util.alloc_no = ERTS_ALC_A_TEMPORARY; +#ifndef SMALL_MEMORY + ip->init.util.mmbcs = 128*1024; /* Main carrier size */ +#else + ip->init.util.mmbcs = 32*1024; /* Main carrier size */ +#endif + ip->init.util.ts = ERTS_ALC_MTA_TEMPORARY; + ip->init.util.rsbcst = 90; + ip->init.util.rmbcmt = 100; +} + +static void +set_default_eheap_alloc_opts(struct au_init *ip) +{ + SET_DEFAULT_ALLOC_OPTS(ip); + ip->enable = AU_ALLOC_DEFAULT_ENABLE(1); + ip->thr_spec = 1; + ip->atype = GOODFIT; + ip->init.util.mmmbc = 100; + ip->init.util.name_prefix = "eheap_"; + ip->init.util.alloc_no = ERTS_ALC_A_EHEAP; +#ifndef SMALL_MEMORY + ip->init.util.mmbcs = 512*1024; /* Main carrier size */ +#else + ip->init.util.mmbcs = 256*1024; /* Main carrier size */ +#endif + ip->init.util.ts = ERTS_ALC_MTA_EHEAP; + ip->init.util.rsbcst = 50; +} + +static void +set_default_binary_alloc_opts(struct au_init *ip) +{ + SET_DEFAULT_ALLOC_OPTS(ip); + ip->enable = AU_ALLOC_DEFAULT_ENABLE(1); + ip->thr_spec = 1; + ip->atype = BESTFIT; + ip->init.util.mmmbc = 50; + ip->init.util.name_prefix = "binary_"; + ip->init.util.alloc_no = ERTS_ALC_A_BINARY; +#ifndef SMALL_MEMORY + ip->init.util.mmbcs = 128*1024; /* Main carrier size */ +#else + ip->init.util.mmbcs = 32*1024; /* Main carrier size */ +#endif + ip->init.util.ts = ERTS_ALC_MTA_BINARY; +} + +static void +set_default_ets_alloc_opts(struct au_init *ip) +{ + SET_DEFAULT_ALLOC_OPTS(ip); + ip->enable = AU_ALLOC_DEFAULT_ENABLE(1); + ip->thr_spec = 1; + ip->atype = BESTFIT; + ip->init.util.mmmbc = 100; + ip->init.util.name_prefix = "ets_"; + ip->init.util.alloc_no = ERTS_ALC_A_ETS; +#ifndef SMALL_MEMORY + ip->init.util.mmbcs = 128*1024; /* Main carrier size */ +#else + ip->init.util.mmbcs = 32*1024; /* Main carrier size */ +#endif + ip->init.util.ts = ERTS_ALC_MTA_ETS; +} + +static void +set_default_driver_alloc_opts(struct au_init *ip) +{ + SET_DEFAULT_ALLOC_OPTS(ip); + ip->enable = AU_ALLOC_DEFAULT_ENABLE(1); + ip->thr_spec = 1; + ip->atype = BESTFIT; + ip->init.util.name_prefix = "driver_"; + ip->init.util.alloc_no = ERTS_ALC_A_DRIVER; +#ifndef SMALL_MEMORY + ip->init.util.mmbcs = 128*1024; /* Main carrier size */ +#else + ip->init.util.mmbcs = 32*1024; /* Main carrier size */ +#endif + ip->init.util.ts = ERTS_ALC_MTA_DRIVER; +} + +#ifdef ERTS_SMP + +static void +adjust_tpref(struct au_init *ip, int no_sched) +{ + if (ip->thr_spec) { + Uint allocs; + if (ip->thr_spec < 0) {/* User specified amount */ + allocs = abs(ip->thr_spec); + if (allocs > no_sched) + allocs = no_sched; + } + else if (no_sched > ERTS_ALC_DEFAULT_MAX_THR_PREF) + allocs = ERTS_ALC_DEFAULT_MAX_THR_PREF; + else + allocs = no_sched; + if (allocs <= 1) + ip->thr_spec = 0; + else { + ip->thr_spec = (int) allocs; + ip->thr_spec *= -1; /* thread preferred */ + + /* If default ... */ + + /* ... shrink main multi-block carrier size */ + if (ip->default_.mmbcs) + ip->init.util.mmbcs /= ERTS_MIN(4, allocs); + /* ... shrink largest multi-block carrier size */ + if (ip->default_.lmbcs) + ip->init.util.lmbcs /= ERTS_MIN(2, allocs); + /* ... shrink smallest multi-block carrier size */ + if (ip->default_.smbcs) + ip->init.util.smbcs /= ERTS_MIN(4, allocs); + /* ... and more than three allocators shrink + max mseg multi-block carriers */ + if (ip->default_.mmmbc && allocs > 2) { + ip->init.util.mmmbc /= ERTS_MIN(4, allocs - 1); + if (ip->init.util.mmmbc < 3) + ip->init.util.mmmbc = 3; + } + } + } +} + +#endif + +static void handle_args(int *, char **, erts_alc_hndl_args_init_t *); + +static void +set_au_allocator(ErtsAlcType_t alctr_n, struct au_init *init); + +static void +start_au_allocator(ErtsAlcType_t alctr_n, + struct au_init *init, + ErtsAllocatorState_t *state); + +static void +refuse_af_strategy(struct au_init *init) +{ + if (init->atype == AFIT) + init->atype = GOODFIT; +} + +static void init_thr_ix(int static_ixs); + +void +erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) +{ + Uint extra_block_size = 0; + int i; + erts_alc_hndl_args_init_t init = { + 0, +#if HAVE_ERTS_MSEG + ERTS_MSEG_INIT_DEFAULT_INITIALIZER, +#endif + ERTS_DEFAULT_TRIM_THRESHOLD, + ERTS_DEFAULT_TOP_PAD, + ERTS_DEFAULT_ALCU_INIT + }; + + erts_sys_alloc_init(); + init_thr_ix(erts_no_schedulers); + erts_init_utils_mem(); + + set_default_sl_alloc_opts(&init.sl_alloc); + set_default_std_alloc_opts(&init.std_alloc); + set_default_ll_alloc_opts(&init.ll_alloc); + set_default_temp_alloc_opts(&init.temp_alloc); + set_default_eheap_alloc_opts(&init.eheap_alloc); + set_default_binary_alloc_opts(&init.binary_alloc); + set_default_ets_alloc_opts(&init.ets_alloc); + set_default_driver_alloc_opts(&init.driver_alloc); + + if (argc && argv) + handle_args(argc, argv, &init); + + if (erts_no_schedulers <= 1) { + init.sl_alloc.thr_spec = 0; + init.std_alloc.thr_spec = 0; + init.ll_alloc.thr_spec = 0; + init.eheap_alloc.thr_spec = 0; + init.binary_alloc.thr_spec = 0; + init.ets_alloc.thr_spec = 0; + init.driver_alloc.thr_spec = 0; + } + + if (init.erts_alloc_config) { + /* Adjust flags that erts_alloc_config won't like */ + init.temp_alloc.thr_spec = 0; + init.sl_alloc.thr_spec = 0; + init.std_alloc.thr_spec = 0; + init.ll_alloc.thr_spec = 0; + init.eheap_alloc.thr_spec = 0; + init.binary_alloc.thr_spec = 0; + init.ets_alloc.thr_spec = 0; + init.driver_alloc.thr_spec = 0; + } + +#ifdef ERTS_SMP + /* Only temp_alloc can use thread specific interface */ + if (init.temp_alloc.thr_spec) + init.temp_alloc.thr_spec = erts_no_schedulers; + + /* Others must use thread preferred interface */ + adjust_tpref(&init.sl_alloc, erts_no_schedulers); + adjust_tpref(&init.std_alloc, erts_no_schedulers); + adjust_tpref(&init.ll_alloc, erts_no_schedulers); + adjust_tpref(&init.eheap_alloc, erts_no_schedulers); + adjust_tpref(&init.binary_alloc, erts_no_schedulers); + adjust_tpref(&init.ets_alloc, erts_no_schedulers); + adjust_tpref(&init.driver_alloc, erts_no_schedulers); + +#else + /* No thread specific if not smp */ + init.temp_alloc.thr_spec = 0; +#endif + + /* + * The following allocators cannot be run with afit strategy. + * Make sure they don't... + */ + refuse_af_strategy(&init.sl_alloc); + refuse_af_strategy(&init.std_alloc); + refuse_af_strategy(&init.ll_alloc); + refuse_af_strategy(&init.eheap_alloc); + refuse_af_strategy(&init.binary_alloc); + refuse_af_strategy(&init.ets_alloc); + refuse_af_strategy(&init.driver_alloc); + +#ifdef ERTS_SMP + if (!init.temp_alloc.thr_spec) + refuse_af_strategy(&init.temp_alloc); +#endif + + erts_mtrace_pre_init(); +#if HAVE_ERTS_MSEG + erts_mseg_init(&init.mseg); +#endif + erts_alcu_init(&init.alloc_util); + erts_afalc_init(); + erts_bfalc_init(); + erts_gfalc_init(); + + for (i = ERTS_ALC_A_MIN; i <= ERTS_ALC_A_MAX; i++) { + erts_allctrs[i].alloc = NULL; + erts_allctrs[i].realloc = NULL; + erts_allctrs[i].free = NULL; + erts_allctrs[i].extra = NULL; + erts_allctrs_info[i].alloc_util = 0; + erts_allctrs_info[i].enabled = 0; + erts_allctrs_info[i].thr_spec = 0; + erts_allctrs_info[i].extra = NULL; + } + +#ifdef ERTS_ALC_N_MIN_A_FIXED_SIZE +#if !defined(PURIFY) && !defined(VALGRIND) + erts_allctrs[ERTS_ALC_A_FIXED_SIZE].alloc = erts_fix_alloc; + erts_allctrs[ERTS_ALC_A_FIXED_SIZE].realloc = erts_fix_realloc; + erts_allctrs[ERTS_ALC_A_FIXED_SIZE].free = erts_fix_free; + erts_allctrs_info[ERTS_ALC_A_FIXED_SIZE].enabled = 1; +#else + erts_allctrs[ERTS_ALC_A_FIXED_SIZE].alloc = erts_sys_alloc; + erts_allctrs[ERTS_ALC_A_FIXED_SIZE].realloc = erts_sys_realloc; + erts_allctrs[ERTS_ALC_A_FIXED_SIZE].free = erts_sys_free; + erts_allctrs_info[ERTS_ALC_A_FIXED_SIZE].enabled = 0; +#endif +#endif + + erts_allctrs[ERTS_ALC_A_SYSTEM].alloc = erts_sys_alloc; + erts_allctrs[ERTS_ALC_A_SYSTEM].realloc = erts_sys_realloc; + erts_allctrs[ERTS_ALC_A_SYSTEM].free = erts_sys_free; + erts_allctrs_info[ERTS_ALC_A_SYSTEM].enabled = 1; + + set_au_allocator(ERTS_ALC_A_TEMPORARY, &init.temp_alloc); + set_au_allocator(ERTS_ALC_A_SHORT_LIVED, &init.sl_alloc); + set_au_allocator(ERTS_ALC_A_STANDARD, &init.std_alloc); + set_au_allocator(ERTS_ALC_A_LONG_LIVED, &init.ll_alloc); + set_au_allocator(ERTS_ALC_A_EHEAP, &init.eheap_alloc); + set_au_allocator(ERTS_ALC_A_BINARY, &init.binary_alloc); + set_au_allocator(ERTS_ALC_A_ETS, &init.ets_alloc); + set_au_allocator(ERTS_ALC_A_DRIVER, &init.driver_alloc); + + for (i = ERTS_ALC_A_MIN; i <= ERTS_ALC_A_MAX; i++) { + if (!erts_allctrs[i].alloc) + erl_exit(ERTS_ABORT_EXIT, + "Missing alloc function for %s\n", ERTS_ALC_A2AD(i)); + if (!erts_allctrs[i].realloc) + erl_exit(ERTS_ABORT_EXIT, + "Missing realloc function for %s\n", ERTS_ALC_A2AD(i)); + if (!erts_allctrs[i].free) + erl_exit(ERTS_ABORT_EXIT, + "Missing free function for %s\n", ERTS_ALC_A2AD(i)); + } + + sys_alloc_opt(SYS_ALLOC_OPT_TRIM_THRESHOLD, init.trim_threshold); + sys_alloc_opt(SYS_ALLOC_OPT_TOP_PAD, init.top_pad); + + if (erts_allctrs_info[ERTS_FIX_CORE_ALLOCATOR].enabled) + erts_fix_core_allocator_ix = ERTS_FIX_CORE_ALLOCATOR; + else + erts_fix_core_allocator_ix = ERTS_ALC_A_SYSTEM; + + erts_mtrace_init(init.instr.mtrace, init.instr.nodename); + + start_au_allocator(ERTS_ALC_A_TEMPORARY, + &init.temp_alloc, + &temp_alloc_state); + + start_au_allocator(ERTS_ALC_A_SHORT_LIVED, + &init.sl_alloc, + &sl_alloc_state); + + start_au_allocator(ERTS_ALC_A_STANDARD, + &init.std_alloc, + &std_alloc_state); + + start_au_allocator(ERTS_ALC_A_LONG_LIVED, + &init.ll_alloc, + &ll_alloc_state); + + start_au_allocator(ERTS_ALC_A_EHEAP, + &init.eheap_alloc, + &eheap_alloc_state); + + start_au_allocator(ERTS_ALC_A_BINARY, + &init.binary_alloc, + &binary_alloc_state); + + start_au_allocator(ERTS_ALC_A_ETS, + &init.ets_alloc, + &ets_alloc_state); + + start_au_allocator(ERTS_ALC_A_DRIVER, + &init.driver_alloc, + &driver_alloc_state); + + fix_core_allocator = erts_allctrs[erts_fix_core_allocator_ix].alloc; + fix_core_extra = erts_allctrs[erts_fix_core_allocator_ix].extra; + + erts_mtrace_install_wrapper_functions(); + extra_block_size += erts_instr_init(init.instr.stat, init.instr.map); + +#ifdef DEBUG + extra_block_size += install_debug_functions(); +#endif + +#ifdef ERTS_ALC_N_MIN_A_FIXED_SIZE + + erts_init_fix_alloc(extra_block_size, fix_core_alloc); + + +#if !defined(PURIFY) && !defined(VALGRIND) + erts_set_fix_size(ERTS_ALC_T_PROC, sizeof(Process)); + erts_set_fix_size(ERTS_ALC_T_DB_TABLE, sizeof(DbTable)); + erts_set_fix_size(ERTS_ALC_T_ATOM, sizeof(Atom)); + erts_set_fix_size(ERTS_ALC_T_EXPORT, sizeof(Export)); + erts_set_fix_size(ERTS_ALC_T_MODULE, sizeof(Module)); + erts_set_fix_size(ERTS_ALC_T_REG_PROC, sizeof(RegProc)); + erts_set_fix_size(ERTS_ALC_T_MONITOR_SH, ERTS_MONITOR_SH_SIZE*sizeof(Uint)); + erts_set_fix_size(ERTS_ALC_T_NLINK_SH, ERTS_LINK_SH_SIZE*sizeof(Uint)); + erts_set_fix_size(ERTS_ALC_T_FUN_ENTRY, sizeof(ErlFunEntry)); +#ifdef ERTS_ALC_T_DRV_EV_D_STATE + erts_set_fix_size(ERTS_ALC_T_DRV_EV_D_STATE, + sizeof(ErtsDrvEventDataState)); +#endif +#ifdef ERTS_ALC_T_DRV_SEL_D_STATE + erts_set_fix_size(ERTS_ALC_T_DRV_SEL_D_STATE, + sizeof(ErtsDrvSelectDataState)); +#endif +#endif +#endif + +} + +static void +set_au_allocator(ErtsAlcType_t alctr_n, struct au_init *init) +{ + ErtsAllocatorFunctions_t *af = &erts_allctrs[alctr_n]; + ErtsAllocatorInfo_t *ai = &erts_allctrs_info[alctr_n]; + ErtsAllocatorThrSpec_t *tspec = &erts_allctr_thr_spec[alctr_n]; + + if (!init->enable) { + af->alloc = erts_sys_alloc; + af->realloc = erts_sys_realloc; + af->free = erts_sys_free; + af->extra = NULL; + ai->alloc_util = 0; + ai->enabled = 0; + ai->extra = NULL; + return; + } + + tspec->enabled = 0; + tspec->all_thr_safe = 0; + ai->thr_spec = 0; +#ifdef USE_THREADS + if (init->thr_spec) { + if (init->thr_spec > 0) { + af->alloc = erts_alcu_alloc_thr_spec; + if (init->init.util.ramv) + af->realloc = erts_alcu_realloc_mv_thr_spec; + else + af->realloc = erts_alcu_realloc_thr_spec; + af->free = erts_alcu_free_thr_spec; + } + else { + af->alloc = erts_alcu_alloc_thr_pref; + if (init->init.util.ramv) + af->realloc = erts_alcu_realloc_mv_thr_pref; + else + af->realloc = erts_alcu_realloc_thr_pref; + af->free = erts_alcu_free_thr_pref; + tspec->all_thr_safe = 1; + } + + tspec->enabled = 1; + tspec->size = abs(init->thr_spec) + 1; + + ai->thr_spec = tspec->size; + } + else if (init->init.util.ts) { + af->alloc = erts_alcu_alloc_ts; + if (init->init.util.ramv) + af->realloc = erts_alcu_realloc_mv_ts; + else + af->realloc = erts_alcu_realloc_ts; + af->free = erts_alcu_free_ts; + } + else +#endif + { + af->alloc = erts_alcu_alloc; + if (init->init.util.ramv) + af->realloc = erts_alcu_realloc_mv; + else + af->realloc = erts_alcu_realloc; + af->free = erts_alcu_free; + } + af->extra = NULL; + ai->alloc_util = 1; + ai->enabled = 1; +} + +static void +start_au_allocator(ErtsAlcType_t alctr_n, + struct au_init *init, + ErtsAllocatorState_t *state) +{ + int i; + int size = 1; + void *as0; + enum allctr_type atype; + ErtsAllocatorFunctions_t *af = &erts_allctrs[alctr_n]; + ErtsAllocatorInfo_t *ai = &erts_allctrs_info[alctr_n]; + ErtsAllocatorThrSpec_t *tspec = &erts_allctr_thr_spec[alctr_n]; + + if (!init->enable) + return; + + if (init->thr_spec) { + void *states = erts_sys_alloc(0, + NULL, + ((sizeof(Allctr_t *) + * (tspec->size + 1)) + + (sizeof(ErtsAllocatorState_t) + * tspec->size) + + ERTS_CACHE_LINE_SIZE - 1)); + if (!states) + erl_exit(ERTS_ABORT_EXIT, + "Failed to allocate allocator states for %salloc\n", + init->init.util.name_prefix); + tspec->allctr = (Allctr_t **) states; + states = ((char *) states) + sizeof(Allctr_t *) * (tspec->size + 1); + states = ((((Uint) states) & ERTS_CACHE_LINE_MASK) + ? (void *) ((((Uint) states) & ~ERTS_CACHE_LINE_MASK) + + ERTS_CACHE_LINE_SIZE) + : (void *) states); + tspec->allctr[0] = init->thr_spec > 0 ? (Allctr_t *) state : (Allctr_t *) NULL; + size = tspec->size; + for (i = 1; i < size; i++) + tspec->allctr[i] = (Allctr_t *) + &((ErtsAllocatorState_t *) states)[i-1]; + } + + for (i = 0; i < size; i++) { + void *as; + atype = init->atype; + + if (!init->thr_spec) + as0 = state; + else { + as0 = (void *) tspec->allctr[i]; + if (!as0) + continue; + if (i == 0) { + if (atype == AFIT) + atype = GOODFIT; + init->init.util.ts = 1; + } + else { + if (init->thr_spec < 0) { + init->init.util.ts = 1; + init->init.util.tspec = 0; + init->init.util.tpref = -1*init->thr_spec; + } + else { + init->init.util.ts = 0; + init->init.util.tspec = init->thr_spec + 1; + init->init.util.tpref = 0; + } + } + } + + switch (atype) { + case GOODFIT: + as = (void *) erts_gfalc_start((GFAllctr_t *) as0, + &init->init.gf, + &init->init.util); + break; + case BESTFIT: + as = (void *) erts_bfalc_start((BFAllctr_t *) as0, + &init->init.bf, + &init->init.util); + break; + case AFIT: + as = (void *) erts_afalc_start((AFAllctr_t *) as0, + &init->init.af, + &init->init.util); + break; + default: + as = NULL; + ASSERT(0); + } + + if (!as) + erl_exit(ERTS_ABORT_EXIT, + "Failed to start %salloc\n", init->init.util.name_prefix); + + ASSERT(as == (void *) as0); + af->extra = as; + } + + if (init->thr_spec) { + af->extra = tspec; + init->init.util.ts = 1; + } + + ai->extra = af->extra; +} + + +static void bad_param(char *param_start, char *param_end) +{ + size_t len = param_end - param_start; + char param[100]; + if (len > 99) + len = 99; + sys_memcpy((void *) param, (void *) param_start, len); + param[len] = '\0'; + erts_fprintf(stderr, "bad \"%s\" parameter\n", param); + erts_usage(); +} + +static void bad_value(char *param_start, char *param_end, char *value) +{ + size_t len = param_end - param_start; + char param[100]; + if (len > 99) + len = 99; + sys_memcpy((void *) param, (void *) param_start, len); + param[len] = '\0'; + erts_fprintf(stderr, "bad \"%s\" value: %s\n", param, value); + erts_usage(); +} + +/* Get arg marks argument as handled by + putting NULL in argv */ +static char * +get_value(char* rest, char** argv, int* ip) +{ + char *param = argv[*ip]+1; + argv[*ip] = NULL; + if (*rest == '\0') { + char *next = argv[*ip + 1]; + if (next[0] == '-' + && next[1] == '-' + && next[2] == '\0') { + bad_value(param, rest, ""); + } + (*ip)++; + argv[*ip] = NULL; + return next; + } + return rest; +} + +static ERTS_INLINE int +has_prefix(const char *prefix, const char *string) +{ + int i; + for (i = 0; prefix[i]; i++) + if (prefix[i] != string[i]) + return 0; + return 1; +} + +static int +get_bool_value(char *param_end, char** argv, int* ip) +{ + char *param = argv[*ip]+1; + char *value = get_value(param_end, argv, ip); + if (strcmp(value, "true") == 0) + return 1; + else if (strcmp(value, "false") == 0) + return 0; + else + bad_value(param, param_end, value); + return -1; +} + +static Uint +get_kb_value(char *param_end, char** argv, int* ip) +{ + Sint tmp; + Uint max = ((~((Uint) 0))/1024) + 1; + char *rest; + char *param = argv[*ip]+1; + char *value = get_value(param_end, argv, ip); + errno = 0; + tmp = (Sint) strtol(value, &rest, 10); + if (errno != 0 || rest == value || tmp < 0 || max < ((Uint) tmp)) + bad_value(param, param_end, value); + if (max == (Uint) tmp) + return ~((Uint) 0); + else + return ((Uint) tmp)*1024; +} + +static Uint +get_amount_value(char *param_end, char** argv, int* ip) +{ + Sint tmp; + char *rest; + char *param = argv[*ip]+1; + char *value = get_value(param_end, argv, ip); + errno = 0; + tmp = (Sint) strtol(value, &rest, 10); + if (errno != 0 || rest == value || tmp < 0) + bad_value(param, param_end, value); + return (Uint) tmp; +} + +static int +get_bool_or_possitive_amount_value(int *bool, Uint *amount, + char *param_end, char** argv, int* ip) +{ + char *param = argv[*ip]+1; + char *value = get_value(param_end, argv, ip); + if (strcmp(value, "true") == 0) { + *bool = 1; + return 1; + } + else if (strcmp(value, "false") == 0) { + *bool = 0; + return 1; + } + else { + Sint tmp; + char *rest; + errno = 0; + tmp = (Sint) strtol(value, &rest, 10); + if (errno != 0 || rest == value || tmp <= 0) { + bad_value(param, param_end, value); + return -1; + } + *amount = (Uint) tmp; + return 0; + } +} + +static void +handle_au_arg(struct au_init *auip, + char* sub_param, + char** argv, + int* ip) +{ + char *param = argv[*ip]+1; + + switch (sub_param[0]) { + case 'a': + if(has_prefix("asbcst", sub_param)) { + auip->init.util.asbcst = get_kb_value(sub_param + 6, argv, ip); + } + else if(has_prefix("as", sub_param)) { + char *alg = get_value(sub_param + 2, argv, ip); + if (strcmp("bf", alg) == 0) { + auip->atype = BESTFIT; + auip->init.bf.ao = 0; + } + else if (strcmp("aobf", alg) == 0) { + auip->atype = BESTFIT; + auip->init.bf.ao = 1; + } + else if (strcmp("gf", alg) == 0) { + auip->atype = GOODFIT; + } + else if (strcmp("af", alg) == 0) { + auip->atype = AFIT; + } + else { + bad_value(param, sub_param + 1, alg); + } + } + else + goto bad_switch; + break; + case 'e': + auip->enable = get_bool_value(sub_param+1, argv, ip); + break; + case 'l': + if (has_prefix("lmbcs", sub_param)) { + auip->default_.lmbcs = 0; + auip->init.util.lmbcs = get_kb_value(sub_param + 5, argv, ip); + } + else + goto bad_switch; + break; + case 'm': + if (has_prefix("mbcgs", sub_param)) { + auip->init.util.mbcgs = get_amount_value(sub_param + 5, argv, ip); + + } + else if (has_prefix("mbsd", sub_param)) { + auip->init.gf.mbsd = get_amount_value(sub_param + 4, argv, ip); + if (auip->init.gf.mbsd < 1) + auip->init.gf.mbsd = 1; + } + else if (has_prefix("mmbcs", sub_param)) { + auip->default_.mmbcs = 0; + auip->init.util.mmbcs = get_kb_value(sub_param + 5, argv, ip); + } + else if (has_prefix("mmmbc", sub_param)) { + auip->default_.mmmbc = 0; + auip->init.util.mmmbc = get_amount_value(sub_param + 5, argv, ip); + } + else if (has_prefix("mmsbc", sub_param)) { + auip->init.util.mmsbc = get_amount_value(sub_param + 5, argv, ip); + } + else + goto bad_switch; + break; + case 'r': + if(has_prefix("rsbcmt", sub_param)) { + auip->init.util.rsbcmt = get_amount_value(sub_param + 6, argv, ip); + if (auip->init.util.rsbcmt > 100) + auip->init.util.rsbcmt = 100; + } + else if(has_prefix("rsbcst", sub_param)) { + auip->init.util.rsbcst = get_amount_value(sub_param + 6, argv, ip); + if (auip->init.util.rsbcst > 100) + auip->init.util.rsbcst = 100; + } + else if (has_prefix("rmbcmt", sub_param)) { + auip->init.util.rmbcmt = get_amount_value(sub_param + 6, argv, ip); + if (auip->init.util.rmbcmt > 100) + auip->init.util.rmbcmt = 100; + } + else if (has_prefix("ramv", sub_param)) { + auip->init.util.ramv = get_bool_value(sub_param + 4, argv, ip); + } + else + goto bad_switch; + break; + case 's': + if(has_prefix("sbct", sub_param)) { + auip->init.util.sbct = get_kb_value(sub_param + 4, argv, ip); + } + else if (has_prefix("smbcs", sub_param)) { + auip->default_.smbcs = 0; + auip->init.util.smbcs = get_kb_value(sub_param + 5, argv, ip); + } + else + goto bad_switch; + break; + case 't': { + Uint no; + int enable; + int res = get_bool_or_possitive_amount_value(&enable, + &no, + sub_param+1, + argv, + ip); + if (res > 0) + auip->thr_spec = enable ? 1 : 0; + else if (res == 0) { + int allocs = (int) no; + if (allocs < 0) + allocs = INT_MIN; + else { + allocs *= -1; + } + auip->thr_spec = allocs; + } + break; + } + default: + bad_switch: + bad_param(param, sub_param); + } +} + +static void +handle_args(int *argc, char **argv, erts_alc_hndl_args_init_t *init) +{ + struct au_init *aui[] = { + &init->binary_alloc, + &init->std_alloc, + &init->ets_alloc, + &init->eheap_alloc, + &init->ll_alloc, + &init->driver_alloc, + &init->sl_alloc, + &init->temp_alloc + }; + int aui_sz = (int) sizeof(aui)/sizeof(aui[0]); + char *arg; + char *rest; + int i, j; + + i = 1; + + ASSERT(argc && argv && init); + + while (i < *argc) { + if(argv[i][0] == '-') { + char *param = argv[i]+1; + switch (argv[i][1]) { + case 'M': + switch (argv[i][2]) { + case 'B': + handle_au_arg(&init->binary_alloc, &argv[i][3], argv, &i); + break; + case 'D': + handle_au_arg(&init->std_alloc, &argv[i][3], argv, &i); + break; + case 'E': + handle_au_arg(&init->ets_alloc, &argv[i][3], argv, &i); + break; + case 'F': /* fix_alloc */ + if (has_prefix("e", param+2)) { + arg = get_value(param+3, argv, &i); + if (strcmp("true", arg) != 0) + bad_value(param, param+3, arg); + } + else + bad_param(param, param+2); + break; + case 'H': + handle_au_arg(&init->eheap_alloc, &argv[i][3], argv, &i); + break; + case 'L': + handle_au_arg(&init->ll_alloc, &argv[i][3], argv, &i); + break; + case 'M': + if (has_prefix("amcbf", argv[i]+3)) { +#if HAVE_ERTS_MSEG + init->mseg.amcbf = +#endif + get_kb_value(argv[i]+8, argv, &i); + } + else if (has_prefix("rmcbf", argv[i]+3)) { +#if HAVE_ERTS_MSEG + init->mseg.rmcbf = +#endif + get_amount_value(argv[i]+8, argv, &i); + } + else if (has_prefix("mcs", argv[i]+3)) { +#if HAVE_ERTS_MSEG + init->mseg.mcs = +#endif + get_amount_value(argv[i]+6, argv, &i); + } + else if (has_prefix("cci", argv[i]+3)) { +#if HAVE_ERTS_MSEG + init->mseg.cci = +#endif + get_amount_value(argv[i]+6, argv, &i); + } + else { + bad_param(param, param+2); + } + break; + case 'R': + handle_au_arg(&init->driver_alloc, &argv[i][3], argv, &i); + break; + case 'S': + handle_au_arg(&init->sl_alloc, &argv[i][3], argv, &i); + break; + case 'T': + handle_au_arg(&init->temp_alloc, &argv[i][3], argv, &i); + break; + case 'Y': { /* sys_alloc */ + if (has_prefix("tt", param+2)) { + /* set trim threshold */ + arg = get_value(param+4, argv, &i); + errno = 0; + init->trim_threshold = (int) strtol(arg, &rest, 10); + if (errno != 0 + || rest == arg + || init->trim_threshold < 0 + || (INT_MAX/1024) < init->trim_threshold) { + bad_value(param, param+4, arg); + } + VERBOSE(DEBUG_SYSTEM, + ("using trim threshold: %d\n", + init->trim_threshold)); + init->trim_threshold *= 1024; + } + else if (has_prefix("tp", param+2)) { + /* set top pad */ + arg = get_value(param+4, argv, &i); + errno = 0; + init->top_pad = (int) strtol(arg, &rest, 10); + if (errno != 0 + || rest == arg + || init->top_pad < 0 + || (INT_MAX/1024) < init->top_pad) { + bad_value(param, param+4, arg); + } + VERBOSE(DEBUG_SYSTEM, + ("using top pad: %d\n",init->top_pad)); + init->top_pad *= 1024; + } + else if (has_prefix("m", param+2)) { + /* Has been handled by erlexec */ + (void) get_value(param+3, argv, &i); + } + else if (has_prefix("e", param+2)) { + arg = get_value(param+3, argv, &i); + if (strcmp("true", arg) != 0) + bad_value(param, param+3, arg); + } + else + bad_param(param, param+2); + break; + } + case 'e': + switch (argv[i][3]) { + case 'a': { + int a; + arg = get_value(argv[i]+4, argv, &i); + if (strcmp("min", arg) == 0) { + for (a = 0; a < aui_sz; a++) + aui[a]->enable = 0; + } + else if (strcmp("max", arg) == 0) { + for (a = 0; a < aui_sz; a++) + aui[a]->enable = 1; + } + else if (strcmp("config", arg) == 0) { + init->erts_alloc_config = 1; + } + else if (strcmp("r9c", arg) == 0 + || strcmp("r10b", arg) == 0 + || strcmp("r11b", arg) == 0) { + set_default_sl_alloc_opts(&init->sl_alloc); + set_default_std_alloc_opts(&init->std_alloc); + set_default_ll_alloc_opts(&init->ll_alloc); + set_default_temp_alloc_opts(&init->temp_alloc); + set_default_eheap_alloc_opts(&init->eheap_alloc); + set_default_binary_alloc_opts(&init->binary_alloc); + set_default_ets_alloc_opts(&init->ets_alloc); + set_default_driver_alloc_opts(&init->driver_alloc); + + init->driver_alloc.enable = 0; + if (strcmp("r9c", arg) == 0) { + init->sl_alloc.enable = 0; + init->std_alloc.enable = 0; + init->binary_alloc.enable = 0; + init->ets_alloc.enable = 0; + } + + for (a = 0; a < aui_sz; a++) { + aui[a]->thr_spec = 0; + aui[a]->init.util.ramv = 0; + aui[a]->init.util.mmmbc = 10; + aui[a]->init.util.lmbcs = 5*1024*1024; + } + } + else { + bad_param(param, param+3); + } + break; + } + default: + bad_param(param, param+1); + } + break; + case 'i': + switch (argv[i][3]) { + case 's': + arg = get_value(argv[i]+4, argv, &i); + if (strcmp("true", arg) == 0) + init->instr.stat = 1; + else if (strcmp("false", arg) == 0) + init->instr.stat = 0; + else + bad_value(param, param+3, arg); + break; + case 'm': + arg = get_value(argv[i]+4, argv, &i); + if (strcmp("true", arg) == 0) + init->instr.map = 1; + else if (strcmp("false", arg) == 0) + init->instr.map = 0; + else + bad_value(param, param+3, arg); + break; + case 't': + init->instr.mtrace = get_value(argv[i]+4, argv, &i); + break; + default: + bad_param(param, param+2); + } + break; + case 'u': + if (has_prefix("ycs", argv[i]+3)) { + init->alloc_util.ycs + = get_kb_value(argv[i]+6, argv, &i); + } + else if (has_prefix("mmc", argv[i]+3)) { + init->alloc_util.mmc + = get_amount_value(argv[i]+6, argv, &i); + } + else { + int a; + int start = i; + char *param = argv[i]; + char *val = i+1 < *argc ? argv[i+1] : NULL; + + for (a = 0; a < aui_sz; a++) { + if (a > 0) { + ASSERT(i == start || i == start+1); + argv[start] = param; + if (i != start) + argv[start + 1] = val; + i = start; + } + handle_au_arg(aui[a], &argv[i][3], argv, &i); + } + } + break; + default: + bad_param(param, param+1); + } + break; + case '-': + if (argv[i][2] == '\0') { + /* End of system flags reached */ + if (init->instr.mtrace + /* || init->instr.stat + || init->instr.map */) { + while (i < *argc) { + if(strcmp(argv[i], "-sname") == 0 + || strcmp(argv[i], "-name") == 0) { + if (i + 1 <*argc) { + init->instr.nodename = argv[i+1]; + break; + } + } + i++; + } + } + goto args_parsed; + } + break; + default: + break; + } + } + i++; + } + + args_parsed: + /* Handled arguments have been marked with NULL. Slide arguments + not handled towards the beginning of argv. */ + for (i = 0, j = 0; i < *argc; i++) { + if (argv[i]) + argv[j++] = argv[i]; + } + *argc = j; + +} + +static char *type_no_str(ErtsAlcType_t n) +{ + +#if ERTS_ALC_N_MIN != 0 + if (n < ERTS_ALC_N_MIN) + return NULL; +#endif + if (n > ERTS_ALC_N_MAX) + return NULL; + return (char *) ERTS_ALC_N2TD(n); +} + +#define type_str(T) type_no_str(ERTS_ALC_T2N((T))) + +erts_tsd_key_t thr_ix_key; +erts_spinlock_t alloc_thr_ix_lock; +int last_thr_ix; +int first_dyn_thr_ix; + +static void +init_thr_ix(int static_ixs) +{ + erts_tsd_key_create(&thr_ix_key); + erts_spinlock_init(&alloc_thr_ix_lock, "alloc_thr_ix_lock"); + last_thr_ix = -4711; + first_dyn_thr_ix = static_ixs+1; +} + +int +erts_alc_get_thr_ix(void) +{ + int ix = (int)(long) erts_tsd_get(thr_ix_key); + if (ix == 0) { + erts_spin_lock(&alloc_thr_ix_lock); + last_thr_ix++; + if (last_thr_ix < 0) + last_thr_ix = first_dyn_thr_ix; + ix = last_thr_ix; + erts_spin_unlock(&alloc_thr_ix_lock); + erts_tsd_set(thr_ix_key, (void *)(long) ix); + } + ASSERT(ix > 0); + return ix; +} + +void erts_alloc_reg_scheduler_id(Uint id) +{ + int ix = (int) id; + ASSERT(0 < ix && ix <= first_dyn_thr_ix); + ASSERT(0 == (int) (long) erts_tsd_get(thr_ix_key)); + erts_tsd_set(thr_ix_key, (void *)(long) ix); +} + +__decl_noreturn void +erts_alc_fatal_error(int error, int func, ErtsAlcType_t n, ...) +{ + char buf[10]; + char *t_str; + char *allctr_str; + + ASSERT(n >= ERTS_ALC_N_MIN); + ASSERT(n <= ERTS_ALC_N_MAX); + + + if (n < ERTS_ALC_N_MIN || ERTS_ALC_N_MAX < n) + allctr_str = "UNKNOWN"; + else { + ErtsAlcType_t a = ERTS_ALC_T2A(ERTS_ALC_N2T(n)); + if (erts_allctrs_info[a].enabled) + allctr_str = (char *) ERTS_ALC_A2AD(a); + else + allctr_str = (char *) ERTS_ALC_A2AD(ERTS_ALC_A_SYSTEM); + } + + t_str = type_no_str(n); + if (!t_str) { + sprintf(buf, "%d", (int) n); + t_str = buf; + } + + switch (error) { + case ERTS_ALC_E_NOTSUP: { + char *op_str; + switch (func) { + case ERTS_ALC_O_ALLOC: op_str = "alloc"; break; + case ERTS_ALC_O_REALLOC: op_str = "realloc"; break; + case ERTS_ALC_O_FREE: op_str = "free"; break; + default: op_str = "UNKNOWN"; break; + } + erl_exit(ERTS_ABORT_EXIT, + "%s: %s operation not supported (memory type: \"%s\")\n", + allctr_str, op_str, t_str); + break; + } + case ERTS_ALC_E_NOMEM: { + Uint size; + va_list argp; + char *op = func == ERTS_ALC_O_REALLOC ? "reallocate" : "allocate"; + + + va_start(argp, n); + size = va_arg(argp, Uint); + va_end(argp); + erl_exit(1, + "%s: Cannot %s %lu bytes of memory (of type \"%s\").\n", + allctr_str, op, size, t_str); + break; + } + case ERTS_ALC_E_NOALLCTR: + erl_exit(ERTS_ABORT_EXIT, + "erts_alloc: Unknown allocator type: %d\n", + ERTS_ALC_T2A(ERTS_ALC_N2T(n))); + break; + default: + erl_exit(ERTS_ABORT_EXIT, "erts_alloc: Unknown error: %d\n", error); + break; + } +} + +__decl_noreturn void +erts_alloc_enomem(ErtsAlcType_t type, Uint size) +{ + erts_alloc_n_enomem(ERTS_ALC_T2N(type), size); +} + +__decl_noreturn void +erts_alloc_n_enomem(ErtsAlcType_t n, Uint size) +{ + erts_alc_fatal_error(ERTS_ALC_E_NOMEM, ERTS_ALC_O_ALLOC, n, size); +} + +__decl_noreturn void +erts_realloc_enomem(ErtsAlcType_t type, void *ptr, Uint size) +{ + erts_realloc_n_enomem(ERTS_ALC_T2N(type), ptr, size); +} + +__decl_noreturn void +erts_realloc_n_enomem(ErtsAlcType_t n, void *ptr, Uint size) +{ + erts_alc_fatal_error(ERTS_ALC_E_NOMEM, ERTS_ALC_O_REALLOC, n, size); +} + +static ERTS_INLINE Uint +alcu_size(ErtsAlcType_t ai) +{ + Uint res = 0; + + ASSERT(erts_allctrs_info[ai].enabled); + ASSERT(erts_allctrs_info[ai].alloc_util); + + if (!erts_allctrs_info[ai].thr_spec) { + Allctr_t *allctr = erts_allctrs_info[ai].extra; + AllctrSize_t asize; + erts_alcu_current_size(allctr, &asize); + res += asize.blocks; + } + else { + ErtsAllocatorThrSpec_t *tspec = &erts_allctr_thr_spec[ai]; + int i; + + ASSERT(tspec->all_thr_safe); + + ASSERT(tspec->enabled); + + for (i = tspec->size - 1; i >= 0; i--) { + Allctr_t *allctr = tspec->allctr[i]; + AllctrSize_t asize; + if (allctr) { + erts_alcu_current_size(allctr, &asize); + res += asize.blocks; + } + } + } + + return res; +} + +Eterm +erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) +{ +#define ERTS_MEM_NEED_ALL_ALCU (!erts_instr_stat && want_tot_or_sys) + ErtsFixInfo efi; + struct { + int total; + int processes; + int processes_used; + int system; + int atom; + int atom_used; + int binary; + int code; + int ets; + int maximum; + } want = {0}; + struct { + Uint total; + Uint processes; + Uint processes_used; + Uint system; + Uint atom; + Uint atom_used; + Uint binary; + Uint code; + Uint ets; + Uint maximum; + } size = {0}; + Eterm atoms[sizeof(size)/sizeof(Uint)]; + Uint *uintps[sizeof(size)/sizeof(Uint)]; + Eterm euints[sizeof(size)/sizeof(Uint)]; + int need_atom; + int want_tot_or_sys; + int length; + Eterm res = THE_NON_VALUE; + ErtsAlcType_t ai; + int only_one_value = 0; + + /* Figure out whats wanted... */ + + length = 0; + if (is_non_value(earg)) { /* i.e. wants all */ + want.total = 1; + atoms[length] = am_total; + uintps[length++] = &size.total; + + want.processes = 1; + atoms[length] = am_processes; + uintps[length++] = &size.processes; + + want.processes_used = 1; + atoms[length] = am_processes_used; + uintps[length++] = &size.processes_used; + + want.system = 1; + atoms[length] = am_system; + uintps[length++] = &size.system; + + want.atom = 1; + atoms[length] = am_atom; + uintps[length++] = &size.atom; + + want.atom_used = 1; + atoms[length] = am_atom_used; + uintps[length++] = &size.atom_used; + + want.binary = 1; + atoms[length] = am_binary; + uintps[length++] = &size.binary; + + want.code = 1; + atoms[length] = am_code; + uintps[length++] = &size.code; + + want.ets = 1; + atoms[length] = am_ets; + uintps[length++] = &size.ets; + + want.maximum = erts_instr_stat; + if (want.maximum) { + atoms[length] = am_maximum; + uintps[length++] = &size.maximum; + } + + } + else { + Eterm tmp_heap[2]; + Eterm wanted_list; + + if (is_nil(earg)) + return NIL; + + if (is_not_atom(earg)) + wanted_list = earg; + else { + wanted_list = CONS(&tmp_heap[0], earg, NIL); + only_one_value = 1; + } + + while (is_list(wanted_list)) { + switch (CAR(list_val(wanted_list))) { + case am_total: + if (!want.total) { + want.total = 1; + atoms[length] = am_total; + uintps[length++] = &size.total; + } + break; + case am_processes: + if (!want.processes) { + want.processes = 1; + atoms[length] = am_processes; + uintps[length++] = &size.processes; + } + break; + case am_processes_used: + if (!want.processes_used) { + want.processes_used = 1; + atoms[length] = am_processes_used; + uintps[length++] = &size.processes_used; + } + break; + case am_system: + if (!want.system) { + want.system = 1; + atoms[length] = am_system; + uintps[length++] = &size.system; + } + break; + case am_atom: + if (!want.atom) { + want.atom = 1; + atoms[length] = am_atom; + uintps[length++] = &size.atom; + } + break; + case am_atom_used: + if (!want.atom_used) { + want.atom_used = 1; + atoms[length] = am_atom_used; + uintps[length++] = &size.atom_used; + } + break; + case am_binary: + if (!want.binary) { + want.binary = 1; + atoms[length] = am_binary; + uintps[length++] = &size.binary; + } + break; + case am_code: + if (!want.code) { + want.code = 1; + atoms[length] = am_code; + uintps[length++] = &size.code; + } + break; + case am_ets: + if (!want.ets) { + want.ets = 1; + atoms[length] = am_ets; + uintps[length++] = &size.ets; + } + break; + case am_maximum: + if (erts_instr_stat) { + if (!want.maximum) { + want.maximum = 1; + atoms[length] = am_maximum; + uintps[length++] = &size.maximum; + } + } + else + return am_badarg; + break; + default: + return am_badarg; + } + wanted_list = CDR(list_val(wanted_list)); + } + if (is_not_nil(wanted_list)) + return am_badarg; + } + + /* All alloc_util allocators *have* to be enabled */ + + for (ai = ERTS_ALC_A_MIN; ai <= ERTS_ALC_A_MAX; ai++) { + switch (ai) { + case ERTS_ALC_A_SYSTEM: + case ERTS_ALC_A_FIXED_SIZE: + break; + default: + if (!erts_allctrs_info[ai].enabled + || !erts_allctrs_info[ai].alloc_util) { + return am_notsup; + } + break; + } + } + + ASSERT(length <= sizeof(atoms)/sizeof(Eterm)); + ASSERT(length <= sizeof(euints)/sizeof(Eterm)); + ASSERT(length <= sizeof(uintps)/sizeof(Uint)); + + + if (proc) { + ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_MAIN + == erts_proc_lc_my_proc_locks(proc)); + /* We'll need locks early in the lock order */ + erts_smp_proc_unlock(proc, ERTS_PROC_LOCK_MAIN); + } + + /* Calculate values needed... */ + + want_tot_or_sys = want.total || want.system; + need_atom = ERTS_MEM_NEED_ALL_ALCU || want.atom; + + if (ERTS_MEM_NEED_ALL_ALCU) { + size.total = 0; + + for (ai = ERTS_ALC_A_MIN; ai <= ERTS_ALC_A_MAX; ai++) { + if (erts_allctrs_info[ai].alloc_util) { + Uint *save; + Uint asz; + switch (ai) { + case ERTS_ALC_A_TEMPORARY: + /* + * Often not thread safe and usually never + * contain any allocated memory. + */ + continue; + case ERTS_ALC_A_EHEAP: + save = &size.processes; + break; + case ERTS_ALC_A_ETS: + save = &size.ets; + break; + case ERTS_ALC_A_BINARY: + save = &size.binary; + break; + default: + save = NULL; + break; + } + asz = alcu_size(ai); + if (save) + *save = asz; + size.total += asz; + } + } + } + + + + if (want_tot_or_sys || want.processes || want.processes_used) { + Uint tmp; + + if (ERTS_MEM_NEED_ALL_ALCU) + tmp = size.processes; + else + tmp = alcu_size(ERTS_ALC_A_EHEAP); + tmp += erts_max_processes*sizeof(Process*); +#ifdef HYBRID + tmp += erts_max_processes*sizeof(Process*); +#endif + tmp += erts_bif_timer_memory_size(); + tmp += erts_tot_link_lh_size(); + + size.processes = size.processes_used = tmp; + + erts_fix_info(ERTS_ALC_T_NLINK_SH, &efi); + size.processes += efi.total; + size.processes_used += efi.used; + + erts_fix_info(ERTS_ALC_T_MONITOR_SH, &efi); + size.processes += efi.total; + size.processes_used += efi.used; + + erts_fix_info(ERTS_ALC_T_PROC, &efi); + size.processes += efi.total; + size.processes_used += efi.used; + + erts_fix_info(ERTS_ALC_T_REG_PROC, &efi); + size.processes += efi.total; + size.processes_used += efi.used; + + } + + if (want.atom || want.atom_used) { + Uint reserved_atom_space, atom_space; + erts_atom_get_text_space_sizes(&reserved_atom_space, &atom_space); + size.atom = size.atom_used = atom_table_sz(); + erts_fix_info(ERTS_ALC_T_ATOM, &efi); + + if (want.atom) { + size.atom += reserved_atom_space; + size.atom += efi.total; + } + + if (want.atom_used) { + size.atom_used += atom_space; + size.atom_used += efi.used; + } + } + + if (!ERTS_MEM_NEED_ALL_ALCU && want.binary) + size.binary = alcu_size(ERTS_ALC_A_BINARY); + + if (want.code) { + size.code = module_table_sz(); + erts_fix_info(ERTS_ALC_T_MODULE, &efi); + size.code += efi.used; + size.code += export_table_sz(); + erts_fix_info(ERTS_ALC_T_EXPORT, &efi); + size.code += efi.used; + size.code += erts_fun_table_sz(); + erts_fix_info(ERTS_ALC_T_FUN_ENTRY, &efi); + size.code += efi.used; + size.code += allocated_modules*sizeof(Range); + size.code += erts_total_code_size; + } + + if (want.ets) { + if (!ERTS_MEM_NEED_ALL_ALCU) + size.ets = alcu_size(ERTS_ALC_A_ETS); + size.ets += erts_get_ets_misc_mem_size(); + } + + if (erts_instr_stat && (want_tot_or_sys || want.maximum)) { + if (want_tot_or_sys) { + size.total = erts_instr_get_total(); + size.system = size.total - size.processes; + } + size.maximum = erts_instr_get_max_total(); + } + else if (want_tot_or_sys) { + size.system = size.total - size.processes; + } + + if (print_to_p) { + int i; + int to = *print_to_p; + void *arg = print_to_arg; + + /* Print result... */ + erts_print(to, arg, "=memory\n"); + for (i = 0; i < length; i++) + erts_print(to, arg, "%T: %bpu\n", atoms[i], *uintps[i]); + } + + if (proc) { + /* Build erlang term result... */ + Uint *hp; + Uint hsz; + + erts_smp_proc_lock(proc, ERTS_PROC_LOCK_MAIN); + + if (only_one_value) { + ASSERT(length == 1); + hsz = 0; + erts_bld_uint(NULL, &hsz, *uintps[0]); + hp = hsz ? HAlloc((Process *) proc, hsz) : NULL; + res = erts_bld_uint(&hp, NULL, *uintps[0]); + } + else { + Uint **hpp = NULL; + Uint *hszp = &hsz; + hsz = 0; + + while (1) { + int i; + for (i = 0; i < length; i++) + euints[i] = erts_bld_uint(hpp, hszp, *uintps[i]); + res = erts_bld_2tup_list(hpp, hszp, length, atoms, euints); + if (hpp) + break; + hp = HAlloc((Process *) proc, hsz); + hpp = &hp; + hszp = NULL; + } + } + } + + return res; + +#undef ERTS_MEM_NEED_ALL_ALCU +} + +struct aa_values { + Uint arity; + const char *name; + Uint ui[2]; +}; + +Eterm +erts_allocated_areas(int *print_to_p, void *print_to_arg, void *proc) +{ +#define MAX_AA_VALUES \ + (20 + (ERTS_ALC_N_MAX_A_FIXED_SIZE - ERTS_ALC_N_MIN_A_FIXED_SIZE + 1)) + + struct aa_values values[MAX_AA_VALUES]; + Eterm res = THE_NON_VALUE; + int i, length; + ErtsFixInfo efi; + Uint reserved_atom_space, atom_space; + + if (proc) { + ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_MAIN + == erts_proc_lc_my_proc_locks(proc)); + + /* We'll need locks early in the lock order */ + erts_smp_proc_unlock(proc, ERTS_PROC_LOCK_MAIN); + } + + i = 0; + + if (erts_instr_stat) { + values[i].arity = 2; + values[i].name = "total"; + values[i].ui[0] = erts_instr_get_total(); + i++; + + values[i].arity = 2; + values[i].name = "maximum"; + values[i].ui[0] = erts_instr_get_max_total(); + i++; + } + + values[i].arity = 2; + values[i].name = "sys_misc"; + values[i].ui[0] = erts_sys_misc_mem_sz(); + i++; + + values[i].arity = 2; + values[i].name = "static"; + values[i].ui[0] = + erts_max_ports*sizeof(Port) /* Port table */ + + erts_timer_wheel_memory_size() /* Timer wheel */ +#ifdef SYS_TMP_BUF_SIZE + + SYS_TMP_BUF_SIZE /* tmp_buf in sys on vxworks & ose */ +#endif + ; + i++; + + erts_atom_get_text_space_sizes(&reserved_atom_space, &atom_space); + + values[i].arity = 3; + values[i].name = "atom_space"; + values[i].ui[0] = reserved_atom_space; + values[i].ui[1] = atom_space; + i++; + + values[i].arity = 2; + values[i].name = "atom_table"; + values[i].ui[0] = atom_table_sz(); + i++; + + values[i].arity = 2; + values[i].name = "module_table"; + values[i].ui[0] = module_table_sz(); + i++; + + values[i].arity = 2; + values[i].name = "export_table"; + values[i].ui[0] = export_table_sz(); + i++; + + values[i].arity = 2; + values[i].name = "register_table"; + values[i].ui[0] = process_reg_sz(); + i++; + + values[i].arity = 2; + values[i].name = "fun_table"; + values[i].ui[0] = erts_fun_table_sz(); + i++; + + values[i].arity = 2; + values[i].name = "module_refs"; + values[i].ui[0] = allocated_modules*sizeof(Range); + i++; + + values[i].arity = 2; + values[i].name = "loaded_code"; + values[i].ui[0] = erts_total_code_size; + i++; + + values[i].arity = 2; + values[i].name = "dist_table"; + values[i].ui[0] = erts_dist_table_size(); + i++; + + values[i].arity = 2; + values[i].name = "node_table"; + values[i].ui[0] = erts_node_table_size(); + i++; + + values[i].arity = 2; + values[i].name = "bits_bufs_size"; + values[i].ui[0] = erts_bits_bufs_size(); + i++; + + values[i].arity = 2; + values[i].name = "bif_timer"; + values[i].ui[0] = erts_bif_timer_memory_size(); + i++; + + values[i].arity = 2; + values[i].name = "link_lh"; + values[i].ui[0] = erts_tot_link_lh_size(); + i++; + + { + Uint n; + + for (n = ERTS_ALC_N_MIN_A_FIXED_SIZE; + n <= ERTS_ALC_N_MAX_A_FIXED_SIZE; + n++) { + erts_fix_info(ERTS_ALC_N2T(n), &efi); + + values[i].arity = 3; + values[i].name = ERTS_ALC_N2TD(n); + values[i].ui[0] = efi.total; + values[i].ui[1] = efi.used; + i++; + } + + } + + length = i; + ASSERT(length <= MAX_AA_VALUES); + + if (print_to_p) { + /* Print result... */ + int to = *print_to_p; + void *arg = print_to_arg; + + erts_print(to, arg, "=allocated_areas\n"); + for (i = 0; i < length; i++) { + switch (values[i].arity) { + case 2: + erts_print(to, arg, "%s: %bpu\n", + values[i].name, values[i].ui[0]); + break; + case 3: + erts_print(to, arg, "%s: %bpu %bpu\n", + values[i].name, values[i].ui[0], values[i].ui[1]); + break; + default: + erts_print(to, arg, "ERROR: internal_error\n"); + ASSERT(0); + return am_internal_error; + } + } + } + + if (proc) { + /* Build erlang term result... */ + Eterm tuples[MAX_AA_VALUES]; + Uint *hp; + Uint **hpp; + Uint hsz; + Uint *hszp; + + erts_smp_proc_lock(proc, ERTS_PROC_LOCK_MAIN); + + hpp = NULL; + hsz = 0; + hszp = &hsz; + + while (1) { + int i; + for (i = 0; i < length; i++) { + Eterm atom; + if (hpp) + atom = am_atom_put(values[i].name, + (int) strlen(values[i].name)); + else + atom = am_true; + + switch (values[i].arity) { + case 2: + tuples[i] = erts_bld_tuple(hpp, hszp, 2, + atom, + erts_bld_uint(hpp, hszp, + values[i].ui[0])); + break; + case 3: + tuples[i] = erts_bld_tuple(hpp, hszp, 3, + atom, + erts_bld_uint(hpp, hszp, + values[i].ui[0]), + erts_bld_uint(hpp, hszp, + values[i].ui[1])); + break; + default: + ASSERT(0); + return am_internal_error; + } + } + res = erts_bld_list(hpp, hszp, length, tuples); + if (hpp) + break; + hp = HAlloc((Process *) proc, hsz); + hpp = &hp; + hszp = NULL; + } + } + + return res; +#undef MAX_AA_VALUES +} + +Eterm +erts_alloc_util_allocators(void *proc) +{ + Eterm res; + Uint *hp; + Uint sz; + int i; + /* + * Currently all allocators except sys_alloc and fix_alloc are + * alloc_util allocators. + */ + sz = ((ERTS_ALC_A_MAX + 1 - ERTS_ALC_A_MIN) - 2)*2; + ASSERT(sz > 0); + hp = HAlloc((Process *) proc, sz); + res = NIL; + for (i = ERTS_ALC_A_MAX; i >= ERTS_ALC_A_MIN; i--) { + switch (i) { + case ERTS_ALC_A_SYSTEM: + case ERTS_ALC_A_FIXED_SIZE: + break; + default: { + char *alc_str = (char *) ERTS_ALC_A2AD(i); + Eterm alc = am_atom_put(alc_str, sys_strlen(alc_str)); + res = CONS(hp, alc, res); + hp += 2; + break; + } + } + } + return res; +} + +Eterm +erts_allocator_info_term(void *proc, Eterm which_alloc, int only_sz) +{ +#define ERTS_AIT_RET(R) \ + do { res = (R); goto done; } while (0) +#define ERTS_AIT_HALLOC(P, S) \ + do { hp = HAlloc((P), (S)); hp_end = hp + (S); } while (0) + + ErtsAlcType_t i; + Uint sz = 0; + Uint *hp = NULL; + Uint *hp_end = NULL; + Eterm res = am_undefined; + + if (is_not_atom(which_alloc)) + goto done; + + for (i = ERTS_ALC_A_MIN; i <= ERTS_ALC_A_MAX; i++) { + if (erts_is_atom_str((char *) ERTS_ALC_A2AD(i), which_alloc)) { + if (!erts_allctrs_info[i].enabled) + ERTS_AIT_RET(am_false); + else { + if (erts_allctrs_info[i].alloc_util) { + Eterm ires, tmp; + Eterm **hpp; + Uint *szp; + Eterm (*info_func)(Allctr_t *, + int, + int *, + void *, + Uint **, + Uint *); + + info_func = (only_sz + ? erts_alcu_sz_info + : erts_alcu_info); + + if (erts_allctrs_info[i].thr_spec) { + ErtsAllocatorThrSpec_t *tspec = &erts_allctr_thr_spec[i]; + int j; + int block_system = !tspec->all_thr_safe; + + if (block_system) { + erts_smp_proc_unlock(proc, ERTS_PROC_LOCK_MAIN); + erts_smp_block_system(0); + } + ASSERT(tspec->enabled); + + szp = &sz; + hpp = NULL; + + while (1) { + ires = NIL; + for (j = tspec->size - 1; j >= 0; j--) { + Allctr_t *allctr = tspec->allctr[j]; + if (allctr) { + tmp = erts_bld_tuple(hpp, + szp, + 3, + erts_bld_atom(hpp, + szp, + "instance"), + make_small((Uint) j), + (*info_func)(allctr, + hpp != NULL, + NULL, + NULL, + hpp, + szp)); + ires = erts_bld_cons(hpp, szp, tmp, ires); + } + } + if (hpp) + break; + ERTS_AIT_HALLOC((Process *) proc, sz); + hpp = &hp; + szp = NULL; + } + + if (block_system) { + erts_smp_release_system(); + erts_smp_proc_lock(proc, ERTS_PROC_LOCK_MAIN); + } + } + else { + Allctr_t *allctr = erts_allctrs_info[i].extra; + szp = &sz; + hpp = NULL; + while (1) { + ires = NIL; + tmp = erts_bld_tuple(hpp, + szp, + 3, + erts_bld_atom(hpp, + szp, + "instance"), + make_small((Uint) 0), + (*info_func)(allctr, + hpp != NULL, + NULL, + NULL, + hpp, + szp)); + ires = erts_bld_cons(hpp, szp, tmp, ires); + if (hpp) + break; + ERTS_AIT_HALLOC((Process *) proc, sz); + hpp = &hp; + szp = NULL; + } + } + ERTS_AIT_RET(ires); + } + else { + Eterm *szp, **hpp; + + switch (i) { + case ERTS_ALC_A_SYSTEM: { + SysAllocStat sas; + Eterm opts_am; + Eterm opts; + Eterm as[4]; + Eterm ts[4]; + int l; + + if (only_sz) + ERTS_AIT_RET(NIL); + + sys_alloc_stat(&sas); + opts_am = am_atom_put("options", 7); + + szp = &sz; + hpp = NULL; + + restart_sys_alloc: + l = 0; + as[l] = am_atom_put("e", 1); + ts[l++] = am_true; +#ifdef ELIB_ALLOC_IS_CLIB + as[l] = am_atom_put("m", 1); + ts[l++] = am_atom_put("elib", 4); +#else + as[l] = am_atom_put("m", 1); + ts[l++] = am_atom_put("libc", 4); +#endif + if(sas.trim_threshold >= 0) { + as[l] = am_atom_put("tt", 2); + ts[l++] = erts_bld_uint(hpp, szp, + (Uint) sas.trim_threshold); + } + if(sas.top_pad >= 0) { + as[l] = am_atom_put("tp", 2); + ts[l++] = erts_bld_uint(hpp, szp, (Uint) sas.top_pad); + } + + opts = erts_bld_2tup_list(hpp, szp, l, as, ts); + res = erts_bld_2tup_list(hpp, szp, 1, &opts_am, &opts); + + if (szp) { + ERTS_AIT_HALLOC((Process *) proc, sz); + szp = NULL; + hpp = &hp; + goto restart_sys_alloc; + } + ERTS_AIT_RET(res); + } + case ERTS_ALC_A_FIXED_SIZE: { + ErtsAlcType_t n; + Eterm as[2], vs[2]; + + if (only_sz) + ERTS_AIT_RET(NIL); + + as[0] = am_atom_put("options", 7); + as[1] = am_atom_put("pools", 5); + + szp = &sz; + hpp = NULL; + + restart_fix_alloc: + + vs[0] = erts_bld_cons(hpp, szp, + erts_bld_tuple(hpp, szp, 2, + am_atom_put("e", + 1), + am_true), + NIL); + + vs[1] = NIL; + for (n = ERTS_ALC_N_MIN_A_FIXED_SIZE; + n <= ERTS_ALC_N_MAX_A_FIXED_SIZE; + n++) { + ErtsFixInfo efi; + erts_fix_info(ERTS_ALC_N2T(n), &efi); + + vs[1] = erts_bld_cons( + hpp, szp, + erts_bld_tuple( + hpp, szp, 3, + am_atom_put((char *) ERTS_ALC_N2TD(n), + strlen(ERTS_ALC_N2TD(n))), + erts_bld_uint(hpp, szp, efi.total), + erts_bld_uint(hpp, szp, efi.used)), + vs[1]); + + } + + res = erts_bld_2tup_list(hpp, szp, 2, as, vs); + if (szp) { + ERTS_AIT_HALLOC((Process *) proc, sz); + szp = NULL; + hpp = &hp; + goto restart_fix_alloc; + } + ERTS_AIT_RET(res); + } + default: + ASSERT(0); + goto done; + } + } + } + } + } + + if (ERTS_IS_ATOM_STR("mseg_alloc", which_alloc)) { +#if HAVE_ERTS_MSEG + if (only_sz) + ERTS_AIT_RET(NIL); + erts_mseg_info(NULL, NULL, 0, NULL, &sz); + if (sz) + ERTS_AIT_HALLOC((Process *) proc, sz); + ERTS_AIT_RET(erts_mseg_info(NULL, NULL, 1, &hp, NULL)); +#else + ERTS_AIT_RET(am_false); +#endif + + } + else if (ERTS_IS_ATOM_STR("alloc_util", which_alloc)) { + if (only_sz) + ERTS_AIT_RET(NIL); + erts_alcu_au_info_options(NULL, NULL, NULL, &sz); + if (sz) + ERTS_AIT_HALLOC((Process *) proc, sz); + ERTS_AIT_RET(erts_alcu_au_info_options(NULL, NULL, &hp, NULL)); + } + + done: + if (hp) { + ASSERT(hp_end >= hp); + HRelease((Process *) proc, hp_end, hp); + } + return res; + +#undef ERTS_AIT_RET +#undef ERTS_AIT_HALLOC +} + +void +erts_allocator_info(int to, void *arg) +{ + ErtsAlcType_t a; + + ERTS_SMP_LC_ASSERT(erts_smp_is_system_blocked(0) + || (ERTS_IS_CRASH_DUMPING + && erts_smp_is_system_blocked(ERTS_BS_FLG_ALLOW_GC))); + + for (a = ERTS_ALC_A_MIN; a <= ERTS_ALC_A_MAX; a++) { + int ai; + for (ai = 0; ai == 0 || ai < erts_allctrs_info[a].thr_spec; ai++) { + if (erts_allctrs_info[a].thr_spec) { + if (!erts_allctr_thr_spec[a].allctr[ai]) + continue; + erts_print(to, arg, "=allocator:%s[%d]\n", + ERTS_ALC_A2AD(a), ai); + } + else { + erts_print(to, arg, "=allocator:%s\n", ERTS_ALC_A2AD(a)); + } + if (!erts_allctrs_info[a].enabled) + erts_print(to, arg, "option e: false\n"); + else { + if (erts_allctrs_info[a].alloc_util) { + void *as; + if (!erts_allctrs_info[a].thr_spec) + as = erts_allctrs_info[a].extra; + else { + ASSERT(erts_allctr_thr_spec[a].enabled); + as = erts_allctr_thr_spec[a].allctr[ai]; + } + /* Binary alloc has its own thread safety... */ + erts_alcu_info(as, 0, &to, arg, NULL, NULL); + } + else { + switch (a) { + case ERTS_ALC_A_SYSTEM: { + SysAllocStat sas; + erts_print(to, arg, "option e: true\n"); +#ifdef ELIB_ALLOC_IS_CLIB + erts_print(to, arg, "option m: elib\n"); +#else + erts_print(to, arg, "option m: libc\n"); +#endif + sys_alloc_stat(&sas); + if(sas.trim_threshold >= 0) + erts_print(to, arg, "option tt: %d\n", sas.trim_threshold); + if(sas.top_pad >= 0) + erts_print(to, arg, "option tp: %d\n", sas.top_pad); + break; + } + case ERTS_ALC_A_FIXED_SIZE: { + ErtsAlcType_t n; + erts_print(to, arg, "option e: true\n"); + + for (n = ERTS_ALC_N_MIN_A_FIXED_SIZE; + n <= ERTS_ALC_N_MAX_A_FIXED_SIZE; + n++) { + ErtsFixInfo efi; + erts_fix_info(ERTS_ALC_N2T(n), &efi); + erts_print(to, arg, "%s: %lu %lu\n", + ERTS_ALC_N2TD(n), + efi.total, + efi.used); + } + break; + } + default: + ASSERT(0); + break; + } + } + } + } + } + +#if HAVE_ERTS_MSEG + erts_print(to, arg, "=allocator:mseg_alloc\n"); + erts_mseg_info(&to, arg, 0, NULL, NULL); +#endif + + erts_print(to, arg, "=allocator:alloc_util\n"); + erts_alcu_au_info_options(&to, arg, NULL, NULL); + + erts_print(to, arg, "=allocator:instr\n"); + erts_print(to, arg, "option m: %s\n", + erts_instr_memory_map ? "true" : "false"); + erts_print(to, arg, "option s: %s\n", + erts_instr_stat ? "true" : "false"); + erts_print(to, arg, "option t: %s\n", + erts_mtrace_enabled ? "true" : "false"); + +} + +Eterm +erts_allocator_options(void *proc) +{ +#if HAVE_ERTS_MSEG + int use_mseg = 0; +#endif + Uint sz, *szp, *hp, **hpp; + Eterm res, features, settings; + Eterm atoms[ERTS_ALC_A_MAX-ERTS_ALC_A_MIN+5]; + Uint terms[ERTS_ALC_A_MAX-ERTS_ALC_A_MIN+5]; + int a, length; + SysAllocStat sas; + Uint *endp = NULL; + + sys_alloc_stat(&sas); + + /* First find out the heap size needed ... */ + hpp = NULL; + szp = &sz; + sz = 0; + + bld_term: + + length = 0; + features = NIL; + settings = NIL; + + for (a = ERTS_ALC_A_MIN; a <= ERTS_ALC_A_MAX; a++) { + Eterm tmp = NIL; + atoms[length] = am_atom_put((char *) ERTS_ALC_A2AD(a), + strlen(ERTS_ALC_A2AD(a))); + if (erts_allctrs_info[a].enabled) { + if (erts_allctrs_info[a].alloc_util) { + Allctr_t *allctr; +#if HAVE_ERTS_MSEG + use_mseg++; +#endif + if (erts_allctr_thr_spec[a].enabled) + allctr = erts_allctr_thr_spec[a].allctr[1]; + else + allctr = erts_allctrs_info[a].extra; + tmp = erts_alcu_info_options(allctr, NULL, NULL, hpp, szp); + } + else { + int l = 0; + Eterm as[4]; + Eterm ts[4]; + + as[l] = am_atom_put("e", 1); + ts[l++] = am_true; + + switch (a) { + case ERTS_ALC_A_SYSTEM: +#ifdef ELIB_ALLOC_IS_CLIB + as[l] = am_atom_put("m", 1); + ts[l++] = am_atom_put("elib", 4); +#else + as[l] = am_atom_put("m", 1); + ts[l++] = am_atom_put("libc", 4); +#endif + if(sas.trim_threshold >= 0) { + as[l] = am_atom_put("tt", 2); + ts[l++] = erts_bld_uint(hpp, szp, + (Uint) sas.trim_threshold); + } + if(sas.top_pad >= 0) { + as[l] = am_atom_put("tp", 2); + ts[l++] = erts_bld_uint(hpp, szp, (Uint) sas.top_pad); + } + break; + default: + break; + } + + tmp = erts_bld_2tup_list(hpp, szp, l, as, ts); + + } + + } + else { + Eterm atom = am_atom_put("e", 1); + Eterm term = am_false; + tmp = erts_bld_2tup_list(hpp, szp, 1, &atom, &term); + } + + terms[length++] = tmp; + + } + +#if HAVE_ERTS_MSEG + if (use_mseg) { + atoms[length] = am_atom_put("mseg_alloc", 10); + terms[length++] = erts_mseg_info_options(NULL, NULL, hpp, szp); + } +#endif + + atoms[length] = am_atom_put("alloc_util", 10); + terms[length++] = erts_alcu_au_info_options(NULL, NULL, hpp, szp); + + { + Eterm o[3], v[3]; + o[0] = am_atom_put("m", 1); + v[0] = erts_instr_memory_map ? am_true : am_false; + o[1] = am_atom_put("s", 1); + v[1] = erts_instr_stat ? am_true : am_false; + o[2] = am_atom_put("t", 1); + v[2] = erts_mtrace_enabled ? am_true : am_false; + + atoms[length] = am_atom_put("instr", 5); + terms[length++] = erts_bld_2tup_list(hpp, szp, 3, o, v); + } + + settings = erts_bld_2tup_list(hpp, szp, length, atoms, terms); + + length = 0; + + for (a = ERTS_ALC_A_MIN; a <= ERTS_ALC_A_MAX; a++) { + if (erts_allctrs_info[a].enabled) { + terms[length++] = am_atom_put((char *) ERTS_ALC_A2AD(a), + strlen(ERTS_ALC_A2AD(a))); + } + } + +#if HAVE_ERTS_MSEG + if (use_mseg) + terms[length++] = am_atom_put("mseg_alloc", 10); +#endif + + features = length ? erts_bld_list(hpp, szp, length, terms) : NIL; + +#if defined(ELIB_ALLOC_IS_CLIB) + { + Eterm version; + int i; + int ver[5]; + i = sscanf(ERLANG_VERSION, + "%d.%d.%d.%d.%d", + &ver[0], &ver[1], &ver[2], &ver[3], &ver[4]); + + version = NIL; + for(i--; i >= 0; i--) + version = erts_bld_cons(hpp, szp, make_small(ver[i]), version); + + res = erts_bld_tuple(hpp, szp, 4, + am_elib_malloc, version, features, settings); + } +#elif defined(__GLIBC__) + { + Eterm AM_glibc = am_atom_put("glibc", 5); + Eterm version; + + version = erts_bld_cons(hpp, + szp, + make_small(__GLIBC__), +#ifdef __GLIBC_MINOR__ + erts_bld_cons(hpp, + szp, + make_small(__GLIBC_MINOR__), + NIL) +#else + NIL +#endif + ); + + res = erts_bld_tuple(hpp, szp, 4, + AM_glibc, version, features, settings); + } + +#else /* unknown allocator */ + + res = erts_bld_tuple(hpp, szp, 4, + am_undefined, NIL, features, settings); + +#endif + + if (szp) { + /* ... and then build the term */ + hp = HAlloc((Process *) proc, sz); + endp = hp + sz; + hpp = &hp; + szp = NULL; + goto bld_term; + } + + ASSERT(endp >= hp); + HRelease((Process *) proc, endp, hp); + + return res; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ + * Deprecated functions * + * * + * These functions are still defined since "non-OTP linked in drivers" may * + * contain (illegal) calls to them. * +\* */ + +/* --- DO *NOT* USE THESE FUNCTIONS --- */ + +void *sys_alloc(Uint sz) +{ return erts_alloc_fnf(ERTS_ALC_T_UNDEF, sz); } +void *sys_realloc(void *ptr, Uint sz) +{ return erts_realloc_fnf(ERTS_ALC_T_UNDEF, ptr, sz); } +void sys_free(void *ptr) +{ erts_free(ERTS_ALC_T_UNDEF, ptr); } +void *safe_alloc(Uint sz) +{ return erts_alloc(ERTS_ALC_T_UNDEF, sz); } +void *safe_realloc(void *ptr, Uint sz) +{ return erts_realloc(ERTS_ALC_T_UNDEF, ptr, sz); } + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ + * NOTE: erts_alc_test() is only supposed to be used for testing. * + * * + * Keep alloc_SUITE_data/allocator_test.h updated if changes are made * + * to erts_alc_test() * +\* */ +#define ERTS_ALC_TEST_ABORT erl_exit(ERTS_ABORT_EXIT, "%s:%d: Internal error\n") + +unsigned long erts_alc_test(unsigned long op, + unsigned long a1, + unsigned long a2, + unsigned long a3) +{ + switch (op >> 8) { + case 0x0: return erts_alcu_test(op, a1, a2); + case 0x1: return erts_gfalc_test(op, a1, a2); + case 0x2: return erts_bfalc_test(op, a1, a2); + case 0x3: return erts_afalc_test(op, a1, a2); + case 0x4: return erts_mseg_test(op, a1, a2, a3); + case 0xf: + switch (op) { + case 0xf00: +#ifdef USE_THREADS + if (((Allctr_t *) a1)->thread_safe) + return (unsigned long) erts_alcu_alloc_ts(ERTS_ALC_T_UNDEF, + (void *) a1, + (Uint) a2); + else +#endif + return (unsigned long) erts_alcu_alloc(ERTS_ALC_T_UNDEF, + (void *) a1, + (Uint) a2); + case 0xf01: +#ifdef USE_THREADS + if (((Allctr_t *) a1)->thread_safe) + return (unsigned long) erts_alcu_realloc_ts(ERTS_ALC_T_UNDEF, + (void *) a1, + (void *) a2, + (Uint) a3); + else +#endif + return (unsigned long) erts_alcu_realloc(ERTS_ALC_T_UNDEF, + (void *) a1, + (void *) a2, + (Uint) a3); + case 0xf02: +#ifdef USE_THREADS + if (((Allctr_t *) a1)->thread_safe) + erts_alcu_free_ts(ERTS_ALC_T_UNDEF, (void *) a1, (void *) a2); + else +#endif + erts_alcu_free(ERTS_ALC_T_UNDEF, (void *) a1, (void *) a2); + return 0; + case 0xf03: { + Allctr_t *allctr; + struct au_init init; + + SET_DEFAULT_ALLOC_OPTS(&init); + init.enable = 1; + init.atype = GOODFIT; + init.init.util.name_prefix = (char *) a1; + init.init.util.ts = a2 ? 1 : 0; + + if ((char **) a3) { + char **argv = (char **) a3; + int i = 0; + while (argv[i]) { + if (argv[i][0] == '-' && argv[i][1] == 't') + handle_au_arg(&init, &argv[i][2], argv, &i); + else + return (unsigned long) NULL; + i++; + } + } + + switch (init.atype) { + case GOODFIT: + allctr = erts_gfalc_start((GFAllctr_t *) + erts_alloc(ERTS_ALC_T_UNDEF, + sizeof(GFAllctr_t)), + &init.init.gf, + &init.init.util); + break; + case BESTFIT: + allctr = erts_bfalc_start((BFAllctr_t *) + erts_alloc(ERTS_ALC_T_UNDEF, + sizeof(BFAllctr_t)), + &init.init.bf, + &init.init.util); + break; + case AFIT: + allctr = erts_afalc_start((AFAllctr_t *) + erts_alloc(ERTS_ALC_T_UNDEF, + sizeof(AFAllctr_t)), + &init.init.af, + &init.init.util); + break; + default: + ASSERT(0); + allctr = NULL; + break; + } + + return (unsigned long) allctr; + } + case 0xf04: + erts_alcu_stop((Allctr_t *) a1); + erts_free(ERTS_ALC_T_UNDEF, (void *) a1); + break; +#ifdef USE_THREADS + case 0xf05: return (unsigned long) 1; + case 0xf06: return (unsigned long) ((Allctr_t *) a1)->thread_safe; +#ifdef ETHR_NO_FORKSAFETY + case 0xf07: return (unsigned long) 0; +#else + case 0xf07: return (unsigned long) ((Allctr_t *) a1)->thread_safe; +#endif + case 0xf08: { + ethr_mutex *mtx = erts_alloc(ERTS_ALC_T_UNDEF, sizeof(ethr_mutex)); + if (ethr_mutex_init(mtx) != 0) + ERTS_ALC_TEST_ABORT; + return (unsigned long) mtx; + } + case 0xf09: { + ethr_mutex *mtx = (ethr_mutex *) a1; + if (ethr_mutex_destroy(mtx) != 0) + ERTS_ALC_TEST_ABORT; + erts_free(ERTS_ALC_T_UNDEF, (void *) mtx); + break; + } + case 0xf0a: + if (ethr_mutex_lock((ethr_mutex *) a1) != 0) + ERTS_ALC_TEST_ABORT; + break; + case 0xf0b: + if (ethr_mutex_unlock((ethr_mutex *) a1) != 0) + ERTS_ALC_TEST_ABORT; + break; + case 0xf0c: { + ethr_cond *cnd = erts_alloc(ERTS_ALC_T_UNDEF, sizeof(ethr_cond)); + if (ethr_cond_init(cnd) != 0) + ERTS_ALC_TEST_ABORT; + return (unsigned long) cnd; + } + case 0xf0d: { + ethr_cond *cnd = (ethr_cond *) a1; + if (ethr_cond_destroy(cnd) != 0) + ERTS_ALC_TEST_ABORT; + erts_free(ERTS_ALC_T_UNDEF, (void *) cnd); + break; + } + case 0xf0e: + if (ethr_cond_broadcast((ethr_cond *) a1) != 0) + ERTS_ALC_TEST_ABORT; + break; + case 0xf0f: { + int res; + do { + res = ethr_cond_wait((ethr_cond *) a1, (ethr_mutex *) a2); + } while (res == EINTR); + if (res != 0) + ERTS_ALC_TEST_ABORT; + break; + } + case 0xf10: { + ethr_tid *tid = erts_alloc(ERTS_ALC_T_UNDEF, sizeof(ethr_tid)); +#ifdef ERTS_ENABLE_LOCK_COUNT + if (erts_lcnt_thr_create(tid, + (void * (*)(void *)) a1, + (void *) a2, + NULL) != 0) +#else + if (ethr_thr_create(tid, + (void * (*)(void *)) a1, + (void *) a2, + NULL) != 0) +#endif + ERTS_ALC_TEST_ABORT; + return (unsigned long) tid; + } + case 0xf11: { + ethr_tid *tid = (ethr_tid *) a1; + if (ethr_thr_join(*tid, NULL) != 0) + ERTS_ALC_TEST_ABORT; + erts_free(ERTS_ALC_T_UNDEF, (void *) tid); + break; + } + case 0xf12: + ethr_thr_exit((void *) a1); + ERTS_ALC_TEST_ABORT; + break; +#endif /* #ifdef USE_THREADS */ + default: + break; + } + return (unsigned long) 0; + default: + break; + } + + ASSERT(0); + return ~((unsigned long) 0); +} + +#ifdef DEBUG +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ + * Debug stuff * +\* */ + +#if 0 +#define PRINT_OPS +#else +#undef PRINT_OPS +#endif + + +#define FENCE_SZ (3*sizeof(Uint)) + +#ifdef ARCH_64 +#define FENCE_PATTERN 0xABCDEF97ABCDEF97 +#else +#define FENCE_PATTERN 0xABCDEF97 +#endif + +#define TYPE_PATTERN_MASK ERTS_ALC_N_MASK +#define TYPE_PATTERN_SHIFT 16 + +#define FIXED_FENCE_PATTERN_MASK \ + (~((Uint) (TYPE_PATTERN_MASK << TYPE_PATTERN_SHIFT))) +#define FIXED_FENCE_PATTERN \ + (FENCE_PATTERN & FIXED_FENCE_PATTERN_MASK) + +#define MK_PATTERN(T) \ + (FIXED_FENCE_PATTERN | (((T) & TYPE_PATTERN_MASK) << TYPE_PATTERN_SHIFT)) + +#define GET_TYPE_OF_PATTERN(P) \ + (((P) >> TYPE_PATTERN_SHIFT) & TYPE_PATTERN_MASK) + + +static void * +set_memory_fence(void *ptr, Uint sz, ErtsAlcType_t n) +{ + Uint *ui_ptr; + Uint pattern; + + if (!ptr) + return NULL; + + ui_ptr = (Uint *) ptr; + pattern = MK_PATTERN(n); + + *(ui_ptr++) = sz; + *(ui_ptr++) = pattern; + memcpy((void *) (((char *) ui_ptr)+sz), (void *) &pattern, sizeof(Uint)); + + return (void *) ui_ptr; +} + +static void * +check_memory_fence(void *ptr, Uint *size, ErtsAlcType_t n, int func) +{ + Uint sz; + Uint found_type; + Uint pre_pattern; + Uint post_pattern; + Uint *ui_ptr; + + if (!ptr) + return NULL; + + ui_ptr = (Uint *) ptr; + pre_pattern = *(--ui_ptr); + *size = sz = *(--ui_ptr); + + found_type = GET_TYPE_OF_PATTERN(pre_pattern); + if (pre_pattern != MK_PATTERN(n)) { + if ((FIXED_FENCE_PATTERN_MASK & pre_pattern) != FIXED_FENCE_PATTERN) + erl_exit(ERTS_ABORT_EXIT, + "ERROR: Fence at beginning of memory block (p=0x%u) " + "clobbered.\n", + (unsigned long) ptr); + } + + memcpy((void *) &post_pattern, (void *) (((char *)ptr)+sz), sizeof(Uint)); + + if (post_pattern != MK_PATTERN(n) + || pre_pattern != post_pattern) { + char fbuf[10]; + char obuf[10]; + char *ftype; + char *otype; + char *op_str; + + if ((FIXED_FENCE_PATTERN_MASK & post_pattern) != FIXED_FENCE_PATTERN) + erl_exit(ERTS_ABORT_EXIT, + "ERROR: Fence at end of memory block (p=0x%u, sz=%u) " + "clobbered.\n", + (unsigned long) ptr, (unsigned long) sz); + if (found_type != GET_TYPE_OF_PATTERN(post_pattern)) + erl_exit(ERTS_ABORT_EXIT, + "ERROR: Fence around memory block (p=0x%u, sz=%u) " + "clobbered.\n", + (unsigned long) ptr, (unsigned long) sz); + + ftype = type_no_str(found_type); + if (!ftype) { + sprintf(fbuf, "%d", (int) found_type); + ftype = fbuf; + } + otype = type_no_str(n); + if (!otype) { + sprintf(obuf, "%d", (int) n); + otype = obuf; + } + + switch (func) { + case ERTS_ALC_O_ALLOC: op_str = "allocated"; break; + case ERTS_ALC_O_REALLOC: op_str = "reallocated"; break; + case ERTS_ALC_O_FREE: op_str = "freed"; break; + default: op_str = "???"; break; + } + + erl_exit(ERTS_ABORT_EXIT, + "ERROR: Memory block (p=0x%u, sz=%u) allocated as type \"%s\"," + " but %s as type \"%s\".\n", + (unsigned long) ptr, (unsigned long) sz, ftype, op_str, otype); + } + + return (void *) ui_ptr; +} + +static ErtsAllocatorFunctions_t real_allctrs[ERTS_ALC_A_MAX+1]; + +static void * +debug_alloc(ErtsAlcType_t n, void *extra, Uint size) +{ + ErtsAllocatorFunctions_t *real_af = (ErtsAllocatorFunctions_t *) extra; + Uint dsize; + void *res; + + ASSERT(ERTS_ALC_N_MIN <= n && n <= ERTS_ALC_N_MAX); + dsize = size + FENCE_SZ; + res = (*real_af->alloc)(n, real_af->extra, dsize); + + res = set_memory_fence(res, size, n); + +#ifdef PRINT_OPS + fprintf(stderr, "0x%lx = alloc(%s, %lu)\r\n", + (Uint) res, ERTS_ALC_N2TD(n), size); +#endif + + return res; +} + + +static void * +debug_realloc(ErtsAlcType_t n, void *extra, void *ptr, Uint size) +{ + ErtsAllocatorFunctions_t *real_af = (ErtsAllocatorFunctions_t *) extra; + Uint dsize; + Uint old_size; + void *dptr; + void *res; + + ASSERT(ERTS_ALC_N_MIN <= n && n <= ERTS_ALC_N_MAX); + + dsize = size + FENCE_SZ; + dptr = check_memory_fence(ptr, &old_size, n, ERTS_ALC_O_REALLOC); + + if (old_size > size) + sys_memset((void *) (((char *) ptr) + size), + 0xf, + sizeof(Uint) + old_size - size); + + res = (*real_af->realloc)(n, real_af->extra, dptr, dsize); + + res = set_memory_fence(res, size, n); + +#ifdef PRINT_OPS + fprintf(stderr, "0x%lx = realloc(%s, 0x%lx, %lu)\r\n", + (Uint) res, ERTS_ALC_N2TD(n), (Uint) ptr, size); +#endif + + return res; +} + +static void +debug_free(ErtsAlcType_t n, void *extra, void *ptr) +{ + ErtsAllocatorFunctions_t *real_af = (ErtsAllocatorFunctions_t *) extra; + void *dptr; + Uint size; + + ASSERT(ERTS_ALC_N_MIN <= n && n <= ERTS_ALC_N_MAX); + + dptr = check_memory_fence(ptr, &size, n, ERTS_ALC_O_FREE); + + sys_memset((void *) dptr, n, size + FENCE_SZ); + + (*real_af->free)(n, real_af->extra, dptr); + +#ifdef PRINT_OPS + fprintf(stderr, "free(%s, 0x%lx)\r\n", ERTS_ALC_N2TD(n), (Uint) ptr); +#endif + +} + +static Uint +install_debug_functions(void) +{ + int i; + ASSERT(sizeof(erts_allctrs) == sizeof(real_allctrs)); + + sys_memcpy((void *)real_allctrs,(void *)erts_allctrs,sizeof(erts_allctrs)); + + for (i = ERTS_ALC_A_MIN; i <= ERTS_ALC_A_MAX; i++) { + erts_allctrs[i].alloc = debug_alloc; + erts_allctrs[i].realloc = debug_realloc; + erts_allctrs[i].free = debug_free; + erts_allctrs[i].extra = (void *) &real_allctrs[i]; + } + return FENCE_SZ; +} + + + +#endif /* #ifdef DEBUG */ |