Skip to content

Commit 0169fe2

Browse files
authored
Merge pull request #20 from erszcz/add-tracing-of-new-children
Enable tracing of newly spawned process children
2 parents 71757a4 + aa269be commit 0169fe2

2 files changed

Lines changed: 99 additions & 3 deletions

File tree

src/tr.erl

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,12 +110,19 @@
110110
trace := none | trace_spec(),
111111
tracer_pid := none | pid()}.
112112

113+
-type new_children_flags() :: all | first | none.
114+
%% Describes which newly spawned children of specified processes to trace - none by default.
115+
%% Handy for tracing a subtree of a process hierarchy, e.g. by tracing a supervisor,
116+
%% its children, and newly started children.
117+
113118
-type trace_spec() :: #{modules := module_spec(),
114119
pids := pids(),
120+
new_children := new_children_flags(),
115121
msg := message_event_types(),
116122
msg_trigger := msg_trigger()}.
117123
-type trace_options() :: #{modules => module_spec(),
118124
pids => pids(),
125+
new_children => new_children_flags(),
119126
msg => message_event_types(),
120127
msg_trigger => msg_trigger()}.
121128
%% Options for tracing.
@@ -293,7 +300,7 @@ trace_apps(Apps) ->
293300
trace(Modules) when is_list(Modules) ->
294301
trace(#{modules => Modules});
295302
trace(Opts) ->
296-
DefaultOpts = #{modules => [], pids => all,
303+
DefaultOpts = #{modules => [], pids => all, new_children => none,
297304
msg => none, msg_trigger => after_traced_call},
298305
Timeout = timer:minutes(1),
299306
gen_server:call(?MODULE, {start_trace, call, maps:merge(DefaultOpts, Opts)}, Timeout).
@@ -841,7 +848,7 @@ handle_tracer_exit(TracerPid, Reason) ->
841848

842849
-spec trace_flags(pid(), trace_spec()) -> erlang_trace_flags().
843850
trace_flags(TracerPid, Spec) ->
844-
[{tracer, TracerPid} | basic_trace_flags()] ++ msg_trace_flags(Spec).
851+
[{tracer, TracerPid} | basic_trace_flags()] ++ msg_trace_flags(Spec) ++ process_trace_flags(Spec).
845852

