summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJosé Valim <jose.valim@dashbit.co>2022-10-15 11:14:53 +0200
committerJosé Valim <jose.valim@dashbit.co>2022-10-25 10:40:03 +0200
commit1cb08afe092e7967844173a7d094a6c0ec8673a9 (patch)
tree8337e86b47848f591bd35260e28ebf7977d18edd
parentffc87d675aaa0beac3d6c2365c08cd6505fb33d8 (diff)
downloaderlang-1cb08afe092e7967844173a7d094a6c0ec8673a9.tar.gz
Improve boot error messages
Today if there is a crash during boot or eval, because a more recent Erlang/OTP version is required, the user is prompted with this message: $ erl -noshell -s oops {"init terminating in do_boot",{undef, [{oops,start,[],[]},{init,start_em,1,[]} ,{init,do_boot,3,[]}]}} init terminating in do_boot ({undef,[{oops, start,[],[]},{init,start_em,1,[]},{init, do_boot,3,[]}]}) Crash dump is being written to: erl_crash.dump...done Unfortunately, this message gives little indication of what is wrong besides the "undef" atom. This particularly problematic when users are running a system software (Rebar, Elixir, etc) against an old system version of Erlang/OTP. This commit improves the boot error messages such that: 1. It removes the double reports from erlang:display/1 and crash dump 2. Validates the module given to -s and attempts to provide a human error for common cases 3. Makes sure the -eval failure is printed and uses a consistent format with -s 4. Improve error messages to not include internal details such as "do_boot" 5. It suggests that, besides using an old version of Erlang/OTP, they may also use a new one Now we get: $ erl -noshell -s oops Error! Failed to load module 'oops' because the module cannot be found. Make sure the module name is correct and its .beam is in the code path Runtime terminating during boot ({undef, [{init,start_it,1,[]},{init,start_em,1,[]}, {init,do_boot,3,[]}]}) Crash dump is being written to: erl_crash.dump...done If the .beam file exists but cannot be loaded: $ erl -noshell -s oops Error! Failed to load module 'oops' because the module requires a more recent Erlang/OTP version or its .beam was corrupted (you are running Erlang/OTP 26) Runtime terminating during boot ({undef, [{init,start_it,1,[]},{init,start_em,1,[]}, {init,do_boot,3,[]}]}) Crash dump is being written to: erl_crash.dump...done
-rw-r--r--erts/emulator/beam/beam_load.c2
-rw-r--r--erts/preloaded/src/init.erl52
-rw-r--r--lib/kernel/test/init_SUITE.erl19
3 files changed, 57 insertions, 16 deletions
diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c
index 85beba57ee..c7c0a715b5 100644
--- a/erts/emulator/beam/beam_load.c
+++ b/erts/emulator/beam/beam_load.c
@@ -47,7 +47,7 @@ Uint erts_total_code_size;
static int load_code(LoaderState *stp);
-#define PLEASE_RECOMPILE "please re-compile this module with an Erlang/OTP " ERLANG_OTP_RELEASE " compiler"
+#define PLEASE_RECOMPILE "please re-compile this module with an Erlang/OTP " ERLANG_OTP_RELEASE " compiler or update your Erlang/OTP version"
/**********************************************************************/
diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl
index a95df3741e..96e0ec1dd6 100644
--- a/erts/preloaded/src/init.erl
+++ b/erts/preloaded/src/init.erl
@@ -396,8 +396,8 @@ boot_loop(BootPid, State) ->
loop(State#state{status = {started,PS},
subscribed = []});
{'EXIT',BootPid,Reason} ->
- erlang:display({"init terminating in do_boot",Reason}),
- crash("init terminating in do_boot", [Reason]);
+ % erlang:display({"init terminating in do_boot",Reason}),
+ crash("Runtime terminating during boot", [Reason]);
{'EXIT',Pid,Reason} ->
Kernel = State#state.kernel,
terminate(Pid,Kernel,Reason), %% If Pid is a Kernel pid, halt()!
@@ -462,9 +462,9 @@ new_kernelpid({_Name,ignore},BootPid,State) ->
BootPid ! {self(),ignore},
State;
new_kernelpid({Name,What},BootPid,State) ->
- erlang:display({"could not start kernel pid",Name,What}),
+ % erlang:display({"could not start kernel pid",Name,What}),
clear_system(false,BootPid,State),
- crash("could not start kernel pid", [Name, What]).
+ crash("Could not start kernel pid", [Name, What]).
%% Here is the main loop after the system has booted.
@@ -834,7 +834,7 @@ terminate(Pid,Kernel,Reason) ->
case kernel_pid(Pid,Kernel) of
{ok,Name} ->
sleep(500), %% Flush error printouts!
- erlang:display({"Kernel pid terminated",Name,Reason}),
+ % erlang:display({"Kernel pid terminated",Name,Reason}),
crash("Kernel pid terminated", [Name, Reason]);
_ ->
false
@@ -1235,18 +1235,44 @@ start_it({eval,Bin}) ->
{value, _Value, _Bs} = erl_eval:exprs(Expr, erl_eval:new_bindings()),
ok
catch E:R:ST ->
- erlang:display_string(
- binary_to_list(
- iolist_to_binary(["Failed to eval: ",Bin,"\n"]))),
+ Message = [<<"Error! Failed to eval: ">>, Bin, <<"\r\n\r\n">>],
+ erlang:display_string(binary_to_list(iolist_to_binary(Message))),
erlang:raise(E,R,ST)
end;
-start_it([_|_]=MFA) ->
- case MFA of
- [M] -> M:start();
- [M,F] -> M:F();
- [M,F|Args] -> M:F(Args) % Args is a list
+start_it([M|FA]) ->
+ case code:ensure_loaded(M) of
+ {module, M} ->
+ case FA of
+ [] -> M:start();
+ [F] -> M:F();
+ [F|Args] -> M:F(Args) % Args is a list
+ end;
+
+ {error, Reason} ->
+ Message = [explain_ensure_loaded_error(M, Reason), <<"\r\n\r\n">>],
+ erlang:display_string(binary_to_list(iolist_to_binary(Message))),
+ erlang:error(undef)
end.
+explain_ensure_loaded_error(M, badfile) ->
+ S = [<<"it requires a more recent Erlang/OTP version "
+ "or its .beam file was corrupted.\r\n"
+ "(You are running Erlang/OTP ">>,
+ erlang:system_info(otp_release), <<".)">>],
+ explain_add_head(M, S);
+explain_ensure_loaded_error(M, nofile) ->
+ S = <<"it cannot be found. Make sure that the module name is correct and\r\n",
+ "that its .beam file is in the code path.">>,
+ explain_add_head(M, S);
+explain_ensure_loaded_error(M, Other) ->
+ [<<"Error! Failed to load module '", (atom_to_binary(M))/binary,
+ "'. Reason: ">>,
+ atom_to_binary(Other)].
+
+explain_add_head(M, S) ->
+ [<<"Error! Failed to load module '", (atom_to_binary(M))/binary,
+ "' because ">>, S].
+
%% Load a module.
do_load_module(Mod, BinCode) ->
diff --git a/lib/kernel/test/init_SUITE.erl b/lib/kernel/test/init_SUITE.erl
index bc3882b862..ea75f040f2 100644
--- a/lib/kernel/test/init_SUITE.erl
+++ b/lib/kernel/test/init_SUITE.erl
@@ -29,7 +29,7 @@
many_restarts/0, many_restarts/1, restart_with_mode/1,
get_plain_arguments/1,
reboot/1, stop_status/1, stop/1, get_status/1, script_id/1,
- dot_erlang/1,
+ dot_erlang/1, unknown_module/1,
find_system_processes/0]).
-export([boot1/1, boot2/1]).
@@ -48,7 +48,7 @@ all() ->
[get_arguments, get_argument, boot_var,
many_restarts, restart_with_mode,
get_plain_arguments, restart, stop_status, get_status, script_id,
- dot_erlang, {group, boot}].
+ dot_erlang, unknown_module, {group, boot}].
groups() ->
[{boot, [], [boot1, boot2]}].
@@ -689,6 +689,21 @@ dot_erlang(Config) ->
ok.
+unknown_module(Config) when is_list(Config) ->
+ Port = open_port({spawn, "erl -s unknown_module"},
+ [exit_status, use_stdio, stderr_to_stdout]),
+ Error = "Error! Failed to load module 'unknown_module' because it cannot be found.",
+ [_ | _] = string:find(collect_until_exit_one(Port), Error),
+ ok.
+
+collect_until_exit_one(Port) ->
+ receive
+ {Port, {data, Msg}} -> Msg ++ collect_until_exit_one(Port);
+ {Port, {exit_status, 1}} -> []
+ after
+ 30_000 -> ct:fail(erl_timeout)
+ end.
+
%% ------------------------------------------------
%% Start the slave system with -boot flag.
%% ------------------------------------------------