summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan Lehnardt <jan@apache.org>2021-03-13 18:00:12 +0100
committerJan Lehnardt <jan@apache.org>2021-03-13 18:00:19 +0100
commit6423dcfcbc5e3bcba99d7cde7ec326ff6ae1dd53 (patch)
treecabf4e0c64e6177ea6148e64f432e8ba0cb543ab
parent61f387929c19e831fe2f34f8ced8d2b42616f835 (diff)
downloadcouchdb-6423dcfcbc5e3bcba99d7cde7ec326ff6ae1dd53.tar.gz
feat: work around get_stacktrace deprecation/removal
This patch introduces a macro and inserts it everywhere we catch errors and then generatre a stacktrace. So far the only thing that is a little bit ugly is that in two places, I had to add a header include dependency on couch_db.erl where those modules didn’t have any ties to couchdb/* before, alas. I’d be willing to duplicate the macros in those modules, if we don’t want the include dependency.
-rw-r--r--Makefile2
-rw-r--r--rebar.config.script2
-rw-r--r--src/chttpd/src/chttpd.erl34
-rw-r--r--src/chttpd/src/chttpd_stats.erl6
-rw-r--r--src/couch/include/couch_db.hrl26
-rw-r--r--src/couch/src/couch_httpd.erl12
-rw-r--r--src/couch/src/couch_proc_manager.erl3
-rw-r--r--src/couch/src/couch_query_servers.erl3
-rw-r--r--src/couch_replicator/src/couch_replicator_scheduler_job.erl4
-rw-r--r--src/ddoc_cache/src/ddoc_cache_entry.erl5
-rw-r--r--src/fabric/src/fabric_rpc.erl9
-rw-r--r--src/fabric/src/fabric_view_all_docs.erl3
-rw-r--r--src/mango/src/mango_httpd.erl3
-rw-r--r--src/mango/src/mango_util.erl9
-rw-r--r--src/rexi/src/rexi_server.erl12
15 files changed, 75 insertions, 58 deletions
diff --git a/Makefile b/Makefile
index 873019f06..f67a2113e 100644
--- a/Makefile
+++ b/Makefile
@@ -175,7 +175,7 @@ exunit: export ERL_LIBS = $(shell pwd)/src
exunit: export ERL_AFLAGS = -config $(shell pwd)/rel/files/eunit.config
exunit: export COUCHDB_QUERY_SERVER_JAVASCRIPT = $(shell pwd)/bin/couchjs $(shell pwd)/share/server/main.js
exunit: couch elixir-init setup-eunit elixir-check-formatted elixir-credo
- @mix test --cover --trace $(EXUNIT_OPTS)
+ @mix test --trace $(EXUNIT_OPTS)
setup-eunit: export BUILDDIR = $(shell pwd)
setup-eunit: export ERL_AFLAGS = -config $(shell pwd)/rel/files/eunit.config
diff --git a/rebar.config.script b/rebar.config.script
index 03e6fcaf5..cbb2192b7 100644
--- a/rebar.config.script
+++ b/rebar.config.script
@@ -195,7 +195,7 @@ ErlOpts = case os:getenv("ERL_OPTS") of
end.
AddConfig = [
- {require_otp_vsn, "19|20|21|22"},
+ {require_otp_vsn, "19|20|21|22|23"},
{deps_dir, "src"},
{deps, lists:map(MakeDep, DepDescs ++ OptionalDeps)},
{sub_dirs, SubDirs},
diff --git a/src/chttpd/src/chttpd.erl b/src/chttpd/src/chttpd.erl
index 16ed82e41..a677c191a 100644
--- a/src/chttpd/src/chttpd.erl
+++ b/src/chttpd/src/chttpd.erl
@@ -268,16 +268,15 @@ before_request(HttpReq) ->
try
chttpd_stats:init(),
chttpd_plugin:before_request(HttpReq)
- catch Tag:Error ->
- {error, catch_error(HttpReq, Tag, Error)}
+ catch ?STACKTRACE(Tag, Error, Stack)
+ {error, catch_error(HttpReq, Tag, Error, Stack)}
end.
after_request(HttpReq, HttpResp0) ->
{ok, HttpResp1} =
try
chttpd_plugin:after_request(HttpReq, HttpResp0)
- catch _Tag:Error ->
- Stack = erlang:get_stacktrace(),
+ catch ?STACKTRACE(_Tag, Error, Stack)
send_error(HttpReq, {Error, nil, Stack}),
{ok, HttpResp0#httpd_resp{status = aborted}}
end,
@@ -310,8 +309,8 @@ process_request(#httpd{mochi_req = MochiReq} = HttpReq) ->
Response ->
{HttpReq, Response}
end
- catch Tag:Error ->
- {HttpReq, catch_error(HttpReq, Tag, Error)}
+ catch ?STACKTRACE(Tag, Error, Stack)
+ {HttpReq, catch_error(HttpReq, Tag, Error, Stack)}
end.
handle_req_after_auth(HandlerKey, HttpReq) ->
@@ -321,17 +320,17 @@ handle_req_after_auth(HandlerKey, HttpReq) ->
AuthorizedReq = chttpd_auth:authorize(possibly_hack(HttpReq),
fun chttpd_auth_request:authorize_request/1),
{AuthorizedReq, HandlerFun(AuthorizedReq)}
- catch Tag:Error ->
- {HttpReq, catch_error(HttpReq, Tag, Error)}
+ catch ?STACKTRACE(Tag, Error, Stack)
+ {HttpReq, catch_error(HttpReq, Tag, Error, Stack)}
end.
-catch_error(_HttpReq, throw, {http_head_abort, Resp}) ->
+catch_error(_HttpReq, throw, {http_head_abort, Resp}, _Stack) ->
{ok, Resp};
-catch_error(_HttpReq, throw, {http_abort, Resp, Reason}) ->
+catch_error(_HttpReq, throw, {http_abort, Resp, Reason}, _Stack) ->
{aborted, Resp, Reason};
-catch_error(HttpReq, throw, {invalid_json, _}) ->
+catch_error(HttpReq, throw, {invalid_json, _}, _Stack) ->
send_error(HttpReq, {bad_request, "invalid UTF-8 JSON"});
-catch_error(HttpReq, exit, {mochiweb_recv_error, E}) ->
+catch_error(HttpReq, exit, {mochiweb_recv_error, E}, _Stack) ->
#httpd{
mochi_req = MochiReq,
peer = Peer,
@@ -343,16 +342,15 @@ catch_error(HttpReq, exit, {mochiweb_recv_error, E}) ->
MochiReq:get(raw_path),
E]),
exit(normal);
-catch_error(HttpReq, exit, {uri_too_long, _}) ->
+catch_error(HttpReq, exit, {uri_too_long, _}, _Stack) ->
send_error(HttpReq, request_uri_too_long);
-catch_error(HttpReq, exit, {body_too_large, _}) ->
+catch_error(HttpReq, exit, {body_too_large, _}, _Stack) ->
send_error(HttpReq, request_entity_too_large);
-catch_error(HttpReq, throw, Error) ->
+catch_error(HttpReq, throw, Error, _Stack) ->
send_error(HttpReq, Error);
-catch_error(HttpReq, error, database_does_not_exist) ->
+catch_error(HttpReq, error, database_does_not_exist, _Stack) ->
send_error(HttpReq, database_does_not_exist);
-catch_error(HttpReq, Tag, Error) ->
- Stack = erlang:get_stacktrace(),
+catch_error(HttpReq, Tag, Error, Stack) ->
% TODO improve logging and metrics collection for client disconnects
case {Tag, Error, Stack} of
{exit, normal, [{mochiweb_request, send, _, _} | _]} ->
diff --git a/src/chttpd/src/chttpd_stats.erl b/src/chttpd/src/chttpd_stats.erl
index 59ec9268d..b76c5618b 100644
--- a/src/chttpd/src/chttpd_stats.erl
+++ b/src/chttpd/src/chttpd_stats.erl
@@ -1,3 +1,4 @@
+
% 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
@@ -12,6 +13,8 @@
-module(chttpd_stats).
+% for the stacktrace macro only so far
+-include_lib("couch/include/couch_db.hrl").
-export([
init/0,
@@ -50,8 +53,7 @@ report(HttpReq, HttpResp) ->
_ ->
ok
end
- catch T:R ->
- S = erlang:get_stacktrace(),
+ catch ?STACKTRACE(T, R, S)
Fmt = "Failed to report chttpd request stats: ~p:~p ~p",
couch_log:error(Fmt, [T, R, S])
end.
diff --git a/src/couch/include/couch_db.hrl b/src/couch/include/couch_db.hrl
index 830b9bcf4..61e5f73d7 100644
--- a/src/couch/include/couch_db.hrl
+++ b/src/couch/include/couch_db.hrl
@@ -219,3 +219,29 @@
-type sec_props() :: [tuple()].
-type sec_obj() :: {sec_props()}.
+%% Erlang/OTP 21 deprecates and 23 removes get_stacktrace(), so
+%% we have to monkey around until we can drop support < 21.
+%% h/t https://github.com/erlang/otp/pull/1783#issuecomment-386190970
+
+%% use like so:
+% try function1(Arg1)
+% catch
+% ?STACKTRACE(exit, badarg, ErrorStackTrace)
+% % do stuff with ErrorStackTrace
+% % ...
+% end,
+
+% for features specific to Erlang/OTP version 20.x (and later versions)
+-ifdef(ERLANG_OTP_VERSION_20).
+-else.
+-define(ERLANG_OTP_VERSION_21_FEATURES, true).
+-endif.
+% Get the stacktrace in a way that is backwards compatible
+-ifdef(ERLANG_OTP_VERSION_21_FEATURES).
+-define(STACKTRACE(ErrorType, Error, ErrorStackTrace),
+ ErrorType:Error:ErrorStackTrace ->).
+-else.
+-define(STACKTRACE(ErrorType, Error, ErrorStackTrace),
+ ErrorType:Error ->
+ ErrorStackTrace = erlang:get_stacktrace(),).
+-endif.
diff --git a/src/couch/src/couch_httpd.erl b/src/couch/src/couch_httpd.erl
index d89c74957..6c0fd8761 100644
--- a/src/couch/src/couch_httpd.erl
+++ b/src/couch/src/couch_httpd.erl
@@ -363,23 +363,19 @@ handle_request_int(MochiReq, DefaultFun,
send_error(HttpReq, request_entity_too_large);
exit:{uri_too_long, _} ->
send_error(HttpReq, request_uri_too_long);
- throw:Error ->
- Stack = erlang:get_stacktrace(),
+ ?STACKTRACE(throw, Error, Stack)
couch_log:debug("Minor error in HTTP request: ~p",[Error]),
couch_log:debug("Stacktrace: ~p",[Stack]),
send_error(HttpReq, Error);
- error:badarg ->
- Stack = erlang:get_stacktrace(),
+ ?STACKTRACE(error, badarg, Stack)
couch_log:error("Badarg error in HTTP request",[]),
couch_log:info("Stacktrace: ~p",[Stack]),
send_error(HttpReq, badarg);
- error:function_clause ->
- Stack = erlang:get_stacktrace(),
+ ?STACKTRACE(error, function_clause, Stack)
couch_log:error("function_clause error in HTTP request",[]),
couch_log:info("Stacktrace: ~p",[Stack]),
send_error(HttpReq, function_clause);
- Tag:Error ->
- Stack = erlang:get_stacktrace(),
+ ?STACKTRACE(Tag, Error, Stack)
couch_log:error("Uncaught error in HTTP request: ~p",
[{Tag, Error}]),
couch_log:info("Stacktrace: ~p",[Stack]),
diff --git a/src/couch/src/couch_proc_manager.erl b/src/couch/src/couch_proc_manager.erl
index 0daef3ee9..1f6d0167b 100644
--- a/src/couch/src/couch_proc_manager.erl
+++ b/src/couch/src/couch_proc_manager.erl
@@ -308,8 +308,7 @@ find_proc(#client{lang = Lang, ddoc = DDoc, ddoc_key = DDocKey} = Client) ->
find_proc(Lang, Fun) ->
try iter_procs(Lang, Fun)
- catch error:Reason ->
- StackTrace = erlang:get_stacktrace(),
+ catch ?STACKTRACE(error, Reason, StackTrace)
couch_log:error("~p ~p ~p", [?MODULE, Reason, StackTrace]),
{error, Reason}
end.
diff --git a/src/couch/src/couch_query_servers.erl b/src/couch/src/couch_query_servers.erl
index 75180a7f8..ebf82ab31 100644
--- a/src/couch/src/couch_query_servers.erl
+++ b/src/couch/src/couch_query_servers.erl
@@ -527,8 +527,7 @@ with_ddoc_proc(#doc{id=DDocId,revs={Start, [DiskRev|_]}}=DDoc, Fun) ->
Resp ->
ok = ret_os_process(Proc),
Resp
- catch Tag:Err ->
- Stack = erlang:get_stacktrace(),
+ catch ?STACKTRACE(Tag, Err, Stack)
catch proc_stop(Proc),
erlang:raise(Tag, Err, Stack)
end.
diff --git a/src/couch_replicator/src/couch_replicator_scheduler_job.erl b/src/couch_replicator/src/couch_replicator_scheduler_job.erl
index 238c725e4..1d328d0a6 100644
--- a/src/couch_replicator/src/couch_replicator_scheduler_job.erl
+++ b/src/couch_replicator/src/couch_replicator_scheduler_job.erl
@@ -319,9 +319,9 @@ handle_info(timeout, InitArgs) ->
catch
exit:{http_request_failed, _, _, max_backoff} ->
{stop, {shutdown, max_backoff}, {error, InitArgs}};
- Class:Error ->
+ ?STACKTRACE(Class, Error, Stack)
ShutdownReason = {error, replication_start_error(Error)},
- StackTop2 = lists:sublist(erlang:get_stacktrace(), 2),
+ StackTop2 = lists:sublist(Stack, 2),
% Shutdown state is a hack as it is not really the state of the
% gen_server (it failed to initialize, so it doesn't have one).
% Shutdown state is used to pass extra info about why start failed.
diff --git a/src/ddoc_cache/src/ddoc_cache_entry.erl b/src/ddoc_cache/src/ddoc_cache_entry.erl
index 4cc3d7e52..32d3ec1a7 100644
--- a/src/ddoc_cache/src/ddoc_cache_entry.erl
+++ b/src/ddoc_cache/src/ddoc_cache_entry.erl
@@ -14,6 +14,8 @@
-behaviour(gen_server).
-vsn(1).
+% for the stacktrace macro only so far
+-include_lib("couch/include/couch_db.hrl").
-export([
dbname/1,
@@ -297,8 +299,7 @@ do_open(Key) ->
try recover(Key) of
Resp ->
erlang:exit({open_ok, Key, Resp})
- catch T:R ->
- S = erlang:get_stacktrace(),
+ catch ?STACKTRACE(T, R, S)
erlang:exit({open_error, Key, {T, R, S}})
end.
diff --git a/src/fabric/src/fabric_rpc.erl b/src/fabric/src/fabric_rpc.erl
index 85da3ff12..7776bd9fe 100644
--- a/src/fabric/src/fabric_rpc.erl
+++ b/src/fabric/src/fabric_rpc.erl
@@ -330,9 +330,9 @@ with_db(DbName, Options, {M,F,A}) ->
apply(M, F, [Db | A])
catch Exception ->
Exception;
- error:Reason ->
+ ?STACKTRACE(error, Reason, Stack)
couch_log:error("rpc ~p:~p/~p ~p ~p", [M, F, length(A)+1, Reason,
- clean_stack()]),
+ clean_stack(Stack)]),
{error, Reason}
end);
Error ->
@@ -596,9 +596,8 @@ make_att_reader({fabric_attachment_receiver, Middleman, Length}) ->
make_att_reader(Else) ->
Else.
-clean_stack() ->
- lists:map(fun({M,F,A}) when is_list(A) -> {M,F,length(A)}; (X) -> X end,
- erlang:get_stacktrace()).
+clean_stack(S) ->
+ lists:map(fun({M,F,A}) when is_list(A) -> {M,F,length(A)}; (X) -> X end, S).
set_io_priority(DbName, Options) ->
case lists:keyfind(io_priority, 1, Options) of
diff --git a/src/fabric/src/fabric_view_all_docs.erl b/src/fabric/src/fabric_view_all_docs.erl
index e4d3d4a40..1b1aa8a9c 100644
--- a/src/fabric/src/fabric_view_all_docs.erl
+++ b/src/fabric/src/fabric_view_all_docs.erl
@@ -296,8 +296,7 @@ open_doc(DbName, Options, Id, IncludeDocs) ->
try open_doc_int(DbName, Options, Id, IncludeDocs) of
#view_row{} = Row ->
exit(Row)
- catch Type:Reason ->
- Stack = erlang:get_stacktrace(),
+ catch ?STACKTRACE(Type, Reason, Stack)
couch_log:error("_all_docs open error: ~s ~s :: ~w ~w", [
DbName, Id, {Type, Reason}, Stack]),
exit({Id, Reason})
diff --git a/src/mango/src/mango_httpd.erl b/src/mango/src/mango_httpd.erl
index 379d2e127..624691bb9 100644
--- a/src/mango/src/mango_httpd.erl
+++ b/src/mango/src/mango_httpd.erl
@@ -37,10 +37,9 @@ handle_req(#httpd{} = Req, Db0) ->
Db = set_user_ctx(Req, Db0),
handle_req_int(Req, Db)
catch
- throw:{mango_error, Module, Reason} ->
+ ?STACKTRACE(throw, {mango_error, Module, Reason}, Stack)
case mango_error:info(Module, Reason) of
{500, ErrorStr, ReasonStr} ->
- Stack = erlang:get_stacktrace(),
chttpd:send_error(Req, {ErrorStr, ReasonStr, Stack});
{Code, ErrorStr, ReasonStr} ->
chttpd:send_error(Req, Code, ErrorStr, ReasonStr)
diff --git a/src/mango/src/mango_util.erl b/src/mango/src/mango_util.erl
index 0d31f15f9..8257e841f 100644
--- a/src/mango/src/mango_util.erl
+++ b/src/mango/src/mango_util.erl
@@ -138,16 +138,13 @@ do_defer(Mod, Fun, Args) ->
Resp ->
erlang:exit({mango_defer_ok, Resp})
catch
- throw:Error ->
- Stack = erlang:get_stacktrace(),
+ ?STACKTRACE(throw, Error, Stack)
couch_log:error("Defered error: ~w~n ~p", [{throw, Error}, Stack]),
erlang:exit({mango_defer_throw, Error});
- error:Error ->
- Stack = erlang:get_stacktrace(),
+ ?STACKTRACE(error, Error, Stack)
couch_log:error("Defered error: ~w~n ~p", [{error, Error}, Stack]),
erlang:exit({mango_defer_error, Error});
- exit:Error ->
- Stack = erlang:get_stacktrace(),
+ ?STACKTRACE(exit, Error, Stack)
couch_log:error("Defered error: ~w~n ~p", [{exit, Error}, Stack]),
erlang:exit({mango_defer_exit, Error})
end.
diff --git a/src/rexi/src/rexi_server.erl b/src/rexi/src/rexi_server.erl
index fedff69c3..4cd9ce66e 100644
--- a/src/rexi/src/rexi_server.erl
+++ b/src/rexi/src/rexi_server.erl
@@ -18,6 +18,9 @@
-export([start_link/1, init_p/2, init_p/3]).
+% for the stacktrace macro only so far
+-include_lib("couch/include/couch_db.hrl").
+
-include_lib("rexi/include/rexi.hrl").
-record(job, {
@@ -135,8 +138,8 @@ init_p(From, {M,F,A}, Nonce) ->
put(rexi_from, From),
put('$initial_call', {M,F,length(A)}),
put(nonce, Nonce),
- try apply(M, F, A) catch exit:normal -> ok; Class:Reason ->
- Stack = clean_stack(),
+ try apply(M, F, A) catch exit:normal -> ok; ?STACKTRACE(Class, Reason, Stack0)
+ Stack = clean_stack(Stack0),
{ClientPid, _ClientRef} = From,
couch_log:error(
"rexi_server: from: ~s(~p) mfa: ~s:~s/~p ~p:~p ~100p", [
@@ -160,9 +163,8 @@ save_error(E, #st{errors=Q, error_limit=L, error_count=C} = St) when C >= L ->
save_error(E, #st{errors=Q, error_count=C} = St) ->
St#st{errors = queue:in(E, Q), error_count = C+1}.
-clean_stack() ->
- lists:map(fun({M,F,A}) when is_list(A) -> {M,F,length(A)}; (X) -> X end,
- erlang:get_stacktrace()).
+clean_stack(S) ->
+ lists:map(fun({M,F,A}) when is_list(A) -> {M,F,length(A)}; (X) -> X end, S).
add_job(Job, #st{workers = Workers, clients = Clients} = State) ->
ets:insert(Workers, Job),