Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Rename every instrumented module

Append their names with `INSTR_PREFIX'.
This way we can instrument libraries that Concuerror
uses or modules that are sticky.

To do so we must open the file containing the source code,
change the `-module()' declaration and save it to a temp
directory. This has to be done because predefined macros
such `MODULE' has to be redefined (and this is the only
way erlang allows us to do that).

We have to keep a list of all the instrumented modules
and check at runtime if some function calls need to
be renamed.

When a ticket is produced, we clean the stacktrace to
hide the fact that we had renamed all the modules :)
  • Loading branch information...
commit dd7740f73159812bf4043ff78154bf46098fd8fb 1 parent 337fcab
@iliastsi iliastsi authored
View
2  include/gen.hrl
@@ -46,6 +46,8 @@
%% Instrumented message atom.
-define(INSTR_MSG, '_._instr_msg').
+%% Instrumented modules prefix.
+-define(INSTR_PREFIX, "conc__").
%% Set-like data structure used in sched, lid and error modules.
-define(SETS, ordsets).
View
8 src/concuerror.erl
@@ -303,8 +303,12 @@ parse([{Opt, Param} | Args], Options) ->
end;
"v" ->
- NewOptions = keyIncrease('verbose', 1, Options),
- parse(Args, NewOptions);
+ case Param of
+ [] ->
+ NewOptions = keyIncrease('verbose', 1, Options),
+ parse(Args, NewOptions);
+ _Other -> wrongArgument('number', Opt)
+ end;
"-help" ->
help(),
View
16 src/concuerror_error.erl
@@ -44,7 +44,8 @@ short({deadlock, Blocked}) ->
Fun = fun(L, A) -> A ++ concuerror_lid:to_string(L) ++ ", " end,
lists:foldl(Fun, "", List) ++ concuerror_lid:to_string(Last);
short({assertion_violation, [{module, Module}, {line, Line}|_Rest]}) ->
- concuerror_util:flat_format("~p.erl:~p", [Module, Line]);
+ OldModule = concuerror_instr:old_module_name(Module),
+ concuerror_util:flat_format("~p.erl:~p", [OldModule, Line]);
short({exception, Reason}) ->
lists:flatten(io_lib:format("~W", [Reason, 3])).
@@ -52,18 +53,19 @@ short({exception, Reason}) ->
long({deadlock, _Blocked} = Error) ->
Format = "Error type : Deadlock~n"
- "Blocked processes : ~s",
+ "Blocked processes : ~s",
concuerror_util:flat_format(Format, [short(Error)]);
long({assertion_violation,
[{module, Module}, {line, Line}, _Xpr, {expected, Exp}, {value, Val}]}) ->
Format = "Error type : Assertion violation~n"
- "Module:Line : ~p.erl:~p~n"
- "Expected : ~p~n"
- "Value : ~p",
- concuerror_util:flat_format(Format, [Module, Line, Exp, Val]);
+ "Module:Line : ~p.erl:~p~n"
+ "Expected : ~p~n"
+ "Value : ~p",
+ OldModule = concuerror_instr:old_module_name(Module),
+ concuerror_util:flat_format(Format, [OldModule, Line, Exp, Val]);
long({exception, Details}) ->
Format = "Error type : Exception~n"
- "Details : ~p",
+ "Details : ~p",
concuerror_util:flat_format(Format, [Details]).
-spec mock() -> {'exception', 'foobar'}.
View
212 src/concuerror_instr.erl
@@ -13,7 +13,8 @@
%%%----------------------------------------------------------------------
-module(concuerror_instr).
--export([delete_and_purge/1, instrument_and_compile/2, load/1]).
+-export([delete_and_purge/0, instrument_and_compile/2, load/1,
+ check_new_module_name/1, new_module_name/1, old_module_name/1]).
-export_type([macros/0]).
@@ -84,6 +85,9 @@
%% Instrumented mod:fun.
-define(INSTR_MOD_FUN, ?INSTR_ERL_MOD_FUN ++ ?INSTR_ETS_FUN).
+%% Temp directory we use to save our module renamed code.
+-define(INSTR_TEMP_DIR, '_._instr_temp_dir').
+
%%%----------------------------------------------------------------------
%%% Types
%%%----------------------------------------------------------------------
@@ -96,14 +100,44 @@
%%% Instrumentation utilities
%%%----------------------------------------------------------------------
-%% Delete and purge all modules in Files.
--spec delete_and_purge([file:filename()]) -> 'ok'.
-
-delete_and_purge(Files) ->
- ModsToPurge = [concuerror_util:get_module_name(F) || F <- Files],
+%% ---------------------------
+%% Delete and purge all modules in `?INSTR_TEMP_DIR'.
+-spec delete_and_purge() -> 'ok'.
+delete_and_purge() ->
+ %% Unload and purge modules.
+ ModsToPurge = [IM || {IM} <- ets:tab2list(?NT_INSTR_MOD)],
Fun = fun (M) -> code:purge(M), code:delete(M), code:purge(M) end,
- lists:foreach(Fun, ModsToPurge).
+ lists:foreach(Fun, ModsToPurge),
+ %% Delete temp directory (ignore errors).
+ TmpDir = get(?INSTR_TEMP_DIR),
+ {ok, Files} = file:list_dir(TmpDir),
+ DelFile = fun(F) -> file:delete(filename:join(TmpDir, F)) end,
+ lists:foreach(DelFile, Files),
+ file:del_dir(TmpDir).
+
+%% ---------------------------
+%% Rename a module for the instrumentation.
+-spec new_module_name(atom() | string()) -> atom().
+new_module_name(Module) when is_atom(Module) ->
+ new_module_name(atom_to_list(Module));
+new_module_name(Module) when is_list(Module) ->
+ list_to_atom(?INSTR_PREFIX ++ Module).
+
+-spec check_new_module_name(atom()) -> atom().
+check_new_module_name(Module) ->
+ case ets:member(?NT_INSTR_MOD, Module) of
+ true -> new_module_name(Module);
+ false -> Module
+ end.
+-spec old_module_name(atom()) -> atom().
+old_module_name(NewModule) ->
+ case atom_to_list(NewModule) of
+ (?INSTR_PREFIX ++ OldModule) -> list_to_atom(OldModule);
+ _Module -> NewModule
+ end.
+
+%% ---------------------------
%% @spec instrument_and_compile(Files::[file:filename()], concuerror:options())
%% -> {'ok', [mfb()]} | 'error'
%% @doc: Instrument and compile a list of files.
@@ -114,7 +148,6 @@ delete_and_purge(Files) ->
%% otherwise `error' is returned. No `.beam' files are produced.
-spec instrument_and_compile([file:filename()], concuerror:options()) ->
{'ok', [mfb()]} | 'error'.
-
instrument_and_compile(Files, Options) ->
Includes =
case lists:keyfind('include', 1, Options) of
@@ -137,20 +170,28 @@ instrument_and_compile(Files, Options) ->
%% Get the modules we are going to instrument and save
%% them to `NT_INSTR_MOD' for future reference.
InstrModules = [{concuerror_util:get_module_name(F)} || F <- Files],
- ets:insert(?NT_INSTR_MOD, [{erlang}, {ets} | InstrModules]),
- concuerror_log:log(0, "Instrumenting files..\n"),
- InstrOne =
- fun(File) ->
- instrument_and_compile_one(File, Includes, Defines, CheckBBModules)
- end,
- MFBs = concuerror_util:pmap(InstrOne, Files),
- case lists:member('error', MFBs) of
- true -> error;
- false -> {ok, MFBs}
+ ets:insert(?NT_INSTR_MOD, InstrModules),
+ %% Create a temp dir to save renamed code
+ case create_tmp_dir() of
+ {ok, DirName} ->
+ put(?INSTR_TEMP_DIR, DirName),
+ concuerror_log:log(0, "Instrumenting files..\n"),
+ InstrOne =
+ fun(File) ->
+ instrument_and_compile_one(File, Includes,
+ Defines, DirName, CheckBBModules)
+ end,
+ MFBs = concuerror_util:pmap(InstrOne, Files),
+ case lists:member('error', MFBs) of
+ true -> error;
+ false -> {ok, MFBs}
+ end;
+ error ->
+ error
end.
%% Instrument and compile a single file.
-instrument_and_compile_one(File, Includes, Defines, CheckBBModules) ->
+instrument_and_compile_one(File, Includes, Defines, TmpDir, CheckBBModules) ->
%% Compilation of original file without emitting code, just to show
%% warnings or stop if an error is found, before instrumenting it.
concuerror_log:log(1, "Validating file ~p...~n", [File]),
@@ -158,22 +199,24 @@ instrument_and_compile_one(File, Includes, Defines, CheckBBModules) ->
OptDefines = [{d, M, V} || {M, V} <- Defines],
PreOptions = [strong_validation,verbose,return | OptIncludes++OptDefines],
put('check_bb_modules', CheckBBModules),
+ put(?INSTR_TEMP_DIR, TmpDir),
case compile:file(File, PreOptions) of
- {ok, Module, Warnings} ->
+ {ok, OldModule, Warnings} ->
%% Log warning messages.
log_warning_list(Warnings),
%% Instrument given source file.
concuerror_log:log(1, "Instrumenting file ~p...~n", [File]),
- case instrument(File, Includes, Defines) of
- {ok, NewForms} ->
+ case instrument(OldModule, File, Includes, Defines) of
+ {ok, NewFile, NewForms} ->
%% Compile instrumented code.
%% TODO: More compile options?
CompOptions = [binary],
case compile:forms(NewForms, CompOptions) of
- {ok, Module, Binary} -> {Module, File, Binary};
+ {ok, NewModule, Binary} ->
+ {NewModule, NewFile, Binary};
error ->
concuerror_log:log(0, "Failed to compile "
- "instrumented file ~p.~n", [File]),
+ "instrumented file ~p.~n", [NewFile]),
error
end;
{error, Error} ->
@@ -187,8 +230,8 @@ instrument_and_compile_one(File, Includes, Defines, CheckBBModules) ->
error
end.
+%% ---------------------------
-spec load([mfb()]) -> 'ok' | 'error'.
-
load([]) -> ok;
load([MFB|Rest]) ->
case load_one(MFB) of
@@ -204,29 +247,86 @@ load_one({Module, File, Binary}) ->
error
end.
-instrument(File, Includes, Defines) ->
+%% ---------------------------
+instrument(Module, File, Includes, Defines) ->
NewIncludes = [filename:dirname(File) | Includes],
- case epp:parse_file(File, NewIncludes, Defines) of
- {ok, OldForms} ->
- %% Remove `type` and `spec` attributes to avoid errors
- %% due to record expansion below.
- StrippedForms = strip_attributes(OldForms, []),
- ExpRecForms = erl_expand_records:module(StrippedForms, []),
- %% Convert `erl_parse tree` to `abstract syntax tree`.
- Tree = erl_recomment:recomment_forms(ExpRecForms, []),
- MapFun = fun(T) -> instrument_toplevel(T) end,
- Transformed = erl_syntax_lib:map_subtrees(MapFun, Tree),
- %% Return an `erl_parse-compatible` representation.
- Abstract = erl_syntax:revert(Transformed),
- ?print(Abstract),
- NewForms = erl_syntax:form_list_elements(Abstract),
- {ok, NewForms};
+ %% Rename module
+ case rename_module(Module, File) of
+ {ok, NewFile} ->
+ case epp:parse_file(NewFile, NewIncludes, Defines) of
+ {ok, OldForms} ->
+ %% Remove `type` and `spec` attributes to avoid
+ %% errors due to record expansion below.
+ %% Also rename our module.
+ StrippedForms = strip_attributes(OldForms, []),
+ ExpRecForms = erl_expand_records:module(StrippedForms, []),
+ %% Convert `erl_parse tree` to `abstract syntax tree`.
+ Tree = erl_recomment:recomment_forms(ExpRecForms, []),
+ MapFun = fun(T) -> instrument_toplevel(T) end,
+ Transformed = erl_syntax_lib:map_subtrees(MapFun, Tree),
+ %% Return an `erl_parse-compatible` representation.
+ Abstract = erl_syntax:revert(Transformed),
+ ?print(Abstract),
+ NewForms = erl_syntax:form_list_elements(Abstract),
+ {ok, NewFile, NewForms};
+ {error, _} = Error -> Error
+ end;
{error, _} = Error -> Error
end.
+%% ---------------------------
+rename_module(Module, File) ->
+ ModuleStr = atom_to_list(Module),
+ NewModuleStr = atom_to_list(new_module_name(Module)),
+ TmpDir = get(?INSTR_TEMP_DIR),
+ NewFile = filename:join(TmpDir, NewModuleStr ++ ".erl"),
+ case file:read_file(File) of
+ {ok, Binary} ->
+ %% Replace the first occurrence of `-module(Module).'
+ Pattern = binary:list_to_bin(
+ "-module(" ++ ModuleStr ++ ")."),
+ Replacement = binary:list_to_bin(
+ "-module(" ++ NewModuleStr ++ ")."),
+ NewBinary = binary:replace(Binary, Pattern, Replacement),
+ %% Write new file in temp directory
+ case file:write_file(NewFile, NewBinary) of
+ ok -> {ok, NewFile};
+ Error -> Error
+ end;
+ Error ->
+ Error
+ end.
+
+%% ---------------------------
+%% Create an unique temp directory based on (starting with) Stem.
+%% A directory name of the form <Stem><Number> is generated.
+%% We use this directory to save our renamed code.
+create_tmp_dir() ->
+ DirName = temp_name("./.conc_temp_"),
+ concuerror_log:log(1, "Create temp dir ~p..\n", [DirName]),
+ case file:make_dir(DirName) of
+ ok ->
+ {ok, DirName};
+ {error, eexist} ->
+ %% Directory exists, try again
+ create_tmp_dir();
+ {error, Reason} ->
+ concuerror_log:log(0, "error: ~p\n", [Reason]),
+ error
+ end.
+
+temp_name(Stem) ->
+ {A, B, C} = erlang:now(),
+ RandomNum = A bxor B bxor C,
+ RandomName = Stem ++ integer_to_list(RandomNum),
+ RandomName.
+
+
+%% ---------------------------
%% XXX: Implementation dependent.
-strip_attributes([], Acc) -> lists:reverse(Acc);
-strip_attributes([{attribute, _Line, Name, _Misc} = Head|Rest], Acc) ->
+strip_attributes([], Acc) ->
+ lists:reverse(Acc);
+strip_attributes([{attribute, _Line, Name, _Misc}=Head | Rest], Acc) ->
case lists:member(Name, ?ATTR_STRIP) of
true -> strip_attributes(Rest, Acc);
false -> strip_attributes(Rest, [Head|Acc])
@@ -234,6 +334,7 @@ strip_attributes([{attribute, _Line, Name, _Misc} = Head|Rest], Acc) ->
strip_attributes([Head|Rest], Acc) ->
strip_attributes(Rest, [Head|Acc]).
+%% ---------------------------
%% Instrument a "top-level" element.
%% Of the "top-level" elements, i.e. functions, specs, etc., only functions are
%% transformed, so leave everything else as is.
@@ -261,9 +362,10 @@ instrument_term(Tree) ->
case erl_syntax:type(Tree) of
application ->
case get_mfa(Tree) of
- no_instr -> Tree;
+ no_instr -> Tree;
+ {rename, Mfa} -> instrument_rename(Mfa);
{normal, Mfa} -> instrument_application(Mfa);
- {var, Mfa} -> instrument_var_application(Mfa)
+ {var, Mfa} -> instrument_var_application(Mfa)
end;
infix_expr ->
Operator = erl_syntax:infix_expr_operator(Tree),
@@ -320,11 +422,17 @@ needs_instrument(Function, ArgTrees) ->
%% Determine whether a `foo:bar(...)` call needs instrumentation.
needs_instrument(Module, Function, ArgTrees) ->
%% Add `Module' to the called modules table.
- ets:insert(?NT_CALLED_MOD, {Module}),
+ ets:insert(?NT_CALLED_MOD, {old_module_name(Module)}),
Arity = length(ArgTrees),
case lists:member({Module, Function, Arity}, ?INSTR_MOD_FUN) of
- true -> {normal, {Module, Function, ArgTrees}};
- false -> no_instr
+ true ->
+ {normal, {Module, Function, ArgTrees}};
+ false ->
+ %% Check if module needs to be renamed
+ case ets:member(?NT_INSTR_MOD, Module) of
+ true -> {rename, {Module, Function, ArgTrees}};
+ false -> no_instr
+ end
end.
instrument_application({erlang, Function, ArgTrees}) ->
@@ -334,8 +442,7 @@ instrument_application({erlang, Function, ArgTrees}) ->
instrument_application({Module, Function, ArgTrees}) ->
RepMod = erl_syntax:atom(?REP_MOD),
RepFun = erl_syntax:atom(list_to_atom("rep_" ++ atom_to_list(Module)
- ++ "_"
- ++ atom_to_list(Function))),
+ ++ "_" ++ atom_to_list(Function))),
erl_syntax:application(RepMod, RepFun, ArgTrees).
instrument_var_application({ModTree, FunTree, ArgTrees}) ->
@@ -346,6 +453,11 @@ instrument_var_application({ModTree, FunTree, ArgTrees}) ->
erl_syntax:application(RepMod, RepFun,
[ModTree, FunTree, ArgList, CheckBBModules]).
+instrument_rename({Module, Function, ArgTrees}) ->
+ RepMod = erl_syntax:atom(new_module_name(Module)),
+ RepFun = erl_syntax:atom(Function),
+ erl_syntax:application(RepMod, RepFun, ArgTrees).
+
%% Instrument a receive expression.
%% ----------------------------------------------------------------------
%% receive
View
26 src/concuerror_rep.erl
@@ -105,13 +105,17 @@ rep_var(Mod, Fun, Args, CheckBBModules) ->
{Key, Callback} ->
apply(Callback, Args);
false ->
+ %% If CheckBBModules, add module to `NT_CALLED_MOD' table
if
CheckBBModules ->
- ets:insert(?NT_CALLED_MOD, {Mod});
+ OldMod = concuerror_instr:old_module_name(Mod),
+ ets:insert(?NT_CALLED_MOD, {OldMod});
true ->
true
end,
- apply(Mod, Fun, Args)
+ %% Rename module
+ RenameMod = concuerror_instr:check_new_module_name(Mod),
+ apply(RenameMod, Fun, Args)
end.
%% @spec: rep_demonitor(reference()) -> 'true'
@@ -489,7 +493,9 @@ find_my_registered_name() ->
%% See `rep_spawn/1'.
-spec rep_spawn(atom(), atom(), [term()]) -> pid().
rep_spawn(Module, Function, Args) ->
- Fun = fun() -> apply(Module, Function, Args) end,
+ %% Rename module
+ NewModule = concuerror_instr:check_new_module_name(Module),
+ Fun = fun() -> apply(NewModule, Function, Args) end,
rep_spawn(Fun).
%% @spec rep_spawn_link(function()) -> pid()
@@ -497,7 +503,6 @@ rep_spawn(Module, Function, Args) ->
%%
%% Before spawned, the new process has to yield.
-spec rep_spawn_link(function()) -> pid().
-
rep_spawn_link(Fun) ->
spawn_center(spawn_link, Fun).
@@ -507,7 +512,9 @@ rep_spawn_link(Fun) ->
%% See `rep_spawn_link/1'.
-spec rep_spawn_link(atom(), atom(), [term()]) -> pid().
rep_spawn_link(Module, Function, Args) ->
- Fun = fun() -> apply(Module, Function, Args) end,
+ %% Rename module
+ NewModule = concuerror_instr:check_new_module_name(Module),
+ Fun = fun() -> apply(NewModule, Function, Args) end,
rep_spawn_link(Fun).
%% @spec rep_spawn_monitor(function()) -> {pid(), reference()}
@@ -524,7 +531,9 @@ rep_spawn_monitor(Fun) ->
%% See rep_spawn_monitor/1.
-spec rep_spawn_monitor(atom(), atom(), [term()]) -> {pid(), reference()}.
rep_spawn_monitor(Module, Function, Args) ->
- Fun = fun() -> apply(Module, Function, Args) end,
+ %% Rename module
+ NewModule = concuerror_instr:check_new_module_name(Module),
+ Fun = fun() -> apply(NewModule, Function, Args) end,
rep_spawn_monitor(Fun).
%% @spec rep_spawn_opt(function(),
@@ -573,9 +582,10 @@ rep_spawn_opt(Fun, Opt) ->
{'min_heap_size', integer()} |
{'min_bin_vheap_size', integer()}]) ->
pid() | {pid(), reference()}.
-
rep_spawn_opt(Module, Function, Args, Opt) ->
- Fun = fun() -> apply(Module, Function, Args) end,
+ %% Rename module
+ NewModule = concuerror_instr:check_new_module_name(Module),
+ Fun = fun() -> apply(NewModule, Function, Args) end,
rep_spawn_opt(Fun, Opt).
%% @spec: rep_unlink(pid() | port()) -> 'true'
View
9 src/concuerror_sched.erl
@@ -97,7 +97,7 @@
-spec analyze(analysis_target(), [file:filename()], concuerror:options()) ->
analysis_ret().
-analyze(Target, Files, Options) ->
+analyze({Mod,Fun,Args}=_Target, Files, Options) ->
PreBound =
case lists:keyfind(preb, 1, Options) of
{preb, inf} -> ?INFINITY;
@@ -109,6 +109,9 @@ analyze(Target, Files, Options) ->
{dpor, Flavor} -> Flavor;
false -> 'none'
end,
+ %% Rename Target's module
+ NewMod = concuerror_instr:new_module_name(Mod),
+ Target = {NewMod, Fun, Args},
%% Initialize `NT_CALLED_MOD' and `NT_INSTR_MOD' table to save
%% all the modules that we call or instrument.
?NT_CALLED_MOD = ets:new(?NT_CALLED_MOD,
@@ -148,11 +151,11 @@ analyze(Target, Files, Options) ->
end;
error -> {error, instr, {Target, 0}}
end,
- concuerror_instr:delete_and_purge(Files),
+ concuerror_instr:delete_and_purge(),
%% Show unistrumented (blackboxed) modules.
Instr_Modules = [IM || {IM} <- ets:tab2list(?NT_INSTR_MOD)],
Called_Modules = [CM || {CM} <- ets:tab2list(?NT_CALLED_MOD)],
- case (Called_Modules -- Instr_Modules) of
+ case (Called_Modules -- ['erlang', 'ets' | Instr_Modules]) of
[] ->
ok;
Black_Modules ->
View
23 src/concuerror_ticket.erl
@@ -38,12 +38,25 @@ new(Error, ErrorDetails) ->
end,
{NewError, ErrorDetails}.
+%% ---------------------------
clean_stacktrace(Stacktrace) ->
- [T || T <- Stacktrace, not is_rep_module(T)].
-
-is_rep_module({?REP_MOD, _, _, _}) -> true;
-is_rep_module(_Else) -> false.
-
+ clean_stacktrace(Stacktrace, []).
+
+clean_stacktrace([], Acc) ->
+ lists:reverse(Acc);
+clean_stacktrace([{?REP_MOD, _, _, _} | Ts], Acc) ->
+ %% Ignore concuerror's rep module
+ clean_stacktrace(Ts, Acc);
+clean_stacktrace([{Mod, Fun, Args, Pos} | Ts], Acc) ->
+ %% Rename modules back to their original names
+ OldMod = concuerror_instr:old_module_name(Mod),
+ %% Rename files after their modules (for now).
+ %% TODO: Rename files back to their original names.
+ OldFile = atom_to_list(OldMod) ++ ".erl",
+ OldPos = lists:keyreplace(file, 1, Pos, {file, OldFile}),
+ clean_stacktrace(Ts, [{OldMod, Fun, Args, OldPos} | Acc]).
+
+%% ---------------------------
-spec get_error(ticket()) -> concuerror_error:error().
get_error({Error, _ErrorDetails}) ->
View
20 utest/concuerror_instr_tests.erl
@@ -29,17 +29,11 @@ syntax_test_() ->
Setup =
fun() ->
_ = concuerror_log:start(),
- _ = concuerror_log:attach(concuerror_log, []),
- ?NT_CALLED_MOD = ets:new(?NT_CALLED_MOD,
- [named_table, public, set, {write_concurrency, true}]),
- ?NT_INSTR_MOD = ets:new(?NT_INSTR_MOD,
- [named_table, public, set, {read_concurrency, true}])
+ _ = concuerror_log:attach(concuerror_log, [])
end,
Cleanup =
fun(_Any) ->
- concuerror_log:stop(),
- ets:delete(?NT_CALLED_MOD),
- ets:delete(?NT_INSTR_MOD)
+ concuerror_log:stop()
end,
Test01 = {"Block expression in after clause",
fun(_Any) -> test_ok("block_after.erl") end},
@@ -54,6 +48,16 @@ syntax_test_() ->
{foreach, local, Setup, Cleanup, [Inst]}.
test_ok(File) ->
+ %% Initialize test
+ ?NT_CALLED_MOD = ets:new(?NT_CALLED_MOD,
+ [named_table, public, set, {write_concurrency, true}]),
+ ?NT_INSTR_MOD = ets:new(?NT_INSTR_MOD,
+ [named_table, public, set, {read_concurrency, true}]),
Path = filename:join([?TEST_PATH, File]),
Result = concuerror_instr:instrument_and_compile([Path], []),
+ %% Cleanup test
+ concuerror_instr:delete_and_purge(),
+ ets:delete(?NT_CALLED_MOD),
+ ets:delete(?NT_INSTR_MOD),
+ %% Assert Result
?assertMatch({ok, _Bin}, Result).
Please sign in to comment.
Something went wrong with that request. Please try again.