aboutsummaryrefslogtreecommitdiffstats
path: root/lib/debugger/src/int.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/debugger/src/int.erl')
-rw-r--r--lib/debugger/src/int.erl736
1 files changed, 736 insertions, 0 deletions
diff --git a/lib/debugger/src/int.erl b/lib/debugger/src/int.erl
new file mode 100644
index 0000000000..eeb4df4a8e
--- /dev/null
+++ b/lib/debugger/src/int.erl
@@ -0,0 +1,736 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(int).
+
+%% External exports
+-export([i/1, i/2, ni/1, ni/2, n/1, nn/1, interpreted/0, file/1,
+ interpretable/1]).
+-export([auto_attach/0, auto_attach/1, auto_attach/2,
+ stack_trace/0, stack_trace/1]).
+-export([break/2, delete_break/2, break_in/3, del_break_in/3,
+ no_break/0, no_break/1,
+ disable_break/2, enable_break/2,
+ action_at_break/3, test_at_break/3, get_binding/2,
+ all_breaks/0, all_breaks/1]).
+-export([snapshot/0, clear/0]).
+-export([continue/1, continue/3]).
+
+%% External exports only to be used by Debugger
+-export([start/0, stop/0, subscribe/0]).
+-export([attach/2, step/1, next/1, finish/1]).
+
+%% External exports only to be used by an attached process
+-export([attached/1, meta/2, meta/3, contents/2, functions/1]).
+
+%% External export only to be used by error_handler
+-export([eval/3]).
+
+-include_lib("kernel/include/file.hrl").
+
+%%==Erlang Interpreter================================================
+%%
+%% int
+%% ---
+%% Interface module.
+%%
+%% i
+%% -
+%% Interface module to int, retained for backwards compatibility only.
+%%
+%% dbg_debugged
+%% ------------
+%% Contains the message loops for a debugged process and is the main
+%% entry point from the breakpoint handler in the error_handler module
+%% (via the int module).
+%%
+%% When a process is debugged, most code is executed in another
+%% process, called the meta process. When the meta process is
+%% interpreting code, the process being debugged just waits in a
+%% receive loop in dbg_debugged. However the debugged process itself
+%% calls any BIFs that must execute in the correct process (such as
+%% link/1 and spawn_link/1), and external code which is not
+%% interpreted.
+%%
+%% dbg_icmd, dbg_ieval
+%% -------------------
+%% Code for the meta process.
+%%
+%% dbg_iserver
+%% -----------
+%% Interpreter main process, keeping and distributing information
+%% about interpreted modules and debugged processes.
+%%
+%% dbg_idb
+%% -------
+%% ETS wrapper, allowing transparent access to tables at a remote node.
+%%
+%% dbg_iload
+%% ---------
+%% Code for interpreting a module.
+%%====================================================================
+
+%%====================================================================
+%% External exports
+%%====================================================================
+
+%%--------------------------------------------------------------------
+%% i(AbsMods) -> {module,Mod} | error | ok
+%% ni(AbsMods) -> {module,Mod} | error | ok
+%% AbsMods = AbsMod | [AbsMod]
+%% AbsMod = atom() | string()
+%% Mod = atom()
+%% Options = term() ignored
+%%--------------------------------------------------------------------
+i(AbsMods) -> i2(AbsMods, local, ok).
+i(AbsMods, _Options) -> i2(AbsMods, local, ok).
+ni(AbsMods) -> i2(AbsMods, distributed, ok).
+ni(AbsMods, _Options) -> i2(AbsMods, distributed, ok).
+
+i2([AbsMod|AbsMods], Dist, Acc)
+ when is_atom(AbsMod); is_list(AbsMod); is_tuple(AbsMod) ->
+ Res = int_mod(AbsMod, Dist),
+ case Acc of
+ error ->
+ i2(AbsMods, Dist, Acc);
+ _ ->
+ i2(AbsMods, Dist, Res)
+ end;
+i2([], _Dist, Acc) ->
+ Acc;
+i2(AbsMod, Dist, _Acc) when is_atom(AbsMod); is_list(AbsMod); is_tuple(AbsMod) ->
+ int_mod(AbsMod, Dist).
+
+%%--------------------------------------------------------------------
+%% n(AbsMods) -> ok
+%% nn(AbsMods) -> ok
+%%--------------------------------------------------------------------
+n(AbsMods) -> n2(AbsMods, local).
+nn(AbsMods) -> n2(AbsMods, distributed).
+
+n2([AbsMod|AbsMods], Dist) when is_atom(AbsMod); is_list(AbsMod) ->
+ del_mod(AbsMod, Dist),
+ n2(AbsMods, Dist);
+n2([AbsMod], Dist) when is_atom(AbsMod); is_list(AbsMod) ->
+ del_mod(AbsMod, Dist);
+n2([], _Dist) ->
+ ok;
+n2(AbsMod, Dist) when is_atom(AbsMod); is_list(AbsMod) ->
+ del_mod(AbsMod, Dist).
+
+%%--------------------------------------------------------------------
+%% interpreted() -> [Mod]
+%%--------------------------------------------------------------------
+interpreted() ->
+ dbg_iserver:safe_call(all_interpreted).
+
+%%--------------------------------------------------------------------
+%% file(Mod) -> File | {error, not_loaded}
+%% Mod = atom()
+%% File = string()
+%%--------------------------------------------------------------------
+file(Mod) when is_atom(Mod) ->
+ dbg_iserver:safe_call({file, Mod}).
+
+%%--------------------------------------------------------------------
+%% interpretable(AbsMod) -> true | {error, Reason}
+%% AbsMod = Mod | File
+%% Reason = no_src | no_beam | no_debug_info | badarg | {app, App}
+%%--------------------------------------------------------------------
+interpretable(AbsMod) ->
+ case check(AbsMod) of
+ {ok, _Res} -> true;
+ Error -> Error
+ end.
+
+%%--------------------------------------------------------------------
+%% auto_attach() -> false | {Flags, Function}
+%% auto_attach(false)
+%% auto_attach(false|Flags, Function)
+%% Flags = Flag | [Flag]
+%% Flag = init | break | exit
+%% Function = {Mod, Func} | {Mod, Func, Args}
+%% Will result in calling:
+%% spawn(Mod, Func, [Dist, Pid, Meta | Args]) (living process) or
+%% spawn(Mod, Func, [Dist, Pid, Reason, Info | Args]) (dead process)
+%%--------------------------------------------------------------------
+auto_attach() ->
+ dbg_iserver:safe_call(get_auto_attach).
+
+auto_attach(false) ->
+ dbg_iserver:safe_cast({set_auto_attach, false}).
+
+auto_attach([], _Function) ->
+ auto_attach(false);
+auto_attach(Flags, {Mod, Func}) ->
+ auto_attach(Flags, {Mod, Func, []});
+auto_attach(Flags, {Mod, Func, Args}) when is_atom(Mod),is_atom(Func),is_list(Args) ->
+ check_flags(Flags),
+ dbg_iserver:safe_cast({set_auto_attach, Flags, {Mod, Func, Args}}).
+
+check_flags([init|Flags]) -> check_flags(Flags);
+check_flags([break|Flags]) -> check_flags(Flags);
+check_flags([exit|Flags]) -> check_flags(Flags);
+check_flags([]) -> true.
+
+%%--------------------------------------------------------------------
+%% stack_trace() -> Flag
+%% stack_trace(Flag)
+%% Flag = all | true | no_tail | false
+%%--------------------------------------------------------------------
+stack_trace() ->
+ dbg_iserver:safe_call(get_stack_trace).
+
+stack_trace(true) ->
+ stack_trace(all);
+stack_trace(Flag) ->
+ check_flag(Flag),
+ dbg_iserver:safe_cast({set_stack_trace, Flag}).
+
+check_flag(all) -> true;
+check_flag(no_tail) -> true;
+check_flag(false) -> true.
+
+%%--------------------------------------------------------------------
+%% break(Mod, Line) -> ok | {error, break_exists}
+%% delete_break(Mod, Line) -> ok
+%% break_in(Mod, Func, Arity) -> ok | {error, function_not_found}
+%% del_break_in(Mod, Function, Arity) -> ok | {error, function_not_found}
+%% no_break()
+%% no_break(Mod)
+%% disable_break(Mod, Line) -> ok
+%% enable_break(Mod, Line) -> ok
+%% action_at_break(Mod, Line, Action) -> ok
+%% test_at_break(Mod, Line, Function) -> ok
+%% get_binding(Var, Bindings) -> {value, Value} | unbound
+%% all_breaks() -> [Break]
+%% all_breaks(Mod) -> [Break]
+%% Mod = atom()
+%% Line = integer()
+%% Func = atom() function name
+%% Arity = integer()
+%% Action = enable | disable | delete
+%% Function = {Mod, Func} must have arity 1 (Bindings)
+%% Var = atom()
+%% Bindings = Value = term()
+%% Break = {Point, Options}
+%% Point = {Mod, Line}
+%% Options = [Status, Action, null, Cond]
+%% Status = active | inactive
+%% Cond = null | Function
+%%--------------------------------------------------------------------
+break(Mod, Line) when is_atom(Mod), is_integer(Line) ->
+ dbg_iserver:safe_call({new_break, {Mod, Line},
+ [active, enable, null, null]}).
+
+delete_break(Mod, Line) when is_atom(Mod), is_integer(Line) ->
+ dbg_iserver:safe_cast({delete_break, {Mod, Line}}).
+
+break_in(Mod, Func, Arity) when is_atom(Mod), is_atom(Func), is_integer(Arity) ->
+ case dbg_iserver:safe_call({is_interpreted, Mod, Func, Arity}) of
+ {true, Clauses} ->
+ Lines = first_lines(Clauses),
+ lists:foreach(fun(Line) -> break(Mod, Line) end, Lines);
+ false ->
+ {error, function_not_found}
+ end.
+
+del_break_in(Mod, Func, Arity) when is_atom(Mod), is_atom(Func), is_integer(Arity) ->
+ case dbg_iserver:safe_call({is_interpreted, Mod, Func, Arity}) of
+ {true, Clauses} ->
+ Lines = first_lines(Clauses),
+ lists:foreach(fun(Line) -> delete_break(Mod, Line) end,
+ Lines);
+ false ->
+ {error, function_not_found}
+ end.
+
+first_lines(Clauses) ->
+ lists:map(fun(Clause) -> first_line(Clause) end, Clauses).
+
+first_line({clause,_L,_Vars,_,Exprs}) ->
+ first_line(Exprs);
+%% Common Test adaptation
+first_line([{call_remote,0,ct_line,line,_As}|Exprs]) ->
+ first_line(Exprs);
+first_line([Expr|_Exprs]) -> % Expr = {Op, Line, ..varying no of args..}
+ element(2, Expr).
+
+no_break() ->
+ dbg_iserver:safe_cast(no_break).
+
+no_break(Mod) when is_atom(Mod) ->
+ dbg_iserver:safe_cast({no_break, Mod}).
+
+disable_break(Mod, Line) when is_atom(Mod), is_integer(Line) ->
+ dbg_iserver:safe_cast({break_option, {Mod, Line}, status, inactive}).
+
+enable_break(Mod, Line) when is_atom(Mod), is_integer(Line) ->
+ dbg_iserver:safe_cast({break_option, {Mod, Line}, status, active}).
+
+action_at_break(Mod, Line, Action) when is_atom(Mod), is_integer(Line) ->
+ check_action(Action),
+ dbg_iserver:safe_cast({break_option, {Mod, Line}, action, Action}).
+
+check_action(enable) -> true;
+check_action(disable) -> true;
+check_action(delete) -> true.
+
+test_at_break(Mod, Line, Function) when is_atom(Mod), is_integer(Line) ->
+ check_function(Function),
+ dbg_iserver:safe_cast({break_option, {Mod, Line}, condition, Function}).
+
+check_function({Mod, Func}) when is_atom(Mod), is_atom(Func) -> true.
+
+get_binding(Var, Bs) ->
+ dbg_icmd:get_binding(Var, Bs).
+
+all_breaks() ->
+ dbg_iserver:safe_call(all_breaks).
+all_breaks(Mod) when is_atom(Mod) ->
+ dbg_iserver:safe_call({all_breaks, Mod}).
+
+%%--------------------------------------------------------------------
+%% snapshot() -> [{Pid, Init, Status, Info}]
+%% Pid = pid()
+%% Init = atom() First interpreted function
+%% Status = idle | running | waiting | break | exit
+%% Info = {} | {Mod, Line} | ExitReason
+%% Mod = atom()
+%% Line = integer()
+%% ExitReason = term()
+%%--------------------------------------------------------------------
+snapshot() ->
+ dbg_iserver:safe_call(snapshot).
+
+%%--------------------------------------------------------------------
+%% clear()
+%%--------------------------------------------------------------------
+clear() ->
+ dbg_iserver:safe_cast(clear).
+
+%%--------------------------------------------------------------------
+%% continue(Pid) -> ok | {error, not_interpreted}
+%% continue(X, Y, Z) -> ok | {error, not_interpreted}
+%%--------------------------------------------------------------------
+continue(Pid) when is_pid(Pid) ->
+ case dbg_iserver:safe_call({get_meta, Pid}) of
+ {ok, Meta} when is_pid(Meta) ->
+ dbg_icmd:continue(Meta),
+ ok;
+ Error ->
+ Error
+ end.
+
+continue(X, Y, Z) when is_integer(X), is_integer(Y), is_integer(Z) ->
+ continue(c:pid(X, Y, Z)).
+
+
+%%====================================================================
+%% External exports only to be used by Debugger
+%%====================================================================
+
+%%--------------------------------------------------------------------
+%% start()
+%% stop()
+%% Functions for starting and stopping dbg_iserver explicitly.
+%%--------------------------------------------------------------------
+start() -> dbg_iserver:start().
+stop() ->
+ lists:foreach(
+ fun(Mod) ->
+ everywhere(distributed,
+ fun() ->
+ erts_debug:breakpoint({Mod,'_','_'}, false)
+ end)
+ end,
+ interpreted()),
+ dbg_iserver:stop().
+
+%%--------------------------------------------------------------------
+%% subscribe()
+%% Subscribe to information from dbg_iserver. The process calling this
+%% function will receive the following messages:
+%% {int, {interpret, Mod}}
+%% {int, {no_interpret, Mod}}
+%% {int, {new_process, Pid, Function, Status, Info}}
+%% {int, {new_status, Pid, Status, Info}}
+%% {int, {new_break, {Point, Options}}}
+%% {int, {delete_break, Point}}
+%% {int, {break_options, {Point, Options}}}
+%% {int, no_break}
+%% {int, {no_break, Mod}}
+%% {int, {auto_attach, false|{Flags, Function}}}
+%% {int, {stack_trace, Flag}}
+%%--------------------------------------------------------------------
+subscribe() -> dbg_iserver:cast({subscribe, self()}).
+
+%%--------------------------------------------------------------------
+%% attach(Pid, Function)
+%% Pid = pid()
+%% Function = {Mod, Func} | {Mod, Func, Args} (see auto_attach/2)
+%% Tell dbg_iserver to attach to Pid using Function. Will result in:
+%% spawn(Mod, Func, [Pid, Status | Args])
+%%--------------------------------------------------------------------
+attach(Pid, {Mod, Func}) ->
+ attach(Pid, {Mod, Func, []});
+attach(Pid, Function) ->
+ dbg_iserver:cast({attach, Pid, Function}).
+
+%%--------------------------------------------------------------------
+%% step(Pid)
+%% next(Pid)
+%% (continue(Pid))
+%% finish(Pid)
+%%--------------------------------------------------------------------
+step(Pid) ->
+ {ok, Meta} = dbg_iserver:call({get_meta, Pid}),
+ dbg_icmd:step(Meta).
+next(Pid) ->
+ {ok, Meta} = dbg_iserver:call({get_meta, Pid}),
+ dbg_icmd:next(Meta).
+finish(Pid) ->
+ {ok, Meta} = dbg_iserver:call({get_meta, Pid}),
+ dbg_icmd:finish(Meta).
+
+
+%%====================================================================
+%% External exports only to be used by an attached process
+%%====================================================================
+
+%%--------------------------------------------------------------------
+%% attached(Pid) -> {ok, Meta} | error
+%% Pid = Meta = pid()
+%% Tell dbg_iserver that I have attached to Pid. dbg_iserver informs
+%% the meta process and returns its pid. dbg_iserver may also refuse,
+%% if there already is a process attached to Pid.
+%%--------------------------------------------------------------------
+attached(Pid) ->
+ dbg_iserver:call({attached, self(), Pid}).
+
+%%--------------------------------------------------------------------
+%% meta(Meta, Cmd)
+%% Meta = pid()
+%% Cmd = step | next | continue | finish | skip | timeout | stop
+%% Cmd = messages => [Message]
+%% meta(Meta, Cmd, Arg)
+%% Cmd = trace, Arg = bool()
+%% Cmd = stack_trace Arg = all | notail | false
+%% Cmd = stack_frame Arg = {up|down, Sp}
+%% => {Sp, Mod, Line} | top | bottom
+%% Cmd = backtrace Arg = integer()
+%% => {Sp, Mod, {Func, Arity}, Line}
+%% Cmd = eval Arg = {Cm, Cmd} | {Cm, Cmd, Sp}
+%%--------------------------------------------------------------------
+meta(Meta, step) -> dbg_icmd:step(Meta);
+meta(Meta, next) -> dbg_icmd:next(Meta);
+meta(Meta, continue) -> dbg_icmd:continue(Meta);
+meta(Meta, finish) -> dbg_icmd:finish(Meta);
+meta(Meta, skip) -> dbg_icmd:skip(Meta);
+meta(Meta, timeout) -> dbg_icmd:timeout(Meta);
+meta(Meta, stop) -> dbg_icmd:stop(Meta);
+meta(Meta, messages) -> dbg_icmd:get(Meta, messages, null).
+
+meta(Meta, trace, Trace) -> dbg_icmd:set(Meta, trace, Trace);
+meta(Meta, stack_trace, Flag) -> dbg_icmd:set(Meta, stack_trace, Flag);
+meta(Meta, bindings, Stack) -> dbg_icmd:get(Meta, bindings, Stack);
+meta(Meta, stack_frame, Arg) -> dbg_icmd:get(Meta, stack_frame, Arg);
+meta(Meta, backtrace, N) -> dbg_icmd:get(Meta, backtrace, N);
+meta(Meta, eval, Arg) -> dbg_icmd:eval(Meta, Arg).
+
+%%--------------------------------------------------------------------
+%% contents(Mod, Pid) -> string()
+%% Mod = atom()
+%% Pid = pid() | any
+%% Return the contents of an interpreted module.
+%%--------------------------------------------------------------------
+contents(Mod, Pid) ->
+ {ok, Bin} = dbg_iserver:call({contents, Mod, Pid}),
+ binary_to_list(Bin).
+
+%%--------------------------------------------------------------------
+%% functions(Mod) -> [[Name, Arity]]
+%% Mod = Name = atom()
+%% Arity = integer()
+%%--------------------------------------------------------------------
+functions(Mod) ->
+ lists:filter(fun([module_info, _Arity]) -> false;
+ (_Func) -> true
+ end,
+ dbg_iserver:call({functions, Mod})).
+
+
+%%====================================================================
+%% External exports only to be used by error_handler
+%%====================================================================
+
+eval(Mod, Func, Args) ->
+ dbg_debugged:eval(Mod, Func, Args).
+
+
+%%====================================================================
+%% Internal functions
+%%====================================================================
+
+%%--Interpreting modules----------------------------------------------
+
+int_mod({Mod, Src, Beam, BeamBin}, Dist)
+ when is_atom(Mod), is_list(Src), is_list(Beam), is_binary(BeamBin) ->
+ try
+ case is_file(Src) of
+ true ->
+ check_application(Src),
+ case check_beam(BeamBin) of
+ {ok, Exp, Abst, _BeamBin} ->
+ load({Mod, Src, Beam, BeamBin, Exp, Abst}, Dist);
+ error ->
+ error
+ end;
+ false ->
+ error
+ end
+ catch
+ throw:Reason ->
+ Reason
+ end;
+int_mod(AbsMod, Dist) when is_atom(AbsMod); is_list(AbsMod) ->
+ case check(AbsMod) of
+ {ok, Res} ->
+ load(Res, Dist);
+ {error, {app, App}} ->
+ io:format("** Cannot interpret ~p module: ~p~n",
+ [App, AbsMod]),
+ error;
+ _Error ->
+ io:format("** Invalid beam file or no abstract code: ~p\n",
+ [AbsMod]),
+ error
+ end.
+
+check(Mod) when is_atom(Mod) -> catch check_module(Mod);
+check(File) when is_list(File) -> catch check_file(File).
+
+load({Mod, Src, Beam, BeamBin, Exp, Abst}, Dist) ->
+ everywhere(Dist,
+ fun() ->
+ code:purge(Mod),
+ erts_debug:breakpoint({Mod,'_','_'}, false),
+ {module,Mod} = code:load_binary(Mod, Beam, BeamBin)
+ end),
+ case erl_prim_loader:get_file(filename:absname(Src)) of
+ {ok, SrcBin, _} ->
+ MD5 = code:module_md5(BeamBin),
+ Bin = term_to_binary({interpreter_module,Exp,Abst,SrcBin,MD5}),
+ {module, Mod} = dbg_iserver:safe_call({load, Mod, Src, Bin}),
+ everywhere(Dist,
+ fun() ->
+ true = erts_debug:breakpoint({Mod,'_','_'}, true) > 0
+ end),
+ {module, Mod};
+ error ->
+ error
+ end.
+
+check_module(Mod) ->
+ case code:which(Mod) of
+ Beam when is_list(Beam) ->
+ case find_src(Beam) of
+ Src when is_list(Src) ->
+ check_application(Src),
+ case check_beam(Beam) of
+ {ok, Exp, Abst, BeamBin} ->
+ {ok, {Mod, Src, Beam, BeamBin, Exp, Abst}};
+ error ->
+ {error, no_debug_info}
+ end;
+ error ->
+ {error, no_src}
+ end;
+ _ ->
+ {error, badarg}
+ end.
+
+check_file(Name0) ->
+ Src =
+ case is_file(Name0) of
+ true ->
+ Name0;
+ false ->
+ Name = Name0 ++ ".erl",
+ case is_file(Name) of
+ true -> Name;
+ false -> error
+ end
+ end,
+ if
+ is_list(Src) ->
+ check_application(Src),
+ Mod = scan_module_name(Src),
+ case find_beam(Mod, Src) of
+ Beam when is_list(Beam) ->
+ case check_beam(Beam) of
+ {ok, Exp, Abst, BeamBin} ->
+ {ok, {Mod, Src, Beam, BeamBin, Exp, Abst}};
+ error ->
+ {error, no_debug_info}
+ end;
+ error ->
+ {error, no_beam}
+ end;
+ true ->
+ {error, badarg}
+ end.
+
+%% Try to avoid interpreting a kernel, stdlib, gs or debugger module.
+check_application(Src) ->
+ case lists:reverse(filename:split(filename:absname(Src))) of
+ [_Mod,"src",AppS|_] ->
+ check_application2(AppS);
+ _ -> ok
+ end.
+check_application2("kernel-"++_) -> throw({error,{app,kernel}});
+check_application2("stdlib-"++_) -> throw({error,{app,stdlib}});
+check_application2("erts-"++_) -> throw({error,{app,erts}});
+check_application2("gs-"++_) -> throw({error,{app,gs}});
+check_application2("debugger-"++_) -> throw({error,{app,debugger}});
+check_application2(_) -> ok.
+
+find_src(Beam) ->
+ Src0 = filename:rootname(Beam) ++ ".erl",
+ case is_file(Src0) of
+ true -> Src0;
+ false ->
+ EbinDir = filename:dirname(Beam),
+ Src = filename:join([filename:dirname(EbinDir), "src",
+ filename:basename(Src0)]),
+ case is_file(Src) of
+ true -> Src;
+ false -> error
+ end
+ end.
+
+find_beam(Mod, Src) ->
+ SrcDir = filename:dirname(Src),
+ BeamFile = packages:last(Mod) ++ code:objfile_extension(),
+ File = filename:join(SrcDir, BeamFile),
+ case is_file(File) of
+ true -> File;
+ false -> find_beam_1(Mod, SrcDir)
+ end.
+
+find_beam_1(Mod, SrcDir) ->
+ RootDir = find_root_dir(SrcDir, packages:first(Mod)),
+ EbinDir = filename:join(RootDir, "ebin"),
+ CodePath = [EbinDir | code:get_path()],
+ BeamFile = to_path(Mod) ++ code:objfile_extension(),
+ lists:foldl(fun(_, Beam) when is_list(Beam) -> Beam;
+ (Dir, error) ->
+ File = filename:join(Dir, BeamFile),
+ case is_file(File) of
+ true -> File;
+ false -> error
+ end
+ end,
+ error,
+ CodePath).
+
+to_path(X) ->
+ filename:join(packages:split(X)).
+
+find_root_dir(Dir, [_|Ss]) ->
+ find_root_dir(filename:dirname(Dir), Ss);
+find_root_dir(Dir, []) ->
+ filename:dirname(Dir).
+
+check_beam(BeamBin) when is_binary(BeamBin) ->
+ case beam_lib:chunks(BeamBin, [abstract_code,exports]) of
+ {ok,{_Mod,[{abstract_code,no_abstract_code}|_]}} ->
+ error;
+ {ok,{_Mod,[{abstract_code,Abst},{exports,Exp}]}} ->
+ {ok,Exp,Abst, BeamBin};
+ _ ->
+ error
+ end;
+check_beam(Beam) when is_list(Beam) ->
+ {ok, Bin, _FullPath} = erl_prim_loader:get_file(filename:absname(Beam)),
+ check_beam(Bin).
+
+is_file(Name) ->
+ filelib:is_regular(filename:absname(Name), erl_prim_loader).
+
+everywhere(distributed, Fun) ->
+ case is_alive() of
+ true -> rpc:multicall(erlang, apply, [Fun,[]]);
+ false -> Fun()
+ end;
+everywhere(local, Fun) ->
+ Fun().
+
+scan_module_name(File) ->
+ case erl_prim_loader:get_file(filename:absname(File)) of
+ {ok, Bin, _FullPath} ->
+ Chars = binary_to_list(Bin),
+ R = (catch {ok, scan_module_name_1(Chars)}),
+ case R of
+ {ok, A} when is_atom(A) -> A;
+ _ -> error
+ end;
+ _ ->
+ error
+ end.
+
+scan_module_name_1(Chars) ->
+ case erl_scan:tokens("", Chars, 1) of
+ {done, {ok, Ts, _}, Rest} ->
+ scan_module_name_2(Ts, Rest);
+ _ ->
+ error
+ end.
+
+scan_module_name_2([{'-',_},{atom,_,module},{'(',_} | _]=Ts, _Chars) ->
+ scan_module_name_3(Ts);
+scan_module_name_2([{'-',_},{atom,_,_} | _], Chars) ->
+ scan_module_name_1(Chars);
+scan_module_name_2(_, _) ->
+ error.
+
+scan_module_name_3(Ts) ->
+ case erl_parse:parse_form(Ts) of
+ {ok, {attribute,_,module,{M,_}}} -> module_atom(M);
+ {ok, {attribute,_,module,M}} -> module_atom(M);
+ _ -> error
+ end.
+
+module_atom(A) when is_atom(A) -> A;
+module_atom(L) when is_list(L) -> list_to_atom(packages:concat(L)).
+
+%%--Stop interpreting modules-----------------------------------------
+
+del_mod(AbsMod, Dist) ->
+ Mod = if
+ is_atom(AbsMod) -> AbsMod;
+ is_list(AbsMod) ->
+ list_to_atom(filename:basename(AbsMod,".erl"))
+ end,
+ dbg_iserver:safe_cast({delete, Mod}),
+ everywhere(Dist,
+ fun() ->
+ erts_debug:breakpoint({Mod,'_','_'}, false),
+ erlang:yield()
+ end),
+ ok.