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 + -
显示快捷键?