From aac2fac655dca8164b9ab4b1d4d1d45ffc7f9530 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 15 Mar 2011 14:45:39 +0100 Subject: Remove support for erlang:fault/{1,2} Since erlang:fault/{1,2} is no longer supported in the run-time system (it was removed several releases ago), there is no need to still support it in the debugger. --- lib/debugger/src/dbg_ieval.erl | 5 ----- lib/debugger/src/dbg_iload.erl | 2 -- 2 files changed, 7 deletions(-) (limited to 'lib/debugger/src') diff --git a/lib/debugger/src/dbg_ieval.erl b/lib/debugger/src/dbg_ieval.erl index 306323f8ea..d63e315614 100644 --- a/lib/debugger/src/dbg_ieval.erl +++ b/lib/debugger/src/dbg_ieval.erl @@ -988,11 +988,6 @@ expr({dbg,Line,error,As0}, Bs0, #ieval{level=Le}=Ieval0) -> {[Term],Bs} = eval_list(As0, Bs0, Ieval), trace(bif, {Le,Line,erlang,error,[Term]}), exception(error, Term, Bs, Ieval); -expr({dbg,Line,fault,As0}, Bs0, #ieval{level=Le}=Ieval0) -> - Ieval = Ieval0#ieval{line=Line}, - {[Term],Bs} = eval_list(As0, Bs0, Ieval), - trace(bif, {Le,Line,erlang,fault,[Term]}), - exception(fault, Term, Bs, Ieval); expr({dbg,Line,exit,As0}, Bs0, #ieval{level=Le}=Ieval0) -> Ieval = Ieval0#ieval{line=Line}, {[Term],Bs} = eval_list(As0, Bs0, Ieval), diff --git a/lib/debugger/src/dbg_iload.erl b/lib/debugger/src/dbg_iload.erl index db5a17ad2e..70d43c20b2 100644 --- a/lib/debugger/src/dbg_iload.erl +++ b/lib/debugger/src/dbg_iload.erl @@ -404,8 +404,6 @@ expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,throw}},[_]=As}) -> {dbg,Line,throw,expr_list(As)}; expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,error}},[_]=As}) -> {dbg,Line,error,expr_list(As)}; -expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,fault}},[_]=As}) -> - {dbg,Line,fault,expr_list(As)}; expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,exit}},[_]=As}) -> {dbg,Line,exit,expr_list(As)}; expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,apply}},[_,_,_]=As0}) -> -- cgit v1.2.3 From d33e59b163744d209eca211050f323c6acd823d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Fri, 18 Mar 2011 06:22:23 +0100 Subject: Handle all guard BIFs as safe in function bodies Make sure that all guards BIFs are handled as safe BIFs in function bodies. BIFs in guards are already handled as safe. (self/0 is not safe, but it is already specially handled.) --- lib/debugger/src/dbg_iload.erl | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) (limited to 'lib/debugger/src') diff --git a/lib/debugger/src/dbg_iload.erl b/lib/debugger/src/dbg_iload.erl index 70d43c20b2..d197cbe247 100644 --- a/lib/debugger/src/dbg_iload.erl +++ b/lib/debugger/src/dbg_iload.erl @@ -415,7 +415,7 @@ expr({call,Line,{remote,_,{atom,_,Mod},{atom,_,Func}},As0}) -> false -> {call_remote,Line,Mod,Func,As}; true -> - case bif_type(Mod, Func) of + case bif_type(Mod, Func, length(As0)) of safe -> {safe_bif,Line,Mod,Func,As}; spawn -> {spawn_bif,Line,Mod,Func,As}; unsafe ->{bif,Line,Mod,Func,As} @@ -583,24 +583,22 @@ new_vars(N, L, Vs) when N > 0 -> new_vars(N-1, L, [V|Vs]); new_vars(0, _, Vs) -> Vs. -bif_type(erlang, Name) -> bif_type(Name); -bif_type(_, _) -> unsafe. +bif_type(erlang, Name, Arity) -> + case erl_internal:guard_bif(Name, Arity) of + true -> + %% Guard BIFs are safe (except for self/0, but it is + %% handled with a special instruction anyway). + safe; + false -> + bif_type(Name) + end; +bif_type(_, _, _) -> unsafe. bif_type(register) -> safe; bif_type(unregister) -> safe; bif_type(whereis) -> safe; bif_type(registered) -> safe; -bif_type(abs) -> safe; -bif_type(float) -> safe; -bif_type(trunc) -> safe; -bif_type(round) -> safe; bif_type(math) -> safe; -bif_type(node) -> safe; -bif_type(length) -> safe; -bif_type(hd) -> safe; -bif_type(tl) -> safe; -bif_type(size) -> safe; -bif_type(element) -> safe; bif_type(setelement) -> safe; bif_type(atom_to_list) -> safe; bif_type(list_to_atom) -> safe; -- cgit v1.2.3 From 0d32172b03e2c1e9d5cbca2f350888a4803564c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Fri, 18 Mar 2011 07:12:07 +0100 Subject: Remove BIFs that no longer exist from dbg_iload:bif_type/1 --- lib/debugger/src/dbg_iload.erl | 5 ----- 1 file changed, 5 deletions(-) (limited to 'lib/debugger/src') diff --git a/lib/debugger/src/dbg_iload.erl b/lib/debugger/src/dbg_iload.erl index d197cbe247..b259c2299f 100644 --- a/lib/debugger/src/dbg_iload.erl +++ b/lib/debugger/src/dbg_iload.erl @@ -598,7 +598,6 @@ bif_type(register) -> safe; bif_type(unregister) -> safe; bif_type(whereis) -> safe; bif_type(registered) -> safe; -bif_type(math) -> safe; bif_type(setelement) -> safe; bif_type(atom_to_list) -> safe; bif_type(list_to_atom) -> safe; @@ -623,18 +622,14 @@ bif_type(list_to_pid) -> safe; bif_type(module_loaded) -> safe; bif_type(binary_to_term) -> safe; bif_type(term_to_binary) -> safe; -bif_type(alive) -> safe; -bif_type(notalive) -> safe; bif_type(nodes) -> safe; bif_type(is_alive) -> safe; bif_type(disconnect_node) -> safe; bif_type(binary_to_list) -> safe; bif_type(list_to_binary) -> safe; bif_type(split_binary) -> safe; -bif_type(term_to_atom) -> safe; bif_type(hash) -> safe; bif_type(pre_loaded) -> safe; -bif_type(info) -> safe; bif_type(set_cookie) -> safe; bif_type(get_cookie) -> safe; bif_type(spawn) -> spawn; -- cgit v1.2.3 From c3d796e5080a44da039bdfb42fbfcfc1e757397e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Fri, 18 Mar 2011 07:17:08 +0100 Subject: Remove the special handling of spawn BIFs BIFs that spawn new processes once upon a time needed/benefited from special handling, but now they are handled in exactly the same way as an unsafe BIF (except for a bug in the handling of the return value trace). Therefore, treat spawn BIFs as unsafe BIFs. --- lib/debugger/src/dbg_ieval.erl | 11 ----------- lib/debugger/src/dbg_iload.erl | 4 ---- 2 files changed, 15 deletions(-) (limited to 'lib/debugger/src') diff --git a/lib/debugger/src/dbg_ieval.erl b/lib/debugger/src/dbg_ieval.erl index d63e315614..9739e9569d 100644 --- a/lib/debugger/src/dbg_ieval.erl +++ b/lib/debugger/src/dbg_ieval.erl @@ -1017,17 +1017,6 @@ expr({bif,Line,M,F,As0}, Bs0, #ieval{level=Le}=Ieval0) -> pop(), Res; -%% Call to a BIF that spawns a new process -expr({spawn_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), - Res = debugged_cmd({apply,M,F,As}, Bs,Ieval#ieval{level=Le+1}), - trace(return, {Le,Res}), - pop(), - Res; - %% Call to an operation expr({op,Line,Op,As0}, Bs0, Ieval0) -> Ieval = Ieval0#ieval{line=Line}, diff --git a/lib/debugger/src/dbg_iload.erl b/lib/debugger/src/dbg_iload.erl index b259c2299f..459c6ee960 100644 --- a/lib/debugger/src/dbg_iload.erl +++ b/lib/debugger/src/dbg_iload.erl @@ -417,7 +417,6 @@ expr({call,Line,{remote,_,{atom,_,Mod},{atom,_,Func}},As0}) -> true -> case bif_type(Mod, Func, length(As0)) of safe -> {safe_bif,Line,Mod,Func,As}; - spawn -> {spawn_bif,Line,Mod,Func,As}; unsafe ->{bif,Line,Mod,Func,As} end end; @@ -632,7 +631,4 @@ bif_type(hash) -> safe; bif_type(pre_loaded) -> safe; bif_type(set_cookie) -> safe; bif_type(get_cookie) -> safe; -bif_type(spawn) -> spawn; -bif_type(spawn_link) -> spawn; -bif_type(spawn_opt) -> spawn; bif_type(_) -> unsafe. -- cgit v1.2.3 From 5cdd2f9d7c1d1846ee09caae3b8dfdb83ef76405 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Fri, 18 Mar 2011 08:02:34 +0100 Subject: Remove the special handling of Mod:module_info/{0,1} Many releases ago, Mod:module_info/{0,1} used to be specially handled in the BEAM loader. The debugger has similar special handling. In the current implementation, Mod:module_info/{0,1} are ordinary functions that call special BIFs to do their work. Therefore, remove the special handling of Mod:module_info/{0,1} in the debugger. --- lib/debugger/src/dbg_ieval.erl | 22 ---------------------- lib/debugger/src/dbg_iload.erl | 30 ++++++++++-------------------- lib/debugger/src/dbg_iserver.erl | 3 --- 3 files changed, 10 insertions(+), 45 deletions(-) (limited to 'lib/debugger/src') diff --git a/lib/debugger/src/dbg_ieval.erl b/lib/debugger/src/dbg_ieval.erl index 9739e9569d..956dc50df0 100644 --- a/lib/debugger/src/dbg_ieval.erl +++ b/lib/debugger/src/dbg_ieval.erl @@ -1056,17 +1056,6 @@ expr({apply,Line,As0}, Bs0, Ieval0) -> {[M,F,As],Bs} = eval_list(As0, Bs0, Ieval), eval_function(M, F, As, Bs, extern, Ieval); -%% Mod:module_info/0,1 -expr({module_info_0,_,Mod}, Bs, _Ieval) -> - {value,[{compile,module_info(Mod,compile)}, - {attributes,module_info(Mod,attributes)}, - {imports,module_info(Mod,imports)}, - {exports,module_info(Mod,exports)}],Bs}; -expr({module_info_1,Line,Mod,[As0]}, Bs0, Ieval0) -> - Ieval = Ieval0#ieval{line=Line}, - {value,What,Bs} = expr(As0, Bs0, Ieval), - {value,module_info(Mod, What),Bs}; - %% Receive statement expr({'receive',Line,Cs}, Bs0, #ieval{level=Le}=Ieval) -> trace(receivex, {Le,false}), @@ -1206,17 +1195,6 @@ eval_b_generate(<<_/bitstring>>=Bin, P, Bs0, CompFun, Ieval) -> eval_b_generate(Term, _P, Bs, _CompFun, Ieval) -> exception(error, {bad_generator,Term}, Bs, Ieval). -module_info(Mod, module) -> Mod; -module_info(_Mod, compile) -> []; -module_info(Mod, attributes) -> - {ok, Attr} = dbg_iserver:call(get(int), {lookup, Mod, attributes}), - Attr; -module_info(_Mod, imports) -> []; -module_info(Mod, exports) -> - {ok, Exp} = dbg_iserver:call(get(int), {lookup, Mod, exports}), - Exp; -module_info(_Mod, functions) -> []. - safe_bif(M, F, As, Bs, Ieval) -> try apply(M, F, As) of Value -> diff --git a/lib/debugger/src/dbg_iload.erl b/lib/debugger/src/dbg_iload.erl index 459c6ee960..ab7f40eeea 100644 --- a/lib/debugger/src/dbg_iload.erl +++ b/lib/debugger/src/dbg_iload.erl @@ -70,14 +70,13 @@ store_module(Mod, File, Binary, Db) -> Forms0 end, dbg_idb:insert(Db, mod_file, File), - dbg_idb:insert(Db, exports, Exp), dbg_idb:insert(Db, defs, []), put(vcount, 0), put(fun_count, 0), put(funs, []), put(mod_md5, MD5), - Attr = store_forms(Forms, Mod, Db, Exp, []), + store_forms(Forms, Mod, Db, Exp), erase(mod_md5), erase(current_function), %% store_funs(Db, Mod), @@ -85,11 +84,10 @@ store_module(Mod, File, Binary, Db) -> erase(funs), erase(fun_count), - dbg_idb:insert(Db, attributes, Attr), NewBinary = store_mod_line_no(Mod, Db, binary_to_list(Src)), dbg_idb:insert(Db, mod_bin, NewBinary), - dbg_idb:insert(Db, mod_raw, <>), %% Add eos - dbg_idb:insert(Db, module, Mod). + dbg_idb:insert(Db, mod_raw, <>). %% Add eos + %% Adjust line numbers using the file/2 attribute. %% Also take the absolute value of line numbers. %% This simple fix will make the marker point at the correct line @@ -111,27 +109,19 @@ abstr(Term) -> Term. % store_funs_1(Fs, Db, Mod); % store_funs_1([], _, _) -> ok. -store_forms([{function,_,module_info,0,_}|Fs], Mod, Db, Exp, Attr) -> - Cs = [{clause,0,[],[], [{module_info_0,0,Mod}]}], - dbg_idb:insert(Db, {Mod,module_info,0,true}, Cs), - store_forms(Fs, Mod, Db, Exp, Attr); -store_forms([{function,_,module_info,1,_}|Fs], Mod, Db, Exp, Attr) -> - Cs = [{clause,0,[{var,0,'What'}],[], [{module_info_1,0,Mod,[{var,0,'What'}]}]}], - dbg_idb:insert(Db, {Mod,module_info,1,true}, Cs), - store_forms(Fs, Mod, Db, Exp, Attr); -store_forms([{function,_,Name,Arity,Cs0}|Fs], Mod, Db, Exp, Attr) -> +store_forms([{function,_,Name,Arity,Cs0}|Fs], Mod, Db, Exp) -> FA = {Name,Arity}, put(current_function, FA), Cs = clauses(Cs0), Exported = lists:member(FA, Exp), dbg_idb:insert(Db, {Mod,Name,Arity,Exported}, Cs), - store_forms(Fs, Mod, Db, Exp, Attr); -store_forms([{attribute,_,Name,Val}|Fs], Mod, Db, Exp, Attr) -> - store_forms(Fs, Mod, Db, Exp, [{Name,Val}|Attr]); -store_forms([F|_], _Mod, _Db, _Exp, _Attr) -> + store_forms(Fs, Mod, Db, Exp); +store_forms([{attribute,_,_Name,_Val}|Fs], Mod, Db, Exp) -> + store_forms(Fs, Mod, Db, Exp); +store_forms([F|_], _Mod, _Db, _Exp) -> exit({unknown_form,F}); -store_forms([], _, _, _, Attr) -> - lists:reverse(Attr). +store_forms([], _, _, _) -> + ok. store_mod_line_no(Mod, Db, Contents) -> store_mod_line_no(Mod, Db, Contents, 1, 0, []). diff --git a/lib/debugger/src/dbg_iserver.erl b/lib/debugger/src/dbg_iserver.erl index 212bc2b8ab..55ac1cef94 100644 --- a/lib/debugger/src/dbg_iserver.erl +++ b/lib/debugger/src/dbg_iserver.erl @@ -97,13 +97,10 @@ ensure_started() -> %% %% Key Value %% --- ----- -%% attributes Attr -%% exports Exp %% defs [] %% mod_bin Binary %% mod_raw Raw Binary %% mod_file File -%% module Mod %% {Mod,Name,Arity,Exported} Cs %% {'fun',Mod,Index,Uniq} {Name,Arity,Cs} %% Line {Pos,PosNL} -- cgit v1.2.3 From ae7858a2631df405f4460f6aefb1e08077d61a3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 23 Mar 2011 16:06:40 +0100 Subject: dbg_iload: Remove unnecessary handling of old guard tests sys_pre_expand has already rewritten old guard tests to new guard tests. --- lib/debugger/src/dbg_iload.erl | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) (limited to 'lib/debugger/src') diff --git a/lib/debugger/src/dbg_iload.erl b/lib/debugger/src/dbg_iload.erl index ab7f40eeea..c94dce1b5e 100644 --- a/lib/debugger/src/dbg_iload.erl +++ b/lib/debugger/src/dbg_iload.erl @@ -234,12 +234,7 @@ and_guard([]) -> []. guard_test({call,Line,{remote,_,{atom,_,erlang},{atom,_,F}},As0}) -> As = gexpr_list(As0), - case map_guard_bif(F, length(As0)) of - {ok,Name} -> - {safe_bif,Line,erlang,Name,As}; - error -> - {safe_bif,Line,erlang,F,As} - end; + {safe_bif,Line,erlang,F,As}; guard_test({op,Line,Op,L0}) -> true = erl_internal:arith_op(Op, 1) orelse %Assertion. erl_internal:bool_op(Op, 1), @@ -263,19 +258,6 @@ guard_test({atom,_,_}=A) -> A; guard_test({nil,_}=N) -> N; guard_test({var,_,_}=V) ->V. % Boolean var -map_guard_bif(integer, 1) -> {ok,is_integer}; -map_guard_bif(float, 1) -> {ok,is_float}; -map_guard_bif(number, 1) -> {ok,is_number}; -map_guard_bif(atom, 1) -> {ok,is_atom}; -map_guard_bif(list, 1) -> {ok,is_list}; -map_guard_bif(tuple, 1) -> {ok,is_tuple}; -map_guard_bif(pid, 1) -> {ok,is_pid}; -map_guard_bif(reference, 1) -> {ok,is_reference}; -map_guard_bif(port, 1) -> {ok,is_port}; -map_guard_bif(binary, 1) -> {ok,is_binary}; -map_guard_bif(function, 1) -> {ok,is_function}; -map_guard_bif(_, _) -> error. - gexpr({var,Line,V}) -> {var,Line,V}; gexpr({integer,Line,I}) -> {value,Line,I}; gexpr({char,Line,I}) -> {value,Line,I}; -- cgit v1.2.3 From 15fc610fe64c052faf1ba997b771123fe4c383d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 15 Mar 2011 15:24:08 +0100 Subject: Break out stack handling into the dbg_istk module --- lib/debugger/src/Makefile | 1 + lib/debugger/src/dbg_icmd.erl | 10 +- lib/debugger/src/dbg_ieval.erl | 238 ++++---------------------------------- lib/debugger/src/dbg_istk.erl | 224 +++++++++++++++++++++++++++++++++++ lib/debugger/src/debugger.app.src | 1 + 5 files changed, 256 insertions(+), 218 deletions(-) create mode 100644 lib/debugger/src/dbg_istk.erl (limited to 'lib/debugger/src') 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= - 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 - {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= + 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 + {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, -- cgit v1.2.3 From 2cbf01d49adda893018a9fbeb76c5e12b57bc3c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 16 Mar 2011 16:22:33 +0100 Subject: dbg_istk: Use a record for stack entries instead of a tuple --- lib/debugger/src/dbg_istk.erl | 51 +++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 26 deletions(-) (limited to 'lib/debugger/src') diff --git a/lib/debugger/src/dbg_istk.erl b/lib/debugger/src/dbg_istk.erl index c0cc6a2697..8331f46eb8 100644 --- a/lib/debugger/src/dbg_istk.erl +++ b/lib/debugger/src/dbg_istk.erl @@ -27,6 +27,14 @@ -define(STACK, ?MODULE). +-record(e, + {level, %Level + mfa, %{Mod,Func,Args|Arity}|{Fun,Args} + cm, %Module called from + line, %Line called from + bindings + }). + init() -> init([]). @@ -46,14 +54,6 @@ init(Stack) -> %% 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 @@ -62,7 +62,7 @@ init(Stack) -> %% 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}}, + Entry = #e{level=Le,mfa=MFA,cm=Cm,line=Li,bindings=Bs}, case get(trace_stack) of false -> ignore; no_tail when Lc -> @@ -93,7 +93,7 @@ pop(Le) -> put(?STACK, pop(Le, get(?STACK))) end. -pop(Level, [{Le, _}|Stack]) when Level= +pop(Level, [#e{level=Le}|Stack]) when Level =< Le -> pop(Level, Stack); pop(_Level, Stack) -> Stack. @@ -105,7 +105,7 @@ stack_level() -> stack_level(get(?STACK)). stack_level([]) -> 1; -stack_level([{Le,_}|_]) -> Le. +stack_level([#e{level=Le}|_]) -> Le. %% exception_stacktrace(HowMuch, #ieval{}) -> Stacktrace %% HowMuch = complete | no_current @@ -135,13 +135,13 @@ sublist([], _Start, _Length) -> sublist(L, Start, Length) -> lists:sublist(L, Start, Length). -fix_stacktrace2([{_,{{M,F,As1},_,_}}, {_,{{M,F,As2},_,_}}|_]) +fix_stacktrace2([#e{mfa={M,F,As1}}, #e{mfa={M,F,As2}}|_]) when length(As1) =:= length(As2) -> [{M,F,As1}]; -fix_stacktrace2([{_,{{Fun,As1},_,_}}, {_,{{Fun,As2},_,_}}|_]) +fix_stacktrace2([#e{mfa={Fun,As1}}, #e{mfa={Fun,As2}}|_]) when length(As1) =:= length(As2) -> [{Fun,As1}]; -fix_stacktrace2([{_,{MFA,_,_}}|Entries]) -> +fix_stacktrace2([#e{mfa=MFA}|Entries]) -> [MFA|fix_stacktrace2(Entries)]; fix_stacktrace2([]) -> []. @@ -159,7 +159,7 @@ args2arity([]) -> bindings(SP) -> bindings(SP, get(?STACK)). -bindings(SP, [{SP,{_MFA,_Wh,Bs}}|_]) -> +bindings(SP, [#e{level=SP,bindings=Bs}|_]) -> Bs; bindings(SP, [_Entry|Entries]) -> bindings(SP, Entries); @@ -180,14 +180,14 @@ stack_frame(up, SP) -> stack_frame(down, SP) -> stack_frame(SP, down, lists:reverse(get(?STACK))). -stack_frame(SP, up, [{Le, {_MFA,Where,Bs}}|_]) when Le - {Le, Where, Bs}; -stack_frame(SP, down, [{Le, {_MFA,Where,Bs}}|_]) when Le>SP -> - {Le, Where, Bs}; -stack_frame(SP, Dir, [{SP, _}|Stack]) -> +stack_frame(SP, up, [#e{level=Le,cm=Cm,line=Li,bindings=Bs}|_]) when Le < SP -> + {Le,{Cm,Li},Bs}; +stack_frame(SP, down, [#e{level=Le,cm=Cm,line=Li,bindings=Bs}|_]) when Le > SP -> + {Le,{Cm,Li},Bs}; +stack_frame(SP, Dir, [#e{level=SP}|Stack]) -> case Stack of - [{Le, {_MFA,Where,Bs}}|_] -> - {Le, Where, Bs}; + [#e{level=Le,cm=Cm,line=Li,bindings=Bs}|_] -> + {Le,{Cm,Li},Bs}; [] when Dir =:= up -> top; [] when Dir =:= down -> @@ -205,7 +205,7 @@ backtrace(HowMany) -> all -> get(?STACK); N -> lists:sublist(get(?STACK), N) end, - [{Le, MFA} || {Le,{MFA,_Wh,_Bs}} <- Stack]. + [{Le,MFA} || #e{level=Le,mfa=MFA} <- Stack]. %%-------------------------------------------------------------------- %% in_use_p(Mod, Cm) -> boolean() @@ -217,8 +217,7 @@ in_use_p(Mod, _Cm) -> case get(trace_stack) of false -> true; _ -> % all | no_tail - lists:any(fun({_,{M,_,_,_}}) when M =:= Mod -> true; + lists:any(fun(#e{mfa={M,_,_}}) when M =:= Mod -> true; (_) -> false - end, - get(?STACK)) + end, get(?STACK)) end. -- cgit v1.2.3 From 704d3240cd05ecc1218547a493fab80f99c8e2fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 22 Mar 2011 08:53:57 +0100 Subject: ieval record: Rename the misnamed 'last_call' field to 'top' The 'last_call' is badly named. What is means is that the next call will leave intepreted code. --- lib/debugger/src/dbg_icmd.erl | 4 +-- lib/debugger/src/dbg_ieval.erl | 56 +++++++++++++++++++++--------------------- lib/debugger/src/dbg_ieval.hrl | 8 +++--- lib/debugger/src/dbg_istk.erl | 2 +- 4 files changed, 36 insertions(+), 34 deletions(-) (limited to 'lib/debugger/src') diff --git a/lib/debugger/src/dbg_icmd.erl b/lib/debugger/src/dbg_icmd.erl index de60f4ad47..c158aad1cf 100644 --- a/lib/debugger/src/dbg_icmd.erl +++ b/lib/debugger/src/dbg_icmd.erl @@ -422,7 +422,7 @@ eval_nonrestricted({From, _Mod, Cmd, _SP}, Bs, eval_nonrestricted_1({match,_,{var,_,Var},Expr}, Bs, Ieval) -> {value,Res,Bs2} = - dbg_ieval:eval_expr(Expr, Bs, Ieval#ieval{last_call=false}), + dbg_ieval:eval_expr(Expr, Bs, Ieval#ieval{top=false}), Bs3 = case lists:keyfind(Var, 1, Bs) of {Var,_Value} -> lists:keyreplace(Var, 1, Bs2, {Var,Res}); @@ -437,7 +437,7 @@ eval_nonrestricted_1({var,_,Var}, Bs, _Ieval) -> {Res,Bs}; eval_nonrestricted_1(Expr, Bs, Ieval) -> {value,Res,Bs2} = - dbg_ieval:eval_expr(Expr, Bs, Ieval#ieval{last_call=false}), + dbg_ieval:eval_expr(Expr, Bs, Ieval#ieval{top=false}), {Res,Bs2}. mark_running(LineNo, Le) -> diff --git a/lib/debugger/src/dbg_ieval.erl b/lib/debugger/src/dbg_ieval.erl index c0ef9c6e7c..cfa8e60d63 100644 --- a/lib/debugger/src/dbg_ieval.erl +++ b/lib/debugger/src/dbg_ieval.erl @@ -385,7 +385,7 @@ catch_value(throw, Reason) -> eval_mfa(Debugged, M, F, As, Ieval) -> Int = get(int), Bs = erl_eval:new_bindings(), - try eval_function(M,F,As,Bs,extern,Ieval#ieval{last_call=true}) of + try eval_function(M,F,As,Bs,extern,Ieval#ieval{top=true}) of {value, Val, _Bs} -> {ready, Val} catch @@ -400,7 +400,7 @@ eval_mfa(Debugged, M, F, As, Ieval) -> eval_function(Mod, Fun, As0, Bs0, _Called, Ieval) when is_function(Fun); Mod =:= ?MODULE, Fun =:= eval_fun -> - #ieval{level=Le, line=Li, last_call=Lc} = Ieval, + #ieval{level=Le, line=Li, top=Top} = Ieval, case lambda(Fun, As0) of {Cs,Module,Name,As,Bs} -> dbg_istk:push({Module,Name,As}, Bs0, Ieval), @@ -412,7 +412,7 @@ eval_function(Mod, Fun, As0, Bs0, _Called, Ieval) when is_function(Fun); trace(return, {Le,Val}), {value, Val, Bs0}; - not_interpreted when Lc -> % We are leaving interpreted code + not_interpreted when Top -> % We are leaving interpreted code trace(call_fun, {Le,Li,Fun,As0}), {value, {dbg_apply,erlang,apply,[Fun,As0]}, Bs0}; not_interpreted -> @@ -438,7 +438,7 @@ eval_function(ct_line, line, As, Bs, extern, #ieval{level=Le}=Ieval) -> {value, ignore, Bs}; eval_function(Mod, Name, As0, Bs0, Called, Ieval) -> - #ieval{level=Le, line=Li, last_call=Lc} = Ieval, + #ieval{level=Le, line=Li, top=Top} = Ieval, dbg_istk:push({Mod,Name,As0}, Bs0, Ieval), trace(call, {Called, {Le,Li,Mod,Name,As0}}), @@ -452,7 +452,7 @@ eval_function(Mod, Name, As0, Bs0, Called, Ieval) -> trace(return, {Le,Val}), {value, Val, Bs0}; - not_interpreted when Lc -> % We are leaving interpreted code + not_interpreted when Top -> % We are leaving interpreted code {value, {dbg_apply,Mod,Name,As0}, Bs0}; not_interpreted -> {value, Val, _Bs} = @@ -594,7 +594,7 @@ seq([E|Es], Bs0, Ieval) -> {skip,Bs} -> seq(Es, Bs, Ieval); Bs1 -> - {value,_,Bs} = expr(E, Bs1, Ieval#ieval{last_call=false}), + {value,_,Bs} = expr(E, Bs1, Ieval#ieval{top=false}), seq(Es, Bs, Ieval) end; seq([], Bs, _) -> @@ -617,7 +617,7 @@ expr({value,Val}, Bs, _Ieval) -> % Special case straight values %% List expr({cons,Line,H0,T0}, Bs0, Ieval0) -> Ieval = Ieval0#ieval{line=Line}, - Ieval1 = Ieval#ieval{last_call=false}, + Ieval1 = Ieval#ieval{top=false}, {value,H,Bs1} = expr(H0,Bs0,Ieval1), {value,T,Bs2} = expr(T0,Bs0,Ieval1), {value,[H|T],merge_bindings(Bs2, Bs1, Ieval)}; @@ -633,7 +633,7 @@ expr({block,Line,Es},Bs,Ieval) -> %% Catch statement expr({'catch',Line,Expr}, Bs0, Ieval) -> - try expr(Expr, Bs0, Ieval#ieval{line=Line, last_call=false}) + try expr(Expr, Bs0, Ieval#ieval{line=Line, top=false}) catch Class:Reason -> %% Exception caught, reset exit info @@ -647,7 +647,7 @@ expr({'catch',Line,Expr}, Bs0, Ieval) -> %% Try-catch statement expr({'try',Line,Es,CaseCs,CatchCs,[]}, Bs0, Ieval0) -> Ieval = Ieval0#ieval{line=Line}, - try seq(Es, Bs0, Ieval#ieval{last_call=false}) of + try seq(Es, Bs0, Ieval#ieval{top=false}) of {value,Val,Bs} = Value -> case CaseCs of [] -> Value; @@ -660,7 +660,7 @@ expr({'try',Line,Es,CaseCs,CatchCs,[]}, Bs0, Ieval0) -> end; expr({'try',Line,Es,CaseCs,CatchCs,As}, Bs0, Ieval0) -> Ieval = Ieval0#ieval{line=Line}, - try seq(Es, Bs0, Ieval#ieval{last_call=false}) of + try seq(Es, Bs0, Ieval#ieval{top=false}) of {value,Val,Bs} = Value -> case CaseCs of [] -> Value; @@ -671,13 +671,13 @@ expr({'try',Line,Es,CaseCs,CatchCs,As}, Bs0, Ieval0) -> Class:Reason when CatchCs =/= [] -> catch_clauses({Class,Reason,[]}, CatchCs, Bs0, Ieval) after - seq(As, Bs0, Ieval#ieval{last_call=false}) + seq(As, Bs0, Ieval#ieval{top=false}) end; %% Case statement expr({'case',Line,E,Cs}, Bs0, Ieval) -> {value,Val,Bs} = - expr(E, Bs0, Ieval#ieval{line=Line, last_call=false}), + expr(E, Bs0, Ieval#ieval{line=Line, top=false}), case_clauses(Val, Cs, Bs, case_clause, Ieval#ieval{line=Line}); %% If statement @@ -686,20 +686,20 @@ expr({'if',Line,Cs}, Bs, Ieval) -> %% Andalso/orelse expr({'andalso',Line,E1,E2}, Bs, Ieval) -> - case expr(E1, Bs, Ieval#ieval{line=Line, last_call=false}) of + case expr(E1, Bs, Ieval#ieval{line=Line, top=false}) of {value,false,_}=Res -> Res; {value,true,_} -> - expr(E2, Bs, Ieval#ieval{line=Line, last_call=false}); + expr(E2, Bs, Ieval#ieval{line=Line, top=false}); {value,Val,Bs} -> exception(error, {badarg,Val}, Bs, Ieval) end; expr({'orelse',Line,E1,E2}, Bs, Ieval) -> - case expr(E1, Bs, Ieval#ieval{line=Line, last_call=false}) of + case expr(E1, Bs, Ieval#ieval{line=Line, top=false}) of {value,true,_}=Res -> Res; {value,false,_} -> - expr(E2, Bs, Ieval#ieval{line=Line, last_call=false}); + expr(E2, Bs, Ieval#ieval{line=Line, top=false}); {value,Val,_} -> exception(error, {badarg,Val}, Bs, Ieval) end; @@ -707,7 +707,7 @@ expr({'orelse',Line,E1,E2}, Bs, Ieval) -> %% Matching expression expr({match,Line,Lhs,Rhs0}, Bs0, Ieval0) -> Ieval = Ieval0#ieval{line=Line}, - {value,Rhs,Bs1} = expr(Rhs0, Bs0, Ieval#ieval{last_call=false}), + {value,Rhs,Bs1} = expr(Rhs0, Bs0, Ieval#ieval{top=false}), case match(Lhs, Rhs, Bs1) of {match,Bs} -> {value,Rhs,Bs}; @@ -876,7 +876,7 @@ expr({'receive',Line,Cs}, Bs0, #ieval{level=Le}=Ieval) -> %% Receive..after statement expr({'receive',Line,Cs,To,ToExprs}, Bs0, #ieval{level=Le}=Ieval0) -> Ieval = Ieval0#ieval{line=Line}, - {value,ToVal,ToBs} = expr(To, Bs0, Ieval#ieval{last_call=false}), + {value,ToVal,ToBs} = expr(To, Bs0, Ieval#ieval{top=false}), trace(receivex, {Le,true}), check_timeoutvalue(ToVal, ToBs, To, Ieval), {Stamp,_} = statistics(wall_clock), @@ -886,7 +886,7 @@ expr({'receive',Line,Cs,To,ToExprs}, Bs0, #ieval{level=Le}=Ieval0) -> %% Send (!) expr({send,Line,To0,Msg0}, Bs0, Ieval0) -> Ieval = Ieval0#ieval{line=Line}, - Ieval1 = Ieval#ieval{last_call=false}, + Ieval1 = Ieval#ieval{top=false}, {value,To,Bs1} = expr(To0, Bs0, Ieval1), {value,Msg,Bs2} = expr(Msg0, Bs0, Ieval1), Bs = merge_bindings(Bs2, Bs1, Ieval), @@ -923,12 +923,12 @@ eval_lc(E, Qs, Bs, Ieval) -> eval_lc1(E, [{generate,Line,P,L0}|Qs], Bs0, Ieval0) -> Ieval = Ieval0#ieval{line=Line}, - {value,L1,Bs1} = expr(L0, Bs0, Ieval#ieval{last_call=false}), + {value,L1,Bs1} = expr(L0, Bs0, Ieval#ieval{top=false}), CompFun = fun(NewBs) -> eval_lc1(E, Qs, NewBs, Ieval) end, eval_generate(L1, P, Bs1, CompFun, Ieval); eval_lc1(E, [{b_generate,Line,P,L0}|Qs], Bs0, Ieval0) -> Ieval = Ieval0#ieval{line=Line}, - {value,Bin,_} = expr(L0, Bs0, Ieval#ieval{last_call=false}), + {value,Bin,_} = expr(L0, Bs0, Ieval#ieval{top=false}), CompFun = fun(NewBs) -> eval_lc1(E, Qs, NewBs, Ieval) end, eval_b_generate(Bin, P, Bs0, CompFun, Ieval); eval_lc1(E, [{guard,Q}|Qs], Bs0, Ieval) -> @@ -937,13 +937,13 @@ eval_lc1(E, [{guard,Q}|Qs], Bs0, Ieval) -> false -> [] end; eval_lc1(E, [Q|Qs], Bs0, Ieval) -> - case expr(Q, Bs0, Ieval#ieval{last_call=false}) of + case expr(Q, Bs0, Ieval#ieval{top=false}) of {value,true,Bs} -> eval_lc1(E, Qs, Bs, Ieval); {value,false,_Bs} -> []; {value,V,Bs} -> exception(error, {bad_filter,V}, Bs, Ieval) end; eval_lc1(E, [], Bs, Ieval) -> - {value,V,_} = expr(E, Bs, Ieval#ieval{last_call=false}), + {value,V,_} = expr(E, Bs, Ieval#ieval{top=false}), [V]. %% eval_bc(Expr,[Qualifier],Bindings,IevalState) -> @@ -956,12 +956,12 @@ eval_bc(E, Qs, Bs, Ieval) -> eval_bc1(E, [{generate,Line,P,L0}|Qs], Bs0, Ieval0) -> Ieval = Ieval0#ieval{line=Line}, - {value,L1,Bs1} = expr(L0, Bs0, Ieval#ieval{last_call=false}), + {value,L1,Bs1} = expr(L0, Bs0, Ieval#ieval{top=false}), CompFun = fun(NewBs) -> eval_bc1(E, Qs, NewBs, Ieval) end, eval_generate(L1, P, Bs1, CompFun, Ieval); eval_bc1(E, [{b_generate,Line,P,L0}|Qs], Bs0, Ieval0) -> Ieval = Ieval0#ieval{line=Line}, - {value,Bin,_} = expr(L0, Bs0, Ieval#ieval{last_call=false}), + {value,Bin,_} = expr(L0, Bs0, Ieval#ieval{top=false}), CompFun = fun(NewBs) -> eval_bc1(E, Qs, NewBs, Ieval) end, eval_b_generate(Bin, P, Bs0, CompFun, Ieval); eval_bc1(E, [{guard,Q}|Qs], Bs0, Ieval) -> @@ -970,13 +970,13 @@ eval_bc1(E, [{guard,Q}|Qs], Bs0, Ieval) -> false -> [] end; eval_bc1(E, [Q|Qs], Bs0, Ieval) -> - case expr(Q, Bs0, Ieval#ieval{last_call=false}) of + case expr(Q, Bs0, Ieval#ieval{top=false}) of {value,true,Bs} -> eval_bc1(E, Qs, Bs, Ieval); {value,false,_Bs} -> []; {value,V,Bs} -> exception(error, {bad_filter,V}, Bs, Ieval) end; eval_bc1(E, [], Bs, Ieval) -> - {value,V,_} = expr(E, Bs, Ieval#ieval{last_call=false}), + {value,V,_} = expr(E, Bs, Ieval#ieval{top=false}), [V]. eval_generate([V|Rest], P, Bs0, CompFun, Ieval) -> @@ -1185,7 +1185,7 @@ eval_list(Es, Bs, Ieval) -> eval_list(Es, [], Bs, Bs, Ieval). eval_list([E|Es], Vs, BsOrig, Bs0, Ieval) -> - {value,V,Bs1} = expr(E, BsOrig, Ieval#ieval{last_call=false}), + {value,V,Bs1} = expr(E, BsOrig, Ieval#ieval{top=false}), eval_list(Es, [V|Vs], BsOrig, merge_bindings(Bs1,Bs0,Ieval), Ieval); eval_list([], Vs, _, Bs, _Ieval) -> {lists:reverse(Vs,[]),Bs}. diff --git a/lib/debugger/src/dbg_ieval.hrl b/lib/debugger/src/dbg_ieval.hrl index a344748f48..ea6189ad02 100644 --- a/lib/debugger/src/dbg_ieval.hrl +++ b/lib/debugger/src/dbg_ieval.hrl @@ -21,6 +21,8 @@ module, % MFA which called the currently function, % interpreted function arguments, % - last_call = false % True if current expression is - }). % the VERY last to be evaluated - % (ie at all, not only in a clause) + + %% True if the current expression is at the top level + %% (i.e. the next call will leave interpreted code). + top = false + }). diff --git a/lib/debugger/src/dbg_istk.erl b/lib/debugger/src/dbg_istk.erl index 8331f46eb8..b852e1f8fd 100644 --- a/lib/debugger/src/dbg_istk.erl +++ b/lib/debugger/src/dbg_istk.erl @@ -61,7 +61,7 @@ init(Stack) -> %% 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}) -> +push(MFA, Bs, #ieval{level=Le,module=Cm,line=Li,top=Lc}) -> Entry = #e{level=Le,mfa=MFA,cm=Cm,line=Li,bindings=Bs}, case get(trace_stack) of false -> ignore; -- cgit v1.2.3 From 778eece5cdc55f19daf354ed374a556dde37cafa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 24 Mar 2011 06:50:23 +0100 Subject: Slightly clean-up and optimize some evaluation code There is no need to set #ieval.top to false in every iteration of eval_list/1. --- lib/debugger/src/dbg_ieval.erl | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'lib/debugger/src') diff --git a/lib/debugger/src/dbg_ieval.erl b/lib/debugger/src/dbg_ieval.erl index cfa8e60d63..e343e6f1e0 100644 --- a/lib/debugger/src/dbg_ieval.erl +++ b/lib/debugger/src/dbg_ieval.erl @@ -616,10 +616,9 @@ expr({value,Val}, Bs, _Ieval) -> % Special case straight values %% List expr({cons,Line,H0,T0}, Bs0, Ieval0) -> - Ieval = Ieval0#ieval{line=Line}, - Ieval1 = Ieval#ieval{top=false}, - {value,H,Bs1} = expr(H0,Bs0,Ieval1), - {value,T,Bs2} = expr(T0,Bs0,Ieval1), + Ieval = Ieval0#ieval{line=Line,top=false}, + {value,H,Bs1} = expr(H0, Bs0, Ieval), + {value,T,Bs2} = expr(T0, Bs0, Ieval), {value,[H|T],merge_bindings(Bs2, Bs1, Ieval)}; %% Tuple @@ -1182,12 +1181,12 @@ flush_traces(Debugged) -> %% eval_list(ExpressionList, Bindings, Ieval) %% Evaluate a list of expressions "in parallel" at the same level. eval_list(Es, Bs, Ieval) -> - eval_list(Es, [], Bs, Bs, Ieval). + eval_list_1(Es, [], Bs, Bs, Ieval#ieval{top=false}). -eval_list([E|Es], Vs, BsOrig, Bs0, Ieval) -> - {value,V,Bs1} = expr(E, BsOrig, Ieval#ieval{top=false}), - eval_list(Es, [V|Vs], BsOrig, merge_bindings(Bs1,Bs0,Ieval), Ieval); -eval_list([], Vs, _, Bs, _Ieval) -> +eval_list_1([E|Es], Vs, BsOrig, Bs0, Ieval) -> + {value,V,Bs1} = expr(E, BsOrig, Ieval), + eval_list_1(Es, [V|Vs], BsOrig, merge_bindings(Bs1, Bs0, Ieval), Ieval); +eval_list_1([], Vs, _, Bs, _Ieval) -> {lists:reverse(Vs,[]),Bs}. %% if_clauses(Clauses, Bindings, Ieval) -- cgit v1.2.3 From 3ccc4730e54f0f9e86bf96360b7600ab8c9e1d47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 31 Mar 2011 07:22:02 +0200 Subject: Rewrite stack handling Problems with the current stack implementation: * The GUI assumes that the stack frame pushed on the stack is level 2. If the 'no_tail' option is set for the process, there may not be an entry for level 2. * In each stack entry, the line number is the line number of the caller, not the line number for the function in the 'mfa' field as might be expected. That complicates generation of a stacktrace with line number information. Change the implementation as follows: * Keep the information for the current function (its MFA and current line number) in the #ieval{} record. Don't push it onto the stack. Only push the information when another function is called. That will ensure that the MFA and the line number is found in the same stack entry. That also has the advantage that if the 'no_tail' option is set, the stack not need to be modified for tail-recursive calls. * Make sure that there always is an entry for level two. --- lib/debugger/src/dbg_icmd.erl | 4 +-- lib/debugger/src/dbg_ieval.erl | 61 ++++++++++++++++++++---------------------- lib/debugger/src/dbg_istk.erl | 49 ++++++++++++++++++--------------- 3 files changed, 58 insertions(+), 56 deletions(-) (limited to 'lib/debugger/src') diff --git a/lib/debugger/src/dbg_icmd.erl b/lib/debugger/src/dbg_icmd.erl index c158aad1cf..b230efaa7a 100644 --- a/lib/debugger/src/dbg_icmd.erl +++ b/lib/debugger/src/dbg_icmd.erl @@ -345,8 +345,8 @@ handle_user_msg({get,stack_frame,From,{Dir,SP}}, _Status, _Bs,_Ieval) -> 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_istk:backtrace(N)). +handle_user_msg({get,backtrace,From,N}, _Status, _Bs, Ieval) -> + reply(From, backtrace, dbg_istk:backtrace(N, Ieval)). set_stack_trace(true) -> set_stack_trace(all); diff --git a/lib/debugger/src/dbg_ieval.erl b/lib/debugger/src/dbg_ieval.erl index e343e6f1e0..445eba3346 100644 --- a/lib/debugger/src/dbg_ieval.erl +++ b/lib/debugger/src/dbg_ieval.erl @@ -382,10 +382,11 @@ catch_value(throw, Reason) -> %% Top level function of meta evaluator. %% Return message to be replied to the target process. %%-------------------------------------------------------------------- -eval_mfa(Debugged, M, F, As, Ieval) -> +eval_mfa(Debugged, M, F, As, #ieval{level=Le}=Ieval0) -> Int = get(int), Bs = erl_eval:new_bindings(), - try eval_function(M,F,As,Bs,extern,Ieval#ieval{top=true}) of + Ieval = Ieval0#ieval{level=Le+1,top=true}, + try do_eval_function(M, F, As, Bs, extern, Ieval) of {value, Val, _Bs} -> {ready, Val} catch @@ -397,18 +398,21 @@ eval_mfa(Debugged, M, F, As, Ieval) -> {exception, {Class, Reason, get(stacktrace)}} end. -eval_function(Mod, Fun, As0, Bs0, _Called, Ieval) when is_function(Fun); - Mod =:= ?MODULE, - Fun =:= eval_fun -> +eval_function(Mod, Name, As, Bs, Called, Ieval0) -> + Ieval = dbg_istk:push(Bs, Ieval0), + Res = do_eval_function(Mod, Name, As, Bs, Called, Ieval), + dbg_istk:pop(), + Res. + +do_eval_function(Mod, Fun, As0, Bs0, _, Ieval) when is_function(Fun); + Mod =:= ?MODULE, + Fun =:= eval_fun -> #ieval{level=Le, line=Li, top=Top} = Ieval, case lambda(Fun, As0) of {Cs,Module,Name,As,Bs} -> - 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}), - dbg_istk:pop(), + fnk_clauses(Cs, Module, Name, As, Bs, Ieval), trace(return, {Le,Val}), {value, Val, Bs0}; @@ -416,12 +420,9 @@ 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 -> - 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}), - dbg_istk:pop(), + debugged_cmd({apply,erlang,apply,[Fun,As0]}, Bs0, Ieval), trace(return, {Le,Val}), {value, Val, Bs0}; @@ -433,22 +434,19 @@ eval_function(Mod, Fun, As0, Bs0, _Called, Ieval) when is_function(Fun); end; %% Common Test adaptation -eval_function(ct_line, line, As, Bs, extern, #ieval{level=Le}=Ieval) -> +do_eval_function(ct_line, line, As, Bs, extern, #ieval{level=Le}=Ieval) -> debugged_cmd({apply,ct_line,line,As}, Bs, Ieval#ieval{level=Le+1}), {value, ignore, Bs}; -eval_function(Mod, Name, As0, Bs0, Called, Ieval) -> - #ieval{level=Le, line=Li, top=Top} = Ieval, - - dbg_istk:push({Mod,Name,As0}, Bs0, Ieval), +do_eval_function(Mod, Name, As0, Bs0, Called, Ieval0) -> + #ieval{level=Le,line=Li,top=Top} = Ieval0, trace(call, {Called, {Le,Li,Mod,Name,As0}}), - + Ieval = Ieval0#ieval{module=Mod,function=Name,arguments=As0}, case get_function(Mod, Name, As0, Called) of Cs when is_list(Cs) -> {value, Val, _Bs} = fnk_clauses(Cs, Mod, Name, As0, erl_eval:new_bindings(), - Ieval#ieval{level=Le+1}), - dbg_istk:pop(), + Ieval), trace(return, {Le,Val}), {value, Val, Bs0}; @@ -456,9 +454,7 @@ eval_function(Mod, Name, As0, Bs0, Called, Ieval) -> {value, {dbg_apply,Mod,Name,As0}, Bs0}; not_interpreted -> {value, Val, _Bs} = - debugged_cmd({apply,Mod,Name,As0}, Bs0, - Ieval#ieval{level=Le+1}), - dbg_istk:pop(), + debugged_cmd({apply,Mod,Name,As0}, Bs0, Ieval), trace(return, {Le,Val}), {value, Val, Bs0}; @@ -807,10 +803,11 @@ expr({dbg,Line,exit,As0}, Bs0, #ieval{level=Le}=Ieval0) -> %% Call to "safe" BIF, ie a BIF that can be executed in Meta process expr({safe_bif,Line,M,F,As0}, Bs0, #ieval{level=Le}=Ieval0) -> - Ieval = Ieval0#ieval{line=Line}, - {As,Bs} = eval_list(As0, Bs0, Ieval), + Ieval1 = Ieval0#ieval{line=Line}, + {As,Bs} = eval_list(As0, Bs0, Ieval1), trace(bif, {Le,Line,M,F,As}), - dbg_istk:push({M,F,As}, Bs0, Ieval), + Ieval2 = dbg_istk:push(Bs0, Ieval1), + Ieval = Ieval2#ieval{module=M,function=F,arguments=As}, {_,Value,_} = Res = safe_bif(M, F, As, Bs, Ieval), trace(return, {Le,Value}), dbg_istk:pop(), @@ -818,12 +815,12 @@ expr({safe_bif,Line,M,F,As0}, Bs0, #ieval{level=Le}=Ieval0) -> %% Call to a BIF that must be evaluated in the correct process expr({bif,Line,M,F,As0}, Bs0, #ieval{level=Le}=Ieval0) -> - Ieval = Ieval0#ieval{line=Line}, - {As,Bs} = eval_list(As0, Bs0, Ieval), + Ieval1 = Ieval0#ieval{line=Line}, + {As,Bs} = eval_list(As0, Bs0, Ieval1), trace(bif, {Le,Line,M,F,As}), - dbg_istk:push({M,F,As}, Bs0, Ieval), - {_,Value,_} = - Res = debugged_cmd({apply,M,F,As}, Bs, Ieval#ieval{level=Le+1}), + Ieval2 = dbg_istk:push(Bs0, Ieval1), + Ieval = Ieval2#ieval{module=M,function=F,arguments=As}, + {_,Value,_} = Res = debugged_cmd({apply,M,F,As}, Bs, Ieval), trace(return, {Le,Value}), dbg_istk:pop(), Res; diff --git a/lib/debugger/src/dbg_istk.erl b/lib/debugger/src/dbg_istk.erl index b852e1f8fd..2e7b114fab 100644 --- a/lib/debugger/src/dbg_istk.erl +++ b/lib/debugger/src/dbg_istk.erl @@ -18,9 +18,9 @@ %% -module(dbg_istk). -export([init/0,to_external/0,from_external/1, - push/3,pop/0,pop/1,stack_level/0, + push/2,pop/0,pop/1,stack_level/0, exception_stacktrace/2, - bindings/1,stack_frame/2,backtrace/1, + bindings/1,stack_frame/2,backtrace/2, in_use_p/2]). -include("dbg_ieval.hrl"). @@ -30,7 +30,6 @@ -record(e, {level, %Level mfa, %{Mod,Func,Args|Arity}|{Fun,Args} - cm, %Module called from line, %Line called from bindings }). @@ -61,17 +60,17 @@ init(Stack) -> %% false - nothing is pushed %% Whenever a function returns, the corresponding call frame is popped. -push(MFA, Bs, #ieval{level=Le,module=Cm,line=Li,top=Lc}) -> - Entry = #e{level=Le,mfa=MFA,cm=Cm,line=Li,bindings=Bs}, +push(Bs, #ieval{level=Le,module=Mod,function=Name,arguments=As, + line=Li,top=Lc}=Ieval) -> + Entry = #e{level=Le,mfa={Mod,Name,As},line=Li,bindings=Bs}, case get(trace_stack) of - false -> ignore; + false -> + Ieval#ieval{level=Le+1}; no_tail when Lc -> - case get(?STACK) of - [] -> put(?STACK, [Entry]); - [_Entry|Entries] -> put(?STACK, [Entry|Entries]) - end; + Ieval; _ -> % all | no_tail when Lc =:= false - put(?STACK, [Entry|get(?STACK)]) + put(?STACK, [Entry|get(?STACK)]), + Ieval#ieval{level=Le+1} end. pop() -> @@ -117,13 +116,15 @@ stack_level([#e{level=Le}|_]) -> Le. %% function and convert argument lists to arity for all but the topmost %% entry (and funs). -exception_stacktrace(complete, #ieval{}) -> - fix_stacktrace(1); +exception_stacktrace(complete, #ieval{}=Ieval) -> + #ieval{module=Mod,function=Name,arguments=As} = Ieval, + Stk = [#e{mfa={Mod,Name,As}}|get(?STACK)], + fix_stacktrace(Stk); exception_stacktrace(no_current, #ieval{}) -> - fix_stacktrace(2). + fix_stacktrace(get(?STACK)). -fix_stacktrace(Start) -> - case fix_stacktrace2(sublist(get(?STACK), Start, 3)) of +fix_stacktrace(Stk) -> + case fix_stacktrace2(sublist(Stk, 1, 3)) of [] -> []; [H|T] -> @@ -180,13 +181,15 @@ stack_frame(up, SP) -> stack_frame(down, SP) -> stack_frame(SP, down, lists:reverse(get(?STACK))). -stack_frame(SP, up, [#e{level=Le,cm=Cm,line=Li,bindings=Bs}|_]) when Le < SP -> +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,cm=Cm,line=Li,bindings=Bs}|_]) when Le > SP -> +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,cm=Cm,line=Li,bindings=Bs}|_] -> + [#e{level=Le,mfa={Cm,_,_},line=Li,bindings=Bs}|_] -> {Le,{Cm,Li},Bs}; [] when Dir =:= up -> top; @@ -200,10 +203,12 @@ stack_frame(SP, Dir, [_Entry|Stack]) -> %% HowMany = all | int() %% Backtrace = {Le, MFA} %% Return all/the last N called functions, in reversed call order -backtrace(HowMany) -> +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 -> get(?STACK); - N -> lists:sublist(get(?STACK), N) + all -> Stack0; + N -> lists:sublist(Stack0, N) end, [{Le,MFA} || #e{level=Le,mfa=MFA} <- Stack]. -- cgit v1.2.3 From e0fe23461cc94a80e02a9542ab2182f62badd5de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 31 Mar 2011 07:31:14 +0200 Subject: Don't build stacktrace until erlang:get_stacktrace() is called Currently, dbg_istk:exception_stacktrace/2 does not do a very good job imitating BEAM's stacktrace. The reason is that it need to be relatively fast since a simulated stacktrace is constructed not only when an exception oocurs, but also before every call to non-interpreted code. To prepare for a future more thorough (and slower) stacktrace construction, refactor the building of the stacktrace so that it only is done when erlang:get_stacktrace/0 is called. --- lib/debugger/src/dbg_debugged.erl | 13 +++++++------ lib/debugger/src/dbg_ieval.erl | 38 +++++++++++++++++++++++++++++--------- lib/debugger/src/dbg_istk.erl | 34 +++++++++++++++++++++------------- 3 files changed, 57 insertions(+), 28 deletions(-) (limited to 'lib/debugger/src') diff --git a/lib/debugger/src/dbg_debugged.erl b/lib/debugger/src/dbg_debugged.erl index 3732c40c73..76ed49fa93 100644 --- a/lib/debugger/src/dbg_debugged.erl +++ b/lib/debugger/src/dbg_debugged.erl @@ -76,8 +76,8 @@ msg_loop(Meta, Mref, SaveStacktrace) -> msg_loop(Meta, Mref, SaveStacktrace); %% Meta needs something evaluated within context of real process - {sys, Meta, {command, Command, Stacktrace}} -> - Reply = handle_command(Command, Stacktrace), + {sys, Meta, {command,Command}} -> + Reply = handle_command(Command), Meta ! {sys, self(), Reply}, msg_loop(Meta, Mref, SaveStacktrace); @@ -93,11 +93,12 @@ msg_loop(Meta, Mref, SaveStacktrace) -> end end. -handle_command(Command, Stacktrace) -> - try reply(Command) +handle_command(Command) -> + try + reply(Command) catch Class:Reason -> - Stacktrace2 = stacktrace_f(erlang:get_stacktrace()), - {exception, {Class,Reason,Stacktrace2++Stacktrace}} + Stacktrace = stacktrace_f(erlang:get_stacktrace()), + {exception,{Class,Reason,Stacktrace}} end. reply({apply,M,F,As}) -> diff --git a/lib/debugger/src/dbg_ieval.erl b/lib/debugger/src/dbg_ieval.erl index 445eba3346..fe7f9af1a9 100644 --- a/lib/debugger/src/dbg_ieval.erl +++ b/lib/debugger/src/dbg_ieval.erl @@ -168,7 +168,7 @@ check_exit_msg(_Msg, _Bs, _Ieval) -> %% and then raise the exception. %%-------------------------------------------------------------------- exception(Class, Reason, Bs, Ieval) -> - exception(Class, Reason, dbg_istk:exception_stacktrace(complete, Ieval), + exception(Class, Reason, dbg_istk:delayed_stacktrace(Ieval), Bs, Ieval). exception(Class, Reason, Stacktrace, Bs, #ieval{module=M, line=Line}) -> @@ -226,8 +226,7 @@ meta(Int, Debugged, M, F, As) -> debugged_cmd(Cmd, Bs, Ieval) -> Debugged = get(self), - Stacktrace = dbg_istk:exception_stacktrace(no_current, Ieval), - Debugged ! {sys, self(), {command,Cmd,Stacktrace}}, + Debugged ! {sys, self(), {command,Cmd}}, meta_loop(Debugged, Bs, Ieval). meta_loop(Debugged, Bs, #ieval{level=Le} = Ieval) -> @@ -240,12 +239,17 @@ meta_loop(Debugged, Bs, #ieval{level=Le} = Ieval) -> {value, Val, Bs}; {sys, Debugged, {value,Val,Bs2}} -> {value, Val, Bs2}; - {sys, Debugged, {exception,{Class,Reason,Stacktrace}}} -> + {sys, Debugged, {exception,{Class,Reason,Stk}}} -> case get(exit_info) of - %% Error occured outside interpreted code + %% Error occurred outside of interpreted code. undefined -> - exception(Class,Reason,Stacktrace,Bs,Ieval); + MakeStk0 = dbg_istk:delayed_stacktrace(), + MakeStk = fun(Depth0) -> + Depth = max(0, Depth0 - length(Stk)), + Stk ++ MakeStk0(Depth) + end, + exception(Class, Reason, MakeStk, Bs, Ieval); %% Error must have occured within a re-entry to %% interpreted code, simply raise the exception @@ -370,7 +374,7 @@ format_args1([]) -> %% Mimic catch behaviour catch_value(error, Reason) -> - {'EXIT',{Reason,get(stacktrace)}}; + {'EXIT',{Reason,get_stacktrace()}}; catch_value(exit, Reason) -> {'EXIT',Reason}; catch_value(throw, Reason) -> @@ -395,7 +399,7 @@ eval_mfa(Debugged, M, F, As, #ieval{level=Le}=Ieval0) -> exit:{Int, Reason} -> exit(Reason); Class:Reason -> - {exception, {Class, Reason, get(stacktrace)}} + {exception, {Class, Reason, get_stacktrace()}} end. eval_function(Mod, Name, As, Bs, Called, Ieval0) -> @@ -782,7 +786,7 @@ expr({dbg,Line,self,[]}, Bs, #ieval{level=Le}) -> {value,Self,Bs}; expr({dbg,Line,get_stacktrace,[]}, Bs, #ieval{level=Le}) -> trace(bif, {Le,Line,erlang,get_stacktrace,[]}), - Stacktrace = get(stacktrace), + Stacktrace = get_stacktrace(), trace(return, {Le,Stacktrace}), {value,Stacktrace,Bs}; expr({dbg,Line,throw,As0}, Bs0, #ieval{level=Le}=Ieval0) -> @@ -1501,3 +1505,19 @@ add_binding(N,Val,[B1|Bs]) -> [B1|add_binding(N,Val,Bs)]; add_binding(N,Val,[]) -> [{N,Val}]. + +%% get_stacktrace() -> Stacktrace +%% Return the latest stacktrace for the process. +get_stacktrace() -> + case get(stacktrace) of + MakeStk when is_function(MakeStk, 1) -> + %% The stacktrace has not been constructed before. + %% Construct it and remember the result. + Depth = erlang:system_flag(backtrace_depth, 8), + erlang:system_flag(backtrace_depth, Depth), + Stk = MakeStk(Depth), + put(stacktrace, Stk), + Stk; + Stk when is_list(Stk) -> + Stk + end. diff --git a/lib/debugger/src/dbg_istk.erl b/lib/debugger/src/dbg_istk.erl index 2e7b114fab..2c4c2a3518 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, - exception_stacktrace/2, + delayed_stacktrace/0,delayed_stacktrace/1, bindings/1,stack_frame/2,backtrace/2, in_use_p/2]). @@ -106,25 +106,33 @@ stack_level() -> stack_level([]) -> 1; stack_level([#e{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. +%% delayed_stacktrace() -> CreateStacktraceFun +%% delayed_stacktrace(#ieval{}) -> CreateStacktraceFun +%% 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). -exception_stacktrace(complete, #ieval{}=Ieval) -> +delayed_stacktrace() -> + Stack = get(?STACK), + do_delayed_stacktrace(Stack). + +delayed_stacktrace(Ieval) -> #ieval{module=Mod,function=Name,arguments=As} = Ieval, - Stk = [#e{mfa={Mod,Name,As}}|get(?STACK)], - fix_stacktrace(Stk); -exception_stacktrace(no_current, #ieval{}) -> - fix_stacktrace(get(?STACK)). + Stack = [#e{mfa={Mod,Name,As}}|get(?STACK)], + do_delayed_stacktrace(Stack). + +do_delayed_stacktrace(Stack) -> + fun(_NumEntries) -> + fix_stacktrace(Stack) + end. -fix_stacktrace(Stk) -> - case fix_stacktrace2(sublist(Stk, 1, 3)) of +fix_stacktrace(Stack) -> + case fix_stacktrace2(sublist(Stack, 1, 3)) of [] -> []; [H|T] -> -- cgit v1.2.3 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 From 45ec105b3a7962370413906b5fa49a6a73cf3d83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 17 Mar 2011 08:33:40 +0100 Subject: Make sure that erlang:raise/3 sets the emulated stacktrace erlang:raise/3 was evaluated in the real process, which produced a correct stacktrace, but did not set emulated stacktrace for the process. Thus, a subsequent call to erlang:get_stacktrace/0 would retrieve the previous stacktrace for the process. --- lib/debugger/src/dbg_ieval.erl | 19 +++++++++++++++++++ lib/debugger/src/dbg_iload.erl | 2 ++ 2 files changed, 21 insertions(+) (limited to 'lib/debugger/src') diff --git a/lib/debugger/src/dbg_ieval.erl b/lib/debugger/src/dbg_ieval.erl index 32848a00ff..a9a5e171f7 100644 --- a/lib/debugger/src/dbg_ieval.erl +++ b/lib/debugger/src/dbg_ieval.erl @@ -797,6 +797,25 @@ expr({dbg,Line,get_stacktrace,[]}, Bs, #ieval{level=Le}) -> Stacktrace = get_stacktrace(), trace(return, {Le,Stacktrace}), {value,Stacktrace,Bs}; +expr({dbg,Line,raise,As0}, Bs0, #ieval{level=Le}=Ieval0) -> + %% Since erlang:get_stacktrace/0 is emulated, we will + %% need to emulate erlang:raise/3 too so that we can + %% capture the stacktrace. + Ieval = Ieval0#ieval{line=Line}, + {[Class,Reason,Stk0]=As,Bs} = eval_list(As0, Bs0, Ieval), + trace(bif, {Le,Line,erlang,raise,As}), + try + %% Evaluate raise/3 for error checking and + %% truncating of the stacktrace to the correct depth. + Error = erlang:raise(Class, Reason, Stk0), + trace(return, {Le,Error}), + {value,Error,Bs} + catch + _:_ -> + Stk = erlang:get_stacktrace(), %Possibly truncated. + StkFun = fun(_) -> Stk end, + do_exception(Class, Reason, StkFun, Bs, Ieval) + end; expr({dbg,Line,throw,As0}, Bs0, #ieval{level=Le}=Ieval0) -> Ieval = Ieval0#ieval{line=Line}, {[Term],Bs} = eval_list(As0, Bs0, Ieval), diff --git a/lib/debugger/src/dbg_iload.erl b/lib/debugger/src/dbg_iload.erl index c94dce1b5e..db0fe0bc10 100644 --- a/lib/debugger/src/dbg_iload.erl +++ b/lib/debugger/src/dbg_iload.erl @@ -378,6 +378,8 @@ expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,error}},[_]=As}) -> {dbg,Line,error,expr_list(As)}; expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,exit}},[_]=As}) -> {dbg,Line,exit,expr_list(As)}; +expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,raise}},[_,_,_]=As}) -> + {dbg,Line,raise,expr_list(As)}; expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,apply}},[_,_,_]=As0}) -> As = expr_list(As0), {apply,Line,As}; -- cgit v1.2.3 From de89cb1afac666d39526f68e4a5b817c246b5ad2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 17 Mar 2011 11:22:33 +0100 Subject: Handle terms in the top-level of guards properly Expressions in guards such as: f() when [1,2,3] -> ok. would cause the debugger to crash when attempting to interpret the module containing the expressions. Other kind of constants such as: f() when 42 -> ok. were converted to an invalid internal format ({integer,Line,42} instead of {value,Line,42}), but that happened to work because because anything not equal to 'true' (even a crash) was interpreted as 'false'. Make sure to handle all possible expressions and convert them directly to {value,Line,false}. Remove the special handling of the atom 'true' in and_guard/1 since it is no longer needed. --- lib/debugger/src/dbg_iload.erl | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'lib/debugger/src') diff --git a/lib/debugger/src/dbg_iload.erl b/lib/debugger/src/dbg_iload.erl index db0fe0bc10..acaf664fc6 100644 --- a/lib/debugger/src/dbg_iload.erl +++ b/lib/debugger/src/dbg_iload.erl @@ -225,8 +225,6 @@ guard([G0|Gs]) -> [G1|guard(Gs)]; guard([]) -> []. -and_guard([{atom,_,true}|Gs]) -> - and_guard(Gs); and_guard([G0|Gs]) -> G1 = guard_test(G0), [G1|and_guard(Gs)]; @@ -251,12 +249,18 @@ guard_test({op,Line,Op,L0,R0}) -> L1 = gexpr(L0), R1 = gexpr(R0), %They see the same variables {safe_bif,Line,erlang,Op,[L1,R1]}; -guard_test({integer,_,_}=I) -> I; -guard_test({char,_,_}=C) -> C; -guard_test({float,_,_}=F) -> F; -guard_test({atom,_,_}=A) -> A; -guard_test({nil,_}=N) -> N; -guard_test({var,_,_}=V) ->V. % Boolean var +guard_test({var,_,_}=V) ->V; % Boolean var +guard_test({atom,Line,true}) -> {value,Line,true}; +%% All other constants at this level means false. +guard_test({atom,Line,_}) -> {value,Line,false}; +guard_test({integer,Line,_}) -> {value,Line,false}; +guard_test({char,Line,_}) -> {value,Line,false}; +guard_test({float,Line,_}) -> {value,Line,false}; +guard_test({string,Line,_}) -> {value,Line,false}; +guard_test({nil,Line}) -> {value,Line,false}; +guard_test({cons,Line,_,_}) -> {value,Line,false}; +guard_test({tuple,Line,_}) -> {value,Line,false}; +guard_test({bin,Line,_}) -> {value,Line,false}. gexpr({var,Line,V}) -> {var,Line,V}; gexpr({integer,Line,I}) -> {value,Line,I}; -- cgit v1.2.3 From 34079325cd195dc5af66ed26e0e54944319f5846 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 17 Mar 2011 11:47:32 +0100 Subject: Fix handling of guard BIFs in list and binary comprehensions In a list comprehension, a failing call to a guard BIF means false (rather than an exception). --- lib/debugger/src/dbg_iload.erl | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) (limited to 'lib/debugger/src') diff --git a/lib/debugger/src/dbg_iload.erl b/lib/debugger/src/dbg_iload.erl index acaf664fc6..fde4e05128 100644 --- a/lib/debugger/src/dbg_iload.erl +++ b/lib/debugger/src/dbg_iload.erl @@ -431,8 +431,8 @@ expr({lc,Line,E0,Gs0}) -> %R8. ({b_generate,L,P0,Qs}) -> %R12. {b_generate,L,expr(P0),expr(Qs)}; (Expr) -> - case is_guard_test(Expr) of - true -> {guard,[[guard_test(Expr)]]}; + case is_guard(Expr) of + true -> {guard,guard([[Expr]])}; false -> expr(Expr) end end, Gs0), @@ -443,8 +443,8 @@ expr({bc,Line,E0,Gs0}) -> %R12. ({b_generate,L,P0,Qs}) -> %R12. {b_generate,L,expr(P0),expr(Qs)}; (Expr) -> - case is_guard_test(Expr) of - true -> {guard,[[guard_test(Expr)]]}; + case is_guard(Expr) of + true -> {guard,guard([[Expr]])}; false -> expr(Expr) end end, Gs0), @@ -486,16 +486,18 @@ expr({bin_element,Line,Expr,Size,Type}) -> expr(Other) -> exit({?MODULE,{unknown_expr,Other}}). -%% is_guard_test(Expression) -> true | false. -%% Test if a general expression is a guard test. Cannot use erl_lint -%% here as sys_pre_expand has transformed source. +%% is_guard(Expression) -> true | false. +%% Test if a general expression is a guard test or guard BIF. +%% Cannot use erl_lint here as sys_pre_expand has transformed source. -is_guard_test({op,_,Op,L,R}) -> +is_guard({op,_,Op,L,R}) -> erl_internal:comp_op(Op, 2) andalso is_gexpr_list([L,R]); -is_guard_test({call,_,{remote,_,{atom,_,erlang},{atom,_,Test}},As}) -> - erl_internal:type_test(Test, length(As)) andalso is_gexpr_list(As); -is_guard_test({atom,_,true}) -> true; -is_guard_test(_) -> false. +is_guard({call,_,{remote,_,{atom,_,erlang},{atom,_,Test}},As}) -> + Arity = length(As), + (erl_internal:guard_bif(Test, Arity) orelse + erl_internal:old_type_test(Test, Arity)) andalso is_gexpr_list(As); +is_guard({atom,_,true}) -> true; +is_guard(_) -> false. is_gexpr({var,_,_}) -> true; is_gexpr({atom,_,_}) -> true; -- cgit v1.2.3 From 19713d214462b863def874385844521f00a2bac5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 31 Mar 2011 08:19:21 +0200 Subject: Delay saving of the stack in exit_info when an exception occurs When an exception occurs, the entire stack will be converted to the external term format and kept in the exit_info variable in the process dictionary. But the exit_info variable will only be needed if the exception causes the process to terminate (not if it is caught). Delay the conversion of the stack to external format. That can save significant amounts of time (e.g. in bs_construct:mem_leak/1). --- lib/debugger/src/dbg_ieval.erl | 11 +++++++---- lib/debugger/src/dbg_istk.erl | 7 ++++--- 2 files changed, 11 insertions(+), 7 deletions(-) (limited to 'lib/debugger/src') diff --git a/lib/debugger/src/dbg_ieval.erl b/lib/debugger/src/dbg_ieval.erl index a9a5e171f7..9b6919074f 100644 --- a/lib/debugger/src/dbg_ieval.erl +++ b/lib/debugger/src/dbg_ieval.erl @@ -140,12 +140,12 @@ check_exit_msg({'DOWN',_,_,_,Reason}, Bs, undefined when Le =:= 1 -> % died outside interpreted code {}; undefined when Le > 1 -> - StackExternal = dbg_istk:to_external(), + StackExternal = (dbg_istk:delayed_to_external())(), {{Mod, Li}, Bs, StackExternal}; %% Debugged has terminated due to an exception - ExitInfo0 -> - ExitInfo0 + ExitInfo0 when is_function(ExitInfo0, 0) -> + ExitInfo0() end, dbg_iserver:cast(get(int), {set_exit_info,self(),ExitInfo}), @@ -180,7 +180,10 @@ exception(Class, Reason, Bs, Ieval, true) -> Bs, Ieval). do_exception(Class, Reason, Stacktrace, Bs, #ieval{module=M, line=Line}) -> - ExitInfo = {{M,Line}, Bs, dbg_istk:to_external()}, + StackFun = dbg_istk:delayed_to_external(), + ExitInfo = fun() -> + {{M,Line},Bs,StackFun()} + end, put(exit_info, ExitInfo), put(stacktrace, Stacktrace), erlang:Class(Reason). diff --git a/lib/debugger/src/dbg_istk.erl b/lib/debugger/src/dbg_istk.erl index 34070aa8f2..a0d3069d50 100644 --- a/lib/debugger/src/dbg_istk.erl +++ b/lib/debugger/src/dbg_istk.erl @@ -17,7 +17,7 @@ %% %CopyrightEnd% %% -module(dbg_istk). --export([init/0,to_external/0,from_external/1, +-export([init/0,delayed_to_external/0,from_external/1, push/2,pop/0,pop/1,stack_level/0, delayed_stacktrace/0,delayed_stacktrace/2, bindings/1,stack_frame/2,backtrace/2, @@ -37,8 +37,9 @@ init() -> init([]). -to_external() -> - {stack,term_to_binary(get(?STACK))}. +delayed_to_external() -> + Stack = get(?STACK), + fun() -> {stack,term_to_binary(Stack)} end. from_external({stack,Stk}) -> put(?STACK, binary_to_term(Stk)). -- cgit v1.2.3 From f2e0e976382eddd3d120110c87882a2185e868aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 22 Mar 2011 05:23:09 +0100 Subject: Fix binary matching in the debugger 'eval_bits' is a common utility module used for evaluting binary construction and matching. The functions that do matching (match_bits/{6,7} and bin_gen/6) are supposed to treat the bindings as an abstract data type, but they assume that the bindings have the same representation as in the erl_eval module. That may cause binary matching to fail in the debugger, because the debugger represents the bindings as an unordered list of two-tuples, while the erl_eval modules uses an ordered list of two-tuple (an ordset). One way to fix the problem would be to let the debugger to use ordered lists to represent the bindings. Unfortunately, that would also change how the bindings are presented in the user interface. Currently, the variable have most been recently assigned is shown first, which is convenient. Fix the matching problem by mending the leaky abstraction in eval_bits. The matching functions needs to be passed two additional operations: one for looking up a variable in the bindings and one for adding a binding. Those operations could be passed as two more funs (in addition to the evaluation and match fun already passed), but the functions already have too many arguments. Therefore, change the meaning of the match fun, so that the first argument is the operation to perform ('match', 'binding', or 'add_binding') and second argument is a tuple with arguments for the operation. --- lib/debugger/src/dbg_ieval.erl | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'lib/debugger/src') diff --git a/lib/debugger/src/dbg_ieval.erl b/lib/debugger/src/dbg_ieval.erl index 9b6919074f..b0792b05f0 100644 --- a/lib/debugger/src/dbg_ieval.erl +++ b/lib/debugger/src/dbg_ieval.erl @@ -1023,7 +1023,7 @@ eval_generate(Term, _P, Bs, _CompFun, Ieval) -> exception(error, {bad_generator,Term}, Bs, Ieval). eval_b_generate(<<_/bitstring>>=Bin, P, Bs0, CompFun, Ieval) -> - Mfun = fun(L, R, Bs) -> match1(L, R, Bs, Bs0) end, + Mfun = match_fun(Bs0), Efun = fun(Exp, Bs) -> expr(Exp, Bs, #ieval{}) end, case eval_bits:bin_gen(P, Bin, erl_eval:new_bindings(), Bs0, Mfun, Efun) of {match,Rest,Bs1} -> @@ -1392,11 +1392,9 @@ match1({cons,_,H,T}, [H1|T1], Bs0, BBs) -> match1({tuple,_,Elts}, Tuple, Bs, BBs) when length(Elts) =:= tuple_size(Tuple) -> match_tuple(Elts, Tuple, 1, Bs, BBs); -match1({bin,_,Fs}, B, Bs0, BBs0) when is_bitstring(B) -> - Bs1 = lists:sort(Bs0), %Kludge. - BBs = lists:sort(BBs0), - try eval_bits:match_bits(Fs, B, Bs1, BBs, - fun(L, R, Bs) -> match1(L, R, Bs, BBs) end, +match1({bin,_,Fs}, B, Bs0, BBs) when is_bitstring(B) -> + try eval_bits:match_bits(Fs, B, Bs0, BBs, + match_fun(BBs), fun(E, Bs) -> expr(E, Bs, #ieval{}) end, false) catch @@ -1405,6 +1403,12 @@ match1({bin,_,Fs}, B, Bs0, BBs0) when is_bitstring(B) -> match1(_,_,_,_) -> throw(nomatch). +match_fun(BBs) -> + fun(match, {L,R,Bs}) -> match1(L, R, Bs, BBs); + (binding, {Name,Bs}) -> binding(Name, Bs); + (add_binding, {Name,Val,Bs}) -> add_binding(Name, Val, Bs) + end. + match_tuple([E|Es], Tuple, I, Bs0, BBs) -> {match,Bs} = match1(E, element(I, Tuple), Bs0, BBs), match_tuple(Es, Tuple, I+1, Bs, BBs); -- cgit v1.2.3 From bbb379003d071f19595bb36d3ead79ba0c8474d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 24 Mar 2011 11:23:58 +0100 Subject: dbg_ieval: Properly handle exceptions when binary construction fails An exception from eval_bits:expr_grp/5 (for constructing binaries) was not caught and handled by exception/4; thus exit_info and and stacktrace for the process was not set. --- lib/debugger/src/dbg_ieval.erl | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'lib/debugger/src') diff --git a/lib/debugger/src/dbg_ieval.erl b/lib/debugger/src/dbg_ieval.erl index b0792b05f0..d4009cea4d 100644 --- a/lib/debugger/src/dbg_ieval.erl +++ b/lib/debugger/src/dbg_ieval.erl @@ -925,10 +925,15 @@ expr({send,Line,To0,Msg0}, Bs0, Ieval0) -> %% Binary expr({bin,Line,Fs}, Bs0, Ieval0) -> Ieval = Ieval0#ieval{line=Line}, - eval_bits:expr_grp(Fs, Bs0, - fun (E, B) -> expr(E, B, Ieval) end, - [], - false); + try + eval_bits:expr_grp(Fs, Bs0, + fun (E, B) -> expr(E, B, Ieval) end, + [], + false) + catch + Class:Reason -> + exception(Class, Reason, Bs0, Ieval) + end; %% List comprehension expr({lc,_Line,E,Qs}, Bs, Ieval) -> -- cgit v1.2.3 From e21b58ac5a016d62bba8117ec09105bcac8b94e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 31 Mar 2011 08:33:56 +0200 Subject: Fix the no_tail option The 'no_tail' option was broken and would work almost as the 'all' option, because it would use #ieval.top (formerly known as #ieval.last_call) as the basis for its decision to push or not. Fix it by including a boolean in each call/apply instruction indicating whether the call is tail-recursive and pass that boolean to the dbg_istk:push() function. --- lib/debugger/src/dbg_ieval.erl | 30 +++--- lib/debugger/src/dbg_iload.erl | 208 +++++++++++++++++++++-------------------- lib/debugger/src/dbg_istk.erl | 6 +- 3 files changed, 123 insertions(+), 121 deletions(-) (limited to 'lib/debugger/src') diff --git a/lib/debugger/src/dbg_ieval.erl b/lib/debugger/src/dbg_ieval.erl index d4009cea4d..251c11fdb9 100644 --- a/lib/debugger/src/dbg_ieval.erl +++ b/lib/debugger/src/dbg_ieval.erl @@ -413,8 +413,8 @@ eval_mfa(Debugged, M, F, As, #ieval{level=Le}=Ieval0) -> {exception, {Class, Reason, get_stacktrace()}} end. -eval_function(Mod, Name, As, Bs, Called, Ieval0) -> - Ieval = dbg_istk:push(Bs, Ieval0), +eval_function(Mod, Name, As, Bs, Called, Ieval0, Lc) -> + Ieval = dbg_istk:push(Bs, Ieval0, Lc), Res = do_eval_function(Mod, Name, As, Bs, Called, Ieval), dbg_istk:pop(), Res. @@ -773,21 +773,21 @@ expr({make_fun,Line,Name,Cs}, Bs, #ieval{module=Module}=Ieval) -> {value,Fun,Bs}; %% Common test adaptation -expr({call_remote,0,ct_line,line,As0}, Bs0, Ieval0) -> +expr({call_remote,0,ct_line,line,As0,Lc}, Bs0, Ieval0) -> {As,_Bs} = eval_list(As0, Bs0, Ieval0), - eval_function(ct_line, line, As, Bs0, extern, Ieval0); + eval_function(ct_line, line, As, Bs0, extern, Ieval0, Lc); %% Local function call -expr({local_call,Line,F,As0}, Bs0, #ieval{module=M} = Ieval0) -> +expr({local_call,Line,F,As0,Lc}, Bs0, #ieval{module=M} = Ieval0) -> Ieval = Ieval0#ieval{line=Line}, {As,Bs} = eval_list(As0, Bs0, Ieval), - eval_function(M, F, As, Bs, local, Ieval); + eval_function(M, F, As, Bs, local, Ieval, Lc); %% Remote function call -expr({call_remote,Line,M,F,As0}, Bs0, Ieval0) -> +expr({call_remote,Line,M,F,As0,Lc}, Bs0, Ieval0) -> Ieval = Ieval0#ieval{line=Line}, {As,Bs} = eval_list(As0, Bs0, Ieval), - eval_function(M, F, As, Bs, extern, Ieval); + eval_function(M, F, As, Bs, extern, Ieval, Lc); %% Emulated semantics of some BIFs expr({dbg,Line,self,[]}, Bs, #ieval{level=Le}) -> @@ -840,7 +840,7 @@ expr({safe_bif,Line,M,F,As0}, Bs0, #ieval{level=Le}=Ieval0) -> Ieval1 = Ieval0#ieval{line=Line}, {As,Bs} = eval_list(As0, Bs0, Ieval1), trace(bif, {Le,Line,M,F,As}), - Ieval2 = dbg_istk:push(Bs0, Ieval1), + Ieval2 = dbg_istk:push(Bs0, Ieval1, false), Ieval = Ieval2#ieval{module=M,function=F,arguments=As}, {_,Value,_} = Res = safe_bif(M, F, As, Bs, Ieval), trace(return, {Le,Value}), @@ -852,7 +852,7 @@ expr({bif,Line,M,F,As0}, Bs0, #ieval{level=Le}=Ieval0) -> Ieval1 = Ieval0#ieval{line=Line}, {As,Bs} = eval_list(As0, Bs0, Ieval1), trace(bif, {Le,Line,M,F,As}), - Ieval2 = dbg_istk:push(Bs0, Ieval1), + Ieval2 = dbg_istk:push(Bs0, Ieval1, false), Ieval = Ieval2#ieval{module=M,function=F,arguments=As}, {_,Value,_} = Res = debugged_cmd({apply,M,F,As}, Bs, Ieval), trace(return, {Le,Value}), @@ -872,7 +872,7 @@ expr({op,Line,Op,As0}, Bs0, Ieval0) -> end; %% apply/2 (fun) -expr({apply_fun,Line,Fun0,As0}, Bs0, #ieval{level=Le}=Ieval0) -> +expr({apply_fun,Line,Fun0,As0,Lc}, Bs0, #ieval{level=Le}=Ieval0) -> Ieval = Ieval0#ieval{line=Line}, FunValue = case expr(Fun0, Bs0, Ieval) of {value,{dbg_apply,Mx,Fx,Asx},Bsx} -> @@ -884,19 +884,19 @@ expr({apply_fun,Line,Fun0,As0}, Bs0, #ieval{level=Le}=Ieval0) -> case FunValue of {value,Fun,Bs1} when is_function(Fun) -> {As,Bs} = eval_list(As0, Bs1, Ieval), - eval_function(undefined, Fun, As, Bs, extern, Ieval); + eval_function(undefined, Fun, As, Bs, extern, Ieval, Lc); {value,{M,F},Bs1} when is_atom(M), is_atom(F) -> {As,Bs} = eval_list(As0, Bs1, Ieval), - eval_function(M, F, As, Bs, extern, Ieval); + eval_function(M, F, As, Bs, extern, Ieval, Lc); {value,BadFun,Bs1} -> exception(error, {badfun,BadFun}, Bs1, Ieval) end; %% apply/3 -expr({apply,Line,As0}, Bs0, Ieval0) -> +expr({apply,Line,As0,Lc}, Bs0, Ieval0) -> Ieval = Ieval0#ieval{line=Line}, {[M,F,As],Bs} = eval_list(As0, Bs0, Ieval), - eval_function(M, F, As, Bs, extern, Ieval); + eval_function(M, F, As, Bs, extern, Ieval, Lc); %% Receive statement expr({'receive',Line,Cs}, Bs0, #ieval{level=Le}=Ieval) -> diff --git a/lib/debugger/src/dbg_iload.erl b/lib/debugger/src/dbg_iload.erl index fde4e05128..b407cf6c99 100644 --- a/lib/debugger/src/dbg_iload.erl +++ b/lib/debugger/src/dbg_iload.erl @@ -154,14 +154,14 @@ get_nl([],Pos,Head) -> {lists:reverse(Head),[],Pos}. %%% to interpret. clauses([C0|Cs]) -> - C1 = clause(C0), + C1 = clause(C0, true), [C1|clauses(Cs)]; clauses([]) -> []. -clause({clause,Line,H0,G0,B0}) -> +clause({clause,Line,H0,G0,B0}, Lc) -> H1 = head(H0), G1 = guard(G0), - B1 = exprs(B0), + B1 = exprs(B0, Lc), {clause,Line,H1,G1,B1}. head(Ps) -> patterns(Ps). @@ -209,7 +209,7 @@ pattern({bin,Line,Grp}) -> {bin,Line,Grp1}; pattern({bin_element,Line,Expr,Size,Type}) -> Expr1 = pattern(Expr), - Size1 = expr(Size), + Size1 = expr(Size, false), {bin_element,Line,Expr1,Size1,Type}. %% These patterns are processed "in parallel" for purposes of variable @@ -317,173 +317,175 @@ gexpr_list([]) -> []. %% These expressions are processed "sequentially" for purposes of variable %% definition etc. -exprs([E0|Es]) -> - E1 = expr(E0), - [E1|exprs(Es)]; -exprs([]) -> []. - -expr({var,Line,V}) -> {var,Line,V}; -expr({integer,Line,I}) -> {value,Line,I}; -expr({char,Line,I}) -> {value,Line,I}; -expr({float,Line,F}) -> {value,Line,F}; -expr({atom,Line,A}) -> {value,Line,A}; -expr({string,Line,S}) -> {value,Line,S}; -expr({nil,Line}) -> {value,Line,[]}; -expr({cons,Line,H0,T0}) -> - case {expr(H0),expr(T0)} of +exprs([E], Lc) -> + [expr(E, Lc)]; +exprs([E0|Es], Lc) -> + E1 = expr(E0, false), + [E1|exprs(Es, Lc)]; +exprs([], _Lc) -> []. + +expr({var,Line,V}, _Lc) -> {var,Line,V}; +expr({integer,Line,I}, _Lc) -> {value,Line,I}; +expr({char,Line,I}, _Lc) -> {value,Line,I}; +expr({float,Line,F}, _Lc) -> {value,Line,F}; +expr({atom,Line,A}, _Lc) -> {value,Line,A}; +expr({string,Line,S}, _Lc) -> {value,Line,S}; +expr({nil,Line}, _Lc) -> {value,Line,[]}; +expr({cons,Line,H0,T0}, _Lc) -> + case {expr(H0, false),expr(T0, false)} of {{value,Line,H1},{value,Line,T1}} -> {value,Line,[H1|T1]}; {H1,T1} -> {cons,Line,H1,T1} end; -expr({tuple,Line,Es0}) -> +expr({tuple,Line,Es0}, _Lc) -> Es1 = expr_list(Es0), {tuple,Line,Es1}; -expr({block,Line,Es0}) -> +expr({block,Line,Es0}, Lc) -> %% Unfold block into a sequence. - Es1 = exprs(Es0), + Es1 = exprs(Es0, Lc), {block,Line,Es1}; -expr({'if',Line,Cs0}) -> - Cs1 = icr_clauses(Cs0), +expr({'if',Line,Cs0}, Lc) -> + Cs1 = icr_clauses(Cs0, Lc), {'if',Line,Cs1}; -expr({'case',Line,E0,Cs0}) -> - E1 = expr(E0), - Cs1 = icr_clauses(Cs0), +expr({'case',Line,E0,Cs0}, Lc) -> + E1 = expr(E0, false), + Cs1 = icr_clauses(Cs0, Lc), {'case',Line,E1,Cs1}; -expr({'receive',Line,Cs0}) -> - Cs1 = icr_clauses(Cs0), +expr({'receive',Line,Cs0}, Lc) -> + Cs1 = icr_clauses(Cs0, Lc), {'receive',Line,Cs1}; -expr({'receive',Line,Cs0,To0,ToEs0}) -> - To1 = expr(To0), - ToEs1 = exprs(ToEs0), - Cs1 = icr_clauses(Cs0), +expr({'receive',Line,Cs0,To0,ToEs0}, Lc) -> + To1 = expr(To0, false), + ToEs1 = exprs(ToEs0, Lc), + Cs1 = icr_clauses(Cs0, Lc), {'receive',Line,Cs1,To1,ToEs1}; -expr({'fun',Line,{clauses,Cs0},{_,_,Name}}) when is_atom(Name) -> +expr({'fun',Line,{clauses,Cs0},{_,_,Name}}, _Lc) when is_atom(Name) -> %% New R10B-2 format (abstract_v2). Cs = fun_clauses(Cs0), {make_fun,Line,Name,Cs}; -expr({'fun',Line,{clauses,Cs0},{_,_,_,_,Name}}) when is_atom(Name) -> +expr({'fun',Line,{clauses,Cs0},{_,_,_,_,Name}}, _Lc) when is_atom(Name) -> %% New R8 format (abstract_v2). Cs = fun_clauses(Cs0), {make_fun,Line,Name,Cs}; -expr({'fun',Line,{function,F,A},{_Index,_OldUniq,Name}}) -> +expr({'fun',Line,{function,F,A},{_Index,_OldUniq,Name}}, _Lc) -> %% New R8 format (abstract_v2). As = new_vars(A, Line), - Cs = [{clause,Line,As,[],[{local_call,Line,F,As}]}], + Cs = [{clause,Line,As,[],[{local_call,Line,F,As,true}]}], {make_fun,Line,Name,Cs}; -expr({'fun',_,{clauses,_},{_OldUniq,_Hvss,_Free}}) -> +expr({'fun',_,{clauses,_},{_OldUniq,_Hvss,_Free}}, _Lc) -> %% Old format (abstract_v1). exit({?MODULE,old_funs}); -expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,self}},[]}) -> +expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,self}},[]}, _Lc) -> {dbg,Line,self,[]}; -expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,get_stacktrace}},[]}) -> +expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,get_stacktrace}},[]}, _Lc) -> {dbg,Line,get_stacktrace,[]}; -expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,throw}},[_]=As}) -> +expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,throw}},[_]=As}, _Lc) -> {dbg,Line,throw,expr_list(As)}; -expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,error}},[_]=As}) -> +expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,error}},[_]=As}, _Lc) -> {dbg,Line,error,expr_list(As)}; -expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,exit}},[_]=As}) -> +expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,exit}},[_]=As}, _Lc) -> {dbg,Line,exit,expr_list(As)}; -expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,raise}},[_,_,_]=As}) -> +expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,raise}},[_,_,_]=As}, _Lc) -> {dbg,Line,raise,expr_list(As)}; -expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,apply}},[_,_,_]=As0}) -> +expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,apply}},[_,_,_]=As0}, Lc) -> As = expr_list(As0), - {apply,Line,As}; -expr({call,Line,{remote,_,{atom,_,Mod},{atom,_,Func}},As0}) -> + {apply,Line,As,Lc}; +expr({call,Line,{remote,_,{atom,_,Mod},{atom,_,Func}},As0}, Lc) -> As = expr_list(As0), case erlang:is_builtin(Mod, Func, length(As)) of false -> - {call_remote,Line,Mod,Func,As}; + {call_remote,Line,Mod,Func,As,Lc}; true -> case bif_type(Mod, Func, length(As0)) of safe -> {safe_bif,Line,Mod,Func,As}; unsafe ->{bif,Line,Mod,Func,As} end end; -expr({call,Line,{remote,_,Mod0,Func0},As0}) -> +expr({call,Line,{remote,_,Mod0,Func0},As0}, Lc) -> %% New R8 format (abstract_v2). - Mod = expr(Mod0), - Func = expr(Func0), + Mod = expr(Mod0, false), + Func = expr(Func0, false), As = consify(expr_list(As0)), - {apply,Line,[Mod,Func,As]}; -expr({call,Line,{atom,_,Func},As0}) -> + {apply,Line,[Mod,Func,As],Lc}; +expr({call,Line,{atom,_,Func},As0}, Lc) -> As = expr_list(As0), - {local_call,Line,Func,As}; -expr({call,Line,Fun0,As0}) -> - Fun = expr(Fun0), + {local_call,Line,Func,As,Lc}; +expr({call,Line,Fun0,As0}, Lc) -> + Fun = expr(Fun0, false), As = expr_list(As0), - {apply_fun,Line,Fun,As}; -expr({'catch',Line,E0}) -> + {apply_fun,Line,Fun,As,Lc}; +expr({'catch',Line,E0}, _Lc) -> %% No new variables added. - E1 = expr(E0), + E1 = expr(E0, false), {'catch',Line,E1}; -expr({'try',Line,Es0,CaseCs0,CatchCs0,As0}) -> +expr({'try',Line,Es0,CaseCs0,CatchCs0,As0}, Lc) -> %% No new variables added. Es = expr_list(Es0), - CaseCs = icr_clauses(CaseCs0), - CatchCs = icr_clauses(CatchCs0), + CaseCs = icr_clauses(CaseCs0, Lc), + CatchCs = icr_clauses(CatchCs0, Lc), As = expr_list(As0), {'try',Line,Es,CaseCs,CatchCs,As}; -expr({'query', Line, E0}) -> - E = expr(E0), +expr({'query', Line, E0}, _Lc) -> + E = expr(E0, false), {'query', Line, E}; -expr({lc,Line,E0,Gs0}) -> %R8. +expr({lc,Line,E0,Gs0}, _Lc) -> %R8. Gs = lists:map(fun ({generate,L,P0,Qs}) -> - {generate,L,expr(P0),expr(Qs)}; + {generate,L,expr(P0, false),expr(Qs, false)}; ({b_generate,L,P0,Qs}) -> %R12. - {b_generate,L,expr(P0),expr(Qs)}; + {b_generate,L,expr(P0, false),expr(Qs, false)}; (Expr) -> case is_guard(Expr) of true -> {guard,guard([[Expr]])}; - false -> expr(Expr) + false -> expr(Expr, false) end end, Gs0), - {lc,Line,expr(E0),Gs}; -expr({bc,Line,E0,Gs0}) -> %R12. + {lc,Line,expr(E0, false),Gs}; +expr({bc,Line,E0,Gs0}, _Lc) -> %R12. Gs = lists:map(fun ({generate,L,P0,Qs}) -> - {generate,L,expr(P0),expr(Qs)}; + {generate,L,expr(P0, false),expr(Qs, false)}; ({b_generate,L,P0,Qs}) -> %R12. - {b_generate,L,expr(P0),expr(Qs)}; + {b_generate,L,expr(P0, false),expr(Qs, false)}; (Expr) -> case is_guard(Expr) of true -> {guard,guard([[Expr]])}; - false -> expr(Expr) + false -> expr(Expr, false) end end, Gs0), - {bc,Line,expr(E0),Gs}; -expr({match,Line,P0,E0}) -> - E1 = expr(E0), + {bc,Line,expr(E0, false),Gs}; +expr({match,Line,P0,E0}, _Lc) -> + E1 = expr(E0, false), P1 = pattern(P0), {match,Line,P1,E1}; -expr({op,Line,Op,A0}) -> - A1 = expr(A0), +expr({op,Line,Op,A0}, _Lc) -> + A1 = expr(A0, false), {op,Line,Op,[A1]}; -expr({op,Line,'++',L0,R0}) -> - L1 = expr(L0), - R1 = expr(R0), %They see the same variables +expr({op,Line,'++',L0,R0}, _Lc) -> + L1 = expr(L0, false), + R1 = expr(R0, false), %They see the same variables {op,Line,append,[L1,R1]}; -expr({op,Line,'--',L0,R0}) -> - L1 = expr(L0), - R1 = expr(R0), %They see the same variables +expr({op,Line,'--',L0,R0}, _Lc) -> + L1 = expr(L0, false), + R1 = expr(R0, false), %They see the same variables {op,Line,subtract,[L1,R1]}; -expr({op,Line,'!',L0,R0}) -> - L1 = expr(L0), - R1 = expr(R0), %They see the same variables +expr({op,Line,'!',L0,R0}, _Lc) -> + L1 = expr(L0, false), + R1 = expr(R0, false), %They see the same variables {send,Line,L1,R1}; -expr({op,Line,Op,L0,R0}) when Op =:= 'andalso'; Op =:= 'orelse' -> - L1 = expr(L0), - R1 = expr(R0), %They see the same variables +expr({op,Line,Op,L0,R0}, _Lc) when Op =:= 'andalso'; Op =:= 'orelse' -> + L1 = expr(L0, false), + R1 = expr(R0, false), %They see the same variables {Op,Line,L1,R1}; -expr({op,Line,Op,L0,R0}) -> - L1 = expr(L0), - R1 = expr(R0), %They see the same variables +expr({op,Line,Op,L0,R0}, _Lc) -> + L1 = expr(L0, false), + R1 = expr(R0, false), %They see the same variables {op,Line,Op,[L1,R1]}; -expr({bin,Line,Grp}) -> +expr({bin,Line,Grp}, _Lc) -> Grp1 = expr_list(Grp), {bin,Line,Grp1}; -expr({bin_element,Line,Expr,Size,Type}) -> - Expr1 = expr(Expr), - Size1 = expr(Size), +expr({bin_element,Line,Expr,Size,Type}, _Lc) -> + Expr1 = expr(Expr, false), + Size1 = expr(Size, false), {bin_element,Line,Expr1,Size1,Type}; -expr(Other) -> +expr(Other, _Lc) -> exit({?MODULE,{unknown_expr,Other}}). %% is_guard(Expression) -> true | false. @@ -532,17 +534,17 @@ consify([]) -> {value,0,[]}. %% definition etc. expr_list([E0|Es]) -> - E1 = expr(E0), + E1 = expr(E0, false), [E1|expr_list(Es)]; expr_list([]) -> []. -icr_clauses([C0|Cs]) -> - C1 = clause(C0), - [C1|icr_clauses(Cs)]; -icr_clauses([]) -> []. +icr_clauses([C0|Cs], Lc) -> + C1 = clause(C0, Lc), + [C1|icr_clauses(Cs, Lc)]; +icr_clauses([], _) -> []. fun_clauses([{clause,L,H,G,B}|Cs]) -> - [{clause,L,head(H),guard(G),exprs(B)}|fun_clauses(Cs)]; + [{clause,L,head(H),guard(G),exprs(B, true)}|fun_clauses(Cs)]; fun_clauses([]) -> []. %% new_var_name() -> VarName. diff --git a/lib/debugger/src/dbg_istk.erl b/lib/debugger/src/dbg_istk.erl index a0d3069d50..21365fa4e9 100644 --- a/lib/debugger/src/dbg_istk.erl +++ b/lib/debugger/src/dbg_istk.erl @@ -18,7 +18,7 @@ %% -module(dbg_istk). -export([init/0,delayed_to_external/0,from_external/1, - push/2,pop/0,pop/1,stack_level/0, + 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]). @@ -61,8 +61,8 @@ init(Stack) -> %% 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,top=Lc}=Ieval) -> +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}, case get(trace_stack) of false -> -- cgit v1.2.3 From d6dc673a00f6244b03e1e9c849e3267b141c23c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 31 Mar 2011 08:40:36 +0200 Subject: Don't include tail-recursive calls in stacktraces The stacktrace in debugger-generated exceptions should be as similar to stacktraces in BEAM-generated exceptions as possible. --- lib/debugger/src/dbg_ieval.erl | 59 +++++++++++++++++++----------------------- lib/debugger/src/dbg_istk.erl | 7 +++-- 2 files changed, 32 insertions(+), 34 deletions(-) (limited to 'lib/debugger/src') diff --git a/lib/debugger/src/dbg_ieval.erl b/lib/debugger/src/dbg_ieval.erl index 251c11fdb9..70600121b3 100644 --- a/lib/debugger/src/dbg_ieval.erl +++ b/lib/debugger/src/dbg_ieval.erl @@ -403,6 +403,7 @@ eval_mfa(Debugged, M, F, As, #ieval{level=Le}=Ieval0) -> Ieval = Ieval0#ieval{level=Le+1,top=true}, try do_eval_function(M, F, As, Bs, extern, Ieval) of {value, Val, _Bs} -> + trace(return, {Le,Val}), {ready, Val} catch exit:{Debugged, Reason} -> @@ -414,38 +415,41 @@ eval_mfa(Debugged, M, F, As, #ieval{level=Le}=Ieval0) -> end. eval_function(Mod, Name, As, Bs, Called, Ieval0, Lc) -> - Ieval = dbg_istk:push(Bs, Ieval0, Lc), - Res = do_eval_function(Mod, Name, As, Bs, Called, Ieval), - dbg_istk:pop(), - Res. + Tail = Lc andalso get(trace_stack) =:= no_tail, + case Tail of + false -> + Ieval = dbg_istk:push(Bs, Ieval0, Lc), + {value,Val,_} = do_eval_function(Mod, Name, As, Bs, Called, Ieval), + dbg_istk:pop(), + trace(return, {Ieval#ieval.level,Val}), + {value,Val,Bs}; + true -> + do_eval_function(Mod, Name, As, Bs, Called, Ieval0) + end. -do_eval_function(Mod, Fun, As0, Bs0, _, Ieval) when is_function(Fun); +do_eval_function(Mod, Fun, As0, Bs0, _, Ieval0) when is_function(Fun); Mod =:= ?MODULE, Fun =:= eval_fun -> - #ieval{level=Le, line=Li, top=Top} = Ieval, + #ieval{level=Le,line=Li,top=Top} = Ieval0, case lambda(Fun, As0) of - {Cs,Module,Name,As,Bs} -> + {[{clause,Fc,_,_,_}|_]=Cs,Module,Name,As,Bs} -> + Ieval = Ieval0#ieval{module=Module,function=Name, + arguments=As0,line=Fc}, trace(call_fun, {Le,Li,Name,As}), - {value, Val, _Bs} = - fnk_clauses(Cs, Module, Name, As, Bs, Ieval), - trace(return, {Le,Val}), - {value, Val, Bs0}; + fnk_clauses(Cs, As, Bs, Ieval); not_interpreted when Top -> % We are leaving interpreted code trace(call_fun, {Le,Li,Fun,As0}), {value, {dbg_apply,erlang,apply,[Fun,As0]}, Bs0}; not_interpreted -> trace(call_fun, {Le,Li,Fun,As0}), - {value, Val, _Bs} = - debugged_cmd({apply,erlang,apply,[Fun,As0]}, Bs0, Ieval), - trace(return, {Le,Val}), - {value, Val, Bs0}; + debugged_cmd({apply,erlang,apply,[Fun,As0]}, Bs0, Ieval0); {error,Reason} -> %% It's ok not to push anything in this case, the error %% reason contains information about the culprit %% ({badarity,{{Mod,Name},As}}) - exception(error, Reason, Bs0, Ieval) + exception(error, Reason, Bs0, Ieval0) end; %% Common Test adaptation @@ -459,19 +463,12 @@ do_eval_function(Mod, Name, As0, Bs0, Called, Ieval0) -> Ieval = Ieval0#ieval{module=Mod,function=Name,arguments=As0}, case get_function(Mod, Name, As0, Called) of Cs when is_list(Cs) -> - {value, Val, _Bs} = - fnk_clauses(Cs, Mod, Name, As0, erl_eval:new_bindings(), - Ieval), - trace(return, {Le,Val}), - {value, Val, Bs0}; + fnk_clauses(Cs, As0, erl_eval:new_bindings(), Ieval); not_interpreted when Top -> % We are leaving interpreted code {value, {dbg_apply,Mod,Name,As0}, Bs0}; not_interpreted -> - {value, Val, _Bs} = - debugged_cmd({apply,Mod,Name,As0}, Bs0, Ieval), - trace(return, {Le,Val}), - {value, Val, Bs0}; + debugged_cmd({apply,Mod,Name,As0}, Bs0, Ieval); undef -> exception(error, undef, Bs0, Ieval, true) @@ -575,22 +572,20 @@ cached(Key) -> %% Try to find a matching function clause %% #ieval.level is set, the other fields must be set in this function -fnk_clauses([{clause,Line,Pars,Gs,Body}|Cs], M, F, As, Bs0, Ieval) -> +fnk_clauses([{clause,Line,Pars,Gs,Body}|Cs], As, Bs0, Ieval) -> case head_match(Pars, As, [], Bs0) of {match,Bs1} -> Bs = add_bindings(Bs1, Bs0), case guard(Gs, Bs) of true -> - seq(Body, Bs, - Ieval#ieval{line=Line, - module=M,function=F,arguments=As}); + seq(Body, Bs, Ieval#ieval{line=Line}); false -> - fnk_clauses(Cs, M, F, As, Bs0, Ieval) + fnk_clauses(Cs, As, Bs0, Ieval) end; nomatch -> - fnk_clauses(Cs, M, F, As, Bs0, Ieval) + fnk_clauses(Cs, As, Bs0, Ieval) end; -fnk_clauses([], _M, _F, _As, Bs, Ieval) -> +fnk_clauses([], _As, Bs, Ieval) -> exception(error, function_clause, Bs, Ieval, true). seq([E], Bs0, Ieval) -> diff --git a/lib/debugger/src/dbg_istk.erl b/lib/debugger/src/dbg_istk.erl index 21365fa4e9..92dc802da4 100644 --- a/lib/debugger/src/dbg_istk.erl +++ b/lib/debugger/src/dbg_istk.erl @@ -31,7 +31,8 @@ {level, %Level mfa, %{Mod,Func,Args|Arity}|{Fun,Args} line, %Line called from - bindings + bindings, + lc %Last call (true|false) }). init() -> @@ -63,7 +64,7 @@ init(Stack) -> 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}, + 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}; @@ -141,6 +142,8 @@ delayed_stacktrace(no_args, Ieval) -> [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 -> -- cgit v1.2.3 From 1774baaa3eb67727c56dd521859c9f27adf71a6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 24 Mar 2011 06:30:25 +0100 Subject: Remove support for very old BEAM files Since the run-time system cannot load those BEAM files, it was not possible to debug them anyway. --- lib/debugger/src/dbg_iload.erl | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) (limited to 'lib/debugger/src') diff --git a/lib/debugger/src/dbg_iload.erl b/lib/debugger/src/dbg_iload.erl index b407cf6c99..ce5631e45f 100644 --- a/lib/debugger/src/dbg_iload.erl +++ b/lib/debugger/src/dbg_iload.erl @@ -62,8 +62,10 @@ load_mod1(Mod, File, Binary, Db) -> store_module(Mod, File, Binary, Db) -> {interpreter_module, Exp, Abst, Src, MD5} = binary_to_term(Binary), Forms = case abstr(Abst) of - {abstract_v1,Forms0} -> Forms0; - {abstract_v2,Forms0} -> Forms0; + {abstract_v1,_} -> + exit({Mod,too_old_beam_file}); + {abstract_v2,_} -> + exit({Mod,too_old_beam_file}); {raw_abstract_v1,Code0} -> Code = interpret_file_attribute(Code0), {_,_,Forms0,_} = sys_pre_expand:module(Code, []), @@ -362,18 +364,11 @@ expr({'fun',Line,{clauses,Cs0},{_,_,Name}}, _Lc) when is_atom(Name) -> %% New R10B-2 format (abstract_v2). Cs = fun_clauses(Cs0), {make_fun,Line,Name,Cs}; -expr({'fun',Line,{clauses,Cs0},{_,_,_,_,Name}}, _Lc) when is_atom(Name) -> - %% New R8 format (abstract_v2). - Cs = fun_clauses(Cs0), - {make_fun,Line,Name,Cs}; expr({'fun',Line,{function,F,A},{_Index,_OldUniq,Name}}, _Lc) -> %% New R8 format (abstract_v2). As = new_vars(A, Line), Cs = [{clause,Line,As,[],[{local_call,Line,F,As,true}]}], {make_fun,Line,Name,Cs}; -expr({'fun',_,{clauses,_},{_OldUniq,_Hvss,_Free}}, _Lc) -> - %% Old format (abstract_v1). - exit({?MODULE,old_funs}); expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,self}},[]}, _Lc) -> {dbg,Line,self,[]}; expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,get_stacktrace}},[]}, _Lc) -> @@ -424,9 +419,6 @@ expr({'try',Line,Es0,CaseCs0,CatchCs0,As0}, Lc) -> CatchCs = icr_clauses(CatchCs0, Lc), As = expr_list(As0), {'try',Line,Es,CaseCs,CatchCs,As}; -expr({'query', Line, E0}, _Lc) -> - E = expr(E0, false), - {'query', Line, E}; expr({lc,Line,E0,Gs0}, _Lc) -> %R8. Gs = lists:map(fun ({generate,L,P0,Qs}) -> {generate,L,expr(P0, false),expr(Qs, false)}; -- cgit v1.2.3 From be04820c070d01d7565b936fa14efc2941055e0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 16 Feb 2011 06:54:15 +0100 Subject: emulator: Add a fourth element in exception stacktraces This commit is a preparation for introducing location information (filename/line number) in stacktraces in exceptions. Currently a stack trace looks like: [{Mod1,Function1,Arity1}, . . . {ModN,FunctionN,ArityN}] Add a forth element to each tuple that can be used indication the filename and line number of the source file: [{Mod1,Function1,Arity1,Location1}, . . . {ModN,FunctionN,ArityN,LocationN}] In this commit, the fourth element will just be an empty list, and we will change all code that look at or manipulate stacktraces. --- lib/debugger/src/dbg_debugged.erl | 2 +- lib/debugger/src/dbg_istk.erl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'lib/debugger/src') diff --git a/lib/debugger/src/dbg_debugged.erl b/lib/debugger/src/dbg_debugged.erl index 76ed49fa93..18dcd92ff3 100644 --- a/lib/debugger/src/dbg_debugged.erl +++ b/lib/debugger/src/dbg_debugged.erl @@ -117,5 +117,5 @@ demonitor(Mref) -> %% Fix stacktrace - keep all above call to this module. %% stacktrace_f([]) -> []; -stacktrace_f([{?MODULE,_,_}|_]) -> []; +stacktrace_f([{?MODULE,_,_,_}|_]) -> []; stacktrace_f([F|S]) -> [F|stacktrace_f(S)]. diff --git a/lib/debugger/src/dbg_istk.erl b/lib/debugger/src/dbg_istk.erl index 92dc802da4..e0c4c61333 100644 --- a/lib/debugger/src/dbg_istk.erl +++ b/lib/debugger/src/dbg_istk.erl @@ -157,9 +157,9 @@ stacktrace(_, _, Acc) -> lists:reverse(Acc). normalize(#e{mfa={_,Fun,As}}) when is_function(Fun) -> - {{Fun,length(As)},{Fun,As}}; + {{Fun,length(As),[]},{Fun,As,[]}}; normalize(#e{mfa={M,F,As}}) -> - {{M,F,length(As)},{M,F,As}}. + {{M,F,length(As),[]},{M,F,As,[]}}. %% bindings(SP) -> Bs %% SP = Le % stack pointer -- cgit v1.2.3 From 959a660d8867aa1690d70f656316db98ed8a504f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 24 Mar 2011 11:29:05 +0100 Subject: debugger: Include line numbers in exceptions --- lib/debugger/src/dbg_ieval.erl | 9 +++++---- lib/debugger/src/dbg_istk.erl | 32 +++++++++++++++++++++----------- 2 files changed, 26 insertions(+), 15 deletions(-) (limited to 'lib/debugger/src') diff --git a/lib/debugger/src/dbg_ieval.erl b/lib/debugger/src/dbg_ieval.erl index 70600121b3..df725ed9e5 100644 --- a/lib/debugger/src/dbg_ieval.erl +++ b/lib/debugger/src/dbg_ieval.erl @@ -462,8 +462,9 @@ do_eval_function(Mod, Name, As0, Bs0, Called, Ieval0) -> trace(call, {Called, {Le,Li,Mod,Name,As0}}), Ieval = Ieval0#ieval{module=Mod,function=Name,arguments=As0}, case get_function(Mod, Name, As0, Called) of - Cs when is_list(Cs) -> - fnk_clauses(Cs, As0, erl_eval:new_bindings(), Ieval); + [{clause,FcLine,_,_,_}|_]=Cs -> + fnk_clauses(Cs, As0, erl_eval:new_bindings(), + Ieval#ieval{line=FcLine}); not_interpreted when Top -> % We are leaving interpreted code {value, {dbg_apply,Mod,Name,As0}, Bs0}; @@ -836,7 +837,7 @@ expr({safe_bif,Line,M,F,As0}, Bs0, #ieval{level=Le}=Ieval0) -> {As,Bs} = eval_list(As0, Bs0, Ieval1), trace(bif, {Le,Line,M,F,As}), Ieval2 = dbg_istk:push(Bs0, Ieval1, false), - Ieval = Ieval2#ieval{module=M,function=F,arguments=As}, + Ieval = Ieval2#ieval{module=M,function=F,arguments=As,line=-1}, {_,Value,_} = Res = safe_bif(M, F, As, Bs, Ieval), trace(return, {Le,Value}), dbg_istk:pop(), @@ -848,7 +849,7 @@ expr({bif,Line,M,F,As0}, Bs0, #ieval{level=Le}=Ieval0) -> {As,Bs} = eval_list(As0, Bs0, Ieval1), trace(bif, {Le,Line,M,F,As}), Ieval2 = dbg_istk:push(Bs0, Ieval1, false), - Ieval = Ieval2#ieval{module=M,function=F,arguments=As}, + Ieval = Ieval2#ieval{module=M,function=F,arguments=As,line=-1}, {_,Value,_} = Res = debugged_cmd({apply,M,F,As}, Bs, Ieval), trace(return, {Le,Value}), dbg_istk:pop(), diff --git a/lib/debugger/src/dbg_istk.erl b/lib/debugger/src/dbg_istk.erl index e0c4c61333..c6922a80e4 100644 --- a/lib/debugger/src/dbg_istk.erl +++ b/lib/debugger/src/dbg_istk.erl @@ -120,26 +120,27 @@ delayed_stacktrace() -> Stack0 = get(?STACK), fun(NumEntries) -> Stack = stacktrace(NumEntries, Stack0, []), - [ArityOnly || {ArityOnly,_} <- Stack] + [finalize(ArityOnly) || {ArityOnly,_} <- Stack] end. delayed_stacktrace(include_args, Ieval) -> - #ieval{module=Mod,function=Name,arguments=As} = Ieval, - Stack0 = [#e{mfa={Mod,Name,As}}|get(?STACK)], + #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] -> - [WithArgs | [ArityOnly || {ArityOnly,_} <- Stack]] + [finalize(WithArgs) | + [finalize(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)], + #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, []), - [ArityOnly || {ArityOnly,_} <- Stack] + [finalize(ArityOnly) || {ArityOnly,_} <- Stack] end. stacktrace(N, [#e{lc=true}|T], Acc) -> @@ -156,10 +157,19 @@ stacktrace(N, [E|T], [{P,_}|_]=Acc) when N > 0 -> stacktrace(_, _, Acc) -> lists:reverse(Acc). -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,[]}}. +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 -- cgit v1.2.3 From 26cceb7a0718182e74083b4ad044985e8f624ee2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 16 Aug 2011 08:21:18 +0200 Subject: debugger: By default, only save non-tail-recursive calls By default, the debugger use to save all calls on its simulated stack. That could facilitate finding errors, but it could also mean that the Debugger could become very slow while executing tail-recursive code. --- lib/debugger/src/dbg_iserver.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/debugger/src') diff --git a/lib/debugger/src/dbg_iserver.erl b/lib/debugger/src/dbg_iserver.erl index 55ac1cef94..1bb73a43b9 100644 --- a/lib/debugger/src/dbg_iserver.erl +++ b/lib/debugger/src/dbg_iserver.erl @@ -114,7 +114,7 @@ init([]) -> process_flag(trap_exit, true), global:register_name(?MODULE, self()), Db = ets:new(?MODULE, [ordered_set, protected]), - {ok, #state{db=Db, auto=false, stack=all}}. + {ok, #state{db=Db, auto=false, stack=no_tail}}. %% Attaching to a process handle_call({attached, AttPid, Pid}, _From, State) -> -- cgit v1.2.3