diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index ff5f2c1303a..65fc679ac57 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -111,6 +111,7 @@ atom asynchronous atom atom atom atom_used atom attributes +atom auto atom auto_connect atom await_exit atom await_microstate_accounting_modifications @@ -217,6 +218,7 @@ atom debug_flags atom decentralized_counters atom decimals atom default +atom debug_hash_fixed_number_of_locks atom delay_trap atom demonitor atom deterministic @@ -486,6 +488,7 @@ atom new_processes atom new_ports atom new_uniq atom newline +atom nifs atom no atom nomatch atom none @@ -765,10 +768,9 @@ atom warning atom warning_msg atom wordsize atom write_concurrency +atom x atom xor atom x86 +atom y atom yes atom yield -atom nifs -atom auto -atom debug_hash_fixed_number_of_locks diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c index 59bb67ebc6a..367d0958a3b 100644 --- a/erts/emulator/beam/beam_bif_load.c +++ b/erts/emulator/beam/beam_bif_load.c @@ -1251,6 +1251,108 @@ any_heap_refs(Eterm* start, Eterm* end, char* mod_start, Uint mod_size) return 0; } +BIF_RETTYPE code_get_debug_info_1(BIF_ALIST_1) +{ +#ifdef BEAMASM + ErtsCodeIndex code_ix; + Module* modp; + const BeamCodeHeader* hdr; + const BeamDebugTab* debug; + Uint i; + Uint alloc_size; + Eterm result = NIL; + Eterm* hp; + Eterm* hend; + + if (is_not_atom(BIF_ARG_1)) { + BIF_ERROR(BIF_P, BADARG); + } + code_ix = erts_active_code_ix(); + modp = erts_get_module(BIF_ARG_1, code_ix); + if (modp == NULL) { + BIF_ERROR(BIF_P, BADARG); + } + hdr = modp->curr.code_hdr; + if (hdr == NULL) { + BIF_ERROR(BIF_P, BADARG); + } + + debug = hdr->debug; + if (debug == NULL) { + return am_none; + } + + alloc_size = 0; + + for (i = 1; i < debug->item_count; i++) { + /* [ {Index, {FrameSize,[{Name,Value}]} ] */ + alloc_size += 2 + 3 + 3 + debug->items[i].num_vars * (2 + 3 + 3); + } + + hp = HAlloc(BIF_P, alloc_size); + hend = hp + alloc_size; + + for (i = debug->item_count-1; i > 0; i--) { + BeamDebugItem* items = &debug->items[i]; + Sint32 frame_size = items->frame_size; + Uint num_vars = items->num_vars; + Eterm *tp = items->first + 2 * num_vars - 2; + Eterm frame_size_term; + Eterm var_list = NIL; + Eterm tmp; + + if (frame_size < 0) { + frame_size_term = am_none; + } else { + frame_size_term = make_small(frame_size); + } + + while (num_vars-- != 0) { + Eterm val; + Eterm tag; + + if (_is_loader_x_reg(tp[1])) { + Uint xreg = loader_x_reg_index(tp[1]); + tag = am_x; + val = make_small(xreg); + } else if (_is_loader_y_reg(tp[1])) { + Uint yreg = loader_y_reg_index(tp[1]); + tag = am_y; + val = make_small(yreg); + } else { + tag = am_value; + val = tp[1]; + } + tmp = TUPLE2(hp, tag, val); + hp += 3; + + tmp = TUPLE2(hp, tp[0], tmp); + hp += 3; + + tp -= 2; + + var_list = CONS(hp, tmp, var_list); + hp += 2; + } + + tmp = TUPLE2(hp, frame_size_term, var_list); + hp += 3; + + tmp = TUPLE2(hp, make_small(i), tmp); + hp += 3; + + result = CONS(hp, tmp, result); + hp += 2; + } + + ASSERT(hp <= hend); + HRelease(BIF_P, hend, hp); + return result; +#endif + + BIF_ERROR(BIF_P, BADARG); +} + /* * Release of literal areas... * diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index 41998c51e17..a1d916266b0 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -803,7 +803,8 @@ bif erts_trace_cleaner:check/0 bif erts_trace_cleaner:send_trace_clean_signal/1 # -# New in 28 +# New in 28. # bif erts_internal:system_monitor/1 bif erts_internal:system_monitor/3 +bif code:get_debug_info/1 diff --git a/lib/compiler/test/beam_debug_info_SUITE.erl b/lib/compiler/test/beam_debug_info_SUITE.erl index 5d8e3392005..dd645f614f3 100644 --- a/lib/compiler/test/beam_debug_info_SUITE.erl +++ b/lib/compiler/test/beam_debug_info_SUITE.erl @@ -26,6 +26,8 @@ -include("beam_opcodes.hrl"). +-include_lib("common_test/include/ct.hrl"). + -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, smoke/1, @@ -58,6 +60,8 @@ end_per_group(_GroupName, Config) -> Config. smoke(_Config) -> + {ok, Peer, Node} = ?CT_PEER(#{}), + TestBeams0 = get_unique_beam_files(), TestBeams = compiler_beams() ++ TestBeams0, @@ -74,12 +78,19 @@ smoke(_Config) -> """, io:put_chars(S), - test_lib:p_run(fun do_smoke/1, TestBeams). + test_lib:p_run(fun(Beam) -> + do_smoke(Beam, Node) + end, TestBeams), + + peer:stop(Peer), + + ok. + compiler_beams() -> filelib:wildcard(filename:join([code:lib_dir(compiler), "ebin", "*.beam"])). -do_smoke(Beam) -> +do_smoke(Beam, Node) -> try {ok,{Mod,[{abstract_code,{raw_abstract_v1,Abstr0}}]}} = beam_lib:chunks(Beam, [abstract_code]), @@ -93,7 +104,27 @@ do_smoke(Beam) -> [beam_debug_info,dexp,binary,report_errors]), SrcVars = source_variables(Abstr), IndexToFunctionMap = abstr_debug_lines(Abstr), + + %% Retrieve the debug information in two different ways. DebugInfo = get_debug_info(Mod, Code), + DebugInfoBif = load_get_debug_info(Node, Mod, Code), + + if + DebugInfo =:= DebugInfoBif -> + ok; + true -> + io:format("~p\n", [DebugInfo]), + io:format("~p\n", [DebugInfoBif]), + error(inconsistent_debug_info) + end, + + case Mod of + ?MODULE -> + %% This module has been compiled with `beam_debug_info`. + DebugInfo = code:get_debug_info(Mod); + _ -> + ok + end, {DbgVars,DbgLiterals} = debug_info_vars(DebugInfo, IndexToFunctionMap), @@ -198,6 +229,27 @@ family_difference(F0, F1) -> S3 = sofs:family_specification(SpecFun, S2), sofs:to_external(S3). +%% Load a module on a remote node and retrieve debug information. +load_get_debug_info(Node, Mod, Beam) -> + erpc:call(Node, + fun() -> + {module,Mod} = code:load_binary(Mod, "", Beam), + DebugInfo = code:get_debug_info(Mod), + + case Mod of + ?MODULE -> + %% Don't purge the module that this fun + %% is located in. + ok; + _ -> + %% Smoke test of purging a module with + %% debug information. + _ = code:delete(Mod), + _ = code:purge(Mod) + end, + DebugInfo + end). + %% %% Extract variables mentioned in the source code. Try to remove %% variables that will never show up in the debug information; for diff --git a/lib/kernel/src/code.erl b/lib/kernel/src/code.erl index dc8fc44a6a3..7bfa00becc3 100644 --- a/lib/kernel/src/code.erl +++ b/lib/kernel/src/code.erl @@ -401,7 +401,8 @@ common reasons. module_status/0, module_status/1, modified_modules/0, - get_mode/0]). + get_mode/0, + get_debug_info/1]). -removed({rehash,0,"the code path cache feature has been removed"}). -removed({is_module_native,1,"HiPE has been removed"}). @@ -2321,3 +2322,10 @@ _See also:_ [Native Coverage Support](#module-native-coverage-support) Supported :: boolean(). coverage_support() -> erlang:nif_error(undefined). + +-doc(#{since => <<"OTP 28.0">>}). +-spec get_debug_info(Module) -> Mode when + Module :: module(), + Mode :: coverage_mode(). +get_debug_info(_Module) -> + erlang:nif_error(undefined).