Skip to content

Commit

Permalink
Implement support for debug information in the runtime system
Browse files Browse the repository at this point in the history
This commit implements support for loading the debug information from
a BEAM file and the `code:get_debug_info/1` BIF for retrieving the
debug information.

As an example, given the following module:

    -module(example).                               %  1
    -export([foo/1]).                               %  2

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

here is how to compile it with BEAM debug information and
display the debug information:

     1> c(example, beam_debug_info).
     {ok,example}
     2> code:get_debug_info(example).
     [{5,{none,[{<<"A">>,{x,0}}]}},
      {7,{1,[{<<"A">>,{y,0}}]}},
      {8,{1,[{<<"B">>,{value,1}},{<<"A">>,{y,0}}]}},
      {10,{1,[{<<"A">>,{y,0}}]}},
      {11,{1,[{<<"C">>,{value,[1,2,3]}},{<<"A">>,{y,0}}]}},
      {13,{1,[{<<"A">>,{y,0}}]}}]
  • Loading branch information
bjorng committed Nov 13, 2024
1 parent d82842b commit 04e1986
Show file tree
Hide file tree
Showing 14 changed files with 567 additions and 15 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 @@ -766,10 +769,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
119 changes: 119 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,125 @@ 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 BeamCodeLineTab* lt;
const BeamDebugTab* debug;
Sint 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);
}

lt = hdr->line_table;

debug = hdr->debug;
if (debug == NULL) {
return am_none;
}

alloc_size = 0;

for (i = 0; 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];
Uint32 location_index;
Sint frame_size = items->frame_size;
Uint32 location;
Uint num_vars = items->num_vars;
Eterm *tp = items->first + 2 * num_vars - 2;
Eterm frame_size_term;
Eterm var_list = NIL;
Eterm tmp;

location_index = items->location_index;

if (location_index == (Uint32)-1) {
continue;
}
if (lt->loc_size == 2) {
location = lt->loc_tab.p2[location_index];
} else {
ASSERT(lt->loc_size == 4);
location = lt->loc_tab.p4[location_index];
}

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(LOC_LINE(location)), 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
21 changes: 21 additions & 0 deletions erts/emulator/beam/beam_code.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
#define MD5_SIZE MD5_DIGEST_LENGTH

typedef struct BeamCodeLineTab_ BeamCodeLineTab;
typedef struct BeamDebugTab_ BeamDebugTab;

/*
* Header of code chunks which contains additional information
Expand Down Expand Up @@ -99,6 +100,11 @@ typedef struct beam_code_header {
Uint32 *loc_index_to_cover_id;
Uint line_coverage_len;

/*
* Debug information. debug->items are indexed directly by
* the index in each `debug_line` instruction.
*/
const BeamDebugTab *debug;
#endif

/*
Expand Down Expand Up @@ -137,6 +143,21 @@ struct BeamCodeLineTab_ {
const void** func_tab[1];
};

/*
* Layout of the debug information.
*/
typedef struct {
Uint32 location_index;
Sint16 frame_size;
Uint16 num_vars;
Eterm *first;
} BeamDebugItem;

struct BeamDebugTab_ {
Uint32 item_count;
BeamDebugItem *items;
};

/* Total code size in bytes */
extern Uint erts_total_code_size;

Expand Down
Loading

0 comments on commit 04e1986

Please sign in to comment.