是否有一种简单的方法可以获取在Erlang中以erlang:send_after
,erlang:apply_after
等开头的所有当前等待计时器的列表?
答案 0 :(得分:6)
出于调试目的,您可以使用dbg
:)。
首先创建一个ets表,它将存储所有计时器引用。
1> ets:new(timer_dbg, ['public', 'named_table', 'bag']).
timer_dbg
然后创建一个dbg处理函数,它检查从erlang:send_after返回的调用,并将返回的计时器引用保存到表中
2> Fun = fun({'trace', _Pid, 'return_from', {erlang, send_after, 3}, Ref}, []) ->
2> ets:insert(timer_dbg, {Ref}), [];
2> (_Msg, []) ->
2> []
2> end.
#Fun<erl_eval.12.113037538>
将该功能设置为跟踪处理程序。同时在所有进程的erlang:send_after()
调用上启用匹配
3> dbg:tracer('process', {Fun, []}).
{ok,<0.35.0>}
4> dbg:p('all', 'c').
{ok,[{matched,nonode@nohost,26}]}
5> dbg:tpl(erlang, send_after, [{'_', [], [{'return_trace'}]}]).
{ok,[{matched,nonode@nohost,1},{saved,1}]}
对erlang:send_after()
6> erlang:send_after(1000, self(), {}).
#Ref<0.0.0.43>
7> erlang:send_after(1000, self(), {}).
#Ref<0.0.0.47>
8> erlang:send_after(1000, self(), {}).
#Ref<0.0.0.51>
最后检查表是否包含这些引用:
9> ets:tab2list(timer_dbg).
[{#Ref<0.0.0.51>},{#Ref<0.0.0.43>},{#Ref<0.0.0.47>}]
这样您就可以存储曾经调用erlang:send_after()
的任何进程创建的所有计时器引用。您可以将它们映射到erlang:read_timer()
以过滤活动计时器。
您可以以类似方式跟踪send_after
的来电。也可以在cancel_timer
上匹配并手动从表中删除已取消的引用。
此外,如果您没有消息密集型应用程序,则应该能够匹配这些计时器触发的消息和/或功能,并从列表中删除过期的引用。
答案 1 :(得分:2)
查看erl_bif_timer.c中的代码我认为崩溃转储是唯一可以找到所有BIF计时器列表的地方。 : - )
答案 2 :(得分:2)
我今天遇到跟踪计时器的必要性。
它正在生产中,所以我不想使用dbg。这些是erlang:timers所以我之前的解决方案毫无用处。
相反,我从binary_to_list(erlang:system_info(info))分析了nbif_timer参数。
我相信(尚未确认),它会报告为计时器分配的内存。在我的系统x64上,它将是17个8字节= 136字节的字。
监控此值可清楚地显示系统何时设置大量计时器。
享受。
答案 3 :(得分:1)
这是一个黑客,但使用:ets:tab2list(timer_tab)。对于两个定时器它持有:
ets:tab2list(timer_tab).
[{{1288384968923398,#Ref<0.0.0.30>},
timeout,
{erlang,integer_to_list,[23]}},
{{23334621698390115688,#Ref<0.0.0.189>},
timeout,
{erlang,integer_to_list,[23]}}]
答案 4 :(得分:0)
你可以保存send_after,aply_after等返回的引用,并使用erlang:read_timer来检查它是否还在等待(如果定时器被取消或者不再等待,read_timer返回false)