660 likes | 909 Views
A Dynamic Aspect-oriented System for Data-driven Profiling of OS Kernels. 柳澤 佳里 指導教員 千葉 滋. OS の性能向上は今でも重要な課題. 性能向上のため、 2000 年以降でも スケジューラーの実装を変更 e.g. ULE および SMP (FreeBSD), O(1) および CFS (Linux) システムコールの実装を変更 e.g. NetBSD 、 FreeBSD でシステムコールの性能が向上
E N D
A Dynamic Aspect-oriented System for Data-driven Profiling of OS Kernels 柳澤 佳里 指導教員 千葉 滋
OSの性能向上は今でも重要な課題 • 性能向上のため、2000年以降でも • スケジューラーの実装を変更 • e.g. ULEおよびSMP (FreeBSD), O(1)およびCFS (Linux) • システムコールの実装を変更 • e.g. NetBSD、FreeBSDでシステムコールの性能が向上 • See Also: http://bulk.fefe.de/scalability/ (syscall ベンチマーク) • スレッド実装を変更 • e.g. KSE (FreeBSD)、NPTL (Linux) ・・・などなど
カーネルプロファイラの必要性 • OSの性能向上にはボトルネックの特定が不可欠 • ボトルネックを発見して除去 • 再コンパイル、再起動なしの調査が必要 • ソースコードを書き換える単純な方法では、 • 現象の再現まで長時間待つ必要がある • 開発者がミスしがち • 仮想化により、問題点の発見がさらに困難に • 関連する部分が増加
カーネルプロファイラの要件 • 自由度 • 任意の調査点で任意の調査コードが実行できる • 調査に応じて調査対象が変化 • 最初はおおざっぱに調査し、徐々に絞り込む • 調査データに基づきログ出力の内容を変更する • 抽象度 • 調査点、調査コードの選択をソースコードレベルの視点で可能 • 調査の効率を向上 • 調査点を選びやすいと調査がしやすい • 抽象度の高い言語で調査コードがかけると調査がしやすい
理想のプロファイリング ソースコード中の関連箇所を 自動的に選び出し、変数の値 とタイムスタンプを記録 再コンパイル、再起動 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);
従来のカーネルプロファイラ (1) • イベントドリブン方式 • 指定したイベントの発生時にログ取得コードを実行 • 任意のログ取得コードを記述できるものが存在 • 調査点の指定は適度に抽象化 • 作者により決められた調査点でのみ調査 • 調査しない場合でもオーバーヘッドが発生 • 例) SystemTAP [Vara ’05]、DTrace [Bryan ’04] • 独自言語を用意し、高い抽象度でのプロファイリングコードの記述が可能 • プロファイリング箇所は関数などに決め打ち
従来のカーネルプロファイラ (2) • 動的コード挿入方式 • 指定されたコードを指定された箇所に動的に挿入 • アドレス指定で任意の箇所にコード挿入可能 • コード挿入箇所の指定方法の抽象度が低い • 例) Kerninst [Ariel ’99], GILK [David ’02] • Linuxカーネルの任意の箇所に動的にコード挿入可能 • アセンブリレベルの抽象度 • 任意の箇所を指定する際は抽象度が非常に低い
Kerninstでの記述例 • 開発者は次のアドレスの調査が必要: • コードを挿入する機械語のアドレス • 記録する変数が格納された場所のアドレス 面倒くさい サンプルコード: Kerninst を用いてログを取得 kmgr.createInstPointAtAddr(inst_ptr, &insert_point); kmgr.findModule(“profiler”, &kmod); kmod.findFunction(“print_log”, &pf); hook = kapi_call_expr(pf.getEntryAddr(), args); Kmgr.insertSnippet(hook,insert_point); void print_log() { … __asm__ (“movl %%ebp, %0” : “=r”(ebp)); uid = ((struct inode*)ebp[11])->i_uid; /* ebp[11] is inode */ …
従来手法のまとめ • 従来のカーネルプロファイラは要件を満たさない • 自由度と抽象度を両立しない • ソースコードレベルの抽象度で任意のコードを書けるシステムが必要
動的アスペクト指向を用いたプロファイリングの提案動的アスペクト指向を用いたプロファイリングの提案 • プログラム中の主要な実行点を調査点として選択可能 • 選択可能な点: 関数呼び出し、実行、変数の参照など • ソースコードレベルの抽象度を提供 • 高級言語で調査コードを記述可能 • 調査点の選択がソースコードレベルの視点で可能 • 調査範囲、対象の変更が実行時に可能 • 必要なときに調査が可能 • 広いところから絞り込める • 無停止なので情報が消失しない • コード挿入後に再現するまで待つ必要がない
ポイントカット 調査点(ジョインポイント)を指定 関数呼び出し call/execution 変数アクセスの指定 set/get アドバイス ポイントカットで指定した実行点で実行するコード ジョインポイントの例 アスペクト指向 (AOP) とは? n_time Iptime() { … getmicrotime(&atv); t = … return (htonl(t)); } 関数実行箇所 関数呼び出し 変数への参照、代入
動的アスペクト指向 (DAOP) • 動作中のカーネルに動的にアスペクトを織り込み • 無停止に調査コード (アスペクト) を挿入 • AOPを使うことで抽象度を確保 • 実装方法 • 動的コード書き換え or フックの挿入 • e.g. PROSE [Andrei ’02]、Wool [Yoshiki ’03] • 仮想マシンの改造 • e.g. Steamloom [Christoph ’04]
本研究の貢献 • カーネルプロファイリング用途でDAOPを使うことを提案(前述) • プロファイリングのためのC言語用DAOPシステムを開発 (後述) • データを扱うためのポイントカットを提供 • 構造体メンバーアクセス、データフローへのポイントカット • 構造体メンバーのポイントカット: 実装方法を提案 • データフローのポイントカット: 新たなポイントカット記述子を提案
従来のDAOPは不十分 • C言語用DAOPでJava用DAOP並のポイントカットがあればいいのに! • Java用のDAOPはカーネルに使えない • ポイントカットの種類が豊富 • 多くのOSはC言語で記述 • 従来のC言語用DAOPは不十分 • データのまとまりを扱うポイントカットが不十分 • OSカーネルの特性に対応できていない
OSカーネルの特性 • 高度なモジュール化 • 共通のデータ構造として構造体を複数モジュールで共用 • ポリモルフィズム風機構を構造体で実装 • データのやりとりに構造体を多用 • 厳しいコーディング規約で使用する構造体に規則が存在 • 複数スレッドでデータ処理 • コールフローでのプロファイリングが不可 • きちんとしたレイヤー分け e.g. • 即応性が求められるデバイスドライバ (bottom half) • 高度な処理をする上位レイヤー (top half)
データを扱うポイントカットのプロファイリングにおける必要性データを扱うポイントカットのプロファイリングにおける必要性 • カーネルの特性をうまく利用 • 構造体メンバーアクセスは大規模Cプログラムで使用 • 関数間でのデータの受け渡しに利用 • ポリモルフィズムの実現に利用 • 効率的 • データを使っているモジュールを一発で選択 • cf. 関数を列挙する方法では、 • 調査するサブシステムについての詳細な知識が必要 • 過不足なく関数を選択するため • 関数のシンボルを残すために最適化禁止が必須 • Inline関数への対応が必要 • 現実のOSカーネルは最適化オプションありでコンパイル
従来のC言語用DAOP • データを扱うポイントカットが不足 • TOSKANA [Michael ’05]、TinyC2 [Charles ’03]、DAC++ [Sufyan ’05] • 関数実行を選択するポイントカットのみ • Arachne [Remi ’05] • 関数呼び出し、返値、グローバル変数を選択するポイントカットのみ • μDyner [Marc ’03] • ジョインポイントとなる関数をアノテーションで指定 • 同様の方法でジョインポイントが増えるが、イベントドリブン方式のプロファイラーと同じ問題を内包
本研究で開発したDAOPの機構 • データ利用箇所を選択するポイントカットを提案 • (1) accessポイントカット [GPCE ’06](点を指定) • 構造体メンバーへのアクセスをポイントカット • OSの特性として、構造体を関連モジュールで共用 • データフローを選択するポイントカットを提案 • (2) xflowポイントカット[PRO ’07](点から線に) • 特定のデータを持つ構造体インスタンスをポイントカット • マルチスレッド環境で処理フローの追跡が可能
KLASY: Kernel Level Aspect-oriented SYstem • Access pointcut • 構造体メンバーへのアクセスをポイントカット • OSカーネルの関連箇所をまたいで選択 • Source-based binary-level dynamic weaving • コンパイル時に得られる豊富な情報を実行時に利用 • コンパイラを拡張し、出力するシンボル情報を拡張 • シンボル情報を用いて構造体メンバーへのアクセス、ローカル変数などの情報を取得 • Kerninstを用いてアドバイスコードを挿入
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の対応する主なポイントカット、アドバイス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の作成したデバッグ情報を利用 • 保存された方法で構造体への参照を得、アドバイスに渡す
実験 • 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
実験結果 • 現実的なオーバーヘッドであることを確認 dhry2reg: drystone syscall: システムコール pipe: pipeシステムコール execl: execlシステムコール context: コンテキストスイッチ
ケーススタディ: ネットワーク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箇所にてログ取得
第1部のまとめ • accessポイントカット • Source-based binary-level dynamic weaving • 構造体メンバーへのアクセスをポイントカットで選択可能 • 実行時コンテキストを取得可能 • ネットワークI/Oサブシステムのプロファイリングにaccessポイントカットが有用であると確認 • 性能ボトルネックがプロセススケジューリングにあると判明
(2) Xflow pointcut and XenLASY XenLASY = KLASY + xflow + VM対応
I/Oフローの追跡が大切 • I/Oフローとは • I/Oを発行してデバイスが送出する流れ • デバイスで受信しアプリケーションが受け取る流れ • 個別のデータの動き (フロー) の追跡が必要 • データがドメインUやドメイン0でどう処理されるかを知りたい • 複数のドメインがI/Oした場合に、区別して追跡したい • 余計なフローは取りたくない • 個々のフローはフロー識別子 (ID) が必要 • 一つ一つのデータの流れを区別するため
単純なフロー追跡では不十分 • コールフロー • 関数呼び出しの流れを追跡 • 実行スレッドが変わると追跡不可 • ドメイン間の追跡が不可能 • トップハーフ / ボトムハーフの追跡が不可能 • 単純なデータフロー • データのポインタを追跡 • データ形式が変化すると追跡不可 • ドメイン間ではデータ形式が変化し、追跡不可 • データの複製、分割すると追跡不可
ドメインU ドメイン0 OS OS 実ドライバ 仮想ドライバ 仮想ドライバ 仮想マシンモニタ (VMM) ハードウェア VMでは特にI/Oフローが重要 • データ処理に複数のOSが関与 • I/Oがドメイン0を通る • 複数のVMのI/Oが競合 例) XenのI/O処理
XenLASY • Xen上のI/O処理をプロファイリングをするためのアスペクト指向システム • xflowポイントカット • Xen上のデータフローを選択するポイントカット • 指定した種類のフローに指定したデータが入っている時を選択 • 文法: xflow(フロー名, データへのポインタ, フローID) なお、フローIDは省略可 • 追跡すべきデータの流れをきめ細かく指定可能 • 開始点、中継点、終了点を指定 • データ形式の変化、分割などにも対処 • 余計なデータが含まれないようにできる
KLASYを拡張 • KLASY [Yanagisawa ’06]を拡張して開発 • KLASYとは、 • カーネル用動的アスペクト指向システム • ソースコードレベルの情報を実行時の織り込みで利用 • accessポイントカットを実現 • 構造体メンバーへのアクセスをポイントカット • Source-based binary-level dynamic weaving • Xenに対応 • xflowを扱えるよう言語拡張 • @ドメイン名を使えるよう言語拡張
accessポイントカットで選択 Netif_receive_skb copy_from_user Netbk_fill_flags Tcp_sendmsg Network_start_xmit br_forward SkGeXmit Netif_rx id1 データフロー (xflow) id2 DomainU Domain0 XenLASYのアスペクト例 アスペクト <aspect> <import>linux/time.h</import> <advice><pointcut> access(sk_buff.%)AND target(skb) AND xflow(netflow, skb, id) </pointcut> <before> long long tsc; DO_RDTSC(tsc); STORE_DATA3($pc$, tsc, id); </before> </advice> </aspect> sk_buff構造体インスタンスがnetflowのデータだった場合にpc、時間、フローidをログ出力 netflow: ネットワークI/Oのフロー
netflow Xen上のネットワークI/O処理フロー start: 開始点 データフローの開始点を指定 transit: 中継点 データ形式が変わる場合 skb_cloneは複製を作成 ドメイン間の場合 quit: 終了点 xflowポイントカットの定義 <xflow name=“netflow”> <start><pointcut> access(sk_buff.data) AND within_function(alloc_skb_from_cache) </pointcut></start> <transit><pointcut> access(sk_buff.head) AND within_function(skb_clone) </pointcut> <move from=“skb” to=“n” /> </transit> <quit><pointcut> access(sk_buff.%) AND within_function(__kfree_skb) </pointcut></quit> </xflow>
構造体インスタンスからIDを引けるよう登録、削除構造体インスタンスからIDを引けるよう登録、削除 構造体インスタンスの指定 省略時はaccessポイントカットで選択した構造体のインスタンスを選択 selectで任意の変数も指定可 DB登録/削除処理は自動で設定 任意のコードを指定する場合はactionを利用 start/quit (開始点/終了点) <start><pointcut> access(sk_buff.data) AND within_function(alloc_skb_from_cache) </pointcut></start> 例 <start><pointcut>…</pointcut> <select local_var=“data” /> </start> <start><pointcut>…</pointcut> <action> int id = new_flowid(); … </action> </start>
moveで対応付けを指示 skb: フローID格納元 n: フローID格納先 skbを鍵としてフローIDが引けないようエントリを抹消 エントリを保持するcopyも用意 transit (中継点) <transit><pointcut> access(sk_buff.head) AND within_function(skb_clone) </pointcut> <move from=“skb” to=“n” /> </transit> 例 <transit><pointcut> … </pointcut> <copy from=“skb” to=“n” /> </transit>
ドメインをまたがるtransit定義 • xin_move、xout_move要素を用意 • ドメインをまたがるとデータのアドレスからフローIDを引けない • ドメイン間ではアドレス空間が違う • ドメイン間ではデータベースを共有していない • フローIDを送信先ドメインに伝搬 • ドメイン間で渡されるヘッダの空き領域にIDを格納 ヘッダ ドメイン0 DomU フローIDを格納 実データ