Archive

Archive for October, 2009

Erlang动态编译加载模块

October 10th, 2009 Comments off

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

本文链接地址: Erlang动态编译加载模块

ejabberd最新的版本有个模块叫做 dynamic_compile, 支持从string动态加载一个模块。有了这个功能我们就可以很方便的动态生成一个模块,加入到我们的运行期。我想的有以下几个功能:

1. const 模块

2. 如日志系统的级别:

log(S) when 0 >  1 ->
   do_log(S);
log(_)->
   skip.

这样的模块 编译的时候 会把前面的就省去了判断, 直接由compiler去掉了,因为when永远不满足。

试验如下:

yu-fengdemacbook-2:~ yufeng$ erl
Erlang (BEAM) emulator version 5.6.5 [source][/source][/source] [async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.6.5  (abort with ^G)
1> {Mod,Code} = dynamic_compile:from_string("-module(test).\n-export([start/0]).\n start()->ok.\n"), code:load_binary(Mod, "test.erl", Code).
{module,test}
2> m(test).
Module test compiled: Date: October 6 2009, Time: 08.43
Compiler options:  []
Object file: test.erl
Exports:
         module_info/0
         module_info/1
         start/0
ok
3> test:start().
ok
4> 

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

Categories: Erlang探索 Tags: ,

dets在64位系统还是有可耻的2G限制

October 10th, 2009 4 comments

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

本文链接地址: dets在64位系统还是有可耻的2G限制

原本以为在 64位操作系统下 文件的读写指针都改成64位的 避免了2G的限制。经过测试 disk_log和file都支持超过2G的数据文件,但是 dets还是可耻的失败了。 经过查看源码 有2个问题:

1. key segment的问题。 dets在设计的时候 是针对32位的 那么它的空间限制就是2G, 所以在计算最大的段的数的最大的偏移时候 干脆在代码里面写死了2G。

2. 偏移的问题。 每个key对应的value在dets_v9文件格式中的偏移值是用 <> 这样的格式来表示的。 所以如果改变成64位 那么文件格式就完全不兼容了。

结论: dets支持2G以上没戏了, 而且同样影响到mnesia.

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

Categories: Erlang探索 Tags:

Erlang动态编译加载模块

October 6th, 2009 Comments off

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

本文链接地址: Erlang动态编译加载模块

ejabberd最新的版本有个模块叫做 dynamic_compile, 支持从string动态加载一个模块。有了这个功能我们就可以很方便的动态生成一个模块,加入到我们的运行期。我想的有以下几个功能:

1. const 模块

2. 如日志系统的级别:

log(S) when 0 >  1 ->
   do_log(S);
log(_)->
   skip.

这样的模块 编译的时候 会把前面的就省去了判断, 直接由compiler去掉了,因为when永远不满足。

试验如下:

yu-fengdemacbook-2:~ yufeng$ erl
Erlang (BEAM) emulator version 5.6.5 [source] [async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.6.5  (abort with ^G)
1> {Mod,Code} = dynamic_compile:from_string("-module(test).\n-export([start/0]).\n start()->ok.\n"), code:load_binary(Mod, "test.erl", Code).
{module,test}
2> m(test).
Module test compiled: Date: October 6 2009, Time: 08.43
Compiler options:  []
Object file: test.erl
Exports:
         module_info/0
         module_info/1
         start/0
ok
3> test:start().
ok
4> 

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

Categories: Erlang探索 Tags:

erlang的profile工具原理和优缺点

October 6th, 2009 Comments off

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

本文链接地址: erlang的profile工具原理和优缺点

erlang的tools application下包含了一系列的profile工具, 包括 eprof cprof fprof, 具体的使用可以参看文档和<< erlang effective guide>>.

我这里要说的是他们的工作原理。 这些模块的核心都是根据erlang的trace机制实现的。在模块执行的时候,trace机制会通知那个函数被调用 返回。根据这些信息就可以统计出来函数调用的频度,调用栈等。

但是利用这个机制会有严重的性能损失。因为每个函数调用都要发送一条trace信息,每个trace 信息会引起上下文切换 而且要耗费2-3的时间。这个对大型的系统是不可接受的。

所以知道这些原理以后, 我们在profile大型的系统的时候,我们可以在dbg模块的帮助下, 只收集我们感兴趣的东西,而且严格限定范围,避免对系统造成大的干扰,这样收集出来的东西才有意义。

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

Categories: 网络编程 Tags:

cover的原理及其启示

October 6th, 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] [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:

binary的常量优化

October 6th, 2009 Comments off

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

本文链接地址: binary的常量优化

erlang的binary在这个网络程序里面占着非常重要的地位,所以otp团队采用了非常多的优化手段包括:
1. binary操作对应着 opcode
2. 根据生命期和作用,有4种类型的binary
3. hipe优化,把bs_操作直接翻译成asm指令
4. 编译器层面消除无必须的操作。

下面的例子就是演示4的特性:

yu-fengdemacbook-2:~ yufeng$ cat bin.erl
-module(bin).
-export([start/1]).

start(A)->
    B1= <<12>>,
    B2 = <<B1/binary, 5.0/float>>,
    B3= <<B2/binary,  "yes">>,
% B3的值是预先可以知道的, 无需一步步的构造

    <<"abcd", 3:32,B3:128/binary,_/binary>> = <<"abcd1234",A/binary,2:32, 8773:64, "a", 5.0/float>>.

% 2:32, 8773:64, "a", 5.0/float 这些都是预先知道的 直接翻译成二进制流。
yu-fengdemacbook-2:~ yufeng$ erlc +"'S'" bin.erl
yu-fengdemacbook-2:~ yufeng$ cat bin.S
{module, bin}.  %% version = 0

{exports, [{module_info,0},{module_info,1},{start,1}]}.

{attributes, []}.

{labels, 8}.


{function, start, 1, 2}.
  {label,1}.
    {func_info,{atom,bin},{atom,start},1}.
  {label,2}.
    {move,{integer,0},{x,1}}.
    {gc_bif,byte_size,{f,0},2,[{x,0}],{x,2}}.
    {bs_add,{f,0},[{x,1},{x,2},1],{x,1}}.
    {bs_add,{f,0},[{x,1},{integer,29},1],{x,1}}.
    {bs_init2,{f,0},{x,1},0,1,{field_flags,[]},{x,1}}.
    {bs_put_string,8,{string,"abcd1234"}}.
    {bs_put_binary,{f,0},{atom,all},8,{field_flags,[unsigned,big]},{x,0}}.

%% 一步到位
    {bs_put_string,21,
                   {string,[0,0,0,2,0,0,0,0,0,0,34,69,97,64,20,0,0,0,0,0,0]}}.

    {test,bs_start_match2,{f,3},[{x,1},2,0,{x,0}]}.
    {test,bs_match_string,{f,3},[{x,0},64,{string,[97,98,99,100,0,0,0,3]}]}.
%%  一步到位

    {test,bs_get_binary2,
          {f,3},
          [{x,0},
           2,
           {integer,128},
           8,
           {field_flags,[{anno,[8,{file,"./bin.erl"}]},unsigned,big]},
           {x,2}]}.
    {test,bs_skip_bits2,
          {f,3},
          [{x,0},
           {atom,all},
           8,
           {field_flags,[{anno,[8,{file,"./bin.erl"}]},unsigned,big]}]}.
    {test,is_eq_exact,
          {f,3},
          [{x,2},{literal,<<12,64,20,0,0,0,0,0,0,121,101,115>>}]}.
%%  一步到位

    {move,{x,1},{x,0}}.
    return.
  {label,3}.
    {badmatch,{x,1}}.


{function, module_info, 0, 5}.
  {label,4}.
    {func_info,{atom,bin},{atom,module_info},0}.
  {label,5}.
    {move,{atom,bin},{x,0}}.
    {call_ext_only,1,{extfunc,erlang,get_module_info,1}}.


{function, module_info, 1, 7}.
  {label,6}.
    {func_info,{atom,bin},{atom,module_info},1}.
  {label,7}.
    {move,{x,0},{x,1}}.
    {move,{atom,bin},{x,0}}.
    {call_ext_only,2,{extfunc,erlang,get_module_info,2}}.

所以我们在使用binary的时候, 尽可能的利用这个特性。

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

Categories: Erlang探索 Tags:

如何用gdb调试erlang运行期(高级)

October 6th, 2009 Comments off

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

本文链接地址: 如何用gdb调试erlang运行期(高级)

前些天在erlang的源码里面闲逛的时候发现了 bin目录下的cerl,一看原来是个调试的高级货。

我之前写过一篇文章http://mryufeng.javaeye.com/blog/113852 如何调试erlang 但是这是土八路的方法, cerl才是现代化工业。

# This is a script to start Erlang/OTP for debugging. PATH is set to
# include this script so if slave nodes are started they will use this
# script as well.
#
# usage: cerl [ OPTIONS ] [ ARGS ]
#
# The OPTIONS are
#
# -rootdir $MYROOTDIR
# Run an installed emulator built from this source
# -debug Run debug compiled emulator
# -gdb Run the debug compiled emulator in emacs and gdb.
# You have to start beam in gdb using “run”.
# -break F Run the debug compiled emulator in emacs and gdb and set break.
# The session is started, i.e. “run” is already don for you.
# -xxgdb FIXME currently disabled
# -purify Run emulator compiled for purify
# -quantify Run emulator compiled for quantify
# -purecov Run emulator compiled for purecov
# -gcov Run emulator compiled for gcov
# -valgrind Run emulator compiled for valgrind
# -lcnt Run emulator compiled for lock counting
# -nox Unset the DISPLAY variable to disable us of X Windows
#

要使用cerl 我们最好准备个调试版本的erlang。R13B 修正了些编译错误,可以编译出debug版本的系统:./configure && make TYPE=debug && make

这样就生成了个beam.debug的运行期。

我们要调试的时候 就可以在otp的binx目录下运行 cerl -debug -gdb -break main

这时候cerl自动会加载 emacs 启动 gud, 整个过程都是自动的。但是这个脚本有点小问题, gud模型下没有把源码和当前对应的调试对应起来。可以通过以下方式修正:

yu-fengdemacbook-2:bin yufeng$ diff cerl cerl2
284c284
<     exec $EMACS --eval "(progn (gdb \"gdb $EMU\") $gdbcmd)"
---
>     exec $EMACS --eval "(progn (gdb \"gdb --annotate=3  $EMU\") $gdbcmd)"

具体的操作和界面可以参照这篇文章:
http://www.nabble.com/printing-of-Eterm%27s-from-gdb-td19240493.html

在调试的时候 我们会希望查看 eterm的值,但是由于eterm的格式非常复杂, gdb的print什么的无法满足我们的需求。 otp开发团队于是开发出了一套方法来减轻我们的负担:

1. erts/etc/unix/etp-commands 这是gdb的脚本 包含了几十个etp方法,而且文档非常详细。

2. 源码里面的 pp, ps等函数, 这些函数是专门为gdb调试开发的。 可以用gdb的 call pp(xxx)来调用。

有了这些工具 调试和研究erts变成了一件很快乐的事情!

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

Categories: 网络编程 Tags: