diff options
Diffstat (limited to 'lib/kernel')
39 files changed, 1885 insertions, 550 deletions
diff --git a/lib/kernel/doc/src/disk_log.xml b/lib/kernel/doc/src/disk_log.xml index 1be28adfb8..884cb32c0c 100644 --- a/lib/kernel/doc/src/disk_log.xml +++ b/lib/kernel/doc/src/disk_log.xml @@ -972,7 +972,7 @@ <item> <p>Specifies if messages will be sent to <c>error_logger</c> on recoverable errors with - the log files. Defaults to <c>true</c>.</p> + the log files. Defaults to <c>false</c>.</p> </item> </taglist> <p><c>open/1</c> returns <c>{ok, <anno>Log</anno>}</c> if the diff --git a/lib/kernel/doc/src/file.xml b/lib/kernel/doc/src/file.xml index b674b3ca93..593bee74fe 100644 --- a/lib/kernel/doc/src/file.xml +++ b/lib/kernel/doc/src/file.xml @@ -59,7 +59,7 @@ terminal supports UTF-8, otherwise <c>latin1</c>. The default can be overridden using <c>+fnl</c> (to force <c>latin1</c> mode) or <c>+fnu</c> (to force <c>utf8</c> mode) when starting - <seealso marker="erts:erl"><c>erts:erl</c></seealso>.</p> + <seealso marker="erts:erl"><c>erl</c></seealso>.</p> <p>On operating systems with transparent naming, files can be inconsistently named, for example, some files are encoded in UTF-8 while @@ -81,6 +81,22 @@ <p>See also section <seealso marker="stdlib:unicode_usage#notes-about-raw-filenames">Notes About Raw Filenames</seealso> in the STDLIB User's Guide.</p> + <note><p> + File operations used to accept filenames containing + null characters (integer value zero). This caused + the name to be truncated at the first null character. + Filenames containing null characters inside the filename + are now <em>rejected</em> and will cause primitive + file operations fail. + </p></note> + <warning><p> + Currently null characters at the end of the filename + will be accepted by primitive file operations. Such + filenames are however still documented as invalid. The + implementation will also change in the future and + reject such filenames. + </p></warning> + </description> <datatypes> @@ -96,9 +112,21 @@ </datatype> <datatype> <name name="filename"/> + <desc> + <p> + See also the documentation of the + <seealso marker="#type-name_all"><c>name_all()</c></seealso> type. + </p> + </desc> </datatype> <datatype> <name name="filename_all"/> + <desc> + <p> + See also the documentation of the + <seealso marker="#type-name_all"><c>name_all()</c></seealso> type. + </p> + </desc> </datatype> <datatype> <name name="io_device"/> @@ -112,21 +140,23 @@ <name name="name"/> <desc> <p>If VM is in Unicode filename mode, <c>string()</c> and <c>char()</c> - are allowed to be > 255. + are allowed to be > 255. See also the documentation of the + <seealso marker="#type-name_all"><c>name_all()</c></seealso> type. </p> </desc> </datatype> <datatype> <name name="name_all"/> <desc> - <p>If VM is in Unicode filename mode, <c>string()</c> and <c>char()</c> + <p>If VM is in Unicode filename mode, characters are allowed to be > 255. <c><anno>RawFilename</anno></c> is a filename not subject to Unicode translation, meaning that it can contain characters not conforming to the Unicode encoding expected from the file system (that is, non-UTF-8 characters although the VM is started - in Unicode filename mode). + in Unicode filename mode). Null characters (integer value zero) + are <em>not</em> allowed in filenames (not even at the end). </p> </desc> </datatype> diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml index b71e8a1e5d..169a76463b 100644 --- a/lib/kernel/doc/src/inet.xml +++ b/lib/kernel/doc/src/inet.xml @@ -222,11 +222,18 @@ fe80::204:acff:fe17:bf38 <name name="get_rc" arity="0"/> <fsummary>Return a list of IP configuration parameters.</fsummary> <desc> - <p>Returns the state of the <c>Inet</c> configuration database in + <p> + Returns the state of the <c>Inet</c> configuration database in form of a list of recorded configuration parameters. For more information, see <seealso marker="erts:inet_cfg">ERTS User's Guide: Inet Configuration</seealso>. - Only parameters with other than default values are returned.</p> + </p> + <p> + Only actual parameters with other than default values + are returned, for example not directives that specify + other sources for configuration parameters nor + directives that clear parameters. + </p> </desc> </func> diff --git a/lib/kernel/doc/src/inet_res.xml b/lib/kernel/doc/src/inet_res.xml index 4ada4203c0..3454e3c6f9 100644 --- a/lib/kernel/doc/src/inet_res.xml +++ b/lib/kernel/doc/src/inet_res.xml @@ -130,7 +130,7 @@ dns_header() = DnsHeader inet_dns:header(DnsHeader) -> [ {id, integer()} | {qr, boolean()} - | {opcode, 'query' | iquery | status | integer()} + | {opcode, query | iquery | status | integer()} | {aa, boolean()} | {tc, boolean()} | {rd, boolean()} diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml index e1cf45109d..a5316dd476 100644 --- a/lib/kernel/doc/src/notes.xml +++ b/lib/kernel/doc/src/notes.xml @@ -31,6 +31,65 @@ </header> <p>This document describes the changes made to the Kernel application.</p> +<section><title>Kernel 5.4</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Processes which did output after switching jobs (Ctrl+G) + could be left forever stuck in the io request.</p> + <p> + Own Id: OTP-14571 Aux Id: ERL-472 </p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p>Lock counting can now be fully toggled at runtime in + the lock counting emulator (<c>-emu_type lcnt</c>). + Everything is enabled by default to match the old + behavior, but specific categories can be toggled at will + with minimal runtime overhead when disabled. Refer to the + documentation on <c>lcnt:rt_mask/1</c> for details.</p> + <p> + Own Id: OTP-13170</p> + </item> + <item> + <p><c>lcnt:collect</c> and <c>lcnt:clear</c> will no + longer block all other threads in the runtime system.</p> + <p> + Own Id: OTP-14412</p> + </item> + <item> + <p> + General Unicode improvements.</p> + <p> + Own Id: OTP-14462</p> + </item> + </list> + </section> + +</section> + +<section><title>Kernel 5.3.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>The documentation for the 'quiet' option in + disk_log:open/1 had an incorrect default value.</p> + <p> + Own Id: OTP-14498</p> + </item> + </list> + </section> + +</section> + <section><title>Kernel 5.3</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/kernel/examples/Makefile b/lib/kernel/examples/Makefile index 26ec58f571..f86e662838 100644 --- a/lib/kernel/examples/Makefile +++ b/lib/kernel/examples/Makefile @@ -45,7 +45,7 @@ RELSYSDIR = $(RELEASE_PATH)/lib/kernel-$(KERNEL_VSN)/examples # Pack and install the complete directory structure from # here (CWD) and down, for all examples. -EXAMPLES = uds_dist +EXAMPLES = uds_dist gen_tcp_dist release_spec: $(INSTALL_DIR) "$(RELSYSDIR)" diff --git a/lib/kernel/examples/gen_tcp_dist/Makefile b/lib/kernel/examples/gen_tcp_dist/Makefile new file mode 100644 index 0000000000..65513a1729 --- /dev/null +++ b/lib/kernel/examples/gen_tcp_dist/Makefile @@ -0,0 +1,20 @@ +RM=rm -f +CP=cp +EBIN=ebin +ERLC=erlc +# Works if building in open source source tree +KERNEL_INCLUDE=$(ERL_TOP)/lib/kernel/include +ERLCFLAGS+= -W -I$(KERNEL_INCLUDE) + +MODULES=gen_tcp_dist + +TARGET_FILES=$(MODULES:%=$(EBIN)/%.beam) + +opt: $(TARGET_FILES) + +$(EBIN)/%.beam: src/%.erl + $(ERLC) $(ERLCFLAGS) -o$(EBIN) $< + +clean: + $(RM) $(TARGET_FILES) + diff --git a/lib/kernel/examples/gen_tcp_dist/ebin/.gitignore b/lib/kernel/examples/gen_tcp_dist/ebin/.gitignore new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/lib/kernel/examples/gen_tcp_dist/ebin/.gitignore diff --git a/lib/kernel/examples/gen_tcp_dist/src/gen_tcp_dist.erl b/lib/kernel/examples/gen_tcp_dist/src/gen_tcp_dist.erl new file mode 100644 index 0000000000..98554ed805 --- /dev/null +++ b/lib/kernel/examples/gen_tcp_dist/src/gen_tcp_dist.erl @@ -0,0 +1,781 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2017. 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(gen_tcp_dist). + +%% +%% This is an example of how to plug in an arbitrary distribution +%% carrier for Erlang using distribution processes. +%% +%% This example uses gen_tcp for transportation of data, but +%% you can use whatever underlying protocol you want as long +%% as your implementation reliably delivers data chunks to the +%% receiving VM in the order they were sent from the sending +%% VM. +%% +%% This code is a rewrite of the lib/kernel/src/inet_tcp_dist.erl +%% distribution impementation for TCP used by default. That +%% implementation use distribution ports instead of distribution +%% processes and is more efficient compared to this implementation. +%% This since this implementation more or less gets the +%% distribution processes in between the VM and the ports without +%% any gain specific gain. +%% + +-export([listen/1, accept/1, accept_connection/5, + setup/5, close/1, select/1, is_node_name/1]). + +%% Optional +-export([setopts/2, getopts/2]). + +%% internal exports + +-export([dist_cntrlr_setup/1, dist_cntrlr_input_setup/3, + dist_cntrlr_tick_handler/1]). + +-export([accept_loop/2,do_accept/6,do_setup/6]). + +-import(error_logger,[error_msg/2]). + +-include("net_address.hrl"). + +-include("dist.hrl"). +-include("dist_util.hrl"). + +%% ------------------------------------------------------------ +%% Select this protocol based on node name +%% select(Node) => Bool +%% ------------------------------------------------------------ + +select(Node) -> + case split_node(atom_to_list(Node), $@, []) of + [_, Host] -> + case inet:getaddr(Host, inet) of + {ok,_} -> true; + _ -> false + end; + _ -> false + end. + +%% ------------------------------------------------------------ +%% Create the listen socket, i.e. the port that this erlang +%% node is accessible through. +%% ------------------------------------------------------------ + +listen(Name) -> + case do_listen([binary, {active, false}, {packet,2}, {reuseaddr, true}]) of + {ok, Socket} -> + TcpAddress = get_tcp_address(Socket), + {_,Port} = TcpAddress#net_address.address, + ErlEpmd = net_kernel:epmd_module(), + case ErlEpmd:register_node(Name, Port) of + {ok, Creation} -> + {ok, {Socket, TcpAddress, Creation}}; + Error -> + Error + end; + Error -> + Error + end. + +do_listen(Options) -> + {First,Last} = case application:get_env(kernel,inet_dist_listen_min) of + {ok,N} when is_integer(N) -> + case application:get_env(kernel, + inet_dist_listen_max) of + {ok,M} when is_integer(M) -> + {N,M}; + _ -> + {N,N} + end; + _ -> + {0,0} + end, + do_listen(First, Last, listen_options([{backlog,128}|Options])). + +do_listen(First,Last,_) when First > Last -> + {error,eaddrinuse}; +do_listen(First,Last,Options) -> + case gen_tcp:listen(First, Options) of + {error, eaddrinuse} -> + do_listen(First+1,Last,Options); + Other -> + Other + end. + +listen_options(Opts0) -> + Opts1 = + case application:get_env(kernel, inet_dist_use_interface) of + {ok, Ip} -> + [{ip, Ip} | Opts0]; + _ -> + Opts0 + end, + case application:get_env(kernel, inet_dist_listen_options) of + {ok,ListenOpts} -> + ListenOpts ++ Opts1; + _ -> + Opts1 + end. + + +%% ------------------------------------------------------------ +%% Accepts new connection attempts from other Erlang nodes. +%% ------------------------------------------------------------ + +accept(Listen) -> + spawn_opt(?MODULE, accept_loop, [self(), Listen], [link, {priority, max}]). + +accept_loop(Kernel, Listen) -> + ?trace("~p~n",[{?MODULE, accept_loop, self()}]), + case gen_tcp:accept(Listen) of + {ok, Socket} -> + DistCtrl = spawn_dist_cntrlr(Socket), + ?trace("~p~n",[{?MODULE, accept_loop, accepted, Socket, DistCtrl, self()}]), + flush_controller(DistCtrl, Socket), + gen_tcp:controlling_process(Socket, DistCtrl), + flush_controller(DistCtrl, Socket), + Kernel ! {accept,self(),DistCtrl,inet,tcp}, + receive + {Kernel, controller, Pid} -> + call_ctrlr(DistCtrl, {supervisor, Pid}), + Pid ! {self(), controller}; + {Kernel, unsupported_protocol} -> + exit(unsupported_protocol) + end, + accept_loop(Kernel, Listen); + Error -> + exit(Error) + end. + +flush_controller(Pid, Socket) -> + receive + {tcp, Socket, Data} -> + Pid ! {tcp, Socket, Data}, + flush_controller(Pid, Socket); + {tcp_closed, Socket} -> + Pid ! {tcp_closed, Socket}, + flush_controller(Pid, Socket) + after 0 -> + ok + end. + +%% ------------------------------------------------------------ +%% Accepts a new connection attempt from another Erlang node. +%% Performs the handshake with the other side. +%% ------------------------------------------------------------ + +accept_connection(AcceptPid, DistCtrl, MyNode, Allowed, SetupTime) -> + spawn_opt(?MODULE, do_accept, + [self(), AcceptPid, DistCtrl, MyNode, Allowed, SetupTime], + [link, {priority, max}]). + +do_accept(Kernel, AcceptPid, DistCtrl, MyNode, Allowed, SetupTime) -> + ?trace("~p~n",[{?MODULE, do_accept, self(), MyNode}]), + receive + {AcceptPid, controller} -> + Timer = dist_util:start_timer(SetupTime), + case check_ip(DistCtrl) of + true -> + HSData0 = hs_data_common(DistCtrl), + HSData = HSData0#hs_data{kernel_pid = Kernel, + this_node = MyNode, + socket = DistCtrl, + timer = Timer, + this_flags = 0, + allowed = Allowed}, + dist_util:handshake_other_started(HSData); + {false,IP} -> + error_msg("** Connection attempt from " + "disallowed IP ~w ** ~n", [IP]), + ?shutdown(no_node) + end + end. + +%% we may not always want the nodelay behaviour +%% for performance reasons + +nodelay() -> + case application:get_env(kernel, dist_nodelay) of + undefined -> + {nodelay, true}; + {ok, true} -> + {nodelay, true}; + {ok, false} -> + {nodelay, false}; + _ -> + {nodelay, true} + end. + +%% ------------------------------------------------------------ +%% Setup a new connection to another Erlang node. +%% Performs the handshake with the other side. +%% ------------------------------------------------------------ + +setup(Node, Type, MyNode, LongOrShortNames,SetupTime) -> + spawn_opt(?MODULE, do_setup, + [self(), Node, Type, MyNode, LongOrShortNames, SetupTime], + [link, {priority, max}]). + +do_setup(Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) -> + ?trace("~p~n",[{?MODULE, do_setup, self(), Node}]), + [Name, Address] = splitnode(Node, LongOrShortNames), + case inet:getaddr(Address, inet) of + {ok, Ip} -> + Timer = dist_util:start_timer(SetupTime), + ErlEpmd = net_kernel:epmd_module(), + case ErlEpmd:port_please(Name, Ip) of + {port, TcpPort, Version} -> + ?trace("port_please(~p) -> version ~p~n", + [Node,Version]), + dist_util:reset_timer(Timer), + case + gen_tcp:connect( + Ip, TcpPort, + connect_options([binary, {active, false}, {packet, 2}])) + of + {ok, Socket} -> + DistCtrl = spawn_dist_cntrlr(Socket), + call_ctrlr(DistCtrl, {supervisor, self()}), + flush_controller(DistCtrl, Socket), + gen_tcp:controlling_process(Socket, DistCtrl), + flush_controller(DistCtrl, Socket), + HSData0 = hs_data_common(DistCtrl), + HSData = HSData0#hs_data{kernel_pid = Kernel, + other_node = Node, + this_node = MyNode, + socket = DistCtrl, + timer = Timer, + this_flags = 0, + other_version = Version, + request_type = Type}, + dist_util:handshake_we_started(HSData); + _ -> + %% Other Node may have closed since + %% port_please ! + ?trace("other node (~p) " + "closed since port_please.~n", + [Node]), + ?shutdown(Node) + end; + _ -> + ?trace("port_please (~p) " + "failed.~n", [Node]), + ?shutdown(Node) + end; + _Other -> + ?trace("inet_getaddr(~p) " + "failed (~p).~n", [Node,_Other]), + ?shutdown(Node) + end. + +connect_options(Opts) -> + case application:get_env(kernel, inet_dist_connect_options) of + {ok,ConnectOpts} -> + ConnectOpts ++ Opts; + _ -> + Opts + end. + +%% +%% Close a socket. +%% +close(Listen) -> + gen_tcp:close(Listen). + + +%% If Node is illegal terminate the connection setup!! +splitnode(Node, LongOrShortNames) -> + case split_node(atom_to_list(Node), $@, []) of + [Name|Tail] when Tail =/= [] -> + Host = lists:append(Tail), + case split_node(Host, $., []) of + [_] when LongOrShortNames =:= longnames -> + case inet:parse_address(Host) of + {ok, _} -> + [Name, Host]; + _ -> + error_msg("** System running to use " + "fully qualified " + "hostnames **~n" + "** Hostname ~ts is illegal **~n", + [Host]), + ?shutdown(Node) + end; + L when length(L) > 1, LongOrShortNames =:= shortnames -> + error_msg("** System NOT running to use fully qualified " + "hostnames **~n" + "** Hostname ~ts is illegal **~n", + [Host]), + ?shutdown(Node); + _ -> + [Name, Host] + end; + [_] -> + error_msg("** Nodename ~p illegal, no '@' character **~n", + [Node]), + ?shutdown(Node); + _ -> + error_msg("** Nodename ~p illegal **~n", [Node]), + ?shutdown(Node) + end. + +split_node([Chr|T], Chr, Ack) -> [lists:reverse(Ack)|split_node(T, Chr, [])]; +split_node([H|T], Chr, Ack) -> split_node(T, Chr, [H|Ack]); +split_node([], _, Ack) -> [lists:reverse(Ack)]. + +%% ------------------------------------------------------------ +%% Fetch local information about a Socket. +%% ------------------------------------------------------------ +get_tcp_address(Socket) -> + {ok, Address} = inet:sockname(Socket), + {ok, Host} = inet:gethostname(), + #net_address { + address = Address, + host = Host, + protocol = tcp, + family = inet + }. + +%% ------------------------------------------------------------ +%% Do only accept new connection attempts from nodes at our +%% own LAN, if the check_ip environment parameter is true. +%% ------------------------------------------------------------ +check_ip(DistCtrl) -> + case application:get_env(check_ip) of + {ok, true} -> + case get_ifs(DistCtrl) of + {ok, IFs, IP} -> + check_ip(IFs, IP); + _ -> + ?shutdown(no_node) + end; + _ -> + true + end. + +get_ifs(DistCtrl) -> + Socket = call_ctrlr(DistCtrl, socket), + case inet:peername(Socket) of + {ok, {IP, _}} -> + case inet:getif(Socket) of + {ok, IFs} -> {ok, IFs, IP}; + Error -> Error + end; + Error -> + Error + end. + +check_ip([{OwnIP, _, Netmask}|IFs], PeerIP) -> + case {inet_tcp:mask(Netmask, PeerIP), inet_tcp:mask(Netmask, OwnIP)} of + {M, M} -> true; + _ -> check_ip(IFs, PeerIP) + end; +check_ip([], PeerIP) -> + {false, PeerIP}. + +is_node_name(Node) when is_atom(Node) -> + case split_node(atom_to_list(Node), $@, []) of + [_, _Host] -> true; + _ -> false + end; +is_node_name(_Node) -> + false. + +hs_data_common(DistCtrl) -> + TickHandler = call_ctrlr(DistCtrl, tick_handler), + Socket = call_ctrlr(DistCtrl, socket), + #hs_data{f_send = send_fun(), + f_recv = recv_fun(), + f_setopts_pre_nodeup = setopts_pre_nodeup_fun(), + f_setopts_post_nodeup = setopts_post_nodeup_fun(), + f_getll = getll_fun(), + f_handshake_complete = handshake_complete_fun(), + f_address = address_fun(), + mf_setopts = setopts_fun(DistCtrl, Socket), + mf_getopts = getopts_fun(DistCtrl, Socket), + mf_getstat = getstat_fun(DistCtrl, Socket), + mf_tick = tick_fun(DistCtrl, TickHandler)}. + +%%% ------------------------------------------------------------ +%%% Distribution controller processes +%%% ------------------------------------------------------------ + +%% +%% There will be five parties working together when the +%% connection is up: +%% - The gen_tcp socket. Providing a tcp/ip connection +%% to the other node. +%% - The output handler. It will dispatch all outgoing +%% traffic from the VM to the gen_tcp socket. This +%% process is registered as distribution controller +%% for this channel with the VM. +%% - The input handler. It will dispatch all incoming +%% traffic from the gen_tcp socket to the VM. This +%% process is also the socket owner and receives +%% incoming traffic using active-N. +%% - The tick handler. Dispatches asynchronous tick +%% requests to the socket. It executes on max priority +%% since it is important to get ticks through to the +%% other end. +%% - The channel supervisor (provided by dist_util). It +%% monitors traffic. Issue tick requests to the tick +%% handler when no outgoing traffic is seen and bring +%% the connection down if no incoming traffic is seen. +%% This process also executes on max priority. +%% +%% These parties are linked togheter so should one +%% of them fail, all of them are terminated and the +%% connection is taken down. +%% + +%% In order to avoid issues with lingering signal binaries +%% we enable off-heap message queue data as well as fullsweep +%% after 0. The fullsweeps will be cheap since we have more +%% or less no live data. +-define(DIST_CNTRL_COMMON_SPAWN_OPTS, + [{message_queue_data, off_heap}, + {fullsweep_after, 0}]). + +tick_fun(DistCtrl, TickHandler) -> + fun (Ctrl) when Ctrl == DistCtrl -> + TickHandler ! tick + end. + +getstat_fun(DistCtrl, Socket) -> + fun (Ctrl) when Ctrl == DistCtrl -> + case inet:getstat(Socket, [recv_cnt, send_cnt, send_pend]) of + {ok, Stat} -> + split_stat(Stat,0,0,0); + Error -> + Error + end + end. + +split_stat([{recv_cnt, R}|Stat], _, W, P) -> + split_stat(Stat, R, W, P); +split_stat([{send_cnt, W}|Stat], R, _, P) -> + split_stat(Stat, R, W, P); +split_stat([{send_pend, P}|Stat], R, W, _) -> + split_stat(Stat, R, W, P); +split_stat([], R, W, P) -> + {ok, R, W, P}. + +setopts_fun(DistCtrl, Socket) -> + fun (Ctrl, Opts) when Ctrl == DistCtrl -> + setopts(Socket, Opts) + end. + +getopts_fun(DistCtrl, Socket) -> + fun (Ctrl, Opts) when Ctrl == DistCtrl -> + getopts(Socket, Opts) + end. + +setopts(S, Opts) -> + case [Opt || {K,_}=Opt <- Opts, + K =:= active orelse K =:= deliver orelse K =:= packet] of + [] -> inet:setopts(S,Opts); + Opts1 -> {error, {badopts,Opts1}} + end. + +getopts(S, Opts) -> + inet:getopts(S, Opts). + +send_fun() -> + fun (Ctrlr, Packet) -> + call_ctrlr(Ctrlr, {send, Packet}) + end. + +recv_fun() -> + fun (Ctrlr, Length, Timeout) -> + case call_ctrlr(Ctrlr, {recv, Length, Timeout}) of + {ok, Bin} when is_binary(Bin) -> + {ok, binary_to_list(Bin)}; + Other -> + Other + end + end. + +getll_fun() -> + fun (Ctrlr) -> + call_ctrlr(Ctrlr, getll) + end. + +address_fun() -> + fun (Ctrlr, Node) -> + case call_ctrlr(Ctrlr, {address, Node}) of + {error, no_node} -> %% No '@' or more than one '@' in node name. + ?shutdown(no_node); + Res -> + Res + end + end. + +setopts_pre_nodeup_fun() -> + fun (Ctrlr) -> + call_ctrlr(Ctrlr, pre_nodeup) + end. + +setopts_post_nodeup_fun() -> + fun (Ctrlr) -> + call_ctrlr(Ctrlr, post_nodeup) + end. + +handshake_complete_fun() -> + fun (Ctrlr, Node, DHandle) -> + call_ctrlr(Ctrlr, {handshake_complete, Node, DHandle}) + end. + +call_ctrlr(Ctrlr, Msg) -> + Ref = erlang:monitor(process, Ctrlr), + Ctrlr ! {Ref, self(), Msg}, + receive + {Ref, Res} -> + erlang:demonitor(Ref, [flush]), + Res; + {'DOWN', Ref, process, Ctrlr, Reason} -> + exit({dist_controller_exit, Reason}) + end. + +%% +%% The tick handler process writes a tick to the +%% socket when it receives a 'tick' message from +%% the connection supervisor. +%% +%% We are not allowed to block the connection +%% superviser when writing a tick and we also want +%% the tick to go through even during a heavily +%% loaded system. gen_tcp does not have a +%% non-blocking send operation exposed in its API +%% and we don't want to run the distribution +%% controller under high priority. Therefore this +%% sparate process with max prio that dispatches +%% ticks. +%% +dist_cntrlr_tick_handler(Socket) -> + receive + tick -> + %% May block due to busy port... + sock_send(Socket, ""); + _ -> + ok + end, + dist_cntrlr_tick_handler(Socket). + +spawn_dist_cntrlr(Socket) -> + spawn_opt(?MODULE, dist_cntrlr_setup, [Socket], + [{priority, max}] ++ ?DIST_CNTRL_COMMON_SPAWN_OPTS). + +dist_cntrlr_setup(Socket) -> + TickHandler = spawn_opt(?MODULE, dist_cntrlr_tick_handler, + [Socket], + [link, {priority, max}] + ++ ?DIST_CNTRL_COMMON_SPAWN_OPTS), + dist_cntrlr_setup_loop(Socket, TickHandler, undefined). + +%% +%% During the handshake phase we loop in dist_cntrlr_setup(). +%% When the connection is up we spawn an input handler and +%% continue as output handler. +%% +dist_cntrlr_setup_loop(Socket, TickHandler, Sup) -> + receive + {tcp_closed, Socket} -> + exit(connection_closed); + + {Ref, From, {supervisor, Pid}} -> + Res = link(Pid), + From ! {Ref, Res}, + dist_cntrlr_setup_loop(Socket, TickHandler, Pid); + + {Ref, From, tick_handler} -> + From ! {Ref, TickHandler}, + dist_cntrlr_setup_loop(Socket, TickHandler, Sup); + + {Ref, From, socket} -> + From ! {Ref, Socket}, + dist_cntrlr_setup_loop(Socket, TickHandler, Sup); + + {Ref, From, {send, Packet}} -> + Res = gen_tcp:send(Socket, Packet), + From ! {Ref, Res}, + dist_cntrlr_setup_loop(Socket, TickHandler, Sup); + + {Ref, From, {recv, Length, Timeout}} -> + Res = gen_tcp:recv(Socket, Length, Timeout), + From ! {Ref, Res}, + dist_cntrlr_setup_loop(Socket, TickHandler, Sup); + + {Ref, From, getll} -> + From ! {Ref, {ok, self()}}, + dist_cntrlr_setup_loop(Socket, TickHandler, Sup); + + {Ref, From, {address, Node}} -> + Res = case inet:peername(Socket) of + {ok, Address} -> + case split_node(atom_to_list(Node), $@, []) of + [_,Host] -> + #net_address{address=Address,host=Host, + protocol=tcp, family=inet}; + _ -> + {error, no_node} + end + end, + From ! {Ref, Res}, + dist_cntrlr_setup_loop(Socket, TickHandler, Sup); + + {Ref, From, pre_nodeup} -> + Res = inet:setopts(Socket, + [{active, false}, + {packet, 4}, + nodelay()]), + From ! {Ref, Res}, + dist_cntrlr_setup_loop(Socket, TickHandler, Sup); + + {Ref, From, post_nodeup} -> + Res = inet:setopts(Socket, + [{active, false}, + {packet, 4}, + nodelay()]), + From ! {Ref, Res}, + dist_cntrlr_setup_loop(Socket, TickHandler, Sup); + + {Ref, From, {handshake_complete, _Node, DHandle}} -> + From ! {Ref, ok}, + %% Handshake complete! Begin dispatching traffic... + + %% We use separate process for dispatching input. This + %% is not necessary, but it enables parallel execution + %% of independent work loads at the same time as it + %% simplifies the the implementation... + InputHandler = spawn_opt(?MODULE, dist_cntrlr_input_setup, + [DHandle, Socket, Sup], + [link] ++ ?DIST_CNTRL_COMMON_SPAWN_OPTS), + + flush_controller(InputHandler, Socket), + gen_tcp:controlling_process(Socket, InputHandler), + flush_controller(InputHandler, Socket), + + ok = erlang:dist_ctrl_input_handler(DHandle, InputHandler), + + InputHandler ! DHandle, + + %% From now on we execute on normal priority + process_flag(priority, normal), + erlang:dist_ctrl_get_data_notification(DHandle), + dist_cntrlr_output_loop(DHandle, Socket) + end. + +%% We use active 10 for good throughput while still +%% maintaining back-pressure if the input controller +%% isn't able to handle all incoming messages... +-define(ACTIVE_INPUT, 10). + +dist_cntrlr_input_setup(DHandle, Socket, Sup) -> + link(Sup), + %% Ensure we don't try to put data before registerd + %% as input handler... + receive + DHandle -> + dist_cntrlr_input_loop(DHandle, Socket, 0) + end. + +dist_cntrlr_input_loop(DHandle, Socket, N) when N =< ?ACTIVE_INPUT/2 -> + inet:setopts(Socket, [{active, ?ACTIVE_INPUT - N}]), + dist_cntrlr_input_loop(DHandle, Socket, ?ACTIVE_INPUT); +dist_cntrlr_input_loop(DHandle, Socket, N) -> + receive + {tcp_closed, Socket} -> + %% Connection to remote node terminated... + exit(connection_closed); + + {tcp, Socket, Data} -> + %% Incoming data from remote node... + try erlang:dist_ctrl_put_data(DHandle, Data) + catch _ : _ -> death_row() + end, + dist_cntrlr_input_loop(DHandle, Socket, N-1); + + _ -> + %% Ignore... + dist_cntrlr_input_loop(DHandle, Socket, N) + end. + +dist_cntrlr_send_data(DHandle, Socket) -> + case erlang:dist_ctrl_get_data(DHandle) of + none -> + erlang:dist_ctrl_get_data_notification(DHandle); + Data -> + sock_send(Socket, Data), + dist_cntrlr_send_data(DHandle, Socket) + end. + + +dist_cntrlr_output_loop(DHandle, Socket) -> + receive + dist_data -> + %% Outgoing data from this node... + try dist_cntrlr_send_data(DHandle, Socket) + catch _ : _ -> death_row() + end, + dist_cntrlr_output_loop(DHandle, Socket); + + {send, From, Ref, Data} -> + %% This is for testing only! + %% + %% Needed by some OTP distribution + %% test suites... + sock_send(Socket, Data), + From ! {Ref, ok}, + dist_cntrlr_output_loop(DHandle, Socket); + + _ -> + %% Drop garbage message... + dist_cntrlr_output_loop(DHandle, Socket) + + end. + +sock_send(Socket, Data) -> + try gen_tcp:send(Socket, Data) of + ok -> ok; + {error, Reason} -> death_row({send_error, Reason}) + catch + Type : Reason -> death_row({send_error, {Type, Reason}}) + end. + +death_row() -> + death_row(connection_closed). + +death_row(normal) -> + %% We do not want to exit with normal + %% exit reason since it wont bring down + %% linked processes... + death_row(); +death_row(Reason) -> + %% When the connection is on its way down operations + %% begin to fail. We catch the failures and call + %% this function waiting for termination. We should + %% be terminated by one of our links to the other + %% involved parties that began bringing the + %% connection down. By waiting for termination we + %% avoid altering the exit reason for the connection + %% teardown. We however limit the wait to 5 seconds + %% and bring down the connection ourselves if not + %% terminated... + receive after 5000 -> exit(Reason) end. diff --git a/lib/kernel/include/dist.hrl b/lib/kernel/include/dist.hrl index d6bccdf474..db4a5eaebc 100644 --- a/lib/kernel/include/dist.hrl +++ b/lib/kernel/include/dist.hrl @@ -40,3 +40,33 @@ -define(DFLAG_UTF8_ATOMS, 16#10000). -define(DFLAG_MAP_TAG, 16#20000). -define(DFLAG_BIG_CREATION, 16#40000). +-define(DFLAG_SEND_SENDER, 16#80000). + +%% DFLAGs that require strict ordering or:ed together... +-define(DFLAGS_STRICT_ORDER_DELIVERY, + ?DFLAG_DIST_HDR_ATOM_CACHE). + + +%% Also update dflag2str() in ../src/dist_util.erl +%% when adding flags... + +-define(DFLAGS_ALL, + (?DFLAG_PUBLISHED + bor ?DFLAG_ATOM_CACHE + bor ?DFLAG_EXTENDED_REFERENCES + bor ?DFLAG_DIST_MONITOR + bor ?DFLAG_FUN_TAGS + bor ?DFLAG_DIST_MONITOR_NAME + bor ?DFLAG_HIDDEN_ATOM_CACHE + bor ?DFLAG_NEW_FUN_TAGS + bor ?DFLAG_EXTENDED_PIDS_PORTS + bor ?DFLAG_EXPORT_PTR_TAG + bor ?DFLAG_BIT_BINARIES + bor ?DFLAG_NEW_FLOATS + bor ?DFLAG_UNICODE_IO + bor ?DFLAG_DIST_HDR_ATOM_CACHE + bor ?DFLAG_SMALL_ATOM_TAGS + bor ?DFLAG_UTF8_ATOMS + bor ?DFLAG_MAP_TAG + bor ?DFLAG_BIG_CREATION + bor ?DFLAG_SEND_SENDER)). diff --git a/lib/kernel/include/dist_util.hrl b/lib/kernel/include/dist_util.hrl index e3d2fe0eb6..eeb0f8dd43 100644 --- a/lib/kernel/include/dist_util.hrl +++ b/lib/kernel/include/dist_util.hrl @@ -29,9 +29,9 @@ -endif. -ifdef(dist_trace). --define(trace(Fmt,Args), io:format("~p ~p:~s",[erlang:timestamp(),node(),lists:flatten(io_lib:format(Fmt, Args))])). +-define(trace(Fmt,Args), io:format("~p ~p:~s",[erlang:convert_time_unit(erlang:monotonic_time()-erlang:system_info(start_time), native, microsecond),node(),lists:flatten(io_lib:format(Fmt, Args))])). % Use the one below for config-file (early boot) connection tracing -%-define(trace(Fmt,Args), erlang:display([erlang:now(),node(),lists:flatten(io_lib:format(Fmt, Args))])). +%-define(trace(Fmt,Args), erlang:display([erlang:convert_time_unit(erlang:monotonic_time()-erlang:system_info(start_time), native, microsecond),node(),lists:flatten(io_lib:format(Fmt, Args))])). -define(trace_factor,8). -else. -define(trace(Fmt,Args), ok). @@ -78,7 +78,13 @@ %% New in kernel-5.1 (OTP 19.1): mf_setopts, %% netkernel:setopts on active connection - mf_getopts %% netkernel:getopts on active connection + mf_getopts, %% netkernel:getopts on active connection + + %% New in kernel-6.0 (OTP 21.0) + f_handshake_complete, %% Notify handshake complete + add_flags, %% dflags to add + reject_flags, %% dflags not to use (not all can be rejected) + require_flags %% dflags that are required }). diff --git a/lib/kernel/src/dist_util.erl b/lib/kernel/src/dist_util.erl index b3507e5d13..08bd5946cd 100644 --- a/lib/kernel/src/dist_util.erl +++ b/lib/kernel/src/dist_util.erl @@ -74,6 +74,48 @@ ticked = 0 }). +dflag2str(?DFLAG_PUBLISHED) -> + "PUBLISHED"; +dflag2str(?DFLAG_ATOM_CACHE) -> + "ATOM_CACHE"; +dflag2str(?DFLAG_EXTENDED_REFERENCES) -> + "EXTENDED_REFERENCES"; +dflag2str(?DFLAG_DIST_MONITOR) -> + "DIST_MONITOR"; +dflag2str(?DFLAG_FUN_TAGS) -> + "FUN_TAGS"; +dflag2str(?DFLAG_DIST_MONITOR_NAME) -> + "DIST_MONITOR_NAME"; +dflag2str(?DFLAG_HIDDEN_ATOM_CACHE) -> + "HIDDEN_ATOM_CACHE"; +dflag2str(?DFLAG_NEW_FUN_TAGS) -> + "NEW_FUN_TAGS"; +dflag2str(?DFLAG_EXTENDED_PIDS_PORTS) -> + "EXTENDED_PIDS_PORTS"; +dflag2str(?DFLAG_EXPORT_PTR_TAG) -> + "EXPORT_PTR_TAG"; +dflag2str(?DFLAG_BIT_BINARIES) -> + "BIT_BINARIES"; +dflag2str(?DFLAG_NEW_FLOATS) -> + "NEW_FLOATS"; +dflag2str(?DFLAG_UNICODE_IO) -> + "UNICODE_IO"; +dflag2str(?DFLAG_DIST_HDR_ATOM_CACHE) -> + "DIST_HDR_ATOM_CACHE"; +dflag2str(?DFLAG_SMALL_ATOM_TAGS) -> + "SMALL_ATOM_TAGS"; +dflag2str(?DFLAG_UTF8_ATOMS) -> + "UTF8_ATOMS"; +dflag2str(?DFLAG_MAP_TAG) -> + "MAP_TAG"; +dflag2str(?DFLAG_BIG_CREATION) -> + "BIG_CREATION"; +dflag2str(?DFLAG_SEND_SENDER) -> + "SEND_SENDER"; +dflag2str(_) -> + "UNKNOWN". + + remove_flag(Flag, Flags) -> case Flags band Flag of 0 -> @@ -82,13 +124,13 @@ remove_flag(Flag, Flags) -> Flags - Flag end. -adjust_flags(ThisFlags, OtherFlags) -> +adjust_flags(ThisFlags, OtherFlags, RejectFlags) -> case (?DFLAG_PUBLISHED band ThisFlags) band OtherFlags of 0 -> {remove_flag(?DFLAG_PUBLISHED, ThisFlags), remove_flag(?DFLAG_PUBLISHED, OtherFlags)}; _ -> - {ThisFlags, OtherFlags} + {ThisFlags, OtherFlags band (bnot RejectFlags)} end. publish_flag(hidden, _) -> @@ -101,36 +143,71 @@ publish_flag(_, OtherNode) -> 0 end. -make_this_flags(RequestType, OtherNode) -> - publish_flag(RequestType, OtherNode) bor - %% The parenthesis below makes the compiler generate better code. - (?DFLAG_EXPORT_PTR_TAG bor - ?DFLAG_EXTENDED_PIDS_PORTS bor - ?DFLAG_EXTENDED_REFERENCES bor - ?DFLAG_DIST_MONITOR bor - ?DFLAG_FUN_TAGS bor - ?DFLAG_DIST_MONITOR_NAME bor - ?DFLAG_HIDDEN_ATOM_CACHE bor - ?DFLAG_NEW_FUN_TAGS bor - ?DFLAG_BIT_BINARIES bor - ?DFLAG_NEW_FLOATS bor - ?DFLAG_UNICODE_IO bor - ?DFLAG_DIST_HDR_ATOM_CACHE bor - ?DFLAG_SMALL_ATOM_TAGS bor - ?DFLAG_UTF8_ATOMS bor - ?DFLAG_MAP_TAG bor - ?DFLAG_BIG_CREATION). - -handshake_other_started(#hs_data{request_type=ReqType}=HSData0) -> +-define(DFLAGS_REMOVABLE, + (?DFLAG_DIST_HDR_ATOM_CACHE + bor ?DFLAG_HIDDEN_ATOM_CACHE + bor ?DFLAG_ATOM_CACHE)). + +-define(DFLAGS_ADDABLE, + (?DFLAGS_ALL + band (bnot (?DFLAG_PUBLISHED + bor ?DFLAG_HIDDEN_ATOM_CACHE + bor ?DFLAG_ATOM_CACHE)))). + +-define(DFLAGS_THIS_DEFAULT, + (?DFLAG_EXPORT_PTR_TAG + bor ?DFLAG_EXTENDED_PIDS_PORTS + bor ?DFLAG_EXTENDED_REFERENCES + bor ?DFLAG_DIST_MONITOR + bor ?DFLAG_FUN_TAGS + bor ?DFLAG_DIST_MONITOR_NAME + bor ?DFLAG_NEW_FUN_TAGS + bor ?DFLAG_BIT_BINARIES + bor ?DFLAG_NEW_FLOATS + bor ?DFLAG_UNICODE_IO + bor ?DFLAG_DIST_HDR_ATOM_CACHE + bor ?DFLAG_SMALL_ATOM_TAGS + bor ?DFLAG_UTF8_ATOMS + bor ?DFLAG_MAP_TAG + bor ?DFLAG_BIG_CREATION + bor ?DFLAG_SEND_SENDER)). + +make_this_flags(RequestType, AddFlags, RemoveFlags, OtherNode) -> + case RemoveFlags band (bnot ?DFLAGS_REMOVABLE) of + 0 -> ok; + Rerror -> exit({"Rejecting non rejectable flags", Rerror}) + end, + case AddFlags band (bnot ?DFLAGS_ADDABLE) of + 0 -> ok; + Aerror -> exit({"Adding non addable flags", Aerror}) + end, + Flgs0 = ?DFLAGS_THIS_DEFAULT, + Flgs1 = Flgs0 bor publish_flag(RequestType, OtherNode), + Flgs2 = Flgs1 bor AddFlags, + Flgs3 = Flgs2 band (bnot (?DFLAG_HIDDEN_ATOM_CACHE + bor ?DFLAG_ATOM_CACHE)), + Flgs3 band (bnot RemoveFlags). + +handshake_other_started(#hs_data{request_type=ReqType, + add_flags=AddFlgs0, + reject_flags=RejFlgs0, + require_flags=ReqFlgs0}=HSData0) -> + AddFlgs = convert_flags(AddFlgs0), + RejFlgs = convert_flags(RejFlgs0), + ReqFlgs = convert_flags(ReqFlgs0), {PreOtherFlags,Node,Version} = recv_name(HSData0), - PreThisFlags = make_this_flags(ReqType, Node), + PreThisFlags = make_this_flags(ReqType, AddFlgs, RejFlgs, Node), {ThisFlags, OtherFlags} = adjust_flags(PreThisFlags, - PreOtherFlags), + PreOtherFlags, + RejFlgs), HSData = HSData0#hs_data{this_flags=ThisFlags, other_flags=OtherFlags, other_version=Version, other_node=Node, - other_started=true}, + other_started=true, + add_flags=AddFlgs, + reject_flags=RejFlgs, + require_flags=ReqFlgs}, check_dflags(HSData), is_allowed(HSData), ?debug({"MD5 connection from ~p (V~p)~n", @@ -165,23 +242,18 @@ is_allowed(#hs_data{other_node = Node, end. %% -%% Check that both nodes can handle the same types of extended -%% node containers. If they can not, abort the connection. +%% Check mandatory flags... %% check_dflags(#hs_data{other_node = Node, other_flags = OtherFlags, - other_started = OtherStarted} = HSData) -> - - Mandatory = [{?DFLAG_EXTENDED_REFERENCES, "EXTENDED_REFERENCES"}, - {?DFLAG_EXTENDED_PIDS_PORTS, "EXTENDED_PIDS_PORTS"}, - {?DFLAG_UTF8_ATOMS, "UTF8_ATOMS"}], - Missing = lists:filtermap(fun({Bit, Str}) -> - case Bit band OtherFlags of - Bit -> false; - 0 -> {true, Str} - end - end, - Mandatory), + other_started = OtherStarted, + require_flags = RequiredFlags} = HSData) -> + Mandatory = ((?DFLAG_EXTENDED_REFERENCES + bor ?DFLAG_EXTENDED_PIDS_PORTS + bor ?DFLAG_UTF8_ATOMS) + bor RequiredFlags), + Missing = check_mandatory(0, ?DFLAGS_ALL, Mandatory, + OtherFlags, []), case Missing of [] -> ok; @@ -201,6 +273,22 @@ check_dflags(#hs_data{other_node = Node, ?shutdown2(Node, {check_dflags_failed, Missing}) end. +check_mandatory(_Bit, 0, _Mandatory, _OtherFlags, Missing) -> + Missing; +check_mandatory(Bit, Left, Mandatory, OtherFlags, Missing) -> + DFlag = (1 bsl Bit), + NewLeft = Left band (bnot DFlag), + NewMissing = case {DFlag band Mandatory, + DFlag band OtherFlags} of + {DFlag, 0} -> + %% Mandatory and missing... + [dflag2str(DFlag) | Missing]; + _ -> + %% Not mandatory or present... + Missing + end, + check_mandatory(Bit+1, NewLeft, Mandatory, OtherFlags, NewMissing). + %% No nodedown will be sent if we fail before this process has %% succeeded to mark the node as pending. @@ -314,13 +402,24 @@ flush_down() -> end. handshake_we_started(#hs_data{request_type=ReqType, - other_node=Node}=PreHSData) -> - PreThisFlags = make_this_flags(ReqType, Node), - HSData = PreHSData#hs_data{this_flags=PreThisFlags}, + other_node=Node, + add_flags=AddFlgs0, + reject_flags=RejFlgs0, + require_flags=ReqFlgs0}=PreHSData) -> + AddFlgs = convert_flags(AddFlgs0), + RejFlgs = convert_flags(RejFlgs0), + ReqFlgs = convert_flags(ReqFlgs0), + PreThisFlags = make_this_flags(ReqType, AddFlgs, RejFlgs, Node), + HSData = PreHSData#hs_data{this_flags = PreThisFlags, + add_flags = AddFlgs, + reject_flags = RejFlgs, + require_flags = ReqFlgs}, send_name(HSData), recv_status(HSData), {PreOtherFlags,ChallengeA} = recv_challenge(HSData), - {ThisFlags,OtherFlags} = adjust_flags(PreThisFlags, PreOtherFlags), + {ThisFlags,OtherFlags} = adjust_flags(PreThisFlags, + PreOtherFlags, + RejFlgs), NewHSData = HSData#hs_data{this_flags = ThisFlags, other_flags = OtherFlags, other_started = false}, @@ -336,15 +435,16 @@ handshake_we_started(#hs_data{request_type=ReqType, handshake_we_started(OldHsData) when element(1,OldHsData) =:= hs_data -> handshake_we_started(convert_old_hsdata(OldHsData)). -convert_old_hsdata({hs_data, KP, ON, TN, S, T, TF, A, OV, OF, OS, FS, FR, - FS_PRE, FS_POST, FG, FA, MFT, MFG, RT}) -> - #hs_data{ - kernel_pid = KP, other_node = ON, this_node = TN, socket = S, timer = T, - this_flags = TF, allowed = A, other_version = OV, other_flags = OF, - other_started = OS, f_send = FS, f_recv = FR, f_setopts_pre_nodeup = FS_PRE, - f_setopts_post_nodeup = FS_POST, f_getll = FG, f_address = FA, - mf_tick = MFT, mf_getstat = MFG, request_type = RT}. +convert_old_hsdata(OldHsData) -> + OHSDL = tuple_to_list(OldHsData), + NoMissing = tuple_size(#hs_data{}) - tuple_size(OldHsData), + true = NoMissing > 0, + list_to_tuple(OHSDL ++ lists:duplicate(NoMissing, undefined)). +convert_flags(Flags) when is_integer(Flags) -> + Flags; +convert_flags(_Undefined) -> + 0. %% -------------------------------------------------------------- %% The connection has been established. @@ -359,15 +459,20 @@ connection(#hs_data{other_node = Node, PType = publish_type(HSData#hs_data.other_flags), case FPreNodeup(Socket) of ok -> - do_setnode(HSData), % Succeeds or exits the process. + DHandle = do_setnode(HSData), % Succeeds or exits the process. Address = FAddress(Socket,Node), mark_nodeup(HSData,Address), case FPostNodeup(Socket) of ok -> + case HSData#hs_data.f_handshake_complete of + undefined -> ok; + HsComplete -> HsComplete(Socket, Node, DHandle) + end, con_loop({HSData#hs_data.kernel_pid, Node, Socket, PType, + DHandle, HSData#hs_data.mf_tick, HSData#hs_data.mf_getstat, HSData#hs_data.mf_setopts, @@ -425,18 +530,16 @@ do_setnode(#hs_data{other_node = Node, socket = Socket, [Node, Port, {publish_type(Flags), '(', Flags, ')', Version}]), - case (catch - erlang:setnode(Node, Port, - {Flags, Version, '', ''})) of - {'EXIT', {system_limit, _}} -> + try + erlang:setnode(Node, Port, {Flags, Version, '', ''}) + catch + error:system_limit -> error_msg("** Distribution system limit reached, " "no table space left for node ~w ** ~n", [Node]), ?shutdown(Node); - {'EXIT', Other} -> - exit(Other); - _Else -> - ok + error:Other -> + exit({Other, erlang:get_stacktrace()}) end; _ -> error_msg("** Distribution connection error, " @@ -468,7 +571,13 @@ mark_nodeup(#hs_data{kernel_pid = Kernel, ?shutdown(Node) end. -con_loop({Kernel, Node, Socket, Type, MFTick, MFGetstat, MFSetOpts, MFGetOpts}=ConData, +getstat(DHandle, _Socket, undefined) -> + erlang:dist_get_stat(DHandle); +getstat(_DHandle, Socket, MFGetstat) -> + MFGetstat(Socket). + +con_loop({Kernel, Node, Socket, Type, DHandle, MFTick, MFGetstat, + MFSetOpts, MFGetOpts}=ConData, Tick) -> receive {tcp_closed, Socket} -> @@ -476,7 +585,7 @@ con_loop({Kernel, Node, Socket, Type, MFTick, MFGetstat, MFSetOpts, MFGetOpts}=C {Kernel, disconnect} -> ?shutdown2(Node, disconnected); {Kernel, aux_tick} -> - case MFGetstat(Socket) of + case getstat(DHandle, Socket, MFGetstat) of {ok, _, _, PendWrite} -> send_tick(Socket, PendWrite, MFTick); _ -> @@ -484,7 +593,7 @@ con_loop({Kernel, Node, Socket, Type, MFTick, MFGetstat, MFSetOpts, MFGetOpts}=C end, con_loop(ConData, Tick); {Kernel, tick} -> - case send_tick(Socket, Tick, Type, + case send_tick(DHandle, Socket, Tick, Type, MFTick, MFGetstat) of {ok, NewTick} -> con_loop(ConData, NewTick); @@ -497,7 +606,7 @@ con_loop({Kernel, Node, Socket, Type, MFTick, MFGetstat, MFSetOpts, MFGetOpts}=C ?shutdown2(Node, send_net_tick_failed) end; {From, get_status} -> - case MFGetstat(Socket) of + case getstat(DHandle, Socket, MFGetstat) of {ok, Read, Write, _} -> From ! {self(), get_status, {ok, Read, Write}}, con_loop(ConData, Tick); @@ -735,14 +844,14 @@ send_status(#hs_data{socket = Socket, other_node = Node, %% we haven't read anything as a hidden node only ticks when it receives %% a TICK !! -send_tick(Socket, Tick, Type, MFTick, MFGetstat) -> +send_tick(DHandle, Socket, Tick, Type, MFTick, MFGetstat) -> #tick{tick = T0, read = Read, write = Write, ticked = Ticked} = Tick, T = T0 + 1, T1 = T rem 4, - case MFGetstat(Socket) of + case getstat(DHandle, Socket, MFGetstat) of {ok, Read, _, _} when Ticked =:= T -> {error, not_responding}; {ok, Read, W, Pend} when Type =:= hidden -> @@ -771,11 +880,10 @@ send_tick(Socket, Tick, Type, MFTick, MFGetstat) -> Error end. -send_tick(Socket, 0, MFTick) -> - MFTick(Socket); -send_tick(_, _Pend, _) -> - %% Dont send tick if pending write. - ok. +send_tick(_, Pend, _) when Pend /= false, Pend /= 0 -> + ok; %% Dont send tick if pending write. +send_tick(Socket, _Pend, MFTick) -> + MFTick(Socket). %% ------------------------------------------------------------ %% Connection setup timeout timer. diff --git a/lib/kernel/src/erl_boot_server.erl b/lib/kernel/src/erl_boot_server.erl index ac81cc9689..2a38266579 100644 --- a/lib/kernel/src/erl_boot_server.erl +++ b/lib/kernel/src/erl_boot_server.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% Copyright Ericsson AB 1996-2017. 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. @@ -253,9 +253,9 @@ handle_info({udp, U, IP, Port, Data}, S0) -> "~w is not a valid address ** ~n", [IP]), {noreply,S0}; {true,_,_} -> - case catch string:substr(Data, 1, length(?EBOOT_REQUEST)) of + case catch string:slice(Data, 0, length(?EBOOT_REQUEST)) of ?EBOOT_REQUEST -> - Vsn = string:substr(Data, length(?EBOOT_REQUEST)+1, length(Data)), + Vsn = string:slice(Data, length(?EBOOT_REQUEST), length(Data)), error_logger:error_msg("** Illegal boot server connection attempt: " "client version is ~s ** ~n", [Vsn]); _ -> diff --git a/lib/kernel/src/erl_epmd.erl b/lib/kernel/src/erl_epmd.erl index 7bc9e2ede3..f96bc88913 100644 --- a/lib/kernel/src/erl_epmd.erl +++ b/lib/kernel/src/erl_epmd.erl @@ -79,7 +79,13 @@ port_please(Node, EpmdAddr, Timeout) -> port_please1(Node,HostName, Timeout) -> - case inet:gethostbyname(HostName, inet, Timeout) of + Family = case inet_db:res_option(inet6) of + true -> + inet6; + false -> + inet + end, + case inet:gethostbyname(HostName, Family, Timeout) of {ok,{hostent, _Name, _ , _Af, _Size, [EpmdAddr | _]}} -> get_port(Node, EpmdAddr, Timeout); Else -> diff --git a/lib/kernel/src/erl_reply.erl b/lib/kernel/src/erl_reply.erl index e1e046cbb4..e1c4ffe839 100644 --- a/lib/kernel/src/erl_reply.erl +++ b/lib/kernel/src/erl_reply.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2016. All Rights Reserved. +%% Copyright Ericsson AB 1997-2017. 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. @@ -42,7 +42,7 @@ reply(_) -> %% convert ip number to tuple ip_string_to_tuple(Ip) -> - [Ip1,Ip2,Ip3,Ip4] = string:tokens(Ip,"."), + [Ip1,Ip2,Ip3,Ip4] = string:lexemes(Ip,"."), {list_to_integer(Ip1), list_to_integer(Ip2), list_to_integer(Ip3), diff --git a/lib/kernel/src/error_logger.erl b/lib/kernel/src/error_logger.erl index 9bf8547745..585507c545 100644 --- a/lib/kernel/src/error_logger.erl +++ b/lib/kernel/src/error_logger.erl @@ -504,7 +504,11 @@ string_p([]) -> string_p(Term) -> string_p1(Term). -string_p1([H|T]) when is_integer(H), H >= $\s, H < 255 -> +string_p1([H|T]) when is_integer(H), H >= $\040, H =< $\176 -> + string_p1(T); +string_p1([H|T]) when is_integer(H), H >= 16#A0, H < 16#D800; + is_integer(H), H > 16#DFFF, H < 16#FFFE; + is_integer(H), H > 16#FFFF, H =< 16#10FFFF -> string_p1(T); string_p1([$\n|T]) -> string_p1(T); string_p1([$\r|T]) -> string_p1(T); diff --git a/lib/kernel/src/erts_debug.erl b/lib/kernel/src/erts_debug.erl index ad92aafc2f..2887014c1c 100644 --- a/lib/kernel/src/erts_debug.erl +++ b/lib/kernel/src/erts_debug.erl @@ -33,10 +33,10 @@ -export([breakpoint/2, disassemble/1, display/1, dist_ext_to_term/2, dump_monitors/1, dump_links/1, flat_size/1, - get_internal_state/1, instructions/0, lock_counters/1, + get_internal_state/1, instructions/0, map_info/1, same/2, set_internal_state/2, - size_shared/1, copy_shared/1, dirty_cpu/2, dirty_io/2, - dirty/3]). + size_shared/1, copy_shared/1, dirty_cpu/2, dirty_io/2, dirty/3, + lcnt_control/1, lcnt_control/2, lcnt_collect/0, lcnt_clear/0]). -spec breakpoint(MFA, Flag) -> non_neg_integer() when MFA :: {Module :: module(), @@ -142,12 +142,31 @@ ic(F) when is_function(F) -> io:format("Total: ~w~n",[lists:sum([C||{_I,C}<-Is])]), R. --spec lock_counters(info) -> term(); - (clear) -> ok; - ({copy_save, boolean()}) -> boolean(); - ({process_locks, boolean()}) -> boolean(). +-spec lcnt_control + (copy_save, boolean()) -> ok; + (mask, list(atom())) -> ok. -lock_counters(_) -> +lcnt_control(_Option, _Value) -> + erlang:nif_error(undef). + +-spec lcnt_control + (copy_save) -> boolean(); + (mask) -> list(atom()). + +lcnt_control(_Option) -> + erlang:nif_error(undef). + +-type lcnt_lock_info() :: {atom(), term(), atom(), term()}. + +-spec lcnt_collect() -> + list({duration, {non_neg_integer(), non_neg_integer()}} | + {locks, list(lcnt_lock_info())}). + +lcnt_collect() -> + erlang:nif_error(undef). + +-spec lcnt_clear() -> ok. +lcnt_clear() -> erlang:nif_error(undef). -spec same(Term1, Term2) -> boolean() when @@ -359,16 +378,11 @@ df(Mod, Func, Arity) when is_atom(Mod), is_atom(Func) -> catch _:_ -> {undef,Mod} end. -dff(File, Fs) when is_pid(File), is_list(Fs) -> - lists:foreach(fun(Mfa) -> - disassemble_function(File, Mfa), - io:nl(File) - end, Fs); -dff(Name, Fs) when is_list(Name) -> - case file:open(Name, [write]) of +dff(Name, Fs) -> + case file:open(Name, [write,raw,delayed_write]) of {ok,F} -> try - dff(F, Fs) + dff_1(F, Fs) after _ = file:close(F) end; @@ -376,12 +390,18 @@ dff(Name, Fs) when is_list(Name) -> {error,{badopen,Reason}} end. +dff_1(File, Fs) -> + lists:foreach(fun(Mfa) -> + disassemble_function(File, Mfa), + file:write(File, "\n") + end, Fs). + disassemble_function(File, {_,_,_}=MFA) -> cont_dis(File, erts_debug:disassemble(MFA), MFA). cont_dis(_, false, _) -> ok; cont_dis(File, {Addr,Str,MFA}, MFA) -> - io:put_chars(File, binary_to_list(Str)), + ok = file:write(File, Str), cont_dis(File, erts_debug:disassemble(Addr), MFA); cont_dis(_, {_,_,_}, _) -> ok. diff --git a/lib/kernel/src/file_server.erl b/lib/kernel/src/file_server.erl index 6504174cbc..6e8f64d932 100644 --- a/lib/kernel/src/file_server.erl +++ b/lib/kernel/src/file_server.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2016. All Rights Reserved. +%% Copyright Ericsson AB 2000-2017. 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. @@ -207,7 +207,7 @@ handle_call(stop, _From, Handle) -> {stop, normal, stopped, Handle}; handle_call(Request, From, Handle) -> - error_logger:error_msg("handle_call(~p, ~p, _)", [Request, From]), + error_logger:error_msg("handle_call(~tp, ~tp, _)", [Request, From]), {noreply, Handle}. %%---------------------------------------------------------------------- @@ -220,7 +220,7 @@ handle_call(Request, From, Handle) -> -spec handle_cast(term(), state()) -> {'noreply', state()}. handle_cast(Msg, State) -> - error_logger:error_msg("handle_cast(~p, _)", [Msg]), + error_logger:error_msg("handle_cast(~tp, _)", [Msg]), {noreply, State}. %%---------------------------------------------------------------------- @@ -243,7 +243,7 @@ handle_info({'EXIT', Handle, _Reason}, Handle) -> {stop, normal, Handle}; handle_info(Info, State) -> - error_logger:error_msg("handle_Info(~p, _)", [Info]), + error_logger:error_msg("handle_Info(~tp, _)", [Info]), {noreply, State}. %%---------------------------------------------------------------------- diff --git a/lib/kernel/src/global.erl b/lib/kernel/src/global.erl index a9e92b28b8..a38522eb5c 100644 --- a/lib/kernel/src/global.erl +++ b/lib/kernel/src/global.erl @@ -262,7 +262,7 @@ check_dupname(Name, Pid) -> {ok, allow} -> true; _ -> - S = "global: ~w registered under several names: ~w\n", + S = "global: ~w registered under several names: ~tw\n", Names = [Name | [Name1 || {_Pid, Name1} <- PidNames]], error_logger:error_msg(S, [Pid, Names]), false @@ -659,7 +659,7 @@ handle_call(stop, _From, S) -> handle_call(Request, From, S) -> error_logger:warning_msg("The global_name_server " "received an unexpected message:\n" - "handle_call(~p, ~p, _)\n", + "handle_call(~tp, ~tp, _)\n", [Request, From]), {noreply, S}. @@ -828,7 +828,7 @@ handle_cast({async_del_lock, _ResourceId, _Pid}, S) -> handle_cast(Request, S) -> error_logger:warning_msg("The global_name_server " "received an unexpected message:\n" - "handle_cast(~p, _)\n", [Request]), + "handle_cast(~tp, _)\n", [Request]), {noreply, S}. %%======================================================================== @@ -955,7 +955,7 @@ handle_info({'DOWN', MonitorRef, process, _Pid, _Info}, S0) -> handle_info(Message, S) -> error_logger:warning_msg("The global_name_server " "received an unexpected message:\n" - "handle_info(~p, _)\n", [Message]), + "handle_info(~tp, _)\n", [Message]), {noreply, S}. @@ -1949,13 +1949,13 @@ exchange_names([{Name, Pid, Method} | Tail], Node, Ops, Res) -> exchange_names(Tail, Node, [Op | Ops], [Op | Res]); {badrpc, Badrpc} -> error_logger:info_msg("global: badrpc ~w received when " - "conflicting name ~w was found\n", + "conflicting name ~tw was found\n", [Badrpc, Name]), Op = {insert, {Name, Pid, Method}}, exchange_names(Tail, Node, [Op | Ops], Res); Else -> error_logger:info_msg("global: Resolve method ~w for " - "conflicting name ~w returned ~w\n", + "conflicting name ~tw returned ~tw\n", [Method, Name, Else]), Op = {delete, Name}, exchange_names(Tail, Node, [Op | Ops], [Op | Res]) @@ -1984,7 +1984,7 @@ minmax(P1,P2) -> Pid2 :: pid(). random_exit_name(Name, Pid, Pid2) -> {Min, Max} = minmax(Pid, Pid2), - error_logger:info_msg("global: Name conflict terminating ~w\n", + error_logger:info_msg("global: Name conflict terminating ~tw\n", [{Name, Max}]), exit(Max, kill), Min. @@ -2200,7 +2200,7 @@ unexpected_message({'EXIT', _Pid, _Reason}, _What) -> ok; unexpected_message(Message, What) -> error_logger:warning_msg("The global_name_server ~w process " - "received an unexpected message:\n~p\n", + "received an unexpected message:\n~tp\n", [What, Message]). %%% Utilities diff --git a/lib/kernel/src/group.erl b/lib/kernel/src/group.erl index bf785959ff..a5210901f2 100644 --- a/lib/kernel/src/group.erl +++ b/lib/kernel/src/group.erl @@ -793,9 +793,9 @@ search_up_stack(Stack, Substr) -> case up_stack(Stack) of {none,NewStack} -> {none,NewStack}; {L, NewStack} -> - case string:str(L, Substr) of - 0 -> search_up_stack(NewStack, Substr); - _ -> {string:strip(L,right,$\n), NewStack} + case string:find(L, Substr) of + nomatch -> search_up_stack(NewStack, Substr); + _ -> {string:trim(L, trailing, "$\n"), NewStack} end end. @@ -803,9 +803,9 @@ search_down_stack(Stack, Substr) -> case down_stack(Stack) of {none,NewStack} -> {none,NewStack}; {L, NewStack} -> - case string:str(L, Substr) of - 0 -> search_down_stack(NewStack, Substr); - _ -> {string:strip(L,right,$\n), NewStack} + case string:find(L, Substr) of + nomatch -> search_down_stack(NewStack, Substr); + _ -> {string:trim(L, trailing, "$\n"), NewStack} end end. diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl index 6aef5476f1..dc20c21c77 100644 --- a/lib/kernel/src/inet.erl +++ b/lib/kernel/src/inet.erl @@ -151,7 +151,8 @@ %%% --------------------------------- --spec get_rc() -> [{Par :: any(), Val :: any()}]. +-spec get_rc() -> [{Par :: atom(), Val :: any()} | + {Par :: atom(), Val1 :: any(), Val2 :: any()}]. get_rc() -> inet_db:get_rc(). diff --git a/lib/kernel/src/inet_config.erl b/lib/kernel/src/inet_config.erl index 4bbc520449..9f76360b8b 100644 --- a/lib/kernel/src/inet_config.erl +++ b/lib/kernel/src/inet_config.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2016. All Rights Reserved. +%% Copyright Ericsson AB 1997-2017. 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. @@ -369,7 +369,7 @@ win32_load1(Reg,Type,HFileKey) -> end. win32_split_line(Line,nt) -> inet_parse:split_line(Line); -win32_split_line(Line,windows) -> string:tokens(Line, ","). +win32_split_line(Line,windows) -> string:lexemes(Line, ","). win32_get_strings(Reg, Names) -> win32_get_strings(Reg, Names, []). diff --git a/lib/kernel/src/inet_dns.erl b/lib/kernel/src/inet_dns.erl index d5f982cc51..f1f58bc872 100644 --- a/lib/kernel/src/inet_dns.erl +++ b/lib/kernel/src/inet_dns.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2016. All Rights Reserved. +%% Copyright Ericsson AB 1997-2017. 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. @@ -29,7 +29,7 @@ -export([decode/1, encode/1]). --import(lists, [reverse/1, reverse/2, nthtail/2]). +-import(lists, [reverse/1]). -include("inet_int.hrl"). -include("inet_dns.hrl"). @@ -473,7 +473,7 @@ decode_data(<<Order:16,Preference:16,Data0/binary>>, _, ?S_NAPTR, Buffer) -> {Data2,Services} = decode_string(Data1), {Data,Regexp} = decode_characters(Data2, utf8), Replacement = decode_domain(Data, Buffer), - {Order,Preference,string:to_lower(Flags),string:to_lower(Services), + {Order,Preference,string:lowercase(Flags),string:lowercase(Services), Regexp,Replacement}; %% ?S_OPT falls through to default decode_data(Data, _, ?S_TXT, _) -> diff --git a/lib/kernel/src/inet_parse.erl b/lib/kernel/src/inet_parse.erl index 29804dc50b..e9685c6554 100644 --- a/lib/kernel/src/inet_parse.erl +++ b/lib/kernel/src/inet_parse.erl @@ -95,7 +95,7 @@ hosts(Fname,File) -> %% interface with a %if suffix. These kind of %% addresses maybe need to be gracefully handled %% throughout inet* and inet_drv. - case string:tokens(Address, "%") of + case string:lexemes(Address, "%") of [Addr,_] -> {ok,_} = address(Addr), skip; @@ -407,7 +407,7 @@ is_dom1([C | Cs]) when C >= $a, C =< $z -> is_dom_ldh(Cs); is_dom1([C | Cs]) when C >= $A, C =< $Z -> is_dom_ldh(Cs); is_dom1([C | Cs]) when C >= $0, C =< $9 -> case is_dom_ldh(Cs) of - true -> is_dom2(string:tokens([C | Cs],".")); + true -> is_dom2(string:lexemes([C | Cs],".")); false -> false end; is_dom1(_) -> false. diff --git a/lib/kernel/src/inet_res.erl b/lib/kernel/src/inet_res.erl index 90e49ddfdf..49aa5f8bda 100644 --- a/lib/kernel/src/inet_res.erl +++ b/lib/kernel/src/inet_res.erl @@ -859,15 +859,17 @@ query_ns(S0, Id, Buffer, IP, Port, Timer, Retry, I, {ok,S} -> Timeout = inet:timeout( (Tm * (1 bsl I)) div Retry, Timer), - {S, case query_udp( S, Id, Buffer, IP, Port, Timeout, Verbose) of {ok,#dns_rec{header=H}} when H#dns_header.tc -> TcpTimeout = inet:timeout(Tm*5, Timer), - query_tcp( - TcpTimeout, Id, Buffer, IP, Port, Verbose); - Reply -> Reply - end}; + {S, query_tcp( + TcpTimeout, Id, Buffer, IP, Port, Verbose)}; + {error, econnrefused} = Err -> + ok = udp_close(S), + {#sock{}, Err}; + Reply -> {S, Reply} + end; Error -> {S0,Error} end diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src index e150938487..b5e5f8eb73 100644 --- a/lib/kernel/src/kernel.app.src +++ b/lib/kernel/src/kernel.app.src @@ -120,6 +120,6 @@ {applications, []}, {env, [{error_logger, tty}]}, {mod, {kernel, []}}, - {runtime_dependencies, ["erts-9.0", "stdlib-3.0", "sasl-3.0"]} + {runtime_dependencies, ["erts-9.1.1", "stdlib-3.4.3", "sasl-3.0"]} ] }. diff --git a/lib/kernel/src/kernel.appup.src b/lib/kernel/src/kernel.appup.src index 77085b2064..fc5417597f 100644 --- a/lib/kernel/src/kernel.appup.src +++ b/lib/kernel/src/kernel.appup.src @@ -18,7 +18,7 @@ %% %CopyrightEnd% {"%VSN%", %% Up from - max one major revision back - [{<<"5\\.[0-2](\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-19.* + [{<<"5\\.3(\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-20.* %% Down to - max one major revision back - [{<<"5\\.[0-2](\\.[0-9]+)*">>,[restart_new_emulator]}] % OTP-19.* + [{<<"5\\.3(\\.[0-9]+)*">>,[restart_new_emulator]}] % OTP-20.* }. diff --git a/lib/kernel/src/net_kernel.erl b/lib/kernel/src/net_kernel.erl index ddda396713..f36b4f1e6a 100644 --- a/lib/kernel/src/net_kernel.erl +++ b/lib/kernel/src/net_kernel.erl @@ -423,8 +423,8 @@ handle_call({connect, Type, Node}, From, State) -> {ok, SetupPid} -> Owners = [{SetupPid, Node} | State#state.conn_owners], {noreply,State#state{conn_owners=Owners}}; - _ -> - ?connect_failure(Node, {setup_call, failed}), + _Error -> + ?connect_failure(Node, {setup_call, failed, _Error}), async_reply({reply, false, State}, From) end end; @@ -778,7 +778,7 @@ handle_info(transition_period_end, {noreply,State#state{tick = #tick{ticker = Tckr, time = T}}}; handle_info(X, State) -> - error_msg("Net kernel got ~w~n",[X]), + error_msg("Net kernel got ~tw~n",[X]), {noreply,State}. %% ----------------------------------------------------------- diff --git a/lib/kernel/src/os.erl b/lib/kernel/src/os.erl index 0250783632..209899d587 100644 --- a/lib/kernel/src/os.erl +++ b/lib/kernel/src/os.erl @@ -178,7 +178,7 @@ verify_executable(Name0, [Ext|Rest], OrigExtensions) -> end; verify_executable(Name, [], OrigExtensions) when OrigExtensions =/= [""] -> %% Windows %% Will only happen on windows, hence case insensitivity - case can_be_full_name(string:to_lower(Name),OrigExtensions) of + case can_be_full_name(string:lowercase(Name),OrigExtensions) of true -> verify_executable(Name,[""],[""]); _ -> diff --git a/lib/kernel/src/pg2.erl b/lib/kernel/src/pg2.erl index edf4aedde2..c4732f37ee 100644 --- a/lib/kernel/src/pg2.erl +++ b/lib/kernel/src/pg2.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2016. All Rights Reserved. +%% Copyright Ericsson AB 1997-2017. 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. @@ -199,7 +199,7 @@ handle_call({delete, Name}, _From, S) -> {reply, ok, S}; handle_call(Request, From, S) -> error_logger:warning_msg("The pg2 server received an unexpected message:\n" - "handle_call(~p, ~p, _)\n", + "handle_call(~tp, ~tp, _)\n", [Request, From]), {noreply, S}. diff --git a/lib/kernel/src/user_drv.erl b/lib/kernel/src/user_drv.erl index b794d4f45e..99ea4210bd 100644 --- a/lib/kernel/src/user_drv.erl +++ b/lib/kernel/src/user_drv.erl @@ -175,6 +175,18 @@ server_loop(Iport, Oport, Curr, User, Gr, {Resp, IOQ} = IOQueue) -> {Iport,eof} -> Curr ! {self(),eof}, server_loop(Iport, Oport, Curr, User, Gr, IOQueue); + + %% We always handle geometry and unicode requests + {Requester,tty_geometry} -> + Requester ! {self(),tty_geometry,get_tty_geometry(Iport)}, + server_loop(Iport, Oport, Curr, User, Gr, IOQueue); + {Requester,get_unicode_state} -> + Requester ! {self(),get_unicode_state,get_unicode_state(Iport)}, + server_loop(Iport, Oport, Curr, User, Gr, IOQueue); + {Requester,set_unicode_state, Bool} -> + Requester ! {self(),set_unicode_state,set_unicode_state(Iport,Bool)}, + server_loop(Iport, Oport, Curr, User, Gr, IOQueue); + Req when element(1,Req) =:= User orelse element(1,Req) =:= Curr, tuple_size(Req) =:= 2 orelse tuple_size(Req) =:= 3 -> %% We match {User|Curr,_}|{User|Curr,_,_} @@ -224,21 +236,16 @@ server_loop(Iport, Oport, Curr, User, Gr, {Resp, IOQ} = IOQueue) -> _ -> % not current, just remove it server_loop(Iport, Oport, Curr, User, gr_del_pid(Gr, Pid), IOQueue) end; + {Requester, {put_chars_sync, _, _, Reply}} -> + %% We need to ack the Req otherwise originating process will hang forever + %% Do discard the output to non visible shells (as was done previously) + Requester ! {reply, Reply}, + server_loop(Iport, Oport, Curr, User, Gr, IOQueue); _X -> - %% Ignore unknown messages. - server_loop(Iport, Oport, Curr, User, Gr, IOQueue) + %% Ignore unknown messages. + server_loop(Iport, Oport, Curr, User, Gr, IOQueue) end. -%% We always handle geometry and unicode requests -handle_req({Curr,tty_geometry},Iport,_Oport,IOQueue) -> - Curr ! {self(),tty_geometry,get_tty_geometry(Iport)}, - IOQueue; -handle_req({Curr,get_unicode_state},Iport,_Oport,IOQueue) -> - Curr ! {self(),get_unicode_state,get_unicode_state(Iport)}, - IOQueue; -handle_req({Curr,set_unicode_state, Bool},Iport,_Oport,IOQueue) -> - Curr ! {self(),set_unicode_state,set_unicode_state(Iport,Bool)}, - IOQueue; handle_req(next,Iport,Oport,{false,IOQ}=IOQueue) -> case queue:out(IOQ) of {empty,_} -> diff --git a/lib/kernel/test/Makefile b/lib/kernel/test/Makefile index b9942e899f..efe3a68531 100644 --- a/lib/kernel/test/Makefile +++ b/lib/kernel/test/Makefile @@ -148,8 +148,8 @@ release_tests_spec: make_emakefile $(INSTALL_DIR) "$(RELSYSDIR)" $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)" $(INSTALL_DATA) $(APP_FILES) "$(RELSYSDIR)" - $(INSTALL_DATA) kernel.spec kernel_smoke.spec $(EMAKEFILE)\ - $(COVERFILE) "$(RELSYSDIR)" + $(INSTALL_DATA) kernel.spec kernel_smoke.spec kernel_bench.spec \ + $(EMAKEFILE) $(COVERFILE) "$(RELSYSDIR)" chmod -R u+w "$(RELSYSDIR)" @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl index 6f8e949aac..612f77149d 100644 --- a/lib/kernel/test/code_SUITE.erl +++ b/lib/kernel/test/code_SUITE.erl @@ -35,6 +35,7 @@ purge_stacktrace/1, mult_lib_roots/1, bad_erl_libs/1, code_archive/1, code_archive2/1, on_load/1, on_load_binary/1, on_load_embedded/1, on_load_errors/1, on_load_update/1, + on_load_trace_on_load/1, on_load_purge/1, on_load_self_call/1, on_load_pending/1, on_load_deleted/1, big_boot_embedded/1, @@ -66,14 +67,16 @@ all() -> ext_mod_dep, clash, where_is_file, purge_stacktrace, mult_lib_roots, bad_erl_libs, code_archive, code_archive2, on_load, - on_load_binary, on_load_embedded, on_load_errors, on_load_update, + on_load_binary, on_load_embedded, on_load_errors, + {group, sequence}, on_load_purge, on_load_self_call, on_load_pending, on_load_deleted, module_status, big_boot_embedded, native_early_modules, get_mode, normalized_paths]. -groups() -> - []. +%% These need to run in order +groups() -> [{sequence, [sequence], [on_load_update, + on_load_trace_on_load]}]. init_per_group(_GroupName, Config) -> Config. @@ -1493,7 +1496,7 @@ do_on_load_error(ReturnValue) -> {undef,[{on_load_error,main,[],_}|_]} = Exit end. -on_load_update(_Config) -> +on_load_update(Config) -> {Mod,Code1} = on_load_update_code(1), {module,Mod} = code:load_binary(Mod, "", Code1), 42 = Mod:a(), @@ -1503,7 +1506,7 @@ on_load_update(_Config) -> {Mod,Code2} = on_load_update_code(2), {error,on_load_failure} = code:load_binary(Mod, "", Code2), 42 = Mod:a(), - 100 = Mod:b(99), + 78 = Mod:b(77), {'EXIT',{undef,_}} = (catch Mod:never()), 4 = erlang:trace_pattern({Mod,'_','_'}, false), @@ -1514,6 +1517,9 @@ on_load_update(_Config) -> {'EXIT',{undef,_}} = (catch Mod:b(10)), {'EXIT',{undef,_}} = (catch Mod:never()), + code:purge(Mod), + code:delete(Mod), + code:purge(Mod), ok. on_load_update_code(Version) -> @@ -1545,6 +1551,40 @@ on_load_update_code_1(3, Mod) -> "f() -> ok.\n", "c() -> 100.\n"]). +%% Test -on_load while trace feature 'on_load' is enabled (OTP-14612) +on_load_trace_on_load(Config) -> + Papa = self(), + Tracer = spawn_link(fun F() -> receive M -> Papa ! M end, F() end), + {tracer,[]} = erlang:trace_info(self(),tracer), + erlang:trace(self(), true, [call, {tracer, Tracer}]), + erlang:trace_pattern(on_load, true, []), + on_load_update(Config), + erlang:trace_pattern(on_load, false, []), + erlang:trace(self(), false, [call]), + + %% WE GET TRACES FOR CALLS TO UNDEFINED FUNCTIONS ??? + %% Remove filter when that is fixed. + Ms = lists:filter(fun({trace,Papa,call, + {error_handler,undefined_function, + [on_load_update_code,_,_]}}) + -> false; + (_) -> true + end, + flush()), + + [{trace, Papa, call, {on_load_update_code, a, []}}, + {trace, Papa, call, {on_load_update_code, b, [99]}}, + {trace, Papa, call, {on_load_update_code, c, []}}] = Ms, + + exit(Tracer, normal), + ok. + +flush() -> + receive M -> [M | flush()] + after 100 -> [] + end. + + on_load_purge(_Config) -> Mod = ?FUNCTION_NAME, register(Mod, self()), diff --git a/lib/kernel/test/file_name_SUITE.erl b/lib/kernel/test/file_name_SUITE.erl index 899102c908..f23529fec9 100644 --- a/lib/kernel/test/file_name_SUITE.erl +++ b/lib/kernel/test/file_name_SUITE.erl @@ -302,7 +302,9 @@ check_normal(Mod) -> {ok, BC} = Mod:read(FD,1024), ok = file:close(FD) end || {regular,Name,Content} <- NormalDir ], + {error, badarg} = Mod:rename("fil1\0tmp_fil2","tmp_fil1"), Mod:rename("fil1","tmp_fil1"), + {error, badarg} = Mod:read_file("tmp_fil1\0.txt"), {ok, <<"fil1">>} = Mod:read_file("tmp_fil1"), {error,enoent} = Mod:read_file("fil1"), Mod:rename("tmp_fil1","fil1"), diff --git a/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl index 929f66d400..331864b5de 100644 --- a/lib/kernel/test/gen_tcp_misc_SUITE.erl +++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl @@ -50,9 +50,8 @@ killing_acceptor/1,killing_multi_acceptors/1,killing_multi_acceptors2/1, several_accepts_in_one_go/1, accept_system_limit/1, active_once_closed/1, send_timeout/1, send_timeout_active/1, - otp_7731/1, zombie_sockets/1, otp_7816/1, otp_8102/1, - wrapping_oct/0, wrapping_oct/1, - otp_9389/1]). + otp_7731/1, zombie_sockets/1, otp_7816/1, otp_8102/1, + wrapping_oct/0, wrapping_oct/1, otp_9389/1, otp_13939/1]). %% Internal exports. -export([sender/3, not_owner/1, passive_sockets_server/2, priority_server/1, @@ -3014,3 +3013,42 @@ ok({ok,V}) -> V. get_hostname(Name) -> "@"++Host = lists:dropwhile(fun(C) -> C =/= $@ end, atom_to_list(Name)), Host. + +otp_13939(doc) -> + ["Check that writing to a remotely closed socket doesn't block forever " + "when exit_on_close is false."]; +otp_13939(suite) -> + []; +otp_13939(Config) when is_list(Config) -> + {Pid, Ref} = spawn_opt( + fun() -> + {ok, Listener} = gen_tcp:listen(0, [{exit_on_close, false}]), + {ok, Port} = inet:port(Listener), + + spawn_link( + fun() -> + {ok, Client} = gen_tcp:connect("localhost", Port, + [{active, false}]), + ok = gen_tcp:close(Client) + end), + + {ok, Accepted} = gen_tcp:accept(Listener), + + ok = gen_tcp:send(Accepted, <<0:(10*1024*1024*8)>>), + + %% The bug surfaces when there's a delay between the send + %% operations; inet:getstat is a red herring. + timer:sleep(100), + + {error, Code} = gen_tcp:send(Accepted, <<0:(10*1024*1024*8)>>), + ct:pal("gen_tcp:send returned ~p~n", [Code]) + end, [link, monitor]), + + receive + {'DOWN', Ref, process, Pid, normal} -> + ok + after 1000 -> + demonitor(Ref, [flush]), + exit(Pid, normal), + ct:fail("Server process blocked on send.") + end. diff --git a/lib/kernel/test/gen_udp_SUITE.erl b/lib/kernel/test/gen_udp_SUITE.erl index aa616d43d6..96e495505a 100644 --- a/lib/kernel/test/gen_udp_SUITE.erl +++ b/lib/kernel/test/gen_udp_SUITE.erl @@ -288,58 +288,56 @@ bad_address(Config) when is_list(Config) -> %% %% Starts a slave node that on command sends a bunch of messages %% to our UDP port. The receiving process just receives and -%% ignores the incoming messages, but counts them. -%% A tracing process traces the receiving process for -%% 'receive' and scheduling events. From the trace, -%% message contents is verified; and, how many messages -%% are received per in/out scheduling, which should be -%% the same as the read_packets parameter. -%% -%% What happens on the SMP emulator remains to be seen... -%% +%% ignores the incoming messages. +%% A tracing process traces the receiving port for +%% 'send' and scheduling events. From the trace, +%% how many messages are received per in/out scheduling, +%% which should never be more than the read_packet parameter. %% OTP-6249 UDP option for number of packet reads. read_packets(Config) when is_list(Config) -> - case erlang:system_info(smp_support) of - false -> - read_packets_1(); - true -> - %% We would need some new sort of tracing to test this - %% option reliably in an SMP emulator. - {skip,"SMP emulator"} - end. - -read_packets_1() -> N1 = 5, - N2 = 7, + N2 = 1, + Msgs = 30000, {ok,R} = gen_udp:open(0, [{read_packets,N1}]), {ok,RP} = inet:port(R), {ok,Node} = start_node(gen_udp_SUITE_read_packets), Die = make_ref(), - Loop = erlang:spawn_link(fun () -> infinite_loop(Die) end), %% - Msgs1 = [erlang:integer_to_list(M) || M <- lists:seq(1, N1*3)], - [V1|_] = read_packets_test(R, RP, Msgs1, Node), + {V1, Trace1} = read_packets_test(R, RP, Msgs, Node), {ok,[{read_packets,N1}]} = inet:getopts(R, [read_packets]), %% ok = inet:setopts(R, [{read_packets,N2}]), - Msgs2 = [erlang:integer_to_list(M) || M <- lists:seq(1, N2*3)], - [V2|_] = read_packets_test(R, RP, Msgs2, Node), + {V2, Trace2} = read_packets_test(R, RP, Msgs, Node), {ok,[{read_packets,N2}]} = inet:getopts(R, [read_packets]), %% stop_node(Node), - Mref = erlang:monitor(process, Loop), - Loop ! Die, - receive - {'DOWN',Mref,_,_, normal} -> - case {V1,V2} of - {N1,N2} -> - ok; - _ when V1 =/= N1, V2 =/= N2 -> - ok - end + ct:log("N1=~p, V1=~p vs N2=~p, V2=~p",[N1,V1,N2,V2]), + + dump_terms(Config, "trace1.terms", Trace2), + dump_terms(Config, "trace2.terms", Trace2), + + %% Because of the inherit racy-ness of the feature it is + %% hard to test that it behaves correctly. + %% Right now (OTP 21) a port task takes 5% of the + %% allotted port task reductions to execute, so + %% the max number of executions a port is allowed to + %% do before being re-scheduled is N * 20 + + if + V1 > (N1 * 20) -> + ct:fail("Got ~p msgs, max was ~p", [V1, N1]); + V2 > (N2 * 20) -> + ct:fail("Got ~p msgs, max was ~p", [V2, N2]); + true -> + ok end. +dump_terms(Config, Name, Terms) -> + FName = filename:join(proplists:get_value(priv_dir, Config),Name), + file:write_file(FName, term_to_binary(Terms)), + ct:log("Logged terms to ~s",[FName]). + infinite_loop(Die) -> receive Die -> @@ -350,7 +348,6 @@ infinite_loop(Die) -> end. read_packets_test(R, RP, Msgs, Node) -> - Len = length(Msgs), Receiver = self(), Tracer = spawn_link( @@ -375,24 +372,24 @@ read_packets_test(R, RP, Msgs, Node) -> [link,{priority,high}]), receive {Sender,{port,SP}} -> - erlang:trace(self(), true, - [running,'receive',{tracer,Tracer}]), + erlang:trace(R, true, + [running_ports,'send',{tracer,Tracer}]), erlang:yield(), Sender ! {Receiver,go}, - read_packets_recv(Len), - erlang:trace(self(), false, [all]), + read_packets_recv(Msgs), + erlang:trace(R, false, [all]), Tracer ! {Receiver,get_trace}, receive {Tracer,{trace,Trace}} -> - read_packets_verify(R, SP, Msgs, Trace) + {read_packets_verify(R, SP, Trace), Trace} end end. -read_packets_send(S, RP, [Msg|Msgs]) -> - ok = gen_udp:send(S, localhost, RP, Msg), - read_packets_send(S, RP, Msgs); -read_packets_send(_S, _RP, []) -> - ok. +read_packets_send(_S, _RP, 0) -> + ok; +read_packets_send(S, RP, Msgs) -> + ok = gen_udp:send(S, localhost, RP, "UDP FLOOOOOOD"), + read_packets_send(S, RP, Msgs - 1). read_packets_recv(0) -> ok; @@ -404,23 +401,24 @@ read_packets_recv(N) -> timeout end. -read_packets_verify(R, SP, Msg, Trace) -> - lists:reverse( - lists:sort(read_packets_verify(R, SP, Msg, Trace, 0))). - -read_packets_verify(R, SP, Msgs, [{trace,Self,OutIn,_}|Trace], M) - when Self =:= self(), OutIn =:= out; - Self =:= self(), OutIn =:= in -> - push(M, read_packets_verify(R, SP, Msgs, Trace, 0)); -read_packets_verify(R, SP, [Msg|Msgs], - [{trace,Self,'receive',{udp,R,{127,0,0,1},SP,Msg}} - |Trace], M) +read_packets_verify(R, SP, Trace) -> + [Max | _] = Pkts = lists:reverse(lists:sort(read_packets_verify(R, SP, Trace, 0))), + ct:pal("~p",[lists:sublist(Pkts,10)]), + Max. + +read_packets_verify(R, SP, [{trace,R,OutIn,_}|Trace], M) + when OutIn =:= out; OutIn =:= in -> + push(M, read_packets_verify(R, SP, Trace, 0)); +read_packets_verify(R, SP, [{trace, R,'receive',timeout}|Trace], M) -> + push(M, read_packets_verify(R, SP, Trace, 0)); +read_packets_verify(R, SP, + [{trace,R,'send',{udp,R,{127,0,0,1},SP,_Msg}, Self} | Trace], M) when Self =:= self() -> - read_packets_verify(R, SP, Msgs, Trace, M+1); -read_packets_verify(_R, _SP, [], [], M) -> + read_packets_verify(R, SP, Trace, M+1); +read_packets_verify(_R, _SP, [], M) -> push(M, []); -read_packets_verify(_R, _SP, Msgs, Trace, M) -> - ct:fail({read_packets_verify,mismatch,Msgs,Trace,M}). +read_packets_verify(_R, _SP, Trace, M) -> + ct:fail({read_packets_verify,mismatch,Trace,M}). push(0, Vs) -> Vs; diff --git a/lib/kernel/test/kernel_bench.spec b/lib/kernel/test/kernel_bench.spec new file mode 100644 index 0000000000..8de60dae31 --- /dev/null +++ b/lib/kernel/test/kernel_bench.spec @@ -0,0 +1 @@ +{groups,"../kernel_test",zlib_SUITE,[bench]}. diff --git a/lib/kernel/test/zlib_SUITE.erl b/lib/kernel/test/zlib_SUITE.erl index 4b67fce9a8..131a0685cd 100644 --- a/lib/kernel/test/zlib_SUITE.erl +++ b/lib/kernel/test/zlib_SUITE.erl @@ -21,60 +21,56 @@ -module(zlib_SUITE). -include_lib("common_test/include/ct.hrl"). - --compile(export_all). - --define(error(Format,Args), - put(test_server_loc,{?MODULE,?LINE}), - error(Format,Args,?MODULE,?LINE)). - -%% Learn erts team how to really write tests ;-) --define(m(ExpectedRes,Expr), - fun() -> - ACtual1 = (catch (Expr)), - try case ACtual1 of - ExpectedRes -> ACtual1 - end - catch - error:{case_clause,ACtuAl} -> - ?error("Not Matching Actual result was:~n ~p ~n", - [ACtuAl]), - ACtuAl - end - end()). - --define(BARG, {'EXIT',{badarg,[{zlib,_,_,_}|_]}}). --define(DATA_ERROR, {'EXIT',{data_error,[{zlib,_,_,_}|_]}}). - -init_per_testcase(_Func, Config) -> - Config. - -end_per_testcase(_Func, _Config) -> - ok. - -error(Format, Args, File, Line) -> - io:format("~p:~p: ERROR: " ++ Format, [File,Line|Args]), - group_leader() ! {failed, File, Line}. - -%% Hopefully I don't need this to get it to work with the testserver.. -%% Fail = #'REASON'{file = filename:basename(File), -%% line = Line, -%% desc = Args}, -%% case global:whereis_name(mnesia_test_case_sup) of -%% undefined -> -%% ignore; -%% Pid -> -%% Pid ! Fail -%% %% global:send(mnesia_test_case_sup, Fail), -%% end, -%% log("<>ERROR<>~n" ++ Format, Args, File, Line). +-include_lib("common_test/include/ct_event.hrl"). + +-export([suite/0, all/0, groups/0]). + +%% API group +-export([api_open_close/1]). +-export([api_deflateInit/1, api_deflateSetDictionary/1, api_deflateReset/1, + api_deflateParams/1, api_deflate/1, api_deflateEnd/1]). +-export([api_inflateInit/1, api_inflateReset/1, api_inflate2/1, api_inflate3/1, + api_inflateChunk/1, api_safeInflate/1, api_inflateEnd/1]). +-export([api_inflateSetDictionary/1, api_inflateGetDictionary/1]). +-export([api_crc32/1, api_adler32/1]). +-export([api_un_compress/1, api_un_zip/1, api_g_un_zip/1]). + +%% Examples group +-export([intro/1]). + +%% Usage group +-export([zip_usage/1, gz_usage/1, gz_usage2/1, compress_usage/1, + dictionary_usage/1, large_deflate/1, crc/1, adler/1, + only_allow_owner/1, sub_heap_binaries/1]). + +%% Bench group +-export([inflate_bench_zeroed/1, inflate_bench_rand/1, + deflate_bench_zeroed/1, deflate_bench_rand/1, + chunk_bench_zeroed/1, chunk_bench_rand/1]). + +%% Others +-export([smp/1, otp_9981/1, otp_7359/1]). + +-define(m(Guard, Expression), + fun() -> + Actual = (catch (Expression)), + case Actual of + Guard -> Actual; + _Other -> + ct:fail("Failed to match ~p, actual result was ~p", + [??Guard, Actual]) + end + end()). + +-define(EXIT(Reason), {'EXIT',{Reason,[{_,_,_,_}|_]}}). suite() -> [{ct_hooks,[ts_install_cth]}, {timetrap,{minutes,1}}]. all() -> - [{group, api}, {group, examples}, {group, func}, smp, + [{group, api}, {group, examples}, {group, func}, + {group, bench}, smp, otp_9981, otp_7359]. @@ -84,28 +80,19 @@ groups() -> api_deflateSetDictionary, api_deflateReset, api_deflateParams, api_deflate, api_deflateEnd, api_inflateInit, api_inflateSetDictionary, api_inflateGetDictionary, - api_inflateSync, api_inflateReset, api_inflate, api_inflateChunk, - api_inflateEnd, api_setBufsz, api_getBufsz, api_crc32, - api_adler32, api_getQSize, api_un_compress, api_un_zip, + api_inflateReset, api_inflate2, api_inflate3, api_inflateChunk, + api_safeInflate, api_inflateEnd, api_crc32, + api_adler32, api_un_compress, api_un_zip, api_g_un_zip]}, {examples, [], [intro]}, {func, [], [zip_usage, gz_usage, gz_usage2, compress_usage, - dictionary_usage, large_deflate, crc, adler]}]. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - + dictionary_usage, large_deflate, crc, adler, + only_allow_owner, sub_heap_binaries]}, + {bench, + [inflate_bench_zeroed, inflate_bench_rand, + deflate_bench_zeroed, deflate_bench_rand, + chunk_bench_zeroed, chunk_bench_rand]}]. %% Test open/0 and close/1. api_open_close(Config) when is_list(Config) -> @@ -113,7 +100,7 @@ api_open_close(Config) when is_list(Config) -> Fd2 = zlib:open(), ?m(false,Fd1 == Fd2), ?m(ok,zlib:close(Fd1)), - ?m(?BARG, zlib:close(Fd1)), + ?m(?EXIT(not_initialized), zlib:close(Fd1)), ?m(ok,zlib:close(Fd2)), %% Make sure that we don't get any EXIT messages if trap_exit is enabled. @@ -128,9 +115,11 @@ api_open_close(Config) when is_list(Config) -> %% Test deflateInit/2 and /6. api_deflateInit(Config) when is_list(Config) -> Z1 = zlib:open(), - ?m(?BARG, zlib:deflateInit(gurka, none)), - ?m(?BARG, zlib:deflateInit(gurka, gurka)), - ?m(?BARG, zlib:deflateInit(Z1, gurka)), + + ?m(?EXIT(badarg), zlib:deflateInit(gurka, none)), + + ?m(?EXIT(bad_compression_level), zlib:deflateInit(gurka, gurka)), + ?m(?EXIT(bad_compression_level), zlib:deflateInit(Z1, gurka)), Levels = [none, default, best_speed, best_compression] ++ lists:seq(0,9), lists:foreach(fun(Level) -> Z = zlib:open(), @@ -138,20 +127,30 @@ api_deflateInit(Config) when is_list(Config) -> ?m(ok,zlib:close(Z)) end, Levels), %% /6 - ?m(?BARG, zlib:deflateInit(Z1,gurka,deflated,-15,8,default)), - - ?m(?BARG, zlib:deflateInit(Z1,default,undefined,-15,8,default)), - - ?m(?BARG, zlib:deflateInit(Z1,default,deflated,48,8,default)), - ?m(?BARG, zlib:deflateInit(Z1,default,deflated,-20,8,default)), - ?m(?BARG, zlib:deflateInit(Z1,default,deflated,-7,8,default)), - ?m(?BARG, zlib:deflateInit(Z1,default,deflated,7,8,default)), - - ?m(?BARG, zlib:deflateInit(Z1,default,deflated,-15,0,default)), - ?m(?BARG, zlib:deflateInit(Z1,default,deflated,-15,10,default)), - - ?m(?BARG, zlib:deflateInit(Z1,default,deflated,-15,8,0)), - ?m(?BARG, zlib:deflateInit(Z1,default,deflated,-15,8,undefined)), + ?m(?EXIT(bad_compression_level), + zlib:deflateInit(Z1,gurka,deflated,-15,8,default)), + + ?m(?EXIT(bad_compression_method), + zlib:deflateInit(Z1,default,undefined,-15,8,default)), + + ?m(?EXIT(bad_compression_strategy), + zlib:deflateInit(Z1,default,deflated,-15,8,0)), + ?m(?EXIT(bad_compression_strategy), + zlib:deflateInit(Z1,default,deflated,-15,8,undefined)), + + ?m(?EXIT(bad_windowbits), + zlib:deflateInit(Z1,default,deflated,48,8,default)), + ?m(?EXIT(bad_windowbits), + zlib:deflateInit(Z1,default,deflated,-20,8,default)), + ?m(?EXIT(bad_windowbits), + zlib:deflateInit(Z1,default,deflated,-7,8,default)), + ?m(?EXIT(bad_windowbits), + zlib:deflateInit(Z1,default,deflated,7,8,default)), + + ?m(?EXIT(bad_memlevel), + zlib:deflateInit(Z1,default,deflated,-15,0,default)), + ?m(?EXIT(bad_memlevel), + zlib:deflateInit(Z1,default,deflated,-15,10,default)), lists:foreach(fun(Level) -> Z = zlib:open(), @@ -183,7 +182,11 @@ api_deflateInit(Config) when is_list(Config) -> ?m(ok,zlib:close(Z)) end, Strategies), ?m(ok, zlib:deflateInit(Z1,default,deflated,-15,8,default)), - ?m({'EXIT',_}, zlib:deflateInit(Z1,none,deflated,-15,8,default)), %% ?? + + %% Let it crash for any reason; we don't care about the order in which the + %% parameters are checked. + ?m(?EXIT(_), zlib:deflateInit(Z1,none,deflated,-15,8,default)), + ?m(ok, zlib:close(Z1)). %% Test deflateSetDictionary. @@ -192,17 +195,17 @@ api_deflateSetDictionary(Config) when is_list(Config) -> ?m(ok, zlib:deflateInit(Z1, default)), ?m(Id when is_integer(Id), zlib:deflateSetDictionary(Z1, <<1,1,2,3,4,5,1>>)), ?m(Id when is_integer(Id), zlib:deflateSetDictionary(Z1, [1,1,2,3,4,5,1])), - ?m(?BARG, zlib:deflateSetDictionary(Z1, gurka)), - ?m(?BARG, zlib:deflateSetDictionary(Z1, 128)), - ?m(_, zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, none)), - ?m({'EXIT',{stream_error,_}},zlib:deflateSetDictionary(Z1,<<1,1,2,3,4,5,1>>)), + ?m(?EXIT(badarg), zlib:deflateSetDictionary(Z1, gurka)), + ?m(?EXIT(badarg), zlib:deflateSetDictionary(Z1, 128)), + ?m(L when is_list(L), zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, none)), + ?m(?EXIT(stream_error), zlib:deflateSetDictionary(Z1,<<1,1,2,3,4,5,1>>)), ?m(ok, zlib:close(Z1)). %% Test deflateReset. api_deflateReset(Config) when is_list(Config) -> Z1 = zlib:open(), ?m(ok, zlib:deflateInit(Z1, default)), - ?m(_, zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, none)), + ?m(L when is_list(L), zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, none)), ?m(ok, zlib:deflateReset(Z1)), ?m(ok, zlib:deflateReset(Z1)), %% FIXME how do I make this go wrong?? @@ -212,9 +215,9 @@ api_deflateReset(Config) when is_list(Config) -> api_deflateParams(Config) when is_list(Config) -> Z1 = zlib:open(), ?m(ok, zlib:deflateInit(Z1, default)), - ?m(_, zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, none)), + ?m(L when is_list(L), zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, none)), ?m(ok, zlib:deflateParams(Z1, best_compression, huffman_only)), - ?m(_, zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, sync)), + ?m(L when is_list(L), zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, sync)), ?m(ok, zlib:close(Z1)). %% Test deflate. @@ -231,11 +234,13 @@ api_deflate(Config) when is_list(Config) -> ?m(B when is_list(B), zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, full)), ?m(B when is_list(B), zlib:deflate(Z1, <<>>, finish)), - ?m(?BARG, zlib:deflate(gurka, <<1,1,1,1,1,1,1,1,1>>, full)), - ?m(?BARG, zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, asdj)), - ?m(?BARG, zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, 198)), + ?m(?EXIT(badarg), zlib:deflate(gurka, <<1,1,1,1,1,1,1,1,1>>, full)), + + ?m(?EXIT(bad_flush_mode), zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, asdj)), + ?m(?EXIT(bad_flush_mode), zlib:deflate(Z1, <<1,1,1,1,1,1,1,1,1>>, 198)), + %% Causes problems ERROR REPORT - ?m(?BARG, zlib:deflate(Z1, [asdj,asd], none)), + ?m(?EXIT(badarg), zlib:deflate(Z1, [asdj,asd], none)), ?m(ok, zlib:close(Z1)). @@ -244,11 +249,11 @@ api_deflateEnd(Config) when is_list(Config) -> Z1 = zlib:open(), ?m(ok, zlib:deflateInit(Z1, default)), ?m(ok, zlib:deflateEnd(Z1)), - ?m({'EXIT', {einval,_}}, zlib:deflateEnd(Z1)), %% ?? - ?m(?BARG, zlib:deflateEnd(gurka)), + ?m(?EXIT(not_initialized), zlib:deflateEnd(Z1)), + ?m(?EXIT(badarg), zlib:deflateEnd(gurka)), ?m(ok, zlib:deflateInit(Z1, default)), ?m(B when is_list(B), zlib:deflate(Z1, <<"Kilroy was here">>)), - ?m({'EXIT', {data_error,_}}, zlib:deflateEnd(Z1)), + ?m(?EXIT(data_error), zlib:deflateEnd(Z1)), ?m(ok, zlib:deflateInit(Z1, default)), ?m(B when is_list(B), zlib:deflate(Z1, <<"Kilroy was here">>)), ?m(B when is_list(B), zlib:deflate(Z1, <<"Kilroy was here">>, finish)), @@ -259,9 +264,9 @@ api_deflateEnd(Config) when is_list(Config) -> %% Test inflateInit /1 and /2. api_inflateInit(Config) when is_list(Config) -> Z1 = zlib:open(), - ?m(?BARG, zlib:inflateInit(gurka)), + ?m(?EXIT(badarg), zlib:inflateInit(gurka)), ?m(ok, zlib:inflateInit(Z1)), - ?m({'EXIT',{einval,_}}, zlib:inflateInit(Z1, 15)), %% ?? + ?m(?EXIT(already_initialized), zlib:inflateInit(Z1, 15)), lists:foreach(fun(Wbits) -> Z11 = zlib:open(), ?m(ok, zlib:inflateInit(Z11,Wbits)), @@ -270,33 +275,34 @@ api_inflateInit(Config) when is_list(Config) -> ?m(ok,zlib:close(Z11)), ?m(ok,zlib:close(Z12)) end, lists:seq(8,15)), - ?m(?BARG, zlib:inflateInit(gurka, -15)), - ?m(?BARG, zlib:inflateInit(Z1, 7)), - ?m(?BARG, zlib:inflateInit(Z1, -7)), - ?m(?BARG, zlib:inflateInit(Z1, 48)), - ?m(?BARG, zlib:inflateInit(Z1, -16)), + ?m(?EXIT(badarg), zlib:inflateInit(gurka, -15)), + ?m(?EXIT(bad_windowbits), zlib:inflateInit(Z1, 7)), + ?m(?EXIT(bad_windowbits), zlib:inflateInit(Z1, -7)), + ?m(?EXIT(bad_windowbits), zlib:inflateInit(Z1, 48)), + ?m(?EXIT(bad_windowbits), zlib:inflateInit(Z1, -16)), ?m(ok, zlib:close(Z1)). %% Test inflateSetDictionary. api_inflateSetDictionary(Config) when is_list(Config) -> Z1 = zlib:open(), ?m(ok, zlib:inflateInit(Z1)), - ?m(?BARG, zlib:inflateSetDictionary(gurka,<<1,1,1,1,1>>)), - ?m(?BARG, zlib:inflateSetDictionary(Z1,102)), - ?m(?BARG, zlib:inflateSetDictionary(Z1,gurka)), + ?m(?EXIT(badarg), zlib:inflateSetDictionary(gurka,<<1,1,1,1,1>>)), + ?m(?EXIT(badarg), zlib:inflateSetDictionary(Z1,102)), + ?m(?EXIT(badarg), zlib:inflateSetDictionary(Z1,gurka)), Dict = <<1,1,1,1,1>>, - ?m({'EXIT',{stream_error,_}}, zlib:inflateSetDictionary(Z1,Dict)), + ?m(?EXIT(stream_error), zlib:inflateSetDictionary(Z1,Dict)), ?m(ok, zlib:close(Z1)). %% Test inflateGetDictionary. api_inflateGetDictionary(Config) when is_list(Config) -> Z1 = zlib:open(), + zlib:inflateInit(Z1), IsOperationSupported = case catch zlib:inflateGetDictionary(Z1) of - {'EXIT',{einval,_}} -> true; - {'EXIT',{enotsup,_}} -> false + ?EXIT(not_supported) -> false; + _ -> true end, - _ = zlib:close(Z1), + zlib:close(Z1), api_inflateGetDictionary_if_supported(IsOperationSupported). api_inflateGetDictionary_if_supported(false) -> @@ -306,64 +312,53 @@ api_inflateGetDictionary_if_supported(true) -> Z1 = zlib:open(), ?m(ok, zlib:deflateInit(Z1)), Dict = <<"foobar barfoo foo bar far boo">>, - ?m(_, zlib:deflateSetDictionary(Z1, Dict)), + Checksum = zlib:deflateSetDictionary(Z1, Dict), Payload = <<"foobarbarbar">>, Compressed = zlib:deflate(Z1, Payload, finish), ?m(ok, zlib:close(Z1)), - % Decompress and test dictionary extraction + % Decompress and test dictionary extraction with inflate/2 Z2 = zlib:open(), ?m(ok, zlib:inflateInit(Z2)), ?m(<<>>, iolist_to_binary(zlib:inflateGetDictionary(Z2))), - ?m({'EXIT',{stream_error,_}}, zlib:inflateSetDictionary(Z2, Dict)), - ?m({'EXIT',{{need_dictionary,_},_}}, zlib:inflate(Z2, Compressed)), + ?m(?EXIT(stream_error), zlib:inflateSetDictionary(Z2, Dict)), + ?m(?EXIT({need_dictionary,Checksum}), zlib:inflate(Z2, Compressed)), ?m(ok, zlib:inflateSetDictionary(Z2, Dict)), ?m(Dict, iolist_to_binary(zlib:inflateGetDictionary(Z2))), - ?m(Payload, iolist_to_binary(zlib:inflate(Z2, Compressed))), + Payload = iolist_to_binary(zlib:inflate(Z2, [])), ?m(ok, zlib:close(Z2)), - ?m(?BARG, zlib:inflateSetDictionary(Z2, Dict)), - ok. + ?m(?EXIT(not_initialized), zlib:inflateSetDictionary(Z2, Dict)), -%% Test inflateSync. -api_inflateSync(Config) when is_list(Config) -> - {skip,"inflateSync/1 sucks"}. -%% Z1 = zlib:open(), -%% ?m(ok, zlib:deflateInit(Z1)), -%% B1list0 = zlib:deflate(Z1, "gurkan gurra ger galna tunnor", full), -%% B2 = zlib:deflate(Z1, "grodan boll", finish), -%% io:format("~p\n", [B1list0]), -%% io:format("~p\n", [B2]), -%% ?m(ok, zlib:deflateEnd(Z1)), -%% B1 = clobber(14, list_to_binary(B1list0)), -%% Compressed = list_to_binary([B1,B2]), -%% io:format("~p\n", [Compressed]), - -%% ?m(ok, zlib:inflateInit(Z1)), -%% ?m(?BARG, zlib:inflateSync(gurka)), -%% ?m({'EXIT',{data_error,_}}, zlib:inflate(Z1, Compressed)), -%% ?m(ok, zlib:inflateSync(Z1)), -%% Ubs = zlib:inflate(Z1, []), -%% <<"grodan boll">> = list_to_binary(Ubs), -%% ?m(ok, zlib:close(Z1)). - -clobber(N, Bin) when is_binary(Bin) -> - T = list_to_tuple(binary_to_list(Bin)), - Byte = case element(N, T) of - 255 -> 254; - B -> B+1 - end, - list_to_binary(tuple_to_list(setelement(N, T, Byte))). + %% ... And do the same for inflate/3 + Z3 = zlib:open(), + ?m(ok, zlib:inflateInit(Z3)), + ?m(<<>>, iolist_to_binary(zlib:inflateGetDictionary(Z3))), + ?m(?EXIT(stream_error), zlib:inflateSetDictionary(Z3, Dict)), + + {need_dictionary, Checksum, _Output = []} = + zlib:inflate(Z3, Compressed, [{exception_on_need_dict, false}]), + + ?m(ok, zlib:inflateSetDictionary(Z3, Dict)), + ?m(Dict, iolist_to_binary(zlib:inflateGetDictionary(Z3))), + + Payload = iolist_to_binary( + zlib:inflate(Z3, [], [{exception_on_need_dict, false}])), + + ?m(ok, zlib:close(Z3)), + ?m(?EXIT(not_initialized), zlib:inflateSetDictionary(Z3, Dict)), + + ok. %% Test inflateReset. api_inflateReset(Config) when is_list(Config) -> Z1 = zlib:open(), ?m(ok, zlib:inflateInit(Z1)), - ?m(?BARG, zlib:inflateReset(gurka)), + ?m(?EXIT(badarg), zlib:inflateReset(gurka)), ?m(ok, zlib:inflateReset(Z1)), ?m(ok, zlib:close(Z1)). -%% Test inflate. -api_inflate(Config) when is_list(Config) -> +%% Test inflate/2 +api_inflate2(Config) when is_list(Config) -> Data = [<<1,2,2,3,3,3,4,4,4,4>>], Compressed = zlib:compress(Data), Z1 = zlib:open(), @@ -373,12 +368,32 @@ api_inflate(Config) when is_list(Config) -> ?m(ok, zlib:inflateEnd(Z1)), ?m(ok, zlib:inflateInit(Z1)), ?m(Data, zlib:inflate(Z1, Compressed)), - ?m(?BARG, zlib:inflate(gurka, Compressed)), - ?m(?BARG, zlib:inflate(Z1, 4384)), - ?m(?BARG, zlib:inflate(Z1, [atom_list])), + ?m(?EXIT(badarg), zlib:inflate(gurka, Compressed)), + ?m(?EXIT(badarg), zlib:inflate(Z1, 4384)), + ?m(?EXIT(badarg), zlib:inflate(Z1, [atom_list])), + ?m(ok, zlib:inflateEnd(Z1)), + ?m(ok, zlib:inflateInit(Z1)), + ?m(?EXIT(data_error), zlib:inflate(Z1, <<2,1,2,1,2>>)), + ?m(ok, zlib:close(Z1)). + +%% Test inflate/3; same as inflate/2 but with the default options inverted. +api_inflate3(Config) when is_list(Config) -> + Data = [<<1,2,2,3,3,3,4,4,4,4>>], + Options = [{exception_on_need_dict, false}], + Compressed = zlib:compress(Data), + Z1 = zlib:open(), + ?m(ok, zlib:inflateInit(Z1)), + ?m([], zlib:inflate(Z1, <<>>, Options)), + ?m(Data, zlib:inflate(Z1, Compressed)), + ?m(ok, zlib:inflateEnd(Z1)), + ?m(ok, zlib:inflateInit(Z1)), + ?m(Data, zlib:inflate(Z1, Compressed, Options)), + ?m(?EXIT(badarg), zlib:inflate(gurka, Compressed, Options)), + ?m(?EXIT(badarg), zlib:inflate(Z1, 4384, Options)), + ?m(?EXIT(badarg), zlib:inflate(Z1, [atom_list], Options)), ?m(ok, zlib:inflateEnd(Z1)), ?m(ok, zlib:inflateInit(Z1)), - ?m({'EXIT',{data_error,_}}, zlib:inflate(Z1, <<2,1,2,1,2>>)), + ?m(?EXIT(data_error), zlib:inflate(Z1, <<2,1,2,1,2>>, Options)), ?m(ok, zlib:close(Z1)). %% Test inflateChunk. @@ -388,69 +403,109 @@ api_inflateChunk(Config) when is_list(Config) -> Part1 = binary:part(Data, 0, ChunkSize), Part2 = binary:part(Data, ChunkSize, ChunkSize), Part3 = binary:part(Data, ChunkSize * 2, ChunkSize), + Compressed = zlib:compress(Data), Z1 = zlib:open(), + zlib:setBufSize(Z1, ChunkSize), + ?m(ok, zlib:inflateInit(Z1)), - ?m([], zlib:inflateChunk(Z1, <<>>)), - ?m({more, Part1}, zlib:inflateChunk(Z1, Compressed)), - ?m({more, Part2}, zlib:inflateChunk(Z1)), - ?m(Part3, zlib:inflateChunk(Z1)), - ?m(ok, zlib:inflateEnd(Z1)), + 0 = iolist_size(zlib:inflateChunk(Z1, <<>>)), + + {more, Part1AsIOList} = zlib:inflateChunk(Z1, Compressed), + {more, Part2AsIOList} = zlib:inflateChunk(Z1), + {more, Part3AsIOList} = zlib:inflateChunk(Z1), + + [] = zlib:inflateChunk(Z1), + [] = zlib:inflateChunk(Z1), + [] = zlib:inflateChunk(Z1), + + ?m(Part1, iolist_to_binary(Part1AsIOList)), + ?m(Part2, iolist_to_binary(Part2AsIOList)), + ?m(Part3, iolist_to_binary(Part3AsIOList)), + + ?m(ok, zlib:inflateEnd(Z1)), ?m(ok, zlib:inflateInit(Z1)), - ?m({more, Part1}, zlib:inflateChunk(Z1, Compressed)), + + ?m({more, Part1AsIOList}, zlib:inflateChunk(Z1, Compressed)), ?m(ok, zlib:inflateReset(Z1)), - zlib:setBufSize(Z1, size(Data)), - ?m(Data, zlib:inflateChunk(Z1, Compressed)), - ?m(ok, zlib:inflateEnd(Z1)), + zlib:setBufSize(Z1, byte_size(Data) + 1), + DataAsIOList = zlib:inflateChunk(Z1, Compressed), + ?m(Data, iolist_to_binary(DataAsIOList)), + + ?m(ok, zlib:inflateEnd(Z1)), ?m(ok, zlib:inflateInit(Z1)), - ?m(?BARG, zlib:inflateChunk(gurka, Compressed)), - ?m(?BARG, zlib:inflateChunk(Z1, 4384)), - ?m({'EXIT',{data_error,_}}, zlib:inflateEnd(Z1)), + + ?m(?EXIT(badarg), zlib:inflateChunk(gurka, Compressed)), + ?m(?EXIT(badarg), zlib:inflateChunk(Z1, 4384)), + + ?m(?EXIT(data_error), zlib:inflateEnd(Z1)), + ?m(ok, zlib:close(Z1)). -%% Test inflateEnd. -api_inflateEnd(Config) when is_list(Config) -> +%% Test safeInflate as a mirror of inflateChunk, but ignore the stuff about +%% exact chunk sizes. +api_safeInflate(Config) when is_list(Config) -> + Data = << <<(I rem 150)>> || I <- lists:seq(1, 20 bsl 10) >>, + Compressed = zlib:compress(Data), Z1 = zlib:open(), - ?m({'EXIT',{einval,_}}, zlib:inflateEnd(Z1)), - ?m(ok, zlib:inflateInit(Z1)), - ?m(?BARG, zlib:inflateEnd(gurka)), - ?m({'EXIT',{data_error,_}}, zlib:inflateEnd(Z1)), - ?m({'EXIT',{einval,_}}, zlib:inflateEnd(Z1)), + ?m(ok, zlib:inflateInit(Z1)), - ?m(B when is_list(B), zlib:inflate(Z1, zlib:compress("abc"))), + + SafeInflateLoop = + fun + Loop({continue, Chunk}, Output) -> + Loop(zlib:safeInflate(Z1, []), [Output, Chunk]); + Loop({finished, Chunk}, Output) -> + [Output, Chunk] + end, + + Decompressed = SafeInflateLoop(zlib:safeInflate(Z1, Compressed), []), + Data = iolist_to_binary(Decompressed), + ?m(ok, zlib:inflateEnd(Z1)), - ?m(ok, zlib:close(Z1)). + ?m(ok, zlib:inflateInit(Z1)), -%% Test getBufsz. -api_getBufsz(Config) when is_list(Config) -> - Z1 = zlib:open(), - ?m(Val when is_integer(Val), zlib:getBufSize(Z1)), - ?m(?BARG, zlib:getBufSize(gurka)), - ?m(ok, zlib:close(Z1)). + {continue, Partial} = zlib:safeInflate(Z1, Compressed), + PBin = iolist_to_binary(Partial), + PSize = byte_size(PBin), + <<PBin:PSize/binary, Rest/binary>> = Data, -%% Test setBufsz. -api_setBufsz(Config) when is_list(Config) -> - Z1 = zlib:open(), - ?m(?BARG, zlib:setBufSize(Z1, gurka)), - ?m(?BARG, zlib:setBufSize(gurka, 1232330)), - Sz = ?m( Val when is_integer(Val), zlib:getBufSize(Z1)), - ?m(ok, zlib:setBufSize(Z1, Sz*2)), - DSz = Sz*2, - ?m(DSz, zlib:getBufSize(Z1)), + ?m(ok, zlib:inflateReset(Z1)), + + {continue, Partial} = zlib:safeInflate(Z1, Compressed), + PBin = iolist_to_binary(Partial), + PSize = byte_size(PBin), + <<PBin:PSize/binary, Rest/binary>> = Data, + + ?m(ok, zlib:inflateReset(Z1)), + + SafeInflateLoop(zlib:safeInflate(Z1, Compressed), []), + + ?m({finished, []}, zlib:safeInflate(Z1, Compressed)), + ?m({finished, []}, zlib:safeInflate(Z1, Compressed)), + + ?m(ok, zlib:inflateReset(Z1)), + ?m(?EXIT(badarg), zlib:safeInflate(gurka, Compressed)), + ?m(?EXIT(badarg), zlib:safeInflate(Z1, 4384)), + ?m(?EXIT(data_error), zlib:inflateEnd(Z1)), ?m(ok, zlib:close(Z1)). -%%% Debug function ?? -%% Test getQSize. -api_getQSize(Config) when is_list(Config) -> +%% Test inflateEnd. +api_inflateEnd(Config) when is_list(Config) -> Z1 = zlib:open(), - Q = ?m(Val when is_integer(Val), zlib:getQSize(Z1)), - io:format("QSize ~p ~n", [Q]), - ?m(?BARG, zlib:getQSize(gurka)), + ?m(?EXIT(not_initialized), zlib:inflateEnd(Z1)), + ?m(ok, zlib:inflateInit(Z1)), + ?m(?EXIT(badarg), zlib:inflateEnd(gurka)), + ?m(?EXIT(data_error), zlib:inflateEnd(Z1)), + ?m(?EXIT(not_initialized), zlib:inflateEnd(Z1)), + ?m(ok, zlib:inflateInit(Z1)), + ?m(B when is_list(B), zlib:inflate(Z1, zlib:compress("abc"))), + ?m(ok, zlib:inflateEnd(Z1)), ?m(ok, zlib:close(Z1)). %% Test crc32. @@ -458,8 +513,8 @@ api_crc32(Config) when is_list(Config) -> Z1 = zlib:open(), ?m(ok, zlib:deflateInit(Z1,best_speed,deflated,-15,8,default)), Bin = <<1,1,1,1,1,1,1,1,1>>, - Compressed1 = ?m(_, zlib:deflate(Z1, Bin, none)), - Compressed2 = ?m(_, zlib:deflate(Z1, <<>>, finish)), + Compressed1 = ?m(L when is_list(L), zlib:deflate(Z1, Bin, none)), + Compressed2 = ?m(L when is_list(L), zlib:deflate(Z1, <<>>, finish)), Compressed = list_to_binary(Compressed1 ++ Compressed2), CRC1 = ?m( CRC1 when is_integer(CRC1), zlib:crc32(Z1)), ?m(CRC1 when is_integer(CRC1), zlib:crc32(Z1,Bin)), @@ -467,15 +522,15 @@ api_crc32(Config) when is_list(Config) -> ?m(CRC2 when is_integer(CRC2), zlib:crc32(Z1,Compressed)), CRC2 = ?m(CRC2 when is_integer(CRC2), zlib:crc32(Z1,0,Compressed)), ?m(CRC3 when CRC2 /= CRC3, zlib:crc32(Z1,234,Compressed)), - ?m(?BARG, zlib:crc32(gurka)), - ?m(?BARG, zlib:crc32(Z1, not_a_binary)), - ?m(?BARG, zlib:crc32(gurka, <<1,1,2,4,4>>)), - ?m(?BARG, zlib:crc32(Z1, 2298929, not_a_binary)), - ?m(?BARG, zlib:crc32(Z1, not_an_int, <<123,123,123,35,231>>)), - ?m(?BARG, zlib:crc32_combine(Z1, not_an_int, 123123, 123)), - ?m(?BARG, zlib:crc32_combine(Z1, noint, 123123, 123)), - ?m(?BARG, zlib:crc32_combine(Z1, 123123, noint, 123)), - ?m(?BARG, zlib:crc32_combine(Z1, 123123, 123, noint)), + ?m(?EXIT(badarg), zlib:crc32(gurka)), + ?m(?EXIT(badarg), zlib:crc32(Z1, not_a_binary)), + ?m(?EXIT(badarg), zlib:crc32(gurka, <<1,1,2,4,4>>)), + ?m(?EXIT(badarg), zlib:crc32(Z1, 2298929, not_a_binary)), + ?m(?EXIT(badarg), zlib:crc32(Z1, not_an_int, <<123,123,123,35,231>>)), + ?m(?EXIT(badarg), zlib:crc32_combine(Z1, not_an_int, 123123, 123)), + ?m(?EXIT(badarg), zlib:crc32_combine(Z1, noint, 123123, 123)), + ?m(?EXIT(badarg), zlib:crc32_combine(Z1, 123123, noint, 123)), + ?m(?EXIT(badarg), zlib:crc32_combine(Z1, 123123, 123, noint)), ?m(ok, zlib:deflateEnd(Z1)), ?m(ok, zlib:close(Z1)). @@ -484,74 +539,124 @@ api_adler32(Config) when is_list(Config) -> Z1 = zlib:open(), ?m(ok, zlib:deflateInit(Z1,best_speed,deflated,-15,8,default)), Bin = <<1,1,1,1,1,1,1,1,1>>, - Compressed1 = ?m(_, zlib:deflate(Z1, Bin, none)), - Compressed2 = ?m(_, zlib:deflate(Z1, <<>>, finish)), + Compressed1 = ?m(L when is_list(L), zlib:deflate(Z1, Bin, none)), + Compressed2 = ?m(L when is_list(L), zlib:deflate(Z1, <<>>, finish)), Compressed = list_to_binary(Compressed1 ++ Compressed2), ?m(ADLER1 when is_integer(ADLER1), zlib:adler32(Z1,Bin)), ?m(ADLER1 when is_integer(ADLER1), zlib:adler32(Z1,binary_to_list(Bin))), ADLER2 = ?m(ADLER2 when is_integer(ADLER2), zlib:adler32(Z1,Compressed)), ?m(ADLER2 when is_integer(ADLER2), zlib:adler32(Z1,1,Compressed)), ?m(ADLER3 when ADLER2 /= ADLER3, zlib:adler32(Z1,234,Compressed)), - ?m(?BARG, zlib:adler32(Z1, not_a_binary)), - ?m(?BARG, zlib:adler32(gurka, <<1,1,2,4,4>>)), - ?m(?BARG, zlib:adler32(Z1, 2298929, not_a_binary)), - ?m(?BARG, zlib:adler32(Z1, not_an_int, <<123,123,123,35,231>>)), - ?m(?BARG, zlib:adler32_combine(Z1, noint, 123123, 123)), - ?m(?BARG, zlib:adler32_combine(Z1, 123123, noint, 123)), - ?m(?BARG, zlib:adler32_combine(Z1, 123123, 123, noint)), + ?m(?EXIT(badarg), zlib:adler32(Z1, not_a_binary)), + ?m(?EXIT(badarg), zlib:adler32(gurka, <<1,1,2,4,4>>)), + ?m(?EXIT(badarg), zlib:adler32(Z1, 2298929, not_a_binary)), + ?m(?EXIT(badarg), zlib:adler32(Z1, not_an_int, <<123,123,123,35,231>>)), + ?m(?EXIT(badarg), zlib:adler32_combine(Z1, noint, 123123, 123)), + ?m(?EXIT(badarg), zlib:adler32_combine(Z1, 123123, noint, 123)), + ?m(?EXIT(badarg), zlib:adler32_combine(Z1, 123123, 123, noint)), ?m(ok, zlib:deflateEnd(Z1)), ?m(ok, zlib:close(Z1)). %% Test compress. api_un_compress(Config) when is_list(Config) -> - ?m(?BARG,zlib:compress(not_a_binary)), + ?m(?EXIT(badarg),zlib:compress(not_a_binary)), Bin = <<1,11,1,23,45>>, Comp = zlib:compress(Bin), - ?m(?BARG,zlib:uncompress(not_a_binary)), - ?m({'EXIT',{data_error,_}}, zlib:uncompress(<<171,171,171,171,171>>)), - ?m({'EXIT',{data_error,_}}, zlib:uncompress(<<>>)), - ?m({'EXIT',{data_error,_}}, zlib:uncompress(<<120>>)), - ?m({'EXIT',{data_error,_}}, zlib:uncompress(<<120,156>>)), - ?m({'EXIT',{data_error,_}}, zlib:uncompress(<<120,156,3>>)), - ?m({'EXIT',{data_error,_}}, zlib:uncompress(<<120,156,3,0>>)), - ?m({'EXIT',{data_error,_}}, zlib:uncompress(<<0,156,3,0,0,0,0,1>>)), + ?m(?EXIT(badarg),zlib:uncompress(not_a_binary)), + ?m(?EXIT(data_error), zlib:uncompress(<<171,171,171,171,171>>)), + ?m(?EXIT(data_error), zlib:uncompress(<<>>)), + ?m(?EXIT(data_error), zlib:uncompress(<<120>>)), + ?m(?EXIT(data_error), zlib:uncompress(<<120,156>>)), + ?m(?EXIT(data_error), zlib:uncompress(<<120,156,3>>)), + ?m(?EXIT(data_error), zlib:uncompress(<<120,156,3,0>>)), + ?m(?EXIT(data_error), zlib:uncompress(<<0,156,3,0,0,0,0,1>>)), ?m(Bin, zlib:uncompress(binary_to_list(Comp))), ?m(Bin, zlib:uncompress(Comp)). %% Test zip. api_un_zip(Config) when is_list(Config) -> - ?m(?BARG,zlib:zip(not_a_binary)), + ?m(?EXIT(badarg),zlib:zip(not_a_binary)), Bin = <<1,11,1,23,45>>, Comp = zlib:zip(Bin), ?m(Comp, zlib:zip(binary_to_list(Bin))), - ?m(?BARG,zlib:unzip(not_a_binary)), - ?m({'EXIT',{data_error,_}}, zlib:unzip(<<171,171,171,171,171>>)), - ?m({'EXIT',{data_error,_}}, zlib:unzip(<<>>)), + ?m(?EXIT(badarg),zlib:unzip(not_a_binary)), + ?m(?EXIT(data_error), zlib:unzip(<<171,171,171,171,171>>)), + ?m(?EXIT(data_error), zlib:unzip(<<>>)), ?m(Bin, zlib:unzip(Comp)), ?m(Bin, zlib:unzip(binary_to_list(Comp))), %% OTP-6396 - B = <<131,104,19,100,0,13,99,95,99,105,100,95,99,115,103,115,110,95,50,97,1,107,0,4,208,161,246,29,107,0,3,237,166,224,107,0,6,66,240,153,0,2,10,1,0,8,97,116,116,97,99,104,101,100,104,2,100,0,22,117,112,100,97,116,101,95,112,100,112,95,99,111,110,116,101,120,116,95,114,101,113,107,0,114,69,3,12,1,11,97,31,113,150,64,104,132,61,64,104,12,3,197,31,113,150,64,104,132,61,64,104,12,1,11,97,31,115,150,64,104,116,73,64,104,0,0,0,0,0,0,65,149,16,61,65,149,16,61,1,241,33,4,5,0,33,4,4,10,6,10,181,4,10,6,10,181,38,15,99,111,109,109,97,110,100,1,114,45,97,112,110,45,49,3,99,111,109,5,109,110,99,57,57,6,109,99,99,50,52,48,4,103,112,114,115,8,0,104,2,104,2,100,0,8,97,99,116,105,118,97,116,101,104,23,100,0,11,112,100,112,95,99,111,110,116,1,120,116,100,0,7,112,114,105,109,97,114,121,97,1,100,0,9,117,110,100,101,102,105,110,101,100,97,1,97,4,97,4,97,7,100,0,9,117,110,100,101,102,105,110,101,100,100,0,9,117,110,100,101,102,105,110,10100,100,0,9,117,110,100,101,102,105,110,101,100,100,0,5,102,97,108,115,101,100,0,9,117,110,100,101,102,105,110,101,100,100,0,9,117,110,100,101,102,105,110,101,100,100,0,9,117,110,100,101,102,105,1,101,100,97,0,100,0,9,117,110,100,101,102,105,110,101,100,107,0,4,16,0,1,144,107,0,4,61,139,186,181,107,0,4,10,8,201,49,100,0,9,117,110,100,101,102,105,110,101,100,100,0,9,117,110,100,101,102,105,0,101,100,100,0,9,117,110,100,101,102,105,110,101,100,104,2,104,3,98,0,0,7,214,97,11,97,20,104,3,97,17,97,16,97,21,106,108,0,0,0,3,104,2,97,1,104,2,104,3,98,0,0,7,214,97,11,97,20,104,3,97,17,97,167,20,104,2,97,4,104,2,104,3,98,0,0,7,214,97,11,97,20,104,3,97,17,97,16,97,21,104,2,97,10,104,2,104,3,98,0,0,7,214,97,11,97,20,104,3,97,17,97,16,97,26,106,100,0,5,118,101,114,57,57,100,0,9,117,110,0,101,102,105,110,101,100,107,0,2,0,244,107,0,4,10,6,102,195,107,0,4,10,6,102,195,100,0,9,117,110,100,101,102,105,110,101,100,100,0,9,117,110,100,101,102,105,110,101,100,107,0,125,248,143,0,203,25115,157,116,65,185,65,172,55,87,164,88,225,50,203,251,115,157,116,65,185,65,172,55,87,164,88,225,50,0,0,82,153,50,0,200,98,87,148,237,193,185,65,149,167,69,144,14,16,153,50,3,81,70,94,13,109,193,1,120,5,181,113,198,118,50,3,81,70,94,13,109,193,185,120,5,181,113,198,118,153,3,81,70,94,13,109,193,185,120,5,181,113,198,118,153,50,16,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,113,92,2,119,128,0,0,108,0,0,1,107,0,114,69,3,12,1,11,97,31,113,150,64,104,132,61,64,104,12,3,11,97,31,113,150,64,104,132,61,64,104,12,1,11,97,31,115,150,64,104,116,73,64,104,0,0,0,0,0,0,65,149,16,61,65,149,16,61,1,241,33,4,0,33,4,4,10,6,10,181,4,10,6,10,181,38,15,99,111,109,109,97,110,100,101,114,45,97,112,110,45,49,3,99,111,109,5,109,110,99,57,57,6,109,99,99,50,52,48,4,103,112,114,115,8,0,106>>, + B = + <<131,104,19,100,0,13,99,95,99,105,100,95,99,115,103,115,110,95,50,97, + 1,107,0,4,208,161,246,29,107,0,3,237,166,224,107,0,6,66,240,153,0,2, + 10,1,0,8,97,116,116,97,99,104,101,100,104,2,100,0,22,117,112,100,97, + 116,101,95,112,100,112,95,99,111,110,116,101,120,116,95,114,101,113, + 107,0,114,69,3,12,1,11,97,31,113,150,64,104,132,61,64,104,12,3,197, + 31,113,150,64,104,132,61,64,104,12,1,11,97,31,115,150,64,104,116,73, + 64,104,0,0,0,0,0,0,65,149,16,61,65,149,16,61,1,241,33,4,5,0,33,4,4,10 + ,6,10,181,4,10,6,10,181,38,15,99,111,109,109,97,110,100,1,114,45,97, + 112,110,45,49,3,99,111,109,5,109,110,99,57,57,6,109,99,99,50,52,48,4, + 103,112,114,115,8,0,104,2,104,2,100,0,8,97,99,116,105,118,97,116,101, + 104,23,100,0,11,112,100,112,95,99,111,110,116,1,120,116,100,0,7,112, + 114,105,109,97,114,121,97,1,100,0,9,117,110,100,101,102,105,110,101, + 100,97,1,97,4,97,4,97,7,100,0,9,117,110,100,101,102,105,110,101,100, + 100,0,9,117,110,100,101,102,105,110,10100,100,0,9,117,110,100,101, + 102,105,110,101,100,100,0,5,102,97,108,115,101,100,0,9,117,110,100, + 101,102,105,110,101,100,100,0,9,117,110,100,101,102,105,110,101,100, + 100,0,9,117,110,100,101,102,105,1,101,100,97,0,100,0,9,117,110,100, + 101,102,105,110,101,100,107,0,4,16,0,1,144,107,0,4,61,139,186,181, + 107,0,4,10,8,201,49,100,0,9,117,110,100,101,102,105,110,101,100,100, + 0,9,117,110,100,101,102,105,0,101,100,100,0,9,117,110,100,101,102, + 105,110,101,100,104,2,104,3,98,0,0,7,214,97,11,97,20,104,3,97,17,97, + 16,97,21,106,108,0,0,0,3,104,2,97,1,104,2,104,3,98,0,0,7,214,97,11, + 97,20,104,3,97,17,97,167,20,104,2,97,4,104,2,104,3,98,0,0,7,214,97, + 11,97,20,104,3,97,17,97,16,97,21,104,2,97,10,104,2,104,3,98,0,0,7, + 214,97,11,97,20,104,3,97,17,97,16,97,26,106,100,0,5,118,101,114,57, + 57,100,0,9,117,110,0,101,102,105,110,101,100,107,0,2,0,244,107,0,4, + 10,6,102,195,107,0,4,10,6,102,195,100,0,9,117,110,100,101,102,105, + 110,101,100,100,0,9,117,110,100,101,102,105,110,101,100,107,0,125, + 248,143,0,203,25115,157,116,65,185,65,172,55,87,164,88,225,50,203, + 251,115,157,116,65,185,65,172,55,87,164,88,225,50,0,0,82,153,50,0, + 200,98,87,148,237,193,185,65,149,167,69,144,14,16,153,50,3,81,70,94, + 13,109,193,1,120,5,181,113,198,118,50,3,81,70,94,13,109,193,185,120, + 5,181,113,198,118,153,3,81,70,94,13,109,193,185,120,5,181,113,198, + 118,153,50,16,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,113,92,2,119,128,0,0, + 108,0,0,1,107,0,114,69,3,12,1,11,97,31,113,150,64,104,132,61,64,104, + 12,3,11,97,31,113,150,64,104,132,61,64,104,12,1,11,97,31,115,150,64, + 104,116,73,64,104,0,0,0,0,0,0,65,149,16,61,65,149,16,61,1,241,33,4,0, + 33,4,4,10,6,10,181,4,10,6,10,181,38,15,99,111,109,109,97,110,100,101, + 114,45,97,112,110,45,49,3,99,111,109,5,109,110,99,57,57,6,109,99,99, + 50,52,48,4,103,112,114,115,8,0,106>>, + Z = zlib:zip(B), ?m(B, zlib:unzip(Z)). %% Test gunzip. api_g_un_zip(Config) when is_list(Config) -> - ?m(?BARG,zlib:gzip(not_a_binary)), + ?m(?EXIT(badarg),zlib:gzip(not_a_binary)), Bin = <<1,11,1,23,45>>, Comp = zlib:gzip(Bin), + ?m(Comp, zlib:gzip(binary_to_list(Bin))), - ?m(?BARG, zlib:gunzip(not_a_binary)), - ?m(?DATA_ERROR, zlib:gunzip(<<171,171,171,171,171>>)), - ?m(?DATA_ERROR, zlib:gunzip(<<>>)), + ?m(?EXIT(badarg), zlib:gunzip(not_a_binary)), + ?m(?EXIT(data_error), zlib:gunzip(<<171,171,171,171,171>>)), + ?m(?EXIT(data_error), zlib:gunzip(<<>>)), ?m(Bin, zlib:gunzip(Comp)), ?m(Bin, zlib:gunzip(binary_to_list(Comp))), + %% RFC 1952: + %% + %% "A gzip file consists of a series of "members" (compressed data + %% sets). [...] The members simply appear one after another in the file, + %% with no additional information before, between, or after them." + Concatenated = <<Bin/binary, Bin/binary>>, + ?m(Concatenated, zlib:gunzip([Comp, Comp])), + %% Bad CRC; bad length. BadCrc = bad_crc_data(), - ?m({'EXIT',{data_error,_}},(catch zlib:gunzip(BadCrc))), + ?m(?EXIT(data_error),(catch zlib:gunzip(BadCrc))), BadLen = bad_len_data(), - ?m({'EXIT',{data_error,_}},(catch zlib:gunzip(BadLen))), + ?m(?EXIT(data_error),(catch zlib:gunzip(BadLen))), ok. bad_crc_data() -> @@ -594,30 +699,15 @@ intro(Config) when is_list(Config) -> large_deflate(Config) when is_list(Config) -> large_deflate_do(). large_deflate_do() -> - Z = zlib:open(), - Plain = rand_bytes(zlib:getBufSize(Z)*5), - ok = zlib:deflateInit(Z), - _ZlibHeader = zlib:deflate(Z, [], full), - Deflated = zlib:deflate(Z, Plain, full), - ?m(ok, zlib:close(Z)), - ?m(Plain, zlib:unzip(list_to_binary([Deflated, 3, 0]))). - -rand_bytes(Sz) -> - L = <<8,2,3,6,1,2,3,2,3,4,8,7,3,7,2,3,4,7,5,8,9,3>>, - rand_bytes(erlang:md5(L),Sz). - -rand_bytes(Bin, Sz) when byte_size(Bin) >= Sz -> - <<Res:Sz/binary, _/binary>> = Bin, - Res; -rand_bytes(Bin, Sz) -> - rand_bytes(<<(erlang:md5(Bin))/binary, Bin/binary>>, Sz). - + Plain = gen_determ_rand_bytes(64 bsl 10), + Deflated = zlib:zip(Plain), + ?m(Plain, zlib:unzip(Deflated)). %% Test a standard compressed zip file. zip_usage(Config) when is_list(Config) -> zip_usage(zip_usage({get_arg,Config})); zip_usage({get_arg,Config}) -> - Out = conf(data_dir,Config), + Out = get_data_dir(Config), {ok,ZIP} = file:read_file(filename:join(Out,"zipdoc.zip")), {ok,ORIG} = file:read_file(filename:join(Out,"zipdoc")), {run,ZIP,ORIG}; @@ -688,7 +778,7 @@ zip_usage({run,ZIP,ORIG}) -> gz_usage(Config) when is_list(Config) -> gz_usage(gz_usage({get_arg,Config})); gz_usage({get_arg,Config}) -> - Out = conf(data_dir,Config), + Out = get_data_dir(Config), {ok,GZIP} = file:read_file(filename:join(Out,"zipdoc.1.gz")), {ok,ORIG} = file:read_file(filename:join(Out,"zipdoc")), {ok,GZIP2} = file:read_file(filename:join(Out,"zipdoc.txt.gz")), @@ -709,7 +799,7 @@ gz_usage2(Config) -> case os:find_executable("gzip") of Name when is_list(Name) -> Z = zlib:open(), - Out = conf(data_dir,Config), + Out = get_data_dir(Config), {ok,ORIG} = file:read_file(filename:join(Out,"zipdoc")), Compressed = zlib:gzip(ORIG), GzOutFile = filename:join(Out,"out.gz"), @@ -737,7 +827,7 @@ gz_usage2(Config) -> compress_usage(Config) when is_list(Config) -> compress_usage(compress_usage({get_arg,Config})); compress_usage({get_arg,Config}) -> - Out = conf(data_dir,Config), + Out = get_data_dir(Config), {ok,C1} = file:read_file(filename:join(Out,"png-compressed.zlib")), {run,C1}; compress_usage({run,C1}) -> @@ -792,7 +882,7 @@ compress_usage({run,C1}) -> crc(Config) when is_list(Config) -> crc(crc({get_arg,Config})); crc({get_arg,Config}) -> - Out = conf(data_dir,Config), + Out = get_data_dir(Config), {ok,C1} = file:read_file(filename:join(Out,"zipdoc")), {run,C1}; crc({run,C1}) -> @@ -821,7 +911,7 @@ crc({run,C1}) -> adler(Config) when is_list(Config) -> adler(adler({get_arg,Config})); adler({get_arg,Config}) -> - Out = conf(data_dir,Config), + Out = get_data_dir(Config), File1 = filename:join(Out,"zipdoc"), {ok,C1} = file:read_file(File1), {run,C1}; @@ -869,10 +959,14 @@ dictionary_usage({run}) -> %% Now uncompress. Z2 = zlib:open(), ?m(ok, zlib:inflateInit(Z2)), - {'EXIT',{{need_dictionary,DictID},_}} = (catch zlib:inflate(Z2, Compressed)), + + ?m(?EXIT({need_dictionary, DictID}), zlib:inflate(Z2, Compressed)), + ?m(ok, zlib:inflateSetDictionary(Z2, Dict)), ?m(ok, zlib:inflateSetDictionary(Z2, binary_to_list(Dict))), + Uncompressed = ?m(B when is_list(B), zlib:inflate(Z2, [])), + ?m(ok, zlib:inflateEnd(Z2)), ?m(ok, zlib:close(Z2)), ?m(Data, list_to_binary(Uncompressed)). @@ -882,33 +976,59 @@ split_bin(<<Part:1997/binary,Rest/binary>>, Acc) -> split_bin(Last,Acc) -> lists:reverse([Last|Acc]). +only_allow_owner(Config) when is_list(Config) -> + Z = zlib:open(), + + ?m(ok, zlib:inflateInit(Z)), + ?m(ok, zlib:inflateReset(Z)), + + {Pid, Ref} = spawn_monitor( + fun() -> + ?m(?EXIT(not_on_controlling_process), zlib:inflateReset(Z)) + end), + + receive + {'DOWN', Ref, process, Pid, _Reason} -> + ok + after 200 -> + ct:fail("Spawned worker timed out.") + end, + + ?m(ok, zlib:inflateReset(Z)). + +sub_heap_binaries(Config) when is_list(Config) -> + Compressed = zlib:compress(<<"gurka">>), + ConfLen = erlang:length(Config), + + HeapBin = <<ConfLen:8/integer, Compressed/binary>>, + <<_:8/integer, SubHeapBin/binary>> = HeapBin, + + ?m(<<"gurka">>, zlib:uncompress(SubHeapBin)), + ok. %% Check concurrent access to zlib driver. smp(Config) -> - case erlang:system_info(smp_support) of - true -> - NumOfProcs = lists:min([8,erlang:system_info(schedulers)]), - io:format("smp starting ~p workers\n",[NumOfProcs]), - - %% Tests to run in parallel. - Funcs = [zip_usage, gz_usage, compress_usage, dictionary_usage, - crc, adler], - - %% We get all function arguments here to avoid repeated parallel - %% file read access. - FnAList = lists:map(fun(F) -> {F,?MODULE:F({get_arg,Config})} - end, Funcs), - - Pids = [spawn_link(?MODULE, worker, [rand:uniform(9999), - list_to_tuple(FnAList), - self()]) - || _ <- lists:seq(1,NumOfProcs)], - wait_pids(Pids); - - false -> - {skipped,"No smp support"} - end. + NumOfProcs = lists:min([8,erlang:system_info(schedulers)]), + io:format("smp starting ~p workers\n",[NumOfProcs]), + + %% Tests to run in parallel. + Funcs = + [zip_usage, gz_usage, compress_usage, dictionary_usage, + crc, adler], + + %% We get all function arguments here to avoid repeated parallel + %% file read access. + UsageArgs = + list_to_tuple([{F, ?MODULE:F({get_arg,Config})} || F <- Funcs]), + Parent = self(), + + WorkerFun = + fun() -> + worker(rand:uniform(9999), UsageArgs, Parent) + end, + Pids = [spawn_link(WorkerFun) || _ <- lists:seq(1, NumOfProcs)], + wait_pids(Pids). worker(Seed, FnATpl, Parent) -> io:format("smp worker ~p, seed=~p~n",[self(),Seed]), @@ -999,43 +1119,98 @@ otp_9981(Config) when is_list(Config) -> Ports = lists:sort(erlang:ports()), ok. +-define(BENCH_SIZE, (16 bsl 20)). + +-define(DECOMPRESS_BENCH(Name, What, Data), + Name(Config) when is_list(Config) -> + Uncompressed = Data, + Compressed = zlib:compress(Uncompressed), + What(Compressed, byte_size(Uncompressed))). + +-define(COMPRESS_BENCH(Name, What, Data), + Name(Config) when is_list(Config) -> + Compressed = Data, + What(Compressed, byte_size(Compressed))). + +?DECOMPRESS_BENCH(inflate_bench_zeroed, throughput_bench_inflate, + <<0:(8 * ?BENCH_SIZE)>>). +?DECOMPRESS_BENCH(inflate_bench_rand, throughput_bench_inflate, + gen_determ_rand_bytes(?BENCH_SIZE)). +?DECOMPRESS_BENCH(chunk_bench_zeroed, throughput_bench_chunk, + <<0:(8 * ?BENCH_SIZE)>>). +?DECOMPRESS_BENCH(chunk_bench_rand, throughput_bench_chunk, + gen_determ_rand_bytes(?BENCH_SIZE)). + +?COMPRESS_BENCH(deflate_bench_zeroed, throughput_bench_deflate, + <<0:(8 * ?BENCH_SIZE)>>). +?COMPRESS_BENCH(deflate_bench_rand, throughput_bench_deflate, + gen_determ_rand_bytes(?BENCH_SIZE)). + +throughput_bench_inflate(Compressed, Size) -> + Z = zlib:open(), + zlib:inflateInit(Z), + + submit_throughput_results(Size, + fun() -> + zlib:inflate(Z, Compressed) + end). + +throughput_bench_deflate(Uncompressed, Size) -> + Z = zlib:open(), + zlib:deflateInit(Z), + + submit_throughput_results(Size, + fun() -> + zlib:deflate(Z, Uncompressed, finish) + end). + +throughput_bench_chunk(Compressed, Size) -> + Z = zlib:open(), + zlib:inflateInit(Z), + + ChunkLoop = + fun + Loop({more, _}) -> Loop(zlib:inflateChunk(Z)); + Loop(_) -> ok + end, + + submit_throughput_results(Size, + fun() -> + ChunkLoop(zlib:inflateChunk(Z, Compressed)) + end). + +submit_throughput_results(Size, Fun) -> + TimeTaken = measure_perf_counter(Fun, millisecond), + + KBPS = trunc((Size bsr 10) / (TimeTaken / 1000)), + ct_event:notify(#event{ name = benchmark_data, data = [{value,KBPS}] }), + {comment, io_lib:format("~p ms, ~p KBPS", [TimeTaken, KBPS])}. + +measure_perf_counter(Fun, Unit) -> + Start = os:perf_counter(Unit), + Fun(), + os:perf_counter(Unit) - Start. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Helps with testing directly %%%%%%%%%%%%% -conf(What,Config) -> - try proplists:get_value(What,Config) of - undefined -> - "./zlib_SUITE_data"; - Dir -> - Dir +get_data_dir(Config) -> + try proplists:get_value(data_dir,Config) of + undefined -> + "./zlib_SUITE_data"; + Dir -> + Dir catch - _:_ -> "./zlib_SUITE_data" + _:_ -> "./zlib_SUITE_data" end. -t() -> t([all]). - -t(What) when not is_list(What) -> - t([What]); -t(What) -> - lists:foreach(fun(T) -> - try ?MODULE:T([]) - catch _E:_R -> - Line = get(test_server_loc), - io:format("Failed ~p:~p ~p ~p ~p~n", - [T,Line,_E,_R, erlang:get_stacktrace()]) - end - end, expand(What)). - -expand(All) -> - lists:reverse(expand(All,[])). -expand([H|T], Acc) -> - case ?MODULE:H(suite) of - [] -> expand(T,[H|Acc]); - Cs -> - R = expand(Cs, Acc), - expand(T, R) - end; -expand([], Acc) -> Acc. - +%% Generates a bunch of statistically random bytes using the size as seed. +gen_determ_rand_bytes(Size) -> + gen_determ_rand_bytes(Size, erlang:md5_init(), <<>>). +gen_determ_rand_bytes(Size, _Context, Acc) when Size =< 0 -> + Acc; +gen_determ_rand_bytes(Size, Context0, Acc) when Size > 0 -> + Context = erlang:md5_update(Context0, <<Size/integer>>), + Checksum = erlang:md5_final(Context), + gen_determ_rand_bytes(Size - 16, Context, <<Acc/binary, Checksum/binary>>). diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk index 4edecd8969..cef54dd41a 100644 --- a/lib/kernel/vsn.mk +++ b/lib/kernel/vsn.mk @@ -1 +1 @@ -KERNEL_VSN = 5.3 +KERNEL_VSN = 5.4 |