From bcce0568bef687ad7052248ac120999f3248b736 Mon Sep 17 00:00:00 2001 From: kraleppa Date: Wed, 28 May 2025 10:12:12 +0200 Subject: [PATCH 01/29] Updated trace topics --- .../gen_servers/callback_tracing_server.ex | 4 ++-- lib/live_debugger/utils/pubsub.ex | 13 +++++++++-- .../helpers/tracing_helper.ex | 6 ++--- .../callback_tracing_server_test.exs | 4 ++-- test/utils/pubsub_test.exs | 22 +++++++++++++++---- 5 files changed, 36 insertions(+), 13 deletions(-) diff --git a/lib/live_debugger/gen_servers/callback_tracing_server.ex b/lib/live_debugger/gen_servers/callback_tracing_server.ex index eb4674a42..ee96ef8d6 100644 --- a/lib/live_debugger/gen_servers/callback_tracing_server.ex +++ b/lib/live_debugger/gen_servers/callback_tracing_server.ex @@ -211,7 +211,7 @@ defmodule LiveDebugger.GenServers.CallbackTracingServer do fun = trace.function pid - |> PubSubUtils.trace_topic(node_id, fun, :call) + |> PubSubUtils.trace_topic_per_node(node_id, fun, :call) |> PubSubUtils.broadcast({:new_trace, trace}) end @@ -227,7 +227,7 @@ defmodule LiveDebugger.GenServers.CallbackTracingServer do end pid - |> PubSubUtils.trace_topic(node_id, fun, :return) + |> PubSubUtils.trace_topic_per_node(node_id, fun, :return) |> PubSubUtils.broadcast({:updated_trace, trace}) end end diff --git a/lib/live_debugger/utils/pubsub.ex b/lib/live_debugger/utils/pubsub.ex index c2d2e5a2c..8d459ebf0 100644 --- a/lib/live_debugger/utils/pubsub.ex +++ b/lib/live_debugger/utils/pubsub.ex @@ -71,16 +71,25 @@ defmodule LiveDebugger.Utils.PubSub do Use `{:new_trace, trace}` or `{:updated_trace, trace}` for broadcasting. """ - @spec trace_topic( + @spec trace_topic_per_node( pid :: pid(), node_id :: TreeNode.id(), fun :: atom(), type :: :call | :return ) :: String.t() - def trace_topic(pid, node_id, fun, type \\ :call) do + def trace_topic_per_node(pid, node_id, fun, type \\ :call) do "#{inspect(pid)}/#{inspect(node_id)}/#{inspect(fun)}/#{inspect(type)}" end + @spec trace_topic_per_pid( + pid :: pid(), + fun :: atom(), + type :: :call | :return + ) :: String.t() + def trace_topic_per_pid(pid, fun, type \\ :call) do + "#{inspect(pid)}/#{inspect(fun)}/#{inspect(type)}" + end + @spec impl() :: module() defp impl() do Application.get_env( diff --git a/lib/live_debugger_web/helpers/tracing_helper.ex b/lib/live_debugger_web/helpers/tracing_helper.ex index 69578a585..d7a43a67c 100644 --- a/lib/live_debugger_web/helpers/tracing_helper.ex +++ b/lib/live_debugger_web/helpers/tracing_helper.ex @@ -142,13 +142,13 @@ defmodule LiveDebuggerWeb.Helpers.TracingHelper do |> Enum.filter(fn {_, active?} -> active? end) |> Enum.flat_map(fn {function, _} -> [ - PubSubUtils.trace_topic( + PubSubUtils.trace_topic_per_node( lv_process.pid, node_id, function, :call ), - PubSubUtils.trace_topic( + PubSubUtils.trace_topic_per_node( lv_process.pid, node_id, function, @@ -164,7 +164,7 @@ defmodule LiveDebuggerWeb.Helpers.TracingHelper do socket.assigns.current_filters.functions |> Enum.map(fn {function, _} -> - PubSubUtils.trace_topic( + PubSubUtils.trace_topic_per_node( lv_process.pid, node_id, function, diff --git a/test/gen_servers/callback_tracing_server_test.exs b/test/gen_servers/callback_tracing_server_test.exs index 10a07781e..18617a006 100644 --- a/test/gen_servers/callback_tracing_server_test.exs +++ b/test/gen_servers/callback_tracing_server_test.exs @@ -128,10 +128,10 @@ defmodule LiveDebugger.GenServers.CallbackTracingServerTest do table = :ets.new(:test_table, [:ordered_set, :public]) expected_trace_call_topic = - PubSubUtils.trace_topic(pid, pid, fun, :call) + PubSubUtils.trace_topic_per_node(pid, pid, fun, :call) expected_trace_return_topic = - PubSubUtils.trace_topic(pid, pid, fun, :return) + PubSubUtils.trace_topic_per_node(pid, pid, fun, :return) MockEtsTableServer |> expect(:table, 2, fn ^pid -> table end) diff --git a/test/utils/pubsub_test.exs b/test/utils/pubsub_test.exs index 8a8d0ec76..dd2cf19d5 100644 --- a/test/utils/pubsub_test.exs +++ b/test/utils/pubsub_test.exs @@ -20,19 +20,33 @@ defmodule LiveDebugger.Utils.PubSubTest do assert "lvdbg/process_status" = PubSubUtils.process_status_topic() end - test "trace_topic/4" do + test "trace_topic_per_node/4" do pid = :c.pid(0, 1, 0) node_id = :c.pid(0, 2, 0) fun = :handle_info assert "#PID<0.1.0>/#PID<0.2.0>/:handle_info/:call" = - PubSubUtils.trace_topic(pid, node_id, fun) + PubSubUtils.trace_topic_per_node(pid, node_id, fun) assert "#PID<0.1.0>/#PID<0.2.0>/:handle_info/:call" = - PubSubUtils.trace_topic(pid, node_id, fun, :call) + PubSubUtils.trace_topic_per_node(pid, node_id, fun, :call) assert "#PID<0.1.0>/#PID<0.2.0>/:handle_info/:return" = - PubSubUtils.trace_topic(pid, node_id, fun, :return) + PubSubUtils.trace_topic_per_node(pid, node_id, fun, :return) + end + + test "trace_topic_per_pid/3" do + pid = :c.pid(0, 1, 0) + fun = :handle_info + + assert "#PID<0.1.0>/:handle_info/:call" = + PubSubUtils.trace_topic_per_pid(pid, fun) + + assert "#PID<0.1.0>/:handle_info/:call" = + PubSubUtils.trace_topic_per_pid(pid, fun, :call) + + assert "#PID<0.1.0>/:handle_info/:return" = + PubSubUtils.trace_topic_per_pid(pid, fun, :return) end describe "mock" do From bfe7cdf44918e5f576652b4dbeb17f28bd509fcc Mon Sep 17 00:00:00 2001 From: kraleppa Date: Wed, 28 May 2025 11:43:17 +0200 Subject: [PATCH 02/29] Added basic global traces view and basic routing --- .../components/navigation_menu.ex | 19 +++++++++++++++---- .../helpers/routes_helper.ex | 5 +++++ .../live/channel_dashboard_live.ex | 3 ++- .../live/global_traces_live.ex | 12 ++++++++++++ lib/live_debugger_web/router.ex | 1 + 5 files changed, 35 insertions(+), 5 deletions(-) create mode 100644 lib/live_debugger_web/live/global_traces_live.ex diff --git a/lib/live_debugger_web/components/navigation_menu.ex b/lib/live_debugger_web/components/navigation_menu.ex index 118acba23..9a0f238b1 100644 --- a/lib/live_debugger_web/components/navigation_menu.ex +++ b/lib/live_debugger_web/components/navigation_menu.ex @@ -5,8 +5,10 @@ defmodule LiveDebuggerWeb.Components.NavigationMenu do use LiveDebuggerWeb, :component alias LiveDebuggerWeb.LiveComponents.LiveDropdown + alias LiveDebuggerWeb.Helpers.RoutesHelper attr(:class, :any, default: nil, doc: "Additional classes to add to the navigation bar.") + attr(:pid, :any, required: true) def sidebar(assigns) do ~H""" @@ -14,14 +16,19 @@ defmodule LiveDebuggerWeb.Components.NavigationMenu do "flex flex-col gap-3 bg-sidebar-bg shadow-custom h-full p-2 border-r border-default-border" | List.wrap(@class) ]}> - <.nav_icon icon="icon-info" /> - <.nav_icon icon="icon-globe" /> + <.link navigate={RoutesHelper.channel_dashboard(@pid)}> + <.nav_icon icon="icon-info" /> + + <.link navigate={RoutesHelper.global_traces(@pid)}> + <.nav_icon icon="icon-globe" /> + """ end attr(:class, :any, default: nil, doc: "Additional classes to add to the navigation bar.") attr(:return_link, :any, required: true, doc: "Link to navigate to.") + attr(:lv_process, :any, required: true) def dropdown(assigns) do ~H""" @@ -39,8 +46,12 @@ defmodule LiveDebuggerWeb.Components.NavigationMenu do - - + <.link :if={@lv_process.ok?} navigate={RoutesHelper.channel_dashboard(@lv_process.result.pid)}> + + + <.link :if={@lv_process.ok?} navigate={RoutesHelper.global_traces(@lv_process.result.pid)}> + + """ diff --git a/lib/live_debugger_web/helpers/routes_helper.ex b/lib/live_debugger_web/helpers/routes_helper.ex index 7083d9f35..0c2e74d42 100644 --- a/lib/live_debugger_web/helpers/routes_helper.ex +++ b/lib/live_debugger_web/helpers/routes_helper.ex @@ -37,4 +37,9 @@ defmodule LiveDebuggerWeb.Helpers.RoutesHelper do def settings(return_to) do ~p"/settings?return_to=#{return_to}" end + + @spec global_traces(pid :: pid()) :: String.t() + def global_traces(pid) when is_pid(pid) do + ~p"/pid/#{Parsers.pid_to_string(pid)}/global_traces" + end end diff --git a/lib/live_debugger_web/live/channel_dashboard_live.ex b/lib/live_debugger_web/live/channel_dashboard_live.ex index 485c0f731..0bf010700 100644 --- a/lib/live_debugger_web/live/channel_dashboard_live.ex +++ b/lib/live_debugger_web/live/channel_dashboard_live.ex @@ -37,6 +37,7 @@ defmodule LiveDebuggerWeb.ChannelDashboardLive do /> @@ -70,7 +71,7 @@ defmodule LiveDebuggerWeb.ChannelDashboardLive do
-
""" end From 1de4a91ca6f6fe3340f90aaf415e95ad0194f181 Mon Sep 17 00:00:00 2001 From: kraleppa Date: Thu, 29 May 2025 10:32:12 +0200 Subject: [PATCH 09/29] Extracted `assign_async_existing_traces` --- .../hooks/traces_live/existing_traces.ex | 98 +++++++++++++++++++ lib/live_debugger_web/live/traces_live.ex | 61 ++---------- 2 files changed, 105 insertions(+), 54 deletions(-) create mode 100644 lib/live_debugger_web/hooks/traces_live/existing_traces.ex diff --git a/lib/live_debugger_web/hooks/traces_live/existing_traces.ex b/lib/live_debugger_web/hooks/traces_live/existing_traces.ex new file mode 100644 index 000000000..fc24b2de1 --- /dev/null +++ b/lib/live_debugger_web/hooks/traces_live/existing_traces.ex @@ -0,0 +1,98 @@ +defmodule LiveDebuggerWeb.Hooks.TracesLive.ExistingTraces do + @moduledoc """ + Assigns introduced by this hook: + - `:traces_continuation` - the continuation token for the existing traces, possible values: `nil`, `:end_of_table`, `ets_continuation()` + - `:traces_empty?` - whether the existing traces are empty, possible values: `true`, `false` + - `:existing_traces_status` - the status of the existing traces, possible values: `:loading`, `:ok`, `:error` + + Streams introduced by this hook: + - `:existing_traces` - the stream of existing traces. + """ + + require Logger + + import Phoenix.LiveView + import Phoenix.Component + import LiveDebuggerWeb.Helpers + + alias LiveDebugger.Services.TraceService + alias LiveDebugger.Structs.TraceDisplay + + @page_size 25 + + def init(socket) do + socket + |> assign(:traces_continuation, nil) + |> assign(:traces_empty?, true) + |> attach_hook(:fetch_existing_traces, :handle_async, &handle_async/3) + end + + def assign_async_existing_traces(socket) do + pid = socket.assigns.lv_process.pid + node_id = socket.assigns.node_id + active_functions = get_active_functions(socket) + execution_times = get_execution_times(socket) + + socket + |> assign(:existing_traces_status, :loading) + |> stream(:existing_traces, [], reset: true) + |> start_async(:fetch_existing_traces, fn -> + TraceService.existing_traces(pid, + node_id: node_id, + limit: @page_size, + functions: active_functions, + execution_times: execution_times + ) + end) + end + + def handle_async(:fetch_existing_traces, {:ok, {trace_list, cont}}, socket) do + trace_list = Enum.map(trace_list, &TraceDisplay.from_trace/1) + + socket + |> assign(:existing_traces_status, :ok) + |> assign(:traces_empty?, false) + |> assign(:traces_continuation, cont) + |> stream(:existing_traces, trace_list) + |> halt() + end + + def handle_async(:fetch_existing_traces, {:ok, :end_of_table}, socket) do + socket + |> assign(:existing_traces_status, :ok) + |> assign(:traces_continuation, :end_of_table) + |> halt() + end + + def handle_async(:fetch_existing_traces, {:exit, reason}, socket) do + log_async_error("fetching existing traces", reason) + + socket + |> assign(:existing_traces_status, :error) + |> halt() + end + + def handle_async(_, _, socket) do + {:cont, socket} + end + + defp get_active_functions(socket) do + socket.assigns.current_filters.functions + |> Enum.filter(fn {_, active?} -> active? end) + |> Enum.map(fn {function, _} -> function end) + end + + defp get_execution_times(socket) do + socket.assigns.current_filters.execution_time + |> Enum.filter(fn {_, value} -> value != "" end) + |> Enum.map(fn {filter, value} -> {filter, String.to_integer(value)} end) + end + + defp log_async_error(operation, reason) do + Logger.error( + "LiveDebugger encountered unexpected error while #{operation}: #{inspect(reason)}" + ) + end + + Phoenix.Live +end diff --git a/lib/live_debugger_web/live/traces_live.ex b/lib/live_debugger_web/live/traces_live.ex index ed8578719..08242c0c1 100644 --- a/lib/live_debugger_web/live/traces_live.ex +++ b/lib/live_debugger_web/live/traces_live.ex @@ -16,6 +16,8 @@ defmodule LiveDebuggerWeb.TracesLive do alias LiveDebugger.Structs.TreeNode alias LiveDebuggerWeb.Components.Traces + alias LiveDebuggerWeb.Hooks.TracesLive.ExistingTraces + @live_stream_limit 128 @page_size 25 @separator %{id: "separator"} @@ -61,18 +63,17 @@ defmodule LiveDebuggerWeb.TracesLive do default_filters = default_filters(node_id) socket + |> ExistingTraces.init() |> assign(:displayed_trace, nil) - |> assign(:traces_continuation, nil) |> assign(current_filters: default_filters) |> assign(default_filters: default_filters) - |> assign(traces_empty?: true) |> assign(trace_callback_running?: false) |> assign(node_id: node_id) |> assign(id: session["id"]) |> assign(root_pid: session["root_pid"]) |> assign(lv_process: lv_process) |> TracingHelper.init() - |> assign_async_existing_traces() + |> ExistingTraces.assign_async_existing_traces() |> ok() end @@ -161,35 +162,6 @@ defmodule LiveDebuggerWeb.TracesLive do """ end - @impl true - def handle_async(:fetch_existing_traces, {:ok, {trace_list, cont}}, socket) do - trace_list = Enum.map(trace_list, &TraceDisplay.from_trace/1) - - socket - |> assign(existing_traces_status: :ok) - |> assign(:traces_empty?, false) - |> assign(:traces_continuation, cont) - |> stream(:existing_traces, trace_list) - |> noreply() - end - - @impl true - def handle_async(:fetch_existing_traces, {:ok, :end_of_table}, socket) do - socket - |> assign(existing_traces_status: :ok) - |> assign(traces_continuation: :end_of_table) - |> noreply() - end - - @impl true - def handle_async(:fetch_existing_traces, {:exit, reason}, socket) do - log_async_error("fetching existing traces", reason) - - socket - |> assign(existing_traces_status: :error) - |> noreply() - end - @impl true def handle_async(:load_more_existing_traces, {:ok, {trace_list, cont}}, socket) do trace_list = Enum.map(trace_list, &TraceDisplay.from_trace/1) @@ -278,7 +250,7 @@ defmodule LiveDebuggerWeb.TracesLive do |> assign(node_id: node_id) |> assign(current_filters: default_filters) |> assign(default_filters: default_filters) - |> assign_async_existing_traces() + |> ExistingTraces.assign_async_existing_traces() |> noreply() end @@ -289,7 +261,7 @@ defmodule LiveDebuggerWeb.TracesLive do socket |> assign(:current_filters, filters) |> assign(:traces_empty?, true) - |> assign_async_existing_traces() + |> ExistingTraces.assign_async_existing_traces() |> noreply() end @@ -369,29 +341,10 @@ defmodule LiveDebuggerWeb.TracesLive do @impl true def handle_event("refresh-history", _, socket) do socket - |> assign_async_existing_traces() + |> ExistingTraces.assign_async_existing_traces() |> noreply() end - defp assign_async_existing_traces(socket) do - pid = socket.assigns.lv_process.pid - node_id = socket.assigns.node_id - active_functions = get_active_functions(socket) - execution_times = get_execution_times(socket) - - socket - |> assign(:existing_traces_status, :loading) - |> stream(:existing_traces, [], reset: true) - |> start_async(:fetch_existing_traces, fn -> - TraceService.existing_traces(pid, - node_id: node_id, - limit: @page_size, - functions: active_functions, - execution_times: execution_times - ) - end) - end - defp load_more_existing_traces(socket) do pid = socket.assigns.lv_process.pid node_id = socket.assigns.node_id From 555e97f2fe3b1cc8e437e939fc09970c4d54f46a Mon Sep 17 00:00:00 2001 From: kraleppa Date: Thu, 29 May 2025 10:47:23 +0200 Subject: [PATCH 10/29] Extracted `load_more_existing_traces` --- .../hooks/traces_live/existing_traces.ex | 60 +++++++++++++++++-- lib/live_debugger_web/live/traces_live.ex | 59 +----------------- 2 files changed, 57 insertions(+), 62 deletions(-) diff --git a/lib/live_debugger_web/hooks/traces_live/existing_traces.ex b/lib/live_debugger_web/hooks/traces_live/existing_traces.ex index fc24b2de1..bd1ed08f9 100644 --- a/lib/live_debugger_web/hooks/traces_live/existing_traces.ex +++ b/lib/live_debugger_web/hooks/traces_live/existing_traces.ex @@ -1,9 +1,15 @@ defmodule LiveDebuggerWeb.Hooks.TracesLive.ExistingTraces do @moduledoc """ + Required assigns: + - `:lv_process` - the LiveView process + - `:current_filters` - the current filters + - `:node_id` - the node ID + Assigns introduced by this hook: - `:traces_continuation` - the continuation token for the existing traces, possible values: `nil`, `:end_of_table`, `ets_continuation()` - `:traces_empty?` - whether the existing traces are empty, possible values: `true`, `false` - `:existing_traces_status` - the status of the existing traces, possible values: `:loading`, `:ok`, `:error` + - `:page_size` - the page size for the existing traces Streams introduced by this hook: - `:existing_traces` - the stream of existing traces. @@ -18,18 +24,18 @@ defmodule LiveDebuggerWeb.Hooks.TracesLive.ExistingTraces do alias LiveDebugger.Services.TraceService alias LiveDebugger.Structs.TraceDisplay - @page_size 25 - - def init(socket) do + def init(socket, page_size) do socket |> assign(:traces_continuation, nil) |> assign(:traces_empty?, true) - |> attach_hook(:fetch_existing_traces, :handle_async, &handle_async/3) + |> assign(:page_size, page_size) + |> attach_hook(:existing_traces, :handle_async, &handle_async/3) end def assign_async_existing_traces(socket) do pid = socket.assigns.lv_process.pid node_id = socket.assigns.node_id + page_size = socket.assigns.page_size active_functions = get_active_functions(socket) execution_times = get_execution_times(socket) @@ -39,7 +45,28 @@ defmodule LiveDebuggerWeb.Hooks.TracesLive.ExistingTraces do |> start_async(:fetch_existing_traces, fn -> TraceService.existing_traces(pid, node_id: node_id, - limit: @page_size, + limit: page_size, + functions: active_functions, + execution_times: execution_times + ) + end) + end + + def assign_async_more_existing_traces(socket) do + pid = socket.assigns.lv_process.pid + node_id = socket.assigns.node_id + cont = socket.assigns.traces_continuation + page_size = socket.assigns.page_size + active_functions = get_active_functions(socket) + execution_times = get_execution_times(socket) + + socket + |> assign(:traces_continuation, :loading) + |> start_async(:load_more_existing_traces, fn -> + TraceService.existing_traces(pid, + node_id: node_id, + limit: page_size, + cont: cont, functions: active_functions, execution_times: execution_times ) @@ -72,6 +99,29 @@ defmodule LiveDebuggerWeb.Hooks.TracesLive.ExistingTraces do |> halt() end + def handle_async(:load_more_existing_traces, {:ok, {trace_list, cont}}, socket) do + trace_list = Enum.map(trace_list, &TraceDisplay.from_trace/1) + + socket + |> assign(:traces_continuation, cont) + |> stream(:existing_traces, trace_list) + |> halt() + end + + def handle_async(:load_more_existing_traces, {:ok, :end_of_table}, socket) do + socket + |> assign(:traces_continuation, :end_of_table) + |> halt() + end + + # TODO: handle this case in a proper way + def handle_async(:load_more_existing_traces, {:exit, reason}, socket) do + log_async_error("loading more existing traces", reason) + + socket + |> halt() + end + def handle_async(_, _, socket) do {:cont, socket} end diff --git a/lib/live_debugger_web/live/traces_live.ex b/lib/live_debugger_web/live/traces_live.ex index 08242c0c1..e4da8f9e4 100644 --- a/lib/live_debugger_web/live/traces_live.ex +++ b/lib/live_debugger_web/live/traces_live.ex @@ -63,7 +63,7 @@ defmodule LiveDebuggerWeb.TracesLive do default_filters = default_filters(node_id) socket - |> ExistingTraces.init() + |> ExistingTraces.init(@page_size) |> assign(:displayed_trace, nil) |> assign(current_filters: default_filters) |> assign(default_filters: default_filters) @@ -162,29 +162,6 @@ defmodule LiveDebuggerWeb.TracesLive do """ end - @impl true - def handle_async(:load_more_existing_traces, {:ok, {trace_list, cont}}, socket) do - trace_list = Enum.map(trace_list, &TraceDisplay.from_trace/1) - - socket - |> assign(:traces_continuation, cont) - |> stream(:existing_traces, trace_list) - |> noreply() - end - - @impl true - def handle_async(:load_more_existing_traces, {:ok, :end_of_table}, socket) do - socket - |> assign(:traces_continuation, :end_of_table) - |> noreply() - end - - @impl true - def handle_async(:load_more_existing_traces, {:exit, reason}, socket) do - log_async_error("loading more existing traces", reason) - socket - end - @impl true def handle_info({:new_trace, trace}, socket) do socket @@ -282,7 +259,7 @@ defmodule LiveDebuggerWeb.TracesLive do @impl true def handle_event("load-more", _, socket) do socket - |> load_more_existing_traces() + |> ExistingTraces.assign_async_more_existing_traces() |> noreply() end @@ -345,26 +322,6 @@ defmodule LiveDebuggerWeb.TracesLive do |> noreply() end - defp load_more_existing_traces(socket) do - pid = socket.assigns.lv_process.pid - node_id = socket.assigns.node_id - cont = socket.assigns.traces_continuation - active_functions = get_active_functions(socket) - execution_times = get_execution_times(socket) - - socket - |> assign(:traces_continuation, :loading) - |> start_async(:load_more_existing_traces, fn -> - TraceService.existing_traces(pid, - node_id: node_id, - limit: @page_size, - cont: cont, - functions: active_functions, - execution_times: execution_times - ) - end) - end - defp default_filters(node_id) do functions = node_id @@ -384,21 +341,9 @@ defmodule LiveDebuggerWeb.TracesLive do } end - defp get_active_functions(socket) do - socket.assigns.current_filters.functions - |> Enum.filter(fn {_, active?} -> active? end) - |> Enum.map(fn {function, _} -> function end) - end - defp get_execution_times(socket) do socket.assigns.current_filters.execution_time |> Enum.filter(fn {_, value} -> value != "" end) |> Enum.map(fn {filter, value} -> {filter, String.to_integer(value)} end) end - - defp log_async_error(operation, reason) do - Logger.error( - "LiveDebugger encountered unexpected error while #{operation}: #{inspect(reason)}" - ) - end end From 5cc1f829d62b0d52ebe21f3dae5076b51484d898 Mon Sep 17 00:00:00 2001 From: kraleppa Date: Thu, 29 May 2025 10:53:05 +0200 Subject: [PATCH 11/29] Changed `page_size` to private --- .../hooks/traces_live/existing_traces.ex | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/live_debugger_web/hooks/traces_live/existing_traces.ex b/lib/live_debugger_web/hooks/traces_live/existing_traces.ex index bd1ed08f9..5c0bdd929 100644 --- a/lib/live_debugger_web/hooks/traces_live/existing_traces.ex +++ b/lib/live_debugger_web/hooks/traces_live/existing_traces.ex @@ -9,7 +9,6 @@ defmodule LiveDebuggerWeb.Hooks.TracesLive.ExistingTraces do - `:traces_continuation` - the continuation token for the existing traces, possible values: `nil`, `:end_of_table`, `ets_continuation()` - `:traces_empty?` - whether the existing traces are empty, possible values: `true`, `false` - `:existing_traces_status` - the status of the existing traces, possible values: `:loading`, `:ok`, `:error` - - `:page_size` - the page size for the existing traces Streams introduced by this hook: - `:existing_traces` - the stream of existing traces. @@ -28,14 +27,14 @@ defmodule LiveDebuggerWeb.Hooks.TracesLive.ExistingTraces do socket |> assign(:traces_continuation, nil) |> assign(:traces_empty?, true) - |> assign(:page_size, page_size) + |> put_private(:page_size, page_size) |> attach_hook(:existing_traces, :handle_async, &handle_async/3) end def assign_async_existing_traces(socket) do pid = socket.assigns.lv_process.pid node_id = socket.assigns.node_id - page_size = socket.assigns.page_size + page_size = socket.private.page_size active_functions = get_active_functions(socket) execution_times = get_execution_times(socket) @@ -56,7 +55,7 @@ defmodule LiveDebuggerWeb.Hooks.TracesLive.ExistingTraces do pid = socket.assigns.lv_process.pid node_id = socket.assigns.node_id cont = socket.assigns.traces_continuation - page_size = socket.assigns.page_size + page_size = socket.private.page_size active_functions = get_active_functions(socket) execution_times = get_execution_times(socket) @@ -143,6 +142,4 @@ defmodule LiveDebuggerWeb.Hooks.TracesLive.ExistingTraces do "LiveDebugger encountered unexpected error while #{operation}: #{inspect(reason)}" ) end - - Phoenix.Live end From 1ff930a235c650f2f2d26f1bc11ad18a034ad1e8 Mon Sep 17 00:00:00 2001 From: kraleppa Date: Thu, 29 May 2025 11:18:05 +0200 Subject: [PATCH 12/29] Basic extraction of incoming traces and checks on required assigns --- .../hooks/traces_live/existing_traces.ex | 23 +++-- .../hooks/traces_live/incoming_traces.ex | 86 +++++++++++++++++++ lib/live_debugger_web/live/traces_live.ex | 74 ++-------------- 3 files changed, 111 insertions(+), 72 deletions(-) create mode 100644 lib/live_debugger_web/hooks/traces_live/incoming_traces.ex diff --git a/lib/live_debugger_web/hooks/traces_live/existing_traces.ex b/lib/live_debugger_web/hooks/traces_live/existing_traces.ex index 5c0bdd929..bcda621ef 100644 --- a/lib/live_debugger_web/hooks/traces_live/existing_traces.ex +++ b/lib/live_debugger_web/hooks/traces_live/existing_traces.ex @@ -1,16 +1,18 @@ defmodule LiveDebuggerWeb.Hooks.TracesLive.ExistingTraces do @moduledoc """ - Required assigns: + Required read-only assigns: - `:lv_process` - the LiveView process - `:current_filters` - the current filters - `:node_id` - the node ID + Required write-only assigns: + - `:traces_empty?` - whether the existing traces are empty, possible values: `true`, `false` + Assigns introduced by this hook: - `:traces_continuation` - the continuation token for the existing traces, possible values: `nil`, `:end_of_table`, `ets_continuation()` - - `:traces_empty?` - whether the existing traces are empty, possible values: `true`, `false` - `:existing_traces_status` - the status of the existing traces, possible values: `:loading`, `:ok`, `:error` - Streams introduced by this hook: + Streams used by this hook: - `:existing_traces` - the stream of existing traces. """ @@ -23,10 +25,13 @@ defmodule LiveDebuggerWeb.Hooks.TracesLive.ExistingTraces do alias LiveDebugger.Services.TraceService alias LiveDebugger.Structs.TraceDisplay - def init(socket, page_size) do + def init_hook(socket, page_size) do socket + |> check_assign(:lv_process) + |> check_assign(:node_id) + |> check_assign(:current_filters) + |> check_assign(:traces_empty?) |> assign(:traces_continuation, nil) - |> assign(:traces_empty?, true) |> put_private(:page_size, page_size) |> attach_hook(:existing_traces, :handle_async, &handle_async/3) end @@ -142,4 +147,12 @@ defmodule LiveDebuggerWeb.Hooks.TracesLive.ExistingTraces do "LiveDebugger encountered unexpected error while #{operation}: #{inspect(reason)}" ) end + + defp check_assign(socket, assign_name) do + if Map.has_key?(socket.assigns, assign_name) do + socket + else + raise "Assign #{assign_name} is required by this hook: #{__MODULE__}" + end + end end diff --git a/lib/live_debugger_web/hooks/traces_live/incoming_traces.ex b/lib/live_debugger_web/hooks/traces_live/incoming_traces.ex new file mode 100644 index 000000000..7109103e4 --- /dev/null +++ b/lib/live_debugger_web/hooks/traces_live/incoming_traces.ex @@ -0,0 +1,86 @@ +defmodule LiveDebuggerWeb.Hooks.TracesLive.IncomingTraces do + @moduledoc """ + Required assigns: + + Assigns introduced by this hook: + + """ + + import Phoenix.LiveView + import Phoenix.Component + import LiveDebuggerWeb.Helpers + + alias LiveDebugger.Structs.TraceDisplay + alias LiveDebuggerWeb.Hooks.Flash + alias LiveDebuggerWeb.Helpers.TracingHelper + alias LiveDebugger.Utils.Parsers + + @live_stream_limit 128 + + def init_hook(socket) do + socket + |> attach_hook(:incoming_traces, :handle_info, &handle_info/2) + end + + def handle_info({:new_trace, trace}, socket) do + socket + |> TracingHelper.check_fuse() + |> case do + {:ok, socket} -> + trace_display = TraceDisplay.from_trace(trace, true) + + socket + |> stream_insert(:existing_traces, trace_display, at: 0, limit: @live_stream_limit) + |> assign(traces_empty?: false) + |> assign(trace_callback_running?: true) + + {:stopped, socket} -> + limit = TracingHelper.trace_limit_per_period() + period = TracingHelper.time_period() |> Parsers.parse_elapsed_time() + + socket.assigns.root_pid + |> Flash.push_flash( + socket, + "Callback tracer stopped: Too many callbacks in a short time. Current limit is #{limit} callbacks in #{period}." + ) + + {_, socket} -> + socket + end + |> halt() + end + + def handle_info({:updated_trace, trace}, socket) when socket.assigns.trace_callback_running? do + trace_display = TraceDisplay.from_trace(trace, true) + + execution_time = get_execution_times(socket) + min_time = Keyword.get(execution_time, :exec_time_min, 0) + max_time = Keyword.get(execution_time, :exec_time_max, :infinity) + + if trace.execution_time >= min_time and trace.execution_time <= max_time do + socket + |> stream_insert(:existing_traces, trace_display, at: 0, limit: @live_stream_limit) + else + socket + |> stream_delete(:existing_traces, trace_display) + end + |> assign(trace_callback_running?: false) + |> TracingHelper.maybe_disable_tracing_after_update() + |> push_event("stop-timer", %{}) + |> halt() + end + + def handle_info({:updated_trace, _trace}, socket) do + {:halt, socket} + end + + def handle_info(_, socket) do + {:cont, socket} + end + + defp get_execution_times(socket) do + socket.assigns.current_filters.execution_time + |> Enum.filter(fn {_, value} -> value != "" end) + |> Enum.map(fn {filter, value} -> {filter, String.to_integer(value)} end) + end +end diff --git a/lib/live_debugger_web/live/traces_live.ex b/lib/live_debugger_web/live/traces_live.ex index e4da8f9e4..394fdaeb4 100644 --- a/lib/live_debugger_web/live/traces_live.ex +++ b/lib/live_debugger_web/live/traces_live.ex @@ -12,13 +12,12 @@ defmodule LiveDebuggerWeb.TracesLive do alias LiveDebugger.Structs.TraceDisplay alias LiveDebugger.Utils.PubSub, as: PubSubUtils alias LiveDebugger.Utils.Callbacks, as: UtilsCallbacks - alias LiveDebugger.Utils.Parsers alias LiveDebugger.Structs.TreeNode alias LiveDebuggerWeb.Components.Traces alias LiveDebuggerWeb.Hooks.TracesLive.ExistingTraces + alias LiveDebuggerWeb.Hooks.TracesLive.IncomingTraces - @live_stream_limit 128 @page_size 25 @separator %{id: "separator"} @@ -63,15 +62,18 @@ defmodule LiveDebuggerWeb.TracesLive do default_filters = default_filters(node_id) socket - |> ExistingTraces.init(@page_size) - |> assign(:displayed_trace, nil) |> assign(current_filters: default_filters) + |> assign(lv_process: lv_process) + |> assign(node_id: node_id) + |> assign(traces_empty?: true) + |> ExistingTraces.init_hook(@page_size) + |> IncomingTraces.init_hook() + |> assign(:displayed_trace, nil) |> assign(default_filters: default_filters) |> assign(trace_callback_running?: false) |> assign(node_id: node_id) |> assign(id: session["id"]) |> assign(root_pid: session["root_pid"]) - |> assign(lv_process: lv_process) |> TracingHelper.init() |> ExistingTraces.assign_async_existing_traces() |> ok() @@ -162,62 +164,6 @@ defmodule LiveDebuggerWeb.TracesLive do """ end - @impl true - def handle_info({:new_trace, trace}, socket) do - socket - |> TracingHelper.check_fuse() - |> case do - {:ok, socket} -> - trace_display = TraceDisplay.from_trace(trace, true) - - socket - |> stream_insert(:existing_traces, trace_display, at: 0, limit: @live_stream_limit) - |> assign(traces_empty?: false) - |> assign(trace_callback_running?: true) - - {:stopped, socket} -> - limit = TracingHelper.trace_limit_per_period() - period = TracingHelper.time_period() |> Parsers.parse_elapsed_time() - - socket.assigns.root_pid - |> push_flash( - socket, - "Callback tracer stopped: Too many callbacks in a short time. Current limit is #{limit} callbacks in #{period}." - ) - - {_, socket} -> - socket - end - |> noreply() - end - - @impl true - def handle_info({:updated_trace, trace}, socket) when socket.assigns.trace_callback_running? do - trace_display = TraceDisplay.from_trace(trace, true) - - execution_time = get_execution_times(socket) - min_time = Keyword.get(execution_time, :exec_time_min, 0) - max_time = Keyword.get(execution_time, :exec_time_max, :infinity) - - if trace.execution_time >= min_time and trace.execution_time <= max_time do - socket - |> stream_insert(:existing_traces, trace_display, at: 0, limit: @live_stream_limit) - else - socket - |> stream_delete(:existing_traces, trace_display) - end - |> assign(trace_callback_running?: false) - |> TracingHelper.maybe_disable_tracing_after_update() - |> push_event("stop-timer", %{}) - |> noreply() - end - - @impl true - def handle_info({:updated_trace, _trace}, socket) do - socket - |> noreply() - end - @impl true def handle_info({:node_changed, node_id}, socket) do default_filters = default_filters(node_id) @@ -340,10 +286,4 @@ defmodule LiveDebuggerWeb.TracesLive do ] } end - - defp get_execution_times(socket) do - socket.assigns.current_filters.execution_time - |> Enum.filter(fn {_, value} -> value != "" end) - |> Enum.map(fn {filter, value} -> {filter, String.to_integer(value)} end) - end end From 39a20f5e9959b5e0bd9d80a3021fd1fe18d59382 Mon Sep 17 00:00:00 2001 From: kraleppa Date: Thu, 29 May 2025 11:54:01 +0200 Subject: [PATCH 13/29] Added checks --- .../helpers/tracing_helper.ex | 21 ++++++++++++++++- .../hooks/traces_live/existing_traces.ex | 19 ++++++++++----- .../hooks/traces_live/incoming_traces.ex | 23 +++++++++++++++++++ lib/live_debugger_web/live/traces_live.ex | 5 ++-- 4 files changed, 59 insertions(+), 9 deletions(-) diff --git a/lib/live_debugger_web/helpers/tracing_helper.ex b/lib/live_debugger_web/helpers/tracing_helper.ex index d7a43a67c..2010ff449 100644 --- a/lib/live_debugger_web/helpers/tracing_helper.ex +++ b/lib/live_debugger_web/helpers/tracing_helper.ex @@ -4,6 +4,12 @@ defmodule LiveDebuggerWeb.Helpers.TracingHelper do It is responsible for determining if the tracing should be stopped. It introduces a fuse mechanism to prevent LiveView from being overloaded with traces. + Required assigns: + - `:lv_process` - the LiveView process + - `:node_id` - the node ID + - `:current_filters` - the current filters + - `:trace_callback_running?` - whether the trace callback is running + """ import Phoenix.Component, only: [assign: 3] @@ -20,7 +26,12 @@ defmodule LiveDebuggerWeb.Helpers.TracingHelper do @spec init(Socket.t()) :: Socket.t() def init(socket) do - clear_tracing(socket) + socket + |> check_assign(:lv_process) + |> check_assign(:node_id) + |> check_assign(:current_filters) + |> check_assign(:trace_callback_running?) + |> clear_tracing() end @spec switch_tracing(Socket.t()) :: Socket.t() @@ -172,4 +183,12 @@ defmodule LiveDebuggerWeb.Helpers.TracingHelper do ) end) end + + defp check_assign(socket, assign_name) do + if Map.has_key?(socket.assigns, assign_name) do + socket + else + raise "Assign #{assign_name} is required by this hook: #{__MODULE__}" + end + end end diff --git a/lib/live_debugger_web/hooks/traces_live/existing_traces.ex b/lib/live_debugger_web/hooks/traces_live/existing_traces.ex index bcda621ef..71d97551d 100644 --- a/lib/live_debugger_web/hooks/traces_live/existing_traces.ex +++ b/lib/live_debugger_web/hooks/traces_live/existing_traces.ex @@ -1,19 +1,17 @@ defmodule LiveDebuggerWeb.Hooks.TracesLive.ExistingTraces do @moduledoc """ - Required read-only assigns: + Required assigns: - `:lv_process` - the LiveView process - `:current_filters` - the current filters - `:node_id` - the node ID - - Required write-only assigns: - `:traces_empty?` - whether the existing traces are empty, possible values: `true`, `false` + Required stream: + - `:existing_traces` - the stream of existing traces. + Assigns introduced by this hook: - `:traces_continuation` - the continuation token for the existing traces, possible values: `nil`, `:end_of_table`, `ets_continuation()` - `:existing_traces_status` - the status of the existing traces, possible values: `:loading`, `:ok`, `:error` - - Streams used by this hook: - - `:existing_traces` - the stream of existing traces. """ require Logger @@ -31,6 +29,7 @@ defmodule LiveDebuggerWeb.Hooks.TracesLive.ExistingTraces do |> check_assign(:node_id) |> check_assign(:current_filters) |> check_assign(:traces_empty?) + |> check_stream(:existing_traces) |> assign(:traces_continuation, nil) |> put_private(:page_size, page_size) |> attach_hook(:existing_traces, :handle_async, &handle_async/3) @@ -155,4 +154,12 @@ defmodule LiveDebuggerWeb.Hooks.TracesLive.ExistingTraces do raise "Assign #{assign_name} is required by this hook: #{__MODULE__}" end end + + defp check_stream(socket, stream_name) do + if Map.has_key?(socket.assigns.streams, stream_name) do + socket + else + raise "Stream #{stream_name} is required by this hook: #{__MODULE__}" + end + end end diff --git a/lib/live_debugger_web/hooks/traces_live/incoming_traces.ex b/lib/live_debugger_web/hooks/traces_live/incoming_traces.ex index 7109103e4..7d9f7254b 100644 --- a/lib/live_debugger_web/hooks/traces_live/incoming_traces.ex +++ b/lib/live_debugger_web/hooks/traces_live/incoming_traces.ex @@ -1,6 +1,11 @@ defmodule LiveDebuggerWeb.Hooks.TracesLive.IncomingTraces do @moduledoc """ Required assigns: + - `:tracing_helper` - the tracing helper + - `:current_filters` - the current filters + - `:trace_callback_running?` - whether the trace callback is running + + Assigns introduced by this hook: @@ -19,6 +24,8 @@ defmodule LiveDebuggerWeb.Hooks.TracesLive.IncomingTraces do def init_hook(socket) do socket + |> check_assign(:tracing_helper) + |> check_stream(:existing_traces) |> attach_hook(:incoming_traces, :handle_info, &handle_info/2) end @@ -83,4 +90,20 @@ defmodule LiveDebuggerWeb.Hooks.TracesLive.IncomingTraces do |> Enum.filter(fn {_, value} -> value != "" end) |> Enum.map(fn {filter, value} -> {filter, String.to_integer(value)} end) end + + defp check_assign(socket, assign_name) do + if Map.has_key?(socket.assigns, assign_name) do + socket + else + raise "Assign #{assign_name} is required by this hook: #{__MODULE__}" + end + end + + defp check_stream(socket, stream_name) do + if Map.has_key?(socket.assigns.streams, stream_name) do + socket + else + raise "Stream #{stream_name} is required by this hook: #{__MODULE__}" + end + end end diff --git a/lib/live_debugger_web/live/traces_live.ex b/lib/live_debugger_web/live/traces_live.ex index 394fdaeb4..0f61f57df 100644 --- a/lib/live_debugger_web/live/traces_live.ex +++ b/lib/live_debugger_web/live/traces_live.ex @@ -66,15 +66,16 @@ defmodule LiveDebuggerWeb.TracesLive do |> assign(lv_process: lv_process) |> assign(node_id: node_id) |> assign(traces_empty?: true) + |> assign(trace_callback_running?: false) + |> stream(:existing_traces, []) + |> TracingHelper.init() |> ExistingTraces.init_hook(@page_size) |> IncomingTraces.init_hook() |> assign(:displayed_trace, nil) |> assign(default_filters: default_filters) - |> assign(trace_callback_running?: false) |> assign(node_id: node_id) |> assign(id: session["id"]) |> assign(root_pid: session["root_pid"]) - |> TracingHelper.init() |> ExistingTraces.assign_async_existing_traces() |> ok() end From 8413c7afc709f03a544a8bacbcf08fb0b802659b Mon Sep 17 00:00:00 2001 From: kraleppa Date: Thu, 29 May 2025 12:15:47 +0200 Subject: [PATCH 14/29] Moved fuse check to tracing helper --- .../helpers/tracing_helper.ex | 36 +++++++++++++++- .../hooks/traces_live/incoming_traces.ex | 43 ++++++------------- lib/live_debugger_web/live/traces_live.ex | 2 +- 3 files changed, 49 insertions(+), 32 deletions(-) diff --git a/lib/live_debugger_web/helpers/tracing_helper.ex b/lib/live_debugger_web/helpers/tracing_helper.ex index 2010ff449..5e36a23d5 100644 --- a/lib/live_debugger_web/helpers/tracing_helper.ex +++ b/lib/live_debugger_web/helpers/tracing_helper.ex @@ -13,9 +13,13 @@ defmodule LiveDebuggerWeb.Helpers.TracingHelper do """ import Phoenix.Component, only: [assign: 3] + import LiveDebuggerWeb.Helpers + import Phoenix.LiveView alias Phoenix.LiveView.Socket alias LiveDebugger.Utils.PubSub, as: PubSubUtils + alias LiveDebuggerWeb.Hooks.Flash + alias LiveDebugger.Utils.Parsers @assign_name :tracing_helper @time_period 1_000_000 @@ -24,13 +28,14 @@ defmodule LiveDebuggerWeb.Helpers.TracingHelper do def trace_limit_per_period(), do: @trace_limit_per_period def time_period(), do: @time_period - @spec init(Socket.t()) :: Socket.t() - def init(socket) do + @spec init_hook(Socket.t()) :: Socket.t() + def init_hook(socket) do socket |> check_assign(:lv_process) |> check_assign(:node_id) |> check_assign(:current_filters) |> check_assign(:trace_callback_running?) + |> attach_hook(:tracing_helper, :handle_info, &handle_info/2) |> clear_tracing() end @@ -77,6 +82,33 @@ defmodule LiveDebuggerWeb.Helpers.TracingHelper do end end + defp handle_info({:new_trace, _}, socket) do + socket + |> check_fuse() + |> case do + {:ok, socket} -> + {:cont, socket} + + {:stopped, socket} -> + limit = trace_limit_per_period() + period = time_period() |> Parsers.parse_elapsed_time() + + socket.assigns.root_pid + |> Flash.push_flash( + socket, + "Callback tracer stopped: Too many callbacks in a short time. Current limit is #{limit} callbacks in #{period}." + ) + |> halt() + + {_, socket} -> + {:halt, socket} + end + end + + defp handle_info(_, socket) do + {:cont, socket} + end + defp period_exceeded?(fuse) do now() - fuse.start_time >= @time_period end diff --git a/lib/live_debugger_web/hooks/traces_live/incoming_traces.ex b/lib/live_debugger_web/hooks/traces_live/incoming_traces.ex index 7d9f7254b..8eaa00d1a 100644 --- a/lib/live_debugger_web/hooks/traces_live/incoming_traces.ex +++ b/lib/live_debugger_web/hooks/traces_live/incoming_traces.ex @@ -1,14 +1,17 @@ defmodule LiveDebuggerWeb.Hooks.TracesLive.IncomingTraces do @moduledoc """ + Has to be declared before TracingHelper hook. + Required assigns: - - `:tracing_helper` - the tracing helper - `:current_filters` - the current filters + - `:traces_empty?` - whether the existing traces are empty, possible values: `true`, `false` - `:trace_callback_running?` - whether the trace callback is running - - Assigns introduced by this hook: + + Required stream: + - `:existing_traces` - the stream of existing traces. """ import Phoenix.LiveView @@ -16,44 +19,26 @@ defmodule LiveDebuggerWeb.Hooks.TracesLive.IncomingTraces do import LiveDebuggerWeb.Helpers alias LiveDebugger.Structs.TraceDisplay - alias LiveDebuggerWeb.Hooks.Flash alias LiveDebuggerWeb.Helpers.TracingHelper - alias LiveDebugger.Utils.Parsers @live_stream_limit 128 def init_hook(socket) do socket - |> check_assign(:tracing_helper) + |> check_assign(:current_filters) + |> check_assign(:traces_empty?) |> check_stream(:existing_traces) + |> check_assign(:trace_callback_running?) |> attach_hook(:incoming_traces, :handle_info, &handle_info/2) end def handle_info({:new_trace, trace}, socket) do + trace_display = TraceDisplay.from_trace(trace, true) + socket - |> TracingHelper.check_fuse() - |> case do - {:ok, socket} -> - trace_display = TraceDisplay.from_trace(trace, true) - - socket - |> stream_insert(:existing_traces, trace_display, at: 0, limit: @live_stream_limit) - |> assign(traces_empty?: false) - |> assign(trace_callback_running?: true) - - {:stopped, socket} -> - limit = TracingHelper.trace_limit_per_period() - period = TracingHelper.time_period() |> Parsers.parse_elapsed_time() - - socket.assigns.root_pid - |> Flash.push_flash( - socket, - "Callback tracer stopped: Too many callbacks in a short time. Current limit is #{limit} callbacks in #{period}." - ) - - {_, socket} -> - socket - end + |> stream_insert(:existing_traces, trace_display, at: 0, limit: @live_stream_limit) + |> assign(traces_empty?: false) + |> assign(trace_callback_running?: true) |> halt() end diff --git a/lib/live_debugger_web/live/traces_live.ex b/lib/live_debugger_web/live/traces_live.ex index 0f61f57df..b8e597916 100644 --- a/lib/live_debugger_web/live/traces_live.ex +++ b/lib/live_debugger_web/live/traces_live.ex @@ -68,7 +68,7 @@ defmodule LiveDebuggerWeb.TracesLive do |> assign(traces_empty?: true) |> assign(trace_callback_running?: false) |> stream(:existing_traces, []) - |> TracingHelper.init() + |> TracingHelper.init_hook() |> ExistingTraces.init_hook(@page_size) |> IncomingTraces.init_hook() |> assign(:displayed_trace, nil) From 678a0ba2449b69a3e3b0133aff1e8d68ff53c4d4 Mon Sep 17 00:00:00 2001 From: kraleppa Date: Thu, 29 May 2025 12:16:39 +0200 Subject: [PATCH 15/29] Fixed module doc --- lib/live_debugger_web/hooks/traces_live/incoming_traces.ex | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/live_debugger_web/hooks/traces_live/incoming_traces.ex b/lib/live_debugger_web/hooks/traces_live/incoming_traces.ex index 8eaa00d1a..08e26a5f7 100644 --- a/lib/live_debugger_web/hooks/traces_live/incoming_traces.ex +++ b/lib/live_debugger_web/hooks/traces_live/incoming_traces.ex @@ -7,9 +7,6 @@ defmodule LiveDebuggerWeb.Hooks.TracesLive.IncomingTraces do - `:traces_empty?` - whether the existing traces are empty, possible values: `true`, `false` - `:trace_callback_running?` - whether the trace callback is running - Assigns introduced by this hook: - - Required stream: - `:existing_traces` - the stream of existing traces. """ From 3d876eed7dd67c15ae8d993de2ca303d107b1103 Mon Sep 17 00:00:00 2001 From: kraleppa Date: Thu, 29 May 2025 12:25:19 +0200 Subject: [PATCH 16/29] Better organization --- lib/live_debugger_web/live/traces_live.ex | 12 ++++++------ .../traces_live/hooks}/existing_traces.ex | 2 +- .../traces_live/hooks}/incoming_traces.ex | 6 ++---- .../traces_live/hooks/tracing_fuse.ex} | 8 +++++++- 4 files changed, 16 insertions(+), 12 deletions(-) rename lib/live_debugger_web/{hooks/traces_live => live/traces_live/hooks}/existing_traces.ex (98%) rename lib/live_debugger_web/{hooks/traces_live => live/traces_live/hooks}/incoming_traces.ex (92%) rename lib/live_debugger_web/{helpers/tracing_helper.ex => live/traces_live/hooks/tracing_fuse.ex} (96%) diff --git a/lib/live_debugger_web/live/traces_live.ex b/lib/live_debugger_web/live/traces_live.ex index b8e597916..9e1d186da 100644 --- a/lib/live_debugger_web/live/traces_live.ex +++ b/lib/live_debugger_web/live/traces_live.ex @@ -7,7 +7,6 @@ defmodule LiveDebuggerWeb.TracesLive do require Logger - alias LiveDebuggerWeb.Helpers.TracingHelper alias LiveDebugger.Services.TraceService alias LiveDebugger.Structs.TraceDisplay alias LiveDebugger.Utils.PubSub, as: PubSubUtils @@ -15,8 +14,9 @@ defmodule LiveDebuggerWeb.TracesLive do alias LiveDebugger.Structs.TreeNode alias LiveDebuggerWeb.Components.Traces - alias LiveDebuggerWeb.Hooks.TracesLive.ExistingTraces - alias LiveDebuggerWeb.Hooks.TracesLive.IncomingTraces + alias LiveDebuggerWeb.Live.TracesLive.Hooks.ExistingTraces + alias LiveDebuggerWeb.Live.TracesLive.Hooks.IncomingTraces + alias LiveDebuggerWeb.Live.TracesLive.Hooks.TracingFuse @page_size 25 @separator %{id: "separator"} @@ -68,7 +68,7 @@ defmodule LiveDebuggerWeb.TracesLive do |> assign(traces_empty?: true) |> assign(trace_callback_running?: false) |> stream(:existing_traces, []) - |> TracingHelper.init_hook() + |> TracingFuse.init_hook() |> ExistingTraces.init_hook(@page_size) |> IncomingTraces.init_hook() |> assign(:displayed_trace, nil) @@ -170,7 +170,7 @@ defmodule LiveDebuggerWeb.TracesLive do default_filters = default_filters(node_id) socket - |> TracingHelper.disable_tracing() + |> TracingFuse.disable_tracing() |> assign(node_id: node_id) |> assign(current_filters: default_filters) |> assign(default_filters: default_filters) @@ -191,7 +191,7 @@ defmodule LiveDebuggerWeb.TracesLive do @impl true def handle_event("switch-tracing", _, socket) do - socket = TracingHelper.switch_tracing(socket) + socket = TracingFuse.switch_tracing(socket) if socket.assigns.tracing_helper.tracing_started? and !socket.assigns.traces_empty? do socket diff --git a/lib/live_debugger_web/hooks/traces_live/existing_traces.ex b/lib/live_debugger_web/live/traces_live/hooks/existing_traces.ex similarity index 98% rename from lib/live_debugger_web/hooks/traces_live/existing_traces.ex rename to lib/live_debugger_web/live/traces_live/hooks/existing_traces.ex index 71d97551d..20c24c139 100644 --- a/lib/live_debugger_web/hooks/traces_live/existing_traces.ex +++ b/lib/live_debugger_web/live/traces_live/hooks/existing_traces.ex @@ -1,4 +1,4 @@ -defmodule LiveDebuggerWeb.Hooks.TracesLive.ExistingTraces do +defmodule LiveDebuggerWeb.Live.TracesLive.Hooks.ExistingTraces do @moduledoc """ Required assigns: - `:lv_process` - the LiveView process diff --git a/lib/live_debugger_web/hooks/traces_live/incoming_traces.ex b/lib/live_debugger_web/live/traces_live/hooks/incoming_traces.ex similarity index 92% rename from lib/live_debugger_web/hooks/traces_live/incoming_traces.ex rename to lib/live_debugger_web/live/traces_live/hooks/incoming_traces.ex index 08e26a5f7..48ec095b3 100644 --- a/lib/live_debugger_web/hooks/traces_live/incoming_traces.ex +++ b/lib/live_debugger_web/live/traces_live/hooks/incoming_traces.ex @@ -1,6 +1,6 @@ -defmodule LiveDebuggerWeb.Hooks.TracesLive.IncomingTraces do +defmodule LiveDebuggerWeb.Live.TracesLive.Hooks.IncomingTraces do @moduledoc """ - Has to be declared before TracingHelper hook. + Has to be declared before TracingFuse hook. Required assigns: - `:current_filters` - the current filters @@ -16,7 +16,6 @@ defmodule LiveDebuggerWeb.Hooks.TracesLive.IncomingTraces do import LiveDebuggerWeb.Helpers alias LiveDebugger.Structs.TraceDisplay - alias LiveDebuggerWeb.Helpers.TracingHelper @live_stream_limit 128 @@ -54,7 +53,6 @@ defmodule LiveDebuggerWeb.Hooks.TracesLive.IncomingTraces do |> stream_delete(:existing_traces, trace_display) end |> assign(trace_callback_running?: false) - |> TracingHelper.maybe_disable_tracing_after_update() |> push_event("stop-timer", %{}) |> halt() end diff --git a/lib/live_debugger_web/helpers/tracing_helper.ex b/lib/live_debugger_web/live/traces_live/hooks/tracing_fuse.ex similarity index 96% rename from lib/live_debugger_web/helpers/tracing_helper.ex rename to lib/live_debugger_web/live/traces_live/hooks/tracing_fuse.ex index 5e36a23d5..0aaf31958 100644 --- a/lib/live_debugger_web/helpers/tracing_helper.ex +++ b/lib/live_debugger_web/live/traces_live/hooks/tracing_fuse.ex @@ -1,4 +1,4 @@ -defmodule LiveDebuggerWeb.Helpers.TracingHelper do +defmodule LiveDebuggerWeb.Live.TracesLive.Hooks.TracingFuse do @moduledoc """ This module provides a helper to manage tracing. It is responsible for determining if the tracing should be stopped. @@ -105,6 +105,12 @@ defmodule LiveDebuggerWeb.Helpers.TracingHelper do end end + defp handle_info({:updated_trace, _}, socket) when socket.assigns.trace_callback_running? do + socket + |> maybe_disable_tracing_after_update() + |> cont() + end + defp handle_info(_, socket) do {:cont, socket} end From 9f3a254946fd3f9c276709578c59926e7d86adc7 Mon Sep 17 00:00:00 2001 From: kraleppa Date: Thu, 29 May 2025 12:29:14 +0200 Subject: [PATCH 17/29] Added helpers --- lib/live_debugger_web/live/traces_live.ex | 23 +--------- .../live/traces_live/helpers.ex | 43 +++++++++++++++++++ .../live/traces_live/hooks/existing_traces.ex | 17 +------- .../live/traces_live/hooks/incoming_traces.ex | 17 +------- .../live/traces_live/hooks/tracing_fuse.ex | 9 +--- 5 files changed, 48 insertions(+), 61 deletions(-) create mode 100644 lib/live_debugger_web/live/traces_live/helpers.ex diff --git a/lib/live_debugger_web/live/traces_live.ex b/lib/live_debugger_web/live/traces_live.ex index 9e1d186da..d0cb8e41e 100644 --- a/lib/live_debugger_web/live/traces_live.ex +++ b/lib/live_debugger_web/live/traces_live.ex @@ -10,14 +10,14 @@ defmodule LiveDebuggerWeb.TracesLive do alias LiveDebugger.Services.TraceService alias LiveDebugger.Structs.TraceDisplay alias LiveDebugger.Utils.PubSub, as: PubSubUtils - alias LiveDebugger.Utils.Callbacks, as: UtilsCallbacks - alias LiveDebugger.Structs.TreeNode alias LiveDebuggerWeb.Components.Traces alias LiveDebuggerWeb.Live.TracesLive.Hooks.ExistingTraces alias LiveDebuggerWeb.Live.TracesLive.Hooks.IncomingTraces alias LiveDebuggerWeb.Live.TracesLive.Hooks.TracingFuse + import LiveDebuggerWeb.Live.TracesLive.Helpers + @page_size 25 @separator %{id: "separator"} @@ -268,23 +268,4 @@ defmodule LiveDebuggerWeb.TracesLive do |> ExistingTraces.assign_async_existing_traces() |> noreply() end - - defp default_filters(node_id) do - functions = - node_id - |> TreeNode.type() - |> case do - :live_view -> UtilsCallbacks.live_view_callbacks() - :live_component -> UtilsCallbacks.live_component_callbacks() - end - |> Enum.map(fn {function, _} -> {function, true} end) - - %{ - functions: functions, - execution_time: [ - {:exec_time_max, ""}, - {:exec_time_min, ""} - ] - } - end end diff --git a/lib/live_debugger_web/live/traces_live/helpers.ex b/lib/live_debugger_web/live/traces_live/helpers.ex new file mode 100644 index 000000000..51fef1d5a --- /dev/null +++ b/lib/live_debugger_web/live/traces_live/helpers.ex @@ -0,0 +1,43 @@ +defmodule LiveDebuggerWeb.Live.TracesLive.Helpers do + @moduledoc """ + This module provides helpers for the TracesLive. + """ + + alias LiveDebugger.Utils.Callbacks, as: UtilsCallbacks + alias LiveDebugger.Structs.TreeNode + + def check_assign(socket, assign_name) do + if Map.has_key?(socket.assigns, assign_name) do + socket + else + raise "Assign #{assign_name} is required." + end + end + + def check_stream(socket, stream_name) do + if Map.has_key?(socket.assigns.streams, stream_name) do + socket + else + raise "Stream #{stream_name} is required." + end + end + + def default_filters(node_id) do + functions = + node_id + |> TreeNode.type() + |> case do + :live_view -> UtilsCallbacks.live_view_callbacks() + :live_component -> UtilsCallbacks.live_component_callbacks() + end + |> Enum.map(fn {function, _} -> {function, true} end) + + %{ + functions: functions, + execution_time: [ + {:exec_time_max, ""}, + {:exec_time_min, ""} + ] + } + end +end diff --git a/lib/live_debugger_web/live/traces_live/hooks/existing_traces.ex b/lib/live_debugger_web/live/traces_live/hooks/existing_traces.ex index 20c24c139..3064ffe5b 100644 --- a/lib/live_debugger_web/live/traces_live/hooks/existing_traces.ex +++ b/lib/live_debugger_web/live/traces_live/hooks/existing_traces.ex @@ -19,6 +19,7 @@ defmodule LiveDebuggerWeb.Live.TracesLive.Hooks.ExistingTraces do import Phoenix.LiveView import Phoenix.Component import LiveDebuggerWeb.Helpers + import LiveDebuggerWeb.Live.TracesLive.Helpers alias LiveDebugger.Services.TraceService alias LiveDebugger.Structs.TraceDisplay @@ -146,20 +147,4 @@ defmodule LiveDebuggerWeb.Live.TracesLive.Hooks.ExistingTraces do "LiveDebugger encountered unexpected error while #{operation}: #{inspect(reason)}" ) end - - defp check_assign(socket, assign_name) do - if Map.has_key?(socket.assigns, assign_name) do - socket - else - raise "Assign #{assign_name} is required by this hook: #{__MODULE__}" - end - end - - defp check_stream(socket, stream_name) do - if Map.has_key?(socket.assigns.streams, stream_name) do - socket - else - raise "Stream #{stream_name} is required by this hook: #{__MODULE__}" - end - end end diff --git a/lib/live_debugger_web/live/traces_live/hooks/incoming_traces.ex b/lib/live_debugger_web/live/traces_live/hooks/incoming_traces.ex index 48ec095b3..ba2c2933d 100644 --- a/lib/live_debugger_web/live/traces_live/hooks/incoming_traces.ex +++ b/lib/live_debugger_web/live/traces_live/hooks/incoming_traces.ex @@ -14,6 +14,7 @@ defmodule LiveDebuggerWeb.Live.TracesLive.Hooks.IncomingTraces do import Phoenix.LiveView import Phoenix.Component import LiveDebuggerWeb.Helpers + import LiveDebuggerWeb.Live.TracesLive.Helpers alias LiveDebugger.Structs.TraceDisplay @@ -70,20 +71,4 @@ defmodule LiveDebuggerWeb.Live.TracesLive.Hooks.IncomingTraces do |> Enum.filter(fn {_, value} -> value != "" end) |> Enum.map(fn {filter, value} -> {filter, String.to_integer(value)} end) end - - defp check_assign(socket, assign_name) do - if Map.has_key?(socket.assigns, assign_name) do - socket - else - raise "Assign #{assign_name} is required by this hook: #{__MODULE__}" - end - end - - defp check_stream(socket, stream_name) do - if Map.has_key?(socket.assigns.streams, stream_name) do - socket - else - raise "Stream #{stream_name} is required by this hook: #{__MODULE__}" - end - end end diff --git a/lib/live_debugger_web/live/traces_live/hooks/tracing_fuse.ex b/lib/live_debugger_web/live/traces_live/hooks/tracing_fuse.ex index 0aaf31958..90f56ccd7 100644 --- a/lib/live_debugger_web/live/traces_live/hooks/tracing_fuse.ex +++ b/lib/live_debugger_web/live/traces_live/hooks/tracing_fuse.ex @@ -15,6 +15,7 @@ defmodule LiveDebuggerWeb.Live.TracesLive.Hooks.TracingFuse do import Phoenix.Component, only: [assign: 3] import LiveDebuggerWeb.Helpers import Phoenix.LiveView + import LiveDebuggerWeb.Live.TracesLive.Helpers alias Phoenix.LiveView.Socket alias LiveDebugger.Utils.PubSub, as: PubSubUtils @@ -221,12 +222,4 @@ defmodule LiveDebuggerWeb.Live.TracesLive.Hooks.TracingFuse do ) end) end - - defp check_assign(socket, assign_name) do - if Map.has_key?(socket.assigns, assign_name) do - socket - else - raise "Assign #{assign_name} is required by this hook: #{__MODULE__}" - end - end end From c3b130ed057b336136d6eb3610684e290139471c Mon Sep 17 00:00:00 2001 From: kraleppa Date: Thu, 29 May 2025 12:43:16 +0200 Subject: [PATCH 18/29] Cleanup --- lib/live_debugger_web/live/traces_live.ex | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/live_debugger_web/live/traces_live.ex b/lib/live_debugger_web/live/traces_live.ex index d0cb8e41e..d6bdbb959 100644 --- a/lib/live_debugger_web/live/traces_live.ex +++ b/lib/live_debugger_web/live/traces_live.ex @@ -73,9 +73,7 @@ defmodule LiveDebuggerWeb.TracesLive do |> IncomingTraces.init_hook() |> assign(:displayed_trace, nil) |> assign(default_filters: default_filters) - |> assign(node_id: node_id) |> assign(id: session["id"]) - |> assign(root_pid: session["root_pid"]) |> ExistingTraces.assign_async_existing_traces() |> ok() end From c685f884c79e3e40d4de815b4ba8eda39637cce6 Mon Sep 17 00:00:00 2001 From: kraleppa Date: Thu, 29 May 2025 14:39:16 +0200 Subject: [PATCH 19/29] Updated descriptions --- lib/live_debugger_web/live/traces_live.ex | 1 + .../live/traces_live/hooks/existing_traces.ex | 31 ++++++--- .../live/traces_live/hooks/incoming_traces.ex | 17 +++-- .../live/traces_live/hooks/tracing_fuse.ex | 68 +++++++++---------- 4 files changed, 65 insertions(+), 52 deletions(-) diff --git a/lib/live_debugger_web/live/traces_live.ex b/lib/live_debugger_web/live/traces_live.ex index d6bdbb959..e8027acaa 100644 --- a/lib/live_debugger_web/live/traces_live.ex +++ b/lib/live_debugger_web/live/traces_live.ex @@ -68,6 +68,7 @@ defmodule LiveDebuggerWeb.TracesLive do |> assign(traces_empty?: true) |> assign(trace_callback_running?: false) |> stream(:existing_traces, []) + |> assign(root_pid: session["root_pid"]) |> TracingFuse.init_hook() |> ExistingTraces.init_hook(@page_size) |> IncomingTraces.init_hook() diff --git a/lib/live_debugger_web/live/traces_live/hooks/existing_traces.ex b/lib/live_debugger_web/live/traces_live/hooks/existing_traces.ex index 3064ffe5b..92edd0e43 100644 --- a/lib/live_debugger_web/live/traces_live/hooks/existing_traces.ex +++ b/lib/live_debugger_web/live/traces_live/hooks/existing_traces.ex @@ -1,6 +1,10 @@ defmodule LiveDebuggerWeb.Live.TracesLive.Hooks.ExistingTraces do @moduledoc """ - Required assigns: + This hook is responsible for fetching the existing traces and displaying them in the LiveView. + It encapsulates logic for async fetching of traces. + It attaches a hook to the `:existing_traces` stream to handle the async fetching. + + Required assigns (that are used somehow in the hook): - `:lv_process` - the LiveView process - `:current_filters` - the current filters - `:node_id` - the node ID @@ -9,7 +13,7 @@ defmodule LiveDebuggerWeb.Live.TracesLive.Hooks.ExistingTraces do Required stream: - `:existing_traces` - the stream of existing traces. - Assigns introduced by this hook: + Assigns introduced by this hook (they can be used outside of the hook): - `:traces_continuation` - the continuation token for the existing traces, possible values: `nil`, `:end_of_table`, `ets_continuation()` - `:existing_traces_status` - the status of the existing traces, possible values: `:loading`, `:ok`, `:error` """ @@ -36,6 +40,10 @@ defmodule LiveDebuggerWeb.Live.TracesLive.Hooks.ExistingTraces do |> attach_hook(:existing_traces, :handle_async, &handle_async/3) end + @doc """ + It loads asynchronously the existing traces and assigns them to the `:existing_traces` stream. + """ + @spec assign_async_existing_traces(Phoenix.LiveView.Socket.t()) :: Phoenix.LiveView.Socket.t() def assign_async_existing_traces(socket) do pid = socket.assigns.lv_process.pid node_id = socket.assigns.node_id @@ -56,6 +64,11 @@ defmodule LiveDebuggerWeb.Live.TracesLive.Hooks.ExistingTraces do end) end + @doc """ + It loads asynchronously more existing traces and assigns them to the `:existing_traces` stream. + """ + @spec assign_async_more_existing_traces(Phoenix.LiveView.Socket.t()) :: + Phoenix.LiveView.Socket.t() def assign_async_more_existing_traces(socket) do pid = socket.assigns.lv_process.pid node_id = socket.assigns.node_id @@ -77,7 +90,7 @@ defmodule LiveDebuggerWeb.Live.TracesLive.Hooks.ExistingTraces do end) end - def handle_async(:fetch_existing_traces, {:ok, {trace_list, cont}}, socket) do + defp handle_async(:fetch_existing_traces, {:ok, {trace_list, cont}}, socket) do trace_list = Enum.map(trace_list, &TraceDisplay.from_trace/1) socket @@ -88,14 +101,14 @@ defmodule LiveDebuggerWeb.Live.TracesLive.Hooks.ExistingTraces do |> halt() end - def handle_async(:fetch_existing_traces, {:ok, :end_of_table}, socket) do + defp handle_async(:fetch_existing_traces, {:ok, :end_of_table}, socket) do socket |> assign(:existing_traces_status, :ok) |> assign(:traces_continuation, :end_of_table) |> halt() end - def handle_async(:fetch_existing_traces, {:exit, reason}, socket) do + defp handle_async(:fetch_existing_traces, {:exit, reason}, socket) do log_async_error("fetching existing traces", reason) socket @@ -103,7 +116,7 @@ defmodule LiveDebuggerWeb.Live.TracesLive.Hooks.ExistingTraces do |> halt() end - def handle_async(:load_more_existing_traces, {:ok, {trace_list, cont}}, socket) do + defp handle_async(:load_more_existing_traces, {:ok, {trace_list, cont}}, socket) do trace_list = Enum.map(trace_list, &TraceDisplay.from_trace/1) socket @@ -112,21 +125,21 @@ defmodule LiveDebuggerWeb.Live.TracesLive.Hooks.ExistingTraces do |> halt() end - def handle_async(:load_more_existing_traces, {:ok, :end_of_table}, socket) do + defp handle_async(:load_more_existing_traces, {:ok, :end_of_table}, socket) do socket |> assign(:traces_continuation, :end_of_table) |> halt() end # TODO: handle this case in a proper way - def handle_async(:load_more_existing_traces, {:exit, reason}, socket) do + defp handle_async(:load_more_existing_traces, {:exit, reason}, socket) do log_async_error("loading more existing traces", reason) socket |> halt() end - def handle_async(_, _, socket) do + defp handle_async(_, _, socket) do {:cont, socket} end diff --git a/lib/live_debugger_web/live/traces_live/hooks/incoming_traces.ex b/lib/live_debugger_web/live/traces_live/hooks/incoming_traces.ex index ba2c2933d..e7e6e33d9 100644 --- a/lib/live_debugger_web/live/traces_live/hooks/incoming_traces.ex +++ b/lib/live_debugger_web/live/traces_live/hooks/incoming_traces.ex @@ -1,8 +1,13 @@ defmodule LiveDebuggerWeb.Live.TracesLive.Hooks.IncomingTraces do @moduledoc """ - Has to be declared before TracingFuse hook. + This hook is responsible for handling incoming traces + It is responsible for inserting new traces into the `:existing_traces` stream. + It also handles the case when the trace callback is running - Required assigns: + This hook has to be added after TracingFuse hook - they're both handling `:new_trace` and `:updated_trace` messages. + TracingFuse has to be added first because it's responsible for stopping the trace callback. + + Required assigns (that are used somehow in the hook): - `:current_filters` - the current filters - `:traces_empty?` - whether the existing traces are empty, possible values: `true`, `false` - `:trace_callback_running?` - whether the trace callback is running @@ -29,7 +34,7 @@ defmodule LiveDebuggerWeb.Live.TracesLive.Hooks.IncomingTraces do |> attach_hook(:incoming_traces, :handle_info, &handle_info/2) end - def handle_info({:new_trace, trace}, socket) do + defp handle_info({:new_trace, trace}, socket) do trace_display = TraceDisplay.from_trace(trace, true) socket @@ -39,7 +44,7 @@ defmodule LiveDebuggerWeb.Live.TracesLive.Hooks.IncomingTraces do |> halt() end - def handle_info({:updated_trace, trace}, socket) when socket.assigns.trace_callback_running? do + defp handle_info({:updated_trace, trace}, socket) when socket.assigns.trace_callback_running? do trace_display = TraceDisplay.from_trace(trace, true) execution_time = get_execution_times(socket) @@ -58,11 +63,11 @@ defmodule LiveDebuggerWeb.Live.TracesLive.Hooks.IncomingTraces do |> halt() end - def handle_info({:updated_trace, _trace}, socket) do + defp handle_info({:updated_trace, _trace}, socket) do {:halt, socket} end - def handle_info(_, socket) do + defp handle_info(_, socket) do {:cont, socket} end diff --git a/lib/live_debugger_web/live/traces_live/hooks/tracing_fuse.ex b/lib/live_debugger_web/live/traces_live/hooks/tracing_fuse.ex index 90f56ccd7..ddff1d3b0 100644 --- a/lib/live_debugger_web/live/traces_live/hooks/tracing_fuse.ex +++ b/lib/live_debugger_web/live/traces_live/hooks/tracing_fuse.ex @@ -1,15 +1,18 @@ defmodule LiveDebuggerWeb.Live.TracesLive.Hooks.TracingFuse do @moduledoc """ - This module provides a helper to manage tracing. + This hook is responsible for managing the tracing fuse. It is responsible for determining if the tracing should be stopped. It introduces a fuse mechanism to prevent LiveView from being overloaded with traces. + It also handles the case when the trace callback is running. - Required assigns: + This hook has to be added before IncomingTraces hook. + + Required assigns (that are used somehow in the hook): - `:lv_process` - the LiveView process - `:node_id` - the node ID - `:current_filters` - the current filters + - `:root_pid` - the root PID - `:trace_callback_running?` - whether the trace callback is running - """ import Phoenix.Component, only: [assign: 3] @@ -26,9 +29,6 @@ defmodule LiveDebuggerWeb.Live.TracesLive.Hooks.TracingFuse do @time_period 1_000_000 @trace_limit_per_period 100 - def trace_limit_per_period(), do: @trace_limit_per_period - def time_period(), do: @time_period - @spec init_hook(Socket.t()) :: Socket.t() def init_hook(socket) do socket @@ -36,6 +36,7 @@ defmodule LiveDebuggerWeb.Live.TracesLive.Hooks.TracingFuse do |> check_assign(:node_id) |> check_assign(:current_filters) |> check_assign(:trace_callback_running?) + |> check_assign(:root_pid) |> attach_hook(:tracing_helper, :handle_info, &handle_info/2) |> clear_tracing() end @@ -54,35 +55,6 @@ defmodule LiveDebuggerWeb.Live.TracesLive.Hooks.TracingFuse do clear_tracing(socket) end - @spec maybe_disable_tracing_after_update(Socket.t()) :: Socket.t() - def maybe_disable_tracing_after_update(socket) do - if socket.assigns[@assign_name].tracing_started? do - socket - else - clear_tracing(socket) - end - end - - @doc """ - Checks if the fuse is blown and stops tracing if it is. - It uses the `#{@assign_name}` assign to store information. - When tracing is not started returns `{:noop, socket}`. - """ - @spec check_fuse(Socket.t()) :: {:ok | :stopped | :noop, Socket.t()} - def check_fuse(%{assigns: %{@assign_name => %{tracing_started?: false}}} = socket) do - {:noop, socket} - end - - def check_fuse(%{assigns: %{@assign_name => %{tracing_started?: true}}} = socket) do - fuse = socket.assigns[@assign_name].fuse - - cond do - period_exceeded?(fuse) -> {:ok, reset_fuse(socket)} - count_exceeded?(fuse) -> {:stopped, clear_tracing(socket)} - true -> {:ok, increment_fuse(socket)} - end - end - defp handle_info({:new_trace, _}, socket) do socket |> check_fuse() @@ -91,8 +63,8 @@ defmodule LiveDebuggerWeb.Live.TracesLive.Hooks.TracingFuse do {:cont, socket} {:stopped, socket} -> - limit = trace_limit_per_period() - period = time_period() |> Parsers.parse_elapsed_time() + limit = @trace_limit_per_period + period = @time_period |> Parsers.parse_elapsed_time() socket.assigns.root_pid |> Flash.push_flash( @@ -116,6 +88,28 @@ defmodule LiveDebuggerWeb.Live.TracesLive.Hooks.TracingFuse do {:cont, socket} end + defp maybe_disable_tracing_after_update(socket) do + if socket.assigns[@assign_name].tracing_started? do + socket + else + clear_tracing(socket) + end + end + + def check_fuse(%{assigns: %{@assign_name => %{tracing_started?: false}}} = socket) do + {:noop, socket} + end + + def check_fuse(%{assigns: %{@assign_name => %{tracing_started?: true}}} = socket) do + fuse = socket.assigns[@assign_name].fuse + + cond do + period_exceeded?(fuse) -> {:ok, reset_fuse(socket)} + count_exceeded?(fuse) -> {:stopped, clear_tracing(socket)} + true -> {:ok, increment_fuse(socket)} + end + end + defp period_exceeded?(fuse) do now() - fuse.start_time >= @time_period end From ee5f8bf37def563c46711795b37533a515953350 Mon Sep 17 00:00:00 2001 From: kraleppa Date: Thu, 29 May 2025 14:41:40 +0200 Subject: [PATCH 20/29] Refactored helpers --- lib/live_debugger_web/live/traces_live/helpers.ex | 15 +++++++++++++-- .../live/traces_live/hooks/existing_traces.ex | 10 +++++----- .../live/traces_live/hooks/incoming_traces.ex | 8 ++++---- .../live/traces_live/hooks/tracing_fuse.ex | 10 +++++----- 4 files changed, 27 insertions(+), 16 deletions(-) diff --git a/lib/live_debugger_web/live/traces_live/helpers.ex b/lib/live_debugger_web/live/traces_live/helpers.ex index 51fef1d5a..3e378ad44 100644 --- a/lib/live_debugger_web/live/traces_live/helpers.ex +++ b/lib/live_debugger_web/live/traces_live/helpers.ex @@ -6,7 +6,11 @@ defmodule LiveDebuggerWeb.Live.TracesLive.Helpers do alias LiveDebugger.Utils.Callbacks, as: UtilsCallbacks alias LiveDebugger.Structs.TreeNode - def check_assign(socket, assign_name) do + @doc """ + Checks if the assign is present in the socket. + If not, it raises an error. + """ + def check_assign!(socket, assign_name) do if Map.has_key?(socket.assigns, assign_name) do socket else @@ -14,7 +18,11 @@ defmodule LiveDebuggerWeb.Live.TracesLive.Helpers do end end - def check_stream(socket, stream_name) do + @doc """ + Checks if the stream is present in the socket. + If not, it raises an error. + """ + def check_stream!(socket, stream_name) do if Map.has_key?(socket.assigns.streams, stream_name) do socket else @@ -22,6 +30,9 @@ defmodule LiveDebuggerWeb.Live.TracesLive.Helpers do end end + @doc """ + Returns the default filters for the traces. + """ def default_filters(node_id) do functions = node_id diff --git a/lib/live_debugger_web/live/traces_live/hooks/existing_traces.ex b/lib/live_debugger_web/live/traces_live/hooks/existing_traces.ex index 92edd0e43..ea3cf45d1 100644 --- a/lib/live_debugger_web/live/traces_live/hooks/existing_traces.ex +++ b/lib/live_debugger_web/live/traces_live/hooks/existing_traces.ex @@ -30,11 +30,11 @@ defmodule LiveDebuggerWeb.Live.TracesLive.Hooks.ExistingTraces do def init_hook(socket, page_size) do socket - |> check_assign(:lv_process) - |> check_assign(:node_id) - |> check_assign(:current_filters) - |> check_assign(:traces_empty?) - |> check_stream(:existing_traces) + |> check_assign!(:lv_process) + |> check_assign!(:node_id) + |> check_assign!(:current_filters) + |> check_assign!(:traces_empty?) + |> check_stream!(:existing_traces) |> assign(:traces_continuation, nil) |> put_private(:page_size, page_size) |> attach_hook(:existing_traces, :handle_async, &handle_async/3) diff --git a/lib/live_debugger_web/live/traces_live/hooks/incoming_traces.ex b/lib/live_debugger_web/live/traces_live/hooks/incoming_traces.ex index e7e6e33d9..15bc70079 100644 --- a/lib/live_debugger_web/live/traces_live/hooks/incoming_traces.ex +++ b/lib/live_debugger_web/live/traces_live/hooks/incoming_traces.ex @@ -27,10 +27,10 @@ defmodule LiveDebuggerWeb.Live.TracesLive.Hooks.IncomingTraces do def init_hook(socket) do socket - |> check_assign(:current_filters) - |> check_assign(:traces_empty?) - |> check_stream(:existing_traces) - |> check_assign(:trace_callback_running?) + |> check_assign!(:current_filters) + |> check_assign!(:traces_empty?) + |> check_stream!(:existing_traces) + |> check_assign!(:trace_callback_running?) |> attach_hook(:incoming_traces, :handle_info, &handle_info/2) end diff --git a/lib/live_debugger_web/live/traces_live/hooks/tracing_fuse.ex b/lib/live_debugger_web/live/traces_live/hooks/tracing_fuse.ex index ddff1d3b0..320ed80e7 100644 --- a/lib/live_debugger_web/live/traces_live/hooks/tracing_fuse.ex +++ b/lib/live_debugger_web/live/traces_live/hooks/tracing_fuse.ex @@ -32,11 +32,11 @@ defmodule LiveDebuggerWeb.Live.TracesLive.Hooks.TracingFuse do @spec init_hook(Socket.t()) :: Socket.t() def init_hook(socket) do socket - |> check_assign(:lv_process) - |> check_assign(:node_id) - |> check_assign(:current_filters) - |> check_assign(:trace_callback_running?) - |> check_assign(:root_pid) + |> check_assign!(:lv_process) + |> check_assign!(:node_id) + |> check_assign!(:current_filters) + |> check_assign!(:trace_callback_running?) + |> check_assign!(:root_pid) |> attach_hook(:tracing_helper, :handle_info, &handle_info/2) |> clear_tracing() end From d2ac78a279471ce266ed0da88de8ed0ddfd7cde5 Mon Sep 17 00:00:00 2001 From: kraleppa Date: Fri, 30 May 2025 09:42:27 +0200 Subject: [PATCH 21/29] Changed function to private --- lib/live_debugger_web/live/traces_live/hooks/tracing_fuse.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/live_debugger_web/live/traces_live/hooks/tracing_fuse.ex b/lib/live_debugger_web/live/traces_live/hooks/tracing_fuse.ex index 320ed80e7..37b982bab 100644 --- a/lib/live_debugger_web/live/traces_live/hooks/tracing_fuse.ex +++ b/lib/live_debugger_web/live/traces_live/hooks/tracing_fuse.ex @@ -96,11 +96,11 @@ defmodule LiveDebuggerWeb.Live.TracesLive.Hooks.TracingFuse do end end - def check_fuse(%{assigns: %{@assign_name => %{tracing_started?: false}}} = socket) do + defp check_fuse(%{assigns: %{@assign_name => %{tracing_started?: false}}} = socket) do {:noop, socket} end - def check_fuse(%{assigns: %{@assign_name => %{tracing_started?: true}}} = socket) do + defp check_fuse(%{assigns: %{@assign_name => %{tracing_started?: true}}} = socket) do fuse = socket.assigns[@assign_name].fuse cond do From e2fb11f91656cb284b043ab7b874f80869506c2d Mon Sep 17 00:00:00 2001 From: kraleppa Date: Fri, 30 May 2025 10:02:16 +0200 Subject: [PATCH 22/29] Moved load more button to a new component --- lib/live_debugger_web/components/traces.ex | 25 ++++++++++++++++++++++ lib/live_debugger_web/live/traces_live.ex | 18 ++++------------ 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/lib/live_debugger_web/components/traces.ex b/lib/live_debugger_web/components/traces.ex index e81c3c03f..b12b16fca 100644 --- a/lib/live_debugger_web/components/traces.ex +++ b/lib/live_debugger_web/components/traces.ex @@ -191,6 +191,31 @@ defmodule LiveDebuggerWeb.Components.Traces do """ end + attr(:traces_continuation, :any, required: true) + attr(:tracing_helper, :any, required: true) + + def load_more_button(%{traces_continuation: nil} = assigns), do: ~H"" + def load_more_button(%{traces_continuation: :end_of_table} = assigns), do: ~H"" + def load_more_button(%{tracing_helper: %{tracing_started?: true}} = assigns), do: ~H"" + + def load_more_button(%{traces_continuation: :loading} = assigns) do + ~H""" +
+ <.spinner size="sm" class="mb-4" /> +
+ """ + end + + def load_more_button(assigns) do + ~H""" +
+ <.button phx-click="load-more" class="w-4 mb-4" variant="secondary"> + Load more + +
+ """ + end + def get_threshold_class(execution_time) do cond do execution_time == nil -> "" diff --git a/lib/live_debugger_web/live/traces_live.ex b/lib/live_debugger_web/live/traces_live.ex index e8027acaa..afed49422 100644 --- a/lib/live_debugger_web/live/traces_live.ex +++ b/lib/live_debugger_web/live/traces_live.ex @@ -143,20 +143,10 @@ defmodule LiveDebuggerWeb.TracesLive do <% end %> <% end %> -
- <%= if @traces_continuation != :loading do %> - <.button - :if={not @tracing_helper.tracing_started? && @traces_continuation != :end_of_table} - phx-click="load-more" - class="w-4 mb-4" - variant="secondary" - > - Load more - - <% else %> - <.spinner size="sm" class="mb-4" /> - <% end %> -
+ From 82d4e6ac928c1f6c87b0ce8aeb8eeb7af3259033 Mon Sep 17 00:00:00 2001 From: kraleppa Date: Fri, 30 May 2025 10:04:01 +0200 Subject: [PATCH 23/29] Moved hooks --- .../hooks => hooks/traces_live_view}/existing_traces.ex | 2 +- .../hooks => hooks/traces_live_view}/incoming_traces.ex | 2 +- .../hooks => hooks/traces_live_view}/tracing_fuse.ex | 2 +- lib/live_debugger_web/live/traces_live.ex | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) rename lib/live_debugger_web/{live/traces_live/hooks => hooks/traces_live_view}/existing_traces.ex (98%) rename lib/live_debugger_web/{live/traces_live/hooks => hooks/traces_live_view}/incoming_traces.ex (97%) rename lib/live_debugger_web/{live/traces_live/hooks => hooks/traces_live_view}/tracing_fuse.ex (98%) diff --git a/lib/live_debugger_web/live/traces_live/hooks/existing_traces.ex b/lib/live_debugger_web/hooks/traces_live_view/existing_traces.ex similarity index 98% rename from lib/live_debugger_web/live/traces_live/hooks/existing_traces.ex rename to lib/live_debugger_web/hooks/traces_live_view/existing_traces.ex index ea3cf45d1..074ce669f 100644 --- a/lib/live_debugger_web/live/traces_live/hooks/existing_traces.ex +++ b/lib/live_debugger_web/hooks/traces_live_view/existing_traces.ex @@ -1,4 +1,4 @@ -defmodule LiveDebuggerWeb.Live.TracesLive.Hooks.ExistingTraces do +defmodule LiveDebuggerWeb.Hooks.TracesLiveView.ExistingTraces do @moduledoc """ This hook is responsible for fetching the existing traces and displaying them in the LiveView. It encapsulates logic for async fetching of traces. diff --git a/lib/live_debugger_web/live/traces_live/hooks/incoming_traces.ex b/lib/live_debugger_web/hooks/traces_live_view/incoming_traces.ex similarity index 97% rename from lib/live_debugger_web/live/traces_live/hooks/incoming_traces.ex rename to lib/live_debugger_web/hooks/traces_live_view/incoming_traces.ex index 15bc70079..0515fb5ed 100644 --- a/lib/live_debugger_web/live/traces_live/hooks/incoming_traces.ex +++ b/lib/live_debugger_web/hooks/traces_live_view/incoming_traces.ex @@ -1,4 +1,4 @@ -defmodule LiveDebuggerWeb.Live.TracesLive.Hooks.IncomingTraces do +defmodule LiveDebuggerWeb.Hooks.TracesLiveView.IncomingTraces do @moduledoc """ This hook is responsible for handling incoming traces It is responsible for inserting new traces into the `:existing_traces` stream. diff --git a/lib/live_debugger_web/live/traces_live/hooks/tracing_fuse.ex b/lib/live_debugger_web/hooks/traces_live_view/tracing_fuse.ex similarity index 98% rename from lib/live_debugger_web/live/traces_live/hooks/tracing_fuse.ex rename to lib/live_debugger_web/hooks/traces_live_view/tracing_fuse.ex index 37b982bab..eb79a38d8 100644 --- a/lib/live_debugger_web/live/traces_live/hooks/tracing_fuse.ex +++ b/lib/live_debugger_web/hooks/traces_live_view/tracing_fuse.ex @@ -1,4 +1,4 @@ -defmodule LiveDebuggerWeb.Live.TracesLive.Hooks.TracingFuse do +defmodule LiveDebuggerWeb.Hooks.TracesLiveView.TracingFuse do @moduledoc """ This hook is responsible for managing the tracing fuse. It is responsible for determining if the tracing should be stopped. diff --git a/lib/live_debugger_web/live/traces_live.ex b/lib/live_debugger_web/live/traces_live.ex index afed49422..80b1c07b7 100644 --- a/lib/live_debugger_web/live/traces_live.ex +++ b/lib/live_debugger_web/live/traces_live.ex @@ -12,9 +12,9 @@ defmodule LiveDebuggerWeb.TracesLive do alias LiveDebugger.Utils.PubSub, as: PubSubUtils alias LiveDebuggerWeb.Components.Traces - alias LiveDebuggerWeb.Live.TracesLive.Hooks.ExistingTraces - alias LiveDebuggerWeb.Live.TracesLive.Hooks.IncomingTraces - alias LiveDebuggerWeb.Live.TracesLive.Hooks.TracingFuse + alias LiveDebuggerWeb.Hooks.TracesLiveView.ExistingTraces + alias LiveDebuggerWeb.Hooks.TracesLiveView.IncomingTraces + alias LiveDebuggerWeb.Hooks.TracesLiveView.TracingFuse import LiveDebuggerWeb.Live.TracesLive.Helpers From da2a893a9cd097885fc8bd12795fe0b72e3e623d Mon Sep 17 00:00:00 2001 From: kraleppa Date: Fri, 30 May 2025 10:05:48 +0200 Subject: [PATCH 24/29] Moved helpers --- .../helpers.ex => helpers/traces_live_view_helper.ex} | 2 +- lib/live_debugger_web/hooks/traces_live_view/existing_traces.ex | 2 +- lib/live_debugger_web/hooks/traces_live_view/incoming_traces.ex | 2 +- lib/live_debugger_web/hooks/traces_live_view/tracing_fuse.ex | 2 +- lib/live_debugger_web/live/traces_live.ex | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) rename lib/live_debugger_web/{live/traces_live/helpers.ex => helpers/traces_live_view_helper.ex} (95%) diff --git a/lib/live_debugger_web/live/traces_live/helpers.ex b/lib/live_debugger_web/helpers/traces_live_view_helper.ex similarity index 95% rename from lib/live_debugger_web/live/traces_live/helpers.ex rename to lib/live_debugger_web/helpers/traces_live_view_helper.ex index 3e378ad44..d0eeaec01 100644 --- a/lib/live_debugger_web/live/traces_live/helpers.ex +++ b/lib/live_debugger_web/helpers/traces_live_view_helper.ex @@ -1,4 +1,4 @@ -defmodule LiveDebuggerWeb.Live.TracesLive.Helpers do +defmodule LiveDebuggerWeb.Helpers.TracesLiveViewHelper do @moduledoc """ This module provides helpers for the TracesLive. """ diff --git a/lib/live_debugger_web/hooks/traces_live_view/existing_traces.ex b/lib/live_debugger_web/hooks/traces_live_view/existing_traces.ex index 074ce669f..6cf885471 100644 --- a/lib/live_debugger_web/hooks/traces_live_view/existing_traces.ex +++ b/lib/live_debugger_web/hooks/traces_live_view/existing_traces.ex @@ -23,7 +23,7 @@ defmodule LiveDebuggerWeb.Hooks.TracesLiveView.ExistingTraces do import Phoenix.LiveView import Phoenix.Component import LiveDebuggerWeb.Helpers - import LiveDebuggerWeb.Live.TracesLive.Helpers + import LiveDebuggerWeb.Helpers.TracesLiveViewHelper alias LiveDebugger.Services.TraceService alias LiveDebugger.Structs.TraceDisplay diff --git a/lib/live_debugger_web/hooks/traces_live_view/incoming_traces.ex b/lib/live_debugger_web/hooks/traces_live_view/incoming_traces.ex index 0515fb5ed..49993a572 100644 --- a/lib/live_debugger_web/hooks/traces_live_view/incoming_traces.ex +++ b/lib/live_debugger_web/hooks/traces_live_view/incoming_traces.ex @@ -19,7 +19,7 @@ defmodule LiveDebuggerWeb.Hooks.TracesLiveView.IncomingTraces do import Phoenix.LiveView import Phoenix.Component import LiveDebuggerWeb.Helpers - import LiveDebuggerWeb.Live.TracesLive.Helpers + import LiveDebuggerWeb.Helpers.TracesLiveViewHelper alias LiveDebugger.Structs.TraceDisplay diff --git a/lib/live_debugger_web/hooks/traces_live_view/tracing_fuse.ex b/lib/live_debugger_web/hooks/traces_live_view/tracing_fuse.ex index eb79a38d8..d449e0f6b 100644 --- a/lib/live_debugger_web/hooks/traces_live_view/tracing_fuse.ex +++ b/lib/live_debugger_web/hooks/traces_live_view/tracing_fuse.ex @@ -18,7 +18,7 @@ defmodule LiveDebuggerWeb.Hooks.TracesLiveView.TracingFuse do import Phoenix.Component, only: [assign: 3] import LiveDebuggerWeb.Helpers import Phoenix.LiveView - import LiveDebuggerWeb.Live.TracesLive.Helpers + import LiveDebuggerWeb.Helpers.TracesLiveViewHelper alias Phoenix.LiveView.Socket alias LiveDebugger.Utils.PubSub, as: PubSubUtils diff --git a/lib/live_debugger_web/live/traces_live.ex b/lib/live_debugger_web/live/traces_live.ex index 80b1c07b7..188e4403d 100644 --- a/lib/live_debugger_web/live/traces_live.ex +++ b/lib/live_debugger_web/live/traces_live.ex @@ -16,7 +16,7 @@ defmodule LiveDebuggerWeb.TracesLive do alias LiveDebuggerWeb.Hooks.TracesLiveView.IncomingTraces alias LiveDebuggerWeb.Hooks.TracesLiveView.TracingFuse - import LiveDebuggerWeb.Live.TracesLive.Helpers + import LiveDebuggerWeb.Helpers.TracesLiveViewHelper @page_size 25 @separator %{id: "separator"} From 8a1c2205e9c42692730dc9a5f0b22851d9e533d1 Mon Sep 17 00:00:00 2001 From: kraleppa Date: Fri, 30 May 2025 10:07:42 +0200 Subject: [PATCH 25/29] Renamed helpers and hooks --- .../{traces_live_view_helper.ex => traces_live_helper.ex} | 2 +- .../{traces_live_view => traces_live}/existing_traces.ex | 4 ++-- .../{traces_live_view => traces_live}/incoming_traces.ex | 4 ++-- .../{traces_live_view => traces_live}/tracing_fuse.ex | 4 ++-- lib/live_debugger_web/live/traces_live.ex | 8 ++++---- 5 files changed, 11 insertions(+), 11 deletions(-) rename lib/live_debugger_web/helpers/{traces_live_view_helper.ex => traces_live_helper.ex} (95%) rename lib/live_debugger_web/hooks/{traces_live_view => traces_live}/existing_traces.ex (97%) rename lib/live_debugger_web/hooks/{traces_live_view => traces_live}/incoming_traces.ex (95%) rename lib/live_debugger_web/hooks/{traces_live_view => traces_live}/tracing_fuse.ex (97%) diff --git a/lib/live_debugger_web/helpers/traces_live_view_helper.ex b/lib/live_debugger_web/helpers/traces_live_helper.ex similarity index 95% rename from lib/live_debugger_web/helpers/traces_live_view_helper.ex rename to lib/live_debugger_web/helpers/traces_live_helper.ex index d0eeaec01..c429163dd 100644 --- a/lib/live_debugger_web/helpers/traces_live_view_helper.ex +++ b/lib/live_debugger_web/helpers/traces_live_helper.ex @@ -1,4 +1,4 @@ -defmodule LiveDebuggerWeb.Helpers.TracesLiveViewHelper do +defmodule LiveDebuggerWeb.Helpers.TracesLiveHelper do @moduledoc """ This module provides helpers for the TracesLive. """ diff --git a/lib/live_debugger_web/hooks/traces_live_view/existing_traces.ex b/lib/live_debugger_web/hooks/traces_live/existing_traces.ex similarity index 97% rename from lib/live_debugger_web/hooks/traces_live_view/existing_traces.ex rename to lib/live_debugger_web/hooks/traces_live/existing_traces.ex index 6cf885471..9f102970c 100644 --- a/lib/live_debugger_web/hooks/traces_live_view/existing_traces.ex +++ b/lib/live_debugger_web/hooks/traces_live/existing_traces.ex @@ -1,4 +1,4 @@ -defmodule LiveDebuggerWeb.Hooks.TracesLiveView.ExistingTraces do +defmodule LiveDebuggerWeb.Hooks.TracesLive.ExistingTraces do @moduledoc """ This hook is responsible for fetching the existing traces and displaying them in the LiveView. It encapsulates logic for async fetching of traces. @@ -23,7 +23,7 @@ defmodule LiveDebuggerWeb.Hooks.TracesLiveView.ExistingTraces do import Phoenix.LiveView import Phoenix.Component import LiveDebuggerWeb.Helpers - import LiveDebuggerWeb.Helpers.TracesLiveViewHelper + import LiveDebuggerWeb.Helpers.TracesLiveHelper alias LiveDebugger.Services.TraceService alias LiveDebugger.Structs.TraceDisplay diff --git a/lib/live_debugger_web/hooks/traces_live_view/incoming_traces.ex b/lib/live_debugger_web/hooks/traces_live/incoming_traces.ex similarity index 95% rename from lib/live_debugger_web/hooks/traces_live_view/incoming_traces.ex rename to lib/live_debugger_web/hooks/traces_live/incoming_traces.ex index 49993a572..018ac88e6 100644 --- a/lib/live_debugger_web/hooks/traces_live_view/incoming_traces.ex +++ b/lib/live_debugger_web/hooks/traces_live/incoming_traces.ex @@ -1,4 +1,4 @@ -defmodule LiveDebuggerWeb.Hooks.TracesLiveView.IncomingTraces do +defmodule LiveDebuggerWeb.Hooks.TracesLive.IncomingTraces do @moduledoc """ This hook is responsible for handling incoming traces It is responsible for inserting new traces into the `:existing_traces` stream. @@ -19,7 +19,7 @@ defmodule LiveDebuggerWeb.Hooks.TracesLiveView.IncomingTraces do import Phoenix.LiveView import Phoenix.Component import LiveDebuggerWeb.Helpers - import LiveDebuggerWeb.Helpers.TracesLiveViewHelper + import LiveDebuggerWeb.Helpers.TracesLiveHelper alias LiveDebugger.Structs.TraceDisplay diff --git a/lib/live_debugger_web/hooks/traces_live_view/tracing_fuse.ex b/lib/live_debugger_web/hooks/traces_live/tracing_fuse.ex similarity index 97% rename from lib/live_debugger_web/hooks/traces_live_view/tracing_fuse.ex rename to lib/live_debugger_web/hooks/traces_live/tracing_fuse.ex index d449e0f6b..bd125b580 100644 --- a/lib/live_debugger_web/hooks/traces_live_view/tracing_fuse.ex +++ b/lib/live_debugger_web/hooks/traces_live/tracing_fuse.ex @@ -1,4 +1,4 @@ -defmodule LiveDebuggerWeb.Hooks.TracesLiveView.TracingFuse do +defmodule LiveDebuggerWeb.Hooks.TracesLive.TracingFuse do @moduledoc """ This hook is responsible for managing the tracing fuse. It is responsible for determining if the tracing should be stopped. @@ -18,7 +18,7 @@ defmodule LiveDebuggerWeb.Hooks.TracesLiveView.TracingFuse do import Phoenix.Component, only: [assign: 3] import LiveDebuggerWeb.Helpers import Phoenix.LiveView - import LiveDebuggerWeb.Helpers.TracesLiveViewHelper + import LiveDebuggerWeb.Helpers.TracesLiveHelper alias Phoenix.LiveView.Socket alias LiveDebugger.Utils.PubSub, as: PubSubUtils diff --git a/lib/live_debugger_web/live/traces_live.ex b/lib/live_debugger_web/live/traces_live.ex index 188e4403d..dffdee161 100644 --- a/lib/live_debugger_web/live/traces_live.ex +++ b/lib/live_debugger_web/live/traces_live.ex @@ -12,11 +12,11 @@ defmodule LiveDebuggerWeb.TracesLive do alias LiveDebugger.Utils.PubSub, as: PubSubUtils alias LiveDebuggerWeb.Components.Traces - alias LiveDebuggerWeb.Hooks.TracesLiveView.ExistingTraces - alias LiveDebuggerWeb.Hooks.TracesLiveView.IncomingTraces - alias LiveDebuggerWeb.Hooks.TracesLiveView.TracingFuse + alias LiveDebuggerWeb.Hooks.TracesLive.ExistingTraces + alias LiveDebuggerWeb.Hooks.TracesLive.IncomingTraces + alias LiveDebuggerWeb.Hooks.TracesLive.TracingFuse - import LiveDebuggerWeb.Helpers.TracesLiveViewHelper + import LiveDebuggerWeb.Helpers.TracesLiveHelper @page_size 25 @separator %{id: "separator"} From 707e78323168f7033960b5b3f7d6ca2add12a2f6 Mon Sep 17 00:00:00 2001 From: kraleppa Date: Fri, 30 May 2025 10:18:13 +0200 Subject: [PATCH 26/29] Added error handling for pagination --- lib/live_debugger_web/components/traces.ex | 58 +++++++++++++------ .../hooks/traces_live/existing_traces.ex | 2 +- 2 files changed, 40 insertions(+), 20 deletions(-) diff --git a/lib/live_debugger_web/components/traces.ex b/lib/live_debugger_web/components/traces.ex index b12b16fca..fcfd74477 100644 --- a/lib/live_debugger_web/components/traces.ex +++ b/lib/live_debugger_web/components/traces.ex @@ -191,37 +191,57 @@ defmodule LiveDebuggerWeb.Components.Traces do """ end + def get_threshold_class(execution_time) do + cond do + execution_time == nil -> "" + execution_time > 500_000 -> "text-error-text" + execution_time > 100_000 -> "text-warning-text" + true -> "" + end + end + attr(:traces_continuation, :any, required: true) attr(:tracing_helper, :any, required: true) - def load_more_button(%{traces_continuation: nil} = assigns), do: ~H"" - def load_more_button(%{traces_continuation: :end_of_table} = assigns), do: ~H"" - def load_more_button(%{tracing_helper: %{tracing_started?: true}} = assigns), do: ~H"" - - def load_more_button(%{traces_continuation: :loading} = assigns) do + def load_more_button(assigns) do ~H"""
- <.spinner size="sm" class="mb-4" /> + <.load_more_button_content + traces_continuation={@traces_continuation} + tracing_helper={@tracing_helper} + />
""" end - def load_more_button(assigns) do + defp load_more_button_content(%{traces_continuation: nil} = assigns), do: ~H"" + defp load_more_button_content(%{traces_continuation: :end_of_table} = assigns), do: ~H"" + defp load_more_button_content(%{tracing_helper: %{tracing_started?: true}} = assigns), do: ~H"" + + defp load_more_button_content(%{traces_continuation: :loading} = assigns) do ~H""" -
- <.button phx-click="load-more" class="w-4 mb-4" variant="secondary"> - Load more - -
+ <.spinner size="sm" class="mb-4" /> """ end - def get_threshold_class(execution_time) do - cond do - execution_time == nil -> "" - execution_time > 500_000 -> "text-error-text" - execution_time > 100_000 -> "text-warning-text" - true -> "" - end + defp load_more_button_content(%{traces_continuation: :error} = assigns) do + ~H""" + <.alert + variant="danger" + with_icon={true} + heading="Error while loading more traces" + class="w-full mb-4" + > + Check logs for more details. + + """ + end + + defp load_more_button_content(%{traces_continuation: cont} = assigns) when is_tuple(cont) do + ~H""" + <.button phx-click="load-more" class="w-4 mb-4" variant="secondary"> + Load more + + """ end end diff --git a/lib/live_debugger_web/hooks/traces_live/existing_traces.ex b/lib/live_debugger_web/hooks/traces_live/existing_traces.ex index 9f102970c..7f36a6dbb 100644 --- a/lib/live_debugger_web/hooks/traces_live/existing_traces.ex +++ b/lib/live_debugger_web/hooks/traces_live/existing_traces.ex @@ -131,11 +131,11 @@ defmodule LiveDebuggerWeb.Hooks.TracesLive.ExistingTraces do |> halt() end - # TODO: handle this case in a proper way defp handle_async(:load_more_existing_traces, {:exit, reason}, socket) do log_async_error("loading more existing traces", reason) socket + |> assign(:traces_continuation, :error) |> halt() end From bd05de7c5cd80bbac8d9c2a837b6e1609595fc89 Mon Sep 17 00:00:00 2001 From: kraleppa Date: Fri, 30 May 2025 10:25:33 +0200 Subject: [PATCH 27/29] Added comment to traces_live_helper --- lib/live_debugger_web/helpers/traces_live_helper.ex | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/live_debugger_web/helpers/traces_live_helper.ex b/lib/live_debugger_web/helpers/traces_live_helper.ex index c429163dd..757239b5a 100644 --- a/lib/live_debugger_web/helpers/traces_live_helper.ex +++ b/lib/live_debugger_web/helpers/traces_live_helper.ex @@ -1,6 +1,10 @@ defmodule LiveDebuggerWeb.Helpers.TracesLiveHelper do @moduledoc """ - This module provides helpers for the TracesLive. + This module provides helpers for the TracesLive and especially its hooks. + Since these hooks get more complex and they touch different assigns and streams, + this module was created to check if the assigns and streams are present in the socket. + + This way we can catch errors early and not have to debug them in the hooks. """ alias LiveDebugger.Utils.Callbacks, as: UtilsCallbacks From 5b4f5800d53ffc00c993e192ae107d2dae53a375 Mon Sep 17 00:00:00 2001 From: kraleppa Date: Fri, 30 May 2025 10:29:30 +0200 Subject: [PATCH 28/29] Extracted duplicated functions --- .../helpers/traces_live_helper.ex | 18 ++++++++++++++++++ .../hooks/traces_live/existing_traces.ex | 12 ------------ .../hooks/traces_live/incoming_traces.ex | 6 ------ 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/lib/live_debugger_web/helpers/traces_live_helper.ex b/lib/live_debugger_web/helpers/traces_live_helper.ex index 757239b5a..ca98a4db3 100644 --- a/lib/live_debugger_web/helpers/traces_live_helper.ex +++ b/lib/live_debugger_web/helpers/traces_live_helper.ex @@ -55,4 +55,22 @@ defmodule LiveDebuggerWeb.Helpers.TracesLiveHelper do ] } end + + @doc """ + Returns the execution times for the traces. + """ + def get_execution_times(socket) do + socket.assigns.current_filters.execution_time + |> Enum.filter(fn {_, value} -> value != "" end) + |> Enum.map(fn {filter, value} -> {filter, String.to_integer(value)} end) + end + + @doc """ + Returns the active functions for the traces. + """ + def get_active_functions(socket) do + socket.assigns.current_filters.functions + |> Enum.filter(fn {_, active?} -> active? end) + |> Enum.map(fn {function, _} -> function end) + end end diff --git a/lib/live_debugger_web/hooks/traces_live/existing_traces.ex b/lib/live_debugger_web/hooks/traces_live/existing_traces.ex index 7f36a6dbb..a092a4afe 100644 --- a/lib/live_debugger_web/hooks/traces_live/existing_traces.ex +++ b/lib/live_debugger_web/hooks/traces_live/existing_traces.ex @@ -143,18 +143,6 @@ defmodule LiveDebuggerWeb.Hooks.TracesLive.ExistingTraces do {:cont, socket} end - defp get_active_functions(socket) do - socket.assigns.current_filters.functions - |> Enum.filter(fn {_, active?} -> active? end) - |> Enum.map(fn {function, _} -> function end) - end - - defp get_execution_times(socket) do - socket.assigns.current_filters.execution_time - |> Enum.filter(fn {_, value} -> value != "" end) - |> Enum.map(fn {filter, value} -> {filter, String.to_integer(value)} end) - end - defp log_async_error(operation, reason) do Logger.error( "LiveDebugger encountered unexpected error while #{operation}: #{inspect(reason)}" diff --git a/lib/live_debugger_web/hooks/traces_live/incoming_traces.ex b/lib/live_debugger_web/hooks/traces_live/incoming_traces.ex index 018ac88e6..f7421c663 100644 --- a/lib/live_debugger_web/hooks/traces_live/incoming_traces.ex +++ b/lib/live_debugger_web/hooks/traces_live/incoming_traces.ex @@ -70,10 +70,4 @@ defmodule LiveDebuggerWeb.Hooks.TracesLive.IncomingTraces do defp handle_info(_, socket) do {:cont, socket} end - - defp get_execution_times(socket) do - socket.assigns.current_filters.execution_time - |> Enum.filter(fn {_, value} -> value != "" end) - |> Enum.map(fn {filter, value} -> {filter, String.to_integer(value)} end) - end end From c97668ef47f5e00444793c45678d3e5a3d15747b Mon Sep 17 00:00:00 2001 From: kraleppa Date: Fri, 30 May 2025 12:51:26 +0200 Subject: [PATCH 29/29] Deleted obsolete tooltip classes --- assets/css/app.css | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/assets/css/app.css b/assets/css/app.css index a4ce56676..b866a6eba 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -47,19 +47,3 @@ body::-webkit-scrollbar { ::-webkit-scrollbar-corner { background: transparent; } - -.tooltip-primary { - background-color: var(--button-primary-bg); - color: var(--button-primary-content); - padding: 0.5rem; - border-radius: 0.25rem; - font-weight: 600; -} - -.tooltip-secondary { - background-color: var(--button-secondary-bg); - color: var(--button-secondary-content); - padding: 0.5rem; - border-radius: 0.25rem; - font-weight: 600; -}