Skip to content

Commit

Permalink
Add code:get_debug_info/1
Browse files Browse the repository at this point in the history
Provided that module Module contains debug information,
code:get_debug_info(Module) will read out a symbolic representation
of it.

The following example has been annotated with the index operand for
the `debug_line` instruction preceding each line in the source
code:

    foo(A) ->
        case A of                        % 1
            0 ->
                B = 1,                   % 2
                io:format("~p\n", [B]);  % 3
            1 ->
                C = [1,2,3],    % 4
                io:format("~p\n", [C])   % 4
        end,
        A.                               % 6

Here follows the dump of the debug information:

    1> code:get_debug_info(t).
     {1,{none,[{<<"A">>,{x,0}}]}},
     {2,{1,[{<<"A">>,{y,0}}]}},
     {3,{1,[{<<"B">>,{value,1}},{<<"A">>,{y,0}}]}},
     {4,{1,[{<<"A">>,{y,0}}]}},
     {5,{1,[{<<"C">>,{value,[1,2,3]}},{<<"A">>,{y,0}}]}},
     {6,{1,[{<<"A">>,{y,0}}]}}]
  • Loading branch information
bjorng committed Oct 10, 2024
1 parent 98675b1 commit ec31979
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 7 deletions.
8 changes: 5 additions & 3 deletions erts/emulator/beam/atom.names
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -486,6 +488,7 @@ atom new_processes
atom new_ports
atom new_uniq
atom newline
atom nifs
atom no
atom nomatch
atom none
Expand Down Expand Up @@ -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
102 changes: 102 additions & 0 deletions erts/emulator/beam/beam_bif_load.c
Original file line number Diff line number Diff line change
Expand Up @@ -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...
*
Expand Down
3 changes: 2 additions & 1 deletion erts/emulator/beam/bif.tab
Original file line number Diff line number Diff line change
Expand Up @@ -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
56 changes: 54 additions & 2 deletions lib/compiler/test/beam_debug_info_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,

Expand All @@ -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]),
Expand All @@ -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),

Expand Down Expand Up @@ -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
Expand Down
10 changes: 9 additions & 1 deletion lib/kernel/src/code.erl
Original file line number Diff line number Diff line change
Expand Up @@ -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"}).
Expand Down Expand Up @@ -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).

0 comments on commit ec31979

Please sign in to comment.