diff options
author | Björn Gustavsson <bjorn@erlang.org> | 2011-03-15 15:24:08 +0100 |
---|---|---|
committer | Björn Gustavsson <bjorn@erlang.org> | 2011-08-16 08:58:46 +0200 |
commit | 15fc610fe64c052faf1ba997b771123fe4c383d7 (patch) | |
tree | 39fb8286fe8cb23b36a2e85e0231eb60217830f1 /lib/debugger | |
parent | ae7858a2631df405f4460f6aefb1e08077d61a3a (diff) | |
download | otp-15fc610fe64c052faf1ba997b771123fe4c383d7.tar.gz otp-15fc610fe64c052faf1ba997b771123fe4c383d7.tar.bz2 otp-15fc610fe64c052faf1ba997b771123fe4c383d7.zip |
Break out stack handling into the dbg_istk module
Diffstat (limited to 'lib/debugger')
-rw-r--r-- | lib/debugger/src/Makefile | 1 | ||||
-rw-r--r-- | lib/debugger/src/dbg_icmd.erl | 10 | ||||
-rw-r--r-- | lib/debugger/src/dbg_ieval.erl | 238 | ||||
-rw-r--r-- | lib/debugger/src/dbg_istk.erl | 224 | ||||
-rw-r--r-- | lib/debugger/src/debugger.app.src | 1 |
5 files changed, 256 insertions, 218 deletions
diff --git a/lib/debugger/src/Makefile b/lib/debugger/src/Makefile index 8551fe887d..6dc7d0d783 100644 --- a/lib/debugger/src/Makefile +++ b/lib/debugger/src/Makefile @@ -44,6 +44,7 @@ MODULES= \ dbg_ieval \ dbg_iload \ dbg_iserver \ + dbg_istk \ dbg_ui_break \ dbg_ui_break_win \ dbg_ui_edit \ diff --git a/lib/debugger/src/dbg_icmd.erl b/lib/debugger/src/dbg_icmd.erl index e9502eaa2b..de60f4ad47 100644 --- a/lib/debugger/src/dbg_icmd.erl +++ b/lib/debugger/src/dbg_icmd.erl @@ -273,7 +273,7 @@ handle_int_msg({old_code,Mod}, Status, Bs, erase([Mod|db]), put(cache, []); true -> - case dbg_ieval:in_use_p(Mod, M) of + case dbg_istk:in_use_p(Mod, M) of true -> %% A call to Mod is on the stack (or might be), %% so we must terminate. @@ -342,11 +342,11 @@ handle_user_msg({set,stack_trace,Flag}, _Status, _Bs, _Ieval) -> handle_user_msg({get,bindings,From,SP}, _Status, Bs, _Ieval) -> reply(From, bindings, bindings(Bs, SP)); handle_user_msg({get,stack_frame,From,{Dir,SP}}, _Status, _Bs,_Ieval) -> - reply(From, stack_frame, dbg_ieval:stack_frame(Dir, SP)); + reply(From, stack_frame, dbg_istk:stack_frame(Dir, SP)); handle_user_msg({get,messages,From,_}, _Status, _Bs, _Ieval) -> reply(From, messages, messages()); handle_user_msg({get,backtrace,From,N}, _Status, _Bs, _Ieval) -> - reply(From, backtrace, dbg_ieval:backtrace(N)). + reply(From, backtrace, dbg_istk:backtrace(N)). set_stack_trace(true) -> set_stack_trace(all); @@ -366,11 +366,11 @@ reply(From, Tag, Reply) -> bindings(Bs, nostack) -> Bs; bindings(Bs, SP) -> - case dbg_ieval:stack_level() of + case dbg_istk:stack_level() of Le when SP > Le -> Bs; _ -> - dbg_ieval:bindings(SP) + dbg_istk:bindings(SP) end. messages() -> diff --git a/lib/debugger/src/dbg_ieval.erl b/lib/debugger/src/dbg_ieval.erl index 956dc50df0..c0ef9c6e7c 100644 --- a/lib/debugger/src/dbg_ieval.erl +++ b/lib/debugger/src/dbg_ieval.erl @@ -20,8 +20,7 @@ -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]). +-export([check_exit_msg/3,exception/4]). -include("dbg_ieval.hrl"). @@ -71,13 +70,12 @@ exit_info(Int, AttPid, OrigPid, Reason, ExitInfo) -> case ExitInfo of {{Mod,Line},Bs,S} -> - Stack = binary_to_term(S), - put(stack, Stack), - Le = stack_level(Stack), + dbg_istk:from_external(S), + Le = dbg_istk:stack_level(), dbg_icmd:tell_attached({exit_at, {Mod, Line}, Reason, Le}), exit_loop(OrigPid, Reason, Bs,#ieval{module=Mod,line=Line}); {} -> - put(stack, []), + dbg_istk:init(), dbg_icmd:tell_attached({exit_at, null, Reason, 1}), exit_loop(OrigPid, Reason, erl_eval:new_bindings(),#ieval{}) end. @@ -142,8 +140,8 @@ check_exit_msg({'DOWN',_,_,_,Reason}, Bs, undefined when Le =:= 1 -> % died outside interpreted code {}; undefined when Le > 1 -> - StackBin = term_to_binary(get(stack)), - {{Mod, Li}, Bs, StackBin}; + StackExternal = dbg_istk:to_external(), + {{Mod, Li}, Bs, StackExternal}; %% Debugged has terminated due to an exception ExitInfo0 -> @@ -170,30 +168,15 @@ check_exit_msg(_Msg, _Bs, _Ieval) -> %% and then raise the exception. %%-------------------------------------------------------------------- exception(Class, Reason, Bs, Ieval) -> - exception(Class, Reason, fix_stacktrace(1), Bs, Ieval). + exception(Class, Reason, dbg_istk:exception_stacktrace(complete, Ieval), + Bs, Ieval). exception(Class, Reason, Stacktrace, Bs, #ieval{module=M, line=Line}) -> - ExitInfo = {{M,Line}, Bs, term_to_binary(get(stack))}, + ExitInfo = {{M,Line}, Bs, dbg_istk:to_external()}, 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 %%==================================================================== @@ -225,7 +208,7 @@ meta(Int, Debugged, M, F, As) -> put(cache, []), put(next_break, Status), % break | running (other values later) put(self, Debugged), % pid() interpreted process - put(stack, []), + dbg_istk:init(), put(stacktrace, []), put(trace_stack, dbg_iserver:call(Int, get_stack_trace)), put(trace, false), % bool() Trace on/off @@ -243,7 +226,7 @@ meta(Int, Debugged, M, F, As) -> debugged_cmd(Cmd, Bs, Ieval) -> Debugged = get(self), - Stacktrace = fix_stacktrace(2), + Stacktrace = dbg_istk:exception_stacktrace(no_current, Ieval), Debugged ! {sys, self(), {command,Cmd,Stacktrace}}, meta_loop(Debugged, Bs, Ieval). @@ -275,7 +258,7 @@ meta_loop(Debugged, Bs, #ieval{level=Le} = Ieval) -> %% Reset process dictionary %% This is really only necessary if the process left %% interpreted code at a call level > 1 - put(stack, []), + dbg_istk:init(), put(stacktrace, []), put(exit_info, undefined), @@ -313,177 +296,6 @@ exit_loop(OrigPid, Reason, 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 � 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, [{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([{Le,_}|_]) -> Le. - -%% fix_stacktrace(Start) -> Stacktrace -%% Start = 1|2 -%% Stacktrace = [{M,F,Args|Arity} | {Fun,Args}] -%% Convert internal stack format to imitation of regular stacktrace. -%% Max three elements, no repeated (recursive) calls to the same -%% function and convert argument lists to arity for all but topmost -%% entry (and funs). -%% 'Start' indicates where at get(stack) to start. This somewhat ugly -%% solution is because fix_stacktrace has two uses: 1) to imitate -%% the stacktrace in the case of an exception in the interpreted code, -%% in which case the current call (top of the stack = first of the list) -%% should be included, and 2) to send a current stacktrace to Debugged -%% when evaluation passes into non-interpreted code, in which case -%% the current call should NOT be included (as it is Debugged which -%% will make the actual function call). -fix_stacktrace(Start) -> - case fix_stacktrace2(sublist(get(stack), Start, 3)) of - [] -> - []; - [H|T] -> - [H|args2arity(T)] - end. - -sublist([], _Start, _Length) -> - []; % workaround, lists:sublist([],2,3) fails -sublist(L, Start, Length) -> - lists:sublist(L, Start, Length). - -fix_stacktrace2([{_,{{M,F,As1},_,_}}, {_,{{M,F,As2},_,_}}|_]) - when length(As1) =:= length(As2) -> - [{M,F,As1}]; -fix_stacktrace2([{_,{{Fun,As1},_,_}}, {_,{{Fun,As2},_,_}}|_]) - when length(As1) =:= length(As2) -> - [{Fun,As1}]; -fix_stacktrace2([{_,{MFA,_,_}}|Entries]) -> - [MFA|fix_stacktrace2(Entries)]; -fix_stacktrace2([]) -> - []. - -args2arity([{M,F,As}|Entries]) when is_list(As) -> - [{M,F,length(As)}|args2arity(Entries)]; -args2arity([Entry|Entries]) -> - [Entry|args2arity(Entries)]; -args2arity([]) -> - []. - -%% bindings(SP) -> Bs -%% SP = Le % stack pointer -%% Return the bindings for the specified call level -bindings(SP) -> - bindings(SP, get(stack)). - -bindings(SP, [{SP,{_MFA,_Wh,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, [{Le, {_MFA,Where,Bs}}|_]) when Le<SP -> - {Le, Where, Bs}; -stack_frame(SP, down, [{Le, {_MFA,Where,Bs}}|_]) when Le>SP -> - {Le, Where, Bs}; -stack_frame(SP, Dir, [{SP, _}|Stack]) -> - case Stack of - [{Le, {_MFA,Where,Bs}}|_] -> - {Le, Where, 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) -> - Stack = case HowMany of - all -> get(stack); - N -> lists:sublist(get(stack), N) - end, - [{Le, MFA} || {Le,{MFA,_Wh,_Bs}} <- Stack]. - %%--Trace function---------------------------------------------------- %%-------------------------------------------------------------------- @@ -591,12 +403,12 @@ eval_function(Mod, Fun, As0, Bs0, _Called, Ieval) when is_function(Fun); #ieval{level=Le, line=Li, last_call=Lc} = Ieval, case lambda(Fun, As0) of {Cs,Module,Name,As,Bs} -> - push({Module,Name,As}, Bs0, Ieval), + dbg_istk:push({Module,Name,As}, Bs0, Ieval), trace(call_fun, {Le,Li,Name,As}), {value, Val, _Bs} = fnk_clauses(Cs, Module, Name, As, Bs, Ieval#ieval{level=Le+1}), - pop(), + dbg_istk:pop(), trace(return, {Le,Val}), {value, Val, Bs0}; @@ -604,12 +416,12 @@ eval_function(Mod, Fun, As0, Bs0, _Called, Ieval) when is_function(Fun); trace(call_fun, {Le,Li,Fun,As0}), {value, {dbg_apply,erlang,apply,[Fun,As0]}, Bs0}; not_interpreted -> - push({Fun,As0}, Bs0, Ieval), + dbg_istk:push({Fun,As0}, Bs0, Ieval), trace(call_fun, {Le,Li,Fun,As0}), {value, Val, _Bs} = debugged_cmd({apply,erlang,apply,[Fun,As0]},Bs0, Ieval#ieval{level=Le+1}), - pop(), + dbg_istk:pop(), trace(return, {Le,Val}), {value, Val, Bs0}; @@ -628,7 +440,7 @@ eval_function(ct_line, line, As, Bs, extern, #ieval{level=Le}=Ieval) -> eval_function(Mod, Name, As0, Bs0, Called, Ieval) -> #ieval{level=Le, line=Li, last_call=Lc} = Ieval, - push({Mod,Name,As0}, Bs0, Ieval), + dbg_istk:push({Mod,Name,As0}, Bs0, Ieval), trace(call, {Called, {Le,Li,Mod,Name,As0}}), case get_function(Mod, Name, As0, Called) of @@ -636,7 +448,7 @@ eval_function(Mod, Name, As0, Bs0, Called, Ieval) -> {value, Val, _Bs} = fnk_clauses(Cs, Mod, Name, As0, erl_eval:new_bindings(), Ieval#ieval{level=Le+1}), - pop(), + dbg_istk:pop(), trace(return, {Le,Val}), {value, Val, Bs0}; @@ -646,7 +458,7 @@ eval_function(Mod, Name, As0, Bs0, Called, Ieval) -> {value, Val, _Bs} = debugged_cmd({apply,Mod,Name,As0}, Bs0, Ieval#ieval{level=Le+1}), - pop(), + dbg_istk:pop(), trace(return, {Le,Val}), {value, Val, Bs0}; @@ -826,7 +638,7 @@ expr({'catch',Line,Expr}, Bs0, Ieval) -> Class:Reason -> %% Exception caught, reset exit info put(exit_info, undefined), - pop(Ieval#ieval.level), + dbg_istk:pop(Ieval#ieval.level), Value = catch_value(Class, Reason), trace(return, {Ieval#ieval.level,Value}), {value, Value, Bs0} @@ -999,10 +811,10 @@ expr({safe_bif,Line,M,F,As0}, Bs0, #ieval{level=Le}=Ieval0) -> Ieval = Ieval0#ieval{line=Line}, {As,Bs} = eval_list(As0, Bs0, Ieval), trace(bif, {Le,Line,M,F,As}), - push({M,F,As}, Bs0, Ieval), + dbg_istk:push({M,F,As}, Bs0, Ieval), {_,Value,_} = Res = safe_bif(M, F, As, Bs, Ieval), trace(return, {Le,Value}), - pop(), + dbg_istk:pop(), Res; %% Call to a BIF that must be evaluated in the correct process @@ -1010,11 +822,11 @@ expr({bif,Line,M,F,As0}, Bs0, #ieval{level=Le}=Ieval0) -> Ieval = Ieval0#ieval{line=Line}, {As,Bs} = eval_list(As0, Bs0, Ieval), trace(bif, {Le,Line,M,F,As}), - push({M,F,As}, Bs0, Ieval), + dbg_istk:push({M,F,As}, Bs0, Ieval), {_,Value,_} = Res = debugged_cmd({apply,M,F,As}, Bs, Ieval#ieval{level=Le+1}), trace(return, {Le,Value}), - pop(), + dbg_istk:pop(), Res; %% Call to an operation @@ -1415,7 +1227,7 @@ catch_clauses(Exception, [{clause,_,[P],G,B}|CatchCs], Bs0, Ieval) -> true -> %% Exception caught, reset exit info put(exit_info, undefined), - pop(Ieval#ieval.level), + dbg_istk:pop(Ieval#ieval.level), seq(B, Bs, Ieval); false -> catch_clauses(Exception, CatchCs, Bs0, Ieval) diff --git a/lib/debugger/src/dbg_istk.erl b/lib/debugger/src/dbg_istk.erl new file mode 100644 index 0000000000..c0cc6a2697 --- /dev/null +++ b/lib/debugger/src/dbg_istk.erl @@ -0,0 +1,224 @@ +%% +%% %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,to_external/0,from_external/1, + push/3,pop/0,pop/1,stack_level/0, + exception_stacktrace/2, + bindings/1,stack_frame/2,backtrace/1, + in_use_p/2]). + +-include("dbg_ieval.hrl"). + +-define(STACK, ?MODULE). + +init() -> + init([]). + +to_external() -> + {stack,term_to_binary(get(?STACK))}. + +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) +%% +%% 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 ¦ 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, [{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([{Le,_}|_]) -> Le. + +%% exception_stacktrace(HowMuch, #ieval{}) -> Stacktrace +%% HowMuch = complete | no_current +%% Stacktrace = [{M,F,Args|Arity} | {Fun,Args}] +%% Convert internal stack format to an imitation of the +%% regular stacktrace. +%% +%% Max three elements, no repeated (recursive) calls to the same +%% function and convert argument lists to arity for all but the topmost +%% entry (and funs). + +exception_stacktrace(complete, #ieval{}) -> + fix_stacktrace(1); +exception_stacktrace(no_current, #ieval{}) -> + fix_stacktrace(2). + +fix_stacktrace(Start) -> + case fix_stacktrace2(sublist(get(?STACK), Start, 3)) of + [] -> + []; + [H|T] -> + [H|args2arity(T)] + end. + +sublist([], _Start, _Length) -> + []; % workaround, lists:sublist([],2,3) fails +sublist(L, Start, Length) -> + lists:sublist(L, Start, Length). + +fix_stacktrace2([{_,{{M,F,As1},_,_}}, {_,{{M,F,As2},_,_}}|_]) + when length(As1) =:= length(As2) -> + [{M,F,As1}]; +fix_stacktrace2([{_,{{Fun,As1},_,_}}, {_,{{Fun,As2},_,_}}|_]) + when length(As1) =:= length(As2) -> + [{Fun,As1}]; +fix_stacktrace2([{_,{MFA,_,_}}|Entries]) -> + [MFA|fix_stacktrace2(Entries)]; +fix_stacktrace2([]) -> + []. + +args2arity([{M,F,As}|Entries]) when is_list(As) -> + [{M,F,length(As)}|args2arity(Entries)]; +args2arity([Entry|Entries]) -> + [Entry|args2arity(Entries)]; +args2arity([]) -> + []. + +%% bindings(SP) -> Bs +%% SP = Le % stack pointer +%% Return the bindings for the specified call level +bindings(SP) -> + bindings(SP, get(?STACK)). + +bindings(SP, [{SP,{_MFA,_Wh,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, [{Le, {_MFA,Where,Bs}}|_]) when Le<SP -> + {Le, Where, Bs}; +stack_frame(SP, down, [{Le, {_MFA,Where,Bs}}|_]) when Le>SP -> + {Le, Where, Bs}; +stack_frame(SP, Dir, [{SP, _}|Stack]) -> + case Stack of + [{Le, {_MFA,Where,Bs}}|_] -> + {Le, Where, 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) -> + Stack = case HowMany of + all -> get(?STACK); + N -> lists:sublist(get(?STACK), N) + end, + [{Le, MFA} || {Le,{MFA,_Wh,_Bs}} <- 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({_,{M,_,_,_}}) when M =:= Mod -> true; + (_) -> false + end, + get(?STACK)) + end. diff --git a/lib/debugger/src/debugger.app.src b/lib/debugger/src/debugger.app.src index 21cf59a2e1..5538f66260 100644 --- a/lib/debugger/src/debugger.app.src +++ b/lib/debugger/src/debugger.app.src @@ -26,6 +26,7 @@ dbg_ieval, dbg_iload, dbg_iserver, + dbg_istk, dbg_ui_break, dbg_ui_break_win, dbg_ui_edit, |