ギーク
straceでプログラムを感じる
Takeyuki Sato
感じるとは
プログラムは、ソースコードを読まないと挙動を把握できないかというとそうではありません。strace,dtrace,mtraceなどのtrace系コマンドを使って挙動を細部まで理解できなくとも大まかに把握することができます。そう、感じるのです(爆
さて、早速ですが「ls」コマンドを感じてみたいと思います。
「ls」を感じてみる
$ strace ls
そうすると標準出力にバーっと出ます。
以下、一部省略
execve("/bin/ls", ["ls"], [/* 51 vars */]) = 0 ↑どのプログラムもまずは実態の呼び出し!引数なしの純粋さ! brk(NULL) = 0x7a0000 ↑動的に割り当てる領域の地面作ったる! access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) ↑なんだかよくわからないけどファイルないみたいだから無視! mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fecaf534000 ↑仮想メモリ 8192バイトもーらい!ここら変にこれから色々なプログラムロードしていくのか!? ... open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3 ↑printfとか入ってるツールボックス開けなきゃだ...ファイルディスクリプタ3!!!! read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P\t\2\0\0\0\0\0"..., 832) = 832 ↑ファイルディスクリプタ3よりバイナリで読みま〜す! fstat(3, {st_mode=S_IFREG|0755, st_size=1864888, ...}) = 0 ↑ファイルの状態取得してる!これでエラーが出るのはファイル名が長いとかのイチャモンで弾かれることあるけど今回はおっけーのreturn 0! mprotect(0x7fecaeee5000, 2097152, PROT_NONE) = 0 ↑指定のアドレス守ってあげる! ... open("/lib/x86_64-linux-gnu/libpcre.so.3", O_RDONLY|O_CLOEXEC) = 3 ↑正規表現ライブラリも欲しいのか! read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0000\25\0\0\0\0\0\0"..., 832) = 832 ... open("/proc/filesystems", O_RDONLY) = 3 fstat(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0 read(3, "nodev\tsysfs\nnodev\trootfs\nnodev\tr"..., 1024) = 380 read(3, "", 1024) = 0 close(3) = 0 ↑ファイルシステム一覧を取得したみたい... open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=4016704, ...}) = 0 mmap(NULL, 4016704, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fecae2c0000 close(3) = 0 ↑文字コードについて取得したみたい... ioctl(1, TCGETS, {B38400 opost isig icanon echo ...}) = 0 ioctl(1, TIOCGWINSZ, {ws_row=48, ws_col=157, ws_xpixel=0, ws_ypixel=0}) = 0 open(".", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 3 ↑ついに準備が整いカレントディレクトリーのファイルディスクリプタを取得! fstat(3, {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 getdents(3, /* 36 entries */, 32768) = 1240 getdents(3, /* 0 entries */, 32768) = 0 ↑ファイルディスクリプタからディレクトリ構成もーらい! close(3) = 0 fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0 write(1, "build Desktop\tDocuments Downlo"..., 95build Desktop Documents Downloads Music ns-3-allinone Pictures Public Templates Videos ) = 95 ↑表示! close(1) = 0 close(2) = 0
慣れると読まなくていいところや繰り返しの部分がわかり5秒くらい(盛ってます)で把握できます!
さて、今回はシステムコールをトレースするツールを使いましたが、共有ライブラリ関数をトレースするのもありさらにプログラムの内部が詳しくわかりますがそれはまた別の記事に書きたいと思います。
問題
最後に以下の内部動作を予想しながらトレースして、対象ホストに接続してるシステムコールを見つけてみてください!
pythonの処理とか色々ごちゃごちゃあって大変かもしれませんがシステムコールにあたりをつけてそのシステムコールだけを表示するオプションがありますので調べてみてください!
$ strace python -c 'import socket;socket.getaddrinfo("google.com","www")'