Sunday, March 31, 2013

えぼりゅーしょん3


この話の続き。

まだ python のプログラムはいじっていなくて、データ処理の話。

家系図は書いてみたので、次は genes のクラスタ分析をしたらどうなるか?…ということで、Rを使ってひとしきりやってみた結果。

1000updateかけた後、生存animal数 152、死亡animal数 466、残plant数 53、生存animalの平均年齢(update) 197.4、生存animalの平均保持 energy 88.5…という状態がお題。

生存animalを対象にすると、genes は8要素のベクターなので、これが152個あることになる。練習としては、まあたいしたことはないデータ量だ。

まず、階層型クラスタリングの R の hclust を使って書いてみたのがこれ。


ふーん、と思いつつ、このへんを参考にk-meansでクラスタ数2~15で変化させながら、あたりをみてみたのがこれ。

クラスタ数5くらいでいいのかしら?と思いながら分類させてみたのがこれ。


実は、Rをそれっぽく使ってみたのは初めてだったのだが、データ入出力はいまいち使い勝手が悪い(or 整理されていない)な…というのが感想だったりする。

まあ、そもそも論として、この環境で種の分化ってどう定義するの?なんて話もあるわけだし、先は長い...

Saturday, March 30, 2013

python 版 mysqlbench

python の練習の続き。

やっている仕事の関係上、python から RDBMS、特に MySQL を触る必要があることが多い(多かった...?)のだが、練習がてら、まずは素直に python-mysqldb を使い、pgbench のMySQL移植の mysqlbench を、さらに python に移植してみた。
なお、mysqlbench のオリジナルは PostgreSQLの石井さんが作った TPC-Bライクなスタンドアロン型のベンチマークツール pgbench であることは言うまでもない。

現状版がこれ↓


で、移植元は、お名前がわからないのが残念なのだが日本の MySQL ユーザ会の有志の方が作成されたこれ↓である。



まず、移植元の mysqlbench の実行結果がこんな感じなのに対して、
(微妙にエラーが出ているのは愛嬌)

$  ~/mysqlbench-0.1/mysqlbench  -U username -P password -h 192.168.1.1 -c 10 -t 1000 pgbench
starting vacuum...OPTIMIZE TABLE tellers: Commands out of sync; you can't run this command now
DELETE FROM history: Commands out of sync; you can't run this command now
OPTIMIZE TABLE history: Commands out of sync; you can't run this command now
end.
all connection OK
go
 end.

transaction type                    . . . : TPC-B (sort of)
scaling factor                      . . . : 1
number of clients                     . . : 10
number of transactions per client         : 1000
number of transactions actually processed : 10000/10000
tps (include connections establishing)  . : 264.557585
tps (exclude connections establishing)  . : 264.608737


今回の python 移植版はこんな感じになる。

$ python python-mysqlbench.py -U username -P password -h 192.168.1.1 -c 10 -t 1000 -D pgbench
all threads completed
transaction type                    . . . : TPC-B (sort of)
scaling factor                      . . . : 1
number of clients                     . . : 10
number of transactions per client         : 1000
number of transactions actually processed : 10000/10000
tps (include connections establishing)  . : 270.410621
tps (exclude connections establishing)  . : 270.462350


だいたい誤差くらいの範囲で同じ値が出るようになったようだ。

まだエラー処理が足りない(特に SQL 発行部分をちゃんと try ~ exception にしていない部分があるの)と、コマンドラインシンタックスが微妙に違う(dbname の指定方法とか...)とか、いろいろあるのだが、それはまあぼちぼちということで。(そういうわけで、まだβ版です...)

なお、また「pep8 でエラーがでるじゃねえか」とワカモノから怒られるような気がするのだが、以下の通り移植元のURLを記載した行と、大元の石井さんのPostgreSQL用のC実装のファイルヘッダに含まれる $Id$ 行がひっかかる。

$ pep8  python-mysqlbench.py
python-mysqlbench.py:9:80: E501 line too long (112 characters)
python-mysqlbench.py:24:80: E501 line too long (86 characters)

