aboutsummaryrefslogtreecommitdiffstats
path: root/erts
diff options
context:
space:
mode:
Diffstat (limited to 'erts')
-rw-r--r--erts/configure.in2
-rw-r--r--erts/doc/src/erlang.xml43
-rw-r--r--erts/emulator/beam/atom.names4
-rw-r--r--erts/emulator/beam/beam_emu.c22
-rw-r--r--erts/emulator/beam/erl_alloc.c6
-rw-r--r--erts/emulator/beam/erl_alloc.types1
-rw-r--r--erts/emulator/beam/erl_alloc_util.c41
-rw-r--r--erts/emulator/beam/erl_bif_trace.c23
-rw-r--r--erts/emulator/beam/erl_lock_check.c5
-rw-r--r--erts/emulator/beam/erl_port_task.c13
-rw-r--r--erts/emulator/beam/erl_process.c1
-rw-r--r--erts/emulator/beam/erl_process.h1
-rw-r--r--erts/emulator/beam/erl_ptab.c2
-rw-r--r--erts/emulator/beam/erl_trace.c128
-rw-r--r--erts/emulator/beam/erl_trace.h2
-rw-r--r--erts/emulator/beam/erl_unicode.c4
-rwxr-xr-xerts/emulator/beam/global.h2
-rw-r--r--erts/emulator/beam/io.c2
-rw-r--r--erts/emulator/beam/utils.c18
-rw-r--r--erts/emulator/sys/common/erl_poll.c166
-rw-r--r--erts/emulator/sys/win32/sys_time.c48
-rw-r--r--erts/emulator/test/port_SUITE.erl2
-rw-r--r--erts/emulator/test/trace_SUITE.erl69
-rw-r--r--erts/preloaded/ebin/erlang.beambin94140 -> 94124 bytes
-rw-r--r--erts/preloaded/src/erlang.erl7
25 files changed, 554 insertions, 58 deletions
diff --git a/erts/configure.in b/erts/configure.in
index c47c211c4e..b056ba44e2 100644
--- a/erts/configure.in
+++ b/erts/configure.in
@@ -429,7 +429,7 @@ case $host_os in
win32)
# The ethread library requires _WIN32_WINNT of at least 0x0403.
# -D_WIN32_WINNT=* from CPPFLAGS is saved in ETHR_DEFS.
- CPPFLAGS="$CPPFLAGS -D_WIN32_WINNT=0x0500 -DWINVER=0x0500"
+ CPPFLAGS="$CPPFLAGS -D_WIN32_WINNT=0x0501 -DWINVER=0x0501"
;;
darwin*)
CPPFLAGS="$CPPFLAGS -D_XOPEN_SOURCE"
diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml
index 7dc59ea954..767edc1cc0 100644
--- a/erts/doc/src/erlang.xml
+++ b/erts/doc/src/erlang.xml
@@ -6058,6 +6058,49 @@ ok
notice.
</p>
</item>
+ <tag><c>{long_schedule, Time}</c></tag>
+ <item>
+ <p>If a process or port in the system runs uninterrupted
+ for at least <c>Time</c> wall clock milliseconds, a
+ message <c>{monitor, PidOrPort, long_schedule, Info}</c>
+ is sent to <c>MonitorPid</c>. <c>PidOrPort</c> is the
+ process or port that was running and <c>Info</c> is a
+ list of two-element tuples describing the event. In case
+ of a <c>pid()</c>, the tuples <c>{timeout, Millis}</c>,
+ <c>{in, Location}</c> and <c>{out, Location}</c> will be
+ present, where <c>Location</c> is either an MFA
+ (<c>{Module, Function, Arity}</c>) describing the
+ function where the process was scheduled in/out, or the
+ atom <c>undefined</c>. In case of a <c>port()</c>, the
+ tuples <c>{timeout, Millis}</c> and <c>{port_op,Op}</c>
+ will be present. <c>Op</c> will be one of <c>proc_sig</c>,
+ <c>timeout</c>, <c>input</c>, <c>output</c>,
+ <c>event</c> or <c>dist_cmd</c>, depending on which
+ driver callback was executing. <c>proc_sig</c> is an
+ internal operation and should never appear, while the
+ others represent the corresponding driver callbacks
+ <c>timeout</c>, <c>ready_input</c>, <c>ready_output</c>,
+ <c>event</c> and finally <c>outputv</c> (when the port
+ is used by distribution). The <c>Millis</c> value in
+ the <c>timeout</c> tuple will tell you the actual
+ uninterrupted execution time of the process or port,
+ which will always be <c>&gt;=</c> the <c>Time</c> value
+ supplied when starting the trace. New tuples may be
+ added to the <c>Info</c> list in the future, and the
+ order of the tuples in the list may be changed at any
+ time without prior notice.
+ </p>
+ <p>This can be used to detect problems with NIF's or
+ drivers that take too long to execute. Generally, 1 ms
+ is considered a good maximum time for a driver callback
+ or a NIF. However, a time sharing system should usually
+ consider everything below 100 ms as "possible" and
+ fairly "normal". Schedule times above that might however
+ indicate swapping or a NIF/driver that is
+ misbehaving. Misbehaving NIF's and drivers could cause
+ bad resource utilization and bad overall performance of
+ the system.</p>
+ </item>
<tag><c>{large_heap, Size}</c></tag>
<item>
<p>If a garbage collection in the system results in
diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names
index 267bcf5209..eba1d0fa23 100644
--- a/erts/emulator/beam/atom.names
+++ b/erts/emulator/beam/atom.names
@@ -178,6 +178,7 @@ atom disable_trace
atom disabled
atom display_items
atom dist
+atom dist_cmd
atom Div='/'
atom div
atom dlink
@@ -313,6 +314,7 @@ atom load_cancelled
atom load_failure
atom local
atom long_gc
+atom long_schedule
atom low
atom Lt='<'
atom machine
@@ -432,6 +434,7 @@ atom port
atom ports
atom port_count
atom port_limit
+atom port_op
atom print
atom priority
atom private
@@ -443,6 +446,7 @@ atom process_display
atom process_limit
atom process_dump
atom procs
+atom proc_sig
atom profile
atom protected
atom protection
diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index 944ed6da81..5781009f58 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -924,6 +924,7 @@ extern int count_instructions;
# define NOINLINE
#endif
+
/*
* The following functions are called directly by process_main().
* Don't inline them.
@@ -1153,6 +1154,9 @@ void process_main(void)
Eterm pt_arity; /* Used by do_put_tuple */
+ Uint64 start_time = 0; /* Monitor long schedule */
+ BeamInstr* start_time_i = NULL;
+
ERL_BITS_DECLARE_STATEP; /* Has to be last declaration */
@@ -1175,6 +1179,16 @@ void process_main(void)
do_schedule:
reds_used = REDS_IN(c_p) - FCALLS;
do_schedule1:
+
+ if (start_time != 0) {
+ Sint64 diff = erts_timestamp_millis() - start_time;
+ if (diff > 0 && (Uint) diff > erts_system_monitor_long_schedule) {
+ BeamInstr *inptr = find_function_from_pc(start_time_i);
+ BeamInstr *outptr = find_function_from_pc(c_p->i);
+ monitor_long_schedule_proc(c_p,inptr,outptr,(Uint) diff);
+ }
+ }
+
PROCESS_MAIN_CHK_LOCKS(c_p);
ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p);
#if HALFWORD_HEAP
@@ -1183,11 +1197,18 @@ void process_main(void)
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
c_p = schedule(c_p, reds_used);
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
+ start_time = 0;
#ifdef DEBUG
pid = c_p->common.id; /* Save for debugging purpouses */
#endif
ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p);
PROCESS_MAIN_CHK_LOCKS(c_p);
+
+ if (erts_system_monitor_long_schedule != 0) {
+ start_time = erts_timestamp_millis();
+ start_time_i = c_p->i;
+ }
+
reg = ERTS_PROC_GET_SCHDATA(c_p)->x_reg_array;
freg = ERTS_PROC_GET_SCHDATA(c_p)->f_reg_array;
#if !HEAP_ON_C_STACK
@@ -6151,6 +6172,7 @@ apply_fun(Process* p, Eterm fun, Eterm args, Eterm* reg)
}
+
static Eterm
new_fun(Process* p, Eterm* reg, ErlFunEntry* fe, int num_free)
{
diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c
index a547191d6d..86575ccb9b 100644
--- a/erts/emulator/beam/erl_alloc.c
+++ b/erts/emulator/beam/erl_alloc.c
@@ -240,6 +240,10 @@ do { \
sys_memcpy((void *) (IP), (void *) &aui__, sizeof(struct au_init)); \
} while (0)
+#if ERTS_ALC_DEFAULT_ACUL \
+ || ERTS_ALC_DEFAULT_ACUL_LL_ALLOC \
+ || ERTS_ALC_DEFAULT_ACUL_EHEAP_ALLOC
+
static ERTS_INLINE void
set_default_acul(struct au_init *ip, int acul)
{
@@ -249,6 +253,8 @@ set_default_acul(struct au_init *ip, int acul)
ip->init.util.acul = acul;
}
+#endif
+
static void
set_default_sl_alloc_opts(struct au_init *ip)
{
diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types
index 99ba31e8e5..bb5eba80be 100644
--- a/erts/emulator/beam/erl_alloc.types
+++ b/erts/emulator/beam/erl_alloc.types
@@ -398,6 +398,7 @@ type POLLSET_UPDREQ SHORT_LIVED SYSTEM pollset_update_req
type POLL_FDS LONG_LIVED SYSTEM poll_fds
type POLL_RES_EVS LONG_LIVED SYSTEM poll_result_events
type FD_STATUS LONG_LIVED SYSTEM fd_status
+type SELECT_FDS LONG_LIVED SYSTEM select_fds
+if unix
diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c
index b19e603a5f..bf8a37c71b 100644
--- a/erts/emulator/beam/erl_alloc_util.c
+++ b/erts/emulator/beam/erl_alloc_util.c
@@ -1753,7 +1753,8 @@ handle_delayed_dealloc(Allctr_t *allctr,
if (IS_FREE_LAST_MBC_BLK(blk)) {
/*
* A multiblock carrier that previously has been migrated away
- * from us and now is back to be deallocated...
+ * from us and now is back to be deallocated. For more info
+ * see schedule_dealloc_carrier().
*
* Note that we cannot use FBLK_TO_MBC(blk) since it
* data has been overwritten by the queue.
@@ -1761,6 +1762,12 @@ handle_delayed_dealloc(Allctr_t *allctr,
Carrier_t *crr = FIRST_BLK_TO_MBC(allctr, blk);
ERTS_ALC_CPOOL_ASSERT(ERTS_ALC_IS_CPOOL_ENABLED(allctr));
ERTS_ALC_CPOOL_ASSERT(allctr == crr->cpool.orig_allctr);
+ ERTS_ALC_CPOOL_ASSERT(((erts_aint_t) allctr)
+ != (erts_smp_atomic_read_nob(&crr->allctr)
+ & ~FLG_MASK));
+
+ erts_smp_atomic_set_nob(&crr->allctr, ((erts_aint_t) allctr));
+
schedule_dealloc_carrier(allctr, crr);
}
else {
@@ -3001,7 +3008,7 @@ check_pending_dealloc_carrier(Allctr_t *allctr,
static void
schedule_dealloc_carrier(Allctr_t *allctr, Carrier_t *crr)
{
- Allctr_t *used_allctr;
+ Allctr_t *orig_allctr;
int check_pending_dealloc;
erts_aint_t max_size;
@@ -3010,25 +3017,37 @@ schedule_dealloc_carrier(Allctr_t *allctr, Carrier_t *crr)
return;
}
- used_allctr = crr->cpool.orig_allctr;
+ orig_allctr = crr->cpool.orig_allctr;
- if (allctr != used_allctr) {
+ if (allctr != orig_allctr) {
Block_t *blk = MBC_TO_FIRST_BLK(allctr, crr);
- int cinit = used_allctr->dd.ix - allctr->dd.ix;
+ int cinit = orig_allctr->dd.ix - allctr->dd.ix;
/*
- * Receiver will recognize that this is a carrier to
- * deallocate since the block is an mbc block that
- * is free and last in carrier...
+ * We send the carrier to its origin for deallocation.
+ * This in order:
+ * - not to complicate things for the thread specific
+ * instances of mseg_alloc, and
+ * - to ensure that we always only reuse empty carriers
+ * originating from our own thread specific mseg_alloc
+ * instance which is beneficial on NUMA systems.
+ *
+ * The receiver will recognize that this is a carrier to
+ * deallocate (and not a block which is the common case)
+ * since the block is an mbc block that is free and last
+ * in the carrier.
*/
ERTS_ALC_CPOOL_ASSERT(IS_FREE_LAST_MBC_BLK(blk));
ERTS_ALC_CPOOL_ASSERT(IS_MBC_FIRST_ABLK(allctr, blk));
ERTS_ALC_CPOOL_ASSERT(crr == FBLK_TO_MBC(blk));
- ERTS_ALC_CPOOL_ASSERT(crr == FIRST_BLK_TO_MBC(used_allctr, blk));
+ ERTS_ALC_CPOOL_ASSERT(crr == FIRST_BLK_TO_MBC(allctr, blk));
+ ERTS_ALC_CPOOL_ASSERT(((erts_aint_t) allctr)
+ == (erts_smp_atomic_read_nob(&crr->allctr)
+ & ~FLG_MASK));
- if (ddq_enqueue(&used_allctr->dd.q, BLK2UMEM(blk), cinit))
- erts_alloc_notify_delayed_dealloc(used_allctr->ix);
+ if (ddq_enqueue(&orig_allctr->dd.q, BLK2UMEM(blk), cinit))
+ erts_alloc_notify_delayed_dealloc(orig_allctr->ix);
return;
}
diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c
index 559cb3efa1..06fbbea123 100644
--- a/erts/emulator/beam/erl_bif_trace.c
+++ b/erts/emulator/beam/erl_bif_trace.c
@@ -2012,6 +2012,7 @@ void erts_system_monitor_clear(Process *c_p) {
#endif
erts_set_system_monitor(NIL);
erts_system_monitor_long_gc = 0;
+ erts_system_monitor_long_schedule = 0;
erts_system_monitor_large_heap = 0;
erts_system_monitor_flags.busy_port = 0;
erts_system_monitor_flags.busy_dist_port = 0;
@@ -2036,12 +2037,17 @@ static Eterm system_monitor_get(Process *p)
Uint hsz = 3 + (erts_system_monitor_flags.busy_dist_port ? 2 : 0) +
(erts_system_monitor_flags.busy_port ? 2 : 0);
Eterm long_gc = NIL;
+ Eterm long_schedule = NIL;
Eterm large_heap = NIL;
if (erts_system_monitor_long_gc != 0) {
hsz += 2+3;
(void) erts_bld_uint(NULL, &hsz, erts_system_monitor_long_gc);
}
+ if (erts_system_monitor_long_schedule != 0) {
+ hsz += 2+3;
+ (void) erts_bld_uint(NULL, &hsz, erts_system_monitor_long_schedule);
+ }
if (erts_system_monitor_large_heap != 0) {
hsz += 2+3;
(void) erts_bld_uint(NULL, &hsz, erts_system_monitor_large_heap);
@@ -2051,6 +2057,10 @@ static Eterm system_monitor_get(Process *p)
if (erts_system_monitor_long_gc != 0) {
long_gc = erts_bld_uint(&hp, NULL, erts_system_monitor_long_gc);
}
+ if (erts_system_monitor_long_schedule != 0) {
+ long_schedule = erts_bld_uint(&hp, NULL,
+ erts_system_monitor_long_schedule);
+ }
if (erts_system_monitor_large_heap != 0) {
large_heap = erts_bld_uint(&hp, NULL, erts_system_monitor_large_heap);
}
@@ -2059,6 +2069,10 @@ static Eterm system_monitor_get(Process *p)
Eterm t = TUPLE2(hp, am_long_gc, long_gc); hp += 3;
res = CONS(hp, t, res); hp += 2;
}
+ if (long_schedule != NIL) {
+ Eterm t = TUPLE2(hp, am_long_schedule, long_schedule); hp += 3;
+ res = CONS(hp, t, res); hp += 2;
+ }
if (large_heap != NIL) {
Eterm t = TUPLE2(hp, am_large_heap, large_heap); hp += 3;
res = CONS(hp, t, res); hp += 2;
@@ -2113,7 +2127,7 @@ system_monitor(Process *p, Eterm monitor_pid, Eterm list)
}
if (is_not_list(list)) goto error;
else {
- Uint long_gc, large_heap;
+ Uint long_gc, long_schedule, large_heap;
int busy_port, busy_dist_port;
system_blocked = 1;
@@ -2123,7 +2137,8 @@ system_monitor(Process *p, Eterm monitor_pid, Eterm list)
if (!erts_pid2proc(p, ERTS_PROC_LOCK_MAIN, monitor_pid, 0))
goto error;
- for (long_gc = 0, large_heap = 0, busy_port = 0, busy_dist_port = 0;
+ for (long_gc = 0, long_schedule = 0, large_heap = 0,
+ busy_port = 0, busy_dist_port = 0;
is_list(list);
list = CDR(list_val(list))) {
Eterm t = CAR(list_val(list));
@@ -2133,6 +2148,9 @@ system_monitor(Process *p, Eterm monitor_pid, Eterm list)
if (tp[1] == am_long_gc) {
if (! term_to_Uint(tp[2], &long_gc)) goto error;
if (long_gc < 1) long_gc = 1;
+ } else if (tp[1] == am_long_schedule) {
+ if (! term_to_Uint(tp[2], &long_schedule)) goto error;
+ if (long_schedule < 1) long_schedule = 1;
} else if (tp[1] == am_large_heap) {
if (! term_to_Uint(tp[2], &large_heap)) goto error;
if (large_heap < 16384) large_heap = 16384;
@@ -2148,6 +2166,7 @@ system_monitor(Process *p, Eterm monitor_pid, Eterm list)
prev = system_monitor_get(p);
erts_set_system_monitor(monitor_pid);
erts_system_monitor_long_gc = long_gc;
+ erts_system_monitor_long_schedule = long_schedule;
erts_system_monitor_large_heap = large_heap;
erts_system_monitor_flags.busy_port = !!busy_port;
erts_system_monitor_flags.busy_dist_port = !!busy_dist_port;
diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c
index bc59fe55d4..2ac5e24d3a 100644
--- a/erts/emulator/beam/erl_lock_check.c
+++ b/erts/emulator/beam/erl_lock_check.c
@@ -180,6 +180,11 @@ static erts_lc_lock_order_t erts_lock_order[] = {
{ "efile_drv dtrace mutex", NULL },
#endif
{ "mtrace_buf", NULL },
+#ifdef __WIN32__
+#ifdef ERTS_SMP
+ { "sys_gethrtime", NULL },
+#endif
+#endif
{ "erts_alloc_hard_debug", NULL }
};
diff --git a/erts/emulator/beam/erl_port_task.c b/erts/emulator/beam/erl_port_task.c
index f753de8f52..7d53ce7152 100644
--- a/erts/emulator/beam/erl_port_task.c
+++ b/erts/emulator/beam/erl_port_task.c
@@ -1594,6 +1594,7 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp)
int fpe_was_unmasked;
erts_aint32_t state;
int active;
+ Uint64 start_time = 0;
ERTS_SMP_LC_ASSERT(erts_smp_lc_runq_is_locked(runq));
@@ -1655,6 +1656,10 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp)
reset_handle(ptp);
+ if (erts_system_monitor_long_schedule != 0) {
+ start_time = erts_timestamp_millis();
+ }
+
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(pp));
ERTS_SMP_CHK_NO_PROC_LOCKS;
ASSERT(pp->drv_ptr);
@@ -1723,6 +1728,14 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp)
reds += erts_port_driver_callback_epilogue(pp, &state);
+ if (start_time != 0) {
+ Sint64 diff = erts_timestamp_millis() - start_time;
+ if (diff > 0 && (Uint) diff > erts_system_monitor_long_schedule) {
+ monitor_long_schedule_port(pp,ptp->type,(Uint) diff);
+ }
+ }
+ start_time = 0;
+
aborted_port_task:
schedule_port_task_free(ptp);
diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c
index 783a529747..64b4c25ba0 100644
--- a/erts/emulator/beam/erl_process.c
+++ b/erts/emulator/beam/erl_process.c
@@ -267,6 +267,7 @@ static Uint last_exact_reductions;
Uint erts_default_process_flags;
Eterm erts_system_monitor;
Eterm erts_system_monitor_long_gc;
+Uint erts_system_monitor_long_schedule;
Eterm erts_system_monitor_large_heap;
struct erts_system_monitor_flags_t erts_system_monitor_flags;
diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h
index 56cefc9e7d..973db65490 100644
--- a/erts/emulator/beam/erl_process.h
+++ b/erts/emulator/beam/erl_process.h
@@ -1019,6 +1019,7 @@ extern erts_smp_rwmtx_t erts_cpu_bind_rwmtx;
*/
extern Eterm erts_system_monitor;
extern Uint erts_system_monitor_long_gc;
+extern Uint erts_system_monitor_long_schedule;
extern Uint erts_system_monitor_large_heap;
struct erts_system_monitor_flags_t {
unsigned int busy_port : 1;
diff --git a/erts/emulator/beam/erl_ptab.c b/erts/emulator/beam/erl_ptab.c
index 8da135b2c8..f7b5f25eac 100644
--- a/erts/emulator/beam/erl_ptab.c
+++ b/erts/emulator/beam/erl_ptab.c
@@ -1435,7 +1435,7 @@ erts_ptab_test_next_id(ErtsPTab *ptab, int set, Uint next)
aid_ix = max_ix;
else
aid_ix--;
- ASSERT((aid_ix & max_ix) == (((Uint32) erts_atomic32_read_nob(&ptab->vola.tile.fid_ix)) & max_ix));
+ ASSERT((aid_ix & max_ix) == (((Uint32) erts_smp_atomic32_read_nob(&ptab->vola.tile.fid_ix)) & max_ix));
#endif
}
diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c
index 848877d43e..bb6ed44523 100644
--- a/erts/emulator/beam/erl_trace.c
+++ b/erts/emulator/beam/erl_trace.c
@@ -2268,7 +2268,134 @@ trace_gc(Process *p, Eterm what)
#undef LOCAL_HEAP_SIZE
}
+void
+monitor_long_schedule_proc(Process *p, BeamInstr *in_fp, BeamInstr *out_fp, Uint time)
+{
+ ErlHeapFragment *bp;
+ ErlOffHeap *off_heap;
+#ifndef ERTS_SMP
+ Process *monitor_p;
+#endif
+ Uint hsz;
+ Eterm *hp, list, in_mfa = am_undefined, out_mfa = am_undefined;
+ Eterm in_tpl, out_tpl, tmo_tpl, tmo, msg;
+
+
+#ifndef ERTS_SMP
+ ASSERT(is_internal_pid(system_monitor));
+ monitor_p = erts_proc_lookup(system_monitor);
+ if (!monitor_p || p == monitor_p) {
+ return;
+ }
+#endif
+ /*
+ * Size: {monitor, pid, long_schedule, [{timeout, T}, {in, {M,F,A}},{out,{M,F,A}}]} ->
+ * 5 (top tuple of 4), (3 (elements) * 2 (cons)) + 3 (timeout tuple of 2) + size of Timeout +
+ * (2 * 3 (in/out tuple of 2)) +
+ * 0 (unknown) or 4 (MFA tuple of 3) + 0 (unknown) or 4 (MFA tuple of 3)
+ * = 20 + (in_fp != NULL) ? 4 : 0 + (out_fp != NULL) ? 4 : 0 + size of Timeout
+ */
+ hsz = 20 + ((in_fp != NULL) ? 4 : 0) + ((out_fp != NULL) ? 4 : 0);
+ (void) erts_bld_uint(NULL, &hsz, time);
+ hp = ERTS_ALLOC_SYSMSG_HEAP(hsz, &bp, &off_heap, monitor_p);
+ tmo = erts_bld_uint(&hp, NULL, time);
+ if (in_fp != NULL) {
+ in_mfa = TUPLE3(hp,(Eterm) in_fp[0], (Eterm) in_fp[1], make_small(in_fp[2]));
+ hp +=4;
+ }
+ if (out_fp != NULL) {
+ out_mfa = TUPLE3(hp,(Eterm) out_fp[0], (Eterm) out_fp[1], make_small(out_fp[2]));
+ hp +=4;
+ }
+ tmo_tpl = TUPLE2(hp,am_timeout, tmo);
+ hp += 3;
+ in_tpl = TUPLE2(hp,am_in,in_mfa);
+ hp += 3;
+ out_tpl = TUPLE2(hp,am_out,out_mfa);
+ hp += 3;
+ list = CONS(hp,out_tpl,NIL);
+ hp += 2;
+ list = CONS(hp,in_tpl,list);
+ hp += 2;
+ list = CONS(hp,tmo_tpl,list);
+ hp += 2;
+ msg = TUPLE4(hp, am_monitor, p->common.id, am_long_schedule, list);
+ hp += 5;
+#ifdef ERTS_SMP
+ enqueue_sys_msg(SYS_MSG_TYPE_SYSMON, p->common.id, NIL, msg, bp);
+#else
+ erts_queue_message(monitor_p, NULL, bp, msg, NIL
+#ifdef USE_VM_PROBES
+ , NIL
+#endif
+ );
+#endif
+}
+void
+monitor_long_schedule_port(Port *pp, ErtsPortTaskType type, Uint time)
+{
+ ErlHeapFragment *bp;
+ ErlOffHeap *off_heap;
+#ifndef ERTS_SMP
+ Process *monitor_p;
+#endif
+ Uint hsz;
+ Eterm *hp, list, op;
+ Eterm op_tpl, tmo_tpl, tmo, msg;
+
+
+#ifndef ERTS_SMP
+ ASSERT(is_internal_pid(system_monitor));
+ monitor_p = erts_proc_lookup(system_monitor);
+ if (!monitor_p) {
+ return;
+ }
+#endif
+ /*
+ * Size: {monitor, port, long_schedule, [{timeout, T}, {op, Operation}]} ->
+ * 5 (top tuple of 4), (2 (elements) * 2 (cons)) + 3 (timeout tuple of 2)
+ * + size of Timeout + 3 (op tuple of 2 atoms)
+ * = 15 + size of Timeout
+ */
+ hsz = 15;
+ (void) erts_bld_uint(NULL, &hsz, time);
+
+ hp = ERTS_ALLOC_SYSMSG_HEAP(hsz, &bp, &off_heap, monitor_p);
+ switch (type) {
+ case ERTS_PORT_TASK_PROC_SIG: op = am_proc_sig; break;
+ case ERTS_PORT_TASK_TIMEOUT: op = am_timeout; break;
+ case ERTS_PORT_TASK_INPUT: op = am_input; break;
+ case ERTS_PORT_TASK_OUTPUT: op = am_output; break;
+ case ERTS_PORT_TASK_EVENT: op = am_event; break;
+ case ERTS_PORT_TASK_DIST_CMD: op = am_dist_cmd; break;
+ default: op = am_undefined; break;
+ }
+
+ tmo = erts_bld_uint(&hp, NULL, time);
+
+ op_tpl = TUPLE2(hp,am_port_op,op);
+ hp += 3;
+
+ tmo_tpl = TUPLE2(hp,am_timeout, tmo);
+ hp += 3;
+
+ list = CONS(hp,op_tpl,NIL);
+ hp += 2;
+ list = CONS(hp,tmo_tpl,list);
+ hp += 2;
+ msg = TUPLE4(hp, am_monitor, pp->common.id, am_long_schedule, list);
+ hp += 5;
+#ifdef ERTS_SMP
+ enqueue_sys_msg(SYS_MSG_TYPE_SYSMON, pp->common.id, NIL, msg, bp);
+#else
+ erts_queue_message(monitor_p, NULL, bp, msg, NIL
+#ifdef USE_VM_PROBES
+ , NIL
+#endif
+ );
+#endif
+}
void
monitor_long_gc(Process *p, Uint time) {
@@ -3011,6 +3138,7 @@ sys_msg_disp_failure(ErtsSysMsgQ *smqp, Eterm receiver)
case SYS_MSG_TYPE_SYSMON:
if (receiver == NIL
&& !erts_system_monitor_long_gc
+ && !erts_system_monitor_long_schedule
&& !erts_system_monitor_large_heap
&& !erts_system_monitor_flags.busy_port
&& !erts_system_monitor_flags.busy_dist_port)
diff --git a/erts/emulator/beam/erl_trace.h b/erts/emulator/beam/erl_trace.h
index 50fb27aab0..54d3aafdda 100644
--- a/erts/emulator/beam/erl_trace.h
+++ b/erts/emulator/beam/erl_trace.h
@@ -83,6 +83,8 @@ void erts_system_profile_setup_active_schedulers(void);
/* system_monitor */
void monitor_long_gc(Process *p, Uint time);
+void monitor_long_schedule_proc(Process *p, BeamInstr *in_i, BeamInstr *out_i, Uint time);
+void monitor_long_schedule_port(Port *pp, ErtsPortTaskType type, Uint time);
void monitor_large_heap(Process *p);
void monitor_generic(Process *p, Eterm type, Eterm spec);
Uint erts_trace_flag2bit(Eterm flag);
diff --git a/erts/emulator/beam/erl_unicode.c b/erts/emulator/beam/erl_unicode.c
index ad6f8b993a..e00440b905 100644
--- a/erts/emulator/beam/erl_unicode.c
+++ b/erts/emulator/beam/erl_unicode.c
@@ -1723,14 +1723,14 @@ static BIF_RETTYPE do_bif_utf8_to_list(Process *p,
if (b_sz) {
ErlSubBin *sb;
Eterm orig;
- ERTS_DECLARE_DUMMY(Uint offset);
+ Uint offset;
ASSERT(state != ERTS_UTF8_OK);
hp = HAlloc(p, ERL_SUB_BIN_SIZE);
sb = (ErlSubBin *) hp;
ERTS_GET_REAL_BIN(orig_bin, orig, offset, bitoffs, bitsize);
sb->thing_word = HEADER_SUB_BIN;
sb->size = b_sz;
- sb->offs = num_bytes_to_process + num_processed_bytes;
+ sb->offs = offset + num_bytes_to_process + num_processed_bytes;
sb->orig = orig;
sb->bitoffs = bitoffs;
sb->bitsize = bitsize;
diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h
index 02087b4218..25aedc91c6 100755
--- a/erts/emulator/beam/global.h
+++ b/erts/emulator/beam/global.h
@@ -858,6 +858,8 @@ void erl_drv_thr_init(void);
/* utils.c */
void erts_cleanup_offheap(ErlOffHeap *offheap);
+Uint64 erts_timestamp_millis(void);
+
Export* erts_find_function(Eterm, Eterm, unsigned int, ErtsCodeIndex);
Eterm store_external_or_ref_in_proc_(Process *, Eterm);
diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c
index 01e130bd64..c1e66b59af 100644
--- a/erts/emulator/beam/io.c
+++ b/erts/emulator/beam/io.c
@@ -4842,7 +4842,7 @@ erts_stale_drv_select(Eterm port,
if (drv_port == ERTS_INVALID_ERL_DRV_PORT) {
Port *prt = erts_port_lookup_raw(port);
if (prt)
- drv_port = ERTS_Port2ErlDrvPort(port);
+ drv_port = ERTS_Port2ErlDrvPort(prt);
else
drv_port = ERTS_INVALID_ERL_DRV_PORT;
}
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index d91ddb7eae..62caa67ce1 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -3722,6 +3722,24 @@ erts_smp_ensure_later_interval_acqb(erts_interval_t *icp, Uint64 ic)
}
+/*
+ * A millisecond timestamp without time correction where there's no hrtime
+ * - for tracing on "long" things...
+ */
+Uint64 erts_timestamp_millis(void)
+{
+#ifdef HAVE_GETHRTIME
+ return (Uint64) (sys_gethrtime() / 1000000);
+#else
+ Uint64 res;
+ SysTimeval tv;
+ sys_gettimeofday(&tv);
+ res = (Uint64) tv.tv_sec*1000000;
+ res += (Uint64) tv.tv_usec;
+ return (res / 1000);
+#endif
+}
+
#ifdef DEBUG
/*
* Handy functions when using a debugger - don't use in the code!
diff --git a/erts/emulator/sys/common/erl_poll.c b/erts/emulator/sys/common/erl_poll.c
index a523d67158..7d292a304a 100644
--- a/erts/emulator/sys/common/erl_poll.c
+++ b/erts/emulator/sys/common/erl_poll.c
@@ -40,6 +40,13 @@
# include "config.h"
#endif
+#if defined(__DARWIN__) || defined(__APPLE__) && defined(__MACH__)
+/* Setting _DARWIN_UNLIMITED_SELECT before including sys/select.h enables
+ * the version of select() that does not place a limit on the fd_set.
+ */
+# define _DARWIN_UNLIMITED_SELECT
+#endif
+
#ifndef WANT_NONBLOCKING
# define WANT_NONBLOCKING
#endif
@@ -90,6 +97,52 @@
#define HARD_DEBUG
#endif
+#ifdef _DARWIN_UNLIMITED_SELECT
+typedef struct {
+ size_t sz;
+ fd_set* ptr;
+}ERTS_fd_set;
+# define ERTS_FD_CLR(fd, fds) FD_CLR((fd), (fds)->ptr)
+# define ERTS_FD_SET(fd, fds) FD_SET((fd), (fds)->ptr)
+# define ERTS_FD_ISSET(fd,fds) FD_ISSET((fd), (fds)->ptr)
+# define ERTS_FD_ZERO(fds) memset((fds)->ptr, 0, (fds)->sz)
+# define ERTS_FD_SIZE(n) ((((n)+NFDBITS-1)/NFDBITS)*sizeof(fd_mask))
+
+static void ERTS_FD_COPY(ERTS_fd_set *src, ERTS_fd_set *dst)
+{
+ if (dst->sz != src->sz) {
+ dst->ptr = dst->ptr
+ ? erts_realloc(ERTS_ALC_T_SELECT_FDS, dst->ptr, src->sz)
+ : erts_alloc(ERTS_ALC_T_SELECT_FDS, src->sz);
+ dst->sz = src->sz;
+ }
+ memcpy(dst->ptr, src->ptr, src->sz);
+}
+
+static ERTS_INLINE
+int ERTS_SELECT(int nfds, ERTS_fd_set *readfds, ERTS_fd_set *writefds,
+ ERTS_fd_set *exceptfds, struct timeval *timeout)
+{
+ ASSERT(!readfds || readfds->sz >= nfds);
+ ASSERT(!writefds || writefds->sz >= nfds);
+ ASSERT(!exceptfds);
+ return select(nfds,
+ (readfds ? readfds->ptr : NULL ),
+ (writefds ? writefds->ptr : NULL),
+ NULL,
+ timeout);
+}
+
+#else /* !_DARWIN_UNLIMITED_SELECT */
+# define ERTS_fd_set fd_set
+# define ERTS_FD_CLR FD_CLR
+# define ERTS_FD_ISSET FD_ISSET
+# define ERTS_FD_SET FD_SET
+# define ERTS_FD_ZERO FD_ZERO
+# define ERTS_FD_COPY(src,dst) (*(dst) = *(src))
+# define ERTS_SELECT select
+#endif
+
#define ERTS_POLL_USE_BATCH_UPDATE_POLLSET (ERTS_POLL_USE_DEVPOLL \
|| ERTS_POLL_USE_KQUEUE)
#define ERTS_POLL_USE_UPDATE_REQUESTS_QUEUE \
@@ -244,10 +297,10 @@ struct ErtsPollSet_ {
#if ERTS_POLL_USE_FALLBACK
int no_select_fds;
#endif
- fd_set input_fds;
- fd_set res_input_fds;
- fd_set output_fds;
- fd_set res_output_fds;
+ ERTS_fd_set input_fds;
+ ERTS_fd_set res_input_fds;
+ ERTS_fd_set output_fds;
+ ERTS_fd_set res_output_fds;
#endif
#if ERTS_POLL_USE_UPDATE_REQUESTS_QUEUE
ErtsPollSetUpdateRequestsBlock update_requests;
@@ -624,6 +677,33 @@ grow_poll_fds(ErtsPollSet ps, int min_ix)
}
#endif
+#ifdef _DARWIN_UNLIMITED_SELECT
+static void
+grow_select_fds(int fd, ERTS_fd_set* fds)
+{
+ int new_len = ERTS_POLL_EXPORT(erts_poll_get_table_len)(fd + 1);
+ if (new_len > max_fds)
+ new_len = max_fds;
+ new_len = ERTS_FD_SIZE(new_len);
+ fds->ptr = fds->sz
+ ? erts_realloc(ERTS_ALC_T_SELECT_FDS, fds->ptr, new_len)
+ : erts_alloc(ERTS_ALC_T_SELECT_FDS, new_len);
+ memset((char*)fds->ptr + fds->sz, 0, new_len - fds->sz);
+ fds->sz = new_len;
+}
+static ERTS_INLINE void
+ensure_select_fds(int fd, ERTS_fd_set* in, ERTS_fd_set* out)
+{
+ ASSERT(in->sz == out->sz);
+ if (ERTS_FD_SIZE(fd+1) > in->sz) {
+ grow_select_fds(fd, in);
+ grow_select_fds(fd, out);
+ }
+}
+#else
+# define ensure_select_fds(fd, in, out) do {} while(0)
+#endif /* _DARWIN_UNLIMITED_SELECT */
+
static void
grow_fds_status(ErtsPollSet ps, int min_fd)
{
@@ -1290,22 +1370,23 @@ static int update_pollset(ErtsPollSet ps, int fd)
#elif ERTS_POLL_USE_SELECT /* --- select ------------------------------ */
{
ErtsPollEvents events = ps->fds_status[fd].events;
+ ensure_select_fds(fd, &ps->input_fds, &ps->output_fds);
if ((ERTS_POLL_EV_IN & events)
!= (ERTS_POLL_EV_IN & ps->fds_status[fd].used_events)) {
if (ERTS_POLL_EV_IN & events) {
- FD_SET(fd, &ps->input_fds);
+ ERTS_FD_SET(fd, &ps->input_fds);
}
else {
- FD_CLR(fd, &ps->input_fds);
+ ERTS_FD_CLR(fd, &ps->input_fds);
}
}
if ((ERTS_POLL_EV_OUT & events)
!= (ERTS_POLL_EV_OUT & ps->fds_status[fd].used_events)) {
if (ERTS_POLL_EV_OUT & events) {
- FD_SET(fd, &ps->output_fds);
+ ERTS_FD_SET(fd, &ps->output_fds);
}
else {
- FD_CLR(fd, &ps->output_fds);
+ ERTS_FD_CLR(fd, &ps->output_fds);
}
}
@@ -1789,7 +1870,7 @@ save_poll_result(ErtsPollSet ps, ErtsPollResFd pr[], int max_res,
while (fd < end_fd && res < max_res) {
pr[res].events = (ErtsPollEvents) 0;
- if (FD_ISSET(fd, &ps->res_input_fds)) {
+ if (ERTS_FD_ISSET(fd, &ps->res_input_fds)) {
#if ERTS_POLL_USE_FALLBACK
if (fd == ps->kp_fd) {
res += get_kp_results(ps, &pr[res], max_res-res);
@@ -1805,7 +1886,7 @@ save_poll_result(ErtsPollSet ps, ErtsPollResFd pr[], int max_res,
#endif
pr[res].events |= ERTS_POLL_EV_IN;
}
- if (FD_ISSET(fd, &ps->res_output_fds))
+ if (ERTS_FD_ISSET(fd, &ps->res_output_fds))
pr[res].events |= ERTS_POLL_EV_OUT;
if (pr[res].events) {
pr[res].fd = fd;
@@ -1832,24 +1913,23 @@ save_poll_result(ErtsPollSet ps, ErtsPollResFd pr[], int max_res,
while (fd < end_fd && res < max_res) {
if (ps->fds_status[fd].events) {
int sres;
- fd_set *iset = NULL;
- fd_set *oset = NULL;
+ ERTS_fd_set *iset = NULL;
+ ERTS_fd_set *oset = NULL;
if (ps->fds_status[fd].events & ERTS_POLL_EV_IN) {
iset = &ps->res_input_fds;
- FD_ZERO(iset);
- FD_SET(fd, iset);
+ ERTS_FD_ZERO(iset);
+ ERTS_FD_SET(fd, iset);
}
if (ps->fds_status[fd].events & ERTS_POLL_EV_OUT) {
oset = &ps->res_output_fds;
- FD_ZERO(oset);
- FD_SET(fd, oset);
-
+ ERTS_FD_ZERO(oset);
+ ERTS_FD_SET(fd, oset);
}
do {
/* Initiate 'tv' each time;
select() may modify it */
SysTimeval tv = {0, 0};
- sres = select(ps->max_fd+1, iset, oset, NULL, &tv);
+ sres = ERTS_SELECT(ps->max_fd+1, iset, oset, NULL, &tv);
} while (sres < 0 && errno == EINTR);
if (sres < 0) {
#if ERTS_POLL_USE_FALLBACK
@@ -1873,7 +1953,7 @@ save_poll_result(ErtsPollSet ps, ErtsPollResFd pr[], int max_res,
}
else if (sres > 0) {
pr[res].fd = fd;
- if (iset && FD_ISSET(fd, iset)) {
+ if (iset && ERTS_FD_ISSET(fd, iset)) {
#if ERTS_POLL_USE_FALLBACK
if (fd == ps->kp_fd) {
res += get_kp_results(ps,
@@ -1891,7 +1971,7 @@ save_poll_result(ErtsPollSet ps, ErtsPollResFd pr[], int max_res,
#endif
pr[res].events |= ERTS_POLL_EV_IN;
}
- if (oset && FD_ISSET(fd, oset)) {
+ if (oset && ERTS_FD_ISSET(fd, oset)) {
pr[res].events |= ERTS_POLL_EV_OUT;
}
ASSERT(pr[res].events);
@@ -1992,14 +2072,14 @@ check_fd_events(ErtsPollSet ps, SysTimeval *tv, int max_res)
#elif ERTS_POLL_USE_SELECT /* --- select ------------------------------ */
SysTimeval to = *tv;
- ps->res_input_fds = ps->input_fds;
- ps->res_output_fds = ps->output_fds;
-
+ ERTS_FD_COPY(&ps->input_fds, &ps->res_input_fds);
+ ERTS_FD_COPY(&ps->output_fds, &ps->res_output_fds);
+
#ifdef ERTS_SMP
if (to.tv_sec || to.tv_usec)
erts_thr_progress_prepare_wait(NULL);
#endif
- res = select(ps->max_fd + 1,
+ res = ERTS_SELECT(ps->max_fd + 1,
&ps->res_input_fds,
&ps->res_output_fds,
NULL,
@@ -2027,7 +2107,7 @@ check_fd_events(ErtsPollSet ps, SysTimeval *tv, int max_res)
ERTS_POLLSET_LOCK(ps);
handle_update_requests(ps);
ERTS_POLLSET_UNLOCK(ps);
- res = select(ps->max_fd + 1,
+ res = ERTS_SELECT(ps->max_fd + 1,
&ps->res_input_fds,
&ps->res_output_fds,
NULL,
@@ -2233,7 +2313,8 @@ ERTS_POLL_EXPORT(erts_poll_init)(void)
max_fds = OPEN_MAX;
#endif
-#if ERTS_POLL_USE_SELECT && defined(FD_SETSIZE)
+#if ERTS_POLL_USE_SELECT && defined(FD_SETSIZE) && \
+ !defined(_DARWIN_UNLIMITED_SELECT)
if (max_fds > FD_SETSIZE)
max_fds = FD_SETSIZE;
#endif
@@ -2301,10 +2382,21 @@ ERTS_POLL_EXPORT(erts_poll_create_pollset)(void)
#if ERTS_POLL_USE_FALLBACK
ps->no_select_fds = 0;
#endif
- FD_ZERO(&ps->input_fds);
- FD_ZERO(&ps->res_input_fds);
- FD_ZERO(&ps->output_fds);
- FD_ZERO(&ps->res_output_fds);
+#ifdef _DARWIN_UNLIMITED_SELECT
+ ps->input_fds.sz = 0;
+ ps->input_fds.ptr = NULL;
+ ps->res_input_fds.sz = 0;
+ ps->res_input_fds.ptr = NULL;
+ ps->output_fds.sz = 0;
+ ps->output_fds.ptr = NULL;
+ ps->res_output_fds.sz = 0;
+ ps->res_output_fds.ptr = NULL;
+#else
+ ERTS_FD_ZERO(&ps->input_fds);
+ ERTS_FD_ZERO(&ps->res_input_fds);
+ ERTS_FD_ZERO(&ps->output_fds);
+ ERTS_FD_ZERO(&ps->res_output_fds);
+#endif
#endif
#if ERTS_POLL_USE_UPDATE_REQUESTS_QUEUE
ps->update_requests.next = NULL;
@@ -2382,6 +2474,16 @@ ERTS_POLL_EXPORT(erts_poll_destroy_pollset)(ErtsPollSet ps)
if (ps->poll_fds)
erts_free(ERTS_ALC_T_POLL_FDS, (void *) ps->poll_fds);
#elif ERTS_POLL_USE_SELECT
+#ifdef _DARWIN_UNLIMITED_SELECT
+ if (ps->input_fds.ptr)
+ erts_free(ERTS_ALC_T_SELECT_FDS, (void *) ps->input_fds.ptr);
+ if (ps->res_input_fds.ptr)
+ erts_free(ERTS_ALC_T_SELECT_FDS, (void *) ps->res_input_fds.ptr);
+ if (ps->output_fds.ptr)
+ erts_free(ERTS_ALC_T_SELECT_FDS, (void *) ps->output_fds.ptr);
+ if (ps->res_output_fds.ptr)
+ erts_free(ERTS_ALC_T_SELECT_FDS, (void *) ps->res_output_fds.ptr);
+#endif
#endif
#if ERTS_POLL_USE_UPDATE_REQUESTS_QUEUE
{
@@ -2445,6 +2547,10 @@ ERTS_POLL_EXPORT(erts_poll_info)(ErtsPollSet ps, ErtsPollInfo *pip)
#if ERTS_POLL_USE_POLL
size += ps->poll_fds_len*sizeof(struct pollfd);
#elif ERTS_POLL_USE_SELECT
+#ifdef _DARWIN_UNLIMITED_SELECT
+ size += ps->input_fds.sz + ps->res_input_fds.sz
+ + ps->output_fds.sz + ps->res_output_fds.sz;
+#endif
#endif
#if ERTS_POLL_USE_UPDATE_REQUESTS_QUEUE
diff --git a/erts/emulator/sys/win32/sys_time.c b/erts/emulator/sys/win32/sys_time.c
index 2f2dfc8197..f7f0161b58 100644
--- a/erts/emulator/sys/win32/sys_time.c
+++ b/erts/emulator/sys/win32/sys_time.c
@@ -63,6 +63,8 @@
static SysHrTime wrap = 0;
static DWORD last_tick_count = 0;
+static erts_smp_mtx_t wrap_lock;
+static ULONGLONG (WINAPI *pGetTickCount64)(void) = NULL;
/* Getting timezone information is a heavy operation, so we want to do this
only once */
@@ -77,11 +79,23 @@ static int days_in_month[2][13] = {
int
sys_init_time(void)
{
+ char kernel_dll_name[] = "kernel32";
+ HMODULE module;
+
+ module = GetModuleHandle(kernel_dll_name);
+ pGetTickCount64 = (module != NULL) ?
+ (ULONGLONG (WINAPI *)(void))
+ GetProcAddress(module,"GetTickCount64") :
+ NULL;
+
if(GetTimeZoneInformation(&static_tzi) &&
static_tzi.StandardDate.wMonth != 0 &&
static_tzi.DaylightDate.wMonth != 0) {
have_static_tzi = 1;
}
+
+ erts_smp_mtx_init(&wrap_lock, "sys_gethrtime");
+
return 1;
}
@@ -363,15 +377,39 @@ sys_gettimeofday(SysTimeval *tv)
EPOCH_JULIAN_DIFF);
}
+extern int erts_initialized;
SysHrTime
sys_gethrtime(void)
{
- DWORD ticks = (SysHrTime) (GetTickCount() & 0x7FFFFFFF);
- if (ticks < (SysHrTime) last_tick_count) {
- wrap += LL_LITERAL(1) << 31;
+ if (pGetTickCount64 != NULL) {
+ return ((SysHrTime) pGetTickCount64()) * LL_LITERAL(1000000);
+ } else {
+ DWORD ticks;
+ SysHrTime res;
+ erts_smp_mtx_lock(&wrap_lock);
+ ticks = (SysHrTime) (GetTickCount() & 0x7FFFFFFF);
+ if (ticks < (SysHrTime) last_tick_count) {
+ /* Detect a race that should no longer be here... */
+ if ((((SysHrTime) last_tick_count) - ((SysHrTime) ticks)) > 1000) {
+ wrap += LL_LITERAL(1) << 31;
+ } else {
+ /*
+ * XXX Debug: Violates locking order, remove all this,
+ * after testing!
+ */
+ erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf();
+ erts_dsprintf(dsbufp, "Did not wrap when last_tick %d "
+ "and tick %d",
+ last_tick_count, ticks);
+ erts_send_error_to_logger_nogl(dsbufp);
+ ticks = last_tick_count;
+ }
+ }
+ last_tick_count = ticks;
+ res = ((((LONGLONG) ticks) + wrap) * LL_LITERAL(1000000));
+ erts_smp_mtx_unlock(&wrap_lock);
+ return res;
}
- last_tick_count = ticks;
- return ((((LONGLONG) ticks) + wrap) * LL_LITERAL(1000000));
}
clock_t
diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl
index e467e844b3..ced8b41c4b 100644
--- a/erts/emulator/test/port_SUITE.erl
+++ b/erts/emulator/test/port_SUITE.erl
@@ -628,7 +628,7 @@ iter_max_ports(Config) when is_list(Config) ->
iter_max_ports_test(Config) ->
- Dog = test_server:timetrap(test_server:minutes(20)),
+ Dog = test_server:timetrap(test_server:minutes(30)),
PortTest = port_test(Config),
Command = lists:concat([PortTest, " -h0 -q"]),
Iters = case os:type() of
diff --git a/erts/emulator/test/trace_SUITE.erl b/erts/emulator/test/trace_SUITE.erl
index 221b65309a..caa58ae281 100644
--- a/erts/emulator/test/trace_SUITE.erl
+++ b/erts/emulator/test/trace_SUITE.erl
@@ -34,6 +34,7 @@
system_monitor_args/1, more_system_monitor_args/1,
system_monitor_long_gc_1/1, system_monitor_long_gc_2/1,
system_monitor_large_heap_1/1, system_monitor_large_heap_2/1,
+ system_monitor_long_schedule/1,
bad_flag/1, trace_delivered/1]).
-include_lib("test_server/include/test_server.hrl").
@@ -52,6 +53,7 @@ all() ->
set_on_first_spawn, system_monitor_args,
more_system_monitor_args, system_monitor_long_gc_1,
system_monitor_long_gc_2, system_monitor_large_heap_1,
+ system_monitor_long_schedule,
system_monitor_large_heap_2, bad_flag, trace_delivered].
groups() ->
@@ -508,6 +510,65 @@ try_l(Val) ->
?line {Self,Comb1} = erlang:system_monitor(undefined),
?line [{large_heap,Val},{long_gc,Arbitrary2}] = lists:sort(Comb1).
+monitor_sys(Parent) ->
+ receive
+ {monitor,Pid,long_schedule,Data} when is_pid(Pid) ->
+ io:format("Long schedule of ~w: ~w~n",[Pid,Data]),
+ Parent ! {Pid,Data},
+ monitor_sys(Parent);
+ {monitor,Port,long_schedule,Data} when is_port(Port) ->
+ {name,Name} = erlang:port_info(Port,name),
+ io:format("Long schedule of ~w (~p): ~w~n",[Port,Name,Data]),
+ Parent ! {Port,Data},
+ monitor_sys(Parent);
+ Other ->
+ erlang:display(Other)
+ end.
+
+start_monitor() ->
+ Parent = self(),
+ Mpid = spawn_link(fun() -> monitor_sys(Parent) end),
+ erlang:system_monitor(Mpid,[{long_schedule,100}]),
+ erlang:yield(), % Need to be rescheduled for the trace to take
+ ok.
+
+system_monitor_long_schedule(suite) ->
+ [];
+system_monitor_long_schedule(doc) ->
+ ["Tests erlang:system_monitor(Pid, [{long_schedule,Time}])"];
+system_monitor_long_schedule(Config) when is_list(Config) ->
+ Path = ?config(data_dir, Config),
+ erl_ddll:start(),
+ case (catch load_driver(Path, slow_drv)) of
+ ok ->
+ do_system_monitor_long_schedule();
+ _Error ->
+ {skip, "Unable to load slow_drv (windows or no usleep()?)"}
+ end.
+do_system_monitor_long_schedule() ->
+ start_monitor(),
+ Port = open_port({spawn_driver,slow_drv}, []),
+ "ok" = erlang:port_control(Port,0,[]),
+ Self = self(),
+ receive
+ {Self,L} when is_list(L) ->
+ ok
+ after 1000 ->
+ ?t:fail(no_trace_of_pid)
+ end,
+ "ok" = erlang:port_control(Port,1,[]),
+ "ok" = erlang:port_control(Port,2,[]),
+ receive
+ {Port,LL} when is_list(LL) ->
+ ok
+ after 1000 ->
+ ?t:fail(no_trace_of_port)
+ end,
+ port_close(Port),
+ erlang:system_monitor(undefined),
+ ok.
+
+
-define(LONG_GC_SLEEP, 670).
system_monitor_long_gc_1(suite) ->
@@ -1521,3 +1582,11 @@ issue_non_empty_runq_warning(DeadLine, RQLen) ->
" Processes info: ~p~n",
[DeadLine div 1000, RQLen, self(), PIs]),
receive after 1000 -> ok end.
+
+load_driver(Dir, Driver) ->
+ case erl_ddll:load_driver(Dir, Driver) of
+ ok -> ok;
+ {error, Error} = Res ->
+ io:format("~s\n", [erl_ddll:format_error(Error)]),
+ Res
+ end.
diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam
index 947fbe7c24..cdc13a656d 100644
--- a/erts/preloaded/ebin/erlang.beam
+++ b/erts/preloaded/ebin/erlang.beam
Binary files differ
diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl
index a8a4425b63..e016a50c4c 100644
--- a/erts/preloaded/src/erlang.erl
+++ b/erts/preloaded/src/erlang.erl
@@ -188,6 +188,7 @@
'busy_port' |
'busy_dist_port' |
{'long_gc', non_neg_integer()} |
+ {'long_schedule', non_neg_integer()} |
{'large_heap', non_neg_integer()}.
@@ -3553,10 +3554,8 @@ sched_wall_time(Ref, N, Acc) ->
{Ref, SWT} -> sched_wall_time(Ref, N-1, [SWT|Acc])
end.
--spec erlang:gather_gc_info_result(Ref) -> [{pos_integer(),
- pos_integer(),
- 0}] when
- Ref :: reference().
+-spec erlang:gather_gc_info_result(Ref) ->
+ {number(),number(),0} when Ref :: reference().
gather_gc_info_result(Ref) when erlang:is_reference(Ref) ->
gc_info(Ref, erlang:system_info(schedulers), {0,0}).