From 800b8ee5acc3bf3b63b262551df7b6dbea4886ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20L=C3=B6scher?= Date: Wed, 17 Feb 2021 18:51:25 +0000 Subject: [PATCH] add timeout config parameter and streamline projectnode startup --- apps/els_core/src/els_distribution_server.erl | 36 +++++---- apps/els_dap/src/els_dap_general_provider.erl | 80 ++++++++++++------- 2 files changed, 72 insertions(+), 44 deletions(-) diff --git a/apps/els_core/src/els_distribution_server.erl b/apps/els_core/src/els_distribution_server.erl index 2ad857bcb..91c3fc924 100644 --- a/apps/els_core/src/els_distribution_server.erl +++ b/apps/els_core/src/els_distribution_server.erl @@ -134,19 +134,13 @@ handle_info(Request, State) -> %%============================================================================== -spec connect_and_monitor(atom()) -> ok | error. connect_and_monitor(Node) -> - case lists:member(Node, erlang:nodes(connected)) of + case net_kernel:connect_node(Node) of true -> - ?LOG_DEBUG("Already connected to node [node=~p]", [Node]), + ?LOG_INFO("Connected to node [node=~p]", [Node]), + erlang:monitor_node(Node, true), ok; false -> - case net_kernel:connect_node(Node) of - true -> - ?LOG_INFO("Connected to node [node=~p]", [Node]), - erlang:monitor_node(Node, true), - ok; - false -> - error - end + error end. -spec start(atom()) -> ok. @@ -166,19 +160,27 @@ wait_connect_and_monitor(Node) -> wait_connect_and_monitor(Node, ?WAIT_ATTEMPTS). -spec wait_connect_and_monitor(Node :: atom(), Attempts :: pos_integer()) -> ok. -wait_connect_and_monitor(Node, 0) -> - ?LOG_ERROR( "Failed to connect to node ~p after ~p attempts" - , [Node, ?WAIT_ATTEMPTS]), - ok; wait_connect_and_monitor(Node, Attempts) -> + wait_connect_and_monitor(Node, Attempts, Attempts). + +-spec wait_connect_and_monitor( + Node :: atom(), + Attempts :: pos_integer(), + MaxAttempts :: pos_integer() +) -> ok. +wait_connect_and_monitor(Node, 0, MaxAttempts) -> + ?LOG_ERROR( "Failed to connect to node ~p after ~p attempts" + , [Node, MaxAttempts]), + error; +wait_connect_and_monitor(Node, Attempts, MaxAttempts) -> timer:sleep(?WAIT_INTERVAL), case connect_and_monitor(Node) of ok -> ok; error -> - ?LOG_DEBUG( "Trying to connect to node ~p (~p/~p)" - , [Node, ?WAIT_ATTEMPTS - Attempts + 1, ?WAIT_ATTEMPTS]), - wait_connect_and_monitor(Node, Attempts - 1) + ?LOG_WARNING( "Trying to connect to node ~p (~p/~p)" + , [Node, MaxAttempts - Attempts + 1, MaxAttempts]), + wait_connect_and_monitor(Node, Attempts - 1, MaxAttempts) end. %% @doc Ensure the Erlang Port Mapper Daemon (EPMD) is up and running diff --git a/apps/els_dap/src/els_dap_general_provider.erl b/apps/els_dap/src/els_dap_general_provider.erl index f2f75d0d5..f7219af5e 100644 --- a/apps/els_dap/src/els_dap_general_provider.erl +++ b/apps/els_dap/src/els_dap_general_provider.erl @@ -52,6 +52,7 @@ , scope_bindings => #{pos_integer() => {binding_type(), bindings()}} , breakpoints := breakpoints() + , timeout := timeout() }. -type bindings() :: [{varname(), term()}]. -type varname() :: atom() | string(). @@ -72,7 +73,8 @@ init() -> #{ threads => #{} , launch_params => #{} , scope_bindings => #{} - , breakpoints => #{}}. + , breakpoints => #{} + , timeout => 30}. -spec handle_request(request(), state()) -> {result(), state()}. handle_request({<<"initialize">>, _Params}, State) -> @@ -92,17 +94,20 @@ handle_request({<<"launch">>, Params}, State) -> els_distribution_server:start_distribution(LocalNode), ?LOG_INFO("Distribution up on: [~p]", [LocalNode]), - %% get configuration for project node and cookie - ProjectNode = - case Params of - #{ <<"projectnode">> := ConfNode } -> binary_to_atom(ConfNode, utf8); - _ -> els_distribution_server:node_name(<<"erlang_ls_dap_project">>, Name) - end, - Cookie = - case Params of - #{ <<"cookie">> := ConfCookie } -> binary_to_atom(ConfCookie, utf8); - _ -> erlang:get_cookie() - end, + %% get default and final launch config + DefaultConfig = #{ + <<"projectnode">> => + atom_to_binary(els_distribution_server:node_name(<<"erlang_ls_dap_project">>, Name)), + <<"cookie">> => atom_to_binary(erlang:get_cookie()), + <<"timeout">> => 30 + }, + #{ <<"projectnode">> := ConfProjectNode + , <<"cookie">> := ConfCookie + , <<"timeout">> := TimeOut} = maps:merge(DefaultConfig, Params), + ProjectNode = binary_to_atom(ConfProjectNode), + Cookie = binary_to_atom(ConfCookie), + + %% set cookie true = erlang:set_cookie(LocalNode, Cookie), @@ -135,15 +140,13 @@ handle_request({<<"launch">>, Params}, State) -> els_dap_server:send_event(<<"initialized">>, #{}), - {#{}, State#{project_node => ProjectNode, launch_params => Params}}; + {#{}, State#{project_node => ProjectNode, launch_params => Params, timeout => TimeOut}}; handle_request( {<<"configurationDone">>, _Params} , #{ project_node := ProjectNode - , launch_params := LaunchParams} = State + , launch_params := LaunchParams + , timeout := Timeout} = State ) -> - els_distribution_server:wait_connect_and_monitor(ProjectNode), - - inject_dap_agent(ProjectNode), - + ensure_connected(ProjectNode, Timeout), %% TODO: Fetch stack_trace mode from Launch Config els_dap_rpc:stack_trace(ProjectNode, all), MFA = {els_dap_agent, int_cb, [self()]}, @@ -164,13 +167,14 @@ handle_request( {<<"configurationDone">>, _Params} {#{}, State}; handle_request( {<<"setBreakpoints">>, Params} , #{ project_node := ProjectNode - , breakpoints := Breakpoints0} = State + , breakpoints := Breakpoints0 + , timeout := Timeout} = State ) -> + ensure_connected(ProjectNode, Timeout), #{<<"source">> := #{<<"path">> := Path}} = Params, SourceBreakpoints = maps:get(<<"breakpoints">>, Params, []), _SourceModified = maps:get(<<"sourceModified">>, Params, false), Module = els_uri:module(els_uri:uri(Path)), - els_distribution_server:wait_connect_and_monitor(ProjectNode), {module, Module} = els_dap_rpc:i(ProjectNode, Module), Lines = [Line || #{<<"line">> := Line} <- SourceBreakpoints], @@ -191,11 +195,11 @@ handle_request({<<"setExceptionBreakpoints">>, _Params}, State) -> {#{}, State}; handle_request({<<"setFunctionBreakpoints">>, Params} , #{ project_node := ProjectNode - , breakpoints := Breakpoints0} = State + , breakpoints := Breakpoints0 + , timeout := Timeout} = State ) -> + ensure_connected(ProjectNode, Timeout), FunctionBreakPoints = maps:get(<<"breakpoints">>, Params, []), - els_distribution_server:wait_connect_and_monitor(ProjectNode), - MFAs = [ begin Spec = {Mod, _, _} = parse_mfa(MFA), @@ -406,10 +410,7 @@ handle_info( {int_cb, ThreadPid} handle_info({nodedown, Node}, State) -> %% the project node is down, there is nothing left to do then to exit ?LOG_NOTICE("project node ~p terminated, ending debug session", [Node]), - els_dap_server:send_event(<<"terminated">>, #{}), - els_dap_server:send_event(<<"exited">>, #{ <<"exitCode">> => <<"0">>}), - ?LOG_NOTICE("terminating debug adapter"), - els_utils:halt(0), + stop_debugger(), State. %%============================================================================== @@ -711,3 +712,28 @@ do_function_breaks(Node, Module, FBreaks, Breaks) -> #{Module := ModBreaks} -> Breaks#{ Module => ModBreaks#{function => FBreaks}}; _ -> Breaks#{ Module => #{line => [], function => FBreaks}} end. + +-spec ensure_connected(node(), timeout()) -> ok. +ensure_connected(Node, Timeout) -> + case is_node_up(Node) of + true -> ok; + false -> + % connect and monitore project node + els_distribution_server:wait_connect_and_monitor(Node, Timeout), + case is_node_up(Node) of + true -> inject_dap_agent(Node); + false -> stop_debugger() + end + end. + +-spec stop_debugger() -> no_return(). +stop_debugger() -> + %% the project node is down, there is nothing left to do then to exit + els_dap_server:send_event(<<"terminated">>, #{}), + els_dap_server:send_event(<<"exited">>, #{ <<"exitCode">> => <<"0">>}), + ?LOG_NOTICE("terminating debug adapter"), + els_utils:halt(0). + +-spec is_node_up(node()) -> boolean(). +is_node_up(Node) -> + lists:member(Node, erlang:nodes(connected)).