diff --git a/erts/emulator/beam/beam_code.h b/erts/emulator/beam/beam_code.h index 456d3cf2e84..7504f68b4e8 100644 --- a/erts/emulator/beam/beam_code.h +++ b/erts/emulator/beam/beam_code.h @@ -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 @@ -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 /* @@ -137,6 +143,20 @@ struct BeamCodeLineTab_ { const void** func_tab[1]; }; +/* + * Layout of the debug information. + */ +typedef struct { + Sint32 frame_size; + Uint32 num_vars; + Eterm *first; +} BeamDebugItem; + +struct BeamDebugTab_ { + Uint32 item_count; + BeamDebugItem *items; +}; + /* Total code size in bytes */ extern Uint erts_total_code_size; diff --git a/erts/emulator/beam/beam_file.c b/erts/emulator/beam/beam_file.c index 03f488ceb8e..f6557cf288e 100644 --- a/erts/emulator/beam/beam_file.c +++ b/erts/emulator/beam/beam_file.c @@ -668,6 +668,192 @@ static int parse_type_chunk(BeamFile *beam, IFF_Chunk *chunk) { } } +static int parse_debug_chunk_data(BeamFile *beam, BeamReader *p_reader) { + Sint32 count; + Sint32 total_num_vars; + int i; + BeamOpAllocator op_allocator; + BeamCodeReader *op_reader; + BeamOp* op = NULL; + BeamFile_DebugTable *debug = &beam->debug; + Eterm *tp; + byte *lp; + + LoadAssert(beamreader_read_i32(p_reader, &count)); + LoadAssert(beamreader_read_i32(p_reader, &total_num_vars)); + + beamopallocator_init(&op_allocator); + + op_reader = erts_alloc(ERTS_ALC_T_PREPARED_CODE, sizeof(BeamCodeReader)); + + op_reader->allocator = &op_allocator; + op_reader->file = beam; + op_reader->pending = NULL; + op_reader->first = 1; + op_reader->reader = *p_reader; + + if (count < 0 || total_num_vars < 0) { + goto error; + } + + /* For convenience of indexing, we will add a dummy zeroth entry. */ + count++; + + debug->item_count = count; + debug->term_count = 2 * total_num_vars; + debug->items = erts_alloc(ERTS_ALC_T_PREPARED_CODE, count * sizeof(BeamFile_DebugItem)); + debug->terms = erts_alloc(ERTS_ALC_T_PREPARED_CODE, 2 * total_num_vars * sizeof(Eterm)); + debug->is_literal = erts_alloc(ERTS_ALC_T_PREPARED_CODE, 2 * total_num_vars * sizeof(Eterm)); + + tp = debug->terms; + lp = debug->is_literal; + + /* Add dummy zeroth entry. */ + debug->items[0].frame_size = -1; + debug->items[0].first = tp; + debug->items[0].num_vars = 0; + + for (i = 1; i < count; i++) { + BeamOpArg *arg; + int extra_args; + Sint32 num_vars; + + if (!beamcodereader_next(op_reader, &op)) { + goto error; + } + if (op->op != genop_call_2) { + goto error; + } + + arg = op->a; + + /* Process frame size. */ + switch (arg->type) { + case TAG_n: + debug->items[i].frame_size = -1; + break; + case TAG_u: + debug->items[i].frame_size = arg[0].val; + break; + default: + goto error; + } + + arg++; + + /* Get and check the number of extra arguments. */ + if (arg->type != TAG_u) { + goto error; + } + extra_args = arg->val; + + arg++; + + /* Process the list of variable mappings. */ + + num_vars = extra_args / 2; + if (2 * num_vars != extra_args || num_vars > total_num_vars) { + goto error; + } + total_num_vars -= num_vars; + + debug->items[i].num_vars = num_vars; + debug->items[i].first = tp; + + while (extra_args > 0) { + Eterm var_name; + + if (arg[0].type != TAG_q) { + goto error; + } + + var_name = beamfile_get_literal(beam, arg[0].val); + if (!is_bitstring(var_name) || TAIL_BITS(bitstring_size(var_name))) { + goto error; + } + *tp++ = arg[0].val; + *lp++ = 1; + + *lp = 0; + switch (arg[1].type) { + case TAG_i: + *tp = make_small(arg[1].val); + break; + case TAG_a: + *tp = arg[1].val; + break; + case TAG_n: + *tp = NIL; + break; + case TAG_x: + *tp = make_loader_x_reg(arg[1].val); + break; + case TAG_y: + *tp = make_loader_y_reg(arg[1].val); + break; + case TAG_q: + *tp = arg[1].val; + *lp = 1; + break; + default: + goto error; + } + + tp++, lp++; + arg += 2; + extra_args -= 2; + } + + beamopallocator_free_op(&op_allocator, op); + op = NULL; + } + + if (total_num_vars != 0) { + goto error; + } + + beamcodereader_close(op_reader); + beamopallocator_dtor(&op_allocator); + + return 1; + + error: + if (op != NULL) { + beamopallocator_free_op(&op_allocator, op); + } + + beamcodereader_close(op_reader); + beamopallocator_dtor(&op_allocator); + + if (debug->items) { + erts_free(ERTS_ALC_T_PREPARED_CODE, debug->items); + debug->items = NULL; + } + + if (debug->terms) { + erts_free(ERTS_ALC_T_PREPARED_CODE, debug->terms); + debug->terms = NULL; + } + + return 0; +} + +static int parse_debug_chunk(BeamFile *beam, IFF_Chunk *chunk) { + BeamReader reader; + Sint32 version; + + beamreader_init(chunk->data, chunk->size, &reader); + + LoadAssert(beamreader_read_i32(&reader, &version)); + + if (version == 0) { + return parse_debug_chunk_data(beam, &reader); + } else { + /* Silently ignore chunk of wrong version. */ + return 1; + } +} + static ErlHeapFragment *new_literal_fragment(Uint size) { ErlHeapFragment *bp; @@ -912,6 +1098,7 @@ beamfile_read(const byte *data, size_t size, BeamFile *beam) { MakeIffId('A', 't', 'o', 'm'), /* 11 */ MakeIffId('T', 'y', 'p', 'e'), /* 12 */ MakeIffId('M', 'e', 't', 'a'), /* 13 */ + MakeIffId('D', 'b', 'g', 'B'), /* 14 */ }; static const int UTF8_ATOM_CHUNK = 0; @@ -930,6 +1117,7 @@ beamfile_read(const byte *data, size_t size, BeamFile *beam) { static const int OBSOLETE_ATOM_CHUNK = 11; static const int TYPE_CHUNK = 12; static const int META_CHUNK = 13; + static const int DEBUG_CHUNK = 14; static const int NUM_CHUNKS = sizeof(chunk_iffs) / sizeof(chunk_iffs[0]); @@ -1027,6 +1215,13 @@ beamfile_read(const byte *data, size_t size, BeamFile *beam) { init_fallback_type_table(beam); } + if (chunks[DEBUG_CHUNK].size > 0) { + if (!parse_debug_chunk(beam, &chunks[DEBUG_CHUNK])) { + error = BEAMFILE_READ_CORRUPT_DEBUG_TABLE; + goto error; + } + } + beam->strings.data = chunks[STR_CHUNK].data; beam->strings.size = chunks[STR_CHUNK].size; @@ -1167,6 +1362,16 @@ void beamfile_free(BeamFile *beam) { beam->types.entries = NULL; } + if (beam->debug.items) { + erts_free(ERTS_ALC_T_PREPARED_CODE, beam->debug.items); + erts_free(ERTS_ALC_T_PREPARED_CODE, beam->debug.terms); + erts_free(ERTS_ALC_T_PREPARED_CODE, beam->debug.is_literal); + + beam->debug.items = NULL; + beam->debug.terms = NULL; + beam->debug.is_literal = NULL; + } + if (beam->static_literals.entries) { beamfile_literal_dtor(&beam->static_literals); } diff --git a/erts/emulator/beam/beam_file.h b/erts/emulator/beam/beam_file.h index f32f9db2670..2d9e4b9bc24 100644 --- a/erts/emulator/beam/beam_file.h +++ b/erts/emulator/beam/beam_file.h @@ -150,6 +150,20 @@ typedef struct { BeamType *entries; } BeamFile_TypeTable; +typedef struct { + Sint32 frame_size; + Sint32 num_vars; + Eterm *first; +} BeamFile_DebugItem; + +typedef struct { + Sint32 item_count; + Sint32 term_count; + BeamFile_DebugItem *items; + Eterm *terms; + byte *is_literal; +} BeamFile_DebugTable; + typedef struct { IFF_File iff; @@ -166,6 +180,7 @@ typedef struct { BeamFile_LambdaTable lambdas; BeamFile_LineTable lines; BeamFile_TypeTable types; + BeamFile_DebugTable debug; /* Static literals are those defined in the file, and dynamic literals are * those created when loading. The former is positively indexed starting @@ -206,7 +221,8 @@ enum beamfile_read_result { BEAMFILE_READ_CORRUPT_LAMBDA_TABLE, BEAMFILE_READ_CORRUPT_LINE_TABLE, BEAMFILE_READ_CORRUPT_LITERAL_TABLE, - BEAMFILE_READ_CORRUPT_TYPE_TABLE + BEAMFILE_READ_CORRUPT_TYPE_TABLE, + BEAMFILE_READ_CORRUPT_DEBUG_TABLE }; typedef struct { diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index 945b84ba2e9..f2a91173182 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -170,6 +170,8 @@ erts_prepare_loading(Binary* magic, Process *c_p, Eterm group_leader, BeamLoadError0(stp, "corrupt locals table"); case BEAMFILE_READ_CORRUPT_TYPE_TABLE: BeamLoadError0(stp, "corrupt type table"); + case BEAMFILE_READ_CORRUPT_DEBUG_TABLE: + BeamLoadError0(stp, "corrupt BEAM debug information table"); case BEAMFILE_READ_SUCCESS: break; } diff --git a/erts/emulator/beam/jit/arm/instr_common.cpp b/erts/emulator/beam/jit/arm/instr_common.cpp index 397d5b88fe2..39c37e9de29 100644 --- a/erts/emulator/beam/jit/arm/instr_common.cpp +++ b/erts/emulator/beam/jit/arm/instr_common.cpp @@ -181,7 +181,7 @@ void BeamModuleAssembler::emit_validate(const ArgWord &Arity) { # ifdef JIT_HARD_DEBUG emit_enter_runtime_frame(); - for (unsigned i = 0; i < arity.get(); i++) { + for (unsigned i = 0; i < Arity.get(); i++) { mov_arg(ARG1, ArgVal(ArgVal::XReg, i)); emit_enter_runtime(); @@ -3173,3 +3173,9 @@ void BeamModuleAssembler::emit_coverage(void *coverage, Uint index, Uint size) { ASSERT(0); } } + +void BeamModuleAssembler::emit_debug_line(const ArgWord &Loc, + const ArgWord &Index, + const ArgWord &Live) { + emit_validate(Live); +} diff --git a/erts/emulator/beam/jit/arm/ops.tab b/erts/emulator/beam/jit/arm/ops.tab index 24562b7727c..78ea867675e 100644 --- a/erts/emulator/beam/jit/arm/ops.tab +++ b/erts/emulator/beam/jit/arm/ops.tab @@ -88,7 +88,7 @@ line I executable_line I I -debug_line u u u => _ +debug_line I I t allocate t t allocate_heap t I t diff --git a/erts/emulator/beam/jit/asm_load.c b/erts/emulator/beam/jit/asm_load.c index 88f471a3dd1..cab870250c9 100644 --- a/erts/emulator/beam/jit/asm_load.c +++ b/erts/emulator/beam/jit/asm_load.c @@ -705,6 +705,13 @@ int beam_load_emit_op(LoaderState *stp, BeamOp *tmp_op) { } break; } + case op_debug_line_IIt: + /* We'll save some memory by not inserting a debug_line entry that + * is equal to the previous one. */ + if (add_line_entry(stp, tmp_op->a[0].val, 0)) { + goto load_error; + } + break; case op_int_code_end: /* End of code found. */ if (stp->function_number != stp->beam.code.function_count) { @@ -858,6 +865,57 @@ static const BeamCodeLineTab *finish_line_table(LoaderState *stp, return line_tab_ro; } +static const BeamDebugTab *finish_debug_table(LoaderState *stp, + char *module_base, + size_t module_size) { + BeamFile_DebugTable *debug = &stp->beam.debug; + const BeamDebugTab *debug_tab_ro; + byte *debug_tab_rw_base; + BeamDebugTab *debug_tab_top; + Eterm *debug_tab_terms; + BeamDebugItem *debug_tab_items; + Uint item_count = (Uint)debug->item_count; + Uint term_count = (Uint)debug->term_count; + Uint i; + + if (item_count == 0) { + return NULL; + } + + debug_tab_ro = (const BeamDebugTab *)beamasm_get_rodata(stp->ba, "debug"); + debug_tab_rw_base = get_writable_ptr(stp->executable_region, + stp->writable_region, + debug_tab_ro); + debug_tab_top = (BeamDebugTab *)debug_tab_rw_base; + debug_tab_terms = (Eterm *)(debug_tab_top + 1); + debug_tab_items = (BeamDebugItem *)(debug_tab_terms + term_count); + + debug_tab_top->item_count = debug->item_count; + debug_tab_top->items = debug_tab_items; + + for (i = 0; i < term_count; i++) { + if (debug->is_literal[i]) { + ASSERT(debug->is_literal[i] == 1); + debug_tab_terms[i] = + beamfile_get_literal(&stp->beam, debug->terms[i]); + } else { + ASSERT(debug->is_literal[i] == 0); + debug_tab_terms[i] = debug->terms[i]; + } + } + + for (i = 0; i < item_count; i++) { + Uint num_vars = (Uint)debug->items[i].num_vars; + + debug_tab_items[i].frame_size = debug->items[i].frame_size; + debug_tab_items[i].num_vars = num_vars; + debug_tab_items[i].first = debug_tab_terms; + debug_tab_terms += 2 * num_vars; + } + + return debug_tab_ro; +} + int beam_load_finish_emit(LoaderState *stp) { const BeamCodeHeader *code_hdr_ro = NULL; BeamCodeHeader *code_hdr_rw = NULL; @@ -887,6 +945,17 @@ int beam_load_finish_emit(LoaderState *stp) { beamasm_embed_bss(stp->ba, "line", line_size); } + /* Calculate size of the load BEAM debug information. */ + if (stp->beam.debug.item_count > 0) { + BeamFile_DebugTable *debug = &stp->beam.debug; + Uint debug_size; + + debug_size = sizeof(BeamDebugTab); + debug_size += (Uint)debug->item_count * sizeof(BeamFile_DebugItem); + debug_size += (Uint)debug->term_count * sizeof(Eterm); + beamasm_embed_bss(stp->ba, "debug", debug_size); + } + /* Place the string table and, optionally, attributes here. */ beamasm_embed_rodata(stp->ba, "str", @@ -977,6 +1046,10 @@ int beam_load_finish_emit(LoaderState *stp) { * names are literal lists. */ code_hdr_rw->line_table = finish_line_table(stp, module_base, module_size); + /* Debug information must be added after moving literals, since literals + * are used extensively. */ + code_hdr_rw->debug = finish_debug_table(stp, module_base, module_size); + if (stp->beam.attributes.size) { const byte *attr = beamasm_get_rodata(stp->ba, "attr"); diff --git a/erts/emulator/beam/jit/x86/instr_common.cpp b/erts/emulator/beam/jit/x86/instr_common.cpp index d722a19d373..9c4483ce257 100644 --- a/erts/emulator/beam/jit/x86/instr_common.cpp +++ b/erts/emulator/beam/jit/x86/instr_common.cpp @@ -3316,3 +3316,9 @@ void BeamModuleAssembler::emit_coverage(void *coverage, Uint index, Uint size) { ASSERT(0); } } + +void BeamModuleAssembler::emit_debug_line(const ArgWord &Loc, + const ArgWord &Index, + const ArgWord &Live) { + emit_validate(Live); +} diff --git a/erts/emulator/beam/jit/x86/ops.tab b/erts/emulator/beam/jit/x86/ops.tab index 0a1f871b968..a618c56e7c3 100644 --- a/erts/emulator/beam/jit/x86/ops.tab +++ b/erts/emulator/beam/jit/x86/ops.tab @@ -88,7 +88,7 @@ line I executable_line I I -debug_line u u u => _ +debug_line I I t allocate t t allocate_heap t I t