diff options
Diffstat (limited to 'scripts/diffable')
| -rwxr-xr-x | scripts/diffable | 317 | 
1 files changed, 149 insertions, 168 deletions
| diff --git a/scripts/diffable b/scripts/diffable index 08d2d5cb35..d2208d9d89 100755 --- a/scripts/diffable +++ b/scripts/diffable @@ -3,56 +3,85 @@  -mode(compile).  main(Args0) -> -    {Args,Opts} = opts(Args0, #{format=>asm,no_compile=>false}), +    DefOpts = #{format=>asm,no_compile=>false,legacy=>false}, +    {Args,Opts} = opts(Args0, DefOpts),      case Args of  	[OutDir] ->  	    do_compile(OutDir, Opts);  	_ -> -            usage(), -	    halt(1) +            usage()      end.  usage() -> -    S = "usage: otp-diffable-asm [OPTION] DIRECTORY\n\n" -        "Options:\n" -        "  --asm          Output to .S files (default)\n" -        "  --dis          Output to .dis files\n" -        "  --no-compile   Disassemble from BEAM files (use with --dis)\n" -        "\n" -        "DESCRIPTION\n" -        "\n" -        "Compile some applications from OTP (more than 700 modules) to either\n" -        ".S files or .dis files. The files are massaged to make them diff-friendly.\n" -        "\n" -        "EXAMPLES\n" -        "\n" -        "This example shows how the effectiveness of a compiler \n" -        "optimization can be verified (alternatively, that pure code\n" -        "refactoring has no effect on the generated code):\n" -        "\n" -        "$ scripts/diffable old\n" -        "# Hack the compiler.\n" -        "$ scripts/diffable new\n" -        "$ diff -u old new\n" -        "\n" -        "This example shows how the effectiveness of loader hacks\n" -        "can be verified:\n" -        "\n" -        "$ scripts/diffable --dis --no-compile old\n" -        "# Hack ops.tab and/or one of the *instr.tab files.\n" -        "$ scripts/diffable --dis --no-compile new\n" -        "$ diff -u old new\n", -    io:put_chars(S). - -opts(["--asm"|Args], Opts) -> -    opts(Args, Opts#{format:=asm}); -opts(["--dis"|Args], Opts) -> -    opts(Args, Opts#{format:=dis}); -opts(["--no-compile"|Args], Opts) -> -    opts(Args, Opts#{format:=dis,no_compile:=true}); +    S = ["usage: otp-diffable-asm [OPTION] DIRECTORY\n\n" +         "Options:\n" +         "  --asm          Output to .S files (default)\n" +         "  --legacy-asm   Output to legacy .S files\n" +         "  --dis          Output to .dis files\n" +         "  --no-compile   Disassemble from BEAM files (use with --dis)\n" +         "\n" +         "DESCRIPTION\n" +         "\n" +         "Compile some applications from OTP (about 1000 modules) to either\n" +         ".S files or .dis files. The files are massaged to make them diff-friendly.\n" +         "\n" +         "The --legacy-asm options forces the output file to be in Latin1 encoding\n" +         "and adds a latin1 encoding comment to the first line of the file.\n" +         "\n" +         "INCLUDING THE ELIXIR STANDARD LIBRARY\n" +         "\n" +         "If the Elixir repository is installed alongside the Erlang/OTP repository,\n" +         "the Elixir standard library will be included in the compilation. For this\n" +         "to work, the Elixir repository must be installed in: \n", +         "\n" +         "    ",elixir_root(),"\n" +         "\n" +         "Here is how to install Elixir:\n" +         "\n" +         "    cd ",filename:dirname(elixir_root()),"\n" +         "    git clone [email protected]:elixir-lang/elixir.git\n" +         "    cd elixir\n" +         "    PATH=",code:root_dir(),"/bin:$PATH make clean test\n" +         "\n" +         "EXAMPLES\n" +         "\n" +         "This example shows how the effectiveness of a compiler \n" +         "optimization can be verified (alternatively, that pure code\n" +         "refactoring has no effect on the generated code):\n" +         "\n" +         "$ scripts/diffable old\n" +         "# Hack the compiler.\n" +         "$ scripts/diffable new\n" +         "$ diff -u old new\n" +         "\n" +         "This example shows how the effectiveness of loader hacks\n" +         "can be verified:\n" +         "\n" +         "$ scripts/diffable --dis --no-compile old\n" +         "# Hack ops.tab and/or one of the *instr.tab files.\n" +         "$ scripts/diffable --dis --no-compile new\n" +         "$ diff -u old new\n"], +    io:put_chars(S), +    halt(1). + +opts(["--"++Opt|Args], Opts0) -> +    Opts = opt(Opt, Opts0), +    opts(Args, Opts);  opts(Args, Opts) ->      {Args,Opts}. +opt("asm", Opts) -> +    Opts#{format:=asm}; +opt("dis", Opts) -> +    Opts#{format:=dis}; +opt("legacy-asm", Opts) -> +    Opts#{format:=asm,legacy:=true}; +opt("no-compile", Opts) -> +    Opts#{format:=dis,no_compile:=true}; +opt(Opt, _Opts) -> +    io:format("Uknown option: --~ts\n\n", [Opt]), +    usage(). +  do_compile(OutDir, Opts0) ->      Opts1 = Opts0#{outdir=>OutDir},      _ = filelib:ensure_dir(filename:join(OutDir, "dummy")), @@ -93,17 +122,26 @@ compile_file(CF, File) ->          CF(File)      catch  	Class:Error:Stk -> -	    io:format("~s: ~p ~p\n~p\n", -		      [File,Class,Error,Stk]), +            if +                is_list(File) -> +                    io:format("~s: ~p ~p\n~p\n\n", +                              [File,Class,Error,Stk]); +                true -> +                    io:format("~p: ~p ~p\n~p\n\n", +                              [File,Class,Error,Stk]) +            end,  	    error      end. +elixir_root() -> +    filename:join(filename:dirname(code:root_dir()), "elixir"). +  %%%  %%% Get names of files (either .erl files or BEAM files).  %%%  get_files(Apps, #{format:=dis,no_compile:=true}=Opts) -> -    Files = get_beams(Apps), +    Files = get_elixir_beams() ++ get_beams(Apps),      {Files,Opts};  get_files(Apps, #{}=Opts) ->      Inc = make_includes(), @@ -113,20 +151,40 @@ get_files(Apps, #{}=Opts) ->                      {d,'COMPILER_VSN',1},                      {d,erlang_daemon_port,1337}|Inc],      Files0 = get_src(Apps), -    Files = add_opts(Files0, CompilerOpts), +    Files1 = add_opts(Files0, CompilerOpts), +    Files = [{Beam,elixir} || Beam <- get_elixir_beams()] ++ Files1,      {Files,Opts}. +get_elixir_beams() -> +    ElixirEbin = filename:join(elixir_root(), "lib/elixir/ebin"), +    case filelib:is_dir(ElixirEbin) of +        true -> +            true = code:add_patha(ElixirEbin), +            filelib:wildcard(filename:join(ElixirEbin, "*.beam")); +        false -> +            [] +    end. +  add_opts([F|Fs], Opts0) -> -    Opts = case filename:basename(F) of -               "group_history.erl" -> +    Opts = case vsn_is_harmful(F) of +               true ->                     Opts0 -- [{d,'VSN',1}]; -               _ -> +               false ->                     Opts0             end,      [{F,Opts}|add_opts(Fs, Opts0)];  add_opts([], _Opts) ->      []. +vsn_is_harmful(F) -> +    case filename:basename(F) of +        "group_history.erl" -> +            true; +        _ -> +            App = filename:basename(filename:dirname(filename:dirname(F))), +            App =:= "ssl" +    end. +  get_src(["preloaded"|Apps]) ->      WC = filename:join(code:root_dir(), "erts/preloaded/src/*.erl"),      filelib:wildcard(WC) ++ get_src(Apps); @@ -188,109 +246,48 @@ get_beams([]) -> [].  %%% Generate renumbered .S files.  %%% -compile_to_asm_fun(#{outdir:=OutDir}) -> +compile_to_asm_fun(#{outdir:=OutDir}=Opts) ->      fun(File) -> -            compile_to_asm(File, OutDir) +            Legacy = map_get(legacy, Opts), +            compile_to_asm(File, OutDir, Legacy)      end. -compile_to_asm({File,Opts}, OutDir) -> -    case compile:file(File, [to_asm,binary,report_errors|Opts]) of +compile_to_asm({Beam,elixir}, OutDir, _Legacy) -> +    Abst = get_abstract_from_beam(Beam), +    Source = filename:rootname(Beam, ".beam"), +    Opts = [diffable,{outdir,OutDir},report_errors,{source,Source}], +    case compile:forms(Abst, Opts) of +        {ok,_Mod,_Asm} -> +            ok;          error -> -            error; -        {ok,Mod,Asm0} -> -            {ok,Asm1} = beam_a:module(Asm0, []), -            Asm2 = renumber_asm(Asm1), -            {ok,Asm} = beam_z:module(Asm2, []), -            print_asm(Mod, OutDir, Asm) +            error +    end; +compile_to_asm({File,Opts}, OutDir, Legacy) -> +    case compile:file(File, [diffable,{outdir,OutDir},report_errors|Opts]) of +        {ok,_Mod} -> +            case Legacy of +                true -> +                    legacy_asm(OutDir, File); +                false -> +                    ok +            end; +        error -> +            error      end. -print_asm(Mod, OutDir, Asm) -> -    S = atom_to_list(Mod) ++ ".S", -    Name = filename:join(OutDir, S), -    {ok,Fd} = file:open(Name, [write,raw,delayed_write]), -    ok = beam_listing(Fd, Asm), -    ok = file:close(Fd). - -renumber_asm({Mod,Exp,Attr,Fs0,NumLabels}) -> -    EntryLabels = maps:from_list(entry_labels(Fs0)), -    Fs = [fix_func(F, EntryLabels) || F <- Fs0], -    {Mod,Exp,Attr,Fs,NumLabels}. - -entry_labels(Fs) -> -    [{Entry,{Name,Arity}} || {function,Name,Arity,Entry,_} <- Fs]. - -fix_func({function,Name,Arity,Entry0,Is0}, LabelMap0) -> -    Entry = maps:get(Entry0, LabelMap0), -    LabelMap = label_map(Is0, 1, LabelMap0), -    Is = replace(Is0, [], LabelMap), -    {function,Name,Arity,Entry,Is}. +legacy_asm(OutDir, Source) -> +    ModName = filename:rootname(filename:basename(Source)), +    File = filename:join(OutDir, ModName), +    AsmFile = File ++ ".S", +    {ok,Asm0} = file:read_file(AsmFile), +    Asm1 = unicode:characters_to_binary(Asm0, utf8, latin1), +    Asm = [<<"%% -*- encoding:latin-1 -*-\n">>|Asm1], +    ok = file:write_file(AsmFile, Asm). -label_map([{label,Old}|Is], New, Map) -> -    case maps:is_key(Old, Map) of -        false -> -            label_map(Is, New+1, Map#{Old=>New}); -        true -> -            label_map(Is, New, Map) -    end; -label_map([_|Is], New, Map) -> -    label_map(Is, New, Map); -label_map([], _New, Map) -> -    Map. - -replace([{label,Lbl}|Is], Acc, D) -> -    replace(Is, [{label,label(Lbl, D)}|Acc], D); -replace([{test,Test,{f,Lbl},Ops}|Is], Acc, D) -> -    replace(Is, [{test,Test,{f,label(Lbl, D)},Ops}|Acc], D); -replace([{test,Test,{f,Lbl},Live,Ops,Dst}|Is], Acc, D) -> -    replace(Is, [{test,Test,{f,label(Lbl, D)},Live,Ops,Dst}|Acc], D); -replace([{select,I,R,{f,Fail0},Vls0}|Is], Acc, D) -> -    Vls = lists:map(fun ({f,L}) -> {f,label(L, D)}; -			(Other) -> Other -		    end, Vls0), -    Fail = label(Fail0, D), -    replace(Is, [{select,I,R,{f,Fail},Vls}|Acc], D); -replace([{'try',R,{f,Lbl}}|Is], Acc, D) -> -    replace(Is, [{'try',R,{f,label(Lbl, D)}}|Acc], D); -replace([{'catch',R,{f,Lbl}}|Is], Acc, D) -> -    replace(Is, [{'catch',R,{f,label(Lbl, D)}}|Acc], D); -replace([{jump,{f,Lbl}}|Is], Acc, D) -> -    replace(Is, [{jump,{f,label(Lbl, D)}}|Acc], D); -replace([{loop_rec,{f,Lbl},R}|Is], Acc, D) -> -    replace(Is, [{loop_rec,{f,label(Lbl, D)},R}|Acc], D); -replace([{loop_rec_end,{f,Lbl}}|Is], Acc, D) -> -    replace(Is, [{loop_rec_end,{f,label(Lbl, D)}}|Acc], D); -replace([{wait,{f,Lbl}}|Is], Acc, D) -> -    replace(Is, [{wait,{f,label(Lbl, D)}}|Acc], D); -replace([{wait_timeout,{f,Lbl},To}|Is], Acc, D) -> -    replace(Is, [{wait_timeout,{f,label(Lbl, D)},To}|Acc], D); -replace([{bif,Name,{f,Lbl},As,R}|Is], Acc, D) when Lbl =/= 0 -> -    replace(Is, [{bif,Name,{f,label(Lbl, D)},As,R}|Acc], D); -replace([{gc_bif,Name,{f,Lbl},Live,As,R}|Is], Acc, D) when Lbl =/= 0 -> -    replace(Is, [{gc_bif,Name,{f,label(Lbl, D)},Live,As,R}|Acc], D); -replace([{call,Ar,{f,Lbl}}|Is], Acc, D) -> -    replace(Is, [{call,Ar,{f,label(Lbl,D)}}|Acc], D); -replace([{make_fun2,{f,Lbl},U1,U2,U3}|Is], Acc, D) -> -    replace(Is, [{make_fun2,{f,label(Lbl, D)},U1,U2,U3}|Acc], D); -replace([{bs_init,{f,Lbl},Info,Live,Ss,Dst}|Is], Acc, D) when Lbl =/= 0 -> -    replace(Is, [{bs_init,{f,label(Lbl, D)},Info,Live,Ss,Dst}|Acc], D); -replace([{bs_put,{f,Lbl},Info,Ss}|Is], Acc, D) when Lbl =/= 0 -> -    replace(Is, [{bs_put,{f,label(Lbl, D)},Info,Ss}|Acc], D); -replace([{put_map=I,{f,Lbl},Op,Src,Dst,Live,List}|Is], Acc, D) -  when Lbl =/= 0 -> -    replace(Is, [{I,{f,label(Lbl, D)},Op,Src,Dst,Live,List}|Acc], D); -replace([{get_map_elements=I,{f,Lbl},Src,List}|Is], Acc, D) when Lbl =/= 0 -> -    replace(Is, [{I,{f,label(Lbl, D)},Src,List}|Acc], D); -replace([{recv_mark=I,{f,Lbl}}|Is], Acc, D) -> -    replace(Is, [{I,{f,label(Lbl, D)}}|Acc], D); -replace([{recv_set=I,{f,Lbl}}|Is], Acc, D) -> -    replace(Is, [{I,{f,label(Lbl, D)}}|Acc], D); -replace([I|Is], Acc, D) -> -    replace(Is, [I|Acc], D); -replace([], Acc, _) -> -    lists:reverse(Acc). - -label(Old, D) when is_integer(Old) -> -    maps:get(Old, D). +get_abstract_from_beam(Beam) -> +    {ok,{_Mod,[AbstChunk]}} = beam_lib:chunks(Beam, [abstract_code]), +    {abstract_code,{raw_abstract_v1,Abst}} = AbstChunk, +    Abst.  %%%  %%% Compile and disassemble the loaded code. @@ -305,6 +302,15 @@ compile_to_dis_fun(#{outdir:=OutDir,no_compile:=true}) ->              dis_only(File, OutDir)      end. +compile_to_dis({File,elixir}, OutDir) -> +    {ok,Beam} = file:read_file(File), +    Mod0 = filename:rootname(filename:basename(File)), +    Mod = list_to_atom(Mod0), +    Dis0 = disasm(Mod, Beam), +    Dis1 = renumber_disasm(Dis0, Mod, Mod), +    Dis = format_disasm(Dis1), +    OutFile = filename:join(OutDir, atom_to_list(Mod)++".dis"), +    ok = file:write_file(OutFile, Dis);  compile_to_dis({File,Opts}, OutDir) ->      case compile:file(File, [to_asm,binary,report_errors|Opts]) of          error -> @@ -598,28 +604,3 @@ p_run_loop(Test, List, N, Refs0, Errors0) ->  	    Refs = Refs0 -- [Ref],  	    p_run_loop(Test, List, N, Refs, Errors)      end. - -%%% -%%% Borrowed from beam_listing and tweaked. -%%% - -beam_listing(Stream, {Mod,Exp,Attr,Code,NumLabels}) -> -    Head = ["%% -*- encoding:latin-1 -*-\n", -            io_lib:format("{module, ~p}.  %% version = ~w\n", -                          [Mod, beam_opcodes:format_number()]), -            io_lib:format("\n{exports, ~p}.\n", [Exp]), -            io_lib:format("\n{attributes, ~p}.\n", [Attr]), -            io_lib:format("\n{labels, ~p}.\n", [NumLabels])], -    ok = file:write(Stream, Head), -    lists:foreach( -      fun ({function,Name,Arity,Entry,Asm}) -> -              S = [io_lib:format("\n\n{function, ~w, ~w, ~w}.\n", -                                 [Name,Arity,Entry])|format_asm(Asm)], -              ok = file:write(Stream, S) -      end, Code). - -format_asm([{label,_}=I|Is]) -> -    [io_lib:format("  ~p", [I]),".\n"|format_asm(Is)]; -format_asm([I|Is]) -> -    [io_lib:format("    ~p", [I]),".\n"|format_asm(Is)]; -format_asm([]) -> []. | 
