查看源码 dbg (runtime_tools v2.1.1)
基于文本的跟踪工具
该模块实现了 trace:process/4
、trace:port/4
和 trace:function/4
BIF 的基于文本的接口,简化了对函数、进程、端口和消息的跟踪。
要快速开始跟踪函数调用,可以在 Erlang shell 中使用以下代码
1> dbg:tracer(). % Start the default trace message receiver
{ok,<0.90.0>}
2> dbg:p(all, c). % Set upp call tracing on all processes
{ok,[{matched,nonode@nohost,49}]}
3> dbg:tp(lists, seq, cx). % Set up call and exception tracing on lists:seq/2,3
{ok,[{matched,nonode@nohost,2},{saved,cx}]}
4> lists:seq(1, 10).
(<0.88.0>) call lists:seq(1,10) ({erl_eval,do_apply,7,{"erl_eval.erl",904}})
[1,2,3,4,5,6,7,8,9,10]
(<0.88.0>) returned from lists:seq/2 -> [1,2,3,4,5,6,7,8,9,10]
这些实用程序也适用于在大型系统上进行系统测试,在这些系统中,其他工具对系统性能的影响过于严重。还包括一些对顺序跟踪的原始支持;请参阅高级主题部分。
从 shell 进行简单的跟踪,无需事先设置
要以最少的麻烦跟踪对函数的调用,请调用 dbg:c(Module, Name, Arguments)
。dbg:c/3
启动一个临时的跟踪接收器,启用所有跟踪标志,并从临时进程调用指定的函数。例如,以下是如何跟踪对 application:which_applications/0
的调用
1> dbg:c(application, which_applications, []).
(<0.92.0>) <0.45.0> ! {'$gen_call',{<0.92.0>,
[alias|
#Ref<0.0.11779.270031856.1478295555.230456>]},
which_applications} (Timestamp: {1710,
847802,
479222})
(<0.92.0>) out {gen,do_call,4} (Timestamp: {1710,847802,479231})
(<0.92.0>) in {gen,do_call,4} (Timestamp: {1710,847802,479271})
(<0.92.0>) << {[alias|#Ref<0.0.11779.270031856.1478295555.230456>],
[{stdlib,"ERTS CXC 138 10","5.2.1"},
{kernel,"ERTS CXC 138 10","9.2.2"}]} (Timestamp: {1710,
847802,
479274})
[{stdlib,"ERTS CXC 138 10","5.2.1"},
{kernel,"ERTS CXC 138 10","9.2.2"}]
生成四个跟踪事件
- 一个发送事件 (
!
),表示从当前进程向application_controller
进程发送请求。 - 当当前进程在
receive
中等待回复到达时,发生一个调度出事件 (out
)。 - 当回复到达时,当前进程被调度进来时,发生一个调度入事件 (
in
)。 - 当当前进程从
application_controller
进程检索回复时,会发生一个receive
事件 (<<
)。
dbg:c/4
函数有一个用于指定跟踪标志的第四个参数。以下是如何仅显示消息发送和接收
2> dbg:c(application, which_applications, [], m).
(<0.96.0>) <0.45.0> ! {'$gen_call',{<0.96.0>,
[alias|
#Ref<0.0.12291.270031856.1478295555.230496>]},
which_applications}
(<0.96.0>) << {[alias|#Ref<0.0.12291.270031856.1478295555.230496>],
[{stdlib,"ERTS CXC 138 10","5.2.1"},
{kernel,"ERTS CXC 138 10","9.2.2"}]}
[{stdlib,"ERTS CXC 138 10","5.2.1"},
{kernel,"ERTS CXC 138 10","9.2.2"}]
从 shell 进行跟踪
从 shell 进行跟踪的另一种方法是显式启动一个跟踪器并在您要跟踪的进程上设置您选择的跟踪标志。例如,以下是如何跟踪消息和进程事件
1> Pid = spawn(fun() -> receive {From,Msg} -> From ! Msg end end).
<0.90.0>
2> dbg:tracer().
{ok,<0.92.0>}
3> dbg:p(Pid, [m,procs]).
{ok,[{matched,nonode@nohost,1}]}
4> Pid ! {self(),hello}.
(<0.90.0>) << {<0.88.0>,hello}
{<0.88.0>,hello}
(<0.90.0>) <0.88.0> ! hello
(<0.90.0>) exit normal
5> flush().
Shell got hello
ok
为了跟踪函数调用,除了为进程启用 call
跟踪标志外,还需要为要跟踪的函数设置跟踪模式。
示例
1> dbg:tracer().
{ok,<0.90.0>}
2> dbg:p(all, call).
{ok,[{matched,nonode@nohost,49}]}
3> dbg:tp(lists, last, 1, []).
{ok,[{matched,nonode@nohost,1}]}
4> lists:last([a,b,c,d,e]).
(<0.88.0>) call lists:last([a,b,c,d,e])
e
5> dbg:tp(lists, last, 1, [{'_',[],[{return_trace}]}]).
{ok,[{matched,nonode@nohost,1},{saved,1}]}
6> lists:last([a,b,c,d,e]).
(<0.88.0>) call lists:last([a,b,c,d,e])
(<0.88.0>) returned from lists:last/1 -> e
e
高级主题 - 与 seq_trace 结合使用
dbg
模块主要针对通过 trace:process/4
函数进行跟踪。有时希望以更精细的方式跟踪消息,这可以在 seq_trace
模块的帮助下完成。
seq_trace
实现顺序跟踪(在 AXE10 世界中称为“forlopp 跟踪”)。dbg
可以解释从 seq_trace
生成的消息,并且可以使用相同类型的跟踪的跟踪器函数。seq_trace
消息也可以发送到跟踪端口以进行进一步分析。
由于匹配规范可以启用顺序跟踪,因此 dbg
和 seq_trace
的组合可能非常强大。这个简短的示例显示了一个会话,其中使用顺序跟踪来跟踪 dbg
模块和跟踪本身
1> dbg:tracer().
{ok,<0.30.0>}
2> {ok, Tracer} = dbg:get_tracer().
{ok,<0.31.0>}
3> seq_trace:set_system_tracer(Tracer).
false
4> dbg:tp(dbg, get_tracer, 0, [{[],[],[{set_seq_token, send, true}]}]).
{ok,[{matched,nonode@nohost,1},{saved,1}]}
5> dbg:p(all,call).
{ok,[{matched,nonode@nohost,22}]}
6> dbg:get_tracer(), seq_trace:set_token([]).
(<0.25.0>) call dbg:get_tracer()
SeqTrace [0]: (<0.25.0>) <0.30.0> ! {<0.25.0>,get_tracer} [Serial: {2,4}]
SeqTrace [0]: (<0.30.0>) <0.25.0> ! {dbg,{ok,<0.31.0>}} [Serial: {4,5}]
{1,0,5,<0.30.0>,4}
此会话将 system_tracer 设置为与普通跟踪器进程相同的进程(即 <0.31.0>),并将函数 dbg:get_tracer
的跟踪模式设置为具有设置顺序令牌的操作的模式。当跟踪进程调用该函数时(在本例中跟踪所有进程),该进程会被令牌“污染”,并且 seq_trace
消息会同时发送给服务器请求和响应。seq_trace:set_token([])
在调用后会清除 seq_trace
令牌,这就是为什么当答案通过 shell 传播到控制台端口时不会发送任何消息的原因。否则输出会更嘈杂。
注意
在组长进程(I/O 进程)上跟踪函数调用时,存在导致死锁的风险。如果组长进程生成一个跟踪消息,并且跟踪器进程通过调用跟踪处理函数向同一组长发送 I/O 请求,则会发生这种情况。仅当跟踪处理程序使用 io
函数(如 format/2
)打印到 tty 时,才会出现此问题。请注意,当调用 dbg:p(all, call)
时,也会跟踪 IO 进程。这是一个例子
%% Using a default line editing shell
1> dbg:tracer(process, {fun(Msg,_) -> io:format("~p~n", [Msg]), 0 end, 0}).
{ok,<0.37.0>}
2> dbg:p(all, [call]).
{ok,[{matched,nonode@nohost,25}]}
3> dbg:tp(mymod,[{'_',[],[]}]).
{ok,[{matched,nonode@nohost,0},{saved,1}]}
4> mymod: % TAB pressed here
%% -- Deadlock --
这是另一个例子
%% Using a shell without line editing (oldshell)
1> dbg:tracer(process).
{ok,<0.31.0>}
2> dbg:p(all, [call]).
{ok,[{matched,nonode@nohost,25}]}
3> dbg:tp(lists,[{'_',[],[]}]).
{ok,[{matched,nonode@nohost,0},{saved,1}]}
% -- Deadlock --
我们在第一个示例中发生死锁的原因是,当按下 TAB 键展开函数名时,组长(处理字符输入)会调用 mymod:module_info()
。这将生成一个跟踪消息,从而导致跟踪器进程通过调用 io:format/2
向组长发送 I/O 请求。我们最终陷入死锁。
在第二个示例中,我们使用默认的跟踪处理程序函数。此处理程序通过向 user
进程发送 IO 请求来打印到 tty。当 Erlang 在 oldshell 模式下启动时,shell 进程将以 user
作为其组长,并且在本示例中跟踪器进程也会以 user
作为其组长。由于 user
调用 lists
中的函数,因此一旦发送第一个 IO 请求,我们就会陷入死锁。
以下是避免死锁的一些建议
- 不要跟踪跟踪器进程的组长。如果已为所有进程打开跟踪,请调用
dbg:p(TracerGLPid, clear)
以停止跟踪组长 (TracerGLPid
)。process_info(TracerPid, group_leader)
会告诉您这是哪个进程 (TracerPid
是从dbg:get_tracer/0
返回的)。 - 如果使用默认的跟踪处理程序函数,则不要跟踪
user
进程。 - 在您自己的跟踪处理程序函数中,调用
erlang:display/1
而不是io
函数,或者,如果user
不用作组长,则打印到user
而不是默认的组长。示例:io:format(user, Str, Args)
。
摘要
函数
使用设置的 Flags
中的跟踪标志评估表达式 apply(Mod, Fun, Args)
。
从已跟踪节点的列表中清除节点。
禁用由 ModuleOrMFA
指定的一个或多个函数的调用跟踪。
清除指定跟踪事件(send
或 'receive'
)的匹配规范,恢复为跟踪所有触发事件的默认值。
禁用由 ModuleOrMFA
指定的一个或多个函数的全局调用跟踪。
禁用由 ModuleOrMFA
指定的一个或多个函数的局部调用跟踪。
伪函数,通过解析转换将函数调用中键入为参数的文字 fun 转换为匹配规范。
返回所有跟踪消息发送到的进程、端口或跟踪器模块。
提供简短在线帮助的项列表。
提供 dbg
模块中函数的简短帮助文本。
显示有关所有已跟踪进程和端口的信息。
在控制台上显示已跟踪节点的列表。
列出之前在会话中使用的所有匹配规范。
将远程节点(节点名
)添加到执行跟踪的节点列表中。
等同于 p(项目, [m])
。
根据 标志
指定的值跟踪 项目
。
使用提供的会话运行 dbg
命令,如果提供会话名称,则在调用期间创建会话。
使用给定的 名称
创建一个新的 dbg
会话。
销毁一个 dbg session/0
。
停止 dbg
服务器,清除所有进程的所有跟踪标志,清除所有函数的跟踪模式,清除发送/接收的跟踪模式,关闭所有跟踪客户端,并关闭所有跟踪端口。
关闭先前启动的跟踪客户端。
为一个或多个由 模块或MFA
指定的导出函数启用调用跟踪。
将匹配规范与跟踪事件 send
或 'receive'
相关联。
为一个或多个由 模块或MFA
指定的函数启用调用跟踪。
启动一个跟踪客户端,该客户端读取由跟踪端口驱动程序创建的输出(请参阅 trace_port/2
),并以与 tracer/0
函数创建的跟踪器进程大致相同的方式处理它。
此函数的工作方式与 trace_client/2
完全相同,但允许您编写自己的处理程序函数。
创建一个生成跟踪端口的 fun,它适合作为 tracer/2
的第二个参数。
此函数用于对给定节点(节点名
)上的活动跟踪端口驱动程序执行控制操作。
在本地节点上启动一个服务器,该服务器将接收所有跟踪消息。
在本地节点上启动具有附加参数的跟踪器服务器。
此函数等同于 tracer/2
,但在给定节点上运行。
类型
-type built_in_alias() :: x | c | cx.
-type match_desc() :: [match_info()].
-type match_spec() :: [{match_pattern(), [_], [_]}].
-opaque session()
-type tp_arity() :: arity() | '_'.
-type tp_function() :: atom() | '_'.
-type tp_id() :: pos_integer().
-type tp_match_spec() :: tp_id() | built_in_alias() | [] | match_spec().
-type tp_module() :: module() | '_'.
-type trace_wrap_file_size() :: non_neg_integer() | {time, WrapTime :: pos_integer()}.
-type trace_wrap_files_spec() :: {file:name_all(), wrap, Suffix :: string()} | {file:name_all(), wrap, Suffix :: string(), WrapSize :: trace_wrap_file_size()} | {file:name_all(), wrap, Suffix :: string(), WrapSize :: trace_wrap_file_size(), WrapCnt :: pos_integer()}.
函数
使用设置的 Flags
中的跟踪标志评估表达式 apply(Mod, Fun, Args)
。
c
代表 call(调用)。
这是从 Erlang shell 跟踪进程的便捷方法。
-spec cn(Nodename) -> ok when Nodename :: node().
从已跟踪节点的列表中清除节点。
cn
代表 clear node(清除节点)。
后续对 tp/2
和 p/2
的调用将不会考虑该节点,但已在该节点上激活的跟踪将继续生效。
返回 ok
。此调用不会失败。
-spec ctp() -> {ok, MatchDesc :: match_desc()} | {error, term()}.
等效于 ctp({'_', '_', '_'})
。
-spec ctp(Module | {Module, Function, Arity}) -> {ok, MatchDesc :: match_desc()} | {error, term()} when Module :: tp_module(), Function :: tp_function(), Arity :: tp_arity().
禁用由 ModuleOrMFA
指定的一个或多个函数的调用跟踪。
如果 模块或MFA
是原子(模块名称),则此函数调用等同于 ctp({模块或MFA, '_', '_'})
。
否则,模块或MFA
应该为 {模块, 函数, 参数个数}
。
ctp
代表 clear trace pattern(清除跟踪模式)。
模块或MFA
的语义与 tp/2
或 tpl/2
中相应的函数规范相同。本地和全局调用跟踪均被禁用。
返回值反映了匹配的函数数量,并按照 tp/2
中的描述构建,但不会返回 {saved, N}
元组。
-spec ctp(Module :: tp_module(), Function :: tp_function()) -> {ok, MatchDesc :: match_desc()} | {error, term()}.
-spec ctp(Module :: tp_module(), Function :: tp_function(), Arity :: tp_arity()) -> {ok, MatchDesc :: match_desc()} | {error, term()}.
-spec ctpe(Event) -> {ok, MatchDesc} | {error, term()} when Event :: send | 'receive', MatchDesc :: [MatchNum], MatchNum :: {matched, node(), 1} | {matched, node(), 0, RPCError :: term()}.
清除指定跟踪事件(send
或 'receive'
)的匹配规范,恢复为跟踪所有触发事件的默认值。
ctpe
代表 clear trace pattern event(清除跟踪模式事件)。
-spec ctpg() -> {ok, MatchDesc :: match_desc()} | {error, term()}.
-spec ctpg(Module | {Module, Function :: tp_function(), Arity :: tp_arity()}) -> {ok, MatchDesc :: term()} | {error, term()} when Module :: tp_module().
禁用由 ModuleOrMFA
指定的一个或多个函数的全局调用跟踪。
如果 模块或MFA
是原子(模块名称),则此函数调用等同于 ctpg({模块或MFA, '_', '_'})
。
否则,模块或MFA
应该为 {模块, 函数, 参数个数}
。
ctpg
代表 clear trace pattern global(清除跟踪模式全局)。
-spec ctpg(Module :: tp_module(), Function :: tp_function()) -> {ok, MatchDesc :: match_desc()} | {error, term()}.
-spec ctpg(Module :: tp_module(), Function :: tp_function(), Arity :: tp_arity()) -> {ok, MatchDesc :: match_desc()} | {error, term()}.
-spec ctpl() -> {ok, MatchDesc :: match_desc()} | {error, term()}.
-spec ctpl(Module | {Module, Function :: tp_function(), Arity :: tp_arity()}) -> {ok, MatchDesc :: term()} | {error, term()} when Module :: tp_module().
禁用由 ModuleOrMFA
指定的一个或多个函数的局部调用跟踪。
如果 模块或MFA
是原子(模块名称),则此函数调用等同于 ctpl({模块或MFA, '_', '_'})
。
否则,模块或MFA
应该为 {模块, 函数, 参数个数}
。
ctpl
代表 clear trace pattern local(清除跟踪模式本地)。
-spec ctpl(Module :: tp_module(), Function :: tp_function()) -> {ok, MatchDesc :: match_desc()} | {error, term()}.
-spec ctpl(Module :: tp_module(), Function :: tp_function(), Arity :: tp_arity()) -> {ok, MatchDesc :: match_desc()} | {error, term()}.
-spec dtp() -> ok.
忘记在调用 tp/2
期间保存的所有匹配规范。
dtp
代表 delete trace patterns(删除跟踪模式)。
在从使用 rtp/1
的文件恢复其他匹配规范之前,删除所有已保存的匹配规范很有用。使用 dtp/1
删除特定的已保存匹配规范。
-spec dtp(N) -> ok when N :: tp_id().
忘记在调用 tp/2
期间保存的特定匹配规范。
dtp
代表 delete trace pattern(删除跟踪模式)。
-spec flush_trace_port() -> term().
-spec fun2ms(LiteralFun) -> MatchSpec when LiteralFun :: fun((term()) -> term()), MatchSpec :: match_spec().
伪函数,通过解析转换将函数调用中键入为参数的文字 fun 转换为匹配规范。
“字面量”的含义是,fun 需要以文本形式编写为函数调用的参数;它不能保存在变量中,然后将该变量传递给函数。此外,必须启用解析转换模块 ms_transform
。启用它的最简单方法是将以下行添加到源文件中
-include_lib("stdlib/include/ms_transform.hrl").
如果未在源文件中包含 ms_transform.hrl
,则会导致运行时错误,而不是编译时错误。
也可以直接从 Erlang shell 调用此函数,如以下示例所示。
fun 的头部必须是一个匹配列表的单个模式。该模式将用于匹配调用的参数
示例:
1> dbg:fun2ms(fun([_,_]) -> true end).
[{['_','_'],[],[true]}]
2> dbg:fun2ms(fun(Args) when length(Args) > 6 -> true end).
[{'$1',[{'>',{length,'$1'},6}],[true]}]
第一个匹配规范在调用具有两个参数的函数时匹配。第二个匹配当调用具有 6 个以上参数的函数时匹配。
示例:
1> dbg:fun2ms(fun(42) -> true end).
Error: dbg:fun2ms requires fun with single variable or list parameter
{error,transform_error}
2> dbg:fun2ms(fun([<<H,T/binary>>]) -> true end).
Error: fun head contains bit syntax matching of variable 'H', which cannot be translated into match_spec
{error,transform_error}
前面的两个示例显示了当 fun 无法转换为匹配规范时会发生什么。在第一个示例中,fun 的头部不可能匹配列表。在第二个示例中,尝试使用位语法分解二进制文件,而匹配规范中当前不支持该语法。
但是,请注意,可以匹配字面量二进制文件
1> dbg:fun2ms(fun([<<"abc">>]) -> true end).
[{[<<"abc">>],[],[true]}]
匹配规范支持 Erlang 支持的 保护表达式 的很大一部分子集,但不是全部。例如,当前不支持更新映射
1> dbg:fun2ms(fun([M]) when map_size(M#{a => b}) > 2 -> true end).
Error: the language element map (in guard) cannot be translated into match_spec
{error,transform_error}
但是,允许在保护中创建映射
1> dbg:fun2ms(fun([M]) when map_size(#{a => b}) > 2 -> true end).
[{['$1'],[{'>',{map_size,#{a => b}},2}],[true]}]
可以导入环境中的变量,因此这可行
1> X = 3.
3
2> dbg:fun2ms(fun([M,N]) when N > X -> return_trace() end).
[{['$1','$2'],[{'>','$2',{const,3}}],[{return_trace}]}]
导入的变量将被替换为 const
表达式,这与 Erlang fun 的静态作用域一致。
在 fun 的主体中,只允许保护表达式和对 仅允许用于跟踪的特殊函数 的调用。
示例:
1> dbg:fun2ms(fun([A]) when is_atom(A) -> return_trace() end).
[{['$1'],[{is_atom,'$1'}],[{return_trace}]}]
2> dbg:fun2ms(fun(_) -> erlang:garbage_collect() end).
Error: fun containing the remote function call 'erlang:garbage_collect/0' (called in body) cannot be translated into match_spec
{error,transform_error}
警告
如果不对调用
dbg:fun2ms/1
的模块应用解析转换,则该调用将在运行时失败,并出现badarg
异常。
更多信息可在 STDLIB 中模块 ms_transform
的文档中找到。
-spec get_tracer() -> term().
等效于 get_tracer(node())
。
-spec get_tracer(Nodename) -> {ok, Tracer} when Nodename :: atom(), Tracer :: port() | pid() | {module(), term()}.
返回所有跟踪消息发送到的进程、端口或跟踪器模块。
-spec h() -> ok.
提供简短在线帮助的项列表。
h
代表 help(帮助)。
-spec h(Item) -> ok when Item :: atom().
提供 dbg
模块中函数的简短帮助文本。
h
代表 help(帮助)。
可以通过调用 dbg:h/0
列出可用的项。
-spec i() -> ok.
显示有关所有已跟踪进程和端口的信息。
i
代表 information(信息)。
-spec ln() -> ok.
在控制台上显示已跟踪节点的列表。
ln
代表 list nodes(列出节点)。
-spec ltp() -> ok.
列出之前在会话中使用的所有匹配规范。
ltp
代表 list trace patterns(列出跟踪模式)。
此函数列出先前在调用 tp/2
和 tpl/2
期间保存的所有匹配规范,以及所有内置匹配规范。这避免了重新键入复杂的匹配规范。请注意,如果调用 stop/0
,则匹配规范将会丢失。
匹配规范可以保存在文件中(如果存在读写文件系统),以便在以后的调试会话中使用;请参阅 wtp/1
和 rtp/1
。
有三种内置跟踪模式
exception_trace
,x
- 设置一个跟踪,它将显示函数名称、参数、返回值以及从函数引发的异常caller_trace
,c
- 设置一个跟踪,它显示函数名称、参数以及有关哪个函数调用它的信息caller_exception_trace
,cx
- 结合了exception_trace
和caller_trace
这是一个示例,展示如何使用内置匹配规范
1> dbg:tracer().
{ok,<0.90.0>}
2> dbg:tp(lists, seq, 2, cx).
{ok,[{matched,nonode@nohost,1},{saved,cx}]}
3> dbg:p(self(), call).
{ok,[{matched,nonode@nohost,1}]}
4> lists:seq(1, 5).
(<0.88.0>) call lists:seq(1,5) ({erl_eval,do_apply,7,{"erl_eval.erl",904}})
[1,2,3,4,5]
(<0.88.0>) returned from lists:seq/2 -> [1,2,3,4,5]
将远程节点(节点名
)添加到执行跟踪的节点列表中。
n
代表 node(节点)。
dbg
服务器维护一个列表,其中包含应执行跟踪的节点。每当调用 tp/2
或 p/2
时,它都会在此列表中的所有节点上执行,包括本地节点(除了使用特定 pid/0
或 port/0
作为第一个参数的 p/2
,在这种情况下,该命令仅在指定进程或端口所在的节点上执行)。
调用此函数时,它会在远程节点上启动一个跟踪器进程,该进程会将所有跟踪消息发送到本地节点上的跟踪器进程(通过 Erlang 分布)。如果本地节点上没有运行跟踪器进程,则会返回错误原因 no_local_tracer
。本地节点上的跟踪器进程必须使用 tracer/0,2
函数启动。
如果 Nodename
是本地节点,则会返回错误原因 cant_add_local_node
。
如果本地节点上正在运行跟踪端口(请参阅 trace_port/2
),则无法使用跟踪器进程跟踪远程节点。会返回错误原因 cant_trace_remote_pid_to_local_port
。但是,可以使用 tracer/3
函数在远程节点上启动跟踪端口。
如果节点 Nodename
不可达,该函数也会返回错误。
等同于 p(项目, [m])
。
-spec p(Item :: term(), Flags :: term()) -> {ok, MatchDesc} | {error, term()} when MatchDesc :: [MatchNum], MatchNum :: {matched, node(), integer()} | {matched, node(), 0, RPCError}, RPCError :: term().
根据 标志
指定的值跟踪 项目
。
p
代表 process(进程)。
以下类型的值允许用于 Item
pid/0
或port/0
- 跟踪相应的进程或端口。该进程或端口可以是远程进程或端口(在另一个 Erlang 节点上)。该节点必须在跟踪节点列表中(请参阅n/1
和tracer/3
)。all
- 将跟踪系统中的所有进程和端口以及之后创建的所有进程和端口。processes
- 将跟踪系统中的所有进程以及之后创建的所有进程。ports
- 将跟踪系统中的所有端口以及之后创建的所有端口。new
- 将跟踪调用后创建的所有进程和端口。new_processes
- 将跟踪调用后创建的所有进程。new_ports
- 将跟踪调用后创建的所有端口。existing
- 将跟踪所有现有进程和端口。existing_processes
- 将跟踪所有现有进程。existing_ports
- 将跟踪所有现有端口。atom/0
- 将跟踪具有相应注册名称的进程或端口。该进程或端口可以在另一个 Erlang 节点上。该节点必须在跟踪节点列表中(请参阅n/1
和tracer/3
)。integer/0
- 跟踪进程<0.Item.0>
。{X, Y, Z}
- 跟踪进程<X.Y.Z>
。string/0
- 如果Item
是一个字符串 "<X.Y.Z>",如从pid_to_list/1
返回的,则跟踪进程<X.Y.Z>
。
当启用表示一组进程的 Item
时,将在使用 n/1
或 tracer/3
函数添加的所有节点上启用该 Item
。
Flags
可以是单个原子或标志列表。可用的标志有:
s (send)
- 跟踪进程或端口发送的消息。r (receive)
- 跟踪进程或端口接收的消息。m (messages)
- 跟踪进程或端口接收和发送的消息。c (call)
- 根据系统中设置的跟踪模式跟踪进程的全局函数调用(请参阅tp/2
)。p (procs)
- 跟踪与该进程相关的事件。ports
- 跟踪与该端口相关的事件。sos (set on spawn)
- 使被跟踪进程创建的所有进程继承被跟踪进程的跟踪标志。sol (set on link)
- 每当被跟踪进程链接到P2
时,使另一个进程P2
继承被跟踪进程的跟踪标志。sofs (set on first spawn)
- 这与sos
相同,但仅适用于被跟踪进程生成的第一个进程。sofl (set on first link)
- 这与sol
相同,但仅适用于被跟踪进程对link/1
的第一次调用。all
- 设置除silent
之外的所有标志。clear
- 清除所有标志。
该列表还可以包含 trace:process/4
和 trace:port/4
中允许的任何标志。
此函数返回一个错误元组或一个 {ok, List}
元组。List
包含有关匹配的进程和端口数量的规范(在单个 pid 的情况下,恰好为 1)。匹配的进程的规范是 {matched, Node, N}
。如果使用 rpc
对远程节点的远程处理器调用失败,则 rpc
错误消息将作为元组中的第四个元素返回,并且匹配的进程数为 0。
从文本文件中读取匹配规范,该文件可能由 wtp/1
函数生成。
rtp
代表 read trace patterns(读取跟踪模式)。
该函数验证所有匹配规范的语法是否正确。如果在任何匹配规范中发现任何错误,则不会将任何匹配规范添加到正在运行的系统的已保存匹配规范列表中。
文件中的匹配规范与当前的匹配规范合并,这样就不会生成重复项。使用 ltp/0
来查看为文件中的规范分配了哪些编号。
该函数将返回一个错误元组,可能是由于 I/O 问题(例如,文件不存在或不可读)或由于文件格式问题。在后一种情况下,Reason
是一种或多或少文本格式的,提示导致问题的原因。
使用提供的会话运行 dbg
命令,如果提供会话名称,则在调用期间创建会话。
使用提供的 fun 调用的任何 dbg
函数将使用提供的 session/0
而不是默认的 dbg
会话。这意味着跟踪将与系统上的其他跟踪用户隔离。
该函数返回 fun 返回的项。
示例:
1> S = dbg:session_create(my_session).
<0.91.0>
2> dbg:session(S, fun() -> dbg:tracer(), dbg:p(all,c), dbg:tp(lists,seq,x) end).
{ok,[{matched,nonode@nohost,2},{saved,x}]}
3> lists:seq(1, 10).
(<0.89.0>) call lists:seq(1,10)
(<0.89.0>) returned from lists:seq/2 -> [1,2,3,4,5,6,7,8,9,10]
[1,2,3,4,5,6,7,8,9,10]
4> dbg:session_destroy(S).
ok
session/0
的状态在 session/2
调用之间保留,因此您可以在调试应用程序时多次调用 session/2
。
示例:
1> S = dbg:session_create(my_session).
<0.91.0>
%% Setup the initial traces
2> dbg:session(S, fun() -> dbg:tracer(), dbg:p(self(),c), dbg:tp(lists,seq,x) end).
{ok,[{matched,nonode@nohost,2},{saved,x}]}
3> lists:seq(1, 3).
(<0.89.0>) call lists:seq(1,3)
(<0.89.0>) returned from lists:seq/2 -> [1,2,3]
[1,2,3]
%% Add an additional trace pattern
4> dbg:session(S, fun() -> dbg:tpl(lists,seq_loop,x) end).
ok
5> lists:seq(1, 3).
(<0.89.0>) call lists:seq(1,3)
(<0.89.0>) call lists:seq_loop(3,3,[])
(<0.89.0>) call lists:seq_loop(1,1,[2,3])
(<0.89.0>) returned from lists:seq_loop/3 -> [1,2,3]
(<0.89.0>) returned from lists:seq_loop/3 -> [1,2,3]
(<0.89.0>) returned from lists:seq/2 -> [1,2,3]
[1,2,3]
6> dbg:session_destroy(S).
ok
注意
会话功能在 Erlang/OTP 27 中是实验性的,并且可能会在未来的版本中更改,恕不另行通知。
使用给定的 名称
创建一个新的 dbg
会话。
会话与调用进程链接,并且将
多个会话可以具有相同的名称。
注意
会话功能在 Erlang/OTP 27 中是实验性的,并且可能会在未来的版本中更改,恕不另行通知。
-spec session_destroy(Session :: session()) -> ok.
销毁一个 dbg session/0
。
这将终止所有已启动的进程并销毁 trace:session/0
。
-spec stop() -> ok.
停止 dbg
服务器,清除所有进程的所有跟踪标志,清除所有函数的跟踪模式,清除发送/接收的跟踪模式,关闭所有跟踪客户端,并关闭所有跟踪端口。
-spec stop_trace_client(Pid) -> ok when Pid :: pid().
关闭先前启动的跟踪客户端。
Pid
参数是从 trace_client/2
或 trace_client/3
调用返回的进程 ID。
-spec tp(Module | {Module, Function, Arity}, MatchSpec) -> {ok, match_desc()} | {error, term()} when Module :: tp_module(), Function :: tp_function(), Arity :: tp_arity(), MatchSpec :: tp_match_spec().
为一个或多个由 模块或MFA
指定的导出函数启用调用跟踪。
如果 ModuleOrMFA
是一个原子(模块名称),则此函数调用等效于 tp({ModuleOrMFA, '_', '_'}, MatchSpec)
。
否则,模块或MFA
应该为 {模块, 函数, 参数个数}
。
tp
代表 trace pattern(跟踪模式)。
所有导出的函数,只要与 {Module, Function, Arity}
参数匹配,都会被考虑在内,但匹配规范可能会进一步缩小生成跟踪消息的函数调用集合。
有关 MatchSpec
参数格式的描述,请参阅Erlang 中的匹配规范,其中解释了通用的匹配规范语言。最常用的通用匹配规范可以作为内置别名找到;有关详细信息,请参见下文的 ltp/0
。
元组的 Module、Function 和/或 Arity 部分可以指定为原子 '_'
,这是一个匹配所有模块、函数或参数数量的通配符。请注意,如果 Module
被指定为 '_'
,则 Function
和 Arity
部分也必须指定为 '_'
。对于 Function
与 Arity
的关系,情况也是如此。
所有通过 n/1
或 tracer/3
添加的节点都将受此调用影响,如果 Module
不是 '_'
,则该模块将在所有节点上加载。
该函数返回一个错误元组或一个 {ok, List}
元组。 List
包含匹配的函数数量的规范,其表示方式与 p/2
的返回值中进程和端口的表示方式相同。
如果 MatchSpec
不是 []
,则返回值中可能存在一个元组 {saved, N}
。整数 N
随后可以在对此函数的调用中用作给定表达式的“别名”。
如果匹配规范无效,则返回 {error, Errors}
元组。 Errors
是元组列表 {error, string()}
,其中字符串是对编译错误的文本解释。例如
1> dbg:tp({dbg,ltp,0},[{[],[],[{message, two, arguments}, {noexist}]}]).
{error,
[{error,"Special form 'message' called with wrong number of
arguments in {message,two,arguments}."},
{error,"Function noexist/1 does_not_exist."}]}
-spec tp(Module :: tp_module(), Function :: tp_function(), MatchSpec :: tp_match_spec()) -> {ok, match_desc()} | {error, term()}.
-spec tp(Module :: tp_module(), Function :: tp_function(), Arity :: tp_arity(), MatchSpec :: tp_match_spec()) -> {ok, match_desc()} | {error, term()}.
-spec tpe(Event, MatchSpec) -> {ok, MatchDesc :: match_desc()} | {error, term()} when Event :: send | 'receive', MatchSpec :: tp_match_spec().
将匹配规范与跟踪事件 send
或 'receive'
相关联。
tpe
代表 trace pattern event(跟踪模式事件)。
默认情况下,如果为进程启用了所有执行的 send
和 'receive'
事件都会被跟踪。可以使用匹配规范来根据发送者、接收者和/或消息内容过滤跟踪的事件。
有关 MatchSpec
参数格式的描述,请参阅Erlang 中的匹配规范,其中解释了通用的匹配规范语言。
对于 send
,匹配是在列表 [Receiver, Msg]
上完成的。 Receiver
是接收者的进程或端口标识,Msg
是消息项。可以使用保护函数 self/0
访问发送进程的 pid。
对于 'receive'
,匹配是在列表 [Node, Sender, Msg]
上完成的。 Node
是发送者的节点名称。Sender
是发送者的进程或端口标识,如果发送者未知(远程发送者可能就是这种情况),则为原子 undefined
。 Msg
是消息项。可以通过调用 self/0
来访问接收进程的 pid。
-spec tpl(Module | {Module, Function :: tp_function(), Arity :: tp_arity()}, MatchSpec :: tp_match_spec()) -> {ok, MatchDesc :: term()} | {error, term()} when Module :: tp_module().
为一个或多个由 模块或MFA
指定的函数启用调用跟踪。
如果 ModuleOrMFA
是一个原子(模块名称),则此函数调用等效于 tpl({ModuleOrMFA, '_', '_'}, MatchSpec)
。
否则,模块或MFA
应该为 {模块, 函数, 参数个数}
。
tpl
代表 trace pattern local(本地跟踪模式)。
此函数与 tp/2
的工作方式相同,但启用对本地和导出函数的本地或远程调用的跟踪。
-spec tpl(Module :: tp_module(), Function :: tp_function(), MatchSpec :: tp_match_spec()) -> {ok, match_desc()} | {error, term()}.
-spec tpl(Module :: tp_module(), Function :: tp_function(), Arity :: tp_arity(), MatchSpec :: tp_match_spec()) -> {ok, match_desc()} | {error, term()}.
-spec trace_client(ip, IPClientPortSpec) -> pid() when IPClientPortSpec :: PortNumber | {Hostname, PortNumber}, PortNumber :: integer(), Hostname :: string(); (Type, Parameters) -> pid() when Type :: file | follow_file, Parameters :: Filename | WrapFilesSpec, Filename :: file:name_all(), WrapFilesSpec :: trace_wrap_files_spec().
启动一个跟踪客户端,该客户端读取由跟踪端口驱动程序创建的输出(请参阅 trace_port/2
),并以与 tracer/0
函数创建的跟踪器进程大致相同的方式处理它。
如果 Type
是 file
,则客户端读取存储在名为 Filename
的文件或由 WrapFilesSpec
指定的文件中的所有跟踪消息(必须与创建跟踪时使用的相同),并让默认处理程序函数在控制台上格式化消息。这是解释文件跟踪端口驱动程序存储在文件中的数据的一种方法。
如果 Type
是 follow_file
,则客户端的行为与 file
情况下的行为相同,但会持续尝试从文件中读取(和处理)更多数据,直到被 stop_trace_client/1
停止。 WrapFilesSpec
不允许用作此 Type
的第二个参数。
如果 Type
是 ip
,则客户端连接到主机 Hostname
上的 TCP/IP 端口 PortNumber
,并从那里读取跟踪消息,直到 TCP/IP 连接关闭。如果未指定 Hostname
,则假定为本地主机。
例如,可以让跟踪消息通过网络发送到另一个 Erlang 节点(最好不是分布式节点),并在该节点上进行格式化。
在节点 stack
上,存在一个 Erlang 节点 ant@stack
。在 shell 中,键入以下内容
ant@stack> dbg:tracer(port, dbg:trace_port(ip, 4711)).
<0.17.0>
ant@stack> dbg:p(self(), send).
{ok,1}
现在,所有跟踪消息都将发送到跟踪端口驱动程序,该驱动程序又在 TCP/IP 端口 4711 上侦听连接。如果我们想在另一个节点(最好在另一个主机上)上查看消息,我们可以这样做
1> dbg:trace_client(ip, {"stack", 4711}).
<0.42.0>
如果我们现在从节点 ant@stack
上的 shell 发送消息,其中来自 shell 的所有发送都被跟踪
ant@stack> self() ! hello.
hello
以下内容将出现在启动跟踪客户端的节点的控制台上
(<0.23.0>) <0.23.0> ! hello
(<0.23.0>) <0.22.0> ! {shell_rep,<0.23.0>,{value,hello,[],[]}}
最后一行是由于 Erlang shell 中的内部消息传递而生成的。 pid 将会有所不同。
-spec trace_client(ip, IPClientPortSpec, HandlerSpec) -> pid() when IPClientPortSpec :: PortNumber | {Hostname, PortNumber}, PortNumber :: integer(), Hostname :: string(), HandlerSpec :: handler_spec(); (Type, Parameters, HandlerSpec) -> pid() when Type :: file | follow_file, Parameters :: Filename | WrapFilesSpec, Filename :: string() | [string()] | atom(), WrapFilesSpec :: trace_wrap_files_spec(), HandlerSpec :: handler_spec().
此函数的工作方式与 trace_client/2
完全相同,但允许您编写自己的处理程序函数。
处理程序函数的工作方式与 tracer/2
中描述的类似,但也必须准备好处理 {drop, N}
形式的跟踪消息,其中 N
是丢弃的消息数。仅当使用 ip
跟踪驱动程序时,才会出现此伪跟踪消息。
对于跟踪类型 file
,伪跟踪消息 end_of_trace
将出现在跟踪结束时。在这种情况下,处理程序函数的返回值将被忽略。
-spec trace_port(ip, IpPortSpec) -> fun(() -> port()) when IpPortSpec :: PortNumber | {PortNumber, QueSize}, PortNumber :: integer(), QueSize :: integer(); (file, Parameters) -> fun(() -> port()) when Parameters :: Filename | WrapFilesSpec, Filename :: file:name_all(), WrapFilesSpec :: trace_wrap_files_spec().
创建一个生成跟踪端口的 fun,它适合作为 tracer/2
的第二个参数。
示例
dbg:tracer(port, dbg:trace_port(ip, 4711)).
跟踪端口是 Erlang 端口,用于直接处理跟踪消息的动态链接驱动程序,而无需将它们作为消息发送到 Erlang 进程的开销。使用跟踪端口可以显著降低跟踪带来的开销。
目前实现了两个跟踪驱动程序:file
和 ip
跟踪驱动程序。
file
驱动程序将所有跟踪消息发送到一个或多个二进制文件中,然后可以使用 trace_client/2
函数从这些文件中提取和处理它们。
ip
驱动程序打开一个 TCP/IP 端口侦听端口。当客户端(最好通过在另一个 Erlang 节点上调用 trace_client/2
启动)连接时,所有跟踪消息都将通过 IP 网络发送,以供远程客户端进一步处理。
file
跟踪驱动程序需要一个文件名或一个包装文件规范作为参数。写入文件时具有高度缓冲,因此无法保证在系统崩溃时所有内容都保存在文件中。
包装文件规范用于限制跟踪消耗的磁盘空间。跟踪写入到数量有限的文件中,每个文件的大小有限。实际文件名是 Filename ++ SeqCnt ++ Suffix
,其中 SeqCnt
从 0
计数到 WrapCnt
,然后再次从 0
循环计数。当写入当前文件的跟踪项使其长度超过 WrapSize
时,该文件将关闭,如果此包装跟踪中的文件数量与 WrapCnt
一样多,则将删除最旧的文件,并打开一个新文件作为当前文件。因此,当停止包装跟踪时,最多会保存 WrapCnt
个跟踪文件,每个文件的大小至少为 WrapSize
(但不会大太多),最后一个文件除外,该文件甚至可能为空。默认值为 WrapSize = 128*1024
和 WrapCnt = 8
。
文件名中的 SeqCnt
值都在 0
到 WrapCnt
的范围内,循环序列中存在间隙。需要此间隙来查找跟踪的末尾。
如果 WrapSize
指定为 {time, WrapTime}
,则当当前文件打开超过 WrapTime
毫秒时,无论其是否为空,都会关闭该文件。
ip
跟踪驱动程序有一个 QueSize
消息的队列等待传递。如果驱动程序无法以运行时系统生成消息的速度传递消息,则会发送一条特殊消息,指示有多少消息被丢弃。该消息将作为元组 {drop, N}
到达 trace_client/3
中指定的处理程序函数,其中 N
是丢弃的连续消息的数量。在大量跟踪的情况下,很可能发生丢弃,如果没有任何客户端读取跟踪消息,则肯定会发生丢弃。 QueSize
的默认值为 200。
-spec trace_port_control(Nodename :: node(), Operation :: term()) -> ok | {ok, Result :: term()} | {error, Reason :: term()}.
此函数用于对给定节点(节点名
)上的活动跟踪端口驱动程序执行控制操作。
允许的操作及其返回值取决于使用的跟踪驱动程序。
如果操作成功,则返回 ok
或 {ok, Result}
,如果当前跟踪器是进程,或者如果它是端口不支持该操作,则返回 {error, Reason}
。
Operation
允许的值为:
flush
- 此函数用于刷新跟踪端口驱动程序持有的内部缓冲区。目前只有file
跟踪驱动程序支持此操作。返回ok
。get_listen_port
- 返回{ok, IpPort}
,其中IpPort
是驱动程序监听套接字使用的 IP 端口号。只有ip
跟踪驱动程序支持此操作。
-spec tracer() -> {ok, pid()} | {error, already_started}.
在本地节点上启动一个服务器,该服务器将接收所有跟踪消息。
所有后续对 p/2
的调用都会导致消息发送到新启动的跟踪服务器。
以这种方式启动的跟踪服务器将简单地在 Erlang Shell 中显示格式化的跟踪消息(即,使用 io:format/2
)。有关如何自定义跟踪消息处理程序,请参阅 tracer/2
。
要在远程节点上启动类似的跟踪器,请使用 n/1
。
-spec tracer(port, PortGenerator) -> {ok, pid()} | {error, Error :: term()} when PortGenerator :: fun(() -> port()); (process, HandlerSpec) -> {ok, pid()} | {error, Error :: term()} when HandlerSpec :: {HandlerFun, InitialData :: term()}, HandlerFun :: fun((Event :: term(), Data :: term()) -> NewData :: term()); (module, ModuleSpec) -> {ok, pid()} | {error, Error :: term()} when ModuleSpec :: fun(() -> {TracerModule, TracerState}) | {TracerModule, TracerState}, TracerModule :: atom(), TracerState :: term(); (file, Filename) -> {ok, pid()} | {error, Error :: term()} when Filename :: file:name_all().
在本地节点上启动具有附加参数的跟踪器服务器。
Type
指示应如何处理跟踪消息
process
- 由接收进程处理port
- 由端口处理;请参阅trace_port/2
module
- 由跟踪器模块处理;请参阅erl_tracer
file
- 通过将它们打印到文件来处理
如果 Type
是 process
,则 Data
应该是一个消息处理函数 (HandlerSpec
)。对于每个跟踪消息,将调用处理函数(它应该是一个带有两个参数的 fun
),第一个参数包含消息本身,第二个参数包含上次调用 fun 的返回值。HandlerSpec
的 InitialData
部分中指定了第二个参数的初始值。HandlerFun
可以选择在被调用时采取任何适当的操作,并且可以通过返回状态来保存下一次调用的状态。
如果 Type
是 port
,那么第二个参数应该是一个不带参数的 fun,当调用时,它会返回一个新打开的跟踪端口。这样的 fun 最好通过调用 trace_port/2
生成。
如果 Type
是 module
,则 Data
应该是一个描述用于跟踪的 erl_tracer
模块和用于该跟踪器模块的状态的元组,或者一个返回这种元组的 fun。
如果 Type
是 file
,则 Data
应该是一个文件名,指定所有跟踪将打印到的文件。
如果返回错误,可能是因为跟踪服务器已经在运行 ({error,already_started}
),或者是因为 HandlerFun
引发了异常。
要在远程节点上启动类似的跟踪器,请使用 tracer/3
。
-spec tracer(Nodename :: node(), Type :: term(), Data :: term()) -> {ok, Nodename :: node()} | {error, Reason :: term()}.
此函数等同于 tracer/2
,但在给定节点上运行。
在节点 (Nodename
) 上启动跟踪器,并将该节点添加到被跟踪节点列表中。
注意
此函数不等同于
n/1
。 虽然n/1
启动一个进程跟踪器,它将所有跟踪信息重定向到本地节点(即跟踪控制节点)上的进程跟踪器,但tracer/3
启动任何类型的跟踪器,独立于跟踪控制节点上的跟踪器类型。
有关详细信息,请参阅 tracer/2
。
将会话期间保存的所有匹配规范(通过调用 tp/2
或 tpl/2
)以及内置匹配规范,保存在名称由 名称
指定的文本文件中。
wtp
代表 write trace patterns(写入跟踪模式)。
文件的格式是文本格式,这意味着可以使用文本编辑器进行编辑,然后使用 rtp/1
恢复。
文件中的每个匹配规范都以句点 (.
) 结尾,并且可以将新的(语法正确的)匹配规范手动添加到文件中。
该函数返回 ok
,或者一个错误元组,其中第二个元素指示写入文件失败的原因。