From 37092dab15448ef6a078800e3ff0cc41880ea765 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 10 Dec 2015 11:10:46 +0100 Subject: erts: Implement tracer modules Add the possibility to use modules as trace data receivers. The functions in the module have to be nifs as otherwise complex trace probes will be very hard to handle (complex means trace probes for ports for example). This commit changes the way that the ptab->tracer field works from always being an immediate, to now be NIL if no tracer is present or else be the tuple {TracerModule, TracerState} where TracerModule is an atom that is later used to lookup the appropriate tracer callbacks to call and TracerState is just passed to the tracer callback. The default process and port tracers have been rewritten to use the new API. This commit also changes the order which trace messages are delivered to the potential tracer process. Any enif_send done in a tracer module may be delayed indefinitely because of lock order issues. If a message is delayed any other trace message send from that process is also delayed so that order is preserved for each traced entity. This means that for some trace events (i.e. send/receive) the events may come in an unintuitive order (receive before send) to the trace receiver. Timestamps are taken when the trace message is generated so trace messages from differented processes may arrive with the timestamp out of order. Both the erlang:trace and seq_trace:set_system_tracer accept the new tracer module tracers and also the backwards compatible arguments. OTP-10267 --- erts/preloaded/ebin/erl_tracer.beam | Bin 0 -> 1864 bytes erts/preloaded/src/Makefile | 3 +- erts/preloaded/src/erl_tracer.erl | 41 +++++++++++++++++++++ erts/preloaded/src/erlang.erl | 67 ++++++++++++++++++++++++++++++----- erts/preloaded/src/erts_internal.erl | 29 ++++++++++++++- erts/preloaded/src/init.erl | 4 +++ 6 files changed, 134 insertions(+), 10 deletions(-) create mode 100644 erts/preloaded/ebin/erl_tracer.beam create mode 100644 erts/preloaded/src/erl_tracer.erl (limited to 'erts/preloaded') diff --git a/erts/preloaded/ebin/erl_tracer.beam b/erts/preloaded/ebin/erl_tracer.beam new file mode 100644 index 0000000000..dcb74c3bf7 Binary files /dev/null and b/erts/preloaded/ebin/erl_tracer.beam differ diff --git a/erts/preloaded/src/Makefile b/erts/preloaded/src/Makefile index 1e3de9f1d7..4a447d3a09 100644 --- a/erts/preloaded/src/Makefile +++ b/erts/preloaded/src/Makefile @@ -43,7 +43,8 @@ PRE_LOADED_ERL_MODULES = \ otp_ring0 \ erts_code_purger \ erlang \ - erts_internal + erts_internal \ + erl_tracer PRE_LOADED_BEAM_MODULES = \ prim_eval diff --git a/erts/preloaded/src/erl_tracer.erl b/erts/preloaded/src/erl_tracer.erl new file mode 100644 index 0000000000..3415ff3135 --- /dev/null +++ b/erts/preloaded/src/erl_tracer.erl @@ -0,0 +1,41 @@ +-module(erl_tracer). + +-export([enabled/3, trace/6, on_load/0]). + +-type tracee() :: port() | pid() | undefined. +-type trace_tag() :: send | send_to_non_existing_process | 'receive' | + call | return_to | return_from | exception_from | + spawn | exit | link | unlink | getting_linked | + getting_unlinked | register | unregister | in | out | + gc_start | gc_end. +-type trace_opts() :: #{ match_spec_result => true | term(), + scheduler_id => undefined | non_neg_integer(), + timestamp => undefined | timestamp | cpu_timestamp | + monotonic | strict_monotonic }. +-type tracer_state() :: term(). + +on_load() -> + case erlang:load_nif(atom_to_list(?MODULE), 0) of + ok -> ok + end. + +%%% +%%% NIF placeholders +%%% + +-spec enabled(Tag :: trace_tag() | seq_trace | trace_status, + TracerState :: tracer_state(), + Tracee :: tracee()) -> + trace | discard | remove. +enabled(_, _, _) -> + erlang:nif_error(nif_not_loaded). + +-spec trace(Tag :: trace_tag() | seq_trace, + TracerState :: tracer_state(), + Tracee :: tracee(), + Msg :: term(), + Extra :: term(), + Opts :: trace_opts()) -> any(). + +trace(_, _, _, _, _, _) -> + erlang:nif_error(nif_not_loaded). diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 4374bdcd89..8d6d17b043 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -245,12 +245,14 @@ set_on_first_spawn | set_on_link | set_on_first_link | - {tracer, pid() | port()}. + {tracer, pid() | port()} | + {tracer, module(), term()}. -type trace_info_item_result() :: {traced, global | local | false | undefined} | {match_spec, trace_match_spec() | false | undefined} | {meta, pid() | port() | false | undefined | []} | + {meta, module(), term() } | {meta_match_spec, trace_match_spec() | false | undefined} | {call_count, non_neg_integer() | boolean() | undefined} | {call_time, [{pid(), non_neg_integer(), @@ -276,6 +278,7 @@ undefined | {flags, [trace_info_flag()]} | {tracer, pid() | port() | []} | + {tracer, module(), term()} | trace_info_item_result() | {all, [ trace_info_item_result() ] | false | undefined}. @@ -1709,8 +1712,28 @@ time() -> PidSpec :: pid() | existing | new | all, How :: boolean(), FlagList :: [trace_flag()]. -trace(_PidSpec, _How, _FlagList) -> - erlang:nif_error(undefined). +trace(PidSpec, How, FlagList) -> + %% Make sure that we have loaded the tracer module + case lists:keyfind(tracer, 1, FlagList) of + {tracer, Module, State} when erlang:is_atom(Module) -> + case erlang:module_loaded(Module) of + false -> + Module:enabled(trace_status, erlang:self(), State); + true -> + ok + end; + _ -> + ignore + end, + + try erts_internal:trace(PidSpec, How, FlagList) of + Res -> Res + catch E:R -> + {_, [_ | CST]} = erlang:process_info( + erlang:self(), current_stacktrace), + erlang:raise( + E, R, [{?MODULE, trace, [PidSpec, How, FlagList], []} | CST]) + end. %% trace_delivered/1 -spec erlang:trace_delivered(Tracee) -> Ref when @@ -2319,7 +2342,7 @@ subtract(_,_) -> OldState :: preliminary | final | volatile; %% These are deliberately not documented (internal_cpu_topology, term()) -> term(); - (sequential_tracer, pid() | port() | false) -> pid() | port() | false; + (sequential_tracer, pid() | port() | {module(), term()} | false) -> pid() | port() | false; (1,0) -> true. system_flag(_Flag, _Value) -> @@ -2355,12 +2378,20 @@ tl(_List) -> | boolean() | restart | pause. -trace_pattern(_MFA, _MatchSpec) -> - erlang:nif_error(undefined). +trace_pattern(MFA, MatchSpec) -> + try erts_internal:trace_pattern(MFA, MatchSpec, []) of + Res -> Res + catch E:R -> + {_, [_ | CST]} = erlang:process_info( + erlang:self(), current_stacktrace), + erlang:raise( + E, R, [{?MODULE, trace_pattern, [MFA, MatchSpec], []} | CST]) + end. -type trace_pattern_flag() :: global | local | meta | {meta, Pid :: pid()} | + {meta, TracerModule :: module(), TracerState :: term()} | call_count | call_time. @@ -2371,8 +2402,28 @@ trace_pattern(_MFA, _MatchSpec) -> | restart | pause, FlagList :: [ trace_pattern_flag() ]. -trace_pattern(_MFA, _MatchSpec, _FlagList) -> - erlang:nif_error(undefined). +trace_pattern(MFA, MatchSpec, FlagList) -> + %% Make sure that we have loaded the tracer module + case lists:keyfind(meta, 1, FlagList) of + {meta, Module, State} when erlang:is_atom(Module) -> + case erlang:module_loaded(Module) of + false -> + Module:enabled(trace_status, erlang:self(), State); + true -> + ok + end; + _ -> + ignore + end, + + try erts_internal:trace_pattern(MFA, MatchSpec, FlagList) of + Res -> Res + catch E:R -> + {_, [_ | CST]} = erlang:process_info( + erlang:self(), current_stacktrace), + erlang:raise( + E, R, [{?MODULE, trace_pattern, [MFA, MatchSpec, FlagList], []} | CST]) + end. %% Shadowed by erl_bif_types: erlang:tuple_to_list/1 -spec tuple_to_list(Tuple) -> [term()] when diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl index 769757ba75..2459ea2a2c 100644 --- a/erts/preloaded/src/erts_internal.erl +++ b/erts/preloaded/src/erts_internal.erl @@ -55,7 +55,9 @@ -export([await_microstate_accounting_modifications/3, gather_microstate_accounting_result/2]). -%% Auto-import name clash +-export([trace/3, trace_pattern/3]). + +%% Auto import name clash -export([check_process_code/2]). %% @@ -403,3 +405,28 @@ microstate_accounting(Ref, Threads) -> {Ref, Res} -> [Res | microstate_accounting(Ref, Threads - 1)] end. + +-spec trace(PidPortSpec, How, FlagList) -> integer() when + PidPortSpec :: pid() | port() + | all | processes | ports + | existing | existing_processes | existing_ports + | new | new_processes | new_ports, + How :: boolean(), + FlagList :: []. +trace(_PidSpec, _How, _FlagList) -> + erlang:nif_error(undefined). + +-type trace_pattern_mfa() :: + {atom(),atom(),arity() | '_'} | on_load. +-type trace_match_spec() :: + [{[term()] | '_' ,[term()],[term()]}]. + +-spec trace_pattern(MFA, MatchSpec, FlagList) -> non_neg_integer() when + MFA :: trace_pattern_mfa(), + MatchSpec :: (MatchSpecList :: trace_match_spec()) + | boolean() + | restart + | pause, + FlagList :: [ ]. +trace_pattern(_MFA, _MatchSpec, _FlagList) -> + erlang:nif_error(undefined). diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl index 5d5d2f8012..77684751c8 100644 --- a/erts/preloaded/src/init.erl +++ b/erts/preloaded/src/init.erl @@ -179,6 +179,10 @@ stop(Status) -> init ! {stop,{stop,Status}}, ok. boot(BootArgs) -> register(init, self()), process_flag(trap_exit, true), + + %% Load the tracer nif + erl_tracer:on_load(), + {Start0,Flags,Args} = parse_boot_args(BootArgs), Start = map(fun prepare_run_args/1, Start0), boot(Start, Flags, Args). -- cgit v1.2.3 From 6cb6b59cd4cd5bd4383053e12ae8ab192711c827 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 8 Feb 2016 18:31:43 +0100 Subject: erts: Extend process and port tracing This commit completes the tracing for processes so that all messages sent by a process (via nifs or otherwise) will be traced. The commit also adds tracing of all types of events from ports. When enabling tracing using erlang:trace, the 'all' flag now also enables tracing on all ports. OTP-13496 --- erts/preloaded/src/erl_tracer.erl | 3 ++- erts/preloaded/src/erlang.erl | 30 +++++++++++++++++++----------- 2 files changed, 21 insertions(+), 12 deletions(-) (limited to 'erts/preloaded') diff --git a/erts/preloaded/src/erl_tracer.erl b/erts/preloaded/src/erl_tracer.erl index 3415ff3135..a8da2b4c14 100644 --- a/erts/preloaded/src/erl_tracer.erl +++ b/erts/preloaded/src/erl_tracer.erl @@ -7,7 +7,8 @@ call | return_to | return_from | exception_from | spawn | exit | link | unlink | getting_linked | getting_unlinked | register | unregister | in | out | - gc_start | gc_end. + in_exiting | out_exiting | out_exited | + open | closed | gc_start | gc_end. -type trace_opts() :: #{ match_spec_result => true | term(), scheduler_id => undefined | non_neg_integer(), timestamp => undefined | timestamp | cpu_timestamp | diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 8d6d17b043..40d0abd321 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -230,17 +230,20 @@ send | 'receive' | procs | + ports | call | - silent | + arity | return_to | + silent | running | exiting | + running_procs | + running_ports | garbage_collection | timestamp | cpu_timestamp | monotonic_timestamp | strict_monotonic_timestamp | - arity | set_on_spawn | set_on_first_spawn | set_on_link | @@ -1708,11 +1711,14 @@ time() -> erlang:nif_error(undefined). %% trace/3 --spec erlang:trace(PidSpec, How, FlagList) -> integer() when - PidSpec :: pid() | existing | new | all, +-spec erlang:trace(PidPortSpec, How, FlagList) -> integer() when + PidPortSpec :: pid() | port() + | all | processes | ports + | existing | existing_processes | existing_ports + | new | new_processes | new_ports, How :: boolean(), FlagList :: [trace_flag()]. -trace(PidSpec, How, FlagList) -> +trace(PidPortSpec, How, FlagList) -> %% Make sure that we have loaded the tracer module case lists:keyfind(tracer, 1, FlagList) of {tracer, Module, State} when erlang:is_atom(Module) -> @@ -1726,13 +1732,13 @@ trace(PidSpec, How, FlagList) -> ignore end, - try erts_internal:trace(PidSpec, How, FlagList) of + try erts_internal:trace(PidPortSpec, How, FlagList) of Res -> Res catch E:R -> {_, [_ | CST]} = erlang:process_info( erlang:self(), current_stacktrace), erlang:raise( - E, R, [{?MODULE, trace, [PidSpec, How, FlagList], []} | CST]) + E, R, [{?MODULE, trace, [PidPortSpec, How, FlagList], []} | CST]) end. %% trace_delivered/1 @@ -1743,14 +1749,16 @@ trace_delivered(_Tracee) -> erlang:nif_error(undefined). %% trace_info/2 --spec erlang:trace_info(PidOrFunc, Item) -> Res when - PidOrFunc :: pid() | new | {Module, Function, Arity} | on_load, +-spec erlang:trace_info(PidPortOrFunc, Item) -> Res when + PidPortOrFunc :: pid() | port() | new | new_processes | new_ports + | {Module, Function, Arity} | on_load, Module :: module(), Function :: atom(), Arity :: arity(), - Item :: flags | tracer | traced | match_spec | meta | meta_match_spec | call_count | call_time | all, + Item :: flags | tracer | traced | match_spec + | meta | meta_match_spec | call_count | call_time | all, Res :: trace_info_return(). -trace_info(_PidOrFunc, _Item) -> +trace_info(_PidPortOrFunc, _Item) -> erlang:nif_error(undefined). %% trunc/1 -- cgit v1.2.3 From 3738103d0b1195e570a7525c4370cd490a5368aa Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 29 Feb 2016 15:09:26 +0100 Subject: erts: Add 'spawned' trace event to 'procs' trace flag OTP-13497 This trace event is triggered when a process is created from the process that is created. --- erts/preloaded/src/erl_tracer.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts/preloaded') diff --git a/erts/preloaded/src/erl_tracer.erl b/erts/preloaded/src/erl_tracer.erl index a8da2b4c14..2177e48f60 100644 --- a/erts/preloaded/src/erl_tracer.erl +++ b/erts/preloaded/src/erl_tracer.erl @@ -5,7 +5,7 @@ -type tracee() :: port() | pid() | undefined. -type trace_tag() :: send | send_to_non_existing_process | 'receive' | call | return_to | return_from | exception_from | - spawn | exit | link | unlink | getting_linked | + spawn | spawned | exit | link | unlink | getting_linked | getting_unlinked | register | unregister | in | out | in_exiting | out_exiting | out_exited | open | closed | gc_start | gc_end. -- cgit v1.2.3 From 8e2ec999394c77741241ef1a12728b11195961c8 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 21 Mar 2016 17:53:26 +0100 Subject: erts: Document erlang:match_spec_test/3 OTP-13501 --- erts/preloaded/src/erlang.erl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'erts/preloaded') diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 40d0abd321..3cc17014ff 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -1163,10 +1163,10 @@ map_size(_Map) -> erlang:nif_error(undefined). %% match_spec_test/3 --spec erlang:match_spec_test(P1, P2, P3) -> TestResult when - P1 :: [term()] | tuple(), - P2 :: term(), - P3 :: table | trace, +-spec erlang:match_spec_test(MatchAgainst, MatchSpec, Type) -> TestResult when + MatchAgainst :: [term()] | tuple(), + MatchSpec :: term(), + Type :: table | trace, TestResult :: {ok, term(), [return_trace], [ {error | warning, string()} ]} | {error, [ {error | warning, string()} ]}. match_spec_test(_P1, _P2, _P3) -> erlang:nif_error(undefined). -- cgit v1.2.3