まあ、これはしかたないよねぇ...ということで。

Thursday, March 28, 2013

えぼりゅーしょん2

またこの話の続き。

今度は、これ↓

https://github.com/thatsdone/junkbox/blob/master/python/evolution.py

をもう少しいじって、animal に id をつけ、parent のidと誕生/死亡のタイミングをトラッキングできるようにしてみた。

で、やりたいのは、クラスタリングとか、そういう処理をできるようにした上で、いずれは genes まわりの仕組みを入れ替え可能にしてGAっぽくして評価してみたい…という話になるのだが、まずは id と parent を記録できるようにしたので、exact な家系図をかいてみましょうという話。

graphviz をつかって素直にプロットしただけなので、いまいちみにくいのだが、1000 update かけた時点で、生きている animal を黄色、すでに死んでしまった animal を青でいろづけして書いてみた結果がこれ。

現状は、genes が効いてくるのは turn のところで、ほぼランダムな効果でしかないようになっているので、もうすこし均一になるかも…と、思ったのだが、それでも淘汰が働いているせいか、クラスタっぽく分かれてきているようだ。噂の種の分化(?)を見るためには genes の距離関数はどうしようか…とか、TODOはあれこれ広がるのだが、まだしばらく遊べそうだということで...w

Saturday, March 23, 2013

えぼりゅーしょん

前回のこの話の続き。
ようするに、このスクリプトの話。

https://github.com/thatsdone/junkbox/blob/master/python/evolution.py

あのあと断続的にいろいろいじっていて、致命的なバグをとったり、pep8 でいろいろ出るじゃねーかとワカモノからケチをつけられてみたり、animalのトラッキングをできるようにしてみたりとか、バッチモードを追加したりとかして遊んでいたのだが、進化のメカニズムそのもののところはオリジナルから手をつけずにいた。

まあ、まだまだ python らしくするために手を入れるところはたくさんあるのだが、いったんこんなグラフが書けたのではりつけておく。





















全 animal について turn/move/eat/reproduce をかける作業を 1update として、
世界を 4000 回updateかけている。
この条件だと、animal数は150くらい、食べ物のplant数は50くらい、animal の平均寿命は 180-200 update くらいで落ち付いてくるようだ。
少なくともオリジナルの Lisp 版では種の分化が起きたりするらしく、これでも同じことが起きるはずなのだが、まだそこまでは手をつけていない。

もうちょっといじったら、進化のメカニズムの面でも、いろいろ遊んでみよう。

Tuesday, March 19, 2013

Land of Lisp とか Common Lisp とか Python とか...


巷では "Land of Lisp: Learn to Program in Lisp, One Game at a Time!" が話題(?)だが、お小遣い制なので本もなかなかなかなか買えない私だったりする。

ところで、この本に出てくるさまざまなゲームのコードは著者のサイト

http://landoflisp.com/source.html

から入手できるようになっているので、scheme 実装の一つである Gauche に移植してみた…なんていう人もいる。

http://practical-scheme.net/wiliki/wiliki.cgi?l=jp&p=Gauche%3aLandOfLisp
http://blog.practical-scheme.net/shiro/20130223-land-of-gauche

スゲー...

私も、特に evolution.lisp に心惹かれてしまっていたのだが、Lisp はいまいち 土地勘がないのと、事情により Python力(ちから)を上げないといけなかったりするので、「それじゃあ、Common Lisp のソースを解読しつつ、Python に移植してみるか?」と、くだらないことを思いついてしまったのだった...
実際やりはじめたのは、March 8, 2013 くらいだったらしい...(たぶん)

そして、できあがったのがこれ↓である。

https://github.com/thatsdone/junkbox/blob/ed2c157524f6b0d058fe5b2a9286342faf1c0e5c/python/evolution.py

HEADはこっち↓
https://github.com/thatsdone/junkbox/blob/master/python/evolution.py


現状は、無限ループをつくるのに末尾再帰を使っているところまで Lisp のコードそのまんまなのだが、一応、readline を使って prompt まわりをマシにしてみたりとか、animals と plants のダンプを表示するコマンドくらいは追加してみた。進化させてみると、それっぽく動いている(ようだ)。
暇をみて、もうちょっと Python らしくする予定。

