summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexey Lebedeff <alebedev@mirantis.com>2016-05-20 15:43:35 +0300
committerAlexey Lebedeff <alebedev@mirantis.com>2016-05-20 17:39:28 +0300
commit4dd4fd61f4e55bff38ec1725fb9c0df7437ba53f (patch)
tree5317398061d980fa27d37c58f6df43b673823944
parent776dd2423f8e64ad7e387a7bb8e3b34e484f3c9d (diff)
downloadrabbitmq-server-git-4dd4fd61f4e55bff38ec1725fb9c0df7437ba53f.tar.gz
Allow caching HiPE-compilation results
That way HiPE compilation can be performed during package installation and will not waste time during every startup. rabbit_hipe is refactored to support both modes of compilation - during every server startup or separately with caching in the filesystem.
-rw-r--r--docs/rabbitmqctl.1.xml27
-rwxr-xr-x[-rw-r--r--]scripts/rabbitmq-env1
-rwxr-xr-xscripts/rabbitmq-server6
-rw-r--r--src/rabbit_control_main.erl13
-rw-r--r--src/rabbit_file.erl10
-rw-r--r--src/rabbit_hipe.erl89
6 files changed, 121 insertions, 25 deletions
diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml
index 1ecf8d4d3a..ec864af6cf 100644
--- a/docs/rabbitmqctl.1.xml
+++ b/docs/rabbitmqctl.1.xml
@@ -290,6 +290,33 @@
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><cmdsynopsis><command>hipe_compile</command> <arg choice="req"><replaceable>directory</replaceable></arg></cmdsynopsis></term>
+ <listitem>
+ <para>
+ Performs HiPE-compilation and caches resulting
+ .beam-files in the given directory.
+ </para>
+ <para>
+ Parent directories are created if necessary. Any
+ existing <command>.beam</command> files from the
+ directory are automatically deleted prior to
+ compilation.
+ </para>
+ <para>
+ To use this precompiled files, you should set
+ <command>RABBITMQ_SERVER_CODE_PATH</command> environment
+ variable to directory specified in
+ <command>hipe_compile</command> invokation.
+ </para>
+ <para role="example-prefix">For example:</para>
+ <screen role="example">rabbitmqctl hipe_compile /tmp/rabbit-hipe/ebin</screen>
+ <para role="example">
+ HiPE-compiles modules and stores them to /tmp/rabbit-hipe/ebin directory.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect2>
diff --git a/scripts/rabbitmq-env b/scripts/rabbitmq-env
index 35239620ca..def47ebd27 100644..100755
--- a/scripts/rabbitmq-env
+++ b/scripts/rabbitmq-env
@@ -177,6 +177,7 @@ DEFAULT_NODE_PORT=5672
[ "x" = "x$RABBITMQ_MNESIA_BASE" ] && RABBITMQ_MNESIA_BASE=${MNESIA_BASE}
[ "x" = "x$RABBITMQ_SERVER_START_ARGS" ] && RABBITMQ_SERVER_START_ARGS=${SERVER_START_ARGS}
[ "x" = "x$RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS" ] && RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS=${SERVER_ADDITIONAL_ERL_ARGS}
+[ "x" = "x$RABBITMQ_SERVER_CODE_PATH" ] && RABBITMQ_SERVER_CODE_PATH=${SERVER_CODE_PATH}
[ "x" = "x$RABBITMQ_MNESIA_DIR" ] && RABBITMQ_MNESIA_DIR=${MNESIA_DIR}
[ "x" = "x$RABBITMQ_MNESIA_DIR" ] && RABBITMQ_MNESIA_DIR=${RABBITMQ_MNESIA_BASE}/${RABBITMQ_NODENAME}
diff --git a/scripts/rabbitmq-server b/scripts/rabbitmq-server
index ab2975feb1..74337311cd 100755
--- a/scripts/rabbitmq-server
+++ b/scripts/rabbitmq-server
@@ -133,11 +133,15 @@ ensure_thread_pool_size() {
}
start_rabbitmq_server() {
+ # "-pa ${RABBITMQ_SERVER_CODE_PATH}" should be the very first
+ # command-line argument. In case of using cached HiPE-compilation,
+ # this will allow for compiled versions of erlang built-in modules
+ # (e.g. lists) to be loaded.
ensure_thread_pool_size
check_start_params &&
RABBITMQ_CONFIG_FILE=$RABBITMQ_CONFIG_FILE \
exec ${ERL_DIR}erl \
- -pa ${RABBITMQ_EBIN_ROOT} \
+ -pa ${RABBITMQ_SERVER_CODE_PATH} ${RABBITMQ_EBIN_ROOT} \
${RABBITMQ_START_RABBIT} \
${RABBITMQ_NAME_TYPE} ${RABBITMQ_NODENAME} \
-boot "${SASL_BOOT_FILE}" \
diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl
index f63694b657..2df4fd96c0 100644
--- a/src/rabbit_control_main.erl
+++ b/src/rabbit_control_main.erl
@@ -37,6 +37,7 @@
reset,
force_reset,
rotate_logs,
+ hipe_compile,
{join_cluster, [?RAM_DEF]},
change_cluster_node_type,
@@ -113,7 +114,7 @@
[stop, stop_app, start_app, wait, reset, force_reset, rotate_logs,
join_cluster, change_cluster_node_type, update_cluster_nodes,
forget_cluster_node, rename_cluster_node, cluster_status, status,
- environment, eval, force_boot, help, node_health_check]).
+ environment, eval, force_boot, help, node_health_check, hipe_compile]).
-define(COMMANDS_WITH_TIMEOUT,
[list_user_permissions, list_policies, list_queues, list_exchanges,
@@ -383,6 +384,16 @@ action(rotate_logs, Node, Args = [Suffix], _Opts, Inform) ->
Inform("Rotating logs to files with suffix \"~s\"", [Suffix]),
call(Node, {rabbit, rotate_logs, Args});
+action(hipe_compile, _Node, [TargetDir], _Opts, _Inform) ->
+ ok = application:load(rabbit),
+ case rabbit_hipe:can_hipe_compile() of
+ true ->
+ {ok, _, _} = rabbit_hipe:compile_to_directory(TargetDir),
+ ok;
+ false ->
+ {error, "HiPE compilation is not supported"}
+ end;
+
action(close_connection, Node, [PidStr, Explanation], _Opts, Inform) ->
Inform("Closing connection \"~s\"", [PidStr]),
rpc_call(Node, rabbit_networking, close_connection,
diff --git a/src/rabbit_file.erl b/src/rabbit_file.erl
index 6c4f0e5ccd..1088f2c2dd 100644
--- a/src/rabbit_file.erl
+++ b/src/rabbit_file.erl
@@ -23,6 +23,7 @@
-export([append_file/2, ensure_parent_dirs_exist/1]).
-export([rename/2, delete/1, recursive_delete/1, recursive_copy/2]).
-export([lock_file/1]).
+-export([filename_as_a_directory/1]).
-import(file_handle_cache, [with_handle/1, with_handle/2]).
@@ -58,6 +59,7 @@
(file:filename(), file:filename())
-> rabbit_types:ok_or_error({file:filename(), file:filename(), any()})).
-spec(lock_file/1 :: (file:filename()) -> rabbit_types:ok_or_error('eexist')).
+-spec(filename_as_a_directory/1 :: (file:filename()) -> file:filename()).
-endif.
@@ -305,3 +307,11 @@ lock_file(Path) ->
ok = prim_file:close(Lock)
end)
end.
+
+filename_as_a_directory(FileName) ->
+ case lists:last(FileName) of
+ "/" ->
+ FileName;
+ _ ->
+ FileName ++ "/"
+ end.
diff --git a/src/rabbit_hipe.erl b/src/rabbit_hipe.erl
index 05b5f3719d..494f0e06b7 100644
--- a/src/rabbit_hipe.erl
+++ b/src/rabbit_hipe.erl
@@ -5,15 +5,15 @@
%% practice 2 processes seems just as fast as any other number > 1,
%% and keeps the progress bar realistic-ish.
-define(HIPE_PROCESSES, 2).
--export([maybe_hipe_compile/0, log_hipe_result/1]).
-%% HiPE compilation happens before we have log handlers - so we have
-%% to io:format/2, it's all we can do.
+-export([maybe_hipe_compile/0, log_hipe_result/1]).
+-export([compile_to_directory/1]).
+-export([can_hipe_compile/0]).
+%% Compile and load during server startup sequence
maybe_hipe_compile() ->
{ok, Want} = application:get_env(rabbit, hipe_compile),
- Can = code:which(hipe) =/= non_existing,
- case {Want, Can} of
+ case {Want, can_hipe_compile()} of
{true, true} -> hipe_compile();
{true, false} -> false;
{false, _} -> {ok, disabled}
@@ -33,38 +33,49 @@ log_hipe_result(false) ->
rabbit_log:warning(
"Not HiPE compiling: HiPE not found in this Erlang installation.~n").
+hipe_compile() ->
+ hipe_compile(fun compile_and_load/1, false).
+
+compile_to_directory(Dir0) ->
+ Dir = rabbit_file:filename_as_a_directory(Dir0),
+ ok = prepare_ebin_directory(Dir),
+ hipe_compile(fun (Mod) -> compile_and_save(Mod, Dir) end, true).
+
+need_compile(Mod, Force) ->
+ Exists = code:which(Mod) =/= non_existing,
+ %% We skip modules already natively compiled. This
+ %% happens when RabbitMQ is stopped (just the
+ %% application, not the entire node) and started
+ %% again.
+ NotYetCompiled = not already_hipe_compiled(Mod),
+ NotVersioned = not compiled_with_version_support(Mod),
+ Exists andalso (Force orelse (NotYetCompiled andalso NotVersioned)).
+
%% HiPE compilation happens before we have log handlers and can take a
%% long time, so make an exception to our no-stdout policy and display
%% progress via stdout.
-hipe_compile() ->
+hipe_compile(CompileFun, Force) ->
{ok, HipeModulesAll} = application:get_env(rabbit, hipe_modules),
- HipeModules = [HM || HM <- HipeModulesAll,
- code:which(HM) =/= non_existing andalso
- %% We skip modules already natively compiled. This
- %% happens when RabbitMQ is stopped (just the
- %% application, not the entire node) and started
- %% again.
- already_hipe_compiled(HM)
- andalso (not compiled_with_version_support(HM))],
+ HipeModules = lists:filter(fun(Mod) -> need_compile(Mod, Force) end, HipeModulesAll),
case HipeModules of
[] -> {ok, already_compiled};
- _ -> do_hipe_compile(HipeModules)
+ _ -> do_hipe_compile(HipeModules, CompileFun)
end.
already_hipe_compiled(Mod) ->
try
%% OTP 18.x or later
- Mod:module_info(native) =:= false
+ Mod:module_info(native) =:= true
%% OTP prior to 18.x
catch error:badarg ->
- code:is_module_native(Mod) =:= false
+ code:is_module_native(Mod) =:= true
end.
compiled_with_version_support(Mod) ->
proplists:get_value(erlang_version_support, Mod:module_info(attributes))
=/= undefined.
-do_hipe_compile(HipeModules) ->
+do_hipe_compile(HipeModules, CompileFun) ->
Count = length(HipeModules),
io:format("~nHiPE compiling: |~s|~n |",
[string:copies("-", Count)]),
@@ -79,11 +90,7 @@ do_hipe_compile(HipeModules) ->
%% advanced API does not load automatically the code, except if the
%% 'load' option is set.
PidMRefs = [spawn_monitor(fun () -> [begin
- {M, Beam, _} =
- code:get_object_code(M),
- {ok, _} =
- hipe:compile(M, [], Beam,
- [o3, load]),
+ CompileFun(M),
io:format("#")
end || M <- Ms]
end) ||
@@ -101,3 +108,39 @@ split(L, N) -> split0(L, [[] || _ <- lists:seq(1, N)]).
split0([], Ls) -> Ls;
split0([I | Is], [L | Ls]) -> split0(Is, Ls ++ [[I | L]]).
+
+prepare_ebin_directory(Dir) ->
+ ok = rabbit_file:ensure_dir(Dir),
+ ok = delete_beam_files(Dir),
+ ok.
+
+delete_beam_files(Dir) ->
+ {ok, Files} = file:list_dir(Dir),
+ lists:foreach(fun(File) ->
+ case filename:extension(File) of
+ ".beam" ->
+ ok = file:delete(filename:join([Dir, File]));
+ _ ->
+ ok
+ end
+ end,
+ Files).
+
+compile_and_load(Mod) ->
+ {Mod, Beam, _} = code:get_object_code(Mod),
+ {ok, _} = hipe:compile(Mod, [], Beam, [o3, load]).
+
+compile_and_save(Module, Dir) ->
+ {Module, BeamCode, _} = code:get_object_code(Module),
+ BeamName = filename:join([Dir, atom_to_list(Module) ++ ".beam"]),
+ {ok, {Architecture, NativeCode}} = hipe:compile(Module, [], BeamCode, [o3]),
+ {ok, _, Chunks0} = beam_lib:all_chunks(BeamCode),
+ ChunkName = hipe_unified_loader:chunk_name(Architecture),
+ Chunks1 = lists:keydelete(ChunkName, 1, Chunks0),
+ Chunks = Chunks1 ++ [{ChunkName,NativeCode}],
+ {ok, BeamPlusNative} = beam_lib:build_module(Chunks),
+ ok = file:write_file(BeamName, BeamPlusNative),
+ BeamName.
+
+can_hipe_compile() ->
+ code:which(hipe) =/= non_existing.