在另一个进程上设置 seq_trace
Setting seq_trace on another process
我知道我可以在 erlang 中设置一个 seq_trace 到当前正在执行的进程。但是我如何在 shell 或远程 shell 的另一个进程(如 dbg 跟踪)上设置它?
您可以使用 dbg
在另一个进程上启用顺序跟踪。例如,假设我们有一个带有导出 call/2
函数的模块 x
:
call(Pid, Msg) ->
Pid ! {self(), Msg},
receive
{Pid, Reply} -> Reply
end.
这个函数实现了一个简单的呼叫响应。假设我们有一个模块 y
,它有一个循环接收函数:
loop() ->
receive
{Pid, Msg} ->
seq_trace:print({?MODULE, self(), Pid, Msg}),
Pid ! {self(), {Msg, os:timestamp()}};
_ -> ok
end,
?MODULE:loop().
此函数需要一条由 x:call/2
发送的消息,当它收到一条消息时,它会将一条消息打印到顺序跟踪中(如果启用),然后将原始消息发送回调用者并增加一个时间戳。它会忽略所有其他消息。
我们还需要一个函数来收集顺序跟踪。下面的递归 systracer/1
函数只是将 seq_trace
元组收集到列表中,并在询问时生成 seq_trace
消息列表:
systracer(Acc) ->
receive
{seq_trace,_,_,_}=S ->
systracer([S|Acc]);
{seq_trace,_,_}=S ->
systracer([S|Acc]);
{dump, Pid} ->
Pid ! lists:reverse(Acc),
systracer([]);
stop -> ok
end.
假设我们的 systracer/1
函数也从模块 x
导出。
让我们使用我们的 Erlang shell 来设置这一切。首先,让我们生成 y:loop/0
和 x:systracer/1
:
1> Y = spawn(y,loop,[]).
<0.36.0>
2> S = spawn(x,systracer,[[]]).
<0.38.0>
3> seq_trace:set_system_tracer(S).
false
生成后 x:systracer/1
我们将进程设置为 seq_trace
系统跟踪器。现在我们需要开始 dbg
:
4> dbg:tracer(), dbg:p(all,call).
{ok,[{matched,nonode@nohost,28}]}
这些 dbg
调用非常标准,但您可以根据需要随意更改它们,特别是如果您还计划在调试会话期间使用 dbg
跟踪。
在实践中,当您使用 dbg
启用顺序跟踪时,您通常通过键入函数的特定参数来实现。这使您能够获取特定于给定函数调用的跟踪,而无需获取该函数的所有调用的跟踪。沿着这些思路,我们将使用 dbg:tpl/3
在调用 x:call/2
且其第二个参数具有原子 trace
的值时打开顺序跟踪标志。首先,我们使用 dbg:fun2ms/1
创建适当的匹配规范以启用我们想要的顺序跟踪标志,然后我们将使用 dbg:tpl/3
:
应用匹配规范
5> Ms = dbg:fun2ms(fun([_,trace]) -> set_seq_token(send,true), set_seq_token('receive',true), set_seq_token(print,true) end).
[{['_',trace],
[],
[{set_seq_token,send,true},
{set_seq_token,'receive',true},
{set_seq_token,print,true}]}]
6> dbg:tpl(x,call,Ms).
{ok,[{matched,nonode@nohost,1},{saved,1}]}
现在我们可以用第二个参数 trace
调用 x:call/2
来进行顺序跟踪。我们从派生进程进行此调用以避免在结果跟踪中出现 shell I/O-related 消息:
7> spawn(fun() -> x:call(Y, trace), x:call(Y, foo) end).
(<0.46.0>) call x:call(<0.36.0>,trace)
<0.46.0>
第一行输出来自正常的 dbg
跟踪,因为我们之前指定了 dbg:p(all, call)
。要获得顺序跟踪结果,我们需要从我们的 systrace/1
进程中获取转储:
8> S ! {dump, self()}.
{dump,<0.34.0>}
这会将到目前为止收集的所有顺序跟踪发送到我们的 shell 进程。我们可以使用shellflush()
命令来查看:
9> flush().
Shell got [{seq_trace,0,{send,{0,1},<0.47.0>,<0.36.0>,{<0.47.0>,trace}}},
{seq_trace,0,{'receive',{0,1},<0.47.0>,<0.36.0>,{<0.47.0>,trace}}},
{seq_trace,0,{print,{1,2},<0.36.0>,[],{y,<0.36.0>,<0.47.0>,trace}}},
{seq_trace,0,
{send,{1,3},
<0.36.0>,<0.47.0>,
{<0.36.0>,{trace,{1423,709096,206121}}}}},
{seq_trace,0,
{'receive',{1,3},
<0.36.0>,<0.47.0>,
{<0.36.0>,{trace,{1423,709096,206121}}}}},
{seq_trace,0,{send,{3,4},<0.47.0>,<0.36.0>,{<0.47.0>,foo}}},
{seq_trace,0,{'receive',{3,4},<0.47.0>,<0.36.0>,{<0.47.0>,foo}}},
{seq_trace,0,{print,{4,5},<0.36.0>,[],{y,<0.36.0>,<0.47.0>,foo}}},
{seq_trace,0,
{send,{4,6},
<0.36.0>,<0.47.0>,
{<0.36.0>,{foo,{1423,709096,206322}}}}},
{seq_trace,0,
{'receive',{4,6},
<0.36.0>,<0.47.0>,
{<0.36.0>,{foo,{1423,709096,206322}}}}}]
果然,这些是我们期望看到的顺序跟踪消息。首先,对于包含 trace
原子的消息,我们有来自 x:call/2
的发送,然后是 y:loop/0
的接收和 seq_trace:print/1
的结果,然后是 [=] 的发送30=] 返回给 x:call/2
的调用者。然后,由于在同一进程中调用了 x:call(Y,foo)
,这意味着所有顺序跟踪标志仍处于启用状态,因此第一组顺序跟踪消息后跟一组类似的 x:call(Y,foo)
调用。
如果我们只调用 x:call(Y,foo)
我们可以看到我们没有得到连续的跟踪消息:
10> spawn(fun() -> x:call(Y, foo) end).
<0.55.0>
11> S ! {dump, self()}.
{dump,<0.34.0>}
12> flush().
Shell got []
这是因为我们的匹配规范仅在 x:call/2
的第二个参数是原子 trace
.
时启用顺序跟踪
有关详细信息,请参阅 seq_trace
and dbg
手册页,并阅读 Erlang 运行-Time System Application (ERTS) 的 匹配规范章节
用户指南.
我知道我可以在 erlang 中设置一个 seq_trace 到当前正在执行的进程。但是我如何在 shell 或远程 shell 的另一个进程(如 dbg 跟踪)上设置它?
您可以使用 dbg
在另一个进程上启用顺序跟踪。例如,假设我们有一个带有导出 call/2
函数的模块 x
:
call(Pid, Msg) ->
Pid ! {self(), Msg},
receive
{Pid, Reply} -> Reply
end.
这个函数实现了一个简单的呼叫响应。假设我们有一个模块 y
,它有一个循环接收函数:
loop() ->
receive
{Pid, Msg} ->
seq_trace:print({?MODULE, self(), Pid, Msg}),
Pid ! {self(), {Msg, os:timestamp()}};
_ -> ok
end,
?MODULE:loop().
此函数需要一条由 x:call/2
发送的消息,当它收到一条消息时,它会将一条消息打印到顺序跟踪中(如果启用),然后将原始消息发送回调用者并增加一个时间戳。它会忽略所有其他消息。
我们还需要一个函数来收集顺序跟踪。下面的递归 systracer/1
函数只是将 seq_trace
元组收集到列表中,并在询问时生成 seq_trace
消息列表:
systracer(Acc) ->
receive
{seq_trace,_,_,_}=S ->
systracer([S|Acc]);
{seq_trace,_,_}=S ->
systracer([S|Acc]);
{dump, Pid} ->
Pid ! lists:reverse(Acc),
systracer([]);
stop -> ok
end.
假设我们的 systracer/1
函数也从模块 x
导出。
让我们使用我们的 Erlang shell 来设置这一切。首先,让我们生成 y:loop/0
和 x:systracer/1
:
1> Y = spawn(y,loop,[]).
<0.36.0>
2> S = spawn(x,systracer,[[]]).
<0.38.0>
3> seq_trace:set_system_tracer(S).
false
生成后 x:systracer/1
我们将进程设置为 seq_trace
系统跟踪器。现在我们需要开始 dbg
:
4> dbg:tracer(), dbg:p(all,call).
{ok,[{matched,nonode@nohost,28}]}
这些 dbg
调用非常标准,但您可以根据需要随意更改它们,特别是如果您还计划在调试会话期间使用 dbg
跟踪。
在实践中,当您使用 dbg
启用顺序跟踪时,您通常通过键入函数的特定参数来实现。这使您能够获取特定于给定函数调用的跟踪,而无需获取该函数的所有调用的跟踪。沿着这些思路,我们将使用 dbg:tpl/3
在调用 x:call/2
且其第二个参数具有原子 trace
的值时打开顺序跟踪标志。首先,我们使用 dbg:fun2ms/1
创建适当的匹配规范以启用我们想要的顺序跟踪标志,然后我们将使用 dbg:tpl/3
:
5> Ms = dbg:fun2ms(fun([_,trace]) -> set_seq_token(send,true), set_seq_token('receive',true), set_seq_token(print,true) end).
[{['_',trace],
[],
[{set_seq_token,send,true},
{set_seq_token,'receive',true},
{set_seq_token,print,true}]}]
6> dbg:tpl(x,call,Ms).
{ok,[{matched,nonode@nohost,1},{saved,1}]}
现在我们可以用第二个参数 trace
调用 x:call/2
来进行顺序跟踪。我们从派生进程进行此调用以避免在结果跟踪中出现 shell I/O-related 消息:
7> spawn(fun() -> x:call(Y, trace), x:call(Y, foo) end).
(<0.46.0>) call x:call(<0.36.0>,trace)
<0.46.0>
第一行输出来自正常的 dbg
跟踪,因为我们之前指定了 dbg:p(all, call)
。要获得顺序跟踪结果,我们需要从我们的 systrace/1
进程中获取转储:
8> S ! {dump, self()}.
{dump,<0.34.0>}
这会将到目前为止收集的所有顺序跟踪发送到我们的 shell 进程。我们可以使用shellflush()
命令来查看:
9> flush().
Shell got [{seq_trace,0,{send,{0,1},<0.47.0>,<0.36.0>,{<0.47.0>,trace}}},
{seq_trace,0,{'receive',{0,1},<0.47.0>,<0.36.0>,{<0.47.0>,trace}}},
{seq_trace,0,{print,{1,2},<0.36.0>,[],{y,<0.36.0>,<0.47.0>,trace}}},
{seq_trace,0,
{send,{1,3},
<0.36.0>,<0.47.0>,
{<0.36.0>,{trace,{1423,709096,206121}}}}},
{seq_trace,0,
{'receive',{1,3},
<0.36.0>,<0.47.0>,
{<0.36.0>,{trace,{1423,709096,206121}}}}},
{seq_trace,0,{send,{3,4},<0.47.0>,<0.36.0>,{<0.47.0>,foo}}},
{seq_trace,0,{'receive',{3,4},<0.47.0>,<0.36.0>,{<0.47.0>,foo}}},
{seq_trace,0,{print,{4,5},<0.36.0>,[],{y,<0.36.0>,<0.47.0>,foo}}},
{seq_trace,0,
{send,{4,6},
<0.36.0>,<0.47.0>,
{<0.36.0>,{foo,{1423,709096,206322}}}}},
{seq_trace,0,
{'receive',{4,6},
<0.36.0>,<0.47.0>,
{<0.36.0>,{foo,{1423,709096,206322}}}}}]
果然,这些是我们期望看到的顺序跟踪消息。首先,对于包含 trace
原子的消息,我们有来自 x:call/2
的发送,然后是 y:loop/0
的接收和 seq_trace:print/1
的结果,然后是 [=] 的发送30=] 返回给 x:call/2
的调用者。然后,由于在同一进程中调用了 x:call(Y,foo)
,这意味着所有顺序跟踪标志仍处于启用状态,因此第一组顺序跟踪消息后跟一组类似的 x:call(Y,foo)
调用。
如果我们只调用 x:call(Y,foo)
我们可以看到我们没有得到连续的跟踪消息:
10> spawn(fun() -> x:call(Y, foo) end).
<0.55.0>
11> S ! {dump, self()}.
{dump,<0.34.0>}
12> flush().
Shell got []
这是因为我们的匹配规范仅在 x:call/2
的第二个参数是原子 trace
.
有关详细信息,请参阅 seq_trace
and dbg
手册页,并阅读 Erlang 运行-Time System Application (ERTS) 的 匹配规范章节
用户指南.