aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--OTP_VERSION2
-rw-r--r--erts/doc/src/erl.xml13
-rw-r--r--erts/doc/src/erl_prim_loader.xml16
-rw-r--r--erts/doc/src/erlang.xml2
-rw-r--r--erts/doc/src/notes.xml19
-rw-r--r--erts/emulator/beam/erl_alloc_util.c9
-rw-r--r--erts/emulator/beam/erl_bif_info.c48
-rw-r--r--erts/emulator/beam/erl_binary.h31
-rw-r--r--erts/emulator/beam/erl_lock_count.c149
-rw-r--r--erts/emulator/beam/erl_lock_count.h20
-rw-r--r--erts/emulator/beam/io.c2
-rw-r--r--erts/emulator/beam/sys.h2
-rw-r--r--erts/preloaded/ebin/erl_prim_loader.beambin54776 -> 56176 bytes
-rw-r--r--erts/preloaded/src/erl_prim_loader.erl38
-rw-r--r--erts/vsn.mk2
-rw-r--r--lib/eunit/src/eunit_data.erl9
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractConnection.java2
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpConnection.java2
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangLong.java4
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangMap.java16
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPid.java2
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangString.java2
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java5
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMbox.java2
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMsg.java4
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java4
-rw-r--r--lib/kernel/doc/src/notes.xml19
-rw-r--r--lib/kernel/src/erl_boot_server.erl6
-rw-r--r--lib/kernel/src/kernel.app.src2
-rw-r--r--lib/kernel/test/erl_prim_loader_SUITE.erl26
-rw-r--r--lib/kernel/vsn.mk2
-rw-r--r--lib/orber/src/orber_iiop.hrl4
-rw-r--r--lib/os_mon/doc/src/disksup.xml10
-rw-r--r--lib/os_mon/src/disksup.erl15
-rw-r--r--lib/os_mon/test/disksup_SUITE.erl39
-rw-r--r--lib/sasl/doc/src/alarm_handler.xml12
-rw-r--r--lib/ssl/src/ssl_handshake.erl12
-rw-r--r--lib/ssl/test/ssl_handshake_SUITE.erl8
-rw-r--r--lib/stdlib/doc/src/notes.xml19
-rw-r--r--lib/stdlib/doc/src/sys.xml5
-rw-r--r--lib/stdlib/src/erl_eval.erl2
-rw-r--r--lib/stdlib/src/filelib.erl14
-rw-r--r--lib/stdlib/src/io_lib_format.erl2
-rw-r--r--lib/stdlib/src/maps.erl6
-rw-r--r--lib/stdlib/src/stdlib.app.src2
-rw-r--r--lib/stdlib/src/stdlib.appup.src6
-rw-r--r--lib/stdlib/src/sys.erl10
-rw-r--r--lib/stdlib/test/filelib_SUITE.erl112
-rw-r--r--lib/stdlib/test/io_SUITE.erl10
-rw-r--r--lib/stdlib/vsn.mk2
-rw-r--r--lib/tools/src/lcnt.erl359
-rw-r--r--lib/tools/test/lcnt_SUITE.erl153
-rw-r--r--lib/tools/test/lcnt_SUITE_data/ehb_3_3_hist.lcntbin0 -> 601135 bytes
-rw-r--r--otp_versions.table1
-rw-r--r--system/doc/efficiency_guide/advanced.xml2
55 files changed, 889 insertions, 376 deletions
diff --git a/OTP_VERSION b/OTP_VERSION
index 6f9c209b3b..59a75133a2 100644
--- a/OTP_VERSION
+++ b/OTP_VERSION
@@ -1 +1 @@
-17.1.1
+17.1.2
diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml
index f8f4d14436..5bde285311 100644
--- a/erts/doc/src/erl.xml
+++ b/erts/doc/src/erl.xml
@@ -851,6 +851,19 @@
</p>
</item>
<tag><marker id="+SDio"><c><![CDATA[+SDio IOSchedulers]]></c></marker></tag>
+ <item>
+ <p>Sets the number of dirty I/O scheduler threads to create when threading
+ support has been enabled. The valid range is 0-1024. By default, the number
+ of dirty I/O scheduler threads created is 10, same as the default number of
+ threads in the <seealso marker="#async_thread_pool_size">async thread pool
+ </seealso>.
+ </p>
+ <p>This option is ignored if the emulator doesn't have threading support
+ enabled. Currently, <em>this option is experimental</em> and is supported only
+ if the emulator was configured and built with support for dirty schedulers
+ enabled (it's disabled by default).
+ </p>
+ </item>
<tag><c><![CDATA[+sFlag Value]]></c></tag>
<item>
<p>Scheduling specific flags.</p>
diff --git a/erts/doc/src/erl_prim_loader.xml b/erts/doc/src/erl_prim_loader.xml
index 6751deda4d..171f84decc 100644
--- a/erts/doc/src/erl_prim_loader.xml
+++ b/erts/doc/src/erl_prim_loader.xml
@@ -148,6 +148,22 @@
</desc>
</func>
<func>
+ <name name="read_link_info" arity="1"/>
+ <fsummary>Get information about a link or file</fsummary>
+ <desc>
+ <p>This function works like
+ <seealso marker="#read_file_info/1">read_file_info/1</seealso>
+ except that if <c><anno>Filename</anno></c> is a symbolic link,
+ information about the link will be returned in the <c>file_info</c>
+ record and the <c>type</c> field of the record will be set to
+ <c>symlink</c>.</p>
+ <p>If <c><anno>Filename</anno></c> is not a symbolic link, this function
+ returns exactly the same result as <c>read_file_info/1</c>.
+ On platforms that do not support symbolic links, this function
+ is always equivalent to <c>read_file_info/1</c>.</p>
+ </desc>
+ </func>
+ <func>
<name name="set_path" arity="1"/>
<fsummary>Set the path of the loader</fsummary>
<desc>
diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml
index 9ad42374bf..84168397f6 100644
--- a/erts/doc/src/erlang.xml
+++ b/erts/doc/src/erlang.xml
@@ -4968,7 +4968,7 @@ true</pre>
<desc>
<p>Note that the run-time is the sum of the run-time for all
threads in the Erlang run-time system and may therefore be greater
- than the wall-clock time.</p>
+ than the wall-clock time. The time is returned in milliseconds.</p>
<pre>
> <input>statistics(runtime).</input>
{1690,1620}
diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml
index 5f39822712..086d29c668 100644
--- a/erts/doc/src/notes.xml
+++ b/erts/doc/src/notes.xml
@@ -30,6 +30,25 @@
</header>
<p>This document describes the changes made to the ERTS application.</p>
+<section><title>Erts 6.1.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ OTP-11850 fixed filelib:wildcard/1 to work with broken
+ symlinks. This correction, however, introduced problems
+ since symlinks were no longer followed for functions like
+ filelib:ensure_dir/1, filelib:is_dir/1,
+ filelib:file_size/1, etc. This is now corrected.</p>
+ <p>
+ Own Id: OTP-12054 Aux Id: seq12660 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Erts 6.1.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c
index 45f0cc4312..a4e164bf51 100644
--- a/erts/emulator/beam/erl_alloc_util.c
+++ b/erts/emulator/beam/erl_alloc_util.c
@@ -3274,6 +3274,15 @@ create_carrier(Allctr_t *allctr, Uint umem_sz, UWord flags)
ASSERT(!(flags & CFLG_FORCE_MSEG && flags & CFLG_FORCE_SYS_ALLOC));
+ if (umem_sz > (ERTS_UINT_MAX - ERTS_UINT_MAX/100)) {
+ /* Do an overly conservative _overflow_ check here so we don't
+ * have to deal with it from here on. I guess we could be more accurate
+ * but I don't think the need to allocate over 99% of the address space
+ * will ever arise on any machine, neither 32 nor 64 bit.
+ */
+ return NULL;
+ }
+
blk_sz = UMEMSZ2BLKSZ(allctr, umem_sz);
#ifdef ERTS_SMP
diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c
index 4d5e55aaf5..6915765dab 100644
--- a/erts/emulator/beam/erl_bif_info.c
+++ b/erts/emulator/beam/erl_bif_info.c
@@ -3856,16 +3856,19 @@ static Eterm lcnt_build_lock_stats_term(Eterm **hpp, Uint *szp, erts_lcnt_lock_s
Uint tries = 0, colls = 0;
unsigned long timer_s = 0, timer_ns = 0, timer_n = 0;
unsigned int line = 0;
+ unsigned int i;
Eterm af, uil;
Eterm uit, uic;
Eterm uits, uitns, uitn;
Eterm tt, tstat, tloc, t;
+ Eterm thist, vhist[ERTS_LCNT_HISTOGRAM_SLOT_SIZE];
/* term:
- * [{{file, line}, {tries, colls, {seconds, nanoseconds, n_blocks}}}]
+ * [{{file, line}, {tries, colls, {seconds, nanoseconds, n_blocks}},
+ * { .. histogram .. }]
*/
-
+
tries = (Uint) ethr_atomic_read(&stats->tries);
colls = (Uint) ethr_atomic_read(&stats->colls);
@@ -3874,23 +3877,27 @@ static Eterm lcnt_build_lock_stats_term(Eterm **hpp, Uint *szp, erts_lcnt_lock_s
timer_ns = stats->timer.ns;
timer_n = stats->timer_n;
- af = erts_atom_put(stats->file, strlen(stats->file), ERTS_ATOM_ENC_LATIN1, 1);
+ af = erts_atom_put((byte *)stats->file, strlen(stats->file), ERTS_ATOM_ENC_LATIN1, 1);
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);
-
+ 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);
+
+ for(i = 0; i < ERTS_LCNT_HISTOGRAM_SLOT_SIZE; i++) {
+ vhist[i] = erts_bld_uint(hpp, szp, stats->hist.ns[i]);
+ }
+ thist = erts_bld_tuplev(hpp, szp, ERTS_LCNT_HISTOGRAM_SLOT_SIZE, vhist);
+
+ t = erts_bld_tuple(hpp, szp, 3, tloc, tstat, thist);
+ res = erts_bld_cons( hpp, szp, t, res);
return res;
}
@@ -3911,13 +3918,13 @@ static Eterm lcnt_build_lock_term(Eterm **hpp, Uint *szp, erts_lcnt_lock_t *lock
ASSERT(ltype);
- type = erts_atom_put(ltype, strlen(ltype), ERTS_ATOM_ENC_LATIN1, 1);
- name = erts_atom_put(lock->name, strlen(lock->name), ERTS_ATOM_ENC_LATIN1, 1);
+ type = erts_atom_put((byte *)ltype, strlen(ltype), ERTS_ATOM_ENC_LATIN1, 1);
+ name = erts_atom_put((byte *)lock->name, strlen(lock->name), ERTS_ATOM_ENC_LATIN1, 1);
if (lock->flag & ERTS_LCNT_LT_ALLOC) {
/* use allocator types names as id's for allocator locks */
ltype = (char *) ERTS_ALC_A2AD(signed_val(lock->id));
- id = erts_atom_put(ltype, strlen(ltype), ERTS_ATOM_ENC_LATIN1, 1);
+ id = erts_atom_put((byte *)ltype, strlen(ltype), ERTS_ATOM_ENC_LATIN1, 1);
} else if (lock->flag & ERTS_LCNT_LT_PROCLOCK) {
/* use registered names as id's for process locks if available */
proc = erts_proc_lookup(lock->id);
@@ -3928,16 +3935,15 @@ static Eterm lcnt_build_lock_term(Eterm **hpp, Uint *szp, erts_lcnt_lock_t *lock
id = lock->id;
}
} else {
- id = lock->id;
+ 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);
+
+ t = erts_bld_tuple(hpp, szp, 4, name, id, type, stats);
+ res = erts_bld_cons( hpp, szp, t, res);
return res;
}
@@ -3957,12 +3963,12 @@ static Eterm lcnt_build_result_term(Eterm **hpp, Uint *szp, erts_lcnt_data_t *da
dtns = erts_bld_uint( hpp, szp, data->duration.ns);
tdt = erts_bld_tuple(hpp, szp, 2, dts, dtns);
- adur = erts_atom_put(str_duration, strlen(str_duration), ERTS_ATOM_ENC_LATIN1, 1);
+ adur = erts_atom_put((byte *)str_duration, strlen(str_duration), ERTS_ATOM_ENC_LATIN1, 1);
tdur = erts_bld_tuple(hpp, szp, 2, adur, tdt);
/* lock tuple */
- aloc = erts_atom_put(str_locks, strlen(str_locks), ERTS_ATOM_ENC_LATIN1, 1);
+ aloc = erts_atom_put((byte *)str_locks, strlen(str_locks), ERTS_ATOM_ENC_LATIN1, 1);
for (lock = data->current_locks->head; lock != NULL ; lock = lock->next ) {
lloc = lcnt_build_lock_term(hpp, szp, lock, lloc);
diff --git a/erts/emulator/beam/erl_binary.h b/erts/emulator/beam/erl_binary.h
index 6c9f53ce87..06dfeb1260 100644
--- a/erts/emulator/beam/erl_binary.h
+++ b/erts/emulator/beam/erl_binary.h
@@ -236,6 +236,8 @@ erts_bin_drv_alloc_fnf(Uint size)
{
Uint bsize = ERTS_SIZEOF_Binary(size) + CHICKEN_PAD;
void *res;
+ if (bsize < size) /* overflow */
+ return NULL;
res = erts_alloc_fnf(ERTS_ALC_T_DRV_BINARY, bsize);
ERTS_CHK_BIN_ALIGNMENT(res);
return (Binary *) res;
@@ -246,6 +248,8 @@ erts_bin_drv_alloc(Uint size)
{
Uint bsize = ERTS_SIZEOF_Binary(size) + CHICKEN_PAD;
void *res;
+ if (bsize < size) /* overflow */
+ erts_alloc_enomem(ERTS_ALC_T_DRV_BINARY, size);
res = erts_alloc(ERTS_ALC_T_DRV_BINARY, bsize);
ERTS_CHK_BIN_ALIGNMENT(res);
return (Binary *) res;
@@ -257,6 +261,8 @@ erts_bin_nrml_alloc(Uint size)
{
Uint bsize = ERTS_SIZEOF_Binary(size) + CHICKEN_PAD;
void *res;
+ if (bsize < size) /* overflow */
+ erts_alloc_enomem(ERTS_ALC_T_BINARY, size);
res = erts_alloc(ERTS_ALC_T_BINARY, bsize);
ERTS_CHK_BIN_ALIGNMENT(res);
return (Binary *) res;
@@ -267,11 +273,12 @@ erts_bin_realloc_fnf(Binary *bp, Uint size)
{
Binary *nbp;
Uint bsize = ERTS_SIZEOF_Binary(size) + CHICKEN_PAD;
+ ErtsAlcType_t type = (bp->flags & BIN_FLAG_DRV) ? ERTS_ALC_T_DRV_BINARY
+ : ERTS_ALC_T_BINARY;
ASSERT((bp->flags & BIN_FLAG_MAGIC) == 0);
- if (bp->flags & BIN_FLAG_DRV)
- nbp = erts_realloc_fnf(ERTS_ALC_T_DRV_BINARY, (void *) bp, bsize);
- else
- nbp = erts_realloc_fnf(ERTS_ALC_T_BINARY, (void *) bp, bsize);
+ if (bsize < size) /* overflow */
+ return NULL;
+ nbp = erts_realloc_fnf(type, (void *) bp, bsize);
ERTS_CHK_BIN_ALIGNMENT(nbp);
return nbp;
}
@@ -281,17 +288,14 @@ erts_bin_realloc(Binary *bp, Uint size)
{
Binary *nbp;
Uint bsize = ERTS_SIZEOF_Binary(size) + CHICKEN_PAD;
+ ErtsAlcType_t type = (bp->flags & BIN_FLAG_DRV) ? ERTS_ALC_T_DRV_BINARY
+ : ERTS_ALC_T_BINARY;
ASSERT((bp->flags & BIN_FLAG_MAGIC) == 0);
- if (bp->flags & BIN_FLAG_DRV)
- nbp = erts_realloc_fnf(ERTS_ALC_T_DRV_BINARY, (void *) bp, bsize);
- else
- nbp = erts_realloc_fnf(ERTS_ALC_T_BINARY, (void *) bp, bsize);
+ if (bsize < size) /* overflow */
+ erts_realloc_enomem(type, bp, size);
+ nbp = erts_realloc_fnf(type, (void *) bp, bsize);
if (!nbp)
- erts_realloc_n_enomem(ERTS_ALC_T2N(bp->flags & BIN_FLAG_DRV
- ? ERTS_ALC_T_DRV_BINARY
- : ERTS_ALC_T_BINARY),
- bp,
- bsize);
+ erts_realloc_enomem(type, bp, bsize);
ERTS_CHK_BIN_ALIGNMENT(nbp);
return nbp;
}
@@ -312,6 +316,7 @@ erts_create_magic_binary(Uint size, void (*destructor)(Binary *))
{
Uint bsize = ERTS_MAGIC_BIN_SIZE(size);
Binary* bptr = erts_alloc_fnf(ERTS_ALC_T_BINARY, bsize);
+ ASSERT(bsize > size);
if (!bptr)
erts_alloc_n_enomem(ERTS_ALC_T2N(ERTS_ALC_T_BINARY), bsize);
ERTS_CHK_BIN_ALIGNMENT(bptr);
diff --git a/erts/emulator/beam/erl_lock_count.c b/erts/emulator/beam/erl_lock_count.c
index 6f44bf097b..17ddfdc8ae 100644
--- a/erts/emulator/beam/erl_lock_count.c
+++ b/erts/emulator/beam/erl_lock_count.c
@@ -61,6 +61,25 @@ static ERTS_INLINE void lcnt_unlock(void) {
ethr_mutex_unlock(&lcnt_data_lock);
}
+const int log2_tab64[64] = {
+ 63, 0, 58, 1, 59, 47, 53, 2,
+ 60, 39, 48, 27, 54, 33, 42, 3,
+ 61, 51, 37, 40, 49, 18, 28, 20,
+ 55, 30, 34, 11, 43, 14, 22, 4,
+ 62, 57, 46, 52, 38, 26, 32, 41,
+ 50, 36, 17, 19, 29, 10, 13, 21,
+ 56, 45, 25, 31, 35, 16, 9, 12,
+ 44, 24, 15, 8, 23, 7, 6, 5};
+
+static ERTS_INLINE int lcnt_log2(Uint64 v) {
+ v |= v >> 1;
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+ v |= v >> 32;
+ return log2_tab64[((Uint64)((v - (v >> 1))*0x07EDD5E59A4E28C2)) >> 58];
+}
static char* lcnt_lock_type(Uint16 flag) {
switch(flag & ERTS_LCNT_LT_ALL) {
@@ -81,19 +100,20 @@ static void lcnt_clear_stats(erts_lcnt_lock_stats_t *stats) {
stats->timer_n = 0;
stats->file = (char *)str_undefined;
stats->line = 0;
+ sys_memzero(stats->hist.ns, sizeof(stats->hist.ns));
}
static void lcnt_time(erts_lcnt_time_t *time) {
-#ifdef HAVE_GETHRTIME
+#if 0 || defined(HAVE_GETHRTIME)
SysHrTime hr_time;
hr_time = sys_gethrtime();
time->s = (unsigned long)(hr_time / 1000000000LL);
time->ns = (unsigned long)(hr_time - 1000000000LL*time->s);
-#else
- SysTimeval tv;
- sys_gettimeofday(&tv);
- time->s = tv.tv_sec;
- time->ns = tv.tv_usec*1000LL;
+#else
+ SysTimeval tv;
+ sys_gettimeofday(&tv);
+ time->s = tv.tv_sec;
+ time->ns = tv.tv_usec*1000LL;
#endif
}
@@ -111,22 +131,20 @@ static void lcnt_time_diff(erts_lcnt_time_t *d, erts_lcnt_time_t *t1, erts_lcnt_
dns += 1000000000LL;
}
+ ASSERT(ds >= 0);
+
d->s = ds;
d->ns = dns;
}
-/* difference d must be positive */
+/* difference d must be non-negative */
static void lcnt_time_add(erts_lcnt_time_t *t, erts_lcnt_time_t *d) {
- unsigned long ngns = 0;
-
t->s += d->s;
t->ns += d->ns;
- ngns = t->ns / 1000000000LL;
+ t->s += t->ns / 1000000000LL;
t->ns = t->ns % 1000000000LL;
-
- t->s += ngns;
}
static erts_lcnt_thread_data_t *lcnt_thread_data_alloc(void) {
@@ -158,59 +176,64 @@ static char* lock_opt(Uint16 flag) {
return "--";
}
-static void print_lock_x(erts_lcnt_lock_t *lock, Uint16 flag, char *action, char *extra) {
- erts_aint_t colls, tries, w_state, r_state;
- erts_lcnt_lock_stats_t *stats = NULL;
-
+static void print_lock_x(erts_lcnt_lock_t *lock, Uint16 flag, char *action) {
+ erts_aint_t w_state, r_state;
char *type;
- int i;
-
+
+ if (strcmp(lock->name, "run_queue") != 0) return;
type = lcnt_lock_type(lock->flag);
r_state = ethr_atomic_read(&lock->r_state);
w_state = ethr_atomic_read(&lock->w_state);
-
if (lock->flag & flag) {
- erts_printf("%20s [%30s] [r/w state %4ld/%4ld] id %T %s\r\n",
- action,
- lock->name,
- r_state,
- w_state,
- lock->id,
- extra);
+ erts_fprintf(stderr,"%10s [%24s] [r/w state %4ld/%4ld] %2s id %T\r\n",
+ action,
+ lock->name,
+ r_state,
+ w_state,
+ type,
+ lock->id);
}
}
-
-static void print_lock(erts_lcnt_lock_t *lock, char *action) {
- if (strcmp(lock->name, "proc_main") == 0) {
- print_lock_x(lock, ERTS_LCNT_LT_ALL, action, "");
- }
-}
-
#endif
static erts_lcnt_lock_stats_t *lcnt_get_lock_stats(erts_lcnt_lock_t *lock, char *file, unsigned int line) {
unsigned int i;
erts_lcnt_lock_stats_t *stats = NULL;
-
- for (i = 0; i < lock->n_stats; i++) {
- if ((lock->stats[i].file == file) && (lock->stats[i].line == line)) {
- return &(lock->stats[i]);
- }
- }
- if (lock->n_stats < ERTS_LCNT_MAX_LOCK_LOCATIONS) {
- stats = &lock->stats[lock->n_stats];
- lock->n_stats++;
- stats->file = file;
- stats->line = line;
- return stats;
+ if (erts_lcnt_rt_options & ERTS_LCNT_OPT_LOCATION) {
+ for (i = 0; i < lock->n_stats; i++) {
+ if ((lock->stats[i].file == file) && (lock->stats[i].line == line)) {
+ return &(lock->stats[i]);
+ }
+ }
+ if (lock->n_stats < ERTS_LCNT_MAX_LOCK_LOCATIONS) {
+ stats = &lock->stats[lock->n_stats];
+ lock->n_stats++;
+ stats->file = file;
+ stats->line = line;
+ return stats;
+ }
}
return &lock->stats[0];
+}
+
+static void lcnt_update_stats_hist(erts_lcnt_hist_t *hist, erts_lcnt_time_t *time_wait) {
+ int idx;
+ unsigned long r;
+ if (time_wait->s > 0 || time_wait->ns > ERTS_LCNT_HISTOGRAM_MAX_NS) {
+ idx = ERTS_LCNT_HISTOGRAM_SLOT_SIZE - 1;
+ } else {
+ r = time_wait->ns >> ERTS_LCNT_HISTOGRAM_RSHIFT;
+ if (r) idx = lcnt_log2(r);
+ else idx = 0;
+ }
+ hist->ns[idx]++;
}
-static void lcnt_update_stats(erts_lcnt_lock_stats_t *stats, int lock_in_conflict, erts_lcnt_time_t *time_wait) {
+static void lcnt_update_stats(erts_lcnt_lock_stats_t *stats, int lock_in_conflict,
+ erts_lcnt_time_t *time_wait) {
ethr_atomic_inc(&stats->tries);
@@ -220,6 +243,7 @@ static void lcnt_update_stats(erts_lcnt_lock_stats_t *stats, int lock_in_conflic
if (time_wait) {
lcnt_time_add(&(stats->timer), time_wait);
stats->timer_n++;
+ lcnt_update_stats_hist(&stats->hist,time_wait);
}
}
@@ -330,8 +354,9 @@ void erts_lcnt_list_delete(erts_lcnt_lock_list_t *list, erts_lcnt_lock_t *lock)
/* interface to erl_threads.h */
/* only lock on init and destroy, all others should use atomics */
void erts_lcnt_init_lock(erts_lcnt_lock_t *lock, char *name, Uint16 flag ) {
- erts_lcnt_init_lock_x(lock, name, flag, am_undefined);
+ erts_lcnt_init_lock_x(lock, name, flag, NIL);
}
+
void erts_lcnt_init_lock_x(erts_lcnt_lock_t *lock, char *name, Uint16 flag, Eterm id) {
int i;
if (!name) {
@@ -360,7 +385,6 @@ void erts_lcnt_init_lock_x(erts_lcnt_lock_t *lock, char *name, Uint16 flag, Eter
}
erts_lcnt_list_insert(erts_lcnt_data->current_locks, lock);
-
lcnt_unlock();
}
@@ -417,8 +441,9 @@ void erts_lcnt_lock_opt(erts_lcnt_lock_t *lock, Uint16 option) {
if ((w_state > 0) || (r_state > 0)) {
eltd->lock_in_conflict = 1;
- if (eltd->timer_set == 0)
+ if (eltd->timer_set == 0) {
lcnt_time(&eltd->timer);
+ }
eltd->timer_set++;
} else {
eltd->lock_in_conflict = 0;
@@ -433,7 +458,7 @@ void erts_lcnt_lock(erts_lcnt_lock_t *lock) {
if (!ERTS_LCNT_LOCK_TYPE(lock)) return;
w_state = ethr_atomic_read(&lock->w_state);
- ethr_atomic_inc( &lock->w_state);
+ ethr_atomic_inc(&lock->w_state);
eltd = lcnt_get_thread_data();
@@ -446,10 +471,10 @@ void erts_lcnt_lock(erts_lcnt_lock_t *lock) {
* 'atomicly'. All other locks will block the thread if w_state > 0
* i.e. locked.
*/
- if (eltd->timer_set == 0)
+ if (eltd->timer_set == 0) {
lcnt_time(&eltd->timer);
+ }
eltd->timer_set++;
-
} else {
eltd->lock_in_conflict = 0;
}
@@ -459,11 +484,10 @@ void erts_lcnt_lock(erts_lcnt_lock_t *lock) {
void erts_lcnt_lock_unaquire(erts_lcnt_lock_t *lock) {
/* should check if this thread was "waiting" */
-
if (erts_lcnt_rt_options & ERTS_LCNT_OPT_SUSPEND) return;
if (!ERTS_LCNT_LOCK_TYPE(lock)) return;
- ethr_atomic_dec( &lock->w_state);
+ ethr_atomic_dec(&lock->w_state);
}
/* erts_lcnt_lock_post
@@ -491,7 +515,7 @@ void erts_lcnt_lock_post_x(erts_lcnt_lock_t *lock, char *file, unsigned int line
if (!(lock->flag & (ERTS_LCNT_LT_RWMUTEX | ERTS_LCNT_LT_RWSPINLOCK))) {
flowstate = ethr_atomic_read(&lock->flowstate);
ASSERT(flowstate == 0);
- ethr_atomic_inc( &lock->flowstate);
+ ethr_atomic_inc(&lock->flowstate);
}
#endif
@@ -500,19 +524,12 @@ void erts_lcnt_lock_post_x(erts_lcnt_lock_t *lock, char *file, unsigned int line
ASSERT(eltd);
/* if lock was in conflict, time it */
-
- if (erts_lcnt_rt_options & ERTS_LCNT_OPT_LOCATION) {
- stats = lcnt_get_lock_stats(lock, file, line);
- } else {
- stats = &lock->stats[0];
- }
-
+ stats = lcnt_get_lock_stats(lock, file, line);
if (eltd->timer_set) {
lcnt_time(&timer);
lcnt_time_diff(&time_wait, &timer, &(eltd->timer));
lcnt_update_stats(stats, eltd->lock_in_conflict, &time_wait);
-
eltd->timer_set--;
ASSERT(eltd->timer_set >= 0);
} else {
@@ -541,11 +558,11 @@ void erts_lcnt_unlock(erts_lcnt_lock_t *lock) {
/* flowstate */
flowstate = ethr_atomic_read(&lock->flowstate);
ASSERT(flowstate == 1);
- ethr_atomic_dec( &lock->flowstate);
+ ethr_atomic_dec(&lock->flowstate);
/* write state */
w_state = ethr_atomic_read(&lock->w_state);
- ASSERT(w_state > 0)
+ ASSERT(w_state > 0);
#endif
ethr_atomic_dec(&lock->w_state);
}
@@ -582,9 +599,7 @@ void erts_lcnt_trylock(erts_lcnt_lock_t *lock, int res) {
ethr_atomic_inc( &lock->flowstate);
#endif
ethr_atomic_inc(&lock->w_state);
-
lcnt_update_stats(&(lock->stats[0]), 0, NULL);
-
} else {
ethr_atomic_inc(&lock->stats[0].tries);
ethr_atomic_inc(&lock->stats[0].colls);
diff --git a/erts/emulator/beam/erl_lock_count.h b/erts/emulator/beam/erl_lock_count.h
index 75f7cd028b..ffbb93da1b 100644
--- a/erts/emulator/beam/erl_lock_count.h
+++ b/erts/emulator/beam/erl_lock_count.h
@@ -35,6 +35,10 @@
* | | | - collisions (including trylock busy)
* | | | - timer (time spent in waiting for lock)
* | | | - n_timer (collisions excluding trylock busy)
+ * | | | - histogram
+ * | | | | - # 0 = log2(lock wait_time ns)
+ * | | | | - ...
+ * | | | | - # n = log2(lock wait_time ns)
*
* Each instance of a lock is the unique lock, i.e. set and id in that set.
* For each lock there is a set of statistics with where and what impact
@@ -68,8 +72,17 @@
#include "ethread.h"
+#define ERTS_LCNT_MAX_LOCK_LOCATIONS (10)
-#define ERTS_LCNT_MAX_LOCK_LOCATIONS (10)
+/* histogram */
+#define ERTS_LCNT_HISTOGRAM_MAX_NS (((unsigned long)1LL << 28) - 1)
+#if 0 || defined(HAVE_GETHRTIME)
+#define ERTS_LCNT_HISTOGRAM_SLOT_SIZE (30)
+#define ERTS_LCNT_HISTOGRAM_RSHIFT (0)
+#else
+#define ERTS_LCNT_HISTOGRAM_SLOT_SIZE (20)
+#define ERTS_LCNT_HISTOGRAM_RSHIFT (10)
+#endif
#define ERTS_LCNT_LT_SPINLOCK (((Uint16) 1) << 0)
#define ERTS_LCNT_LT_RWSPINLOCK (((Uint16) 1) << 1)
@@ -104,6 +117,10 @@ typedef struct {
extern erts_lcnt_time_t timer_start;
+typedef struct {
+ Uint32 ns[ERTS_LCNT_HISTOGRAM_SLOT_SIZE]; /* log2 array of nano seconds occurences */
+} erts_lcnt_hist_t;
+
typedef struct erts_lcnt_lock_stats_s {
/* "tries" and "colls" needs to be atomic since
* trylock busy does not aquire a lock and there
@@ -118,6 +135,7 @@ typedef struct erts_lcnt_lock_stats_s {
unsigned long timer_n; /* #times waited for lock */
erts_lcnt_time_t timer; /* total wait time for lock */
+ erts_lcnt_hist_t hist;
} erts_lcnt_lock_stats_t;
/* rw locks uses both states, other locks only uses w_state */
diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c
index edf4a28784..d028737664 100644
--- a/erts/emulator/beam/io.c
+++ b/erts/emulator/beam/io.c
@@ -6129,7 +6129,7 @@ driver_pdl_create(ErlDrvPort dp)
return NULL;
pdl = erts_alloc(ERTS_ALC_T_PORT_DATA_LOCK,
sizeof(struct erl_drv_port_data_lock));
- erts_mtx_init(&pdl->mtx, "port_data_lock");
+ erts_mtx_init_x(&pdl->mtx, "port_data_lock", pp->common.id, 1);
pdl_init_refc(pdl);
erts_port_inc_refc(pp);
pdl->prt = pp;
diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h
index 05f07e57b2..3d8dd9c6d0 100644
--- a/erts/emulator/beam/sys.h
+++ b/erts/emulator/beam/sys.h
@@ -274,6 +274,7 @@ __decl_noreturn void __noreturn erl_assert_error(const char* expr, const char *f
typedef unsigned int Eterm;
typedef unsigned int Uint;
typedef int Sint;
+#define ERTS_UINT_MAX UINT_MAX
#define ERTS_SIZEOF_ETERM SIZEOF_INT
#define ErtsStrToSint strtol
#else
@@ -347,6 +348,7 @@ typedef long long Sint;
typedef Uint UWord;
typedef Sint SWord;
+#define ERTS_UINT_MAX ERTS_UWORD_MAX
#endif /* HALFWORD_HEAP */
diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam
index eec49f3983..5f2b619322 100644
--- a/erts/preloaded/ebin/erl_prim_loader.beam
+++ b/erts/preloaded/ebin/erl_prim_loader.beam
Binary files differ
diff --git a/erts/preloaded/src/erl_prim_loader.erl b/erts/preloaded/src/erl_prim_loader.erl
index 578913b633..466e0b0020 100644
--- a/erts/preloaded/src/erl_prim_loader.erl
+++ b/erts/preloaded/src/erl_prim_loader.erl
@@ -42,11 +42,11 @@
%% Public
-export([start/3, set_path/1, get_path/0, get_file/1, get_files/2,
- list_dir/1, read_file_info/1, get_cwd/0, get_cwd/1]).
+ list_dir/1, read_file_info/1, read_link_info/1, get_cwd/0, get_cwd/1]).
%% Used by erl_boot_server
-export([prim_init/0, prim_get_file/2, prim_list_dir/2,
- prim_read_file_info/2, prim_get_cwd/2]).
+ prim_read_file_info/3, prim_get_cwd/2]).
%% Used by escript and code
-export([set_primary_archive/4, release_archives/0]).
@@ -223,6 +223,12 @@ list_dir(Dir) ->
read_file_info(File) ->
check_file_result(read_file_info, File, request({read_file_info,File})).
+-spec read_link_info(Filename) -> {'ok', FileInfo} | 'error' when
+ Filename :: string(),
+ FileInfo :: file:file_info().
+read_link_info(File) ->
+ check_file_result(read_link_info, File, request({read_link_info,File})).
+
-spec get_cwd() -> {'ok', string()} | 'error'.
get_cwd() ->
check_file_result(get_cwd, [], request({get_cwd,[]})).
@@ -325,6 +331,9 @@ loop(State, Parent, Paths) ->
{read_file_info,File} ->
{Res,State1} = handle_read_file_info(State, File),
{Res,State1,Paths};
+ {read_link_info,File} ->
+ {Res,State1} = handle_read_link_info(State, File),
+ {Res,State1,Paths};
{get_cwd,[]} ->
{Res,State1} = handle_get_cwd(State, []),
{Res,State1,Paths};
@@ -387,10 +396,15 @@ handle_list_dir(State = #state{loader = inet}, Dir) ->
?SAFE2(inet_list_dir(State, Dir), State).
handle_read_file_info(State = #state{loader = efile}, File) ->
- ?SAFE2(efile_read_file_info(State, File), State);
+ ?SAFE2(efile_read_file_info(State, File, true), State);
handle_read_file_info(State = #state{loader = inet}, File) ->
?SAFE2(inet_read_file_info(State, File), State).
+handle_read_link_info(State = #state{loader = efile}, File) ->
+ ?SAFE2(efile_read_file_info(State, File, false), State);
+handle_read_link_info(State = #state{loader = inet}, File) ->
+ ?SAFE2(inet_read_link_info(State, File), State).
+
handle_get_cwd(State = #state{loader = efile}, Drive) ->
?SAFE2(efile_get_cwd(State, Drive), State);
handle_get_cwd(State = #state{loader = inet}, Drive) ->
@@ -514,8 +528,8 @@ efile_list_dir(#state{prim_state = PS} = State, Dir) ->
{Res, PS2} = prim_list_dir(PS, Dir),
{Res, State#state{prim_state = PS2}}.
-efile_read_file_info(#state{prim_state = PS} = State, File) ->
- {Res, PS2} = prim_read_file_info(PS, File),
+efile_read_file_info(#state{prim_state = PS} = State, File, FollowLinks) ->
+ {Res, PS2} = prim_read_file_info(PS, File, FollowLinks),
{Res, State#state{prim_state = PS2}}.
efile_get_cwd(#state{prim_state = PS} = State, Drive) ->
@@ -718,6 +732,10 @@ inet_list_dir(State, Dir) ->
inet_read_file_info(State, File) ->
inet_send_and_rcv({read_file_info,File}, read_file_info, State).
+%% -> {{ok,Info},State} | {{error,Reason},State}
+inet_read_link_info(State, File) ->
+ inet_send_and_rcv({read_link_info,File}, read_link_info, State).
+
%% -> {{ok,Cwd},State} | {{error,Reason},State}
inet_get_cwd(State, []) ->
inet_send_and_rcv(get_cwd, get_cwd, State);
@@ -951,16 +969,18 @@ prim_list_dir(PS, Dir) ->
debug(PS, {return, Res2}),
{Res2, PS3}.
--spec prim_read_file_info(prim_state(), file:filename()) ->
+-spec prim_read_file_info(prim_state(), file:filename(), boolean()) ->
{{'ok', #file_info{}}, prim_state()}
| {{'error', term()}, prim_state()}.
-prim_read_file_info(PS, File) ->
+prim_read_file_info(PS, File, FollowLinks) ->
debug(PS, {read_file_info, File}),
{Res2, PS2} =
case name_split(PS#prim_state.primary_archive, File) of
{file, PrimFile} ->
- Res = prim_file:read_file_info(PrimFile),
- {Res, PS};
+ case FollowLinks of
+ true -> {prim_file:read_file_info(PrimFile), PS};
+ false -> {prim_file:read_link_info(PrimFile), PS}
+ end;
{archive, ArchiveFile, []} ->
%% Fake top directory
debug(PS, {archive_read_file_info, ArchiveFile}),
diff --git a/erts/vsn.mk b/erts/vsn.mk
index 96edae99d9..0db4370ea8 100644
--- a/erts/vsn.mk
+++ b/erts/vsn.mk
@@ -17,7 +17,7 @@
# %CopyrightEnd%
#
-VSN = 6.1.1
+VSN = 6.1.2
# Port number 4365 in 4.2
# Port number 4366 in 4.3
diff --git a/lib/eunit/src/eunit_data.erl b/lib/eunit/src/eunit_data.erl
index 0350f9bf6e..cbbc6fbc15 100644
--- a/lib/eunit/src/eunit_data.erl
+++ b/lib/eunit/src/eunit_data.erl
@@ -440,13 +440,8 @@ parse_function({M, F}) when is_atom(M), is_atom(F) ->
parse_function(F) ->
bad_test(F).
-check_arity(F, N, T) when is_function(F) ->
- case erlang:fun_info(F, arity) of
- {arity, N} ->
- ok;
- _ ->
- bad_test(T)
- end;
+check_arity(F, N, _) when is_function(F, N) ->
+ ok;
check_arity(_, _, T) ->
bad_test(T).
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractConnection.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractConnection.java
index 9ba6a4a0ab..7aa30eef4a 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractConnection.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractConnection.java
@@ -266,7 +266,7 @@ public abstract class AbstractConnection extends Thread {
*
* @param dest
* the Erlang PID of the remote process.
- * @param msg
+ * @param payload
* the encoded message to send.
*
* @exception java.io.IOException
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpConnection.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpConnection.java
index 8e8bd473c8..e7a9d1092c 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpConnection.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpConnection.java
@@ -404,7 +404,7 @@ public class OtpConnection extends AbstractConnection {
*
* @param dest
* the Erlang PID of the remote process.
- * @param msg
+ * @param payload
* the encoded message to send.
*
* @exception java.io.IOException
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangLong.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangLong.java
index 7e3e2a7296..84b1355c54 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangLong.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangLong.java
@@ -51,8 +51,8 @@ public class OtpErlangLong extends OtpErlangObject implements Serializable,
/**
* Create an Erlang integer from the given value.
*
- * @param val
- * the long value to use.
+ * @param v
+ * the big integer value to use.
*/
public OtpErlangLong(final BigInteger v) {
if (v == null) {
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangMap.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangMap.java
index 03c18e55a2..0254edd5da 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangMap.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangMap.java
@@ -58,15 +58,23 @@ public class OtpErlangMap extends OtpErlangObject implements Serializable,
/**
* Create a map from an array of terms.
*
- * @param elems
+ * @param keys
* the array of terms to create the map from.
- * @param start
- * the offset of the first term to insert.
+ * @param kstart
+ * the offset of the first key to insert.
+ * @param kcount
+ * the number of keys to insert.
+ * @param values
+ * the array of values to create the map from.
+ * @param vstart
+ * the offset of the first value to insert.
* @param vcount
- * the number of terms to insert.
+ * the number of values to insert.
*
* @exception java.lang.IllegalArgumentException
* if any array is empty (null) or contains null elements.
+ * @exception java.lang.IllegalArgumentException
+ * if kcount and vcount differ.
*/
public OtpErlangMap(final OtpErlangObject[] keys, final int kstart,
final int kcount, final OtpErlangObject[] values, final int vstart,
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPid.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPid.java
index fe81ce302d..f75e4353d0 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPid.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPid.java
@@ -162,7 +162,7 @@ public class OtpErlangPid extends OtpErlangObject implements Serializable,
* Determine if two PIDs are equal. PIDs are equal if their components are
* equal.
*
- * @param port
+ * @param o
* the other PID to compare to.
*
* @return true if the PIDs are equal, false otherwise.
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangString.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangString.java
index 6766b52ce5..a5e202c473 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangString.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangString.java
@@ -41,8 +41,6 @@ public class OtpErlangString extends OtpErlangObject implements Serializable,
/**
* Create an Erlang string from a list of integers.
- *
- * @return an Erlang string with Unicode code units.
*
* @throws OtpErlangException
* for non-proper and non-integer lists.
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java
index 0d1342d796..f813594541 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java
@@ -21,6 +21,7 @@ package com.ericsson.otp.erlang;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.math.BigDecimal;
+import java.util.Arrays;
/**
* Provides a stream for decoding Erlang terms from external format.
@@ -819,7 +820,7 @@ public class OtpInputStream extends ByteArrayInputStream {
if (unsigned) {
if (c < 0) {
throw new OtpErlangDecodeException("Value not unsigned: "
- + b);
+ + Arrays.toString(b));
}
while (b[i] == 0) {
i++; // Skip leading zero sign bytes
@@ -844,7 +845,7 @@ public class OtpInputStream extends ByteArrayInputStream {
if (b.length - i > 8) {
// More than 64 bits of value
throw new OtpErlangDecodeException(
- "Value does not fit in long: " + b);
+ "Value does not fit in long: " + Arrays.toString(b));
}
// Convert the necessary bytes
for (v = c < 0 ? -1 : 0; i < b.length; i++) {
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMbox.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMbox.java
index 0fd93b09f4..4a4a1e7f8f 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMbox.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMbox.java
@@ -69,6 +69,7 @@ package com.ericsson.otp.erlang;
* notify other parties in a timely manner.
* </p>
*
+ * <p>
* When retrieving messages from a mailbox that has received an exit signal, an
* {@link OtpErlangExit OtpErlangExit} exception will be raised. Note that the
* exception is queued in the mailbox along with other messages, and will not be
@@ -420,7 +421,6 @@ public class OtpMbox {
/**
* Equivalent to <code>exit(new OtpErlangAtom(reason))</code>.
- * </p>
*
* @see #exit(OtpErlangObject)
*/
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMsg.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMsg.java
index 6f507bf4bb..31a5d0fb8f 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMsg.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMsg.java
@@ -30,14 +30,14 @@ package com.ericsson.otp.erlang;
* </p>
*
* <p>
- * The header information that is available is as follows: <lu>
+ * The header information that is available is as follows: <ul>
* <li> a tag indicating the type of message
* <li> the intended recipient of the message, either as a
* {@link OtpErlangPid pid} or as a String, but never both.
* <li> (sometimes) the sender of the message. Due to some eccentric
* characteristics of the Erlang distribution protocol, not all messages have
* information about the sending process. In particular, only messages whose tag
- * is {@link OtpMsg#regSendTag regSendTag} contain sender information. </lu>
+ * is {@link OtpMsg#regSendTag regSendTag} contain sender information. </ul>
*
* <p>
* Message are sent using the Erlang external format (see separate
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java
index a78423db44..c98790bbd4 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java
@@ -202,7 +202,7 @@ public class OtpOutputStream extends ByteArrayOutputStream {
/**
* Write an array of bytes to the stream.
*
- * @param buf
+ * @param bytes
* the array of bytes to write.
*
*/
@@ -637,7 +637,7 @@ public class OtpOutputStream extends ByteArrayOutputStream {
* Write a positive short to the stream. The short is interpreted as a two's
* complement unsigned short even if it is negative.
*
- * @param s
+ * @param us
* the short to use.
*/
public void write_ushort(final short us) {
diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml
index 3a8de841d0..35889f9d11 100644
--- a/lib/kernel/doc/src/notes.xml
+++ b/lib/kernel/doc/src/notes.xml
@@ -30,6 +30,25 @@
</header>
<p>This document describes the changes made to the Kernel application.</p>
+<section><title>Kernel 3.0.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ OTP-11850 fixed filelib:wildcard/1 to work with broken
+ symlinks. This correction, however, introduced problems
+ since symlinks were no longer followed for functions like
+ filelib:ensure_dir/1, filelib:is_dir/1,
+ filelib:file_size/1, etc. This is now corrected.</p>
+ <p>
+ Own Id: OTP-12054 Aux Id: seq12660 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Kernel 3.0.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/kernel/src/erl_boot_server.erl b/lib/kernel/src/erl_boot_server.erl
index 9a49655a9f..ef09d86ca4 100644
--- a/lib/kernel/src/erl_boot_server.erl
+++ b/lib/kernel/src/erl_boot_server.erl
@@ -341,9 +341,13 @@ handle_command(S, PS, Msg) ->
send_file_result(S, list_dir, Res),
PS2;
{read_file_info,File} ->
- {Res, PS2} = erl_prim_loader:prim_read_file_info(PS, File),
+ {Res, PS2} = erl_prim_loader:prim_read_file_info(PS, File, true),
send_file_result(S, read_file_info, Res),
PS2;
+ {read_link_info,File} ->
+ {Res, PS2} = erl_prim_loader:prim_read_file_info(PS, File, false),
+ send_file_result(S, read_link_info, Res),
+ PS2;
get_cwd ->
{Res, PS2} = erl_prim_loader:prim_get_cwd(PS, []),
send_file_result(S, get_cwd, Res),
diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src
index 5658c6b6cf..9f6c0f4624 100644
--- a/lib/kernel/src/kernel.app.src
+++ b/lib/kernel/src/kernel.app.src
@@ -115,6 +115,6 @@
{applications, []},
{env, [{error_logger, tty}]},
{mod, {kernel, []}},
- {runtime_dependencies, ["erts-6.0", "stdlib-2.0", "sasl-2.4"]}
+ {runtime_dependencies, ["erts-6.1.2", "stdlib-2.0", "sasl-2.4"]}
]
}.
diff --git a/lib/kernel/test/erl_prim_loader_SUITE.erl b/lib/kernel/test/erl_prim_loader_SUITE.erl
index b2ca3bdbc2..658c31c14d 100644
--- a/lib/kernel/test/erl_prim_loader_SUITE.erl
+++ b/lib/kernel/test/erl_prim_loader_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. 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
@@ -328,6 +328,30 @@ file_requests(Config) when is_list(Config) ->
{ok,Info} = file:read_file_info(code:which(test_server)),
?line {ok,Info} = rpc:call(Node, erl_prim_loader, read_file_info,
[code:which(test_server)]),
+
+ PrivDir = ?config(priv_dir,Config),
+ Dir = filename:join(PrivDir,?MODULE_STRING++"_file_requests"),
+ ok = file:make_dir(Dir),
+ Alias = filename:join(Dir,"symlink"),
+ case file:make_symlink(code:which(test_server), Alias) of
+ {error, enotsup} ->
+ %% Links not supported on this platform
+ ok;
+ {error, eperm} ->
+ {win32,_} = os:type(),
+ %% Windows user not privileged to create symlinks"
+ ok;
+ ok ->
+ %% Reading file info for link should return file info for
+ %% link target
+ {ok,Info} = rpc:call(Node, erl_prim_loader, read_file_info,
+ [Alias]),
+ #file_info{type=regular} = Info,
+ {ok,#file_info{type=symlink}} =
+ rpc:call(Node, erl_prim_loader, read_link_info,
+ [Alias])
+ end,
+
{ok,Cwd} = file:get_cwd(),
?line {ok,Cwd} = rpc:call(Node, erl_prim_loader, get_cwd, []),
case file:get_cwd("C:") of
diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk
index aebe28655e..1ecfde190e 100644
--- a/lib/kernel/vsn.mk
+++ b/lib/kernel/vsn.mk
@@ -1 +1 @@
-KERNEL_VSN = 3.0.1
+KERNEL_VSN = 3.0.2
diff --git a/lib/orber/src/orber_iiop.hrl b/lib/orber/src/orber_iiop.hrl
index 7a30af63e4..b2e970b30d 100644
--- a/lib/orber/src/orber_iiop.hrl
+++ b/lib/orber/src/orber_iiop.hrl
@@ -467,14 +467,14 @@
[{"iiop_version",?IIOP_VERSION },
{"host", {'tk_string', 0}},
{"port", 'tk_ushort'},
- {"object_key", {'tk_sequence', 'tk_octet', 0}}
+ {"object_key", {'tk_sequence', 'tk_octet', 0}},
{"components", ?IOP_TAGGEDCOMPONENT_SEQ}]}).
-define(PROFILEBODY_1_2_TYPEDEF, {'tk_struct', ?SYSTEM_TYPE, 'IIOP_ProfileBody_1_1',
[{"iiop_version",?IIOP_VERSION },
{"host", {'tk_string', 0}},
{"port", 'tk_ushort'},
- {"object_key", {'tk_sequence', 'tk_octet', 0}}
+ {"object_key", {'tk_sequence', 'tk_octet', 0}},
{"components", ?IOP_TAGGEDCOMPONENT_SEQ}]}).
-define(SSLIOP_SSL, {'tk_struct', ?SYSTEM_TYPE, 'SSLIOP_SSL',
diff --git a/lib/os_mon/doc/src/disksup.xml b/lib/os_mon/doc/src/disksup.xml
index dbcfd65095..c8566d0126 100644
--- a/lib/os_mon/doc/src/disksup.xml
+++ b/lib/os_mon/doc/src/disksup.xml
@@ -73,6 +73,16 @@
much disk can be utilized before the <c>disk_almost_full</c>
alarm is set. The default is 0.80 (80%).</p>
</item>
+ <tag><c>disksup_posix_only = bool()</c></tag>
+ <item>
+ <p>Specifies whether the <c>disksup</c> helper process should only
+ use POSIX conformant commands (<c>true</c>) or not. The default is
+ <c>false</c>. Setting this parameter to <c>true</c> can be
+ necessary on embedded systems with stripped-down versions
+ of Unix tools like <c>df</c>. The returned disk data and alarms
+ can be different when using this option.</p>
+ <p>The parameter is ignored on Windows.</p>
+ </item>
</taglist>
<p>See <seealso marker="kernel:config">config(4)</seealso> for
information about how to change the value of configuration
diff --git a/lib/os_mon/src/disksup.erl b/lib/os_mon/src/disksup.erl
index 278da26a20..3fd80f2365 100644
--- a/lib/os_mon/src/disksup.erl
+++ b/lib/os_mon/src/disksup.erl
@@ -81,10 +81,12 @@ param_type(disk_space_check_interval, Val) when is_integer(Val),
param_type(disk_almost_full_threshold, Val) when is_number(Val),
0=<Val,
Val=<1 -> true;
+param_type(disksup_posix_only, Val) when Val==true; Val==false -> true;
param_type(_Param, _Val) -> false.
param_default(disk_space_check_interval) -> 30;
-param_default(disk_almost_full_threshold) -> 0.80.
+param_default(disk_almost_full_threshold) -> 0.80;
+param_default(disksup_posix_only) -> false.
%%----------------------------------------------------------------------
%% gen_server callbacks
@@ -94,7 +96,8 @@ init([]) ->
process_flag(trap_exit, true),
process_flag(priority, low),
- OS = get_os(),
+ PosixOnly = os_mon:get_env(disksup, disksup_posix_only),
+ OS = get_os(PosixOnly),
Port = case OS of
{unix, Flavor} when Flavor==sunos4;
Flavor==solaris;
@@ -102,6 +105,7 @@ init([]) ->
Flavor==dragonfly;
Flavor==darwin;
Flavor==linux;
+ Flavor==posix;
Flavor==openbsd;
Flavor==netbsd;
Flavor==irix64;
@@ -205,8 +209,10 @@ format_status(_Opt, [_PDict, #state{os = OS, threshold = Threshold,
%% Internal functions
%%----------------------------------------------------------------------
-get_os() ->
+get_os(PosixOnly) ->
case os:type() of
+ {unix, _} when PosixOnly ->
+ {unix, posix};
{unix, sunos} ->
case os:version() of
{5,_,_} -> {unix, solaris};
@@ -259,6 +265,9 @@ check_disk_space({unix, irix}, Port, Threshold) ->
check_disk_space({unix, linux}, Port, Threshold) ->
Result = my_cmd("/bin/df -lk", Port),
check_disks_solaris(skip_to_eol(Result), Threshold);
+check_disk_space({unix, posix}, Port, Threshold) ->
+ Result = my_cmd("df -k -P", Port),
+ check_disks_solaris(skip_to_eol(Result), Threshold);
check_disk_space({unix, dragonfly}, Port, Threshold) ->
Result = my_cmd("/bin/df -k -t ufs,hammer", Port),
check_disks_solaris(skip_to_eol(Result), Threshold);
diff --git a/lib/os_mon/test/disksup_SUITE.erl b/lib/os_mon/test/disksup_SUITE.erl
index 94661cfa77..2f62564959 100644
--- a/lib/os_mon/test/disksup_SUITE.erl
+++ b/lib/os_mon/test/disksup_SUITE.erl
@@ -29,6 +29,7 @@
-export([port/1]).
-export([terminate/1, unavailable/1, restart/1]).
-export([otp_5910/1]).
+-export([posix_only/1]).
%% Default timetrap timeout (set in init_per_testcase)
-define(default_timeout, ?t:minutes(1)).
@@ -62,9 +63,9 @@ all() ->
Bugs = [otp_5910],
case test_server:os_type() of
{unix, sunos} ->
- [api, config, alarm, port, unavailable] ++ Bugs;
- {unix, _OSname} -> [api, alarm] ++ Bugs;
- {win32, _OSname} -> [api, alarm] ++ Bugs;
+ [api, config, alarm, port, unavailable, posix_only] ++ Bugs;
+ {unix, _OSname} -> [api, alarm, posix_only] ++ Bugs;
+ {win32, _OSname} -> [api, alarm, posix_only] ++ Bugs;
_OS -> [unavailable]
end.
@@ -83,12 +84,7 @@ api(doc) -> ["Test of API functions"];
api(Config) when is_list(Config) ->
%% get_disk_data()
- [{Id,KByte,Capacity}|_] = get_disk_data(),
- true = io_lib:printable_list(Id),
- true = is_integer(KByte),
- true = is_integer(Capacity),
- true = Capacity>0,
- true = KByte>0,
+ ok = check_get_disk_data(),
%% get_check_interval()
1800000 = disksup:get_check_interval(),
@@ -405,9 +401,34 @@ otp_5910(Config) when is_list(Config) ->
ok = application:start(os_mon),
ok.
+posix_only(suite) -> [];
+posix_only(doc) -> ["Test disksup_posix_only option"];
+posix_only(Config) when is_list(Config) ->
+ %% Set option and restart disksup
+ ok = application:set_env(os_mon, disksup_posix_only, true),
+ ok = supervisor:terminate_child(os_mon_sup, disksup),
+ {ok, _Child1} = supervisor:restart_child(os_mon_sup, disksup),
+
+ ok = check_get_disk_data(),
+
+ %% Reset option and restart disksup
+ ok = application:set_env(os_mon, disksup_posix_only, false),
+ ok = supervisor:terminate_child(os_mon_sup, disksup),
+ {ok, _Child2} = supervisor:restart_child(os_mon_sup, disksup),
+ ok.
+
dump_info() ->
io:format("Status: ~p~n", [sys:get_status(disksup)]).
+check_get_disk_data() ->
+ [{Id,KByte,Capacity}|_] = get_disk_data(),
+ true = io_lib:printable_list(Id),
+ true = is_integer(KByte),
+ true = is_integer(Capacity),
+ true = Capacity>0,
+ true = KByte>0,
+ ok.
+
% filter get_disk_data and remove entriew with zero capacity
% "non-normal" filesystems report zero capacity
% - Perhaps errorneous 'df -k -l'?
diff --git a/lib/sasl/doc/src/alarm_handler.xml b/lib/sasl/doc/src/alarm_handler.xml
index ab3041137e..e4def7c7f5 100644
--- a/lib/sasl/doc/src/alarm_handler.xml
+++ b/lib/sasl/doc/src/alarm_handler.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1996</year>
- <year>2013</year>
+ <year>2014</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -87,7 +87,9 @@
<v>AlarmId = term()</v>
</type>
<desc>
- <p>Clears all alarms with id <c>AlarmId</c>.
+ <p>Sends the <c>clear_alarm</c> event to all event handlers.</p>
+ <p>When receiving this event, the default simple handler
+ clears the latest received alarm with id <c>AlarmId</c>.
</p>
</desc>
</func>
@@ -109,8 +111,10 @@
<v>AlarmDescription = term()</v>
</type>
<desc>
- <p>Sets an alarm with id <c>AlarmId</c>. This id is used at a
- later stage when the alarm is cleared.
+ <p>Sends the <c>set_alarm</c> event to all event handlers.</p>
+ <p>When receiving this event, the default simple handler
+ stores the alarm. The <c>AlarmId</c> identifies the alarm
+ and is used when the alarm is cleared.
</p>
</desc>
</func>
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index fc67d2c28d..b018332df1 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -1719,6 +1719,11 @@ dec_hello_extensions(<<?UINT16(?EC_POINT_FORMATS_EXT), ?UINT16(Len),
dec_hello_extensions(Rest, Acc#hello_extensions{ec_point_formats =
#ec_point_formats{ec_point_format_list =
ECPointFormats}});
+
+dec_hello_extensions(<<?UINT16(?SNI_EXT), ?UINT16(Len),
+ ExtData:Len/binary, Rest/binary>>, Acc) ->
+ <<?UINT16(_), NameList/binary>> = ExtData,
+ dec_hello_extensions(Rest, Acc#hello_extensions{sni = dec_sni(NameList)});
%% Ignore data following the ClientHello (i.e.,
%% extensions) if not understood.
@@ -1731,6 +1736,13 @@ dec_hello_extensions(_, Acc) ->
dec_hashsign(<<?BYTE(HashAlgo), ?BYTE(SignAlgo)>>) ->
{ssl_cipher:hash_algorithm(HashAlgo), ssl_cipher:sign_algorithm(SignAlgo)}.
+%% Ignore unknown names (only host_name is supported)
+dec_sni(<<?BYTE(?SNI_NAMETYPE_HOST_NAME), ?UINT16(Len),
+ HostName:Len/binary, _/binary>>) ->
+ #sni{hostname = binary_to_list(HostName)};
+dec_sni(<<?BYTE(_), ?UINT16(Len), _:Len, Rest/binary>>) -> dec_sni(Rest);
+dec_sni(_) -> undefined.
+
decode_next_protocols({next_protocol_negotiation, Protocols}) ->
decode_next_protocols(Protocols, []).
decode_next_protocols(<<>>, Acc) ->
diff --git a/lib/ssl/test/ssl_handshake_SUITE.erl b/lib/ssl/test/ssl_handshake_SUITE.erl
index 5f36842f9e..e5e942ce1b 100644
--- a/lib/ssl/test/ssl_handshake_SUITE.erl
+++ b/lib/ssl/test/ssl_handshake_SUITE.erl
@@ -38,6 +38,7 @@ all() -> [decode_hello_handshake,
decode_supported_elliptic_curves_hello_extension_correctly,
decode_unknown_hello_extension_correctly,
encode_single_hello_sni_extension_correctly,
+ decode_single_hello_sni_extension_correctly,
select_proper_tls_1_2_rsa_default_hashsign].
%%--------------------------------------------------------------------
@@ -98,6 +99,13 @@ encode_single_hello_sni_extension_correctly(_Config) ->
Encoded = ssl_handshake:encode_hello_extensions(Exts),
HelloExt = Encoded.
+decode_single_hello_sni_extension_correctly(_Config) ->
+ Exts = #hello_extensions{sni = #sni{hostname = "test.com"}},
+ SNI = <<16#00, 16#00, 16#00, 16#0d, 16#00, 16#0b, 16#00, 16#00, 16#08,
+ $t, $e, $s, $t, $., $c, $o, $m>>,
+ Decoded = ssl_handshake:decode_hello_extensions(SNI),
+ Exts = Decoded.
+
select_proper_tls_1_2_rsa_default_hashsign(_Config) ->
% RFC 5246 section 7.4.1.4.1 tells to use {sha1,rsa} as default signature_algorithm for RSA key exchanges
{sha, rsa} = ssl_handshake:select_hashsign_algs(undefined, ?rsaEncryption, {3,3}),
diff --git a/lib/stdlib/doc/src/notes.xml b/lib/stdlib/doc/src/notes.xml
index 0421d560b6..5e74616099 100644
--- a/lib/stdlib/doc/src/notes.xml
+++ b/lib/stdlib/doc/src/notes.xml
@@ -30,6 +30,25 @@
</header>
<p>This document describes the changes made to the STDLIB application.</p>
+<section><title>STDLIB 2.1.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ OTP-11850 fixed filelib:wildcard/1 to work with broken
+ symlinks. This correction, however, introduced problems
+ since symlinks were no longer followed for functions like
+ filelib:ensure_dir/1, filelib:is_dir/1,
+ filelib:file_size/1, etc. This is now corrected.</p>
+ <p>
+ Own Id: OTP-12054 Aux Id: seq12660 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>STDLIB 2.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/stdlib/doc/src/sys.xml b/lib/stdlib/doc/src/sys.xml
index a46fa1289f..19605f325b 100644
--- a/lib/stdlib/doc/src/sys.xml
+++ b/lib/stdlib/doc/src/sys.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2013</year>
+ <year>1996</year><year>2014</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -115,6 +115,9 @@
<datatype>
<name name="dbg_fun"/>
</datatype>
+ <datatype>
+ <name name="format_fun"/>
+ </datatype>
</datatypes>
<funcs>
<func>
diff --git a/lib/stdlib/src/erl_eval.erl b/lib/stdlib/src/erl_eval.erl
index 3cfedfee97..639ddfc214 100644
--- a/lib/stdlib/src/erl_eval.erl
+++ b/lib/stdlib/src/erl_eval.erl
@@ -77,7 +77,7 @@
%% Only exprs/2 checks the command by calling erl_lint. The reason is
%% that if there is a function handler present, then it is possible
%% that there are valid constructs in Expression to be taken care of
-%% by a function handler but considerad errors by erl_lint.
+%% by a function handler but considered errors by erl_lint.
-spec(exprs(Expressions, Bindings) -> {value, Value, NewBindings} when
Expressions :: expressions(),
diff --git a/lib/stdlib/src/filelib.erl b/lib/stdlib/src/filelib.erl
index c0921e4cf1..9efbe8da20 100644
--- a/lib/stdlib/src/filelib.erl
+++ b/lib/stdlib/src/filelib.erl
@@ -265,7 +265,7 @@ do_wildcard(Pattern, Cwd, Mod) ->
lists:sort(Files).
do_wildcard_1({exists,File}, Mod) ->
- case eval_read_file_info(File, Mod) of
+ case eval_read_link_info(File, Mod) of
{ok,_} -> [File];
_ -> []
end;
@@ -488,7 +488,7 @@ badpattern(Reason) ->
error({badpattern,Reason}).
eval_read_file_info(File, file) ->
- file:read_link_info(File);
+ file:read_file_info(File);
eval_read_file_info(File, erl_prim_loader) ->
case erl_prim_loader:read_file_info(File) of
error -> {error, erl_prim_loader};
@@ -497,6 +497,16 @@ eval_read_file_info(File, erl_prim_loader) ->
eval_read_file_info(File, Mod) ->
Mod:read_file_info(File).
+eval_read_link_info(File, file) ->
+ file:read_link_info(File);
+eval_read_link_info(File, erl_prim_loader) ->
+ case erl_prim_loader:read_link_info(File) of
+ error -> {error, erl_prim_loader};
+ Res-> Res
+ end;
+eval_read_link_info(File, Mod) ->
+ Mod:read_link_info(File).
+
eval_list_dir(Dir, file) ->
file:list_dir(Dir);
eval_list_dir(Dir, erl_prim_loader) ->
diff --git a/lib/stdlib/src/io_lib_format.erl b/lib/stdlib/src/io_lib_format.erl
index 56e15a17ec..89ae6fb187 100644
--- a/lib/stdlib/src/io_lib_format.erl
+++ b/lib/stdlib/src/io_lib_format.erl
@@ -255,7 +255,7 @@ term(T, none, _Adj, none, _Pad) -> T;
term(T, none, Adj, P, Pad) -> term(T, P, Adj, P, Pad);
term(T, F, Adj, P0, Pad) ->
L = lists:flatlength(T),
- P = case P0 of none -> erlang:min(L, F); _ -> P0 end,
+ P = erlang:min(L, case P0 of none -> F; _ -> min(P0, F) end),
if
L > P ->
adjust(chars($*, P), chars(Pad, F-P), Adj);
diff --git a/lib/stdlib/src/maps.erl b/lib/stdlib/src/maps.erl
index 4ef1638e6d..3f019aa35a 100644
--- a/lib/stdlib/src/maps.erl
+++ b/lib/stdlib/src/maps.erl
@@ -133,10 +133,10 @@ to_list(_) -> erlang:nif_error(undef).
update(_,_,_) -> erlang:nif_error(undef).
--spec values(Map) -> Keys when
+-spec values(Map) -> Values when
Map :: map(),
- Keys :: [Key],
- Key :: term().
+ Values :: [Value],
+ Value :: term().
values(_) -> erlang:nif_error(undef).
diff --git a/lib/stdlib/src/stdlib.app.src b/lib/stdlib/src/stdlib.app.src
index d388410de0..3585eec342 100644
--- a/lib/stdlib/src/stdlib.app.src
+++ b/lib/stdlib/src/stdlib.app.src
@@ -103,7 +103,7 @@
dets]},
{applications, [kernel]},
{env, []},
- {runtime_dependencies, ["sasl-2.4","kernel-3.0","erts-6.0","crypto-3.3",
+ {runtime_dependencies, ["sasl-2.4","kernel-3.0.2","erts-6.1.2","crypto-3.3",
"compiler-5.0"]}
]}.
diff --git a/lib/stdlib/src/stdlib.appup.src b/lib/stdlib/src/stdlib.appup.src
index 22eefb2514..99d9b8b431 100644
--- a/lib/stdlib/src/stdlib.appup.src
+++ b/lib/stdlib/src/stdlib.appup.src
@@ -17,9 +17,11 @@
%% %CopyrightEnd%
{"%VSN%",
%% Up from - max one major revision back
- [{<<"2\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R17
+ [{<<"2\\.1(\\.[0-9]+)*">>,[restart_new_emulator]}, %% 17.1
+ {<<"2\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, %% 17.0
{<<"1\\.19(\\.[0-9]+)*">>,[restart_new_emulator]}],%% R16
%% Down to - max one major revision back
- [{<<"2\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R17
+ [{<<"2\\.1(\\.[0-9]+)*">>,[restart_new_emulator]}, %% 17.1
+ {<<"2\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, %% 17.0
{<<"1\\.19(\\.[0-9]+)*">>,[restart_new_emulator]}] %% R16
}.
diff --git a/lib/stdlib/src/sys.erl b/lib/stdlib/src/sys.erl
index e25cc25f57..d3ba09ce82 100644
--- a/lib/stdlib/src/sys.erl
+++ b/lib/stdlib/src/sys.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2014. 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
@@ -46,7 +46,7 @@
{N :: non_neg_integer(),
[{Event :: system_event(),
FuncState :: _,
- FormFunc :: dbg_fun()}]}}
+ FormFunc :: format_fun()}]}}
| {'statistics', {file:date_time(),
{'reductions', non_neg_integer()},
MessagesIn :: non_neg_integer(),
@@ -57,6 +57,10 @@
Event :: system_event(),
ProcState :: _) -> 'done' | (NewFuncState :: _)).
+-type format_fun() :: fun((Device :: io:device() | file:io_device(),
+ Event :: system_event(),
+ Extra :: term()) -> any()).
+
%%-----------------------------------------------------------------
%% System messages
%%-----------------------------------------------------------------
@@ -346,7 +350,7 @@ handle_system_msg(SysState, Msg, From, Parent, Mod, Debug, Misc, Hib) ->
%%-----------------------------------------------------------------
-spec handle_debug(Debug, FormFunc, Extra, Event) -> [dbg_opt()] when
Debug :: [dbg_opt()],
- FormFunc :: dbg_fun(),
+ FormFunc :: format_fun(),
Extra :: term(),
Event :: system_event().
handle_debug([{trace, true} | T], FormFunc, State, Event) ->
diff --git a/lib/stdlib/test/filelib_SUITE.erl b/lib/stdlib/test/filelib_SUITE.erl
index 8203a03a7a..040ae1effc 100644
--- a/lib/stdlib/test/filelib_SUITE.erl
+++ b/lib/stdlib/test/filelib_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2014. 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
@@ -23,7 +23,8 @@
init_per_group/2,end_per_group/2,
init_per_testcase/2,end_per_testcase/2,
wildcard_one/1,wildcard_two/1,wildcard_errors/1,
- fold_files/1,otp_5960/1,ensure_dir_eexist/1,symlinks/1]).
+ fold_files/1,otp_5960/1,ensure_dir_eexist/1,ensure_dir_symlink/1,
+ wildcard_symlink/1, is_file_symlink/1, file_props_symlink/1]).
-import(lists, [foreach/2]).
@@ -43,7 +44,8 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[wildcard_one, wildcard_two, wildcard_errors,
- fold_files, otp_5960, ensure_dir_eexist, symlinks].
+ fold_files, otp_5960, ensure_dir_eexist, ensure_dir_symlink,
+ wildcard_symlink, is_file_symlink, file_props_symlink].
groups() ->
[].
@@ -367,9 +369,28 @@ ensure_dir_eexist(Config) when is_list(Config) ->
?line {error, eexist} = filelib:ensure_dir(NeedFileB),
ok.
-symlinks(Config) when is_list(Config) ->
+ensure_dir_symlink(Config) when is_list(Config) ->
PrivDir = ?config(priv_dir, Config),
- Dir = filename:join(PrivDir, ?MODULE_STRING++"_symlinks"),
+ Dir = filename:join(PrivDir, "ensure_dir_symlink"),
+ Name = filename:join(Dir, "same_name_as_file_and_dir"),
+ ok = filelib:ensure_dir(Name),
+ ok = file:write_file(Name, <<"some string\n">>),
+ %% With a symlink to the directory.
+ Symlink = filename:join(PrivDir, "ensure_dir_symlink_link"),
+ case file:make_symlink(Dir, Symlink) of
+ {error,enotsup} ->
+ {skip,"Symlinks not supported on this platform"};
+ {error,eperm} ->
+ {win32,_} = os:type(),
+ {skip,"Windows user not privileged to create symlinks"};
+ ok ->
+ SymlinkedName = filename:join(Symlink, "same_name_as_file_and_dir"),
+ ok = filelib:ensure_dir(SymlinkedName)
+ end.
+
+wildcard_symlink(Config) when is_list(Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ Dir = filename:join(PrivDir, ?MODULE_STRING++"_wildcard_symlink"),
SubDir = filename:join(Dir, "sub"),
AFile = filename:join(SubDir, "a_file"),
Alias = filename:join(Dir, "symlink"),
@@ -387,6 +408,18 @@ symlinks(Config) when is_list(Config) ->
basenames(Dir, filelib:wildcard(filename:join(Dir, "*"))),
["symlink"] =
basenames(Dir, filelib:wildcard(filename:join(Dir, "symlink"))),
+ ["sub","symlink"] =
+ basenames(Dir, filelib:wildcard(filename:join(Dir, "*"),
+ erl_prim_loader)),
+ ["symlink"] =
+ basenames(Dir, filelib:wildcard(filename:join(Dir, "symlink"),
+ erl_prim_loader)),
+ ["sub","symlink"] =
+ basenames(Dir, filelib:wildcard(filename:join(Dir, "*"),
+ prim_file)),
+ ["symlink"] =
+ basenames(Dir, filelib:wildcard(filename:join(Dir, "symlink"),
+ prim_file)),
ok = file:delete(AFile),
%% The symlink should still be visible even when its target
%% has been deleted.
@@ -394,6 +427,18 @@ symlinks(Config) when is_list(Config) ->
basenames(Dir, filelib:wildcard(filename:join(Dir, "*"))),
["symlink"] =
basenames(Dir, filelib:wildcard(filename:join(Dir, "symlink"))),
+ ["sub","symlink"] =
+ basenames(Dir, filelib:wildcard(filename:join(Dir, "*"),
+ erl_prim_loader)),
+ ["symlink"] =
+ basenames(Dir, filelib:wildcard(filename:join(Dir, "symlink"),
+ erl_prim_loader)),
+ ["sub","symlink"] =
+ basenames(Dir, filelib:wildcard(filename:join(Dir, "*"),
+ prim_file)),
+ ["symlink"] =
+ basenames(Dir, filelib:wildcard(filename:join(Dir, "symlink"),
+ prim_file)),
ok
end.
@@ -402,3 +447,60 @@ basenames(Dir, Files) ->
Dir = filename:dirname(F),
filename:basename(F)
end || F <- Files].
+
+is_file_symlink(Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ Dir = filename:join(PrivDir, ?MODULE_STRING++"_is_file_symlink"),
+ SubDir = filename:join(Dir, "sub"),
+ AFile = filename:join(SubDir, "a_file"),
+ DirAlias = filename:join(Dir, "dir_symlink"),
+ FileAlias = filename:join(Dir, "file_symlink"),
+ ok = file:make_dir(Dir),
+ ok = file:make_dir(SubDir),
+ ok = file:write_file(AFile, "not that big\n"),
+ case file:make_symlink(SubDir, DirAlias) of
+ {error, enotsup} ->
+ {skip, "Links not supported on this platform"};
+ {error, eperm} ->
+ {win32,_} = os:type(),
+ {skip, "Windows user not privileged to create symlinks"};
+ ok ->
+ true = filelib:is_dir(DirAlias),
+ true = filelib:is_dir(DirAlias, erl_prim_loader),
+ true = filelib:is_dir(DirAlias, prim_file),
+ true = filelib:is_file(DirAlias),
+ true = filelib:is_file(DirAlias, erl_prim_loader),
+ true = filelib:is_file(DirAlias, prim_file),
+ ok = file:make_symlink(AFile,FileAlias),
+ true = filelib:is_file(FileAlias),
+ true = filelib:is_file(FileAlias, erl_prim_loader),
+ true = filelib:is_file(FileAlias, prim_file),
+ true = filelib:is_regular(FileAlias),
+ true = filelib:is_regular(FileAlias, erl_prim_loader),
+ true = filelib:is_regular(FileAlias, prim_file),
+ ok
+ end.
+
+file_props_symlink(Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ Dir = filename:join(PrivDir, ?MODULE_STRING++"_file_props_symlink"),
+ AFile = filename:join(Dir, "a_file"),
+ Alias = filename:join(Dir, "symlink"),
+ ok = file:make_dir(Dir),
+ ok = file:write_file(AFile, "not that big\n"),
+ case file:make_symlink(AFile, Alias) of
+ {error, enotsup} ->
+ {skip, "Links not supported on this platform"};
+ {error, eperm} ->
+ {win32,_} = os:type(),
+ {skip, "Windows user not privileged to create symlinks"};
+ ok ->
+ {_,_} = LastMod = filelib:last_modified(AFile),
+ LastMod = filelib:last_modified(Alias),
+ LastMod = filelib:last_modified(Alias, erl_prim_loader),
+ LastMod = filelib:last_modified(Alias, prim_file),
+ FileSize = filelib:file_size(AFile),
+ FileSize = filelib:file_size(Alias),
+ FileSize = filelib:file_size(Alias, erl_prim_loader),
+ FileSize = filelib:file_size(Alias, prim_file)
+ end.
diff --git a/lib/stdlib/test/io_SUITE.erl b/lib/stdlib/test/io_SUITE.erl
index 5a8971c071..3a76275f31 100644
--- a/lib/stdlib/test/io_SUITE.erl
+++ b/lib/stdlib/test/io_SUITE.erl
@@ -30,7 +30,7 @@
io_fread_newlines/1, otp_8989/1, io_lib_fread_literal/1,
printable_range/1,
io_lib_print_binary_depth_one/1, otp_10302/1, otp_10755/1,
- otp_10836/1]).
+ otp_10836/1, io_lib_width_too_small/1]).
-export([pretty/2]).
@@ -69,7 +69,8 @@ all() ->
io_lib_collect_line_3_wb, cr_whitespace_in_string,
io_fread_newlines, otp_8989, io_lib_fread_literal,
printable_range,
- io_lib_print_binary_depth_one, otp_10302, otp_10755, otp_10836].
+ io_lib_print_binary_depth_one, otp_10302, otp_10755, otp_10836,
+ io_lib_width_too_small].
groups() ->
[].
@@ -2213,3 +2214,8 @@ compile_file(File, Text, Config) ->
try compile:file(Fname, [return])
after ok %file:delete(Fname)
end.
+
+io_lib_width_too_small(Config) ->
+ "**" = lists:flatten(io_lib:format("~2.3w", [3.14])),
+ "**" = lists:flatten(io_lib:format("~2.5w", [3.14])),
+ ok.
diff --git a/lib/stdlib/vsn.mk b/lib/stdlib/vsn.mk
index 52ed78c557..9881ab040e 100644
--- a/lib/stdlib/vsn.mk
+++ b/lib/stdlib/vsn.mk
@@ -1 +1 @@
-STDLIB_VSN = 2.1
+STDLIB_VSN = 2.1.1
diff --git a/lib/tools/src/lcnt.erl b/lib/tools/src/lcnt.erl
index 20ee32c861..f1251fddab 100644
--- a/lib/tools/src/lcnt.erl
+++ b/lib/tools/src/lcnt.erl
@@ -61,6 +61,8 @@
locations/1,
inspect/1,
inspect/2,
+ histogram/1,
+ histogram/2,
information/0,
swap_pid_keys/0,
% set options
@@ -89,14 +91,14 @@
duration = 0
}).
-
-record(stats, {
- file,
- line,
- tries,
- colls,
- time, % us
- nt % #timings collected
+ file :: atom(),
+ line :: non_neg_integer(),
+ tries :: non_neg_integer(),
+ colls :: non_neg_integer(),
+ time :: non_neg_integer(), % us
+ nt :: non_neg_integer(), % #timings collected
+ hist :: tuple() % histogram
}).
-record(lock, {
@@ -115,7 +117,9 @@
colls,
cr, % collision ratio
time,
- dtr % time duration ratio
+ dtr, % time duration ratio
+ %% new
+ hist % log2 histogram of lock wait_time
}).
@@ -127,7 +131,7 @@
%% -------------------------------------------------------------------- %%
start() -> gen_server:start({local, ?MODULE}, ?MODULE, [], []).
-stop() -> gen_server:cast(?MODULE, stop).
+stop() -> gen_server:call(?MODULE, stop, infinity).
init([]) -> {ok, #state{ locks = [], duration = 0 } }.
%% -------------------------------------------------------------------- %%
@@ -171,6 +175,8 @@ conflicts() -> call({conflicts, []}).
conflicts(Opts) -> call({conflicts, Opts}).
inspect(Lock) -> call({inspect, Lock, []}).
inspect(Lock, Opts) -> call({inspect, Lock, Opts}).
+histogram(Lock) -> call({histogram, Lock, []}).
+histogram(Lock, Opts)-> call({histogram, Lock, Opts}).
information() -> call(information).
swap_pid_keys() -> call(swap_pid_keys).
raw() -> call(raw).
@@ -283,14 +289,14 @@ handle_call({locations, InOpts}, _From, #state{ locks = Locks } = State) when is
{reply, ok, State};
-handle_call({inspect, Lockname, InOpts}, _From, #state{ duration = Duration, locks = Locks } = State) when is_list(InOpts) ->
+handle_call({inspect, Lockname, InOpts}, _From, #state{ duration=Duration, locks=Locks } = State) when is_list(InOpts) ->
Default = [
{sort, time},
{reverse, false},
- {print, [name,id,tries,colls,ratio,time,duration]},
+ {print, [name,id,tries,colls,ratio,time,duration,histogram]},
{max_locks, 20},
{combine, false},
- {thresholds, [] },
+ {thresholds, []},
{locations, false}],
Opts = options(InOpts, Default),
@@ -299,7 +305,7 @@ handle_call({inspect, Lockname, InOpts}, _From, #state{ duration = Duration, loc
{true, true} -> locks_ids(Filtered);
_ -> []
end,
- Combos = combine_classes(Filtered, proplists:get_value(combine, Opts)),
+ Combos = combine_classes(Filtered, proplists:get_value(combine, Opts)),
case proplists:get_value(locations, Opts) of
true ->
lists:foreach(fun
@@ -313,17 +319,14 @@ handle_call({inspect, Lockname, InOpts}, _From, #state{ duration = Duration, loc
[] ->
ok;
_ ->
- %io:format("Combined ~p~n", [Combined]),
print("lock: " ++ term2string(Name)),
print("id: " ++ IdString),
print("type: " ++ term2string(Type)),
Ps = stats2print(Combined, Duration),
- Opts1 = options([{print, [entry, tries,colls,ratio,time,duration]},
+ Opts1 = options([{print, [entry, tries,colls,ratio,time,duration,histogram]},
{thresholds, [{tries, -1}, {colls, -1}, {time, -1}]}], Opts),
print_lock_information(filter_print(Ps, Opts1), proplists:get_value(print, Opts1))
end
- % (#lock{ name = Name, id = Id}) ->
- % io:format("Empty lock ~p ~p~n", [Name, Id])
end, Combos);
_ ->
Print1 = locks2print(Combos, Duration),
@@ -332,6 +335,34 @@ handle_call({inspect, Lockname, InOpts}, _From, #state{ duration = Duration, loc
end,
{reply, ok, State};
+%% histogram
+
+handle_call({histogram, Lockname, InOpts}, _From, #state{ duration=Duration, locks=Locks} = State)->
+ Default = [
+ {sort, time},
+ {reverse, false},
+ {print, [name,id,tries,colls,ratio,time,duration,histogram]},
+ {max_locks, 20},
+ {combine, true},
+ {thresholds, []},
+ {locations, false}],
+
+ Opts = options(InOpts, Default),
+ Filtered = filter_locks(Locks, Lockname),
+ Combos = combine_classes(Filtered, proplists:get_value(combine, Opts)),
+ lists:foreach(fun
+ (#lock{ stats = Stats }=L) ->
+ SumStats = summate_stats(Stats),
+ Opts1 = options([{print, [name,id,tries,colls,ratio,time,duration]},
+ {thresholds, [{tries, -1}, {colls, -1}, {time, -1}]}], Opts),
+ Prints = locks2print([L], Duration),
+ print_lock_information(Prints, proplists:get_value(print, Opts1)),
+ print_full_histogram(SumStats#stats.hist),
+ io:format("~n")
+ end, Combos),
+
+ {reply, ok, State};
+
handle_call(raw, _From, #state{ locks = Locks} = State)->
{reply, Locks, State};
@@ -347,7 +378,6 @@ handle_call(swap_pid_keys, _From, #state{ locks = Locks } = State)->
(L) ->
L
end, Locks),
-
{reply, ok, State#state{ locks = SwappedLocks}};
% settings
@@ -380,6 +410,8 @@ handle_call({save, Filename}, _From, State) ->
{reply, {error, Error}, State}
end;
+handle_call(stop, _From, State) ->
+ {stop, normal, ok, State};
handle_call(Command, _From, State) ->
{reply, {error, {undefined, Command}}, State}.
@@ -390,8 +422,6 @@ handle_call(Command, _From, State) ->
%%
%% -------------------------------------------------------------------- %%
-handle_cast(stop, State) ->
- {stop, normal, State};
handle_cast(_, State) ->
{noreply, State}.
@@ -432,15 +462,32 @@ code_change(_OldVsn, State, _Extra) ->
summate_locks(Locks) -> summate_locks(Locks, #stats{ tries = 0, colls = 0, time = 0, nt = 0}).
summate_locks([], Stats) -> Stats;
-summate_locks([L|Ls], #stats{ tries = Tries, colls = Colls, time = Time, nt = Nt}) ->
+summate_locks([L|Ls], #stats{ tries = Tries, colls = Colls, time = Time, nt = Nt, hist = Hist}) ->
S = summate_stats(L#lock.stats),
- summate_locks(Ls, #stats{ tries = Tries + S#stats.tries, colls = Colls + S#stats.colls, time = Time + S#stats.time, nt = Nt + S#stats.nt}).
+ summate_locks(Ls, #stats{
+ tries = Tries + S#stats.tries,
+ colls = Colls + S#stats.colls,
+ time = Time + S#stats.time,
+ nt = Nt + S#stats.nt,
+ hist = summate_histogram(Hist, S#stats.hist)
+ }).
summate_stats(Stats) -> summate_stats(Stats, #stats{ tries = 0, colls = 0, time = 0, nt = 0}).
summate_stats([], Stats) -> Stats;
-summate_stats([S|Ss], #stats{ tries = Tries, colls = Colls, time = Time, nt = Nt}) ->
- summate_stats(Ss, #stats{ tries = Tries + S#stats.tries, colls = Colls + S#stats.colls, time = Time + S#stats.time, nt = Nt + S#stats.nt}).
-
+summate_stats([S|Ss], #stats{ tries = Tries, colls = Colls, time = Time, nt = Nt, hist = Hist}) ->
+ summate_stats(Ss, #stats{
+ tries = Tries + S#stats.tries,
+ colls = Colls + S#stats.colls,
+ time = Time + S#stats.time,
+ nt = Nt + S#stats.nt,
+ hist = summate_histogram(Hist, S#stats.hist)
+ }).
+
+%% first call is undefined
+summate_histogram(Tup,undefined) when is_tuple(Tup) -> Tup;
+summate_histogram(undefined,Tup) when is_tuple(Tup) -> Tup;
+summate_histogram(Hs1,Hs2) ->
+ list_to_tuple([ A + B || {A,B} <- lists:zip(tuple_to_list(Hs1),tuple_to_list(Hs2))]).
%% manipulators
filter_locks_type(Locks, undefined) -> Locks;
@@ -465,17 +512,16 @@ filter_print(PLs, Opts) ->
TLs = threshold_locks(PLs, proplists:get_value(thresholds, Opts, [])),
SLs = sort_locks(TLs, proplists:get_value(sort, Opts, time)),
CLs = cut_locks(SLs, proplists:get_value(max_locks, Opts, none)),
- reverse_locks(CLs, proplists:get_value(reverse, Opts, false)).
-
-sort_locks(Locks, Type) -> lists:reverse(sort_locks0(Locks, Type)).
-sort_locks0(Locks, name) -> lists:keysort(#print.name, Locks);
-sort_locks0(Locks, id) -> lists:keysort(#print.id, Locks);
-sort_locks0(Locks, type) -> lists:keysort(#print.type, Locks);
-sort_locks0(Locks, tries) -> lists:keysort(#print.tries, Locks);
-sort_locks0(Locks, colls) -> lists:keysort(#print.colls, Locks);
-sort_locks0(Locks, ratio) -> lists:keysort(#print.cr, Locks);
-sort_locks0(Locks, time) -> lists:keysort(#print.time, Locks);
-sort_locks0(Locks, _) -> sort_locks0(Locks, time).
+ reverse_locks(CLs, not proplists:get_value(reverse,Opts, false)).
+
+sort_locks(Locks, name) -> lists:keysort(#print.name, Locks);
+sort_locks(Locks, id) -> lists:keysort(#print.id, Locks);
+sort_locks(Locks, type) -> lists:keysort(#print.type, Locks);
+sort_locks(Locks, tries) -> lists:keysort(#print.tries, Locks);
+sort_locks(Locks, colls) -> lists:keysort(#print.colls, Locks);
+sort_locks(Locks, ratio) -> lists:keysort(#print.cr, Locks);
+sort_locks(Locks, time) -> lists:keysort(#print.time, Locks);
+sort_locks(Locks, _) -> sort_locks(Locks, time).
% cut locks not above certain thresholds
threshold_locks(Locks, Thresholds) ->
@@ -556,45 +602,61 @@ locks_ids(Locks) -> locks_ids(Locks, []).
locks_ids([], Out) -> Out;
locks_ids([#lock{ name = Key } = L|Ls], Out) ->
case proplists:get_value(Key, Out) of
- undefined ->
- locks_ids(Ls, [{Key, [L#lock.id] } | Out]);
- Ids ->
- locks_ids(Ls, [{Key, [L#lock.id | Ids] } | proplists:delete(Key,Out)])
+ undefined -> locks_ids(Ls, [{Key, [L#lock.id]}|Out]);
+ Ids -> locks_ids(Ls, [{Key, [L#lock.id|Ids]}|proplists:delete(Key,Out)])
end.
stats2print(Stats, Duration) ->
lists:map(fun
(S) ->
- #print{
- entry = term2string("~tp:~p", [S#stats.file, S#stats.line]),
- colls = S#stats.colls,
- tries = S#stats.tries,
- cr = percent(S#stats.colls, S#stats.tries),
- time = S#stats.time,
- dtr = percent(S#stats.time, Duration)
- }
+ #print{entry = term2string("~tp:~p", [S#stats.file, S#stats.line]),
+ colls = S#stats.colls,
+ tries = S#stats.tries,
+ cr = percent(S#stats.colls, S#stats.tries),
+ time = S#stats.time,
+ dtr = percent(S#stats.time, Duration),
+ hist = format_histogram(S#stats.hist)}
end, Stats).
locks2print(Locks, Duration) ->
lists:map( fun
(L) ->
- Tries = lists:sum([T || #stats{ tries = T} <- L#lock.stats]),
- Colls = lists:sum([C || #stats{ colls = C} <- L#lock.stats]),
- Time = lists:sum([T || #stats{ time = T} <- L#lock.stats]),
- Cr = percent(Colls, Tries),
- Dtr = percent(Time, Duration),
- #print{
- name = L#lock.name,
- id = L#lock.id,
- type = L#lock.type,
- tries = Tries,
- colls = Colls,
- cr = Cr,
- time = Time,
- dtr = Dtr
- }
+ #stats{tries = Tries,
+ colls = Colls,
+ time = Time,
+ hist = Hist} = summate_stats(L#lock.stats),
+ Cr = percent(Colls, Tries),
+ Dtr = percent(Time, Duration),
+ #print{name = L#lock.name,
+ id = L#lock.id,
+ type = L#lock.type,
+ tries = Tries,
+ colls = Colls,
+ hist = format_histogram(Hist),
+ cr = Cr,
+ time = Time,
+ dtr = Dtr}
end, Locks).
+
+format_histogram(Tup) when is_tuple(Tup) ->
+ Vs = tuple_to_list(Tup),
+ Max = lists:max(Vs),
+ case Max of
+ 0 -> string_histogram(Vs);
+ _ -> string_histogram([case V of 0 -> 0; _ -> V/Max end || V <- Vs])
+ end.
+
+string_histogram([0|Vs]) ->
+ [$\s|string_histogram(Vs)];
+string_histogram([V|Vs]) when V > 0.66 ->
+ [$X|string_histogram(Vs)];
+string_histogram([V|Vs]) when V > 0.33 ->
+ [$x|string_histogram(Vs)];
+string_histogram([_|Vs]) ->
+ [$.|string_histogram(Vs)];
+string_histogram([]) -> [].
+
%% state making
data2state(Data, State) ->
@@ -606,22 +668,32 @@ data2state(Data, State) ->
locks = Locks
}.
-locks2records(Locks) -> locks2records(Locks, []).
-locks2records([], Out) -> Out;
-locks2records([{Name, Id, Type, Stats}|Locks], Out) ->
- Lock = #lock{
- name = Name,
- id = clean_id_creation(Id),
- type = Type,
- stats = [ #stats{
- file = File,
- line = Line,
- tries = Tries,
- colls = Colls,
- time = time2us({S, Ns}),
- nt = N
- } || {{File, Line}, {Tries, Colls, {S, Ns, N}}} <- Stats] },
- locks2records(Locks, [Lock|Out]).
+locks2records([{Name, Id, Type, Stats}|Locks]) ->
+ [#lock{name = Name,
+ id = clean_id_creation(Id),
+ type = Type,
+ stats = stats2record(Stats)}|locks2records(Locks)];
+locks2records([]) -> [].
+
+%% new stats with histogram
+stats2record([{{File,Line},{Tries,Colls,{S,Ns,N}},Hist}|Stats]) ->
+ [#stats{file = File,
+ line = Line,
+ hist = Hist,
+ tries = Tries,
+ colls = Colls,
+ time = time2us({S, Ns}),
+ nt = N} | stats2record(Stats)];
+%% old stats without histogram
+stats2record([{{File,Line},{Tries,Colls,{S,Ns,N}}}|Stats]) ->
+ [#stats{file = File,
+ line = Line,
+ hist = {},
+ tries = Tries,
+ colls = Colls,
+ time = time2us({S, Ns}),
+ nt = N} | stats2record(Stats)];
+stats2record([]) -> [].
clean_id_creation(Id) when is_pid(Id) ->
Bin = term_to_binary(Id),
@@ -647,22 +719,45 @@ state2list(State) ->
(X, Y) -> {X,Y}
end, record_info(fields, state), Values).
-list2state(List) -> list2state(record_info(fields, state), List, [state]).
-list2state([], _, Out) -> list_to_tuple(lists:reverse(Out));
-list2state([locks|Fs], List, Out) ->
- Locks = [ list2lock(Lock) || Lock <- proplists:get_value(locks, List, [])],
- list2state(Fs, List, [Locks|Out]);
-list2state([F|Fs], List, Out) -> list2state(Fs, List, [proplists:get_value(F, List, state_default(F))|Out]).
-
lock_default(Field) -> proplists:get_value(Field, lock2list(#lock{})).
lock2list(Lock) ->
[_|Values] = tuple_to_list(Lock),
lists:zip(record_info(fields, lock), Values).
-list2lock(List) -> list2lock(record_info(fields, lock), List, [lock]).
-list2lock([], _, Out) -> list_to_tuple(lists:reverse(Out));
-list2lock([F|Fs], List, Out) -> list2lock(Fs, List, [proplists:get_value(F, List, lock_default(F))|Out]).
+
+list2state(List) ->
+ list_to_tuple([state|list2state(record_info(fields, state), List)]).
+list2state([], _) -> [];
+list2state([locks|Fs], List) ->
+ Locks = [list2lock(Lock) || Lock <- proplists:get_value(locks, List, [])],
+ [Locks|list2state(Fs,List)];
+list2state([F|Fs], List) ->
+ [proplists:get_value(F, List, state_default(F))|list2state(Fs, List)].
+
+list2lock(Ls) ->
+ list_to_tuple([lock|list2lock(record_info(fields, lock), Ls)]).
+
+list2lock([],_) -> [];
+list2lock([stats=F|Fs], Ls) ->
+ Stats = stats2stats(proplists:get_value(F, Ls, lock_default(F))),
+ [Stats|list2lock(Fs, Ls)];
+list2lock([F|Fs], Ls) ->
+ [proplists:get_value(F, Ls, lock_default(F))|list2lock(Fs, Ls)].
+
+%% process old stats (hack)
+%% old stats had no histograms
+%% in future versions stats should be serialized as a list, not a record
+
+stats2stats([]) -> [];
+stats2stats([Stat|Stats]) ->
+ Sz = tuple_size(#stats{}),
+ [stat2stat(Stat,Sz)|stats2stats(Stats)].
+
+stat2stat(Stat,Sz) when tuple_size(Stat) =:= Sz -> Stat;
+stat2stat(Stat,_) ->
+ %% assume no histogram at the end
+ list_to_tuple(tuple_to_list(Stat) ++ [{0}]).
%% printing
@@ -683,7 +778,7 @@ auto_print_width(Locks, Print) ->
({print,print}, Out) -> [print|Out];
({Str, Len}, Out) -> [erlang:min(erlang:max(length(s(Str))+1,Len),80)|Out]
end, [], lists:zip(tuple_to_list(L), tuple_to_list(Max)))))
- end, #print{ id = 4, type = 5, entry = 5, name = 6, tries = 8, colls = 13, cr = 16, time = 11, dtr = 14 },
+ end, #print{ id = 4, type = 5, entry = 5, name = 6, tries = 8, colls = 13, cr = 16, time = 11, dtr = 14, hist=20 },
Locks),
% Setup the offsets for later pruning
Offsets = [
@@ -695,7 +790,9 @@ auto_print_width(Locks, Print) ->
{colls, R#print.colls},
{ratio, R#print.cr},
{time, R#print.time},
- {duration, R#print.dtr}],
+ {duration, R#print.dtr},
+ {histogram, R#print.hist}
+ ],
% Prune offsets to only allow specified print options
lists:foldr(fun
({Type, W}, Out) -> [{Type, W}|Out];
@@ -705,9 +802,7 @@ auto_print_width(Locks, Print) ->
print_lock_information(Locks, Print) ->
% remake Print to autosize entries
AutoPrint = auto_print_width(Locks, Print),
-
print_header(AutoPrint),
-
lists:foreach(fun
(L) ->
print_lock(L, AutoPrint)
@@ -724,7 +819,8 @@ print_header(Opts) ->
colls = "#collisions",
cr = "collisions [%]",
time = "time [us]",
- dtr = "duration [%]"
+ dtr = "duration [%]",
+ hist = "histogram"
},
Divider = #print{
name = lists:duplicate(1 + length(Header#print.name), 45),
@@ -735,39 +831,44 @@ print_header(Opts) ->
colls = lists:duplicate(1 + length(Header#print.colls), 45),
cr = lists:duplicate(1 + length(Header#print.cr), 45),
time = lists:duplicate(1 + length(Header#print.time), 45),
- dtr = lists:duplicate(1 + length(Header#print.dtr), 45)
+ dtr = lists:duplicate(1 + length(Header#print.dtr), 45),
+ hist = lists:duplicate(1 + length(Header#print.hist), 45)
},
print_lock(Header, Opts),
print_lock(Divider, Opts),
ok.
-print_lock(L, Opts) -> print_lock(L, Opts, []).
-print_lock(_, [], Formats) -> print(strings(lists:reverse(Formats)));
-print_lock(L, [Opt|Opts], Formats) ->
+print_lock(L, Opts) ->
+ print(strings(format_lock(L, Opts))).
+
+format_lock(_, []) -> [];
+format_lock(L, [Opt|Opts]) ->
case Opt of
- id -> print_lock(L, Opts, [{space, 25, s(L#print.id) } | Formats]);
- {id, W} -> print_lock(L, Opts, [{space, W, s(L#print.id) } | Formats]);
- type -> print_lock(L, Opts, [{space, 18, s(L#print.type) } | Formats]);
- {type, W} -> print_lock(L, Opts, [{space, W, s(L#print.type) } | Formats]);
- entry -> print_lock(L, Opts, [{space, 30, s(L#print.entry)} | Formats]);
- {entry, W} -> print_lock(L, Opts, [{space, W, s(L#print.entry)} | Formats]);
- name -> print_lock(L, Opts, [{space, 22, s(L#print.name) } | Formats]);
- {name, W} -> print_lock(L, Opts, [{space, W, s(L#print.name) } | Formats]);
- tries -> print_lock(L, Opts, [{space, 12, s(L#print.tries)} | Formats]);
- {tries, W} -> print_lock(L, Opts, [{space, W, s(L#print.tries)} | Formats]);
- colls -> print_lock(L, Opts, [{space, 14, s(L#print.colls)} | Formats]);
- {colls, W} -> print_lock(L, Opts, [{space, W, s(L#print.colls)} | Formats]);
- ratio -> print_lock(L, Opts, [{space, 20, s(L#print.cr) } | Formats]);
- {ratio, W} -> print_lock(L, Opts, [{space, W, s(L#print.cr) } | Formats]);
- time -> print_lock(L, Opts, [{space, 15, s(L#print.time) } | Formats]);
- {time, W} -> print_lock(L, Opts, [{space, W, s(L#print.time) } | Formats]);
- duration -> print_lock(L, Opts, [{space, 20, s(L#print.dtr) } | Formats]);
- {duration, W} -> print_lock(L, Opts, [{space, W, s(L#print.dtr) } | Formats]);
- _ -> print_lock(L, Opts, Formats)
+ id -> [{space, 25, s(L#print.id) } | format_lock(L, Opts)];
+ {id, W} -> [{space, W, s(L#print.id) } | format_lock(L, Opts)];
+ type -> [{space, 18, s(L#print.type) } | format_lock(L, Opts)];
+ {type, W} -> [{space, W, s(L#print.type) } | format_lock(L, Opts)];
+ entry -> [{space, 30, s(L#print.entry)} | format_lock(L, Opts)];
+ {entry, W} -> [{space, W, s(L#print.entry)} | format_lock(L, Opts)];
+ name -> [{space, 22, s(L#print.name) } | format_lock(L, Opts)];
+ {name, W} -> [{space, W, s(L#print.name) } | format_lock(L, Opts)];
+ tries -> [{space, 12, s(L#print.tries)} | format_lock(L, Opts)];
+ {tries, W} -> [{space, W, s(L#print.tries)} | format_lock(L, Opts)];
+ colls -> [{space, 14, s(L#print.colls)} | format_lock(L, Opts)];
+ {colls, W} -> [{space, W, s(L#print.colls)} | format_lock(L, Opts)];
+ ratio -> [{space, 20, s(L#print.cr) } | format_lock(L, Opts)];
+ {ratio, W} -> [{space, W, s(L#print.cr) } | format_lock(L, Opts)];
+ time -> [{space, 15, s(L#print.time) } | format_lock(L, Opts)];
+ {time, W} -> [{space, W, s(L#print.time) } | format_lock(L, Opts)];
+ duration -> [{space, 20, s(L#print.dtr) } | format_lock(L, Opts)];
+ {duration, W} -> [{space, W, s(L#print.dtr) } | format_lock(L, Opts)];
+ histogram -> [{space, 0, s(L#print.hist) } | format_lock(L, Opts)];
+ {histogram, W} -> [{space, W, s(L#print.hist) } | format_lock(L, Opts)];
+ _ -> format_lock(L, Opts)
end.
-print_state_information(#state{ locks = Locks} = State) ->
+print_state_information(#state{locks = Locks} = State) ->
Stats = summate_locks(Locks),
print("information:"),
print(kv("#locks", s(length(Locks)))),
@@ -779,9 +880,25 @@ print_state_information(#state{ locks = Locks} = State) ->
print(kv("percent of duration", s(Stats#stats.time/State#state.duration*100) ++ " %")),
ok.
+
+print_full_histogram(T) when is_tuple(T) ->
+ Vs = tuple_to_list(T),
+ Max = lists:max(Vs),
+ W = 60,
+ print_full_histogram(0,Vs,Max,W).
+
+print_full_histogram(_,[],_,_) -> ok;
+print_full_histogram(Ix,[V|Vs],0,W) ->
+ io:format("~2w = log2 : ~8w |~n", [Ix,V]),
+ print_full_histogram(Ix+1,Vs,0,W);
+print_full_histogram(Ix,[V|Vs],Max,W) ->
+ io:format("~2w = log2 : ~8w | ~s~n", [Ix,V,lists:duplicate(trunc(W*(V/Max)), $#)]),
+ print_full_histogram(Ix+1,Vs,Max,W).
+
+
%% AUX
-time2us({S, Ns}) -> round(S*1000000 + Ns/1000).
+time2us({S, Ns}) -> S*1000000 + (Ns div 1000).
percent(_,0) -> 0.0;
percent(T,N) -> T/N*100.
@@ -808,7 +925,7 @@ s(T) -> term2string(T).
strings(Strings) -> strings(Strings, []).
strings([], Out) -> Out;
-strings([{space, N, S} | Ss], Out) -> strings(Ss, Out ++ term2string(term2string("~~~ps", [N]), [S]));
+strings([{space, N, S} | Ss], Out) -> strings(Ss, Out ++ term2string(term2string("~~~ws", [N]), [S]));
strings([{format, Format, S} | Ss], Out) -> strings(Ss, Out ++ term2string(Format, [S]));
strings([S|Ss], Out) -> strings(Ss, Out ++ term2string("~ts", [S])).
@@ -825,7 +942,7 @@ term2string(Term) when is_pid(Term) ->
term2string(Term) -> term2string("~w", [Term]).
term2string(Format, Terms) -> lists:flatten(io_lib:format(Format, Terms)).
-%%% AUD id binary
+%%% AUX id binary
bytes16(Value) ->
B0 = Value band 255,
diff --git a/lib/tools/test/lcnt_SUITE.erl b/lib/tools/test/lcnt_SUITE.erl
index 1bee6021ab..010dffe138 100644
--- a/lib/tools/test/lcnt_SUITE.erl
+++ b/lib/tools/test/lcnt_SUITE.erl
@@ -27,11 +27,11 @@
%% Test cases
-export([
- load_v1/1,
- conflicts/1,
- locations/1,
- swap_keys/1
- ]).
+ t_load/1,
+ t_conflicts/1,
+ t_locations/1,
+ t_swap_keys/1
+ ]).
%% Default timetrap timeout (set in init_per_testcase)
-define(default_timeout, ?t:minutes(4)).
@@ -54,48 +54,52 @@ end_per_testcase(_Case, Config) ->
suite() -> [{ct_hooks,[ts_install_cth]}].
-all() ->
- [load_v1, conflicts, locations, swap_keys].
+all() -> [t_load, t_conflicts, t_locations, t_swap_keys].
-groups() ->
- [].
+groups() -> [].
-init_per_group(_GroupName, Config) ->
- Config.
+init_per_group(_GroupName, Config) -> Config.
-end_per_group(_GroupName, Config) ->
- Config.
+end_per_group(_GroupName, Config) -> Config.
%%----------------------------------------------------------------------
%% Tests
%%----------------------------------------------------------------------
-load_v1(suite) ->
- [];
-load_v1(doc) ->
- ["Load data from file."];
-load_v1(Config) when is_list(Config) ->
- ?line {ok, _} = lcnt:start(),
- ?line Path = ?config(data_dir, Config),
- ?line File = filename:join([Path,"big_bang_40.lcnt"]),
- ?line ok = lcnt:load(File),
- ?line ok = lcnt:stop(),
+t_load(suite) -> [];
+t_load(doc) -> ["Load data from file."];
+t_load(Config) when is_list(Config) ->
+ Path = ?config(data_dir, Config),
+ Files = [filename:join([Path,"big_bang_40.lcnt"]),
+ filename:join([Path,"ehb_3_3_hist.lcnt"])],
+ ok = t_load_file(Files),
ok.
-conflicts(suite) ->
- [];
-conflicts(doc) ->
- ["API: conflicts"];
-conflicts(Config) when is_list(Config) ->
- ?line {ok, _} = lcnt:start(),
- ?line Path = ?config(data_dir, Config),
- ?line File = filename:join([Path,"big_bang_40.lcnt"]),
- ?line ok = lcnt:load(File),
- ?line ok = lcnt:conflicts(),
- THs = [-1, 0, 100, 1000],
- Print = [name , id , type , entry , tries , colls , ratio , time , duration],
- Opts = [
+t_load_file([]) -> ok;
+t_load_file([File|Files]) ->
+ {ok, _} = lcnt:start(),
+ ok = lcnt:load(File),
+ ok = lcnt:stop(),
+ t_load_file(Files).
+
+t_conflicts(suite) -> [];
+t_conflicts(doc) -> ["API: conflicts"];
+t_conflicts(Config) when is_list(Config) ->
+ Path = ?config(data_dir, Config),
+ Files = [filename:join([Path,"big_bang_40.lcnt"]),
+ filename:join([Path,"ehb_3_3_hist.lcnt"])],
+ ok = t_conflicts_file(Files),
+ ok.
+
+t_conflicts_file([]) -> ok;
+t_conflicts_file([File|Files]) ->
+ {ok, _} = lcnt:start(),
+ ok = lcnt:load(File),
+ ok = lcnt:conflicts(),
+ THs = [-1, 0, 100, 1000],
+ Print = [name , id , type , entry , tries , colls , ratio , time , duration],
+ Opts = [
[{sort, Sort}, {reverse, Rev}, {max_locks, ML}, {combine, Combine}, {thresholds, [TH]}, {print, [Print]}] ||
Sort <- [name , id , type , tries , colls , ratio , time , entry],
ML <- [none, 1 , 32, 4096],
@@ -103,28 +107,33 @@ conflicts(Config) when is_list(Config) ->
TH <- [{tries, Tries} || Tries <- THs] ++ [{colls, Colls} || Colls <- THs] ++ [{time, Time} || Time <- THs],
Rev <- [true, false]
],
- ?line ok = test_conflicts_opts(Opts),
- ?line ok = lcnt:stop(),
- ok.
+ ok = test_conflicts_opts(Opts),
+ ok = lcnt:stop(),
+ t_conflicts_file(Files).
+
test_conflicts_opts([]) -> ok;
test_conflicts_opts([Opt|Opts]) ->
- ?line ok = lcnt:conflicts(Opt),
+ ok = lcnt:conflicts(Opt),
test_conflicts_opts(Opts).
-locations(suite) ->
- [];
-locations(doc) ->
- ["API: locations"];
-locations(Config) when is_list(Config) ->
- ?line {ok, _} = lcnt:start(),
- ?line Path = ?config(data_dir, Config),
- ?line File = filename:join([Path,"big_bang_40.lcnt"]),
- ?line ok = lcnt:load(File),
- ?line ok = lcnt:locations(),
- THs = [-1, 0, 100, 1000],
- Print = [name , id , type , entry , tries , colls , ratio , time , duration],
- Opts = [
+t_locations(suite) -> [];
+t_locations(doc) -> ["API: locations"];
+t_locations(Config) when is_list(Config) ->
+ Path = ?config(data_dir, Config),
+ Files = [filename:join([Path,"big_bang_40.lcnt"]),
+ filename:join([Path,"ehb_3_3_hist.lcnt"])],
+ ok = t_locations_file(Files),
+ ok.
+
+t_locations_file([]) -> ok;
+t_locations_file([File|Files]) ->
+ {ok, _} = lcnt:start(),
+ ok = lcnt:load(File),
+ ok = lcnt:locations(),
+ THs = [-1, 0, 100, 1000],
+ Print = [name , id , type , entry , tries , colls , ratio , time , duration],
+ Opts = [
[{full_id, Id}, {sort, Sort}, {max_locks, ML}, {combine, Combine}, {thresholds, [TH]}, {print, Print}] ||
Sort <- [name , id , type , tries , colls , ratio , time , entry],
ML <- [none, 1 , 64],
@@ -132,30 +141,34 @@ locations(Config) when is_list(Config) ->
TH <- [{tries, Tries} || Tries <- THs] ++ [{colls, Colls} || Colls <- THs] ++ [{time, Time} || Time <- THs],
Id <- [true, false]
],
- ?line ok = test_locations_opts(Opts),
- ?line ok = lcnt:stop(),
- ok.
+ ok = test_locations_opts(Opts),
+ ok = lcnt:stop(),
+ t_locations_file(Files).
test_locations_opts([]) -> ok;
test_locations_opts([Opt|Opts]) ->
- ?line ok = lcnt:locations(Opt),
+ ok = lcnt:locations(Opt),
test_locations_opts(Opts).
-swap_keys(suite) ->
- [];
-swap_keys(doc) ->
- ["Test interchanging port/process id with class"];
-swap_keys(Config) when is_list(Config) ->
- ?line {ok, _} = lcnt:start(),
- ?line Path = ?config(data_dir, Config),
- ?line File = filename:join([Path,"big_bang_40.lcnt"]),
- ?line ok = lcnt:load(File),
- ?line ok = lcnt:conflicts(),
- ?line ok = lcnt:swap_pid_keys(),
- ?line ok = lcnt:conflicts(),
- ?line ok = lcnt:stop(),
+t_swap_keys(suite) -> [];
+t_swap_keys(doc) -> ["Test interchanging port/process id with class"];
+t_swap_keys(Config) when is_list(Config) ->
+ Path = ?config(data_dir, Config),
+ Files = [filename:join([Path,"big_bang_40.lcnt"]),
+ filename:join([Path,"ehb_3_3_hist.lcnt"])],
+ ok = t_swap_keys_file(Files),
ok.
+t_swap_keys_file([]) -> ok;
+t_swap_keys_file([File|Files]) ->
+ {ok, _} = lcnt:start(),
+ ok = lcnt:load(File),
+ ok = lcnt:conflicts(),
+ ok = lcnt:swap_pid_keys(),
+ ok = lcnt:conflicts(),
+ ok = lcnt:stop(),
+ t_swap_keys_file(Files).
+
%%----------------------------------------------------------------------
%% Auxiliary tests
diff --git a/lib/tools/test/lcnt_SUITE_data/ehb_3_3_hist.lcnt b/lib/tools/test/lcnt_SUITE_data/ehb_3_3_hist.lcnt
new file mode 100644
index 0000000000..ff5bdcbdaa
--- /dev/null
+++ b/lib/tools/test/lcnt_SUITE_data/ehb_3_3_hist.lcnt
Binary files differ
diff --git a/otp_versions.table b/otp_versions.table
index 6dee6faf16..472680f16f 100644
--- a/otp_versions.table
+++ b/otp_versions.table
@@ -1,3 +1,4 @@
+OTP-17.1.2 : erts-6.1.2 kernel-3.0.2 stdlib-2.1.1 # asn1-3.0.1 common_test-1.8.1 compiler-5.0.1 cosEvent-2.1.15 cosEventDomain-1.1.14 cosFileTransfer-1.1.16 cosNotification-1.1.21 cosProperty-1.1.17 cosTime-1.1.14 cosTransactions-1.2.14 crypto-3.4 debugger-4.0.1 dialyzer-2.7.1 diameter-1.7 edoc-0.7.14 eldap-1.0.3 erl_docgen-0.3.5 erl_interface-3.7.17 et-1.5 eunit-2.2.7 gs-1.5.16 hipe-3.11 ic-4.3.5 inets-5.10.2 jinterface-1.5.9 megaco-3.17.1 mnesia-4.12.1 observer-2.0.1 odbc-2.10.20 orber-3.6.27 os_mon-2.2.15 ose-1.0 otp_mibs-1.0.9 parsetools-2.0.11 percept-0.8.9 public_key-0.22 reltool-0.6.6 runtime_tools-1.8.14 sasl-2.4 snmp-4.25.1 ssh-3.0.3 ssl-5.3.5 syntax_tools-1.6.16 test_server-3.7.1 tools-2.6.15 typer-0.9.8 webtool-0.8.10 wx-1.3 xmerl-1.3.7 :
OTP-17.1.1 : edoc-0.7.14 erts-6.1.1 syntax_tools-1.6.16 # asn1-3.0.1 common_test-1.8.1 compiler-5.0.1 cosEvent-2.1.15 cosEventDomain-1.1.14 cosFileTransfer-1.1.16 cosNotification-1.1.21 cosProperty-1.1.17 cosTime-1.1.14 cosTransactions-1.2.14 crypto-3.4 debugger-4.0.1 dialyzer-2.7.1 diameter-1.7 eldap-1.0.3 erl_docgen-0.3.5 erl_interface-3.7.17 et-1.5 eunit-2.2.7 gs-1.5.16 hipe-3.11 ic-4.3.5 inets-5.10.2 jinterface-1.5.9 kernel-3.0.1 megaco-3.17.1 mnesia-4.12.1 observer-2.0.1 odbc-2.10.20 orber-3.6.27 os_mon-2.2.15 ose-1.0 otp_mibs-1.0.9 parsetools-2.0.11 percept-0.8.9 public_key-0.22 reltool-0.6.6 runtime_tools-1.8.14 sasl-2.4 snmp-4.25.1 ssh-3.0.3 ssl-5.3.5 stdlib-2.1 test_server-3.7.1 tools-2.6.15 typer-0.9.8 webtool-0.8.10 wx-1.3 xmerl-1.3.7 :
OTP-17.1 : asn1-3.0.1 common_test-1.8.1 compiler-5.0.1 crypto-3.4 debugger-4.0.1 dialyzer-2.7.1 diameter-1.7 erl_interface-3.7.17 erts-6.1 hipe-3.11 inets-5.10.2 kernel-3.0.1 mnesia-4.12.1 observer-2.0.1 reltool-0.6.6 ssh-3.0.3 ssl-5.3.5 stdlib-2.1 syntax_tools-1.6.15 test_server-3.7.1 tools-2.6.15 typer-0.9.8 wx-1.3 # cosEvent-2.1.15 cosEventDomain-1.1.14 cosFileTransfer-1.1.16 cosNotification-1.1.21 cosProperty-1.1.17 cosTime-1.1.14 cosTransactions-1.2.14 edoc-0.7.13 eldap-1.0.3 erl_docgen-0.3.5 et-1.5 eunit-2.2.7 gs-1.5.16 ic-4.3.5 jinterface-1.5.9 megaco-3.17.1 odbc-2.10.20 orber-3.6.27 os_mon-2.2.15 ose-1.0 otp_mibs-1.0.9 parsetools-2.0.11 percept-0.8.9 public_key-0.22 runtime_tools-1.8.14 sasl-2.4 snmp-4.25.1 webtool-0.8.10 xmerl-1.3.7 :
OTP-17.0.2 : inets-5.10.1 ssh-3.0.2 # asn1-3.0 common_test-1.8 compiler-5.0 cosEvent-2.1.15 cosEventDomain-1.1.14 cosFileTransfer-1.1.16 cosNotification-1.1.21 cosProperty-1.1.17 cosTime-1.1.14 cosTransactions-1.2.14 crypto-3.3 debugger-4.0 dialyzer-2.7 diameter-1.6 edoc-0.7.13 eldap-1.0.3 erl_docgen-0.3.5 erl_interface-3.7.16 erts-6.0.1 et-1.5 eunit-2.2.7 gs-1.5.16 hipe-3.10.3 ic-4.3.5 jinterface-1.5.9 kernel-3.0 megaco-3.17.1 mnesia-4.12 observer-2.0 odbc-2.10.20 orber-3.6.27 os_mon-2.2.15 ose-1.0 otp_mibs-1.0.9 parsetools-2.0.11 percept-0.8.9 public_key-0.22 reltool-0.6.5 runtime_tools-1.8.14 sasl-2.4 snmp-4.25.1 ssl-5.3.4 stdlib-2.0 syntax_tools-1.6.14 test_server-3.7 tools-2.6.14 typer-0.9.7 webtool-0.8.10 wx-1.2 xmerl-1.3.7 :
diff --git a/system/doc/efficiency_guide/advanced.xml b/system/doc/efficiency_guide/advanced.xml
index b5771a5929..51f1b2612c 100644
--- a/system/doc/efficiency_guide/advanced.xml
+++ b/system/doc/efficiency_guide/advanced.xml
@@ -183,7 +183,7 @@ On 64-bit architectures: 4 words for a reference from the current local node, an
<tag><em>Open ports</em></tag>
<item>
<marker id="ports"></marker>
- <p>The maximum number of simultaneously oper Erlang ports is
+ <p>The maximum number of simultaneously open Erlang ports is
often by default 16384. This limit can be configured at startup,
for more information see the
<seealso marker="erts:erl#max_ports"><c>+Q</c></seealso>