Permalink
Find file
Fetching contributors…
Cannot retrieve contributors at this time
executable file 77 lines (66 sloc) 2.76 KB
defmodule BufferTracer do
use GenServer
def start_link(target_pid), do: GenServer.start_link(__MODULE__, target_pid)
def stats(tracer_pid), do: GenServer.call(tracer_pid, :stats)
def stop(tracer_pid), do: GenServer.stop(tracer_pid)
def init(target_pid) do
:erlang.trace(target_pid, true, [:receive, :send, :garbage_collection, :monotonic_timestamp])
{:ok, %{times: :ets.new(:times, [:duplicate_bag]), assigns: %{}}}
end
def handle_call(:stats, _from, state) do
percentiles = [99, 99.9, 99.99, 99.999, 100]
result =
for key <- [:"push/pull", :gc] do
times = Enum.sort(List.flatten(:ets.match(state.times, {key, :"$1"})))
count = length(times)
{key, %{
percentiles: Enum.zip(percentiles, percentiles(times, Enum.map(percentiles, &(&1 / 100)))),
count: count,
avg: Float.round(Enum.sum(times) / count, 2),
worst_10:
times
|> Enum.reverse()
|> Enum.take(10)
|> Enum.reverse()
}}
end
{:reply, result, state}
end
def handle_info({:trace_ts, _pid, :gc_minor_start, _info, time}, state), do:
{:noreply, assign(state, :gc_minor_start, time)}
def handle_info({:trace_ts, _pid, :gc_major_start, _info, time}, state), do:
{:noreply, assign(state, :gc_major_start, time)}
def handle_info({:trace_ts, _pid, :gc_minor_end, _info, time}, state) do
:ets.insert(state.times, {:gc, round((time - state.assigns.gc_minor_start) / 1000)})
{:noreply, state}
end
def handle_info({:trace_ts, _pid, :gc_major_end, _info, time}, state) do
:ets.insert(state.times, {:gc, round((time - state.assigns.gc_major_start) / 1000)})
{:noreply, state}
end
def handle_info({:trace_ts, _pid, :receive, {:"$gen_call", from, {:push, _msg}}, time}, state), do:
{:noreply, assign(state, from, time)}
def handle_info({:trace_ts, _pid, :receive, {:"$gen_call", from,:pull}, time}, state), do:
{:noreply, assign(state, from, time)}
def handle_info({:trace_ts, _pid, :send, msg, to, time}, state) do
with {ref, _response} <- msg,
{:ok, start_time} <- Map.fetch(state.assigns, {to, ref}) do
:ets.insert(state.times, {:"push/pull", round((time - start_time) / 1000)})
{:noreply, update_in(state.assigns, &Map.delete(&1, {to, ref}))}
else
_ -> {:noreply, state}
end
end
defp assign(state, key, value), do:
put_in(state, [:assigns, key], value)
defp percentiles(sorted_series, percentiles) do
indexed =
sorted_series
|> Stream.with_index()
|> Enum.map(fn({value, index}) -> {index, value} end)
|> Enum.into(%{})
percentiles
|> Enum.map(&min(trunc(Float.ceil(Map.size(indexed) * &1)), Map.size(indexed) - 1))
|> Enum.map(&Map.fetch!(indexed, &1))
end
end