はじめに

こんにちは!今回はstraceコマンドについて解説します。

straceはプロセスが実行するシステムコール(Linux カーネルへのリクエスト)をリアルタイムで追跡するコマンドです。プログラムがファイルを開いたり、ネットワークに接続したり、メモリを確保したりする時、その動きを全部見ることができます。

デバッグやトラブルシューティングで「このプログラム、何してるんだろう?」って時に超便利ですよ。

straceコマンドとは

straceは、プロセスの動作を詳細に追跡するデバッグツールです。“system call trace"の略ですね。

プログラムが実行するすべてのシステムコールを表示します。ファイルの読み書き、プロセス生成、シグナル処理、ネットワーク通信など、あらゆるカーネルとのやり取りが記録されます。

「このコマンド、なぜ遅いのか」「なぜエラーが出るのか」という問題を調べる時に活躍します。プログラム開発者やシステム管理者の強い味方です。

基本構文

1
strace [オプション] コマンド [コマンドの引数]

指定したコマンドを実行しながら、システムコールをリアルタイムで表示します。

1
strace [オプション] -p PID

実行中のプロセス(PID)にアタッチしてトレースすることもできます。

主なオプション

オプション 説明
-e trace=SYSCALL 特定のシステムコール(例:open, read)のみを表示
-o FILE 出力をファイルに保存
-p PID 実行中のプロセスをアタッチしてトレース
-f 子プロセスも一緒にトレース
-c システムコール統計を表示
-v 詳細表示(構造体の内容なども表示)
-e write write系のシステムコール(write, pwrite64など)をトレース
-e open,read 複数のシステムコールを指定
-s NUM 表示する文字列の最大長を指定(デフォルト32)
-x 16進数で表示
--follow-forks -fと同じ(フォークを追跡)

使用例

例1: 基本的なトレース

1
strace ls /tmp

実行結果:

1
2
3
4
5
6
7
execve("/bin/ls", ["ls", "/tmp"], 0x7fff8c0ed270 /* 54 vars */) = 0
brk(NULL)                               = 0x55f3e8e5a000
arch_prctl(ARCH_SET_FS, 0x7f8f0d3b8080) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8f0d3b6000
access("/etc/ld.so.preload", F_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
...(以下省略)

lsコマンドが実行する全システムコールが表示されます。たくさん出ますね。

例2: 特定のシステムコールのみを表示

1
strace -e trace=open,read ls /tmp

実行結果:

1
2
3
4
5
6
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0@\n\2\0\0\0\0\0"..., 832) = 832
openat(AT_FDCWD, "/tmp", O_RDONLY|O_CLOEXEC) = 3
read(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 32768) = 4096
...(省略)

openreadシステムコールだけが表示されます。ノイズが減ってわかりやすいですね。

例3: 出力をファイルに保存

1
strace -o output.log ls /tmp

実行結果:

1
(コマンド出力はそのまま表示される)

トレース結果をoutput.logに保存します。ログを後で確認できるので便利です。

例4: 統計情報を表示

1
strace -c ls /tmp

実行結果:

1
2
3
4
5
6
7
8
9
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 15.23    0.001234          20        61           mmap
 12.45    0.001011          18        56           mprotect
  8.76    0.000712          15        48           open
  6.54    0.000531          20        27           read
  ...
------ ----------- ----------- --------- --------- ----------------
100.00    0.008123                   345        12 total

システムコール別の実行時間と呼び出し回数の統計を表示。どのシステムコールが遅いかわかります。

例5: write系のシステムコールをトレース

1
strace -e write echo "Hello, Linux!"

実行結果:

1
2
write(1, "Hello, Linux!\n", 14)         = 14
+++ exited with 0 +++

echoが標準出力(FD 1)に"Hello, Linux!\n"と14バイト書き込んでいることがわかります。

例6: 実行中のプロセスにアタッチ

1
2
3
4
5
# ターミナル1: 実行時間が長いプロセスを起動
sleep 100

# ターミナル2: PIDを確認
ps aux | grep sleep

実行結果:

1
user      12345  0.0  0.0   2484   512 pts/0    S+   12:34   0:00 sleep 100
1
strace -p 12345

実行結果:

1
strace: attach: ptrace(PTRACE_ATTACH, 12345): Operation not permitted

実行中のsleepプロセスにアタッチしてトレースします。(権限不足の場合はsudoが必要)

例7: 子プロセスもトレース

1
strace -f bash -c "ls /tmp; pwd"

実行結果:

1
2
3
4
5
6
execve("/bin/bash", ["bash", "-c", "ls /tmp; pwd"], 0x7fff...) = 0
...
execve("/bin/ls", ["ls", "/tmp"], ...) = 0
...
execve("/bin/pwd", ["pwd"], ...) = 0
...

-fフラグで子プロセスも追跡されます。パイプやコマンド連鎖の動きが見えます。

例8: ファイルオープンのトレース

1
strace -e trace=openat cat /etc/hostname

実行結果:

1
2
3
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/etc/hostname", O_RDONLY) = 3

ファイルがどうやって開かれるかがわかります。openatが使われているんですね。

例9: エラーを追跡

1
strace cat /nonexistent

実行結果:

1
2
3
...
openat(AT_FDCWD, "/nonexistent", O_RDONLY) = -1 ENOENT (No such file or directory)
...

-1ENOENTエラーが表示されます。「なぜ失敗したか」がすぐわかります。

例10: ネットワーク通信をトレース

1
strace -e trace=network wget https://example.com

実行結果:

1
2
3
socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) = 3
connect(3, {sa_family=AF_INET, sin_port=htons(443), sin_addr=inet_aton("93.184.216.34")}, 16) = 0
...

