diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index 758c84d1c1d7..5c5f9175a4e0 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 @@ -763,10 +766,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 59bb67ebc6a4..f3a8adfe4261 100644 --- a/erts/emulator/beam/beam_bif_load.c +++ b/erts/emulator/beam/beam_bif_load.c @@ -1251,6 +1251,103 @@ 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; + + if (_is_loader_x_reg(tp[1])) { + Uint xreg = loader_x_reg_index(tp[1]); + val = TUPLE2(hp, am_x, make_small(xreg)); + hp += 3; + } else if (_is_loader_y_reg(tp[1])) { + Uint yreg = loader_y_reg_index(tp[1]); + val = TUPLE2(hp, am_y, make_small(yreg)); + hp += 3; + } else { + val = tp[1]; + } + tmp = TUPLE2(hp, tp[0], val); + 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 798a7418d749..2299abfc37ae 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -805,3 +805,8 @@ bif erts_internal:trace_pattern/4 bif erts_internal:trace_info/3 bif erts_trace_cleaner:check/0 bif erts_trace_cleaner:send_trace_clean_signal/1 + +# +# New in 28. +# +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 17f1b194c5ec..0d74df9a0133 100644 --- a/lib/compiler/test/beam_debug_info_SUITE.erl +++ b/lib/compiler/test/beam_debug_info_SUITE.erl @@ -20,6 +20,8 @@ -module(beam_debug_info_SUITE). -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]). @@ -49,6 +51,8 @@ end_per_group(_GroupName, Config) -> Config. smoke(_Config) -> + {ok, Peer, Node} = ?CT_PEER(#{}), + TestBeams0 = get_unique_beam_files(), TestBeams = compiler_beams() ++ TestBeams0, @@ -62,12 +66,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]), @@ -81,7 +92,20 @@ do_smoke(Beam) -> [beam_debug_info,dexp,binary,report_errors]), SrcVars = source_variables(Abstr), IndexToFunctionMap = abstr_executable_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, + {DbgVars,DbgLiterals} = debug_info_vars(DebugInfo, IndexToFunctionMap), %% The debug information must only contain variables that are @@ -187,6 +211,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 dc8fc44a6a36..7bfa00becc35 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).