SystemtapでSleep sort
なんか書かねばならぬ気がしたのでやってしまった。
probe begin { foreach(i in argv) system("sleep " . argv[i]) } probe syscall.nanosleep.return { ctsk = task_current(); ptsk = task_parent(ctsk); if (task_execname(ctsk) != "sleep" || task_execname(ptsk) != "stapio") next; println($rqtp->tv_sec); }
- コマンドライン引数をうけとってsleepを呼び出す。
- sleepはnanosleepシステムコールを呼び出すので、その終了時をフック。
- 一応実行コマンド名と、親コマンド名があってるかどうかチェック(適当ですね)。
- 引数の秒数部分を表示(本当はちゃんとユーザ空間アクセスチェックをすべき(笑))。
実行するとこんな感じ。
# stap sleepsort.stp 2 4 1 3 1 2 3 4 ^CToo many interrupts received, exiting.
最後自分でCtrl+Cを押す必要がある。最大値でもとっておけばいいんだろうけどねー。
やはり 「sleep」 sortなので、sleepを呼び出すところは譲れない。
fizzbuzzっぽいもの
これだけだと面白くないので、fizzbuzzモードを追加した
global fillstr; global candidate; global readpos; %{ #include <linux/string.h> %} function fizzbuzz:string (num:long) { s = "" if (num % 3 == 0) s = "fizz" if (num % 5 == 0) s = s . "buzz" return s } function basename:string (path:string) %{ const char *p, *s = (void *)(unsigned long)THIS->path; p = strrchr(s, '/'); if (!p) p = s; else p++; if (p) strlcpy(THIS->__retvalue, p, MAXSTRINGLEN); %} function write_string (buf:long, str:string, len:long, pos:long) %{ char *buf = (void *)(unsigned long)THIS->buf; int64_t len = THIS->len, slen = strlen(THIS->str); int64_t cnt, pos = THIS->pos; if (pos % slen) { memcpy(buf, (const char *)THIS->str + (pos % slen), slen - (pos % slen)); buf += slen - (pos % slen); len -= slen - (pos % slen); } for (cnt = 0; (cnt + 1) * slen < len; cnt++) strlcpy(buf + (cnt * slen), THIS->str, slen + 1); memcpy(buf + (cnt * slen), THIS->str, len - cnt * slen); %} probe syscall.open { candidate[pid()] = basename(filename) } probe kernel.function("memory_open") { fillstr[pid()] = candidate[pid()] } probe kernel.function("read_zero").return { if (fillstr[pid()] == "zero") next len = $return str = fillstr[pid()] if (str == "fizzbuzz") { str = fizzbuzz(pid()); printf("fizzbuzz: %d, %s\n", pid(), str) if (str == "") next } if (len > 0) { write_string($buf, str, len, readpos[tid()]) readpos[tid()] += len; } }
リンク先のファイル名がfizzbuzzの場合、fizzbuzzモードに入る。fizzbuzzモードでは、PIDが3の倍数ならfizz fill、5の倍数ならbuzz fill、15の倍数ならfizzbuzz fill、それ以外はzero fillするというもの。
ちなみにSystemTapでは"."が文字列結合演算になるが、これを利用している例というのはほとんど見たことがないぐらいマイナーだ。たぶん、初めにこの演算を定義した本人も悔やんでいると思う。というのは、大体の場合、この演算子は以下のように間違って使われてエラーになり、ユーザが迷惑する仕様になっている。
var = $struct.member
ただしくは、以下のようにどのような場合でもアロー演算子を使うのがSystemTap流。
var = $struct->member
perf probeではこの教訓を生かし、Cと同じく.と->を分けている。何事も他山の石だと思う。
短いがこのあたりでさらば。
/dev/zeroに対抗して/dev/0を作る
読み出したら zero fillならぬ0 fillをするデバイスを作る。とはいっても、デバイスファイルの登録とか面倒だし、SystemTapでちょっと小細工をする。
出来たのが以下。
global fillstr; global candidate; global readpos; %{ #include <linux/string.h> %} function basename:string (path:string) %{ const char *p, *s = (void *)(unsigned long)THIS->path; p = strrchr(s, '/'); if (!p) p = s; else p++; if (p) strlcpy(THIS->__retvalue, p, MAXSTRINGLEN); %} function write_string (buf:long, str:string, len:long, pos:long) %{ char *buf = (void *)(unsigned long)THIS->buf; int64_t len = THIS->len, slen = strlen(THIS->str); int64_t cnt, pos = THIS->pos; if (pos % slen) { memcpy(buf, (const char *)THIS->str + (pos % slen), slen - (pos % slen)); buf += slen - (pos % slen); len -= slen - (pos % slen); } for (cnt = 0; (cnt + 1) * slen < len; cnt++) strlcpy(buf + (cnt * slen), THIS->str, slen + 1); memcpy(buf + (cnt * slen), THIS->str, len - cnt * slen); %} probe syscall.open { candidate[pid()] = basename(filename) } probe kernel.function("memory_open") { fillstr[pid()] = candidate[pid()] } probe kernel.function("read_zero").return { if (fillstr[pid()] == "zero") next len = $return if (len > 0) { write_string($buf, fillstr[pid()], len, readpos[tid()]) readpos[tid()] += len; } }
使い方は、このスクリプトをGuruモードで走らせてから/dev/zeroから/dev/0へSymbolic linkを張るだけ。
stap -gF zerohook.stp ln -s /dev/zero /dev/0 less /dev/0
適当に長いbasenameでも使えるし、/devにある必要すらないので、ln -s /dev/zero hogehugaとかするとhogehuga fillされたデータが出てくる。
基本アイデアは、system callのopenをフックしてbasenameを取得、次に/dev/zeroの処理をフックして、渡されたバッファを先ほどのbasenameで埋め尽くすというもの。
工夫というほどでもないが、単にフックするだけだと他のプロセスやスレッドのopenと重なる場合があるので、tidごとに変数を分けるようにしている。
SystemTapでfizzbuzz
とりあえず、advent calenderに穴を開けたくないので、何か書こうと思っていろいろ考えてみたが、あまり手をかける余裕もないので、SystemTapでちょっとしたものを書くことにする。
stapgamesをgithubに移したよ
すっかり放置していたstapgamesをgithubに移すことにした。
いろいろ用意を整えて、さあ移行するか!と思ったら、・・・subversionから直接インポートできるのか。(´・ω・`)
とりあえず、https://github.com/mhiramat/stapgamesで公開しておきました。まだ最新のsystemtap対応とかしてないですが・・・。
djprobeマージ
V10にしてようやくマージされた。長かった。これからまだしばらくは安定化が必要だけど、とりあえず一区切りついた。
今回のパッチその物は去年の6月からだから10ヶ月モノだ。
だが待ってほしい。Djprobeその物についてはOLS2007で発表している。もちろん発表の前に構想があり、試作があった。それはいつか。なんと2005年だった。
つまりdjprobeの試作は、2005年にkprobesがマージされた直後から始まっているわけで、実のところプロジェクトとしてみたら5年かかったわけだ。その間にいろいろと実装は変わった*1ものの、x86上での基本的な仕組みはほとんど変わっていない。当時の説明スライドがそのまま使えるのではないかと思うぐらいだ。
さて、次はperf probeの機能追加を考えねば。
Xperia X10 mini / QUE / Ideapad U1
気になるなあ。
Xperia mini
これだけ小さいと、PVにあるように、スマートフォンなのに普通の携帯の延長の使い方ができる。スマートフォン=アプリを使う、ブラウズする、というPCを取り込んだような使い方から、一周して普通の携帯のように日常生活の補助をする、という存在になりうるんじゃないかと思う。ネックはスマートフォンの料金モデルに乗せないといけない所かなあ。AppleがiPhone miniとか出すと変わるかも。