Packages

A simple tracer with recording capabilities that would help you in live debugging.

Current section

Files

Jump to
smart_tracer lib core.ex
Raw

lib/core.ex

defmodule SmartTracer.Core do
@moduledoc false
alias SmartTracer.Utils.Receiver
alias SmartTracer.Utils.ProcessManager
alias SmartTracer.Utils.Recorder
def trace(function, limit, module, opts \\ []) do
# Ensure clean slate every trace start
stop()
record? = Keyword.get(opts, :record, false)
function_info = :erlang.fun_info(function)
arity = function_info[:arity]
module_name = function_info[:module]
function_name = function_info[:name]
ms = get_matchspec(arity, opts[:return])
scope = if opts[:scope] == :local, do: [:local], else: []
return = if opts[:return], do: [:return_to], else: []
tracer_pid = spawn_link(Receiver, :tracer, [])
Process.register(tracer_pid, :tracer)
trace_action_pid = spawn_link(Receiver, :trace_action, [module, limit])
Process.register(trace_action_pid, :trace_action)
number_of_matches = :erlang.trace_pattern({module_name, function_name, arity}, ms, scope)
:erlang.trace(:all, true, [:call, {:tracer, tracer_pid} | return])
# Do not trace tracer processes
:erlang.trace(trace_action_pid, false, [:all])
:erlang.trace(tracer_pid, false, [:all])
if record?, do: Recorder.start_recording()
if number_of_matches > 0, do: :ok, else: :no_matches
end
def stop() do
# Stop tracing
:erlang.trace(:all, false, [:all])
# Disable tracing for all patterns
:erlang.trace_pattern({:_, :_, :_}, false, [:local])
:erlang.trace_pattern({:_, :_, :_}, false, [])
# Kill tracer and trace receiver
ProcessManager.kill(:tracer)
ProcessManager.kill(:trace_action)
# Stop recording traces
Recorder.stop()
end
defp get_matchspec(arity, return?) do
var_placeholders = generate_var_placeholders(arity)
trace_opts = if return?, do: [{:return_trace}], else: []
[{var_placeholders, [], trace_opts}]
end
defp generate_var_placeholders(arity) do
1..arity
|> Enum.to_list()
|> Enum.map(fn num -> :"$#{num}" end)
end
end