diff options
Diffstat (limited to 'lib/kernel/src')
| -rw-r--r-- | lib/kernel/src/code.erl | 164 | ||||
| -rw-r--r-- | lib/kernel/src/code_server.erl | 28 | ||||
| -rw-r--r-- | lib/kernel/src/disk_log.erl | 394 | ||||
| -rw-r--r-- | lib/kernel/src/disk_log.hrl | 4 | ||||
| -rw-r--r-- | lib/kernel/src/disk_log_1.erl | 32 | ||||
| -rw-r--r-- | lib/kernel/src/erts_debug.erl | 25 | ||||
| -rw-r--r-- | lib/kernel/src/file.erl | 49 | ||||
| -rw-r--r-- | lib/kernel/src/global.erl | 16 | ||||
| -rw-r--r-- | lib/kernel/src/hipe_ext_format.hrl | 12 | ||||
| -rw-r--r-- | lib/kernel/src/hipe_unified_loader.erl | 379 | ||||
| -rw-r--r-- | lib/kernel/src/inet_db.erl | 3 | ||||
| -rw-r--r-- | lib/kernel/src/kernel.app.src | 2 | ||||
| -rw-r--r-- | lib/kernel/src/kernel.appup.src | 6 | ||||
| -rw-r--r-- | lib/kernel/src/net_kernel.erl | 4 | 
14 files changed, 569 insertions, 549 deletions
| diff --git a/lib/kernel/src/code.erl b/lib/kernel/src/code.erl index 8d0a2fbf66..5a7ca493cc 100644 --- a/lib/kernel/src/code.erl +++ b/lib/kernel/src/code.erl @@ -70,7 +70,9 @@  	 where_is_file/2,  	 set_primary_archive/4,  	 clash/0, -     get_mode/0]). +         module_status/1, +         modified_modules/0, +         get_mode/0]).  -deprecated({rehash,0,next_major_release}). @@ -116,8 +118,8 @@ get_chunk(_, _) ->  is_module_native(_) ->      erlang:nif_error(undef). --spec make_stub_module(Module, Beam, Info) -> Module when -      Module :: module(), +-spec make_stub_module(LoaderState, Beam, Info) -> module() when +      LoaderState :: binary(),        Beam :: binary(),        Info :: {list(), list(), binary()}. @@ -719,38 +721,14 @@ start_get_mode() ->  which(Module) when is_atom(Module) ->      case is_loaded(Module) of  	false -> -	    which2(Module); +            which(Module, get_path());  	{file, File} ->  	    File      end. -which2(Module) -> -    Base = atom_to_list(Module), -    File = filename:basename(Base) ++ objfile_extension(), -    Path = get_path(), -    which(File, filename:dirname(Base), Path). - --spec which(file:filename(), file:filename(), [file:filename()]) -> -        'non_existing' | file:filename(). - -which(_, _, []) -> -    non_existing; -which(File, Base, [Directory|Tail]) -> -    Path = if -	       Base =:= "." -> Directory; -	       true -> filename:join(Directory, Base) -	   end, -    case erl_prim_loader:list_dir(Path) of -	{ok,Files} -> -	    case lists:member(File,Files) of -		true -> -		    filename:append(Path, File); -		false -> -		    which(File, Base, Tail) -	    end; -	_Error -> -	    which(File, Base, Tail) -    end. +which(Module, Path) when is_atom(Module) -> +    File = atom_to_list(Module) ++ objfile_extension(), +    where_is_file(Path, File).  %% Search the code path for a specific file. Try to locate  %% it in the code path cache if possible. @@ -760,13 +738,33 @@ which(File, Base, [Directory|Tail]) ->        Absname :: file:filename().  where_is_file(File) when is_list(File) ->      Path = get_path(), -    which(File, ".", Path). +    where_is_file(Path, File). --spec where_is_file(Path :: file:filename(), Filename :: file:filename()) -> -        file:filename() | 'non_existing'. +%% To avoid unnecessary work when looking at many modules, this also +%% accepts pairs of directories and pre-fetched contents in the path +-spec where_is_file(Path :: [Dir|{Dir,Files}], Filename :: file:filename()) -> +          'non_existing' | file:filename() when +      Dir :: file:filename(), Files :: [file:filename()]. + +where_is_file([], _) -> +    non_existing; +where_is_file([{Path, Files}|Tail], File) -> +    where_is_file(Tail, File, Path, Files); +where_is_file([Path|Tail], File) -> +    case erl_prim_loader:list_dir(Path) of +	{ok,Files} -> +            where_is_file(Tail, File, Path, Files); +	_Error -> +	    where_is_file(Tail, File) +    end. -where_is_file(Path, File) when is_list(Path), is_list(File) -> -    which(File, ".", Path). +where_is_file(Tail, File, Path, Files) -> +    case lists:member(File, Files) of +        true -> +            filename:append(Path, File); +        false -> +            where_is_file(Tail, File) +    end.  -spec set_primary_archive(ArchiveFile :: file:filename(),  			  ArchiveBin :: binary(), @@ -899,3 +897,97 @@ load_all_native_1([{Mod,BeamFilename}|T], ChunkTag) ->      load_all_native_1(T, ChunkTag);  load_all_native_1([], _) ->      ok. + +%% Returns the status of the module in relation to object file on disk. +-spec module_status(Module :: module()) -> not_loaded | loaded | modified | removed. +module_status(Module) -> +    module_status(Module, code:get_path()). + +%% Note that we don't want to go via which/1, since it doesn't look at the +%% disk contents at all if the module is already loaded. +module_status(Module, PathFiles) -> +    case code:is_loaded(Module) of +        false -> not_loaded; +        {file, preloaded} -> loaded; +        {file, cover_compiled} -> +            %% cover compilation loads directly to memory and does not +            %% create a beam file, so report 'modified' if a file exists +            case which(Module, PathFiles) of +                non_existing -> removed; +                _File -> modified +            end; +        {file, []} -> loaded;  % no beam file - generated code +        {file, OldFile} when is_list(OldFile) -> +            %% we don't care whether or not the file is in the same location +            %% as when last loaded, as long as it can be found in the path +            case which(Module, PathFiles) of +                non_existing -> removed; +                Path -> +                    case module_changed_on_disk(Module, Path) of +                        true -> modified; +                        false -> loaded +                    end +            end +    end. + +%% Detects actual code changes only, e.g. to decide whether a module should +%% be reloaded; does not care about file timestamps or compilation time +module_changed_on_disk(Module, Path) -> +    MD5 = erlang:get_module_info(Module, md5), +    case erlang:system_info(hipe_architecture) of +        undefined -> +            %% straightforward, since native is not supported +            MD5 =/= beam_file_md5(Path); +        Architecture -> +            case code:is_module_native(Module) of +                true -> +                    %% MD5 is for native code, so we check only the native +                    %% code on disk, ignoring the beam code +                    MD5 =/= beam_file_native_md5(Path, Architecture); +                _ -> +                    %% MD5 is for beam code, so check only the beam code on +                    %% disk, even if the file contains native code as well +                    MD5 =/= beam_file_md5(Path) +            end +    end. + +beam_file_md5(Path) -> +    case beam_lib:md5(Path) of +        {ok,{_Mod,MD5}} -> MD5; +        _ -> undefined +    end. + +beam_file_native_md5(Path, Architecture) -> +    try +        get_beam_chunk(Path, hipe_unified_loader:chunk_name(Architecture)) +    of +        NativeCode when is_binary(NativeCode) -> +            erlang:md5(NativeCode) +    catch +        _:_ -> undefined +    end. + +get_beam_chunk(Path, Chunk) -> +    {ok, {_, [{_, Bin}]}} = beam_lib:chunks(Path, [Chunk]), +    Bin. + +%% Returns a list of all modules modified on disk. +-spec modified_modules() -> [module()]. +modified_modules() -> +    PathFiles = path_files(), +    [M || {M, _} <- code:all_loaded(), +          module_status(M, PathFiles) =:= modified]. + +%% prefetch the directory contents of code path directories +path_files() -> +    path_files(code:get_path()). + +path_files([]) -> +    []; +path_files([Path|Tail]) -> +    case erl_prim_loader:list_dir(Path) of +        {ok, Files} -> +            [{Path,Files} | path_files(Tail)]; +        _Error -> +            path_files(Tail) +    end. diff --git a/lib/kernel/src/code_server.erl b/lib/kernel/src/code_server.erl index 59b26176bf..418b0c50e1 100644 --- a/lib/kernel/src/code_server.erl +++ b/lib/kernel/src/code_server.erl @@ -1130,19 +1130,18 @@ try_load_module_2(File, Mod, Bin, From, Architecture,                    #state{moddb=Db}=St) ->      case catch hipe_unified_loader:load_native_code(Mod, Bin, Architecture) of          {module,Mod} = Module -> -	    ets:insert(Db, [{{native,Mod},true},{Mod,File}]), +	    ets:insert(Db, {Mod,File}),              {reply,Module,St};          no_native ->              try_load_module_3(File, Mod, Bin, From, Architecture, St);          Error ->              error_msg("Native loading of ~ts failed: ~p\n", [File,Error]), -            {reply,ok,St} +            {reply,{error,Error},St}      end. -try_load_module_3(File, Mod, Bin, From, Architecture, St0) -> +try_load_module_3(File, Mod, Bin, From, _Architecture, St0) ->      Action = fun({module,_}=Module, #state{moddb=Db}=S) ->  		     ets:insert(Db, {Mod,File}), -		     post_beam_load([Mod], Architecture, S),  		     {reply,Module,S};  		({error,on_load_failure}=Error, S) ->  		     {reply,Error,S}; @@ -1153,24 +1152,14 @@ try_load_module_3(File, Mod, Bin, From, Architecture, St0) ->      Res = erlang:load_module(Mod, Bin),      handle_on_load(Res, Action, Mod, From, St0). -hipe_result_to_status(Result, #state{moddb=Db}) -> +hipe_result_to_status(Result, #state{}) ->      case Result of -	{module,Mod} -> -	    ets:insert(Db, [{{native,Mod},true}]), +	{module,_} ->  	    Result;  	_ ->  	    {error,Result}      end. -post_beam_load(_, undefined, _) -> -    %% HiPE is disabled. -    ok; -post_beam_load(Mods0, _Architecture, #state{moddb=Db}) -> -    %% post_beam_load/2 can potentially be very expensive because it -    %% blocks multi-scheduling. Therefore, we only want to call -    %% it with modules that are known to have native code loaded. -    Mods = [M || M <- Mods0, ets:member(Db, {native,M})], -    hipe_unified_loader:post_beam_load(Mods).  int_list([H|T]) when is_integer(H) -> int_list(T);  int_list([_|_])                    -> false; @@ -1313,15 +1302,12 @@ abort_if_sticky(L, Db) ->  	[_|_] -> {error,Sticky}      end. -do_finish_loading(Prepared, #state{moddb=Db}=St) -> +do_finish_loading(Prepared, #state{moddb=Db}) ->      MagicBins = [B || {_,{B,_}} <- Prepared],      case erlang:finish_loading(MagicBins) of  	ok ->  	    MFs = [{M,F} || {M,{_,F}} <- Prepared],  	    true = ets:insert(Db, MFs), -	    Ms = [M || {M,_} <- MFs], -	    Architecture = erlang:system_info(hipe_architecture), -	    post_beam_load(Ms, Architecture, St),  	    ok;  	{Reason,Ms} ->  	    {error,[{M,Reason} || M <- Ms]} @@ -1423,7 +1409,7 @@ finish_on_load_report(Mod, Term) ->      %% from the code_server process.      spawn(fun() ->  		  F = "The on_load function for module " -		      "~s returned ~P\n", +		      "~s returned:~n~P\n",  		  %% Express the call as an apply to simplify  		  %% the ext_mod_dep/1 test case. diff --git a/lib/kernel/src/disk_log.erl b/lib/kernel/src/disk_log.erl index 9b44021872..2ade7fd77a 100644 --- a/lib/kernel/src/disk_log.erl +++ b/lib/kernel/src/disk_log.erl @@ -67,7 +67,7 @@  %%-define(PROFILE(C), C).  -define(PROFILE(C), void). --compile({inline,[{log_loop,5},{log_end_sync,2},{replies,2},{rflat,1}]}). +-compile({inline,[{log_loop,6},{log_end_sync,2},{replies,2},{rflat,1}]}).  %%%----------------------------------------------------------------------  %%% Contract type specifications @@ -75,8 +75,6 @@  -opaque continuation() :: #continuation{}. --type bytes()          :: binary() | [byte()]. -  -type file_error()     :: term().  % XXX: refine  -type invalid_header() :: term().  % XXX: refine @@ -127,28 +125,28 @@ open(A) ->        Log :: log(),        Term :: term().  log(Log, Term) ->  -    req(Log, {log, term_to_binary(Term)}). +    req(Log, {log, internal, [term_to_binary(Term)]}).  -spec blog(Log, Bytes) -> ok | {error, Reason :: log_error_rsn()} when        Log :: log(), -      Bytes :: bytes(). +      Bytes :: iodata().  blog(Log, Bytes) -> -    req(Log, {blog, check_bytes(Bytes)}). +    req(Log, {log, external, [ensure_binary(Bytes)]}).  -spec log_terms(Log, TermList) -> ok | {error, Resaon :: log_error_rsn()} when        Log :: log(),        TermList :: [term()].  log_terms(Log, Terms) ->      Bs = terms2bins(Terms), -    req(Log, {log, Bs}). +    req(Log, {log, internal, Bs}).  -spec blog_terms(Log, BytesList) ->                          ok | {error, Reason :: log_error_rsn()} when        Log :: log(), -      BytesList :: [bytes()]. +      BytesList :: [iodata()].  blog_terms(Log, Bytess) -> -    Bs = check_bytes_list(Bytess, Bytess), -    req(Log, {blog, Bs}). +    Bs = ensure_binary_list(Bytess), +    req(Log, {log, external, Bs}).  -type notify_ret() :: 'ok' | {'error', 'no_such_log'}. @@ -156,27 +154,27 @@ blog_terms(Log, Bytess) ->        Log :: log(),        Term :: term().  alog(Log, Term) ->  -    notify(Log, {alog, term_to_binary(Term)}). +    notify(Log, {alog, internal, [term_to_binary(Term)]}).  -spec alog_terms(Log, TermList) -> notify_ret() when        Log :: log(),        TermList :: [term()].  alog_terms(Log, Terms) ->      Bs = terms2bins(Terms), -    notify(Log, {alog, Bs}). +    notify(Log, {alog, internal, Bs}).  -spec balog(Log, Bytes) -> notify_ret() when        Log :: log(), -      Bytes :: bytes(). +      Bytes :: iodata().  balog(Log, Bytes) -> -    notify(Log, {balog, check_bytes(Bytes)}). +    notify(Log, {alog, external, [ensure_binary(Bytes)]}).  -spec balog_terms(Log, ByteList) -> notify_ret() when        Log :: log(), -      ByteList :: [bytes()]. +      ByteList :: [iodata()].  balog_terms(Log, Bytess) -> -    Bs = check_bytes_list(Bytess, Bytess), -    notify(Log, {balog, Bs}). +    Bs = ensure_binary_list(Bytess), +    notify(Log, {alog, external, Bs}).  -type close_error_rsn() ::'no_such_log' | 'nonode'                           | {'file_error', file:filename(), file_error()}. @@ -219,9 +217,9 @@ truncate(Log, Head) ->  -spec btruncate(Log, BHead) -> 'ok' | {'error', trunc_error_rsn()} when        Log :: log(), -      BHead :: bytes(). +      BHead :: iodata().  btruncate(Log, Head) -> -    req(Log, {truncate, {ok, check_bytes(Head)}, btruncate, 2}). +    req(Log, {truncate, {ok, ensure_binary(Head)}, btruncate, 2}).  -type reopen_error_rsn() :: no_such_log                            | nonode @@ -248,9 +246,9 @@ reopen(Log, NewFile, NewHead) ->  -spec breopen(Log, File, BHead) -> 'ok' | {'error', reopen_error_rsn()} when        Log :: log(),        File :: file:filename(), -      BHead :: bytes(). +      BHead :: iodata().  breopen(Log, NewFile, NewHead) -> -    req(Log, {reopen, NewFile, {ok, check_bytes(NewHead)}, breopen, 3}). +    req(Log, {reopen, NewFile, {ok, ensure_binary(NewHead)}, breopen, 3}).  -type inc_wrap_error_rsn() :: 'no_such_log' | 'nonode'                              | {'read_only_mode', log()} @@ -670,13 +668,12 @@ init(Parent, Server) ->      process_flag(trap_exit, true),      loop(#state{parent = Parent, server = Server}). -loop(State) when State#state.messages =:= [] -> +loop(#state{messages = []}=State) ->      receive  	Message ->  	    handle(Message, State)      end; -loop(State) -> -    [M | Ms] = State#state.messages, +loop(#state{messages = [M | Ms]}=State) ->      handle(M, State#state{messages = Ms}).  handle({From, write_cache}, S) when From =:= self() -> @@ -686,106 +683,79 @@ handle({From, write_cache}, S) when From =:= self() ->          Error ->  	    loop(S#state{cache_error = Error})      end; -handle({From, {log, B}}, S) -> +handle({From, {log, Format, B}}=Message, S) ->      case get(log) of -	L when L#log.mode =:= read_only -> +	#log{mode = read_only}=L ->  	    reply(From, {error, {read_only_mode, L#log.name}}, S); -	L when L#log.status =:= ok, L#log.format =:= internal -> -	    log_loop(S, From, [B], [], iolist_size(B)); -	L when L#log.status =:= ok, L#log.format =:= external -> +	#log{status = ok, format=external}=L when Format =:= internal ->  	    reply(From, {error, {format_external, L#log.name}}, S); -	L when L#log.status =:= {blocked, false} -> -	    reply(From, {error, {blocked_log, L#log.name}}, S); -	L when L#log.blocked_by =:= From -> -	    reply(From, {error, {blocked_log, L#log.name}}, S); -	_ -> -	    loop(S#state{queue = [{From, {log, B}} | S#state.queue]}) -    end;     -handle({From, {blog, B}}, S) -> -    case get(log) of -	L when L#log.mode =:= read_only -> -	    reply(From, {error, {read_only_mode, L#log.name}}, S); -	L when L#log.status =:= ok -> -	    log_loop(S, From, [B], [], iolist_size(B)); -	L when L#log.status =:= {blocked, false} -> +	#log{status = ok, format=LogFormat} -> +	    log_loop(S, From, [B], [], iolist_size(B), LogFormat); +	#log{status = {blocked, false}}=L ->  	    reply(From, {error, {blocked_log, L#log.name}}, S); -	L when L#log.blocked_by =:= From -> +	#log{blocked_by = From}=L ->  	    reply(From, {error, {blocked_log, L#log.name}}, S);  	_ -> -	    loop(S#state{queue = [{From, {blog, B}} | S#state.queue]}) +	    enqueue(Message, S)      end; -handle({alog, B}, S) -> +handle({alog, Format, B}=Message, S) ->      case get(log) of -	L when L#log.mode =:= read_only -> +	#log{mode = read_only} ->  	    notify_owners({read_only,B}),  	    loop(S); -	L when L#log.status =:= ok, L#log.format =:= internal -> -	    log_loop(S, [], [B], [], iolist_size(B)); -	L when L#log.status =:= ok -> +	#log{status = ok, format = external} when Format =:= internal ->  	    notify_owners({format_external, B}),  	    loop(S); -	L when L#log.status =:= {blocked, false} -> +	#log{status = ok, format=LogFormat} -> +	    log_loop(S, [], [B], [], iolist_size(B), LogFormat); +	#log{status = {blocked, false}} ->  	    notify_owners({blocked_log, B}),  	    loop(S);  	_ -> -	    loop(S#state{queue = [{alog, B} | S#state.queue]}) +	    enqueue(Message, S)      end; -handle({balog, B}, S) -> +handle({From, {block, QueueLogRecs}}=Message, S) ->      case get(log) of -	L when L#log.mode =:= read_only -> -	    notify_owners({read_only,B}), -	    loop(S); -	L when L#log.status =:= ok -> -	    log_loop(S, [], [B], [], iolist_size(B)); -	L when L#log.status =:= {blocked, false} -> -	    notify_owners({blocked_log, B}), -	    loop(S); -	_ -> -	    loop(S#state{queue = [{balog, B} | S#state.queue]}) -    end; -handle({From, {block, QueueLogRecs}}, S) -> -    case get(log) of -	L when L#log.status =:= ok -> +	#log{status = ok}=L ->  	    do_block(From, QueueLogRecs, L),  	    reply(From, ok, S); -	L when L#log.status =:= {blocked, false} -> +	#log{status = {blocked, false}}=L ->  	    reply(From, {error, {blocked_log, L#log.name}}, S); -	L when L#log.blocked_by =:= From -> +	#log{blocked_by = From}=L ->  	    reply(From, {error, {blocked_log, L#log.name}}, S);  	_ -> -	    loop(S#state{queue = [{From, {block, QueueLogRecs}} | -				  S#state.queue]}) +	    enqueue(Message, S)      end;  handle({From, unblock}, S) ->      case get(log) of -	L when L#log.status =:= ok -> +	#log{status = ok}=L ->  	    reply(From, {error, {not_blocked, L#log.name}}, S); -	L when L#log.blocked_by =:= From -> +	#log{blocked_by = From}=L ->  	    S2 = do_unblock(L, S),  	    reply(From, ok, S2);  	L ->  	    reply(From, {error, {not_blocked_by_pid, L#log.name}}, S)      end; -handle({From, sync}, S) -> +handle({From, sync}=Message, S) ->      case get(log) of -	L when L#log.mode =:= read_only -> +	#log{mode = read_only}=L ->  	    reply(From, {error, {read_only_mode, L#log.name}}, S); -	L when L#log.status =:= ok -> -	    sync_loop([From], S); -	L when L#log.status =:= {blocked, false} -> +	#log{status = ok, format=LogFormat} -> +            log_loop(S, [], [], [From], 0, LogFormat); +	#log{status = {blocked, false}}=L ->  	    reply(From, {error, {blocked_log, L#log.name}}, S); -	L when L#log.blocked_by =:= From -> +	#log{blocked_by = From}=L ->  	    reply(From, {error, {blocked_log, L#log.name}}, S);  	_ -> -	    loop(S#state{queue = [{From, sync} | S#state.queue]}) +	    enqueue(Message, S)      end; -handle({From, {truncate, Head, F, A}}, S) -> +handle({From, {truncate, Head, F, A}}=Message, S) ->      case get(log) of -	L when L#log.mode =:= read_only -> +	#log{mode = read_only}=L ->  	    reply(From, {error, {read_only_mode, L#log.name}}, S); -	L when L#log.status =:= ok, S#state.cache_error =/= ok -> +	#log{status = ok} when S#state.cache_error =/= ok ->  	    loop(cache_error(S, [From])); -	L when L#log.status =:= ok -> +	#log{status = ok}=L ->  	    H = merge_head(Head, L#log.head),  	    case catch do_trunc(L, H) of  		ok -> @@ -796,48 +766,46 @@ handle({From, {truncate, Head, F, A}}, S) ->  		Error ->  		    do_exit(S, From, Error, ?failure(Error, F, A))  	    end; -	L when L#log.status =:= {blocked, false} -> +	#log{status = {blocked, false}}=L ->  	    reply(From, {error, {blocked_log, L#log.name}}, S); -	L when L#log.blocked_by =:= From -> +	#log{blocked_by = From}=L ->  	    reply(From, {error, {blocked_log, L#log.name}}, S);  	_ -> -	    loop(S#state{queue = [{From, {truncate, Head, F, A}}  -				  | S#state.queue]}) +	    enqueue(Message, S)      end; -handle({From, {chunk, Pos, B, N}},  S) -> +handle({From, {chunk, Pos, B, N}}=Message,  S) ->      case get(log) of -	L when L#log.status =:= ok, S#state.cache_error =/= ok -> +	#log{status = ok} when S#state.cache_error =/= ok ->  	    loop(cache_error(S, [From])); -	L when L#log.status =:= ok ->	 +	#log{status = ok}=L ->  	    R = do_chunk(L, Pos, B, N),  	    reply(From, R, S); -	L when L#log.blocked_by =:= From ->	 +	#log{blocked_by = From}=L ->  	    R = do_chunk(L, Pos, B, N),  	    reply(From, R, S); -	L when L#log.status =:= {blocked, false} -> +	#log{status = {blocked, false}}=L ->  	    reply(From, {error, {blocked_log, L#log.name}}, S);  	_L -> -	    loop(S#state{queue = [{From, {chunk, Pos, B, N}} | S#state.queue]}) +	    enqueue(Message, S)      end; -handle({From, {chunk_step, Pos, N}},  S) -> +handle({From, {chunk_step, Pos, N}}=Message,  S) ->      case get(log) of -	L when L#log.status =:= ok, S#state.cache_error =/= ok -> +	#log{status = ok} when S#state.cache_error =/= ok ->  	    loop(cache_error(S, [From])); -	L when L#log.status =:= ok ->	 +	#log{status = ok}=L ->  	    R = do_chunk_step(L, Pos, N),  	    reply(From, R, S); -	L when L#log.blocked_by =:= From ->	 +	#log{blocked_by = From}=L ->  	    R = do_chunk_step(L, Pos, N),  	    reply(From, R, S); -	L when L#log.status =:= {blocked, false} -> +	#log{status = {blocked, false}}=L ->  	    reply(From, {error, {blocked_log, L#log.name}}, S);  	_ -> -	    loop(S#state{queue = [{From, {chunk_step, Pos, N}} -				  | S#state.queue]}) +	    enqueue(Message, S)      end; -handle({From, {change_notify, Pid, NewNotify}}, S) -> +handle({From, {change_notify, Pid, NewNotify}}=Message, S) ->      case get(log) of -	L when L#log.status =:= ok -> +	#log{status = ok}=L ->  	    case do_change_notify(L, Pid, NewNotify) of  		{ok, L1} ->  		    put(log, L1), @@ -845,39 +813,37 @@ handle({From, {change_notify, Pid, NewNotify}}, S) ->  		Error ->  		    reply(From, Error, S)  	    end; -	L when L#log.status =:= {blocked, false} -> +	#log{status = {blocked, false}}=L ->  	    reply(From, {error, {blocked_log, L#log.name}}, S); -	L when L#log.blocked_by =:= From -> +	#log{blocked_by = From}=L ->  	    reply(From, {error, {blocked_log, L#log.name}}, S);  	_ -> -	    loop(S#state{queue = [{From, {change_notify, Pid, NewNotify}} -				  | S#state.queue]}) +	    enqueue(Message, S)      end; -handle({From, {change_header, NewHead}}, S) -> +handle({From, {change_header, NewHead}}=Message, S) ->      case get(log) of -	L when L#log.mode =:= read_only -> +	#log{mode = read_only}=L ->  	    reply(From, {error, {read_only_mode, L#log.name}}, S); -	L when L#log.status =:= ok -> -	    case check_head(NewHead, L#log.format) of  +	#log{status = ok, format = Format}=L -> +	    case check_head(NewHead, Format) of  		{ok, Head} -> -		    put(log, L#log{head = mk_head(Head, L#log.format)}), +		    put(log, L#log{head = mk_head(Head, Format)}),  		    reply(From, ok, S);  		Error ->  		    reply(From, Error, S)  	    end; -	L when L#log.status =:= {blocked, false} -> +	#log{status = {blocked, false}}=L ->  	    reply(From, {error, {blocked_log, L#log.name}}, S); -	L when L#log.blocked_by =:= From -> +	#log{blocked_by = From}=L ->  	    reply(From, {error, {blocked_log, L#log.name}}, S);  	_ -> -	    loop(S#state{queue = [{From, {change_header, NewHead}} -				  | S#state.queue]}) +	    enqueue(Message, S)      end; -handle({From, {change_size, NewSize}}, S) -> +handle({From, {change_size, NewSize}}=Message, S) ->      case get(log) of -	L when L#log.mode =:= read_only -> +	#log{mode = read_only}=L ->  	    reply(From, {error, {read_only_mode, L#log.name}}, S); -	L when L#log.status =:= ok ->	 +	#log{status = ok}=L ->  	    case check_size(L#log.type, NewSize) of  		ok ->  		    case catch do_change_size(L, NewSize) of % does the put @@ -894,23 +860,22 @@ handle({From, {change_size, NewSize}}, S) ->  		not_ok ->  		    reply(From, {error, {badarg, size}}, S)  	    end; -	L when L#log.status =:= {blocked, false} -> +	#log{status = {blocked, false}}=L ->  	    reply(From, {error, {blocked_log, L#log.name}}, S); -	L when L#log.blocked_by =:= From -> +	#log{blocked_by = From}=L ->  	    reply(From, {error, {blocked_log, L#log.name}}, S);  	_ -> -	    loop(S#state{queue = [{From, {change_size, NewSize}}  -				  | S#state.queue]}) +	    enqueue(Message, S)      end; -handle({From, inc_wrap_file}, S) -> +handle({From, inc_wrap_file}=Message, S) ->      case get(log) of -	L when L#log.mode =:= read_only -> +	#log{mode = read_only}=L ->  	    reply(From, {error, {read_only_mode, L#log.name}}, S); -	L when L#log.type =:= halt -> +	#log{type = halt}=L ->  	    reply(From, {error, {halt_log, L#log.name}}, S); -	L when L#log.status =:= ok, S#state.cache_error =/= ok -> +	#log{status = ok} when S#state.cache_error =/= ok ->  	    loop(cache_error(S, [From])); -	L when L#log.status =:= ok ->	 +	#log{status = ok}=L ->  	    case catch do_inc_wrap_file(L) of  		{ok, L2, Lost} ->  		    put(log, L2), @@ -920,20 +885,22 @@ handle({From, inc_wrap_file}, S) ->  		    put(log, L2),		      		    reply(From, Error, state_err(S, Error))  	    end; -	L when L#log.status =:= {blocked, false} -> +	#log{status = {blocked, false}}=L ->  	    reply(From, {error, {blocked_log, L#log.name}}, S); -	L when L#log.blocked_by =:= From -> +	#log{blocked_by = From}=L ->  	    reply(From, {error, {blocked_log, L#log.name}}, S);  	_ -> -	    loop(S#state{queue = [{From, inc_wrap_file} | S#state.queue]}) +	    enqueue(Message, S)      end;  handle({From, {reopen, NewFile, Head, F, A}}, S) ->      case get(log) of -	L when L#log.mode =:= read_only -> +	#log{mode = read_only}=L ->  	    reply(From, {error, {read_only_mode, L#log.name}}, S); -	L when L#log.status =:= ok, S#state.cache_error =/= ok -> +	#log{status = ok} when S#state.cache_error =/= ok ->  	    loop(cache_error(S, [From])); -	L when L#log.status =:= ok, L#log.filename =/= NewFile  -> +	#log{status = ok, filename = NewFile}=L -> +	    reply(From, {error, {same_file_name, L#log.name}}, S); +	#log{status = ok}=L ->  	    case catch close_disk_log2(L) of  		closed ->  		    File = L#log.filename, @@ -966,8 +933,6 @@ handle({From, {reopen, NewFile, Head, F, A}}, S) ->  		Error ->  		    do_exit(S, From, Error, ?failure(Error, F, A))  	    end; -	L when L#log.status =:= ok -> -	    reply(From, {error, {same_file_name, L#log.name}}, S);  	L ->  	    reply(From, {error, {blocked_log, L#log.name}}, S)      end; @@ -1005,11 +970,11 @@ handle({From, close}, S) ->      end;  handle({From, info}, S) ->      reply(From, do_info(get(log), S#state.cnt), S); -handle({'EXIT', From, Reason}, S) when From =:= S#state.parent -> +handle({'EXIT', From, Reason}, #state{parent=From}=S) ->      %% Parent orders shutdown.      _ = do_stop(S),      exit(Reason); -handle({'EXIT', From, Reason}, S) when From =:= S#state.server -> +handle({'EXIT', From, Reason}, #state{server=From}=S) ->      %% The server is gone.      _ = do_stop(S),      exit(Reason); @@ -1034,57 +999,59 @@ handle({system, From, Req}, S) ->  handle(_, S) ->      loop(S). -sync_loop(From, S) -> -    log_loop(S, [], [], From, 0). +enqueue(Message, #state{queue = Queue}=S) -> +    loop(S#state{queue = [Message | Queue]}). + +%% Collect further log and sync requests already in the mailbox or queued  -define(MAX_LOOK_AHEAD, 64*1024).  %% Inlined. -log_loop(#state{cache_error = CE}=S, Pids, _Bins, _Sync, _Sz) when CE =/= ok -> +log_loop(#state{cache_error = CE}=S, Pids, _Bins, _Sync, _Sz, _F) when CE =/= ok ->      loop(cache_error(S, Pids)); -log_loop(#state{}=S, Pids, Bins, Sync, Sz) when Sz > ?MAX_LOOK_AHEAD -> -    loop(log_end(S, Pids, Bins, Sync)); -log_loop(#state{messages = []}=S, Pids, Bins, Sync, Sz) -> -    receive  +log_loop(#state{}=S, Pids, Bins, Sync, Sz, _F) when Sz > ?MAX_LOOK_AHEAD -> +    loop(log_end(S, Pids, Bins, Sync, Sz)); +log_loop(#state{messages = []}=S, Pids, Bins, Sync, Sz, F) -> +    receive  	Message -> -            log_loop(Message, Pids, Bins, Sync, Sz, S, get(log)) +            log_loop(Message, Pids, Bins, Sync, Sz, F, S)      after 0 -> -	    loop(log_end(S, Pids, Bins, Sync)) +	    loop(log_end(S, Pids, Bins, Sync, Sz))      end; -log_loop(#state{messages = [M | Ms]}=S, Pids, Bins, Sync, Sz) -> +log_loop(#state{messages = [M | Ms]}=S, Pids, Bins, Sync, Sz, F) ->      S1 = S#state{messages = Ms}, -    log_loop(M, Pids, Bins, Sync, Sz, S1, get(log)). +    log_loop(M, Pids, Bins, Sync, Sz, F, S1).  %% Items logged after the last sync request found are sync:ed as well. -log_loop({alog,B}, Pids, Bins, Sync, Sz, S, #log{format = internal}) -> -    %% {alog, _} allowed for the internal format only. -    log_loop(S, Pids, [B | Bins], Sync, Sz+iolist_size(B)); -log_loop({balog, B}, Pids, Bins, Sync, Sz, S, _L) -> -    log_loop(S, Pids, [B | Bins], Sync, Sz+iolist_size(B)); -log_loop({From, {log, B}}, Pids, Bins, Sync, Sz, S, #log{format = internal}) -> -    %% {log, _} allowed for the internal format only. -    log_loop(S, [From | Pids], [B | Bins], Sync, Sz+iolist_size(B)); -log_loop({From, {blog, B}}, Pids, Bins, Sync, Sz, S, _L) -> -    log_loop(S, [From | Pids], [B | Bins], Sync, Sz+iolist_size(B)); -log_loop({From, sync}, Pids, Bins, Sync, Sz, S, _L) -> -    log_loop(S, Pids, Bins, [From | Sync], Sz); -log_loop(Message, Pids, Bins, Sync, _Sz, S, _L) -> -    NS = log_end(S, Pids, Bins, Sync), +log_loop({alog, internal, B}, Pids, Bins, Sync, Sz, internal=F, S) -> +    %% alog of terms allowed for the internal format only +    log_loop(S, Pids, [B | Bins], Sync, Sz+iolist_size(B), F); +log_loop({alog, binary, B}, Pids, Bins, Sync, Sz, F, S) -> +    log_loop(S, Pids, [B | Bins], Sync, Sz+iolist_size(B), F); +log_loop({From, {log, internal, B}}, Pids, Bins, Sync, Sz, internal=F, S) -> +    %% log of terms allowed for the internal format only +    log_loop(S, [From | Pids], [B | Bins], Sync, Sz+iolist_size(B), F); +log_loop({From, {log, binary, B}}, Pids, Bins, Sync, Sz, F, S) -> +    log_loop(S, [From | Pids], [B | Bins], Sync, Sz+iolist_size(B), F); +log_loop({From, sync}, Pids, Bins, Sync, Sz, F, S) -> +    log_loop(S, Pids, Bins, [From | Sync], Sz, F); +log_loop(Message, Pids, Bins, Sync, Sz, _F, S) -> +    NS = log_end(S, Pids, Bins, Sync, Sz),      handle(Message, NS). -log_end(S, [], [], Sync) -> +log_end(S, [], [], Sync, _Sz) ->      log_end_sync(S, Sync); -log_end(S, Pids, Bins, Sync) -> -    case do_log(get(log), rflat(Bins)) of +log_end(#state{cnt = Cnt}=S, Pids, Bins, Sync, Sz) -> +    case do_log(get(log), rflat(Bins), Sz) of  	N when is_integer(N) ->  	    ok = replies(Pids, ok), -	    S1 = (state_ok(S))#state{cnt = S#state.cnt+N}, +	    S1 = (state_ok(S))#state{cnt = Cnt + N},  	    log_end_sync(S1, Sync);          {error, {error, {full, _Name}}, N} when Pids =:= [] -> -            log_end_sync(state_ok(S#state{cnt = S#state.cnt + N}), Sync); +            log_end_sync(state_ok(S#state{cnt = Cnt + N}), Sync);  	{error, Error, N} ->  	    ok = replies(Pids, Error), -	    state_err(S#state{cnt = S#state.cnt + N}, Error) +	    state_err(S#state{cnt = Cnt + N}, Error)      end.  %% Inlined. @@ -1096,12 +1063,9 @@ log_end_sync(S, Sync) ->      state_err(S, Res).  %% Inlined. -rflat([B]=L) when is_binary(B) -> L;  rflat([B]) -> B;  rflat(B) -> rflat(B, []). -rflat([B | Bs], L) when is_binary(B) -> -    rflat(Bs, [B | L]);  rflat([B | Bs], L) ->      rflat(Bs, B ++ L);  rflat([], L) -> L. @@ -1138,17 +1102,17 @@ close_owner(Pid, L, S) ->      S2 = do_unblock(Pid, get(log), S),      unlink(Pid),      do_close2(L1, S2). -     +  %% -> {stop, S} | {continue, S} -close_user(Pid, L, S) when L#log.users > 0 -> -    L1 = L#log{users = L#log.users - 1}, +close_user(Pid, #log{users=Users}=L, S) when Users > 0 -> +    L1 = L#log{users = Users - 1},      put(log, L1),      S2 = do_unblock(Pid, get(log), S),      do_close2(L1, S2);  close_user(_Pid, _L, S) ->      {continue, S}. -do_close2(L, S) when L#log.users =:= 0, L#log.owners =:= [] -> +do_close2(#log{users = 0, owners = []}, S) ->      {stop, S};  do_close2(_L, S) ->      {continue, S}. @@ -1227,14 +1191,14 @@ add_pid(Pid, Notify, L) when is_pid(Pid) ->  add_pid(_NotAPid, _Notify, L) ->      {ok, L#log{users = L#log.users + 1}}. -unblock_pid(L) when L#log.blocked_by =:= none -> +unblock_pid(#log{blocked_by = none}) ->      ok; -unblock_pid(L) -> -    case is_owner(L#log.blocked_by, L) of +unblock_pid(#log{blocked_by = Pid}=L) -> +    case is_owner(Pid, L) of  	{true, _Notify} ->  	    ok;  	false -> -	    unlink(L#log.blocked_by) +	    unlink(Pid)      end.  %% -> true | false @@ -1326,7 +1290,7 @@ do_open(A) ->      end.  mk_head({head, Term}, internal) -> {ok, term_to_binary(Term)}; -mk_head({head, Bytes}, external) -> {ok, check_bytes(Bytes)}; +mk_head({head, Bytes}, external) -> {ok, ensure_binary(Bytes)};  mk_head(H, _) -> H.  terms2bins([T | Ts]) -> @@ -1334,30 +1298,29 @@ terms2bins([T | Ts]) ->  terms2bins([]) ->      []. -check_bytes_list([B | Bs], Bs0) when is_binary(B) -> -    check_bytes_list(Bs, Bs0); -check_bytes_list([], Bs0) -> +ensure_binary_list(Bs) -> +    ensure_binary_list(Bs, Bs). + +ensure_binary_list([B | Bs], Bs0) when is_binary(B) -> +    ensure_binary_list(Bs, Bs0); +ensure_binary_list([], Bs0) ->      Bs0; -check_bytes_list(_, Bs0) -> -    check_bytes_list(Bs0). - -check_bytes_list([B | Bs]) when is_binary(B) -> -    [B | check_bytes_list(Bs)]; -check_bytes_list([B | Bs]) -> -    [list_to_binary(B) | check_bytes_list(Bs)]; -check_bytes_list([]) -> +ensure_binary_list(_, Bs0) -> +    make_binary_list(Bs0). + +make_binary_list([B | Bs]) -> +    [ensure_binary(B) | make_binary_list(Bs)]; +make_binary_list([]) ->      []. -check_bytes(Binary) when is_binary(Binary) ->      -    Binary; -check_bytes(Bytes) ->  -    list_to_binary(Bytes). +ensure_binary(Bytes) -> +    iolist_to_binary(Bytes).  %%-----------------------------------------------------------------  %% Change size of the logs in runtime.  %%-----------------------------------------------------------------  %% -> ok | {big, CurSize} | throw(Error) -do_change_size(L, NewSize) when L#log.type =:= halt -> +do_change_size(#log{type = halt}=L, NewSize) ->      Halt = L#log.extra,      CurB = Halt#halt.curB,      NewLog = L#log{extra = Halt#halt{size = NewSize}}, @@ -1373,7 +1336,7 @@ do_change_size(L, NewSize) when L#log.type =:= halt ->  	true ->  	    {big, CurB}      end; -do_change_size(L, NewSize) when L#log.type =:= wrap -> +do_change_size(#log{type = wrap}=L, NewSize) ->      #log{extra = Extra, version = Version} = L,      {ok, Handle} = disk_log_1:change_size_wrap(Extra, NewSize, Version),      erase(is_full), @@ -1388,7 +1351,7 @@ check_head({head_func, {M, F, A}}, _Format) when is_atom(M),                                                   is_list(A) ->      {ok, {M, F, A}};  check_head({head, Head}, external) -> -    case catch check_bytes(Head) of +    case catch ensure_binary(Head) of  	{'EXIT', _} ->  	    {error, {badarg, head}};  	_ -> @@ -1674,7 +1637,7 @@ do_block(Pid, QueueLogRecs, L) ->  	    link(Pid)      end. -do_unblock(Pid, L, S) when L#log.blocked_by =:= Pid -> +do_unblock(Pid, #log{blocked_by = Pid}=L, S) ->      do_unblock(L, S);  do_unblock(_Pid, _L, S) ->      S. @@ -1692,10 +1655,13 @@ do_unblock(L, S) ->  -spec do_log(#log{}, [binary()]) -> integer() | {'error', _, integer()}. -do_log(L, B) when L#log.type =:= halt -> +do_log(L, B) -> +    do_log(L, B, iolist_size(B)). + +do_log(#log{type = halt}=L, B, BSz) ->      #log{format = Format, extra = Halt} = L,      #halt{curB = CurSize, size = Sz} = Halt, -    {Bs, BSize} = bsize(B, Format), +    {Bs, BSize} = logl(B, Format, BSz),      case get(is_full) of  	true ->              {error, {error, {full, L#log.name}}, 0}; @@ -1704,7 +1670,7 @@ do_log(L, B) when L#log.type =:= halt ->  	undefined ->  	    halt_write_full(L, B, Format, 0)      end; -do_log(L, B) when L#log.format_type =:= wrap_int -> +do_log(#log{format_type = wrap_int}=L, B, _BSz) ->      case disk_log_1:mf_int_log(L#log.extra, B, L#log.head) of  	{ok, Handle, Logged, Lost, Wraps} ->  	    notify_owners_wrap(Wraps), @@ -1717,7 +1683,7 @@ do_log(L, B) when L#log.format_type =:= wrap_int ->  	    put(log, L#log{extra = Handle}),  	    {error, Error, Logged - Lost}      end; -do_log(L, B) when L#log.format_type =:= wrap_ext -> +do_log(#log{format_type = wrap_ext}=L, B, _BSz) ->      case disk_log_1:mf_ext_log(L#log.extra, B, L#log.head) of  	{ok, Handle, Logged, Lost, Wraps} ->  	    notify_owners_wrap(Wraps), @@ -1731,17 +1697,16 @@ do_log(L, B) when L#log.format_type =:= wrap_ext ->  	    {error, Error, Logged - Lost}      end. -bsize(B, external) -> -    {B, xsz(B, 0)}; -bsize(B, internal) -> +logl(B, external, undefined) -> +    {B, iolist_size(B)}; +logl(B, external, Sz) -> +    {B, Sz}; +logl(B, internal, _Sz) ->      disk_log_1:logl(B). -xsz([B|T], Sz) -> xsz(T, byte_size(B) + Sz); -xsz([], Sz) -> Sz. -	  halt_write_full(L, [Bin | Bins], Format, N) ->      B = [Bin], -    {Bs, BSize} = bsize(B, Format), +    {Bs, BSize} = logl(B, Format, undefined),      Halt = L#log.extra,      #halt{curB = CurSize, size = Sz} = Halt,      if  @@ -1793,7 +1758,7 @@ do_sync(#log{type = wrap, extra = Handle} = Log) ->      Reply.  %% -> ok | Error | throw(Error) -do_trunc(L, Head) when L#log.type =:= halt -> +do_trunc(#log{type = halt}=L, Head) ->      #log{filename = FName, extra = Halt} = L,      FdC = Halt#halt.fdc,      {Reply1, FdC2} =  @@ -1822,7 +1787,7 @@ do_trunc(L, Head) when L#log.type =:= halt ->      end,      put(log, L#log{extra = NewHalt}),      Reply; -do_trunc(L, Head) when L#log.type =:= wrap -> +do_trunc(#log{type = wrap}=L, Head) ->      Handle = L#log.extra,      OldHead = L#log.head,      {MaxB, MaxF} = disk_log_1:get_wrap_size(Handle), @@ -2016,8 +1981,7 @@ notify_owners(Note) ->  		     (_) -> ok  		  end, L#log.owners). -cache_error(S, Pids) -> -    Error = S#state.cache_error, +cache_error(#state{cache_error=Error}=S, Pids) ->      ok = replies(Pids, Error),      state_err(S#state{cache_error = ok}, Error). diff --git a/lib/kernel/src/disk_log.hrl b/lib/kernel/src/disk_log.hrl index 3262d979ee..593dbb31ab 100644 --- a/lib/kernel/src/disk_log.hrl +++ b/lib/kernel/src/disk_log.hrl @@ -39,6 +39,7 @@  -define(MAX_FILES, 65000).  -define(MAX_BYTES, ((1 bsl 64) - 1)).  -define(MAX_CHUNK_SIZE, 65536). +-define(MAX_FWRITE_CACHE, 65536).  %% Object defines  -define(LOGMAGIC, <<1,2,3,4>>).  @@ -54,11 +55,10 @@  %% Types -- alphabetically  %%------------------------------------------------------------------------ --type dlog_byte()        :: [dlog_byte()] | byte().  -type dlog_format()      :: 'external' | 'internal'.  -type dlog_format_type() :: 'halt_ext' | 'halt_int' | 'wrap_ext' | 'wrap_int'.  -type dlog_head()        :: 'none' | {'ok', binary()} | mfa(). --type dlog_head_opt()    :: none | term() | binary() | [dlog_byte()]. +-type dlog_head_opt()    :: none | term() | iodata().  -type log()              :: term().  % XXX: refine  -type dlog_mode()        :: 'read_only' | 'read_write'.  -type dlog_name()        :: atom() | string(). diff --git a/lib/kernel/src/disk_log_1.erl b/lib/kernel/src/disk_log_1.erl index 2e61363aa6..d83c30f35f 100644 --- a/lib/kernel/src/disk_log_1.erl +++ b/lib/kernel/src/disk_log_1.erl @@ -1416,24 +1416,36 @@ open_truncate(FileName) ->  %%% Functions that access files, and throw on error.  --define(MAX, 16384). % bytes  -define(TIMEOUT, 2000). % ms  %% -> {Reply, cache()}; Reply = ok | Error -fwrite(#cache{c = []} = FdC, _FN, B, Size) -> +fwrite(FdC, _FN, _B, 0) -> +    {ok, FdC};  % avoid starting a timer for empty writes +fwrite(#cache{fd = Fd, c = C, sz = Sz} = FdC, FileName, B, Size) -> +    Sz1 = Sz + Size, +    C1 = cache_append(C, B), +    if Sz1 > ?MAX_FWRITE_CACHE -> +            write_cache(Fd, FileName, C1); +       true -> +            maybe_start_timer(C), +            {ok, FdC#cache{sz = Sz1, c = C1}} +    end. + +cache_append([], B) -> B; +cache_append(C, B) -> [C | B]. + +%% if the cache was empty, start timer (unless it's already running) +maybe_start_timer([]) ->      case get(write_cache_timer_is_running) of -        true ->  +        true ->              ok; -        _ ->  +        _ ->              put(write_cache_timer_is_running, true),              erlang:send_after(?TIMEOUT, self(), {self(), write_cache}),              ok -    end, -    {ok, FdC#cache{sz = Size, c = B}}; -fwrite(#cache{sz = Sz, c = C} = FdC, _FN, B, Size) when Sz < ?MAX -> -    {ok, FdC#cache{sz = Sz+Size, c = [C | B]}}; -fwrite(#cache{fd = Fd, c = C}, FileName, B, _Size) -> -    write_cache(Fd, FileName, [C | B]). +    end; +maybe_start_timer(_C) -> +    ok.  fwrite_header(Fd, B, Size) ->      {ok, #cache{fd = Fd, sz = Size, c = B}}. diff --git a/lib/kernel/src/erts_debug.erl b/lib/kernel/src/erts_debug.erl index 7b3f1e313a..ad92aafc2f 100644 --- a/lib/kernel/src/erts_debug.erl +++ b/lib/kernel/src/erts_debug.erl @@ -35,7 +35,8 @@           dump_monitors/1, dump_links/1, flat_size/1,           get_internal_state/1, instructions/0, lock_counters/1,           map_info/1, same/2, set_internal_state/2, -         size_shared/1, copy_shared/1]). +         size_shared/1, copy_shared/1, dirty_cpu/2, dirty_io/2, +	 dirty/3]).  -spec breakpoint(MFA, Flag) -> non_neg_integer() when        MFA :: {Module :: module(), @@ -182,6 +183,28 @@ same(_, _) ->  set_internal_state(_, _) ->      erlang:nif_error(undef). +-spec dirty_cpu(Term1, Term2) -> term() when +      Term1 :: term(), +      Term2 :: term(). + +dirty_cpu(_, _) -> +    erlang:nif_error(undef). + +-spec dirty_io(Term1, Term2) -> term() when +      Term1 :: term(), +      Term2 :: term(). + +dirty_io(_, _) -> +    erlang:nif_error(undef). + +-spec dirty(Term1, Term2, Term3) -> term() when +      Term1 :: term(), +      Term2 :: term(), +      Term3 :: term(). + +dirty(_, _, _) -> +    erlang:nif_error(undef). +  %%% End of BIFs  %% size(Term) diff --git a/lib/kernel/src/file.erl b/lib/kernel/src/file.erl index 58b601e456..1971df9038 100644 --- a/lib/kernel/src/file.erl +++ b/lib/kernel/src/file.erl @@ -397,25 +397,36 @@ write_file(Name, Bin) ->        Modes :: [mode()],        Reason :: posix() | badarg | terminated | system_limit. -write_file(Name, Bin, ModeList) when is_list(ModeList) -> -    case make_binary(Bin) of -	B when is_binary(B) -> -	    case open(Name, [binary, write |  -			     lists:delete(binary,  -					  lists:delete(write, ModeList))]) of -		{ok, Handle} -> -		    case write(Handle, B) of -			ok -> -			    close(Handle); -			E1 -> -			    _ = close(Handle), -			    E1 -		    end; -		E2 -> -		    E2 -	    end; -	E3 -> -	    E3 +write_file(Name, IOData, ModeList) when is_list(ModeList) -> +    case lists:member(raw, ModeList) of +        true -> +          %% For backwards compatibility of error messages +            try iolist_size(IOData) of +                _Size -> do_write_file(Name, IOData, ModeList) +            catch +                error:Error -> {error, Error} +            end; +        false -> +            case make_binary(IOData) of +                Bin when is_binary(Bin) -> +                    do_write_file(Name, Bin, ModeList); +                Error -> +                    Error +            end +    end. + +do_write_file(Name, IOData, ModeList) -> +    case open(Name, [binary, write | ModeList]) of +        {ok, Handle} -> +            case write(Handle, IOData) of +                ok -> +                    close(Handle); +                E1 -> +                    _ = close(Handle), +                    E1 +            end; +        E2 -> +            E2      end.  %% Obsolete, undocumented, local node only, don't use!. diff --git a/lib/kernel/src/global.erl b/lib/kernel/src/global.erl index 0c73ead7c5..5e8bc2ba5d 100644 --- a/lib/kernel/src/global.erl +++ b/lib/kernel/src/global.erl @@ -58,14 +58,18 @@  %%% In certain places in the server, calling io:format hangs everything,  %%% so we'd better use erlang:display/1.  %%% my_tracer is used in testsuites --define(trace(_), ok). +%% uncomment this if tracing is wanted +%%-define(DEBUG, true). +-ifdef(DEBUG). +-define(trace(T), erlang:display({format, node(), cs(), T})). +  cs() -> +     {_Big, Small, Tiny} = erlang:timestamp(), +     (Small rem 100) * 100 + (Tiny div 10000).  %-define(trace(T), (catch my_tracer ! {node(), {line,?LINE}, T})). - -%-define(trace(T), erlang:display({format, node(), cs(), T})). -%cs() -> -%    {_Big, Small, Tiny} = now(), -%    (Small rem 100) * 100 + (Tiny div 10000). +-else. +-define(trace(_), ok). +-endif.  %% These are the protocol versions:  %% Vsn 1 is the original protocol. diff --git a/lib/kernel/src/hipe_ext_format.hrl b/lib/kernel/src/hipe_ext_format.hrl index 102cb49a2b..05c678fdec 100644 --- a/lib/kernel/src/hipe_ext_format.hrl +++ b/lib/kernel/src/hipe_ext_format.hrl @@ -1,3 +1,15 @@ +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%%     http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +  %% hipe_x86_ext_format.hrl  %% Definitions for unified external object format  %% Currently: sparc, x86, amd64 diff --git a/lib/kernel/src/hipe_unified_loader.erl b/lib/kernel/src/hipe_unified_loader.erl index 087cceb5d8..f4c7c277ed 100644 --- a/lib/kernel/src/hipe_unified_loader.erl +++ b/lib/kernel/src/hipe_unified_loader.erl @@ -1,4 +1,15 @@  %% -*- erlang-indent-level: 2 -*- +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%%     http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License.  %% =======================================================================  %%  Filename : 	hipe_unified_loader.erl  %%  Module   :	hipe_unified_loader @@ -41,10 +52,11 @@  % I think the real solution would be to let BIF erlang:load_module/2 redirect all  % hipe calls to the module and thereby remove post_beam_load. +% SVERK: Can we remove -compile(no_native) now when post_beam_load is gone? +  -export([chunk_name/1,  	 %% Only the code and code_server modules may call the entries below!  	 load_native_code/3, -	 post_beam_load/1,  	 load_module/4,  	 load/3]). @@ -101,15 +113,13 @@ word_size(Architecture) ->  load_native_code(_Mod, _Bin, undefined) ->    no_native;  load_native_code(Mod, Bin, Architecture) when is_atom(Mod), is_binary(Bin) -> -  %% patch_to_emu(Mod),    case code:get_chunk(Bin, chunk_name(Architecture)) of      undefined -> no_native;      NativeCode when is_binary(NativeCode) ->        erlang:system_flag(multi_scheduling, block_normal),        try -        OldReferencesToPatch = patch_to_emu_step1(Mod), -        case load_module(Mod, NativeCode, Bin, OldReferencesToPatch, -                         Architecture) of +        put(hipe_patch_closures, false), +        case load_common(Mod, NativeCode, Bin, Architecture) of            bad_crc -> no_native;            Result -> Result          end @@ -120,22 +130,6 @@ load_native_code(Mod, Bin, Architecture) when is_atom(Mod), is_binary(Bin) ->  %%======================================================================== --spec post_beam_load([module()]) -> 'ok'. - -post_beam_load([])-> -  ok; -post_beam_load([_|_]=Mods) -> -  erlang:system_flag(multi_scheduling, block_normal), -  try -    _ = [patch_to_emu(Mod) || Mod <- Mods], -    ok -  after -    erlang:system_flag(multi_scheduling, unblock_normal) -  end, -  ok. - -%%======================================================================== -  version_check(Version, Mod) when is_atom(Mod) ->    Ver = ?VERSION_STRING(),    case Version < Ver of @@ -153,19 +147,12 @@ version_check(Version, Mod) when is_atom(Mod) ->  load_module(Mod, Bin, Beam, Architecture) ->    erlang:system_flag(multi_scheduling, block_normal),    try -    load_module_nosmp(Mod, Bin, Beam, Architecture) +    put(hipe_patch_closures, false), +    load_common(Mod, Bin, Beam, Architecture)    after      erlang:system_flag(multi_scheduling, unblock_normal)    end. -load_module_nosmp(Mod, Bin, Beam, Architecture) -> -  load_module(Mod, Bin, Beam, [], Architecture). - -load_module(Mod, Bin, Beam, OldReferencesToPatch, Architecture) -> -  ?debug_msg("************ Loading Module ~w ************\n",[Mod]), -  %% Loading a whole module, let the BEAM loader patch closures. -  put(hipe_patch_closures, false), -  load_common(Mod, Bin, Beam, OldReferencesToPatch, Architecture).  %%======================================================================== @@ -175,20 +162,17 @@ load_module(Mod, Bin, Beam, OldReferencesToPatch, Architecture) ->  load(Mod, Bin, Architecture) ->    erlang:system_flag(multi_scheduling, block_normal),    try -    load_nosmp(Mod, Bin, Architecture) +    ?debug_msg("********* Loading funs in module ~w *********\n",[Mod]), +    %% Loading just some functions in a module; patch closures separately. +    put(hipe_patch_closures, true), +    load_common(Mod, Bin, [], Architecture)    after      erlang:system_flag(multi_scheduling, unblock_normal)    end. -load_nosmp(Mod, Bin, Architecture) -> -  ?debug_msg("********* Loading funs in module ~w *********\n",[Mod]), -  %% Loading just some functions in a module; patch closures separately. -  put(hipe_patch_closures, true), -  load_common(Mod, Bin, [], [], Architecture). -  %%------------------------------------------------------------------------ -load_common(Mod, Bin, Beam, OldReferencesToPatch, Architecture) -> +load_common(Mod, Bin, Beam, Architecture) ->    %% Unpack the binary.    [{Version, CheckSum},     ConstAlign, ConstSize, ConstMap, LabelMap, ExportMap, @@ -215,29 +199,31 @@ load_common(Mod, Bin, Beam, OldReferencesToPatch, Architecture) ->        put(closures_to_patch, []),        WordSize = word_size(Architecture),        WriteWord = write_word_fun(WordSize), +      LoaderState = hipe_bifs:alloc_loader_state(Mod), +      put(hipe_loader_state, LoaderState),        %% Create data segment        {ConstAddr,ConstMap2} = -	create_data_segment(ConstAlign, ConstSize, ConstMap, WriteWord), +	create_data_segment(ConstAlign, ConstSize, ConstMap, WriteWord, +			    LoaderState),        %% Find callees for which we may need trampolines.        CalleeMFAs = find_callee_mfas(Refs, Architecture),        %% Write the code to memory.        {CodeAddress,Trampolines} = -	enter_code(CodeSize, CodeBinary, CalleeMFAs, Mod, Beam), +	enter_code(CodeSize, CodeBinary, CalleeMFAs, LoaderState),        %% Construct CalleeMFA-to-trampoline mapping.        TrampolineMap = mk_trampoline_map(CalleeMFAs, Trampolines,                                          Architecture),        %% Patch references to code labels in data seg.        ok = patch_consts(LabelMap, ConstAddr, CodeAddress, WriteWord), +        %% Find out which functions are being loaded (and where). -      %% Note: Addresses are sorted descending. -      {MFAs,Addresses} = exports(ExportMap, CodeAddress), -      %% Remove references to old versions of the module. -      ReferencesToPatch = get_refs_from(MFAs, []), -      %% io:format("References to patch: ~w~n", [ReferencesToPatch]), -      ok = remove_refs_from(MFAs), +      %% Note: FunDefs are sorted descending address order. +      FunDefs = exports(ExportMap, CodeAddress), +        %% Patch all dynamic references in the code.        %%  Function calls, Atoms, Constants, System calls -      ok = patch(Refs, CodeAddress, ConstMap2, Addresses, TrampolineMap), + +      ok = patch(Refs, CodeAddress, ConstMap2, FunDefs, TrampolineMap),        %% Tell the system where the loaded funs are.         %%  (patches the BEAM code to redirect to native.) @@ -250,25 +236,22 @@ load_common(Mod, Bin, Beam, OldReferencesToPatch, Architecture) ->  	  lists:foreach(fun({FE, DestAddress}) ->  			    hipe_bifs:set_native_address_in_fe(FE, DestAddress)  			end, erase(closures_to_patch)), -	  export_funs(Addresses), +          ok = hipe_bifs:commit_patch_load(LoaderState), +	  set_beam_call_traps(FunDefs),  	  ok;  	BeamBinary when is_binary(BeamBinary) ->  	  %% Find all closures in the code.  	  [] = erase(closures_to_patch),	%Clean up, assertion.  	  ClosurePatches = find_closure_patches(Refs),  	  AddressesOfClosuresToPatch = -	    calculate_addresses(ClosurePatches, CodeAddress, Addresses), -	  export_funs(Addresses), -	  export_funs(Mod, MD5, BeamBinary, -                      Addresses, AddressesOfClosuresToPatch) +	    calculate_addresses(ClosurePatches, CodeAddress, FunDefs), +	  export_funs(FunDefs), +	  make_beam_stub(Mod, LoaderState, MD5, BeamBinary, FunDefs, +                         AddressesOfClosuresToPatch)        end, -      %% Redirect references to the old module to the new module's BEAM stub. -      patch_to_emu_step2(OldReferencesToPatch), -      %% Patch referring functions to call the new function -      %% The call to export_funs/1 above updated the native addresses -      %% for the targets, so passing 'Addresses' is not needed. -      redirect(ReferencesToPatch), +        %% Final clean up. +      _ = erase(hipe_loader_state),        _ = erase(hipe_patch_closures),        _ = erase(hipe_assert_code_area),        ?debug_msg("****************Loader Finished****************\n", []), @@ -371,31 +354,31 @@ trampoline_map_lookup(Primop, Map) ->  		 is_exported :: boolean()}).  exports(ExportMap, BaseAddress) -> -  exports(ExportMap, BaseAddress, [], []). +  exports(ExportMap, BaseAddress, []). -exports([Offset,M,F,A,IsClosure,IsExported|Rest], BaseAddress, MFAs, Addresses) -> +exports([Offset,M,F,A,IsClosure,IsExported|Rest], BaseAddress, FunDefs) ->    case IsExported andalso erlang:is_builtin(M, F, A) of      true -> -      exports(Rest, BaseAddress, MFAs, Addresses); +      exports(Rest, BaseAddress, FunDefs);      _false ->        MFA = {M,F,A},        Address = BaseAddress + Offset,        FunDef = #fundef{address=Address, mfa=MFA, is_closure=IsClosure,  		       is_exported=IsExported}, -      exports(Rest, BaseAddress, [MFA|MFAs], [FunDef|Addresses]) +      exports(Rest, BaseAddress, [FunDef|FunDefs])    end; -exports([], _, MFAs, Addresses) -> -  {MFAs, Addresses}. +exports([], _, FunDefs) -> +  FunDefs.  mod({M,_F,_A}) -> M.  %%------------------------------------------------------------------------ -calculate_addresses(PatchOffsets, Base, Addresses) -> +calculate_addresses(PatchOffsets, Base, FunDefs) ->    RemoteOrLocal = local, % closure code refs are local    [{Data,      offsets_to_addresses(Offsets, Base), -    get_native_address(DestMFA, Addresses, RemoteOrLocal)} ||  +    get_native_address(DestMFA, FunDefs, RemoteOrLocal)} ||      {{DestMFA,_,_}=Data,Offsets} <- PatchOffsets].  offsets_to_addresses(Os, Base) -> @@ -424,9 +407,9 @@ find_closure_refs([], Refs) ->  %%------------------------------------------------------------------------ -export_funs([FunDef | Addresses]) -> +set_beam_call_traps([FunDef | FunDefs]) ->    #fundef{address=Address, mfa=MFA, is_closure=IsClosure, -	  is_exported=IsExported} = FunDef, +	  is_exported=_IsExported} = FunDef,    ?IF_DEBUG({M,F,A} = MFA, no_debug),    ?IF_DEBUG(       case IsClosure of @@ -437,21 +420,38 @@ export_funs([FunDef | Addresses]) ->  	 ?debug_msg("LINKING: ~w:~w/~w to closure (0x~.16b)\n",  		    [M,F,A, Address])       end, no_debug), -  hipe_bifs:set_funinfo_native_address(MFA, Address, IsExported),    hipe_bifs:set_native_address(MFA, Address, IsClosure), -  export_funs(Addresses); +  set_beam_call_traps(FunDefs); +set_beam_call_traps([]) -> +  ok. + +export_funs([FunDef | FunDefs]) -> +  #fundef{address=Address, mfa=MFA, is_closure=_IsClosure, +	  is_exported=IsExported} = FunDef, +  ?IF_DEBUG({M,F,A} = MFA, no_debug), +  ?IF_DEBUG( +     case _IsClosure of +       false -> +	 ?debug_msg("LINKING: ~w:~w/~w to (0x~.16b)\n", +		    [M,F,A, Address]); +       true -> +	 ?debug_msg("LINKING: ~w:~w/~w to closure (0x~.16b)\n", +		    [M,F,A, Address]) +     end, no_debug), +  hipe_bifs:set_funinfo_native_address(MFA, Address, IsExported), +  export_funs(FunDefs);  export_funs([]) ->    ok. -export_funs(Mod, MD5, Beam, Addresses, ClosuresToPatch) -> -  Fs = [{F,A,Address} || #fundef{address=Address, mfa={_M,F,A}} <- Addresses], -  Mod = code:make_stub_module(Mod, Beam, {Fs,ClosuresToPatch,MD5}), +make_beam_stub(Mod, LoaderState, MD5, Beam, FunDefs, ClosuresToPatch) -> +  Fs = [{F,A,Address} || #fundef{address=Address, mfa={_M,F,A}} <- FunDefs], +  Mod = code:make_stub_module(LoaderState, Beam, {Fs,ClosuresToPatch,MD5}),    ok.  %%========================================================================  %% Patching   %%  @spec patch(refs(), BaseAddress::integer(), ConstAndZone::term(), -%%              Addresses::term(), TrampolineMap::term()) -> 'ok'. +%%              FunDefs::term(), TrampolineMap::term()) -> 'ok'.  %%   @type refs()=[{RefType::integer(), Reflist::reflist()} | refs()]  %%  %%   @type reflist()=   [{Data::term(), Offsets::offests()}|reflist()] @@ -463,39 +463,39 @@ export_funs(Mod, MD5, Beam, Addresses, ClosuresToPatch) ->  %%  (we use this to look up the address of a referred function only once).  %% -patch([{Type,SortedRefs}|Rest], CodeAddress, ConstMap2, Addresses, TrampolineMap) -> +patch([{Type,SortedRefs}|Rest], CodeAddress, ConstMap2, FunDefs, TrampolineMap) ->    ?debug_msg("Patching ~w at [~w+offset] with ~w\n",  	     [Type,CodeAddress,SortedRefs]),    case ?EXT2PATCH_TYPE(Type) of       call_local ->  -      patch_call(SortedRefs, CodeAddress, Addresses, 'local', TrampolineMap); +      patch_call(SortedRefs, CodeAddress, FunDefs, 'local', TrampolineMap);      call_remote -> -      patch_call(SortedRefs, CodeAddress, Addresses, 'remote', TrampolineMap); +      patch_call(SortedRefs, CodeAddress, FunDefs, 'remote', TrampolineMap);      Other ->  -      patch_all(Other, SortedRefs, CodeAddress, {ConstMap2,CodeAddress}, Addresses) +      patch_all(Other, SortedRefs, CodeAddress, {ConstMap2,CodeAddress}, FunDefs)    end, -  patch(Rest, CodeAddress, ConstMap2, Addresses, TrampolineMap); +  patch(Rest, CodeAddress, ConstMap2, FunDefs, TrampolineMap);  patch([], _, _, _, _) -> ok.  %%----------------------------------------------------------------  %% Handle a 'call_local' or 'call_remote' patch.  %% -patch_call([{DestMFA,Offsets}|SortedRefs], BaseAddress, Addresses, RemoteOrLocal, TrampolineMap) -> +patch_call([{DestMFA,Offsets}|SortedRefs], BaseAddress, FunDefs, RemoteOrLocal, TrampolineMap) ->    case bif_address(DestMFA) of      false -> -      %% Previous code used mfa_to_address(DestMFA, Addresses) +      %% Previous code used mfa_to_address(DestMFA, FunDefs)        %% here for local calls. That is wrong because even local -      %% destinations may not be present in Addresses: they may +      %% destinations may not be present in FunDefs: they may        %% not have been compiled yet, or they may be BEAM-only        %% functions (e.g. module_info). -      DestAddress = get_native_address(DestMFA, Addresses, RemoteOrLocal), +      DestAddress = get_native_address(DestMFA, FunDefs, RemoteOrLocal),        Trampoline = trampoline_map_get(DestMFA, TrampolineMap), -      patch_mfa_call_list(Offsets, BaseAddress, DestMFA, DestAddress, Addresses, RemoteOrLocal, Trampoline); +      patch_mfa_call_list(Offsets, BaseAddress, DestMFA, DestAddress, FunDefs, RemoteOrLocal, Trampoline);      BifAddress when is_integer(BifAddress) ->        Trampoline = trampoline_map_lookup(DestMFA, TrampolineMap),        patch_bif_call_list(Offsets, BaseAddress, BifAddress, Trampoline)    end, -  patch_call(SortedRefs, BaseAddress, Addresses, RemoteOrLocal, TrampolineMap); +  patch_call(SortedRefs, BaseAddress, FunDefs, RemoteOrLocal, TrampolineMap);  patch_call([], _, _, _, _) ->    ok. @@ -506,49 +506,48 @@ patch_bif_call_list([Offset|Offsets], BaseAddress, BifAddress, Trampoline) ->    patch_bif_call_list(Offsets, BaseAddress, BifAddress, Trampoline);  patch_bif_call_list([], _, _, _) -> ok. -patch_mfa_call_list([Offset|Offsets], BaseAddress, DestMFA, DestAddress, Addresses, RemoteOrLocal, Trampoline) -> +patch_mfa_call_list([Offset|Offsets], BaseAddress, DestMFA, DestAddress, FunDefs, RemoteOrLocal, Trampoline) ->    CallAddress = BaseAddress+Offset, -  add_ref(DestMFA, CallAddress, Addresses, 'call', Trampoline, RemoteOrLocal), +  add_ref(DestMFA, CallAddress, FunDefs, 'call', Trampoline, RemoteOrLocal),    ?ASSERT(assert_local_patch(CallAddress)),    patch_call_insn(CallAddress, DestAddress, Trampoline), -  patch_mfa_call_list(Offsets, BaseAddress, DestMFA, DestAddress, Addresses, RemoteOrLocal, Trampoline); +  patch_mfa_call_list(Offsets, BaseAddress, DestMFA, DestAddress, FunDefs, RemoteOrLocal, Trampoline);  patch_mfa_call_list([], _, _, _, _, _, _) -> ok.  patch_call_insn(CallAddress, DestAddress, Trampoline) -> -  %% This assertion is false when we're called from redirect/2. -  %% ?ASSERT(assert_local_patch(CallAddress)), +  ?ASSERT(assert_local_patch(CallAddress)),    hipe_bifs:patch_call(CallAddress, DestAddress, Trampoline).  %% ____________________________________________________________________  %%  -patch_all(Type, [{Dest,Offsets}|Rest], BaseAddress, ConstAndZone, Addresses)-> -  patch_all_offsets(Type, Dest, Offsets, BaseAddress, ConstAndZone, Addresses), -  patch_all(Type, Rest, BaseAddress, ConstAndZone, Addresses); +patch_all(Type, [{Dest,Offsets}|Rest], BaseAddress, ConstAndZone, FunDefs)-> +  patch_all_offsets(Type, Dest, Offsets, BaseAddress, ConstAndZone, FunDefs), +  patch_all(Type, Rest, BaseAddress, ConstAndZone, FunDefs);  patch_all(_, [], _, _, _) -> ok.  patch_all_offsets(Type, Data, [Offset|Offsets], BaseAddress, -		  ConstAndZone, Addresses) -> +		  ConstAndZone, FunDefs) ->    ?debug_msg("Patching ~w at [~w+~w] with ~w\n",  	     [Type,BaseAddress,Offset, Data]),    Address = BaseAddress + Offset, -  patch_offset(Type, Data, Address, ConstAndZone, Addresses), +  patch_offset(Type, Data, Address, ConstAndZone, FunDefs),    ?debug_msg("Patching done\n",[]), -  patch_all_offsets(Type, Data, Offsets, BaseAddress, ConstAndZone, Addresses); +  patch_all_offsets(Type, Data, Offsets, BaseAddress, ConstAndZone, FunDefs);  patch_all_offsets(_, _, [], _, _, _) -> ok.  %%----------------------------------------------------------------  %% Handle any patch type except 'call_local' or 'call_remote'.  %% -patch_offset(Type, Data, Address, ConstAndZone, Addresses) -> +patch_offset(Type, Data, Address, ConstAndZone, FunDefs) ->    case Type of      load_address -> -      patch_load_address(Data, Address, ConstAndZone, Addresses); +      patch_load_address(Data, Address, ConstAndZone, FunDefs);      load_atom ->        Atom = Data,        patch_atom(Address, Atom);      sdesc -> -      patch_sdesc(Data, Address, ConstAndZone, Addresses); +      patch_sdesc(Data, Address, ConstAndZone, FunDefs);      x86_abs_pcrel ->        patch_instr(Address, Data, x86_abs_pcrel)      %% _ -> @@ -561,37 +560,38 @@ patch_atom(Address, Atom) ->    patch_instr(Address, hipe_bifs:atom_to_word(Atom), atom).  patch_sdesc(?STACK_DESC(SymExnRA, FSize, Arity, Live), -	    Address, {_ConstMap2,CodeAddress}, _Addresses) -> +	    Address, {_ConstMap2,CodeAddress}, FunDefs) ->    ExnRA =      case SymExnRA of        [] -> 0; % No catch        LabelOffset -> CodeAddress + LabelOffset      end,    ?ASSERT(assert_local_patch(Address)), -  DBG_MFA = ?IF_DEBUG(address_to_mfa_lth(Address, _Addresses), {undefined,undefined,0}), -  hipe_bifs:enter_sdesc({Address, ExnRA, FSize, Arity, Live, DBG_MFA}). +  MFA = address_to_mfa_lth(Address, FunDefs), +  hipe_bifs:enter_sdesc({Address, ExnRA, FSize, Arity, Live, MFA}, +		       get(hipe_loader_state)).  %%----------------------------------------------------------------  %% Handle a 'load_address'-type patch.  %% -patch_load_address(Data, Address, ConstAndZone, Addresses) -> +patch_load_address(Data, Address, ConstAndZone, FunDefs) ->    case Data of      {local_function,DestMFA} -> -      patch_load_mfa(Address, DestMFA, Addresses, 'local'); +      patch_load_mfa(Address, DestMFA, FunDefs, 'local');      {remote_function,DestMFA} -> -      patch_load_mfa(Address, DestMFA, Addresses, 'remote'); +      patch_load_mfa(Address, DestMFA, FunDefs, 'remote');      {constant,Name} ->        {ConstMap2,_CodeAddress} = ConstAndZone,        ConstAddress = find_const(Name, ConstMap2),        patch_instr(Address, ConstAddress, constant);      {closure,{DestMFA,Uniq,Index}} -> -      patch_closure(DestMFA, Uniq, Index, Address, Addresses); +      patch_closure(DestMFA, Uniq, Index, Address, FunDefs);      {c_const,CConst} ->        patch_instr(Address, bif_address(CConst), c_const)    end. -patch_closure(DestMFA, Uniq, Index, Address, Addresses) -> +patch_closure(DestMFA, Uniq, Index, Address, FunDefs) ->    case get(hipe_patch_closures) of      false ->        []; % This is taken care of when registering the module. @@ -602,7 +602,7 @@ patch_closure(DestMFA, Uniq, Index, Address, Addresses) ->        %% address into the fun entry to ensure that the native code cannot        %% be called until it has been completely fixed up.        RemoteOrLocal = local, % closure code refs are local -      DestAddress = get_native_address(DestMFA, Addresses, RemoteOrLocal), +      DestAddress = get_native_address(DestMFA, FunDefs, RemoteOrLocal),        BEAMAddress = hipe_bifs:fun_to_address(DestMFA),        FE = hipe_bifs:get_fe(mod(DestMFA), {Uniq, Index, BEAMAddress}),        put(closures_to_patch, [{FE,DestAddress}|get(closures_to_patch)]), @@ -616,17 +616,17 @@ patch_closure(DestMFA, Uniq, Index, Address, Addresses) ->  %% Patch an instruction loading the address of an MFA.  %% RemoteOrLocal ::= 'remote' | 'local'  %% -patch_load_mfa(CodeAddress, DestMFA, Addresses, RemoteOrLocal) -> +patch_load_mfa(CodeAddress, DestMFA, FunDefs, RemoteOrLocal) -> +  ?ASSERT(assert_local_patch(CodeAddress)),    DestAddress =      case bif_address(DestMFA) of        false -> -	NativeAddress = get_native_address(DestMFA, Addresses, RemoteOrLocal), -	add_ref(DestMFA, CodeAddress, Addresses, 'load_mfa', [], RemoteOrLocal), +	NativeAddress = get_native_address(DestMFA, FunDefs, RemoteOrLocal), +	add_ref(DestMFA, CodeAddress, FunDefs, 'load_mfa', [], RemoteOrLocal),  	NativeAddress;        BifAddress when is_integer(BifAddress) ->  	BifAddress      end, -  ?ASSERT(assert_local_patch(CodeAddress)),    patch_instr(CodeAddress, DestAddress, 'load_mfa').  %%---------------------------------------------------------------- @@ -702,9 +702,9 @@ bif_address(Name) when is_atom(Name) ->  %% memory, and produces a ConstMap2 mapping each constant's ConstNo to  %% its runtime address, tagged if the constant is a term.  %% -create_data_segment(DataAlign, DataSize, DataList, WriteWord) -> +create_data_segment(DataAlign, DataSize, DataList, WriteWord, LoaderState) ->    %%io:format("create_data_segment: \nDataAlign: ~p\nDataSize: ~p\nDataList: ~p\n",[DataAlign,DataSize,DataList]), -  DataAddress = hipe_bifs:alloc_data(DataAlign, DataSize), +  DataAddress = hipe_bifs:alloc_data(DataAlign, DataSize, LoaderState),    enter_data(DataList, [], DataAddress, DataSize, WriteWord).  enter_data(List, ConstMap2, DataAddress, DataSize, WriteWord) -> @@ -772,7 +772,7 @@ find_const(ConstNo, []) ->  %%----------------------------------------------------------------  %% Record that the code at address 'Address' has a reference  %% of type 'RefType' ('call' or 'load_mfa') to 'CalleeMFA'. -%% 'Addresses' must be an address-descending list from exports/2. +%% 'FunDefs' must be an address-descending list from exports/2.  %%  %% If 'RefType' is 'call', then 'Trampoline' may be the address  %% of a stub branching to 'CalleeMFA', where the stub is reachable @@ -781,34 +781,29 @@ find_const(ConstNo, []) ->  %% RemoteOrLocal ::= 'remote' | 'local'.  %% -%% -%% -record(ref, {caller_mfa, address, ref_type, trampoline, remote_or_local}). -%% +add_ref(CalleeMFA, Address, FunDefs, RefType, Trampoline, RemoteOrLocal) -> +  CallerMFA = address_to_mfa_lth(Address, FunDefs), +  case RemoteOrLocal of +    local -> +      %% assert that the callee and caller are from the same module +      {M,_,_} = CalleeMFA, +      {M,_,_} = CallerMFA, +      ok; +    remote -> +      hipe_bifs:add_ref(CalleeMFA, {CallerMFA,Address,RefType,Trampoline, +				    get(hipe_loader_state)}) +  end. -add_ref(CalleeMFA, Address, Addresses, RefType, Trampoline, RemoteOrLocal) -> -  CallerMFA = address_to_mfa_lth(Address, Addresses), -  %% just a sanity assertion below -  true = case RemoteOrLocal of -	   local -> -	     {M1,_,_} = CalleeMFA, -	     {M2,_,_} = CallerMFA, -	     M1 =:= M2; -	   remote -> -	     true -	 end, -  %% io:format("Adding ref ~w\n",[{CallerMFA, CalleeMFA, Address, RefType}]), -  hipe_bifs:add_ref(CalleeMFA, {CallerMFA,Address,RefType,Trampoline,RemoteOrLocal}). - -% For FunDefs sorted from low to high addresses +%% For FunDefs sorted from low to high addresses  address_to_mfa_lth(Address, FunDefs) -> -    case address_to_mfa_lth(Address, FunDefs, false) of -	false -> -	    ?error_msg("Local adddress not found ~w\n",[Address]), -	    exit({?MODULE, local_address_not_found}); -	MFA -> -	    MFA -    end. -     +  case address_to_mfa_lth(Address, FunDefs, false) of +    false -> +      ?error_msg("Local adddress not found ~w\n",[Address]), +      exit({?MODULE, local_address_not_found}); +    MFA -> +      MFA +  end. +  address_to_mfa_lth(Address, [#fundef{address=Adr, mfa=MFA}|Rest], Prev) ->    if Address < Adr ->   	  Prev; @@ -818,107 +813,33 @@ address_to_mfa_lth(Address, [#fundef{address=Adr, mfa=MFA}|Rest], Prev) ->  address_to_mfa_lth(_Address, [], Prev) ->       Prev. -% For FunDefs sorted from high to low addresses +%% For FunDefs sorted from high to low addresses  %% address_to_mfa_htl(Address, [#fundef{address=Adr, mfa=MFA}|_Rest]) when Address >= Adr -> MFA;  %% address_to_mfa_htl(Address, [_ | Rest]) -> address_to_mfa_htl(Address, Rest);  %% address_to_mfa_htl(Address, []) ->   %%   ?error_msg("Local adddress not found ~w\n",[Address]),  %%   exit({?MODULE, local_address_not_found}). -%%---------------------------------------------------------------- -%% Change callers of the given module to instead trap to BEAM. -%% load_native_code/3 calls this just before loading native code. -%% -patch_to_emu(Mod) -> -  patch_to_emu_step2(patch_to_emu_step1(Mod)). - -%% Step 1 must occur before the loading of native code updates -%% references information or creates a new BEAM stub module. -patch_to_emu_step1(Mod) -> -  case is_loaded(Mod) of -    true -> -      %% Get exported functions -      MFAs = [{Mod,Fun,Arity} || {Fun,Arity} <- Mod:module_info(exports)], -      %% get_refs_from/2 only finds references from compiled static -      %% call sites to the module, but some native address entries -      %% were added as the result of dynamic apply calls. We must -      %% purge them too, but we have no explicit record of them. -      %% Therefore invalidate all native addresses for the module. -      hipe_bifs:invalidate_funinfo_native_addresses(MFAs), -      %% Find all call sites that call these MFAs. As a side-effect, -      %% create native stubs for any MFAs that are referred. -      ReferencesToPatch = get_refs_from(MFAs, []), -      ok = remove_refs_from(MFAs), -      ReferencesToPatch; -    false -> -      %% The first time we load the module, no redirection needs to be done. -      [] -  end. - -%% Step 2 must occur after the new BEAM stub module is created. -patch_to_emu_step2(ReferencesToPatch) -> -  redirect(ReferencesToPatch). - --spec is_loaded(Module::atom()) -> boolean(). -%% @doc Checks whether a module is loaded or not. -is_loaded(M) when is_atom(M) -> -  try hipe_bifs:fun_to_address({M,module_info,0}) of -    I when is_integer(I) -> true -  catch _:_ -> false -  end. - -%%-------------------------------------------------------------------- -%% Given a list of MFAs, tag them with their referred_from references. -%% The resulting {MFA,Refs} list is later passed to redirect/1, once -%% the MFAs have been bound to (possibly new) native-code addresses. -%% -get_refs_from(MFAs, []) -> -  mark_referred_from(MFAs), -  MFAs. - -mark_referred_from(MFAs) -> -  lists:foreach(fun(MFA) -> hipe_bifs:mark_referred_from(MFA) end, MFAs). - -%%-------------------------------------------------------------------- -%% Given a list of MFAs with referred_from references, update their -%% callers to refer to their new native-code addresses. -%% -%% The {MFA,Refs} list must come from get_refs_from/2. -%% -redirect(MFAs) -> -  lists:foreach(fun(MFA) -> hipe_bifs:redirect_referred_from(MFA) end, MFAs). - -%%-------------------------------------------------------------------- -%% Given a list of MFAs, remove all referred_from references having -%% any of them as CallerMFA. -%% -%% This is the only place using refers_to. Whenever a reference is -%% added from CallerMFA to CalleeMFA, CallerMFA is added to CalleeMFA's -%% referred_from list, and CalleeMFA is added to CallerMFA's refers_to -%% list. The refers_to list is used here to find the CalleeMFAs whose -%% referred_from lists should be updated. -%% -remove_refs_from(MFAs) -> -  lists:foreach(fun(MFA) -> hipe_bifs:remove_refs_from(MFA) end, MFAs).  %%--------------------------------------------------------------------  %% To find the native code of an MFA we need to look in 3 places: -%%  1. If it is compiled now look in the Addresses data structure. +%%  1. If it is compiled now look in the FunDefs data structure.  %%  2. Then look in native_addresses from module info.   %%  3. Then (the function might have been singled compiled) look in  %%      hipe_funinfo  %%  If all else fails create a native stub for the MFA  -get_native_address(MFA, Addresses, RemoteOrLocal) -> -  case mfa_to_address(MFA, Addresses, RemoteOrLocal) of +get_native_address(MFA, FunDefs, RemoteOrLocal) -> +  case mfa_to_address(MFA, FunDefs, RemoteOrLocal) of      Adr when is_integer(Adr) -> Adr;      false -> -      IsRemote =  	case RemoteOrLocal of -	  remote -> true; -	  local -> false -	end, -      hipe_bifs:find_na_or_make_stub(MFA, IsRemote) +	  remote -> +	    hipe_bifs:find_na_or_make_stub(MFA); +	  local -> +	    ?error_msg("Local function ~p not found\n",[MFA]), +	    exit({function_not_found,MFA}) +	end    end.  mfa_to_address(MFA, [#fundef{address=Adr, mfa=MFA, @@ -958,12 +879,10 @@ assert_local_patch(Address) when is_integer(Address) ->  %% ____________________________________________________________________  %%  -%% Beam: nil() | binary()  (used as a flag) - -enter_code(CodeSize, CodeBinary, CalleeMFAs, Mod, Beam) -> +enter_code(CodeSize, CodeBinary, CalleeMFAs, LoaderState) ->    true = byte_size(CodeBinary) =:= CodeSize, -  hipe_bifs:update_code_size(Mod, Beam, CodeSize), -  {CodeAddress,Trampolines} = hipe_bifs:enter_code(CodeBinary, CalleeMFAs), +  {CodeAddress,Trampolines} = hipe_bifs:enter_code(CodeBinary, CalleeMFAs, +						   LoaderState),    ?init_assert_patch(CodeAddress, byte_size(CodeBinary)),    {CodeAddress,Trampolines}. diff --git a/lib/kernel/src/inet_db.erl b/lib/kernel/src/inet_db.erl index 465cec1b45..6cbb6ac2da 100644 --- a/lib/kernel/src/inet_db.erl +++ b/lib/kernel/src/inet_db.erl @@ -1379,7 +1379,8 @@ cache_rr(_Db, Cache, RR) ->      ets:insert(Cache, RR).  times() -> -    erlang:convert_time_unit(erlang:monotonic_time() - erlang:system_info(start_time),native,seconds). +    erlang:convert_time_unit(erlang:monotonic_time() - erlang:system_info(start_time), +			     native, second).  %% lookup and remove old entries diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src index d184223524..4d08a55c7c 100644 --- a/lib/kernel/src/kernel.app.src +++ b/lib/kernel/src/kernel.app.src @@ -118,6 +118,6 @@    {applications, []},    {env, [{error_logger, tty}]},    {mod, {kernel, []}}, -  {runtime_dependencies, ["erts-8.1", "stdlib-3.0", "sasl-3.0"]} +  {runtime_dependencies, ["erts-9.0", "stdlib-3.0", "sasl-3.0"]}   ]  }. diff --git a/lib/kernel/src/kernel.appup.src b/lib/kernel/src/kernel.appup.src index 82cf73cbda..b505524471 100644 --- a/lib/kernel/src/kernel.appup.src +++ b/lib/kernel/src/kernel.appup.src @@ -18,9 +18,7 @@  %% %CopyrightEnd%  {"%VSN%",   %% Up from - max one major revision back - [{<<"5\\.[0-1](\\.[0-9]+)*">>,[restart_new_emulator]},   % OTP-19.* -  {<<"4\\.[0-2](\\.[0-9]+)*">>,[restart_new_emulator]}],  % OTP-18.* + [{<<"5\\.[0-1](\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-19.*   %% Down to - max one major revision back - [{<<"5\\.[0-1](\\.[0-9]+)*">>,[restart_new_emulator]},  % OTP-19.* -  {<<"4\\.[0-2](\\.[0-9]+)*">>,[restart_new_emulator]}]  % OTP-18.* + [{<<"5\\.[0-1](\\.[0-9]+)*">>,[restart_new_emulator]}]  % OTP-19.*  }. diff --git a/lib/kernel/src/net_kernel.erl b/lib/kernel/src/net_kernel.erl index 0c679e7349..0a9f9316b0 100644 --- a/lib/kernel/src/net_kernel.erl +++ b/lib/kernel/src/net_kernel.erl @@ -26,15 +26,13 @@  %%-define(dist_debug, true). -%-define(DBG,erlang:display([?MODULE,?LINE])). -  -ifdef(dist_debug).  -define(debug(Term), erlang:display(Term)).  -else.  -define(debug(Term), ok).  -endif. --ifdef(DEBUG). +-ifdef(dist_debug).  -define(connect_failure(Node,Term),  	io:format("Net Kernel 2: Failed connection to node ~p, reason ~p~n",  		  [Node,Term])). | 
