Skip to content

Instantly share code, notes, and snippets.

@garthk
Last active October 13, 2020 02:51
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save garthk/14de47aa19e426cd9d362aaf338ed429 to your computer and use it in GitHub Desktop.
Save garthk/14de47aa19e426cd9d362aaf338ed429 to your computer and use it in GitHub Desktop.
LiveView call time
defmodule LVCallTime do
@moduledoc """
Best used sparingly.
LVCallTime.start()
:timer.sleep(:timer.seconds(20))
LVCallTime.dump()
LVCallTime.stop()
or just
LVCallTime.auto(60)
"""
use GenServer, restart: :transient, shutdown: 1000
require Logger
def auto(seconds \\ 60) do
start()
:timer.sleep(:timer.seconds(seconds))
results = dump()
stop()
results
end
def start do
Logger.warn("Starting call_time tracing...")
{:ok, pid} = GenServer.start_link(__MODULE__, [], name: __MODULE__)
:erlang.trace(:all, true, [:call, {:tracer, pid}])
modules = view_modules() ++ phoenix_live_view_modules()
for module <- modules, {function, arity} <- module.__info__(:functions) do
trace_pattern_mfa = {module, function, arity}
trace_match_spec = [{:_, [], [{:return_trace}]}]
trace_pattern_flags = [:local, :call_time]
:erlang.trace_pattern(trace_pattern_mfa, trace_match_spec, trace_pattern_flags)
end
{:ok, pid, modules}
end
def stop do
GenServer.stop(__MODULE__)
end
def dump do
for {m, f, a} = mfa <- GenServer.call(__MODULE__, :dump),
Kernel.function_exported?(m, f, a),
{:call_time, tups} = :erlang.trace_info(mfa, :call_time),
{pid, count, seconds, microseconds} <- tups,
reduce: %{} do
acc ->
ms = seconds * 1000 + microseconds / 1000
update_in(acc, [mfa], fn
nil ->
{ms, count, MapSet.new([pid])}
{total_ms, total_count, pids} ->
{total_ms + ms, total_count + count, MapSet.put(pids, pid)}
end)
end
|> Enum.sort_by(fn {mfa, {total_ms, _total_count, _pids}} -> total_ms end, :desc)
|> Enum.take(20)
end
@impl true
def init([]) do
{:ok, MapSet.new()}
end
@impl true
def handle_info({:trace, _pid, :call, {module, function, args}}, state) do
mfa = {module, function, Enum.count(args)}
{:noreply, MapSet.put(state, mfa)}
end
def handle_info({:trace, _pid, :return_from, {_module, _function, _arity}, _value}, state) do
{:noreply, state}
end
def handle_info(msg, state) do
Pretty.inspect(msg, limit: 5, printable_limit: 20)
{:noreply, state}
end
@impl true
def handle_call(:dump, _from, state) do
{:reply, state, state}
end
@impl true
def terminate(_reason, _state) do
Logger.warn("Stopping call_time tracing...")
:erlang.trace_pattern({:_, :_, :_}, false, [])
:erlang.trace(:all, false, [])
end
defp view_modules do
for {app, _desc, _version} <- :application.loaded_applications(),
{:ok, modules} = :application.get_key(app, :modules),
module <- modules,
Kernel.function_exported?(module, :__live__, 0),
do: module
end
defp phoenix_live_view_modules do
{:ok, modules} = :application.get_key(:phoenix_live_view, :modules)
modules
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment