Current section
Files
Jump to
Current section
Files
src/fs_tracer.erl
-module(fs_tracer).
-behaviour(gen_server).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
-export([go/0, add/2, del/2, clear/0, list/0]).
-export([formatter/1]).
-record(state, {
pid :: pid(),
trace :: list(),
formatter = undefined :: undefined | fun()
}).
go() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
formatter(F) ->
gen_server:call(?MODULE, {formatter, F}).
add(M, F) ->
gen_server:call(?MODULE, {add, M, F}).
clear() ->
gen_server:call(?MODULE, {clear}).
del(M,F) ->
gen_server:call(?MODULE, {del, M, F}).
list() ->
gen_server:call(?MODULE, {list}).
safe_formatter(F, A) ->
try
F(A)
catch
_:_ -> A
end.
one([X]) -> X;
one(X) -> X.
apply_formatter(undefined, Args) -> Args;
apply_formatter(F, Args) ->
lists:map(fun(A) -> safe_formatter(F, A) end, Args).
handle_info({trace_ts, _Sender, call, {M,F,A}, _TS}, S=#state{formatter=Fmt}) ->
io:format("TRACE: ~s:~s/~p <- ~180p~n", [M, F, erlang:length(A), apply_formatter(Fmt, A)]),
{noreply, S};
handle_info({trace_ts, _Sender, return_to, {M,F,A}, _TS}, S=#state{}) ->
io:format("TRACE: ~s:~s/~p~n", [M,F,A]),
{noreply, S};
handle_info({trace_ts, _Sender, return_from, {M,F,A}, Value, _TS}, S=#state{formatter=Fmt}) ->
io:format("TRACE: ~s:~s/~p -> ~180p~n", [M,F,A, one(apply_formatter(Fmt, [Value]))]),
{noreply, S};
handle_info(Msg, S=#state{}) ->
io:format("TRACE ALL:~p~n", [Msg]),
{noreply, S}.
init([]) ->
{ok, #state{pid=self(), trace=[]}}.
handle_cast(_Msg, S=#state{}) -> {noreply, S}.
handle_call({add, M, F}, _From, S=#state{pid=Pid, trace=Trace}) ->
case lists:member({M,F}, Trace) of
true ->
{reply, 0, S};
false ->
Num = add_trace(Pid, M, F),
{reply, Num, S#state{trace=[{M,F} | Trace]}}
end;
handle_call({del, M, F}, _From, S=#state{pid=Pid, trace=Trace}) ->
case lists:member({M,F}, Trace) of
true ->
Num = del_trace(Pid, M, F),
{reply, Num, S#state{trace=lists:filter(fun(X) -> X =/= {M,F} end, Trace)}};
false ->
{reply, 0, S}
end;
handle_call({formatter, F}, _From, S=#state{}) ->
{reply, ok, S#state{formatter=F}};
handle_call({clear}, _From, S=#state{pid=Pid, trace=Trace}) ->
Re = [ del_trace(Pid, M, F) || {M,F} <- Trace ],
{reply, Re, S#state{trace=[]}};
handle_call({list}, _From, S=#state{trace=Trace}) ->
{reply, Trace, S};
handle_call(_Request, _From, S=#state{}) -> {reply, ok, S}.
terminate(_Reason, _S) -> ok.
code_change(_OldVsn, S=#state{}, _Extra) -> {ok, S}.
del_trace(_Pid, M, F) ->
erlang:trace_pattern({M,F,'_'}, false, [local]).
add_trace(Pid, M, F) ->
erlang:trace(all, true, [call, timestamp, {tracer, Pid}]),
Pattern = [{
'_',
[],
[{return_trace}]
}],
erlang:trace_pattern({M,F,'_'}, Pattern, [local]).