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