Skip to content

Commit 32f13b1

Browse files
committed
Aviod deadlock when meta pid is down
1 parent 87e5c6c commit 32f13b1

File tree

1 file changed

+68
-42
lines changed

1 file changed

+68
-42
lines changed

apps/debug_adapter/lib/debug_adapter/stacktrace.ex

Lines changed: 68 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -36,52 +36,78 @@ defmodule ElixirLS.DebugAdapter.Stacktrace do
3636
def get(pid) do
3737
case :dbg_iserver.safe_call({:get_meta, pid}) do
3838
{:ok, meta_pid} ->
39-
[{level, {module, function, args}} | backtrace_rest] =
40-
:int.meta(meta_pid, :backtrace, :all)
41-
42-
messages = :int.meta(meta_pid, :messages)
43-
44-
first_frame = %Frame{
45-
level: level,
46-
module: module,
47-
function: {function, get_arity(args)},
48-
args: args,
49-
file: get_file(module),
50-
# vscode raises invalid request when line is nil
51-
line: break_line(pid) || 1,
52-
bindings: get_bindings(meta_pid, level),
53-
messages: messages
54-
}
55-
56-
# If backtrace_rest is empty, calling stack_frames causes an exception
57-
other_frames =
58-
case backtrace_rest do
59-
[] ->
60-
[]
61-
62-
_ ->
63-
frames = List.zip([backtrace_rest, stack_frames(meta_pid, level)])
64-
65-
for {{level, {mod, function, args}}, {level, {mod, line}, bindings}} <- frames do
66-
%Frame{
67-
level: level,
68-
module: mod,
69-
function: {function, get_arity(args)},
70-
args: args,
71-
file: get_file(mod),
72-
# vscode raises invalid request when line is nil
73-
line: line || 1,
74-
bindings: Enum.into(bindings, %{}),
75-
messages: messages
76-
}
39+
parent = self()
40+
ref = Process.monitor(meta_pid)
41+
42+
meta_query_pid =
43+
spawn(fn ->
44+
[{level, {module, function, args}} | backtrace_rest] =
45+
:int.meta(meta_pid, :backtrace, :all)
46+
47+
messages = :int.meta(meta_pid, :messages)
48+
49+
first_frame = %Frame{
50+
level: level,
51+
module: module,
52+
function: {function, get_arity(args)},
53+
args: args,
54+
file: get_file(module),
55+
# vscode raises invalid request when line is nil
56+
line: break_line(pid) || 1,
57+
bindings: get_bindings(meta_pid, level),
58+
messages: messages
59+
}
60+
61+
# If backtrace_rest is empty, calling stack_frames causes an exception
62+
other_frames =
63+
case backtrace_rest do
64+
[] ->
65+
[]
66+
67+
_ ->
68+
frames = List.zip([backtrace_rest, stack_frames(meta_pid, level)])
69+
70+
for {{level, {mod, function, args}}, {level, {mod, line}, bindings}} <- frames do
71+
%Frame{
72+
level: level,
73+
module: mod,
74+
function: {function, get_arity(args)},
75+
args: args,
76+
file: get_file(mod),
77+
# vscode raises invalid request when line is nil
78+
line: line || 1,
79+
bindings: Enum.into(bindings, %{}),
80+
messages: messages
81+
}
82+
end
7783
end
78-
end
7984

80-
[first_frame | other_frames]
85+
send(parent, {:ok, [first_frame | other_frames]})
86+
end)
87+
88+
receive do
89+
{:ok, trace} ->
90+
trace
91+
92+
{:DOWN, _, :process, ^meta_pid, reason} ->
93+
Process.exit(meta_query_pid, :kill)
94+
95+
Output.debugger_console(
96+
"Meta process down fo pid #{inspect(pid)}: #{inspect(reason)}\n"
97+
)
98+
99+
[]
100+
after
101+
5000 ->
102+
Process.exit(meta_query_pid, :kill)
103+
Process.demonitor(ref, false)
104+
Output.debugger_console("Timed out while obtaining meta for pid #{inspect(pid)}\n")
105+
[]
106+
end
81107

82108
error ->
83-
Output.debugger_important(
84-
"Failed to obtain meta for pid #{inspect(pid)}: #{inspect(error)}"
109+
Output.debugger_console(
110+
"Failed to obtain meta for pid #{inspect(pid)}: #{inspect(error)}\n"
85111
)
86112

87113
[]

0 commit comments

Comments
 (0)