1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 1997-2020. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19
20%%% Purpose : Test interaction Erlang/Drivers (new features as of R3A)
21
22%%% Checks that new features (as of R3) of the Erlang/Driver
23%%% implementation works as expected.
24%%%
25%%% Things that should be tested:
26%%% - outputv
27%%% - timeouts
28%%% - queueing
29
30-module(driver_SUITE).
31-export([all/0, suite/0,groups/0,init_per_suite/1,
32         end_per_suite/1, init_per_group/2,end_per_group/2,
33         init_per_testcase/2,
34         end_per_testcase/2,
35
36         a_test/1,
37         outputv_echo/1,
38         timer_measure/1,
39         timer_cancel/1,
40         timer_change/1,
41         timer_delay/1,
42         queue_echo/1,
43         outputv_errors/1,
44         driver_unloaded/1,
45         io_ready_exit/1,
46         use_fallback_pollset/0,
47         use_fallback_pollset/1,
48         bad_fd_in_pollset/1,
49         fd_change/1,
50         steal_control/1,
51         otp_6602/1,
52         driver_system_info_base_ver/1,
53         driver_system_info_prev_ver/1,
54         driver_system_info_current_ver/1,
55         driver_monitor/1,
56
57         ioq_exit_ready_input/1,
58         ioq_exit_ready_output/1,
59         ioq_exit_timeout/1,
60         ioq_exit_ready_async/1,
61         ioq_exit_ready_input_async/1,
62         ioq_exit_ready_output_async/1,
63         ioq_exit_timeout_async/1,
64         zero_extended_marker_garb_drv/1,
65         invalid_extended_marker_drv/1,
66         larger_major_vsn_drv/1,
67         larger_minor_vsn_drv/1,
68         smaller_major_vsn_drv/1,
69         smaller_minor_vsn_drv/1,
70         peek_non_existing_queue/1,
71         otp_6879/1,
72         caller/1,
73         many_events/1,
74         missing_callbacks/1,
75         smp_select/1,
76         driver_select_use/1,
77         thread_mseg_alloc_cache_clean/1,
78         otp_9302/1,
79         thr_free_drv/1,
80         async_blast/1,
81         thr_msg_blast/1,
82         consume_timeslice/1,
83         env/1,
84         poll_pipe/1,
85         lots_of_used_fds_on_boot/1,
86         lots_of_used_fds_on_boot_slave/1,
87         z_test/1]).
88
89-export([bin_prefix/2]).
90
91-export([get_check_io_total/1]).   % for z_SUITE.erl
92
93-include_lib("common_test/include/ct.hrl").
94
95
96% First byte in communication with the timer driver
97-define(START_TIMER, 0).
98-define(CANCEL_TIMER, 1).
99-define(DELAY_START_TIMER, 2).
100-define(TIMER, 3).
101-define(CANCELLED, 4).
102
103% First byte in communication with queue driver
104-define(PUSHQ, 0).
105-define(ENQ, 1).
106-define(PUSHQ_BIN, 2).
107-define(ENQ_BIN, 3).
108-define(PUSHQV, 4).
109-define(ENQV, 5).
110
111-define(DEQ, 6).
112-define(BYTES_QUEUED, 7).
113-define(READ_HEAD, 8).
114
115-define(RANDOM, random).
116
117% Max data size that is queued in one instance
118-define(MAX_DATA_SIZE, 16384).
119
120% This is the allowed delay when testing the driver timer functionality
121-define(delay, 400).
122
123-define(heap_binary_size, 64).
124
125suite() ->
126    [{ct_hooks,[ts_install_cth]},
127     {timetrap, {minutes, 1}}].
128
129all() -> %% Keep a_test first and z_test last...
130    [a_test, outputv_errors, outputv_echo, queue_echo,
131     {group, timer},
132     driver_unloaded, io_ready_exit, otp_6602,
133     {group, polling},
134     {group, poll_thread},
135     {group, poll_set},
136     driver_system_info_base_ver,
137     driver_system_info_prev_ver,
138     driver_system_info_current_ver, driver_monitor,
139     {group, ioq_exit}, zero_extended_marker_garb_drv,
140     invalid_extended_marker_drv, larger_major_vsn_drv,
141     larger_minor_vsn_drv, smaller_major_vsn_drv,
142     smaller_minor_vsn_drv, peek_non_existing_queue,
143     otp_6879, caller, many_events, missing_callbacks,
144     thread_mseg_alloc_cache_clean,
145     otp_9302,
146     thr_free_drv,
147     async_blast,
148     thr_msg_blast,
149     consume_timeslice,
150     env,
151     poll_pipe,
152     z_test].
153
154groups() ->
155    [{timer, [],
156      [timer_measure, timer_cancel, timer_delay,
157       timer_change]},
158     {poll_thread, [], [{group, polling}]},
159     {poll_set, [], [{group, polling}]},
160     {polling, [],
161      [a_test, use_fallback_pollset,
162       bad_fd_in_pollset, fd_change,
163       steal_control, smp_select,
164       driver_select_use,
165       lots_of_used_fds_on_boot,
166       z_test]},
167     {ioq_exit, [],
168      [ioq_exit_ready_input, ioq_exit_ready_output,
169       ioq_exit_timeout, ioq_exit_ready_async,
170       ioq_exit_ready_input_async, ioq_exit_ready_output_async,
171       ioq_exit_timeout_async]}].
172
173init_per_suite(Config) ->
174    Config.
175
176end_per_suite(_Config) ->
177    catch erts_debug:set_internal_state(available_internal_state, false).
178
179init_per_group(poll_thread, Config) ->
180    [{node_args, "+IOt 2"} | Config];
181init_per_group(poll_set, Config) ->
182    [{node_args, "+IOt 2 +IOp 2"} | Config];
183init_per_group(polling, Config) ->
184    case proplists:get_value(node_args, Config) of
185        undefined ->
186            Config;
187        Args ->
188            {ok, Node} = start_node(polling, Args),
189            [{node, Node} | Config]
190    end;
191init_per_group(_GroupName, Config) ->
192    Config.
193
194end_per_group(_GroupName, Config) ->
195    case proplists:get_value(node, Config) of
196        undefined ->
197            ok;
198        Node ->
199            stop_node(Node)
200    end,
201    Config.
202
203init_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
204    CIOD = rpc(Config,
205               fun() ->
206                       case catch erts_debug:get_internal_state(available_internal_state) of
207                           true -> ok;
208                           _ -> erts_debug:set_internal_state(available_internal_state, true)
209                       end,
210                       erts_debug:get_internal_state(check_io_debug)
211               end),
212    erlang:display({init_per_testcase, Case}),
213    0 = element(1, CIOD),
214    [{testcase, Case}|Config].
215
216end_per_testcase(Case, Config) ->
217    erlang:display({end_per_testcase, Case}),
218    try rpc(Config, fun() ->
219                            get_stable_check_io_info(),
220                            erts_debug:get_internal_state(check_io_debug)
221                    end) of
222        CIOD ->
223            0 = element(1, CIOD)
224    catch _E:_R:_ST ->
225            %% Logs some info about the system
226            ct_os_cmd("epmd -names"),
227            ct_os_cmd("ps aux"),
228            %% Restart the node
229            case proplists:get_value(node, Config) of
230                undefined ->
231                    ok;
232                Node ->
233                    timer:sleep(1000), %% Give the node time to die
234                    [NodeName, _] = string:lexemes(atom_to_list(Node),"@"),
235                    {ok, Node} = start_node_final(
236                                   list_to_atom(NodeName),
237                                   proplists:get_value(node_args, Config))
238            end
239    end,
240    ok.
241
242ct_os_cmd(Cmd) ->
243    ct:log("~s: ~s",[Cmd,os:cmd(Cmd)]).
244
245%% Test sending bad types to port with an outputv-capable driver.
246outputv_errors(Config) when is_list(Config) ->
247    Path = proplists:get_value(data_dir, Config),
248    erl_ddll:start(),
249    ok = load_driver(Path, outputv_drv),
250
251    outputv_bad_types(fun(T) ->
252                              outputv_errors_1(T),
253                              outputv_errors_1([1|T]),
254                              L = [1,2,3],
255                              outputv_errors_1([L,T]),
256                              outputv_errors_1([L|T])
257                      end),
258    outputv_errors_1(42),
259
260    %% Test iolists that do not fit in the address space.
261    %% Unfortunately, it would be too slow to test in a 64-bit emulator.
262    case erlang:system_info(wordsize) of
263        4 -> outputv_huge_iolists();
264        _ -> ok
265    end.
266
267outputv_bad_types(Test) ->
268    Types = [-1,256,atom,42.0,{a,b,c},make_ref(),fun() -> 42 end,
269             [1|2],<<1:1>>,<<1:9>>,<<1:15>>],
270    _ = [Test(Type) || Type <- Types],
271    ok.
272
273outputv_huge_iolists() ->
274    FourGigs = 1 bsl 32,
275    Sizes = [FourGigs+N || N <- lists:seq(0, 64)] ++
276    [1 bsl N || N <- lists:seq(33, 37)],
277    Base = <<0:(1 bsl 20)/unit:8>>,
278    [begin
279         L = build_iolist(Sz, Base),
280         outputv_errors_1(L)
281     end || Sz <- Sizes],
282    ok.
283
284outputv_errors_1(Term) ->
285    Port = open_port({spawn_driver,outputv_drv}, []),
286    {'EXIT',{badarg,_}} = (catch port_command(Port, Term)),
287    port_close(Port).
288
289build_iolist(N, Base) when N < 16 ->
290    case rand:uniform(3) of
291        1 ->
292            <<Bin:N/binary,_/binary>> = Base,
293            Bin;
294        _ ->
295            lists:seq(1, N)
296    end;
297build_iolist(N, Base) when N =< byte_size(Base) ->
298    case rand:uniform(3) of
299        1 ->
300            <<Bin:N/binary,_/binary>> = Base,
301            Bin;
302        2 ->
303            <<Bin:N/binary,_/binary>> = Base,
304            [Bin];
305        3 ->
306            case N rem 2 of
307                0 ->
308                    L = build_iolist(N div 2, Base),
309                    [L,L];
310                1 ->
311                    L = build_iolist(N div 2, Base),
312                    [L,L,45]
313            end
314    end;
315build_iolist(N0, Base) ->
316    Small = rand:uniform(15),
317    Seq = lists:seq(1, Small),
318    N = N0 - Small,
319    case N rem 2 of
320        0 ->
321            L = build_iolist(N div 2, Base),
322            [L,L|Seq];
323        1 ->
324            L = build_iolist(N div 2, Base),
325            [47,L,L|Seq]
326    end.
327
328%% Test echoing data with a driver that supports outputv.
329outputv_echo(Config) when is_list(Config) ->
330    ct:timetrap({minutes, 10}),
331    Name = 'outputv_drv',
332    P = start_driver(Config, Name, true),
333
334    ov_test(P, {bin,0}),
335    ov_test(P, {bin,1}),
336    ov_test(P, {bin,2}),
337    ov_test(P, {bin,3}),
338    ov_test(P, {bin,4}),
339    ov_test(P, {bin,5}),
340    ov_test(P, {bin,6}),
341    ov_test(P, {bin,7}),
342    ov_test(P, {bin,8}),
343    ov_test(P, {bin,15}),
344    ov_test(P, {bin,16}),
345    ov_test(P, {bin,17}),
346
347    ov_test(P, {list,0}),
348    ov_test(P, {list,1}),
349    ov_test(P, {list,2}),
350    ov_test(P, [int,int,{list,0},int]),
351    ov_test(P, [int,int,{list,1},int]),
352    ov_test(P, [int,int,{list,2}]),
353    ov_test(P, [{list,3},int,int,{list,2}]),
354    ov_test(P, {list,33}),
355
356    ov_test(P, [{bin,0}]),
357    ov_test(P, [{bin,1}]),
358    ov_test(P, [{bin,2}]),
359    ov_test(P, [{bin,3}]),
360    ov_test(P, [{bin,4}]),
361    ov_test(P, [{bin,5}]),
362    ov_test(P, [{bin,6},int]),
363    ov_test(P, [int,{bin,3}]),
364    ov_test(P, [int|{bin,4}]),
365    ov_test(P, [{bin,17},int,{bin,13}|{bin,3}]),
366
367    ov_test(P, [int,{bin,17},int,{bin,?heap_binary_size+1}|{bin,3}]),
368
369    stop_driver(P, Name),
370    ok.
371
372ov_test(Port, Template) ->
373    Self = self(),
374    spawn_opt(erlang, apply, [fun () -> ov_test(Self, Port, Template) end,[]],
375              [link,{fullsweep_after,0}]),
376    receive
377        done -> ok
378    end.
379
380ov_test(Parent, Port, Template) ->
381    true = port_connect(Port, self()),
382
383    HeapData = build_data(Template),
384    io:format("Mostly heap binaries"),
385    ov_send_and_test(Port, HeapData, HeapData),
386
387    %% Try sub binaries.
388    io:format("Mostly sub binaries of heap binaries"),
389    SubHeapData = make_sub_binaries(HeapData),
390    ov_send_and_test(Port, SubHeapData, HeapData),
391
392    %% Try refc binaries.
393    io:format("Refc binaries"),
394    RefcData = make_refc_binaries(HeapData),
395    ov_send_and_test(Port, RefcData, RefcData),
396
397    %% Try sub binaries of heap binaries.
398    io:format("Sub binaries of refc binaries"),
399    SubRefcData = make_sub_binaries(RefcData),
400    ov_send_and_test(Port, SubRefcData, RefcData),
401    io:format("", []),
402
403    %% Garbage collect and make sure that there are no binaries left.
404    %% R7 note:
405    %%  - dead variables on the stack are killed after last use,
406    %%  - erlang:garbage_collect/0 collects garbage immediately.
407    %%  (there used to be dummy functions here)
408    erlang:garbage_collect(),
409    {binary,[]} = process_info(self(), binary),
410
411    %% Reassign Port back to parent and tell him we are done.
412    true = port_connect(Port, Parent),
413    Parent ! done.
414
415ov_send_and_test(Port, Data, ExpectedResult) ->
416    io:format("~p ! ~P", [Port,Data,12]),
417    Port ! {self(),{command,Data}},
418    receive
419        {Port,{data,ReturnData}} ->
420            io:format("~p returned ~P", [Port,ReturnData,12]),
421            compare(ReturnData, ExpectedResult);
422        {Port,{data,OtherData}} ->
423            ct:fail("~p returned WRONG data ~p", [Port,OtherData]);
424        Wrong ->
425            ct:fail({unexpected_port_or_data,Wrong})
426    end.
427
428compare(Got, Expected) ->
429    case {list_to_binary([Got]),list_to_binary([Expected])} of
430        {B,B} -> ok;
431        {_Gb,_Eb} ->
432            ct:fail(got_bad_data)
433    end.
434
435
436%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
437%% 		Driver timer test suites
438%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
439
440
441%% Check that timers time out in good time.
442timer_measure(Config) when is_list(Config) ->
443    Name = 'timer_drv',
444    Port = start_driver(Config, Name, false),
445
446    try_timeouts(Port, 8997),
447
448    stop_driver(Port, Name),
449    ok.
450
451try_timeouts(_, 0) -> ok;
452try_timeouts(Port, Timeout) ->
453    TimeBefore = erlang:monotonic_time(),
454    erlang:port_command(Port, <<?START_TIMER,Timeout:32>>),
455    receive
456        {Port,{data,[?TIMER]}} ->
457            Elapsed = erl_millisecs() - erl_millisecs(TimeBefore),
458            io:format("Elapsed: ~p Timeout: ~p\n", [Elapsed, Timeout]),
459            if
460                Elapsed < Timeout ->
461                    ct:fail(too_short);
462                Elapsed > Timeout + ?delay ->
463                    ct:fail(too_long);
464                true ->
465                    try_timeouts(Port, Timeout div 2)
466            end
467    after Timeout + 100*?delay ->
468              ct:fail("driver failed to timeout")
469    end.
470
471%% Try cancelling timers set in a driver.
472timer_cancel(Config) when is_list(Config) ->
473    Name = 'timer_drv',
474    Port = start_driver(Config, Name, false),
475
476    try_cancel(Port, 10000),
477
478    stop_driver(Port, Name),
479    ok.
480
481try_cancel(Port, Timeout) ->
482    T_before = erl_millisecs(),
483    Port ! {self(),{command,<<?START_TIMER,(Timeout + ?delay):32>>}},
484    receive
485        {Port, {data, [?TIMER]}} ->
486            ct:fail("driver timed out before cancelling it")
487    after Timeout ->
488              Port ! {self(), {command, [?CANCEL_TIMER]}},
489              receive
490                  {Port, {data, [?TIMER]}} ->
491                      ct:fail("driver timed out after cancelling it");
492                  {Port, {data, [?CANCELLED]}} ->
493                      Time_milli_secs = erl_millisecs() - T_before,
494
495                      io:format("Time_milli_secs: ~p Timeout: ~p\n",
496                                [Time_milli_secs, Timeout]),
497                      if
498                          Time_milli_secs > (Timeout + ?delay) ->
499                              ct:fail("too long real time");
500                          Timeout == 0 -> ok;
501                          true -> try_cancel(Port, Timeout div 2)
502                      end
503              after 100*?delay ->
504                        ct:fail("No message from driver")
505              end
506    end.
507
508%% Test that timers don't time out too early if we do a sleep
509%% before setting a timer.
510
511timer_delay(Config) when is_list(Config) ->
512    Name = 'timer_drv',
513    Port = start_driver(Config, Name, false),
514
515    TimeBefore = erlang:monotonic_time(),
516    Timeout0 = 350,
517    erlang:port_command(Port, <<?DELAY_START_TIMER,Timeout0:32>>),
518    Timeout = Timeout0 + 1000,
519    receive
520        {Port,{data,[?TIMER]}} ->
521            Elapsed = erl_millisecs() - erl_millisecs(TimeBefore),
522            io:format("Elapsed time: ~p Timeout: ~p\n",
523                      [Elapsed,Timeout]),
524            if
525                Elapsed < Timeout ->
526                    ct:fail(too_short);
527                Elapsed > Timeout + ?delay ->
528                    ct:fail(too_long);
529                true ->
530                    ok
531            end
532    end,
533
534    stop_driver(Port, Name),
535    ok.
536
537%% Test that driver_set_timer with new timout really changes
538%% the timer (ticket OTP-5942), it didn't work before
539
540timer_change(Config) when is_list(Config) ->
541    Name = 'timer_drv',
542    Port = start_driver(Config, Name, false),
543
544    try_change_timer(Port, 10000),
545
546    stop_driver(Port, Name),
547    ok.
548
549try_change_timer(_Port, 0) -> ok;
550try_change_timer(Port, Timeout) ->
551    Timeout_3 = Timeout*3,
552    TimeBefore = erlang:monotonic_time(),
553    erlang:port_command(Port, <<?START_TIMER,Timeout_3:32>>),
554    erlang:port_command(Port, <<?START_TIMER,Timeout:32>>),
555    receive
556        {Port,{data,[?TIMER]}} ->
557            Elapsed = erl_millisecs() - erl_millisecs(TimeBefore),
558            io:format("Elapsed: ~p Timeout: ~p\n", [Elapsed,Timeout]),
559            if
560                Elapsed < Timeout ->
561                    ct:fail(too_short);
562                Elapsed > Timeout + ?delay ->
563                    ct:fail(too_long);
564                true ->
565                    try_timeouts(Port, Timeout div 2)
566            end
567    after Timeout + 100*?delay ->
568              ct:fail("driver failed to timeout")
569    end.
570
571
572%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
573%% 		Queue test suites
574%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
575
576%% 1) Queue up data in a driver that uses the full driver_queue API to do this.
577%% 2) Get the data back, a random amount at a time.
578queue_echo(Config) when is_list(Config) ->
579    case test_server:is_native(?MODULE) of
580        true -> exit(crashes_native_code);
581        false -> queue_echo_1(Config)
582    end.
583
584queue_echo_1(Config) ->
585    ct:timetrap({minutes, 10}),
586    Name = 'queue_drv',
587    P = start_driver(Config, Name, true),
588
589    q_echo(P, [{?ENQ, {list,1}},
590               {?ENQ, {list,0}},
591               {?ENQ, {bin,0}},
592               {?ENQ, {bin,1}},
593               {?ENQ, {bin,2}},
594               {?ENQ, {bin,3}},
595               {?ENQ, {bin,4}},
596               {?ENQ, {bin,5}},
597               {?ENQ, {bin,600}},
598               {?PUSHQ, {list,0}},
599               {?PUSHQ, {list,1}},
600               {?PUSHQ, {bin,0}},
601               {?PUSHQ, {bin,1}},
602               {?PUSHQ, {bin,888}},
603               {?ENQ_BIN, {bin,0}},
604               {?ENQ_BIN, {bin,1}},
605               {?ENQ_BIN, {bin,2}},
606               {?ENQ_BIN, {bin,3}},
607               {?ENQ_BIN, {bin,4}},
608               {?ENQ_BIN, {bin,777}},
609               {?PUSHQ_BIN, {bin,0}},
610               {?PUSHQ_BIN, {bin,1}},
611               {?PUSHQ_BIN, {bin,334}},
612               {?ENQV, [{bin,0},{list,1},{bin,1},{bin,555}]},
613               {?ENQV, [{bin,0},{list,1},{bin,1}]},
614               {?PUSHQV, [{bin,0},{list,1},{bin,1},{bin,319}]}]),
615
616    stop_driver(P, Name),
617    ok.
618
619q_echo(Port, SpecList) ->
620    io:format("Heap binaries"),
621    HeapData = [{M,build_data(T)} || {M,T} <- SpecList],
622    {HeapDataReturn,HeapDataLen} = feed_driver(Port, HeapData),
623    dequeue(Port, HeapDataReturn, HeapDataLen, 1),
624
625    %% Try sub binaries.
626    io:format("Sub binaries of heap binaries"),
627    SubHeapData = make_sub_binaries(HeapData),
628    %% The following line will generate a warning.
629    {HeapDataReturn,HeapDataLen} = feed_driver(Port, SubHeapData),
630    dequeue(Port, HeapDataReturn, HeapDataLen, 1),
631
632    %% Try refc binaries.
633    io:format("Refc binaries"),
634    RefcData = make_refc_binaries(HeapData),
635    {RefcDataReturn,RefcDataLen} = feed_driver(Port, RefcData),
636    dequeue(Port, RefcDataReturn, RefcDataLen, 1),
637
638    %% Try sub binaries of refc binaries.
639    io:format("Sub binaries of refc binaries"),
640    SubRefcData = make_sub_binaries(RefcData),
641    {RefcDataReturn,RefcDataLen} = feed_driver(Port, SubRefcData),
642    dequeue(Port, RefcDataReturn, RefcDataLen, 1),
643
644    %% Try a writable binary.
645    io:format("Writable binaries"),
646    WritableBinData = make_writable_binaries(HeapData),
647    {WritableDataReturn,WritableDatalen} = feed_driver(Port, WritableBinData),
648    _ = append_to_writable_binaries(WritableBinData),
649    dequeue(Port, WritableDataReturn, WritableDatalen, 1),
650
651    %% Try dequeing more than one byte at the time.
652    io:format("Heap binaries -- dequeueing more than one byte at the time"),
653    feed_and_dequeue(Port, HeapData, 2),
654    feed_and_dequeue(Port, HeapData, 3),
655    feed_and_dequeue(Port, HeapData, 4),
656
657    io:format("\n").
658
659feed_and_dequeue(Port, Data, DeqSize) ->
660    {DataReturn,DataLen} = feed_driver(Port, Data),
661    dequeue(Port, DataReturn, DataLen, DeqSize),
662    ok.
663
664%% Send all data according to the specification to the driver side (where it
665%% is queued up for later return to this process).
666
667feed_driver(Port, Description) ->
668    feed_driver(Port, Description, <<>>, 0).
669
670feed_driver(Port, [], ExpectedInPort, Qb) ->
671    io:format("Expected in port: ~P", [ExpectedInPort,12]),
672    io:format("In port: ~P", [read_head(Port, Qb),12]),
673    {ExpectedInPort,Qb};
674feed_driver(Port, [{Method0,Data}|T], Expected_return, Qb_before) ->
675    Method = case Method0 of
676                 ?RANDOM -> uniform(6)-1;
677                 Other -> Other
678             end,
679    Size = size(list_to_binary([Data])),
680
681    %% ***********************************************************************
682    %% NOTE! Never never never change this to io:format/2, as that will imply
683    %% message sending, and sending as message will spoil the test of
684    %% writable binaries.
685
686    %% erlang:display({sending,method_name(Method),Data}),
687    %% ***********************************************************************
688
689    queue_op(Port, Method, Data),
690
691    Qb_in_driver = bytes_queued(Port),
692    case Qb_before + Size of
693        Qb_in_driver -> ok;
694        Sum ->
695            ct:fail("Qb_before: ~p\n"
696                    "Qb_before+Size: ~p\n"
697                    "Qb_in_driver: ~p",
698                    [Qb_before,Sum,Qb_in_driver])
699    end,
700    X_return = case Method of
701                   ?ENQ -> list_to_binary([Expected_return,Data]);
702                   ?PUSHQ -> list_to_binary([Data,Expected_return]);
703                   ?PUSHQ_BIN -> list_to_binary([Data,Expected_return]);
704                   ?ENQ_BIN -> list_to_binary([Expected_return,Data]);
705                   ?PUSHQV -> list_to_binary([Data,Expected_return]);
706                   ?ENQV -> list_to_binary([Expected_return,Data])
707               end,
708    feed_driver(Port, T, X_return, Qb_before + Size).
709
710%% method_name(0) -> pushq;
711%% method_name(1) -> enq;
712%% method_name(2) -> pushq_bin;
713%% method_name(3) -> enq_bin;
714%% method_name(4) -> pushqv;
715%% method_name(5) -> enqv.
716
717dequeue(Port, DataList, LenToGet, DeqSize) ->
718    io:format("Dequeuing ~p bytes, ~p byte(s) at once...", [LenToGet,DeqSize]),
719    compare_return(Port, DataList, LenToGet, DeqSize).
720
721compare_return(Port, _Data_list, 0, _Back_len) ->
722    0 = bytes_queued(Port);
723compare_return(Port, QueuedInPort0, Len_to_get, DeqSize) ->
724    case bytes_queued(Port) of
725        Len_to_get -> ok;
726        BytesInQueue ->
727            ct:fail("Len_to_get: ~p\nBytes in queue: ~p", [Len_to_get,BytesInQueue])
728    end,
729    BytesToDequeue = if (DeqSize > Len_to_get) -> Len_to_get;
730                        true -> DeqSize
731                     end,
732    Dequeued = read_head(Port, BytesToDequeue),
733    case bin_prefix(Dequeued, QueuedInPort0) of
734        true ->
735            deq(Port, BytesToDequeue),
736            <<_:BytesToDequeue/binary,QueuedInPort/binary>> = QueuedInPort0,
737            compare_return(Port, QueuedInPort, Len_to_get - BytesToDequeue, DeqSize);
738        false ->
739            ct:fail("Bytes to dequeue: ~p\nDequeued: ~p\nQueued in port: ~P",
740                    [BytesToDequeue, Dequeued, QueuedInPort0,12])
741    end.
742
743%% bin_prefix(PrefixBinary, Binary)
744%%  Is PrefixBinary a prefix of Binary?
745
746bin_prefix(<<C:8,PreTail/binary>>, <<C:8,Tail/binary>>) ->
747    bin_prefix(PreTail, Tail);
748bin_prefix(<<>>, _Bin) -> true;
749bin_prefix(_, _) -> false.
750
751queue_op(Port, Method, Data) ->
752    [] = erlang:port_control(Port, Method, []),
753    Port ! {self(),{command,Data}},
754    ok.
755
756bytes_queued(Port) ->
757    case erlang:port_control(Port, ?BYTES_QUEUED, []) of
758        <<I:32>> -> I;
759        Bad -> ct:fail({bad_result,Bad})
760    end.
761
762deq(Port, Size) ->
763    [] = erlang:port_control(Port, ?DEQ, <<Size:32>>).
764
765read_head(Port, Size) ->
766    erlang:port_control(Port, ?READ_HEAD, <<Size:32>>).
767
768
769driver_unloaded(Config) when is_list(Config) ->
770    process_flag(trap_exit, true),
771    Drv = timer_drv,
772    User = self(),
773    Loaded = make_ref(),
774    Die = make_ref(),
775    Loader = spawn(fun () ->
776                           erl_ddll:start(),
777                           ok = load_driver(proplists:get_value(data_dir,
778                                                                Config),
779                                            Drv),
780                           User ! Loaded,
781                           receive Die -> exit(bye) end
782                   end),
783    receive Loaded -> ok end,
784    Port = open_port({spawn, Drv}, []),
785    Loader ! Die,
786    receive
787        {'EXIT', Port, Reason} ->
788            driver_unloaded = Reason
789            %% Reason used to be -1
790    end.
791
792
793io_ready_exit(Config) when is_list(Config) ->
794    OTE = process_flag(trap_exit, true),
795    Test = self(),
796    Dgawd = spawn(fun () ->
797                          ok = dgawd_handler:install(),
798                          Mon = erlang:monitor(process, Test),
799                          Test ! dgawd_handler_started,
800                          receive
801                              {'DOWN', Mon, _, _, _} -> ok;
802                              stop_dgawd_handler -> ok
803                          end,
804                          dgawd_handler:restore(),
805                          Test ! dgawd_handler_stopped
806                  end),
807    receive dgawd_handler_started -> ok end,
808    Drv = io_ready_exit_drv,
809    erl_ddll:start(),
810    ok = load_driver(proplists:get_value(data_dir, Config), Drv),
811    Port = open_port({spawn, Drv}, []),
812    case erlang:port_control(Port, 0, "") of
813        "ok" ->
814            receive
815                {'EXIT', Port, Reason} ->
816                    case Reason of
817                        ready_output_driver_failure ->
818                            io:format("Exited in output_ready()~n"),
819                            ok;
820                        ready_input_driver_failure ->
821                            io:format("Exited in input_ready()~n"),
822                            ok;
823                        Error -> ct:fail(Error)
824                    end
825            end,
826            receive after 2000 -> ok end,
827            false = dgawd_handler:got_dgawd_report(),
828            Dgawd ! stop_dgawd_handler,
829            receive dgawd_handler_stopped -> ok end,
830            process_flag(trap_exit, OTE),
831            ok;
832        "nyiftos" ->
833            process_flag(trap_exit, OTE),
834            {skipped, "Not yet implemented for this OS"};
835        Error ->
836            process_flag(trap_exit, OTE),
837            ct:fail({unexpected_control_result, Error})
838    end.
839
840
841-define(CHKIO_STOP, 0).
842-define(CHKIO_USE_FALLBACK_POLLSET, 1).
843-define(CHKIO_BAD_FD_IN_POLLSET, 2).
844-define(CHKIO_FD_CHANGE, 4).
845-define(CHKIO_STEAL, 5).
846-define(CHKIO_STEAL_AUX, 6).
847-define(CHKIO_SMP_SELECT, 7).
848-define(CHKIO_DRV_USE, 8).
849
850use_fallback_pollset() ->
851    [{timetrap, {minutes, 2}}].
852
853use_fallback_pollset(Config) when is_list(Config) ->
854    rpc(Config, fun() -> use_fallback_pollset_t(Config) end).
855
856use_fallback_pollset_t(Config) when is_list(Config) ->
857    FlbkFun = fun () ->
858                      {Flbk, _} = get_fallback(erlang:system_info(check_io)),
859                      case lists:keysearch(total_poll_set_size, 1, Flbk) of
860                          {value, {total_poll_set_size, N}} when N > 0 ->
861                              ok;
862                          Error ->
863                              ct:fail({failed_to_use_fallback, Error})
864                      end
865              end,
866    {BckupTest, Handel, OkRes}
867    = case chkio_test_init(Config) of
868          {erts_poll_info, ChkIo} = Hndl ->
869              case lists:keysearch(fallback, 1, ChkIo) of
870                  {value, {fallback, B}} when B =/= false ->
871                      {FlbkFun, Hndl, ok};
872                  _ ->
873                      {fun () -> ok end,
874                       Hndl,
875                       {comment,
876                        "This implementation does not use "
877                        "a fallback pollset"}}
878              end;
879          Skip ->
880              {fun () -> ok end, Skip, ok}
881      end,
882    io:format("Node = ~p~n",[node()]),
883    case chkio_test_fini(chkio_test(Handel,
884                                    ?CHKIO_USE_FALLBACK_POLLSET,
885                                    fun () ->
886                                            sleep(1000),
887                                            BckupTest()
888                                    end)) of
889        {skipped, _} = Res -> Res;
890        _ -> OkRes
891    end.
892
893bad_fd_in_pollset(Config) when is_list(Config) ->
894    rpc(Config,
895        fun() ->
896                chkio_test_fini(chkio_test(chkio_test_init(Config),
897                                           ?CHKIO_BAD_FD_IN_POLLSET,
898                                           fun () -> sleep(1000) end))
899        end).
900
901fd_change(Config) when is_list(Config) ->
902    rpc(Config,
903        fun() ->
904                chkio_test_fini(chkio_test(chkio_test_init(Config),
905                                           ?CHKIO_FD_CHANGE,
906                                           fun () -> sleep(1000) end))
907        end).
908
909steal_control(Config) when is_list(Config) ->
910    rpc(Config,
911        fun() ->
912                chkio_test_fini(case chkio_test_init(Config) of
913                                    {erts_poll_info, _} = Hndl ->
914                                        steal_control_test(Hndl);
915                                    Skip ->
916                                        Skip
917                                end)
918        end).
919
920steal_control_test(Hndl = {erts_poll_info, Before}) ->
921    Port = open_chkio_port(),
922    case erlang:port_control(Port, ?CHKIO_STEAL_AUX, "") of
923        [$f,$d,$s,$:| _] = FdList ->
924            chk_chkio_port(Port),
925            sleep(500),
926            chk_chkio_port(Port),
927            Res = chkio_test(Hndl,
928                             ?CHKIO_STEAL,
929                             FdList,
930                             fun () ->
931                                     chk_chkio_port(Port),
932                                     sleep(500),
933                                     chk_chkio_port(Port)
934                             end),
935            case erlang:port_control(Port, ?CHKIO_STOP, "") of
936                "ok" ->
937                    chk_chkio_port(Port),
938                    ok;
939                StopErr ->
940                    chk_chkio_port(Port),
941                    ct:fail({stop_error, StopErr})
942            end,
943            close_chkio_port(Port),
944            Res;
945        [$s,$k,$i,$p,$:,$\ |Skip] ->
946            chk_chkio_port(Port),
947            close_chkio_port(Port),
948            {chkio_test_result,
949             {skipped, Skip},
950             Before};
951        StartErr ->
952            chk_chkio_port(Port),
953            ct:fail({start_error, StartErr})
954    end.
955
956chkio_test_init(Config) when is_list(Config) ->
957    ChkIo = get_stable_check_io_info(),
958    case catch lists:keysearch(name, 1, ChkIo) of
959        {value, {name, erts_poll}} ->
960            ct:log("Before test: ~p~n", [ChkIo]),
961            Path = proplists:get_value(data_dir, Config),
962            erl_ddll:start(),
963            ok = load_driver(Path, 'chkio_drv'),
964            process_flag(trap_exit, true),
965            {erts_poll_info, ChkIo};
966        _ ->
967            {skipped, "Test written to test erts_poll() which isn't used"}
968    end.
969
970
971chkio_test_fini({skipped, _} = Res) ->
972    Res;
973chkio_test_fini({chkio_test_result, Res, Before}) ->
974    ok = erl_ddll:unload_driver('chkio_drv'),
975    ok = erl_ddll:stop(),
976    After = get_stable_check_io_info(),
977    io:format("After test: ~p~n", [After]),
978    verify_chkio_state(Before, After),
979    Res.
980
981open_chkio_port() ->
982    open_port({spawn, 'chkio_drv'}, []).
983
984close_chkio_port(Port) when is_port(Port) ->
985    true = erlang:port_close(Port),
986    receive
987        {'EXIT', Port, normal} ->
988            ok;
989        {'EXIT', Port, Reason} ->
990            ct:fail({abnormal_port_exit, Port, Reason});
991        {Port, Message} ->
992            ct:fail({strange_message_from_port, Message})
993    end.
994
995chk_chkio_port(Port) ->
996    receive
997        {'EXIT', Port, Reason} when Reason /= normal ->
998            ct:fail({port_exited, Port, Reason})
999    after 0 ->
1000              ok
1001    end.
1002
1003
1004chkio_test({skipped, _} = Res, _Test, _Fun) ->
1005    Res;
1006chkio_test({erts_poll_info, _Before} = EPI, Test, Fun) when is_integer(Test) ->
1007    chkio_test(EPI, Test, "", Fun).
1008
1009chkio_test({skipped, _} = Res, _Test, _TestArgs, _Fun) ->
1010    Res;
1011chkio_test({erts_poll_info, Before},
1012           Test,
1013           TestArgs,
1014           Fun) when is_integer(Test),
1015                     is_list(TestArgs) ->
1016    Port = open_chkio_port(),
1017    case erlang:port_control(Port, Test, TestArgs) of
1018        "ok" ->
1019            chk_chkio_port(Port),
1020            Fun(),
1021            During = get_check_io_total(erlang:system_info(check_io)),
1022            erlang:display(During),
1023
1024            [0 = element(1, erts_debug:get_internal_state(check_io_debug)) ||
1025                %% The pollset is not stable when running the fallback testcase
1026                Test /= ?CHKIO_USE_FALLBACK_POLLSET],
1027            io:format("During test: ~p~n", [During]),
1028            chk_chkio_port(Port),
1029            case erlang:port_control(Port, ?CHKIO_STOP, "") of
1030                Res when is_list(Res) ->
1031                    chk_chkio_port(Port),
1032                    io:format("~s", [Res]),
1033                    close_chkio_port(Port),
1034                    Res,
1035                    case Res of
1036                        [$c,$o,$m,$m,$e,$n,$t,$:,$\ |Cmnt] ->
1037                            {chkio_test_result,
1038                             {comment, Cmnt},
1039                             Before};
1040                        _ ->
1041                            {chkio_test_result,
1042                             Res,
1043                             Before}
1044                    end;
1045                StopErr ->
1046                    chk_chkio_port(Port),
1047                    ct:fail({stop_error, StopErr})
1048            end;
1049        [$s,$k,$i,$p,$:,$\ |Skip] ->
1050            chk_chkio_port(Port),
1051            close_chkio_port(Port),
1052            {chkio_test_result,
1053             {skipped, Skip},
1054             Before};
1055        StartErr ->
1056            chk_chkio_port(Port),
1057            ct:fail({start_error, StartErr})
1058    end.
1059
1060verify_chkio_state(Before, After) ->
1061    TotSetSize = lists:keysearch(total_poll_set_size, 1, Before),
1062    TotSetSize = lists:keysearch(total_poll_set_size, 1, After),
1063    case lists:keysearch(fallback, 1, Before) of
1064        {value,{fallback,false}} ->
1065            ok;
1066        _ ->
1067            BckupSetSize = lists:keysearch(fallback_poll_set_size,
1068                                           1,
1069                                           Before),
1070            BckupSetSize = lists:keysearch(fallback_poll_set_size,
1071                                           1,
1072                                                       After)
1073    end,
1074    ok.
1075
1076get_stable_check_io_info() ->
1077    get_stable_check_io_info(10).
1078get_stable_check_io_info(0) ->
1079    get_check_io_total(erlang:system_info(check_io));
1080get_stable_check_io_info(N) ->
1081    ChkIo = get_check_io_total(erlang:system_info(check_io)),
1082    PendUpdNo = proplists:get_value(pending_updates, ChkIo, 0),
1083    ActFds = proplists:get_value(active_fds, ChkIo),
1084    case {PendUpdNo, ActFds} of
1085        {0, 0} ->
1086            ChkIo;
1087        _ ->
1088            receive after 100 -> ok end,
1089            get_stable_check_io_info(N-1)
1090    end.
1091
1092%% Merge return from erlang:system_info(check_io)
1093%% as if it was one big pollset.
1094get_check_io_total(ChkIo) ->
1095    ct:log("ChkIo = ~p~n",[ChkIo]),
1096    {Fallback, Rest} = get_fallback(ChkIo),
1097    OnlyPollThreads = [PS || PS <- Rest, not is_scheduler_pollset(PS)],
1098    add_fallback_infos(Fallback,
1099      lists:foldl(
1100        fun(Pollset, Acc) ->
1101                lists:zipwith(fun(A, B) ->
1102                                      add_pollset_infos(A,B)
1103                              end,
1104                              Pollset, Acc)
1105        end,
1106        hd(OnlyPollThreads), tl(OnlyPollThreads))).
1107
1108is_scheduler_pollset(Pollset) ->
1109    proplists:get_value(poll_threads, Pollset) == 0.
1110
1111add_pollset_infos({Tag, A}=TA , {Tag, B}=TB) ->
1112    case tag_type(Tag) of
1113        sum ->
1114            {Tag, A + B};
1115        const ->
1116            case A of
1117                B -> TA;
1118                _ ->
1119                    ct:fail("Unexpected diff in pollsets ~p != ~p",
1120                            [TA,TB])
1121            end
1122    end.
1123
1124get_fallback([MaybeFallback | ChkIo] = AllChkIo) ->
1125    case proplists:get_value(fallback, MaybeFallback) of
1126        true ->
1127            {MaybeFallback, ChkIo};
1128        false ->
1129            {undefined, AllChkIo}
1130    end.
1131
1132add_fallback_infos(undefined, Acc) ->
1133    Acc;
1134add_fallback_infos(Flbk, Acc) ->
1135    lists:zipwith(fun({Tag, A}=TA, {Tag, B}=TB) ->
1136                          case tag_type(Tag) of
1137                              sum -> {Tag, A + B};
1138                              const when Tag =:= fallback -> TA;
1139                              const -> TB
1140                          end
1141                  end,
1142                  Flbk, Acc).
1143
1144tag_type(name) -> const;
1145tag_type(primary) -> const;
1146tag_type(fallback) -> const;
1147tag_type(kernel_poll) -> const;
1148tag_type(memory_size) -> sum;
1149tag_type(total_poll_set_size) -> sum;
1150tag_type(lazy_updates) -> const;
1151tag_type(pending_updates) -> sum;
1152tag_type(batch_updates) -> const;
1153tag_type(concurrent_updates) -> const;
1154tag_type(max_fds) -> const;
1155tag_type(active_fds) -> sum;
1156tag_type(poll_threads) -> sum.
1157
1158
1159%% Missed port lock when stealing control of fd from a
1160%% driver that didn't use the same lock. The lock checker
1161%% used to trigger on this and dump core.
1162otp_6602(Config) when is_list(Config) ->
1163    {ok, Node} = start_node(Config),
1164    Done = make_ref(),
1165    Parent = self(),
1166    Tester = spawn_link(Node,
1167                        fun () ->
1168                                %% Inet driver use port locking...
1169                                {ok, S} = gen_udp:open(0),
1170                                {ok, Fd} = inet:getfd(S),
1171                                %% Steal fd (lock checker used to
1172                                %% trigger here).
1173                                {ok, _S2} = gen_udp:open(0,[{fd,Fd}]),
1174                                Parent ! Done
1175                        end),
1176    receive Done -> ok end,
1177    unlink(Tester),
1178    stop_node(Node),
1179    ok.
1180
1181-define(EXPECTED_SYSTEM_INFO_NAMES1,
1182        ["drv_drv_vsn",
1183         "emu_drv_vsn",
1184         "erts_vsn",
1185         "otp_vsn",
1186         "thread",
1187         "smp"]).
1188-define(EXPECTED_SYSTEM_INFO_NAMES2,
1189        (?EXPECTED_SYSTEM_INFO_NAMES1 ++
1190         ["async_thrs",
1191          "sched_thrs"])).
1192
1193-define(EXPECTED_SYSTEM_INFO_NAMES3,
1194        (?EXPECTED_SYSTEM_INFO_NAMES2 ++
1195         ["emu_nif_vsn"])).
1196
1197-define(EXPECTED_SYSTEM_INFO_NAMES4,
1198        (?EXPECTED_SYSTEM_INFO_NAMES3 ++
1199         ["dirty_sched"])).
1200
1201-define(EXPECTED_SYSTEM_INFO_NAMES, ?EXPECTED_SYSTEM_INFO_NAMES4).
1202
1203'driver_system_info_base_ver'(Config) when is_list(Config) ->
1204 driver_system_info_test(Config, sys_info_base_drv).
1205
1206'driver_system_info_prev_ver'(Config) when is_list(Config) ->
1207 driver_system_info_test(Config, sys_info_prev_drv).
1208
1209driver_system_info_current_ver(Config) when is_list(Config) ->
1210    driver_system_info_test(Config, sys_info_curr_drv).
1211
1212driver_system_info_test(Config, Name) ->
1213    Port = start_driver(Config, Name, false),
1214    case erlang:port_control(Port, 0, []) of
1215        [$o,$k,$:,_ | Result] ->
1216            check_driver_system_info_result(Result);
1217        [$e,$r,$r,$o,$r,$:,_ | Error] ->
1218            ct:fail(Error);
1219        Unexpected ->
1220            ct:fail({unexpected_result, Unexpected})
1221    end,
1222    stop_driver(Port, Name),
1223    ok.
1224
1225check_driver_system_info_result(Result) ->
1226    io:format("All names: ~p~n", [?EXPECTED_SYSTEM_INFO_NAMES]),
1227    io:format("Result: ~p~n", [Result]),
1228    {[], Ns, DDVSN} = chk_sis(lists:map(fun (Str) ->
1229                                                string:lexemes(Str, "=")
1230                                        end,
1231                                        string:lexemes(Result, " ")),
1232                              ?EXPECTED_SYSTEM_INFO_NAMES),
1233    case {DDVSN,
1234          drv_vsn_str2tup(erlang:system_info(driver_version))} of
1235        {DDVSN, DDVSN} ->
1236            [] = Ns;
1237        %% {{1, 0}, _} ->
1238        %% 	  ExpNs = lists:sort(?EXPECTED_SYSTEM_INFO_NAMES
1239        %% 				   -- ?EXPECTED_SYSTEM_INFO_NAMES1),
1240        %% 	  ExpNs = lists:sort(Ns);
1241        %% {{1, 1}, _} ->
1242        %% 	  ExpNs = lists:sort(?EXPECTED_SYSTEM_INFO_NAMES
1243        %% 				   -- ?EXPECTED_SYSTEM_INFO_NAMES2),
1244        %% 	  ExpNs = lists:sort(Ns);
1245        {{3, 0}, _} ->
1246            ExpNs = lists:sort(?EXPECTED_SYSTEM_INFO_NAMES
1247                               -- ?EXPECTED_SYSTEM_INFO_NAMES3),
1248            ExpNs = lists:sort(Ns)
1249    end.
1250
1251chk_sis(SIs, Ns) ->
1252    chk_sis(SIs, Ns, unknown).
1253
1254chk_sis(SIs, [], DDVSN) ->
1255    {SIs, [], DDVSN};
1256chk_sis([], Ns, DDVSN) ->
1257    {[], Ns, DDVSN};
1258chk_sis([[N, _] = SI| SIs], Ns, DDVSN) ->
1259    true = lists:member(N, Ns),
1260    case check_si_res(SI) of
1261        {driver_version, NewDDVSN} ->
1262            chk_sis(SIs, lists:delete(N, Ns), NewDDVSN);
1263        _ ->
1264            chk_sis(SIs, lists:delete(N, Ns), DDVSN)
1265    end.
1266
1267%% Data in first version of driver_system_info() (driver version 1.0)
1268check_si_res(["drv_drv_vsn", Value]) ->
1269    DDVSN = drv_vsn_str2tup(Value),
1270    {Major, DMinor} = DDVSN,
1271    {Major, EMinor} = drv_vsn_str2tup(erlang:system_info(driver_version)),
1272    true = DMinor =< EMinor,
1273    {driver_version, DDVSN};
1274check_si_res(["emu_drv_vsn", Value]) ->
1275    Value = erlang:system_info(driver_version);
1276check_si_res(["erts_vsn", Value]) ->
1277    Value = erlang:system_info(version);
1278check_si_res(["otp_vsn", Value]) ->
1279    Value = erlang:system_info(otp_release);
1280check_si_res(["thread", "true"]) ->
1281    true = erlang:system_info(threads);
1282check_si_res(["thread", "false"]) ->
1283    false = erlang:system_info(threads);
1284check_si_res(["smp", "true"]) ->
1285    true = erlang:system_info(smp_support);
1286
1287%% Data added in second version of driver_system_info() (driver version 1.1)
1288check_si_res(["async_thrs", Value]) ->
1289    Value = integer_to_list(erlang:system_info(thread_pool_size));
1290check_si_res(["sched_thrs", Value]) ->
1291    Value = integer_to_list(erlang:system_info(schedulers));
1292
1293%% Data added in 3rd version of driver_system_info() (driver version 1.5)
1294check_si_res(["emu_nif_vsn", Value]) ->
1295    Value = erlang:system_info(nif_version);
1296
1297%% Data added in 4th version of driver_system_info() (driver version 3.1)
1298check_si_res(["dirty_sched", _Value]) ->
1299    true;
1300
1301check_si_res(Unexpected) ->
1302    ct:fail({unexpected_result, Unexpected}).
1303
1304-define(MON_OP_I_AM_IPID,1).
1305-define(MON_OP_MONITOR_ME,2).
1306-define(MON_OP_DEMONITOR_ME,3).
1307-define(MON_OP_MONITOR_ME_LATER,4).
1308-define(MON_OP_DO_DELAYED_MONITOR,5).
1309
1310%% Test monitoring of processes from drivers
1311driver_monitor(Config) when is_list(Config) ->
1312    Name = monitor_drv,
1313    Port = start_driver(Config, Name, false),
1314    "ok" = port_control(Port,?MON_OP_I_AM_IPID,[]),
1315    "ok" = port_control(Port,?MON_OP_MONITOR_ME,[]),
1316    "ok" = port_control(Port,?MON_OP_DEMONITOR_ME,[]),
1317    {monitors, []} = erlang:port_info(Port,monitors),
1318
1319    "ok:"++Id1 = port_control(Port,?MON_OP_MONITOR_ME_LATER,[]),
1320    {monitored_by, []} = process_info(self(),monitored_by),
1321    "ok" = port_control(Port,?MON_OP_DO_DELAYED_MONITOR,Id1),
1322    {monitored_by, [Port]} = process_info(self(),monitored_by),
1323    "ok" = port_control(Port,?MON_OP_DEMONITOR_ME,[]),
1324    {monitored_by, []} = process_info(self(),monitored_by),
1325
1326    "ok" = port_control(Port,?MON_OP_MONITOR_ME,[]),
1327    Me = self(),
1328    {Pid1,Ref1} =
1329    spawn_monitor(fun() ->
1330                          Me ! port_control(Port,?MON_OP_MONITOR_ME,[]),
1331                          Me ! process_info(self(),monitored_by),
1332                          Me ! erlang:port_info(Port,monitors)
1333                  end),
1334    ok = receive
1335             "ok" ->
1336                 ok
1337         after 1000 ->
1338                   timeout
1339         end,
1340    ok = receive
1341             {monitored_by, L} ->
1342                 L2 = lists:sort(L),
1343                 L3 = lists:sort([Me,Port]),
1344                 case L2 of
1345                     L3 ->
1346                         ok;
1347                     _ ->
1348                         mismatch
1349                 end
1350         after 1000 ->
1351                   timeout
1352         end,
1353    ok = receive
1354             {monitors, LL} ->
1355                 LL2 = lists:sort(LL),
1356                 LL3 = lists:sort([{process,Me},{process,Pid1}]),
1357                 case LL2 of
1358                     LL3 ->
1359                         ok;
1360                     _ ->
1361                         mismatch
1362                 end
1363         after 1000 ->
1364                   timeout
1365         end,
1366    ok = receive
1367             {'DOWN', Ref1, process, Pid1, _} ->
1368                 ok
1369         after 1000 ->
1370                   timeout
1371         end,
1372    ok = receive
1373             {monitor_fired,Port,Pid1} ->
1374                 ok
1375         after 1000 ->
1376                   timeout
1377         end,
1378    "ok" = port_control(Port,?MON_OP_DEMONITOR_ME,[]),
1379    {monitors,[]} = erlang:port_info(Port,monitors),
1380    {monitored_by, []} = process_info(self(),monitored_by),
1381
1382    "ok" = port_control(Port,?MON_OP_MONITOR_ME,[]),
1383    {Pid2,Ref2} =
1384    spawn_monitor(fun() ->
1385                          receive go -> ok end,
1386                          Me ! port_control(Port,?MON_OP_MONITOR_ME_LATER,[]),
1387                          Me ! process_info(self(),monitored_by),
1388                          Me ! erlang:port_info(Port,monitors)
1389                  end),
1390    Pid2 ! go,
1391    {ok,Id2} = receive
1392                   "ok:"++II ->
1393                       {ok,II}
1394               after 1000 ->
1395                         timeout
1396               end,
1397    ok = receive
1398             {monitored_by, [Me]} ->
1399                 ok
1400         after 1000 ->
1401                   timeout
1402         end,
1403    ok = receive
1404             {monitors, [{process,Me}]} ->
1405                 ok
1406         after 1000 ->
1407                   timeout
1408         end,
1409    ok = receive
1410             {'DOWN', Ref2, process, Pid2, _} ->
1411                 ok
1412         after 1000 ->
1413                   timeout
1414         end,
1415    "noproc" = port_control(Port,?MON_OP_DO_DELAYED_MONITOR,Id2),
1416    {monitors,[{process,Me}]} = erlang:port_info(Port,monitors),
1417    "ok" = port_control(Port,?MON_OP_DEMONITOR_ME,[]),
1418    "not_monitored" = port_control(Port,?MON_OP_DEMONITOR_ME,[]),
1419    {monitors,[]} = erlang:port_info(Port,monitors),
1420    {monitored_by, []} = process_info(self(),monitored_by),
1421
1422
1423    "ok" = port_control(Port,?MON_OP_MONITOR_ME,[]),
1424    {Pid3,Ref3} =
1425    spawn_monitor(fun() ->
1426                          receive go -> ok end,
1427                          Me ! port_control(Port,?MON_OP_MONITOR_ME_LATER,[]),
1428                          Me ! process_info(self(),monitored_by),
1429                          Me ! erlang:port_info(Port,monitors) ,
1430                          receive die -> ok end
1431                  end),
1432    Pid3 ! go,
1433    {ok,Id3} = receive
1434                   "ok:"++III ->
1435                       {ok,III}
1436               after 1000 ->
1437                         timeout
1438               end,
1439    ok = receive
1440             {monitored_by, [Me]} ->
1441                 ok
1442         after 1000 ->
1443                   timeout
1444         end,
1445    ok = receive
1446             {monitors, [{process,Me}]} ->
1447                 ok
1448         after 1000 ->
1449                   timeout
1450         end,
1451    "ok" = port_control(Port,?MON_OP_DO_DELAYED_MONITOR,Id3),
1452    LLL1 = lists:sort([{process,Me},{process,Pid3}]),
1453    {monitors,LLL2} = erlang:port_info(Port,monitors),
1454    LLL1 = lists:sort(LLL2),
1455    "ok" = port_control(Port,?MON_OP_DEMONITOR_ME,[]),
1456    {monitors,[{process,Pid3}]} = erlang:port_info(Port,monitors),
1457    Pid3 ! die,
1458    ok = receive
1459             {'DOWN', Ref3, process, Pid3, _} ->
1460                 ok
1461         after 1000 ->
1462                   timeout
1463         end,
1464    "not_found" = port_control(Port,?MON_OP_DO_DELAYED_MONITOR,Id2),
1465    {monitors,[]} = erlang:port_info(Port,monitors),
1466    "not_monitored" = port_control(Port,?MON_OP_DEMONITOR_ME,[]),
1467    {monitors,[]} = erlang:port_info(Port,monitors),
1468    {monitored_by, []} = process_info(self(),monitored_by),
1469
1470    stop_driver(Port, Name),
1471    ok.
1472
1473
1474-define(IOQ_EXIT_READY_INPUT, 1).
1475-define(IOQ_EXIT_READY_OUTPUT, 2).
1476-define(IOQ_EXIT_TIMEOUT, 3).
1477-define(IOQ_EXIT_READY_ASYNC, 4).
1478-define(IOQ_EXIT_READY_INPUT_ASYNC, 6).
1479-define(IOQ_EXIT_READY_OUTPUT_ASYNC, 7).
1480-define(IOQ_EXIT_TIMEOUT_ASYNC, 8).
1481
1482ioq_exit_test(Config, TestNo) ->
1483    Drv = ioq_exit_drv,
1484    try
1485        begin
1486            case load_driver(proplists:get_value(data_dir, Config),
1487                             Drv) of
1488                ok -> ok;
1489                {error, permanent} -> ok;
1490                LoadError -> ct:fail({load_error, LoadError})
1491            end,
1492            case open_port({spawn, Drv}, []) of
1493                Port when is_port(Port) ->
1494                    try port_control(Port, TestNo, "") of
1495                        "ok" ->
1496                            ok;
1497                        "nyiftos" ->
1498                            throw({skipped,
1499                                   "Not yet implemented for "
1500                                   "this OS"});
1501                        [$s,$k,$i,$p,$:,$ | Comment] ->
1502                            throw({skipped, Comment});
1503                        [$e,$r,$r,$o,$r,$:,$ | Error] ->
1504                            ct:fail(Error)
1505                    after
1506                        Port ! {self(), close},
1507                        receive {Port, closed} -> ok end,
1508                        false = lists:member(Port, erlang:ports()),
1509                        ok
1510                    end;
1511                Error ->
1512                    ct:fail({open_port_failed, Error})
1513            end
1514        end
1515    catch
1516        throw:Term -> Term
1517    after
1518        erl_ddll:unload_driver(Drv)
1519    end.
1520
1521ioq_exit_ready_input(Config) when is_list(Config) ->
1522    ioq_exit_test(Config, ?IOQ_EXIT_READY_INPUT).
1523
1524ioq_exit_ready_output(Config) when is_list(Config) ->
1525    ioq_exit_test(Config, ?IOQ_EXIT_READY_OUTPUT).
1526
1527ioq_exit_timeout(Config) when is_list(Config) ->
1528    ioq_exit_test(Config, ?IOQ_EXIT_TIMEOUT).
1529
1530ioq_exit_ready_async(Config) when is_list(Config) ->
1531    ioq_exit_test(Config, ?IOQ_EXIT_READY_ASYNC).
1532
1533ioq_exit_ready_input_async(Config) when is_list(Config) ->
1534    ioq_exit_test(Config, ?IOQ_EXIT_READY_INPUT_ASYNC).
1535
1536ioq_exit_ready_output_async(Config) when is_list(Config) ->
1537    ioq_exit_test(Config, ?IOQ_EXIT_READY_OUTPUT_ASYNC).
1538
1539ioq_exit_timeout_async(Config) when is_list(Config) ->
1540    ioq_exit_test(Config, ?IOQ_EXIT_TIMEOUT_ASYNC).
1541
1542
1543vsn_mismatch_test(Config, LoadResult) ->
1544    Path = proplists:get_value(data_dir, Config),
1545    DrvName = proplists:get_value(testcase, Config),
1546    LoadResult = load_driver(Path, DrvName),
1547    case LoadResult of
1548        ok ->
1549            Port = open_port({spawn, DrvName}, []),
1550            true = is_port(Port),
1551            true = port_close(Port),
1552            ok = erl_ddll:unload_driver(DrvName);
1553        _ ->
1554            ok
1555    end.
1556
1557zero_extended_marker_garb_drv(Config) when is_list(Config) ->
1558    vsn_mismatch_test(Config, {error, driver_incorrect_version}).
1559
1560invalid_extended_marker_drv(Config) when is_list(Config) ->
1561    vsn_mismatch_test(Config, {error, driver_incorrect_version}).
1562
1563larger_major_vsn_drv(Config) when is_list(Config) ->
1564    vsn_mismatch_test(Config, {error, driver_incorrect_version}).
1565
1566larger_minor_vsn_drv(Config) when is_list(Config) ->
1567    vsn_mismatch_test(Config, {error, driver_incorrect_version}).
1568
1569smaller_major_vsn_drv(Config) when is_list(Config) ->
1570    vsn_mismatch_test(Config, {error, driver_incorrect_version}).
1571
1572smaller_minor_vsn_drv(Config) when is_list(Config) ->
1573    DrvVsnStr = erlang:system_info(driver_version),
1574    case drv_vsn_str2tup(DrvVsnStr) of
1575        {_, 0} ->
1576            {skipped,
1577             "Cannot perform test when minor driver version is 0. "
1578             "Current driver version is " ++ DrvVsnStr ++ "."};
1579        _ ->
1580            vsn_mismatch_test(Config, ok)
1581    end.
1582
1583-define(PEEK_NONXQ_TEST, 0).
1584-define(PEEK_NONXQ_WAIT, 1).
1585
1586peek_non_existing_queue(Config) when is_list(Config) ->
1587    OTE = process_flag(trap_exit, true),
1588    Drv = peek_non_existing_queue_drv,
1589    try
1590        begin
1591            case load_driver(proplists:get_value(data_dir, Config),
1592                             Drv) of
1593                ok -> ok;
1594                {error, permanent} -> ok;
1595                LoadError -> ct:fail({load_error, LoadError})
1596            end,
1597            case open_port({spawn, Drv}, []) of
1598                Port1 when is_port(Port1) ->
1599                    try port_control(Port1, ?PEEK_NONXQ_TEST, "") of
1600                        "ok" ->
1601                            ok;
1602                        [$s,$k,$i,$p,$p,$e,$d,$:,$ | SkipReason] ->
1603                            throw({skipped, SkipReason});
1604                        [$e,$r,$r,$o,$r,$:,$ | Error1] ->
1605                            ct:fail(Error1)
1606                    after
1607                        exit(Port1, kill),
1608                        receive {'EXIT', Port1, _} -> ok end
1609                    end;
1610                Error1 ->
1611                    ct:fail({open_port1_failed, Error1})
1612            end,
1613            case open_port({spawn, Drv}, []) of
1614                Port2 when is_port(Port2) ->
1615                    try port_control(Port2, ?PEEK_NONXQ_WAIT, "") of
1616                        "ok" ->
1617                            ok;
1618                        [$e,$r,$r,$o,$r,$:,$ | Error2] ->
1619                            ct:fail(Error2)
1620                    after
1621                        receive {Port2, test_successful} -> ok end,
1622                        Port2 ! {self(), close},
1623                        receive {Port2, closed} -> ok end
1624                    end;
1625                Error2 ->
1626                    ct:fail({open_port2_failed, Error2})
1627            end
1628        end
1629    catch
1630        throw:Term -> Term
1631    after
1632        process_flag(trap_exit, OTE),
1633        erl_ddll:unload_driver(Drv)
1634    end.
1635
1636otp_6879(Config) when is_list(Config) ->
1637    Drv = 'otp_6879_drv',
1638    Parent = self(),
1639    ok = load_driver(proplists:get_value(data_dir, Config), Drv),
1640    Procs = lists:map(
1641              fun (No) ->
1642                      spawn_link(
1643                        fun () ->
1644                                case open_port({spawn, Drv}, []) of
1645                                    Port when is_port(Port) ->
1646                                        Res = otp_6879_call(Port, No, 10000),
1647                                        erlang:port_close(Port),
1648                                        Parent ! {self(), Res};
1649                                    _ ->
1650                                        Parent ! {self(),
1651                                                  open_port_failed}
1652                                end
1653                        end)
1654              end,
1655              lists:seq(1,10)),
1656    lists:foreach(fun (P) ->
1657                          receive
1658                              {P, ok} ->
1659                                  ok;
1660                              {P, Error} ->
1661                                  ct:fail({P, Error})
1662                          end
1663                  end,
1664                  Procs),
1665    %% Also try it when input exceeds default buffer (256 bytes)
1666    Data = lists:seq(1, 1000),
1667    case open_port({spawn, Drv}, []) of
1668        Port when is_port(Port) ->
1669            ok = otp_6879_call(Port, Data, 10),
1670            erlang:port_close(Port);
1671        _ ->
1672            ct:fail(open_port_failed)
1673    end,
1674    erl_ddll:unload_driver(Drv),
1675    ok.
1676
1677otp_6879_call(_Port, _Data, 0) ->
1678    ok;
1679otp_6879_call(Port, Data, N) ->
1680    case catch erlang:port_call(Port, 0, Data) of
1681        Data -> otp_6879_call(Port, Data, N-1);
1682        BadData -> {mismatch, Data, BadData}
1683    end.
1684
1685caller(Config) when is_list(Config) ->
1686    run_caller_test(Config, false),
1687    run_caller_test(Config, true).
1688
1689run_caller_test(Config, Outputv) ->
1690    Drv = 'caller_drv',
1691    Cmd = case Outputv of
1692              true ->
1693                  os:putenv("CALLER_DRV_USE_OUTPUTV",
1694                            "true"),
1695                  outputv;
1696              false ->
1697                  os:putenv("CALLER_DRV_USE_OUTPUTV",
1698                            "false"),
1699                  output
1700          end,
1701    ok = load_driver(proplists:get_value(data_dir, Config), Drv),
1702    Port = open_port({spawn, Drv}, []),
1703    true = is_port(Port),
1704    chk_caller(Port, start, self()),
1705    chk_caller(Port,
1706               Cmd,
1707               spawn_link(
1708                 fun () ->
1709                         port_command(Port, "")
1710                 end)),
1711    Port ! {self(), {command, ""}},
1712    chk_caller(Port, Cmd, self()),
1713    chk_caller(Port,
1714               control,
1715               spawn_link(
1716                 fun () ->
1717                         port_control(Port, 0, "")
1718                 end)),
1719    chk_caller(Port,
1720               call,
1721               spawn_link(
1722                 fun () ->
1723                         erlang:port_call(Port, 0, "")
1724                 end)),
1725    true = port_close(Port),
1726    erl_ddll:unload_driver(Drv),
1727    ok.
1728
1729chk_caller(Port, Callback, ExpectedCaller) ->
1730    receive
1731        {caller, Port, Callback, Caller} ->
1732            ExpectedCaller = Caller
1733    end.
1734
1735%% Check that many simultaneously signalled events work (win32)
1736many_events(Config) when is_list(Config) ->
1737    Name = 'many_events_drv',
1738    Port = start_driver(Config, Name, false),
1739    Number = "1000",
1740    Port ! {self(), {command, Number}},
1741    receive
1742        {Port, {data,Number}} ->
1743            receive %% Just to make sure the emulator does not crash
1744                %% after this case is run (if faulty)
1745            after 2000 ->
1746                      ok
1747            end
1748    after 1000 ->
1749              exit(the_driver_does_not_respond)
1750    end,
1751    stop_driver(Port, Name),
1752    ok.
1753
1754
1755missing_callbacks(Config) when is_list(Config) ->
1756    Name = 'missing_callback_drv',
1757    Port = start_driver(Config, Name, false),
1758
1759    Port ! {self(), {command, "tjenix"}},
1760    true = erlang:port_command(Port, "halloj"),
1761    {'EXIT', {badarg, _}} = (catch erlang:port_control(Port, 4711, "mors")),
1762    {'EXIT', {badarg, _}} = (catch erlang:port_call(Port, 17, "hej")),
1763
1764    %% Give the (non-existing) ready_output(), ready_input(), event(),
1765    %% and timeout() some time to be called.
1766    receive after 1000 -> ok end,
1767
1768    stop_driver(Port, Name),
1769    ok.
1770
1771%% Test concurrent calls to driver_select.
1772smp_select(Config) when is_list(Config) ->
1773    case os:type() of
1774        {win32,_} -> {skipped, "Test not implemented for this OS"};
1775        _ -> rpc(Config, fun() -> smp_select0(Config) end)
1776    end.
1777
1778smp_select0(Config) ->
1779    DrvName = 'chkio_drv',
1780    Path = proplists:get_value(data_dir, Config),
1781    erl_ddll:start(),
1782    ok = load_driver(Path, DrvName),
1783    Master = self(),
1784    ProcFun = fun()-> io:format("Worker ~p starting\n",[self()]),
1785                      Port = open_port({spawn, DrvName}, []),
1786                      smp_select_loop(Port, 100000),
1787                      smp_select_done(Port),
1788                      true = erlang:port_close(Port),
1789                      Master ! {ok,self()},
1790                      io:format("Worker ~p finished\n",[self()])
1791              end,
1792    Pids = lists:map(fun(_) -> spawn_link(ProcFun) end,
1793                     lists:seq(1,4)),
1794    TimeoutMsg = make_ref(),
1795    {ok,TRef} = timer:send_after(5*1000, TimeoutMsg), % Limit test duration on slow machines
1796    smp_select_wait(Pids, TimeoutMsg),
1797    timer:cancel(TRef),
1798    ok = erl_ddll:unload_driver(DrvName),
1799    ok = erl_ddll:stop(),
1800    ok.
1801
1802smp_select_loop(_, 0) ->
1803    ok;
1804smp_select_loop(Port, N) ->
1805    case erlang:port_control(Port, ?CHKIO_SMP_SELECT, []) of
1806        "yield" -> erlang:yield();
1807        "ok" -> ok
1808    end,
1809    receive
1810        stop ->
1811            io:format("Worker ~p stopped with ~p laps left\n",[self(), N]),
1812            ok
1813    after 0 ->
1814              smp_select_loop(Port, N-1)
1815    end.
1816
1817smp_select_done(Port) ->
1818    case erlang:port_control(Port, ?CHKIO_SMP_SELECT, "done") of
1819        "wait" ->
1820            receive
1821                {Port, done} ->
1822                    ok
1823            after 10*1000 ->
1824                    %% Seems we have a lost ready_input event.
1825                    %% Go ahead anyway, port will crash VM when closed.
1826                    ok
1827            end;
1828
1829        "ok" -> ok
1830    end.
1831
1832smp_select_wait([], _) ->
1833    ok;
1834smp_select_wait(Pids, TimeoutMsg) ->
1835    receive
1836        {ok,Pid} when is_pid(Pid) ->
1837            smp_select_wait(lists:delete(Pid,Pids), TimeoutMsg);
1838        TimeoutMsg ->
1839            lists:foreach(fun(Pid)-> Pid ! stop end,
1840                          Pids),
1841            smp_select_wait(Pids, TimeoutMsg)
1842    end.
1843
1844
1845%% Test driver_select() with new ERL_DRV_USE flag.
1846driver_select_use(Config) when is_list(Config) ->
1847    case os:type() of
1848        {win32,_} -> {skipped, "Test not implemented for this OS"};
1849        _ -> rpc(Config, fun() -> driver_select_use0(Config) end)
1850    end.
1851
1852driver_select_use0(Config) ->
1853    DrvName = 'chkio_drv',
1854    Path = proplists:get_value(data_dir, Config),
1855    erl_ddll:start(),
1856    ok = load_driver(Path, DrvName),
1857    Port = open_port({spawn, DrvName}, []),
1858    "ok" = erlang:port_control(Port, ?CHKIO_DRV_USE, []),
1859    {Port,{data,"TheEnd"}} = receive Msg -> Msg
1860                             after 10000 -> timeout end,
1861    true = erlang:port_close(Port),
1862    ok = erl_ddll:unload_driver(DrvName),
1863    ok = erl_ddll:stop(),
1864    ok.
1865
1866lots_of_used_fds_on_boot(Config) ->
1867    case os:type() of
1868        {unix, _} -> lots_of_used_fds_on_boot_test(Config);
1869        _ -> {skipped, "Unix only test"}
1870    end.
1871
1872lots_of_used_fds_on_boot_test(Config) ->
1873    %% Start a node in a wrapper which have lots of fds
1874    %% open. This used to hang the whole VM at boot in
1875    %% an eternal loop trying to figure out how to size
1876    %% arrays in erts_poll() implementation.
1877    Name = lots_of_used_fds_on_boot,
1878    HostSuffix = lists:dropwhile(fun ($@) -> false; (_) -> true end,
1879				 atom_to_list(node())),
1880    FullName = list_to_atom(atom_to_list(Name) ++ HostSuffix),
1881    Pa = filename:dirname(code:which(?MODULE)),
1882    Prog = case catch init:get_argument(progname) of
1883	       {ok,[[P]]} -> P;
1884	       _ -> exit(no_progname_argument_found)
1885	   end,
1886    NameSw = case net_kernel:longnames() of
1887		 false -> "-sname ";
1888		 true -> "-name ";
1889		 _ -> exit(not_distributed_node)
1890	     end,
1891    {ok, Pwd} = file:get_cwd(),
1892    NameStr = atom_to_list(Name),
1893    DataDir = proplists:get_value(data_dir, Config),
1894    Wrapper = filename:join(DataDir, "lots_of_fds_used_wrapper"),
1895    CmdLine = Wrapper ++ " " ++ Prog ++ " -noshell -noinput "
1896	++ NameSw ++ " " ++ NameStr ++ " "
1897	++ "-pa " ++ Pa ++ " "
1898	++ "-env ERL_CRASH_DUMP " ++ Pwd ++ "/erl_crash_dump." ++ NameStr ++ " "
1899	++ "-setcookie " ++ atom_to_list(erlang:get_cookie()) ++ " "
1900        ++ "-s " ++ atom_to_list(?MODULE) ++ " lots_of_used_fds_on_boot_slave "
1901        ++ atom_to_list(node()),
1902    io:format("Starting node ~p: ~s~n", [FullName, CmdLine]),
1903    net_kernel:monitor_nodes(true),
1904    Port = case open_port({spawn, CmdLine}, [exit_status]) of
1905               Prt when is_port(Prt) ->
1906                   Prt;
1907               OPError ->
1908                   exit({failed_to_start_node, {open_port_error, OPError}})
1909           end,
1910    receive
1911        {Port, {exit_status, 17}} ->
1912            {skip, "Cannot open enough fds to test this"};
1913        {Port, {exit_status, Error}} ->
1914            exit({failed_to_start_node, {exit_status, Error}});
1915        {nodeup, FullName} ->
1916            io:format("~p connected!~n", [FullName]),
1917            FullName = rpc:call(FullName, erlang, node, []),
1918            rpc:cast(FullName, erlang, halt, []),
1919            receive
1920                {Port, {exit_status, 0}} ->
1921                    ok;
1922                {Port, {exit_status, Error}} ->
1923                    exit({unexpected_exit_status, Error})
1924            after 5000 ->
1925                    exit(missing_exit_status)
1926            end
1927    after 5000 ->
1928            exit(connection_timeout)
1929    end.
1930
1931lots_of_used_fds_on_boot_slave([Master]) ->
1932    erlang:monitor_node(Master, true),
1933    receive
1934        {nodedown, Master} ->
1935            erlang:halt()
1936    end,
1937    ok.
1938
1939thread_mseg_alloc_cache_clean(Config) when is_list(Config) ->
1940    case {erlang:system_info(threads),
1941          erlang:system_info({allocator,mseg_alloc}),
1942          driver_alloc_sbct()} of
1943        {_, false, _} ->
1944            {skipped, "No mseg_alloc"};
1945        {false, _, _} ->
1946            {skipped, "No threads"};
1947        {_, _, false} ->
1948            {skipped, "driver_alloc() not using the alloc_util framework"};
1949        {_, _, SBCT} when is_integer(SBCT), SBCT > 10*1024*1024 ->
1950            {skipped, "driver_alloc() using too large single block threshold"};
1951        {_, _, 0} ->
1952            {skipped, "driver_alloc() using too low single block threshold"};
1953        {true, _MsegAllocInfo, SBCT} ->
1954            DrvName = 'thr_alloc_drv',
1955            Path = proplists:get_value(data_dir, Config),
1956            erl_ddll:start(),
1957            ok = load_driver(Path, DrvName),
1958            Port = open_port({spawn, DrvName}, []),
1959            CCI = 1000,
1960            io:format("CCI = ~p~n", [CCI]),
1961            CCC = mseg_alloc_ccc(),
1962            io:format("CCC = ~p~n", [CCC]),
1963            thread_mseg_alloc_cache_clean_test(Port,
1964                                               10,
1965                                               CCI,
1966                                               SBCT+100),
1967            true = erlang:port_close(Port),
1968            ok = erl_ddll:unload_driver(DrvName),
1969            ok = erl_ddll:stop(),
1970            ok
1971    end.
1972
1973mseg_alloc_ccc() ->
1974    mseg_alloc_ccc(mseg_inst_info(0)).
1975
1976mseg_alloc_ccc(MsegAllocInfo) ->
1977    {value,{memkind, MKL}} = lists:keysearch(memkind,1,MsegAllocInfo),
1978    {value,{calls, CL}} = lists:keysearch(calls, 1, MKL),
1979    {value,{mseg_check_cache, GigaCCC, CCC}}
1980    = lists:keysearch(mseg_check_cache, 1, CL),
1981    GigaCCC*1000000000 + CCC.
1982
1983mseg_alloc_cached_segments() ->
1984    mseg_alloc_cached_segments(mseg_inst_info(0)).
1985
1986mseg_alloc_cached_segments(MsegAllocInfo) ->
1987    MemName = "all memory",
1988    [{memkind,DrvMem}]
1989    = lists:filter(fun(E) -> case E of
1990                                 {memkind, [{name, MemName} | _]} -> true;
1991                                 _ -> false
1992                             end end, MsegAllocInfo),
1993    {value,{status, SL}}
1994    = lists:keysearch(status, 1, DrvMem),
1995    {value,{cached_segments, CS}}
1996    = lists:keysearch(cached_segments, 1, SL),
1997    CS.
1998
1999mseg_inst_info(I) ->
2000    {value, {instance, I, Value}}
2001    = lists:keysearch(I,
2002                      2,
2003                      erlang:system_info({allocator,mseg_alloc})),
2004    Value.
2005
2006driver_alloc_sbct() ->
2007    {_, _, _, As} = erlang:system_info(allocator),
2008    case lists:keysearch(driver_alloc, 1, As) of
2009        {value,{driver_alloc,DAOPTs}} ->
2010            case lists:keysearch(sbct, 1, DAOPTs) of
2011                {value,{sbct,SBCT}} ->
2012                    SBCT;
2013                _ ->
2014                    false
2015            end;
2016        _ ->
2017            false
2018    end.
2019
2020thread_mseg_alloc_cache_clean_test(_Port, 0, _CCI, _Size) ->
2021    ok;
2022thread_mseg_alloc_cache_clean_test(Port, N, CCI, Size) ->
2023    wait_until(fun () -> 0 == mseg_alloc_cached_segments() end),
2024    receive after CCI+500 -> ok end,
2025    OCCC = mseg_alloc_ccc(),
2026    "ok" = erlang:port_control(Port, 0, integer_to_list(Size)),
2027    receive after CCI+500 -> ok end,
2028    CCC = mseg_alloc_ccc(),
2029    io:format("CCC = ~p~n", [CCC]),
2030    true = CCC > OCCC,
2031    thread_mseg_alloc_cache_clean_test(Port, N-1, CCI, Size).
2032
2033otp_9302(Config) when is_list(Config) ->
2034    Path = proplists:get_value(data_dir, Config),
2035    erl_ddll:start(),
2036    ok = load_driver(Path, otp_9302_drv),
2037    Port = open_port({spawn, otp_9302_drv}, []),
2038    true = is_port(Port),
2039    port_command(Port, ""),
2040    {msg, block} = get_port_msg(Port, infinity),
2041    {msg, job} = get_port_msg(Port, infinity),
2042    C = case erlang:system_info(thread_pool_size) of
2043            0 ->
2044                {msg, cancel} = get_port_msg(Port, infinity),
2045                {msg, job} = get_port_msg(Port, infinity),
2046                false;
2047            _ ->
2048                case get_port_msg(Port, infinity) of
2049                    {msg, cancel} -> %% Cancel always fail in Rel >= 15
2050                        {msg, job} = get_port_msg(Port, infinity),
2051                        false;
2052                    {msg, job} ->
2053                        ok,
2054                        true
2055                end
2056        end,
2057    {msg, end_of_jobs} = get_port_msg(Port, infinity),
2058    no_msg = get_port_msg(Port, 2000),
2059    port_close(Port),
2060    case C of
2061        true ->
2062            {comment, "Async job cancelled"};
2063        false ->
2064            {comment, "Async job not cancelled"}
2065    end.
2066
2067thr_free_drv(Config) when is_list(Config) ->
2068    case erlang:system_info(threads) of
2069        false ->
2070            {skipped, "No thread support"};
2071        true ->
2072            thr_free_drv_do(Config)
2073    end.
2074
2075thr_free_drv_do(Config) ->
2076    Path = proplists:get_value(data_dir, Config),
2077    erl_ddll:start(),
2078    ok = load_driver(Path, thr_free_drv),
2079    MemBefore = driver_alloc_size(),
2080    %    io:format("SID=~p", [erlang:system_info(scheduler_id)]),
2081    Port = open_port({spawn, thr_free_drv}, []),
2082    MemPeek = driver_alloc_size(),
2083    true = is_port(Port),
2084    ok = thr_free_drv_control(Port, 0),
2085    port_close(Port),
2086    MemAfter = driver_alloc_size(),
2087    io:format("MemPeek=~p~n", [MemPeek]),
2088    io:format("MemBefore=~p, MemAfter=~p~n", [MemBefore, MemAfter]),
2089    MemBefore = MemAfter,
2090    case MemPeek of
2091        undefined -> ok;
2092        _ ->
2093            true = MemPeek > MemBefore
2094    end,
2095    ok.
2096
2097thr_free_drv_control(Port, N) ->
2098    case erlang:port_control(Port, 0, "") of
2099        "done" ->
2100            ok;
2101        "more" ->
2102            erlang:yield(),
2103            %	    io:format("N=~p, SID=~p", [N, erlang:system_info(scheduler_id)]),
2104            thr_free_drv_control(Port, N+1)
2105    end.
2106
2107async_blast(Config) when is_list(Config) ->
2108    Path = proplists:get_value(data_dir, Config),
2109    erl_ddll:start(),
2110    ok = load_driver(Path, async_blast_drv),
2111    SchedOnln = erlang:system_info(schedulers_online),
2112    MemBefore = driver_alloc_size(),
2113    Start = os:timestamp(),
2114    Blast = fun () ->
2115                    Port = open_port({spawn, async_blast_drv}, []),
2116                    true = is_port(Port),
2117                    port_command(Port, ""),
2118                    receive
2119                        {Port, done} ->
2120                            ok
2121                    end,
2122                    port_close(Port)
2123            end,
2124    Ps = lists:map(fun (N) ->
2125                           spawn_opt(Blast,
2126                                     [{scheduler,
2127                                       (N rem SchedOnln)+ 1},
2128                                      monitor])
2129                   end,
2130                   lists:seq(1, 100)),
2131    MemMid = driver_alloc_size(),
2132    lists:foreach(fun ({Pid, Mon}) ->
2133                          receive
2134                              {'DOWN',Mon,process,Pid,_} -> ok
2135                          end
2136                  end, Ps),
2137    End = os:timestamp(),
2138    MemAfter = driver_alloc_size(),
2139    io:format("MemBefore=~p, MemMid=~p, MemAfter=~p~n",
2140              [MemBefore, MemMid, MemAfter]),
2141    AsyncBlastTime = timer:now_diff(End,Start)/1000000,
2142    io:format("AsyncBlastTime=~p~n", [AsyncBlastTime]),
2143    MemBefore = MemAfter,
2144    erlang:display({async_blast_time, AsyncBlastTime}),
2145    ok.
2146
2147thr_msg_blast_receiver(_Port, N, N) ->
2148    ok;
2149thr_msg_blast_receiver(Port, N, Max) ->
2150    receive
2151        {Port, hi} ->
2152            thr_msg_blast_receiver(Port, N+1, Max)
2153    end.
2154
2155thr_msg_blast_receiver_proc(Port, Max, Parent, Done) ->
2156    case port_control(Port, 0, "") of
2157        "receiver" ->
2158            spawn(fun () ->
2159                          thr_msg_blast_receiver_proc(Port, Max+1, Parent, Done)
2160                  end),
2161            thr_msg_blast_receiver(Port, 0, Max);
2162        "done" ->
2163            Parent ! Done
2164    end.
2165
2166thr_msg_blast(Config) when is_list(Config) ->
2167    Path = proplists:get_value(data_dir, Config),
2168    erl_ddll:start(),
2169    ok = load_driver(Path, thr_msg_blast_drv),
2170    MemBefore = driver_alloc_size(),
2171    Start = os:timestamp(),
2172    Port = open_port({spawn, thr_msg_blast_drv}, []),
2173    true = is_port(Port),
2174    Done = make_ref(),
2175    Me = self(),
2176    spawn(fun () ->
2177                  thr_msg_blast_receiver_proc(Port, 1, Me, Done)
2178          end),
2179    receive
2180        Done -> ok
2181    end,
2182    ok = thr_msg_blast_receiver(Port, 0, 32*10000),
2183    port_close(Port),
2184    End = os:timestamp(),
2185    receive
2186        Garbage ->
2187            ct:fail({received_garbage, Port, Garbage})
2188    after 2000 ->
2189            ok
2190    end,
2191    MemAfter = driver_alloc_size(),
2192    io:format("MemBefore=~p, MemAfter=~p~n",
2193              [MemBefore, MemAfter]),
2194    ThrMsgBlastTime = timer:now_diff(End,Start)/1000000,
2195    io:format("ThrMsgBlastTime=~p~n", [ThrMsgBlastTime]),
2196    MemBefore = MemAfter,
2197    Res = {thr_msg_blast_time, ThrMsgBlastTime},
2198    erlang:display(Res),
2199    Res.
2200
2201-define(IN_RANGE(LoW_, VaLuE_, HiGh_),
2202        case in_range(LoW_, VaLuE_, HiGh_) of
2203            true -> ok;
2204            false ->
2205                case erlang:system_info(lock_checking) of
2206                    true ->
2207                        io:format("~p:~p: Ignore bad sched count due to "
2208                                  "lock checking~n",
2209                                  [?MODULE,?LINE]);
2210                    false ->
2211                        ct:fail({unexpected_sched_counts, VaLuE_})
2212                end
2213        end).
2214
2215
2216consume_timeslice(Config) when is_list(Config) ->
2217    %%
2218    %% Verify that erl_drv_consume_timeslice() works.
2219    %%
2220    %% The first four cases expect that the command signal is
2221    %% delivered immediately, i.e., isn't scheduled. Since there
2222    %% are no conflicts these signals should normally be delivered
2223    %% immediately. However some builds and configurations may
2224    %% schedule these ops anyway, in these cases we do not verify
2225    %% scheduling counts.
2226    %%
2227    %% When signal is delivered immediately we must take into account
2228    %% that process and port are "virtualy" scheduled out and in
2229    %% in the trace generated.
2230    %%
2231    %% Port ! {_, {command, _}, and port_command() differs. The send
2232    %% instruction needs to check if the caller is out of reductions
2233    %% at the end of the instruction, since no erlang function call
2234    %% is involved. Otherwise, a sequence of send instructions would
2235    %% not be scheduled out even when out of reductions. port_commond()
2236    %% doesn't do that since it will always (since R16A) be called via
2237    %% the erlang wrappers in the erlang module.
2238    %%
2239    %% The last two cases tests scheduled operations. We create
2240    %% a conflict by executing at the same time on different
2241    %% schedulers. When only one scheduler we enable parallelism on
2242    %% the port instead.
2243    %%
2244
2245    Path = proplists:get_value(data_dir, Config),
2246    erl_ddll:start(),
2247    ok = load_driver(Path, consume_timeslice_drv),
2248    Port = open_port({spawn, consume_timeslice_drv}, [{parallelism, false}]),
2249
2250    Parent = self(),
2251    Go = make_ref(),
2252
2253    "enabled" = port_control(Port, $E, ""),
2254    Proc1 = spawn_link(fun () ->
2255                               receive Go -> ok end,
2256                               Port ! {Parent, {command, ""}},
2257                               Port ! {Parent, {command, ""}},
2258                               Port ! {Parent, {command, ""}},
2259                               Port ! {Parent, {command, ""}},
2260                               Port ! {Parent, {command, ""}},
2261                               Port ! {Parent, {command, ""}},
2262                               Port ! {Parent, {command, ""}},
2263                               Port ! {Parent, {command, ""}},
2264                               Port ! {Parent, {command, ""}},
2265                               Port ! {Parent, {command, ""}}
2266                       end),
2267    receive after 100 -> ok end,
2268    count_pp_sched_start(),
2269    Proc1 ! Go,
2270    wait_command_msgs(Port, 10),
2271    [{Port, Sprt1}, {Proc1, Sproc1}] = count_pp_sched_stop([Port, Proc1]),
2272    ?IN_RANGE(10, Sprt1, 10),
2273    ?IN_RANGE(5, Sproc1-10, 7),
2274
2275    "disabled" = port_control(Port, $D, ""),
2276    Proc2 = spawn_link(fun () ->
2277                               receive Go -> ok end,
2278                               Port ! {Parent, {command, ""}},
2279                               Port ! {Parent, {command, ""}},
2280                               Port ! {Parent, {command, ""}},
2281                               Port ! {Parent, {command, ""}},
2282                               Port ! {Parent, {command, ""}},
2283                               Port ! {Parent, {command, ""}},
2284                               Port ! {Parent, {command, ""}},
2285                               Port ! {Parent, {command, ""}},
2286                               Port ! {Parent, {command, ""}},
2287                               Port ! {Parent, {command, ""}}
2288                       end),
2289    receive after 100 -> ok end,
2290    count_pp_sched_start(),
2291    Proc2 ! Go,
2292    wait_command_msgs(Port, 10),
2293    [{Port, Sprt2}, {Proc2, Sproc2}] = count_pp_sched_stop([Port, Proc2]),
2294    ?IN_RANGE(10, Sprt2, 10),
2295    ?IN_RANGE(1, Sproc2-10, 2),
2296
2297    "enabled" = port_control(Port, $E, ""),
2298    Proc3 = spawn_link(fun () ->
2299                               receive Go -> ok end,
2300                               port_command(Port, ""),
2301                               port_command(Port, ""),
2302                               port_command(Port, ""),
2303                               port_command(Port, ""),
2304                               port_command(Port, ""),
2305                               port_command(Port, ""),
2306                               port_command(Port, ""),
2307                               port_command(Port, ""),
2308                               port_command(Port, ""),
2309                               port_command(Port, "")
2310                       end),
2311    count_pp_sched_start(),
2312    Proc3 ! Go,
2313    wait_command_msgs(Port, 10),
2314    [{Port, Sprt3}, {Proc3, Sproc3}] = count_pp_sched_stop([Port, Proc3]),
2315    ?IN_RANGE(10, Sprt3, 10),
2316    ?IN_RANGE(5, Sproc3-10, 7),
2317
2318    "disabled" = port_control(Port, $D, ""),
2319    Proc4 = spawn_link(fun () ->
2320                               receive Go -> ok end,
2321                               port_command(Port, ""),
2322                               port_command(Port, ""),
2323                               port_command(Port, ""),
2324                               port_command(Port, ""),
2325                               port_command(Port, ""),
2326                               port_command(Port, ""),
2327                               port_command(Port, ""),
2328                               port_command(Port, ""),
2329                               port_command(Port, ""),
2330                               port_command(Port, "")
2331                       end),
2332    count_pp_sched_start(),
2333    Proc4 ! Go,
2334    wait_command_msgs(Port, 10),
2335    [{Port, Sprt4}, {Proc4, Sproc4}] = count_pp_sched_stop([Port, Proc4]),
2336    ?IN_RANGE(10, Sprt4, 10),
2337    ?IN_RANGE(1, Sproc4-10, 2),
2338
2339    SOnl = erlang:system_info(schedulers_online),
2340    %% If only one scheduler use port with parallelism set to true,
2341    %% in order to trigger scheduling of command signals
2342    Port2 = case SOnl of
2343                1 ->
2344                    Port ! {self(), close},
2345                    receive {Port, closed} -> ok end,
2346                    open_port({spawn, consume_timeslice_drv},
2347                              [{parallelism, true}]);
2348                _ ->
2349                    process_flag(scheduler, 1),
2350                    1 = erlang:system_info(scheduler_id),
2351                    Port
2352            end,
2353    count_pp_sched_start(),
2354    "enabled" = port_control(Port2, $E, ""),
2355    W5 = case SOnl of
2356             1 ->
2357                 false;
2358             _ ->
2359                 W1= spawn_opt(fun () ->
2360                                       2 = erlang:system_info(scheduler_id),
2361                                       "sleeped" = port_control(Port2, $S, "")
2362                               end, [link,{scheduler,2}]),
2363                 receive after 100 -> ok end,
2364                 W1
2365         end,
2366    Proc5 = spawn_opt(fun () ->
2367                              receive Go -> ok end,
2368                              1 = erlang:system_info(scheduler_id),
2369                              Port2 ! {Parent, {command, ""}},
2370                              Port2 ! {Parent, {command, ""}},
2371                              Port2 ! {Parent, {command, ""}},
2372                              Port2 ! {Parent, {command, ""}},
2373                              Port2 ! {Parent, {command, ""}},
2374                              Port2 ! {Parent, {command, ""}},
2375                              Port2 ! {Parent, {command, ""}},
2376                              Port2 ! {Parent, {command, ""}},
2377                              Port2 ! {Parent, {command, ""}},
2378                              Port2 ! {Parent, {command, ""}}
2379                      end, [link,{scheduler,1}]),
2380    receive after 100 -> ok end,
2381    Proc5 ! Go,
2382    wait_procs_exit([W5, Proc5]),
2383    wait_command_msgs(Port2, 10),
2384    [{Port2, Sprt5}, {Proc5, Sproc5}] = count_pp_sched_stop([Port2, Proc5]),
2385    ?IN_RANGE(2, Sproc5, 3),
2386    ?IN_RANGE(6, Sprt5, 20),
2387
2388    count_pp_sched_start(),
2389    "disabled" = port_control(Port2, $D, ""),
2390    W6 = case SOnl of
2391             1 ->
2392                 false;
2393             _ ->
2394                 W2= spawn_opt(fun () ->
2395                                       2 = erlang:system_info(scheduler_id),
2396                                       "sleeped" = port_control(Port2, $S, "")
2397                               end, [link,{scheduler,2}]),
2398                 receive after 100 -> ok end,
2399                 W2
2400         end,
2401    Proc6 = spawn_opt(fun () ->
2402                              receive Go -> ok end,
2403                              1 = erlang:system_info(scheduler_id),
2404                              Port2 ! {Parent, {command, ""}},
2405                              Port2 ! {Parent, {command, ""}},
2406                              Port2 ! {Parent, {command, ""}},
2407                              Port2 ! {Parent, {command, ""}},
2408                              Port2 ! {Parent, {command, ""}},
2409                              Port2 ! {Parent, {command, ""}},
2410                              Port2 ! {Parent, {command, ""}},
2411                              Port2 ! {Parent, {command, ""}},
2412                              Port2 ! {Parent, {command, ""}},
2413                              Port2 ! {Parent, {command, ""}}
2414                      end, [link,{scheduler,1}]),
2415    receive after 100 -> ok end,
2416    Proc6 ! Go,
2417    wait_procs_exit([W6, Proc6]),
2418    wait_command_msgs(Port2, 10),
2419    [{Port2, Sprt6}, {Proc6, Sproc6}] = count_pp_sched_stop([Port2, Proc6]),
2420    ?IN_RANGE(2, Sproc6, 3),
2421    ?IN_RANGE(2, Sprt6, 6),
2422
2423    process_flag(scheduler, 0),
2424
2425    Port2 ! {self(), close},
2426    receive {Port2, closed} -> ok end,
2427    ok.
2428
2429
2430wait_command_msgs(_, 0) ->
2431    ok;
2432wait_command_msgs(Port, N) ->
2433    receive
2434        {Port, command} ->
2435            wait_command_msgs(Port, N-1)
2436    end.
2437
2438in_range(Low, Val, High) when is_integer(Low),
2439                              is_integer(Val),
2440                              is_integer(High),
2441                              Low =< Val,
2442                              Val =< High ->
2443    true;
2444in_range(Low, Val, High) when is_integer(Low),
2445                              is_integer(Val),
2446                              is_integer(High) ->
2447    false.
2448
2449count_pp_sched_start() ->
2450    erlang:trace(all, true, [running_procs, running_ports, {tracer, self()}]),
2451    ok.
2452
2453count_pp_sched_stop(Ps) ->
2454    Td = erlang:trace_delivered(all),
2455    erlang:trace(all, false, [running_procs, running_ports, {tracer, self()}]),
2456    PNs = lists:map(fun (P) -> {P, 0} end, Ps),
2457    receive {trace_delivered, all, Td} -> ok end,
2458    Res = count_proc_sched(Ps, PNs),
2459    io:format("Scheduling counts: ~p~n", [Res]),
2460    erlang:display({scheduling_counts, Res}),
2461    Res.
2462
2463do_inc_pn(_P, []) ->
2464    throw(undefined);
2465do_inc_pn(P, [{P,N}|PNs]) ->
2466    [{P,N+1}|PNs];
2467do_inc_pn(P, [PN|PNs]) ->
2468    [PN|do_inc_pn(P, PNs)].
2469
2470inc_pn(P, PNs) ->
2471    try
2472        do_inc_pn(P, PNs)
2473    catch
2474        throw:undefined -> PNs
2475    end.
2476
2477count_proc_sched(Ps, PNs) ->
2478    receive
2479        TT when element(1, TT) == trace, element(3, TT) == in ->
2480            %	    erlang:display(TT),
2481            count_proc_sched(Ps, inc_pn(element(2, TT), PNs));
2482        TT when element(1, TT) == trace, element(3, TT) == out ->
2483            count_proc_sched(Ps, PNs)
2484    after 0 ->
2485              PNs
2486    end.
2487
2488%%
2489%% Tests whether erl_drv_putenv reflects in os:getenv and vice versa.
2490%%
2491env(Config) when is_list(Config) ->
2492    ok = load_driver(proplists:get_value(data_dir, Config), env_drv),
2493    Port = open_port({spawn_driver, env_drv}, []),
2494    true = is_port(Port),
2495
2496    Keys = ["env_drv_a_key", "env_drv_b_key", "env_drv_c_key"],
2497    Values = ["a_value", "b_value", "c_value"],
2498
2499    [env_put_test(Port, Key, Value) || Key <- Keys, Value <- Values],
2500    [env_get_test(Port, Key, Value) || Key <- Keys, Value <- Values],
2501    [env_oversize_test(Port, Key) || Key <- Keys],
2502    [env_notfound_test(Port, Key) || Key <- Keys],
2503
2504    true = port_close(Port),
2505    erl_ddll:unload_driver(env_drv),
2506    ok.
2507
2508env_control(Port, Command, Key, Value) ->
2509    KeyBin = list_to_binary(Key),
2510    ValueBin = list_to_binary(Value),
2511    Header = <<(byte_size(KeyBin)), (byte_size(ValueBin))>>,
2512    Payload = <<KeyBin/binary, ValueBin/binary>>,
2513    port_control(Port, Command, <<Header/binary, Payload/binary>>).
2514
2515env_put_test(Port, Key, Value) ->
2516    os:unsetenv(Key),
2517    [0] = env_control(Port, 0, Key, Value),
2518    Value = os:getenv(Key).
2519
2520env_get_test(Port, Key, ExpectedValue) ->
2521    true = os:putenv(Key, ExpectedValue),
2522    [0] = env_control(Port, 1, Key, ExpectedValue).
2523
2524env_oversize_test(Port, Key) ->
2525    os:putenv(Key, [$A || _ <- lists:seq(1, 1024)]),
2526    [127] = env_control(Port, 1, Key, "").
2527
2528env_notfound_test(Port, Key) ->
2529    true = os:unsetenv(Key),
2530    [255] = env_control(Port, 1, Key, "").
2531
2532
2533a_test(Config) when is_list(Config) ->
2534    rpc(Config, fun check_io_debug/0).
2535
2536z_test(Config) when is_list(Config) ->
2537    rpc(Config, fun check_io_debug/0).
2538
2539%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2540%% 		Utilities
2541%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2542
2543check_io_debug() ->
2544    get_stable_check_io_info(),
2545    {NoErrorFds, NoUsedFds, NoDrvSelStructs, NoEnifSelStructs}
2546        = CheckIoDebug = erts_debug:get_internal_state(check_io_debug),
2547    HasGetHost = has_gethost(),
2548    ct:log("check_io_debug: ~p~n"
2549           "HasGetHost: ~p",[CheckIoDebug, HasGetHost]),
2550    0 = NoErrorFds,
2551    if
2552        NoUsedFds == NoDrvSelStructs ->
2553            ok;
2554        HasGetHost andalso (NoUsedFds == (NoDrvSelStructs - 1)) ->
2555            %% If the inet_gethost port is alive, we may have
2556            %% one extra used fd that is not selected on
2557            ok
2558    end,
2559    0 = NoEnifSelStructs,
2560    ok.
2561
2562has_gethost() ->
2563    has_gethost(erlang:ports()).
2564has_gethost([P|T]) ->
2565    case erlang:port_info(P, name) of
2566        {name,"inet_gethost"++_} ->
2567            true;
2568        _ ->
2569            has_gethost(T)
2570    end;
2571has_gethost([]) ->
2572    false.
2573
2574%flush_msgs() ->
2575%    receive
2576%	M ->
2577%	    erlang:display(M),
2578%	    flush_msgs()
2579%    after 0 ->
2580%	    ok
2581%    end.
2582
2583wait_procs_exit([]) ->
2584    ok;
2585wait_procs_exit([P|Ps]) when is_pid(P) ->
2586    Mon = erlang:monitor(process, P),
2587    receive
2588        {'DOWN', Mon, process, P, _} ->
2589            wait_procs_exit(Ps)
2590    end;
2591wait_procs_exit([_|Ps]) ->
2592    wait_procs_exit(Ps).
2593
2594get_port_msg(Port, Timeout) ->
2595    receive
2596        {Port, What} ->
2597            {msg, What}
2598    after Timeout ->
2599              no_msg
2600    end.
2601
2602wait_until(Fun) ->
2603    case Fun() of
2604        true -> ok;
2605        false ->
2606            receive after 100 -> ok end,
2607            wait_until(Fun)
2608    end.
2609
2610drv_vsn_str2tup(Str) ->
2611    [Major, Minor] = string:lexemes(Str, "."),
2612    {list_to_integer(Major), list_to_integer(Minor)}.
2613
2614%% Build port data from a template.
2615
2616build_data({bin,Size})  -> build_binary(Size);
2617build_data({list,Size}) -> build_list(Size);
2618build_data(int) -> random_char();
2619build_data([])  -> [];
2620build_data([H|T]) -> [build_data(H)|build_data(T)].
2621
2622%% Transform all binaries in a term.
2623
2624transform_bins(_Transform, []) -> [];
2625transform_bins(Transform, [H|T]) ->
2626    [transform_bins(Transform, H)|transform_bins(Transform, T)];
2627transform_bins(Transform, Tuple) when is_tuple(Tuple) ->
2628    list_to_tuple([transform_bins(Transform, E) || E <- tuple_to_list(Tuple)]);
2629transform_bins(Transform, Bin) when is_binary(Bin) ->
2630    Transform(Bin);
2631transform_bins(_Transform, Other) -> Other.
2632
2633
2634%% Convert all binaries in a term to sub binaries.
2635
2636make_sub_binaries(Term) ->
2637    MakeSub = fun(Bin0) ->
2638                      Bin1 = <<243:8,0:3,Bin0/binary,31:5,19:8>>,
2639                      Sz = size(Bin0),
2640                      <<243:8,0:3,Bin:Sz/binary,31:5,19:8>> = id(Bin1),
2641                      Bin
2642              end,
2643    transform_bins(MakeSub, Term).
2644
2645id(I) -> I.
2646
2647%% Convert all binaries in a term to refc binaries.
2648
2649make_refc_binaries(Term) ->
2650    F = fun(B0) -> list_to_binary([build_binary(?heap_binary_size+1),B0]) end,
2651    transform_bins(F, Term).
2652
2653build_binary(Elements) ->
2654    list_to_binary(build_list(Elements)).
2655
2656build_list(Elements) -> build_list(Elements, []).
2657
2658build_list(0, Acc) -> Acc;
2659build_list(Elements, Acc) -> build_list(Elements-1, [random_char()|Acc]).
2660
2661
2662%% Convert all binaries in a list to writable binaries.
2663
2664make_writable_binaries(Term) ->
2665    transform_bins(fun(Bin) -> <<Bin/binary,1,2,3>> end, Term).
2666
2667append_to_writable_binaries(Term) ->
2668    transform_bins(fun(Bin) -> <<Bin/binary,0:(64*1024*8)>> end, Term).
2669
2670random_char() ->
2671    uniform(256) - 1.
2672
2673uniform(N) ->
2674    rand:uniform(N).
2675
2676erl_millisecs() ->
2677    erl_millisecs(erlang:monotonic_time()).
2678
2679erl_millisecs(MonotonicTime) ->
2680    (1000*MonotonicTime)/erlang:convert_time_unit(1,second,native).
2681
2682%% Start/stop drivers.
2683start_driver(Config, Name, Binary) ->
2684    Path = proplists:get_value(data_dir, Config),
2685    erl_ddll:start(),
2686
2687    %% Load the driver
2688    ok = load_driver(Path, Name),
2689
2690    %% open port.
2691    case Binary of
2692        true ->
2693            open_port({spawn, Name}, [binary]);
2694        false ->
2695            open_port({spawn, Name}, [])
2696    end.
2697
2698stop_driver(Port, Name) ->
2699    true = erlang:port_close(Port),
2700    receive
2701        {Port,Message} ->
2702            ct:fail({strange_message_from_port,Message})
2703    after 0 ->
2704              ok
2705    end,
2706
2707    %% Unload the driver.
2708    ok = erl_ddll:unload_driver(Name),
2709    ok = erl_ddll:stop().
2710
2711load_driver(Dir, Driver) ->
2712    Before = erlang:system_info(taints),
2713    case erl_ddll:load_driver(Dir, Driver) of
2714        ok ->
2715            After = erlang:system_info(taints),
2716            case lists:member(Driver, Before) of
2717                true ->
2718                    After = Before;
2719                false ->
2720                    true = lists:member(Driver, After),
2721                    Before = lists:delete(Driver, After)
2722            end,
2723            ok;
2724        {error, Error} = Res ->
2725            io:format("~s\n", [erl_ddll:format_error(Error)]),
2726            Res
2727    end.
2728
2729sleep() ->
2730    receive after infinity -> ok end.
2731
2732sleep(infinity) ->
2733    sleep();
2734sleep(Ms) when is_integer(Ms), Ms >= 0 ->
2735    receive after Ms -> ok end.
2736
2737
2738start_node(Config) when is_list(Config) ->
2739    start_node(proplists:get_value(testcase, Config));
2740start_node(Name) ->
2741    start_node(Name, "").
2742start_node(NodeName, Args) ->
2743    Name = list_to_atom(atom_to_list(?MODULE)
2744                        ++ "-"
2745                        ++ atom_to_list(NodeName)
2746                        ++ "-"
2747                        ++ integer_to_list(erlang:system_time(second))
2748                        ++ "-"
2749                        ++ integer_to_list(erlang:unique_integer([positive]))),
2750    start_node_final(Name, Args).
2751start_node_final(Name, Args) ->
2752    {ok, Pwd} = file:get_cwd(),
2753    FinalArgs = [Args, " -pa ", filename:dirname(code:which(?MODULE))],
2754    {ok, Node} = test_server:start_node(Name, slave, [{args, FinalArgs}]),
2755    LogPath = Pwd ++ "/error_log." ++ atom_to_list(Name),
2756    ct:pal("Logging to: ~s", [LogPath]),
2757    rpc:call(Node, logger, add_handler, [file_handler, logger_std_h,
2758                                         #{formatter => {logger_formatter,#{ single_line => false }},
2759                                           config => #{file => LogPath }}]),
2760    {ok, Node}.
2761
2762stop_node(Node) ->
2763    test_server:stop_node(Node).
2764
2765wait_deallocations() ->
2766    try
2767        erts_debug:set_internal_state(wait, deallocations)
2768    catch error:undef ->
2769              erts_debug:set_internal_state(available_internal_state, true),
2770              wait_deallocations()
2771    end.
2772
2773driver_alloc_size() ->
2774    wait_deallocations(),
2775    erts_debug:alloc_blocks_size(driver_alloc).
2776
2777rpc(Config, Fun) ->
2778    case proplists:get_value(node, Config) of
2779        undefined ->
2780            Fun();
2781        Node ->
2782            Self = self(),
2783            Ref = make_ref(),
2784            Pid = spawn(Node,
2785                        fun() ->
2786                                Result
2787                                    = try Fun() of
2788                                          Res -> Res
2789                                      catch E:R:Stk ->
2790                                              {'EXIT',E,R,Stk}
2791                                      end,
2792                                Self ! {Ref, Result}
2793                        end),
2794            MRef = monitor(process, Pid),
2795            receive
2796                {'DOWN', MRef, _Type, _Object, Info} ->
2797                    erlang:error({died, Pid, Info});
2798                {Ref, {'EXIT',E,R,ST}} ->
2799                    erlang:demonitor(MRef, [flush]),
2800                    erlang:raise(E,R,ST);
2801                {Ref, Ret} ->
2802                    erlang:demonitor(MRef, [flush]),
2803                    Ret;
2804                Other ->
2805                    ct:fail(Other)
2806            end
2807    end.
2808
2809poll_pipe(Config) when is_list(Config) ->
2810    %% ERL-647; we wouldn't see any events on EOF when polling a pipe using
2811    %% kqueue(2).
2812    case os:type() of
2813        {unix, _} ->
2814            Command = "erl -noshell -eval "
2815                      "'\"DATA\n\" = io:get_line(\"\"),"
2816                      "eof = io:get_line(\"\"),"
2817                      "halt()' <<< 'DATA'",
2818            Ref = make_ref(),
2819            Self = self(),
2820            Pid = spawn(fun() -> os:cmd(Command), Self ! Ref end),
2821            receive
2822                Ref -> ok
2823            after 5000 ->
2824                exit(Pid, kill),
2825                ct:fail("Stuck reading from stdin.")
2826            end;
2827        _ ->
2828            {skipped, "Unix-only test"}
2829    end.
2830