Archive

Posts Tagged ‘cover’

如何找出异常所在的行(新思路)

April 21st, 2010 3 comments

原创文章,转载请注明: 转载自系统技术非业余研究

本文链接地址: 如何找出异常所在的行(新思路)

在Erlang-china的邮件列表上看到这样的问题:

我的服务经常发生这样的错误,举例:
Error in process <0.33.0> with exit value: {badarg,[{erlang,’++’,[undefined,[{“37”}]]},{groups,doWork,1},
{groups,doWork,1},{groups,manage_clients,1}]}

大意明白,但问题是我使用匹配机制时没考虑到多个函数”doWork/1″出错无法定位到其中一个,这该如何是好?
Erlang是否会像其它语言一样提示某一行出错?

这个问题确实很常见, Erlang的运行期没有给出出错的具体行数, 这给我们定位问题带来了很大的麻烦.

有先驱给出了这样的解决方案 http://mryufeng.javaeye.com/blog/368507 但是这个模块已经很老了, 过时不维护了.

这里我给出另外一个方案, 利用erlang现有的模块来实现的: cover + dbg

cover的工作原理可以参考这篇文章 http://mryufeng.javaeye.com/blog/482204.

原理就是cover编译过的模块会在每行执行前, 先执行ets:update_counter(cover_internal_data_table,{bump,Mod,Fun,1,1,Line},1) 来更新模块某行的执行次数.
那么我们只要截取 ets:update_counter这个动作, 我们就知道改模块最后的执行行, 也就是异常所在的行.

Ok, 原理介绍完毕, 上菜.

[root@centos ~]# cat line.erl

-module(line).
-export([dbg/1]).
-include_lib("stdlib/include/ms_transform.hrl").

dbg(Mod)->
    cover:compile(Mod),
    dbg:tracer(),
    dbg:p(all, [call]),
    dbg:tpl(ets,
            update_counter,
            dbg:fun2ms(fun([_,{bump,Mod,_,_,_,_},1]) ->
                               return_trace()
                       end)),
    ok.

[root@centos ~]# cat hello.erl

-module(hello).
-export([start/0]).

start()->
    a=a,
    A=2,
    C=3,
    A=C-1,
    C=A+1,
    io:format("hello world~n",[]),
    test(C),
    ok.


test(C)->
    A=4,
    A=C,  % Error is on this line.
    ok.

我们可以看到这个hello模块会在hello:test发生异常, A=C这个地方是具体位置. 现在让我们找到行号:

