查看源代码 跟踪工具构建器

简介

跟踪工具构建器是为单节点或分布式 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,4ttb:tpl/2,3,4 - 如果要跟踪函数调用(即,如果在任何进程上设置了跟踪标志 call),则还必须使用 ttb:tp/2,3,4ttb: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/2ttb:tp/tpl...)。这是为了避免节点之间的跟踪设置不一致。

与选项 overload 一起提供的 Module:Function 必须处理三个调用:initcheckstopinitstop 允许检查所需的一些设置和拆卸。过载检查模块可以如下所示

-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 始终由同一进程调用,因此可以使用 putget

自动恢复

节点可能会崩溃(可能是有缺陷的节点,因此会被跟踪)。使用 resume 在节点恢复时自动恢复该节点上的跟踪。当 Runtime_Tools 启动时,发生故障的节点会尝试重新连接到跟踪控制节点。这意味着 Runtime_Tools 必须包含在其他节点的启动链中(如果不是,您仍然可以通过手动启动 Runtime_Tools 来恢复跟踪,即通过 RPC 调用)。

为了不丢失故障节点在崩溃时存储的数据,控制节点会在重启跟踪之前尝试获取这些数据。此操作必须在允许的时间范围内完成,否则将被中止(默认值为 10 秒,但可以使用 {resume, MSec} 进行更改)。以这种方式获取的数据随后会与所有其他跟踪数据合并。

自动启动功能需要在被跟踪节点上存储更多数据。默认情况下,数据会自动存储到被跟踪节点当前工作目录 (cwd) 中名为“ttb_autostart.bin”的文件中。用户可以通过指定自己的模块来处理自动启动数据的存储和检索(runtime_toolsttb_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} 元组的列表。键 flagshandlerfilenode 用于 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 必须知道每个函数的调用者和返回时间。这可以使用跟踪时的 callreturn_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_procsfuncs_and_procs 过滤器分别等效于 modulesfunctions 过滤器,但每个模块或函数可以有多条垂直线,每个进程上都有一条。

在以下示例中,使用了模块 foobar

-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()}}).

这将呈现类似于以下的结果

Filter: "processes"

Filter: "mods_and_procs"

请注意,函数 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 来设置进程的跟踪标志和调用跟踪的跟踪模式,即函数 ptptplctpctplctpgttb 为这些函数仅添加了以下两项内容:

  • 所有调用都存储在历史缓冲区中,可以被调用和存储在配置文件中。这样可以轻松设置相同的跟踪环境,例如,如果您想比较两次测试运行。当从 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 启动跟踪器。然后可以通过以下任一方式启动顺序跟踪:

示例 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,它会打印每次进程调度和取消调度,包括进程、时间戳和当前函数。它还会打印每个跟踪进程被调度的总时间。