From 059dab74c2930eb5627737c336c428ca30f290c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 31 Mar 2011 08:01:38 +0200 Subject: Make stacktraces in exceptions more similar to BEAM's stacktraces When an exception was generated from interpreted code, the stacktrace would not look exactly like BEAM's stacktraces. There would generally be fewer entries (never more than three), and the top entry would always have MFAs with the actual arguments (rather than the arity). There are two good reasons for making the stacktraces as similar as possible: * Code that examines a stacktrace can behave differently in the interpreted and BEAM code if the stacktraces are different. * It is easier to test the debugger if test suites for other applications (such as the emulator) can be run with the debugger with as few modifications as possible. --- lib/debugger/src/dbg_ieval.erl | 24 ++++++++---- lib/debugger/src/dbg_istk.erl | 83 ++++++++++++++++++++---------------------- 2 files changed, 55 insertions(+), 52 deletions(-) (limited to 'lib/debugger/src') diff --git a/lib/debugger/src/dbg_ieval.erl b/lib/debugger/src/dbg_ieval.erl index fe7f9af1a9..32848a00ff 100644 --- a/lib/debugger/src/dbg_ieval.erl +++ b/lib/debugger/src/dbg_ieval.erl @@ -168,10 +168,18 @@ check_exit_msg(_Msg, _Bs, _Ieval) -> %% and then raise the exception. %%-------------------------------------------------------------------- exception(Class, Reason, Bs, Ieval) -> - exception(Class, Reason, dbg_istk:delayed_stacktrace(Ieval), - Bs, Ieval). - -exception(Class, Reason, Stacktrace, Bs, #ieval{module=M, line=Line}) -> + exception(Class, Reason, Bs, Ieval, false). + +exception(Class, Reason, Bs, Ieval, false) -> + do_exception(Class, Reason, + dbg_istk:delayed_stacktrace(no_args, Ieval), + Bs, Ieval); +exception(Class, Reason, Bs, Ieval, true) -> + do_exception(Class, Reason, + dbg_istk:delayed_stacktrace(include_args, Ieval), + Bs, Ieval). + +do_exception(Class, Reason, Stacktrace, Bs, #ieval{module=M, line=Line}) -> ExitInfo = {{M,Line}, Bs, dbg_istk:to_external()}, put(exit_info, ExitInfo), put(stacktrace, Stacktrace), @@ -249,7 +257,7 @@ meta_loop(Debugged, Bs, #ieval{level=Le} = Ieval) -> Depth = max(0, Depth0 - length(Stk)), Stk ++ MakeStk0(Depth) end, - exception(Class, Reason, MakeStk, Bs, Ieval); + do_exception(Class, Reason, MakeStk, Bs, Ieval); %% Error must have occured within a re-entry to %% interpreted code, simply raise the exception @@ -463,7 +471,7 @@ do_eval_function(Mod, Name, As0, Bs0, Called, Ieval0) -> {value, Val, Bs0}; undef -> - exception(error, undef, Bs0, Ieval) + exception(error, undef, Bs0, Ieval, true) end. lambda(eval_fun, [Cs,As,Bs,{Mod,Name}=F]) -> @@ -580,7 +588,7 @@ fnk_clauses([{clause,Line,Pars,Gs,Body}|Cs], M, F, As, Bs0, Ieval) -> fnk_clauses(Cs, M, F, As, Bs0, Ieval) end; fnk_clauses([], _M, _F, _As, Bs, Ieval) -> - exception(error, function_clause, Bs, Ieval). + exception(error, function_clause, Bs, Ieval, true). seq([E], Bs0, Ieval) -> case dbg_icmd:cmd(E, Bs0, Ieval) of @@ -1013,7 +1021,7 @@ safe_bif(M, F, As, Bs, Ieval) -> {value,Value,Bs} catch Class:Reason -> - exception(Class, Reason, Bs, Ieval) + exception(Class, Reason, Bs, Ieval, true) end. eval_send(To, Msg, Bs, Ieval) -> diff --git a/lib/debugger/src/dbg_istk.erl b/lib/debugger/src/dbg_istk.erl index 2c4c2a3518..34070aa8f2 100644 --- a/lib/debugger/src/dbg_istk.erl +++ b/lib/debugger/src/dbg_istk.erl @@ -19,7 +19,7 @@ -module(dbg_istk). -export([init/0,to_external/0,from_external/1, push/2,pop/0,pop/1,stack_level/0, - delayed_stacktrace/0,delayed_stacktrace/1, + delayed_stacktrace/0,delayed_stacktrace/2, bindings/1,stack_frame/2,backtrace/2, in_use_p/2]). @@ -107,60 +107,55 @@ stack_level([]) -> 1; stack_level([#e{level=Le}|_]) -> Le. %% delayed_stacktrace() -> CreateStacktraceFun -%% delayed_stacktrace(#ieval{}) -> 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. -%% -%% 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). delayed_stacktrace() -> - Stack = get(?STACK), - do_delayed_stacktrace(Stack). + Stack0 = get(?STACK), + fun(NumEntries) -> + Stack = stacktrace(NumEntries, Stack0, []), + [ArityOnly || {ArityOnly,_} <- Stack] + end. -delayed_stacktrace(Ieval) -> +delayed_stacktrace(include_args, Ieval) -> #ieval{module=Mod,function=Name,arguments=As} = Ieval, - Stack = [#e{mfa={Mod,Name,As}}|get(?STACK)], - do_delayed_stacktrace(Stack). - -do_delayed_stacktrace(Stack) -> - fun(_NumEntries) -> - fix_stacktrace(Stack) + Stack0 = [#e{mfa={Mod,Name,As}}|get(?STACK)], + fun(NumEntries) -> + case stacktrace(NumEntries, Stack0, []) of + [] -> + []; + [{_,WithArgs}|Stack] -> + [WithArgs | [ArityOnly || {ArityOnly,_} <- Stack]] + end + end; +delayed_stacktrace(no_args, Ieval) -> + #ieval{module=Mod,function=Name,arguments=As} = Ieval, + Stack0 = [#e{mfa={Mod,Name,As}}|get(?STACK)], + fun(NumEntries) -> + Stack = stacktrace(NumEntries, Stack0, []), + [ArityOnly || {ArityOnly,_} <- Stack] end. -fix_stacktrace(Stack) -> - case fix_stacktrace2(sublist(Stack, 1, 3)) of - [] -> - []; - [H|T] -> - [H|args2arity(T)] - end. +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). -sublist([], _Start, _Length) -> - []; % workaround, lists:sublist([],2,3) fails -sublist(L, Start, Length) -> - lists:sublist(L, Start, Length). - -fix_stacktrace2([#e{mfa={M,F,As1}}, #e{mfa={M,F,As2}}|_]) - when length(As1) =:= length(As2) -> - [{M,F,As1}]; -fix_stacktrace2([#e{mfa={Fun,As1}}, #e{mfa={Fun,As2}}|_]) - when length(As1) =:= length(As2) -> - [{Fun,As1}]; -fix_stacktrace2([#e{mfa=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([]) -> - []. +normalize(#e{mfa={_,Fun,As}}) when is_function(Fun) -> + {{Fun,length(As)},{Fun,As}}; +normalize(#e{mfa={M,F,As}}) -> + {{M,F,length(As)},{M,F,As}}. %% bindings(SP) -> Bs %% SP = Le % stack pointer -- cgit v1.2.3