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