The Trace Tool Builder is a base for building trace tools for
single node or distributed erlang systems. It requires the
The main features of the Trace Tool Builder are:
Even though the intention of the Trace Tool Builder is to serve
as a base for tailor made trace tools, it is of course possible
to use it directly from the erlang shell. The application only
allows the use of file port tracer, so if you would like would
like to use other types of trace clients you will be better off
using
The
If you want to trace function calls (i.e. if you have the
This small module is used in the example:
-module(m).
-export([f/0]).
f() ->
receive
From when pid(From) ->
Now = erlang:now(),
From ! {self(),Now}
end.
The following example shows the basic use of
%% First I spawn a process running my test function
(tiger@durin)47> Pid = spawn(m,f,[]).
<0.125.0>
(tiger@durin)48>
(tiger@durin)48> %% Then I start a tracer...
(tiger@durin)48> ttb:tracer().
{ok,[tiger@durin]}
(tiger@durin)49>
(tiger@durin)49> %% and activate the new process for tracing
(tiger@durin)49> %% function calls and sent messages.
(tiger@durin)49> ttb:p(Pid,[call,send]).
{ok,[{<0.125.0>,[{matched,tiger@durin,1}]}]}
(tiger@durin)50>
(tiger@durin)50> %% Here I set a trace pattern on erlang:now/0
(tiger@durin)50> %% The trace pattern is a simple match spec
(tiger@durin)50> %% generated by dbg:fun2ms/1. It indicates that
(tiger@durin)50> %% the return value shall be traced.
(tiger@durin)50> MS = dbg:fun2ms(fun(_) -> return_trace() end).
[{'_',[],[{return_trace}]}]
(tiger@durin)51> ttb:tp(erlang,now,MS).
{ok,[{matched,tiger@durin,1},{saved,1}]}
(tiger@durin)52>
(tiger@durin)52> %% I run my test (i.e. send a message to
(tiger@durin)52> %% my new process)
(tiger@durin)52> Pid ! self().
<0.72.0>
(tiger@durin)53>
(tiger@durin)53> %% And then I have to stop ttb in order to flush
(tiger@durin)53> %% the trace port buffer
(tiger@durin)53> ttb:stop().
stopped
(tiger@durin)54>
(tiger@durin)54> %% Finally I format my trace log
(tiger@durin)54> ttb:format("tiger@durin-ttb").
({<0.125.0>,{m,f,0},tiger@durin}) call erlang:now()
({<0.125.0>,{m,f,0},tiger@durin}) returned from erlang:now/0 ->
{1031,133451,667611}
({<0.125.0>,{m,f,0},tiger@durin}) <0.72.0> !
{<0.125.0>,{1031,133451,667611}}
ok ]]>
This small example shows a simple tool for "debug tracing", i.e. tracing of function calls with return values.
%% The options specify that the binary log shall be named
%% -debug_log and that the print/4 function in this
%% module shall be used as format handler
ttb:tracer(all,[{file,"debug_log"},{handler,{{?MODULE,print},0}}]),
%% All processes (existing and new) shall trace function calls
%% and include a timestamp in each trace message
ttb:p(all,[call,timestamp]).
%%% Set trace pattern on function(s)
trc(M) when atom(M) ->
trc({M,'_','_'});
trc({M,F}) when atom(M), atom(F) ->
trc({M,F,'_'});
trc({M,F,_A}=MFA) when atom(M), atom(F) ->
%% This match spec specifies that return values shall
%% be traced. NOTE that ms_transform.hrl must be included
%% if dbg:fun2ms/1 shall be used!
MatchSpec = dbg:fun2ms(fun(_) -> return_trace() end),
ttb:tpl(MFA,MatchSpec).
%%% Format a binary trace log
format(File) ->
ttb:format(File).
%%% Stop the "mydebug" tool
stop() ->
ttb:stop().
%%% --------Internal functions--------
%%% ----------------------------------
%%% Format handler
print(_Out,end_of_trace,_TI,N) ->
N;
print(Out,Trace,_TI,N) ->
do_print(Out,Trace,N),
N+1.
do_print(Out,{trace_ts,P,call,{M,F,A},Ts},N) ->
io:format(Out,
"~w: ~w, ~w:~n"
"Call : ~w:~w/~w~n"
"Arguments :~p~n~n",
[N,Ts,P,M,F,length(A),A]);
do_print(Out,{trace_ts,P,return_from,{M,F,A},R,Ts},N) ->
io:format(Out,
"~w: ~w, ~w:~n"
"Return from : ~w:~w/~w~n"
"Return value :~p~n~n",
[N,Ts,P,M,F,A,R]). ]]>
To distinguish trace logs produced with this tool from other
logs, the
By using the
The
The Observer application might not always be available on the node that shall be traced (in the following called the "traced node"). It is still possible to run the Trace Tool Builder from another node (in the following called the "trace control node") as long as
If the Trace Tool Builder shall be used against a remote node,
it is highly recommended to start the trace control node as
hidden. This way it can connect to the traced node
without the traced node "seeing" it, i.e. if the
% erl -sname trace_control -hidden
If the traced node is diskless,
(trace_control@durin)1> ttb:tracer(mynode@diskless,[{file,{local,
{wrap,"mytrace"}}}]).
{ok,[mynode@diskless]}
In addition to the trace log file(s), a file with the extension
To be able to use all this information during formatting, it is
important that the trace information file exists in the same
directory as the trace log, and that it has the same name as the
trace log with the additional extension
Except for the process information, everything in the trace
information file is passed on to the handler function when
formatting. The
You can add information to the trace information file by
calling
If you want to limit the size of the trace logs, you can use
wrap logs. This works almost like a circular buffer. You can
specify the maximum number of binary logs and the maximum size of
each log.
Wrap logs can be formatted one by one or all at once. See
Formatting can be done automatically when stopping
Formatting means to read a binary log and present it in a
readable format. You can use the default format handler in
The first argument to
The second argument to
A format handler is a fun taking four arguments. This fun will be called for each trace message in the binary log(s). A simple example which only prints each trace message could be like this:
fun(Fd, Trace, _TraceInfo, State) ->
io:format(Fd, "Trace: ~p~n", [Trace]),
State
end.
ttb:format("tiger@durin-ttb", [{handler, {{Mod,Fun}, initial_state}}])
^^^^^^^^^^^^^
Another format handler could be used to calculate time spent by the garbage collector:
fun(_Fd,{trace_ts,P,gc_start,_Info,StartTs},_TraceInfo,State) ->
[{P,StartTs}|State];
(Fd,{trace_ts,P,gc_end,_Info,EndTs},_TraceInfo,State) ->
{value,{P,StartTs}} = lists:keysearch(P,1,State),
Time = diff(StartTs,EndTs),
io:format("GC in process ~w: ~w milliseconds~n", [P,Time]),
State -- [{P,StartTs}]
end
A more refined version of this format handler is the function
By giving the format handler
Wrap logs can be formatted one by one or all in one go. To format one of the wrap logs in a set, give the exact name of the file. To format the whole set of wrap logs, give the name with '*' instead of the wrap count. An example:
Start tracing:
(tiger@durin)1> ttb:tracer(node(),[{file,{wrap,"trace"}}]).
{ok,[tiger@durin]}
(tiger@durin)2> ttb:p(...)
...
This will give a set of binary logs, like:
tiger@durin-trace.0.wrp
tiger@durin-trace.1.wrp
tiger@durin-trace.2.wrp
...
Format the whole set of logs:
1> ttb:format("tiger@durin-trace.*.wrp").
....
ok
2>
Format only the first log:
1> ttb:format("tiger@durin-trace.0.wrp").
....
ok
2>
To merge all wrap logs from two nodes:
1> ttb:format(["tiger@durin-trace.*.wrp","lion@durin-trace.*.wrp"]).
....
ok
2>
For detailed information about the Event Tracer, please turn
to the User's Guide and Reference Manuals for the
By giving the format handler
The
The rest of the filters will only show function calls and
function returns. All other trace message are discarded. To get
the most out of these filters,
The same result can be obtained by using the
1> dbg:fun2ms(fun(_) -> return_trace(),message(caller()) end).
[{'_',[],[{return_trace},{message,{caller}}]}]
This should however be done with care, since the
The
The
The
As an example this module is used, and the function
-module(bar).
-export([f1/0,f3/0]).
f1() ->
f2(),
ok.
f2() ->
spawn(?MODULE,f3,[]).
f3() ->
ok.
The
If the option
If the option
For the tracing functionality,
Use
The main purpose of the history buffer is the possibility to create configuration files. Any function stored in the history buffer can be written to a configuration file and used for creating a specific configuration at any time with one single function call.
A configuration file is created or extended with
You can write the complete content of the history buffer to a
config file by calling
User defined entries can also be written to a config file by
calling the function
Any existing file
See the content of the history buffer
ttb:tracer().
{ok,[tiger@durin]}
(tiger@durin)192> ttb:p(self(),[garbage_collection,call]).
{ok,{[<0.1244.0>],[garbage_collection,call]}}
(tiger@durin)193> ttb:tp(ets,new,2,[]).
{ok,[{matched,1}]}
(tiger@durin)194> ttb:list_history().
[{1,{ttb,tracer,[tiger@durin,[]]}},
{2,{ttb,p,[<0.1244.0>,[garbage_collection,call]]}},
{3,{ttb,tp,[ets,new,2,[]]}}] ]]>
Execute an entry from the history buffer:
ttb:ctp(ets,new,2).
{ok,[{matched,1}]}
(tiger@durin)196> ttb:list_history().
[{1,{ttb,tracer,[tiger@durin,[]]}},
{2,{ttb,p,[<0.1244.0>,[garbage_collection,call]]}},
{3,{ttb,tp,[ets,new,2,[]]}},
{4,{ttb,ctp,[ets,new,2]}}]
(tiger@durin)197> ttb:run_history(3).
ttb:tp(ets,new,2,[]) ->
{ok,[{matched,1}]} ]]>
Write the content of the history buffer to a configuration file:
ttb:write_config("myconfig",all).
ok
(tiger@durin)199> ttb:list_config("myconfig").
[{1,{ttb,tracer,[tiger@durin,[]]}},
{2,{ttb,p,[<0.1244.0>,[garbage_collection,call]]}},
{3,{ttb,tp,[ets,new,2,[]]}},
{4,{ttb,ctp,[ets,new,2]}},
{5,{ttb,tp,[ets,new,2,[]]}}] ]]>
Extend an existing configuration:
ttb:write_config("myconfig",[{ttb,tp,[ets,delete,1,[]]}],
[append]).
ok
(tiger@durin)201> ttb:list_config("myconfig").
[{1,{ttb,tracer,[tiger@durin,[]]}},
{2,{ttb,p,[<0.1244.0>,[garbage_collection,call]]}},
{3,{ttb,tp,[ets,new,2,[]]}},
{4,{ttb,ctp,[ets,new,2]}},
{5,{ttb,tp,[ets,new,2,[]]}},
{6,{ttb,tp,[ets,delete,1,[]]}}] ]]>
Go back to a previous configuration after stopping Trace Tool Builder:
ttb:stop().
ok
(tiger@durin)203> ttb:run_config("myconfig").
ttb:tracer(tiger@durin,[]) ->
{ok,[tiger@durin]}
ttb:p(<0.1244.0>,[garbage_collection,call]) ->
{ok,{[<0.1244.0>],[garbage_collection,call]}}
ttb:tp(ets,new,2,[]) ->
{ok,[{matched,1}]}
ttb:ctp(ets,new,2) ->
{ok,[{matched,1}]}
ttb:tp(ets,new,2,[]) ->
{ok,[{matched,1}]}
ttb:tp(ets,delete,1,[]) ->
{ok,[{matched,1}]}
ok ]]>
Write selected entries from the history buffer to a configuration file:
ttb:list_history().
[{1,{ttb,tracer,[tiger@durin,[]]}},
{2,{ttb,p,[<0.1244.0>,[garbage_collection,call]]}},
{3,{ttb,tp,[ets,new,2,[]]}},
{4,{ttb,ctp,[ets,new,2]}},
{5,{ttb,tp,[ets,new,2,[]]}},
{6,{ttb,tp,[ets,delete,1,[]]}}]
(tiger@durin)205> ttb:write_config("myconfig",[1,2,3,6]).
ok
(tiger@durin)206> ttb:list_config("myconfig").
[{1,{ttb,tracer,[tiger@durin,[]]}},
{2,{ttb,p,[<0.1244.0>,[garbage_collection,call]]}},
{3,{ttb,tp,[ets,new,2,[]]}},
{4,{ttb,tp,[ets,delete,1,[]]}}]
(tiger@durin)207> ]]>
To learn what sequential tracing is and how it can be used,
please turn to the reference manual for the
The support for sequential tracing provided by the Trace Tool Builder includes
Starting sequential tracing requires that a tracer has been
started with the
In the following example, the function
ttb:tracer().
{ok,[tiger@durin]}
(tiger@durin)111> ttb:p(self(),call).
{ok,{[<0.158.0>],[call]}}
(tiger@durin)112> ttb:tp(dbg,get_tracer,0,ttb:seq_trigger_ms(send)).
{ok,[{matched,1},{saved,1}]}
(tiger@durin)113> dbg:get_tracer(), seq_trace:reset_trace().
true
(tiger@durin)114> ttb:stop().
ok
(tiger@durin)115> ttb:format("tiger@durin-ttb").
({<0.158.0>,{shell,evaluator,3},tiger@durin}) call dbg:get_tracer()
SeqTrace [0]: ({<0.158.0>,{shell,evaluator,3},tiger@durin})
{<0.237.0>,dbg,tiger@durin} ! {<0.158.0>,{get_tracer,tiger@durin}}
[Serial: {0,1}]
SeqTrace [0]: ({<0.237.0>,dbg,tiger@durin})
{<0.158.0>,{shell,evaluator,3},tiger@durin} ! {dbg,{ok,#Port<0.222>}}
[Serial: {1,2}]
ok
(tiger@durin)116> ]]>
Starting sequential tracing with a trigger is actually more useful if the trigger function is not called directly from the shell, but rather implicitly within a larger system. When calling a function from the shell, it is simpler to start sequential tracing directly, e.g.
ttb:tracer().
{ok,[tiger@durin]}
(tiger@durin)117> seq_trace:set_token(send,true), dbg:get_tracer(),
seq_trace:reset_trace().
true
(tiger@durin)118> ttb:stop().
ok
(tiger@durin)119> ttb:format("tiger@durin-ttb").
SeqTrace [0]: ({<0.158.0>,{shell,evaluator,3},tiger@durin})
{<0.246.0>,dbg,tiger@durin} ! {<0.158.0>,{get_tracer,tiger@durin}}
[Serial: {0,1}]
SeqTrace [0]: ({<0.246.0>,dbg,tiger@durin})
{<0.158.0>,{shell,evaluator,3},tiger@durin} ! {dbg,{ok,#Port<0.229>}}
[Serial: {1,2}]
ok
(tiger@durin)120> ]]>
In both examples above, the
All functions in the
The module