340 likes | 464 Views
OS カーネル用 アスペクト指向システム KLASY. 柳澤 佳里 光来 健一 千葉 滋 石川 零 東京工業大学 情報理工学研究科 数理・計算科学専攻. プロファイリングコードを 実行中のカーネルに挿入するには?. 目的 : 任意の点でタイムスタンプをロギング 性能チューニングのため 例 ) パケット到達からカーネルバッファにいたるまでの経過時間を調査 既存の方法 カーネルプロファイラを利用 ? 決まった点でしかログを取得できない カーネルソースコードを変更し、再コンパイル ? 面倒くさくて、ミスしがち. プロファイリングコード.
E N D
OSカーネル用アスペクト指向システム KLASY 柳澤 佳里 光来 健一 千葉 滋 石川 零 東京工業大学 情報理工学研究科 数理・計算科学専攻
プロファイリングコードを実行中のカーネルに挿入するには?プロファイリングコードを実行中のカーネルに挿入するには? • 目的: 任意の点でタイムスタンプをロギング • 性能チューニングのため • 例) パケット到達からカーネルバッファにいたるまでの経過時間を調査 • 既存の方法 • カーネルプロファイラを利用? • 決まった点でしかログを取得できない • カーネルソースコードを変更し、再コンパイル? • 面倒くさくて、ミスしがち
プロファイリングコード ソースコードの指定した行にて変数の値とタイムスタンプを 記録 再コンパイル、再起動 Linux カーネルソースコード (fs/attr.c) int inode_change_ok(…) { … if ((ia_valid & ATTR_UID) && … attr->ia_uid != inode->i_uid) … goto error; if ((ia_valid & ATTR_GID) && … } 無しでプロファイリングしたい プロファイリングコード 挿入 struct timeval tv; do_gettimeofday(&tv) ; print_tv(tv); printk(“%ld”, inode->i_uid);
Kerninst [Tamches et al. ’99] • 実行時コード操作ツール • アセンブリレベルの抽象度 • 開発者は次のアドレスの調査が必要: • コードを挿入する機械語のアドレス • 記録する変数が格納された場所のアドレス サンプルコード: Kerninst を用いてログを取得 kmgr.findModule(“kernel”, &kmod); kmod.findFuction(“inode_change_ok”, &ifunc); ifunc.findEntryPoint(&entries); kmgr.findModule(“profiler”, &kmod); kmod.findFunction(“print_log”, &pf); hook = kapi_call_expr(pf.getEntryAddr(), args); kmgr.insertSnippet(hook, entries[0]); void print_log() { … __asm__ (“movl %%ebp, %0” : “=r”(ebp)); uid = ((struct inode*)ebp[11])->i_uid; /* ebp[11] is inode */ …
KLASY • Kernel-level Aspect-Oriented System • ソースコードレベルの抽象度 • アスペクト指向プログラミング(AOP)を用いた利点 • KLASYにより開発者は: • ソースコードレベルの視点で任意の実行点を選択可能 • ポイントカット • C言語でプロファイリングコードを記述可能 • アドバイス • 選択された点で実行されるコード • 実行点で利用可能な変数にアクセス可能
ロギング • AOPのキラーアプリケーション • ロギング(or プロファイリング)コードは独立した別個のモジュールに分離 • なぜ新しいAOPシステムが必要か? • 既存のC言語用動的AOPシステムの問題 • 実行時コンテキストの取得が不可能: 変数アクセスができない • 構造体メンバーアクセスのポイントカットが不可能 • たとえば、inode->i_uidがアップデートされたときのタイムスタンプを調査不可能 プロファイリングに不可欠
KLASYのアスペクト例 アスペクト Linux カーネルコード (fs/attr.c) <aspect> <import>linux/time.h</import> <advice><pointcut> access(inode.i_uid)AND within_function(inode_change_ok) AND target(inode_value) </pointcut> <before> struct inode *i = inode_value; struct timeval tv; do_gettimeofday(&tv); print(i->i_uid, tv.tv_sec, tv.tv_usec); </before> </advice> </aspect> int inode_change_ok(…) { … if ((ia_valid & ATTR_UID) && … attr->ia_uid != inode->i_uid) … goto error; if ((ia_valid & ATTR_GID) && … } pointcut 選択 advice
KLASYのアスペクト例 アスペクト Linux カーネルコード (fs/attr.c) アドバイスボディで使う ヘッダーファイルを指定 <aspect> <import>linux/time.h</import> <advice><pointcut> access(inode.i_uid)AND within_function(inode_change_ok) AND target(inode_value) </pointcut> <before> struct inode *i = inode_value; struct timeval tv; do_gettimeofday(&tv); print(i->i_uid, tv.tv_sec, tv.tv_usec); </before> </advice> </aspect> int inode_change_ok(…) { … if ((ia_valid & ATTR_UID) && … attr->ia_uid != inode->i_uid) … goto error; if ((ia_valid & ATTR_GID) && … } pointcut 選択 advice
KLASYのアスペクト例 アスペクト Linux カーネルコード (fs/attr.c) <aspect> <import>linux/time.h</import> <advice><pointcut> access(inode.i_uid)AND within_function(inode_change_ok) AND target(inode_value) </pointcut> <before> struct inode *i = inode_value; struct timeval tv; do_gettimeofday(&tv); print(i->i_uid, tv.tv_sec, tv.tv_usec); </before> </advice> </aspect> int inode_change_ok(…) { … if ((ia_valid & ATTR_UID) && … attr->ia_uid != inode->i_uid) … goto error; if ((ia_valid & ATTR_GID) && … } pointcut 選択 advice
KLASYのアスペクト例 アスペクト Linux カーネルコード (fs/attr.c) <aspect> <import>linux/time.h</import> <advice><pointcut> access(inode.i_uid)AND within_function(inode_change_ok) AND target(inode_value) </pointcut> <before> struct inode *i = inode_value; struct timeval tv; do_gettimeofday(&tv); print(i->i_uid, tv.tv_sec, tv.tv_usec); </before> </advice> </aspect> int inode_change_ok(…) { … if ((ia_valid & ATTR_UID) && … attr->ia_uid != inode->i_uid) … goto error; if ((ia_valid & ATTR_GID) && … } pointcut 選択 advice access(inode.i_uid) ポイントカットでinode->i_uid を選択
KLASYのアスペクト例 アスペクト Linux カーネルコード (fs/attr.c) <aspect> <import>linux/time.h</import> <advice><pointcut> access(inode.i_uid)AND within_function(inode_change_ok) AND target(inode_value) </pointcut> <before> struct inode *i = inode_value; struct timeval tv; do_gettimeofday(&tv); print(i->i_uid, tv.tv_sec, tv.tv_usec); </before> </advice> </aspect> int inode_change_ok(…) { … if ((ia_valid & ATTR_UID) && … attr->ia_uid != inode->i_uid) … goto error; if ((ia_valid & ATTR_GID) && … } pointcut 選択 advice within_function() により選択範囲を inode_change_ok関数内に制限。
KLASYのアスペクト例 アスペクト Linux カーネルコード (fs/attr.c) <aspect> <import>linux/time.h</import> <advice><pointcut> access(inode.i_uid)AND within_function(inode_change_ok) AND target(inode_value) </pointcut> <before> struct inode *i = inode_value; struct timeval tv; do_gettimeofday(&tv); print(i->i_uid, tv.tv_sec, tv.tv_usec); </before> </advice> </aspect> int inode_change_ok(…) { … if ((ia_valid & ATTR_UID) && … attr->ia_uid != inode->i_uid) … goto error; if ((ia_valid & ATTR_GID) && … } pointcut 選択 advice 実行時コンテキスト取得: inode_valueに選択した構造体(inode)への参照を設定
KLASYのアスペクト例 アスペクト Linux カーネルコード (fs/attr.c) <aspect> <import>linux/time.h</import> <advice><pointcut> access(inode.i_uid)AND within_function(inode_change_ok) AND target(inode_value) </pointcut> <before> struct inode *i = inode_value; struct timeval tv; do_gettimeofday(&tv); print(i->i_uid, tv.tv_sec, tv.tv_usec); </before> </advice> </aspect> int inode_change_ok(…) { … if ((ia_valid & ATTR_UID) && … attr->ia_uid != inode->i_uid) … goto error; if ((ia_valid & ATTR_GID) && … } pointcut 選択 advice タイムスタンプを取得し、i_uidの値とともに保存
KLASYの対応する主なポイントカット、アドバイスKLASYの対応する主なポイントカット、アドバイス • ポイントカット • access • 本研究で提案 • 構造体メンバーアクセスを選択 • execution • 関数実行を選択 • アドバイス • before • 選択された点の前でプロファイリングコードを実行 • after • 選択された点の後でプロファイリングコードを実行
実行時コンテキストの取得に使うポイントカット実行時コンテキストの取得に使うポイントカット • ポイントカット記述子 • target • accessポイントカットで選択したメンバーを持つ構造体変数への参照を取得 • local_var • accessポイントカットで選択した箇所でローカル変数への参照を取得 • argument • executionポイントカットで選択した関数の引数への参照を取得
KLASYの実装 • Source-based binary-level dynamic weaving • GNU C コンパイラ(gcc)を拡張 • 生成した拡張シンボル情報を用い: • 構造体メンバーへのアクセスをポイントカット可能 • 実行時コンテキスト(変数のアドレス)を取得可能 • Kerninstをバックエンドに利用 • 動的織り込みを実現 • C言語でアドバイスを記述 • ソースレベルの視点で記述可能 • XML風のタグで囲まれている
処理の流れ OS ソースコード アスペクト KLASY gcc アスペクトコンパイラー 拡張シンボル情報 ポイントカット insmod ウィーバー OSカーネル コンパイル済みアドバイス OS カーネル 本体 フック
処理の流れ OS ソースコード アスペクト KLASY gcc アスペクトコンパイラー 拡張シンボル情報 ポイントカット insmod ウィーバー OSカーネル コンパイル済みアドバイス OS カーネル 本体 フック
KLASY gcc • KLASY gccは次のシンボル情報を収集: • 構造体メンバーアクセスのあるファイル名、行番号 • コンパイラーのパーザーを拡張 • 従来のgccではこの情報は消失 • 各行の先頭命令のあるアドレス • コンパイル時にデバッグオプション(-g)を利用 • 構造体メンバーアクセスのポイントカットにこれも必要
処理の流れ OS ソースコード アスペクト KLASY gcc アスペクトコンパイラー 拡張シンボル情報 ポイントカット insmod ウィーバー OSカーネル コンパイル済みアドバイス OS カーネル 本体 フック
ウィーバー • Kerninstを用いポイントカットで選択した点にフック挿入 • フック • アドバイスボディを呼び出すためのコード • フック挿入アドレスの取得方法 • 構造体メンバーアクセス • ファイル名、行番号 • 行の先頭アドレス 拡張シンボル情報を利用
アンウィーブ • KLASYでは実行時にOSカーネルから織り込んだアスペクトを削除可能 • プロファイリングには重要な機能 • 一般に利用者は様々なアスペクトを試用 • 観察効果を避けるため不要なアスペクトの削除は必要 • 利用者はわかりやすい名前でアスペクトを指定可能 • Kerninstを用いてウィーバーの入れたフックを消去
アスペクト例における実行時コンテキストの取得アスペクト例における実行時コンテキストの取得 アスペクト Linux カーネルソースコード (fs/attr.c) <aspect> <import>linux/time.h</import> <advice><pointcut> access(inode.i_uid)AND within_function(inode_change_ok) AND target(inode_value) </pointcut> <before> struct inode *i = inode_value; struct timeval tv; do_gettimeofday(&tv); print(i->i_uid, tv.tv_sec, tv.tv_usec); </before> </advice> </aspect> int inode_change_ok(…) { … if ((ia_valid & ATTR_UID) && … attr->ia_uid != inode->i_uid) … goto error; if ((ia_valid & ATTR_GID) && … } ポイントカット selected ローカル変数を取得し、 アドバイス内で利用 アドバイス
実行時コンテキストの取得の実装 • ウィーバーとKLASY gccの連携により実現 • KLASY gccにて構造体への参照方法を保存 • ローカル変数から目的の構造体への参照に至る方法を保存 • 例) inode.length → &inodeinode_ptr->length → inode_ptr • ウィーバーにて参照を得るトランポリンコードを作成 • ローカル変数の格納場所をデバッグ情報をから取得 • Gccの作成したデバッグ情報を利用 • 保存された方法で構造体への参照を得、アドバイスに渡す
ケーススタディ: ネットワークI/Oサブシステムの調査 • 目的 • 過負荷におけるLinuxネットワークサブシステムの性能ボトルネックを発見 • Sk_buff構造体へのアクセスをトレース • Sk_buff構造体はLinuxにてネットワークサブシステムでバッファとして利用 • Sk_buffのトレースによりネットワークサブシステムの挙動を把握 • KLASYの実行時コンテキスト取得機能を利用 • 個々のパケットを識別するため • 無関係なパケットを無視するため
トレースに用いたアスペクト ワイルドカード <aspect><advice> <pointcut> access(sk_buff.%) AND target(arg0) </pointcut> <before> struct sk_buff *skb = arg0; unsigned long timestamp; if (skb->protocol != ETH_P_ARP) { STORE_DATA($pc$); STORE_DATA(skb); DO_RDTSC(timestamp); STORE_DATA(timestamp); } </before> </advice></aspect> skb->protocol プログラムカウンタ、sk_buff出現位置、 タイムスタンプを保存 ARPパケットを無視
トレース結果 • プロセススケジューリングがボトルネックと判明 • Skb_copy_datagram_iovec関数はプロセスの発行したシステムコールから呼び出されるので 差が大きい __kfree_skb ip_rcv skb_copy_datagram_iovec netif_receive_skb tcp_v4_rcv tcp_rcv_established 1000_clean_rx_irq ip_rcv_finish tcp_v4_do_rcv
ケーススタディでのaccessポイントカットの有用性ケーススタディでのaccessポイントカットの有用性 • Sk_buff構造体の全メンバーをaccessポイントカット • Linuxネットワークサブシステムの挙動を把握 • もし、executionポイントカットでやるとしたら… • ネットワークサブシステムについての詳細な知識が必要 • 過不足なく関数を選択するため • 本ケーススタディでは76箇所(関数21個)にてログ取得 • 最適化禁止が必須 • Inline関数への対応が必要 • 現実と大幅に異なる • OSカーネルは通常最適化オプションつきでコンパイルされる • 例) Linux カーネルは-Os最適化オプションでコンパイル • 本ケーススタディではstatic inline関数6箇所にてログ取得
KLASYの有用性 (まとめ) • 構造体メンバーアクセスをポイントカット • 構造体メンバーアクセスは大規模Cプログラムで使用 • 関数間でのデータの受け渡しに利用 • ポリモルフィズムの実現に利用 • メンバーに関数へのポインターを保持 • 例) 仮想ファイルシステム(VFS)、仮想デバイスなど • 例) OS kernel (FreeBSD, Linux), X window System • 実行時コンテキストの取得 • 実行時コンテキストがあると… • 詳細な調査が可能 • 必要なデータのみのログ出力が可能 • ログ保存領域を節約
既存のC言語用動的アスペクト指向システム • TOSKANA [Engel ’05], DAC++ [Almajali ’05],TinyC2 [Zhang ’03],and Arachne [Douence ’05] • 実行時コード操作 • 関数実行、呼び出しのみポイントカット可能 • シンボル情報を不使用 • TOSKANA-VM [Engel ’05] • 仮想機械上でOSカーネルを実行 • 実際のハードウェア上のプロファイリングは不可能
実験 • UnixBenchによるベンチマーク • Linux: 通常gccでコンパイルしたカーネル • -fomit-frame-pointerオプションあり • KLASY: KLASY gccでコンパイルしたカーネル • 実行時コンテキスト取得のため、-fomit-frame-pointerオプション使用不可 • デバッグ情報の示す変数位置と実位置がずれるため • ほぼ-fomit-frame-pointer禁止によるオーバーヘッドを測定 • 実験環境 CPU: AthlonXP™ 1800+, Mem: 1GB, Linux 2.6.10 Kerninst 2.1.1, gcc 3.3.3, Intel Ethernet PRO/1000
実験結果 • 現実的なオーバーヘッドであることを確認 • オーバーヘッド: • 0 ~ 12 % • 平均: 4.4 % dhry2reg: drystone syscall: システムコール pipe: pipeシステムコール execl: execlシステムコール context: コンテキストスイッチ
関連研究 • C言語用静的アスペクト指向システム • 実行中のカーネルを変更可能 • プロファイリングコードの変更に再起動が必要 • 例) AspectC [Coady ’01], AspectC++ [Spinczyk ’02] • カーネルプロファイラー • LKST、 DTrace [Cantrill ’04]、SystemTAP [Prasad ’05]、 LTT [Yaghmour ’00] • カーネル内で発生したイベントについてログを取得するツール • プロファイラー開発者の設定した箇所でのみプロファイリング可能
まとめ • KLASY (Kernel-level Aspect-oriented System)を提案 • Source-based binary-level dynamic weaving • 構造体メンバーへのアクセスをポイントカットで選択可能 • 実行時コンテキストを取得可能 • ネットワークI/OサブシステムのプロファイリングにKLASYが有用であると確認 • 性能ボトルネックがプロセススケジューリングにあると判明