やってみての感想は、

  • Common Lisp (だけじゃないんだろうな、たぶん)って、なんか面白い :)
  • 意外に global variables を多用するのね
  • 竹内先生のあの本をちょっとかじっていたからこそ読めたのかなぁ...

云々とか、そんな感じ。
Lisp の黒魔術をかいま見るまでは、まだまだ遠い(笑)

余談だが、OpenStack Advent Calendar のこんな記事(by @irix_jp さん)とかこんな記事(by @saito_hideki さん)とかの凄腕な人たちのネタに触発されているというのはヒミツw

しかし、Land of Lisp ほしいなぁ…(fade out)

Friday, March 8, 2013

python-dnspython を使う場合のタイムアウトについて

python から DNS を触りに行ったりしていて気が付いたことがあったのでメモ。

python には dnspython というライブラリがあって、DNS Update 機能等も含めて便利に使えるようになっている。Ubuntu/Debian では、python-dnspython という名前のパッケージを apt-get install すればよい。

さて、DNSサーバも落ちたりするわけで、そういう場合に備えて、特に自前で運用しているDNSサーバなら、単純には

/etc/resolv.conf

にこんな感じで、

nameserver  192.168.0.1
nameserver  192.168.0.2
search  example.com
options  timeout:5  attempt:1

複数DNSサーバを並べて、options で timeout やリトライ回数を書いておくとか、そういう感じかと思う。

さて、このへんから本題。
素人の私は、上記のように書いておけば、dnspython が nslookup なり dig なりのように中から参照してくれるのかと思い込んでいたのだが、そうではないのだった。

(現行stable版の 1.10.0の)ドキュメント


を読んでみてもよくわからないので、最初は「もしかしたら、timeoutを指定できないのか?」と思ったら、とんだ濡れ衣で、ちゃんとできた。:o

以下のような感じで、名前解決なら Resolver オブジェクトを作ったあと、timeout とか lifetime といった attribute に秒単位で設定すればよい。

    def lookup(hostname):
        import dns.resolver
           :
        resolver = dns.resolver.Resolver()
        resolver.timeout = 5.0
        resolver.lifetime = 5.0
           :

実は、このへんは以下で参照可能なソースの


