aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--erts/doc/src/erl.xml6
-rw-r--r--erts/doc/src/erlang.xml20
-rw-r--r--erts/emulator/Makefile.in2
-rw-r--r--erts/emulator/beam/erl_alloc.c32
-rw-r--r--erts/emulator/beam/erl_alloc.types27
-rw-r--r--erts/emulator/beam/erl_alloc_util.h4
-rw-r--r--erts/emulator/beam/erl_ao_firstfit_alloc.c32
-rw-r--r--erts/emulator/beam/erl_ao_firstfit_alloc.h6
-rw-r--r--erts/emulator/beam/erl_bif_chksum.c2
-rw-r--r--erts/emulator/beam/erl_bif_info.c3
-rw-r--r--erts/emulator/beam/erl_db.c53
-rw-r--r--erts/emulator/beam/erl_db.h5
-rw-r--r--erts/emulator/beam/erl_db_util.c8
-rw-r--r--erts/emulator/beam/erl_process.c5
-rw-r--r--erts/emulator/beam/erl_unicode.c10
-rw-r--r--erts/emulator/beam/utils.c7
-rw-r--r--erts/emulator/sys/common/erl_mmap.c4
-rw-r--r--erts/emulator/test/system_info_SUITE.erl17
-rw-r--r--erts/lib_src/Makefile.in2
-rw-r--r--erts/preloaded/ebin/erlang.beambin103344 -> 103296 bytes
-rw-r--r--erts/preloaded/src/erlang.erl5
-rw-r--r--erts/preloaded/src/erts_code_purger.erl4
-rw-r--r--erts/preloaded/src/zlib.erl50
-rw-r--r--lib/compiler/src/beam_asm.erl4
-rw-r--r--lib/crypto/c_src/crypto.c11
-rw-r--r--lib/dialyzer/src/dialyzer.erl4
-rw-r--r--lib/dialyzer/src/dialyzer_contracts.erl99
-rw-r--r--lib/dialyzer/src/typer.erl2
-rw-r--r--lib/dialyzer/test/overspecs_SUITE_data/dialyzer_options1
-rw-r--r--lib/dialyzer/test/overspecs_SUITE_data/results/iodata2
-rw-r--r--lib/dialyzer/test/overspecs_SUITE_data/results/iolist2
-rw-r--r--lib/dialyzer/test/overspecs_SUITE_data/src/iodata.erl41
-rw-r--r--lib/dialyzer/test/overspecs_SUITE_data/src/iolist.erl41
-rw-r--r--lib/dialyzer/test/specdiffs_SUITE_data/dialyzer_options1
-rw-r--r--lib/dialyzer/test/specdiffs_SUITE_data/results/iodata3
-rw-r--r--lib/dialyzer/test/specdiffs_SUITE_data/results/iolist2
-rw-r--r--lib/dialyzer/test/specdiffs_SUITE_data/src/iodata.erl41
-rw-r--r--lib/dialyzer/test/specdiffs_SUITE_data/src/iolist.erl41
-rw-r--r--lib/erl_interface/src/prog/erl_call.c11
-rw-r--r--lib/kernel/src/code.erl4
-rw-r--r--lib/kernel/src/file_server.erl6
-rw-r--r--lib/kernel/src/kernel_refc.erl4
-rw-r--r--lib/public_key/doc/src/public_key.xml28
-rw-r--r--lib/public_key/src/public_key.erl38
-rw-r--r--lib/public_key/test/public_key_SUITE.erl19
-rw-r--r--lib/public_key/test/public_key_SUITE_data/pkix_verify_hostname_subjAltName.pem24
-rw-r--r--lib/public_key/test/public_key_SUITE_data/verify_hostname.conf3
-rw-r--r--lib/ssh/src/ssh_client_channel.erl2
-rw-r--r--lib/ssh/src/ssh_connection.erl42
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl8
-rw-r--r--lib/ssh/test/Makefile3
-rw-r--r--lib/ssh/test/ssh_chan_behaviours_SUITE.erl152
-rw-r--r--lib/ssh/test/ssh_chan_behaviours_SUITE_data/ssh_host_dsa_key13
-rw-r--r--lib/ssh/test/ssh_chan_behaviours_SUITE_data/ssh_host_dsa_key.pub11
-rw-r--r--lib/ssh/test/ssh_chan_behaviours_SUITE_data/ssh_host_ecdsa_key6
-rw-r--r--lib/ssh/test/ssh_chan_behaviours_SUITE_data/ssh_host_ecdsa_key.pub1
-rw-r--r--lib/ssh/test/ssh_chan_behaviours_SUITE_data/ssh_host_rsa_key16
-rw-r--r--lib/ssh/test/ssh_chan_behaviours_SUITE_data/ssh_host_rsa_key.pub5
-rw-r--r--lib/ssh/test/ssh_chan_behaviours_client.erl143
-rw-r--r--lib/ssh/test/ssh_chan_behaviours_server.erl96
-rw-r--r--lib/stdlib/doc/src/calendar.xml9
-rw-r--r--lib/stdlib/doc/src/ets.xml6
-rw-r--r--lib/stdlib/src/array.erl2
-rw-r--r--lib/stdlib/src/io.erl11
-rw-r--r--lib/stdlib/test/ets_SUITE.erl13
-rw-r--r--lib/syntax_tools/src/erl_syntax.erl4
-rw-r--r--lib/syntax_tools/test/syntax_tools_SUITE.erl24
-rw-r--r--make/otp_release_targets.mk2
-rw-r--r--system/doc/efficiency_guide/advanced.xml5
69 files changed, 1054 insertions, 256 deletions
diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml
index 74654a295d..4cf0066999 100644
--- a/erts/doc/src/erl.xml
+++ b/erts/doc/src/erl.xml
@@ -631,14 +631,16 @@
of process heaps is destroyed by the crash dump generation.</p>
<p>Option <c>+d</c> instructs the emulator to produce only a
core dump and no crash dump if an internal error is detected.</p>
- <p>Calling <seealso marker="erlang:halt/1">
+ <p>Calling <seealso marker="erlang#halt/1">
<c>erlang:halt/1</c></seealso> with a string argument still
produces a crash dump. On Unix systems, sending an emulator process
a <c>SIGUSR1</c> signal also forces a crash dump.</p>
</item>
<tag><marker id="+e"/><c><![CDATA[+e Number]]></c></tag>
<item>
- <p>Sets the maximum number of ETS tables.</p>
+ <p>Sets the maximum number of ETS tables. This limit is
+ <seealso marker="stdlib:ets#max_ets_tables">partially obsolete</seealso>.
+ </p>
</item>
<tag><c><![CDATA[+ec]]></c></tag>
<item>
diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml
index cff56b9cb8..15bd80e72f 100644
--- a/erts/doc/src/erlang.xml
+++ b/erts/doc/src/erlang.xml
@@ -36,8 +36,8 @@
in this module. Some of the BIFs are viewed more
or less as part of the Erlang programming language and are
<em>auto-imported</em>. Thus, it is not necessary to specify the
- module name. For example, the calls <c>atom_to_list(Erlang)</c>
- and <c>erlang:atom_to_list(Erlang)</c> are identical.</p>
+ module name. For example, the calls <c>atom_to_list(erlang)</c>
+ and <c>erlang:atom_to_list(erlang)</c> are identical.</p>
<p>Auto-imported BIFs are listed without module prefix.
BIFs listed with module prefix are not auto-imported.</p>
@@ -8166,16 +8166,18 @@ ok
<c>erl(1)</c>.
</p>
</item>
+ <tag><marker id="system_info_ets_count"/>
+ <c>ets_count</c></tag>
+ <item>
+ <p>Returns the number of ETS tables currently existing at the
+ local node.</p>
+ </item>
<tag><marker id="system_info_ets_limit"/>
<c>ets_limit</c></tag>
<item>
- <p>Returns the maximum number of ETS tables allowed. This
- limit can be increased at startup by passing
- command-line flag
- <seealso marker="erts:erl#+e"><c>+e</c></seealso> to
- <c>erl(1)</c> or by setting environment variable
- <c>ERL_MAX_ETS_TABLES</c> before starting the Erlang
- runtime system.</p>
+ <p>Returns the limit for number of ETS tables. This limit is
+ <seealso marker="stdlib:ets#max_ets_tables">partially obsolete</seealso>
+ and number of tables are only limited by available memory.</p>
</item>
<tag><marker id="system_info_port_count"/><c>port_count</c></tag>
<item>
diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in
index 221cf84622..054692819e 100644
--- a/erts/emulator/Makefile.in
+++ b/erts/emulator/Makefile.in
@@ -517,7 +517,9 @@ release_docs_spec:
# Generated source code. Put in $(TARGET) directory
#
+ifneq ($(strip $(CREATE_DIRS)),)
_create_dirs := $(shell mkdir -p $(CREATE_DIRS))
+endif
# has to be run after _create_dirs
diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c
index d99d2ea57b..575d6ca867 100644
--- a/erts/emulator/beam/erl_alloc.c
+++ b/erts/emulator/beam/erl_alloc.c
@@ -2821,20 +2821,20 @@ erts_allocator_options(void *proc)
Eterm as[4];
Eterm ts[4];
- as[l] = am_atom_put("e", 1);
+ as[l] = ERTS_MAKE_AM("e");
ts[l++] = am_true;
switch (a) {
case ERTS_ALC_A_SYSTEM:
- as[l] = am_atom_put("m", 1);
- ts[l++] = am_atom_put("libc", 4);
+ as[l] = ERTS_MAKE_AM("m");
+ ts[l++] = ERTS_MAKE_AM("libc");
if(sas.trim_threshold >= 0) {
- as[l] = am_atom_put("tt", 2);
+ as[l] = ERTS_MAKE_AM("tt");
ts[l++] = erts_bld_uint(hpp, szp,
(Uint) sas.trim_threshold);
}
if(sas.top_pad >= 0) {
- as[l] = am_atom_put("tp", 2);
+ as[l] = ERTS_MAKE_AM("tp");
ts[l++] = erts_bld_uint(hpp, szp, (Uint) sas.top_pad);
}
break;
@@ -2848,7 +2848,7 @@ erts_allocator_options(void *proc)
}
else {
- Eterm atom = am_atom_put("e", 1);
+ Eterm atom = ERTS_MAKE_AM("e");
Eterm term = am_false;
tmp = erts_bld_2tup_list(hpp, szp, 1, &atom, &term);
}
@@ -2859,12 +2859,12 @@ erts_allocator_options(void *proc)
#if HAVE_ERTS_MSEG
if (use_mseg) {
- atoms[length] = am_atom_put("mseg_alloc", 10);
+ atoms[length] = ERTS_MAKE_AM("mseg_alloc");
terms[length++] = erts_mseg_info_options(0, NULL, NULL, hpp, szp);
}
#endif
- atoms[length] = am_atom_put("alloc_util", 10);
+ atoms[length] = ERTS_MAKE_AM("alloc_util");
terms[length++] = erts_alcu_au_info_options(NULL, NULL, hpp, szp);
#if HAVE_ERTS_MMAP
@@ -2874,17 +2874,15 @@ erts_allocator_options(void *proc)
#endif
{
Eterm o[1], v[1];
- o[0] = am_atom_put("t", 1);
+ o[0] = ERTS_MAKE_AM("t");
v[0] = erts_mtrace_enabled ? am_true : am_false;
- atoms[length] = am_atom_put("instr", 5);
+ atoms[length] = ERTS_MAKE_AM("instr");
terms[length++] = erts_bld_2tup_list(hpp, szp, 1, o, v);
}
- atoms[length] = am_atom_put("lock_physical_memory", 20);
- terms[length++] = (lock_all_physical_memory
- ? am_atom_put("all", 3)
- : am_atom_put("no", 2));
+ atoms[length] = ERTS_MAKE_AM("lock_physical_memory");
+ terms[length++] = (lock_all_physical_memory ? am_all : am_no);
settings = erts_bld_2tup_list(hpp, szp, length, atoms, terms);
@@ -2899,10 +2897,10 @@ erts_allocator_options(void *proc)
#if HAVE_ERTS_MSEG
if (use_mseg)
- terms[length++] = am_atom_put("mseg_alloc", 10);
+ terms[length++] = ERTS_MAKE_AM("mseg_alloc");
#endif
#if ERTS_HAVE_ERTS_SYS_ALIGNED_ALLOC
- terms[length++] = am_atom_put("sys_aligned_alloc", 17);
+ terms[length++] = ERTS_MAKE_AM("sys_aligned_alloc");
#endif
#if defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION)
terms[length++] = ERTS_MAKE_AM("literal_mmap");
@@ -2911,7 +2909,7 @@ erts_allocator_options(void *proc)
#if defined(__GLIBC__)
{
- Eterm AM_glibc = am_atom_put("glibc", 5);
+ Eterm AM_glibc = ERTS_MAKE_AM("glibc");
Eterm version;
version = erts_bld_cons(hpp,
diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types
index 9db600dce0..5409b89bab 100644
--- a/erts/emulator/beam/erl_alloc.types
+++ b/erts/emulator/beam/erl_alloc.types
@@ -118,9 +118,6 @@ type PORT DRIVER SYSTEM port
type ATOM LONG_LIVED ATOM atom_entry
type MODULE LONG_LIVED CODE module_entry
type REG_PROC STANDARD PROCESSES reg_proc
-type LINK_LH STANDARD PROCESSES link_lh
-type SUSPEND_MON STANDARD PROCESSES suspend_monitor
-type PEND_SUSPEND SHORT_LIVED PROCESSES pending_suspend
type PROC_LIST SHORT_LIVED PROCESSES proc_list
type SAVED_ESTACK SHORT_LIVED PROCESSES saved_estack
type FUN_ENTRY LONG_LIVED CODE fun_entry
@@ -133,7 +130,6 @@ type TMP_HEAP TEMPORARY PROCESSES tmp_heap
type MSG_REF FIXED_SIZE PROCESSES msg_ref
type MSG EHEAP PROCESSES message
type MSGQ_CHNG SHORT_LIVED PROCESSES messages_queue_change
-type MSG_ROOTS TEMPORARY PROCESSES msg_roots
type ROOTSET TEMPORARY PROCESSES root_set
type LOADER_TMP TEMPORARY CODE loader_tmp
type PREPARED_CODE SHORT_LIVED CODE prepared_code
@@ -178,7 +174,6 @@ type BPD STANDARD SYSTEM bpd
type LINEBUF STANDARD SYSTEM line_buf
type IOQ STANDARD SYSTEM io_queue
type BITS_BUF STANDARD SYSTEM bits_buf
-type TMP_DIST_BUF TEMPORARY SYSTEM tmp_dist_buf
type ASYNC_DATA LONG_LIVED SYSTEM internal_async_data
type ESTACK TEMPORARY SYSTEM estack
type DB_TABLE ETS ETS db_tab
@@ -191,7 +186,6 @@ type DB_MC_STK TEMPORARY ETS db_mc_stack
type DB_MS_RUN_HEAP SHORT_LIVED ETS db_match_spec_run_heap
type DB_MS_CMPL_HEAP TEMPORARY ETS db_match_spec_cmpl_heap
type DB_SEG ETS ETS db_segment
-type DB_SEG_TAB ETS ETS db_segment_tab
type DB_STK ETS ETS db_stack
type DB_TRANS_TAB ETS ETS db_trans_tab
type DB_SEL_LIST ETS ETS db_select_list
@@ -200,7 +194,6 @@ type DB_DMC_ERR_INFO ETS ETS db_dmc_error_info
type DB_TERM ETS ETS db_term
type DB_PROC_CLEANUP SHORT_LIVED ETS db_proc_cleanup_state
type ETS_ALL_REQ SHORT_LIVED ETS ets_all_request
-type INSTR_INFO LONG_LIVED SYSTEM instr_info
type LOGGER_DSBUF TEMPORARY SYSTEM logger_dsbuf
type TMP_DSBUF TEMPORARY SYSTEM tmp_dsbuf
type INFO_DSBUF SYSTEM SYSTEM info_dsbuf
@@ -215,7 +208,6 @@ type PT_HNDL_LIST SHORT_LIVED SYSTEM port_task_handle_list
type MISC_OP_LIST SHORT_LIVED SYSTEM misc_op_list
type PORT_NAMES SHORT_LIVED SYSTEM port_names
type PORT_DATA_LOCK DRIVER SYSTEM port_data_lock
-type NODES_MON STANDARD PROCESSES nodes_monitor
type PTAB_LIST_DEL SHORT_LIVED PROCESSES ptab_list_deleted_el
type PTAB_LIST_CNKI SHORT_LIVED PROCESSES ptab_list_chunk_info
type PTAB_LIST_PIDS SHORT_LIVED PROCESSES ptab_list_pids
@@ -238,10 +230,8 @@ type CPUDATA LONG_LIVED SYSTEM cpu_data
type TMP_CPU_IDS SHORT_LIVED SYSTEM tmp_cpu_ids
type EXT_TERM_DATA SHORT_LIVED PROCESSES external_term_data
type CPU_GRPS_MAP LONG_LIVED SYSTEM cpu_groups_map
-type AUX_WORK_TMO LONG_LIVED SYSTEM aux_work_timeouts
type MISC_AUX_WORK_Q LONG_LIVED SYSTEM misc_aux_work_q
type CODE_IX_LOCK_Q SHORT_LIVED SYSTEM code_ix_lock_q
-type PROC_INTERVAL LONG_LIVED SYSTEM process_interval
type BUSY_CALLER_TAB SHORT_LIVED SYSTEM busy_caller_table
type BUSY_CALLER SHORT_LIVED SYSTEM busy_caller
type PROC_SYS_TSK SHORT_LIVED PROCESSES proc_sys_task
@@ -250,8 +240,6 @@ type NEW_TIME_OFFSET SHORT_LIVED SYSTEM new_time_offset
type IOB_REQ SHORT_LIVED SYSTEM io_bytes_request
type TRACER_NIF LONG_LIVED SYSTEM tracer_nif
type TRACE_MSG_QUEUE SHORT_LIVED SYSTEM trace_message_queue
-type SCHED_ASYNC_JOB SHORT_LIVED SYSTEM async_calls
-type DIRTY_START STANDARD PROCESSES dirty_start
type DIRTY_SL SHORT_LIVED SYSTEM dirty_short_lived
type MREF_NSCHED_ENT FIXED_SIZE SYSTEM nsched_magic_ref_entry
type MREF_ENT STANDARD SYSTEM magic_ref_entry
@@ -259,7 +247,6 @@ type MREF_TAB_BKTS STANDARD SYSTEM magic_ref_table_buckets
type MREF_TAB LONG_LIVED SYSTEM magic_ref_table
type MINDIRECTION FIXED_SIZE SYSTEM magic_indirection
type BINARY_FIND SHORT_LIVED PROCESSES binary_find
-type OPEN_PORT_ENV TEMPORARY SYSTEM open_port_env
type CRASH_DUMP STANDARD SYSTEM crash_dump
type DIST_TRANSCODE SHORT_LIVED SYSTEM dist_transcode_context
@@ -273,10 +260,8 @@ type THR_Q_LL LONG_LIVED SYSTEM long_lived_thr_queue
type ASYNC SHORT_LIVED SYSTEM async
type ZLIB STANDARD SYSTEM zlib
-type PORT_LOCK STANDARD SYSTEM port_lock
type DRIVER_LOCK STANDARD SYSTEM driver_lock
type XPORTS_LIST SHORT_LIVED SYSTEM extra_port_list
-type PROC_LCK_WTR LONG_LIVED SYSTEM proc_lock_waiter
type RUNQ_BLNS LONG_LIVED SYSTEM run_queue_balancing
type THR_PRGR_IDATA LONG_LIVED SYSTEM thr_prgr_internal_data
type THR_PRGR_DATA LONG_LIVED SYSTEM thr_prgr_data
@@ -317,12 +302,6 @@ type HIPE_EXEC EXEC CODE hipe_code
+endif
-+if heap_frag_elim_test
-
-type SSB SHORT_LIVED PROCESSES ssb
-
-+endif
-
+if lcnt
type LCNT_CARRIER STANDARD SYSTEM lcnt_lock_info_carrier
@@ -342,7 +321,6 @@ type PURGE_DATA SHORT_LIVED CODE purge_data
type DB_HEIR_DATA STANDARD ETS db_heir_data
type DB_MS_PSDO_PROC LONG_LIVED ETS db_match_pseudo_proc
type SCHDLR_DATA LONG_LIVED SYSTEM scheduler_data
-type LL_TEMP_TERM LONG_LIVED SYSTEM ll_temp_term
type NIF_TRAP_EXPORT STANDARD PROCESSES nif_trap_export_entry
type NIF_EXP_TRACE FIXED_SIZE PROCESSES nif_export_trace
@@ -366,21 +344,17 @@ type DRV_TAB LONG_LIVED SYSTEM drv_tab
type DRV_EV_STATE LONG_LIVED SYSTEM driver_event_state
type DRV_SEL_D_STATE FIXED_SIZE SYSTEM driver_select_data_state
type NIF_SEL_D_STATE FIXED_SIZE SYSTEM enif_select_data_state
-type FD_LIST SHORT_LIVED SYSTEM fd_list
type POLLSET LONG_LIVED SYSTEM pollset
type POLLSET_UPDREQ SHORT_LIVED SYSTEM pollset_update_req
type POLL_FDS LONG_LIVED SYSTEM poll_fds
-type POLL_RES_EVS LONG_LIVED SYSTEM poll_result_events
type FD_STATUS LONG_LIVED SYSTEM fd_status
type SELECT_FDS LONG_LIVED SYSTEM select_fds
+if unix
type SYS_READ_BUF TEMPORARY SYSTEM sys_read_buf
-type FD_TAB LONG_LIVED SYSTEM fd_tab
type FD_ENTRY_BUF STANDARD SYSTEM fd_entry_buf
type CS_PROG_PATH LONG_LIVED SYSTEM cs_prog_path
-type PRT_REP_EXIT STANDARD SYSTEM port_report_exit
type SYS_BLOCKING STANDARD SYSTEM sys_blocking
type SYS_WRITE_BUF TEMPORARY SYSTEM sys_write_buf
@@ -392,7 +366,6 @@ type SYS_WRITE_BUF TEMPORARY SYSTEM sys_write_buf
type DRV_DATA_BUF SYSTEM SYSTEM drv_data_buf
type PRELOADED LONG_LIVED SYSTEM preloaded
type WAITER_OBJ LONG_LIVED SYSTEM waiter_object
-type CON_VPRINTF_BUF TEMPORARY SYSTEM con_vprintf_buf
+endif
diff --git a/erts/emulator/beam/erl_alloc_util.h b/erts/emulator/beam/erl_alloc_util.h
index ff4d10b206..cbae8ce98a 100644
--- a/erts/emulator/beam/erl_alloc_util.h
+++ b/erts/emulator/beam/erl_alloc_util.h
@@ -414,6 +414,10 @@ struct AOFF_RBTree_t_ {
AOFF_RBTree_t *right;
Uint32 flags;
Uint32 max_sz; /* of all blocks in this sub-tree */
+ union {
+ AOFF_RBTree_t* next; /* for best fit */
+ Sint64 birth_time; /* for age first fit */
+ } u;
};
void aoff_add_pooled_mbc(Allctr_t*, Carrier_t*);
diff --git a/erts/emulator/beam/erl_ao_firstfit_alloc.c b/erts/emulator/beam/erl_ao_firstfit_alloc.c
index 0c5545401a..ebbe4af53d 100644
--- a/erts/emulator/beam/erl_ao_firstfit_alloc.c
+++ b/erts/emulator/beam/erl_ao_firstfit_alloc.c
@@ -100,22 +100,14 @@
#define AOFF_BLK_SZ(B) MBC_FBLK_SZ(&(B)->hdr)
-/* BF block nodes keeps list of all with equal size
- */
-typedef struct {
- AOFF_RBTree_t t;
- AOFF_RBTree_t *next;
-}AOFF_RBTreeList_t;
-
-#define LIST_NEXT(N) (((AOFF_RBTreeList_t*) (N))->next)
-#define LIST_PREV(N) (((AOFF_RBTreeList_t*) (N))->t.parent)
+#define LIST_NEXT(N) (((AOFF_RBTree_t*)(N))->u.next)
+#define LIST_PREV(N) (((AOFF_RBTree_t*)(N))->parent)
typedef struct AOFF_Carrier_t_ AOFF_Carrier_t;
struct AOFF_Carrier_t_ {
Carrier_t crr;
AOFF_RBTree_t rbt_node; /* My node in the carrier tree */
- Sint64 birth_time;
AOFF_RBTree_t* root; /* Root of my block tree */
};
#define RBT_NODE_TO_MBC(PTR) ErtsContainerStruct((PTR), AOFF_Carrier_t, rbt_node)
@@ -199,9 +191,7 @@ static ERTS_INLINE SWord cmp_blocks(enum AOFFSortOrder order,
{
ASSERT(lhs != rhs);
if (order == FF_AGEFF) {
- AOFF_Carrier_t* lc = RBT_NODE_TO_MBC(lhs);
- AOFF_Carrier_t* rc = RBT_NODE_TO_MBC(rhs);
- Sint64 diff = lc->birth_time - rc->birth_time;
+ Sint64 diff = lhs->u.birth_time - rhs->u.birth_time;
#ifdef ARCH_64
if (diff)
return diff;
@@ -296,8 +286,10 @@ erts_aoffalc_start(AOFFAllctr_t *alc,
allctr->mbc_header_size = sizeof(AOFF_Carrier_t);
allctr->min_mbc_size = MIN_MBC_SZ;
allctr->min_mbc_first_free_size = MIN_MBC_FIRST_FREE_SZ;
- allctr->min_block_size = (aoffinit->blk_order == FF_BF ?
- sizeof(AOFF_RBTreeList_t):sizeof(AOFF_RBTree_t));
+ allctr->min_block_size = (aoffinit->blk_order == FF_BF
+ ? (offsetof(AOFF_RBTree_t, u.next)
+ + ErtsSizeofMember(AOFF_RBTree_t, u.next))
+ : offsetof(AOFF_RBTree_t, u));
allctr->vsn_str = ERTS_ALC_AOFF_ALLOC_VSN_STR;
@@ -939,8 +931,11 @@ static void aoff_creating_mbc(Allctr_t *allctr, Carrier_t *carrier)
HARD_CHECK_TREE(NULL, alc->crr_order, *root, 0);
crr->rbt_node.hdr.bhdr = 0;
- if (alc->crr_order == FF_AGEFF || IS_DEBUG)
- crr->birth_time = get_birth_time();
+ if (alc->crr_order == FF_AGEFF || IS_DEBUG) {
+ Sint64 bt = get_birth_time();
+ crr->rbt_node.u.birth_time = bt;
+ crr->crr.cpool.pooled.u.birth_time = bt;
+ }
rbt_insert(alc->crr_order, root, &crr->rbt_node);
/* aoff_link_free_block will add free block later */
@@ -978,6 +973,7 @@ static void aoff_add_mbc(Allctr_t *allctr, Carrier_t *carrier)
void aoff_add_pooled_mbc(Allctr_t *allctr, Carrier_t *crr)
{
+ AOFFAllctr_t *alc = (AOFFAllctr_t *) allctr;
AOFF_RBTree_t **root = &allctr->cpool.pooled_tree;
ASSERT(allctr == crr->cpool.orig_allctr);
@@ -985,7 +981,7 @@ void aoff_add_pooled_mbc(Allctr_t *allctr, Carrier_t *crr)
/* Link carrier in address order tree
*/
- rbt_insert(FF_AOFF, root, &crr->cpool.pooled);
+ rbt_insert(alc->crr_order, root, &crr->cpool.pooled);
HARD_CHECK_TREE(NULL, 0, *root, 0);
}
diff --git a/erts/emulator/beam/erl_ao_firstfit_alloc.h b/erts/emulator/beam/erl_ao_firstfit_alloc.h
index 9cf4fc81a8..dad864801f 100644
--- a/erts/emulator/beam/erl_ao_firstfit_alloc.h
+++ b/erts/emulator/beam/erl_ao_firstfit_alloc.h
@@ -29,10 +29,10 @@
typedef struct AOFFAllctr_t_ AOFFAllctr_t;
enum AOFFSortOrder {
- FF_AGEFF = 0,
+ FF_AGEFF = 0, /* carrier trees only */
FF_AOFF = 1,
- FF_AOBF = 2,
- FF_BF = 3
+ FF_AOBF = 2, /* block trees only */
+ FF_BF = 3 /* block trees only */
};
typedef struct {
diff --git a/erts/emulator/beam/erl_bif_chksum.c b/erts/emulator/beam/erl_bif_chksum.c
index 9095bcd380..cf92687595 100644
--- a/erts/emulator/beam/erl_bif_chksum.c
+++ b/erts/emulator/beam/erl_bif_chksum.c
@@ -44,7 +44,7 @@ void erts_init_bif_chksum(void)
{
/* Non visual BIF to trap to. */
erts_init_trap_export(&chksum_md5_2_exp,
- am_erlang, am_atom_put("md5_trap",8), 2,
+ am_erlang, ERTS_MAKE_AM("md5_trap"), 2,
&md5_2);
}
diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c
index 8b2b1a58c7..5789fa8e71 100644
--- a/erts/emulator/beam/erl_bif_info.c
+++ b/erts/emulator/beam/erl_bif_info.c
@@ -3129,6 +3129,9 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
else if (ERTS_IS_ATOM_STR("ets_limit",BIF_ARG_1)) {
BIF_RET(make_small(erts_db_get_max_tabs()));
}
+ else if (ERTS_IS_ATOM_STR("ets_count",BIF_ARG_1)) {
+ BIF_RET(make_small(erts_ets_table_count()));
+ }
else if (ERTS_IS_ATOM_STR("atom_limit",BIF_ARG_1)) {
BIF_RET(make_small(erts_get_atom_limit()));
}
diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c
index 3a29f8cf56..5bae1730e4 100644
--- a/erts/emulator/beam/erl_db.c
+++ b/erts/emulator/beam/erl_db.c
@@ -322,10 +322,10 @@ erts_db_make_tid(Process *c_p, DbTableCommon *tb)
/*
** The meta hash table of all NAMED ets tables
*/
-# define META_NAME_TAB_LOCK_CNT 16
+# define META_NAME_TAB_LOCK_CNT 256
union {
erts_rwmtx_t lck;
- byte _cache_line_alignment[64];
+ byte align[ERTS_ALC_CACHE_LINE_ALIGN_SIZE(sizeof(erts_rwmtx_t))];
}meta_name_tab_rwlocks[META_NAME_TAB_LOCK_CNT];
static struct meta_name_tab_entry {
union {
@@ -450,7 +450,7 @@ save_sched_table(Process *c_p, DbTable *tb)
DbTable *first;
ASSERT(esdp);
- esdp->ets_tables.count++;
+ erts_atomic_inc_nob(&esdp->ets_tables.count);
erts_refc_inc(&tb->common.refc, 1);
first = esdp->ets_tables.clist;
@@ -474,8 +474,8 @@ remove_sched_table(ErtsSchedulerData *esdp, DbTable *tb)
ASSERT(erts_get_ref_numbers_thr_id(ERTS_MAGIC_BIN_REFN(tb->common.btid))
== (Uint32) esdp->no);
- ASSERT(esdp->ets_tables.count > 0);
- esdp->ets_tables.count--;
+ ASSERT(erts_atomic_read_nob(&esdp->ets_tables.count) > 0);
+ erts_atomic_dec_nob(&esdp->ets_tables.count);
eaydp = ERTS_SCHED_AUX_YIELD_DATA(esdp, ets_all);
if (eaydp->ongoing) {
@@ -1776,9 +1776,11 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2)
ret = make_tid(BIF_P, tb);
save_sched_table(BIF_P, tb);
+ save_owned_table(BIF_P, tb);
if (is_named && !insert_named_tab(BIF_ARG_1, tb, 0)) {
tid_clear(BIF_P, tb);
+ delete_owned_table(BIF_P, tb);
db_lock(tb,LCK_WRITE);
free_heir_data(tb);
@@ -1789,7 +1791,6 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2)
}
BIF_P->flags |= F_USING_DB; /* So we can remove tb if p dies */
- save_owned_table(BIF_P, tb);
#ifdef HARDDEBUG
erts_fprintf(stderr,
@@ -2445,7 +2446,7 @@ ets_all_reply(ErtsSchedulerData *esdp, ErtsEtsAllReq **reqpp,
ASSERT(!*tablepp);
/* Max heap size needed... */
- sz = esdp->ets_tables.count;
+ sz = erts_atomic_read_nob(&esdp->ets_tables.count);
sz *= ERTS_MAGIC_REF_THING_SIZE + 2;
sz += 3 + ERTS_REF_THING_SIZE;
hfragp = new_message_buffer(sz);
@@ -2529,7 +2530,8 @@ erts_handle_yielded_ets_all_request(ErtsSchedulerData *esdp,
if (!eaydp->queue)
return 0; /* All work completed! */
- if (yc < ERTS_ETS_ALL_TB_YCNT_START && yc > esdp->ets_tables.count)
+ if (yc < ERTS_ETS_ALL_TB_YCNT_START &&
+ yc > erts_atomic_read_nob(&esdp->ets_tables.count))
return 1; /* Yield! */
eaydp->ongoing = ongoing = eaydp->queue;
@@ -2608,7 +2610,6 @@ BIF_RETTYPE ets_internal_request_all_0(BIF_ALIST_0)
BIF_RET(ref);
}
-
/*
** db_slot(Db, Slot) -> [Items].
*/
@@ -3490,7 +3491,11 @@ void init_db(ErtsDbSpinCount db_spin_count)
db_max_tabs, ((Uint)1)<<SMALL_BITS);
}
- meta_name_tab_mask = (((Uint) 1)<<(bits-1)) - 1; /* At least half the size of main tab */
+ /*
+ * We don't have ony hard limit for number of tables anymore, .
+ * but we use 'db_max_tabs' to determine size of name hash table.
+ */
+ meta_name_tab_mask = (((Uint) 1)<<bits) - 1;
size = sizeof(struct meta_name_tab_entry)*(meta_name_tab_mask+1);
meta_name_tab = erts_db_alloc_nt(ERTS_ALC_T_DB_TABLES, size);
ERTS_ETS_MISC_MEM_ADD(size);
@@ -3505,27 +3510,27 @@ void init_db(ErtsDbSpinCount db_spin_count)
/* Non visual BIF to trap to. */
erts_init_trap_export(&ets_select_delete_continue_exp,
- am_ets, am_atom_put("delete_trap",11), 1,
+ am_ets, ERTS_MAKE_AM("select_delete_trap"), 1,
&ets_select_delete_trap_1);
/* Non visual BIF to trap to. */
erts_init_trap_export(&ets_select_count_continue_exp,
- am_ets, am_atom_put("count_trap",11), 1,
+ am_ets, ERTS_MAKE_AM("count_trap"), 1,
&ets_select_count_1);
/* Non visual BIF to trap to. */
erts_init_trap_export(&ets_select_replace_continue_exp,
- am_ets, am_atom_put("replace_trap",11), 1,
+ am_ets, ERTS_MAKE_AM("replace_trap"), 1,
&ets_select_replace_1);
/* Non visual BIF to trap to. */
erts_init_trap_export(&ets_select_continue_exp,
- am_ets, am_atom_put("select_trap",11), 1,
+ am_ets, ERTS_MAKE_AM("select_trap"), 1,
&ets_select_trap_1);
/* Non visual BIF to trap to. */
erts_init_trap_export(&ets_delete_continue_exp,
- am_ets, am_atom_put("delete_trap",11), 1,
+ am_ets, ERTS_MAKE_AM("delete_trap"), 1,
&ets_delete_trap);
}
@@ -3538,7 +3543,7 @@ erts_ets_sched_spec_data_init(ErtsSchedulerData *esdp)
eaydp->tab = NULL;
eaydp->queue = NULL;
esdp->ets_tables.clist = NULL;
- esdp->ets_tables.count = 0;
+ erts_atomic_init_nob(&esdp->ets_tables.count, 0);
}
@@ -4154,7 +4159,7 @@ static Eterm table_info(Process* p, DbTable* tb, Eterm What)
else if (What == am_data) {
print_table(ERTS_PRINT_STDOUT, NULL, 1, tb);
ret = am_true;
- } else if (What == am_atom_put("fixed",5)) {
+ } else if (ERTS_IS_ATOM_STR("fixed",What)) {
if (IS_FIXED(tb))
ret = am_true;
else
@@ -4206,7 +4211,7 @@ static Eterm table_info(Process* p, DbTable* tb, Eterm What)
ret = am_false;
}
erts_mtx_unlock(&tb->common.fixlock);
- } else if (What == am_atom_put("stats",5)) {
+ } else if (ERTS_IS_ATOM_STR("stats",What)) {
if (IS_HASH_TABLE(tb->common.status)) {
FloatDef f;
DbHashStats stats;
@@ -4346,6 +4351,18 @@ erts_db_get_max_tabs()
return db_max_tabs;
}
+Uint erts_ets_table_count(void)
+{
+ Uint tb_count = 0;
+ Uint six;
+
+ for (six = 0; six < erts_no_schedulers; six++) {
+ ErtsSchedulerData *esdp = &erts_aligned_scheduler_data[six].esd;
+ tb_count += erts_atomic_read_nob(&esdp->ets_tables.count);
+ }
+ return tb_count;
+}
+
/*
* For testing of meta tables only.
*
diff --git a/erts/emulator/beam/erl_db.h b/erts/emulator/beam/erl_db.h
index db86c81914..db1dec015c 100644
--- a/erts/emulator/beam/erl_db.h
+++ b/erts/emulator/beam/erl_db.h
@@ -45,7 +45,7 @@ typedef struct {
} ErtsEtsAllYieldData;
typedef struct {
- Uint count;
+ erts_atomic_t count;
DbTable *clist;
} ErtsEtsTables;
@@ -69,6 +69,7 @@ typedef struct {
/*TT*/
Uint erts_get_ets_misc_mem_size(void);
+Uint erts_ets_table_count(void);
typedef struct {
DbTableCommon common;
@@ -93,7 +94,7 @@ union db_table {
/*TT*/
};
-#define DB_DEF_MAX_TABS 2053 /* Superseeded by environment variable
+#define DB_DEF_MAX_TABS 8192 /* Superseeded by environment variable
"ERL_MAX_ETS_TABLES" */
#define ERL_MAX_ETS_TABLES_ENV "ERL_MAX_ETS_TABLES"
diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c
index 37d261d0df..f1d47326b4 100644
--- a/erts/emulator/beam/erl_db_util.c
+++ b/erts/emulator/beam/erl_db_util.c
@@ -2770,9 +2770,7 @@ Eterm db_format_dmc_err_info(Process *p, DMCErrInfo *ei)
sys_strcpy(buff,tmp->error_string);
sl = sys_strlen(buff);
shp = HAlloc(p, sl * 2 + 5);
- sev = (tmp->severity == dmcWarning) ?
- am_atom_put("warning",7) :
- am_error;
+ sev = (tmp->severity == dmcWarning) ? am_warning : am_error;
tlist = buf_to_intlist(&shp, buff, sl, NIL);
tpl = TUPLE2(shp, sev, tlist);
shp += 3;
@@ -5180,7 +5178,7 @@ BIF_RETTYPE match_spec_test_3(BIF_ALIST_3)
{
Eterm res;
#ifdef DMC_DEBUG
- if (BIF_ARG_3 == am_atom_put("dis",3)) {
+ if (BIF_ARG_3 == ERTS_MAKE_AM("dis")) {
test_disassemble_next = 1;
BIF_RET(am_true);
} else
@@ -5291,7 +5289,7 @@ static Eterm match_spec_test(Process *p, Eterm against, Eterm spec, int trace)
erts_free(ERTS_ALC_T_DB_TMP, arr);
}
erts_bin_free(mps);
- ret = TUPLE4(hp, am_atom_put("ok",2), res, flg, lint_res);
+ ret = TUPLE4(hp, am_ok, res, flg, lint_res);
}
return ret;
}
diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c
index 1478b71195..8253ec4f40 100644
--- a/erts/emulator/beam/erl_process.c
+++ b/erts/emulator/beam/erl_process.c
@@ -9186,6 +9186,7 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
internal_sched_out_proc:
ERTS_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p);
+ ASSERT(p->scheduler_data || ERTS_SCHEDULER_IS_DIRTY(esdp));
ASSERT(actual_reds >= 0);
if (reds < ERTS_PROC_MIN_CONTEXT_SWITCH_REDS_COST)
@@ -9618,6 +9619,8 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
state = erts_atomic32_read_nob(&p->state);
if (is_normal_sched) {
+ ASSERT(!p->scheduler_data);
+ p->scheduler_data = esdp;
if ((!!(state & ERTS_PSFLGS_DIRTY_WORK))
& (!(state & ERTS_PSFLG_ACTIVE_SYS))) {
/* Migrate to dirty scheduler... */
@@ -9625,8 +9628,6 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
goto sched_out_proc;
}
- ASSERT(!p->scheduler_data);
- p->scheduler_data = esdp;
}
else {
if (!(state & ERTS_PSFLGS_DIRTY_WORK)) {
diff --git a/erts/emulator/beam/erl_unicode.c b/erts/emulator/beam/erl_unicode.c
index 8673e029e6..d225916ac5 100644
--- a/erts/emulator/beam/erl_unicode.c
+++ b/erts/emulator/beam/erl_unicode.c
@@ -79,23 +79,23 @@ void erts_init_unicode(void)
max_loop_limit = CONTEXT_REDS * LOOP_FACTOR;
/* Non visual BIFs to trap to. */
erts_init_trap_export(&characters_to_utf8_trap_exp,
- am_erlang, am_atom_put("characters_to_utf8_trap",23), 3,
+ am_erlang, ERTS_MAKE_AM("characters_to_utf8_trap"), 3,
&characters_to_utf8_trap);
erts_init_trap_export(&characters_to_list_trap_1_exp,
- am_erlang, am_atom_put("characters_to_list_trap_1",25), 3,
+ am_erlang, ERTS_MAKE_AM("characters_to_list_trap_1"), 3,
&characters_to_list_trap_1);
erts_init_trap_export(&characters_to_list_trap_2_exp,
- am_erlang, am_atom_put("characters_to_list_trap_2",25), 3,
+ am_erlang, ERTS_MAKE_AM("characters_to_list_trap_2"), 3,
&characters_to_list_trap_2);
erts_init_trap_export(&characters_to_list_trap_3_exp,
- am_erlang, am_atom_put("characters_to_list_trap_3",25), 3,
+ am_erlang, ERTS_MAKE_AM("characters_to_list_trap_3"), 3,
&characters_to_list_trap_3);
erts_init_trap_export(&characters_to_list_trap_4_exp,
- am_erlang, am_atom_put("characters_to_list_trap_4",25), 1,
+ am_erlang, ERTS_MAKE_AM("characters_to_list_trap_4"), 1,
&characters_to_list_trap_4);
c_to_b_int_trap_exportp = erts_export_put(am_unicode,am_characters_to_binary_int,2);
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index d74052d8b2..87709edc15 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -1972,8 +1972,7 @@ static void do_send_logger_message(Eterm gl, Eterm tag, Eterm format, Eterm args
break;
}
- md = MAP2(hp, am_emulator, am_true,
- am_atom_put("tag", 3), el_tag);
+ md = MAP2(hp, am_emulator, am_true, ERTS_MAKE_AM("tag"), el_tag);
hp += MAP2_SZ;
if (is_nil(gl) && is_non_value(pid)) {
@@ -1994,14 +1993,14 @@ static void do_send_logger_message(Eterm gl, Eterm tag, Eterm format, Eterm args
/* no gl */
md = MAP3(hp,
am_error_logger, md,
- am_atom_put("gl", 2), gl,
+ ERTS_MAKE_AM("gl"), gl,
am_time, time);
hp += MAP3_SZ;
pid = NIL;
} else {
md = MAP4(hp,
am_error_logger, md,
- am_atom_put("gl", 2), gl,
+ ERTS_MAKE_AM("gl"), gl,
am_pid, pid,
am_time, time);
hp += MAP4_SZ;
diff --git a/erts/emulator/sys/common/erl_mmap.c b/erts/emulator/sys/common/erl_mmap.c
index 5dadd8a5a6..145503bea7 100644
--- a/erts/emulator/sys/common/erl_mmap.c
+++ b/erts/emulator/sys/common/erl_mmap.c
@@ -2514,8 +2514,8 @@ Eterm erts_mmap_debug_info(Process* p)
sizeof(values)/sizeof(*values),
tags, values);
- sa_list = TUPLE2(hp, am_atom_put("sa_free_segs",12), sa_list); hp+=3;
- sua_list = TUPLE2(hp, am_atom_put("sua_free_segs",13), sua_list); hp+=3;
+ sa_list = TUPLE2(hp, ERTS_MAKE_AM("sa_free_segs"), sa_list); hp+=3;
+ sua_list = TUPLE2(hp, ERTS_MAKE_AM("sua_free_segs"), sua_list); hp+=3;
list = CONS(hp, sua_list, list); hp+=2;
list = CONS(hp, sa_list, list); hp+=2;
diff --git a/erts/emulator/test/system_info_SUITE.erl b/erts/emulator/test/system_info_SUITE.erl
index fdf4aab24d..7309908337 100644
--- a/erts/emulator/test/system_info_SUITE.erl
+++ b/erts/emulator/test/system_info_SUITE.erl
@@ -37,6 +37,7 @@
-export([process_count/1, system_version/1, misc_smoke_tests/1,
heap_size/1, wordsize/1, memory/1, ets_limit/1, atom_limit/1,
+ ets_count/1,
atom_count/1]).
suite() ->
@@ -45,6 +46,7 @@ suite() ->
all() ->
[process_count, system_version, misc_smoke_tests,
+ ets_count,
heap_size, wordsize, memory, ets_limit, atom_limit, atom_count].
%%%
@@ -478,6 +480,21 @@ get_node_name(Config) ->
++ "-"
++ integer_to_list(erlang:unique_integer([positive]))).
+ets_count(Config) when is_list(Config) ->
+ [ets_count_do([Type | Named])
+ || Type <- [set, bag, duplicate_bag, ordered_set],
+ Named <- [[named_table], []]
+ ],
+ ok.
+
+ets_count_do(Opts) ->
+ Before = erlang:system_info(ets_count),
+ T = ets:new(?MODULE, Opts),
+ After = erlang:system_info(ets_count),
+ After = Before + 1,
+ ets:delete(T),
+ Before = erlang:system_info(ets_count).
+
%% Verify system_info(ets_limit) reflects max ETS table settings.
ets_limit(Config0) when is_list(Config0) ->
diff --git a/erts/lib_src/Makefile.in b/erts/lib_src/Makefile.in
index 48660f7c71..dfaf664a18 100644
--- a/erts/lib_src/Makefile.in
+++ b/erts/lib_src/Makefile.in
@@ -334,7 +334,7 @@ ETHREAD_LIB=
endif
-ifneq ($(CREATE_DIRS),)
+ifneq ($(strip $(CREATE_DIRS)),)
_create_dirs := $(shell mkdir -p $(CREATE_DIRS))
endif
diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam
index 52f4c686a9..99f11495a5 100644
--- a/erts/preloaded/ebin/erlang.beam
+++ b/erts/preloaded/ebin/erlang.beam
Binary files differ
diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl
index 3a42e841e2..f4f31b1e4b 100644
--- a/erts/preloaded/src/erlang.erl
+++ b/erts/preloaded/src/erlang.erl
@@ -3778,8 +3778,8 @@ blocks_size([], Acc) ->
Acc.
get_fix_proc([{ProcType, A1, U1}| Rest], {A0, U0}) when ProcType == proc;
- ProcType == monitor_sh;
- ProcType == nlink_sh;
+ ProcType == monitor;
+ ProcType == link;
ProcType == msg_ref;
ProcType == ll_ptimer;
ProcType == hl_ptimer;
@@ -3903,7 +3903,6 @@ aa_mem_data(#memory{processes = Proc,
processes_used = ProcU,
system = Sys} = Mem,
[{ProcData, Sz} | Rest]) when ProcData == bif_timer;
- ProcData == link_lh;
ProcData == process_table ->
aa_mem_data(Mem#memory{processes = Proc+Sz,
processes_used = ProcU+Sz,
diff --git a/erts/preloaded/src/erts_code_purger.erl b/erts/preloaded/src/erts_code_purger.erl
index fd214228c7..c41532ed87 100644
--- a/erts/preloaded/src/erts_code_purger.erl
+++ b/erts/preloaded/src/erts_code_purger.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2016. All Rights Reserved.
+%% Copyright Ericsson AB 2016-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -25,7 +25,7 @@
-export([start/0, purge/1, soft_purge/1, pending_purge_lambda/3,
finish_after_on_load/2]).
--spec start() -> term().
+-spec start() -> no_return().
start() ->
register(erts_code_purger, self()),
process_flag(trap_exit, true),
diff --git a/erts/preloaded/src/zlib.erl b/erts/preloaded/src/zlib.erl
index a4ef42204d..6f53e67901 100644
--- a/erts/preloaded/src/zlib.erl
+++ b/erts/preloaded/src/zlib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2017. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -87,7 +87,7 @@
%%------------------------------------------------------------------------
%% Public data types.
--type zstream() :: term().
+-type zstream() :: reference().
-type zflush() :: 'none' | 'sync' | 'full' | 'finish'.
-type zlevel() ::
@@ -102,11 +102,11 @@
-type zmethod() :: 'deflated'.
-record(zlib_opts, {
- stream :: zstream(),
- method :: term(),
- input_chunk_size :: integer(),
- output_chunk_size :: integer(),
- flush :: integer()
+ stream :: zstream() | 'undefined',
+ method :: function(),
+ input_chunk_size :: pos_integer(),
+ output_chunk_size :: pos_integer(),
+ flush :: non_neg_integer()
}).
%%------------------------------------------------------------------------
@@ -168,7 +168,7 @@ deflateInit_nif(_Z, _Level, _Method, _WindowBits, _MemLevel, _Strategy) ->
-spec deflateSetDictionary(Z, Dictionary) -> Adler32 when
Z :: zstream(),
Dictionary :: iodata(),
- Adler32 :: integer().
+ Adler32 :: non_neg_integer().
deflateSetDictionary(Z, Dictionary) ->
deflateSetDictionary_nif(Z, Dictionary).
deflateSetDictionary_nif(_Z, _Dictionary) ->
@@ -295,7 +295,7 @@ inflate(Z, Data) ->
Options :: list({exception_on_need_dict, boolean()}),
Decompressed :: iolist() |
{need_dictionary,
- Adler32 :: integer(),
+ Adler32 :: non_neg_integer(),
Output :: iolist()}.
inflate(Z, Data, Options) ->
enqueue_input(Z, Data),
@@ -357,7 +357,7 @@ exception_on_need_dict(Z, Output) when is_list(Output); is_binary(Output) ->
Result :: {continue, Output :: iolist()} |
{finished, Output :: iolist()} |
{need_dictionary,
- Adler32 :: integer(),
+ Adler32 :: non_neg_integer(),
Output :: iolist()}.
safeInflate(Z, Data) ->
enqueue_input(Z, Data),
@@ -389,7 +389,7 @@ getBufSize_nif(_Z) ->
-spec crc32(Z) -> CRC when
Z :: zstream(),
- CRC :: integer().
+ CRC :: non_neg_integer().
crc32(Z) ->
crc32_nif(Z).
crc32_nif(_Z) ->
@@ -398,7 +398,7 @@ crc32_nif(_Z) ->
-spec crc32(Z, Data) -> CRC when
Z :: zstream(),
Data :: iodata(),
- CRC :: integer().
+ CRC :: non_neg_integer().
crc32(Z, Data) when is_reference(Z) ->
erlang:crc32(Data);
crc32(_Z, _Data) ->
@@ -406,9 +406,9 @@ crc32(_Z, _Data) ->
-spec crc32(Z, PrevCRC, Data) -> CRC when
Z :: zstream(),
- PrevCRC :: integer(),
+ PrevCRC :: non_neg_integer(),
Data :: iodata(),
- CRC :: integer().
+ CRC :: non_neg_integer().
crc32(Z, CRC, Data) when is_reference(Z) ->
erlang:crc32(CRC, Data);
crc32(_Z, _CRC, _Data) ->
@@ -416,10 +416,10 @@ crc32(_Z, _CRC, _Data) ->
-spec crc32_combine(Z, CRC1, CRC2, Size2) -> CRC when
Z :: zstream(),
- CRC :: integer(),
- CRC1 :: integer(),
- CRC2 :: integer(),
- Size2 :: integer().
+ CRC :: non_neg_integer(),
+ CRC1 :: non_neg_integer(),
+ CRC2 :: non_neg_integer(),
+ Size2 :: non_neg_integer().
crc32_combine(Z, CRC1, CRC2, Size2) when is_reference(Z) ->
erlang:crc32_combine(CRC1, CRC2, Size2);
crc32_combine(_Z, _CRC1, _CRC2, _Size2) ->
@@ -428,7 +428,7 @@ crc32_combine(_Z, _CRC1, _CRC2, _Size2) ->
-spec adler32(Z, Data) -> CheckSum when
Z :: zstream(),
Data :: iodata(),
- CheckSum :: integer().
+ CheckSum :: non_neg_integer().
adler32(Z, Data) when is_reference(Z) ->
erlang:adler32(Data);
adler32(_Z, _Data) ->
@@ -436,9 +436,9 @@ adler32(_Z, _Data) ->
-spec adler32(Z, PrevAdler, Data) -> CheckSum when
Z :: zstream(),
- PrevAdler :: integer(),
+ PrevAdler :: non_neg_integer(),
Data :: iodata(),
- CheckSum :: integer().
+ CheckSum :: non_neg_integer().
adler32(Z, Adler, Data) when is_reference(Z) ->
erlang:adler32(Adler, Data);
adler32(_Z, _Adler, _Data) ->
@@ -446,10 +446,10 @@ adler32(_Z, _Adler, _Data) ->
-spec adler32_combine(Z, Adler1, Adler2, Size2) -> Adler when
Z :: zstream(),
- Adler :: integer(),
- Adler1 :: integer(),
- Adler2 :: integer(),
- Size2 :: integer().
+ Adler :: non_neg_integer(),
+ Adler1 :: non_neg_integer(),
+ Adler2 :: non_neg_integer(),
+ Size2 :: non_neg_integer().
adler32_combine(Z, Adler1, Adler2, Size2) when is_reference(Z) ->
erlang:adler32_combine(Adler1, Adler2, Size2);
adler32_combine(_Z, _Adler1, _Adler2, _Size2) ->
diff --git a/lib/compiler/src/beam_asm.erl b/lib/compiler/src/beam_asm.erl
index 5ef340c831..df0321e85a 100644
--- a/lib/compiler/src/beam_asm.erl
+++ b/lib/compiler/src/beam_asm.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -475,7 +475,7 @@ encode_alloc_list_1([{floats,Floats}|T], Dict, Acc0) ->
encode_alloc_list_1([], Dict, Acc) ->
{iolist_to_binary(Acc),Dict}.
--spec encode(non_neg_integer(), integer()) -> iodata().
+-spec encode(non_neg_integer(), integer()) -> iolist() | integer().
encode(Tag, N) when N < 0 ->
encode1(Tag, negative_to_bytes(N));
diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c
index 6e113ef39e..2c69dbb5ff 100644
--- a/lib/crypto/c_src/crypto.c
+++ b/lib/crypto/c_src/crypto.c
@@ -3071,10 +3071,11 @@ static ERL_NIF_TERM dh_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_
if ((dhkey = EVP_PKEY_new())
&& (params = EVP_PKEY_new())
&& EVP_PKEY_set1_DH(params, dh_params) /* set the key referenced by params to dh_params.
- dh_params (and params) must be freed by us*/
+ dh_params (and params) must be freed */
&& (ctx = EVP_PKEY_CTX_new(params, NULL))
&& EVP_PKEY_keygen_init(ctx)
- && EVP_PKEY_keygen(ctx, &dhkey)
+ && EVP_PKEY_keygen(ctx, &dhkey) /* "performs a key generation operation, the
+ generated key is written to ppkey." (=last arg) */
&& (dh_params = EVP_PKEY_get1_DH(dhkey)) /* return the referenced key. dh_params and dhkey must be freed */
) {
#else
@@ -3108,8 +3109,8 @@ static ERL_NIF_TERM dh_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_
DH_free(dh_params);
#ifdef HAS_EVP_PKEY_CTX
if (ctx) EVP_PKEY_CTX_free(ctx);
- /* if (dhkey) EVP_PKEY_free(dhkey); */
- /* if (params) EVP_PKEY_free(params); */
+ if (dhkey) EVP_PKEY_free(dhkey);
+ if (params) EVP_PKEY_free(params);
#endif
return ret;
}
@@ -3207,7 +3208,7 @@ static ERL_NIF_TERM dh_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_T
if (dh_pub) DH_free(dh_pub);
#ifdef HAS_EVP_PKEY_CTX
if (ctx) EVP_PKEY_CTX_free(ctx);
- /* if (my_priv_key) EVP_PKEY_free(my_priv_key); */
+ if (my_priv_key) EVP_PKEY_free(my_priv_key);
/* if (peer_pub_key) EVP_PKEY_free(peer_pub_key); */
#endif
return ret;
diff --git a/lib/dialyzer/src/dialyzer.erl b/lib/dialyzer/src/dialyzer.erl
index 1538174d4a..185c8c9ae6 100644
--- a/lib/dialyzer/src/dialyzer.erl
+++ b/lib/dialyzer/src/dialyzer.erl
@@ -409,6 +409,10 @@ message_to_string({extra_range, [M, F, A, ExtraRanges, SigRange]}) ->
io_lib:format("The specification for ~w:~tw/~w states that the function"
" might also return ~ts but the inferred return is ~ts\n",
[M, F, A, ExtraRanges, SigRange]);
+message_to_string({missing_range, [M, F, A, ExtraRanges, ContrRange]}) ->
+ io_lib:format("The success typing for ~w:~tw/~w implies that the function"
+ " might also return ~ts but the specification return is ~ts\n",
+ [M, F, A, ExtraRanges, ContrRange]);
message_to_string({overlapping_contract, [M, F, A]}) ->
io_lib:format("Overloaded contract for ~w:~tw/~w has overlapping domains;"
" such contracts are currently unsupported and are simply ignored\n",
diff --git a/lib/dialyzer/src/dialyzer_contracts.erl b/lib/dialyzer/src/dialyzer_contracts.erl
index 0df15e55f9..af7f4385ad 100644
--- a/lib/dialyzer/src/dialyzer_contracts.erl
+++ b/lib/dialyzer/src/dialyzer_contracts.erl
@@ -197,9 +197,11 @@ check_contracts(Contracts, Callgraph, FunTypes, ModOpaques) ->
false ->
[{MFA, Contract}|NewContracts]
end;
- {error, {extra_range, _, _}} ->
- %% do not treat extra range as an error in this check
- %% since that prevents discovering other actual errors
+ {range_warnings, _} ->
+ %% do not treat extra range, either in contract or
+ %% in success typing, as an error in this check
+ %% since that prevents discovering other actual
+ %% errors
[{MFA, Contract}|NewContracts];
{error, _Error} -> NewContracts
end;
@@ -210,14 +212,26 @@ check_contracts(Contracts, Callgraph, FunTypes, ModOpaques) ->
end,
orddict:from_list(lists:foldl(FoldFun, [], orddict:to_list(FunTypes))).
+-type check_contract_return() ::
+ 'ok'
+ | {'error',
+ 'invalid_contract'
+ | {'opaque_mismatch', erl_types:erl_type()}
+ | {'overlapping_contract', [module() | atom() | byte()]}
+ | string()}
+ | {'range_warnings',
+ [{'error', {'extra_range' | 'missing_range',
+ erl_types:erl_type(),
+ erl_types:erl_type()}}]}.
+
%% Checks all components of a contract
--spec check_contract(#contract{}, erl_types:erl_type()) -> 'ok' | {'error', term()}.
+-spec check_contract(#contract{}, erl_types:erl_type()) -> check_contract_return().
check_contract(Contract, SuccType) ->
check_contract(Contract, SuccType, 'universe').
-spec check_contract(#contract{}, erl_types:erl_type(), erl_types:opaques()) ->
- 'ok' | {'error', term()}.
+ check_contract_return().
check_contract(#contract{contracts = Contracts}, SuccType, Opaques) ->
try
@@ -290,15 +304,23 @@ check_contract_inf_list([], _SuccType, _Opaques, OM) ->
check_extraneous([], _SuccType) -> ok;
check_extraneous([C|Cs], SuccType) ->
case check_extraneous_1(C, SuccType) of
- ok -> check_extraneous(Cs, SuccType);
- Error -> Error
+ {error, invalid_contract} = Error ->
+ Error;
+ {error, {extra_range, _, _}} = Error ->
+ {range_warnings, [Error | check_missing(C, SuccType)]};
+ ok ->
+ case check_missing(C, SuccType) of
+ [] -> check_extraneous(Cs, SuccType);
+ ErrorL -> {range_warnings, ErrorL}
+ end
end.
check_extraneous_1(Contract, SuccType) ->
CRng = erl_types:t_fun_range(Contract),
CRngs = erl_types:t_elements(CRng),
STRng = erl_types:t_fun_range(SuccType),
- ?debug("CR = ~tp\nSR = ~tp\n", [CRngs, STRng]),
+ ?debug("\nCR = ~ts\nSR = ~ts\n", [erl_types:t_to_string(CRng),
+ erl_types:t_to_string(STRng)]),
case [CR || CR <- CRngs,
erl_types:t_is_none(erl_types:t_inf(CR, STRng))] of
[] ->
@@ -341,6 +363,18 @@ map_part(Type) ->
is_empty_map(Type) ->
erl_types:t_is_equal(Type, erl_types:t_from_term(#{})).
+check_missing(Contract, SuccType) ->
+ CRng = erl_types:t_fun_range(Contract),
+ STRng = erl_types:t_fun_range(SuccType),
+ STRngs = erl_types:t_elements(STRng),
+ ?debug("\nCR = ~ts\nSR = ~ts\n", [erl_types:t_to_string(CRng),
+ erl_types:t_to_string(STRng)]),
+ case [STR || STR <- STRngs,
+ erl_types:t_is_none(erl_types:t_inf(STR, CRng))] of
+ [] -> [];
+ STRs -> [{error, {missing_range, erl_types:t_sup(STRs), CRng}}]
+ end.
+
%% This is the heart of the "range function"
-spec process_contracts([contract_pair()], [erl_types:erl_type()]) ->
erl_types:erl_type().
@@ -712,22 +746,30 @@ get_invalid_contract_warnings_funs([{MFA, {FileLine, Contract, _Xtra}}|Left],
[W|Acc];
{error, {overlapping_contract, []}} ->
[overlapping_contract_warning(MFA, WarningInfo)|Acc];
- {error, {extra_range, ExtraRanges, STRange}} ->
- Warn =
- case t_from_forms_without_remote(Contract#contract.forms,
- MFA, RecDict) of
- {ok, NoRemoteType} ->
- CRet = erl_types:t_fun_range(NoRemoteType),
- erl_types:t_is_subtype(ExtraRanges, CRet);
- unsupported ->
- true
- end,
- case Warn of
- true ->
- [extra_range_warning(MFA, WarningInfo, ExtraRanges, STRange)|Acc];
- false ->
- Acc
- end;
+ {range_warnings, Errors} ->
+ Fun =
+ fun({error, {extra_range, ExtraRanges, STRange}}, Acc0) ->
+ Warn =
+ case t_from_forms_without_remote(Contract#contract.forms,
+ MFA, RecDict) of
+ {ok, NoRemoteType} ->
+ CRet = erl_types:t_fun_range(NoRemoteType),
+ erl_types:t_is_subtype(ExtraRanges, CRet);
+ unsupported ->
+ true
+ end,
+ case Warn of
+ true ->
+ [extra_range_warning(MFA, WarningInfo,
+ ExtraRanges, STRange)|Acc0];
+ false ->
+ Acc0
+ end;
+ ({error, {missing_range, ExtraRanges, CRange}}, Acc0) ->
+ [missing_range_warning(MFA, WarningInfo,
+ ExtraRanges, CRange)|Acc0]
+ end,
+ lists:foldl(Fun, Acc, Errors);
{error, Msg} ->
[{?WARN_CONTRACT_SYNTAX, WarningInfo, Msg}|Acc];
ok ->
@@ -745,6 +787,9 @@ get_invalid_contract_warnings_funs([{MFA, {FileLine, Contract, _Xtra}}|Left],
{error, _} ->
[invalid_contract_warning(MFA, WarningInfo, BifSig, RecDict)
|Acc];
+ {range_warnings, _} ->
+ picky_contract_check(CSig, BifSig, MFA, WarningInfo,
+ Contract, RecDict, Acc);
ok ->
picky_contract_check(CSig, BifSig, MFA, WarningInfo,
Contract, RecDict, Acc)
@@ -778,6 +823,12 @@ extra_range_warning({M, F, A}, WarningInfo, ExtraRanges, STRange) ->
{?WARN_CONTRACT_SUPERTYPE, WarningInfo,
{extra_range, [M, F, A, ERangesStr, STRangeStr]}}.
+missing_range_warning({M, F, A}, WarningInfo, ExtraRanges, CRange) ->
+ ERangesStr = erl_types:t_to_string(ExtraRanges),
+ CRangeStr = erl_types:t_to_string(CRange),
+ {?WARN_CONTRACT_SUBTYPE, WarningInfo,
+ {missing_range, [M, F, A, ERangesStr, CRangeStr]}}.
+
picky_contract_check(CSig0, Sig0, MFA, WarningInfo, Contract, RecDict, Acc) ->
CSig = erl_types:t_abstract_records(CSig0, RecDict),
Sig = erl_types:t_abstract_records(Sig0, RecDict),
diff --git a/lib/dialyzer/src/typer.erl b/lib/dialyzer/src/typer.erl
index 9d3d9ce438..4b99f5f72e 100644
--- a/lib/dialyzer/src/typer.erl
+++ b/lib/dialyzer/src/typer.erl
@@ -401,7 +401,7 @@ get_type({{M, F, A} = MFA, Range, Arg}, CodeServer, Records) ->
Sig = erl_types:t_fun(Arg, Range),
case dialyzer_contracts:check_contract(Contract, Sig) of
ok -> {{F, A}, {contract, Contract}};
- {error, {extra_range, _, _}} ->
+ {range_warnings, _} ->
{{F, A}, {contract, Contract}};
{error, {overlapping_contract, []}} ->
{{F, A}, {contract, Contract}};
diff --git a/lib/dialyzer/test/overspecs_SUITE_data/dialyzer_options b/lib/dialyzer/test/overspecs_SUITE_data/dialyzer_options
new file mode 100644
index 0000000000..ff4517e59d
--- /dev/null
+++ b/lib/dialyzer/test/overspecs_SUITE_data/dialyzer_options
@@ -0,0 +1 @@
+{dialyzer_options, [{warnings, [overspecs]}]}.
diff --git a/lib/dialyzer/test/overspecs_SUITE_data/results/iodata b/lib/dialyzer/test/overspecs_SUITE_data/results/iodata
new file mode 100644
index 0000000000..d9c70330ec
--- /dev/null
+++ b/lib/dialyzer/test/overspecs_SUITE_data/results/iodata
@@ -0,0 +1,2 @@
+
+iodata.erl:7: The success typing for iodata:encode/2 implies that the function might also return integer() but the specification return is binary() | maybe_improper_list(binary() | maybe_improper_list(any(),binary() | []) | byte(),binary() | [])
diff --git a/lib/dialyzer/test/overspecs_SUITE_data/results/iolist b/lib/dialyzer/test/overspecs_SUITE_data/results/iolist
new file mode 100644
index 0000000000..ca556f017c
--- /dev/null
+++ b/lib/dialyzer/test/overspecs_SUITE_data/results/iolist
@@ -0,0 +1,2 @@
+
+iolist.erl:7: The success typing for iolist:encode/2 implies that the function might also return integer() but the specification return is maybe_improper_list(binary() | maybe_improper_list(any(),binary() | []) | byte(),binary() | [])
diff --git a/lib/dialyzer/test/overspecs_SUITE_data/src/iodata.erl b/lib/dialyzer/test/overspecs_SUITE_data/src/iodata.erl
new file mode 100644
index 0000000000..caa44f6c91
--- /dev/null
+++ b/lib/dialyzer/test/overspecs_SUITE_data/src/iodata.erl
@@ -0,0 +1,41 @@
+-module(iodata).
+
+%% A small part of beam_asm.
+
+-export([encode/2]).
+
+-spec encode(non_neg_integer(), integer()) -> iodata(). % extra range binary()
+
+encode(Tag, N) when Tag >= 0, N < 0 ->
+ encode1(Tag, negative_to_bytes(N));
+encode(Tag, N) when Tag >= 0, N < 16 ->
+ (N bsl 4) bor Tag; % not in the specification
+encode(Tag, N) when Tag >= 0, N < 16#800 ->
+ [((N bsr 3) band 2#11100000) bor Tag bor 2#00001000, N band 16#ff];
+encode(Tag, N) when Tag >= 0 ->
+ encode1(Tag, to_bytes(N)).
+
+encode1(Tag, Bytes) ->
+ case iolist_size(Bytes) of
+ Num when 2 =< Num, Num =< 8 ->
+ [((Num-2) bsl 5) bor 2#00011000 bor Tag| Bytes];
+ Num when 8 < Num ->
+ [2#11111000 bor Tag, encode(0, Num-9)| Bytes]
+ end.
+
+to_bytes(N) ->
+ Bin = binary:encode_unsigned(N),
+ case Bin of
+ <<0:1,_/bits>> -> Bin;
+ <<1:1,_/bits>> -> [0,Bin]
+ end.
+
+negative_to_bytes(N) when N >= -16#8000 ->
+ <<N:16>>;
+negative_to_bytes(N) ->
+ Bytes = byte_size(binary:encode_unsigned(-N)),
+ Bin = <<N:Bytes/unit:8>>,
+ case Bin of
+ <<0:1,_/bits>> -> [16#ff,Bin];
+ <<1:1,_/bits>> -> Bin
+ end.
diff --git a/lib/dialyzer/test/overspecs_SUITE_data/src/iolist.erl b/lib/dialyzer/test/overspecs_SUITE_data/src/iolist.erl
new file mode 100644
index 0000000000..7cceeda24e
--- /dev/null
+++ b/lib/dialyzer/test/overspecs_SUITE_data/src/iolist.erl
@@ -0,0 +1,41 @@
+-module(iolist).
+
+%% A small part of beam_asm.
+
+-export([encode/2]).
+
+-spec encode(non_neg_integer(), integer()) -> iolist().
+
+encode(Tag, N) when Tag >= 0, N < 0 ->
+ encode1(Tag, negative_to_bytes(N));
+encode(Tag, N) when Tag >= 0, N < 16 ->
+ (N bsl 4) bor Tag; % not in the specification
+encode(Tag, N) when Tag >= 0, N < 16#800 ->
+ [((N bsr 3) band 2#11100000) bor Tag bor 2#00001000, N band 16#ff];
+encode(Tag, N) when Tag >= 0 ->
+ encode1(Tag, to_bytes(N)).
+
+encode1(Tag, Bytes) ->
+ case iolist_size(Bytes) of
+ Num when 2 =< Num, Num =< 8 ->
+ [((Num-2) bsl 5) bor 2#00011000 bor Tag| Bytes];
+ Num when 8 < Num ->
+ [2#11111000 bor Tag, encode(0, Num-9)| Bytes]
+ end.
+
+to_bytes(N) ->
+ Bin = binary:encode_unsigned(N),
+ case Bin of
+ <<0:1,_/bits>> -> Bin;
+ <<1:1,_/bits>> -> [0,Bin]
+ end.
+
+negative_to_bytes(N) when N >= -16#8000 ->
+ <<N:16>>;
+negative_to_bytes(N) ->
+ Bytes = byte_size(binary:encode_unsigned(-N)),
+ Bin = <<N:Bytes/unit:8>>,
+ case Bin of
+ <<0:1,_/bits>> -> [16#ff,Bin];
+ <<1:1,_/bits>> -> Bin
+ end.
diff --git a/lib/dialyzer/test/specdiffs_SUITE_data/dialyzer_options b/lib/dialyzer/test/specdiffs_SUITE_data/dialyzer_options
new file mode 100644
index 0000000000..56b36f2ed4
--- /dev/null
+++ b/lib/dialyzer/test/specdiffs_SUITE_data/dialyzer_options
@@ -0,0 +1 @@
+{dialyzer_options, [{warnings, [specdiffs]}]}.
diff --git a/lib/dialyzer/test/specdiffs_SUITE_data/results/iodata b/lib/dialyzer/test/specdiffs_SUITE_data/results/iodata
new file mode 100644
index 0000000000..3fb12fe000
--- /dev/null
+++ b/lib/dialyzer/test/specdiffs_SUITE_data/results/iodata
@@ -0,0 +1,3 @@
+
+iodata.erl:7: The specification for iodata:encode/2 states that the function might also return binary() but the inferred return is nonempty_maybe_improper_list(<<_:8,_:_*8>> | nonempty_maybe_improper_list(<<_:8,_:_*8>> | nonempty_maybe_improper_list(any(),<<_:8,_:_*8>> | []) | byte(),<<_:8,_:_*8>> | []) | integer(),<<_:8,_:_*8>> | []) | integer()
+iodata.erl:7: The success typing for iodata:encode/2 implies that the function might also return integer() but the specification return is binary() | maybe_improper_list(binary() | maybe_improper_list(any(),binary() | []) | byte(),binary() | [])
diff --git a/lib/dialyzer/test/specdiffs_SUITE_data/results/iolist b/lib/dialyzer/test/specdiffs_SUITE_data/results/iolist
new file mode 100644
index 0000000000..ca556f017c
--- /dev/null
+++ b/lib/dialyzer/test/specdiffs_SUITE_data/results/iolist
@@ -0,0 +1,2 @@
+
+iolist.erl:7: The success typing for iolist:encode/2 implies that the function might also return integer() but the specification return is maybe_improper_list(binary() | maybe_improper_list(any(),binary() | []) | byte(),binary() | [])
diff --git a/lib/dialyzer/test/specdiffs_SUITE_data/src/iodata.erl b/lib/dialyzer/test/specdiffs_SUITE_data/src/iodata.erl
new file mode 100644
index 0000000000..caa44f6c91
--- /dev/null
+++ b/lib/dialyzer/test/specdiffs_SUITE_data/src/iodata.erl
@@ -0,0 +1,41 @@
+-module(iodata).
+
+%% A small part of beam_asm.
+
+-export([encode/2]).
+
+-spec encode(non_neg_integer(), integer()) -> iodata(). % extra range binary()
+
+encode(Tag, N) when Tag >= 0, N < 0 ->
+ encode1(Tag, negative_to_bytes(N));
+encode(Tag, N) when Tag >= 0, N < 16 ->
+ (N bsl 4) bor Tag; % not in the specification
+encode(Tag, N) when Tag >= 0, N < 16#800 ->
+ [((N bsr 3) band 2#11100000) bor Tag bor 2#00001000, N band 16#ff];
+encode(Tag, N) when Tag >= 0 ->
+ encode1(Tag, to_bytes(N)).
+
+encode1(Tag, Bytes) ->
+ case iolist_size(Bytes) of
+ Num when 2 =< Num, Num =< 8 ->
+ [((Num-2) bsl 5) bor 2#00011000 bor Tag| Bytes];
+ Num when 8 < Num ->
+ [2#11111000 bor Tag, encode(0, Num-9)| Bytes]
+ end.
+
+to_bytes(N) ->
+ Bin = binary:encode_unsigned(N),
+ case Bin of
+ <<0:1,_/bits>> -> Bin;
+ <<1:1,_/bits>> -> [0,Bin]
+ end.
+
+negative_to_bytes(N) when N >= -16#8000 ->
+ <<N:16>>;
+negative_to_bytes(N) ->
+ Bytes = byte_size(binary:encode_unsigned(-N)),
+ Bin = <<N:Bytes/unit:8>>,
+ case Bin of
+ <<0:1,_/bits>> -> [16#ff,Bin];
+ <<1:1,_/bits>> -> Bin
+ end.
diff --git a/lib/dialyzer/test/specdiffs_SUITE_data/src/iolist.erl b/lib/dialyzer/test/specdiffs_SUITE_data/src/iolist.erl
new file mode 100644
index 0000000000..7cceeda24e
--- /dev/null
+++ b/lib/dialyzer/test/specdiffs_SUITE_data/src/iolist.erl
@@ -0,0 +1,41 @@
+-module(iolist).
+
+%% A small part of beam_asm.
+
+-export([encode/2]).
+
+-spec encode(non_neg_integer(), integer()) -> iolist().
+
+encode(Tag, N) when Tag >= 0, N < 0 ->
+ encode1(Tag, negative_to_bytes(N));
+encode(Tag, N) when Tag >= 0, N < 16 ->
+ (N bsl 4) bor Tag; % not in the specification
+encode(Tag, N) when Tag >= 0, N < 16#800 ->
+ [((N bsr 3) band 2#11100000) bor Tag bor 2#00001000, N band 16#ff];
+encode(Tag, N) when Tag >= 0 ->
+ encode1(Tag, to_bytes(N)).
+
+encode1(Tag, Bytes) ->
+ case iolist_size(Bytes) of
+ Num when 2 =< Num, Num =< 8 ->
+ [((Num-2) bsl 5) bor 2#00011000 bor Tag| Bytes];
+ Num when 8 < Num ->
+ [2#11111000 bor Tag, encode(0, Num-9)| Bytes]
+ end.
+
+to_bytes(N) ->
+ Bin = binary:encode_unsigned(N),
+ case Bin of
+ <<0:1,_/bits>> -> Bin;
+ <<1:1,_/bits>> -> [0,Bin]
+ end.
+
+negative_to_bytes(N) when N >= -16#8000 ->
+ <<N:16>>;
+negative_to_bytes(N) ->
+ Bytes = byte_size(binary:encode_unsigned(-N)),
+ Bin = <<N:Bytes/unit:8>>,
+ case Bin of
+ <<0:1,_/bits>> -> [16#ff,Bin];
+ <<1:1,_/bits>> -> Bin
+ end.
diff --git a/lib/erl_interface/src/prog/erl_call.c b/lib/erl_interface/src/prog/erl_call.c
index 7577a07a3e..66265b3e6a 100644
--- a/lib/erl_interface/src/prog/erl_call.c
+++ b/lib/erl_interface/src/prog/erl_call.c
@@ -517,6 +517,15 @@ int erl_call(int argc, char **argv)
}
}
+
+ /*
+ * If we loaded any module source code, we can free the buffer
+ * now. This buffer was allocated in read_stdin().
+ */
+ if (module != NULL) {
+ free(module);
+ }
+
/*
* Eval the Erlang functions read from stdin/
*/
@@ -795,8 +804,6 @@ static int get_module(char **mbuf, char **mname)
*mname = (char *) ei_chk_calloc(i+1, sizeof(char));
memcpy(*mname, start, i);
}
- if (*mbuf)
- free(*mbuf); /* Allocated in read_stdin() */
return len;
diff --git a/lib/kernel/src/code.erl b/lib/kernel/src/code.erl
index f143a49d2f..7faef93609 100644
--- a/lib/kernel/src/code.erl
+++ b/lib/kernel/src/code.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -630,7 +630,7 @@ do_par_recv(N, Good, Bad) ->
call(Req) ->
code_server:call(Req).
--spec start_link() -> {'ok', pid()} | {'error', 'crash'}.
+-spec start_link() -> {'ok', pid()}.
start_link() ->
do_start().
diff --git a/lib/kernel/src/file_server.erl b/lib/kernel/src/file_server.erl
index ecc1ffbdd6..29eaa23375 100644
--- a/lib/kernel/src/file_server.erl
+++ b/lib/kernel/src/file_server.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2017. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -73,7 +73,7 @@ stop() ->
%% {stop, Reason}
%%----------------------------------------------------------------------
--spec init([]) -> {'ok', state()} | {'stop', term()}.
+-spec init([]) -> {'ok', state()}.
init([]) ->
process_flag(trap_exit, true),
@@ -225,7 +225,7 @@ handle_cast(Msg, State) ->
%%----------------------------------------------------------------------
-spec handle_info(term(), state()) ->
- {'noreply', state()} | {'stop', 'normal', state()}.
+ {'noreply', state()}.
handle_info({'EXIT', Pid, _Reason}, State) when is_pid(Pid) ->
ets:delete(?FILE_IO_SERVER_TABLE, Pid),
diff --git a/lib/kernel/src/kernel_refc.erl b/lib/kernel/src/kernel_refc.erl
index 05076dc885..8e04ff99d8 100644
--- a/lib/kernel/src/kernel_refc.erl
+++ b/lib/kernel/src/kernel_refc.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2017. All Rights Reserved.
+%% Copyright Ericsson AB 2017-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -44,7 +44,7 @@ scheduler_wall_time(Bool) ->
%% Callback functions from gen_server
%%-----------------------------------------------------------------
--spec init([]) -> {'ok', map()} | {'stop', term()}.
+-spec init([]) -> {'ok', map()}.
init([]) ->
resource(scheduler_wall_time, false),
diff --git a/lib/public_key/doc/src/public_key.xml b/lib/public_key/doc/src/public_key.xml
index 7284da0499..5d57109140 100644
--- a/lib/public_key/doc/src/public_key.xml
+++ b/lib/public_key/doc/src/public_key.xml
@@ -986,10 +986,38 @@ fun(#'DistributionPoint'{}, #'CertificateList'{},
<p>The <c>ip</c> Reference ID takes an <seealso marker="inet:inet#type-ip_address">inet:ip_address()</seealso>
or an ip address in string format (E.g "10.0.1.1" or "1234::5678:9012") as second element.
</p>
+ <p>See <seealso marker="#pkix_verify_hostname_match_fun-1">pkix_verify_hostname_match_fun/1</seealso> for a
+ function that return a fun suitable for this option.
+ </p>
</desc>
</func>
<func>
+ <name>pkix_verify_hostname_match_fun(Alg) -> fun(RefId | FQDN::string(), PresentedID) -> boolean() | default</name>
+ <fsummary>Returns a fun that is intendended as argument to the match_fun option in pkix_verify_hostname/3.
+ </fsummary>
+ <type>
+ <v>Alg = https</v>
+ <d>The algorithm for wich the fun should implement the special matching rules</d>
+ <v>RefId</v>
+ <d>See <seealso marker="#pkix_verify_hostname-3">pkix_verify_hostname/3</seealso>.</d>
+ <v>FQDN</v>
+ <d>See <seealso marker="#pkix_verify_hostname-3">pkix_verify_hostname/3</seealso>.</d>
+ <v>PresentedID</v>
+ <d>See <seealso marker="#pkix_verify_hostname-3">pkix_verify_hostname/3</seealso>.</d>
+ </type>
+ <desc>
+ <p>The return value of calling this function is intended to be used in the <c>match_fun</c> option in
+ <seealso marker="#pkix_verify_hostname-3">pkix_verify_hostname/3</seealso>.
+ </p>
+ <p>The returned fun augments the verify hostname matching according to the specific rules for
+ the protocol in the argument.
+ </p>
+ </desc>
+ </func>
+
+
+ <func>
<name>sign(Msg, DigestType, Key) -> binary()</name>
<name>sign(Msg, DigestType, Key, Options) -> binary()</name>
<fsummary>Creates a digital signature.</fsummary>
diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl
index 1c4acc9e1a..f2b57fd330 100644
--- a/lib/public_key/src/public_key.erl
+++ b/lib/public_key/src/public_key.erl
@@ -49,6 +49,7 @@
pkix_normalize_name/1,
pkix_path_validation/3,
pkix_verify_hostname/2, pkix_verify_hostname/3,
+ pkix_verify_hostname_match_fun/1,
ssh_decode/2, ssh_encode/2,
ssh_hostkey_fingerprint/1, ssh_hostkey_fingerprint/2,
ssh_curvename2oid/1, oid2ssh_curvename/1,
@@ -883,12 +884,23 @@ pkix_crls_validate(OtpCert, DPAndCRLs0, Options) ->
Options, pubkey_crl:init_revokation_state()).
%--------------------------------------------------------------------
--spec pkix_verify_hostname(Cert :: #'OTPCertificate'{} | binary(),
- ReferenceIDs :: [{uri_id | dns_id | ip | srv_id | oid(), string()}]) -> boolean().
+-spec pkix_verify_hostname(#'OTPCertificate'{} | binary(),
+ referenceIDs()
+ ) -> boolean().
--spec pkix_verify_hostname(Cert :: #'OTPCertificate'{} | binary(),
- ReferenceIDs :: [{uri_id | dns_id | ip | srv_id | oid(), string()}],
- Options :: proplists:proplist()) -> boolean().
+-spec pkix_verify_hostname(#'OTPCertificate'{} | binary(),
+ referenceIDs(),
+ proplists:proplist()) -> boolean().
+
+-type referenceIDs() :: [referenceID()] .
+-type referenceID() :: {uri_id | dns_id | ip | srv_id | oid(), string()} .
+
+-spec pkix_verify_hostname_match_fun(high_level_alg()) -> match_fun() .
+
+-type high_level_alg() :: https .
+-type match_fun() :: fun((ReferenceID::referenceID() | string(),
+ PresentedID::{atom()|oid(),string()}) -> match_fun_result() ) .
+-type match_fun_result() :: boolean() | default .
%% Description: Validates a hostname to RFC 6125
%%--------------------------------------------------------------------
@@ -953,6 +965,11 @@ pkix_verify_hostname(Cert = #'OTPCertificate'{tbsCertificate = TbsCert}, Referen
end
end.
+pkix_verify_hostname_match_fun(https) ->
+ fun({dns_id,FQDN=[_|_]}, {dNSName,Name=[_|_]}) -> verify_hostname_match_wildcard(FQDN, Name);
+ (_, _) -> default
+ end.
+
%%--------------------------------------------------------------------
-spec ssh_decode(binary(), public_key | ssh_file()) -> [{public_key(), Attributes::list()}]
; (binary(), ssh2_pubkey) -> public_key()
@@ -1516,9 +1533,7 @@ verify_hostname_match_default(Ref, Pres) ->
verify_hostname_match_default0(FQDN=[_|_], {cn,FQDN}) ->
not lists:member($*, FQDN);
verify_hostname_match_default0(FQDN=[_|_], {cn,Name=[_|_]}) ->
- [F1|Fs] = string:tokens(FQDN, "."),
- [N1|Ns] = string:tokens(Name, "."),
- match_wild(F1,N1) andalso Fs==Ns;
+ verify_hostname_match_wildcard(FQDN, Name);
verify_hostname_match_default0({dns_id,R}, {dNSName,P}) ->
R==P;
verify_hostname_match_default0({uri_id,R}, {uniformResourceIdentifier,P}) ->
@@ -1553,6 +1568,13 @@ verify_hostname_match_default0({srv_id,R}, {?srvName_OID,P}) ->
verify_hostname_match_default0(_, _) ->
false.
+
+verify_hostname_match_wildcard(FQDN, Name) ->
+ [F1|Fs] = string:tokens(FQDN, "."),
+ [N1|Ns] = string:tokens(Name, "."),
+ match_wild(F1,N1) andalso Fs==Ns.
+
+
ok({ok,X}) -> X.
l16_to_tup(L) -> list_to_tuple(l16_to_tup(L, [])).
diff --git a/lib/public_key/test/public_key_SUITE.erl b/lib/public_key/test/public_key_SUITE.erl
index 572748edc9..fcc9bdc080 100644
--- a/lib/public_key/test/public_key_SUITE.erl
+++ b/lib/public_key/test/public_key_SUITE.erl
@@ -310,7 +310,7 @@ init_ec_pem_encode_generated(Config) ->
ec_pem_encode_generated() ->
[{doc, "PEM-encode generated EC key"}].
-ec_pem_encode_generated(Config) ->
+ec_pem_encode_generated(_Config) ->
Key1 = public_key:generate_key({namedCurve, 'secp384r1'}),
public_key:pem_entry_encode('ECPrivateKey', Key1),
@@ -965,7 +965,7 @@ pkix_verify_hostname_cn(Config) ->
%% openssl req -x509 -nodes -newkey rsa:1024 -keyout /dev/null -extensions SAN -config public_key_SUITE_data/verify_hostname.conf 2>/dev/null > public_key_SUITE_data/pkix_verify_hostname_subjAltName.pem
%%
%% Subject: C=SE, CN=example.com
-%% Subject Alternative Name: DNS:kb.example.org, URI:http://www.example.org, URI:https://wws.example.org
+%% Subject Alternative Name: DNS:kb.example.org, DNS:*.example.org, URI:http://www.example.org, URI:https://wws.example.org
pkix_verify_hostname_subjAltName(Config) ->
DataDir = proplists:get_value(data_dir, Config),
@@ -984,7 +984,16 @@ pkix_verify_hostname_subjAltName(Config) ->
{dns_id,"wws.example.org"}]),
%% Check that a dns_id matches a DNS subjAltName:
- true = public_key:pkix_verify_hostname(Cert, [{dns_id,"kb.example.org"}]).
+ true = public_key:pkix_verify_hostname(Cert, [{dns_id,"kb.example.org"}]),
+
+ %% Check that a dns_id does not match a DNS subjAltName wiht wildcard
+ false = public_key:pkix_verify_hostname(Cert, [{dns_id,"other.example.org"}]),
+
+ %% Check that a dns_id does nmatches a DNS subjAltName wiht wildcard with matchfun
+ true = public_key:pkix_verify_hostname(Cert, [{dns_id,"other.example.org"}],
+ [{match_fun, public_key:pkix_verify_hostname_match_fun(https)}
+ ]
+ ).
%%--------------------------------------------------------------------
%% Uses the pem-file for pkix_verify_hostname_cn
@@ -1351,7 +1360,7 @@ do_gen_ec_param(File) ->
ct:fail({key_gen_fail, File})
end.
-init_per_testcase_gen_ec_param(TC, Curve, Config) ->
+init_per_testcase_gen_ec_param(_TC, Curve, Config) ->
case crypto:ec_curves() of
[] ->
{skip, missing_ec_support};
@@ -1367,7 +1376,7 @@ init_per_testcase_gen_ec_param(TC, Curve, Config) ->
end.
-crypto_supported_curve(Curve, Curves) ->
+crypto_supported_curve(Curve, _Curves) ->
try crypto:generate_key(ecdh, Curve) of
{error,_} -> false; % Just in case crypto is changed in the future...
_-> true
diff --git a/lib/public_key/test/public_key_SUITE_data/pkix_verify_hostname_subjAltName.pem b/lib/public_key/test/public_key_SUITE_data/pkix_verify_hostname_subjAltName.pem
index 83e1ad37b3..7ab9ed7b96 100644
--- a/lib/public_key/test/public_key_SUITE_data/pkix_verify_hostname_subjAltName.pem
+++ b/lib/public_key/test/public_key_SUITE_data/pkix_verify_hostname_subjAltName.pem
@@ -1,14 +1,14 @@
-----BEGIN CERTIFICATE-----
-MIICEjCCAXugAwIBAgIJANwliLph5EiAMA0GCSqGSIb3DQEBCwUAMCMxCzAJBgNV
-BAYTAlNFMRQwEgYDVQQDEwtleGFtcGxlLmNvbTAeFw0xNjEyMjAxNTEyMjRaFw0x
-NzAxMTkxNTEyMjRaMCMxCzAJBgNVBAYTAlNFMRQwEgYDVQQDEwtleGFtcGxlLmNv
-bTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAydstIN157w8QxkVaOl3wm81j
-fgZ8gqO3BXkECPF6bw5ewLlmePL6Qs4RypsaRe7cKJ9rHFlwhpdcYkxWSWEt2N7Z
-Ry3N4SjuU04ohWbYgy3ijTt7bJg7jOV1Dh56BnI4hwhQj0oNFizNZOeRRfEzdMnS
-+uk03t/Qre2NS7KbwnUCAwEAAaNOMEwwSgYDVR0RBEMwQYIOa2IuZXhhbXBsZS5v
-cmeGFmh0dHA6Ly93d3cuZXhhbXBsZS5vcmeGF2h0dHBzOi8vd3dzLmV4YW1wbGUu
-b3JnMA0GCSqGSIb3DQEBCwUAA4GBAKqFqW5gCso422bXriCBJoygokOTTOw1Rzpq
-K8Mm0B8W9rrW9OTkoLEcjekllZcUCZFin2HovHC5HlHZz+mQvBI1M6sN2HVQbSzS
-EgL66U9gwJVnn9/U1hXhJ0LO28aGbyE29DxnewNR741dWN3oFxCdlNaO6eMWaEsO
-gduJ5sDl
+MIICITCCAYqgAwIBAgIJAP31suf/Fi4oMA0GCSqGSIb3DQEBCwUAMCMxCzAJBgNV
+BAYTAlNFMRQwEgYDVQQDEwtleGFtcGxlLmNvbTAeFw0xODA1MTcxMDIzNDBaFw0x
+ODA2MTYxMDIzNDBaMCMxCzAJBgNVBAYTAlNFMRQwEgYDVQQDEwtleGFtcGxlLmNv
+bTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAsUVMXSM4Q6vYp7H4Svsfv4QQ
+dmUD3IdTbtumlyAqLZuc6Z0HU9IOE0wpF97+5AE3moHluwN/MtSX/fb9oxCjh3L6
+iDla770uUoIgiWkA9lyzuYXt7zGsqc0EmGMJRAHp4jOxI26U/C8wdXoyZsGD8GPr
+hYAI2Me4CkdDqCoRuUUCAwEAAaNdMFswWQYDVR0RBFIwUIIOa2IuZXhhbXBsZS5v
+cmeCDSouZXhhbXBsZS5vcmeGFmh0dHA6Ly93d3cuZXhhbXBsZS5vcmeGF2h0dHBz
+Oi8vd3dzLmV4YW1wbGUub3JnMA0GCSqGSIb3DQEBCwUAA4GBAKs8vWMqpXiuFhcq
+6W1dMrVB4tuDjt1Ctr3g2USXBLgm8NxsZzslFyDnrvtZY0hbjcAkGKMMhy8lFD5t
++GjBbyp7MKII6vJaVvc+wbrsbNdvioB1puGwbgVhgD3Kb79do9h6JrNncjMvBN7j
+VK6BUB8TUofFmztMjoPlxFOs/7qK
-----END CERTIFICATE-----
diff --git a/lib/public_key/test/public_key_SUITE_data/verify_hostname.conf b/lib/public_key/test/public_key_SUITE_data/verify_hostname.conf
index a28864dc78..6b4e4f284e 100644
--- a/lib/public_key/test/public_key_SUITE_data/verify_hostname.conf
+++ b/lib/public_key/test/public_key_SUITE_data/verify_hostname.conf
@@ -10,7 +10,8 @@ CN=example.com
subjectAltName = @alt_names
[alt_names]
-DNS = kb.example.org
+DNS.1 = kb.example.org
+DNS.2 = *.example.org
URI.1 = http://www.example.org
URI.2 = https://wws.example.org
diff --git a/lib/ssh/src/ssh_client_channel.erl b/lib/ssh/src/ssh_client_channel.erl
index 134d3f08bd..8b5e196412 100644
--- a/lib/ssh/src/ssh_client_channel.erl
+++ b/lib/ssh/src/ssh_client_channel.erl
@@ -180,6 +180,8 @@ init([Options]) ->
{stop, Why} ->
{stop, Why}
catch
+ _:undef ->
+ {stop, {bad_channel_callback_module,Cb}};
_:Reason ->
{stop, Reason}
end.
diff --git a/lib/ssh/src/ssh_connection.erl b/lib/ssh/src/ssh_connection.erl
index ed03b4e2ed..dad7636e3f 100644
--- a/lib/ssh/src/ssh_connection.erl
+++ b/lib/ssh/src/ssh_connection.erl
@@ -498,25 +498,24 @@ handle_msg(#ssh_msg_channel_request{recipient_channel = ChannelId,
data = Data},
#connection{channel_cache = Cache} = Connection, server) ->
<<?DEC_BIN(SsName,_SsLen)>> = Data,
-
- #channel{remote_id = RemoteId} = Channel0 =
+ #channel{remote_id=RemoteId} = Channel =
ssh_client_channel:cache_lookup(Cache, ChannelId),
-
- ReplyMsg = {subsystem, ChannelId, WantReply, binary_to_list(SsName)},
-
- try
- {ok, Pid} = start_subsystem(SsName, Connection, Channel0, ReplyMsg),
- erlang:monitor(process, Pid),
- Channel = Channel0#channel{user = Pid},
- ssh_client_channel:cache_update(Cache, Channel),
- Reply = {connection_reply,
- channel_success_msg(RemoteId)},
- {[Reply], Connection}
- catch
- _:_ ->
- ErrorReply = {connection_reply, channel_failure_msg(RemoteId)},
- {[ErrorReply], Connection}
- end;
+ Reply =
+ try
+ start_subsystem(SsName, Connection, Channel,
+ {subsystem, ChannelId, WantReply, binary_to_list(SsName)})
+ of
+ {ok, Pid} ->
+ erlang:monitor(process, Pid),
+ ssh_client_channel:cache_update(Cache, Channel#channel{user=Pid}),
+ channel_success_msg(RemoteId);
+ {error,_Error} ->
+ channel_failure_msg(RemoteId)
+ catch
+ _:_ ->
+ channel_failure_msg(RemoteId)
+ end,
+ {[{connection_reply,Reply}], Connection};
handle_msg(#ssh_msg_channel_request{request_type = "subsystem"},
Connection, client) ->
@@ -822,7 +821,12 @@ start_channel(Cb, Id, Args, SubSysSup, Exec, Opts) ->
ChannelSup = ssh_subsystem_sup:channel_supervisor(SubSysSup),
case max_num_channels_not_exceeded(ChannelSup, Opts) of
true ->
- ssh_server_channel_sup:start_child(ChannelSup, Cb, Id, Args, Exec);
+ case ssh_server_channel_sup:start_child(ChannelSup, Cb, Id, Args, Exec) of
+ {error,{Error,_Info}} ->
+ throw(Error);
+ Others ->
+ Others
+ end;
false ->
throw(max_num_channels_exceeded)
end.
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index f1ff3a70e2..3e224fe13f 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -1345,11 +1345,11 @@ handle_event(info, {Proto, Sock, NewData}, StateName, D0 = #data{socket = Sock,
{next_event, internal, Msg}
]}
catch
- C:E ->
+ C:E:ST ->
{Shutdown, D} =
?send_disconnect(?SSH_DISCONNECT_PROTOCOL_ERROR,
io_lib:format("Bad packet: Decrypted, but can't decode~n~p:~p~n~p",
- [C,E,erlang:get_stacktrace()]),
+ [C,E,ST]),
StateName, D1),
{stop, Shutdown, D}
end;
@@ -1378,10 +1378,10 @@ handle_event(info, {Proto, Sock, NewData}, StateName, D0 = #data{socket = Sock,
StateName, D0),
{stop, Shutdown, D}
catch
- C:E ->
+ C:E:ST ->
{Shutdown, D} =
?send_disconnect(?SSH_DISCONNECT_PROTOCOL_ERROR,
- io_lib:format("Bad packet: Couldn't decrypt~n~p:~p~n~p",[C,E,erlang:get_stacktrace()]),
+ io_lib:format("Bad packet: Couldn't decrypt~n~p:~p~n~p",[C,E,ST]),
StateName, D0),
{stop, Shutdown, D}
end;
diff --git a/lib/ssh/test/Makefile b/lib/ssh/test/Makefile
index 0a99d31a63..9832a9b210 100644
--- a/lib/ssh/test/Makefile
+++ b/lib/ssh/test/Makefile
@@ -36,6 +36,7 @@ MODULES= \
ssh_options_SUITE \
ssh_basic_SUITE \
ssh_bench_SUITE \
+ ssh_chan_behaviours_SUITE \
ssh_compat_SUITE \
ssh_connection_SUITE \
ssh_dbg_SUITE \
@@ -53,6 +54,8 @@ MODULES= \
ssh_key_cb_options \
ssh_key_cb_engine_keys \
ssh_trpt_test_lib \
+ ssh_chan_behaviours_client \
+ ssh_chan_behaviours_server \
ssh_echo_server \
ssh_bench_dev_null \
ssh_peername_sockname_server \
diff --git a/lib/ssh/test/ssh_chan_behaviours_SUITE.erl b/lib/ssh/test/ssh_chan_behaviours_SUITE.erl
new file mode 100644
index 0000000000..16ed152bcd
--- /dev/null
+++ b/lib/ssh/test/ssh_chan_behaviours_SUITE.erl
@@ -0,0 +1,152 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2018-2018. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+
+-module(ssh_chan_behaviours_SUITE).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("ssh/src/ssh.hrl").
+-include("ssh_test_lib.hrl").
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap,{seconds,60}}].
+
+all() ->
+ [
+ noexist_subsystem,
+ undefined_subsystem,
+ defined_subsystem,
+ subsystem_client
+ ].
+
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ ?CHECK_CRYPTO(
+ begin
+ ssh:start(),
+ Config
+ end).
+
+end_per_suite(_Config) ->
+ {Time,R} = timer:tc(ssh, stop, []),
+ ct:log("Stop ssh: ~p ms",[(100*(Time div 1000)) / 100]),
+ R.
+
+init_per_testcase(_TC, Config) ->
+ SubSystems = [
+ {"bad_cb", {ssh_chan_behaviours_undefined, []}}, % A non-existing file
+ {"ch1", {ssh_chan_behaviours_server, [self(),true]}}
+ ],
+ {Pid, Host, Port} = ssh_test_lib:std_daemon(Config, [{subsystems,SubSystems}]),
+ C = ssh_test_lib:std_connect(Config, Host, Port, []),
+ [{connref,C}, {daemon_pid,Pid}| Config].
+
+end_per_testcase(_TC, Config) ->
+ {Time,_} = timer:tc(ssh, stop_daemon, [proplists:get_value(daemon_pid,Config)]),
+ ct:log("Stop daemon: ~p ms",[(100*(Time div 1000)) / 100]),
+ case flush() of
+ [] -> ok;
+ Msgs -> ct:pal("Unhandled messages:~n~p", [Msgs])
+ end.
+
+
+-define(EXPECT(What, Bind),
+ Bind =
+ (fun() ->
+ receive What ->
+ ct:log("~p:~p ~p got ~p",[?MODULE,?LINE,self(),What]),
+ Bind
+ after 5000 ->
+ ct:log("~p:~p ~p Flushed:~n~p",[?MODULE,?LINE,self(),flush()]),
+ ct:fail("Timeout!",[])
+ end
+ end)()
+ ).
+
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+%% Try start a subsystem whos name is not known by the server
+noexist_subsystem(Config) ->
+ C = proplists:get_value(connref, Config),
+ {ok, Ch} = ssh_connection:session_channel(C, infinity),
+ failure = ssh_connection:subsystem(C, Ch, "noexist", infinity),
+ ok = ssh_connection:close(C, Ch),
+ ?EXPECT({ssh_cm,C,{closed,Ch}},[]),
+ ok.
+
+%% Try to start a subsystem with a known name, but without any callback file
+undefined_subsystem(Config) ->
+ C = proplists:get_value(connref, Config),
+ {ok, Ch} = ssh_connection:session_channel(C, infinity),
+ failure = ssh_connection:subsystem(C, Ch, "bad_cb", infinity),
+ ok = ssh_connection:close(C, Ch),
+ ?EXPECT({ssh_cm,C,{closed,Ch}},[]), % self() is instead of a proper channel handler
+ ok.
+
+%% Try to start and stop a subsystem with known name and defined callback file
+defined_subsystem(Config) ->
+ C = proplists:get_value(connref, Config),
+ {ok, Ch1} = ssh_connection:session_channel(C, infinity),
+
+ success = ssh_connection:subsystem(C, Ch1, "ch1", infinity),
+ IDsrv = ?EXPECT({{_Csrv,_Ch1srv}, {ssh_channel_up,_Ch1srv,_Csrv}}, {_Csrv,_Ch1srv}),
+
+ ok = ssh_connection:close(C, Ch1),
+ ?EXPECT({IDsrv, {terminate,normal}}, []),
+ ?EXPECT({ssh_cm, C, {closed,Ch1}}, []), % self() is instead of a proper channel handler
+ ok.
+
+%% Try to start and stop a subsystem from a ssh_client_channel behviour
+subsystem_client(Config) ->
+ C = proplists:get_value(connref, Config),
+
+ {ok,ChRef} = ssh_chan_behaviours_client:start_link(C),
+ IDclt = ?EXPECT({{C,Ch1clt}, {ssh_channel_up,Ch1clt,C}}, {C,Ch1clt}),
+ IDsrv = ?EXPECT({{_Csrv,Ch1srv}, {ssh_channel_up,Ch1srv,_Csrv}}, {_Csrv,Ch1srv}),
+
+ ok = ssh_chan_behaviours_client:stop(ChRef),
+ ?EXPECT({IDclt, {terminate,normal}}, []), % From the proper channel handler
+ ?EXPECT({IDsrv, {terminate,normal}}, []),
+ ok.
+
+%%%================================================================
+%%%
+%%%
+
+flush() -> lists:reverse(flush([])).
+
+flush(Acc) ->
+ receive
+ M ->
+ flush([M|Acc])
+ after 0 ->
+ Acc
+ end.
+
diff --git a/lib/ssh/test/ssh_chan_behaviours_SUITE_data/ssh_host_dsa_key b/lib/ssh/test/ssh_chan_behaviours_SUITE_data/ssh_host_dsa_key
new file mode 100644
index 0000000000..51ab6fbd88
--- /dev/null
+++ b/lib/ssh/test/ssh_chan_behaviours_SUITE_data/ssh_host_dsa_key
@@ -0,0 +1,13 @@
+-----BEGIN DSA PRIVATE KEY-----
+MIIBuwIBAAKBgQCClaHzE2ul0gKSUxah5W0W8UiJLy4hXngKEqpaUq9SSdVdY2LK
+wVfKH1gt5iuaf1FfzOhsIC9G/GLnjYttXZc92cv/Gfe3gR+s0ni2++MX+T++mE/Q
+diltXv/Hp27PybS67SmiFW7I+RWnT2OKlMPtw2oUuKeztCe5UWjaj/y5FQIVAPLA
+l9RpiU30Z87NRAHY3NTRaqtrAoGANMRxw8UfdtNVR0CrQj3AgPaXOGE4d+G4Gp4X
+skvnCHycSVAjtYxebUkzUzt5Q6f/IabuLUdge3gXrc8BetvrcKbp+XZgM0/Vj2CF
+Ymmy3in6kzGZq7Fw1sZaku6AOU8vLa5woBT2vAcHLLT1bLAzj7viL048T6MfjrOP
+ef8nHvACgYBhDWFQJ1mf99sg92LalVq1dHLmVXb3PTJDfCO/Gz5NFmj9EZbAtdah
+/XcF3DeRF+eEoz48wQF/ExVxSMIhLdL+o+ElpVhlM7Yii+T7dPhkQfEul6zZXu+U
+ykSTXYUbtsfTNRFQGBW2/GfnEc0mnIxfn9v10NEWMzlq5z9wT9P0CgIVAN4wtL5W
+Lv62jKcdskxNyz2NQoBx
+-----END DSA PRIVATE KEY-----
+
diff --git a/lib/ssh/test/ssh_chan_behaviours_SUITE_data/ssh_host_dsa_key.pub b/lib/ssh/test/ssh_chan_behaviours_SUITE_data/ssh_host_dsa_key.pub
new file mode 100644
index 0000000000..4dbb1305b0
--- /dev/null
+++ b/lib/ssh/test/ssh_chan_behaviours_SUITE_data/ssh_host_dsa_key.pub
@@ -0,0 +1,11 @@
+---- BEGIN SSH2 PUBLIC KEY ----
+AAAAB3NzaC1kc3MAAACBAIKVofMTa6XSApJTFqHlbRbxSIkvLiFeeAoSqlpSr1JJ1V1j
+YsrBV8ofWC3mK5p/UV/M6GwgL0b8YueNi21dlz3Zy/8Z97eBH6zSeLb74xf5P76YT9B2
+KW1e/8enbs/JtLrtKaIVbsj5FadPY4qUw+3DahS4p7O0J7lRaNqP/LkVAAAAFQDywJfU
+aYlN9GfOzUQB2NzU0WqrawAAAIA0xHHDxR9201VHQKtCPcCA9pc4YTh34bganheyS+cI
+fJxJUCO1jF5tSTNTO3lDp/8hpu4tR2B7eBetzwF62+twpun5dmAzT9WPYIViabLeKfqT
+MZmrsXDWxlqS7oA5Ty8trnCgFPa8BwcstPVssDOPu+IvTjxPox+Os495/yce8AAAAIBh
+DWFQJ1mf99sg92LalVq1dHLmVXb3PTJDfCO/Gz5NFmj9EZbAtdah/XcF3DeRF+eEoz48
+wQF/ExVxSMIhLdL+o+ElpVhlM7Yii+T7dPhkQfEul6zZXu+UykSTXYUbtsfTNRFQGBW2
+/GfnEc0mnIxfn9v10NEWMzlq5z9wT9P0Cg==
+---- END SSH2 PUBLIC KEY ----
diff --git a/lib/ssh/test/ssh_chan_behaviours_SUITE_data/ssh_host_ecdsa_key b/lib/ssh/test/ssh_chan_behaviours_SUITE_data/ssh_host_ecdsa_key
new file mode 100644
index 0000000000..fb1a862ded
--- /dev/null
+++ b/lib/ssh/test/ssh_chan_behaviours_SUITE_data/ssh_host_ecdsa_key
@@ -0,0 +1,6 @@
+-----BEGIN EC PRIVATE KEY-----
+MIGkAgEBBDArxbDfh3p1okrD9wQw6jJ4d4DdlBPD5GqXE8bIeRJiK41Sh40LgvPw
+mkqEDSXK++CgBwYFK4EEACKhZANiAAScl43Ih2lWTDKrSox5ve5uiTXil4smsup3
+CfS1XPjKxgBAmlfBim8izbdrT0BFdQzz2joduNMtpt61wO4rGs6jm0UP7Kim9PC7
+Hneb/99fIYopdMH5NMnk60zGO1uZ2vc=
+-----END EC PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_chan_behaviours_SUITE_data/ssh_host_ecdsa_key.pub b/lib/ssh/test/ssh_chan_behaviours_SUITE_data/ssh_host_ecdsa_key.pub
new file mode 100644
index 0000000000..428d5fb7d7
--- /dev/null
+++ b/lib/ssh/test/ssh_chan_behaviours_SUITE_data/ssh_host_ecdsa_key.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBJyXjciHaVZMMqtKjHm97m6JNeKXiyay6ncJ9LVc+MrGAECaV8GKbyLNt2tPQEV1DPPaOh240y2m3rXA7isazqObRQ/sqKb08Lsed5v/318hiil0wfk0yeTrTMY7W5na9w== uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_chan_behaviours_SUITE_data/ssh_host_rsa_key b/lib/ssh/test/ssh_chan_behaviours_SUITE_data/ssh_host_rsa_key
new file mode 100644
index 0000000000..79968bdd7d
--- /dev/null
+++ b/lib/ssh/test/ssh_chan_behaviours_SUITE_data/ssh_host_rsa_key
@@ -0,0 +1,16 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQDCZX+4FBDwZIh9y/Uxee1VJnEXlowpz2yDKwj8semM4q843337
+zbNfxHmladB1lpz2NqyxI175xMIJuDxogyZdsOxGnFAzAnthR4dqL/RWRWzjaxSB
+6IAO9SPYVVlrpZ+1hsjLW79fwXK/yc8VdhRuWTeQiRgYY2ek8+OKbOqz4QIDAQAB
+AoGANmvJzJO5hkLuvyDZHKfAnGTtpifcR1wtSa9DjdKUyn8vhKF0mIimnbnYQEmW
+NUUb3gXCZLi9PvkpRSVRrASDOZwcjoU/Kvww163vBUVb2cOZfFhyn6o2Sk88Tt++
+udH3hdjpf9i7jTtUkUe+QYPsia+wgvvrmn4QrahLAH86+kECQQDx5gFeXTME3cnW
+WMpFz3PPumduzjqgqMMWEccX4FtQkMX/gyGa5UC7OHFyh0N/gSWvPbRHa8A6YgIt
+n8DO+fh5AkEAzbqX4DOn8NY6xJIi42q7l/2jIA0RkB6P7YugW5NblhqBZ0XDnpA5
+sMt+rz+K07u9XZtxgh1xi7mNfwY6lEAMqQJBAJBEauCKmRj35Z6OyeQku59SPsnY
++SJEREVvSNw2lH9SOKQQ4wPsYlTGbvKtNVZgAcen91L5MmYfeckYE/fdIZECQQCt
+64zxsTnM1I8iFxj/gP/OYlJBikrKt8udWmjaghzvLMEw+T2DExJyb9ZNeT53+UMB
+m6O+B/4xzU/djvp+0hbhAkAemIt+rA5kTmYlFndhpvzkSSM8a2EXsO4XIPgGWCTT
+tQKS/tTly0ADMjN/TVy11+9d6zcqadNVuHXHGtR4W0GR
+-----END RSA PRIVATE KEY-----
+
diff --git a/lib/ssh/test/ssh_chan_behaviours_SUITE_data/ssh_host_rsa_key.pub b/lib/ssh/test/ssh_chan_behaviours_SUITE_data/ssh_host_rsa_key.pub
new file mode 100644
index 0000000000..75d2025c71
--- /dev/null
+++ b/lib/ssh/test/ssh_chan_behaviours_SUITE_data/ssh_host_rsa_key.pub
@@ -0,0 +1,5 @@
+---- BEGIN SSH2 PUBLIC KEY ----
+AAAAB3NzaC1yc2EAAAADAQABAAAAgQDCZX+4FBDwZIh9y/Uxee1VJnEXlowpz2yDKwj8
+semM4q843337zbNfxHmladB1lpz2NqyxI175xMIJuDxogyZdsOxGnFAzAnthR4dqL/RW
+RWzjaxSB6IAO9SPYVVlrpZ+1hsjLW79fwXK/yc8VdhRuWTeQiRgYY2ek8+OKbOqz4Q==
+---- END SSH2 PUBLIC KEY ----
diff --git a/lib/ssh/test/ssh_chan_behaviours_client.erl b/lib/ssh/test/ssh_chan_behaviours_client.erl
new file mode 100644
index 0000000000..07ac21ba97
--- /dev/null
+++ b/lib/ssh/test/ssh_chan_behaviours_client.erl
@@ -0,0 +1,143 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+
+%%% Description: Example ssh client
+-module(ssh_chan_behaviours_client).
+-behaviour(ssh_client_channel).
+-record(state, {
+ parent,
+ cm,
+ ch,
+ dbg
+ }).
+-export([start_link/1, start/1,
+ stop/1, send_eof/1,
+ init/1, handle_msg/2, handle_ssh_msg/2, terminate/2,
+ code_change/3, handle_call/3, handle_cast/2
+ ]).
+
+-define(DBG(State,Fmt,Args),
+ case State#state.dbg of
+ true -> ct:log("~p:~p ~p C=~p Ch=~p "++Fmt,
+ [?MODULE,?LINE,self(),State#state.cm,State#state.ch|Args]);
+ false -> ok
+ end).
+
+
+start_link(C) ->
+ {ok, Ch} = ssh_connection:session_channel(C, infinity),
+ ssh_client_channel:start_link(C, Ch, ssh_chan_behaviours_client, [C, Ch, self(), true]).
+
+start(C) ->
+ {ok, Ch} = ssh_connection:session_channel(C, infinity),
+ ssh_client_channel:start(C, Ch, ssh_chan_behaviours_client, [C, Ch, self(), true]).
+
+send_eof(ChRef) ->
+ ssh_client_channel:call(ChRef, send_eof).
+
+stop(ChRef) ->
+ ssh_client_channel:call(ChRef, stop).
+
+
+init([C, Ch, Parent, Dbg|_Exec]) ->
+ case ssh_connection:subsystem(C, Ch, "ch1", infinity) of
+ success ->
+ State = #state{cm = C,
+ ch = Ch,
+ parent=Parent,
+ dbg=Dbg},
+ ?DBG(State, "callback spawned, parent = ~p", [Parent]),
+ {ok, State};
+
+ Other ->
+ {stop, Other}
+ end.
+
+handle_msg({ssh_channel_up, ChannelId, ConnectionManager}=M, State0) ->
+ State = State0#state{cm = ConnectionManager,
+ ch = ChannelId},
+ tell_parent(M, State),
+ ?DBG(State, "ssh_channel_up",[]),
+ {ok, State}.
+
+handle_ssh_msg({ssh_cm, C, {data, Ch, 0, Data}}=M, #state{ch=Ch,cm=C} = State) ->
+ tell_parent(M, State),
+ ?DBG(State, "ssh_cm data size(Data)=~p",[size(Data)]),
+ {ok, State};
+
+handle_ssh_msg({ssh_cm, C, {data, Ch, Type, Data}}=M, #state{ch=Ch,cm=C} = State) ->
+ tell_parent(M, State),
+ ?DBG(State, "ssh_cm data Type=~p : ~p",[Type,Data]),
+ {ok, State};
+
+handle_ssh_msg({ssh_cm, C, {eof, Ch}}=M, #state{ch=Ch,cm=C} = State) ->
+ tell_parent(M, State),
+ ?DBG(State, "eof",[]),
+ {ok, State};
+
+handle_ssh_msg({ssh_cm, C, {signal, _Ch, _SigNameStr}=Sig} = M, #state{ch=Ch,cm=C} = State) ->
+ %% Ignore signals according to RFC 4254 section 6.9.
+ tell_parent(M, State),
+ ?DBG(State, "~p",[Sig]),
+ {ok, State};
+
+handle_ssh_msg({ssh_cm, C, {exit_signal, Ch, _, _Error, _}=Sig}=M, #state{ch=Ch,cm=C} = State) ->
+ tell_parent(M, State),
+ ?DBG(State, "~p",[Sig]),
+ {stop, Ch, State};
+
+handle_ssh_msg({ssh_cm, C, {exit_status, Ch, _Status}=Sig}=M, #state{ch=Ch,cm=C} = State) ->
+ tell_parent(M, State),
+ ?DBG(State, "~p",[Sig]),
+ {stop, Ch, State}.
+
+
+handle_call(send_eof, _From,#state{ch=Ch,cm=C} = State) ->
+ {reply, ssh_connection:send_eof(C,Ch), State};
+
+handle_call(stop, _From, State) ->
+ {stop, normal, ok, State};
+
+handle_call(Msg, _From, State) ->
+ ?DBG(State, "Unknown call ~p", [Msg]),
+ {reply, {unknown_call,Msg}, State}.
+
+
+terminate(Reason, State) ->
+ tell_parent({terminate,Reason}, State),
+ ?DBG(State, "terminate Reason = ~p",[Reason]).
+
+
+handle_cast(Msg, State) ->
+ ?DBG(State, "Unknown cast ~p", [Msg]),
+ {noreply, State}.
+
+code_change(_OldVsn, State, _Extra) -> {ok, State}.
+
+%%%================================================================
+%%%
+%%%
+
+tell_parent(Msg, #state{parent = Parent,
+ cm = C,
+ ch = Ch}) -> Parent ! {{C,Ch}, Msg}.
+
diff --git a/lib/ssh/test/ssh_chan_behaviours_server.erl b/lib/ssh/test/ssh_chan_behaviours_server.erl
new file mode 100644
index 0000000000..a5ec19e0cf
--- /dev/null
+++ b/lib/ssh/test/ssh_chan_behaviours_server.erl
@@ -0,0 +1,96 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+
+%%% Description: Example ssh server
+-module(ssh_chan_behaviours_server).
+-behaviour(ssh_server_channel).
+-record(state, {
+ parent,
+ cm,
+ ch,
+ dbg
+ }).
+-export([init/1, handle_msg/2, handle_ssh_msg/2, terminate/2]).
+
+-define(DBG(State,Fmt,Args),
+ case State#state.dbg of
+ true -> ct:log("~p:~p ~p C=~p Ch=~p "++Fmt,
+ [?MODULE,?LINE,self(),State#state.cm,State#state.ch|Args]);
+ false -> ok
+ end).
+
+
+init([Pid,Dbg|_Exec]) ->
+ {ok, #state{parent=Pid,
+ dbg=Dbg}}.
+
+handle_msg({ssh_channel_up, ChannelId, ConnectionManager}=M, State0) ->
+ State = State0#state{cm = ConnectionManager,
+ ch = ChannelId},
+ tell_parent(M, State),
+ ?DBG(State, "ssh_channel_up",[]),
+ {ok, State}.
+
+handle_ssh_msg({ssh_cm, C, {data, Ch, 0, Data}}=M, #state{ch=Ch,cm=C} = State) ->
+ tell_parent(M, State),
+ ?DBG(State, "ssh_cm data size(Data)=~p",[size(Data)]),
+ {ok, State};
+
+handle_ssh_msg({ssh_cm, C, {data, Ch, Type, Data}}=M, #state{ch=Ch,cm=C} = State) ->
+ tell_parent(M, State),
+ ?DBG(State, "ssh_cm data Type=~p : ~p",[Type,Data]),
+ {ok, State};
+
+handle_ssh_msg({ssh_cm, C, {eof, Ch}}=M, #state{ch=Ch,cm=C} = State) ->
+ tell_parent(M, State),
+ ?DBG(State, "eof",[]),
+ {ok, State};
+
+handle_ssh_msg({ssh_cm, C, {signal, _Ch, _SigNameStr}=Sig} = M, #state{ch=Ch,cm=C} = State) ->
+ %% Ignore signals according to RFC 4254 section 6.9.
+ tell_parent(M, State),
+ ?DBG(State, "~p",[Sig]),
+ {ok, State};
+
+handle_ssh_msg({ssh_cm, C, {exit_signal, Ch, _, _Error, _}=Sig}=M, #state{ch=Ch,cm=C} = State) ->
+ tell_parent(M, State),
+ ?DBG(State, "~p",[Sig]),
+ {stop, Ch, State};
+
+handle_ssh_msg({ssh_cm, C, {exit_status, Ch, _Status}=Sig}=M, #state{ch=Ch,cm=C} = State) ->
+ tell_parent(M, State),
+ ?DBG(State, "~p",[Sig]),
+ {stop, Ch, State}.
+
+terminate(Reason, State) ->
+ tell_parent({terminate,Reason}, State),
+ ?DBG(State, "terminate Reason = ~p",[Reason]),
+ ok.
+
+%%%================================================================
+%%%
+%%%
+
+tell_parent(Msg, #state{parent = Parent,
+ cm = C,
+ ch = Ch}) -> Parent ! {{C,Ch}, Msg}.
+
diff --git a/lib/stdlib/doc/src/calendar.xml b/lib/stdlib/doc/src/calendar.xml
index 8f2b6b747a..6b4fa7f98a 100644
--- a/lib/stdlib/doc/src/calendar.xml
+++ b/lib/stdlib/doc/src/calendar.xml
@@ -323,7 +323,9 @@
<type name="rfc3339_string"/>
<type name="rfc3339_time_unit"/>
<desc>
- <p>Converts an RFC 3339 timestamp into system time.</p>
+ <p>Converts an RFC 3339 timestamp into system time. The data format
+ of RFC 3339 timestamps is described by
+ <url href="https://www.ietf.org/rfc/rfc3339.txt">RFC 3339</url>.</p>
<p>Valid option:</p>
<taglist>
<tag><c>{unit, Unit}</c></tag>
@@ -378,7 +380,10 @@
<type name="rfc3339_string"/>
<type name="rfc3339_time_unit"/>
<desc>
- <p>Converts a system time into RFC 3339 timestamp.</p>
+ <p>Converts a system time into an RFC 3339 timestamp. The data format
+ of RFC 3339 timestamps is described by
+ <url href="https://www.ietf.org/rfc/rfc3339.txt">RFC 3339</url>.
+ The data format of offsets is also described by RFC 3339.</p>
<p>Valid options:</p>
<taglist>
<tag><c>{offset, Offset}</c></tag>
diff --git a/lib/stdlib/doc/src/ets.xml b/lib/stdlib/doc/src/ets.xml
index 305376a425..1995262145 100644
--- a/lib/stdlib/doc/src/ets.xml
+++ b/lib/stdlib/doc/src/ets.xml
@@ -49,14 +49,16 @@
associated with each key. A <c>bag</c> or <c>duplicate_bag</c> table can
have many objects associated with each key.</p>
+ <marker id="max_ets_tables"></marker>
<note>
<p>
The number of tables stored at one Erlang node <em>used</em> to
be limited. This is no longer the case (except by memory usage).
The previous default limit was about 1400 tables and
could be increased by setting the environment variable
- <c>ERL_MAX_ETS_TABLES</c> before starting the Erlang runtime
- system. This hard limit has been removed, but it is currently
+ <c>ERL_MAX_ETS_TABLES</c> or the command line option
+ <seealso marker="erts:erl#+e"><c>+e</c></seealso> before starting the
+ Erlang runtime system. This hard limit has been removed, but it is currently
useful to set the <c>ERL_MAX_ETS_TABLES</c> anyway. It should be
set to an approximate of the maximum amount of tables used. This since
an internal table for named tables is sized using this value. If
diff --git a/lib/stdlib/src/array.erl b/lib/stdlib/src/array.erl
index a237eaa489..939b1fb488 100644
--- a/lib/stdlib/src/array.erl
+++ b/lib/stdlib/src/array.erl
@@ -290,7 +290,7 @@ new(Size, Fixed, Default) ->
end,
#array{size = Size, max = M, default = Default, elements = E}.
--spec find_max(integer(), integer()) -> integer().
+-spec find_max(integer(), non_neg_integer()) -> non_neg_integer().
find_max(I, M) when I >= M ->
find_max(I, ?extend(M));
diff --git a/lib/stdlib/src/io.erl b/lib/stdlib/src/io.erl
index f510f61e9f..5d5773c80c 100644
--- a/lib/stdlib/src/io.erl
+++ b/lib/stdlib/src/io.erl
@@ -86,7 +86,16 @@ put_chars(Chars) ->
CharData :: unicode:chardata().
put_chars(Io, Chars) ->
- o_request(Io, {put_chars,unicode,Chars}, put_chars).
+ put_chars(Io, unicode, Chars).
+
+%% This function is here to make the erlang:raise in o_request actually raise to
+%% a valid function.
+-spec put_chars(IoDevice, Encoding, CharData) -> 'ok' when
+ IoDevice :: device(),
+ Encoding :: unicode,
+ CharData :: unicode:chardata().
+put_chars(Io, Encoding, Chars) ->
+ o_request(Io, {put_chars,Encoding,Chars}, put_chars).
-spec nl() -> 'ok'.
diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl
index 574aac96c8..a97fe4a5d9 100644
--- a/lib/stdlib/test/ets_SUITE.erl
+++ b/lib/stdlib/test/ets_SUITE.erl
@@ -6209,20 +6209,23 @@ spawn_logger(Procs) ->
ok;
(Proc) ->
Mon = erlang:monitor(process, Proc),
- receive
+ ok = receive
{'DOWN', Mon, _, _, _} ->
ok
after 0 ->
case Kill of
true -> exit(Proc, kill);
- _ ->
- erlang:display({"Waiting for 'DOWN' from", Proc,
- process_info(Proc),
- pid_status(Proc)})
+ _ -> ok
end,
receive
{'DOWN', Mon, _, _, _} ->
ok
+ after 5000 ->
+ io:format("Waiting for 'DOWN' from ~w, status=~w\n"
+ "info = ~p\n", [Proc,
+ pid_status(Proc),
+ process_info(Proc)]),
+ timeout
end
end
end, Procs),
diff --git a/lib/syntax_tools/src/erl_syntax.erl b/lib/syntax_tools/src/erl_syntax.erl
index b816c0699c..029f1e88ac 100644
--- a/lib/syntax_tools/src/erl_syntax.erl
+++ b/lib/syntax_tools/src/erl_syntax.erl
@@ -5328,7 +5328,7 @@ revert_map_type_assoc(Node) ->
Pos = get_pos(Node),
Name = map_type_assoc_name(Node),
Value = map_type_assoc_value(Node),
- {type, Pos, map_type_assoc, [Name, Value]}.
+ {type, Pos, map_field_assoc, [Name, Value]}.
%% =====================================================================
@@ -5386,7 +5386,7 @@ revert_map_type_exact(Node) ->
Pos = get_pos(Node),
Name = map_type_exact_name(Node),
Value = map_type_exact_value(Node),
- {type, Pos, map_type_exact, [Name, Value]}.
+ {type, Pos, map_field_exact, [Name, Value]}.
%% =====================================================================
diff --git a/lib/syntax_tools/test/syntax_tools_SUITE.erl b/lib/syntax_tools/test/syntax_tools_SUITE.erl
index ae2c67c03e..c8e6448d37 100644
--- a/lib/syntax_tools/test/syntax_tools_SUITE.erl
+++ b/lib/syntax_tools/test/syntax_tools_SUITE.erl
@@ -24,13 +24,14 @@
%% Test cases
-export([app_test/1,appup_test/1,smoke_test/1,revert/1,revert_map/1,
+ revert_map_type/1,
t_abstract_type/1,t_erl_parse_type/1,t_epp_dodger/1,
t_comment_scan/1,t_igor/1,t_erl_tidy/1]).
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [app_test,appup_test,smoke_test,revert,revert_map,
+ [app_test,appup_test,smoke_test,revert,revert_map,revert_map_type,
t_abstract_type,t_erl_parse_type,t_epp_dodger,
t_comment_scan,t_igor,t_erl_tidy].
@@ -121,7 +122,26 @@ revert_map(Config) when is_list(Config) ->
{map_field_assoc,{atom,17,name},{var,18,'Value'}}}]),
?t:timetrap_cancel(Dog).
-
+%% Testing bug fix for reverting map_field_assoc in types
+revert_map_type(Config) when is_list(Config) ->
+ Dog = ?t:timetrap(?t:minutes(1)),
+ Form1 = {attribute,4,record,
+ {state,
+ [{typed_record_field,
+ {record_field,5,{atom,5,x}},
+ {type,5,map,
+ [{type,5,map_field_exact,[{atom,5,y},{atom,5,z}]}]}}]}},
+ Mapped1 = erl_syntax_lib:map(fun(X) -> X end, Form1),
+ Form1 = erl_syntax:revert(Mapped1),
+ Form2 = {attribute,4,record,
+ {state,
+ [{typed_record_field,
+ {record_field,5,{atom,5,x}},
+ {type,5,map,
+ [{type,5,map_field_assoc,[{atom,5,y},{atom,5,z}]}]}}]}},
+ Mapped2 = erl_syntax_lib:map(fun(X) -> X end, Form2),
+ Form2 = erl_syntax:revert(Mapped2),
+ ?t:timetrap_cancel(Dog).
%% api tests
diff --git a/make/otp_release_targets.mk b/make/otp_release_targets.mk
index 779aaa1a1e..f57116569c 100644
--- a/make/otp_release_targets.mk
+++ b/make/otp_release_targets.mk
@@ -36,7 +36,9 @@ endif
# -------------------------------------------------------
# Take the XML files and add the github link info to them
# -------------------------------------------------------
+ifneq ($(strip $(XMLDIR)),)
_create_xml_dirs := $(shell mkdir -p $(XMLDIR))
+endif
XML_GEN_FILES+=$(patsubst %.xml,$(XMLDIR)/%.xml,$(XML_FILES))
$(XMLDIR)/%.xml: %.xml
diff --git a/system/doc/efficiency_guide/advanced.xml b/system/doc/efficiency_guide/advanced.xml
index 83b87a3641..fe77ce8ea4 100644
--- a/system/doc/efficiency_guide/advanced.xml
+++ b/system/doc/efficiency_guide/advanced.xml
@@ -188,11 +188,6 @@
limit can be raised or lowered using the <c>+t</c> option.</cell>
</row>
<row>
- <cell>Ets tables</cell>
- <cell>Default is 1400. It can be changed with the environment
- variable <c>ERL_MAX_ETS_TABLES</c>.</cell>
- </row>
- <row>
<cell>Elements in a tuple</cell>
<cell>The maximum number of elements in a tuple is 16,777,215
(24-bit unsigned integer).</cell>