cover的原理及其启示
October 10th, 2009
原创文章,转载请注明: 转载自系统技术非业余研究
本文链接地址: 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.
Recent Comments