我们先看下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'}],
[],
[[color=red]{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}]},
[/color]
%% 我们清楚的看到 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的文档。
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'}],
[],
[[color=red]{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}]},
[/color]
%% 我们清楚的看到 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的文档。