diff options
author | Björn Gustavsson <bjorn@erlang.org> | 2021-02-20 09:27:28 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-02-20 09:27:28 +0100 |
commit | c056ac98882b0559f142cada1a8be940bb73edcf (patch) | |
tree | 147405278847b3392eff00da4309511629a92b03 | |
parent | 870cb247fb3099fc1870b53be29272b143c4059c (diff) | |
parent | fba5a42890990a01aca764750ebb6a4913cbc24c (diff) | |
download | erlang-c056ac98882b0559f142cada1a8be940bb73edcf.tar.gz |
Merge pull request #4535 from bjorng/bjorn/elixir-abstract-code/GH-4353/OTP-17194
beam_lib, cover: Don't crash when an abstract code backend is missing
-rw-r--r-- | lib/stdlib/src/beam_lib.erl | 11 | ||||
-rw-r--r-- | lib/stdlib/test/beam_lib_SUITE.erl | 28 | ||||
-rw-r--r-- | lib/tools/doc/src/cover.xml | 3 | ||||
-rw-r--r-- | lib/tools/src/cover.erl | 22 | ||||
-rw-r--r-- | lib/tools/test/cover_SUITE.erl | 31 |
5 files changed, 80 insertions, 15 deletions
diff --git a/lib/stdlib/src/beam_lib.erl b/lib/stdlib/src/beam_lib.erl index 0a6dc74bd0..23413f43b1 100644 --- a/lib/stdlib/src/beam_lib.erl +++ b/lib/stdlib/src/beam_lib.erl @@ -107,6 +107,7 @@ -type chnk_rsn() :: {'unknown_chunk', file:filename(), atom()} | {'key_missing_or_invalid', file:filename(), 'abstract_code' | 'debug_info'} + | {'missing_backend', file:filename(), module()} | info_rsn(). -type cmp_rsn() :: {'modules_different', module(), module()} | {'chunks_different', chunkid()} @@ -310,6 +311,9 @@ format_error(badfun) -> "not a fun or the fun has the wrong arity"; format_error(exists) -> "a fun has already been installed"; +format_error({missing_backend, File, Backend}) -> + io_lib:format("~tp: Cannot retrieve abstract code because the backend ~p is missing", + [File, Backend]); format_error(E) -> io_lib:format("~tp~n", [E]). @@ -682,10 +686,13 @@ chunks_to_data([{abst_chunk, Name} | CNs], Chunks, File, Cs, Module, Atoms, L) - {NewAtoms, Ret} = case catch chunk_to_data(debug_info, DbgiChunk, File, Cs, Atoms, Module) of {DbgiAtoms, {debug_info, {debug_info_v1, Backend, Metadata}}} -> - case Backend:debug_info(erlang_v1, Module, Metadata, []) of + try Backend:debug_info(erlang_v1, Module, Metadata, []) of {ok, Code} -> {DbgiAtoms, {abstract_code, {raw_abstract_v1, Code}}}; {error, _} -> {DbgiAtoms, {abstract_code, no_abstract_code}} - end; + catch + error:undef -> + error({missing_backend,File,Backend}) + end; {error,beam_lib,{key_missing_or_invalid,Path,debug_info}} -> error({key_missing_or_invalid,Path,abstract_code}); _ -> diff --git a/lib/stdlib/test/beam_lib_SUITE.erl b/lib/stdlib/test/beam_lib_SUITE.erl index 9ef85a15e0..e28301ec9e 100644 --- a/lib/stdlib/test/beam_lib_SUITE.erl +++ b/lib/stdlib/test/beam_lib_SUITE.erl @@ -36,7 +36,8 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, normal/1, error/1, cmp/1, cmp_literals/1, strip/1, strip_add_chunks/1, otp_6711/1, - building/1, md5/1, encrypted_abstr/1, encrypted_abstr_file/1]). + building/1, md5/1, encrypted_abstr/1, encrypted_abstr_file/1, + missing_debug_info_backend/1]). -export([init_per_testcase/2, end_per_testcase/2]). @@ -46,7 +47,7 @@ suite() -> all() -> [error, normal, cmp, cmp_literals, strip, strip_add_chunks, otp_6711, - building, md5, encrypted_abstr, encrypted_abstr_file]. + building, md5, encrypted_abstr, encrypted_abstr_file, missing_debug_info_backend]. groups() -> []. @@ -775,6 +776,29 @@ write_crypt_file(Contents0) -> io:format("~s\n", [binary_to_list(Contents)]), ok = file:write_file(".erlang.crypt", Contents). +%% GH-4353: Don't crash when the backend for generating the abstract code +%% is missing. +missing_debug_info_backend(Conf) -> + PrivDir = ?privdir, + Simple = filename:join(PrivDir, "simple"), + Source = Simple ++ ".erl", + BeamFile = Simple ++ ".beam", + simple_file(Source), + + %% Create a debug_info chunk with a non-existing backend. + {ok,simple} = compile:file(Source, [{outdir,PrivDir}]), + {ok,simple,All0} = beam_lib:all_chunks(BeamFile), + FakeBackend = definitely__not__an__existing__backend, + FakeDebugInfo = {debug_info_v1, FakeBackend, nothing_here}, + All = lists:keyreplace("Dbgi", 1, All0, {"Dbgi", term_to_binary(FakeDebugInfo)}), + {ok,NewBeam} = beam_lib:build_module(All), + ok = file:write_file(BeamFile, NewBeam), + + %% beam_lib should not crash, but return an error. + verify(missing_backend, beam_lib:chunks(BeamFile, [abstract_code])), + + ok. + compare_chunks(File1, File2, ChunkIds) -> {ok, {_, Chunks1}} = beam_lib:chunks(File1, ChunkIds), {ok, {_, Chunks2}} = beam_lib:chunks(File2, ChunkIds), diff --git a/lib/tools/doc/src/cover.xml b/lib/tools/doc/src/cover.xml index 8073bfc528..0889a16f65 100644 --- a/lib/tools/doc/src/cover.xml +++ b/lib/tools/doc/src/cover.xml @@ -217,9 +217,10 @@ <v>ModFiles = ModFile | [ModFile]</v> <v>ModFile = Module | BeamFile</v> <v> Module = atom()</v> + <v> BackendModule = atom()</v> <v> BeamFile = string()</v> <v>Result = {ok,Module} | {error,BeamFile} | {error,Reason}</v> - <v> Reason = non_existing | {no_abstract_code,BeamFile} | {encrypted_abstract_code,BeamFile} | {already_cover_compiled,no_beam_found,Module} | not_main_node</v> + <v> Reason = non_existing | {no_abstract_code,BeamFile} | {{missing_backend,BackendModule},BeamFile} | {encrypted_abstract_code,BeamFile} | {already_cover_compiled,no_beam_found,Module} | not_main_node</v> </type> <desc> <p>Does the same as <c>compile/1,2</c>, but uses an existing diff --git a/lib/tools/src/cover.erl b/lib/tools/src/cover.erl index 9f86a68942..d1781c7b08 100644 --- a/lib/tools/src/cover.erl +++ b/lib/tools/src/cover.erl @@ -1552,14 +1552,12 @@ do_compile2(File, UserOptions, LocalOnly) -> do_compile_beam1(Module,Beam,UserOptions,LocalOnly) -> %% Clear database do_clear(Module), - + %% Extract the abstract format. case get_abstract_code(Module, Beam) of - no_abstract_code=E -> - {error,E}; - encrypted_abstract_code=E -> - {error,E}; - {raw_abstract_v1,Code} -> + {error,_}=Error -> + Error; + {ok,{raw_abstract_v1,Code}} -> Forms0 = epp:interpret_file_attribute(Code), case find_main_filename(Forms0) of {ok,MainFile} -> @@ -1568,7 +1566,7 @@ do_compile_beam1(Module,Beam,UserOptions,LocalOnly) -> Error -> Error end; - {_VSN,_Code} -> + {ok,{_VSN,_Code}} -> %% Wrong version of abstract code. Just report that there %% is no abstract code. {error,no_abstract_code} @@ -1577,10 +1575,14 @@ do_compile_beam1(Module,Beam,UserOptions,LocalOnly) -> get_abstract_code(Module, Beam) -> case beam_lib:chunks(Beam, [abstract_code]) of {ok, {Module, [{abstract_code, AbstractCode}]}} -> - AbstractCode; + case AbstractCode of + no_abstract_code=E -> {error, E}; + _ -> {ok,AbstractCode} + end; {error,beam_lib,{key_missing_or_invalid,_,_}} -> - encrypted_abstract_code; - Error -> Error + {error,encrypted_abstract_code}; + {error,beam_lib,{missing_backend,_,Backend}} -> + {error,{missing_backend,Backend}} end. do_compile_beam2(Module,Beam,UserOptions,Forms0,MainFile,LocalOnly) -> diff --git a/lib/tools/test/cover_SUITE.erl b/lib/tools/test/cover_SUITE.erl index f4c3a0e3b6..c12ea581e2 100644 --- a/lib/tools/test/cover_SUITE.erl +++ b/lib/tools/test/cover_SUITE.erl @@ -31,6 +31,7 @@ all() -> NoStartStop = [eif,otp_5305,otp_5418,otp_7095,otp_8273, otp_8340,otp_8188,compile_beam_opts,eep37, analyse_no_beam, line_0, compile_beam_no_file, + compile_beam_missing_backend, otp_13277, otp_13289], StartStop = [start, compile, analyse, misc, stop, distribution, reconnect, die_and_reconnect, @@ -1706,12 +1707,42 @@ compile_beam_no_file(Config) -> [{error,{no_file_attribute,BeamFile}}] = cover:compile_beam_directory(Dir), ok. +%% GH-4353: Don't crash when the backend for generating the abstract code +%% is missing. +compile_beam_missing_backend(Config) -> + PrivDir = proplists:get_value(priv_dir, Config), + Dir = filename:join(PrivDir, ?FUNCTION_NAME), + ok = filelib:ensure_dir(filename:join(Dir, "*")), + code:add_patha(Dir), + Str = lists:append( + ["-module(no_backend).\n" + "-compile(export_all).\n" + "foo() -> ok.\n"]), + TT = do_scan(Str), + Forms = [ begin {ok,Y} = erl_parse:parse_form(X),Y end || X <- TT ], + {ok,_,Bin} = compile:forms(Forms, [debug_info]), + + %% Create a debug_info chunk with a non-existing backend. + {ok,no_backend,All0} = beam_lib:all_chunks(Bin), + FakeBackend = definitely__not__an__existing__backend, + FakeDebugInfo = {debug_info_v1,FakeBackend,nothing_here}, + All = lists:keyreplace("Dbgi", 1, All0, {"Dbgi", term_to_binary(FakeDebugInfo)}), + {ok,NewBeam} = beam_lib:build_module(All), + BeamFile = filename:join(Dir, "no_backend.beam"), + ok = file:write_file(BeamFile, NewBeam), + + {error,{{missing_backend,FakeBackend},BeamFile}} = cover:compile_beam(no_backend), + [{error,{{missing_backend,FakeBackend},BeamFile}}] = cover:compile_beam_directory(Dir), + + ok. + do_scan([]) -> []; do_scan(Str) -> {done,{ok,T,_},C} = erl_scan:tokens([],Str,0), [ T | do_scan(C) ]. + %% PR 856. Fix a bc bug. otp_13277(Config) -> Test = <<"-module(t). |