aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--erts/doc/src/erl_nif.xml29
-rw-r--r--erts/emulator/beam/erl_bif_port.c2
-rw-r--r--erts/emulator/beam/erl_nif.c19
-rw-r--r--erts/emulator/beam/erl_nif.h11
-rw-r--r--erts/emulator/beam/erl_nif_api_funcs.h4
-rw-r--r--erts/emulator/beam/erl_process.c2
-rw-r--r--erts/emulator/beam/sys.h3
-rw-r--r--erts/emulator/drivers/common/inet_drv.c81
-rw-r--r--erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c11
-rw-r--r--erts/emulator/test/process_SUITE.erl6
-rw-r--r--erts/preloaded/ebin/prim_inet.beambin76292 -> 76348 bytes
-rw-r--r--erts/preloaded/src/prim_inet.erl4
-rw-r--r--lib/common_test/src/test_server.erl132
-rw-r--r--lib/common_test/src/test_server_ctrl.erl63
-rw-r--r--lib/common_test/src/test_server_gl.erl5
-rw-r--r--lib/common_test/src/test_server_io.erl17
-rw-r--r--lib/common_test/src/test_server_node.erl12
-rw-r--r--lib/common_test/src/test_server_sup.erl5
-rw-r--r--lib/common_test/src/vts.erl19
-rw-r--r--lib/common_test/test/Makefile3
-rw-r--r--lib/common_test/test/ct_log_SUITE.erl328
-rw-r--r--lib/common_test/test/ct_repeat_testrun_SUITE.erl60
-rw-r--r--lib/compiler/doc/src/compile.xml16
-rw-r--r--lib/compiler/src/compile.erl9
-rw-r--r--lib/compiler/test/compile_SUITE.erl22
-rw-r--r--lib/debugger/src/dbg_debugged.erl5
-rw-r--r--lib/debugger/src/dbg_icmd.erl8
-rw-r--r--lib/debugger/src/dbg_iserver.erl17
-rw-r--r--lib/debugger/src/dbg_wx_break_win.erl34
-rw-r--r--lib/debugger/src/dbg_wx_code.erl10
-rw-r--r--lib/debugger/src/dbg_wx_filedialog_win.erl16
-rw-r--r--lib/debugger/src/dbg_wx_mon.erl4
-rw-r--r--lib/debugger/src/dbg_wx_mon_win.erl24
-rw-r--r--lib/debugger/src/dbg_wx_src_view.erl2
-rw-r--r--lib/debugger/src/dbg_wx_trace.erl2
-rw-r--r--lib/debugger/src/dbg_wx_trace_win.erl57
-rw-r--r--lib/debugger/src/dbg_wx_win.erl13
-rw-r--r--lib/debugger/src/dbg_wx_winman.erl4
-rw-r--r--lib/debugger/src/i.erl2
-rw-r--r--lib/debugger/src/int.erl24
-rw-r--r--lib/dialyzer/src/dialyzer_contracts.erl11
-rw-r--r--lib/dialyzer/src/dialyzer_dataflow.erl58
-rw-r--r--lib/dialyzer/test/small_SUITE_data/results/higher_order_discrepancy5
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/higher_order_discrepancy.erl5
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/loopy.erl17
-rw-r--r--lib/dialyzer/test/user_SUITE_data/results/wpc_hlines3
-rw-r--r--lib/dialyzer/test/user_SUITE_data/src/wpc_hlines.erl22
-rw-r--r--lib/edoc/priv/edoc.dtd6
-rw-r--r--lib/edoc/src/edoc_layout.erl24
-rw-r--r--lib/edoc/src/edoc_parser.yrl17
-rw-r--r--lib/edoc/src/edoc_scanner.erl2
-rw-r--r--lib/edoc/src/edoc_specs.erl6
-rw-r--r--lib/edoc/src/edoc_types.erl4
-rw-r--r--lib/edoc/src/edoc_types.hrl2
-rw-r--r--lib/erl_docgen/src/docgen_otp_specs.erl38
-rw-r--r--lib/erl_interface/src/misc/ei_portio.h9
-rw-r--r--lib/kernel/doc/src/gen_tcp.xml21
-rw-r--r--lib/kernel/doc/src/gen_udp.xml19
-rw-r--r--lib/kernel/doc/src/inet.xml53
-rw-r--r--lib/kernel/src/gen_tcp.erl12
-rw-r--r--lib/kernel/src/gen_udp.erl10
-rw-r--r--lib/kernel/src/inet.erl92
-rw-r--r--lib/kernel/src/inet_int.hrl1
-rw-r--r--lib/kernel/src/local_tcp.erl10
-rw-r--r--lib/kernel/src/local_udp.erl9
-rw-r--r--lib/observer/src/crashdump_viewer.erl3
-rw-r--r--lib/observer/src/observer_procinfo.erl2
-rw-r--r--lib/sasl/test/release_handler_SUITE.erl2
-rw-r--r--lib/ssl/doc/src/ssl.xml17
-rw-r--r--lib/ssl/src/ssl.erl29
-rw-r--r--lib/ssl/src/ssl_socket.erl7
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl74
-rw-r--r--lib/stdlib/src/supervisor.erl17
-rw-r--r--lib/stdlib/test/id_transform_SUITE.erl9
-rw-r--r--lib/stdlib/test/supervisor_SUITE.erl12
75 files changed, 1244 insertions, 469 deletions
diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml
index 4efd155b09..b2e2254a65 100644
--- a/erts/doc/src/erl_nif.xml
+++ b/erts/doc/src/erl_nif.xml
@@ -1101,15 +1101,6 @@ typedef enum {
Erlang operators <c>=:=</c> and
<c>=/=</c>.</p></desc>
</func>
- <func><name><ret>int</ret><nametext>enif_is_on_dirty_scheduler(ErlNifEnv* env)</nametext></name>
- <fsummary>Check to see if executing on a dirty scheduler thread</fsummary>
- <desc>
- <p>Check to see if the current NIF is executing on a dirty scheduler thread. If
- executing on a dirty scheduler thread true returned; otherwise false.</p>
- <p>This function can only be used from a NIF-calling thread, and with an
- environment corresponding to currently executing processes.</p>
- </desc>
- </func>
<func><name><ret>int</ret><nametext>enif_is_pid(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name>
<fsummary>Determine if a term is a pid</fsummary>
<desc><p>Return true if <c>term</c> is a pid.</p></desc>
@@ -1820,7 +1811,25 @@ enif_map_iterator_destroy(env, &amp;iter);
<desc><p>Same as <seealso marker="erl_driver#erl_drv_thread_self">erl_drv_thread_self</seealso>.
</p></desc>
</func>
-
+ <func><name><ret>int</ret><nametext>enif_thread_type(void)</nametext></name>
+ <fsummary>Determine type of current thread</fsummary>
+ <desc>
+ <p>Determine the type of currently executing thread. A positive value
+ indicates a scheduler thread while a negative value or zero indicates
+ another type of thread. Currently the following specific types exist
+ (which may be extended in the future):</p>
+ <taglist>
+ <tag><c>ERL_NIF_THR_UNDEFINED</c></tag>
+ <value><p>Undefined thread that is not a scheduler thread.</p></value>
+ <tag><c>ERL_NIF_THR_NORMAL_SCHEDULER</c></tag>
+ <value><p>A normal scheduler thread.</p></value>
+ <tag><c>ERL_NIF_THR_DIRTY_CPU_SCHEDULER</c></tag>
+ <value><p>A dirty CPU scheduler thread.</p></value>
+ <tag><c>ERL_NIF_THR_DIRTY_IO_SCHEDULER</c></tag>
+ <value><p>A dirty I/O scheduler thread.</p></value>
+ </taglist>
+ </desc>
+ </func>
<func>
<name><ret>ErlNifTime</ret><nametext>enif_time_offset(ErlNifTimeUnit time_unit)</nametext></name>
<fsummary>Get current Time Offset</fsummary>
diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c
index 37f4e1de49..fefa9d8391 100644
--- a/erts/emulator/beam/erl_bif_port.c
+++ b/erts/emulator/beam/erl_bif_port.c
@@ -1411,7 +1411,7 @@ BIF_RETTYPE decode_packet_3(BIF_ALIST_3)
trunc_len = val;
goto next_option;
case am_line_delimiter:
- if (type == TCP_PB_LINE_LF && val >= 0 && val <= 255) {
+ if (type == TCP_PB_LINE_LF && val <= 255) {
delimiter = (char)val;
goto next_option;
}
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index 4fd82bad10..039f97ef43 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -2658,18 +2658,21 @@ done:
}
int
-enif_is_on_dirty_scheduler(ErlNifEnv* env)
+enif_thread_type(void)
{
- int scheduler;
- Process *c_p;
+ ErtsSchedulerData *esdp = erts_get_scheduler_data();
- execution_state(env, &c_p, &scheduler);
+ if (!esdp)
+ return ERL_NIF_THR_UNDEFINED;
+
+ if (!ERTS_SCHEDULER_IS_DIRTY(esdp))
+ return ERL_NIF_THR_NORMAL_SCHEDULER;
- if (!c_p || !scheduler)
- erts_exit(ERTS_ABORT_EXIT, "enif_is_on_dirty_scheduler: "
- "Invalid env");
+ if (ERTS_SCHEDULER_IS_DIRTY_CPU(esdp))
+ return ERL_NIF_THR_DIRTY_CPU_SCHEDULER;
- return scheduler < 0;
+ ASSERT(ERTS_SCHEDULER_IS_DIRTY_IO(esdp));
+ return ERL_NIF_THR_DIRTY_IO_SCHEDULER;
}
/* Maps */
diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h
index da7a754757..494971e118 100644
--- a/erts/emulator/beam/erl_nif.h
+++ b/erts/emulator/beam/erl_nif.h
@@ -209,6 +209,17 @@ typedef enum {
ERL_NIF_BIN2TERM_SAFE = 0x20000000
} ErlNifBinaryToTerm;
+/*
+ * Return values from enif_thread_type(). Negative values
+ * reserved for specific types of non-scheduler threads.
+ * Positive values reserved for scheduler thread types.
+ */
+
+#define ERL_NIF_THR_UNDEFINED 0
+#define ERL_NIF_THR_NORMAL_SCHEDULER 1
+#define ERL_NIF_THR_DIRTY_CPU_SCHEDULER 2
+#define ERL_NIF_THR_DIRTY_IO_SCHEDULER 3
+
#if (defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_))
# define ERL_NIF_API_FUNC_DECL(RET_TYPE, NAME, ARGS) RET_TYPE (*NAME) ARGS
typedef struct {
diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h
index b211ab4b16..9a8f216773 100644
--- a/erts/emulator/beam/erl_nif_api_funcs.h
+++ b/erts/emulator/beam/erl_nif_api_funcs.h
@@ -173,7 +173,7 @@ ERL_NIF_API_FUNC_DECL(int, enif_get_local_port, (ErlNifEnv* env, ERL_NIF_TERM, E
ERL_NIF_API_FUNC_DECL(int, enif_term_to_binary, (ErlNifEnv *env, ERL_NIF_TERM term, ErlNifBinary *bin));
ERL_NIF_API_FUNC_DECL(size_t, enif_binary_to_term, (ErlNifEnv *env, const unsigned char* data, size_t sz, ERL_NIF_TERM *term, unsigned int opts));
ERL_NIF_API_FUNC_DECL(int, enif_port_command, (ErlNifEnv *env, const ErlNifPort* to_port, ErlNifEnv *msg_env, ERL_NIF_TERM msg));
-ERL_NIF_API_FUNC_DECL(int,enif_is_on_dirty_scheduler,(ErlNifEnv*));
+ERL_NIF_API_FUNC_DECL(int,enif_thread_type,(void));
ERL_NIF_API_FUNC_DECL(int,enif_snprintf,(char * buffer, size_t size, const char *format, ...));
/*
@@ -330,7 +330,7 @@ ERL_NIF_API_FUNC_DECL(int,enif_snprintf,(char * buffer, size_t size, const char
# define enif_term_to_binary ERL_NIF_API_FUNC_MACRO(enif_term_to_binary)
# define enif_binary_to_term ERL_NIF_API_FUNC_MACRO(enif_binary_to_term)
# define enif_port_command ERL_NIF_API_FUNC_MACRO(enif_port_command)
-# define enif_is_on_dirty_scheduler ERL_NIF_API_FUNC_MACRO(enif_is_on_dirty_scheduler)
+# define enif_thread_type ERL_NIF_API_FUNC_MACRO(enif_thread_type)
# define enif_snprintf ERL_NIF_API_FUNC_MACRO(enif_snprintf)
/*
diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c
index c0b1d7246c..5193be85b4 100644
--- a/erts/emulator/beam/erl_process.c
+++ b/erts/emulator/beam/erl_process.c
@@ -10082,7 +10082,7 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls)
}
}
- if (ERTS_IS_GC_DESIRED(p)) {
+ if (ERTS_IS_GC_DESIRED(p) && !ERTS_SCHEDULER_IS_DIRTY_IO(esdp)) {
if (!(state & ERTS_PSFLG_EXITING) && !(p->flags & (F_DELAY_GC|F_DISABLE_GC))) {
int cost = scheduler_gc_proc(p, reds);
calls += cost;
diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h
index 9a205d50d3..dfe82cab44 100644
--- a/erts/emulator/beam/sys.h
+++ b/erts/emulator/beam/sys.h
@@ -154,8 +154,9 @@ typedef ERTS_SYS_FD_TYPE ErtsSysFdType;
# define ERTS_WRITE_UNLIKELY(X) X
#endif
+/* clang may have too low __GNUC__ versions but can handle it */
#ifdef __GNUC__
-# if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 5)
+# if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 5) || defined(__clang__)
# define ERTS_DECLARE_DUMMY(X) X __attribute__ ((unused))
# else
# define ERTS_DECLARE_DUMMY(X) X
diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c
index 06dfb2dd10..93ea9f5dcb 100644
--- a/erts/emulator/drivers/common/inet_drv.c
+++ b/erts/emulator/drivers/common/inet_drv.c
@@ -627,6 +627,7 @@ static int is_nonzero(const char *s, size_t n)
*/
/* general address encode/decode tag */
+#define INET_AF_UNSPEC 0
#define INET_AF_INET 1
#define INET_AF_INET6 2
#define INET_AF_ANY 3 /* INADDR_ANY or IN6ADDR_ANY_INIT */
@@ -655,7 +656,7 @@ static int is_nonzero(const char *s, size_t n)
/* INET_REQ_GETSTATUS enumeration */
#define INET_F_OPEN 0x0001
-#define INET_F_BOUND 0x0002
+/* INET_F_BOUND removed - renumber when there comes a bigger rewrite */
#define INET_F_ACTIVE 0x0004
#define INET_F_LISTEN 0x0008
#define INET_F_CON 0x0010
@@ -853,19 +854,15 @@ static int is_nonzero(const char *s, size_t n)
#define INET_STATE_CLOSED (0)
#define INET_STATE_OPEN (INET_F_OPEN)
-#define INET_STATE_BOUND (INET_STATE_OPEN | INET_F_BOUND)
-#define INET_STATE_CONNECTED (INET_STATE_BOUND | INET_F_ACTIVE)
-#define INET_STATE_LISTENING (INET_STATE_BOUND | INET_F_LISTEN)
-#define INET_STATE_CONNECTING (INET_STATE_BOUND | INET_F_CON)
+#define INET_STATE_CONNECTED (INET_STATE_OPEN | INET_F_ACTIVE)
+#define INET_STATE_LISTENING (INET_STATE_OPEN | INET_F_LISTEN)
+#define INET_STATE_CONNECTING (INET_STATE_OPEN | INET_F_CON)
#define INET_STATE_ACCEPTING (INET_STATE_LISTENING | INET_F_ACC)
#define INET_STATE_MULTI_ACCEPTING (INET_STATE_ACCEPTING | INET_F_MULTI_CLIENT)
#define IS_OPEN(d) \
(((d)->state & INET_F_OPEN) == INET_F_OPEN)
-#define IS_BOUND(d) \
- (((d)->state & INET_F_BOUND) == INET_F_BOUND)
-
#define IS_CONNECTED(d) \
(((d)->state & INET_STATE_CONNECTED) == INET_STATE_CONNECTED)
@@ -1287,6 +1284,7 @@ static int async_ref = 0; /* async reference id generator */
static ErlDrvTermData am_ok;
static ErlDrvTermData am_undefined;
+static ErlDrvTermData am_unspec;
static ErlDrvTermData am_tcp;
static ErlDrvTermData am_error;
static ErlDrvTermData am_einval;
@@ -1553,10 +1551,18 @@ static int load_address(ErlDrvTermData* spec, int i, char* buf)
break;
}
#endif
- default: { /* INET_AF_UNDEFINED */
- i = LOAD_ATOM(spec, i, am_undefined);
+ case INET_AF_UNSPEC: {
+ i = LOAD_ATOM(spec, i, am_unspec);
+ i = LOAD_BUF2BINARY(spec, i, buf, 0);
+ spec[i++] = ERL_DRV_TUPLE;
+ spec[i++] = 2;
spec[i++] = ERL_DRV_INT;
spec[i++] = 0;
+ break;
+ }
+ default: { /* INET_AF_UNDEFINED */
+ i = LOAD_ATOM(spec, i, am_undefined);
+ i = LOAD_BUF2BINARY(spec, i, buf, 0);
spec[i++] = ERL_DRV_TUPLE;
spec[i++] = 2;
spec[i++] = ERL_DRV_INT;
@@ -3823,6 +3829,7 @@ static int inet_init()
INIT_ATOM(ok);
INIT_ATOM(undefined);
+ INIT_ATOM(unspec);
INIT_ATOM(tcp);
#ifdef HAVE_UDP
INIT_ATOM(udp);
@@ -4149,6 +4156,10 @@ static int inet_get_address(char* dst, inet_address* src, unsigned int* len)
return 0;
}
#endif
+ else if (family == AF_UNSPEC) {
+ dst[0] = INET_AF_UNSPEC;
+ *len = 1;
+ }
else {
dst[0] = INET_AF_UNDEFINED;
*len = 1;
@@ -4419,10 +4430,12 @@ static ErlDrvSSizeT inet_ctl_fdopen(inet_descriptor* desc, int domain, int type,
char** rbuf, ErlDrvSizeT rsize)
{
inet_address name;
- unsigned int sz = sizeof(name);
+ unsigned int sz;
if (bound) {
/* check that it is a socket and that the socket is bound */
+ sz = sizeof(name);
+ sys_memzero((char *) &name, sz);
if (IS_SOCKET_ERROR(sock_name(s, (struct sockaddr*) &name, &sz)))
return ctl_error(sock_errno(), rbuf, rsize);
if (name.sa.sa_family != domain)
@@ -4437,10 +4450,7 @@ static ErlDrvSSizeT inet_ctl_fdopen(inet_descriptor* desc, int domain, int type,
driver_select(desc->port, desc->event, ERL_DRV_READ, 1);
#endif
- if (bound)
- desc->state = INET_STATE_BOUND;
- else
- desc->state = INET_STATE_OPEN;
+ desc->state = INET_STATE_OPEN;
if (type == SOCK_STREAM) { /* check if connected */
sz = sizeof(name);
@@ -8448,7 +8458,6 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf,
if (len != 4) return ctl_error(EINVAL, rbuf, rsize);
if (! IS_OPEN(desc)) return ctl_xerror(EXBADPORT, rbuf, rsize);
- if (! IS_BOUND(desc)) return ctl_xerror(EXBADSEQ, rbuf, rsize);
#ifdef HAVE_SCTP
if (IS_SCTP(desc) && p_sctp_getpaddrs) {
@@ -8524,7 +8533,6 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf,
if (len != 4) return ctl_error(EINVAL, rbuf, rsize);
if (! IS_OPEN(desc)) return ctl_xerror(EXBADPORT, rbuf, rsize);
- if (! IS_BOUND(desc)) return ctl_xerror(EXBADSEQ, rbuf, rsize);
#ifdef HAVE_SCTP
if (IS_SCTP(desc) && p_sctp_getladdrs) {
@@ -8546,6 +8554,7 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf,
int i;
sz = sizeof(addr);
+ sys_memzero((char *) &addr, sz);
i = sock_name(desc->s, (struct sockaddr *) &addr, &sz);
return reply_inet_addrs(i >= 0 ? 1 : i, &addr, rbuf, rsize, sz);
}
@@ -8559,15 +8568,13 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf,
DEBUGF(("inet_ctl(%ld): NAME\r\n", (long)desc->port));
- if (!IS_BOUND(desc))
- return ctl_error(EINVAL, rbuf, rsize); /* address is not valid */
-
if ((ptr = desc->name_ptr) != NULL) {
sz = desc->name_addr_len;
}
else {
ptr = &name;
sz = sizeof(name);
+ sys_memzero((char *) &name, sz);
if (IS_SOCKET_ERROR
(sock_name(desc->s, (struct sockaddr*)ptr, &sz)))
return ctl_error(sock_errno(), rbuf, rsize);
@@ -8612,11 +8619,12 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf,
if (IS_SOCKET_ERROR(sock_bind(desc->s,(struct sockaddr*) &local, len)))
return ctl_error(sock_errno(), rbuf, rsize);
- desc->state = INET_STATE_BOUND;
+ desc->state = INET_STATE_OPEN;
port = inet_address_port(&local);
if (port == 0) {
SOCKLEN_T adrlen = sizeof(local);
+ sys_memzero((char *) &local, adrlen);
sock_name(desc->s, &local.sa, &adrlen);
port = inet_address_port(&local);
}
@@ -9136,8 +9144,6 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd,
return ctl_xerror(EXBADPORT, rbuf, rsize);
if (!IS_OPEN(INETP(desc)))
return ctl_xerror(EXBADPORT, rbuf, rsize);
- if (!IS_BOUND(INETP(desc)))
- return ctl_xerror(EXBADSEQ, rbuf, rsize);
if (len != 2)
return ctl_error(EINVAL, rbuf, rsize);
backlog = get_int16(buf);
@@ -9160,8 +9166,6 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd,
return ctl_xerror(EXBADPORT, rbuf, rsize);
if (IS_CONNECTED(INETP(desc)))
return ctl_error(EISCONN, rbuf, rsize);
- if (!IS_BOUND(INETP(desc)))
- return ctl_xerror(EXBADSEQ, rbuf, rsize);
if (IS_CONNECTING(INETP(desc)))
return ctl_error(EINVAL, rbuf, rsize);
if (len < 6)
@@ -9262,6 +9266,7 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd,
return ctl_reply(INET_REP_OK, tbuf, 2, rbuf, rsize);
} else {
n = sizeof(desc->inet.remote);
+ sys_memzero((char *) &remote, n);
s = sock_accept(desc->inet.s, (struct sockaddr*) &remote, &n);
if (s == INVALID_SOCKET) {
if (sock_errno() == ERRNO_BLOCK) {
@@ -10147,6 +10152,7 @@ static int tcp_inet_input(tcp_descriptor* desc, HANDLE event)
inet_async_op *this_op = desc->inet.opt;
len = sizeof(desc->inet.remote);
+ sys_memzero((char *) &remote, len);
s = sock_accept(desc->inet.s, (struct sockaddr*) &remote, &len);
if (s == INVALID_SOCKET && sock_errno() == ERRNO_BLOCK) {
/* Just try again, no real error, just a ghost trigger from poll,
@@ -10213,6 +10219,7 @@ static int tcp_inet_input(tcp_descriptor* desc, HANDLE event)
while (desc->inet.state == INET_STATE_MULTI_ACCEPTING) {
len = sizeof(desc->inet.remote);
+ sys_memzero((char *) &remote, len);
s = sock_accept(desc->inet.s, (struct sockaddr*) &remote, &len);
if (s == INVALID_SOCKET && sock_errno() == ERRNO_BLOCK) {
/* Just try again, no real error, keep the last return code */
@@ -10653,7 +10660,7 @@ static int tcp_inet_output(tcp_descriptor* desc, HANDLE event)
(struct sockaddr*) &desc->inet.remote, &sz);
if (IS_SOCKET_ERROR(code)) {
- desc->inet.state = INET_STATE_BOUND; /* restore state */
+ desc->inet.state = INET_STATE_OPEN; /* restore state */
ret = async_error(INETP(desc), sock_errno());
goto done;
}
@@ -10666,7 +10673,7 @@ static int tcp_inet_output(tcp_descriptor* desc, HANDLE event)
(void *)&error, &sz);
if ((code < 0) || error) {
- desc->inet.state = INET_STATE_BOUND; /* restore state */
+ desc->inet.state = INET_STATE_OPEN; /* restore state */
ret = async_error(INETP(desc), error);
goto done;
}
@@ -11075,8 +11082,6 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf,
if (!IS_OPEN(desc))
return ctl_xerror(EXBADPORT, rbuf, rsize);
- if (!IS_BOUND(desc))
- return ctl_xerror(EXBADSEQ, rbuf, rsize);
#ifdef HAVE_SCTP
if (IS_SCTP(desc)) {
inet_address remote;
@@ -11163,8 +11168,6 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf,
return ctl_xerror(EXBADPORT, rbuf, rsize);
if (!IS_OPEN(desc))
return ctl_xerror(EXBADPORT, rbuf, rsize);
- if (!IS_BOUND(desc))
- return ctl_xerror(EXBADSEQ, rbuf, rsize);
if (len != 2)
return ctl_error(EINVAL, rbuf, rsize);
@@ -11211,7 +11214,7 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf,
return ctl_error(sock_errno(), rbuf, rsize);
}
- desc->state = INET_STATE_BOUND;
+ desc->state = INET_STATE_OPEN;
return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize);
}
@@ -11228,8 +11231,6 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf,
return ctl_xerror(EXBADPORT, rbuf, rsize);
if (!IS_OPEN(desc))
return ctl_xerror(EXBADPORT, rbuf, rsize);
- if (!IS_BOUND(desc))
- return ctl_xerror(EXBADSEQ, rbuf, rsize);
if (! p_sctp_peeloff)
return ctl_error(ENOTSUP, rbuf, rsize);
@@ -11270,8 +11271,6 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf,
/* INPUT: Timeout(4), Length(4) */
if (!IS_OPEN(desc))
return ctl_xerror(EXBADPORT, rbuf, rsize);
- if (!IS_BOUND(desc))
- return ctl_error(EINVAL, rbuf, rsize);
if (desc->active || (len != 8))
return ctl_error(EINVAL, rbuf, rsize);
timeout = get_int32(buf);
@@ -11336,10 +11335,6 @@ static void packet_inet_command(ErlDrvData e, char* buf, ErlDrvSizeT len)
inet_reply_error(desc, EINVAL);
return;
}
- if (!IS_BOUND(desc)) {
- inet_reply_error(desc, EINVAL);
- return;
- }
#ifdef HAVE_SCTP
if (IS_SCTP(desc))
@@ -11470,6 +11465,8 @@ static int packet_inet_input(udp_descriptor* udesc, HANDLE event)
while(packet_count--) {
unsigned int len = sizeof(other);
+ sys_memzero((char *) &other, sizeof(other));
+
/* udesc->i_buf is only kept between SCTP fragments */
if (udesc->i_buf == NULL) {
udesc->i_bufsz = desc->bufsz + len;
@@ -11665,7 +11662,7 @@ static int packet_inet_output(udp_descriptor* udesc, HANDLE event)
(struct sockaddr*) &desc->remote, &sz);
if (IS_SOCKET_ERROR(code)) {
- desc->state = INET_STATE_BOUND; /* restore state */
+ desc->state = INET_STATE_OPEN; /* restore state */
ret = async_error(desc, sock_errno());
goto done;
}
@@ -11678,7 +11675,7 @@ static int packet_inet_output(udp_descriptor* udesc, HANDLE event)
(void *)&error, &sz);
if ((code < 0) || error) {
- desc->state = INET_STATE_BOUND; /* restore state */
+ desc->state = INET_STATE_OPEN; /* restore state */
ret = async_error(desc, error);
goto done;
}
diff --git a/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c b/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c
index e38bececde..d92933a096 100644
--- a/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c
+++ b/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c
@@ -48,7 +48,8 @@ static ERL_NIF_TERM dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[
char s[10];
ErlNifBinary b;
if (have_dirty_schedulers()) {
- assert(enif_is_on_dirty_scheduler(env));
+ assert(ERL_NIF_THR_DIRTY_CPU_SCHEDULER == enif_thread_type()
+ || ERL_NIF_THR_DIRTY_IO_SCHEDULER == enif_thread_type());
}
assert(argc == 3);
enif_get_int(env, argv[0], &n);
@@ -65,7 +66,7 @@ static ERL_NIF_TERM call_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM
int n;
char s[10];
ErlNifBinary b;
- assert(!enif_is_on_dirty_scheduler(env));
+ assert(ERL_NIF_THR_NORMAL_SCHEDULER == enif_thread_type());
if (argc != 3)
return enif_make_badarg(env);
if (have_dirty_schedulers()) {
@@ -151,7 +152,8 @@ dirty_sleeper(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
ErlNifPid pid;
ErlNifEnv* msg_env = NULL;
- assert(enif_is_on_dirty_scheduler(env));
+ assert(ERL_NIF_THR_DIRTY_CPU_SCHEDULER == enif_thread_type()
+ || ERL_NIF_THR_DIRTY_IO_SCHEDULER == enif_thread_type());
/* If we get a pid argument, it indicates a process involved in the
test wants a message from us. Prior to the sleep we send a 'ready'
@@ -221,7 +223,8 @@ static ERL_NIF_TERM dirty_heap_access_nif(ErlNifEnv* env, int argc, const ERL_NI
{
ERL_NIF_TERM res = enif_make_list(env, 0);
int i;
- assert(enif_is_on_dirty_scheduler(env));
+ assert(ERL_NIF_THR_DIRTY_CPU_SCHEDULER == enif_thread_type()
+ || ERL_NIF_THR_DIRTY_IO_SCHEDULER == enif_thread_type());
for (i = 0; i < 1000; i++)
res = enif_make_list_cell(env, enif_make_copy(env, argv[0]), res);
diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl
index eaa4026a8a..4ebc1f5782 100644
--- a/erts/emulator/test/process_SUITE.erl
+++ b/erts/emulator/test/process_SUITE.erl
@@ -147,11 +147,7 @@ spawn_with_binaries(Config) when is_list(Config) ->
TwoMeg = lists:duplicate(1024, L),
Fun = fun() -> spawn(?MODULE, binary_owner, [list_to_binary(TwoMeg)]),
receive after 1 -> ok end end,
- Iter = case test_server:purify_is_running() of
- true -> 10;
- false -> 150
- end,
- test_server:do_times(Iter, Fun),
+ test_server:do_times(150, Fun),
ok.
binary_owner(Bin) when is_binary(Bin) ->
diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam
index edb86d35a6..fdf8bbf4e2 100644
--- a/erts/preloaded/ebin/prim_inet.beam
+++ b/erts/preloaded/ebin/prim_inet.beam
Binary files differ
diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl
index 71dbfbd0a7..560810d222 100644
--- a/erts/preloaded/src/prim_inet.erl
+++ b/erts/preloaded/src/prim_inet.erl
@@ -1746,8 +1746,8 @@ enc_value_2(addr, {inet6,{IP,Port}}) ->
[?INET_AF_INET6,?int16(Port)|ip6_to_bytes(IP)];
enc_value_2(addr, {local,Addr}) ->
%% A binary is passed as is, but anything else will be
- %% regarded as a filename and therefore UTF-8 encoded
- %% if the system filename encoding flag so dictates.
+ %% regarded as a filename and therefore encoded according to
+ %% the current system filename encoding mode.
Bin =
if
is_binary(Addr) ->
diff --git a/lib/common_test/src/test_server.erl b/lib/common_test/src/test_server.erl
index 919526c5d7..bd677e971b 100644
--- a/lib/common_test/src/test_server.erl
+++ b/lib/common_test/src/test_server.erl
@@ -21,7 +21,7 @@
-define(DEFAULT_TIMETRAP_SECS, 60).
%%% TEST_SERVER_CTRL INTERFACE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
--export([run_test_case_apply/1,init_target_info/0,init_purify/0]).
+-export([run_test_case_apply/1,init_target_info/0]).
-export([cover_compile/1,cover_analyse/2]).
%%% TEST_SERVER_SUP INTERFACE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -49,10 +49,6 @@
-export([break/1,break/2,break/3,continue/0,continue/1]).
-%%% DEBUGGER INTERFACE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
--export([purify_new_leaks/0, purify_format/2, purify_new_fds_inuse/0,
- purify_is_running/0]).
-
%%% PRIVATE EXPORTED %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-export([]).
@@ -73,10 +69,6 @@ init_target_info() ->
username=test_server_sup:get_username(),
cookie=atom_to_list(erlang:get_cookie())}.
-init_purify() ->
- purify_new_leaks().
-
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% cover_compile(#cover{app=App,incl=Include,excl=Exclude,cross=Cross}) ->
%% {ok,#cover{mods=AnalyseModules}} | {error,Reason}
@@ -100,7 +92,7 @@ cover_compile(CoverInfo=#cover{app=none,incl=Include,cross=Cross}) ->
case length(CompileMods) of
0 ->
io:fwrite("WARNING: No modules to cover compile!\n\n",[]),
- cover:start(), % start cover server anyway
+ {ok, _} = start_cover(), % start cover server anyway
{ok,CoverInfo#cover{mods=[]}};
N ->
io:fwrite("Cover compiling ~w modules - "
@@ -115,7 +107,7 @@ cover_compile(CoverInfo=#cover{app=App,excl=all,incl=Include,cross=Cross}) ->
case length(CompileMods) of
0 ->
io:fwrite("WARNING: No modules to cover compile!\n\n",[]),
- cover:start(), % start cover server anyway
+ {ok, _} = start_cover(), % start cover server anyway
{ok,CoverInfo#cover{mods=[]}};
N ->
io:fwrite("Cover compiling '~w' (~w files) - "
@@ -158,7 +150,7 @@ cover_compile(CoverInfo=#cover{app=App,excl=Exclude,
case length(CompileMods) of
0 ->
io:fwrite("WARNING: No modules to cover compile!\n\n",[]),
- cover:start(), % start cover server anyway
+ {ok, _} = start_cover(), % start cover server anyway
{ok,CoverInfo#cover{mods=[]}};
N ->
io:fwrite("Cover compiling '~w' (~w files) - "
@@ -175,11 +167,11 @@ module_names(Beams) ->
do_cover_compile(Modules) ->
- cover:start(),
+ {ok, _} = start_cover(),
Sticky = prepare_cover_compile(Modules,[]),
R = cover:compile_beam(Modules),
- [warn_compile(Error) || Error <- R,element(1,Error)=/=ok],
- [code:stick_mod(M) || M <- Sticky],
+ _ = [warn_compile(Error) || Error <- R,element(1,Error)=/=ok],
+ _ = [code:stick_mod(M) || M <- Sticky],
ok.
warn_compile({error,{Reason,Module}}) ->
@@ -366,9 +358,7 @@ stick_all_sticky(Node,Sticky) ->
%% compensate timetraps for runtime delays introduced by e.g. tools like
%% cover.
-run_test_case_apply({CaseNum,Mod,Func,Args,Name,
- RunInit,TimetrapData}) ->
- purify_format("Test case #~w ~w:~w/1", [CaseNum, Mod, Func]),
+run_test_case_apply({Mod,Func,Args,Name,RunInit,TimetrapData}) ->
case os:getenv("TS_RUN_VALGRIND") of
false ->
ok;
@@ -380,7 +370,6 @@ run_test_case_apply({CaseNum,Mod,Func,Args,Name,
Result = run_test_case_apply(Mod, Func, Args, Name, RunInit,
TimetrapData),
ProcAft = erlang:system_info(process_count),
- purify_new_leaks(),
DetFail = get(test_server_detected_fail),
{Result,DetFail,ProcBef,ProcAft}.
@@ -585,7 +574,8 @@ run_test_case_msgloop(#st{ref=Ref,pid=Pid,end_conf_pid=EndConfPid0}=St0) ->
{user_timetrap,Pid,_TrapTime,StartTime,E={user_timetrap_error,_},_} ->
case update_user_timetraps(Pid, StartTime) of
proceed ->
- self() ! {abort_current_testcase,E,Pid};
+ self() ! {abort_current_testcase,E,Pid},
+ ok;
ignore ->
ok
end,
@@ -600,7 +590,8 @@ run_test_case_msgloop(#st{ref=Ref,pid=Pid,end_conf_pid=EndConfPid0}=St0) ->
true ->
TrapTime
end,
- timetrap(TrapTime, TotalTime, Pid, Scale);
+ _ = timetrap(TrapTime, TotalTime, Pid, Scale),
+ ok;
ignore ->
ok
end,
@@ -724,7 +715,7 @@ do_call_end_conf(Starter,Mod,Func,Data,TCExitReason,Conf,TVal) ->
Supervisor = self(),
EndConfApply =
fun() ->
- timetrap(TVal),
+ _ = timetrap(TVal),
%% We can't handle fails or skips here
%% (neither input nor output). The error can
%% be read from Conf though (tc_status).
@@ -775,7 +766,8 @@ print_end_conf_result(Mod,Func,Conf,Cause,Error) ->
" ~s!\n\tReason: ~ts\n",
[Mod,Func,Conf,Cause,ErrorStr])
end,
- group_leader() ! {printout,12,Str2Print}.
+ group_leader() ! {printout,12,Str2Print},
+ ok.
spawn_fw_call(Mod,IPTC={init_per_testcase,Func},CurrConf,Pid,
@@ -1287,7 +1279,9 @@ user_callback({CBMod,CBFunc}, Mod, Func, InitOrEnd, Args) ->
init_per_testcase(Mod, Func, Args) ->
case code:is_loaded(Mod) of
- false -> code:load_file(Mod);
+ false ->
+ _ = code:load_file(Mod),
+ ok;
_ -> ok
end,
case erlang:function_exported(Mod, init_per_testcase, 2) of
@@ -1355,7 +1349,8 @@ print_init_conf_result(Line,Cause,Reason) ->
"\tLocation: ~ts\n\tReason: ~ts\n",
[Cause,FormattedLoc,ReasonStr])
end,
- group_leader() ! {printout,12,Str2Print}.
+ group_leader() ! {printout,12,Str2Print},
+ ok.
end_per_testcase(Mod, Func, Conf) ->
@@ -1426,7 +1421,8 @@ print_end_tc_warning(EndFunc,Reason,Cause,Loc) ->
"Reason: ~ts\nLine: ~ts\n",
[EndFunc,Cause,ReasonStr,FormattedLoc])
end,
- group_leader() ! {printout,12,Str2Print}.
+ group_leader() ! {printout,12,Str2Print},
+ ok.
get_loc() ->
get(test_server_loc).
@@ -1829,7 +1825,6 @@ timetrap_scale_factor() ->
timetrap_scale_factor([
{ 2, fun() -> has_lock_checking() end},
{ 3, fun() -> has_superfluous_schedulers() end},
- { 5, fun() -> purify_is_running() end},
{ 6, fun() -> is_debug() end},
{10, fun() -> is_cover() end}
]).
@@ -2129,7 +2124,8 @@ timetrap_cancel_all(TCPid, SendToServer) ->
ok;
Timers ->
[timetrap_cancel_one(Handle, false) ||
- {Handle,Pid,_} <- Timers, Pid == TCPid]
+ {Handle,Pid,_} <- Timers, Pid == TCPid],
+ ok
end,
case get(test_server_user_timetrap) of
undefined ->
@@ -2139,13 +2135,15 @@ timetrap_cancel_all(TCPid, SendToServer) ->
{UserTTSup,_StartTime} ->
remove_user_timetrap(UserTTSup),
put(test_server_user_timetrap,
- proplists:delete(TCPid, UserTTs));
+ proplists:delete(TCPid, UserTTs)),
+ ok;
undefined ->
ok
end
end,
if SendToServer == true ->
- group_leader() ! {timetrap_cancel_all,TCPid,self()};
+ group_leader() ! {timetrap_cancel_all,TCPid,self()},
+ ok;
true ->
ok
end,
@@ -2560,10 +2558,11 @@ run_on_shielded_node(Fun, CArgs) when is_function(Fun), is_list(CArgs) ->
-spec start_job_proxy_fun(_, _) -> fun(() -> no_return()).
start_job_proxy_fun(Master, Fun) ->
fun () ->
- start_job_proxy(),
+ _ = start_job_proxy(),
receive
Ref ->
- Master ! {Ref, Fun()}
+ Master ! {Ref, Fun()},
+ ok
end,
receive after infinity -> infinity end
end.
@@ -2729,64 +2728,25 @@ is_commercial() ->
_ -> true
end.
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% DEBUGGER INTERFACE %%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% purify_is_running() -> false|true
-%%
-%% Tests if Purify is currently running.
-
-purify_is_running() ->
- case catch erlang:system_info({error_checker, running}) of
- {'EXIT', _} -> false;
- Res -> Res
- end.
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% purify_new_leaks() -> false|BytesLeaked
-%% BytesLeaked = integer()
-%%
-%% Checks for new memory leaks if Purify is active.
-%% Returns the number of bytes leaked, or false if Purify
-%% is not running.
-purify_new_leaks() ->
- case catch erlang:system_info({error_checker, memory}) of
- {'EXIT', _} -> false;
- Leaked when is_integer(Leaked) -> Leaked
- end.
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% purify_new_fds_inuse() -> false|FdsInuse
-%% FdsInuse = integer()
-%%
-%% Checks for new file descriptors in use.
-%% Returns the number of new file descriptors in use, or false
-%% if Purify is not running.
-purify_new_fds_inuse() ->
- case catch erlang:system_info({error_checker, fd}) of
- {'EXIT', _} -> false;
- Inuse when is_integer(Inuse) -> Inuse
- end.
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% purify_format(Format, Args) -> ok
-%% Format = string()
-%% Args = lists()
-%%
-%% Outputs the formatted string to Purify's logfile,if Purify is active.
-purify_format(Format, Args) ->
- (catch erlang:system_info({error_checker, io_lib:format(Format, Args)})),
- ok.
-
-
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% Apply given function and reply to caller or proxy.
%%
do_sync_apply(Proxy, From, {M,F,A}) ->
Result = apply(M, F, A),
- if is_pid(Proxy) -> Proxy ! {sync_result_proxy,From,Result};
- true -> From ! {sync_result,Result}
+ if is_pid(Proxy) ->
+ Proxy ! {sync_result_proxy,From,Result},
+ ok;
+ true ->
+ From ! {sync_result,Result},
+ ok
end.
+
+start_cover() ->
+ case cover:start() of
+ {error, {already_started, Pid}} ->
+ {ok, Pid};
+ Else ->
+ Else
+ end.
+
diff --git a/lib/common_test/src/test_server_ctrl.erl b/lib/common_test/src/test_server_ctrl.erl
index ff960c22a5..84e35e7371 100644
--- a/lib/common_test/src/test_server_ctrl.erl
+++ b/lib/common_test/src/test_server_ctrl.erl
@@ -512,7 +512,7 @@ init([]) ->
TI = TI0#target_info{host=TargetHost,
naming=naming(),
master=TargetHost},
- ets:new(slave_tab, [named_table,set,public,{keypos,2}]),
+ _ = ets:new(slave_tab, [named_table,set,public,{keypos,2}]),
set_hosts([TI#target_info.host]),
{ok,State#state{target_info=TI}}.
@@ -867,7 +867,7 @@ handle_call({create_priv_dir,Value}, _From, State) ->
handle_call({testcase_callback,ModFunc}, _From, State) ->
case ModFunc of
{Mod,Func} ->
- case code:is_loaded(Mod) of
+ _ = case code:is_loaded(Mod) of
{file,_} ->
ok;
false ->
@@ -1079,8 +1079,8 @@ terminate(_Reason, State) ->
false -> ok;
Sock -> test_server_node:stop_tracer_node(Sock)
end,
- kill_all_jobs(State#state.jobs),
- test_server_node:kill_nodes(),
+ ok = kill_all_jobs(State#state.jobs),
+ _ = test_server_node:kill_nodes(),
ok.
kill_all_jobs([{_Name,JobPid}|Jobs]) ->
@@ -1125,7 +1125,7 @@ spawn_tester(Mod, Func, Args, Dir, Name, Levels, RejectIoReqs,
init_tester(Mod, Func, Args, Dir, Name, {_,_,MinLev}=Levels,
RejectIoReqs, CreatePrivDir, TCCallback, ExtraTools) ->
process_flag(trap_exit, true),
- test_server_io:start_link(),
+ _ = test_server_io:start_link(),
put(test_server_name, Name),
put(test_server_dir, Dir),
put(test_server_total_time, 0),
@@ -1199,8 +1199,7 @@ init_tester(Mod, Func, Args, Dir, Name, {_,_,MinLev}=Levels,
{UnexpectedIoName,UnexpectedIoFooter} = get(test_server_unexpected_footer),
{ok,UnexpectedIoFd} = open_html_file(UnexpectedIoName, [append]),
io:put_chars(UnexpectedIoFd, "\n</pre>\n"++UnexpectedIoFooter),
- file:close(UnexpectedIoFd),
- ok.
+ ok = file:close(UnexpectedIoFd).
report_severe_error(Reason) ->
test_server_sup:framework_call(report, [severe_error,Reason]).
@@ -1927,7 +1926,7 @@ html_convert_modules([Mod|Mods]) ->
Name = atom_to_list(Mod),
DestFile = filename:join(DestDir,
downcase(Name)++?src_listing_ext),
- html_possibly_convert(SrcFile1, SrcFileInfo, DestFile),
+ _ = html_possibly_convert(SrcFile1, SrcFileInfo, DestFile),
html_convert_modules(Mods)
end;
_Other ->
@@ -2066,7 +2065,7 @@ add_init_and_end_per_suite([], LastMod, LastRef, FwMod) ->
end.
do_add_init_and_end_per_suite(LastMod, LastRef, Mod, FwMod) ->
- case code:is_loaded(Mod) of
+ _ = case code:is_loaded(Mod) of
false -> code:load_file(Mod);
_ -> ok
end,
@@ -2140,7 +2139,6 @@ do_add_end_per_suite_and_skip(LastMod, LastRef, Mod, FwMod) ->
%% Runs the specified tests, then displays/logs the summary.
run_test_cases(TestSpec, Config, TimetrapData) ->
- test_server:init_purify(),
case lists:member(no_src, get(test_server_logopts)) of
true ->
ok;
@@ -2323,7 +2321,7 @@ run_test_cases_loop([{SkipTag,{Type,Ref,Case,Comment},SkipMode}|Cases],
Config, TimetrapData, Mode, Status) when
((SkipTag==auto_skip_case) or (SkipTag==skip_case)) and
((Type==conf) or (Type==make)) ->
- file:set_cwd(filename:dirname(get(test_server_dir))),
+ ok = file:set_cwd(filename:dirname(get(test_server_dir))),
CurrIOHandler = get(test_server_common_io_handler),
ParentMode = tl(Mode),
@@ -2339,7 +2337,7 @@ run_test_cases_loop([{SkipTag,{Type,Ref,Case,Comment},SkipMode}|Cases],
false ->
%% this is a skipped end conf for a top level parallel
%% group, buffered io can be flushed
- handle_test_case_io_and_status(),
+ _ = handle_test_case_io_and_status(),
set_io_buffering(undefined),
{Mod,Func} = skip_case(AutoOrUser, Ref, 0, Case, Comment,
false, SkipMode),
@@ -2351,7 +2349,7 @@ run_test_cases_loop([{SkipTag,{Type,Ref,Case,Comment},SkipMode}|Cases],
_ ->
%% this is a skipped end conf for a parallel group nested
%% under a parallel group (io buffering is active)
- wait_for_cases(Ref),
+ _ = wait_for_cases(Ref),
{Mod,Func} = skip_case(AutoOrUser, Ref, 0, Case, Comment,
true, SkipMode),
ConfData = {Mod,{Func,get_name(SkipMode)},Comment},
@@ -2459,7 +2457,7 @@ run_test_cases_loop([{auto_skip_case,{Case,Comment},SkipMode}|Cases],
run_test_cases_loop([{skip_case,{{Mod,all}=Case,Comment},SkipMode}|Cases],
Config, TimetrapData, Mode, Status) ->
- skip_case(user, undefined, 0, Case, Comment, false, SkipMode),
+ _ = skip_case(user, undefined, 0, Case, Comment, false, SkipMode),
test_server_sup:framework_call(report, [tc_user_skip,
{Mod,{all,get_name(SkipMode)},
Comment}]),
@@ -2489,7 +2487,7 @@ run_test_cases_loop([{conf,Ref,Props,{Mod,Func}}|_Cases]=Cs0,
%% collect results from the test case processes
%% and calc total time
OkSkipFail = handle_test_case_io_and_status(),
- file:set_cwd(filename:dirname(get(test_server_dir))),
+ ok = file:set_cwd(filename:dirname(get(test_server_dir))),
After = ?now,
Before = get(test_server_parallel_start_time),
Elapsed = timer:now_diff(After, Before)/1000000,
@@ -3582,7 +3580,7 @@ handle_io_and_exit_loop([], [{undefined,CurrPid,CaseNum,Mod,Func}|Ps] = Cases, O
handle_io_and_exit_loop(Refs, [{Ref,CurrPid,CaseNum,Mod,Func}|Ps] = Cases, Ok,Skip,Fail) ->
receive
{started,_,CurrPid,CaseNum,Mod,Func} ->
- handle_io_and_exits(self(), CurrPid, CaseNum, Mod, Func, Cases),
+ _ = handle_io_and_exits(self(), CurrPid, CaseNum, Mod, Func, Cases),
Refs1 =
case Refs of
[Ref|Rs] -> % must be end conf case for subgroup
@@ -3658,7 +3656,7 @@ handle_io_and_exits(Main, CurrPid, CaseNum, Mod, Func, Cases) ->
%% about the execution time and the return value of the test case function.
run_test_case(Ref, Num, Mod, Func, Args, RunInit, TimetrapData) ->
- file:set_cwd(filename:dirname(get(test_server_dir))),
+ ok = file:set_cwd(filename:dirname(get(test_server_dir))),
run_test_case1(Ref, Num, Mod, Func, Args, RunInit,
TimetrapData, [], self()).
@@ -3668,7 +3666,7 @@ run_test_case(Ref, Num, Mod, Func, Args, skip_init, TimetrapData, Mode) ->
TimetrapData, Mode, self());
run_test_case(Ref, Num, Mod, Func, Args, RunInit, TimetrapData, Mode) ->
- file:set_cwd(filename:dirname(get(test_server_dir))),
+ ok = file:set_cwd(filename:dirname(get(test_server_dir))),
Main = self(),
case check_prop(parallel, Mode) of
false ->
@@ -3682,7 +3680,7 @@ run_test_case(Ref, Num, Mod, Func, Args, RunInit, TimetrapData, Mode) ->
spawn_link(
fun() ->
process_flag(trap_exit, true),
- [put(Key, Val) || {Key,Val} <- Dictionary],
+ _ = [put(Key, Val) || {Key,Val} <- Dictionary],
set_io_buffering({tc,Main}),
run_test_case1(Ref, Num, Mod, Func, Args, RunInit,
TimetrapData, Mode, Main)
@@ -3699,7 +3697,8 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit,
false -> ok;
true ->
test_server_io:start_transaction(),
- Main ! {started,Ref,self(),Num,Mod,Func}
+ Main ! {started,Ref,self(),Num,Mod,Func},
+ ok
end,
TSDir = get(test_server_dir),
@@ -3774,7 +3773,7 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit,
%% run the test case
{Result,DetectedFail,ProcsBefore,ProcsAfter} =
- run_test_case_apply(Num, Mod, Func, [UpdatedArgs], GrName,
+ run_test_case_apply(Mod, Func, [UpdatedArgs], GrName,
RunInit, TimetrapData),
{Time,RetVal,Loc,Opts,Comment} =
case Result of
@@ -3906,7 +3905,8 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit,
true ->
test_server_io:end_transaction(),
Main ! {finished,Ref,self(),Num,Mod,Func,
- ?mod_result(Status),{Time,RetVal,Opts}}
+ ?mod_result(Status),{Time,RetVal,Opts}},
+ ok
end,
{Time,RetVal,Opts}.
@@ -4329,7 +4329,7 @@ do_format_exception(Reason={Error,Stack}) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% run_test_case_apply(CaseNum, Mod, Func, Args, Name, RunInit,
+%% run_test_case_apply(Mod, Func, Args, Name, RunInit,
%% TimetrapData) ->
%% {{Time,RetVal,Loc,Opts,Comment},DetectedFail,ProcessesBefore,ProcessesAfter} |
%% {{died,Reason,unknown,Comment},DetectedFail,ProcessesBefore,ProcessesAfter}
@@ -4343,9 +4343,9 @@ do_format_exception(Reason={Error,Stack}) ->
%% ProcessesBefore = ProcessesAfter = integer()
%%
-run_test_case_apply(CaseNum, Mod, Func, Args, Name, RunInit,
+run_test_case_apply(Mod, Func, Args, Name, RunInit,
TimetrapData) ->
- test_server:run_test_case_apply({CaseNum,Mod,Func,Args,Name,RunInit,
+ test_server:run_test_case_apply({Mod,Func,Args,Name,RunInit,
TimetrapData}).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -5276,7 +5276,8 @@ check_cross([]) ->
%% This per application analysis writes the file cover.html in the
%% application's run.<timestamp> directory.
stop_cover(#cover{}=CoverInfo, TestDir) ->
- cover_analyse(CoverInfo, TestDir);
+ cover_analyse(CoverInfo, TestDir),
+ ok;
stop_cover(_CoverInfo, _TestDir) ->
%% Cover is probably controlled by the framework
ok.
@@ -5315,7 +5316,7 @@ cover_analyse(CoverInfo, TestDir) ->
[?cross_coverlog_name]),
io:fwrite(CoverLog, "<p>CoverFile: <code>~tp</code>\n", [CoverFile]),
- write_cross_cover_info(TestDir,Cross),
+ ok = write_cross_cover_info(TestDir,Cross),
case length(cover:imported_modules()) of
Imps when Imps > 0 ->
@@ -5329,7 +5330,7 @@ cover_analyse(CoverInfo, TestDir) ->
io:fwrite(CoverLog, "<p>Excluded module(s): <code>~tp</code>\n", [Excluded]),
Coverage = test_server:cover_analyse(TestDir, CoverInfo),
- write_binary_file(filename:join(TestDir,?raw_coverlog_name),
+ ok = write_binary_file(filename:join(TestDir,?raw_coverlog_name),
term_to_binary(Coverage)),
case lists:filter(fun({_M,{_,_,_}}) -> false;
@@ -5344,8 +5345,8 @@ cover_analyse(CoverInfo, TestDir) ->
end,
TotPercent = write_cover_result_table(CoverLog, Coverage),
- write_binary_file(filename:join(TestDir, ?cover_total),
- term_to_binary(TotPercent)).
+ ok = write_binary_file(filename:join(TestDir, ?cover_total),
+ term_to_binary(TotPercent)).
%% Cover analysis - accumulated over multiple tests
%% This can be executed on any node after all tests are finished.
@@ -5395,7 +5396,7 @@ write_cross_cover_info(Dir,Cross) ->
write_cross_cover_logs([{Tag,Coverage}|T],TagDirMods) ->
case lists:keyfind(Tag,1,TagDirMods) of
{_,Dir,Mods} when Mods=/=[] ->
- write_binary_file(filename:join(Dir,?raw_cross_coverlog_name),
+ ok = write_binary_file(filename:join(Dir,?raw_cross_coverlog_name),
term_to_binary(Coverage)),
CoverLogName = filename:join(Dir,?cross_coverlog_name),
{ok,CoverLog} = open_html_file(CoverLogName),
diff --git a/lib/common_test/src/test_server_gl.erl b/lib/common_test/src/test_server_gl.erl
index 333c8fc06e..7d6fe64b92 100644
--- a/lib/common_test/src/test_server_gl.erl
+++ b/lib/common_test/src/test_server_gl.erl
@@ -185,7 +185,7 @@ handle_info({capture,Cap0}, St) ->
end,
{noreply,St#st{capture=Cap}};
handle_info({io_request,From,ReplyAs,Req}=IoReq, St) ->
- try io_req(Req, From, St) of
+ _ = try io_req(Req, From, St) of
passthrough ->
group_leader() ! IoReq;
{EscapeHtml,Data} ->
@@ -197,7 +197,8 @@ handle_info({io_request,From,ReplyAs,Req}=IoReq, St) ->
#st{capture=none} ->
ok;
#st{capture=CapturePid} ->
- CapturePid ! {captured,Data}
+ CapturePid ! {captured,Data},
+ ok
end,
case EscapeHtml andalso St#st.escape_chars of
true ->
diff --git a/lib/common_test/src/test_server_io.erl b/lib/common_test/src/test_server_io.erl
index 8c5c0aef35..3d5238052b 100644
--- a/lib/common_test/src/test_server_io.erl
+++ b/lib/common_test/src/test_server_io.erl
@@ -215,7 +215,7 @@ handle_call({set_fd,Tag,Fd}, _From, #st{fds=Fds0,tags=Tags0,
true ->
%% Fd ready, print anything buffered for associated Tag
lists:filtermap(fun({T,From,Str}) when T == Tag ->
- output(From, Tag, Str, St1),
+ _ = output(From, Tag, Str, St1),
false;
(_) ->
true
@@ -274,14 +274,15 @@ handle_call(reset_state, _From, #st{fds=Fds,tags=Tags,gls=Gls,
end
end, Tags),
GlList = gb_sets:to_list(Gls),
- [test_server_gl:stop(GL) || GL <- GlList],
+ _ = [test_server_gl:stop(GL) || GL <- GlList],
timer:sleep(100),
case lists:filter(fun(GlPid) -> is_process_alive(GlPid) end, GlList) of
[] ->
ok;
_ ->
timer:sleep(2000),
- [exit(GL, kill) || GL <- GlList]
+ [exit(GL, kill) || GL <- GlList],
+ ok
end,
Empty = gb_trees:empty(),
{ok,Shared} = test_server_gl:start_link(),
@@ -304,7 +305,7 @@ handle_call({stop,FdTags}, From, #st{fds=Fds0,tags=Tags0,
none ->
{Fds,Tags};
{value,Fd} ->
- file:close(Fd),
+ _ = file:close(Fd),
{gb_trees:delete(Tag, Fds),
lists:delete(Tag, Tags)}
end
@@ -333,7 +334,7 @@ handle_info({'EXIT',_Pid,Reason}, _St) ->
handle_info(stop_group_leaders, #st{gls=Gls}=St) ->
%% Stop the remaining group leaders.
GlPids = gb_sets:to_list(Gls),
- [test_server_gl:stop(GL) || GL <- GlPids],
+ _ = [test_server_gl:stop(GL) || GL <- GlPids],
timer:sleep(100),
Wait =
case lists:filter(fun(GlPid) -> is_process_alive(GlPid) end, GlPids) of
@@ -344,7 +345,7 @@ handle_info(stop_group_leaders, #st{gls=Gls}=St) ->
{noreply,St};
handle_info(kill_group_leaders, #st{gls=Gls,stopping=From,
pending_ops=Ops}=St) ->
- [exit(GL, kill) || GL <- gb_sets:to_list(Gls)],
+ _ = [exit(GL, kill) || GL <- gb_sets:to_list(Gls)],
if From /= undefined ->
gen_server:reply(From, ok);
true -> % reply has been sent already
@@ -434,7 +435,7 @@ do_print_buffered(Q0, St) ->
eot ->
Q;
{Tag,Str} ->
- do_output(Tag, Str, undefined, St),
+ _ = do_output(Tag, Str, undefined, St),
do_print_buffered(Q, St)
end.
@@ -448,5 +449,5 @@ gc(#st{gls=Gls0}) ->
InUse = ordsets:from_list(InUse0),
Gls = gb_sets:to_list(Gls0),
NotUsed = ordsets:subtract(Gls, InUse),
- [test_server_gl:stop(Pid) || Pid <- NotUsed],
+ _ = [test_server_gl:stop(Pid) || Pid <- NotUsed],
ok.
diff --git a/lib/common_test/src/test_server_node.erl b/lib/common_test/src/test_server_node.erl
index c64399e485..0b406c54cc 100644
--- a/lib/common_test/src/test_server_node.erl
+++ b/lib/common_test/src/test_server_node.erl
@@ -198,9 +198,9 @@ trc_loop(Sock,Patterns,Type) ->
gen_tcp:close(Sock)
end.
add_nodes(Nodes,Patterns,_Type) ->
- ttb:tracer(Nodes,[{file,{local, test_server}},
- {handler, {{?MODULE,handle_debug},initial}}]),
- ttb:p(all,[call,timestamp]),
+ {ok, _} = ttb:tracer(Nodes,[{file,{local, test_server}},
+ {handler, {{?MODULE,handle_debug},initial}}]),
+ {ok, _} = ttb:p(all,[call,timestamp]),
lists:foreach(fun({TP,M,F,A,Pat}) -> ttb:TP(M,F,A,Pat);
({CTP,M,F,A}) -> ttb:CTP(M,F,A)
end,
@@ -360,8 +360,8 @@ start_node_peer(SlaveName, OptList, From, TI) ->
-spec wait_for_node_started_fun(_, _, _, _, _) -> fun(() -> no_return()).
wait_for_node_started_fun(LSock, Tmo, Cleanup, TI, Self) ->
fun() ->
- wait_for_node_started(LSock,Tmo,undefined,
- Cleanup,TI,Self),
+ {{ok, _}, _} = wait_for_node_started(LSock,Tmo,undefined,
+ Cleanup,TI,Self),
receive after infinity -> ok end
end.
@@ -432,7 +432,7 @@ wait_for_node_started(LSock,Timeout,Client,Cleanup,TI,CtrlPid) ->
client=Client});
false -> ok
end,
- gen_tcp:controlling_process(Sock,CtrlPid),
+ ok = gen_tcp:controlling_process(Sock,CtrlPid),
test_server_ctrl:node_started(Nodename),
{{ok,Nodename},W}
end;
diff --git a/lib/common_test/src/test_server_sup.erl b/lib/common_test/src/test_server_sup.erl
index fa2bb33c2d..6922e01fcc 100644
--- a/lib/common_test/src/test_server_sup.erl
+++ b/lib/common_test/src/test_server_sup.erl
@@ -755,7 +755,7 @@ framework_call(FW,_Func,_Args,DefaultReturn)
DefaultReturn;
framework_call(Callback,Func,Args,DefaultReturn) ->
Mod = list_to_atom(Callback),
- case code:is_loaded(Mod) of
+ _ = case code:is_loaded(Mod) of
false -> code:load_file(Mod);
_ -> ok
end,
@@ -851,7 +851,8 @@ util_start() ->
spawn_link(fun() ->
register(?MODULE, self()),
util_loop(#util_state{starter=Starter})
- end);
+ end),
+ ok;
_Pid ->
ok
end.
diff --git a/lib/common_test/src/vts.erl b/lib/common_test/src/vts.erl
index e1c16fbda4..f1c5051164 100644
--- a/lib/common_test/src/vts.erl
+++ b/lib/common_test/src/vts.erl
@@ -64,7 +64,7 @@
%%%-----------------------------------------------------------------
%%% User API
start() ->
- ct_webtool:start(),
+ {ok, _} = ct_webtool:start(),
ct_webtool:start_tools([],"app=vts").
init_data(ConfigFiles,EvHandlers,LogDir,LogOpts,Tests) ->
@@ -169,7 +169,7 @@ loop(State) ->
NewState = State#state{config=Config,event_handler=EvHandlers,
current_log_dir=LogDir,
logopts=LogOpts,tests=Tests},
- ct_install(NewState),
+ _ = ct_install(NewState),
return(From,ok),
loop(NewState);
{start_page,From} ->
@@ -192,12 +192,12 @@ loop(State) ->
loop(State);
{{add_config_file,Input},From} ->
{Return,State1} = add_config_file1(Input,State),
- ct_install(State1),
+ _ = ct_install(State1),
return(From,Return),
loop(State1);
{{remove_config_file,Input},From} ->
{Return,State1} = remove_config_file1(Input,State),
- ct_install(State1),
+ _ = ct_install(State1),
return(From,Return),
loop(State1);
{run_frame,From} ->
@@ -233,7 +233,7 @@ loop(State) ->
return(From,result_summary_frame1(State)),
loop(State);
stop_reload_results ->
- file:set_cwd(State#state.start_dir),
+ ok = file:set_cwd(State#state.start_dir),
loop(State#state{reload_results=false});
{no_result_log_frame,From} ->
return(From,no_result_log_frame1()),
@@ -277,8 +277,8 @@ call(Msg) ->
end.
return({To,Ref},Result) ->
- To ! {Ref, Result}.
-
+ To ! {Ref, Result},
+ ok.
run_test1(State=#state{tests=Tests,current_log_dir=LogDir,
logopts=LogOpts}) ->
@@ -311,7 +311,6 @@ run_test1(State=#state{tests=Tests,current_log_dir=LogDir,
ct_install(#state{config=Config,event_handler=EvHandlers,
current_log_dir=LogDir}) ->
ct_run:install([{config,Config},{event_handler,EvHandlers}],LogDir).
-
%%%-----------------------------------------------------------------
%%% HTML
start_page1() ->
@@ -549,7 +548,7 @@ case_select(Dir,Suite,Case,N) ->
end,
case MakeResult of
ok ->
- code:add_pathz(Dir),
+ true = code:add_pathz(Dir),
case catch apply(Suite,all,[]) of
{'EXIT',Reason} ->
io:format("\n~p\n",[Reason]),
@@ -755,7 +754,7 @@ report1(tests_start,{TestName,_N},State) ->
end,
State#state{testruns=TestRuns};
report1(tests_done,{_Ok,_Fail,_Skip},State) ->
- timer:send_after(5000, self(),stop_reload_results),
+ {ok, _} = timer:send_after(5000, self(),stop_reload_results),
State#state{running=State#state.running-1,reload_results=true};
report1(tc_start,{_Suite,_Case},State) ->
State;
diff --git a/lib/common_test/test/Makefile b/lib/common_test/test/Makefile
index 1532b6c1f7..b1eddfedd7 100644
--- a/lib/common_test/test/Makefile
+++ b/lib/common_test/test/Makefile
@@ -69,7 +69,8 @@ MODULES= \
erl2html2_SUITE \
test_server_SUITE \
test_server_test_lib \
- ct_release_test_SUITE
+ ct_release_test_SUITE \
+ ct_log_SUITE
ERL_FILES= $(MODULES:%=%.erl)
HRL_FILES= test_server_test_lib.hrl
diff --git a/lib/common_test/test/ct_log_SUITE.erl b/lib/common_test/test/ct_log_SUITE.erl
new file mode 100644
index 0000000000..9bdd44cbdf
--- /dev/null
+++ b/lib/common_test/test/ct_log_SUITE.erl
@@ -0,0 +1,328 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-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%
+%%
+
+%%%-------------------------------------------------------------------
+%%% File: ct_log_SUITE
+%%%
+%%% Description: Test that ct:log, ct:pal and io:format print to
+%%% the test case log file as expected, with or without special HTML
+%%% characters being escaped.
+%%%
+%%%-------------------------------------------------------------------
+-module(ct_log_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+-define(eh, ct_test_support_eh).
+
+%%--------------------------------------------------------------------
+%% TEST SERVER CALLBACK FUNCTIONS
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_testcase(_TestCase, Config) ->
+ Config.
+
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [{group,print_and_verify}].
+
+groups() ->
+ [{print_and_verify,[sequence],[print,verify]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+%%--------------------------------------------------------------------
+
+%%%-----------------------------------------------------------------
+%%%
+print(Config) ->
+ TcLogFile = proplists:get_value(tc_logfile, Config),
+ Pid = self(),
+ String = atom_to_list(?MODULE),
+
+ %% START mark
+ io:format("LOGGING START~n"),
+
+ %% io:format
+ io:format("1. Printing nothing~n", []),
+ io:format("2. Printing a string: ~s~n", [String]),
+ io:format("3. Printing a string: ~p~n", [String]),
+ io:format("4. Printing a tuple: ~w~n", [{module,?MODULE}]),
+ io:format("5. Printing a pid: ~w~n", [Pid]),
+ io:format("6. Printing HTML: <pre>~s</pre>~n", [String]),
+
+ %% --- API ---
+ %% pal(Format) ->
+ %% = ct:pal(default, 50, Format, []).
+ %% pal(X1, X2) -> ok
+ %% X1 = Category | Importance | Format
+ %% X2 = Format | FormatArgs
+ %% pal(X1, X2, X3) -> ok
+ %% X1 = Category | Importance
+ %% X2 = Importance | Format
+ %% X3 = Format | FormatArgs
+ %% pal(Category, Importance, Format, FormatArgs) -> ok
+ %% ------
+ ct:pal("1. Printing nothing"),
+ ct:pal("2. Printing nothing", []),
+ ct:pal("3. Printing a string: ~s", [String]),
+ ct:pal("4. Printing a string: ~p", [String]),
+ ct:pal("5. Printing a tuple: ~w", [{module,?MODULE}]),
+ ct:pal("6. Printing a pid: ~w", [Pid]),
+ ct:pal("7. Printing HTML: <pre>~s</pre>", [String]),
+ ct:pal(ct_internal, "8. Printing with category"),
+ ct:pal(ct_internal, "9. Printing with ~s", ["category"]),
+ ct:pal(50, "10. Printing with importance"),
+ ct:pal(50, "11. Printing with ~s", ["importance"]),
+ ct:pal(ct_internal, 50, "12. Printing with ~s", ["category and importance"]),
+
+ %% --- API ---
+ %% log(Format) -> ok
+ %% = ct:log(default, 50, Format, [], []).
+ %% log(X1, X2) -> ok
+ %% X1 = Category | Importance | Format
+ %% X2 = Format | FormatArgs
+ %% log(X1, X2, X3) -> ok
+ %% X1 = Category | Importance
+ %% X2 = Importance | Format
+ %% X3 = Format | FormatArgs | Opts
+ %% log(X1, X2, X3, X4) -> ok
+ %% X1 = Category | Importance
+ %% X2 = Importance | Format
+ %% X3 = Format | FormatArgs
+ %% X4 = FormatArgs | Opts
+ %% log(Category, Importance, Format, FormatArgs, Opts) -> ok
+ %% ------
+ ct:log("1. Printing nothing"),
+ ct:log("2. Printing nothing", []),
+ ct:log("3. Printing a string: ~s", [String]),
+ ct:log("4. Printing a string: ~p", [String]),
+ ct:log("5. Printing a tuple: ~w", [{module,?MODULE}]),
+ ct:log("6. Printing a pid: ~w", [Pid]),
+ ct:log("7. Printing HTML: <pre>~s</pre>", [String]),
+ ct:log("8. Printing a pid escaped: ~w", [Pid], [esc_chars]),
+ ct:log("9. Printing a string escaped: ~p", [String], [esc_chars]),
+ ct:log("10. Printing HTML escaped: <pre>~s</pre>", [String], [esc_chars]),
+ ct:log("11. Printing a string, no css: ~s", [String], [no_css]),
+ ct:log("12. Printing a pid escaped, no css: ~w", [Pid],
+ [esc_chars, no_css]),
+ ct:log(ct_internal, "13. Printing with category"),
+ ct:log(ct_internal, "14. Printing with ~s", ["category"]),
+ ct:log(ct_internal, "15. Printing with ~s, no_css", ["category"],
+ [no_css]),
+ ct:log(50, "16. Printing with importance"),
+ ct:log(50, "17. Printing with ~s", ["importance"]),
+ ct:log(50, "18. Printing with ~s, no_css", ["importance"], [no_css]),
+ ct:log(ct_internal, 50, "19. Printing with category and importance"),
+ ct:log(ct_internal, 50, "20. Printing with ~s", ["category and importance"]),
+ ct:log(ct_internal, 50, "21. Printing a pid escaped with ~s, no_css: ~w",
+ ["category and importance",Pid], [esc_chars,no_css]),
+
+ %% END mark
+ ct:log("LOGGING END", [], [no_css]),
+ {save_config,[{the_logfile,TcLogFile},{the_pid,Pid},{the_string,String}]}.
+
+
+verify(Config) ->
+ {print,SavedCfg} = proplists:get_value(saved_config, Config),
+ TcLogFile = proplists:get_value(the_logfile, SavedCfg),
+ Pid = proplists:get_value(the_pid, SavedCfg),
+ StrPid = lists:flatten(io_lib:format("~p",[Pid])),
+ EscPid = "&lt;" ++ string:substr(StrPid, 2, length(StrPid)-2) ++ "&gt;",
+ String = proplists:get_value(the_string, SavedCfg),
+ ct:log("Read from prev testcase: ~p & ~p", [TcLogFile,Pid]),
+ {ok,Dev} = file:open(TcLogFile, [read]),
+ ok = read_until(Dev, "LOGGING START\n"),
+
+ %% io:format
+ match_line(Dev, "1. Printing nothing", []),
+ read_nl(Dev),
+ match_line(Dev, "2. Printing a string: ~s", [String]),
+ read_nl(Dev),
+ match_line(Dev, "3. Printing a string: ~p", [String]),
+ read_nl(Dev),
+ match_line(Dev, "4. Printing a tuple: ~w", [{module,?MODULE}]),
+ read_nl(Dev),
+ match_line(Dev, "5. Printing a pid: ~s", [EscPid]),
+ read_nl(Dev),
+ match_line(Dev, "6. Printing HTML: &lt;pre&gt;~s&lt;/pre&gt;", [String]),
+ read_nl(Dev),
+ %% ct:pal
+ read_header(Dev),
+ match_line(Dev, "1. Printing nothing", []),
+ read_footer(Dev),
+ read_header(Dev),
+ match_line(Dev, "2. Printing nothing", []),
+ read_footer(Dev),
+ read_header(Dev),
+ match_line(Dev, "3. Printing a string: ~s", [String]),
+ read_footer(Dev),
+ read_header(Dev),
+ match_line(Dev, "4. Printing a string: ~p", [String]),
+ read_footer(Dev),
+ read_header(Dev),
+ match_line(Dev, "5. Printing a tuple: ~w", [{module,?MODULE}]),
+ read_footer(Dev),
+ read_header(Dev),
+ match_line(Dev, "6. Printing a pid: ~s", [EscPid]),
+ read_footer(Dev),
+ read_header(Dev),
+ match_line(Dev, "7. Printing HTML: &lt;pre&gt;~s&lt;/pre&gt;", [String]),
+ read_footer(Dev),
+ read_header(Dev, "\"ct_internal\""),
+ match_line(Dev, "8. Printing with category", []),
+ read_footer(Dev),
+ read_header(Dev, "\"ct_internal\""),
+ match_line(Dev, "9. Printing with ~s", ["category"]),
+ read_footer(Dev),
+ read_header(Dev),
+ match_line(Dev, "10. Printing with importance", []),
+ read_footer(Dev),
+ read_header(Dev),
+ match_line(Dev, "11. Printing with ~s", ["importance"]),
+ read_footer(Dev),
+ read_header(Dev, "\"ct_internal\""),
+ match_line(Dev, "12. Printing with ~s", ["category and importance"]),
+ read_footer(Dev),
+ %% ct:log
+ read_header(Dev),
+ match_line(Dev, "1. Printing nothing", []),
+ read_footer(Dev),
+ read_header(Dev),
+ match_line(Dev, "2. Printing nothing", []),
+ read_footer(Dev),
+ read_header(Dev),
+ match_line(Dev, "3. Printing a string: ~s", [String]),
+ read_footer(Dev),
+ read_header(Dev),
+ match_line(Dev, "4. Printing a string: ~p", [String]),
+ read_footer(Dev),
+ read_header(Dev),
+ match_line(Dev, "5. Printing a tuple: ~w", [{module,?MODULE}]),
+ read_footer(Dev),
+ read_header(Dev),
+ match_line(Dev, "6. Printing a pid: ~w", [Pid]),
+ read_footer(Dev),
+ read_header(Dev),
+ match_line(Dev, "7. Printing HTML: <pre>~s</pre>", [String]),
+ read_footer(Dev),
+ read_header(Dev),
+ match_line(Dev, "8. Printing a pid escaped: ~s", [EscPid]),
+ read_footer(Dev),
+ read_header(Dev),
+ match_line(Dev, "9. Printing a string escaped: ~p", [String]),
+ read_footer(Dev),
+ read_header(Dev),
+ match_line(Dev, "10. Printing HTML escaped: &lt;pre&gt;~s&lt;/pre&gt;",
+ [String]),
+ read_footer(Dev),
+ match_line(Dev, "11. Printing a string, no css: ~s", [String]),
+ match_line(Dev, "12. Printing a pid escaped, no css: ~s", [EscPid]),
+ read_header(Dev, "\"ct_internal\""),
+ match_line(Dev, "13. Printing with category", []),
+ read_footer(Dev),
+ read_header(Dev, "\"ct_internal\""),
+ match_line(Dev, "14. Printing with ~s", ["category"]),
+ read_footer(Dev),
+ match_line(Dev, "15. Printing with ~s, no_css", ["category"]),
+ read_header(Dev),
+ match_line(Dev, "16. Printing with importance", []),
+ read_footer(Dev),
+ read_header(Dev),
+ match_line(Dev, "17. Printing with ~s", ["importance"]),
+ read_footer(Dev),
+ match_line(Dev, "18. Printing with ~s, no_css", ["importance"]),
+ read_header(Dev, "\"ct_internal\""),
+ match_line(Dev, "19. Printing with category and importance", []),
+ read_footer(Dev),
+ read_header(Dev, "\"ct_internal\""),
+ match_line(Dev, "20. Printing with ~s", ["category and importance"]),
+ read_footer(Dev),
+ match_line(Dev, "21. Printing a pid escaped with ~s, no_css: ~s",
+ ["category and importance",EscPid]),
+
+ file:close(Dev),
+ ok.
+
+%%%-----------------------------------------------------------------
+%%% HELP FUNCTIONS
+%%%-----------------------------------------------------------------
+
+read_until(Dev, Pat) ->
+ case file:read_line(Dev) of
+ {ok,Pat} ->
+ file:read_line(Dev), % \n
+ ok;
+ eof ->
+ file:close(Dev),
+ {error,{not_found,Pat}};
+ _ ->
+ read_until(Dev, Pat)
+ end.
+
+match_line(Dev, Format, Args) ->
+ Pat = lists:flatten(io_lib:format(Format, Args)),
+ Line = element(2, file:read_line(Dev)),
+ case re:run(Line, Pat) of
+ {match,_} ->
+ ok;
+ nomatch ->
+ ct:pal("ERROR! No match for ~p.\nLine = ~p", [Pat,Line]),
+ file:close(Dev),
+ ct:fail({mismatch,Pat,Line})
+ end.
+
+read_header(Dev) ->
+ read_header(Dev, "\"default\"").
+
+read_header(Dev, Cat) ->
+ file:read_line(Dev), % \n
+ "</pre>\n" = element(2, file:read_line(Dev)),
+ {match,_} =
+ re:run(element(2, file:read_line(Dev)), "<div class="++Cat++"><pre><b>"
+ "\\*\\*\\* User \\d{4}-\\d{2}-\\d{2} "
+ "\\d{2}:\\d{2}:\\d{2}.\\d{1,} \\*\\*\\*</b>").
+
+read_footer(Dev) ->
+ "</pre></div>\n" = element(2, file:read_line(Dev)),
+ "<pre>\n" = element(2, file:read_line(Dev)).
+
+read_nl(Dev) ->
+ file:read_line(Dev).
+
+
diff --git a/lib/common_test/test/ct_repeat_testrun_SUITE.erl b/lib/common_test/test/ct_repeat_testrun_SUITE.erl
index 632597c214..f8b6a379f6 100644
--- a/lib/common_test/test/ct_repeat_testrun_SUITE.erl
+++ b/lib/common_test/test/ct_repeat_testrun_SUITE.erl
@@ -66,25 +66,47 @@
%% there will be clashes with logging processes etc).
%%--------------------------------------------------------------------
init_per_suite(Config0) ->
- Config = ct_test_support:init_per_suite(Config0),
- DataDir = ?config(data_dir, Config),
- Suite1 = filename:join([DataDir,"a_test","r1_SUITE"]),
- Suite2 = filename:join([DataDir,"b_test","r2_SUITE"]),
- Opts0 = ct_test_support:get_opts(Config),
- Opts1 = Opts0 ++ [{suite,Suite1},{testcase,tc2},{label,timing1}],
- Opts2 = Opts0 ++ [{suite,Suite2},{testcase,tc2},{label,timing2}],
-
- %% Make sure both suites are compiled
- {1,0,{0,0}} = ct_test_support:run(ct,run_test,[Opts1],Config),
- {1,0,{0,0}} = ct_test_support:run(ct,run_test,[Opts2],Config),
-
- %% Time the shortest testcase to use for offset
- {_T0,{1,0,{0,0}}} = timer:tc(ct_test_support,run,[ct,run_test,[Opts1],Config]),
-
- %% -2 is to ensure we hit inside the target test case and not after
-% T = round(T0/1000000)-2,
- T=0,
- [{offset,T}|Config].
+ TTInfo = {_T,{_Scaled,ScaleVal}} = ct:get_timetrap_info(),
+ ct:pal("Timetrap info = ~w", [TTInfo]),
+ if ScaleVal > 1 ->
+ {skip,"Skip on systems running e.g. cover or debug!"};
+ ScaleVal =< 1 ->
+ Config = ct_test_support:init_per_suite(Config0),
+ DataDir = ?config(data_dir, Config),
+ Suite1 = filename:join([DataDir,"a_test","r1_SUITE"]),
+ Suite2 = filename:join([DataDir,"b_test","r2_SUITE"]),
+ Opts0 = ct_test_support:get_opts(Config),
+ Opts1 = Opts0 ++ [{suite,Suite1},{testcase,tc2},{label,timing1}],
+ Opts2 = Opts0 ++ [{suite,Suite2},{testcase,tc2},{label,timing2}],
+
+ %% Make sure both suites are compiled
+ {1,0,{0,0}} = ct_test_support:run(ct,run_test,[Opts1],Config),
+ {1,0,{0,0}} = ct_test_support:run(ct,run_test,[Opts2],Config),
+
+ %% Check if file i/o is too slow for correct measurements
+ Opts3 = Opts0 ++ [{suite,Suite1},{testcase,tc1},{label,timing3}],
+ {T,_} =
+ timer:tc(
+ fun() ->
+ {1,0,{0,0}} = ct_test_support:run(ct,run_test,
+ [Opts3],Config),
+ {1,0,{0,0}} = ct_test_support:run(ct,run_test,
+ [Opts3],Config)
+ end),
+ %% The time to compare with here must match the timeout value
+ %% in the test suite. Accept 30% logging overhead (26 sec total).
+ if T > 26000000 ->
+ ct:pal("Timing test took ~w sec (< 27 sec expected). "
+ "Skipping the suite!",
+ [trunc(T/1000000)]),
+ ct_test_support:end_per_suite(Config),
+ {skip,"File I/O too slow for this suite"};
+ true ->
+ ct:pal("Timing test took ~w sec. Proceeding...",
+ [trunc(T/1000000)]),
+ [{offset,0}|Config]
+ end
+ end.
end_per_suite(Config) ->
ct_test_support:end_per_suite(Config).
diff --git a/lib/compiler/doc/src/compile.xml b/lib/compiler/doc/src/compile.xml
index 954750fcdd..61e214294e 100644
--- a/lib/compiler/doc/src/compile.xml
+++ b/lib/compiler/doc/src/compile.xml
@@ -40,6 +40,19 @@
<funcs>
<func>
+ <name>env_compiler_options()</name>
+ <fsummary>
+ Compiler options defined via the environment variable
+ <c>ERL_COMPILER_OPTIONS</c>
+ </fsummary>
+ <desc>
+ <p>Return compiler options given via the environment variable
+ <c>ERL_COMPILER_OPTIONS</c>. If the value is a list, it is
+ returned as is. If it is not a list, it is put into a list.
+ </p>
+ </desc>
+ </func>
+ <func>
<name>file(File)</name>
<fsummary>Compiles a file.</fsummary>
<desc>
@@ -768,6 +781,9 @@ module.beam: module.erl \
if you do not want the environment variable to be consulted,
for example, if you are calling the compiler recursively from
inside a parse transform.</p>
+
+ <p>The list can be retrieved with
+ <seealso marker="#env_compiler_options/0">env_compiler_options/0</seealso>.</p>
</section>
<section>
diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl
index 149086152a..82ff8a95f3 100644
--- a/lib/compiler/src/compile.erl
+++ b/lib/compiler/src/compile.erl
@@ -26,6 +26,7 @@
-export([forms/1,forms/2,noenv_forms/2]).
-export([output_generated/1,noenv_output_generated/1]).
-export([options/0]).
+-export([env_compiler_options/0]).
%% Erlc interface.
-export([compile/3,compile_beam/3,compile_asm/3,compile_core/3]).
@@ -131,6 +132,14 @@ noenv_output_generated(Opts) ->
end, Passes).
%%
+%% Retrieve ERL_COMPILER_OPTIONS as a list of terms
+%%
+
+-spec env_compiler_options() -> [term()].
+
+env_compiler_options() -> env_default_opts().
+
+%%
%% Local functions
%%
diff --git a/lib/compiler/test/compile_SUITE.erl b/lib/compiler/test/compile_SUITE.erl
index a15efc2a00..b0148f7103 100644
--- a/lib/compiler/test/compile_SUITE.erl
+++ b/lib/compiler/test/compile_SUITE.erl
@@ -34,7 +34,7 @@
cover/1, env/1, core/1,
core_roundtrip/1, asm/1,
sys_pre_attributes/1, dialyzer/1,
- warnings/1, pre_load_check/1
+ warnings/1, pre_load_check/1, env_compiler_options/1
]).
suite() -> [{ct_hooks,[ts_install_cth]}].
@@ -50,7 +50,8 @@ all() ->
other_output, encrypted_abstr,
strict_record,
cover, env, core, core_roundtrip, asm,
- sys_pre_attributes, dialyzer, warnings, pre_load_check].
+ sys_pre_attributes, dialyzer, warnings, pre_load_check,
+ env_compiler_options].
groups() ->
[].
@@ -1092,6 +1093,23 @@ compiler_modules() ->
FN = filename,
[list_to_atom(FN:rootname(FN:basename(M), ".beam")) || M <- Ms].
+%% Test that ERL_COMPILER_OPTIONS are correctly retrieved
+%% by env_compiler_options/0
+
+env_compiler_options(_Config) ->
+ Cases = [
+ {"bin_opt_info", [bin_opt_info]},
+ {"'S'", ['S']},
+ {"{source, \"test.erl\"}", [{source, "test.erl"}]},
+ {"[{d,macro_one,1},{d,macro_two}]", [{d, macro_one, 1}, {d, macro_two}]},
+ {"[warn_export_all, warn_export_vars]", [warn_export_all, warn_export_vars]}
+ ],
+ F = fun({Env, Expected}) ->
+ true = os:putenv("ERL_COMPILER_OPTIONS", Env),
+ Expected = compile:env_compiler_options()
+ end,
+ lists:foreach(F, Cases).
+
%%%
%%% Utilities.
%%%
diff --git a/lib/debugger/src/dbg_debugged.erl b/lib/debugger/src/dbg_debugged.erl
index 5b1469a10e..e142af4ae0 100644
--- a/lib/debugger/src/dbg_debugged.erl
+++ b/lib/debugger/src/dbg_debugged.erl
@@ -70,7 +70,10 @@ msg_loop(Meta, Mref, SaveStacktrace) ->
%% Meta is evaluating a receive, must be done within context
%% of real (=this) process
{sys, Meta, {'receive',Msg}} ->
- receive Msg -> Meta ! {self(), rec_acked} end,
+ receive Msg ->
+ Meta ! {self(), rec_acked},
+ ok
+ end,
msg_loop(Meta, Mref, SaveStacktrace);
%% Meta needs something evaluated within context of real process
diff --git a/lib/debugger/src/dbg_icmd.erl b/lib/debugger/src/dbg_icmd.erl
index 1b274e20ae..57a3719a50 100644
--- a/lib/debugger/src/dbg_icmd.erl
+++ b/lib/debugger/src/dbg_icmd.erl
@@ -171,10 +171,10 @@ handle_cmd(Bs, Status, Ieval) ->
%% User control of process execution and settings
%%====================================================================
-step(Meta) -> Meta ! {user, {cmd, step}}.
-next(Meta) -> Meta ! {user, {cmd, next}}.
-continue(Meta) -> Meta ! {user, {cmd, continue}}.
-finish(Meta) -> Meta ! {user, {cmd, finish}}.
+step(Meta) -> Meta ! {user, {cmd, step}}, ok.
+next(Meta) -> Meta ! {user, {cmd, next}}, ok.
+continue(Meta) -> Meta ! {user, {cmd, continue}}, ok.
+finish(Meta) -> Meta ! {user, {cmd, finish}}, ok.
skip(Meta) -> Meta ! {user, {cmd, skip}}.
timeout(Meta) -> Meta ! {user, timeout}.
diff --git a/lib/debugger/src/dbg_iserver.erl b/lib/debugger/src/dbg_iserver.erl
index 0ad303d8d9..3561454685 100644
--- a/lib/debugger/src/dbg_iserver.erl
+++ b/lib/debugger/src/dbg_iserver.erl
@@ -72,17 +72,17 @@ cast(Int, Request) ->
gen_server:cast(Int, Request).
safe_call(Request) ->
- ensure_started(),
+ {ok, _} = ensure_started(),
call(Request).
safe_cast(Request) ->
- ensure_started(),
+ {ok, _} = ensure_started(),
cast(Request).
ensure_started() ->
case whereis(?MODULE) of
undefined -> start();
- _Pid -> ignore
+ Pid -> {ok, Pid}
end.
%%--Module database---------------------------------------------------
@@ -402,8 +402,10 @@ handle_cast({set_status, Meta, Status, Info}, State) ->
send_all(subscriber, {new_status, Proc#proc.pid, Status, Info}, State),
if
Status =:= break ->
- auto_attach(break, State#state.auto, Proc);
- true -> ignore
+ _ = auto_attach(break, State#state.auto, Proc),
+ ok;
+ true ->
+ ok
end,
Proc2 = Proc#proc{status=Status, info=Info},
{noreply, State#state{procs=lists:keyreplace(Meta, #proc.meta,
@@ -470,7 +472,7 @@ handle_info({'EXIT',Who,Why}, State) ->
[self(),AttPid,Pid,Why,ExitInfo]);
undefined ->
%% Otherwise, auto attach if necessary
- auto_attach(exit, State#state.auto, Pid),
+ _ = auto_attach(exit, State#state.auto, Pid),
Who
end,
send_all(subscriber, {new_status,Pid,exit,Why}, State),
@@ -583,7 +585,8 @@ send_all(Pids, Msg) ->
lists:foreach(fun(Pid) -> send(Pid, Msg) end, Pids).
send(Pid, Msg) ->
- Pid ! {int, Msg}.
+ Pid ! {int, Msg},
+ ok.
get_proc({Type, Pid}, Procs) ->
Index = case Type of
diff --git a/lib/debugger/src/dbg_wx_break_win.erl b/lib/debugger/src/dbg_wx_break_win.erl
index cd1e81456f..770681510d 100644
--- a/lib/debugger/src/dbg_wx_break_win.erl
+++ b/lib/debugger/src/dbg_wx_break_win.erl
@@ -65,18 +65,18 @@ create_win(Parent, Pos, function, Mod, _Line) ->
{choices, IntStrs}]),
Expand = [{border, 5}, {flag,?wxLEFT bor ?wxRIGHT bor ?wxEXPAND}],
- wxSizer:add(MainS, Label, [{border,5},
+ _ = wxSizer:add(MainS, Label, [{border,5},
{flag,?wxTOP bor ?wxLEFT bor ?wxRIGHT}]),
- wxSizer:add(MainS, Text, Expand),
+ _ = wxSizer:add(MainS, Text, Expand),
FunLabel = wxStaticText:new(Win, ?wxID_ANY, "Function:"),
LB = wxListBox:new(Win, ?wxID_ANY, [{size,{-1, 100}},{style,?wxLB_MULTIPLE}]),
- wxSizer:add(MainS, FunLabel, Expand),
- wxSizer:add(MainS, LB, [{proportion,1}|Expand]),
+ _ = wxSizer:add(MainS, FunLabel, Expand),
+ _ = wxSizer:add(MainS, LB, [{proportion,1}|Expand]),
wxSizer:setMinSize(MainS, 300, 400),
OK = wxDialog:createStdDialogButtonSizer(Win, ?wxOK bor ?wxCANCEL),
- wxSizer:add(MainS, OK, [{border,5},{flag,?wxALL}]),
+ _ = wxSizer:add(MainS, OK, [{border,5},{flag,?wxALL}]),
wxDialog:setSizer(Win,MainS),
- wxSizer:fit(MainS, Win),
+ _ = wxSizer:fit(MainS, Win),
wxSizer:setSizeHints(MainS,Win),
wxComboBox:setFocus(Text),
wxDialog:connect(Win, command_button_clicked),
@@ -110,11 +110,11 @@ create_win(Parent, Pos, Type, Mod, Line) ->
IntStrs = [atom_to_list(M) || M <- Int],
ModT = wxComboBox:new(Win, ?wxID_ANY, [{choices,IntStrs}]),
ModSz = create_label_of_control(Win, "Module:", ModT, Mod),
- wxSizer:add(MainS,ModSz,[{flag, ?wxEXPAND}]),
+ _ = wxSizer:add(MainS,ModSz,[{flag, ?wxEXPAND}]),
%% Create rest of text input fields
Add = fun({IType, Label, Def}) ->
{Sz, Text} = create_sizer_with_text(Win, Label, Def),
- wxSizer:add(MainS, Sz, [{flag, ?wxEXPAND}]),
+ _ = wxSizer:add(MainS, Sz, [{flag, ?wxEXPAND}]),
{Text, IType}
end,
Inputs = case Type of
@@ -129,15 +129,15 @@ create_win(Parent, Pos, Type, Mod, Line) ->
Entries = wx:map(Add, Inputs),
%% Create and add radio box
{TriggerBox,Trigger} = create_trigger_box(Win),
- wxSizer:add(MainS, TriggerBox, [{border,5},{flag,?wxALL bor ?wxEXPAND}]),
+ _ = wxSizer:add(MainS, TriggerBox, [{border,5},{flag,?wxALL bor ?wxEXPAND}]),
- wxSizer:addStretchSpacer(MainS),
+ _ = wxSizer:addStretchSpacer(MainS),
%% Put it together
OK = wxDialog:createStdDialogButtonSizer(Win, ?wxOK bor ?wxCANCEL),
- wxSizer:add(MainS, OK, [{border,5},{flag,?wxALL}]),
+ _ = wxSizer:add(MainS, OK, [{border,5},{flag,?wxALL}]),
wxSizer:setMinSize(MainS, 300, -1),
wxDialog:setSizer(Win,MainS),
- wxSizer:fit(MainS, Win),
+ _ = wxSizer:fit(MainS, Win),
wxSizer:setSizeHints(MainS,Win),
wxComboBox:setFocus(ModT),
wxDialog:connect(Win, command_button_clicked),
@@ -243,8 +243,8 @@ create_label_of_control(Parent, Label, Control, Def) ->
Text = wxStaticText:new(Parent, ?wxID_ANY, Label),
Border = {border, 5},
Flag = ?wxRIGHT bor ?wxLEFT bor ?wxALIGN_CENTRE_VERTICAL,
- wxSizer:add(Sizer, Text, [{proportion,1}, {flag,Flag}, Border]),
- wxSizer:add(Sizer, Control, [{proportion,3}, {flag,Flag bor ?wxEXPAND}, Border]),
+ _ = wxSizer:add(Sizer, Text, [{proportion,1}, {flag,Flag}, Border]),
+ _ = wxSizer:add(Sizer, Control, [{proportion,3}, {flag,Flag bor ?wxEXPAND}, Border]),
wxControl:setLabel(Control, dbg_wx_win:to_string(Def)),
Sizer.
@@ -252,11 +252,11 @@ create_trigger_box(Win) ->
SBox = wxStaticBox:new(Win, ?wxID_ANY, "Trigger Action:"),
SBS = wxStaticBoxSizer:new(SBox, ?wxVERTICAL),
Ebtn = wxRadioButton:new(Win, ?wxID_ANY, "Enable"),
- wxSizer:add(SBS,Ebtn),
+ _ = wxSizer:add(SBS,Ebtn),
Dibtn = wxRadioButton:new(Win, ?wxID_ANY, "Disable"),
- wxSizer:add(SBS,Dibtn),
+ _ = wxSizer:add(SBS,Dibtn),
Debtn = wxRadioButton:new(Win, ?wxID_ANY, "Delete"),
- wxSizer:add(SBS,Debtn),
+ _ = wxSizer:add(SBS,Debtn),
wxRadioButton:setValue(Ebtn, true),
{SBS, [{Ebtn,enable},{Dibtn,disable},{Debtn,delete}]}.
diff --git a/lib/debugger/src/dbg_wx_code.erl b/lib/debugger/src/dbg_wx_code.erl
index f8fc331a81..473963500a 100644
--- a/lib/debugger/src/dbg_wx_code.erl
+++ b/lib/debugger/src/dbg_wx_code.erl
@@ -127,20 +127,22 @@ load_code(Ed, Code) ->
%%io:format("~p ~p ~p~n", [Lines, Sz, LW]),
?stc:setMarginWidth(Ed, 0, LW+5),
?stc:setReadOnly(Ed, true),
- Ed.
+ ok.
unload_code(Ed) ->
?stc:setReadOnly(Ed, false),
?stc:setTextRaw(Ed, <<0:8>>),
?stc:setReadOnly(Ed, true),
- Ed.
+ ok.
add_break_to_code(Ed, Line, active) ->
?stc:markerDelete(Ed, Line-1, 1),
- ?stc:markerAdd(Ed, Line-1, 0);
+ ?stc:markerAdd(Ed, Line-1, 0),
+ ok;
add_break_to_code(Ed, Line, inactive) ->
?stc:markerDelete(Ed, Line-1, 0),
- ?stc:markerAdd(Ed, Line-1, 1).
+ ?stc:markerAdd(Ed, Line-1, 1),
+ ok.
del_break_from_code(Ed,Line) ->
?stc:markerDelete(Ed, Line-1, 0),
diff --git a/lib/debugger/src/dbg_wx_filedialog_win.erl b/lib/debugger/src/dbg_wx_filedialog_win.erl
index 2103536a04..f7b031dc28 100644
--- a/lib/debugger/src/dbg_wx_filedialog_win.erl
+++ b/lib/debugger/src/dbg_wx_filedialog_win.erl
@@ -118,9 +118,9 @@ init([Parent, Id, Options0]) ->
wxTextCtrl:connect(Dir, char, [{callback, IsTab}]),
Top = wxBoxSizer:new(?wxHORIZONTAL),
- wxSizer:add(Top, Back, [{border, 2},{flag,?wxALL bor ?wxEXPAND}]),
- wxSizer:add(Top, Forw, [{border, 2},{flag,?wxALL bor ?wxEXPAND}]),
- wxSizer:add(Top, Up, [{border, 2},{flag,?wxALL bor ?wxEXPAND}]),
+ _ = wxSizer:add(Top, Back, [{border, 2},{flag,?wxALL bor ?wxEXPAND}]),
+ _ = wxSizer:add(Top, Forw, [{border, 2},{flag,?wxALL bor ?wxEXPAND}]),
+ _ = wxSizer:add(Top, Up, [{border, 2},{flag,?wxALL bor ?wxEXPAND}]),
%% List Ctrl
{Art, IconMap} = create_icons(ExtraIcons),
@@ -154,13 +154,13 @@ init([Parent, Id, Options0]) ->
%% OK done
Box = wxBoxSizer:new(?wxVERTICAL),
- wxSizer:add(Box, Top, [{border, 2}, {flag,?wxALL bor ?wxEXPAND}]),
- wxSizer:add(Box, Dir, [{border, 2}, {flag,?wxALL bor ?wxEXPAND}]),
- wxSizer:add(Box, LC, [{border, 2}, {flag,?wxALL bor ?wxEXPAND}, {proportion, 1}]),
- wxSizer:add(Box, Bott, [{border, 2}, {flag,?wxALL bor ?wxEXPAND}]),
+ _ = wxSizer:add(Box, Top, [{border, 2}, {flag,?wxALL bor ?wxEXPAND}]),
+ _ = wxSizer:add(Box, Dir, [{border, 2}, {flag,?wxALL bor ?wxEXPAND}]),
+ _ = wxSizer:add(Box, LC, [{border, 2}, {flag,?wxALL bor ?wxEXPAND}, {proportion, 1}]),
+ _ = wxSizer:add(Box, Bott, [{border, 2}, {flag,?wxALL bor ?wxEXPAND}]),
wxWindow:setSizer(Dlg, Box),
- wxSizer:fit(Box, Dlg),
+ _ = wxSizer:fit(Box, Dlg),
wxSizer:setSizeHints(Box,Dlg),
State = #state{win=Dlg,
back=Back, forward=Forw, up=Up,
diff --git a/lib/debugger/src/dbg_wx_mon.erl b/lib/debugger/src/dbg_wx_mon.erl
index 345367a911..a32a6894b8 100644
--- a/lib/debugger/src/dbg_wx_mon.erl
+++ b/lib/debugger/src/dbg_wx_mon.erl
@@ -135,7 +135,7 @@ init2(CallingPid, Mode, SFile, GS) ->
%% Start other necessary stuff
dbg_wx_win:init(),
- dbg_wx_winman:start(), % Debugger window manager
+ _ = dbg_wx_winman:start(), % Debugger window manager
%% Create monitor window
Title = "Monitor",
@@ -339,7 +339,7 @@ gui_cmd('Delete All Modules', State) ->
lists:foreach(fun(Mod) -> int:nn(Mod) end, int:interpreted()),
State;
gui_cmd({module, Mod, What}, State) ->
- case What of
+ _ = case What of
delete -> int:nn(Mod);
view ->
Window = dbg_wx_mon_win:get_window(State#state.win),
diff --git a/lib/debugger/src/dbg_wx_mon_win.erl b/lib/debugger/src/dbg_wx_mon_win.erl
index 2e48210f55..9737c9e67f 100644
--- a/lib/debugger/src/dbg_wx_mon_win.erl
+++ b/lib/debugger/src/dbg_wx_mon_win.erl
@@ -107,31 +107,31 @@ create_win_batch(Title, Menus) ->
Hlb = 200,
Listbox = wxListBox:new(Panel, ?wxID_ANY, [{size,{?Wf,Hlb}},
{style,?wxLB_SINGLE}]),
- wxSizer:add(LeftSz,Listbox,[{proportion,1},{border,3},{flag,?wxEXPAND}]),
+ _ = wxSizer:add(LeftSz,Listbox,[{proportion,1},{border,3},{flag,?wxEXPAND}]),
wxListBox:connect(Listbox, command_listbox_doubleclicked),
wxListBox:connect(Listbox, right_down),
SBox = wxStaticBox:new(Panel, ?wxID_ANY, "Auto Attach:"),
SBS = wxStaticBoxSizer:new(SBox, ?wxVERTICAL),
Fbtn = wxCheckBox:new(Panel, ?autoId, "First Call"),
- wxSizer:add(SBS,Fbtn),
+ _ = wxSizer:add(SBS,Fbtn),
Bbtn = wxCheckBox:new(Panel, ?autoId, "On Break"),
- wxSizer:add(SBS,Bbtn),
+ _ = wxSizer:add(SBS,Bbtn),
Ebtn = wxCheckBox:new(Panel, ?autoId, "On Exit"),
- wxSizer:add(SBS,Ebtn),
+ _ = wxSizer:add(SBS,Ebtn),
wxFrame:connect(Panel, command_checkbox_clicked),
- wxSizer:add(LeftSz,SBS, [{flag,?wxEXPAND}]),
+ _ = wxSizer:add(LeftSz,SBS, [{flag,?wxEXPAND}]),
SLabel = wxStaticText:new(Panel, ?wxID_ANY, "Stack Trace:\n On (with tail)"),
- wxSizer:add(LeftSz,SLabel),
+ _ = wxSizer:add(LeftSz,SLabel),
BLabel = wxStaticText:new(Panel, ?wxID_ANY, "Back Trace Size:\n 50000"),
- wxSizer:add(LeftSz,BLabel),
+ _ = wxSizer:add(LeftSz,BLabel),
StringsBox = wxStaticBox:new(Panel, ?wxID_ANY, "Strings:"),
StringsBS = wxStaticBoxSizer:new(StringsBox, ?wxVERTICAL),
Stringsbtn = wxCheckBox:new(Panel, ?stringsId, ?STRTEXT),
- wxSizer:add(StringsBS,Stringsbtn),
- wxSizer:add(LeftSz,StringsBS, [{flag,?wxEXPAND}]),
+ _ = wxSizer:add(StringsBS,Stringsbtn),
+ _ = wxSizer:add(LeftSz,StringsBS, [{flag,?wxEXPAND}]),
%% Create list_crtl / grid
Grid = wxListCtrl:new(Panel, [{winid, ?GRID},
@@ -169,12 +169,12 @@ create_win_batch(Title, Menus) ->
wxWindow:setFocus(Grid),
%% Put it in the window
- wxSizer:add(MainSz, LeftSz, [{border, 3}, {flag,?wxALL bor ?wxEXPAND}]),
- wxSizer:add(MainSz, Grid, [{border, 3}, {flag,?wxALL bor ?wxEXPAND},
+ _ = wxSizer:add(MainSz, LeftSz, [{border, 3}, {flag,?wxALL bor ?wxEXPAND}]),
+ _ = wxSizer:add(MainSz, Grid, [{border, 3}, {flag,?wxALL bor ?wxEXPAND},
{proportion, 1}]),
wxWindow:setSizer(Panel,MainSz),
- wxSizer:fit(MainSz, Win),
+ _ = wxSizer:fit(MainSz, Win),
wxSizer:setSizeHints(MainSz,Win),
IconFile = dbg_wx_win:find_icon("erlang_bug.png"),
diff --git a/lib/debugger/src/dbg_wx_src_view.erl b/lib/debugger/src/dbg_wx_src_view.erl
index 571c6b01bb..207c407fbc 100644
--- a/lib/debugger/src/dbg_wx_src_view.erl
+++ b/lib/debugger/src/dbg_wx_src_view.erl
@@ -56,7 +56,7 @@ code_area(Parent, Sizer) ->
end,
[SetStyle(Style) || Style <- Styles],
?stc:setKeyWords(Ed, 0, keyWords()),
- wxSizer:add(Sizer, Ed, [{proportion,1}, {flag, ?wxEXPAND}]),
+ _ = wxSizer:add(Sizer, Ed, [{proportion,1}, {flag, ?wxEXPAND}]),
Ed.
diff --git a/lib/debugger/src/dbg_wx_trace.erl b/lib/debugger/src/dbg_wx_trace.erl
index f9c60f9b72..6af19af33b 100644
--- a/lib/debugger/src/dbg_wx_trace.erl
+++ b/lib/debugger/src/dbg_wx_trace.erl
@@ -321,7 +321,7 @@ gui_cmd('Kill', State) ->
exit(State#state.pid, kill),
State;
gui_cmd('Messages', State) ->
- case int:meta(State#state.meta, messages) of
+ _ = case int:meta(State#state.meta, messages) of
[] ->
dbg_wx_trace_win:eval_output(State#state.win,"< No Messages!\n", bold);
Messages ->
diff --git a/lib/debugger/src/dbg_wx_trace_win.erl b/lib/debugger/src/dbg_wx_trace_win.erl
index 51687ad4e9..972a917728 100644
--- a/lib/debugger/src/dbg_wx_trace_win.erl
+++ b/lib/debugger/src/dbg_wx_trace_win.erl
@@ -123,7 +123,8 @@
%% GS = term()
%%--------------------------------------------------------------------
init() ->
- dbg_wx_win:init().
+ _ = dbg_wx_win:init(),
+ ok.
stop(#winInfo{window=Win}) ->
(catch wxFrame:destroy(Win)),
@@ -149,29 +150,29 @@ create_win(Parent, Title, Windows, Menus) ->
Sizer = wxBoxSizer:new(?wxVERTICAL),
Code = code_area(Panel),
- wxSizer:add(Sizer, Code#sub.win,
+ _ = wxSizer:add(Sizer, Code#sub.win,
[{proportion,1}, {border, 2},
{flag, ?wxEXPAND bor ?wxDOWN}]),
wxSizer:setVirtualSizeHints(Sizer, Code#sub.win),
ExpandWithBorder = [{border, 3},{flag,?wxEXPAND bor ?wxALL}],
Search = search_area(Panel),
- wxSizer:add(Sizer, Search#sub.win, ExpandWithBorder),
+ _ = wxSizer:add(Sizer, Search#sub.win, ExpandWithBorder),
Bs = button_area(Panel),
- wxSizer:add(Sizer, Bs#sub.win, ExpandWithBorder),
+ _ = wxSizer:add(Sizer, Bs#sub.win, ExpandWithBorder),
InfoArea = wxBoxSizer:new(?wxHORIZONTAL),
wxSizer:setMinSize(InfoArea, {100, ?EVAL_H}),
Eval = eval_area(Panel),
- wxSizer:add(InfoArea, Eval#sub.win, [{proportion,1},{flag,?wxEXPAND}]),
+ _ = wxSizer:add(InfoArea, Eval#sub.win, [{proportion,1},{flag,?wxEXPAND}]),
Bind = bind_area(Panel),
- wxSizer:add(InfoArea, Bind#sub.win,
+ _ = wxSizer:add(InfoArea, Bind#sub.win,
[{proportion,1},{border, 2},
{flag,?wxEXPAND bor ?wxLEFT}]),
- wxSizer:add(Sizer, InfoArea, ExpandWithBorder),
+ _ = wxSizer:add(Sizer, InfoArea, ExpandWithBorder),
Trace = trace_area(Panel),
- wxSizer:add(Sizer, Trace#sub.win, ExpandWithBorder),
+ _ = wxSizer:add(Sizer, Trace#sub.win, ExpandWithBorder),
SB = wxFrame:createStatusBar(Win,[]),
%% Note id and lastId to get the event when it dragged is complete
@@ -192,7 +193,7 @@ create_win(Parent, Title, Windows, Menus) ->
Wi = show_windows(enable_windows(Wi0,Windows)),
wxWindow:setSizer(Panel, Sizer),
- wxSizer:fit(Sizer, Win),
+ _ = wxSizer:fit(Sizer, Win),
wxSizer:setSizeHints(Sizer,Win),
IconFile = dbg_wx_win:find_icon("erlang_bug.png"),
@@ -230,11 +231,11 @@ get_window(WinInfo) ->
%%--------------------------------------------------------------------
configure(Wi=#winInfo{window=Win,m_szr={Panel,Sizer}}) ->
wx:batch(fun() ->
- show_windows(Wi),
+ _ = show_windows(Wi),
wxSizer:layout(Sizer),
%%wxWindow:setSizerAndFit(Panel,Sizer),
wxWindow:setSizer(Panel, Sizer),
- wxSizer:fit(Sizer, Win),
+ _ = wxSizer:fit(Sizer, Win),
wxSizer:setSizeHints(Sizer,Win),
Wi
end).
@@ -242,10 +243,10 @@ configure(Wi=#winInfo{window=Win,m_szr={Panel,Sizer}}) ->
configure(Wi0=#winInfo{window=Win,m_szr={Panel,Sizer}}, Windows) ->
wx:batch(fun() ->
Wi = enable_windows(Wi0, Windows),
- show_windows(Wi),
+ _ = show_windows(Wi),
wxSizer:layout(Sizer),
wxWindow:setSizer(Panel, Sizer),
- wxSizer:fit(Sizer, Win),
+ _ = wxSizer:fit(Sizer, Win),
wxSizer:setSizeHints(Sizer,Win),
Wi
end).
@@ -348,7 +349,7 @@ add_break(WinInfo, Menu, {{Mod,Line},[Status|_Options]}=Break) ->
case WinInfo#winInfo.editor of
{Mod, Editor} ->
dbg_wx_code:add_break_to_code(Editor, Line, Status);
- _ -> ignore
+ _ -> ok
end,
add_break_to_menu(WinInfo, Menu, Break).
@@ -372,7 +373,7 @@ update_break(WinInfo, {{Mod,Line},[Status|_Options]}=Break) ->
case WinInfo#winInfo.editor of
{Mod, Editor} ->
dbg_wx_code:add_break_to_code(Editor, Line, Status);
- _ -> ignore
+ _ -> ok
end,
update_break_in_menu(WinInfo, Break).
@@ -929,7 +930,7 @@ button_area(Parent) ->
B=wxButton:new(Parent, Button,
[{label,dbg_wx_win:to_string(Name)}]),
Id = wxWindow:getId(B),
- wxSizer:add(Sz,B, []),
+ _ = wxSizer:add(Sz,B, []),
wxButton:connect(B, command_button_clicked, [{id,Id}])
end, buttons()),
#sub{name='Button Area', win=Sz}.
@@ -938,22 +939,22 @@ button_area(Parent) ->
search_area(Parent) ->
HSz = wxBoxSizer:new(?wxHORIZONTAL),
- wxSizer:add(HSz, wxStaticText:new(Parent, ?wxID_ANY, "Find:"),
+ _ = wxSizer:add(HSz, wxStaticText:new(Parent, ?wxID_ANY, "Find:"),
[{flag,?wxALIGN_CENTER_VERTICAL}]),
TC1 = wxTextCtrl:new(Parent, ?SEARCH_ENTRY, [{style, ?wxTE_PROCESS_ENTER}]),
- wxSizer:add(HSz, TC1, [{proportion,3}, {flag, ?wxEXPAND}]),
+ _ = wxSizer:add(HSz, TC1, [{proportion,3}, {flag, ?wxEXPAND}]),
Nbtn = wxRadioButton:new(Parent, ?wxID_ANY, "Next"),
wxRadioButton:setValue(Nbtn, true),
- wxSizer:add(HSz,Nbtn,[{flag,?wxALIGN_CENTER_VERTICAL}]),
+ _ = wxSizer:add(HSz,Nbtn,[{flag,?wxALIGN_CENTER_VERTICAL}]),
Pbtn = wxRadioButton:new(Parent, ?wxID_ANY, "Previous"),
- wxSizer:add(HSz,Pbtn,[{flag,?wxALIGN_CENTER_VERTICAL}]),
+ _ = wxSizer:add(HSz,Pbtn,[{flag,?wxALIGN_CENTER_VERTICAL}]),
Cbtn = wxCheckBox:new(Parent, ?wxID_ANY, "Match Case"),
- wxSizer:add(HSz,Cbtn,[{flag,?wxALIGN_CENTER_VERTICAL}]),
- wxSizer:add(HSz, 15,15, [{proportion,1}, {flag, ?wxEXPAND}]),
- wxSizer:add(HSz, wxStaticText:new(Parent, ?wxID_ANY, "Goto Line:"),
+ _ = wxSizer:add(HSz,Cbtn,[{flag,?wxALIGN_CENTER_VERTICAL}]),
+ _ = wxSizer:add(HSz, 15,15, [{proportion,1}, {flag, ?wxEXPAND}]),
+ _ = wxSizer:add(HSz, wxStaticText:new(Parent, ?wxID_ANY, "Goto Line:"),
[{flag,?wxALIGN_CENTER_VERTICAL}]),
TC2 = wxTextCtrl:new(Parent, ?GOTO_ENTRY, [{style, ?wxTE_PROCESS_ENTER}]),
- wxSizer:add(HSz, TC2, [{proportion,0}, {flag, ?wxEXPAND}]),
+ _ = wxSizer:add(HSz, TC2, [{proportion,0}, {flag, ?wxEXPAND}]),
wxTextCtrl:connect(TC1, command_text_updated),
wxTextCtrl:connect(TC1, command_text_enter),
wxTextCtrl:connect(TC1, kill_focus),
@@ -969,14 +970,14 @@ eval_area(Parent) ->
VSz = wxBoxSizer:new(?wxVERTICAL),
HSz = wxBoxSizer:new(?wxHORIZONTAL),
- wxSizer:add(HSz, wxStaticText:new(Parent, ?wxID_ANY, "Evaluator:"),
+ _ = wxSizer:add(HSz, wxStaticText:new(Parent, ?wxID_ANY, "Evaluator:"),
[{flag,?wxALIGN_CENTER_VERTICAL}]),
TC = wxTextCtrl:new(Parent, ?EVAL_ENTRY, [{style, ?wxTE_PROCESS_ENTER}]),
- wxSizer:add(HSz, TC, [{proportion,1}, {flag, ?wxEXPAND}]),
- wxSizer:add(VSz, HSz, [{flag, ?wxEXPAND}]),
+ _ = wxSizer:add(HSz, TC, [{proportion,1}, {flag, ?wxEXPAND}]),
+ _ = wxSizer:add(VSz, HSz, [{flag, ?wxEXPAND}]),
TL = wxTextCtrl:new(Parent, ?EVAL_LOG, [{style, ?wxTE_DONTWRAP bor
?wxTE_MULTILINE bor ?wxTE_READONLY}]),
- wxSizer:add(VSz, TL, [{proportion,5}, {flag, ?wxEXPAND}]),
+ _ = wxSizer:add(VSz, TL, [{proportion,5}, {flag, ?wxEXPAND}]),
wxTextCtrl:connect(TC, command_text_enter),
#sub{name='Evaluator Area', win=VSz, in=TC, out=TL}.
diff --git a/lib/debugger/src/dbg_wx_win.erl b/lib/debugger/src/dbg_wx_win.erl
index 1ff8818bbe..25ffc5054c 100644
--- a/lib/debugger/src/dbg_wx_win.erl
+++ b/lib/debugger/src/dbg_wx_win.erl
@@ -43,7 +43,8 @@
%% GS = term()
%%--------------------------------------------------------------------
init() ->
- wx:new().
+ _ = wx:new(),
+ ok.
%%--------------------------------------------------------------------
%% create_menus(MenuBar, [Menu])
@@ -80,12 +81,12 @@ create_menus(_MB,[], _Win,Id) ->
Id.
create_menu_item(Menu, [separator|Is], Win, Id,Connect) ->
- wxMenu:appendSeparator(Menu),
+ _ = wxMenu:appendSeparator(Menu),
create_menu_item(Menu,Is,Win,Id+1,Connect);
create_menu_item(Menu, [{Name, _N, cascade, Items}|Is], Win, Id0,Connect) ->
Sub = wxMenu:new([]),
Id = create_menu_item(Sub, Items, Win, Id0, false),
- wxMenu:append(Menu, ?wxID_ANY, menu_name(Name,ignore), Sub),
+ _ = wxMenu:append(Menu, ?wxID_ANY, menu_name(Name,ignore), Sub),
%% Simulate GS sub checkBox/RadioBox behaviour
Self = self(),
Butts = [{MI,get(MI)} || {MI,_,_} <- Items],
@@ -99,8 +100,8 @@ create_menu_item(Menu, [{Name, _N, cascade, Items}|Is], Win, Id0,Connect) ->
Enabled = lists:foldl(IsChecked, [], Butts),
Self ! Ev#wx{userData={Name, Enabled}}
end,
- wxMenu:connect(Win, command_menu_selected,
- [{id,Id0},{lastId, Id-1},{callback,Filter}]),
+ _ = wxMenu:connect(Win, command_menu_selected,
+ [{id,Id0},{lastId, Id-1},{callback,Filter}]),
create_menu_item(Menu, Is, Win, Id, Connect);
create_menu_item(Menu, [{Name,Pos}|Is], Win, Id, Connect) ->
MenuId = case lists:member(Name, ['Debugger']) of
@@ -168,7 +169,7 @@ add_break(Win, MenuName, Point) ->
Delete = wxMenu:appendRadioItem(Trigger, ?wxID_ANY,"Delete"),
Add(Delete, {break,Point,{trigger,delete}}),
- wxMenu:append(Sub, ?wxID_ANY, "Trigger Action", Trigger),
+ _ = wxMenu:append(Sub, ?wxID_ANY, "Trigger Action", Trigger),
MenuBtn = wxMenu:append(Menu,?wxID_ANY, Label, Sub),
#break{mb={Menu,MenuBtn},
diff --git a/lib/debugger/src/dbg_wx_winman.erl b/lib/debugger/src/dbg_wx_winman.erl
index efa58ae325..ca858fa4bb 100644
--- a/lib/debugger/src/dbg_wx_winman.erl
+++ b/lib/debugger/src/dbg_wx_winman.erl
@@ -100,11 +100,11 @@ update_windows_menu(Win, [MonInfo|Infos]) ->
OldItems = wxMenu:getMenuItems(Menu),
[wxMenu:delete(Menu, Item) || Item <- OldItems],
menuitem(Win, Menu,MonInfo, 700),
- wxMenu:appendSeparator(Menu),
+ _ = wxMenu:appendSeparator(Menu),
wx:foldl(fun(Info,Acc) -> menuitem(Win,Menu,Info,Acc) end, 701, Infos).
menuitem(Window, Menu, {Title, Win}, Id) ->
- wxMenu:append(Menu, Id, Title),
+ _ = wxMenu:append(Menu, Id, Title),
wxWindow:connect(Window, command_menu_selected,
[{id,Id},{userData,{dbg_ui_winman,Win}}]),
Id+1.
diff --git a/lib/debugger/src/i.erl b/lib/debugger/src/i.erl
index 4ed5265bdf..2da3e77618 100644
--- a/lib/debugger/src/i.erl
+++ b/lib/debugger/src/i.erl
@@ -108,7 +108,7 @@ ib(Module,Function,Arity) ->
ib(Module,Function,Arity,Cond) ->
Breaks1 = int:all_breaks(Module),
- int:break_in(Module,Function,Arity),
+ ok = int:break_in(Module,Function,Arity),
Breaks2 = int:all_breaks(Module),
lists:foreach(fun({Mod,Line}) -> int:test_at_break(Mod,Line,Cond) end,
Breaks2--Breaks1).
diff --git a/lib/debugger/src/int.erl b/lib/debugger/src/int.erl
index 3906c22afd..e5bade9abe 100644
--- a/lib/debugger/src/int.erl
+++ b/lib/debugger/src/int.erl
@@ -352,10 +352,10 @@ start() -> dbg_iserver:start().
stop() ->
lists:foreach(
fun(Mod) ->
- everywhere(distributed,
- fun() ->
+ _ = everywhere(distributed,
+ fun() ->
erts_debug:breakpoint({Mod,'_','_'}, false)
- end)
+ end)
end,
interpreted()),
dbg_iserver:stop().
@@ -524,21 +524,21 @@ check(Mod) when is_atom(Mod) -> catch check_module(Mod);
check(File) when is_list(File) -> catch check_file(File).
load({Mod, Src, Beam, BeamBin, Exp, Abst}, Dist) ->
- everywhere(Dist,
- fun() ->
+ _ = everywhere(Dist,
+ fun() ->
code:purge(Mod),
erts_debug:breakpoint({Mod,'_','_'}, false),
{module,Mod} = code:load_binary(Mod, Beam, BeamBin)
- end),
+ end),
case erl_prim_loader:get_file(filename:absname(Src)) of
{ok, SrcBin, _} ->
MD5 = code:module_md5(BeamBin),
Bin = term_to_binary({interpreter_module,Exp,Abst,SrcBin,MD5}),
{module, Mod} = dbg_iserver:safe_call({load, Mod, Src, Bin}),
- everywhere(Dist,
- fun() ->
+ _ = everywhere(Dist,
+ fun() ->
true = erts_debug:breakpoint({Mod,'_','_'}, true) > 0
- end),
+ end),
{module, Mod};
error ->
error
@@ -738,9 +738,9 @@ del_mod(AbsMod, Dist) ->
list_to_atom(filename:basename(AbsMod,".erl"))
end,
dbg_iserver:safe_cast({delete, Mod}),
- everywhere(Dist,
- fun() ->
+ _ = everywhere(Dist,
+ fun() ->
erts_debug:breakpoint({Mod,'_','_'}, false),
erlang:yield()
- end),
+ end),
ok.
diff --git a/lib/dialyzer/src/dialyzer_contracts.erl b/lib/dialyzer/src/dialyzer_contracts.erl
index d1ffa07706..272ad10e90 100644
--- a/lib/dialyzer/src/dialyzer_contracts.erl
+++ b/lib/dialyzer/src/dialyzer_contracts.erl
@@ -591,10 +591,13 @@ remove_uses([{Var, Use}|ToRemove], Constrs0) ->
remove_uses(_Var, _Use, []) -> [];
remove_uses(Var, Use, [Constr|Constrs]) ->
{V, Form} = Constr,
- case erl_types:t_var_name(V) =:= Var of
- true -> [{V, remove_use(Form, Use)}|Constrs];
- false -> [Constr|remove_uses(Var, Use, Constrs)]
- end.
+ NewConstr = case erl_types:t_var_name(V) =:= Var of
+ true ->
+ {V, remove_use(Form, Use)};
+ false ->
+ Constr
+ end,
+ [NewConstr|remove_uses(Var, Use, Constrs)].
remove_use({var, L, V}, V) -> {var, L, '_'};
remove_use(T, V) when is_tuple(T) ->
diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl
index 5ab0c39c04..3349b12932 100644
--- a/lib/dialyzer/src/dialyzer_dataflow.erl
+++ b/lib/dialyzer/src/dialyzer_dataflow.erl
@@ -430,17 +430,35 @@ handle_apply(Tree, Map, State) ->
handle_apply_or_call(FunInfoList, Args, ArgTypes, Map, Tree, State) ->
None = t_none(),
+ %% Call-site analysis may be inaccurate and consider more funs than those that
+ %% are actually possible. If all of them are incorrect, then warnings can be
+ %% emitted. If at least one fun is ok, however, then no warning is emitted,
+ %% just in case the bad ones are not really possible. The last argument is
+ %% used for this, with the following encoding:
+ %% Initial value: {none, []}
+ %% First fun checked: {one, <List of warns>}
+ %% More funs checked: {many, <List of warns>}
+ %% A '{one, []}' can only become '{many, []}'.
+ %% If at any point an fun does not add warnings, then the list is also
+ %% replaced with an empty list.
handle_apply_or_call(FunInfoList, Args, ArgTypes, Map, Tree, State,
- [None || _ <- ArgTypes], None, false).
+ [None || _ <- ArgTypes], None, false, {none, []}).
handle_apply_or_call([{local, external}|Left], Args, ArgTypes, Map, Tree, State,
- _AccArgTypes, _AccRet, _HadExternal) ->
+ _AccArgTypes, _AccRet, _HadExternal, Warns) ->
+ {HowMany, _} = Warns,
+ NewHowMany =
+ case HowMany of
+ none -> one;
+ _ -> many
+ end,
+ NewWarns = {NewHowMany, []},
handle_apply_or_call(Left, Args, ArgTypes, Map, Tree, State,
- ArgTypes, t_any(), true);
+ ArgTypes, t_any(), true, NewWarns);
handle_apply_or_call([{TypeOfApply, {Fun, Sig, Contr, LocalRet}}|Left],
Args, ArgTypes, Map, Tree,
#state{opaques = Opaques} = State,
- AccArgTypes, AccRet, HadExternal) ->
+ AccArgTypes, AccRet, HadExternal, Warns) ->
Any = t_any(),
AnyArgs = [Any || _ <- Args],
GenSig = {AnyArgs, fun(_) -> t_any() end},
@@ -586,16 +604,32 @@ handle_apply_or_call([{TypeOfApply, {Fun, Sig, Contr, LocalRet}}|Left],
end,
NewAccRet = t_sup(AccRet, TotalRet),
?debug("NewAccRet: ~s\n", [t_to_string(NewAccRet)]),
+ {NewWarnings, State4} = state__remove_added_warnings(State, State3),
+ {HowMany, OldWarnings} = Warns,
+ NewWarns =
+ case HowMany of
+ none -> {one, NewWarnings};
+ _ ->
+ case OldWarnings =:= [] of
+ true -> {many, []};
+ false ->
+ case NewWarnings =:= [] of
+ true -> {many, []};
+ false -> {many, NewWarnings ++ OldWarnings}
+ end
+ end
+ end,
handle_apply_or_call(Left, Args, ArgTypes, Map, Tree,
- State3, NewAccArgTypes, NewAccRet, HadExternal);
+ State4, NewAccArgTypes, NewAccRet, HadExternal, NewWarns);
handle_apply_or_call([], Args, _ArgTypes, Map, _Tree, State,
- AccArgTypes, AccRet, HadExternal) ->
+ AccArgTypes, AccRet, HadExternal, {_, Warnings}) ->
+ State1 = state__add_warnings(Warnings, State),
case HadExternal of
false ->
NewMap = enter_type_lists(Args, AccArgTypes, Map),
- {State, NewMap, AccRet};
+ {State1, NewMap, AccRet};
true ->
- {had_external, State}
+ {had_external, State1}
end.
apply_fail_reason(FailedSig, FailedBif, FailedContract) ->
@@ -3038,6 +3072,14 @@ state__add_warning(#state{warnings = Warnings, warning_mode = true} = State,
end
end.
+state__remove_added_warnings(OldState, NewState) ->
+ #state{warnings = OldWarnings} = OldState,
+ #state{warnings = NewWarnings} = NewState,
+ {NewWarnings -- OldWarnings, NewState#state{warnings = OldWarnings}}.
+
+state__add_warnings(Warns, #state{warnings = Warnings} = State) ->
+ State#state{warnings = Warns ++ Warnings}.
+
-spec state__set_curr_fun(curr_fun(), state()) -> state().
state__set_curr_fun(undefined, State) ->
diff --git a/lib/dialyzer/test/small_SUITE_data/results/higher_order_discrepancy b/lib/dialyzer/test/small_SUITE_data/results/higher_order_discrepancy
index 7ce440a60d..11b9ecade6 100644
--- a/lib/dialyzer/test/small_SUITE_data/results/higher_order_discrepancy
+++ b/lib/dialyzer/test/small_SUITE_data/results/higher_order_discrepancy
@@ -1,4 +1,3 @@
-higher_order_discrepancy.erl:11: The call higher_order_discrepancy:g('foo') will never return since it differs in the 1st argument from the success typing arguments: ('bar')
-higher_order_discrepancy.erl:14: Function g/1 has no local return
-higher_order_discrepancy.erl:14: The pattern 'bar' can never match the type 'foo'
+higher_order_discrepancy.erl:19: Function g/1 has no local return
+higher_order_discrepancy.erl:19: The pattern 'bar' can never match the type 'foo'
diff --git a/lib/dialyzer/test/small_SUITE_data/src/higher_order_discrepancy.erl b/lib/dialyzer/test/small_SUITE_data/src/higher_order_discrepancy.erl
index ff5ee6bac4..f9547d4929 100644
--- a/lib/dialyzer/test/small_SUITE_data/src/higher_order_discrepancy.erl
+++ b/lib/dialyzer/test/small_SUITE_data/src/higher_order_discrepancy.erl
@@ -1,3 +1,8 @@
+%% With the patch introduced to avoid false warnings in
+%% user_SUITE_data/src/wpc_hlines.erl we can unfortunately no longer precisely
+%% catch problems like this one... The refinement procedure is still enough to
+%% keep some of the details, nevertheless.
+
-module(higher_order_discrepancy).
-export([test/1]).
diff --git a/lib/dialyzer/test/small_SUITE_data/src/loopy.erl b/lib/dialyzer/test/small_SUITE_data/src/loopy.erl
new file mode 100644
index 0000000000..28125ec3d9
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/loopy.erl
@@ -0,0 +1,17 @@
+%% ERL-157, OTP-13653.
+%% Would cause Dialyzer to go into an infinite loop.
+
+-module(loopy).
+
+-export([loop/1]).
+
+
+-spec loop(Args) -> ok when
+ Args :: [{Module, Args}],
+ Module :: module(),
+ Args :: any().
+loop([{Module, Args} | Rest]) ->
+ Module:init(Args),
+ loop(Rest);
+loop([]) ->
+ ok.
diff --git a/lib/dialyzer/test/user_SUITE_data/results/wpc_hlines b/lib/dialyzer/test/user_SUITE_data/results/wpc_hlines
new file mode 100644
index 0000000000..d6e3f29ab9
--- /dev/null
+++ b/lib/dialyzer/test/user_SUITE_data/results/wpc_hlines
@@ -0,0 +1,3 @@
+
+wpc_hlines.erl:22: Function bad/1 has no local return
+wpc_hlines.erl:22: The pattern 'false' can never match the type 'true'
diff --git a/lib/dialyzer/test/user_SUITE_data/src/wpc_hlines.erl b/lib/dialyzer/test/user_SUITE_data/src/wpc_hlines.erl
new file mode 100644
index 0000000000..8c205a8247
--- /dev/null
+++ b/lib/dialyzer/test/user_SUITE_data/src/wpc_hlines.erl
@@ -0,0 +1,22 @@
+%% Bug reported by Dan Gudmundsson, test shrunk down by Magnus LÃ¥ng.
+
+%% The problem is that dialyzer_dep generates edges from the fun
+%% application to both of the functions, and then during the warning pass
+%% dialyzer_dataflow:handle_apply_or_call generates warnings for any such
+%% edge that won't return.
+
+%% Since dialyzer_dep is currently supposed to overapproximate rather than
+%% underapproximate, the fix was to modify handle_apply_or_call to not generate
+%% warnings if some of the possible funs can succeed.
+
+-module(wpc_hlines).
+
+-export([do_export/0]).
+
+do_export() ->
+ {Proj, _} = % The culprit seems to be putting the funs in a tuple
+ {fun good/1, fun bad/1},
+ Proj(true).
+
+good(_) -> ok.
+bad(false) -> ok.
diff --git a/lib/edoc/priv/edoc.dtd b/lib/edoc/priv/edoc.dtd
index 4278a9e643..89058f5d85 100644
--- a/lib/edoc/priv/edoc.dtd
+++ b/lib/edoc/priv/edoc.dtd
@@ -103,7 +103,7 @@
<!ATTLIST type name CDATA #IMPLIED>
<!ELEMENT union (typevar | atom | integer | float | nil | list | tuple |
- fun | record | abstype)+>
+ fun | record | map | abstype)+>
<!ELEMENT typevar EMPTY>
<!ATTLIST typevar name CDATA #REQUIRED>
@@ -129,6 +129,10 @@
<!ELEMENT field (atom, type)>
+<!ELEMENT map (map_field)*>
+
+<!ELEMENT map_field (type, type)>
+
<!ELEMENT abstype (erlangName, type*)>
<!ATTLIST abstype
href CDATA #IMPLIED>
diff --git a/lib/edoc/src/edoc_layout.erl b/lib/edoc/src/edoc_layout.erl
index f723cd8373..e86d090b13 100644
--- a/lib/edoc/src/edoc_layout.erl
+++ b/lib/edoc/src/edoc_layout.erl
@@ -898,8 +898,20 @@ t_map(Es) ->
Fs = get_elem(map_field, Es),
["#{"] ++ seq(fun t_map_field/1, Fs, ["}"]).
-t_map_field(#xmlElement{content = [K,V]}) ->
- t_utype_elem(K) ++ [" => "] ++ t_utype_elem(V).
+t_map_field(#xmlElement{content = [K,V]}=E) ->
+ KElem = t_utype_elem(K),
+ VElem = t_utype_elem(V),
+ AT = get_attrval(assoc_type, E),
+ IsAny = fun(["any","()"]) -> true; (_) -> false end,
+ case AT =:= "assoc" andalso IsAny(KElem) andalso IsAny(VElem) of
+ true -> "...";
+ false ->
+ AS = case AT of
+ "assoc" -> " => ";
+ "exact" -> " := "
+ end,
+ KElem ++ [AS] ++ VElem
+ end.
t_record(E, Es) ->
Name = ["#"] ++ t_type(get_elem(atom, Es)),
@@ -1133,8 +1145,12 @@ ot_tuple(Es) ->
ot_map(Es) ->
{type,0,map,[ot_map_field(E) || E <- get_elem(map_field,Es)]}.
-ot_map_field(#xmlElement{content=[K,V]}) ->
- {type,0,map_field_assoc,ot_utype_elem(K), ot_utype_elem(V)}.
+ot_map_field(#xmlElement{content=[K,V]}=E) ->
+ A = case get_attrval(assoc_type, E) of
+ "assoc" -> map_field_assoc;
+ "exact" -> map_field_exact
+ end,
+ {type,0,A,[ot_utype_elem(K), ot_utype_elem(V)]}.
ot_fun(Es) ->
Range = ot_utype(get_elem(type, Es)),
diff --git a/lib/edoc/src/edoc_parser.yrl b/lib/edoc/src/edoc_parser.yrl
index 835e7ccaa6..983e2f8496 100644
--- a/lib/edoc/src/edoc_parser.yrl
+++ b/lib/edoc/src/edoc_parser.yrl
@@ -36,8 +36,8 @@ Terminals
atom float integer var an_var string start_spec start_typedef start_throws
start_ref
-'(' ')' ',' '.' '=>' '->' '{' '}' '[' ']' '|' '+' ':' '::' '=' '/' '//' '*'
-'#' 'where' '<<' '>>' '..' '...'.
+'(' ')' ',' '.' '=>' ':=' '->' '{' '}' '[' ']' '|' '+' ':' '::' '=' '/' '//'
+'*' '#' 'where' '<<' '>>' '..' '...'.
Rootsymbol start.
@@ -76,7 +76,15 @@ utype_map_fields -> '$empty' : [].
utype_map_fields -> utype_map_field : ['$1'].
utype_map_fields -> utype_map_fields ',' utype_map_field : ['$3' | '$1'].
-utype_map_field -> utype '=>' utype : #t_map_field{ k_type = '$1', v_type = '$3'}.
+utype_map_field -> utype '=>' utype : #t_map_field{assoc_type = assoc,
+ k_type = '$1',
+ v_type = '$3'}.
+utype_map_field -> utype ':=' utype : #t_map_field{assoc_type = exact,
+ k_type = '$1',
+ v_type = '$3'}.
+utype_map_field -> '...' : #t_map_field{assoc_type = assoc,
+ k_type = any(),
+ v_type = any()}.
utype_tuple -> '{' utypes '}' : lists:reverse('$2').
@@ -346,6 +354,9 @@ all_vars([#t_var{} | As]) ->
all_vars(As) ->
As =:= [].
+any() ->
+ #t_type{name = #t_name{name = any}, args = []}.
+
%% ---------------------------------------------------------------------
%% @doc EDoc type specification parsing. Parses the content of
diff --git a/lib/edoc/src/edoc_scanner.erl b/lib/edoc/src/edoc_scanner.erl
index 36423d63f8..f1d5e1d4b9 100644
--- a/lib/edoc/src/edoc_scanner.erl
+++ b/lib/edoc/src/edoc_scanner.erl
@@ -146,6 +146,8 @@ scan1([$>,$>|Cs], Toks, Pos) ->
scan1(Cs, [{'>>',Pos}|Toks], Pos);
scan1([$-,$>|Cs], Toks, Pos) ->
scan1(Cs, [{'->',Pos}|Toks], Pos);
+scan1([$:,$=|Cs], Toks, Pos) ->
+ scan1(Cs, [{':=',Pos}|Toks], Pos);
scan1([$:,$:|Cs], Toks, Pos) ->
scan1(Cs, [{'::',Pos}|Toks], Pos);
scan1([$/,$/|Cs], Toks, Pos) ->
diff --git a/lib/edoc/src/edoc_specs.erl b/lib/edoc/src/edoc_specs.erl
index faee8adf7b..c15dfd328f 100644
--- a/lib/edoc/src/edoc_specs.erl
+++ b/lib/edoc/src/edoc_specs.erl
@@ -369,11 +369,11 @@ d2e({type,_,map,any}, _Prec) ->
d2e({type,_,map,Es}, _Prec) ->
#t_map{types = d2e(Es) };
d2e({type,_,map_field_assoc,[K,V]}, Prec) ->
- T = #t_map_field{k_type = d2e(K), v_type=d2e(V) },
+ T = #t_map_field{assoc_type = assoc, k_type = d2e(K), v_type=d2e(V) },
{P,_R} = erl_parse:type_preop_prec('#'),
maybe_paren(P, Prec, T);
-d2e({type,_,map_field_exact,K,V}, Prec) ->
- T = #t_map_field{k_type = d2e(K), v_type=d2e(V) },
+d2e({type,_,map_field_exact,[K,V]}, Prec) ->
+ T = #t_map_field{assoc_type = exact, k_type = d2e(K), v_type=d2e(V) },
{P,_R} = erl_parse:type_preop_prec('#'),
maybe_paren(P, Prec, T);
d2e({type,_,tuple,Ts0}, _Prec) ->
diff --git a/lib/edoc/src/edoc_types.erl b/lib/edoc/src/edoc_types.erl
index 65fba61a72..5bb68e79fb 100644
--- a/lib/edoc/src/edoc_types.erl
+++ b/lib/edoc/src/edoc_types.erl
@@ -89,8 +89,8 @@ to_xml(#t_fun{args = As, range = T}, Env) ->
wrap_utype(T, Env)]};
to_xml(#t_map{ types = Ts}, Env) ->
{map, map(fun to_xml/2, Ts, Env)};
-to_xml(#t_map_field{ k_type=K, v_type=V}, Env) ->
- {map_field, [wrap_utype(K,Env), wrap_utype(V, Env)]};
+to_xml(#t_map_field{assoc_type = AT, k_type=K, v_type=V}, Env) ->
+ {map_field, [{assoc_type, AT}], [wrap_utype(K,Env), wrap_utype(V, Env)]};
to_xml(#t_tuple{types = Ts}, Env) ->
{tuple, map(fun wrap_utype/2, Ts, Env)};
to_xml(#t_list{type = T}, Env) ->
diff --git a/lib/edoc/src/edoc_types.hrl b/lib/edoc/src/edoc_types.hrl
index 7fec10d936..3e5e91484f 100644
--- a/lib/edoc/src/edoc_types.hrl
+++ b/lib/edoc/src/edoc_types.hrl
@@ -157,5 +157,5 @@
-record(t_paren, {a=[], type}). % parentheses
-record(t_map, {a=[], types=[]}).
--record(t_map_field, {a=[], k_type, v_type}).
+-record(t_map_field, {a=[], assoc_type, k_type, v_type}).
diff --git a/lib/erl_docgen/src/docgen_otp_specs.erl b/lib/erl_docgen/src/docgen_otp_specs.erl
index e154323f07..5bc3be7a8d 100644
--- a/lib/erl_docgen/src/docgen_otp_specs.erl
+++ b/lib/erl_docgen/src/docgen_otp_specs.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2015. All Rights Reserved.
+%% Copyright Ericsson AB 1996-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.
@@ -389,10 +389,10 @@ t_type([#xmlElement{name = list, content = Es}]) ->
t_list(Es);
t_type([#xmlElement{name = nonempty_list, content = Es}]) ->
t_nonempty_list(Es);
-t_type([#xmlElement{name = tuple, content = Es}]) ->
- t_tuple(Es);
t_type([#xmlElement{name = map, content = Es}]) ->
t_map(Es);
+t_type([#xmlElement{name = tuple, content = Es}]) ->
+ t_tuple(Es);
t_type([#xmlElement{name = 'fun', content = Es}]) ->
["fun("] ++ t_fun(Es) ++ [")"];
t_type([E = #xmlElement{name = record, content = Es}]) ->
@@ -435,16 +435,28 @@ t_nonempty_list(Es) ->
t_tuple(Es) ->
["{"] ++ seq(fun t_utype_elem/1, Es, ["}"]).
+t_fun(Es) ->
+ ["("] ++ seq(fun t_utype_elem/1, get_content(argtypes, Es),
+ [") -> "] ++ t_utype(get_elem(type, Es))).
+
t_map(Es) ->
Fs = get_elem(map_field, Es),
["#{"] ++ seq(fun t_map_field/1, Fs, ["}"]).
-t_map_field(#xmlElement{content = [K,V]}) ->
- [t_utype_elem(K) ++ " => " ++ t_utype_elem(V)].
-
-t_fun(Es) ->
- ["("] ++ seq(fun t_utype_elem/1, get_content(argtypes, Es),
- [") -> "] ++ t_utype(get_elem(type, Es))).
+t_map_field(#xmlElement{content = [K,V]}=E) ->
+ KElem = t_utype_elem(K),
+ VElem = t_utype_elem(V),
+ AT = get_attrval(assoc_type, E),
+ IsAny = fun(["any","()"]) -> true; (_) -> false end,
+ case AT =:= "assoc" andalso IsAny(KElem) andalso IsAny(VElem) of
+ true -> "...";
+ false ->
+ AS = case AT of
+ "assoc" -> " => ";
+ "exact" -> " := "
+ end,
+ KElem ++ [AS] ++ VElem
+ end.
t_record(E, Es) ->
Name = ["#"] ++ t_type(get_elem(atom, Es)),
@@ -618,8 +630,12 @@ ot_tuple(Es) ->
ot_map(Es) ->
{type,0,map,[ot_map_field(E) || E <- get_elem(map_field,Es)]}.
-ot_map_field(#xmlElement{content=[K,V]}) ->
- {type,0,map_field_assoc,[ot_utype_elem(K),ot_utype_elem(V)]}.
+ot_map_field(#xmlElement{content=[K,V]}=E) ->
+ A = case get_attrval(assoc_type, E) of
+ "assoc" -> map_field_assoc;
+ "exact" -> map_field_exact
+ end,
+ {type,0,A,[ot_utype_elem(K), ot_utype_elem(V)]}.
ot_fun(Es) ->
Range = ot_utype(get_elem(type, Es)),
diff --git a/lib/erl_interface/src/misc/ei_portio.h b/lib/erl_interface/src/misc/ei_portio.h
index a14fdbd7d1..fbb61b0ccf 100644
--- a/lib/erl_interface/src/misc/ei_portio.h
+++ b/lib/erl_interface/src/misc/ei_portio.h
@@ -21,6 +21,12 @@
*/
#ifndef _EI_PORTIO_H
#define _EI_PORTIO_H
+#if !defined(__WIN32__) || !defined(VXWORKS)
+#ifdef HAVE_WRITEV
+/* Declaration of struct iovec *iov should be visible in this scope. */
+#include <sys/uio.h>
+#endif
+#endif
int ei_accept_t(int fd, void *addr, void *addrlen, unsigned ms);
int ei_connect_t(int fd, void *sinp, int sin_siz, unsigned ms);
@@ -29,8 +35,7 @@ int ei_write_fill(int fd, const char *buf, int len);
int ei_read_fill_t(int fd, char* buf, int len, unsigned ms);
int ei_write_fill_t(int fd, const char *buf, int len, unsigned ms);
#ifdef HAVE_WRITEV
-int ei_writev_fill_t(int fd, const struct iovec *iov, int iovcnt,
- unsigned ms);
+int ei_writev_fill_t(int fd, const struct iovec *iov, int iovcnt, unsigned ms);
#endif
#endif /* _EI_PORTIO_H */
diff --git a/lib/kernel/doc/src/gen_tcp.xml b/lib/kernel/doc/src/gen_tcp.xml
index b75d42d198..88135ea43d 100644
--- a/lib/kernel/doc/src/gen_tcp.xml
+++ b/lib/kernel/doc/src/gen_tcp.xml
@@ -153,17 +153,17 @@ do_recv(Sock, Bs) ->
<c><anno>Address</anno></c> can be a hostname or an IP address.</p>
<p>The following options are available:</p>
<taglist>
- <tag><c>{ip, ip_address()}</c></tag>
+ <tag><c>{ip, Address}</c></tag>
<item><p>If the host has many network interfaces, this option
specifies which one to use.</p></item>
- <tag><c>{ifaddr, ip_address()}</c></tag>
- <item><p>Same as <c>{ip, ip_address()}</c>. If the host has many
+ <tag><c>{ifaddr, Address}</c></tag>
+ <item><p>Same as <c>{ip, Address}</c>. If the host has many
network interfaces, this option specifies which one to use.</p>
</item>
<tag><c>{fd, integer() >= 0}</c></tag>
<item><p>If a socket has somehow been connected without using
<c>gen_tcp</c>, use this option to pass the file descriptor
- for it. If <c>{ip, ip_address()}</c> and/or
+ for it. If <c>{ip, Address}</c> and/or
<c>{port, port_number()}</c> is combined with this option, the
<c>fd</c> is bound to the specified interface and port before
connecting. If these options are not specified, it is assumed that
@@ -175,9 +175,10 @@ do_recv(Sock, Bs) ->
<tag><c>local</c></tag>
<item>
<p>
- Sets up the socket for local address family. This option is only
- valid together with <c>{fd, integer()}</c> when the file descriptor
- is of local address family (e.g. a Unix Domain Socket)
+ Sets up a Unix Domain Socket. See
+ <seealso marker="inet#type-local_address">
+ <c>inet:local_address()</c>
+ </seealso>
</p>
</item>
<tag><c>{port, Port}</c></tag>
@@ -254,7 +255,7 @@ do_recv(Sock, Bs) ->
<item><p><c>B</c> is an integer &gt;= <c>0</c>. The backlog value
defines the maximum length that the queue of pending connections
can grow to. Defaults to <c>5</c>.</p></item>
- <tag><c>{ip, ip_address()}</c></tag>
+ <tag><c>{ip, Address}</c></tag>
<item><p>If the host has many network interfaces, this option
specifies which one to listen on.</p></item>
<tag><c>{port, Port}</c></tag>
@@ -263,8 +264,8 @@ do_recv(Sock, Bs) ->
<item><p>If a socket has somehow been connected without using
<c>gen_tcp</c>, use this option to pass the file
descriptor for it.</p></item>
- <tag><c>{ifaddr, ip_address()}</c></tag>
- <item><p>Same as <c>{ip, ip_address()}</c>. If the host has many
+ <tag><c>{ifaddr, Address}</c></tag>
+ <item><p>Same as <c>{ip, Address}</c>. If the host has many
network interfaces, this option specifies which one to use.</p>
</item>
<tag><c>inet6</c></tag>
diff --git a/lib/kernel/doc/src/gen_udp.xml b/lib/kernel/doc/src/gen_udp.xml
index ca9d9c978c..3f88a0272d 100644
--- a/lib/kernel/doc/src/gen_udp.xml
+++ b/lib/kernel/doc/src/gen_udp.xml
@@ -85,11 +85,11 @@
<item><p>Received <c>Packet</c> is delivered as a list.</p></item>
<tag><c>binary</c></tag>
<item><p>Received <c>Packet</c> is delivered as a binary.</p></item>
- <tag><c>{ip, ip_address()}</c></tag>
+ <tag><c>{ip, Address}</c></tag>
<item><p>If the host has many network interfaces, this option
specifies which one to use.</p></item>
- <tag><c>{ifaddr, ip_address()}</c></tag>
- <item><p>Same as <c>{ip, ip_address()}</c>. If the host has many
+ <tag><c>{ifaddr, Address}</c></tag>
+ <item><p>Same as <c>{ip, Address}</c>. If the host has many
network interfaces, this option specifies which one to
use.</p></item>
<tag><c>{fd, integer() >= 0}</c></tag>
@@ -107,9 +107,10 @@
<tag><c>local</c></tag>
<item>
<p>
- Sets up the socket for local address family. This option is only
- valid together with <c>{fd, integer()}</c> when the file descriptor
- is of local address family (e.g. a Unix Domain Socket)
+ Sets up a Unix Domain Socket. See
+ <seealso marker="inet#type-local_address">
+ <c>inet:local_address()</c>
+ </seealso>
</p>
</item>
<tag><c>{udp_module, module()}</c></tag>
@@ -184,8 +185,10 @@
<name name="send" arity="4"/>
<fsummary>Send a packet.</fsummary>
<desc>
- <p>Sends a packet to the specified address and port. Argument
- <c><anno>Address</anno></c> can be a hostname or an IP address.</p>
+ <p>
+ Sends a packet to the specified address and port. Argument
+ <c><anno>Address</anno></c> can be a hostname or a socket address.
+ </p>
</desc>
</func>
</funcs>
diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml
index 5ff167bcb3..c0dce2f50c 100644
--- a/lib/kernel/doc/src/inet.xml
+++ b/lib/kernel/doc/src/inet.xml
@@ -118,6 +118,59 @@ fe80::204:acff:fe17:bf38
<name name="port_number"/>
</datatype>
<datatype>
+ <name name="local_address"/>
+ <desc>
+ <p>
+ This address family only works on Unix-like systems.
+ </p>
+ <p>
+ <c><anno>File</anno></c> is normally a file pathname
+ in a local filesystem. It is limited in length by the
+ operating system, traditionally to 108 bytes.
+ </p>
+ <p>
+ A <c>binary()</c> is passed as is to the operating system,
+ but a <c>string()</c> is encoded according to the
+ <seealso marker="file#native_name_encoding/0">
+ system filename encoding mode.
+ </seealso>
+ </p>
+ <p>
+ Other addresses are possible, for example Linux implements
+ "Abstract Addresses". See the documentation for
+ Unix Domain Sockets on your system,
+ normally <c>unix</c> in manual section 7.
+ </p>
+ <p>
+ In most API functions where you can use
+ this address family the port number must be <c>0</c>.
+ </p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="socket_address"/>
+ </datatype>
+ <datatype>
+ <name name="returned_non_ip_address"/>
+ <desc>
+ <p>
+ Addresses besides
+ <seealso marker="#type-ip_address">
+ <c>ip_address()</c>
+ </seealso>
+ ones that are returned from socket API functions.
+ See in particular
+ <seealso marker="#type-local_address">
+ <c>local_address()</c>.
+ </seealso>
+ The <c>unspec</c> family corresponds to AF_UNSPEC and can
+ occur if the other side has no socket address.
+ The <c>undefined</c> family can only occur in the unlikely
+ event of an address family that the VM does not recognize.
+ </p>
+ </desc>
+ </datatype>
+ <datatype>
<name name="posix"/>
<desc>
<p>An atom that is named from the POSIX error codes used in Unix,
diff --git a/lib/kernel/src/gen_tcp.erl b/lib/kernel/src/gen_tcp.erl
index 2b3afcd44c..1a21541b7c 100644
--- a/lib/kernel/src/gen_tcp.erl
+++ b/lib/kernel/src/gen_tcp.erl
@@ -96,17 +96,17 @@
tos |
ipv6_v6only.
-type connect_option() ::
- {ip, inet:ip_address()} |
+ {ip, inet:socket_address()} |
{fd, Fd :: non_neg_integer()} |
- {ifaddr, inet:ip_address()} |
+ {ifaddr, inet:socket_address()} |
inet:address_family() |
{port, inet:port_number()} |
{tcp_module, module()} |
option().
-type listen_option() ::
- {ip, inet:ip_address()} |
+ {ip, inet:socket_address()} |
{fd, Fd :: non_neg_integer()} |
- {ifaddr, inet:ip_address()} |
+ {ifaddr, inet:socket_address()} |
inet:address_family() |
{port, inet:port_number()} |
{backlog, B :: non_neg_integer()} |
@@ -122,7 +122,7 @@
%%
-spec connect(Address, Port, Options) -> {ok, Socket} | {error, Reason} when
- Address :: inet:ip_address() | inet:hostname(),
+ Address :: inet:socket_address() | inet:hostname(),
Port :: inet:port_number(),
Options :: [connect_option()],
Socket :: socket(),
@@ -133,7 +133,7 @@ connect(Address, Port, Opts) ->
-spec connect(Address, Port, Options, Timeout) ->
{ok, Socket} | {error, Reason} when
- Address :: inet:ip_address() | inet:hostname(),
+ Address :: inet:socket_address() | inet:hostname(),
Port :: inet:port_number(),
Options :: [connect_option()],
Timeout :: timeout(),
diff --git a/lib/kernel/src/gen_udp.erl b/lib/kernel/src/gen_udp.erl
index 2227bb3562..98d2f0bcfb 100644
--- a/lib/kernel/src/gen_udp.erl
+++ b/lib/kernel/src/gen_udp.erl
@@ -92,9 +92,9 @@ open(Port) ->
-spec open(Port, Opts) -> {ok, Socket} | {error, Reason} when
Port :: inet:port_number(),
Opts :: [Option],
- Option :: {ip, inet:ip_address()}
+ Option :: {ip, inet:socket_address()}
| {fd, non_neg_integer()}
- | {ifaddr, inet:ip_address()}
+ | {ifaddr, inet:socket_address()}
| inet:address_family()
| {port, inet:port_number()}
| option(),
@@ -114,7 +114,7 @@ close(S) ->
-spec send(Socket, Address, Port, Packet) -> ok | {error, Reason} when
Socket :: socket(),
- Address :: inet:ip_address() | inet:hostname(),
+ Address :: inet:socket_address() | inet:hostname(),
Port :: inet:port_number(),
Packet :: iodata(),
Reason :: not_owner | inet:posix().
@@ -148,7 +148,7 @@ send(S, Packet) when is_port(S) ->
{ok, {Address, Port, Packet}} | {error, Reason} when
Socket :: socket(),
Length :: non_neg_integer(),
- Address :: inet:ip_address(),
+ Address :: inet:ip_address() | inet:returned_non_ip_address(),
Port :: inet:port_number(),
Packet :: string() | binary(),
Reason :: not_owner | inet:posix().
@@ -166,7 +166,7 @@ recv(S,Len) when is_port(S), is_integer(Len) ->
Socket :: socket(),
Length :: non_neg_integer(),
Timeout :: timeout(),
- Address :: inet:ip_address(),
+ Address :: inet:ip_address() | inet:returned_non_ip_address(),
Port :: inet:port_number(),
Packet :: string() | binary(),
Reason :: not_owner | inet:posix().
diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl
index de43ea792b..9fc685e728 100644
--- a/lib/kernel/src/inet.erl
+++ b/lib/kernel/src/inet.erl
@@ -73,9 +73,9 @@
-export([start_timer/1, timeout/1, timeout/2, stop_timer/1]).
-export_type([address_family/0, hostent/0, hostname/0, ip4_address/0,
- ip6_address/0, ip_address/0, posix/0, socket/0,
- port_number/0]).
-
+ ip6_address/0, ip_address/0, port_number/0,
+ local_address/0, socket_address/0, returned_non_ip_address/0,
+ posix/0, socket/0, stat_option/0]).
%% imports
-import(lists, [append/1, duplicate/2, filter/2, foldl/3]).
@@ -98,6 +98,11 @@
0..65535,0..65535,0..65535,0..65535}.
-type ip_address() :: ip4_address() | ip6_address().
-type port_number() :: 0..65535.
+-type local_address() :: {local, File :: binary() | string()}.
+-type returned_non_ip_address() ::
+ {local, binary()} |
+ {unspec, <<>>} |
+ {undefined, any()}.
-type posix() :: exbadport | exbadseq | file:posix().
-type socket() :: port().
@@ -138,7 +143,7 @@
-type socket_protocol() :: 'tcp' | 'udp' | 'sctp'.
-type socket_type() :: 'stream' | 'dgram' | 'seqpacket'.
-type socket_address() ::
- ip_address() | {address_family(), any()} | 'any' | 'loopback'.
+ ip_address() | 'any' | 'loopback' | local_address().
-type stat_option() ::
'recv_cnt' | 'recv_max' | 'recv_avg' | 'recv_oct' | 'recv_dvi' |
'send_cnt' | 'send_max' | 'send_avg' | 'send_oct' | 'send_pend'.
@@ -163,26 +168,33 @@ close(Socket) ->
end.
--spec peername(Socket) -> {ok, {Address, Port}} | {error, posix()} when
- Socket :: socket(),
- Address :: ip_address(),
- Port :: non_neg_integer().
+-spec peername(Socket :: socket()) ->
+ {ok,
+ {ip_address(), port_number()} |
+ returned_non_ip_address()} |
+ {error, posix()}.
peername(Socket) ->
prim_inet:peername(Socket).
--spec setpeername(Socket :: socket(), Address :: {ip_address(), port_number()}) ->
- 'ok' | {'error', any()}.
+-spec setpeername(
+ Socket :: socket(),
+ Address ::
+ {ip_address() | 'any' | 'loopback',
+ port_number()} |
+ socket_address()) ->
+ 'ok' | {'error', any()}.
setpeername(Socket, {IP,Port}) ->
prim_inet:setpeername(Socket, {IP,Port});
setpeername(Socket, undefined) ->
prim_inet:setpeername(Socket, undefined).
--spec peernames(Socket) -> {ok, [{Address, Port}]} | {error, posix()} when
- Socket :: socket(),
- Address :: ip_address(),
- Port :: non_neg_integer().
+-spec peernames(Socket :: socket()) ->
+ {ok,
+ [{ip_address(), port_number()} |
+ returned_non_ip_address()]} |
+ {error, posix()}.
peernames(Socket) ->
prim_inet:peernames(Socket).
@@ -198,15 +210,21 @@ peernames(Socket, Assoc) ->
prim_inet:peernames(Socket, Assoc).
--spec sockname(Socket) -> {ok, {Address, Port}} | {error, posix()} when
- Socket :: socket(),
- Address :: ip_address(),
- Port :: non_neg_integer().
+-spec sockname(Socket :: socket()) ->
+ {ok,
+ {ip_address(), port_number()} |
+ returned_non_ip_address()} |
+ {error, posix()}.
sockname(Socket) ->
prim_inet:sockname(Socket).
--spec setsockname(Socket :: socket(), Address :: {ip_address(), port_number()}) ->
+-spec setsockname(
+ Socket :: socket(),
+ Address ::
+ {ip_address() | 'any' | 'loopback',
+ port_number()} |
+ socket_address()) ->
'ok' | {'error', any()}.
setsockname(Socket, {IP,Port}) ->
@@ -214,10 +232,11 @@ setsockname(Socket, {IP,Port}) ->
setsockname(Socket, undefined) ->
prim_inet:setsockname(Socket, undefined).
--spec socknames(Socket) -> {ok, [{Address, Port}]} | {error, posix()} when
- Socket :: socket(),
- Address :: ip_address(),
- Port :: non_neg_integer().
+-spec socknames(Socket :: socket()) ->
+ {ok,
+ [{ip_address(), port_number()} |
+ returned_non_ip_address()]} |
+ {error, posix()}.
socknames(Socket) ->
prim_inet:socknames(Socket).
@@ -1296,7 +1315,17 @@ gethostbyaddr_tm_native(Addr, Timer, Opts) ->
end.
-spec open(Fd_or_OpenOpts :: integer() | list(),
- Addr :: socket_address(),
+ Addr ::
+ socket_address() |
+ {ip_address() | 'any' | 'loopback', % Unofficial
+ port_number()} |
+ {inet, % Unofficial
+ {ip4_address() | 'any' | 'loopback',
+ port_number()}} |
+ {inet6, % Unofficial
+ {ip6_address() | 'any' | 'loopback',
+ port_number()}} |
+ undefined, % Internal - no bind()
Port :: port_number(),
Opts :: [socket_setopt()],
Protocol :: socket_protocol(),
@@ -1316,11 +1345,16 @@ open(FdO, Addr, Port, Opts, Protocol, Family, Type, Module)
{ok,S} ->
case prim_inet:setopts(S, Opts) of
ok ->
- case if is_list(Addr) ->
- bindx(S, Addr, Port);
- true ->
- prim_inet:bind(S, Addr, Port)
- end of
+ case
+ case Addr of
+ undefined ->
+ {ok, undefined};
+ _ when is_list(Addr) ->
+ bindx(S, Addr, Port);
+ _ ->
+ prim_inet:bind(S, Addr, Port)
+ end
+ of
{ok, _} ->
inet_db:register_socket(S, Module),
{ok,S};
diff --git a/lib/kernel/src/inet_int.hrl b/lib/kernel/src/inet_int.hrl
index 32d09fb63c..c8a8962e78 100644
--- a/lib/kernel/src/inet_int.hrl
+++ b/lib/kernel/src/inet_int.hrl
@@ -25,6 +25,7 @@
%%
%% family codes to open
+-define(INET_AF_UNSPEC, 0).
-define(INET_AF_INET, 1).
-define(INET_AF_INET6, 2).
-define(INET_AF_ANY, 3). % Fake for ANY in any address family
diff --git a/lib/kernel/src/local_tcp.erl b/lib/kernel/src/local_tcp.erl
index 64085ec42e..e3c67dfbb7 100644
--- a/lib/kernel/src/local_tcp.erl
+++ b/lib/kernel/src/local_tcp.erl
@@ -107,8 +107,14 @@ do_connect(Addr = {?FAMILY, _}, 0, Opts, Time) ->
when tuple_size(BAddr) =:= 2, element(1, BAddr) =:= ?FAMILY;
BAddr =:= any ->
case inet:open(
- Fd, BAddr, 0, SockOpts,
- ?PROTO, ?FAMILY, ?TYPE, ?MODULE) of
+ Fd,
+ case BAddr of
+ any ->
+ undefined;
+ _ ->
+ BAddr
+ end,
+ 0, SockOpts, ?PROTO, ?FAMILY, ?TYPE, ?MODULE) of
{ok, S} ->
case prim_inet:connect(S, Addr, 0, Time) of
ok -> {ok,S};
diff --git a/lib/kernel/src/local_udp.erl b/lib/kernel/src/local_udp.erl
index ebb4d2b33f..481a8c4910 100644
--- a/lib/kernel/src/local_udp.erl
+++ b/lib/kernel/src/local_udp.erl
@@ -59,7 +59,14 @@ open(0, Opts) ->
when tuple_size(BAddr) =:= 2, element(1, BAddr) =:= ?FAMILY;
BAddr =:= any ->
inet:open(
- Fd, BAddr, 0, SockOpts, ?PROTO, ?FAMILY, ?TYPE, ?MODULE);
+ Fd,
+ case BAddr of
+ any ->
+ undefined;
+ _ ->
+ BAddr
+ end,
+ 0, SockOpts, ?PROTO, ?FAMILY, ?TYPE, ?MODULE);
{ok, _} -> exit(badarg)
end.
diff --git a/lib/observer/src/crashdump_viewer.erl b/lib/observer/src/crashdump_viewer.erl
index de52e2a995..9268dac180 100644
--- a/lib/observer/src/crashdump_viewer.erl
+++ b/lib/observer/src/crashdump_viewer.erl
@@ -1491,6 +1491,9 @@ get_portinfo(Fd,Port) ->
"Port controls linked-in driver" ->
Str = lists:flatten(["Linked in driver: " | val(Fd)]),
get_portinfo(Fd,Port#port{controls=Str});
+ "Port controls forker process" ->
+ Str = lists:flatten(["Forker process: " | val(Fd)]),
+ get_portinfo(Fd,Port#port{controls=Str});
"Port controls external process" ->
Str = lists:flatten(["External proc: " | val(Fd)]),
get_portinfo(Fd,Port#port{controls=Str});
diff --git a/lib/observer/src/observer_procinfo.erl b/lib/observer/src/observer_procinfo.erl
index fe2aa11450..620979dcc9 100644
--- a/lib/observer/src/observer_procinfo.erl
+++ b/lib/observer/src/observer_procinfo.erl
@@ -317,7 +317,7 @@ fetch_state_info2(Pid, M) ->
of
{status, _, {module, _},
[_PDict, _SysState, _Parent, _Dbg,
- [Header,{data, First},{data, Second}]]} ->
+ [Header,{data, First},{data, Second}|_]]} ->
[{"Behaviour", B}, Header] ++ First ++ Second;
{status, _, {module, _},
[_PDict, _SysState, _Parent, _Dbg,
diff --git a/lib/sasl/test/release_handler_SUITE.erl b/lib/sasl/test/release_handler_SUITE.erl
index 4dcaec03a7..8134e02221 100644
--- a/lib/sasl/test/release_handler_SUITE.erl
+++ b/lib/sasl/test/release_handler_SUITE.erl
@@ -1366,7 +1366,7 @@ upgrade_supervisor(Conf) when is_list(Conf) ->
ASupBeam2 = rpc:call(Node, code, which, [a_sup]),
%% Check that the restart strategy and child spec is updated
- {status, _, {module, _}, [_, _, _, _, [_,_,{data,[{"State",State}]}]]} =
+ {status, _, {module, _}, [_, _, _, _, [_,_,{data,[{"State",State}]}|_]]} =
rpc:call(Node,sys,get_status,[a_sup]),
{state,_,RestartStrategy,[Child],_,_,_,_,_,_,_} = State,
one_for_all = RestartStrategy, % changed from one_for_one
diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml
index 62f6263e9e..bed82cdb91 100644
--- a/lib/ssl/doc/src/ssl.xml
+++ b/lib/ssl/doc/src/ssl.xml
@@ -901,6 +901,23 @@ fun(srp, Username :: string(), UserState :: term()) ->
</func>
<func>
+ <name>getstat(Socket) ->
+ {ok, OptionValues} | {error, inet:posix()}</name>
+ <name>getstat(Socket, OptionNames) ->
+ {ok, OptionValues} | {error, inet:posix()}</name>
+ <fsummary>Get one or more statistic options for a socket</fsummary>
+ <type>
+ <v>Socket = sslsocket()</v>
+ <v>OptionNames = [atom()]</v>
+ <v>OptionValues = [{inet:stat_option(), integer()}]</v>
+ </type>
+ <desc>
+ <p>Gets one or more statistic options for the underlying TCP socket.</p>
+ <p>See inet:getstat/2 for statistic options description.</p>
+ </desc>
+ </func>
+
+ <func>
<name>listen(Port, Options) ->
{ok, ListenSocket} | {error, Reason}</name>
<fsummary>Creates an SSL listen socket.</fsummary>
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index 0058e5ec9a..d2aeb3258f 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -34,7 +34,8 @@
listen/2, transport_accept/1, transport_accept/2,
ssl_accept/1, ssl_accept/2, ssl_accept/3,
controlling_process/2, peername/1, peercert/1, sockname/1,
- close/1, close/2, shutdown/2, recv/2, recv/3, send/2, getopts/2, setopts/2
+ close/1, close/2, shutdown/2, recv/2, recv/3, send/2,
+ getopts/2, setopts/2, getstat/1, getstat/2
]).
%% SSL/TLS protocol handling
-export([cipher_suites/0, cipher_suites/1,
@@ -469,6 +470,32 @@ setopts(#sslsocket{}, Options) ->
{error, {options,{not_a_proplist, Options}}}.
%%---------------------------------------------------------------
+-spec getstat(Socket) ->
+ {ok, OptionValues} | {error, inet:posix()} when
+ Socket :: #sslsocket{},
+ OptionValues :: [{inet:stat_option(), integer()}].
+%%
+%% Description: Get all statistic options for a socket.
+%%--------------------------------------------------------------------
+getstat(Socket) ->
+ getstat(Socket, inet:stats()).
+
+%%---------------------------------------------------------------
+-spec getstat(Socket, Options) ->
+ {ok, OptionValues} | {error, inet:posix()} when
+ Socket :: #sslsocket{},
+ Options :: [inet:stat_option()],
+ OptionValues :: [{inet:stat_option(), integer()}].
+%%
+%% Description: Get one or more statistic options for a socket.
+%%--------------------------------------------------------------------
+getstat(#sslsocket{pid = {Listen, #config{transport_info = {Transport, _, _, _}}}}, Options) when is_port(Listen), is_list(Options) ->
+ ssl_socket:getstat(Transport, Listen, Options);
+
+getstat(#sslsocket{pid = Pid, fd = {Transport, Socket, _, _}}, Options) when is_pid(Pid), is_list(Options) ->
+ ssl_socket:getstat(Transport, Socket, Options).
+
+%%---------------------------------------------------------------
-spec shutdown(#sslsocket{}, read | write | read_write) -> ok | {error, reason()}.
%%
%% Description: Same as gen_tcp:shutdown/2
diff --git a/lib/ssl/src/ssl_socket.erl b/lib/ssl/src/ssl_socket.erl
index 95a70a4602..b2aea2ba9c 100644
--- a/lib/ssl/src/ssl_socket.erl
+++ b/lib/ssl/src/ssl_socket.erl
@@ -24,7 +24,7 @@
-include("ssl_internal.hrl").
-include("ssl_api.hrl").
--export([socket/5, setopts/3, getopts/3, peername/2, sockname/2, port/2]).
+-export([socket/5, setopts/3, getopts/3, getstat/3, peername/2, sockname/2, port/2]).
-export([emulated_options/0, internal_inet_values/0, default_inet_values/0,
init/1, start_link/3, terminate/2, inherit_tracker/3, get_emulated_opts/1,
set_emulated_opts/2, get_all_opts/1, handle_call/3, handle_cast/2,
@@ -74,6 +74,11 @@ getopts(gen_tcp, Socket, Options) ->
getopts(Transport, Socket, Options) ->
Transport:getopts(Socket, Options).
+getstat(gen_tcp, Socket, Options) ->
+ inet:getstat(Socket, Options);
+getstat(Transport, Socket, Options) ->
+ Transport:getstat(Socket, Options).
+
peername(gen_tcp, Socket) ->
inet:peername(Socket);
peername(Transport, Socket) ->
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index efe996e57c..93b6ae66dc 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -150,6 +150,7 @@ api_tests() ->
sockname,
versions,
controlling_process,
+ getstat,
close_with_timeout,
hibernate,
hibernate_right_away,
@@ -691,6 +692,75 @@ controlling_process(Config) when is_list(Config) ->
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
+getstat() ->
+ [{doc,"Test API function getstat/2"}].
+
+getstat(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server1 =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result, []}},
+ {options, [{active, false} | ServerOpts]}]),
+ Port1 = ssl_test_lib:inet_port(Server1),
+ Server2 =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result, []}},
+ {options, [{active, false} | ServerOpts]}]),
+ Port2 = ssl_test_lib:inet_port(Server2),
+ {ok, ActiveC} = rpc:call(ClientNode, ssl, connect,
+ [Hostname,Port1,[{active, once}|ClientOpts]]),
+ {ok, PassiveC} = rpc:call(ClientNode, ssl, connect,
+ [Hostname,Port2,[{active, false}|ClientOpts]]),
+
+ ct:log("Testcase ~p, Client ~p Servers ~p, ~p ~n",
+ [self(), self(), Server1, Server2]),
+
+ %% We only check that the values are non-zero initially
+ %% (due to the handshake), and that sending more changes the values.
+
+ %% Passive socket.
+
+ {ok, InitialStats} = ssl:getstat(PassiveC),
+ ct:pal("InitialStats ~p~n", [InitialStats]),
+ [true] = lists:usort([0 =/= proplists:get_value(Name, InitialStats)
+ || Name <- [recv_cnt, recv_oct, recv_avg, recv_max, send_cnt, send_oct, send_avg, send_max]]),
+
+ ok = ssl:send(PassiveC, "Hello world"),
+ wait_for_send(PassiveC),
+ {ok, SStats} = ssl:getstat(PassiveC, [send_cnt, send_oct]),
+ ct:pal("SStats ~p~n", [SStats]),
+ [true] = lists:usort([proplists:get_value(Name, SStats) =/= proplists:get_value(Name, InitialStats)
+ || Name <- [send_cnt, send_oct]]),
+
+ %% Active socket.
+
+ {ok, InitialAStats} = ssl:getstat(ActiveC),
+ ct:pal("InitialAStats ~p~n", [InitialAStats]),
+ [true] = lists:usort([0 =/= proplists:get_value(Name, InitialAStats)
+ || Name <- [recv_cnt, recv_oct, recv_avg, recv_max, send_cnt, send_oct, send_avg, send_max]]),
+
+ _ = receive
+ {ssl, ActiveC, _} ->
+ ok
+ after
+ ?SLEEP ->
+ exit(timeout)
+ end,
+
+ ok = ssl:send(ActiveC, "Hello world"),
+ wait_for_send(ActiveC),
+ {ok, ASStats} = ssl:getstat(ActiveC, [send_cnt, send_oct]),
+ ct:pal("ASStats ~p~n", [ASStats]),
+ [true] = lists:usort([proplists:get_value(Name, ASStats) =/= proplists:get_value(Name, InitialAStats)
+ || Name <- [send_cnt, send_oct]]),
+
+ ok.
+
+%%--------------------------------------------------------------------
controller_dies() ->
[{doc,"Test that the socket is closed after controlling process dies"}].
controller_dies(Config) when is_list(Config) ->
@@ -4592,4 +4662,6 @@ first_rsa_suite([{rsa, _, _, _} = Suite| _]) ->
first_rsa_suite([_ | Rest]) ->
first_rsa_suite(Rest).
-
+wait_for_send(Socket) ->
+ %% Make sure TLS process processed send message event
+ _ = ssl:connection_information(Socket).
diff --git a/lib/stdlib/src/supervisor.erl b/lib/stdlib/src/supervisor.erl
index a594c66fa7..c81e72689c 100644
--- a/lib/stdlib/src/supervisor.erl
+++ b/lib/stdlib/src/supervisor.erl
@@ -30,7 +30,7 @@
%% Internal exports
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
- terminate/2, code_change/3]).
+ terminate/2, code_change/3, format_status/2]).
-export([try_again_restart/2]).
%% For release_handler only
@@ -264,8 +264,13 @@ cast(Supervisor, Req) ->
get_callback_module(Pid) ->
{status, _Pid, {module, _Mod},
[_PDict, _SysState, _Parent, _Dbg, Misc]} = sys:get_status(Pid),
- [_Header, _Data, {data, [{"State", State}]}] = Misc,
- State#state.module.
+ case lists:keyfind(supervisor, 1, Misc) of
+ {supervisor, [{"Callback", Mod}]} ->
+ Mod;
+ _ ->
+ [_Header, _Data, {data, [{"State", State}]} | _] = Misc,
+ State#state.module
+ end.
%%% ---------------------------------------------------
%%%
@@ -1450,3 +1455,9 @@ report_progress(Child, SupName) ->
Progress = [{supervisor, SupName},
{started, extract_child(Child)}],
error_logger:info_report(progress, Progress).
+
+format_status(terminate, [_PDict, State]) ->
+ State;
+format_status(_, [_PDict, State]) ->
+ [{data, [{"State", State}]},
+ {supervisor, [{"Callback", State#state.module}]}].
diff --git a/lib/stdlib/test/id_transform_SUITE.erl b/lib/stdlib/test/id_transform_SUITE.erl
index 54f452825f..3d4ae1a189 100644
--- a/lib/stdlib/test/id_transform_SUITE.erl
+++ b/lib/stdlib/test/id_transform_SUITE.erl
@@ -61,13 +61,8 @@ id_transform(Config) when is_list(Config) ->
"erl_id_trans.erl"]),
{ok,erl_id_trans,Bin} = compile:file(File,[binary]),
{module,erl_id_trans} = code:load_binary(erl_id_trans, File, Bin),
- case test_server:purify_is_running() of
- false ->
- ct:timetrap({hours,1}),
- run_in_test_suite();
- true ->
- {skip,"Valgrind (too slow)"}
- end.
+ ct:timetrap({hours,1}),
+ run_in_test_suite().
run_in_test_suite() ->
SuperDir = filename:dirname(filename:dirname(code:which(?MODULE))),
diff --git a/lib/stdlib/test/supervisor_SUITE.erl b/lib/stdlib/test/supervisor_SUITE.erl
index 3f1aa0e7a3..cd2c6b0cbb 100644
--- a/lib/stdlib/test/supervisor_SUITE.erl
+++ b/lib/stdlib/test/supervisor_SUITE.erl
@@ -66,7 +66,7 @@
%% Misc tests
-export([child_unlink/1, tree/1, count_children/1,
- count_restarting_children/1,
+ count_restarting_children/1, get_callback_module/1,
do_not_save_start_parameters_for_temporary_children/1,
do_not_save_child_specs_for_temporary_children/1,
simple_one_for_one_scale_many_temporary_children/1,
@@ -91,7 +91,7 @@ all() ->
{group, normal_termination},
{group, shutdown_termination},
{group, abnormal_termination}, child_unlink, tree,
- count_children, count_restarting_children,
+ count_children, count_restarting_children, get_callback_module,
do_not_save_start_parameters_for_temporary_children,
do_not_save_child_specs_for_temporary_children,
simple_one_for_one_scale_many_temporary_children, temporary_bystander,
@@ -1507,6 +1507,14 @@ count_restarting_children(Config) when is_list(Config) ->
[1,0,0,0] = get_child_counts(SupPid).
%%-------------------------------------------------------------------------
+%% Test get_callback_module
+get_callback_module(Config) when is_list(Config) ->
+ Child = {child, {supervisor_1, start_child, []}, temporary, 1000,
+ worker, []},
+ {ok, SupPid} = start_link({ok, {{simple_one_for_one, 2, 3600}, [Child]}}),
+ supervisor_SUITE = supervisor:get_callback_module(SupPid).
+
+%%-------------------------------------------------------------------------
%% Temporary children shall not be restarted so they should not save
%% start parameters, as it potentially can take up a huge amount of
%% memory for no purpose.