1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 1999-2018. 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 21 22-module(port_trace_SUITE). 23 24-export([all/0, suite/0,init_per_suite/1, end_per_suite/1, 25 init_per_group/2,end_per_group/2, 26 init_per_testcase/2,end_per_testcase/2]). 27-export([port_specs/1, ports/1, open_close/1, 28 command/1, control/1, connect/1, call/1, 29 output/1, output2/1, output_binary/1, 30 outputv/1, set_timer/1, failure_eof/1, 31 failure_atom/1, failure_posix/1, 32 failure/1, output_term/1, 33 driver_output_term/1, 34 send_term/1, driver_send_term/1, 35 driver_remote_send_term/1]). 36 37-define(ECHO_DRV_NOOP, 0). 38-define(ECHO_DRV_OUTPUT, 1). 39-define(ECHO_DRV_OUTPUT2, 2). 40-define(ECHO_DRV_OUTPUT_BINARY, 3). 41-define(ECHO_DRV_OUTPUTV, 4). 42-define(ECHO_DRV_SET_TIMER, 5). 43-define(ECHO_DRV_FAILURE_EOF, 6). 44-define(ECHO_DRV_FAILURE_ATOM, 7). 45-define(ECHO_DRV_FAILURE_POSIX, 8). 46-define(ECHO_DRV_FAILURE, 9). 47-define(ECHO_DRV_OUTPUT_TERM, 10). 48-define(ECHO_DRV_DRIVER_OUTPUT_TERM, 11). 49-define(ECHO_DRV_SEND_TERM, 12). 50-define(ECHO_DRV_DRIVER_SEND_TERM, 13). 51-define(ECHO_DRV_SAVE_CALLER, 14). 52-define(ECHO_DRV_REMOTE_SEND_TERM, 15). 53 54suite() -> [{ct_hooks,[ts_install_cth]}, 55 {timetrap, {minutes, 2}}]. 56 57all() -> 58 [port_specs, ports, open_close, 59 command, control, connect, call, 60 output, output2, output_binary, 61 outputv, set_timer, failure_eof, 62 failure_atom, failure_posix, 63 failure, output_term, 64 driver_output_term, 65 send_term, driver_send_term, 66 driver_remote_send_term]. 67 68init_per_suite(Config) -> 69 Config. 70 71end_per_suite(_Config) -> 72 ok. 73 74init_per_group(_GroupName, Config) -> 75 Config. 76 77end_per_group(_GroupName, Config) -> 78 Config. 79 80 81init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> 82 erlang:trace(all, false, [all]), 83 os:unsetenv("OUTPUTV"), 84 reload_drv(Config), 85 Config. 86 87end_per_testcase(_Func, _Config) -> 88 erlang:trace(all, false, [all]), 89 ok. 90 91%% Test the first argument of trace/3 92port_specs(_Config) -> 93 94 S = self(), 95 96 Tracer = fun F() -> 97 receive 98 stop -> 99 ok; 100 M -> 101 S ! M, 102 F() 103 end 104 end, 105 106 Test = fun(TraceSpec, Info1, Info2) -> 107 {TracerPid,Ref} = spawn_monitor(Tracer), 108 Prt1 = erlang:open_port({spawn, echo_drv}, [binary]), 109 erlang:trace(TraceSpec, true, ['receive', {tracer, TracerPid}]), 110 %% We disable trace messages from the testcase process 111 erlang:trace(self(), false, ['receive']), 112 Prt2 = erlang:open_port({spawn, echo_drv}, [binary]), 113 114 InfoCheck = 115 fun(Info, Prt) -> 116 if 117 Info -> 118 {tracer, TracerPid} = erlang:trace_info(Prt, tracer), 119 {flags,['receive']} = erlang:trace_info(Prt, flags); 120 not Info -> 121 {tracer,[]} = erlang:trace_info(Prt, tracer), 122 {flags,[]} = erlang:trace_info(Prt, flags) 123 end 124 end, 125 InfoCheck(Info1, Prt1), 126 InfoCheck(Info2, Prt2), 127 128 %% These may create trace messages 129 erlang:port_command(Prt1, <<?ECHO_DRV_NOOP>>), 130 erlang:port_command(Prt2, <<?ECHO_DRV_NOOP>>), 131 132 %% Test what happens when the tracer dies 133 trace_delivered(), 134 TracerPid ! stop, 135 receive {'DOWN', Ref, process, TracerPid, normal} -> ok end, 136 137 %% These should not generate any trace messages 138 erlang:port_command(Prt1, <<?ECHO_DRV_NOOP>>), 139 erlang:port_command(Prt2, <<?ECHO_DRV_NOOP>>), 140 141 InfoCheck(false, Prt1), 142 InfoCheck(false, Prt2), 143 144 erlang:port_close(Prt1), 145 erlang:port_close(Prt2), 146 erlang:trace(all, false, [all]), 147 {Prt1, Prt2} 148 end, 149 150 {_Prt11, Prt12} = Test(new, false, true), 151 [{trace, Prt12, 'receive', {S, {command,<<?ECHO_DRV_NOOP>>}}}] 152 = flush(Prt12), 153 154 {_Prt21, Prt22} = Test(new_ports, false, true), 155 [{trace, Prt22, 'receive', {S, {command,<<?ECHO_DRV_NOOP>>}}}] 156 = flush(Prt22), 157 158 {Prt31, _Prt32} = Test(existing, true, false), 159 [{trace, Prt31, 'receive', {S, {command,<<?ECHO_DRV_NOOP>>}}}] 160 = flush(Prt31), 161 162 {Prt41, _Prt42} = Test(existing_ports, true, false), 163 [{trace, Prt41, 'receive', {S, {command,<<?ECHO_DRV_NOOP>>}}}] 164 = flush(Prt41), 165 166 {Prt51, Prt52} = Test(all, true, true), 167 [{trace, Prt51, 'receive', {S, {command,<<?ECHO_DRV_NOOP>>}}}] 168 = flush(Prt51), 169 [{trace, Prt52, 'receive', {S, {command,<<?ECHO_DRV_NOOP>>}}}] 170 = flush(Prt52), 171 172 {Prt61, Prt62} = Test(ports, true, true), 173 [{trace, Prt61, 'receive', {S, {command,<<?ECHO_DRV_NOOP>>}}}] 174 = flush(Prt61), 175 [{trace, Prt62, 'receive', {S, {command,<<?ECHO_DRV_NOOP>>}}}] 176 = flush(Prt62), 177 178 ok. 179 180%% Test that the 'ports' trace flag works 181ports(_Config) -> 182 183 {Prt, S} = trace_and_open([ports],[binary]), 184 185 [{trace, Prt, open, S, echo_drv}, 186 {trace, Prt, getting_linked, S}] = flush(), 187 188 register(?MODULE, Prt), 189 unregister(?MODULE), 190 register(?MODULE, Prt), 191 192 [{trace,Prt,register,port_trace_SUITE}, 193 {trace,Prt,unregister,port_trace_SUITE}, 194 {trace,Prt,register,port_trace_SUITE}] = flush(), 195 196 unlink(Prt), 197 link(Prt), 198 199 [{trace,Prt,getting_unlinked,S}, 200 {trace,Prt,getting_linked,S}] = flush(), 201 202 erlang:port_close(Prt), 203 204 [{trace,Prt,closed,normal}, 205 {trace,Prt,unregister,port_trace_SUITE}] = flush(), 206 207 ok. 208 209%% Test that port_close and ! close generate correct trace messages 210open_close(_Config) -> 211 212 S = trace_ports([send,'receive']), 213 214 Prt = erlang:open_port({spawn, echo_drv}, [binary]), 215 erlang:port_close(Prt), 216 [{trace, Prt, 'receive', {S, close}}] = flush(), 217 218 Prt2 = erlang:open_port({spawn, echo_drv}, [binary]), 219 Prt2 ! {S, close}, 220 recv({Prt2, closed}), 221 [{trace, Prt2, 'receive', {S, close}}, 222 {trace, Prt2, send, closed, S}] = flush(), 223 224 catch erlang:port_close(Prt2), 225 [] = flush(), 226 227 ok. 228 229%% Test that port_command and ! command generate correct trace messages 230command(Config) -> 231 232 Flags = [send,'receive'], 233 S = trace_ports(Flags), 234 Prt = erlang:open_port({spawn, echo_drv}, [binary]), 235 236 erlang:port_command(Prt, <<?ECHO_DRV_NOOP:8>>), 237 [{trace, Prt, 'receive', {S, {command, <<?ECHO_DRV_NOOP:8>>}}}] = flush(), 238 239 erlang:port_command(Prt, [?ECHO_DRV_NOOP, <<0:8>>]), 240 [{trace, Prt, 'receive', {S, {command, <<?ECHO_DRV_NOOP:8,0:8>>}}}] = flush(), 241 242 Prt ! {S, {command, <<?ECHO_DRV_NOOP:8>>}}, 243 [{trace, Prt, 'receive', {S, {command, <<?ECHO_DRV_NOOP:8>>}}}] = flush(), 244 245 OutputMsg = <<?ECHO_DRV_NOOP:8,0:(8*512)>>, 246 Prt ! {S, {command, OutputMsg}}, 247 [{trace, Prt, 'receive', {S, {command, OutputMsg}}}] = flush(), 248 249 close(Prt, Flags), 250 251 os:putenv("OUTPUTV","true"), 252 reload_drv(Config), 253 254 Prt2 = erlang:open_port({spawn, echo_drv}, [binary]), 255 OutputvMsg = [<<0:8>>,<<0:(8*512)>>,<<0:(8*256)>>,<<0:8>>], 256 257 erlang:port_command(Prt2, OutputvMsg), 258 [{trace, Prt2, 'receive', {S, {command, OutputvMsg}}}] = flush(), 259 260 Prt2 ! {S, {command, OutputvMsg}}, 261 [{trace, Prt2, 'receive', {S, {command, OutputvMsg}}}] = flush(), 262 263 close(Prt2, Flags), 264 265 os:unsetenv("OUTPUTV"), 266 267 ok. 268 269%% Test that port_control generate correct trace messages 270control(_Config) -> 271 272 Flags = [send,'receive'], 273 {Prt, S} = trace_and_open(Flags,[binary]), 274 275 [0] = erlang:port_control(Prt, 1, <<?ECHO_DRV_NOOP:8, 0:8>>), 276 [{trace, Prt, 'receive', {S, {control, {1, <<?ECHO_DRV_NOOP:8, 0:8>>}}}}, 277 {trace, Prt, send, {Prt, {control, <<0:8>>}}, S}] = flush(), 278 279 [0] = erlang:port_control(Prt, (1 bsl 32) - 1, <<?ECHO_DRV_NOOP:8, 0:8>>), 280 [{trace, Prt, 'receive', {S, {control, {(1 bsl 32) - 1, <<?ECHO_DRV_NOOP:8, 0:8>>}}}}, 281 {trace, Prt, send, {Prt, {control, <<0:8>>}}, S}] = flush(), 282 283 Msg = <<?ECHO_DRV_NOOP:8, 0:(8*512)>>, 284 Pat = lists:duplicate(512, 0), 285 Pat = erlang:port_control(Prt, 1, Msg), 286 [{trace, Prt, 'receive', {S, {control, {1, Msg}}}}, 287 {trace, Prt, send, {Prt, {control, <<0:(8*512)>>}}, S}] = flush(), 288 289 close(Prt, Flags), 290 291 ok. 292 293%% Test that port_connect and ! connect generate correct trace messages 294%% This includes that the proper getting_linked messages are sent 295connect(_Config) -> 296 297 298 {Prt, S} = trace_and_open([send, 'receive', ports],[binary]), 299 300 flush(), 301 302 {Pid,Ref} = spawn_monitor( 303 fun() -> 304 receive 305 go -> 306 Prt ! {self(), {connect, S}}, 307 receive {Prt, connected} -> unlink(Prt) end 308 end 309 end), 310 erlang:trace(Pid, true, [send, 'receive', procs]), 311 312 erlang:port_connect(Prt, Pid), 313 unlink(Prt), 314 315 [{trace,Prt,getting_linked,Pid}, 316 {trace,Prt,'receive',{S,{connect,Pid}}}, 317 {trace,Prt,send,{Prt,connected},S}, 318 {trace,Prt,getting_unlinked, S}] = flush(Prt), 319 320 [{trace,Pid,getting_linked,Prt}] = flush(), 321 322 Pid ! go, 323 recv({'DOWN',Ref,process,Pid,normal}), 324 325 [{trace,Prt,'receive',{Pid,{connect,S}}}, 326 {trace,Prt,send,{Prt,connected},Pid}, 327 {trace,Prt,getting_unlinked,Pid}] = flush(Prt), 328 329 [{trace,Pid,'receive',go}, 330 {trace,Pid,send,{Pid,{connect,S}}, Prt}, 331 {trace,Pid,'receive',{Prt,connected}}, 332 {trace,Pid,unlink,Prt}, 333 {trace,Pid,exit,normal}] = flush(), 334 335 erlang:port_close(Prt), 336 [{trace, Prt, 'receive', {S, close}}, 337 {trace, Prt, closed, normal}] = flush(), 338 ok. 339 340%% Test that port_call generate correct trace messages 341call(_Config) -> 342 343 Flags = [send,'receive'], 344 {Prt, S} = trace_and_open(Flags,[binary]), 345 346 Test = fun(Msg) -> 347 BinMsg = term_to_binary(Msg), 348 349 Msg = erlang:port_call(Prt, 0, Msg), 350 [{trace, Prt, 'receive', {S, {call, {0, BinMsg}}}}, 351 {trace, Prt, send, {Prt, {call, BinMsg}}, S}] = flush() 352 end, 353 354 Test({hello, world, make_ref()}), 355 Test({hello, world, lists:seq(1,1000)}), 356 357 close(Prt, Flags), 358 359 ok. 360 361%% Test that driver_output generate correct trace messages 362output(_Config) -> 363 364 Flags = [send], 365 {Prt, S} = trace_and_open(Flags,[binary]), 366 367 erlang:port_command(Prt, <<?ECHO_DRV_OUTPUT, 123456:32>>), 368 recv({Prt,{data,<<123456:32>>}}), 369 370 [{trace, Prt, send, {Prt, {data, <<123456:32>>}}, S}] = flush(), 371 372 close(Prt, Flags), 373 374 ok. 375 376%% Test that driver_output2 generate correct trace messages 377output2(_Config) -> 378 379 Flags = [send], 380 {Prt, S} = trace_and_open(Flags,[binary]), 381 382 erlang:port_command(Prt, <<?ECHO_DRV_OUTPUT2, 123456:32>>), 383 recv({Prt,{data,[$a|<<123456:32>>]}}), 384 [{trace, Prt, send, {Prt, {data, [$a|<<123456:32>>]}}, S}] = flush(), 385 386 close(Prt, Flags), 387 388 ok. 389 390%% Test that driver_output_binary generate correct trace messages 391output_binary(_Config) -> 392 393 Flags = [send], 394 {Prt, S} = trace_and_open(Flags,[binary]), 395 396 erlang:port_command(Prt, <<?ECHO_DRV_OUTPUT_BINARY, 0, 123456:32>>), 397 recv({Prt,{data,[$a|<<123456:32>>]}}), 398 [{trace, Prt, send, {Prt, {data, [$a|<<123456:32>>]}}, S}] = flush(), 399 400 close(Prt, Flags), 401 402 ok. 403 404%% Test that driver_outputv generate correct trace messages 405outputv(_Config) -> 406 407 Flags = [send], 408 {Prt, S} = trace_and_open(Flags,[binary]), 409 410 erlang:port_command(Prt, <<?ECHO_DRV_OUTPUTV, 123456:32>>), 411 recv({Prt,{data,[$a|<<123456:32>>]}}), 412 413 [{trace, Prt, send, {Prt, {data, [$a|<<123456:32>>]}}, S}] = flush(), 414 415 erlang:port_close(Prt), 416 [] = flush(), 417 418 ok. 419 420%% Test that driver_set_timer generate correct trace messages 421set_timer(_Config) -> 422 423 Flags = [send,'receive'], 424 {Prt, S} = trace_and_open(Flags,[binary]), 425 426 erlang:port_command(Prt, <<?ECHO_DRV_SET_TIMER>>), 427 timer:sleep(100), 428 [{trace, Prt, 'receive', {S, {command, <<?ECHO_DRV_SET_TIMER>>}}}, 429 {trace, Prt, 'receive', timeout}] = flush(), 430 431 close(Prt, Flags), 432 433 ok. 434 435%% Test that driver_failure* generate correct trace messages 436failure_eof(_Config) -> 437 438 Flags = [send,'receive', ports], 439 S = trace_ports(Flags), 440 441 Prt = erlang:open_port({spawn, echo_drv}, [eof, binary]), 442 [{trace, Prt, open, S, echo_drv}, 443 {trace, Prt, getting_linked, S}] = flush(), 444 445 erlang:port_command(Prt, <<?ECHO_DRV_FAILURE_EOF>>), 446 recv({Prt,eof}), 447 [{trace, Prt, 'receive', {S, {command, <<?ECHO_DRV_FAILURE_EOF>>}}}, 448 {trace, Prt, send, {Prt, eof}, S}] = flush(), 449 450 close(Prt, Flags), 451 452 %% Run same test without eof option 453 failure_test(<<?ECHO_DRV_FAILURE_EOF>>, normal). 454 455failure_atom(_Config) -> 456 failure_test(<<?ECHO_DRV_FAILURE_ATOM, "failure\0">>, failure). 457failure_posix(_Config) -> 458 failure_test(<<?ECHO_DRV_FAILURE_POSIX>>, eagain). 459failure(_Config) -> 460 failure_test(<<?ECHO_DRV_FAILURE, 1>>, 1). 461 462failure_test(Failure, Reason) -> 463 464 {Prt, S} = trace_and_open([send, 'receive', ports],[binary]), 465 466 [{trace, Prt, open, S, echo_drv}, 467 {trace, Prt, getting_linked, S}] = flush(), 468 469 process_flag(trap_exit, true), 470 erlang:port_command(Prt, Failure), 471 try 472 recv({'EXIT',Prt,Reason}) 473 after 474 process_flag(trap_exit, false) 475 end, 476 [{trace, Prt, 'receive', {S, {command, Failure}}}, 477 {trace, Prt, closed, Reason}] = flush(), 478 479 ok. 480 481%% Test that erl_drv_output_term generate correct trace messages 482output_term(_Config) -> 483 484 Flags = [send], 485 {Prt, S} = trace_and_open(Flags,[binary]), 486 487 erlang:port_command(Prt, <<?ECHO_DRV_OUTPUT_TERM, 123456:32>>), 488 recv({echo, Prt, <<123456:32>>}), 489 [{trace, Prt, send, {echo, Prt, <<123456:32>>}, S}] = flush(), 490 491 close(Prt, Flags), 492 493 ok. 494 495%% Test that driver_output_term generate correct trace messages 496driver_output_term(_Config) -> 497 498 Flags = [send], 499 {Prt, S} = trace_and_open(Flags,[binary]), 500 501 erlang:port_command(Prt, <<?ECHO_DRV_DRIVER_OUTPUT_TERM, 123456:32>>), 502 recv({echo, Prt, <<123456:32>>}), 503 [{trace, Prt, send, {echo, Prt, <<123456:32>>}, S}] = flush(), 504 505 close(Prt, Flags), 506 507 ok. 508 509%% Test that erl_drv_send_term generate correct trace messages 510send_term(_Config) -> 511 512 Flags = [send], 513 {Prt, S} = trace_and_open(Flags,[binary]), 514 515 erlang:port_command(Prt, <<?ECHO_DRV_SEND_TERM, 123456:32>>), 516 recv({echo, Prt, <<123456:32>>}), 517 [{trace, Prt, send, {echo, Prt, <<123456:32>>}, S}] = flush(), 518 519 {Pid, Ref} = spawn_monitor(fun() -> erlang:port_command(Prt, <<?ECHO_DRV_SAVE_CALLER>>) end), 520 recv({'DOWN',Ref,process,Pid,normal}), 521 erlang:port_command(Prt, <<?ECHO_DRV_SEND_TERM, 123456:32>>), 522 [{trace, Prt, send_to_non_existing_process, {echo, Prt, <<123456:32>>}, Pid}] = flush(), 523 524 close(Prt, Flags), 525 526 ok. 527 528%% Test that driver_send_term generate correct trace messages 529driver_send_term(_Config) -> 530 531 Flags = [send], 532 {Prt, S} = trace_and_open(Flags,[binary]), 533 534 erlang:port_command(Prt, <<?ECHO_DRV_DRIVER_SEND_TERM, 123456:32>>), 535 recv({echo, Prt, <<123456:32>>}), 536 [{trace, Prt, send, {echo, Prt, <<123456:32>>}, S}] = flush(), 537 538 {Pid, Ref} = spawn_monitor(fun() -> erlang:port_command(Prt, <<?ECHO_DRV_SAVE_CALLER>>) end), 539 recv({'DOWN',Ref,process,Pid,normal}), 540 erlang:port_command(Prt, <<?ECHO_DRV_SEND_TERM, 123456:32>>), 541 [{trace, Prt, send_to_non_existing_process, {echo, Prt, <<123456:32>>}, Pid}] = flush(), 542 543 close(Prt, Flags), 544 545 ok. 546 547%% Test that driver_send_term from non-scheduler thread does not 548%% generate trace messages. 549driver_remote_send_term(_Config) -> 550 551 Flags = [send], 552 {Prt, S} = trace_and_open(Flags,[binary]), 553 554 erlang:port_command(Prt, <<?ECHO_DRV_REMOTE_SEND_TERM, 123456:32>>), 555 recv({echo, Prt, <<123456:32>>}), 556 [] = flush(), 557 558 Pid = spawn_link( 559 fun() -> 560 erlang:port_command(Prt, <<?ECHO_DRV_SAVE_CALLER>>), 561 S ! ok, 562 receive M -> S ! M end 563 end), 564 recv(ok), 565 erlang:trace(Pid, true, ['receive']), 566 567 erlang:port_command(Prt, <<?ECHO_DRV_REMOTE_SEND_TERM, 123456:32>>), 568 recv({echo, Prt, <<123456:32>>}), 569 [{trace, Pid, 'receive', {echo, Prt, <<123456:32>>}}] = flush(), 570 571 close(Prt, Flags), 572 573 ok. 574 575%%%%%%%%%%%%%%%%%%% 576%% Helper functions 577%%%%%%%%%%%%%%%%%%% 578 579trace_ports(TraceFlags) -> 580 erlang:trace(new_ports, true, TraceFlags), 581 self(). 582 583trace_and_open(TraceFlags, OpenFlags) -> 584 S = self(), 585 Ports = proplists:get_value(ports, TraceFlags), 586 [trace_ports(TraceFlags) || Ports], 587 Prt = erlang:open_port({spawn, echo_drv}, OpenFlags), 588 [erlang:trace(Prt, true, TraceFlags) || Ports == undefined], 589 {Prt, S}. 590 591close(Prt, Flags) -> 592 Recv = proplists:get_value('receive', Flags), 593 Ports = proplists:get_value(ports, Flags), 594 S = self(), 595 596 erlang:port_close(Prt), 597 598 if Recv, Ports -> 599 [{trace, Prt, 'receive', {S, close}}, 600 {trace, Prt, closed, normal}] = flush(); 601 Recv -> 602 [{trace, Prt, 'receive', {S, close}}] = flush(); 603 Ports -> 604 [{trace, Prt, closed, normal}] = flush(); 605 true -> 606 [] = flush() 607 end. 608 609trace_delivered() -> 610 Ref = erlang:trace_delivered(all), 611 receive {trace_delivered, all, Ref} -> ok end. 612 613flush() -> 614 flush(all). 615flush(From) -> 616 trace_delivered(), 617 f(From). 618 619f(From) -> 620 receive 621 M when From =:= all; element(2, M) == From -> 622 [M | f(From)] 623 after 0 -> 624 [] 625 end. 626 627recv(Msg) -> 628 receive Msg -> ok after 1000 -> ct:fail({did_not_get_data,Msg,flush()}) end. 629 630load_drv(Config) -> 631 Path = proplists:get_value(data_dir, Config), 632 case erl_ddll:load_driver(Path, echo_drv) of 633 ok -> ok; 634 {error, Error} = Res -> 635 io:format("~s\n", [erl_ddll:format_error(Error)]), 636 ct:fail(Res) 637 end. 638 639reload_drv(Config) -> 640 erl_ddll:unload_driver(echo_drv), 641 load_drv(Config). 642