查看源代码 跟踪工具构建器
简介
跟踪工具构建器是为单节点或分布式 Erlang 系统构建跟踪工具的基础。它要求被跟踪节点上存在 Runtime_Tools 应用程序。
以下是跟踪工具构建器的主要功能:
- 通过一个函数调用,开始在多个节点上的文件端口进行跟踪。
- 将更多信息写入跟踪信息文件,该文件在格式化期间读取。
- 通过维护历史缓冲区和处理配置文件来恢复以前的配置。
- 为顺序跟踪提供一些简单的支持。
- 格式化二进制跟踪日志并合并来自多个节点的日志。
跟踪工具构建器的目的是作为定制跟踪工具的基础,但也可以直接从 Erlang shell 使用(它可以模仿 dbg
的行为,同时仍然提供有用的附加功能,例如匹配规范快捷方式)。跟踪工具构建器仅允许使用文件端口跟踪器,因此要使用其他类型的跟踪客户端,最好直接使用 dbg
。
入门
模块 ttb
是跟踪工具构建器中所有函数的接口。
要开始使用,您最少需要做的是使用 ttb:tracer/0,1,2
启动跟踪器,并使用 ttb:p/2
在要跟踪的进程上设置所需的跟踪标志。
跟踪完成后,使用 ttb:stop/0,1
停止跟踪器,并使用 ttb:format/1,2
格式化跟踪日志(如果需要格式化)。
有用的函数
ttb:tracer/0,1,2
- 在每个要跟踪的节点上打开一个跟踪端口。默认情况下,跟踪消息写入远程节点上的二进制文件(二进制跟踪日志)。ttb:p/2
- 指定要跟踪的进程。在此调用中指定的跟踪标志指定在每个进程上跟踪的内容。如果希望在不同的进程上设置不同的跟踪标志,可以多次调用此函数。ttb:tp/2,3,4
或ttb:tpl/2,3,4
- 如果要跟踪函数调用(即,如果在任何进程上设置了跟踪标志call
),则还必须使用ttb:tp/2,3,4
或ttb:tpl/2,3,4
在所需的函数上设置跟踪模式。只有当函数具有跟踪模式时,才会被跟踪。跟踪模式通过使用匹配规范来指定如何跟踪函数。匹配规范在 ERTS 用户指南中描述。ttb:stop/0,1
- 停止所有节点上的跟踪,删除所有跟踪模式,并刷新跟踪端口缓冲区。ttb:format/1/2
- 将二进制跟踪日志转换为可读的内容。默认情况下,ttb
将每个跟踪消息呈现为一行文本,但您也可以编写自己的处理程序来对跟踪信息进行更复杂的解释。跟踪日志也可以使用应用程序事件跟踪器 (ET) 以图形方式呈现。如果在
ttb:stop/1
中指定了选项format
,则在停止ttb
时会自动进行格式化。
从 Erlang Shell 跟踪本地节点
以下小模块用于后续示例中
-module(m).
-export([f/0]).
f() ->
receive
From when is_pid(From) ->
Now = erlang:now(),
From ! {self(),Now}
end.
以下示例展示了从 Erlang shell 使用 ttb
的基本用法。启动跟踪器和格式化都使用默认选项(但是提供了自定义的获取目录)。这会在新创建的目录中生成一个名为 Node-ttb
的跟踪日志,其中 Node
是节点名称。默认处理程序会在 shell 中打印格式化的跟踪消息。
(tiger@durin)47> %% 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> %% indicating that the return value should be
(tiger@durin)50> %% traced. Refer to the reference_manual for
(tiger@durin)50> %% the full list of match spec shortcuts
(tiger@durin)50> %% available.
(tiger@durin)51> ttb:tp(erlang,now,return).
{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([return, {fetch_dir, "fetch"}]).
{stopped, "fetch"}
(tiger@durin)54>
(tiger@durin)54> %% Finally I format my trace log
(tiger@durin)54> ttb:format("fetch").
({<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
构建自己的工具
以下示例展示了一个用于“调试跟踪”的简单工具,即跟踪带有返回值的函数调用
-module(mydebug).
-export([start/0,trc/1,stop/0,format/1]).
-export([print/4]).
%% Include ms_transform.hrl so that I can use dbg:fun2ms/2 to
%% generate match specifications.
-include_lib("stdlib/include/ms_transform.hrl").
%%% -------------Tool API-------------
%%% ----------------------------------
%%% Star the "mydebug" tool
start() ->
%% The options specify that the binary log shall be named
%% <Node>-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
%% We want trace messages to be sorted upon format, which requires
%% timestamp flag. The flag is however enabled by default in ttb.
ttb:p(all,call).
%%% Set trace pattern on function(s)
trc(M) when is_atom(M) ->
trc({M,'_','_'});
trc({M,F}) when is_atom(M), is_atom(F) ->
trc({M,F,'_'});
trc({M,F,_A}=MFA) when is_atom(M), is_atom(F) ->
%% This match spec shortcut specifies that return values shall
%% be traced.
MatchSpec = dbg:fun2ms(fun(_) -> return_trace() end),
ttb:tpl(MFA,MatchSpec).
%%% Format a binary trace log
format(Dir) ->
ttb:format(Dir).
%%% Stop the "mydebug" tool
stop() ->
ttb:stop(return).
%%% --------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]).
为了区分使用此工具生成的跟踪日志与其他日志,在 tracer/2
中使用了选项 file
。因此,日志会被获取到名为 ttb_upload_debug_log-YYYYMMDD-HHMMSS
的目录中
通过在启动跟踪器时使用选项 handler
,有关如何格式化文件的信息会存储在跟踪信息文件 (.ti
) 中。这不是必需的,因为它可以在格式化时指定。但是,如果您想使用 ttb:stop/1
中的选项 format
自动格式化跟踪日志,则它会很有用。此外,您无需了解二进制日志的内容即可按照预期的方式对其进行格式化。如果在启动跟踪器和格式化时都指定了选项 handler
,则使用格式化时指定的选项。
跟踪标志 call
设置在所有进程上。这意味着,使用命令 trc/1
激活的任何函数都会在所有现有和新进程上进行跟踪。
针对远程节点运行跟踪工具构建器
Observer 应用程序可能并不总是在要跟踪的节点上可用(以下称为“被跟踪节点”)。但是,只要满足以下条件,仍然可以从另一个节点(以下称为“跟踪控制节点”)运行跟踪工具构建器
- Observer 应用程序在跟踪控制节点上可用。
- Runtime_Tools 应用程序在跟踪控制节点和被跟踪节点上都可用。
如果要针对远程节点使用跟踪工具构建器,强烈建议将跟踪控制节点作为隐藏节点启动。这样,它可以连接到被跟踪节点而不会被其“看到”,也就是说,如果在被跟踪节点上调用 nodes/0
BIF,则不会显示跟踪控制节点。要启动隐藏节点,请将选项 -hidden
添加到 erl
命令,例如
% erl -sname trace_control -hidden
无盘节点
如果被跟踪节点是无盘节点,则必须从具有磁盘访问权限的跟踪控制节点启动 ttb
,并且必须为函数 tracer/2
指定选项 file
,其值为 {local, File}
,例如
(trace_control@durin)1> ttb:tracer(mynode@diskless,
{file,{local,{wrap,"mytrace"}}}).
{ok,[mynode@diskless]}
更多跟踪选项
在设置跟踪时,还可以激活以下功能
- 时间约束跟踪
- 过载保护
- 自动恢复
dbg
模式
时间约束跟踪
有时,启用指定时间段的跟踪可能会有所帮助(例如,监视系统 24 小时或半秒钟)。可以使用选项 {timer, TimerSpec}
来完成此操作。如果 TimerSpec
的格式为 MSec
,则在使用 ttb:stop/0
后,跟踪会在 MSec
毫秒后停止。如果提供了更多选项(TimerSpec = {MSec, Opts}
),则会使用 Opts
作为参数调用 ttb:stop/1
。
计时器使用 ttb:p/2
启动,因此必须预先设置任何跟踪模式。ttb:start_trace/4
总是在调用 ttb:p/2
之前设置所有模式。
以下示例演示如何设置一个在 5 秒后自动停止并格式化的跟踪
(tiger@durin)1> ttb:start_trace([node()],
[{erlang, now,[]}],
{all, call},
[{timer, {5000, format}}]).
注意
由于网络和处理延迟,跟踪的时间段是近似的。
过载保护
在跟踪实时系统时,始终要特别注意不要因跟踪过于繁重而使节点过载。ttb
提供了选项 overload
来解决此问题。
{overload, MSec, Module, Function}
指示 ttb
后端(Runtime_Tools 应用程序的一部分)每 MSec
毫秒执行一次过载检查。如果检查(名为 Module:Function(check)
)返回 true
,则会在选定的节点上禁用跟踪。
在一个节点上激活的过载保护不会影响其他节点,这些节点上的跟踪会照常继续。ttb:stop/0
会从所有客户端获取数据,包括在激活过载保护之前收集的所有数据。
注意
一旦在其中一个被跟踪节点中激活过载保护,则不允许更改跟踪详细信息(使用
ttb:p/2
和ttb:tp/tpl...
)。这是为了避免节点之间的跟踪设置不一致。
与选项 overload
一起提供的 Module:Function
必须处理三个调用:init
、check
和 stop
。init
和 stop
允许检查所需的一些设置和拆卸。过载检查模块可以如下所示
-module(overload).
-export([check/1]).
check(init) ->
Pid = sophisticated_module:start(),
put(pid, Pid);
check(check) ->
get(pid) ! is_overloaded,
receive
Reply ->
Reply
after 5000 ->
true
end;
check(stop) ->
get(pid) ! stop.
注意
check
始终由同一进程调用,因此可以使用put
和get
。
自动恢复
节点可能会崩溃(可能是有缺陷的节点,因此会被跟踪)。使用 resume
在节点恢复时自动恢复该节点上的跟踪。当 Runtime_Tools
启动时,发生故障的节点会尝试重新连接到跟踪控制节点。这意味着 Runtime_Tools
必须包含在其他节点的启动链中(如果不是,您仍然可以通过手动启动 Runtime_Tools
来恢复跟踪,即通过 RPC 调用)。
为了不丢失故障节点在崩溃时存储的数据,控制节点会在重启跟踪之前尝试获取这些数据。此操作必须在允许的时间范围内完成,否则将被中止(默认值为 10 秒,但可以使用 {resume, MSec}
进行更改)。以这种方式获取的数据随后会与所有其他跟踪数据合并。
自动启动功能需要在被跟踪节点上存储更多数据。默认情况下,数据会自动存储到被跟踪节点当前工作目录 (cwd) 中名为“ttb_autostart.bin”的文件中。用户可以通过指定自己的模块来处理自动启动数据的存储和检索(runtime_tools
的 ttb_autostart_module
环境变量),从而更改此行为(即在无盘节点上)。有关 API 的信息,请参阅模块 ttb
。以下示例显示了默认处理程序
-module(ttb_autostart).
-export([read_config/0,
write_config/1,
delete_config/0]).
-define(AUTOSTART_FILENAME, "ttb_autostart.bin").
delete_config() ->
file:delete(?AUTOSTART_FILENAME).
read_config() ->
case file:read_file(?AUTOSTART_FILENAME) of
{ok, Data} -> {ok, binary_to_term(Data)};
Error -> Error
end.
write_config(Data) ->
file:write_file(?AUTOSTART_FILENAME, term_to_binary(Data)).
注意
请记住,文件跟踪端口默认情况下会缓冲数据。如果节点崩溃,跟踪消息不会刷新到二进制日志中。如果发生故障的风险较高,最好不时自动刷新缓冲区。将
{flush, MSec}
作为ttb:tracer/2
的选项传递,将每MSec
毫秒刷新所有缓冲区。
dbg 模式
选项 {shell, ShellType}
允许使 ttb
的操作类似于 dbg
。使用 {shell, true}
会在存储所有跟踪消息之前将其显示在 shell 中。{shell, only}
还会禁用消息存储(使该工具的行为与 dbg
完全相同)。这仅允许与 IP 跟踪端口一起使用({trace, {local, File}}
)。
命令 ttb:tracer(dbg)
是纯 dbg
模式的快捷方式({shell, only}
)。
跟踪信息和文件 .ti
除了跟踪日志文件之外,当启动 Trace Tool Builder 时,还会创建一个扩展名为 .ti
的文件。这是跟踪信息文件。它是一个二进制文件,其中包含进程信息、使用的跟踪标志、它所属的节点名称以及使用函数 ttb:write_trace_info/2
写入的所有信息。当停止跟踪时,.ti
文件始终与其他日志一起获取。
除了进程信息之外,跟踪信息文件中的所有内容都会在格式化时传递给处理函数。参数 TI
是 {Key,ValueList}
元组的列表。键 flags
、handler
、file
和 node
用于 ttb
直接写入的信息。
可以通过调用 ttb:write_trace_info/2
将信息添加到跟踪信息文件中。请注意,ValueList
始终是一个列表,如果您多次使用相同的 Key
调用 write_trace_info/2
,则每次都会使用新值扩展 ValueList
。
示例
ttb:write_trace_info(mykey,1)
在 TI
中给出条目 {mykey,[1]}
。另一次调用 ttb:write_trace_info(mykey,2)
,将此条目更改为 {mykey,[1,2]}
。
包装日志
如果要限制跟踪日志的大小,可以使用包装日志。它的工作方式几乎类似于循环缓冲区。您可以指定二进制日志的最大数量和每个日志的最大大小。ttb
然后会在每次日志达到最大大小时创建一个新的二进制日志。当达到最大日志数时,将删除最旧的日志,然后再创建一个新日志。
注意
ttb
生成的数据的总大小可能大于包装规范所建议的大小。如果被跟踪节点重新启动并且启用了自动恢复,则始终会存储旧的包装日志并创建一个新的日志。
包装日志可以一个一个地格式化,也可以一次全部格式化。请参阅格式化。
格式化
格式化可以在停止 ttb
时自动完成(请参阅自动收集和格式化来自所有节点的日志部分),也可以通过调用函数 ttb:format/1,2
显式完成。
格式化是指读取二进制日志并以可读格式呈现它。您可以使用 ttb
中的默认格式处理程序将每个跟踪消息呈现为一行文本,或者编写自己的处理程序来对跟踪信息进行更复杂的解释。您还可以使用应用程序 ET 以图形方式呈现跟踪日志(请参阅使用事件跟踪器呈现跟踪日志部分)。
ttb:format/1,2
的第一个参数指定要格式化哪些二进制日志。这通常是 ttb
在日志获取期间创建的目录的名称。除非提供了选项 disable_sort
,否则来自不同文件的日志始终会根据跟踪中的时间戳进行排序。
ttb:format/2
的第二个参数是一个选项列表,如下所示
out
- 指定写入格式化文本的目标位置。默认目标位置是standard_io
,但也可以指定文件名。handler
- 指定要使用的格式处理程序。如果未指定此选项,则使用启动跟踪器时指定的选项handler
。如果在启动跟踪器时也未指定选项handler
,则使用默认处理程序,该处理程序将每个跟踪消息打印为文本行。disable_sort
- 指示不按时间戳合并日志,而是按顺序处理一个文件接一个文件(这可能会快一点)。
格式处理程序是一个接受四个参数的 fun。对于二进制日志中的每个跟踪消息都会调用此 fun。一个仅打印每个跟踪消息的简单示例如下
fun(Fd, Trace, _TraceInfo, State) ->
io:format(Fd, "Trace: ~p~n", [Trace]),
State
end.
在此,Fd
是目标文件的文件描述符,或原子 standard_io
。_TraceInfo
包含来自跟踪信息文件的信息(请参阅跟踪信息和文件 .ti部分)。State
是格式处理程序 fun 的状态变量。变量 State
的初始值在处理程序选项中指定,例如
ttb:format("tiger@durin-ttb", [{handler, {{Mod,Fun}, initial_state}}])
^^^^^^^^^^^^^
可以使用另一个格式处理程序来计算垃圾回收器花费的时间
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
此格式处理程序的更精细版本是 Observer 应用程序的目录 src
中包含的模块 multitrace.erl
中的函数 handle_gc/4
。
跟踪消息作为第二个参数 (Trace
) 传递。Trace
的可能值如下
erlang:trace/3
中描述的所有跟踪消息- 如果使用 IP 跟踪器,则为
{drop, N}
(请参阅dbg:trace_port/2
) - 当处理完所有跟踪消息时,会收到一次
end_of_trace
通过给格式处理程序 ttb:get_et_handler()
,您可以使用 ET 应用程序中的 et_viewer
以图形方式呈现跟踪日志(请参阅使用事件跟踪器呈现跟踪日志部分)。
您始终可以决定不格式化获取目录中包含的整个跟踪数据,而是分析单个文件。为此,必须将单个文件(或文件列表)作为第一个参数传递给 format/1,2
。
包装日志可以一个一个地格式化,也可以一次全部格式化。要格式化一组包装日志中的一个,请指定确切的文件名。要格式化整组包装日志,请指定名称,其中使用 *
代替包装计数。
示例
开始跟踪
(tiger@durin)1> ttb:tracer(node(),{file,{wrap,"trace"}}).
{ok,[tiger@durin]}
(tiger@durin)2> ttb:p(...)
...
这将产生一组二进制日志,例如
[email protected]
[email protected]
[email protected]
...
格式化整组日志
1> ttb:format("tiger@durin-trace.*.wrp").
....
ok
2>
仅格式化第一个日志
1> ttb:format("[email protected]").
....
ok
2>
要合并来自两个节点的所有包装日志
1> ttb:format(["tiger@durin-trace.*.wrp","lion@durin-trace.*.wrp"]).
....
ok
2>
使用事件跟踪器呈现跟踪日志
有关事件跟踪器的详细信息,请参阅ET 应用程序。
通过给格式处理程序 ttb:get_et_handler()
,您可以使用 ET 应用程序中的 et_viewer
以图形方式呈现跟踪日志。ttb
提供了可以从 et_viewer
窗口中的 Filter 菜单中选择的过滤器。这些过滤器的名称根据它们呈现的参与者类型(即序列图中每条垂直线代表的内容)命名。参与者之间的交互显示为两个垂直线之间的红色箭头,参与者内的活动显示为参与者线右侧的蓝色文本。
processes
过滤器是唯一显示来自跟踪日志的所有跟踪消息的过滤器。序列图中的每条垂直线代表一个进程。Erlang 消息、spawn 和 link/unlink 是进程之间典型的交互。函数调用、调度和垃圾回收是进程中典型的活动。processes
是默认过滤器。
其余过滤器仅显示函数调用和函数返回。所有其他跟踪消息都将被丢弃。要充分利用这些过滤器,et_viewer
必须知道每个函数的调用者和返回时间。这可以使用跟踪时的 call
和 return_to
标志来获得。请注意,标志 return_to
仅适用于本地调用跟踪,即当使用 ttb:tpl
设置跟踪模式时。
仅使用 call
标志,并通过在本地或全局函数调用上设置匹配规范,也可以获得相同的结果,如下所示:
1> dbg:fun2ms(fun(_) -> return_trace(),message(caller()) end).
[{'_',[],[{return_trace},{message,{caller}}]}]
但必须谨慎操作,因为匹配规范中的函数 {return_trace}
会破坏尾递归。
modules
过滤器在序列图中将每个模块显示为一条垂直线。外部函数调用/返回显示为模块之间的交互,而内部函数调用/返回显示为模块内的活动。
functions
过滤器在序列图中将每个函数显示为一条垂直线。一个函数调用自身显示为函数内的活动,而所有其他函数调用显示为函数之间的交互。
mods_and_procs
和 funcs_and_procs
过滤器分别等效于 modules
和 functions
过滤器,但每个模块或函数可以有多条垂直线,每个进程上都有一条。
在以下示例中,使用了模块 foo
和 bar
-module(foo).
-export([start/0,go/0]).
start() ->
spawn(?MODULE, go, []).
go() ->
receive
stop ->
ok;
go ->
bar:f1(),
go()
end.
-module(bar).
-export([f1/0,f3/0]).
f1() ->
f2(),
ok.
f2() ->
spawn(?MODULE,f3,[]).
f3() ->
ok.
设置跟踪
(tiger@durin)1> %%First we retrieve the Pid to limit traced processes set
(tiger@durin)1> Pid = foo:start().
(tiger@durin)2> %%Now we set up tracing
(tiger@durin)2> ttb:tracer().
(tiger@durin)3> ttb:p(Pid, [call, return_to, procs, set_on_spawn]).
(tiger@durin)4> ttb:tpl(bar, []).
(tiger@durin)5> %%Invoke our test function and see output with et viewer
(tiger@durin)5> Pid ! go.
(tiger@durin)6> ttb:stop({format, {handler, ttb:get_et_handler()}}).
这将呈现类似于以下的结果
请注意,函数 ttb:start_trace/4
可以如下使用以提供帮助
(tiger@durin)1> Pid = foo:start().
(tiger@durin)2> ttb:start_trace([node()],
[{bar,[]}],
{Pid, [call, return_to, procs, set_on_spawn]}
{handler, ttb:get_et_handler()}).
(tiger@durin)3> Pid ! go.
(tiger@durin)4> ttb:stop(format).
自动收集和格式化来自所有节点的日志
默认情况下,ttb:stop/1
从所有节点获取跟踪日志和跟踪信息文件。日志存储在跟踪控制节点工作目录下的名为 ttb_upload-Filename-Timestamp
的新目录中。可以通过向 ttb:stop/1
提供选项 nofetch
来禁用获取。用户可以通过传递选项 {fetch_dir, Dir}
来指定获取目录。
如果向 ttb:stop/1
指定了选项 format
,则在停止跟踪后会自动格式化跟踪日志。
历史记录和配置文件
对于跟踪功能,可以使用 dbg
而不是 ttb
来设置进程的跟踪标志和调用跟踪的跟踪模式,即函数 p
、tp
、tpl
、ctp
、ctpl
和 ctpg
。 ttb
为这些函数仅添加了以下两项内容:
- 所有调用都存储在历史缓冲区中,可以被调用和存储在配置文件中。这样可以轻松设置相同的跟踪环境,例如,如果您想比较两次测试运行。当从 Erlang shell 使用
ttb
时,还可以减少输入量。 - 为最常见的匹配规范提供了快捷方式(为了不强制您持续使用
dbg:fun2ms
)。
使用 ttb:list_history/0
查看历史缓冲区的内容,使用 ttb:run_history/1
重新执行其中一个条目。
历史缓冲区的主要目的是创建配置文件的可能性。存储在历史缓冲区中的任何函数都可以写入配置文件,并使用单个函数调用随时创建特定配置。
使用 ttb:write_config/2,3
创建或扩展配置文件。配置文件是二进制文件,因此只能使用 ttb
提供的函数读取和写入。
通过调用 ttb:write_config(ConfigFile,all)
,可以将历史缓冲区的完整内容写入配置文件。通过调用 ttb:write_config(ConfigFile,NumList)
,可以写入历史记录中选定的条目,其中 NumList
是指向要写入的历史记录条目的整数列表。此外,当调用 ttb:stop/0,1
时,历史缓冲区始终转储到 ttb_last_config
。
通过调用函数 ttb:write_config(ConfigFile,ConfigList)
,用户定义的条目也可以写入配置文件,其中 ConfigList
是 {Module,Function,Args}
列表。
当调用 write_config/2
时,将删除任何现有文件 ConfigFile
并创建一个新文件。选项 append
可用于在现有配置文件的末尾添加内容,例如,ttb:write_config(ConfigFile,What,[append])
。
示例
查看历史缓冲区的内容
(tiger@durin)191> 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,[]]}}]
执行历史缓冲区中的一个条目
(tiger@durin)195> 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}]}
将历史缓冲区的内容写入配置文件
(tiger@durin)198> 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,[]]}}]
扩展现有配置
(tiger@durin)200> 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,[]]}}]
停止 Trace Tool Builder 后返回到之前的配置
(tiger@durin)202> 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
将历史缓冲区中的选定条目写入配置文件
(tiger@durin)204> 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>
顺序跟踪
要了解什么是顺序跟踪以及如何使用它,请参阅 seq_trace
的参考手册。
Trace Tool Builder 提供的顺序跟踪支持包括以下内容:
- 系统跟踪器的启动。当使用
ttb:tracer/0,1,2
启动跟踪端口时,会自动执行此操作。 - 创建激活顺序跟踪的匹配规范。
启动顺序跟踪需要使用函数 ttb:tracer/0,1,2
启动跟踪器。然后可以通过以下任一方式启动顺序跟踪:
- 通过使用
ttb:seq_trigger_ms/0,1
创建的带有匹配规范的触发函数。 - 直接使用模块
seq_trace
。
示例 1
在以下示例中,函数 dbg:get_tracer/0
用作顺序跟踪的触发器
(tiger@durin)110> 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(format).
({<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>
示例 2
如果触发函数不是直接从 shell 调用,而是在更大的系统中隐式调用,则使用触发器启动顺序跟踪会更有用。当从 shell 调用函数时,直接启动顺序跟踪会更简单,例如,如下所示:
(tiger@durin)116> 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(format).
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>
在前面的两个示例中,seq_trace:reset_trace/0
在跟踪的函数之后立即重置跟踪令牌,以避免由于 Erlang shell 中的打印输出而产生许多跟踪消息。
在使用 ttb:tracer/0,1,2
启动跟踪端口后,可以使用模块 seq_trace
中的所有函数,除了 set_system_tracer/1
。
多用途跟踪工具
Observer 应用程序的目录 src
中的模块 multitrace
提供了一个小工具,具有三种可能的跟踪设置。跟踪消息被写入二进制文件,可以使用函数 multitrace:format/1,2
进行格式化。
multitrace:debug(What)
- 在所有进程上启动调用跟踪,并跟踪指定的函数。使用的格式处理程序是multitrace:handle_debug/4
,它会打印每次调用和返回。What
必须是要跟踪的一个项或一个项列表,指定格式为{Module,Function,Arity}
、{Module,Function}
或仅Module
。multitrace:gc(Procs)
- 跟踪指定进程上的垃圾回收。使用的格式处理程序是multitrace:handle_gc/4
,它会打印每次垃圾回收的开始、停止和所花费的时间。multitrace:schedule(Procs)
- 跟踪指定进程上的进程调度和取消调度。使用的格式处理程序是multitrace:handle_schedule/4
,它会打印每次进程调度和取消调度,包括进程、时间戳和当前函数。它还会打印每个跟踪进程被调度的总时间。