Skip to content

Commit c58a0d0

Browse files
agastyabahlfacebook-github-bot
authored andcommitted
Add option enable_multi_node to edb:reverse_attach()
Summary: # Context As part of multi-node debugging support, we need to issue a startDebugging reverse-request for each new node that attempts to attach. # Problem We need to make this an optional part of reverse attaching, since we want the experience of debugging and reverse attaching onto one node to remain the same. # This diff Adds `multi_node_enabled` and `add_flags_necessary` as `boolean()` options to `edb:reverse_attach/1`. The `node_attached` event is now only sent when `multi_node_enabled` is set to `true`. It is by default `false`. `add_flags_necessary` is true by default and specifies if the user requires edb to setup ERL_AFLAGS to inject reverse attach code, or if they have already done so. Reviewed By: jcpetruzza Differential Revision: D80168956 fbshipit-source-id: 003f868f9ddb1f38d89cf5a6414aa919bd2a3810
1 parent 4a4610f commit c58a0d0

3 files changed

Lines changed: 228 additions & 33 deletions

File tree

edb_core/src/edb.erl

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -271,16 +271,33 @@ Subscribers will receive a single event of the form `{edb_event, Subscription, E
271271

272272
The events include a `Ref` that should match the reference that was returned by this call.
273273

274+
When the `enable_multi_node` options is set to `true`, additional nodes may attempt to reverse attach after the
275+
initial reverse-attach event. In that case, those nodes will be paused and subscribers will receive a new
276+
`{reverse_attach, Ref, {node_attached, AdditionalNode}}` event. In that case, `edb` will remain attached
277+
to the initial node.
278+
279+
By default, it is the caller's responsibility to ensure that additional nodes execute the code returned by this
280+
function, so that these nodes try to reverse-attach as well. As a convenience, the `instrument_erl_aflags_on_attach` can
281+
be set to `true` and the `ERL_AFLAGS` variable of the first node attached will be instrumented to contain a suitable
282+
`-eval` instruction.
283+
274284
This call may start distribution and set the node name.
275285

276286
Options:
277287

278288
- `name_domain`: whether we expect to be attached by a node using longnames or shortnames;
279-
- `timeout`: how long to wait for the node to be up; defaults to infinity.
289+
- `timeout`: how long to wait for the node to be up; defaults to infinity;
290+
- `multi_node_enabled`: whether to allow multiple nodes to attach; defaults to `false`;
291+
- `instrument_erl_aflags_on_attach`: the ERL_AFLAGS will be updated with an `-eval` flag on the first attachment; defaults to `false`.
280292

281293
""".
282294
-spec reverse_attach(Opts) -> {ok, Info} | {error, Reason} when
283-
Opts :: #{name_domain := longnames | shortnames, timeout => timeout()},
295+
Opts :: #{
296+
name_domain := longnames | shortnames,
297+
timeout => timeout(),
298+
multi_node_enabled => boolean(),
299+
instrument_erl_aflags_on_attach => boolean()
300+
},
284301
Info :: #{erl_code_to_inject := binary(), reverse_attach_ref := reference()},
285302
Reason :: attachment_in_progress.
286303
reverse_attach(AttachOpts0) ->
@@ -289,19 +306,43 @@ reverse_attach(AttachOpts0) ->
289306
default => infinity,
290307
parse => fun parse_timeout/1
291308
}),
309+
{MultiNodeEnabled, AttachOpts3} = take_arg(multi_node_enabled, AttachOpts2, #{
310+
default => false,
311+
parse => fun parse_boolean/1
312+
}),
313+
{InstrumentErlAflagsOnAttach, AttachOpts4} = take_arg(instrument_erl_aflags_on_attach, AttachOpts3, #{
314+
default => false,
315+
parse => fun parse_boolean/1
316+
}),
292317

293-
ok = no_more_args(AttachOpts2),
318+
ok = no_more_args(AttachOpts4),
294319

295320
case net_kernel:get_state() of
296321
#{started := no} -> ok = start_distribution(NameDomain);
297322
#{started := _, name_domain := NameDomain} -> ok;
298323
#{started := _, name_domain := _} -> error({invalid_name_domain, NameDomain})
299324
end,
300325

301-
{ok, GatekeeperId, ReverseAttachCode} = edb_gatekeeper:new(),
326+
{ok, GatekeeperId, ReverseAttachCode} = edb_gatekeeper:new(#{multi_node_enabled => MultiNodeEnabled}),
327+
ReverseAttachCodeInFlags =
328+
case {MultiNodeEnabled, InstrumentErlAflagsOnAttach} of
329+
{true, true} ->
330+
ReverseAttachCode;
331+
{false, true} ->
332+
error({badarg, {instrument_erl_aflags_on_attach, requires, multi_node_enabled}});
333+
_ ->
334+
none
335+
end,
336+
302337
ReverseAttachRef = erlang:make_ref(),
303338
case
304-
edb_node_monitor:expect_reverse_attach(GatekeeperId, ReverseAttachRef, ReverseAttachTimeout, ReverseAttachCode)
339+
edb_node_monitor:expect_reverse_attach(
340+
GatekeeperId, ReverseAttachRef, #{
341+
timeout => ReverseAttachTimeout,
342+
reverse_attach_code => ReverseAttachCodeInFlags,
343+
multi_node_enabled => MultiNodeEnabled
344+
}
345+
)
305346
of
306347
ok ->
307348
{ok, #{
@@ -865,6 +906,10 @@ parse_timeout(Timeout) -> parse_non_neg_integer(Timeout).
865906
parse_name_domain(longnames) -> longnames;
866907
parse_name_domain(shortnames) -> shortnames.
867908

909+
-spec parse_boolean(term()) -> boolean().
910+
parse_boolean(true) -> true;
911+
parse_boolean(false) -> false.
912+
868913
-spec parse_pair(L, R) -> fun((term()) -> {A, B}) when
869914
L :: fun((term()) -> A), R :: fun((term()) -> B).
870915
parse_pair(L, R) ->

edb_core/src/edb_node_monitor.erl

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
%% Public API
2727
-export([start_link/0]).
2828
-export([attach/2, detach/0, attached_node/0]).
29-
-export([expect_reverse_attach/4, reverse_attach_notification/2]).
29+
-export([expect_reverse_attach/3, reverse_attach_notification/2]).
3030

3131
-export([subscribe/0, unsubscribe/1]).
3232
-export([safe_sname_hostname/0]).
@@ -138,20 +138,22 @@ attached_node() ->
138138
error(not_attached)
139139
end.
140140

141-
-spec expect_reverse_attach(Id, ReverseAttachRef, Timeout, ReverseAttachCode) -> ok | {error, Reason} when
141+
-spec expect_reverse_attach(Id, ReverseAttachRef, Opts) ->
142+
ok | {error, Reason}
143+
when
142144
Id :: edb_gatekeeper:id(),
143145
ReverseAttachRef :: reference(),
144-
Timeout :: timeout(),
145-
ReverseAttachCode :: none | binary(),
146+
Opts :: #{
147+
timeout := timeout(),
148+
reverse_attach_code := none | binary(),
149+
multi_node_enabled := boolean()
150+
},
146151
Reason :: attachment_in_progress.
147-
expect_reverse_attach(Id, ReverseAttachRef, Timeout, ReverseAttachCode) ->
152+
expect_reverse_attach(Id, ReverseAttachRef, Opts) ->
148153
call(
149-
{expect_reverse_attach, #{
154+
{expect_reverse_attach, Opts#{
150155
gatekeeper_id => Id,
151-
reverse_attach_ref => ReverseAttachRef,
152-
timeout => Timeout,
153-
reverse_attach_code => ReverseAttachCode,
154-
multi_node_enabled => false
156+
reverse_attach_ref => ReverseAttachRef
155157
}}
156158
).
157159

@@ -393,17 +395,12 @@ expect_reverse_attach_impl(
393395
Data0
394396
) ->
395397
{_, _, Data1} = detach_impl_1(State0, Data0),
396-
ReverseAttachCodeToUse =
397-
case MultiNodeEnabled of
398-
true -> ReverseAttachCode;
399-
false -> none
400-
end,
401398
State1 = #{
402399
state => attachment_in_progress,
403400
type => reverse_attach,
404401
gatekeeper => GatekeeperId,
405402
reverse_attach_ref => ReverseAttachRef,
406-
multi_node_reverse_attach_code => ReverseAttachCodeToUse,
403+
multi_node_reverse_attach_code => ReverseAttachCode,
407404
multi_node_enabled => MultiNodeEnabled
408405
},
409406
{next_state, State1, Data1, [

0 commit comments

Comments
 (0)