dbg_ieval.erl
来自「OTP是开放电信平台的简称」· ERL 代码 · 共 355 行
ERL
355 行
%% ``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 via the world wide web 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.%% %% The Initial Developer of the Original Code is Ericsson Utvecklings AB.%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings%% AB. All Rights Reserved.''%% %% $Id$%%-module(dbg_ieval).-export([eval/3,exit_info/5]).-export([eval_expr/3]).-export([check_exit_msg/3,exception/4,in_use_p/2]).-export([stack_level/0, bindings/1, stack_frame/2, backtrace/1]).-include("dbg_ieval.hrl").%%====================================================================%% External exports%%====================================================================%%--------------------------------------------------------------------%% eval(Mod, Func, Args) -> Meta%% Mod = Func = atom()%% Args = [term()]%% MFA = {Mod,Func,Args} | {Mod,Func,Arity} | {Fun,Args}%% Arity = integer()%% Meta = pid()%% Entry point from debugged process (dbg_debugged).%% Immediately returns the pid for the meta process.%% The evaluated value will later be sent as a message to%% the calling process.%%--------------------------------------------------------------------eval(Mod, Func, Args) -> Debugged = self(), Int = dbg_iserver:find(), case dbg_iserver:call(Int, {get_meta,Debugged}) of {ok,Meta} -> Meta ! {re_entry, Debugged, {eval,{Mod,Func,Args}}}, Meta; {error,not_interpreted} -> spawn(fun() -> meta(Int, Debugged, Mod, Func, Args) end) end.%%--------------------------------------------------------------------%% exit_info(Int, AttPid, OrigPid, Reason, ExitInfo)%% Int = AttPid = OrigPid = pid()%% Reason = term()%% ExitInfo = {{Mod,Line}, Bs, Stack} | {}%% Meta process started when attaching to a terminated process.%% Spawned (by dbg_iserver) in response to user request.%%--------------------------------------------------------------------exit_info(Int, AttPid, OrigPid, Reason, ExitInfo) -> put(int, Int), put(attached, AttPid), put(breakpoints, dbg_iserver:call(Int, all_breaks)), put(self, OrigPid), put(exit_info, ExitInfo), case ExitInfo of {{Mod,Line},Bs,S} -> Stack = binary_to_term(S), put(stack, Stack), Le = stack_level(Stack), dbg_icmd:tell_attached({exit_at, {Mod, Line}, Reason, Le}), exit_loop(OrigPid, Reason, Bs,#ieval{module=Mod,line=Line}); {} -> put(stack, []), dbg_icmd:tell_attached({exit_at, null, Reason, 1}), exit_loop(OrigPid, Reason, erl_eval:new_bindings(),#ieval{}) end.%%--------------------------------------------------------------------%% eval_expr(Expr, Bs, Ieval) -> {value, Value, Bs}%%%% Evalute a shell expression in the real process.%% Called (dbg_icmd) in response to a user request.%%--------------------------------------------------------------------eval_expr(Expr, Bs, Ieval) -> %% Save current exit info ExitInfo = get(exit_info), Stacktrace = get(stacktrace), %% Emulate a surrounding catch try debugged_cmd({eval,Expr,Bs}, Bs, Ieval) catch Class:Reason -> Result = case Class of throw -> Reason; _ -> {'EXIT', Reason} end, %% Reset exit info put(exit_info, ExitInfo), put(stacktrace, Stacktrace), {value, Result, Bs} end.%%--------------------------------------------------------------------%% check_exit_msg(Msg, Bs, Ieval)%% Msg = term()%% Check if Msg is an 'EXIT' msg from the iserver or a 'DOWN' msg%% from the debugged process. If so exit with correct reason.%%--------------------------------------------------------------------check_exit_msg({'EXIT', Int, Reason}, _Bs, #ieval{level=Le}) -> %% This *must* be interpreter which has terminated, %% we are not linked to anyone else if Le==1 -> exit(Reason); Le>1 -> exit({Int, Reason}) end;check_exit_msg({'DOWN',_,_,_,Reason}, Bs, #ieval{level=Le, module=Mod, line=Li}) -> %% This *must* be Debugged which has terminated, %% we are not monitoring anyone else %% Inform Int about current position, bindings and stack ExitInfo = case get(exit_info) of %% Debugged has been terminated by someone %% - really the position, bindings and stack are of no %% importance in this case %% If we don't save them, however, post-mortem analysis %% of the process isn't possible undefined when Le==1 -> % died outside interpreted code {}; undefined when Le>1 -> StackBin = term_to_binary(get(stack)), {{Mod, Li}, Bs, StackBin}; %% Debugged has terminated due to an exception ExitInfo0 -> ExitInfo0 end, dbg_iserver:cast(get(int), {set_exit_info,self(),ExitInfo}), if Le==1 -> exit(Reason); Le>1 -> exit({get(self), Reason}) end;check_exit_msg(_Msg, _Bs, _Ieval) -> ignore.%%--------------------------------------------------------------------%% exception(Class, Reason, Bs, Ieval)%% Class = error | exit | throw%% Reason = term()%% Bs = bindings()%% Ieval = #ieval{}%% Store information about where in the code the error is located%% and then raise the exception.%%--------------------------------------------------------------------exception(Class, Reason, Bs, Ieval) -> exception(Class, Reason, fix_stacktrace(1), Bs, Ieval).exception(Class, Reason, Stacktrace, Bs, #ieval{module=M, line=Line}) -> ExitInfo = {{M,Line}, Bs, term_to_binary(get(stack))}, put(exit_info, ExitInfo), put(stacktrace, Stacktrace), erlang:Class(Reason).%%--------------------------------------------------------------------%% in_use_p(Mod, Cm) -> boolean()%% Mod = Cm = atom()%% Returns true if Mod is found on the stack, otherwise false.%%--------------------------------------------------------------------in_use_p(Mod, Mod) -> true;in_use_p(Mod, _Cm) -> case get(trace_stack) of false -> true; _ -> % all | no_tail lists:any(fun({_,{M,_,_,_}}) when M =:= Mod -> true; (_) -> false end, get(stack)) end.%%====================================================================%% Internal functions%%====================================================================%%--Loops-------------------------------------------------------------%% Entry point for first-time initialization of meta processmeta(Int, Debugged, M, F, As) -> process_flag(trap_exit, true), erlang:monitor(process, Debugged), %% Inform dbg_iserver, get the initial status in return Pargs = case {M, F} of %% If it's a fun we're evaluating, show a text %% representation of the fun and its arguments, %% not dbg_ieval:eval_fun(...) {dbg_ieval, eval_fun} -> {Mx, Fx} = lists:last(As), {Mx, Fx, lists:nth(2, As)}; _ -> {M, F, As} end, Status = dbg_iserver:call(Int, {new_process,Debugged,self(),Pargs}), %% Initiate process dictionary put(int, Int), % pid() dbg_iserver put(attached, undefined),% pid() attached process put(breakpoints, dbg_iserver:call(Int, all_breaks)), put(cache, []), put(next_break, Status), % break | running (other values later) put(self, Debugged), % pid() interpreted process put(stack, []), put(stacktrace, []), put(trace_stack, dbg_iserver:call(Int, get_stack_trace)), put(trace, false), % bool() Trace on/off put(user_eval, []), %% Send the result of the meta process Ieval = #ieval{}, Debugged ! {sys, self(), eval_mfa(Debugged,M,F,As,Ieval)}, dbg_iserver:cast(Int, {set_status, self(), idle, {}}), dbg_icmd:tell_attached(idle), meta_loop(Debugged, erl_eval:new_bindings(), Ieval).debugged_cmd(Cmd, Bs, Ieval) -> Debugged = get(self), Stacktrace = fix_stacktrace(2), Debugged ! {sys, self(), {command,Cmd,Stacktrace}}, meta_loop(Debugged, Bs, Ieval).meta_loop(Debugged, Bs, #ieval{level=Le} = Ieval) -> receive %% The following messages can only be received when Meta is %% waiting for Debugged to evaluate non-interpreted code %% or a Bif. Le>1 {sys, Debugged, {value,Val}} -> {value, Val, Bs}; {sys, Debugged, {value,Val,Bs2}} -> {value, Val, Bs2}; {sys, Debugged, {exception,{Class,Reason,Stacktrace}}} -> case get(exit_info) of %% Error occured outside interpreted code undefined -> exception(Class,Reason,Stacktrace,Bs,Ieval); %% Error must have occured within a re-entry to %% interpreted code, simply raise the exception _ -> erlang:Class(Reason) end; %% Re-entry to Meta from non-interpreted code {re_entry, Debugged, {eval,{M,F,As}}} when Le==1 -> %% Reset process dictionary %% This is really only necessary if the process left %% interpreted code at a call level > 1 put(stack, []), put(stacktrace, []), put(exit_info, undefined), dbg_iserver:cast(get(int), {set_status,self(),running,{}}), dbg_icmd:tell_attached(running), %% Tell attached process(es) to update source code. dbg_icmd:tell_attached({re_entry,M,F}), %% Send the result of the meta process Debugged ! {sys,self(),eval_mfa(Debugged,M,F,As,Ieval)}, dbg_iserver:cast(get(int), {set_status,self(),idle,{}}), dbg_icmd:tell_attached(idle), meta_loop(Debugged, Bs, Ieval); %% Evaluation in Debugged results in call to interpreted %% function (probably? a fun) {re_entry, Debugged, {eval,{M,F,As}}} when Le>1 -> Ieval2 = Ieval#ieval{module=undefined, line=-1}, Debugged ! {sys,self(),eval_mfa(Debugged,M,F,As,Ieval2)}, meta_loop(Debugged, Bs, Ieval); Msg -> check_exit_msg(Msg, Bs, Ieval), dbg_icmd:handle_msg(Msg, idle, Bs, Ieval), meta_loop(Debugged, Bs, Ieval) end.exit_loop(OrigPid, Reason, Bs, Ieval) -> receive Msg -> check_exit_msg(Msg, Bs, Ieval), dbg_icmd:handle_msg(Msg, exit_at, Bs, Ieval), exit_loop(OrigPid, Reason, Bs, Ieval) end.%%--Stack emulation---------------------------------------------------%% We keep track of a call stack that is used for%% 1) saving stack frames that can be inspected from an Attached%% Process GUI (using dbg_icmd:get(Meta, stack_frame, {Dir, SP})%% 2) generate an approximation of regular stacktrace -- sent to%% Debugged when it should raise an exception or evaluate a%% function (since it might possible raise an exception)%%%% Stack = [Entry]%% Entry = {Le, {MFA, Where, Bs}}%% Le = int() % current call level%% MFA = {M,F,Args} % called function (or fun)%% | {Fun,Args} %%% Where = {M,Li} % from where (module+line) function is called%% Bs = bindings() % current variable bindings%%%% How to push depends on the "Stack Trace" option (value saved in%% process dictionary item 'trace_stack').%% all - everything is pushed%% no_tail - tail recursive push%% false - nothing is pushed%% Whenever a function returns, the corresponding call frame is popped.push(MFA, Bs, #ieval{level=Le,module=Cm,line=Li,last_call=Lc}) -> Entry = {Le, {MFA, {Cm,Li}, Bs}}, case get(trace_stack) of false -> ignore; no_tail when Lc -> case get(stack) of [] -> put(stack, [Entry]); [_Entry|Entries] -> put(stack, [Entry|Entries]) end; _ -> % all | no_tail when Lc==false put(stack, [Entry|get(stack)]) end.pop() -> case get(trace_stack) of false -> ignore; _ -> % all
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?