以下のあたりのResolver クラスの定義をつらつら眺めていて判明したのだった。:o

 394 -class Resolver(object): 
 395      """DNS stub resolver 
    (snip)
 408      @ivar timeout: The number of seconds to wait for a response from a 
 409      server, before timing out. 
 410      @type timeout: float 
 411      @ivar lifetime: The total number of seconds to spend trying to get an 
 412      answer to the question.  If the lifetime expires, a Timeout exception 
 413      will occur. 
 414      @type lifetime: float 

ところで、ここでもう一つ注意がある。

timeout や lifetime の型は float であって int ではないことに注意。実際、上記のサンプルの 5.0 のところは、最初 5 と書いていたのだが、私が動かしてみた限りは思った通りに timeout してくれず、(たぶん)デフォルト値の 30 秒のままだった :(

文字列と判断されて無視されたのかなぁ...とか、つらつら思いつつ、初心者の日々は流れていくのであった。

Thursday, March 7, 2013

GNU ld の--no-as-neededと--as-neededオプションについて


GNU ld の挙動で気が付いたことがあったのでメモ。

例によって少し前置きが長くなるが、端的にいえば、system call 等の他人が提供する関数呼び出しをフックして、処理を横取りしたくなることがたまにある。

例えば、system call の write(2) をフックしたいなら、以下のようなプログラムを書くことになる。

  1 #include <dlfcn.h>  /* これが必要 */
  2 #include <unistd.h> /* 今回hookしたい write(2)が定義されている */
  3 #include <stdio.h>
  4 #include <errno.h>
  5
    ↓ hook したい関数、この場合はwrite(2) と同じようにする。
  6 ssize_t (*org)(int fd, const void *buf, size_t count) = 0;
  7
    ↓main()実行の前に呼ばれる初期化ルーチン
  8 __attribute__((constructor))
  9 static void init_hook()
 10 {
 11     org = (ssize_t(*)(int, const void *, size_t))dlsym(RTLD_NEXT, "write");
 12     printf("%s: constructor called! %p\n", __func__, org);
 13 }
 14
    ↓main()から抜けた後に呼ばれる終了ルーチン
 15 __attribute__((destructor))
 16 static void fini_hook()
 17 {
 18     printf("%s: destructor called!\n", __func__);
 19 }
 20
    ↓write(2)の処理を横取りするルーチン
 21 ssize_t write(int fd, const void *buf, size_t count)
 22 {
 23     int ret;
 24
 25     /*
 26      * Do what you like to do here
 27      */
 28     printf("%s: hook!\n", __func__);
 29     ret = (int)org(fd, buf, count);
 30     /*
 31      * Do what you like to do here
 32      */
 33     return ret;
 34
 35 }

これを、hook.c として保存しておき、hook.so という名前の shared object を作成する。この際、初期化ルーチンの中で、オリジナルの write(2)のエントリを探すのに使っている dlsym(3)のために、-ldl オプション(と -D_GNU_SOURCE)が必要になる。

  $gcc  -D_GNU_SOURCE  -fPIC -shared -ldl hook.c -o hook.so

ぐぐると日本語の解説もいくつか出てくるので参照してほしいのだが、上記のようにして shared object 作り、プログラムの実行時に LD_PRELOAD 環境変数で読み混ませてやれば目的を達することができる。

…はずだった。これまでは。

しかし、上記を(少なくとも) Ubuntu 12.10 上で作成して、実行してみると

  $ env LD_PRELOAD=./hook.so cat /tmp/foo
  cat: symbol lookup error: ./hook.so: undefined symbol: dlsym

…と、エラーになってしまう。
オリジナルの write(2)を探すのに使っていて、-ldl でちゃんと指定しているはずの dlsym()が見つからないとか言っている。

なんで?
…ということで確認してみると、当然リンクされているはずの libdl.so がリンクされていない!

  $ ldd hook.so
          linux-vdso.so.1 =>  (0x00007fff8edba000)
          libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f06d3745000)
          /lib64/ld-linux-x86-64.so.2 (0x00007f06d3d1a000)

どういうことなの?
…と、ぐぐっていて出てきた以下のトピックを読んで初めて知ったのだが、

  http://os.inf.tu-dresden.de/pipermail/l4-hackers/2011/005078.html

実は、少なくとも現行の Ubuntu ではリンカ (GNU ld) の挙動が変更されており、以下のように、--no-as-needed というオプションを指定しないといけないことが分かった。
(脱線するが、L4のMLって、まだ普通に生きているのだなぁ...と、遠い目になったのはひみつ)

  $ gcc -D_GNU_SOURCE -Wl,'--no-as-needed' -fPIC -shared -ldl hook.c -o hook.so
  $ ldd hook.so
          linux-vdso.so.1 =>  (0x00007fff5494d000)
          libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f2d72be2000)
          libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f2d72823000)
          /lib64/ld-linux-x86-64.so.2 (0x00007f2d72ffc000)

赤字部分のように、今度は意図の通り、リンクされている。

現行のデフォルトの挙動は'--no-as-needed'の逆の'--as-needed'で、これは、リンカ(ld)に -ldl 等と渡された shared object の一覧の中から、プログラムの中で実際に使われているシンボルが入っている shared object のみ実際にリンクするという意味になるようだ。

でも、ちゃんと使っているじゃん?…と思うかもしれない。
これについては、ソースレベルでは(まだ)未確認なのだが、今回の場合は、_init ルーチンの中で dlsym() を使っており、'--as-needed' の場合に探索される範囲から外れているから…というのが真相らしい。

さて、ここでもうひとつ注意がある。'--no-as-needed' をコマンドラインのどの順番で指定するかには意味がある。この例では -ldl の前に指定する必要がある。
実際、-ldl の後ろに指定して shared object を作ってみると

  $ gcc  -g -Wall -D_GNU_SOURCE -fPIC -shared -ldl -Wl,'--no-as-needed' hook.c -o hook.so
  $ ldd hook.so
          linux-vdso.so.1 =>  (0x00007fffea666000)
          libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fb722dc8000)
          /lib64/ld-linux-x86-64.so.2 (0x00007fb72339d000)

の通り、また libdl.so がリンクされなってしまうので注意のこと。


ちなみに、以下は man ld からの引用なのだが、赤字のところが --no-as-neededの場合は逆になる…ということのようだ。
# そんなの気がつかねーよ...orz

LD(1)                        GNU Development Tools                       LD(1)

NAME
       ld - The GNU linker

SYNOPSIS
       ld [options] objfile ...

DESCRIPTION
  (snip)
       --as-needed
       --no-as-needed
           This option affects ELF DT_NEEDED tags for dynamic libraries
           mentioned on the command line after the --as-needed option.
           Normally the linker will add a DT_NEEDED tag for each dynamic
           library mentioned on the command line, regardless of whether the
           library is actually needed or not.  --as-needed causes a DT_NEEDED
           tag to only be emitted for a library that satisfies an undefined
           symbol reference from a regular object file or, if the library is
           not found in the DT_NEEDED lists of other libraries linked up to
           that point, an undefined symbol reference from another dynamic
           library.  --no-as-needed restores the default behaviour.


最後に、今回作った hook.so を実際に使ってみるとどうなるかも載せておく。

write(2) を使うできるだけ単純なプログラムということで、cat を使って

$ cat /tmp/bar
FOO

というファイルを表示してみると、

$ env LD_PRELOAD=./hook.so  cat /tmp/bar
init_hook: constructor called! 0x7fd48f0228f0
write: hook!
FOO

$

ということで、冒頭のサンプルに書いてある通り、実際の write(2)が走る前に "write: hook!" と自分で追加した処理が実行されていることがわかる。

Saturday, March 2, 2013

RHELでもコネクション単位にTCPの再送タイムアウトを制御したい


前に書いた「TCPの再送タイムアウトを制御したい」の続き。

要は、コネクション単位で再送タイムアウトを調整したい件である。

国内で仕事でLinuxを使っていると、どうしても Redhat にバイアスがかかる事情があって、これはこれでなんとかしたいのだが、それはまた別の話。

それで、2/21(現地時間)にGAがアナウンスされた RedHat 6.4 では、

http://www.redhat.com/about/news/press-archive/2013/2/red-hat-announces-general-availability-of-next-minor-release-of-red-hat-enterprise-linux-6

kernel version は 2.6.32-358 まであがっている。もちろん、以下からソース(SRPM)も入手できる。

http://ftp.redhat.com/pub/redhat/linux/enterprise/6Server/en/os/SRPMS/

さて、本日のお題は例の setsockopt() で指定する TCP_USER_TIMEOUT が使えるかどうか?…ということで、さっそく上記から

  kernel-2.6.32-358.el6.src.rpm

を入手して調べてみたところ、backport されているようだ。:)

kernel.spec を眺めてみると、bugilla の ID: 819610 で feature request されたと思しき記述があるのだが、

| - [net] tcp: Add TCP_USER_TIMEOUT socket option (Jiri Benc) [819610]

一般には見られないようだ。残念…というか、まあ特定の顧客対応だったものと思われる。


余談だが、RHEL6.4 のカーネルは、2/21にGAがアナウンスされた後、2/26には RHSA が出てアップデートがかかっている。

https://rhn.redhat.com/errata/RHSA-2013-0567.html

ptrace まわりで脆弱性が見つかったらしい。

なお、さらに余談なのだが

https://lwn.net/Articles/539394/

でも触れられている

https://rhn.redhat.com/errata/RHSA-2013-0496.html

って、発行は2/21じゃん…と思ったのだが、よくみてみたらリリース版のバージョンそのものなので、これを同梱してGAということだったらしい。

#あるある...www