1. 程式人生 > >系統技術非業餘研究 » cover的原理及其啟示

系統技術非業餘研究 » 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] [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.