From 37c70198c4acf54625a13faeb87390bb97ba9d24 Mon Sep 17 00:00:00 2001 From: Lukasz Samson Date: Tue, 4 Jun 2024 10:18:13 +0200 Subject: [PATCH] return OTP 27 process labels in debug adapter threads response --- .../debug_adapter/lib/debug_adapter/server.ex | 26 +++++-- apps/debug_adapter/test/debugger_test.exs | 72 +++++++++++++++++++ 2 files changed, 92 insertions(+), 6 deletions(-) diff --git a/apps/debug_adapter/lib/debug_adapter/server.ex b/apps/debug_adapter/lib/debug_adapter/server.ex index 41077f5b2..a0d80d83a 100644 --- a/apps/debug_adapter/lib/debug_adapter/server.ex +++ b/apps/debug_adapter/lib/debug_adapter/server.ex @@ -2278,13 +2278,27 @@ defmodule ElixirLS.DebugAdapter.Server do end defp process_name(process_info) do - registered_name = Keyword.get(process_info, :registered_name) + # if present registered_name is na atom + registered_name = Keyword.fetch(process_info, :registered_name) + # OTP 27+ process label may be any term + # it's not documented but :proc_lib.get_label reads `:$process_label` key from process dictionary + # https://github.com/erlang/otp/blob/601a012837ea0a5c8095bf24223132824177124d/lib/stdlib/src/proc_lib.erl#L876 + # we have already read it so can get it directly + label = process_info |> Keyword.fetch!(:dictionary) |> Keyword.fetch(:"$process_label") - if registered_name do - inspect(registered_name) - else - {mod, func, arity} = Keyword.fetch!(process_info, :initial_call) - "#{inspect(mod)}.#{to_string(func)}/#{arity}" + case {registered_name, label} do + {{:ok, registered_name}, {:ok, label}} -> + "#{registered_name}: #{inspect(label)}" + + {{:ok, registered_name}, _} -> + to_string(registered_name) + + {_, {:ok, label}} -> + inspect(label) + + _ -> + {mod, func, arity} = Keyword.fetch!(process_info, :initial_call) + "#{inspect(mod)}.#{to_string(func)}/#{arity}" end end diff --git a/apps/debug_adapter/test/debugger_test.exs b/apps/debug_adapter/test/debugger_test.exs index 66e05c172..24692cc74 100644 --- a/apps/debug_adapter/test/debugger_test.exs +++ b/apps/debug_adapter/test/debugger_test.exs @@ -3303,6 +3303,78 @@ defmodule ElixirLS.DebugAdapter.ServerTest do end) end + if System.otp_release() |> String.to_integer() >= 27 do + @tag :fixture + test "returns process label", %{server: server} do + in_fixture(__DIR__, "mix_project", fn -> + Server.receive_packet(server, initialize_req(1, %{})) + + assert_receive( + response(_, 1, "initialize", %{"supportsConfigurationDoneRequest" => true}) + ) + + Server.receive_packet( + server, + launch_req(2, %{ + "request" => "launch", + "type" => "mix_task", + "task" => "run", + "taskArgs" => ["-e", "MixProject.Some.sleep()"], + "projectDir" => File.cwd!() + }) + ) + + assert_receive(response(_, 2, "launch", %{}), 5000) + assert_receive(event(_, "initialized", %{})) + + Server.receive_packet(server, request(5, "configurationDone", %{})) + assert_receive(response(_, 5, "configurationDone", %{})) + Process.sleep(1000) + + {:ok, pid} = + Task.start(fn -> + :proc_lib.set_label("foo") + + receive do + :done -> :ok + end + end) + + Process.monitor(pid) + + send(server, :update_threads) + state = :sys.get_state(server) + + thread_id = state.pids_to_thread_ids[pid] + assert thread_id + assert state.thread_ids_to_pids[thread_id] == pid + + Server.receive_packet(server, request(6, "threads", %{})) + assert_receive(response(_, 6, "threads", %{"threads" => threads}), 1_000) + + assert Enum.find(threads, &(&1["id"] == thread_id))["name"] == + "\"foo\" #{:erlang.pid_to_list(pid)}" + + send(pid, :done) + + receive do + {:DOWN, _, _, ^pid, _} -> :ok + end + + send(server, :update_threads) + state = :sys.get_state(server) + + refute Map.has_key?(state.pids_to_thread_ids, pid) + refute Map.has_key?(state.thread_ids_to_pids, thread_id) + + Server.receive_packet(server, request(6, "threads", %{})) + assert_receive(response(_, 6, "threads", %{"threads" => threads}), 1_000) + + refute Enum.find(threads, &(&1["id"] == thread_id)) + end) + end + end + describe "evaluate" do defp gen_watch_expression_packet(seq, expr) do %{