./ct_report/coverage/backend_module.COVER.html

1 %%%----------------------------------------------------------------------
2 %%% File : backend_module.erl
3 %%% Author : Alexey Shchepin <alexey@process-one.net>
4 %%%
5 %%%
6 %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
7 %%% Copyright 2016 Erlang Solutions Ltd.
8 %%%
9 %%% This program is free software; you can redistribute it and/or
10 %%% modify it under the terms of the GNU General Public License as
11 %%% published by the Free Software Foundation; either version 2 of the
12 %%% License, or (at your option) any later version.
13 %%%
14 %%% This program is distributed in the hope that it will be useful,
15 %%% but WITHOUT ANY WARRANTY; without even the implied warranty of
16 %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 %%% General Public License for more details.
18 %%%
19 %%% You should have received a copy of the GNU General Public License
20 %%% along with this program; if not, write to the Free Software
21 %%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 %%%
23 %%%----------------------------------------------------------------------
24
25 -module(backend_module).
26 -author('alexey@process-one.net').
27 -author('konrad.zemek@erlang-solutions.com').
28
29 -export([create/2, create/3, backend_module/2, is_exported/3]).
30
31 -ignore_xref([create/2, backend_module/2, behaviour_info/1]).
32
33 %% Callback implemented by proxy modules.
34 -callback backend() -> module().
35
36 %% API
37
38 -spec create(For :: module(), Name :: atom()) ->
39 {ok, module()} | {error, already_loaded}.
40 create(For, Name) ->
41
:-(
create(For, Name, []).
42
43 -spec create(For :: module(), Name :: atom(), TrackedFuns :: [atom()]) ->
44 {ok, module()} | {error, already_loaded}.
45 create(Module, Backend, TrackedFuns) ->
46 289 ProxyModule = proxy_module(Module),
47 289 BackendModule = backend_module(Module, Backend),
48 289 mongoose_backend:ensure_backend_metrics(Module, TrackedFuns),
49 289 case catch ProxyModule:backend() of
50 BackendModule ->
51 289 {error, already_loaded};
52 _ ->
53
:-(
{ProxyModuleStr, CodeString} = backend_code(Module, Backend, TrackedFuns),
54
:-(
{Mod, Code} = dynamic_compile:from_string(CodeString),
55
:-(
code:load_binary(Mod, ProxyModuleStr ++ ".erl", Code),
56
:-(
{ok, ProxyModule}
57 end.
58
59 -spec is_exported(Module :: module(), Function :: atom(),
60 Arity :: integer()) -> boolean().
61 is_exported(Module, Function, Arity) ->
62 13384 code:ensure_loaded(Module),
63 13384 erlang:function_exported(Module, Function, Arity).
64
65 %% Internal functions
66
67 -spec proxy_module(Module :: module()) -> module().
68 proxy_module(Module) ->
69 289 list_to_atom(atom_to_list(Module) ++ "_backend").
70
71 -spec backend_module(Module :: module(), Backend :: atom()) -> module().
72 backend_module(Module, Backend) ->
73 289 list_to_atom(atom_to_list(Module) ++ "_" ++ atom_to_list(Backend)).
74
75 -spec backend_code(module(), atom(), list()) -> {nonempty_string(), list()}.
76 backend_code(Module, Backend, TrackedFuns) when is_atom(Backend) ->
77
:-(
Callbacks = Module:behaviour_info(callbacks),
78
:-(
ModuleStr = atom_to_list(Module),
79
:-(
ProxyModuleName = ModuleStr ++ "_backend",
80
:-(
RealBackendModule = ModuleStr ++ "_" ++ atom_to_list(Backend),
81
:-(
BehaviourExports = [generate_export(F, A) || {F, A} <- Callbacks],
82
83
:-(
BehaviourImpl = [generate_fun(Module, RealBackendModule, F, A, TrackedFuns) ||
84
:-(
{F, A} <- Callbacks],
85
:-(
Code = lists:flatten(
86 ["-module(", ProxyModuleName, ").\n",
87 "-behaviour(backend_module).\n"
88 "-export([backend/0, backend_name/0]).\n",
89 BehaviourExports,
90
91
92 "-spec backend() -> atom().\n",
93 "backend() ->", RealBackendModule, ".\n",
94 "backend_name() ->", atom_to_list(Backend), ".\n",
95 BehaviourImpl
96 ]),
97
:-(
{ProxyModuleName, Code}.
98
99 generate_export(F, A) ->
100
:-(
"-export([" ++ atom_to_list(F) ++ "/" ++ integer_to_list(A) ++ "]).\n".
101
102 generate_fun(BaseModule, RealBackendModule, F, A, TrackedFuns) ->
103
:-(
Args = string:join(["A" ++ integer_to_list(I) || I <- lists:seq(1, A)], ", "),
104
:-(
IsTracked = lists:member(F, TrackedFuns),
105
:-(
[fun_header(F, Args), " ->\n",
106 generate_fun_body(IsTracked, BaseModule, RealBackendModule, F, Args)].
107
108 fun_header(F, Args) ->
109
:-(
[atom_to_list(F), "(", Args, ")"].
110
111
:-(
time_metric(Module, Op) -> [backends, Module, Op].
112
:-(
calls_metric(Module, Op) -> [backends, Module, calls, Op].
113
114 generate_fun_body(false, _, RealBackendModule, F, Args) ->
115
:-(
[" ", RealBackendModule, ":", fun_header(F, Args), ".\n"];
116 generate_fun_body(true, BaseModule, RealBackendModule, F, Args) ->
117
:-(
FS = atom_to_list(F),
118 %% returned is the following
119 %% mongoose_metrics:update(global, calls_metric(Backend, F), 1),
120 %% {Time, Result} = timer:tc(Backend, F, Args),
121 %% mongoose_metrics:update(global, time_metric(Backend, F), Time),
122 %% Result.
123
:-(
CallsMetric = io_lib:format("~p", [calls_metric(BaseModule, F)]),
124
:-(
TimeMetric = io_lib:format("~p", [time_metric(BaseModule, F)]),
125
:-(
[" mongoose_metrics:update(global, ", CallsMetric, ", 1), \n",
126 " {Time, Result} = timer:tc(", RealBackendModule, ", ", FS, ", [", Args, "]), \n",
127 " mongoose_metrics:update(global, ", TimeMetric, ", Time), \n",
128 " Result.\n"].
Line Hits Source