dbg:tracer可视化递归函数,例如通过缩进 [英] dbg:tracer visualizing recursive functions e.g. by indenting
问题描述
我有问题调试一个复杂的递归函数我使用的成语:
dbg:tracer(),dbg :p(all,c),dbg:tpl(Mod,Fun1,x),dbg:tpl(Mod,Fun2,x)...
这给我一个对所有函数的调用的平面列表,很难找出哪个返回属于哪个调用。
<有没有一个简单的方法来使这更可读,例如通过缩进。
我可以稍后对每个通话的生成和缩进的文字进行处理,并且对于每个返回来说都是过往的,但这对我来说听起来不是很优雅。 >
在此期间,我想出了如何做到这一点,其实并不太难。要处理跟踪消息输出的过程,您只需使用 dbg:trace / 2,3
并编写一个执行格式化的函数。
我们写一个包含该函数的小模块传递给 dbg:trace
:
-module(trtool)。
-export([nested / 2])。
嵌套({trace,_Pid,call,{Mod,Fun,Param}},Level) - >
io:format(〜s〜p:〜p〜p\\\
,
[lists:duplicate(Level,|),Mod,Fun,Param]),
级+ 1;
嵌套({trace,_Pid,return_from,{Mod,Fun,Arity},Rval},Level) - >
L =级别-1,
io:格式(〜s〜p:〜p /〜b - >〜p\\\
,
[ |),Mod,Fun,Arity,Rval]),
L;
嵌套(Any,Level) - >
io:format(trace_msg:〜p \\\
,[Any]),
级别。
该函数接受两个参数,首先它通过跟踪消息,这是一个方便的元组领域。要找出要格式化的消息的结构,只需从一个简单的函数开始,该函数打印例如函数的最后一个子句。
第二种格式是可以包含任何数据的状态。调用 dbg:trace
并从我们的函数返回下一个值时,我们传递初始值。
code>嵌套示例,我们只是传递将在调用
和 return_from中递增和递减的缩进级别
子句
现在让我们试试一下,首先调用 dbg:tracer / 2
,第一个参数必须是原子进程
,第二个参数是一个包含我们新写的乐趣的元组和状态参数的初始值。
1> dbg:tracer(process,{fun trtool:nested / 2,0})
{ok,<0.70.0>}
然后我们设置跟踪:
2> dbg:p(all,c),dbg:tpl(user_default,hop,x),dbg:tpl(user_default,rec,x)。
{ok,[{matched,nonode @ nohost,2},{saved,x}]}
然后,我们开始我们的函数调用跟踪,可以轻松地跟踪嵌套:
3> rec(3)。
user_default:rec [3]
| user_default:rec [3,1,3]
| | user_default:rec [3,1,2]
| | | user_default:rec [3,1,1]
| | | | user_default:rec [3,1,0]
| | | | | user_default:hop [3,1,0]
| | | | | user_default:hop / 3 - > {3,21}
| | | | user_default:rec / 3 - > {3,21,1}
| | | | user_default:rec [6,2,-1]
| | | | | user_default:hop [6,2,1]
| | | | | user_default:hop / 3 - > {2,46}
| | | | user_default:rec / 3 - > {2,46,1}
| | | user_default:rec / 3 - > {5,67,2}
| | | user_default:rec [8,3,0]
| | | | user_default:hop [8,3,0]
| | | | user_default:hop / 3 - > {3,144}
| | | user_default:rec / 3 - > {3,144,1}
| | user_default:rec / 3 - > {8,211,3}
| | user_default:rec [11,4,1]
| | | user_default:rec [11,4,0]
| | | | user_default:hop [11,4,0]
| | | | user_default:hop / 3 - > {3,258}
| | | user_default:rec / 3 - > {3,258,1}
| | | user_default:rec [14,5,-1]
| | | | user_default:hop [14,5,1]
| | | | user_default:hop / 3 - > {2,260}
| | | user_default:rec / 3 - > {2,260,1}
| | user_default:rec / 3 - > {5,518,2}
| user_default:rec / 3 - > {13,729,5}
user_default:rec / 1 - > {15,729}
{15,729}
4>
I have the problem debugging an complicated recursive function I'm using the idiom:
dbg:tracer(),dbg:p(all,c),dbg:tpl(Mod,Fun1,x),dbg:tpl(Mod,Fun2,x)...
This gives me a flat list of calls to all functions where it is very hard to find out which return belongs to which call.
Is there a easy way to make this more readable e.g. by indenting.
I could just post process the text produced and indent for each call, and outdent for each return, but this sounds not very elegant to me.
In the meantime I figured it out how to do this, its actually not too hard. To have a process handling the trace message output you just have to use dbg:trace/2,3
and write one function that does the formatting.
We write a small module containing the function to pass to dbg:trace
:
-module(trtool).
-export([nested/2]).
nested({trace, _Pid, call, {Mod, Fun, Param}}, Level) ->
io:format("~s~p:~p ~p\n",
[lists:duplicate(Level, "| "), Mod, Fun, Param]),
Level + 1;
nested({trace, _Pid, return_from, {Mod, Fun, Arity}, Rval}, Level) ->
L = Level - 1,
io:format("~s~p:~p/~b -> ~p\n",
[lists:duplicate(L, "| "), Mod, Fun, Arity, Rval]),
L;
nested(Any, Level) ->
io:format("trace_msg: ~p\n", [Any]),
Level.
The function takes two arguments, in the first it gets passed the trace message which is a tuple with convenient fields. To find out how the messages you want to format are structured just start with a simple function that prints everything like the last clause of the example function.
The second format is a kind of state that could contain any data. We pass the initial value when calling dbg:trace
and return the next value from our function.
In the nested
example we just pass the indent level which will be incremented and decremented in the call
and return_from
clauses.
Now lets try this out, first calling dbg:tracer/2
, first parameter must be the atom process
, second parameter a tuple containing our newly written fun and the initial value for the state param.
1> dbg:tracer(process, {fun trtool:nested/2, 0}).
{ok,<0.70.0>}
Then we set up tracing as before:
2> dbg:p(all, c), dbg:tpl(user_default,hop,x),dbg:tpl(user_default,rec,x).
{ok,[{matched,nonode@nohost,2},{saved,x}]}
Then we start our function call to trace and the nesting can be easily followed:
3> rec(3).
user_default:rec [3]
| user_default:rec [3,1,3]
| | user_default:rec [3,1,2]
| | | user_default:rec [3,1,1]
| | | | user_default:rec [3,1,0]
| | | | | user_default:hop [3,1,0]
| | | | | user_default:hop/3 -> {3,21}
| | | | user_default:rec/3 -> {3,21,1}
| | | | user_default:rec [6,2,-1]
| | | | | user_default:hop [6,2,1]
| | | | | user_default:hop/3 -> {2,46}
| | | | user_default:rec/3 -> {2,46,1}
| | | user_default:rec/3 -> {5,67,2}
| | | user_default:rec [8,3,0]
| | | | user_default:hop [8,3,0]
| | | | user_default:hop/3 -> {3,144}
| | | user_default:rec/3 -> {3,144,1}
| | user_default:rec/3 -> {8,211,3}
| | user_default:rec [11,4,1]
| | | user_default:rec [11,4,0]
| | | | user_default:hop [11,4,0]
| | | | user_default:hop/3 -> {3,258}
| | | user_default:rec/3 -> {3,258,1}
| | | user_default:rec [14,5,-1]
| | | | user_default:hop [14,5,1]
| | | | user_default:hop/3 -> {2,260}
| | | user_default:rec/3 -> {2,260,1}
| | user_default:rec/3 -> {5,518,2}
| user_default:rec/3 -> {13,729,5}
user_default:rec/1 -> {15,729}
{15,729}
4>
这篇关于dbg:tracer可视化递归函数,例如通过缩进的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!