🐧 Linux 総合学習プラットフォーム
システムコール ・ 上級

strace でシステムコールを観察する

strace は、あるコマンドが内部でどのシステムコールを、どんな引数で呼び、何を返したかを順番に表示するツールです。プログラムのソースが手元になくても、OSとどうやり取りしているかを外から覗けるのが強みです。各行は「呼び出し名(引数) = 戻り値」の形で並び、失敗した呼び出しには errno の名前(例: ENOENT)も添えられます。-e trace=open,read のように対象を絞ったり、-f で子プロセスまで追跡できます。「設定ファイルをどこから読もうとして失敗したのか」といった障害調査でも非常に役立ちます。

プログラムが内部で何をしているかを知りたいとき、ソースコードを読むのが正攻法ですが、ソースが手元にない、あるいは膨大すぎて追いきれない、ということは現場では珍しくありません。そんなときに頼りになるのがstraceです。straceは、あるコマンドが実行中にどんなシステムコール(syscall)を、どんな引数で呼び、何を返したかを、時間順にすべて表示してくれるツールです。プログラムとカーネルのあいだで交わされる「会話」を、外からそっくり盗み聞きするようなものだと考えると分かりやすいでしょう。中身がブラックボックスに見えるプログラムでも、「OSとどんなやり取りをしているか」という観点からなら、その振る舞いを客観的に観察できます。これは、ソースを書き換えたりデバッガを仕込んだりしなくても使える、手軽さが大きな魅力です。

出力の読み方

使い方は拍子抜けするほど単純で、調べたいコマンドの前にstraceを付けるだけです。たとえばstrace ./helloのように実行します。すると画面には、そのプログラムが発行したシステムコールが一行ずつ流れていきます。各行は基本的に「呼び出し名(引数の並び) = 戻り値」という決まった形をしています。実例で言えば、execve("./hello", ["./hello"], ...) = 0 はプログラムの起動そのもの、openat(...) = 3 はファイルを開いてファイルディスクリプタ(fd)の3番を得たこと、write(1, "Hello, World!\n", 14) = 14 は1番(標準出力)へ14バイトを書き込んで14バイト処理できたこと、を表します。プログラムの起動から終了まで、実際に発行された生のシステムコールが順番に見えるため、「このプログラムは結局どんな順で、カーネルに何を頼んだのか」が一目で追えます。最初は見慣れない呼び出しが大量に並んで圧倒されますが、自分が書いたコードに対応する行(writeやopenなど)を拾い読みするところから慣れていくとよいでしょう。

失敗を見つける ― errno の表示

straceがとりわけ威力を発揮するのは、失敗した呼び出しを見つけるときです。システムコールが失敗すると戻り値は-1になり、その理由はerrno(errno)に記録されますが、straceは気の利いたことに、その-1のうしろに、errnoの名前とその意味する英文まで添えて表示してくれます。たとえば openat("/etc/myapp.conf", O_RDONLY) = -1 ENOENT (No such file or directory) という一行を見れば、「このプログラムは/etc/myapp.confという設定ファイルを読もうとしたが、そのファイルが存在せず失敗した」と即座に分かります。アプリが原因不明で起動しない、設定が反映されない、ライブラリが見つからないといった障害の調査で、「どのファイルを、どこから読もうとして、なぜ失敗したのか」を、ソースを一行も追わずに突き止められるのは、実務で計り知れないほど重宝します。エラーメッセージが不親切なプログラムでも、straceをかければ本当の失敗箇所が浮かび上がる、ということは少なくありません。

対象を絞る ― 主要なオプション

実際のプログラムは起動するだけで何百ものシステムコールを呼ぶため、そのまま流すと情報の洪水になり、肝心の行を見失います。そこで対象を絞り込むオプションが役立ちます。-e trace=openat,read のように書くと、指定した種類のシステムコールだけを表示できます。ファイル関連だけを見たいなら-e trace=file、ネットワーク関連だけなら-e trace=networkといった、まとまった指定も可能です。-fを付けると、forkなどで生まれた子プロセスまで追跡対象に含めます。前章で見たfork→exec→waitの流れを観察したいときは、この-fが欠かせません。出力を画面ではなくファイルに保存して後でじっくり読みたいときは-o ログファイル名を、各呼び出しにどれだけ時間がかかったかを計測したいときは-Tを、システムコールごとの回数や所要時間を集計したいときは-cを使います。これらを組み合わせて、知りたい情報だけを浮かび上がらせるのが、straceを使いこなすコツです。

ltrace との違い、そして使いどころ

よく似たツールにltraceがあります。straceがカーネルへのシステムコールを表示するのに対し、ltraceはC標準ライブラリ(libc)の関数呼び出しを表示します。たとえば自作プログラムがprintfを呼ぶ様子はltraceで見え、その結果として実際に発行されるwriteはstraceで見える、という棲み分けです。両者を見比べると、ライブラリ関数という上の層と、システムコールという下の層の関係が立体的に理解できます。straceの典型的な使いどころをいくつかまとめておきます。起動に失敗するプログラムの原因究明、設定ファイルや共有ライブラリをどこから探しているかの確認、想定外に遅い処理でどのシステムコールが時間を食っているかの特定、そして「このプログラムは外部とどんな通信をしているのか」という挙動の調査です。すでに動いているプロセスを途中から覗きたいときは、-p プロセス番号 のように対象のPIDを指定してアタッチすることもできます。一点だけ注意すると、straceで監視されたプログラムはシステムコールごとに横から覗かれるぶん動作が遅くなるため、時間に敏感な処理の本番計測には向きません。あくまで挙動の調査や原因究明の道具と心得ましょう。出力に出てきた呼び出し名で正確な仕様が知りたくなったら、その名前をそのままman 2で引けば、引数や戻り値、設定されうるerrnoの意味を一次情報として確認できます。straceで現象をつかみ、man 2で仕様を裏取りする、という往復が調査の王道です。

この項目に出てくる用語

システムコールしすてむこーる
アプリがカーネルに処理を依頼する公式の窓口。
errnoえらーなんばー
直近のシステムコール失敗の原因を表す番号。
ファイルディスクリプタふぁいるでぃすくりぷた
開いたファイルを指す小さな整数(0以上)。

関連コマンド

straceltrace

▶ 学習アプリでこの続きを学ぶ・演習する