ubf解析 その1
まずは動かしてみる
ubfは仕様の文章化が進んでいないので,実装の動作から逆に仕様を探って行く必要があります.erlangのプログラムを解析するのが私には難しいので,テストケースを追いかけてみました.調べているのは
$ git log
commit 458aa25abcd14473b8723db1f6d377dd4fc4a704
Author: Joseph Wayne Norton
Date: Tue Jun 29 20:46:41 2010 +0900UBF requires Erlang/OTP R13B01 or newer
...
というバージョンです.
make check
installまでの手順でmake checkする項目がありました.
$ make -n check rm -f ./*.log env ERL_MAX_ETS_TABLES=10007 erl +A 64 +K true -smp auto \ +Mis true -sname runerl1 -pz ../ebin \ -kernel net_ticktime 60 -config ../priv/sys \ -pz ./Unit-Test-Files -pz ./Unit-EUnit-Files \ -pz ./Unit-Quick-Files -noinput -noshell \ -pz ./Unit-Test-Files -pz ./Unit-EUnit-Files \ -s ubf_test tests \ -s test_ubf tests \ -s test_ebf tests \ -s test_etf tests \ -s stateless_plugin_test do_eunit \ -s stateful_plugin_test do_eunit \ -s erlang halt \ > ./check.log
ずいぶん沢山引数がありますが,ざっとみてみると
引数 | 概要 |
---|---|
env ERL_MAX_ETS_TABLES=10007 | 標準1400のETSテーブルの数を10007に |
+A 64 | async thread pool数 |
+K true | emuで使えるようならkernel poll機能を使う |
-smp auto | SMPモードで起動 |
+Mis true | Status over allocated memoryをemuで保持 |
-sname runerl1 | dnsが動作してない環境で分散erlangを実行 |
-pz ../ebin | 検索パスの最後に../ebinを追加 |
-kernel net_ticktime 60 | 分散ノードの死活監視時間間隔 |
-config ../priv/sys | 設定として ../priv/sys.config を使う |
-noinput | 入力を受け付けない |
-noshell | shellなしで起動する |
-s ubf_test tests | ubf_test ファイルのtestsメソッドを実行 |
という意味になっています.これを元に,一番簡単そうなubf_test.erlのtest9というメソッドを
test9() -> test_ubf({abc,"kdjhkshfkhfkhsfkhaf", [a,c,d]}).
実行してみると,
$ env ERL_MAX_ETS_TABLES=10007 erl +A 64 +K true \ -smp auto +Mis true -sname runerl1 -pz ../ebin \ -kernel net_ticktime 60 -config ../priv/sys \ -pz ./Unit-Test-Files -pz ./Unit-EUnit-Files \ -pz ./Unit-Quick-Files -noinput -noshell \ -s ubf_test test9 -s erlang halt encode test #Bin=49 L={'abc',#102&97&104&107&102&115&104&107&102&104&107&102&104&115&107&104&106&100&107&,#'d'&'c'&'a'&}$ ubf size =99 Identical
となりました.この実行結果はubr_test.erlの
test_ubf(T) -> B = term_to_binary(T), io:format("encode test #Bin=~p~n",[size(B)]), L = encode(T), %% io:format("L=~s~n",[L]), io:format("ubf size =~p~n",[length(L)]), Val = decode(L), case Val of {ok, T, _} -> io:format("Identical~n"); X -> io:format("Differences (~p)~n", [X]), io:format("Val=~p~n",[T]) end.
からio:formatのコメントアウトをはずした状態で実行しています.encodeしてdecodeした結果が元と同じになるのは当然ですね.
$ perl -le 'print join ",", unpack "C*", "kdjhkshfkhfkhsfkhaf"' 107,100,106,104,107,115,104,102,107,104,102,107,104,115,102,107,104,97,102
とやってみてみると,文字もスタックにおしりから積まれている状況がわかります.
サーバを動かしてみる
元の論文にも出ていたfile serverを動かして,今回の記事を終わりにします.file serverの動かし方はREADMEにもでてないし,Makefileにも書いてなかったので試行錯誤の末に見つけました.参考にしたのはfile_client.erlに書いてあったコメントです.ただ,このコメントもそのままでは動きませんでした.
$ env ERL_MAX_ETS_TABLES=10007 erl +A 64 +K true \ -smp auto +Mis true -sname runerl1 -pz ../ebin \ -kernel net_ticktime 60 -config ../priv/sys \ -pz ./Unit-Test-Files Erlang R13B04 (erts-5.7.5) [source] [64-bit] [rq:1] [async-threads:64] [kernel-poll:true] Eshell V5.7.5 (abort with ^G) (runerl1@foo)1> ubf_server:start([file_plugin],file_client:defaultPort()). true (runerl1@foo)2> file_client:test(). DEBUG: Arg [] Pid <0.45.0> Info = {ok,{'#S',"I am a mini file server"}} ls: H_Data myFirstData0_is_not_used ls: Env <0.45.0> Files=["check.log.20100708","erl_crash.dump","contract_yecc.erl", "contract_lex.erl","ubf_utils.erl","ubf_server.erl", "ubf_plugin_stateless.erl","ubf_plugin_stateful.erl", "ubf_plugin_meta_stateless.erl","ubf_plugin_meta_stateless.con", "ubf_plugin_meta_stateful.erl","ubf_plugin_meta_stateful.con", "ubf_plugin_meta.con","ubf_plugin_handler.erl","ubf_driver.erl", "ubf_client.erl","ubf.erl","proc_utils.erl","proc_socket_server.erl", "ebf_driver.erl","ebf.erl","contracts_abnf.erl","contracts.erl", "contract_yecc.yrl","contract_proto.erl","contract_parser.erl", "contract_manager_tlog.erl","contract_manager.erl","contract_lex.xrl", "contract_driver.erl","Unit-Test-Files","Unit-EUnit-Files","Makefile"] got 8897 bytes for ubf.erl test worked ok (runerl1@foo)3>
正しく動いているようです.ついでに,サーバにtelnetからアクセスしてみると,file_client:defaultPort()が2000なので,
$ telnet localhost 2000 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. {'ubf1.0',"meta_server"," See http://www.sics.se/~joe/ubf/ for details of this service. See http://github.com/norton/ubf for source code extensions available as part of the larger OSS community. Type 'info'$ for information "}$ 'info'$ {"I am a meta server - type 'help'$ ... to find out what I can do",'start'}$ 'help'$ {1100~ This server speaks Universal Binary Format 1.0 See http://www.sics.se/~joe/ubf.html See http://github.com/norton/ubf/tree/master for some source code extensions available as part of the larger OSS community. UBF servers are introspective - which means the servers can describe themselves. The following commands are always available: 'help'$ This information 'info'$ Short information about the current service 'description'$ Long information about the current service 'services'$ A list of available services 'contract'$ Return the service contract (Note this is encoded in UBF) To start a service: {'startSession', "Name", Arg} Name should be one of the names in the services list. Arg is an initial argument for the Name service and is specific to that service; use 'foo' or # (the empty list) if the service ignores this argument. Warning: Without reading the documentation you might find the output from some of these commands difficult to understand :-) ~,'start'}$ 'services'$ {#"file_server"&,'start'}$ {'startSession',"file_server",#}$ {{'ok',"I am a mini file server"},'start'}$ 'ls'$ {{'files',#"Makefile"&"Unit-EUnit-Files"&" Unit-Test-Files"&"contract_driver.erl"&"contract_lex.xrl"&" contract_manager.erl"&"contract_manager_tlog.erl"&" contract_parser.erl"&"contract_proto.erl"&" contract_yecc.yrl"&"contracts.erl"&"contracts_abnf.erl"&" ebf.erl"&"ebf_driver.erl"&"proc_socket_server.erl"&" proc_utils.erl"&"ubf.erl"&"ubf_client.erl"&" ubf_driver.erl"&"ubf_plugin_handler.erl"&" ubf_plugin_meta.con"&"ubf_plugin_meta_stateful.con"&" ubf_plugin_meta_stateful.erl"&" ubf_plugin_meta_stateless.con"&" ubf_plugin_meta_stateless.erl"&"ubf_plugin_stateful.erl"&" ubf_plugin_stateless.erl"&"ubf_server.erl"&"ubf_utils.erl"&" contract_lex.erl"&"contract_yecc.erl"&"erl_crash.dump"&" check.log.20100708"&},'start'}$
となって正しく動いていることがわかります(画面の都合上適宜改行を入れています).次は中身の解析に進みます.