川の流れのように‥(Eiji Ota's Weblog)
mdbの強力な助っ人CTF (でも、誰もみてくれない可哀想なやつ)‥の話
Solaris10の新機能ではありませんけれど、今日はSolaris9で導入されたmdbのCTF (Compact ANSI-C Type Format)についてお話したいと思います。 日頃お世話になっているツールですものね。クラフトマンは、ツールを大事にするものですもの。(^-^) 率直に言って、このCTFは、カーネルのデバックには相当な強力な味方になっていて、 一度これにはまると、CTF無しでデバックしたくなくなるほど、便利です。 またデバック時間もかなり節約してくれる、やりくり上手。開発者には必須のアイテムって言っても 良いくらいなんですね。でも、これが黒子、縁の下の力持ちなんですね。 そんな、日頃から陰で支えてくれているのに、陽の当たるところには出ない、CTFについて ちょっと紹介してあげたいって今回は思いました。^_^ oO (Yoroshiku!) CTF以前は、デバック情報を残すためにはコンパイラ・オプションで-gを指定するなどしなくて はなりませんでした。その結果、コンパイルされたカーネルは肥大化してしまい、 32bitの890Kバイト程のunixカーネルが、7Mバイト強まで膨れ上がってしまう有様で、 とても製品版のカーネルに使える状態ではありませんでした。 こんな大きいカーネルではロードにも時間がかかるし、格納媒体も増えてしまいますよね。 (でも、DVDだったら一枚で収まるのかな?) それに、CTF以前のデバックでは、adbのマクロや自分で作成したmdbのコマンドなどを使用して データ構造をクラッシュダンプから出力してやらなくてはならず、ソースが変更されたり、 別のバージョンのカーネルが使用されたりしてデータ構造に変更があったりすると、 表示された結果が妥当なものかすら分からないことがありました。 開発者としては、こっちも相当大きいですよね。adbのマクロやmdbのコマンドを その都度作成していくのは、大変手間暇がかかって、うんざりすることもままありました。 (今だから言える! えへへへへ‥) 結果、各開発者は各々お手製のコマンド、ライブラリを持つことになるんですけれど‥。 (まぁ、自然な流れとして‥) こういう問題を解決するため、C言語のシンボル情報、静的変数、大域変数の情報、リターン値、 引数情報などを効率良くカーネルに残すことを目的に、CTFが作られたわけです。 偉い、さすがっ! って一応エールを送りませう。これを開発したのは、Mike Shaprioさんと Matt Simmonsさんですね。Mikeさんは、元々mdbの開発者です。 そして、MattさんはSolaris10でkmdbを開発しています。(パチパチ "-") CTFの情報に加えて、不要な情報の削除、重複情報の削除、圧縮などを施した結果、 平均して1-2%の増加でデバック情報をカーネルに含ませることができました。 これなら、いつもカーネルに含ませることができますね。(うん、グットだぁ) CTF情報はコンパイル時に作成され、ELFセクションに一度格納された後、 リンカーでロードされる時に、各々のモジュールの持つ情報はマージされて、メモリーにロードされます。 パニックが発生した場合に、自動的にこれらの情報がクラッシュダンプに入るので、 デバック時には動作中のカーネルそのものの情報が得られます。 この仕組みにより、ソース変更や、カーネル・バージョンを気にせずデバックが可能になりました。 そして、Solaris9からmdbはこのCTF情報を使用しており、次のコマンドが導入されています。 ・sizeコマンド ・offsetofコマンド ・printコマンド それでは、ちょっとこれらのコマンドを使うとどういうことになるか、見てみましょうね。(うんうん) まず、psコマンドで、パニック発生時に動いていたプロセスをリストアップさせて、 この中のautomoutdのプロセス構造体を表示させてあげましょう。
> ::ps
S PID PPID PGID SID UID FLAGS ADDR NAME
R 0 0 0 0 0 0x00000001 0000000001842d30 sched
R 3 0 0 0 0 0x00020001 0000030005089b90 fsflush
R 2 0 0 0 0 0x00020001 000003000508a778 pageout
R 1 0 0 0 0 0x42004000 000003000508b360 init
R 101898 1 101898 101898 0 0x42000000 0000030019763398 automountd
R 101881 1 101881 101881 0 0x42000000 000003000f2197f8 nscd
:
automountdのプロセス構造体のアドレスは0x30019763398ですから、
このアドレスをprintコマンドに指定させてあげるだけで、プロセス構造体の中身を見ることができます。
> 0000030019763398::print proc_t
{
p_exec = 0x3001280bd00
p_as = 0x300070617a8
p_lockp = 0x300063f2a00
p_crlock = {
_opaque = [ 0 ]
}
p_cred = 0x300127cf350
p_swapcnt = 0
p_stat = '\002'
p_wcode = '\0'
p_pidflag = 0
p_wdata = 0
p_ppid = 0x1
p_link = 0
p_parent = 0x3000508b360
p_child = 0
p_sibling = 0x3000f2197f8
:
また、プロセス構造体のサイズが知りたい時は、sizeofコマンドを使用して、
> ::sizeof proc_t sizeof (proc_t) = 0xbd0とやってやります。 例えば、プロセス構造体の中にあるas構造体のオフセットが知りたければ、offsetコマンドを使用して、
> ::offsetof proc_t p_as offsetof (proc_t, p_as) = 8とやってやります。 これらのコマンドは、皆クラッシュダンプに格納されているCTF情報を参照してデータ構造(上の例だとプロセス構造体)を把握しているわけですね。実に便利っ! このprintコマンドは、多彩なオプションを実はもっていて、非常に便利。このprintコマンドだけでもかなりデバックがサクサク進む‥。ありがたや、ありがたや。
> ::help print NAME print - print the contents of a data structure SYNOPSIS [ addr ] ::print [-aCdhiLptx] [-c lim] [-l lim] [type] [member|offset ...] DESCRIPTION -a show address of object -c limit limit the length of character arrays -C unlimit the length of character arrays -d output values in decimal -h print holes in structures -l limit limit the length of standard arrays -L unlimit the length of standard arrays -n don't print pointers as symbol offsets -p interpret address as a physical memory address -t show type of object -i interpret address as data of the given type -x output values in hexadecimal type may be omitted if the C type of addr can be inferred. Members may be specified with standard C syntax using the array indexing operator "[index]", structure member operator ".", or structure pointer operator "->". Offsets must use the $[ expression ] syntax ATTRIBUTES Target: kvm Module: mdb Interface Stability: Evolving例えば、-taオプションを指定してやると、先の例は次のようになります。
> 0000030019763398::print -ta proc_t
{
30019763398 struct vnode *p_exec = 0x3001280bd00
300197633a0 struct as *p_as = 0x300070617a8
300197633a8 struct plock *p_lockp = 0x300063f2a00
300197633b0 kmutex_t p_crlock = {
300197633b0 void *[1] _opaque = [ 0 ]
}
300197633b8 struct cred *p_cred = 0x300127cf350
300197633c0 int p_swapcnt = 0
300197633c4 char p_stat = '\002'
300197633c5 char p_wcode = '\0'
300197633c6 ushort_t p_pidflag = 0
300197633c8 int p_wdata = 0
300197633cc pid_t p_ppid = 0x1
300197633d0 struct proc *p_link = 0
300197633d8 struct proc *p_parent = 0x3000508b360
300197633e0 struct proc *p_child = 0
300197633e8 struct proc *p_sibling = 0x3000f2197f8
300197633f0 struct proc *p_psibling = 0
:
このprintコマンドだけを見ても、CTFが如何に便利か分かりますよね。
でも、普段は誰からも意識されない存在。ちょっと可哀想。^^;
皆さん、mdbを使った時に、是非CTF君のことをちょっと思い出してあげて下さいね。(^-^)v
それではよい週末を。
Posted at 01:04午後 6 03, 2005 by eota in opensolaris | Comments[0]