Erlang 在另一个进程上设置seq_跟踪

Erlang 在另一个进程上设置seq_跟踪,erlang,elixir,lfe,Erlang,Elixir,Lfe,我知道我可以在erlang中将seq_跟踪设置为当前正在执行的进程。但是如何在shell的另一个进程上设置它,或者像远程shell一样的dbg跟踪?您可以使用dbg在另一个进程上启用顺序跟踪。例如,假设我们有一个模块x,带有一个导出的call/2函数: call(Pid, Msg) -> Pid ! {self(), Msg}, receive {Pid, Reply} -> Reply end. loop() ->

我知道我可以在erlang中将seq_跟踪设置为当前正在执行的进程。但是如何在shell的另一个进程上设置它,或者像远程shell一样的dbg跟踪?

您可以使用
dbg
在另一个进程上启用顺序跟踪。例如,假设我们有一个模块
x
,带有一个导出的
call/2
函数:

call(Pid, Msg) ->
    Pid ! {self(), Msg},
    receive      
        {Pid, Reply} -> Reply
    end.
loop() ->
    receive
        {Pid, Msg} ->
            seq_trace:print({?MODULE, self(), Pid, Msg}),
            Pid ! {self(), {Msg, os:timestamp()}};
        _ -> ok
    end,
    ?MODULE:loop().
此函数实现一个简单的调用响应。假设我们有一个模块
y
,具有循环接收器功能:

call(Pid, Msg) ->
    Pid ! {self(), Msg},
    receive      
        {Pid, Reply} -> Reply
    end.
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
这些
dbg
调用非常标准,但是您可以根据需要随意更改它们,特别是如果您计划在调试会话期间使用
dbg
跟踪

在实践中,当您使用
dbg
启用顺序跟踪时,通常通过键入函数的特定参数来实现。这使您能够获取特定于给定函数调用的跟踪,而不必获取该函数所有调用的跟踪。沿着这些思路,我们将使用
dbg:tpl/3
在调用
x:call/2
时启用顺序跟踪标志,其第二个参数的值为atom
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相关的消息:

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>
这会将迄今为止收集到的所有顺序跟踪发送到我们的shell进程。我们可以使用shell
flush()
命令查看它们:

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}}}}}]
这是因为我们的match规范仅在
x:call/2
的第二个参数是atom
trace
时才启用顺序跟踪


有关更多信息,请参阅和手册页,并阅读。

对不起,为什么要在此处添加Elixir标签?这里有没有我遗漏的长生不老药元素?主要是无耻的标签劫持。Erlang工具也适用于elixir,所以任何在elixir中使用它们的人都可以回答这个问题。
10> spawn(fun() -> x:call(Y, foo) end).
<0.55.0>
11> S ! {dump, self()}.
{dump,<0.34.0>}
12> flush().
Shell got []