diff options
Diffstat (limited to 'lib/debugger/src/dbg_istk.erl')
-rw-r--r-- | lib/debugger/src/dbg_istk.erl | 245 |
1 files changed, 245 insertions, 0 deletions
diff --git a/lib/debugger/src/dbg_istk.erl b/lib/debugger/src/dbg_istk.erl new file mode 100644 index 0000000000..c6922a80e4 --- /dev/null +++ b/lib/debugger/src/dbg_istk.erl @@ -0,0 +1,245 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2011. 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(dbg_istk). +-export([init/0,delayed_to_external/0,from_external/1, + push/3,pop/0,pop/1,stack_level/0, + delayed_stacktrace/0,delayed_stacktrace/2, + bindings/1,stack_frame/2,backtrace/2, + in_use_p/2]). + +-include("dbg_ieval.hrl"). + +-define(STACK, ?MODULE). + +-record(e, + {level, %Level + mfa, %{Mod,Func,Args|Arity}|{Fun,Args} + line, %Line called from + bindings, + lc %Last call (true|false) + }). + +init() -> + init([]). + +delayed_to_external() -> + Stack = get(?STACK), + fun() -> {stack,term_to_binary(Stack)} end. + +from_external({stack,Stk}) -> + put(?STACK, binary_to_term(Stk)). + +init(Stack) -> + put(?STACK, Stack). + +%% 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) +%% +%% 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(Bs, #ieval{level=Le,module=Mod,function=Name, + arguments=As,line=Li}=Ieval, Lc) -> + Entry = #e{level=Le,mfa={Mod,Name,As},line=Li,bindings=Bs,lc=Lc}, + case get(trace_stack) of + false -> + Ieval#ieval{level=Le+1}; + no_tail when Lc -> + Ieval; + _ -> % all | no_tail when Lc =:= false + put(?STACK, [Entry|get(?STACK)]), + Ieval#ieval{level=Le+1} + end. + +pop() -> + case get(trace_stack) of + false -> ignore; + _ -> % all ¦ no_tail + case get(?STACK) of + [_Entry|Entries] -> + put(?STACK, Entries); + [] -> + ignore + end + end. + +pop(Le) -> + case get(trace_stack) of + false -> ignore; + _ -> % all | no_tail + put(?STACK, pop(Le, get(?STACK))) + end. + +pop(Level, [#e{level=Le}|Stack]) when Level =< Le -> + pop(Level, Stack); +pop(_Level, Stack) -> + Stack. + +%% stack_level() -> Le +%% stack_level(Stack) -> Le +%% Top call level +stack_level() -> + stack_level(get(?STACK)). + +stack_level([]) -> 1; +stack_level([#e{level=Le}|_]) -> Le. + +%% delayed_stacktrace() -> CreateStacktraceFun +%% delayed_stacktrace(ArgFlag, #ieval{}) -> CreateStacktraceFun +%% ArgFlag = no_args | include_args +%% CreateStacktraceFun = fun(NumberOfEntries) +%% +%% Return a fun that can convert the internal stack format to +%% an imitation of the regular stacktrace. + +delayed_stacktrace() -> + Stack0 = get(?STACK), + fun(NumEntries) -> + Stack = stacktrace(NumEntries, Stack0, []), + [finalize(ArityOnly) || {ArityOnly,_} <- Stack] + end. + +delayed_stacktrace(include_args, Ieval) -> + #ieval{module=Mod,function=Name,arguments=As,line=Li} = Ieval, + Stack0 = [#e{mfa={Mod,Name,As},line=Li}|get(?STACK)], + fun(NumEntries) -> + case stacktrace(NumEntries, Stack0, []) of + [] -> + []; + [{_,WithArgs}|Stack] -> + [finalize(WithArgs) | + [finalize(ArityOnly) || {ArityOnly,_} <- Stack]] + end + end; +delayed_stacktrace(no_args, Ieval) -> + #ieval{module=Mod,function=Name,arguments=As,line=Li} = Ieval, + Stack0 = [#e{mfa={Mod,Name,As},line=Li}|get(?STACK)], + fun(NumEntries) -> + Stack = stacktrace(NumEntries, Stack0, []), + [finalize(ArityOnly) || {ArityOnly,_} <- Stack] + end. + +stacktrace(N, [#e{lc=true}|T], Acc) -> + stacktrace(N, T, Acc); +stacktrace(N, [E|T], []) -> + stacktrace(N-1, T, [normalize(E)]); +stacktrace(N, [E|T], [{P,_}|_]=Acc) when N > 0 -> + case normalize(E) of + {P,_} -> + stacktrace(N, T, Acc); + New -> + stacktrace(N-1, T, [New|Acc]) + end; +stacktrace(_, _, Acc) -> + lists:reverse(Acc). + +normalize(#e{mfa={M,Fun,As},line=Li}) when is_function(Fun) -> + Loc = {M,Li}, + {{Fun,length(As),Loc},{Fun,As,Loc}}; +normalize(#e{mfa={M,F,As},line=Li}) -> + Loc = {M,Li}, + {{M,F,length(As),Loc},{M,F,As,Loc}}. + +finalize({M,F,A,Loc}) -> {M,F,A,line(Loc)}; +finalize({Fun,A,Loc}) -> {Fun,A,line(Loc)}. + +line({Mod,Line}) when Line > 0 -> + [{file,atom_to_list(Mod)++".erl"},{line,Line}]; +line(_) -> []. + +%% bindings(SP) -> Bs +%% SP = Le % stack pointer +%% Return the bindings for the specified call level +bindings(SP) -> + bindings(SP, get(?STACK)). + +bindings(SP, [#e{level=SP,bindings=Bs}|_]) -> + Bs; +bindings(SP, [_Entry|Entries]) -> + bindings(SP, Entries); +bindings(_SP, []) -> + erl_eval:new_bindings(). + +%% stack_frame(Dir, SP) -> {Le, Where, Bs} | top | bottom +%% Dir = up | down +%% Where = {Cm, Li} +%% Cm = Module | undefined % module +%% Li = int() | -1 % line number +%% Bs = bindings() +%% Return stack frame info one step up/down from given stack pointer +%% up = to lower call levels +%% down = to higher call levels +stack_frame(up, SP) -> + stack_frame(SP, up, get(?STACK)); +stack_frame(down, SP) -> + stack_frame(SP, down, lists:reverse(get(?STACK))). + +stack_frame(SP, up, [#e{level=Le,mfa={Cm,_,_},line=Li,bindings=Bs}|_]) + when Le < SP -> + {Le,{Cm,Li},Bs}; +stack_frame(SP, down, [#e{level=Le,mfa={Cm,_,_},line=Li,bindings=Bs}|_]) + when Le > SP -> + {Le,{Cm,Li},Bs}; +stack_frame(SP, Dir, [#e{level=SP}|Stack]) -> + case Stack of + [#e{level=Le,mfa={Cm,_,_},line=Li,bindings=Bs}|_] -> + {Le,{Cm,Li},Bs}; + [] when Dir =:= up -> + top; + [] when Dir =:= down -> + bottom + end; +stack_frame(SP, Dir, [_Entry|Stack]) -> + stack_frame(SP, Dir, Stack). + +%% backtrace(HowMany) -> Backtrace +%% HowMany = all | int() +%% Backtrace = {Le, MFA} +%% Return all/the last N called functions, in reversed call order +backtrace(HowMany, Ieval) -> + #ieval{level=Level,module=Mod,function=Name,arguments=As} = Ieval, + Stack0 = [#e{level=Level,mfa={Mod,Name,As}}|get(?STACK)], + Stack = case HowMany of + all -> Stack0; + N -> lists:sublist(Stack0, N) + end, + [{Le,MFA} || #e{level=Le,mfa=MFA} <- Stack]. + +%%-------------------------------------------------------------------- +%% 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(#e{mfa={M,_,_}}) when M =:= Mod -> true; + (_) -> false + end, get(?STACK)) + end. |