diff options
Diffstat (limited to 'erts/emulator/beam/erl_init.c')
-rw-r--r-- | erts/emulator/beam/erl_init.c | 1461 |
1 files changed, 1461 insertions, 0 deletions
diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c new file mode 100644 index 0000000000..8afd349b85 --- /dev/null +++ b/erts/emulator/beam/erl_init.c @@ -0,0 +1,1461 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1997-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% + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "sys.h" +#include <ctype.h> +#include "erl_vm.h" +#include "global.h" +#include "erl_process.h" +#include "error.h" +#include "erl_version.h" +#include "erl_db.h" +#include "beam_bp.h" +#include "erl_bits.h" +#include "erl_binary.h" +#include "dist.h" +#include "erl_mseg.h" +#include "erl_nmgc.h" +#include "erl_threads.h" +#include "erl_bif_timer.h" +#include "erl_instrument.h" +#include "erl_printf_term.h" +#include "erl_misc_utils.h" +#include "packet_parser.h" + +#ifdef HIPE +#include "hipe_mode_switch.h" /* for hipe_mode_switch_init() */ +#include "hipe_signal.h" /* for hipe_signal_init() */ +#endif + +#ifdef HAVE_SYS_RESOURCE_H +# include <sys/resource.h> +#endif + +/* + * Note about VxWorks: All variables must be initialized by executable code, + * not by an initializer. Otherwise a new instance of the emulator will + * inherit previous values. + */ + +extern void erl_crash_dump_v(char *, int, char *, va_list); +#ifdef __WIN32__ +extern void ConNormalExit(void); +extern void ConWaitForExit(void); +#endif + +#define ERTS_MIN_COMPAT_REL 7 + +#ifdef ERTS_SMP +erts_smp_atomic_t erts_writing_erl_crash_dump; +#else +volatile int erts_writing_erl_crash_dump = 0; +#endif +int erts_initialized = 0; + +#if defined(USE_THREADS) && !defined(ERTS_SMP) +static erts_tid_t main_thread; +#endif + +erts_cpu_info_t *erts_cpuinfo; + +int erts_use_sender_punish; + +/* + * Configurable parameters. + */ + +Uint display_items; /* no of items to display in traces etc */ +Uint display_loads; /* print info about loaded modules */ +int H_MIN_SIZE; /* The minimum heap grain */ + +Uint32 erts_debug_flags; /* Debug flags. */ +#ifdef ERTS_OPCODE_COUNTER_SUPPORT +int count_instructions; +#endif +int erts_backtrace_depth; /* How many functions to show in a backtrace + * in error codes. + */ + +int erts_async_max_threads; /* number of threads for async support */ +int erts_async_thread_suggested_stack_size; +erts_smp_atomic_t erts_max_gen_gcs; + +Eterm erts_error_logger_warnings; /* What to map warning logs to, am_error, + am_info or am_warning, am_error is + the default for BC */ + +int erts_compat_rel; + +static int use_multi_run_queue; +static int no_schedulers; +static int no_schedulers_online; + +#ifdef DEBUG +Uint32 verbose; /* See erl_debug.h for information about verbose */ +#endif + +int erts_disable_tolerant_timeofday; /* Time correction can be disabled it is + * not and/or it is too slow. + */ + +int erts_modified_timing_level; + +int erts_no_crash_dump = 0; /* Use -d to suppress crash dump. */ + +/* + * Other global variables. + */ + +ErtsModifiedTimings erts_modified_timings[] = { + /* 0 */ {make_small(0), CONTEXT_REDS, INPUT_REDUCTIONS}, + /* 1 */ {make_small(0), 2*CONTEXT_REDS, 2*INPUT_REDUCTIONS}, + /* 2 */ {make_small(0), CONTEXT_REDS/2, INPUT_REDUCTIONS/2}, + /* 3 */ {make_small(0), 3*CONTEXT_REDS, 3*INPUT_REDUCTIONS}, + /* 4 */ {make_small(0), CONTEXT_REDS/3, 3*INPUT_REDUCTIONS}, + /* 5 */ {make_small(0), 4*CONTEXT_REDS, INPUT_REDUCTIONS/2}, + /* 6 */ {make_small(1), CONTEXT_REDS/4, 2*INPUT_REDUCTIONS}, + /* 7 */ {make_small(1), 5*CONTEXT_REDS, INPUT_REDUCTIONS/3}, + /* 8 */ {make_small(10), CONTEXT_REDS/5, 3*INPUT_REDUCTIONS}, + /* 9 */ {make_small(10), 6*CONTEXT_REDS, INPUT_REDUCTIONS/4} +}; + +#define ERTS_MODIFIED_TIMING_LEVELS \ + (sizeof(erts_modified_timings)/sizeof(ErtsModifiedTimings)) + +Export *erts_delay_trap = NULL; + +int erts_use_r9_pids_ports; + +#ifdef HYBRID +Eterm *global_heap; +Eterm *global_hend; +Eterm *global_htop; +Eterm *global_saved_htop; +Eterm *global_old_heap; +Eterm *global_old_hend; +ErlOffHeap erts_global_offheap; +Uint global_heap_sz = SH_DEFAULT_SIZE; + +#ifndef INCREMENTAL +Eterm *global_high_water; +Eterm *global_old_htop; +#endif + +Uint16 global_gen_gcs; +Uint16 global_max_gen_gcs; +Uint global_gc_flags; + +Uint global_heap_min_sz = SH_DEFAULT_SIZE; +#endif + +int ignore_break; +int replace_intr; + +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 char* +progname(char *fullname) +{ + int i; + + i = strlen(fullname); + while (i >= 0) { + if ((fullname[i] != '/') && (fullname[i] != '\\')) + i--; + else + break; + } + return fullname+i+1; +} + +static int +this_rel_num(void) +{ + static int this_rel = -1; + + if (this_rel < 1) { + int i; + char this_rel_str[] = ERLANG_OTP_RELEASE; + + i = 0; + while (this_rel_str[i] && !isdigit((int) this_rel_str[i])) + i++; + this_rel = atoi(&this_rel_str[i]); + if (this_rel < 1) + erl_exit(-1, "Unexpected ERLANG_OTP_RELEASE format\n"); + } + return this_rel; +} + +/* + * Common error printout function, all error messages + * that don't go to the error logger go through here. + */ + +void erl_error(char *fmt, va_list args) +{ + erts_vfprintf(stderr, fmt, args); +} + +static void early_init(int *argc, char **argv); + +void +erts_short_init(void) +{ + early_init(NULL, NULL); + erl_init(); + erts_initialized = 1; +} + +void +erl_init(void) +{ + init_benchmarking(); + +#ifdef ERTS_SMP + erts_system_block_init(); +#endif + + erts_init_monitors(); + erts_init_gc(); + init_time(); + erts_init_process(); + erts_init_scheduling(use_multi_run_queue, + no_schedulers, + no_schedulers_online); + + H_MIN_SIZE = erts_next_heap_size(H_MIN_SIZE, 0); + + erts_init_trace(); + erts_init_binary(); + erts_init_bits(); + erts_init_fun_table(); + init_atom_table(); + init_export_table(); + init_module_table(); + init_register_table(); + init_message(); + erts_bif_info_init(); + erts_ddll_init(); + init_emulator(); + erts_bp_init(); + init_db(); /* Must be after init_emulator */ + erts_bif_timer_init(); + erts_init_node_tables(); + init_dist(); + erl_drv_thr_init(); + init_io(); + init_copy(); + init_load(); + erts_init_bif(); + erts_init_bif_chksum(); + erts_init_bif_re(); + erts_init_unicode(); /* after RE to get access to PCRE unicode */ + erts_delay_trap = erts_export_put(am_erlang, am_delay_trap, 2); + erts_late_init_process(); +#if HAVE_ERTS_MSEG + erts_mseg_late_init(); /* Must be after timer (init_time()) and thread + initializations */ +#endif +#ifdef HIPE + hipe_mode_switch_init(); /* Must be after init_load/beam_catches/init */ +#endif +#ifdef _OSE_ + erl_sys_init_final(); +#endif + packet_parser_init(); +} + +static void +init_shared_memory(int argc, char **argv) +{ +#ifdef HYBRID + int arg_size = 0; + + global_heap_sz = erts_next_heap_size(global_heap_sz,0); + + /* Make sure arguments will fit on the heap, no one else will check! */ + while (argc--) + arg_size += 2 + strlen(argv[argc]); + if (global_heap_sz < arg_size) + global_heap_sz = erts_next_heap_size(arg_size,1); + +#ifndef INCREMENTAL + global_heap = (Eterm *) ERTS_HEAP_ALLOC(ERTS_ALC_T_HEAP, + sizeof(Eterm) * global_heap_sz); + global_hend = global_heap + global_heap_sz; + global_htop = global_heap; + global_high_water = global_heap; + global_old_hend = global_old_htop = global_old_heap = NULL; +#endif + + global_gen_gcs = 0; + global_max_gen_gcs = erts_smp_atomic_read(&erts_max_gen_gcs); + global_gc_flags = erts_default_process_flags; + + erts_global_offheap.mso = NULL; +#ifndef HYBRID /* FIND ME! */ + erts_global_offheap.funs = NULL; +#endif + erts_global_offheap.overhead = 0; +#endif + +#ifdef INCREMENTAL + erts_init_incgc(); +#endif +} + + +/* + * Create the very first process. + */ + +void +erts_first_process(Eterm modname, void* code, unsigned size, int argc, char** argv) +{ + int i; + Eterm args; + Eterm pid; + Eterm* hp; + Process parent; + Process* p; + ErlSpawnOpts so; + + if (erts_find_function(modname, am_start, 1) == NULL) { + char sbuf[256]; + Atom* ap; + + ap = atom_tab(atom_val(modname)); + memcpy(sbuf, ap->name, ap->len); + sbuf[ap->len] = '\0'; + erl_exit(5, "No function %s:start/1\n", sbuf); + } + + /* + * We need a dummy parent process to be able to call erl_create_process(). + */ + erts_init_empty_process(&parent); + hp = HAlloc(&parent, argc*2 + 4); + args = NIL; + for (i = argc-1; i >= 0; i--) { + int len = sys_strlen(argv[i]); + args = CONS(hp, new_binary(&parent, (byte*)argv[i], len), args); + hp += 2; + } + args = CONS(hp, new_binary(&parent, code, size), args); + hp += 2; + args = CONS(hp, args, NIL); + + so.flags = 0; + pid = erl_create_process(&parent, modname, am_start, args, &so); + p = process_tab[internal_pid_index(pid)]; + p->group_leader = pid; + + erts_cleanup_empty_process(&parent); +} + +/* + * XXX Old way of starting. Hopefully soon obsolete. + */ + +static void +erl_first_process_otp(char* modname, void* code, unsigned size, int argc, char** argv) +{ + int i; + Eterm start_mod; + Eterm args; + Eterm* hp; + Process parent; + ErlSpawnOpts so; + Eterm env; + + start_mod = am_atom_put(modname, sys_strlen(modname)); + if (erts_find_function(start_mod, am_start, 2) == NULL) { + erl_exit(5, "No function %s:start/2\n", modname); + } + + /* + * We need a dummy parent process to be able to call erl_create_process(). + */ + + erts_init_empty_process(&parent); + erts_smp_proc_lock(&parent, ERTS_PROC_LOCK_MAIN); + hp = HAlloc(&parent, argc*2 + 4); + args = NIL; + for (i = argc-1; i >= 0; i--) { + int len = sys_strlen(argv[i]); + args = CONS(hp, new_binary(&parent, (byte*)argv[i], len), args); + hp += 2; + } + env = new_binary(&parent, code, size); + args = CONS(hp, args, NIL); + hp += 2; + args = CONS(hp, env, args); + + so.flags = 0; + (void) erl_create_process(&parent, start_mod, am_start, args, &so); + erts_smp_proc_unlock(&parent, ERTS_PROC_LOCK_MAIN); + erts_cleanup_empty_process(&parent); +} + +Eterm +erts_preloaded(Process* p) +{ + Eterm previous; + int j; + int need; + Eterm mod; + Eterm* hp; + char* name; + const Preload *preload = sys_preloaded(); + + j = 0; + while (preload[j].name != NULL) { + j++; + } + previous = NIL; + need = 2*j; + hp = HAlloc(p, need); + j = 0; + while ((name = preload[j].name) != NULL) { + mod = am_atom_put(name, sys_strlen(name)); + previous = CONS(hp, mod, previous); + hp += 2; + j++; + } + return previous; +} + + +/* static variables that must not change (use same values at restart) */ +static char* program; +static char* init = "init"; +static char* boot = "boot"; +static int boot_argc; +static char** boot_argv; + +static char * +get_arg(char* rest, char* next, int* ip) +{ + if (*rest == '\0') { + if (next == NULL) { + erts_fprintf(stderr, "too few arguments\n"); + erts_usage(); + } + (*ip)++; + return next; + } + return rest; +} + +static void +load_preloaded(void) +{ + int i; + int res; + Preload* preload_p; + Eterm module_name; + byte* code; + char* name; + int length; + + if ((preload_p = sys_preloaded()) == NULL) { + return; + } + i = 0; + while ((name = preload_p[i].name) != NULL) { + length = preload_p[i].size; + module_name = am_atom_put(name, sys_strlen(name)); + if ((code = sys_preload_begin(&preload_p[i])) == 0) + erl_exit(1, "Failed to find preloaded code for module %s\n", + name); + res = erts_load_module(NULL, 0, NIL, &module_name, code, length); + sys_preload_end(&preload_p[i]); + if (res < 0) + erl_exit(1,"Failed loading preloaded module %s\n", name); + i++; + } +} + +/* be helpful (or maybe downright rude:-) */ +void erts_usage(void) +{ + erts_fprintf(stderr, "Usage: %s [flags] [ -- [init_args] ]\n", progname(program)); + erts_fprintf(stderr, "The flags are:\n\n"); + + /* erts_fprintf(stderr, "-# number set the number of items to be used in traces etc\n"); */ + + erts_fprintf(stderr, "-a size suggested stack size in kilo words for threads\n"); + erts_fprintf(stderr, " in the async-thread pool, valid range is [%d-%d]\n", + ERTS_ASYNC_THREAD_MIN_STACK_SIZE, + ERTS_ASYNC_THREAD_MAX_STACK_SIZE); + erts_fprintf(stderr, "-A number set number of threads in async thread pool,\n"); + erts_fprintf(stderr, " valid range is [0-%d]\n", + ERTS_MAX_NO_OF_ASYNC_THREADS); + + erts_fprintf(stderr, "-B[c|d|i] c to have Ctrl-c interrupt the Erlang shell,\n"); + erts_fprintf(stderr, " d (or no extra option) to disable the break\n"); + erts_fprintf(stderr, " handler, i to ignore break signals\n"); + + /* erts_fprintf(stderr, "-b func set the boot function (default boot)\n"); */ + + erts_fprintf(stderr, "-c disable continuous date/time correction with\n"); + erts_fprintf(stderr, " respect to uptime\n"); + + erts_fprintf(stderr, "-d don't write a crash dump for internally detected errors\n"); + erts_fprintf(stderr, " (halt(String) will still produce a crash dump)\n"); + + erts_fprintf(stderr, "-h number set minimum heap size in words (default %d)\n", + H_DEFAULT_SIZE); + + /* erts_fprintf(stderr, "-i module set the boot module (default init)\n"); */ + + erts_fprintf(stderr, "-K boolean enable or disable kernel poll\n"); + + erts_fprintf(stderr, "-l turn on auto load tracing\n"); + + erts_fprintf(stderr, "-M<X> <Y> memory allocator switches,\n"); + erts_fprintf(stderr, " see the erts_alloc(3) documentation for more info.\n"); + + erts_fprintf(stderr, "-P number set maximum number of processes on this node,\n"); + erts_fprintf(stderr, " valid range is [%d-%d]\n", + ERTS_MIN_PROCESSES, ERTS_MAX_PROCESSES); + erts_fprintf(stderr, "-R number set compatibility release number,\n"); + erts_fprintf(stderr, " valid range [%d-%d]\n", + ERTS_MIN_COMPAT_REL, this_rel_num()); + + erts_fprintf(stderr, "-r force ets memory block to be moved on realloc\n"); + erts_fprintf(stderr, "-sbt type set scheduler bind type, valid types are:\n"); + erts_fprintf(stderr, " u|ns|ts|ps|s|nnts|nnps|tnnps|db\n"); + erts_fprintf(stderr, "-sct cput set cpu topology,\n"); + erts_fprintf(stderr, " see the erl(1) documentation for more info.\n"); + erts_fprintf(stderr, "-sss size suggested stack size in kilo words for scheduler threads,\n"); + erts_fprintf(stderr, " valid range is [%d-%d]\n", + ERTS_SCHED_THREAD_MIN_STACK_SIZE, + ERTS_SCHED_THREAD_MAX_STACK_SIZE); + erts_fprintf(stderr, "-S n1:n2 set number of schedulers (n1), and number of\n"); + erts_fprintf(stderr, " schedulers online (n2), valid range for both\n"); + erts_fprintf(stderr, " numbers are [1-%d]\n", + ERTS_MAX_NO_OF_SCHEDULERS); + erts_fprintf(stderr, "-T number set modified timing level,\n"); + erts_fprintf(stderr, " valid range is [0-%d]\n", + ERTS_MODIFIED_TIMING_LEVELS-1); + erts_fprintf(stderr, "-V print Erlang version\n"); + + erts_fprintf(stderr, "-v turn on chatty mode (GCs will be reported etc)\n"); + + erts_fprintf(stderr, "-W<i|w> set error logger warnings mapping,\n"); + erts_fprintf(stderr, " see error_logger documentation for details\n"); + + erts_fprintf(stderr, "\n"); + erts_fprintf(stderr, "Note that if the emulator is started with erlexec (typically\n"); + erts_fprintf(stderr, "from the erl script), these flags should be specified with +.\n"); + erts_fprintf(stderr, "\n\n"); + erl_exit(-1, ""); +} + +static void +early_init(int *argc, char **argv) /* + * Only put things here which are + * really important initialize + * early! + */ +{ + ErtsAllocInitOpts alloc_opts = ERTS_ALLOC_INIT_DEF_OPTS_INITER; + int ncpu; + int ncpuonln; + int ncpuavail; + int schdlrs; + int schdlrs_onln; + use_multi_run_queue = 1; + erts_printf_eterm_func = erts_printf_term; + erts_disable_tolerant_timeofday = 0; + display_items = 200; + display_loads = 0; + erts_backtrace_depth = DEFAULT_BACKTRACE_SIZE; + erts_async_max_threads = 0; + erts_async_thread_suggested_stack_size = ERTS_ASYNC_THREAD_MIN_STACK_SIZE; + H_MIN_SIZE = H_DEFAULT_SIZE; + + erts_initialized = 0; + + erts_use_sender_punish = 1; + + erts_cpuinfo = erts_cpu_info_create(); + +#ifdef ERTS_SMP + ncpu = erts_get_cpu_configured(erts_cpuinfo); + ncpuonln = erts_get_cpu_online(erts_cpuinfo); + ncpuavail = erts_get_cpu_available(erts_cpuinfo); +#else + ncpu = 1; + ncpuonln = 1; + ncpuavail = 1; +#endif + + ignore_break = 0; + replace_intr = 0; + program = argv[0]; + + erts_modified_timing_level = -1; + + erts_compat_rel = this_rel_num(); + + erts_use_r9_pids_ports = 0; + + erts_sys_pre_init(); + +#ifdef ERTS_ENABLE_LOCK_CHECK + erts_lc_init(); +#endif +#ifdef ERTS_SMP + erts_smp_atomic_init(&erts_writing_erl_crash_dump, 0L); +#else + erts_writing_erl_crash_dump = 0; +#endif + + erts_smp_atomic_init(&erts_max_gen_gcs, (long)((Uint16) -1)); + + erts_pre_init_process(); +#if defined(USE_THREADS) && !defined(ERTS_SMP) + main_thread = erts_thr_self(); +#endif + + /* + * We need to know the number of schedulers to use before we + * can initialize the allocators. + */ + no_schedulers = (Uint) (ncpu > 0 ? ncpu : 1); + no_schedulers_online = (ncpuavail > 0 + ? ncpuavail + : (ncpuonln > 0 ? ncpuonln : no_schedulers)); + + schdlrs = no_schedulers; + schdlrs_onln = no_schedulers_online; + + if (argc && argv) { + int i = 1; + while (i < *argc) { + if (strcmp(argv[i], "--") == 0) { /* end of emulator options */ + i++; + break; + } + if (argv[i][0] == '-') { + switch (argv[i][1]) { + case 'S' : { + int tot, onln; + char *arg = get_arg(argv[i]+2, argv[i+1], &i); + switch (sscanf(arg, "%d:%d", &tot, &onln)) { + case 0: + switch (sscanf(arg, ":%d", &onln)) { + case 1: + tot = no_schedulers; + goto chk_S; + default: + goto bad_S; + } + case 1: + onln = tot < schdlrs_onln ? tot : schdlrs_onln; + case 2: + chk_S: + if (tot > 0) + schdlrs = tot; + else + schdlrs = no_schedulers + tot; + if (onln > 0) + schdlrs_onln = onln; + else + schdlrs_onln = no_schedulers_online + onln; + if (schdlrs < 1 || ERTS_MAX_NO_OF_SCHEDULERS < schdlrs) { + erts_fprintf(stderr, + "bad amount of schedulers %d\n", + tot); + erts_usage(); + } + if (schdlrs_onln < 1 || schdlrs < schdlrs_onln) { + erts_fprintf(stderr, + "bad amount of schedulers online %d " + "(total amount of schedulers %d)\n", + schdlrs_onln, schdlrs); + erts_usage(); + } + break; + default: + bad_S: + erts_fprintf(stderr, + "bad amount of schedulers %s\n", + arg); + erts_usage(); + break; + } + + VERBOSE(DEBUG_SYSTEM, + ("using %d:%d scheduler(s)\n", tot, onln)); + break; + } + default: + break; + } + } + i++; + } + } + +#ifdef ERTS_SMP + no_schedulers = schdlrs; + no_schedulers_online = schdlrs_onln; + + erts_no_schedulers = (Uint) no_schedulers; +#endif + + erts_alloc_init(argc, argv, &alloc_opts); /* Handles (and removes) + -M flags. */ + + erts_early_init_scheduling(); /* Require allocators */ + erts_init_utils(); /* Require allocators */ + +#ifdef ERTS_ENABLE_LOCK_CHECK + erts_lc_late_init(); +#endif + +#if defined(HIPE) + hipe_signal_init(); /* must be done very early */ +#endif + erl_sys_init(); + + erl_sys_args(argc, argv); + + erts_ets_realloc_always_moves = 0; + +} + +#ifndef ERTS_SMP +static void set_main_stack_size(void) +{ + if (erts_sched_thread_suggested_stack_size > 0) { +# if HAVE_DECL_GETRLIMIT && HAVE_DECL_SETRLIMIT && HAVE_DECL_RLIMIT_STACK + struct rlimit rl; + int bytes = erts_sched_thread_suggested_stack_size * sizeof(Uint) * 1024; + if (getrlimit(RLIMIT_STACK, &rl) != 0 || + (rl.rlim_cur = bytes, setrlimit(RLIMIT_STACK, &rl) != 0)) { + erts_fprintf(stderr, "failed to set stack size for scheduler " + "thread to %d bytes\n", bytes); + erts_usage(); + } +# else + erts_fprintf(stderr, "no OS support for dynamic stack size limit\n"); + erts_usage(); +# endif + } +} +#endif + +void +erl_start(int argc, char **argv) +{ + int i = 1; + char* arg=NULL; + char* Parg = NULL; + int have_break_handler = 1; + char envbuf[21]; /* enough for any 64-bit integer */ + size_t envbufsz; + int async_max_threads = erts_async_max_threads; + + early_init(&argc, argv); + + envbufsz = sizeof(envbuf); + if (erts_sys_getenv(ERL_MAX_ETS_TABLES_ENV, envbuf, &envbufsz) == 0) + user_requested_db_max_tabs = atoi(envbuf); + else + user_requested_db_max_tabs = 0; + + envbufsz = sizeof(envbuf); + if (erts_sys_getenv("ERL_FULLSWEEP_AFTER", envbuf, &envbufsz) == 0) { + Uint16 max_gen_gcs = atoi(envbuf); + erts_smp_atomic_set(&erts_max_gen_gcs, (long) max_gen_gcs); + } + + envbufsz = sizeof(envbuf); + if (erts_sys_getenv("ERL_THREAD_POOL_SIZE", envbuf, &envbufsz) == 0) { + async_max_threads = atoi(envbuf); + } + + +#ifdef DEBUG + verbose = DEBUG_DEFAULT; +#endif + + erts_error_logger_warnings = am_error; + + while (i < argc) { + if (argv[i][0] != '-') { + erts_usage(); + } + if (strcmp(argv[i], "--") == 0) { /* end of emulator options */ + i++; + break; + } + switch (argv[i][1]) { + + /* + * NOTE: -M flags are handled (and removed from argv) by + * erts_alloc_init(). + * + * The -d, -m, -S, -t, and -T flags was removed in + * Erlang 5.3/OTP R9C. + * + * -S, and -T has been reused in Erlang 5.5/OTP R11B. + * + * -d has been reused in a patch R12B-4. + */ + + case '#' : + arg = get_arg(argv[i]+2, argv[i+1], &i); + if ((display_items = atoi(arg)) == 0) { + erts_fprintf(stderr, "bad display items%s\n", arg); + erts_usage(); + } + VERBOSE(DEBUG_SYSTEM, + ("using display items %d\n",display_items)); + break; + + case 'l': + display_loads++; + break; + + case 'v': +#ifdef DEBUG + if (argv[i][2] == '\0') { + verbose |= DEBUG_SYSTEM; + } else { + char *ch; + for (ch = argv[i]+2; *ch != '\0'; ch++) { + switch (*ch) { + case 's': verbose |= DEBUG_SYSTEM; break; + case 'g': verbose |= DEBUG_PRIVATE_GC; break; + case 'h': verbose |= DEBUG_HYBRID_GC; break; + case 'M': verbose |= DEBUG_MEMORY; break; + case 'a': verbose |= DEBUG_ALLOCATION; break; + case 't': verbose |= DEBUG_THREADS; break; + case 'p': verbose |= DEBUG_PROCESSES; break; + case 'm': verbose |= DEBUG_MESSAGES; break; + default : erts_fprintf(stderr,"Unknown verbose option: %c\n",*ch); + } + } + } + erts_printf("Verbose level: "); + if (verbose & DEBUG_SYSTEM) erts_printf("SYSTEM "); + if (verbose & DEBUG_PRIVATE_GC) erts_printf("PRIVATE_GC "); + if (verbose & DEBUG_HYBRID_GC) erts_printf("HYBRID_GC "); + if (verbose & DEBUG_MEMORY) erts_printf("PARANOID_MEMORY "); + if (verbose & DEBUG_ALLOCATION) erts_printf("ALLOCATION "); + if (verbose & DEBUG_THREADS) erts_printf("THREADS "); + if (verbose & DEBUG_PROCESSES) erts_printf("PROCESSES "); + if (verbose & DEBUG_MESSAGES) erts_printf("MESSAGES "); + erts_printf("\n"); +#else + erts_fprintf(stderr, "warning: -v (only in debug compiled code)\n"); +#endif + break; + case 'V' : + { + char tmp[256]; + + tmp[0] = tmp[1] = '\0'; +#ifdef DEBUG + strcat(tmp, ",DEBUG"); +#endif +#ifdef ERTS_SMP + strcat(tmp, ",SMP"); +#endif +#ifdef USE_THREADS + strcat(tmp, ",ASYNC_THREADS"); +#endif +#ifdef HIPE + strcat(tmp, ",HIPE"); +#endif +#ifdef INCREMENTAL + strcat(tmp, ",INCREMENTAL_GC"); +#endif +#ifdef HYBRID + strcat(tmp, ",HYBRID"); +#endif + erts_fprintf(stderr, "Erlang "); + if (tmp[1]) { + erts_fprintf(stderr, "(%s) ", tmp+1); + } + erts_fprintf(stderr, "(" EMULATOR ") emulator version " + ERLANG_VERSION "\n"); + erl_exit(0, ""); + } + break; + + case 'H': /* undocumented */ + fprintf(stderr, "The undocumented +H option has been removed (R10B-6).\n\n"); + break; + + case 'h': + /* set default heap size */ + arg = get_arg(argv[i]+2, argv[i+1], &i); + if ((H_MIN_SIZE = atoi(arg)) <= 0) { + erts_fprintf(stderr, "bad heap size %s\n", arg); + erts_usage(); + } + VERBOSE(DEBUG_SYSTEM, + ("using minimum heap size %d\n",H_MIN_SIZE)); + break; + + case 'd': + /* + * Never produce crash dumps for internally detected + * errors; only produce a core dump. (Generation of + * crash dumps is destructive and makes it impossible + * to inspect the contents of process heaps in the + * core dump.) + */ + erts_no_crash_dump = 1; + break; + + case 'e': + /* set maximum number of ets tables */ + arg = get_arg(argv[i]+2, argv[i+1], &i); + if (( user_requested_db_max_tabs = atoi(arg) ) < 0) { + erts_fprintf(stderr, "bad maximum number of ets tables %s\n", arg); + erts_usage(); + } + VERBOSE(DEBUG_SYSTEM, + ("using maximum number of ets tables %d\n", + user_requested_db_max_tabs)); + break; + + case 'i': + /* define name of module for initial function */ + init = get_arg(argv[i]+2, argv[i+1], &i); + break; + + case 'b': + /* define name of initial function */ + boot = get_arg(argv[i]+2, argv[i+1], &i); + break; + + case 'B': + if (argv[i][2] == 'i') /* +Bi */ + ignore_break = 1; + else if (argv[i][2] == 'c') /* +Bc */ + replace_intr = 1; + else if (argv[i][2] == 'd') /* +Bd */ + have_break_handler = 0; + else if (argv[i+1][0] == 'i') { /* +B i */ + get_arg(argv[i]+2, argv[i+1], &i); + ignore_break = 1; + } + else if (argv[i+1][0] == 'c') { /* +B c */ + get_arg(argv[i]+2, argv[i+1], &i); + replace_intr = 1; + } + else if (argv[i+1][0] == 'd') { /* +B d */ + get_arg(argv[i]+2, argv[i+1], &i); + have_break_handler = 0; + } + else /* +B */ + have_break_handler = 0; + break; + + case 'K': + /* If kernel poll support is present, + erl_sys_args() will remove the K parameter + and value */ + get_arg(argv[i]+2, argv[i+1], &i); + erts_fprintf(stderr, + "kernel-poll not supported; \"K\" parameter ignored\n", + arg); + break; + + case 'P': + /* set maximum number of processes */ + Parg = get_arg(argv[i]+2, argv[i+1], &i); + erts_max_processes = atoi(Parg); + /* Check of result is delayed until later. This is because +R + may be given after +P. */ + break; + + case 'S' : /* Was handled in early_init() just read past it */ + (void) get_arg(argv[i]+2, argv[i+1], &i); + break; + + case 's' : { + char *estr; + int res; + char *sub_param = argv[i]+2; + if (has_prefix("bt", sub_param)) { + arg = get_arg(sub_param+2, argv[i+1], &i); + res = erts_init_scheduler_bind_type(arg); + if (res != ERTS_INIT_SCHED_BIND_TYPE_SUCCESS) { + switch (res) { + case ERTS_INIT_SCHED_BIND_TYPE_NOT_SUPPORTED: + estr = "not supported"; + break; + case ERTS_INIT_SCHED_BIND_TYPE_ERROR_NO_CPU_TOPOLOGY: + estr = "no cpu topology available"; + break; + case ERTS_INIT_SCHED_BIND_TYPE_ERROR_NO_BAD_TYPE: + estr = "invalid type"; + break; + default: + estr = "undefined error"; + break; + } + erts_fprintf(stderr, + "setting scheduler bind type '%s' failed: %s\n", + arg, + estr); + erts_usage(); + } + } + else if (has_prefix("ct", sub_param)) { + arg = get_arg(sub_param+2, argv[i+1], &i); + res = erts_init_cpu_topology(arg); + if (res != ERTS_INIT_CPU_TOPOLOGY_OK) { + switch (res) { + case ERTS_INIT_CPU_TOPOLOGY_INVALID_ID: + estr = "invalid identifier"; + break; + case ERTS_INIT_CPU_TOPOLOGY_INVALID_ID_RANGE: + estr = "invalid identifier range"; + break; + case ERTS_INIT_CPU_TOPOLOGY_INVALID_HIERARCHY: + estr = "invalid hierarchy"; + break; + case ERTS_INIT_CPU_TOPOLOGY_INVALID_ID_TYPE: + estr = "invalid identifier type"; + break; + case ERTS_INIT_CPU_TOPOLOGY_INVALID_NODES: + estr = "invalid nodes declaration"; + break; + case ERTS_INIT_CPU_TOPOLOGY_MISSING_LID: + estr = "missing logical identifier"; + break; + case ERTS_INIT_CPU_TOPOLOGY_NOT_UNIQUE_LIDS: + estr = "not unique logical identifiers"; + break; + case ERTS_INIT_CPU_TOPOLOGY_NOT_UNIQUE_ENTITIES: + estr = "not unique entities"; + break; + case ERTS_INIT_CPU_TOPOLOGY_MISSING: + estr = "missing cpu topology"; + break; + default: + estr = "undefined error"; + break; + } + erts_fprintf(stderr, + "bad cpu topology '%s': %s\n", + arg, + estr); + erts_usage(); + } + } + else if (sys_strcmp("mrq", sub_param) == 0) + use_multi_run_queue = 1; + else if (sys_strcmp("srq", sub_param) == 0) + use_multi_run_queue = 0; + else if (sys_strcmp("nsp", sub_param) == 0) + erts_use_sender_punish = 0; + else if (has_prefix("ss", sub_param)) { + /* suggested stack size (Kilo Words) for scheduler threads */ + arg = get_arg(sub_param+2, argv[i+1], &i); + erts_sched_thread_suggested_stack_size = atoi(arg); + + if ((erts_sched_thread_suggested_stack_size + < ERTS_SCHED_THREAD_MIN_STACK_SIZE) + || (erts_sched_thread_suggested_stack_size > + ERTS_SCHED_THREAD_MAX_STACK_SIZE)) { + erts_fprintf(stderr, "bad stack size for scheduler threads %s\n", + arg); + erts_usage(); + } + VERBOSE(DEBUG_SYSTEM, + ("suggested scheduler thread stack size %d kilo words\n", + erts_sched_thread_suggested_stack_size)); + } + else { + erts_fprintf(stderr, "bad scheduling option %s\n", argv[i]); + erts_usage(); + } + break; + } + case 'T' : + arg = get_arg(argv[i]+2, argv[i+1], &i); + errno = 0; + erts_modified_timing_level = atoi(arg); + if ((erts_modified_timing_level == 0 && errno != 0) + || erts_modified_timing_level < 0 + || erts_modified_timing_level >= ERTS_MODIFIED_TIMING_LEVELS) { + erts_fprintf(stderr, "bad modified timing level %s\n", arg); + erts_usage(); + } + else { + VERBOSE(DEBUG_SYSTEM, + ("using modified timing level %d\n", + erts_modified_timing_level)); + } + + break; + + case 'R': { + /* set compatibility release */ + + arg = get_arg(argv[i]+2, argv[i+1], &i); + erts_compat_rel = atoi(arg); + + if (erts_compat_rel < ERTS_MIN_COMPAT_REL + || erts_compat_rel > this_rel_num()) { + erts_fprintf(stderr, "bad compatibility release number %s\n", arg); + erts_usage(); + } + + ASSERT(ERTS_MIN_COMPAT_REL >= 7); + switch (erts_compat_rel) { + case 7: + case 8: + case 9: + erts_use_r9_pids_ports = 1; + default: + break; + } + + break; + } + + case 'A': + /* set number of threads in thread pool */ + arg = get_arg(argv[i]+2, argv[i+1], &i); + if (((async_max_threads = atoi(arg)) < 0) || + (async_max_threads > ERTS_MAX_NO_OF_ASYNC_THREADS)) { + erts_fprintf(stderr, "bad number of async threads %s\n", arg); + erts_usage(); + } + + VERBOSE(DEBUG_SYSTEM, ("using %d async-threads\n", + async_max_threads)); + break; + + case 'a': + /* suggested stack size (Kilo Words) for threads in thread pool */ + arg = get_arg(argv[i]+2, argv[i+1], &i); + erts_async_thread_suggested_stack_size = atoi(arg); + + if ((erts_async_thread_suggested_stack_size + < ERTS_ASYNC_THREAD_MIN_STACK_SIZE) + || (erts_async_thread_suggested_stack_size > + ERTS_ASYNC_THREAD_MAX_STACK_SIZE)) { + erts_fprintf(stderr, "bad stack size for async threads %s\n", + arg); + erts_usage(); + } + + VERBOSE(DEBUG_SYSTEM, + ("suggested async-thread stack size %d kilo words\n", + erts_async_thread_suggested_stack_size)); + break; + + case 'r': + erts_ets_realloc_always_moves = 1; + break; + case 'n': /* XXX obsolete */ + break; + case 'c': + if (argv[i][2] == 0) { /* -c: documented option */ + erts_disable_tolerant_timeofday = 1; + } +#ifdef ERTS_OPCODE_COUNTER_SUPPORT + else if (argv[i][2] == 'i') { /* -ci: undcoumented option*/ + count_instructions = 1; + } +#endif + break; + case 'W': + arg = get_arg(argv[i]+2, argv[i+1], &i); + switch (arg[0]) { + case 'i': + erts_error_logger_warnings = am_info; + break; + case 'w': + erts_error_logger_warnings = am_warning; + break; + case 'e': /* The default */ + erts_error_logger_warnings = am_error; + default: + erts_fprintf(stderr, "unrecognized warning_map option %s\n", arg); + erts_usage(); + } + break; + + default: + erts_fprintf(stderr, "%s unknown flag %s\n", argv[0], argv[i]); + erts_usage(); + } + i++; + } + +#ifdef USE_THREADS + erts_async_max_threads = async_max_threads; +#endif + + /* Delayed check of +P flag */ + if (erts_max_processes < ERTS_MIN_PROCESSES + || erts_max_processes > ERTS_MAX_PROCESSES + || (erts_use_r9_pids_ports + && erts_max_processes > ERTS_MAX_R9_PROCESSES)) { + erts_fprintf(stderr, "bad number of processes %s\n", Parg); + erts_usage(); + } + + /* Restart will not reinstall the break handler */ +#ifdef __WIN32__ + if (ignore_break) + erts_set_ignore_break(); + else if (replace_intr) + erts_replace_intr(); + else + init_break_handler(); +#else + if (ignore_break) + erts_set_ignore_break(); + else if (have_break_handler) + init_break_handler(); + if (replace_intr) + erts_replace_intr(); +#endif + + boot_argc = argc - i; /* Number of arguments to init */ + boot_argv = &argv[i]; + + erl_init(); + + init_shared_memory(boot_argc, boot_argv); + load_preloaded(); + + erts_initialized = 1; + + erl_first_process_otp("otp_ring0", NULL, 0, boot_argc, boot_argv); + +#ifdef ERTS_SMP + erts_start_schedulers(); + /* Let system specific code decide what to do with the main thread... */ + + erts_sys_main_thread(); /* May or may not return! */ +#else + set_main_stack_size(); + process_main(); +#endif +} + + +#ifdef USE_THREADS + +__decl_noreturn void erts_thr_fatal_error(int err, char *what) +{ + char *errstr = err ? strerror(err) : NULL; + erts_fprintf(stderr, + "Failed to %s: %s%s(%d)\n", + what, + errstr ? errstr : "", + errstr ? " " : "", + err); + abort(); +} + +#endif + +static void +system_cleanup(int exit_code) +{ + /* No cleanup wanted if ... + * 1. we are about to do an abnormal exit + * 2. we haven't finished initializing, or + * 3. another thread than the main thread is performing the exit + * (in threaded non smp case). + */ + + if (exit_code != 0 + || !erts_initialized +#if defined(USE_THREADS) && !defined(ERTS_SMP) + || !erts_equal_tids(main_thread, erts_thr_self()) +#endif + ) + return; + +#ifdef ERTS_SMP +#ifdef ERTS_ENABLE_LOCK_CHECK + erts_lc_check_exact(NULL, 0); +#endif + erts_smp_block_system(ERTS_BS_FLG_ALLOW_GC); /* We never release it... */ +#endif + +#ifdef HYBRID + if (ma_src_stack) erts_free(ERTS_ALC_T_OBJECT_STACK, + (void *)ma_src_stack); + if (ma_dst_stack) erts_free(ERTS_ALC_T_OBJECT_STACK, + (void *)ma_dst_stack); + if (ma_offset_stack) erts_free(ERTS_ALC_T_OBJECT_STACK, + (void *)ma_offset_stack); + ma_src_stack = NULL; + ma_dst_stack = NULL; + ma_offset_stack = NULL; + erts_cleanup_offheap(&erts_global_offheap); +#endif + +#if defined(HYBRID) && !defined(INCREMENTAL) + if (global_heap) { + ERTS_HEAP_FREE(ERTS_ALC_T_HEAP, + (void*) global_heap, + sizeof(Eterm) * global_heap_sz); + } + global_heap = NULL; +#endif + +#ifdef INCREMENTAL + erts_cleanup_incgc(); +#endif + +#if defined(USE_THREADS) && !defined(ERTS_SMP) + exit_async(); +#endif +#if HAVE_ERTS_MSEG + erts_mseg_exit(); +#endif + + /* + * A lot more cleaning could/should have been done... + */ + +} + +/* + * Common exit function, all exits from the system go through here. + * n <= 0 -> normal exit with status n; + * n = 127 -> Erlang crash dump produced, exit with status 1; + * other positive n -> Erlang crash dump and core dump produced. + */ + +__decl_noreturn void erl_exit0(char *file, int line, int n, char *fmt,...) +{ + unsigned int an; + va_list args; + + va_start(args, fmt); + + save_statistics(); + + system_cleanup(n); + + an = abs(n); + + if (erts_mtrace_enabled) + erts_mtrace_exit((Uint32) an); + + /* Produce an Erlang core dump if error */ + if (n > 0 && erts_initialized && + (erts_no_crash_dump == 0 || n == ERTS_DUMP_EXIT)) { + erl_crash_dump_v(file, line, fmt, args); + } + + /* need to reinitialize va_args thing */ + va_end(args); + va_start(args, fmt); + + if (fmt != NULL && *fmt != '\0') + erl_error(fmt, args); /* Print error message. */ + va_end(args); +#ifdef __WIN32__ + if(n > 0) ConWaitForExit(); + else ConNormalExit(); +#endif +#if !defined(__WIN32__) && !defined(VXWORKS) && !defined(_OSE_) + sys_tty_reset(); +#endif + + if (n == ERTS_INTR_EXIT) + exit(0); + else if (n == 127) + ERTS_EXIT_AFTER_DUMP(1); + else if (n > 0 || n == ERTS_ABORT_EXIT) + abort(); + exit(an); +} + +__decl_noreturn void erl_exit(int n, char *fmt,...) +{ + unsigned int an; + va_list args; + + va_start(args, fmt); + + save_statistics(); + + system_cleanup(n); + + an = abs(n); + + if (erts_mtrace_enabled) + erts_mtrace_exit((Uint32) an); + + /* Produce an Erlang core dump if error */ + if (n > 0 && erts_initialized && + (erts_no_crash_dump == 0 || n == ERTS_DUMP_EXIT)) { + erl_crash_dump_v((char*) NULL, 0, fmt, args); + } + + /* need to reinitialize va_args thing */ + va_end(args); + va_start(args, fmt); + + if (fmt != NULL && *fmt != '\0') + erl_error(fmt, args); /* Print error message. */ + va_end(args); +#ifdef __WIN32__ + if(n > 0) ConWaitForExit(); + else ConNormalExit(); +#endif +#if !defined(__WIN32__) && !defined(VXWORKS) && !defined(_OSE_) + sys_tty_reset(); +#endif + + if (n == ERTS_INTR_EXIT) + exit(0); + else if (n == ERTS_DUMP_EXIT) + ERTS_EXIT_AFTER_DUMP(1); + else if (n > 0 || n == ERTS_ABORT_EXIT) + abort(); + exit(an); +} + |