Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions apps/edb/src/edb_app.erl
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,15 @@ edb public API

-spec start(application:start_type(), term()) -> {ok, pid()}.
start(_StartType, _StartArgs) ->
{ok, _Sup} = edb_sup:start_link().
Options =
case application:get_env(edb, dap_language) of
{ok, DapLanguage} ->
#{dap_language => DapLanguage};
undefined ->
#{}
end,
{ok, _Sup} = edb_sup:start_link(Options).

-spec stop(term()) -> ok.
stop(_State) ->
ok.

%% internal functions
6 changes: 4 additions & 2 deletions apps/edb/src/edb_dap_internal_events.erl
Original file line number Diff line number Diff line change
Expand Up @@ -128,10 +128,11 @@ paused_impl(#{state := S}, Event) ->
Result :: edb:reverse_attachment_event(),
State :: edb_dap_server:state(),
Reaction :: reaction().
reverse_attach_impl({attached, Node}, State0 = #{state := launching}) ->
reverse_attach_impl({attached, Node}, State0 = #{state := launching, dap_language := DapLanguage}) ->
State1 = maps:without([shell_process_id], State0),

ProcessId = list_to_integer(erpc:call(Node, os, getpid, [])),
DapLanguageState = DapLanguage:init(),

AttachType0 = maps:with([shell_process_id], State0),
AttachType1 = AttachType0#{request => launch, process_id => ProcessId},
Expand All @@ -140,7 +141,8 @@ reverse_attach_impl({attached, Node}, State0 = #{state := launching}) ->
new_state => State1#{
state => configuring,
type => AttachType1,
node => Node
node => Node,
dap_language_state => DapLanguageState
}
};
reverse_attach_impl({error, Node, {bootstrap_failed, BootstrapFailure}}, #{state := launching}) ->
Expand Down
34 changes: 34 additions & 0 deletions apps/edb/src/edb_dap_language.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
%% Copyright (c) Meta Platforms, Inc. and affiliates.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.

%%% % @format

-module(edb_dap_language).

-oncall("whatsapp_server_devx").
-moduledoc """
Language-specific hooks for the DAP adapter.
""".
-compile(warn_missing_spec_all).

-export_type([state/0]).

-type state() :: dynamic().

-callback init() -> dynamic().
-callback source_to_modules(Path, Lines, State) -> {Modules, State} when
Path :: binary(),
Lines :: [edb:line()],
Modules :: [module()],
State :: dynamic().
40 changes: 40 additions & 0 deletions apps/edb/src/edb_dap_language_erlang.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
%% Copyright (c) Meta Platforms, Inc. and affiliates.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.

%%% % @format

-module(edb_dap_language_erlang).

-oncall("whatsapp_server_devx").
-moduledoc """
Default Erlang language hooks.
""".
-compile(warn_missing_spec_all).

-behaviour(edb_dap_language).

-export([init/0, source_to_modules/3]).

-spec init() -> #{}.
init() ->
#{}.

-spec source_to_modules(Path, Lines, State) -> {[module()], State} when
Path :: binary(),
Lines :: [edb:line()],
State :: #{}.
source_to_modules(Path, _Lines, State) ->
Extension = filename:extension(Path),
ModuleName = filename:basename(Path, Extension),
{[binary_to_atom(unicode:characters_to_binary(ModuleName))], State}.
4 changes: 3 additions & 1 deletion apps/edb/src/edb_dap_request_attach.erl
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ parse_arguments(Args) ->
-spec handle(State, Args) -> edb_dap_request:reaction() when
State :: edb_dap_server:state(),
Args :: config().
handle(State0 = #{state := initialized}, Args) ->
handle(State0 = #{state := initialized, dap_language := DapLanguage}, Args) ->
#{config := Config} = Args,
AttachArgs = maps:without([cwd, stripSourcePrefix], Config),
case edb:attach(AttachArgs) of
Expand All @@ -83,6 +83,7 @@ handle(State0 = #{state := initialized}, Args) ->
Node = maps:get(node, Config),
ProcessId = list_to_integer(erpc:call(Node, os, getpid, [])),
{ok, Subscription} = edb:subscribe(),
DapLanguageState = DapLanguage:init(),
State1 = State0#{
state => configuring,
type => #{
Expand All @@ -93,6 +94,7 @@ handle(State0 = #{state := initialized}, Args) ->
node => Node,
reverse_attach_ref => undefined,
cwd => edb_dap_utils:strip_suffix(Cwd, StripSourcePrefix),
dap_language_state => DapLanguageState,
subscription => Subscription
},
#{
Expand Down
4 changes: 2 additions & 2 deletions apps/edb/src/edb_dap_request_initialize.erl
Original file line number Diff line number Diff line change
Expand Up @@ -183,10 +183,10 @@ parse_arguments(Args) ->
-spec handle(State, Args) -> edb_dap_request:reaction(capabilities()) when
State :: edb_dap_server:state(),
Args :: arguments().
handle(#{state := started}, ClientInfo) ->
handle(State0 = #{state := started}, ClientInfo) ->
#{
response => edb_dap_request:success(capabilities()),
new_state => #{state => initialized, client_info => ClientInfo}
new_state => State0#{state => initialized, client_info => ClientInfo}
};
handle(_InvalidState, _Args) ->
edb_dap_request:unexpected_request().
Expand Down
54 changes: 44 additions & 10 deletions apps/edb/src/edb_dap_request_set_breakpoints.erl
Original file line number Diff line number Diff line change
Expand Up @@ -200,25 +200,27 @@ parse_arguments(Args) ->
-spec handle(State, Args) -> edb_dap_request:reaction(response()) when
State :: edb_dap_server:state(),
Args :: arguments().
handle(#{state := configuring}, Args) ->
set_breakpoints(Args);
handle(#{state := attached}, Args) ->
set_breakpoints(Args);
handle(State = #{state := configuring}, Args) ->
set_breakpoints(State, Args);
handle(State = #{state := attached}, Args) ->
set_breakpoints(State, Args);
handle(_UnexpectedState, _) ->
edb_dap_request:unexpected_request().

%% ------------------------------------------------------------------
%% Helpers
%% ------------------------------------------------------------------
-spec set_breakpoints(Args) -> edb_dap_request:reaction(response()) when
-spec set_breakpoints(State, Args) -> edb_dap_request:reaction(response()) when
State :: edb_dap_server:state(),
Args :: arguments().
set_breakpoints(Args = #{source := #{path := Path}}) ->
Module = binary_to_atom(filename:basename(Path, ".erl")),

set_breakpoints(State0, Args = #{source := #{path := Path}}) ->
SourceBreakpoints = maps:get(breakpoints, Args, []),
SourceBreakpointLines = [Line || #{line := Line} <- SourceBreakpoints],
#{dap_language := DapLanguage, dap_language_state := DapLanguageState0} = State0,
{Modules, DapLanguageState1} = DapLanguage:source_to_modules(Path, SourceBreakpointLines, DapLanguageState0),
State1 = State0#{dap_language_state => DapLanguageState1},

LineResults = edb:set_breakpoints(Module, SourceBreakpointLines),
LineResults = set_breakpoints_in_modules(Modules, SourceBreakpointLines),

Breakpoints = [
case Result of
Expand All @@ -230,7 +232,39 @@ set_breakpoints(Args = #{source := #{path := Path}}) ->
end
|| {Line, Result} <:- LineResults
],
#{response => edb_dap_request:success(#{breakpoints => Breakpoints})}.
#{
response => edb_dap_request:success(#{breakpoints => Breakpoints}),
new_state => State1
}.

-spec set_breakpoints_in_modules(Modules, Lines) -> edb:set_breakpoints_result() when
Modules :: [module()],
Lines :: [edb:line()].
set_breakpoints_in_modules([Module], Lines) ->
edb:set_breakpoints(Module, Lines);
set_breakpoints_in_modules(Modules, Lines) ->
ResultsByModule = [edb:set_breakpoints(Module, Lines) || Module <- Modules],
[{Line, merge_line_results(Line, ResultsByModule)} || Line <- Lines].

-spec merge_line_results(Line, ResultsByModule) -> ok | {error, edb:add_breakpoint_error()} when
Line :: edb:line(),
ResultsByModule :: [edb:set_breakpoints_result()].
merge_line_results(Line, ResultsByModule) ->
LineResults = [
Result
|| ModuleResults <- ResultsByModule,
{ResultLine, Result} <- ModuleResults,
ResultLine =:= Line
],
case lists:member(ok, LineResults) of
true ->
ok;
false ->
case LineResults of
[{error, Reason} | _] -> {error, Reason};
[] -> {error, {badkey, Line}}
end
end.

-spec format_breakpoint_error(Error) -> binary() when
Error :: edb:add_breakpoint_error().
Expand Down
29 changes: 22 additions & 7 deletions apps/edb/src/edb_dap_server.erl
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ For details see https://microsoft.github.io/debug-adapter-protocol/specification
-include_lib("edb/include/edb_dap.hrl").

% Public API
-export([start_link/0]).
-export([start_link/0, start_link/1]).
-export([handle_message/1]).

%% gen_server callbacks
Expand All @@ -47,6 +47,9 @@ For details see https://microsoft.github.io/debug-adapter-protocol/specification
%%% Types
%%%---------------------------------------------------------------------------------
-type client_info() :: edb_dap_request_initialize:arguments().
-type options() :: #{
dap_language => module()
}.

-type attach_type() ::
#{
Expand All @@ -62,17 +65,20 @@ For details see https://microsoft.github.io/debug-adapter-protocol/specification
-type state() ::
#{
% Server is up, waiting for an `initialize` request from the client
state := started
state := started,
dap_language := module()
}
| #{
% Server received an `initialize` request and is waiting for `attach`/`launch` requests
state := initialized,
client_info := client_info()
client_info := client_info(),
dap_language := module()
}
| #{
% A `launch` request was received and we are waiting for the debuggee node to be up
state := launching,
client_info := client_info(),
dap_language := module(),
port := port() | none,
shell_process_id => number(),
reverse_attach_ref := reference(),
Expand All @@ -87,21 +93,25 @@ For details see https://microsoft.github.io/debug-adapter-protocol/specification
state := configuring,
type := attach_type(),
client_info := client_info(),
dap_language := module(),
port := port() | none,
node := node(),
reverse_attach_ref := reference() | undefined,
cwd := binary(),
dap_language_state := edb_dap_language:state(),
subscription := edb:event_subscription()
}
| #{
% We are attached to the debuggee node, and debugging is in progress
state := attached,
type := attach_type(),
client_info := client_info(),
dap_language := module(),
port := port() | none,
node := node(),
reverse_attach_ref := reference() | undefined,
cwd := binary(),
dap_language_state := edb_dap_language:state(),
subscription := edb:event_subscription()
}
| #{
Expand Down Expand Up @@ -154,7 +164,11 @@ For details see https://microsoft.github.io/debug-adapter-protocol/specification

-spec start_link() -> {ok, pid()}.
start_link() ->
{ok, _Pid} = gen_server:start_link({local, ?SERVER}, ?MODULE, noargs, []).
start_link(#{}).

-spec start_link(options()) -> {ok, pid()}.
start_link(Options) ->
{ok, _Pid} = gen_server:start_link({local, ?SERVER}, ?MODULE, Options, []).

-spec handle_message(Message) -> ok when
Message :: edb_dap:request() | edb_dap:response().
Expand All @@ -165,9 +179,10 @@ handle_message(Message) ->
%%% Callbacks
%%%---------------------------------------------------------------------------------

-spec init(noargs) -> {ok, state()}.
init(noargs) ->
{ok, #{state => started}}.
-spec init(options()) -> {ok, state()}.
init(Options) ->
DapLanguage = maps:get(dap_language, Options, edb_dap_language_erlang),
{ok, #{state => started, dap_language => DapLanguage}}.

-spec terminate(Reason :: term(), state()) -> ok.
terminate(_Reason, _State) ->
Expand Down
18 changes: 13 additions & 5 deletions apps/edb/src/edb_sup.erl
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,23 @@ edb top level supervisor.

-behaviour(supervisor).

-export([start_link/0]).
-export([start_link/0, start_link/1]).

-export([init/1]).

-define(SERVER, ?MODULE).

-type options() :: #{
dap_language => module()
}.

-spec start_link() -> supervisor:startlink_ret().
start_link() ->
supervisor:start_link({local, ?SERVER}, ?MODULE, []).
start_link(#{}).

-spec start_link(options()) -> supervisor:startlink_ret().
start_link(Options) ->
supervisor:start_link({local, ?SERVER}, ?MODULE, [Options]).

%% sup_flags() = #{strategy => strategy(), % optional
%% intensity => non_neg_integer(), % optional
Expand All @@ -43,8 +51,8 @@ start_link() ->
%% shutdown => shutdown(), % optional
%% type => worker(), % optional
%% modules => modules()} % optional
-spec init([]) -> {ok, {supervisor:sup_flags(), [supervisor:child_spec()]}}.
init([]) ->
-spec init([options()]) -> {ok, {supervisor:sup_flags(), [supervisor:child_spec()]}}.
init([Options]) ->
SupFlags = #{
strategy => rest_for_one,
intensity => 5,
Expand All @@ -66,7 +74,7 @@ init([]) ->
},
#{
id => edb_dap_server,
start => {edb_dap_server, start_link, []},
start => {edb_dap_server, start_link, [maps:with([dap_language], Options)]},
restart => transient
},
#{
Expand Down
Loading
Loading