aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--HOWTO/INSTALL-CROSS.md1
-rw-r--r--HOWTO/INSTALL-WIN32.md3
-rw-r--r--HOWTO/INSTALL.md1
-rw-r--r--HOWTO/MARKDOWN.md15
-rw-r--r--bootstrap/lib/compiler/ebin/beam_disasm.beambin26204 -> 26188 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet.beambin23312 -> 23380 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_tcp_dist.beambin7184 -> 7212 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/net_adm.beambin3008 -> 3036 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/supervisor.beambin23436 -> 23348 bytes
-rw-r--r--erts/configure.in2
-rw-r--r--erts/doc/src/erl.xml22
-rw-r--r--erts/doc/src/erl_nif.xml4
-rw-r--r--erts/doc/src/erl_tracer.xml96
-rw-r--r--erts/doc/src/erlang.xml13
-rw-r--r--erts/doc/src/init.xml15
-rw-r--r--erts/emulator/beam/atom.names8
-rw-r--r--erts/emulator/beam/beam_bif_load.c141
-rw-r--r--erts/emulator/beam/bif.c64
-rw-r--r--erts/emulator/beam/bif.tab2
-rw-r--r--erts/emulator/beam/erl_bif_info.c5
-rw-r--r--erts/emulator/beam/erl_gc.c26
-rw-r--r--erts/emulator/beam/erl_init.c6
-rw-r--r--erts/emulator/beam/erl_message.c43
-rw-r--r--erts/emulator/beam/erl_message.h38
-rw-r--r--erts/emulator/beam/erl_nif.c24
-rw-r--r--erts/emulator/beam/erl_process.c9
-rw-r--r--erts/emulator/beam/erl_trace.c65
-rw-r--r--erts/emulator/beam/io.c59
-rw-r--r--erts/emulator/beam/utils.c8
-rw-r--r--erts/emulator/hipe/hipe_bif0.c10
-rw-r--r--erts/emulator/hipe/hipe_x86.c4
-rw-r--r--erts/emulator/nifs/common/erl_tracer_nif.c34
-rw-r--r--erts/emulator/test/bif_SUITE.erl2
-rw-r--r--erts/emulator/test/code_SUITE.erl132
-rw-r--r--erts/emulator/test/distribution_SUITE.erl69
-rw-r--r--erts/emulator/test/message_queue_data_SUITE.erl42
-rw-r--r--erts/emulator/test/tracer_SUITE.erl103
-rw-r--r--erts/emulator/test/tracer_SUITE_data/tracer_test.c4
-rw-r--r--erts/emulator/test/tracer_test.erl4
-rw-r--r--erts/etc/common/erlexec.c21
-rw-r--r--erts/preloaded/ebin/erl_tracer.beambin2112 -> 2200 bytes
-rw-r--r--erts/preloaded/ebin/erlang.beambin104620 -> 104648 bytes
-rw-r--r--erts/preloaded/ebin/init.beambin49924 -> 50052 bytes
-rw-r--r--erts/preloaded/src/erl_tracer.erl14
-rw-r--r--erts/preloaded/src/erlang.erl10
-rw-r--r--erts/preloaded/src/init.erl33
-rw-r--r--lib/compiler/src/beam_disasm.erl5
-rw-r--r--lib/compiler/src/beam_disasm.hrl6
-rw-r--r--lib/compiler/src/v3_kernel.erl2
-rw-r--r--lib/eldap/test/eldap_basic_SUITE.erl132
-rw-r--r--lib/et/src/et_collector.erl29
-rw-r--r--lib/et/src/et_wx_contents_viewer.erl109
-rw-r--r--lib/et/src/et_wx_viewer.erl136
-rw-r--r--lib/hipe/cerl/erl_bif_types.erl6
-rw-r--r--lib/hipe/icode/Makefile2
-rw-r--r--lib/hipe/icode/hipe_icode_call_elim.erl78
-rw-r--r--lib/hipe/icode/hipe_icode_type.erl1
-rw-r--r--lib/hipe/llvm/Makefile16
-rw-r--r--lib/hipe/llvm/elf_format.erl654
-rw-r--r--lib/hipe/llvm/elf_format.hrl40
-rw-r--r--lib/hipe/llvm/hipe_llvm.erl56
-rw-r--r--lib/hipe/llvm/hipe_llvm_main.erl189
-rw-r--r--lib/hipe/llvm/hipe_rtl_to_llvm.erl23
-rw-r--r--lib/hipe/main/hipe.app.src1
-rw-r--r--lib/hipe/main/hipe.erl55
-rw-r--r--lib/hipe/main/hipe_main.erl14
-rw-r--r--lib/hipe/misc/hipe_consttab.erl13
-rw-r--r--lib/hipe/misc/hipe_consttab.hrl2
-rw-r--r--lib/hipe/test/Makefile5
-rw-r--r--lib/hipe/test/hipe_testsuite_driver.erl24
-rw-r--r--lib/hipe/test/opt_verify_SUITE.erl62
-rw-r--r--lib/hipe/test/opt_verify_SUITE_data/call_elim_test.erl12
-rw-r--r--lib/hipe/test/opt_verify_SUITE_data/call_elim_test_branches_no_opt_poss.erl32
-rw-r--r--lib/hipe/test/opt_verify_SUITE_data/call_elim_test_branches_opt_poss.erl32
-rw-r--r--lib/inets/test/Makefile3
-rw-r--r--lib/inets/test/ftp_SUITE.erl482
-rw-r--r--lib/inets/test/ftp_format_SUITE.erl109
-rw-r--r--lib/inets/test/ftp_suite_lib.erl1695
-rw-r--r--lib/inets/test/http_format_SUITE.erl8
-rw-r--r--lib/inets/test/httpc_SUITE.erl51
-rw-r--r--lib/inets/test/httpc_cookie_SUITE.erl369
-rw-r--r--lib/inets/test/httpc_proxy_SUITE.erl16
-rw-r--r--lib/inets/test/httpd_SUITE.erl331
-rw-r--r--lib/inets/test/httpd_basic_SUITE.erl33
-rw-r--r--lib/inets/test/httpd_block.erl2
-rw-r--r--lib/inets/test/httpd_load.erl5
-rw-r--r--lib/inets/test/httpd_mod.erl185
-rw-r--r--lib/inets/test/inets_SUITE.erl119
-rw-r--r--lib/inets/test/inets_app_test.erl245
-rw-r--r--lib/inets/test/inets_appup_test.erl71
-rw-r--r--lib/inets/test/inets_socketwrap_SUITE.erl6
-rw-r--r--lib/inets/test/inets_sup_SUITE.erl69
-rw-r--r--lib/inets/test/inets_test_lib.hrl88
-rw-r--r--lib/inets/test/old_httpd_SUITE.erl487
-rw-r--r--lib/kernel/doc/src/inet.xml7
-rw-r--r--lib/kernel/src/inet.erl7
-rw-r--r--lib/kernel/src/inet_tcp_dist.erl6
-rw-r--r--lib/kernel/src/net_adm.erl3
-rw-r--r--lib/kernel/test/init_SUITE.erl18
-rw-r--r--lib/mnesia/src/mnesia_index.erl4
-rw-r--r--lib/mnesia/test/mnesia_dirty_access_test.erl56
-rw-r--r--lib/observer/doc/src/observer_ug.xml111
-rw-r--r--lib/observer/doc/src/ttb.xml56
-rw-r--r--lib/observer/src/Makefile1
-rw-r--r--lib/observer/src/observer.app.src1
-rw-r--r--lib/observer/src/observer_app_wx.erl8
-rw-r--r--lib/observer/src/observer_port_wx.erl479
-rw-r--r--lib/observer/src/observer_pro_wx.erl61
-rw-r--r--lib/observer/src/observer_trace_wx.erl672
-rw-r--r--lib/observer/src/observer_traceoptions_wx.erl106
-rw-r--r--lib/observer/src/observer_tv_wx.erl34
-rw-r--r--lib/observer/src/observer_wx.erl40
-rw-r--r--lib/observer/src/ttb.erl51
-rw-r--r--lib/observer/test/observer_SUITE.erl26
-rw-r--r--lib/runtime_tools/c_src/dyntrace.c43
-rw-r--r--lib/runtime_tools/doc/src/LTTng.xml144
-rw-r--r--lib/runtime_tools/doc/src/dbg.xml4
-rw-r--r--lib/runtime_tools/src/dbg.erl5
-rw-r--r--lib/runtime_tools/src/dyntrace.erl38
-rw-r--r--lib/runtime_tools/src/observer_backend.erl12
-rw-r--r--lib/runtime_tools/test/dbg_SUITE.erl10
-rw-r--r--lib/runtime_tools/test/dbg_SUITE_data/dbg_SUITE.c4
-rw-r--r--lib/ssh/src/ssh_message.erl10
-rw-r--r--lib/ssh/test/ssh_algorithms_SUITE.erl57
-rw-r--r--lib/ssh/test/ssh_benchmark_SUITE.erl19
-rw-r--r--lib/ssh/test/ssh_sftp_SUITE.erl86
-rw-r--r--lib/ssh/test/ssh_test_lib.erl53
-rw-r--r--lib/ssl/doc/src/ssl.xml18
-rw-r--r--lib/ssl/doc/src/ssl_app.xml27
-rw-r--r--lib/ssl/src/dtls_connection.erl3
-rw-r--r--lib/ssl/src/inet_tls_dist.erl3
-rw-r--r--lib/ssl/src/ssl.erl7
-rw-r--r--lib/ssl/src/ssl_internal.hrl3
-rw-r--r--lib/ssl/src/ssl_record.erl43
-rw-r--r--lib/ssl/src/ssl_record.hrl4
-rw-r--r--lib/ssl/src/ssl_tls_dist_proxy.erl3
-rw-r--r--lib/ssl/src/tls_connection.erl3
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl64
-rw-r--r--lib/stdlib/src/beam_lib.erl2
-rw-r--r--lib/stdlib/src/erl_compile.erl1
-rw-r--r--lib/stdlib/src/escript.erl1
-rw-r--r--lib/stdlib/src/proc_lib.erl14
-rw-r--r--lib/stdlib/src/supervisor.erl16
-rw-r--r--make/emd2exml.in37
-rw-r--r--system/doc/efficiency_guide/advanced.xml18
145 files changed, 4693 insertions, 5068 deletions
diff --git a/HOWTO/INSTALL-CROSS.md b/HOWTO/INSTALL-CROSS.md
index 0c984a825d..29614966b8 100644
--- a/HOWTO/INSTALL-CROSS.md
+++ b/HOWTO/INSTALL-CROSS.md
@@ -286,6 +286,7 @@ document for information on how to build the documentation.
Testing the cross compiled system
---------------------------------
+
Some of the tests that come with erlang use native code to test. This means
that when cross compiling erlang you also have to cross compile test suites
in order to run tests on the target host. To do this you first have to release
diff --git a/HOWTO/INSTALL-WIN32.md b/HOWTO/INSTALL-WIN32.md
index 067c939d7a..d7be255e9f 100644
--- a/HOWTO/INSTALL-WIN32.md
+++ b/HOWTO/INSTALL-WIN32.md
@@ -60,7 +60,8 @@ followed by some FAQ, and then we’ll go into more details of the setup.
Short Version
---------------------------
+-------------
+
In the following sections, we've described as much as we could about the
installation of the tools needed. Once the tools are installed, building
is quite easy. We have also tried to make these instructions understandable
diff --git a/HOWTO/INSTALL.md b/HOWTO/INSTALL.md
index 2ae1ed3c8d..8632f46264 100644
--- a/HOWTO/INSTALL.md
+++ b/HOWTO/INSTALL.md
@@ -824,6 +824,7 @@ Known platform issues
Daily Build and Test
--------------------
+
At Ericsson we have a "Daily Build and Test" that runs on:
* Solaris 8, 9
diff --git a/HOWTO/MARKDOWN.md b/HOWTO/MARKDOWN.md
index 2df0460dab..e6773a0a8e 100644
--- a/HOWTO/MARKDOWN.md
+++ b/HOWTO/MARKDOWN.md
@@ -70,6 +70,9 @@ in the future.
* Lists aren't supported inside block quotes.
+* Nested block quotes can be generated, but current DTD does not
+ support it.
+
* Link and image definition names *are* case sensitive.
#### Additional Features ####
@@ -126,7 +129,7 @@ places. Appropriate attributes to the `X` tag will also be generated.
<icaption>...</icaption> </image>` sequence where the "title" will be
placed between `<icaption>` and `</icaption>`.
-* Block quotes generate `<blockquote>` tags.
+* Block quotes generate `<quote>` tags.
* If the first line of a top level block quote begins with a `> *NOTE*:`
character sequence, a `<note>` tag will be generated instead of a
@@ -152,7 +155,7 @@ places. Appropriate attributes to the `X` tag will also be generated.
* Emphasis (single `*` or `_`) will generate `<em>` tags.
-* Strong emphasis (double `*` or `_`) will generate `<b>` tags.
+* Strong emphasis (double `*` or `_`) will generate `<strong>` tags.
* The level 1 heading will cause the following to be generated:
@@ -180,11 +183,11 @@ places. Appropriate attributes to the `X` tag will also be generated.
contain information from a \%CopyrightBegin\%, \%CopyrightEnd\% block
if such exist (see below).
-* A level `X` heading where `1 < X <= 3` will cause the the following
+* A level `X` heading where `1 < X <= 6` will cause the the following
to be generated:
- <marker id="..."/>
<section>
+ <marker id="..."/>
<title>...</title>
...
</section>
@@ -206,11 +209,11 @@ places. Appropriate attributes to the `X` tag will also be generated.
other documents. That is, *be careful* when changing headings in an
existing document.
-* A level `X` heading where `3 < X` will cause the the following
+* A level `X` heading where `6 < X` will cause the the following
to be generated:
<marker id="..."/>
- <p><b>...</b></p>
+ <p><strong>...</strong></p>
...
Current DTD:s used don't support deeper levels of sections, and we
diff --git a/bootstrap/lib/compiler/ebin/beam_disasm.beam b/bootstrap/lib/compiler/ebin/beam_disasm.beam
index 477ab0a957..3b74bc6e3e 100644
--- a/bootstrap/lib/compiler/ebin/beam_disasm.beam
+++ b/bootstrap/lib/compiler/ebin/beam_disasm.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet.beam b/bootstrap/lib/kernel/ebin/inet.beam
index 8cfdf389ad..9058389555 100644
--- a/bootstrap/lib/kernel/ebin/inet.beam
+++ b/bootstrap/lib/kernel/ebin/inet.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_tcp_dist.beam b/bootstrap/lib/kernel/ebin/inet_tcp_dist.beam
index 59cb7ce40e..d06bdf709a 100644
--- a/bootstrap/lib/kernel/ebin/inet_tcp_dist.beam
+++ b/bootstrap/lib/kernel/ebin/inet_tcp_dist.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/net_adm.beam b/bootstrap/lib/kernel/ebin/net_adm.beam
index 00772b61ff..60f51602b3 100644
--- a/bootstrap/lib/kernel/ebin/net_adm.beam
+++ b/bootstrap/lib/kernel/ebin/net_adm.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/supervisor.beam b/bootstrap/lib/stdlib/ebin/supervisor.beam
index d30b4a037b..ba6a64d594 100644
--- a/bootstrap/lib/stdlib/ebin/supervisor.beam
+++ b/bootstrap/lib/stdlib/ebin/supervisor.beam
Binary files differ
diff --git a/erts/configure.in b/erts/configure.in
index 4a63381eb7..a34dfc6dbd 100644
--- a/erts/configure.in
+++ b/erts/configure.in
@@ -3868,7 +3868,7 @@ if test "$enable_lttng_test" = "yes" ; then
#define TRACEPOINT_DEFINE],
[if(tracepoint_enabled(com_ericsson_otp,dummy)) do {} while(0)])],
[AC_MSG_RESULT([yes])],
- [AC_MSG_ERROR([no (must be present)])])
+ [AC_MSG_ERROR([no (available in lttng-ust v2.7)])])
if test "x$ac_cv_header_lttng_tracepoint_h" = "xyes" \
-a "x$ac_cv_header_lttng_tracepoint_event_h" = "xyes"; then
# No straight forward way to test for liblttng-ust when no public symbol exists,
diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml
index 1bbde7f1e0..5d5bfb141f 100644
--- a/erts/doc/src/erl.xml
+++ b/erts/doc/src/erl.xml
@@ -338,7 +338,8 @@
<seealso marker="kernel:net_kernel">net_kernel(3)</seealso>.
It is also ensured that <c><![CDATA[epmd]]></c> runs on the current host
before Erlang is started. See
- <seealso marker="epmd">epmd(1)</seealso>.</p>
+ <seealso marker="epmd">epmd(1)</seealso> and the
+ <seealso marker="#start_epmd"><c>-start_epmd</c></seealso> option.</p>
<p>The name of the node will be <c><![CDATA[Name@Host]]></c>, where
<c><![CDATA[Host]]></c> is the fully qualified host name of the current
host. For short names, use the <c><![CDATA[-sname]]></c> flag instead.</p>
@@ -463,6 +464,21 @@
flag and those running with the <c><![CDATA[-name]]></c> flag, as node
names must be unique in distributed Erlang systems.</p>
</item>
+ <tag><marker id="start_epmd"/><c>-start_epmd true | false</c></tag>
+ <item>
+
+ <p>Specifies whether Erlang should start
+ <seealso marker="epmd">epmd</seealso> on startup. By default
+ this is <c>true</c>, but if you prefer to start epmd
+ manually, set this to <c>false</c>.</p>
+
+ <p>This only applies if Erlang is started as a distributed node,
+ i.e. if <c>-name</c> or <c>-sname</c> is specified. Otherwise,
+ epmd is not started even if <c>-start_epmd true</c> is given.</p>
+
+ <p>Note that a distributed node will fail to start if epmd is
+ not running.</p>
+ </item>
<tag><marker id="smp"/><c><![CDATA[-smp [enable|auto|disable]]]></c></tag>
<item>
<p><c>-smp enable</c> and <c>-smp</c> starts the Erlang runtime
@@ -660,11 +676,11 @@
<p>Sets the initial process dictionary size of processes to the size
<c><![CDATA[Size]]></c>.</p>
</item>
- <tag><marker id="+hmqd"><c>+hmqd off_heap|on_heap|mixed</c></marker></tag>
+ <tag><marker id="+hmqd"><c>+hmqd off_heap|on_heap</c></marker></tag>
<item><p>
Sets the default value for the process flag
<c>message_queue_data</c>. If <c>+hmqd</c> is not
- passed, <c>mixed</c> will be the default. For more information,
+ passed, <c>on_heap</c> will be the default. For more information,
see the documentation of
<seealso marker="erlang#process_flag_message_queue_data"><c>process_flag(message_queue_data,
MQD)</c></seealso>.
diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml
index 33a4fee182..57e047af08 100644
--- a/erts/doc/src/erl_nif.xml
+++ b/erts/doc/src/erl_nif.xml
@@ -295,14 +295,14 @@ ok
</p></item>
<tag><marker id="time_measurement"/>Time Measurement</tag>
- <item><p>Support for time measurement in NIF libraries:
+ <item><p>Support for time measurement in NIF libraries:</p>
<list>
<item><seealso marker="#ErlNifTime"><c>ErlNifTime</c></seealso></item>
<item><seealso marker="#ErlNifTimeUnit"><c>ErlNifTimeUnit</c></seealso></item>
<item><seealso marker="#enif_monotonic_time"><c>enif_monotonic_time()</c></seealso></item>
<item><seealso marker="#enif_time_offset"><c>enif_time_offset()</c></seealso></item>
<item><seealso marker="#enif_convert_time_unit"><c>enif_convert_time_unit()</c></seealso></item>
- </list></p>
+ </list>
</item>
<tag><marker id="lengthy_work"/>Long-running NIFs</tag>
diff --git a/erts/doc/src/erl_tracer.xml b/erts/doc/src/erl_tracer.xml
index d4c8bbad31..7841fdfd63 100644
--- a/erts/doc/src/erl_tracer.xml
+++ b/erts/doc/src/erl_tracer.xml
@@ -67,7 +67,7 @@
<desc>
<p>The different trace tags that the tracer will be called with.
Each trace tag is described in greater detail in
- <seealso marker="#Module:trace/6">Module:trace/6</seealso>
+ <seealso marker="#Module:trace/5">Module:trace/5</seealso>
</p>
</desc>
</datatype>
@@ -84,14 +84,18 @@
<p>The options for the tracee.</p>
<taglist>
<tag><c>timestamp</c></tag>
- <item>If not set to <c>undefined</c>, the tracer has been requested to
- include a timestamp.</item>
+ <item>If set the tracer has been requested to include a timestamp.</item>
+ <tag><c>extra</c></tag>
+ <item>If set the tracepoint has included additonal data about
+ the trace event. What the additional data is depends on which
+ <c>TraceTag</c> has been triggered. The <c>extra</c> trace data
+ corresponds to the fifth elemnt in the trace tuples described in
+ <seealso marker="erlang#trace_3_trace_messages">erlang:trace/3</seealso>.</item>
<tag><c>match_spec_result</c></tag>
- <item>If not set to <c>true</c>, the tracer has been requested to
- include the output of a match specification that was run.</item>
+ <item>If set the tracer has been requested to include the output
+ of a match specification that was run.</item>
<tag><c>scheduler_id</c></tag>
- <item>Set to a number if the scheduler id is to be included by the tracer.
- Otherwise it is set to <c>undefined</c>.</item>
+ <item>Set the scheduler id is to be included by the tracer.</item>
</taglist>
</desc>
</datatype>
@@ -115,23 +119,23 @@
<taglist>
<tag><seealso marker="#Module:enabled/3"><c>Module:enabled/3</c></seealso></tag>
<item>Mandatory</item>
- <tag><seealso marker="#Module:trace/6"><c>Module:trace/6</c></seealso></tag>
+ <tag><seealso marker="#Module:trace/5"><c>Module:trace/5</c></seealso></tag>
<item>Mandatory</item>
<tag><seealso marker="#Module:enabled_procs/3"><c>Module:enabled_procs/3</c></seealso></tag>
<item>Optional</item>
- <tag><seealso marker="#Module:trace_procs/6"><c>Module:trace_procs/6</c></seealso></tag>
+ <tag><seealso marker="#Module:trace_procs/5"><c>Module:trace_procs/5</c></seealso></tag>
<item>Optional</item>
<tag><seealso marker="#Module:enabled_ports/3"><c>Module:enabled_ports/3</c></seealso></tag>
<item>Optional</item>
- <tag><seealso marker="#Module:trace_ports/6"><c>Module:trace_ports/6</c></seealso></tag>
+ <tag><seealso marker="#Module:trace_ports/5"><c>Module:trace_ports/5</c></seealso></tag>
<item>Optional</item>
<tag><seealso marker="#Module:enabled_running_ports/3"><c>Module:enabled_running_ports/3</c></seealso></tag>
<item>Optional</item>
- <tag><seealso marker="#Module:trace_running_ports/6"><c>Module:trace_running_ports/6</c></seealso></tag>
+ <tag><seealso marker="#Module:trace_running_ports/5"><c>Module:trace_running_ports/5</c></seealso></tag>
<item>Optional</item>
<tag><seealso marker="#Module:enabled_running_procs/3"><c>Module:enabled_running_procs/3</c></seealso></tag>
<item>Optional</item>
- <tag><seealso marker="#Module:trace_running_procs/6"><c>Module:trace_running_procs/6</c></seealso></tag>
+ <tag><seealso marker="#Module:trace_running_procs/5"><c>Module:trace_running_procs/5</c></seealso></tag>
<item>Optional</item>
</taglist>
@@ -166,14 +170,13 @@
</desc>
</func>
<func>
- <name>Module:trace(TraceTag, TracerState, Tracee, FirstTraceTerm, SecondTraceTerm, Opts) -> Result</name>
+ <name>Module:trace(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result</name>
<fsummary>Check if a trace event should be generated.</fsummary>
<type>
<v>TraceTag = <seealso marker="#type-trace_tag">trace_tag()</seealso></v>
<v>TracerState = term()</v>
<v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
<v>FirstTraceTerm = term()</v>
- <v>SecondTraceTerm = term() | undefined</v>
<v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v>
<v>Result = ok</v>
</type>
@@ -182,17 +185,17 @@
the <seealso marker="#Module:enabled/3">Module:enabled/3</seealso>
callback returned <c>trace</c>. In it any side effects needed by
the tracer should be done. The tracepoint payload is located in
- the <c>FirstTraceTerm</c> and <c>SecondTraceTerm</c>. The content
- of the TraceTerms depends on which <c>TraceTag</c> has been triggered.
- The <c>FirstTraceTerm</c> and <c>SecondTraceTerm</c> correspond to the
- fourth and fifth slot in the trace tuples described in
+ the <c>TraceTerm</c>. The content of the TraceTerm depends on which
+ <c>TraceTag</c> has been triggered.
+ The <c>TraceTerm</c> corresponds to the
+ fourth element in the trace tuples described in
<seealso marker="erlang#trace_3_trace_messages">erlang:trace/3</seealso>.
- If the tuple only has four elements, <c>SecondTraceTerm</c> will be
- <c>undefined</c>.</p>
+ If the trace tuple has five elements, the fifth element will be sent as
+ the <c>extra</c> value in the <c>Opts</c> maps.</p>
</desc>
</func>
<func>
- <name name="trace">Module:trace(seq_trace, TracerState, Label, SeqTraceInfo, undefined, Opts) -> Result</name>
+ <name name="trace">Module:trace(seq_trace, TracerState, Label, SeqTraceInfo, Opts) -> Result</name>
<fsummary>Check if a sequence trace event should be generated.</fsummary>
<type>
<v>TracerState = term()</v>
@@ -228,14 +231,13 @@
</func>
<func>
- <name>Module:trace_procs(TraceTag, TracerState, Tracee, FirstTraceTerm, SecondTraceTerm, Opts) -> Result</name>
+ <name>Module:trace_procs(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result</name>
<fsummary>Check if a trace event should be generated.</fsummary>
<type>
<v>TraceTag = <seealso marker="#type-trace_tag_procs">trace_tag()</seealso></v>
<v>TracerState = term()</v>
<v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
<v>FirstTraceTerm = term()</v>
- <v>SecondTraceTerm = term() | undefined</v>
<v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v>
<v>Result = ok</v>
</type>
@@ -243,7 +245,7 @@
<p>This callback will be called when a tracepoint is triggered and
the <seealso marker="#Module:enabled_procs/3">Module:enabled_procs/3</seealso>
callback returned <c>trace</c>.</p>
- <p>If <c>trace_procs/6</c> is not defined <c>trace/6</c> will be called instead.</p>
+ <p>If <c>trace_procs/5</c> is not defined <c>trace/5</c> will be called instead.</p>
</desc>
</func>
@@ -265,14 +267,13 @@
</func>
<func>
- <name>Module:trace_ports(TraceTag, TracerState, Tracee, FirstTraceTerm, SecondTraceTerm, Opts) -> Result</name>
+ <name>Module:trace_ports(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result</name>
<fsummary>Check if a trace event should be generated.</fsummary>
<type>
<v>TraceTag = <seealso marker="#type-trace_tag_ports">trace_tag()</seealso></v>
<v>TracerState = term()</v>
<v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
<v>FirstTraceTerm = term()</v>
- <v>SecondTraceTerm = term() | undefined</v>
<v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v>
<v>Result = ok</v>
</type>
@@ -280,7 +281,7 @@
<p>This callback will be called when a tracepoint is triggered and
the <seealso marker="#Module:enabled_ports/3">Module:enabled_ports/3</seealso>
callback returned <c>trace</c>.</p>
- <p>If <c>trace_ports/6</c> is not defined <c>trace/6</c> will be called instead.</p>
+ <p>If <c>trace_ports/5</c> is not defined <c>trace/5</c> will be called instead.</p>
</desc>
</func>
@@ -302,14 +303,13 @@
</func>
<func>
- <name>Module:trace_running_procs(TraceTag, TracerState, Tracee, FirstTraceTerm, SecondTraceTerm, Opts) -> Result</name>
+ <name>Module:trace_running_procs(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result</name>
<fsummary>Check if a trace event should be generated.</fsummary>
<type>
<v>TraceTag = <seealso marker="#type-trace_tag_running_procs">trace_tag_running_procs()</seealso></v>
<v>TracerState = term()</v>
<v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
<v>FirstTraceTerm = term()</v>
- <v>SecondTraceTerm = term() | undefined</v>
<v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v>
<v>Result = ok</v>
</type>
@@ -317,7 +317,7 @@
<p>This callback will be called when a tracepoint is triggered and
the <seealso marker="#Module:enabled_running_procs/3">Module:enabled_running_procs/3</seealso>
callback returned <c>trace</c>.</p>
- <p>If <c>trace_running_procs/6</c> is not defined <c>trace/6</c> will be called instead.</p>
+ <p>If <c>trace_running_procs/5</c> is not defined <c>trace/5</c> will be called instead.</p>
</desc>
</func>
@@ -339,14 +339,13 @@
</func>
<func>
- <name>Module:trace_running_ports(TraceTag, TracerState, Tracee, FirstTraceTerm, SecondTraceTerm, Opts) -> Result</name>
+ <name>Module:trace_running_ports(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result</name>
<fsummary>Check if a trace event should be generated.</fsummary>
<type>
<v>TraceTag = <seealso marker="#type-trace_tag_running_ports">trace_tag_running_ports()</seealso></v>
<v>TracerState = term()</v>
<v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
<v>FirstTraceTerm = term()</v>
- <v>SecondTraceTerm = term() | undefined</v>
<v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v>
<v>Result = ok</v>
</type>
@@ -354,7 +353,7 @@
<p>This callback will be called when a tracepoint is triggered and
the <seealso marker="#Module:enabled_running_ports/3">Module:enabled_running_ports/3</seealso>
callback returned <c>trace</c>.</p>
- <p>If <c>trace_running_ports/6</c> is not defined <c>trace/6</c> will be called instead.</p>
+ <p>If <c>trace_running_ports/5</c> is not defined <c>trace/5</c> will be called instead.</p>
</desc>
</func>
@@ -376,14 +375,13 @@
</func>
<func>
- <name>Module:trace_call(TraceTag, TracerState, Tracee, FirstTraceTerm, SecondTraceTerm, Opts) -> Result</name>
+ <name>Module:trace_call(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result</name>
<fsummary>Check if a trace event should be generated.</fsummary>
<type>
<v>TraceTag = <seealso marker="#type-trace_tag_call">trace_tag_call()</seealso></v>
<v>TracerState = term()</v>
<v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
<v>FirstTraceTerm = term()</v>
- <v>SecondTraceTerm = term() | undefined</v>
<v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v>
<v>Result = ok</v>
</type>
@@ -391,7 +389,7 @@
<p>This callback will be called when a tracepoint is triggered and
the <seealso marker="#Module:enabled_call/3">Module:enabled_call/3</seealso>
callback returned <c>trace</c>.</p>
- <p>If <c>trace_call/6</c> is not defined <c>trace/6</c> will be called instead.</p>
+ <p>If <c>trace_call/5</c> is not defined <c>trace/5</c> will be called instead.</p>
</desc>
</func>
@@ -413,14 +411,13 @@
</func>
<func>
- <name>Module:trace_send(TraceTag, TracerState, Tracee, FirstTraceTerm, SecondTraceTerm, Opts) -> Result</name>
+ <name>Module:trace_send(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result</name>
<fsummary>Check if a trace event should be generated.</fsummary>
<type>
<v>TraceTag = <seealso marker="#type-trace_tag_send">trace_tag_send()</seealso></v>
<v>TracerState = term()</v>
<v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
<v>FirstTraceTerm = term()</v>
- <v>SecondTraceTerm = term() | undefined</v>
<v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v>
<v>Result = ok</v>
</type>
@@ -428,7 +425,7 @@
<p>This callback will be called when a tracepoint is triggered and
the <seealso marker="#Module:enabled_send/3">Module:enabled_send/3</seealso>
callback returned <c>trace</c>.</p>
- <p>If <c>trace_send/6</c> is not defined <c>trace/6</c> will be called instead.</p>
+ <p>If <c>trace_send/5</c> is not defined <c>trace/5</c> will be called instead.</p>
</desc>
</func>
@@ -450,14 +447,13 @@
</func>
<func>
- <name>Module:trace_receive(TraceTag, TracerState, Tracee, FirstTraceTerm, SecondTraceTerm, Opts) -> Result</name>
+ <name>Module:trace_receive(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result</name>
<fsummary>Check if a trace event should be generated.</fsummary>
<type>
<v>TraceTag = <seealso marker="#type-trace_tag_receive">trace_tag_receive()</seealso></v>
<v>TracerState = term()</v>
<v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
<v>FirstTraceTerm = term()</v>
- <v>SecondTraceTerm = term() | undefined</v>
<v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v>
<v>Result = ok</v>
</type>
@@ -465,7 +461,7 @@
<p>This callback will be called when a tracepoint is triggered and
the <seealso marker="#Module:enabled_receive/3">Module:enabled_receive/3</seealso>
callback returned <c>trace</c>.</p>
- <p>If <c>trace_receive/6</c> is not defined <c>trace/6</c> will be called instead.</p>
+ <p>If <c>trace_receive/5</c> is not defined <c>trace/5</c> will be called instead.</p>
</desc>
</func>
@@ -487,14 +483,13 @@
</func>
<func>
- <name>Module:trace_garbage_collection(TraceTag, TracerState, Tracee, FirstTraceTerm, SecondTraceTerm, Opts) -> Result</name>
+ <name>Module:trace_garbage_collection(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result</name>
<fsummary>Check if a trace event should be generated.</fsummary>
<type>
<v>TraceTag = <seealso marker="#type-trace_tag_gc">trace_tag_gc()</seealso></v>
<v>TracerState = term()</v>
<v>Tracee = <seealso marker="#type-tracee">tracee()</seealso></v>
<v>FirstTraceTerm = term()</v>
- <v>SecondTraceTerm = term() | undefined</v>
<v>Opts = <seealso marker="#type-trace_opts">trace_opts()</seealso></v>
<v>Result = ok</v>
</type>
@@ -502,7 +497,7 @@
<p>This callback will be called when a tracepoint is triggered and
the <seealso marker="#Module:enabled_garbage_collection/3">Module:enabled_garbage_collection/3</seealso>
callback returned <c>trace</c>.</p>
- <p>If <c>trace_garbage_collection/6</c> is not defined <c>trace/6</c> will be called instead.</p>
+ <p>If <c>trace_garbage_collection/5</c> is not defined <c>trace/5</c> will be called instead.</p>
</desc>
</func>
@@ -543,7 +538,7 @@ ok
<pre>
-module(erl_msg_tracer).
--export([enabled/3, trace/6, load/0]).
+-export([enabled/3, trace/5, load/0]).
load() ->
erlang:load_nif("erl_msg_tracer", []).
@@ -551,7 +546,7 @@ load() ->
enabled(_, _, _) ->
error.
-trace(_, _, _,_, _, _) ->
+trace(_, _, _,_, _) ->
error.
</pre>
<p>erl_msg_tracer.c</p>
@@ -569,7 +564,7 @@ static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ErlNifFunc nif_funcs[] = {
{"enabled", 3, enabled},
- {"trace", 6, trace}
+ {"trace", 5, trace}
};
ERL_NIF_INIT(erl_msg_tracer, nif_funcs, load, NULL, upgrade, unload)
@@ -632,9 +627,8 @@ static ERL_NIF_TERM enabled(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
* argv[0]: TraceTag, should only be 'send'
* argv[1]: TracerState, process to send {argv[2], argv[4]} to
* argv[2]: Tracee
- * argv[3]: Message, ignored
- * argv[4]: Recipient
- * argv[5]: Options, ignored
+ * argv[3]: Recipient
+ * argv[4]: Options, ignored
*/
static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml
index 9287b32fec..e0c3fed0c2 100644
--- a/erts/doc/src/erlang.xml
+++ b/erts/doc/src/erlang.xml
@@ -1802,6 +1802,8 @@ os_prompt% </pre>
<tag>string()</tag>
<item>An Erlang crash dump is produced with <c><anno>Status</anno></c>
as slogan. Then the runtime system exits with status code <c>1</c>.
+ Note that only code points in the range 0-255 may be used
+ and the string will be truncated if longer than 200 characters.
</item>
<tag><c>abort</c></tag>
<item>
@@ -4449,11 +4451,6 @@ os_prompt% </pre>
off heap. This is how messages always have been stored
up until ERTS version 8.0.
</p></item>
- <tag><c>mixed</c></tag>
- <item><p>
- Messages may be placed either on the heap or outside
- of the heap.
- </p></item>
</taglist>
<p>
The default <c>message_queue_data</c> process flag is determined
@@ -4877,7 +4874,7 @@ os_prompt% </pre>
<item>
<p>Returns the current state of the <c>message_queue_data</c>
process flag. <c><anno>MQD</anno></c> is either <c>off_heap</c>,
- <c>on_heap</c>, or <c>mixed</c>. For more information, see the
+ or <c>on_heap</c>. For more information, see the
documentation of
<seealso marker="#process_flag_message_queue_data"><c>process_flag(message_queue_data,
MQD)</c></seealso>.</p>
@@ -5808,7 +5805,7 @@ true</pre>
<item>
<p>Sets the state of the <c>message_queue_data</c> process
flag. <c><anno>MQD</anno></c> should be either <c>off_heap</c>,
- <c>on_heap</c>, or <c>mixed</c>. The default
+ or <c>on_heap</c>. The default
<c>message_queue_data</c> process flag is determined by the
<seealso marker="erl#+hmqd"><c>+hmqd</c></seealso> <c>erl</c>
command line argument. For more information, see the
@@ -7164,7 +7161,7 @@ ok
<tag><marker id="system_info_message_queue_data"><c>message_queue_data</c></marker></tag>
<item>
<p>Returns the default value of the <c>message_queue_data</c>
- process flag which is either <c>off_heap</c>, <c>on_heap</c>, or <c>mixed</c>.
+ process flag which is either <c>off_heap</c>, or <c>on_heap</c>.
This default is set by the <c>erl</c> command line argument
<seealso marker="erl#+hmqd"><c>+hmqd</c></seealso>. For more information on the
<c>message_queue_data</c> process flag, see documentation of
diff --git a/erts/doc/src/init.xml b/erts/doc/src/init.xml
index 84a5aea335..a88a815ef6 100644
--- a/erts/doc/src/init.xml
+++ b/erts/doc/src/init.xml
@@ -178,14 +178,8 @@
<name name="stop" arity="0"/>
<fsummary>Take down an Erlang node smoothly</fsummary>
<desc>
- <p>All applications are taken down smoothly, all code is
- unloaded, and all ports are closed before the system
- terminates. If the <c>-heart</c> command line flag was given,
- the <c>heart</c> program is terminated before the Erlang node
- terminates. Refer to <c>heart(3)</c> for more information.</p>
- <p>To limit the shutdown time, the time <c>init</c> is allowed
- to spend taking down applications, the <c>-shutdown_time</c>
- command line flag should be used.</p>
+ <p>The same as
+ <seealso marker="#stop/1"><c>stop(0)</c></seealso>.</p>
</desc>
</func>
<func>
@@ -241,6 +235,11 @@
marker="kernel:code">code(3)</seealso>.</p>
</item>
+ <tag><c>-epmd_module Module</c></tag>
+ <item>
+ <p>Specifies the module to use for registration and lookup of
+ node names. Defaults to <c>erl_epmd</c>.</p>
+ </item>
<tag><c>-eval Expr</c></tag>
<item>
<p>Scans, parses and evaluates an arbitrary expression
diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names
index 8f65e71531..badd69856e 100644
--- a/erts/emulator/beam/atom.names
+++ b/erts/emulator/beam/atom.names
@@ -228,9 +228,6 @@ atom endian
atom env
atom eof
atom eol
-atom exception_from
-atom exception_trace
-atom extended
atom Eq='=:='
atom Eqeq='=='
atom erl_tracer
@@ -243,6 +240,8 @@ atom ets
atom ETS_TRANSFER='ETS-TRANSFER'
atom event
atom exact_reductions
+atom exception_from
+atom exception_trace
atom exclusive
atom exit_status
atom existing
@@ -251,7 +250,9 @@ atom existing_ports
atom existing
atom exiting
atom exports
+atom extended
atom external
+atom extra
atom false
atom fcgi
atom fd
@@ -389,7 +390,6 @@ atom min_heap_size
atom min_bin_vheap_size
atom minor_version
atom Minus='-'
-atom mixed
atom module
atom module_info
atom monitored_by
diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c
index 40d44dda4c..15e878ba65 100644
--- a/erts/emulator/beam/beam_bif_load.c
+++ b/erts/emulator/beam/beam_bif_load.c
@@ -762,6 +762,11 @@ check_mod_funs(Process *p, ErlOffHeap *off_heap, char *area, size_t area_size)
return 0;
}
+static Uint hfrag_literal_size(Eterm* start, Eterm* end,
+ char* lit_start, Uint lit_size);
+static void hfrag_literal_copy(Eterm **hpp, ErlOffHeap *ohp,
+ Eterm *start, Eterm *end,
+ char *lit_start, Uint lit_size);
static Eterm
check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls)
@@ -842,9 +847,14 @@ check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls
}
/*
- * Message queue can contains funs, but (at least currently) no
+ * Message queue can contains funs, and may contain
* literals. If we got references to this module from the message
- * queue, a GC cannot remove these...
+ * queue.
+ *
+ * If a literal is in the message queue we maka an explicit copy of
+ * and attach it to the heap fragment. Each message needs to be
+ * self contained, we cannot save the literal in the old_heap or
+ * any other heap than the message it self.
*/
erts_smp_proc_lock(rp, ERTS_PROC_LOCK_MSGQ);
@@ -861,15 +871,31 @@ check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls
hfrag = msgp->data.heap_frag;
else
continue;
- for (; hfrag; hfrag = hfrag->next) {
- if (check_mod_funs(rp, &hfrag->off_heap, mod_start, mod_size))
- return am_true;
- /* Should not contain any literals... */
- ASSERT(!any_heap_refs(&hfrag->mem[0],
- &hfrag->mem[hfrag->used_size],
- literals,
- lit_bsize));
- }
+ {
+ ErlHeapFragment *hf;
+ Uint lit_sz;
+ for (hf=hfrag; hf; hf = hf->next) {
+ if (check_mod_funs(rp, &hfrag->off_heap, mod_start, mod_size))
+ return am_true;
+ lit_sz = hfrag_literal_size(&hf->mem[0], &hf->mem[hf->used_size],
+ literals, lit_bsize);
+ }
+ if (lit_sz > 0) {
+ ErlHeapFragment *bp = new_message_buffer(lit_sz);
+ Eterm *hp = bp->mem;
+
+ for (hf=hfrag; hf; hf = hf->next) {
+ hfrag_literal_copy(&hp, &bp->off_heap,
+ &hf->mem[0], &hf->mem[hf->used_size],
+ literals, lit_bsize);
+ hfrag=hf;
+ }
+ /* link new hfrag last */
+ ASSERT(hfrag->next == NULL);
+ hfrag->next = bp;
+ bp->next = NULL;
+ }
+ }
}
while (1) {
@@ -916,29 +942,26 @@ check_process_code(Process* rp, Module* modp, Uint flags, int *redsp, int fcalls
goto try_literal_gc;
}
-#ifdef DEBUG
/*
- * Message buffer fragments should not have any references
- * to literals, and off heap lists should already have
- * been moved into process off heap structure.
+ * Message buffer fragments (matched messages)
+ * - off heap lists should already have been moved into
+ * process off heap structure.
+ * - Check for literals
*/
for (msgp = rp->msg_frag; msgp; msgp = msgp->next) {
- if (msgp->data.attached == ERTS_MSG_COMBINED_HFRAG)
- hfrag = &msgp->hfrag;
- else
- hfrag = msgp->data.heap_frag;
+ hfrag = erts_message_to_heap_frag(msgp);
for (; hfrag; hfrag = hfrag->next) {
Eterm *hp, *hp_end;
ASSERT(!check_mod_funs(rp, &hfrag->off_heap, mod_start, mod_size));
hp = &hfrag->mem[0];
hp_end = &hfrag->mem[hfrag->used_size];
- ASSERT(!any_heap_refs(hp, hp_end, literals, lit_bsize));
+
+ if (any_heap_refs(hp, hp_end, literals, lit_bsize))
+ goto try_literal_gc;
}
}
-#endif
-
return am_false;
try_literal_gc:
@@ -1038,6 +1061,80 @@ any_heap_refs(Eterm* start, Eterm* end, char* mod_start, Uint mod_size)
return 0;
}
+static Uint
+hfrag_literal_size(Eterm* start, Eterm* end, char* lit_start, Uint lit_size)
+{
+ Eterm* p;
+ Eterm val;
+ Uint sz = 0;
+
+ for (p = start; p < end; p++) {
+ val = *p;
+ switch (primary_tag(val)) {
+ case TAG_PRIMARY_BOXED:
+ case TAG_PRIMARY_LIST:
+ if (ErtsInArea(val, lit_start, lit_size)) {
+ sz += size_object(val);
+ }
+ break;
+ case TAG_PRIMARY_HEADER:
+ if (!header_is_transparent(val)) {
+ Eterm* new_p;
+ if (header_is_bin_matchstate(val)) {
+ ErlBinMatchState *ms = (ErlBinMatchState*) p;
+ ErlBinMatchBuffer *mb = &(ms->mb);
+ if (ErtsInArea(mb->orig, lit_start, lit_size)) {
+ sz += size_object(mb->orig);
+ }
+ }
+ new_p = p + thing_arityval(val);
+ ASSERT(start <= new_p && new_p < end);
+ p = new_p;
+ }
+ }
+ }
+ return sz;
+}
+
+static void
+hfrag_literal_copy(Eterm **hpp, ErlOffHeap *ohp,
+ Eterm *start, Eterm *end,
+ char *lit_start, Uint lit_size) {
+ Eterm* p;
+ Eterm val;
+ Uint sz;
+
+ for (p = start; p < end; p++) {
+ val = *p;
+ switch (primary_tag(val)) {
+ case TAG_PRIMARY_BOXED:
+ case TAG_PRIMARY_LIST:
+ if (ErtsInArea(val, lit_start, lit_size)) {
+ sz = size_object(val);
+ val = copy_struct(val, sz, hpp, ohp);
+ *p = val;
+ }
+ break;
+ case TAG_PRIMARY_HEADER:
+ if (!header_is_transparent(val)) {
+ Eterm* new_p;
+ /* matchstate in message, not possible. */
+ if (header_is_bin_matchstate(val)) {
+ ErlBinMatchState *ms = (ErlBinMatchState*) p;
+ ErlBinMatchBuffer *mb = &(ms->mb);
+ if (ErtsInArea(mb->orig, lit_start, lit_size)) {
+ sz = size_object(mb->orig);
+ mb->orig = copy_struct(mb->orig, sz, hpp, ohp);
+ }
+ }
+ new_p = p + thing_arityval(val);
+ ASSERT(start <= new_p && new_p < end);
+ p = new_p;
+ }
+ }
+ }
+}
+
#undef in_area
#ifdef ERTS_SMP
diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c
index 2a3bd4afe5..b18910e2c7 100644
--- a/erts/emulator/beam/bif.c
+++ b/erts/emulator/beam/bif.c
@@ -917,9 +917,6 @@ BIF_RETTYPE spawn_opt_1(BIF_ALIST_1)
goto error;
} else if (arg == am_message_queue_data) {
switch (val) {
- case am_mixed:
- so.flags &= ~(SPO_OFF_HEAP_MSGQ|SPO_ON_HEAP_MSGQ);
- break;
case am_on_heap:
so.flags &= ~SPO_OFF_HEAP_MSGQ;
so.flags |= SPO_ON_HEAP_MSGQ;
@@ -3836,59 +3833,11 @@ BIF_RETTYPE display_nl_0(BIF_ALIST_0)
/**********************************************************************/
-/* stop the system */
-/* ARGSUSED */
-BIF_RETTYPE halt_0(BIF_ALIST_0)
-{
- VERBOSE(DEBUG_SYSTEM,("System halted by BIF halt()\n"));
- erts_halt(0);
- ERTS_BIF_YIELD1(bif_export[BIF_halt_1], BIF_P, am_undefined);
-}
-
-/**********************************************************************/
#define HALT_MSG_SIZE 200
-static char halt_msg[HALT_MSG_SIZE];
-
-/* stop the system with exit code */
-/* ARGSUSED */
-BIF_RETTYPE halt_1(BIF_ALIST_1)
-{
- Uint code;
-
- if (term_to_Uint_mask(BIF_ARG_1, &code)) {
- int pos_int_code = (int) (code & INT_MAX);
- VERBOSE(DEBUG_SYSTEM,("System halted by BIF halt(%T)\n", BIF_ARG_1));
- erts_halt(pos_int_code);
- ERTS_BIF_YIELD1(bif_export[BIF_halt_1], BIF_P, am_undefined);
- }
- else if (ERTS_IS_ATOM_STR("abort", BIF_ARG_1)) {
- VERBOSE(DEBUG_SYSTEM,("System halted by BIF halt(%T)\n", BIF_ARG_1));
- erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
- erts_exit(ERTS_ABORT_EXIT, "");
- }
- else if (is_string(BIF_ARG_1) || BIF_ARG_1 == NIL) {
- Sint i;
-
- if ((i = intlist_to_buf(BIF_ARG_1, halt_msg, HALT_MSG_SIZE-1)) < 0) {
- goto error;
- }
- halt_msg[i] = '\0';
- VERBOSE(DEBUG_SYSTEM,("System halted by BIF halt(%T)\n", BIF_ARG_1));
- erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
- erts_exit(ERTS_DUMP_EXIT, "%s\n", halt_msg);
- }
- else
- goto error;
- return NIL; /* Pedantic (lint does not know about erts_exit) */
- error:
- BIF_ERROR(BIF_P, BADARG);
-}
-
-/**********************************************************************/
+static char halt_msg[HALT_MSG_SIZE+1];
/* stop the system with exit code and flags */
-/* ARGSUSED */
BIF_RETTYPE halt_2(BIF_ALIST_2)
{
Uint code;
@@ -3924,7 +3873,7 @@ BIF_RETTYPE halt_2(BIF_ALIST_2)
("System halted by BIF halt(%T, %T)\n", BIF_ARG_1, BIF_ARG_2));
if (flush) {
erts_halt(pos_int_code);
- ERTS_BIF_YIELD1(bif_export[BIF_halt_1], BIF_P, am_undefined);
+ ERTS_BIF_YIELD2(bif_export[BIF_halt_2], BIF_P, am_undefined, am_undefined);
}
else {
erts_smp_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
@@ -3940,9 +3889,12 @@ BIF_RETTYPE halt_2(BIF_ALIST_2)
else if (is_string(BIF_ARG_1) || BIF_ARG_1 == NIL) {
Sint i;
- if ((i = intlist_to_buf(BIF_ARG_1, halt_msg, HALT_MSG_SIZE-1)) < 0) {
- goto error;
- }
+ if ((i = intlist_to_buf(BIF_ARG_1, halt_msg, HALT_MSG_SIZE)) == -1) {
+ goto error;
+ }
+ if (i == -2) /* truncated string */
+ i = HALT_MSG_SIZE;
+ ASSERT(i >= 0 && i <= HALT_MSG_SIZE);
halt_msg[i] = '\0';
VERBOSE(DEBUG_SYSTEM,
("System halted by BIF halt(%T, %T)\n", BIF_ARG_1, BIF_ARG_2));
diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index 872f0f9b2a..065018514a 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -72,8 +72,6 @@ bif erlang:get/1
bif erlang:get_keys/1
bif erlang:group_leader/0
bif erlang:group_leader/2
-bif erlang:halt/0
-bif erlang:halt/1
bif erlang:halt/2
bif erlang:phash/2
bif erlang:phash2/1
diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c
index 2e195db0ee..b410578d37 100644
--- a/erts/emulator/beam/erl_bif_info.c
+++ b/erts/emulator/beam/erl_bif_info.c
@@ -1565,9 +1565,6 @@ process_info_aux(Process *BIF_P,
case F_ON_HEAP_MSGQ:
res = am_on_heap;
break;
- case 0:
- res = am_mixed;
- break;
default:
res = am_error;
ERTS_INTERNAL_ERROR("Inconsistent message queue management state");
@@ -2809,8 +2806,6 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
BIF_RET(am_off_heap);
case SPO_ON_HEAP_MSGQ:
BIF_RET(am_on_heap);
- case 0:
- BIF_RET(am_mixed);
default:
ERTS_INTERNAL_ERROR("Inconsistent message queue management state");
BIF_RET(am_error);
diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c
index d740b2baec..c7bbbd5ca0 100644
--- a/erts/emulator/beam/erl_gc.c
+++ b/erts/emulator/beam/erl_gc.c
@@ -2279,10 +2279,7 @@ move_msgq_to_heap(Process *p)
}
else {
- if (mp->data.attached == ERTS_MSG_COMBINED_HFRAG)
- bp = &mp->hfrag;
- else
- bp = mp->data.heap_frag;
+ bp = erts_message_to_heap_frag(mp);
if (bp->next)
erts_move_multi_frags(&factory.hp, factory.off_heap, bp,
@@ -2296,18 +2293,13 @@ move_msgq_to_heap(Process *p)
free_message_buffer(bp);
}
else {
- ErtsMessage *tmp = erts_alloc_message(0, NULL);
- sys_memcpy((void *) tmp->m, (void *) mp->m,
- sizeof(Eterm)*ERL_MESSAGE_REF_ARRAY_SZ);
- tmp->next = mp->next;
- if (p->msg.save == &mp->next)
- p->msg.save = &tmp->next;
- if (p->msg.last == &mp->next)
- p->msg.last = &tmp->next;
- *mpp = tmp;
+ ErtsMessage *new_mp = erts_alloc_message(0, NULL);
+ sys_memcpy((void *) new_mp->m, (void *) mp->m,
+ sizeof(Eterm)*ERL_MESSAGE_REF_ARRAY_SZ);
+ erts_msgq_replace_msg_ref(&p->msg, new_mp, mpp);
mp->next = NULL;
erts_cleanup_messages(mp);
- mp = tmp;
+ mp = new_mp;
}
}
@@ -3304,11 +3296,7 @@ within2(Eterm *ptr, Process *p, Eterm *real_htop)
while (mp) {
- if (mp->data.attached == ERTS_MSG_COMBINED_HFRAG)
- bp = &mp->hfrag;
- else
- bp = mp->data.heap_frag;
-
+ bp = erts_message_to_heap_frag(mp);
mp = mp->next;
search_heap_frags:
diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c
index 0649fb68de..fbdafec4ef 100644
--- a/erts/emulator/beam/erl_init.c
+++ b/erts/emulator/beam/erl_init.c
@@ -585,7 +585,7 @@ void erts_usage(void)
erts_fprintf(stderr, "-hpds size initial process dictionary size (default %d)\n",
erts_pd_initial_size);
erts_fprintf(stderr, "-hmqd val set default message queue data flag for processes,\n");
- erts_fprintf(stderr, " valid values are: off_heap | on_heap | mixed\n");
+ erts_fprintf(stderr, " valid values are: off_heap | on_heap\n");
/* erts_fprintf(stderr, "-i module set the boot module (default init)\n"); */
@@ -1526,9 +1526,7 @@ erl_start(int argc, char **argv)
erts_pd_initial_size));
} else if (has_prefix("mqd", sub_param)) {
arg = get_arg(sub_param+3, argv[i+1], &i);
- if (sys_strcmp(arg, "mixed") == 0)
- erts_default_spo_flags &= ~(SPO_ON_HEAP_MSGQ|SPO_OFF_HEAP_MSGQ);
- else if (sys_strcmp(arg, "on_heap") == 0) {
+ if (sys_strcmp(arg, "on_heap") == 0) {
erts_default_spo_flags &= ~SPO_OFF_HEAP_MSGQ;
erts_default_spo_flags |= SPO_ON_HEAP_MSGQ;
}
diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c
index 579f6e427d..ac7b9d6606 100644
--- a/erts/emulator/beam/erl_message.c
+++ b/erts/emulator/beam/erl_message.c
@@ -1123,11 +1123,9 @@ erts_change_message_queue_management(Process *c_p, Eterm new_state)
break;
case am_on_heap:
c_p->flags |= F_ON_HEAP_MSGQ;
+ c_p->flags &= ~F_OFF_HEAP_MSGQ;
erts_smp_atomic32_read_bor_nob(&c_p->state,
ERTS_PSFLG_ON_HEAP_MSGQ);
- /* fall through */
- case am_mixed:
- c_p->flags &= ~F_OFF_HEAP_MSGQ;
/*
* We are not allowed to clear ERTS_PSFLG_OFF_HEAP_MSGQ
* if a off heap change is ongoing. It will be adjusted
@@ -1151,11 +1149,6 @@ erts_change_message_queue_management(Process *c_p, Eterm new_state)
switch (new_state) {
case am_on_heap:
break;
- case am_mixed:
- c_p->flags &= ~F_ON_HEAP_MSGQ;
- erts_smp_atomic32_read_band_nob(&c_p->state,
- ~ERTS_PSFLG_ON_HEAP_MSGQ);
- break;
case am_off_heap:
c_p->flags &= ~F_ON_HEAP_MSGQ;
erts_smp_atomic32_read_band_nob(&c_p->state,
@@ -1167,25 +1160,6 @@ erts_change_message_queue_management(Process *c_p, Eterm new_state)
}
break;
- case 0:
- res = am_mixed;
-
- switch (new_state) {
- case am_mixed:
- break;
- case am_on_heap:
- c_p->flags |= F_ON_HEAP_MSGQ;
- erts_smp_atomic32_read_bor_nob(&c_p->state,
- ERTS_PSFLG_ON_HEAP_MSGQ);
- break;
- case am_off_heap:
- goto change_to_off_heap;
- default:
- res = THE_NON_VALUE; /* badarg */
- break;
- }
- break;
-
default:
res = am_error;
ERTS_INTERNAL_ERROR("Inconsistent message queue management state");
@@ -1371,10 +1345,10 @@ erts_prep_msgq_for_inspection(Process *c_p, Process *rp,
mpp = i == 0 ? &rp->msg.first : &mip[i-1].msgp->next;
- if (rp->msg.save == &bad_mp->next)
- rp->msg.save = mpp;
- if (rp->msg.last == &bad_mp->next)
- rp->msg.last = mpp;
+ ASSERT((*mpp)->next == bad_mp);
+
+ erts_msgq_update_internal_pointers(&rp->msg, mpp, &bad_mp->next);
+
mp = mp->next;
*mpp = mp;
rp->msg.len--;
@@ -1411,12 +1385,7 @@ erts_prep_msgq_for_inspection(Process *c_p, Process *rp,
sys_memcpy((void *) tmp->m, (void *) mp->m,
sizeof(Eterm)*ERL_MESSAGE_REF_ARRAY_SZ);
mpp = i == 0 ? &rp->msg.first : &mip[i-1].msgp->next;
- tmp->next = mp->next;
- if (rp->msg.save == &mp->next)
- rp->msg.save = &tmp->next;
- if (rp->msg.last == &mp->next)
- rp->msg.last = &tmp->next;
- *mpp = tmp;
+ erts_msgq_replace_msg_ref(&rp->msg, tmp, mpp);
erts_save_message_in_proc(rp, mp);
mp = tmp;
}
diff --git a/erts/emulator/beam/erl_message.h b/erts/emulator/beam/erl_message.h
index 4493df1c1a..6df969367b 100644
--- a/erts/emulator/beam/erl_message.h
+++ b/erts/emulator/beam/erl_message.h
@@ -366,9 +366,19 @@ ERTS_GLB_FORCE_INLINE ErtsMessage *erts_shrink_message(ErtsMessage *mp, Uint sz,
ERTS_GLB_FORCE_INLINE void erts_free_message(ErtsMessage *mp);
ERTS_GLB_INLINE Uint erts_used_frag_sz(const ErlHeapFragment*);
ERTS_GLB_INLINE Uint erts_msg_attached_data_size(ErtsMessage *msg);
+ERTS_GLB_INLINE void erts_msgq_update_internal_pointers(ErlMessageQueue *msgq,
+ ErtsMessage **newpp,
+ ErtsMessage **oldpp);
+ERTS_GLB_INLINE void erts_msgq_replace_msg_ref(ErlMessageQueue *msgq,
+ ErtsMessage *newp,
+ ErtsMessage **oldpp);
#define ERTS_MSG_COMBINED_HFRAG ((void *) 0x1)
+#define erts_message_to_heap_frag(MP) \
+ (((MP)->data.attached == ERTS_MSG_COMBINED_HFRAG) ? \
+ &(MP)->hfrag : (MP)->data.heap_frag)
+
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
ERTS_GLB_FORCE_INLINE ErtsMessage *erts_alloc_message(Uint sz, Eterm **hpp)
@@ -449,10 +459,7 @@ ERTS_GLB_INLINE Uint erts_msg_attached_data_size(ErtsMessage *msg)
ASSERT(msg->data.attached);
if (is_value(ERL_MESSAGE_TERM(msg))) {
ErlHeapFragment *bp;
- if (msg->data.attached == ERTS_MSG_COMBINED_HFRAG)
- bp = &msg->hfrag;
- else
- bp = msg->data.heap_frag;
+ bp = erts_message_to_heap_frag(msg);
return erts_used_frag_sz(bp);
}
else if (msg->data.dist_ext->heap_size < 0)
@@ -467,6 +474,29 @@ ERTS_GLB_INLINE Uint erts_msg_attached_data_size(ErtsMessage *msg)
return sz;
}
}
+
+ERTS_GLB_INLINE void
+erts_msgq_update_internal_pointers(ErlMessageQueue *msgq,
+ ErtsMessage **newpp,
+ ErtsMessage **oldpp)
+{
+ if (msgq->save == oldpp)
+ msgq->save = newpp;
+ if (msgq->last == oldpp)
+ msgq->last = newpp;
+ if (msgq->saved_last == oldpp)
+ msgq->saved_last = newpp;
+}
+
+ERTS_GLB_INLINE void
+erts_msgq_replace_msg_ref(ErlMessageQueue *msgq, ErtsMessage *newp, ErtsMessage **oldpp)
+{
+ ErtsMessage *oldp = *oldpp;
+ newp->next = oldp->next;
+ erts_msgq_update_internal_pointers(msgq, &newp->next, &oldp->next);
+ *oldpp = newp;
+}
+
#endif
#endif
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index 606b73c7b5..2bbb8e3c91 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -623,10 +623,28 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
}
} else {
Uint sz = size_object(msg);
+ ErlOffHeap *ohp;
Eterm *hp;
- mp = erts_alloc_message(sz, &hp);
- msg = copy_struct(msg, sz, &hp, &mp->hfrag.off_heap);
- ASSERT(hp == mp->hfrag.mem+mp->hfrag.used_size);
+ if (env && !env->tracee) {
+ flush_env(env);
+ mp = erts_alloc_message_heap(rp, &rp_locks, sz, &hp, &ohp);
+ cache_env(env);
+ }
+ else {
+ erts_aint_t state = erts_smp_atomic32_read_nob(&rp->state);
+ if (state & ERTS_PSFLG_OFF_HEAP_MSGQ) {
+ mp = erts_alloc_message(sz, &hp);
+ ohp = sz == 0 ? NULL : &mp->hfrag.off_heap;
+ }
+ else {
+ ErlHeapFragment *bp = new_message_buffer(sz);
+ mp = erts_alloc_message(0, NULL);
+ mp->data.heap_frag = bp;
+ hp = bp->mem;
+ ohp = &bp->off_heap;
+ }
+ }
+ msg = copy_struct(msg, sz, &hp, ohp);
}
ERL_MESSAGE_TERM(mp) = msg;
diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c
index a853ec585b..f8cbe60e76 100644
--- a/erts/emulator/beam/erl_process.c
+++ b/erts/emulator/beam/erl_process.c
@@ -149,7 +149,7 @@ extern BeamInstr beam_apply[];
extern BeamInstr beam_exit[];
extern BeamInstr beam_continue_exit[];
-int ERTS_WRITE_UNLIKELY(erts_default_spo_flags) = 0;
+int ERTS_WRITE_UNLIKELY(erts_default_spo_flags) = SPO_ON_HEAP_MSGQ;
int ERTS_WRITE_UNLIKELY(erts_eager_check_io) = 1;
int ERTS_WRITE_UNLIKELY(erts_sched_compact_load);
int ERTS_WRITE_UNLIKELY(erts_sched_balance_util) = 0;
@@ -11206,6 +11206,8 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
flags |= F_ON_HEAP_MSGQ;
}
+ ASSERT((flags & F_ON_HEAP_MSGQ) || (flags & F_OFF_HEAP_MSGQ));
+
if (!rq)
rq = erts_get_runq_proc(parent);
@@ -11218,6 +11220,11 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
goto error;
}
+ ASSERT((erts_smp_atomic32_read_nob(&p->state)
+ & ERTS_PSFLG_ON_HEAP_MSGQ)
+ || (erts_smp_atomic32_read_nob(&p->state)
+ & ERTS_PSFLG_OFF_HEAP_MSGQ));
+
#ifdef BM_COUNTERS
processes_busy++;
#endif
diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c
index ca001fc156..3dca58d60b 100644
--- a/erts/emulator/beam/erl_trace.c
+++ b/erts/emulator/beam/erl_trace.c
@@ -2625,7 +2625,7 @@ static void init_tracer_template(ErtsTracerNif *tnif) {
/* default tracer functions */
tnif->tracers[TRACE_FUN_DEFAULT].name = "trace";
- tnif->tracers[TRACE_FUN_DEFAULT].arity = 6;
+ tnif->tracers[TRACE_FUN_DEFAULT].arity = 5;
tnif->tracers[TRACE_FUN_DEFAULT].cb = NULL;
tnif->tracers[TRACE_FUN_ENABLED].name = "enabled";
@@ -2634,35 +2634,35 @@ static void init_tracer_template(ErtsTracerNif *tnif) {
/* specific tracer functions */
tnif->tracers[TRACE_FUN_T_SEND].name = "trace_send";
- tnif->tracers[TRACE_FUN_T_SEND].arity = 6;
+ tnif->tracers[TRACE_FUN_T_SEND].arity = 5;
tnif->tracers[TRACE_FUN_T_SEND].cb = NULL;
tnif->tracers[TRACE_FUN_T_RECEIVE].name = "trace_receive";
- tnif->tracers[TRACE_FUN_T_RECEIVE].arity = 6;
+ tnif->tracers[TRACE_FUN_T_RECEIVE].arity = 5;
tnif->tracers[TRACE_FUN_T_RECEIVE].cb = NULL;
tnif->tracers[TRACE_FUN_T_CALL].name = "trace_call";
- tnif->tracers[TRACE_FUN_T_CALL].arity = 6;
+ tnif->tracers[TRACE_FUN_T_CALL].arity = 5;
tnif->tracers[TRACE_FUN_T_CALL].cb = NULL;
tnif->tracers[TRACE_FUN_T_SCHED_PROC].name = "trace_running_procs";
- tnif->tracers[TRACE_FUN_T_SCHED_PROC].arity = 6;
+ tnif->tracers[TRACE_FUN_T_SCHED_PROC].arity = 5;
tnif->tracers[TRACE_FUN_T_SCHED_PROC].cb = NULL;
tnif->tracers[TRACE_FUN_T_SCHED_PORT].name = "trace_running_ports";
- tnif->tracers[TRACE_FUN_T_SCHED_PORT].arity = 6;
+ tnif->tracers[TRACE_FUN_T_SCHED_PORT].arity = 5;
tnif->tracers[TRACE_FUN_T_SCHED_PORT].cb = NULL;
tnif->tracers[TRACE_FUN_T_GC].name = "trace_garbage_collection";
- tnif->tracers[TRACE_FUN_T_GC].arity = 6;
+ tnif->tracers[TRACE_FUN_T_GC].arity = 5;
tnif->tracers[TRACE_FUN_T_GC].cb = NULL;
tnif->tracers[TRACE_FUN_T_PROCS].name = "trace_procs";
- tnif->tracers[TRACE_FUN_T_PROCS].arity = 6;
+ tnif->tracers[TRACE_FUN_T_PROCS].arity = 5;
tnif->tracers[TRACE_FUN_T_PROCS].cb = NULL;
tnif->tracers[TRACE_FUN_T_PORTS].name = "trace_ports";
- tnif->tracers[TRACE_FUN_T_PORTS].arity = 6;
+ tnif->tracers[TRACE_FUN_T_PORTS].arity = 5;
tnif->tracers[TRACE_FUN_T_PORTS].cb = NULL;
/* specific enabled functions */
@@ -2834,10 +2834,12 @@ send_to_tracer_nif_raw(Process *c_p, Process *tracee,
Eterm tag, Eterm msg, Eterm extra, Eterm pam_result)
{
if (tnif || (tnif = lookup_tracer_nif(tracer)) != NULL) {
-#define MAP_SIZE 3
- Eterm argv[6], local_heap[3+MAP_SIZE /* values */ + (MAP_SIZE+1 /* keys */)];
+#define MAP_SIZE 4
+ Eterm argv[5], local_heap[3+MAP_SIZE /* values */ + (MAP_SIZE+1 /* keys */)];
flatmap_t *map = (flatmap_t*)(local_heap+(MAP_SIZE+1));
Eterm *map_values = flatmap_get_values(map);
+ Eterm *map_keys = local_heap + 1;
+ Uint map_elem_count = 0;
topt = (tnif->tracers[topt].cb) ? topt : TRACE_FUN_DEFAULT;
ASSERT(topt < NIF_TRACER_TYPES);
@@ -2846,31 +2848,40 @@ send_to_tracer_nif_raw(Process *c_p, Process *tracee,
argv[1] = ERTS_TRACER_STATE(tracer);
argv[2] = t_p_id;
argv[3] = msg;
- argv[4] = extra == THE_NON_VALUE ? am_undefined : extra;
- argv[5] = make_flatmap(map);
+ argv[4] = make_flatmap(map);
map->thing_word = MAP_HEADER_FLATMAP;
- map->size = MAP_SIZE;
- map->keys = TUPLE3(local_heap, am_match_spec_result, am_scheduler_id, am_timestamp);
-
- *map_values++ = pam_result;
- if (tracee_flags & F_TRACE_SCHED_NO)
- *map_values++ = make_small(erts_get_scheduler_id());
- else
- *map_values++ = am_undefined;
+
+ if (extra != THE_NON_VALUE) {
+ map_keys[map_elem_count] = am_extra;
+ map_values[map_elem_count++] = extra;
+ }
+
+ if (pam_result != am_true) {
+ map_keys[map_elem_count] = am_match_spec_result;
+ map_values[map_elem_count++] = pam_result;
+ }
+
+ if (tracee_flags & F_TRACE_SCHED_NO) {
+ map_keys[map_elem_count] = am_scheduler_id;
+ map_values[map_elem_count++] = make_small(erts_get_scheduler_id());
+ }
+ map_keys[map_elem_count] = am_timestamp;
if (tracee_flags & F_NOW_TS)
#ifdef HAVE_ERTS_NOW_CPU
if (erts_cpu_timestamp)
- *map_values++ = am_cpu_timestamp;
+ map_values[map_elem_count++] = am_cpu_timestamp;
else
#endif
- *map_values++ = am_timestamp;
+ map_values[map_elem_count++] = am_timestamp;
else if (tracee_flags & F_STRICT_MON_TS)
- *map_values++ = am_strict_monotonic;
+ map_values[map_elem_count++] = am_strict_monotonic;
else if (tracee_flags & F_MON_TS)
- *map_values++ = am_monotonic;
- else
- *map_values++ = am_undefined;
+ map_values[map_elem_count++] = am_monotonic;
+
+ map->size = map_elem_count;
+ map->keys = make_tuple(local_heap);
+ local_heap[0] = make_arityval(map_elem_count);
#undef MAP_SIZE
erts_nif_call_function(c_p, tracee ? tracee : c_p,
diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c
index 0377f6cb5e..01df5476db 100644
--- a/erts/emulator/beam/io.c
+++ b/erts/emulator/beam/io.c
@@ -938,18 +938,32 @@ int erts_port_handle_xports(Port *prt)
** -2 on type error
*/
-#define SET_VEC(iov, bv, bin, ptr, len, vlen) do { \
- (iov)->iov_base = (ptr); \
- (iov)->iov_len = (len); \
- if (sizeof((iov)->iov_len) < sizeof(len) \
- /* Check if (len) overflowed (iov)->iov_len */ \
- && (iov)->iov_len != (len)) { \
- goto L_overflow; \
- } \
- *(bv)++ = (bin); \
- (iov)++; \
- (vlen)++; \
-} while(0)
+#ifdef DEBUG
+#define MAX_SYSIOVEC_IOVLEN (1ull << (32 - 1))
+#else
+#define MAX_SYSIOVEC_IOVLEN (1ull << (sizeof(((SysIOVec*)0)->iov_len) * 8 - 1))
+#endif
+
+static ERTS_INLINE void
+io_list_to_vec_set_vec(SysIOVec **iov, ErlDrvBinary ***binv,
+ ErlDrvBinary *bin, byte *ptr, Uint len,
+ int *vlen)
+{
+ while (len > MAX_SYSIOVEC_IOVLEN) {
+ (*iov)->iov_base = ptr;
+ (*iov)->iov_len = MAX_SYSIOVEC_IOVLEN;
+ ptr += MAX_SYSIOVEC_IOVLEN;
+ len -= MAX_SYSIOVEC_IOVLEN;
+ (*iov)++;
+ (*vlen)++;
+ *(*binv)++ = bin;
+ }
+ (*iov)->iov_base = ptr;
+ (*iov)->iov_len = len;
+ *(*binv)++ = bin;
+ (*iov)++;
+ (*vlen)++;
+}
static int
io_list_to_vec(Eterm obj, /* io-list */
@@ -960,11 +974,11 @@ io_list_to_vec(Eterm obj, /* io-list */
{
DECLARE_ESTACK(s);
Eterm* objp;
- char *buf = cbin->orig_bytes;
+ byte *buf = (byte*)cbin->orig_bytes;
Uint len = cbin->orig_size;
Uint csize = 0;
int vlen = 0;
- char* cptr = buf;
+ byte* cptr = buf;
goto L_jump_start; /* avoid push */
@@ -1032,15 +1046,17 @@ io_list_to_vec(Eterm obj, /* io-list */
len -= size;
} else {
if (csize != 0) {
- SET_VEC(iov, binv, cbin, cptr, csize, vlen);
+ io_list_to_vec_set_vec(&iov, &binv, cbin,
+ cptr, csize, &vlen);
cptr = buf;
csize = 0;
}
if (pb->flags) {
erts_emasculate_writable_binary(pb);
}
- SET_VEC(iov, binv, Binary2ErlDrvBinary(pb->val),
- pb->bytes+offset, size, vlen);
+ io_list_to_vec_set_vec(
+ &iov, &binv, Binary2ErlDrvBinary(pb->val),
+ pb->bytes+offset, size, &vlen);
}
} else {
ErlHeapBin* hb = (ErlHeapBin *) bptr;
@@ -1060,7 +1076,7 @@ io_list_to_vec(Eterm obj, /* io-list */
}
if (csize != 0) {
- SET_VEC(iov, binv, cbin, cptr, csize, vlen);
+ io_list_to_vec_set_vec(&iov, &binv, cbin, cptr, csize, &vlen);
}
DESTROY_ESTACK(s);
@@ -1086,10 +1102,13 @@ do { \
if (_bitsize != 0) goto L_type_error; \
if (thing_subtag(*binary_val(_real)) == REFC_BINARY_SUBTAG && \
_bitoffs == 0) { \
- b_size += _size; \
+ b_size += _size; \
if (b_size < _size) goto L_overflow_error; \
in_clist = 0; \
- v_size++; \
+ v_size++; \
+ /* If iov_len is smaller then Uint we split the binary into*/ \
+ /* multiple smaller (2GB) elements in the iolist.*/ \
+ v_size += _size / MAX_SYSIOVEC_IOVLEN; \
if (_size >= ERL_SMALL_IO_BIN_LIMIT) { \
p_in_clist = 0; \
p_v_size++; \
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index cedc88e5fe..f0418446a8 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -3893,8 +3893,10 @@ void bin_write(int to, void *to_arg, byte* buf, size_t sz)
}
/* Fill buf with the contents of bytelist list
- return number of chars in list or -1 for error */
-
+ * return number of chars in list
+ * or -1 for type error
+ * or -2 for not enough buffer space (buffer contains truncated result)
+ */
Sint
intlist_to_buf(Eterm list, char *buf, Sint len)
{
@@ -3917,7 +3919,7 @@ intlist_to_buf(Eterm list, char *buf, Sint len)
return -1;
listptr = list_val(*(listptr + 1));
}
- return -1; /* not enough space */
+ return -2; /* not enough space */
}
/*
diff --git a/erts/emulator/hipe/hipe_bif0.c b/erts/emulator/hipe/hipe_bif0.c
index 4063cbf306..58b5be3906 100644
--- a/erts/emulator/hipe/hipe_bif0.c
+++ b/erts/emulator/hipe/hipe_bif0.c
@@ -418,6 +418,8 @@ BIF_RETTYPE hipe_bifs_enter_code_2(BIF_ALIST_2)
BIF_RET(make_tuple(hp));
}
+#define IS_POWER_OF_TWO(Val) (((Val) > 0) && (((Val) & ((Val)-1)) == 0))
+
/*
* Allocate memory for arbitrary non-Erlang data.
*/
@@ -427,16 +429,18 @@ BIF_RETTYPE hipe_bifs_alloc_data_2(BIF_ALIST_2)
void *block;
if (is_not_small(BIF_ARG_1) || is_not_small(BIF_ARG_2) ||
- (align = unsigned_val(BIF_ARG_1),
- align != sizeof(long) && align != sizeof(double)))
+ (align = unsigned_val(BIF_ARG_1), !IS_POWER_OF_TWO(align)))
BIF_ERROR(BIF_P, BADARG);
nrbytes = unsigned_val(BIF_ARG_2);
if (nrbytes == 0)
BIF_RET(make_small(0));
block = erts_alloc(ERTS_ALC_T_HIPE, nrbytes);
- if ((unsigned long)block & (align-1))
+ if ((unsigned long)block & (align-1)) {
fprintf(stderr, "%s: erts_alloc(%lu) returned %p which is not %lu-byte aligned\r\n",
__FUNCTION__, (unsigned long)nrbytes, block, (unsigned long)align);
+ erts_free(ERTS_ALC_T_HIPE, block);
+ BIF_ERROR(BIF_P, EXC_NOTSUP);
+ }
BIF_RET(address_to_term(block, BIF_P));
}
diff --git a/erts/emulator/hipe/hipe_x86.c b/erts/emulator/hipe/hipe_x86.c
index 3d25646231..5f6c8c200e 100644
--- a/erts/emulator/hipe/hipe_x86.c
+++ b/erts/emulator/hipe/hipe_x86.c
@@ -37,7 +37,7 @@
void hipe_patch_load_fe(Uint32 *address, Uint32 value)
{
/* address points to a disp32 or imm32 operand */
- *address = value;
+ *address += value;
}
int hipe_patch_insn(void *address, Uint32 value, Eterm type)
@@ -54,7 +54,7 @@ int hipe_patch_insn(void *address, Uint32 value, Eterm type)
default:
return -1;
}
- *(Uint32*)address = value;
+ *(Uint32*)address += value;
return 0;
}
diff --git a/erts/emulator/nifs/common/erl_tracer_nif.c b/erts/emulator/nifs/common/erl_tracer_nif.c
index 6dddc80607..c0cc48ff42 100644
--- a/erts/emulator/nifs/common/erl_tracer_nif.c
+++ b/erts/emulator/nifs/common/erl_tracer_nif.c
@@ -45,7 +45,7 @@ static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ErlNifFunc nif_funcs[] = {
{"enabled", 3, enabled},
- {"trace", 6, trace}
+ {"trace", 5, trace}
};
@@ -57,6 +57,7 @@ ERL_NIF_INIT(erl_tracer, nif_funcs, load, NULL, upgrade, unload)
ATOM_DECL(cpu_timestamp); \
ATOM_DECL(discard); \
ATOM_DECL(exception_from); \
+ ATOM_DECL(extra); \
ATOM_DECL(match_spec_result); \
ATOM_DECL(monotonic); \
ATOM_DECL(ok); \
@@ -76,8 +77,7 @@ ERL_NIF_INIT(erl_tracer, nif_funcs, load, NULL, upgrade, unload)
ATOM_DECL(gc_minor_start); \
ATOM_DECL(gc_minor_end); \
ATOM_DECL(gc_major_start); \
- ATOM_DECL(gc_major_end); \
- ATOM_DECL(undefined);
+ ATOM_DECL(gc_major_end);
#define ATOM_DECL(A) static ERL_NIF_TERM atom_##A
ATOMS
@@ -154,11 +154,6 @@ static ERL_NIF_TERM enabled(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
Tracee :: pid() || port() || undefined,
Msg :: term(),
Opts :: map()) -> ignored().
- -spec trace(Tag :: atom(), TracerState :: pid() | port(),
- Tracee :: pid() || port() || undefined,
- Msg :: term(),
- Extra :: term(),
- Opts :: map()) -> ignored().
*/
static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
@@ -167,7 +162,8 @@ static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
ErlNifPort to_port;
size_t tt_sz = 0;
int is_port = 0;
- ASSERT(argc == 6);
+ size_t opts_sz = 0;
+ ASSERT(argc == 5);
if (!enif_get_local_pid(env, argv[1], &to_pid)) {
if (!enif_get_local_port(env, argv[1], &to_port)) {
@@ -179,12 +175,17 @@ static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
is_port = 1;
}
- if (!enif_is_identical(argv[4], atom_undefined)) {
+ opts = argv[4];
+
+ if (!enif_get_map_size(env, opts, &opts_sz))
+ opts_sz = 0;
+
+ if (opts_sz && enif_get_map_value(env, opts, atom_extra, &value)) {
tt[tt_sz++] = atom_trace;
tt[tt_sz++] = argv[2];
tt[tt_sz++] = argv[0];
tt[tt_sz++] = argv[3];
- tt[tt_sz++] = argv[4];
+ tt[tt_sz++] = value;
} else {
if (enif_is_identical(argv[0], atom_seq_trace)) {
tt[tt_sz++] = atom_seq_trace;
@@ -198,21 +199,16 @@ static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
}
}
- opts = argv[5];
- if (enif_get_map_value(env, opts, atom_match_spec_result,
- &value)
- && !enif_is_identical(value, atom_true)) {
+ if (opts_sz && enif_get_map_value(env, opts, atom_match_spec_result, &value)) {
tt[tt_sz++] = value;
}
- if (enif_get_map_value(env, opts, atom_scheduler_id, &value)
- && !enif_is_identical(value, atom_undefined)) {
+ if (opts_sz && enif_get_map_value(env, opts, atom_scheduler_id, &value)) {
tt[tt_sz++] = value;
}
- if (enif_get_map_value(env, opts, atom_timestamp, &value)
- && !enif_is_identical(value, atom_undefined)) {
+ if (opts_sz && enif_get_map_value(env, opts, atom_timestamp, &value)) {
ERL_NIF_TERM ts;
if (enif_is_identical(value, atom_monotonic)) {
ErlNifTime mon = enif_monotonic_time(ERL_NIF_NSEC);
diff --git a/erts/emulator/test/bif_SUITE.erl b/erts/emulator/test/bif_SUITE.erl
index 26bb416bf0..ec6cb6ab72 100644
--- a/erts/emulator/test/bif_SUITE.erl
+++ b/erts/emulator/test/bif_SUITE.erl
@@ -646,6 +646,8 @@ erlang_halt(Config) when is_list(Config) ->
{badrpc,nodedown} = rpc:call(N2, erlang, halt, [0]),
{ok,N3} = slave:start(H, halt_node3),
{badrpc,nodedown} = rpc:call(N3, erlang, halt, [0,[]]),
+ {ok,N4} = slave:start(H, halt_node4),
+ {badrpc,nodedown} = rpc:call(N4, erlang, halt, [lists:duplicate(300,$x)]),
% This test triggers a segfault when dumping a crash dump
% to make sure that we can handle it properly.
diff --git a/erts/emulator/test/code_SUITE.erl b/erts/emulator/test/code_SUITE.erl
index 29b95ef674..2347a3d4ef 100644
--- a/erts/emulator/test/code_SUITE.erl
+++ b/erts/emulator/test/code_SUITE.erl
@@ -26,7 +26,7 @@
external_fun/1,get_chunk/1,module_md5/1,make_stub/1,
make_stub_many_funs/1,constant_pools/1,constant_refc_binaries/1,
false_dependency/1,coverage/1,fun_confusion/1,
- t_copy_literals/1]).
+ t_copy_literals/1, t_copy_literals_frags/1]).
-define(line_trace, 1).
-include_lib("common_test/include/ct.hrl").
@@ -38,7 +38,7 @@ all() ->
t_check_process_code_ets, t_check_old_code, external_fun, get_chunk,
module_md5, make_stub, make_stub_many_funs,
constant_pools, constant_refc_binaries, false_dependency,
- coverage, fun_confusion, t_copy_literals].
+ coverage, fun_confusion, t_copy_literals, t_copy_literals_frags].
init_per_suite(Config) ->
erts_debug:set_internal_state(available_internal_state, true),
@@ -766,6 +766,134 @@ t_copy_literals(Config) when is_list(Config) ->
ok = flush(),
ok.
+-define(mod, t_copy_literals_frags).
+t_copy_literals_frags(Config) when is_list(Config) ->
+ Bin = gen_lit(?mod,[{a,{1,2,3,4,5,6,7}},
+ {b,"hello world"},
+ {c, <<"hello world">>},
+ {d, {"hello world", {1.0, 2.0, <<"some">>, "string"}}},
+ {e, <<"off heap", 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9,10,11,12,13,14,15,
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9,10,11,12,13,14,15,
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9,10,11,12,13,14,15,
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9,10,11,12,13,14,15>>}]),
+
+ {module, ?mod} = erlang:load_module(?mod, Bin),
+ N = 6000,
+ Recv = spawn_opt(fun() -> receive
+ read ->
+ io:format("reading"),
+ literal_receiver()
+ end
+ end, [link,{min_heap_size, 10000}]),
+ Switcher = spawn_link(fun() -> literal_switcher() end),
+ Pids = [spawn_opt(fun() -> receive
+ {Pid, go, Recv, N} ->
+ io:format("sender batch (~w) start ~w~n",[N,self()]),
+ literal_sender(N,Recv),
+ Pid ! {self(), ok}
+ end
+ end, [link,{min_heap_size,800}]) || _ <- lists:seq(1,100)],
+ _ = [Pid ! {self(), go, Recv, N} || Pid <- Pids],
+ %% don't read immediately
+ timer:sleep(5),
+ Recv ! read,
+ Switcher ! {switch,?mod,Bin,[Recv|Pids],200},
+ _ = [receive {Pid, ok} -> ok end || Pid <- Pids],
+ Switcher ! {self(), done},
+ receive {Switcher, ok} -> ok end,
+ Recv ! {self(), done},
+ receive {Recv, ok} -> ok end,
+ ok.
+
+literal_receiver() ->
+ receive
+ {Pid, done} ->
+ io:format("reader_done~n"),
+ Pid ! {self(), ok};
+ {_Pid, msg, [A,B,C,D,E]} ->
+ A = ?mod:a(),
+ B = ?mod:b(),
+ C = ?mod:c(),
+ D = ?mod:d(),
+ E = ?mod:e(),
+ literal_receiver();
+ {Pid, sender_confirm} ->
+ io:format("sender confirm ~w~n", [Pid]),
+ Pid ! {self(), ok},
+ literal_receiver()
+ end.
+
+literal_sender(0, Recv) ->
+ Recv ! {self(), sender_confirm},
+ receive {Recv, ok} -> ok end;
+literal_sender(N, Recv) ->
+ Recv ! {self(), msg, [?mod:a(),
+ ?mod:b(),
+ ?mod:c(),
+ ?mod:d(),
+ ?mod:e()]},
+ literal_sender(N - 1, Recv).
+
+literal_switcher() ->
+ receive
+ {switch,Mod,Bin,Pids,Tmo} ->
+ literal_switcher(Mod,Bin,Pids,Tmo)
+ end.
+literal_switcher(Mod,Bin,Pids,Tmo) ->
+ receive
+ {Pid,done} ->
+ Pid ! {self(),ok}
+ after Tmo ->
+ io:format("load module ~w~n", [Mod]),
+ {module, Mod} = erlang:load_module(Mod,Bin),
+ ok = check_and_purge(Pids,Mod),
+ io:format("purge complete ~w~n", [Mod]),
+ literal_switcher(Mod,Bin,Pids,Tmo+Tmo)
+ end.
+
+check_and_purge([],Mod) ->
+ erlang:purge_module(Mod),
+ ok;
+check_and_purge(Pids,Mod) ->
+ io:format("purge ~w~n", [Mod]),
+ Tag = make_ref(),
+ _ = [begin
+ erlang:check_process_code(Pid,Mod,[{async,{Tag,Pid}}])
+ end || Pid <- Pids],
+ Retry = check_and_purge_receive(Pids,Tag,[]),
+ check_and_purge(Retry,Mod).
+
+check_and_purge_receive([Pid|Pids],Tag,Retry) ->
+ receive
+ {check_process_code, {Tag, Pid}, false} ->
+ check_and_purge_receive(Pids,Tag,Retry);
+ {check_process_code, {Tag, Pid}, true} ->
+ check_and_purge_receive(Pids,Tag,[Pid|Retry])
+ end;
+check_and_purge_receive([],_,Retry) ->
+ Retry.
+
+
+gen_lit(Module,Terms) ->
+ FunStrings = [lists:flatten(io_lib:format("~w() -> ~w.~n", [F,Term]))||{F,Term}<-Terms],
+ FunForms = function_forms(FunStrings),
+ Forms = [{attribute,erl_anno:new(1),module,Module},
+ {attribute,erl_anno:new(2),export,[FA || {FA,_} <- FunForms]}] ++
+ [Function || {_, Function} <- FunForms],
+ {ok, Module, Bin} = compile:forms(Forms),
+ Bin.
+
+function_forms([]) -> [];
+function_forms([S|Ss]) ->
+ {ok, Ts,_} = erl_scan:string(S),
+ {ok, Form} = erl_parse:parse_form(Ts),
+ Fun = element(3, Form),
+ Arity = element(4, Form),
+ [{{Fun,Arity}, Form}|function_forms(Ss)].
chase_msg(0, Pid) ->
chase_loop(Pid);
diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl
index d0096fb1bc..26780f6017 100644
--- a/erts/emulator/test/distribution_SUITE.erl
+++ b/erts/emulator/test/distribution_SUITE.erl
@@ -55,7 +55,8 @@
bad_dist_ext_receive/1,
bad_dist_ext_process_info/1,
bad_dist_ext_control/1,
- bad_dist_ext_connection_id/1]).
+ bad_dist_ext_connection_id/1,
+ start_epmd_false/1, epmd_module/1]).
%% Internal exports.
-export([sender/3, receiver2/2, dummy_waiter/0, dead_process/0,
@@ -64,6 +65,9 @@
dist_evil_parallel_receiver/0,
sendersender/4, sendersender2/4]).
+%% epmd_module exports
+-export([start_link/0, register_node/2, port_please/2]).
+
suite() ->
[{ct_hooks,[ts_install_cth]},
{timetrap, {minutes, 4}}].
@@ -76,7 +80,8 @@ all() ->
{group, trap_bif}, {group, dist_auto_connect},
dist_parallel_send, atom_roundtrip, unicode_atom_roundtrip, atom_roundtrip_r15b,
contended_atom_cache_entry, contended_unicode_atom_cache_entry,
- bad_dist_structure, {group, bad_dist_ext}].
+ bad_dist_structure, {group, bad_dist_ext},
+ start_epmd_false, epmd_module].
groups() ->
[{bulk_send, [], [bulk_send_small, bulk_send_big, bulk_send_bigbig]},
@@ -1881,6 +1886,66 @@ dmsg_ext(Term) ->
dmsg_bad_atom_cache_ref() ->
[$R, 137].
+start_epmd_false(Config) when is_list(Config) ->
+ %% Start a node with the option -start_epmd false.
+ {ok, OtherNode} = start_node(start_epmd_false, "-start_epmd false"),
+ %% We should be able to ping it, as epmd was started by us:
+ pong = net_adm:ping(OtherNode),
+ stop_node(OtherNode),
+
+ ok.
+
+epmd_module(Config) when is_list(Config) ->
+ %% We need a relay node to test this, since the test node uses the
+ %% standard epmd module.
+ Sock1 = start_relay_node(epmd_module_node1, "-epmd_module " ++ ?MODULE_STRING),
+ Node1 = inet_rpc_nodename(Sock1),
+ %% Ask what port it's listening on - it won't have registered with
+ %% epmd.
+ {ok, {ok, Port1}} = do_inet_rpc(Sock1, application, get_env, [kernel, dist_listen_port]),
+
+ %% Start a second node, passing the port number as a secret
+ %% argument.
+ Sock2 = start_relay_node(epmd_module_node2, "-epmd_module " ++ ?MODULE_STRING
+ ++ " -other_node_port " ++ integer_to_list(Port1)),
+ Node2 = inet_rpc_nodename(Sock2),
+ %% Node 1 can't ping node 2
+ {ok, pang} = do_inet_rpc(Sock1, net_adm, ping, [Node2]),
+ {ok, []} = do_inet_rpc(Sock1, erlang, nodes, []),
+ {ok, []} = do_inet_rpc(Sock2, erlang, nodes, []),
+ %% But node 2 can ping node 1
+ {ok, pong} = do_inet_rpc(Sock2, net_adm, ping, [Node1]),
+ {ok, [Node2]} = do_inet_rpc(Sock1, erlang, nodes, []),
+ {ok, [Node1]} = do_inet_rpc(Sock2, erlang, nodes, []),
+
+ stop_relay_node(Sock2),
+ stop_relay_node(Sock1).
+
+%% epmd_module functions:
+
+start_link() ->
+ ignore.
+
+register_node(_Name, Port) ->
+ %% Save the port number we're listening on.
+ application:set_env(kernel, dist_listen_port, Port),
+ Creation = rand:uniform(3),
+ {ok, Creation}.
+
+port_please(_Name, _Ip) ->
+ case init:get_argument(other_node_port) of
+ error ->
+ %% None specified. Default to 42.
+ Port = 42,
+ Version = 5,
+ {port, Port, Version};
+ {ok, [[PortS]]} ->
+ %% Port number given on command line.
+ Port = list_to_integer(PortS),
+ Version = 5,
+ {port, Port, Version}
+ end.
+
%%% Utilities
timestamp() ->
diff --git a/erts/emulator/test/message_queue_data_SUITE.erl b/erts/emulator/test/message_queue_data_SUITE.erl
index 226462676c..44e77dfad0 100644
--- a/erts/emulator/test/message_queue_data_SUITE.erl
+++ b/erts/emulator/test/message_queue_data_SUITE.erl
@@ -52,18 +52,12 @@ basic(Config) when is_list(Config) ->
ok = rpc:call(Node2, ?MODULE, basic_test, [on_heap]),
stop_node(Node2),
- {ok, Node3} = start_node(Config, "+hmqd mixed"),
- ok = rpc:call(Node3, ?MODULE, basic_test, [mixed]),
- stop_node(Node3),
-
ok.
is_valid_mqd_value(off_heap) ->
true;
is_valid_mqd_value(on_heap) ->
true;
-is_valid_mqd_value(mixed) ->
- true;
is_valid_mqd_value(_) ->
false.
@@ -78,9 +72,6 @@ basic_test(Default) ->
{message_queue_data, off_heap} = process_info(self(), message_queue_data),
off_heap = process_flag(message_queue_data, on_heap),
{message_queue_data, on_heap} = process_info(self(), message_queue_data),
- on_heap = process_flag(message_queue_data, mixed),
- {message_queue_data, mixed} = process_info(self(), message_queue_data),
- mixed = process_flag(message_queue_data, Default),
{'EXIT', _} = (catch process_flag(message_queue_data, blupp)),
P1 = spawn_opt(fun () -> receive after infinity -> ok end end,
@@ -101,12 +92,6 @@ basic_test(Default) ->
unlink(P3),
exit(P3, bye),
- P4 = spawn_opt(fun () -> receive after infinity -> ok end end,
- [link, {message_queue_data, mixed}]),
- {message_queue_data, mixed} = process_info(P4, message_queue_data),
- unlink(P4),
- exit(P4, bye),
-
{'EXIT', _} = (catch spawn_opt(fun () -> receive after infinity -> ok end end,
[link, {message_queue_data, blapp}])),
@@ -116,21 +101,18 @@ process_info_messages(Config) when is_list(Config) ->
Tester = self(),
P1 = spawn_opt(fun () ->
receive after 500 -> ok end,
- mixed = process_flag(message_queue_data, off_heap),
+ on_heap = process_flag(message_queue_data, off_heap),
Tester ! first,
receive after 500 -> ok end,
off_heap = process_flag(message_queue_data, on_heap),
Tester ! second,
receive after 500 -> ok end,
- on_heap = process_flag(message_queue_data, mixed),
+ on_heap = process_flag(message_queue_data, off_heap),
Tester ! third,
- receive after 500 -> ok end,
- mixed = process_flag(message_queue_data, off_heap),
- Tester ! fourth,
receive after infinity -> ok end
end,
- [link, {message_queue_data, mixed}]),
+ [link, {message_queue_data, on_heap}]),
P1 ! "A",
receive first -> ok end,
@@ -139,25 +121,20 @@ process_info_messages(Config) when is_list(Config) ->
P1 ! "C",
receive third -> ok end,
P1 ! "D",
- receive fourth -> ok end,
- P1 ! "E",
- {messages, ["A", "B", "C", "D", "E"]} = process_info(P1, messages),
+ {messages, ["A", "B", "C", "D"]} = process_info(P1, messages),
P2 = spawn_opt(fun () ->
receive after 500 -> ok end,
- mixed = process_flag(message_queue_data, off_heap),
+ on_heap = process_flag(message_queue_data, off_heap),
Tester ! first,
receive after 500 -> ok end,
off_heap = process_flag(message_queue_data, on_heap),
Tester ! second,
receive after 500 -> ok end,
- on_heap = process_flag(message_queue_data, mixed),
+ on_heap = process_flag(message_queue_data, off_heap),
Tester ! third,
receive after 500 -> ok end,
- mixed = process_flag(message_queue_data, off_heap),
- Tester ! fourth,
- receive after 500 -> ok end,
Tester ! process_info(self(), messages),
@@ -165,11 +142,10 @@ process_info_messages(Config) when is_list(Config) ->
receive M2 -> M2 = "B" end,
receive M3 -> M3 = "C" end,
receive M4 -> M4 = "D" end,
- receive M5 -> M5 = "E" end,
Tester ! self()
end,
- [link, {message_queue_data, mixed}]),
+ [link, {message_queue_data, on_heap}]),
P2 ! "A",
receive first -> ok end,
@@ -178,12 +154,10 @@ process_info_messages(Config) when is_list(Config) ->
P2 ! "C",
receive third -> ok end,
P2 ! "D",
- receive fourth -> ok end,
- P2 ! "E",
receive
Msg ->
- {messages, ["A", "B", "C", "D", "E"]} = Msg
+ {messages, ["A", "B", "C", "D"]} = Msg
end,
receive P2 -> ok end,
diff --git a/erts/emulator/test/tracer_SUITE.erl b/erts/emulator/test/tracer_SUITE.erl
index 20fb7e475e..9eb55c9af3 100644
--- a/erts/emulator/test/tracer_SUITE.erl
+++ b/erts/emulator/test/tracer_SUITE.erl
@@ -28,9 +28,9 @@
init_per_group/2,end_per_group/2, init_per_testcase/2,
end_per_testcase/2]).
-export([load/1, unload/1, reload/1, invalid_tracers/1]).
--export([send/1, recv/1, spawn/1, exit/1, link/1, unlink/1,
- getting_linked/1, getting_unlinked/1, register/1, unregister/1,
- in/1, out/1, gc_start/1, gc_end/1]).
+-export([send/1, recv/1, call/1, call_return/1, spawn/1, exit/1,
+ link/1, unlink/1, getting_linked/1, getting_unlinked/1,
+ register/1, unregister/1, in/1, out/1, gc_start/1, gc_end/1]).
suite() -> [{ct_hooks,[ts_install_cth]},
{timetrap, {minutes, 1}}].
@@ -39,9 +39,9 @@ all() ->
[load, unload, reload, invalid_tracers, {group, basic}].
groups() ->
- [{ basic, [], [send, recv, spawn, exit, link, unlink, getting_linked,
- getting_unlinked, register, unregister, in, out,
- gc_start, gc_end]}].
+ [{ basic, [], [send, recv, call, call_return, spawn, exit,
+ link, unlink, getting_linked, getting_unlinked,
+ register, unregister, in, out, gc_start, gc_end]}].
init_per_suite(Config) ->
erlang:trace_pattern({'_','_','_'}, false, [local]),
@@ -223,8 +223,8 @@ send(_Config) ->
Expect = fun(Pid, State, EOpts) ->
receive
Msg ->
- {send, State, Pid, ok, Self, Opts} = Msg,
- check_opts(EOpts, Opts)
+ {send, State, Pid, ok, Opts} = Msg,
+ check_opts(EOpts, Opts, Self)
end
end,
test(send, Tc, Expect).
@@ -239,13 +239,59 @@ recv(_Config) ->
Expect = fun(Pid, State, EOpts) ->
receive
Msg ->
- {'receive', State, Pid, ok, undefined, Opts} = Msg,
+ {'receive', State, Pid, ok, Opts} = Msg,
check_opts(EOpts, Opts)
end
end,
test('receive', Tc, Expect, false).
+call(_Config) ->
+
+ Self = self(),
+ Tc = fun(Pid) ->
+ Pid ! fun() -> call_test(Self), Self ! ok end,
+ receive ok -> ok after 100 -> ct:fail(timeout) end
+ end,
+
+ erlang:trace_pattern({?MODULE, call_test, 1}, [], [local]),
+
+ Expect = fun(Pid, State, EOpts) ->
+ receive
+ Msg ->
+ {call, State, Pid, {?MODULE, call_test, [Self]}, Opts} = Msg,
+ check_opts(EOpts, Opts)
+ end
+ end,
+ test(call, Tc, Expect).
+
+call_return(_Config) ->
+
+ Self = self(),
+ Tc = fun(Pid) ->
+ Pid ! fun() -> call_test(undefined), Self ! ok end,
+ receive ok -> ok after 100 -> ct:fail(timeout) end
+ end,
+
+ 1 = erlang:trace_pattern({?MODULE, call_test, 1}, [{'_',[],[{return_trace}]}], [local]),
+
+ Expect = fun(Pid, State, EOpts) ->
+ receive
+ CallMsg ->
+ {call, State, Pid, {?MODULE, call_test, [undefined]}, COpts} = CallMsg,
+ check_opts(EOpts, COpts)
+ end,
+ receive
+ RetMsg ->
+ {return_from, State, Pid, {?MODULE, call_test, 1}, ROpts} = RetMsg,
+ check_opts(EOpts, ROpts, undefined)
+ end
+ end,
+ test(call, Tc, Expect).
+
+call_test(Arg) ->
+ Arg.
+
spawn(_Config) ->
Tc = fun(Pid) ->
@@ -256,9 +302,8 @@ spawn(_Config) ->
fun(Pid, State, EOpts) ->
receive
Msg ->
- {spawn, State, Pid, NewPid,
- {lists,seq,[1,10]}, Opts} = Msg,
- check_opts(EOpts, Opts),
+ {spawn, State, Pid, NewPid, Opts} = Msg,
+ check_opts(EOpts, Opts, {lists,seq,[1,10]}),
true = is_pid(NewPid) andalso NewPid /= Pid
end
end,
@@ -274,7 +319,7 @@ exit(_Config) ->
fun(Pid, State, EOpts) ->
receive
Msg ->
- {exit, State, Pid, normal, undefined, Opts} = Msg,
+ {exit, State, Pid, normal, Opts} = Msg,
check_opts(EOpts, Opts)
end
end,
@@ -295,7 +340,7 @@ link(_Config) ->
fun(Pid, State, EOpts) ->
receive
Msg ->
- {link, State, Pid, NewPid, undefined, Opts} = Msg,
+ {link, State, Pid, NewPid, Opts} = Msg,
check_opts(EOpts, Opts),
true = is_pid(NewPid) andalso NewPid /= Pid
end
@@ -318,7 +363,7 @@ unlink(_Config) ->
fun(Pid, State, EOpts) ->
receive
Msg ->
- {unlink, State, Pid, NewPid, undefined, Opts} = Msg,
+ {unlink, State, Pid, NewPid, Opts} = Msg,
check_opts(EOpts, Opts),
true = is_pid(NewPid) andalso NewPid /= Pid
end
@@ -340,7 +385,7 @@ getting_linked(_Config) ->
fun(Pid, State, EOpts) ->
receive
Msg ->
- {getting_linked, State, Pid, NewPid, undefined, Opts} = Msg,
+ {getting_linked, State, Pid, NewPid, Opts} = Msg,
check_opts(EOpts, Opts),
true = is_pid(NewPid) andalso NewPid /= Pid
end
@@ -364,7 +409,7 @@ getting_unlinked(_Config) ->
fun(Pid, State, EOpts) ->
receive
Msg ->
- {getting_unlinked, State, Pid, NewPid, undefined, Opts} = Msg,
+ {getting_unlinked, State, Pid, NewPid, Opts} = Msg,
check_opts(EOpts, Opts),
true = is_pid(NewPid) andalso NewPid /= Pid
end
@@ -386,7 +431,7 @@ register(_Config) ->
fun(Pid, State, EOpts) ->
receive
Msg ->
- {register, State, Pid, ?MODULE, undefined, Opts} = Msg,
+ {register, State, Pid, ?MODULE, Opts} = Msg,
check_opts(EOpts, Opts)
end
end,
@@ -407,7 +452,7 @@ unregister(_Config) ->
fun(Pid, State, EOpts) ->
receive
Msg ->
- {unregister, State, Pid, ?MODULE, undefined, Opts} = Msg,
+ {unregister, State, Pid, ?MODULE, Opts} = Msg,
check_opts(EOpts, Opts)
end
end,
@@ -427,8 +472,7 @@ in(_Config) ->
N = (fun F(N) ->
receive
Msg ->
- {in, State, Pid, _,
- undefined, Opts} = Msg,
+ {in, State, Pid, _, Opts} = Msg,
check_opts(EOpts, Opts),
F(N+1)
after 0 -> N
@@ -452,8 +496,7 @@ out(_Config) ->
N = (fun F(N) ->
receive
Msg ->
- {out, State, Pid, _,
- undefined, Opts} = Msg,
+ {out, State, Pid, _, Opts} = Msg,
check_opts(EOpts, Opts),
F(N+1)
after 0 -> N
@@ -477,7 +520,7 @@ gc_start(_Config) ->
fun(Pid, State, EOpts) ->
receive
Msg ->
- {gc_major_start, State, Pid, _, undefined, Opts} = Msg,
+ {gc_major_start, State, Pid, _, Opts} = Msg,
check_opts(EOpts, Opts)
end
end,
@@ -497,7 +540,7 @@ gc_end(_Config) ->
fun(Pid, State, EOpts) ->
receive
Msg ->
- {gc_major_end, State, Pid, _, undefined, Opts} = Msg,
+ {gc_major_end, State, Pid, _, Opts} = Msg,
check_opts(EOpts, Opts)
end
end,
@@ -513,9 +556,7 @@ test(Event, TraceFlag, Tc, Expect, Removes) ->
test(Event, TraceFlag, Tc, Expect, _Removes, Dies) ->
ComplexState = {fun() -> ok end, <<0:(128*8)>>},
- Opts = #{ timestamp => undefined,
- scheduler_id => undefined,
- match_spec_result => true },
+ Opts = #{ },
%% Test that trace works
State1 = {#{ Event => trace }, self(), ComplexState},
@@ -540,8 +581,8 @@ test(Event, TraceFlag, Tc, Expect, _Removes, Dies) ->
Tc(Pid1T),
ok = trace_delivered(Pid1T),
- Expect(Pid1T, State1, Opts#{ scheduler_id := number,
- timestamp := timestamp}),
+ Expect(Pid1T, State1, Opts#{ scheduler_id => number,
+ timestamp => timestamp}),
receive M11T -> ct:fail({unexpected, M11T}) after 0 -> ok end,
if not Dies ->
{flags, [scheduler_id, TraceFlag, timestamp]}
@@ -568,6 +609,8 @@ test(Event, TraceFlag, Tc, Expect, _Removes, Dies) ->
ok.
+check_opts(E, O, Extra) ->
+ check_opts(E#{ extra => Extra }, O).
check_opts(#{ scheduler_id := number } = E, #{ scheduler_id := N } = O)
when is_integer(N) ->
E1 = maps:remove(scheduler_id, E),
diff --git a/erts/emulator/test/tracer_SUITE_data/tracer_test.c b/erts/emulator/test/tracer_SUITE_data/tracer_test.c
index 908f35da9c..a26bb33600 100644
--- a/erts/emulator/test/tracer_SUITE_data/tracer_test.c
+++ b/erts/emulator/test/tracer_SUITE_data/tracer_test.c
@@ -36,7 +36,7 @@ static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ErlNifFunc nif_funcs[] = {
{"enabled", 3, enabled},
- {"trace", 6, trace}
+ {"trace", 5, trace}
};
ERL_NIF_INIT(tracer_test, nif_funcs, load, NULL, upgrade, unload)
@@ -100,7 +100,7 @@ static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
ErlNifPid self, to;
ERL_NIF_TERM *tuple, msg;
const ERL_NIF_TERM *state_tuple;
- ASSERT(argc == 6);
+ ASSERT(argc == 5);
enif_get_tuple(env, argv[1], &state_arity, &state_tuple);
diff --git a/erts/emulator/test/tracer_test.erl b/erts/emulator/test/tracer_test.erl
index d4778f4531..1da80bfe31 100644
--- a/erts/emulator/test/tracer_test.erl
+++ b/erts/emulator/test/tracer_test.erl
@@ -24,14 +24,14 @@
%%% Test tracer
%%%
--export([enabled/3, trace/6]).
+-export([enabled/3, trace/5]).
-export([load/1, load/2]).
-on_load(load/0).
enabled(_, _, _) ->
erlang:nif_error(nif_not_loaded).
-trace(_, _, _, _, _, _) ->
+trace(_, _, _, _, _) ->
erlang:nif_error(nif_not_loaded).
load() ->
diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c
index 42da05b1f7..2b2e0e480a 100644
--- a/erts/etc/common/erlexec.c
+++ b/erts/etc/common/erlexec.c
@@ -195,6 +195,7 @@ static char *plusz_val_switches[] = {
#endif
void usage(const char *switchname);
+static void usage_format(char *format, ...);
void start_epmd(char *epmd);
void error(char* format, ...);
@@ -795,6 +796,24 @@ int main(int argc, char **argv)
get_start_erl_data((char *) NULL);
}
#endif
+ else if (strcmp(argv[i], "-start_epmd") == 0) {
+ if (i+1 >= argc)
+ usage("-start_epmd");
+
+ if (strcmp(argv[i+1], "true") == 0) {
+ /* The default */
+ no_epmd = 0;
+ }
+ else if (strcmp(argv[i+1], "false") == 0) {
+ no_epmd = 1;
+ }
+ else
+ usage_format("Expected boolean argument for \'-start_epmd\'.\n");
+
+ add_arg(argv[i]);
+ add_arg(argv[i+1]);
+ i++;
+ }
else
add_arg(argv[i]);
@@ -1173,7 +1192,7 @@ usage_aux(void)
"]"
#endif
"] "
- "[-make] [-man [manopts] MANPAGE] [-x] [-emu_args] "
+ "[-make] [-man [manopts] MANPAGE] [-x] [-emu_args] [-start_epmd BOOLEAN] "
"[-args_file FILENAME] [+A THREADS] [+a SIZE] [+B[c|d|i]] [+c [BOOLEAN]] "
"[+C MODE] [+h HEAP_SIZE_OPTION] [+K BOOLEAN] "
"[+l] [+M<SUBSWITCH> <ARGUMENT>] [+P MAX_PROCS] [+Q MAX_PORTS] "
diff --git a/erts/preloaded/ebin/erl_tracer.beam b/erts/preloaded/ebin/erl_tracer.beam
index 69804540c9..22286ed221 100644
--- a/erts/preloaded/ebin/erl_tracer.beam
+++ b/erts/preloaded/ebin/erl_tracer.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam
index 8379bf1768..cde8c9ab72 100644
--- a/erts/preloaded/ebin/erlang.beam
+++ b/erts/preloaded/ebin/erlang.beam
Binary files differ
diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam
index 7b5797e90a..6fc95b914e 100644
--- a/erts/preloaded/ebin/init.beam
+++ b/erts/preloaded/ebin/init.beam
Binary files differ
diff --git a/erts/preloaded/src/erl_tracer.erl b/erts/preloaded/src/erl_tracer.erl
index fe15812535..c810069d17 100644
--- a/erts/preloaded/src/erl_tracer.erl
+++ b/erts/preloaded/src/erl_tracer.erl
@@ -1,6 +1,6 @@
-module(erl_tracer).
--export([enabled/3, trace/6, on_load/0]).
+-export([enabled/3, trace/5, on_load/0]).
-type tracee() :: port() | pid() | undefined.
@@ -26,9 +26,9 @@
| trace_tag_running_ports()
| trace_tag_gc().
--type trace_opts() :: #{ match_spec_result => true | term(),
- scheduler_id => undefined | non_neg_integer(),
- timestamp => undefined | timestamp | cpu_timestamp |
+-type trace_opts() :: #{ extra => term(), match_spec_result => term(),
+ scheduler_id => non_neg_integer(),
+ timestamp => timestamp | cpu_timestamp |
monotonic | strict_monotonic }.
-type tracer_state() :: term().
@@ -41,6 +41,9 @@ on_load() ->
%%% NIF placeholders
%%%
+%% This suppression is needed as trace_tag gets collapsed to atom()
+-dialyzer({no_contracts, enabled/3}).
+
-spec enabled(Tag :: trace_status,
TracerState :: tracer_state(),
Tracee :: tracee()) ->
@@ -56,8 +59,7 @@ enabled(_, _, _) ->
TracerState :: tracer_state(),
Tracee :: tracee(),
Msg :: term(),
- Extra :: term(),
Opts :: trace_opts()) -> any().
-trace(_, _, _, _, _, _) ->
+trace(_, _, _, _, _) ->
erlang:nif_error(nif_not_loaded).
diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl
index 3d152c4e92..5283519c0a 100644
--- a/erts/preloaded/src/erlang.erl
+++ b/erts/preloaded/src/erlang.erl
@@ -977,17 +977,15 @@ group_leader(_GroupLeader, _Pid) ->
erlang:nif_error(undefined).
%% halt/0
-%% Shadowed by erl_bif_types: erlang:halt/0
-spec halt() -> no_return().
halt() ->
- erlang:nif_error(undefined).
+ erlang:halt(0, []).
%% halt/1
-%% Shadowed by erl_bif_types: erlang:halt/1
-spec halt(Status) -> no_return() when
Status :: non_neg_integer() | 'abort' | string().
-halt(_Status) ->
- erlang:nif_error(undefined).
+halt(Status) ->
+ erlang:halt(Status, []).
%% halt/2
%% Shadowed by erl_bif_types: erlang:halt/2
@@ -2059,7 +2057,7 @@ open_port(PortName, PortSettings) ->
low | normal | high | max.
-type message_queue_data() ::
- off_heap | on_heap | mixed.
+ off_heap | on_heap.
-spec process_flag(trap_exit, Boolean) -> OldBoolean when
Boolean :: boolean(),
diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl
index 618b53f6bb..45468b3b9c 100644
--- a/erts/preloaded/src/init.erl
+++ b/erts/preloaded/src/init.erl
@@ -90,6 +90,7 @@
-define(ON_LOAD_HANDLER, init__boot__on_load_handler).
+
debug(false, _) -> ok;
debug(_, T) -> erlang:display(T).
@@ -173,7 +174,25 @@ stop() -> init ! {stop,stop}, ok.
-spec stop(Status) -> 'ok' when
Status :: non_neg_integer() | string().
-stop(Status) -> init ! {stop,{stop,Status}}, ok.
+stop(Status) when is_integer(Status), Status >= 0 ->
+ stop_1(Status);
+stop(Status) when is_list(Status) ->
+ case is_bytelist(Status) of
+ true ->
+ stop_1(Status);
+ false ->
+ erlang:error(badarg)
+ end;
+stop(_) ->
+ erlang:error(badarg).
+
+is_bytelist([B|Bs]) when is_integer(B), B >= 0, B < 256 -> is_bytelist(Bs);
+is_bytelist([]) -> true;
+is_bytelist(_) -> false.
+
+%% Note that we check the type of Status beforehand to ensure that
+%% the call to halt(Status) by the init process cannot fail
+stop_1(Status) -> init ! {stop,{stop,Status}}, ok.
-spec boot(BootArgs) -> no_return() when
BootArgs :: [binary()].
@@ -285,21 +304,13 @@ things_to_string([]) ->
"".
halt_string(String, List) ->
- HaltString = String ++ things_to_string(List),
- if
- length(HaltString)<199 -> HaltString;
- true -> first198(HaltString, 198)
- end.
-
-first198([H|T], N) when N>0 ->
- [H|first198(T, N-1)];
-first198(_, 0) ->
- [].
+ String ++ things_to_string(List).
%% String = string()
%% List = [string() | atom() | pid() | number()]
%% Any other items in List, such as tuples, are ignored when creating
%% the string used as argument to erlang:halt/1.
+-spec crash(_, _) -> no_return().
crash(String, List) ->
halt(halt_string(String, List)).
diff --git a/lib/compiler/src/beam_disasm.erl b/lib/compiler/src/beam_disasm.erl
index 5badcce696..c699672db1 100644
--- a/lib/compiler/src/beam_disasm.erl
+++ b/lib/compiler/src/beam_disasm.erl
@@ -314,10 +314,7 @@ get_funs({LsR0,[{func_info,[{atom,M}=AtomM,{atom,F}=AtomF,ArityArg]}|Code0]})
when is_atom(M), is_atom(F) ->
Arity = resolve_arg_unsigned(ArityArg),
{LsR,Code,RestCode} = get_fun(Code0, []),
- Entry = case Code of
- [{label,[{u,E}]}|_] -> E;
- _ -> undefined
- end,
+ [{label,[{u,Entry}]}|_] = Code,
[#function{name=F,
arity=Arity,
entry=Entry,
diff --git a/lib/compiler/src/beam_disasm.hrl b/lib/compiler/src/beam_disasm.hrl
index e18214644f..d968cd9587 100644
--- a/lib/compiler/src/beam_disasm.hrl
+++ b/lib/compiler/src/beam_disasm.hrl
@@ -22,7 +22,9 @@
%% the system (e.g. in the translation from Beam to Icode).
%%
-%% XXX: THE FOLLOWING TYPE DECLARATION DOES NOT BELONG HERE...
+%% XXX: THE FOLLOWING TYPE DECLARATION DOES NOT BELONG HERE.
+%% IT SHOULD BE MOVED TO A FILE THAT DEFINES (AND EXPORTS)
+%% PROPER TYPES FOR THE SET OF BEAM INSTRUCTIONS.
%%
-type beam_instr() :: 'bs_init_writable' | 'fclearerror' | 'if_end'
| 'remove_message' | 'return' | 'send' | 'timeout'
@@ -34,7 +36,7 @@
-record(function, {name :: atom(),
arity :: byte(),
- entry, %% unused ??
+ entry :: beam_lib:label(), %% unnecessary ?
code = [] :: [beam_instr()]}).
-record(beam_file, {module :: module(),
diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl
index 402e3c4912..b4bbc5e739 100644
--- a/lib/compiler/src/v3_kernel.erl
+++ b/lib/compiler/src/v3_kernel.erl
@@ -242,7 +242,7 @@ gexpr_test_add(Ke, St0) ->
expr(#c_var{anno=A,name={_Name,Arity}}=Fname, Sub, St) ->
%% A local in an expression.
%% For now, these are wrapped into a fun by reverse
- %% etha-conversion, but really, there should be exactly one
+ %% eta-conversion, but really, there should be exactly one
%% such "lambda function" for each escaping local name,
%% instead of one for each occurrence as done now.
Vs = [#c_var{name=list_to_atom("V" ++ integer_to_list(V))} ||
diff --git a/lib/eldap/test/eldap_basic_SUITE.erl b/lib/eldap/test/eldap_basic_SUITE.erl
index 536e891a1e..d52a7c83f7 100644
--- a/lib/eldap/test/eldap_basic_SUITE.erl
+++ b/lib/eldap/test/eldap_basic_SUITE.erl
@@ -28,12 +28,11 @@
-include_lib("eldap/ebin/ELDAPv3.hrl").
--define(TIMEOUT, 120000). % 2 min
-
-
%% Control to delete a referral object:
-define(manageDsaIT, {control,"2.16.840.1.113730.3.4.2",false,asn1_NOVALUE}).
+suite() ->
+ [{timetrap,{seconds,40}}].
all() ->
[app,
@@ -124,7 +123,7 @@ end_per_suite(_Config) ->
init_per_group(return_values, Config) ->
- case ?config(ldap_server,Config) of
+ case proplists:get_value(ldap_server,Config) of
undefined ->
{skip, "LDAP server not availble"};
{Host,Port} ->
@@ -132,7 +131,7 @@ init_per_group(return_values, Config) ->
Config
end;
init_per_group(plain_api, Config0) ->
- case ?config(ldap_server,Config0) of
+ case proplists:get_value(ldap_server,Config0) of
undefined ->
{skip, "LDAP server not availble"};
Server = {Host,Port} ->
@@ -140,7 +139,7 @@ init_per_group(plain_api, Config0) ->
initialize_db([{server,Server}, {ssl_flag,false}, {start_tls,false} | Config0])
end;
init_per_group(ssl_api, Config0) ->
- case ?config(ldaps_server,Config0) of
+ case proplists:get_value(ldaps_server,Config0) of
undefined ->
{skip, "LDAPS server not availble"};
Server = {Host,Port} ->
@@ -148,7 +147,7 @@ init_per_group(ssl_api, Config0) ->
initialize_db([{server,Server}, {ssl_flag,true}, {start_tls,false} | Config0])
end;
init_per_group(start_tls_api, Config0) ->
- case {?config(ldap_server,Config0), ?config(ssl_available,Config0)} of
+ case {proplists:get_value(ldap_server,Config0), proplists:get_value(ssl_available,Config0)} of
{undefined,true} ->
{skip, "LDAP server not availble"};
{_,false} ->
@@ -187,18 +186,18 @@ end_per_group(_Group, Config) -> Config.
init_per_testcase(ssl_connection, Config) ->
- case ?config(ssl_available,Config) of
+ case proplists:get_value(ssl_available,Config) of
true ->
SSL_Port = 9999,
- CertFile = filename:join(?config(data_dir,Config), "certs/server/cert.pem"),
- KeyFile = filename:join(?config(data_dir,Config), "certs/server/key.pem"),
+ CertFile = filename:join(proplists:get_value(data_dir,Config), "certs/server/cert.pem"),
+ KeyFile = filename:join(proplists:get_value(data_dir,Config), "certs/server/key.pem"),
Parent = self(),
Listener = spawn_link(
fun() ->
case ssl:listen(SSL_Port, [{certfile, CertFile},
{keyfile, KeyFile}
- | ?config(tcp_listen_opts,Config)
+ | proplists:get_value(tcp_listen_opts,Config)
]) of
{ok,SSL_LSock} ->
Parent ! {ok,self()},
@@ -245,7 +244,7 @@ init_per_testcase(TC, Config) ->
end;
false ->
- case proplists:get_value(name,?config(tc_group_properties, Config)) of
+ case proplists:get_value(name,proplists:get_value(tc_group_properties, Config)) of
api_not_bound ->
{ok,H} = open(Config),
[{handle,H} | Config];
@@ -282,7 +281,7 @@ appup(Config) when is_list(Config) ->
%%%----------------------------------------------------------------
open_ret_val_success(Config) ->
- {Host,Port} = ?config(ldap_server,Config),
+ {Host,Port} = proplists:get_value(ldap_server,Config),
{ok,H} = eldap:open([Host], [{port,Port}]),
catch eldap:close(H).
@@ -292,7 +291,7 @@ open_ret_val_error(_Config) ->
%%%----------------------------------------------------------------
close_ret_val(Config) ->
- {Host,Port} = ?config(ldap_server,Config),
+ {Host,Port} = proplists:get_value(ldap_server,Config),
{ok,H} = eldap:open([Host], [{port,Port}]),
ok = eldap:close(H).
@@ -313,7 +312,6 @@ tcp_connection(Config) ->
end.
%%%----------------------------------------------------------------
-
close_after_tcp_error(Config) ->
Host = proplists:get_value(listen_host, Config),
Port = proplists:get_value(listen_port, Config),
@@ -436,37 +434,37 @@ tcp_connection_option(Config) ->
%%%----------------------------------------------------------------
elementary_search(Config) ->
{ok, #eldap_search_result{entries=[_]}} =
- eldap:search(?config(handle,Config),
- #eldap_search{base = ?config(eldap_path, Config),
+ eldap:search(proplists:get_value(handle,Config),
+ #eldap_search{base = proplists:get_value(eldap_path, Config),
filter= eldap:present("objectclass"),
scope = eldap:wholeSubtree()}).
%%%----------------------------------------------------------------
search_non_existant(Config) ->
{error, noSuchObject} =
- eldap:search(?config(handle,Config),
- #eldap_search{base = "cn=Bar," ++ ?config(eldap_path, Config),
+ eldap:search(proplists:get_value(handle,Config),
+ #eldap_search{base = "cn=Bar," ++ proplists:get_value(eldap_path, Config),
filter= eldap:present("objectclass"),
scope = eldap:wholeSubtree()}).
%%%----------------------------------------------------------------
add_when_not_bound(Config) ->
- {error, _} = eldap:add(?config(handle,Config),
- "cn=Jonas Jonsson," ++ ?config(eldap_path, Config),
+ {error, _} = eldap:add(proplists:get_value(handle,Config),
+ "cn=Jonas Jonsson," ++ proplists:get_value(eldap_path, Config),
[{"objectclass", ["person"]},
{"cn", ["Jonas Jonsson"]},
{"sn", ["Jonsson"]}]).
%%%----------------------------------------------------------------
bind(Config) ->
- ok = eldap:simple_bind(?config(handle,Config),
+ ok = eldap:simple_bind(proplists:get_value(handle,Config),
"cn=Manager,dc=ericsson,dc=se",
"hejsan").
%%%----------------------------------------------------------------
add_when_bound(Config) ->
- ok = eldap:add(?config(handle, Config),
- "cn=Jonas Jonsson," ++ ?config(eldap_path, Config),
+ ok = eldap:add(proplists:get_value(handle, Config),
+ "cn=Jonas Jonsson," ++ proplists:get_value(eldap_path, Config),
[{"objectclass", ["person"]},
{"cn", ["Jonas Jonsson"]},
{"sn", ["Jonsson"]}]).
@@ -474,16 +472,16 @@ add_when_bound(Config) ->
%%%----------------------------------------------------------------
add_already_exists(Config) ->
{error, entryAlreadyExists} =
- eldap:add(?config(handle, Config),
- "cn=Jonas Jonsson," ++ ?config(eldap_path, Config),
+ eldap:add(proplists:get_value(handle, Config),
+ "cn=Jonas Jonsson," ++ proplists:get_value(eldap_path, Config),
[{"objectclass", ["person"]},
{"cn", ["Jonas Jonsson"]},
{"sn", ["Jonsson"]}]).
%%%----------------------------------------------------------------
more_add(Config) ->
- H = ?config(handle, Config),
- BasePath = ?config(eldap_path, Config),
+ H = proplists:get_value(handle, Config),
+ BasePath = proplists:get_value(eldap_path, Config),
ok = eldap:add(H, "cn=Foo Bar," ++ BasePath,
[{"objectclass", ["person"]},
{"cn", ["Foo Bar"]},
@@ -495,8 +493,8 @@ more_add(Config) ->
%%%----------------------------------------------------------------
add_referral(Config) ->
- H = ?config(handle, Config),
- BasePath = ?config(eldap_path, Config),
+ H = proplists:get_value(handle, Config),
+ BasePath = proplists:get_value(eldap_path, Config),
{ok,{referral,["ldap://nowhere.example.com"++_]}} =
eldap:add(H, "cn=Foo Bar,dc=notHere," ++ BasePath,
[{"objectclass", ["person"]},
@@ -506,28 +504,28 @@ add_referral(Config) ->
%%%----------------------------------------------------------------
search_filter_equalityMatch(Config) ->
- BasePath = ?config(eldap_path, Config),
+ BasePath = proplists:get_value(eldap_path, Config),
ExpectedDN = "cn=Jonas Jonsson," ++ BasePath,
{ok, #eldap_search_result{entries=[#eldap_entry{object_name=ExpectedDN}]}} =
- eldap:search(?config(handle, Config),
+ eldap:search(proplists:get_value(handle, Config),
#eldap_search{base = BasePath,
filter = eldap:equalityMatch("sn", "Jonsson"),
scope=eldap:singleLevel()}).
%%%----------------------------------------------------------------
search_filter_substring_any(Config) ->
- BasePath = ?config(eldap_path, Config),
+ BasePath = proplists:get_value(eldap_path, Config),
ExpectedDN = "cn=Jonas Jonsson," ++ BasePath,
{ok, #eldap_search_result{entries=[#eldap_entry{object_name=ExpectedDN}]}} =
- eldap:search(?config(handle, Config),
+ eldap:search(proplists:get_value(handle, Config),
#eldap_search{base = BasePath,
filter = eldap:substrings("sn", [{any, "ss"}]),
scope=eldap:singleLevel()}).
%%%----------------------------------------------------------------
search_filter_initial(Config) ->
- H = ?config(handle, Config),
- BasePath = ?config(eldap_path, Config),
+ H = proplists:get_value(handle, Config),
+ BasePath = proplists:get_value(eldap_path, Config),
ExpectedDN = "cn=Foo Bar," ++ BasePath,
{ok, #eldap_search_result{entries=[#eldap_entry{object_name=ExpectedDN}]}} =
eldap:search(H,
@@ -537,8 +535,8 @@ search_filter_initial(Config) ->
%%%----------------------------------------------------------------
search_filter_final(Config) ->
- H = ?config(handle, Config),
- BasePath = ?config(eldap_path, Config),
+ H = proplists:get_value(handle, Config),
+ BasePath = proplists:get_value(eldap_path, Config),
ExpectedDN = "cn=Foo Bar," ++ BasePath,
{ok, #eldap_search_result{entries=[#eldap_entry{object_name=ExpectedDN}]}} =
eldap:search(H,
@@ -548,8 +546,8 @@ search_filter_final(Config) ->
%%%----------------------------------------------------------------
search_filter_and(Config) ->
- H = ?config(handle, Config),
- BasePath = ?config(eldap_path, Config),
+ H = proplists:get_value(handle, Config),
+ BasePath = proplists:get_value(eldap_path, Config),
ExpectedDN = "cn=Foo Bar," ++ BasePath,
{ok, #eldap_search_result{entries=[#eldap_entry{object_name=ExpectedDN}]}} =
eldap:search(H,
@@ -560,8 +558,8 @@ search_filter_and(Config) ->
%%%----------------------------------------------------------------
search_filter_or(Config) ->
- H = ?config(handle, Config),
- BasePath = ?config(eldap_path, Config),
+ H = proplists:get_value(handle, Config),
+ BasePath = proplists:get_value(eldap_path, Config),
ExpectedDNs = lists:sort(["cn=Foo Bar," ++ BasePath,
"ou=Team," ++ BasePath]),
{ok, #eldap_search_result{entries=Es}} =
@@ -574,8 +572,8 @@ search_filter_or(Config) ->
%%%----------------------------------------------------------------
search_filter_and_not(Config) ->
- H = ?config(handle, Config),
- BasePath = ?config(eldap_path, Config),
+ H = proplists:get_value(handle, Config),
+ BasePath = proplists:get_value(eldap_path, Config),
{ok, #eldap_search_result{entries=[]}} =
eldap:search(H,
#eldap_search{base = BasePath,
@@ -587,8 +585,8 @@ search_filter_and_not(Config) ->
%%%----------------------------------------------------------------
search_two_hits(Config) ->
- H = ?config(handle, Config),
- BasePath = ?config(eldap_path, Config),
+ H = proplists:get_value(handle, Config),
+ BasePath = proplists:get_value(eldap_path, Config),
DN1 = "cn=Santa Claus," ++ BasePath,
DN2 = "cn=Jultomten," ++ BasePath,
%% Add two objects:
@@ -619,8 +617,8 @@ search_two_hits(Config) ->
%%%----------------------------------------------------------------
search_referral(Config) ->
- H = ?config(handle, Config),
- BasePath = ?config(eldap_path, Config),
+ H = proplists:get_value(handle, Config),
+ BasePath = proplists:get_value(eldap_path, Config),
DN = "cn=Santa Claus,dc=notHere," ++ BasePath,
{ok,{referral,["ldap://nowhere.example.com"++_]}} =
eldap:search(H, #eldap_search{base = DN,
@@ -629,8 +627,8 @@ search_referral(Config) ->
%%%----------------------------------------------------------------
modify(Config) ->
- H = ?config(handle, Config),
- BasePath = ?config(eldap_path, Config),
+ H = proplists:get_value(handle, Config),
+ BasePath = proplists:get_value(eldap_path, Config),
%% The object to modify
DN = "cn=Foo Bar," ++ BasePath,
@@ -662,8 +660,8 @@ modify(Config) ->
%%%----------------------------------------------------------------
modify_referral(Config) ->
- H = ?config(handle, Config),
- BasePath = ?config(eldap_path, Config),
+ H = proplists:get_value(handle, Config),
+ BasePath = proplists:get_value(eldap_path, Config),
%% The object to modify
DN = "cn=Foo Bar,dc=notHere," ++ BasePath,
@@ -675,8 +673,8 @@ modify_referral(Config) ->
%%%----------------------------------------------------------------
delete(Config) ->
- H = ?config(handle, Config),
- BasePath = ?config(eldap_path, Config),
+ H = proplists:get_value(handle, Config),
+ BasePath = proplists:get_value(eldap_path, Config),
%% The element to play with:
DN = "cn=Jonas Jonsson," ++ BasePath,
@@ -693,16 +691,16 @@ delete(Config) ->
%%%----------------------------------------------------------------
delete_referral(Config) ->
- H = ?config(handle, Config),
- BasePath = ?config(eldap_path, Config),
+ H = proplists:get_value(handle, Config),
+ BasePath = proplists:get_value(eldap_path, Config),
%% The element to play with:
DN = "cn=Jonas Jonsson,dc=notHere," ++ BasePath,
{ok,{referral,["ldap://nowhere.example.com"++_]}} = eldap:delete(H, DN).
%%%----------------------------------------------------------------
modify_dn_delete_old(Config) ->
- H = ?config(handle, Config),
- BasePath = ?config(eldap_path, Config),
+ H = proplists:get_value(handle, Config),
+ BasePath = proplists:get_value(eldap_path, Config),
OrigCN = "Foo Bar",
OriginalRDN = "cn="++OrigCN,
DN = OriginalRDN ++ "," ++ BasePath,
@@ -747,8 +745,8 @@ modify_dn_delete_old(Config) ->
%%%----------------------------------------------------------------
modify_dn_keep_old(Config) ->
- H = ?config(handle, Config),
- BasePath = ?config(eldap_path, Config),
+ H = proplists:get_value(handle, Config),
+ BasePath = proplists:get_value(eldap_path, Config),
OriginalRDN = "cn=Foo Bar",
DN = OriginalRDN ++ "," ++ BasePath,
NewCN = "Niclas Andre",
@@ -887,7 +885,7 @@ initialize_db(Config) ->
clear_db(Config) ->
{ok,H} = open_bind(Config),
- Path = ?config(eldap_path, Config),
+ Path = proplists:get_value(eldap_path, Config),
delete_old_contents(H, Path),
eldap:close(H),
Config.
@@ -939,20 +937,20 @@ ok(MODULE, LINE, X) ->
cond_start_tls(H, Config) ->
- case ?config(start_tls,Config) of
+ case proplists:get_value(start_tls,Config) of
true -> start_tls(H,Config);
_ -> Config
end.
start_tls(H, Config) ->
- KeyFile = filename:join([?config(data_dir,Config),
+ KeyFile = filename:join([proplists:get_value(data_dir,Config),
"certs/client/key.pem"
]),
case eldap:start_tls(H, [{keyfile, KeyFile}]) of
ok ->
[{start_tls_success,true} | Config];
Error ->
- ct:log("Start_tls on ~p failed: ~p",[?config(url,Config) ,Error]),
+ ct:log("Start_tls on ~p failed: ~p",[proplists:get_value(url,Config) ,Error]),
ct:fail("start_tls failed")
end.
@@ -964,8 +962,8 @@ open_bind(Config) ->
{ok,H}.
open(Config) ->
- {Host,Port} = ?config(server,Config),
- SSLflag = ?config(ssl_flag,Config),
+ {Host,Port} = proplists:get_value(server,Config),
+ SSLflag = proplists:get_value(ssl_flag,Config),
{ok,H} = eldap:open([Host], [{port,Port},{ssl,SSLflag}]),
cond_start_tls(H, Config),
{ok,H}.
@@ -1023,7 +1021,7 @@ init_ssl_certs_et_al(Config) ->
of
R when R==ok ; R=={error,{already_started,ssl}} ->
try make_certs:all("/dev/null",
- filename:join(?config(data_dir,Config), "certs"))
+ filename:join(proplists:get_value(data_dir,Config), "certs"))
of
{ok,_} -> true;
Other ->
diff --git a/lib/et/src/et_collector.erl b/lib/et/src/et_collector.erl
index b8916c0eb9..aba90b0be1 100644
--- a/lib/et/src/et_collector.erl
+++ b/lib/et/src/et_collector.erl
@@ -27,8 +27,7 @@
-behaviour(gen_server).
%% External exports
--export([
- start_link/1,
+-export([start_link/1,
stop/1,
report/2,
@@ -55,8 +54,7 @@
dict_delete/2,
dict_lookup/2,
dict_match/2,
- multicast/2
- ]).
+ multicast/2]).
%% Internal export
-export([monitor_trace_port/2]).
@@ -258,7 +256,7 @@ parse_opt(BadList, _S, _Dict, _Clients) ->
{error, {bad_option_list, BadList}}.
start_clients(CollectorPid, [{Type, Parameters} | T]) ->
- start_trace_client(CollectorPid, Type, Parameters),
+ _ = start_trace_client(CollectorPid, Type, Parameters),
start_clients(CollectorPid, T);
start_clients(CollectorPid, []) ->
{ok, CollectorPid}.
@@ -892,7 +890,7 @@ init([InitialS, Dict]) ->
process_flag(trap_exit, true),
case InitialS#state.parent_pid of
undefined ->
- ignore;
+ ok;
Pid when is_pid(Pid) ->
link(Pid)
end,
@@ -914,7 +912,7 @@ init_global(S) ->
Spec = trace_spec_wrapper(EventFun, EndFun, {ok, self()}),
dbg:tracer(process, Spec),
et_selector:change_pattern(S#state.trace_pattern),
- net_kernel:monitor_nodes(true),
+ ok = net_kernel:monitor_nodes(true),
lists:foreach(fun(N) -> self() ! {nodeup, N} end, nodes()),
S#state{trace_nodes = [node()]};
false ->
@@ -1001,7 +999,7 @@ handle_call({save_event_file, FileName, Options}, _From, S) ->
%% insert() ->
%% case S2#state.file of
%% undefined ->
- %% ignore;
+ %% ok;
%% F ->
%% Fd = F#file.desc,
%% ok = disk_log:log(Fd, Event)
@@ -1010,7 +1008,7 @@ handle_call({save_event_file, FileName, Options}, _From, S) ->
Fun = fun({_, E}, A) -> ok = disk_log:log(Fd, E), A end,
Tab = S#state.event_tab,
Reply = tab_iterate(Fun, Tab, ets:first(Tab), ok),
- disk_log:close(Fd),
+ ok = disk_log:close(Fd),
{Reply, S}
%% all ->
%% Reply = tab_iterate(WriteFun, Tab, ok),
@@ -1033,7 +1031,7 @@ handle_call({save_event_file, FileName, Options}, _From, S) ->
handle_call({change_pattern, Pattern}, _From, S) ->
Ns = S#state.trace_nodes,
- rpc:multicall(Ns, et_selector, change_pattern, [Pattern]),
+ {_,[]} = rpc:multicall(Ns, et_selector, change_pattern, [Pattern]),
Reply = {old_pattern, S#state.trace_pattern},
S2 = S#state{trace_pattern = Pattern},
reply(Reply, S2);
@@ -1045,8 +1043,9 @@ handle_call(clear_table, _From, S) ->
handle_call(stop, _From, S) ->
do_multicast(S#state.subscribers, close),
case S#state.trace_global of
- true -> rpc:multicall(S#state.trace_nodes, dbg, stop_clear, []);
- false -> ignore
+ true -> {_,[]} = rpc:multicall(S#state.trace_nodes, dbg, stop_clear, []),
+ ok;
+ false -> ok
end,
{stop, shutdown, ok, S};
handle_call(Request, From, S) ->
@@ -1239,8 +1238,8 @@ tab_iterate(Fun, Tab, Key, Acc) ->
file_open(F) ->
Fd = make_ref(),
case F#file.file_opt of
- write -> file:rename(F#file.name, F#file.name ++ ".OLD");
- append -> ignore
+ write -> ok = file:rename(F#file.name, F#file.name ++ ".OLD");
+ append -> ok
end,
Args = [{file, F#file.name}, {name, Fd},
{repair, true}, {mode, read_write}],
@@ -1278,7 +1277,7 @@ do_multicast([], _Msg) ->
opt_unlink(Pid) ->
if
Pid =:= undefined ->
- ignore;
+ ok;
true ->
unlink(Pid)
end.
diff --git a/lib/et/src/et_wx_contents_viewer.erl b/lib/et/src/et_wx_contents_viewer.erl
index d5347a43db..247dd4c7ba 100644
--- a/lib/et/src/et_wx_contents_viewer.erl
+++ b/lib/et/src/et_wx_contents_viewer.erl
@@ -89,7 +89,7 @@ start_link(Options) ->
S#state.parent_pid =/= self() ->
unlink(Pid);
true ->
- ignore
+ ok
end,
{ok, Pid}
catch
@@ -242,11 +242,12 @@ handle_event(#wx{id = Id,
S) ->
case proplists:get_value(Id, S#state.menu_data) of
undefined ->
- ignore;
+ ok;
Data when is_record(Data, filter) ->
F = Data,
- ChildState= S#state{active_filter = F#filter.name},
- wx_object:start_link(?MODULE, [ChildState], []);
+ ChildState = S#state{active_filter = F#filter.name},
+ _ = wx_object:start_link(?MODULE, [ChildState], []),
+ ok;
{hide, Actors} ->
send_viewer_event(S, {delete_actors, Actors});
{show, Actors} ->
@@ -267,7 +268,7 @@ handle_event(#wx{id = Id,
TimeStamp =
case S#state.event_order of
trace_ts -> Event#event.trace_ts;
- event_ts -> Event#event.event_ts
+ event_ts -> Event#event.event_ts
end,
FileName = lists:flatten(["et_contents_viewer_", now_to_string(TimeStamp), ".txt"]),
Style = ?wxFD_SAVE bor ?wxFD_OVERWRITE_PROMPT,
@@ -275,7 +276,7 @@ handle_event(#wx{id = Id,
case select_file(S#state.frame, Msg, filename:absname(FileName), Style) of
{ok, FileName2} ->
Bin = list_to_binary(event_to_string(Event, S#state.event_order)),
- file:write_file(FileName2, Bin);
+ ok = file:write_file(FileName2, Bin);
cancel ->
ok
end,
@@ -351,19 +352,21 @@ handle_event(#wx{event = #wxKey{rawCode = KeyCode}}, S) ->
$0 ->
case lists:keysearch(?DEFAULT_FILTER_NAME, #filter.name, S#state.filters) of
{value, F} when is_record(F, filter) ->
- ChildState= S#state{active_filter = F#filter.name},
- wx_object:start_link(?MODULE, [ChildState], []);
+ ChildState = S#state{active_filter = F#filter.name},
+ _ = wx_object:start_link(?MODULE, [ChildState], []),
+ ok;
false ->
- ignore
+ ok
end,
{noreply, S};
Int when is_integer(Int), Int > $0, Int =< $9 ->
case catch lists:nth(Int-$0, S#state.filters) of
F when is_record(F, filter) ->
- ChildState= S#state{active_filter = F#filter.name},
- wx_object:start_link(?MODULE, [ChildState], []);
+ ChildState = S#state{active_filter = F#filter.name},
+ _ = wx_object:start_link(?MODULE, [ChildState], []),
+ ok;
{'EXIT', _} ->
- ignore
+ ok
end,
{noreply, S};
@@ -443,11 +446,11 @@ create_window(S) ->
Title = lists:concat([?MODULE, " (filter: ", Name, ")"]),
WinOpt = [{size, {W,H}}],
Frame = wxFrame:new(wx:null(), ?wxID_ANY, Title, WinOpt),
- wxFrame:createStatusBar(Frame),
+ _ = wxFrame:createStatusBar(Frame),
Panel = wxPanel:new(Frame, []),
Bar = wxMenuBar:new(),
- wxFrame:setMenuBar(Frame,Bar),
+ _ = wxFrame:setMenuBar(Frame,Bar),
create_file_menu(Bar),
Editor = wxTextCtrl:new(Panel, ?wxID_ANY, [{style, 0
bor ?wxDEFAULT
@@ -457,21 +460,21 @@ create_window(S) ->
bor ?wxTE_DONTWRAP}]),
Font = wxFont:new(10, ?wxFONTFAMILY_TELETYPE, ?wxNORMAL, ?wxNORMAL,[]),
TextAttr = wxTextAttr:new(?wxBLACK, [{font, Font}]),
- wxTextCtrl:setDefaultStyle(Editor, TextAttr),
+ _ = wxTextCtrl:setDefaultStyle(Editor, TextAttr),
Sizer = wxBoxSizer:new(?wxHORIZONTAL),
- wxSizer:add(Sizer, Editor, [{flag, ?wxEXPAND}, {proportion, 1}]),
+ _ = wxSizer:add(Sizer, Editor, [{flag, ?wxEXPAND}, {proportion, 1}]),
FilteredEvent = config_editor(Editor, S),
S2 = S#state{win = Frame, panel = Panel, filtered_event = FilteredEvent},
HideData = create_hide_menu(Bar, S2),
SearchData = create_search_menu(Bar, S2),
FilterData = create_filter_menu(Bar, S#state.filters),
- wxFrame:connect(Frame, command_menu_selected, []),
- wxFrame:connect(Frame, key_up),
- wxFrame:connect(Frame, close_window, [{skip,true}]),
- wxFrame:setFocus(Frame),
- wxPanel:setSizer(Panel, Sizer),
- wxSizer:fit(Sizer, Panel),
- wxFrame:show(Frame),
+ _ = wxFrame:connect(Frame, command_menu_selected, []),
+ _ = wxFrame:connect(Frame, key_up),
+ _ = wxFrame:connect(Frame, close_window, [{skip,true}]),
+ _ = wxFrame:setFocus(Frame),
+ _ = wxPanel:setSizer(Panel, Sizer),
+ _ = wxSizer:fit(Sizer, Panel),
+ _ = wxFrame:show(Frame),
S2#state{menu_data = HideData++SearchData++FilterData, editor = Editor, frame = Frame}.
menuitem(Menu, Id, Text, UserData) ->
@@ -480,16 +483,17 @@ menuitem(Menu, Id, Text, UserData) ->
create_file_menu(Bar) ->
Menu = wxMenu:new([]),
- wxMenu:append(Menu, ?wxID_SAVE, "Save"),
- wxMenu:append(Menu, ?wxID_PRINT,"Print"),
- wxMenu:appendSeparator(Menu),
- wxMenu:append(Menu, ?wxID_EXIT, "Close"),
- wxMenuBar:append(Bar, Menu, "File").
+ _ = wxMenu:append(Menu, ?wxID_SAVE, "Save"),
+ _ = wxMenu:append(Menu, ?wxID_PRINT,"Print"),
+ _ = wxMenu:appendSeparator(Menu),
+ _ = wxMenu:append(Menu, ?wxID_EXIT, "Close"),
+ _ = wxMenuBar:append(Bar, Menu, "File"),
+ ok.
create_filter_menu(Bar, Filters) ->
Menu = wxMenu:new([]),
- wxMenuItem:enable(wxMenu:append(Menu, ?wxID_ANY, "Select Filter"), [{enable, false}]),
- wxMenu:appendSeparator(Menu),
+ _ = wxMenuItem:enable(wxMenu:append(Menu, ?wxID_ANY, "Select Filter"), [{enable, false}]),
+ _ = wxMenu:appendSeparator(Menu),
Item = fun(F, {N,Acc}) when F#filter.name =:= ?DEFAULT_FILTER_NAME->
Label = lists:concat([pad_string(F#filter.name, 20, $\ , right), "(0)"]),
MenuItem = menuitem(Menu, ?wxID_ANY, Label, F),
@@ -502,7 +506,7 @@ create_filter_menu(Bar, Filters) ->
end,
Filters2 = lists:keysort(#filter.name, Filters),
{_,MenuData} = lists:foldl(Item, {1, []}, Filters2),
- wxMenuBar:append(Bar, Menu, "Filters"),
+ _ = wxMenuBar:append(Bar, Menu, "Filters"),
MenuData.
create_hide_menu(Bar, S) ->
@@ -515,33 +519,33 @@ create_hide_menu(Bar, S) ->
S#state.viewer_pid =:= undefined ->
ignore;
From =:= To ->
- wxMenuItem:enable(wxMenu:append(Menu, ?wxID_ANY, "Hide actor in Viewer "),
- [{enable, false}]),
- wxMenu:appendSeparator(Menu),
+ _ = wxMenuItem:enable(wxMenu:append(Menu, ?wxID_ANY, "Hide actor in Viewer "),
+ [{enable, false}]),
+ _ = wxMenu:appendSeparator(Menu),
Hide = menuitem(Menu, ?wxID_ANY, "From=To (f|t|b)", {hide, [From]}),
- wxMenu:appendSeparator(Menu),
- wxMenuItem:enable(wxMenu:append(Menu, ?wxID_ANY, "Show actor in Viewer "),
- [{enable, false}]),
- wxMenu:appendSeparator(Menu),
+ _ = wxMenu:appendSeparator(Menu),
+ _ = wxMenuItem:enable(wxMenu:append(Menu, ?wxID_ANY, "Show actor in Viewer "),
+ [{enable, false}]),
+ _ = wxMenu:appendSeparator(Menu),
Show = menuitem(Menu, ?wxID_ANY, "From=To (F|T|B)", {show, [From]}),
[Show,Hide];
true ->
- wxMenuItem:enable(wxMenu:append(Menu, ?wxID_ANY, "Hide actor in Viewer "),
- [{enable, false}]),
- wxMenu:appendSeparator(Menu),
+ _ = wxMenuItem:enable(wxMenu:append(Menu, ?wxID_ANY, "Hide actor in Viewer "),
+ [{enable, false}]),
+ _ = wxMenu:appendSeparator(Menu),
Hide = [menuitem(Menu, ?wxID_ANY, "From (f)", {hide, [From]}),
menuitem(Menu, ?wxID_ANY, "To (t)", {hide, [To]}),
menuitem(Menu, ?wxID_ANY, "Both (b)", {hide, [From, To]})],
- wxMenu:appendSeparator(Menu),
- wxMenuItem:enable(wxMenu:append(Menu, ?wxID_ANY, "Show actor in Viewer "),
- [{enable, false}]),
- wxMenu:appendSeparator(Menu),
+ _ = wxMenu:appendSeparator(Menu),
+ _ = wxMenuItem:enable(wxMenu:append(Menu, ?wxID_ANY, "Show actor in Viewer "),
+ [{enable, false}]),
+ _ = wxMenu:appendSeparator(Menu),
Show = [menuitem(Menu, ?wxID_ANY, "From (F)", {show, [From]}),
menuitem(Menu, ?wxID_ANY, "To (T)", {show, [To]}),
menuitem(Menu, ?wxID_ANY, "Both (B)", {show, [From, To]})],
Show++Hide
end,
- wxMenuBar:append(Bar, Menu, "Hide"),
+ _ = wxMenuBar:append(Bar, Menu, "Hide"),
MenuData.
create_search_menu(Bar, S) ->
@@ -549,9 +553,9 @@ create_search_menu(Bar, S) ->
E = S#state.filtered_event,
From = E#event.from,
To = E#event.to,
- wxMenuItem:enable(wxMenu:append(Menu, ?wxID_ANY, "Search in Viewer "),
- [{enable, false}]),
- wxMenu:appendSeparator(Menu),
+ _ = wxMenuItem:enable(wxMenu:append(Menu, ?wxID_ANY, "Search in Viewer "),
+ [{enable, false}]),
+ _ = wxMenu:appendSeparator(Menu),
MenuData =
if
S#state.viewer_pid =:= undefined ->
@@ -571,7 +575,7 @@ create_search_menu(Bar, S) ->
menuitem(Menu, ?wxID_ANY, "Reverse from this event (r)", {mode, ModeR}),
menuitem(Menu, ?wxID_ANY, "Abort search. Display all (a)", {mode, all})]
end,
- wxMenuBar:append(Bar, Menu, "Search"),
+ _ = wxMenuBar:append(Bar, Menu, "Search"),
MenuData.
config_editor(Editor, S) ->
@@ -668,9 +672,10 @@ pad_string(String, MinLen, Char, Dir) when is_integer(MinLen), MinLen >= 0 ->
send_viewer_event(S, Event) ->
case S#state.viewer_pid of
ViewerPid when is_pid(ViewerPid) ->
- ViewerPid ! {et, Event};
+ ViewerPid ! {et, Event},
+ ok;
undefined ->
- ignore
+ ok
end.
select_file(Frame, Message, DefaultFile, Style) ->
diff --git a/lib/et/src/et_wx_viewer.erl b/lib/et/src/et_wx_viewer.erl
index 0fc10cb37b..9613299e6b 100644
--- a/lib/et/src/et_wx_viewer.erl
+++ b/lib/et/src/et_wx_viewer.erl
@@ -320,8 +320,8 @@ init([S]) when is_record(S, state) ->
undefined -> ok;
ParentPid -> link(ParentPid)
end,
- wx:new(),
- wx:debug(S#state.wx_debug),
+ _ = wx:new(),
+ _ = wx:debug(S#state.wx_debug),
et_collector:dict_insert(S#state.collector_pid,
{subscriber, self()},
?MODULE),
@@ -532,26 +532,25 @@ handle_info(#wx{id=Id, event = #wxCommand{type = command_menu_selected}}, S=#sta
load_all ->
Style = ?wxFD_OPEN bor ?wxFD_OVERWRITE_PROMPT,
Msg = "Select a file to load events from",
- S2 =
- case select_file(S#state.frame, Msg, S#state.event_file, Style) of
- {ok, NewFile} ->
- et_collector:start_trace_client(CollectorPid, event_file, NewFile),
- S#state{event_file = NewFile};
- cancel ->
- S
- end,
+ S2 = case select_file(S#state.frame, Msg, S#state.event_file, Style) of
+ {ok, NewFile} ->
+ _ = et_collector:start_trace_client(CollectorPid, event_file, NewFile),
+ S#state{event_file = NewFile};
+ cancel ->
+ S
+ end,
noreply(S2);
save_all ->
Style = ?wxFD_SAVE bor ?wxFD_OVERWRITE_PROMPT,
Msg = "Select a file to save events to",
- S2 =
- case select_file(S#state.frame, Msg, S#state.event_file, Style) of
- {ok, NewFile} ->
- et_collector:save_event_file(CollectorPid, NewFile, [existing, write, keep]),
- S#state{event_file = NewFile};
- cancel ->
- S
- end,
+ S2 = case select_file(S#state.frame, Msg, S#state.event_file, Style) of
+ {ok, NewFile} ->
+ ok = et_collector:save_event_file(CollectorPid, NewFile,
+ [existing, write, keep]),
+ S#state{event_file = NewFile};
+ cancel ->
+ S
+ end,
noreply(S2);
print_setup ->
S2 = print_setup(S),
@@ -787,7 +786,7 @@ handle_info(#wx{event = #wxKey{keyCode = KeyCode, shiftDown = SD}}, S) ->
noreply(S)
end;
handle_info(#wx{event = #wxScroll{type = scroll_changed}} = Wx, S) ->
- get_latest_scroll(Wx),
+ _ = get_latest_scroll(Wx),
Pos = wxScrollBar:getThumbPosition(S#state.scroll_bar),
{_, LineTopY, LineBotY} = calc_y(S),
Range = LineBotY - LineTopY,
@@ -1107,7 +1106,7 @@ pick_n([Head | Tail], N, Acc) when N > 0 ->
pick_n(Tail, N - 1, [Head | Acc]).
close_all(S) ->
- close_all_others(S),
+ _ = close_all_others(S),
wxFrame:destroy(S#state.frame),
opt_unlink(S#state.parent_pid),
{stop, shutdown, S}.
@@ -1115,12 +1114,12 @@ close_all(S) ->
close_all_others(S) ->
Fun =
fun({{subscriber, Pid}, _}) ->
- if
- Pid =:= self() ->
- ignore;
+ if Pid =:= self() ->
+ ok;
true ->
unlink(Pid),
- Pid ! {et, close}
+ Pid ! {et, close},
+ ok
end
end,
All = et_collector:dict_match(S#state.collector_pid,
@@ -1208,9 +1207,9 @@ create_main_window(S) ->
CheckBoxData = [{wxCheckBox:getId(HideActions), hide_actions},
{wxCheckBox:getId(HideActors), hide_actors}],
wxPanel:connect(Panel, command_checkbox_clicked),
- wxSizer:add(CheckSizer, HideActions),
- wxSizer:add(CheckSizer,HideActors),
- wxSizer:add(OptSizer, CheckSizer, [{border, 10}, {flag, ?wxALL}]),
+ _ = wxSizer:add(CheckSizer, HideActions),
+ _ = wxSizer:add(CheckSizer,HideActors),
+ _ = wxSizer:add(OptSizer, CheckSizer, [{border, 10}, {flag, ?wxALL}]),
DetailLevelBox = wxStaticBoxSizer:new(?wxHORIZONTAL,
Panel,
[{label, "Detail level"}]),
@@ -1222,22 +1221,21 @@ create_main_window(S) ->
{size, {200,-1}}]),
wxStatusBar:setStatusText(StatusBar, where_text(S)),
wxFrame:connect(Frame, command_slider_updated),
- wxSizer:add(DetailLevelBox, DetailLevel),
- wxSizer:add(OptSizer, DetailLevelBox, [{border, 10}, {flag, ?wxALL}]),
- wxSizer:addStretchSpacer(OptSizer),
- wxSizer:add(MainSizer, OptSizer),
- wxSizer:add(MainSizer,
- wxStaticLine:new(Panel, [{style, ?wxLI_HORIZONTAL}]),
- [{flag, ?wxEXPAND}]),
+ _ = wxSizer:add(DetailLevelBox, DetailLevel),
+ _ = wxSizer:add(OptSizer, DetailLevelBox, [{border, 10}, {flag, ?wxALL}]),
+ _ = wxSizer:addStretchSpacer(OptSizer),
+ _ = wxSizer:add(MainSizer, OptSizer),
+ _ = wxSizer:add(MainSizer, wxStaticLine:new(Panel, [{style, ?wxLI_HORIZONTAL}]),
+ [{flag, ?wxEXPAND}]),
CanvasSizer = wxBoxSizer:new(?wxHORIZONTAL),
Canvas = wxPanel:new(Panel, [{style, ?wxFULL_REPAINT_ON_RESIZE}]),
{CanvasW,CanvasH} = wxPanel:getSize(Canvas),
ScrollBar = wxScrollBar:new(Panel, ?wxID_ANY, [{style, ?wxSB_VERTICAL}]),
- wxSizer:add(CanvasSizer, Canvas, [{flag, ?wxEXPAND}, {proportion, 1}]),
- wxSizer:add(CanvasSizer, ScrollBar, [{flag, ?wxEXPAND}]),
- wxSizer:add(MainSizer, CanvasSizer, [{flag, ?wxEXPAND}, {proportion, 1}]),
+ _ = wxSizer:add(CanvasSizer, Canvas, [{flag, ?wxEXPAND}, {proportion, 1}]),
+ _ = wxSizer:add(CanvasSizer, ScrollBar, [{flag, ?wxEXPAND}]),
+ _ = wxSizer:add(MainSizer, CanvasSizer, [{flag, ?wxEXPAND}, {proportion, 1}]),
wxPanel:connect(Canvas, left_down),
wxPanel:connect(Canvas, left_up),
wxPanel:connect(Canvas, right_up),
@@ -1337,8 +1335,7 @@ menuitem(Menu, Id, Text, UserData) ->
create_file_menu(Bar) ->
Menu = wxMenu:new([]),
- Data = [
- menuitem(Menu, ?wxID_ANY, "Clear all events in the Collector", clear_all),
+ Data = [menuitem(Menu, ?wxID_ANY, "Clear all events in the Collector", clear_all),
menuitem(Menu, ?wxID_ANY, "Load events to the Collector from file", load_all),
menuitem(Menu, ?wxID_ANY, "Save all events in the Collector to file", save_all),
@@ -1348,51 +1345,44 @@ create_file_menu(Bar) ->
menuitem(Menu, ?wxID_ANY, "Close this Viewer", close),
menuitem(Menu, ?wxID_ANY, "Close all other Viewers, but this (c)", close_all_others),
- menuitem(Menu, ?wxID_ANY, "Close all Viewers and the Collector) (C) ", close_all)
- ],
- wxMenu:insertSeparator(Menu, 3),
- wxMenu:insertSeparator(Menu, 7),
+ menuitem(Menu, ?wxID_ANY, "Close all Viewers and the Collector) (C) ", close_all)],
+ _ = wxMenu:insertSeparator(Menu, 3),
+ _ = wxMenu:insertSeparator(Menu, 7),
wxMenuBar:append(Bar, Menu, "File"),
Data.
create_viewer_menu(Bar) ->
Menu = wxMenu:new([]),
- wxMenuItem:enable(wxMenu:append(Menu, ?wxID_ANY, "Scroll this Viewer"),
- [{enable, false}]),
- wxMenu:appendSeparator(Menu),
- D1 = [
- menuitem(Menu, ?wxID_ANY, "First (f)", first),
+ _ = wxMenuItem:enable(wxMenu:append(Menu, ?wxID_ANY, "Scroll this Viewer"),
+ [{enable, false}]),
+ _ = wxMenu:appendSeparator(Menu),
+ D1 = [menuitem(Menu, ?wxID_ANY, "First (f)", first),
menuitem(Menu, ?wxID_ANY, "Last (l)", last),
menuitem(Menu, ?wxID_ANY, "Prev (p)", prev),
menuitem(Menu, ?wxID_ANY, "Next (n)", next),
- menuitem(Menu, ?wxID_ANY, "Refresh (r)", refresh)
- ],
- wxMenu:appendSeparator(Menu),
- D2 = [
- menuitem(Menu, ?wxID_ANY, "Up 5 (Up)", up),
- menuitem(Menu, ?wxID_ANY, "Down 5 (Down)", down)
- ],
- wxMenu:appendSeparator(Menu),
- wxMenuItem:enable(wxMenu:append(Menu, ?wxID_ANY, "Actor visibility in this Viewer"),
- [{enable, false}]),
- wxMenu:appendSeparator(Menu),
+ menuitem(Menu, ?wxID_ANY, "Refresh (r)", refresh)],
+ _ = wxMenu:appendSeparator(Menu),
+ D2 = [menuitem(Menu, ?wxID_ANY, "Up 5 (Up)", up),
+ menuitem(Menu, ?wxID_ANY, "Down 5 (Down)", down)],
+ _ = wxMenu:appendSeparator(Menu),
+ _ = wxMenuItem:enable(wxMenu:append(Menu, ?wxID_ANY, "Actor visibility in this Viewer"),
+ [{enable, false}]),
+ _ = wxMenu:appendSeparator(Menu),
D3 = [menuitem(Menu, ?wxID_ANY, "Display all actors (a)", display_all)],
- wxMenuBar:append(Bar, Menu, "Viewer"),
+ _ = wxMenuBar:append(Bar, Menu, "Viewer"),
[D1,D2,D3].
create_collector_menu(Bar) ->
Menu = wxMenu:new([]),
- wxMenuItem:enable(wxMenu:append(Menu, ?wxID_ANY, "Scroll all Viewers"),
- [{enable, false}]),
- wxMenu:appendSeparator(Menu),
- Data = [
- menuitem(Menu, ?wxID_ANY, "First (F)", first_all),
+ _ = wxMenuItem:enable(wxMenu:append(Menu, ?wxID_ANY, "Scroll all Viewers"),
+ [{enable, false}]),
+ _ = wxMenu:appendSeparator(Menu),
+ Data = [menuitem(Menu, ?wxID_ANY, "First (F)", first_all),
menuitem(Menu, ?wxID_ANY, "Last (L)", last_all),
menuitem(Menu, ?wxID_ANY, "Prev (P)", prev_all),
menuitem(Menu, ?wxID_ANY, "Next (N)", next_all),
- menuitem(Menu, ?wxID_ANY, "Refresh (R)", refresh_all)
- ],
- wxMenuBar:append(Bar, Menu, "Collector"),
+ menuitem(Menu, ?wxID_ANY, "Refresh (R)", refresh_all)],
+ _ = wxMenuBar:append(Bar, Menu, "Collector"),
Data.
create_filter_menu(S=#state{filter_menu = {Menu,Data}}, ActiveFilterName, Filters) ->
@@ -1421,21 +1411,19 @@ create_filter_menu(S=#state{filter_menu = {Menu,Data}}, ActiveFilterName, Filter
Same = lists:concat([pad_string(ActiveFilterName, 20), "(=) same scale"]),
Larger = lists:concat([pad_string(ActiveFilterName, 20), "(+) bigger scale"]),
Smaller = lists:concat([pad_string(ActiveFilterName, 20), "(-) smaller scale"]),
- D2 = [
- menuitem(Menu, ?wxID_ANY, Same, {data, Filter, 0}),
+ D2 = [menuitem(Menu, ?wxID_ANY, Same, {data, Filter, 0}),
menuitem(Menu, ?wxID_ANY, Smaller, {data, Filter, -1}),
menuitem(Menu, ?wxID_ANY, Larger, {data, Filter, 1}),
wxMenu:appendSeparator(Menu),
I2 = wxMenu:append(Menu, ?wxID_ANY, "New Filter Same Scale"),
- wxMenu:appendSeparator(Menu)
- ],
- wxMenuItem:enable(I2, [{enable,false}]),
+ wxMenu:appendSeparator(Menu)],
+ _ = wxMenuItem:enable(I2, [{enable,false}]),
{_,D3} = lists:foldl(Item, {1,[]}, Filters),
S#state{filter_menu = {Menu, lists:flatten([D1,D2,D3])}}.
create_help_menu(Bar) ->
Menu = wxMenu:new([]),
- menuitem(Menu, ?wxID_HELP, "Info", help),
+ _ = menuitem(Menu, ?wxID_HELP, "Info", help),
wxMenuBar:append(Bar, Menu, "Help").
clear_canvas(S) ->
diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl
index 9453ca6c6f..f649c6e599 100644
--- a/lib/hipe/cerl/erl_bif_types.erl
+++ b/lib/hipe/cerl/erl_bif_types.erl
@@ -154,8 +154,6 @@ type(M, F, A, Xs) ->
erl_types:erl_type().
%%-- erlang -------------------------------------------------------------------
-type(erlang, halt, 0, _, _) -> t_none();
-type(erlang, halt, 1, _, _) -> t_none();
type(erlang, halt, 2, _, _) -> t_none();
type(erlang, exit, 1, _, _) -> t_none();
type(erlang, error, 1, _, _) -> t_none();
@@ -2341,10 +2339,6 @@ arg_types(erlang, bit_size, 1) ->
%% Guard bif, needs to be here.
arg_types(erlang, byte_size, 1) ->
[t_bitstr()];
-arg_types(erlang, halt, 0) ->
- [];
-arg_types(erlang, halt, 1) ->
- [t_sup([t_non_neg_fixnum(), t_atom('abort'), t_string()])];
arg_types(erlang, halt, 2) ->
[t_sup([t_non_neg_fixnum(), t_atom('abort'), t_string()]),
t_list(t_tuple([t_atom('flush'), t_boolean()]))];
diff --git a/lib/hipe/icode/Makefile b/lib/hipe/icode/Makefile
index a5edb10d90..c86562a981 100644
--- a/lib/hipe/icode/Makefile
+++ b/lib/hipe/icode/Makefile
@@ -59,7 +59,7 @@ DOC_MODULES = hipe_beam_to_icode \
hipe_icode_pp hipe_icode_primops \
hipe_icode_range \
hipe_icode_split_arith \
- hipe_icode_ssa hipe_icode_ssa_const_prop \
+ hipe_icode_ssa hipe_icode_ssa_const_prop hipe_icode_call_elim \
hipe_icode_ssa_copy_prop hipe_icode_ssa_struct_reuse \
hipe_icode_type $(HIPE_MODULES)
diff --git a/lib/hipe/icode/hipe_icode_call_elim.erl b/lib/hipe/icode/hipe_icode_call_elim.erl
new file mode 100644
index 0000000000..6a22133962
--- /dev/null
+++ b/lib/hipe/icode/hipe_icode_call_elim.erl
@@ -0,0 +1,78 @@
+%% -*- erlang-indent-level: 2 -*-
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2016. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%----------------------------------------------------------------------
+%% File : hipe_icode_call_elim.erl
+%% Authors : Daniel S. McCain <[email protected]>,
+%% Magnus Lång <[email protected]>
+%% Created : 14 Apr 2014 by Magnus Lång <[email protected]>
+%% Purpose : Eliminate calls to BIFs that are side-effect free only when
+%% executed on some argument types.
+%%----------------------------------------------------------------------
+-module(hipe_icode_call_elim).
+-export([cfg/1]).
+
+-include("hipe_icode.hrl").
+-include("../flow/cfg.hrl").
+
+-spec cfg(cfg()) -> cfg().
+
+cfg(IcodeSSA) ->
+ lists:foldl(fun (Lbl, CFG1) ->
+ BB1 = hipe_icode_cfg:bb(CFG1, Lbl),
+ Code1 = hipe_bb:code(BB1),
+ Code2 = lists:map(fun elim_insn/1, Code1),
+ BB2 = hipe_bb:code_update(BB1, Code2),
+ hipe_icode_cfg:bb_add(CFG1, Lbl, BB2)
+ end, IcodeSSA, hipe_icode_cfg:labels(IcodeSSA)).
+
+-spec elim_insn(icode_instr()) -> icode_instr().
+elim_insn(Insn=#icode_call{'fun'={_,_,_}=MFA, args=Args, type=remote,
+ dstlist=[Dst=#icode_variable{
+ annotation={type_anno, RetType, _}}]}) ->
+ Opaques = 'universe',
+ case erl_types:t_is_singleton(RetType, Opaques) of
+ true ->
+ ArgTypes = [case Arg of
+ #icode_variable{annotation={type_anno, Type, _}} -> Type;
+ #icode_const{} ->
+ erl_types:t_from_term(hipe_icode:const_value(Arg))
+ end || Arg <- Args],
+ case can_be_eliminated(MFA, ArgTypes) of
+ true ->
+ Const = hipe_icode:mk_const(
+ erl_types:t_singleton_to_term(RetType, Opaques)),
+ #icode_move{dst=Dst, src=Const};
+ false -> Insn
+ end;
+ false -> Insn
+ end;
+elim_insn(Insn) -> Insn.
+
+
+%% A function can be eliminated for some argument types if it has no side
+%% effects when run on arguments of those types.
+
+-spec can_be_eliminated(mfa(), [erl_types:erl_type()]) -> boolean().
+
+can_be_eliminated({maps, is_key, 2}, [_K, M]) ->
+ erl_types:t_is_map(M);
+can_be_eliminated(_, _) ->
+ false.
diff --git a/lib/hipe/icode/hipe_icode_type.erl b/lib/hipe/icode/hipe_icode_type.erl
index e3ba00c5e9..5eae8d440a 100644
--- a/lib/hipe/icode/hipe_icode_type.erl
+++ b/lib/hipe/icode/hipe_icode_type.erl
@@ -363,6 +363,7 @@ call_always_fails(#icode_call{} = I, Info) ->
%% These can actually be calls too.
{erlang, halt, 0} -> false;
{erlang, halt, 1} -> false;
+ {erlang, halt, 2} -> false;
{erlang, exit, 1} -> false;
{erlang, error, 1} -> false;
{erlang, error, 2} -> false;
diff --git a/lib/hipe/llvm/Makefile b/lib/hipe/llvm/Makefile
index d2d39fb9e3..25b47a580f 100644
--- a/lib/hipe/llvm/Makefile
+++ b/lib/hipe/llvm/Makefile
@@ -40,12 +40,12 @@ RELSYSDIR = $(RELEASE_PATH)/lib/hipe-$(VSN)
# Target Specs
# ----------------------------------------------------
ifdef HIPE_ENABLED
-HIPE_MODULES = hipe_rtl_to_llvm \
+HIPE_MODULES = elf_format \
hipe_llvm \
- elf_format \
+ hipe_llvm_liveness \
hipe_llvm_main \
hipe_llvm_merge \
- hipe_llvm_liveness
+ hipe_rtl_to_llvm
else
HIPE_MODULES =
endif
@@ -71,7 +71,7 @@ TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR))
include ../native.mk
-ERL_COMPILE_FLAGS += +inline #+warn_missing_spec
+ERL_COMPILE_FLAGS += +inline +warn_export_vars #+warn_missing_spec
# if in 32 bit backend define BIT32 symbol
ARCH = $(shell echo $(TARGET) | sed 's/^\(x86_64\)-.*/64bit/')
@@ -108,3 +108,11 @@ release_spec: opt
$(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin
release_docs_spec:
+
+$(EBIN)/elf_format.beam: elf_format.hrl elf32_format.hrl elf64_format.hrl
+$(EBIN)/hipe_llvm_main.beam: ../../kernel/src/hipe_ext_format.hrl \
+ hipe_llvm_arch.hrl elf_format.hrl elf32_format.hrl elf64_format.hrl
+$(EBIN)/hipe_llvm_merge.beam: ../../kernel/src/hipe_ext_format.hrl \
+ hipe_llvm_arch.hrl ../rtl/hipe_literals.hrl ../main/hipe.hrl
+$(EBIN)/hipe_rtl_to_llvm.beam: ../rtl/hipe_rtl.hrl ../rtl/hipe_literals.hrl \
+ hipe_llvm_arch.hrl
diff --git a/lib/hipe/llvm/elf_format.erl b/lib/hipe/llvm/elf_format.erl
index 260da9b5e6..8cf6ea6250 100644
--- a/lib/hipe/llvm/elf_format.erl
+++ b/lib/hipe/llvm/elf_format.erl
@@ -13,21 +13,20 @@
-module(elf_format).
--export([get_tab_entries/1,
- %% Relocations
- get_rodata_relocs/1,
- get_text_relocs/1,
+-export([%% Relocations
extract_rela/2,
- get_rela_addends/1,
%% Note
extract_note/2,
%% Executable code
extract_text/1,
%% GCC Exception Table
- get_exn_handlers/1,
- %% Misc.
- set_architecture_flag/1,
- is64bit/0
+ get_exn_handlers/1,
+ %% Symbols
+ elf_symbols/1,
+ %% Sections
+ section_contents/2,
+ %% Main interface
+ read/1
]).
-include("elf_format.hrl").
@@ -36,27 +35,57 @@
%% Types
%%------------------------------------------------------------------------------
--type elf() :: binary().
-
--type lp() :: non_neg_integer(). % landing pad
--type num() :: non_neg_integer().
--type index() :: non_neg_integer().
--type offset() :: non_neg_integer().
--type size() :: non_neg_integer().
--type start() :: non_neg_integer().
-
--type info() :: index().
--type nameoff() :: offset().
--type valueoff() :: offset().
-
--type name() :: string().
--type name_size() :: {name(), size()}.
--type name_sizes() :: [name_size()].
+-export_type([elf/0
+ ,addend/0
+ ,bitflags/0
+ ,name/0
+ ,offset/0
+ ,reloc_type/0
+ ,shdr_type/0
+ ,size/0
+ ,sym_bind/0
+ ,sym_type/0
+ ,valueoff/0
+ ]).
+
+-type bitflags() :: non_neg_integer().
+-type index() :: non_neg_integer().
+-type lp() :: non_neg_integer(). % landing pad
+-type num() :: non_neg_integer().
+-type offset() :: non_neg_integer().
+-type size() :: non_neg_integer().
+-type start() :: non_neg_integer().
+
+-type addend() :: integer() | undefined.
+-type name() :: string().
+-type shdr_type() :: 'null' | 'progbits' | 'symtab' | 'strtab' | 'rela'
+ | 'hash' | 'dynamic' | 'note' | 'nobits' | 'rel' | 'shlib'
+ | 'dynsym' | {os, ?SHT_LOOS..?SHT_HIOS}
+ | {proc, ?SHT_LOPROC..?SHT_HIPROC}.
+-type sym_bind() :: 'local' | 'global' | 'weak' | {os, ?STB_LOOS..?STB_HIOS}
+ | {proc, ?STB_LOPROC..?STB_HIPROC}.
+-type sym_type() :: 'notype' | 'object' | 'func' | 'section' | 'file'
+ | {os, ?STT_LOOS..?STT_HIOS}
+ | {proc, ?STT_LOPROC..?STT_HIPROC}.
+-type valueoff() :: offset().
+
+-ifdef(BIT32). % 386
+-type reloc_type() :: '32' | 'pc32'.
+-else. % X86_64
+-type reloc_type() :: '64' | 'pc32' | '32'.
+-endif.
%%------------------------------------------------------------------------------
%% Abstract Data Types and Accessors for ELF Structures.
%%------------------------------------------------------------------------------
+-record(elf, {file :: binary()
+ ,sections :: [elf_shdr()]
+ ,sec_nam :: #{string() => elf_shdr()}
+ ,symbols :: undefined | [elf_sym()]
+ }).
+-opaque elf() :: #elf{}.
+
%% File header
-record(elf_ehdr, {ident, % ELF identification
type, % Object file type
@@ -85,42 +114,6 @@
}).
%% -type elf_ehdr_ident() :: #elf_ehdr_ident{}.
-%% Section header entries
--record(elf_shdr, {name, % Section name
- type, % Section type
- flags, % Section attributes
- addr, % Virtual address in memory
- offset :: offset(), % Offset in file
- size :: size(), % Size of section
- link, % Link to other section
- info, % Miscellaneous information
- addralign, % Address align boundary
- entsize % Size of entries, if section has table
- }).
-%% -type elf_shdr() :: #elf_shdr{}.
-
-%% Symbol table entries
--record(elf_sym, {name :: nameoff(), % Symbol name
- info, % Type and Binding attributes
- other, % Reserved
- shndx, % Section table index
- value :: valueoff(), % Symbol value
- size :: size() % Size of object
- }).
--type elf_sym() :: #elf_sym{}.
-
-%% Relocations
--record(elf_rel, {r_offset :: offset(), % Address of reference
- r_info :: info() % Symbol index and type of relocation
- }).
--type elf_rel() :: #elf_rel{}.
-
--record(elf_rela, {r_offset :: offset(), % Address of reference
- r_info :: info(), % Symbol index and type of relocation
- r_addend :: offset() % Constant part of expression
- }).
--type elf_rela() :: #elf_rela{}.
-
%% %% Program header table
%% -record(elf_phdr, {type, % Type of segment
%% flags, % Segment attributes
@@ -199,44 +192,19 @@ mk_shdr(Name, Type, Flags, Addr, Offset, Size, Link, Info, AddrAlign, EntSize) -
%%%-------------------------
%%% Symbol Table Entries
%%%-------------------------
-mk_sym(Name, Info, Other, Shndx, Value, Size) ->
- #elf_sym{name = Name, info = Info, other = Other,
- shndx = Shndx, value = Value, size = Size}.
-
--spec sym_name(elf_sym()) -> nameoff().
-sym_name(#elf_sym{name = Name}) -> Name.
+mk_sym(Name, Bind, Type, Section, Value, Size) ->
+ #elf_sym{name = Name, bind = Bind, type = Type,
+ section = Section, value = Value, size = Size}.
+%% -spec sym_name(elf_sym()) -> string().
+%% sym_name(#elf_sym{name = Name}) -> Name.
+%%
%% -spec sym_value(elf_sym()) -> valueoff().
%% sym_value(#elf_sym{value = Value}) -> Value.
%%
%% -spec sym_size(elf_sym()) -> size().
%% sym_size(#elf_sym{size = Size}) -> Size.
-%%%-------------------------
-%%% Relocations
-%%%-------------------------
--spec mk_rel(offset(), info()) -> elf_rel().
-mk_rel(Offset, Info) ->
- #elf_rel{r_offset = Offset, r_info = Info}.
-
-%% The following two functions capitalize on the fact that the two kinds of
-%% relocation records (for 32- and 64-bit architectures have similar structure.
-
--spec r_offset(elf_rel() | elf_rela()) -> offset().
-r_offset(#elf_rel{r_offset = Offset}) -> Offset;
-r_offset(#elf_rela{r_offset = Offset}) -> Offset.
-
--spec r_info(elf_rel() | elf_rela()) -> info().
-r_info(#elf_rel{r_info = Info}) -> Info;
-r_info(#elf_rela{r_info = Info}) -> Info.
-
--spec mk_rela(offset(), info(), offset()) -> elf_rela().
-mk_rela(Offset, Info, Addend) ->
- #elf_rela{r_offset = Offset, r_info = Info, r_addend = Addend}.
-
--spec rela_addend(elf_rela()) -> offset().
-rela_addend(#elf_rela{r_addend = Addend}) -> Addend.
-
%% %%%-------------------------
%% %%% GCC exception table
%% %%%-------------------------
@@ -263,15 +231,30 @@ mk_gccexntab_callsite(Start, Size, LP, Action) ->
%% gccexntab_callsite_lp(#elf_gccexntab_callsite{lp = LP}) -> LP.
%%------------------------------------------------------------------------------
+%% Main interface function
+%%------------------------------------------------------------------------------
+
+%% @doc Parses an ELF file.
+-spec read(binary()) -> elf().
+read(ElfBin) ->
+ Header = extract_header(ElfBin),
+ [_UndefinedSec|Sections] = extract_shdrtab(ElfBin, Header),
+ SecNam = maps:from_list(
+ [{Name, Sec} || Sec = #elf_shdr{name=Name} <- Sections]),
+ Elf0 = #elf{file=ElfBin, sections=Sections, sec_nam=SecNam},
+ [_UndefinedSym|Symbols] = extract_symtab(Elf0, extract_strtab(Elf0)),
+ Elf0#elf{symbols=Symbols}.
+
+%%------------------------------------------------------------------------------
%% Functions to manipulate the ELF File Header
%%------------------------------------------------------------------------------
%% @doc Extracts the File Header from an ELF formatted object file. Also sets
%% the ELF class variable in the process dictionary (used by many functions
%% in this and hipe_llvm_main modules).
--spec extract_header(elf()) -> elf_ehdr().
-extract_header(Elf) ->
- Ehdr_bin = get_binary_segment(Elf, 0, ?ELF_EHDR_SIZE),
+-spec extract_header(binary()) -> elf_ehdr().
+extract_header(ElfBin) ->
+ Ehdr_bin = get_binary_segment(ElfBin, 0, ?ELF_EHDR_SIZE),
<< %% Structural pattern matching on fields.
Ident_bin:?E_IDENT_SIZE/binary,
Type:?bits(?E_TYPE_SIZE)/integer-little,
@@ -300,19 +283,28 @@ extract_header(Elf) ->
%% Functions to manipulate Section Header Entries
%%------------------------------------------------------------------------------
+-type shdrtab() :: [elf_shdr()].
+
%% @doc Extracts the Section Header Table from an ELF formated Object File.
-extract_shdrtab(Elf) ->
- %% Extract File Header to get info about Section Header Offset (in bytes),
- %% Entry Size (in bytes) and Number of entries
- #elf_ehdr{shoff = ShOff, shentsize = ShEntsize, shnum = ShNum} =
- extract_header(Elf),
+-spec extract_shdrtab(binary(), elf_ehdr()) -> shdrtab().
+extract_shdrtab(ElfBin, #elf_ehdr{shoff=ShOff, shentsize=?ELF_SHDRENTRY_SIZE,
+ shnum=ShNum, shstrndx=ShStrNdx}) ->
%% Get actual Section header table (binary)
- ShdrBin = get_binary_segment(Elf, ShOff, ShNum * ShEntsize),
- get_shdrtab_entries(ShdrBin, []).
-
-get_shdrtab_entries(<<>>, Acc) ->
- lists:reverse(Acc);
-get_shdrtab_entries(ShdrBin, Acc) ->
+ ShdrBin = get_binary_segment(ElfBin, ShOff, ShNum * ?ELF_SHDRENTRY_SIZE),
+ %% We need to lookup the offset and size of the section header string table
+ %% before we can fully parse the section table. We compute its offset and
+ %% extract the fields we need here.
+ ShStrEntryOffset = ShStrNdx * ?ELF_SHDRENTRY_SIZE,
+ <<_:ShStrEntryOffset/binary, _:?SH_NAME_SIZE/binary,
+ _:?SH_TYPE_SIZE/binary, _:?SH_FLAGS_SIZE/binary, _:?SH_ADDR_SIZE/binary,
+ ShStrOffset:?bits(?SH_OFFSET_SIZE)/little,
+ ShStrSize:?bits(?SH_SIZE_SIZE)/little,
+ _/binary>> = ShdrBin,
+ ShStrTab = parse_strtab(get_binary_segment(ElfBin, ShStrOffset, ShStrSize)),
+ get_shdrtab_entries(ShdrBin, ShStrTab).
+
+get_shdrtab_entries(<<>>, _ShStrTab) -> [];
+get_shdrtab_entries(ShdrTab, ShStrTab) ->
<<%% Structural pattern matching on fields.
Name:?bits(?SH_NAME_SIZE)/integer-little,
Type:?bits(?SH_TYPE_SIZE)/integer-little,
@@ -324,205 +316,166 @@ get_shdrtab_entries(ShdrBin, Acc) ->
Info:?bits(?SH_INFO_SIZE)/integer-little,
Addralign:?bits(?SH_ADDRALIGN_SIZE)/integer-little,
Entsize:?bits(?SH_ENTSIZE_SIZE)/integer-little,
- MoreShdrE/binary
- >> = ShdrBin,
- ShdrE = mk_shdr(Name, Type, Flags, Addr, Offset,
- Size, Link, Info, Addralign, Entsize),
- get_shdrtab_entries(MoreShdrE, [ShdrE | Acc]).
-
-%% @doc Extracts a specific Entry of a Section Header Table. This function
-%% takes as argument the Section Header Table (`SHdrTab') and the entry's
-%% serial number (`EntryNum') and returns the entry (`shdr').
-get_shdrtab_entry(SHdrTab, EntryNum) ->
- lists:nth(EntryNum + 1, SHdrTab).
-
-%%------------------------------------------------------------------------------
-%% Functions to manipulate Section Header String Table
-%%------------------------------------------------------------------------------
-
-%% @doc Extracts the Section Header String Table. This section is not a known
-%% ELF Object File section. It is just a "hidden" table storing the
-%% names of all sections that exist in current object file.
--spec extract_shstrtab(elf()) -> [name()].
-extract_shstrtab(Elf) ->
- %% Extract Section Name String Table Index
- #elf_ehdr{shstrndx = ShStrNdx} = extract_header(Elf),
- ShHdrTab = extract_shdrtab(Elf),
- %% Extract Section header entry and get actual Section-header String Table
- #elf_shdr{offset = ShStrOffset, size = ShStrSize} =
- get_shdrtab_entry(ShHdrTab, ShStrNdx),
- case get_binary_segment(Elf, ShStrOffset, ShStrSize) of
- <<>> -> %% Segment empty
- [];
- ShStrTab -> %% Convert to string table
- [Name || {Name, _Size} <- get_names(ShStrTab)]
- end.
-
-%%------------------------------------------------------------------------------
-
--spec get_tab_entries(elf()) -> [{name(), valueoff(), size()}].
-get_tab_entries(Elf) ->
- SymTab = extract_symtab(Elf),
- Ts = [{Name, Value, Size div ?ELF_XWORD_SIZE}
- || #elf_sym{name = Name, value = Value, size = Size} <- SymTab,
- Name =/= 0],
- {NameIndices, ValueOffs, Sizes} = lists:unzip3(Ts),
- %% Find the names of the symbols.
- %% Get string table entries ([{Name, Offset in strtab section}]). Keep only
- %% relevant entries:
- StrTab = extract_strtab(Elf),
- Relevant = [get_strtab_entry(StrTab, Off) || Off <- NameIndices],
- %% Zip back to {Name, ValueOff, Size}
- lists:zip3(Relevant, ValueOffs, Sizes).
+ Rest/binary
+ >> = ShdrTab,
+ Entry = mk_shdr(get_strtab_entry(Name, ShStrTab), decode_shdr_type(Type),
+ Flags, Addr, Offset, Size, Link, Info, Addralign, Entsize),
+ [Entry | get_shdrtab_entries(Rest, ShStrTab)].
+
+decode_shdr_type(?SHT_NULL) -> 'null';
+decode_shdr_type(?SHT_PROGBITS) -> 'progbits';
+decode_shdr_type(?SHT_SYMTAB) -> 'symtab';
+decode_shdr_type(?SHT_STRTAB) -> 'strtab';
+decode_shdr_type(?SHT_RELA) -> 'rela';
+decode_shdr_type(?SHT_HASH) -> 'hash'; %unused
+decode_shdr_type(?SHT_DYNAMIC) -> 'dynamic'; %unused
+decode_shdr_type(?SHT_NOTE) -> 'note'; %unused
+decode_shdr_type(?SHT_NOBITS) -> 'nobits';
+decode_shdr_type(?SHT_REL) -> 'rel';
+decode_shdr_type(?SHT_SHLIB) -> 'shlib'; %unused
+decode_shdr_type(?SHT_DYNSYM) -> 'dynsym'; %unused
+decode_shdr_type(OS) when ?SHT_LOOS =< OS, OS =< ?SHT_HIOS -> {os, OS};
+decode_shdr_type(Proc) when ?SHT_LOPROC =< Proc, Proc =< ?SHT_HIPROC ->
+ {proc, Proc}.
+
+-spec elf_section(non_neg_integer(), elf()) -> undefined | abs | elf_shdr().
+elf_section(0, #elf{}) -> undefined;
+elf_section(?SHN_ABS, #elf{}) -> abs;
+elf_section(Index, #elf{sections=SecIdx}) ->
+ lists:nth(Index, SecIdx).
+
+%% Reads the contents of a section from an object
+-spec section_contents(elf_shdr(), elf()) -> binary().
+section_contents(#elf_shdr{offset=Offset, size=Size}, #elf{file=ElfBin}) ->
+ get_binary_segment(ElfBin, Offset, Size).
%%------------------------------------------------------------------------------
%% Functions to manipulate Symbol Table
%%------------------------------------------------------------------------------
%% @doc Function that extracts Symbol Table from an ELF Object file.
-extract_symtab(Elf) ->
- Symtab_bin = extract_segment_by_name(Elf, ?SYMTAB),
- get_symtab_entries(Symtab_bin, []).
-
-get_symtab_entries(<<>>, Acc) ->
- lists:reverse(Acc);
-get_symtab_entries(Symtab_bin, Acc) ->
- <<SymE_bin:?ELF_SYM_SIZE/binary, MoreSymE/binary>> = Symtab_bin,
- case is64bit() of
- true ->
- <<%% Structural pattern matching on fields.
- Name:?bits(?ST_NAME_SIZE)/integer-little,
- Info:?bits(?ST_INFO_SIZE)/integer-little,
- Other:?bits(?ST_OTHER_SIZE)/integer-little,
- Shndx:?bits(?ST_SHNDX_SIZE)/integer-little,
- Value:?bits(?ST_VALUE_SIZE)/integer-little,
- Size:?bits(?ST_SIZE_SIZE)/integer-little
- >> = SymE_bin;
- false ->
- << %% Same fields in different order:
- Name:?bits(?ST_NAME_SIZE)/integer-little,
- Value:?bits(?ST_VALUE_SIZE)/integer-little,
- Size:?bits(?ST_SIZE_SIZE)/integer-little,
- Info:?bits(?ST_INFO_SIZE)/integer-little,
- Other:?bits(?ST_OTHER_SIZE)/integer-little,
- Shndx:?bits(?ST_SHNDX_SIZE)/integer-little
- >> = SymE_bin
- end,
- SymE = mk_sym(Name, Info, Other, Shndx, Value, Size),
- get_symtab_entries(MoreSymE, [SymE | Acc]).
-
-%% @doc Extracts a specific entry from the Symbol Table (as binary).
-%% This function takes as arguments the Symbol Table (`SymTab')
-%% and the entry's serial number and returns that entry (`sym').
-get_symtab_entry(SymTab, EntryNum) ->
- lists:nth(EntryNum + 1, SymTab).
+extract_symtab(Elf, StrTab) ->
+ Symtab = extract_segment_by_name(Elf, ?SYMTAB),
+ [parse_sym(Sym, Elf, StrTab) || <<Sym:?ELF_SYM_SIZE/binary>> <= Symtab].
+
+-ifdef(BIT32).
+parse_sym(<<%% Structural pattern matching on fields.
+ Name:?bits(?ST_NAME_SIZE)/integer-little,
+ Value:?bits(?ST_VALUE_SIZE)/integer-little,
+ Size:?bits(?ST_SIZE_SIZE)/integer-little,
+ Info:?bits(?ST_INFO_SIZE)/integer-little,
+ _Other:?bits(?ST_OTHER_SIZE)/integer-little,
+ Shndx:?bits(?ST_SHNDX_SIZE)/integer-little>>,
+ Elf, StrTab) ->
+ mk_sym(get_strtab_entry(Name, StrTab), decode_symbol_bind(?ELF_ST_BIND(Info)),
+ decode_symbol_type(?ELF_ST_TYPE(Info)), elf_section(Shndx, Elf), Value,
+ Size).
+-else.
+parse_sym(<<%% Same fields in different order:
+ Name:?bits(?ST_NAME_SIZE)/integer-little,
+ Info:?bits(?ST_INFO_SIZE)/integer-little,
+ _Other:?bits(?ST_OTHER_SIZE)/integer-little,
+ Shndx:?bits(?ST_SHNDX_SIZE)/integer-little,
+ Value:?bits(?ST_VALUE_SIZE)/integer-little,
+ Size:?bits(?ST_SIZE_SIZE)/integer-little>>,
+ Elf, StrTab) ->
+ mk_sym(get_strtab_entry(Name, StrTab), decode_symbol_bind(?ELF_ST_BIND(Info)),
+ decode_symbol_type(?ELF_ST_TYPE(Info)), elf_section(Shndx, Elf), Value,
+ Size).
+-endif.
+
+decode_symbol_bind(?STB_LOCAL) -> 'local';
+decode_symbol_bind(?STB_GLOBAL) -> 'global';
+decode_symbol_bind(?STB_WEAK) -> 'weak'; %unused
+decode_symbol_bind(OS) when ?STB_LOOS =< OS, OS =< ?STB_HIOS -> {os, OS};
+decode_symbol_bind(Proc) when ?STB_LOPROC =< Proc, Proc =< ?STB_HIPROC ->
+ {proc, Proc}.
+
+decode_symbol_type(?STT_NOTYPE) -> 'notype';
+decode_symbol_type(?STT_OBJECT) -> 'object';
+decode_symbol_type(?STT_FUNC) -> 'func';
+decode_symbol_type(?STT_SECTION) -> 'section';
+decode_symbol_type(?STT_FILE) -> 'file';
+decode_symbol_type(OS) when ?STT_LOOS =< OS, OS =< ?STT_HIOS -> {os, OS};
+decode_symbol_type(Proc) when ?STT_LOPROC =< Proc, Proc =< ?STT_HIPROC ->
+ {proc, Proc}.
+
+%% @doc Extracts a specific entry from the Symbol Table.
+-spec elf_symbol(0, elf()) -> undefined;
+ (pos_integer(), elf()) -> elf_sym().
+elf_symbol(0, #elf{}) -> undefined;
+elf_symbol(Index, #elf{symbols=Symbols}) -> lists:nth(Index, Symbols).
+
+-spec elf_symbols(elf()) -> [elf_sym()].
+elf_symbols(#elf{symbols=Symbols}) -> Symbols.
%%------------------------------------------------------------------------------
%% Functions to manipulate String Table
%%------------------------------------------------------------------------------
+%% ADT: get_strtab_entry/1 must be used to consume this type.
+-type strtab() :: binary().
+
%% @doc Extracts String Table from an ELF formated Object File.
--spec extract_strtab(elf()) -> [{string(), offset()}].
+-spec extract_strtab(elf()) -> strtab().
extract_strtab(Elf) ->
- Strtab_bin = extract_segment_by_name(Elf, ?STRTAB),
- NamesSizes = get_names(Strtab_bin),
- make_offsets(NamesSizes).
-
-%% @doc Returns the name of the symbol at the given offset. The string table
-%% contains entries of the form {Name, Offset}. If no such offset exists
-%% returns the empty string (`""').
-%% XXX: There might be a bug here because of the "compact" saving the ELF
-%% format uses: e.g. only stores ".rela.text" for ".rela.text" and ".text".
-get_strtab_entry(Strtab, Offset) ->
- case lists:keyfind(Offset, 2, Strtab) of
- {Name, Offset} -> Name;
- false -> ""
- end.
+ parse_strtab(extract_segment_by_name(Elf, ?STRTAB)).
+
+-spec parse_strtab(binary()) -> strtab().
+parse_strtab(StrTabSectionBin) -> StrTabSectionBin.
+
+%% @doc Returns the name of the symbol at the given offset.
+-spec get_strtab_entry(non_neg_integer(), strtab()) -> string().
+get_strtab_entry(Offset, StrTab) ->
+ <<_:Offset/binary, StrBin/binary>> = StrTab,
+ bin_get_string(StrBin).
+
+%% @doc Extracts a null-terminated string from a binary.
+-spec bin_get_string(binary()) -> string().
+%% FIXME: No regard for encoding (just happens to work for ASCII and Latin-1)
+bin_get_string(<<0, _/binary>>) -> [];
+bin_get_string(<<Char, Rest/binary>>) -> [Char|bin_get_string(Rest)].
%%------------------------------------------------------------------------------
%% Functions to manipulate Relocations
%%------------------------------------------------------------------------------
-%% @doc This function gets as argument an ELF binary file and returns a list
-%% with all .rela.rodata labels (i.e. constants and literals in code)
-%% or an empty list if no ".rela.rodata" section exists in code.
--spec get_rodata_relocs(elf()) -> [offset()].
-get_rodata_relocs(Elf) ->
- case is64bit() of
- true ->
- %% Only care about the addends (== offsets):
- get_rela_addends(extract_rela(Elf, ?RODATA));
- false ->
- %% Find offsets hardcoded in ".rodata" entry
- %%XXX: Treat all 0s as padding and skip them!
- [SkipPadding || SkipPadding <- extract_rodata(Elf), SkipPadding =/= 0]
- end.
-
--spec get_rela_addends([elf_rela()]) -> [offset()].
-get_rela_addends(RelaEntries) ->
- [rela_addend(E) || E <- RelaEntries].
-
-%% @doc Extract a list of the form `[{SymbolName, Offset}]' with all relocatable
-%% symbols and their offsets in the code from the ".text" section.
--spec get_text_relocs(elf()) -> [{name(), offset()}].
-get_text_relocs(Elf) ->
- %% Only care about the symbol table index and the offset:
- NameOffsetTemp = [{?ELF_R_SYM(r_info(E)), r_offset(E)}
- || E <- extract_rela(Elf, ?TEXT)],
- {NameIndices, ActualOffsets} = lists:unzip(NameOffsetTemp),
- %% Find the names of the symbols:
- %%
- %% Get those symbol table entries that are related to Text relocs:
- Symtab = extract_symtab(Elf),
- SymtabEs = [get_symtab_entry(Symtab, Index) || Index <- NameIndices],
- %XXX: not zero-indexed!
- %% Symbol table entries contain the offset of the name of the symbol in
- %% String Table:
- SymtabEs2 = [sym_name(E) || E <- SymtabEs], %XXX: Do we need to sort SymtabE?
- %% Get string table entries ([{Name, Offset in strtab section}]). Keep only
- %% relevant entries:
- Strtab = extract_strtab(Elf),
- Relevant = [get_strtab_entry(Strtab, Off) || Off <- SymtabEs2],
- %% Zip back with actual offsets:
- lists:zip(Relevant, ActualOffsets).
-
%% @doc Extract the Relocations segment for section `Name' (that is passed
%% as second argument) from an ELF formated Object file binary.
--spec extract_rela(elf(), name()) -> [elf_rel() | elf_rela()].
+-spec extract_rela(elf(), name()) -> [elf_rel()].
+
+-ifdef(BIT32).
extract_rela(Elf, Name) ->
- SegName =
- case is64bit() of
- true -> ?RELA(Name); % ELF-64 uses ".rela"
- false -> ?REL(Name) % ...while ELF-32 uses ".rel"
- end,
- Rela_bin = extract_segment_by_name(Elf, SegName),
- get_rela_entries(Rela_bin, []).
-
-get_rela_entries(<<>>, Acc) ->
- lists:reverse(Acc);
-get_rela_entries(Bin, Acc) ->
- E = case is64bit() of
- true ->
- <<%% Structural pattern matching on fields of a Rela Entry.
- Offset:?bits(?R_OFFSET_SIZE)/integer-little,
- Info:?bits(?R_INFO_SIZE)/integer-little,
- Addend:?bits(?R_ADDEND_SIZE)/integer-little,
- Rest/binary
- >> = Bin,
- mk_rela(Offset, Info, Addend);
- false ->
- <<%% Structural pattern matching on fields of a Rel Entry.
- Offset:?bits(?R_OFFSET_SIZE)/integer-little,
- Info:?bits(?R_INFO_SIZE)/integer-little,
- Rest/binary
- >> = Bin,
- mk_rel(Offset, Info)
- end,
- get_rela_entries(Rest, [E | Acc]).
-
-%% %% @doc Extract the `EntryNum' (serial number) Relocation Entry.
-%% get_rela_entry(Rela, EntryNum) ->
-%% lists:nth(EntryNum + 1, Rela).
+ SecData = extract_segment_by_name(Elf, Name),
+ [#elf_rel{offset=Offset, symbol=elf_symbol(?ELF_R_SYM(Info), Elf),
+ type=decode_reloc_type(?ELF_R_TYPE(Info)),
+ addend=read_implicit_addend(Offset, SecData)}
+ || <<Offset:?bits(?R_OFFSET_SIZE)/little,
+ Info:?bits(?R_INFO_SIZE)/little % 386 uses ".rel"
+ >> <= extract_segment_by_name(Elf, ?REL(Name))].
+
+%% The only types HiPE knows how to patch
+decode_reloc_type(1) -> '32';
+decode_reloc_type(2) -> 'pc32'.
+
+read_implicit_addend(Offset, Section) ->
+ %% All x86 relocation types uses 'word32' relocation fields; i.e. 32-bit LE.
+ <<_:Offset/binary, Addend:32/signed-little, _/binary>> = Section,
+ Addend.
+
+-else. %% BIT32
+extract_rela(Elf, Name) ->
+ [#elf_rel{offset=Offset, symbol=elf_symbol(?ELF_R_SYM(Info), Elf),
+ type=decode_reloc_type(?ELF_R_TYPE(Info)), addend=Addend}
+ || <<Offset:?bits(?R_OFFSET_SIZE)/little,
+ Info:?bits(?R_INFO_SIZE)/little,
+ Addend:?bits(?R_ADDEND_SIZE)/signed-little % X86_64 uses ".rela"
+ >> <= extract_segment_by_name(Elf, ?RELA(Name))].
+
+decode_reloc_type(1) -> '64';
+decode_reloc_type(2) -> 'pc32';
+decode_reloc_type(10) -> '32'.
+-endif. %% BIT32
%%------------------------------------------------------------------------------
%% Functions to manipulate Executable Code segment
@@ -615,19 +568,6 @@ get_gccexntab_callsites(CSTab, Acc) ->
get_gccexntab_callsites(More, [GccCS | Acc]).
%%------------------------------------------------------------------------------
-%% Functions to manipulate Read-only Data (.rodata)
-%%------------------------------------------------------------------------------
-extract_rodata(Elf) ->
- Rodata_bin = extract_segment_by_name(Elf, ?RODATA),
- get_rodata_entries(Rodata_bin, []).
-
-get_rodata_entries(<<>>, Acc) ->
- lists:reverse(Acc);
-get_rodata_entries(Rodata_bin, Acc) ->
- <<Num:?bits(?ELF_ADDR_SIZE)/integer-little, More/binary>> = Rodata_bin,
- get_rodata_entries(More, [Num | Acc]).
-
-%%------------------------------------------------------------------------------
%% Helper functions
%%------------------------------------------------------------------------------
@@ -647,107 +587,15 @@ get_binary_segment(Bin, Offset, Size) ->
%% There are handy macros defined in elf_format.hrl for all Standard
%% Section Names.
-spec extract_segment_by_name(elf(), string()) -> binary().
-extract_segment_by_name(Elf, SectionName) ->
- %% Extract Section Header Table and Section Header String Table from binary
- SHdrTable = extract_shdrtab(Elf),
- Names = extract_shstrtab(Elf),
- %% Zip to a list of (Name,ShdrE)
- [_Zero | ShdrEs] = lists:keysort(2, SHdrTable), % Skip first entry (zeros).
- L = lists:zip(Names, ShdrEs),
+extract_segment_by_name(#elf{file=ElfBin, sec_nam=SecNam}, SectionName) ->
%% Find Section Header Table entry by name
- case lists:keyfind(SectionName, 1, L) of
- {SectionName, ShdrE} -> %% Note: Same name.
- #elf_shdr{offset = Offset, size = Size} = ShdrE,
- get_binary_segment(Elf, Offset, Size);
- false -> %% Not found.
+ case SecNam of
+ #{SectionName := #elf_shdr{offset=Offset, size=Size}} ->
+ get_binary_segment(ElfBin, Offset, Size);
+ #{} -> %% Not found.
<<>>
end.
-%% @doc Extracts a list of strings with (zero-separated) names from a binary.
-%% Returns tuples of `{Name, Size}'.
-%% XXX: Skip trailing 0.
--spec get_names(<<_:8,_:_*8>>) -> name_sizes().
-get_names(<<0, Bin/binary>>) ->
- NamesSizes = get_names(Bin, []),
- fix_names(NamesSizes, []).
-
-get_names(<<>>, Acc) ->
- lists:reverse(Acc);
-get_names(Bin, Acc) ->
- {Name, MoreNames} = bin_get_string(Bin),
- get_names(MoreNames, [{Name, length(Name)} | Acc]).
-
-%% @doc Fix names:
-%% e.g. If ".rela.text" exists, ".text" does not. Same goes for
-%% ".rel.text". In that way, the Section Header String Table is more
-%% compact. Add ".text" just *before* the corresponding rela-field,
-%% etc.
--spec fix_names(name_sizes(), name_sizes()) -> name_sizes().
-fix_names([], Acc) ->
- lists:reverse(Acc);
-fix_names([{Name, Size}=T | Names], Acc) ->
- case is64bit() of
- true ->
- case string:str(Name, ".rela") =:= 1 of
- true -> %% Name starts with ".rela":
- Section = string:substr(Name, 6),
- fix_names(Names, [{Section, Size - 5}
- | [T | Acc]]); % XXX: Is order ok? (".text"
- % always before ".rela.text")
- false -> %% Name does not start with ".rela":
- fix_names(Names, [T | Acc])
- end;
- false ->
- case string:str(Name, ".rel") =:= 1 of
- true -> %% Name starts with ".rel":
- Section = string:substr(Name, 5),
- fix_names(Names, [{Section, Size - 4}
- | [T | Acc]]); % XXX: Is order ok? (".text"
- % always before ".rela.text")
- false -> %% Name does not start with ".rel":
- fix_names(Names, [T | Acc])
- end
- end.
-
-
-%% @doc A function that byte-reverses a binary. This might be needed because of
-%% little (fucking!) endianess.
--spec bin_reverse(binary()) -> binary().
-bin_reverse(Bin) when is_binary(Bin) ->
- bin_reverse(Bin, <<>>).
-
--spec bin_reverse(binary(), binary()) -> binary().
-bin_reverse(<<>>, Acc) ->
- Acc;
-bin_reverse(<<Head, More/binary>>, Acc) ->
- bin_reverse(More, <<Head, Acc/binary>>).
-
-%% @doc A function that extracts a null-terminated string from a binary. It
-%% returns the found string along with the rest of the binary.
--spec bin_get_string(binary()) -> {string(), binary()}.
-bin_get_string(Bin) ->
- bin_get_string(Bin, <<>>).
-
-bin_get_string(<<>>, BinAcc) ->
- Bin = bin_reverse(BinAcc), % little endian!
- {binary_to_list(Bin), <<>>};
-bin_get_string(<<0, MoreBin/binary>>, BinAcc) ->
- Bin = bin_reverse(BinAcc), % little endian!
- {binary_to_list(Bin), MoreBin};
-bin_get_string(<<Letter, Tail/binary>>, BinAcc) ->
- bin_get_string(Tail, <<Letter, BinAcc/binary>>).
-
-%% @doc
-make_offsets(NamesSizes) ->
- {Names, Sizes} = lists:unzip(NamesSizes),
- Offsets = make_offsets_from_sizes(Sizes, 1, []),
- lists:zip(Names, Offsets).
-
-make_offsets_from_sizes([], _, Acc) ->
- lists:reverse(Acc);
-make_offsets_from_sizes([Size | Sizes], Cur, Acc) ->
- make_offsets_from_sizes(Sizes, Size+Cur+1, [Cur | Acc]). % For the "."!
-
%% @doc Little-Endian Base 128 (LEB128) Decoder
%% This function extracts the <b>first</b> LEB128-encoded integer in a
%% binary and returns that integer along with the remaining binary. This is
@@ -770,21 +618,3 @@ leb128_decode(LebNum, NoOfBits, Acc) ->
<<Num:Size/integer>> = <<NextBundle:7/bits, Acc/bits>>,
{Num, MoreLebNums}
end.
-
-%% @doc Extract ELF Class from ELF header and export symbol to process
-%% dictionary.
--spec set_architecture_flag(elf()) -> 'ok'.
-set_architecture_flag(Elf) ->
- %% Extract information about ELF Class from ELF Header
- <<16#7f, $E, $L, $F, EI_Class, _MoreHeader/binary>>
- = get_binary_segment(Elf, 0, ?ELF_EHDR_SIZE),
- put(elf_class, EI_Class),
- ok.
-
-%% @doc Read from object file header if the file class is ELF32 or ELF64.
--spec is64bit() -> boolean().
-is64bit() ->
- case get(elf_class) of
- ?ELFCLASS64 -> true;
- ?ELFCLASS32 -> false
- end.
diff --git a/lib/hipe/llvm/elf_format.hrl b/lib/hipe/llvm/elf_format.hrl
index 7a3cdfead6..57a36f0c3e 100644
--- a/lib/hipe/llvm/elf_format.hrl
+++ b/lib/hipe/llvm/elf_format.hrl
@@ -486,3 +486,43 @@
%% Misc.
%%------------------------------------------------------------------------------
-define(bits(Bytes), ((Bytes) bsl 3)).
+
+%%------------------------------------------------------------------------------
+%% Exported record and type declarations for 'elf_format' module
+%%------------------------------------------------------------------------------
+
+%% Section header entries
+-record(elf_shdr,
+ {name :: elf_format:name() % Section name
+ ,type :: elf_format:shdr_type() % Section type
+ ,flags :: elf_format:bitflags() % Section attributes
+ ,addr :: elf_format:offset() % Virtual address in memory
+ ,offset :: elf_format:offset() % Offset in file
+ ,size :: elf_format:size() % Size of section
+ ,link :: non_neg_integer() % Link to other section
+ ,info :: non_neg_integer() % Miscellaneous information
+ ,addralign :: elf_format:size() % Address align boundary
+ ,entsize :: elf_format:size() % Size of entries, if section has
+ % table
+ }).
+-type elf_shdr() :: #elf_shdr{}.
+
+%% Symbol table entries
+-record(elf_sym,
+ {name :: elf_format:name() % Symbol name
+ ,bind :: elf_format:sym_bind() % Symbol binding
+ ,type :: elf_format:sym_type() % Symbol type
+ ,value :: elf_format:valueoff() % Symbol value
+ ,size :: elf_format:size() % Size of object
+ ,section :: undefined | abs | elf_shdr()
+ }).
+-type elf_sym() :: #elf_sym{}.
+
+%% Relocations
+-record(elf_rel,
+ {offset :: elf_format:offset()
+ ,type :: elf_format:reloc_type()
+ ,addend :: elf_format:addend()
+ ,symbol :: elf_sym()
+ }).
+-type elf_rel() :: #elf_rel{}.
diff --git a/lib/hipe/llvm/hipe_llvm.erl b/lib/hipe/llvm/hipe_llvm.erl
index 5e33731a2b..c2547dd89e 100644
--- a/lib/hipe/llvm/hipe_llvm.erl
+++ b/lib/hipe/llvm/hipe_llvm.erl
@@ -234,7 +234,7 @@
function_arg_type_list/1
]).
--export([pp_ins_list/2, pp_ins/2]).
+-export([pp_ins_list/3, pp_ins/3]).
%%-----------------------------------------------------------------------------
@@ -765,13 +765,17 @@ function_arg_type_list(#llvm_fun{arg_type_list=Arg_type_list}) ->
%% Pretty-printer Functions
%%----------------------------------------------------------------------------
-%% @doc Pretty-print a list of LLVM instructions to a Device.
-pp_ins_list(_Dev, []) -> ok;
-pp_ins_list(Dev, [I|Is]) ->
- pp_ins(Dev, I),
- pp_ins_list(Dev, Is).
+-type llvm_version() :: {Major :: integer(), Minor :: integer()}.
-pp_ins(Dev, I) ->
+%% @doc Pretty-print a list of LLVM instructions to a Device, using syntax
+%% compatible with LLVM v. Major.Minor
+-spec pp_ins_list(file:io_device(), llvm_version(), [llvm_instr()]) -> ok.
+pp_ins_list(_Dev, _Ver, []) -> ok;
+pp_ins_list(Dev, Ver={_,_}, [I|Is]) ->
+ pp_ins(Dev, Ver, I),
+ pp_ins_list(Dev, Ver, Is).
+
+pp_ins(Dev, Ver, I) ->
case indent(I) of
true -> write(Dev, " ");
false -> ok
@@ -861,7 +865,7 @@ pp_ins(Dev, I) ->
true -> write(Dev, "volatile ");
false -> ok
end,
- pp_type(Dev, load_p_type(I)),
+ pp_dereference_type(Dev, Ver, load_p_type(I)),
write(Dev, [" ", load_pointer(I), " "]),
case load_alignment(I) of
[] -> ok;
@@ -897,7 +901,7 @@ pp_ins(Dev, I) ->
true -> write(Dev, "inbounds ");
false -> ok
end,
- pp_type(Dev, getelementptr_p_type(I)),
+ pp_dereference_type(Dev, Ver, getelementptr_p_type(I)),
write(Dev, [" ", getelementptr_value(I)]),
pp_typed_idxs(Dev, getelementptr_typed_idxs(I)),
write(Dev, "\n");
@@ -958,12 +962,16 @@ pp_ins(Dev, I) ->
pp_args(Dev, fun_def_arglist(I)),
write(Dev, ") "),
pp_options(Dev, fun_def_fn_attrs(I)),
+ case Ver >= {3,7} of false -> ok; true ->
+ write(Dev, "personality i32 (i32, i64, i8*,i8*)* "
+ "@__gcc_personality_v0 ")
+ end,
case fun_def_align(I) of
[] -> ok;
N -> write(Dev, ["align ", N])
end,
write(Dev, "{\n"),
- pp_ins_list(Dev, fun_def_body(I)),
+ pp_ins_list(Dev, Ver, fun_def_body(I)),
write(Dev, "}\n");
#llvm_fun_decl{} ->
write(Dev, "declare "),
@@ -992,8 +1000,12 @@ pp_ins(Dev, I) ->
pp_type(Dev, const_decl_type(I)),
write(Dev, [" ", const_decl_value(I), "\n"]);
#llvm_landingpad{} ->
- write(Dev, "landingpad { i8*, i32 } personality i32 (i32, i64, i8*,i8*)*
- @__gcc_personality_v0 cleanup\n");
+ write(Dev, "landingpad { i8*, i32 } "),
+ case Ver < {3,7} of false -> ok; true ->
+ write(Dev, "personality i32 (i32, i64, i8*,i8*)* "
+ "@__gcc_personality_v0 ")
+ end,
+ write(Dev, "cleanup\n");
#llvm_asm{} ->
write(Dev, [asm_instruction(I), "\n"]);
#llvm_adj_stack{} ->
@@ -1002,13 +1014,27 @@ pp_ins(Dev, I) ->
pp_type(Dev, adj_stack_type(I)),
write(Dev, [" ", adj_stack_offset(I),")\n"]);
#llvm_branch_meta{} ->
- write(Dev, ["!", branch_meta_id(I), " = metadata !{metadata !\"branch_weights\",
- i32 ", branch_meta_true_weight(I), ", i32 ",
- branch_meta_false_weight(I), "}\n"]);
+ write(Dev, ["!", branch_meta_id(I), " = "]),
+ if Ver < {3,6} -> write(Dev, "metadata !{metadata ");
+ Ver >= {3,6} -> write(Dev, "!{ ")
+ end,
+ write(Dev, ["!\"branch_weights\", i32 ", branch_meta_true_weight(I),
+ ", i32 ", branch_meta_false_weight(I), "}\n"]);
Other ->
exit({?MODULE, pp_ins, {"Unknown LLVM instruction", Other}})
end.
+%% @doc Print the type of a dereference in an LLVM instruction using syntax
+%% parsable by the specified LLVM version.
+pp_dereference_type(Dev, Ver, Type) ->
+ case Ver >= {3,7} of
+ false -> ok;
+ true ->
+ pp_type(Dev, pointer_type(Type)),
+ write(Dev, ", ")
+ end,
+ pp_type(Dev, Type).
+
%% @doc Pretty-print a list of types
pp_type_list(_Dev, []) -> ok;
pp_type_list(Dev, [T]) ->
diff --git a/lib/hipe/llvm/hipe_llvm_main.erl b/lib/hipe/llvm/hipe_llvm_main.erl
index 3c24425828..476d6fb49c 100644
--- a/lib/hipe/llvm/hipe_llvm_main.erl
+++ b/lib/hipe/llvm/hipe_llvm_main.erl
@@ -13,7 +13,7 @@
%% chain is invoked in order to produce an object file.
rtl_to_native(MFA, RTL, Roots, Options) ->
%% Compile to LLVM and get Instruction List (along with infos)
- {LLVMCode, RelocsDict, ConstTab} =
+ {LLVMCode, RelocsDict0, ConstTab0} =
hipe_rtl_to_llvm:translate(RTL, Roots),
%% Fix function name to an acceptable LLVM identifier (needed for closures)
{_Module, Fun, Arity} = hipe_rtl_to_llvm:fix_mfa_name(MFA),
@@ -24,34 +24,33 @@ rtl_to_native(MFA, RTL, Roots, Options) ->
%% Extract information from object file
%%
ObjBin = open_object_file(ObjectFile),
- %% Read and set the ELF class
- elf_format:set_architecture_flag(ObjBin),
+ Obj = elf_format:read(ObjBin),
%% Get labels info (for switches and jump tables)
- Labels = elf_format:get_rodata_relocs(ObjBin),
- {Switches, Closures} = get_tables(ObjBin),
+ Labels = elf_format:extract_rela(Obj, ?RODATA),
+ Tables = get_tables(Obj),
%% Associate Labels with Switches and Closures with stack args
- {SwitchInfos, ExposedClosures} =
- correlate_labels(Switches ++ Closures, Labels),
+ {SwitchInfos, ExposedClosures} = correlate_labels(Tables, Labels),
%% SwitchInfos: [{"table_50", [Labels]}]
%% ExposedClosures: [{"table_closures", [Labels]}]
-
+
%% Labelmap contains the offsets of the labels in the code that are
%% used for switch's jump tables
- LabelMap = create_labelmap(MFA, SwitchInfos, RelocsDict),
+ LabelMap = create_labelmap(MFA, SwitchInfos, RelocsDict0),
+ {RelocsDict, ConstTab} = extract_constants(RelocsDict0, ConstTab0, Obj),
%% Get relocation info
- TextRelocs = elf_format:get_text_relocs(ObjBin),
+ TextRelocs = elf_format:extract_rela(Obj, ?TEXT),
%% AccRefs contains the offsets of all references to relocatable symbols in
%% the code:
AccRefs = fix_relocations(TextRelocs, RelocsDict, MFA),
%% Get stack descriptors
- SDescs = get_sdescs(ObjBin),
+ SDescs = get_sdescs(Obj),
%% FixedSDescs are the stack descriptors after correcting calls that have
%% arguments in the stack
FixedSDescs =
fix_stack_descriptors(RelocsDict, AccRefs, SDescs, ExposedClosures),
Refs = AccRefs ++ FixedSDescs,
%% Get binary code from object file
- BinCode = elf_format:extract_text(ObjBin),
+ BinCode = elf_format:extract_text(Obj),
%% Remove temp files (if needed)
ok = remove_temp_folder(Dir, Options),
%% Return the code together with information that will be used in the
@@ -78,7 +77,8 @@ compile_with_llvm(FunName, Arity, LLVMCode, Options, UseBuffer) ->
false -> []
end,
{ok, File_llvm} = file:open(Dir ++ Filename ++ ".ll", OpenOpts),
- hipe_llvm:pp_ins_list(File_llvm, LLVMCode),
+ Ver = hipe:get_llvm_version(), %% Should probably cache this
+ hipe_llvm:pp_ins_list(File_llvm, Ver, LLVMCode),
%% delayed_write can cause file:close not to do a close, hence the two calls
ok = file:close(File_llvm),
__ = file:close(File_llvm),
@@ -158,12 +158,10 @@ trans_optlev_flag(Tool, Options) ->
%%------------------------------------------------------------------------------
%% @doc Get switch table and closure table.
+-spec get_tables(elf_format:elf()) -> [elf_sym()].
get_tables(Elf) ->
- %% Search Symbol Table for an entry with name prefixed with "table_":
- Triples = elf_format:get_tab_entries(Elf),
- Switches = [T || T={"table_" ++ _, _, _} <- Triples],
- Closures = [T || T={"table_closures" ++ _, _, _} <- Switches],
- {Switches, Closures}.
+ %% Search Symbol Table for entries where name is prefixed with "table_":
+ [S || S=#elf_sym{name="table_" ++ _} <- elf_format:elf_symbols(Elf)].
%% @doc This function associates symbols who point to some table of labels with
%% the corresponding offsets of the labels in the code. These tables can
@@ -171,14 +169,12 @@ get_tables(Elf) ->
%% of blocks that contain closure calls with more than ?NR_ARG_REGS.
correlate_labels([], _L) -> {[], []};
correlate_labels(Tables, Labels) ->
- %% Sort "Tables" based on "ValueOffsets"
- OffsetSortedTb = lists:ukeysort(2, Tables),
- %% Unzip offset-sorted list of "Switches"
- {Names, _Offsets, TablesSizeList} = lists:unzip3(OffsetSortedTb),
- %% Associate switch names with labels
- L = split_list(Labels, TablesSizeList),
- %% Zip back! (to [{SwitchName, Values}])
- NamesValues = lists:zip(Names, L),
+ %% Assumes that the relocations are sorted
+ RelocTree = gb_trees:from_orddict(
+ [{Rel#elf_rel.offset, Rel#elf_rel.addend} || Rel <- Labels]),
+ %% Lookup all relocations pertaining to each symbol
+ NamesValues = [{Name, lookup_range(Value, Value+Size, RelocTree)}
+ || #elf_sym{name=Name, value=Value, size=Size} <- Tables],
case lists:keytake("table_closures", 1, NamesValues) of
false -> %% No closures in the code, no closure table
{NamesValues, []};
@@ -186,6 +182,17 @@ correlate_labels(Tables, Labels) ->
{SwitchesNV, ClosureTableNV}
end.
+%% Fetches all values with a key in [Low, Hi)
+-spec lookup_range(_::K, _::K, gb_trees:tree(K,V)) -> [_::V].
+lookup_range(Low, Hi, Tree) ->
+ lookup_range_1(Hi, gb_trees:iterator_from(Low, Tree)).
+
+lookup_range_1(Hi, Iter0) ->
+ case gb_trees:next(Iter0) of
+ {Key, Value, Iter} when Key < Hi -> [Value | lookup_range_1(Hi, Iter)];
+ _ -> []
+ end.
+
%% @doc Create a gb_tree which contains information about the labels that used
%% for switch's jump tables. The keys of the gb_tree are of the form
%% {MFA, Label} and the values are the actual Offsets.
@@ -213,40 +220,80 @@ insert_to_labelmap([{Key, Value}|Rest], LabelMap) ->
insert_to_labelmap(Rest, LabelMap)
end.
+%% Find any LLVM-generated constants and add them to the constant table
+extract_constants(RelocsDict0, ConstTab0, Obj) ->
+ TextRelocs = elf_format:extract_rela(Obj, ?TEXT),
+ AnonConstSections =
+ lists:usort([{Sec, Offset}
+ || #elf_rel{symbol=#elf_sym{type=section, section=Sec},
+ addend=Offset} <- TextRelocs]),
+ lists:foldl(
+ fun({#elf_shdr{name=Name, type=progbits, addralign=Align, entsize=EntSize,
+ size=Size} = Section, Offset}, {RelocsDict1, ConstTab1})
+ when EntSize > 0, 0 =:= Size rem EntSize, 0 =:= Offset rem EntSize ->
+ SectionBin = elf_format:section_contents(Section, Obj),
+ Constant = binary:part(SectionBin, Offset, EntSize),
+ {ConstTab, ConstLbl} =
+ hipe_consttab:insert_binary_const(ConstTab1, Align, Constant),
+ {dict:store({anon, Name, Offset}, {constant, ConstLbl}, RelocsDict1),
+ ConstTab}
+ end, {RelocsDict0, ConstTab0}, AnonConstSections).
+
%% @doc Correlate object file relocation symbols with info from translation to
%% llvm code.
fix_relocations(Relocs, RelocsDict, MFA) ->
- fix_relocs(Relocs, RelocsDict, MFA, []).
-
-fix_relocs([], _, _, RelocAcc) -> RelocAcc;
-fix_relocs([{Name, Offset}|Rs], RelocsDict, {ModName,_,_}=MFA, RelocAcc) ->
+ lists:map(fun(Reloc) -> fix_reloc(Reloc, RelocsDict, MFA) end, Relocs).
+
+%% Relocation types and expected addends for x86 and amd64
+-define(PCREL_T, 'pc32').
+-define(PCREL_A, -4). %% Hard-coded in hipe_x86.c and hipe_amd64.c
+-ifdef(BIT32).
+-define(ABS_T, '32').
+-define(ABS_A, _). %% We support any addend
+-else.
+-define(ABS_T, '64').
+-define(ABS_A, 0).
+-endif.
+
+fix_reloc(#elf_rel{symbol=#elf_sym{name=Name, section=undefined, type=notype},
+ offset=Offset, type=?PCREL_T, addend=?PCREL_A},
+ RelocsDict, {ModName,_,_}) when Name =/= "" ->
case dict:fetch(Name, RelocsDict) of
- {atom, AtomName} ->
- fix_relocs(Rs, RelocsDict, MFA,
- [{?LOAD_ATOM, Offset, AtomName}|RelocAcc]);
- {constant, Label} ->
- fix_relocs(Rs, RelocsDict, MFA,
- [{?LOAD_ADDRESS, Offset, {constant, Label}}|RelocAcc]);
- {switch, _, JTabLab} -> %% Treat switch exactly as constant
- fix_relocs(Rs, RelocsDict, MFA,
- [{?LOAD_ADDRESS, Offset, {constant, JTabLab}}|RelocAcc]);
- {closure, _}=Closure ->
- fix_relocs(Rs, RelocsDict, MFA,
- [{?LOAD_ADDRESS, Offset, Closure}|RelocAcc]);
- {call, {bif, BifName, _}} ->
- fix_relocs(Rs, RelocsDict, MFA,
- [{?CALL_LOCAL, Offset, BifName}|RelocAcc]);
+ {call, {bif, BifName, _}} -> {?CALL_LOCAL, Offset, BifName};
%% MFA calls to functions in the same module are of type 3, while all
%% other MFA calls are of type 2.
- {call, {ModName,_F,_A}=CallMFA} ->
- fix_relocs(Rs, RelocsDict, MFA,
- [{?CALL_LOCAL, Offset, CallMFA}|RelocAcc]);
- {call, CallMFA} ->
- fix_relocs(Rs, RelocsDict, MFA,
- [{?CALL_REMOTE, Offset, CallMFA}|RelocAcc]);
- Other ->
- exit({?MODULE, fix_relocs,
- {"Relocation not in relocation dictionary", Other}})
+ %% XXX: Does this code break hot code loading (by transforming external
+ %% calls into local calls?)
+ {call, {ModName,_F,_A}=CallMFA} -> {?CALL_LOCAL, Offset, CallMFA};
+ {call, CallMFA} -> {?CALL_REMOTE, Offset, CallMFA}
+ end;
+fix_reloc(#elf_rel{symbol=#elf_sym{name=Name, section=undefined, type=notype},
+ offset=Offset, type=?ABS_T, addend=?ABS_A},
+ RelocsDict, _) when Name =/= "" ->
+ case dict:fetch(Name, RelocsDict) of
+ {atom, AtomName} -> {?LOAD_ATOM, Offset, AtomName};
+ {constant, Label} -> {?LOAD_ADDRESS, Offset, {constant, Label}};
+ {closure, _}=Closure -> {?LOAD_ADDRESS, Offset, Closure}
+ end;
+fix_reloc(#elf_rel{symbol=#elf_sym{name=Name, section=#elf_shdr{name=?TEXT},
+ type=func},
+ offset=Offset, type=?PCREL_T, addend=?PCREL_A},
+ RelocsDict, MFA) when Name =/= "" ->
+ case dict:fetch(Name, RelocsDict) of
+ {call, MFA} -> {?CALL_LOCAL, Offset, MFA}
+ end;
+fix_reloc(#elf_rel{symbol=#elf_sym{name=Name, section=#elf_shdr{name=?RODATA},
+ type=object},
+ offset=Offset, type=?ABS_T, addend=?ABS_A},
+ RelocsDict, _) when Name =/= "" ->
+ case dict:fetch(Name, RelocsDict) of
+ {switch, _, JTabLab} -> %% Treat switch exactly as constant
+ {?LOAD_ADDRESS, Offset, {constant, JTabLab}}
+ end;
+fix_reloc(#elf_rel{symbol=#elf_sym{type=section, section=#elf_shdr{name=Name}},
+ offset=Offset, type=?ABS_T, addend=Addend}, RelocsDict, _) ->
+ case dict:fetch({anon, Name, Addend}, RelocsDict) of
+ {constant, Label} -> {?LOAD_ADDRESS, Offset, {constant, Label}}
end.
%%------------------------------------------------------------------------------
@@ -271,20 +318,14 @@ get_sdescs(Elf) ->
T = SPCount * ?SP_ADDR_SIZE,
%% Pattern match fields of ".note.gc":
<<SPCount:(?bits(?SP_COUNT_SIZE))/integer-little, % Sanity check!
- SPAddrs:T/binary, % NOTE: In 64bit they are relocs!
+ _SPAddrs:T/binary, % NOTE: In 64bit they are relocs!
StkFrameSize:(?bits(?SP_STKFRAME_SIZE))/integer-little,
StkArity:(?bits(?SP_STKARITY_SIZE))/integer-little,
_LiveRootCount:(?bits(?SP_LIVEROOTCNT_SIZE))/integer-little, % Skip
Roots/binary>> = NoteGC_bin,
LiveRoots = get_liveroots(Roots, []),
- %% Extract information about the safe point addresses:
- SPOffs =
- case elf_format:is64bit() of
- true -> %% Find offsets in ".rela.note.gc":
- elf_format:get_rela_addends(RelaNoteGC);
- false -> %% Find offsets in SPAddrs (in ".note.gc"):
- get_spoffs(SPAddrs, [])
- end,
+ %% Extract the safe point offsets:
+ SPOffs = [A || #elf_rel{addend=A} <- RelaNoteGC],
%% Extract Exception Handler labels:
ExnHandlers = elf_format:get_exn_handlers(Elf),
%% Combine ExnHandlers and Safe point addresses (return addresses):
@@ -300,13 +341,6 @@ get_liveroots(<<Root:?bits(?LR_STKINDEX_SIZE)/integer-little,
MoreRoots/binary>>, Acc) ->
get_liveroots(MoreRoots, [Root | Acc]).
-%% @doc Extracts a bunch of integers (safepoint offsets) from a binary. Returns
-%% a tuple as need for stack descriptors.
-get_spoffs(<<>>, Acc) ->
- lists:reverse(Acc);
-get_spoffs(<<SPOff:?bits(?SP_ADDR_SIZE)/integer-little, More/binary>>, Acc) ->
- get_spoffs(More, [SPOff | Acc]).
-
combine_ras_and_exns(_, [], Acc) ->
lists:reverse(Acc);
combine_ras_and_exns(ExnHandlers, [RA | MoreRAs], Acc) ->
@@ -489,18 +523,3 @@ unique_folder(FunName, Arity, Options) ->
dir_exists(Filename) ->
{Flag, Info} = file:read_file_info(Filename),
(Flag =:= ok) andalso (element(3, Info) =:= directory).
-
-%% @doc Function that takes as arguments a list of integers and a list with
-%% numbers indicating how many items should each tuple have and splits
-%% the original list to a list of lists of integers (with the specified
-%% number of elements), i.e. [ [...], [...] ].
--spec split_list([integer()], [integer()]) -> [ [integer()] ].
-split_list(List, ElemsPerTuple) ->
- split_list(List, ElemsPerTuple, []).
-
--spec split_list([integer()], [integer()], [ [integer()] ]) -> [ [integer()] ].
-split_list([], [], Acc) ->
- lists:reverse(Acc);
-split_list(List, [NumOfElems | MoreNums], Acc) ->
- {L1, L2} = lists:split(NumOfElems, List),
- split_list(L2, MoreNums, [ L1 | Acc]).
diff --git a/lib/hipe/llvm/hipe_rtl_to_llvm.erl b/lib/hipe/llvm/hipe_rtl_to_llvm.erl
index d7d8d1b049..b23d756d6c 100644
--- a/lib/hipe/llvm/hipe_rtl_to_llvm.erl
+++ b/lib/hipe/llvm/hipe_rtl_to_llvm.erl
@@ -266,17 +266,18 @@ trans_alub_overflow(I, Sign, Relocs) ->
T2 = mk_temp(),
%% T1{1}: Boolean variable indicating overflow
I6 = hipe_llvm:mk_extractvalue(T2, ReturnType, T1, "1", []),
- case hipe_rtl:alub_cond(I) of
- Op when Op =:= overflow orelse Op =:= ltu ->
- True_label = mk_jump_label(hipe_rtl:alub_true_label(I)),
- False_label = mk_jump_label(hipe_rtl:alub_false_label(I)),
- MetaData = branch_metadata(hipe_rtl:alub_pred(I));
- not_overflow ->
- True_label = mk_jump_label(hipe_rtl:alub_false_label(I)),
- False_label = mk_jump_label(hipe_rtl:alub_true_label(I)),
- MetaData = branch_metadata(1 - hipe_rtl:alub_pred(I))
- end,
- I7 = hipe_llvm:mk_br_cond(T2, True_label, False_label, MetaData),
+ {TrueLabel, FalseLabel, MetaData} =
+ case hipe_rtl:alub_cond(I) of
+ Op when Op =:= overflow orelse Op =:= ltu ->
+ {mk_jump_label(hipe_rtl:alub_true_label(I)),
+ mk_jump_label(hipe_rtl:alub_false_label(I)),
+ branch_metadata(hipe_rtl:alub_pred(I))};
+ not_overflow ->
+ {mk_jump_label(hipe_rtl:alub_false_label(I)),
+ mk_jump_label(hipe_rtl:alub_true_label(I)),
+ branch_metadata(1 - hipe_rtl:alub_pred(I))}
+ end,
+ I7 = hipe_llvm:mk_br_cond(T2, TrueLabel, FalseLabel, MetaData),
{[I7, I6, I5, I4, I3, I2, I1], NewRelocs}.
trans_alub_op(I, Sign) ->
diff --git a/lib/hipe/main/hipe.app.src b/lib/hipe/main/hipe.app.src
index aa86b6dc5b..f8487151d7 100644
--- a/lib/hipe/main/hipe.app.src
+++ b/lib/hipe/main/hipe.app.src
@@ -88,6 +88,7 @@
hipe_icode2rtl,
hipe_icode_bincomp,
hipe_icode_callgraph,
+ hipe_icode_call_elim,
hipe_icode_cfg,
hipe_icode_coordinator,
hipe_icode_ebb,
diff --git a/lib/hipe/main/hipe.erl b/lib/hipe/main/hipe.erl
index 0e32da1d36..981265b3e9 100644
--- a/lib/hipe/main/hipe.erl
+++ b/lib/hipe/main/hipe.erl
@@ -200,8 +200,9 @@
compile/4,
compile_core/4,
file/1,
- file/2,
- llvm_support_available/0,
+ file/2,
+ get_llvm_version/0,
+ llvm_support_available/0,
load/1,
help/0,
help_hiper/0,
@@ -1165,6 +1166,9 @@ option_text(caller_save_spill_restore) ->
"Activates caller save register spills and restores";
option_text(debug) ->
"Outputs internal debugging information during compilation";
+option_text(icode_call_elim) ->
+ "Performs call elimination of BIFs that are side-effect free\n" ++
+ "only on some argument types";
option_text(icode_range) ->
"Performs integer range analysis on the Icode level";
option_text(icode_ssa_check) ->
@@ -1318,6 +1322,7 @@ opt_keys() ->
get_called_modules,
split_arith,
split_arith_unsafe,
+ icode_call_elim,
icode_inline_bifs,
icode_ssa_check,
icode_ssa_copy_prop,
@@ -1399,7 +1404,7 @@ o1_opts(TargetArch) ->
o2_opts(TargetArch) ->
Common = [icode_ssa_const_prop, icode_ssa_copy_prop, % icode_ssa_struct_reuse,
- icode_type, icode_inline_bifs, rtl_lcm,
+ icode_type, icode_inline_bifs, icode_call_elim, rtl_lcm,
rtl_ssa, rtl_ssa_const_prop,
spillmin_color, use_indexing, remove_comments,
concurrent_comp, binary_opt | o1_opts(TargetArch)],
@@ -1429,6 +1434,7 @@ opt_negations() ->
{no_icode_inline_bifs, icode_inline_bifs},
{no_icode_range, icode_range},
{no_icode_split_arith, icode_split_arith},
+ {no_icode_call_elim, icode_call_elim},
{no_icode_ssa_check, icode_ssa_check},
{no_icode_ssa_copy_prop, icode_ssa_copy_prop},
{no_icode_ssa_const_prop, icode_ssa_const_prop},
@@ -1479,18 +1485,25 @@ opt_expansions(TargetArch) ->
[{o1, o1_opts(TargetArch)},
{o2, o2_opts(TargetArch)},
{o3, o3_opts(TargetArch)},
- {to_llvm, llvm_opts(o3)},
- {{to_llvm, o0}, llvm_opts(o0)},
- {{to_llvm, o1}, llvm_opts(o1)},
- {{to_llvm, o2}, llvm_opts(o2)},
- {{to_llvm, o3}, llvm_opts(o3)},
+ {to_llvm, llvm_opts(o3, TargetArch)},
+ {{to_llvm, o0}, llvm_opts(o0, TargetArch)},
+ {{to_llvm, o1}, llvm_opts(o1, TargetArch)},
+ {{to_llvm, o2}, llvm_opts(o2, TargetArch)},
+ {{to_llvm, o3}, llvm_opts(o3, TargetArch)},
{x87, [x87, inline_fp]},
{inline_fp, case TargetArch of %% XXX: Temporary until x86 has sse2
x86 -> [x87, inline_fp];
_ -> [inline_fp] end}].
-llvm_opts(O) ->
- [to_llvm, {llvm_opt, O}, {llvm_llc, O}].
+llvm_opts(O, TargetArch) ->
+ Base = [to_llvm, {llvm_opt, O}, {llvm_llc, O}],
+ case TargetArch of
+ %% A llvm bug present in 3.4 through (at least) 3.8 miscompiles x86
+ %% functions that have floats are spilled to stack by clobbering the process
+ %% pointer (ebp) trying to realign the stack pointer.
+ x86 -> [no_inline_fp | Base];
+ _ -> Base
+ end.
%% This expands "basic" options, which may be tested early and cannot be
%% in conflict with options found in the source code.
@@ -1520,7 +1533,8 @@ expand_options(Opts, TargetArch) ->
proplists:normalize(Opts, [{negations, opt_negations()},
{aliases, opt_aliases()},
{expand, opt_basic_expansions()},
- {expand, opt_expansions(TargetArch)}]).
+ {expand, opt_expansions(TargetArch)},
+ {negations, opt_negations()}]).
-spec check_options(comp_options()) -> 'ok'.
@@ -1538,18 +1552,27 @@ check_options(Opts) ->
-spec llvm_support_available() -> boolean().
llvm_support_available() ->
- get_llvm_version() >= 3.4.
+ get_llvm_version() >= {3,4}.
+
+-type llvm_version() :: {Major :: integer(), Minor :: integer()}.
+-spec get_llvm_version() -> llvm_version() | {0, 0}.
get_llvm_version() ->
OptStr = os:cmd("opt -version"),
SubStr = "LLVM version ", N = length(SubStr),
case string:str(OptStr, SubStr) of
0 -> % No opt available
- 0.0;
+ {0, 0};
S ->
- case string:to_float(string:sub_string(OptStr, S + N)) of
- {error, _} -> 0.0; %XXX: Assumes no revision numbers in versioning
- {Float, _} -> Float
+ case string:tokens(string:sub_string(OptStr, S + N), ".") of
+ [MajorS, MinorS | _] ->
+ case {string:to_integer(MajorS), string:to_integer(MinorS)} of
+ {{Major, ""}, {Minor, _}}
+ when is_integer(Major), is_integer(Minor) ->
+ {Major, Minor};
+ _ -> {0, 0}
+ end;
+ _ -> {0, 0} %XXX: Assumes no revision numbers in versioning
end
end.
diff --git a/lib/hipe/main/hipe_main.erl b/lib/hipe/main/hipe_main.erl
index be5050e155..b9d783d20a 100644
--- a/lib/hipe/main/hipe_main.erl
+++ b/lib/hipe/main/hipe_main.erl
@@ -284,8 +284,9 @@ icode_ssa_type(IcodeSSA, MFA, Options, Servers) ->
false -> AnnIcode1
end,
AnnIcode3 = icode_range_analysis(AnnIcode2, MFA, Options, Servers),
- pp(AnnIcode3, MFA, icode, pp_range_icode, Options, Servers),
- hipe_icode_type:unannotate_cfg(AnnIcode3)
+ AnnIcode4 = icode_eliminate_safe_calls(AnnIcode3, Options),
+ pp(AnnIcode4, MFA, icode, pp_range_icode, Options, Servers),
+ hipe_icode_type:unannotate_cfg(AnnIcode4)
end.
icode_ssa_convert(IcodeCfg, Options) ->
@@ -334,6 +335,15 @@ icode_range_analysis(IcodeSSA, MFA, Options, Servers) ->
IcodeSSA
end.
+icode_eliminate_safe_calls(IcodeSSA, Options) ->
+ case proplists:get_bool(icode_call_elim, Options) of
+ true ->
+ ?option_time(hipe_icode_call_elim:cfg(IcodeSSA),
+ "Icode SSA safe call elimination", Options);
+ false ->
+ IcodeSSA
+ end.
+
icode_ssa_dead_code_elimination(IcodeSSA, Options) ->
IcodeSSA1 = ?option_time(hipe_icode_ssa:remove_dead_code(IcodeSSA),
"Icode SSA dead code elimination pass 2",
diff --git a/lib/hipe/misc/hipe_consttab.erl b/lib/hipe/misc/hipe_consttab.erl
index f361edc79c..226b20fa46 100644
--- a/lib/hipe/misc/hipe_consttab.erl
+++ b/lib/hipe/misc/hipe_consttab.erl
@@ -87,7 +87,8 @@
% {NewTab, Lbl}
insert_sorted_block/4,
insert_block/3,
- %% insert_global_word/2,
+ insert_binary_const/3,
+ %% insert_global_word/2,
%% insert_global_block/4,
%% update_word/3, % update_word(ConstTab, Value) -> {NewTab, Lbl}
%% update_block/5,
@@ -196,6 +197,16 @@ insert_block({ConstTab, RefToLabels, NextLabel}, ElementType, InitList) ->
{ElementType,InitList}),
{insert_backrefs(NewTa, Id, ReferredLabels), Id}.
+%% @doc Inserts a binary constant literal into the const table.
+-spec insert_binary_const(hipe_consttab(), ct_alignment(), binary()) ->
+ {hipe_consttab(), hipe_constlbl()}.
+insert_binary_const(ConstTab, Alignment, Binary)
+ when (Alignment =:= 4 orelse Alignment =:= 8 orelse Alignment =:= 16
+ orelse Alignment =:= 32), is_binary(Binary),
+ size(Binary) rem Alignment =:= 0 ->
+ insert_const(ConstTab, block, Alignment, false,
+ {byte, binary_to_list(Binary)}).
+
%% @spec (ConstTab::hipe_consttab(), ElementType::element_type(),
%% InitList::block(), SortOrder) -> {hipe_consttab(), hipe_constlbl()}
diff --git a/lib/hipe/misc/hipe_consttab.hrl b/lib/hipe/misc/hipe_consttab.hrl
index d2dbbe509c..550da0455c 100644
--- a/lib/hipe/misc/hipe_consttab.hrl
+++ b/lib/hipe/misc/hipe_consttab.hrl
@@ -20,7 +20,7 @@
%%
%%-----------------------------------------------------------------------------
--type ct_alignment() :: 4 | 8.
+-type ct_alignment() :: 4 | 8 | 16 | 32.
-type hipe_constlbl() :: non_neg_integer().
-type hipe_consttab() :: {dict:dict(), [hipe_constlbl()], hipe_constlbl()}.
diff --git a/lib/hipe/test/Makefile b/lib/hipe/test/Makefile
index 09f4fd2129..544888719f 100644
--- a/lib/hipe/test/Makefile
+++ b/lib/hipe/test/Makefile
@@ -6,7 +6,8 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk
# ----------------------------------------------------
MODULES= \
- hipe_SUITE
+ hipe_SUITE \
+ opt_verify_SUITE
# .erl files for these modules are automatically generated
GEN_MODULES= \
@@ -79,4 +80,4 @@ release_tests_spec: make_emakefile
@tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -)
cd "$(RELSYSDIR)";\
erlc hipe_testsuite_driver.erl;\
- erl -noshell -run hipe_testsuite_driver create_all_suites -s erlang halt
+ erl -noshell -run hipe_testsuite_driver create_all_suites $(GEN_MODULES) -s erlang halt
diff --git a/lib/hipe/test/hipe_testsuite_driver.erl b/lib/hipe/test/hipe_testsuite_driver.erl
index 64c5c0a7c9..03ec7adfd0 100644
--- a/lib/hipe/test/hipe_testsuite_driver.erl
+++ b/lib/hipe/test/hipe_testsuite_driver.erl
@@ -1,6 +1,6 @@
-module(hipe_testsuite_driver).
--export([create_all_suites/0, run/3]).
+-export([create_all_suites/1, run/3]).
-include_lib("kernel/include/file.hrl").
@@ -16,25 +16,17 @@
outputfile :: file:io_device(),
testcases :: [testcase()]}).
--spec create_all_suites() -> 'ok'.
+-spec create_all_suites([string()]) -> 'ok'.
-create_all_suites() ->
- {ok, Cwd} = file:get_cwd(),
- Suites = get_suites(Cwd),
+create_all_suites(SuitesWithSuiteSuffix) ->
+ Suites = get_suites(SuitesWithSuiteSuffix),
lists:foreach(fun create_suite/1, Suites).
--spec get_suites(file:filename()) -> [string()].
+-spec get_suites([string()]) -> [string()].
-get_suites(Dir) ->
- case file:list_dir(Dir) of
- {error, _} -> [];
- {ok, Filenames} ->
- FullFilenames = [filename:join(Dir, F) || F <- Filenames],
- Dirs = [suffix(filename:basename(F), ?suite_data) ||
- F <- FullFilenames,
- file_type(F) =:= {ok, 'directory'}],
- [S || {yes, S} <- Dirs]
- end.
+get_suites(SuitesWithSuiteSuffix) ->
+ Prefixes = [suffix(F, ?suite_suffix) || F <- SuitesWithSuiteSuffix],
+ [S || {yes, S} <- Prefixes].
suffix(String, Suffix) ->
case string:rstr(String, Suffix) of
diff --git a/lib/hipe/test/opt_verify_SUITE.erl b/lib/hipe/test/opt_verify_SUITE.erl
new file mode 100644
index 0000000000..61952e81d7
--- /dev/null
+++ b/lib/hipe/test/opt_verify_SUITE.erl
@@ -0,0 +1,62 @@
+-module(opt_verify_SUITE).
+
+-compile([export_all]).
+
+all() ->
+ [call_elim].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ case erlang:system_info(hipe_architecture) of
+ undefined -> {skip, "HiPE not available or enabled"};
+ _ -> Config
+ end.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+call_elim_test_file(Config, FileName, Option) ->
+ PrivDir = test_server:lookup_config(priv_dir, Config),
+ TempOut = test_server:temp_name(filename:join(PrivDir, "call_elim_out")),
+ {ok, TestCase} = compile:file(FileName),
+ {ok, TestCase} = hipe:c(TestCase, [Option, {pp_range_icode, {file, TempOut}}]),
+ {ok, Icode} = file:read_file(TempOut),
+ ok = file:delete(TempOut),
+ Icode.
+
+substring_count(Icode, Substring) ->
+ substring_count(Icode, Substring, 0).
+substring_count(Icode, Substring, N) ->
+ case string:str(Icode, Substring) of
+ 0 -> N;
+ I -> substring_count(lists:nthtail(I, Icode), Substring, N+1)
+ end.
+
+call_elim() ->
+ [{doc, "Test that the call elimination optimization pass is ok"}].
+call_elim(Config) ->
+ DataDir = test_server:lookup_config(data_dir, Config),
+ F1 = filename:join(DataDir, "call_elim_test.erl"),
+ Icode1 = call_elim_test_file(Config, F1, icode_call_elim),
+ 0 = substring_count(binary:bin_to_list(Icode1), "is_key"),
+ Icode2 = call_elim_test_file(Config, F1, no_icode_call_elim),
+ true = (0 /= substring_count(binary:bin_to_list(Icode2), "is_key")),
+ F2 = filename:join(DataDir, "call_elim_test_branches_no_opt_poss.erl"),
+ Icode3 = call_elim_test_file(Config, F2, icode_call_elim),
+ 3 = substring_count(binary:bin_to_list(Icode3), "is_key"),
+ Icode4 = call_elim_test_file(Config, F2, no_icode_call_elim),
+ 3 = substring_count(binary:bin_to_list(Icode4), "is_key"),
+ F3 = filename:join(DataDir, "call_elim_test_branches_opt_poss.erl"),
+ Icode5 = call_elim_test_file(Config, F3, icode_call_elim),
+ 0 = substring_count(binary:bin_to_list(Icode5), "is_key"),
+ Icode6 = call_elim_test_file(Config, F3, no_icode_call_elim),
+ 3 = substring_count(binary:bin_to_list(Icode6), "is_key"),
+ ok.
diff --git a/lib/hipe/test/opt_verify_SUITE_data/call_elim_test.erl b/lib/hipe/test/opt_verify_SUITE_data/call_elim_test.erl
new file mode 100644
index 0000000000..8b725f8ffe
--- /dev/null
+++ b/lib/hipe/test/opt_verify_SUITE_data/call_elim_test.erl
@@ -0,0 +1,12 @@
+-module(call_elim_test).
+
+-export([test/0]).
+
+test() ->
+ true = has_1_field(#{1=>true}),
+ true = has_1_field(#{1=>"hej", b=>2}),
+ true = has_1_field(#{b=>3, 1=>4}),
+ ok.
+
+has_1_field(#{1:=_}) -> true;
+has_1_field(#{}) -> false.
diff --git a/lib/hipe/test/opt_verify_SUITE_data/call_elim_test_branches_no_opt_poss.erl b/lib/hipe/test/opt_verify_SUITE_data/call_elim_test_branches_no_opt_poss.erl
new file mode 100644
index 0000000000..7ffae86797
--- /dev/null
+++ b/lib/hipe/test/opt_verify_SUITE_data/call_elim_test_branches_no_opt_poss.erl
@@ -0,0 +1,32 @@
+-module(call_elim_test_branches_no_opt_poss).
+
+-export([test/1]).
+
+test(A) ->
+ if A > 0 ->
+ false = has_a_field(#{b=>true}),
+ true = has_a_field(#{b=>1, a=>"2"}),
+ false = has_a_field(#{b=>5, c=>4}),
+ false = has_tuple_field(#{{ab, 2}=><<"qq">>, 1 =>0}),
+ false = has_tuple_field(#{up =>down, {ab, 2}=>[]}),
+ false = has_tuple_field(#{{ab, 2}=>42});
+ A =< 0 ->
+ true = has_a_field(#{a=>q, 'A' =>nej}),
+ true = has_a_field(#{a=>"hej", false=>true}),
+ true = has_a_field(#{a=>3}),
+ true = has_tuple_field(#{{ab, 1}=>q, 'A' =>nej}),
+ true = has_tuple_field(#{{ab, 1}=>"hej", false=>true}),
+ true = has_tuple_field(#{{ab, 1}=>3})
+ end,
+ true = has_nil_field(#{[] =>3, b=>"seven"}),
+ true = has_nil_field(#{"seventeen"=>17}),
+ ok.
+
+has_tuple_field(#{{ab, 1}:=_}) -> true;
+has_tuple_field(#{}) -> false.
+
+has_a_field(#{a:=_}) -> true;
+has_a_field(#{}) -> false.
+
+has_nil_field(#{[]:=_}) -> true;
+has_nil_field(#{}) -> false.
diff --git a/lib/hipe/test/opt_verify_SUITE_data/call_elim_test_branches_opt_poss.erl b/lib/hipe/test/opt_verify_SUITE_data/call_elim_test_branches_opt_poss.erl
new file mode 100644
index 0000000000..c8ddfa1e75
--- /dev/null
+++ b/lib/hipe/test/opt_verify_SUITE_data/call_elim_test_branches_opt_poss.erl
@@ -0,0 +1,32 @@
+-module(call_elim_test_branches_opt_poss).
+
+-export([test/1]).
+
+test(A) ->
+ if A > 0 ->
+ true = has_a_field(#{a=>true}),
+ true = has_a_field(#{b=>1, a=>"2"}),
+ true = has_a_field(#{a=>5, c=>4}),
+ true = has_tuple_field(#{{ab, 1}=><<"qq">>, 1 =>0}),
+ true = has_tuple_field(#{up =>down, {ab, 1}=>[]}),
+ true = has_tuple_field(#{{ab, 1}=>42});
+ A =< 0 ->
+ true = has_a_field(#{a=>q, 'A' =>nej}),
+ true = has_a_field(#{a=>"hej", false=>true}),
+ true = has_a_field(#{a=>3}),
+ true = has_tuple_field(#{{ab, 1}=>q, 'A' =>nej}),
+ true = has_tuple_field(#{{ab, 1}=>"hej", false=>true}),
+ true = has_tuple_field(#{{ab, 1}=>3})
+ end,
+ true = has_nil_field(#{[] =>3, b =>"seven"}),
+ true = has_nil_field(#{"seventeen"=>17, []=>nil}),
+ ok.
+
+has_tuple_field(#{{ab, 1}:=_}) -> true;
+has_tuple_field(#{}) -> false.
+
+has_a_field(#{a:=_}) -> true;
+has_a_field(#{}) -> false.
+
+has_nil_field(#{[]:=_}) -> true;
+has_nil_field(#{}) -> false.
diff --git a/lib/inets/test/Makefile b/lib/inets/test/Makefile
index 65983c528d..ffc512050a 100644
--- a/lib/inets/test/Makefile
+++ b/lib/inets/test/Makefile
@@ -151,7 +151,6 @@ MODULES = \
inets_test_lib \
erl_make_certs \
ftp_SUITE \
- ftp_suite_lib \
ftp_format_SUITE \
http_format_SUITE \
httpc_SUITE \
@@ -169,8 +168,6 @@ MODULES = \
httpd_test_lib \
inets_sup_SUITE \
inets_SUITE \
- inets_app_test \
- inets_appup_test \
tftp_test_lib \
tftp_SUITE \
uri_SUITE \
diff --git a/lib/inets/test/ftp_SUITE.erl b/lib/inets/test/ftp_SUITE.erl
index cc40377ebf..08295d4e3c 100644
--- a/lib/inets/test/ftp_SUITE.erl
+++ b/lib/inets/test/ftp_SUITE.erl
@@ -42,8 +42,10 @@
-define(BAD_USER, "baduser").
-define(BAD_DIR, "baddirectory").
-go() -> ct:run_test([{suite,"ftp_SUITE"}, {logdir,"LOG"}]).
-gos() -> ct:run_test([{suite,"ftp_SUITE"}, {group,ftps_passive}, {logdir,"LOG"}]).
+-record(progress, {
+ current = 0,
+ total
+ }).
%%--------------------------------------------------------------------
%% Common Test interface functions -----------------------------------
@@ -90,7 +92,14 @@ ftp_tests()->
recv_chunk,
type,
quote,
- ip_v6_disabled
+ ip_v6_disabled,
+ progress_report_send,
+ progress_report_recv,
+ not_owner,
+ unexpected_call,
+ unexpected_cast,
+ unexpected_bang,
+ clean_shutdown
].
%%--------------------------------------------------------------------
@@ -117,9 +126,9 @@ ftp_tests()->
-define(default_ftp_servers,
[{"vsftpd",
fun(__CONF__) ->
- DataDir = ?config(data_dir,__CONF__),
+ DataDir = proplists:get_value(data_dir,__CONF__),
ConfFile = filename:join(DataDir, "vsftpd.conf"),
- PrivDir = ?config(priv_dir,__CONF__),
+ PrivDir = proplists:get_value(priv_dir,__CONF__),
AnonRoot = PrivDir,
Cmd = ["vsftpd "++filename:join(DataDir,"vsftpd.conf"),
" -oftpd_banner=erlang_otp_testing",
@@ -145,7 +154,7 @@ ftp_tests()->
fun(_StartResult) -> os:cmd("kill `ps ax | grep erlang_otp_testing | awk '/vsftpd/{print $1}'`")
end,
fun(__CONF__) ->
- AnonRoot = ?config(priv_dir,__CONF__),
+ AnonRoot = proplists:get_value(priv_dir,__CONF__),
[{id2ftp, fun(Id) -> filename:join(AnonRoot,Id) end},
{id2ftp_result,fun(Id) -> filename:join(AnonRoot,Id) end} | __CONF__]
end,
@@ -161,9 +170,9 @@ init_per_suite(Config) ->
false ->
{skip, "No ftp server found"};
{ok,Data} ->
- TstDir = filename:join(?config(priv_dir,Config), "test"),
+ TstDir = filename:join(proplists:get_value(priv_dir,Config), "test"),
file:make_dir(TstDir),
- make_cert_files(dsa, rsa, "server-", ?config(data_dir,Config)),
+ make_cert_files(dsa, rsa, "server-", proplists:get_value(data_dir,Config)),
start_ftpd([{test_dir,TstDir},
{ftpd_data,Data}
| Config])
@@ -181,8 +190,16 @@ init_per_group(_Group, Config) -> Config.
end_per_group(_Group, Config) -> Config.
%%--------------------------------------------------------------------
-init_per_testcase(Case, Config0) ->
- Group = proplists:get_value(name,?config(tc_group_properties,Config0)),
+
+init_per_testcase(Case, Config) when (Case =:= progress_report_send) orelse
+ (Case =:= progress_report_recv) ->
+ common_init_per_testcase(Case, [{progress, {?MODULE, progress, #progress{}}} | Config]);
+
+init_per_testcase(Case, Config) ->
+ common_init_per_testcase(Case, Config).
+
+common_init_per_testcase(Case, Config0) ->
+ Group = proplists:get_value(name,proplists:get_value(tc_group_properties,Config0)),
try ?MODULE:Case(doc) of
Msg -> ct:comment(Msg)
catch
@@ -203,9 +220,9 @@ init_per_testcase(Case, Config0) ->
user -> Config;
bad_user -> Config;
_ ->
- Pid = ?config(ftp,Config),
+ Pid = proplists:get_value(ftp,Config),
ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS(atom_to_list(Group)++"-"++atom_to_list(Case)) ),
- ok = ftp:cd(Pid, ?config(priv_dir,Config)),
+ ok = ftp:cd(Pid, proplists:get_value(priv_dir,Config)),
Config
end.
@@ -213,10 +230,10 @@ init_per_testcase(Case, Config0) ->
end_per_testcase(user, _Config) -> ok;
end_per_testcase(bad_user, _Config) -> ok;
end_per_testcase(_Case, Config) ->
- case ?config(tc_status,Config) of
+ case proplists:get_value(tc_status,Config) of
ok -> ok;
_ ->
- try ftp:latest_ctrl_response(?config(ftp,Config))
+ try ftp:latest_ctrl_response(proplists:get_value(ftp,Config))
of
{ok,S} -> ct:log("***~n*** Latest ctrl channel response:~n*** ~p~n***",[S])
catch
@@ -228,38 +245,43 @@ end_per_testcase(_Case, Config) ->
%%--------------------------------------------------------------------
%% Test Cases --------------------------------------------------------
%%--------------------------------------------------------------------
-user(doc) -> ["Open an ftp connection to a host, and logon as anonymous ftp, then logoff"];
+user() -> [
+ {doc, "Open an ftp connection to a host, and logon as anonymous ftp,"
+ " then logoff"}].
user(Config) ->
- Pid = ?config(ftp, Config),
+ Pid = proplists:get_value(ftp, Config),
ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS("")),% logon
ok = ftp:close(Pid), % logoff
{error,eclosed} = ftp:pwd(Pid), % check logoff result
ok.
%%-------------------------------------------------------------------------
-bad_user(doc) -> ["Open an ftp connection to a host, and logon with bad user."];
+bad_user() ->
+ [{doc, "Open an ftp connection to a host, and logon with bad user."}].
bad_user(Config) ->
- Pid = ?config(ftp, Config),
+ Pid = proplists:get_value(ftp, Config),
{error, euser} = ftp:user(Pid, ?BAD_USER, ?FTP_PASS("")),
ok.
%%-------------------------------------------------------------------------
-pwd(doc) -> ["Test ftp:pwd/1 & ftp:lpwd/1"];
+pwd() ->
+ [{doc, "Test ftp:pwd/1 & ftp:lpwd/1"}].
pwd(Config0) ->
Config = set_state([reset], Config0),
- Pid = ?config(ftp, Config),
+ Pid = proplists:get_value(ftp, Config),
{ok, PWD} = ftp:pwd(Pid),
{ok, PathLpwd} = ftp:lpwd(Pid),
PWD = id2ftp_result("", Config),
PathLpwd = id2ftp_result("", Config).
%%-------------------------------------------------------------------------
-cd(doc) -> ["Open an ftp connection, log on as anonymous ftp, and cd to a"
- "directory and to a non-existent directory."];
+cd() ->
+ ["Open an ftp connection, log on as anonymous ftp, and cd to a"
+ "directory and to a non-existent directory."].
cd(Config0) ->
Dir = "test",
Config = set_state([reset,{mkdir,Dir}], Config0),
- Pid = ?config(ftp, Config),
+ Pid = proplists:get_value(ftp, Config),
ok = ftp:cd(Pid, id2ftp(Dir,Config)),
{ok, PWD} = ftp:pwd(Pid),
ExpectedPWD = id2ftp_result(Dir, Config),
@@ -267,12 +289,12 @@ cd(Config0) ->
{error, epath} = ftp:cd(Pid, ?BAD_DIR).
%%-------------------------------------------------------------------------
-lcd(doc) ->
- ["Test api function ftp:lcd/2"];
+lcd() ->
+ [{doc, "Test api function ftp:lcd/2"}].
lcd(Config0) ->
Dir = "test",
Config = set_state([reset,{mkdir,Dir}], Config0),
- Pid = ?config(ftp, Config),
+ Pid = proplists:get_value(ftp, Config),
ok = ftp:lcd(Pid, id2ftp(Dir,Config)),
{ok, PWD} = ftp:lpwd(Pid),
ExpectedPWD = id2ftp_result(Dir, Config),
@@ -280,19 +302,20 @@ lcd(Config0) ->
{error, epath} = ftp:lcd(Pid, ?BAD_DIR).
%%-------------------------------------------------------------------------
-ls(doc) -> ["Open an ftp connection; ls the current directory, and the "
- "\"test\" directory. We assume that ls never fails, since "
- "it's output is meant to be read by humans. "];
+ls() ->
+ [{doc, "Open an ftp connection; ls the current directory, and the "
+ "\"test\" directory. We assume that ls never fails, since "
+ "it's output is meant to be read by humans. "}].
ls(Config0) ->
Config = set_state([reset,{mkdir,"test"}], Config0),
- Pid = ?config(ftp, Config),
+ Pid = proplists:get_value(ftp, Config),
{ok, _R1} = ftp:ls(Pid),
{ok, _R2} = ftp:ls(Pid, id2ftp("test",Config)),
%% neither nlist nor ls operates on a directory
%% they operate on a pathname, which *can* be a
%% directory, but can also be a filename or a group
%% of files (including wildcards).
- case ?config(wildcard_support, Config) of
+ case proplists:get_value(wildcard_support, Config) of
true ->
{ok, _R3} = ftp:ls(Pid, id2ftp("te*",Config));
_ ->
@@ -300,20 +323,21 @@ ls(Config0) ->
end.
%%-------------------------------------------------------------------------
-nlist(doc) -> ["Open an ftp connection; nlist the current directory, and the "
+nlist() ->
+ [{doc,"Open an ftp connection; nlist the current directory, and the "
"\"test\" directory. Nlist does not behave consistenly over "
"operating systems. On some it is an error to have an empty "
- "directory."];
+ "directory."}].
nlist(Config0) ->
Config = set_state([reset,{mkdir,"test"}], Config0),
- Pid = ?config(ftp, Config),
+ Pid = proplists:get_value(ftp, Config),
{ok, _R1} = ftp:nlist(Pid),
{ok, _R2} = ftp:nlist(Pid, id2ftp("test",Config)),
%% neither nlist nor ls operates on a directory
%% they operate on a pathname, which *can* be a
%% directory, but can also be a filename or a group
%% of files (including wildcards).
- case ?config(wildcard_support, Config) of
+ case proplists:get_value(wildcard_support, Config) of
true ->
{ok, _R3} = ftp:nlist(Pid, id2ftp("te*",Config));
_ ->
@@ -321,13 +345,14 @@ nlist(Config0) ->
end.
%%-------------------------------------------------------------------------
-rename(doc) -> ["Rename a file."];
+rename() ->
+ [{doc, "Rename a file."}].
rename(Config0) ->
Contents = <<"ftp_SUITE test ...">>,
OldFile = "old.txt",
NewFile = "new.txt",
Config = set_state([reset,{mkfile,OldFile,Contents}], Config0),
- Pid = ?config(ftp, Config),
+ Pid = proplists:get_value(ftp, Config),
ok = ftp:rename(Pid,
id2ftp(OldFile,Config),
@@ -338,13 +363,14 @@ rename(Config0) ->
%%-------------------------------------------------------------------------
-send(doc) -> ["Transfer a file with ftp using send/2."];
+send() ->
+ [{doc, "Transfer a file with ftp using send/2."}].
send(Config0) ->
Contents = <<"ftp_SUITE test ...">>,
SrcDir = "data",
File = "file.txt",
Config = set_state([reset,{mkfile,[SrcDir,File],Contents}], Config0),
- Pid = ?config(ftp, Config),
+ Pid = proplists:get_value(ftp, Config),
chk_no_file([File],Config),
chk_file([SrcDir,File],Contents,Config),
@@ -356,14 +382,15 @@ chk_file([SrcDir,File],Contents,Config),
chk_file(File, Contents, Config).
%%-------------------------------------------------------------------------
-send_3(doc) -> ["Transfer a file with ftp using send/3."];
+send_3() ->
+ [{doc, "Transfer a file with ftp using send/3."}].
send_3(Config0) ->
Contents = <<"ftp_SUITE test ...">>,
Dir = "incoming",
File = "file.txt",
RemoteFile = "remfile.txt",
Config = set_state([reset,{mkfile,File,Contents},{mkdir,Dir}], Config0),
- Pid = ?config(ftp, Config),
+ Pid = proplists:get_value(ftp, Config),
ok = ftp:cd(Pid, id2ftp(Dir,Config)),
ok = ftp:lcd(Pid, id2ftp("",Config)),
@@ -372,23 +399,25 @@ send_3(Config0) ->
chk_file([Dir,RemoteFile], Contents, Config).
%%-------------------------------------------------------------------------
-send_bin(doc) -> ["Send a binary."];
+send_bin() ->
+ [{doc, "Send a binary."}].
send_bin(Config0) ->
BinContents = <<"ftp_SUITE test ...">>,
File = "file.txt",
Config = set_state([reset], Config0),
- Pid = ?config(ftp, Config),
+ Pid = proplists:get_value(ftp, Config),
{error, enotbinary} = ftp:send_bin(Pid, "some string", id2ftp(File,Config)),
ok = ftp:send_bin(Pid, BinContents, id2ftp(File,Config)),
chk_file(File, BinContents, Config).
%%-------------------------------------------------------------------------
-send_chunk(doc) -> ["Send a binary using chunks."];
+send_chunk() ->
+ [{doc, "Send a binary using chunks."}].
send_chunk(Config0) ->
Contents = <<"ftp_SUITE test ...">>,
File = "file.txt",
Config = set_state([reset,{mkdir,"incoming"}], Config0),
- Pid = ?config(ftp, Config),
+ Pid = proplists:get_value(ftp, Config),
ok = ftp:send_chunk_start(Pid, id2ftp(File,Config)),
{error, echunk} = ftp:cd(Pid, "incoming"),
@@ -399,63 +428,69 @@ send_chunk(Config0) ->
chk_file(File, <<Contents/binary,Contents/binary>>, Config).
%%-------------------------------------------------------------------------
-delete(doc) -> ["Delete a file."];
+delete() ->
+ [{doc, "Delete a file."}].
delete(Config0) ->
Contents = <<"ftp_SUITE test ...">>,
File = "file.txt",
Config = set_state([reset,{mkfile,File,Contents}], Config0),
- Pid = ?config(ftp, Config),
+ Pid = proplists:get_value(ftp, Config),
ok = ftp:delete(Pid, id2ftp(File,Config)),
chk_no_file([File], Config).
%%-------------------------------------------------------------------------
-mkdir(doc) -> ["Make a remote directory."];
+mkdir() ->
+ [{doc, "Make a remote directory."}].
mkdir(Config0) ->
NewDir = "new_dir",
Config = set_state([reset], Config0),
- Pid = ?config(ftp, Config),
+ Pid = proplists:get_value(ftp, Config),
ok = ftp:mkdir(Pid, id2ftp(NewDir,Config)),
chk_dir([NewDir], Config).
%%-------------------------------------------------------------------------
-rmdir(doc) -> ["Remove a directory."];
+rmdir() ->
+ [{doc, "Remove a directory."}].
rmdir(Config0) ->
Dir = "dir",
Config = set_state([reset,{mkdir,Dir}], Config0),
- Pid = ?config(ftp, Config),
+ Pid = proplists:get_value(ftp, Config),
ok = ftp:rmdir(Pid, id2ftp(Dir,Config)),
chk_no_dir([Dir], Config).
%%-------------------------------------------------------------------------
-append(doc) -> ["Append a local file twice to a remote file"];
+append() ->
+ [{doc, "Append a local file twice to a remote file"}].
append(Config0) ->
SrcFile = "f_src.txt",
DstFile = "f_dst.txt",
Contents = <<"ftp_SUITE test ...">>,
Config = set_state([reset,{mkfile,SrcFile,Contents}], Config0),
- Pid = ?config(ftp, Config),
+ Pid = proplists:get_value(ftp, Config),
ok = ftp:append(Pid, id2ftp(SrcFile,Config), id2ftp(DstFile,Config)),
ok = ftp:append(Pid, id2ftp(SrcFile,Config), id2ftp(DstFile,Config)),
chk_file(DstFile, <<Contents/binary,Contents/binary>>, Config).
%%-------------------------------------------------------------------------
-append_bin(doc) -> ["Append a local file twice to a remote file using append_bin"];
+append_bin() ->
+ [{doc, "Append a local file twice to a remote file using append_bin"}].
append_bin(Config0) ->
DstFile = "f_dst.txt",
Contents = <<"ftp_SUITE test ...">>,
Config = set_state([reset], Config0),
- Pid = ?config(ftp, Config),
+ Pid = proplists:get_value(ftp, Config),
ok = ftp:append_bin(Pid, Contents, id2ftp(DstFile,Config)),
ok = ftp:append_bin(Pid, Contents, id2ftp(DstFile,Config)),
chk_file(DstFile, <<Contents/binary,Contents/binary>>, Config).
%%-------------------------------------------------------------------------
-append_chunk(doc) -> ["Append chunks."];
+append_chunk() ->
+ [{doc, "Append chunks."}].
append_chunk(Config0) ->
File = "f_dst.txt",
Contents = [<<"ER">>,<<"LE">>,<<"RL">>],
Config = set_state([reset], Config0),
- Pid = ?config(ftp, Config),
+ Pid = proplists:get_value(ftp, Config),
ok = ftp:append_chunk_start(Pid, id2ftp(File,Config)),
{error, enotbinary} = ftp:append_chunk(Pid, binary_to_list(lists:nth(1,Contents))),
ok = ftp:append_chunk(Pid,lists:nth(1,Contents)),
@@ -465,53 +500,58 @@ append_chunk(Config0) ->
chk_file(File, <<"ERLERL">>, Config).
%%-------------------------------------------------------------------------
-recv(doc) -> ["Receive a file using recv/2"];
+recv() ->
+ [{doc, "Receive a file using recv/2"}].
recv(Config0) ->
File = "f_dst.txt",
SrcDir = "a_dir",
Contents = <<"ftp_SUITE test ...">>,
Config = set_state([reset, {mkfile,[SrcDir,File],Contents}], Config0),
- Pid = ?config(ftp, Config),
+ Pid = proplists:get_value(ftp, Config),
ok = ftp:cd(Pid, id2ftp(SrcDir,Config)),
ok = ftp:lcd(Pid, id2ftp("",Config)),
ok = ftp:recv(Pid, File),
chk_file(File, Contents, Config).
%%-------------------------------------------------------------------------
-recv_3(doc) -> ["Receive a file using recv/3"];
+recv_3() ->
+ [{doc,"Receive a file using recv/3"}].
recv_3(Config0) ->
DstFile = "f_src.txt",
SrcFile = "f_dst.txt",
Contents = <<"ftp_SUITE test ...">>,
Config = set_state([reset, {mkfile,SrcFile,Contents}], Config0),
- Pid = ?config(ftp, Config),
+ Pid = proplists:get_value(ftp, Config),
ok = ftp:cd(Pid, id2ftp("",Config)),
ok = ftp:recv(Pid, SrcFile, id2abs(DstFile,Config)),
chk_file(DstFile, Contents, Config).
%%-------------------------------------------------------------------------
-recv_bin(doc) -> ["Receive a file as a binary."];
+recv_bin() ->
+ [{doc, "Receive a file as a binary."}].
recv_bin(Config0) ->
File = "f_dst.txt",
Contents = <<"ftp_SUITE test ...">>,
Config = set_state([reset, {mkfile,File,Contents}], Config0),
- Pid = ?config(ftp, Config),
+ Pid = proplists:get_value(ftp, Config),
{ok,Received} = ftp:recv_bin(Pid, id2ftp(File,Config)),
find_diff(Received, Contents).
%%-------------------------------------------------------------------------
-recv_chunk(doc) -> ["Receive a file using chunk-wise."];
+recv_chunk() ->
+ [{doc, "Receive a file using chunk-wise."}].
recv_chunk(Config0) ->
File = "big_file.txt",
Contents = list_to_binary( lists:duplicate(1000, lists:seq(0,255)) ),
Config = set_state([reset, {mkfile,File,Contents}], Config0),
- Pid = ?config(ftp, Config),
+ Pid = proplists:get_value(ftp, Config),
{{error, "ftp:recv_chunk_start/2 not called"},_} = recv_chunk(Pid, <<>>),
ok = ftp:recv_chunk_start(Pid, id2ftp(File,Config)),
{ok, ReceivedContents, _Ncunks} = recv_chunk(Pid, <<>>),
find_diff(ReceivedContents, Contents).
-recv_chunk(Pid, Acc) -> recv_chunk(Pid, Acc, 0).
+recv_chunk(Pid, Acc) ->
+ recv_chunk(Pid, Acc, 0).
recv_chunk(Pid, Acc, N) ->
case ftp:recv_chunk(Pid) of
@@ -521,18 +561,18 @@ recv_chunk(Pid, Acc, N) ->
end.
%%-------------------------------------------------------------------------
-type(doc) -> ["Test that we can change btween ASCCI and binary transfer mode"];
+type() ->
+ [{doc,"Test that we can change btween ASCCI and binary transfer mode"}].
type(Config) ->
- Pid = ?config(ftp, Config),
+ Pid = proplists:get_value(ftp, Config),
ok = ftp:type(Pid, ascii),
ok = ftp:type(Pid, binary),
ok = ftp:type(Pid, ascii),
{error, etype} = ftp:type(Pid, foobar).
%%-------------------------------------------------------------------------
-quote(doc) -> [""];
quote(Config) ->
- Pid = ?config(ftp, Config),
+ Pid = proplists:get_value(ftp, Config),
["257 \""++_Rest] = ftp:quote(Pid, "pwd"), %% 257
[_| _] = ftp:quote(Pid, "help"),
%% This negativ test causes some ftp servers to hang. This test
@@ -541,48 +581,6 @@ quote(Config) ->
%% = ftp:quote(Pid, "list"),
ok.
-
-%%-------------------------------------------------------------------------
-ip_v6_disabled(doc) -> ["Test ipv4 command PORT"];
-ip_v6_disabled(_Config) ->
- %%% FIXME!!!! What is this???
- ok.%% send(Config).
-
-%%-------------------------------------------------------------------------
-%% big_one(doc) ->
-%% ["Create a local file and transfer it to the remote host into the "
-%% "the \"incoming\" directory, remove "
-%% "the local file. Then open a new connection; cd to \"incoming\", "
-%% "lcd to the private directory; receive the file; delete the "
-%% "remote file; close connection; check that received file is in "
-%% "the correct directory; cleanup." ];
-%% big_one(Config) ->
-%% Pid = ?config(ftp, Config),
-%% do_recv(Pid, Config).
-
-%% do_recv(Pid, Config) ->
-%% PrivDir = ?config(priv_dir, Config),
-%% File = ?config(file, Config),
-%% Newfile = ?config(new_file, Config),
-%% AbsFile = filename:absname(File, PrivDir),
-%% Contents = "ftp_SUITE:recv test ...",
-%% ok = file:write_file(AbsFile, list_to_binary(Contents)),
-%% ok = ftp:cd(Pid, "incoming"),
-%% ftp:delete(Pid, File), % reset
-%% ftp:lcd(Pid, PrivDir),
-%% ok = ftp:send(Pid, File),
-%% ok = file:delete(AbsFile), % cleanup
-%% test_server:sleep(100),
-%% ok = ftp:lcd(Pid, PrivDir),
-%% ok = ftp:recv(Pid, File),
-%% {ok, Files} = file:list_dir(PrivDir),
-%% true = lists:member(File, Files),
-%% ok = file:delete(AbsFile), % cleanup
-%% ok = ftp:recv(Pid, File, Newfile),
-%% ok = ftp:delete(Pid, File), % cleanup
-%% ok.
-
-
%%--------------------------------------------------------------------
%% Internal functions -----------------------------------------------
%%--------------------------------------------------------------------
@@ -676,13 +674,114 @@ chk_no_dir(PathList, Config) ->
ct:fail("Unexpected error for ~p: ~p",[Path,Error])
end.
+%%-------------------------------------------------------------------------
+progress_report_send() ->
+ [{doc, "Test the option progress for ftp:send/[2,3]"}].
+progress_report_send(Config) when is_list(Config) ->
+ ReportPid =
+ spawn_link(?MODULE, progress_report_receiver_init, [self(), 1]),
+ send(Config),
+ receive
+ {ReportPid, ok} ->
+ ok
+ end.
+%%-------------------------------------------------------------------------
+progress_report_recv() ->
+ [{doc, "Test the option progress for ftp:recv/[2,3]"}].
+progress_report_recv(Config) when is_list(Config) ->
+ ReportPid =
+ spawn_link(?MODULE, progress_report_receiver_init, [self(), 3]),
+ recv(Config),
+ receive
+ {ReportPid, ok} ->
+ ok
+ end.
+
+%%-------------------------------------------------------------------------
+
+not_owner() ->
+ [{doc, "Test what happens if a process that not owns the connection tries "
+ "to use it"}].
+not_owner(Config) when is_list(Config) ->
+ Pid = proplists:get_value(ftp, Config),
+ OtherPid = spawn_link(?MODULE, not_owner, [Pid, self()]),
+
+ receive
+ {OtherPid, ok} ->
+ {ok, _} = ftp:pwd(Pid)
+ end.
+
+
+%%-------------------------------------------------------------------------
+
+
+unexpected_call()->
+ [{doc, "Test that behaviour of the ftp process if the api is abused"}].
+unexpected_call(Config) when is_list(Config) ->
+ Flag = process_flag(trap_exit, true),
+ Pid = proplists:get_value(ftp, Config),
+
+ %% Serious programming fault, connetion will be shut down
+ case (catch gen_server:call(Pid, {self(), foobar, 10}, infinity)) of
+ {error, {connection_terminated, 'API_violation'}} ->
+ ok;
+ Unexpected1 ->
+ exit({unexpected_result, Unexpected1})
+ end,
+ ct:sleep(500),
+ undefined = process_info(Pid, status),
+ process_flag(trap_exit, Flag).
+%%-------------------------------------------------------------------------
+
+unexpected_cast()->
+ [{doc, "Test that behaviour of the ftp process if the api is abused"}].
+unexpected_cast(Config) when is_list(Config) ->
+ Flag = process_flag(trap_exit, true),
+ Pid = proplists:get_value(ftp, Config),
+ %% Serious programming fault, connetion will be shut down
+ gen_server:cast(Pid, {self(), foobar, 10}),
+ ct:sleep(500),
+ undefined = process_info(Pid, status),
+ process_flag(trap_exit, Flag).
+%%-------------------------------------------------------------------------
+
+unexpected_bang()->
+ [{doc, "Test that connection ignores unexpected bang"}].
+unexpected_bang(Config) when is_list(Config) ->
+ Flag = process_flag(trap_exit, true),
+ Pid = proplists:get_value(ftp, Config),
+ %% Could be an innocent misstake the connection lives.
+ Pid ! foobar,
+ ct:sleep(500),
+ {status, _} = process_info(Pid, status),
+ process_flag(trap_exit, Flag).
+
+%%-------------------------------------------------------------------------
+
+clean_shutdown() ->
+ [{doc, "Test that owning process that exits with reason "
+ "'shutdown' does not cause an error message. OTP 6035"}].
+
+clean_shutdown(Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ LogFile = filename:join([PrivDir,"ticket_6035.log"]),
+ Host = proplists:get_value(ftpd_host,Config),
+ try
+ Pid = spawn(?MODULE, open_wait_6035, [Host, self()]),
+ error_logger:logfile({open, LogFile}),
+ true = kill_ftp_proc_6035(Pid, LogFile),
+ error_logger:logfile(close)
+ catch
+ throw:{error, not_found} ->
+ {skip, "No available FTP servers"}
+ end.
%%--------------------------------------------------------------------
+%% Internal functions
%%--------------------------------------------------------------------
-%% find a suitable ftpd
-%%
+
find_executable(Config) ->
- FTPservers = case ?config(ftpservers,Config) of
+ FTPservers = case proplists:get_value(ftpservers,Config) of
undefined -> ?default_ftp_servers;
L -> L
end,
@@ -694,11 +793,9 @@ find_executable(Config) ->
not_available({Name,_StartCmd,_ChkUp,_StopCommand,_ConfigUpd,_Host,_Port}) ->
os:find_executable(Name) == false.
-%%--------------------------------------------------------------------
-%% start/stop of ftpd
-%%
+
start_ftpd(Config) ->
- {Name,StartCmd,_ChkUp,_StopCommand,ConfigRewrite,Host,Port} = ?config(ftpd_data, Config),
+ {Name,StartCmd,_ChkUp,_StopCommand,ConfigRewrite,Host,Port} = proplists:get_value(ftpd_data, Config),
case StartCmd(Config) of
{ok,StartResult} ->
[{ftpd_host,Host},
@@ -709,38 +806,31 @@ start_ftpd(Config) ->
end.
stop_ftpd(Config) ->
- {_Name,_StartCmd,_ChkUp,StopCommand,_ConfigUpd,_Host,_Port} = ?config(ftpd_data, Config),
- StopCommand(?config(ftpd_start_result,Config)).
+ {_Name,_StartCmd,_ChkUp,StopCommand,_ConfigUpd,_Host,_Port} = proplists:get_value(ftpd_data, Config),
+ StopCommand(proplists:get_value(ftpd_start_result,Config)).
ps_ftpd(Config) ->
- {_Name,_StartCmd,ChkUp,_StopCommand,_ConfigUpd,_Host,_Port} = ?config(ftpd_data, Config),
- ct:log( ChkUp(?config(ftpd_start_result,Config)) ).
+ {_Name,_StartCmd,ChkUp,_StopCommand,_ConfigUpd,_Host,_Port} = proplists:get_value(ftpd_data, Config),
+ ct:log( ChkUp(proplists:get_value(ftpd_start_result,Config)) ).
ftpd_running(Config) ->
- {_Name,_StartCmd,ChkUp,_StopCommand,_ConfigUpd,_Host,_Port} = ?config(ftpd_data, Config),
- ChkUp(?config(ftpd_start_result,Config)).
+ {_Name,_StartCmd,ChkUp,_StopCommand,_ConfigUpd,_Host,_Port} = proplists:get_value(ftpd_data, Config),
+ ChkUp(proplists:get_value(ftpd_start_result,Config)).
-%%--------------------------------------------------------------------
-%% start/stop of ftpc
-%%
ftp__open(Config, Options) ->
- Host = ?config(ftpd_host,Config),
- Port = ?config(ftpd_port,Config),
+ Host = proplists:get_value(ftpd_host,Config),
+ Port = proplists:get_value(ftpd_port,Config),
ct:log("Host=~p, Port=~p",[Host,Port]),
{ok,Pid} = ftp:open(Host, [{port,Port} | Options]),
[{ftp,Pid}|Config].
ftp__close(Config) ->
- ok = ftp:close(?config(ftp,Config)),
+ ok = ftp:close(proplists:get_value(ftp,Config)),
Config.
-%%--------------------------------------------------------------------
-%%
split(Cs) -> string:tokens(Cs, "\r\n").
-%%--------------------------------------------------------------------
-%%
find_diff(Bin1, Bin2) ->
case find_diff(Bin1, Bin2, 1) of
{error, {diff,Pos,RC,LC}} ->
@@ -753,15 +843,14 @@ find_diff(Bin1, Bin2) ->
find_diff(A, A, _) -> true;
find_diff(<<H,T1/binary>>, <<H,T2/binary>>, Pos) -> find_diff(T1, T2, Pos+1);
find_diff(RC, LC, Pos) -> {error, {diff, Pos, RC, LC}}.
-%%--------------------------------------------------------------------
-%%
+
set_state(Ops, Config) when is_list(Ops) -> lists:foldl(fun set_state/2, Config, Ops);
set_state(reset, Config) ->
rm('*', id2abs("",Config)),
- PrivDir = ?config(priv_dir,Config),
+ PrivDir = proplists:get_value(priv_dir,Config),
file:set_cwd(PrivDir),
- ftp:lcd(?config(ftp,Config),PrivDir),
+ ftp:lcd(proplists:get_value(ftp,Config),PrivDir),
set_state({mkdir,""},Config);
set_state({mkdir,Id}, Config) ->
Abs = id2abs(Id, Config),
@@ -787,7 +876,6 @@ mk_path(F, Pfx) ->
AbsName
end.
-
rm('*', Pfx) ->
{ok,Fs} = file:list_dir(Pfx),
lists:foreach(fun(F) -> rm(F, Pfx) end, Fs);
@@ -805,17 +893,115 @@ rm(F, Pfx) ->
ok
end.
-%%--------------------------------------------------------------------
-%%
+not_owner(FtpPid, Pid) ->
+ {error, not_connection_owner} = ftp:pwd(FtpPid),
+ ftp:close(FtpPid),
+ ct:sleep(100),
+ Pid ! {self(), ok}.
-id2abs(Id, Conf) -> filename:join(?config(priv_dir,Conf),ids(Id)).
-id2ftp(Id, Conf) -> (?config(id2ftp,Conf))(ids(Id)).
-id2ftp_result(Id, Conf) -> (?config(id2ftp_result,Conf))(ids(Id)).
+id2abs(Id, Conf) -> filename:join(proplists:get_value(priv_dir,Conf),ids(Id)).
+id2ftp(Id, Conf) -> (proplists:get_value(id2ftp,Conf))(ids(Id)).
+id2ftp_result(Id, Conf) -> (proplists:get_value(id2ftp_result,Conf))(ids(Id)).
ids([[_|_]|_]=Ids) -> filename:join(Ids);
ids(Id) -> Id.
-is_expected_absName(Id, File, Conf) -> File = (?config(id2abs,Conf))(Id).
-is_expected_ftpInName(Id, File, Conf) -> File = (?config(id2ftp,Conf))(Id).
-is_expected_ftpOutName(Id, File, Conf) -> File = (?config(id2ftp_result,Conf))(Id).
+is_expected_absName(Id, File, Conf) -> File = (proplists:get_value(id2abs,Conf))(Id).
+is_expected_ftpInName(Id, File, Conf) -> File = (proplists:get_value(id2ftp,Conf))(Id).
+is_expected_ftpOutName(Id, File, Conf) -> File = (proplists:get_value(id2ftp_result,Conf))(Id).
+
+
+progress(#progress{} = Progress , _File, {file_size, Total}) ->
+ progress_report_receiver ! start,
+ Progress#progress{total = Total};
+
+progress(#progress{total = Total, current = Current}
+ = Progress, _File, {transfer_size, 0}) ->
+ progress_report_receiver ! finish,
+ case Total of
+ unknown ->
+ ok;
+ Current ->
+ ok;
+ _ ->
+ ct:fail({error, {progress, {total, Total},
+ {current, Current}}})
+ end,
+ Progress;
+progress(#progress{current = Current} = Progress, _File,
+ {transfer_size, Size}) ->
+ progress_report_receiver ! update,
+ Progress#progress{current = Current + Size}.
+
+progress_report_receiver_init(Pid, N) ->
+ register(progress_report_receiver, self()),
+ receive
+ start ->
+ ok
+ end,
+ progress_report_receiver_loop(Pid, N-1).
+
+progress_report_receiver_loop(Pid, N) ->
+ receive
+ update ->
+ progress_report_receiver_loop(Pid, N);
+ finish when N =:= 0 ->
+ Pid ! {self(), ok};
+ finish ->
+ Pid ! {self(), ok},
+ receive
+ start ->
+ ok
+ end,
+ progress_report_receiver_loop(Pid, N-1)
+ end.
+
+kill_ftp_proc_6035(Pid, LogFile) ->
+ receive
+ open ->
+ exit(Pid, shutdown),
+ kill_ftp_proc_6035(Pid, LogFile);
+ {open_failed, Reason} ->
+ exit({skip, {failed_openening_server_connection, Reason}})
+ after
+ 5000 ->
+ is_error_report_6035(LogFile)
+ end.
+
+open_wait_6035({_Tag, FtpServer}, From) ->
+ case ftp:open(FtpServer, [{timeout, timer:seconds(15)}]) of
+ {ok, Pid} ->
+ _LoginResult = ftp:user(Pid,"anonymous","kldjf"),
+ From ! open,
+ receive
+ dummy ->
+ ok
+ after
+ 10000 ->
+ ok
+ end,
+ ok;
+ {error, Reason} ->
+ From ! {open_failed, {Reason, FtpServer}},
+ ok
+ end.
+
+is_error_report_6035(LogFile) ->
+ Res =
+ case file:read_file(LogFile) of
+ {ok, Bin} ->
+ Txt = binary_to_list(Bin),
+ read_log_6035(Txt);
+ _ ->
+ false
+ end,
+ %% file:delete(LogFile),
+ Res.
+
+read_log_6035("=ERROR REPORT===="++_Rest) ->
+ true;
+read_log_6035([_|T]) ->
+ read_log_6035(T);
+read_log_6035([]) ->
+ false.
diff --git a/lib/inets/test/ftp_format_SUITE.erl b/lib/inets/test/ftp_format_SUITE.erl
index cbe12e566b..2c17e2657c 100644
--- a/lib/inets/test/ftp_format_SUITE.erl
+++ b/lib/inets/test/ftp_format_SUITE.erl
@@ -24,18 +24,13 @@
-include_lib("common_test/include/ct.hrl").
-include("ftp_internal.hrl").
-%% Test server specific exports
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
- init_per_testcase/2, end_per_testcase/2]).
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
-%% Test cases must be exported.
--export([ ftp_150/1,
- ftp_200/1, ftp_220/1, ftp_226/1, ftp_257/1, ftp_331/1, ftp_425/1,
- ftp_other_status_codes/1, ftp_multiple_lines/1,
- ftp_multipel_ctrl_messages/1, format_error/1]).
-
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap,{seconds,5}}
+ ].
all() ->
[{group, ftp_response}, format_error].
@@ -60,23 +55,16 @@ end_per_group(_GroupName, Config) ->
init_per_testcase(_, Config) ->
- Dog = test_server:timetrap(?t:minutes(1)),
- NewConfig = lists:keydelete(watchdog, 1, Config),
- [{watchdog, Dog} | NewConfig].
-
-end_per_testcase(_, Config) ->
- Dog = ?config(watchdog, Config),
- test_server:timetrap_cancel(Dog),
+ Config.
+end_per_testcase(_, _) ->
ok.
%%-------------------------------------------------------------------------
%% Test cases starts here.
%%-------------------------------------------------------------------------
-ftp_150(doc) ->
- ["Especially check that respons can be devided in a random place."];
-ftp_150(suite) ->
- [];
+ftp_150() ->
+ [{doc, "Especially check that respons can be devided in a random place."}].
ftp_150(Config) when is_list(Config) ->
FtpResponse = ["150 ASCII data conn", "ection for /bin/ls ",
"(134.138.177", ".89,50434) (0 bytes).\r\n"],
@@ -84,14 +72,11 @@ ftp_150(Config) when is_list(Config) ->
"150 ASCII data connection for /bin/ls "
"(134.138.177.89,50434) (0 bytes).\r\n" = Msg =
parse(ftp_response, parse_lines, [[], start], FtpResponse),
- {pos_prel, _} = ftp_response:interpret(Msg),
- ok.
-
-ftp_200(doc) ->
- ["Especially check that respons can be devided after the first status "
- "code character and in the end delimiter."];
-ftp_200(suite) ->
- [];
+ {pos_prel, _} = ftp_response:interpret(Msg).
+
+ftp_200() ->
+ [{doc, "Especially check that respons can be devided after the first status "
+ "code character and in the end delimiter."}].
ftp_200(Config) when is_list(Config) ->
FtpResponse = ["2", "00 PORT command successful.", [?CR], [?LF]],
@@ -100,11 +85,9 @@ ftp_200(Config) when is_list(Config) ->
{pos_compl, _} = ftp_response:interpret(Msg),
ok.
-ftp_220(doc) ->
- ["Especially check that respons can be devided after the "
- "first with space "];
-ftp_220(suite) ->
- [];
+ftp_220() ->
+ [{doc, "Especially check that respons can be devided after the "
+ "first with space "}].
ftp_220(Config) when is_list(Config) ->
FtpResponse = ["220 ","fingon FTP server (SunOS 5.8) ready.\r\n"],
@@ -113,11 +96,9 @@ ftp_220(Config) when is_list(Config) ->
{pos_compl, _} = ftp_response:interpret(Msg),
ok.
-ftp_226(doc) ->
- ["Especially check that respons can be devided after second status code"
- " character and in the end delimiter."];
-ftp_226(suite) ->
- [];
+ftp_226() ->
+ [{doc, "Especially check that respons can be devided after second status code"
+ " character and in the end delimiter."}].
ftp_226(Config) when is_list(Config) ->
FtpResponse = ["22" "6 Transfer complete.\r", [?LF]],
@@ -126,10 +107,8 @@ ftp_226(Config) when is_list(Config) ->
{pos_compl, _} = ftp_response:interpret(Msg),
ok.
-ftp_257(doc) ->
- ["Especially check that quoted chars do not cause a problem."];
-ftp_257(suite) ->
- [];
+ftp_257() ->
+ [{doc, "Especially check that quoted chars do not cause a problem."}].
ftp_257(Config) when is_list(Config) ->
FtpResponse = ["257 \"/\" is current directory.\r\n"],
@@ -138,11 +117,9 @@ ftp_257(Config) when is_list(Config) ->
{pos_compl, _} = ftp_response:interpret(Msg),
ok.
-ftp_331(doc) ->
- ["Especially check that respons can be devided after the third status "
- " status code character."];
-ftp_331(suite) ->
- [];
+ftp_331() ->
+ [{doc, "Especially check that respons can be devided after the third status "
+ " status code character."}].
ftp_331(Config) when is_list(Config) ->
%% Brake before white space after code
FtpResponse =
@@ -153,10 +130,8 @@ ftp_331(Config) when is_list(Config) ->
{pos_interm, _} = ftp_response:interpret(Msg),
ok.
-ftp_425(doc) ->
- ["Especially check a message that was received in only one part."];
-ftp_425(suite) ->
- [];
+ftp_425() ->
+ [{doc, "Especially check a message that was received in only one part."}].
ftp_425(Config) when is_list(Config) ->
FtpResponse =
["425 Can't build data connection: Connection refused.\r\n"],
@@ -166,10 +141,8 @@ ftp_425(Config) when is_list(Config) ->
{trans_neg_compl, _} = ftp_response:interpret(Msg),
ok.
-ftp_multiple_lines(doc) ->
- ["Especially check multiple lines devided in significant places"];
-ftp_multiple_lines(suite) ->
- [];
+ftp_multiple_lines() ->
+ [{doc, "Especially check multiple lines devided in significant places"}].
ftp_multiple_lines(Config) when is_list(Config) ->
FtpResponse = ["21", "4","-The",
" following commands are recognized:\r\n"
@@ -247,13 +220,11 @@ ftp_multiple_lines(Config) when is_list(Config) ->
FtpResponse2),
ok.
-ftp_other_status_codes(doc) ->
- ["Check that other valid status codes, than the ones above, are handled"
+ftp_other_status_codes() ->
+ [{doc, "Check that other valid status codes, than the ones above, are handled"
"by ftp_response:interpret/1. Note there are som ftp status codes"
"that will not be received with the current ftp instruction support,"
- "they are not included here."];
-ftp_other_status_codes(suite) ->
- [];
+ "they are not included here."}].
ftp_other_status_codes(Config) when is_list(Config) ->
%% 1XX
@@ -289,11 +260,9 @@ ftp_other_status_codes(Config) when is_list(Config) ->
{efnamena, _ } = ftp_response:interpret("553 Foobar\r\n"),
ok.
-ftp_multipel_ctrl_messages(doc) ->
- ["The ftp server may send more than one control message as a reply,"
- "check that they are handled one at the time."];
-ftp_multipel_ctrl_messages(suite) ->
- [];
+ftp_multipel_ctrl_messages() ->
+ [{doc, "The ftp server may send more than one control message as a reply,"
+ "check that they are handled one at the time."}].
ftp_multipel_ctrl_messages(Config) when is_list(Config) ->
FtpResponse = ["200 PORT command successful.\r\n200 Foobar\r\n"],
@@ -306,10 +275,6 @@ ftp_multipel_ctrl_messages(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
-format_error(doc) ->
- [""];
-format_error(suite) ->
- [];
format_error(Config) when is_list(Config) ->
"Synchronisation error during chunk sending." =
ftp:formaterror(echunk),
@@ -346,7 +311,7 @@ parse(Module, Function, [AccLines, StatusCode], [Data | Rest]) ->
{continue, {NewData, NewAccLines, NewStatusCode}} ->
case Rest of
[] ->
- test_server:fail({wrong_input, Data, Rest});
+ ct:fail({wrong_input, Data, Rest});
[_ | _] ->
parse(Module, Function, [NewAccLines, NewStatusCode],
[binary_to_list(NewData) ++ hd(Rest) | tl(Rest)])
diff --git a/lib/inets/test/ftp_suite_lib.erl b/lib/inets/test/ftp_suite_lib.erl
deleted file mode 100644
index 0f82b1c1c3..0000000000
--- a/lib/inets/test/ftp_suite_lib.erl
+++ /dev/null
@@ -1,1695 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%
-
--module(ftp_suite_lib).
-
-
--include_lib("common_test/include/ct.hrl").
--include("inets_test_lib.hrl").
-
-%% Test server specific exports
-% -export([init_per_testcase/2, end_per_testcase/2]).
-
--compile(export_all).
-
-
--record(progress, {
- current = 0,
- total
- }).
-
-
-
--define(FTP_USER, "anonymous").
--define(FTP_PASS, passwd()).
--define(FTP_PORT, 21).
-
--define(BAD_HOST, "badhostname").
--define(BAD_USER, "baduser").
--define(BAD_DIR, "baddirectory").
-
--ifdef(ftp_debug_client).
--define(ftp_open(Host, Flags),
- do_ftp_open(Host, [{debug, debug},
- {timeout, timer:seconds(15)} | Flags])).
--else.
--ifdef(ftp_trace_client).
--define(ftp_open(Host, Flags),
- do_ftp_open(Host, [{debug, trace},
- {timeout, timer:seconds(15)} | Flags])).
--else.
--define(ftp_open(Host, Flags),
- do_ftp_open(Host, [{verbose, true},
- {timeout, timer:seconds(15)} | Flags])).
--endif.
--endif.
-
-%% -- Tickets --
-
-tickets(doc) ->
- "Test cases for reported bugs";
-tickets(suite) ->
- [ticket_6035].
-
-%% --
-
-ftpd_init(FtpdTag, Config) ->
- %% Get the host name(s) of FTP server
- Hosts =
- case ct:get_config(ftpd_hosts) of
- undefined ->
- ftpd_hosts(data_dir(Config));
- H ->
- H
- end,
- p("ftpd_init -> "
- "~n Hosts: ~p"
- "~n Config: ~p"
- "~n FtpdTag: ~p", [Hosts, Config, FtpdTag]),
- %% Get the first host that actually have a running FTP server
- case lists:keysearch(FtpdTag, 1, Hosts) of
- {value, {_, TagHosts}} when is_list(TagHosts) ->
- inets:start(),
- case (catch get_ftpd_host(TagHosts)) of
- {ok, Host} ->
- inets:stop(),
- [{ftp_remote_host, Host}|Config];
- _ ->
- inets:stop(),
- Reason = lists:flatten(
- io_lib:format("Could not find a valid "
- "FTP server for ~p (~p)",
- [FtpdTag, TagHosts])),
- {skip, Reason}
- end;
- _ ->
- Reason = lists:flatten(
- io_lib:format("No host(s) running FTPD server "
- "for ~p", [FtpdTag])),
- {skip, Reason}
- end.
-
-ftpd_fin(Config) ->
- lists:keydelete(ftp_remote_host, 1, Config).
-
-get_ftpd_host([]) ->
- {error, no_host};
-get_ftpd_host([Host|Hosts]) ->
- p("get_ftpd_host -> entry with"
- "~n Host: ~p"
- "~n", [Host]),
- case (catch ftp:open(Host, [{port, ?FTP_PORT}, {timeout, 20000}])) of
- {ok, Pid} ->
- (catch ftp:close(Pid)),
- {ok, Host};
- _ ->
- get_ftpd_host(Hosts)
- end.
-
-
-%%--------------------------------------------------------------------
-
-dirty_select_ftpd_host(Config) ->
- Hosts =
- case ct:get_config(ftpd_hosts) of
- undefined ->
- ftpd_hosts(data_dir(Config));
- H ->
- H
- end,
- dirty_select_ftpd_host2(Hosts).
-
-dirty_select_ftpd_host2([]) ->
- throw({error, not_found});
-dirty_select_ftpd_host2([{PlatformTag, Hosts} | PlatformHosts]) ->
- case dirty_select_ftpd_host3(Hosts) of
- none ->
- dirty_select_ftpd_host2(PlatformHosts);
- {ok, Host} ->
- {PlatformTag, Host}
- end.
-
-dirty_select_ftpd_host3([]) ->
- none;
-dirty_select_ftpd_host3([Host|Hosts]) when is_list(Host) ->
- case dirty_select_ftpd_host4(Host) of
- true ->
- {ok, Host};
- false ->
- dirty_select_ftpd_host3(Hosts)
- end;
-dirty_select_ftpd_host3([_|Hosts]) ->
- dirty_select_ftpd_host3(Hosts).
-
-%% This is a very simple and dirty test that there is a
-%% (FTP) deamon on the other end.
-dirty_select_ftpd_host4(Host) ->
- Port = 21,
- IpFam = inet,
- Opts = [IpFam, binary, {packet, 0}, {active, false}],
- Timeout = ?SECS(5),
- case gen_tcp:connect(Host, Port, Opts, Timeout) of
- {ok, Sock} ->
- gen_tcp:close(Sock),
- true;
- _Error ->
- false
- end.
-
-
-%%--------------------------------------------------------------------
-
-test_filenames() ->
- {ok, Host} = inet:gethostname(),
- File = Host ++ "_ftp_test.txt",
- NewFile = "new_" ++ File,
- {File, NewFile}.
-
-%%--------------------------------------------------------------------
-%% Function: init_per_testcase(Case, Config) -> Config
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%%
-%% Description: Initiation before each test case
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%%--------------------------------------------------------------------
-init_per_testcase(Case, Config)
- when (Case =:= open) orelse
- (Case =:= open_port) ->
- put(ftp_testcase, Case),
- io:format(user, "~n~n*** INIT ~w:~w ***~n~n", [?MODULE, Case]),
- inets:start(),
- NewConfig = data_dir(Config),
- watch_dog(NewConfig);
-
-init_per_testcase(Case, Config) ->
- put(ftp_testcase, Case),
- do_init_per_testcase(Case, Config).
-
-do_init_per_testcase(Case, Config)
- when (Case =:= passive_user) ->
- io:format(user, "~n~n*** INIT ~w:~w ***~n~n", [?MODULE,Case]),
- inets:start(),
- NewConfig = close_connection(watch_dog(Config)),
- Host = ftp_host(Config),
- case (catch ?ftp_open(Host, [{mode, passive}])) of
- {ok, Pid} ->
- [{ftp, Pid} | data_dir(NewConfig)];
- {skip, _} = SKIP ->
- SKIP
- end;
-
-do_init_per_testcase(Case, Config)
- when (Case =:= active_user) ->
- io:format(user, "~n~n*** INIT ~w:~w ***~n~n", [?MODULE, Case]),
- inets:start(),
- NewConfig = close_connection(watch_dog(Config)),
- Host = ftp_host(Config),
- case (catch ?ftp_open(Host, [{mode, active}])) of
- {ok, Pid} ->
- [{ftp, Pid} | data_dir(NewConfig)];
- {skip, _} = SKIP ->
- SKIP
- end;
-
-do_init_per_testcase(Case, Config)
- when (Case =:= progress_report_send) orelse
- (Case =:= progress_report_recv) ->
- inets:start(),
- io:format(user, "~n~n*** INIT ~w:~w ***~n~n", [?MODULE, Case]),
- NewConfig = close_connection(watch_dog(Config)),
- Host = ftp_host(Config),
- Opts = [{port, ?FTP_PORT},
- {verbose, true},
- {progress, {?MODULE, progress, #progress{}}}],
- case ftp:open(Host, Opts) of
- {ok, Pid} ->
- ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS),
- [{ftp, Pid} | data_dir(NewConfig)];
- {skip, _} = SKIP ->
- SKIP
- end;
-
-do_init_per_testcase(Case, Config) ->
- io:format(user,"~n~n*** INIT ~w:~w ***~n~n", [?MODULE, Case]),
- inets:start(),
- NewConfig = close_connection(watch_dog(Config)),
- Host = ftp_host(Config),
- Opts1 =
- if
- ((Case =:= passive_ip_v6_disabled) orelse
- (Case =:= active_ip_v6_disabled)) ->
- [{ipfamily, inet}];
- true ->
- []
- end,
- Opts2 =
- case string:tokens(atom_to_list(Case), [$_]) of
- ["active" | _] ->
- [{mode, active} | Opts1];
- _ ->
- [{mode, passive} | Opts1]
- end,
- case (catch ?ftp_open(Host, Opts2)) of
- {ok, Pid} ->
- ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS),
- [{ftp, Pid} | data_dir(NewConfig)];
- {skip, _} = SKIP ->
- SKIP
- end.
-
-
-%%--------------------------------------------------------------------
-%% Function: end_per_testcase(Case, Config) -> _
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after each test case
-%%--------------------------------------------------------------------
-end_per_testcase(_, Config) ->
- NewConfig = close_connection(Config),
- Dog = ?config(watchdog, NewConfig),
- inets:stop(),
- test_server:timetrap_cancel(Dog),
- ok.
-
-
-%%-------------------------------------------------------------------------
-%% Suites similar for all hosts.
-%%-------------------------------------------------------------------------
-
-passive(suite) ->
- [
- passive_user,
- passive_pwd,
- passive_cd,
- passive_lcd,
- passive_ls,
- passive_nlist,
- passive_rename,
- passive_delete,
- passive_mkdir,
- passive_send,
- passive_send_bin,
- passive_send_chunk,
- passive_append,
- passive_append_bin,
- passive_append_chunk,
- passive_recv,
- passive_recv_bin,
- passive_recv_chunk,
- passive_type,
- passive_quote,
- passive_ip_v6_disabled
- ].
-
-active(suite) ->
- [
- active_user,
- active_pwd,
- active_cd,
- active_lcd,
- active_ls,
- active_nlist,
- active_rename,
- active_delete,
- active_mkdir,
- active_send,
- active_send_bin,
- active_send_chunk,
- active_append,
- active_append_bin,
- active_append_chunk,
- active_recv,
- active_recv_bin,
- active_recv_chunk,
- active_type,
- active_quote,
- active_ip_v6_disabled
- ].
-
-
-
-%%-------------------------------------------------------------------------
-%% Test cases starts here.
-%%-------------------------------------------------------------------------
-
-open(doc) ->
- ["Open an ftp connection to a host and close the connection."
- "Also check that !-messages does not disturbe the connection"];
-open(suite) ->
- [];
-open(Config) when is_list(Config) ->
- Host = ftp_host(Config),
- (catch tc_open(Host)).
-
-
-tc_open(Host) ->
- p("tc_open -> entry with"
- "~n Host: ~p", [Host]),
- {ok, Pid} = ?ftp_open(Host, []),
- ok = ftp:close(Pid),
- p("tc_open -> try (ok) open 1"),
- {ok, Pid1} =
- ftp:open({option_list, [{host,Host},
- {port, ?FTP_PORT},
- {flags, [verbose]},
- {timeout, 30000}]}),
- ok = ftp:close(Pid1),
-
- p("tc_open -> try (fail) open 2"),
- {error, ehost} =
- ftp:open({option_list, [{port, ?FTP_PORT}, {flags, [verbose]}]}),
- {ok, Pid2} = ftp:open(Host),
- ok = ftp:close(Pid2),
-
- p("tc_open -> try (ok) open 3"),
- {ok, NewHost} = inet:getaddr(Host, inet),
- {ok, Pid3} = ftp:open(NewHost),
- ftp:user(Pid3, ?FTP_USER, ?FTP_PASS),
- Pid3 ! foobar,
- test_server:sleep(5000),
- {message_queue_len, 0} = process_info(self(), message_queue_len),
- ["200" ++ _] = ftp:quote(Pid3, "NOOP"),
- ok = ftp:close(Pid3),
-
- %% Bad input that has default values are ignored and the defult
- %% is used.
- p("tc_open -> try (ok) open 4"),
- {ok, Pid4} =
- ftp:open({option_list, [{host, Host},
- {port, badarg},
- {flags, [verbose]},
- {timeout, 30000}]}),
- test_server:sleep(100),
- ok = ftp:close(Pid4),
-
- p("tc_open -> try (ok) open 5"),
- {ok, Pid5} =
- ftp:open({option_list, [{host, Host},
- {port, ?FTP_PORT},
- {flags, [verbose]},
- {timeout, -42}]}),
- test_server:sleep(100),
- ok = ftp:close(Pid5),
-
- p("tc_open -> try (ok) open 6"),
- {ok, Pid6} =
- ftp:open({option_list, [{host, Host},
- {port, ?FTP_PORT},
- {flags, [verbose]},
- {mode, cool}]}),
- test_server:sleep(100),
- ok = ftp:close(Pid6),
-
- p("tc_open -> try (ok) open 7"),
- {ok, Pid7} =
- ftp:open(Host, [{port, ?FTP_PORT}, {verbose, true}, {timeout, 30000}]),
- ok = ftp:close(Pid7),
-
- p("tc_open -> try (ok) open 8"),
- {ok, Pid8} =
- ftp:open(Host, ?FTP_PORT),
- ok = ftp:close(Pid8),
-
- p("tc_open -> try (ok) open 9"),
- {ok, Pid9} =
- ftp:open(Host, [{port, ?FTP_PORT},
- {verbose, true},
- {timeout, 30000},
- {dtimeout, -99}]),
- ok = ftp:close(Pid9),
-
- p("tc_open -> try (ok) open 10"),
- {ok, Pid10} =
- ftp:open(Host, [{port, ?FTP_PORT},
- {verbose, true},
- {timeout, 30000},
- {dtimeout, "foobar"}]),
- ok = ftp:close(Pid10),
-
- p("tc_open -> try (ok) open 11"),
- {ok, Pid11} =
- ftp:open(Host, [{port, ?FTP_PORT},
- {verbose, true},
- {timeout, 30000},
- {dtimeout, 1}]),
- ok = ftp:close(Pid11),
-
- p("tc_open -> done"),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-open_port(doc) ->
- ["Open an ftp connection to a host with given port number "
- "and close the connection."]; % See also OTP-3892
-open_port(suite) ->
- [];
-open_port(Config) when is_list(Config) ->
- Host = ftp_host(Config),
- {ok, Pid} = ftp:open(Host, [{port, ?FTP_PORT}]),
- ok = ftp:close(Pid),
- {error, ehost} = ftp:open(?BAD_HOST, []),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-passive_user(doc) ->
- ["Open an ftp connection to a host, and logon as anonymous ftp."];
-passive_user(suite) ->
- [];
-passive_user(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- p("Pid: ~p",[Pid]),
- do_user(Pid).
-
-
-%%-------------------------------------------------------------------------
-
-passive_pwd(doc) ->
- ["Test ftp:pwd/1 & ftp:lpwd/1"];
-passive_pwd(suite) ->
- [];
-passive_pwd(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_pwd(Pid).
-
-
-%%-------------------------------------------------------------------------
-
-passive_cd(doc) ->
- ["Open an ftp connection, log on as anonymous ftp, and cd to the"
- "directory \"/pub\" and the to the non-existent directory."];
-passive_cd(suite) ->
- [];
-passive_cd(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_cd(Pid).
-
-
-%%-------------------------------------------------------------------------
-
-passive_lcd(doc) ->
- ["Test api function ftp:lcd/2"];
-passive_lcd(suite) ->
- [];
-passive_lcd(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- PrivDir = ?config(priv_dir, Config),
- do_lcd(Pid, PrivDir).
-
-
-%%-------------------------------------------------------------------------
-
-passive_ls(doc) ->
- ["Open an ftp connection; ls the current directory, and the "
- "\"incoming\" directory. We assume that ls never fails, since "
- "it's output is meant to be read by humans. "];
-passive_ls(suite) ->
- [];
-passive_ls(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_ls(Pid).
-
-
-%%-------------------------------------------------------------------------
-
-passive_nlist(doc) ->
- ["Open an ftp connection; nlist the current directory, and the "
- "\"incoming\" directory. Nlist does not behave consistenly over "
- "operating systems. On some it is an error to have an empty "
- "directory."];
-passive_nlist(suite) ->
- [];
-passive_nlist(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- WildcardSupport = ?config(wildcard_support, Config),
- do_nlist(Pid, WildcardSupport).
-
-
-%%-------------------------------------------------------------------------
-
-passive_rename(doc) ->
- ["Transfer a file to the server, and rename it; then remove it."];
-passive_rename(suite) ->
- [];
-passive_rename(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_rename(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-passive_delete(doc) ->
- ["Transfer a file to the server, and then delete it"];
-passive_delete(suite) ->
- [];
-passive_delete(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_delete(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-passive_mkdir(doc) ->
- ["Make a remote directory, cd to it, go to parent directory, and "
- "remove the directory."];
-passive_mkdir(suite) ->
- [];
-passive_mkdir(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_mkdir(Pid).
-
-
-%%-------------------------------------------------------------------------
-
-passive_send(doc) ->
- ["Create a local file in priv_dir; open an ftp connection to a host; "
- "logon as anonymous ftp; cd to the directory \"incoming\"; lcd to "
- "priv_dir; send the file; get a directory listing and check that "
- "the file is on the list;, delete the remote file; get another listing "
- "and check that the file is not on the list; close the session; "
- "delete the local file."];
-passive_send(suite) ->
- [];
-passive_send(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_send(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-passive_append(doc) ->
- ["Create a local file in priv_dir; open an ftp connection to a host; "
- "logon as anonymous ftp; cd to the directory \"incoming\"; lcd to "
- "priv_dir; append the file to a file at the remote side that not exits"
- "this will create the file at the remote side. Then it append the file "
- "again. When this is done it recive the remote file and control that"
- "the content is doubled in it.After that it will remove the files"];
-passive_append(suite) ->
- [];
-passive_append(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_append(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-passive_send_bin(doc) ->
- ["Open a connection to a host; cd to the directory \"incoming\"; "
- "send a binary; remove file; close the connection."];
-passive_send_bin(suite) ->
- [];
-passive_send_bin(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_send_bin(Pid, Config).
-
-%%-------------------------------------------------------------------------
-
-passive_append_bin(doc) ->
- ["Open a connection to a host; cd to the directory \"incoming\"; "
- "append a binary twice; get the file and compare the content"
- "remove file; close the connection."];
-passive_append_bin(suite) ->
- [];
-passive_append_bin(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_append_bin(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-passive_send_chunk(doc) ->
- ["Open a connection to a host; cd to the directory \"incoming\"; "
- "send chunks; remove file; close the connection."];
-passive_send_chunk(suite) ->
- [];
-passive_send_chunk(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_send_chunk(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-passive_append_chunk(doc) ->
- ["Open a connection to a host; cd to the directory \"incoming\"; "
- "append chunks;control content remove file; close the connection."];
-passive_append_chunk(suite) ->
- [];
-passive_append_chunk(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_append_chunk(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-passive_recv(doc) ->
- ["Create a local file and transfer it to the remote host into the "
- "the \"incoming\" directory, remove "
- "the local file. Then open a new connection; cd to \"incoming\", "
- "lcd to the private directory; receive the file; delete the "
- "remote file; close connection; check that received file is in "
- "the correct directory; cleanup." ];
-passive_recv(suite) ->
- [];
-passive_recv(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_recv(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-passive_recv_bin(doc) ->
- ["Send a binary to the remote host; and retreive "
- "the file; then remove the file."];
-passive_recv_bin(suite) ->
- [];
-passive_recv_bin(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_recv_bin(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-passive_recv_chunk(doc) ->
- ["Send a binary to the remote host; Connect again, and retreive "
- "the file; then remove the file."];
-passive_recv_chunk(suite) ->
- [];
-passive_recv_chunk(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_recv_chunk(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-passive_type(doc) ->
- ["Test that we can change btween ASCCI and binary transfer mode"];
-passive_type(suite) ->
- [];
-passive_type(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_type(Pid).
-
-
-%%-------------------------------------------------------------------------
-
-passive_quote(doc) ->
- [""];
-passive_quote(suite) ->
- [];
-passive_quote(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_quote(Pid).
-
-
-%%-------------------------------------------------------------------------
-
-passive_ip_v6_disabled(doc) ->
- ["Test ipv4 command PASV"];
-passive_ip_v6_disabled(suite) ->
- [];
-passive_ip_v6_disabled(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_send(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-active_user(doc) ->
- ["Open an ftp connection to a host, and logon as anonymous ftp."];
-active_user(suite) ->
- [];
-active_user(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_user(Pid).
-
-
-%%-------------------------------------------------------------------------
-
-active_pwd(doc) ->
- ["Test ftp:pwd/1 & ftp:lpwd/1"];
-active_pwd(suite) ->
- [];
-active_pwd(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_pwd(Pid).
-
-
-%%-------------------------------------------------------------------------
-
-active_cd(doc) ->
- ["Open an ftp connection, log on as anonymous ftp, and cd to the"
- "directory \"/pub\" and to a non-existent directory."];
-active_cd(suite) ->
- [];
-active_cd(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_cd(Pid).
-
-
-%%-------------------------------------------------------------------------
-
-active_lcd(doc) ->
- ["Test api function ftp:lcd/2"];
-active_lcd(suite) ->
- [];
-active_lcd(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- PrivDir = ?config(priv_dir, Config),
- do_lcd(Pid, PrivDir).
-
-
-%%-------------------------------------------------------------------------
-
-active_ls(doc) ->
- ["Open an ftp connection; ls the current directory, and the "
- "\"incoming\" directory. We assume that ls never fails, since "
- "it's output is meant to be read by humans. "];
-active_ls(suite) ->
- [];
-active_ls(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_ls(Pid).
-
-
-%%-------------------------------------------------------------------------
-
-active_nlist(doc) ->
- ["Open an ftp connection; nlist the current directory, and the "
- "\"incoming\" directory. Nlist does not behave consistenly over "
- "operating systems. On some it is an error to have an empty "
- "directory."];
-active_nlist(suite) ->
- [];
-active_nlist(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- WildcardSupport = ?config(wildcard_support, Config),
- do_nlist(Pid, WildcardSupport).
-
-
-%%-------------------------------------------------------------------------
-
-active_rename(doc) ->
- ["Transfer a file to the server, and rename it; then remove it."];
-active_rename(suite) ->
- [];
-active_rename(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_rename(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-active_delete(doc) ->
- ["Transfer a file to the server, and then delete it"];
-active_delete(suite) ->
- [];
-active_delete(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_delete(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-active_mkdir(doc) ->
- ["Make a remote directory, cd to it, go to parent directory, and "
- "remove the directory."];
-active_mkdir(suite) ->
- [];
-active_mkdir(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_mkdir(Pid).
-
-
-%%-------------------------------------------------------------------------
-
-active_send(doc) ->
- ["Create a local file in priv_dir; open an ftp connection to a host; "
- "logon as anonymous ftp; cd to the directory \"incoming\"; lcd to "
- "priv_dir; send the file; get a directory listing and check that "
- "the file is on the list;, delete the remote file; get another listing "
- "and check that the file is not on the list; close the session; "
- "delete the local file."];
-active_send(suite) ->
- [];
-active_send(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_send(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-active_append(doc) ->
- ["Create a local file in priv_dir; open an ftp connection to a host; "
- "logon as anonymous ftp; cd to the directory \"incoming\"; lcd to "
- "priv_dir; append the file to a file at the remote side that not exits"
- "this will create the file at the remote side. Then it append the file "
- "again. When this is done it recive the remote file and control that"
- "the content is doubled in it.After that it will remove the files"];
-active_append(suite) ->
- [];
-active_append(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_append(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-active_send_bin(doc) ->
- ["Open a connection to a host; cd to the directory \"incoming\"; "
- "send a binary; remove file; close the connection."];
-active_send_bin(suite) ->
- [];
-active_send_bin(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_send_bin(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-active_append_bin(doc) ->
- ["Open a connection to a host; cd to the directory \"incoming\"; "
- "append a binary twice; get the file and compare the content"
- "remove file; close the connection."];
-active_append_bin(suite) ->
- [];
-active_append_bin(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_append_bin(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-active_send_chunk(doc) ->
- ["Open a connection to a host; cd to the directory \"incoming\"; "
- "send chunks; remove file; close the connection."];
-active_send_chunk(suite) ->
- [];
-active_send_chunk(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_send_chunk(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-active_append_chunk(doc) ->
- ["Open a connection to a host; cd to the directory \"incoming\"; "
- "append chunks;control content remove file; close the connection."];
-active_append_chunk(suite) ->
- [];
-active_append_chunk(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_append_chunk(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-active_recv(doc) ->
- ["Create a local file and transfer it to the remote host into the "
- "the \"incoming\" directory, remove "
- "the local file. Then open a new connection; cd to \"incoming\", "
- "lcd to the private directory; receive the file; delete the "
- "remote file; close connection; check that received file is in "
- "the correct directory; cleanup." ];
-active_recv(suite) ->
- [];
-active_recv(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_recv(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-active_recv_bin(doc) ->
- ["Send a binary to the remote host; and retreive "
- "the file; then remove the file."];
-active_recv_bin(suite) ->
- [];
-active_recv_bin(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_recv_bin(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-active_recv_chunk(doc) ->
- ["Send a binary to the remote host; Connect again, and retreive "
- "the file; then remove the file."];
-active_recv_chunk(suite) ->
- [];
-active_recv_chunk(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_recv_chunk(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-active_type(doc) ->
- ["Test that we can change btween ASCCI and binary transfer mode"];
-active_type(suite) ->
- [];
-active_type(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_type(Pid).
-
-
-%%-------------------------------------------------------------------------
-
-active_quote(doc) ->
- [""];
-active_quote(suite) ->
- [];
-active_quote(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_quote(Pid).
-
-
-%%-------------------------------------------------------------------------
-
-active_ip_v6_disabled(doc) ->
- ["Test ipv4 command PORT"];
-active_ip_v6_disabled(suite) ->
- [];
-active_ip_v6_disabled(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- do_send(Pid, Config).
-
-
-%%-------------------------------------------------------------------------
-
-api_missuse(doc)->
- ["Test that behaviour of the ftp process if the api is abused"];
-api_missuse(suite) -> [];
-api_missuse(Config) when is_list(Config) ->
- p("api_missuse -> entry"),
- Flag = process_flag(trap_exit, true),
- Pid = ?config(ftp, Config),
- Host = ftp_host(Config),
-
- %% Serious programming fault, connetion will be shut down
- p("api_missuse -> verify bad call termination (~p)", [Pid]),
- case (catch gen_server:call(Pid, {self(), foobar, 10}, infinity)) of
- {error, {connection_terminated, 'API_violation'}} ->
- ok;
- Unexpected1 ->
- exit({unexpected_result, Unexpected1})
- end,
- test_server:sleep(500),
- undefined = process_info(Pid, status),
-
- p("api_missuse -> start new client"),
- {ok, Pid2} = ?ftp_open(Host, []),
- %% Serious programming fault, connetion will be shut down
- p("api_missuse -> verify bad cast termination"),
- gen_server:cast(Pid2, {self(), foobar, 10}),
- test_server:sleep(500),
- undefined = process_info(Pid2, status),
-
- p("api_missuse -> start new client"),
- {ok, Pid3} = ?ftp_open(Host, []),
- %% Could be an innocent misstake the connection lives.
- p("api_missuse -> verify bad bang"),
- Pid3 ! foobar,
- test_server:sleep(500),
- {status, _} = process_info(Pid3, status),
- process_flag(trap_exit, Flag),
- p("api_missuse -> done"),
- ok.
-
-
-%%-------------------------------------------------------------------------
-
-not_owner(doc) ->
- ["Test what happens if a process that not owns the connection tries "
- "to use it"];
-not_owner(suite) ->
- [];
-not_owner(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- OtherPid = spawn_link(?MODULE, not_owner, [Pid, self()]),
-
- receive
- {OtherPid, ok} ->
- {ok, _} = ftp:pwd(Pid)
- end,
- ok.
-
-not_owner(FtpPid, Pid) ->
- {error, not_connection_owner} = ftp:pwd(FtpPid),
- ftp:close(FtpPid),
- test_server:sleep(100),
- Pid ! {self(), ok}.
-
-
-%%-------------------------------------------------------------------------
-
-
-progress_report(doc) ->
- ["Solaris 8 sparc test the option progress."];
-progress_report(suite) ->
- [progress_report_send, progress_report_recv].
-
-
-%% --
-
-progress_report_send(doc) ->
- ["Test the option progress for ftp:send/[2,3]"];
-progress_report_send(suite) ->
- [];
-progress_report_send(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- ReportPid =
- spawn_link(?MODULE, progress_report_receiver_init, [self(), 1]),
- do_send(Pid, Config),
- receive
- {ReportPid, ok} ->
- ok
- end.
-
-
-%% --
-
-progress_report_recv(doc) ->
- ["Test the option progress for ftp:recv/[2,3]"];
-progress_report_recv(suite) ->
- [];
-progress_report_recv(Config) when is_list(Config) ->
- Pid = ?config(ftp, Config),
- ReportPid =
- spawn_link(?MODULE, progress_report_receiver_init, [self(), 3]),
- do_recv(Pid, Config),
- receive
- {ReportPid, ok} ->
- ok
- end,
- ok.
-
-progress(#progress{} = Progress , _File, {file_size, Total}) ->
- progress_report_receiver ! start,
- Progress#progress{total = Total};
-progress(#progress{total = Total, current = Current}
- = Progress, _File, {transfer_size, 0}) ->
- progress_report_receiver ! finish,
- case Total of
- unknown ->
- ok;
- Current ->
- ok;
- _ ->
- test_server:fail({error, {progress, {total, Total},
- {current, Current}}})
- end,
- Progress;
-progress(#progress{current = Current} = Progress, _File,
- {transfer_size, Size}) ->
- progress_report_receiver ! update,
- Progress#progress{current = Current + Size}.
-
-progress_report_receiver_init(Pid, N) ->
- register(progress_report_receiver, self()),
- receive
- start ->
- ok
- end,
- progress_report_receiver_loop(Pid, N-1).
-
-progress_report_receiver_loop(Pid, N) ->
- receive
- update ->
- progress_report_receiver_loop(Pid, N);
- finish when N =:= 0 ->
- Pid ! {self(), ok};
- finish ->
- Pid ! {self(), ok},
- receive
- start ->
- ok
- end,
- progress_report_receiver_loop(Pid, N-1)
- end.
-
-
-%%-------------------------------------------------------------------------
-%% Ticket test cases
-%%-------------------------------------------------------------------------
-
-ticket_6035(doc) -> ["Test that owning process that exits with reason "
- "'shutdown' does not cause an error message."];
-ticket_6035(suite) -> [];
-ticket_6035(Config) ->
- p("ticket_6035 -> entry with"
- "~n Config: ~p", [Config]),
- PrivDir = ?config(priv_dir, Config),
- LogFile = filename:join([PrivDir,"ticket_6035.log"]),
- try
- begin
- p("ticket_6035 -> select ftpd host"),
- Host = dirty_select_ftpd_host(Config),
- p("ticket_6035 -> ftpd host selected (~p) => now spawn ftp owner", [Host]),
- Pid = spawn(?MODULE, open_wait_6035, [Host, self()]),
- p("ticket_6035 -> waiter spawned: ~p => now open error logfile (~p)",
- [Pid, LogFile]),
- error_logger:logfile({open, LogFile}),
- p("ticket_6035 -> error logfile open => now kill waiter process"),
- true = kill_ftp_proc_6035(Pid, LogFile),
- p("ticket_6035 -> waiter process killed => now close error logfile"),
- error_logger:logfile(close),
- p("ticket_6035 -> done", []),
- ok
- end
- catch
- throw:{error, not_found} ->
- {skip, "No available FTP servers"}
- end.
-
-kill_ftp_proc_6035(Pid, LogFile) ->
- p("kill_ftp_proc_6035 -> entry"),
- receive
- open ->
- p("kill_ftp_proc_6035 -> received open => now issue shutdown"),
- exit(Pid, shutdown),
- kill_ftp_proc_6035(Pid, LogFile);
- {open_failed, Reason} ->
- p("kill_ftp_proc_6035 -> received open_failed"
- "~n Reason: ~p", [Reason]),
- exit({skip, {failed_openening_server_connection, Reason}})
- after
- 5000 ->
- p("kill_ftp_proc_6035 -> timeout"),
- is_error_report_6035(LogFile)
- end.
-
-open_wait_6035({Tag, FtpServer}, From) ->
- p("open_wait_6035 -> try connect to [~p] ~s for ~p", [Tag, FtpServer, From]),
- case ftp:open(FtpServer, [{timeout, timer:seconds(15)}]) of
- {ok, Pid} ->
- p("open_wait_6035 -> connected (~p), now login", [Pid]),
- LoginResult = ftp:user(Pid,"anonymous","kldjf"),
- p("open_wait_6035 -> login result: ~p", [LoginResult]),
- From ! open,
- receive
- dummy ->
- p("open_wait_6035 -> received dummy"),
- ok
- after
- 10000 ->
- p("open_wait_6035 -> timeout"),
- ok
- end,
- p("open_wait_6035 -> done(ok)"),
- ok;
- {error, Reason} ->
- p("open_wait_6035 -> open failed"
- "~n Reason: ~p", [Reason]),
- From ! {open_failed, {Reason, FtpServer}},
- p("open_wait_6035 -> done(error)"),
- ok
- end.
-
-is_error_report_6035(LogFile) ->
- p("is_error_report_6035 -> entry"),
- Res =
- case file:read_file(LogFile) of
- {ok, Bin} ->
- Txt = binary_to_list(Bin),
- p("is_error_report_6035 -> logfile read: ~n~p", [Txt]),
- read_log_6035(Txt);
- _ ->
- false
- end,
- p("is_error_report_6035 -> logfile read result: "
- "~n ~p", [Res]),
- %% file:delete(LogFile),
- Res.
-
-read_log_6035("=ERROR REPORT===="++_Rest) ->
- p("read_log_6035 -> ERROR REPORT detected"),
- true;
-read_log_6035([H|T]) ->
- p("read_log_6035 -> OTHER: "
- "~p", [H]),
- read_log_6035(T);
-read_log_6035([]) ->
- p("read_log_6035 -> done"),
- false.
-
-
-%%--------------------------------------------------------------------
-%% Internal functions
-%%--------------------------------------------------------------------
-do_user(Pid) ->
- {error, euser} = ftp:user(Pid, ?BAD_USER, ?FTP_PASS),
- {error, euser} = ftp:user(Pid, ?FTP_USER++"\r\nPASS "++?FTP_PASS, ?FTP_PASS),
- {error, euser} = ftp:user(Pid, ?FTP_USER, ?FTP_PASS++"\r\nCWD ."),
- ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS),
- ok.
-
-do_pwd(Pid) ->
- {ok, "/"} = ftp:pwd(Pid),
- {ok, Path} = ftp:lpwd(Pid),
- {ok, Path} = file:get_cwd(),
- ok.
-
-do_cd(Pid) ->
- ok = ftp:cd(Pid, "/pub"),
- {error, epath} = ftp:cd(Pid, ?BAD_DIR),
- {error, efnamena} = ftp:cd(Pid, "/pub\r\nCWD ."),
- ok.
-
-do_lcd(Pid, Dir) ->
- ok = ftp:lcd(Pid, Dir),
- {error, epath} = ftp:lcd(Pid, ?BAD_DIR),
- ok.
-
-
-do_ls(Pid) ->
- {ok, _} = ftp:ls(Pid),
- {ok, _} = ftp:ls(Pid, "incoming"),
- %% neither nlist nor ls operates on a directory
- %% they operate on a pathname, which *can* be a
- %% directory, but can also be a filename or a group
- %% of files (including wildcards).
- {ok, _} = ftp:ls(Pid, "incom*"),
- %% but \r\n can't be in the wildcard
- {error, efnamena} = ftp:ls(Pid, "incoming\r\nCWD ."),
- ok.
-
-do_nlist(Pid, WildcardSupport) ->
- {ok, _} = ftp:nlist(Pid),
- {ok, _} = ftp:nlist(Pid, "incoming"),
- {error, efnamena} = ftp:ls(Pid, "incoming\r\nCWD ."),
- %% neither nlist nor ls operates on a directory
- %% they operate on a pathname, which *can* be a
- %% directory, but can also be a filename or a group
- %% of files (including wildcards).
- case WildcardSupport of
- true ->
- {ok, _} = ftp:nlist(Pid, "incom*"),
- ok;
- _ ->
- ok
- end.
-
-do_rename(Pid, Config) ->
- PrivDir = ?config(priv_dir, Config),
- LFile = ?config(file, Config),
- NewLFile = ?config(new_file, Config),
- AbsLFile = filename:absname(LFile, PrivDir),
- Contents = "ftp_SUITE test ...",
- ok = file:write_file(AbsLFile, list_to_binary(Contents)),
- ok = ftp:cd(Pid, "incoming"),
- ok = ftp:lcd(Pid, PrivDir),
- ftp:delete(Pid, LFile), % reset
- ftp:delete(Pid, NewLFile), % reset
- ok = ftp:send(Pid, LFile),
- {error, epath} = ftp:rename(Pid, NewLFile, LFile),
- {error, efnamena} = ftp:rename(Pid, NewLFile++"\r\nRNTO "++LFile++"\r\nRNFR "++NewLFile, LFile),
- {error, efnamena} = ftp:rename(Pid, NewLFile, LFile++"\r\nCWD ."),
- ok = ftp:rename(Pid, LFile, NewLFile),
- ftp:delete(Pid, LFile), % cleanup
- ftp:delete(Pid, NewLFile), % cleanup
- ok.
-
-do_delete(Pid, Config) ->
- PrivDir = ?config(priv_dir, Config),
- LFile = ?config(file, Config),
- AbsLFile = filename:absname(LFile, PrivDir),
- Contents = "ftp_SUITE test ...",
- ok = file:write_file(AbsLFile, list_to_binary(Contents)),
- ok = ftp:cd(Pid, "incoming"),
- ok = ftp:lcd(Pid, PrivDir),
- ftp:delete(Pid,LFile), % reset
- {error, efnamena} = ftp:delete(Pid,LFile++"\r\nCWD ."),
- ok = ftp:send(Pid, LFile),
- ok = ftp:delete(Pid,LFile),
- ok.
-
-do_mkdir(Pid) ->
- NewDir = "earl_" ++
- integer_to_list(erlang:unique_integer([positive])),
-
- ok = ftp:cd(Pid, "incoming"),
- {ok, CurrDir} = ftp:pwd(Pid),
- {error, efnamena} = ftp:mkdir(Pid, NewDir++"\r\nCWD ."),
- {error, efnamena} = ftp:rmdir(Pid, NewDir++"\r\nCWD ."),
- ok = ftp:mkdir(Pid, NewDir),
- ok = ftp:cd(Pid, NewDir),
- ok = ftp:cd(Pid, CurrDir),
- ok = ftp:rmdir(Pid, NewDir),
- ok.
-
-do_send(Pid, Config) ->
- PrivDir = ?config(priv_dir, Config),
- LFile = ?config(file, Config),
- RFile = LFile ++ ".remote",
- AbsLFile = filename:absname(LFile, PrivDir),
- Contents = "ftp_SUITE test ...",
- ok = file:write_file(AbsLFile, list_to_binary(Contents)),
- ok = ftp:cd(Pid, "incoming"),
- ok = ftp:lcd(Pid, PrivDir),
- {error, efnamena} = ftp:send(Pid, LFile, RFile++"1\r\nCWD ."),
- ok = ftp:send(Pid, LFile, RFile),
- {ok, RFilesString} = ftp:nlist(Pid),
- RFiles = split(RFilesString),
- true = lists:member(RFile, RFiles),
- ok = ftp:delete(Pid, RFile),
- case ftp:nlist(Pid) of
- {error, epath} ->
- ok; % No files
- {ok, RFilesString1} ->
- RFiles1 = split(RFilesString1),
- false = lists:member(RFile, RFiles1)
- end,
- ok = file:delete(AbsLFile).
-
-do_append(Pid, Config) ->
- PrivDir = ?config(priv_dir, Config),
- LFile = ?config(file, Config),
- RFile = ?config(new_file, Config),
- AbsLFile = filename:absname(LFile, PrivDir),
- Contents = "ftp_SUITE test:appending\r\n",
-
- ok = file:write_file(AbsLFile, list_to_binary(Contents)),
- ok = ftp:cd(Pid, "incoming"),
- ok = ftp:lcd(Pid, PrivDir),
-
- %% remove files from earlier failed test case
- ftp:delete(Pid, RFile),
- ftp:delete(Pid, LFile),
-
- {error, efnamena} = ftp:append(Pid, LFile, RFile++"1\r\nCWD ."),
- ok = ftp:append(Pid, LFile, RFile),
- ok = ftp:append(Pid, LFile, RFile),
- ok = ftp:append(Pid, LFile),
-
- %% Control the contents of the file
- {ok, Bin1} = ftp:recv_bin(Pid, RFile),
- ok = ftp:delete(Pid, RFile),
- ok = file:delete(AbsLFile),
- ok = check_content(binary_to_list(Bin1), Contents, double),
-
- {ok, Bin2} = ftp:recv_bin(Pid, LFile),
- ok = ftp:delete(Pid, LFile),
- ok = check_content(binary_to_list(Bin2), Contents, singel),
- ok.
-
-do_send_bin(Pid, Config) ->
- File = ?config(file, Config),
- Contents = "ftp_SUITE test ...",
- Bin = list_to_binary(Contents),
- ok = ftp:cd(Pid, "incoming"),
- {error, enotbinary} = ftp:send_bin(Pid, Contents, File),
- {error, efnamena} = ftp:send_bin(Pid, Bin, File++"1\r\nCWD ."),
- ok = ftp:send_bin(Pid, Bin, File),
- {ok, RFilesString} = ftp:nlist(Pid),
- RFiles = split(RFilesString),
- true = lists:member(File, RFiles),
- ok = ftp:delete(Pid, File),
- ok.
-
-do_append_bin(Pid, Config) ->
- File = ?config(file, Config),
- Contents = "ftp_SUITE test ...",
- Bin = list_to_binary(Contents),
- ok = ftp:cd(Pid, "incoming"),
- {error, enotbinary} = ftp:append_bin(Pid, Contents, File),
- {error, efnamena} = ftp:append_bin(Pid, Bin, File++"1\r\nCWD ."),
- ok = ftp:append_bin(Pid, Bin, File),
- ok = ftp:append_bin(Pid, Bin, File),
- %% Control the contents of the file
- {ok, Bin2} = ftp:recv_bin(Pid, File),
- ok = ftp:delete(Pid,File),
- ok = check_content(binary_to_list(Bin2),binary_to_list(Bin), double).
-
-do_send_chunk(Pid, Config) ->
- File = ?config(file, Config),
- Contents = "ftp_SUITE test ...",
- Bin = list_to_binary(Contents),
- ok = ftp:cd(Pid, "incoming"),
- {error, efnamena} = ftp:send_chunk_start(Pid, File++"1\r\nCWD ."),
- ok = ftp:send_chunk_start(Pid, File),
- {error, echunk} = ftp:cd(Pid, "incoming"),
- {error, enotbinary} = ftp:send_chunk(Pid, Contents),
- ok = ftp:send_chunk(Pid, Bin),
- ok = ftp:send_chunk(Pid, Bin),
- ok = ftp:send_chunk_end(Pid),
- {ok, RFilesString} = ftp:nlist(Pid),
- RFiles = split(RFilesString),
- true = lists:member(File, RFiles),
- ok = ftp:delete(Pid, File),
- ok.
-
-do_append_chunk(Pid, Config) ->
- File = ?config(file, Config),
- Contents = ["ER","LE","RL"],
- ok = ftp:cd(Pid, "incoming"),
- {error, efnamena} = ftp:append_chunk_start(Pid, File++"1\r\nCWD ."),
- ok = ftp:append_chunk_start(Pid, File),
- {error, enotbinary} = ftp:append_chunk(Pid, lists:nth(1,Contents)),
- ok = ftp:append_chunk(Pid,list_to_binary(lists:nth(1,Contents))),
- ok = ftp:append_chunk(Pid,list_to_binary(lists:nth(2,Contents))),
- ok = ftp:append_chunk(Pid,list_to_binary(lists:nth(3,Contents))),
- ok = ftp:append_chunk_end(Pid),
- %%Control the contents of the file
- {ok, Bin2} = ftp:recv_bin(Pid, File),
- ok = check_content(binary_to_list(Bin2),"ERL", double),
- ok = ftp:delete(Pid, File),
- ok.
-
-do_recv(Pid, Config) ->
- PrivDir = ?config(priv_dir, Config),
- File = ?config(file, Config),
- Newfile = ?config(new_file, Config),
- AbsFile = filename:absname(File, PrivDir),
- Contents = "ftp_SUITE:recv test ...",
- ok = file:write_file(AbsFile, list_to_binary(Contents)),
- ok = ftp:cd(Pid, "incoming"),
- ftp:delete(Pid, File), % reset
- ftp:lcd(Pid, PrivDir),
- ok = ftp:send(Pid, File),
- ok = file:delete(AbsFile), % cleanup
- test_server:sleep(100),
- ok = ftp:lcd(Pid, PrivDir),
- {error, efnamena} = ftp:recv(Pid, File++"\r\nCWD ."),
- ok = ftp:recv(Pid, File),
- {ok, Files} = file:list_dir(PrivDir),
- true = lists:member(File, Files),
- ok = file:delete(AbsFile), % cleanup
- ok = ftp:recv(Pid, File, Newfile),
- ok = ftp:delete(Pid, File), % cleanup
- ok.
-
-do_recv_bin(Pid, Config) ->
- File = ?config(file, Config),
- Contents1 = "ftp_SUITE test ...",
- Bin1 = list_to_binary(Contents1),
- ok = ftp:cd(Pid, "incoming"),
- ok = ftp:send_bin(Pid, Bin1, File),
- test_server:sleep(100),
- {error, efnamena} = ftp:recv_bin(Pid, File++"\r\nCWD ."),
- {ok, Bin2} = ftp:recv_bin(Pid, File),
- ok = ftp:delete(Pid, File), % cleanup
- Contents2 = binary_to_list(Bin2),
- Contents1 = Contents2,
- ok.
-
-do_recv_chunk(Pid, Config) ->
- File = ?config(file, Config),
- Data = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
- "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
- "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"
- "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD"
- "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE"
- "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
- "GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG"
- "HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH"
- "IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII",
-
- Contents1 = lists:flatten(lists:duplicate(10, Data)),
- Bin1 = list_to_binary(Contents1),
- ok = ftp:cd(Pid, "incoming"),
- ok = ftp:type(Pid, binary),
- ok = ftp:send_bin(Pid, Bin1, File),
- test_server:sleep(100),
- {error, "ftp:recv_chunk_start/2 not called"} = recv_chunk(Pid, <<>>),
- {error, efnamena} = ftp:recv_chunk_start(Pid, File++"\r\nCWD ."),
- ok = ftp:recv_chunk_start(Pid, File),
- {ok, Contents2} = recv_chunk(Pid, <<>>),
- ok = ftp:delete(Pid, File), % cleanup
- ok = find_diff(Contents2, Contents1, 1),
- ok.
-
-do_type(Pid) ->
- ok = ftp:type(Pid, ascii),
- ok = ftp:type(Pid, binary),
- ok = ftp:type(Pid, ascii),
- {error, etype} = ftp:type(Pid, foobar),
- ok.
-
-do_quote(Pid) ->
- ["257 \"/\""++_Rest] = ftp:quote(Pid, "pwd"), %% 257
- [_| _] = ftp:quote(Pid, "help"),
- %% This negativ test causes some ftp servers to hang. This test
- %% is not important for the client, so we skip it for now.
- %%["425 Can't build data connection: Connection refused."]
- %% = ftp:quote(Pid, "list"),
- ok.
-
- watch_dog(Config) ->
- Dog = test_server:timetrap(inets_test_lib:minutes(1)),
- NewConfig = lists:keydelete(watchdog, 1, Config),
- [{watchdog, Dog} | NewConfig].
-
- close_connection(Config) ->
- case ?config(ftp, Config) of
- Pid when is_pid(Pid) ->
- ok = ftp:close(Pid),
- lists:delete({ftp, Pid}, Config);
- _ ->
- Config
- end.
-
-ftp_host(Config) ->
- case ?config(ftp_remote_host, Config) of
- undefined ->
- exit({skip, "No host specified"});
- Host ->
- Host
- end.
-
-check_content(RContent, LContent, Amount) ->
- LContent2 = case Amount of
- double ->
- LContent ++ LContent;
- singel ->
- LContent
- end,
- case string:equal(RContent, LContent2) of
- true ->
- ok;
- false ->
- %% Find where the diff is
- Where = find_diff(RContent, LContent2, 1),
- Where
- end.
-
-find_diff(A, A, _) ->
- ok;
-find_diff([H|T1], [H|T2], Pos) ->
- find_diff(T1, T2, Pos+1);
-find_diff(RC, LC, Pos) ->
- {error, {diff, Pos, RC, LC}}.
-
-recv_chunk(Pid, Acc) ->
- case ftp:recv_chunk(Pid) of
- ok ->
- {ok, binary_to_list(Acc)};
- {ok, Bin} ->
- recv_chunk(Pid, <<Acc/binary, Bin/binary>>);
- Error ->
- Error
- end.
-
-split(Cs) ->
- split(Cs, [], []).
-
-split([$\r, $\n| Cs], I, Is) ->
- split(Cs, [], [lists:reverse(I)| Is]);
-split([C| Cs], I, Is) ->
- split(Cs, [C| I], Is);
-split([], I, Is) ->
- lists:reverse([lists:reverse(I)| Is]).
-
-do_ftp_open(Host, Opts) ->
- p("do_ftp_open -> entry with"
- "~n Host: ~p"
- "~n Opts: ~p", [Host, Opts]),
- case ftp:open(Host, Opts) of
- {ok, _} = OK ->
- OK;
- {error, Reason} ->
- Str =
- lists:flatten(
- io_lib:format("Unable to reach test FTP server ~p (~p)",
- [Host, Reason])),
- throw({skip, Str})
- end.
-
-
-passwd() ->
- Host =
- case inet:gethostname() of
- {ok, H} ->
- H;
- _ ->
- "localhost"
- end,
- "ftp_SUITE@" ++ Host.
-
-ftpd_hosts(Config) ->
- DataDir = ?config(data_dir, Config),
- FileName = filename:join([DataDir, "../ftp_SUITE_data/", ftpd_hosts]),
- p("FileName: ~p", [FileName]),
- case file:consult(FileName) of
- {ok, [Hosts]} when is_list(Hosts) ->
- Hosts;
- _ ->
- []
- end.
-
-wrapper(Prefix,doc,Func) ->
- Prefix++Func(doc);
-wrapper(_,X,Func) ->
- Func(X).
-
-data_dir(Config) ->
- case ?config(data_dir, Config) of
- List when (length(List) > 0) ->
- PathList = filename:split(List),
- {NewPathList,_} = lists:split((length(PathList)-1), PathList),
- DataDir = filename:join(NewPathList ++ [ftp_SUITE_data]),
- NewConfig =
- lists:keyreplace(data_dir,1,Config, {data_dir,DataDir}),
- NewConfig;
- _ -> Config
- end.
-
-
-
-p(F) ->
- p(F, []).
-
-p(F, A) ->
- case get(ftp_testcase) of
- undefined ->
- io:format("~w [~w] " ++ F ++ "~n", [?MODULE, self() | A]);
- TC when is_atom(TC) ->
- io:format("~w [~w] ~w:" ++ F ++ "~n", [?MODULE, self(), TC | A])
- end.
diff --git a/lib/inets/test/http_format_SUITE.erl b/lib/inets/test/http_format_SUITE.erl
index e977bd1b9b..a2b463e98c 100644
--- a/lib/inets/test/http_format_SUITE.erl
+++ b/lib/inets/test/http_format_SUITE.erl
@@ -55,13 +55,9 @@ end_per_group(_GroupName, Config) ->
init_per_testcase(_, Config) ->
- Dog = test_server:timetrap(?t:minutes(1)),
- NewConfig = lists:keydelete(watchdog, 1, Config),
- [{watchdog, Dog} | NewConfig].
+ Config.
-end_per_testcase(_, Config) ->
- Dog = ?config(watchdog, Config),
- test_server:timetrap_cancel(Dog),
+end_per_testcase(_, _) ->
ok.
%%-------------------------------------------------------------------------
diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl
index e6c4e48feb..42772923e4 100644
--- a/lib/inets/test/httpc_SUITE.erl
+++ b/lib/inets/test/httpc_SUITE.erl
@@ -42,7 +42,8 @@
%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
suite() ->
- [{ct_hooks,[ts_install_cth]}].
+ [{ct_hooks,[ts_install_cth]}
+ ].
all() ->
[
@@ -137,8 +138,9 @@ misc() ->
%%--------------------------------------------------------------------
init_per_suite(Config) ->
- PrivDir = ?config(priv_dir, Config),
- DataDir = ?config(data_dir, Config),
+ ct:timetrap({seconds, 30}),
+ PrivDir = proplists:get_value(priv_dir, Config),
+ DataDir = proplists:get_value(data_dir, Config),
inets_test_lib:start_apps([inets]),
ServerRoot = filename:join(PrivDir, "server_root"),
DocRoot = filename:join(ServerRoot, "htdocs"),
@@ -147,7 +149,7 @@ init_per_suite(Config) ->
end_per_suite(Config) ->
inets_test_lib:stop_apps([inets]),
- PrivDir = ?config(priv_dir, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
inets_test_lib:del_dirs(PrivDir),
ok.
@@ -159,6 +161,7 @@ init_per_group(misc = Group, Config) ->
Config;
init_per_group(Group, Config0) when Group =:= sim_https; Group =:= https->
+ ct:timetrap({seconds, 30}),
start_apps(Group),
StartSsl = try ssl:start()
catch
@@ -199,7 +202,15 @@ init_per_testcase(persistent_connection, Config) ->
{max_keep_alive_length, 3}], persistent_connection),
Config;
-
+init_per_testcase(wait_for_whole_response, Config) ->
+ ct:timetrap({seconds, 60*3}),
+ Config;
+init_per_testcase(Case, Config) when Case == post;
+ Case == delete;
+ Case == post_delete;
+ Case == post_stream ->
+ ct:timetrap({seconds, 30}),
+ Config;
init_per_testcase(_Case, Config) ->
Config.
@@ -356,7 +367,7 @@ pipeline(Config) when is_list(Config) ->
{ok, _} = httpc:request(get, Request, [], [], pipeline),
%% Make sure pipeline session is registerd
- test_server:sleep(4000),
+ ct:sleep(4000),
keep_alive_requests(Request, pipeline).
%%--------------------------------------------------------------------
@@ -366,7 +377,7 @@ persistent_connection(Config) when is_list(Config) ->
{ok, _} = httpc:request(get, Request, [], [], persistent),
%% Make sure pipeline session is registerd
- test_server:sleep(4000),
+ ct:sleep(4000),
keep_alive_requests(Request, persistent).
%%-------------------------------------------------------------------------
@@ -394,7 +405,7 @@ async(Config) when is_list(Config) ->
save_to_file() ->
[{doc, "Test to save the http body to a file"}].
save_to_file(Config) when is_list(Config) ->
- PrivDir = ?config(priv_dir, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
FilePath = filename:join(PrivDir, "dummy.html"),
URL = url(group_name(Config), "/dummy.html", Config),
Request = {URL, []},
@@ -408,7 +419,7 @@ save_to_file(Config) when is_list(Config) ->
save_to_file_async() ->
[{doc,"Test to save the http body to a file"}].
save_to_file_async(Config) when is_list(Config) ->
- PrivDir = ?config(priv_dir, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
FilePath = filename:join(PrivDir, "dummy.html"),
URL = url(group_name(Config), "/dummy.html", Config),
Request = {URL, []},
@@ -868,7 +879,7 @@ headers() ->
headers(Config) when is_list(Config) ->
URL = url(group_name(Config), "/dummy.html", Config),
- DocRoot = ?config(doc_root, Config),
+ DocRoot = proplists:get_value(doc_root, Config),
{ok, FileInfo} =
file:read_file_info(filename:join([DocRoot,"dummy.html"])),
@@ -1212,11 +1223,11 @@ not_streamed_test(Request, To) ->
end.
url(http, End, Config) ->
- Port = ?config(port, Config),
+ Port = proplists:get_value(port, Config),
{ok,Host} = inet:gethostname(),
?URL_START ++ Host ++ ":" ++ integer_to_list(Port) ++ End;
url(https, End, Config) ->
- Port = ?config(port, Config),
+ Port = proplists:get_value(port, Config),
{ok,Host} = inet:gethostname(),
?TLS_URL_START ++ Host ++ ":" ++ integer_to_list(Port) ++ End;
url(sim_http, End, Config) ->
@@ -1224,10 +1235,10 @@ url(sim_http, End, Config) ->
url(sim_https, End, Config) ->
url(https, End, Config).
url(http, UserInfo, End, Config) ->
- Port = ?config(port, Config),
+ Port = proplists:get_value(port, Config),
?URL_START ++ UserInfo ++ integer_to_list(Port) ++ End;
url(https, UserInfo, End, Config) ->
- Port = ?config(port, Config),
+ Port = proplists:get_value(port, Config),
?TLS_URL_START ++ UserInfo ++ integer_to_list(Port) ++ End;
url(sim_http, UserInfo, End, Config) ->
url(http, UserInfo, End, Config);
@@ -1235,7 +1246,7 @@ url(sim_https, UserInfo, End, Config) ->
url(https, UserInfo, End, Config).
group_name(Config) ->
- GroupProp = ?config(tc_group_properties, Config),
+ GroupProp = proplists:get_value(tc_group_properties, Config),
proplists:get_value(name, GroupProp).
server_start(sim_http, _) ->
@@ -1257,11 +1268,11 @@ server_start(_, HttpdConfig) ->
proplists:get_value(port, Info).
server_config(http, Config) ->
- ServerRoot = ?config(server_root, Config),
+ ServerRoot = proplists:get_value(server_root, Config),
[{port, 0},
{server_name,"httpc_test"},
{server_root, ServerRoot},
- {document_root, ?config(doc_root, Config)},
+ {document_root, proplists:get_value(doc_root, Config)},
{bind_address, any},
{ipfamily, inet_version()},
{mime_type, "text/plain"},
@@ -1283,7 +1294,7 @@ start_apps(_) ->
ok.
ssl_config(Config) ->
- DataDir = ?config(data_dir, Config),
+ DataDir = proplists:get_value(data_dir, Config),
[{certfile, filename:join(DataDir, "ssl_server_cert.pem")},
{verify, verify_none}
].
@@ -1931,9 +1942,9 @@ handle_uri(_,"/once.html",_,_,Socket,_) ->
"Content-Length:32\r\n\r\n",
send(Socket, Head),
send(Socket, "<HTML><BODY>fo"),
- test_server:sleep(1000),
+ ct:sleep(1000),
send(Socket, "ob"),
- test_server:sleep(1000),
+ ct:sleep(1000),
send(Socket, "ar</BODY></HTML>");
handle_uri(_,"/invalid_http.html",_,_,_,_) ->
diff --git a/lib/inets/test/httpc_cookie_SUITE.erl b/lib/inets/test/httpc_cookie_SUITE.erl
index 0eca3edae2..8140967bca 100644
--- a/lib/inets/test/httpc_cookie_SUITE.erl
+++ b/lib/inets/test/httpc_cookie_SUITE.erl
@@ -23,14 +23,8 @@
-include_lib("common_test/include/ct.hrl").
-include_lib("stdlib/include/ms_transform.hrl").
-%% Test server specific exports
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, init_per_testcase/2, end_per_testcase/2]).
-
-%% Test cases must be exported.
--export([session_cookies_only/1, netscape_cookies/1,
- cookie_cancel/1, cookie_expires/1, persistent_cookie/1,
- domain_cookie/1, secure_cookie/1, update_cookie/1,
- update_cookie_session/1, cookie_attributes/1]).
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
-define(URL, "http://myhost.cookie.test.org").
-define(URL_DOMAIN, "http://myhost2.cookie.test.org").
@@ -38,86 +32,10 @@
%% Test server callback functions
-%%--------------------------------------------------------------------
-%% Function: init_per_testcase(TestCase, Config) -> Config
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%%
-%% Description: Initiation before each test case
-%%
-%% Note: This function is free to add any key/value pairs to the Config
-%% variable, but should NOT alter/remove any existing entries.
-%% Description: Initiation before each test case
-%%--------------------------------------------------------------------
-init_per_testcase(session_cookies_only = Case, Config0) ->
- tsp("init_per_testcase(~p) -> entry with"
- "~n Config0: ~p", [Case, Config0]),
- Config = init_workdir(Case, Config0),
- application:start(inets),
- httpc:set_options([{cookies, verify}]),
- watch_dog(Config);
-
-init_per_testcase(Case, Config0) ->
- tsp("init_per_testcase(~p) -> entry with"
- "~n Config0: ~p", [Case, Config0]),
- Config = init_workdir(Case, Config0),
- CaseDir = ?config(case_top_dir, Config),
- application:load(inets),
- application:set_env(inets, services, [{httpc, {default, CaseDir}}]),
- application:start(inets),
- httpc:set_options([{cookies, verify}]),
- watch_dog(Config).
-
-watch_dog(Config) ->
- Dog = test_server:timetrap(inets_test_lib:minutes(10)),
- NewConfig = lists:keydelete(watchdog, 1, Config),
- [{watchdog, Dog} | NewConfig].
-
-init_workdir(Case, Config) ->
- PrivDir = ?config(priv_dir, Config),
- SuiteTopDir = filename:join(PrivDir, ?MODULE),
- case file:make_dir(SuiteTopDir) of
- ok ->
- ok;
- {error, eexist} ->
- ok;
- Error ->
- tsf({failed_creating_subsuite_top_dir, Error})
- end,
-
- CaseTopDir = filename:join(SuiteTopDir, Case),
- ?line ok = file:make_dir(CaseTopDir),
- [{suite_top_dir, SuiteTopDir},
- {case_top_dir, CaseTopDir} | Config].
-
-
-%%--------------------------------------------------------------------
-%% Function: end_per_testcase(TestCase, Config) -> _
-%% Case - atom()
-%% Name of the test case that is about to be run.
-%% Config - [tuple()]
-%% A list of key/value pairs, holding the test case configuration.
-%% Description: Cleanup after each test case
-%%--------------------------------------------------------------------
-end_per_testcase(Case, Config) ->
- tsp("end_per_testcase(~p) -> entry with"
- "~n Config: ~p", [Case, Config]),
- application:stop(inets),
- Dog = ?config(watchdog, Config),
- test_server:timetrap_cancel(Dog),
- ok.
-
-%%--------------------------------------------------------------------
-%% Function: all(Clause) -> TestCases
-%% Clause - atom() - suite | doc
-%% TestCases - [Case]
-%% Case - atom()
-%% Name of a test case.
-%% Description: Returns a list of all test cases in this test suite
-%%--------------------------------------------------------------------
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap,{seconds,15}}
+ ].
all() ->
[
@@ -148,17 +66,55 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
+init_per_testcase(session_cookies_only = Case, Config0) ->
+ Config = init_workdir(Case, Config0),
+ application:start(inets),
+ httpc:set_options([{cookies, verify}]),
+ Config;
+init_per_testcase(cookie_expires = Case, Config0) ->
+ Config = init_workdir(Case, Config0),
+ CaseDir = proplists:get_value(case_top_dir, Config),
+ application:start(inets),
+ application:set_env(inets, services, [{httpc, {default, CaseDir}}]),
+ application:start(inets),
+ httpc:set_options([{cookies, verify}]),
+ Config;
+init_per_testcase(Case, Config0) ->
+ Config = init_workdir(Case, Config0),
+ CaseDir = proplists:get_value(case_top_dir, Config),
+ application:load(inets),
+ application:set_env(inets, services, [{httpc, {default, CaseDir}}]),
+ application:start(inets),
+ httpc:set_options([{cookies, verify}]),
+ Config.
+
+init_workdir(Case, Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ SuiteTopDir = filename:join(PrivDir, ?MODULE),
+ case file:make_dir(SuiteTopDir) of
+ ok ->
+ ok;
+ {error, eexist} ->
+ ok;
+ Error ->
+ ct:fail({failed_creating_subsuite_top_dir, Error})
+ end,
+
+ CaseTopDir = filename:join(SuiteTopDir, Case),
+ ok = file:make_dir(CaseTopDir),
+ [{suite_top_dir, SuiteTopDir},
+ {case_top_dir, CaseTopDir} | Config].
+
+end_per_testcase(_, _) ->
+ application:stop(inets).
+
%% Test cases starts here.
%%--------------------------------------------------------------------
-session_cookies_only(doc) ->
- ["Test that all cookies are handled as session cookies if there"
- "does not exist a directory to save presitent cookies in."];
-session_cookies_only(suite) ->
- [];
+session_cookies_only() ->
+ [{doc, "Test that all cookies are handled as session cookies if there"
+ "does not exist a directory to save presitent cookies in."}].
session_cookies_only(Config) when is_list(Config) ->
- tsp("session_cookies_only -> Cookies 1: ~p", [httpc:which_cookies()]),
-
SetCookieHeaders = [{"set-cookie", "test_cookie=true; path=/;"
";max-age=60000"}],
httpc:store_cookies(SetCookieHeaders, ?URL),
@@ -166,36 +122,22 @@ session_cookies_only(Config) when is_list(Config) ->
httpc:cookie_header(?URL),
application:stop(inets),
application:start(inets),
- {"cookie", ""} = httpc:cookie_header(?URL),
-
- tsp("session_cookies_only -> Cookies 2: ~p", [httpc:which_cookies()]),
- ok.
+ {"cookie", ""} = httpc:cookie_header(?URL).
-netscape_cookies(doc) ->
- ["Test that the old (original) format of cookies are accepted."];
-netscape_cookies(suite) ->
- [];
+netscape_cookies() ->
+ [{doc, "Test that the old (original) format of cookies are accepted."}].
netscape_cookies(Config) when is_list(Config) ->
- tsp("netscape_cookies -> Cookies 1: ~p", [httpc:which_cookies()]),
-
Expires = future_netscape_date(),
SetCookieHeaders = [{"set-cookie", "test_cookie=true; path=/; "
"expires=" ++ Expires}],
httpc:store_cookies(SetCookieHeaders, ?URL),
{"cookie", "$Version=0; test_cookie=true; $Path=/"} =
- httpc:cookie_header(?URL),
+ httpc:cookie_header(?URL).
- tsp("netscape_cookies -> Cookies 2: ~p", [httpc:which_cookies()]),
- ok.
-
-cookie_cancel(doc) ->
- ["A cookie can be canceld by sending the same cookie with max-age=0 "
- "this test cheks that cookie is canceled."];
-cookie_cancel(suite) ->
- [];
+cookie_cancel() ->
+ [{doc, "A cookie can be canceld by sending the same cookie with max-age=0 "
+ "this test cheks that cookie is canceled."}].
cookie_cancel(Config) when is_list(Config) ->
- tsp("cookie_cancel -> Cookies 1: ~p", [httpc:which_cookies()]),
-
SetCookieHeaders = [{"set-cookie", "test_cookie=true; path=/;"
"max-age=60000"}],
httpc:store_cookies(SetCookieHeaders, ?URL),
@@ -204,155 +146,60 @@ cookie_cancel(Config) when is_list(Config) ->
NewSetCookieHeaders =
[{"set-cookie", "test_cookie=true; path=/;max-age=0"}],
httpc:store_cookies(NewSetCookieHeaders, ?URL),
- {"cookie", ""} = httpc:cookie_header(?URL),
+ {"cookie", ""} = httpc:cookie_header(?URL).
- tsp("cookie_cancel -> Cookies 2: ~p", [httpc:which_cookies()]),
- ok.
-
-cookie_expires(doc) ->
- ["Test that a cookie is not used when it has expired"];
-cookie_expires(suite) ->
- [];
+cookie_expires() ->
+ [{doc, "Test that a cookie is not used when it has expired"}].
cookie_expires(Config) when is_list(Config) ->
- tsp("cookie_expires -> Cookies 1: ~p", [httpc:which_cookies()]),
-
SetCookieHeaders = [{"set-cookie", "test_cookie=true; path=/;"
"max-age=5"}],
httpc:store_cookies(SetCookieHeaders, ?URL),
{"cookie", "$Version=0; test_cookie=true; $Path=/"} =
httpc:cookie_header(?URL),
- test_server:sleep(10000),
- {"cookie", ""} = httpc:cookie_header(?URL),
+ timer:sleep(10000),
+ {"cookie", ""} = httpc:cookie_header(?URL).
- tsp("cookie_expires -> Cookies 2: ~p", [httpc:which_cookies()]),
- ok.
-
-persistent_cookie(doc) ->
- ["Test domian cookie attribute"];
-persistent_cookie(suite) ->
- [];
+persistent_cookie() ->
+ [{doc, "Test domian cookie attribute"}].
persistent_cookie(Config) when is_list(Config)->
- tsp("persistent_cookie -> Cookies 1: ~p", [httpc:which_cookies()]),
-
SetCookieHeaders = [{"set-cookie", "test_cookie=true; path=/;"
"max-age=60000"}],
httpc:store_cookies(SetCookieHeaders, ?URL),
{"cookie", "$Version=0; test_cookie=true; $Path=/"} =
httpc:cookie_header(?URL),
- CaseDir = ?config(case_top_dir, Config),
+ CaseDir = proplists:get_value(case_top_dir, Config),
application:stop(inets),
application:load(inets),
application:set_env(inets, services, [{httpc, {default, CaseDir}}]),
application:start(inets),
httpc:set_options([{cookies, enabled}]),
- {"cookie","$Version=0; test_cookie=true; $Path=/"} = httpc:cookie_header(?URL),
+ {"cookie","$Version=0; test_cookie=true; $Path=/"} = httpc:cookie_header(?URL).
- tsp("persistent_cookie -> Cookies 2: ~p", [httpc:which_cookies()]),
- ok.
-
-
-domain_cookie(doc) ->
- ["Test the domian cookie attribute"];
-domain_cookie(suite) ->
- [];
+domain_cookie() ->
+ [{doc, "Test the domian cookie attribute"}].
domain_cookie(Config) when is_list(Config) ->
- tsp("domain_cookie -> Cookies 1: ~p", [httpc:which_cookies()]),
-
SetCookieHeaders = [{"set-cookie", "test_cookie=true; path=/;"
"domain=.cookie.test.org"}],
httpc:store_cookies(SetCookieHeaders, ?URL),
{"cookie","$Version=0; test_cookie=true; $Path=/; "
"$Domain=.cookie.test.org"} =
- httpc:cookie_header(?URL_DOMAIN),
+ httpc:cookie_header(?URL_DOMAIN).
- tsp("domain_cookie -> Cookies 2: ~p", [httpc:which_cookies()]),
- ok.
-
-
-secure_cookie(doc) ->
- ["Test the secure cookie attribute"];
-secure_cookie(suite) ->
- [];
+secure_cookie() ->
+ [{doc, "Test the secure cookie attribute"}].
secure_cookie(Config) when is_list(Config) ->
- tsp("secure_cookie -> entry with"
- "~n Config: ~p", [Config]),
-
- %% httpc:reset_cookies(),
-
- tsp("secure_cookie -> Cookies 1: ~p", [httpc:which_cookies()]),
-
SetCookieHeaders = [{"set-cookie", "test_cookie=true; path=/; secure"}],
- tsp("secure_cookie -> store cookies (1)"),
ok = httpc:store_cookies(SetCookieHeaders, ?URL),
-
- tsp("secure_cookie -> Cookies 2: ~p", [httpc:which_cookies()]),
-
- tsp("secure_cookie -> check cookie (secure)"),
check_cookie("$Version=0; test_cookie=true; $Path=/", ?URL_SECURE),
-
- tsp("secure_cookie -> check cookie (plain)"),
check_cookie("", ?URL),
-
- tsp("secure_cookie -> store cookies (2)"),
SetCookieHeaders1 = [{"set-cookie", "test1_cookie=true; path=/; secure"}],
ok = httpc:store_cookies(SetCookieHeaders1, ?URL),
-
- tsp("secure_cookie -> Cookies 3: ~p", [httpc:which_cookies()]),
-
- tsp("secure_cookie -> cookie header (3)"),
check_cookie("$Version=0; test_cookie=true; $Path=/; "
"test1_cookie=true; $Path=/",
- ?URL_SECURE),
-%% {"cookie","$Version=0; test_cookie=true; $Path=/; "
-%% "test1_cookie=true; $Path=/"} = httpc:cookie_header(?URL_SECURE),
-
- tsp("secure_cookie -> Cookies 4: ~p", [httpc:which_cookies()]),
-
- tsp("secure_cookie -> done"),
- ok.
+ ?URL_SECURE).
-expect_cookie_header(No, ExpectedCookie) ->
- case httpc:cookie_header(?URL) of
- {"cookie", ExpectedCookie} ->
- ok;
- {"cookie", BadCookie} ->
- io:format("Bad Cookie ~w: "
- "~n Expected: ~s"
- "~n Received: ~s"
- "~n", [No, ExpectedCookie, BadCookie]),
- exit({bad_cookie_header, No, ExpectedCookie, BadCookie})
- end.
-
-print_cookies(Pre) ->
- io:format("~s: ", [Pre]),
- print_cookies2(httpc:which_cookies()).
-
-print_cookies2([]) ->
- ok;
-print_cookies2([{cookies, Cookies}|Rest]) ->
- print_cookies3("Cookies", Cookies),
- print_cookies2(Rest);
-print_cookies2([{session_cookies, Cookies}|Rest]) ->
- print_cookies3("Session Cookies", Cookies),
- print_cookies2(Rest);
-print_cookies2([_|Rest]) ->
- print_cookies2(Rest).
-
-print_cookies3(Header, []) ->
- io:format(" ~s: []", [Header]);
-print_cookies3(Header, Cookies) ->
- io:format(" ~s: ", [Header]),
- Prefix = " ",
- PrintCookie =
- fun(Cookie) ->
- io:format("~s", [httpc_cookie:image_of(Prefix, Cookie)])
- end,
- lists:foreach(PrintCookie, Cookies).
-
-update_cookie(doc)->
- ["Test that a (plain) cookie can be updated."];
-update_cookie(suite) ->
- [];
+update_cookie()->
+ [{doc, "Test that a (plain) cookie can be updated."}].
update_cookie(Config) when is_list(Config) ->
print_cookies("Cookies before store"),
@@ -377,10 +224,8 @@ update_cookie(Config) when is_list(Config) ->
"test_cookie=false; $Path=/",
expect_cookie_header(2, ExpectCookie2).
-update_cookie_session(doc)->
- ["Test that a session cookie can be updated."];
-update_cookie_session(suite) ->
- [];
+update_cookie_session()->
+ [{doc, "Test that a session cookie can be updated."}].
update_cookie_session(Config) when is_list(Config)->
print_cookies("Cookies before store"),
@@ -400,23 +245,57 @@ update_cookie_session(Config) when is_list(Config)->
expect_cookie_header(2, ExpectedCookie2).
-cookie_attributes(doc) ->
- ["Test attribute not covered by the other test cases"];
-cookie_attributes(suite) ->
- [];
+cookie_attributes() ->
+ [{doc, "Test attribute not covered by the other test cases"}].
cookie_attributes(Config) when is_list(Config) ->
SetCookieHeaders = [{"set-cookie", "test_cookie=true;version=1;"
"comment=foobar; "%% Comment
"foo=bar;" %% Nonsense should be ignored
"max-age=60000"}],
httpc:store_cookies(SetCookieHeaders, ?URL),
- {"cookie","$Version=1; test_cookie=true"} = httpc:cookie_header(?URL),
- ok.
+ {"cookie","$Version=1; test_cookie=true"} = httpc:cookie_header(?URL).
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
+print_cookies(Pre) ->
+ io:format("~s: ", [Pre]),
+ print_cookies2(httpc:which_cookies()).
+
+print_cookies2([]) ->
+ ok;
+print_cookies2([{cookies, Cookies}|Rest]) ->
+ print_cookies3("Cookies", Cookies),
+ print_cookies2(Rest);
+print_cookies2([{session_cookies, Cookies}|Rest]) ->
+ print_cookies3("Session Cookies", Cookies),
+ print_cookies2(Rest);
+print_cookies2([_|Rest]) ->
+ print_cookies2(Rest).
+
+print_cookies3(Header, []) ->
+ io:format(" ~s: []", [Header]);
+print_cookies3(Header, Cookies) ->
+ io:format(" ~s: ", [Header]),
+ Prefix = " ",
+ PrintCookie =
+ fun(Cookie) ->
+ io:format("~s", [httpc_cookie:image_of(Prefix, Cookie)])
+ end,
+ lists:foreach(PrintCookie, Cookies).
+
+expect_cookie_header(No, ExpectedCookie) ->
+ case httpc:cookie_header(?URL) of
+ {"cookie", ExpectedCookie} ->
+ ok;
+ {"cookie", BadCookie} ->
+ io:format("Bad Cookie ~w: "
+ "~n Expected: ~s"
+ "~n Received: ~s"
+ "~n", [No, ExpectedCookie, BadCookie]),
+ exit({bad_cookie_header, No, ExpectedCookie, BadCookie})
+ end.
check_cookie(Expect, URL) ->
case httpc:cookie_header(URL) of
@@ -426,12 +305,12 @@ check_cookie(Expect, URL) ->
case lists:prefix(Expect, Unexpected) of
true ->
Extra = Unexpected -- Expect,
- tsf({extra_cookie_info, Extra});
+ ct:fail({extra_cookie_info, Extra});
false ->
- tsf({unknown_cookie, Expect, Unexpected})
+ ct:fail({unknown_cookie, Expect, Unexpected})
end;
Bad ->
- tsf({bad_cookies, Bad})
+ ct:fail({bad_cookies, Bad})
end.
@@ -509,11 +388,3 @@ month_str(11) ->"Nov";
month_str(12) ->"Dec".
-tsp(F) ->
- tsp(F, []).
-tsp(F, A) ->
- test_server:format("~p ~p:" ++ F ++ "~n", [self(), ?MODULE | A]).
-
-tsf(Reason) ->
- test_server:fail(Reason).
-
diff --git a/lib/inets/test/httpc_proxy_SUITE.erl b/lib/inets/test/httpc_proxy_SUITE.erl
index 2f6f0bc26f..198b245399 100644
--- a/lib/inets/test/httpc_proxy_SUITE.erl
+++ b/lib/inets/test/httpc_proxy_SUITE.erl
@@ -141,7 +141,7 @@ end_per_testcase(_Case, Config) ->
%% internal functions
apps(_Case, Config) ->
- case ?config(protocol, Config) of
+ case proplists:get_value(protocol, Config) of
https ->
[ssl];
_ ->
@@ -438,7 +438,7 @@ header_value(Name, [{HeaderName,HeaderValue}|Headers]) ->
https_connect_error(doc) ->
["Error from CONNECT tunnel should be returned"];
https_connect_error(Config) when is_list(Config) ->
- {HttpServer,HttpPort} = ?config(http, Config),
+ {HttpServer,HttpPort} = proplists:get_value(http, Config),
Method = get,
%% using HTTPS scheme with HTTP port to trigger connection error
URL = "https://" ++ HttpServer ++ ":" ++
@@ -477,7 +477,7 @@ app_start(App, Config) ->
inets ->
application:stop(App),
ok = application:start(App),
- case ?config(proxy, Config) of
+ case proplists:get_value(proxy, Config) of
undefined -> ok;
{_,ProxySpec} ->
ok = httpc:set_options([{proxy,ProxySpec}])
@@ -495,7 +495,7 @@ app_stop(App) ->
application:stop(App).
make_cert_files(Alg, Prefix, Config) ->
- PrivDir = ?config(priv_dir, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
CaInfo = {CaCert,_} = erl_make_certs:make_cert([{key,Alg}]),
{Cert,CertKey} = erl_make_certs:make_cert([{key,Alg},{issuer,CaInfo}]),
CaCertFile = filename:join(PrivDir, Prefix++"cacerts.pem"),
@@ -513,8 +513,8 @@ der_to_pem(File, Entries) ->
url(AbsPath, Config) ->
- Protocol = ?config(protocol, Config),
- {ServerName,ServerPort} = ?config(Protocol, Config),
+ Protocol = proplists:get_value(protocol, Config),
+ {ServerName,ServerPort} = proplists:get_value(Protocol, Config),
atom_to_list(Protocol) ++ "://" ++
ServerName ++ ":" ++ integer_to_list(ServerPort) ++
AbsPath.
@@ -548,8 +548,8 @@ init_local_proxy_string(String, Config) ->
|Config].
rcmd_local_proxy(Args, Config) ->
- DataDir = ?config(data_dir, Config),
- PrivDir = ?config(priv_dir, Config),
+ DataDir = proplists:get_value(data_dir, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
Script = filename:join(DataDir, ?LOCAL_PROXY_SCRIPT),
rcmd(Script, Args, [{cd,PrivDir}]).
diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl
index 93520c1cb4..f5167116ce 100644
--- a/lib/inets/test/httpd_SUITE.erl
+++ b/lib/inets/test/httpd_SUITE.erl
@@ -46,7 +46,9 @@
%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
suite() ->
- [{ct_hooks,[ts_install_cth]}].
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap,{seconds, 120}}
+ ].
all() ->
[
@@ -149,8 +151,8 @@ load() ->
].
init_per_suite(Config) ->
- PrivDir = ?config(priv_dir, Config),
- DataDir = ?config(data_dir, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
+ DataDir = proplists:get_value(data_dir, Config),
inets_test_lib:stop_apps([inets]),
ServerRoot = filename:join(PrivDir, "server_root"),
inets_test_lib:del_dirs(ServerRoot),
@@ -218,22 +220,22 @@ init_per_group(http_0_9, Config) ->
[{http_version, "HTTP/0.9"} | Config]
end;
init_per_group(http_htaccess = Group, Config) ->
- Path = ?config(doc_root, Config),
+ Path = proplists:get_value(doc_root, Config),
catch remove_htaccess(Path),
- create_htaccess_data(Path, ?config(address, Config)),
+ create_htaccess_data(Path, proplists:get_value(address, Config)),
ok = start_apps(Group),
init_httpd(Group, [{type, ip_comm} | Config]);
init_per_group(https_htaccess = Group, Config) ->
- Path = ?config(doc_root, Config),
+ Path = proplists:get_value(doc_root, Config),
catch remove_htaccess(Path),
- create_htaccess_data(Path, ?config(address, Config)),
+ create_htaccess_data(Path, proplists:get_value(address, Config)),
init_ssl(Group, Config);
init_per_group(auth_api, Config) ->
[{auth_prefix, ""} | Config];
init_per_group(auth_api_dets, Config) ->
[{auth_prefix, "dets_"} | Config];
init_per_group(auth_api_mnesia, Config) ->
- start_mnesia(?config(node, Config)),
+ start_mnesia(proplists:get_value(node, Config)),
[{auth_prefix, "mnesia_"} | Config];
init_per_group(_, Config) ->
Config.
@@ -271,7 +273,8 @@ end_per_group(_, _) ->
%%--------------------------------------------------------------------
init_per_testcase(Case, Config) when Case == host; Case == trace ->
- Prop = ?config(tc_group_properties, Config),
+ ct:timetrap({seconds, 20}),
+ Prop = proplists:get_value(tc_group_properties, Config),
Name = proplists:get_value(name, Prop),
Cb = case Name of
http_1_0 ->
@@ -282,11 +285,13 @@ init_per_testcase(Case, Config) when Case == host; Case == trace ->
[{version_cb, Cb} | proplists:delete(version_cb, Config)];
init_per_testcase(range, Config) ->
- DocRoot = ?config(doc_root, Config),
+ ct:timetrap({seconds, 20}),
+ DocRoot = proplists:get_value(doc_root, Config),
create_range_data(DocRoot),
Config;
init_per_testcase(_, Config) ->
+ ct:timetrap({seconds, 20}),
Config.
end_per_testcase(_Case, _Config) ->
@@ -300,10 +305,10 @@ head() ->
[{doc, "HTTP HEAD request for static page"}].
head(Config) when is_list(Config) ->
- Version = ?config(http_version, Config),
- Host = ?config(host, Config),
- ok = httpd_test_lib:verify_request(?config(type, Config), Host,
- ?config(port, Config), ?config(node, Config),
+ Version = proplists:get_value(http_version, Config),
+ Host = proplists:get_value(host, Config),
+ ok = httpd_test_lib:verify_request(proplists:get_value(type, Config), Host,
+ proplists:get_value(port, Config), proplists:get_value(node, Config),
http_request("HEAD /index.html ", Version, Host),
[{statuscode, head_status(Version)},
{version, Version}]).
@@ -312,13 +317,13 @@ get() ->
[{doc, "HTTP GET request for static page"}].
get(Config) when is_list(Config) ->
- Version = ?config(http_version, Config),
- Host = ?config(host, Config),
- Type = ?config(type, Config),
- ok = httpd_test_lib:verify_request(?config(type, Config), Host,
- ?config(port, Config),
+ Version = proplists:get_value(http_version, Config),
+ Host = proplists:get_value(host, Config),
+ Type = proplists:get_value(type, Config),
+ ok = httpd_test_lib:verify_request(proplists:get_value(type, Config), Host,
+ proplists:get_value(port, Config),
transport_opts(Type, Config),
- ?config(node, Config),
+ proplists:get_value(node, Config),
http_request("GET /index.html ", Version, Host),
[{statuscode, 200},
{header, "Content-Type", "text/html"},
@@ -339,8 +344,8 @@ basic_auth() ->
[{doc, "Test Basic authentication with WWW-Authenticate header"}].
basic_auth(Config) ->
- Version = ?config(http_version, Config),
- Host = ?config(host, Config),
+ Version = proplists:get_value(http_version, Config),
+ Host = proplists:get_value(host, Config),
basic_auth_requiered(Config),
%% Authentication OK! ["one:OnePassword" user first in user list]
ok = auth_status(auth_request("/open/dummy.html", "one", "onePassword", Version, Host), Config,
@@ -380,15 +385,15 @@ auth_api() ->
[{doc, "Test mod_auth API"}].
auth_api(Config) when is_list(Config) ->
- Prefix = ?config(auth_prefix, Config),
+ Prefix = proplists:get_value(auth_prefix, Config),
do_auth_api(Prefix, Config).
do_auth_api(AuthPrefix, Config) ->
- Version = ?config(http_version, Config),
- Host = ?config(host, Config),
- Port = ?config(port, Config),
- Node = ?config(node, Config),
- ServerRoot = ?config(server_root, Config),
+ Version = proplists:get_value(http_version, Config),
+ Host = proplists:get_value(host, Config),
+ Port = proplists:get_value(port, Config),
+ Node = proplists:get_value(node, Config),
+ ServerRoot = proplists:get_value(server_root, Config),
ok = http_status("GET / ", Config,
[{statuscode, 200}]),
ok = auth_status(auth_request("/", "one", "WrongPassword", Version, Host), Config,
@@ -550,12 +555,12 @@ ipv6(Config) when is_list(Config) ->
{ok, Hostname0} = inet:gethostname(),
case lists:member(list_to_atom(Hostname0), ct:get_config(ipv6_hosts)) of
true ->
- Version = ?config(http_version, Config),
- Host = ?config(host, Config),
+ Version = proplists:get_value(http_version, Config),
+ Host = proplists:get_value(host, Config),
URI = http_request("GET / ", Version, Host),
- httpd_test_lib:verify_request(?config(type, Config), Host,
- ?config(port, Config), [inet6],
- ?config(code, Config),
+ httpd_test_lib:verify_request(proplists:get_value(type, Config), Host,
+ proplists:get_value(port, Config), [inet6],
+ proplists:get_value(code, Config),
URI,
[{statuscode, 200}, {version, Version}]);
false ->
@@ -576,11 +581,11 @@ htaccess() ->
[{doc, "Test mod_auth API"}].
htaccess(Config) when is_list(Config) ->
- Version = ?config(http_version, Config),
- Host = ?config(host, Config),
- Type = ?config(type, Config),
- Port = ?config(port, Config),
- Node = ?config(node, Config),
+ Version = proplists:get_value(http_version, Config),
+ Host = proplists:get_value(host, Config),
+ Type = proplists:get_value(type, Config),
+ Port = proplists:get_value(port, Config),
+ Node = proplists:get_value(node, Config),
%% Control that authentication required!
%% Control that the pages that shall be
%% authenticated really need authenticatin
@@ -691,23 +696,23 @@ host() ->
[{doc, "Test host header"}].
host(Config) when is_list(Config) ->
- Cb = ?config(version_cb, Config),
- Cb:host(?config(type, Config), ?config(port, Config),
- ?config(host, Config), ?config(node, Config)).
+ Cb = proplists:get_value(version_cb, Config),
+ Cb:host(proplists:get_value(type, Config), proplists:get_value(port, Config),
+ proplists:get_value(host, Config), proplists:get_value(node, Config)).
%%-------------------------------------------------------------------------
chunked() ->
[{doc, "Check that the server accepts chunked requests."}].
chunked(Config) when is_list(Config) ->
- httpd_1_1:chunked(?config(type, Config), ?config(port, Config),
- ?config(host, Config), ?config(node, Config)).
+ httpd_1_1:chunked(proplists:get_value(type, Config), proplists:get_value(port, Config),
+ proplists:get_value(host, Config), proplists:get_value(node, Config)).
%%-------------------------------------------------------------------------
expect() ->
["Check that the server handles request with the expect header "
"field appropiate"].
expect(Config) when is_list(Config) ->
- httpd_1_1:expect(?config(type, Config), ?config(port, Config),
- ?config(host, Config), ?config(node, Config)).
+ httpd_1_1:expect(proplists:get_value(type, Config), proplists:get_value(port, Config),
+ proplists:get_value(host, Config), proplists:get_value(node, Config)).
%%-------------------------------------------------------------------------
max_clients_1_1() ->
[{doc, "Test max clients limit"}].
@@ -762,10 +767,10 @@ esi(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
mod_esi_chunk_timeout(Config) when is_list(Config) ->
- ok = httpd_1_1:mod_esi_chunk_timeout(?config(type, Config),
- ?config(port, Config),
- ?config(host, Config),
- ?config(node, Config)).
+ ok = httpd_1_1:mod_esi_chunk_timeout(proplists:get_value(type, Config),
+ proplists:get_value(port, Config),
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)).
%%-------------------------------------------------------------------------
cgi() ->
@@ -846,7 +851,7 @@ cgi(Config) when is_list(Config) ->
cgi_chunked_encoding_test() ->
[{doc, "Test chunked encoding together with mod_cgi "}].
cgi_chunked_encoding_test(Config) when is_list(Config) ->
- Host = ?config(host, Config),
+ Host = proplists:get_value(host, Config),
Script =
case test_server:os_type() of
{win32, _} ->
@@ -858,9 +863,9 @@ cgi_chunked_encoding_test(Config) when is_list(Config) ->
["GET " ++ Script ++ " HTTP/1.1\r\nHost:"++ Host ++"\r\n\r\n",
"GET /cgi-bin/erl/httpd_example/newformat HTTP/1.1\r\nHost:"
++ Host ++"\r\n\r\n"],
- httpd_1_1:mod_cgi_chunked_encoding_test(?config(type, Config), ?config(port, Config),
+ httpd_1_1:mod_cgi_chunked_encoding_test(proplists:get_value(type, Config), proplists:get_value(port, Config),
Host,
- ?config(node, Config),
+ proplists:get_value(node, Config),
Requests).
%%-------------------------------------------------------------------------
alias_1_1() ->
@@ -920,52 +925,52 @@ range() ->
[{doc, "Test Range header"}].
range(Config) when is_list(Config) ->
- httpd_1_1:range(?config(type, Config), ?config(port, Config),
- ?config(host, Config), ?config(node, Config)).
+ httpd_1_1:range(proplists:get_value(type, Config), proplists:get_value(port, Config),
+ proplists:get_value(host, Config), proplists:get_value(node, Config)).
%%-------------------------------------------------------------------------
if_modified_since() ->
[{doc, "Test If-Modified-Since header"}].
if_modified_since(Config) when is_list(Config) ->
- httpd_1_1:if_test(?config(type, Config), ?config(port, Config),
- ?config(host, Config), ?config(node, Config),
- ?config(doc_root, Config)).
+ httpd_1_1:if_test(proplists:get_value(type, Config), proplists:get_value(port, Config),
+ proplists:get_value(host, Config), proplists:get_value(node, Config),
+ proplists:get_value(doc_root, Config)).
%%-------------------------------------------------------------------------
trace() ->
[{doc, "Test TRACE method"}].
trace(Config) when is_list(Config) ->
- Cb = ?config(version_cb, Config),
- Cb:trace(?config(type, Config), ?config(port, Config),
- ?config(host, Config), ?config(node, Config)).
+ Cb = proplists:get_value(version_cb, Config),
+ Cb:trace(proplists:get_value(type, Config), proplists:get_value(port, Config),
+ proplists:get_value(host, Config), proplists:get_value(node, Config)).
%%-------------------------------------------------------------------------
light() ->
["Test light load"].
light(Config) when is_list(Config) ->
- httpd_load:load_test(?config(type, Config), ?config(port, Config), ?config(host, Config),
- ?config(node, Config), 10).
+ httpd_load:load_test(proplists:get_value(type, Config), proplists:get_value(port, Config), proplists:get_value(host, Config),
+ proplists:get_value(node, Config), 10).
%%-------------------------------------------------------------------------
medium() ->
["Test medium load"].
medium(Config) when is_list(Config) ->
- httpd_load:load_test(?config(type, Config), ?config(port, Config), ?config(host, Config),
- ?config(node, Config), 100).
+ httpd_load:load_test(proplists:get_value(type, Config), proplists:get_value(port, Config), proplists:get_value(host, Config),
+ proplists:get_value(node, Config), 100).
%%-------------------------------------------------------------------------
heavy() ->
["Test heavy load"].
heavy(Config) when is_list(Config) ->
- httpd_load:load_test(?config(type, Config), ?config(port, Config), ?config(host, Config),
- ?config(node, Config),
+ httpd_load:load_test(proplists:get_value(type, Config), proplists:get_value(port, Config), proplists:get_value(host, Config),
+ proplists:get_value(node, Config),
1000).
%%-------------------------------------------------------------------------
content_length() ->
["Tests that content-length is correct OTP-5775"].
content_length(Config) ->
- Version = ?config(http_version, Config),
- Host = ?config(host, Config),
- ok = httpd_test_lib:verify_request(?config(type, Config), Host,
- ?config(port, Config), ?config(node, Config),
+ Version = proplists:get_value(http_version, Config),
+ Host = proplists:get_value(host, Config),
+ ok = httpd_test_lib:verify_request(proplists:get_value(type, Config), Host,
+ proplists:get_value(port, Config), proplists:get_value(node, Config),
http_request("GET /cgi-bin/erl/httpd_example:get_bin ",
Version, Host),
[{statuscode, 200},
@@ -975,10 +980,10 @@ content_length(Config) ->
bad_hex() ->
["Tests that a URI with a bad hexadecimal code is handled OTP-6003"].
bad_hex(Config) ->
- Version = ?config(http_version, Config),
- Host = ?config(host, Config),
- ok = httpd_test_lib:verify_request(?config(type, Config), Host,
- ?config(port, Config), ?config(node, Config),
+ Version = proplists:get_value(http_version, Config),
+ Host = proplists:get_value(host, Config),
+ ok = httpd_test_lib:verify_request(proplists:get_value(type, Config), Host,
+ proplists:get_value(port, Config), proplists:get_value(node, Config),
http_request("GET http://www.erlang.org/%skalle ",
Version, Host),
[{statuscode, 400},
@@ -987,10 +992,10 @@ bad_hex(Config) ->
missing_CR() ->
["Tests missing CR in delimiter OTP-7304"].
missing_CR(Config) ->
- Version = ?config(http_version, Config),
- Host = ?config(host, Config),
- ok = httpd_test_lib:verify_request(?config(type, Config), Host,
- ?config(port, Config), ?config(node, Config),
+ Version = proplists:get_value(http_version, Config),
+ Host = proplists:get_value(host, Config),
+ ok = httpd_test_lib:verify_request(proplists:get_value(type, Config), Host,
+ proplists:get_value(port, Config), proplists:get_value(node, Config),
http_request_missing_CR("GET /index.html ", Version, Host),
[{statuscode, 200},
{version, Version}]).
@@ -1001,12 +1006,12 @@ customize() ->
customize(Config) when is_list(Config) ->
Version = "HTTP/1.1",
- Host = ?config(host, Config),
- Type = ?config(type, Config),
- ok = httpd_test_lib:verify_request(?config(type, Config), Host,
- ?config(port, Config),
+ Host = proplists:get_value(host, Config),
+ Type = proplists:get_value(type, Config),
+ ok = httpd_test_lib:verify_request(proplists:get_value(type, Config), Host,
+ proplists:get_value(port, Config),
transport_opts(Type, Config),
- ?config(node, Config),
+ proplists:get_value(node, Config),
http_request("GET /index.html ", Version, Host),
[{statuscode, 200},
{header, "Content-Type", "text/html"},
@@ -1019,12 +1024,12 @@ add_default() ->
add_default(Config) when is_list(Config) ->
Version = "HTTP/1.1",
- Host = ?config(host, Config),
- Type = ?config(type, Config),
- ok = httpd_test_lib:verify_request(?config(type, Config), Host,
- ?config(port, Config),
+ Host = proplists:get_value(host, Config),
+ Type = proplists:get_value(type, Config),
+ ok = httpd_test_lib:verify_request(proplists:get_value(type, Config), Host,
+ proplists:get_value(port, Config),
transport_opts(Type, Config),
- ?config(node, Config),
+ proplists:get_value(node, Config),
http_request("GET /index.html ", Version, Host),
[{statuscode, 200},
{header, "Content-Type", "text/html"},
@@ -1036,24 +1041,24 @@ add_default(Config) when is_list(Config) ->
max_header() ->
["Denial Of Service (DOS) attack, prevented by max_header"].
max_header(Config) when is_list(Config) ->
- Version = ?config(http_version, Config),
- Host = ?config(host, Config),
+ Version = proplists:get_value(http_version, Config),
+ Host = proplists:get_value(host, Config),
case Version of
"HTTP/0.9" ->
{skip, not_implemented};
_ ->
- dos_hostname(?config(type, Config), ?config(port, Config), Host,
- ?config(node, Config), Version, ?MAX_HEADER_SIZE)
+ dos_hostname(proplists:get_value(type, Config), proplists:get_value(port, Config), Host,
+ proplists:get_value(node, Config), Version, ?MAX_HEADER_SIZE)
end.
%%-------------------------------------------------------------------------
max_content_length() ->
["Denial Of Service (DOS) attack, prevented by max_content_length"].
max_content_length(Config) when is_list(Config) ->
- Version = ?config(http_version, Config),
- Host = ?config(host, Config),
- garbage_content_length(?config(type, Config), ?config(port, Config), Host,
- ?config(node, Config), Version).
+ Version = proplists:get_value(http_version, Config),
+ Host = proplists:get_value(host, Config),
+ garbage_content_length(proplists:get_value(type, Config), proplists:get_value(port, Config), Host,
+ proplists:get_value(node, Config), Version).
%%-------------------------------------------------------------------------
security_1_1(Config) when is_list(Config) ->
@@ -1065,15 +1070,15 @@ security_1_0(Config) when is_list(Config) ->
security() ->
["Test mod_security"].
security(Config) ->
- Version = ?config(http_version, Config),
- Host = ?config(host, Config),
- Port = ?config(port, Config),
- Node = ?config(node, Config),
- ServerRoot = ?config(server_root, Config),
+ Version = proplists:get_value(http_version, Config),
+ Host = proplists:get_value(host, Config),
+ Port = proplists:get_value(port, Config),
+ Node = proplists:get_value(node, Config),
+ ServerRoot = proplists:get_value(server_root, Config),
global:register_name(mod_security_test, self()), % Receive events
- test_server:sleep(5000),
+ ct:sleep(5000),
OpenDir = filename:join([ServerRoot, "htdocs", "open"]),
@@ -1171,7 +1176,7 @@ security(Config) ->
["one"] = list_auth_users(Node, Port, OpenDir),
%% Wait for successful auth to timeout.
- test_server:sleep(?AUTH_TIMEOUT*1001),
+ ct:sleep(?AUTH_TIMEOUT*1001),
[] = list_auth_users(Node, Port),
@@ -1200,11 +1205,11 @@ disturbing_reconfiger_dies(Config) when is_list(Config) ->
do_reconfiger_dies([{http_version, "HTTP/1.1"} | Config], disturbing).
do_reconfiger_dies(Config, DisturbingType) ->
- Server = ?config(server_pid, Config),
- Version = ?config(http_version, Config),
- Host = ?config(host, Config),
- Port = ?config(port, Config),
- Type = ?config(type, Config),
+ Server = proplists:get_value(server_pid, Config),
+ Version = proplists:get_value(http_version, Config),
+ Host = proplists:get_value(host, Config),
+ Port = proplists:get_value(port, Config),
+ Type = proplists:get_value(type, Config),
HttpdConfig = httpd:info(Server),
BlockRequest = http_request("GET /eval?httpd_example:delay(2000) ", Version, Host),
@@ -1235,11 +1240,11 @@ disturbing_0_9(Config) when is_list(Config) ->
disturbing([{http_version, "HTTP/0.9"} | Config]).
disturbing(Config) when is_list(Config)->
- Server = ?config(server_pid, Config),
- Version = ?config(http_version, Config),
- Host = ?config(host, Config),
- Port = ?config(port, Config),
- Type = ?config(type, Config),
+ Server = proplists:get_value(server_pid, Config),
+ Version = proplists:get_value(http_version, Config),
+ Host = proplists:get_value(host, Config),
+ Port = proplists:get_value(port, Config),
+ Type = proplists:get_value(type, Config),
HttpdConfig = httpd:info(Server),
BlockRequest = http_request("GET /eval?httpd_example:delay(2000) ", Version, Host),
{ok, Socket} = inets_test_lib:connect_bin(Type, Host, Port, transport_opts(Type, Config)),
@@ -1267,11 +1272,11 @@ non_disturbing_0_9(Config) when is_list(Config) ->
non_disturbing([{http_version, "HTTP/0.9"} | Config]).
non_disturbing(Config) when is_list(Config)->
- Server = ?config(server_pid, Config),
- Version = ?config(http_version, Config),
- Host = ?config(host, Config),
- Port = ?config(port, Config),
- Type = ?config(type, Config),
+ Server = proplists:get_value(server_pid, Config),
+ Version = proplists:get_value(http_version, Config),
+ Host = proplists:get_value(host, Config),
+ Port = proplists:get_value(port, Config),
+ Type = proplists:get_value(type, Config),
HttpdConfig = httpd:info(Server),
BlockRequest = http_request("GET /eval?httpd_example:delay(2000) ", Version, Host),
@@ -1296,15 +1301,15 @@ non_disturbing(Config) when is_list(Config)->
%% Internal functions -----------------------------------
%%--------------------------------------------------------------------
url(http, End, Config) ->
- Port = ?config(port, Config),
+ Port = proplists:get_value(port, Config),
{ok,Host} = inet:gethostname(),
?URL_START ++ Host ++ ":" ++ integer_to_list(Port) ++ End.
do_max_clients(Config) ->
- Version = ?config(http_version, Config),
- Host = ?config(host, Config),
- Port = ?config(port, Config),
- Type = ?config(type, Config),
+ Version = proplists:get_value(http_version, Config),
+ Host = proplists:get_value(host, Config),
+ Port = proplists:get_value(port, Config),
+ Type = proplists:get_value(type, Config),
Request = http_request("GET /index.html ", Version, Host),
BlockRequest = http_request("GET /eval?httpd_example:delay(2000) ", Version, Host),
@@ -1314,7 +1319,7 @@ do_max_clients(Config) ->
ok = httpd_test_lib:verify_request(Type, Host,
Port,
transport_opts(Type, Config),
- ?config(node, Config),
+ proplists:get_value(node, Config),
Request,
[{statuscode, 503},
{version, Version}]),
@@ -1327,7 +1332,7 @@ do_max_clients(Config) ->
ok = httpd_test_lib:verify_request(Type, Host,
Port,
transport_opts(Type, Config),
- ?config(node, Config),
+ proplists:get_value(node, Config),
Request,
[{statuscode, 200},
{version, Version}]).
@@ -1406,7 +1411,7 @@ server_start(_, HttpdConfig) ->
{Pid, proplists:get_value(port, Info)}.
init_ssl(Group, Config) ->
- PrivDir = ?config(priv_dir, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
CaKey = {_Trusted,_} =
erl_make_certs:make_cert([{key, dsa},
{subject,
@@ -1457,54 +1462,54 @@ server_config(https_custom, Config) ->
server_config(https_limit, Config) ->
[{max_clients, 1}] ++ server_config(https, Config);
server_config(http_basic_auth, Config) ->
- ServerRoot = ?config(server_root, Config),
+ ServerRoot = proplists:get_value(server_root, Config),
auth_conf(ServerRoot) ++ server_config(http, Config);
server_config(https_basic_auth, Config) ->
- ServerRoot = ?config(server_root, Config),
+ ServerRoot = proplists:get_value(server_root, Config),
auth_conf(ServerRoot) ++ server_config(https, Config);
server_config(http_auth_api, Config) ->
- ServerRoot = ?config(server_root, Config),
+ ServerRoot = proplists:get_value(server_root, Config),
auth_api_conf(ServerRoot, plain) ++ server_config(http, Config);
server_config(https_auth_api, Config) ->
- ServerRoot = ?config(server_root, Config),
+ ServerRoot = proplists:get_value(server_root, Config),
auth_api_conf(ServerRoot, plain) ++ server_config(https, Config);
server_config(http_auth_api_dets, Config) ->
- ServerRoot = ?config(server_root, Config),
+ ServerRoot = proplists:get_value(server_root, Config),
auth_api_conf(ServerRoot, dets) ++ server_config(http, Config);
server_config(https_auth_api_dets, Config) ->
- ServerRoot = ?config(server_root, Config),
+ ServerRoot = proplists:get_value(server_root, Config),
auth_api_conf(ServerRoot, dets) ++ server_config(https, Config);
server_config(http_auth_api_mnesia, Config) ->
- ServerRoot = ?config(server_root, Config),
+ ServerRoot = proplists:get_value(server_root, Config),
auth_api_conf(ServerRoot, mnesia) ++ server_config(http, Config);
server_config(https_auth_api_mnesia, Config) ->
- ServerRoot = ?config(server_root, Config),
+ ServerRoot = proplists:get_value(server_root, Config),
auth_api_conf(ServerRoot, mnesia) ++ server_config(https, Config);
server_config(http_htaccess, Config) ->
auth_access_conf() ++ server_config(http, Config);
server_config(https_htaccess, Config) ->
auth_access_conf() ++ server_config(https, Config);
server_config(http_security, Config) ->
- ServerRoot = ?config(server_root, Config),
+ ServerRoot = proplists:get_value(server_root, Config),
tl(auth_conf(ServerRoot)) ++ security_conf(ServerRoot) ++ server_config(http, Config);
server_config(https_security, Config) ->
- ServerRoot = ?config(server_root, Config),
+ ServerRoot = proplists:get_value(server_root, Config),
tl(auth_conf(ServerRoot)) ++ security_conf(ServerRoot) ++ server_config(https, Config);
server_config(http_mime_types, Config0) ->
Config1 = basic_conf() ++ server_config(http, Config0),
- ServerRoot = ?config(server_root, Config0),
+ ServerRoot = proplists:get_value(server_root, Config0),
MimeTypesFile = filename:join([ServerRoot,"config", "mime.types"]),
[{mime_types, MimeTypesFile} | proplists:delete(mime_types, Config1)];
server_config(http, Config) ->
- ServerRoot = ?config(server_root, Config),
+ ServerRoot = proplists:get_value(server_root, Config),
[{port, 0},
{socket_type, {ip_comm, [{nodelay, true}]}},
{server_name,"httpd_test"},
{server_root, ServerRoot},
- {document_root, ?config(doc_root, Config)},
+ {document_root, proplists:get_value(doc_root, Config)},
{bind_address, any},
- {ipfamily, ?config(ipfamily, Config)},
+ {ipfamily, proplists:get_value(ipfamily, Config)},
{max_header_size, 256},
{max_header_action, close},
{directory_index, ["index.html", "welcome.html"]},
@@ -1519,7 +1524,7 @@ server_config(http, Config) ->
];
server_config(https, Config) ->
- PrivDir = ?config(priv_dir, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
[{socket_type, {essl,
[{nodelay, true},
{cacertfile,
@@ -1693,35 +1698,35 @@ mod_security_conf(SecFile, Dir) ->
http_status(Request, Config, Expected) ->
- Version = ?config(http_version, Config),
- Host = ?config(host, Config),
- Type = ?config(type, Config),
- httpd_test_lib:verify_request(?config(type, Config), Host,
- ?config(port, Config),
+ Version = proplists:get_value(http_version, Config),
+ Host = proplists:get_value(host, Config),
+ Type = proplists:get_value(type, Config),
+ httpd_test_lib:verify_request(proplists:get_value(type, Config), Host,
+ proplists:get_value(port, Config),
transport_opts(Type, Config),
- ?config(node, Config),
+ proplists:get_value(node, Config),
http_request(Request, Version, Host),
Expected ++ [{version, Version}]).
http_status(Request, HeadersAndBody, Config, Expected) ->
- Version = ?config(http_version, Config),
- Host = ?config(host, Config),
- Type = ?config(type, Config),
- httpd_test_lib:verify_request(?config(type, Config), Host,
- ?config(port, Config),
+ Version = proplists:get_value(http_version, Config),
+ Host = proplists:get_value(host, Config),
+ Type = proplists:get_value(type, Config),
+ httpd_test_lib:verify_request(proplists:get_value(type, Config), Host,
+ proplists:get_value(port, Config),
transport_opts(Type, Config),
- ?config(node, Config),
+ proplists:get_value(node, Config),
http_request(Request, Version, Host, HeadersAndBody),
Expected ++ [{version, Version}]).
auth_status(AuthRequest, Config, Expected) ->
- Version = ?config(http_version, Config),
- Host = ?config(host, Config),
- Type = ?config(type, Config),
- httpd_test_lib:verify_request(?config(type, Config), Host,
- ?config(port, Config),
+ Version = proplists:get_value(http_version, Config),
+ Host = proplists:get_value(host, Config),
+ Type = proplists:get_value(type, Config),
+ httpd_test_lib:verify_request(proplists:get_value(type, Config), Host,
+ proplists:get_value(port, Config),
transport_opts(Type, Config),
- ?config(node, Config),
+ proplists:get_value(node, Config),
AuthRequest,
Expected ++ [{version, Version}]).
@@ -1773,11 +1778,11 @@ cleanup_mnesia() ->
ok.
transport_opts(ssl, Config) ->
- PrivDir = ?config(priv_dir, Config),
- [?config(ipfamily, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
+ [proplists:get_value(ipfamily, Config),
{cacertfile, filename:join(PrivDir, "public_key_cacert.pem")}];
transport_opts(_, Config) ->
- [?config(ipfamily, Config)].
+ [proplists:get_value(ipfamily, Config)].
%%% mod_range
@@ -2076,4 +2081,4 @@ peer(Config) ->
"true";
_ ->
"false"
- end. \ No newline at end of file
+ end.
diff --git a/lib/inets/test/httpd_basic_SUITE.erl b/lib/inets/test/httpd_basic_SUITE.erl
index 352ff3b1ae..f413248092 100644
--- a/lib/inets/test/httpd_basic_SUITE.erl
+++ b/lib/inets/test/httpd_basic_SUITE.erl
@@ -30,7 +30,8 @@
-define(URL_START, "http://localhost:").
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() -> [{ct_hooks,[ts_install_cth]},
+ {timetrap, {seconds, 30}}].
all() ->
[uri_too_long_414,
@@ -66,8 +67,8 @@ end_per_group(_GroupName, Config) ->
init_per_suite(Config) ->
inets_test_lib:stop_apps([inets]),
inets_test_lib:start_apps([inets]),
- PrivDir = ?config(priv_dir, Config),
- DataDir = ?config(data_dir, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
+ DataDir = proplists:get_value(data_dir, Config),
Dummy =
"<HTML>
@@ -152,7 +153,7 @@ end_per_testcase(_Case, Config) ->
uri_too_long_414() ->
[{doc, "Test that too long uri's get 414 HTTP code"}].
uri_too_long_414(Config) when is_list(Config) ->
- HttpdConf = ?config(httpd_conf, Config),
+ HttpdConf = proplists:get_value(httpd_conf, Config),
{ok, Pid} = inets:start(httpd, [{max_uri_size, 10}
| HttpdConf]),
Info = httpd:info(Pid),
@@ -173,7 +174,7 @@ uri_too_long_414(Config) when is_list(Config) ->
header_too_long_413() ->
[{doc,"Test that too long headers's get 413 HTTP code"}].
header_too_long_413(Config) when is_list(Config) ->
- HttpdConf = ?config(httpd_conf, Config),
+ HttpdConf = proplists:get_value(httpd_conf, Config),
{ok, Pid} = inets:start(httpd, [{max_header_size, 10}
| HttpdConf]),
Info = httpd:info(Pid),
@@ -192,7 +193,7 @@ header_too_long_413(Config) when is_list(Config) ->
entity_too_long() ->
[{doc, "Test that too long versions and method strings are rejected"}].
entity_too_long(Config) when is_list(Config) ->
- HttpdConf = ?config(httpd_conf, Config),
+ HttpdConf = proplists:get_value(httpd_conf, Config),
{ok, Pid} = inets:start(httpd, HttpdConf),
Info = httpd:info(Pid),
Port = proplists:get_value(port, Info),
@@ -259,7 +260,7 @@ erl_script_nocache_opt(doc) ->
erl_script_nocache_opt(suite) ->
[];
erl_script_nocache_opt(Config) when is_list(Config) ->
- HttpdConf = ?config(httpd_conf, Config),
+ HttpdConf = proplists:get_value(httpd_conf, Config),
{ok, Pid} = inets:start(httpd, [{port, 0}, {erl_script_nocache, true} | HttpdConf]),
Info = httpd:info(Pid),
Port = proplists:get_value(port, Info),
@@ -282,7 +283,7 @@ erl_script_nocache_opt(Config) when is_list(Config) ->
escaped_url_in_error_body() ->
[{doc, "Test Url-encoding see OTP-8940"}].
escaped_url_in_error_body(Config) when is_list(Config) ->
- HttpdConf = ?config(httpd_conf, Config),
+ HttpdConf = proplists:get_value(httpd_conf, Config),
{ok, Pid} = inets:start(httpd, [{port, 0} | HttpdConf]),
Info = httpd:info(Pid),
Port = proplists:get_value(port, Info),
@@ -324,7 +325,7 @@ keep_alive_timeout(doc) ->
keep_alive_timeout(suite) ->
[];
keep_alive_timeout(Config) when is_list(Config) ->
- HttpdConf = ?config(httpd_conf, Config),
+ HttpdConf = proplists:get_value(httpd_conf, Config),
{ok, Pid} = inets:start(httpd, [{port, 0}, {keep_alive, true}, {keep_alive_timeout, 2} | HttpdConf]),
Info = httpd:info(Pid),
Port = proplists:get_value(port, Info),
@@ -348,9 +349,9 @@ script_timeout(Config) when is_list(Config) ->
ok.
verify_script_timeout(Config, ScriptTimeout, StatusCode) ->
- HttpdConf = ?config(httpd_conf, Config),
- CgiScript = ?config(cgi_sleep, Config),
- CgiDir = ?config(cgi_dir, Config),
+ HttpdConf = proplists:get_value(httpd_conf, Config),
+ CgiScript = proplists:get_value(cgi_sleep, Config),
+ CgiDir = proplists:get_value(cgi_dir, Config),
{ok, Pid} = inets:start(httpd, [{port, 0},
{script_alias,
{"/cgi-bin/", CgiDir ++ "/"}},
@@ -371,7 +372,7 @@ verify_script_timeout(Config, ScriptTimeout, StatusCode) ->
slowdose() ->
[{doc, "Testing minimum bytes per second option"}].
slowdose(Config) when is_list(Config) ->
- HttpdConf = ?config(httpd_conf, Config),
+ HttpdConf = proplists:get_value(httpd_conf, Config),
{ok, Pid} = inets:start(httpd, [{port, 0}, {minimum_bytes_per_second, 200}|HttpdConf]),
Info = httpd:info(Pid),
Port = proplists:get_value(port, Info),
@@ -386,9 +387,9 @@ slowdose(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
verify_script_nocache(Config, CgiNoCache, EsiNoCache, CgiOption, EsiOption) ->
- HttpdConf = ?config(httpd_conf, Config),
- CgiScript = ?config(cgi_printenv, Config),
- CgiDir = ?config(cgi_dir, Config),
+ HttpdConf = proplists:get_value(httpd_conf, Config),
+ CgiScript = proplists:get_value(cgi_printenv, Config),
+ CgiDir = proplists:get_value(cgi_dir, Config),
{ok, Pid} = inets:start(httpd, [{port, 0},
{script_alias,
{"/cgi-bin/", CgiDir ++ "/"}},
diff --git a/lib/inets/test/httpd_block.erl b/lib/inets/test/httpd_block.erl
index 44f6f644ad..45547e6d4e 100644
--- a/lib/inets/test/httpd_block.erl
+++ b/lib/inets/test/httpd_block.erl
@@ -133,7 +133,7 @@ block_disturbing_active_timeout_released(Type, Port, Host, Node) ->
block_non_disturbing_active_timeout_not_released(Type, Port, Host, Node) ->
process_flag(trap_exit, true),
Poller = long_poll(Type, Host, Port, Node, 200, 60000),
- test_server:sleep(5000),
+ ct:sleep(5000),
ok = block_nd_server(Node, Host, Port, 40000),
await_normal_process_exit(Poller, "poller", 60000),
blocked = get_admin_state(Node, Host, Port),
diff --git a/lib/inets/test/httpd_load.erl b/lib/inets/test/httpd_load.erl
index 37ebc4fc90..7f4d16139f 100644
--- a/lib/inets/test/httpd_load.erl
+++ b/lib/inets/test/httpd_load.erl
@@ -63,7 +63,7 @@ load_test(Fun, URIs, Type, Host, Port, Node, 0, List) ->
{'EXIT', Pid, Reason} ->
Str = lists:flatten(io_lib:format("client ~p exited: ~p",
[Pid,Reason])),
- test_server:fail(Str);
+ ct:fail(Str);
_ ->
load_test(Fun, URIs, Type, Host, Port, Node, 0, List)
end;
@@ -86,12 +86,11 @@ load_test_client(Fun, [URI|URIs], Type, Host, Port, Node, Boss, Timeout) ->
{'EXIT', {suite_failed, connection_closed, _, _}} ->
%% Some platforms seems to handle heavy load badly.
%% So, back off and see if this helps
- %%?LOG("load_test_client->requestfailed:connection_closed"[]),
2 * Timeout;
_ ->
Timeout
end,
- test_server:sleep(Timeout1),
+ ct:sleep(Timeout1),
load_test_client(Fun, URIs, Type, Host, Port, Node, Boss, Timeout1).
load_test_client_done(Boss) ->
diff --git a/lib/inets/test/httpd_mod.erl b/lib/inets/test/httpd_mod.erl
index 7a1250cb29..d9118aa1a4 100644
--- a/lib/inets/test/httpd_mod.erl
+++ b/lib/inets/test/httpd_mod.erl
@@ -86,240 +86,116 @@ actions(Type, Port, Host, Node) ->
security(ServerRoot, Type, Port, Host, Node) ->
global:register_name(mod_security_test, self()), % Receive events
-
- tsp("security -> "
- "sleep"),
- test_server:sleep(5000),
+
+ ct:sleep(5000),
OpenDir = filename:join([ServerRoot, "htdocs", "open"]),
%% Test blocking / unblocking of users.
%% /open, require user one Aladdin
- tsp("security -> "
- "blocking and unblocking of users - "
- "remove all existing users"),
+
remove_users(Node, ServerRoot, Host, Port, "open"),
- tsp("security -> "
- "blocking and unblocking of users - "
- "auth request for nonex user 'one' - expect 401"),
auth_request(Type, Host, Port, Node, "/open/", "one", "onePassword",
[{statuscode, 401}]),
- tsp("security -> "
- "blocking and unblocking of users - "
- "await fail security event"),
receive_security_event({event, auth_fail, Port, OpenDir,
[{user, "one"}, {password, "onePassword"}]},
Node, Port),
- tsp("security -> "
- "blocking and unblocking of users - "
- "auth request for nonex user 'two' - expect 401"),
auth_request(Type,Host,Port,Node,"/open/", "two", "twoPassword",
[{statuscode, 401}]),
- tsp("security -> "
- "blocking and unblocking of users - "
- "await fail security event"),
receive_security_event({event, auth_fail, Port, OpenDir,
[{user, "two"}, {password, "twoPassword"}]},
Node, Port),
-
- tsp("security -> "
- "blocking and unblocking of users - "
- "auth request for nonex user 'Alladin' - expect 401"),
auth_request(Type, Host, Port, Node,"/open/", "Aladdin",
"AladdinPassword", [{statuscode, 401}]),
- tsp("security -> "
- "blocking and unblocking of users - "
- "await fail security event"),
receive_security_event({event, auth_fail, Port, OpenDir,
[{user, "Aladdin"},
{password, "AladdinPassword"}]},
Node, Port),
-
- tsp("security -> "
- "blocking and unblocking of users - "
- "add user 'one'"),
add_user(Node, ServerRoot, Port, "open", "one", "onePassword", []),
- tsp("security -> "
- "blocking and unblocking of users - "
- "add user 'two'"),
add_user(Node, ServerRoot, Port, "open", "two", "twoPassword", []),
- tsp("security -> "
- "blocking and unblocking of users - "
- "auth request 1 for user 'one' with wrong password - expect 401"),
auth_request(Type, Host, Port, Node,"/open/", "one", "WrongPassword",
[{statuscode, 401}]),
-
- tsp("security -> "
- "blocking and unblocking of users - "
- "await fail security event"),
receive_security_event({event, auth_fail, Port, OpenDir,
[{user, "one"}, {password, "WrongPassword"}]},
Node, Port),
-
- tsp("security -> "
- "blocking and unblocking of users - "
- "auth request 2 for user 'one' with wrong password - expect 401"),
auth_request(Type, Host, Port, Node,"/open/", "one", "WrongPassword",
[{statuscode, 401}]),
- tsp("security -> "
- "blocking and unblocking of users - "
- "await fail security event"),
receive_security_event({event, auth_fail, Port, OpenDir,
[{user, "one"}, {password, "WrongPassword"}]},
Node, Port),
-
- tsp("security -> "
- "blocking and unblocking of users - "
- "await block security event (two failed attempts)"),
- receive_security_event({event, user_block, Port, OpenDir,
+ receive_security_event({event, user_block, Port, OpenDir,
[{user, "one"}]}, Node, Port),
- tsp("security -> "
- "blocking and unblocking of users - "
- "unregister - no more security events"),
global:unregister_name(mod_security_test), % No more events.
- tsp("security -> "
- "blocking and unblocking of users - "
- "auth request for user 'one' with wrong password - expect 401"),
auth_request(Type, Host, Port, Node,"/open/", "one", "WrongPassword",
[{statuscode, 401}]),
- tsp("security -> "
- "blocking and unblocking of users - "
- "auth request for user 'one' with correct password - expect 403"),
auth_request(Type, Host, Port, Node,"/open/", "one", "onePassword",
[{statuscode, 403}]),
%% User "one" should be blocked now..
- tsp("security -> "
- "blocking and unblocking of users - "
- "list blocked users - 'one' should be the only one"),
case list_blocked_users(Node, Port) of
[{"one",_, Port, OpenDir,_}] ->
ok;
Blocked ->
- tsp(" *** unexpected blocked users ***"
- "~n Blocked: ~p", [Blocked]),
exit({unexpected_blocked, Blocked})
end,
- tsp("security -> "
- "blocking and unblocking of users - "
- "list users blocked for dir '~p' - "
- "user 'one' should be the only one", [OpenDir]),
[{"one",_, Port, OpenDir,_}] = list_blocked_users(Node, Port, OpenDir),
- tsp("security -> "
- "blocking and unblocking of users - "
- "unblock user 'one' for dir '~p'", [OpenDir]),
true = unblock_user(Node, "one", Port, OpenDir),
%% User "one" should not be blocked any more.
- tsp("security -> "
- "blocking and unblocking of users - "
- "ensure user 'one' is no longer blocked"),
[] = list_blocked_users(Node, Port),
-
- tsp("security -> "
- "blocking and unblocking of users - "
- "auth request for user 'one' with correct password - expect 200"),
auth_request(Type, Host, Port, Node,"/open/", "one", "onePassword",
[{statuscode, 200}]),
%% Test list_auth_users & auth_timeout
-
- tsp("security -> "
- "list-auth-users and auth-timeout - "
- "list auth users - expect user 'one'"),
["one"] = list_auth_users(Node, Port),
- tsp("security -> "
- "list-auth-users and auth-timeout - "
- "auth request for user 'two' with wrong password - expect 401"),
auth_request(Type, Host, Port, Node,"/open/", "two", "onePassword",
[{statuscode, 401}]),
-
- tsp("security -> "
- "list-auth-users and auth-timeout - "
- "list auth users - expect user 'one'"),
["one"] = list_auth_users(Node, Port),
- tsp("security -> "
- "list-auth-users and auth-timeout - "
- "list auth users for dir '~p' - expect user 'one'", [OpenDir]),
["one"] = list_auth_users(Node, Port, OpenDir),
- tsp("security -> "
- "list-auth-users and auth-timeout - "
- "auth request for user 'two' with correct password - expect 401"),
auth_request(Type, Host, Port, Node,"/open/", "two", "twoPassword",
[{statuscode, 401}]),
- tsp("security -> "
- "list-auth-users and auth-timeout - "
- "list auth users - expect user 'one'"),
["one"] = list_auth_users(Node, Port),
- tsp("security -> "
- "list-auth-users and auth-timeout - "
- "list auth users for dir '~p' - expect user 'one'", [OpenDir]),
["one"] = list_auth_users(Node, Port, OpenDir),
%% Wait for successful auth to timeout.
- tsp("security -> "
- "list-auth-users and auth-timeout - "
- "wait for successful auth to timeout"),
- test_server:sleep(?AUTH_TIMEOUT*1001),
-
- tsp("security -> "
- "list-auth-users and auth-timeout - "
- "list auth users - expect none"),
+ ct:sleep(?AUTH_TIMEOUT*1001),
+
[] = list_auth_users(Node, Port),
- tsp("security -> "
- "list-auth-users and auth-timeout - "
- "list auth users for dir '~p'~n - expect none", [OpenDir]),
+
[] = list_auth_users(Node, Port, OpenDir),
%% "two" is blocked.
- tsp("security -> "
- "list-auth-users and auth-timeout - "
- "unblock user 'two' for dir '~p'", [OpenDir]),
true = unblock_user(Node, "two", Port, OpenDir),
-
-
%% Test explicit blocking. Block user 'two'.
- tsp("security -> "
- "explicit blocking - list blocked users - should be none"),
[] = list_blocked_users(Node,Port,OpenDir),
- tsp("security -> "
- "explicit blocking - "
- "block user 'two' for dir '~p'", [OpenDir]),
true = block_user(Node, "two", Port, OpenDir, 10),
-
- tsp("security -> "
- "explicit blocking - "
- "auth request for user 'two' with correct password - expect 401"),
auth_request(Type, Host, Port, Node,"/open/", "two", "twoPassword",
- [{statuscode, 401}]),
- tsp("security -> "
- "done").
-
+ [{statuscode, 401}]).
%%-------------------------------------------------------------------------
auth(Type, Port, Host, Node) ->
@@ -743,7 +619,6 @@ cgi(Type, Port, Host, Node) ->
end,
%% The length (> 100) is intentional
-%% tsp("cgi -> request 01 with length > 100"),
ok = httpd_test_lib:
verify_request(Type, Host, Port, Node,
"POST /cgi-bin/" ++ Script3 ++
@@ -771,55 +646,51 @@ cgi(Type, Port, Host, Node) ->
{version, "HTTP/1.0"},
{header, "content-type", "text/plain"}]),
-%% tsp("cgi -> request 02"),
ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
"GET /cgi-bin/"++ Script ++
" HTTP/1.0\r\n\r\n",
[{statuscode, 200},
{version, "HTTP/1.0"}]),
-%% tsp("cgi -> request 03"),
+
ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
"GET /cgi-bin/not_there "
"HTTP/1.0\r\n\r\n",
[{statuscode, 404},{statuscode, 500},
{version, "HTTP/1.0"}]),
-%% tsp("cgi -> request 04"),
+
ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
"GET /cgi-bin/"++ Script ++
"?Nisse:kkk?sss/lll HTTP/1.0\r\n\r\n",
[{statuscode, 200},
{version, "HTTP/1.0"}]),
-%% tsp("cgi -> request 04"),
+
ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
"POST /cgi-bin/"++ Script ++
" HTTP/1.0\r\n\r\n",
[{statuscode, 200},
{version, "HTTP/1.0"}]),
-%% tsp("cgi -> request 05"),
+
ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
"GET /htbin/"++ Script ++
" HTTP/1.0\r\n\r\n",
[{statuscode, 200},
{version, "HTTP/1.0"}]),
-%% tsp("cgi -> request 06"),
ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
"GET /htbin/not_there "
"HTTP/1.0\r\n\r\n",
[{statuscode, 404},{statuscode, 500},
{version, "HTTP/1.0"}]),
-%% tsp("cgi -> request 07"),
+
ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
"GET /htbin/"++ Script ++
"?Nisse:kkk?sss/lll HTTP/1.0\r\n\r\n",
[{statuscode, 200},
{version, "HTTP/1.0"}]),
-%% tsp("cgi -> request 08"),
ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
"POST /htbin/"++ Script ++
" HTTP/1.0\r\n\r\n",
[{statuscode, 200},
{version, "HTTP/1.0"}]),
-%% tsp("cgi -> request 09"),
ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
"POST /htbin/"++ Script ++
" HTTP/1.0\r\n\r\n",
@@ -827,31 +698,25 @@ cgi(Type, Port, Host, Node) ->
{version, "HTTP/1.0"}]),
%% Execute an existing, but bad CGI script..
-%% tsp("cgi -> request 10 - bad script"),
ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
"POST /htbin/"++ Script2 ++
" HTTP/1.0\r\n\r\n",
[{statuscode, 404},
{version, "HTTP/1.0"}]),
-%% tsp("cgi -> request 11 - bad script"),
ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
"POST /cgi-bin/"++ Script2 ++
" HTTP/1.0\r\n\r\n",
[{statuscode, 404},
{version, "HTTP/1.0"}]),
-%% tsp("cgi -> done"),
-
%% Check "ScriptNoCache" directive (default: false)
ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
"GET /cgi-bin/" ++ Script ++
" HTTP/1.0\r\n\r\n",
[{statuscode, 200},
{no_header, "cache-control"},
- {version, "HTTP/1.0"}]),
- ok.
-
+ {version, "HTTP/1.0"}]).
%%--------------------------------------------------------------------
esi(Type, Port, Host, Node) ->
@@ -1018,18 +883,15 @@ list_users(Node, Root, _Host, Port, Dir) ->
receive_security_event(Event, Node, Port) ->
- tsp("receive_security_event -> await ~w event", [element(2, Event)]),
receive
Event ->
- tsp("receive_security_event -> "
- "received expected ~w event", [element(2, Event)]),
ok;
{'EXIT', _, _} ->
receive_security_event(Event, Node, Port)
after 5000 ->
%% Flush the message queue, to see if we got something...
Msgs = inets_test_lib:flush(),
- tsf({expected_event_not_received, Msgs})
+ ct:fail({expected_event_not_received, Msgs})
end.
@@ -1045,10 +907,10 @@ receive_security_event(Event, Node, Port) ->
%% {'EXIT', _, _} ->
%% receive_security_event(Event, Node, Port);
%% Other ->
-%% test_server:fail({unexpected_event,
+%% ct:fail({unexpected_event,
%% {expected, Event}, {received, Other}})
%% after 5000 ->
-%% test_server:fail(no_event_recived)
+%% ct:fail(no_event_recived)
%% end.
@@ -1130,17 +992,4 @@ check_lists_members1(L1,L2) ->
{error,{lists_not_equal,L1,L2}}.
-%% p(F) ->
-%% p(F, []).
-
-%% p(F, A) ->
-%% io:format(user, "~w:" ++ F ++ "~n", [?MODULE|A]).
-
-tsp(F) ->
- inets_test_lib:tsp(F).
-tsp(F, A) ->
- inets_test_lib:tsp(F, A).
-
-tsf(Reason) ->
- test_server:fail(Reason).
diff --git a/lib/inets/test/inets_SUITE.erl b/lib/inets/test/inets_SUITE.erl
index 928d9dc391..5eaf3a28a0 100644
--- a/lib/inets/test/inets_SUITE.erl
+++ b/lib/inets/test/inets_SUITE.erl
@@ -28,10 +28,13 @@
-define(NUM_DEFAULT_SERVICES, 1).
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap,{seconds,5}}
+ ].
all() ->
- [{group, app_test}, {group, appup_test},
+ [{group, app_test},
{group, services_test}, httpd_reload].
groups() ->
@@ -42,8 +45,7 @@ groups() ->
start_ftpc,
start_tftpd
]},
- {app_test, [], [{inets_app_test, all}]},
- {appup_test, [], [{inets_appup_test, all}]}].
+ {app_test, [], [app, appup]}].
init_per_group(_GroupName, Config) ->
Config.
@@ -84,6 +86,10 @@ end_per_suite(_Config) ->
%% Note: This function is free to add any key/value pairs to the Config
%% variable, but should NOT alter/remove any existing entries.
%%--------------------------------------------------------------------
+init_per_testcase(httpd_reload, Config) ->
+ inets:stop(),
+ ct:timetrap({seconds, 40}),
+ Config;
init_per_testcase(_Case, Config) ->
inets:stop(),
Config.
@@ -102,6 +108,15 @@ end_per_testcase(_, Config) ->
%%-------------------------------------------------------------------------
%% Test cases starts here.
%%-------------------------------------------------------------------------
+app() ->
+ [{doc, "Test that the inets app file is ok"}].
+app(Config) when is_list(Config) ->
+ ok = ?t:app_test(inets).
+%%--------------------------------------------------------------------
+appup() ->
+ [{doc, "Test that the inets appup file is ok"}].
+appup(Config) when is_list(Config) ->
+ ok = ?t:appup_test(inets).
start_inets() ->
[{doc, "Test inets API functions"}].
@@ -134,7 +149,7 @@ start_httpc() ->
[{doc, "Start/stop of httpc service"}].
start_httpc(Config) when is_list(Config) ->
process_flag(trap_exit, true),
- PrivDir = ?config(priv_dir, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
ok = inets:start(),
{ok, Pid0} = inets:start(httpc, [{profile, foo}]),
@@ -145,7 +160,7 @@ start_httpc(Config) when is_list(Config) ->
inets:stop(httpc, Pid0),
- test_server:sleep(100),
+ ct:sleep(100),
Pids1 = [ServicePid || {_, ServicePid} <- inets:services()],
false = lists:member(Pid0, Pids1),
@@ -188,7 +203,7 @@ start_httpd() ->
[{doc, "Start/stop of httpd service"}].
start_httpd(Config) when is_list(Config) ->
process_flag(trap_exit, true),
- PrivDir = ?config(priv_dir, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
HttpdConf = [{server_name, "httpd_test"}, {server_root, PrivDir},
{document_root, PrivDir}, {bind_address, any}],
@@ -199,7 +214,7 @@ start_httpd(Config) when is_list(Config) ->
[_|_] = inets:services_info(),
inets:stop(httpd, Pid0),
- test_server:sleep(500),
+ ct:sleep(500),
Pids1 = [ServicePid || {_, ServicePid} <- inets:services()],
false = lists:member(Pid0, Pids1),
{ok, Pid1} =
@@ -212,7 +227,7 @@ start_httpd(Config) when is_list(Config) ->
{'EXIT', Pid1, shutdown} ->
ok
after 100 ->
- test_server:fail(stand_alone_not_shutdown)
+ ct:fail(stand_alone_not_shutdown)
end,
ok = inets:stop(),
File0 = filename:join(PrivDir, "httpd.conf"),
@@ -279,44 +294,38 @@ start_httpd(Config) when is_list(Config) ->
start_ftpc(doc) ->
[{doc, "Start/stop of ftpc service"}];
-start_ftpc(Config) when is_list(Config) ->
+start_ftpc(Config0) when is_list(Config0) ->
process_flag(trap_exit, true),
ok = inets:start(),
- try
- begin
- {_Tag, FtpdHost} = ftp_suite_lib:dirty_select_ftpd_host(Config),
- case inets:start(ftpc, [{host, FtpdHost}]) of
- {ok, Pid0} ->
- Pids0 = [ServicePid || {_, ServicePid} <-
- inets:services()],
- true = lists:member(Pid0, Pids0),
- [_|_] = inets:services_info(),
- inets:stop(ftpc, Pid0),
- test_server:sleep(100),
- Pids1 = [ServicePid || {_, ServicePid} <-
- inets:services()],
- false = lists:member(Pid0, Pids1),
- {ok, Pid1} =
- inets:start(ftpc, [{host, FtpdHost}], stand_alone),
- Pids2 = [ServicePid || {_, ServicePid} <-
- inets:services()],
- false = lists:member(Pid1, Pids2),
- ok = inets:stop(stand_alone, Pid1),
- receive
- {'EXIT', Pid1, shutdown} ->
- ok
- after 100 ->
- ct:fail(stand_alone_not_shutdown)
- end,
- ok = inets:stop(),
- ok;
- _ ->
- {skip, "Unable to reach selected FTP server " ++ FtpdHost}
- end
- end
- catch
- throw:{error, not_found} ->
- {skip, "No available FTP servers"}
+ case ftp_SUITE:init_per_suite(Config0) of
+ {skip, _} = Skip ->
+ Skip;
+ Config ->
+ FtpdHost = proplists:get_value(ftpd_host,Config),
+ {ok, Pid0} = inets:start(ftpc, [{host, FtpdHost}]),
+ Pids0 = [ServicePid || {_, ServicePid} <-
+ inets:services()],
+ true = lists:member(Pid0, Pids0),
+ [_|_] = inets:services_info(),
+ inets:stop(ftpc, Pid0),
+ ct:sleep(100),
+ Pids1 = [ServicePid || {_, ServicePid} <-
+ inets:services()],
+ false = lists:member(Pid0, Pids1),
+ {ok, Pid1} =
+ inets:start(ftpc, [{host, FtpdHost}], stand_alone),
+ Pids2 = [ServicePid || {_, ServicePid} <-
+ inets:services()],
+ false = lists:member(Pid1, Pids2),
+ ok = inets:stop(stand_alone, Pid1),
+ receive
+ {'EXIT', Pid1, shutdown} ->
+ ok
+ after 100 ->
+ ct:fail(stand_alone_not_shutdown)
+ end,
+ ok = inets:stop(),
+ catch ftp_SUITE:end_per_SUITE(Config)
end.
%%-------------------------------------------------------------------------
@@ -331,7 +340,7 @@ start_tftpd(Config) when is_list(Config) ->
true = lists:member(Pid0, Pids0),
[_|_] = inets:services_info(),
inets:stop(tftpd, Pid0),
- test_server:sleep(100),
+ ct:sleep(100),
Pids1 = [ServicePid || {_, ServicePid} <- inets:services()],
false = lists:member(Pid0, Pids1),
{ok, Pid1} =
@@ -343,7 +352,7 @@ start_tftpd(Config) when is_list(Config) ->
{'EXIT', Pid1, shutdown} ->
ok
after 100 ->
- test_server:fail(stand_alone_not_shutdown)
+ ct:fail(stand_alone_not_shutdown)
end,
ok = inets:stop(),
application:load(inets),
@@ -360,34 +369,34 @@ httpd_reload() ->
[{doc, "Reload httpd configuration without restarting service"}].
httpd_reload(Config) when is_list(Config) ->
process_flag(trap_exit, true),
- PrivDir = ?config(priv_dir, Config),
- DataDir = ?config(data_dir, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
+ DataDir = proplists:get_value(data_dir, Config),
HttpdConf = [{server_name, "httpd_test"},
{server_root, PrivDir},
{document_root, PrivDir},
{bind_address, "localhost"}],
ok = inets:start(),
- test_server:sleep(5000),
+ ct:sleep(5000),
{ok, Pid0} = inets:start(httpd, [{port, 0},
{ipfamily, inet} | HttpdConf]),
- test_server:sleep(5000),
+ ct:sleep(5000),
[{port, Port0}] = httpd:info(Pid0, [port]),
- test_server:sleep(5000),
+ ct:sleep(5000),
[{document_root, PrivDir}] = httpd:info(Pid0, [document_root]),
- test_server:sleep(5000),
+ ct:sleep(5000),
ok = httpd:reload_config([{port, Port0}, {ipfamily, inet},
{server_name, "httpd_test"},
{server_root, PrivDir},
{document_root, DataDir},
{bind_address, "localhost"}], non_disturbing),
- test_server:sleep(5000),
+ ct:sleep(5000),
[{document_root, DataDir}] = httpd:info(Pid0, [document_root]),
- test_server:sleep(5000),
+ ct:sleep(5000),
ok = httpd:reload_config([{port, Port0}, {ipfamily, inet},
{server_name, "httpd_test"},
diff --git a/lib/inets/test/inets_app_test.erl b/lib/inets/test/inets_app_test.erl
deleted file mode 100644
index c6d0715e1f..0000000000
--- a/lib/inets/test/inets_app_test.erl
+++ /dev/null
@@ -1,245 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2002-2015. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%
-%%----------------------------------------------------------------------
-%% Purpose: Verify the application specifics of the inets application
-%%----------------------------------------------------------------------
--module(inets_app_test).
-
--compile(export_all).
-
--include("inets_test_lib.hrl").
-
-
-% t() -> megaco_test_lib:t(?MODULE).
-% t(Case) -> megaco_test_lib:t({?MODULE, Case}).
-
-
-%% Test server callbacks
-init_per_testcase(_, Config) ->
- Config.
-
-end_per_testcase(_Case, Config) ->
- Config.
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-all() ->
- [fields, modules, exportall, app_depend].
-
-groups() ->
- [].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-init_per_suite(suite) -> [];
-init_per_suite(doc) -> [];
-init_per_suite(Config) when is_list(Config) ->
- case is_app(inets) of
- {ok, AppFile} ->
- io:format("AppFile: ~n~p~n", [AppFile]),
- inets:print_version_info(),
- [{app_file, AppFile}|Config];
- {error, Reason} ->
- fail(Reason)
- end.
-
-is_app(App) ->
- LibDir = code:lib_dir(App),
- File = filename:join([LibDir, "ebin", atom_to_list(App) ++ ".app"]),
- case file:consult(File) of
- {ok, [{application, App, AppFile}]} ->
- {ok, AppFile};
- Error ->
- {error, {invalid_format, Error}}
- end.
-
-
-end_per_suite(suite) -> [];
-end_per_suite(doc) -> [];
-end_per_suite(Config) when is_list(Config) ->
- Config.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-fields(suite) ->
- [];
-fields(doc) ->
- [];
-fields(Config) when is_list(Config) ->
- AppFile = key1search(app_file, Config),
- Fields = [vsn, description, modules, registered, applications],
- case check_fields(Fields, AppFile, []) of
- [] ->
- ok;
- Missing ->
- fail({missing_fields, Missing})
- end.
-
-check_fields([], _AppFile, Missing) ->
- Missing;
-check_fields([Field|Fields], AppFile, Missing) ->
- check_fields(Fields, AppFile, check_field(Field, AppFile, Missing)).
-
-check_field(Name, AppFile, Missing) ->
- io:format("checking field: ~p~n", [Name]),
- case lists:keymember(Name, 1, AppFile) of
- true ->
- Missing;
- false ->
- [Name|Missing]
- end.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-modules(suite) ->
- [];
-modules(doc) ->
- [];
-modules(Config) when is_list(Config) ->
- AppFile = key1search(app_file, Config),
- Mods = key1search(modules, AppFile),
- EbinList = get_ebin_mods(inets),
- case missing_modules(Mods, EbinList, []) of
- [] ->
- ok;
- Missing ->
- throw({error, {missing_modules, Missing}})
- end,
- case extra_modules(Mods, EbinList, []) of
- [] ->
- ok;
- Extra ->
- throw({error, {extra_modules, Extra}})
- end,
- {ok, Mods}.
-
-get_ebin_mods(App) ->
- LibDir = code:lib_dir(App),
- EbinDir = filename:join([LibDir,"ebin"]),
- {ok, Files0} = file:list_dir(EbinDir),
- Files1 = [lists:reverse(File) || File <- Files0],
- [list_to_atom(lists:reverse(Name)) || [$m,$a,$e,$b,$.|Name] <- Files1].
-
-
-missing_modules([], _Ebins, Missing) ->
- Missing;
-missing_modules([Mod|Mods], Ebins, Missing) ->
- case lists:member(Mod, Ebins) of
- true ->
- missing_modules(Mods, Ebins, Missing);
- false ->
- io:format("missing module: ~p~n", [Mod]),
- missing_modules(Mods, Ebins, [Mod|Missing])
- end.
-
-
-extra_modules(_Mods, [], Extra) ->
- Extra;
-extra_modules(Mods, [Mod|Ebins], Extra) ->
- case lists:member(Mod, Mods) of
- true ->
- extra_modules(Mods, Ebins, Extra);
- false ->
- io:format("supefluous module: ~p~n", [Mod]),
- extra_modules(Mods, Ebins, [Mod|Extra])
- end.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-
-exportall(suite) ->
- [];
-exportall(doc) ->
- [];
-exportall(Config) when is_list(Config) ->
- AppFile = key1search(app_file, Config),
- Mods = key1search(modules, AppFile),
- check_export_all(Mods).
-
-
-check_export_all([]) ->
- ok;
-check_export_all([Mod|Mods]) ->
- case (catch apply(Mod, module_info, [compile])) of
- {'EXIT', {undef, _}} ->
- check_export_all(Mods);
- O ->
- case lists:keysearch(options, 1, O) of
- false ->
- check_export_all(Mods);
- {value, {options, List}} ->
- case lists:member(export_all, List) of
- true ->
- throw({error, {export_all, Mod}});
- false ->
- check_export_all(Mods)
- end
- end
- end.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-app_depend(suite) ->
- [];
-app_depend(doc) ->
- [];
-app_depend(Config) when is_list(Config) ->
- AppFile = key1search(app_file, Config),
- Apps = key1search(applications, AppFile),
- check_apps(Apps).
-
-
-check_apps([]) ->
- ok;
-check_apps([App|Apps]) ->
- case is_app(App) of
- {ok, _} ->
- check_apps(Apps);
- Error ->
- throw({error, {missing_app, {App, Error}}})
- end.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-
-fail(Reason) ->
- exit({suite_failed, Reason}).
-
-key1search(Key, L) ->
- case lists:keysearch(Key, 1, L) of
- undefined ->
- fail({not_found, Key, L});
- {value, {Key, Value}} ->
- Value
- end.
diff --git a/lib/inets/test/inets_appup_test.erl b/lib/inets/test/inets_appup_test.erl
deleted file mode 100644
index b76cd59141..0000000000
--- a/lib/inets/test/inets_appup_test.erl
+++ /dev/null
@@ -1,71 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2002-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%
-%%----------------------------------------------------------------------
-%% Purpose: Verify the application specifics of the Inets application
-%%----------------------------------------------------------------------
--module(inets_appup_test).
-
--compile(export_all).
--include_lib("common_test/include/ct.hrl").
-
-
-%% Test server callbacks
-init_per_testcase(_Case, Config) ->
- Config.
-
-end_per_testcase(_Case, Config) ->
- Config.
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-all() ->
- [appup].
-
-groups() ->
- [].
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-init_per_suite(suite) -> [];
-init_per_suite(doc) -> [];
-init_per_suite(Config) when is_list(Config) ->
- Config.
-
-
-end_per_suite(suite) -> [];
-end_per_suite(doc) -> [];
-end_per_suite(Config) when is_list(Config) ->
- Config.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-appup() ->
- [{doc, "Perform a simple check of the inets appup file"}].
-appup(Config) when is_list(Config) ->
- ok = ?t:appup_test(inets).
diff --git a/lib/inets/test/inets_socketwrap_SUITE.erl b/lib/inets/test/inets_socketwrap_SUITE.erl
index cfbda3ccf5..18df995215 100644
--- a/lib/inets/test/inets_socketwrap_SUITE.erl
+++ b/lib/inets/test/inets_socketwrap_SUITE.erl
@@ -61,8 +61,8 @@ end_per_testcase(_, Config) ->
start_httpd_fd() ->
[{doc, "Start/stop of httpd service with socket wrapper"}].
start_httpd_fd(Config) when is_list(Config) ->
- PrivDir = ?config(priv_dir, Config),
- DataDir = ?config(data_dir, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
+ DataDir = proplists:get_value(data_dir, Config),
HttpdConf = [{port, 80}, {ipfamily, inet},
{server_name, "httpd_fd_test"}, {server_root, PrivDir},
{document_root, PrivDir}, {bind_address, any}],
@@ -94,7 +94,7 @@ start_httpd_fd(Config) when is_list(Config) ->
start_tftpd_fd() ->
[{doc, "Start/stop of tfpd service with socket wrapper"}].
start_tftpd_fd(Config) when is_list(Config) ->
- DataDir = ?config(data_dir, Config),
+ DataDir = proplists:get_value(data_dir, Config),
case setup_node_info(node()) of
{skip, _} = Skip ->
Skip;
diff --git a/lib/inets/test/inets_sup_SUITE.erl b/lib/inets/test/inets_sup_SUITE.erl
index 9836cd76e0..5b8b1463c8 100644
--- a/lib/inets/test/inets_sup_SUITE.erl
+++ b/lib/inets/test/inets_sup_SUITE.erl
@@ -26,7 +26,10 @@
%% Note: This directive should only be used in test suites.
-compile(export_all).
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap,{seconds, 10}}
+ ].
all() ->
[default_tree, ftpc_worker, tftpd_worker,
@@ -50,9 +53,7 @@ end_per_suite(_) ->
ok.
init_per_testcase(httpd_subtree, Config) ->
- Dog = test_server:timetrap(?t:minutes(1)),
- NewConfig = lists:keydelete(watchdog, 1, Config),
- PrivDir = ?config(priv_dir, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
Dir = filename:join(PrivDir, "root"),
ok = file:make_dir(Dir),
@@ -67,7 +68,7 @@ init_per_testcase(httpd_subtree, Config) ->
inets:stop(),
inets:start(),
inets:start(httpd, SimpleConfig),
- [{watchdog, Dog} | NewConfig]
+ Config
catch
_:Reason ->
inets:stop(),
@@ -75,9 +76,7 @@ init_per_testcase(httpd_subtree, Config) ->
end;
init_per_testcase(httpd_subtree_profile, Config) ->
- Dog = test_server:timetrap(?t:minutes(1)),
- NewConfig = lists:keydelete(watchdog, 1, Config),
- PrivDir = ?config(priv_dir, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
Dir = filename:join(PrivDir, "root"),
ok = file:make_dir(Dir),
@@ -93,7 +92,7 @@ init_per_testcase(httpd_subtree_profile, Config) ->
inets:stop(),
inets:start(),
{ok, _} = inets:start(httpd, SimpleConfig),
- [{watchdog, Dog} | NewConfig]
+ Config
catch
_:Reason ->
inets:stop(),
@@ -102,24 +101,18 @@ init_per_testcase(httpd_subtree_profile, Config) ->
init_per_testcase(_Case, Config) ->
- Dog = test_server:timetrap(?t:minutes(5)),
- NewConfig = lists:keydelete(watchdog, 1, Config),
inets:stop(),
ok = inets:start(),
- [{watchdog, Dog} | NewConfig].
+ Config.
end_per_testcase(Case, Config) when Case == httpd_subtree;
Case == httpd_subtree_profile ->
- Dog = ?config(watchdog, Config),
- test_server:timetrap_cancel(Dog),
- PrivDir = ?config(priv_dir, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
Dir = filename:join(PrivDir, "root"),
inets_test_lib:del_dirs(Dir),
ok;
-end_per_testcase(_, Config) ->
- Dog = ?config(watchdog, Config),
- test_server:timetrap_cancel(Dog),
+end_per_testcase(_, _) ->
inets:stop(),
ok.
@@ -164,29 +157,25 @@ default_tree(Config) when is_list(Config) ->
ftpc_worker() ->
[{doc, "Makes sure the ftp worker processes are added and removed "
"appropriatly to/from the supervison tree."}].
-ftpc_worker(Config) when is_list(Config) ->
+ftpc_worker(Config0) when is_list(Config0) ->
[] = supervisor:which_children(ftp_sup),
- try
- begin
- {_Tag, FtpdHost} = ftp_suite_lib:dirty_select_ftpd_host(Config),
- case inets:start(ftpc, [{host, FtpdHost}]) of
- {ok, Pid} ->
- case supervisor:which_children(ftp_sup) of
- [{_,_, worker, [ftp]}] ->
- inets:stop(ftpc, Pid),
- test_server:sleep(5000),
- [] = supervisor:which_children(ftp_sup),
- ok;
- Children ->
- exit({unexpected_children, Children})
- end;
- _ ->
- {skip, "Unable to reach test FTP server"}
+ case ftp_SUITE:init_per_suite(Config0) of
+ {skip, _} = Skip ->
+ Skip;
+ Config ->
+ FtpdHost = proplists:get_value(ftpd_host,Config),
+ {ok, Pid} = inets:start(ftpc, [{host, FtpdHost}]),
+ case supervisor:which_children(ftp_sup) of
+ [{_,_, worker, [ftp]}] ->
+ inets:stop(ftpc, Pid),
+ ct:sleep(5000),
+ [] = supervisor:which_children(ftp_sup),
+ catch ftp_SUITE:end_per_SUITE(Config),
+ ok;
+ Children ->
+ catch ftp_SUITE:end_per_SUITE(Config),
+ exit({unexpected_children, Children})
end
- end
- catch
- throw:{error, not_found} ->
- {skip, "No available FTP servers"}
end.
tftpd_worker() ->
@@ -200,7 +189,7 @@ tftpd_worker(Config) when is_list(Config) ->
[{_,Pid0, worker, _}] = supervisor:which_children(tftp_sup),
inets:stop(tftpd, Pid0),
- test_server:sleep(5000),
+ ct:sleep(5000),
[] = supervisor:which_children(tftp_sup),
ok.
diff --git a/lib/inets/test/inets_test_lib.hrl b/lib/inets/test/inets_test_lib.hrl
index a36ab0b1cf..d436395290 100644
--- a/lib/inets/test/inets_test_lib.hrl
+++ b/lib/inets/test/inets_test_lib.hrl
@@ -21,96 +21,8 @@
%%----------------------------------------------------------------------
%% Purpose: Define common macros for testing
%%----------------------------------------------------------------------
-
-%% - Print macros -
-
--ifdef(inets_debug).
--define(DEBUG(F,A), inets_test_lib:debug(F, A, ?MODULE, ?LINE)).
--else.
--define(DEBUG(F,A),ok).
--endif.
-
--ifdef(inets_log).
--define(LOG(F,A), inets_test_lib:log(F, A, ?MODULE, ?LINE)).
--else.
--define(LOG(F,A),ok).
--endif.
-
--define(INFO(F,A), inets_test_lib:info(F, A, ?MODULE, ?LINE)).
--define(PRINT(F,A), inets_test_lib:print(F, A, ?MODULE, ?LINE)).
-
-
-%% - Macros stolen from the test server -
-
--ifndef(line).
--define(line,put(test_server_loc,{?MODULE,?LINE}),).
--endif.
-
-
-%% - OS Command and stuff
-
--define(OSCMD(Cmd), inets_test_lib:oscmd(Cmd)).
-
--define(PRINT_SYSTEM_INFO(P), inets_test_lib:print_system_info(P)).
-
--define(RUN_ON_OS(OS, FUN), inets_test_lib:run_on_os(OS, FUN)).
--define(RUN_ON_WINDOWS(FUN), inets_test_lib:run_on_windows(FUN)).
-
-
-%% - Test case macros -
-
--define(EXPANDABLE(I, C, F), inets_test_lib:expandable(I, C, F)).
--define(OS_BASED_SKIP(Skippable),
- inets_test_lib:os_based_skip(Skippable)).
-
--define(NON_PC_TC_MAYBE_SKIP(Config, Condition),
- inets_test_lib:non_pc_tc_maybe_skip(Config, Condition, ?MODULE, ?LINE)).
-
-
-
%% - Misc macros -
-define(ENSURE_STARTED(A), inets_test_lib:ensure_started(A)).
--define(UPDATE(K,V,C), inets_test_lib:update_config(K,V,C)).
--define(CONFIG(K,C), inets_test_lib:get_config(K,C)).
--define(HOSTNAME(), inets_test_lib:hostname()).
--define(SZ(X), inets_test_lib:sz(X)).
-
-
-%% - Test case macros -
-
--define(SKIP(Reason), inets_test_lib:skip(Reason, ?MODULE, ?LINE)).
--define(FAIL(Reason), inets_test_lib:fail(Reason, ?MODULE, ?LINE)).
-
-
-%% - Socket macros -
-
--define(CONNECT(M,H,P), inets_test_lib:connect(M,H,P)).
--define(SEND(M,S,D), inets_test_lib:send(M,S,D)).
--define(CSEND(M,S,D,C,T), inets_test_lib:csend(M,S,D,C,T)).
--define(CLOSE(M,S), inets_test_lib:close(M,S)).
-
-
-%% - Time macros -
-
--define(HOURS(N), inets_test_lib:hours(N)).
--define(MINS(N), inets_test_lib:minutes(N)).
--define(SECS(N), inets_test_lib:seconds(N)).
-
--define(WD_START(T), inets_test_lib:watchdog_start(T)).
--define(WD_STOP(P), inets_test_lib:watchdog_stop(P)).
-
--define(SLEEP(MSEC), inets_test_lib:sleep(MSEC)).
--define(M(), inets_test_lib:millis()).
--define(MDIFF(A,B), inets_test_lib:millis_diff(A,B)).
-
-
-%% - Process utility macros -
-
--define(FLUSH(), inets_test_lib:flush_mqueue()).
--define(ETRAP_GET(), inets_test_lib:trap_exit()).
--define(ETRAP_SET(O), inets_test_lib:trap_exit(O)).
-
-
diff --git a/lib/inets/test/old_httpd_SUITE.erl b/lib/inets/test/old_httpd_SUITE.erl
index 9902c1459e..172db53844 100644
--- a/lib/inets/test/old_httpd_SUITE.erl
+++ b/lib/inets/test/old_httpd_SUITE.erl
@@ -286,9 +286,7 @@ init_per_suite(Config) ->
"~n Config: ~p"
"~n", [Config]),
- ?PRINT_SYSTEM_INFO([]),
-
- PrivDir = ?config(priv_dir, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
SuiteTopDir = filename:join(PrivDir, ?MODULE),
case file:make_dir(SuiteTopDir) of
ok ->
@@ -314,7 +312,7 @@ init_per_suite(Config) ->
%%--------------------------------------------------------------------
end_per_suite(_Config) ->
- %% SuiteTopDir = ?config(suite_top_dir, Config),
+ %% SuiteTopDir = proplists:get_value(suite_top_dir, Config),
%% inets_test_lib:del_dirs(SuiteTopDir),
ok.
@@ -346,8 +344,8 @@ init_per_testcase2(Case, Config) ->
SslNormal = integer_to_list(?SSL_PORT) ++ ".conf",
SslHtaccess = integer_to_list(?SSL_PORT) ++ "htaccess.conf",
- DataDir = ?config(data_dir, Config),
- SuiteTopDir = ?config(suite_top_dir, Config),
+ DataDir = proplists:get_value(data_dir, Config),
+ SuiteTopDir = proplists:get_value(suite_top_dir, Config),
%% tsp("init_per_testcase2 -> "
%% "~n SuiteDir: ~p"
@@ -499,7 +497,7 @@ init_per_testcase3(Case, Config) ->
Dog = test_server:timetrap(inets_test_lib:minutes(10)),
NewConfig = lists:keydelete(watchdog, 1, Config),
- TcTopDir = ?config(tc_top_dir, Config),
+ TcTopDir = proplists:get_value(tc_top_dir, Config),
CaseRest =
case atom_to_list(Case) of
@@ -581,16 +579,16 @@ init_per_testcase3(Case, Config) ->
{skip, _} = Skip ->
Skip;
"mod_auth_" ++ _ ->
- start_mnesia(?config(node, Config)),
+ start_mnesia(proplists:get_value(node, Config)),
[{watchdog, Dog} | NewConfig];
"mod_htaccess" ->
- ServerRoot = ?config(server_root, Config),
+ ServerRoot = proplists:get_value(server_root, Config),
Path = filename:join([ServerRoot, "htdocs"]),
catch remove_htaccess(Path),
- create_htaccess_data(Path, ?config(address, Config)),
+ create_htaccess_data(Path, proplists:get_value(address, Config)),
[{watchdog, Dog} | NewConfig];
"range" ->
- ServerRoot = ?config(server_root, Config),
+ ServerRoot = proplists:get_value(server_root, Config),
Path = filename:join([ServerRoot, "htdocs"]),
create_range_data(Path),
[{watchdog, Dog} | NewConfig];
@@ -613,7 +611,7 @@ init_per_testcase3(Case, Config) ->
%% Description: Cleanup after each test case
%%--------------------------------------------------------------------
end_per_testcase(Case, Config) ->
- Dog = ?config(watchdog, Config),
+ Dog = proplists:get_value(watchdog, Config),
test_server:timetrap_cancel(Dog),
end_per_testcase2(Case, lists:keydelete(watchdog, 1, Config)),
ok.
@@ -641,7 +639,7 @@ ip_mod_alias(suite) ->
[];
ip_mod_alias(Config) when is_list(Config) ->
httpd_mod:alias(ip_comm, ?IP_PORT,
- ?config(host, Config), ?config(node, Config)),
+ proplists:get_value(host, Config), proplists:get_value(node, Config)),
ok.
%%-------------------------------------------------------------------------
@@ -651,7 +649,7 @@ ip_mod_actions(suite) ->
[];
ip_mod_actions(Config) when is_list(Config) ->
httpd_mod:actions(ip_comm, ?IP_PORT,
- ?config(host, Config), ?config(node, Config)),
+ proplists:get_value(host, Config), proplists:get_value(node, Config)),
ok.
%%-------------------------------------------------------------------------
@@ -660,9 +658,9 @@ ip_mod_security(doc) ->
ip_mod_security(suite) ->
[];
ip_mod_security(Config) when is_list(Config) ->
- ServerRoot = ?config(server_root, Config),
+ ServerRoot = proplists:get_value(server_root, Config),
httpd_mod:security(ServerRoot, ip_comm, ?IP_PORT,
- ?config(host, Config), ?config(node, Config)),
+ proplists:get_value(host, Config), proplists:get_value(node, Config)),
ok.
%%-------------------------------------------------------------------------
@@ -672,7 +670,7 @@ ip_mod_auth(suite) ->
[];
ip_mod_auth(Config) when is_list(Config) ->
httpd_mod:auth(ip_comm, ?IP_PORT,
- ?config(host, Config), ?config(node, Config)),
+ proplists:get_value(host, Config), proplists:get_value(node, Config)),
ok.
%%-------------------------------------------------------------------------
@@ -681,9 +679,9 @@ ip_mod_auth_api(doc) ->
ip_mod_auth_api(suite) ->
[];
ip_mod_auth_api(Config) when is_list(Config) ->
- ServerRoot = ?config(server_root, Config),
- Host = ?config(host, Config),
- Node = ?config(node, Config),
+ ServerRoot = proplists:get_value(server_root, Config),
+ Host = proplists:get_value(host, Config),
+ Node = proplists:get_value(node, Config),
httpd_mod:auth_api(ServerRoot, "", ip_comm, ?IP_PORT, Host, Node),
httpd_mod:auth_api(ServerRoot, "dets_", ip_comm, ?IP_PORT, Host, Node),
httpd_mod:auth_api(ServerRoot, "mnesia_", ip_comm, ?IP_PORT, Host, Node),
@@ -695,7 +693,7 @@ ip_mod_auth_mnesia_api(suite) ->
[];
ip_mod_auth_mnesia_api(Config) when is_list(Config) ->
httpd_mod:auth_mnesia_api(ip_comm, ?IP_PORT,
- ?config(host, Config), ?config(node, Config)),
+ proplists:get_value(host, Config), proplists:get_value(node, Config)),
ok.
%%-------------------------------------------------------------------------
ip_mod_htaccess(doc) ->
@@ -704,7 +702,7 @@ ip_mod_htaccess(suite) ->
[];
ip_mod_htaccess(Config) when is_list(Config) ->
httpd_mod:htaccess(ip_comm, ?IP_PORT,
- ?config(host, Config), ?config(node, Config)),
+ proplists:get_value(host, Config), proplists:get_value(node, Config)),
ok.
%%-------------------------------------------------------------------------
ip_mod_cgi(doc) ->
@@ -713,7 +711,7 @@ ip_mod_cgi(suite) ->
[];
ip_mod_cgi(Config) when is_list(Config) ->
httpd_mod:cgi(ip_comm, ?IP_PORT,
- ?config(host, Config), ?config(node, Config)),
+ proplists:get_value(host, Config), proplists:get_value(node, Config)),
ok.
%%-------------------------------------------------------------------------
ip_mod_esi(doc) ->
@@ -722,7 +720,7 @@ ip_mod_esi(suite) ->
[];
ip_mod_esi(Config) when is_list(Config) ->
httpd_mod:esi(ip_comm, ?IP_PORT,
- ?config(host, Config), ?config(node, Config)),
+ proplists:get_value(host, Config), proplists:get_value(node, Config)),
ok.
%%-------------------------------------------------------------------------
@@ -732,7 +730,7 @@ ip_mod_get(suite) ->
[];
ip_mod_get(Config) when is_list(Config) ->
httpd_mod:get(ip_comm, ?IP_PORT,
- ?config(host, Config), ?config(node, Config)),
+ proplists:get_value(host, Config), proplists:get_value(node, Config)),
ok.
%%-------------------------------------------------------------------------
@@ -742,7 +740,7 @@ ip_mod_head(suite) ->
[];
ip_mod_head(Config) when is_list(Config) ->
httpd_mod:head(ip_comm, ?IP_PORT,
- ?config(host, Config), ?config(node, Config)),
+ proplists:get_value(host, Config), proplists:get_value(node, Config)),
ok.
%%-------------------------------------------------------------------------
ip_mod_all(doc) ->
@@ -751,7 +749,7 @@ ip_mod_all(suite) ->
[];
ip_mod_all(Config) when is_list(Config) ->
httpd_mod:all(ip_comm, ?IP_PORT,
- ?config(host, Config), ?config(node, Config)),
+ proplists:get_value(host, Config), proplists:get_value(node, Config)),
ok.
%%-------------------------------------------------------------------------
ip_load_light(doc) ->
@@ -759,8 +757,8 @@ ip_load_light(doc) ->
ip_load_light(suite) ->
[];
ip_load_light(Config) when is_list(Config) ->
- httpd_load:load_test(ip_comm, ?IP_PORT, ?config(host, Config),
- ?config(node, Config),
+ httpd_load:load_test(ip_comm, ?IP_PORT, proplists:get_value(host, Config),
+ proplists:get_value(node, Config),
get_nof_clients(ip_comm, light)),
ok.
%%-------------------------------------------------------------------------
@@ -769,8 +767,8 @@ ip_load_medium(doc) ->
ip_load_medium(suite) ->
[];
ip_load_medium(Config) when is_list(Config) ->
- httpd_load:load_test(ip_comm, ?IP_PORT, ?config(host, Config),
- ?config(node, Config),
+ httpd_load:load_test(ip_comm, ?IP_PORT, proplists:get_value(host, Config),
+ proplists:get_value(node, Config),
get_nof_clients(ip_comm, medium)),
ok.
%%-------------------------------------------------------------------------
@@ -779,8 +777,8 @@ ip_load_heavy(doc) ->
ip_load_heavy(suite) ->
[];
ip_load_heavy(Config) when is_list(Config) ->
- httpd_load:load_test(ip_comm, ?IP_PORT, ?config(host, Config),
- ?config(node, Config),
+ httpd_load:load_test(ip_comm, ?IP_PORT, proplists:get_value(host, Config),
+ proplists:get_value(node, Config),
get_nof_clients(ip_comm, heavy)),
ok.
@@ -791,8 +789,8 @@ ip_dos_hostname(doc) ->
ip_dos_hostname(suite) ->
[];
ip_dos_hostname(Config) when is_list(Config) ->
- dos_hostname(ip_comm, ?IP_PORT, ?config(host, Config),
- ?config(node, Config), ?MAX_HEADER_SIZE),
+ dos_hostname(ip_comm, ?IP_PORT, proplists:get_value(host, Config),
+ proplists:get_value(node, Config), ?MAX_HEADER_SIZE),
ok.
@@ -802,13 +800,7 @@ ip_time_test(doc) ->
ip_time_test(suite) ->
[];
ip_time_test(Config) when is_list(Config) ->
- %% <CONDITIONAL-SKIP>
- Skippable = [win32],
- Condition = fun() -> ?OS_BASED_SKIP(Skippable) end,
- ?NON_PC_TC_MAYBE_SKIP(Config, Condition),
- %% </CONDITIONAL-SKIP>
-
- httpd_time_test:t(ip_comm, ?config(host, Config), ?IP_PORT),
+ httpd_time_test:t(ip_comm, proplists:get_value(host, Config), ?IP_PORT),
ok.
%%-------------------------------------------------------------------------
@@ -818,8 +810,8 @@ ip_block_503(doc) ->
ip_block_503(suite) ->
[];
ip_block_503(Config) when is_list(Config) ->
- httpd_block:block_503(ip_comm, ?IP_PORT, ?config(host, Config),
- ?config(node, Config)),
+ httpd_block:block_503(ip_comm, ?IP_PORT, proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
ok.
%%-------------------------------------------------------------------------
ip_block_disturbing_idle(doc) ->
@@ -829,8 +821,8 @@ ip_block_disturbing_idle(suite) ->
[];
ip_block_disturbing_idle(Config) when is_list(Config) ->
httpd_block:block_disturbing_idle(ip_comm, ?IP_PORT,
- ?config(host, Config),
- ?config(node, Config)),
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
ok.
%%-------------------------------------------------------------------------
ip_block_non_disturbing_idle(doc) ->
@@ -840,8 +832,8 @@ ip_block_non_disturbing_idle(suite) ->
[];
ip_block_non_disturbing_idle(Config) when is_list(Config) ->
httpd_block:block_non_disturbing_idle(ip_comm, ?IP_PORT,
- ?config(host, Config),
- ?config(node, Config)),
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
ok.
%%-------------------------------------------------------------------------
ip_block_disturbing_active(doc) ->
@@ -851,8 +843,8 @@ ip_block_disturbing_active(suite) ->
[];
ip_block_disturbing_active(Config) when is_list(Config) ->
httpd_block:block_disturbing_active(ip_comm, ?IP_PORT,
- ?config(host, Config),
- ?config(node, Config)),
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
ok.
%%-------------------------------------------------------------------------
ip_block_non_disturbing_active(doc) ->
@@ -862,8 +854,8 @@ ip_block_non_disturbing_active(suite) ->
[];
ip_block_non_disturbing_active(Config) when is_list(Config) ->
httpd_block:block_non_disturbing_idle(ip_comm, ?IP_PORT,
- ?config(host, Config),
- ?config(node, Config)),
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
ok.
%%-------------------------------------------------------------------------
@@ -877,9 +869,9 @@ ip_block_disturbing_active_timeout_not_released(Config)
when is_list(Config) ->
httpd_block:block_disturbing_active_timeout_not_released(ip_comm,
?IP_PORT,
- ?config(host,
+ proplists:get_value(host,
Config),
- ?config(node,
+ proplists:get_value(node,
Config)),
ok.
%%-------------------------------------------------------------------------
@@ -893,9 +885,9 @@ ip_block_disturbing_active_timeout_released(Config)
when is_list(Config) ->
httpd_block:block_disturbing_active_timeout_released(ip_comm,
?IP_PORT,
- ?config(host,
+ proplists:get_value(host,
Config),
- ?config(node,
+ proplists:get_value(node,
Config)),
ok.
@@ -910,9 +902,9 @@ ip_block_non_disturbing_active_timeout_not_released(Config)
httpd_block:
block_non_disturbing_active_timeout_not_released(ip_comm,
?IP_PORT,
- ?config(host,
+ proplists:get_value(host,
Config),
- ?config(node,
+ proplists:get_value(node,
Config)),
ok.
%%-------------------------------------------------------------------------
@@ -927,9 +919,9 @@ ip_block_non_disturbing_active_timeout_released(Config)
httpd_block:
block_non_disturbing_active_timeout_released(ip_comm,
?IP_PORT,
- ?config(host,
+ proplists:get_value(host,
Config),
- ?config(node,
+ proplists:get_value(node,
Config)),
ok.
%%-------------------------------------------------------------------------
@@ -939,8 +931,8 @@ ip_block_disturbing_blocker_dies(suite) ->
[];
ip_block_disturbing_blocker_dies(Config) when is_list(Config) ->
httpd_block:disturbing_blocker_dies(ip_comm, ?IP_PORT,
- ?config(host, Config),
- ?config(node, Config)),
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
ok.
%%-------------------------------------------------------------------------
ip_block_non_disturbing_blocker_dies(doc) ->
@@ -949,8 +941,8 @@ ip_block_non_disturbing_blocker_dies(suite) ->
[];
ip_block_non_disturbing_blocker_dies(Config) when is_list(Config) ->
httpd_block:non_disturbing_blocker_dies(ip_comm, ?IP_PORT,
- ?config(host, Config),
- ?config(node, Config)),
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
ok.
%%-------------------------------------------------------------------------
ip_restart_no_block(doc) ->
@@ -958,8 +950,8 @@ ip_restart_no_block(doc) ->
ip_restart_no_block(suite) ->
[];
ip_restart_no_block(Config) when is_list(Config) ->
- httpd_block:restart_no_block(ip_comm, ?IP_PORT, ?config(host, Config),
- ?config(node, Config)),
+ httpd_block:restart_no_block(ip_comm, ?IP_PORT, proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
ok.
%%-------------------------------------------------------------------------
ip_restart_disturbing_block(doc) ->
@@ -967,33 +959,9 @@ ip_restart_disturbing_block(doc) ->
ip_restart_disturbing_block(suite) ->
[];
ip_restart_disturbing_block(Config) when is_list(Config) ->
- %% <CONDITIONAL-SKIP>
- Condition =
- fun() ->
- case os:type() of
- {unix, linux} ->
- HW = string:strip(os:cmd("uname -m"), right, $\n),
- case HW of
- "ppc" ->
- case inet:gethostname() of
- {ok, "peach"} ->
- true;
- _ ->
- false
- end;
- _ ->
- false
- end;
- _ ->
- false
- end
- end,
- ?NON_PC_TC_MAYBE_SKIP(Config, Condition),
- %% </CONDITIONAL-SKIP>
-
httpd_block:restart_disturbing_block(ip_comm, ?IP_PORT,
- ?config(host, Config),
- ?config(node, Config)),
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
ok.
%%-------------------------------------------------------------------------
@@ -1002,33 +970,9 @@ ip_restart_non_disturbing_block(doc) ->
ip_restart_non_disturbing_block(suite) ->
[];
ip_restart_non_disturbing_block(Config) when is_list(Config) ->
- %% <CONDITIONAL-SKIP>
- Condition =
- fun() ->
- case os:type() of
- {unix, linux} ->
- HW = string:strip(os:cmd("uname -m"), right, $\n),
- case HW of
- "ppc" ->
- case inet:gethostname() of
- {ok, "peach"} ->
- true;
- _ ->
- false
- end;
- _ ->
- false
- end;
- _ ->
- false
- end
- end,
- ?NON_PC_TC_MAYBE_SKIP(Config, Condition),
- %% </CONDITIONAL-SKIP>
-
httpd_block:restart_non_disturbing_block(ip_comm, ?IP_PORT,
- ?config(host, Config),
- ?config(node, Config)),
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
ok.
%%-------------------------------------------------------------------------
@@ -1043,7 +987,7 @@ essl_mod_alias(Config) when is_list(Config) ->
ssl_mod_alias(Tag, Config) ->
httpd_mod:alias(Tag, ?SSL_PORT,
- ?config(host, Config), ?config(node, Config)),
+ proplists:get_value(host, Config), proplists:get_value(node, Config)),
ok.
@@ -1060,8 +1004,8 @@ essl_mod_actions(Config) when is_list(Config) ->
ssl_mod_actions(Tag, Config) ->
httpd_mod:actions(Tag,
?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config)),
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
ok.
@@ -1075,12 +1019,12 @@ essl_mod_security(Config) when is_list(Config) ->
ssl_mod_security(essl, Config).
ssl_mod_security(Tag, Config) ->
- ServerRoot = ?config(server_root, Config),
+ ServerRoot = proplists:get_value(server_root, Config),
httpd_mod:security(ServerRoot,
Tag,
?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config)),
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
ok.
@@ -1096,8 +1040,8 @@ essl_mod_auth(Config) when is_list(Config) ->
ssl_mod_auth(Tag, Config) ->
httpd_mod:auth(Tag,
?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config)),
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
ok.
@@ -1112,9 +1056,9 @@ essl_mod_auth_api(Config) when is_list(Config) ->
ssl_mod_auth_api(essl, Config).
ssl_mod_auth_api(Tag, Config) ->
- ServerRoot = ?config(server_root, Config),
- Host = ?config(host, Config),
- Node = ?config(node, Config),
+ ServerRoot = proplists:get_value(server_root, Config),
+ Host = proplists:get_value(host, Config),
+ Node = proplists:get_value(node, Config),
httpd_mod:auth_api(ServerRoot, "", Tag, ?SSL_PORT, Host, Node),
httpd_mod:auth_api(ServerRoot, "dets_", Tag, ?SSL_PORT, Host, Node),
httpd_mod:auth_api(ServerRoot, "mnesia_", Tag, ?SSL_PORT, Host, Node),
@@ -1134,8 +1078,8 @@ essl_mod_auth_mnesia_api(Config) when is_list(Config) ->
ssl_mod_auth_mnesia_api(Tag, Config) ->
httpd_mod:auth_mnesia_api(Tag,
?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config)),
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
ok.
@@ -1151,8 +1095,8 @@ essl_mod_htaccess(Config) when is_list(Config) ->
ssl_mod_htaccess(Tag, Config) ->
httpd_mod:htaccess(Tag,
?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config)),
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
ok.
@@ -1168,8 +1112,8 @@ essl_mod_cgi(Config) when is_list(Config) ->
ssl_mod_cgi(Tag, Config) ->
httpd_mod:cgi(Tag,
?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config)),
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
ok.
@@ -1185,8 +1129,8 @@ essl_mod_esi(Config) when is_list(Config) ->
ssl_mod_esi(Tag, Config) ->
httpd_mod:esi(Tag,
?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config)),
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
ok.
@@ -1202,8 +1146,8 @@ essl_mod_get(Config) when is_list(Config) ->
ssl_mod_get(Tag, Config) ->
httpd_mod:get(Tag,
?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config)),
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
ok.
@@ -1219,8 +1163,8 @@ essl_mod_head(Config) when is_list(Config) ->
ssl_mod_head(Tag, Config) ->
httpd_mod:head(Tag,
?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config)),
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
ok.
@@ -1236,8 +1180,8 @@ essl_mod_all(Config) when is_list(Config) ->
ssl_mod_all(Tag, Config) ->
httpd_mod:all(Tag,
?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config)),
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
ok.
@@ -1253,8 +1197,8 @@ essl_load_light(Config) when is_list(Config) ->
ssl_load_light(Tag, Config) ->
httpd_load:load_test(Tag,
?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config),
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config),
get_nof_clients(ssl, light)),
ok.
@@ -1269,16 +1213,10 @@ essl_load_medium(Config) when is_list(Config) ->
ssl_load_medium(essl, Config).
ssl_load_medium(Tag, Config) ->
- %% <CONDITIONAL-SKIP>
- Skippable = [win32],
- Condition = fun() -> ?OS_BASED_SKIP(Skippable) end,
- ?NON_PC_TC_MAYBE_SKIP(Config, Condition),
- %% </CONDITIONAL-SKIP>
-
httpd_load:load_test(Tag,
?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config),
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config),
get_nof_clients(ssl, medium)),
ok.
@@ -1293,16 +1231,10 @@ essl_load_heavy(Config) when is_list(Config) ->
ssl_load_heavy(essl, Config).
ssl_load_heavy(Tag, Config) ->
- %% <CONDITIONAL-SKIP>
- Skippable = [win32],
- Condition = fun() -> ?OS_BASED_SKIP(Skippable) end,
- ?NON_PC_TC_MAYBE_SKIP(Config, Condition),
- %% </CONDITIONAL-SKIP>
-
httpd_load:load_test(Tag,
?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config),
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config),
get_nof_clients(ssl, heavy)),
ok.
@@ -1320,8 +1252,8 @@ essl_dos_hostname(Config) when is_list(Config) ->
ssl_dos_hostname(Tag, Config) ->
dos_hostname(Tag,
?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config),
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config),
?MAX_HEADER_SIZE),
ok.
@@ -1337,23 +1269,8 @@ essl_time_test(Config) when is_list(Config) ->
ssl_time_test(essl, Config).
ssl_time_test(Tag, Config) when is_list(Config) ->
- %% <CONDITIONAL-SKIP>
- FreeBSDVersionVerify =
- fun() ->
- case os:version() of
- {7, 1, _} -> % We only have one such machine, so...
- true;
- _ ->
- false
- end
- end,
- Skippable = [win32, {unix, [{freebsd, FreeBSDVersionVerify}]}],
- Condition = fun() -> ?OS_BASED_SKIP(Skippable) end,
- ?NON_PC_TC_MAYBE_SKIP(Config, Condition),
- %% </CONDITIONAL-SKIP>
-
httpd_time_test:t(Tag,
- ?config(host, Config),
+ proplists:get_value(host, Config),
?SSL_PORT),
ok.
@@ -1372,8 +1289,8 @@ essl_block_503(Config) when is_list(Config) ->
ssl_block_503(Tag, Config) ->
httpd_block:block_503(Tag,
?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config)),
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
ok.
@@ -1391,8 +1308,8 @@ essl_block_disturbing_idle(Config) when is_list(Config) ->
ssl_block_disturbing_idle(Tag, Config) ->
httpd_block:block_disturbing_idle(Tag,
?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config)),
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
ok.
@@ -1410,8 +1327,8 @@ essl_block_non_disturbing_idle(Config) when is_list(Config) ->
ssl_block_non_disturbing_idle(Tag, Config) ->
httpd_block:block_non_disturbing_idle(Tag,
?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config)),
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
ok.
@@ -1429,8 +1346,8 @@ essl_block_disturbing_active(Config) when is_list(Config) ->
ssl_block_disturbing_active(Tag, Config) ->
httpd_block:block_disturbing_active(Tag,
?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config)),
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
ok.
@@ -1448,8 +1365,8 @@ essl_block_non_disturbing_active(Config) when is_list(Config) ->
ssl_block_non_disturbing_active(Tag, Config) ->
httpd_block:block_non_disturbing_idle(Tag,
?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config)),
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
ok.
@@ -1468,8 +1385,8 @@ essl_block_disturbing_active_timeout_not_released(Config)
ssl_block_disturbing_active_timeout_not_released(Tag, Config) ->
Port = ?SSL_PORT,
- Host = ?config(host, Config),
- Node = ?config(node, Config),
+ Host = proplists:get_value(host, Config),
+ Node = proplists:get_value(node, Config),
httpd_block:block_disturbing_active_timeout_not_released(Tag,
Port, Host, Node),
ok.
@@ -1490,8 +1407,8 @@ essl_block_disturbing_active_timeout_released(Config)
ssl_block_disturbing_active_timeout_released(Tag, Config) ->
Port = ?SSL_PORT,
- Host = ?config(host, Config),
- Node = ?config(node, Config),
+ Host = proplists:get_value(host, Config),
+ Node = proplists:get_value(node, Config),
httpd_block:block_disturbing_active_timeout_released(Tag,
Port,
Host,
@@ -1513,8 +1430,8 @@ essl_block_non_disturbing_active_timeout_not_released(Config)
ssl_block_non_disturbing_active_timeout_not_released(Tag, Config) ->
Port = ?SSL_PORT,
- Host = ?config(host, Config),
- Node = ?config(node, Config),
+ Host = proplists:get_value(host, Config),
+ Node = proplists:get_value(node, Config),
httpd_block:block_non_disturbing_active_timeout_not_released(Tag,
Port,
Host,
@@ -1539,8 +1456,8 @@ essl_block_non_disturbing_active_timeout_released(Config)
ssl_block_non_disturbing_active_timeout_released(Tag, Config)
when is_list(Config) ->
Port = ?SSL_PORT,
- Host = ?config(host, Config),
- Node = ?config(node, Config),
+ Host = proplists:get_value(host, Config),
+ Node = proplists:get_value(node, Config),
httpd_block:block_non_disturbing_active_timeout_released(Tag,
Port,
Host,
@@ -1562,8 +1479,8 @@ essl_block_disturbing_blocker_dies(Config) when is_list(Config) ->
ssl_block_disturbing_blocker_dies(Tag, Config) ->
httpd_block:disturbing_blocker_dies(Tag,
?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config)),
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
ok.
@@ -1579,8 +1496,8 @@ essl_block_non_disturbing_blocker_dies(Config) when is_list(Config) ->
ssl_block_non_disturbing_blocker_dies(Tag, Config) ->
httpd_block:non_disturbing_blocker_dies(Tag,
?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config)),
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
ok.
@@ -1597,8 +1514,8 @@ essl_restart_no_block(Config) when is_list(Config) ->
ssl_restart_no_block(Tag, Config) ->
httpd_block:restart_no_block(Tag,
?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config)),
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
ok.
@@ -1613,43 +1530,9 @@ essl_restart_disturbing_block(Config) when is_list(Config) ->
ssl_restart_disturbing_block(essl, Config).
ssl_restart_disturbing_block(Tag, Config) ->
- %% <CONDITIONAL-SKIP>
- Condition =
- fun() ->
- case os:type() of
- {unix, linux} ->
- case ?OSCMD("uname -m") of
- "ppc" ->
- case file:read_file_info("/etc/fedora-release") of
- {ok, _} ->
- case ?OSCMD("awk '{print $2}' /etc/fedora-release") of
- "release" ->
- %% Fedora 7 and later
- case ?OSCMD("awk '{print $3}' /etc/fedora-release") of
- "7" ->
- true;
- _ ->
- false
- end;
- _ ->
- false
- end;
- _ ->
- false
- end;
- _ ->
- false
- end;
- _ ->
- false
- end
- end,
- ?NON_PC_TC_MAYBE_SKIP(Config, Condition),
- %% </CONDITIONAL-SKIP>
-
httpd_block:restart_disturbing_block(Tag, ?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config)),
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
ok.
@@ -1664,34 +1547,10 @@ essl_restart_non_disturbing_block(Config) when is_list(Config) ->
ssl_restart_non_disturbing_block(essl, Config).
ssl_restart_non_disturbing_block(Tag, Config) ->
- %% <CONDITIONAL-SKIP>
- Condition =
- fun() ->
- case os:type() of
- {unix, linux} ->
- HW = string:strip(os:cmd("uname -m"), right, $\n),
- case HW of
- "ppc" ->
- case inet:gethostname() of
- {ok, "peach"} ->
- true;
- _ ->
- false
- end;
- _ ->
- false
- end;
- _ ->
- false
- end
- end,
- ?NON_PC_TC_MAYBE_SKIP(Config, Condition),
- %% </CONDITIONAL-SKIP>
-
httpd_block:restart_non_disturbing_block(Tag,
?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config)),
+ proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
ok.
@@ -1701,8 +1560,8 @@ ip_host(doc) ->
ip_host(suite)->
[];
ip_host(Config) when is_list(Config) ->
- httpd_1_1:host(ip_comm, ?IP_PORT, ?config(host, Config),
- ?config(node, Config)),
+ httpd_1_1:host(ip_comm, ?IP_PORT, proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
ok.
%%-------------------------------------------------------------------------
ip_chunked(doc) ->
@@ -1710,8 +1569,8 @@ ip_chunked(doc) ->
ip_chunked(suite) ->
[];
ip_chunked(Config) when is_list(Config) ->
- httpd_1_1:chunked(ip_comm, ?IP_PORT, ?config(host, Config),
- ?config(node, Config)),
+ httpd_1_1:chunked(ip_comm, ?IP_PORT, proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
ok.
%%-------------------------------------------------------------------------
ip_expect(doc) ->
@@ -1720,8 +1579,8 @@ ip_expect(doc) ->
ip_expect(suite)->
[];
ip_expect(Config) when is_list(Config) ->
- httpd_1_1:expect(ip_comm, ?IP_PORT, ?config(host, Config),
- ?config(node, Config)),
+ httpd_1_1:expect(ip_comm, ?IP_PORT, proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
ok.
%%-------------------------------------------------------------------------
ip_range(doc) ->
@@ -1729,8 +1588,8 @@ ip_range(doc) ->
ip_range(suite)->
[];
ip_range(Config) when is_list(Config) ->
- httpd_1_1:range(ip_comm, ?IP_PORT, ?config(host, Config),
- ?config(node, Config)),
+ httpd_1_1:range(ip_comm, ?IP_PORT, proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
ok.
%%-------------------------------------------------------------------------
ip_if_test(doc) ->
@@ -1738,10 +1597,10 @@ ip_if_test(doc) ->
ip_if_test(suite) ->
[];
ip_if_test(Config) when is_list(Config) ->
- ServerRoot = ?config(server_root, Config),
+ ServerRoot = proplists:get_value(server_root, Config),
DocRoot = filename:join([ServerRoot, "htdocs"]),
- httpd_1_1:if_test(ip_comm, ?IP_PORT, ?config(host, Config),
- ?config(node, Config), DocRoot),
+ httpd_1_1:if_test(ip_comm, ?IP_PORT, proplists:get_value(host, Config),
+ proplists:get_value(node, Config), DocRoot),
ok.
%%-------------------------------------------------------------------------
ip_http_trace(doc) ->
@@ -1749,8 +1608,8 @@ ip_http_trace(doc) ->
ip_http_trace(suite) ->
[];
ip_http_trace(Config) when is_list(Config) ->
- httpd_1_1:http_trace(ip_comm, ?IP_PORT, ?config(host, Config),
- ?config(node, Config)),
+ httpd_1_1:http_trace(ip_comm, ?IP_PORT, proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
ok.
%%-------------------------------------------------------------------------
ip_http1_1_head(doc) ->
@@ -1758,8 +1617,8 @@ ip_http1_1_head(doc) ->
ip_http1_1_head(suite)->
[];
ip_http1_1_head(Config) when is_list(Config) ->
- httpd_1_1:head(ip_comm, ?IP_PORT, ?config(host, Config),
- ?config(node, Config)),
+ httpd_1_1:head(ip_comm, ?IP_PORT, proplists:get_value(host, Config),
+ proplists:get_value(node, Config)),
ok.
%%-------------------------------------------------------------------------
@@ -1768,8 +1627,8 @@ ip_get_0_9(doc) ->
ip_get_0_9(suite)->
[];
ip_get_0_9(Config) when is_list(Config) ->
- Host = ?config(host, Config),
- Node = ?config(node, Config),
+ Host = proplists:get_value(host, Config),
+ Node = proplists:get_value(node, Config),
ok = httpd_test_lib:verify_request(ip_comm, Host, ?IP_PORT, Node,
"GET / \r\n\r\n",
[{statuscode, 200},
@@ -1791,8 +1650,8 @@ ip_head_1_0(doc) ->
ip_head_1_0(suite)->
[];
ip_head_1_0(Config) when is_list(Config) ->
- Host = ?config(host, Config),
- Node = ?config(node, Config),
+ Host = proplists:get_value(host, Config),
+ Node = proplists:get_value(node, Config),
ok = httpd_test_lib:verify_request(ip_comm, Host, ?IP_PORT, Node,
"HEAD / HTTP/1.0\r\n\r\n", [{statuscode, 200},
{version, "HTTP/1.0"}]),
@@ -1804,8 +1663,8 @@ ip_get_1_0(doc) ->
ip_get_1_0(suite)->
[];
ip_get_1_0(Config) when is_list(Config) ->
- Host = ?config(host, Config),
- Node = ?config(node, Config),
+ Host = proplists:get_value(host, Config),
+ Node = proplists:get_value(node, Config),
ok = httpd_test_lib:verify_request(ip_comm, Host, ?IP_PORT, Node,
"GET / HTTP/1.0\r\n\r\n", [{statuscode, 200},
{version, "HTTP/1.0"}]),
@@ -1817,8 +1676,8 @@ ip_post_1_0(doc) ->
ip_post_1_0(suite)->
[];
ip_post_1_0(Config) when is_list(Config) ->
- Host = ?config(host, Config),
- Node = ?config(node, Config),
+ Host = proplists:get_value(host, Config),
+ Node = proplists:get_value(node, Config),
%% Test the post message formatin 1.0! Real post are testes elsewhere
ok = httpd_test_lib:verify_request(ip_comm, Host, ?IP_PORT, Node,
"POST / HTTP/1.0\r\n\r\n "
@@ -1832,7 +1691,7 @@ ip_mod_cgi_chunked_encoding_test(doc) ->
ip_mod_cgi_chunked_encoding_test(suite)->
[];
ip_mod_cgi_chunked_encoding_test(Config) when is_list(Config) ->
- Host = ?config(host, Config),
+ Host = proplists:get_value(host, Config),
Script =
case test_server:os_type() of
{win32, _} ->
@@ -1846,7 +1705,7 @@ ip_mod_cgi_chunked_encoding_test(Config) when is_list(Config) ->
++ Host ++"\r\n\r\n"],
httpd_1_1:mod_cgi_chunked_encoding_test(ip_comm, ?IP_PORT,
Host,
- ?config(node, Config),
+ proplists:get_value(node, Config),
Requests),
ok.
@@ -1875,7 +1734,7 @@ ipv6_hostname(SocketType, Port, Config) when is_list(Config) ->
"~n SocketType: ~p"
"~n Port: ~p"
"~n Config: ~p", [SocketType, Port, Config]),
- Host = ?config(host, Config),
+ Host = proplists:get_value(host, Config),
URI = "GET HTTP://" ++
Host ++ ":" ++ integer_to_list(Port) ++ "/ HTTP/1.1\r\n\r\n",
tsp("ipv6_hostname -> Host: ~p", [Host]),
@@ -1910,7 +1769,7 @@ ipv6_address(SocketType, Port, Config) when is_list(Config) ->
"~n SocketType: ~p"
"~n Port: ~p"
"~n Config: ~p", [SocketType, Port, Config]),
- Host = ?config(host, Config),
+ Host = proplists:get_value(host, Config),
tsp("ipv6_address -> Host: ~p", [Host]),
URI = "GET HTTP://" ++
Host ++ ":" ++ integer_to_list(Port) ++ "/ HTTP/1.1\r\n\r\n",
@@ -1927,8 +1786,8 @@ ticket_5775(doc) ->
ticket_5775(suite) ->
[];
ticket_5775(Config) ->
- ok=httpd_test_lib:verify_request(ip_comm, ?config(host, Config),
- ?IP_PORT, ?config(node, Config),
+ ok=httpd_test_lib:verify_request(ip_comm, proplists:get_value(host, Config),
+ ?IP_PORT, proplists:get_value(node, Config),
"GET /cgi-bin/erl/httpd_example:get_bin "
"HTTP/1.0\r\n\r\n",
[{statuscode, 200},
@@ -1939,9 +1798,9 @@ ticket_5865(doc) ->
ticket_5865(suite) ->
[];
ticket_5865(Config) ->
- ?SKIP(as_of_r15_behaviour_of_calendar_has_changed),
- Host = ?config(host,Config),
- ServerRoot = ?config(server_root, Config),
+ ct:skip(as_of_r15_behaviour_of_calendar_has_changed),
+ Host = proplists:get_value(host,Config),
+ ServerRoot = proplists:get_value(server_root, Config),
DocRoot = filename:join([ServerRoot, "htdocs"]),
File = filename:join([DocRoot,"last_modified.html"]),
@@ -1957,7 +1816,7 @@ ticket_5865(Config) ->
case file:write_file_info(File,FI#file_info{mtime=Bad_mtime}) of
ok ->
ok = httpd_test_lib:verify_request(ip_comm, Host,
- ?IP_PORT, ?config(node, Config),
+ ?IP_PORT, proplists:get_value(node, Config),
"GET /last_modified.html"
" HTTP/1.1\r\nHost:"
++Host++"\r\n\r\n",
@@ -1977,8 +1836,8 @@ ticket_5913(doc) ->
["Tests that a header without last-modified is handled"];
ticket_5913(suite) -> [];
ticket_5913(Config) ->
- ok = httpd_test_lib:verify_request(ip_comm, ?config(host, Config),
- ?IP_PORT, ?config(node, Config),
+ ok = httpd_test_lib:verify_request(ip_comm, proplists:get_value(host, Config),
+ ?IP_PORT, proplists:get_value(node, Config),
"GET /cgi-bin/erl/httpd_example:get_bin "
"HTTP/1.0\r\n\r\n",
[{statuscode, 200},
@@ -1989,8 +1848,8 @@ ticket_6003(doc) ->
["Tests that a URI with a bad hexadecimal code is handled"];
ticket_6003(suite) -> [];
ticket_6003(Config) ->
- ok = httpd_test_lib:verify_request(ip_comm, ?config(host, Config),
- ?IP_PORT, ?config(node, Config),
+ ok = httpd_test_lib:verify_request(ip_comm, proplists:get_value(host, Config),
+ ?IP_PORT, proplists:get_value(node, Config),
"GET http://www.erlang.org/%skalle "
"HTTP/1.0\r\n\r\n",
[{statuscode, 400},
@@ -2002,8 +1861,8 @@ ticket_7304(doc) ->
ticket_7304(suite) ->
[];
ticket_7304(Config) ->
- ok = httpd_test_lib:verify_request(ip_comm, ?config(host, Config),
- ?IP_PORT, ?config(node, Config),
+ ok = httpd_test_lib:verify_request(ip_comm, proplists:get_value(host, Config),
+ ?IP_PORT, proplists:get_value(node, Config),
"GET / HTTP/1.0\r\n\n",
[{statuscode, 200},
{version, "HTTP/1.0"}]),
@@ -2030,11 +1889,11 @@ dos_hostname(Type, Port, Host, Node, Max) ->
%%--------------------------------------------------------------------
%% Other help functions
create_config(Config, Access, FileName) ->
- ServerRoot = ?config(server_root, Config),
- TcTopDir = ?config(tc_top_dir, Config),
- Port = ?config(port, Config),
- Type = ?config(sock_type, Config),
- Host = ?config(host, Config),
+ ServerRoot = proplists:get_value(server_root, Config),
+ TcTopDir = proplists:get_value(tc_top_dir, Config),
+ Port = proplists:get_value(port, Config),
+ Type = proplists:get_value(sock_type, Config),
+ Host = proplists:get_value(host, Config),
Mods = io_lib:format("~p", [httpd_mod]),
Funcs = io_lib:format("~p", [ssl_password_cb]),
MaxHdrSz = io_lib:format("~p", [256]),
@@ -2424,13 +2283,13 @@ create_range_data(Path) ->
"12345678901234567890"])).
create_ipv6_config(Config, FileName, Ipv6Address) ->
- ServerRoot = ?config(server_root, Config),
- TcTopDir = ?config(tc_top_dir, Config),
- Port = ?config(port, Config),
- SockType = ?config(sock_type, Config),
+ ServerRoot = proplists:get_value(server_root, Config),
+ TcTopDir = proplists:get_value(tc_top_dir, Config),
+ Port = proplists:get_value(port, Config),
+ SockType = proplists:get_value(sock_type, Config),
Mods = io_lib:format("~p", [httpd_mod]),
Funcs = io_lib:format("~p", [ssl_password_cb]),
- Host = ?config(ipv6_host, Config),
+ Host = proplists:get_value(ipv6_host, Config),
MaxHdrSz = io_lib:format("~p", [256]),
MaxHdrAct = io_lib:format("~p", [close]),
diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml
index bca04aa244..5ff167bcb3 100644
--- a/lib/kernel/doc/src/inet.xml
+++ b/lib/kernel/doc/src/inet.xml
@@ -11,7 +11,7 @@
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
-
+
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
@@ -207,6 +207,10 @@ fe80::204:acff:fe17:bf38
<desc>
<p>Returns a <c>hostent</c> record for the host with the specified
hostname.</p>
+ <p>If resolver option <c>inet6</c> is <c>true</c>,
+ an IPv6 address is looked up. If that fails,
+ the IPv4 address is looked up and returned on
+ IPv6-mapped IPv4 format.</p>
</desc>
</func>
@@ -1267,4 +1271,3 @@ inet:setopts(Sock,[{raw,6,8,<<30:32/native>>}]),]]></code>
</list>
</section>
</erlref>
-
diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl
index c1ae99ea24..713a9cf725 100644
--- a/lib/kernel/src/inet.erl
+++ b/lib/kernel/src/inet.erl
@@ -439,7 +439,12 @@ getstat(Socket,What) ->
Hostent :: hostent().
gethostbyname(Name) ->
- gethostbyname_tm(Name, inet, false).
+ case inet_db:res_option(inet6) of
+ true ->
+ gethostbyname_tm(Name, inet6, false);
+ false ->
+ gethostbyname_tm(Name, inet, false)
+ end.
-spec gethostbyname(Hostname, Family) ->
{ok, Hostent} | {error, posix()} when
diff --git a/lib/kernel/src/inet_tcp_dist.erl b/lib/kernel/src/inet_tcp_dist.erl
index 64b28bb49b..f91d7ef7c3 100644
--- a/lib/kernel/src/inet_tcp_dist.erl
+++ b/lib/kernel/src/inet_tcp_dist.erl
@@ -73,7 +73,8 @@ gen_listen(Driver, Name) ->
{ok, Socket} ->
TcpAddress = get_tcp_address(Driver, Socket),
{_,Port} = TcpAddress#net_address.address,
- case erl_epmd:register_node(Name, Port) of
+ ErlEpmd = net_kernel:epmd_module(),
+ case ErlEpmd:register_node(Name, Port) of
{ok, Creation} ->
{ok, {Socket, TcpAddress, Creation}};
Error ->
@@ -280,7 +281,8 @@ do_setup(Driver, Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) ->
case inet:getaddr(Address, AddressFamily) of
{ok, Ip} ->
Timer = dist_util:start_timer(SetupTime),
- case erl_epmd:port_please(Name, Ip) of
+ ErlEpmd = net_kernel:epmd_module(),
+ case ErlEpmd:port_please(Name, Ip) of
{port, TcpPort, Version} ->
?trace("port_please(~p) -> version ~p~n",
[Node,Version]),
diff --git a/lib/kernel/src/net_adm.erl b/lib/kernel/src/net_adm.erl
index a38356c224..8ec275b88b 100644
--- a/lib/kernel/src/net_adm.erl
+++ b/lib/kernel/src/net_adm.erl
@@ -96,7 +96,8 @@ names() ->
Reason :: address | file:posix().
names(Hostname) ->
- erl_epmd:names(Hostname).
+ ErlEpmd = net_kernel:epmd_module(),
+ ErlEpmd:names(Hostname).
-spec dns_hostname(Host) -> {ok, Name} | {error, Host} when
Host :: atom() | string(),
diff --git a/lib/kernel/test/init_SUITE.erl b/lib/kernel/test/init_SUITE.erl
index e7b44a714c..a332e7966b 100644
--- a/lib/kernel/test/init_SUITE.erl
+++ b/lib/kernel/test/init_SUITE.erl
@@ -27,7 +27,7 @@
-export([get_arguments/1, get_argument/1, boot_var/1, restart/1,
many_restarts/0, many_restarts/1,
get_plain_arguments/1,
- reboot/1, stop/1, get_status/1, script_id/1]).
+ reboot/1, stop_status/1, stop/1, get_status/1, script_id/1]).
-export([boot1/1, boot2/1]).
-export([init_per_testcase/2, end_per_testcase/2]).
@@ -48,7 +48,7 @@ suite() ->
all() ->
[get_arguments, get_argument, boot_var,
many_restarts,
- get_plain_arguments, restart, get_status, script_id,
+ get_plain_arguments, restart, stop_status, get_status, script_id,
{group, boot}].
groups() ->
@@ -465,6 +465,20 @@ reboot(Config) when is_list(Config) ->
%% ------------------------------------------------
%%
%% ------------------------------------------------
+stop_status(Config) when is_list(Config) ->
+ badarg = catch_stop([65,[66],67]), % flat strings only
+ badarg = catch_stop([65, 666, 67]), % only bytes in string
+ badarg = catch_stop(abort), % 'abort' not allowed
+ badarg = catch_stop(true), % other atoms not allowed
+ badarg = catch_stop(-1), % no negative statuses
+ ok.
+
+catch_stop(Status) ->
+ try init:stop(Status) catch error:badarg -> badarg end.
+
+%% ------------------------------------------------
+%%
+%% ------------------------------------------------
stop(Config) when is_list(Config) ->
Args = args(),
{ok, Node} = start_node(init_test, Args),
diff --git a/lib/mnesia/src/mnesia_index.erl b/lib/mnesia/src/mnesia_index.erl
index 73d170d1fa..c79f790973 100644
--- a/lib/mnesia/src/mnesia_index.erl
+++ b/lib/mnesia/src/mnesia_index.erl
@@ -513,11 +513,11 @@ db_put({dets, Ixt}, V) ->
ok = dets:insert(Ixt, V).
db_get({ram, _}=Ixt, IxKey) ->
- Pat = [{{{IxKey, '$1'}}, [], [{{IxKey,'$1'}}]}],
+ Pat = [{{{IxKey, '$1'}}, [], [{element, 1, '$_'}]}],
db_select(Ixt, Pat);
db_get({{ext,_,_} = _Storage, {_,_,{_,Type}}} = Ixt, IxKey) ->
Pat = case Type of
- ordered -> [{{{IxKey, '$1'}}, [], [{{IxKey,'$1'}}]}];
+ ordered -> [{{{IxKey, '$1'}}, [], [{element, 1, '$_'}]}];
bag -> [{{IxKey, '_'}, [], ['$_']}]
end,
db_select(Ixt, Pat);
diff --git a/lib/mnesia/test/mnesia_dirty_access_test.erl b/lib/mnesia/test/mnesia_dirty_access_test.erl
index c9df8ed353..6d970ac990 100644
--- a/lib/mnesia/test/mnesia_dirty_access_test.erl
+++ b/lib/mnesia/test/mnesia_dirty_access_test.erl
@@ -461,77 +461,81 @@ dirty_index_update_set_xets(Config) when is_list(Config) ->
dirty_index_update_set(Config, ext_ets).
dirty_index_update_set(Config, Storage) ->
- [Node1] = Nodes = ?acquire_nodes(1, Config),
- Tab = index_test,
- ValPos = v1,
+ [Node1] = Nodes = ?acquire_nodes(1, Config),
+ Tab = index_test,
+ ValPos = v1,
ValPos2 = v3,
Def = [{attributes, [k, v1, v2, v3]},
{Storage, [Node1]},
- {index, [ValPos]}],
- ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
-
+ {index, [ValPos]}],
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+
Pat1 = {Tab, '$1', 2, '$2', '$3'},
- Pat2 = {Tab, '$1', '$2', '$3', '$4'},
-
- Rec1 = {Tab, 1, 2, 3, 4},
+ Pat2 = {Tab, '$1', '$2', '$3', '$4'},
+ Pat3 = {Tab, '_', '_', '_', {4, 14}},
+
+ Rec1 = {Tab, 1, 2, 3, {4, 14}},
Rec2 = {Tab, 2, 2, 13, 14},
- Rec3 = {Tab, 1, 12, 13, 14},
- Rec4 = {Tab, 4, 2, 13, 14},
-
+ Rec3 = {Tab, 1, 12, 13, 14},
+ Rec4 = {Tab, 4, 2, 13, 14},
+
?match([], mnesia:dirty_index_read(Tab, 2, ValPos)),
?match(ok, mnesia:dirty_write(Rec1)),
?match([Rec1], mnesia:dirty_index_read(Tab, 2, ValPos)),
-
+
?match(ok, mnesia:dirty_write(Rec2)),
R1 = mnesia:dirty_index_read(Tab, 2, ValPos),
?match([Rec1, Rec2], lists:sort(R1)),
-
+
?match(ok, mnesia:dirty_write(Rec3)),
R2 = mnesia:dirty_index_read(Tab, 2, ValPos),
?match([Rec2], lists:sort(R2)),
?match([Rec2], mnesia:dirty_index_match_object(Pat1, ValPos)),
-
- {atomic, R3} = mnesia:transaction(fun() -> mnesia:match_object(Pat2) end),
+
+ {atomic, R3} = mnesia:transaction(fun() -> mnesia:match_object(Pat2) end),
?match([Rec3, Rec2], lists:sort(R3)),
-
+
?match(ok, mnesia:dirty_write(Rec4)),
R4 = mnesia:dirty_index_read(Tab, 2, ValPos),
?match([Rec2, Rec4], lists:sort(R4)),
-
+
?match(ok, mnesia:dirty_delete({Tab, 4})),
?match([Rec2], mnesia:dirty_index_read(Tab, 2, ValPos)),
-
+
?match({atomic, ok}, mnesia:del_table_index(Tab, ValPos)),
?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec4) end)),
?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)),
?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos2)),
-
+
R5 = mnesia:dirty_match_object(Pat2),
?match([Rec3, Rec2, Rec4], lists:sort(R5)),
-
+
R6 = mnesia:dirty_index_read(Tab, 2, ValPos),
- ?match([Rec2, Rec4], lists:sort(R6)),
- ?match([], mnesia:dirty_index_read(Tab, 4, ValPos2)),
+ ?match([Rec2, Rec4], lists:sort(R6)),
+ ?match([], mnesia:dirty_index_read(Tab, {4,14}, ValPos2)),
R7 = mnesia:dirty_index_read(Tab, 14, ValPos2),
?match([Rec3, Rec2, Rec4], lists:sort(R7)),
?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec1) end)),
R8 = mnesia:dirty_index_read(Tab, 2, ValPos),
?match([Rec1, Rec2, Rec4], lists:sort(R8)),
- ?match([Rec1], mnesia:dirty_index_read(Tab, 4, ValPos2)),
+ ?match([Rec1], mnesia:dirty_index_read(Tab, {4,14}, ValPos2)),
+ ?match([Rec1], mnesia:dirty_match_object(Pat3)),
+ ?match([Rec1], mnesia:dirty_index_match_object(Pat3, ValPos2)),
+
R9 = mnesia:dirty_index_read(Tab, 14, ValPos2),
?match([Rec2, Rec4], lists:sort(R9)),
?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:delete_object(Rec2) end)),
R10 = mnesia:dirty_index_read(Tab, 2, ValPos),
?match([Rec1, Rec4], lists:sort(R10)),
- ?match([Rec1], mnesia:dirty_index_read(Tab, 4, ValPos2)),
+ ?match([Rec1], mnesia:dirty_index_read(Tab, {4,14}, ValPos2)),
?match([Rec4], mnesia:dirty_index_read(Tab, 14, ValPos2)),
?match(ok, mnesia:dirty_delete({Tab, 4})),
R11 = mnesia:dirty_index_read(Tab, 2, ValPos),
?match([Rec1], lists:sort(R11)),
- ?match([Rec1], mnesia:dirty_index_read(Tab, 4, ValPos2)),
+ ?match([Rec1], mnesia:dirty_index_read(Tab, {4,14}, ValPos2)),
?match([], mnesia:dirty_index_read(Tab, 14, ValPos2)),
?verify_mnesia(Nodes, []).
diff --git a/lib/observer/doc/src/observer_ug.xml b/lib/observer/doc/src/observer_ug.xml
index ca354df864..6eb72f3e58 100644
--- a/lib/observer/doc/src/observer_ug.xml
+++ b/lib/observer/doc/src/observer_ug.xml
@@ -168,7 +168,7 @@
<item><p>The length of the message queue for the process.</p></item>
</taglist>
- <p>Option <em>Process info</em> opens a detailed information window on the selected process,
+ <p>Option <em>Process info</em> opens a detailed information window on the process under the mouse pointer,
including the following:</p>
<taglist>
<tag>Process Information</tag>
@@ -195,12 +195,53 @@
</p>
</note>
- <p>Option <em>Trace Processes</em> adds the selected process identifiers to tab
+ <p>Option <em>Trace selected processes</em> adds the selected process identifiers to tab
<em>Trace Overview</em> plus the node that the processes reside on.
</p>
- <p>Option <em>Trace Named Processes</em> adds the registered name of the processes. This can be
+ <p>Option <em>Trace selected processes by name</em> adds the registered name of the processes. This can be
useful when tracing is done on many nodes, as processes with that name are then traced on
all traced nodes.</p>
+ <p>Option <em>Kill process</em> brutally kills the processes under
+ the mouse pointer by sending an exit signal with
+ reason <c>kill</c>.</p>
+
+ </section>
+
+ <section>
+ <title>Ports Tab</title>
+ <p>Tab <em>Ports</em> lists port information in columns.
+ For each port the following information is displayed:
+ </p>
+ <taglist>
+ <tag>Id</tag>
+ <item><p>The port identifier.</p></item>
+ <tag>Connected</tag>
+ <item><p>The process identifier for the process that owns the
+ port.</p></item>
+ <tag>Name</tag>
+ <item><p>The registered name of the port, if any.</p></item>
+ <tag>Controls</tag>
+ <item><p>The name of the command set by <seealso marker="erts:erlang#open_port-2"><c>erlang:open_port/2</c></seealso>.</p></item>
+ <tag>Slot</tag>
+ <item><p>The internal index of the port.</p></item>
+ </taglist>
+
+ <p>Option <em>Port info</em> opens a detailed information window
+ for the port under the mouse pointer. In addition to the
+ information above, it also shows links and monitors.</p>
+
+ <p>Option <em>Trace selected ports</em> adds the selected port
+ identifiers, and the nodes that the ports reside on,
+ to tab <em>Trace Overview</em>.</p>
+
+ <p>Option <em>Trace selected ports by name</em> adds the
+ registered name of the port to tab <em>Trace Overview</em>. This
+ can be useful when tracing is done on many nodes, as ports with
+ that name are then traced on all traced nodes.</p>
+
+ <p>Option <em>Close</em>
+ executes <seealso marker="erts:erlang#port_close-1"><c>erlang:port_close/1</c></seealso>
+ on the port under the mouse pointer.</p>
</section>
@@ -211,8 +252,11 @@
applications are not diplayed. Use menu <em>View</em> to view "system"
ETS tables, unreadable ETS tables, or Mnesia tables.
</p>
- <p>Double-click to view the table content. To view table information, select the table
- and activate menu <em>View &gt; Table information</em>.</p>
+ <p>Double-click to view the table content, or right-click and
+ select option <em>Show Table Content</em>. To view table
+ information, select the table and activate menu <em>View &gt;
+ Table information</em>, or right-click and select option <em>Table
+ info</em>.</p>
<p>You can use <seealso marker="stdlib:re">regular
expressions</seealso> and search for objects, and edit or delete them.
</p>
@@ -220,11 +264,12 @@
<section>
<title>Trace Overview Tab</title>
- <p>Tab <em>Trace Overview</em> handles tracing. Trace
- by selecting the processes to be traced and how to trace
- them. You can trace messages, function calls, and events, where
- events are process-related events such as <c>spawn</c>,
- <c>exit</c>, and many others.
+ <p>Tab <em>Trace Overview</em> handles tracing. Trace by selecting
+ the processes or ports to be traced and how to trace them. For
+ processes, you can trace messages, function calls, scheduling,
+ garbage collections, and process-related events such
+ as <c>spawn</c>, <c>exit</c>, and many others. For ports, you can
+ trace messages, scheduling and port-related events.
</p>
<p>To trace function calls, you also need to set up
@@ -234,27 +279,51 @@
specifications can also be used to trigger more information
in the trace messages.
</p>
- <note><p>Trace patterns only apply to the traced processes.</p></note>
+
+ <p>You can also set match specifications on messages. By default,
+ if tracing messages, all messages sent and/or received by the
+ process or port are traced. Match specifications can be used to
+ reduce the number of traced messages and/or to trigger more
+ information in the trace messages.</p>
+
+ <note><p>Trace patterns only apply to the traced processes and
+ ports.</p></note>
<p>
- Processes are added from the <em>Applications</em> or <em>Processes</em> tabs.
- A special <em>new</em> identifier, meaning all processes spawned after trace
- start, can be added with button <em>Add 'new' Process</em>.
+ Processes are added from the <em>Applications</em>
+ or <em>Processes</em> tabs. Ports are added from
+ the <em>Ports</em> tab. A special <em>new</em> identifier,
+ meaning all processes, or ports, started after trace start, can
+ be added with buttons <em>Add 'new' Processes</em> and <em>Add
+ 'new' Ports</em>, respecively.
</p>
<p>
- When adding processes, a window with trace options is displayed. The chosen
- options are set for the selected processes.
- Process options can be changed by right-clicking a process.
+ When adding processes or ports, a window with trace options is
+ displayed. The chosen options are set for the selected
+ processes/ports. To change the options, right-click the process
+ or port and select <em>Edit process options</em>. To remove a
+ process or port from the list, right-click and select <em>Remove
+ process</em> or <em>Remove port</em>, respectively.
</p>
<p>
- Processes added by process identifiers add the nodes these
- processes reside on in the node list. More nodes can be added by clicking
- button <em>Add Nodes</em>.
+ Processes and ports added by process/port identifiers add the
+ nodes these processes/ports reside on in the node list. More
+ nodes can be added by clicking button <em>Add Nodes</em>, or by
+ right-clicking in the <em>Nodes</em> list and select <em>Add
+ Nodes</em>. To remove nodes, select them, then right-click and
+ choose <em>Remove nodes</em>.
</p>
<p>
If function calls are traced, trace patterns must be added by clicking button
<em>Add Trace Pattern</em>. Select a module, function(s), and a match specification.
- If no functions are selected, all functions in the module are traced.
+ If no functions are selected, all functions in the module are traced.</p>
+ <p>
+ Trace patterns can also be added for traced messages. Click
+ button <em>Add Trace Pattern</em> and select <em>Messages
+ sent</em> or <em>Messages received</em>, and a match
+ specification.
+ </p>
+ <p>
A few basic match specifications are provided in the tool, and
you can provide your own match specifications. The syntax of match
specifications is described in the <seealso
diff --git a/lib/observer/doc/src/ttb.xml b/lib/observer/doc/src/ttb.xml
index 2b637551db..94ecef24b4 100644
--- a/lib/observer/doc/src/ttb.xml
+++ b/lib/observer/doc/src/ttb.xml
@@ -229,27 +229,33 @@ ttb:p(all, call).</input></pre>
</func>
<func>
- <name>p(Procs,Flags) -> Return</name>
- <fsummary>Set the specified trace flags on the specified processes.</fsummary>
+ <name>p(Item,Flags) -> Return</name>
+ <fsummary>Set the specified trace flags on the specified processes or ports.</fsummary>
<type>
- <v>Return = {ok,[{Procs,MatchDesc}]}</v>
- <v>Procs = Process | [Process] | all | new | existing</v>
- <v>Process = pid() | atom() | {global,atom()}</v>
+ <v>Return = {ok,[{Item,MatchDesc}]}</v>
+ <v>Items = Item | [Item]</v>
+ <v>Item = pid() | port() | RegName | {global,GlobalRegName} |
+ all | processes | ports |
+ existing | existing_processes | existing_ports |
+ new | new_processes | new_ports</v>
+ <v>RegName = atom()</v>
+ <v>GlobalRegName = term()</v>
<v>Flags = Flag | [Flag]</v>
</type>
<desc>
- <p>Sets the specified trace flags on the specified
- processes. Flag <c>timestamp</c> is always turned on.
+ <p>Sets the specified trace flags on the specified processes
+ or ports. Flag <c>timestamp</c> is always turned on.
</p>
<p>See the Reference Manual for module
<seealso marker="runtime_tools:dbg"><c>dbg</c></seealso>
- and the possible trace flags. Parameter
+ for the possible trace flags. Parameter
<c>MatchDesc</c> is the same as returned from
<c>dbg:p/2</c>.</p>
<p>Processes can be specified as registered names, globally
- registered names, or process identifiers. If a registered name
- is specified, the flags are set on processes with this name on all
- active nodes.</p>
+ registered names, or process identifiers. Ports can be
+ specified as registered names or port identifiers. If a
+ registered name is specified, the flags are set on
+ processes/ports with this name on all active nodes.</p>
<p>Issuing this command starts the timer for this trace if option
<c>timer</c> is specified with <c>tracer/2</c>.
</p>
@@ -257,17 +263,23 @@ ttb:p(all, call).</input></pre>
</func>
<func>
- <name>tp, tpl, ctp, ctpl, ctpg</name>
+ <name>tp, tpl, tpe, ctp, ctpl, ctpg, ctpe</name>
<fsummary>Set and clear trace patterns.</fsummary>
<desc>
- <p>These functions are to be used with
- trace flag <c>call</c> for setting and clearing trace
- patterns. When trace flag <c>call</c> is set on a process,
+ <p>These functions are to be used with trace
+ flag <c>call</c>, <c>send</c>, and <c>'receive'</c> for
+ setting and clearing trace patterns.</p>
+ <p>When trace flag <c>call</c> is set on a process,
function calls are traced on that process if a trace
- pattern is set for the called function. Trace patterns
- specify how to trace a function by using match
- specifications. Match specifications are described in the
- <seealso marker="erts:users_guide"><c>ERTS User's Guide</c></seealso>.
+ pattern is set for the called function.</p>
+ <p>The <c>send</c> and <c>'receive'</c> flags enable tracing
+ of all messages sent and received by the process/port. Trace
+ patterns set with <c>tpe</c> may limit traced messages based
+ on the message content, the sender, and/or the receiver.</p>
+ <p>Trace patterns specify how to trace a function or a message
+ by using match specifications. Match specifications are
+ described in the
+ <seealso marker="erts:match_spec"><c>ERTS User's Guide</c></seealso>.
</p>
<p>These functions are equivalent to the corresponding
functions in module
@@ -284,6 +296,8 @@ ttb:p(all, call).</input></pre>
<item><p>Sets trace patterns on global function calls.</p></item>
<tag><c>tpl</c></tag>
<item><p>Sets trace patterns on local and global function calls.</p></item>
+ <tag><c>tpe</c></tag>
+ <item><p>Sets trace patterns on messages.</p></item>
<tag><c>ctp</c></tag>
<item><p>Clears trace patterns on local and global function
calls.</p></item>
@@ -291,13 +305,15 @@ ttb:p(all, call).</input></pre>
<item><p>Clears trace patterns on local function calls.</p></item>
<tag><c>ctpg</c></tag>
<item><p>Clears trace patterns on global function calls.</p></item>
+ <tag><c>ctpe</c></tag>
+ <item><p>Clears trace patterns on messages.</p></item>
</taglist>
<p>With <c>tp</c> and <c>tpl</c>, one of the match specification shortcuts
can be used (for example, <c>ttb:tp(foo_module, caller)</c>).</p>
<p>The shortcuts are as follows:</p>
<list type="bulleted">
<item><c>return</c> - for <c>[{'_',[],[{return_trace}]}]</c>
- (report the return value)</item>
+ (report the return value from a traced function)</item>
<item><c>caller</c> - for <c>[{'_',[],[{message,{caller}}]}]</c>
(report the calling function)</item>
<item><c>{codestr, Str}</c> - for <c>dbg:fun2ms/1</c> arguments
diff --git a/lib/observer/src/Makefile b/lib/observer/src/Makefile
index 85dc5933c1..dd7831fa2b 100644
--- a/lib/observer/src/Makefile
+++ b/lib/observer/src/Makefile
@@ -67,6 +67,7 @@ MODULES= \
observer_html_lib \
observer_lib \
observer_perf_wx \
+ observer_port_wx \
observer_pro_wx \
observer_procinfo \
observer_sys_wx \
diff --git a/lib/observer/src/observer.app.src b/lib/observer/src/observer.app.src
index 5ddf65fa59..3a5bd172e7 100644
--- a/lib/observer/src/observer.app.src
+++ b/lib/observer/src/observer.app.src
@@ -51,6 +51,7 @@
observer_html_lib,
observer_lib,
observer_perf_wx,
+ observer_port_wx,
observer_pro_wx,
observer_procinfo,
observer_sys_wx,
diff --git a/lib/observer/src/observer_app_wx.erl b/lib/observer/src/observer_app_wx.erl
index cef83037d0..936b2783e2 100644
--- a/lib/observer/src/observer_app_wx.erl
+++ b/lib/observer/src/observer_app_wx.erl
@@ -221,21 +221,21 @@ handle_event(#wx{id=?ID_PROC_KILL, event=#wxCommand{type=command_menu_selected}}
%%% Trace api
handle_event(#wx{id=?ID_TRACE_PID, event=#wxCommand{type=command_menu_selected}},
State = #state{sel={Box,_}}) ->
- observer_trace_wx:add_processes(observer_wx:get_tracer(), [box_to_pid(Box)]),
+ observer_trace_wx:add_processes([box_to_pid(Box)]),
{noreply, State};
handle_event(#wx{id=?ID_TRACE_NAME, event=#wxCommand{type=command_menu_selected}},
State = #state{sel={Box,_}}) ->
- observer_trace_wx:add_processes(observer_wx:get_tracer(), [box_to_reg(Box)]),
+ observer_trace_wx:add_processes([box_to_reg(Box)]),
{noreply, State};
handle_event(#wx{id=?ID_TRACE_TREE_PIDS, event=#wxCommand{type=command_menu_selected}},
State = #state{sel=Sel}) ->
Get = fun(Box) -> box_to_pid(Box) end,
- observer_trace_wx:add_processes(observer_wx:get_tracer(), tree_map(Sel, Get)),
+ observer_trace_wx:add_processes(tree_map(Sel, Get)),
{noreply, State};
handle_event(#wx{id=?ID_TRACE_TREE_NAMES, event=#wxCommand{type=command_menu_selected}},
State = #state{sel=Sel}) ->
Get = fun(Box) -> box_to_reg(Box) end,
- observer_trace_wx:add_processes(observer_wx:get_tracer(), tree_map(Sel, Get)),
+ observer_trace_wx:add_processes(tree_map(Sel, Get)),
{noreply, State};
handle_event(Event, _State) ->
diff --git a/lib/observer/src/observer_port_wx.erl b/lib/observer/src/observer_port_wx.erl
new file mode 100644
index 0000000000..3b788642cc
--- /dev/null
+++ b/lib/observer/src/observer_port_wx.erl
@@ -0,0 +1,479 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2011-2014. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+-module(observer_port_wx).
+
+-export([start_link/2]).
+
+%% wx_object callbacks
+-export([init/1, handle_info/2, terminate/2, code_change/3, handle_call/3,
+ handle_event/2, handle_sync_event/3, handle_cast/2]).
+
+-behaviour(wx_object).
+-include_lib("wx/include/wx.hrl").
+-include("observer_defs.hrl").
+
+-define(GRID, 300).
+-define(ID_REFRESH, 301).
+-define(ID_REFRESH_INTERVAL, 302).
+-define(ID_PORT_INFO, 303).
+-define(ID_PORT_INFO_SELECTED, 304).
+-define(ID_TRACE_PORTS, 305).
+-define(ID_TRACE_NAMES, 306).
+-define(ID_TRACE_NEW, 307).
+-define(ID_TRACE_ALL, 308).
+-define(ID_CLOSE_PORT, 309).
+
+-define(TRACE_PORTS_STR, "Trace selected ports").
+-define(TRACE_NAMES_STR, "Trace selected ports, "
+ "if a process have a registered name "
+ "processes with same name will be traced on all nodes").
+
+-record(port,
+ {id,
+ connected,
+ name,
+ controls,
+ slot,
+ id_str,
+ links,
+ monitors}).
+
+-record(opt, {sort_key=2,
+ sort_incr=true
+ }).
+
+-record(state,
+ {
+ parent,
+ grid,
+ panel,
+ node=node(),
+ opt=#opt{},
+ right_clicked_port,
+ ports,
+ timer,
+ open_wins=[]
+ }).
+
+start_link(Notebook, Parent) ->
+ wx_object:start_link(?MODULE, [Notebook, Parent], []).
+
+init([Notebook, Parent]) ->
+ Panel = wxPanel:new(Notebook),
+ Sizer = wxBoxSizer:new(?wxVERTICAL),
+ Style = ?wxLC_REPORT bor ?wxLC_HRULES,
+ Grid = wxListCtrl:new(Panel, [{winid, ?GRID}, {style, Style}]),
+ wxSizer:add(Sizer, Grid, [{flag, ?wxEXPAND bor ?wxALL},
+ {proportion, 1}, {border, 5}]),
+ wxWindow:setSizer(Panel, Sizer),
+ Li = wxListItem:new(),
+ AddListEntry = fun({Name, Align, DefSize}, Col) ->
+ wxListItem:setText(Li, Name),
+ wxListItem:setAlign(Li, Align),
+ wxListCtrl:insertColumn(Grid, Col, Li),
+ wxListCtrl:setColumnWidth(Grid, Col, DefSize),
+ Col + 1
+ end,
+ ListItems = [{"Id", ?wxLIST_FORMAT_LEFT, 150},
+ {"Connected", ?wxLIST_FORMAT_LEFT, 150},
+ {"Name", ?wxLIST_FORMAT_LEFT, 150},
+ {"Controls", ?wxLIST_FORMAT_LEFT, 200},
+ {"Slot", ?wxLIST_FORMAT_RIGHT, 50}],
+ lists:foldl(AddListEntry, 0, ListItems),
+ wxListItem:destroy(Li),
+
+ wxListCtrl:connect(Grid, command_list_item_right_click),
+ wxListCtrl:connect(Grid, command_list_item_activated),
+ wxListCtrl:connect(Grid, command_list_col_click),
+ wxListCtrl:connect(Grid, size, [{skip, true}]),
+
+ wxWindow:setFocus(Grid),
+ {Panel, #state{grid=Grid, parent=Parent, panel=Panel, timer={false, 10}}}.
+
+handle_event(#wx{id=?ID_REFRESH},
+ State = #state{node=Node, grid=Grid, opt=Opt}) ->
+ Ports0 = get_ports(Node),
+ Ports = update_grid(Grid, Opt, Ports0),
+ {noreply, State#state{ports=Ports}};
+
+handle_event(#wx{obj=Obj, event=#wxClose{}}, #state{open_wins=Opened} = State) ->
+ NewOpened =
+ case lists:keytake(Obj,2,Opened) of
+ false -> Opened;
+ {value,_,Rest} -> Rest
+ end,
+ {noreply, State#state{open_wins=NewOpened}};
+
+handle_event(#wx{event=#wxList{type=command_list_col_click, col=Col}},
+ State = #state{node=Node, grid=Grid,
+ opt=Opt0=#opt{sort_key=Key, sort_incr=Bool}}) ->
+ Opt = case Col+2 of
+ Key -> Opt0#opt{sort_incr=not Bool};
+ NewKey -> Opt0#opt{sort_key=NewKey}
+ end,
+ Ports0 = get_ports(Node),
+ Ports = update_grid(Grid, Opt, Ports0),
+ wxWindow:setFocus(Grid),
+ {noreply, State#state{opt=Opt, ports=Ports}};
+
+handle_event(#wx{event=#wxSize{size={W,_}}}, State=#state{grid=Grid}) ->
+ observer_lib:set_listctrl_col_size(Grid, W),
+ {noreply, State};
+
+handle_event(#wx{event=#wxList{type=command_list_item_activated,
+ itemIndex=Index}},
+ State=#state{grid=Grid, ports=Ports, open_wins=Opened}) ->
+ Port = lists:nth(Index+1, Ports),
+ NewOpened = display_port_info(Grid, Port, Opened),
+ {noreply, State#state{open_wins=NewOpened}};
+
+handle_event(#wx{event=#wxList{type=command_list_item_right_click,
+ itemIndex=Index}},
+ State=#state{panel=Panel, ports=Ports}) ->
+ case Index of
+ -1 ->
+ {noreply, State};
+ _ ->
+ Port = lists:nth(Index+1, Ports),
+ Menu = wxMenu:new(),
+ wxMenu:append(Menu, ?ID_PORT_INFO,
+ "Port info for " ++ erlang:port_to_list(Port#port.id)),
+ wxMenu:append(Menu, ?ID_TRACE_PORTS,
+ "Trace selected ports",
+ [{help, ?TRACE_PORTS_STR}]),
+ wxMenu:append(Menu, ?ID_TRACE_NAMES,
+ "Trace selected ports by name (all nodes)",
+ [{help, ?TRACE_NAMES_STR}]),
+ wxMenu:append(Menu, ?ID_CLOSE_PORT,
+ "Close " ++ erlang:port_to_list(Port#port.id)),
+ wxWindow:popupMenu(Panel, Menu),
+ wxMenu:destroy(Menu),
+ {noreply, State#state{right_clicked_port=Port}}
+ end;
+
+handle_event(#wx{id=?ID_PORT_INFO},
+ State = #state{grid=Grid, right_clicked_port=Port,
+ open_wins=Opened}) ->
+ case Port of
+ undefined ->
+ {noreply, State};
+ _ ->
+ NewOpened = display_port_info(Grid, Port, Opened),
+ {noreply, State#state{right_clicked_port=undefined,
+ open_wins=NewOpened}}
+ end;
+
+handle_event(#wx{id=?ID_PORT_INFO_SELECTED},
+ State = #state{grid=Grid, ports=Ports, open_wins=Opened}) ->
+ case get_selected_items(Grid,Ports) of
+ [] ->
+ observer_wx:create_txt_dialog(State#state.panel, "No selected ports",
+ "Port Info", ?wxICON_EXCLAMATION),
+ {noreply, State};
+ Selected ->
+ NewOpened = lists:foldl(fun(P,O) -> display_port_info(Grid, P, O) end,
+ Opened, Selected),
+ {noreply, State#state{open_wins = NewOpened}}
+ end;
+
+handle_event(#wx{id=?ID_CLOSE_PORT}, State = #state{right_clicked_port=Port}) ->
+ case Port of
+ undefined ->
+ {noreply, State};
+ _ ->
+ erlang:port_close(Port#port.id),
+ {noreply, State#state{right_clicked_port=undefined}}
+ end;
+
+handle_event(#wx{id=?ID_TRACE_PORTS}, #state{grid=Grid, ports=Ports}=State) ->
+ case get_selected_items(Grid, Ports) of
+ [] ->
+ observer_wx:create_txt_dialog(State#state.panel, "No selected ports",
+ "Tracer", ?wxICON_EXCLAMATION);
+ Selected ->
+ SelectedIds = [Port#port.id || Port <- Selected],
+ observer_trace_wx:add_ports(SelectedIds)
+ end,
+ {noreply, State};
+
+handle_event(#wx{id=?ID_TRACE_NAMES}, #state{grid=Grid, ports=Ports}=State) ->
+ case get_selected_items(Grid, Ports) of
+ [] ->
+ observer_wx:create_txt_dialog(State#state.panel, "No selected ports",
+ "Tracer", ?wxICON_EXCLAMATION);
+ Selected ->
+ IdsOrRegs =
+ [case Port#port.name of
+ [] -> Port#port.id;
+ Name -> Name
+ end || Port <- Selected],
+ observer_trace_wx:add_ports(IdsOrRegs)
+ end,
+ {noreply, State};
+
+handle_event(#wx{id=?ID_TRACE_NEW, event=#wxCommand{type=command_menu_selected}}, State) ->
+ observer_trace_wx:add_ports([new_ports]),
+ {noreply, State};
+
+handle_event(#wx{id=?ID_REFRESH_INTERVAL},
+ State = #state{grid=Grid, timer=Timer0}) ->
+ Timer = observer_lib:interval_dialog(Grid, Timer0, 10, 5*60),
+ {noreply, State#state{timer=Timer}};
+
+handle_event(#wx{event=#wxMouse{type=left_down}, userData=TargetPid}, State) ->
+ observer ! {open_link, TargetPid},
+ {noreply, State};
+
+handle_event(#wx{obj=Obj, event=#wxMouse{type=enter_window}}, State) ->
+ wxTextCtrl:setForegroundColour(Obj,{0,0,100,255}),
+ {noreply, State};
+
+handle_event(#wx{obj=Obj, event=#wxMouse{type=leave_window}}, State) ->
+ wxTextCtrl:setForegroundColour(Obj,?wxBLUE),
+ {noreply, State};
+
+handle_event(Event, _State) ->
+ error({unhandled_event, Event}).
+
+handle_sync_event(_Event, _Obj, _State) ->
+ ok.
+
+handle_call(Event, From, _State) ->
+ error({unhandled_call, Event, From}).
+
+handle_cast(Event, _State) ->
+ error({unhandled_cast, Event}).
+
+handle_info({portinfo_open, PortIdStr},
+ State = #state{grid=Grid, ports=Ports, open_wins=Opened}) ->
+ Port = lists:keyfind(PortIdStr,#port.id_str,Ports),
+ NewOpened = display_port_info(Grid, Port, Opened),
+ {noreply, State#state{open_wins = NewOpened}};
+
+handle_info(refresh_interval, State = #state{node=Node, grid=Grid, opt=Opt,
+ ports=OldPorts}) ->
+ case get_ports(Node) of
+ OldPorts ->
+ %% no change
+ {noreply, State};
+ Ports0 ->
+ Ports = update_grid(Grid, Opt, Ports0),
+ {noreply, State#state{ports=Ports}}
+ end;
+
+handle_info({active, Node}, State = #state{parent=Parent, grid=Grid, opt=Opt,
+ timer=Timer0}) ->
+ Ports0 = get_ports(Node),
+ Ports = update_grid(Grid, Opt, Ports0),
+ wxWindow:setFocus(Grid),
+ create_menus(Parent),
+ Timer = observer_lib:start_timer(Timer0),
+ {noreply, State#state{node=Node, ports=Ports, timer=Timer}};
+
+handle_info(not_active, State = #state{timer = Timer0}) ->
+ Timer = observer_lib:stop_timer(Timer0),
+ {noreply, State#state{timer=Timer}};
+
+handle_info({error, Error}, State) ->
+ handle_error(Error),
+ {noreply, State};
+
+handle_info(_Event, State) ->
+ {noreply, State}.
+
+terminate(_Event, _State) ->
+ ok.
+
+code_change(_, _, State) ->
+ State.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+create_menus(Parent) ->
+ MenuEntries =
+ [{"View",
+ [#create_menu{id = ?ID_PORT_INFO_SELECTED,
+ text = "Port info for selected ports\tCtrl-I"},
+ separator,
+ #create_menu{id = ?ID_REFRESH, text = "Refresh\tCtrl-R"},
+ #create_menu{id = ?ID_REFRESH_INTERVAL, text = "Refresh Interval..."}
+ ]},
+ {"Trace",
+ [#create_menu{id=?ID_TRACE_PORTS, text="Trace selected ports"},
+ #create_menu{id=?ID_TRACE_NAMES, text="Trace selected ports by name (all nodes)"},
+ #create_menu{id=?ID_TRACE_NEW, text="Trace new ports"}
+ ]}
+ ],
+ observer_wx:create_menus(Parent, MenuEntries).
+
+get_ports(Node) ->
+ case get_ports2(Node) of
+ Error = {error, _} ->
+ self() ! Error,
+ [];
+ Res ->
+ Res
+ end.
+get_ports2(Node) ->
+ case rpc:call(Node, observer_backend, get_port_list, []) of
+ {badrpc, Error} ->
+ {error, Error};
+ Error = {error, _} ->
+ Error;
+ Result ->
+ [list_to_portrec(Port) || Port <- Result]
+ end.
+
+list_to_portrec(PL) ->
+ %% PortInfo:
+ %% {registered_name, RegisteredName :: atom()} |
+ %% {id, Index :: integer() >= 0} |
+ %% {connected, Pid :: pid()} |
+ %% {links, Pids :: [pid()]} |
+ %% {name, String :: string()} |
+ %% {input, Bytes :: integer() >= 0} |
+ %% {output, Bytes :: integer() >= 0} |
+ %% {os_pid, OsPid :: integer() >= 0 | undefined},
+ PortId = proplists:get_value(port_id, PL),
+ #port{id = PortId,
+ id_str = erlang:port_to_list(PortId),
+ slot = proplists:get_value(id, PL),
+ connected = proplists:get_value(connected, PL),
+ links = proplists:get_value(links, PL, []),
+ name = proplists:get_value(registered_name, PL, []),
+ monitors = proplists:get_value(monitors, PL, []),
+ controls = proplists:get_value(name, PL)}.
+
+portrec_to_list(#port{id = Id,
+ slot = Slot,
+ connected = Connected,
+ links = Links,
+ name = Name,
+ monitors = Monitors,
+ controls = Controls}) ->
+ [{id,Id},
+ {slot,Slot},
+ {connected,Connected},
+ {links,Links},
+ {name,Name},
+ {monitors,Monitors},
+ {controls,Controls}].
+
+display_port_info(Parent, PortRec, Opened) ->
+ PortIdStr = PortRec#port.id_str,
+ case lists:keyfind(PortIdStr,1,Opened) of
+ false ->
+ Frame = do_display_port_info(Parent, PortRec),
+ [{PortIdStr,Frame}|Opened];
+ {_,Win} ->
+ wxFrame:raise(Win),
+ Opened
+ end.
+
+do_display_port_info(Parent0, PortRec) ->
+ Parent = observer_lib:get_wx_parent(Parent0),
+ Title = "Port Info: " ++ PortRec#port.id_str,
+ Frame = wxMiniFrame:new(Parent, ?wxID_ANY, Title,
+ [{style, ?wxSYSTEM_MENU bor ?wxCAPTION
+ bor ?wxCLOSE_BOX bor ?wxRESIZE_BORDER}]),
+
+ Port = portrec_to_list(PortRec),
+ Fields0 = port_info_fields(Port),
+ {_FPanel, _Sizer, _UpFields} = observer_lib:display_info(Frame, Fields0),
+ wxFrame:center(Frame),
+ wxFrame:connect(Frame, close_window, [{skip, true}]),
+ wxFrame:show(Frame),
+ Frame.
+
+
+port_info_fields(Port) ->
+ Struct =
+ [{"Overview",
+ [{"Name", name},
+ {"Connected", {click,connected}},
+ {"Slot", slot},
+ {"Controls", controls}]},
+ {scroll_boxes,
+ [{"Links",1,{click,links}},
+ {"Monitors",1,{click,filter_monitor_info()}}]}],
+ observer_lib:fill_info(Struct, Port).
+
+filter_monitor_info() ->
+ fun(Data) ->
+ Ms = proplists:get_value(monitors, Data),
+ [Pid || {process, Pid} <- Ms]
+ end.
+
+
+handle_error(Foo) ->
+ Str = io_lib:format("ERROR: ~s~n",[Foo]),
+ observer_lib:display_info_dialog(Str).
+
+update_grid(Grid, Opt, Ports) ->
+ wx:batch(fun() -> update_grid2(Grid, Opt, Ports) end).
+update_grid2(Grid, #opt{sort_key=Sort,sort_incr=Dir}, Ports) ->
+ wxListCtrl:deleteAllItems(Grid),
+ Update =
+ fun(#port{id = Id,
+ slot = Slot,
+ connected = Connected,
+ name = Name,
+ controls = Ctrl},
+ Row) ->
+ _Item = wxListCtrl:insertItem(Grid, Row, ""),
+ if (Row rem 2) =:= 0 ->
+ wxListCtrl:setItemBackgroundColour(Grid, Row, ?BG_EVEN);
+ true -> ignore
+ end,
+
+ lists:foreach(fun({Col, Val}) ->
+ wxListCtrl:setItem(Grid, Row, Col,
+ observer_lib:to_str(Val))
+ end,
+ [{0,Id},{1,Connected},{2,Name},{3,Ctrl},{4,Slot}]),
+ Row + 1
+ end,
+ PortInfo = case Dir of
+ false -> lists:reverse(lists:keysort(Sort, Ports));
+ true -> lists:keysort(Sort, Ports)
+ end,
+ lists:foldl(Update, 0, PortInfo),
+ PortInfo.
+
+
+get_selected_items(Grid, Data) ->
+ get_indecies(get_selected_items(Grid, -1, []), Data).
+get_selected_items(Grid, Index, ItemAcc) ->
+ Item = wxListCtrl:getNextItem(Grid, Index, [{geometry, ?wxLIST_NEXT_ALL},
+ {state, ?wxLIST_STATE_SELECTED}]),
+ case Item of
+ -1 ->
+ lists:reverse(ItemAcc);
+ _ ->
+ get_selected_items(Grid, Item, [Item | ItemAcc])
+ end.
+
+get_indecies(Items, Data) ->
+ get_indecies(Items, 0, Data).
+get_indecies([I|Rest], I, [H|T]) ->
+ [H|get_indecies(Rest, I+1, T)];
+get_indecies(Rest = [_|_], I, [_|T]) ->
+ get_indecies(Rest, I+1, T);
+get_indecies(_, _, _) ->
+ [].
diff --git a/lib/observer/src/observer_pro_wx.erl b/lib/observer/src/observer_pro_wx.erl
index bd914cdf65..ee6829b847 100644
--- a/lib/observer/src/observer_pro_wx.erl
+++ b/lib/observer/src/observer_pro_wx.erl
@@ -83,6 +83,7 @@
timer,
procinfo_menu_pids=[],
sel={[], []},
+ right_clicked_pid,
holder}).
start_link(Notebook, Parent) ->
@@ -303,13 +304,14 @@ handle_event(#wx{id=?ID_REFRESH_INTERVAL},
Timer = observer_lib:interval_dialog(Panel, Timer0, 1, 5*60),
{noreply, State#state{timer=Timer}};
-handle_event(#wx{id=?ID_KILL}, #state{sel={[_|Ids], [ToKill|Pids]}}=State) ->
- exit(ToKill, kill),
- {noreply, State#state{sel={Ids,Pids}}};
+handle_event(#wx{id=?ID_KILL}, #state{right_clicked_pid=Pid, sel=Sel0}=State) ->
+ exit(Pid, kill),
+ Sel = rm_selected(Pid,Sel0),
+ {noreply, State#state{sel=Sel}};
handle_event(#wx{id=?ID_PROC},
- #state{panel=Panel, sel={_, [Pid|_]},procinfo_menu_pids=Opened}=State) ->
+ #state{panel=Panel, right_clicked_pid=Pid, procinfo_menu_pids=Opened}=State) ->
Opened2 = start_procinfo(Pid, Panel, Opened),
{noreply, State#state{procinfo_menu_pids=Opened2}};
@@ -319,7 +321,7 @@ handle_event(#wx{id=?ID_TRACE_PIDS}, #state{sel={_, Pids}, panel=Panel}=State)
observer_wx:create_txt_dialog(Panel, "No selected processes", "Tracer", ?wxICON_EXCLAMATION),
{noreply, State};
Pids ->
- observer_trace_wx:add_processes(observer_wx:get_tracer(), Pids),
+ observer_trace_wx:add_processes(Pids),
{noreply, State}
end;
@@ -330,12 +332,12 @@ handle_event(#wx{id=?ID_TRACE_NAMES}, #state{sel={SelIds,_Pids}, holder=Holder,
{noreply, State};
_ ->
PidsOrReg = call(Holder, {get_name_or_pid, self(), SelIds}),
- observer_trace_wx:add_processes(observer_wx:get_tracer(), PidsOrReg),
+ observer_trace_wx:add_processes(PidsOrReg),
{noreply, State}
end;
handle_event(#wx{id=?ID_TRACE_NEW, event=#wxCommand{type=command_menu_selected}}, State) ->
- observer_trace_wx:add_processes(observer_wx:get_tracer(), [new]),
+ observer_trace_wx:add_processes([new_processes]),
{noreply, State};
handle_event(#wx{event=#wxSize{size={W,_}}},
@@ -347,20 +349,26 @@ handle_event(#wx{event=#wxList{type=command_list_item_right_click,
itemIndex=Row}},
#state{panel=Panel, holder=Holder}=State) ->
- case call(Holder, {get_row, self(), Row, pid}) of
- {error, undefined} ->
- undefined;
- {ok, _} ->
- Menu = wxMenu:new(),
- wxMenu:append(Menu, ?ID_PROC, "Process info"),
- wxMenu:append(Menu, ?ID_TRACE_PIDS, "Trace processes", [{help, ?TRACE_PIDS_STR}]),
- wxMenu:append(Menu, ?ID_TRACE_NAMES, "Trace named processes (all nodes)",
- [{help, ?TRACE_NAMES_STR}]),
- wxMenu:append(Menu, ?ID_KILL, "Kill Process"),
- wxWindow:popupMenu(Panel, Menu),
- wxMenu:destroy(Menu)
- end,
- {noreply, State};
+ Pid =
+ case call(Holder, {get_row, self(), Row, pid}) of
+ {error, undefined} ->
+ undefined;
+ {ok, P} ->
+ Menu = wxMenu:new(),
+ wxMenu:append(Menu, ?ID_PROC,
+ "Process info for " ++ pid_to_list(P)),
+ wxMenu:append(Menu, ?ID_TRACE_PIDS,
+ "Trace selected processes",
+ [{help, ?TRACE_PIDS_STR}]),
+ wxMenu:append(Menu, ?ID_TRACE_NAMES,
+ "Trace selected processes by name (all nodes)",
+ [{help, ?TRACE_NAMES_STR}]),
+ wxMenu:append(Menu, ?ID_KILL, "Kill process " ++ pid_to_list(P)),
+ wxWindow:popupMenu(Panel, Menu),
+ wxMenu:destroy(Menu),
+ P
+ end,
+ {noreply, State#state{right_clicked_pid=Pid}};
handle_event(#wx{event=#wxList{type=command_list_item_focused,
itemIndex=Row}},
@@ -432,6 +440,17 @@ set_focus([Old|_], [New|_], Grid) ->
wxListCtrl:setItemState(Grid, Old, 0, ?wxLIST_STATE_FOCUSED),
wxListCtrl:setItemState(Grid, New, 16#FFFF, ?wxLIST_STATE_FOCUSED).
+rm_selected(Pid, {Ids, Pids}) ->
+ rm_selected(Pid, Ids, Pids, [], []).
+
+rm_selected(Pid, [_Id|Ids], [Pid|Pids], AccIds, AccPids) ->
+ {lists:reverse(AccIds)++Ids,lists:reverse(AccPids)++Pids};
+rm_selected(Pid, [Id|Ids], [OtherPid|Pids], AccIds, AccPids) ->
+ rm_selected(Pid, Ids, Pids, [Id|AccIds], [OtherPid|AccPids]);
+rm_selected(_, [], [], AccIds, AccPids) ->
+ {lists:reverse(AccIds), lists:reverse(AccPids)}.
+
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%TABLE HOLDER%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
init_table_holder(Parent, Attrs) ->
diff --git a/lib/observer/src/observer_trace_wx.erl b/lib/observer/src/observer_trace_wx.erl
index 9c0243e4a7..af90e2100c 100644
--- a/lib/observer/src/observer_trace_wx.erl
+++ b/lib/observer/src/observer_trace_wx.erl
@@ -19,7 +19,7 @@
-module(observer_trace_wx).
--export([start_link/2, add_processes/2]).
+-export([start_link/2, add_processes/1, add_ports/1]).
-export([init/1, handle_info/2, terminate/2, code_change/3, handle_call/3,
handle_event/2, handle_cast/2]).
@@ -31,11 +31,15 @@
-define(SAVE_TRACEOPTS, 305).
-define(LOAD_TRACEOPTS, 306).
-define(TOGGLE_TRACE, 307).
--define(ADD_NEW, 308).
--define(ADD_TP, 309).
--define(TRACE_OUTPUT, 310).
--define(TRACE_DEFMS, 311).
--define(TRACE_DEFPS, 312).
+-define(ADD_NEW_PROCS, 308).
+-define(ADD_NEW_PORTS, 309).
+-define(ADD_TP, 310).
+-define(TRACE_OUTPUT, 311).
+-define(DEF_MS_FUNCS, 312).
+-define(DEF_MS_SEND, 313).
+-define(DEF_MS_RECV, 314).
+-define(DEF_PROC_OPTS, 315).
+-define(DEF_PORT_OPTS, 316).
-define(NODES_WIN, 330).
-define(ADD_NODES, 331).
@@ -45,36 +49,53 @@
-define(EDIT_PROCS, 341).
-define(REMOVE_PROCS, 342).
--define(MODULES_WIN, 350).
+-define(PORT_WIN, 350).
+-define(EDIT_PORTS, 351).
+-define(REMOVE_PORTS, 352).
--define(FUNCS_WIN, 360).
--define(EDIT_FUNCS_MS, 361).
--define(REMOVE_FUNCS_MS, 362).
+-define(MODULES_WIN, 360).
+-define(REMOVE_MOD_MS, 361).
--define(LOG_WIN, 370).
--define(LOG_SAVE, 321).
--define(LOG_CLEAR, 322).
+-define(FUNCS_WIN, 370).
+-define(EDIT_FUNCS_MS, 371).
+-define(REMOVE_FUNCS_MS, 372).
+
+-define(LOG_WIN, 380).
+-define(LOG_SAVE, 381).
+-define(LOG_CLEAR, 382).
+
+-define(NO_NODES_HELP,"Right click to add nodes").
+-define(NODES_HELP,"Select nodes to see traced processes and ports").
+-define(NO_P_HELP,"Add items from Processes/Ports tab").
+-define(P_HELP,"Select nodes to see traced processes and ports").
+-define(NO_TP_HELP,"Add trace pattern with button below").
+-define(TP_HELP,"Select module to see trace patterns").
-record(state,
{parent,
panel,
- n_view, p_view, m_view, f_view, %% The listCtrl's
+ n_view, proc_view, port_view, m_view, f_view, %% The listCtrl's
logwin, %% The latest log window
nodes = [],
toggle_button,
- tpids = [], %% #tpid
- def_trace_opts = [],
+ tpids = [], % #titem
+ tports = [], % #titem
+ def_proc_flags = [],
+ def_port_flags = [],
output = [],
tpatterns = dict:new(), % Key =:= Module::atom, Value =:= {M, F, A, MatchSpec}
match_specs = []}). % [ #match_spec{} ]
--record(tpid, {pid, opts}).
+-record(titem, {id, opts}).
start_link(Notebook, ParentPid) ->
wx_object:start_link(?MODULE, [Notebook, ParentPid], []).
-add_processes(Tracer, Pids) when is_list(Pids) ->
- wx_object:cast(Tracer, {add_processes, Pids}).
+add_processes(Pids) when is_list(Pids) ->
+ wx_object:cast(observer_wx:get_tracer(), {add_processes, Pids}).
+
+add_ports(Ports) when is_list(Ports) ->
+ wx_object:cast(observer_wx:get_tracer(), {add_ports, Ports}).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -87,11 +108,13 @@ create_window(Notebook, ParentPid) ->
Sizer = wxBoxSizer:new(?wxVERTICAL),
Splitter = wxSplitterWindow:new(Panel, [{size, wxWindow:getClientSize(Panel)},
{style, ?SASH_STYLE}]),
- {NodeProcView, NodeView, ProcessView} = create_process_view(Splitter),
+ {NodeProcView, NodeView, ProcessView, PortView} =
+ create_proc_port_view(Splitter),
{MatchSpecView,ModView,FuncView} = create_matchspec_view(Splitter),
wxSplitterWindow:setSashGravity(Splitter, 0.5),
wxSplitterWindow:setMinimumPaneSize(Splitter,50),
- wxSplitterWindow:splitHorizontally(Splitter, NodeProcView, MatchSpecView),
+ wxSplitterWindow:splitHorizontally(Splitter, NodeProcView, MatchSpecView,
+ [{sashPosition,368}]),
wxSizer:add(Sizer, Splitter, [{flag, ?wxEXPAND bor ?wxALL}, {border, 5}, {proportion, 1}]),
%% Buttons
Buttons = wxBoxSizer:new(?wxHORIZONTAL),
@@ -99,7 +122,8 @@ create_window(Notebook, ParentPid) ->
wxSizer:add(Buttons, ToggleButton, [{flag, ?wxALIGN_CENTER_VERTICAL}]),
wxSizer:addSpacer(Buttons, 15),
wxSizer:add(Buttons, wxButton:new(Panel, ?ADD_NODES, [{label, "Add Nodes"}])),
- wxSizer:add(Buttons, wxButton:new(Panel, ?ADD_NEW, [{label, "Add 'new' Process"}])),
+ wxSizer:add(Buttons, wxButton:new(Panel, ?ADD_NEW_PROCS, [{label, "Add 'new' Processes"}])),
+ wxSizer:add(Buttons, wxButton:new(Panel, ?ADD_NEW_PORTS, [{label, "Add 'new' Ports"}])),
wxSizer:add(Buttons, wxButton:new(Panel, ?ADD_TP, [{label, "Add Trace Pattern"}])),
wxMenu:connect(Panel, command_togglebutton_clicked, [{skip, true}]),
wxMenu:connect(Panel, command_button_clicked, [{skip, true}]),
@@ -107,24 +131,47 @@ create_window(Notebook, ParentPid) ->
{border, 5}, {proportion,0}]),
wxWindow:setSizer(Panel, Sizer),
{Panel, #state{parent=ParentPid, panel=Panel,
- n_view=NodeView, p_view=ProcessView, m_view=ModView, f_view=FuncView,
+ n_view=NodeView, proc_view=ProcessView, port_view=PortView,
+ m_view=ModView, f_view=FuncView,
toggle_button = ToggleButton,
match_specs=default_matchspecs()}}.
default_matchspecs() ->
- Ms = [{"Return Trace", [{'_', [], [{return_trace}]}], "fun(_) -> return_trace() end"},
- {"Exception Trace", [{'_', [], [{exception_trace}]}], "fun(_) -> exception_trace() end"},
- {"Message Caller", [{'_', [], [{message,{caller}}]}], "fun(_) -> message(caller()) end"},
- {"Message Dump", [{'_', [], [{message,{process_dump}}]}], "fun(_) -> message(process_dump()) end"}],
+ [{Key,default_matchspecs(Key)} || Key <- [funcs,send,'receive']].
+default_matchspecs(Key) ->
+ Ms = get_default_matchspecs(Key),
[make_ms(Name,Term,FunStr) || {Name,Term,FunStr} <- Ms].
-create_process_view(Parent) ->
+get_default_matchspecs(funcs) ->
+ [{"Return Trace", [{'_', [], [{return_trace}]}],
+ "fun(_) -> return_trace() end"},
+ {"Exception Trace", [{'_', [], [{exception_trace}]}],
+ "fun(_) -> exception_trace() end"},
+ {"Message Caller", [{'_', [], [{message,{caller}}]}],
+ "fun(_) -> message(caller()) end"},
+ {"Message Dump", [{'_', [], [{message,{process_dump}}]}],
+ "fun(_) -> message(process_dump()) end"}];
+get_default_matchspecs(send) ->
+ [{"To local node", [{['$1','_'], [{'==',{node,'$1'},{node}}], []}],
+ "fun([Pid,_]) when node(Pid)==node() ->\n true\nend"},
+ {"To remote node", [{['$1','_'], [{'=/=',{node,'$1'},{node}}], []}],
+ "fun([Pid,_]) when node(Pid)=/=node() ->\n true\nend"}];
+get_default_matchspecs('receive') ->
+ [{"From local node", [{['$1','_','_'], [{'==','$1',{node}}], []}],
+ "fun([Node,_,_]) when Node==node() ->\n true\nend"},
+ {"From remote node", [{['$1','_','_'], [{'=/=','$1',{node}}], []}],
+ "fun([Node,_,_]) when Node=/=node() ->\n true\nend"}].
+
+
+create_proc_port_view(Parent) ->
Panel = wxPanel:new(Parent),
MainSz = wxBoxSizer:new(?wxHORIZONTAL),
Style = ?wxLC_REPORT bor ?wxLC_HRULES,
Splitter = wxSplitterWindow:new(Panel, [{style, ?SASH_STYLE}]),
Nodes = wxListCtrl:new(Splitter, [{winid, ?NODES_WIN}, {style, Style}]),
- Procs = wxListCtrl:new(Splitter, [{winid, ?PROC_WIN}, {style, Style}]),
+ ProcsPortsSplitter = wxSplitterWindow:new(Splitter, [{style, ?SASH_STYLE}]),
+ Procs = wxListCtrl:new(ProcsPortsSplitter, [{winid,?PROC_WIN},{style,Style}]),
+ Ports = wxListCtrl:new(ProcsPortsSplitter, [{winid,?PORT_WIN},{style,Style}]),
Li = wxListItem:new(),
wxListItem:setText(Li, "Nodes"),
wxListCtrl:insertColumn(Nodes, 0, Li),
@@ -136,31 +183,57 @@ create_process_view(Parent) ->
wxListCtrl:setColumnWidth(Procs, Col, DefSize),
Col + 1
end,
- ListItems = [{"Process Id", ?wxLIST_FORMAT_CENTER, 120},
- {"Trace Options", ?wxLIST_FORMAT_LEFT, 300}],
- lists:foldl(AddProc, 0, ListItems),
+ ProcListItems = [{"Process Id", ?wxLIST_FORMAT_CENTER, 120},
+ {"Trace Options", ?wxLIST_FORMAT_LEFT, 300}],
+ lists:foldl(AddProc, 0, ProcListItems),
+
+ AddPort = fun({Name, Align, DefSize}, Col) ->
+ wxListItem:setText(Li, Name),
+ wxListItem:setAlign(Li, Align),
+ wxListCtrl:insertColumn(Ports, Col, Li),
+ wxListCtrl:setColumnWidth(Ports, Col, DefSize),
+ Col + 1
+ end,
+ PortListItems = [{"Port Id", ?wxLIST_FORMAT_CENTER, 120},
+ {"Trace Options", ?wxLIST_FORMAT_LEFT, 300}],
+ lists:foldl(AddPort, 0, PortListItems),
+
wxListItem:destroy(Li),
wxSplitterWindow:setSashGravity(Splitter, 0.0),
wxSplitterWindow:setMinimumPaneSize(Splitter,50),
- wxSplitterWindow:splitVertically(Splitter, Nodes, Procs, [{sashPosition, 155}]),
+ wxSplitterWindow:splitVertically(Splitter, Nodes, ProcsPortsSplitter,
+ [{sashPosition, 155}]),
wxSizer:add(MainSz, Splitter, [{flag, ?wxEXPAND}, {proportion, 1}]),
+ wxSplitterWindow:setSashGravity(ProcsPortsSplitter, 0.5),
+ wxSplitterWindow:setMinimumPaneSize(ProcsPortsSplitter,50),
+ wxSplitterWindow:splitHorizontally(ProcsPortsSplitter, Procs, Ports,
+ [{sashPosition, 182}]),
+
wxListCtrl:connect(Procs, command_list_item_right_click),
+ wxListCtrl:connect(Ports, command_list_item_right_click),
wxListCtrl:connect(Nodes, command_list_item_right_click),
+ wxListCtrl:connect(Nodes, command_list_item_selected),
wxListCtrl:connect(Procs, size, [{skip, true}]),
+ wxListCtrl:connect(Ports, size, [{skip, true}]),
wxListCtrl:connect(Nodes, size, [{skip, true}]),
+ wxListCtrl:setToolTip(Nodes, ?NO_NODES_HELP),
+ wxListCtrl:setToolTip(Procs, ?NO_P_HELP),
+ wxListCtrl:setToolTip(Ports, ?NO_P_HELP),
+
wxPanel:setSizer(Panel, MainSz),
wxWindow:setFocus(Procs),
- {Panel, Nodes, Procs}.
+ {Panel, Nodes, Procs, Ports}.
create_matchspec_view(Parent) ->
Panel = wxPanel:new(Parent),
MainSz = wxBoxSizer:new(?wxHORIZONTAL),
Style = ?wxLC_REPORT bor ?wxLC_HRULES,
Splitter = wxSplitterWindow:new(Panel, [{style, ?SASH_STYLE}]),
- Modules = wxListCtrl:new(Splitter, [{winid, ?MODULES_WIN}, {style, Style}]),
+ Modules = wxListCtrl:new(Splitter, [{winid, ?MODULES_WIN},
+ {style, Style bor ?wxLC_SINGLE_SEL}]),
Funcs = wxListCtrl:new(Splitter, [{winid, ?FUNCS_WIN}, {style, Style}]),
Li = wxListItem:new(),
@@ -182,7 +255,9 @@ create_matchspec_view(Parent) ->
wxListCtrl:connect(Modules, size, [{skip, true}]),
wxListCtrl:connect(Funcs, size, [{skip, true}]),
wxListCtrl:connect(Modules, command_list_item_selected),
+ wxListCtrl:connect(Modules, command_list_item_right_click),
wxListCtrl:connect(Funcs, command_list_item_right_click),
+ wxListCtrl:setToolTip(Panel, ?NO_TP_HELP),
wxPanel:setSizer(Panel, MainSz),
{Panel, Modules, Funcs}.
@@ -192,8 +267,11 @@ create_menues(Parent) ->
#create_menu{id = ?SAVE_TRACEOPTS, text = "Save settings"}]},
{"Options",
[#create_menu{id = ?TRACE_OUTPUT, text = "Output"},
- #create_menu{id = ?TRACE_DEFMS, text = "Match Specifications"},
- #create_menu{id = ?TRACE_DEFPS, text = "Default Process Options"}]}
+ #create_menu{id = ?DEF_MS_FUNCS, text = "Default Match Specifications for Functions"},
+ #create_menu{id = ?DEF_MS_SEND, text = "Default Match Specifications for 'send'"},
+ #create_menu{id = ?DEF_MS_RECV, text = "Default Match Specifications for 'receive'"},
+ #create_menu{id = ?DEF_PROC_OPTS, text = "Default Process Options"},
+ #create_menu{id = ?DEF_PORT_OPTS, text = "Default Port Options"}]}
],
observer_wx:create_menus(Parent, Menus).
@@ -206,11 +284,19 @@ handle_event(#wx{obj=Obj, event=#wxSize{size={W,_}}}, State) ->
end,
{noreply, State};
-handle_event(#wx{id=?ADD_NEW}, State = #state{panel=Parent, def_trace_opts=TraceOpts}) ->
+handle_event(#wx{id=?ADD_NEW_PROCS}, State = #state{panel=Parent, def_proc_flags=TraceOpts}) ->
try
Opts = observer_traceoptions_wx:process_trace(Parent, TraceOpts),
- Process = #tpid{pid=new, opts=Opts},
- {noreply, do_add_processes([Process], State#state{def_trace_opts=Opts})}
+ Process = #titem{id=new_processes, opts=Opts},
+ {noreply, do_add_processes([Process], State#state{def_proc_flags=Opts})}
+ catch cancel -> {noreply, State}
+ end;
+
+handle_event(#wx{id=?ADD_NEW_PORTS}, State = #state{panel=Parent, def_port_flags=TraceOpts}) ->
+ try
+ Opts = observer_traceoptions_wx:port_trace(Parent, TraceOpts),
+ Port = #titem{id=new_ports, opts=Opts},
+ {noreply, do_add_ports([Port], State#state{def_port_flags=Opts})}
catch cancel -> {noreply, State}
end;
@@ -233,26 +319,36 @@ handle_event(#wx{id=?MODULES_WIN, event=#wxList{type=command_list_item_selected,
update_functions_view(dict:fetch(Module, TPs), Fview),
{noreply, State};
+handle_event(#wx{id=?NODES_WIN,
+ event=#wxList{type=command_list_item_selected}},
+ State = #state{tpids=Tpids, tports=Tports, n_view=Nview,
+ proc_view=ProcView, port_view=PortView, nodes=Ns}) ->
+ Nodes = get_selected_items(Nview, Ns),
+ update_p_view(Tpids, ProcView, Nodes),
+ update_p_view(Tports, PortView, Nodes),
+ {noreply, State};
+
handle_event(#wx{event = #wxCommand{type = command_togglebutton_clicked, commandInt = 1}},
#state{panel = Panel,
nodes = Nodes,
tpids = TProcs,
+ tports = TPorts,
tpatterns = TPs0,
toggle_button = ToggleBtn,
output = Opts
} = State) ->
try
TPs = dict:to_list(TPs0),
- (TProcs == []) andalso throw({error, "No processes traced"}),
+ (TProcs == []) andalso (TPorts == []) andalso throw({error, "No processes or ports traced"}),
(Nodes == []) andalso throw({error, "No nodes traced"}),
- HaveCallTrace = fun(#tpid{opts=Os}) -> lists:member(functions,Os) end,
+ HaveCallTrace = fun(#titem{opts=Os}) -> lists:member(functions,Os) end,
WStr = "Call trace actived but no trace patterns used",
(TPs == []) andalso lists:any(HaveCallTrace, TProcs) andalso
observer_wx:create_txt_dialog(Panel, WStr, "Warning", ?wxICON_WARNING),
{TTB, LogWin} = ttb_output_args(Panel, Opts),
{ok, _} = ttb:tracer(Nodes, TTB),
- setup_ttb(TPs, TProcs),
+ setup_ttb(TPs, TProcs, TPorts),
wxToggleButton:setLabel(ToggleBtn, "Stop Trace"),
{noreply, State#state{logwin=LogWin}}
catch {error, Msg} ->
@@ -302,7 +398,8 @@ handle_event(#wx{id=?LOG_SAVE, userData=TCtrl}, #state{panel=Panel} = State) ->
handle_event(#wx{id = ?SAVE_TRACEOPTS},
#state{panel = Panel,
- def_trace_opts = TraceOpts,
+ def_proc_flags = ProcFlags,
+ def_port_flags = PortFlags,
match_specs = MatchSpecs,
tpatterns = TracePatterns,
output = Output
@@ -312,7 +409,7 @@ handle_event(#wx{id = ?SAVE_TRACEOPTS},
?wxID_OK ->
Path = wxFileDialog:getPath(Dialog),
write_file(Panel, Path,
- TraceOpts, MatchSpecs, Output,
+ ProcFlags, PortFlags, MatchSpecs, Output,
dict:to_list(TracePatterns)
);
_ ->
@@ -333,52 +430,159 @@ handle_event(#wx{id = ?LOAD_TRACEOPTS}, #state{panel = Panel} = State) ->
wxDialog:destroy(Dialog),
{noreply, State2};
-handle_event(#wx{id=Type, event=#wxList{type=command_list_item_right_click}},
- State = #state{panel=Panel}) ->
- Menus = case Type of
- ?PROC_WIN ->
- [{?EDIT_PROCS, "Edit process options"},
- {?REMOVE_PROCS, "Remove processes"}];
- ?FUNCS_WIN ->
- [{?EDIT_FUNCS_MS, "Edit matchspecs"},
- {?REMOVE_FUNCS_MS, "Remove trace patterns"}];
- ?NODES_WIN ->
- [{?ADD_NODES, "Trace other nodes"},
- {?REMOVE_NODES, "Remove nodes"}]
- end,
- Menu = wxMenu:new(),
- [wxMenu:append(Menu,Id,Str) || {Id,Str} <- Menus],
- wxWindow:popupMenu(Panel, Menu),
- wxMenu:destroy(Menu),
+handle_event(#wx{id=?PROC_WIN, event=#wxList{type=command_list_item_right_click}},
+ State = #state{panel=Panel, proc_view=LCtrl, tpids=Tpids,
+ n_view=Nview, nodes=Nodes}) ->
+ case get_visible_ps(Tpids, Nodes, Nview) of
+ [] ->
+ ok;
+ Visible ->
+ case get_selected_items(LCtrl, Visible) of
+ [] ->
+ ok;
+ _ ->
+ create_right_click_menu(
+ Panel,
+ [{?EDIT_PROCS, "Edit process options"},
+ {?REMOVE_PROCS, "Remove processes"}])
+ end
+ end,
+ {noreply, State};
+
+handle_event(#wx{id=?PORT_WIN, event=#wxList{type=command_list_item_right_click}},
+ State = #state{panel=Panel, port_view=LCtrl, tports=Tports,
+ n_view=Nview, nodes=Nodes}) ->
+ case get_visible_ps(Tports, Nodes, Nview) of
+ [] ->
+ ok;
+ Visible ->
+ case get_selected_items(LCtrl, Visible) of
+ [] ->
+ ok;
+ _ ->
+ create_right_click_menu(
+ Panel,
+ [{?EDIT_PORTS, "Edit port options"},
+ {?REMOVE_PORTS, "Remove ports"}])
+ end
+ end,
+ {noreply, State};
+
+handle_event(#wx{id=?MODULES_WIN,event=#wxList{type=command_list_item_right_click}},
+ State = #state{panel=Panel, m_view=Mview, tpatterns=TPs}) ->
+ case get_selected_items(Mview, lists:sort(dict:fetch_keys(TPs))) of
+ [] ->
+ ok;
+ _ ->
+ create_right_click_menu(
+ Panel,
+ [{?REMOVE_MOD_MS, "Remove trace patterns"}])
+ end,
+ {noreply,State};
+
+handle_event(#wx{id=?FUNCS_WIN,event=#wxList{type=command_list_item_right_click}},
+ State = #state{panel=Panel, m_view=Mview, f_view=Fview,
+ tpatterns=TPs}) ->
+ case get_selected_items(Mview, lists:sort(dict:fetch_keys(TPs))) of
+ [] ->
+ ok;
+ [Module] ->
+ case get_selected_items(Fview, dict:fetch(Module, TPs)) of
+ [] ->
+ ok;
+ _ ->
+ create_right_click_menu(
+ Panel,
+ [{?EDIT_FUNCS_MS, "Edit matchspecs"},
+ {?REMOVE_FUNCS_MS, "Remove trace patterns"}])
+ end
+ end,
+ {noreply,State};
+
+handle_event(#wx{id=?NODES_WIN,event=#wxList{type=command_list_item_right_click}},
+ State = #state{panel=Panel, n_view=Nview, nodes=Nodes}) ->
+ Menu =
+ case get_selected_items(Nview, Nodes) of
+ [] ->
+ [{?ADD_NODES, "Add nodes"}];
+ _ ->
+ [{?ADD_NODES, "Add nodes"},
+ {?REMOVE_NODES, "Remove nodes"}]
+ end,
+ create_right_click_menu(Panel,Menu),
{noreply, State};
-handle_event(#wx{id=?EDIT_PROCS}, #state{panel=Panel, tpids=Tpids, p_view=Ps} = State) ->
+handle_event(#wx{id=?EDIT_PROCS}, #state{panel=Panel, tpids=Tpids, proc_view=Procs} = State) ->
try
- [#tpid{opts=DefOpts}|_] = Selected = get_selected_items(Ps, Tpids),
+ [#titem{opts=DefOpts}|_] = Selected = get_selected_items(Procs, Tpids),
Opts = observer_traceoptions_wx:process_trace(Panel, DefOpts),
- Changed = [Tpid#tpid{opts=Opts} || Tpid <- Selected],
- {noreply, do_add_processes(Changed, State#state{def_trace_opts=Opts})}
+ Changed = [Tpid#titem{opts=Opts} || Tpid <- Selected],
+ {noreply, do_add_processes(Changed, State#state{def_proc_flags=Opts})}
catch _:_ ->
{noreply, State}
end;
-handle_event(#wx{id=?REMOVE_PROCS}, #state{tpids=Tpids, p_view=LCtrl} = State) ->
+handle_event(#wx{id=?REMOVE_PROCS},
+ #state{tpids=Tpids, proc_view=LCtrl,
+ n_view=Nview, nodes=Nodes} = State) ->
Selected = get_selected_items(LCtrl, Tpids),
Pids = Tpids -- Selected,
- update_process_view(Pids, LCtrl),
+ update_p_view(Pids, LCtrl, Nodes, Nview),
{noreply, State#state{tpids=Pids}};
-handle_event(#wx{id=?TRACE_DEFPS}, #state{panel=Panel, def_trace_opts=PO} = State) ->
+handle_event(#wx{id=?EDIT_PORTS}, #state{panel=Panel, tports=Tports, port_view=Ports} = State) ->
+ try
+ [#titem{opts=DefOpts}|_] = Selected = get_selected_items(Ports, Tports),
+ Opts = observer_traceoptions_wx:port_trace(Panel, DefOpts),
+ Changed = [Tport#titem{opts=Opts} || Tport <- Selected],
+ {noreply, do_add_ports(Changed, State#state{def_port_flags=Opts})}
+ catch _:_ ->
+ {noreply, State}
+ end;
+
+handle_event(#wx{id=?REMOVE_PORTS},
+ #state{tports=Tports, port_view=LCtrl,
+ n_view=Nview, nodes=Nodes} = State) ->
+ Selected = get_selected_items(LCtrl, Tports),
+ Ports = Tports -- Selected,
+ update_p_view(Ports, LCtrl, Nodes, Nview),
+ {noreply, State#state{tports=Ports}};
+
+handle_event(#wx{id=?DEF_PROC_OPTS}, #state{panel=Panel, def_proc_flags=PO} = State) ->
try
Opts = observer_traceoptions_wx:process_trace(Panel, PO),
- {noreply, State#state{def_trace_opts=Opts}}
+ {noreply, State#state{def_proc_flags=Opts}}
catch _:_ ->
{noreply, State}
end;
-handle_event(#wx{id=?TRACE_DEFMS}, #state{panel=Panel, match_specs=Ms} = State) ->
+handle_event(#wx{id=?DEF_PORT_OPTS}, #state{panel=Panel, def_port_flags=PO} = State) ->
+ try
+ Opts = observer_traceoptions_wx:port_trace(Panel, PO),
+ {noreply, State#state{def_port_flags=Opts}}
+ catch _:_ ->
+ {noreply, State}
+ end;
+
+handle_event(#wx{id=?DEF_MS_FUNCS}, #state{panel=Panel, match_specs=Ms} = State) ->
try %% Return selected MS and sends new MS's to us
- observer_traceoptions_wx:select_matchspec(self(), Panel, Ms)
+ observer_traceoptions_wx:select_matchspec(self(), Panel, Ms, funcs)
+ catch _:_ ->
+ cancel
+ end,
+ {noreply, State};
+
+handle_event(#wx{id=?DEF_MS_SEND}, #state{panel=Panel, match_specs=Ms} = State) ->
+ try %% Return selected MS and sends new MS's to us
+ observer_traceoptions_wx:select_matchspec(self(), Panel, Ms, send)
+ catch _:_ ->
+ cancel
+ end,
+ {noreply, State};
+
+handle_event(#wx{id=?DEF_MS_RECV}, #state{panel=Panel, match_specs=Ms} = State) ->
+ try %% Return selected MS and sends new MS's to us
+ observer_traceoptions_wx:select_matchspec(self(), Panel, Ms, 'receive')
catch _:_ ->
cancel
end,
@@ -389,12 +593,34 @@ handle_event(#wx{id=?EDIT_FUNCS_MS}, #state{panel=Panel, tpatterns=TPs,
match_specs=Mss
} = State) ->
try
- [Module] = get_selected_items(Mview, lists:sort(dict:fetch_keys(TPs))),
- Selected = get_selected_items(LCtrl, dict:fetch(Module, TPs)),
- Ms = observer_traceoptions_wx:select_matchspec(self(), Panel, Mss),
- Changed = [TP#tpattern{ms=Ms} || TP <- Selected],
- {noreply, do_add_patterns({Module, Changed}, State)}
- catch _:_ ->
+ case get_selected_items(Mview, lists:sort(dict:fetch_keys(TPs))) of
+ [] ->
+ throw({error,"No module selected"});
+ [Module] ->
+ Selected = get_selected_items(LCtrl, dict:fetch(Module, TPs)),
+ Key = case Module of
+ 'Events' ->
+ SelectedEvents =
+ [Event || #tpattern{fa=Event} <- Selected],
+ E1 = hd(SelectedEvents),
+ case lists:all(fun(E) when E==E1 -> true;
+ (_) -> false
+ end,
+ SelectedEvents) of
+ true -> E1;
+ false -> throw({error,"Can not set match specs for multiple event types"})
+ end;
+ _ -> funcs
+ end,
+ Ms = observer_traceoptions_wx:select_matchspec(self(), Panel,
+ Mss, Key),
+ Changed = [TP#tpattern{ms=Ms} || TP <- Selected],
+ {noreply, do_add_patterns({Module, Changed}, State)}
+ end
+ catch {error, Msg} ->
+ observer_wx:create_txt_dialog(Panel, Msg, "Error", ?wxICON_ERROR),
+ {noreply, State};
+ cancel ->
{noreply, State}
end;
@@ -417,6 +643,16 @@ handle_event(#wx{id=?REMOVE_FUNCS_MS}, #state{tpatterns=TPs0, f_view=LCtrl, m_vi
{noreply, State#state{tpatterns=TPs}}
end;
+handle_event(#wx{id=?REMOVE_MOD_MS}, #state{tpatterns=TPs0, f_view=LCtrl, m_view=Mview} = State) ->
+ case get_selected_items(Mview, lists:sort(dict:fetch_keys(TPs0))) of
+ [] -> {noreply, State};
+ [Module] ->
+ update_functions_view([], LCtrl),
+ TPs = dict:erase(Module, TPs0),
+ update_modules_view(lists:sort(dict:fetch_keys(TPs)), Module, Mview),
+ {noreply, State#state{tpatterns=TPs}}
+ end;
+
handle_event(#wx{id=?TRACE_OUTPUT}, #state{panel=Panel, output=Out0} = State) ->
try
Out = observer_traceoptions_wx:output(Panel, Out0),
@@ -458,11 +694,20 @@ handle_call(Msg, From, _State) ->
error({unhandled_call, Msg, From}).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-handle_cast({add_processes, Pids}, State = #state{panel=Parent, def_trace_opts=TraceOpts}) ->
+handle_cast({add_processes, Pids}, State = #state{panel=Parent, def_proc_flags=TraceOpts}) ->
try
Opts = observer_traceoptions_wx:process_trace(Parent, TraceOpts),
- POpts = [#tpid{pid=Pid, opts=Opts} || Pid <- Pids],
- S = do_add_processes(POpts, State#state{def_trace_opts=Opts}),
+ POpts = [#titem{id=Pid, opts=Opts} || Pid <- Pids],
+ S = do_add_processes(POpts, State#state{def_proc_flags=Opts}),
+ {noreply, S}
+ catch cancel ->
+ {noreply, State}
+ end;
+handle_cast({add_ports, Ports}, State = #state{panel=Parent, def_port_flags=TraceOpts}) ->
+ try
+ Opts = observer_traceoptions_wx:port_trace(Parent, TraceOpts),
+ POpts = [#titem{id=Id, opts=Opts} || Id <- Ports],
+ S = do_add_ports(POpts, State#state{def_port_flags=Opts}),
{noreply, S}
catch cancel ->
{noreply, State}
@@ -510,45 +755,93 @@ do_add_patterns({Module, NewPs}, State=#state{tpatterns=TPs0, m_view=Mview, f_vi
State#state{tpatterns=TPs}
end.
-do_add_processes(POpts, S0=#state{n_view=Nview, p_view=LCtrl, tpids=OldPids, nodes=Ns0}) ->
- case merge_pids(POpts, OldPids) of
- {OldPids, [], []} ->
- S0;
- {Pids, New, _Changed} ->
- update_process_view(Pids, LCtrl),
- Ns1 = lists:usort([node(Pid) || #tpid{pid=Pid} <- New, is_pid(Pid)]),
+do_add_processes(POpts, S0=#state{n_view=Nview, proc_view=LCtrl, tpids=OldPids, nodes=OldNodes}) ->
+ CheckFun = fun(Pid) -> is_pid(Pid) end,
+ {Pids, Nodes} = do_add_pid_or_port(POpts, Nview, LCtrl,
+ OldPids, OldNodes, CheckFun),
+ S0#state{tpids=Pids, nodes=Nodes}.
+
+do_add_ports(POpts, S0=#state{n_view=Nview, port_view=LCtrl, tports=OldPorts, nodes=OldNodes}) ->
+ CheckFun = fun(Port) -> is_port(Port) end,
+ {Ports, Nodes} = do_add_pid_or_port(POpts, Nview, LCtrl,
+ OldPorts, OldNodes, CheckFun),
+ S0#state{tports=Ports, nodes=Nodes}.
+
+do_add_pid_or_port(POpts, Nview, LCtrl, OldPs, Ns0, Check) ->
+ case merge_trace_items(POpts, OldPs) of
+ {OldPs, [], []} ->
+ {OldPs,Ns0};
+ {Ps, New, _Changed} ->
+ Ns1 = lists:usort([node(Id) || #titem{id=Id} <- New, Check(Id)]),
Nodes = case ordsets:subtract(Ns1, Ns0) of
+ [] when Ns0==[] -> [observer_wx:get_active_node()];
[] -> Ns0; %% No new Nodes
- NewNs ->
- %% if dynamicly updates add trace patterns for new nodes
- All = ordsets:union(NewNs, Ns0),
- update_nodes_view(All, Nview),
- All
+ NewNs -> ordsets:union(NewNs, Ns0)
end,
- S0#state{tpids=Pids, nodes=Nodes}
+ update_nodes_view(Nodes, Nview),
+ update_p_view(Ps, LCtrl, Nodes, Nview),
+ {Ps, Nodes}
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-update_process_view(Pids, LCtrl) ->
+get_visible_ps(PidsOrPorts, [Node], _Nview) ->
+ %% If only one node, treat this as selected
+ get_visible_ps(PidsOrPorts, [Node]);
+get_visible_ps(PidsOrPorts, Nodes, Nview) ->
+ get_visible_ps(PidsOrPorts, get_selected_items(Nview, Nodes)).
+
+get_visible_ps(PidsOrPorts, Nodes) ->
+ %% Show pids/ports belonging to the selected nodes only (+ named pids/ports)
+ [P || P <- PidsOrPorts,
+ is_atom(P#titem.id) orelse
+ lists:member(node(P#titem.id),Nodes)].
+
+update_p_view(PidsOrPorts, LCtrl, Nodes, Nview) ->
+ update_p_view(get_visible_ps(PidsOrPorts, Nodes, Nview), LCtrl).
+update_p_view(PidsOrPorts, LCtrl, Nodes) ->
+ update_p_view(get_visible_ps(PidsOrPorts, Nodes), LCtrl).
+
+update_p_view(PidsOrPorts, LCtrl) ->
+ %% pid- or port-view
wxListCtrl:deleteAllItems(LCtrl),
- wx:foldl(fun(#tpid{pid=Pid, opts=Opts}, Row) ->
+ wx:foldl(fun(#titem{id=Id, opts=Opts}, Row) ->
_Item = wxListCtrl:insertItem(LCtrl, Row, ""),
?EVEN(Row) andalso
wxListCtrl:setItemBackgroundColour(LCtrl, Row, ?BG_EVEN),
- wxListCtrl:setItem(LCtrl, Row, 0, observer_lib:to_str(Pid)),
+ wxListCtrl:setItem(LCtrl, Row, 0, observer_lib:to_str(Id)),
wxListCtrl:setItem(LCtrl, Row, 1, observer_lib:to_str(Opts)),
Row+1
- end, 0, Pids).
+ end, 0, PidsOrPorts),
+ case PidsOrPorts of
+ [] ->
+ wxListCtrl:setToolTip(LCtrl,?NO_P_HELP);
+ _ ->
+ wxListCtrl:setToolTip(LCtrl,?P_HELP)
+ end.
update_nodes_view(Nodes, LCtrl) ->
+ Selected =
+ case Nodes of
+ [_] -> Nodes;
+ _ -> get_selected_items(LCtrl, Nodes)
+ end,
wxListCtrl:deleteAllItems(LCtrl),
wx:foldl(fun(Node, Row) ->
_Item = wxListCtrl:insertItem(LCtrl, Row, ""),
?EVEN(Row) andalso
wxListCtrl:setItemBackgroundColour(LCtrl, Row, ?BG_EVEN),
wxListCtrl:setItem(LCtrl, Row, 0, observer_lib:to_str(Node)),
+ lists:member(Node,Selected) andalso % keep selection
+ wxListCtrl:setItemState(LCtrl, Row, 16#FFFF,
+ ?wxLIST_STATE_SELECTED),
Row+1
- end, 0, Nodes).
+ end, 0, Nodes),
+ case Nodes of
+ [] ->
+ wxListCtrl:setToolTip(LCtrl,?NO_NODES_HELP);
+ _ ->
+ wxListCtrl:setToolTip(LCtrl,?NODES_HELP)
+ end.
update_modules_view(Mods, Module, LCtrl) ->
wxListCtrl:deleteAllItems(LCtrl),
@@ -560,33 +853,51 @@ update_modules_view(Mods, Module, LCtrl) ->
(Mod =:= Module) andalso
wxListCtrl:setItemState(LCtrl, Row, 16#FFFF, ?wxLIST_STATE_SELECTED),
Row+1
- end, 0, Mods).
+ end, 0, Mods),
+ Parent = wxListCtrl:getParent(LCtrl),
+ case Mods of
+ [] ->
+ wxListCtrl:setToolTip(Parent,?NO_TP_HELP);
+ _ ->
+ wxListCtrl:setToolTip(Parent,?TP_HELP)
+ end.
update_functions_view(Funcs, LCtrl) ->
wxListCtrl:deleteAllItems(LCtrl),
- wx:foldl(fun(#tpattern{fa=FA, ms=#match_spec{str=Ms}}, Row) ->
+ wx:foldl(fun(#tpattern{m=M, fa=FA, ms=#match_spec{str=Ms}}, Row) ->
_Item = wxListCtrl:insertItem(LCtrl, Row, ""),
?EVEN(Row) andalso wxListCtrl:setItemBackgroundColour(LCtrl, Row, ?BG_EVEN),
- wxListCtrl:setItem(LCtrl, Row, 0, observer_lib:to_str({func,FA})),
+ FuncStr =
+ case M of
+ 'Events' ->
+ observer_lib:to_str(FA);
+ _ ->
+ observer_lib:to_str({func,FA})
+ end,
+ wxListCtrl:setItem(LCtrl, Row, 0, FuncStr),
wxListCtrl:setItem(LCtrl, Row, 1, Ms),
Row+1
end, 0, Funcs).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-merge_pids([N1=#tpid{pid=new}|Ns], [N2=#tpid{pid=new}|Old]) ->
- {Pids, New, Changed} = merge_pids_1(Ns,Old),
- {[N1|Pids], New, [{N2,N2}|Changed]};
-merge_pids([N1=#tpid{pid=new}|Ns], Old) ->
- {Pids, New, Changed} = merge_pids_1(Ns,Old),
- {[N1|Pids], [N1|New], Changed};
-merge_pids(Ns, [N2=#tpid{pid=new}|Old]) ->
- {Pids, New, Changed} = merge_pids_1(Ns,Old),
- {[N2|Pids], New, Changed};
-merge_pids(New, Old) ->
- merge_pids_1(New, Old).
-
-merge_pids_1(New, Old) ->
- merge(lists:sort(New), Old, #tpid.pid, [], [], []).
+%% Trace items are processes and ports
+merge_trace_items([N1=#titem{id=NewP}|Ns], [N2=#titem{id=NewP}|Old])
+ when NewP==new_processes; NewP==new_ports ->
+ {Ids, New, Changed} = merge_trace_items_1(Ns,Old),
+ {[N1|Ids], New, [{N2,N2}|Changed]};
+merge_trace_items([N1=#titem{id=NewP}|Ns], Old)
+ when NewP==new_processes; NewP==new_ports ->
+ {Ids, New, Changed} = merge_trace_items_1(Ns,Old),
+ {[N1|Ids], [N1|New], Changed};
+merge_trace_items(Ns, [N2=#titem{id=NewP}|Old])
+ when NewP==new_processes; NewP==new_ports ->
+ {Ids, New, Changed} = merge_trace_items_1(Ns,Old),
+ {[N2|Ids], New, Changed};
+merge_trace_items(New, Old) ->
+ merge_trace_items_1(New, Old).
+
+merge_trace_items_1(New, Old) ->
+ merge(lists:sort(New), Old, #titem.id, [], [], []).
merge_patterns(New, Old) ->
merge(lists:sort(New), Old, #tpattern.fa, [], [], []).
@@ -676,10 +987,12 @@ create_logwindow(Parent, true) ->
wxFrame:show(LogWin),
{LogWin, Text}.
-setup_ttb(TPs, TPids) ->
+setup_ttb(TPs, TPids, TPorts) ->
_R1 = [setup_tps(FTP, []) || {_, FTP} <- TPs],
- _R2 = [ttb:p(Pid, dbg_flags(Flags)) || #tpid{pid=Pid, opts=Flags} <- TPids],
- [#tpid{pid=_Pid, opts=_Flags}|_] = TPids,
+ _R2 = [ttb:p(Pid, dbg_flags(proc,Flags)) ||
+ #titem{id=Pid, opts=Flags} <- TPids],
+ _R3 = [ttb:p(Port, dbg_flags(port,Flags)) ||
+ #titem{id=Port, opts=Flags} <- TPorts],
ok.
%% Sigh order is important
@@ -695,20 +1008,24 @@ setup_tps([First|Rest], Prev) ->
setup_tps([], Prev) ->
[setup_tp(TP) || TP <- lists:reverse(Prev)].
+setup_tp(#tpattern{m='Events',fa=Event, ms=#match_spec{term=Ms}}) ->
+ ttb:tpe(Event,Ms);
setup_tp(#tpattern{m=M,fa={F,A}, ms=#match_spec{term=Ms}}) ->
ttb:tpl(M,F,A,Ms).
-dbg_flags(Flags) ->
- [dbg_flag(Flag) || Flag <- Flags].
+dbg_flags(Type,Flags) ->
+ [dbg_flag(Type,Flag) || Flag <- Flags].
-dbg_flag(send) -> s;
-dbg_flag('receive') -> r;
-dbg_flag(functions) -> c;
-dbg_flag(on_spawn) -> sos;
-dbg_flag(on_link) -> sol;
-dbg_flag(on_first_spawn) -> sofs;
-dbg_flag(on_first_link) -> sofl;
-dbg_flag(events) -> p.
+dbg_flag(_,send) -> s;
+dbg_flag(_,'receive') -> r;
+dbg_flag(proc,functions) -> c;
+dbg_flag(proc,on_spawn) -> sos;
+dbg_flag(proc,on_link) -> sol;
+dbg_flag(proc,on_first_spawn) -> sofs;
+dbg_flag(proc,on_first_link) -> sofl;
+dbg_flag(proc,events) -> p;
+dbg_flag(port,events) -> ports;
+dbg_flag(_,Flag) -> Flag.
textformat(Trace) when element(1, Trace) == trace_ts, tuple_size(Trace) >= 4 ->
format_trace(Trace, tuple_size(Trace)-1, element(tuple_size(Trace),Trace));
@@ -784,23 +1101,28 @@ ftup(Trace, Index, Size) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-write_file(Frame, Filename, TraceOps, MatchSpecs, Output, TPs) ->
- FormatMS = fun(#match_spec{name=Id, term=T, func=F}) ->
- io_lib:format("[{name,\"~s\"}, {term, ~w}, {func, \"~s\"}]",
- [Id, T, F])
- end,
- FormatTP = fun({Module, FTPs}) ->
- List = format_ftp(FTPs, FormatMS),
- io_lib:format("{tp, ~w, [~s]}.~n",[Module, List])
+write_file(Frame, Filename, ProcFlags, PortFlags, MatchSpecs, Output, TPs) ->
+ MSToList = fun(#match_spec{name=Id, term=T, func=F}) ->
+ [{name,Id},{term,T},{func,F}]
end,
+ MSTermList = [{ms,Key,[MSToList(MS) || MS <- MSs]} ||
+ {Key,MSs} <- MatchSpecs],
+ TPToTuple = fun(#tpattern{fa={F,A}, ms=Ms}) ->
+ {F,A,MSToList(Ms)}
+ end,
+ ModuleTermList = [{tp, Module, [TPToTuple(FTP) || FTP <- FTPs]} ||
+ {Module,FTPs} <- TPs],
+
Str =
["%%%\n%%% This file is generated by Observer\n",
"%%%\n%%% DO NOT EDIT!\n%%%\n",
- [["{ms, ", FormatMS(Ms), "}.\n"] || Ms <- MatchSpecs],
- "{traceopts, ", io_lib:format("~w",[TraceOps]) ,"}.\n",
- "{output, ", io_lib:format("~w",[Output]) ,"}.\n",
- [FormatTP(TP) || TP <- TPs]
+ [io_lib:format("~p.~n",[MSTerm]) || MSTerm <- MSTermList],
+ io_lib:format("~p.~n",[{procflags,ProcFlags}]),
+ io_lib:format("~p.~n",[{portflags,PortFlags}]),
+ io_lib:format("~p.~n",[{output,Output}]),
+ [io_lib:format("~p.~n",[ModuleTerm]) || ModuleTerm <- ModuleTermList]
],
+
case file:write_file(Filename, list_to_binary(Str)) of
ok ->
success;
@@ -809,38 +1131,66 @@ write_file(Frame, Filename, TraceOps, MatchSpecs, Output, TPs) ->
observer_wx:create_txt_dialog(Frame, FailMsg, "Error", ?wxICON_ERROR)
end.
-format_ftp([#tpattern{fa={F,A}, ms=Ms}], FormatMS) ->
- io_lib:format("{~w, ~w, ~s}", [F,A,FormatMS(Ms)]);
-format_ftp([#tpattern{fa={F,A}, ms=Ms}|Rest], FormatMS) ->
- [io_lib:format("{~w, ~w, ~s},~n ", [F,A,FormatMS(Ms)]),
- format_ftp(Rest, FormatMS)].
-
-read_settings(Filename, #state{match_specs=Ms0, def_trace_opts=TO0} = State) ->
+read_settings(Filename, #state{match_specs=Ms0, def_proc_flags=ProcFs0, def_port_flags=PortFs0} = State) ->
case file:consult(Filename) of
{ok, Terms} ->
- Ms = lists:usort(Ms0 ++ [parse_ms(MsList) || {ms, MsList} <- Terms]),
- TOs = lists:usort(TO0 ++ proplists:get_value(traceopts, Terms, [])),
+ Ms = parse_ms(Terms, Ms0),
+ ProcFs1 = proplists:get_value(procflags, Terms, []) ++
+ proplists:get_value(traceopts, Terms, []), % for backwards comp.
+ ProcFs = lists:usort(ProcFs0 ++ ProcFs1),
+ PortFs = lists:usort(PortFs0 ++
+ proplists:get_value(portflags, Terms, [])),
Out = proplists:get_value(output, Terms, []),
lists:foldl(fun parse_tp/2,
- State#state{match_specs=Ms, def_trace_opts=TOs, output=Out},
+ State#state{match_specs=Ms, def_proc_flags=ProcFs,
+ def_port_flags=PortFs, output=Out},
Terms);
{error, _} ->
- observer_wx:create_txt_dialog(State#state.panel, "Could not load settings",
+ observer_wx:create_txt_dialog(State#state.panel,
+ "Could not load settings",
"Error", ?wxICON_ERROR),
State
end.
-parse_ms(Opts) ->
- Name = proplists:get_value(name, Opts, "TracePattern"),
- Term = proplists:get_value(term, Opts, [{'_',[],[ok]}]),
- FunStr = proplists:get_value(term, Opts, "fun(_) -> ok end"),
- make_ms(Name, Term, FunStr).
+parse_ms(Terms, OldMSs) ->
+ MSs =
+ case [{Key,[make_ms(MS) || MS <- MSs]} || {ms,Key,MSs} <- Terms] of
+ [] ->
+ case [make_ms(MS) || {ms,MS} <- Terms] of
+ [] ->
+ [];
+ FuncMSs -> % for backwards compatibility
+ [{funcs,FuncMSs}]
+ end;
+ KeyMSs ->
+ KeyMSs
+ end,
+ parse_ms_1(MSs, dict:from_list(OldMSs)).
+
+parse_ms_1([{Key,MSs} | T], Dict) ->
+ parse_ms_1(T, dict:append_list(Key,MSs,Dict));
+parse_ms_1([],Dict) ->
+ [{Key,rm_dups(MSs,[])} || {Key,MSs} <- dict:to_list(Dict)].
+
+rm_dups([H|T],Acc) ->
+ case lists:member(H,Acc) of
+ true ->
+ rm_dups(T,Acc);
+ false ->
+ rm_dups(T,[H|Acc])
+ end;
+rm_dups([],Acc) ->
+ lists:reverse(Acc).
+
+make_ms(MS) ->
+ [{func,FunStr},{name,Name},{term,Term}] = lists:keysort(1,MS),
+ make_ms(Name,Term,FunStr).
make_ms(Name, Term, FunStr) ->
#match_spec{name=Name, term=Term, str=io_lib:format("~w", Term), func = FunStr}.
parse_tp({tp, Mod, FAs}, State) ->
- Patterns = [#tpattern{m=Mod,fa={F,A}, ms=parse_ms(List)} ||
+ Patterns = [#tpattern{m=Mod,fa={F,A}, ms=make_ms(List)} ||
{F,A,List} <- FAs],
do_add_patterns({Mod, Patterns}, State);
parse_tp(_, State) ->
@@ -866,3 +1216,9 @@ get_indecies(Rest = [_|_], I, [_|T]) ->
get_indecies(Rest, I+1, T);
get_indecies(_, _, _) ->
[].
+
+create_right_click_menu(Panel,Menus) ->
+ Menu = wxMenu:new(),
+ [wxMenu:append(Menu,Id,Str) || {Id,Str} <- Menus],
+ wxWindow:popupMenu(Panel, Menu),
+ wxMenu:destroy(Menu).
diff --git a/lib/observer/src/observer_traceoptions_wx.erl b/lib/observer/src/observer_traceoptions_wx.erl
index 9ba9b72b6f..285c298c4b 100644
--- a/lib/observer/src/observer_traceoptions_wx.erl
+++ b/lib/observer/src/observer_traceoptions_wx.erl
@@ -22,8 +22,8 @@
-include_lib("wx/include/wx.hrl").
-include("observer_defs.hrl").
--export([process_trace/2, trace_pattern/4, select_nodes/2,
- output/2, select_matchspec/3]).
+-export([process_trace/2, port_trace/2, trace_pattern/4, select_nodes/2,
+ output/2, select_matchspec/4]).
process_trace(Parent, Default) ->
Dialog = wxDialog:new(Parent, ?wxID_ANY, "Process Options",
@@ -36,12 +36,20 @@ process_trace(Parent, Default) ->
FuncBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace function call", []),
check_box(FuncBox, lists:member(functions, Default)),
+ ArityBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace arity instead of arguments", []),
+ check_box(ArityBox, lists:member(functions, Default)),
SendBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace send message", []),
check_box(SendBox, lists:member(send, Default)),
RecBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace receive message", []),
check_box(RecBox, lists:member('receive', Default)),
EventBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace process events", []),
check_box(EventBox, lists:member(events, Default)),
+ SchedBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace scheduling of processes", []),
+ check_box(SchedBox, lists:member(running_procs, Default)),
+ ExitBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace scheduling of exiting processes", []),
+ check_box(ExitBox, lists:member(exiting, Default)),
+ GCBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace garbage collections", []),
+ check_box(GCBox, lists:member(garbage_collection, Default)),
{SpawnBox, SpwnAllRadio, SpwnFirstRadio} =
optionpage_top_right(Panel, RightSz, [{flag, ?wxBOTTOM},{border, 5}], "spawn"),
@@ -57,7 +65,7 @@ process_trace(Parent, Default) ->
{Radio, Opt} <- [{SpwnAllRadio, on_spawn}, {SpwnFirstRadio, on_first_spawn},
{LinkAllRadio, on_link}, {LinkFirstRadio, on_first_link}]],
- [wxSizer:add(LeftSz, CheckBox, []) || CheckBox <- [FuncBox,SendBox,RecBox,EventBox]],
+ [wxSizer:add(LeftSz, CheckBox, []) || CheckBox <- [FuncBox,ArityBox,SendBox,RecBox,EventBox,SchedBox,ExitBox,GCBox]],
wxSizer:add(LeftSz, 150, -1),
wxSizer:add(PanelSz, LeftSz, [{flag, ?wxEXPAND}, {proportion,1}]),
@@ -80,7 +88,9 @@ process_trace(Parent, Default) ->
case wxDialog:showModal(Dialog) of
?wxID_OK ->
All = [{SendBox, send}, {RecBox, 'receive'},
- {FuncBox, functions}, {EventBox, events},
+ {FuncBox, functions}, {ArityBox, arity},
+ {EventBox, events}, {SchedBox, running_procs},
+ {ExitBox, exiting}, {GCBox, garbage_collection},
{{SpawnBox, SpwnAllRadio}, on_spawn},
{{SpawnBox,SpwnFirstRadio}, on_first_spawn},
{{LinkBox, LinkAllRadio}, on_link},
@@ -98,12 +108,57 @@ process_trace(Parent, Default) ->
throw(cancel)
end.
+port_trace(Parent, Default) ->
+ Dialog = wxDialog:new(Parent, ?wxID_ANY, "Port Options",
+ [{style, ?wxDEFAULT_DIALOG_STYLE bor ?wxRESIZE_BORDER}]),
+ Panel = wxPanel:new(Dialog),
+ MainSz = wxBoxSizer:new(?wxVERTICAL),
+ OptsSz = wxStaticBoxSizer:new(?wxVERTICAL, Panel, [{label, "Tracing options"}]),
+
+ SendBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace send message", []),
+ check_box(SendBox, lists:member(send, Default)),
+ RecBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace receive message", []),
+ check_box(RecBox, lists:member('receive', Default)),
+ EventBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace port events", []),
+ check_box(EventBox, lists:member(events, Default)),
+ SchedBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace scheduling of ports", []),
+ check_box(SchedBox, lists:member(running_ports, Default)),
+
+ [wxSizer:add(OptsSz, CheckBox, []) || CheckBox <- [SendBox,RecBox,EventBox,SchedBox]],
+ wxSizer:add(OptsSz, 150, -1),
+
+ wxPanel:setSizer(Panel, OptsSz),
+ wxSizer:add(MainSz, Panel, [{flag, ?wxEXPAND}, {proportion,1}]),
+ Buttons = wxDialog:createButtonSizer(Dialog, ?wxOK bor ?wxCANCEL),
+ wxSizer:add(MainSz, Buttons, [{flag, ?wxEXPAND bor ?wxALL}, {border, 5}]),
+ wxWindow:setSizerAndFit(Dialog, MainSz),
+ wxSizer:setSizeHints(MainSz, Dialog),
+
+ case wxDialog:showModal(Dialog) of
+ ?wxID_OK ->
+ All = [{SendBox, send}, {RecBox, 'receive'},
+ {EventBox, events}, {SchedBox, running_ports}],
+ Opts = [Id || {Tick, Id} <- All, wxCheckBox:getValue(Tick)],
+ wxDialog:destroy(Dialog),
+ lists:reverse(Opts);
+ ?wxID_CANCEL ->
+ wxDialog:destroy(Dialog),
+ throw(cancel)
+ end.
+
trace_pattern(ParentPid, Parent, Node, MatchSpecs) ->
try
- Module = module_selector(Parent, Node),
- MFAs = function_selector(Parent, Node, Module),
- MatchSpec = select_matchspec(ParentPid, Parent, MatchSpecs),
- {Module, [#tpattern{m=M,fa={F,A},ms=MatchSpec} || {M,F,A} <- MFAs]}
+ {Module,MFAs,MatchSpec} =
+ case module_selector(Parent, Node) of
+ {'$trace_event',Event} ->
+ MS = select_matchspec(ParentPid, Parent, MatchSpecs, Event),
+ {'Events',[{'Events',Event}],MS};
+ Mod ->
+ MFAs0 = function_selector(Parent, Node, Mod),
+ MS = select_matchspec(ParentPid, Parent, MatchSpecs, funcs),
+ {Mod,MFAs0,MS}
+ end,
+ {Module, [#tpattern{m=M,fa=FA,ms=MatchSpec} || {M,FA} <- MFAs]}
catch cancel -> cancel
end.
@@ -112,7 +167,7 @@ select_nodes(Parent, Nodes) ->
check_selector(Parent, Choices).
module_selector(Parent, Node) ->
- Dialog = wxDialog:new(Parent, ?wxID_ANY, "Select Module",
+ Dialog = wxDialog:new(Parent, ?wxID_ANY, "Select Module or Event",
[{style, ?wxDEFAULT_DIALOG_STYLE bor ?wxRESIZE_BORDER},
{size, {400, 400}}]),
Panel = wxPanel:new(Dialog),
@@ -136,7 +191,9 @@ module_selector(Parent, Node) ->
wxWindow:setFocus(TxtCtrl),
%% init data
Modules = get_modules(Node),
- AllModules = [{atom_to_list(X), X} || X <- Modules],
+ Events = [{"Messages sent",{'$trace_event',send}},
+ {"Messages received",{'$trace_event','receive'}}],
+ AllModules = Events ++ [{atom_to_list(X), X} || X <- Modules],
filter_listbox_data("", AllModules, ListBox),
wxTextCtrl:connect(TxtCtrl, command_text_updated,
[{callback, fun(#wx{event=#wxCommand{cmdString=Input}}, _) ->
@@ -174,9 +231,9 @@ function_selector(Parent, Node, Module) ->
not(erl_internal:guard_bif(Name, Arity))]),
ParsedChoices = parse_function_names(Choices),
case check_selector(Parent, ParsedChoices) of
- [] -> [{Module, '_', '_'}];
+ [] -> [{Module, {'_', '_'}}];
FAs ->
- [{Module, F, A} || {F,A} <- FAs]
+ [{Module, {F, A}} || {F,A} <- FAs]
end.
check_selector(Parent, ParsedChoices) ->
@@ -268,7 +325,12 @@ get_checked(ListBox, Acc) ->
lists:reverse(Acc)
end.
-select_matchspec(Pid, Parent, MatchSpecs) ->
+select_matchspec(Pid, Parent, AllMatchSpecs, Key) ->
+ {MatchSpecs,RestMS} =
+ case lists:keytake(Key,1,AllMatchSpecs) of
+ {value,{Key,MSs0},Rest} -> {MSs0,Rest};
+ false -> {[],AllMatchSpecs}
+ end,
Dialog = wxDialog:new(Parent, ?wxID_ANY, "Trace Match Specifications",
[{style, ?wxDEFAULT_DIALOG_STYLE bor ?wxRESIZE_BORDER},
{size, {400, 400}}]),
@@ -313,8 +375,12 @@ select_matchspec(Pid, Parent, MatchSpecs) ->
filter_listbox_data("", Choices, ListBox),
Add = fun(_,_) ->
- case edit_ms(TextCtrl, new, Parent) of
- Ms = #match_spec{} -> add_and_select(-1, Ms, ListBox);
+ case edit_ms(TextCtrl, new, Dialog) of
+ Ms = #match_spec{} ->
+ add_and_select(-1, Ms, ListBox),
+ wxWindow:enable(OkButt),
+ wxWindow:enable(EditMsBtn),
+ wxWindow:enable(DelMsBtn);
Else -> Else
end
end,
@@ -323,8 +389,12 @@ select_matchspec(Pid, Parent, MatchSpecs) ->
case SelId >= 0 of
true ->
#match_spec{name=Name} = wxListBox:getClientData(ListBox,SelId),
- case edit_ms(TextCtrl, Name, Parent) of
- Ms = #match_spec{} -> add_and_select(SelId, Ms, ListBox);
+ case edit_ms(TextCtrl, Name, Dialog) of
+ Ms = #match_spec{} ->
+ add_and_select(SelId, Ms, ListBox),
+ wxWindow:enable(OkButt),
+ wxWindow:enable(EditMsBtn),
+ wxWindow:enable(DelMsBtn);
Else -> Else
end;
false ->
@@ -367,7 +437,7 @@ select_matchspec(Pid, Parent, MatchSpecs) ->
Count = wxListBox:getCount(ListBox),
MSs = [wxListBox:getClientData(ListBox, Id) ||
Id <- lists:seq(0, Count-1)],
- Pid ! {update_ms, MSs},
+ Pid ! {update_ms, [{Key,MSs}|RestMS]},
MS = lists:nth(SelId+1, MSs),
wxDialog:destroy(Dialog),
MS;
diff --git a/lib/observer/src/observer_tv_wx.erl b/lib/observer/src/observer_tv_wx.erl
index 1860f2f469..59f6443551 100644
--- a/lib/observer/src/observer_tv_wx.erl
+++ b/lib/observer/src/observer_tv_wx.erl
@@ -37,7 +37,8 @@
-define(ID_UNREADABLE, 405).
-define(ID_SYSTEM_TABLES, 406).
-define(ID_TABLE_INFO, 407).
-
+-define(ID_SHOW_TABLE, 408).
+
-record(opt, {type=ets,
sys_hidden=true,
unread_hidden=true,
@@ -49,6 +50,7 @@
{
parent,
grid,
+ panel,
node=node(),
opt=#opt{},
selected,
@@ -86,12 +88,13 @@ init([Notebook, Parent]) ->
wxListItem:destroy(Li),
wxListCtrl:connect(Grid, command_list_item_activated),
+ wxListCtrl:connect(Grid, command_list_item_right_click),
wxListCtrl:connect(Grid, command_list_item_selected),
wxListCtrl:connect(Grid, command_list_col_click),
wxListCtrl:connect(Grid, size, [{skip, true}]),
wxWindow:setFocus(Grid),
- {Panel, #state{grid=Grid, parent=Parent, timer={false, 10}}}.
+ {Panel, #state{grid=Grid, parent=Parent, panel=Panel, timer={false, 10}}}.
handle_event(#wx{id=?ID_REFRESH},
State = #state{node=Node, grid=Grid, opt=Opt}) ->
@@ -145,6 +148,16 @@ handle_event(#wx{event=#wxList{type=command_list_item_activated,
end,
{noreply, State};
+handle_event(#wx{event=#wxList{type=command_list_item_right_click}},
+ State=#state{panel=Panel}) ->
+
+ Menu = wxMenu:new(),
+ wxMenu:append(Menu, ?ID_TABLE_INFO, "Table info"),
+ wxMenu:append(Menu, ?ID_SHOW_TABLE, "Show Table Content"),
+ wxWindow:popupMenu(Panel, Menu),
+ wxMenu:destroy(Menu),
+ {noreply, State};
+
handle_event(#wx{event=#wxList{type=command_list_item_selected, itemIndex=Index}},
State) ->
{noreply, State#state{selected=Index}};
@@ -160,6 +173,22 @@ handle_event(#wx{id=?ID_TABLE_INFO},
{noreply, State}
end;
+handle_event(#wx{id=?ID_SHOW_TABLE},
+ State=#state{grid=Grid, node=Node, opt=#opt{type=Type}, tabs=Tabs, selected=Sel}) ->
+ case Sel of
+ undefined ->
+ {noreply, State};
+ R when is_integer(R) ->
+ Table = lists:nth(Sel+1, Tabs),
+ case Table#tab.protection of
+ private ->
+ self() ! {error, "Table has 'private' protection and can not be read"};
+ _ ->
+ observer_tv_table:start_link(Grid, [{node,Node}, {type,Type}, {table,Table}])
+ end,
+ {noreply, State}
+ end;
+
handle_event(#wx{id=?ID_REFRESH_INTERVAL},
State = #state{grid=Grid, timer=Timer0}) ->
Timer = observer_lib:interval_dialog(Grid, Timer0, 10, 5*60),
@@ -315,6 +344,7 @@ display_table_info(Parent0, Node, Source, Table) ->
{_, Sizer, _} = observer_lib:display_info(Frame, [IdInfo,Settings,Memory]),
wxSizer:setSizeHints(Sizer, Frame),
+ wxWindow:setMinSize(Frame, {300, -1}),
wxFrame:center(Frame),
wxFrame:show(Frame).
diff --git a/lib/observer/src/observer_wx.erl b/lib/observer/src/observer_wx.erl
index 30bd4043e4..301bb4b32f 100644
--- a/lib/observer/src/observer_wx.erl
+++ b/lib/observer/src/observer_wx.erl
@@ -21,8 +21,8 @@
-behaviour(wx_object).
-export([start/0, stop/0]).
--export([create_menus/2, get_attrib/1, get_tracer/0, set_status/1,
- create_txt_dialog/4, try_rpc/4, return_to_localnode/2]).
+-export([create_menus/2, get_attrib/1, get_tracer/0, get_active_node/0,
+ set_status/1, create_txt_dialog/4, try_rpc/4, return_to_localnode/2]).
-export([init/1, handle_event/2, handle_cast/2, terminate/2, code_change/3,
handle_call/3, handle_info/2, check_page_title/1]).
@@ -55,6 +55,7 @@
notebook,
main_panel,
pro_panel,
+ port_panel,
tv_panel,
sys_panel,
trace_panel,
@@ -90,6 +91,9 @@ set_status(What) ->
get_tracer() ->
wx_object:call(observer, get_tracer).
+get_active_node() ->
+ wx_object:call(observer, get_active_node).
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
init(_Args) ->
@@ -165,6 +169,10 @@ setup(#state{frame = Frame} = State) ->
ProPanel = observer_pro_wx:start_link(Notebook, self()),
wxNotebook:addPage(Notebook, ProPanel, "Processes", []),
+ %% Port Panel
+ PortPanel = observer_port_wx:start_link(Notebook, self()),
+ wxNotebook:addPage(Notebook, PortPanel, "Ports", []),
+
%% Table Viewer Panel
TVPanel = observer_tv_wx:start_link(Notebook, self()),
wxNotebook:addPage(Notebook, TVPanel, "Table Viewer", []),
@@ -188,6 +196,7 @@ setup(#state{frame = Frame} = State) ->
status_bar = StatusBar,
sys_panel = SysPanel,
pro_panel = ProPanel,
+ port_panel = PortPanel,
tv_panel = TVPanel,
trace_panel = TracePanel,
app_panel = AppPanel,
@@ -379,6 +388,9 @@ handle_call({get_attrib, Attrib}, _From, State) ->
handle_call(get_tracer, _From, State=#state{trace_panel=TraceP}) ->
{reply, TraceP, State};
+handle_call(get_active_node, _From, State=#state{node=Node}) ->
+ {reply, Node, State};
+
handle_call(stop, From, State) ->
stop_servers(State),
{noreply, State#state{reply_to=From}};
@@ -406,16 +418,21 @@ handle_info({nodedown, Node},
create_txt_dialog(Frame, Msg, "Node down", ?wxICON_EXCLAMATION),
{noreply, State3};
-handle_info({open_link, Pid0}, State = #state{pro_panel=ProcViewer, frame=Frame}) ->
- Pid = case Pid0 of
- [_|_] -> try list_to_pid(Pid0) catch _:_ -> Pid0 end;
- _ -> Pid0
+handle_info({open_link, Id0}, State = #state{pro_panel=ProcViewer,
+ port_panel=PortViewer,
+ frame=Frame}) ->
+ Id = case Id0 of
+ [_|_] -> try list_to_pid(Id0) catch _:_ -> Id0 end;
+ _ -> Id0
end,
%% Forward to process tab
- case is_pid(Pid) of
- true -> wx_object:get_pid(ProcViewer) ! {procinfo_open, Pid};
- false ->
- Msg = io_lib:format("Information about ~p is not available or implemented",[Pid]),
+ case Id of
+ Pid when is_pid(Pid) ->
+ wx_object:get_pid(ProcViewer) ! {procinfo_open, Pid};
+ "#Port" ++ _ = Port ->
+ wx_object:get_pid(PortViewer) ! {portinfo_open, Port};
+ _ ->
+ Msg = io_lib:format("Information about ~p is not available or implemented",[Id]),
Info = wxMessageDialog:new(Frame, Msg),
wxMessageDialog:showModal(Info),
wxMessageDialog:destroy(Info)
@@ -541,10 +558,11 @@ check_page_title(Notebook) ->
get_active_pid(#state{notebook=Notebook, pro_panel=Pro, sys_panel=Sys,
tv_panel=Tv, trace_panel=Trace, app_panel=App,
- perf_panel=Perf, allc_panel=Alloc
+ perf_panel=Perf, allc_panel=Alloc, port_panel=Port
}) ->
Panel = case check_page_title(Notebook) of
"Processes" -> Pro;
+ "Ports" -> Port;
"System" -> Sys;
"Table Viewer" -> Tv;
?TRACE_STR -> Trace;
diff --git a/lib/observer/src/ttb.erl b/lib/observer/src/ttb.erl
index 4d6eb3ba8d..ac6c4572eb 100644
--- a/lib/observer/src/ttb.erl
+++ b/lib/observer/src/ttb.erl
@@ -25,7 +25,8 @@
-export([tracer/0,tracer/1,tracer/2,p/2,stop/0,stop/1,start_trace/4]).
-export([get_et_handler/0]).
-export([tp/2, tp/3, tp/4, ctp/0, ctp/1, ctp/2, ctp/3, tpl/2, tpl/3, tpl/4,
- ctpl/0, ctpl/1, ctpl/2, ctpl/3, ctpg/0, ctpg/1, ctpg/2, ctpg/3]).
+ ctpl/0, ctpl/1, ctpl/2, ctpl/3, ctpg/0, ctpg/1, ctpg/2, ctpg/3,
+ tpe/2, ctpe/1]).
-export([seq_trigger_ms/0,seq_trigger_ms/1]).
-export([write_trace_info/2]).
-export([write_config/2,write_config/3,run_config/1,run_config/2,list_config/1]).
@@ -397,16 +398,16 @@ arg_list([A1|A],Acc) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Set trace flags on processes
-p(Procs0,Flags0) ->
+p(ProcsPorts0,Flags0) ->
ensure_no_overloaded_nodes(),
- store(p,[Procs0,Flags0]),
- no_store_p(Procs0,Flags0).
-no_store_p(Procs0,Flags0) ->
+ store(p,[ProcsPorts0,Flags0]),
+ no_store_p(ProcsPorts0,Flags0).
+no_store_p(ProcsPorts0,Flags0) ->
case transform_flags(to_list(Flags0)) of
{error,Reason} ->
{error,Reason};
Flags ->
- Procs = procs(Procs0),
+ ProcsPorts = procs_ports(ProcsPorts0),
case lists:foldl(fun(P,{PMatched,Ps}) -> case dbg:p(P,Flags) of
{ok,Matched} ->
{[{P,Matched}|PMatched],[P|Ps]};
@@ -414,7 +415,7 @@ no_store_p(Procs0,Flags0) ->
display_warning(P,Reason),
{PMatched,Ps}
end
- end,{[],[]},Procs) of
+ end,{[],[]},ProcsPorts) of
{[],[]} -> {error, no_match};
{SuccMatched,Succ} ->
no_store_write_trace_info(flags,{Succ,Flags}),
@@ -429,20 +430,22 @@ transform_flags(Flags) ->
dbg:transform_flags([timestamp | Flags]).
-procs(Procs) when is_list(Procs) ->
- lists:foldl(fun(P,Acc) -> proc(P)++Acc end,[],Procs);
-procs(Proc) ->
- proc(Proc).
+procs_ports(Procs) when is_list(Procs) ->
+ lists:foldl(fun(P,Acc) -> proc_port(P)++Acc end,[],Procs);
+procs_ports(Proc) ->
+ proc_port(Proc).
-proc(Procs) when Procs=:=all; Procs=:=ports; Procs=:=processes;
- Procs=:=existing; Procs=:=existing_ports; Procs=:=existing_processes;
- Procs=:=new; Procs=:=new_ports; Procs=:=new_processes ->
- [Procs];
-proc(Name) when is_atom(Name) ->
+proc_port(P) when P=:=all; P=:=ports; P=:=processes;
+ P=:=existing; P=:=existing_ports; P=:=existing_processes;
+ P=:=new; P=:=new_ports; P=:=new_processes ->
+ [P];
+proc_port(Name) when is_atom(Name) ->
[Name]; % can be registered on this node or other node
-proc(Pid) when is_pid(Pid) ->
+proc_port(Pid) when is_pid(Pid) ->
[Pid];
-proc({global,Name}) ->
+proc_port(Port) when is_port(Port) ->
+ [Port];
+proc_port({global,Name}) ->
case global:whereis_name(Name) of
Pid when is_pid(Pid) ->
[Pid];
@@ -479,6 +482,11 @@ tpl(A,B,C,D) ->
store(tpl,[A,B,C,ms(D)]),
dbg:tpl(A,B,C,ms(D)).
+tpe(A,B) ->
+ ensure_no_overloaded_nodes(),
+ store(tpe,[A,ms(B)]),
+ dbg:tpe(A,ms(B)).
+
ctp() ->
store(ctp,[]),
dbg:ctp().
@@ -518,6 +526,10 @@ ctpg(A,B,C) ->
store(ctpg,[A,B,C]),
dbg:ctpg(A,B,C).
+ctpe(A) ->
+ store(ctpe,[A]),
+ dbg:ctpe(A).
+
ms(return) ->
[{'_',[],[{return_trace}]}];
ms(caller) ->
@@ -1298,6 +1310,9 @@ ip_to_file(Trace, {shell_only, Fun} = State) ->
ip_to_file(Trace,{{file,File}, ShellOutput}) ->
Fun = dbg:trace_port(file,File), %File can be a filename or a wrap spec
Port = Fun(),
+ %% Just in case this is on the traced node,
+ %% make sure the port is not traced.
+ p(Port,clear),
%% Store the port so it can be properly closed
?MODULE ! {ip_to_file_trace_port, Port, self()},
receive {?MODULE,ok} -> ok end,
diff --git a/lib/observer/test/observer_SUITE.erl b/lib/observer/test/observer_SUITE.erl
index ae8111dcd6..4c882ad951 100644
--- a/lib/observer/test/observer_SUITE.erl
+++ b/lib/observer/test/observer_SUITE.erl
@@ -171,6 +171,7 @@ test_page("Applications" ++ _, _Window) ->
test_page("Processes" ++ _, _Window) ->
timer:sleep(500), %% Give it time to refresh
Active = get_active(),
+ Active ! refresh_interval,
ChangeSort = fun(N) ->
FakeEv = #wx{event=#wxList{type=command_list_col_click, col=N}},
Active ! FakeEv,
@@ -184,7 +185,23 @@ test_page("Processes" ++ _, _Window) ->
timer:sleep(1000), %% Give it time to refresh
ok;
-test_page(_Title = "Table" ++ _, _Window) ->
+test_page("Ports" ++ _, _Window) ->
+ timer:sleep(500), %% Give it time to refresh
+ Active = get_active(),
+ Active ! refresh_interval,
+ ChangeSort = fun(N) ->
+ FakeEv = #wx{event=#wxList{type=command_list_col_click, col=N}},
+ Active ! FakeEv,
+ timer:sleep(200)
+ end,
+ [ChangeSort(N) || N <- lists:seq(1,4) ++ [0]],
+ Activate = #wx{event=#wxList{type=command_list_item_activated,
+ itemIndex=2}},
+ Active ! Activate,
+ timer:sleep(1000), %% Give it time to refresh
+ ok;
+
+test_page("Table" ++ _, _Window) ->
Tables = [ets:new(list_to_atom("Test-" ++ [C]), [public]) || C <- lists:seq($A, $Z)],
Table = lists:nth(3, Tables),
ets:insert(Table, [{N,100-N} || N <- lists:seq(1,100)]),
@@ -208,6 +225,13 @@ test_page(_Title = "Table" ++ _, _Window) ->
timer:sleep(1000),
ok;
+test_page("Trace Overview" ++ _, _Window) ->
+ timer:sleep(500), %% Give it time to refresh
+ Active = get_active(),
+ Active ! refresh_interval,
+ timer:sleep(1000), %% Give it time to refresh
+ ok;
+
test_page(Title, Window) ->
io:format("Page ~p: ~p~n", [Title, Window]),
%% Just let it display some info and hopefully it doesn't crash
diff --git a/lib/runtime_tools/c_src/dyntrace.c b/lib/runtime_tools/c_src/dyntrace.c
index e7a4a73373..3d2de9c21c 100644
--- a/lib/runtime_tools/c_src/dyntrace.c
+++ b/lib/runtime_tools/c_src/dyntrace.c
@@ -96,14 +96,14 @@ static ErlNifFunc nif_funcs[] = {
{"user_trace_i4s4", 9, user_trace_i4s4},
{"user_trace_n", 10, user_trace_n},
#ifdef HAVE_USE_LTTNG
- {"trace_procs", 6, trace_procs},
- {"trace_ports", 6, trace_ports},
- {"trace_running_procs", 6, trace_running_procs},
- {"trace_running_ports", 6, trace_running_ports},
- {"trace_call", 6, trace_call},
- {"trace_send", 6, trace_send},
- {"trace_receive", 6, trace_receive},
- {"trace_garbage_collection", 6, trace_garbage_collection},
+ {"trace_procs", 5, trace_procs},
+ {"trace_ports", 5, trace_ports},
+ {"trace_running_procs", 5, trace_running_procs},
+ {"trace_running_ports", 5, trace_running_ports},
+ {"trace_call", 5, trace_call},
+ {"trace_send", 5, trace_send},
+ {"trace_receive", 5, trace_receive},
+ {"trace_garbage_collection", 5, trace_garbage_collection},
{"enabled_procs", 3, enabled_procs},
{"enabled_ports", 3, enabled_ports},
{"enabled_running_procs", 3, enabled_running_procs},
@@ -114,8 +114,7 @@ static ErlNifFunc nif_funcs[] = {
{"enabled_garbage_collection", 3, enabled_garbage_collection},
#endif
{"enabled", 3, enabled},
- {"trace", 5, trace},
- {"trace", 6, trace}
+ {"trace", 5, trace}
};
ERL_NIF_INIT(dyntrace, nif_funcs, load, NULL, NULL, NULL)
@@ -123,6 +122,7 @@ ERL_NIF_INIT(dyntrace, nif_funcs, load, NULL, NULL, NULL)
static ERL_NIF_TERM atom_true;
static ERL_NIF_TERM atom_false;
static ERL_NIF_TERM atom_error;
+static ERL_NIF_TERM atom_extra;
static ERL_NIF_TERM atom_not_available;
static ERL_NIF_TERM atom_badarg;
static ERL_NIF_TERM atom_ok;
@@ -187,6 +187,7 @@ static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
atom_true = enif_make_atom(env,"true");
atom_false = enif_make_atom(env,"false");
atom_error = enif_make_atom(env,"error");
+ atom_extra = enif_make_atom(env,"extra");
atom_not_available = enif_make_atom(env,"not_available");
atom_badarg = enif_make_atom(env,"badarg");
atom_ok = enif_make_atom(env,"ok");
@@ -325,7 +326,7 @@ static ERL_NIF_TERM trace_garbage_collection(ErlNifEnv* env, int argc, const ERL
int arity;
unsigned long ohbsz, nhbsz, size;
- ASSERT(argc == 6);
+ ASSERT(argc == 5);
/* Assume gc info order does not change */
gci = argv[3];
@@ -428,11 +429,13 @@ static ERL_NIF_TERM trace_call(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv
}
} else if (argv[0] == atom_exception_from) {
const ERL_NIF_TERM* tuple;
+ ERL_NIF_TERM extra;
int arity;
lttng_decl_mfabuf(mfa);
char class[LTTNG_BUFFER_SZ];
- enif_get_tuple(env, argv[4], &arity, &tuple);
+ enif_get_map_value(env, argv[4], atom_extra, &extra);
+ enif_get_tuple(env, extra, &arity, &tuple);
enif_snprintf(class, LTTNG_BUFFER_SZ, "%T", tuple[0]);
if (enif_get_tuple(env, argv[3], &arity, &tuple)) {
@@ -457,14 +460,17 @@ static ERL_NIF_TERM enabled_send(ErlNifEnv *env, int argc, const ERL_NIF_TERM ar
static ERL_NIF_TERM trace_send(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
+ ERL_NIF_TERM extra;
lttng_decl_procbuf(pid);
lttng_pid_to_str(argv[2], pid);
+ enif_get_map_value(env, argv[4], atom_extra, &extra);
+
if (argv[0] == atom_send) {
lttng_decl_procbuf(to);
char msg[LTTNG_BUFFER_SZ];
- lttng_pid_to_str(argv[4], to);
+ lttng_pid_to_str(extra, to);
enif_snprintf(msg, LTTNG_BUFFER_SZ, "%T", argv[3]);
LTTNG3(message_send, pid, to, msg);
@@ -472,7 +478,7 @@ static ERL_NIF_TERM trace_send(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv
lttng_decl_procbuf(to);
char msg[LTTNG_BUFFER_SZ];
- lttng_pid_to_str(argv[4], to);
+ lttng_pid_to_str(extra, to);
enif_snprintf(msg, LTTNG_BUFFER_SZ, "%T", argv[3]);
/* mark it as non existing ? */
@@ -537,6 +543,7 @@ static ERL_NIF_TERM trace_procs(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg
/* spawn */
if (argv[0] == atom_spawn) {
+ ERL_NIF_TERM extra;
char undef[] = "undefined";
const ERL_NIF_TERM* tuple;
int arity;
@@ -545,7 +552,9 @@ static ERL_NIF_TERM trace_procs(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg
lttng_pid_to_str(argv[3], to);
- if (enif_get_tuple(env, argv[4], &arity, &tuple)) {
+ enif_get_map_value(env, argv[4], atom_extra, &extra);
+
+ if (enif_get_tuple(env, extra, &arity, &tuple)) {
if (enif_is_list(env, tuple[2])) {
enif_get_list_length(env, tuple[2], &len);
} else {
@@ -618,11 +627,13 @@ static ERL_NIF_TERM trace_ports(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg
/* open and closed */
if (argv[0] == atom_open) {
+ ERL_NIF_TERM extra;
char driver[LTTNG_BUFFER_SZ];
lttng_decl_procbuf(pid);
lttng_pid_to_str(argv[3], pid);
+ enif_get_map_value(env, argv[4], atom_extra, &extra);
- enif_snprintf(driver, LTTNG_BUFFER_SZ, "%T", argv[4]);
+ enif_snprintf(driver, LTTNG_BUFFER_SZ, "%T", extra);
LTTNG3(port_open, pid, driver, port);
} else if (argv[0] == atom_closed) {
char reason[LTTNG_BUFFER_SZ];
diff --git a/lib/runtime_tools/doc/src/LTTng.xml b/lib/runtime_tools/doc/src/LTTng.xml
index 06152c66d6..9b490a27a0 100644
--- a/lib/runtime_tools/doc/src/LTTng.xml
+++ b/lib/runtime_tools/doc/src/LTTng.xml
@@ -75,6 +75,10 @@ $ make </code>
<item><c>parent : string</c> :: Process ID. Ex. <c>"&lt;0.131.0&gt;"</c></item>
<item><c>entry : string</c> :: Code Location. Ex. <c>"lists:sort/1"</c></item>
</list>
+ <p>
+ Available through
+ <seealso marker="erts:erlang#trace-3"><c>erlang:trace/3</c></seealso> with trace flag <c>procs</c> and <c>{tracer,dyntrace,[]}</c> as tracer module.
+ </p>
<p>Example:</p>
<code type="none">process_spawn: { cpu_id = 3 }, { pid = "&lt;0.131.0&gt;", parent = "&lt;0.130.0&gt;", entry = "erlang:apply/2" }</code>
@@ -84,6 +88,10 @@ $ make </code>
<item><c>from : string</c> :: Process ID or Port ID. Ex. <c>"&lt;0.131.0&gt;"</c></item>
<item><c>type : string</c> :: <c>"link" | "unlink"</c></item>
</list>
+ <p>
+ Available through
+ <seealso marker="erts:erlang#trace-3"><c>erlang:trace/3</c></seealso> with trace flag <c>procs</c> and <c>{tracer,dyntrace,[]}</c> as tracer module.
+ </p>
<p>Example:</p>
<code type="none">process_link: { cpu_id = 3 }, { from = "&lt;0.130.0&gt;", to = "&lt;0.131.0&gt;", type = "link" }</code>
@@ -93,6 +101,10 @@ $ make </code>
<item><c>pid : string</c> :: Process ID. Ex. <c>"&lt;0.131.0&gt;"</c></item>
<item><c>reason : string</c> :: Exit reason. Ex. <c>"normal"</c></item>
</list>
+ <p>
+ Available through
+ <seealso marker="erts:erlang#trace-3"><c>erlang:trace/3</c></seealso> with trace flag <c>procs</c> and <c>{tracer,dyntrace,[]}</c> as tracer module.
+ </p>
<p>Example:</p>
<code type="none">process_exit: { cpu_id = 3 }, { pid = "&lt;0.130.0&gt;", reason = "normal" }</code>
@@ -111,7 +123,10 @@ $ make </code>
<item><c>entry : string</c> :: Code Location. Ex. <c>"lists:sort/1"</c></item>
<item><c>type : string</c> :: <c>"in" | "out" | "in_exiting" | "out_exiting" | "out_exited"</c></item>
</list>
-
+ <p>
+ Available through
+ <seealso marker="erts:erlang#trace-3"><c>erlang:trace/3</c></seealso> with trace flag <c>running</c> and <c>{tracer,dyntrace,[]}</c> as tracer module.
+ </p>
<p>Example:</p>
<code type="none">process_scheduled: { cpu_id = 0 }, { pid = "&lt;0.136.0&gt;", entry = "erlang:apply/2", type = "in" }</code>
@@ -122,7 +137,10 @@ $ make </code>
<item><c>driver : string</c> :: Driver name. Ex. <c>"efile"</c></item>
<item><c>port : string</c> :: Port ID. Ex. <c>"#Port&lt;0.1031&gt;"</c></item>
</list>
-
+ <p>
+ Available through
+ <seealso marker="erts:erlang#trace-3"><c>erlang:trace/3</c></seealso> with trace flag <c>ports</c> and <c>{tracer,dyntrace,[]}</c> as tracer module.
+ </p>
<p>Example:</p>
<code type="none">port_open: { cpu_id = 5 }, { pid = "&lt;0.131.0&gt;", driver = "'/bin/sh -s unix:cmd'", port = "#Port&lt;0.1887&gt;" }</code>
@@ -131,6 +149,10 @@ $ make </code>
<item><c>port : string</c> :: Port ID. Ex. <c>"#Port&lt;0.1031&gt;"</c></item>
<item><c>reason : string</c> :: Exit reason. Ex. <c>"normal"</c></item>
</list>
+ <p>
+ Available through
+ <seealso marker="erts:erlang#trace-3"><c>erlang:trace/3</c></seealso> with trace flag <c>ports</c> and <c>{tracer,dyntrace,[]}</c> as tracer module.
+ </p>
<p>Example:</p>
<code type="none">port_exit: { cpu_id = 5 }, { port = "#Port&lt;0.1887&gt;", reason = "normal" }</code>
@@ -140,10 +162,18 @@ $ make </code>
<item><c>from : string</c> :: Process ID. Ex. <c>"&lt;0.131.0&gt;"</c></item>
<item><c>type : string</c> :: <c>"link" | "unlink"</c></item>
</list>
+ <p>
+ Available through
+ <seealso marker="erts:erlang#trace-3"><c>erlang:trace/3</c></seealso> with trace flag <c>ports</c> and <c>{tracer,dyntrace,[]}</c> as tracer module.
+ </p>
<p>Example:</p>
<code type="none">port_link: { cpu_id = 5 }, { from = "#Port&lt;0.1887&gt;", to = "&lt;0.131.0&gt;", type = "unlink" }</code>
<p><em>port_scheduled</em></p>
+ <p>
+ Available through
+ <seealso marker="erts:erlang#trace-3"><c>erlang:trace/3</c></seealso> with trace flag <c>running</c> and <c>{tracer,dyntrace,[]}</c> as tracer module.
+ </p>
<list type="bulleted">
<item><c>port : string</c> :: Port ID. Ex. <c>"#Port&lt;0.1031&gt;"</c></item>
<item><c>entry : string</c> :: Callback. Ex. <c>"open"</c></item>
@@ -152,13 +182,20 @@ $ make </code>
<p>Example:</p>
<code type="none">port_scheduled: { cpu_id = 5 }, { pid = "#Port&lt;0.1905&gt;", entry = "close", type = "out" }</code>
-
+ <p>
+ Available through
+ <seealso marker="erts:erlang#trace-3"><c>erlang:trace/3</c></seealso> with trace flag <c>running</c> and <c>{tracer,dyntrace,[]}</c> as tracer module.
+ </p>
<p><em>function_call</em></p>
<list type="bulleted">
<item><c>pid : string</c> :: Process ID. Ex. <c>"&lt;0.131.0&gt;"</c></item>
<item><c>entry : string</c> :: Code Location. Ex. <c>"lists:sort/1"</c></item>
<item><c>depth : integer</c> :: Stack depth. Ex. <c>0</c></item>
</list>
+ <p>
+ Available through
+ <seealso marker="erts:erlang#trace-3"><c>erlang:trace/3</c></seealso> with trace flag <c>call</c> and <c>{tracer,dyntrace,[]}</c> as tracer module.
+ </p>
<p>Example:</p>
<code type="none">function_call: { cpu_id = 5 }, { pid = "&lt;0.145.0&gt;", entry = "dyntrace_lttng_SUITE:'-t_call/1-fun-1-'/0", depth = 0 }</code>
@@ -168,6 +205,10 @@ $ make </code>
<item><c>entry : string</c> :: Code Location. Ex. <c>"lists:sort/1"</c></item>
<item><c>depth : integer</c> :: Stack depth. Ex. <c>0</c></item>
</list>
+ <p>
+ Available through
+ <seealso marker="erts:erlang#trace-3"><c>erlang:trace/3</c></seealso> with trace flag <c>call</c> or <c>return_to</c> and <c>{tracer,dyntrace,[]}</c> as tracer module.
+ </p>
<p>Example:</p>
<code type="none">function_return: { cpu_id = 5 }, { pid = "&lt;0.145.0&gt;", entry = "dyntrace_lttng_SUITE:waiter/0", depth = 0 }</code>
@@ -177,6 +218,10 @@ $ make </code>
<item><c>entry : string</c> :: Code Location. Ex. <c>"lists:sort/1"</c></item>
<item><c>class : string</c> :: Error reason. Ex. <c>"error"</c></item>
</list>
+ <p>
+ Available through
+ <seealso marker="erts:erlang#trace-3"><c>erlang:trace/3</c></seealso> with trace flag <c>call</c> and <c>{tracer,dyntrace,[]}</c> as tracer module.
+ </p>
<p>Example:</p>
<code type="none">function_exception: { cpu_id = 5 }, { pid = "&lt;0.144.0&gt;", entry = "t:call_exc/1", class = "error" }</code>
@@ -186,6 +231,10 @@ $ make </code>
<item><c>to : string</c> :: Process ID or Port ID. Ex. <c>"&lt;0.131.0&gt;"</c></item>
<item><c>message : string</c> :: Message sent. Ex. <c>"{&lt;0.162.0&gt;,ok}"</c></item>
</list>
+ <p>
+ Available through
+ <seealso marker="erts:erlang#trace-3"><c>erlang:trace/3</c></seealso> with trace flag <c>send</c> and <c>{tracer,dyntrace,[]}</c> as tracer module.
+ </p>
<p>Example:</p>
<code type="none">message_send: { cpu_id = 3 }, { from = "#Port&lt;0.1938&gt;", to = "&lt;0.160.0&gt;", message = "{#Port&lt;0.1938&gt;,eof}" }</code>
@@ -194,6 +243,10 @@ $ make </code>
<item><c>to : string</c> :: Process ID or Port ID. Ex. <c>"&lt;0.131.0&gt;"</c></item>
<item><c>message : string</c> :: Message received. Ex. <c>"{&lt;0.162.0&gt;,ok}"</c></item>
</list>
+ <p>
+ Available through
+ <seealso marker="erts:erlang#trace-3"><c>erlang:trace/3</c></seealso> with trace flag <c>'receive'</c> and <c>{tracer,dyntrace,[]}</c> as tracer module.
+ </p>
<p>Example:</p>
<code type="none">message_receive: { cpu_id = 7 }, { to = "&lt;0.167.0&gt;", message = "{&lt;0.165.0&gt;,ok}" }</code>
@@ -204,6 +257,10 @@ $ make </code>
<item><c>heap : integer</c> :: Young heap word size. Ex. <c>233</c></item>
<item><c>old_heap : integer</c> :: Old heap word size. Ex. <c>233</c></item>
</list>
+ <p>
+ Available through
+ <seealso marker="erts:erlang#trace-3"><c>erlang:trace/3</c></seealso> with trace flag <c>garbage_collection</c> and <c>{tracer,dyntrace,[]}</c> as tracer module.
+ </p>
<p>Example:</p>
<code type="none">gc_minor_start: { cpu_id = 0 }, { pid = "&lt;0.172.0&gt;", need = 0, heap = 610, old_heap = 0 }</code>
@@ -214,6 +271,10 @@ $ make </code>
<item><c>heap : integer</c> :: Young heap word size. Ex. <c>233</c></item>
<item><c>old_heap : integer</c> :: Old heap word size. Ex. <c>233</c></item>
</list>
+ <p>
+ Available through
+ <seealso marker="erts:erlang#trace-3"><c>erlang:trace/3</c></seealso> with trace flag <c>garbage_collection</c> and <c>{tracer,dyntrace,[]}</c> as tracer module.
+ </p>
<p>Example:</p>
<code type="none">gc_minor_end: { cpu_id = 0 }, { pid = "&lt;0.172.0&gt;", reclaimed = 120, heap = 1598, old_heap = 1598 }</code>
@@ -224,6 +285,10 @@ $ make </code>
<item><c>heap : integer</c> :: Young heap word size. Ex. <c>233</c></item>
<item><c>old_heap : integer</c> :: Old heap word size. Ex. <c>233</c></item>
</list>
+ <p>
+ Available through
+ <seealso marker="erts:erlang#trace-3"><c>erlang:trace/3</c></seealso> with trace flag <c>garbage_collection</c> and <c>{tracer,dyntrace,[]}</c> as tracer module.
+ </p>
<p>Example:</p>
<code type="none">gc_major_start: { cpu_id = 0 }, { pid = "&lt;0.172.0&gt;", need = 8, heap = 2586, old_heap = 1598 }</code>
@@ -234,6 +299,10 @@ $ make </code>
<item><c>heap : integer</c> :: Young heap word size. Ex. <c>233</c></item>
<item><c>old_heap : integer</c> :: Old heap word size. Ex. <c>233</c></item>
</list>
+ <p>
+ Available through
+ <seealso marker="erts:erlang#trace-3"><c>erlang:trace/3</c></seealso> with trace flag <c>garbage_collection</c> and <c>{tracer,dyntrace,[]}</c> as tracer module.
+ </p>
<p>Example:</p>
<code type="none">gc_major_end: { cpu_id = 0 }, { pid = "&lt;0.172.0&gt;", reclaimed = 240, heap = 4185, old_heap = 0 }</code>
@@ -454,6 +523,73 @@ $ make </code>
</section>
<section>
- <title>Examples</title>
+ <title>Example of process tracing</title>
+ <p>An example of process tracing of <c>os_mon</c> and friends.</p>
+
+ <p>Clean start of lttng in a bash shell.</p>
+
+ <pre>$ lttng create erlang-demo
+Spawning a session daemon
+Session erlang-demo created.
+Traces will be written in /home/egil/lttng-traces/erlang-demo-20160526-165920</pre>
+
+ <p>Start an Erlang node with lttng enabled.</p>
+
+ <pre>$ erl
+Erlang/OTP 19 [erts-8.0] [source-4d7b24d] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [lttng]
+
+Eshell V8.0 (abort with ^G)
+1></pre>
+
+ <p>Load the <c>dyntrace</c> module.</p>
+
+ <pre>1> <input>l(dyntrace).</input>
+{module,dyntrace}</pre>
+
+ <p>All tracepoints via dyntrace are now visibile and can be listed through <c>lttng list -u</c>.</p>
+
+ <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>
+
+ <p>Enable process tracing for new processes and use <c>dyntrace</c> as tracer backend.</p>
+
+ <pre>2> <input>erlang:trace(new,true,[procs,{tracer,dyntrace,[]}]).</input>
+0</pre>
+
+ <p>Start LTTng tracing.</p>
+
+ <pre>$ lttng start
+Tracing started for session erlang-demo</pre>
+
+ <p>Start the <c>os_mon</c> application in Erlang.</p>
+
+ <pre>3> <input>application:ensure_all_started(os_mon).</input>
+{ok,[sasl,os_mon]}</pre>
+
+ <p>Stop LTTng tracing and view the result.</p>
+
+ <pre>$ lttng stop
+Tracing stopped for session erlang-demo
+$ lttng view
+[17:20:42.561168759] (+?.?????????) elxd1168lx9 com_ericsson_dyntrace:process_register: \
+ { cpu_id = 5 }, { pid = "&lt;0.66.0&gt;", name = "sasl_sup", type = "register" }
+[17:20:42.561215519] (+0.000046760) elxd1168lx9 com_ericsson_dyntrace:process_register: \
+ { cpu_id = 5 }, { pid = "&lt;0.67.0&gt;", name = "sasl_safe_sup", type = "register" }
+[17:20:42.562149024] (+0.000933505) elxd1168lx9 com_ericsson_dyntrace:process_register: \
+ { cpu_id = 5 }, { pid = "&lt;0.68.0&gt;", name = "alarm_handler", type = "register" }
+[17:20:42.571035803] (+0.008886779) elxd1168lx9 com_ericsson_dyntrace:process_register: \
+ { cpu_id = 5 }, { pid = "&lt;0.69.0&gt;", name = "release_handler", type = "register" }
+[17:20:42.574939868] (+0.003904065) elxd1168lx9 com_ericsson_dyntrace:process_register: \
+ { cpu_id = 5 }, { pid = "&lt;0.74.0&gt;", name = "os_mon_sup", type = "register" }
+[17:20:42.576818712] (+0.001878844) elxd1168lx9 com_ericsson_dyntrace:process_register: \
+ { cpu_id = 5 }, { pid = "&lt;0.75.0&gt;", name = "disksup", type = "register" }
+[17:20:42.580032013] (+0.003213301) elxd1168lx9 com_ericsson_dyntrace:process_register: \
+ { cpu_id = 5 }, { pid = "&lt;0.76.0&gt;", name = "memsup", type = "register" }
+[17:20:42.583046339] (+0.003014326) elxd1168lx9 com_ericsson_dyntrace:process_register: \
+ { cpu_id = 5 }, { pid = "&lt;0.78.0&gt;", name = "cpu_sup", type = "register" }
+[17:20:42.586206242] (+0.003159903) elxd1168lx9 com_ericsson_dyntrace:process_register: \
+ { cpu_id = 5 }, { pid = "&lt;0.82.0&gt;", name = "timer_server", type = "register" }</pre>
</section>
</chapter>
diff --git a/lib/runtime_tools/doc/src/dbg.xml b/lib/runtime_tools/doc/src/dbg.xml
index 49b11ddc3c..14a81b2293 100644
--- a/lib/runtime_tools/doc/src/dbg.xml
+++ b/lib/runtime_tools/doc/src/dbg.xml
@@ -189,9 +189,9 @@ Error: fun containing local erlang function calls ('is_atomm' called in guard)\
<tag><c>all</c></tag>
<item>All processes and ports in the system as well as all processes and ports
created hereafter are to be traced.</item>
- <tag><c>all_processes</c></tag>
+ <tag><c>processes</c></tag>
<item>All processes in the system as well as all processes created hereafter are to be traced.</item>
- <tag><c>all_ports</c></tag>
+ <tag><c>ports</c></tag>
<item>All ports in the system as well as all ports created hereafter are to be traced.</item>
<tag><c>new</c></tag>
<item>All processes and ports created after the call is are to be traced.</item>
diff --git a/lib/runtime_tools/src/dbg.erl b/lib/runtime_tools/src/dbg.erl
index 8cdb5a43e3..c0d4665bda 100644
--- a/lib/runtime_tools/src/dbg.erl
+++ b/lib/runtime_tools/src/dbg.erl
@@ -1155,7 +1155,7 @@ all() ->
[send,'receive',call,procs,ports,garbage_collection,running,
set_on_spawn,set_on_first_spawn,set_on_link,set_on_first_link,
timestamp,monotonic_timestamp,strict_monotonic_timestamp,
- arity,return_to,silent,running_procs,running_ports].
+ arity,return_to,silent,running_procs,running_ports,exiting].
display_info([Node|Nodes]) ->
io:format("~nNode ~w:~n",[Node]),
@@ -1313,6 +1313,9 @@ tc_loop(Other, _Handler, _HData) ->
gen_reader(ip, {Host, Portno}) ->
case gen_tcp:connect(Host, Portno, [{active, false}, binary]) of
{ok, Sock} ->
+ %% Just in case this is on the traced node,
+ %% make sure the port is not traced.
+ p(Sock,clear),
mk_reader(fun ip_read/2, Sock);
Error ->
exit(Error)
diff --git a/lib/runtime_tools/src/dyntrace.erl b/lib/runtime_tools/src/dyntrace.erl
index 28e6d67d96..58c5a773c3 100644
--- a/lib/runtime_tools/src/dyntrace.erl
+++ b/lib/runtime_tools/src/dyntrace.erl
@@ -42,15 +42,14 @@
-export([put_tag/1, get_tag/0, get_tag_data/0, spread_tag/1, restore_tag/1]).
-export([trace/5,
- trace/6,
- trace_procs/6,
- trace_ports/6,
- trace_running_procs/6,
- trace_running_ports/6,
- trace_call/6,
- trace_send/6,
- trace_receive/6,
- trace_garbage_collection/6]).
+ trace_procs/5,
+ trace_ports/5,
+ trace_running_procs/5,
+ trace_running_ports/5,
+ trace_call/5,
+ trace_send/5,
+ trace_receive/5,
+ trace_garbage_collection/5]).
-export([enabled_procs/3,
enabled_ports/3,
@@ -147,34 +146,31 @@ user_trace_i4s4(_, _, _, _, _, _, _, _, _) ->
user_trace_n(_, _, _, _, _, _, _, _, _, _) ->
erlang:nif_error(nif_not_loaded).
-trace(_TracerState, _Label, _SeqTraceInfo, _, _Opts) ->
+trace(_TraceTag, _TracerState, _Tracee, _TraceTerm, _Opts) ->
erlang:nif_error(nif_not_loaded).
-trace(_TraceTag, _TracerState, _Tracee, _FirstTraceTerm, _SecondTraceTerm, _Opts) ->
+trace_procs(_TraceTag, _TracerState, _Tracee, _TraceTerm, _Opts) ->
erlang:nif_error(nif_not_loaded).
-trace_procs(_TraceTag, _TracerState, _Tracee, _FirstTraceTerm, _SecondTraceTerm, _Opts) ->
+trace_ports(_TraceTag, _TracerState, _Tracee, _TraceTerm, _Opts) ->
erlang:nif_error(nif_not_loaded).
-trace_ports(_TraceTag, _TracerState, _Tracee, _FirstTraceTerm, _SecondTraceTerm, _Opts) ->
+trace_running_procs(_TraceTag, _TracerState, _Tracee, _TraceTerm, _Opts) ->
erlang:nif_error(nif_not_loaded).
-trace_running_procs(_TraceTag, _TracerState, _Tracee, _FirstTraceTerm, _SecondTraceTerm, _Opts) ->
+trace_running_ports(_TraceTag, _TracerState, _Tracee, _TraceTerm, _Opts) ->
erlang:nif_error(nif_not_loaded).
-trace_running_ports(_TraceTag, _TracerState, _Tracee, _FirstTraceTerm, _SecondTraceTerm, _Opts) ->
+trace_call(_TraceTag, _TracerState, _Tracee, _TraceTerm, _Opts) ->
erlang:nif_error(nif_not_loaded).
-trace_call(_TraceTag, _TracerState, _Tracee, _FirstTraceTerm, _SecondTraceTerm, _Opts) ->
+trace_send(_TraceTag, _TracerState, _Tracee, _TraceTerm, _Opts) ->
erlang:nif_error(nif_not_loaded).
-trace_send(_TraceTag, _TracerState, _Tracee, _FirstTraceTerm, _SecondTraceTerm, _Opts) ->
+trace_receive(_TraceTag, _TracerState, _Tracee, _TraceTerm, _Opts) ->
erlang:nif_error(nif_not_loaded).
-trace_receive(_TraceTag, _TracerState, _Tracee, _FirstTraceTerm, _SecondTraceTerm, _Opts) ->
- erlang:nif_error(nif_not_loaded).
-
-trace_garbage_collection(_TraceTag, _TracerState, _Tracee, _FirstTraceTerm, _SecondTraceTerm, _Opts) ->
+trace_garbage_collection(_TraceTag, _TracerState, _Tracee, _TraceTerm, _Opts) ->
erlang:nif_error(nif_not_loaded).
enabled(_TraceTag, _TracerState, _Tracee) ->
diff --git a/lib/runtime_tools/src/observer_backend.erl b/lib/runtime_tools/src/observer_backend.erl
index 66653c5b7f..cedb677178 100644
--- a/lib/runtime_tools/src/observer_backend.erl
+++ b/lib/runtime_tools/src/observer_backend.erl
@@ -23,7 +23,8 @@
-export([vsn/0]).
%% observer stuff
--export([sys_info/0, get_table/3, get_table_list/2, fetch_stats/2]).
+-export([sys_info/0, get_port_list/0,
+ get_table/3, get_table_list/2, fetch_stats/2]).
%% etop stuff
-export([etop_collect/1]).
@@ -139,6 +140,15 @@ get_mnesia_loop(Parent, {Match, Cont}) ->
Parent ! {self(), Match},
get_mnesia_loop(Parent, mnesia:select(Cont)).
+get_port_list() ->
+ [begin
+ [{port_id,P}|erlang:port_info(P)] ++
+ case erlang:port_info(P,monitors) of
+ undefined -> [];
+ Monitors -> [Monitors]
+ end
+ end || P <- erlang:ports()].
+
get_table_list(ets, Opts) ->
HideUnread = proplists:get_value(unread_hidden, Opts, true),
HideSys = proplists:get_value(sys_hidden, Opts, true),
diff --git a/lib/runtime_tools/test/dbg_SUITE.erl b/lib/runtime_tools/test/dbg_SUITE.erl
index 5a3c885571..4b0864858c 100644
--- a/lib/runtime_tools/test/dbg_SUITE.erl
+++ b/lib/runtime_tools/test/dbg_SUITE.erl
@@ -30,7 +30,7 @@
erl_tracer/1, distributed_erl_tracer/1]).
-export([tracee1/1, tracee2/1]).
-export([dummy/0, exported/1]).
--export([enabled/3, trace/6, load_nif/1]).
+-export([enabled/3, trace/5, load_nif/1]).
-include_lib("common_test/include/ct.hrl").
@@ -961,7 +961,7 @@ erl_tracer(Config) ->
{ok, _} = dbg:p(self(), [c, timestamp]),
{ok, _} = dbg:tp(?MODULE, dummy, []),
ok = ?MODULE:dummy(),
- [{Self, call, Self, Self, {?MODULE, dummy, []}, undefined, #{}}] = flush(),
+ [{Self, call, Self, Self, {?MODULE, dummy, []}, #{}}] = flush(),
ok.
%% Test that distributed erl_tracer modules work
@@ -997,10 +997,10 @@ distributed_erl_tracer(Config) ->
{ok, {?MODULE, RNifProxy}} = dbg:get_tracer(RNode),
LCall = spawn_link(LNode, fun() -> ?MODULE:dummy() end),
- [{LCall, call, LNifProxy, LCall, {?MODULE, dummy, []}, undefined, #{}}] = flush(),
+ [{LCall, call, LNifProxy, LCall, {?MODULE, dummy, []}, #{}}] = flush(),
RCall = spawn_link(RNode, fun() -> ?MODULE:dummy() end),
- [{RCall, call, RNifProxy, RCall, {?MODULE, dummy, []}, undefined, #{}}] = flush(),
+ [{RCall, call, RNifProxy, RCall, {?MODULE, dummy, []}, #{}}] = flush(),
ok.
@@ -1018,7 +1018,7 @@ load_nif(Config) ->
enabled(_, _, _) ->
erlang:nif_error(nif_not_loaded).
-trace(_, _, _, _, _, _) ->
+trace(_, _, _, _, _) ->
erlang:nif_error(nif_not_loaded).
%%
diff --git a/lib/runtime_tools/test/dbg_SUITE_data/dbg_SUITE.c b/lib/runtime_tools/test/dbg_SUITE_data/dbg_SUITE.c
index 45f14938c6..ecdee7e3a2 100644
--- a/lib/runtime_tools/test/dbg_SUITE_data/dbg_SUITE.c
+++ b/lib/runtime_tools/test/dbg_SUITE_data/dbg_SUITE.c
@@ -36,7 +36,7 @@ static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ErlNifFunc nif_funcs[] = {
{"enabled", 3, enabled},
- {"trace", 6, trace}
+ {"trace", 5, trace}
};
ERL_NIF_INIT(dbg_SUITE, nif_funcs, load, NULL, upgrade, unload)
@@ -93,7 +93,7 @@ static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
int state_arity;
ErlNifPid self, to;
ERL_NIF_TERM *tuple, msg;
- ASSERT(argc == 6);
+ ASSERT(argc == 5);
tuple = enif_alloc(sizeof(ERL_NIF_TERM)*(argc+1));
memcpy(tuple+1,argv,sizeof(ERL_NIF_TERM)*argc);
diff --git a/lib/ssh/src/ssh_message.erl b/lib/ssh/src/ssh_message.erl
index db80d4c9e3..562f040477 100644
--- a/lib/ssh/src/ssh_message.erl
+++ b/lib/ssh/src/ssh_message.erl
@@ -50,7 +50,15 @@
-define(Empint(X), (ssh_bits:mpint(X))/binary ).
-define(Ebinary(X), ?STRING(X) ).
--define(unicode_list(B), unicode:characters_to_list(B)).
+ucl(B) ->
+ try unicode:characters_to_list(B) of
+ L when is_list(L) -> L;
+ {error,_Matched,Rest} -> throw({error,{bad_unicode,Rest}})
+ catch
+ _:_ -> throw({error,bad_unicode})
+ end.
+
+-define(unicode_list(B), ucl(B)).
encode(#ssh_msg_global_request{
name = Name,
diff --git a/lib/ssh/test/ssh_algorithms_SUITE.erl b/lib/ssh/test/ssh_algorithms_SUITE.erl
index 9910b8f1d7..6894f83547 100644
--- a/lib/ssh/test/ssh_algorithms_SUITE.erl
+++ b/lib/ssh/test/ssh_algorithms_SUITE.erl
@@ -28,7 +28,7 @@
%% Note: This directive should only be used in test suites.
-compile(export_all).
--define(TIMEOUT, 10000).
+-define(TIMEOUT, 35000).
%%--------------------------------------------------------------------
%% Common Test interface functions -----------------------------------
@@ -70,10 +70,10 @@ two_way_tags() -> [cipher,mac,compression].
%%--------------------------------------------------------------------
init_per_suite(Config) ->
- ct:log("os:getenv(\"HOME\") = ~p~n"
- "init:get_argument(home) = ~p",
- [os:getenv("HOME"), init:get_argument(home)]),
- ct:log("~n~n"
+ 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"
@@ -82,7 +82,9 @@ init_per_suite(Config) ->
" -- Default dh group exchange parameters ({min,def,max}): ~p~n"
" -- dh_default_groups: ~p~n"
" -- Max num algorithms: ~p~n"
- ,[os:cmd("ssh -V"),
+ ,[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),
@@ -136,14 +138,14 @@ end_per_group(_Alg, Config) ->
-init_per_testcase(sshc_simple_exec, Config) ->
+init_per_testcase(sshc_simple_exec_os_cmd, Config) ->
start_pubkey_daemon([?config(pref_algs,Config)], Config);
init_per_testcase(_TC, Config) ->
Config.
-end_per_testcase(sshc_simple_exec, Config) ->
+end_per_testcase(sshc_simple_exec_os_cmd, Config) ->
case ?config(srvr_pid,Config) of
Pid when is_pid(Pid) ->
ssh:stop_daemon(Pid),
@@ -154,7 +156,6 @@ end_per_testcase(sshc_simple_exec, Config) ->
end_per_testcase(_TC, Config) ->
Config.
-
%%--------------------------------------------------------------------
%% Test Cases --------------------------------------------------------
%%--------------------------------------------------------------------
@@ -221,18 +222,36 @@ interpolate(Is) ->
%%--------------------------------------------------------------------
%% Use the ssh client of the OS to connect
-sshc_simple_exec(Config) ->
+sshc_simple_exec_os_cmd(Config) ->
PrivDir = ?config(priv_dir, Config),
KnownHosts = filename:join(PrivDir, "known_hosts"),
{Host,Port} = ?config(srvr_addr, Config),
- Cmd = lists:concat(["ssh -p ",Port,
- " -C",
- " -o UserKnownHostsFile=",KnownHosts,
- " -o StrictHostKeyChecking=no",
- " ",Host," 1+1."]),
- ct:log("~p",[Cmd]),
- OpenSsh = ssh_test_lib:open_port({spawn, Cmd}, [eof,exit_status]),
- ssh_test_lib:rcv_expected({data,<<"2\n">>}, OpenSsh, ?TIMEOUT).
+ Parent = self(),
+ Client = spawn(
+ fun() ->
+ Cmd = lists:concat(["ssh -p ",Port,
+ " -C"
+ " -o UserKnownHostsFile=",KnownHosts,
+ " -o StrictHostKeyChecking=no"
+ " ",Host," 1+1."]),
+ Result = os:cmd(Cmd),
+ ct:log("~p~n = ~p",[Cmd, Result]),
+ Parent ! {result, self(), Result, "2"}
+ end),
+ receive
+ {result, Client, RawResult, Expect} ->
+ Lines = string:tokens(RawResult, "\r\n"),
+ case lists:any(fun(Line) -> Line==Expect end,
+ Lines) of
+ true ->
+ ok;
+ false ->
+ ct:log("Bad result: ~p~nExpected: ~p~nMangled result: ~p", [RawResult,Expect,Lines]),
+ {fail, "Bad result"}
+ end
+ after ?TIMEOUT ->
+ ct:fail("Did not receive answer")
+ end.
%%--------------------------------------------------------------------
%% Connect to the ssh server of the OS
@@ -299,7 +318,7 @@ specific_test_cases(Tag, Alg, SshcAlgos, SshdAlgos) ->
true ->
case ssh_test_lib:ssh_type() of
openSSH ->
- [sshc_simple_exec];
+ [sshc_simple_exec_os_cmd];
_ ->
[]
end;
diff --git a/lib/ssh/test/ssh_benchmark_SUITE.erl b/lib/ssh/test/ssh_benchmark_SUITE.erl
index d9be1a32b7..5d8c94be73 100644
--- a/lib/ssh/test/ssh_benchmark_SUITE.erl
+++ b/lib/ssh/test/ssh_benchmark_SUITE.erl
@@ -184,7 +184,7 @@ openssh_client_shell(Config, Options) ->
end, Times),
ssh:stop_daemon(ServerPid),
ok
- after 10000 ->
+ after 60*1000 ->
ssh:stop_daemon(ServerPid),
exit(SlavePid, kill),
{fail, timeout}
@@ -215,6 +215,7 @@ openssh_client_sftp(Config, Options) ->
{root, SftpSrcDir}])]},
{failfun, fun ssh_test_lib:failfun/2}
| Options]),
+ ct:pal("ServerPid = ~p",[ServerPid]),
ct:sleep(500),
Cmd = lists:concat(["sftp",
" -b -",
@@ -231,7 +232,7 @@ openssh_client_sftp(Config, Options) ->
end),
receive
{SlavePid, _ClientResponse} ->
- ct:pal("ClientResponse = ~p",[_ClientResponse]),
+ ct:pal("ClientResponse = ~p~nServerPid = ~p",[_ClientResponse,ServerPid]),
{ok, List} = get_trace_list(TracerPid),
%%ct:pal("List=~p",[List]),
Times = find_times(List, [channel_open_close]),
@@ -260,7 +261,7 @@ openssh_client_sftp(Config, Options) ->
end, Times),
ssh:stop_daemon(ServerPid),
ok
- after 10000 ->
+ after 2*60*1000 ->
ssh:stop_daemon(ServerPid),
exit(SlavePid, kill),
{fail, timeout}
@@ -445,10 +446,18 @@ increment({Alg,Sz,T},[]) ->
%%% API for the traceing
%%%
get_trace_list(TracerPid) ->
+ MonRef = monitor(process, TracerPid),
TracerPid ! {get_trace_list,self()},
receive
- {trace_list,L} -> {ok, pair_events(lists:reverse(L))}
- after 5000 -> {error,no_reply}
+ {trace_list,L} ->
+ demonitor(MonRef),
+ {ok, pair_events(lists:reverse(L))};
+ {'DOWN', MonRef, process, TracerPid, Info} ->
+ {error, {tracer_down,Info}}
+
+ after 3*60*1000 ->
+ demonitor(MonRef),
+ {error,no_reply}
end.
erlang_trace() ->
diff --git a/lib/ssh/test/ssh_sftp_SUITE.erl b/lib/ssh/test/ssh_sftp_SUITE.erl
index f6d7be41d6..26fe0935e1 100644
--- a/lib/ssh/test/ssh_sftp_SUITE.erl
+++ b/lib/ssh/test/ssh_sftp_SUITE.erl
@@ -1,4 +1,4 @@
-%%
+%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
@@ -93,35 +93,35 @@ groups() ->
init_per_group(not_unicode, Config) ->
ct:comment("Begin ~p",[grps(Config)]),
DataDir = ?config(data_dir, Config),
- PrivDir = ?config(priv_dir, Config),
[{user, "Alladin"},
{passwd, "Sesame"},
{data, <<"Hello world!">>},
- {filename, filename:join(PrivDir, "sftp.txt")},
- {testfile, filename:join(PrivDir, "test.txt")},
- {linktest, filename:join(PrivDir, "link_test.txt")},
- {tar_filename, filename:join(PrivDir, "sftp_tar_test.tar")},
- {tar_F1_txt, "f1.txt"},
+ {filename, "sftp.txt"},
+ {testfile, "test.txt"},
+ {linktest, "link_test.txt"},
+ {tar_filename, "sftp_tar_test.tar"},
+ {tar_F1_txt, "f1.txt"},
{datadir_tar, filename:join(DataDir,"sftp_tar_test_data")}
| Config];
init_per_group(unicode, Config) ->
- case file:native_name_encoding() of
- utf8 ->
+ case (file:native_name_encoding() == utf8)
+ andalso ("四" == [22235])
+ of
+ true ->
ct:comment("Begin ~p",[grps(Config)]),
DataDir = ?config(data_dir, Config),
- PrivDir = ?config(priv_dir, Config),
NewConfig =
[{user, "åke高兴"},
{passwd, "ärlig日本じん"},
{data, <<"foobar å 一二三四いちにさんち">>},
- {filename, filename:join(PrivDir, "sftp瑞点.txt")},
- {testfile, filename:join(PrivDir, "testハンス.txt")},
- {linktest, filename:join(PrivDir, "link_test語.txt")},
- {tar_filename, filename:join(PrivDir, "sftp_tar_test一二三.tar")},
- {tar_F1_txt, "F一.txt"},
- {tar_F3_txt, "f3.txt"},
- {tar_F4_txt, "g四.txt"},
+ {filename, "sftp瑞点.txt"},
+ {testfile, "testハンス.txt"},
+ {linktest, "link_test語.txt"},
+ {tar_filename, "sftp_tar_test一二三.tar"},
+ {tar_F1_txt, "F一.txt"},
+ {tar_F3_txt, "f3.txt"},
+ {tar_F4_txt, "g四.txt"},
{datadir_tar, filename:join(DataDir,"sftp_tar_test_data_高兴")}
| lists:foldl(fun(K,Cf) -> lists:keydelete(K,1,Cf) end,
Config,
@@ -228,8 +228,8 @@ init_per_testcase(sftp_nonexistent_subsystem, Config) ->
]),
[{sftpd, Sftpd} | Config];
-init_per_testcase(version_option, Config) ->
- prep(Config),
+init_per_testcase(version_option, Config0) ->
+ Config = prepare(Config0),
TmpConfig0 = lists:keydelete(watchdog, 1, Config),
TmpConfig = lists:keydelete(sftp, 1, TmpConfig0),
Dog = ct:timetrap(?default_timeout),
@@ -246,8 +246,8 @@ init_per_testcase(version_option, Config) ->
Sftp = {ChannelPid, Connection},
[{sftp,Sftp}, {watchdog, Dog} | TmpConfig];
-init_per_testcase(Case, Config0) ->
- prep(Config0),
+init_per_testcase(Case, Config00) ->
+ Config0 = prepare(Config00),
Config1 = lists:keydelete(watchdog, 1, Config0),
Config2 = lists:keydelete(sftp, 1, Config1),
Dog = ct:timetrap(2 * ?default_timeout),
@@ -279,7 +279,7 @@ init_per_testcase(Case, Config0) ->
[{sftp, Sftp}, {watchdog, Dog} | Config2]
end,
- case catch ?config(remote_tar,Config) of
+ case catch proplists:get_value(remote_tar,Config) of
%% The 'catch' is for the case of Config={skip,...}
true ->
%% Provide a ChannelPid independent of the sftp-channel already opened.
@@ -329,7 +329,7 @@ open_close_file(Server, File, Mode) ->
open_close_dir() ->
[{doc, "Test API functions opendir/2 and close/2"}].
open_close_dir(Config) when is_list(Config) ->
- PrivDir = ?config(priv_dir, Config),
+ PrivDir = ?config(sftp_priv_dir, Config),
{Sftp, _} = ?config(sftp, Config),
FileName = ?config(filename, Config),
@@ -351,7 +351,7 @@ read_file(Config) when is_list(Config) ->
read_dir() ->
[{doc,"Test API function list_dir/2"}].
read_dir(Config) when is_list(Config) ->
- PrivDir = ?config(priv_dir, Config),
+ PrivDir = ?config(sftp_priv_dir, Config),
{Sftp, _} = ?config(sftp, Config),
{ok, Files} = ssh_sftp:list_dir(Sftp, PrivDir),
ct:log("sftp list dir: ~p~n", [Files]).
@@ -415,7 +415,7 @@ sftp_read_big_file(Config) when is_list(Config) ->
remove_file() ->
[{doc,"Test API function delete/2"}].
remove_file(Config) when is_list(Config) ->
- PrivDir = ?config(priv_dir, Config),
+ PrivDir = ?config(sftp_priv_dir, Config),
FileName = ?config(filename, Config),
{Sftp, _} = ?config(sftp, Config),
@@ -429,7 +429,7 @@ remove_file(Config) when is_list(Config) ->
rename_file() ->
[{doc, "Test API function rename_file/2"}].
rename_file(Config) when is_list(Config) ->
- PrivDir = ?config(priv_dir, Config),
+ PrivDir = ?config(sftp_priv_dir, Config),
FileName = ?config(filename, Config),
NewFileName = ?config(testfile, Config),
@@ -449,7 +449,7 @@ rename_file(Config) when is_list(Config) ->
mk_rm_dir() ->
[{doc,"Test API functions make_dir/2, del_dir/2"}].
mk_rm_dir(Config) when is_list(Config) ->
- PrivDir = ?config(priv_dir, Config),
+ PrivDir = ?config(sftp_priv_dir, Config),
{Sftp, _} = ?config(sftp, Config),
DirName = filename:join(PrivDir, "test"),
@@ -945,7 +945,7 @@ aes_ctr_stream_crypto_tar(Config) ->
%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
-prep(Config) ->
+oldprep(Config) ->
DataDir = ?config(data_dir, Config),
TestFile = ?config(filename, Config),
TestFile1 = ?config(testfile, Config),
@@ -965,6 +965,36 @@ prep(Config) ->
ok = file:write_file_info(TestFile,
FileInfo#file_info{mode = Mode}).
+prepare(Config0) ->
+ PrivDir = proplists:get_value(priv_dir, Config0),
+ Dir = filename:join(PrivDir, random_chars(10)),
+ file:make_dir(Dir),
+ Keys = [filename,
+ testfile,
+ linktest,
+ tar_filename],
+ Config1 = foldl_keydelete(Keys, Config0),
+ Config2 = lists:foldl(fun({Key,Name}, ConfAcc) ->
+ [{Key, filename:join(Dir,Name)} | ConfAcc]
+ end,
+ Config1,
+ lists:zip(Keys, [proplists:get_value(K,Config0) || K<-Keys])),
+
+ DataDir = proplists:get_value(data_dir, Config2),
+ FilenameSrc = filename:join(DataDir, "sftp.txt"),
+ FilenameDst = proplists:get_value(filename, Config2),
+ {ok,_} = file:copy(FilenameSrc, FilenameDst),
+ [{sftp_priv_dir,Dir} | Config2].
+
+
+random_chars(N) -> [crypto:rand_uniform($a,$z) || _<-lists:duplicate(N,x)].
+
+foldl_keydelete(Keys, L) ->
+ lists:foldl(fun(K,E) -> lists:keydelete(K,1,E) end,
+ L,
+ Keys).
+
+
chk_tar(Items, Config) ->
chk_tar(Items, Config, []).
diff --git a/lib/ssh/test/ssh_test_lib.erl b/lib/ssh/test/ssh_test_lib.erl
index c6541461a1..a1291146e4 100644
--- a/lib/ssh/test/ssh_test_lib.erl
+++ b/lib/ssh/test/ssh_test_lib.erl
@@ -655,17 +655,48 @@ sshc(Tag) ->
).
ssh_type() ->
- case os:find_executable("ssh") of
- false -> not_found;
- _ ->
- case os:cmd("ssh -V") of
- "OpenSSH" ++ _ ->
- openSSH;
- Str ->
- ct:log("ssh client ~p is unknown",[Str]),
- unknown
- end
- end.
+ Parent = self(),
+ Pid = spawn(fun() ->
+ Parent ! {ssh_type,self(),ssh_type1()}
+ end),
+ MonitorRef = monitor(process, Pid),
+ receive
+ {ssh_type, Pid, Result} ->
+ demonitor(MonitorRef),
+ Result;
+ {'DOWN', MonitorRef, process, Pid, _Info} ->
+ ct:log("~p:~p Process DOWN",[?MODULE,?LINE]),
+ not_found
+ after
+ 10000 ->
+ ct:log("~p:~p Timeout",[?MODULE,?LINE]),
+ demonitor(MonitorRef),
+ not_found
+ end.
+
+
+ssh_type1() ->
+ try
+ case os:find_executable("ssh") of
+ false ->
+ ct:log("~p:~p Executable \"ssh\" not found",[?MODULE,?LINE]),
+ not_found;
+ _ ->
+ case os:cmd("ssh -V") of
+ "OpenSSH" ++ _ ->
+ openSSH;
+ Str ->
+ ct:log("ssh client ~p is unknown",[Str]),
+ unknown
+ end
+ end
+ catch
+ Class:Exception ->
+ ct:log("~p:~p Exception ~p:~p",[?MODULE,?LINE,Class,Exception]),
+ not_found
+ end.
+
+
algo_intersection([], _) -> [];
algo_intersection(_, []) -> [];
diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml
index 154664d855..33ece8f769 100644
--- a/lib/ssl/doc/src/ssl.xml
+++ b/lib/ssl/doc/src/ssl.xml
@@ -417,10 +417,24 @@ fun(srp, Username :: string(), UserState :: term()) ->
If set to <c>false</c>, it disables the block cipher padding check
to be able to interoperate with legacy software.</p></item>
- </taglist>
-
<warning><p>Using <c>{padding_check, boolean()}</c> makes TLS
vulnerable to the Poodle attack.</p></warning>
+
+ <tag><c>{beast_mitigation, one_n_minus_one | zero_n | disabled}</c></tag>
+ <item><p>Affects SSL-3.0 and TLS-1.0 connections only. Used to change the BEAST
+ mitigation strategy to interoperate with legacy software.
+ Defaults to <c>one_n_minus_one</c></p>.
+
+ <p><c>one_n_minus_one</c> - Perform 1/n-1 BEAST mitigation.</p>
+
+ <p><c>zero_n</c> - Perform 0/n BEAST mitigation.</p>
+
+ <p><c>disabled</c> - Disable BEAST mitigation.</p></item>
+
+ <warning><p>Using <c>{beast_mitigation, disabled}</c> makes SSL or TLS
+ vulnerable to the BEAST attack.</p></warning>
+ </taglist>
+
</section>
<section>
diff --git a/lib/ssl/doc/src/ssl_app.xml b/lib/ssl/doc/src/ssl_app.xml
index daa06a40fc..e50ffdbfe6 100644
--- a/lib/ssl/doc/src/ssl_app.xml
+++ b/lib/ssl/doc/src/ssl_app.xml
@@ -94,7 +94,10 @@
to <c>ssl:connect/[2,3]</c> and <c>ssl:listen/2</c>.</p></item>
<tag><c><![CDATA[session_lifetime = integer() <optional>]]></c></tag>
- <item><p>Maximum lifetime of the session data in seconds.</p></item>
+ <item><p>Maximum lifetime of the session data in seconds. Defaults to 24 hours which is the maximum
+ recommended lifetime by <url href="http://www.ietf.org/rfc/5246rfc.txt">RFC 5246</url>. However
+ sessions may be invalidated earlier due to the maximum limitation of the session cache table.
+ </p></item>
<tag><c><![CDATA[session_cb = atom() <optional>]]></c></tag>
<item><p>Name of the session cache callback module that implements
@@ -107,16 +110,24 @@
in the session cache callback module. Defaults to <c>[]</c>.</p></item>
<tag><c><![CDATA[session_cache_client_max = integer() <optional>]]></c><br/>
- <c><![CDATA[session_cache_server_max = integer() <optional>]]></c></tag>
- <item><p>Limits the growth of the clients/servers session cache,
- if the maximum number of sessions is reached, the current cache entries will
- be invalidated regardless of their remaining lifetime. Defaults to 1000.
- </p></item>
+ <item><p>Limits the growth of the clients session cache, that is
+ how many sessions towards servers that are cached to be used by
+ new client connections. If the maximum number of sessions is
+ reached, the current cache entries will be invalidated
+ regardless of their remaining lifetime. Defaults to
+ 1000.</p></item>
+
+ <tag> <c><![CDATA[session_cache_server_max = integer() <optional>]]></c></tag>
+ <item><p>Limits the growth of the servers session cache, that is
+ how many client sessions are cached by the server. If the
+ maximum number of sessions is reached, the current cache entries
+ will be invalidated regardless of their remaining
+ lifetime. Defaults to 1000.</p></item>
<tag><c><![CDATA[ssl_pem_cache_clean = integer() <optional>]]></c></tag>
<item>
<p>
- Number of milliseconds between PEM cache validations.
+ Number of milliseconds between PEM cache validations. Defaults to 2 minutes.
</p>
<seealso
marker="ssl#clear_pem_cache-0">ssl:clear_pem_cache/0</seealso>
@@ -131,7 +142,7 @@
shutdown gracefully. Defaults to 5000 milliseconds.
</p>
</item>
-
+ </tag>
</taglist>
</section>
diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl
index 82d6faee42..60a61bc901 100644
--- a/lib/ssl/src/dtls_connection.erl
+++ b/lib/ssl/src/dtls_connection.erl
@@ -416,7 +416,8 @@ encode_change_cipher(#change_cipher_spec{}, Version, ConnectionStates) ->
initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User,
{CbModule, DataTag, CloseTag, ErrorTag}) ->
- ConnectionStates = ssl_record:init_connection_states(Role),
+ #ssl_options{beast_mitigation = BeastMitigation} = SSLOptions,
+ ConnectionStates = ssl_record:init_connection_states(Role, BeastMitigation),
SessionCacheCb = case application:get_env(ssl, session_cb) of
{ok, Cb} when is_atom(Cb) ->
diff --git a/lib/ssl/src/inet_tls_dist.erl b/lib/ssl/src/inet_tls_dist.erl
index 3481e89af0..0da4b3587f 100644
--- a/lib/ssl/src/inet_tls_dist.erl
+++ b/lib/ssl/src/inet_tls_dist.erl
@@ -87,7 +87,8 @@ do_setup(Driver, Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) ->
case inet:getaddr(Address, Driver:family()) of
{ok, Ip} ->
Timer = dist_util:start_timer(SetupTime),
- case erl_epmd:port_please(Name, Ip) of
+ ErlEpmd = net_kernel:epmd_module(),
+ case ErlEpmd:port_please(Name, Ip) of
{port, TcpPort, Version} ->
?trace("port_please(~p) -> version ~p~n",
[Node,Version]),
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index 51732b4a59..5dc2e583a5 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -725,6 +725,7 @@ handle_options(Opts0, Role) ->
server, Role),
protocol = proplists:get_value(protocol, Opts, tls),
padding_check = proplists:get_value(padding_check, Opts, true),
+ beast_mitigation = handle_option(beast_mitigation, Opts, one_n_minus_one),
fallback = handle_option(fallback, Opts,
proplists:get_value(fallback, Opts,
default_option_role(client,
@@ -746,7 +747,7 @@ handle_options(Opts0, Role) ->
alpn_preferred_protocols, next_protocols_advertised,
client_preferred_next_protocols, log_alert,
server_name_indication, honor_cipher_order, padding_check, crl_check, crl_cache,
- fallback, signature_algs],
+ fallback, signature_algs, beast_mitigation],
SockOpts = lists:foldl(fun(Key, PropList) ->
proplists:delete(Key, PropList)
@@ -986,6 +987,10 @@ validate_option(crl_check, Value) when (Value == best_effort) or (Value == peer)
Value;
validate_option(crl_cache, {Cb, {_Handle, Options}} = Value) when is_atom(Cb) and is_list(Options) ->
Value;
+validate_option(beast_mitigation, Value) when Value == one_n_minus_one orelse
+ Value == zero_n orelse
+ Value == disabled ->
+ Value;
validate_option(Opt, Value) ->
throw({error, {options, {Opt, Value}}}).
diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl
index 076e663cd4..dddcbdeeda 100644
--- a/lib/ssl/src/ssl_internal.hrl
+++ b/lib/ssl/src/ssl_internal.hrl
@@ -133,6 +133,9 @@
%% the client?
honor_cipher_order = false :: boolean(),
padding_check = true :: boolean(),
+ %%Should we use 1/n-1 or 0/n splitting to mitigate BEAST, or disable
+ %%mitigation entirely?
+ beast_mitigation = one_n_minus_one :: one_n_minus_one | zero_n | disabled,
fallback = false :: boolean(),
crl_check :: boolean() | peer | best_effort,
crl_cache,
diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl
index 866bfcef7e..0a086f5eeb 100644
--- a/lib/ssl/src/ssl_record.erl
+++ b/lib/ssl/src/ssl_record.erl
@@ -30,7 +30,7 @@
-include("ssl_alert.hrl").
%% Connection state handling
--export([init_connection_states/1,
+-export([init_connection_states/2,
current_connection_state/2, pending_connection_state/2,
activate_pending_connection_state/2,
set_security_params/3,
@@ -62,15 +62,16 @@
%%====================================================================
%%--------------------------------------------------------------------
--spec init_connection_states(client | server) -> #connection_states{}.
+-spec init_connection_states(client | server, one_n_minus_one | zero_n | disabled ) ->
+ #connection_states{}.
%%
%% Description: Creates a connection_states record with appropriate
%% values for the initial SSL connection setup.
%%--------------------------------------------------------------------
-init_connection_states(Role) ->
+init_connection_states(Role, BeastMitigation) ->
ConnectionEnd = record_protocol_role(Role),
- Current = initial_connection_state(ConnectionEnd),
- Pending = empty_connection_state(ConnectionEnd),
+ Current = initial_connection_state(ConnectionEnd, BeastMitigation),
+ Pending = empty_connection_state(ConnectionEnd, BeastMitigation),
#connection_states{current_read = Current,
pending_read = Pending,
current_write = Current,
@@ -119,9 +120,10 @@ activate_pending_connection_state(States =
read) ->
NewCurrent = Pending#connection_state{epoch = dtls_next_epoch(Current),
sequence_number = 0},
+ BeastMitigation = Pending#connection_state.beast_mitigation,
SecParams = Pending#connection_state.security_parameters,
ConnectionEnd = SecParams#security_parameters.connection_end,
- EmptyPending = empty_connection_state(ConnectionEnd),
+ EmptyPending = empty_connection_state(ConnectionEnd, BeastMitigation),
SecureRenegotation = NewCurrent#connection_state.secure_renegotiation,
NewPending = EmptyPending#connection_state{secure_renegotiation = SecureRenegotation},
States#connection_states{current_read = NewCurrent,
@@ -134,9 +136,10 @@ activate_pending_connection_state(States =
write) ->
NewCurrent = Pending#connection_state{epoch = dtls_next_epoch(Current),
sequence_number = 0},
+ BeastMitigation = Pending#connection_state.beast_mitigation,
SecParams = Pending#connection_state.security_parameters,
ConnectionEnd = SecParams#security_parameters.connection_end,
- EmptyPending = empty_connection_state(ConnectionEnd),
+ EmptyPending = empty_connection_state(ConnectionEnd, BeastMitigation),
SecureRenegotation = NewCurrent#connection_state.secure_renegotiation,
NewPending = EmptyPending#connection_state{secure_renegotiation = SecureRenegotation},
States#connection_states{current_write = NewCurrent,
@@ -314,12 +317,13 @@ set_pending_cipher_state(#connection_states{pending_read = Read,
encode_handshake(Frag, Version,
#connection_states{current_write =
#connection_state{
+ beast_mitigation = BeastMitigation,
security_parameters =
#security_parameters{bulk_cipher_algorithm = BCA}}} =
ConnectionStates) ->
case iolist_size(Frag) of
N when N > ?MAX_PLAIN_TEXT_LENGTH ->
- Data = split_bin(iolist_to_binary(Frag), ?MAX_PLAIN_TEXT_LENGTH, Version, BCA),
+ Data = split_bin(iolist_to_binary(Frag), ?MAX_PLAIN_TEXT_LENGTH, Version, BCA, BeastMitigation),
encode_iolist(?HANDSHAKE, Data, Version, ConnectionStates);
_ ->
encode_plain_text(?HANDSHAKE, Version, Frag, ConnectionStates)
@@ -352,10 +356,11 @@ encode_change_cipher_spec(Version, ConnectionStates) ->
%%--------------------------------------------------------------------
encode_data(Frag, Version,
#connection_states{current_write = #connection_state{
+ beast_mitigation = BeastMitigation,
security_parameters =
#security_parameters{bulk_cipher_algorithm = BCA}}} =
ConnectionStates) ->
- Data = split_bin(Frag, ?MAX_PLAIN_TEXT_LENGTH, Version, BCA),
+ Data = split_bin(Frag, ?MAX_PLAIN_TEXT_LENGTH, Version, BCA, BeastMitigation),
encode_iolist(?APPLICATION_DATA, Data, Version, ConnectionStates).
uncompress(?NULL, Data, CS) ->
@@ -447,9 +452,10 @@ decipher_aead(Version, CipherFragment,
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
-empty_connection_state(ConnectionEnd) ->
+empty_connection_state(ConnectionEnd, BeastMitigation) ->
SecParams = empty_security_params(ConnectionEnd),
- #connection_state{security_parameters = SecParams}.
+ #connection_state{security_parameters = SecParams,
+ beast_mitigation = BeastMitigation}.
empty_security_params(ConnectionEnd = ?CLIENT) ->
#security_parameters{connection_end = ConnectionEnd,
@@ -478,10 +484,11 @@ record_protocol_role(client) ->
record_protocol_role(server) ->
?SERVER.
-initial_connection_state(ConnectionEnd) ->
+initial_connection_state(ConnectionEnd, BeastMitigation) ->
#connection_state{security_parameters =
initial_security_params(ConnectionEnd),
- sequence_number = 0
+ sequence_number = 0,
+ beast_mitigation = BeastMitigation
}.
initial_security_params(ConnectionEnd) ->
@@ -506,11 +513,17 @@ encode_iolist(Type, Data, Version, ConnectionStates0) ->
%% 1/n-1 splitting countermeasure Rizzo/Duong-Beast, RC4 chiphers are
%% not vulnerable to this attack.
-split_bin(<<FirstByte:8, Rest/binary>>, ChunkSize, Version, BCA) when
+split_bin(<<FirstByte:8, Rest/binary>>, ChunkSize, Version, BCA, one_n_minus_one) when
BCA =/= ?RC4 andalso ({3, 1} == Version orelse
{3, 0} == Version) ->
do_split_bin(Rest, ChunkSize, [[FirstByte]]);
-split_bin(Bin, ChunkSize, _, _) ->
+%% 0/n splitting countermeasure for clients that are incompatible with 1/n-1
+%% splitting.
+split_bin(Bin, ChunkSize, Version, BCA, zero_n) when
+ BCA =/= ?RC4 andalso ({3, 1} == Version orelse
+ {3, 0} == Version) ->
+ do_split_bin(Bin, ChunkSize, [[<<>>]]);
+split_bin(Bin, ChunkSize, _, _, _) ->
do_split_bin(Bin, ChunkSize, []).
do_split_bin(<<>>, _, Acc) ->
diff --git a/lib/ssl/src/ssl_record.hrl b/lib/ssl/src/ssl_record.hrl
index d34d144343..87fde35258 100644
--- a/lib/ssl/src/ssl_record.hrl
+++ b/lib/ssl/src/ssl_record.hrl
@@ -40,7 +40,9 @@
%% RFC 5746
secure_renegotiation,
client_verify_data,
- server_verify_data
+ server_verify_data,
+ %% How to do BEAST mitigation?
+ beast_mitigation
}).
-record(connection_states, {
diff --git a/lib/ssl/src/ssl_tls_dist_proxy.erl b/lib/ssl/src/ssl_tls_dist_proxy.erl
index 4651687fe6..2e308a15b7 100644
--- a/lib/ssl/src/ssl_tls_dist_proxy.erl
+++ b/lib/ssl/src/ssl_tls_dist_proxy.erl
@@ -116,7 +116,8 @@ handle_call({listen, Driver, Name}, _From, State) ->
{ok, TcpAddress} = get_tcp_address(Socket),
{ok, WorldTcpAddress} = get_tcp_address(World),
{_,Port} = WorldTcpAddress#net_address.address,
- case erl_epmd:register_node(Name, Port) of
+ ErlEpmd = net_kernel:epmd_module(),
+ case ErlEpmd:register_node(Name, Port) of
{ok, Creation} ->
{reply, {ok, {Socket, TcpAddress, Creation}},
State#state{listen={Socket, World}}};
diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl
index 40f3eea527..91903b4a1f 100644
--- a/lib/ssl/src/tls_connection.erl
+++ b/lib/ssl/src/tls_connection.erl
@@ -496,7 +496,8 @@ decode_alerts(Bin) ->
initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions, Tracker}, User,
{CbModule, DataTag, CloseTag, ErrorTag}) ->
- ConnectionStates = ssl_record:init_connection_states(Role),
+ #ssl_options{beast_mitigation = BeastMitigation} = SSLOptions,
+ ConnectionStates = ssl_record:init_connection_states(Role, BeastMitigation),
SessionCacheCb = case application:get_env(ssl, session_cb) of
{ok, Cb} when is_atom(Cb) ->
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index d1162ab4a5..cd06b97ab2 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -236,7 +236,10 @@ error_handling_tests_tls()->
rizzo_tests() ->
[rizzo,
- no_rizzo_rc4].
+ no_rizzo_rc4,
+ rizzo_one_n_minus_one,
+ rizzo_zero_n,
+ rizzo_disabled].
%%--------------------------------------------------------------------
init_per_suite(Config0) ->
@@ -346,10 +349,27 @@ init_per_testcase(TestCase, Config) when TestCase == psk_cipher_suites;
ssl_test_lib:ct_log_supported_protocol_versions(Config),
ct:timetrap({seconds, 30}),
Config;
+
init_per_testcase(rizzo, Config) ->
ssl_test_lib:ct_log_supported_protocol_versions(Config),
ct:timetrap({seconds, 40}),
Config;
+
+init_per_testcase(rizzo_one_n_minus_one, Config) ->
+ ct:log("TLS/SSL version ~p~n ", [tls_record:supported_protocol_versions()]),
+ ct:timetrap({seconds, 40}),
+ rizzo_add_mitigation_option(one_n_minus_one, Config);
+
+init_per_testcase(rizzo_zero_n, Config) ->
+ ct:log("TLS/SSL version ~p~n ", [tls_record:supported_protocol_versions()]),
+ ct:timetrap({seconds, 40}),
+ rizzo_add_mitigation_option(zero_n, Config);
+
+init_per_testcase(rizzo_disabled, Config) ->
+ ct:log("TLS/SSL version ~p~n ", [tls_record:supported_protocol_versions()]),
+ ct:timetrap({seconds, 40}),
+ rizzo_add_mitigation_option(disabled, Config);
+
init_per_testcase(prf, Config) ->
ct:log("TLS/SSL version ~p~n ", [tls_record:supported_protocol_versions()]),
ct:timetrap({seconds, 40}),
@@ -3488,6 +3508,36 @@ no_rizzo_rc4(Config) when is_list(Config) ->
run_send_recv_rizzo(Ciphers, Config, Version,
{?MODULE, send_recv_result_active_no_rizzo, []}).
+rizzo_one_n_minus_one() ->
+ [{doc,"Test that the 1/n-1-split mitigation of Rizzo/Dungon attack can be explicitly selected"}].
+
+rizzo_one_n_minus_one(Config) when is_list(Config) ->
+ Ciphers = [X || X ={_,Y,_} <- ssl:cipher_suites(), Y =/= rc4_128],
+ Prop = proplists:get_value(tc_group_properties, Config),
+ Version = proplists:get_value(name, Prop),
+ run_send_recv_rizzo(Ciphers, Config, Version,
+ {?MODULE, send_recv_result_active_rizzo, []}).
+
+rizzo_zero_n() ->
+ [{doc,"Test that the 0/n-split mitigation of Rizzo/Dungon attack can be explicitly selected"}].
+
+rizzo_zero_n(Config) when is_list(Config) ->
+ Ciphers = [X || X ={_,Y,_} <- ssl:cipher_suites(), Y =/= rc4_128],
+ Prop = proplists:get_value(tc_group_properties, Config),
+ Version = proplists:get_value(name, Prop),
+ run_send_recv_rizzo(Ciphers, Config, Version,
+ {?MODULE, send_recv_result_active_no_rizzo, []}).
+
+rizzo_disabled() ->
+ [{doc,"Test that the mitigation of Rizzo/Dungon attack can be explicitly disabled"}].
+
+rizzo_disabled(Config) when is_list(Config) ->
+ Ciphers = [X || X ={_,Y,_} <- ssl:cipher_suites(), Y =/= rc4_128],
+ Prop = proplists:get_value(tc_group_properties, Config),
+ Version = proplists:get_value(name, Prop),
+ run_send_recv_rizzo(Ciphers, Config, Version,
+ {?MODULE, send_recv_result_active_no_rizzo, []}).
+
%%--------------------------------------------------------------------
new_server_wants_peer_cert() ->
[{doc, "Test that server configured to do client certification does"
@@ -3960,6 +4010,18 @@ renegotiate_rejected(Socket) ->
ssl:send(Socket, "Hello world"),
ok.
+rizzo_add_mitigation_option(Value, Config) ->
+ lists:foldl(fun(Opt, Acc) ->
+ case proplists:get_value(Opt, Acc) of
+ undefined -> Acc;
+ C ->
+ N = lists:keystore(beast_mitigation, 1, C,
+ {beast_mitigation, Value}),
+ lists:keystore(Opt, 1, Acc, {Opt, N})
+ end
+ end, Config,
+ [client_opts, client_dsa_opts, server_opts, server_dsa_opts,
+ server_ecdsa_opts, server_ecdh_rsa_opts]).
new_config(PrivDir, ServerOpts0) ->
CaCertFile = proplists:get_value(cacertfile, ServerOpts0),
diff --git a/lib/stdlib/src/beam_lib.erl b/lib/stdlib/src/beam_lib.erl
index 7a17226e46..fe9df601eb 100644
--- a/lib/stdlib/src/beam_lib.erl
+++ b/lib/stdlib/src/beam_lib.erl
@@ -46,7 +46,7 @@
terminate/2,code_change/3]).
-export([make_crypto_key/2, get_crypto_key/1]). %Utilities used by compiler
--export_type([attrib_entry/0, compinfo_entry/0, labeled_entry/0]).
+-export_type([attrib_entry/0, compinfo_entry/0, labeled_entry/0, label/0]).
-import(lists, [append/1, delete/2, foreach/2, keysort/2,
member/2, reverse/1, sort/1, splitwith/2]).
diff --git a/lib/stdlib/src/erl_compile.erl b/lib/stdlib/src/erl_compile.erl
index ef54076ee3..a6ae398d03 100644
--- a/lib/stdlib/src/erl_compile.erl
+++ b/lib/stdlib/src/erl_compile.erl
@@ -60,6 +60,7 @@ compile_cmdline() ->
_ -> my_halt(2)
end.
+-spec my_halt(_) -> no_return().
my_halt(Reason) ->
erlang:halt(Reason).
diff --git a/lib/stdlib/src/escript.erl b/lib/stdlib/src/escript.erl
index b8ce311c35..f53b0e2246 100644
--- a/lib/stdlib/src/escript.erl
+++ b/lib/stdlib/src/escript.erl
@@ -906,6 +906,7 @@ anno(L) ->
fatal(Str) ->
throw(Str).
+-spec my_halt(_) -> no_return().
my_halt(Reason) ->
erlang:halt(Reason).
diff --git a/lib/stdlib/src/proc_lib.erl b/lib/stdlib/src/proc_lib.erl
index 4a19603ec2..3dc1848550 100644
--- a/lib/stdlib/src/proc_lib.erl
+++ b/lib/stdlib/src/proc_lib.erl
@@ -479,18 +479,12 @@ trans_init(gen,init_it,[gen_server,_,_,supervisor_bridge,[Module|_],_]) ->
{supervisor_bridge,Module,1};
trans_init(gen,init_it,[gen_server,_,_,_,supervisor_bridge,[Module|_],_]) ->
{supervisor_bridge,Module,1};
-trans_init(gen,init_it,[GenMod,_,_,Module,_,_])
- when GenMod =:= gen_server;
- GenMod =:= gen_statem;
- GenMod =:= gen_fsm ->
- {Module,init,1};
-trans_init(gen,init_it,[GenMod,_,_,_,Module|_])
- when GenMod =:= gen_server;
- GenMod =:= gen_statem;
- GenMod =:= gen_fsm ->
- {Module,init,1};
trans_init(gen,init_it,[gen_event|_]) ->
{gen_event,init_it,6};
+trans_init(gen,init_it,[_GenMod,_,_,Module,_,_]) when is_atom(Module) ->
+ {Module,init,1};
+trans_init(gen,init_it,[_GenMod,_,_,_,Module|_]) when is_atom(Module) ->
+ {Module,init,1};
trans_init(M, F, A) when is_atom(M), is_atom(F) ->
{M,F,length(A)}.
diff --git a/lib/stdlib/src/supervisor.erl b/lib/stdlib/src/supervisor.erl
index 38b764541a..a594c66fa7 100644
--- a/lib/stdlib/src/supervisor.erl
+++ b/lib/stdlib/src/supervisor.erl
@@ -246,7 +246,7 @@ check_childspecs(ChildSpecs) when is_list(ChildSpecs) ->
check_childspecs(X) -> {error, {badarg, X}}.
%%%-----------------------------------------------------------------
-%%% Called by timer:apply_after from restart/2
+%%% Called by restart/2
-spec try_again_restart(SupRef, Child) -> ok when
SupRef :: sup_ref(),
Child :: child_id() | pid().
@@ -571,8 +571,8 @@ count_child(#child{pid = Pid, child_type = supervisor},
end.
-%%% If a restart attempt failed, this message is sent via
-%%% timer:apply_after(0,...) in order to give gen_server the chance to
+%%% If a restart attempt failed, this message is cast
+%%% from restart/2 in order to give gen_server the chance to
%%% check it's inbox before trying again.
-spec handle_cast({try_again_restart, child_id() | pid()}, state()) ->
{'noreply', state()} | {stop, shutdown, state()}.
@@ -790,16 +790,10 @@ restart(Child, State) ->
Id = if ?is_simple(State) -> Child#child.pid;
true -> Child#child.name
end,
- {ok, _TRef} = timer:apply_after(0,
- ?MODULE,
- try_again_restart,
- [self(),Id]),
+ ok = try_again_restart(self(), Id),
{ok,NState2};
{try_again, NState2, #child{name=ChName}} ->
- {ok, _TRef} = timer:apply_after(0,
- ?MODULE,
- try_again_restart,
- [self(),ChName]),
+ ok = try_again_restart(self(), ChName),
{ok,NState2};
Other ->
Other
diff --git a/make/emd2exml.in b/make/emd2exml.in
index d84b967e40..903d707716 100644
--- a/make/emd2exml.in
+++ b/make/emd2exml.in
@@ -39,7 +39,7 @@
%%% Created : 25 Feb 2010 by Rickard Green
%%%-------------------------------------------------------------------
--define(MAX_HEADING, 3).
+-define(MAX_HEADING, 6).
-define(DELAYED_COPYRIGHT_IX, 0).
-define(DELAYED_TOC_IX, 1).
@@ -80,7 +80,6 @@
copyright = false,
copyright_data = [],
have_h1 = false,
- smarker_ix = false,
toc = [],
ifile,
ofile}).
@@ -368,9 +367,9 @@ put_text(S, "%OTP-REL%"++Cs, CTag, EmTag, Acc) ->
put_text(S, [$\\,C|Cs], no, EmTag, Acc) ->
put_text(S, Cs, no, EmTag, [C|Acc]);
put_text(S, [C,C|Cs], no, b, Acc) when C == $*; C == $_ ->
- put_text(S, Cs, no, no, ["</b>"|Acc]);
+ put_text(S, Cs, no, no, ["</strong>"|Acc]);
put_text(S, [C,C|Cs], no, no, Acc) when C == $*; C == $_ ->
- put_text(S, Cs, no, b, ["<b>"|Acc]);
+ put_text(S, Cs, no, b, ["<strong>"|Acc]);
put_text(S, [C|Cs], no, em, Acc) when C == $*; C == $_ ->
put_text(S, Cs, no, no, ["</em>"|Acc]);
put_text(S, [C|Cs], no, no, Acc) when C == $*; C == $_ ->
@@ -614,8 +613,7 @@ strip_lvls(_N, Str) ->
put_title(S, 1, Title) ->
header(chk_h1(1, S#state{h = 1, mlist = [top]}), Title);
put_title(#state{mlist = MList0,
- toc = TOC,
- smarker_ix = SMarkerIX} = S0, H, Title) ->
+ toc = TOC} = S0, H, Title) ->
TitleStr = text(Title),
MList1 = [mk_lvl_marker(Title) | MList0],
Marker = mk_marker(MList1),
@@ -626,12 +624,10 @@ put_title(#state{mlist = MList0,
"<seealso marker=\"#",Marker,"\">",
TitleStr,"</seealso>",nl()],
h = H,
- mlist = MList1,
- smarker_ix = false}),
- true = is_integer(SMarkerIX),
- S2 = write_delayed(S1, SMarkerIX, ["<marker id=\"", Marker, "\"/>",nl()]),
+ mlist = MList1}),
+ S2 = put_chars(S1, ["<marker id=\"", Marker, "\"/>",nl()]),
{STag, ETag} = case H > ?MAX_HEADING of
- true -> {"<p><b>", "</b></p>"};
+ true -> {"<p><strong>", "</strong></p>"};
false -> {"<title>", "</title>"}
end,
put_chars(S2, [STag, TitleStr, ETag, nl()]).
@@ -701,15 +697,10 @@ sections_change(H, OldH, #state{mlist = [_|ML], toc = TOC} = S0) ->
begin_section(1, S) ->
put_line(S, "<chapter>");
-begin_section(H, #state{delayed_array_ix = IX} = S0) when H > ?MAX_HEADING ->
- false = S0#state.smarker_ix,
- true = is_integer(IX),
- put_delayed(S0#state{smarker_ix = IX, delayed_array_ix = IX+1}, IX);
-begin_section(H, #state{delayed_array_ix = IX} = S0) when H > 1 ->
- false = S0#state.smarker_ix,
- true = is_integer(IX),
- S1 = put_delayed(S0#state{smarker_ix = IX, delayed_array_ix = IX+1}, IX),
- put_line(S1, "<section>");
+begin_section(H, S) when H > ?MAX_HEADING ->
+ S;
+begin_section(H, S0) when H > 1 ->
+ put_line(S0, "<section>");
begin_section(_H, S) ->
S.
@@ -779,7 +770,7 @@ create_toc(#state{toc = TOC} = S) ->
{value,{"true",[]}} ->
write_delayed(S,
?DELAYED_TOC_IX,
- ["<p><b>Table of Contents</b></p>", nl(), TOC]);
+ ["<p><strong>Table of Contents</strong></p>", nl(), TOC]);
_ ->
write_delayed(S, ?DELAYED_TOC_IX, "")
end.
@@ -986,10 +977,10 @@ chg_bq_lvl(Lvl, #state{bq_lvl = Lvl} = S) ->
S;
chg_bq_lvl(NewLvl, #state{bq_lvl = Lvl} = S) when NewLvl > Lvl ->
chg_bq_lvl(NewLvl,
- put_line(end_p(end_code(S#state{bq_lvl = Lvl+1})), "<blockquote>"));
+ put_line(end_p(end_code(S#state{bq_lvl = Lvl+1})), "<quote>"));
chg_bq_lvl(NewLvl, #state{bq_lvl = Lvl} = S) ->
chg_bq_lvl(NewLvl,
- put_line(end_p(end_code(S#state{bq_lvl = Lvl-1})), "</blockquote>")).
+ put_line(end_p(end_code(S#state{bq_lvl = Lvl-1})), "</quote>")).
%%
%% Resolve link
diff --git a/system/doc/efficiency_guide/advanced.xml b/system/doc/efficiency_guide/advanced.xml
index 3609b8d88e..016302fe50 100644
--- a/system/doc/efficiency_guide/advanced.xml
+++ b/system/doc/efficiency_guide/advanced.xml
@@ -35,8 +35,7 @@
how much memory different data types and operations require. It is
implementation-dependent how much memory the Erlang data types and
other items consume, but the following table shows some figures for
- the <c>erts-5.2</c> system in R9B. There have been no significant
- changes in R13.</p>
+ the <c>erts-8.0</c> system in OTP 19.0.</p>
<p>The unit of measurement is memory words. There exists both a
32-bit and a 64-bit implementation. A word is therefore 4 bytes or
@@ -87,6 +86,19 @@
<cell>2 words + the size of each element.</cell>
</row>
<row>
+ <cell>Small Map</cell>
+ <cell>4 words + 2 words per entry (key and value) + the size of each key and value pair.</cell>
+ </row>
+ <row>
+ <cell>Large Map</cell>
+ <cell>
+ At least, 2 words + 2 x <c>N</c> words + 2 x log16(<c>N</c>) words +
+ the size of each key and value pair, where <c>N</c> is the number of pairs in the Map.
+ A large Map is represented as a tree internally where each node in the tree is a
+ "sparse tuple" of arity 16.
+ </cell>
+ </row>
+ <row>
<cell>Pid</cell>
<cell>1 word for a process identifier from the current local node
+ 5 words for a process identifier from another node.<br></br>
@@ -122,7 +134,7 @@
</row>
<row>
<cell>Erlang process</cell>
- <cell>327 words when spawned, including a heap of 233 words.</cell>
+ <cell>338 words when spawned, including a heap of 233 words.</cell>
</row>
<tcaption>Memory Size of Different Data Types</tcaption>
</table>