IT戦記 このページをアンテナに追加 RSSフィード Twitter

2008-04-01

駄文 - C 言語の勉強

C で Hash table を作ってみた。 22 時から 4 時までの 6 時間でやっと完成。 orz

300 行くらい。まだまだ LL 脳が抜けない。

昔、仕事で使ってたんだけどなー。おっかしーなー

gdb のプロになりたい。

C 言語はエラー処理が大変、あとデータのインスペクトが難しい。

MacPorts の SpiderMonkey と CPAN の JavaScript::SpiderMonkey を一緒に入れる

MacPortsSpiderMonkey を入れた場合は、以下の位置にライブラリファイルとインクルードファイルがあります。

/opt/local/lib/*
/opt/local/include/*

なので、 JavaScript::SpiderMonkeyインストールする際には、 Makefile.PL の 38 行目あたりを以下のように修正しなければなりません。

my %possible_install_paths = ( 
  "../js/src/*"      => "../js/src",
  "/usr/lib"         => "/usr/include",
  "/usr/local/lib"   => "/usr/local/include",
  "/usr/lib/firefox" => "/usr/include/firefox",
  "/opt/local/lib"   => "/opt/local/include",      # この行を追加
);

テストが少しだけ失敗してたけど、とりあえずインストールはできました。

dtrace を使ったシステムコールのトレース

dtrace とは

Mac OS X Leopard から導入された、プログラムを書き換えることなくプログラムの情報を監視(トレース)できる便利ツールです。

使い方

たとえば、 vim という名前のプロセスからシステムコール stat, open, stat64 されたときに第一引数(ファイル名)を監視するには以下のようにします。

$ sudo dtrace -n "syscall::open:entry, syscall::stat:entry, syscall::stat64:entry / execname == \"vim\" / { trace(copyinstr(arg0)) } "

説明

まず、 -n というのは「probe」の「name」を指定して dtrace する!という意味です。

$ sudo dtrace [-n [[[ provider: ] module: ] func: ] name [[ predicate ] action ]]
probe とは

probe というのは、 監視ポイントのことです。

以下のコマンドで監視ポイントの一覧を見ることができます。

$ sudo dtrace -l

たくさんあるので、 grep するといいです。

$ sudo dtrace -l | grep syscall

とにかくいろんな情報を監視できるのが分かりますね!

probe の指定方法

probe は固有の数字か、「provider:module:func:name」というコロンで区切られた文字列で指定します。

文字列で指定する場合は、さっきのように -n を使います。

$ sudo dtrace -n syscall::open:entry

syscall には module 名がないのでコロンコロンになってます。

また、

数字で指定する場合は、以下のように -i を使います。

$ sudo dtrace -i 18270

この数字は sudo dtrace -l で分かります。

action とは

action とは監視した情報をどのようにトレースするかという情報です。中括弧で囲って書きます。

たとえば、 プロセス名とプロセス ID を trace したい場合は、以下のようにします。

$ sudo dtrace -n "syscall::open:entry { trace(execname); trace(pid) }"

この中括弧の中には、 d 言語(別の D 言語と誤解しないように)というスクリプトのステートメント(文)を書きます。

trace 以外にも様々な関数があり、 execname や pid 以外にも様々な変数があります。

predicate とは

predicate とは監視した情報をフィルタリングする場合に使います。スラッシュで囲って書きます。

ここには、 d 言語の式を書きます。

たとえば、以下のようにすれば vim という名前のプロセスか emacs という名前のプロセスだけをトレースすることができます。

$ sudo dtrace -n "syscall::open:entry / execname == \"emacs\" || pid == \"vim\" / { trace(execname); trace(pid) }"
d 言語の詳細なドキュメントは

ここにあります。

http://wikis.sun.com/display/DTrace/Documentation

d 言語を別のファイルにする

d 言語を別のファイルにすることもできます

#! /usr/sbin/dtrace -s

syscall::open:entry
/ execname == "emacs" || pid == "vim" /
{
  trace(execname);
  trace(pid);
}

profile:::tick-100sec
{
  trace("timeout!\n")
  exit(0)
}

パーミッションを設定すれば、そのまま実行できます。

実際に実行してみる

こんな感じです。

$ sudo dtrace -n "syscall::open:entry, syscall::stat:entry, syscall::stat64:entry / execname == \"vim\" / { trace(copyinr(arg0)) } " > dtrace.txt &
[1] 70629
dtrace: description 'syscall::open:entry, syscall::stat:entry, syscall::stat64:entry ' matched 3 probes

$ vim hoge

$ fg
sudo dtrace -n "syscall::open:entry, syscall::stat:entry, syscall::stat64:entry / execname == \"vim\" / { trace(copyinstr(arg0)) } " > dtrace.txt
^C

$ cat dtrace.txt 
CPU     ID                    FUNCTION:NAME
  0  17970                       stat:entry   /usr/lib/libncurses.5.4.dylib    
  0  17970                       stat:entry   /usr/lib/libiconv.2.dylib        
  0  17970                       stat:entry   /usr/lib/libgcc_s.1.dylib        
  0  17970                       stat:entry   /usr/lib/libSystem.B.dylib       
  0  17970                       stat:entry   /usr/lib/system/libmathCommon.A.dylib
  0  17604                       open:entry   /dev/dtracehelper                
  0  18270                     stat64:entry   /Users/amachang/Downloads        
  0  18270                     stat64:entry   /Users/amachang                  
  0  17970                       stat:entry   /usr/share/vim/vim70             
  0  17970                       stat:entry   /usr/share/vim                   
  0  18270                     stat64:entry   /Users/amachang/Downloads        
  0  17970                       stat:entry   hoge                             
  0  17604                       open:entry   /usr/share/terminfo/78/xterm-color
  0  17604                       open:entry   .                                
  0  18270                     stat64:entry   /usr/share/vim                   
  0  17970                       stat:entry   /usr/share/vim/vimrc             
  0  17970                       stat:entry   /usr/share/vim/vimrc             
  0  17604                       open:entry   .                                
  0  18270                     stat64:entry   /Users/amachang                  
  0  17970                       stat:entry   /Users/amachang/.vimrc           
  0  17970                       stat:entry   /Users/amachang/.vimrc           
:
:

おおお dylib が読み込まれて、設定ファイルが読み込まれていく様が分かりますね!

まとめ

Leopard に元々入ってる RubyPerl などはパッチがあたっていて関数呼び出しなどがトレースできるらしいのですが、自分がやってみたらできませんでした。何故だろう?

とにかく ktrace の変わりに dtrace を使ってみてはいかがでしょうか。

objdump, objcopy を Mac に入れる

こっちのエントリはエイプリルフールネタではないよ

インストール

nm とか strings とかとあわせて binutils っていうツールなんですね

$ sudo install binutils

で、インストールできました。

内容

で、どんなものがインストールされたかというと

$ port contents binutils
Port binutils contains:
  /opt/local/bin/gaddr2line
  /opt/local/bin/gar
  /opt/local/bin/gc++filt
  /opt/local/bin/gnm
  /opt/local/bin/gobjcopy
  /opt/local/bin/gobjdump
  /opt/local/bin/granlib
  /opt/local/bin/greadelf
  /opt/local/bin/gsize
  /opt/local/bin/gstrings
  /opt/local/bin/gstrip
:
:

って感じで、 /opt/local/bin の中に g というプリフィックスが付いた状態でインストールされていました。

OS X が元々用意している nm や strings などと区別して使うためなんですかね?

こんな感じ

$ gobjdump -d /bin/ls
??? /bin/ls ??:
unable to read unknown load command 0x1b
unable to read unknown load command 0x1d

/bin/ls:     ?ե???????? mach-o-le

????????? LC_SEGMENT.__TEXT ?εե?????֥?:

00001000 <LC_SEGMENT.__TEXT>:
    1000:	ce                   	into   
    1001:	fa                   	cli    
    1002:	ed                   	in     (%dx),%eax
    1003:	fe 07                	incb   (%edi)
    1005:	00 00                	add    %al,(%eax)
    1007:	00 03                	add    %al,(%ebx)
    1009:	00 00                	add    %al,(%eax)
    100b:	00 02                	add    %al,(%edx)
    100d:	00 00                	add    %al,(%eax)
    100f:	00 0e                	add    %cl,(%esi)
    1011:	00 00                	add    %al,(%eax)
    1013:	00 18                	add    %bl,(%eax)
    1015:	05 00 00 85 00       	add    $0x850000,%eax
    101a:	00 00                	add    %al,(%eax)
    101c:	01 00                	add    %eax,(%eax)

なんかちょっと文字化けしてますが、普通に使えました。

ちゃんと、 mach-o-le にも対応してるんですね(あたりまえか