diff options
90 files changed, 1303 insertions, 302 deletions
diff --git a/bootstrap/lib/kernel/ebin/application.beam b/bootstrap/lib/kernel/ebin/application.beam Binary files differindex c0a615f5a3..e39352117f 100644 --- a/bootstrap/lib/kernel/ebin/application.beam +++ b/bootstrap/lib/kernel/ebin/application.beam diff --git a/erts/.gitignore b/erts/.gitignore index 526d5da0b9..e515dc8811 100644 --- a/erts/.gitignore +++ b/erts/.gitignore @@ -11,6 +11,7 @@ /etc/common/Install /etc/common/erl.src +/etc/unix/etp-commands /test/Emakefile /test/*.beam diff --git a/erts/configure.in b/erts/configure.in index 2f624e5853..64436e933c 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -1051,12 +1051,15 @@ if test $ERTS_BUILD_SMP_EMU = yes; then AC_DEFINE(ERTS_HAVE_SMP_EMU, 1, [Define if the smp emulator is built]) + test "X$smp_require_native_atomics" = "Xyes" && + AC_DEFINE(ETHR_SMP_REQUIRE_NATIVE_IMPLS, 1, [Define if you want to enable check for native ethread implementations]) + case "$ethr_have_native_atomics-$smp_require_native_atomics-$ethr_have_native_spinlock" in yes-*) ;; no-yes-*) - AC_MSG_ERROR([No native atomic implementation found. See INSTALL.md for more information.]) + AC_MSG_ERROR([No native atomic implementation found. See Configuring section in INSTALL.md for more information.]) ;; no-no-yes) diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml index efe0483b31..540390e1b1 100644 --- a/erts/doc/src/erl_driver.xml +++ b/erts/doc/src/erl_driver.xml @@ -666,7 +666,7 @@ typedef struct ErlDrvBinary { <item> <p>The <c>ErlDrvData</c> is a handle to driver-specific data, passed to the driver call-backs. It is a pointer, and is - most often type casted to a specific pointer in the driver.</p> + most often type cast to a specific pointer in the driver.</p> </item> <tag>SysIOVec</tag> <item> @@ -1014,7 +1014,7 @@ typedef struct ErlIOVec { <fsummary>Read a system timestamp</fsummary> <desc> <marker id="driver_get_now"></marker> - <p>This function reads a timestamp into the memory pointed to by + <p>This function reads a timestamp into the memory pointed to by the parameter <c>now</c>. See the description of <seealso marker="#ErlDrvNowData">ErlDrvNowData</seealso> for specification of its fields. </p> <p>The return value is 0 unless the <c>now</c> pointer is not @@ -1056,7 +1056,7 @@ typedef struct ErlIOVec { returned. Another thread may still be using the event object internally. To safely close an event object call <c>driver_select</c> with <c>ERL_DRV_USE</c> and <c>on==0</c>. That - will clear all events and then call + will clear all events and then call <seealso marker="driver_entry#stop_select">stop_select</seealso> when it is safe to close the event object. <c>ERL_DRV_USE</c> should be set together with the first event @@ -1068,7 +1068,7 @@ typedef struct ErlIOVec { <p>ERL_DRV_USE was added in OTP release R13. Old drivers will still work as before. But it is recommended to update them to use <c>ERL_DRV_USE</c> and <c>stop_select</c> to make sure that event objects are closed in a safe way.</p> - </note> + </note> <p>The return value is 0 (failure, -1, only if the <c>ready_input</c>/<c>ready_output</c> is <c>NULL</c>).</p> @@ -1524,7 +1524,7 @@ typedef struct ErlIOVec { <marker id="remove_driver_entry"></marker> <p>This function removes a driver entry <c>de</c> previously added with <c>add_driver_entry</c>.</p> - <p>Driver entries added by the <c>erl_ddll</c> erlang interface can + <p>Driver entries added by the <c>erl_ddll</c> erlang interface can not be removed by using this interface.</p> </desc> </func> @@ -1758,7 +1758,7 @@ typedef struct ErlIOVec { <pre> Term type Argument(s) =========================================== -ERL_DRV_NIL +ERL_DRV_NIL ERL_DRV_ATOM ErlDrvTermData atom (from driver_mk_atom(char *string)) ERL_DRV_INT ErlDrvSInt integer ERL_DRV_UINT ErlDrvUInt integer @@ -1779,11 +1779,11 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len signed integer data type <c>ErlDrvSInt</c> are 64 bits wide on a 64 bit runtime system and 32 bits wide on a 32 bit runtime system. They were introduced in erts version 5.6, - and replaced some of the <c>int</c> arguments in the list above. + and replaced some of the <c>int</c> arguments in the list above. </p> <p>The unsigned integer data type <c>ErlDrvUInt64</c> and the signed integer data type <c>ErlDrvSInt64</c> are always 64 bits - wide. They were introduced in erts version 5.7.4. + wide. They were introduced in erts version 5.7.4. </p> <p>To build the tuple <c>{tcp, Port, [100 | Binary]}</c>, the @@ -1879,7 +1879,7 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len <fsummary>Send term data from driver to port owner</fsummary> <desc> <marker id="driver_output_term"></marker> - <warning><p><c>driver_output_term()</c> is deferred and will + <warning><p><c>driver_output_term()</c> is deprecated and will be removed in the OTP-R17 release. Use <seealso marker="#erl_drv_send_term">erl_drv_output_term()</seealso> instead.</p> @@ -1937,7 +1937,7 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len <fsummary>Send term data to other process than port owner process</fsummary> <desc> <marker id="driver_send_term"></marker> - <warning><p><c>driver_send_term()</c> is deferred and will + <warning><p><c>driver_send_term()</c> is deprecated and will be removed in the OTP-R17 release. Use <seealso marker="#erl_drv_send_term">erl_drv_send_term()</seealso> instead.</p> @@ -1981,7 +1981,7 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len thread, the following call can be used:</p> <p></p> <code type="none"><![CDATA[ - unsigned int myKey = (unsigned int) myPort; + unsigned int myKey = driver_async_port_key(myPort); r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> @@ -1998,7 +1998,7 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len The data should be freed in <c>async_free</c>, because it's called if <c>driver_async_cancel</c> is called.</p> <p>When the async operation is done, <seealso marker="driver_entry#ready_async">ready_async</seealso> driver - entry function is called. If <c>async_ready</c> is null in + entry function is called. If <c>ready_async</c> is null in the driver entry, the <c>async_free</c> function is called instead.</p> <p>The return value is a handle to the asynchronous task, which @@ -2022,6 +2022,24 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len </desc> </func> <func> + <name><ret>unsigned int</ret><nametext>driver_async_port_key (ErlDrvPort port)</nametext></name> + <fsummary>Calculate an async key from an ErlDrvPort</fsummary> + <desc> + <marker id="driver_async_port_key"></marker> + <p>This function calculates a key for later use in <seealso + marker="#driver_async">driver_async()</seealso>. The keys are + evenly distributed so that a fair mapping between port id's + and async thread id's is achieved.</p> + <note> + <p>Before OTP-R16, the actual port id could be used as a key + with proper casting, but after the rewrite of the port + subsystem, this is no longer the case. With this function, you + can achieve the same distribution based on port id's as before + OTP-R16.</p> + </note> + </desc> + </func> + <func> <name><ret>int</ret><nametext>driver_async_cancel(long id)</nametext></name> <fsummary>Cancel an asynchronous call</fsummary> <desc> @@ -2033,10 +2051,10 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len The user had to implement synchronization of cancellation anyway. It also unnecessarily complicated the implementation. Therefore, as of OTP-R15B <c>driver_async_cancel()</c> is deprecated, and - scheduled for removal in OTP-R16. It will currently always fail, + scheduled for removal in OTP-R17. It will currently always fail, and return 0.</p> - <warning><p><c>driver_async_cancel()</c> is deferred and will - be removed in the OTP-R16 release.</p> + <warning><p><c>driver_async_cancel()</c> is deprecated and will + be removed in the OTP-R17 release.</p> </warning> </desc> @@ -2048,7 +2066,7 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len <marker id="driver_lock_driver"></marker> <p>This function locks the driver used by the port <c>port</c> in memory for the rest of the emulator process' - lifetime. After this call, the driver behaves as one of Erlang's + lifetime. After this call, the driver behaves as one of Erlang's statically linked in drivers.</p> </desc> </func> @@ -2076,7 +2094,7 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len <seealso marker="driver_entry">driver_entry</seealso>).</item> <tag><c>drv_data</c></tag> <item>The driver defined handle that will be passed in subsequent - calls to driver call-backs. Note, that the + calls to driver call-backs. Note, that the <seealso marker="driver_entry#start">driver start call-back</seealso> will not be called for this new driver instance. The driver defined handle is normally created in the @@ -2284,7 +2302,7 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len <item>A thread identifier.</item> </taglist> <p>This function compares two thread identifiers for equality, - and returns <c>0</c> it they aren't equal, and + and returns <c>0</c> it they aren't equal, and a value not equal to <c>0</c> if they are equal.</p> <note><p>A Thread identifier may be reused very quickly after a thread has terminated. Therefore, if a thread @@ -2469,7 +2487,7 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len </taglist> <p>This function broadcasts on a condition variable. That is, if other threads are waiting on the condition variable being - broadcasted on, <em>all</em> of them will be woken. + broadcast on, <em>all</em> of them will be woken. </p> <p>This function is thread-safe.</p> </desc> @@ -2498,7 +2516,7 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len the calling thread when calling this function. </p> <note><p><c>erl_drv_cond_wait()</c> might return even though - no-one has signaled or broadcasted on the condition + no-one has signaled or broadcast on the condition variable. Code calling <c>erl_drv_cond_wait()</c> should always be prepared for <c>erl_drv_cond_wait()</c> returning even though the condition that the thread was @@ -2822,7 +2840,7 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len <item>A pointer to an output buffer.</item> <tag><c>value_size</c></tag> <item>A pointer to an integer. The integer is both used for - passing input and output sizes (see below). + passing input and output sizes (see below). </item> </taglist> <p>This function retrieves the value of an environment variable. @@ -2900,4 +2918,3 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len Guide Ch. 3)</p> </section> </cref> - diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 767edc1cc0..5ee40823bc 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -235,7 +235,7 @@ <code> 1> Bin = <<1,2,3,4,5,6,7,8,9,10>>. -2> binary_part(Bin,{byte_size(Bin), -5)). +2> binary_part(Bin,{byte_size(Bin), -5}). <<6,7,8,9,10>> </code> diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c index bf8a37c71b..e6d9f83aed 100644 --- a/erts/emulator/beam/erl_alloc_util.c +++ b/erts/emulator/beam/erl_alloc_util.c @@ -4369,7 +4369,7 @@ info_options(Allctr_t *allctr, #endif "option lmbcs: %beu\n" "option smbcs: %beu\n" - "option mbcgs: %beu\n", + "option mbcgs: %beu\n" "option acul: %d\n", topt, allctr->ramv ? "true" : "false", diff --git a/erts/emulator/beam/erl_async.c b/erts/emulator/beam/erl_async.c index 054d1a48f6..e6d72f569b 100644 --- a/erts/emulator/beam/erl_async.c +++ b/erts/emulator/beam/erl_async.c @@ -583,6 +583,20 @@ int erts_async_ready_clean(void *varq, void *val) #endif /* +** Generate a fair async key prom an ErlDrvPort +** The port data gives a fair distribution grom port pointer +** to unsigned integer - to be used in key for driver_async below. +*/ +unsigned int driver_async_port_key(ErlDrvPort port) +{ + ErlDrvTermData td = driver_mk_port(port); + if (td == (ErlDrvTermData) NIL) { + return 0; + } + return (unsigned int) (UWord) internal_port_data(td); +} + +/* ** Schedule async_invoke on a worker thread ** NOTE will be syncrounous when threads are unsupported ** return values: diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index 713ac0ba18..ef3749a2c4 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -2319,6 +2319,8 @@ restart: break; case matchSilent: --esp; + if (in_flags & ERTS_PAM_IGNORE_TRACE_SILENT) + break; if (*esp == am_true) { erts_smp_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR); ERTS_TRACE_FLAGS(c_p) |= F_TRACE_SILENT; @@ -4971,7 +4973,8 @@ static Eterm match_spec_test(Process *p, Eterm against, Eterm spec, int trace) save_cp = p->cp; p->cp = NULL; res = erts_match_set_run(p, mps, arr, n, - ERTS_PAM_COPY_RESULT, &ret_flags); + ERTS_PAM_COPY_RESULT|ERTS_PAM_IGNORE_TRACE_SILENT, + &ret_flags); p->cp = save_cp; } else { n = 0; diff --git a/erts/emulator/beam/erl_driver.h b/erts/emulator/beam/erl_driver.h index e280563de1..1ab6e17f56 100644 --- a/erts/emulator/beam/erl_driver.h +++ b/erts/emulator/beam/erl_driver.h @@ -133,7 +133,7 @@ typedef struct { #define ERL_DRV_EXTENDED_MARKER (0xfeeeeeed) #define ERL_DRV_EXTENDED_MAJOR_VERSION 2 -#define ERL_DRV_EXTENDED_MINOR_VERSION 1 +#define ERL_DRV_EXTENDED_MINOR_VERSION 2 /* * The emulator will refuse to load a driver with different major @@ -638,6 +638,8 @@ EXTERN int erl_drv_send_term(ErlDrvTermData port, int len); /* Async IO functions */ +EXTERN unsigned int driver_async_port_key(ErlDrvPort port); + EXTERN long driver_async(ErlDrvPort ix, unsigned int* key, void (*async_invoke)(void*), diff --git a/erts/emulator/beam/erl_ptab.c b/erts/emulator/beam/erl_ptab.c index d69619dd44..fa5482b841 100644 --- a/erts/emulator/beam/erl_ptab.c +++ b/erts/emulator/beam/erl_ptab.c @@ -433,7 +433,7 @@ erts_ptab_mem_size(ErtsPTab *ptab) { UWord size = ptab->r.o.max*sizeof(erts_smp_atomic_t); if (ptab->r.o.free_id_data) - size += ptab->r.o.max*sizeof(Uint32); + size += ptab->r.o.max*sizeof(erts_smp_atomic32_t); return size; } @@ -474,7 +474,7 @@ erts_ptab_init_table(ErtsPTab *ptab, tab_sz = ERTS_ALC_CACHE_LINE_ALIGN_SIZE(size*sizeof(erts_smp_atomic_t)); alloc_sz = tab_sz; if (!legacy) - alloc_sz += ERTS_ALC_CACHE_LINE_ALIGN_SIZE(size*sizeof(Uint32)); + alloc_sz += ERTS_ALC_CACHE_LINE_ALIGN_SIZE(size*sizeof(erts_smp_atomic32_t)); ptab->r.o.tab = erts_alloc_permanent_cache_aligned(atype, alloc_sz); tab_end = ((char *) ptab->r.o.tab) + tab_sz; tab_entry = ptab->r.o.tab; @@ -497,6 +497,10 @@ erts_ptab_init_table(ErtsPTab *ptab, ASSERT(ptab->r.o.pix_cl_shift + ptab->r.o.pix_cli_shift == bits); + ptab->r.o.invalid_element = invalid_element; + ptab->r.o.invalid_data = erts_ptab_id2data(ptab, invalid_element->id); + ptab->r.o.release_element = release_element; + if (legacy) { ptab->r.o.free_id_data = NULL; ptab->r.o.dix_cl_mask = 0; @@ -506,11 +510,11 @@ erts_ptab_init_table(ErtsPTab *ptab, } else { - tab_sz = ERTS_ALC_CACHE_LINE_ALIGN_SIZE(size*sizeof(Uint32)); - ptab->r.o.free_id_data = (Uint32 *) tab_end; + tab_sz = ERTS_ALC_CACHE_LINE_ALIGN_SIZE(size*sizeof(erts_smp_atomic32_t)); + ptab->r.o.free_id_data = (erts_smp_atomic32_t *) tab_end; tab_cache_lines = tab_sz/ERTS_CACHE_LINE_SIZE; - ix_per_cache_line = (ERTS_CACHE_LINE_SIZE/sizeof(Uint32)); + ix_per_cache_line = (ERTS_CACHE_LINE_SIZE/sizeof(erts_smp_atomic32_t)); ptab->r.o.dix_cl_mask = tab_cache_lines-1; ptab->r.o.dix_cl_shift = erts_fit_in_bits_int32(ix_per_cache_line-1); @@ -525,7 +529,9 @@ erts_ptab_init_table(ErtsPTab *ptab, ix = 0; for (cl = 0; cl < tab_cache_lines; cl++) { for (cli = 0; cli < ix_per_cache_line; cli++) { - ptab->r.o.free_id_data[ix] = cli*tab_cache_lines+cl; + erts_smp_atomic32_init_nob(&ptab->r.o.free_id_data[ix], + cli*tab_cache_lines+cl); + ASSERT(erts_smp_atomic32_read_nob(&ptab->r.o.free_id_data[ix]) != ptab->r.o.invalid_data); ix++; } } @@ -534,9 +540,6 @@ erts_ptab_init_table(ErtsPTab *ptab, erts_smp_atomic32_init_nob(&ptab->vola.tile.fid_ix, -1); } - ptab->r.o.invalid_element = invalid_element; - ptab->r.o.invalid_data = erts_ptab_id2data(ptab, invalid_element->id); - ptab->r.o.release_element = release_element; erts_smp_interval_init(&ptab->list.data.interval); ptab->list.data.deleted.start = NULL; @@ -606,11 +609,13 @@ erts_ptab_new_element(ErtsPTab *ptab, = erts_smp_current_interval_nob(erts_ptab_interval(ptab)); if (ptab->r.o.free_id_data) { + do { + ix = (Uint32) erts_smp_atomic32_inc_read_acqb(&ptab->vola.tile.aid_ix); + ix = ix_to_free_id_data_ix(ptab, ix); - ix = (Uint32) erts_smp_atomic32_inc_read_acqb(&ptab->vola.tile.aid_ix); - ix = ix_to_free_id_data_ix(ptab, ix); - - data = ptab->r.o.free_id_data[ix]; + data = erts_smp_atomic32_xchg_nob(&ptab->r.o.free_id_data[ix], + (erts_aint32_t)ptab->r.o.invalid_data); + }while ((Eterm)data == ptab->r.o.invalid_data); init_ptab_el(init_arg, (Eterm) data); @@ -763,7 +768,7 @@ erts_ptab_delete_element(ErtsPTab *ptab, erts_smp_atomic_set_relb(&ptab->r.o.tab[pix], ERTS_AINT_NULL); if (ptab->r.o.free_id_data) { - + Uint32 prev_data; /* Next data for this slot... */ data = (Uint32) erts_ptab_id2data(ptab, ptab_el->id); data += ptab->r.o.max; @@ -772,14 +777,17 @@ erts_ptab_delete_element(ErtsPTab *ptab, data += ptab->r.o.max; data &= ~(~((Uint32) 0) << ERTS_PTAB_ID_DATA_SIZE); } - ASSERT(data != ptab->r.o.invalid_data); ASSERT(pix == erts_ptab_data2pix(ptab, data)); - ix = (Uint32) erts_smp_atomic32_inc_read_relb(&ptab->vola.tile.fid_ix); - ix = ix_to_free_id_data_ix(ptab, ix); - - ptab->r.o.free_id_data[ix] = data; + do { + ix = (Uint32) erts_smp_atomic32_inc_read_relb(&ptab->vola.tile.fid_ix); + ix = ix_to_free_id_data_ix(ptab, ix); + + prev_data = erts_smp_atomic32_cmpxchg_nob(&ptab->r.o.free_id_data[ix], + data, + ptab->r.o.invalid_data); + }while ((Eterm)prev_data != ptab->r.o.invalid_data); } ASSERT(erts_smp_atomic32_read_nob(&ptab->vola.tile.count) > 0); @@ -1392,6 +1400,31 @@ erts_ptab_init(void) * Debug stuff */ +static void assert_ptab_consistency(ErtsPTab *ptab) +{ +#ifdef DEBUG + if (ptab->r.o.free_id_data) { + Uint32 ix, pix, data; + int free_pids = 0; + int null_slots = 0; + + for (ix=0; ix < ptab->r.o.max; ix++) { + if (erts_smp_atomic32_read_nob(&ptab->r.o.free_id_data[ix]) != ptab->r.o.invalid_data) { + ++free_pids; + data = erts_smp_atomic32_read_nob(&ptab->r.o.free_id_data[ix]); + pix = erts_ptab_data2pix(ptab, (Eterm) data); + ASSERT(erts_ptab_pix2intptr_nob(ptab, pix) == ERTS_AINT_NULL); + } + if (erts_smp_atomic_read_nob(&ptab->r.o.tab[ix]) == ERTS_AINT_NULL) { + ++null_slots; + } + } + ASSERT(free_pids == null_slots); + ASSERT(free_pids == ptab->r.o.max - erts_smp_atomic32_read_nob(&ptab->vola.tile.count)); + } +#endif +} + Sint erts_ptab_test_next_id(ErtsPTab *ptab, int set, Uint next) { @@ -1402,46 +1435,49 @@ erts_ptab_test_next_id(ErtsPTab *ptab, int set, Uint next) erts_ptab_rwlock(ptab); + assert_ptab_consistency(ptab); + if (ptab->r.o.free_id_data) { - Uint32 aid_ix, dix; + Uint32 id_ix, dix; if (set) { - Uint32 max_ix, ser, num, start; + Uint32 i, max_ix, num, stop_id_ix; max_ix = ptab->r.o.max - 1; - ser = next & ~max_ix; - start = num = next & max_ix; - - aid_ix = (Uint32) erts_smp_atomic32_read_nob(&ptab->vola.tile.aid_ix) + 1; - - do { - Uint32 pix = erts_ptab_data2pix(ptab, num); + num = next; + id_ix = (Uint32) erts_smp_atomic32_read_nob(&ptab->vola.tile.aid_ix); + + for (i=0; i <= max_ix; ++i) { + Uint32 pix; + ++num; + num &= ~(~((Uint32) 0) << ERTS_PTAB_ID_DATA_SIZE); + if (num == ptab->r.o.invalid_data) { + num += ptab->r.o.max; + num &= ~(~((Uint32) 0) << ERTS_PTAB_ID_DATA_SIZE); + } + pix = erts_ptab_data2pix(ptab, num); if (ERTS_AINT_NULL == erts_ptab_pix2intptr_nob(ptab, pix)) { - dix = ix_to_free_id_data_ix(ptab, aid_ix); - ptab->r.o.free_id_data[dix] = ser + num; - ASSERT(pix == erts_ptab_data2pix(ptab, ser+num)); - if (aid_ix == max_ix) - aid_ix = 0; - else - aid_ix++; + ++id_ix; + dix = ix_to_free_id_data_ix(ptab, id_ix); + erts_smp_atomic32_set_nob(&ptab->r.o.free_id_data[dix], num); + ASSERT(pix == erts_ptab_data2pix(ptab, num)); } - if (num == max_ix) - num = 0; - else - num++; - } while (num != start); + } + erts_smp_atomic32_set_nob(&ptab->vola.tile.fid_ix, id_ix); -#ifdef DEBUG - if (aid_ix == 0) - aid_ix = max_ix; - else - aid_ix--; - ASSERT((aid_ix & max_ix) == (((Uint32) erts_smp_atomic32_read_nob(&ptab->vola.tile.fid_ix)) & max_ix)); -#endif + /* Write invalid_data in rest of free_id_data[]: */ + stop_id_ix = (1 + erts_smp_atomic32_read_nob(&ptab->vola.tile.aid_ix)) & max_ix; + while (1) { + id_ix = (id_ix+1) & max_ix; + if (id_ix == stop_id_ix) + break; + dix = ix_to_free_id_data_ix(ptab, id_ix); + erts_smp_atomic32_set_nob(&ptab->r.o.free_id_data[dix], + ptab->r.o.invalid_data); + } } - - aid_ix = (Uint32) erts_smp_atomic32_read_nob(&ptab->vola.tile.aid_ix) + 1; - dix = ix_to_free_id_data_ix(ptab, aid_ix); - res = (Sint) ptab->r.o.free_id_data[dix]; + id_ix = (Uint32) erts_smp_atomic32_read_nob(&ptab->vola.tile.aid_ix) + 1; + dix = ix_to_free_id_data_ix(ptab, id_ix); + res = (Sint) erts_smp_atomic32_read_nob(&ptab->r.o.free_id_data[dix]); } else { /* Deprecated legacy algorithm... */ @@ -1485,6 +1521,7 @@ erts_ptab_test_next_id(ErtsPTab *ptab, int set, Uint next) } } + assert_ptab_consistency(ptab); erts_ptab_rwunlock(ptab); return res; diff --git a/erts/emulator/beam/erl_ptab.h b/erts/emulator/beam/erl_ptab.h index c2d8bd9cad..e3e05f14af 100644 --- a/erts/emulator/beam/erl_ptab.h +++ b/erts/emulator/beam/erl_ptab.h @@ -100,7 +100,7 @@ typedef struct { typedef struct { erts_smp_atomic_t *tab; - Uint32 *free_id_data; + erts_smp_atomic32_t *free_id_data; Uint32 max; Uint32 pix_mask; Uint32 pix_cl_mask; diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index cecfa8a0fd..bacd5a5752 100755 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -1018,9 +1018,10 @@ Eterm erts_match_set_lint(Process *p, Eterm matchexpr); extern void erts_match_set_release_result(Process* p); enum erts_pam_run_flags { - ERTS_PAM_TMP_RESULT=0, - ERTS_PAM_COPY_RESULT=1, - ERTS_PAM_CONTIGUOUS_TUPLE=2 + ERTS_PAM_TMP_RESULT=1, + ERTS_PAM_COPY_RESULT=2, + ERTS_PAM_CONTIGUOUS_TUPLE=4, + ERTS_PAM_IGNORE_TRACE_SILENT=8 }; extern Eterm erts_match_set_run(Process *p, Binary *mpsp, Eterm *args, int num_args, diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c index 595b0488a8..c997fe1bf9 100644 --- a/erts/emulator/drivers/common/efile_drv.c +++ b/erts/emulator/drivers/common/efile_drv.c @@ -546,53 +546,81 @@ static void *ef_safe_realloc(void *op, Uint s) (((char *)(ev)->iov[(q)].iov_base) + (p)) /* int EV_GET_CHAR(ErlIOVec *ev, char *p, int *pp, int *qp) */ -#define EV_GET_CHAR(ev, p, pp, qp) \ - (*(pp)+1 <= (ev)->iov[*(qp)].iov_len \ - ? (*(p) = *EV_CHAR_P(ev, *(pp), *(qp)), \ - *(pp) = ( *(pp)+1 < (ev)->iov[*(qp)].iov_len \ - ? *(pp)+1 \ - : ((*(qp))++, 0)), \ - !0) \ - : 0) +#define EV_GET_CHAR(ev, p, pp, qp) efile_ev_get_char(ev, p ,pp, qp) +static int +efile_ev_get_char(ErlIOVec *ev, char *p, int *pp, int *qp) { + if (*(pp)+1 <= (ev)->iov[*(qp)].iov_len) { + *(p) = *EV_CHAR_P(ev, *(pp), *(qp)); + if (*(pp)+1 < (ev)->iov[*(qp)].iov_len) + *(pp) = *(pp)+1; + else { + (*(qp))++; + *pp = 0; + } + return !0; + } + return 0; +} /* Uint32 EV_UINT32(ErlIOVec *ev, int p, int q)*/ #define EV_UINT32(ev, p, q) \ ((Uint32) *(((unsigned char *)(ev)->iov[(q)].iov_base) + (p))) /* int EV_GET_UINT32(ErlIOVec *ev, Uint32 *p, int *pp, int *qp) */ -#define EV_GET_UINT32(ev, p, pp, qp) \ - (*(pp)+4 <= (ev)->iov[*(qp)].iov_len \ - ? (*(p) = (EV_UINT32(ev, *(pp), *(qp)) << 24) \ - | (EV_UINT32(ev, *(pp)+1, *(qp)) << 16) \ - | (EV_UINT32(ev, *(pp)+2, *(qp)) << 8) \ - | (EV_UINT32(ev, *(pp)+3, *(qp))), \ - *(pp) = ( *(pp)+4 < (ev)->iov[*(qp)].iov_len \ - ? *(pp)+4 \ - : ((*(qp))++, 0)), \ - !0) \ - : 0) +#define EV_GET_UINT32(ev, p, pp, qp) efile_ev_get_uint32(ev,p,pp,qp) +static int +efile_ev_get_uint32(ErlIOVec *ev, Uint32 *p, int *pp, int *qp) { + if (*(pp)+4 <= (ev)->iov[*(qp)].iov_len) { + *(p) = (EV_UINT32(ev, *(pp), *(qp)) << 24) + | (EV_UINT32(ev, *(pp)+1, *(qp)) << 16) + | (EV_UINT32(ev, *(pp)+2, *(qp)) << 8) + | (EV_UINT32(ev, *(pp)+3, *(qp))); + if (*(pp)+4 < (ev)->iov[*(qp)].iov_len) + *(pp) = *(pp)+4; + else { + (*(qp))++; + *pp = 0; + } + return !0; + } + return 0; +} /* Uint64 EV_UINT64(ErlIOVec *ev, int p, int q)*/ #define EV_UINT64(ev, p, q) \ ((Uint64) *(((unsigned char *)(ev)->iov[(q)].iov_base) + (p))) -/* int EV_GET_UINT64(ErlIOVec *ev, Uint32 *p, int *pp, int *qp) */ -#define EV_GET_UINT64(ev, p, pp, qp) \ - (*(pp)+8 <= (ev)->iov[*(qp)].iov_len \ - ? (*(p) = (EV_UINT64(ev, *(pp), *(qp)) << 56) \ - | (EV_UINT64(ev, *(pp)+1, *(qp)) << 48) \ - | (EV_UINT64(ev, *(pp)+2, *(qp)) << 40) \ - | (EV_UINT64(ev, *(pp)+3, *(qp)) << 32) \ - | (EV_UINT64(ev, *(pp)+4, *(qp)) << 24) \ - | (EV_UINT64(ev, *(pp)+5, *(qp)) << 16) \ - | (EV_UINT64(ev, *(pp)+6, *(qp)) << 8) \ - | (EV_UINT64(ev, *(pp)+7, *(qp))), \ - *(pp) = ( *(pp)+8 < (ev)->iov[*(qp)].iov_len \ - ? *(pp)+8 \ - : ((*(qp))++, 0)), \ - !0) \ - : 0) +/* int EV_GET_UINT64(ErlIOVec *ev, Uint64 *p, int *pp, int *qp) */ +#define EV_GET_UINT64(ev, p, pp, qp) efile_ev_get_uint64(ev,p,pp,qp) +static int +efile_ev_get_uint64(ErlIOVec *ev, Uint64 *p, int *pp, int *qp) { + if (*(pp)+8 <= (ev)->iov[*(qp)].iov_len) { + *(p) = (EV_UINT64(ev, *(pp), *(qp)) << 56) + | (EV_UINT64(ev, *(pp)+1, *(qp)) << 48) + | (EV_UINT64(ev, *(pp)+2, *(qp)) << 40) + | (EV_UINT64(ev, *(pp)+3, *(qp)) << 32) + | (EV_UINT64(ev, *(pp)+4, *(qp)) << 24) + | (EV_UINT64(ev, *(pp)+5, *(qp)) << 16) + | (EV_UINT64(ev, *(pp)+6, *(qp)) << 8) + | (EV_UINT64(ev, *(pp)+7, *(qp))); + if (*(pp)+8 < (ev)->iov[*(qp)].iov_len) + *(pp) = *(pp)+8; + else { + (*(qp))++; + *pp = 0; + } + return !0; + } + return 0; +} +/* int EV_GET_SINT64(ErlIOVec *ev, Uint64 *p, int *pp, int *qp) */ +#define EV_GET_SINT64(ev, p, pp, qp) efile_ev_get_sint64(ev,p,pp,qp) +static int +efile_ev_get_sint64(ErlIOVec *ev, Sint64 *p, int *pp, int *qp) { + Uint64 *tmp = (Uint64*)p; + return EV_GET_UINT64(ev,tmp,pp,qp); +} #if 0 @@ -744,6 +772,7 @@ file_init(void) return 0; } + /********************************************************************* * Driver entry point -> start */ @@ -760,7 +789,7 @@ file_start(ErlDrvPort port, char* command) } desc->fd = FILE_FD_INVALID; desc->port = port; - desc->key = (unsigned int) (UWord) port; + desc->key = driver_async_port_key(port); desc->flags = 0; desc->invoke = NULL; desc->d = NULL; @@ -3105,25 +3134,25 @@ file_flush(ErlDrvData e) { /********************************************************************* * Driver entry point -> control + * Only debug functionality... */ static ErlDrvSSizeT file_control(ErlDrvData e, unsigned int command, char* buf, ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen) { - /* - * warning: variable ‘desc’ set but not used - * [-Wunused-but-set-variable] - * ... no kidding ... - * - * file_descriptor *desc = (file_descriptor *)e; switch (command) { + case 'K' : + if (rlen < 4) { + *rbuf = EF_ALLOC(4); + } + (*rbuf)[0] = ((desc->key) >> 24) & 0xFF; + (*rbuf)[1] = ((desc->key) >> 16) & 0xFF; + (*rbuf)[2] = ((desc->key) >> 8) & 0xFF; + (*rbuf)[3] = (desc->key) & 0xFF; + return 4; default: return 0; - } - ASSERT(0); - desc = NULL; - */ - return 0; + } } /********************************************************************* @@ -3604,7 +3633,7 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { for(i = 0; i < n; i++) { Uint32 sizeH, sizeL; size_t size; - if ( !EV_GET_UINT64(ev, &d->c.pwritev.specs[i].offset, &p, &q) + if ( !EV_GET_SINT64(ev, &d->c.pwritev.specs[i].offset, &p, &q) || !EV_GET_UINT32(ev, &sizeH, &p, &q) || !EV_GET_UINT32(ev, &sizeL, &p, &q)) { /* Misalignment in buffer */ @@ -3746,7 +3775,7 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { for (i = 1; i < 1+n; i++) { Uint32 sizeH, sizeL; size_t size; - if ( !EV_GET_UINT64(ev, &d->c.preadv.offsets[i-1], &p, &q) + if ( !EV_GET_SINT64(ev, &d->c.preadv.offsets[i-1], &p, &q) || !EV_GET_UINT32(ev, &sizeH, &p, &q) || !EV_GET_UINT32(ev, &sizeL, &p, &q)) { reply_posix_error(desc, EINVAL); @@ -3814,7 +3843,7 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { Uint32 origin; /* Origin of seek. */ if (ev->size < 1+8+4 - || !EV_GET_UINT64(ev, &offset, &p, &q) + || !EV_GET_SINT64(ev, &offset, &p, &q) || !EV_GET_UINT32(ev, &origin, &p, &q)) { /* Wrong length of buffer to contain offset and origin */ reply_posix_error(desc, EINVAL); @@ -3927,7 +3956,7 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) { goto done; } if (ev->size < 1+1+8+4 - || !EV_GET_UINT64(ev, &hdr_offset, &p, &q) + || !EV_GET_SINT64(ev, &hdr_offset, &p, &q) || !EV_GET_UINT32(ev, &max_size, &p, &q)) { /* Buffer too short to contain * the header offset and max size spec */ diff --git a/erts/emulator/drivers/win32/win_efile.c b/erts/emulator/drivers/win32/win_efile.c index be3d86a1d2..b36a103f8e 100644 --- a/erts/emulator/drivers/win32/win_efile.c +++ b/erts/emulator/drivers/win32/win_efile.c @@ -772,6 +772,7 @@ efile_may_openfile(Efile_error* errInfo, char *name) { DWORD attr; if ((attr = GetFileAttributesW(wname)) == INVALID_FILE_ATTRIBUTES) { + errno = ENOENT; return check_error(-1, errInfo); } diff --git a/erts/emulator/sys/win32/erl_win_dyn_driver.h b/erts/emulator/sys/win32/erl_win_dyn_driver.h index a7c53c904d..b9a9838a36 100644 --- a/erts/emulator/sys/win32/erl_win_dyn_driver.h +++ b/erts/emulator/sys/win32/erl_win_dyn_driver.h @@ -80,6 +80,7 @@ WDD_TYPEDEF(int, erl_drv_output_term, (ErlDrvTermData, ErlDrvTermData*, int)); WDD_TYPEDEF(int, driver_output_term, (ErlDrvPort, ErlDrvTermData*, int)); WDD_TYPEDEF(int, erl_drv_send_term, (ErlDrvTermData, ErlDrvTermData, ErlDrvTermData*, int)); WDD_TYPEDEF(int, driver_send_term, (ErlDrvPort, ErlDrvTermData, ErlDrvTermData*, int)); +WDD_TYPEDEF(unsigned int, driver_async_port_key, (ErlDrvPort)); WDD_TYPEDEF(long, driver_async, (ErlDrvPort,unsigned int*,void (*)(void*),void*,void (*)(void*))); WDD_TYPEDEF(int, driver_async_cancel, (unsigned int)); WDD_TYPEDEF(int, driver_lock_driver, (ErlDrvPort)); @@ -197,6 +198,7 @@ typedef struct { WDD_FTYPE(driver_output_term) *driver_output_term; WDD_FTYPE(erl_drv_send_term) *erl_drv_send_term; WDD_FTYPE(driver_send_term) *driver_send_term; + WDD_FTYPE(driver_async_port_key) *driver_async_port_key; WDD_FTYPE(driver_async) *driver_async; WDD_FTYPE(driver_async_cancel) *driver_async_cancel; WDD_FTYPE(driver_lock_driver) *driver_lock_driver; @@ -308,6 +310,7 @@ extern TWinDynDriverCallbacks WinDynDriverCallbacks; #define driver_output_term (WinDynDriverCallbacks.driver_output_term) #define erl_drv_send_term (WinDynDriverCallbacks.erl_drv_send_term) #define driver_send_term (WinDynDriverCallbacks.driver_send_term) +#define driver_async_port_key (WinDynDriverCallbacks.driver_async_port_key) #define driver_async (WinDynDriverCallbacks.driver_async) #define driver_async_cancel (WinDynDriverCallbacks.driver_async_cancel) #define driver_lock_driver (WinDynDriverCallbacks.driver_lock_driver) @@ -443,6 +446,7 @@ do { \ ((W).driver_output_term) = driver_output_term; \ ((W).erl_drv_send_term) = erl_drv_send_term; \ ((W).driver_send_term) = driver_send_term; \ +((W).driver_async_port_key) = driver_async_port_key; \ ((W).driver_async) = driver_async; \ ((W).driver_async_cancel) = driver_async_cancel; \ ((W).driver_lock_driver) = driver_lock_driver; \ diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile index 9594ab48b1..f02ca3cb98 100644 --- a/erts/emulator/test/Makefile +++ b/erts/emulator/test/Makefile @@ -140,7 +140,8 @@ EMAKEFILE=Emakefile TEST_SPEC_FILES= emulator.spec \ emulator.spec.win \ - emulator_bench.spec + emulator_bench.spec \ + emulator_smoke.spec # ---------------------------------------------------- # Release directory specification diff --git a/erts/emulator/test/efile_SUITE.erl b/erts/emulator/test/efile_SUITE.erl index 65367eab98..f79bb761d1 100644 --- a/erts/emulator/test/efile_SUITE.erl +++ b/erts/emulator/test/efile_SUITE.erl @@ -19,16 +19,16 @@ -module(efile_SUITE). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2]). --export([iter_max_files/1]). +-export([iter_max_files/1, async_dist/1]). --export([do_iter_max_files/2]). +-export([do_iter_max_files/2, do_async_dist/1]). -include_lib("test_server/include/test_server.hrl"). suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [iter_max_files]. + [iter_max_files, async_dist]. groups() -> []. @@ -45,6 +45,84 @@ init_per_group(_GroupName, Config) -> end_per_group(_GroupName, Config) -> Config. +do_async_dist(Dir) -> + X = 100, + AT = erlang:system_info(thread_pool_size), + Keys = file_keys(Dir,AT*X,[],[]), + Tab = ets:new(x,[ordered_set]), + [ ets:insert(Tab,{N,0}) || N <- lists:seq(0,AT-1) ], + [ ets:update_counter(Tab,(N rem AT),1) || N <- Keys ], + Res = [ V || {_,V} <- ets:tab2list(Tab) ], + ets:delete(Tab), + {Res, sdev(Res)/X}. + +sdev(List) -> + Len = length(List), + Mean = lists:sum(List)/Len, + math:sqrt(lists:sum([ (X - Mean) * (X - Mean) || X <- List ]) / Len). + +file_keys(_,0,FdList,FnList) -> + [ file:close(FD) || FD <- FdList ], + [ file:delete(FN) || FN <- FnList ], + []; +file_keys(Dir,Num,FdList,FnList) -> + Name = "dummy"++integer_to_list(Num), + FN = filename:join([Dir,Name]), + case file:open(FN,[write,raw]) of + {ok,FD} -> + {file_descriptor,prim_file,{Port,_}} = FD, + <<X:32/integer-big>> = + iolist_to_binary(erlang:port_control(Port,$K,[])), + [X | file_keys(Dir,Num-1,[FD|FdList],[FN|FnList])]; + {error,_} -> + % Try freeing up FD's if there are any + case FdList of + [] -> + exit({cannot_open_file,FN}); + _ -> + [ file:close(FD) || FD <- FdList ], + [ file:delete(F) || F <- FnList ], + file_keys(Dir,Num,[],[]) + end + end. + +async_dist(doc) -> + "Check that the distribution of files over async threads is fair"; +async_dist(Config) when is_list(Config) -> + DataDir = ?config(data_dir,Config), + TestFile = filename:join(DataDir, "existing_file"), + Dir = filename:dirname(code:which(?MODULE)), + AsyncSizes = [7,10,100,255,256,64,63,65], + Max = 0.5, + + lists:foreach(fun(Size) -> + {ok,Node} = + test_server:start_node + (test_iter_max_files,slave, + [{args, + "+A "++integer_to_list(Size)++ + " -pa " ++ Dir}]), + {Distr,SD} = rpc:call(Node,?MODULE,do_async_dist, + [DataDir]), + test_server:stop_node(Node), + if + SD > Max -> + io:format("Bad async queue distribution for " + "~p async threads:~n" + " Standard deviation is ~p~n" + " Key distribution:~n ~lp~n", + [Size,SD,Distr]), + exit({bad_async_dist,Size,SD,Distr}); + true -> + io:format("OK async queue distribution for " + "~p async threads:~n" + " Standard deviation is ~p~n" + " Key distribution:~n ~lp~n", + [Size,SD,Distr]), + ok + end + end, AsyncSizes), + ok. %% %% Open as many files as possible. Do this several times and check @@ -98,7 +176,7 @@ open_files(Name) -> ?line case file:open(Name, [read,raw]) of {ok, Fd} -> [Fd| open_files(Name)]; - {error, Reason} -> -% io:format("Error reason: ~p", [Reason]), + {error, _Reason} -> +% io:format("Error reason: ~p", [_Reason]), [] end. diff --git a/erts/emulator/test/emulator_smoke.spec b/erts/emulator/test/emulator_smoke.spec new file mode 100644 index 0000000000..3219aeb823 --- /dev/null +++ b/erts/emulator/test/emulator_smoke.spec @@ -0,0 +1,3 @@ +{suites,"../emulator_test",[smoke_test_SUITE,time_SUITE]}. +{cases,"../emulator_test",crypto_SUITE,[t_md5]}. +{cases,"../emulator_test",float_SUITE,[fpe,cmp_integer]}.
\ No newline at end of file diff --git a/erts/emulator/test/match_spec_SUITE.erl b/erts/emulator/test/match_spec_SUITE.erl index 8dbc6b6538..b56b7ce525 100644 --- a/erts/emulator/test/match_spec_SUITE.erl +++ b/erts/emulator/test/match_spec_SUITE.erl @@ -22,7 +22,7 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, not_run/1]). -export([test_1/1, test_2/1, test_3/1, bad_match_spec_bin/1, - trace_control_word/1, silent/1, silent_no_ms/1, + trace_control_word/1, silent/1, silent_no_ms/1, silent_test/1, ms_trace2/1, ms_trace3/1, boxed_and_small/1, destructive_in_test_bif/1, guard_exceptions/1, unary_plus/1, unary_minus/1, moving_labels/1]). @@ -55,7 +55,7 @@ all() -> case test_server:is_native(match_spec_SUITE) of false -> [test_1, test_2, test_3, bad_match_spec_bin, - trace_control_word, silent, silent_no_ms, ms_trace2, + trace_control_word, silent, silent_no_ms, silent_test, ms_trace2, ms_trace3, boxed_and_small, destructive_in_test_bif, guard_exceptions, unary_plus, unary_minus, fpe, moving_labels, @@ -501,6 +501,14 @@ silent_no_ms(Config) when is_list(Config) -> {trace,Tracee,return_to,{?MODULE,f3,2}}] end). +silent_test(doc) -> + ["Test that match_spec_test does not activate silent"]; +silent_test(_Config) -> + {flags,[]} = erlang:trace_info(self(),flags), + erlang:match_spec_test([],[{'_',[],[{silent,true}]}],trace), + {flags,[]} = erlang:trace_info(self(),flags). + + ms_trace2(doc) -> ["Test the match spec functions {trace/2}"]; ms_trace2(suite) -> []; diff --git a/erts/etc/Makefile b/erts/etc/Makefile index 2b32b8ae50..5b54ef9c3e 100644 --- a/erts/etc/Makefile +++ b/erts/etc/Makefile @@ -18,10 +18,11 @@ # include $(ERL_TOP)/make/target.mk - SUB_DIRECTORIES = common ifeq ($(TARGET),win32) SUB_DIRECTORIES += win32 +else +SUB_DIRECTORIES += unix endif include $(ERL_TOP)/make/otp_subdir.mk diff --git a/erts/etc/unix/Makefile b/erts/etc/unix/Makefile new file mode 100644 index 0000000000..e85d2fab0c --- /dev/null +++ b/erts/etc/unix/Makefile @@ -0,0 +1,46 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2013. All Rights Reserved. +# +# The contents of this file are subject to the Erlang Public License, +# Version 1.1, (the "License"); you may not use this file except in +# compliance with the License. You should have received a copy of the +# Erlang Public License along with this software. If not, it can be +# retrieved online at http://www.erlang.org/. +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# %CopyrightEnd% +# + +include $(ERL_TOP)/make/output.mk +include $(ERL_TOP)/make/target.mk + +include $(ERL_TOP)/make/$(TARGET)/otp.mk +include ../../vsn.mk + +opt debug: etc + +.PHONY: etc +etc: etp-commands + +etp-commands: etp-commands.in + sed 's:@ERL_TOP@:${ERL_TOP}:g' etp-commands.in > etp-commands + +.PHONY: docs +docs: + +.PHONY: clean +clean: + +# ---------------------------------------------------- +# Release Target +# ---------------------------------------------------- +include $(ERL_TOP)/make/otp_release_targets.mk + +.PHONY: release_spec +release_spec: etc
\ No newline at end of file diff --git a/erts/etc/unix/etp-commands b/erts/etc/unix/etp-commands.in index 35f75df5c1..54ff7b3e3a 100644 --- a/erts/etc/unix/etp-commands +++ b/erts/etc/unix/etp-commands.in @@ -2757,6 +2757,10 @@ document etp-run %--------------------------------------------------------------------------- end +define etp-thr + source @ERL_TOP@/erts/etc/unix/etp-thr.py +end + ############################################################################ # Toolbox parameter handling # diff --git a/erts/etc/unix/etp-thr.py b/erts/etc/unix/etp-thr.py new file mode 100644 index 0000000000..64fb858d20 --- /dev/null +++ b/erts/etc/unix/etp-thr.py @@ -0,0 +1,55 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2013. All Rights Reserved. +# +# The contents of this file are subject to the Erlang Public License, +# Version 1.1, (the "License"); you may not use this file except in +# compliance with the License. You should have received a copy of the +# Erlang Public License along with this software. If not, it can be +# retrieved online at http://www.erlang.org/. +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# %CopyrightEnd% +# + +def get_thread_name(t): + f = gdb.newest_frame(); + while f: + if f.name() == "async_main": + return "async"; + elif f.name() == "erts_sys_main_thread": + return "main"; + elif f.name() == "signal_dispatcher_thread_func": + return "signal_dispatcher"; + elif f.name() == "sys_msg_dispatcher_func": + return "sys_msg_dispatcher"; + elif f.name() == "child_waiter": + return "child_waiter"; + elif f.name() == "sched_thread_func": + return "scheduler"; + elif f.name() == "aux_thread": + return "aux"; + f = f.older(); + return "unknown"; + + +curr_thread = gdb.selected_thread(); + +for i in gdb.inferiors(): + gdb.write(" Id Thread Name Frame\n"); + for t in i.threads(): + t.switch(); + if curr_thread == t: + gdb.write("*"); + else: + gdb.write(" "); + gdb.write("{0:<3} {1:20} {2}\n".format( + t.num,get_thread_name(t), + gdb.newest_frame().name())); + +curr_thread.switch(); diff --git a/erts/include/internal/ethread.h b/erts/include/internal/ethread.h index 407097d4b1..38b8e9e9b6 100644 --- a/erts/include/internal/ethread.h +++ b/erts/include/internal/ethread.h @@ -361,6 +361,10 @@ extern ethr_runtime_t ethr_runtime__; # endif #endif /* !ETHR_DISABLE_NATIVE_IMPLS */ +#if !defined(ETHR_HAVE_NATIVE_ATOMIC32) && !defined(ETHR_HAVE_NATIVE_ATOMIC64) && !defined(ETHR_DISABLE_NATIVE_IMPLS) && defined(ETHR_SMP_REQUIRE_NATIVE_IMPLS) +#error "No native ethread implementation found. If you want to use fallbacks you have to disable native ethread support with configure." +#endif + #include "ethr_atomics.h" /* The atomics API */ #if defined(__GNUC__) diff --git a/erts/include/internal/gcc/ethread.h b/erts/include/internal/gcc/ethread.h index fcfdc39441..365a3535cf 100644 --- a/erts/include/internal/gcc/ethread.h +++ b/erts/include/internal/gcc/ethread.h @@ -25,6 +25,9 @@ #ifndef ETHREAD_GCC_H__ #define ETHREAD_GCC_H__ +#if defined(ETHR_HAVE___SYNC_VAL_COMPARE_AND_SWAP32) \ + || defined(ETHR_HAVE___SYNC_VAL_COMPARE_AND_SWAP64) + #ifndef ETHR_MEMBAR # include "ethr_membar.h" #endif @@ -46,3 +49,5 @@ #endif #endif + +#endif diff --git a/erts/test/Makefile b/erts/test/Makefile index 6b409e2f1b..74a5bb1ccc 100644 --- a/erts/test/Makefile +++ b/erts/test/Makefile @@ -79,7 +79,7 @@ release_spec: release_tests_spec: opt $(INSTALL_DIR) "$(RELSYSDIR)" - $(INSTALL_DATA) system.spec system.dynspec \ + $(INSTALL_DATA) system.spec system.dynspec system_smoke.spec \ $(ERL_FILES) $(TARGET_FILES) "$(RELSYSDIR)" chmod -R u+w "$(RELSYSDIR)" tar cf - *_SUITE_data utils | (cd "$(RELSYSDIR)"; tar xf -) diff --git a/erts/test/erlc_SUITE.erl b/erts/test/erlc_SUITE.erl index ab774dbc4f..ed7a43c7e7 100644 --- a/erts/test/erlc_SUITE.erl +++ b/erts/test/erlc_SUITE.erl @@ -23,7 +23,8 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, compile_erl/1, compile_yecc/1, compile_script/1, - compile_mib/1, good_citizen/1, deep_cwd/1, arg_overflow/1]). + compile_mib/1, good_citizen/1, deep_cwd/1, arg_overflow/1, + make_dep_options/1]). -include_lib("test_server/include/test_server.hrl"). @@ -31,7 +32,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [compile_erl, compile_yecc, compile_script, compile_mib, - good_citizen, deep_cwd, arg_overflow]. + good_citizen, deep_cwd, arg_overflow, make_dep_options]. groups() -> []. @@ -255,13 +256,89 @@ erlc() -> Erlc -> "\"" ++ Erlc ++ "\"" end. - + +make_dep_options(Config) -> + {SrcDir,OutDir,Cmd} = get_cmd(Config), + FileName = filename:join(SrcDir, "erl_test_ok.erl"), + + + DepRE = ["/erl_test_ok[.]beam: \\\\$", + "/system_test/erlc_SUITE_data/src/erl_test_ok[.]erl \\\\$", + "/system_test/erlc_SUITE_data/include/erl_test[.]hrl$", + "_OK_"], + + DepRETarget = + ["^target: \\\\$", + "/system_test/erlc_SUITE_data/src/erl_test_ok[.]erl \\\\$", + "/system_test/erlc_SUITE_data/include/erl_test[.]hrl$", + "_OK_"], + + DepREMP = + ["/erl_test_ok[.]beam: \\\\$", + "/system_test/erlc_SUITE_data/src/erl_test_ok[.]erl \\\\$", + "/system_test/erlc_SUITE_data/include/erl_test[.]hrl$", + [], + "/system_test/erlc_SUITE_data/include/erl_test.hrl:$", + "_OK_"], + + DepREMissing = + ["/erl_test_missing_header[.]beam: \\\\$", + "/system_test/erlc_SUITE_data/src/erl_test_missing_header[.]erl \\\\$", + "/system_test/erlc_SUITE_data/include/erl_test[.]hrl \\\\$", + "missing.hrl$", + "_OK_"], + + %% Test plain -M + run(Config, Cmd, FileName, "-M", DepRE), + + %% Test -MF File + DepFile = filename:join(OutDir, "my.deps"), + run(Config, Cmd, FileName, "-MF "++DepFile, ["_OK_"]), + {ok,MFBin} = file:read_file(DepFile), + verify_result(binary_to_list(MFBin)++["_OK_"], DepRE), + + %% Test -MD + run(Config, Cmd, FileName, "-MD", ["_OK_"]), + MDFile = filename:join(OutDir, "erl_test_ok.Pbeam"), + {ok,MFBin} = file:read_file(MDFile), + + %% Test -M -MT Target + run(Config, Cmd, FileName, "-M -MT target", DepRETarget), + + %% Test -MF File -MT Target + TargetDepFile = filename:join(OutDir, "target.deps"), + run(Config, Cmd, FileName, "-MF "++TargetDepFile++" -MT target", + ["_OK_"]), + {ok,TargetBin} = file:read_file(TargetDepFile), + verify_result(binary_to_list(TargetBin)++["_OK_"], DepRETarget), + + %% Test -MD -MT Target + run(Config, Cmd, FileName, "-MD -MT target", ["_OK_"]), + TargetMDFile = filename:join(OutDir, "erl_test_ok.Pbeam"), + {ok,TargetBin} = file:read_file(TargetMDFile), + + %% Test -M -MQ Target. (Note: Passing a $ on the command line + %% portably for Unix and Windows is tricky, so we will just test + %% that MQ works at all.) + run(Config, Cmd, FileName, "-M -MQ target", DepRETarget), + + %% Test -M -MP + run(Config, Cmd, FileName, "-M -MP", DepREMP), + + %% Test -M -MG + MissingHeader = filename:join(SrcDir, "erl_test_missing_header.erl"), + run(Config, Cmd, MissingHeader, "-M -MG", DepREMissing), + ok. + %% Runs a command. run(Config, Cmd0, Name, Options, Expect) -> Cmd = Cmd0 ++ " " ++ Options ++ " " ++ Name, io:format("~s", [Cmd]), Result = run_command(Config, Cmd), + verify_result(Result, Expect). + +verify_result(Result, Expect) -> Messages = split(Result, [], []), io:format("Result: ~p", [Messages]), io:format("Expected: ~p", [Expect]), diff --git a/erts/test/erlc_SUITE_data/src/erl_test_missing_header.erl b/erts/test/erlc_SUITE_data/src/erl_test_missing_header.erl new file mode 100644 index 0000000000..4d6c42c803 --- /dev/null +++ b/erts/test/erlc_SUITE_data/src/erl_test_missing_header.erl @@ -0,0 +1,22 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2013. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +-module(erl_test_missing_header). +-include("erl_test.hrl"). +-include("missing.hrl"). diff --git a/erts/test/system_smoke.spec b/erts/test/system_smoke.spec new file mode 100644 index 0000000000..933d1ba22d --- /dev/null +++ b/erts/test/system_smoke.spec @@ -0,0 +1,3 @@ +{suites,"../system_test",[ethread_SUITE]}. +{cases,"../system_test",otp_SUITE,[undefined_functions]}. +{skip_cases,"../system_test",ethread_SUITE,[max_threads],"Skip"}. diff --git a/lib/common_test/src/ct_logs.erl b/lib/common_test/src/ct_logs.erl index f5355bfefe..bd37b690b6 100644 --- a/lib/common_test/src/ct_logs.erl +++ b/lib/common_test/src/ct_logs.erl @@ -446,6 +446,8 @@ tc_print(Category,Importance,Format,Args) -> ct_util:get_verbosity('$unspecified'); {error,bad_invocation} -> ?MAX_VERBOSITY; + {error,_Failure} -> + ?MAX_VERBOSITY; Val -> Val end, @@ -690,14 +692,15 @@ logger_loop(State) -> false -> %% Group leader is dead, so write to the %% CtLog or unexpected_io log instead - unexpected_io(Pid,Category,List,State), + unexpected_io(Pid,Category,Importance, + List,State), logger_loop(State) end; {ct_log,_Fd,TCGLs} -> %% If category is ct_internal then write %% to ct_log, else write to unexpected_io %% log - unexpected_io(Pid,Category,List,State), + unexpected_io(Pid,Category,Importance,List,State), logger_loop(State#logger_state{ tc_groupleaders = TCGLs}) end; @@ -798,7 +801,7 @@ print_to_log(sync, FromPid, Category, TCGL, List, State) -> IoFun = create_io_fun(FromPid, State), io:format(TCGL,"~ts", [lists:foldl(IoFun, [], List)]); true -> - unexpected_io(FromPid,Category,List,State) + unexpected_io(FromPid,Category,?MAX_IMPORTANCE,List,State) end, State; @@ -814,7 +817,8 @@ print_to_log(async, FromPid, Category, TCGL, List, State) -> end; true -> fun() -> - unexpected_io(FromPid,Category,List,State) + unexpected_io(FromPid,Category,?MAX_IMPORTANCE, + List,State) end end, case State#logger_state.async_print_jobs of @@ -3066,10 +3070,20 @@ html_encoding(latin1) -> html_encoding(utf8) -> "utf-8". -unexpected_io(Pid,ct_internal,List,#logger_state{ct_log_fd=Fd}=State) -> +unexpected_io(Pid,ct_internal,_Importance,List,State) -> IoFun = create_io_fun(Pid,State), - io:format(Fd, "~ts", [lists:foldl(IoFun, [], List)]); -unexpected_io(Pid,_Category,List,State) -> + io:format(State#logger_state.ct_log_fd, "~ts", + [lists:foldl(IoFun, [], List)]); +unexpected_io(Pid,Category,Importance,List,State) -> IoFun = create_io_fun(Pid,State), Data = io_lib:format("~ts", [lists:foldl(IoFun, [], List)]), - test_server_io:print_unexpected(Data). + %% if unexpected io comes in during startup or shutdown, test_server + %% might not be running - if so (noproc exit), simply print to + %% stdout instead (will result in double printouts when pal is used) + try test_server_io:print_unexpected(Data) of + _ -> + ok + catch + _:{noproc,_} -> tc_print(Category,Importance,Data,[]); + _:Reason -> exit(Reason) + end. diff --git a/lib/common_test/src/ct_util.erl b/lib/common_test/src/ct_util.erl index 68e76c2396..abda87c2cd 100644 --- a/lib/common_test/src/ct_util.erl +++ b/lib/common_test/src/ct_util.erl @@ -286,14 +286,23 @@ get_start_dir() -> %% handle verbosity outside ct_util_server (let the client read %% the verbosity table) to avoid possible deadlock situations set_verbosity(Elem = {_Category,_Level}) -> - ets:insert(?verbosity_table, Elem), - ok. + try ets:insert(?verbosity_table, Elem) of + _ -> + ok + catch + _:Reason -> + {error,Reason} + end. + get_verbosity(Category) -> - case ets:lookup(?verbosity_table, Category) of + try ets:lookup(?verbosity_table, Category) of [{Category,Level}] -> Level; _ -> undefined + catch + _:Reason -> + {error,Reason} end. loop(Mode,TestData,StartDir) -> diff --git a/lib/common_test/test/ct_test_support.erl b/lib/common_test/test/ct_test_support.erl index 6bcac12326..4132995bf6 100644 --- a/lib/common_test/test/ct_test_support.erl +++ b/lib/common_test/test/ct_test_support.erl @@ -36,6 +36,8 @@ verify_events/3, verify_events/4, reformat/2, log_events/4, join_abs_dirs/2]). +-export([start_slave/3, slave_stop/1]). + -export([ct_test_halt/1]). -include_lib("kernel/include/file.hrl"). @@ -66,10 +68,14 @@ init_per_suite(Config, Level) -> start_slave(Config, Level). -start_slave(Config,Level) -> +start_slave(Config, Level) -> + start_slave(ct, Config, Level). + +start_slave(NodeName, Config, Level) -> [_,Host] = string:tokens(atom_to_list(node()), "@"), - test_server:format(0, "Trying to start ~s~n", ["ct@"++Host]), - case slave:start(Host, ct, []) of + test_server:format(0, "Trying to start ~s~n", + [atom_to_list(NodeName)++"@"++Host]), + case slave:start(Host, NodeName, []) of {error,Reason} -> test_server:fail(Reason); {ok,CTNode} -> @@ -77,7 +83,7 @@ start_slave(Config,Level) -> IsCover = test_server:is_cover(), if IsCover -> cover:start(CTNode); - true-> + true -> ok end, diff --git a/lib/common_test/test/ct_verbosity_SUITE.erl b/lib/common_test/test/ct_verbosity_SUITE.erl index 32488b1db9..1aa71953ec 100644 --- a/lib/common_test/test/ct_verbosity_SUITE.erl +++ b/lib/common_test/test/ct_verbosity_SUITE.erl @@ -53,9 +53,19 @@ init_per_suite(Config) -> end_per_suite(Config) -> ct_test_support:end_per_suite(Config). +init_per_testcase(no_crashing, Config) -> + Opts = ct_test_support:start_slave(ctX, Config, 50), + XNode = proplists:get_value(ct_node, Opts), + ct:pal("Node ~p started!", [XNode]), + [{xnode,XNode} | Config]; init_per_testcase(TestCase, Config) -> ct_test_support:init_per_testcase(TestCase, Config). +end_per_testcase(no_crashing, Config) -> + XNode = proplists:get_value(xnode, Config), + ct_test_support:slave_stop(XNode), + ct:pal("Node ~p stopped!", [XNode]), + ok; end_per_testcase(TestCase, Config) -> ct_test_support:end_per_testcase(TestCase, Config). @@ -72,7 +82,8 @@ all() -> combine_categories, testspec_only, merge_with_testspec, - possible_deadlock + possible_deadlock, + no_crashing ]. %%-------------------------------------------------------------------- @@ -189,6 +200,19 @@ possible_deadlock(Config) -> %%%----------------------------------------------------------------- +%%% +no_crashing(Config) -> + XNode = proplists:get_value(xnode, Config), + ok = rpc:call(XNode, ct, print, ["hello",[]]), + ok = rpc:call(XNode, ct, pal, ["hello",[]]), + ok = rpc:call(XNode, ct, log, ["hello",[]]), + Data = io_lib:format("hello", []), + {badrpc,{'EXIT',{noproc,_}}} = + (catch rpc:call(XNode, test_server_io, print_unexpected, [Data])), + ok. + + +%%%----------------------------------------------------------------- %%% HELP FUNCTIONS %%%----------------------------------------------------------------- diff --git a/lib/compiler/src/beam_bool.erl b/lib/compiler/src/beam_bool.erl index cf5455dfde..124abd13c1 100644 --- a/lib/compiler/src/beam_bool.erl +++ b/lib/compiler/src/beam_bool.erl @@ -425,6 +425,9 @@ bopt_tree([], Forest, Pre) -> safe_bool_op(N, Ar) -> erl_internal:new_type_test(N, Ar) orelse erl_internal:comp_op(N, Ar). +bopt_bool_args([V0,V0], Forest0) -> + {V,Forest} = bopt_bool_arg(V0, Forest0), + {[V,V],Forest}; bopt_bool_args(As, Forest) -> mapfoldl(fun bopt_bool_arg/2, Forest, As). diff --git a/lib/compiler/test/guard_SUITE.erl b/lib/compiler/test/guard_SUITE.erl index 5656d23090..a0a9bb7ddd 100644 --- a/lib/compiler/test/guard_SUITE.erl +++ b/lib/compiler/test/guard_SUITE.erl @@ -813,8 +813,16 @@ and_guard(Config) when is_list(Config) -> ?line ok = relprod({'Set',a,b}, {'Set',a,b}), + ok = and_same_var(42), + {'EXIT',{if_clause,_}} = (catch and_same_var(x)), ok. +and_same_var(V) -> + B = is_integer(V), + if + B or B -> ok + end. + relprod(R1, R2) when (erlang:size(R1) =:= 3) and (erlang:element(1,R1) =:= 'Set'), (erlang:size(R2) =:= 3) and (erlang:element(1,R2) =:= 'Set') -> ok. diff --git a/lib/erl_interface/test/Makefile b/lib/erl_interface/test/Makefile index 2b85dfc571..1ed34c74a0 100644 --- a/lib/erl_interface/test/Makefile +++ b/lib/erl_interface/test/Makefile @@ -42,7 +42,7 @@ MODULES= \ runner SPEC_FILES = \ - erl_interface.spec + erl_interface.spec erl_interface_smoke.spec COVER_FILE = erl_interface.cover diff --git a/lib/erl_interface/test/erl_interface_smoke.spec b/lib/erl_interface/test/erl_interface_smoke.spec new file mode 100644 index 0000000000..bfaea2b279 --- /dev/null +++ b/lib/erl_interface/test/erl_interface_smoke.spec @@ -0,0 +1 @@ +{suites,"../erl_interface_test",[ei_decode_encode_SUITE]}. diff --git a/lib/ic/test/Makefile b/lib/ic/test/Makefile index 54ac186c16..63af6ed9f1 100644 --- a/lib/ic/test/Makefile +++ b/lib/ic/test/Makefile @@ -33,7 +33,7 @@ RELSYSDIR = $(RELEASE_PATH)/ic_test # ---------------------------------------------------- # Target Specs # ---------------------------------------------------- -TEST_SPEC_FILE = ic.spec +TEST_SPEC_FILE = ic.spec ic_smoke.spec IDL_FILES = diff --git a/lib/ic/test/ic_smoke.spec b/lib/ic/test/ic_smoke.spec new file mode 100644 index 0000000000..ec3b5758b1 --- /dev/null +++ b/lib/ic/test/ic_smoke.spec @@ -0,0 +1 @@ +{suites,"../ic_test",[ic_SUITE]}. diff --git a/lib/inets/doc/src/httpd.xml b/lib/inets/doc/src/httpd.xml index 3c132d34fa..4210aea3ec 100644 --- a/lib/inets/doc/src/httpd.xml +++ b/lib/inets/doc/src/httpd.xml @@ -251,14 +251,14 @@ </item> <marker id="prop_max_uri"></marker> - <tag>{max_uri, integer()}</tag> + <tag>{max_uri_size, integer()}</tag> <item> <p>Limits the size of the HTTP request URI. By default there is no limit. </p> </item> <marker id="prop_max_keep_alive_req"></marker> - <tag>{max_keep_alive_requests, integer()}</tag> + <tag>{max_keep_alive_request, integer()}</tag> <item> <p>The number of request that a client can do on one connection. When the server has responded to the number of @@ -632,7 +632,7 @@ bytes </item> <marker id="prop_edlog"></marker> - <tag>{error_disk_log, internal | external}</tag> + <tag>{error_disk_log, path()}</tag> <item> <p>Defines the filename of the (disk_log(3)) error log file to be used to log server errors. If the filename does not begin diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml index d2e7ade5d6..f6bb2cca49 100644 --- a/lib/inets/doc/src/notes.xml +++ b/lib/inets/doc/src/notes.xml @@ -45,8 +45,6 @@ </item> </list> </section> - - <section><title>Improvements and New Features</title> <list> <item> @@ -158,7 +156,20 @@ </section> </section> +<section><title>Inets 5.9.2.2</title> + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Make log_alert configurable as option in ssl, SSLLogLevel + added as option to inets conf file</p> + <p> + Own Id: OTP-11259</p> + </item> + </list> + </section> +</section> <section><title>Inets 5.9.2.1</title> <section><title>Improvements and New Features</title> <list> diff --git a/lib/inets/src/http_lib/http_transport.erl b/lib/inets/src/http_lib/http_transport.erl index df58fa1b81..7e679531cf 100644 --- a/lib/inets/src/http_lib/http_transport.erl +++ b/lib/inets/src/http_lib/http_transport.erl @@ -159,7 +159,7 @@ listen(ip_comm = _SocketType, Addr, Port, Fd, IpFamily) -> listen_ip_comm(Addr, Port, Fd, IpFamily); listen({essl, SSLConfig}, Addr, Port, Fd, IpFamily) -> - listen_ssl(Addr, Port, Fd, SSLConfig, IpFamily). + listen_ssl(Addr, Port, Fd, SSLConfig, IpFamily, []). listen(ip_comm = _SocketType, Addr, Port, IpFamily) -> listen_ip_comm(Addr, Port, undefined, IpFamily); @@ -178,7 +178,13 @@ listen({essl, SSLConfig}, Addr, Port, IpFamily) -> [{addr, Addr}, {port, Port}, {ssl_config, SSLConfig}]), - listen_ssl(Addr, Port, undefined, SSLConfig, IpFamily). + {SSLConfig2, ExtraOpts} = case proplists:get_value(log_alert, SSLConfig, undefined) of + undefined -> + {SSLConfig, []}; + LogAlert -> + {proplists:delete(log_alert, SSLConfig), [{log_alert, LogAlert}]} + end, + listen_ssl(Addr, Port, undefined, SSLConfig2, IpFamily, ExtraOpts). listen_ip_comm(Addr, Port, Fd, IpFamily) -> case (catch do_listen_ip_comm(Addr, Port, Fd, IpFamily)) of @@ -221,24 +227,23 @@ do_listen_ip_comm(Addr, Port, Fd, IpFamily) -> gen_tcp:listen(NewPort, Opts2) end. - -listen_ssl(Addr, Port, Fd, Opts0, IpFamily) -> +listen_ssl(Addr, Port, Fd, Opts0, IpFamily, ExtraOpts) -> {NewPort, SockOpt} = get_socket_info(Addr, Port, Fd), Opts = SockOpt ++ Opts0, case IpFamily of inet6fb4 -> - Opts2 = [inet6 | Opts], + Opts2 = [inet6 | Opts] ++ ExtraOpts, ?hlrt("try ipv6 listen", [{opts, Opts2}]), case (catch ssl:listen(Port, Opts2)) of {error, Reason} when ((Reason =:= nxdomain) orelse (Reason =:= eafnosupport)) -> - Opts3 = [inet | Opts], + Opts3 = [inet | Opts] ++ ExtraOpts, ?hlrt("ipv6 listen failed - try ipv4 instead", [{reason, Reason}, {opts, Opts3}]), ssl:listen(NewPort, Opts3); {'EXIT', Reason} -> - Opts3 = [inet | Opts], + Opts3 = [inet | Opts] ++ ExtraOpts, ?hlrt("ipv6 listen exit - try ipv4 instead", [{reason, Reason}, {opts, Opts3}]), ssl:listen(NewPort, Opts3); @@ -251,7 +256,7 @@ listen_ssl(Addr, Port, Fd, Opts0, IpFamily) -> _ -> Opts2 = [IpFamily | Opts], ?hlrt("listen", [{opts, Opts2}]), - ssl:listen(NewPort, Opts2) + ssl:listen(NewPort, Opts2 ++ ExtraOpts) end. diff --git a/lib/inets/src/http_server/httpd_conf.erl b/lib/inets/src/http_server/httpd_conf.erl index d45f3c0048..b3ca13e2fe 100644 --- a/lib/inets/src/http_server/httpd_conf.erl +++ b/lib/inets/src/http_server/httpd_conf.erl @@ -390,6 +390,13 @@ load("SSLCertificateFile " ++ SSLCertificateFile, []) -> {error, ?NICE(clean(SSLCertificateFile)++ " is an invalid SSLCertificateFile")} end; +load("SSLLogLevel " ++ SSLLogAlert, []) -> + case SSLLogAlert of + "none" -> + {ok, [], {ssl_log_alert, false}}; + _ -> + {ok, [], {ssl_log_alert, true}} + end; load("SSLCertificateKeyFile " ++ SSLCertificateKeyFile, []) -> case is_file(clean(SSLCertificateKeyFile)) of {ok, File} -> @@ -948,7 +955,8 @@ ssl_config(ConfigDB) -> ssl_ciphers(ConfigDB) ++ ssl_password(ConfigDB) ++ ssl_verify_depth(ConfigDB) ++ - ssl_ca_certificate_file(ConfigDB). + ssl_ca_certificate_file(ConfigDB) ++ + ssl_log_level(ConfigDB). @@ -1214,6 +1222,14 @@ ssl_certificate_key_file(ConfigDB) -> [{keyfile,SSLCertificateKeyFile}] end. +ssl_log_level(ConfigDB) -> + case httpd_util:lookup(ConfigDB,ssl_log_alert) of + undefined -> + []; + SSLLogLevel -> + [{log_alert,SSLLogLevel}] + end. + ssl_verify_client(ConfigDB) -> case httpd_util:lookup(ConfigDB,ssl_verify_client) of undefined -> diff --git a/lib/inets/src/http_server/httpd_request_handler.erl b/lib/inets/src/http_server/httpd_request_handler.erl index 0f47d785ef..cb20159794 100644 --- a/lib/inets/src/http_server/httpd_request_handler.erl +++ b/lib/inets/src/http_server/httpd_request_handler.erl @@ -106,7 +106,7 @@ init([Manager, ConfigDB, AcceptTimeout]) -> case http_transport:negotiate(SocketType, Socket, TimeOut) of {error, Error} -> ?hdrd("negotiation failed", [{error, Error}]), - exit(Error); %% Can be 'normal'. + exit(shutdown); %% Can be 'normal'. ok -> ?hdrt("negotiation successfull", []), NewTimeout = TimeOut - timer:now_diff(now(),Then) div 1000, diff --git a/lib/inets/src/http_server/httpd_response.erl b/lib/inets/src/http_server/httpd_response.erl index 6b6532266b..a45b04f275 100644 --- a/lib/inets/src/http_server/httpd_response.erl +++ b/lib/inets/src/http_server/httpd_response.erl @@ -20,7 +20,7 @@ -module(httpd_response). -export([generate_and_send_response/1, send_status/3, send_header/3, send_body/3, send_chunk/3, send_final_chunk/2, split_header/2, - is_disable_chunked_send/1, cache_headers/1]). + is_disable_chunked_send/1, cache_headers/2]). -export([map_status_code/2]). -include("httpd.hrl"). @@ -266,8 +266,8 @@ get_connection(false,"HTTP/1.1") -> get_connection(_,_) -> "". -cache_headers(#mod{config_db = Db}) -> - case httpd_util:lookup(Db, script_nocache, false) of +cache_headers(#mod{config_db = Db}, NoCacheType) -> + case httpd_util:lookup(Db, NoCacheType, false) of true -> Date = httpd_util:rfc1123_date(), [{"cache-control", "no-cache"}, diff --git a/lib/inets/src/http_server/mod_cgi.erl b/lib/inets/src/http_server/mod_cgi.erl index c854166c29..f1b73810e6 100644 --- a/lib/inets/src/http_server/mod_cgi.erl +++ b/lib/inets/src/http_server/mod_cgi.erl @@ -295,7 +295,7 @@ receive_headers(Port, Module, Function, Args, Timeout) -> end. send_headers(ModData, {StatusCode, _}, HTTPHeaders) -> - ExtraHeaders = httpd_response:cache_headers(ModData), + ExtraHeaders = httpd_response:cache_headers(ModData, script_nocache), httpd_response:send_header(ModData, StatusCode, ExtraHeaders ++ HTTPHeaders). diff --git a/lib/inets/src/http_server/mod_esi.erl b/lib/inets/src/http_server/mod_esi.erl index e36c33b282..b11df34f9e 100644 --- a/lib/inets/src/http_server/mod_esi.erl +++ b/lib/inets/src/http_server/mod_esi.erl @@ -440,7 +440,7 @@ receive_headers(Timeout) -> end. send_headers(ModData, StatusCode, HTTPHeaders) -> - ExtraHeaders = httpd_response:cache_headers(ModData), + ExtraHeaders = httpd_response:cache_headers(ModData, erl_script_nocache), httpd_response:send_header(ModData, StatusCode, ExtraHeaders ++ HTTPHeaders). diff --git a/lib/inets/test/httpd_basic_SUITE.erl b/lib/inets/test/httpd_basic_SUITE.erl index 523cf9d38c..fef0a1f0f4 100644 --- a/lib/inets/test/httpd_basic_SUITE.erl +++ b/lib/inets/test/httpd_basic_SUITE.erl @@ -33,7 +33,8 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [ uri_too_long_414, - header_too_long_413, + header_too_long_413, + erl_script_nocache_opt, escaped_url_in_error_body, slowdose ]. @@ -178,6 +179,28 @@ header_too_long_413(Config) when is_list(Config) -> {version, "HTTP/1.1"}]), inets:stop(httpd, Pid). +%%------------------------------------------------------------------------- +%%------------------------------------------------------------------------- + +erl_script_nocache_opt(doc) -> + ["Test that too long headers's get 413 HTTP code"]; +erl_script_nocache_opt(suite) -> + []; +erl_script_nocache_opt(Config) when is_list(Config) -> + HttpdConf = ?config(httpd_conf, Config), + {ok, Pid} = inets:start(httpd, [{port, 0}, {erl_script_nocache, true} | HttpdConf]), + Info = httpd:info(Pid), + Port = proplists:get_value(port, Info), + _Address = proplists:get_value(bind_address, Info), + URL1 = ?URL_START ++ integer_to_list(Port), + case httpc:request(get, {URL1 ++ "/dummy.html", []}, + [{url_encode, false}, + {version, "HTTP/1.0"}], + [{full_result, false}]) of + {ok, {200, _}} -> + ok + end, + inets:stop(httpd, Pid). %%------------------------------------------------------------------------- %%------------------------------------------------------------------------- diff --git a/lib/jinterface/test/Makefile b/lib/jinterface/test/Makefile index d9ff406994..90d4e01035 100644 --- a/lib/jinterface/test/Makefile +++ b/lib/jinterface/test/Makefile @@ -32,7 +32,7 @@ RELSYSDIR = $(RELEASE_PATH)/jinterface_test # ---------------------------------------------------- # Target Specs # ---------------------------------------------------- -TEST_SPEC_FILE = jinterface.spec +TEST_SPEC_FILE = jinterface.spec jinterface_smoke.spec COVER_FILE = jinterface.cover MODULES = nc_SUITE \ diff --git a/lib/jinterface/test/jinterface_smoke.spec b/lib/jinterface/test/jinterface_smoke.spec new file mode 100644 index 0000000000..4a76cce4cd --- /dev/null +++ b/lib/jinterface/test/jinterface_smoke.spec @@ -0,0 +1 @@ +{cases,"../jinterface_test",jinterface_SUITE,[java_erlang_send_receive]}. diff --git a/lib/kernel/doc/src/application.xml b/lib/kernel/doc/src/application.xml index 362c373c6c..3909b11e59 100644 --- a/lib/kernel/doc/src/application.xml +++ b/lib/kernel/doc/src/application.xml @@ -253,15 +253,30 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code> </warning> </desc> </func> - <func> + <func> <name name="ensure_started" arity="1"/> <name name="ensure_started" arity="2"/> <fsummary>Load and start an application</fsummary> - <desc> - <p>Equivalent to <seealso marker="#start/2"><c>application:start/1,2</c></seealso> except - it returns <c>ok</c> for already started applications.</p> - </desc> - </func> + <desc> + <p>Equivalent to <seealso marker="#start/2"><c>application:start/1,2</c></seealso> except + it returns <c>ok</c> for already started applications.</p> + </desc> + </func> + <func> + <name name="ensure_all_started" arity="1"/> + <name name="ensure_all_started" arity="2"/> + <fsummary>Load and start an application and its dependencies, recursively</fsummary> + <desc> + <p>Equivalent to calling <seealso marker="#start/2"><c>application:start/1,2</c></seealso> + repeatedly on all dependencies that have not yet been started for an application. + The function returns <c>{ok, AppNames}</c> for a successful start or for an already started + application (which are however omitted from the <c>AppNames</c> list), and reports + <c>{error, {AppName,Reason}}</c> for errors, where <c>Reason</c> is any possible reason + returned by <seealso marker="#start/2"><c>application:start/1,2</c></seealso> when starting a + specific dependency. In case of an error, the applications that were started by the + function are stopped to bring the set of running applications back to its initial state.</p> + </desc> + </func> <func> <name name="start" arity="1"/> <name name="start" arity="2"/> diff --git a/lib/kernel/src/application.erl b/lib/kernel/src/application.erl index 5dd6b73857..4e8ba1b78a 100644 --- a/lib/kernel/src/application.erl +++ b/lib/kernel/src/application.erl @@ -18,7 +18,8 @@ %% -module(application). --export([start/1, start/2, start_boot/1, start_boot/2, stop/1, +-export([ensure_all_started/1, ensure_all_started/2, start/1, start/2, + start_boot/1, start_boot/2, stop/1, load/1, load/2, unload/1, takeover/2, which_applications/0, which_applications/1, loaded_applications/0, permit/2]). @@ -113,6 +114,46 @@ load1(Application, DistNodes) -> unload(Application) -> application_controller:unload_application(Application). + +-spec ensure_all_started(Application) -> {'ok', Started} | {'error', Reason} when + Application :: atom(), + Started :: [atom()], + Reason :: term(). +ensure_all_started(Application) -> + ensure_all_started(Application, temporary). + +-spec ensure_all_started(Application, Type) -> {'ok', Started} | {'error', Reason} when + Application :: atom(), + Type :: restart_type(), + Started :: [atom()], + Reason :: term(). +ensure_all_started(Application, Type) -> + case ensure_all_started(Application, Type, []) of + {ok, Started} -> + {ok, lists:reverse(Started)}; + {error, Reason, Started} -> + [stop(App) || App <- Started], + {error, Reason} + end. + +ensure_all_started(Application, Type, Started) -> + case start(Application, Type) of + ok -> + {ok, [Application | Started]}; + {error, {already_started, Application}} -> + {ok, Started}; + {error, {not_started, Dependency}} -> + case ensure_all_started(Dependency, Type, Started) of + {ok, NewStarted} -> + ensure_all_started(Application, Type, NewStarted); + Error -> + Error + end; + {error, Reason} -> + {error, {Application, Reason}, Started} + end. + + -spec start(Application) -> 'ok' | {'error', Reason} when Application :: atom(), Reason :: term(). diff --git a/lib/kernel/test/Makefile b/lib/kernel/test/Makefile index cb11d4e899..f1b8a105ed 100644 --- a/lib/kernel/test/Makefile +++ b/lib/kernel/test/Makefile @@ -145,7 +145,7 @@ release_tests_spec: make_emakefile $(INSTALL_DIR) "$(RELSYSDIR)" $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)" $(INSTALL_DATA) $(APP_FILES) "$(RELSYSDIR)" - $(INSTALL_DATA) kernel.spec $(EMAKEFILE)\ + $(INSTALL_DATA) kernel.spec kernel_smoke.spec $(EMAKEFILE)\ $(COVERFILE) "$(RELSYSDIR)" chmod -R u+w "$(RELSYSDIR)" @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) diff --git a/lib/kernel/test/application_SUITE.erl b/lib/kernel/test/application_SUITE.erl index 1ff291be54..9ec8a15861 100644 --- a/lib/kernel/test/application_SUITE.erl +++ b/lib/kernel/test/application_SUITE.erl @@ -35,7 +35,7 @@ -export([config_change/1, distr_changed_tc1/1, distr_changed_tc2/1, - ensure_started/1, + ensure_started/1, ensure_all_started/1, shutdown_func/1, do_shutdown/1, shutdown_timeout/1]). -define(TESTCASE, testcase_name). @@ -52,7 +52,7 @@ all() -> [failover, failover_comp, permissions, load, load_use_cache, ensure_started, {group, reported_bugs}, start_phases, script_start, nodedown_start, permit_false_start_local, - permit_false_start_dist, get_key, get_env, + permit_false_start_dist, get_key, get_env, ensure_all_started, {group, distr_changed}, config_change, shutdown_func, shutdown_timeout]. groups() -> @@ -978,6 +978,85 @@ ensure_started(Conf) -> ok = application:unload(app1), ok. +ensure_all_started(suite) -> []; +ensure_all_started(doc) -> ["Test application:ensure_all_started/1-2."]; +ensure_all_started(Conf) -> + + {ok, Fd1} = file:open("app1.app", [write]), + w_app1(Fd1), + file:close(Fd1), + {ok, Fd9} = file:open("app9.app", [write]), + w_app9(Fd9), + file:close(Fd9), + {ok, Fd10} = file:open("app10.app", [write]), + w_app10_dep9(Fd10), + file:close(Fd10), + {ok, FdErr} = file:open("app_chain_error.app", [write]), + w_app(FdErr, app_chain_error()), + file:close(FdErr), + {ok, FdErr2} = file:open("app_chain_error2.app", [write]), + w_app(FdErr2, app_chain_error2()), + file:close(FdErr2), + + %% Single app start/stop + false = lists:keyfind(app1, 1, application:which_applications()), + {ok, [app1]} = application:ensure_all_started(app1), % app1 started + {app1, _, _} = lists:keyfind(app1, 1, application:which_applications()), + {ok, []} = application:ensure_all_started(app1), % no start needed + ok = application:stop(app1), + false = lists:keyfind(app1, 1, application:which_applications()), + ok = application:unload(app1), + + %% App or dependency not found. + Name = hopefully_not_an_existing_app_file, + {error,{Name, {"no such file or directory", _ }}} = + application:ensure_all_started(Name), + + %% Start dependencies. + {error, {not_started, app9}} = application:start(app10), + {ok, [app9,app10]} = application:ensure_all_started(app10, temporary), + {app9, _, _} = lists:keyfind(app9, 1, application:which_applications()), + {app10, _, _} = lists:keyfind(app10, 1, application:which_applications()), + %% Only report apps/dependencies that actually needed to start + ok = application:stop(app10), + ok = application:unload(app10), + {ok, [app10]} = application:ensure_all_started(app10, temporary), + ok = application:stop(app9), + ok = application:unload(app9), + ok = application:stop(app10), + ok = application:unload(app10), + + %% Deeper failure chain. We have the following dependencies: + %% app_chain_error -> app_chain_error2 + %% app_chain_error2 -> app10 + %% app_chain_error2 -> hopefully_not_an_existing_app + %% app10 -> app 9 + %% First we have none running and we expect to have neither app9 + %% nor app10 running after failing to start + %% hopefully_not_an_existing_app + {error, {hopefully_not_an_existing_app, {"no such file or directory", _}}}= + application:ensure_all_started(app_chain_error), + false = lists:keyfind(app9, 1, application:which_applications()), + false = lists:keyfind(app10, 1, application:which_applications()), + false = lists:keyfind(app_chain_error2,1,application:which_applications()), + false = lists:keyfind(app_chain_error, 1, application:which_applications()), + %% Here we will have app9 already running, and app10 should be + %% able to boot fine. + %% In this dependency failing, we expect app9 to still be running, but + %% not app10 after failing to start hopefully_not_an_existing_app + {ok, [app9]} = application:ensure_all_started(app9, temporary), + {error, {hopefully_not_an_existing_app, {"no such file or directory", _}}}= + application:ensure_all_started(app_chain_error), + {app9, _, _} = lists:keyfind(app9, 1, application:which_applications()), + false = lists:keyfind(app10, 1, application:which_applications()), + false = lists:keyfind(app_chain_error2,1,application:which_applications()), + false = lists:keyfind(app_chain_error, 1, application:which_applications()), + ok = application:stop(app9), + ok = application:unload(app9), + ok = application:unload(app10), + ok = application:unload(app_chain_error2), + ok = application:unload(app_chain_error), + ok. %%%----------------------------------------------------------------- %%% Testing of reported bugs and other tickets. @@ -2125,6 +2204,24 @@ app_start_error() -> {applications, [kernel]}, {mod, {app_start_error, []}}]}. +app_chain_error() -> + {application, app_chain_error, + [{description, "ERTS CXC 138 ce"}, + {vsn, "2.0"}, + {modules, []}, + {registered, []}, + {applications, [kernel, app_chain_error2]}, + {mod, {ch_sup, {app_chain_error, 20,20}}}]}. + +app_chain_error2() -> + {application, app_chain_error2, + [{description, "ERTS CXC 138 ce2"}, + {vsn, "2.0"}, + {modules, []}, + {registered, []}, + {applications, [kernel, app10, hopefully_not_an_existing_app]}, + {mod, {ch_sup, {app_chain_error2, 21,21}}}]}. + app_group_leader() -> {application, group_leader, [{description, "GROUP_LEADER CXC 138 11"}, @@ -2374,6 +2471,12 @@ w_app7(Fd) -> w_app8(Fd) -> io:format(Fd, "~p.\n", [app8()]). +w_app9(Fd) -> + io:format(Fd, "~p.\n", [app9()]). + +w_app10_dep9(Fd) -> + io:format(Fd, "~p.\n", [app10_dep9()]). + w_app_start_error(Fd) -> io:format(Fd, "~p.\n", [app_start_error()]). diff --git a/lib/kernel/test/kernel_smoke.spec b/lib/kernel/test/kernel_smoke.spec new file mode 100644 index 0000000000..e5d8273c56 --- /dev/null +++ b/lib/kernel/test/kernel_smoke.spec @@ -0,0 +1,9 @@ +{config, "../test_server/ts.config"}. +{config, "../test_server/ts.unix.config"}. + +{cases,"../kernel_test", inet_SUITE,[t_gethostbyaddr,t_gethostbyname, + t_gethostbyaddr_v6,t_gethostbyname_v6,t_gethostnative,getifaddrs]}. +{cases,"../kernel_test", inet_res_SUITE,[gethostbyaddr,gethostbyname, + gethostbyaddr_v6,gethostbyname_v6,basic]}. +{cases,"../kernel_test", gen_tcp_echo_SUITE,[active_echo]}. +{cases,"../kernel_test", heart_SUITE,[reboot]}. diff --git a/lib/mnesia/src/mnesia_bup.erl b/lib/mnesia/src/mnesia_bup.erl index fd87be1759..3b084e7371 100644 --- a/lib/mnesia/src/mnesia_bup.erl +++ b/lib/mnesia/src/mnesia_bup.erl @@ -1052,11 +1052,7 @@ local_uninstall_fallback(Master, FA) -> Tmp = FA2#fallback_args.fallback_tmp, Bup = FA2#fallback_args.fallback_bup, file:delete(Tmp), - Res = - case fallback_exists(Bup) of - true -> file:delete(Bup); - false -> ok - end, + Res = file:delete(Bup), ?eval_debug_fun({?MODULE, uninstall_fallback2, post_delete}, []), Master ! {self(), Res}, unlink(Master), diff --git a/lib/odbc/c_src/odbcserver.c b/lib/odbc/c_src/odbcserver.c index 5730e20774..8de81a30ae 100644 --- a/lib/odbc/c_src/odbcserver.c +++ b/lib/odbc/c_src/odbcserver.c @@ -277,11 +277,15 @@ int main(void) msg = receive_erlang_port_msg(); temp = strtok(msg, ";"); + if (temp == NULL) + DO_EXIT(EXIT_STDIN_BODY); length = strlen(temp); supervisor_port = safe_malloc(length + 1); strcpy(supervisor_port, temp); temp = strtok(NULL, ";"); + if (temp == NULL) + DO_EXIT(EXIT_STDIN_BODY); length = strlen(temp); odbc_port = safe_malloc(length + 1); strcpy(odbc_port, temp); @@ -1819,12 +1823,20 @@ static byte * receive_erlang_port_msg(void) len |= lengthstr[i]; } + if (len <= 0 || len > 1024) { + DO_EXIT(EXIT_STDIN_HEADER); + } + buffer = (byte *)safe_malloc(len); if (read_exact(buffer, len) <= 0) { DO_EXIT(EXIT_STDIN_BODY); } + if (buffer[len-1] != '\0') { + DO_EXIT(EXIT_STDIN_BODY); + } + return buffer; } diff --git a/lib/os_mon/test/Makefile b/lib/os_mon/test/Makefile index 461bebc102..cbb014324d 100644 --- a/lib/os_mon/test/Makefile +++ b/lib/os_mon/test/Makefile @@ -85,7 +85,8 @@ release_spec: release_tests_spec: make_emakefile $(INSTALL_DIR) "$(RELSYSDIR)" - $(INSTALL_DATA) os_mon.spec os_mon.cover $(EMAKEFILE) $(SOURCE) "$(RELSYSDIR)" + $(INSTALL_DATA) os_mon.spec os_mon.cover os_mon_smoke.spec \ + $(EMAKEFILE) $(SOURCE) "$(RELSYSDIR)" $(INSTALL_DATA) os_mon_mib_SUITE.cfg "$(RELSYSDIR)" ## tar chf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) diff --git a/lib/os_mon/test/os_mon_smoke.spec b/lib/os_mon/test/os_mon_smoke.spec new file mode 100644 index 0000000000..6f0d02494b --- /dev/null +++ b/lib/os_mon/test/os_mon_smoke.spec @@ -0,0 +1,3 @@ +{cases,"../os_mon_test",disksup_SUITE,[api]}. +{cases,"../os_mon_test",cpu_sup_SUITE,[load_api,util_api]}. +{cases,"../os_mon_test",memsup_SUITE,[api]}.
\ No newline at end of file diff --git a/lib/runtime_tools/doc/src/dbg.xml b/lib/runtime_tools/doc/src/dbg.xml index d8c82b2459..36b3b51a99 100644 --- a/lib/runtime_tools/doc/src/dbg.xml +++ b/lib/runtime_tools/doc/src/dbg.xml @@ -244,7 +244,7 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\ </item> <tag><c>all</c></tag> <item> - <p>Sets all flags.</p> + <p>Sets all flags except <c>silent</c>.</p> </item> <tag><c>clear</c></tag> <item> diff --git a/lib/runtime_tools/src/dbg.erl b/lib/runtime_tools/src/dbg.erl index 6b2fb0460f..f0086e8cc7 100644 --- a/lib/runtime_tools/src/dbg.erl +++ b/lib/runtime_tools/src/dbg.erl @@ -1113,7 +1113,7 @@ transform_flags([sos|Tail],Acc) -> transform_flags(Tail,[set_on_spawn|Acc]); transform_flags([sol|Tail],Acc) -> transform_flags(Tail,[set_on_link|Acc]); transform_flags([sofs|Tail],Acc) -> transform_flags(Tail,[set_on_first_spawn|Acc]); transform_flags([sofl|Tail],Acc) -> transform_flags(Tail,[set_on_first_link|Acc]); -transform_flags([all|_],_Acc) -> all(); +transform_flags([all|_],_Acc) -> all()--[silent]; transform_flags([F|Tail]=List,Acc) when is_atom(F) -> case lists:member(F, all()) of true -> transform_flags(Tail,[F|Acc]); @@ -1124,7 +1124,7 @@ transform_flags(Bad,_Acc) -> {error,{bad_flags,Bad}}. all() -> [send,'receive',call,procs,garbage_collection,running, set_on_spawn,set_on_first_spawn,set_on_link,set_on_first_link, - timestamp,arity,return_to]. + timestamp,arity,return_to,silent]. display_info([Node|Nodes]) -> io:format("~nNode ~w:~n",[Node]), diff --git a/lib/sasl/doc/src/rb.xml b/lib/sasl/doc/src/rb.xml index 3da825878e..b94914d8f9 100644 --- a/lib/sasl/doc/src/rb.xml +++ b/lib/sasl/doc/src/rb.xml @@ -138,6 +138,24 @@ </desc> </func> <func> + <name>log_list()</name> + <name>log_list(Type)</name> + <fsummary>Log reports list</fsummary> + <type> + <v>Type = type()</v> + <v>type() = error | error_report | info_msg | info_report | + warning_msg | warning_report | crash_report | + supervisor_report | progress</v> + </type> + <desc> + <p>Same as <c>list/0</c> or <c>list/1</c> functions + but result is printed to logfile, if set, otherwise to standard_io. + </p> + <p>If no <c>Type</c> is given, all reports are listed. + </p> + </desc> + </func> + <func> <name>rescan()</name> <name>rescan(Options)</name> <fsummary>Rescan the report directory</fsummary> @@ -172,7 +190,7 @@ <type> <v>Options = [opt()]</v> <v>opt() = {start_log, FileName} | {max, MaxNoOfReports} | {report_dir, DirString} | {type, ReportType} | {abort_on_error, Bool}</v> - <v>FileName = string() | standard_io</v> + <v>FileName = string() | atom() | pid()</v> <v>MaxNoOfReports = int() | all</v> <v>DirString = string()</v> <v>ReportType = type() | [type()] | all</v> @@ -185,11 +203,13 @@ reports can be browsed. When the <c>rb_server</c> is started, the files in the specified directory are scanned. The other functions assume that the server has - started. + started. </p> - <p><c>{start_log, FileName}</c> starts logging to file. All - reports will be printed to the named file. The default is - <c>standard_io</c>. + <p><c>{start_log, FileName}</c> starts logging to file, + registered name or io_device. All reports will be printed + to the named file. The default is <c>standard_io</c>. + The option {start_log, standard_error} is not allowed and + will be replaced by default standard_io. </p> <p><c>{max, MaxNoOfReports}</c>. Controls how many reports the <c>rb_server</c> should read on start-up. This option is @@ -226,11 +246,11 @@ <name>start_log(FileName)</name> <fsummary>Redirect all output to <c>FileName</c></fsummary> <type> - <v>FileName = string()</v> + <v>FileName = string() | atom() | pid()</v> </type> <desc> <p>Redirects all report output from the RB tool to the - specified file. + specified file, registered name or io_device. </p> </desc> </func> diff --git a/lib/sasl/src/rb.erl b/lib/sasl/src/rb.erl index 8004ef2c5a..767932e659 100644 --- a/lib/sasl/src/rb.erl +++ b/lib/sasl/src/rb.erl @@ -22,7 +22,7 @@ %% External exports -export([start/0, start/1, stop/0, rescan/0, rescan/1]). --export([list/0, list/1, show/0, show/1, grep/1, filter/1, filter/2, start_log/1, stop_log/0]). +-export([list/0, list/1, log_list/0, log_list/1, show/0, show/1, grep/1, filter/1, filter/2, start_log/1, stop_log/0]). -export([h/0, help/0]). %% Internal exports @@ -62,6 +62,9 @@ rescan(Options) -> list() -> list(all). list(Type) -> call({list, Type}). +log_list() -> log_list(all). +log_list(Type) -> call({log_list, Type}). + show() -> call(show). @@ -93,6 +96,8 @@ help() -> io:format("rb:help() - print this help~n"), io:format("rb:list() - list all reports~n"), io:format("rb:list(Type) - list all reports of type Type~n"), + io:format("rb:log_list() - log list of all reports~n"), + io:format("rb:log_list(Type) - log list of all reports of type Type~n"), io:format(" currently supported types are:~n"), print_types(), io:format("rb:grep(RegExp) - print reports containing RegExp.~n"), @@ -113,7 +118,7 @@ help() -> io:format("rb:show(Number) - print report no Number~n"), io:format("rb:show(Type) - print all reports of type Type~n"), io:format("rb:show() - print all reports~n"), - io:format("rb:start_log(File) - redirect all reports to file~n"), + io:format("rb:start_log(File) - redirect all reports to file or io_device~n"), io:format("rb:stop_log() - close the log file and redirect to~n"), io:format(" standard_io~n"), io:format("rb:stop - stop the rb_server~n"). @@ -207,7 +212,10 @@ handle_call({rescan, Options}, _From, State) -> handle_call(_, _From, #state{data = undefined}) -> {reply, {error, no_data}, #state{}}; handle_call({list, Type}, _From, State) -> - print_list(State#state.data, Type), + print_list(standard_io, State#state.data, Type), + {reply, ok, State}; +handle_call({log_list, Type}, _From, State) -> + print_list(State#state.device, State#state.data, Type), {reply, ok, State}; handle_call({start_log, FileName}, _From, State) -> NewDevice = open_log_file(FileName), @@ -262,7 +270,16 @@ code_change(_OldVsn, State, _Extra) -> %% Returns: A Device for later use in call to io:format %%----------------------------------------------------------------- open_log_file(standard_io) -> standard_io; -open_log_file(FileName) -> +open_log_file(Fd) when is_atom(Fd),Fd=/=standard_error -> + case whereis(Fd) of + undefined -> io:format("rb: Registered name not found '~s'.~n", + [Fd]), + io:format("rb: Using standard_io~n"), + open_log_file(standard_io); + Pid -> open_log_file(Pid) + end; +open_log_file(Fd) when is_pid(Fd)-> Fd; +open_log_file(FileName) when is_list(FileName) -> case file:open(FileName, [write,append]) of {ok, Fd} -> Fd; Error -> @@ -270,7 +287,10 @@ open_log_file(FileName) -> [FileName, Error]), io:format("rb: Using standard_io~n"), standard_io - end. + end; +open_log_file(standard_error) -> + io:format("rb: Using standard_io~n"), + standard_io. close_device(Fd) when is_pid(Fd) -> catch file:close(Fd); @@ -550,18 +570,18 @@ local_time_to_universal_time({Date,Time}) -> end. -print_list(Data, Type) -> +print_list(Fd, Data, Type) -> Header = {"No", "Type", "Process", "Date Time"}, Width = find_width([Header | Data], 0)+1, DateWidth = find_date_width([Header | Data], 0) +1, Format = lists:concat(["~4s~20s ~", Width, "s~20s~n"]), - io:format(Format, tuple_to_list(Header)), - io:format(Format, ["==", "====", "=======", "==== ===="]), - print_list(Data, Type, Width, DateWidth). -print_list([], _, _, _) -> true; -print_list([H|T], Type, Width, DateWidth) -> - print_one_report(H, Type, Width, DateWidth), - print_list(T, Type, Width, DateWidth). + io:format(Fd, Format, tuple_to_list(Header)), + io:format(Fd, Format, ["==", "====", "=======", "==== ===="]), + print_list(Fd, Data, Type, Width, DateWidth). +print_list(_, [], _, _, _) -> true; +print_list(Fd, [H|T], Type, Width, DateWidth) -> + print_one_report(Fd, H, Type, Width, DateWidth), + print_list(Fd, T, Type, Width, DateWidth). find_width([], Width) -> Width; find_width([H|T], Width) -> @@ -578,22 +598,22 @@ find_date_width([H|T], Width) -> true -> find_date_width(T, Width) end. -print_one_report({No, RealType, ShortDescr, Date, _Fname, _FilePos}, +print_one_report(Fd, {No, RealType, ShortDescr, Date, _Fname, _FilePos}, WantedType, Width, DateWidth) -> if WantedType == all -> - print_short_descr(No, RealType, ShortDescr, Date, Width, + print_short_descr(Fd, No, RealType, ShortDescr, Date, Width, DateWidth); WantedType == RealType -> - print_short_descr(No, RealType, ShortDescr, Date, Width, + print_short_descr(Fd, No, RealType, ShortDescr, Date, Width, DateWidth); true -> ok end. -print_short_descr(No, Type, ShortDescr, Date, Width, DateWidth) -> +print_short_descr(Fd, No, Type, ShortDescr, Date, Width, DateWidth) -> Format = lists:concat(["~4w~20w ~", Width, "s~", DateWidth,"s~n"]), - io:format(Format, [No, + io:format(Fd, Format, [No, Type, io_lib:format("~s", [ShortDescr]), Date]). diff --git a/lib/sasl/test/rb_SUITE.erl b/lib/sasl/test/rb_SUITE.erl index b0e43be3a2..453f992850 100644 --- a/lib/sasl/test/rb_SUITE.erl +++ b/lib/sasl/test/rb_SUITE.erl @@ -362,18 +362,48 @@ start_stop_log(Config) -> StdioResult = [_|_] = capture(fun() -> rb:show(1) end), {ok,<<>>} = file:read_file(OutFile), - %% Start log and check that show is printed to log and not to standad_io + %% Start log and check that show is printed to log and not to standard_io ok = rb:start_log(OutFile), [] = capture(fun() -> rb:show(1) end), {ok,Bin} = file:read_file(OutFile), true = (Bin =/= <<>>), + %% Start log with atom standard_io and check that show is printed to standard_io + ok = rb:stop_log(), + ok = file:write_file(OutFile,[]), + ok = rb:start_log(standard_io), + StdioResult = [_|_] = capture(fun() -> rb:show(1) end), + {ok,<<>>} = file:read_file(OutFile), + + %% Start log and check that show is printed to iodevice log and not to standard_io + ok = rb:stop_log(), + ok = file:write_file(OutFile,[]), + {ok, IoOutFile} = file:open(OutFile,[write]), + ok = rb:start_log(IoOutFile), + [] = capture(fun() -> rb:show(1) end), + {ok,Bin} = file:read_file(OutFile), + true = (Bin =/= <<>>), + ok = file:close(IoOutFile), + %% Stop log and check that show is printed to standard_io and not to log ok = rb:stop_log(), ok = file:write_file(OutFile,[]), StdioResult = capture(fun() -> rb:show(1) end), {ok,<<>>} = file:read_file(OutFile), + %% Start log and check that list is printed to log and not to standard_io + ok = file:write_file(OutFile,[]), + ok = rb:start_log(OutFile), + [] = capture(fun() -> rb:log_list() end), + {ok,Bin2} = file:read_file(OutFile), + true = (Bin2 =/= <<>>), + + %% Stop log and check that list is printed to standard_io and not to log + ok = rb:stop_log(), + ok = file:write_file(OutFile,[]), + StdioResult2 = capture(fun() -> rb:log_list() end), + {ok,<<>>} = file:read_file(OutFile), + %% Test that standard_io is used if log file can not be opened ok = rb:start_log(filename:join(nonexistingdir,"newfile.txt")), StdioResult = capture(fun() -> rb:show(1) end), diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml index bd0d3d49dd..141d3df38e 100644 --- a/lib/ssh/doc/src/ssh.xml +++ b/lib/ssh/doc/src/ssh.xml @@ -88,6 +88,7 @@ number for SSH.</d> <v>Options = [{Option, Value}]</v> <v>Timeout = infinity | integer(milliseconds)</v> + <d>Negotiation timeout, for connection timeout use the option <c>{connect_timeout, timeout()}</c>.</d> </type> <desc> <p>Connects to an SSH server. No channel is started. This is done diff --git a/lib/ssh/src/ssh.hrl b/lib/ssh/src/ssh.hrl index da5750b6c3..4fd347ba8f 100644 --- a/lib/ssh/src/ssh.hrl +++ b/lib/ssh/src/ssh.hrl @@ -127,7 +127,8 @@ userauth_supported_methods , % userauth_methods, userauth_preference, - available_host_keys + available_host_keys, + authenticated = false }). -record(alg, diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl index df6175e27c..9de4dd5967 100644 --- a/lib/ssh/src/ssh_connection_handler.erl +++ b/lib/ssh/src/ssh_connection_handler.erl @@ -426,10 +426,10 @@ userauth(#ssh_msg_userauth_info_response{} = Msg, language = "en"}, State) end; -userauth(#ssh_msg_userauth_success{}, #state{ssh_params = #ssh{role = client}, +userauth(#ssh_msg_userauth_success{}, #state{ssh_params = #ssh{role = client} = Ssh, manager = Pid} = State) -> Pid ! ssh_connected, - {next_state, connected, next_packet(State)}; + {next_state, connected, next_packet(State#state{ssh_params = Ssh#ssh{authenticated = true}})}; userauth(#ssh_msg_userauth_failure{}, #state{ssh_params = #ssh{role = client, diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl index beaffdc025..682d766d99 100644 --- a/lib/ssh/src/ssh_transport.erl +++ b/lib/ssh/src/ssh_transport.erl @@ -206,6 +206,7 @@ key_exchange_init_msg(Ssh0) -> kex_init(#ssh{role = Role, opts = Opts, available_host_keys = HostKeyAlgs}) -> Random = ssh_bits:random(16), Compression = case proplists:get_value(compression, Opts, none) of + openssh_zlib -> ["[email protected]", "none"]; zlib -> ["zlib", "none"]; none -> ["none", "zlib"] end, @@ -855,13 +856,14 @@ decrypt(#ssh{decrypt = 'aes128-cbc', decrypt_keys = Key, IV = crypto:next_iv(aes_cbc, Data), {Ssh#ssh{decrypt_ctx = IV}, Dec}. - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Compression %% -%% none REQUIRED no compression -%% zlib OPTIONAL ZLIB (LZ77) compression +%% none REQUIRED no compression +%% zlib OPTIONAL ZLIB (LZ77) compression +%% openssh_zlib OPTIONAL ZLIB (LZ77) compression %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + compress_init(SSH) -> compress_init(SSH, 1). @@ -870,19 +872,32 @@ compress_init(#ssh{compress = none} = Ssh, _) -> compress_init(#ssh{compress = zlib} = Ssh, Level) -> Zlib = zlib:open(), ok = zlib:deflateInit(Zlib, Level), + {ok, Ssh#ssh{compress_ctx = Zlib}}; +compress_init(#ssh{compress = '[email protected]'} = Ssh, Level) -> + Zlib = zlib:open(), + ok = zlib:deflateInit(Zlib, Level), {ok, Ssh#ssh{compress_ctx = Zlib}}. - compress_final(#ssh{compress = none} = Ssh) -> {ok, Ssh}; compress_final(#ssh{compress = zlib, compress_ctx = Context} = Ssh) -> zlib:close(Context), + {ok, Ssh#ssh{compress = none, compress_ctx = undefined}}; +compress_final(#ssh{compress = '[email protected]', authenticated = false} = Ssh) -> + {ok, Ssh}; +compress_final(#ssh{compress = '[email protected]', compress_ctx = Context, authenticated = true} = Ssh) -> + zlib:close(Context), {ok, Ssh#ssh{compress = none, compress_ctx = undefined}}. compress(#ssh{compress = none} = Ssh, Data) -> {Ssh, Data}; compress(#ssh{compress = zlib, compress_ctx = Context} = Ssh, Data) -> Compressed = zlib:deflate(Context, Data, sync), + {Ssh, list_to_binary(Compressed)}; +compress(#ssh{compress = '[email protected]', authenticated = false} = Ssh, Data) -> + {Ssh, Data}; +compress(#ssh{compress = '[email protected]', compress_ctx = Context, authenticated = true} = Ssh, Data) -> + Compressed = zlib:deflate(Context, Data, sync), {Ssh, list_to_binary(Compressed)}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -894,18 +909,32 @@ decompress_init(#ssh{decompress = none} = Ssh) -> decompress_init(#ssh{decompress = zlib} = Ssh) -> Zlib = zlib:open(), ok = zlib:inflateInit(Zlib), + {ok, Ssh#ssh{decompress_ctx = Zlib}}; +decompress_init(#ssh{decompress = '[email protected]'} = Ssh) -> + Zlib = zlib:open(), + ok = zlib:inflateInit(Zlib), {ok, Ssh#ssh{decompress_ctx = Zlib}}. decompress_final(#ssh{decompress = none} = Ssh) -> {ok, Ssh}; decompress_final(#ssh{decompress = zlib, decompress_ctx = Context} = Ssh) -> zlib:close(Context), + {ok, Ssh#ssh{decompress = none, decompress_ctx = undefined}}; +decompress_final(#ssh{decompress = '[email protected]', authenticated = false} = Ssh) -> + {ok, Ssh}; +decompress_final(#ssh{decompress = '[email protected]', decompress_ctx = Context, authenticated = true} = Ssh) -> + zlib:close(Context), {ok, Ssh#ssh{decompress = none, decompress_ctx = undefined}}. decompress(#ssh{decompress = none} = Ssh, Data) -> {Ssh, Data}; decompress(#ssh{decompress = zlib, decompress_ctx = Context} = Ssh, Data) -> Decompressed = zlib:inflate(Context, Data), + {Ssh, list_to_binary(Decompressed)}; +decompress(#ssh{decompress = '[email protected]', authenticated = false} = Ssh, Data) -> + {Ssh, Data}; +decompress(#ssh{decompress = '[email protected]', decompress_ctx = Context, authenticated = true} = Ssh, Data) -> + Decompressed = zlib:inflate(Context, Data), {Ssh, list_to_binary(Decompressed)}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl index 93029c5038..0aa60624bf 100644 --- a/lib/ssh/test/ssh_basic_SUITE.erl +++ b/lib/ssh/test/ssh_basic_SUITE.erl @@ -48,8 +48,8 @@ all() -> close]. groups() -> - [{dsa_key, [], [send, exec, exec_compressed, shell, known_hosts, idle_time, rekey]}, - {rsa_key, [], [send, exec, exec_compressed, shell, known_hosts, idle_time, rekey]}, + [{dsa_key, [], [send, exec, exec_compressed, shell, known_hosts, idle_time, rekey, openssh_zlib_basic_test]}, + {rsa_key, [], [send, exec, exec_compressed, shell, known_hosts, idle_time, rekey, openssh_zlib_basic_test]}, {dsa_pass_key, [], [pass_phrase]}, {rsa_pass_key, [], [pass_phrase]}, {internal_error, [], [internal_error]} @@ -493,7 +493,24 @@ close(Config) when is_list(Config) -> exit(CM, {shutdown, normal}), ok = ssh:close(CM). - + +openssh_zlib_basic_test() -> + [{doc, "Test basic connection with openssh_zlib"}]. +openssh_zlib_basic_test(Config) -> + SystemDir = filename:join(?config(priv_dir, Config), system), + UserDir = ?config(priv_dir, Config), + + {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, + {user_dir, UserDir}, + {failfun, fun ssh_test_lib:failfun/2}]), + ConnectionRef = + ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true}, + {user_dir, UserDir}, + {user_interaction, false}, + {compression, openssh_zlib}]), + ok = ssh:close(ConnectionRef), + ssh:stop_daemon(Pid). + %%-------------------------------------------------------------------- %% Internal functions ------------------------------------------------ %%-------------------------------------------------------------------- diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml index 8875d07535..301ff21068 100644 --- a/lib/ssl/doc/src/notes.xml +++ b/lib/ssl/doc/src/notes.xml @@ -25,7 +25,6 @@ <file>notes.xml</file> </header> <p>This document describes the changes made to the SSL application.</p> - <section><title>SSL 5.3</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -100,7 +99,6 @@ </section> <section><title>SSL 5.2.1</title> - <section><title>Improvements and New Features</title> <list> <item> @@ -126,9 +124,20 @@ </section> </section> - +<section><title>SSL 5.1.2.1</title> +<section><title>Improvements and New Features</title> +<list> + <item> + <p> + Make log_alert configurable as option in ssl, SSLLogLevel + added as option to inets conf file</p> + <p> + Own Id: OTP-11259</p> + </item> +</list> +</section> +</section> <section><title>SSL 5.2</title> - <section><title>Fixed Bugs and Malfunctions</title> <list> <item> diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index 0c1e47311d..dc6898d001 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -219,4 +219,3 @@ format_error(Error) -> random_bytes(N) -> tls:random_bytes(N). - diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl index 14db4a6067..de8d20d399 100644 --- a/lib/ssl/src/ssl_internal.hrl +++ b/lib/ssl/src/ssl_internal.hrl @@ -111,7 +111,8 @@ %% This option should only be set to true by inet_tls_dist erl_dist = false, next_protocols_advertised = undefined, %% [binary()], - next_protocol_selector = undefined %% fun([binary()]) -> binary()) + next_protocol_selector = undefined, %% fun([binary()]) -> binary()) + log_alert }). -record(socket_options, diff --git a/lib/ssl/src/tls.erl b/lib/ssl/src/tls.erl index bb02695c12..b220a48f73 100644 --- a/lib/ssl/src/tls.erl +++ b/lib/ssl/src/tls.erl @@ -663,7 +663,8 @@ handle_options(Opts0, _Role) -> handle_option(next_protocols_advertised, Opts, undefined), next_protocol_selector = make_next_protocol_selector( - handle_option(client_preferred_next_protocols, Opts, undefined)) + handle_option(client_preferred_next_protocols, Opts, undefined)), + log_alert = handle_option(log_alert, Opts, true) }, CbInfo = proplists:get_value(cb_info, Opts, {gen_tcp, tcp, tcp_closed, tcp_error}), @@ -675,7 +676,7 @@ handle_options(Opts0, _Role) -> reuse_session, reuse_sessions, ssl_imp, cb_info, renegotiate_at, secure_renegotiate, hibernate_after, erl_dist, next_protocols_advertised, - client_preferred_next_protocols], + client_preferred_next_protocols, log_alert], SockOpts = lists:foldl(fun(Key, PropList) -> proplists:delete(Key, PropList) @@ -840,6 +841,9 @@ validate_option(client_preferred_next_protocols = Opt, {Precedence, PreferredPro validate_option(client_preferred_next_protocols, undefined) -> undefined; +validate_option(log_alert, Value) when Value == true; + Value == false -> + Value; validate_option(next_protocols_advertised = Opt, Value) when is_list(Value) -> case tls_record:highest_protocol_version([]) of {3,0} -> diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl index 246fecf34a..159ba406d3 100644 --- a/lib/ssl/src/tls_connection.erl +++ b/lib/ssl/src/tls_connection.erl @@ -89,7 +89,6 @@ cert_db_ref, % ref() bytes_to_read, % integer(), # bytes to read in passive mode user_data_buffer, % binary() - log_alert, % boolean() renegotiation, % {boolean(), From | internal | peer} start_or_recv_from, % "gen_fsm From" timer, % start_or_recv_timer @@ -978,7 +977,7 @@ handle_sync_event(negotiated_next_protocol, _From, StateName, #state{next_protoc handle_sync_event(negotiated_next_protocol, _From, StateName, #state{next_protocol = NextProtocol} = State) -> {reply, {ok, NextProtocol}, StateName, State, get_timeout(State)}; -handle_sync_event({set_opts, Opts0}, _From, StateName, +handle_sync_event({set_opts, Opts0}, _From, StateName0, #state{socket_options = Opts1, socket = Socket, transport_cb = Transport, @@ -987,11 +986,12 @@ handle_sync_event({set_opts, Opts0}, _From, StateName, State1 = State0#state{socket_options = Opts}, if Opts#socket_options.active =:= false -> - {reply, Reply, StateName, State1, get_timeout(State1)}; + {reply, Reply, StateName0, State1, get_timeout(State1)}; Buffer =:= <<>>, Opts1#socket_options.active =:= false -> %% Need data, set active once {Record, State2} = next_record_if_active(State1), - case next_state(StateName, StateName, Record, State2) of + %% Note: Renogotiation may cause StateName0 =/= StateName + case next_state(StateName0, StateName0, Record, State2) of {next_state, StateName, State, Timeout} -> {reply, Reply, StateName, State, Timeout}; {stop, Reason, State} -> @@ -999,13 +999,14 @@ handle_sync_event({set_opts, Opts0}, _From, StateName, end; Buffer =:= <<>> -> %% Active once already set - {reply, Reply, StateName, State1, get_timeout(State1)}; + {reply, Reply, StateName0, State1, get_timeout(State1)}; true -> case read_application_data(<<>>, State1) of Stop = {stop,_,_} -> Stop; {Record, State2} -> - case next_state(StateName, StateName, Record, State2) of + %% Note: Renogotiation may cause StateName0 =/= StateName + case next_state(StateName0, StateName0, Record, State2) of {next_state, StateName, State, Timeout} -> {reply, Reply, StateName, State, Timeout}; {stop, Reason, State} -> @@ -2458,7 +2459,7 @@ do_format_reply(list, _,_, Data) -> binary_to_list(Data). header(0, <<>>) -> - []; + <<>>; header(_, <<>>) -> []; header(0, Binary) -> @@ -2677,7 +2678,6 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User, tls_cipher_texts = [], user_application = {Monitor, User}, user_data_buffer = <<>>, - log_alert = true, session_cache_cb = SessionCacheCb, renegotiation = {false, first}, start_or_recv_from = undefined, @@ -2778,12 +2778,11 @@ handle_alerts([Alert | Alerts], {next_state, StateName, State, _Timeout}) -> handle_alerts(Alerts, handle_alert(Alert, StateName, State)). handle_alert(#alert{level = ?FATAL} = Alert, StateName, - #state{socket = Socket, transport_cb = Transport, - start_or_recv_from = From, host = Host, + #state{socket = Socket, transport_cb = Transport, ssl_options = SslOpts, start_or_recv_from = From, host = Host, port = Port, session = Session, user_application = {_Mon, Pid}, - log_alert = Log, role = Role, socket_options = Opts} = State) -> + role = Role, socket_options = Opts} = State) -> invalidate_session(Role, Host, Port, Session), - log_alert(Log, StateName, Alert), + log_alert(SslOpts#ssl_options.log_alert, StateName, Alert), alert_user(Transport, Socket, StateName, Opts, Pid, From, Alert, Role), {stop, normal, State}; @@ -2793,21 +2792,21 @@ handle_alert(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert, {stop, {shutdown, peer_close}, State}; handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName, - #state{log_alert = Log, renegotiation = {true, internal}} = State) -> - log_alert(Log, StateName, Alert), + #state{ssl_options = SslOpts, renegotiation = {true, internal}} = State) -> + log_alert(SslOpts#ssl_options.log_alert, StateName, Alert), handle_normal_shutdown(Alert, StateName, State), {stop, {shutdown, peer_close}, State}; handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName, - #state{log_alert = Log, renegotiation = {true, From}} = State0) -> - log_alert(Log, StateName, Alert), + #state{ssl_options = SslOpts, renegotiation = {true, From}} = State0) -> + log_alert(SslOpts#ssl_options.log_alert, StateName, Alert), gen_fsm:reply(From, {error, renegotiation_rejected}), {Record, State} = next_record(State0), next_state(StateName, connection, Record, State); handle_alert(#alert{level = ?WARNING, description = ?USER_CANCELED} = Alert, StateName, - #state{log_alert = Log} = State0) -> - log_alert(Log, StateName, Alert), + #state{ssl_options = SslOpts} = State0) -> + log_alert(SslOpts#ssl_options.log_alert, StateName, Alert), {Record, State} = next_record(State0), next_state(StateName, StateName, Record, State). @@ -2845,7 +2844,7 @@ handle_own_alert(Alert, Version, StateName, #state{transport_cb = Transport, socket = Socket, connection_states = ConnectionStates, - log_alert = Log} = State) -> + ssl_options = SslOpts} = State) -> try %% Try to tell the other side {BinMsg, _} = encode_alert(Alert, Version, ConnectionStates), @@ -2855,7 +2854,7 @@ handle_own_alert(Alert, Version, StateName, ignore end, try %% Try to tell the local user - log_alert(Log, StateName, Alert), + log_alert(SslOpts#ssl_options.log_alert, StateName, Alert), handle_normal_shutdown(Alert,StateName, State) catch _:_ -> ok diff --git a/lib/ssl/test/ssl_packet_SUITE.erl b/lib/ssl/test/ssl_packet_SUITE.erl index 36f7af784d..d50498f547 100644 --- a/lib/ssl/test/ssl_packet_SUITE.erl +++ b/lib/ssl/test/ssl_packet_SUITE.erl @@ -1631,8 +1631,8 @@ header_decode_one_byte_active(Config) when is_list(Config) -> {from, self()}, {mfa, {?MODULE, client_header_decode_active, [Data, [11 | <<"Hello world">> ]]}}, - {options, [{active, true}, {header, 1}, - binary | ClientOpts]}]), + {options, [{active, true}, binary, {header, 1} + | ClientOpts]}]), ssl_test_lib:check_result(Server, ok, Client, ok), @@ -1688,7 +1688,7 @@ header_decode_two_bytes_two_sent_active(Config) when is_list(Config) -> Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, {from, self()}, {mfa, {?MODULE, server_header_decode_active, - [Data, [$H, $e]]}}, + [Data, [$H, $e | <<>>]]}}, {options, [{active, true}, binary, {header,2}|ServerOpts]}]), @@ -1697,7 +1697,7 @@ header_decode_two_bytes_two_sent_active(Config) when is_list(Config) -> {host, Hostname}, {from, self()}, {mfa, {?MODULE, client_header_decode_active, - [Data, [$H, $e]]}}, + [Data, [$H, $e | <<>>]]}}, {options, [{active, true}, {header, 2}, binary | ClientOpts]}]), @@ -1765,8 +1765,8 @@ header_decode_one_byte_passive(Config) when is_list(Config) -> {from, self()}, {mfa, {?MODULE, client_header_decode_passive, [Data, [11 | <<"Hello world">> ]]}}, - {options, [{active, false}, {header, 1}, - binary | ClientOpts]}]), + {options, [{active, false}, binary, {header, 1} + | ClientOpts]}]), ssl_test_lib:check_result(Server, ok, Client, ok), @@ -1822,7 +1822,7 @@ header_decode_two_bytes_two_sent_passive(Config) when is_list(Config) -> Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, {from, self()}, {mfa, {?MODULE, server_header_decode_passive, - [Data, [$H, $e]]}}, + [Data, [$H, $e | <<>>]]}}, {options, [{active, false}, binary, {header,2}|ServerOpts]}]), @@ -1831,7 +1831,7 @@ header_decode_two_bytes_two_sent_passive(Config) when is_list(Config) -> {host, Hostname}, {from, self()}, {mfa, {?MODULE, client_header_decode_passive, - [Data, [$H, $e]]}}, + [Data, [$H, $e | <<>>]]}}, {options, [{active, false}, {header, 2}, binary | ClientOpts]}]), @@ -2124,10 +2124,14 @@ client_header_decode_passive(Socket, Packet, Result) -> %% option and the bitsynax makes it obsolete! check_header_result([Byte1 | _], [Byte1]) -> ok; +check_header_result([Byte1 | _], [Byte1| <<>>]) -> + ok; check_header_result([Byte1, Byte2 | _], [Byte1, Byte2]) -> ok; -check_header_result(_,Got) -> - exit({?LINE, Got}). +check_header_result([Byte1, Byte2 | _], [Byte1, Byte2 | <<>>]) -> + ok; +check_header_result(Expected,Got) -> + exit({?LINE, {Expected, Got}}). server_line_packet_decode(Socket, Packet) when is_binary(Packet) -> [L1, L2] = string:tokens(binary_to_list(Packet), "\n"), diff --git a/lib/stdlib/doc/src/filelib.xml b/lib/stdlib/doc/src/filelib.xml index bd780b2b2f..d24d17be80 100644 --- a/lib/stdlib/doc/src/filelib.xml +++ b/lib/stdlib/doc/src/filelib.xml @@ -49,6 +49,12 @@ <datatype> <name name="dirname"/> </datatype> + <datatype> + <name name="dirname_all"/> + </datatype> + <datatype> + <name name="filename_all"/> + </datatype> </datatypes> <funcs> diff --git a/lib/stdlib/doc/src/io_lib.xml b/lib/stdlib/doc/src/io_lib.xml index 3dac259477..68352ffeb1 100644 --- a/lib/stdlib/doc/src/io_lib.xml +++ b/lib/stdlib/doc/src/io_lib.xml @@ -54,6 +54,9 @@ <name name="fread_error"/> </datatype> <datatype> + <name name="fread_item"/> + </datatype> + <datatype> <name name="latin1_string"/> </datatype> </datatypes> diff --git a/lib/stdlib/src/dets.erl b/lib/stdlib/src/dets.erl index 50812cc532..68b157c13c 100644 --- a/lib/stdlib/src/dets.erl +++ b/lib/stdlib/src/dets.erl @@ -951,10 +951,10 @@ do_trav(Proc, Acc, Fun) -> Error end. -do_trav(#dets_cont{bin = eof}, _Proc, Acc, _Fun) -> - Acc; do_trav(State, Proc, Acc, Fun) -> case req(Proc, {match_init, State, safe}) of + '$end_of_table'-> + Acc; {cont, {Bins, NewState}} -> do_trav_bins(NewState, Proc, Acc, Fun, lists:reverse(Bins)); Error -> diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl index 08b8541014..8f07750b9b 100644 --- a/lib/stdlib/src/erl_lint.erl +++ b/lib/stdlib/src/erl_lint.erl @@ -3219,7 +3219,8 @@ modify_line(T, F0) -> %% Forms. modify_line1({function,F,A}, _Mf) -> {function,F,A}; -modify_line1({function,M,F,A}, _Mf) -> {function,M,F,A}; +modify_line1({function,M,F,A}, Mf) -> + {function,modify_line1(M, Mf),modify_line1(F, Mf),modify_line1(A, Mf)}; modify_line1({attribute,L,record,{Name,Fields}}, Mf) -> {attribute,Mf(L),record,{Name,modify_line1(Fields, Mf)}}; modify_line1({attribute,L,spec,{Fun,Types}}, Mf) -> diff --git a/lib/stdlib/src/io_lib.erl b/lib/stdlib/src/io_lib.erl index a9b6d4131e..92a086b077 100644 --- a/lib/stdlib/src/io_lib.erl +++ b/lib/stdlib/src/io_lib.erl @@ -83,7 +83,8 @@ -export([write_unicode_string/1, write_unicode_char/1, deep_unicode_char_list/1]). --export_type([chars/0, latin1_string/0, continuation/0, fread_error/0]). +-export_type([chars/0, latin1_string/0, continuation/0, + fread_error/0, fread_item/0]). %%---------------------------------------------------------------------- @@ -106,6 +107,8 @@ | 'string' | 'unsigned'. +-type fread_item() :: string() | atom() | integer() | float(). + %%---------------------------------------------------------------------- %% Interface calls to sub-modules. @@ -120,7 +123,7 @@ fwrite(Format, Args) -> -spec fread(Format, String) -> Result when Format :: string(), String :: string(), - Result :: {'ok', InputList :: [term()], LeftOverChars :: string()} + Result :: {'ok', InputList :: [fread_item()], LeftOverChars :: string()} | {'more', RestFormat :: string(), Nchars :: non_neg_integer(), InputStack :: chars()} @@ -135,7 +138,7 @@ fread(Chars, Format) -> Format :: string(), Return :: {'more', Continuation1 :: continuation()} | {'done', Result, LeftOverChars :: string()}, - Result :: {'ok', InputList :: [term()]} + Result :: {'ok', InputList :: [fread_item()]} | 'eof' | {'error', {'fread', What :: fread_error()}}. diff --git a/lib/stdlib/src/io_lib_fread.erl b/lib/stdlib/src/io_lib_fread.erl index 92a34995b8..491e1f40d7 100644 --- a/lib/stdlib/src/io_lib_fread.erl +++ b/lib/stdlib/src/io_lib_fread.erl @@ -41,9 +41,9 @@ Format :: string(), Return :: {'more', Continuation1 :: io_lib:continuation()} | {'done', Result, LeftOverChars :: string()}, - Result :: {'ok', InputList :: io_lib:chars()} + Result :: {'ok', InputList :: [io_lib:fread_item()]} | 'eof' - | {'error', {'read', What :: io_lib:fread_error()}}. + | {'error', {'fread', What :: io_lib:fread_error()}}. fread([], Chars, Format) -> %%io:format("FREAD: ~w `~s'~n", [Format,Chars]), @@ -101,11 +101,12 @@ fread_line(Format0, Line, N0, Results0, More, Newline) -> -spec fread(Format, String) -> Result when Format :: string(), String :: string(), - Result :: {'ok', InputList :: io_lib:chars(), LeftOverChars :: string()} + Result :: {'ok', InputList :: [io_lib:fread_item()], + LeftOverChars :: string()} | {'more', RestFormat :: string(), Nchars :: non_neg_integer(), InputStack :: io_lib:chars()} - | {'error', What :: term()}. + | {'error', {'fread', What :: io_lib:fread_error()}}. fread(Format, Line) -> fread(Format, Line, 0, []). diff --git a/lib/stdlib/test/dets_SUITE.erl b/lib/stdlib/test/dets_SUITE.erl index 8ff7c3ccc9..059d553b00 100644 --- a/lib/stdlib/test/dets_SUITE.erl +++ b/lib/stdlib/test/dets_SUITE.erl @@ -52,7 +52,7 @@ simultaneous_open/1, insert_new/1, repair_continuation/1, otp_5487/1, otp_6206/1, otp_6359/1, otp_4738/1, otp_7146/1, otp_8070/1, otp_8856/1, otp_8898/1, otp_8899/1, otp_8903/1, - otp_8923/1, otp_9282/1]). + otp_8923/1, otp_9282/1, otp_11245/1]). -export([dets_dirty_loop/0]). @@ -109,7 +109,7 @@ all() -> many_clients, otp_4906, otp_5402, simultaneous_open, insert_new, repair_continuation, otp_5487, otp_6206, otp_6359, otp_4738, otp_7146, otp_8070, otp_8856, otp_8898, - otp_8899, otp_8903, otp_8923, otp_9282 + otp_8899, otp_8903, otp_8923, otp_9282, otp_11245 ]. groups() -> @@ -3898,6 +3898,28 @@ some_calls(Tab, Config) -> file:delete(File). +otp_11245(doc) -> + ["OTP-11245. Tables remained fixed after traversal"]; +otp_11245(suite) -> + []; +otp_11245(Config) when is_list(Config) -> + Tab = otp_11245, + File = filename(Tab, Config), + {ok, Tab} = dets:open_file(Tab, [{file,File}]), + N = 1024, + ins(Tab, N), + N = length(dets:match(Tab, '_')), + false = dets:info(Tab, safe_fixed), + dets:traverse(Tab, fun(_) -> continue end), + false = dets:info(Tab, safe_fixed), + N = dets:foldl(fun(_, N2) -> N2+1 end, 0, Tab), + false = dets:info(Tab, safe_fixed), + N = dets:foldr(fun(_, N2) -> N2+1 end, 0, Tab), + false = dets:info(Tab, safe_fixed), + ok = dets:close(Tab), + file:delete(File), + ok. + %% %% Parts common to several test cases %% diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl index f8345559c4..4dc7a44064 100644 --- a/lib/stdlib/test/erl_lint_SUITE.erl +++ b/lib/stdlib/test/erl_lint_SUITE.erl @@ -50,7 +50,7 @@ unsafe_vars_try/1, guard/1, otp_4886/1, otp_4988/1, otp_5091/1, otp_5276/1, otp_5338/1, otp_5362/1, otp_5371/1, otp_7227/1, otp_5494/1, otp_5644/1, otp_5878/1, - otp_5917/1, otp_6585/1, otp_6885/1, otp_10436/1, + otp_5917/1, otp_6585/1, otp_6885/1, otp_10436/1, otp_11254/1, export_all/1, bif_clash/1, behaviour_basic/1, behaviour_multiple/1, @@ -82,7 +82,7 @@ all() -> unsafe_vars, unsafe_vars2, unsafe_vars_try, guard, otp_4886, otp_4988, otp_5091, otp_5276, otp_5338, otp_5362, otp_5371, otp_7227, otp_5494, otp_5644, - otp_5878, otp_5917, otp_6585, otp_6885, otp_10436, export_all, + otp_5878, otp_5917, otp_6585, otp_6885, otp_10436, otp_11254,export_all, bif_clash, behaviour_basic, behaviour_multiple, otp_7550, otp_8051, format_warn, {group, on_load}, too_many_arguments, basic_errors, bin_syntax_errors]. @@ -2418,6 +2418,20 @@ otp_10436(Config) when is_list(Config) -> run_test2(Config, Ts2, []), ok. +otp_11254(doc) -> + "OTP-11254. Warnings for opaque types."; +otp_11254(suite) -> []; +otp_11254(Config) when is_list(Config) -> + Ts = <<"-module(p2). + -export([manifest/2]). + manifest(Module, Name) -> + fun Module:Nine/1. + ">>, + {error,[{4,erl_lint,{unbound_var,'Nine'}}], + [{3,erl_lint,{unused_var,'Name'}}]} = + run_test2(Config, Ts, []), + ok. + export_all(doc) -> "OTP-7392. Warning for export_all."; export_all(Config) when is_list(Config) -> diff --git a/lib/test_server/src/ts.erl b/lib/test_server/src/ts.erl index 4e5dc1b759..8e71c69d35 100644 --- a/lib/test_server/src/ts.erl +++ b/lib/test_server/src/ts.erl @@ -28,6 +28,7 @@ tests/0, tests/1, install/0, install/1, bench/0, bench/1, bench/2, benchmarks/0, + smoke_test/0, smoke_test/1,smoke_test/2, smoke_tests/0, estone/0, estone/1, cross_cover_analyse/1, compile_testcases/0, compile_testcases/1, @@ -174,6 +175,13 @@ help(installed) -> " ts:bench(Spec) - Runs all benchmarks in the given spec file.\n" " The spec file is actually ../*_test/Spec_bench.spec\n\n" " ts:bench can take the same Options argument as ts:run.\n" + "Smoke test functions:\n" + " ts:smoke_tests() - Get all available families of smoke tests\n" + " ts:smoke_test() - Runs all smoke tests\n" + " ts:smoke_test(Spec)\n" + " - Runs all smoke tests in the given spec file.\n" + " The spec file is actually ../*_test/Spec_smoke.spec\n\n" + " ts:smoke_test can take the same Options argument as ts:run.\n" "\n" "Installation (already done):\n" ], @@ -258,6 +266,7 @@ run(List, Opts) when is_list(List), is_list(Opts) -> %% Runs one test spec with Options run(Testspec, Config) when is_atom(Testspec), is_list(Config) -> Options=check_test_get_opts(Testspec, Config), + IsSmoke=proplists:get_value(smoke,Config), File=atom_to_list(Testspec), WhatToDo = case Testspec of @@ -293,6 +302,8 @@ run(Testspec, Config) when is_atom(Testspec), is_list(Config) -> case WhatToDo of skip -> create_skip_spec(Testspec, tests(Testspec)); + test when IsSmoke -> + File++"_smoke.spec"; test -> File++".spec" end, @@ -507,7 +518,22 @@ bench(Specs, Opts) -> benchmarks() -> ts_benchmark:benchmarks(). +smoke_test() -> + smoke_test([]). +smoke_test(Opts) when is_list(Opts) -> + smoke_test(smoke_tests(),Opts); +smoke_test(Spec) -> + smoke_test([Spec],[]). + +smoke_test(Spec, Opts) when is_atom(Spec) -> + smoke_test([Spec],Opts); +smoke_test(Specs, Opts) -> + run(Specs, [{smoke,true}|Opts]). + +smoke_tests() -> + {ok, Cwd} = file:get_cwd(), + ts_lib:specialized_specs(Cwd,"smoke"). %% %% estone/0, estone/1 diff --git a/lib/test_server/src/ts_benchmark.erl b/lib/test_server/src/ts_benchmark.erl index 516d22fd2d..bd6abc3372 100644 --- a/lib/test_server/src/ts_benchmark.erl +++ b/lib/test_server/src/ts_benchmark.erl @@ -30,12 +30,7 @@ benchmarks() -> {ok, Cwd} = file:get_cwd(), - Benches = filelib:wildcard( - filename:join([Cwd,"..","*_test","*_bench.spec"])), - [begin - Base = filename:basename(N), - list_to_atom(string:substr(Base,1,string:rstr(Base,"_")-1)) - end || N <- Benches]. + ts_lib:specialized_specs(Cwd,"bench"). run(Specs, Opts, Vars) -> {ok, Cwd} = file:get_cwd(), diff --git a/lib/test_server/src/ts_lib.erl b/lib/test_server/src/ts_lib.erl index a00f607fc1..52bb346043 100644 --- a/lib/test_server/src/ts_lib.erl +++ b/lib/test_server/src/ts_lib.erl @@ -27,6 +27,7 @@ erlang_type/1, initial_capital/1, specs/1, suites/2, + specialized_specs/2, subst_file/3, subst/2, print_data/1, make_non_erlang/2, maybe_atom_to_list/1, progress/4, @@ -91,13 +92,22 @@ initial_capital([C|Rest]) when $a =< C, C =< $z -> initial_capital(String) -> String. +specialized_specs(Dir,PostFix) -> + Specs = filelib:wildcard(filename:join([filename:dirname(Dir), + "*_test", "*_"++PostFix++".spec"])), + sort_tests([begin + Base = filename:basename(Name), + list_to_atom(string:substr(Base,1,string:rstr(Base,"_")-1)) + end || Name <- Specs]). + specs(Dir) -> Specs = filelib:wildcard(filename:join([filename:dirname(Dir), "*_test", "*.{dyn,}spec"])), - % Filter away all spec which end with _bench.spec + % Filter away all spec which end with {_bench,_smoke}.spec NoBench = fun(SpecName) -> case lists:reverse(SpecName) of "ceps.hcneb_"++_ -> false; + "ceps.ekoms_"++_ -> false; _ -> true end end, diff --git a/lib/tools/emacs/erlang.el b/lib/tools/emacs/erlang.el index f3bc95e3e5..3ecda1d285 100644 --- a/lib/tools/emacs/erlang.el +++ b/lib/tools/emacs/erlang.el @@ -2813,6 +2813,9 @@ Return nil if inside string, t if in a comment." (- (+ previous erlang-argument-indent) 1)))) (t (nth 2 stack-top)))) + ((= (following-char) ?,) + ;; a comma at the start of the line: line up with opening parenthesis. + (nth 2 stack-top)) (t (goto-char (nth 1 stack-top)) (let ((base (cond ((looking-at "[({]\\s *\\($\\|%\\)") diff --git a/lib/tools/emacs/test.erl.indented b/lib/tools/emacs/test.erl.indented index 6c9343f6cb..7e61bcc45b 100644 --- a/lib/tools/emacs/test.erl.indented +++ b/lib/tools/emacs/test.erl.indented @@ -700,3 +700,34 @@ some_function_name_xyz(xyzzy, #some_record{ field2 = Field1, field2 = Field2}}), {ok, SomeVariable}. + +commas_first() -> + {abc, [ {some_var, 1} + , {some_other_var, 2} + , {erlang_ftw, 9} + , {erlang_cookie, 'cookie'} + , {cmds, + [ {one, "sudo ls"} + , {one, "sudo ls"} + , {two, "sudo ls"} + , {three, "sudo ls"} + , {four, "sudo ls"} + , {three, "sudo ls"} + ] } + , {ssh_username, "yow"} + , {cluster, + [ {aaaa, [ {"10.198.55.12" , "" } + , {"10.198.55.13" , "" } + ] } + , {bbbb, [ {"10.198.55.151", "" } + , {"10.198.55.123", "" } + , {"10.198.55.34" , "" } + , {"10.198.55.85" , "" } + , {"10.198.55.67" , "" } + ] } + , {cccc, [ {"10.198.55.68" , "" } + , {"10.198.55.69" , "" } + ] } + ] } + ] + }. diff --git a/lib/tools/emacs/test.erl.orig b/lib/tools/emacs/test.erl.orig index 0f8c4a9175..932758997d 100644 --- a/lib/tools/emacs/test.erl.orig +++ b/lib/tools/emacs/test.erl.orig @@ -700,3 +700,34 @@ some_function_name_xyz(xyzzy, #some_record{ field2 = Field1, field2 = Field2}}), {ok, SomeVariable}. + +commas_first() -> + {abc, [ {some_var, 1} + , {some_other_var, 2} + , {erlang_ftw, 9} + , {erlang_cookie, 'cookie'} + , {cmds, + [ {one, "sudo ls"} + , {one, "sudo ls"} + , {two, "sudo ls"} + , {three, "sudo ls"} + , {four, "sudo ls"} + , {three, "sudo ls"} + ] } + , {ssh_username, "yow"} + , {cluster, + [ {aaaa, [ {"10.198.55.12" , "" } + , {"10.198.55.13" , "" } + ] } + , {bbbb, [ {"10.198.55.151", "" } + , {"10.198.55.123", "" } + , {"10.198.55.34" , "" } + , {"10.198.55.85" , "" } + , {"10.198.55.67" , "" } + ] } + , {cccc, [ {"10.198.55.68" , "" } + , {"10.198.55.69" , "" } + ] } + ] } +] +}. diff --git a/lib/xmerl/doc/src/xmerl_ug.xmlsrc b/lib/xmerl/doc/src/xmerl_ug.xmlsrc index 8a0805020e..10c770c400 100644 --- a/lib/xmerl/doc/src/xmerl_ug.xmlsrc +++ b/lib/xmerl/doc/src/xmerl_ug.xmlsrc @@ -409,9 +409,9 @@ Data = specification but the basic functionality. For all details see the <seealso marker="xmerl_xs">reference manual</seealso></p> <p>First, some words about the xmerl_xs functionality:</p> - <p>You need to wright template functions to be able to control + <p>You need to write template functions to be able to control what kind of output you want. Thus if you want to encapsulate a - <c>bike</c> element in <p> tags you simply wright a + <c>bike</c> element in <p> tags you simply write a function:</p> <pre> template(E = #xmlElement{name='bike'}) -> |