%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 1998-2016. All Rights Reserved.
%%
%% 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.
%%
%% %CopyrightEnd%
%%
-module(seq_trace).
-define(SEQ_TRACE_SEND, 1). %(1 << 0)
-define(SEQ_TRACE_RECEIVE, 2). %(1 << 1)
-define(SEQ_TRACE_PRINT, 4). %(1 << 2)
-define(SEQ_TRACE_NOW_TIMESTAMP, 8). %(1 << 3)
-define(SEQ_TRACE_STRICT_MON_TIMESTAMP, 16). %(1 << 4)
-define(SEQ_TRACE_MON_TIMESTAMP, 32). %(1 << 5)
-export([set_token/1,
set_token/2,
get_token/0,
get_token/1,
print/1,
print/2,
reset_trace/0,
set_system_tracer/1,
get_system_tracer/0]).
%%---------------------------------------------------------------------------
-type flag() :: 'send' | 'receive' | 'print' | 'timestamp' | 'monotonic_timestamp' | 'strict_monotonic_timestamp'.
-type component() :: 'label' | 'serial' | flag().
-type value() :: (Integer :: non_neg_integer())
| {Previous :: non_neg_integer(),
Current :: non_neg_integer()}
| (Bool :: boolean()).
%%---------------------------------------------------------------------------
-type token() :: {integer(), boolean(), _, _, _}.
-spec set_token(Token) -> PreviousToken | 'ok' when
Token :: [] | token(),
PreviousToken :: [] | token().
set_token([]) ->
erlang:seq_trace(sequential_trace_token,[]);
set_token({Flags,Label,Serial,_From,Lastcnt}) ->
F = decode_flags(Flags),
set_token2([{label,Label},{serial,{Lastcnt, Serial}} | F]).
%% We limit the label type to always be a small integer because erl_interface
%% expects that, the BIF can however "unofficially" handle atoms as well, and
%% atoms can be used if only Erlang nodes are involved
-spec set_token(Component, Val) -> {Component, OldVal} when
Component :: component(),
Val :: value(),
OldVal :: value().
set_token(Type, Val) ->
erlang:seq_trace(Type, Val).
-spec get_token() -> [] | token().
get_token() ->
element(2,process_info(self(),sequential_trace_token)).
-spec get_token(Component) -> {Component, Val} when
Component :: component(),
Val :: value().
get_token(Type) ->
erlang:seq_trace_info(Type).
-spec print(TraceInfo) -> 'ok' when
TraceInfo :: term().
print(Term) ->
erlang:seq_trace_print(Term),
ok.
-spec print(Label, TraceInfo) -> 'ok' when
Label :: integer(),
TraceInfo :: term().
print(Label, Term) when is_atom(Label) ->
erlang:error(badarg, [Label, Term]);
print(Label, Term) ->
erlang:seq_trace_print(Label, Term),
ok.
-spec reset_trace() -> 'true'.
reset_trace() ->
erlang:system_flag(1, 0).
%% reset_trace(Pid) -> % this might be a useful function too
-type tracer() :: (Pid :: pid()) | port() | 'false'.
-spec set_system_tracer(Tracer) -> OldTracer when
Tracer :: tracer(),
OldTracer :: tracer().
set_system_tracer(Pid) ->
erlang:system_flag(sequential_tracer, Pid).
-spec get_system_tracer() -> Tracer when
Tracer :: tracer().
get_system_tracer() ->
element(2, erlang:system_info(sequential_tracer)).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% internal help functions
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
set_token2([{Type,Val}|T]) ->
_ = erlang:seq_trace(Type, Val),
set_token2(T);
set_token2([]) ->
ok.
decode_flags(Flags) ->
Print = (Flags band ?SEQ_TRACE_PRINT) > 0,
Send = (Flags band ?SEQ_TRACE_SEND) > 0,
Rec = (Flags band ?SEQ_TRACE_RECEIVE) > 0,
NowTs = (Flags band ?SEQ_TRACE_NOW_TIMESTAMP) > 0,
StrictMonTs = (Flags band ?SEQ_TRACE_STRICT_MON_TIMESTAMP) > 0,
MonTs = (Flags band ?SEQ_TRACE_MON_TIMESTAMP) > 0,
[{print,Print},{send,Send},{'receive',Rec},{timestamp,NowTs},
{strict_monotonic_timestamp, StrictMonTs},
{monotonic_timestamp, MonTs}].