[root@centos ~]# erl
Erlang R13B02 (erts-5.7.3) [source] [64-bit] [smp:2:2] [rq:2] [async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.7.3  (abort with ^G)
1> line:dbg(hello).
ok
2> hello:start().
hello world
** exception error: no match of right hand side value 3
     in function  hello:test/1
     in call from hello:start/0
4> (<0.34.0>) call ets:update_counter(cover_internal_data_table,{bump,hello,start,0,1,5},1)
(<0.34.0>) returned from ets:update_counter/3 -> 1
(<0.34.0>) call ets:update_counter(cover_internal_data_table,{bump,hello,start,0,1,6},1)
(<0.34.0>) returned from ets:update_counter/3 -> 1
(<0.34.0>) call ets:update_counter(cover_internal_data_table,{bump,hello,start,0,1,7},1)
(<0.34.0>) returned from ets:update_counter/3 -> 1
(<0.34.0>) call ets:update_counter(cover_internal_data_table,{bump,hello,start,0,1,8},1)
(<0.34.0>) returned from ets:update_counter/3 -> 1
(<0.34.0>) call ets:update_counter(cover_internal_data_table,{bump,hello,start,0,1,9},1)
(<0.34.0>) returned from ets:update_counter/3 -> 1
(<0.34.0>) call ets:update_counter(cover_internal_data_table,{bump,hello,start,0,1,10},1)
(<0.34.0>) returned from ets:update_counter/3 -> 1
(<0.34.0>) call ets:update_counter(cover_internal_data_table,{bump,hello,start,0,1,11},1)
(<0.34.0>) returned from ets:update_counter/3 -> 1
(<0.34.0>) call ets:update_counter(cover_internal_data_table,{bump,hello,test,1,1,16},1)
(<0.34.0>) returned from ets:update_counter/3 -> 1
(<0.34.0>) call ets:update_counter(cover_internal_data_table,{bump,hello,test,1,1,17},1)   %这里我们看到出错的行号
(<0.34.0>) returned from ets:update_counter/3 -> 1
3> 
BREAK: (a)bort (c)ontinue (p)roc info (i)nfo (l)oaded
       (v)ersion (k)ill (D)b-tables (d)istribution

我们可以看到最后一次执行hello模块的行数是17.

Bingo!

Post Footer automatically generated by wp-posturl plugin for wordpress.

cover的原理及其启示

October 10th, 2009 Comments off

原创文章,转载请注明: 转载自系统技术非业余研究

本文链接地址: cover的原理及其启示

我们先看下cover模块的功能:

The module cover provides a set of functions for coverage analysis of Erlang programs, counting how many times each executable line of code is executed when a program is run.

那它是如何做到的呢?

它是这样实现的: cover一个模块的时候要先编译, 这个过程中, 根据模块的abstract code 里面的行号,在每个有效的语句前面插入一个 ets:update_couter() 语句,这样编译出来的模块运行的时候,我们就可以收集到每个有效行的运行信息。

我hack了下cover.erl:

yu-fengdemacbook-2:src yufeng$ diff cover.erl cover_orig.erl 
1242,1243c1242
<     {ok, Module, Binary} = compile:forms(Forms, [debug_info]),
<     io:format("abstract code: ~n~p~n", [get_abstract_code(Module, Binary)]),
---
>     {ok, Module, Binary} = compile:forms(Forms, []),

yu-fengdemacbook-2:~ yufeng$ cat float.erl
-module(float).

-export([new/1,update/3]).

new(N) ->
       hipe_bifs:bytearray(N*8,0).

update(Arr,N,Float) ->
       <<A1,A2,A3,A4,A5,A6,A7,A8>> = <<Float/float>>,
       Start=N*8,
       hipe_bifs:bytearray_update(Arr,Start,A1),
       hipe_bifs:bytearray_update(Arr,Start+1,A2),
       hipe_bifs:bytearray_update(Arr,Start+2,A3),
       hipe_bifs:bytearray_update(Arr,Start+3,A4),
       hipe_bifs:bytearray_update(Arr,Start+4,A5),
       hipe_bifs:bytearray_update(Arr,Start+5,A6),
       hipe_bifs:bytearray_update(Arr,Start+6,A7),
       hipe_bifs:bytearray_update(Arr,Start+7,A8).
yu-fengdemacbook-2:~ yufeng$ otp_src_R13B02-1/bin/erl
Erlang R13B02 (erts-5.7.3) [source][/source][/source] [smp:2:2] [rq:2] [async-threads:0] [kernel-poll:false]

Eshell V5.7.3  (abort with ^G)
1> cover:start().
{ok,<0.33.0>}
2> cover:compile(float).
abstract code:
{raw_abstract_v1,
    [{attribute,1,file,{"/Users/yufeng/float.erl",1}},
     {attribute,1,module,float},
     {attribute,3,export,[{new,1},{update,3}]},
     {function,5,new,1,
         [{clause,5,
              [{var,5,'N'}],
              [],
              [{call,0,
                   {remote,0,{atom,0,ets},{atom,0,update_counter}},
                   [{atom,0,cover_internal_data_table},
                    {tuple,0,
                        [{atom,0,bump},
                         {atom,0,float},
                         {atom,0,new},
                         {integer,0,1},
                         {integer,0,1},
                         {integer,0,6}]},
                    {integer,0,1}]},

%% 我们清楚的看到 ets:update_counter()的调用

              {call,6,
                   {remote,6,{atom,6,hipe_bifs},{atom,6,bytearray}},
                   [{op,6,'*',{var,6,'N'},{integer,6,8}},{integer,6,0}]}]}]},
     {function,8,update,3,
         [{clause,8,
              [{var,8,'Arr'},{var,8,'N'},{var,8,'Float'}],
              [],
              [{call,0,
                   {remote,0,{atom,0,ets},{atom,0,update_counter}},
                   [{atom,0,cover_internal_data_table},
                    {tuple,0,
                        [{atom,0,bump},
                         {atom,0,float},
                         {atom,0,update},
                         {integer,0,3},
                         {integer,0,1},
                         {integer,0,9}]},
                    {integer,0,1}]},
               {match,9,
                   {bin,9,
                       [{bin_element,9,{var,9,'A1'},default,default},
                        {bin_element,9,{var,9,'A2'},default,default},
                        {bin_element,9,{var,9,'A3'},default,default},
                        {bin_element,9,{var,9,'A4'},default,default},
                        {bin_element,9,{var,9,'A5'},default,default},
                        {bin_element,9,{var,9,'A6'},default,default},
                        {bin_element,9,{var,9,'A7'},default,default},
                        {bin_element,9,{var,9,'A8'},default,default}]},
                   {bin,9,[{bin_element,9,{var,9,'Float'},default,[float]}]}},
               {call,0,
                   {remote,0,{atom,0,ets},{atom,0,update_counter}},
                   [{atom,0,cover_internal_data_table},
                    {tuple,0,
                        [{atom,0,bump},
                         {atom,0,float},
                         {atom,0,update},
                         {integer,0,3},
                         {integer,0,1},
                         {integer,0,10}]},
                    {integer,0,1}]},
               {match,10,
                   {var,10,'Start'},
                   {op,10,'*',{var,10,'N'},{integer,10,8}}},
               {call,0,
                   {remote,0,{atom,0,ets},{atom,0,update_counter}},
                   [{atom,0,cover_internal_data_table},
                    {tuple,0,
                        [{atom,0,bump},
                         {atom,0,float},
                         {atom,0,update},
                         {integer,0,3},
                         {integer,0,1},
                         {integer,0,11}]},
                    {integer,0,1}]},
               {call,11,
                   {remote,11,{atom,11,hipe_bifs},{atom,11,bytearray_update}},
                   [{var,11,'Arr'},{var,11,'Start'},{var,11,'A1'}]},
               {call,0,
                   {remote,0,{atom,0,ets},{atom,0,update_counter}},
                   [{atom,0,cover_internal_data_table},
                    {tuple,0,
                        [{atom,0,bump},
                         {atom,0,float},
                         {atom,0,update},
                         {integer,0,3},
                         {integer,0,1},
                         {integer,0,12}]},
                    {integer,0,1}]},
               {call,12,
                   {remote,12,{atom,12,hipe_bifs},{atom,12,bytearray_update}},
                   [{var,12,'Arr'},
                    {op,12,'+',{var,12,'Start'},{integer,12,1}},
                    {var,12,'A2'}]},
               {call,0,
                   {remote,0,{atom,0,ets},{atom,0,update_counter}},
                   [{atom,0,cover_internal_data_table},
                    {tuple,0,
                        [{atom,0,bump},
                         {atom,0,float},
                         {atom,0,update},
                         {integer,0,3},
                         {integer,0,1},
                         {integer,0,13}]},
                    {integer,0,1}]},
               {call,13,
                   {remote,13,{atom,13,hipe_bifs},{atom,13,bytearray_update}},
                   [{var,13,'Arr'},
                    {op,13,'+',{var,13,'Start'},{integer,13,2}},
                    {var,13,'A3'}]},
               {call,0,
                   {remote,0,{atom,0,ets},{atom,0,update_counter}},
                   [{atom,0,cover_internal_data_table},
                    {tuple,0,
                        [{atom,0,bump},
                         {atom,0,float},
                         {atom,0,update},
                         {integer,0,3},
                         {integer,0,1},
                         {integer,0,14}]},
                    {integer,0,1}]},
               {call,14,
                   {remote,14,{atom,14,hipe_bifs},{atom,14,bytearray_update}},
                   [{var,14,'Arr'},
                    {op,14,'+',{var,14,'Start'},{integer,14,3}},
                    {var,14,'A4'}]},
               {call,0,
                   {remote,0,{atom,0,ets},{atom,0,update_counter}},
                   [{atom,0,cover_internal_data_table},
                    {tuple,0,
                        [{atom,0,bump},
                         {atom,0,float},
                         {atom,0,update},
                         {integer,0,3},
                         {integer,0,1},
                         {integer,0,15}]},
                    {integer,0,1}]},
               {call,15,
                   {remote,15,{atom,15,hipe_bifs},{atom,15,bytearray_update}},
                   [{var,15,'Arr'},
                    {op,15,'+',{var,15,'Start'},{integer,15,4}},
                    {var,15,'A5'}]},
               {call,0,
                   {remote,0,{atom,0,ets},{atom,0,update_counter}},
                   [{atom,0,cover_internal_data_table},
                    {tuple,0,
                        [{atom,0,bump},
                         {atom,0,float},
                         {atom,0,update},
                         {integer,0,3},
                         {integer,0,1},
                         {integer,0,16}]},
                    {integer,0,1}]},
               {call,16,
                   {remote,16,{atom,16,hipe_bifs},{atom,16,bytearray_update}},
                   [{var,16,'Arr'},
                    {op,16,'+',{var,16,'Start'},{integer,16,5}},
                    {var,16,'A6'}]},
               {call,0,
                   {remote,0,{atom,0,ets},{atom,0,update_counter}},
                   [{atom,0,cover_internal_data_table},
                    {tuple,0,
                        [{atom,0,bump},
                         {atom,0,float},
                         {atom,0,update},
                         {integer,0,3},
                         {integer,0,1},
                         {integer,0,17}]},
                    {integer,0,1}]},
               {call,17,
                   {remote,17,{atom,17,hipe_bifs},{atom,17,bytearray_update}},
                   [{var,17,'Arr'},
                    {op,17,'+',{var,17,'Start'},{integer,17,6}},
                    {var,17,'A7'}]},
               {call,0,
                   {remote,0,{atom,0,ets},{atom,0,update_counter}},
                   [{atom,0,cover_internal_data_table},
                    {tuple,0,
                        [{atom,0,bump},
                         {atom,0,float},
                         {atom,0,update},
                         {integer,0,3},
                         {integer,0,1},
                         {integer,0,18}]},
                    {integer,0,1}]},
               {call,18,
                   {remote,18,{atom,18,hipe_bifs},{atom,18,bytearray_update}},
                   [{var,18,'Arr'},
                    {op,18,'+',{var,18,'Start'},{integer,18,7}},
                    {var,18,'A8'}]}]}]},
     {eof,19}]}
{ok,float}

这个故事告诉我们对 erlang系统的跟踪除了trace机制以为, 我们还可以用parse transform在编译的时候加入我们想要的代码,达到跟踪, 了解系统的目的。具体的可以参考 compiler模块 parse_transform的文档。

Post Footer automatically generated by wp-posturl plugin for wordpress.

Categories: Erlang探索 Tags: , ,