defmodule Tracer.Utils do
@moduledoc """
Some utils functions and reimplemmenting some usefull functions, because if they traced, the
calls, made from tracer shouldn't traced too.
"""
defmacrop waiting(answer, process, mref, timeout) do
quote do
receive do
unquote(answer) = reply ->
Process.demonitor(unquote(mref), [:flush])
{:ok, reply}
{:DOWN, ^unquote(mref), _, _, :noconnection} ->
{:error, {:nodedown, node(unquote(process))}}
{:DOWN, ^unquote(mref), _, _, reason} ->
{:error, reason}
after
unquote(timeout) ->
Process.demonitor(unquote(mref), [:flush])
{:error, :timeout}
end
end
end
def call!(identifier, request, timeout \\ 10000) do
case call(identifier, request, timeout) do
{:ok, reply} -> reply
{:error, error} -> exit(error)
end
end
def call(identifier, request, timeout \\ 10000) do
pid = get_process(identifier)
mref = Process.monitor(pid)
try do
Process.send(pid, {{self(), mref}, request}, [:noconnect])
catch
_, _ -> :ok
end
with {:ok, {_, reply}} <- waiting({^mref, _}, pid, mref, timeout), do: {:ok, reply}
end
defp get_process({name, node}), do: rpc(node, :erlang, :whereis, [name])
defp get_process(pid) when is_pid(pid), do: pid
defp get_process(name), do: :erlang.whereis(name)
def load_modules(node, module_list \\ [Tracer.Utils]) do
data = for m <- module_list, do: :code.get_object_code(m)
for {m, object_code, filename} <- data do
## We need to use :rpc, because on remote node our code may be not loaded and not exists
{:module, _} = :rpc.call(node, :code, :load_binary, [m, filename, object_code])
end
true
end
def rpc(node, module, function, args, timeout \\ 5000) do
tag = make_ref()
pid =
if node() == node do
spawn(Tracer.Utils, :rpc_local, [self(), tag, module, function, args])
else
:erlang.spawn(node, Tracer.Utils, :rpc_local, [self(), tag, module, function, args])
end
mref = Process.monitor(pid)
case waiting({^tag, _}, pid, mref, timeout) do
{:ok, {_, reply}} -> reply
{:error, error} -> exit(error)
end
end
def rpc_local(parent, tag, module, function, args) do
result = apply(module, function, args)
send(parent, {tag, result})
end
def apply_func(func, arguments \\ [])
def apply_func(func, arguments) when not is_list(arguments), do: apply_func(func, List.wrap(arguments))
def apply_func({m, f, opts}, arguments), do: apply(m, f, arguments ++ opts)
def apply_func(fun, arguments), do: apply(fun, arguments)
end