HTTPやDNS通信の詳細が見えます。

Tips・注意点

出力が多い場合はフィルタリング

straceの出力は膨大です。必ず-e trace=でフィルタリングしましょう。

1
2
3
4
5
# 悪い例:全出力でカオス
strace ls

# 良い例:必要なものだけ
strace -e trace=open,read ls

標準出力とトレース出力を分ける

1
strace -o strace.log ls /tmp

-oで出力をファイルに保存すれば、コマンドの結果はそのまま表示されます。

権限が必要な場合がある

実行中のプロセスにアタッチするときはsudoが必要な場合があります。

1
sudo strace -p PID

統計情報は最後に表示

1
strace -c command 2>&1 | tail -20

-cで統計を見たい時は、出力の最後の方を見ましょう。

実践的な使い方

プログラムが遅い理由を調べる

1
strace -c slow_program > /dev/null

統計を見て、どのシステムコールに時間がかかってるか調べます。

ファイルアクセスパターンを分析

1
strace -e trace=open,openat,read,write python script.py

プログラムがどんなファイルにアクセスしているか、読み込みと書き込みのバランスはどうかがわかります。

デバッグ:プログラムがクラッシュする直前の動作

1
strace ./buggy_program 2>&1 | tail -20

クラッシュ直前のシステムコール列を見れば、原因がわかることがあります。

ネットワーク通信の確認

1
strace -e trace=socket,connect,sendto,recvfrom curl https://example.com

HTTPクライアントがどう通信しているか追跡できます。

まとめ

straceコマンドのポイント:

  • プロセスが実行するシステムコールを追跡する強力なデバッグツール
  • -e trace=: 特定のシステムコール(open, read, writeなど)をフィルタリング
  • -o FILE: 出力をファイルに保存
  • -c: システムコール統計を表示(パフォーマンス分析に最適)
  • -f: 子プロセスもトレース
  • -p PID: 実行中のプロセスにアタッチ
  • よく使う組み合わせ: strace -e trace=open,read -o log.txt command

プログラムが何してるか不可解な時は、straceで追跡して真実を見ましょう。デバッグの最強の武器ですよ!