aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/beam/erl_bif_info.c
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /erts/emulator/beam/erl_bif_info.c
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'erts/emulator/beam/erl_bif_info.c')
-rw-r--r--erts/emulator/beam/erl_bif_info.c3803
1 files changed, 3803 insertions, 0 deletions
diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c
new file mode 100644
index 0000000000..60216aa8e4
--- /dev/null
+++ b/erts/emulator/beam/erl_bif_info.c
@@ -0,0 +1,3803 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1999-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 "erl_vm.h"
+#include "global.h"
+#include "erl_process.h"
+#include "erl_nmgc.h"
+#include "error.h"
+#include "erl_driver.h"
+#include "bif.h"
+#include "big.h"
+#include "erl_version.h"
+#include "erl_db_util.h"
+#include "erl_message.h"
+#include "erl_binary.h"
+#include "erl_db.h"
+#include "erl_instrument.h"
+#include "dist.h"
+#include "erl_gc.h"
+#ifdef ELIB_ALLOC_IS_CLIB
+#include "elib_stat.h"
+#endif
+#ifdef HIPE
+#include "hipe_arch.h"
+#endif
+
+#ifdef ERTS_ENABLE_LOCK_COUNT
+#include "erl_lock_count.h"
+#endif
+
+#ifdef VALGRIND
+#include <valgrind/valgrind.h>
+#include <valgrind/memcheck.h>
+#endif
+
+#define DECL_AM(S) Eterm AM_ ## S = am_atom_put(#S, sizeof(#S) - 1)
+
+/* Keep erts_system_version as a global variable for easy access from a core */
+static char erts_system_version[] = ("Erlang " ERLANG_OTP_RELEASE
+ " (erts-" ERLANG_VERSION ")"
+#ifndef OTP_RELEASE
+ " [source]"
+#endif
+#ifdef ARCH_64
+ " [64-bit]"
+#endif
+#ifdef ERTS_SMP
+ " [smp:%bpu:%bpu]"
+#endif
+ " [rq:%bpu]"
+#ifdef USE_THREADS
+ " [async-threads:%d]"
+#endif
+#ifdef HIPE
+ " [hipe]"
+#endif
+#ifdef ERTS_ENABLE_KERNEL_POLL
+ " [kernel-poll:%s]"
+#endif
+#ifdef HYBRID
+ " [hybrid heap]"
+#endif
+#ifdef INCREMENTAL
+ " [incremental GC]"
+#endif
+#ifdef ET_DEBUG
+#if ET_DEBUG
+ " [type-assertions]"
+#endif
+#endif
+#ifdef DEBUG
+ " [debug-compiled]"
+#endif
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ " [lock-checking]"
+#endif
+#ifdef ERTS_ENABLE_LOCK_COUNT
+ " [lock-counting]"
+#endif
+#ifdef PURIFY
+ " [purify-compiled]"
+#endif
+#ifdef VALGRIND
+ " [valgrind-compiled]"
+#endif
+ "\n");
+
+#define ASIZE(a) (sizeof(a)/sizeof(a[0]))
+
+#if defined(HAVE_SOLARIS_SPARC_PERFMON)
+# include <sys/ioccom.h>
+# define PERFMON_SETPCR _IOW('P', 1, unsigned long long)
+# define PERFMON_GETPCR _IOR('P', 2, unsigned long long)
+#endif
+
+static Eterm
+bld_bin_list(Uint **hpp, Uint *szp, ProcBin* pb)
+{
+ Eterm res = NIL;
+ Eterm tuple;
+
+ for (; pb; pb = pb->next) {
+ Eterm val = erts_bld_uint(hpp, szp, (Uint) pb->val);
+ Eterm orig_size = erts_bld_uint(hpp, szp, pb->val->orig_size);
+
+ if (szp)
+ *szp += 4+2;
+ if (hpp) {
+ Uint refc = (Uint) erts_smp_atomic_read(&pb->val->refc);
+ tuple = TUPLE3(*hpp, val, orig_size, make_small(refc));
+ res = CONS(*hpp + 4, tuple, res);
+ *hpp += 4+2;
+ }
+ }
+ return res;
+}
+
+
+/*
+ make_monitor_list:
+ returns a list of records..
+ -record(erl_monitor, {
+ type, % MON_ORIGIN or MON_TARGET (1 or 3)
+ ref,
+ pid, % Process or nodename
+ name % registered name or []
+ }).
+*/
+
+static void do_calc_mon_size(ErtsMonitor *mon, void *vpsz)
+{
+ Uint *psz = vpsz;
+ *psz += IS_CONST(mon->ref) ? 0 : NC_HEAP_SIZE(mon->ref);
+ *psz += IS_CONST(mon->pid) ? 0 : NC_HEAP_SIZE(mon->pid);
+ *psz += 8; /* CONS + 5-tuple */
+}
+
+typedef struct {
+ Process *p;
+ Eterm *hp;
+ Eterm res;
+ Eterm tag;
+} MonListContext;
+
+static void do_make_one_mon_element(ErtsMonitor *mon, void * vpmlc)
+{
+ MonListContext *pmlc = vpmlc;
+ Eterm tup;
+ Eterm r = (IS_CONST(mon->ref)
+ ? mon->ref
+ : STORE_NC(&(pmlc->hp), &MSO(pmlc->p).externals, mon->ref));
+ Eterm p = (IS_CONST(mon->pid)
+ ? mon->pid
+ : STORE_NC(&(pmlc->hp), &MSO(pmlc->p).externals, mon->pid));
+ tup = TUPLE5(pmlc->hp, pmlc->tag, make_small(mon->type), r, p, mon->name);
+ pmlc->hp += 6;
+ pmlc->res = CONS(pmlc->hp, tup, pmlc->res);
+ pmlc->hp += 2;
+}
+
+static Eterm
+make_monitor_list(Process *p, ErtsMonitor *root)
+{
+ DECL_AM(erl_monitor);
+ Uint sz = 0;
+ MonListContext mlc;
+
+ erts_doforall_monitors(root, &do_calc_mon_size, &sz);
+ if (sz == 0) {
+ return NIL;
+ }
+ mlc.p = p;
+ mlc.hp = HAlloc(p,sz);
+ mlc.res = NIL;
+ mlc.tag = AM_erl_monitor;
+ erts_doforall_monitors(root, &do_make_one_mon_element, &mlc);
+ return mlc.res;
+}
+
+/*
+ make_link_list:
+ returns a list of records..
+ -record(erl_link, {
+ type, % LINK_NODE or LINK_PID (1 or 3)
+ pid, % Process or nodename
+ targets % List of erl_link's or nil
+ }).
+*/
+
+static void do_calc_lnk_size(ErtsLink *lnk, void *vpsz)
+{
+ Uint *psz = vpsz;
+ *psz += IS_CONST(lnk->pid) ? 0 : NC_HEAP_SIZE(lnk->pid);
+ if (lnk->type != LINK_NODE && ERTS_LINK_ROOT(lnk) != NULL) {
+ /* Node links use this pointer as ref counter... */
+ erts_doforall_links(ERTS_LINK_ROOT(lnk),&do_calc_lnk_size,vpsz);
+ }
+ *psz += 7; /* CONS + 4-tuple */
+}
+
+typedef struct {
+ Process *p;
+ Eterm *hp;
+ Eterm res;
+ Eterm tag;
+} LnkListContext;
+
+static void do_make_one_lnk_element(ErtsLink *lnk, void * vpllc)
+{
+ LnkListContext *pllc = vpllc;
+ Eterm tup;
+ Eterm old_res, targets = NIL;
+ Eterm p = (IS_CONST(lnk->pid)
+ ? lnk->pid
+ : STORE_NC(&(pllc->hp), &MSO(pllc->p).externals, lnk->pid));
+ if (lnk->type == LINK_NODE) {
+ targets = make_small(ERTS_LINK_REFC(lnk));
+ } else if (ERTS_LINK_ROOT(lnk) != NULL) {
+ old_res = pllc->res;
+ pllc->res = NIL;
+ erts_doforall_links(ERTS_LINK_ROOT(lnk),&do_make_one_lnk_element, vpllc);
+ targets = pllc->res;
+ pllc->res = old_res;
+ }
+ tup = TUPLE4(pllc->hp, pllc->tag, make_small(lnk->type), p, targets);
+ pllc->hp += 5;
+ pllc->res = CONS(pllc->hp, tup, pllc->res);
+ pllc->hp += 2;
+}
+
+static Eterm
+make_link_list(Process *p, ErtsLink *root, Eterm tail)
+{
+ DECL_AM(erl_link);
+ Uint sz = 0;
+ LnkListContext llc;
+
+ erts_doforall_links(root, &do_calc_lnk_size, &sz);
+ if (sz == 0) {
+ return tail;
+ }
+ llc.p = p;
+ llc.hp = HAlloc(p,sz);
+ llc.res = tail;
+ llc.tag = AM_erl_link;
+ erts_doforall_links(root, &do_make_one_lnk_element, &llc);
+ return llc.res;
+}
+
+int
+erts_print_system_version(int to, void *arg, Process *c_p)
+{
+#ifdef ERTS_SMP
+ Uint total, online, active;
+ (void) erts_schedulers_state(&total, &online, &active, 0);
+#endif
+ return erts_print(to, arg, erts_system_version
+#ifdef ERTS_SMP
+ , total, online, erts_no_run_queues
+#else
+ , 1
+#endif
+#ifdef USE_THREADS
+ , erts_async_max_threads
+#endif
+#ifdef ERTS_ENABLE_KERNEL_POLL
+ , erts_use_kernel_poll ? "true" : "false"
+#endif
+ );
+}
+
+typedef struct {
+ Eterm entity;
+ Eterm node;
+} MonitorInfo;
+
+typedef struct {
+ MonitorInfo *mi;
+ Uint mi_i;
+ Uint mi_max;
+ int sz;
+} MonitorInfoCollection;
+
+#define INIT_MONITOR_INFOS(MIC) do { \
+ (MIC).mi = NULL; \
+ (MIC).mi_i = (MIC).mi_max = 0; \
+ (MIC).sz = 0; \
+} while(0)
+
+#define MI_INC 50
+#define EXTEND_MONITOR_INFOS(MICP) \
+do { \
+ if ((MICP)->mi_i >= (MICP)->mi_max) { \
+ (MICP)->mi = ((MICP)->mi ? erts_realloc(ERTS_ALC_T_TMP, \
+ (MICP)->mi, \
+ ((MICP)->mi_max+MI_INC) \
+ * sizeof(MonitorInfo)) \
+ : erts_alloc(ERTS_ALC_T_TMP, \
+ MI_INC*sizeof(MonitorInfo))); \
+ (MICP)->mi_max += MI_INC; \
+ } \
+ } while (0)
+#define DESTROY_MONITOR_INFOS(MIC) \
+do { \
+ if ((MIC).mi != NULL) { \
+ erts_free(ERTS_ALC_T_TMP, (void *) (MIC).mi); \
+ } \
+ } while (0)
+
+static void collect_one_link(ErtsLink *lnk, void *vmicp)
+{
+ MonitorInfoCollection *micp = vmicp;
+ EXTEND_MONITOR_INFOS(micp);
+ if (!(lnk->type == LINK_PID)) {
+ return;
+ }
+ micp->mi[micp->mi_i].entity = lnk->pid;
+ micp->sz += 2 + NC_HEAP_SIZE(lnk->pid);
+ micp->mi_i++;
+}
+
+static void collect_one_origin_monitor(ErtsMonitor *mon, void *vmicp)
+{
+ MonitorInfoCollection *micp = vmicp;
+
+ if (mon->type != MON_ORIGIN) {
+ return;
+ }
+ EXTEND_MONITOR_INFOS(micp);
+ if (is_atom(mon->pid)) { /* external by name */
+ micp->mi[micp->mi_i].entity = mon->name;
+ micp->mi[micp->mi_i].node = mon->pid;
+ micp->sz += 3; /* need one 2-tuple */
+ } else if (is_external_pid(mon->pid)) { /* external by pid */
+ micp->mi[micp->mi_i].entity = mon->pid;
+ micp->mi[micp->mi_i].node = NIL;
+ micp->sz += NC_HEAP_SIZE(mon->pid);
+ } else if (!is_nil(mon->name)) { /* internal by name */
+ micp->mi[micp->mi_i].entity = mon->name;
+ micp->mi[micp->mi_i].node = erts_this_dist_entry->sysname;
+ micp->sz += 3; /* need one 2-tuple */
+ } else { /* internal by pid */
+ micp->mi[micp->mi_i].entity = mon->pid;
+ micp->mi[micp->mi_i].node = NIL;
+ /* no additional heap space needed */
+ }
+ micp->mi_i++;
+ micp->sz += 2 + 3; /* For a cons cell and a 2-tuple */
+}
+
+static void collect_one_target_monitor(ErtsMonitor *mon, void *vmicp)
+{
+ MonitorInfoCollection *micp = vmicp;
+
+ if (mon->type != MON_TARGET) {
+ return;
+ }
+
+ EXTEND_MONITOR_INFOS(micp);
+
+ micp->mi[micp->mi_i].node = NIL;
+ micp->mi[micp->mi_i].entity = mon->pid;
+ micp->sz += (NC_HEAP_SIZE(mon->pid) + 2 /* cons */);
+ micp->mi_i++;
+}
+
+typedef struct {
+ Process *c_p;
+ ErtsProcLocks c_p_locks;
+ ErtsSuspendMonitor **smi;
+ Uint smi_i;
+ Uint smi_max;
+ int sz;
+} ErtsSuspendMonitorInfoCollection;
+
+#define ERTS_INIT_SUSPEND_MONITOR_INFOS(SMIC, CP, CPL) do { \
+ (SMIC).c_p = (CP); \
+ (SMIC).c_p_locks = (CPL); \
+ (SMIC).smi = NULL; \
+ (SMIC).smi_i = (SMIC).smi_max = 0; \
+ (SMIC).sz = 0; \
+} while(0)
+
+#define ERTS_SMI_INC 50
+#define ERTS_EXTEND_SUSPEND_MONITOR_INFOS(SMICP) \
+do { \
+ if ((SMICP)->smi_i >= (SMICP)->smi_max) { \
+ (SMICP)->smi = ((SMICP)->smi \
+ ? erts_realloc(ERTS_ALC_T_TMP, \
+ (SMICP)->smi, \
+ ((SMICP)->smi_max \
+ + ERTS_SMI_INC) \
+ * sizeof(ErtsSuspendMonitor *)) \
+ : erts_alloc(ERTS_ALC_T_TMP, \
+ ERTS_SMI_INC \
+ * sizeof(ErtsSuspendMonitor *))); \
+ (SMICP)->smi_max += ERTS_SMI_INC; \
+ } \
+ } while (0)
+
+#define ERTS_DESTROY_SUSPEND_MONITOR_INFOS(SMIC) \
+do { \
+ if ((SMIC).smi != NULL) { \
+ erts_free(ERTS_ALC_T_TMP, (void *) (SMIC).smi); \
+ } \
+ } while (0)
+
+static void
+collect_one_suspend_monitor(ErtsSuspendMonitor *smon, void *vsmicp)
+{
+ ErtsSuspendMonitorInfoCollection *smicp = vsmicp;
+ Process *suspendee = erts_pid2proc(smicp->c_p,
+ smicp->c_p_locks,
+ smon->pid,
+ 0);
+ if (suspendee) { /* suspendee is alive */
+ Sint a, p;
+ if (smon->active) {
+ smon->active += smon->pending;
+ smon->pending = 0;
+ }
+
+ ASSERT((smon->active && !smon->pending)
+ || (smon->pending && !smon->active));
+
+ ERTS_EXTEND_SUSPEND_MONITOR_INFOS(smicp);
+
+ smicp->smi[smicp->smi_i] = smon;
+ smicp->sz += 2 /* cons */ + 4 /* 3-tuple */;
+
+ a = (Sint) smon->active; /* quiet compiler warnings */
+ p = (Sint) smon->pending; /* on 64-bit machines */
+
+ if (!IS_SSMALL(a))
+ smicp->sz += BIG_UINT_HEAP_SIZE;
+ if (!IS_SSMALL(p))
+ smicp->sz += BIG_UINT_HEAP_SIZE;
+ smicp->smi_i++;
+ }
+}
+
+
+static void one_link_size(ErtsLink *lnk, void *vpu)
+{
+ Uint *pu = vpu;
+ *pu += ERTS_LINK_SIZE*sizeof(Uint);
+ if(!IS_CONST(lnk->pid))
+ *pu += NC_HEAP_SIZE(lnk->pid)*sizeof(Uint);
+ if (lnk->type != LINK_NODE && ERTS_LINK_ROOT(lnk) != NULL) {
+ erts_doforall_links(ERTS_LINK_ROOT(lnk),&one_link_size,vpu);
+ }
+}
+static void one_mon_size(ErtsMonitor *mon, void *vpu)
+{
+ Uint *pu = vpu;
+ *pu += ERTS_MONITOR_SIZE*sizeof(Uint);
+ if(!IS_CONST(mon->pid))
+ *pu += NC_HEAP_SIZE(mon->pid)*sizeof(Uint);
+ if(!IS_CONST(mon->ref))
+ *pu += NC_HEAP_SIZE(mon->ref)*sizeof(Uint);
+}
+
+/*
+ * process_info/[1,2]
+ */
+
+#define ERTS_PI_FAIL_TYPE_BADARG 0
+#define ERTS_PI_FAIL_TYPE_YIELD 1
+#define ERTS_PI_FAIL_TYPE_AWAIT_EXIT 2
+
+static ERTS_INLINE ErtsProcLocks
+pi_locks(Eterm info)
+{
+ switch (info) {
+ case am_status:
+ case am_priority:
+ return ERTS_PROC_LOCK_STATUS;
+ case am_links:
+ case am_monitors:
+ case am_monitored_by:
+ case am_suspending:
+ return ERTS_PROC_LOCK_LINK;
+ case am_messages:
+ case am_message_queue_len:
+ case am_total_heap_size:
+ return ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_MSGQ;
+ case am_memory:
+ return ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_MSGQ;
+ default:
+ return ERTS_PROC_LOCK_MAIN;
+ }
+}
+
+/*
+ * All valid process_info arguments.
+ */
+static Eterm pi_args[] = {
+ am_registered_name,
+ am_current_function,
+ am_initial_call,
+ am_status,
+ am_messages,
+ am_message_queue_len,
+ am_links,
+ am_monitors,
+ am_monitored_by,
+ am_dictionary,
+ am_trap_exit,
+ am_error_handler,
+ am_heap_size,
+ am_stack_size,
+ am_memory,
+ am_garbage_collection,
+ am_group_leader,
+ am_reductions,
+ am_priority,
+ am_trace,
+ am_binary,
+ am_sequential_trace_token,
+ am_catchlevel,
+ am_backtrace,
+ am_last_calls,
+ am_total_heap_size,
+ am_suspending,
+#ifdef HYBRID
+ am_message_binary
+#endif
+};
+
+#define ERTS_PI_ARGS ((int) (sizeof(pi_args)/sizeof(Eterm)))
+
+static ERTS_INLINE Eterm
+pi_ix2arg(int ix)
+{
+ if (ix < 0 || ERTS_PI_ARGS <= ix)
+ return am_undefined;
+ return pi_args[ix];
+}
+
+static ERTS_INLINE int
+pi_arg2ix(Eterm arg)
+{
+ switch (arg) {
+ case am_registered_name: return 0;
+ case am_current_function: return 1;
+ case am_initial_call: return 2;
+ case am_status: return 3;
+ case am_messages: return 4;
+ case am_message_queue_len: return 5;
+ case am_links: return 6;
+ case am_monitors: return 7;
+ case am_monitored_by: return 8;
+ case am_dictionary: return 9;
+ case am_trap_exit: return 10;
+ case am_error_handler: return 11;
+ case am_heap_size: return 12;
+ case am_stack_size: return 13;
+ case am_memory: return 14;
+ case am_garbage_collection: return 15;
+ case am_group_leader: return 16;
+ case am_reductions: return 17;
+ case am_priority: return 18;
+ case am_trace: return 19;
+ case am_binary: return 20;
+ case am_sequential_trace_token: return 21;
+ case am_catchlevel: return 22;
+ case am_backtrace: return 23;
+ case am_last_calls: return 24;
+ case am_total_heap_size: return 25;
+ case am_suspending: return 26;
+#ifdef HYBRID
+ case am_message_binary: return 27;
+#endif
+ default: return -1;
+ }
+}
+
+static Eterm pi_1_keys[] = {
+ am_registered_name,
+ am_current_function,
+ am_initial_call,
+ am_status,
+ am_message_queue_len,
+ am_messages,
+ am_links,
+ am_dictionary,
+ am_trap_exit,
+ am_error_handler,
+ am_priority,
+ am_group_leader,
+ am_total_heap_size,
+ am_heap_size,
+ am_stack_size,
+ am_reductions,
+ am_garbage_collection,
+ am_suspending
+};
+
+#define ERTS_PI_1_NO_OF_KEYS (sizeof(pi_1_keys)/sizeof(Eterm))
+
+static Eterm pi_1_keys_list;
+static Uint pi_1_keys_list_heap[2*ERTS_PI_1_NO_OF_KEYS];
+
+static void
+process_info_init(void)
+{
+ Eterm *hp = &pi_1_keys_list_heap[0];
+ int i;
+
+ pi_1_keys_list = NIL;
+
+ for (i = ERTS_PI_1_NO_OF_KEYS-1; i >= 0; i--) {
+ pi_1_keys_list = CONS(hp, pi_1_keys[i], pi_1_keys_list);
+ hp += 2;
+ }
+
+#ifdef DEBUG
+ { /* Make sure the process_info argument mappings are consistent */
+ int ix;
+ for (ix = 0; ix < ERTS_PI_ARGS; ix++) {
+ ASSERT(pi_arg2ix(pi_ix2arg(ix)) == ix);
+ }
+ }
+#endif
+
+}
+
+static ERTS_INLINE Process *
+pi_pid2proc(Process *c_p, Eterm pid, ErtsProcLocks info_locks)
+{
+#ifdef ERTS_SMP
+ /*
+ * If the main lock is needed, we use erts_pid2proc_not_running()
+ * instead of erts_pid2proc() for two reasons:
+ * * Current function of pid and possibly other information will
+ * have been updated so that process_info() is consistent with an
+ * info-request/info-response signal model.
+ * * We avoid blocking the whole scheduler executing the
+ * process that is calling process_info() for a long time
+ * which will happen if pid is currently running.
+ * The caller of process_info() may have to yield if pid
+ * is currently running.
+ */
+
+ if (info_locks & ERTS_PROC_LOCK_MAIN)
+ return erts_pid2proc_not_running(c_p, ERTS_PROC_LOCK_MAIN,
+ pid, info_locks);
+ else
+#endif
+ return erts_pid2proc(c_p, ERTS_PROC_LOCK_MAIN,
+ pid, info_locks);
+}
+
+
+
+BIF_RETTYPE
+process_info_aux(Process *BIF_P,
+ Process *rp,
+ Eterm rpid,
+ Eterm item,
+ int always_wrap);
+
+#define ERTS_PI_RES_ELEM_IX_BUF_INC 1024
+#define ERTS_PI_DEF_RES_ELEM_IX_BUF_SZ ERTS_PI_ARGS
+
+static Eterm
+process_info_list(Process *c_p, Eterm pid, Eterm list, int always_wrap,
+ int *fail_type)
+{
+ int want_messages = 0;
+ int def_res_elem_ix_buf[ERTS_PI_DEF_RES_ELEM_IX_BUF_SZ];
+ int *res_elem_ix = &def_res_elem_ix_buf[0];
+ int res_elem_ix_ix = -1;
+ int res_elem_ix_sz = ERTS_PI_DEF_RES_ELEM_IX_BUF_SZ;
+ Eterm part_res[ERTS_PI_ARGS];
+ Eterm res, arg;
+ Uint *hp, *hp_end;
+ ErtsProcLocks locks = (ErtsProcLocks) 0;
+ int res_len, ix;
+ Process *rp = NULL;
+
+ *fail_type = ERTS_PI_FAIL_TYPE_BADARG;
+
+ for (ix = 0; ix < ERTS_PI_ARGS; ix++)
+ part_res[ix] = THE_NON_VALUE;
+
+ ASSERT(is_list(list));
+
+ while (is_list(list)) {
+ Eterm* consp = list_val(list);
+
+ arg = CAR(consp);
+ ix = pi_arg2ix(arg);
+ if (ix < 0) {
+ res = THE_NON_VALUE;
+ goto done;
+ }
+ if (arg == am_messages)
+ want_messages = 1;
+ locks |= pi_locks(arg);
+ res_elem_ix_ix++;
+ if (res_elem_ix_ix >= res_elem_ix_sz) {
+ if (res_elem_ix != &def_res_elem_ix_buf[0])
+ res_elem_ix =
+ erts_realloc(ERTS_ALC_T_TMP,
+ res_elem_ix,
+ sizeof(int)*(res_elem_ix_sz
+ += ERTS_PI_RES_ELEM_IX_BUF_INC));
+ else {
+ int new_res_elem_ix_sz = ERTS_PI_RES_ELEM_IX_BUF_INC;
+ int *new_res_elem_ix = erts_alloc(ERTS_ALC_T_TMP,
+ sizeof(int)*new_res_elem_ix_sz);
+ sys_memcpy((void *) new_res_elem_ix,
+ (void *) res_elem_ix,
+ sizeof(int)*res_elem_ix_sz);
+ res_elem_ix = new_res_elem_ix;
+ res_elem_ix_sz = new_res_elem_ix_sz;
+ }
+ }
+ res_elem_ix[res_elem_ix_ix] = ix;
+ list = CDR(consp);
+ }
+ if (is_not_nil(list)) {
+ res = THE_NON_VALUE;
+ goto done;
+ }
+
+ res_len = res_elem_ix_ix+1;
+
+ ASSERT(res_len > 0);
+
+ rp = pi_pid2proc(c_p, pid, locks|ERTS_PROC_LOCK_STATUS);
+ if (!rp) {
+ res = am_undefined;
+ goto done;
+ }
+ else if (rp == ERTS_PROC_LOCK_BUSY) {
+ rp = NULL;
+ res = THE_NON_VALUE;
+ *fail_type = ERTS_PI_FAIL_TYPE_YIELD;
+ goto done;
+ }
+ else if (c_p != rp && ERTS_PROC_PENDING_EXIT(rp)) {
+ locks |= ERTS_PROC_LOCK_STATUS;
+ res = THE_NON_VALUE;
+ *fail_type = ERTS_PI_FAIL_TYPE_AWAIT_EXIT;
+ goto done;
+ }
+ else if (!(locks & ERTS_PROC_LOCK_STATUS)) {
+ erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_STATUS);
+ }
+
+
+ /*
+ * We always handle 'messages' first if it should be part
+ * of the result. This since if both 'messages' and
+ * 'message_queue_len' are wanted, 'messages' may
+ * change the result of 'message_queue_len' (in case
+ * the queue contain bad distribution messages).
+ */
+ if (want_messages) {
+ ix = pi_arg2ix(am_messages);
+ ASSERT(part_res[ix] == THE_NON_VALUE);
+ part_res[ix] = process_info_aux(c_p, rp, pid, am_messages, always_wrap);
+ ASSERT(part_res[ix] != THE_NON_VALUE);
+ }
+
+ for (; res_elem_ix_ix >= 0; res_elem_ix_ix--) {
+ ix = res_elem_ix[res_elem_ix_ix];
+ if (part_res[ix] == THE_NON_VALUE) {
+ arg = pi_ix2arg(ix);
+ part_res[ix] = process_info_aux(c_p, rp, pid, arg, always_wrap);
+ ASSERT(part_res[ix] != THE_NON_VALUE);
+ }
+ }
+
+ hp = HAlloc(c_p, res_len*2);
+ hp_end = hp + res_len*2;
+ res = NIL;
+
+ for (res_elem_ix_ix = res_len - 1; res_elem_ix_ix >= 0; res_elem_ix_ix--) {
+ ix = res_elem_ix[res_elem_ix_ix];
+ ASSERT(part_res[ix] != THE_NON_VALUE);
+ /*
+ * If we should ignore the value of registered_name,
+ * its value is nil. For more info, see comment in the
+ * beginning of process_info_aux().
+ */
+ if (is_nil(part_res[ix])) {
+ ASSERT(!always_wrap);
+ ASSERT(pi_ix2arg(ix) == am_registered_name);
+ }
+ else {
+ res = CONS(hp, part_res[ix], res);
+ hp += 2;
+ }
+ }
+
+ if (!always_wrap) {
+ HRelease(c_p, hp_end, hp);
+ }
+
+ done:
+
+ if (c_p == rp)
+ locks &= ~ERTS_PROC_LOCK_MAIN;
+ if (locks && rp)
+ erts_smp_proc_unlock(rp, locks);
+
+ if (res_elem_ix != &def_res_elem_ix_buf[0])
+ erts_free(ERTS_ALC_T_TMP, res_elem_ix);
+
+ return res;
+}
+
+BIF_RETTYPE process_info_1(BIF_ALIST_1)
+{
+ Eterm res;
+ int fail_type;
+
+ if (is_external_pid(BIF_ARG_1)
+ && external_pid_dist_entry(BIF_ARG_1) == erts_this_dist_entry)
+ BIF_RET(am_undefined);
+
+ if (is_not_internal_pid(BIF_ARG_1)
+ || internal_pid_index(BIF_ARG_1) >= erts_max_processes) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+
+ res = process_info_list(BIF_P, BIF_ARG_1, pi_1_keys_list, 0, &fail_type);
+ if (is_non_value(res)) {
+ switch (fail_type) {
+ case ERTS_PI_FAIL_TYPE_BADARG:
+ BIF_ERROR(BIF_P, BADARG);
+ case ERTS_PI_FAIL_TYPE_YIELD:
+ ERTS_BIF_YIELD1(bif_export[BIF_process_info_1], BIF_P, BIF_ARG_1);
+ case ERTS_PI_FAIL_TYPE_AWAIT_EXIT:
+ ERTS_BIF_AWAIT_X_DATA_TRAP(BIF_P, BIF_ARG_1, am_undefined);
+ default:
+ erl_exit(ERTS_ABORT_EXIT, "%s:%d: Internal error", __FILE__, __LINE__);
+ }
+ }
+
+ ASSERT(!(BIF_P->flags & F_P2PNR_RESCHED));
+ BIF_RET(res);
+}
+
+
+BIF_RETTYPE process_info_2(BIF_ALIST_2)
+{
+ Eterm res;
+ Process *rp;
+ Eterm pid = BIF_ARG_1;
+ ErtsProcLocks info_locks;
+ int fail_type;
+
+ if (is_external_pid(pid)
+ && external_pid_dist_entry(pid) == erts_this_dist_entry)
+ BIF_RET(am_undefined);
+
+ if (is_not_internal_pid(pid)
+ || internal_pid_index(BIF_ARG_1) >= erts_max_processes) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+
+ if (is_nil(BIF_ARG_2))
+ BIF_RET(NIL);
+
+ if (is_list(BIF_ARG_2)) {
+ res = process_info_list(BIF_P, BIF_ARG_1, BIF_ARG_2, 1, &fail_type);
+ if (is_non_value(res)) {
+ switch (fail_type) {
+ case ERTS_PI_FAIL_TYPE_BADARG:
+ BIF_ERROR(BIF_P, BADARG);
+ case ERTS_PI_FAIL_TYPE_YIELD:
+ ERTS_BIF_YIELD2(bif_export[BIF_process_info_2], BIF_P,
+ BIF_ARG_1, BIF_ARG_2);
+ case ERTS_PI_FAIL_TYPE_AWAIT_EXIT:
+ ERTS_BIF_AWAIT_X_DATA_TRAP(BIF_P, BIF_ARG_1, am_undefined);
+ default:
+ erl_exit(ERTS_ABORT_EXIT, "%s:%d: Internal error",
+ __FILE__, __LINE__);
+ }
+ }
+ ASSERT(!(BIF_P->flags & F_P2PNR_RESCHED));
+ BIF_RET(res);
+ }
+
+ if (pi_arg2ix(BIF_ARG_2) < 0)
+ BIF_ERROR(BIF_P, BADARG);
+
+ info_locks = pi_locks(BIF_ARG_2);
+
+ rp = pi_pid2proc(BIF_P, pid, info_locks|ERTS_PROC_LOCK_STATUS);
+ if (!rp)
+ res = am_undefined;
+ else if (rp == ERTS_PROC_LOCK_BUSY)
+ ERTS_BIF_YIELD2(bif_export[BIF_process_info_2], BIF_P,
+ BIF_ARG_1, BIF_ARG_2);
+ else if (rp != BIF_P && ERTS_PROC_PENDING_EXIT(rp)) {
+ erts_smp_proc_unlock(rp, info_locks|ERTS_PROC_LOCK_STATUS);
+ ERTS_BIF_AWAIT_X_DATA_TRAP(BIF_P, BIF_ARG_1, am_undefined);
+ }
+ else {
+ if (!(info_locks & ERTS_PROC_LOCK_STATUS))
+ erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_STATUS);
+ res = process_info_aux(BIF_P, rp, pid, BIF_ARG_2, 0);
+ }
+ ASSERT(is_value(res));
+
+#ifdef ERTS_SMP
+ if (BIF_P == rp)
+ info_locks &= ~ERTS_PROC_LOCK_MAIN;
+ if (rp && info_locks)
+ erts_smp_proc_unlock(rp, info_locks);
+#endif
+
+ ASSERT(!(BIF_P->flags & F_P2PNR_RESCHED));
+ BIF_RET(res);
+}
+
+Eterm
+process_info_aux(Process *BIF_P,
+ Process *rp,
+ Eterm rpid,
+ Eterm item,
+ int always_wrap)
+{
+ Eterm *hp;
+ Eterm res = NIL;
+
+ ASSERT(rp);
+
+ /*
+ * Q: Why this always_wrap argument?
+ *
+ * A: registered_name is strange. If process has no registered name,
+ * process_info(Pid, registered_name) returns [], and
+ * the result of process_info(Pid) has no {registered_name, Name}
+ * tuple in the resulting list. This is inconsistent with all other
+ * options, but we do not dare to change it.
+ *
+ * When process_info/2 is called with a list as second argument,
+ * registered_name behaves as it should, i.e. a
+ * {registered_name, []} will appear in the resulting list.
+ *
+ * If always_wrap != 0, process_info_aux() always wrap the result
+ * in a key two tuple.
+ */
+
+ switch (item) {
+
+ case am_registered_name:
+ if (rp->reg != NULL) {
+ hp = HAlloc(BIF_P, 3);
+ res = rp->reg->name;
+ } else {
+ if (always_wrap) {
+ hp = HAlloc(BIF_P, 3);
+ res = NIL;
+ }
+ else {
+ return NIL;
+ }
+ }
+ break;
+
+ case am_current_function:
+ if (rp->current == NULL) {
+ rp->current = find_function_from_pc(rp->i);
+ }
+ if (rp->current == NULL) {
+ hp = HAlloc(BIF_P, 3);
+ res = am_undefined;
+ } else {
+ Eterm* current;
+
+ if (rp->current[0] == am_erlang &&
+ rp->current[1] == am_process_info &&
+ (rp->current[2] == 1 || rp->current[2] == 2) &&
+ (current = find_function_from_pc(rp->cp)) != NULL) {
+
+ /*
+ * The current function is erlang:process_info/2,
+ * which is not the answer that the application want.
+ * We will use the function pointed into by rp->cp
+ * instead.
+ */
+
+ rp->current = current;
+ }
+
+ hp = HAlloc(BIF_P, 3+4);
+ res = TUPLE3(hp, rp->current[0],
+ rp->current[1], make_small(rp->current[2]));
+ hp += 4;
+ }
+ break;
+
+ case am_initial_call:
+ hp = HAlloc(BIF_P, 3+4);
+ res = TUPLE3(hp,
+ rp->initial[INITIAL_MOD],
+ rp->initial[INITIAL_FUN],
+ make_small(rp->initial[INITIAL_ARI]));
+ hp += 4;
+ break;
+
+ case am_status:
+ res = erts_process_status(BIF_P, ERTS_PROC_LOCK_MAIN, rp, rpid);
+ ASSERT(res != am_undefined);
+ hp = HAlloc(BIF_P, 3);
+ break;
+
+ case am_messages: {
+ ErlMessage* mp;
+ int n;
+
+ ERTS_SMP_MSGQ_MV_INQ2PRIVQ(rp);
+ n = rp->msg.len;
+
+ if (n == 0 || rp->trace_flags & F_SENSITIVE) {
+ hp = HAlloc(BIF_P, 3);
+ } else {
+ int remove_bad_messages = 0;
+ struct {
+ Uint copy_struct_size;
+ ErlMessage* msgp;
+ } *mq = erts_alloc(ERTS_ALC_T_TMP, n*sizeof(*mq));
+ Sint i = 0;
+ Uint heap_need = 3;
+ Eterm *hp_end;
+
+ for (mp = rp->msg.first; mp; mp = mp->next) {
+ heap_need += 2;
+ mq[i].msgp = mp;
+ if (rp != BIF_P) {
+ Eterm msg = ERL_MESSAGE_TERM(mq[i].msgp);
+ if (is_value(msg)) {
+ mq[i].copy_struct_size = (is_immed(msg)
+#ifdef HYBRID
+ || NO_COPY(msg)
+#endif
+ ? 0
+ : size_object(msg));
+ }
+ else if (mq[i].msgp->data.attached) {
+ mq[i].copy_struct_size
+ = erts_msg_attached_data_size(mq[i].msgp);
+ }
+ else {
+ /* Bad distribution message; ignore */
+ remove_bad_messages = 1;
+ mq[i].copy_struct_size = 0;
+ }
+ heap_need += mq[i].copy_struct_size;
+ }
+ else {
+ mq[i].copy_struct_size = 0;
+ if (mp->data.attached)
+ heap_need += erts_msg_attached_data_size(mp);
+ }
+ i++;
+ }
+
+ hp = HAlloc(BIF_P, heap_need);
+ hp_end = hp + heap_need;
+ ASSERT(i == n);
+ for (i--; i >= 0; i--) {
+ Eterm msg = ERL_MESSAGE_TERM(mq[i].msgp);
+ if (rp != BIF_P) {
+ if (is_value(msg)) {
+ if (mq[i].copy_struct_size)
+ msg = copy_struct(msg,
+ mq[i].copy_struct_size,
+ &hp,
+ &MSO(BIF_P));
+ }
+ else if (mq[i].msgp->data.attached) {
+ ErlHeapFragment *hfp;
+ /*
+ * Decode it into a message buffer and attach it
+ * to the message instead of the attached external
+ * term.
+ *
+ * Note that we may not pass a process pointer
+ * to erts_msg_distext2heap(), since it would then
+ * try to alter locks on that process.
+ */
+ msg = erts_msg_distext2heap(
+ NULL, NULL, &hfp, &ERL_MESSAGE_TOKEN(mq[i].msgp),
+ mq[i].msgp->data.dist_ext);
+
+ ERL_MESSAGE_TERM(mq[i].msgp) = msg;
+ mq[i].msgp->data.heap_frag = hfp;
+
+ if (is_non_value(msg)) {
+ ASSERT(!mq[i].msgp->data.heap_frag);
+ /* Bad distribution message; ignore */
+ remove_bad_messages = 1;
+ continue;
+ }
+ else {
+ /* Make our copy of the message */
+ ASSERT(size_object(msg) == hfp->size);
+ msg = copy_struct(msg,
+ hfp->size,
+ &hp,
+ &MSO(BIF_P));
+ }
+ }
+ else {
+ /* Bad distribution message; ignore */
+ remove_bad_messages = 1;
+ continue;
+ }
+ }
+ else {
+ if (mq[i].msgp->data.attached) {
+ /* Decode it on the heap */
+ erts_move_msg_attached_data_to_heap(&hp,
+ &MSO(BIF_P),
+ mq[i].msgp);
+ msg = ERL_MESSAGE_TERM(mq[i].msgp);
+ ASSERT(!mq[i].msgp->data.attached);
+ if (is_non_value(msg)) {
+ /* Bad distribution message; ignore */
+ remove_bad_messages = 1;
+ continue;
+ }
+ }
+ }
+
+ res = CONS(hp, msg, res);
+ hp += 2;
+ }
+ HRelease(BIF_P, hp_end, hp+3);
+ erts_free(ERTS_ALC_T_TMP, mq);
+ if (remove_bad_messages) {
+ ErlMessage **mpp;
+ /*
+ * We need to remove bad distribution messages from
+ * the queue, so that the value returned for
+ * 'message_queue_len' is consistent with the value
+ * returned for 'messages'.
+ */
+ mpp = &rp->msg.first;
+ mp = rp->msg.first;
+ while (mp) {
+ if (is_value(ERL_MESSAGE_TERM(mp))) {
+ mpp = &mp->next;
+ mp = mp->next;
+ }
+ else {
+ ErlMessage* bad_mp = mp;
+ ASSERT(!mp->data.attached);
+ if (rp->msg.save == &mp->next)
+ rp->msg.save = mpp;
+ if (rp->msg.last == &mp->next)
+ rp->msg.last = mpp;
+ *mpp = mp->next;
+ mp = mp->next;
+ rp->msg.len--;
+ free_message(bad_mp);
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ case am_message_queue_len:
+ hp = HAlloc(BIF_P, 3);
+ ERTS_SMP_MSGQ_MV_INQ2PRIVQ(rp);
+ res = make_small(rp->msg.len);
+ break;
+
+ case am_links: {
+ MonitorInfoCollection mic;
+ int i;
+ Eterm item;
+
+ INIT_MONITOR_INFOS(mic);
+
+ erts_doforall_links(rp->nlinks,&collect_one_link,&mic);
+
+ hp = HAlloc(BIF_P, 3 + mic.sz);
+ res = NIL;
+ for (i = 0; i < mic.mi_i; i++) {
+ item = STORE_NC(&hp, &MSO(BIF_P).externals, mic.mi[i].entity);
+ res = CONS(hp, item, res);
+ hp += 2;
+ }
+ DESTROY_MONITOR_INFOS(mic);
+ break;
+ }
+
+ case am_monitors: {
+ MonitorInfoCollection mic;
+ int i;
+
+ INIT_MONITOR_INFOS(mic);
+ erts_doforall_monitors(rp->monitors,&collect_one_origin_monitor,&mic);
+ hp = HAlloc(BIF_P, 3 + mic.sz);
+ res = NIL;
+ for (i = 0; i < mic.mi_i; i++) {
+ if (is_atom(mic.mi[i].entity)) {
+ /* Monitor by name.
+ * Build {process, {Name, Node}} and cons it.
+ */
+ Eterm t1, t2;
+
+ t1 = TUPLE2(hp, mic.mi[i].entity, mic.mi[i].node);
+ hp += 3;
+ t2 = TUPLE2(hp, am_process, t1);
+ hp += 3;
+ res = CONS(hp, t2, res);
+ hp += 2;
+ }
+ else {
+ /* Monitor by pid. Build {process, Pid} and cons it. */
+ Eterm t;
+ Eterm pid = STORE_NC(&hp,
+ &MSO(BIF_P).externals,
+ mic.mi[i].entity);
+ t = TUPLE2(hp, am_process, pid);
+ hp += 3;
+ res = CONS(hp, t, res);
+ hp += 2;
+ }
+ }
+ DESTROY_MONITOR_INFOS(mic);
+ break;
+ }
+
+ case am_monitored_by: {
+ MonitorInfoCollection mic;
+ int i;
+ Eterm item;
+
+ INIT_MONITOR_INFOS(mic);
+ erts_doforall_monitors(rp->monitors,&collect_one_target_monitor,&mic);
+ hp = HAlloc(BIF_P, 3 + mic.sz);
+
+ res = NIL;
+ for (i = 0; i < mic.mi_i; ++i) {
+ item = STORE_NC(&hp, &MSO(BIF_P).externals, mic.mi[i].entity);
+ res = CONS(hp, item, res);
+ hp += 2;
+ }
+ DESTROY_MONITOR_INFOS(mic);
+ break;
+ }
+
+ case am_suspending: {
+ ErtsSuspendMonitorInfoCollection smic;
+ int i;
+ Eterm item;
+#ifdef DEBUG
+ Eterm *hp_end;
+#endif
+
+ ERTS_INIT_SUSPEND_MONITOR_INFOS(smic,
+ BIF_P,
+ (BIF_P == rp
+ ? ERTS_PROC_LOCK_MAIN
+ : 0) | ERTS_PROC_LOCK_LINK);
+
+ erts_doforall_suspend_monitors(rp->suspend_monitors,
+ &collect_one_suspend_monitor,
+ &smic);
+ hp = HAlloc(BIF_P, 3 + smic.sz);
+#ifdef DEBUG
+ hp_end = hp + smic.sz;
+#endif
+
+ res = NIL;
+ for (i = 0; i < smic.smi_i; i++) {
+ Sint a = (Sint) smic.smi[i]->active; /* quiet compiler warnings */
+ Sint p = (Sint) smic.smi[i]->pending; /* on 64-bit machines... */
+ Eterm active;
+ Eterm pending;
+ if (IS_SSMALL(a))
+ active = make_small(a);
+ else {
+ active = small_to_big(a, hp);
+ hp += BIG_UINT_HEAP_SIZE;
+ }
+ if (IS_SSMALL(p))
+ pending = make_small(p);
+ else {
+ pending = small_to_big(p, hp);
+ hp += BIG_UINT_HEAP_SIZE;
+ }
+ item = TUPLE3(hp, smic.smi[i]->pid, active, pending);
+ hp += 4;
+ res = CONS(hp, item, res);
+ hp += 2;
+ }
+
+ ERTS_DESTROY_SUSPEND_MONITOR_INFOS(smic);
+ ASSERT(hp == hp_end);
+
+ break;
+ }
+
+ case am_dictionary:
+ if (rp->trace_flags & F_SENSITIVE) {
+ res = NIL;
+ } else {
+ res = erts_dictionary_copy(BIF_P, rp->dictionary);
+ }
+ hp = HAlloc(BIF_P, 3);
+ break;
+
+ case am_trap_exit:
+ hp = HAlloc(BIF_P, 3);
+ if (rp->flags & F_TRAPEXIT)
+ res = am_true;
+ else
+ res = am_false;
+ break;
+
+ case am_error_handler:
+ hp = HAlloc(BIF_P, 3);
+ res = erts_proc_get_error_handler(BIF_P);
+ break;
+
+ case am_heap_size: {
+ Uint hsz = 3;
+ (void) erts_bld_uint(NULL, &hsz, HEAP_SIZE(rp));
+ hp = HAlloc(BIF_P, hsz);
+ res = erts_bld_uint(&hp, NULL, HEAP_SIZE(rp));
+ break;
+ }
+
+ case am_total_heap_size: {
+ ErlMessage *mp;
+ Uint total_heap_size;
+ Uint hsz = 3;
+
+ total_heap_size = rp->heap_sz;
+ if (rp->old_hend && rp->old_heap)
+ total_heap_size += rp->old_hend - rp->old_heap;
+
+ total_heap_size += rp->mbuf_sz;
+
+ ERTS_SMP_MSGQ_MV_INQ2PRIVQ(rp);
+
+ for (mp = rp->msg.first; mp; mp = mp->next)
+ if (mp->data.attached)
+ total_heap_size += erts_msg_attached_data_size(mp);
+
+ (void) erts_bld_uint(NULL, &hsz, total_heap_size);
+ hp = HAlloc(BIF_P, hsz);
+ res = erts_bld_uint(&hp, NULL, total_heap_size);
+ break;
+ }
+
+ case am_stack_size: {
+ Uint stack_size = STACK_START(rp) - rp->stop;
+ Uint hsz = 3;
+ (void) erts_bld_uint(NULL, &hsz, stack_size);
+ hp = HAlloc(BIF_P, hsz);
+ res = erts_bld_uint(&hp, NULL, stack_size);
+ break;
+ }
+
+ case am_memory: { /* Memory consumed in bytes */
+ ErlMessage *mp;
+ Uint size = 0;
+ Uint hsz = 3;
+ struct saved_calls *scb;
+ size += sizeof(Process);
+
+ ERTS_SMP_MSGQ_MV_INQ2PRIVQ(rp);
+
+ erts_doforall_links(rp->nlinks, &one_link_size, &size);
+ erts_doforall_monitors(rp->monitors, &one_mon_size, &size);
+ size += (rp->heap_sz + rp->mbuf_sz) * sizeof(Eterm);
+ if (rp->old_hend && rp->old_heap)
+ size += (rp->old_hend - rp->old_heap) * sizeof(Eterm);
+
+ size += rp->msg.len * sizeof(ErlMessage);
+
+ for (mp = rp->msg.first; mp; mp = mp->next)
+ if (mp->data.attached)
+ size += erts_msg_attached_data_size(mp)*sizeof(Eterm);
+
+ if (rp->arg_reg != rp->def_arg_reg) {
+ size += rp->arity * sizeof(rp->arg_reg[0]);
+ }
+
+ if (rp->psd)
+ size += sizeof(ErtsPSD);
+
+ scb = ERTS_PROC_GET_SAVED_CALLS_BUF(rp);
+ if (scb) {
+ size += (sizeof(struct saved_calls)
+ + (scb->len-1) * sizeof(scb->ct[0]));
+ }
+
+ size += erts_dicts_mem_size(rp);
+
+ (void) erts_bld_uint(NULL, &hsz, size);
+ hp = HAlloc(BIF_P, hsz);
+ res = erts_bld_uint(&hp, NULL, size);
+ break;
+ }
+
+ case am_garbage_collection: {
+ DECL_AM(minor_gcs);
+ Eterm t;
+
+ hp = HAlloc(BIF_P, 3+2+3+2+3);
+ t = TUPLE2(hp, AM_minor_gcs, make_small(GEN_GCS(rp)));
+ hp += 3;
+ res = CONS(hp, t, NIL);
+ hp += 2;
+ t = TUPLE2(hp, am_fullsweep_after, make_small(MAX_GEN_GCS(rp)));
+ hp += 3;
+ res = CONS(hp, t, res);
+ hp += 2;
+ break;
+ }
+
+ case am_group_leader: {
+ int sz = NC_HEAP_SIZE(rp->group_leader);
+ hp = HAlloc(BIF_P, 3 + sz);
+ res = STORE_NC(&hp, &MSO(BIF_P).externals, rp->group_leader);
+ break;
+ }
+
+ case am_reductions: {
+ Uint reds = rp->reds + erts_current_reductions(BIF_P, rp);
+ Uint hsz = 3;
+ (void) erts_bld_uint(NULL, &hsz, reds);
+ hp = HAlloc(BIF_P, hsz);
+ res = erts_bld_uint(&hp, NULL, reds);
+ break;
+ }
+
+ case am_priority:
+ hp = HAlloc(BIF_P, 3);
+ res = erts_get_process_priority(rp);
+ break;
+
+ case am_trace:
+ hp = HAlloc(BIF_P, 3);
+ res = make_small(rp->trace_flags & TRACEE_FLAGS);
+ break;
+
+ case am_binary: {
+ Uint sz = 3;
+ (void) bld_bin_list(NULL, &sz, MSO(rp).mso);
+ hp = HAlloc(BIF_P, sz);
+ res = bld_bin_list(&hp, NULL, MSO(rp).mso);
+ break;
+ }
+
+#ifdef HYBRID
+ case am_message_binary: {
+ Uint sz = 3;
+ (void) bld_bin_list(NULL, &sz, erts_global_offheap.mso);
+ hp = HAlloc(BIF_P, sz);
+ res = bld_bin_list(&hp, NULL, erts_global_offheap.mso);
+ break;
+ }
+#endif
+
+ case am_sequential_trace_token:
+ res = copy_object(rp->seq_trace_token, BIF_P);
+ hp = HAlloc(BIF_P, 3);
+ break;
+
+ case am_catchlevel:
+ hp = HAlloc(BIF_P, 3);
+ res = make_small(catchlevel(BIF_P));
+ break;
+
+ case am_backtrace: {
+ erts_dsprintf_buf_t *dsbufp = erts_create_tmp_dsbuf(0);
+ erts_stack_dump(ERTS_PRINT_DSBUF, (void *) dsbufp, rp);
+ res = new_binary(BIF_P, (byte *) dsbufp->str, (int) dsbufp->str_len);
+ erts_destroy_tmp_dsbuf(dsbufp);
+ hp = HAlloc(BIF_P, 3);
+ break;
+ }
+
+ case am_last_calls: {
+ struct saved_calls *scb = ERTS_PROC_GET_SAVED_CALLS_BUF(BIF_P);
+ if (!scb) {
+ hp = HAlloc(BIF_P, 3);
+ res = am_false;
+ } else {
+ /*
+ * One cons cell and a 3-struct, and a 2-tuple.
+ * Might be less than that, if there are sends, receives or timeouts,
+ * so we must do a HRelease() to avoid creating holes.
+ */
+ Uint needed = scb->n*(2+4) + 3;
+ Eterm* limit;
+ Eterm term, list;
+ int i, j;
+
+ hp = HAlloc(BIF_P, needed);
+ limit = hp + needed;
+ list = NIL;
+ for (i = 0; i < scb->n; i++) {
+ j = scb->cur - i - 1;
+ if (j < 0)
+ j += scb->len;
+ if (scb->ct[j] == &exp_send)
+ term = am_send;
+ else if (scb->ct[j] == &exp_receive)
+ term = am_receive;
+ else if (scb->ct[j] == &exp_timeout)
+ term = am_timeout;
+ else {
+ term = TUPLE3(hp,
+ scb->ct[j]->code[0],
+ scb->ct[j]->code[1],
+ make_small(scb->ct[j]->code[2]));
+ hp += 4;
+ }
+ list = CONS(hp, term, list);
+ hp += 2;
+ }
+ res = list;
+ res = TUPLE2(hp, item, res);
+ hp += 3;
+ HRelease(BIF_P,limit,hp);
+ return res;
+ }
+ break;
+ }
+
+ default:
+ return THE_NON_VALUE; /* will produce badarg */
+
+ }
+
+ return TUPLE2(hp, item, res);
+}
+#undef MI_INC
+
+#if defined(VALGRIND)
+static int check_if_xml(void)
+{
+ char buf[1];
+ size_t bufsz = sizeof(buf);
+ return erts_sys_getenv("VALGRIND_LOG_XML", buf, &bufsz) >= 0;
+}
+#else
+#define check_if_xml() 0
+#endif
+
+/*
+ * This function takes care of calls to erlang:system_info/1 when the argument
+ * is a tuple.
+ */
+static BIF_RETTYPE
+info_1_tuple(Process* BIF_P, /* Pointer to current process. */
+ Eterm* tp, /* Pointer to first element in tuple */
+ int arity) /* Arity of tuple (untagged). */
+{
+ Eterm ret;
+ Eterm sel;
+
+ sel = *tp++;
+
+ if (sel == am_allocator_sizes && arity == 2) {
+ return erts_allocator_info_term(BIF_P, *tp, 1);
+ } else if (sel == am_allocated) {
+ if (arity == 2) {
+ Eterm res = THE_NON_VALUE;
+ char *buf;
+ int len = is_string(*tp);
+ if (len <= 0)
+ return res;
+ buf = (char *) erts_alloc(ERTS_ALC_T_TMP, len+1);
+ if (intlist_to_buf(*tp, buf, len) != len)
+ erl_exit(1, "%s:%d: Internal error\n", __FILE__, __LINE__);
+ buf[len] = '\0';
+ res = erts_instr_dump_memory_map(buf) ? am_true : am_false;
+ erts_free(ERTS_ALC_T_TMP, (void *) buf);
+ if (is_non_value(res))
+ goto badarg;
+ return res;
+ }
+ else if (arity == 3 && tp[0] == am_status) {
+ if (is_atom(tp[1]))
+ return erts_instr_get_stat(BIF_P, tp[1], 1);
+ else {
+ Eterm res = THE_NON_VALUE;
+ char *buf;
+ int len = is_string(tp[1]);
+ if (len <= 0)
+ return res;
+ buf = (char *) erts_alloc(ERTS_ALC_T_TMP, len+1);
+ if (intlist_to_buf(tp[1], buf, len) != len)
+ erl_exit(1, "%s:%d: Internal error\n", __FILE__, __LINE__);
+ buf[len] = '\0';
+ res = erts_instr_dump_stat(buf, 1) ? am_true : am_false;
+ erts_free(ERTS_ALC_T_TMP, (void *) buf);
+ if (is_non_value(res))
+ goto badarg;
+ return res;
+ }
+ }
+ else
+ goto badarg;
+ } else if (sel == am_allocator && arity == 2) {
+ return erts_allocator_info_term(BIF_P, *tp, 0);
+ } else if (ERTS_IS_ATOM_STR("internal_cpu_topology", sel) && arity == 2) {
+ return erts_get_cpu_topology_term(BIF_P, *tp);
+ } else if (ERTS_IS_ATOM_STR("cpu_topology", sel) && arity == 2) {
+ Eterm res = erts_get_cpu_topology_term(BIF_P, *tp);
+ ERTS_BIF_PREP_TRAP1(ret, erts_format_cpu_topology_trap, BIF_P, res);
+ return ret;
+#if defined(PURIFY) || defined(VALGRIND)
+ } else if (ERTS_IS_ATOM_STR("error_checker", sel)
+#if defined(PURIFY)
+ || sel == am_purify
+#elif defined(VALGRIND)
+ || ERTS_IS_ATOM_STR("valgrind", sel)
+#endif
+ ) {
+ if (*tp == am_memory) {
+#if defined(PURIFY)
+ BIF_RET(erts_make_integer(purify_new_leaks(), BIF_P));
+#elif defined(VALGRIND)
+ VALGRIND_DO_LEAK_CHECK;
+ BIF_RET(make_small(0));
+#endif
+ } else if (*tp == am_fd) {
+#if defined(PURIFY)
+ BIF_RET(erts_make_integer(purify_new_fds_inuse(), BIF_P));
+#elif defined(VALGRIND)
+ /* Not present in valgrind... */
+ BIF_RET(make_small(0));
+#endif
+ } else if (*tp == am_running) {
+#if defined(PURIFY)
+ BIF_RET(purify_is_running() ? am_true : am_false);
+#elif defined(VALGRIND)
+ BIF_RET(RUNNING_ON_VALGRIND ? am_true : am_false);
+#endif
+ } else if (is_list(*tp)) {
+#if defined(PURIFY)
+#define ERTS_ERROR_CHECKER_PRINTF purify_printf
+#elif defined(VALGRIND)
+#define ERTS_ERROR_CHECKER_PRINTF VALGRIND_PRINTF
+#endif
+ int buf_size = 8*1024; /* Try with 8KB first */
+ char *buf = erts_alloc(ERTS_ALC_T_TMP, buf_size);
+ int r = io_list_to_buf(*tp, (char*) buf, buf_size - 1);
+ if (r < 0) {
+ erts_free(ERTS_ALC_T_TMP, (void *) buf);
+ buf_size = io_list_len(*tp);
+ if (buf_size < 0)
+ goto badarg;
+ buf_size++;
+ buf = erts_alloc(ERTS_ALC_T_TMP, buf_size);
+ r = io_list_to_buf(*tp, (char*) buf, buf_size - 1);
+ ASSERT(r == buf_size - 1);
+ }
+ buf[buf_size - 1 - r] = '\0';
+ if (check_if_xml()) {
+ ERTS_ERROR_CHECKER_PRINTF("<erlang_info_log>"
+ "%s</erlang_info_log>\n", buf);
+ } else {
+ ERTS_ERROR_CHECKER_PRINTF("%s\n", buf);
+ }
+ erts_free(ERTS_ALC_T_TMP, (void *) buf);
+ BIF_RET(am_true);
+#undef ERTS_ERROR_CHECKER_PRINTF
+ }
+#endif
+#ifdef QUANTIFY
+ } else if (sel == am_quantify) {
+ if (*tp == am_clear) {
+ quantify_clear_data();
+ BIF_RET(am_true);
+ } else if (*tp == am_start) {
+ quantify_start_recording_data();
+ BIF_RET(am_true);
+ } else if (*tp == am_stop) {
+ quantify_stop_recording_data();
+ BIF_RET(am_true);
+ } else if (*tp == am_running) {
+ BIF_RET(quantify_is_running() ? am_true : am_false);
+ }
+#endif
+#if defined(__GNUC__) && defined(HAVE_SOLARIS_SPARC_PERFMON)
+ } else if (ERTS_IS_ATOM_STR("ultrasparc_set_pcr", sel)) {
+ unsigned long long tmp;
+ int fd;
+ int rc;
+
+ if (arity != 2 || !is_small(*tp)) {
+ goto badarg;
+ }
+ tmp = signed_val(*tp);
+ if ((fd = open("/dev/perfmon", O_RDONLY)) == -1) {
+ BIF_RET(am_false);
+ }
+ rc = ioctl(fd, PERFMON_SETPCR, &tmp);
+ close(fd);
+ if (rc < 0) {
+ BIF_RET(am_false);
+ }
+ BIF_RET(am_true);
+#endif
+ }
+
+ badarg:
+ ERTS_BIF_PREP_ERROR(ret, BIF_P, BADARG);
+
+ return ret;
+}
+
+#define INFO_DSBUF_INC_SZ 256
+
+static erts_dsprintf_buf_t *
+grow_info_dsbuf(erts_dsprintf_buf_t *dsbufp, size_t need)
+{
+ size_t size;
+ size_t free_size = dsbufp->size - dsbufp->str_len;
+
+ ASSERT(dsbufp);
+
+ if (need <= free_size)
+ return dsbufp;
+ size = need - free_size + INFO_DSBUF_INC_SZ;
+ size = ((size + INFO_DSBUF_INC_SZ - 1)/INFO_DSBUF_INC_SZ)*INFO_DSBUF_INC_SZ;
+ size += dsbufp->size;
+ ASSERT(dsbufp->str_len + need <= size);
+ dsbufp->str = (char *) erts_realloc(ERTS_ALC_T_INFO_DSBUF,
+ (void *) dsbufp->str,
+ size);
+ dsbufp->size = size;
+ return dsbufp;
+}
+
+static erts_dsprintf_buf_t *
+erts_create_info_dsbuf(Uint size)
+{
+ Uint init_size = size ? size : INFO_DSBUF_INC_SZ;
+ erts_dsprintf_buf_t init = ERTS_DSPRINTF_BUF_INITER(grow_info_dsbuf);
+ erts_dsprintf_buf_t *dsbufp = erts_alloc(ERTS_ALC_T_INFO_DSBUF,
+ sizeof(erts_dsprintf_buf_t));
+ sys_memcpy((void *) dsbufp, (void *) &init, sizeof(erts_dsprintf_buf_t));
+ dsbufp->str = (char *) erts_alloc(ERTS_ALC_T_INFO_DSBUF, init_size);
+ dsbufp->str[0] = '\0';
+ dsbufp->size = init_size;
+ return dsbufp;
+}
+
+static void
+erts_destroy_info_dsbuf(erts_dsprintf_buf_t *dsbufp)
+{
+ if (dsbufp->str)
+ erts_free(ERTS_ALC_T_INFO_DSBUF, (void *) dsbufp->str);
+ erts_free(ERTS_ALC_T_INFO_DSBUF, (void *) dsbufp);
+}
+
+static Eterm
+c_compiler_used(Eterm **hpp, Uint *szp)
+{
+
+#if defined(__GNUC__)
+# if defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__)
+# define ERTS_GNUC_VSN_NUMS 3
+# elif defined(__GNUC_MINOR__)
+# define ERTS_GNUC_VSN_NUMS 2
+# else
+# define ERTS_GNUC_VSN_NUMS 1
+# endif
+ return erts_bld_tuple(hpp,
+ szp,
+ 2,
+ erts_bld_atom(hpp, szp, "gnuc"),
+#if ERTS_GNUC_VSN_NUMS > 1
+ erts_bld_tuple(hpp,
+ szp,
+ ERTS_GNUC_VSN_NUMS,
+#endif
+ erts_bld_uint(hpp, szp,
+ (Uint) __GNUC__)
+#ifdef __GNUC_MINOR__
+ ,
+ erts_bld_uint(hpp, szp,
+ (Uint) __GNUC_MINOR__)
+#ifdef __GNUC_PATCHLEVEL__
+ ,
+ erts_bld_uint(hpp, szp,
+ (Uint) __GNUC_PATCHLEVEL__)
+#endif
+#endif
+#if ERTS_GNUC_VSN_NUMS > 1
+ )
+#endif
+ );
+
+#elif defined(_MSC_VER)
+ return erts_bld_tuple(hpp,
+ szp,
+ 2,
+ erts_bld_atom(hpp, szp, "msc"),
+ erts_bld_uint(hpp, szp, (Uint) _MSC_VER));
+
+#else
+ return erts_bld_tuple(hpp,
+ szp,
+ 2,
+ am_undefined,
+ am_undefined);
+#endif
+
+}
+
+BIF_RETTYPE system_info_1(BIF_ALIST_1)
+{
+ Eterm res;
+ Eterm* hp;
+ Eterm val;
+ int i;
+
+ if (is_tuple(BIF_ARG_1)) {
+ Eterm* tp = tuple_val(BIF_ARG_1);
+ Uint arity = *tp++;
+ return info_1_tuple(BIF_P, tp, arityval(arity));
+ } else if (BIF_ARG_1 == am_scheduler_id) {
+#ifdef ERTS_SMP
+ ASSERT(BIF_P->scheduler_data);
+ BIF_RET(make_small(BIF_P->scheduler_data->no));
+#else
+ BIF_RET(make_small(1));
+#endif
+ } else if (BIF_ARG_1 == am_compat_rel) {
+ ASSERT(erts_compat_rel > 0);
+ BIF_RET(make_small(erts_compat_rel));
+ } else if (BIF_ARG_1 == am_multi_scheduling) {
+#ifndef ERTS_SMP
+ BIF_RET(am_disabled);
+#else
+ if (erts_no_schedulers == 1)
+ BIF_RET(am_disabled);
+ else {
+ BIF_RET(erts_is_multi_scheduling_blocked()
+ ? am_blocked
+ : am_enabled);
+ }
+#endif
+ } else if (BIF_ARG_1 == am_allocated_areas) {
+ res = erts_allocated_areas(NULL, NULL, BIF_P);
+ BIF_RET(res);
+ } else if (BIF_ARG_1 == am_allocated) {
+ BIF_RET(erts_instr_get_memory_map(BIF_P));
+ } else if (BIF_ARG_1 == am_hipe_architecture) {
+#if defined(HIPE)
+ BIF_RET(hipe_arch_name);
+#else
+ BIF_RET(am_undefined);
+#endif
+ } else if (BIF_ARG_1 == am_trace_control_word) {
+ BIF_RET(db_get_trace_control_word_0(BIF_P));
+ } else if (ERTS_IS_ATOM_STR("ets_realloc_moves", BIF_ARG_1)) {
+ BIF_RET((erts_ets_realloc_always_moves) ? am_true : am_false);
+ } else if (BIF_ARG_1 == am_sequential_tracer) {
+ val = erts_get_system_seq_tracer();
+ ASSERT(is_internal_pid(val) || is_internal_port(val) || val==am_false)
+ hp = HAlloc(BIF_P, 3);
+ res = TUPLE2(hp, am_sequential_tracer, val);
+ BIF_RET(res);
+ } else if (BIF_ARG_1 == am_garbage_collection){
+ Uint val = (Uint) erts_smp_atomic_read(&erts_max_gen_gcs);
+ hp = HAlloc(BIF_P, 3+2);
+ res = TUPLE2(hp, am_fullsweep_after, make_small(val));
+ hp += 3;
+ res = CONS(hp, res, NIL);
+ BIF_RET(res);
+ } else if (BIF_ARG_1 == am_fullsweep_after){
+ Uint val = (Uint) erts_smp_atomic_read(&erts_max_gen_gcs);
+ hp = HAlloc(BIF_P, 3);
+ res = TUPLE2(hp, am_fullsweep_after, make_small(val));
+ BIF_RET(res);
+ } else if (BIF_ARG_1 == am_process_count) {
+ BIF_RET(make_small(erts_process_count()));
+ } else if (BIF_ARG_1 == am_process_limit) {
+ BIF_RET(make_small(erts_max_processes));
+ } else if (BIF_ARG_1 == am_info
+ || BIF_ARG_1 == am_procs
+ || BIF_ARG_1 == am_loaded
+ || BIF_ARG_1 == am_dist) {
+ erts_dsprintf_buf_t *dsbufp = erts_create_info_dsbuf(0);
+
+ /* Need to be the only thread running... */
+ erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ erts_smp_block_system(0);
+
+ if (BIF_ARG_1 == am_info)
+ info(ERTS_PRINT_DSBUF, (void *) dsbufp);
+ else if (BIF_ARG_1 == am_procs)
+ process_info(ERTS_PRINT_DSBUF, (void *) dsbufp);
+ else if (BIF_ARG_1 == am_loaded)
+ loaded(ERTS_PRINT_DSBUF, (void *) dsbufp);
+ else
+ distribution_info(ERTS_PRINT_DSBUF, (void *) dsbufp);
+
+ erts_smp_release_system();
+ erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
+
+ ASSERT(dsbufp && dsbufp->str);
+ res = new_binary(BIF_P, (byte *) dsbufp->str, (int) dsbufp->str_len);
+ erts_destroy_info_dsbuf(dsbufp);
+ BIF_RET(res);
+ } else if (ERTS_IS_ATOM_STR("dist_ctrl", BIF_ARG_1)) {
+ DistEntry *dep;
+ i = 0;
+ /* Need to be the only thread running... */
+ erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ erts_smp_block_system(0);
+ for (dep = erts_visible_dist_entries; dep; dep = dep->next)
+ ++i;
+ for (dep = erts_hidden_dist_entries; dep; dep = dep->next)
+ ++i;
+ hp = HAlloc(BIF_P,i*(3+2));
+ res = NIL;
+ for (dep = erts_hidden_dist_entries; dep; dep = dep->next) {
+ Eterm tpl;
+ ASSERT(is_immed(dep->cid));
+ tpl = TUPLE2(hp, dep->sysname, dep->cid);
+ hp +=3;
+ res = CONS(hp, tpl, res);
+ hp += 2;
+ }
+ for (dep = erts_visible_dist_entries; dep; dep = dep->next) {
+ Eterm tpl;
+ ASSERT(is_immed(dep->cid));
+ tpl = TUPLE2(hp, dep->sysname, dep->cid);
+ hp +=3;
+ res = CONS(hp, tpl, res);
+ hp += 2;
+ }
+ erts_smp_release_system();
+ erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ BIF_RET(res);
+ } else if (BIF_ARG_1 == am_system_version) {
+ erts_dsprintf_buf_t *dsbufp = erts_create_tmp_dsbuf(0);
+ erts_print_system_version(ERTS_PRINT_DSBUF, (void *) dsbufp, BIF_P);
+ hp = HAlloc(BIF_P, dsbufp->str_len*2);
+ res = buf_to_intlist(&hp, dsbufp->str, dsbufp->str_len, NIL);
+ erts_destroy_tmp_dsbuf(dsbufp);
+ BIF_RET(res);
+ } else if (BIF_ARG_1 == am_system_architecture) {
+ hp = HAlloc(BIF_P, 2*(sizeof(ERLANG_ARCHITECTURE)-1));
+ BIF_RET(buf_to_intlist(&hp,
+ ERLANG_ARCHITECTURE,
+ sizeof(ERLANG_ARCHITECTURE)-1,
+ NIL));
+ }
+ else if (BIF_ARG_1 == am_memory_types) {
+ return erts_instr_get_type_info(BIF_P);
+ }
+ else if (BIF_ARG_1 == am_os_type) {
+ Eterm type = am_atom_put(os_type, strlen(os_type));
+ Eterm flav, tup;
+ char *buf = erts_alloc(ERTS_ALC_T_TMP, 1024); /* More than enough */
+
+ os_flavor(buf, 1024);
+ flav = am_atom_put(buf, strlen(buf));
+ hp = HAlloc(BIF_P, 3);
+ tup = TUPLE2(hp, type, flav);
+ erts_free(ERTS_ALC_T_TMP, (void *) buf);
+ BIF_RET(tup);
+ }
+ else if (BIF_ARG_1 == am_allocator) {
+ BIF_RET(erts_allocator_options((void *) BIF_P));
+ }
+ else if (BIF_ARG_1 == am_thread_pool_size) {
+#ifdef USE_THREADS
+ extern int erts_async_max_threads;
+#endif
+ int n;
+
+#ifdef USE_THREADS
+ n = erts_async_max_threads;
+#else
+ n = 0;
+#endif
+ BIF_RET(make_small(n));
+ }
+ else if (BIF_ARG_1 == am_alloc_util_allocators) {
+ BIF_RET(erts_alloc_util_allocators((void *) BIF_P));
+ }
+ else if (BIF_ARG_1 == am_elib_malloc) {
+#ifdef ELIB_ALLOC_IS_CLIB
+ struct elib_stat stat;
+ DECL_AM(heap_size);
+ DECL_AM(max_alloced_size);
+ DECL_AM(alloced_size);
+ DECL_AM(free_size);
+ DECL_AM(no_alloced_blocks);
+ DECL_AM(no_free_blocks);
+ DECL_AM(smallest_alloced_block);
+ DECL_AM(largest_free_block);
+ Eterm atoms[8];
+ Eterm ints[8];
+ Uint **hpp;
+ Uint sz;
+ Uint *szp;
+ int length;
+#ifdef DEBUG
+ Uint *endp;
+#endif
+
+ elib_stat(&stat);
+
+ /* First find out the heap size needed ... */
+ hpp = NULL;
+ szp = &sz;
+ sz = 0;
+
+ build_elib_malloc_term:
+ length = 0;
+ atoms[length] = AM_heap_size;
+ ints[length++] = erts_bld_uint(hpp, szp,
+ (Uint) stat.mem_total*sizeof(Uint));
+ atoms[length] = AM_max_alloced_size;
+ ints[length++] = erts_bld_uint(hpp, szp,
+ (Uint) stat.mem_max_alloc*sizeof(Uint));
+ atoms[length] = AM_alloced_size;
+ ints[length++] = erts_bld_uint(hpp, szp,
+ (Uint) stat.mem_alloc*sizeof(Uint));
+ atoms[length] = AM_free_size;
+ ints[length++] = erts_bld_uint(hpp, szp,
+ (Uint) stat.mem_free*sizeof(Uint));
+ atoms[length] = AM_no_alloced_blocks;
+ ints[length++] = erts_bld_uint(hpp, szp, (Uint) stat.mem_blocks);
+ atoms[length] = AM_no_free_blocks;
+ ints[length++] = erts_bld_uint(hpp, szp, (Uint) stat.free_blocks);
+ atoms[length] = AM_smallest_alloced_block;
+ ints[length++] = erts_bld_uint(hpp, szp,
+ (Uint) stat.min_used*sizeof(Uint));
+ atoms[length] = AM_largest_free_block;
+ ints[length++] = erts_bld_uint(hpp, szp,
+ (Uint) stat.max_free*sizeof(Uint));
+
+
+
+ ASSERT(length <= sizeof(atoms)/sizeof(Eterm));
+ ASSERT(length <= sizeof(ints)/sizeof(Eterm));
+
+ res = erts_bld_2tup_list(hpp, szp, length, atoms, ints);
+
+ if (szp) {
+ /* ... and then build the term */
+ hp = HAlloc(BIF_P, sz);
+#ifdef DEBUG
+ endp = hp + sz;
+#endif
+
+ szp = NULL;
+ hpp = &hp;
+ goto build_elib_malloc_term;
+ }
+
+#ifdef DEBUG
+ ASSERT(endp == hp);
+#endif
+
+#else /* #ifdef ELIB_ALLOC_IS_CLIB */
+ res = am_false;
+#endif /* #ifdef ELIB_ALLOC_IS_CLIB */
+
+ BIF_RET(res);
+ }
+ else if (BIF_ARG_1 == am_os_version) {
+ int major, minor, build;
+ Eterm tup;
+
+ os_version(&major, &minor, &build);
+ hp = HAlloc(BIF_P, 4);
+ tup = TUPLE3(hp,
+ make_small(major),
+ make_small(minor),
+ make_small(build));
+ BIF_RET(tup);
+ }
+ else if (BIF_ARG_1 == am_version) {
+ int n = strlen(ERLANG_VERSION);
+ hp = HAlloc(BIF_P, ((sizeof ERLANG_VERSION)-1) * 2);
+ BIF_RET(buf_to_intlist(&hp, ERLANG_VERSION, n, NIL));
+ }
+ else if (BIF_ARG_1 == am_machine) {
+ int n = strlen(EMULATOR);
+ hp = HAlloc(BIF_P, n*2);
+ BIF_RET(buf_to_intlist(&hp, EMULATOR, n, NIL));
+ }
+ else if (BIF_ARG_1 == am_garbage_collection) {
+ BIF_RET(am_generational);
+#ifdef ERTS_OPCODE_COUNTER_SUPPORT
+ } else if (BIF_ARG_1 == am_instruction_counts) {
+#ifdef DEBUG
+ Eterm *endp;
+#endif
+ Eterm *hp, **hpp;
+ Uint hsz, *hszp;
+ int i;
+
+ hpp = NULL;
+ hsz = 0;
+ hszp = &hsz;
+
+ bld_instruction_counts:
+
+ res = NIL;
+ for (i = num_instructions-1; i >= 0; i--) {
+ res = erts_bld_cons(hpp, hszp,
+ erts_bld_tuple(hpp, hszp, 2,
+ am_atom_put(opc[i].name,
+ strlen(opc[i].name)),
+ erts_bld_uint(hpp, hszp,
+ opc[i].count)),
+ res);
+ }
+
+ if (!hpp) {
+ hp = HAlloc(BIF_P, hsz);
+ hpp = &hp;
+#ifdef DEBUG
+ endp = hp + hsz;
+#endif
+ hszp = NULL;
+ goto bld_instruction_counts;
+ }
+
+#ifdef DEBUG
+ ASSERT(endp == hp);
+#endif
+
+ BIF_RET(res);
+#endif /* #ifndef ERTS_SMP */
+ } else if (BIF_ARG_1 == am_wordsize) {
+ return make_small(sizeof(Eterm));
+ } else if (BIF_ARG_1 == am_endian) {
+#if defined(WORDS_BIGENDIAN)
+ return am_big;
+#else
+ return am_little;
+#endif
+ } else if (BIF_ARG_1 == am_heap_sizes) {
+ return erts_heap_sizes(BIF_P);
+ } else if (BIF_ARG_1 == am_global_heaps_size) {
+#ifdef HYBRID
+ Uint hsz = 0;
+ Uint sz = 0;
+
+ sz += global_heap_sz;
+#ifdef INCREMENTAL
+ /* The size of the old generation is a bit hard to define here...
+ * The amount of live data in the last collection perhaps..? */
+ sz = 0;
+#else
+ if (global_old_hend && global_old_heap)
+ sz += global_old_hend - global_old_heap;
+#endif
+
+ sz *= sizeof(Eterm);
+
+ (void) erts_bld_uint(NULL, &hsz, sz);
+ hp = hsz ? HAlloc(BIF_P, hsz) : NULL;
+ res = erts_bld_uint(&hp, NULL, sz);
+#else
+ res = make_small(0);
+#endif
+ return res;
+ } else if (BIF_ARG_1 == am_heap_type) {
+#if defined(HYBRID)
+ return am_hybrid;
+#else
+ return am_private;
+#endif
+ } else if (ERTS_IS_ATOM_STR("cpu_topology", BIF_ARG_1)) {
+ res = erts_get_cpu_topology_term(BIF_P, am_used);
+ BIF_TRAP1(erts_format_cpu_topology_trap, BIF_P, res);
+#if defined(__GNUC__) && defined(HAVE_SOLARIS_SPARC_PERFMON)
+ } else if (ERTS_IS_ATOM_STR("ultrasparc_read_tick1", BIF_ARG_1)) {
+ register unsigned high asm("%l0");
+ register unsigned low asm("%l1");
+
+ hp = HAlloc(BIF_P, 5);
+ asm volatile (".word 0xa3410000;" /* rd %tick, %l1 */
+ ".word 0xa1347020" /* srlx %l1, 0x20, %l0 */
+ : "=r" (high), "=r" (low));
+ res = TUPLE4(hp, make_small(high >> 16),
+ make_small(high & 0xFFFF),
+ make_small(low >> 16),
+ make_small(low & 0xFFFF));
+ BIF_RET(res);
+ } else if (ERTS_IS_ATOM_STR("ultrasparc_read_tick2", BIF_ARG_1)) {
+ register unsigned high asm("%l0");
+ register unsigned low asm("%l1");
+
+ asm volatile (".word 0xa3410000;" /* rd %tick, %l1 */
+ ".word 0xa1347020" /* srlx %l1, 0x20, %l0 */
+ : "=r" (high), "=r" (low));
+ hp = HAlloc(BIF_P, 5);
+ res = TUPLE4(hp, make_small(high >> 16),
+ make_small(high & 0xFFFF),
+ make_small(low >> 16),
+ make_small(low & 0xFFFF));
+ BIF_RET(res);
+ } else if (ERTS_IS_ATOM_STR("ultrasparc_read_pic1", BIF_ARG_1)) {
+ register unsigned high asm("%l0");
+ register unsigned low asm("%l1");
+
+ hp = HAlloc(BIF_P, 5);
+ asm volatile (".word 0xa3444000;" /* rd %asr17, %l1 */
+ ".word 0xa1347020" /* srlx %l1, 0x20, %l0 */
+ : "=r" (high), "=r" (low));
+ res = TUPLE4(hp, make_small(high >> 16),
+ make_small(high & 0xFFFF),
+ make_small(low >> 16),
+ make_small(low & 0xFFFF));
+ BIF_RET(res);
+ } else if (ERTS_IS_ATOM_STR("ultrasparc_read_pic2", BIF_ARG_1)) {
+ register unsigned high asm("%l0");
+ register unsigned low asm("%l1");
+
+ asm volatile (".word 0xa3444000;" /* rd %asr17, %l1 */
+ ".word 0xa1347020" /* srlx %l1, 0x20, %l0 */
+ : "=r" (high), "=r" (low));
+ hp = HAlloc(BIF_P, 5);
+ res = TUPLE4(hp, make_small(high >> 16),
+ make_small(high & 0xFFFF),
+ make_small(low >> 16),
+ make_small(low & 0xFFFF));
+ BIF_RET(res);
+#endif
+ } else if (BIF_ARG_1 == am_threads) {
+#ifdef USE_THREADS
+ return am_true;
+#else
+ return am_false;
+#endif
+ } else if (BIF_ARG_1 == am_creation) {
+ return make_small(erts_this_node->creation);
+ } else if (BIF_ARG_1 == am_break_ignored) {
+ extern int ignore_break;
+ if (ignore_break)
+ return am_true;
+ else
+ return am_false;
+ }
+ /* Arguments that are unusual follow ... */
+ else if (ERTS_IS_ATOM_STR("logical_processors", BIF_ARG_1)) {
+ int no = erts_get_cpu_configured(erts_cpuinfo);
+ if (no > 0)
+ BIF_RET(make_small((Uint) no));
+ else {
+ DECL_AM(unknown);
+ BIF_RET(AM_unknown);
+ }
+ }
+ else if (ERTS_IS_ATOM_STR("logical_processors_online", BIF_ARG_1)) {
+ int no = erts_get_cpu_online(erts_cpuinfo);
+ if (no > 0)
+ BIF_RET(make_small((Uint) no));
+ else {
+ DECL_AM(unknown);
+ BIF_RET(AM_unknown);
+ }
+ }
+ else if (ERTS_IS_ATOM_STR("logical_processors_available", BIF_ARG_1)) {
+ int no = erts_get_cpu_available(erts_cpuinfo);
+ if (no > 0)
+ BIF_RET(make_small((Uint) no));
+ else {
+ DECL_AM(unknown);
+ BIF_RET(AM_unknown);
+ }
+ } else if (ERTS_IS_ATOM_STR("otp_release", BIF_ARG_1)) {
+ int n = sizeof(ERLANG_OTP_RELEASE)-1;
+ hp = HAlloc(BIF_P, 2*n);
+ BIF_RET(buf_to_intlist(&hp, ERLANG_OTP_RELEASE, n, NIL));
+ } else if (ERTS_IS_ATOM_STR("driver_version", BIF_ARG_1)) {
+ char buf[42];
+ int n = erts_snprintf(buf, 42, "%d.%d",
+ ERL_DRV_EXTENDED_MAJOR_VERSION,
+ ERL_DRV_EXTENDED_MINOR_VERSION);
+ hp = HAlloc(BIF_P, 2*n);
+ BIF_RET(buf_to_intlist(&hp, buf, n, NIL));
+ } else if (ERTS_IS_ATOM_STR("smp_support", BIF_ARG_1)) {
+#ifdef ERTS_SMP
+ BIF_RET(am_true);
+#else
+ BIF_RET(am_false);
+#endif
+ } else if (ERTS_IS_ATOM_STR("scheduler_bind_type", BIF_ARG_1)) {
+ BIF_RET(erts_bound_schedulers_term(BIF_P));
+ } else if (ERTS_IS_ATOM_STR("scheduler_bindings", BIF_ARG_1)) {
+ BIF_RET(erts_get_schedulers_binds(BIF_P));
+ } else if (ERTS_IS_ATOM_STR("constant_pool_support", BIF_ARG_1)) {
+ BIF_RET(am_true);
+ } else if (ERTS_IS_ATOM_STR("schedulers", BIF_ARG_1)
+ || ERTS_IS_ATOM_STR("schedulers_total", BIF_ARG_1)) {
+ res = make_small(erts_no_schedulers);
+ BIF_RET(res);
+ } else if (ERTS_IS_ATOM_STR("schedulers_state", BIF_ARG_1)) {
+#ifndef ERTS_SMP
+ Eterm *hp = HAlloc(BIF_P, 4);
+ res = TUPLE3(hp, make_small(1), make_small(1), make_small(1));
+ BIF_RET(res);
+#else
+ Uint total, online, active;
+ switch (erts_schedulers_state(&total,
+ &online,
+ &active,
+ 1)) {
+ case ERTS_SCHDLR_SSPND_DONE: {
+ Eterm *hp = HAlloc(BIF_P, 4);
+ res = TUPLE3(hp,
+ make_small(total),
+ make_small(online),
+ make_small(active));
+ BIF_RET(res);
+ }
+ case ERTS_SCHDLR_SSPND_YIELD_RESTART:
+ ERTS_VBUMP_ALL_REDS(BIF_P);
+ BIF_TRAP1(bif_export[BIF_system_info_1],
+ BIF_P, BIF_ARG_1);
+ default:
+ ASSERT(0);
+ BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR);
+ }
+#endif
+ } else if (ERTS_IS_ATOM_STR("schedulers_online", BIF_ARG_1)) {
+#ifndef ERTS_SMP
+ BIF_RET(make_small(1));
+#else
+ Uint total, online, active;
+ switch (erts_schedulers_state(&total, &online, &active, 1)) {
+ case ERTS_SCHDLR_SSPND_DONE:
+ BIF_RET(make_small(online));
+ case ERTS_SCHDLR_SSPND_YIELD_RESTART:
+ ERTS_VBUMP_ALL_REDS(BIF_P);
+ BIF_TRAP1(bif_export[BIF_system_info_1],
+ BIF_P, BIF_ARG_1);
+ default:
+ ASSERT(0);
+ BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR);
+ }
+#endif
+ } else if (ERTS_IS_ATOM_STR("schedulers_active", BIF_ARG_1)) {
+#ifndef ERTS_SMP
+ BIF_RET(make_small(1));
+#else
+ Uint total, online, active;
+ switch (erts_schedulers_state(&total, &online, &active, 1)) {
+ case ERTS_SCHDLR_SSPND_DONE:
+ BIF_RET(make_small(active));
+ case ERTS_SCHDLR_SSPND_YIELD_RESTART:
+ ERTS_VBUMP_ALL_REDS(BIF_P);
+ BIF_TRAP1(bif_export[BIF_system_info_1],
+ BIF_P, BIF_ARG_1);
+ default:
+ ASSERT(0);
+ BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR);
+ }
+#endif
+ } else if (ERTS_IS_ATOM_STR("run_queues", BIF_ARG_1)) {
+ res = make_small(erts_no_run_queues);
+ BIF_RET(res);
+ } else if (ERTS_IS_ATOM_STR("c_compiler_used", BIF_ARG_1)) {
+ Eterm *hp = NULL;
+ Uint sz = 0;
+ (void) c_compiler_used(NULL, &sz);
+ if (sz)
+ hp = HAlloc(BIF_P, sz);
+ BIF_RET(c_compiler_used(&hp, NULL));
+ } else if (ERTS_IS_ATOM_STR("stop_memory_trace", BIF_ARG_1)) {
+ erts_mtrace_stop();
+ BIF_RET(am_true);
+ } else if (ERTS_IS_ATOM_STR("context_reductions", BIF_ARG_1)) {
+ BIF_RET(make_small(CONTEXT_REDS));
+ } else if (ERTS_IS_ATOM_STR("kernel_poll", BIF_ARG_1)) {
+#ifdef ERTS_ENABLE_KERNEL_POLL
+ BIF_RET(erts_use_kernel_poll ? am_true : am_false);
+#else
+ BIF_RET(am_false);
+#endif
+ } else if (ERTS_IS_ATOM_STR("lock_checking", BIF_ARG_1)) {
+#ifdef ERTS_ENABLE_LOCK_CHECK
+ BIF_RET(am_true);
+#else
+ BIF_RET(am_false);
+#endif
+ } else if (ERTS_IS_ATOM_STR("lock_counting", BIF_ARG_1)) {
+#ifdef ERTS_ENABLE_LOCK_COUNT
+ BIF_RET(am_true);
+#else
+ BIF_RET(am_false);
+#endif
+ } else if (ERTS_IS_ATOM_STR("debug_compiled", BIF_ARG_1)) {
+#ifdef DEBUG
+ BIF_RET(am_true);
+#else
+ BIF_RET(am_false);
+#endif
+ } else if (ERTS_IS_ATOM_STR("check_io", BIF_ARG_1)) {
+ BIF_RET(erts_check_io_info(BIF_P));
+ } else if (ERTS_IS_ATOM_STR("multi_scheduling_blockers", BIF_ARG_1)) {
+#ifndef ERTS_SMP
+ BIF_RET(NIL);
+#else
+ if (erts_no_schedulers == 1)
+ BIF_RET(NIL);
+ else
+ BIF_RET(erts_multi_scheduling_blockers(BIF_P));
+#endif
+ } else if (ERTS_IS_ATOM_STR("modified_timing_level", BIF_ARG_1)) {
+ BIF_RET(ERTS_USE_MODIFIED_TIMING()
+ ? make_small(erts_modified_timing_level)
+ : am_undefined);
+ } else if (ERTS_IS_ATOM_STR("port_tasks", BIF_ARG_1)) {
+ BIF_RET(am_true);
+ } else if (ERTS_IS_ATOM_STR("io_thread", BIF_ARG_1)) {
+ BIF_RET(am_false);
+ } else if (ERTS_IS_ATOM_STR("scheduling_statistics", BIF_ARG_1)) {
+ BIF_RET(erts_sched_stat_term(BIF_P, 0));
+ } else if (ERTS_IS_ATOM_STR("total_scheduling_statistics", BIF_ARG_1)) {
+ BIF_RET(erts_sched_stat_term(BIF_P, 1));
+ } else if (ERTS_IS_ATOM_STR("taints", BIF_ARG_1)) {
+ BIF_RET(erts_nif_taints(BIF_P));
+ }
+
+ BIF_ERROR(BIF_P, BADARG);
+}
+
+Eterm
+port_info_1(Process* p, Eterm pid)
+{
+ static Eterm keys[] = {
+ am_name,
+ am_links,
+ am_id,
+ am_connected,
+ am_input,
+ am_output
+ };
+ Eterm items[ASIZE(keys)];
+ Eterm result = NIL;
+ Eterm reg_name;
+ Eterm* hp;
+ Uint need;
+ int i;
+
+ /*
+ * Collect all information about the port.
+ */
+
+ for (i = 0; i < ASIZE(keys); i++) {
+ Eterm item;
+
+ item = port_info_2(p, pid, keys[i]);
+ if (is_non_value(item)) {
+ return THE_NON_VALUE;
+ }
+ if (item == am_undefined) {
+ return am_undefined;
+ }
+ items[i] = item;
+ }
+ reg_name = port_info_2(p, pid, am_registered_name);
+
+ /*
+ * Build the resulting list.
+ */
+
+ need = 2*ASIZE(keys);
+ if (is_tuple(reg_name)) {
+ need += 2;
+ }
+ hp = HAlloc(p, need);
+ for (i = ASIZE(keys) - 1; i >= 0; i--) {
+ result = CONS(hp, items[i], result);
+ hp += 2;
+ }
+ if (is_tuple(reg_name)) {
+ result = CONS(hp, reg_name, result);
+ }
+
+ return result;
+}
+
+
+/**********************************************************************/
+/* Return information on ports */
+/* Info:
+** id Port index
+** connected (Pid)
+** links List of pids
+** name String
+** input Number of bytes input from port program
+** output Number of bytes output to the port program
+*/
+
+BIF_RETTYPE port_info_2(BIF_ALIST_2)
+{
+ BIF_RETTYPE ret;
+ Eterm portid = BIF_ARG_1;
+ Port *prt;
+ Eterm item = BIF_ARG_2;
+ Eterm res;
+ Eterm* hp;
+ int count;
+
+ if (is_internal_port(portid))
+ prt = erts_id2port(portid, BIF_P, ERTS_PROC_LOCK_MAIN);
+ else if (is_atom(portid))
+ erts_whereis_name(BIF_P, ERTS_PROC_LOCK_MAIN,
+ portid, NULL, 0, 0, &prt);
+ else if (is_external_port(portid)
+ && external_port_dist_entry(portid) == erts_this_dist_entry)
+ BIF_RET(am_undefined);
+ else {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+
+ if (!prt) {
+ BIF_RET(am_undefined);
+ }
+
+ if (item == am_id) {
+ hp = HAlloc(BIF_P, 3);
+ res = make_small(internal_port_number(portid));
+ }
+ else if (item == am_links) {
+ MonitorInfoCollection mic;
+ int i;
+ Eterm item;
+
+ INIT_MONITOR_INFOS(mic);
+
+ erts_doforall_links(prt->nlinks, &collect_one_link, &mic);
+
+ hp = HAlloc(BIF_P, 3 + mic.sz);
+ res = NIL;
+ for (i = 0; i < mic.mi_i; i++) {
+ item = STORE_NC(&hp, &MSO(BIF_P).externals, mic.mi[i].entity);
+ res = CONS(hp, item, res);
+ hp += 2;
+ }
+ DESTROY_MONITOR_INFOS(mic);
+
+ }
+ else if (item == am_monitors) {
+ MonitorInfoCollection mic;
+ int i;
+ Eterm item;
+
+ INIT_MONITOR_INFOS(mic);
+
+ erts_doforall_monitors(prt->monitors, &collect_one_origin_monitor, &mic);
+
+ hp = HAlloc(BIF_P, 3 + mic.sz);
+ res = NIL;
+ for (i = 0; i < mic.mi_i; i++) {
+ Eterm t;
+ item = STORE_NC(&hp, &MSO(BIF_P).externals, mic.mi[i].entity);
+ t = TUPLE2(hp, am_process, item);
+ hp += 3;
+ res = CONS(hp, t, res);
+ hp += 2;
+ }
+ DESTROY_MONITOR_INFOS(mic);
+
+ }
+ else if (item == am_name) {
+ count = sys_strlen(prt->name);
+
+ hp = HAlloc(BIF_P, 3 + 2*count);
+ res = buf_to_intlist(&hp, prt->name, count, NIL);
+ }
+ else if (item == am_connected) {
+ hp = HAlloc(BIF_P, 3);
+ res = prt->connected; /* internal pid */
+ }
+ else if (item == am_input) {
+ Uint hsz = 3;
+ Uint n = prt->bytes_in;
+ (void) erts_bld_uint(NULL, &hsz, n);
+ hp = HAlloc(BIF_P, hsz);
+ res = erts_bld_uint(&hp, NULL, n);
+ }
+ else if (item == am_output) {
+ Uint hsz = 3;
+ Uint n = prt->bytes_out;
+ (void) erts_bld_uint(NULL, &hsz, n);
+ hp = HAlloc(BIF_P, hsz);
+ res = erts_bld_uint(&hp, NULL, n);
+ }
+ else if (item == am_registered_name) {
+ RegProc *reg;
+ reg = prt->reg;
+ if (reg == NULL) {
+ ERTS_BIF_PREP_RET(ret, NIL);
+ goto done;
+ } else {
+ hp = HAlloc(BIF_P, 3);
+ res = reg->name;
+ }
+ }
+ else if (item == am_memory) {
+ /* All memory consumed in bytes (the Port struct should not be
+ included though).
+ */
+ Uint hsz = 3;
+ Uint size = 0;
+ ErlHeapFragment* bp;
+
+ hp = HAlloc(BIF_P, 3);
+
+ erts_doforall_links(prt->nlinks, &one_link_size, &size);
+
+ for (bp = prt->bp; bp; bp = bp->next)
+ size += sizeof(ErlHeapFragment) + (bp->size - 1)*sizeof(Eterm);
+
+ if (prt->linebuf)
+ size += sizeof(LineBuf) + prt->linebuf->ovsiz;
+
+ /* ... */
+
+
+ /* All memory allocated by the driver should be included, but it is
+ hard to retrieve... */
+
+ (void) erts_bld_uint(NULL, &hsz, size);
+ hp = HAlloc(BIF_P, hsz);
+ res = erts_bld_uint(&hp, NULL, size);
+ }
+ else if (item == am_queue_size) {
+ Uint ioq_size = erts_port_ioq_size(prt);
+ Uint hsz = 3;
+ (void) erts_bld_uint(NULL, &hsz, ioq_size);
+ hp = HAlloc(BIF_P, hsz);
+ res = erts_bld_uint(&hp, NULL, ioq_size);
+ }
+ else if (ERTS_IS_ATOM_STR("locking", item)) {
+ hp = HAlloc(BIF_P, 3);
+#ifndef ERTS_SMP
+ res = am_false;
+#else
+ if (prt->status & ERTS_PORT_SFLG_PORT_SPECIFIC_LOCK) {
+ DECL_AM(port_level);
+ ASSERT(prt->drv_ptr->flags
+ & ERL_DRV_FLAG_USE_PORT_LOCKING);
+ res = AM_port_level;
+ }
+ else {
+ DECL_AM(driver_level);
+ ASSERT(!(prt->drv_ptr->flags
+ & ERL_DRV_FLAG_USE_PORT_LOCKING));
+ res = AM_driver_level;
+ }
+#endif
+ }
+ else {
+ ERTS_BIF_PREP_ERROR(ret, BIF_P, BADARG);
+ goto done;
+ }
+
+ ERTS_BIF_PREP_RET(ret, TUPLE2(hp, item, res));
+
+ done:
+
+ erts_smp_port_unlock(prt);
+
+ return ret;
+}
+
+
+Eterm
+fun_info_2(Process* p, Eterm fun, Eterm what)
+{
+ Eterm* hp;
+ Eterm val;
+
+ if (is_fun(fun)) {
+ ErlFunThing* funp = (ErlFunThing *) fun_val(fun);
+
+ switch (what) {
+ case am_type:
+ hp = HAlloc(p, 3);
+ val = am_local;
+ break;
+ case am_pid:
+ hp = HAlloc(p, 3);
+ val = funp->creator;
+ break;
+ case am_module:
+ hp = HAlloc(p, 3);
+ val = funp->fe->module;
+ break;
+ case am_new_index:
+ hp = HAlloc(p, 3);
+ val = make_small(funp->fe->index);
+ break;
+ case am_new_uniq:
+ val = new_binary(p, funp->fe->uniq, 16);
+ hp = HAlloc(p, 3);
+ break;
+ case am_index:
+ hp = HAlloc(p, 3);
+ val = make_small(funp->fe->old_index);
+ break;
+ case am_uniq:
+ hp = HAlloc(p, 3);
+ val = make_small(funp->fe->old_uniq);
+ break;
+ case am_env:
+ {
+ Uint num_free = funp->num_free;
+ int i;
+
+ hp = HAlloc(p, 3 + 2*num_free);
+ val = NIL;
+ for (i = num_free-1; i >= 0; i--) {
+ val = CONS(hp, funp->env[i], val);
+ hp += 2;
+ }
+ }
+ break;
+ case am_refc:
+ val = erts_make_integer(erts_smp_atomic_read(&funp->fe->refc), p);
+ hp = HAlloc(p, 3);
+ break;
+ case am_arity:
+ hp = HAlloc(p, 3);
+ val = make_small(funp->arity);
+ break;
+ case am_name:
+ hp = HAlloc(p, 3);
+ val = funp->fe->address[-2];
+ break;
+ default:
+ goto error;
+ }
+ } else if (is_export(fun)) {
+ Export* exp = (Export *) (export_val(fun))[1];
+ switch (what) {
+ case am_type:
+ hp = HAlloc(p, 3);
+ val = am_external;
+ break;
+ case am_pid:
+ hp = HAlloc(p, 3);
+ val = am_undefined;
+ break;
+ case am_module:
+ hp = HAlloc(p, 3);
+ val = exp->code[0];
+ break;
+ case am_new_index:
+ hp = HAlloc(p, 3);
+ val = am_undefined;
+ break;
+ case am_new_uniq:
+ hp = HAlloc(p, 3);
+ val = am_undefined;
+ break;
+ case am_index:
+ hp = HAlloc(p, 3);
+ val = am_undefined;
+ break;
+ case am_uniq:
+ hp = HAlloc(p, 3);
+ val = am_undefined;
+ break;
+ case am_env:
+ hp = HAlloc(p, 3);
+ val = NIL;
+ break;
+ case am_refc:
+ hp = HAlloc(p, 3);
+ val = am_undefined;
+ break;
+ case am_arity:
+ hp = HAlloc(p, 3);
+ val = make_small(exp->code[2]);
+ break;
+ case am_name:
+ hp = HAlloc(p, 3);
+ val = exp->code[1];
+ break;
+ default:
+ goto error;
+ }
+ } else {
+ error:
+ BIF_ERROR(p, BADARG);
+ }
+ return TUPLE2(hp, what, val);
+}
+
+BIF_RETTYPE is_process_alive_1(BIF_ALIST_1)
+{
+ if(is_internal_pid(BIF_ARG_1)) {
+ Process *rp;
+
+ if (BIF_ARG_1 == BIF_P->id)
+ BIF_RET(am_true);
+
+ if(internal_pid_index(BIF_ARG_1) >= erts_max_processes)
+ BIF_ERROR(BIF_P, BADARG);
+
+ rp = erts_pid2proc(BIF_P, ERTS_PROC_LOCK_MAIN,
+ BIF_ARG_1, ERTS_PROC_LOCK_STATUS);
+ if (!rp) {
+ BIF_RET(am_false);
+ }
+ else {
+ int have_pending_exit = ERTS_PROC_PENDING_EXIT(rp);
+ erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_STATUS);
+ if (have_pending_exit)
+ ERTS_BIF_AWAIT_X_DATA_TRAP(BIF_P, BIF_ARG_1, am_false);
+ else
+ BIF_RET(am_true);
+ }
+ }
+ else if(is_external_pid(BIF_ARG_1)) {
+ if(external_pid_dist_entry(BIF_ARG_1) == erts_this_dist_entry)
+ BIF_RET(am_false); /* A pid from an old incarnation of this node */
+ else
+ BIF_ERROR(BIF_P, BADARG);
+ }
+ else {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+}
+
+BIF_RETTYPE process_display_2(BIF_ALIST_2)
+{
+ Process *rp;
+
+ if (BIF_ARG_2 != am_backtrace)
+ BIF_ERROR(BIF_P, BADARG);
+
+ rp = erts_pid2proc_nropt(BIF_P, ERTS_PROC_LOCK_MAIN,
+ BIF_ARG_1, ERTS_PROC_LOCKS_ALL);
+ if(!rp) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+ if (rp == ERTS_PROC_LOCK_BUSY)
+ ERTS_BIF_YIELD2(bif_export[BIF_process_display_2], BIF_P,
+ BIF_ARG_1, BIF_ARG_2);
+ if (rp != BIF_P && ERTS_PROC_PENDING_EXIT(rp)) {
+ Eterm args[2] = {BIF_ARG_1, BIF_ARG_2};
+ erts_smp_proc_unlock(rp, ERTS_PROC_LOCKS_ALL);
+ ERTS_BIF_AWAIT_X_APPLY_TRAP(BIF_P,
+ BIF_ARG_1,
+ am_erlang,
+ am_process_display,
+ args,
+ 2);
+ }
+ erts_stack_dump(ERTS_PRINT_STDERR, NULL, rp);
+#ifdef ERTS_SMP
+ erts_smp_proc_unlock(rp, (BIF_P == rp
+ ? ERTS_PROC_LOCKS_ALL_MINOR
+ : ERTS_PROC_LOCKS_ALL));
+#endif
+ BIF_RET(am_true);
+}
+
+
+/* this is a general call which return some possibly useful information */
+
+BIF_RETTYPE statistics_1(BIF_ALIST_1)
+{
+ Eterm res;
+ Eterm* hp;
+
+ if (BIF_ARG_1 == am_context_switches) {
+ Eterm cs = erts_make_integer(erts_get_total_context_switches(), BIF_P);
+ hp = HAlloc(BIF_P, 3);
+ res = TUPLE2(hp, cs, SMALL_ZERO);
+ BIF_RET(res);
+ } else if (BIF_ARG_1 == am_garbage_collection) {
+ Uint hsz = 4;
+ ErtsGCInfo gc_info;
+ Eterm gcs;
+ Eterm recl;
+ erts_gc_info(&gc_info);
+ (void) erts_bld_uint(NULL, &hsz, gc_info.garbage_collections);
+ (void) erts_bld_uint(NULL, &hsz, gc_info.reclaimed);
+ hp = HAlloc(BIF_P, hsz);
+ gcs = erts_bld_uint(&hp, NULL, gc_info.garbage_collections);
+ recl = erts_bld_uint(&hp, NULL, gc_info.reclaimed);
+ res = TUPLE3(hp, gcs, recl, SMALL_ZERO);
+ BIF_RET(res);
+ } else if (BIF_ARG_1 == am_reductions) {
+ Uint reds;
+ Uint diff;
+ Uint hsz = 3;
+ Eterm b1, b2;
+
+ erts_get_total_reductions(&reds, &diff);
+ (void) erts_bld_uint(NULL, &hsz, reds);
+ (void) erts_bld_uint(NULL, &hsz, diff);
+ hp = HAlloc(BIF_P, hsz);
+ b1 = erts_bld_uint(&hp, NULL, reds);
+ b2 = erts_bld_uint(&hp, NULL, diff);
+ res = TUPLE2(hp, b1, b2);
+ BIF_RET(res);
+ } else if (BIF_ARG_1 == am_exact_reductions) {
+ Uint reds;
+ Uint diff;
+ Uint hsz = 3;
+ Eterm b1, b2;
+
+ erts_get_exact_total_reductions(BIF_P, &reds, &diff);
+ (void) erts_bld_uint(NULL, &hsz, reds);
+ (void) erts_bld_uint(NULL, &hsz, diff);
+ hp = HAlloc(BIF_P, hsz);
+ b1 = erts_bld_uint(&hp, NULL, reds);
+ b2 = erts_bld_uint(&hp, NULL, diff);
+ res = TUPLE2(hp, b1, b2);
+ BIF_RET(res);
+ } else if (BIF_ARG_1 == am_runtime) {
+ unsigned long u1, u2, dummy;
+ Eterm b1, b2;
+ elapsed_time_both(&u1,&dummy,&u2,&dummy);
+ b1 = erts_make_integer(u1,BIF_P);
+ b2 = erts_make_integer(u2,BIF_P);
+ hp = HAlloc(BIF_P,3);
+ res = TUPLE2(hp, b1, b2);
+ BIF_RET(res);
+ } else if (BIF_ARG_1 == am_run_queue) {
+ res = erts_run_queues_len(NULL);
+ BIF_RET(make_small(res));
+ } else if (BIF_ARG_1 == am_wall_clock) {
+ Uint w1, w2;
+ Eterm b1, b2;
+ wall_clock_elapsed_time_both(&w1, &w2);
+ b1 = erts_make_integer(w1,BIF_P);
+ b2 = erts_make_integer(w2,BIF_P);
+ hp = HAlloc(BIF_P,3);
+ res = TUPLE2(hp, b1, b2);
+ BIF_RET(res);
+ } else if (BIF_ARG_1 == am_io) {
+ Eterm r1, r2;
+ Eterm in, out;
+ Uint hsz = 9;
+ Uint bytes_in = (Uint) erts_smp_atomic_read(&erts_bytes_in);
+ Uint bytes_out = (Uint) erts_smp_atomic_read(&erts_bytes_out);
+
+ (void) erts_bld_uint(NULL, &hsz, bytes_in);
+ (void) erts_bld_uint(NULL, &hsz, bytes_out);
+ hp = HAlloc(BIF_P, hsz);
+ in = erts_bld_uint(&hp, NULL, bytes_in);
+ out = erts_bld_uint(&hp, NULL, bytes_out);
+
+ r1 = TUPLE2(hp, am_input, in);
+ hp += 3;
+ r2 = TUPLE2(hp, am_output, out);
+ hp += 3;
+ BIF_RET(TUPLE2(hp, r1, r2));
+ }
+ else if (ERTS_IS_ATOM_STR("run_queues", BIF_ARG_1)) {
+ Eterm res, *hp, **hpp;
+ Uint sz, *szp;
+ int no_qs = erts_no_run_queues;
+ Uint *qszs = erts_alloc(ERTS_ALC_T_TMP,sizeof(Uint)*no_qs*2);
+ (void) erts_run_queues_len(qszs);
+ sz = 0;
+ szp = &sz;
+ hpp = NULL;
+ while (1) {
+ int i;
+ for (i = 0; i < no_qs; i++)
+ qszs[no_qs+i] = erts_bld_uint(hpp, szp, qszs[i]);
+ res = erts_bld_tuplev(hpp, szp, no_qs, &qszs[no_qs]);
+ if (hpp) {
+ erts_free(ERTS_ALC_T_TMP, qszs);
+ BIF_RET(res);
+ }
+ hp = HAlloc(BIF_P, sz);
+ szp = NULL;
+ hpp = &hp;
+ }
+ }
+ BIF_ERROR(BIF_P, BADARG);
+}
+
+BIF_RETTYPE memory_0(BIF_ALIST_0)
+{
+ BIF_RETTYPE res = erts_memory(NULL, NULL, BIF_P, THE_NON_VALUE);
+ switch (res) {
+ case am_badarg: BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR); /* never... */
+ case am_notsup: BIF_ERROR(BIF_P, EXC_NOTSUP);
+ default: BIF_RET(res);
+ }
+}
+
+BIF_RETTYPE memory_1(BIF_ALIST_1)
+{
+ BIF_RETTYPE res = erts_memory(NULL, NULL, BIF_P, BIF_ARG_1);
+ switch (res) {
+ case am_badarg: BIF_ERROR(BIF_P, BADARG);
+ case am_notsup: BIF_ERROR(BIF_P, EXC_NOTSUP);
+ default: BIF_RET(res);
+ }
+}
+
+BIF_RETTYPE error_logger_warning_map_0(BIF_ALIST_0)
+{
+ BIF_RET(erts_error_logger_warnings);
+}
+
+static erts_smp_atomic_t available_internal_state;
+
+BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1)
+{
+ /*
+ * NOTE: Only supposed to be used for testing, and debugging.
+ */
+
+ if (!erts_smp_atomic_read(&available_internal_state)) {
+ BIF_ERROR(BIF_P, EXC_UNDEF);
+ }
+
+ if (is_atom(BIF_ARG_1)) {
+ if (ERTS_IS_ATOM_STR("reds_left", BIF_ARG_1)) {
+ /* Used by (emulator) */
+ BIF_RET(make_small((Uint) ERTS_BIF_REDS_LEFT(BIF_P)));
+ }
+ else if (ERTS_IS_ATOM_STR("node_and_dist_references", BIF_ARG_1)) {
+ /* Used by node_container_SUITE (emulator) */
+ Eterm res = erts_get_node_and_dist_references(BIF_P);
+ BIF_RET(res);
+ }
+ else if (ERTS_IS_ATOM_STR("monitoring_nodes", BIF_ARG_1)) {
+ BIF_RET(erts_processes_monitoring_nodes(BIF_P));
+ }
+ else if (ERTS_IS_ATOM_STR("next_pid", BIF_ARG_1)
+ || ERTS_IS_ATOM_STR("next_port", BIF_ARG_1)) {
+ /* Used by node_container_SUITE (emulator) */
+ Eterm res;
+ if (ERTS_IS_ATOM_STR("next_pid", BIF_ARG_1))
+ res = erts_test_next_pid(0, 0);
+ else {
+ res = erts_test_next_port(0, 0);
+ }
+ if (res < 0)
+ BIF_RET(am_false);
+ BIF_RET(erts_make_integer(res, BIF_P));
+ }
+ else if (ERTS_IS_ATOM_STR("DbTable_words", BIF_ARG_1)) {
+ /* Used by ets_SUITE (stdlib) */
+ size_t words = (sizeof(DbTable) + sizeof(Uint) - 1)/sizeof(Uint);
+ BIF_RET(make_small((Uint) words));
+ }
+ else if (ERTS_IS_ATOM_STR("check_io_debug", BIF_ARG_1)) {
+ /* Used by (emulator) */
+ int res;
+#ifdef HAVE_ERTS_CHECK_IO_DEBUG
+ erts_smp_proc_unlock(BIF_P,ERTS_PROC_LOCK_MAIN);
+ res = erts_check_io_debug();
+ erts_smp_proc_lock(BIF_P,ERTS_PROC_LOCK_MAIN);
+#else
+ res = 0;
+#endif
+ ASSERT(res >= 0);
+ BIF_RET(erts_make_integer((Uint) res, BIF_P));
+ }
+ else if (ERTS_IS_ATOM_STR("process_info_args", BIF_ARG_1)) {
+ /* Used by process_SUITE (emulator) */
+ int i;
+ Eterm res = NIL;
+ Uint *hp = HAlloc(BIF_P, 2*ERTS_PI_ARGS);
+ for (i = ERTS_PI_ARGS-1; i >= 0; i--) {
+ res = CONS(hp, pi_args[i], res);
+ hp += 2;
+ }
+ BIF_RET(res);
+ }
+ else if (ERTS_IS_ATOM_STR("processes", BIF_ARG_1)) {
+ /* Used by process_SUITE (emulator) */
+ BIF_RET(erts_debug_processes(BIF_P));
+ }
+ else if (ERTS_IS_ATOM_STR("processes_bif_info", BIF_ARG_1)) {
+ /* Used by process_SUITE (emulator) */
+ BIF_RET(erts_debug_processes_bif_info(BIF_P));
+ }
+ else if (ERTS_IS_ATOM_STR("max_atom_out_cache_index", BIF_ARG_1)) {
+ /* Used by distribution_SUITE (emulator) */
+ BIF_RET(make_small((Uint) erts_debug_max_atom_out_cache_index()));
+ }
+ else if (ERTS_IS_ATOM_STR("nbalance", BIF_ARG_1)) {
+ Uint n;
+ erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ n = erts_debug_nbalance();
+ erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ BIF_RET(erts_make_integer(n, BIF_P));
+ }
+ else if (ERTS_IS_ATOM_STR("available_internal_state", BIF_ARG_1)) {
+ BIF_RET(am_true);
+ }
+ }
+ else if (is_tuple(BIF_ARG_1)) {
+ Eterm* tp = tuple_val(BIF_ARG_1);
+ switch (arityval(tp[0])) {
+ case 2: {
+ if (ERTS_IS_ATOM_STR("process_status", tp[1])) {
+ /* Used by timer process_SUITE, timer_bif_SUITE, and
+ node_container_SUITE (emulator) */
+ if (is_internal_pid(tp[2])) {
+ BIF_RET(erts_process_status(BIF_P,
+ ERTS_PROC_LOCK_MAIN,
+ NULL,
+ tp[2]));
+ }
+ }
+ else if (ERTS_IS_ATOM_STR("link_list", tp[1])) {
+ /* Used by erl_link_SUITE (emulator) */
+ if(is_internal_pid(tp[2])) {
+ Eterm res;
+ Process *p;
+
+ p = erts_pid2proc(BIF_P,
+ ERTS_PROC_LOCK_MAIN,
+ tp[2],
+ ERTS_PROC_LOCK_LINK);
+ if (!p) {
+ ERTS_SMP_ASSERT_IS_NOT_EXITING(BIF_P);
+ BIF_RET(am_undefined);
+ }
+ res = make_link_list(BIF_P, p->nlinks, NIL);
+ erts_smp_proc_unlock(p, ERTS_PROC_LOCK_LINK);
+ BIF_RET(res);
+ }
+ else if(is_internal_port(tp[2])) {
+ Eterm res;
+ Port *p = erts_id2port(tp[2], BIF_P, ERTS_PROC_LOCK_MAIN);
+ if(!p)
+ BIF_RET(am_undefined);
+ res = make_link_list(BIF_P, p->nlinks, NIL);
+ erts_smp_port_unlock(p);
+ BIF_RET(res);
+ }
+ else if(is_node_name_atom(tp[2])) {
+ DistEntry *dep = erts_find_dist_entry(tp[2]);
+ if(dep) {
+ Eterm subres;
+ erts_smp_de_links_lock(dep);
+ subres = make_link_list(BIF_P, dep->nlinks, NIL);
+ subres = make_link_list(BIF_P, dep->node_links, subres);
+ erts_smp_de_links_unlock(dep);
+ erts_deref_dist_entry(dep);
+ BIF_RET(subres);
+ } else {
+ BIF_RET(am_undefined);
+ }
+ }
+ }
+ else if (ERTS_IS_ATOM_STR("monitor_list", tp[1])) {
+ /* Used by erl_link_SUITE (emulator) */
+ if(is_internal_pid(tp[2])) {
+ Process *p;
+ Eterm res;
+
+ p = erts_pid2proc(BIF_P,
+ ERTS_PROC_LOCK_MAIN,
+ tp[2],
+ ERTS_PROC_LOCK_LINK);
+ if (!p) {
+ ERTS_SMP_ASSERT_IS_NOT_EXITING(BIF_P);
+ BIF_RET(am_undefined);
+ }
+ res = make_monitor_list(BIF_P, p->monitors);
+ erts_smp_proc_unlock(p, ERTS_PROC_LOCK_LINK);
+ BIF_RET(res);
+ } else if(is_node_name_atom(tp[2])) {
+ DistEntry *dep = erts_find_dist_entry(tp[2]);
+ if(dep) {
+ Eterm ml;
+ erts_smp_de_links_lock(dep);
+ ml = make_monitor_list(BIF_P, dep->monitors);
+ erts_smp_de_links_unlock(dep);
+ erts_deref_dist_entry(dep);
+ BIF_RET(ml);
+ } else {
+ BIF_RET(am_undefined);
+ }
+ }
+ }
+ else if (ERTS_IS_ATOM_STR("channel_number", tp[1])) {
+ Eterm res;
+ DistEntry *dep = erts_find_dist_entry(tp[2]);
+ if (!dep)
+ res = am_undefined;
+ else {
+ Uint cno = dist_entry_channel_no(dep);
+ res = make_small(cno);
+ erts_deref_dist_entry(dep);
+ }
+ BIF_RET(res);
+ }
+ else if (ERTS_IS_ATOM_STR("have_pending_exit", tp[1])) {
+ Process *rp = erts_pid2proc(BIF_P, ERTS_PROC_LOCK_MAIN,
+ tp[2], ERTS_PROC_LOCK_STATUS);
+ if (!rp) {
+ BIF_RET(am_undefined);
+ }
+ else {
+ Eterm res = ERTS_PROC_PENDING_EXIT(rp) ? am_true : am_false;
+ erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_STATUS);
+ BIF_RET(res);
+ }
+ }
+ else if (ERTS_IS_ATOM_STR("binary_info", tp[1])) {
+ Eterm bin = tp[2];
+ if (is_binary(bin)) {
+ Eterm real_bin = bin;
+ Eterm res = am_true;
+ ErlSubBin* sb = (ErlSubBin *) binary_val(real_bin);
+
+ if (sb->thing_word == HEADER_SUB_BIN) {
+ real_bin = sb->orig;
+ }
+ if (*binary_val(real_bin) == HEADER_PROC_BIN) {
+ ProcBin* pb;
+ Binary* val;
+ Eterm SzTerm;
+ Uint hsz = 3 + 5;
+ Eterm* hp;
+ DECL_AM(refc_binary);
+
+ pb = (ProcBin *) binary_val(real_bin);
+ val = pb->val;
+ (void) erts_bld_uint(NULL, &hsz, pb->size);
+ (void) erts_bld_uint(NULL, &hsz, val->orig_size);
+ hp = HAlloc(BIF_P, hsz);
+
+ /* Info about the Binary* object */
+ SzTerm = erts_bld_uint(&hp, NULL, val->orig_size);
+ res = TUPLE2(hp, am_binary, SzTerm);
+ hp += 3;
+
+ /* Info about the ProcBin* object */
+ SzTerm = erts_bld_uint(&hp, NULL, pb->size);
+ res = TUPLE4(hp, AM_refc_binary, SzTerm,
+ res, make_small(pb->flags));
+ } else { /* heap binary */
+ DECL_AM(heap_binary);
+ res = AM_heap_binary;
+ }
+ BIF_RET(res);
+ }
+ }
+ else if (ERTS_IS_ATOM_STR("term_to_binary_no_funs", tp[1])) {
+ Uint dflags = (DFLAG_EXTENDED_REFERENCES |
+ DFLAG_EXTENDED_PIDS_PORTS |
+ DFLAG_BIT_BINARIES);
+ BIF_RET(erts_term_to_binary(BIF_P, tp[2], 0, dflags));
+ }
+ else if (ERTS_IS_ATOM_STR("dist_port", tp[1])) {
+ Eterm res = am_undefined;
+ DistEntry *dep = erts_sysname_to_connected_dist_entry(tp[2]);
+ if (dep) {
+ erts_smp_de_rlock(dep);
+ if (is_internal_port(dep->cid))
+ res = dep->cid;
+ erts_smp_de_runlock(dep);
+ erts_deref_dist_entry(dep);
+ }
+ BIF_RET(res);
+ }
+ else if (ERTS_IS_ATOM_STR("atom_out_cache_index", tp[1])) {
+ /* Used by distribution_SUITE (emulator) */
+ if (is_atom(tp[2])) {
+ BIF_RET(make_small(
+ (Uint)
+ erts_debug_atom_to_out_cache_index(tp[2])));
+ }
+ }
+ else if (ERTS_IS_ATOM_STR("fake_scheduler_bindings", tp[1])) {
+ return erts_fake_scheduler_bindings(BIF_P, tp[2]);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ BIF_ERROR(BIF_P, BADARG);
+}
+
+static erts_smp_atomic_t hipe_test_reschedule_flag;
+
+BIF_RETTYPE erts_debug_set_internal_state_2(BIF_ALIST_2)
+{
+ /*
+ * NOTE: Only supposed to be used for testing, and debugging.
+ */
+ if (ERTS_IS_ATOM_STR("available_internal_state", BIF_ARG_1)
+ && (BIF_ARG_2 == am_true || BIF_ARG_2 == am_false)) {
+ long on = (long) (BIF_ARG_2 == am_true);
+ long prev_on = erts_smp_atomic_xchg(&available_internal_state, on);
+ if (on) {
+ erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf();
+ erts_dsprintf(dsbufp, "Process %T ", BIF_P->id);
+ if (erts_is_alive)
+ erts_dsprintf(dsbufp, "on node %T ", erts_this_node->sysname);
+ erts_dsprintf(dsbufp,
+ "enabled access to the emulator internal state.\n");
+ erts_dsprintf(dsbufp,
+ "NOTE: This is an erts internal test feature and "
+ "should *only* be used by OTP test-suites.\n");
+ erts_send_warning_to_logger(BIF_P->group_leader, dsbufp);
+ }
+ BIF_RET(prev_on ? am_true : am_false);
+ }
+
+ if (!erts_smp_atomic_read(&available_internal_state)) {
+ BIF_ERROR(BIF_P, EXC_UNDEF);
+ }
+
+ if (is_atom(BIF_ARG_1)) {
+
+ if (ERTS_IS_ATOM_STR("reds_left", BIF_ARG_1)) {
+ Sint reds;
+ if (term_to_Sint(BIF_ARG_2, &reds) != 0) {
+ if (0 <= reds && reds <= CONTEXT_REDS) {
+ if (!ERTS_PROC_GET_SAVED_CALLS_BUF(BIF_P))
+ BIF_P->fcalls = reds;
+ else
+ BIF_P->fcalls = reds - CONTEXT_REDS;
+ }
+ BIF_RET(am_true);
+ }
+ }
+ else if (ERTS_IS_ATOM_STR("block", BIF_ARG_1)
+ || ERTS_IS_ATOM_STR("sleep", BIF_ARG_1)) {
+ int block = ERTS_IS_ATOM_STR("block", BIF_ARG_1);
+ Sint ms;
+ if (term_to_Sint(BIF_ARG_2, &ms) != 0) {
+ if (ms > 0) {
+ erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ if (block)
+ erts_smp_block_system(0);
+ while (erts_milli_sleep((long) ms) != 0);
+ if (block)
+ erts_smp_release_system();
+ erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ }
+ BIF_RET(am_true);
+ }
+ }
+ else if (ERTS_IS_ATOM_STR("block_scheduler", BIF_ARG_1)) {
+ Sint ms;
+ if (term_to_Sint(BIF_ARG_2, &ms) != 0) {
+ if (ms > 0) {
+ erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ while (erts_milli_sleep((long) ms) != 0);
+ erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ }
+ BIF_RET(am_true);
+ }
+ }
+ else if (ERTS_IS_ATOM_STR("next_pid", BIF_ARG_1)
+ || ERTS_IS_ATOM_STR("next_port", BIF_ARG_1)) {
+ /* Used by node_container_SUITE (emulator) */
+ Uint next;
+
+ if (term_to_Uint(BIF_ARG_2, &next) != 0) {
+ Eterm res;
+
+ if (ERTS_IS_ATOM_STR("next_pid", BIF_ARG_1))
+ res = erts_test_next_pid(1, next);
+ else {
+ res = erts_test_next_port(1, next);
+ }
+ if (res < 0)
+ BIF_RET(am_false);
+ BIF_RET(erts_make_integer(res, BIF_P));
+ }
+ }
+ else if (ERTS_IS_ATOM_STR("force_gc", BIF_ARG_1)) {
+ /* Used by signal_SUITE (emulator) */
+ Process *rp = erts_pid2proc(BIF_P, ERTS_PROC_LOCK_MAIN,
+ BIF_ARG_2, ERTS_PROC_LOCK_MAIN);
+ if (!rp) {
+ BIF_RET(am_false);
+ }
+ else {
+ FLAGS(rp) |= F_FORCE_GC;
+ if (BIF_P != rp)
+ erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_MAIN);
+ BIF_RET(am_true);
+ }
+ }
+ else if (ERTS_IS_ATOM_STR("send_fake_exit_signal", BIF_ARG_1)) {
+ /* Used by signal_SUITE (emulator) */
+
+ /* Testcases depend on the exit being received via
+ a pending exit when the receiver is the same as
+ the caller. */
+ if (is_tuple(BIF_ARG_2)) {
+ Eterm* tp = tuple_val(BIF_ARG_2);
+ if (arityval(tp[0]) == 3
+ && (is_pid(tp[1]) || is_port(tp[1]))
+ && is_internal_pid(tp[2])) {
+ int xres;
+ ErtsProcLocks rp_locks = ERTS_PROC_LOCKS_XSIG_SEND;
+ Process *rp = erts_pid2proc_opt(BIF_P, ERTS_PROC_LOCK_MAIN,
+ tp[2], rp_locks,
+ ERTS_P2P_FLG_SMP_INC_REFC);
+ if (!rp) {
+ DECL_AM(dead);
+ BIF_RET(AM_dead);
+ }
+
+#ifdef ERTS_SMP
+ if (BIF_P == rp)
+ rp_locks |= ERTS_PROC_LOCK_MAIN;
+#endif
+ xres = erts_send_exit_signal(NULL, /* NULL in order to
+ force a pending exit
+ when we send to our
+ selves. */
+ tp[1],
+ rp,
+ &rp_locks,
+ tp[3],
+ NIL,
+ NULL,
+ 0);
+#ifdef ERTS_SMP
+ if (BIF_P == rp)
+ rp_locks &= ~ERTS_PROC_LOCK_MAIN;
+#endif
+ erts_smp_proc_unlock(rp, rp_locks);
+ erts_smp_proc_dec_refc(rp);
+ if (xres > 1) {
+ DECL_AM(message);
+ BIF_RET(AM_message);
+ }
+ else if (xres == 0) {
+ DECL_AM(unaffected);
+ BIF_RET(AM_unaffected);
+ }
+ else {
+ DECL_AM(exit);
+ BIF_RET(AM_exit);
+ }
+ }
+ }
+ }
+ else if (ERTS_IS_ATOM_STR("colliding_names", BIF_ARG_1)) {
+ /* Used by ets_SUITE (stdlib) */
+ if (is_tuple(BIF_ARG_2)) {
+ Eterm* tpl = tuple_val(BIF_ARG_2);
+ Uint cnt;
+ if (arityval(tpl[0]) == 2 && is_atom(tpl[1]) &&
+ term_to_Uint(tpl[2], &cnt)) {
+ BIF_RET(erts_ets_colliding_names(BIF_P,tpl[1],cnt));
+ }
+ }
+ }
+ else if (ERTS_IS_ATOM_STR("re_loop_limit", BIF_ARG_1)) {
+ /* Used by re_SUITE (stdlib) */
+ Uint max_loops;
+ if (is_atom(BIF_ARG_2) && ERTS_IS_ATOM_STR("default", BIF_ARG_2)) {
+ max_loops = erts_re_set_loop_limit(-1);
+ BIF_RET(make_small(max_loops));
+ } else if (term_to_Uint(BIF_ARG_2, &max_loops) != 0) {
+ max_loops = erts_re_set_loop_limit(max_loops);
+ BIF_RET(make_small(max_loops));
+ }
+ }
+ else if (ERTS_IS_ATOM_STR("unicode_loop_limit", BIF_ARG_1)) {
+ /* Used by unicode_SUITE (stdlib) */
+ Uint max_loops;
+ if (is_atom(BIF_ARG_2) && ERTS_IS_ATOM_STR("default", BIF_ARG_2)) {
+ max_loops = erts_unicode_set_loop_limit(-1);
+ BIF_RET(make_small(max_loops));
+ } else if (term_to_Uint(BIF_ARG_2, &max_loops) != 0) {
+ max_loops = erts_unicode_set_loop_limit(max_loops);
+ BIF_RET(make_small(max_loops));
+ }
+ }
+ else if (ERTS_IS_ATOM_STR("hipe_test_reschedule_suspend", BIF_ARG_1)) {
+ /* Used by hipe test suites */
+ long flag = erts_smp_atomic_read(&hipe_test_reschedule_flag);
+ if (!flag && BIF_ARG_2 != am_false) {
+ erts_smp_atomic_set(&hipe_test_reschedule_flag, 1);
+ erts_suspend(BIF_P, ERTS_PROC_LOCK_MAIN, NULL);
+ ERTS_BIF_YIELD2(bif_export[BIF_erts_debug_set_internal_state_2],
+ BIF_P, BIF_ARG_1, BIF_ARG_2);
+ }
+ erts_smp_atomic_set(&hipe_test_reschedule_flag, !flag);
+ BIF_RET(NIL);
+ }
+ else if (ERTS_IS_ATOM_STR("hipe_test_reschedule_resume", BIF_ARG_1)) {
+ /* Used by hipe test suites */
+ Eterm res = am_false;
+ Process *rp = erts_pid2proc(BIF_P, ERTS_PROC_LOCK_MAIN,
+ BIF_ARG_2, ERTS_PROC_LOCK_STATUS);
+ if (rp) {
+ erts_resume(rp, ERTS_PROC_LOCK_STATUS);
+ res = am_true;
+ erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_STATUS);
+ }
+ BIF_RET(res);
+ }
+ else if (ERTS_IS_ATOM_STR("test_long_gc_sleep", BIF_ARG_1)) {
+ if (term_to_Uint(BIF_ARG_2, &erts_test_long_gc_sleep) > 0)
+ BIF_RET(am_true);
+ }
+ else if (ERTS_IS_ATOM_STR("abort", BIF_ARG_1)) {
+ erl_exit(ERTS_ABORT_EXIT, "%T\n", BIF_ARG_2);
+ }
+ else if (ERTS_IS_ATOM_STR("kill_dist_connection", BIF_ARG_1)) {
+ DistEntry *dep = erts_sysname_to_connected_dist_entry(BIF_ARG_2);
+ if (!dep)
+ BIF_RET(am_false);
+ else {
+ Uint32 con_id;
+ erts_smp_de_rlock(dep);
+ con_id = dep->connection_id;
+ erts_smp_de_runlock(dep);
+ erts_kill_dist_connection(dep, con_id);
+ erts_deref_dist_entry(dep);
+ BIF_RET(am_true);
+ }
+ }
+ else if (ERTS_IS_ATOM_STR("not_running_optimization", BIF_ARG_1)) {
+#ifdef ERTS_SMP
+ int old_use_opt, use_opt;
+ switch (BIF_ARG_2) {
+ case am_true:
+ use_opt = 1;
+ break;
+ case am_false:
+ use_opt = 0;
+ break;
+ default:
+ BIF_ERROR(BIF_P, BADARG);
+ }
+
+ erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ erts_smp_block_system(0);
+ old_use_opt = !erts_disable_proc_not_running_opt;
+ erts_disable_proc_not_running_opt = !use_opt;
+ erts_smp_release_system();
+ erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ BIF_RET(old_use_opt ? am_true : am_false);
+#else
+ BIF_ERROR(BIF_P, EXC_NOTSUP);
+#endif
+ }
+ }
+
+ BIF_ERROR(BIF_P, BADARG);
+}
+
+#ifdef ERTS_ENABLE_LOCK_COUNT
+static Eterm lcnt_build_lock_stats_term(Eterm **hpp, Uint *szp, erts_lcnt_lock_stats_t *stats, Eterm res) {
+ unsigned long tries = 0, colls = 0;
+ unsigned long timer_s = 0, timer_ns = 0, timer_n = 0;
+ unsigned int line = 0;
+
+ Eterm af, uil;
+ Eterm uit, uic;
+ Eterm uits, uitns, uitn;
+ Eterm tt, tstat, tloc, t;
+
+ /* term:
+ * [{{file, line}, {tries, colls, {seconds, nanoseconds, n_blocks}}}]
+ */
+
+ ethr_atomic_read(&stats->tries, (long *)&tries);
+ ethr_atomic_read(&stats->colls, (long *)&colls);
+
+ line = stats->line;
+ timer_s = stats->timer.s;
+ timer_ns = stats->timer.ns;
+ timer_n = stats->timer_n;
+
+ af = am_atom_put(stats->file, strlen(stats->file));
+ uil = erts_bld_uint( hpp, szp, line);
+ tloc = erts_bld_tuple(hpp, szp, 2, af, uil);
+
+ uit = erts_bld_uint( hpp, szp, tries);
+ uic = erts_bld_uint( hpp, szp, colls);
+
+ uits = erts_bld_uint( hpp, szp, timer_s);
+ uitns = erts_bld_uint( hpp, szp, timer_ns);
+ uitn = erts_bld_uint( hpp, szp, timer_n);
+ tt = erts_bld_tuple(hpp, szp, 3, uits, uitns, uitn);
+
+ tstat = erts_bld_tuple(hpp, szp, 3, uit, uic, tt);
+
+ t = erts_bld_tuple(hpp, szp, 2, tloc, tstat);
+
+ res = erts_bld_cons( hpp, szp, t, res);
+
+ return res;
+}
+
+static Eterm lcnt_build_lock_term(Eterm **hpp, Uint *szp, erts_lcnt_lock_t *lock, Eterm res) {
+ Eterm name, type, id, stats = NIL, t;
+ Process *proc = NULL;
+ char *ltype;
+ int i;
+
+ /* term:
+ * [{name, id, type, stats()}]
+ */
+
+ ASSERT(lock->name);
+
+ ltype = erts_lcnt_lock_type(lock->flag);
+
+ ASSERT(ltype);
+
+ type = am_atom_put(ltype, strlen(ltype));
+
+ name = am_atom_put(lock->name, strlen(lock->name));
+
+ if (lock->flag & ERTS_LCNT_LT_ALLOC) {
+ /* use allocator types names as id's for allocator locks */
+ ltype = ERTS_ALC_A2AD(signed_val(lock->id));
+ id = am_atom_put(ltype, strlen(ltype));
+ } else if (lock->flag & ERTS_LCNT_LT_PROCLOCK) {
+ /* use registered names as id's for process locks if available */
+ proc = erts_pid2proc_unlocked(lock->id);
+ if (proc && proc->reg) {
+ id = proc->reg->name;
+ } else {
+ /* otherwise use process id */
+ id = lock->id;
+ }
+ } else {
+ id = lock->id;
+ }
+
+ for (i = 0; i < lock->n_stats; i++) {
+ stats = lcnt_build_lock_stats_term(hpp, szp, &(lock->stats[i]), stats);
+ }
+
+ t = erts_bld_tuple(hpp, szp, 4, name, id, type, stats);
+
+ res = erts_bld_cons( hpp, szp, t, res);
+
+ return res;
+}
+
+static Eterm lcnt_build_result_term(Eterm **hpp, Uint *szp, erts_lcnt_data_t *data, Eterm res) {
+ Eterm dts, dtns, tdt, adur, tdur, aloc, lloc = NIL, tloc;
+ erts_lcnt_lock_t *lock = NULL;
+ char *str_duration = "duration";
+ char *str_locks = "locks";
+
+ /* term:
+ * [{'duration', {seconds, nanoseconds}}, {'locks', locks()}]
+ */
+
+ /* duration tuple */
+ dts = erts_bld_uint( hpp, szp, data->duration.s);
+ dtns = erts_bld_uint( hpp, szp, data->duration.ns);
+ tdt = erts_bld_tuple(hpp, szp, 2, dts, dtns);
+
+ adur = am_atom_put(str_duration, strlen(str_duration));
+ tdur = erts_bld_tuple(hpp, szp, 2, adur, tdt);
+
+ /* lock tuple */
+
+ aloc = am_atom_put(str_locks, strlen(str_locks));
+
+ for (lock = data->current_locks->head; lock != NULL ; lock = lock->next ) {
+ lloc = lcnt_build_lock_term(hpp, szp, lock, lloc);
+ }
+
+ for (lock = data->deleted_locks->head; lock != NULL ; lock = lock->next ) {
+ lloc = lcnt_build_lock_term(hpp, szp, lock, lloc);
+ }
+
+ tloc = erts_bld_tuple(hpp, szp, 2, aloc, lloc);
+
+ res = erts_bld_cons( hpp, szp, tloc, res);
+ res = erts_bld_cons( hpp, szp, tdur, res);
+
+ return res;
+}
+#endif
+
+BIF_RETTYPE erts_debug_lock_counters_1(BIF_ALIST_1)
+{
+#ifdef ERTS_ENABLE_LOCK_COUNT
+ Eterm res = NIL;
+ erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ erts_smp_block_system(0);
+
+ if (BIF_ARG_1 == am_info) {
+ erts_lcnt_data_t *data;
+ Uint hsize = 0;
+ Uint *szp;
+ Eterm* hp;
+
+ erts_lcnt_set_rt_opt(ERTS_LCNT_OPT_SUSPEND);
+
+ data = erts_lcnt_get_data();
+
+ /* calculate size */
+
+ szp = &hsize;
+ lcnt_build_result_term(NULL, szp, data, NIL);
+
+ /* alloc and build */
+
+ hp = HAlloc(BIF_P, hsize);
+
+ res = lcnt_build_result_term(&hp, NULL, data, res);
+
+ erts_lcnt_clear_rt_opt(ERTS_LCNT_OPT_SUSPEND);
+
+ goto done;
+ } else if (BIF_ARG_1 == am_clear) {
+ erts_lcnt_clear_counters();
+ res = am_ok;
+ goto done;
+ } else if (is_tuple(BIF_ARG_1)) {
+ Uint prev = 0;
+ Eterm* tp = tuple_val(BIF_ARG_1);
+ switch (arityval(tp[0])) {
+ case 2:
+ if (ERTS_IS_ATOM_STR("process_locks", tp[1])) {
+ if (tp[2] == am_true) {
+ prev = erts_lcnt_set_rt_opt(ERTS_LCNT_OPT_PROCLOCK);
+ if (prev) res = am_true;
+ else res = am_false;
+ goto done;
+ } else if (tp[2] == am_false) {
+ prev = erts_lcnt_clear_rt_opt(ERTS_LCNT_OPT_PROCLOCK);
+ if (prev) res = am_true;
+ else res = am_false;
+ goto done;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ erts_smp_release_system();
+ erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
+#endif
+ BIF_ERROR(BIF_P, BADARG);
+#ifdef ERTS_ENABLE_LOCK_COUNT
+done:
+ erts_smp_release_system();
+ erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ BIF_RET(res);
+#endif
+}
+
+void
+erts_bif_info_init(void)
+{
+ erts_smp_atomic_init(&available_internal_state, 0);
+ erts_smp_atomic_init(&hipe_test_reschedule_flag, 0);
+
+ process_info_init();
+}