Packages

Concuerror: Stateless Model Checking tool for Erlang programs

Current section

Files

Jump to
concuerror src concuerror_loader.erl
Raw

src/concuerror_loader.erl

%%% @private
-module(concuerror_loader).
-export([initialize/1, load/1, load_initially/1, is_instrumenting/0]).
%%------------------------------------------------------------------------------
-export_type([instrumented/0]).
-type instrumented() :: 'concuerror_instrumented'.
%%------------------------------------------------------------------------------
-include("concuerror.hrl").
%%------------------------------------------------------------------------------
-spec initialize([atom()]) -> 'ok' | {'error', string()}.
initialize(Excluded) ->
Instrumented = get_instrumented_table(),
case ets:info(Instrumented, name) =:= undefined of
true ->
setup_sticky_directories(),
Instrumented = ets:new(Instrumented, [named_table, public]),
ok;
false ->
ets:match_delete(Instrumented, {'_', concuerror_excluded}),
ok
end,
Entries = [{X, concuerror_excluded} || X <- Excluded],
try
true = ets:insert_new(Instrumented, Entries),
ok
catch
_:_ ->
Error =
"Excluded modules have already been instrumented. Restart the shell.",
{error, Error}
end.
setup_sticky_directories() ->
{module, concuerror_inspect} = code:ensure_loaded(concuerror_inspect),
_ = [true = code:unstick_mod(M) || {M, preloaded} <- code:all_loaded()],
[] = [D || D <- code:get_path(), ok =/= code:unstick_dir(D)],
case code:get_object_code(erlang) =:= error of
true ->
true =
code:add_pathz(filename:join(code:root_dir(), "erts/preloaded/ebin"));
false ->
ok
end.
%%------------------------------------------------------------------------------
-spec load(module()) -> {'ok', iodata()} | 'already_done' | 'fail'.
load(Module) ->
Instrumented = get_instrumented_table(),
load(Module, Instrumented).
load(Module, Instrumented) ->
case ets:lookup(Instrumented, Module) =:= [] of
true ->
set_is_instrumenting({true, Module}),
{Beam, Filename} =
case code:which(Module) of
preloaded ->
{Module, BeamBinary, F} = code:get_object_code(Module),
{BeamBinary, F};
F ->
{F, F}
end,
try
{ok, Warnings} = load_binary(Module, Filename, Beam, Instrumented),
set_is_instrumenting(false),
{ok, Warnings}
catch
_:_ -> fail
end;
false -> already_done
end.
%%------------------------------------------------------------------------------
-spec load_initially(module()) ->
{ok, module(), [string()]} | {error, string()}.
load_initially(Module) ->
Instrumented = get_instrumented_table(),
load_initially(Module, Instrumented).
load_initially(File, Instrumented) ->
MaybeModule =
case filename:extension(File) of
".erl" ->
case compile:file(File, [binary, debug_info, report_errors]) of
error ->
Format = "could not compile ~s (try to add the .beam file instead)",
{error, io_lib:format(Format, [File])};
Else -> Else
end;
".beam" ->
case beam_lib:chunks(File, []) of
{ok, {M, []}} ->
{ok, M, File};
Else ->
{error, beam_lib:format_error(Else)}
end;
_Other ->
{error, io_lib:format("~s is not a .erl or .beam file", [File])}
end,
case MaybeModule of
{ok, Module, Binary} ->
Warnings = check_shadow(File, Module),
{ok, MoreWarnings} = load_binary(Module, File, Binary, Instrumented),
{ok, Module, Warnings ++ MoreWarnings};
Error -> Error
end.
%%------------------------------------------------------------------------------
-spec is_instrumenting() -> {'true', module()} | 'false'.
is_instrumenting() ->
Instrumented = get_instrumented_table(),
[{_, V}] = ets:lookup(Instrumented, {is_instrumenting}),
V.
-spec set_is_instrumenting( {'true', module()} | 'false') -> 'ok'.
set_is_instrumenting(Value) ->
Instrumented = get_instrumented_table(),
ets:insert(Instrumented, {{is_instrumenting}, Value}),
ok.
%%------------------------------------------------------------------------------
get_instrumented_table() ->
concuerror_instrumented.
check_shadow(File, Module) ->
Default = code:which(Module),
case Default =:= non_existing of
true -> [];
false ->
[io_lib:format("File ~s shadows ~s (found in path)", [File, Default])]
end.
load_binary(Module, Filename, Beam, Instrumented) ->
Core = get_core(Beam),
{InstrumentedCore, Warnings} =
case ets:lookup(Instrumented, Module) =:= [] of
true ->
ets:insert(Instrumented, {Module, concuerror_instrumented}),
concuerror_instrumenter:instrument(Module, Core, Instrumented);
false ->
{Core, []}
end,
{ok, _, NewBinary} =
compile:forms(InstrumentedCore, [from_core, report_errors, binary]),
{module, Module} = code:load_binary(Module, Filename, NewBinary),
{ok, Warnings}.
get_core(Beam) ->
{ok, {Module, [{abstract_code, ChunkInfo}]}} =
beam_lib:chunks(Beam, [abstract_code]),
case ChunkInfo of
{_, Chunk} ->
{ok, Module, Core} = compile:forms(Chunk, [binary, to_core0]),
Core;
no_abstract_code ->
{ok, {Module, [{compile_info, CompileInfo}]}} =
beam_lib:chunks(Beam, [compile_info]),
{source, File} = proplists:lookup(source, CompileInfo),
{options, CompileOptions} = proplists:lookup(options, CompileInfo),
Filter =
fun(Option) ->
lists:member(element(1, Option), [d, i, parse_transform])
end,
CleanOptions = lists:filter(Filter, CompileOptions),
Options = [debug_info, report_errors, binary, to_core0|CleanOptions],
{ok, Module, Core} = compile:file(File, Options),
Core
end.