diff options
90 files changed, 2562 insertions, 1309 deletions
diff --git a/erts/configure.in b/erts/configure.in index ac2fae70ce..81ecad4f51 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -3858,15 +3858,15 @@ if test "$enable_lttng_test" = "yes" ; then AC_COMPILE_IFELSE( [AC_LANG_PROGRAM( [#include <lttng/tracepoint.h> - #define TRACEPOINT_PROVIDER com_ericsson_otp + #define TRACEPOINT_PROVIDER org_erlang_otp TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, dummy, TP_ARGS(int, my_int), TP_FIELDS(ctf_integer(int, my_int, my_int))) #define TRACEPOINT_CREATE_PROBES #define TRACEPOINT_DEFINE], - [if(tracepoint_enabled(com_ericsson_otp,dummy)) do {} while(0)])], + [if(tracepoint_enabled(org_erlang_otp,dummy)) do {} while(0)])], [AC_MSG_RESULT([yes])], [AC_MSG_ERROR([no (available in lttng-ust v2.7)])]) if test "x$ac_cv_header_lttng_tracepoint_h" = "xyes" \ diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index e0c3fed0c2..fa13e4c142 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -2916,107 +2916,105 @@ os_prompt% </pre> <func> <name name="monitor" arity="2" clause_i="1"/> <name name="monitor" arity="2" clause_i="2"/> + <name name="monitor" arity="2" clause_i="3"/> <fsummary>Starts monitoring.</fsummary> <type name="registered_name"/> <type name="registered_process_identifier"/> <type name="monitor_process_identifier"/> + <type name="monitor_port_identifier"/> <desc> - <p>Send a monitor request of type <c><anno>Type</anno></c> to the - entity identified by <c><anno>Item</anno></c>. The caller of - <c>monitor/2</c> will later be notified by a monitor message on the - following format if the monitored state is changed:</p> + <p>Sends a monitor request of type <c><anno>Type</anno></c> to the + entity identified by <c><anno>Item</anno></c>. If the monitored entity + does not exist or when it dies, the caller of <c>monitor/2</c> will + be notified by a message on the following format:</p> <code type="none">{Tag, <anno>MonitorRef</anno>, <anno>Type</anno>, Object, Info}</code> <note><p>The monitor request is an asynchronous signal. That is, it takes time before the signal reaches its destination.</p></note> - <p>Valid <c><anno>Type</anno></c>s:</p> - <taglist> - <tag><marker id="monitor_process"/><c>process</c></tag> - <item> - <p>Monitor the existence of the process identified by - <c><anno>Item</anno></c>. Valid - <c><anno>Item</anno></c>s in combination with the - <c>process <anno>Type</anno></c> can be any of the following:</p> - <taglist> - <tag><c>pid()</c></tag> - <item> - <p>The process identifier of the process to monitor.</p> - </item> - <tag><c>{RegisteredName, Node}</c></tag> - <item> - <p>A tuple consisting of a registered name of a process and - a node name. The process residing on the node <c>Node</c> - with the registered name <c>{RegisteredName, Node}</c> will - be monitored.</p> - </item> - <tag><c>RegisteredName</c></tag> - <item> - <p>The process locally registered as <c>RegisteredName</c> - will become monitored.</p> - </item> - </taglist> - <note><p>When a registered name is used, the - process that has the registered name when the - monitor request reach its destination will be monitored. - The monitor is not effected if the registered name is - unregistered, or unregistered and later registered on another - process.</p></note> - <p>The monitor is triggered either when the monitored process - terminates, is non existing, or if the connection to it is - lost. In the case the connection to it is lost, we do not know - if it still exist or not. After this type of monitor has been - triggered, the monitor is automatically removed.</p> - <p>When the monitor is triggered a <c>'DOWN'</c> message is - sent to the monitoring process. A <c>'DOWN'</c> message has - the following pattern:</p> - <code type="none">{'DOWN', MonitorRef, Type, Object, Info}</code> - <p>Here <c>MonitorRef</c> and <c>Type</c> are the same as - described earlier, and:</p> - <taglist> - <tag><c>Object</c></tag> - <item> - <p>equals:</p> - <taglist> - <tag><c><anno>Item</anno></c></tag> - <item>If <c><anno>Item</anno></c> is specified by a - process identifier.</item> - <tag><c>{RegisteredName, Node}</c></tag> - <item>If <c><anno>Item</anno></c> is specified as - <c>RegisteredName</c>, or <c>{RegisteredName, Node}</c> - where <c>Node</c> corresponds to the node that the - monitored process resides on.</item> - </taglist> - </item> - <tag><c>Info</c></tag> - <item> - <p>Either the exit reason of the process, <c>noproc</c> - (non-existing process), or <c>noconnection</c> (no - connection to the node where the monitored process - resides).</p></item> - </taglist> - <p>The monitoring is turned off when the <c>'DOWN'</c> - message is sent or when - <seealso marker="#demonitor/1">demonitor/1</seealso> - is called.</p> - <p>If an attempt is made to monitor a process on an older node - (where remote process monitoring is not implemented or - where remote process monitoring by registered name is not - implemented), the call fails with <c>badarg</c>.</p> - <note> - <p>The format of the <c>'DOWN'</c> message changed in ERTS - version 5.2 (OTP R9B) for monitoring - <em>by registered name</em>. Element <c>Object</c> of - the <c>'DOWN'</c> message could in earlier versions - sometimes be the process identifier of the monitored process and sometimes - be the registered name. Now element <c>Object</c> is - always a tuple consisting of the registered name and - the node name. Processes on new nodes (ERTS version 5.2 - or higher) always get <c>'DOWN'</c> messages on - the new format even if they are monitoring processes on old - nodes. Processes on old nodes always get <c>'DOWN'</c> - messages on the old format.</p> - </note> - </item> - <tag><marker id="monitor_time_offset"/><c>time_offset</c></tag> + + <p><c><anno>Type</anno></c> can be one of the following atoms: + <c>process</c>, <c>port</c> or <c>time_offset</c>.</p> + + <p>A monitor is triggered only once, after that it is removed from + both monitoring process and the monitored entity. + Monitors are fired when the monitored process or port terminates, + does not exist at the moment of creation, or if the connection to + it is lost. In the case with connection, we lose knowledge about + the fact if it still exists or not. The monitoring is also turned off + when <seealso marker="#demonitor/1">demonitor/1</seealso> + is called.</p> + + <p>When monitoring by name please note, that the <c>RegisteredName</c> + is resolved to <c>pid()</c> or <c>port()</c> only once + at the moment of monitor instantiation, later changes to the name + registration will not affect the existing monitor.</p> + + <p>When a monitor is triggered, a <c>'DOWN'</c> message that has the + following pattern <c>{'DOWN', MonitorRef, Type, Object, Info}</c> + is sent to the monitoring process.</p> + + <p>In monitor message <c>MonitorRef</c> and <c>Type</c> are the same as + described earlier, and:</p> + <taglist> + <tag><c>Object</c></tag> + <item> + <p>The monitored entity, which triggered the event. When monitoring + a local process or port, <c>Object</c> will be equal to the + <c>pid()</c> or <c>port()</c> that was being monitored. When + monitoring process or port by name, <c>Object</c> will have format + <c>{RegisteredName, Node}</c> where <c>RegisteredName</c> is the + name which has been used with <c>monitor/2</c> call and + <c>Node</c> is local or remote node name (for ports monitored by + name, <c>Node</c> is always local node name).</p> + </item> + <tag><c>Info</c></tag> + <item> + <p>Either the exit reason of the process, <c>noproc</c> + (process or port did not exist at the time of monitor creation), + or <c>noconnection</c> (no connection to the node where the + monitored process resides). </p></item> + </taglist> + + <p>If an attempt is made to monitor a process on an older node + (where remote process monitoring is not implemented or + where remote process monitoring by registered name is not + implemented), the call fails with <c>badarg</c>.</p> + <note> + <p>The format of the <c>'DOWN'</c> message changed in ERTS + version 5.2 (OTP R9B) for monitoring + <em>by registered name</em>. Element <c>Object</c> of + the <c>'DOWN'</c> message could in earlier versions + sometimes be the process identifier of the monitored process and sometimes + be the registered name. Now element <c>Object</c> is + always a tuple consisting of the registered name and + the node name. Processes on new nodes (ERTS version 5.2 + or higher) always get <c>'DOWN'</c> messages on + the new format even if they are monitoring processes on old + nodes. Processes on old nodes always get <c>'DOWN'</c> + messages on the old format.</p> + </note> + + <taglist> + <tag>Monitoring a <marker id="monitor_process"/><c>process</c></tag> + <item> + <p>Creates monitor between the current process and another + process identified by <c><anno>Item</anno></c>, which can be a + <c>pid()</c> (local or remote), an atom <c>RegisteredName</c> or + a tuple <c>{RegisteredName, Node}</c> for a registered process, + located elsewhere.</p> + </item> + + <tag>Monitoring a <marker id="monitor_port"/><c>port</c></tag> + <item> + <p>Creates monitor between the current process and a port + identified by <c><anno>Item</anno></c>, which can be a + <c>port()</c> (only local), an atom <c>RegisteredName</c> or + a tuple <c>{RegisteredName, Node}</c> for a registered port, + located on this node. Note, that attempt to monitor a remote port + will result in <c>badarg</c>.</p> + </item> + + <tag>Monitoring a + <marker id="monitor_time_offset"/><c>time_offset</c></tag> <item> <p>Monitor changes in <seealso marker="#time_offset/0">time offset</seealso> @@ -3072,15 +3070,17 @@ os_prompt% </pre> Note that you can observe the change of the time offset when calling <c>erlang:time_offset()</c> before you get the <c>'CHANGE'</c> message.</p> - </item> </taglist> + <p>Making several calls to <c>monitor/2</c> for the same - <c><anno>Item</anno></c> and/or <c><anno>Type</anno></c> is not - an error; it results in as many independent monitoring instances.</p> + <c><anno>Item</anno></c> and/or <c><anno>Type</anno></c> is not + an error; it results in as many independent monitoring instances.</p> + <p>The monitor functionality is expected to be extended. That is, - other <c><anno>Type</anno></c>s and <c><anno>Item</anno></c>s - are expected to be supported in a future release.</p> + other <c><anno>Type</anno></c>s and <c><anno>Item</anno></c>s + are expected to be supported in a future release.</p> + <note> <p>If or when <c>monitor/2</c> is extended, other possible values for <c>Tag</c>, <c>Object</c> and @@ -4150,6 +4150,22 @@ os_prompt% </pre> <func> <name name="port_info" arity="2" clause_i="8"/> + <fsummary>Which processes are monitoring this port.</fsummary> + <desc> + <p>Returns list of pids that are monitoring given port at the + moment.</p> + <p>If the port identified by <c><anno>Port</anno></c> is not open, + <c>undefined</c> is returned. If the port is closed and the + calling process was previously linked to the port, the exit + signal from the port is guaranteed to be delivered before + <c>port_info/2</c> returns <c>undefined</c>.</p> + <p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local + port identifier, or an atom.</p> + </desc> + </func> + + <func> + <name name="port_info" arity="2" clause_i="9"/> <fsummary>Information about the name of a port.</fsummary> <desc> <p><c><anno>Name</anno></c> is the command name set by @@ -4165,7 +4181,7 @@ os_prompt% </pre> </func> <func> - <name name="port_info" arity="2" clause_i="9"/> + <name name="port_info" arity="2" clause_i="10"/> <fsummary>Information about the OS pid of a port.</fsummary> <desc> <p><c><anno>OsPid</anno></c> is the process identifier (or equivalent) @@ -4184,7 +4200,7 @@ os_prompt% </pre> </func> <func> - <name name="port_info" arity="2" clause_i="10"/> + <name name="port_info" arity="2" clause_i="11"/> <fsummary>Information about the output of a port.</fsummary> <desc> <p><c><anno>Bytes</anno></c> is the total number of bytes written @@ -4203,7 +4219,7 @@ os_prompt% </pre> </func> <func> - <name name="port_info" arity="2" clause_i="11"/> + <name name="port_info" arity="2" clause_i="12"/> <fsummary>Information about the parallelism hint of a port.</fsummary> <desc> <p><c><anno>Boolean</anno></c> corresponds to the port parallelism @@ -4214,7 +4230,7 @@ os_prompt% </pre> </func> <func> - <name name="port_info" arity="2" clause_i="12"/> + <name name="port_info" arity="2" clause_i="13"/> <fsummary>Information about the queue size of a port.</fsummary> <desc> <p><c><anno>Bytes</anno></c> is the total number @@ -4231,7 +4247,7 @@ os_prompt% </pre> </func> <func> - <name name="port_info" arity="2" clause_i="13"/> + <name name="port_info" arity="2" clause_i="14"/> <fsummary>Information about the registered name of a port.</fsummary> <desc> <p><c><anno>RegisteredName</anno></c> is the registered name of @@ -4865,10 +4881,19 @@ os_prompt% </pre> <p>A list of monitors (started by <c>monitor/2</c>) that are active for the process. For a local process monitor or a remote process monitor by a process - identifier, the list item is <c>{process, <anno>Pid</anno>}</c>. - For a remote process - monitor by name, the list item is - <c>{process, {<anno>RegName</anno>, <anno>Node</anno>}}</c>.</p> + identifier, the list consists of:</p> + <taglist> + <tag><c>{process, <anno>Pid</anno>}</c></tag> + <item>Process is monitored by pid.</item> + <tag><c>{process, {<anno>RegName</anno>, <anno>Node</anno>}}</c></tag> + <item>Local or remote process is monitored by name.</item> + <tag><c>{port, PortId}</c></tag> + <item>Local port is monitored by port id.</item> + <tag><c>{port, {<anno>RegName</anno>, <anno>Node</anno>}}</c></tag> + <item>Local port is monitored by name. Please note, that + remote port monitors are not supported, so <c>Node</c> will + always be the local node name.</item> + </taglist> </item> <tag><c>{message_queue_data, <anno>MQD</anno>}</c></tag> <item> diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index b18910e2c7..fc14061a44 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -282,20 +282,17 @@ res_no_proc: { } } -#define ERTS_DEMONITOR_FALSE 2 -#define ERTS_DEMONITOR_TRUE 1 -#define ERTS_DEMONITOR_BADARG 0 -#define ERTS_DEMONITOR_YIELD_TRUE -1 -#define ERTS_DEMONITOR_INTERNAL_ERROR -2 - -static int +/* This function is allowed to return range of values handled by demonitor/1-2 + * Namely: atoms true, false, yield, internal_error, badarg or THE_NON_VALUE + */ +static Eterm remote_demonitor(Process *c_p, DistEntry *dep, Eterm ref, Eterm to) { ErtsDSigData dsd; ErtsMonitor *dmon; ErtsMonitor *mon; int code; - int res; + Eterm res = am_false; #ifndef ERTS_SMP int stale_mon = 0; #endif @@ -328,7 +325,7 @@ remote_demonitor(Process *c_p, DistEntry *dep, Eterm ref, Eterm to) mon = erts_remove_monitor(&ERTS_P_MONITORS(c_p), ref); erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_LINK); - res = ERTS_DEMONITOR_TRUE; + res = am_true; break; case ERTS_DSIG_PREP_CONNECTED: @@ -352,7 +349,7 @@ remote_demonitor(Process *c_p, DistEntry *dep, Eterm ref, Eterm to) * This is possible when smp support is enabled. * 'DOWN' message just arrived. */ - res = ERTS_DEMONITOR_TRUE; + res = am_true; } else { /* @@ -367,16 +364,13 @@ remote_demonitor(Process *c_p, DistEntry *dep, Eterm ref, Eterm to) : mon->pid), ref, 0); - res = (code == ERTS_DSIG_SEND_YIELD - ? ERTS_DEMONITOR_YIELD_TRUE - : ERTS_DEMONITOR_TRUE); + res = (code == ERTS_DSIG_SEND_YIELD ? am_yield : am_true); erts_destroy_monitor(dmon); - } break; default: ASSERT(! "Invalid dsig prepare result"); - return ERTS_DEMONITOR_INTERNAL_ERROR; + return am_internal_error; } #ifndef ERTS_SMP @@ -404,27 +398,96 @@ remote_demonitor(Process *c_p, DistEntry *dep, Eterm ref, Eterm to) return res; } -static int demonitor(Process *c_p, Eterm ref, Eterm *multip) +static ERTS_INLINE void +demonitor_local_process(Process *c_p, Eterm ref, Eterm to, Eterm *res) +{ + Process *rp = erts_pid2proc_opt(c_p, + ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK, + to, + ERTS_PROC_LOCK_LINK, + ERTS_P2P_FLG_ALLOW_OTHER_X); + ErtsMonitor *mon = erts_remove_monitor(&ERTS_P_MONITORS(c_p), ref); + +#ifndef ERTS_SMP + ASSERT(mon); +#else + if (!mon) + *res = am_false; + else +#endif + { + *res = am_true; + erts_destroy_monitor(mon); + } + if (rp) { + ErtsMonitor *rmon; + rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), ref); + if (rp != c_p) + erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK); + if (rmon != NULL) + erts_destroy_monitor(rmon); + } + else { + ERTS_SMP_ASSERT_IS_NOT_EXITING(c_p); + } +} + +static ERTS_INLINE BIF_RETTYPE +demonitor_local_port(Process *origin, Eterm ref, Eterm target) { - ErtsMonitor *mon = NULL; /* The monitor entry to delete */ - Process *rp; /* Local target process */ - Eterm to = NIL; /* Monitor link traget */ - DistEntry *dep = NULL; /* Target's distribution entry */ - int deref_de = 0; - int res; - int unlock_link = 1; + BIF_RETTYPE res = am_false; + Port *port = erts_port_lookup_raw(target); + + if (!port) { + BIF_ERROR(origin, BADARG); + } + erts_smp_proc_unlock(origin, ERTS_PROC_LOCK_LINK); + + if (port) { + Eterm trap_ref; + switch (erts_port_demonitor(origin, ERTS_PORT_DEMONITOR_NORMAL, + port, ref, &trap_ref)) { + case ERTS_PORT_OP_DROPPED: + case ERTS_PORT_OP_BADARG: + break; + case ERTS_PORT_OP_SCHEDULED: + BIF_TRAP3(await_port_send_result_trap, origin, trap_ref, + am_busy_port, am_true); + /* the busy_port atom will never be returned, because it cannot be + * returned from erts_port_(de)monitor, but just in case if in future + * internal API changes - you may see this atom */ + default: + break; + } + } + else { + ERTS_SMP_ASSERT_IS_NOT_EXITING(origin); + } + BIF_RET(res); +} +/* Can return atom true, false, yield, internal_error, badarg or + * THE_NON_VALUE if error occured or trap has been set up + */ +static +BIF_RETTYPE demonitor(Process *c_p, Eterm ref, Eterm *multip) +{ + ErtsMonitor *mon = NULL; /* The monitor entry to delete */ + Eterm to = NIL; /* Monitor link traget */ + DistEntry *dep = NULL; /* Target's distribution entry */ + int deref_de = 0; + BIF_RETTYPE res = am_false; + int unlock_link = 1; erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_LINK); if (is_not_internal_ref(ref)) { - res = ERTS_DEMONITOR_BADARG; + res = am_badarg; goto done; /* Cannot be this monitor's ref */ } mon = erts_lookup_monitor(ERTS_P_MONITORS(c_p), ref); if (!mon) { - res = ERTS_DEMONITOR_FALSE; goto done; } @@ -432,70 +495,50 @@ static int demonitor(Process *c_p, Eterm ref, Eterm *multip) case MON_TIME_OFFSET: *multip = am_true; erts_demonitor_time_offset(ref); - res = ERTS_DEMONITOR_TRUE; + res = am_true; break; case MON_ORIGIN: to = mon->pid; *multip = am_false; if (is_atom(to)) { - /* Monitoring a name at node to */ - ASSERT(is_node_name_atom(to)); - dep = erts_sysname_to_connected_dist_entry(to); - ASSERT(dep != erts_this_dist_entry); - if (dep) - deref_de = 1; + /* Monitoring a name at node to */ + ASSERT(is_node_name_atom(to)); + dep = erts_sysname_to_connected_dist_entry(to); + ASSERT(dep != erts_this_dist_entry); + if (dep) + deref_de = 1; + } else if (is_port(to)) { + if (port_dist_entry(to) != erts_this_dist_entry) { + goto badarg; + } + res = demonitor_local_port(c_p, ref, to); + unlock_link = 0; + goto done; } else { - ASSERT(is_pid(to)); - dep = pid_dist_entry(to); + ASSERT(is_pid(to)); + dep = pid_dist_entry(to); } if (dep != erts_this_dist_entry) { - res = remote_demonitor(c_p, dep, ref, to); - /* remote_demonitor() unlocks link lock on c_p */ - unlock_link = 0; + res = remote_demonitor(c_p, dep, ref, to); + /* remote_demonitor() unlocks link lock on c_p */ + unlock_link = 0; } else { /* Local monitor */ - if (deref_de) { - deref_de = 0; - erts_deref_dist_entry(dep); - } - dep = NULL; - rp = erts_pid2proc_opt(c_p, - ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK, - to, - ERTS_PROC_LOCK_LINK, - ERTS_P2P_FLG_ALLOW_OTHER_X); - mon = erts_remove_monitor(&ERTS_P_MONITORS(c_p), ref); -#ifndef ERTS_SMP - ASSERT(mon); -#else - if (!mon) - res = ERTS_DEMONITOR_FALSE; - else -#endif - { - res = ERTS_DEMONITOR_TRUE; - erts_destroy_monitor(mon); - } - if (rp) { - ErtsMonitor *rmon; - rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), ref); - if (rp != c_p) - erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK); - if (rmon != NULL) - erts_destroy_monitor(rmon); - } - else { - ERTS_SMP_ASSERT_IS_NOT_EXITING(c_p); - } - + if (deref_de) { + deref_de = 0; + erts_deref_dist_entry(dep); + } + dep = NULL; + demonitor_local_process(c_p, ref, to, &res); } break; - default: - res = ERTS_DEMONITOR_BADARG; + default /* case */ : +badarg: + res = am_badarg; /* will be converted to error by caller */ *multip = am_false; break; } - done: +done: if (unlock_link) erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_LINK); @@ -506,21 +549,20 @@ static int demonitor(Process *c_p, Eterm ref, Eterm *multip) } ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_MAIN == erts_proc_lc_my_proc_locks(c_p)); - return res; + BIF_RET(res); } BIF_RETTYPE demonitor_1(BIF_ALIST_1) { Eterm multi; switch (demonitor(BIF_P, BIF_ARG_1, &multi)) { - case ERTS_DEMONITOR_FALSE: - case ERTS_DEMONITOR_TRUE: - BIF_RET(am_true); - case ERTS_DEMONITOR_YIELD_TRUE: - ERTS_BIF_YIELD_RETURN(BIF_P, am_true); - case ERTS_DEMONITOR_BADARG: - BIF_ERROR(BIF_P, BADARG); - case ERTS_DEMONITOR_INTERNAL_ERROR: + case am_false: + case am_true: BIF_RET(am_true); + case THE_NON_VALUE: BIF_RET(THE_NON_VALUE); + case am_yield: ERTS_BIF_YIELD_RETURN(BIF_P, am_true); + case am_badarg: BIF_ERROR(BIF_P, BADARG); + + case am_internal_error: default: ASSERT(! "demonitor(): internal error"); BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR); @@ -529,11 +571,11 @@ BIF_RETTYPE demonitor_1(BIF_ALIST_1) BIF_RETTYPE demonitor_2(BIF_ALIST_2) { - Eterm res = am_true; - Eterm multi = am_false; - int info = 0; - int flush = 0; - Eterm list = BIF_ARG_2; + BIF_RETTYPE res = am_true; + Eterm multi = am_false; + int info = 0; + int flush = 0; + Eterm list = BIF_ARG_2; while (is_list(list)) { Eterm* consp = list_val(list); @@ -554,24 +596,27 @@ BIF_RETTYPE demonitor_2(BIF_ALIST_2) goto badarg; switch (demonitor(BIF_P, BIF_ARG_1, &multi)) { - case ERTS_DEMONITOR_FALSE: + case THE_NON_VALUE: + /* If other error occured or trap has been set up - pass through */ + BIF_RET(THE_NON_VALUE); + case am_false: if (info) res = am_false; if (flush) { - flush_messages: +flush_messages: BIF_TRAP3(flush_monitor_messages_trap, BIF_P, BIF_ARG_1, multi, res); } - case ERTS_DEMONITOR_TRUE: + case am_true: if (multi == am_true && flush) goto flush_messages; BIF_RET(res); - case ERTS_DEMONITOR_YIELD_TRUE: + case am_yield: ERTS_BIF_YIELD_RETURN(BIF_P, am_true); - case ERTS_DEMONITOR_BADARG: - badarg: + case am_badarg: +badarg: BIF_ERROR(BIF_P, BADARG); - case ERTS_DEMONITOR_INTERNAL_ERROR: + case am_internal_error: default: ASSERT(! "demonitor(): internal error"); BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR); @@ -615,14 +660,13 @@ erts_queue_monitor_message(Process *p, erts_queue_message(p, *p_locksp, msgp, tup, am_system); } -static BIF_RETTYPE +static Eterm local_pid_monitor(Process *p, Eterm target, Eterm mon_ref, int boolean) { - BIF_RETTYPE ret; - Process *rp; + Eterm ret = mon_ref; + Process *rp; ErtsProcLocks p_locks = ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK; - ERTS_BIF_PREP_RET(ret, mon_ref); if (target == p->common.id) { return ret; } @@ -658,40 +702,112 @@ local_pid_monitor(Process *p, Eterm target, Eterm mon_ref, int boolean) } static BIF_RETTYPE -local_name_monitor(Process *p, Eterm target_name) +local_port_monitor(Process *origin, Eterm target) { - BIF_RETTYPE ret; - Eterm mon_ref; - ErtsProcLocks p_locks = ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK; - Process *rp; + BIF_RETTYPE ref = erts_make_ref(origin); + Port *port = erts_sig_lookup_port(origin, target); + ErtsProcLocks p_locks = ERTS_PROC_LOCK_MAIN; - mon_ref = erts_make_ref(p); - ERTS_BIF_PREP_RET(ret, mon_ref); - erts_smp_proc_lock(p, ERTS_PROC_LOCK_LINK); - rp = erts_whereis_process(p, p_locks, target_name, ERTS_PROC_LOCK_LINK, - ERTS_P2P_FLG_ALLOW_OTHER_X); - if (!rp) { - DeclareTmpHeap(lhp,3,p); + if (!port) { +res_no_proc: + /* Send the DOWN message immediately. Ref is made on the fly because + * caller has never seen it yet. */ + erts_queue_monitor_message(origin, &p_locks, ref, + am_port, target, am_noproc); + } + else { + switch (erts_port_monitor(origin, port, target, &ref)) { + case ERTS_PORT_OP_DROPPED: + case ERTS_PORT_OP_BADARG: + goto res_no_proc; + case ERTS_PORT_OP_SCHEDULED: + BIF_TRAP3(await_port_send_result_trap, origin, ref, + am_busy_port, ref); + /* the busy_port atom will never be returned, because it cannot be + * returned from erts_port_monitor, but just in case if in future + * internal API changes - you may see this atom */ + default: + break; + } + } + erts_smp_proc_unlock(origin, p_locks & ~ERTS_PROC_LOCK_MAIN); + BIF_RET(ref); +} + +/* Type = process | port :: atom(), 1st argument passed to erlang:monitor/2 + */ +static BIF_RETTYPE +local_name_monitor(Process *self, Eterm type, Eterm target_name) +{ + BIF_RETTYPE ret = erts_make_ref(self); + + ErtsProcLocks p_locks = ERTS_PROC_LOCK_MAIN | ERTS_PROC_LOCK_LINK; + Process *proc = NULL; + Port *port = NULL; + + erts_smp_proc_lock(self, ERTS_PROC_LOCK_LINK); + + erts_whereis_name(self, p_locks, target_name, + &proc, ERTS_PROC_LOCK_LINK, + ERTS_P2P_FLG_ALLOW_OTHER_X, + &port, 0); + + /* If the name is not registered, + * or if we asked for proc and got a port, + * or if we asked for port and got a proc, + * we just send the 'DOWN' message. + */ + if ((!proc && !port) || + (type == am_process && port) || + (type == am_port && proc)) { + DeclareTmpHeap(lhp,3,self); Eterm item; - UseTmpHeap(3,p); - erts_smp_proc_unlock(p, ERTS_PROC_LOCK_LINK); + UseTmpHeap(3,self); + + erts_smp_proc_unlock(self, ERTS_PROC_LOCK_LINK); p_locks &= ~ERTS_PROC_LOCK_LINK; + item = TUPLE2(lhp, target_name, erts_this_dist_entry->sysname); - erts_queue_monitor_message(p, &p_locks, - mon_ref, am_process, item, am_noproc); - UnUseTmpHeap(3,p); - } - else if (rp != p) { - erts_add_monitor(&ERTS_P_MONITORS(p), MON_ORIGIN, mon_ref, rp->common.id, - target_name); - erts_add_monitor(&ERTS_P_MONITORS(rp), MON_TARGET, mon_ref, p->common.id, - target_name); - erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK); + erts_queue_monitor_message(self, &p_locks, + ret, + type, /* = process|port :: atom() */ + item, am_noproc); + UnUseTmpHeap(3,self); + } + else if (port) { + erts_smp_proc_unlock(self, p_locks & ~ERTS_PROC_LOCK_MAIN); + p_locks &= ~ERTS_PROC_LOCK_MAIN; + + switch (erts_port_monitor(self, port, target_name, &ret)) { + case ERTS_PORT_OP_DONE: + return ret; + case ERTS_PORT_OP_SCHEDULED: { /* Scheduled a signal */ + ASSERT(is_internal_ref(ret)); + BIF_TRAP3(await_port_send_result_trap, self, + ret, am_true, ret); + /* bif_trap returns */ + } break; + default: + goto badarg; + } + } + else if (proc != self) { + erts_add_monitor(&ERTS_P_MONITORS(self), MON_ORIGIN, ret, + proc->common.id, target_name); + erts_add_monitor(&ERTS_P_MONITORS(proc), MON_TARGET, ret, + self->common.id, target_name); + erts_smp_proc_unlock(proc, ERTS_PROC_LOCK_LINK); } - erts_smp_proc_unlock(p, p_locks & ~ERTS_PROC_LOCK_MAIN); - - return ret; + if (p_locks) { + erts_smp_proc_unlock(self, p_locks & ~ERTS_PROC_LOCK_MAIN); + } + BIF_RET(ret); +badarg: + if (p_locks) { + erts_smp_proc_unlock(self, p_locks & ~ERTS_PROC_LOCK_MAIN); + } + BIF_ERROR(self, BADARG); } static BIF_RETTYPE @@ -758,7 +874,7 @@ remote_monitor(Process *p, Eterm bifarg1, Eterm bifarg2, break; } - return ret; + BIF_RET(ret); } BIF_RETTYPE monitor_2(BIF_ALIST_2) @@ -772,8 +888,9 @@ BIF_RETTYPE monitor_2(BIF_ALIST_2) switch (BIF_ARG_1) { case am_time_offset: { Eterm ref; - if (BIF_ARG_2 != am_clock_service) - goto error; + if (BIF_ARG_2 != am_clock_service) { + goto badarg; + } ref = erts_make_ref(BIF_P); erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_LINK); erts_add_monitor(&ERTS_P_MONITORS(BIF_P), MON_TIME_OFFSET, @@ -783,46 +900,57 @@ BIF_RETTYPE monitor_2(BIF_ALIST_2) BIF_RET(ref); } case am_process: + case am_port: break; default: - goto error; + goto badarg; } - if (is_internal_pid(target)) { - local_pid: - ret = local_pid_monitor(BIF_P, target, erts_make_ref(BIF_P), 0); - } else if (is_external_pid(target)) { + if (is_internal_pid(target) && BIF_ARG_1 == am_process) { +local_pid: + ret = local_pid_monitor(BIF_P, target, erts_make_ref(BIF_P), 0); + } else if (is_external_pid(target) && BIF_ARG_1 == am_process) { dep = external_pid_dist_entry(target); if (dep == erts_this_dist_entry) goto local_pid; ret = remote_monitor(BIF_P, BIF_ARG_1, BIF_ARG_2, dep, target, 0); + } else if (is_internal_port(target) && BIF_ARG_1 == am_port) { +local_port: + ret = local_port_monitor(BIF_P, target); + } else if (is_external_port(target) && BIF_ARG_1 == am_port) { + dep = external_port_dist_entry(target); + if (dep == erts_this_dist_entry) { + goto local_port; + } + goto badarg; /* No want remote port */ } else if (is_atom(target)) { - ret = local_name_monitor(BIF_P, target); + ret = local_name_monitor(BIF_P, BIF_ARG_1, target); } else if (is_tuple(target)) { Eterm *tp = tuple_val(target); Eterm remote_node; Eterm name; - if (arityval(*tp) != 2) - goto error; + if (arityval(*tp) != 2) { + goto badarg; + } remote_node = tp[2]; name = tp[1]; if (!is_atom(remote_node) || !is_atom(name)) { - goto error; + goto badarg; } if (!erts_is_alive && remote_node != am_Noname) { - goto error; /* Remote monitor from (this) undistributed node */ + goto badarg; /* Remote monitor from (this) undistributed node */ } dep = erts_sysname_to_connected_dist_entry(remote_node); if (dep == erts_this_dist_entry) { deref_de = 1; - ret = local_name_monitor(BIF_P, name); + ret = local_name_monitor(BIF_P, BIF_ARG_1, name); } else { if (dep) deref_de = 1; ret = remote_monitor(BIF_P, BIF_ARG_1, BIF_ARG_2, dep, name, 1); } } else { - error: +badarg: ERTS_BIF_PREP_ERROR(ret, BIF_P, BADARG); } if (deref_de) { diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index b410578d37..3fb866733c 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -361,8 +361,13 @@ erts_print_system_version(int to, void *arg, Process *c_p) } typedef struct { + /* {Entity,Node} = {monitor.Name,monitor.Pid} for external by name + * {Entity,Node} = {monitor.Pid,NIL} for external/external by pid + * {Entity,Node} = {monitor.Name,erlang:node()} for internal by name */ Eterm entity; Eterm node; + /* pid is actual target being monitored, no matter pid/port or name */ + Eterm pid; } MonitorInfo; typedef struct { @@ -420,21 +425,27 @@ static void collect_one_origin_monitor(ErtsMonitor *mon, void *vmicp) EXTEND_MONITOR_INFOS(micp); if (is_atom(mon->pid)) { /* external by name */ micp->mi[micp->mi_i].entity = mon->name; - micp->mi[micp->mi_i].node = mon->pid; - micp->sz += 3; /* need one 2-tuple */ + micp->mi[micp->mi_i].node = mon->pid; + micp->sz += 3; /* need one 2-tuple */ } else if (is_external_pid(mon->pid)) { /* external by pid */ micp->mi[micp->mi_i].entity = mon->pid; - micp->mi[micp->mi_i].node = NIL; - micp->sz += NC_HEAP_SIZE(mon->pid); + micp->mi[micp->mi_i].node = NIL; + micp->sz += NC_HEAP_SIZE(mon->pid); } else if (!is_nil(mon->name)) { /* internal by name */ micp->mi[micp->mi_i].entity = mon->name; - micp->mi[micp->mi_i].node = erts_this_dist_entry->sysname; - micp->sz += 3; /* need one 2-tuple */ + micp->mi[micp->mi_i].node = erts_this_dist_entry->sysname; + micp->sz += 3; /* need one 2-tuple */ } else { /* internal by pid */ micp->mi[micp->mi_i].entity = mon->pid; - micp->mi[micp->mi_i].node = NIL; + micp->mi[micp->mi_i].node = NIL; /* no additional heap space needed */ } + + /* have always pid at hand, to assist with figuring out if its a port or + * a process, when we monitored by name and process_info is requested. + * See: erl_bif_info.c:process_info_aux section for am_monitors */ + micp->mi[micp->mi_i].pid = mon->pid; + micp->mi_i++; micp->sz += 2 + 3; /* For a cons cell and a 2-tuple */ } @@ -1190,37 +1201,49 @@ process_info_aux(Process *BIF_P, case am_monitors: { MonitorInfoCollection mic; - int i; + int i; INIT_MONITOR_INFOS(mic); - erts_doforall_monitors(ERTS_P_MONITORS(rp),&collect_one_origin_monitor,&mic); - hp = HAlloc(BIF_P, 3 + mic.sz); + erts_doforall_monitors(ERTS_P_MONITORS(rp), + &collect_one_origin_monitor, &mic); + hp = HAlloc(BIF_P, 3 + mic.sz); res = NIL; for (i = 0; i < mic.mi_i; i++) { if (is_atom(mic.mi[i].entity)) { /* Monitor by name. - * Build {process, {Name, Node}} and cons it. + * Build {process|port, {Name, Node}} and cons it. */ Eterm t1, t2; + /* If pid is an atom, then it is a remote named monitor, which + has to be a process */ + Eterm m_type = is_port(mic.mi[i].pid) ? am_port : am_process; + ASSERT(is_pid(mic.mi[i].pid) + || is_port(mic.mi[i].pid) + || is_atom(mic.mi[i].pid)); t1 = TUPLE2(hp, mic.mi[i].entity, mic.mi[i].node); hp += 3; - t2 = TUPLE2(hp, am_process, t1); + t2 = TUPLE2(hp, m_type, t1); hp += 3; res = CONS(hp, t2, res); - hp += 2; + hp += 2; } else { - /* Monitor by pid. Build {process, Pid} and cons it. */ + /* Monitor by pid. Build {process|port, Pid} and cons it. */ Eterm t; Eterm pid = STORE_NC(&hp, &MSO(BIF_P), mic.mi[i].entity); - t = TUPLE2(hp, am_process, pid); + + Eterm m_type = is_port(mic.mi[i].pid) ? am_port : am_process; + ASSERT(is_pid(mic.mi[i].pid) + || is_port(mic.mi[i].pid)); + + t = TUPLE2(hp, m_type, pid); hp += 3; res = CONS(hp, t, res); - hp += 2; + hp += 2; } } - DESTROY_MONITOR_INFOS(mic); + DESTROY_MONITOR_INFOS(mic); break; } @@ -2880,7 +2903,8 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) */ Eterm -erts_bld_port_info(Eterm **hpp, ErlOffHeap *ohp, Uint *szp, Port *prt, Eterm item) +erts_bld_port_info(Eterm **hpp, ErlOffHeap *ohp, Uint *szp, Port *prt, + Eterm item) { Eterm res = THE_NON_VALUE; @@ -2928,8 +2952,8 @@ erts_bld_port_info(Eterm **hpp, ErlOffHeap *ohp, Uint *szp, Port *prt, Eterm ite Eterm item; INIT_MONITOR_INFOS(mic); - - erts_doforall_monitors(ERTS_P_MONITORS(prt), &collect_one_origin_monitor, &mic); + erts_doforall_monitors(ERTS_P_MONITORS(prt), + &collect_one_origin_monitor, &mic); if (szp) *szp += mic.sz; @@ -2938,14 +2962,16 @@ erts_bld_port_info(Eterm **hpp, ErlOffHeap *ohp, Uint *szp, Port *prt, Eterm ite res = NIL; for (i = 0; i < mic.mi_i; i++) { Eterm t; - item = STORE_NC(hpp, ohp, mic.mi[i].entity); - t = TUPLE2(*hpp, am_process, item); + Eterm m_type; + + item = STORE_NC(hpp, ohp, mic.mi[i].entity); + m_type = is_port(item) ? am_port : am_process; + t = TUPLE2(*hpp, m_type, item); *hpp += 3; res = CONS(*hpp, t, res); *hpp += 2; } - } - + } // hpp DESTROY_MONITOR_INFOS(mic); if (szp) { @@ -2953,6 +2979,32 @@ erts_bld_port_info(Eterm **hpp, ErlOffHeap *ohp, Uint *szp, Port *prt, Eterm ite goto done; } } + else if (item == am_monitored_by) { + MonitorInfoCollection mic; + int i; + Eterm item; + + INIT_MONITOR_INFOS(mic); + erts_doforall_monitors(ERTS_P_MONITORS(prt), + &collect_one_target_monitor, &mic); + if (szp) + *szp += mic.sz; + + if (hpp) { + res = NIL; + for (i = 0; i < mic.mi_i; ++i) { + item = STORE_NC(hpp, ohp, mic.mi[i].entity); + res = CONS(*hpp, item, res); + *hpp += 2; + } + } // hpp + DESTROY_MONITOR_INFOS(mic); + + if (szp) { + res = am_true; + goto done; + } + } else if (item == am_name) { int count = sys_strlen(prt->name); diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c index fefa9d8391..90e78a9b0b 100644 --- a/erts/emulator/beam/erl_bif_port.c +++ b/erts/emulator/beam/erl_bif_port.c @@ -139,6 +139,12 @@ sig_lookup_port(Process *c_p, Eterm id_or_name) return lookup_port(c_p, id_or_name, ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP); } +/* Non-inline copy of sig_lookup_port to be exported */ +Port *erts_sig_lookup_port(Process *c_p, Eterm id_or_name) +{ + return lookup_port(c_p, id_or_name, ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP); +} + static ERTS_INLINE Port * data_lookup_port(Process *c_p, Eterm id_or_name) { diff --git a/erts/emulator/beam/erl_port.h b/erts/emulator/beam/erl_port.h index f0075ca2b9..f90844ccc8 100644 --- a/erts/emulator/beam/erl_port.h +++ b/erts/emulator/beam/erl_port.h @@ -361,6 +361,8 @@ Eterm erts_request_io_bytes(Process *c_p); #define ERTS_PORT_REDS_CONNECT (CONTEXT_REDS/200) #define ERTS_PORT_REDS_UNLINK (CONTEXT_REDS/200) #define ERTS_PORT_REDS_LINK (CONTEXT_REDS/200) +#define ERTS_PORT_REDS_MONITOR (CONTEXT_REDS/200) +#define ERTS_PORT_REDS_DEMONITOR (CONTEXT_REDS/200) #define ERTS_PORT_REDS_BADSIG (CONTEXT_REDS/200) #define ERTS_PORT_REDS_CONTROL (CONTEXT_REDS/100) #define ERTS_PORT_REDS_CALL (CONTEXT_REDS/50) @@ -850,16 +852,20 @@ void erts_port_resume_procs(Port *); struct binary; -#define ERTS_P2P_SIG_TYPE_BAD 0 -#define ERTS_P2P_SIG_TYPE_OUTPUT 1 -#define ERTS_P2P_SIG_TYPE_OUTPUTV 2 -#define ERTS_P2P_SIG_TYPE_CONNECT 3 -#define ERTS_P2P_SIG_TYPE_EXIT 4 -#define ERTS_P2P_SIG_TYPE_CONTROL 5 -#define ERTS_P2P_SIG_TYPE_CALL 6 -#define ERTS_P2P_SIG_TYPE_INFO 7 -#define ERTS_P2P_SIG_TYPE_LINK 8 -#define ERTS_P2P_SIG_TYPE_UNLINK 9 +enum { + ERTS_P2P_SIG_TYPE_BAD = 0, + ERTS_P2P_SIG_TYPE_OUTPUT = 1, + ERTS_P2P_SIG_TYPE_OUTPUTV = 2, + ERTS_P2P_SIG_TYPE_CONNECT = 3, + ERTS_P2P_SIG_TYPE_EXIT = 4, + ERTS_P2P_SIG_TYPE_CONTROL = 5, + ERTS_P2P_SIG_TYPE_CALL = 6, + ERTS_P2P_SIG_TYPE_INFO = 7, + ERTS_P2P_SIG_TYPE_LINK = 8, + ERTS_P2P_SIG_TYPE_UNLINK = 9, + ERTS_P2P_SIG_TYPE_MONITOR = 10, + ERTS_P2P_SIG_TYPE_DEMONITOR = 11 +}; #define ERTS_P2P_SIG_TYPE_BITS 4 #define ERTS_P2P_SIG_TYPE_MASK \ @@ -921,6 +927,15 @@ struct ErtsProc2PortSigData_ { struct { Eterm from; } unlink; + struct { + Eterm origin; /* who receives monitor event, pid */ + Eterm name; /* either name for named monitor, or port id */ + } monitor; + struct { + Eterm origin; /* who is at the other end of the monitor, pid */ + Eterm name; /* port id */ + Uint32 ref[ERTS_MAX_REF_NUMBERS]; /* box contents of a ref */ + } demonitor; } u; } ; @@ -1017,6 +1032,29 @@ ErtsPortOpResult erts_port_control(Process *, Port *, unsigned int, Eterm, Eterm ErtsPortOpResult erts_port_call(Process *, Port *, unsigned int, Eterm, Eterm *); ErtsPortOpResult erts_port_info(Process *, Port *, Eterm, Eterm *); +/* Creates monitor between Origin and Target. Ref must be initialized to + * a reference (ref may be rewritten to be used to serve additionally as a + * signal id). Name is atom if user monitors port by name or NIL */ +ErtsPortOpResult erts_port_monitor(Process *origin, Port *target, Eterm name, + Eterm *ref); + +typedef enum { + /* Normal demonitor rules apply with locking and reductions bump */ + ERTS_PORT_DEMONITOR_NORMAL = 1, + /* Relaxed demonitor rules when process is about to die, which means that + * pid lookup won't work, locks won't work, no reductions bump. */ + ERTS_PORT_DEMONITOR_ORIGIN_ON_DEATHBED = 2, +} ErtsDemonitorMode; + +/* Removes monitor between origin and target, identified by ref. + * origin_is_dying can be 0 (false, normal locking rules and reductions bump + * apply) or 1 (true, in case when we avoid origin locking) */ +ErtsPortOpResult erts_port_demonitor(Process *origin, ErtsDemonitorMode mode, + Port *target, Eterm ref, + Eterm *trap_ref); +/* defined in erl_bif_port.c */ +Port *erts_sig_lookup_port(Process *c_p, Eterm id_or_name); + int erts_port_output_async(Port *, Eterm, Eterm); /* diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 5193be85b4..48f89d2bd7 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -12239,7 +12239,6 @@ static void doit_exit_monitor(ErtsMonitor *mon, void *vpcontext) ExitMonitorContext *pcontext = vpcontext; DistEntry *dep; ErtsMonitor *rmon; - Process *rp; switch (mon->type) { case MON_ORIGIN: @@ -12268,9 +12267,10 @@ static void doit_exit_monitor(ErtsMonitor *mon, void *vpcontext) erts_deref_dist_entry(dep); } } else { - ASSERT(is_pid(mon->pid)); - if (is_internal_pid(mon->pid)) { /* local by pid or name */ - rp = erts_pid2proc(NULL, 0, mon->pid, ERTS_PROC_LOCK_LINK); + ASSERT(is_pid(mon->pid) || is_port(mon->pid)); + /* if is local by pid or name */ + if (is_internal_pid(mon->pid)) { + Process *rp = erts_pid2proc(NULL, 0, mon->pid, ERTS_PROC_LOCK_LINK); if (!rp) { goto done; } @@ -12280,7 +12280,17 @@ static void doit_exit_monitor(ErtsMonitor *mon, void *vpcontext) goto done; } erts_destroy_monitor(rmon); - } else { /* remote by pid */ + } else if (is_internal_port(mon->pid)) { + /* Is a local port */ + Port *prt = erts_port_lookup_raw(mon->pid); + if (!prt) { + goto done; + } + erts_port_demonitor(pcontext->p, + ERTS_PORT_DEMONITOR_ORIGIN_ON_DEATHBED, + prt, mon->ref, NULL); + return; /* let erts_port_demonitor do the deletion */ + } else { /* remote by pid */ ASSERT(is_external_pid(mon->pid)); dep = external_pid_dist_entry(mon->pid); ASSERT(dep != NULL); @@ -12318,6 +12328,7 @@ static void doit_exit_monitor(ErtsMonitor *mon, void *vpcontext) erts_port_release(prt); } else if (is_internal_pid(mon->pid)) {/* local by name or pid */ Eterm watched; + Process *rp; DeclareTmpHeapNoproc(lhp,3); ErtsProcLocks rp_locks = (ERTS_PROC_LOCK_LINK | ERTS_PROC_LOCKS_MSG_SEND); diff --git a/erts/emulator/beam/erl_process_dump.c b/erts/emulator/beam/erl_process_dump.c index eeaa9a569c..a70dfb8e73 100644 --- a/erts/emulator/beam/erl_process_dump.c +++ b/erts/emulator/beam/erl_process_dump.c @@ -560,6 +560,11 @@ dump_externally(int to, void *to_arg, Eterm term) } } + /* Do not handle maps */ + if (is_map(term)) { + term = am_undefined; + } + s = p = sbuf; erts_encode_ext(term, &p); erts_print(to, to_arg, "E%X:", p-s); diff --git a/erts/emulator/beam/erlang_lttng.h b/erts/emulator/beam/erlang_lttng.h index 12f68e477b..4e869671f7 100644 --- a/erts/emulator/beam/erlang_lttng.h +++ b/erts/emulator/beam/erlang_lttng.h @@ -20,7 +20,7 @@ #ifdef USE_LTTNG #undef TRACEPOINT_PROVIDER -#define TRACEPOINT_PROVIDER com_ericsson_otp +#define TRACEPOINT_PROVIDER org_erlang_otp #undef TRACEPOINT_INCLUDE #define TRACEPOINT_INCLUDE "erlang_lttng.h" @@ -33,7 +33,7 @@ /* Schedulers */ TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, scheduler_poll, TP_ARGS( int, id, @@ -62,7 +62,7 @@ typedef struct { /* Port and Driver Scheduling */ TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_start, TP_ARGS( char*, pid, @@ -77,7 +77,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_init, TP_ARGS( char*, driver, @@ -94,7 +94,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_outputv, TP_ARGS( char*, pid, @@ -111,7 +111,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_output, TP_ARGS( char*, pid, @@ -128,7 +128,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_ready_input, TP_ARGS( char*, pid, @@ -143,7 +143,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_ready_output, TP_ARGS( char*, pid, @@ -158,7 +158,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_event, TP_ARGS( char*, pid, @@ -173,7 +173,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_timeout, TP_ARGS( char*, pid, @@ -188,7 +188,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_stop_select, TP_ARGS( char*, driver @@ -199,7 +199,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_flush, TP_ARGS( char*, pid, @@ -214,7 +214,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_stop, TP_ARGS( char*, pid, @@ -229,7 +229,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_process_exit, TP_ARGS( char*, pid, @@ -244,7 +244,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_ready_async, TP_ARGS( char*, pid, @@ -259,7 +259,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_finish, TP_ARGS( char*, driver @@ -270,7 +270,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_call, TP_ARGS( char*, pid, @@ -289,7 +289,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_control, TP_ARGS( char*, pid, @@ -310,7 +310,7 @@ TRACEPOINT_EVENT( /* Async pool */ TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, aio_pool_get, TP_ARGS( char*, port, @@ -323,7 +323,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, aio_pool_put, TP_ARGS( char*, port, @@ -339,7 +339,7 @@ TRACEPOINT_EVENT( /* Memory Allocator */ TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, carrier_create, TP_ARGS( const char*, type, @@ -365,7 +365,7 @@ TRACEPOINT_EVENT( TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, carrier_destroy, TP_ARGS( const char*, type, @@ -390,7 +390,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, carrier_pool_put, TP_ARGS( const char*, name, @@ -405,7 +405,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, carrier_pool_get, TP_ARGS( const char*, name, diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 01df5476db..cb8792dffa 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -1260,7 +1260,7 @@ typedef struct { /* * Try doing an immediate driver callback call from a process. If * this fail, the operation should be scheduled in the normal case... - * + * Returns: ok to do the call, or error (lock busy, does not exist, etc) */ static ERTS_INLINE ErtsTryImmDrvCallResult try_imm_drv_call(ErtsTryImmDrvCallState *sp) @@ -3074,6 +3074,250 @@ erts_port_link(Process *c_p, Port *prt, Eterm to, Eterm *refp) } static void +port_monitor_failure(Eterm port_id, Eterm origin, Eterm ref_DOWN) +{ + Process *origin_p; + ErtsProcLocks p_locks = ERTS_PROC_LOCK_LINK; + ASSERT(is_internal_pid(origin)); + + origin_p = erts_pid2proc(NULL, 0, origin, p_locks); + if (! origin_p) { return; } + + /* Send the DOWN message immediately. Ref is made on the fly because + * caller has never seen it yet. */ + erts_queue_monitor_message(origin_p, &p_locks, ref_DOWN, + am_port, port_id, am_noproc); + erts_smp_proc_unlock(origin_p, p_locks); +} + +/* Origin wants to monitor port Prt. State contains possible error, which has + * happened just before. Name is either NIL or an atom, if user monitors + * a port by name. Ref is premade reference that will be returned to user */ +static void +port_monitor(Port *prt, erts_aint32_t state, Eterm origin, + Eterm name, Eterm ref) +{ + Eterm name_or_nil = is_atom(name) ? name : NIL; + + ASSERT(is_pid(origin)); + ASSERT(is_atom(name) || is_port(name) || name == NIL); + ASSERT(is_internal_ref(ref)); + + if (!(state & ERTS_PORT_SFLGS_INVALID_LOOKUP)) { + ErtsProcLocks p_locks = ERTS_PROC_LOCK_LINK; + + Process *origin_p = erts_pid2proc(NULL, 0, origin, p_locks); + if (! origin_p) { + goto failure; + } + erts_add_monitor(&ERTS_P_MONITORS(origin_p), MON_ORIGIN, ref, + prt->common.id, name_or_nil); + erts_add_monitor(&ERTS_P_MONITORS(prt), MON_TARGET, ref, + origin, name_or_nil); + + erts_smp_proc_unlock(origin_p, p_locks); + } else { +failure: + port_monitor_failure(prt->common.id, origin, ref); + } +} + +static int +port_sig_monitor(Port *prt, erts_aint32_t state, int op, + ErtsProc2PortSigData *sigdp) +{ + Eterm hp[REF_THING_SIZE]; + Eterm ref = make_internal_ref(&hp); + write_ref_thing(hp, sigdp->ref[0], sigdp->ref[1], sigdp->ref[2]); + + if (op == ERTS_PROC2PORT_SIG_EXEC) { + /* erts_add_monitor call inside port_monitor will copy ref from hp */ + port_monitor(prt, state, + sigdp->u.monitor.origin, + sigdp->u.monitor.name, + ref); + } else { + port_monitor_failure(sigdp->u.monitor.name, + sigdp->u.monitor.origin, + ref); + } + if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY) { + port_sched_op_reply(sigdp->caller, sigdp->ref, am_true, prt); + } + return ERTS_PORT_REDS_MONITOR; +} + +/* Creates monitor between Origin and Target. Ref must be initialized to + * a reference (ref may be rewritten to be used to serve additionally as a + * signal id). Name is atom if user monitors port by name or NIL */ +ErtsPortOpResult +erts_port_monitor(Process *origin, Port *port, Eterm name, Eterm *refp) +{ + ErtsProc2PortSigData *sigdp; + ErtsTryImmDrvCallState try_call_state + = ERTS_INIT_TRY_IMM_DRV_CALL_STATE( + origin, port, ERTS_PORT_SFLGS_INVALID_LOOKUP, + 0, + 0, /* trap_ref is always set so !trap_ref always is false */ + am_monitor); + + ASSERT(origin); + ASSERT(port); + ASSERT(is_atom(name) || is_port(name)); + ASSERT(refp); + + switch (try_imm_drv_call(&try_call_state)) { + case ERTS_TRY_IMM_DRV_CALL_OK: + port_monitor(port, try_call_state.state, origin->common.id, name, *refp); + finalize_imm_drv_call(&try_call_state); + BUMP_REDS(origin, ERTS_PORT_REDS_MONITOR); + return ERTS_PORT_OP_DONE; + case ERTS_TRY_IMM_DRV_CALL_INVALID_PORT: + return ERTS_PORT_OP_BADARG; + default: + break; /* Schedule call instead... */ + } + + sigdp = erts_port_task_alloc_p2p_sig_data(); + sigdp->flags = ERTS_P2P_SIG_TYPE_MONITOR; + sigdp->u.monitor.origin = origin->common.id; + sigdp->u.monitor.name = name; /* either named monitor, or port id */ + + /* Ref contents will be initialized here */ + return erts_schedule_proc2port_signal(origin, port, origin->common.id, + refp, sigdp, 0, NULL, + port_sig_monitor); +} + +static void +port_demonitor_failure(Eterm port_id, Eterm origin, Eterm ref) +{ + Process *origin_p; + ErtsProcLocks rp_locks = ERTS_PROC_LOCK_LINK; + ErtsMonitor *mon1; + ASSERT(is_internal_pid(origin)); + + origin_p = erts_pid2proc(NULL, 0, origin, rp_locks); + if (! origin_p) { return; } + + /* do not send any DOWN messages, drop monitors on process */ + mon1 = erts_remove_monitor(&ERTS_P_MONITORS(origin_p), ref); + if (mon1 != NULL) { + erts_destroy_monitor(mon1); + } + + erts_smp_proc_unlock(origin_p, rp_locks); +} + +/* Origin wants to demonitor port Prt. State contains possible error, which has + * happened just before. Ref is reference to monitor */ +static void +port_demonitor(Port *port, erts_aint32_t state, Eterm origin, Eterm ref) +{ + ASSERT(port); + ASSERT(is_pid(origin)); + ASSERT(is_internal_ref(ref)); + + if (!(state & ERTS_PORT_SFLGS_INVALID_LOOKUP)) { + ErtsProcLocks p_locks = ERTS_PROC_LOCK_LINK; + Process *origin_p = erts_pid2proc(NULL, 0, origin, p_locks); + if (origin_p) { + ErtsMonitor *mon1 = erts_remove_monitor(&ERTS_P_MONITORS(origin_p), + ref); + if (mon1 != NULL) { + erts_destroy_monitor(mon1); + } + } + if (1) { + ErtsMonitor *mon2 = erts_remove_monitor(&ERTS_P_MONITORS(port), + ref); + if (mon2 != NULL) { + erts_destroy_monitor(mon2); + } + } + if (origin_p) { /* when origin is dying, it won't be found */ + erts_smp_proc_unlock(origin_p, p_locks); + } + } else { + port_demonitor_failure(port->common.id, origin, ref); + } +} + +static int +port_sig_demonitor(Port *prt, erts_aint32_t state, int op, + ErtsProc2PortSigData *sigdp) +{ + Eterm hp[REF_THING_SIZE]; + Eterm ref = make_internal_ref(&hp); + write_ref_thing(hp, sigdp->u.demonitor.ref[0], + sigdp->u.demonitor.ref[1], + sigdp->u.demonitor.ref[2]); + if (op == ERTS_PROC2PORT_SIG_EXEC) { + port_demonitor(prt, state, sigdp->u.demonitor.origin, ref); + } else { + port_demonitor_failure(sigdp->u.demonitor.name, + sigdp->u.demonitor.origin, + ref); + } + if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY) { + port_sched_op_reply(sigdp->caller, sigdp->ref, am_true, prt); + } + return ERTS_PORT_REDS_DEMONITOR; +} + +/* Removes monitor between origin and target, identified by ref. + * Mode defines normal or relaxed demonitor rules (process is at death) */ +ErtsPortOpResult erts_port_demonitor(Process *origin, ErtsDemonitorMode mode, + Port *target, Eterm ref, + Eterm *trap_ref) +{ + Process *c_p = mode == ERTS_PORT_DEMONITOR_NORMAL ? origin : NULL; + ErtsProc2PortSigData *sigdp; + ErtsTryImmDrvCallState try_call_state + = ERTS_INIT_TRY_IMM_DRV_CALL_STATE( + c_p, + target, ERTS_PORT_SFLGS_INVALID_LOOKUP, + 0, + !trap_ref, + am_demonitor); + + ASSERT(origin); + ASSERT(target); + ASSERT(is_internal_ref(ref)); + + switch (try_imm_drv_call(&try_call_state)) { + case ERTS_TRY_IMM_DRV_CALL_OK: + port_demonitor(target, try_call_state.state, origin->common.id, ref); + finalize_imm_drv_call(&try_call_state); + if (mode == ERTS_PORT_DEMONITOR_NORMAL) { + BUMP_REDS(origin, ERTS_PORT_REDS_DEMONITOR); + } + return ERTS_PORT_OP_DONE; + case ERTS_TRY_IMM_DRV_CALL_INVALID_PORT: + return ERTS_PORT_OP_BADARG; + default: + break; /* Schedule call instead... */ + } + + sigdp = erts_port_task_alloc_p2p_sig_data(); + sigdp->flags = ERTS_P2P_SIG_TYPE_DEMONITOR; + sigdp->u.demonitor.origin = origin->common.id; + sigdp->u.demonitor.name = target->common.id; + { + RefThing *reft = ref_thing_ptr(ref); + /* Start from 1 skip ref arity */ + sys_memcpy(sigdp->u.demonitor.ref, + internal_thing_ref_numbers(reft), + sizeof(sigdp->u.demonitor.ref)); + } + + /* Ref contents will be initialized here */ + return erts_schedule_proc2port_signal(c_p, target, origin->common.id, + trap_ref, sigdp, 0, NULL, + port_sig_demonitor); +} + +static void init_ack_send_reply(Port *port, Eterm resp) { @@ -3942,23 +4186,30 @@ erts_terminate_port(Port *pp) terminate_port(pp); } +static void port_fire_one_monitor(ErtsMonitor *mon, void *ctx0); static void sweep_one_monitor(ErtsMonitor *mon, void *vpsc) { - ErtsMonitor *rmon; - Process *rp; + switch (mon->type) { + case MON_ORIGIN: { + ErtsMonitor *rmon; + Process *rp; - ASSERT(mon->type == MON_ORIGIN); - ASSERT(is_internal_pid(mon->pid)); - rp = erts_pid2proc(NULL, 0, mon->pid, ERTS_PROC_LOCK_LINK); - if (!rp) { - goto done; - } - rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), mon->ref); - erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK); - if (rmon == NULL) { - goto done; + ASSERT(is_internal_pid(mon->pid)); + rp = erts_pid2proc(NULL, 0, mon->pid, ERTS_PROC_LOCK_LINK); + if (!rp) { + goto done; + } + rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), mon->ref); + erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK); + if (rmon == NULL) { + goto done; + } + erts_destroy_monitor(rmon); + } break; + case MON_TARGET: { + port_fire_one_monitor(mon, vpsc); /* forward call */ + } break; } - erts_destroy_monitor(rmon); done: erts_destroy_monitor(mon); } @@ -4039,6 +4290,43 @@ static void sweep_one_link(ErtsLink *lnk, void *vpsc) erts_destroy_link(lnk); } +static void +port_fire_one_monitor(ErtsMonitor *mon, void *ctx0) +{ + Process *origin; + ErtsProcLocks origin_locks; + + if (mon->type != MON_TARGET || ! is_pid(mon->pid)) { + return; + } + /* + * Proceed here if someone monitors us, we (port) are the target and + * origin is some process + */ + origin_locks = ERTS_PROC_LOCKS_MSG_SEND | ERTS_PROC_LOCK_LINK; + + origin = erts_pid2proc(NULL, 0, mon->pid, origin_locks); + if (origin) { + DeclareTmpHeapNoproc(lhp,3); + SweepContext *ctx = (SweepContext *)ctx0; + ErtsMonitor *rmon; + Eterm watched = (is_atom(mon->name) + ? TUPLE2(lhp, mon->name, erts_this_dist_entry->sysname) + : ctx->port->common.id); + + erts_queue_monitor_message(origin, &origin_locks, mon->ref, am_port, + watched, ctx->reason); + UnUseTmpHeapNoproc(3); + + rmon = erts_remove_monitor(&ERTS_P_MONITORS(origin), mon->ref); + erts_smp_proc_unlock(origin, origin_locks); + + if (rmon) { + erts_destroy_monitor(rmon); + } + } +} + /* 'from' is sending 'this_port' an exit signal, (this_port must be internal). * If reason is normal we don't do anything, *unless* from is our connected * process in which case we close the port. Any other reason kills the port. @@ -4050,39 +4338,40 @@ static void sweep_one_link(ErtsLink *lnk, void *vpsc) */ int -erts_deliver_port_exit(Port *p, Eterm from, Eterm reason, int send_closed, +erts_deliver_port_exit(Port *prt, Eterm from, Eterm reason, int send_closed, int drop_normal) { ErtsLink *lnk; - Eterm rreason; + Eterm modified_reason; erts_aint32_t state, set_state_flags; ERTS_SMP_CHK_NO_PROC_LOCKS; - ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(p)); + ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt)); - rreason = (reason == am_kill) ? am_killed : reason; + modified_reason = (reason == am_kill) ? am_killed : reason; #ifdef USE_VM_PROBES if (DTRACE_ENABLED(port_exit)) { DTRACE_CHARBUF(from_str, DTRACE_TERM_BUF_SIZE); DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE); - DTRACE_CHARBUF(rreason_str, 64); + DTRACE_CHARBUF(reason_str, 64); erts_snprintf(from_str, sizeof(DTRACE_CHARBUF_NAME(from_str)), "%T", from); - dtrace_port_str(p, port_str); - erts_snprintf(rreason_str, sizeof(DTRACE_CHARBUF_NAME(rreason_str)), "%T", rreason); - DTRACE4(port_exit, from_str, port_str, p->name, rreason_str); + dtrace_port_str(prt, port_str); + erts_snprintf(reason_str, sizeof(DTRACE_CHARBUF_NAME(reason_str)), "%T", + modified_reason); + DTRACE4(port_exit, from_str, port_str, prt->name, reason_str); } #endif - state = erts_atomic32_read_nob(&p->state); + state = erts_atomic32_read_nob(&prt->state); if (state & (ERTS_PORT_SFLGS_DEAD | ERTS_PORT_SFLG_EXITING | ERTS_PORT_SFLG_CLOSING)) return 0; - if (reason == am_normal && from != ERTS_PORT_GET_CONNECTED(p) - && from != p->common.id && drop_normal) { + if (reason == am_normal && from != ERTS_PORT_GET_CONNECTED(prt) + && from != prt->common.id && drop_normal) { return 0; } @@ -4090,53 +4379,54 @@ erts_deliver_port_exit(Port *p, Eterm from, Eterm reason, int send_closed, if (send_closed) set_state_flags |= ERTS_PORT_SFLG_SEND_CLOSED; - erts_port_task_sched_enter_exiting_state(&p->sched); + erts_port_task_sched_enter_exiting_state(&prt->sched); - state = erts_atomic32_read_bor_mb(&p->state, set_state_flags); + state = erts_atomic32_read_bor_mb(&prt->state, set_state_flags); state |= set_state_flags; - if (IS_TRACED_FL(p, F_TRACE_PORTS)) - trace_port(p, am_closed, reason); + if (IS_TRACED_FL(prt, F_TRACE_PORTS)) + trace_port(prt, am_closed, reason); - erts_trace_check_exiting(p->common.id); + erts_trace_check_exiting(prt->common.id); - set_busy_port(ERTS_Port2ErlDrvPort(p), 0); + set_busy_port(ERTS_Port2ErlDrvPort(prt), 0); - if (p->common.u.alive.reg != NULL) - (void) erts_unregister_name(NULL, 0, p, p->common.u.alive.reg->name); + if (prt->common.u.alive.reg != NULL) + (void) erts_unregister_name(NULL, 0, prt, prt->common.u.alive.reg->name); { - SweepContext sc = {p, rreason}; - lnk = ERTS_P_LINKS(p); - ERTS_P_LINKS(p) = NULL; + SweepContext sc = {prt, modified_reason}; + lnk = ERTS_P_LINKS(prt); + ERTS_P_LINKS(prt) = NULL; erts_sweep_links(lnk, &sweep_one_link, &sc); } - DRV_MONITOR_LOCK_PDL(p); + DRV_MONITOR_LOCK_PDL(prt); { - ErtsMonitor *moni = ERTS_P_MONITORS(p); - ERTS_P_MONITORS(p) = NULL; - erts_sweep_monitors(moni, &sweep_one_monitor, NULL); + SweepContext ctx = {prt, modified_reason}; + ErtsMonitor *moni = ERTS_P_MONITORS(prt); + ERTS_P_MONITORS(prt) = NULL; + erts_sweep_monitors(moni, &sweep_one_monitor, &ctx); } - DRV_MONITOR_UNLOCK_PDL(p); + DRV_MONITOR_UNLOCK_PDL(prt); - if ((state & ERTS_PORT_SFLG_DISTRIBUTION) && p->dist_entry) { - erts_do_net_exits(p->dist_entry, rreason); - erts_deref_dist_entry(p->dist_entry); - p->dist_entry = NULL; - erts_atomic32_read_band_relb(&p->state, + if ((state & ERTS_PORT_SFLG_DISTRIBUTION) && prt->dist_entry) { + erts_do_net_exits(prt->dist_entry, modified_reason); + erts_deref_dist_entry(prt->dist_entry); + prt->dist_entry = NULL; + erts_atomic32_read_band_relb(&prt->state, ~ERTS_PORT_SFLG_DISTRIBUTION); } - if ((reason != am_kill) && !is_port_ioq_empty(p)) { + if ((reason != am_kill) && !is_port_ioq_empty(prt)) { /* must turn exiting flag off */ - erts_atomic32_read_bset_relb(&p->state, + erts_atomic32_read_bset_relb(&prt->state, (ERTS_PORT_SFLG_EXITING | ERTS_PORT_SFLG_CLOSING), ERTS_PORT_SFLG_CLOSING); - flush_port(p); + flush_port(prt); } else { - terminate_port(p); + terminate_port(prt); } return 1; diff --git a/erts/emulator/beam/lttng-wrapper.h b/erts/emulator/beam/lttng-wrapper.h index 294872c365..0bc75c1552 100644 --- a/erts/emulator/beam/lttng-wrapper.h +++ b/erts/emulator/beam/lttng-wrapper.h @@ -77,23 +77,23 @@ (RQ)->scheduler->no #define LTTNG_ENABLED(Name) \ - tracepoint_enabled(com_ericsson_otp, Name) + tracepoint_enabled(org_erlang_otp, Name) /* include a special LTTNG_DO for do_tracepoint ? */ #define LTTNG1(Name, Arg1) \ - tracepoint(com_ericsson_otp, Name, (Arg1)) + tracepoint(org_erlang_otp, Name, (Arg1)) #define LTTNG2(Name, Arg1, Arg2) \ - tracepoint(com_ericsson_otp, Name, (Arg1), (Arg2)) + tracepoint(org_erlang_otp, Name, (Arg1), (Arg2)) #define LTTNG3(Name, Arg1, Arg2, Arg3) \ - tracepoint(com_ericsson_otp, Name, (Arg1), (Arg2), (Arg3)) + tracepoint(org_erlang_otp, Name, (Arg1), (Arg2), (Arg3)) #define LTTNG4(Name, Arg1, Arg2, Arg3, Arg4) \ - tracepoint(com_ericsson_otp, Name, (Arg1), (Arg2), (Arg3), (Arg4)) + tracepoint(org_erlang_otp, Name, (Arg1), (Arg2), (Arg3), (Arg4)) #define LTTNG5(Name, Arg1, Arg2, Arg3, Arg4, Arg5) \ - tracepoint(com_ericsson_otp, Name, (Arg1), (Arg2), (Arg3), (Arg4), (Arg5)) + tracepoint(org_erlang_otp, Name, (Arg1), (Arg2), (Arg3), (Arg4), (Arg5)) #else /* USE_LTTNG */ diff --git a/erts/emulator/beam/register.c b/erts/emulator/beam/register.c index 77f79fcea4..ac7096745e 100644 --- a/erts/emulator/beam/register.c +++ b/erts/emulator/beam/register.c @@ -323,7 +323,8 @@ erts_whereis_name(Process *c_p, Process** proc, ErtsProcLocks need_locks, int flags, - Port** port) + Port** port, + int lock_port) { RegProc* rp = NULL; HashValue hval; @@ -406,31 +407,33 @@ erts_whereis_name(Process *c_p, *port = NULL; else { #ifdef ERTS_SMP - if (pending_port == rp->pt) - pending_port = NULL; - else { - if (pending_port) { - /* Ahh! Registered port changed while reg lock - was unlocked... */ - erts_port_release(pending_port); - pending_port = NULL; - } + if (lock_port) { + if (pending_port == rp->pt) + pending_port = NULL; + else { + if (pending_port) { + /* Ahh! Registered port changed while reg lock + was unlocked... */ + erts_port_release(pending_port); + pending_port = NULL; + } - if (erts_smp_port_trylock(rp->pt) == EBUSY) { - Eterm id = rp->pt->common.id; /* id read only... */ - /* Unlock all locks, acquire port lock, and restart... */ - if (current_c_p_locks) { - erts_smp_proc_unlock(c_p, current_c_p_locks); - current_c_p_locks = 0; - } - reg_read_unlock(); - pending_port = erts_id2port(id); - goto restart; - } - } + if (erts_smp_port_trylock(rp->pt) == EBUSY) { + Eterm id = rp->pt->common.id; /* id read only... */ + /* Unlock all locks, acquire port lock, and restart... */ + if (current_c_p_locks) { + erts_smp_proc_unlock(c_p, current_c_p_locks); + current_c_p_locks = 0; + } + reg_read_unlock(); + pending_port = erts_id2port(id); + goto restart; + } + } + ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(rp->pt)); + } #endif *port = rp->pt; - ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(*port)); } } @@ -452,7 +455,7 @@ erts_whereis_process(Process *c_p, int flags) { Process *proc; - erts_whereis_name(c_p, c_p_locks, name, &proc, need_locks, flags, NULL); + erts_whereis_name(c_p, c_p_locks, name, &proc, need_locks, flags, NULL, 0); return proc; } diff --git a/erts/emulator/beam/register.h b/erts/emulator/beam/register.h index 88ab7b7bf1..d839f55d6b 100644 --- a/erts/emulator/beam/register.h +++ b/erts/emulator/beam/register.h @@ -49,7 +49,7 @@ int erts_register_name(Process *, Eterm, Eterm); Eterm erts_whereis_name_to_id(Process *, Eterm); void erts_whereis_name(Process *, ErtsProcLocks, Eterm, Process**, ErtsProcLocks, int, - Port**); + Port**, int); Process *erts_whereis_process(Process *, ErtsProcLocks, Eterm, diff --git a/erts/emulator/test/lttng_SUITE.erl b/erts/emulator/test/lttng_SUITE.erl index 1360751aee..6b7ad836f5 100644 --- a/erts/emulator/test/lttng_SUITE.erl +++ b/erts/emulator/test/lttng_SUITE.erl @@ -80,34 +80,34 @@ end_per_testcase(Case, _Config) -> ok. %% Not tested yet -%% com_ericsson_otp:driver_process_exit -%% com_ericsson_otp:driver_event +%% org_erlang_otp:driver_process_exit +%% org_erlang_otp:driver_event %% tracepoints %% -%% com_ericsson_otp:carrier_pool_get -%% com_ericsson_otp:carrier_pool_put -%% com_ericsson_otp:carrier_destroy -%% com_ericsson_otp:carrier_create -%% com_ericsson_otp:aio_pool_put -%% com_ericsson_otp:aio_pool_get -%% com_ericsson_otp:driver_control -%% com_ericsson_otp:driver_call -%% com_ericsson_otp:driver_finish -%% com_ericsson_otp:driver_ready_async -%% com_ericsson_otp:driver_process_exit -%% com_ericsson_otp:driver_stop -%% com_ericsson_otp:driver_flush -%% com_ericsson_otp:driver_stop_select -%% com_ericsson_otp:driver_timeout -%% com_ericsson_otp:driver_event -%% com_ericsson_otp:driver_ready_output -%% com_ericsson_otp:driver_ready_input -%% com_ericsson_otp:driver_output -%% com_ericsson_otp:driver_outputv -%% com_ericsson_otp:driver_init -%% com_ericsson_otp:driver_start -%% com_ericsson_otp:scheduler_poll +%% org_erlang_otp:carrier_pool_get +%% org_erlang_otp:carrier_pool_put +%% org_erlang_otp:carrier_destroy +%% org_erlang_otp:carrier_create +%% org_erlang_otp:aio_pool_put +%% org_erlang_otp:aio_pool_get +%% org_erlang_otp:driver_control +%% org_erlang_otp:driver_call +%% org_erlang_otp:driver_finish +%% org_erlang_otp:driver_ready_async +%% org_erlang_otp:driver_process_exit +%% org_erlang_otp:driver_stop +%% org_erlang_otp:driver_flush +%% org_erlang_otp:driver_stop_select +%% org_erlang_otp:driver_timeout +%% org_erlang_otp:driver_event +%% org_erlang_otp:driver_ready_output +%% org_erlang_otp:driver_ready_input +%% org_erlang_otp:driver_output +%% org_erlang_otp:driver_outputv +%% org_erlang_otp:driver_init +%% org_erlang_otp:driver_start +%% org_erlang_otp:scheduler_poll %% %% Testcases @@ -117,48 +117,48 @@ t_lttng_list(_Config) -> {ok, _} = cmd("lttng list -u"), ok. -%% com_ericsson_otp:carrier_pool_get -%% com_ericsson_otp:carrier_pool_put +%% org_erlang_otp:carrier_pool_get +%% org_erlang_otp:carrier_pool_put t_carrier_pool(Config) -> case have_carriers(ets_alloc) of false -> {skip, "No Memory Carriers configured on system."}; true -> - ok = lttng_start_event("com_ericsson_otp:carrier_pool*", Config), + ok = lttng_start_event("org_erlang_otp:carrier_pool*", Config), ok = ets_load(), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_otp:carrier_pool_get", Res), - ok = check_tracepoint("com_ericsson_otp:carrier_pool_put", Res), + ok = check_tracepoint("org_erlang_otp:carrier_pool_get", Res), + ok = check_tracepoint("org_erlang_otp:carrier_pool_put", Res), ok end. -%% com_ericsson_otp:carrier_destroy -%% com_ericsson_otp:carrier_create +%% org_erlang_otp:carrier_destroy +%% org_erlang_otp:carrier_create t_memory_carrier(Config) -> case have_carriers(ets_alloc) of false -> {skip, "No Memory Carriers configured on system."}; true -> - ok = lttng_start_event("com_ericsson_otp:carrier_*", Config), + ok = lttng_start_event("org_erlang_otp:carrier_*", Config), ok = ets_load(), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_otp:carrier_destroy", Res), - ok = check_tracepoint("com_ericsson_otp:carrier_create", Res), + ok = check_tracepoint("org_erlang_otp:carrier_destroy", Res), + ok = check_tracepoint("org_erlang_otp:carrier_create", Res), ok end. -%% com_ericsson_otp:aio_pool_put -%% com_ericsson_otp:aio_pool_get +%% org_erlang_otp:aio_pool_put +%% org_erlang_otp:aio_pool_get t_async_io_pool(Config) -> case have_async_threads() of false -> {skip, "No Async Threads configured on system."}; true -> - ok = lttng_start_event("com_ericsson_otp:aio_pool_*", Config), + ok = lttng_start_event("org_erlang_otp:aio_pool_*", Config), Path1 = proplists:get_value(priv_dir, Config), {ok, [[Path2]]} = init:get_argument(home), @@ -168,16 +168,16 @@ t_async_io_pool(Config) -> {ok, _} = file:list_dir(Path2), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_otp:aio_pool_put", Res), - ok = check_tracepoint("com_ericsson_otp:aio_pool_get", Res), + ok = check_tracepoint("org_erlang_otp:aio_pool_put", Res), + ok = check_tracepoint("org_erlang_otp:aio_pool_get", Res), ok end. -%% com_ericsson_otp:driver_start -%% com_ericsson_otp:driver_stop +%% org_erlang_otp:driver_start +%% org_erlang_otp:driver_stop t_driver_start_stop(Config) -> - ok = lttng_start_event("com_ericsson_otp:driver_*", Config), + ok = lttng_start_event("org_erlang_otp:driver_*", Config), timer:sleep(500), Path = proplists:get_value(priv_dir, Config), Name = filename:join(Path, "sometext.txt"), @@ -186,35 +186,35 @@ t_driver_start_stop(Config) -> {ok, Bin} = file:read_file(Name), timer:sleep(500), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_otp:driver_start", Res), - ok = check_tracepoint("com_ericsson_otp:driver_stop", Res), - ok = check_tracepoint("com_ericsson_otp:driver_control", Res), - ok = check_tracepoint("com_ericsson_otp:driver_outputv", Res), - ok = check_tracepoint("com_ericsson_otp:driver_ready_async", Res), + ok = check_tracepoint("org_erlang_otp:driver_start", Res), + ok = check_tracepoint("org_erlang_otp:driver_stop", Res), + ok = check_tracepoint("org_erlang_otp:driver_control", Res), + ok = check_tracepoint("org_erlang_otp:driver_outputv", Res), + ok = check_tracepoint("org_erlang_otp:driver_ready_async", Res), ok. -%% com_ericsson_otp:driver_control -%% com_ericsson_otp:driver_outputv -%% com_ericsson_otp:driver_ready_async +%% org_erlang_otp:driver_control +%% org_erlang_otp:driver_outputv +%% org_erlang_otp:driver_ready_async t_driver_control_ready_async(Config) -> - ok = lttng_start_event("com_ericsson_otp:driver_control", Config), - ok = lttng_start_event("com_ericsson_otp:driver_outputv", Config), - ok = lttng_start_event("com_ericsson_otp:driver_ready_async", Config), + ok = lttng_start_event("org_erlang_otp:driver_control", Config), + ok = lttng_start_event("org_erlang_otp:driver_outputv", Config), + ok = lttng_start_event("org_erlang_otp:driver_ready_async", Config), Path = proplists:get_value(priv_dir, Config), Name = filename:join(Path, "sometext.txt"), Bin = txt(), ok = file:write_file(Name, Bin), {ok, Bin} = file:read_file(Name), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_otp:driver_control", Res), - ok = check_tracepoint("com_ericsson_otp:driver_outputv", Res), - ok = check_tracepoint("com_ericsson_otp:driver_ready_async", Res), + ok = check_tracepoint("org_erlang_otp:driver_control", Res), + ok = check_tracepoint("org_erlang_otp:driver_outputv", Res), + ok = check_tracepoint("org_erlang_otp:driver_ready_async", Res), ok. -%% com_ericsson_otp:driver_ready_input -%% com_ericsson_otp:driver_ready_output +%% org_erlang_otp:driver_ready_input +%% org_erlang_otp:driver_ready_output t_driver_ready_input_output(Config) -> - ok = lttng_start_event("com_ericsson_otp:driver_ready_*", Config), + ok = lttng_start_event("org_erlang_otp:driver_ready_*", Config), timer:sleep(500), Me = self(), Pid = spawn_link(fun() -> tcp_server(Me, active) end), @@ -230,15 +230,15 @@ t_driver_ready_input_output(Config) -> timer:sleep(500), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_otp:driver_ready_input", Res), - ok = check_tracepoint("com_ericsson_otp:driver_ready_output", Res), + ok = check_tracepoint("org_erlang_otp:driver_ready_input", Res), + ok = check_tracepoint("org_erlang_otp:driver_ready_output", Res), ok. -%% com_ericsson_otp:driver_stop_select -%% com_ericsson_otp:driver_timeout +%% org_erlang_otp:driver_stop_select +%% org_erlang_otp:driver_timeout t_driver_timeout(Config) -> - ok = lttng_start_event("com_ericsson_otp:driver_*", Config), + ok = lttng_start_event("org_erlang_otp:driver_*", Config), Me = self(), Pid = spawn_link(fun() -> tcp_server(Me, timeout) end), receive {Pid, accept} -> ok end, @@ -247,16 +247,16 @@ t_driver_timeout(Config) -> receive {Pid, done} -> ok end, ok = gen_tcp:close(Sock), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_otp:driver_timeout", Res), - ok = check_tracepoint("com_ericsson_otp:driver_stop_select", Res), + ok = check_tracepoint("org_erlang_otp:driver_timeout", Res), + ok = check_tracepoint("org_erlang_otp:driver_stop_select", Res), ok. -%% com_ericsson_otp:driver_call -%% com_ericsson_otp:driver_output -%% com_ericsson_otp:driver_init -%% com_ericsson_otp:driver_finish +%% org_erlang_otp:driver_call +%% org_erlang_otp:driver_output +%% org_erlang_otp:driver_init +%% org_erlang_otp:driver_finish t_driver_caller(Config) -> - ok = lttng_start_event("com_ericsson_otp:driver_*", Config), + ok = lttng_start_event("org_erlang_otp:driver_*", Config), Drv = 'caller_drv', os:putenv("CALLER_DRV_USE_OUTPUTV", "false"), @@ -282,25 +282,25 @@ t_driver_caller(Config) -> erl_ddll:unload_driver(Drv), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_otp:driver_call", Res), - ok = check_tracepoint("com_ericsson_otp:driver_output", Res), - ok = check_tracepoint("com_ericsson_otp:driver_init", Res), - ok = check_tracepoint("com_ericsson_otp:driver_finish", Res), + ok = check_tracepoint("org_erlang_otp:driver_call", Res), + ok = check_tracepoint("org_erlang_otp:driver_output", Res), + ok = check_tracepoint("org_erlang_otp:driver_init", Res), + ok = check_tracepoint("org_erlang_otp:driver_finish", Res), ok. -%% com_ericsson_otp:scheduler_poll +%% org_erlang_otp:scheduler_poll t_scheduler_poll(Config) -> - ok = lttng_start_event("com_ericsson_otp:scheduler_poll", Config), + ok = lttng_start_event("org_erlang_otp:scheduler_poll", Config), ok = memory_load(), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_otp:scheduler_poll", Res), + ok = check_tracepoint("org_erlang_otp:scheduler_poll", Res), ok. -%% com_ericsson_otp:driver_flush +%% org_erlang_otp:driver_flush t_driver_flush(Config) -> - ok = lttng_start_event("com_ericsson_otp:driver_flush", Config), + ok = lttng_start_event("org_erlang_otp:driver_flush", Config), Me = self(), Pid = spawn_link(fun() -> tcp_server(Me, passive_no_read) end), @@ -324,7 +324,7 @@ t_driver_flush(Config) -> receive {Pid, done} -> ok end, Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_otp:driver_flush", Res), + ok = check_tracepoint("org_erlang_otp:driver_flush", Res), ok. %% @@ -416,29 +416,29 @@ tcp_server(Pid, Type) -> txt() -> <<"%% tracepoints\n" "%%\n" - "%% com_ericsson_otp:carrier_pool_get\n" - "%% com_ericsson_otp:carrier_pool_put\n" - "%% com_ericsson_otp:carrier_destroy\n" - "%% com_ericsson_otp:carrier_create\n" - "%% com_ericsson_otp:aio_pool_put\n" - "%% com_ericsson_otp:aio_pool_get\n" - "%% com_ericsson_otp:driver_control\n" - "%% com_ericsson_otp:driver_call\n" - "%% com_ericsson_otp:driver_finish\n" - "%% com_ericsson_otp:driver_ready_async\n" - "%% com_ericsson_otp:driver_process_exit\n" - "%% com_ericsson_otp:driver_stop\n" - "%% com_ericsson_otp:driver_flush\n" - "%% com_ericsson_otp:driver_stop_select\n" - "%% com_ericsson_otp:driver_timeout\n" - "%% com_ericsson_otp:driver_event\n" - "%% com_ericsson_otp:driver_ready_output\n" - "%% com_ericsson_otp:driver_ready_input\n" - "%% com_ericsson_otp:driver_output\n" - "%% com_ericsson_otp:driver_outputv\n" - "%% com_ericsson_otp:driver_init\n" - "%% com_ericsson_otp:driver_start\n" - "%% com_ericsson_otp:scheduler_poll">>. + "%% org_erlang_otp:carrier_pool_get\n" + "%% org_erlang_otp:carrier_pool_put\n" + "%% org_erlang_otp:carrier_destroy\n" + "%% org_erlang_otp:carrier_create\n" + "%% org_erlang_otp:aio_pool_put\n" + "%% org_erlang_otp:aio_pool_get\n" + "%% org_erlang_otp:driver_control\n" + "%% org_erlang_otp:driver_call\n" + "%% org_erlang_otp:driver_finish\n" + "%% org_erlang_otp:driver_ready_async\n" + "%% org_erlang_otp:driver_process_exit\n" + "%% org_erlang_otp:driver_stop\n" + "%% org_erlang_otp:driver_flush\n" + "%% org_erlang_otp:driver_stop_select\n" + "%% org_erlang_otp:driver_timeout\n" + "%% org_erlang_otp:driver_event\n" + "%% org_erlang_otp:driver_ready_output\n" + "%% org_erlang_otp:driver_ready_input\n" + "%% org_erlang_otp:driver_output\n" + "%% org_erlang_otp:driver_outputv\n" + "%% org_erlang_otp:driver_init\n" + "%% org_erlang_otp:driver_start\n" + "%% org_erlang_otp:scheduler_poll">>. load_driver(Dir, Driver) -> case erl_ddll:load_driver(Dir, Driver) of diff --git a/erts/emulator/test/monitor_SUITE.erl b/erts/emulator/test/monitor_SUITE.erl index 8955e62df5..90d2bd8c5d 100644 --- a/erts/emulator/test/monitor_SUITE.erl +++ b/erts/emulator/test/monitor_SUITE.erl @@ -21,6 +21,7 @@ -module(monitor_SUITE). -include_lib("common_test/include/ct.hrl"). +-include_lib("eunit/include/eunit.hrl"). -export([all/0, suite/0, groups/0, case_1/1, case_1a/1, case_2/1, case_2a/1, mon_e_1/1, demon_e_1/1, demon_1/1, @@ -706,7 +707,7 @@ named_down(Config) when is_list(Config) -> spawn_opt(fun () -> WFun = fun (F, hej) -> F(F, hopp); -(F, hopp) -> F(F, hej) + (F, hopp) -> F(F, hej) end, NoSchedulers = erlang:system_info(schedulers_online), lists:foreach(fun (_) -> @@ -726,13 +727,14 @@ named_down(Config) when is_list(Config) -> NamedProc = spawn_link(fun () -> receive after infinity -> ok end end), - true = register(Name, NamedProc), + ?assertEqual(true, register(Name, NamedProc)), unlink(NamedProc), exit(NamedProc, bang), Mon = erlang:monitor(process, Name), - receive {'DOWN',Mon, _, _, _} -> ok end, - true = register(Name, self()), - true = unregister(Name), + receive {'DOWN',Mon, _, _, bang} -> ok + after 3000 -> ?assert(false) end, + ?assertEqual(true, register(Name, self())), + ?assertEqual(true, unregister(Name)), process_flag(priority,Prio), ok. diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl index 79abcbde5f..ee07699884 100644 --- a/erts/emulator/test/port_SUITE.erl +++ b/erts/emulator/test/port_SUITE.erl @@ -74,27 +74,68 @@ %% --export([all/0, suite/0, groups/0, - init_per_testcase/2, end_per_testcase/2, - init_per_suite/1, end_per_suite/1, - stream_small/1, stream_big/1, - basic_ping/1, slow_writes/1, bad_packet/1, bad_port_messages/1, - mul_basic/1, mul_slow_writes/1, - dying_port/1, port_program_with_path/1, - open_input_file_port/1, open_output_file_port/1, - count_fds/1, - iter_max_ports/1, eof/1, input_only/1, output_only/1, - name1/1, - t_binary/1, parallell/1, t_exit/1, - env/1, huge_env/1, bad_env/1, cd/1, exit_status/1, - bad_args/1, - tps_16_bytes/1, tps_1K/1, line/1, stderr_to_stdout/1, - otp_3906/1, otp_4389/1, win_massive/1, win_massive_client/1, - mix_up_ports/1, otp_5112/1, otp_5119/1, otp_6224/1, - exit_status_multi_scheduling_block/1, ports/1, - spawn_driver/1, spawn_executable/1, close_deaf_port/1, - port_setget_data/1, - unregister_name/1, parallelism_option/1]). +-export([all/0, suite/0, groups/0, init_per_testcase/2, end_per_testcase/2, + init_per_suite/1, end_per_suite/1]). +-export([ + bad_args/1, + bad_env/1, + bad_packet/1, + bad_port_messages/1, + basic_ping/1, + cd/1, + close_deaf_port/1, + count_fds/1, + dying_port/1, + env/1, + eof/1, + exit_status/1, + exit_status_multi_scheduling_block/1, + huge_env/1, + input_only/1, + iter_max_ports/1, + line/1, + mix_up_ports/1, + mon_port_invalid_type/1, + mon_port_bad_named/1, + mon_port_bad_remote_on_local/1, + mon_port_local/1, + mon_port_name_demonitor/1, + mon_port_named/1, + mon_port_origin_dies/1, + mon_port_pid_demonitor/1, + mon_port_remote_on_remote/1, + mon_port_driver_die/1, + mon_port_driver_die_demonitor/1, + mul_basic/1, + mul_slow_writes/1, + name1/1, + open_input_file_port/1, + open_output_file_port/1, + otp_3906/1, + otp_4389/1, + otp_5112/1, + otp_5119/1, + otp_6224/1, + output_only/1, + parallelism_option/1, + parallell/1, + port_program_with_path/1, + port_setget_data/1, + ports/1, + slow_writes/1, + spawn_driver/1, + spawn_executable/1, + stderr_to_stdout/1, + stream_big/1, + stream_small/1, + t_binary/1, + t_exit/1, + tps_16_bytes/1, + tps_1K/1, + unregister_name/1, + win_massive/1, + win_massive_client/1 +]). -export([do_iter_max_ports/2]). @@ -105,12 +146,13 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("kernel/include/file.hrl"). +-include_lib("eunit/include/eunit.hrl"). suite() -> [{ct_hooks,[ts_install_cth]}, {timetrap, {seconds, 10}}]. -all() -> +all() -> [otp_6224, {group, stream}, basic_ping, slow_writes, bad_packet, bad_port_messages, {group, options}, {group, multiple_packets}, parallell, dying_port, @@ -123,14 +165,32 @@ all() -> exit_status_multi_scheduling_block, ports, spawn_driver, spawn_executable, close_deaf_port, unregister_name, port_setget_data, - parallelism_option]. - -groups() -> + parallelism_option, + mon_port_invalid_type, + mon_port_local, + mon_port_remote_on_remote, + mon_port_bad_remote_on_local, + mon_port_origin_dies, + mon_port_named, + mon_port_bad_named, + mon_port_pid_demonitor, + mon_port_name_demonitor, + mon_port_driver_die, + mon_port_driver_die_demonitor + ]. + +groups() -> [{stream, [], [stream_small, stream_big]}, {options, [], [t_binary, eof, input_only, output_only]}, {multiple_packets, [], [mul_basic, mul_slow_writes]}, {tps, [], [tps_16_bytes, tps_1K]}]. +init_per_testcase(Case, Config) when Case =:= mon_port_driver_die; + Case =:= mon_port_driver_die_demonitor -> + case erlang:system_info(schedulers_online) of + 1 -> {skip, "Need 2 schedulers to run testcase"}; + _ -> Config + end; init_per_testcase(Case, Config) -> [{testcase, Case} |Config]. @@ -160,7 +220,7 @@ do_win_massive() -> ct:timetrap({minutes, 6}), SuiteDir = filename:dirname(code:which(?MODULE)), Ports = " +Q 8192", - {ok, Node} = + {ok, Node} = test_server:start_node(win_massive, slave, [{args, " -pa " ++ SuiteDir ++ Ports}]), @@ -169,7 +229,7 @@ do_win_massive() -> ok. win_massive_client(N) -> - {ok,P}=gen_tcp:listen(?WIN_MASSIVE_PORT,[{reuseaddr,true}]), + {ok,P}=gen_tcp:listen(?WIN_MASSIVE_PORT,[{reuseaddr,true}]), L = win_massive_loop(P,N), Len = length(L), lists:foreach(fun(E) -> @@ -278,7 +338,7 @@ bad_port_messages(Config) when is_list(Config) -> bad_message(PortTest, {self(),{connect,no_pid}}), ok. -bad_message(PortTest, Message) -> +bad_message(PortTest, Message) -> P = open_port({spawn,PortTest}, []), P ! Message, receive @@ -773,7 +833,7 @@ line(Config) when is_list(Config) -> S1 = lists:flatten(io_lib:format("-l~w", [length(L1)])), io:format("S1 = ~w, L1 = ~w~n", [S1,L1]), port_expect(Config,[{L1, - [{eol, Packet1}, {noeol, Packet2}, eof]}], 0, + [{eol, Packet1}, {noeol, Packet2}, eof]}], 0, S1, [{line,Siz},eof]), %% Test that lonely <CR> Don't get treated as newlines port_expect(Config,[{lists:append([Packet1, [13], Packet2, @@ -844,9 +904,9 @@ env(Config) when is_list(Config) -> {"glurf","a glorfy string"}]), %% A lot of non existing variables (mingled with existing) - NotExistingList = [{lists:flatten(io_lib:format("V~p_not_existing",[X])),false} + NotExistingList = [{lists:flatten(io_lib:format("V~p_not_existing",[X])),false} || X <- lists:seq(1,150)], - ExistingList = [{lists:flatten(io_lib:format("V~p_existing",[X])),"a_value"} + ExistingList = [{lists:flatten(io_lib:format("V~p_existing",[X])),"a_value"} || X <- lists:seq(1,150)], env_slave(Temp, lists:sort(ExistingList ++ NotExistingList)), ok. @@ -1320,22 +1380,22 @@ spawn_driver(Config) when is_list(Config) -> ok = load_driver(Path, "echo_drv"), Port = erlang:open_port({spawn_driver, "echo_drv"}, []), Port ! {self(), {command, "Hello port!"}}, - receive - {Port, {data, "Hello port!"}} = Msg1 -> + receive + {Port, {data, "Hello port!"}} = Msg1 -> io:format("~p~n", [Msg1]), - ok; + ok; Other -> ct:fail({unexpected, Other}) end, Port ! {self(), close}, receive {Port, closed} -> ok end, - Port2 = erlang:open_port({spawn_driver, "echo_drv -Hello port?"}, + Port2 = erlang:open_port({spawn_driver, "echo_drv -Hello port?"}, []), - receive - {Port2, {data, "Hello port?"}} = Msg2 -> + receive + {Port2, {data, "Hello port?"}} = Msg2 -> io:format("~p~n", [Msg2]), - ok; + ok; Other2 -> ct:fail({unexpected2, Other2}) end, @@ -1354,23 +1414,23 @@ parallelism_option(Config) when is_list(Config) -> [{parallelism, true}]), {parallelism, true} = erlang:port_info(Port, parallelism), Port ! {self(), {command, "Hello port!"}}, - receive - {Port, {data, "Hello port!"}} = Msg1 -> + receive + {Port, {data, "Hello port!"}} = Msg1 -> io:format("~p~n", [Msg1]), - ok; + ok; Other -> ct:fail({unexpected, Other}) end, Port ! {self(), close}, receive {Port, closed} -> ok end, - Port2 = erlang:open_port({spawn_driver, "echo_drv -Hello port?"}, + Port2 = erlang:open_port({spawn_driver, "echo_drv -Hello port?"}, [{parallelism, false}]), {parallelism, false} = erlang:port_info(Port2, parallelism), - receive - {Port2, {data, "Hello port?"}} = Msg2 -> + receive + {Port2, {data, "Hello port?"}} = Msg2 -> io:format("~p~n", [Msg2]), - ok; + ok; Other2 -> ct:fail({unexpected2, Other2}) end, @@ -1389,20 +1449,20 @@ spawn_executable(Config) when is_list(Config) -> ["echo_args"] = run_echo_args(DataDir,[binary, "echo_args"]), ["echo_arguments"] = run_echo_args(DataDir,["echo_arguments"]), ["echo_arguments"] = run_echo_args(DataDir,[binary, "echo_arguments"]), - [ExactFile1,"hello world","dlrow olleh"] = + [ExactFile1,"hello world","dlrow olleh"] = run_echo_args(DataDir,[ExactFile1,"hello world","dlrow olleh"]), [ExactFile1] = run_echo_args(DataDir,[default]), [ExactFile1] = run_echo_args(DataDir,[binary, default]), - [ExactFile1,"hello world","dlrow olleh"] = + [ExactFile1,"hello world","dlrow olleh"] = run_echo_args(DataDir,[switch_order,ExactFile1,"hello world", "dlrow olleh"]), - [ExactFile1,"hello world","dlrow olleh"] = + [ExactFile1,"hello world","dlrow olleh"] = run_echo_args(DataDir,[binary,switch_order,ExactFile1,"hello world", "dlrow olleh"]), [ExactFile1,"hello world","dlrow olleh"] = run_echo_args(DataDir,[default,"hello world","dlrow olleh"]), - [ExactFile1,"hello world","dlrow olleh"] = + [ExactFile1,"hello world","dlrow olleh"] = run_echo_args_2("\""++ExactFile1++"\" "++"\"hello world\" \"dlrow olleh\""), [ExactFile1,"hello world","dlrow olleh"] = run_echo_args_2(unicode:characters_to_binary("\""++ExactFile1++"\" "++"\"hello world\" \"dlrow olleh\"")), @@ -1418,7 +1478,7 @@ spawn_executable(Config) when is_list(Config) -> [ExactFile2] = run_echo_args(SpaceDir,[]), ["echo_args"] = run_echo_args(SpaceDir,["echo_args"]), ["echo_arguments"] = run_echo_args(SpaceDir,["echo_arguments"]), - [ExactFile2,"hello world","dlrow olleh"] = + [ExactFile2,"hello world","dlrow olleh"] = run_echo_args(SpaceDir,[ExactFile2,"hello world","dlrow olleh"]), [ExactFile2,"hello world","dlrow olleh"] = run_echo_args(SpaceDir,[binary, ExactFile2,"hello world","dlrow olleh"]), @@ -1429,16 +1489,16 @@ spawn_executable(Config) when is_list(Config) -> run_echo_args(SpaceDir,[binary, ExactFile2,"hello \"world\"","\"dlrow\" olleh"]), [ExactFile2] = run_echo_args(SpaceDir,[default]), - [ExactFile2,"hello world","dlrow olleh"] = + [ExactFile2,"hello world","dlrow olleh"] = run_echo_args(SpaceDir,[switch_order,ExactFile2,"hello world", "dlrow olleh"]), - [ExactFile2,"hello world","dlrow olleh"] = + [ExactFile2,"hello world","dlrow olleh"] = run_echo_args(SpaceDir,[default,"hello world","dlrow olleh"]), - [ExactFile2,"hello world","dlrow olleh"] = + [ExactFile2,"hello world","dlrow olleh"] = run_echo_args_2("\""++ExactFile2++"\" "++"\"hello world\" \"dlrow olleh\""), [ExactFile2,"hello world","dlrow olleh"] = run_echo_args_2(unicode:characters_to_binary("\""++ExactFile2++"\" "++"\"hello world\" \"dlrow olleh\"")), - ExeExt = + ExeExt = case string:to_lower(lists:last(string:tokens(ExactFile2,"."))) of "exe" -> ".exe"; @@ -1452,17 +1512,17 @@ spawn_executable(Config) when is_list(Config) -> [ExactFile3] = run_echo_args(SpaceDir,Executable2,[]), ["echo_args"] = run_echo_args(SpaceDir,Executable2,["echo_args"]), ["echo_arguments"] = run_echo_args(SpaceDir,Executable2,["echo_arguments"]), - [ExactFile3,"hello world","dlrow olleh"] = + [ExactFile3,"hello world","dlrow olleh"] = run_echo_args(SpaceDir,Executable2,[ExactFile3,"hello world","dlrow olleh"]), [ExactFile3] = run_echo_args(SpaceDir,Executable2,[default]), - [ExactFile3,"hello world","dlrow olleh"] = + [ExactFile3,"hello world","dlrow olleh"] = run_echo_args(SpaceDir,Executable2, [switch_order,ExactFile3,"hello world", "dlrow olleh"]), - [ExactFile3,"hello world","dlrow olleh"] = + [ExactFile3,"hello world","dlrow olleh"] = run_echo_args(SpaceDir,Executable2, [default,"hello world","dlrow olleh"]), - [ExactFile3,"hello world","dlrow olleh"] = + [ExactFile3,"hello world","dlrow olleh"] = run_echo_args_2("\""++ExactFile3++"\" "++"\"hello world\" \"dlrow olleh\""), [ExactFile3,"hello world","dlrow olleh"] = run_echo_args_2(unicode:characters_to_binary("\""++ExactFile3++"\" "++"\"hello world\" \"dlrow olleh\"")), @@ -1510,11 +1570,11 @@ test_bat_file(Dir) -> <<"\r\n">>], file:write_file(Full,list_to_binary(D)), EF = filename:basename(FN), - [DN,"hello","world"] = + [DN,"hello","world"] = run_echo_args(Dir,FN, [default,"hello","world"]), %% The arg0 argumant should be ignored when running batch files - [DN,"hello","world"] = + [DN,"hello","world"] = run_echo_args(Dir,FN, ["knaskurt","hello","world"]), EF = filename:basename(DN), @@ -1533,10 +1593,10 @@ test_sh_file(Dir) -> <<"done\n">>], file:write_file(Full,list_to_binary(D)), chmodplusx(Full), - [Full,"hello","world"] = + [Full,"hello","world"] = run_echo_args(Dir,FN, [default,"hello","world"]), - [Full,"hello","world of spaces"] = + [Full,"hello","world of spaces"] = run_echo_args(Dir,FN, [default,"hello","world of spaces"]), file:write_file(filename:join([Dir,"testfile1"]),<<"testdata1">>), @@ -1544,7 +1604,7 @@ test_sh_file(Dir) -> Pattern = filename:join([Dir,"testfile*"]), L = filelib:wildcard(Pattern), 2 = length(L), - [Full,"hello",Pattern] = + [Full,"hello",Pattern] = run_echo_args(Dir,FN, [default,"hello",Pattern]), ok. @@ -1620,10 +1680,10 @@ mix_up_ports(Config) when is_list(Config) -> ok = load_driver(Path, "echo_drv"), Port = erlang:open_port({spawn, "echo_drv"}, []), Port ! {self(), {command, "Hello port!"}}, - receive - {Port, {data, "Hello port!"}} = Msg1 -> + receive + {Port, {data, "Hello port!"}} = Msg1 -> io:format("~p~n", [Msg1]), - ok; + ok; Other -> ct:fail({unexpected, Other}) end, @@ -1631,7 +1691,7 @@ mix_up_ports(Config) when is_list(Config) -> receive {Port, closed} -> ok end, loop(start, done, fun(P) -> - Q = + Q = (catch erlang:open_port({spawn, "echo_drv"}, [])), %% io:format("~p ", [Q]), if is_port(Q) -> @@ -1642,7 +1702,7 @@ mix_up_ports(Config) when is_list(Config) -> end end), Port ! {self(), {command, "Hello again port!"}}, - receive + receive Msg2 -> ct:fail({unexpected, Msg2}) after 1000 -> @@ -1802,7 +1862,7 @@ exit_status_msb_test(Config, SleepSecs) when is_list(Config) -> %% We want to start port programs from as many schedulers as possible %% and we want these port programs to terminate while multi-scheduling %% is blocked. - %% + %% NoSchedsOnln = erlang:system_info(schedulers_online), Parent = self(), io:format("SleepSecs = ~p~n", [SleepSecs]), @@ -2214,7 +2274,7 @@ ports_snapshots(0, _, _) -> ok; ports_snapshots(Iter, TrafficPid, OtherPorts) -> - TrafficPid ! start, + TrafficPid ! start, receive after 1 -> ok end, Snapshot = erlang:ports(), @@ -2243,7 +2303,7 @@ ports_traffic_stopped(MaxPorts, {PortList, PortCnt}) -> end. ports_traffic_started(MaxPorts, {PortList, PortCnt}, EventList) -> - receive + receive {Pid, stop} -> %%io:format("Traffic stopped in ~p\n",[self()]), Pid ! {self(), EventList, PortList}, @@ -2256,7 +2316,7 @@ ports_traffic_started(MaxPorts, {PortList, PortCnt}, EventList) -> ports_traffic_do(MaxPorts, {PortList, PortCnt}, EventList) -> N = uniform(MaxPorts), case N > PortCnt of - true -> % Open port + true -> % Open port P = open_port({spawn, "exit_drv"}, []), %%io:format("Created port ~p\n",[P]), ports_traffic_started(MaxPorts, {[P|PortList], PortCnt+1}, @@ -2270,7 +2330,7 @@ ports_traffic_do(MaxPorts, {PortList, PortCnt}, EventList) -> [{close,P}|EventList]) end. -ports_verify(Ports, PortsAfter, EventList) -> +ports_verify(Ports, PortsAfter, EventList) -> %%io:format("Candidate=~p\nEvents=~p\n", [PortsAfter, EventList]), case lists:sort(Ports) =:= lists:sort(PortsAfter) of true -> @@ -2280,10 +2340,10 @@ ports_verify(Ports, PortsAfter, EventList) -> %% Note that we track the event list "backwards", undoing open/close: case EventList of [{open,P} | Tail] -> - ports_verify(Ports, lists:delete(P,PortsAfter), Tail); + ports_verify(Ports, lists:delete(P,PortsAfter), Tail); [{close,P} | Tail] -> - ports_verify(Ports, [P | PortsAfter], Tail); + ports_verify(Ports, [P | PortsAfter], Tail); [] -> ct:fail("Inconsistent snapshot from erlang:ports()") @@ -2391,3 +2451,227 @@ wait_until(Fun) -> receive after 100 -> ok end, wait_until(Fun) end. + +%% Attempt to monitor pid as port, and port as pid +mon_port_invalid_type(_Config) -> + Port = hd(erlang:ports()), + ?assertError(badarg, erlang:monitor(port, self())), + ?assertError(badarg, erlang:monitor(process, Port)), + ok. + +%% With local port +mon_port_local(Config) -> + Port1 = create_port(Config, ["-h1", "-q"]), % will close after we send 1 byte + Ref1 = erlang:monitor(port, Port1), + ?assertMatch({proc_monitors, true, port_monitored_by, true}, + port_is_monitored(self(), Port1)), + Port1 ! {self(), {command, <<"1">>}}, % port test will close self immediately + receive ExitP1 -> ?assertMatch({'DOWN', Ref1, port, Port1, _}, ExitP1) + after 1000 -> ?assert(false) end, + ?assertMatch({proc_monitors, false, port_monitored_by, false}, + port_is_monitored(self(), Port1)), + + %% Trying to re-monitor a port which exists but is not healthy will + %% succeed but then will immediately send DOWN + Ref2 = erlang:monitor(port, Port1), + receive ExitP2 -> ?assertMatch({'DOWN', Ref2, port, Port1, _}, ExitP2) + after 1000 -> ?assert(false) end, + ok. + +%% With remote port on remote node (should fail) +mon_port_remote_on_remote(_Config) -> + Port3 = binary_to_term(<<131, 102, % Ext term format: PORT_EXT + 100, 0, 13, "fgsfds@fgsfds", % Node :: ATOM_EXT + 1:32/big, % Id + 0>>), % Creation + ?assertError(badarg, erlang:monitor(port, Port3)), + ok. + +%% Remote port belongs to this node and does not exist +%% Port4 produces #Port<0.167772160> which should not exist in a test run +mon_port_bad_remote_on_local(_Config) -> + Port4 = binary_to_term(<<131, 102, % Ext term format: PORT_EXT + 100, 0, 13, "nonode@nohost", % Node + 167772160:32/big, % Id + 0>>), % Creation + ?assertError(badarg, erlang:monitor(port, Port4)), + ok. + +%% Monitor owner (origin) dies before port is closed +mon_port_origin_dies(Config) -> + Port5 = create_port(Config, ["-h1", "-q"]), % will close after we send 1 byte + Self5 = self(), + Proc5 = spawn(fun() -> + Self5 ! test5_started, + erlang:monitor(port, Port5), + receive stop -> ok end + end), + erlang:monitor(process, Proc5), % we want to sync with its death + receive test5_started -> ok + after 1000 -> ?assert(false) end, + ?assertMatch({proc_monitors, true, port_monitored_by, true}, + port_is_monitored(Proc5, Port5)), + Proc5 ! stop, + % receive from monitor (removing race condition) + receive ExitP5 -> ?assertMatch({'DOWN', _, process, Proc5, _}, ExitP5) + after 1000 -> ?assert(false) end, + ?assertMatch({proc_monitors, false, port_monitored_by, false}, + port_is_monitored(Proc5, Port5)), + Port5 ! {self(), {command, <<"1">>}}, % make port quit + ok. + +%% Monitor a named port +mon_port_named(Config) -> + Name6 = test_port6, + Port6 = create_port(Config, ["-h1", "-q"]), % will close after we send 1 byte + erlang:register(Name6, Port6), + erlang:monitor(port, Name6), + ?assertMatch({proc_monitors, true, port_monitored_by, true}, + port_is_monitored(self(), Name6)), + Port6 ! {self(), {command, <<"1">>}}, % port test will close self immediately + receive ExitP6 -> ?assertMatch({'DOWN', _, port, {Name6, _}, _}, ExitP6) + after 1000 -> ?assert(false) end, + ?assertMatch({proc_monitors, false, port_monitored_by, false}, + port_is_monitored(self(), Name6)), + ok. + +%% Named does not exist: Should succeed but immediately send 'DOWN' +mon_port_bad_named(_Config) -> + Name7 = test_port7, + erlang:monitor(port, Name7), + receive {'DOWN', _, port, {Name7, _}, noproc} -> ok + after 1000 -> ?assert(false) end, + ok. + +%% Monitor a pid and demonitor by ref +mon_port_pid_demonitor(Config) -> + Port8 = create_port(Config, ["-h1", "-q"]), % will close after we send 1 byte + Ref8 = erlang:monitor(port, Port8), + ?assertMatch({proc_monitors, true, port_monitored_by, true}, + port_is_monitored(self(), Port8)), + erlang:demonitor(Ref8), + ?assertMatch({proc_monitors, false, port_monitored_by, false}, + port_is_monitored(self(), Port8)), + Port8 ! {self(), {command, <<"1">>}}, % port test will close self immediately + ok. + +%% Monitor by name and demonitor by ref +mon_port_name_demonitor(Config) -> + Name9 = test_port9, + Port9 = create_port(Config, ["-h1", "-q"]), % will close after we send 1 byte + erlang:register(Name9, Port9), + Ref9 = erlang:monitor(port, Name9), + ?assertMatch({proc_monitors, true, port_monitored_by, true}, + port_is_monitored(self(), Name9)), + erlang:demonitor(Ref9), + ?assertMatch({proc_monitors, false, port_monitored_by, false}, + port_is_monitored(self(), Name9)), + Port9 ! {self(), {command, <<"1">>}}, % port test will close self immediately + ok. + +%% 1. Spawn a port which will sleep 3 seconds +%% 2. Port driver and dies horribly (via C driver_failure call). This should +%% mark port as exiting or something. +%% 3. While the command happens, a monitor is requested on the port +mon_port_driver_die(Config) -> + erlang:process_flag(scheduler, 1), + + Path = proplists:get_value(data_dir, Config), + ok = load_driver(Path, "sleep_failure_drv"), + Port = open_port({spawn, "sleep_failure_drv"}, []), + + Self = self(), + erlang:spawn_opt(fun() -> + timer:sleep(250), + Ref = erlang:monitor(port, Port), + %% Now check that msg actually arrives + receive + {'DOWN', Ref, _Port2, _, _} = M -> Self ! M + after 3000 -> Self ! no_down_message + end + end,[{scheduler, 2}]), + Port ! {self(), {command, "Fail, please!"}}, + receive + A when is_atom(A) -> ?assertEqual(A, 'A_should_be_printed'); + {'DOWN', _R, port, Port, noproc} -> ok; + {'DOWN', _R, _P, _, _} = M -> ct:fail({got_wrong_down,M}) + after 5000 -> ?assert(false) + end, + ok. + + +%% 1. Spawn a port which will sleep 3 seconds +%% 2. Monitor port +%% 3. Port driver and dies horribly (via C driver_failure call). This should +%% mark port as exiting or something. +%% 4. While the command happens, a demonitor is requested on the port +mon_port_driver_die_demonitor(Config) -> + erlang:process_flag(scheduler, 1), + + Path = proplists:get_value(data_dir, Config), + ok = load_driver(Path, "sleep_failure_drv"), + Port = open_port({spawn, "sleep_failure_drv"}, []), + + Self = self(), + erlang:spawn_opt( + fun() -> + Ref = erlang:monitor(port, Port), + Self ! Ref, + timer:sleep(250), + erlang:demonitor(Ref), + %% Now check that msg still arrives, + %% the demon should have arrived after + %% the port exited + receive + {'DOWN', Ref, _Port2, _, _} = M -> Self ! M + after 3000 -> Self ! no_down_message + end + end,[{scheduler, 2}]), + Ref = receive R -> R end, + Port ! {self(), {command, "Fail, please!"}}, + receive + {'DOWN', Ref, port, Port, normal} -> ok; + {'DOWN', _R, _P, _, _} = M -> ct:fail({got_wrong_down,M}) + after 5000 -> ?assert(false) + end, + ok. + +%% @doc Makes a controllable port for testing. Underlying mechanism of this +%% port is not important, only important is our ability to close/kill it or +%% have it monitored. +create_port(Config, Args) -> + DataDir = ?config(data_dir, Config), + %% Borrow port test utility from port SUITE + Program = filename:join([DataDir, "port_test"]), + erlang:open_port({spawn_executable, Program}, [{args, Args}]). + +%% @doc Checks if process Pid exists, and if so, if its monitoring (or not) +%% the Port (or if port doesn't exist, we assume answer is no). +port_is_monitored(Pid, Port) when is_pid(Pid), is_port(Port) -> + %% Variant for when port is a port id (port()) + A = case erlang:process_info(Pid, monitors) of + undefined -> false; + {monitors, ProcMTargets} -> lists:member({port, Port}, ProcMTargets) + end, + B = case erlang:port_info(Port, monitored_by) of + undefined -> false; + {monitored_by, PortMonitors} -> lists:member(Pid, PortMonitors) + end, + {proc_monitors, A, port_monitored_by, B}; +port_is_monitored(Pid, PortName) when is_pid(Pid), is_atom(PortName) -> + %% Variant for when port is an atom + A = case erlang:process_info(Pid, monitors) of + undefined -> false; + {monitors, ProcMTargets} -> + lists:member({port, {PortName, node()}}, ProcMTargets) + end, + B = case erlang:whereis(PortName) of + undefined -> false; % name is not registered or is dead + PortId -> + case erlang:port_info(PortId, monitored_by) of + undefined -> false; % is dead + {monitored_by, PortMonitors} -> + lists:member(Pid, PortMonitors) + end + end, + {proc_monitors, A, port_monitored_by, B}. diff --git a/erts/emulator/test/port_SUITE_data/Makefile.src b/erts/emulator/test/port_SUITE_data/Makefile.src index ff822ae720..fb7685c4b6 100644 --- a/erts/emulator/test/port_SUITE_data/Makefile.src +++ b/erts/emulator/test/port_SUITE_data/Makefile.src @@ -4,7 +4,7 @@ CFLAGS = @CFLAGS@ -I@erl_include@ @DEFS@ CROSSLDFLAGS = @CROSSLDFLAGS@ PROGS = port_test@exe@ echo_args@exe@ dead_port@exe@ -DRIVERS = echo_drv@dll@ exit_drv@dll@ failure_drv@dll@ +DRIVERS = echo_drv@dll@ exit_drv@dll@ failure_drv@dll@ sleep_failure_drv@dll@ all: $(PROGS) $(DRIVERS) port_test.@EMULATOR@ diff --git a/erts/emulator/test/port_SUITE_data/sleep_failure_drv.c b/erts/emulator/test/port_SUITE_data/sleep_failure_drv.c new file mode 100644 index 0000000000..1f52646572 --- /dev/null +++ b/erts/emulator/test/port_SUITE_data/sleep_failure_drv.c @@ -0,0 +1,76 @@ +#include <stdio.h> +#include "erl_driver.h" +#ifdef __WIN32__ +# include <windows.h> +#else +# include <unistd.h> +#endif + +typedef struct _erl_drv_data FailureDrvData; + +static FailureDrvData *failure_drv_start(ErlDrvPort, char *); +static void failure_drv_stop(FailureDrvData *); +static void failure_drv_output(ErlDrvData, char *, ErlDrvSizeT); +static void failure_drv_finish(void); + +static ErlDrvEntry failure_drv_entry = { + NULL, /* init */ + failure_drv_start, + failure_drv_stop, + failure_drv_output, + NULL, /* ready_input */ + NULL, /* ready_output */ + "sleep_failure_drv", + NULL, /* finish */ + NULL, /* handle */ + NULL, /* control */ + NULL, /* timeout */ + NULL, /* outputv */ + NULL, /* ready_async */ + NULL, + NULL, + NULL, + ERL_DRV_EXTENDED_MARKER, + ERL_DRV_EXTENDED_MAJOR_VERSION, + ERL_DRV_EXTENDED_MINOR_VERSION, + 0, + NULL, + NULL, + NULL, +}; + + + +/* ------------------------------------------------------------------------- +** Entry functions +**/ + +DRIVER_INIT(failure_drv) +{ + return &failure_drv_entry; +} + +static FailureDrvData *failure_drv_start(ErlDrvPort port, char *command) { + void *void_ptr; + + return void_ptr = port; +} + +static void failure_drv_stop(FailureDrvData *data_p) { +} + +static void failure_drv_output(ErlDrvData drv_data, char *buf, ErlDrvSizeT len) { + FailureDrvData *data_p = (FailureDrvData *) drv_data; + void *void_ptr; + ErlDrvPort port = void_ptr = data_p; + +#ifdef __WIN32__ + Sleep(3000); +#else + sleep(3); +#endif + driver_failure(port, 0); +} + +static void failure_drv_finish() { +} diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 94f3078173..edf79b8f75 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -1206,16 +1206,18 @@ module_loaded(_Module) -> erlang:nif_error(undefined). -type registered_name() :: atom(). - -type registered_process_identifier() :: registered_name() | {registered_name(), node()}. - -type monitor_process_identifier() :: pid() | registered_process_identifier(). +-type monitor_port_identifier() :: port() | registered_name(). %% monitor/2 --spec monitor(process, monitor_process_identifier()) -> MonitorRef when - MonitorRef :: reference(); - (time_offset, clock_service) -> MonitorRef when - MonitorRef :: reference(). +-spec monitor + (process, monitor_process_identifier()) -> MonitorRef + when MonitorRef :: reference(); + (port, monitor_port_identifier()) -> MonitorRef + when MonitorRef :: reference(); + (time_offset, clock_service) -> MonitorRef + when MonitorRef :: reference(). monitor(_Type, _Item) -> erlang:nif_error(undefined). @@ -2160,7 +2162,7 @@ process_flag(_Flag, _Value) -> {max_heap_size, MaxHeapSize :: max_heap_size()} | {monitored_by, Pids :: [pid()]} | {monitors, - Monitors :: [{process, Pid :: pid() | + Monitors :: [{process | port, Pid :: pid() | port() | {RegName :: atom(), Node :: node()}}]} | {message_queue_data, MQD :: message_queue_data()} | {priority, Level :: priority_level()} | @@ -3087,6 +3089,9 @@ port_info(Port) -> (Port, monitors) -> {monitors, Monitors} | 'undefined' when Port :: port() | atom(), Monitors :: [{process, pid()}]; + (Port, monitored_by) -> {monitored_by, MonitoredBy} | 'undefined' when + Port :: port() | atom(), + MonitoredBy :: [pid()]; (Port, name) -> {name, Name} | 'undefined' when Port :: port() | atom(), Name :: string(); diff --git a/lib/common_test/doc/src/ct.xml b/lib/common_test/doc/src/ct.xml index 5231ef24a4..264bcff251 100644 --- a/lib/common_test/doc/src/ct.xml +++ b/lib/common_test/doc/src/ct.xml @@ -935,7 +935,7 @@ </func> <func> - <name>reload_config(Required) -> ValueOrElement</name> + <name>reload_config(Required) -> ValueOrElement | {error, Reason}</name> <fsummary>Reloads configuration file containing specified configuration key.</fsummary> <type> diff --git a/lib/common_test/src/ct.erl b/lib/common_test/src/ct.erl index cae7bea406..d7ae81a5ce 100644 --- a/lib/common_test/src/ct.erl +++ b/lib/common_test/src/ct.erl @@ -282,7 +282,7 @@ step(TestDir,Suite,Case,Opts) -> %%% <c>> ct_telnet:cmd(unix_telnet, "ls .").</c><br/> %%% <c>{ok,["ls","file1 ...",...]}</c></p> start_interactive() -> - ct_util:start(interactive), + _ = ct_util:start(interactive), ok. %%%----------------------------------------------------------------- diff --git a/lib/common_test/src/ct_config.erl b/lib/common_test/src/ct_config.erl index b499bc8b05..99de311570 100644 --- a/lib/common_test/src/ct_config.erl +++ b/lib/common_test/src/ct_config.erl @@ -119,7 +119,8 @@ call(Msg) -> end. return({To,Ref},Result) -> - To ! {Ref, Result}. + To ! {Ref, Result}, + ok. loop(StartDir) -> receive @@ -128,11 +129,11 @@ loop(StartDir) -> return(From,Result), loop(StartDir); {{set_default_config,{Config,Scope}},From} -> - set_config(Config,{true,Scope}), + _ = set_config(Config,{true,Scope}), return(From,ok), loop(StartDir); {{set_default_config,{Name,Config,Scope}},From} -> - set_config(Name,Config,{true,Scope}), + _ = set_config(Name,Config,{true,Scope}), return(From,ok), loop(StartDir); {{delete_default_config,Scope},From} -> @@ -149,7 +150,7 @@ loop(StartDir) -> loop(StartDir); {{stop},From} -> ets:delete(?attr_table), - file:set_cwd(StartDir), + ok = file:set_cwd(StartDir), return(From,ok) end. @@ -257,7 +258,7 @@ read_config_files(Opts) -> read_config_files_int([{Callback, File}|Files], FunToSave) -> case Callback:read_config(File) of {ok, Config} -> - FunToSave(Config, Callback, File), + _ = FunToSave(Config, Callback, File), read_config_files_int(Files, FunToSave); {error, {ErrorName, ErrorDetail}} -> {user_error, {ErrorName, File, ErrorDetail}}; @@ -267,6 +268,15 @@ read_config_files_int([{Callback, File}|Files], FunToSave) -> read_config_files_int([], _FunToSave) -> ok. + +read_config_files(ConfigFiles, FunToSave) -> + case read_config_files_int(ConfigFiles, FunToSave) of + {user_error, Error} -> + {error, Error}; + ok -> + ok + end. + store_config(Config, Callback, File) when is_tuple(Config) -> store_config([Config], Callback, File); @@ -455,8 +465,12 @@ reload_conf(KeyOrName) -> undefined; HandlerList -> HandlerList2 = lists:usort(HandlerList), - read_config_files_int(HandlerList2, fun rewrite_config/3), - get_config(KeyOrName) + case read_config_files(HandlerList2, fun rewrite_config/3) of + ok -> + get_config(KeyOrName); + Error -> + Error + end end. release_allocated() -> @@ -490,16 +504,16 @@ associate(Name,_Key,Configs) -> associate_int(Name,Configs,os:getenv("COMMON_TEST_ALIAS_TOP")). associate_int(Name,Configs,"true") -> - lists:map(fun({K,_Config}) -> + lists:foreach(fun({K,_Config}) -> Cs = ets:match_object( ?attr_table, #ct_conf{key=element(1,K), name='_UNDEF',_='_'}), [ets:insert(?attr_table,C#ct_conf{name=Name}) || C <- Cs] - end,Configs); + end,Configs); associate_int(Name,Configs,_) -> - lists:map(fun({K,Config}) -> + lists:foreach(fun({K,Config}) -> Key = if is_tuple(K) -> element(1,K); is_atom(K) -> K end, @@ -511,7 +525,7 @@ associate_int(Name,Configs,_) -> [ets:insert(?attr_table,C#ct_conf{name=Name, value=Config}) || C <- Cs] - end,Configs). + end,Configs). @@ -576,7 +590,7 @@ encrypt_config_file(SrcFileName, EncryptFileName, {file,KeyFile}) -> end; encrypt_config_file(SrcFileName, EncryptFileName, {key,Key}) -> - crypto:start(), + _ = crypto:start(), {Key,IVec} = make_crypto_key(Key), case file:read_file(SrcFileName) of {ok,Bin0} -> @@ -615,7 +629,7 @@ decrypt_config_file(EncryptFileName, TargetFileName, {file,KeyFile}) -> end; decrypt_config_file(EncryptFileName, TargetFileName, {key,Key}) -> - crypto:start(), + _ = crypto:start(), {Key,IVec} = make_crypto_key(Key), case file:read_file(EncryptFileName) of {ok,Bin} -> @@ -778,14 +792,13 @@ prepare_config_list(Args) -> % TODO: add logging of the loaded configuration file to the CT FW log!!! add_config(Callback, []) -> - read_config_files_int([{Callback, []}], fun store_config/3); + read_config_files([{Callback, []}], fun store_config/3); add_config(Callback, [File|_Files]=Config) when is_list(File) -> lists:foreach(fun(CfgStr) -> - read_config_files_int([{Callback, CfgStr}], fun store_config/3) end, + read_config_files([{Callback, CfgStr}], fun store_config/3) end, Config); add_config(Callback, [C|_]=Config) when is_integer(C) -> - read_config_files_int([{Callback, Config}], fun store_config/3), - ok. + read_config_files([{Callback, Config}], fun store_config/3). remove_config(Callback, Config) -> ets:match_delete(?attr_table, diff --git a/lib/common_test/src/ct_framework.erl b/lib/common_test/src/ct_framework.erl index eb32f7f3fc..104515e57e 100644 --- a/lib/common_test/src/ct_framework.erl +++ b/lib/common_test/src/ct_framework.erl @@ -128,7 +128,7 @@ init_tc1(?MODULE,_,error_in_suite,_,[Config0]) when is_list(Config0) -> ct_event:notify(#event{name=tc_start, node=node(), data={?MODULE,error_in_suite}}), - ct_suite_init(?MODULE,error_in_suite,[],Config0), + _ = ct_suite_init(?MODULE,error_in_suite,[],Config0), case ?val(error,Config0) of undefined -> {fail,"unknown_error_in_suite"}; @@ -212,7 +212,7 @@ init_tc2(Mod,Suite,Func,HookFunc,SuiteInfo,MergeResult,Config) -> %% timetrap must be handled before require MergedInfo = timetrap_first(MergeResult, [], []), %% tell logger to use specified style sheet - case lists:keysearch(stylesheet,1,MergeResult++Config) of + _ = case lists:keysearch(stylesheet,1,MergeResult++Config) of {value,{stylesheet,SSFile}} -> ct_logs:set_stylesheet(Func,add_data_dir(SSFile,Config)); _ -> @@ -632,10 +632,10 @@ try_set_default(Name,Key,Info,Where) -> {_,[]} -> no_default; {'_UNDEF',_} -> - [ct_config:set_default_config([CfgVal],Where) || CfgVal <- CfgElems], + _ = [ct_config:set_default_config([CfgVal],Where) || CfgVal <- CfgElems], ok; _ -> - [ct_config:set_default_config(Name,[CfgVal],Where) || CfgVal <- CfgElems], + _ = [ct_config:set_default_config(Name,[CfgVal],Where) || CfgVal <- CfgElems], ok end. @@ -1315,7 +1315,7 @@ report(What,Data) -> %% top level test index page needs to be refreshed TestName = filename:basename(?val(topdir, Data), ".logs"), RunDir = ?val(rundir, Data), - ct_logs:make_all_suites_index({TestName,RunDir}), + _ = ct_logs:make_all_suites_index({TestName,RunDir}), ok; tests_start -> ok; diff --git a/lib/common_test/src/ct_ftp.erl b/lib/common_test/src/ct_ftp.erl index 48914864e4..84e664b387 100644 --- a/lib/common_test/src/ct_ftp.erl +++ b/lib/common_test/src/ct_ftp.erl @@ -292,7 +292,7 @@ init(KeyOrName,{IP,Port},{Username,Password}) -> end. ftp_connect(IP,Port,Username,Password) -> - inets:start(), + _ = inets:start(), case inets:start(ftpc,[{host,IP},{port,Port}]) of {ok,FtpPid} -> case ftp:user(FtpPid,Username,Password) of diff --git a/lib/common_test/src/ct_groups.erl b/lib/common_test/src/ct_groups.erl index dd04c5410a..1375e7dcc7 100644 --- a/lib/common_test/src/ct_groups.erl +++ b/lib/common_test/src/ct_groups.erl @@ -402,12 +402,7 @@ expand(Mod, Name, Defs) -> end. make_all_conf(Dir, Mod, Props, TestSpec) -> - case code:is_loaded(Mod) of - false -> - code:load_abs(filename:join(Dir,atom_to_list(Mod))); - _ -> - ok - end, + _ = load_abs(Dir, Mod), make_all_conf(Mod, Props, TestSpec). make_all_conf(Mod, Props, TestSpec) -> @@ -428,16 +423,19 @@ make_all_conf(Mod, Props, TestSpec) -> end. make_conf(Dir, Mod, Name, Props, TestSpec) -> + _ = load_abs(Dir, Mod), + make_conf(Mod, Name, Props, TestSpec). + +load_abs(Dir, Mod) -> case code:is_loaded(Mod) of false -> code:load_abs(filename:join(Dir,atom_to_list(Mod))); _ -> ok - end, - make_conf(Mod, Name, Props, TestSpec). + end. make_conf(Mod, Name, Props, TestSpec) -> - case code:is_loaded(Mod) of + _ = case code:is_loaded(Mod) of false -> code:load_file(Mod); _ -> diff --git a/lib/common_test/src/ct_hooks.erl b/lib/common_test/src/ct_hooks.erl index 5422d449fd..c9a4abb5ee 100644 --- a/lib/common_test/src/ct_hooks.erl +++ b/lib/common_test/src/ct_hooks.erl @@ -408,7 +408,8 @@ catch_apply(M,F,A, Default) -> maybe_start_locker(Mod,GroupName,Opts) -> case lists:member(parallel,Opts) of true -> - {ok, _Pid} = ct_hooks_lock:start({Mod,GroupName}); + {ok, _Pid} = ct_hooks_lock:start({Mod,GroupName}), + ok; false -> ok end. diff --git a/lib/common_test/src/ct_hooks_lock.erl b/lib/common_test/src/ct_hooks_lock.erl index f41f259f7b..fea298e535 100644 --- a/lib/common_test/src/ct_hooks_lock.erl +++ b/lib/common_test/src/ct_hooks_lock.erl @@ -82,7 +82,7 @@ init(Id) -> %% @doc Handling call messages handle_call({stop,Id}, _From, #state{ id = Id, requests = Reqs } = State) -> - [gen_server:reply(Req, locker_stopped) || {Req,_ReqId} <- Reqs], + _ = [gen_server:reply(Req, locker_stopped) || {Req,_ReqId} <- Reqs], {stop, normal, stopped, State}; handle_call({stop,_Id}, _From, State) -> {reply, stopped, State}; diff --git a/lib/common_test/src/ct_logs.erl b/lib/common_test/src/ct_logs.erl index e6d683c8a9..53245c596a 100644 --- a/lib/common_test/src/ct_logs.erl +++ b/lib/common_test/src/ct_logs.erl @@ -137,7 +137,8 @@ close(Info, StartDir) -> %% so we need to use a local copy of the log cache data LogCacheBin = case make_last_run_index() of - {error,_} -> % log server not responding + {error, Reason} -> % log server not responding + io:format("Warning! ct_logs not responding: ~p~n", [Reason]), undefined; LCB -> LCB @@ -149,7 +150,7 @@ close(Info, StartDir) -> ok; CacheBin -> %% save final version of the log cache to file - file:write_file(?log_cache_name,CacheBin), + _ = file:write_file(?log_cache_name,CacheBin), put(ct_log_cache,undefined) end end, @@ -175,12 +176,12 @@ close(Info, StartDir) -> Error -> io:format("Warning! Cleanup failed: ~p~n", [Error]) end, - make_all_suites_index(stop), + _ = make_all_suites_index(stop), make_all_runs_index(stop), Cache2File(); true -> - file:set_cwd(".."), - make_all_suites_index(stop), + ok = file:set_cwd(".."), + _ = make_all_suites_index(stop), make_all_runs_index(stop), Cache2File(), case ct_util:get_profile_data(browser, StartDir) of @@ -240,7 +241,7 @@ call(Msg) -> Pid -> MRef = erlang:monitor(process,Pid), Ref = make_ref(), - ?MODULE ! {Msg,{self(),Ref}}, + Pid ! {Msg,{self(),Ref}}, receive {Ref, Result} -> erlang:demonitor(MRef, [flush]), @@ -251,16 +252,30 @@ call(Msg) -> end. return({To,Ref},Result) -> - To ! {Ref, Result}. + To ! {Ref, Result}, + ok. cast(Msg) -> case whereis(?MODULE) of undefined -> - {error,does_not_exist}; + io:format("Warning: ct_logs not started~n"), + {_,_,_,_,_,_,Content,_} = Msg, + FormatArgs = get_format_args(Content), + _ = [io:format(Format, Args) || {Format, Args} <- FormatArgs], + ok; _Pid -> - ?MODULE ! Msg + ?MODULE ! Msg, + ok end. +get_format_args(Content) -> + lists:map(fun(C) -> + case C of + {_, FA, _} -> FA; + {_, _} -> C + end + end, Content). + %%%----------------------------------------------------------------- %%% @spec init_tc(RefreshLog) -> ok %%% @@ -631,7 +646,7 @@ logger(Parent, Mode, Verbosity) -> end, %%! <--- - file:make_dir(Dir), + _ = file:make_dir(Dir), AbsDir = ?abs(Dir), put(ct_run_dir, AbsDir), @@ -671,7 +686,7 @@ logger(Parent, Mode, Verbosity) -> end end, - test_server_io:start_link(), + _ = test_server_io:start_link(), MiscIoName = filename:join(Dir, ?misc_io_log), {ok,MiscIoFd} = file:open(MiscIoName, [write,{encoding,utf8}]), @@ -701,13 +716,13 @@ logger(Parent, Mode, Verbosity) -> ct_event:notify(#event{name=start_logging,node=node(), data=AbsDir}), make_all_runs_index(start), - make_all_suites_index(start), + _ = make_all_suites_index(start), case Mode of interactive -> interactive_link(); _ -> ok end, - file:set_cwd(Dir), - make_last_run_index(Time), + ok = file:set_cwd(Dir), + _ = make_last_run_index(Time), CtLogFd = open_ctlog(?misc_io_log), io:format(CtLogFd,int_header()++int_footer(), [log_timestamp(?now),"Common Test Logger started"]), @@ -721,13 +736,13 @@ logger(Parent, Mode, Verbosity) -> GenLvl -> io:format(CtLogFd, "~-25s~3w~n", ["general level",GenLvl]) end, - [begin put({verbosity,Cat},VLvl), - if Cat == '$unspecified' -> + _ = [begin put({verbosity,Cat},VLvl), + if Cat == '$unspecified' -> ok; - true -> + true -> io:format(CtLogFd, "~-25w~3w~n", [Cat,VLvl]) - end - end || {Cat,VLvl} <- Verbosity], + end + end || {Cat,VLvl} <- Verbosity], io:nl(CtLogFd), TcEscChars = case application:get_env(common_test, esc_chars) of {ok,ECBool} -> ECBool; @@ -804,7 +819,7 @@ logger_loop(State) -> print_style(GL, IoFormat, State#logger_state.stylesheet), set_evmgr_gl(GL), TCGLs = add_tc_gl(TCPid,GL,State), - if not RefreshLog -> + _ = if not RefreshLog -> ok; true -> make_last_run_index(State#logger_state.start_time) @@ -831,7 +846,7 @@ logger_loop(State) -> return(From,{ok,filename:basename(State#logger_state.log_dir)}), logger_loop(State); {make_last_run_index,From} -> - make_last_run_index(State#logger_state.start_time), + _ = make_last_run_index(State#logger_state.start_time), return(From,get(ct_log_cache)), logger_loop(State); {set_stylesheet,_,SSFile} when State#logger_state.stylesheet == @@ -1169,7 +1184,7 @@ print_style_error(Fd, IoFormat, StyleSheet, Reason) -> close_ctlog(Fd) -> io:format(Fd, "\n</pre>\n", []), io:format(Fd, [xhtml("<br><br>\n", "<br /><br />\n") | footer()], []), - file:close(Fd). + ok = file:close(Fd). %%%----------------------------------------------------------------- %%% tc_io_format/3 @@ -1773,7 +1788,7 @@ count_cases(Dir) -> %% file yet. {0,0,0,0}; Summary -> - write_summary(SumFile, Summary), + _ = write_summary(SumFile, Summary), Summary end; {error, Reason} -> @@ -2088,7 +2103,7 @@ interactive_link() -> "</body>\n", "</html>\n" ], - file:write_file("last_interactive.html",unicode:characters_to_binary(Body)), + _ = file:write_file("last_interactive.html",unicode:characters_to_binary(Body)), io:format("~n~nUpdated ~ts\n" "Any CT activities will be logged here\n", [?abs("last_interactive.html")]). @@ -2219,9 +2234,9 @@ runentry(Dir, _, _) -> write_totals_file(Name,Label,Logs,Totals) -> AbsName = ?abs(Name), notify_and_lock_file(AbsName), - force_write_file(AbsName, - term_to_binary({atom_to_list(node()), - Label,Logs,Totals})), + _ = force_write_file(AbsName, + term_to_binary({atom_to_list(node()), + Label,Logs,Totals})), notify_and_unlock_file(AbsName). %% this function needs to convert from old formats to new so that old @@ -2266,7 +2281,7 @@ read_totals_file(Name) -> Result. force_write_file(Name,Contents) -> - force_delete(Name), + _ = force_delete(Name), file:write_file(Name,Contents). force_delete(Name) -> @@ -2817,18 +2832,18 @@ get_cache_data({ok,CacheBin}) -> true -> {ok,CacheRec}; false -> - file:delete(?log_cache_name), + _ = file:delete(?log_cache_name), {error,old_cache_file} end; _ -> - file:delete(?log_cache_name), + _ = file:delete(?log_cache_name), {error,invalid_cache_file} end; get_cache_data(NoCache) -> NoCache. cache_vsn() -> - application:load(common_test), + _ = application:load(common_test), case application:get_key(common_test,vsn) of {ok,VSN} -> VSN; diff --git a/lib/common_test/src/ct_master.erl b/lib/common_test/src/ct_master.erl index c4905b316f..4eef27d2a5 100644 --- a/lib/common_test/src/ct_master.erl +++ b/lib/common_test/src/ct_master.erl @@ -376,7 +376,7 @@ init_master(Parent,NodeOptsList,EvHandlers,MasterLogDir,LogDirs, end, %% start master event manager and add default handler - ct_master_event:start_link(), + {ok, _} = start_ct_master_event(), ct_master_event:add_handler(), %% add user handlers for master event manager Add = fun({H,Args}) -> @@ -398,6 +398,14 @@ init_master(Parent,NodeOptsList,EvHandlers,MasterLogDir,LogDirs, end, init_master1(Parent,NodeOptsList,InitOptions,LogDirs). +start_ct_master_event() -> + case ct_master_event:start_link() of + {error, {already_started, Pid}} -> + {ok, Pid}; + Else -> + Else + end. + init_master1(Parent,NodeOptsList,InitOptions,LogDirs) -> {Inaccessible,NodeOptsList1,InitOptions1} = init_nodes(NodeOptsList, InitOptions), @@ -658,7 +666,7 @@ refresh_logs([D|Dirs],Refreshed) -> {ok,Cwd} = file:get_cwd(), case catch ct_run:refresh_logs(D) of {'EXIT',Reason} -> - file:set_cwd(Cwd), + ok = file:set_cwd(Cwd), refresh_logs(Dirs,[{D,{error,Reason}}|Refreshed]); Result -> refresh_logs(Dirs,[{D,Result}|Refreshed]) @@ -701,7 +709,7 @@ init_node_ctrl(MasterPid,Cookie,Opts) -> end, %% start a local event manager - ct_event:start_link(), + {ok, _} = start_ct_event(), ct_event:add_handler([{master,MasterPid}]), %% log("Running test with options: ~p~n", [Opts]), @@ -721,6 +729,14 @@ init_node_ctrl(MasterPid,Cookie,Opts) -> "Can't report result!~n~n", [MasterNode]) end. +start_ct_event() -> + case ct_event:start_link() of + {error, {already_started, Pid}} -> + {ok, Pid}; + Else -> + Else + end. + %%%----------------------------------------------------------------- %%% Event handling %%%----------------------------------------------------------------- @@ -778,7 +794,7 @@ reply(Result,To) -> ok. init_nodes(NodeOptions, InitOptions)-> - ping_nodes(NodeOptions), + _ = ping_nodes(NodeOptions), start_nodes(InitOptions), eval_on_nodes(InitOptions), {Inaccessible, NodeOptions1}=ping_nodes(NodeOptions), diff --git a/lib/common_test/src/ct_master_logs.erl b/lib/common_test/src/ct_master_logs.erl index 39f87a7f09..a2542171f8 100644 --- a/lib/common_test/src/ct_master_logs.erl +++ b/lib/common_test/src/ct_master_logs.erl @@ -91,8 +91,8 @@ init(Parent,LogDir,Nodes) -> Time = calendar:local_time(), RunDir = make_dirname(Time), RunDirAbs = filename:join(LogDir,RunDir), - file:make_dir(RunDirAbs), - write_details_file(RunDirAbs,{node(),Nodes}), + ok = make_dir(RunDirAbs), + _ = write_details_file(RunDirAbs,{node(),Nodes}), case basic_html() of true -> @@ -128,7 +128,7 @@ init(Parent,LogDir,Nodes) -> end end, - make_all_runs_index(LogDir), + _ = make_all_runs_index(LogDir), CtLogFd = open_ct_master_log(RunDirAbs), NodeStr = lists:flatten(lists:map(fun(N) -> @@ -181,7 +181,7 @@ loop(State) -> lists:foreach(Fun,List), loop(State); {make_all_runs_index,From} -> - make_all_runs_index(State#state.logdir), + _ = make_all_runs_index(State#state.logdir), return(From,State#state.logdir), loop(State); {{nodedir,Node,RunDir},From} -> @@ -189,12 +189,12 @@ loop(State) -> return(From,ok), loop(State); stop -> - make_all_runs_index(State#state.logdir), + _ = make_all_runs_index(State#state.logdir), io:format(State#state.log_fd, int_header()++int_footer(), [log_timestamp(?now),"Finished!"]), - close_ct_master_log(State#state.log_fd), - close_nodedir_index(State#state.nodedir_ix_fd), + _ = close_ct_master_log(State#state.log_fd), + _ = close_nodedir_index(State#state.nodedir_ix_fd), ok end. @@ -496,7 +496,7 @@ make_relative(Dir) -> ct_logs:make_relative(Dir). force_write_file(Name,Contents) -> - force_delete(Name), + _ = force_delete(Name), file:write_file(Name,Contents). force_delete(Name) -> @@ -534,13 +534,34 @@ call(Msg) -> end. return({To,Ref},Result) -> - To ! {Ref, Result}. + To ! {Ref, Result}, + ok. cast(Msg) -> case whereis(?MODULE) of undefined -> - {error,does_not_exist}; + io:format("Warning: ct_master_logs not started~n"), + {_,_,Content} = Msg, + FormatArgs = get_format_args(Content), + _ = [io:format(Format, Args) || {Format, Args} <- FormatArgs], + ok; _Pid -> - ?MODULE ! Msg + ?MODULE ! Msg, + ok end. +get_format_args(Content) -> + lists:map(fun(C) -> + case C of + {_, FA, _} -> FA; + _ -> C + end + end, Content). + +make_dir(Dir) -> + case file:make_dir(Dir) of + {error, exist} -> + ok; + Else -> + Else + end. diff --git a/lib/common_test/src/ct_property_test.erl b/lib/common_test/src/ct_property_test.erl index 7dc78e949a..12c3d726d3 100644 --- a/lib/common_test/src/ct_property_test.erl +++ b/lib/common_test/src/ct_property_test.erl @@ -161,7 +161,9 @@ property_tests_path(Dir, Config) -> add_code_pathz(Dir) -> case lists:member(Dir, code:get_path()) of true -> ok; - false -> code:add_pathz(Dir) + false -> + true = code:add_pathz(Dir), + ok end. compile_tests(Path, ToolModule) -> @@ -171,10 +173,10 @@ compile_tests(Path, ToolModule) -> {ok,FileNames} = file:list_dir("."), BeamFiles = [F || F<-FileNames, filename:extension(F) == ".beam"], - [file:delete(F) || F<-BeamFiles], + _ = [file:delete(F) || F<-BeamFiles], ct:pal("Compiling in ~p:~n Deleted ~p~n MacroDefs=~p",[Path,BeamFiles,MacroDefs]), Result = make:all([load|MacroDefs]), - file:set_cwd(Cwd), + ok = file:set_cwd(Cwd), Result. diff --git a/lib/common_test/src/ct_release_test.erl b/lib/common_test/src/ct_release_test.erl index 4e0f88cf5f..d783f8d04e 100644 --- a/lib/common_test/src/ct_release_test.erl +++ b/lib/common_test/src/ct_release_test.erl @@ -342,7 +342,7 @@ cleanup(Config) -> end end, AllNodes), - [rpc:call(Node,erlang,halt,[]) || Node <- Nodes], + _ = [rpc:call(Node,erlang,halt,[]) || Node <- Nodes], Config. %%----------------------------------------------------------------- @@ -552,14 +552,14 @@ target_system(Apps,CreateDir,InstallDir,{FromVsn,_,AllAppsVsns,Path}) -> %% Add bin and log dirs BinDir = filename:join([InstallDir, "bin"]), - file:make_dir(BinDir), - file:make_dir(filename:join(InstallDir,"log")), + ok = make_dir(BinDir), + ok = make_dir(filename:join(InstallDir,"log")), %% Delete start scripts - they will be added later ErtsBinDir = filename:join([InstallDir, "erts-" ++ ErtsVsn, "bin"]), - file:delete(filename:join([ErtsBinDir, "erl"])), - file:delete(filename:join([ErtsBinDir, "start"])), - file:delete(filename:join([ErtsBinDir, "start_erl"])), + ok = delete_file(filename:join([ErtsBinDir, "erl"])), + ok = delete_file(filename:join([ErtsBinDir, "start"])), + ok = delete_file(filename:join([ErtsBinDir, "start_erl"])), %% Copy .boot to bin/start.boot copy_file(RelName++".boot",filename:join([BinDir, "start.boot"])), @@ -680,7 +680,7 @@ do_upgrade({Cb,InitState},FromVsn,FromAppsVsns,ToRel,ToAppsVsns,InstallDir) -> %% even if install_release returned {ok,...} there might be an %% emulator restart (instruction restart_emulator), so we must %% always make sure the node is running. - wait_node_up(current,ToVsn,ToAppsVsns), + {ok, _} = wait_node_up(current,ToVsn,ToAppsVsns), [{"OTP upgrade test",ToVsn,_,current}, {"OTP upgrade test",FromVsn,_,permanent}] = @@ -703,7 +703,7 @@ do_upgrade({Cb,InitState},FromVsn,FromAppsVsns,ToRel,ToAppsVsns,InstallDir) -> %% even if install_release returned {ok,...} there might be an %% emulator restart (instruction restart_emulator), so we must %% always make sure the node is running. - wait_node_up(current,FromVsn,FromAppsVsns), + {ok, _} = wait_node_up(current,FromVsn,FromAppsVsns), [{"OTP upgrade test",ToVsn,_,permanent}, {"OTP upgrade test",FromVsn,_,current}] = @@ -854,7 +854,7 @@ copy_file(Src, Dest, Opts) -> case lists:member(preserve, Opts) of true -> {ok, FileInfo} = file:read_file_info(Src), - file:write_file_info(Dest, FileInfo); + ok = file:write_file_info(Dest, FileInfo); false -> ok end. @@ -862,8 +862,8 @@ copy_file(Src, Dest, Opts) -> write_file(FName, Conts) -> Enc = file:native_name_encoding(), {ok, Fd} = file:open(FName, [write]), - file:write(Fd, unicode:characters_to_binary(Conts,Enc,Enc)), - file:close(Fd). + ok = file:write(Fd, unicode:characters_to_binary(Conts,Enc,Enc)), + ok = file:close(Fd). %% Substitute all occurrences of %Var% for Val in the given scripts subst_src_scripts(Scripts, SrcDir, DestDir, Vars, Opts) -> @@ -944,3 +944,19 @@ rm_rf(Dir) -> _ -> ok end. + +delete_file(FileName) -> + case file:delete(FileName) of + {error, enoent} -> + ok; + Else -> + Else + end. + +make_dir(Dir) -> + case file:make_dir(Dir) of + {error, eexist} -> + ok; + Else -> + Else + end. diff --git a/lib/common_test/src/ct_repeat.erl b/lib/common_test/src/ct_repeat.erl index 31c5755c7e..dac596a135 100644 --- a/lib/common_test/src/ct_repeat.erl +++ b/lib/common_test/src/ct_repeat.erl @@ -44,13 +44,13 @@ loop_test(If,Args) when is_list(Args) -> false; E = {error,_} -> io:format("Common Test error: ~p\n\n",[E]), - file:set_cwd(Cwd), + ok = file:set_cwd(Cwd), E; {repeat,N} -> io:format("\nCommon Test: Will repeat tests ~w times.\n\n",[N]), Args1 = [{loop_info,[{repeat,1,N}]} | Args], Result = loop(If,repeat,0,N,undefined,Args1,undefined,[]), - file:set_cwd(Cwd), + ok = file:set_cwd(Cwd), Result; {stop_time,StopTime} -> Result = @@ -76,7 +76,7 @@ loop_test(If,Args) when is_list(Args) -> Args1 = [{loop_info,[{stop_time,Secs,StopTime,1}]} | Args], loop(If,stop_time,0,Secs,StopTime,Args1,TPid,[]) end, - file:set_cwd(Cwd), + ok = file:set_cwd(Cwd), Result end. diff --git a/lib/common_test/src/ct_rpc.erl b/lib/common_test/src/ct_rpc.erl index cbcc212bb8..b4a0bc9d74 100644 --- a/lib/common_test/src/ct_rpc.erl +++ b/lib/common_test/src/ct_rpc.erl @@ -81,7 +81,7 @@ app_node(App, [], _, _) -> app_node(App, _Candidates = [CandidateNode | Nodes], FailOnBadRPC, Cookie) -> Cookie0 = set_the_cookie(Cookie), Result = rpc:call(CandidateNode, application, which_applications, []), - set_the_cookie(Cookie0), + _ = set_the_cookie(Cookie0), case Result of {badrpc,Reason} when FailOnBadRPC == true -> ct:fail({Reason,CandidateNode}); @@ -145,7 +145,7 @@ call({Fun, FunArgs}, Module, Function, Args, TimeOut, Cookie) -> call(Node, Module, Function, Args, TimeOut, Cookie) when is_atom(Node) -> Cookie0 = set_the_cookie(Cookie), Result = rpc:call(Node, Module, Function, Args, TimeOut), - set_the_cookie(Cookie0), + _ = set_the_cookie(Cookie0), Result. %%% @spec cast(Node, Module, Function, Args) -> ok @@ -190,7 +190,7 @@ cast({Fun, FunArgs}, Module, Function, Args, Cookie) -> cast(Node, Module, Function, Args, Cookie) when is_atom(Node) -> Cookie0 = set_the_cookie(Cookie), true = rpc:cast(Node, Module, Function, Args), - set_the_cookie(Cookie0), + _ = set_the_cookie(Cookie0), ok. diff --git a/lib/common_test/src/ct_run.erl b/lib/common_test/src/ct_run.erl index 1e5f935198..fbb9c7ab60 100644 --- a/lib/common_test/src/ct_run.erl +++ b/lib/common_test/src/ct_run.erl @@ -400,21 +400,21 @@ run_or_refresh(Opts = #opts{logdir = LogDir}, Args) -> [RefreshDir] -> ?abs(RefreshDir) end, {ok,Cwd} = file:get_cwd(), - file:set_cwd(LogDir1), + ok = file:set_cwd(LogDir1), %% give the shell time to print version etc timer:sleep(500), io:nl(), case catch ct_logs:make_all_runs_index(refresh) of {'EXIT',ARReason} -> - file:set_cwd(Cwd), + ok = file:set_cwd(Cwd), {error,{all_runs_index,ARReason}}; _ -> case catch ct_logs:make_all_suites_index(refresh) of {'EXIT',ASReason} -> - file:set_cwd(Cwd), + ok = file:set_cwd(Cwd), {error,{all_suites_index,ASReason}}; _ -> - file:set_cwd(Cwd), + ok = file:set_cwd(Cwd), io:format("Logs in ~ts refreshed!~n~n", [LogDir1]), timer:sleep(500), % time to flush io before quitting @@ -756,8 +756,8 @@ script_start4(#opts{label = Label, profile = Profile, {ct_hooks, CTHooks}, {enable_builtin_hooks,EnableBuiltinHooks}]) of ok -> - ct_util:start(interactive, LogDir, - add_verbosity_defaults(Verbosity)), + _ = ct_util:start(interactive, LogDir, + add_verbosity_defaults(Verbosity)), ct_util:set_testdata({logopts, LogOpts}), log_ts_names(Specs), io:nl(), @@ -901,9 +901,8 @@ install(Opts, LogDir) -> VarFile = variables_file_name(LogDir), case file:open(VarFile, [write]) of {ok,Fd} -> - [io:format(Fd, "~p.\n", [Opt]) || Opt <- ConfOpts ], - file:close(Fd), - ok; + _ = [io:format(Fd, "~p.\n", [Opt]) || Opt <- ConfOpts], + ok = file:close(Fd); {error,Reason} -> io:format("CT failed to install configuration data. Please " "verify that the log directory exists and that " @@ -960,7 +959,7 @@ run_test1(StartOpts) when is_list(StartOpts) -> false -> case catch run_test2(StartOpts) of {'EXIT',Reason} -> - file:set_cwd(Cwd), + ok = file:set_cwd(Cwd), {error,Reason}; Result -> Result @@ -971,7 +970,7 @@ run_test1(StartOpts) when is_list(StartOpts) -> stop_trace(Tracing), exit(Res); RefreshDir -> - refresh_logs(?abs(RefreshDir)), + ok = refresh_logs(?abs(RefreshDir)), exit(done) end. @@ -1209,7 +1208,6 @@ run_all_specs([], _, _, TotResult) -> end; run_all_specs([{Specs,TS} | TSs], Opts, StartOpts, TotResult) -> - log_ts_names(Specs), Combined = #opts{config = TSConfig} = combine_test_opts(TS, Specs, Opts), AllConfig = merge_vals([Opts#opts.config, TSConfig]), try run_one_spec(TS, @@ -1430,7 +1428,7 @@ run_testspec1(TestSpec) -> io:format("~nCommon Test starting (cwd is ~ts)~n~n", [Cwd]), case catch run_testspec2(TestSpec) of {'EXIT',Reason} -> - file:set_cwd(Cwd), + ok = file:set_cwd(Cwd), exit({error,Reason}); Result -> exit(Result) @@ -1562,15 +1560,15 @@ refresh_logs(LogDir) -> _ -> case catch ct_logs:make_all_suites_index(refresh) of {'EXIT',ASReason} -> - file:set_cwd(Cwd), + ok = file:set_cwd(Cwd), {error,{all_suites_index,ASReason}}; _ -> case catch ct_logs:make_all_runs_index(refresh) of {'EXIT',ARReason} -> - file:set_cwd(Cwd), + ok = file:set_cwd(Cwd), {error,{all_runs_index,ARReason}}; _ -> - file:set_cwd(Cwd), + ok = file:set_cwd(Cwd), io:format("Logs in ~ts refreshed!~n",[LogDir]), ok end @@ -1610,22 +1608,34 @@ delistify(E) -> E. %%% @hidden %%% @equiv ct:run/3 run(TestDir, Suite, Cases) -> - install([]), - reformat_result(catch do_run(tests(TestDir, Suite, Cases), [])). + case install([]) of + ok -> + reformat_result(catch do_run(tests(TestDir, Suite, Cases), [])); + Error -> + Error + end. %%%----------------------------------------------------------------- %%% @hidden %%% @equiv ct:run/2 run(TestDir, Suite) when is_list(TestDir), is_integer(hd(TestDir)) -> - install([]), - reformat_result(catch do_run(tests(TestDir, Suite), [])). + case install([]) of + ok -> + reformat_result(catch do_run(tests(TestDir, Suite), [])); + Error -> + Error + end. %%%----------------------------------------------------------------- %%% @hidden %%% @equiv ct:run/1 run(TestDirs) -> - install([]), - reformat_result(catch do_run(tests(TestDirs), [])). + case install([]) of + ok -> + reformat_result(catch do_run(tests(TestDirs), [])); + Error -> + Error + end. reformat_result({'EXIT',{user_error,Reason}}) -> {error,Reason}; @@ -2017,7 +2027,7 @@ save_make_errors(Errors) -> "Error compiling or locating the " "following suites: ~n~p",[Suites]), %% save the info for logger - file:write_file(?missing_suites_info,term_to_binary(Errors)), + ok = file:write_file(?missing_suites_info,term_to_binary(Errors)), Errors. get_bad_suites([{{_TestDir,_Suite},Failed}|Errors], BadSuites) -> @@ -2234,7 +2244,7 @@ do_run_test(Tests, Skip, Opts0) -> end, application:set_env(test_server, esc_chars, EscChars), - test_server_ctrl:start_link(local), + {ok, _} = test_server_ctrl:start_link(local), %% let test_server expand the test tuples and count no of cases {Suites,NoOfCases} = count_test_cases(Tests, Skip), @@ -2295,7 +2305,7 @@ do_run_test(Tests, Skip, Opts0) -> lists:foreach(fun(Suite) -> maybe_cleanup_interpret(Suite, Opts#opts.step) end, CleanUp), - [code:del_path(Dir) || Dir <- AddedToPath], + _ = [code:del_path(Dir) || Dir <- AddedToPath], %% If a severe error has occurred in the test_server, %% we will generate an exception here. @@ -2422,7 +2432,7 @@ count_test_cases(Tests, Skip) -> SendResult = fun(Me, Result) -> Me ! {no_of_cases,Result} end, TSPid = test_server_ctrl:start_get_totals(SendResult), Ref = erlang:monitor(process, TSPid), - add_jobs(Tests, Skip, #opts{}, []), + _ = add_jobs(Tests, Skip, #opts{}, []), Counted = (catch count_test_cases1(length(Tests), 0, [], Ref)), erlang:demonitor(Ref, [flush]), case Counted of @@ -2776,14 +2786,14 @@ maybe_interpret1(Suite, Cases, StepOpts) when is_list(Cases) -> maybe_interpret2(Suite, Cases, StepOpts) -> set_break_on_config(Suite, StepOpts), - [begin try i:ib(Suite, Case, 1) of + _ = [begin try i:ib(Suite, Case, 1) of _ -> ok catch _:_Error -> io:format(user, "Invalid breakpoint: ~w:~w/1~n", [Suite,Case]) end - end || Case <- Cases, is_atom(Case)], + end || Case <- Cases, is_atom(Case)], test_server_ctrl:multiply_timetraps(infinity), WinOp = case lists:member(keep_inactive, ensure_atom(StepOpts)) of true -> no_kill; @@ -2802,12 +2812,12 @@ set_break_on_config(Suite, StepOpts) -> false -> ok end end, - SetBPIfExists(init_per_suite, 1), - SetBPIfExists(init_per_group, 2), - SetBPIfExists(init_per_testcase, 2), - SetBPIfExists(end_per_testcase, 2), - SetBPIfExists(end_per_group, 2), - SetBPIfExists(end_per_suite, 1); + ok = SetBPIfExists(init_per_suite, 1), + ok = SetBPIfExists(init_per_group, 2), + ok = SetBPIfExists(init_per_testcase, 2), + ok = SetBPIfExists(end_per_testcase, 2), + ok = SetBPIfExists(end_per_group, 2), + ok = SetBPIfExists(end_per_suite, 1); false -> ok end. @@ -2984,31 +2994,31 @@ add_verbosity_defaults(VLvls) -> %% relative dirs "post run_test erl_args" is not kept! rel_to_abs(CtArgs) -> {PA,PZ} = get_pa_pz(CtArgs, [], []), - [begin + _ = [begin Dir = rm_trailing_slash(D), Abs = make_abs(Dir), - if Dir /= Abs -> - code:del_path(Dir), - code:del_path(Abs), + _ = if Dir /= Abs -> + _ = code:del_path(Dir), + _ = code:del_path(Abs), io:format(user, "Converting ~p to ~p and re-inserting " "with add_pathz/1~n", [Dir, Abs]); true -> - code:del_path(Dir) + _ = code:del_path(Dir) end, code:add_pathz(Abs) end || D <- PZ], - [begin + _ = [begin Dir = rm_trailing_slash(D), Abs = make_abs(Dir), - if Dir /= Abs -> - code:del_path(Dir), - code:del_path(Abs), + _ = if Dir /= Abs -> + _ = code:del_path(Dir), + _ = code:del_path(Abs), io:format(user, "Converting ~p to ~p and re-inserting " "with add_patha/1~n", [Dir, Abs]); true -> - code:del_path(Dir) + _ = code:del_path(Dir) end, code:add_patha(Abs) end || D <- PA], diff --git a/lib/common_test/src/ct_slave.erl b/lib/common_test/src/ct_slave.erl index 3ad3937548..571958ca03 100644 --- a/lib/common_test/src/ct_slave.erl +++ b/lib/common_test/src/ct_slave.erl @@ -325,7 +325,7 @@ do_start(Host, Node, Options) -> Functions end, MasterHost = gethostname(), - if + _ = if MasterHost == Host -> spawn_local_node(Node, Options); true-> @@ -359,7 +359,7 @@ do_start(Host, Node, Options) -> pang-> {error, boot_timeout, ENode} end, - case Result of + _ = case Result of {ok, ENode}-> ok; {error, Timeout, ENode} @@ -422,7 +422,7 @@ spawn_remote_node(Host, Node, Options) -> {_, _}-> [{user, Username}, {password, Password}] end ++ [{silently_accept_hosts, true}] ++ SSHOpts, - application:ensure_all_started(ssh), + {ok, _} = application:ensure_all_started(ssh), {ok, SSHConnRef} = ssh:connect(atom_to_list(Host), SSHPort, SSHOptions), {ok, SSHChannelId} = ssh_connection:session_channel(SSHConnRef, infinity), ssh_setenv(SSHConnRef, SSHChannelId, Env), diff --git a/lib/common_test/src/ct_snmp.erl b/lib/common_test/src/ct_snmp.erl index bb0167eb22..2c59b19196 100644 --- a/lib/common_test/src/ct_snmp.erl +++ b/lib/common_test/src/ct_snmp.erl @@ -221,9 +221,17 @@ start(Config, MgrAgentConfName, SnmpAppConfName) -> Config, SysName, AgentManagerIP, IP), setup_manager(StartManager, MgrAgentConfName, SnmpAppConfName, Config, AgentManagerIP), - application:start(snmp), + ok = start_application(snmp), manager_register(StartManager, MgrAgentConfName). + +start_application(App) -> + case application:start(App) of + {error, {already_started, App}} -> + ok; + Else -> + Else + end. %%% @spec stop(Config) -> ok %%% Config = [{Key, Value}] @@ -233,8 +241,8 @@ start(Config, MgrAgentConfName, SnmpAppConfName) -> %%% @doc Stops the snmp manager and/or agent removes all files created. stop(Config) -> PrivDir = ?config(priv_dir, Config), - application:stop(snmp), - application:stop(mnesia), + ok = application:stop(snmp), + ok = application:stop(mnesia), MgrDir = filename:join(PrivDir,"mgr"), ConfDir = filename:join(PrivDir, "conf"), DbDir = filename:join(PrivDir,"db"), @@ -311,7 +319,7 @@ set_info(Config) -> SetLogFile = filename:join(PrivDir, ?CT_SNMP_LOG_FILE), case file:consult(SetLogFile) of {ok, SetInfo} -> - file:delete(SetLogFile), + ok = delete_file(SetLogFile), lists:reverse(SetInfo); _ -> [] @@ -513,7 +521,7 @@ unload_mibs(Mibs) -> prepare_snmp_env() -> %% To make sure application:set_env is not overwritten by any %% app-file settings. - application:load(snmp), + _ = application:load(snmp), %% Fix for older versions of snmp where there are some %% inappropriate default values for alway starting an @@ -533,7 +541,7 @@ setup_manager(true, MgrConfName, SnmpConfName, Config, IP) -> Users = [], Agents = [], Usms = [], - file:make_dir(MgrDir), + ok = make_dir(MgrDir), snmp_config:write_manager_snmp_files(MgrDir, IP, Port, MaxMsgSize, EngineID, Users, Agents, Usms), @@ -549,7 +557,7 @@ setup_agent(false,_, _, _, _, _, _) -> ok; setup_agent(true, AgentConfName, SnmpConfName, Config, SysName, ManagerIP, AgentIP) -> - application:start(mnesia), + ok = start_application(mnesia), PrivDir = ?config(priv_dir, Config), Vsns = ct:get_config({AgentConfName, agent_vsns}, ?CONF_FILE_VER), TrapUdp = ct:get_config({AgentConfName, agent_trap_udp}, ?TRAP_UDP), @@ -565,8 +573,8 @@ setup_agent(true, AgentConfName, SnmpConfName, ConfDir = filename:join(PrivDir, "conf"), DbDir = filename:join(PrivDir,"db"), - file:make_dir(ConfDir), - file:make_dir(DbDir), + ok = make_dir(ConfDir), + ok = make_dir(DbDir), snmp_config:write_agent_snmp_files(ConfDir, Vsns, ManagerIP, TrapUdp, AgentIP, AgentUdp, SysName, NotifType, SecType, Passwd, @@ -684,7 +692,7 @@ log(PrivDir, Agent, {_, _, Varbinds}, NewVarsAndVals) -> File = filename:join(PrivDir, ?CT_SNMP_LOG_FILE), {ok, Fd} = file:open(File, [write, append]), io:format(Fd, "~p.~n", [{Agent, OldVarsAndVals, NewVarsAndVals}]), - file:close(Fd), + ok = file:close(Fd), ok. %%%--------------------------------------------------------------------------- del_dir(Dir) -> @@ -692,7 +700,7 @@ del_dir(Dir) -> FullPathFiles = lists:map(fun(File) -> filename:join(Dir, File) end, Files), lists:foreach(fun file:delete/1, FullPathFiles), - file:del_dir(Dir), + ok = delete_dir(Dir), ok. %%%--------------------------------------------------------------------------- agent_conf(Agent, MgrAgentConfName) -> @@ -738,8 +746,8 @@ override_contexts(Config, {data_dir_file, File}) -> override_contexts(Config, Contexts) -> Dir = filename:join(?config(priv_dir, Config),"conf"), File = filename:join(Dir,"context.conf"), - file:delete(File), - snmp_config:write_agent_context_config(Dir, "", Contexts). + ok = delete_file(File), + ok = snmp_config:write_agent_context_config(Dir, "", Contexts). %%%--------------------------------------------------------------------------- override_sysinfo(_, undefined) -> @@ -754,8 +762,8 @@ override_sysinfo(Config, {data_dir_file, File}) -> override_sysinfo(Config, SysInfo) -> Dir = filename:join(?config(priv_dir, Config),"conf"), File = filename:join(Dir,"standard.conf"), - file:delete(File), - snmp_config:write_agent_standard_config(Dir, "", SysInfo). + ok = delete_file(File), + ok = snmp_config:write_agent_standard_config(Dir, "", SysInfo). %%%--------------------------------------------------------------------------- override_target_address(_, undefined) -> @@ -769,8 +777,8 @@ override_target_address(Config, {data_dir_file, File}) -> override_target_address(Config, TargetAddressConf) -> Dir = filename:join(?config(priv_dir, Config),"conf"), File = filename:join(Dir,"target_addr.conf"), - file:delete(File), - snmp_config:write_agent_target_addr_config(Dir, "", TargetAddressConf). + ok = delete_file(File), + ok = snmp_config:write_agent_target_addr_config(Dir, "", TargetAddressConf). %%%--------------------------------------------------------------------------- @@ -785,8 +793,8 @@ override_target_params(Config, {data_dir_file, File}) -> override_target_params(Config, TargetParamsConf) -> Dir = filename:join(?config(priv_dir, Config),"conf"), File = filename:join(Dir,"target_params.conf"), - file:delete(File), - snmp_config:write_agent_target_params_config(Dir, "", TargetParamsConf). + ok = delete_file(File), + ok = snmp_config:write_agent_target_params_config(Dir, "", TargetParamsConf). %%%--------------------------------------------------------------------------- override_notify(_, undefined) -> @@ -800,8 +808,8 @@ override_notify(Config, {data_dir_file, File}) -> override_notify(Config, NotifyConf) -> Dir = filename:join(?config(priv_dir, Config),"conf"), File = filename:join(Dir,"notify.conf"), - file:delete(File), - snmp_config:write_agent_notify_config(Dir, "", NotifyConf). + ok = delete_file(File), + ok = snmp_config:write_agent_notify_config(Dir, "", NotifyConf). %%%--------------------------------------------------------------------------- override_usm(_, undefined) -> @@ -815,8 +823,8 @@ override_usm(Config, {data_dir_file, File}) -> override_usm(Config, UsmConf) -> Dir = filename:join(?config(priv_dir, Config),"conf"), File = filename:join(Dir,"usm.conf"), - file:delete(File), - snmp_config:write_agent_usm_config(Dir, "", UsmConf). + ok = delete_file(File), + ok = snmp_config:write_agent_usm_config(Dir, "", UsmConf). %%%-------------------------------------------------------------------------- override_community(_, undefined) -> @@ -830,8 +838,8 @@ override_community(Config, {data_dir_file, File}) -> override_community(Config, CommunityConf) -> Dir = filename:join(?config(priv_dir, Config),"conf"), File = filename:join(Dir,"community.conf"), - file:delete(File), - snmp_config:write_agent_community_config(Dir, "", CommunityConf). + ok = delete_file(File), + ok = snmp_config:write_agent_community_config(Dir, "", CommunityConf). %%%--------------------------------------------------------------------------- @@ -846,8 +854,8 @@ override_vacm(Config, {data_dir_file, File}) -> override_vacm(Config, VacmConf) -> Dir = filename:join(?config(priv_dir, Config),"conf"), File = filename:join(Dir,"vacm.conf"), - file:delete(File), - snmp_config:write_agent_vacm_config(Dir, "", VacmConf). + ok = delete_file(File), + ok = snmp_config:write_agent_vacm_config(Dir, "", VacmConf). %%%--------------------------------------------------------------------------- @@ -861,3 +869,21 @@ while_ok(Fun,[H|T]) -> end; while_ok(_Fun,[]) -> ok. + +delete_file(FileName) -> + case file:delete(FileName) of + {error, enoent} -> ok; + Else -> Else + end. + +make_dir(Dir) -> + case file:make_dir(Dir) of + {error, eexist} -> ok; + Else -> Else + end. + +delete_dir(Dir) -> + case file:del_dir(Dir) of + {error, enoent} -> ok; + Else -> Else + end. diff --git a/lib/common_test/src/ct_ssh.erl b/lib/common_test/src/ct_ssh.erl index 92d912052e..6ab3bf036c 100644 --- a/lib/common_test/src/ct_ssh.erl +++ b/lib/common_test/src/ct_ssh.erl @@ -962,8 +962,8 @@ init(KeyOrName, {ConnType,Addr,Port}, AllOpts) -> end, [], AllOpts1), FinalOptions = [{silently_accept_hosts,true}, {user_interaction,false} | Options], - crypto:start(), - ssh:start(), + _ = crypto:start(), + _ = ssh:start(), Result = case ConnType of ssh -> ssh:connect(Addr, Port, FinalOptions); diff --git a/lib/common_test/src/ct_telnet.erl b/lib/common_test/src/ct_telnet.erl index f5f4f648f4..8fb411ec4f 100644 --- a/lib/common_test/src/ct_telnet.erl +++ b/lib/common_test/src/ct_telnet.erl @@ -605,7 +605,7 @@ init(Name,{Ip,Port,Type},{TargetMod,KeepAlive,Extra}) -> set_telnet_defaults(Settings,#state{}) end, %% Handle old user versions of TargetMod - code:ensure_loaded(TargetMod), + _ = code:ensure_loaded(TargetMod), try case erlang:function_exported(TargetMod,connect,7) of true -> @@ -688,7 +688,7 @@ handle_msg({cmd,Cmd,Opts},State) -> debug_cont_gen_log("Throwing Buffer:",[]), debug_log_lines(State#state.buffer), - case {State#state.type,State#state.prompt} of + _ = case {State#state.type,State#state.prompt} of {ts,_} -> silent_teln_expect(State#state.name, State#state.teln_pid, @@ -735,7 +735,7 @@ handle_msg({send,Cmd,Opts},State) -> debug_cont_gen_log("Throwing Buffer:",[]), debug_log_lines(State#state.buffer), - case {State#state.type,State#state.prompt} of + _ = case {State#state.type,State#state.prompt} of {ts,_} -> silent_teln_expect(State#state.name, State#state.teln_pid, diff --git a/lib/common_test/src/ct_telnet_client.erl b/lib/common_test/src/ct_telnet_client.erl index 1f1311776f..5df7e279ac 100644 --- a/lib/common_test/src/ct_telnet_client.erl +++ b/lib/common_test/src/ct_telnet_client.erl @@ -272,7 +272,7 @@ send(Data, Sock, ConnName) -> _:_ -> ok end end, - gen_tcp:send(Sock, Data), + ok = gen_tcp:send(Sock, Data), ok. %% [IAC,IAC] = buffer data value 255 @@ -284,7 +284,7 @@ check_msg(Sock, [?IAC | Cs], Acc) -> case get_cmd(Cs) of {Cmd,Cs1} -> cmd_dbg("Got",Cmd), - respond_cmd(Cmd, Sock), + ok = respond_cmd(Cmd, Sock), check_msg(Sock, Cs1, Acc); error -> Acc diff --git a/lib/common_test/src/ct_util.erl b/lib/common_test/src/ct_util.erl index d5a8e3fbc0..82a8743cf0 100644 --- a/lib/common_test/src/ct_util.erl +++ b/lib/common_test/src/ct_util.erl @@ -131,14 +131,14 @@ do_start(Parent, Mode, LogDir, Verbosity) -> create_table(?suite_table,#suite_data.key), create_table(?verbosity_table,1), - [ets:insert(?verbosity_table,{Cat,Lvl}) || {Cat,Lvl} <- Verbosity], + _ = [ets:insert(?verbosity_table,{Cat,Lvl}) || {Cat,Lvl} <- Verbosity], {ok,StartDir} = file:get_cwd(), case file:set_cwd(LogDir) of ok -> ok; E -> exit(E) end, - DoExit = fun(Reason) -> file:set_cwd(StartDir), exit(Reason) end, + DoExit = fun(Reason) -> ok = file:set_cwd(StartDir), exit(Reason) end, Opts = case read_opts() of {ok,Opts1} -> Opts1; @@ -169,7 +169,7 @@ do_start(Parent, Mode, LogDir, Verbosity) -> end, %% add user event handlers - case lists:keysearch(event_handler,1,Opts) of + _ = case lists:keysearch(event_handler,1,Opts) of {value,{_,Handlers}} -> Add = fun({H,Args}) -> case catch gen_event:add_handler(?CT_EVMGR_REF,H,Args) of @@ -195,7 +195,7 @@ do_start(Parent, Mode, LogDir, Verbosity) -> data={StartTime, lists:flatten(TestLogDir)}}), %% Initialize ct_hooks - try ct_hooks:init(Opts) of + _ = try ct_hooks:init(Opts) of ok -> Parent ! {self(),started}; {fail,CTHReason} -> @@ -228,7 +228,8 @@ create_table(TableName,KeyPos) -> create_table(TableName,set,KeyPos). create_table(TableName,Type,KeyPos) -> catch ets:delete(TableName), - ets:new(TableName,[Type,named_table,public,{keypos,KeyPos}]). + _ = ets:new(TableName,[Type,named_table,public,{keypos,KeyPos}]), + ok. read_opts() -> case file:consult(ct_run:variables_file_name("./")) of @@ -473,7 +474,7 @@ loop(Mode,TestData,StartDir) -> ct_logs:close(Info, StartDir), ct_event:stop(), ct_config:stop(), - file:set_cwd(StartDir), + ok = file:set_cwd(StartDir), return(From, Info); {Ref, _Msg} when is_reference(Ref) -> %% This clause is used when doing cast operations. @@ -505,7 +506,7 @@ loop(Mode,TestData,StartDir) -> %% Let process crash in case of error, this shouldn't happen! io:format("\n\nct_util_server got EXIT " "from ~w: ~p\n\n", [Pid,Reason]), - file:set_cwd(StartDir), + ok = file:set_cwd(StartDir), exit(Reason) end end. @@ -1035,10 +1036,12 @@ call(Msg, Timeout) -> end. return({To,Ref},Result) -> - To ! {Ref, Result}. + To ! {Ref, Result}, + ok. cast(Msg) -> - ct_util_server ! {Msg, {ct_util_server, make_ref()}}. + ct_util_server ! {Msg, {ct_util_server, make_ref()}}, + ok. seconds(T) -> test_server:seconds(T). @@ -1074,7 +1077,7 @@ abs_name2([],Acc) -> open_url(iexplore, Args, URL) -> {ok,R} = win32reg:open([read]), ok = win32reg:change_key(R,"applications\\iexplore.exe\\shell\\open\\command"), - case win32reg:values(R) of + _ = case win32reg:values(R) of {ok, Paths} -> Path = proplists:get_value(default, Paths), [Cmd | _] = string:tokens(Path, "%"), diff --git a/lib/common_test/src/ct_webtool.erl b/lib/common_test/src/ct_webtool.erl index 6cbcd98cc3..87af442fd3 100644 --- a/lib/common_test/src/ct_webtool.erl +++ b/lib/common_test/src/ct_webtool.erl @@ -84,27 +84,27 @@ %% Function = {FunctionName,Arity} | FunctionName | %% {Module, FunctionName, Arity} | {Module,FunctionName} debug(F) -> - ttb:tracer(all,[{file,"webtool.trc"}]), % tracing all nodes - ttb:p(all,[call,timestamp]), + {ok, _} = ttb:tracer(all,[{file,"webtool.trc"}]), % tracing all nodes + {ok, _} = ttb:p(all,[call,timestamp]), MS = [{'_',[],[{return_trace},{message,{caller}}]}], - tp(F,MS), - ttb:ctp(?MODULE,stop_debug), % don't want tracing of the stop_debug func + _ = tp(F,MS), + {ok, _} = ttb:ctp(?MODULE,stop_debug), % don't want tracing of the stop_debug func ok. tp(local,MS) -> % all functions ttb:tpl(?MODULE,MS); tp(global,MS) -> % all exported functions ttb:tp(?MODULE,MS); tp([{M,F,A}|T],MS) -> % Other module - ttb:tpl(M,F,A,MS), + {ok, _} = ttb:tpl(M,F,A,MS), tp(T,MS); tp([{M,F}|T],MS) when is_atom(F) -> % Other module - ttb:tpl(M,F,MS), + {ok, _} = ttb:tpl(M,F,MS), tp(T,MS); tp([{F,A}|T],MS) -> % function/arity - ttb:tpl(?MODULE,F,A,MS), + {ok, _} = ttb:tpl(?MODULE,F,A,MS), tp(T,MS); tp([F|T],MS) -> % function - ttb:tpl(?MODULE,F,MS), + {ok, _} = ttb:tpl(?MODULE,F,MS), tp(T,MS); tp([],_MS) -> ok. @@ -112,10 +112,10 @@ stop_debug() -> ttb:stop([format]). debug_app(Mod) -> - ttb:tracer(all,[{file,"webtool_app.trc"},{handler,{fun out/4,true}}]), - ttb:p(all,[call,timestamp]), + {ok, _} = ttb:tracer(all,[{file,"webtool_app.trc"},{handler,{fun out/4,true}}]), + {ok, _} = ttb:p(all,[call,timestamp]), MS = [{'_',[],[{return_trace},{message,{caller}}]}], - ttb:tp(Mod,MS), + {ok, _} = ttb:tp(Mod,MS), ok. out(_,{trace_ts,Pid,call,MFA={M,F,A},{W,_,_},TS},_,S) @@ -145,7 +145,7 @@ script_start([App]) -> script_start([App,DefaultBrowser]); script_start([App,Browser]) -> io:format("Starting webtool...\n"), - start(), + {ok, _} = start(), AvailableApps = get_applications(), {OSType,_} = os:type(), case lists:keysearch(App,1,AvailableApps) of @@ -159,14 +159,14 @@ script_start([App,Browser]) -> _ -> "http://localhost:" ++ PortStr ++ "/" ++ StartPage end, - case Browser of + _ = case Browser of none -> ok; iexplore when OSType == win32-> io:format("Starting internet explorer...\n"), {ok,R} = win32reg:open(""), Key="\\local_machine\\SOFTWARE\\Microsoft\\IE Setup\\Setup", - win32reg:change_key(R,Key), + ok = win32reg:change_key(R,Key), {ok,Val} = win32reg:value(R,"Path"), IExplore=filename:join(win32reg:expand(Val),"iexplore.exe"), os:cmd("\"" ++ IExplore ++ "\" " ++ Url); @@ -186,7 +186,7 @@ script_start([App,Browser]) -> {Port,{exit_status,_Error}} -> io:format(" not running, starting ~w...\n", [Browser]), - os:cmd(BStr ++ " " ++ Url), + _ = os:cmd(BStr ++ " " ++ Url), ok after ?SEND_URL_TIMEOUT -> io:format(" failed, starting ~w...\n",[Browser]), @@ -206,7 +206,7 @@ script_start([App,Browser]) -> usage() -> io:format("Starting webtool...\n"), - start(), + {ok, _} = start(), Apps = lists:map(fun({A,_}) -> A end,get_applications()), io:format( "\nUsage: start_webtool application [ browser ]\n" @@ -254,7 +254,12 @@ start(Path,Port) when is_integer(Port)-> start(Path,Data0)-> Data = Data0 ++ rest_of_standard_data(), - gen_server:start({local,ct_web_tool},ct_webtool,{Path,Data},[]). + case gen_server:start({local,ct_web_tool},ct_webtool,{Path,Data},[]) of + {error, {already_started, Pid}} -> + {ok, Pid}; + Else -> + Else + end. stop()-> gen_server:call(ct_web_tool,stoppit). diff --git a/lib/common_test/src/cth_conn_log.erl b/lib/common_test/src/cth_conn_log.erl index 0501c6ed1c..883da0da0a 100644 --- a/lib/common_test/src/cth_conn_log.erl +++ b/lib/common_test/src/cth_conn_log.erl @@ -170,7 +170,7 @@ post_end_per_testcase(TestCase,_Config,Return,CthState) -> end, case ct_util:update_testdata(?MODULE, Update) of deleted -> - [ct_util:delete_testdata({?MODULE,ConnMod}) || + _ = [ct_util:delete_testdata({?MODULE,ConnMod}) || {ConnMod,_} <- CthState], error_logger:delete_report_handler(ct_conn_log_h); {error,no_response} -> diff --git a/lib/common_test/src/erl2html2.erl b/lib/common_test/src/erl2html2.erl index a1e0bf3879..e819f345de 100644 --- a/lib/common_test/src/erl2html2.erl +++ b/lib/common_test/src/erl2html2.erl @@ -62,15 +62,15 @@ convert(File, Dest, InclPath, Header) -> {ok,SFd} -> case file:open(Dest,[write,raw]) of {ok,DFd} -> - file:write(DFd,[Header,"<pre>\n"]), + ok = file:write(DFd,[Header,"<pre>\n"]), _Lines = build_html(SFd,DFd,encoding(File),Functions), - file:write(DFd,["</pre>\n",footer(), + ok = file:write(DFd,["</pre>\n",footer(), "</body>\n</html>\n"]), %% {_, Time2} = statistics(runtime), %% io:format("Converted ~p lines in ~.2f Seconds.~n", %% [_Lines, Time2/1000]), - file:close(SFd), - file:close(DFd), + ok = file:close(SFd), + ok = file:close(DFd), ok; Error -> Error @@ -138,7 +138,7 @@ parse_non_preprocessed_file(File) -> case file:open(File, []) of {ok,Epp} -> Forms = parse_non_preprocessed_file(Epp, File, 1), - file:close(Epp), + ok = file:close(Epp), {ok,Forms}; Error = {error,_E} -> Error @@ -205,14 +205,14 @@ build_html(SFd,DFd,Encoding,FuncsAndCs) -> %% line of last expression in function found build_html(SFd,DFd,Enc,{ok,Str},LastL,FuncsAndCs,_IsFuncDef,{F,LastL}) -> LastLineLink = test_server_ctrl:uri_encode(F++"-last_expr",utf8), - file:write(DFd,["<a name=\"", - to_raw_list(LastLineLink,Enc),"\"/>"]), + ok = file:write(DFd,["<a name=\"", + to_raw_list(LastLineLink,Enc),"\"/>"]), build_html(SFd,DFd,Enc,{ok,Str},LastL,FuncsAndCs,true,undefined); %% function start line found build_html(SFd,DFd,Enc,{ok,Str},L0,[{F,A,L0,LastL}|FuncsAndCs], _IsFuncDef,_FAndLastL) -> FALink = test_server_ctrl:uri_encode(F++"-"++integer_to_list(A),utf8), - file:write(DFd,["<a name=\"",to_raw_list(FALink,Enc),"\"/>"]), + ok = file:write(DFd,["<a name=\"",to_raw_list(FALink,Enc),"\"/>"]), build_html(SFd,DFd,Enc,{ok,Str},L0,FuncsAndCs,true,{F,LastL}); build_html(SFd,DFd,Enc,{ok,Str},L,[{clause,L}|FuncsAndCs], _IsFuncDef,FAndLastL) -> @@ -220,7 +220,7 @@ build_html(SFd,DFd,Enc,{ok,Str},L,[{clause,L}|FuncsAndCs], build_html(SFd,DFd,Enc,{ok,Str},L,FuncsAndCs,IsFuncDef,FAndLastL) -> LStr = line_number(L), Str1 = line(Str,IsFuncDef), - file:write(DFd,[LStr,Str1]), + ok = file:write(DFd,[LStr,Str1]), build_html(SFd,DFd,Enc,file:read_line(SFd),L+1,FuncsAndCs,false,FAndLastL); build_html(_SFd,_DFd,_Enc,eof,L,_FuncsAndCs,_IsFuncDef,_FAndLastL) -> L. diff --git a/lib/common_test/src/test_server.erl b/lib/common_test/src/test_server.erl index bd677e971b..924086f2bd 100644 --- a/lib/common_test/src/test_server.erl +++ b/lib/common_test/src/test_server.erl @@ -1653,7 +1653,8 @@ messages_get() -> %% %% Make sure proceeding IO from FromPid won't get rejected permit_io(GroupLeader, FromPid) -> - GroupLeader ! {permit_io,FromPid}. + GroupLeader ! {permit_io,FromPid}, + ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% sleep(Time) -> ok diff --git a/lib/common_test/src/test_server_ctrl.erl b/lib/common_test/src/test_server_ctrl.erl index 84e35e7371..b52e4bef9b 100644 --- a/lib/common_test/src/test_server_ctrl.erl +++ b/lib/common_test/src/test_server_ctrl.erl @@ -294,7 +294,7 @@ start_link(_) -> start() -> case gen_server:start({local,?MODULE}, ?MODULE, [], []) of - {ok, Pid} -> + {error, {already_started, Pid}} -> {ok, Pid}; Other -> Other @@ -302,7 +302,7 @@ start() -> start_link() -> case gen_server:start_link({local,?MODULE}, ?MODULE, [], []) of - {ok, Pid} -> + {error, {already_started, Pid}} -> {ok, Pid}; Other -> Other diff --git a/lib/common_test/test/ct_gen_conn_SUITE_data/conn_SUITE.erl b/lib/common_test/test/ct_gen_conn_SUITE_data/conn_SUITE.erl index 1404df6410..19eb1211fa 100644 --- a/lib/common_test/test/ct_gen_conn_SUITE_data/conn_SUITE.erl +++ b/lib/common_test/test/ct_gen_conn_SUITE_data/conn_SUITE.erl @@ -73,24 +73,28 @@ handles_to_multi_conn_pids(_Config) -> ConnPid3 = ct_gen_conn:get_conn_pid(Handle3), {true,true} = {is_process_alive(Handle3),is_process_alive(ConnPid3)}, + monitor_procs([Handle1,ConnPid1,Handle2,ConnPid2,Handle3,ConnPid3]), + ok = proto:close(Handle1), - ct:sleep(100), + ok = wait_procs_down([Handle1,ConnPid1]), {false,false} = {is_process_alive(Handle1),is_process_alive(ConnPid1)}, {true,true} = {is_process_alive(Handle2),is_process_alive(ConnPid2)}, ok = proto:kill_conn_proc(Handle2), - ct:sleep(100), + ok = wait_procs_down([ConnPid2]), {true,false} = {is_process_alive(Handle2),is_process_alive(ConnPid2)}, ConnPid2x = ct_gen_conn:get_conn_pid(Handle2), true = is_process_alive(ConnPid2x), + monitor_procs([ConnPid2x]), + ok = proto:close(Handle2), - ct:sleep(100), + ok = wait_procs_down([Handle2,ConnPid2x]), {false,false} = {is_process_alive(Handle2),is_process_alive(ConnPid2x)}, application:set_env(ct_test, reconnect, false), ok = proto:kill_conn_proc(Handle3), - ct:sleep(100), + ok = wait_procs_down([Handle3,ConnPid3]), {false,false} = {is_process_alive(Handle3),is_process_alive(ConnPid3)}, ok. @@ -116,24 +120,28 @@ handles_to_single_conn_pids(_Config) -> {undefined,Handle3,_,_}] = lists:sort(ct_util:get_connections(ConnPid)), ct:pal("CONNS = ~n~p", [Conns]), + monitor_procs([Handle1,Handle2,Handle3,ConnPid]), ok = proto:close(Handle1), - ct:sleep(100), + ok = wait_procs_down([Handle1]), {false,true} = {is_process_alive(Handle1),is_process_alive(ConnPid)}, ok = proto:kill_conn_proc(Handle2), - ct:sleep(100), + ok = wait_procs_down([ConnPid]), NewConnPid = ct_gen_conn:get_conn_pid(Handle2), NewConnPid = ct_gen_conn:get_conn_pid(Handle3), true = is_process_alive(Handle2), true = is_process_alive(Handle3), + false = is_process_alive(ConnPid), + + monitor_procs([NewConnPid]), ok = proto:close(Handle2), - ct:sleep(100), + ok = wait_procs_down([Handle2]), {false,true} = {is_process_alive(Handle2),is_process_alive(NewConnPid)}, application:set_env(ct_test, reconnect, false), ok = proto:kill_conn_proc(Handle3), - ct:sleep(100), + ok = wait_procs_down([Handle3,NewConnPid]), {false,false} = {is_process_alive(Handle3),is_process_alive(NewConnPid)}, ok. @@ -158,30 +166,37 @@ names_to_multi_conn_pids(_Config) -> Handle1 = proto:open(mconn1), + monitor_procs([Handle1,ConnPid1,Handle2,ConnPid2,Handle3,ConnPid3]), + ok = proto:close(mconn1), - ct:sleep(100), + ok = wait_procs_down([Handle1,ConnPid1]), {false,false} = {is_process_alive(Handle1),is_process_alive(ConnPid1)}, ok = proto:kill_conn_proc(Handle2), - ct:sleep(100), + ok = wait_procs_down([ConnPid2]), Handle2 = proto:open(mconn2), % should've been reconnected already {true,false} = {is_process_alive(Handle2),is_process_alive(ConnPid2)}, ConnPid2x = ct_gen_conn:get_conn_pid(Handle2), true = is_process_alive(ConnPid2x), + monitor_procs([ConnPid2x]), + ok = proto:close(mconn2), - ct:sleep(100), + ok = wait_procs_down([Handle2,ConnPid2x]), {false,false} = {is_process_alive(Handle2),is_process_alive(ConnPid2x)}, Handle2y = proto:open(mconn2), ConnPid2y = ct_gen_conn:get_conn_pid(Handle2y), {true,true} = {is_process_alive(Handle2y),is_process_alive(ConnPid2y)}, + + monitor_procs([Handle2y,ConnPid2y]), + ok = proto:close(mconn2), - ct:sleep(100), + ok = wait_procs_down([Handle2y,ConnPid2y]), {false,false} = {is_process_alive(Handle2y),is_process_alive(ConnPid2y)}, application:set_env(ct_test, reconnect, false), ok = proto:kill_conn_proc(Handle3), - ct:sleep(100), + ok = wait_procs_down([Handle3,ConnPid3]), {false,false} = {is_process_alive(Handle3),is_process_alive(ConnPid3)}, ok. @@ -211,16 +226,20 @@ names_to_single_conn_pids(_Config) -> {sconn3,Handle3,_,_}] = lists:sort(ct_util:get_connections(ConnPid)), ct:pal("CONNS on ~p = ~n~p", [ConnPid,Conns]), + monitor_procs([Handle1,Handle2,Handle3,ConnPid]), + ok = proto:close(sconn1), - ct:sleep(100), + ok = wait_procs_down([Handle1]), {false,true} = {is_process_alive(Handle1),is_process_alive(ConnPid)}, ok = proto:kill_conn_proc(Handle2), - ct:sleep(100), + ok = wait_procs_down([ConnPid]), {true,false} = {is_process_alive(Handle2),is_process_alive(ConnPid)}, Handle2 = proto:open(sconn2), % should've been reconnected already NewConnPid = ct_gen_conn:get_conn_pid(Handle2), true = is_process_alive(NewConnPid), + + monitor_procs([NewConnPid]), Conns1 = [{sconn2,Handle2,_,_}, {sconn3,Handle3,_,_}] = @@ -228,14 +247,29 @@ names_to_single_conn_pids(_Config) -> ct:pal("CONNS on ~p = ~n~p", [NewConnPid,Conns1]), ok = proto:close(sconn2), - ct:sleep(100), + ok = wait_procs_down([Handle2]), {false,true} = {is_process_alive(Handle2),is_process_alive(NewConnPid)}, application:set_env(ct_test, reconnect, false), ok = proto:kill_conn_proc(Handle3), - ct:sleep(100), + ok = wait_procs_down([Handle3,NewConnPid]), {false,false} = {is_process_alive(Handle3),is_process_alive(NewConnPid)}, ok. +%%%----------------------------------------------------------------- +monitor_procs(Pids) -> + [erlang:monitor(process,Pid) || Pid <- Pids], + ok. + +wait_procs_down([]) -> + ok; +wait_procs_down(Pids) -> + receive + {'DOWN',_,process,Pid,_} -> + wait_procs_down(lists:delete(Pid,Pids)) + after 2000 -> + timeout + end. + diff --git a/lib/common_test/test/ct_netconfc_SUITE.erl b/lib/common_test/test/ct_netconfc_SUITE.erl index 03fbc17bd2..2919f01605 100644 --- a/lib/common_test/test/ct_netconfc_SUITE.erl +++ b/lib/common_test/test/ct_netconfc_SUITE.erl @@ -44,16 +44,28 @@ %% there will be clashes with logging processes etc). %%-------------------------------------------------------------------- init_per_suite(Config) -> - case application:load(crypto) of - {error,Reason} when Reason=/={already_loaded,crypto} -> - {skip, Reason}; - _ -> - case application:load(ssh) of - {error,Reason} when Reason=/={already_loaded,ssh} -> - {skip, Reason}; - _ -> - ct_test_support:init_per_suite(Config) - end + case check_crypto_and_ssh() of + ok -> + ct_test_support:init_per_suite(Config); + Skip -> + Skip + end. + +check_crypto_and_ssh() -> + (catch code:load_file(crypto)), + case code:is_loaded(crypto) of + {file,_} -> + case ssh:start() of + Ok when Ok==ok; Ok=={error,{already_started,ssh}} -> + ct:log("ssh started",[]), + ok; + Other -> + ct:log("could not start ssh: ~p",[Other]), + {skip, "SSH could not be started!"} + end; + Other -> + ct:log("could not load crypto: ~p",[Other]), + {skip, "crypto could not be loaded!"} end. end_per_suite(Config) -> diff --git a/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl index f34969683c..2aa6c4d354 100644 --- a/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl +++ b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl @@ -124,8 +124,9 @@ end_per_testcase(_Case, _Config) -> ok. init_per_suite(Config) -> - case catch ssh:start() of - Ok when Ok==ok; Ok=={error,{already_started,ssh}} -> + (catch code:load_file(crypto)), + case {ssh:start(),code:is_loaded(crypto)} of + {Ok,{file,_}} when Ok==ok; Ok=={error,{already_started,ssh}} -> ct:log("ssh started",[]), SshDir = filename:join(filename:dirname(code:which(?MODULE)), "ssh_dir"), @@ -133,7 +134,7 @@ init_per_suite(Config) -> ct:log("netconf server started",[]), [{netconf_server,Server},{ssh_dir,SshDir}|Config]; Other -> - ct:log("could not start ssh: ~p",[Other]), + ct:log("could not start ssh or load crypto: ~p",[Other]), {skip, "SSH could not be started!"} end. @@ -360,7 +361,7 @@ get(Config) -> get_a_lot(Config) -> SshDir = ?config(ssh_dir,Config), {ok,Client} = open_success(SshDir), - Descr = lists:append(lists:duplicate(1000,"Description of myserver! ")), + Descr = lists:append(lists:duplicate(100,"Description of myserver! ")), Server = {server,[{xmlns,"myns"}],[{name,[],["myserver"]}, {description,[],[Descr]}]}, Data = lists:duplicate(100,Server), diff --git a/lib/common_test/test/ct_netconfc_SUITE_data/netconfc_remote_SUITE.erl b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc_remote_SUITE.erl index 0a49cdabbb..a65275da43 100644 --- a/lib/common_test/test/ct_netconfc_SUITE_data/netconfc_remote_SUITE.erl +++ b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc_remote_SUITE.erl @@ -62,14 +62,15 @@ stop_node(Case) -> init_per_suite(Config) -> - case ssh:start() of - Ok when Ok==ok; Ok=={error,{already_started,ssh}} -> + (catch code:load_file(crypto)), + case {ssh:start(),code:is_loaded(crypto)} of + {Ok,{file,_}} when Ok==ok; Ok=={error,{already_started,ssh}} -> ct:log("SSH started locally",[]), SshDir = filename:join(filename:dirname(code:which(?MODULE)), "ssh_dir"), [{ssh_dir,SshDir}|Config]; Other -> - ct:log("could not start ssh locally: ~p",[Other]), + ct:log("could not start ssh or load crypto locally: ~p",[Other]), {skip, "SSH could not be started locally!"} end. @@ -85,15 +86,15 @@ remote_crash(Config) -> {ok,Node} = ct_slave:start(nc_remote_crash), Pa = filename:dirname(code:which(?NS)), true = rpc:call(Node,code,add_patha,[Pa]), - - case rpc:call(Node,ssh,start,[]) of - Ok when Ok==ok; Ok=={error,{already_started,ssh}} -> + rpc:call(Node,code,load_file,[crypto]), + case {rpc:call(Node,ssh,start,[]),rpc:call(Node,code,is_loaded,[crypto])} of + {Ok,{file,_}} when Ok==ok; Ok=={error,{already_started,ssh}} -> ct:log("SSH started remote",[]), ns(Node,start,[?config(ssh_dir,Config)]), ct:log("netconf server started remote",[]), remote_crash(Node,Config); Other -> - ct:log("could not start ssh remote: ~p",[Other]), + ct:log("could not start ssh or load crypto remote: ~p",[Other]), {skip, "SSH could not be started remote!"} end. diff --git a/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl b/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl index 9fb1fb6547..e62bc617fa 100644 --- a/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl +++ b/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl @@ -279,7 +279,7 @@ send({CM,Ch},Data) -> %%% Split into many small parts and send to client send_frag({CM,Ch},Data) -> - Sz = rand:uniform(2000), + Sz = rand:uniform(1000), case Data of <<Chunk:Sz/binary,Rest/binary>> -> ssh_connection:send(CM, Ch, Chunk), diff --git a/lib/common_test/test/ct_test_support.erl b/lib/common_test/test/ct_test_support.erl index 477fcb8a26..e926abd885 100644 --- a/lib/common_test/test/ct_test_support.erl +++ b/lib/common_test/test/ct_test_support.erl @@ -484,7 +484,8 @@ get_events(_, Config) -> {event_receiver,CTNode} ! {self(),get_events}, Events = receive {event_receiver,Evs} -> Evs end, test_server:format(Level, "Stopping event receiver!~n", []), - {event_receiver,CTNode} ! stop, + {event_receiver,CTNode} ! {self(),stop}, + receive {event_receiver,stopped} -> ok end, Events. er() -> @@ -499,8 +500,9 @@ er_loop(Evs) -> {From,get_events} -> From ! {event_receiver,lists:reverse(Evs)}, er_loop(Evs); - stop -> + {From,stop} -> unregister(event_receiver), + From ! {event_receiver,stopped}, ok end. diff --git a/lib/common_test/test/telnet_server.erl b/lib/common_test/test/telnet_server.erl index 2c33cb268a..65300b0bdf 100644 --- a/lib/common_test/test/telnet_server.erl +++ b/lib/common_test/test/telnet_server.erl @@ -117,38 +117,64 @@ init_client(#state{client=Sock}=State) -> dbg("Server sending: ~p~n",["login: "]), R = case gen_tcp:send(Sock,"login: ") of ok -> - loop(State, 1); + loop(State); Error -> Error end, _ = gen_tcp:close(Sock), R. -loop(State, N) -> +loop(State=#state{client=Sock}) -> receive - {tcp,_,Data} -> + {tcp,Sock,Data} -> try handle_data(Data,State) of {ok,State1} -> - loop(State1, N); + loop(State1); closed -> + _ = flush(State), closed catch throw:Error -> + _ = flush(State), Error end; - {tcp_closed, _} -> + {tcp_closed,Sock} -> closed; - {tcp_error,_,Error} -> + {tcp_error,Sock,Error} -> {error,tcp,Error}; disconnect -> - Sock = State#state.client, dbg("Server closing connection on socket ~p~n", [Sock]), + timer:sleep(1000), ok = gen_tcp:close(Sock), - closed; + _ = flush(State); stop -> + _ = flush(State), stopped end. +flush(State=#state{client=Sock}) -> + receive + {tcp,Sock,Data} = M-> + dbg("Message flushed after close or error: ~p~n", [M]), + try handle_data(Data,State) of + {ok,State1} -> + flush(State1); + closed -> + flush(State) + catch + throw:Error -> + Error + end; + {tcp_closed,Sock} = M -> + dbg("Message flushed after close or error: ~p~n", [M]), + ok; + {tcp_error,Sock,Error} = M -> + dbg("Message flushed after close or error: ~p~n", [M]), + {error,tcp,Error} + after 100 -> + ok + end. + handle_data(Cmd,#state{break=true}=State) -> dbg("Server got data when in break mode: ~p~n",[Cmd]), handle_break_cmd(Cmd,State); diff --git a/lib/dialyzer/test/map_SUITE_data/results/exact b/lib/dialyzer/test/map_SUITE_data/results/exact index 374ada8869..ea00e61330 100644 --- a/lib/dialyzer/test/map_SUITE_data/results/exact +++ b/lib/dialyzer/test/map_SUITE_data/results/exact @@ -1,3 +1,3 @@ exact.erl:15: Function t2/1 has no local return -exact.erl:19: The variable _ can never match since previous clauses completely covered the type #{'a':=_, ...} +exact.erl:19: The variable _ can never match since previous clauses completely covered the type #{'a':=_, _=>_} diff --git a/lib/dialyzer/test/map_SUITE_data/results/guard_update b/lib/dialyzer/test/map_SUITE_data/results/guard_update index e4bc892195..98df23907f 100644 --- a/lib/dialyzer/test/map_SUITE_data/results/guard_update +++ b/lib/dialyzer/test/map_SUITE_data/results/guard_update @@ -1,5 +1,5 @@ guard_update.erl:5: Function t/0 has no local return -guard_update.erl:6: The call guard_update:f(#{'a':=2}) will never return since it differs in the 1st argument from the success typing arguments: (#{'b':=_, ...}) +guard_update.erl:6: The call guard_update:f(#{'a':=2}) will never return since it differs in the 1st argument from the success typing arguments: (#{'b':=_, _=>_}) guard_update.erl:8: Clause guard cannot succeed. The variable M was matched against the type #{'a':=2} guard_update.erl:8: Function f/1 has no local return diff --git a/lib/dialyzer/test/map_SUITE_data/results/map_in_guard2 b/lib/dialyzer/test/map_SUITE_data/results/map_in_guard2 index 6bc0c010d7..f6fb98a863 100644 --- a/lib/dialyzer/test/map_SUITE_data/results/map_in_guard2 +++ b/lib/dialyzer/test/map_SUITE_data/results/map_in_guard2 @@ -1,7 +1,7 @@ map_in_guard2.erl:10: The call map_in_guard2:assoc_guard_clause('not_a_map') will never return since it differs in the 1st argument from the success typing arguments: (map()) map_in_guard2.erl:12: The pattern 'true' can never match the type 'false' -map_in_guard2.erl:14: The call map_in_guard2:exact_guard_clause(#{}) will never return since it differs in the 1st argument from the success typing arguments: (#{'a':=_, ...}) +map_in_guard2.erl:14: The call map_in_guard2:exact_guard_clause(#{}) will never return since it differs in the 1st argument from the success typing arguments: (#{'a':=_, _=>_}) map_in_guard2.erl:17: Clause guard cannot succeed. The variable M was matched against the type 'not_a_map' map_in_guard2.erl:20: Function assoc_update/1 has no local return map_in_guard2.erl:20: Guard test is_map(M::'not_a_map') can never succeed diff --git a/lib/dialyzer/test/map_SUITE_data/results/typeflow b/lib/dialyzer/test/map_SUITE_data/results/typeflow index e3378a24bb..acfb7f551e 100644 --- a/lib/dialyzer/test/map_SUITE_data/results/typeflow +++ b/lib/dialyzer/test/map_SUITE_data/results/typeflow @@ -1,4 +1,4 @@ typeflow.erl:14: Function t2/1 has no local return typeflow.erl:16: The call lists:sort(integer()) will never return since it differs in the 1st argument from the success typing arguments: ([any()]) -typeflow.erl:9: The variable _ can never match since previous clauses completely covered the type #{'a':=integer(), ...} +typeflow.erl:9: The variable _ can never match since previous clauses completely covered the type #{'a':=integer(), _=>_} diff --git a/lib/dialyzer/test/map_SUITE_data/results/typesig b/lib/dialyzer/test/map_SUITE_data/results/typesig index 3049402860..fb2f851a7d 100644 --- a/lib/dialyzer/test/map_SUITE_data/results/typesig +++ b/lib/dialyzer/test/map_SUITE_data/results/typesig @@ -1,5 +1,5 @@ typesig.erl:5: Function t1/0 has no local return -typesig.erl:5: The call typesig:test(#{'a':=1}) will never return since it differs in the 1st argument from the success typing arguments: (#{'a':={number()}, ...}) +typesig.erl:5: The call typesig:test(#{'a':=1}) will never return since it differs in the 1st argument from the success typing arguments: (#{'a':={number()}, _=>_}) typesig.erl:6: Function t2/0 has no local return -typesig.erl:6: The call typesig:test(#{'a':={'b'}}) will never return since it differs in the 1st argument from the success typing arguments: (#{'a':={number()}, ...}) +typesig.erl:6: The call typesig:test(#{'a':={'b'}}) will never return since it differs in the 1st argument from the success typing arguments: (#{'a':={number()}, _=>_}) diff --git a/lib/dialyzer/test/small_SUITE_data/results/maps1 b/lib/dialyzer/test/small_SUITE_data/results/maps1 index a178e96b20..f36f7f4926 100644 --- a/lib/dialyzer/test/small_SUITE_data/results/maps1 +++ b/lib/dialyzer/test/small_SUITE_data/results/maps1 @@ -1,4 +1,4 @@ maps1.erl:43: Function t3/0 has no local return -maps1.erl:44: The call maps1:foo(#{'greger'=>3, #{'arne'=>'anka'}=>45},1) will never return since it differs in the 1st and 2nd argument from the success typing arguments: (#{'beta':=_, ...},'b') +maps1.erl:44: The call maps1:foo(#{'greger'=>3, #{'arne'=>'anka'}=>45},1) will never return since it differs in the 1st and 2nd argument from the success typing arguments: (#{'beta':=_, _=>_},'b') maps1.erl:52: The variable Mod can never match since previous clauses completely covered the type #{} diff --git a/lib/edoc/src/edoc_layout.erl b/lib/edoc/src/edoc_layout.erl index e86d090b13..ef57b7b084 100644 --- a/lib/edoc/src/edoc_layout.erl +++ b/lib/edoc/src/edoc_layout.erl @@ -901,17 +901,11 @@ t_map(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. + AS = case get_attrval(assoc_type, E) of + "assoc" -> " => "; + "exact" -> " := " + end, + KElem ++ [AS] ++ VElem. t_record(E, Es) -> Name = ["#"] ++ t_type(get_elem(atom, Es)), diff --git a/lib/edoc/src/edoc_parser.yrl b/lib/edoc/src/edoc_parser.yrl index 983e2f8496..68a3439f10 100644 --- a/lib/edoc/src/edoc_parser.yrl +++ b/lib/edoc/src/edoc_parser.yrl @@ -82,9 +82,6 @@ utype_map_field -> utype '=>' utype : #t_map_field{assoc_type = assoc, 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'). @@ -354,9 +351,6 @@ 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/erl_docgen/src/docgen_otp_specs.erl b/lib/erl_docgen/src/docgen_otp_specs.erl index 5bc3be7a8d..6c41147e27 100644 --- a/lib/erl_docgen/src/docgen_otp_specs.erl +++ b/lib/erl_docgen/src/docgen_otp_specs.erl @@ -446,17 +446,11 @@ t_map(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. + AS = case get_attrval(assoc_type, E) of + "assoc" -> " => "; + "exact" -> " := " + end, + KElem ++ [AS] ++ VElem. t_record(E, Es) -> Name = ["#"] ++ t_type(get_elem(atom, Es)), diff --git a/lib/hipe/cerl/erl_types.erl b/lib/hipe/cerl/erl_types.erl index b62cd5ba35..7826dada9d 100644 --- a/lib/hipe/cerl/erl_types.erl +++ b/lib/hipe/cerl/erl_types.erl @@ -4304,7 +4304,6 @@ t_to_string(?map(Pairs0,DefK,DefV), RecDict) -> {Pairs, ExtraEl} = case {DefK, DefV} of {?none, ?none} -> {Pairs0, []}; - {?any, ?any} -> {Pairs0, ["..."]}; _ -> {Pairs0 ++ [{DefK,?opt,DefV}], []} end, Tos = fun(T) -> case T of diff --git a/lib/inets/test/ftp_SUITE.erl b/lib/inets/test/ftp_SUITE.erl index a8d39e3fe7..e2dec0c42a 100644 --- a/lib/inets/test/ftp_SUITE.erl +++ b/lib/inets/test/ftp_SUITE.erl @@ -114,6 +114,7 @@ ftp_tests()-> %%% ftpservers list of servers to check if they are available %%% The element is: %%% {Name, % string(). The os command name +%%% Path, % string(). The os PATH syntax, e.g "/bin:/usr/bin" %%% StartCommand, % fun()->{ok,start_result()} | {error,string()}. %%% % The command to start the daemon with. %%% ChkUp, % fun(start_result()) -> string(). Os command to check @@ -129,12 +130,13 @@ ftp_tests()-> -define(default_ftp_servers, [{"vsftpd", - fun(__CONF__) -> + "/sbin:/usr/sbin:/usr/local/sbin", + fun(__CONF__, AbsName) -> DataDir = proplists:get_value(data_dir,__CONF__), ConfFile = filename:join(DataDir, "vsftpd.conf"), PrivDir = proplists:get_value(priv_dir,__CONF__), AnonRoot = PrivDir, - Cmd = ["vsftpd "++filename:join(DataDir,"vsftpd.conf"), + Cmd = [AbsName ++" "++filename:join(DataDir,"vsftpd.conf"), " -oftpd_banner=erlang_otp_testing", " -oanon_root=\"",AnonRoot,"\"", " -orsa_cert_file=\"",filename:join(DataDir,"server-cert.pem"),"\"", @@ -856,28 +858,51 @@ chk_no_dir(PathList, Config) -> %%-------------------------------------------------------------------- find_executable(Config) -> - FTPservers = case proplists:get_value(ftpservers,Config) of - undefined -> ?default_ftp_servers; - L -> L - end, - case lists:dropwhile(fun not_available/1, FTPservers) of - [] -> false; - [FTPD_data|_] -> {ok, FTPD_data} - end. + search_executable(proplists:get_value(ftpservers, Config, ?default_ftp_servers)). + + +search_executable([{Name,Paths,_StartCmd,_ChkUp,_StopCommand,_ConfigUpd,_Host,_Port}|Srvrs]) -> + case os_find(Name,Paths) of + false -> + ct:log("~p not found",[Name]), + search_executable(Srvrs); + AbsName -> + ct:comment("Found ~p",[AbsName]), + {ok, {AbsName,_StartCmd,_ChkUp,_StopCommand,_ConfigUpd,_Host,_Port}} + end; +search_executable([]) -> + false. -not_available({Name,_StartCmd,_ChkUp,_StopCommand,_ConfigUpd,_Host,_Port}) -> - os:find_executable(Name) == false. +os_find(Name, Paths) -> + case os:find_executable(Name, Paths) of + false -> os:find_executable(Name); + AbsName -> AbsName + end. -start_ftpd(Config) -> - {Name,StartCmd,_ChkUp,_StopCommand,ConfigRewrite,Host,Port} = proplists:get_value(ftpd_data, Config), - case StartCmd(Config) of +%%%---------------------------------------------------------------- +start_ftpd(Config0) -> + {AbsName,StartCmd,_ChkUp,_StopCommand,ConfigRewrite,Host,Port} = + proplists:get_value(ftpd_data, Config0), + case StartCmd(Config0, AbsName) of {ok,StartResult} -> - [{ftpd_host,Host}, - {ftpd_port,Port}, - {ftpd_start_result,StartResult} | ConfigRewrite(Config)]; + Config = [{ftpd_host,Host}, + {ftpd_port,Port}, + {ftpd_start_result,StartResult} | ConfigRewrite(Config0)], + try + ftp__close(ftp__open(Config,[verbose])) + of + Config1 when is_list(Config1) -> + ct:log("Usuable ftp server ~p started on ~p:~p",[AbsName,Host,Port]), + Config + catch + Class:Exception -> + ct:log("Ftp server ~p started on ~p:~p but is unusable:~n~p:~p", + [AbsName,Host,Port,Class,Exception]), + {skip, [AbsName," started but unusuable"]} + end; {error,Msg} -> - {skip, [Name," not started: ",Msg]} + {skip, [AbsName," not started: ",Msg]} end. stop_ftpd(Config) -> diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl index 42772923e4..932567ec55 100644 --- a/lib/inets/test/httpc_SUITE.erl +++ b/lib/inets/test/httpc_SUITE.erl @@ -2080,7 +2080,7 @@ run_clients(NumClients, ServerPort, SeqNumServer) -> end end), MRef = erlang:monitor(process, Pid), - timer:sleep(10 + random:uniform(1334)), + timer:sleep(10 + rand:uniform(1334)), {Id, Pid, MRef} end, lists:seq(1, NumClients)). @@ -2169,7 +2169,7 @@ slowly_send_response(CSock, Answer) -> [length(Answer), Answer])), lists:foreach( fun(Char) -> - timer:sleep(random:uniform(500)), + timer:sleep(rand:uniform(500)), gen_tcp:send(CSock, <<Char>>) end, Response). @@ -2189,9 +2189,8 @@ parse_connection_type(Request) -> set_random_seed() -> Unique = erlang:unique_integer(), - A = erlang:phash2([make_ref(), self(), Unique]), - random:seed(A, A, A). + rand:seed(exsplus, {A, A, A}). otp_8739(doc) -> diff --git a/lib/runtime_tools/c_src/dyntrace_lttng.h b/lib/runtime_tools/c_src/dyntrace_lttng.h index 2a3224e191..5e838892d6 100644 --- a/lib/runtime_tools/c_src/dyntrace_lttng.h +++ b/lib/runtime_tools/c_src/dyntrace_lttng.h @@ -19,7 +19,7 @@ */ #undef TRACEPOINT_PROVIDER -#define TRACEPOINT_PROVIDER com_ericsson_dyntrace +#define TRACEPOINT_PROVIDER org_erlang_dyntrace #if !defined(DYNTRACE_LTTNG_H) || defined(TRACEPOINT_HEADER_MULTI_READ) #define DYNTRACE_LTTNG_H @@ -27,22 +27,22 @@ #include <lttng/tracepoint.h> #define LTTNG1(Name, Arg1) \ - tracepoint(com_ericsson_dyntrace, Name, (Arg1)) + tracepoint(org_erlang_dyntrace, Name, (Arg1)) #define LTTNG2(Name, Arg1, Arg2) \ - tracepoint(com_ericsson_dyntrace, Name, (Arg1), (Arg2)) + tracepoint(org_erlang_dyntrace, Name, (Arg1), (Arg2)) #define LTTNG3(Name, Arg1, Arg2, Arg3) \ - tracepoint(com_ericsson_dyntrace, Name, (Arg1), (Arg2), (Arg3)) + tracepoint(org_erlang_dyntrace, Name, (Arg1), (Arg2), (Arg3)) #define LTTNG4(Name, Arg1, Arg2, Arg3, Arg4) \ - tracepoint(com_ericsson_dyntrace, Name, (Arg1), (Arg2), (Arg3), (Arg4)) + tracepoint(org_erlang_dyntrace, Name, (Arg1), (Arg2), (Arg3), (Arg4)) #define LTTNG5(Name, Arg1, Arg2, Arg3, Arg4, Arg5) \ - tracepoint(com_ericsson_dyntrace, Name, (Arg1), (Arg2), (Arg3), (Arg4), (Arg5)) + tracepoint(org_erlang_dyntrace, Name, (Arg1), (Arg2), (Arg3), (Arg4), (Arg5)) #define LTTNG_ENABLED(Name) \ - tracepoint_enabled(com_ericsson_dyntrace, Name) + tracepoint_enabled(org_erlang_dyntrace, Name) #define LTTNG_BUFFER_SZ (256) #define LTTNG_PROC_BUFFER_SZ (16) @@ -76,7 +76,7 @@ /* Process scheduling */ TRACEPOINT_EVENT( - com_ericsson_dyntrace, + org_erlang_dyntrace, process_spawn, TP_ARGS( char*, p, @@ -91,7 +91,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_dyntrace, + org_erlang_dyntrace, process_link, TP_ARGS( char*, from, @@ -106,7 +106,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_dyntrace, + org_erlang_dyntrace, process_exit, TP_ARGS( char*, p, @@ -119,7 +119,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_dyntrace, + org_erlang_dyntrace, process_register, TP_ARGS( char*, pid, @@ -136,7 +136,7 @@ TRACEPOINT_EVENT( /* Scheduled */ TRACEPOINT_EVENT( - com_ericsson_dyntrace, + org_erlang_dyntrace, process_scheduled, TP_ARGS( char*, p, @@ -154,7 +154,7 @@ TRACEPOINT_EVENT( TRACEPOINT_EVENT( - com_ericsson_dyntrace, + org_erlang_dyntrace, port_open, TP_ARGS( char*, pid, @@ -169,7 +169,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_dyntrace, + org_erlang_dyntrace, port_exit, TP_ARGS( char*, port, @@ -182,7 +182,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_dyntrace, + org_erlang_dyntrace, port_link, TP_ARGS( char*, from, @@ -197,7 +197,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_dyntrace, + org_erlang_dyntrace, port_scheduled, TP_ARGS( char*, p, @@ -214,7 +214,7 @@ TRACEPOINT_EVENT( /* Call tracing */ TRACEPOINT_EVENT( - com_ericsson_dyntrace, + org_erlang_dyntrace, function_call, TP_ARGS( char*, pid, @@ -229,7 +229,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_dyntrace, + org_erlang_dyntrace, function_return, TP_ARGS( char*, pid, @@ -244,7 +244,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_dyntrace, + org_erlang_dyntrace, function_exception, TP_ARGS( char*, pid, @@ -261,7 +261,7 @@ TRACEPOINT_EVENT( /* Process messages */ TRACEPOINT_EVENT( - com_ericsson_dyntrace, + org_erlang_dyntrace, message_send, TP_ARGS( char*, sender, @@ -276,7 +276,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_dyntrace, + org_erlang_dyntrace, message_receive, TP_ARGS( char*, receiver, @@ -291,7 +291,7 @@ TRACEPOINT_EVENT( /* Process Memory */ TRACEPOINT_EVENT( - com_ericsson_dyntrace, + org_erlang_dyntrace, gc_minor_start, TP_ARGS( char*, p, @@ -308,7 +308,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_dyntrace, + org_erlang_dyntrace, gc_minor_end, TP_ARGS( char*, p, @@ -325,7 +325,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_dyntrace, + org_erlang_dyntrace, gc_major_start, TP_ARGS( char*, p, @@ -342,7 +342,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_dyntrace, + org_erlang_dyntrace, gc_major_end, TP_ARGS( char*, p, diff --git a/lib/runtime_tools/doc/src/LTTng.xml b/lib/runtime_tools/doc/src/LTTng.xml index 9b490a27a0..82a4c79379 100644 --- a/lib/runtime_tools/doc/src/LTTng.xml +++ b/lib/runtime_tools/doc/src/LTTng.xml @@ -66,7 +66,7 @@ $ make </code> <section> <title>Dyntrace Tracepoints</title> - <p>All tracepoints are in the domain of <c>com_ericsson_dyntrace</c></p> + <p>All tracepoints are in the domain of <c>org_erlang_dyntrace</c></p> <p>All Erlang types are the string equivalent in LTTng.</p> <p><em>process_spawn</em></p> @@ -310,7 +310,7 @@ $ make </code> <section> <title>BEAM Tracepoints</title> - <p>All tracepoints are in the domain of <c>com_ericsson_otp</c></p> + <p>All tracepoints are in the domain of <c>org_erlang_otp</c></p> <p>All Erlang types are the string equivalent in LTTng.</p> <p><em>scheduler_poll</em></p> @@ -550,8 +550,8 @@ Eshell V8.0 (abort with ^G) <p>Enable the process_register LTTng tracepoint for Erlang.</p> - <pre>$ lttng enable-event -u com_ericsson_dyntrace:process_register -UST event com_ericsson_dyntrace:process_register created in channel channel0</pre> + <pre>$ lttng enable-event -u org_erlang_dyntrace:process_register +UST event org_erlang_dyntrace:process_register created in channel channel0</pre> <p>Enable process tracing for new processes and use <c>dyntrace</c> as tracer backend.</p> @@ -573,23 +573,23 @@ Tracing started for session erlang-demo</pre> <pre>$ lttng stop Tracing stopped for session erlang-demo $ lttng view -[17:20:42.561168759] (+?.?????????) elxd1168lx9 com_ericsson_dyntrace:process_register: \ +[17:20:42.561168759] (+?.?????????) elxd1168lx9 org_erlang_dyntrace:process_register: \ { cpu_id = 5 }, { pid = "<0.66.0>", name = "sasl_sup", type = "register" } -[17:20:42.561215519] (+0.000046760) elxd1168lx9 com_ericsson_dyntrace:process_register: \ +[17:20:42.561215519] (+0.000046760) elxd1168lx9 org_erlang_dyntrace:process_register: \ { cpu_id = 5 }, { pid = "<0.67.0>", name = "sasl_safe_sup", type = "register" } -[17:20:42.562149024] (+0.000933505) elxd1168lx9 com_ericsson_dyntrace:process_register: \ +[17:20:42.562149024] (+0.000933505) elxd1168lx9 org_erlang_dyntrace:process_register: \ { cpu_id = 5 }, { pid = "<0.68.0>", name = "alarm_handler", type = "register" } -[17:20:42.571035803] (+0.008886779) elxd1168lx9 com_ericsson_dyntrace:process_register: \ +[17:20:42.571035803] (+0.008886779) elxd1168lx9 org_erlang_dyntrace:process_register: \ { cpu_id = 5 }, { pid = "<0.69.0>", name = "release_handler", type = "register" } -[17:20:42.574939868] (+0.003904065) elxd1168lx9 com_ericsson_dyntrace:process_register: \ +[17:20:42.574939868] (+0.003904065) elxd1168lx9 org_erlang_dyntrace:process_register: \ { cpu_id = 5 }, { pid = "<0.74.0>", name = "os_mon_sup", type = "register" } -[17:20:42.576818712] (+0.001878844) elxd1168lx9 com_ericsson_dyntrace:process_register: \ +[17:20:42.576818712] (+0.001878844) elxd1168lx9 org_erlang_dyntrace:process_register: \ { cpu_id = 5 }, { pid = "<0.75.0>", name = "disksup", type = "register" } -[17:20:42.580032013] (+0.003213301) elxd1168lx9 com_ericsson_dyntrace:process_register: \ +[17:20:42.580032013] (+0.003213301) elxd1168lx9 org_erlang_dyntrace:process_register: \ { cpu_id = 5 }, { pid = "<0.76.0>", name = "memsup", type = "register" } -[17:20:42.583046339] (+0.003014326) elxd1168lx9 com_ericsson_dyntrace:process_register: \ +[17:20:42.583046339] (+0.003014326) elxd1168lx9 org_erlang_dyntrace:process_register: \ { cpu_id = 5 }, { pid = "<0.78.0>", name = "cpu_sup", type = "register" } -[17:20:42.586206242] (+0.003159903) elxd1168lx9 com_ericsson_dyntrace:process_register: \ +[17:20:42.586206242] (+0.003159903) elxd1168lx9 org_erlang_dyntrace:process_register: \ { cpu_id = 5 }, { pid = "<0.82.0>", name = "timer_server", type = "register" }</pre> </section> </chapter> diff --git a/lib/runtime_tools/test/dyntrace_lttng_SUITE.erl b/lib/runtime_tools/test/dyntrace_lttng_SUITE.erl index 07707c6a12..a98ac6e99c 100644 --- a/lib/runtime_tools/test/dyntrace_lttng_SUITE.erl +++ b/lib/runtime_tools/test/dyntrace_lttng_SUITE.erl @@ -84,25 +84,25 @@ end_per_testcase(Case, _Config) -> %% tracepoints %% -%% com_ericsson_dyntrace:gc_major_end -%% com_ericsson_dyntrace:gc_major_start -%% com_ericsson_dyntrace:gc_minor_end -%% com_ericsson_dyntrace:gc_minor_start -%% com_ericsson_dyntrace:message_receive -%% com_ericsson_dyntrace:message_send -%% -com_ericsson_dyntrace:message_queued -%% com_ericsson_dyntrace:function_exception -%% com_ericsson_dyntrace:function_return -%% com_ericsson_dyntrace:function_call -%% com_ericsson_dyntrace:port_link -%% com_ericsson_dyntrace:port_exit -%% com_ericsson_dyntrace:port_open -%% com_ericsson_dyntrace:port_scheduled -%% com_ericsson_dyntrace:process_scheduled -%% com_ericsson_dyntrace:process_register -%% com_ericsson_dyntrace:process_exit -%% com_ericsson_dyntrace:process_link -%% com_ericsson_dyntrace:process_spawn +%% org_erlang_dyntrace:gc_major_end +%% org_erlang_dyntrace:gc_major_start +%% org_erlang_dyntrace:gc_minor_end +%% org_erlang_dyntrace:gc_minor_start +%% org_erlang_dyntrace:message_receive +%% org_erlang_dyntrace:message_send +%% -org_erlang_dyntrace:message_queued +%% org_erlang_dyntrace:function_exception +%% org_erlang_dyntrace:function_return +%% org_erlang_dyntrace:function_call +%% org_erlang_dyntrace:port_link +%% org_erlang_dyntrace:port_exit +%% org_erlang_dyntrace:port_open +%% org_erlang_dyntrace:port_scheduled +%% org_erlang_dyntrace:process_scheduled +%% org_erlang_dyntrace:process_register +%% org_erlang_dyntrace:process_exit +%% org_erlang_dyntrace:process_link +%% org_erlang_dyntrace:process_spawn %% %% Testcases %% @@ -112,7 +112,7 @@ t_lttng_list(_Config) -> ok. t_procs(Config) when is_list(Config) -> - ok = lttng_start_event("com_ericsson_dyntrace:process_*", Config), + ok = lttng_start_event("org_erlang_dyntrace:process_*", Config), _ = erlang:trace(new, true, [{tracer, dyntrace, []},procs]), Pid = spawn_link(fun() -> waiter() end), @@ -122,27 +122,27 @@ t_procs(Config) when is_list(Config) -> _ = erlang:trace(all, false, [procs]), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_dyntrace:process_spawn", Res), - ok = check_tracepoint("com_ericsson_dyntrace:process_link", Res), - ok = check_tracepoint("com_ericsson_dyntrace:process_exit", Res), - ok = check_tracepoint("com_ericsson_dyntrace:process_register", Res), + ok = check_tracepoint("org_erlang_dyntrace:process_spawn", Res), + ok = check_tracepoint("org_erlang_dyntrace:process_link", Res), + ok = check_tracepoint("org_erlang_dyntrace:process_exit", Res), + ok = check_tracepoint("org_erlang_dyntrace:process_register", Res), ok. t_ports(Config) when is_list(Config) -> - ok = lttng_start_event("com_ericsson_dyntrace:port_*", Config), + ok = lttng_start_event("org_erlang_dyntrace:port_*", Config), _ = erlang:trace(new, true, [{tracer, dyntrace, []},ports]), _ = os:cmd("ls"), _ = erlang:trace(all, false, [{tracer, dyntrace, []},ports]), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_dyntrace:port_open", Res), - ok = check_tracepoint("com_ericsson_dyntrace:port_link", Res), - ok = check_tracepoint("com_ericsson_dyntrace:port_exit", Res), + ok = check_tracepoint("org_erlang_dyntrace:port_open", Res), + ok = check_tracepoint("org_erlang_dyntrace:port_link", Res), + ok = check_tracepoint("org_erlang_dyntrace:port_exit", Res), ok. t_running_process(Config) when is_list(Config) -> - ok = lttng_start_event("com_ericsson_dyntrace:process_scheduled", Config), + ok = lttng_start_event("org_erlang_dyntrace:process_scheduled", Config), _ = erlang:trace(new, true, [{tracer, dyntrace, []},running]), Pid = spawn_link(fun() -> waiter() end), @@ -152,11 +152,11 @@ t_running_process(Config) when is_list(Config) -> _ = erlang:trace(all, false, [running]), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_dyntrace:process_scheduled", Res), + ok = check_tracepoint("org_erlang_dyntrace:process_scheduled", Res), ok. t_running_port(Config) when is_list(Config) -> - ok = lttng_start_event("com_ericsson_dyntrace:port_scheduled", Config), + ok = lttng_start_event("org_erlang_dyntrace:port_scheduled", Config), _ = erlang:trace(new, true, [{tracer, dyntrace, []},running_ports]), _ = os:cmd("ls"), @@ -164,12 +164,12 @@ t_running_port(Config) when is_list(Config) -> _ = erlang:trace(all, false, [running_ports]), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_dyntrace:port_scheduled", Res), + ok = check_tracepoint("org_erlang_dyntrace:port_scheduled", Res), ok. t_call(Config) when is_list(Config) -> - ok = lttng_start_event("com_ericsson_dyntrace:function_*", Config), + ok = lttng_start_event("org_erlang_dyntrace:function_*", Config), _ = erlang:trace(new, true, [{tracer, dyntrace, []}, call]), _ = erlang:trace_pattern({?MODULE, '_', '_'}, [{'_',[],[{exception_trace}]}], [local]), @@ -184,13 +184,13 @@ t_call(Config) when is_list(Config) -> _ = erlang:trace_pattern({?MODULE, '_', '_'}, false, [local]), _ = erlang:trace(all, false, [call]), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_dyntrace:function_call", Res), - ok = check_tracepoint("com_ericsson_dyntrace:function_return", Res), - ok = check_tracepoint("com_ericsson_dyntrace:function_exception", Res), + ok = check_tracepoint("org_erlang_dyntrace:function_call", Res), + ok = check_tracepoint("org_erlang_dyntrace:function_return", Res), + ok = check_tracepoint("org_erlang_dyntrace:function_exception", Res), ok. t_send(Config) when is_list(Config) -> - ok = lttng_start_event("com_ericsson_dyntrace:message_send", Config), + ok = lttng_start_event("org_erlang_dyntrace:message_send", Config), _ = erlang:trace(new, true, [{tracer, dyntrace, []},send]), Pid = spawn_link(fun() -> waiter() end), @@ -201,11 +201,11 @@ t_send(Config) when is_list(Config) -> _ = erlang:trace(all, false, [send]), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_dyntrace:message_send", Res), + ok = check_tracepoint("org_erlang_dyntrace:message_send", Res), ok. t_call_return_to(Config) when is_list(Config) -> - ok = lttng_start_event("com_ericsson_dyntrace:function_*", Config), + ok = lttng_start_event("org_erlang_dyntrace:function_*", Config), _ = erlang:trace(new, true, [{tracer, dyntrace, []}, call, return_to]), _ = erlang:trace_pattern({lists, '_', '_'}, true, [local]), _ = erlang:trace_pattern({?MODULE, '_', '_'}, true, [local]), @@ -219,11 +219,11 @@ t_call_return_to(Config) when is_list(Config) -> _ = erlang:trace_pattern({lists, '_', '_'}, false, [local]), _ = erlang:trace(all, false, [call,return_to]), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_dyntrace:function_call", Res), + ok = check_tracepoint("org_erlang_dyntrace:function_call", Res), ok. t_call_silent(Config) when is_list(Config) -> - ok = lttng_start_event("com_ericsson_dyntrace:function_*", Config), + ok = lttng_start_event("org_erlang_dyntrace:function_*", Config), _ = erlang:trace(new, true, [{tracer, dyntrace, []}, call, silent]), _ = erlang:trace_pattern({?MODULE, '_', '_'}, [{'_',[],[{exception_trace}]}], [local]), @@ -238,14 +238,14 @@ t_call_silent(Config) when is_list(Config) -> _ = erlang:trace_pattern({?MODULE, '_', '_'}, false, [local]), _ = erlang:trace(all, false, [call]), Res = lttng_stop_and_view(Config), - notfound = check_tracepoint("com_ericsson_dyntrace:function_call", Res), - notfound = check_tracepoint("com_ericsson_dyntrace:function_return", Res), - notfound = check_tracepoint("com_ericsson_dyntrace:function_exception", Res), + notfound = check_tracepoint("org_erlang_dyntrace:function_call", Res), + notfound = check_tracepoint("org_erlang_dyntrace:function_return", Res), + notfound = check_tracepoint("org_erlang_dyntrace:function_exception", Res), ok. t_receive(Config) when is_list(Config) -> - ok = lttng_start_event("com_ericsson_dyntrace:message_receive", Config), + ok = lttng_start_event("org_erlang_dyntrace:message_receive", Config), _ = erlang:trace(new, true, [{tracer, dyntrace, []},'receive']), timer:sleep(20), @@ -260,11 +260,11 @@ t_receive(Config) when is_list(Config) -> timer:sleep(10), _ = erlang:trace(all, false, ['receive']), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_dyntrace:message_receive", Res), + ok = check_tracepoint("org_erlang_dyntrace:message_receive", Res), ok. t_garbage_collection(Config) when is_list(Config) -> - ok = lttng_start_event("com_ericsson_dyntrace:gc_*", Config), + ok = lttng_start_event("org_erlang_dyntrace:gc_*", Config), _ = erlang:trace(new, true, [{tracer, dyntrace, []},garbage_collection]), Pid = spawn_link(fun() -> gcfier() end), @@ -273,14 +273,14 @@ t_garbage_collection(Config) when is_list(Config) -> timer:sleep(10), _ = erlang:trace(all, false, [garbage_collection]), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_dyntrace:gc_major_start", Res), - ok = check_tracepoint("com_ericsson_dyntrace:gc_major_end", Res), - ok = check_tracepoint("com_ericsson_dyntrace:gc_minor_start", Res), - ok = check_tracepoint("com_ericsson_dyntrace:gc_minor_end", Res), + ok = check_tracepoint("org_erlang_dyntrace:gc_major_start", Res), + ok = check_tracepoint("org_erlang_dyntrace:gc_major_end", Res), + ok = check_tracepoint("org_erlang_dyntrace:gc_minor_start", Res), + ok = check_tracepoint("org_erlang_dyntrace:gc_minor_end", Res), ok. t_all(Config) when is_list(Config) -> - ok = lttng_start_event("com_ericsson_dyntrace:*", Config), + ok = lttng_start_event("org_erlang_dyntrace:*", Config), _ = erlang:trace(new, true, [{tracer, dyntrace, []},all]), Pid1 = spawn_link(fun() -> waiter() end), @@ -297,21 +297,21 @@ t_all(Config) when is_list(Config) -> _ = erlang:trace(all, false, [all]), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_dyntrace:process_spawn", Res), - ok = check_tracepoint("com_ericsson_dyntrace:process_link", Res), - ok = check_tracepoint("com_ericsson_dyntrace:process_exit", Res), - ok = check_tracepoint("com_ericsson_dyntrace:process_register", Res), - ok = check_tracepoint("com_ericsson_dyntrace:port_open", Res), - ok = check_tracepoint("com_ericsson_dyntrace:port_link", Res), - ok = check_tracepoint("com_ericsson_dyntrace:port_exit", Res), - ok = check_tracepoint("com_ericsson_dyntrace:process_scheduled", Res), - ok = check_tracepoint("com_ericsson_dyntrace:port_scheduled", Res), - ok = check_tracepoint("com_ericsson_dyntrace:message_send", Res), - ok = check_tracepoint("com_ericsson_dyntrace:message_receive", Res), - ok = check_tracepoint("com_ericsson_dyntrace:gc_major_start", Res), - ok = check_tracepoint("com_ericsson_dyntrace:gc_major_end", Res), - ok = check_tracepoint("com_ericsson_dyntrace:gc_minor_start", Res), - ok = check_tracepoint("com_ericsson_dyntrace:gc_minor_end", Res), + ok = check_tracepoint("org_erlang_dyntrace:process_spawn", Res), + ok = check_tracepoint("org_erlang_dyntrace:process_link", Res), + ok = check_tracepoint("org_erlang_dyntrace:process_exit", Res), + ok = check_tracepoint("org_erlang_dyntrace:process_register", Res), + ok = check_tracepoint("org_erlang_dyntrace:port_open", Res), + ok = check_tracepoint("org_erlang_dyntrace:port_link", Res), + ok = check_tracepoint("org_erlang_dyntrace:port_exit", Res), + ok = check_tracepoint("org_erlang_dyntrace:process_scheduled", Res), + ok = check_tracepoint("org_erlang_dyntrace:port_scheduled", Res), + ok = check_tracepoint("org_erlang_dyntrace:message_send", Res), + ok = check_tracepoint("org_erlang_dyntrace:message_receive", Res), + ok = check_tracepoint("org_erlang_dyntrace:gc_major_start", Res), + ok = check_tracepoint("org_erlang_dyntrace:gc_major_end", Res), + ok = check_tracepoint("org_erlang_dyntrace:gc_minor_start", Res), + ok = check_tracepoint("org_erlang_dyntrace:gc_minor_end", Res), ok. diff --git a/lib/ssh/test/ssh_algorithms_SUITE.erl b/lib/ssh/test/ssh_algorithms_SUITE.erl index ed9e7aacaa..0f68130a05 100644 --- a/lib/ssh/test/ssh_algorithms_SUITE.erl +++ b/lib/ssh/test/ssh_algorithms_SUITE.erl @@ -24,6 +24,7 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("ssh/src/ssh_transport.hrl"). +-include("ssh_test_lib.hrl"). %% Note: This directive should only be used in test suites. -compile(export_all). @@ -70,32 +71,39 @@ two_way_tags() -> [cipher,mac,compression]. %%-------------------------------------------------------------------- init_per_suite(Config) -> - ct:log("~n" - "Environment:~n============~n" - "os:getenv(\"HOME\") = ~p~n" - "init:get_argument(home) = ~p~n~n~n" - "OS ssh:~n=======~n~p~n~n~n" - "Erl ssh:~n========~n~p~n~n~n" - "Installed ssh client:~n=====================~n~p~n~n~n" - "Installed ssh server:~n=====================~n~p~n~n~n" - "Misc values:~n============~n" - " -- Default dh group exchange parameters ({min,def,max}): ~p~n" - " -- dh_default_groups: ~p~n" - " -- Max num algorithms: ~p~n" - ,[os:getenv("HOME"), - init:get_argument(home), - os:cmd("ssh -V"), - ssh:default_algorithms(), - ssh_test_lib:default_algorithms(sshc), - ssh_test_lib:default_algorithms(sshd), - {?DEFAULT_DH_GROUP_MIN,?DEFAULT_DH_GROUP_NBITS,?DEFAULT_DH_GROUP_MAX}, - public_key:dh_gex_group_sizes(), - ?MAX_NUM_ALGORITHMS - ]), - ct:log("all() ->~n ~p.~n~ngroups()->~n ~p.~n",[all(),groups()]), - ssh:start(), - [{std_simple_sftp_size,25000} % Sftp transferred data size - | setup_pubkey(Config)]. + ?CHECK_CRYPTO( + begin + ct:log("~n" + "Environment:~n============~n" + "os:getenv(\"HOME\") = ~p~n" + "init:get_argument(home) = ~p~n~n~n" + "OS ssh:~n=======~n~p~n~n~n" + "Erl ssh:~n========~n~p~n~n~n" + "crypto:info_lib():~n========~n~p~n~n~n" + "Installed ssh client:~n=====================~n~p~n~n~n" + "Installed ssh server:~n=====================~n~p~n~n~n" + "Misc values:~n============~n" + " -- Default dh group exchange parameters ({min,def,max}): ~p~n" + " -- dh_default_groups: ~p~n" + " -- Max num algorithms: ~p~n" + ,[os:getenv("HOME"), + init:get_argument(home), + os:cmd("ssh -V"), + ssh:default_algorithms(), + crypto:info_lib(), + ssh_test_lib:default_algorithms(sshc), + ssh_test_lib:default_algorithms(sshd), + {?DEFAULT_DH_GROUP_MIN,?DEFAULT_DH_GROUP_NBITS,?DEFAULT_DH_GROUP_MAX}, + public_key:dh_gex_group_sizes(), + ?MAX_NUM_ALGORITHMS + ]), + ct:log("all() ->~n ~p.~n~ngroups()->~n ~p.~n",[all(),groups()]), + ssh:start(), + [{std_simple_sftp_size,25000} % Sftp transferred data size + | setup_pubkey(Config)] + end + ). + end_per_suite(_Config) -> ssh:stop(). diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl index 4991816850..733414e23a 100644 --- a/lib/ssh/test/ssh_basic_SUITE.erl +++ b/lib/ssh/test/ssh_basic_SUITE.erl @@ -25,6 +25,7 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("kernel/include/inet.hrl"). -include_lib("kernel/include/file.hrl"). +-include("ssh_test_lib.hrl"). %% Note: This directive should only be used in test suites. %%-compile(export_all). @@ -130,7 +131,7 @@ basic_tests() -> %%-------------------------------------------------------------------- init_per_suite(Config) -> - Config. + ?CHECK_CRYPTO(Config). end_per_suite(_Config) -> ssh:stop(). diff --git a/lib/ssh/test/ssh_connection_SUITE.erl b/lib/ssh/test/ssh_connection_SUITE.erl index a52633a269..bcf3b01824 100644 --- a/lib/ssh/test/ssh_connection_SUITE.erl +++ b/lib/ssh/test/ssh_connection_SUITE.erl @@ -43,6 +43,7 @@ suite() -> all() -> [ {group, openssh}, + small_interrupted_send, interrupted_send, start_shell, start_shell_exec, @@ -81,7 +82,7 @@ sock() -> %%-------------------------------------------------------------------- init_per_suite(Config) -> - Config. + ?CHECK_CRYPTO(Config). end_per_suite(Config) -> Config. @@ -124,7 +125,7 @@ simple_exec(Config) when is_list(Config) -> do_simple_exec(ConnectionRef). -simple_exec_sock(Config) -> +simple_exec_sock(_Config) -> {ok, Sock} = gen_tcp:connect("localhost", ?SSH_DEFAULT_PORT, [{active,false}]), {ok, ConnectionRef} = ssh:connect(Sock, [{silently_accept_hosts, true}, {user_interaction, false}]), @@ -361,44 +362,96 @@ ptty_alloc_pixel(Config) when is_list(Config) -> ssh:close(ConnectionRef). %%-------------------------------------------------------------------- +small_interrupted_send(Config) -> + K = 1024, + M = K*K, + do_interrupted_send(Config, 10*M, 4*K). interrupted_send(Config) -> + M = 1024*1024, + do_interrupted_send(Config, 10*M, 4*M). + +do_interrupted_send(Config, SendSize, EchoSize) -> PrivDir = proplists:get_value(priv_dir, Config), UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth file:make_dir(UserDir), SysDir = proplists:get_value(data_dir, Config), + EchoSS_spec = {ssh_echo_server, [EchoSize,[{dbg,true}]]}, {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir}, {user_dir, UserDir}, {password, "morot"}, - {subsystems, [{"echo_n", {ssh_echo_server, [4000000]}}]}]), - + {subsystems, [{"echo_n",EchoSS_spec}]}]), + + ct:log("connect", []), ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true}, {user, "foo"}, {password, "morot"}, {user_interaction, false}, {user_dir, UserDir}]), - - {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity), - - success = ssh_connection:subsystem(ConnectionRef, ChannelId, "echo_n", infinity), - - %% build 10MB binary - Data = << <<X:32>> || X <- lists:seq(1,2500000)>>, - - %% expect remote end to send us 4MB back - <<ExpectedData:4000000/binary, _/binary>> = Data, - - %% pre-adjust receive window so the other end doesn't block - ssh_connection:adjust_window(ConnectionRef, ChannelId, size(ExpectedData) + 1), - - case ssh_connection:send(ConnectionRef, ChannelId, Data, 10000) of - {error, closed} -> - ok; - Msg -> - ct:fail({expected,{error,closed}, got, Msg}) - end, - receive_data(ExpectedData, ConnectionRef, ChannelId), - ssh:close(ConnectionRef), - ssh:stop_daemon(Pid). + ct:log("connected", []), + + %% build big binary + Data = << <<X:32>> || X <- lists:seq(1,SendSize div 4)>>, + + %% expect remote end to send us EchoSize back + <<ExpectedData:EchoSize/binary, _/binary>> = Data, + + %% Spawn listener. Otherwise we could get a deadlock due to filled buffers + Parent = self(), + ResultPid = spawn( + fun() -> + ct:log("open channel",[]), + {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity), + ct:log("start subsystem", []), + case ssh_connection:subsystem(ConnectionRef, ChannelId, "echo_n", infinity) of + success -> + Parent ! {self(), channelId, ChannelId}, + + Result = + try collect_data(ConnectionRef, ChannelId) + of + ExpectedData -> + ok; + _ -> + {fail,"unexpected result"} + catch + Class:Exception -> + {fail, io_lib:format("Exception ~p:~p",[Class,Exception])} + end, + Parent ! {self(), Result}; + Other -> + Parent ! {self(), channelId, error, Other} + end + end), + + receive + {ResultPid, channelId, ChannelId} -> + %% pre-adjust receive window so the other end doesn't block + ct:log("adjust window", []), + ssh_connection:adjust_window(ConnectionRef, ChannelId, size(ExpectedData) + 1), + + ct:log("going to send ~p bytes", [size(Data)]), + case ssh_connection:send(ConnectionRef, ChannelId, Data, 30000) of + {error, closed} -> + ct:log("{error,closed} - That's what we expect :)", []), + ok; + Msg -> + ct:log("Got ~p - that's bad, very bad indeed",[Msg]), + ct:fail({expected,{error,closed}, got, Msg}) + end, + ct:log("going to check the result (if it is available)", []), + receive + {ResultPid, Result} -> + ct:log("Got result: ~p", [Result]), + ssh:close(ConnectionRef), + ssh:stop_daemon(Pid), + Result + end; + + {ResultPid, channelId, error, Other} -> + ssh:close(ConnectionRef), + ssh:stop_daemon(Pid), + {fail, io_lib:format("ssh_connection:subsystem: ~p",[Other])} + end. %%-------------------------------------------------------------------- start_shell() -> @@ -549,10 +602,10 @@ start_shell_sock_daemon_exec(Config) -> spawn_link(fun() -> {ok,Ss} = gen_tcp:connect("localhost", Port, [{active,false}]), - {ok, Pid} = ssh:daemon(Ss, [{system_dir, SysDir}, - {user_dir, UserDir}, - {password, "morot"}, - {exec, fun ssh_exec/1}]) + {ok, _Pid} = ssh:daemon(Ss, [{system_dir, SysDir}, + {user_dir, UserDir}, + {password, "morot"}, + {exec, fun ssh_exec/1}]) end), {ok,Sc} = gen_tcp:accept(Sl), {ok,ConnectionRef} = ssh:connect(Sc, [{silently_accept_hosts, true}, @@ -856,20 +909,36 @@ big_cat_rx(ConnectionRef, ChannelId, Acc) -> timeout end. -receive_data(ExpectedData, ConnectionRef, ChannelId) -> - ExpectedData = collect_data(ConnectionRef, ChannelId). - collect_data(ConnectionRef, ChannelId) -> - collect_data(ConnectionRef, ChannelId, []). + ct:log("Listener ~p running! ConnectionRef=~p, ChannelId=~p",[self(),ConnectionRef,ChannelId]), + collect_data(ConnectionRef, ChannelId, [], 0). -collect_data(ConnectionRef, ChannelId, Acc) -> +collect_data(ConnectionRef, ChannelId, Acc, Sum) -> + TO = 5000, receive - {ssh_cm, ConnectionRef, {data, ChannelId, 0, Data}} -> - collect_data(ConnectionRef, ChannelId, [Data | Acc]); + {ssh_cm, ConnectionRef, {data, ChannelId, 0, Data}} when is_binary(Data) -> + ct:log("collect_data: received ~p bytes. total ~p bytes",[size(Data),Sum+size(Data)]), + collect_data(ConnectionRef, ChannelId, [Data | Acc], Sum+size(Data)); {ssh_cm, ConnectionRef, {eof, ChannelId}} -> - iolist_to_binary(lists:reverse(Acc)) - after 5000 -> - timeout + try + iolist_to_binary(lists:reverse(Acc)) + of + Bin -> + ct:log("collect_data: received eof.~nGot in total ~p bytes",[size(Bin)]), + Bin + catch + C:E -> + ct:log("collect_data: received eof.~nAcc is strange...~nException=~p:~p~nAcc=~p", + [C,E,Acc]), + {error,{C,E}} + end; + Msg -> + ct:log("collect_data: ***** unexpected message *****~n~p",[Msg]), + collect_data(ConnectionRef, ChannelId, Acc, Sum) + + after TO -> + ct:log("collect_data: ----- Nothing received for ~p seconds -----~n",[]), + collect_data(ConnectionRef, ChannelId, Acc, Sum) end. %%%------------------------------------------------------------------- diff --git a/lib/ssh/test/ssh_echo_server.erl b/lib/ssh/test/ssh_echo_server.erl index ed9bbe1b67..5387d21efd 100644 --- a/lib/ssh/test/ssh_echo_server.erl +++ b/lib/ssh/test/ssh_echo_server.erl @@ -26,15 +26,29 @@ -record(state, { n, id, - cm + cm, + dbg = false }). -export([init/1, handle_msg/2, handle_ssh_msg/2, terminate/2]). +-define(DBG(State,Fmt,Args), + case State#state.dbg of + true -> ct:log("~p:~p ~p "++Fmt, [?MODULE,?LINE,self()|Args]); + false -> ok + end). + + init([N]) -> - ct:pal("Echo server: ~p",[self()]), - {ok, #state{n = N}}. + {ok, #state{n = N}}; +init([N,Opts]) -> + State = #state{n = N, + dbg = proplists:get_value(dbg,Opts,false) + }, + ?DBG(State, "init([~p])",[N]), + {ok, State}. handle_msg({ssh_channel_up, ChannelId, ConnectionManager}, State) -> + ?DBG(State, "ssh_channel_up Cid=~p ConnMngr=~p",[ChannelId,ConnectionManager]), {ok, State#state{id = ChannelId, cm = ConnectionManager}}. @@ -42,32 +56,39 @@ handle_ssh_msg({ssh_cm, CM, {data, ChannelId, 0, Data}}, #state{n = N} = State) M = N - size(Data), case M > 0 of true -> + ?DBG(State, "ssh_cm data Cid=~p size(Data)=~p M=~p",[ChannelId,size(Data),M]), ssh_connection:send(CM, ChannelId, Data), {ok, State#state{n = M}}; false -> <<SendData:N/binary, _/binary>> = Data, + ?DBG(State, "ssh_cm data Cid=~p size(Data)=~p M=~p size(SendData)=~p~nSend eof",[ChannelId,size(Data),M,size(SendData)]), ssh_connection:send(CM, ChannelId, SendData), ssh_connection:send_eof(CM, ChannelId), {stop, ChannelId, State} end; handle_ssh_msg({ssh_cm, _ConnectionManager, {data, _ChannelId, 1, Data}}, State) -> + ?DBG(State, "stderr: ~p",[Data]), error_logger:format(standard_error, " ~p~n", [binary_to_list(Data)]), {ok, State}; handle_ssh_msg({ssh_cm, _ConnectionManager, {eof, _ChannelId}}, State) -> + ?DBG(State, "{eof ~p}",[_ChannelId]), {ok, State}; -handle_ssh_msg({ssh_cm, _, {signal, _, _}}, State) -> +handle_ssh_msg({ssh_cm, _, _Sig={signal, _, _}}, State) -> %% Ignore signals according to RFC 4254 section 6.9. + ?DBG(State, "~p",[_Sig]), {ok, State}; -handle_ssh_msg({ssh_cm, _, {exit_signal, ChannelId, _, _Error, _}}, - State) -> +handle_ssh_msg({ssh_cm, _, _Sig={exit_signal, ChannelId, _, _Error, _}}, State) -> + ?DBG(State, "~p",[_Sig]), {stop, ChannelId, State}; -handle_ssh_msg({ssh_cm, _, {exit_status, ChannelId, _Status}}, State) -> +handle_ssh_msg({ssh_cm, _, _Sig={exit_status, ChannelId, _Status}}, State) -> + ?DBG(State, "~p",[_Sig]), {stop, ChannelId, State}. terminate(_Reason, _State) -> + ?DBG(_State, "terminate ~p",[_Reason]), ok. diff --git a/lib/ssh/test/ssh_options_SUITE.erl b/lib/ssh/test/ssh_options_SUITE.erl index d1e3d6cb0e..61883c0647 100644 --- a/lib/ssh/test/ssh_options_SUITE.erl +++ b/lib/ssh/test/ssh_options_SUITE.erl @@ -27,7 +27,7 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("kernel/include/file.hrl"). - +-include("ssh_test_lib.hrl"). %%% Test cases -export([connectfun_disconnectfun_client/1, @@ -126,7 +126,7 @@ groups() -> %%-------------------------------------------------------------------- init_per_suite(Config) -> - Config. + ?CHECK_CRYPTO(Config). end_per_suite(_Config) -> ssh:stop(). diff --git a/lib/ssh/test/ssh_protocol_SUITE.erl b/lib/ssh/test/ssh_protocol_SUITE.erl index 41faf951e1..4fac1f718a 100644 --- a/lib/ssh/test/ssh_protocol_SUITE.erl +++ b/lib/ssh/test/ssh_protocol_SUITE.erl @@ -26,6 +26,7 @@ -include_lib("ssh/src/ssh.hrl"). % ?UINT32, ?BYTE, #ssh{} ... -include_lib("ssh/src/ssh_transport.hrl"). -include_lib("ssh/src/ssh_auth.hrl"). +-include("ssh_test_lib.hrl"). %% Note: This directive should only be used in test suites. -compile(export_all). @@ -87,7 +88,7 @@ groups() -> init_per_suite(Config) -> - start_std_daemon( setup_dirs( start_apps(Config))). + ?CHECK_CRYPTO(start_std_daemon( setup_dirs( start_apps(Config)))). end_per_suite(Config) -> stop_apps(Config). diff --git a/lib/ssh/test/ssh_renegotiate_SUITE.erl b/lib/ssh/test/ssh_renegotiate_SUITE.erl index 300816276a..b10ec3707f 100644 --- a/lib/ssh/test/ssh_renegotiate_SUITE.erl +++ b/lib/ssh/test/ssh_renegotiate_SUITE.erl @@ -21,6 +21,7 @@ -module(ssh_renegotiate_SUITE). -include_lib("common_test/include/ct.hrl"). +-include("ssh_test_lib.hrl"). %% Note: This directive should only be used in test suites. -compile(export_all). @@ -45,7 +46,7 @@ tests() -> [rekey, rekey_limit, renegotiate1, renegotiate2]. %%-------------------------------------------------------------------- init_per_suite(Config) -> - Config. + ?CHECK_CRYPTO(Config). end_per_suite(_Config) -> ssh:stop(). diff --git a/lib/ssh/test/ssh_sftp_SUITE.erl b/lib/ssh/test/ssh_sftp_SUITE.erl index 19cf6d446e..19ad81e7da 100644 --- a/lib/ssh/test/ssh_sftp_SUITE.erl +++ b/lib/ssh/test/ssh_sftp_SUITE.erl @@ -26,7 +26,7 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("kernel/include/file.hrl"). - +-include("ssh_test_lib.hrl"). % Default timetrap timeout -define(default_timeout, ?t:minutes(1)). @@ -45,10 +45,13 @@ all() -> init_per_suite(Config) -> - ct:log("file:native_name_encoding() = ~p,~nio:getopts() = ~p", - [file:native_name_encoding(),io:getopts()]), - ssh:start(), - Config. + ?CHECK_CRYPTO( + begin + ct:log("file:native_name_encoding() = ~p,~nio:getopts() = ~p", + [file:native_name_encoding(),io:getopts()]), + ssh:start(), + Config + end). end_per_suite(_onfig) -> ssh:stop(). diff --git a/lib/ssh/test/ssh_sftpd_SUITE.erl b/lib/ssh/test/ssh_sftpd_SUITE.erl index 4a69fd36b3..52a26110c4 100644 --- a/lib/ssh/test/ssh_sftpd_SUITE.erl +++ b/lib/ssh/test/ssh_sftpd_SUITE.erl @@ -28,6 +28,7 @@ -include_lib("kernel/include/file.hrl"). -include("ssh_xfer.hrl"). -include("ssh.hrl"). +-include("ssh_test_lib.hrl"). -define(USER, "Alladin"). -define(PASSWD, "Sesame"). @@ -72,14 +73,17 @@ groups() -> %%-------------------------------------------------------------------- init_per_suite(Config) -> - DataDir = proplists:get_value(data_dir, Config), - PrivDir = proplists:get_value(priv_dir, Config), - ssh_test_lib:setup_dsa(DataDir, PrivDir), - %% to make sure we don't use public-key-auth - %% this should be tested by other test suites - UserDir = filename:join(proplists:get_value(priv_dir, Config), nopubkey), - file:make_dir(UserDir), - Config. + ?CHECK_CRYPTO( + begin + DataDir = proplists:get_value(data_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), + ssh_test_lib:setup_dsa(DataDir, PrivDir), + %% to make sure we don't use public-key-auth + %% this should be tested by other test suites + UserDir = filename:join(proplists:get_value(priv_dir, Config), nopubkey), + file:make_dir(UserDir), + Config + end). end_per_suite(Config) -> SysDir = proplists:get_value(priv_dir, Config), diff --git a/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl b/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl index 75b5090c2b..56a33d6349 100644 --- a/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl +++ b/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl @@ -26,6 +26,7 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("kernel/include/file.hrl"). +-include("ssh_test_lib.hrl"). -define(USER, "Alladin"). -define(PASSWD, "Sesame"). @@ -53,17 +54,20 @@ groups() -> %%-------------------------------------------------------------------- init_per_suite(Config) -> - catch ssh:stop(), - DataDir = proplists:get_value(data_dir, Config), - PrivDir = proplists:get_value(priv_dir, Config), - FileAlt = filename:join(DataDir, "ssh_sftpd_file_alt.erl"), - c:c(FileAlt), - FileName = filename:join(DataDir, "test.txt"), - {ok, FileInfo} = file:read_file_info(FileName), - ok = file:write_file_info(FileName, - FileInfo#file_info{mode = 8#400}), - ssh_test_lib:setup_dsa(DataDir, PrivDir), - Config. + ?CHECK_CRYPTO( + begin + catch ssh:stop(), + DataDir = proplists:get_value(data_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), + FileAlt = filename:join(DataDir, "ssh_sftpd_file_alt.erl"), + c:c(FileAlt), + FileName = filename:join(DataDir, "test.txt"), + {ok, FileInfo} = file:read_file_info(FileName), + ok = file:write_file_info(FileName, + FileInfo#file_info{mode = 8#400}), + ssh_test_lib:setup_dsa(DataDir, PrivDir), + Config + end). end_per_suite(Config) -> UserDir = filename:join(proplists:get_value(priv_dir, Config), nopubkey), diff --git a/lib/ssh/test/ssh_sup_SUITE.erl b/lib/ssh/test/ssh_sup_SUITE.erl index 574564f6e9..ff53e1c4c6 100644 --- a/lib/ssh/test/ssh_sup_SUITE.erl +++ b/lib/ssh/test/ssh_sup_SUITE.erl @@ -53,11 +53,14 @@ end_per_group(_GroupName, Config) -> Config. init_per_suite(Config) -> - Port = ssh_test_lib:inet_port(node()), - PrivDir = proplists:get_value(priv_dir, Config), - UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth - file:make_dir(UserDir), - [{userdir, UserDir},{port, Port}, {host, "localhost"}, {host_ip, any} | Config]. + ?CHECK_CRYPTO( + begin + Port = ssh_test_lib:inet_port(node()), + PrivDir = proplists:get_value(priv_dir, Config), + UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth + file:make_dir(UserDir), + [{userdir, UserDir},{port, Port}, {host, "localhost"}, {host_ip, any} | Config] + end). end_per_suite(_) -> ok. diff --git a/lib/ssh/test/ssh_test_lib.hrl b/lib/ssh/test/ssh_test_lib.hrl index 7cb7edeaa8..54c93b7e87 100644 --- a/lib/ssh/test/ssh_test_lib.hrl +++ b/lib/ssh/test/ssh_test_lib.hrl @@ -1,4 +1,14 @@ %%------------------------------------------------------------------------- +%% Check for usable crypt +%%------------------------------------------------------------------------- +-define(CHECK_CRYPTO(Available), + try crypto:start() + of _ -> Available + catch _:_ -> {skip, "Can't start crypto"} + end + ). + +%%------------------------------------------------------------------------- %% Help macro %%------------------------------------------------------------------------- -define(wait_match(Pattern, FunctionCall, Bind, Timeout, Ntries), diff --git a/lib/ssh/test/ssh_to_openssh_SUITE.erl b/lib/ssh/test/ssh_to_openssh_SUITE.erl index f96a2cc62b..a914938c41 100644 --- a/lib/ssh/test/ssh_to_openssh_SUITE.erl +++ b/lib/ssh/test/ssh_to_openssh_SUITE.erl @@ -22,6 +22,7 @@ -module(ssh_to_openssh_SUITE). -include_lib("common_test/include/ct.hrl"). +-include("ssh_test_lib.hrl"). %% Note: This directive should only be used in test suites. -compile(export_all). @@ -62,12 +63,14 @@ groups() -> ]. init_per_suite(Config) -> - case gen_tcp:connect("localhost", 22, []) of - {error,econnrefused} -> - {skip,"No openssh deamon"}; - _ -> - ssh_test_lib:openssh_sanity_check(Config) - end. + ?CHECK_CRYPTO( + case gen_tcp:connect("localhost", 22, []) of + {error,econnrefused} -> + {skip,"No openssh deamon"}; + _ -> + ssh_test_lib:openssh_sanity_check(Config) + end + ). end_per_suite(_Config) -> ok. diff --git a/lib/ssh/test/ssh_upgrade_SUITE.erl b/lib/ssh/test/ssh_upgrade_SUITE.erl index 9d9b2b78fb..b5b27c369a 100644 --- a/lib/ssh/test/ssh_upgrade_SUITE.erl +++ b/lib/ssh/test/ssh_upgrade_SUITE.erl @@ -23,6 +23,7 @@ -compile(export_all). -include_lib("common_test/include/ct.hrl"). +-include("ssh_test_lib.hrl"). -record(state, { config, @@ -48,13 +49,15 @@ all() -> ]. init_per_suite(Config0) -> - case ct_release_test:init(Config0) of - {skip, Reason} -> - {skip, Reason}; - Config -> - ssh:start(), - Config - end. + ?CHECK_CRYPTO( + case ct_release_test:init(Config0) of + {skip, Reason} -> + {skip, Reason}; + Config -> + ssh:start(), + Config + end + ). end_per_suite(Config) -> ct_release_test:cleanup(Config), diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl index 887ba70859..85b2816451 100644 --- a/lib/stdlib/src/erl_parse.yrl +++ b/lib/stdlib/src/erl_parse.yrl @@ -170,9 +170,6 @@ fun_type -> '(' top_types ')' '->' top_type : {type, ?anno('$1'), 'fun', [{type, ?anno('$1'), product, '$2'},'$5']}. -map_pair_types -> '...' : [{type, ?anno('$1'), map_field_assoc, - [{type, ?anno('$1'), any, []}, - {type, ?anno('$1'), any, []}]}]. map_pair_types -> map_pair_type : ['$1']. map_pair_types -> map_pair_type ',' map_pair_types : ['$1'|'$3']. diff --git a/lib/stdlib/src/erl_pp.erl b/lib/stdlib/src/erl_pp.erl index 71a3835b66..016962f538 100644 --- a/lib/stdlib/src/erl_pp.erl +++ b/lib/stdlib/src/erl_pp.erl @@ -344,27 +344,9 @@ binary_type(I1, I2) -> map_type(Fs) -> {first,[$#],map_pair_types(Fs)}. -map_pair_types(Fs0) -> - Fs = replace_any_map(Fs0), +map_pair_types(Fs) -> tuple_type(Fs, fun map_pair_type/2). -replace_any_map([{type,Line,map_field_assoc,[KType,VType]}]=Fs) -> - IsAny = fun({type,_,any,[]}) -> true; - %% ({var,_,'_'}) -> true; - (_) -> false - end, - case IsAny(KType) andalso IsAny(VType) of - true -> - [{type,Line,map_field_assoc,any}]; - false -> - Fs - end; -replace_any_map([F|Fs]) -> - [F|replace_any_map(Fs)]; -replace_any_map([]) -> []. - -map_pair_type({type,_Line,map_field_assoc,any}, _Prec) -> - leaf("..."); map_pair_type({type,_Line,map_field_assoc,[KType,VType]}, Prec) -> {list,[{cstep,[ltype(KType, Prec),leaf(" =>")],ltype(VType, Prec)}]}; map_pair_type({type,_Line,map_field_exact,[KType,VType]}, Prec) -> diff --git a/lib/stdlib/test/erl_pp_SUITE.erl b/lib/stdlib/test/erl_pp_SUITE.erl index a48ba7b5b7..951a17d3eb 100644 --- a/lib/stdlib/test/erl_pp_SUITE.erl +++ b/lib/stdlib/test/erl_pp_SUITE.erl @@ -1112,15 +1112,14 @@ pr_1014(Config) -> ok = pp_forms(<<"-type t() :: #{any() => _}. ">>), ok = pp_forms(<<"-type t() :: #{_ => any()}. ">>), ok = pp_forms(<<"-type t() :: #{any() => any()}. ">>), - ok = pp_forms(<<"-type t() :: #{...}. ">>), - ok = pp_forms(<<"-type t() :: #{atom() := integer(), ...}. ">>), + ok = pp_forms(<<"-type t() :: #{atom() := integer(), any() => any()}. ">>), FileName = filename('pr_1014.erl', Config), C = <<"-module pr_1014.\n" "-compile export_all.\n" "-type m() :: #{..., a := integer()}.\n">>, ok = file:write_file(FileName, C), - {error,[{_,[{3,erl_parse,["syntax error before: ","','"]}]}],_} = + {error,[{_,[{3,erl_parse,["syntax error before: ","'...'"]}]}],_} = compile:file(FileName, [return]), ok. diff --git a/lib/syntax_tools/src/erl_prettypr.erl b/lib/syntax_tools/src/erl_prettypr.erl index 119d375746..f1615b2610 100644 --- a/lib/syntax_tools/src/erl_prettypr.erl +++ b/lib/syntax_tools/src/erl_prettypr.erl @@ -1130,13 +1130,14 @@ lay_2(Node, Ctxt) -> any_size -> text("map()"); Fs -> - {Prec, _PrecR} = type_preop_prec('#'), - Es = lay_map_fields(Fs, - floating(text(",")), - reset_prec(Ctxt)), + Ctxt1 = reset_prec(Ctxt), + Es = seq(Fs, + floating(text(",")), Ctxt1, + fun lay/2), D = beside(floating(text("#{")), beside(par(Es), floating(text("}")))), + {Prec, _PrecR} = type_preop_prec('#'), maybe_parentheses(D, Prec, Ctxt) end; @@ -1400,36 +1401,6 @@ lay_error_info(T, Ctxt) -> lay_concrete(T, Ctxt) -> lay(erl_syntax:abstract(T), Ctxt). -lay_map_fields([H | T], Separator, Ctxt) -> - case T of - [] -> - [case erl_syntax:type(H) of - map_type_assoc -> - lay_last_type_assoc(H, Ctxt); - _ -> - lay(H, Ctxt) - end]; - _ -> - [maybe_append(Separator, lay(H, Ctxt)) - | lay_map_fields(T, Separator, Ctxt)] - end; -lay_map_fields([], _, _) -> - [empty()]. - -lay_last_type_assoc(Node, Ctxt) -> - Name = erl_syntax:map_type_assoc_name(Node), - Value = erl_syntax:map_type_assoc_value(Node), - IsAny = fun({type,_,any,[]}) -> true; - %% ({var,_,'_'}) -> true; - (_) -> false - end, - case IsAny(Name) andalso IsAny(Value) of - true -> - text("..."); - false -> - lay_type_assoc(Name, Value, Ctxt) - end. - lay_type_assoc(Name, Value, Ctxt) -> Ctxt1 = reset_prec(Ctxt), D1 = lay(Name, Ctxt1), diff --git a/lib/syntax_tools/test/syntax_tools_SUITE_data/type_specs.erl b/lib/syntax_tools/test/syntax_tools_SUITE_data/type_specs.erl index 5621d3a293..e4f8a1c3de 100644 --- a/lib/syntax_tools/test/syntax_tools_SUITE_data/type_specs.erl +++ b/lib/syntax_tools/test/syntax_tools_SUITE_data/type_specs.erl @@ -49,9 +49,12 @@ -type m1() :: #{} | map(). -type m2() :: #{a := m1(), b => #{} | fy:m2()}. --type m3() :: #{...}. --type m4() :: #{_ => _, ...}. --type m5() :: #{any() => any(), ...}. % Currently printed as `#{..., ...}'. +%-type m3() :: #{...}. +%-type m4() :: #{_ => _, ...}. +%-type m5() :: #{any() => any(), ...}. +-type m3() :: #{any() => any()}. +-type m4() :: #{_ => _, any() => any()}. +-type m5() :: #{any() => any(), any() => any()}. -type b1() :: B1 :: binary() | (BitString :: bitstring()). -define(PAIR(A, B), {(A), (B)}). diff --git a/lib/tools/src/xref_base.erl b/lib/tools/src/xref_base.erl index 4322943c59..bb9815f9b0 100644 --- a/lib/tools/src/xref_base.erl +++ b/lib/tools/src/xref_base.erl @@ -669,16 +669,45 @@ do_add_directory(Dir, AppName, Bui, Rec, Ver, War, State) -> warnings(War, unreadable, Unreadable), case Errors of [] -> - do_add_modules(FileNames, AppName, Bui, Ver, War, State, []); + do_add_modules(FileNames, AppName, Bui, Ver, War, State); [Error | _] -> throw(Error) end. -do_add_modules([], _AppName, _OB, _OV, _OW, State, Modules) -> +do_add_modules(Files, AppName, OB, OV, OW, State0) -> + NFiles = length(Files), + Reader = fun(SplitName, State) -> + _Pid = read_module(SplitName, AppName, OB, OV, OW, State) + end, + N = parallelism(), + Files1 = start_readers(Files, Reader, State0, N), + %% Increase the number of readers towards the end to decrease the + %% waiting time for the collecting process: + Nx = N, + add_mods(Files1, Reader, State0, [], NFiles, Nx). + +add_mods(_, _ReaderFun, State, Modules, 0, _Nx) -> {ok, sort(Modules), State}; -do_add_modules([File | Files], AppName, OB, OV, OW, State, Modules) -> - {ok, M, NewState} = do_add_module(File, AppName, OB, OV, OW, State), - do_add_modules(Files, AppName, OB, OV, OW, NewState, M ++ Modules). +add_mods(Files, ReaderFun, State, Modules, N, Nx) -> + {I, Nx1} = case Nx > 0 of + false -> {1, Nx}; + true -> {2, Nx - 1} + end, + Files1 = start_readers(Files, ReaderFun, State, I), + {ok, M, NewState} = process_module(State), + add_mods(Files1, ReaderFun, NewState, M ++ Modules, N - 1, Nx1). + +start_readers([SplitName|Files], ReaderFun, State, N) when N > 0 -> + _Pid = ReaderFun(SplitName, State), + start_readers(Files, ReaderFun, State, N - 1); +start_readers(Files, _ReaderFun, _State, _) -> + Files. + +parallelism() -> + case erlang:system_info(multi_scheduling) of + enabled -> erlang:system_info(schedulers_online); + _ -> 1 + end. %% -> {ok, Module, State} | throw(Error) do_add_a_module(File, AppName, Builtins, Verbose, Warnings, State) -> @@ -692,50 +721,75 @@ do_add_a_module(File, AppName, Builtins, Verbose, Warnings, State) -> %% -> {ok, Module, State} | throw(Error) %% Options: verbose, warnings, builtins -do_add_module({Dir, Basename}, AppName, Builtins, Verbose, Warnings, State) -> - File = filename:join(Dir, Basename), - {ok, M, Bad, NewState} = - do_add_module1(Dir, File, AppName, Builtins, Verbose, Warnings, State), - _ = filter(fun({Tag,B}) -> warnings(Warnings, Tag, [[File,B]]) end, Bad), - {ok, M, NewState}. - -do_add_module1(Dir, File, AppName, Builtins, Verbose, Warnings, State) -> - message(Verbose, reading_beam, [File]), - Mode = State#xref.mode, +do_add_module(SplitName, AppName, Builtins, Verbose, Warnings, State) -> + _Pid = read_module(SplitName, AppName, Builtins, Verbose, Warnings, State), + process_module(State). + +read_module(SplitName, AppName, Builtins, Verbose, Warnings, State) -> Me = self(), - Fun = fun() -> Me ! {self(), abst(File, Builtins, Mode)} end, - case xref_utils:subprocess(Fun, [link, {min_heap_size,100000}]) of + #xref{mode = Mode} = State, + Fun = + fun() -> + Me ! {?MODULE, + read_a_module(SplitName, AppName, Builtins, Verbose, + Warnings, Mode)} + end, + spawn_opt(Fun, [link, {min_heap_size, 1000000}, {priority, high}]). + +read_a_module({Dir, BaseName}, AppName, Builtins, Verbose, Warnings, Mode) -> + File = filename:join(Dir, BaseName), + case abst(File, Builtins, Mode) of {ok, _M, no_abstract_code} when Verbose -> - message(Verbose, skipped_beam, []), - {ok, [], [], State}; + message(Verbose, no_debug_info, [File]), + no; {ok, _M, no_abstract_code} when not Verbose -> message(Warnings, no_debug_info, [File]), - {ok, [], [], State}; + no; {ok, M, Data, UnresCalls0} -> - %% Remove duplicates. Identical unresolved calls on the - %% same line are counted as _one_ unresolved call. - UnresCalls = usort(UnresCalls0), - message(Verbose, done, []), - NoUnresCalls = length(UnresCalls), - case NoUnresCalls of - 0 -> ok; - 1 -> warnings(Warnings, unresolved_summary1, [[M]]); - N -> warnings(Warnings, unresolved_summary, [[M, N]]) - end, - T = case xref_utils:file_info(File) of - {ok, {_, _, _, Time}} -> Time; - Error -> throw(Error) - end, - XMod = #xref_mod{name = M, app_name = AppName, dir = Dir, - mtime = T, builtins = Builtins, - no_unresolved = NoUnresCalls}, - do_add_module(State, XMod, UnresCalls, Data); + message(Verbose, done, [File]), + %% Remove duplicates. Identical unresolved calls on the + %% same line are counted as _one_ unresolved call. + UnresCalls = usort(UnresCalls0), + NoUnresCalls = length(UnresCalls), + case NoUnresCalls of + 0 -> ok; + 1 -> warnings(Warnings, unresolved_summary1, [[M]]); + N -> warnings(Warnings, unresolved_summary, [[M, N]]) + end, + case xref_utils:file_info(File) of + {ok, {_, _, _, Time}} -> + XMod = #xref_mod{name = M, app_name = AppName, + dir = Dir, mtime = Time, + builtins = Builtins, + no_unresolved = NoUnresCalls}, + {ok, PrepMod, Bad} = + prepare_module(Mode, XMod, UnresCalls, Data), + foreach(fun({Tag,B}) -> + warnings(Warnings, Tag, + [[File,B]]) + end, Bad), + {ok, PrepMod}; + Error -> Error + end; Error -> message(Verbose, error, []), - throw(Error) + Error end. -abst(File, Builtins, Mode) when Mode =:= functions -> +process_module(State) -> + receive + {?MODULE, Reply} -> + case Reply of + no -> + {ok, [], State}; + {ok, PrepMod} -> + finish_module(PrepMod, State); + Error -> + throw(Error) + end + end. + +abst(File, Builtins, _Mode = functions) -> case beam_lib:chunks(File, [abstract_code, exports, attributes]) of {ok, {M,[{abstract_code,NoA},_X,_A]}} when NoA =:= no_abstract_code -> {ok, M, NoA}; @@ -762,7 +816,7 @@ abst(File, Builtins, Mode) when Mode =:= functions -> Error when element(1, Error) =:= error -> Error end; -abst(File, Builtins, Mode) when Mode =:= modules -> +abst(File, Builtins, _Mode = modules) -> case beam_lib:chunks(File, [exports, imports, attributes]) of {ok, {Mod, [{exports,X0}, {imports,I0}, {attributes,At}]}} -> X1 = mfa_exports(X0, At, Mod), @@ -856,19 +910,13 @@ deprecated_flag(_) -> undefined. %% dom CallAt = LC U XC %% Attrs is collected from the attribute 'xref' (experimental). do_add_module(S, XMod, Unres, Data) -> - M = XMod#xref_mod.name, - case dict:find(M, S#xref.modules) of - {ok, OldXMod} -> - BF2 = module_file(XMod), - BF1 = module_file(OldXMod), - throw_error({module_clash, {M, BF1, BF2}}); - error -> - do_add_module(S, M, XMod, Unres, Data) - end. + #xref{mode = Mode} = S, + Mode = S#xref.mode, + {ok, PrepMod, Bad} = prepare_module(Mode, XMod, Unres, Data), + {ok, Ms, NS} = finish_module(PrepMod, S), + {ok, Ms, Bad, NS}. -%%do_add_module(S, M, _XMod, _Unres, Data)-> -%% {ok, M, [], S}; -do_add_module(S, M, XMod, Unres0, Data) when S#xref.mode =:= functions -> +prepare_module(_Mode = functions, XMod, Unres0, Data) -> {DefAt0, LPreCAt0, XPreCAt0, LC0, XC0, X0, Attrs, Depr} = Data, %% Bad is a list of bad values of 'xref' attributes. {ALC0,AXC0,Bad0} = Attrs, @@ -904,26 +952,27 @@ do_add_module(S, M, XMod, Unres0, Data) when S#xref.mode =:= functions -> LC = union(LC1, ALC), {DF1,DF_11,DF_21,DF_31,DBad} = depr_mod(Depr, X), + {EE, ECallAt} = inter_graph(X, L, LC, XC, CallAt), + {ok, {functions, XMod, [DefAt,L,X,LCallAt,XCallAt,CallAt,LC,XC,EE,ECallAt, + DF1,DF_11,DF_21,DF_31], NoCalls, Unres}, + DBad++Bad}; +prepare_module(_Mode = modules, XMod, _Unres, Data) -> + {X0, I0, Depr} = Data, + X1 = xref_utils:xset(X0, [tspec(func)]), + I1 = xref_utils:xset(I0, [tspec(func)]), + {DF1,DF_11,DF_21,DF_31,DBad} = depr_mod(Depr, X1), + {ok, {modules, XMod, [X1,I1,DF1,DF_11,DF_21,DF_31]}, DBad}. - %% {EE, ECallAt} = inter_graph(X, L, LC, XC, LCallAt, XCallAt), - Self = self(), - Fun = fun() -> inter_graph(Self, X, L, LC, XC, CallAt) end, - {EE, ECallAt} = - xref_utils:subprocess(Fun, [link, {min_heap_size,100000}]), - +finish_module({functions, XMod, List, NoCalls, Unres}, S) -> + ok = check_module(XMod, S), [DefAt2,L2,X2,LCallAt2,XCallAt2,CallAt2,LC2,XC2,EE2,ECallAt2, - DF2,DF_12,DF_22,DF_32] = - pack([DefAt,L,X,LCallAt,XCallAt,CallAt,LC,XC,EE,ECallAt, - DF1,DF_11,DF_21,DF_31]), - - %% Foo = [DefAt2,L2,X2,LCallAt2,XCallAt2,CallAt2,LC2,XC2,EE2,ECallAt2, - %% DF2,DF_12,DF_22,DF_32], - %% io:format("{~p, ~p, ~p},~n", [M, pack:lsize(Foo), pack:usize(Foo)]), + DF2,DF_12,DF_22,DF_32] = pack(List), LU = range(LC2), LPredefined = predefined_funs(LU), + M = XMod#xref_mod.name, MS = xref_utils:xset(M, atom), T = from_sets({MS,DefAt2,L2,X2,LCallAt2,XCallAt2,CallAt2, LC2,XC2,LU,EE2,ECallAt2,Unres,LPredefined, @@ -934,19 +983,28 @@ do_add_module(S, M, XMod, Unres0, Data) when S#xref.mode =:= functions -> XMod1 = XMod#xref_mod{data = T, info = Info}, S1 = S#xref{modules = dict:store(M, XMod1, S#xref.modules)}, - {ok, [M], DBad++Bad, take_down(S1)}; -do_add_module(S, M, XMod, _Unres, Data) when S#xref.mode =:= modules -> - {X0, I0, Depr} = Data, - X1 = xref_utils:xset(X0, [tspec(func)]), - I1 = xref_utils:xset(I0, [tspec(func)]), - {DF1,DF_11,DF_21,DF_31,DBad} = depr_mod(Depr, X1), - [X2,I2,DF2,DF_12,DF_22,DF_32] = pack([X1,I1,DF1,DF_11,DF_21,DF_31]), + {ok, [M], take_down(S1)}; +finish_module({modules, XMod, List}, S) -> + ok = check_module(XMod, S), + [X2,I2,DF2,DF_12,DF_22,DF_32] = pack(List), + M = XMod#xref_mod.name, MS = xref_utils:xset(M, atom), T = from_sets({MS, X2, I2, DF2, DF_12, DF_22, DF_32}), Info = [], XMod1 = XMod#xref_mod{data = T, info = Info}, S1 = S#xref{modules = dict:store(M, XMod1, S#xref.modules)}, - {ok, [M], DBad, take_down(S1)}. + {ok, [M], take_down(S1)}. + +check_module(XMod, State) -> + M = XMod#xref_mod.name, + case dict:find(M, State#xref.modules) of + {ok, OldXMod} -> + BF2 = module_file(XMod), + BF1 = module_file(OldXMod), + throw_error({module_clash, {M, BF1, BF2}}); + error -> + ok + end. depr_mod({Depr,Bad0}, X) -> %% Bad0 are badly formed deprecated attributes. @@ -992,9 +1050,6 @@ no_info(X, L, LC, XC, EE, Unres, NoCalls, NoUnresCalls) -> %% Note: this is overwritten in do_set_up(): {no_inter_function_calls, no_elements(EE)}]. -inter_graph(Pid, X, L, LC, XC, CallAt) -> - Pid ! {self(), inter_graph(X, L, LC, XC, CallAt)}. - %% Inter Call Graph. %inter_graph(_X, _L, _LC, _XC, _CallAt) -> % {empty_set(), empty_set()}; @@ -1766,10 +1821,6 @@ tpack(T, I, L) -> message(true, What, Arg) -> case What of - reading_beam -> - io:format("~ts... ", Arg); - skipped_beam -> - io:format("skipped (no debug information)~n", Arg); no_debug_info -> io:format("Skipping ~ts (no debug information)~n", Arg); unresolved_summary1 -> @@ -1791,7 +1842,7 @@ message(true, What, Arg) -> set_up -> io:format("Setting up...", Arg); done -> - io:format("done~n", Arg); + io:format("done reading ~ts~n", Arg); error -> io:format("error~n", Arg); Else -> diff --git a/lib/tools/src/xref_utils.erl b/lib/tools/src/xref_utils.erl index f69aa70244..b0c168e018 100644 --- a/lib/tools/src/xref_utils.erl +++ b/lib/tools/src/xref_utils.erl @@ -47,8 +47,6 @@ -export([options/2]). --export([subprocess/2]). - -export([format_error/1]). -import(lists, [append/1, delete/2, filter/2, foldl/3, foreach/2, @@ -512,12 +510,6 @@ find_beam(Culprit) -> options(Options, Valid) -> split_options(Options, [], [], [], Valid). -subprocess(Fun, Opts) -> - Pid = spawn_opt(Fun, Opts), - receive - {Pid, Reply} -> Reply - end. - format_error({error, Module, Error}) -> Module:format_error(Error); format_error({file_error, FileName, Reason}) -> diff --git a/system/doc/reference_manual/typespec.xml b/system/doc/reference_manual/typespec.xml index 9e26e9058d..f17e5df277 100644 --- a/system/doc/reference_manual/typespec.xml +++ b/system/doc/reference_manual/typespec.xml @@ -142,7 +142,7 @@ PairList :: Pair | Pair, PairList - Pair :: Type := Type %% notes a pair that must be present + Pair :: Type := Type %% denotes a pair that must be present | Type => Type TList :: Type @@ -174,19 +174,13 @@ </p> <p> The general form of maps is <c>#{PairList}</c>. The key types in - <c>PairList</c> are allowed to overlap, and if they do, the leftmost pair - takes precedence. A map value does not belong to this type if contains a key - that is not in <c>PairList</c>. + <c>PairList</c> are allowed to overlap, and if they do, the + leftmost pair takes precedence. A map pair has a key in + <c>PairList</c> if it belongs to this type. </p> <p> - Because it is common to end a map type with <c>any() => any()</c> to denote - that keys that do not belong to any other pair in <c>PairList</c> are - allowed, and may map to any value, the shorthand notation <c>...</c> is - allowed as the last pair of a map type. - </p> - <p> - Notice that the syntactic representation of <c>map()</c> is <c>#{...}</c> - (or <c>#{_ => _}</c>, or <c>#{any() => any()}</c>), not <c>#{}</c>. + Notice that the syntactic representation of <c>map()</c> is + <c>#{any() => any()}</c> (or <c>#{_ => _}</c>), not <c>#{}</c>. The notation <c>#{}</c> specifies the singleton type for the empty map. </p> <p> |