From 32f13b1a11826f648c181391ed1efd454135f1c7 Mon Sep 17 00:00:00 2001 From: Lukasz Samson Date: Thu, 11 Jul 2024 21:38:36 +0200 Subject: [PATCH] Aviod deadlock when meta pid is down --- .../lib/debug_adapter/stacktrace.ex | 110 +++++++++++------- 1 file changed, 68 insertions(+), 42 deletions(-) diff --git a/apps/debug_adapter/lib/debug_adapter/stacktrace.ex b/apps/debug_adapter/lib/debug_adapter/stacktrace.ex index 1dc89c960..c63ce07d0 100644 --- a/apps/debug_adapter/lib/debug_adapter/stacktrace.ex +++ b/apps/debug_adapter/lib/debug_adapter/stacktrace.ex @@ -36,52 +36,78 @@ defmodule ElixirLS.DebugAdapter.Stacktrace do def get(pid) do case :dbg_iserver.safe_call({:get_meta, pid}) do {:ok, meta_pid} -> - [{level, {module, function, args}} | backtrace_rest] = - :int.meta(meta_pid, :backtrace, :all) - - messages = :int.meta(meta_pid, :messages) - - first_frame = %Frame{ - level: level, - module: module, - function: {function, get_arity(args)}, - args: args, - file: get_file(module), - # vscode raises invalid request when line is nil - line: break_line(pid) || 1, - bindings: get_bindings(meta_pid, level), - messages: messages - } - - # If backtrace_rest is empty, calling stack_frames causes an exception - other_frames = - case backtrace_rest do - [] -> - [] - - _ -> - frames = List.zip([backtrace_rest, stack_frames(meta_pid, level)]) - - for {{level, {mod, function, args}}, {level, {mod, line}, bindings}} <- frames do - %Frame{ - level: level, - module: mod, - function: {function, get_arity(args)}, - args: args, - file: get_file(mod), - # vscode raises invalid request when line is nil - line: line || 1, - bindings: Enum.into(bindings, %{}), - messages: messages - } + parent = self() + ref = Process.monitor(meta_pid) + + meta_query_pid = + spawn(fn -> + [{level, {module, function, args}} | backtrace_rest] = + :int.meta(meta_pid, :backtrace, :all) + + messages = :int.meta(meta_pid, :messages) + + first_frame = %Frame{ + level: level, + module: module, + function: {function, get_arity(args)}, + args: args, + file: get_file(module), + # vscode raises invalid request when line is nil + line: break_line(pid) || 1, + bindings: get_bindings(meta_pid, level), + messages: messages + } + + # If backtrace_rest is empty, calling stack_frames causes an exception + other_frames = + case backtrace_rest do + [] -> + [] + + _ -> + frames = List.zip([backtrace_rest, stack_frames(meta_pid, level)]) + + for {{level, {mod, function, args}}, {level, {mod, line}, bindings}} <- frames do + %Frame{ + level: level, + module: mod, + function: {function, get_arity(args)}, + args: args, + file: get_file(mod), + # vscode raises invalid request when line is nil + line: line || 1, + bindings: Enum.into(bindings, %{}), + messages: messages + } + end end - end - [first_frame | other_frames] + send(parent, {:ok, [first_frame | other_frames]}) + end) + + receive do + {:ok, trace} -> + trace + + {:DOWN, _, :process, ^meta_pid, reason} -> + Process.exit(meta_query_pid, :kill) + + Output.debugger_console( + "Meta process down fo pid #{inspect(pid)}: #{inspect(reason)}\n" + ) + + [] + after + 5000 -> + Process.exit(meta_query_pid, :kill) + Process.demonitor(ref, false) + Output.debugger_console("Timed out while obtaining meta for pid #{inspect(pid)}\n") + [] + end error -> - Output.debugger_important( - "Failed to obtain meta for pid #{inspect(pid)}: #{inspect(error)}" + Output.debugger_console( + "Failed to obtain meta for pid #{inspect(pid)}: #{inspect(error)}\n" ) []