-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathsocket_dashboard_live.ex
135 lines (114 loc) · 3.78 KB
/
socket_dashboard_live.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
defmodule LiveDebugger.LiveViews.SocketDashboardLive do
use LiveDebuggerWeb, :live_view
require Logger
alias LiveDebugger.Services.LiveViewScraper
alias LiveDebugger.Services.CallbackTracer
@impl true
def mount(%{"socket_id" => socket_id}, _session, socket) do
socket
|> assign(:socket_id, socket_id)
|> assign(:tracing_session, nil)
|> assign_async_debugged_pid()
|> ok()
end
@impl true
def render(assigns) do
~H"""
<.loading_variant :if={@debugged_pid.status == :loading} />
<.not_found_component :if={@debugged_pid.status == :not_found} />
<.error_component :if={@debugged_pid.status == :error} />
<.container :if={@debugged_pid.status == :ok} max_width="full">
<div>Monitored socket: <span class="text-blue-500">{@socket_id}</span></div>
<div>Debugged PID: <span class="text-blue-500">{inspect(@debugged_pid.result)}</span></div>
</.container>
"""
end
@impl true
def handle_async(:fetch_debugged_pid, {:ok, nil}, socket) do
socket
|> assign(:debugged_pid, %{status: :not_found, result: nil})
|> noreply()
end
@impl true
def handle_async(:fetch_debugged_pid, {:ok, fetched_pid}, socket) do
Process.monitor(fetched_pid)
{:ok, tracing_session} =
CallbackTracer.start_tracing_session(socket.assigns.socket_id, fetched_pid, self())
socket
|> assign(:debugged_pid, %{status: :ok, result: fetched_pid})
|> assign(:tracing_session, tracing_session)
|> noreply()
end
@impl true
def handle_async(:fetch_debugged_pid, {:exit, reason}, socket) do
Logger.error(
"LiveDebugger encountered unexpected error while fetching debugged pid: #{inspect(reason)}"
)
socket
|> assign(:debugged_pid, %{status: :error, result: nil})
|> noreply()
end
@impl true
def handle_info({:DOWN, _, :process, _closed_pid, _}, socket) do
CallbackTracer.stop_tracing_session(socket.assigns.tracing_session)
socket
|> assign_async_debugged_pid()
|> noreply()
end
def handle_info({:new_trace, trace}, socket) do
Logger.debug("Received a new trace: \n#{inspect(trace)}")
{:noreply, socket}
end
@impl true
def terminate(_reason, socket) do
CallbackTracer.stop_tracing_session(socket.assigns.tracing_session)
end
defp loading_variant(assigns) do
~H"""
<div class="h-full flex items-center justify-center">
<.spinner size="md" />
</div>
"""
end
defp not_found_component(assigns) do
~H"""
<div class="h-full flex flex-col items-center justify-center mx-8">
<.icon name="hero-exclamation-circle" class="w-16 h-16" />
<.h2 class="text-center">Debugger disconnected</.h2>
<.h5 class="text-center">
We couldn't find any LiveView associated with the given socket id
</.h5>
<span>You can close this window</span>
</div>
"""
end
defp error_component(assigns) do
~H"""
<div class="h-full flex flex-col items-center justify-center mx-8">
<.icon name="hero-exclamation-circle" class="w-16 h-16" />
<.h2 class="text-center">Unexpected error</.h2>
<.h5 class="text-center">
Debugger encountered unexpected error - check logs for more
</.h5>
<span>You can close this window</span>
</div>
"""
end
defp assign_async_debugged_pid(socket) do
socket_id = socket.assigns.socket_id
# credo:disable-for-lines:9
socket
|> assign(:debugged_pid, %{status: :loading})
|> start_async(:fetch_debugged_pid, fn ->
with nil <- fetch_pid_after(socket_id, 200),
nil <- fetch_pid_after(socket_id, 800),
nil <- fetch_pid_after(socket_id, 1000) do
nil
end
end)
end
defp fetch_pid_after(socket_id, milliseconds) do
Process.sleep(milliseconds)
LiveViewScraper.pid_by_socket_id(socket_id)
end
end