os カーネル用 アスペクト指向システム klasy
DESCRIPTION
OS カーネル用 アスペクト指向システム KLASY. 柳澤 佳里 光来 健一 千葉 滋 石川 零 東京工業大学 情報理工学研究科 数理・計算科学専攻. プロファイリングコードを 実行中のカーネルに挿入するには?. 目的 : 任意の点でタイムスタンプをロギング 性能チューニングのため 例 ) パケット到達からカーネルバッファにいたるまでの経過時間を調査 既存の方法 カーネルプロファイラを利用 ? 決まった点でしかログを取得できない カーネルソースコードを変更し、再コンパイル ? 面倒くさくて、ミスしがち. プロファイリングコード. - PowerPoint PPT PresentationTRANSCRIPT
2
プロファイリングコードを実行中のカーネルに挿入するには? 目的 : 任意の点でタイムスタンプをロギング
性能チューニングのため 例 ) パケット到達からカーネルバッファにいたる
までの経過時間を調査
既存の方法 カーネルプロファイラを利用 ?
決まった点でしかログを取得できない カーネルソースコードを変更し、再コンパイル ?
面倒くさくて、ミスしがち
3
プロファイリングコード
int inode_change_ok(…){ … if ((ia_valid & ATTR_UID) && … attr->ia_uid != inode->i_uid) … goto error;
if ((ia_valid & ATTR_GID) && …}
Linux カーネルソースコード (fs/attr.c)
struct timeval tv;do_gettimeofday(&tv) ;print_tv(tv);printk(“%ld”, inode->i_uid);
挿入プロファイリングコード
ソースコードの指定した行にて変数の値とタイムスタンプを記録
再コンパイル、再起動無しでプロファイリングしたい
4
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]);
Kerninst [Tamches et al. ’99]
実行時コード操作ツール アセンブリレベルの抽象度 開発者は次のアドレスの調査が必要 :
コードを挿入する機械語のアドレス 記録する変数が格納された場所のアドレス
void print_log() { … __asm__ (“movl %%ebp, %0” : “=r”(ebp)); uid = ((struct inode*)ebp[11])->i_uid; /* ebp[11] is inode */ …
サンプルコード : Kerninst を用いてログを取得
5
KLASY
Kernel-level Aspect-Oriented System ソースコードレベルの抽象度
アスペクト指向プログラミング (AOP) を用いた利点 KLASY により開発者は :
ソースコードレベルの視点で任意の実行点を選択可能 ポイントカット
C 言語でプロファイリングコードを記述可能 アドバイス
選択された点で実行されるコード 実行点で利用可能な変数にアクセス可能
6
ロギング
AOP のキラーアプリケーション ロギング (or プロファイリング ) コードは独立し
た別個のモジュールに分離 なぜ新しい AOP システムが必要か ?
既存の C 言語用動的 AOP システムの問題 実行時コンテキストの取得が不可能 : 変数アクセスがで
きない 構造体メンバーアクセスのポイントカットが不可能
たとえば、 inode->i_uid がアップデートされたときのタイムスタンプを調査不可能 プロファイリングに不可欠
7
KLASY のアスペクト例
<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>
pointcut
advice
int inode_change_ok(…){ … if ((ia_valid & ATTR_UID) && … attr->ia_uid != inode->i_uid) … goto error;
if ((ia_valid & ATTR_GID) && …}
選択
Linux カーネルコード (fs/attr.c)アスペクト
8
KLASY のアスペクト例
<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>
pointcut
advice
int inode_change_ok(…){ … if ((ia_valid & ATTR_UID) && … attr->ia_uid != inode->i_uid) … goto error;
if ((ia_valid & ATTR_GID) && …}
選択
Linux カーネルコード (fs/attr.c)アスペクトアドバイスボディで使うヘッダーファイルを指定
9
KLASY のアスペクト例
<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>
pointcut
advice
int inode_change_ok(…){ … if ((ia_valid & ATTR_UID) && … attr->ia_uid != inode->i_uid) … goto error;
if ((ia_valid & ATTR_GID) && …}
選択
Linux カーネルコード (fs/attr.c)アスペクト
10
KLASY のアスペクト例
<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>
pointcut
advice
int inode_change_ok(…){ … if ((ia_valid & ATTR_UID) && … attr->ia_uid != inode->i_uid) … goto error;
if ((ia_valid & ATTR_GID) && …}
選択
Linux カーネルコード (fs/attr.c)アスペクト
access(inode.i_uid) ポイントカットで
inode->i_uid を選択
11
KLASY のアスペクト例
<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>
pointcut
advice
int inode_change_ok(…){ … if ((ia_valid & ATTR_UID) && … attr->ia_uid != inode->i_uid) … goto error;
if ((ia_valid & ATTR_GID) && …}
選択
Linux カーネルコード (fs/attr.c)アスペクト
within_function() により選択範囲を inode_change_ok 関数内に制
限。
12
KLASY のアスペクト例
<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>
pointcut
advice
int inode_change_ok(…){ … if ((ia_valid & ATTR_UID) && … attr->ia_uid != inode->i_uid) … goto error;
if ((ia_valid & ATTR_GID) && …}
選択
Linux カーネルコード (fs/attr.c)アスペクト
実行時コンテキスト取得 :
inode_value に選択した構造体 (inode) への参
照を設定
13
KLASY のアスペクト例
<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>
pointcut
advice
int inode_change_ok(…){ … if ((ia_valid & ATTR_UID) && … attr->ia_uid != inode->i_uid) … goto error;
if ((ia_valid & ATTR_GID) && …}
選択
Linux カーネルコード (fs/attr.c)アスペクト
タイムスタンプを取得し、 i_uid の値とと
もに保存
14
KLASY の対応する主なポイントカット、アドバイス ポイントカット
access 本研究で提案 構造体メンバーアクセスを選択
execution 関数実行を選択
アドバイス before
選択された点の前でプロファイリングコードを実行 after
選択された点の後でプロファイリングコードを実行
15
実行時コンテキストの取得に使うポイントカット ポイントカット記述子
target access ポイントカットで選択したメンバーを持つ構造体変
数への参照を取得 local_var
access ポイントカットで選択した箇所でローカル変数への参照を取得
argument execution ポイントカットで選択した関数の引数への
参照を取得
16
KLASY の実装
Source-based binary-level dynamic weaving GNU C コンパイラ (gcc) を拡張
生成した拡張シンボル情報を用い : 構造体メンバーへのアクセスをポイントカット可能 実行時コンテキスト ( 変数のアドレス ) を取得可能
Kerninst をバックエンドに利用 動的織り込みを実現
C 言語でアドバイスを記述 ソースレベルの視点で記述可能 XML 風のタグで囲まれている
17
処理の流れ
コンパイル済みアドバイス
アスペクト OSソースコード
ポイントカット
拡張シンボル情報
ウィーバーOS カーネ
ル本体フック
OS カーネル
アスペクトコンパイラー KLASYgcc
insmod
18
処理の流れ
コンパイル済みアドバイス
アスペクト OSソースコード
ポイントカット
拡張シンボル情報
ウィーバーOS カーネ
ル本体フック
OS カーネル
アスペクトコンパイラー KLASYgcc
insmod
19
KLASY gcc
KLASY gcc は次のシンボル情報を収集 : 構造体メンバーアクセスのあるファイル名、行
番号 コンパイラーのパーザーを拡張 従来の gcc ではこの情報は消失
各行の先頭命令のあるアドレス コンパイル時にデバッグオプション (-g) を利用 構造体メンバーアクセスのポイントカットにこれも必
要
20
処理の流れ
コンパイル済みアドバイス
アスペクト OSソースコード
ポイントカット
拡張シンボル情報
ウィーバーOS カーネ
ル本体フック
OS カーネル
アスペクトコンパイラー KLASYgcc
insmod
21
ウィーバー
Kerninst を用いポイントカットで選択した点にフック挿入
フック アドバイスボディを呼び出すためのコード
フック挿入アドレスの取得方法 構造体メンバーアクセス
ファイル名、行番号 行の先頭アドレス
拡張シンボル情報を利用
22
アンウィーブ
KLASY では実行時に OS カーネルから織り込んだアスペクトを削除可能 プロファイリングには重要な機能
一般に利用者は様々なアスペクトを試用 観察効果を避けるため不要なアスペクトの削除は必要
利用者はわかりやすい名前でアスペクトを指定可能
Kerninst を用いてウィーバーの入れたフックを消去
23
アスペクト例における実行時コンテキストの取得
<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
Linux カーネルソースコード (fs/attr.c)アスペクト
ローカル変数を取得し、アドバイス内で利用
24
実行時コンテキストの取得の実装
ウィーバーと KLASY gcc の連携により実現 KLASY gcc にて構造体への参照方法を保存
ローカル変数から目的の構造体への参照に至る方法を保存 例 )
inode.length → &inodeinode_ptr->length → inode_ptr
ウィーバーにて参照を得るトランポリンコードを作成 ローカル変数の格納場所をデバッグ情報をから取得
Gcc の作成したデバッグ情報を利用 保存された方法で構造体への参照を得、アドバイスに渡す
25
ケーススタディ : ネットワーク I/O サブシステムの調査 目的
過負荷における Linux ネットワークサブシステムの性能ボトルネックを発見
Sk_buff 構造体へのアクセスをトレース Sk_buff 構造体は Linux にてネットワークサブシス
テムでバッファとして利用 Sk_buff のトレースによりネットワークサブシステムの挙
動を把握 KLASY の実行時コンテキスト取得機能を利用
個々のパケットを識別するため 無関係なパケットを無視するため
26
トレースに用いたアスペクト
<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>
ARP パケットを無視プログラムカウンタ、sk_buff 出現位置、タイムスタンプを保存
ワイルドカード
skb->protocol
27
トレース結果
プロセススケジューリングがボトルネックと判明 Skb_copy_datagram_iovec 関数はプロセスの発行し
たシステムコールから呼び出されるのでTime scale of packet arrival
0.1 1 10 100 1000Elapsed time
差が大きい
skb_copy_datagram_iovec
1000_clean_rx_irq
netif_receive_skbip_rcv
ip_rcv_finish tcp_v4_do_rcv
tcp_v4_rcv tcp_rcv_established
__kfree_skb
28
ケーススタディでのaccess ポイントカットの有用性 Sk_buff 構造体の全メンバーを access ポイント
カット Linux ネットワークサブシステムの挙動を把握
もし、 execution ポイントカットでやるとしたら… ネットワークサブシステムについての詳細な知識が必要
過不足なく関数を選択するため 本ケーススタディでは 76 箇所 ( 関数 21 個 ) にてログ取得
最適化禁止が必須 Inline 関数への対応が必要
現実と大幅に異なる OS カーネルは通常最適化オプションつきでコンパイルされる 例 ) Linux カーネルは -Os最適化オプションでコンパイル
本ケーススタディでは static inline 関数 6 箇所にてログ取得
29
KLASY の有用性 (まとめ)
構造体メンバーアクセスをポイントカット 構造体メンバーアクセスは大規模 C プログラムで
使用 関数間でのデータの受け渡しに利用 ポリモルフィズムの実現に利用
メンバーに関数へのポインターを保持 例 ) 仮想ファイルシステム (VFS) 、仮想デバイスなど
例 ) OS kernel (FreeBSD, Linux), X window System
実行時コンテキストの取得 実行時コンテキストがあると…
詳細な調査が可能 必要なデータのみのログ出力が可能
ログ保存領域を節約
30
既存のC 言語用動的アスペクト指向システム TOSKANA [Engel ’05], DAC++ [Almajali ’05],
TinyC2 [Zhang ’03],and Arachne [Douence ’05] 実行時コード操作 関数実行、呼び出しのみポイントカット可能
シンボル情報を不使用 TOSKANA-VM [Engel ’05]
仮想機械上で OS カーネルを実行 実際のハードウェア上のプロファイリングは不
可能
31
実験
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
32
実験結果
現実的なオーバーヘッドであることを確認 オーバーヘッド :
0 ~ 12 % 平均 : 4.4 %
dhry2reg: drystonesyscall: システムコールpipe: pipe システムコールexecl: execl システムコールcontext: コンテキストスイッチ
0200400600800
1000
dhry2
reg
sysc
allpip
eex
ecl
conte
xt
Linux KLASY
33
関連研究
C 言語用静的アスペクト指向システム 実行中のカーネルを変更可能 プロファイリングコードの変更に再起動が必要
例 ) AspectC [Coady ’01], AspectC++ [Spinczyk ’02]
カーネルプロファイラー LKST 、 DTrace [Cantrill ’04] 、 SystemTAP [Prasad ’05] 、
LTT [Yaghmour ’00] カーネル内で発生したイベントについてログを取得するツー
ル プロファイラー開発者の設定した箇所でのみプロファイリン
グ可能