在另一个进程上设置seq_trace

时间:2015-02-05 20:50:11

标签: erlang elixir lfe

据我所知,我可以在erlang中将seq_trace设置为正在执行的当前进程。但是如何在shell或远程shell上设置它,如dbg tracing?

1 个答案:

答案 0 :(得分:3)

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

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/0x: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}]}

现在我们可以使用第二个参数x:call/2调用trace以导致顺序跟踪发生。我们从生成的进程中进行此调用,以避免在生成的跟踪中出现与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>

第一行输出来自正常dbg跟踪,因为我们之前已指定dbg:p(all, call)。要获得顺序跟踪结果,我们需要从systrace/1进程获取转储:

8> S ! {dump, self()}.
{dump,<0.34.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}}}}}]

当然,这些是我们期望看到的顺序跟踪消息。首先,对于包含trace原子的邮件,我们收到来自x:call/2的发送,接着是y:loop/0中的接收和seq_trace:print/1的结果,然后来自{{ 1}}回到y:loop/0的来电者。然后,由于在同一进程中调用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时,我们的匹配规范才会启用顺序跟踪。

有关详情,请参阅seq_tracedbg手册页,另请阅读match specification chapter of the Erlang Run-Time System Application (ERTS) User's Guide