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