846853
-spec msg_trace_flags(trace_spec()) -> erlang_trace_flags().
847854
msg_trace_flags(#{msg := all}) -> [send, 'receive'];
@@ -851,6 +858,10 @@ msg_trace_flags(#{msg := none}) -> [].
851858

852859
basic_trace_flags() -> [call, timestamp].
853860

861+
process_trace_flags(#{new_children := all}) -> [set_on_spawn];
862+
process_trace_flags(#{new_children := first}) -> [set_on_first_spawn];
863+
process_trace_flags(#{new_children := none}) -> [].
864+
854865
-spec set_tracing(pids(), boolean(), erlang_trace_flags()) -> ok.
855866
set_tracing(all, How, FlagList) ->
856867
erlang:trace(all, How, FlagList),

test/tr_SUITE.erl

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ groups() ->
4040
duplicates]},
4141
{trace, [single_pid,
4242
single_pid_with_msg,
43-
msg_after_traced_call]},
43+
msg_after_traced_call,
44+
new_children_first,
45+
new_children_all]},
4446
{range, [ranges,
4547
ranges_max_depth,
4648
range,
@@ -200,6 +202,68 @@ msg_after_traced_call(_Config) ->
200202
#tr{index = 3, event = send, pid = Pid, data = {ok, 1}, info = {Self, true}},
201203
#tr{index = 4, event = recv, pid = Pid, data = stop}] = tr:select().
202204

205+
new_children_first(_Config) ->
206+
%% Start a parent process before tracing
207+
Parent = self(),
208+
ParentPid = spawn_link(fun() -> spawner_loop(Parent) end),
209+
receive {spawner_ready, ParentPid} -> ok end,
210+
211+
%% Trace only the parent with new_children => first
212+
MFA = {?MODULE, traced_child_fun, 1},
213+
tr:trace(#{modules => [MFA], pids => [ParentPid], new_children => first}),
214+
215+
%% Spawn two children from the parent - only the first should be traced
216+
ParentPid ! {spawn_child, 1},
217+
Child1 = receive {child_spawned, Ch1, 1} -> Ch1 end,
218+
ParentPid ! {spawn_child, 2},
219+
Child2 = receive {child_spawned, Ch2, 2} -> Ch2 end,
220+
221+
%% Wait for traces - only 2 traces from the first child (call + return)
222+
wait_for_traces(2),
223+
tr:stop_tracing(),
224+
225+
%% Clean up
226+
ParentPid ! stop,
227+
228+
%% Verify only the first child's traces are collected
229+
[#tr{index = 1, event = call, mfa = MFA, pid = Child1, data = [1]},
230+
#tr{index = 2, event = return, mfa = MFA, pid = Child1, data = 1}] = tr:select(),
231+
232+
%% Verify Child2 is different from Child1
233+
?assertNotEqual(Child1, Child2).
234+
235+
new_children_all(_Config) ->
236+
%% Start a parent process before tracing
237+
Parent = self(),
238+
ParentPid = spawn_link(fun() -> spawner_loop(Parent) end),
239+
receive {spawner_ready, ParentPid} -> ok end,
240+
241+
%% Trace only the parent with new_children => all
242+
MFA = {?MODULE, traced_child_fun, 1},
243+
tr:trace(#{modules => [MFA], pids => [ParentPid], new_children => all}),
244+
245+
%% Spawn two children from the parent - both should be traced
246+
ParentPid ! {spawn_child, 1},
247+
Child1 = receive {child_spawned, Ch1, 1} -> Ch1 end,
248+
ParentPid ! {spawn_child, 2},
249+
Child2 = receive {child_spawned, Ch2, 2} -> Ch2 end,
250+
251+
%% Wait for traces - 4 traces total (2 from each child)
252+
wait_for_traces(4),
253+
tr:stop_tracing(),
254+
255+
%% Clean up
256+
ParentPid ! stop,
257+
258+
%% Verify both children's traces are collected
259+
[#tr{index = 1, event = call, mfa = MFA, pid = Child1, data = [1]},
260+
#tr{index = 2, event = return, mfa = MFA, pid = Child1, data = 1},
261+
#tr{index = 3, event = call, mfa = MFA, pid = Child2, data = [2]},
262+
#tr{index = 4, event = return, mfa = MFA, pid = Child2, data = 2}] = tr:select(),
263+
264+
%% Verify Child2 is different from Child1
265+
?assertNotEqual(Child1, Child2).
266+
203267
ranges(_Config) ->
204268
Traces = trace_fib3(),
205269

@@ -835,3 +899,24 @@ log(#{msg := {Format, Args}, level := Level}, #{config := Pid}) ->
835899
Pid ! {Level, lists:flatten(io_lib:format(Format, Args))};
836900
log(_Event, _Config) ->
837901
ok.
902+
903+
%% Helper for new_children tests
904+
spawner_loop(Parent) ->
905+
Parent ! {spawner_ready, self()},
906+
spawner_loop_ready(Parent).
907+
908+
spawner_loop_ready(Parent) ->
909+
receive
910+
{spawn_child, Id} ->
911+
Child = spawn_link(fun() ->
912+
?MODULE:traced_child_fun(Id),
913+
receive after infinity -> ok end
914+
end),
915+
Parent ! {child_spawned, Child, Id},
916+
spawner_loop_ready(Parent);
917+
stop ->
918+
ok
919+
end.
920+
921+
traced_child_fun(N) ->
922+
N.

0 commit comments

Comments
 (0)