File 2502-cover-Gracefully-handle-failures-to-collect-coverage.patch of Package erlang

From 426c582fbd5b3eab073a32b621f9a9ecd2463de1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= <bjorn@erlang.org>
Date: Thu, 1 Aug 2024 15:40:46 +0200
Subject: [PATCH] cover: Gracefully handle failures to collect coverage data

On platforms that support native coverage (introduced in
Erlang/OTP 27), the `cover` module did not properly handle failed
calls to `code:get_coverage/2`. The call to that function will fail
if the module in question has been reloaded or unloaded.

For example, if `cover:export/1` was called and a module had been
reloaded on a remote node (for example by `meck`), the
`cover:export/1` call would never return.

Because the current API for `cover` has no good way to report this
kind of error, we will fix the issue by logging the failed call using
the `logger` module and otherwise ignore the failure. That implies
that there will be no coverage information for modules that have been
reloaded. That is compatible with the behavior of `cover` prior to
Erlang/OTP 27.

Closes #8691
---
 lib/tools/src/cover.erl        | 28 ++++++++++++++++++++++++++--
 lib/tools/test/cover_SUITE.erl | 26 +++++++++++++++++++++++++-
 2 files changed, 51 insertions(+), 3 deletions(-)

diff --git a/lib/tools/src/cover.erl b/lib/tools/src/cover.erl
index 8827a10647..fc48997195 100644
--- a/lib/tools/src/cover.erl
+++ b/lib/tools/src/cover.erl
@@ -2364,8 +2364,17 @@ standard_move(Mod) ->
     end.
 
 native_move(Mod) ->
-    Coverage = maps:from_list(code:get_coverage(cover_id_line, Mod)),
-    _ = code:reset_coverage(Mod),
+    Coverage0 =
+        try
+            code:get_coverage(cover_id_line, Mod)
+        catch
+            error:badarg ->
+                log_native_move_error(Mod),
+                []
+        end,
+    _ = catch code:reset_coverage(Mod),
+    Coverage = maps:from_list(Coverage0),
+
     fun({#bump{}=Key,Index}) ->
             case Coverage of
                 #{Index := false} ->
@@ -2379,6 +2388,21 @@ native_move(Mod) ->
             end
     end.
 
+log_native_move_error(Mod) ->
+    S = "Module ~tp: Failed to collect coverage information. "
+        "Has it been reloaded or unloaded?",
+    F = fun(#{node := Node}) ->
+                case Node of
+                    nonode@nohost ->
+                        {S,[Mod]};
+                    _ ->
+                        {"On node ~tp: " ++ S,[Node,Mod]}
+                end
+        end,
+    logger:warning(#{coverage_collection_failed => Mod,
+                     node => node()},
+                   #{report_cb => F}).
+
 %% Reset counters (set counters to 0).
 reset_counters(Mod) ->
     case has_native_coverage() of
diff --git a/lib/tools/test/cover_SUITE.erl b/lib/tools/test/cover_SUITE.erl
index 489c35d24d..d9e77cd651 100644
--- a/lib/tools/test/cover_SUITE.erl
+++ b/lib/tools/test/cover_SUITE.erl
@@ -35,7 +35,7 @@ all() ->
                    otp_13277, otp_13289, guard_in_lc, gh_4796,
                    eep49, gh_8159],
     StartStop = [start, compile, analyse, misc, stop,
-                 distribution, reconnect, die_and_reconnect,
+                 distribution, distribution_export, reconnect, die_and_reconnect,
                  dont_reconnect_after_stop, stop_node_after_disconnect,
                  export_import, otp_5031, otp_6115,
                  otp_8270, otp_10979_hanging_node, otp_14817,
@@ -539,6 +539,30 @@ distribution(Config) when is_list(Config) ->
     peer:stop(P1),
     peer:stop(P2).
 
+%% GH-8661. An attempt to export cover data on a remote node could
+%% hang if the module had been reloaded.
+distribution_export(Config) when is_list(Config) ->
+    ct:timetrap({seconds, 30}),
+
+    DataDir = proplists:get_value(data_dir, Config),
+
+    ok = file:set_cwd(DataDir),
+
+    {ok,P1,N1} = ?CT_PEER(),
+
+    {ok,f} = cover:compile(f),
+    {ok,[_]} = cover:start([N1]),
+    ok = cover:export("f.coverdata"),
+
+    {ok,f} = compile:file(f, [debug_info]),
+    {module, f} = erpc:call(N1, code, load_file, [f]),
+
+    ok = cover:export("f.coverdata"),
+
+    %% Cleanup
+    peer:stop(P1),
+    ok.
+
 %% Test that a lost node is reconnected
 reconnect(Config) ->
     DataDir = proplists:get_value(data_dir, Config),
-- 
2.43.0

openSUSE Build Service is sponsored by