strace on Ubuntu — システムコールトレースでプロセスの動作を解析

コマンド

strace は、プロセスが Linux カーネルに対して行う「システムコール」をリアルタイムで表示するコマンドです。「このコマンドがどのファイルを開いているのか」「なぜエラーが出るのか」といった疑問に、ログや設定ファイルを読むよりずっと直接的に答えてくれます。

私が Ubuntu 24.04 で実際に試してみると、echo hello という単純なコマンドでも 36 回ものシステムコールが走ることがわかりました。動的リンクや初期化処理が意外と多い、というのが正直な感想です。

本記事では Ubuntu 24.04 LTS の Docker コンテナで strace を実際に動かし、インストール方法から実用的なオプションまで実出力を交えて説明します。

この記事のポイント

  • apt install strace だけで使えます(Ubuntu 24.04 では strace 6.8 が入ります)
  • strace -c でシステムコールの種類と回数を集計できます
  • -e trace=openat などのフィルタで、見たい情報だけ絞れます
  • 既存プロセスにアタッチするには strace -p <PID> を使います
  • 出力が多すぎる場合は -o ファイル名 でファイルに書き出すと読みやすくなります

目次

  1. strace とは
  2. インストール
  3. 基本的な使い方
  4. システムコールをフィルタする(-e オプション)
  5. 集計モード(-c オプション)
  6. 時間計測(-T オプション)
  7. 出力をファイルに保存する(-o オプション)
  8. 実行中のプロセスにアタッチする(-p オプション)
  9. 実用シナリオ
  10. よくあるエラーと対処
  11. まとめ

strace とは

Linux ではプログラムがハードウェアやOSの機能(ファイルの読み書き・ネットワーク接続・プロセス生成など)を使うとき、必ずカーネルに「システムコール」として依頼します。strace はそのシステムコールを逐一表示するツールです。

デバッガ(gdb)がソースコードレベルで内部を見るのに対して、straceバイナリがすでにある状態でもブラックボックスを覗ける という強みがあります。「設定ファイルのパスを間違えていてエラーが出る」「ライブラリが見つからない」といったトラブルは、strace でファイルアクセスを追うだけで一発でわかることが多いです。

インストール

手順1:パッケージを更新する




ubuntu@linuxlab: ~
$ sudo apt update
Hit:1 http://jp.archive.ubuntu.com/ubuntu noble InRelease
Reading package lists… Done

手順2:strace をインストールする




ubuntu@linuxlab: ~
$ sudo apt install strace
Reading state information… Done
The following NEW packages will be installed:
libunwind8 strace
Setting up strace (6.8-0ubuntu2) …

Ubuntu 24.04 では strace 6.8(パッケージ名 strace 6.8-0ubuntu2)がインストールされます。下図は実際のインストールログです。

apt install strace の実ログ(Ubuntu 24.04)
apt install strace の実ログ(Ubuntu 24.04)

手順3:バージョンを確認する




ubuntu@linuxlab: ~
$ strace –version
strace — version 6.8
Copyright (c) 1991-2024 The strace developers <https://strace.io>.
Optional features enabled: stack-trace=libunwind stack-demangle m32-mpers mx32-mpers
Ubuntu 24.04 + strace 6.8 環境確認(実測)
Ubuntu 24.04 + strace 6.8 環境確認(実測)

Ubuntu 22.04 では strace 5.16、Ubuntu 24.04 では strace 6.8 が入ります。バージョン間で出力形式は大きく変わりませんが、6.8 ではシステムコールの種別表示がより詳細になっています。下表は両バージョンを Docker で実際に確認した結果です。

Ubuntu 22.04 vs 24.04 strace バージョン比較表(実測)
Ubuntu 22.04 vs 24.04 strace バージョン比較表(実測)

基本的な使い方

もっとも基本的な使い方は、追跡したいコマンドの先頭に strace を付けるだけです。




ubuntu@linuxlab: ~
$ strace echo hello
execve(“/usr/bin/echo”, [“echo”, “hello”], 0x7ffe… /* 6 vars */) = 0
brk(NULL) = 0x55c3a1e5b000
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
…(中略)…
write(1, “hello\n”, 6hello) = 6
+++ exited with 0 +++

出力の読み方は、左から「システムコール名(引数)= 戻り値」という形式です。上の例では最後の write(1, "hello\n", 6) = 6 が実際に画面に文字を書いた行で、戻り値の 6 は書き込んだバイト数です。

注意

システムコールはかなりの量が出力されます。echo hello のような小さいコマンドでも 50 行以上になります。見たい情報だけ絞るには後述の -e フィルタを使うのがコツです。

strace 基本使用例ターミナル
strace 基本使用例ターミナル

システムコールをフィルタする(-e オプション)

-e trace=システムコール名 で、特定の種類のシステムコールだけに絞れます。これが実際の調査でいちばん使う機能です。

①ファイルアクセスだけ追う




ubuntu@linuxlab: ~
$ strace -e trace=openat cat /etc/hostname
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
67164b0c7983
+++ exited with 0 +++

Ubuntu 24.04 で実測すると、cat /etc/hostname は以下の順番でファイルを開いていました:ld.so.cache(動的リンク設定)→ libc.so.6(C標準ライブラリ)→ 最後にやっと /etc/hostname 本体、という流れです。目的のファイルにたどり着く前に 2 回ライブラリを読み込んでいる、というのが直感的にわかります。

strace -e trace=openat の実測トレース(実測)
strace -e trace=openat の実測トレース(実測)

②書き込みだけ追う




ubuntu@linuxlab: ~
$ strace -e trace=write echo hello
write(1, “hello\n”, 6hello)
= 6
+++ exited with 0 +++

write だけに絞ると、1 行しか出ません。「echo hello が何をしているか」という問いの核心がここにあります。

③複数のシステムコールを同時に指定する




ubuntu@linuxlab: ~
$ strace -e trace=openat,read,write ls /etc/hostname
openat(AT_FDCWD, “/etc/ld.so.cache”, O_RDONLY|O_CLOEXEC) = 3
read(3, “\177ELF\2\1\1\0…”…, 832) = 832
openat(AT_FDCWD, “/lib/x86_64-linux-gnu/libselinux.so.1”, O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, “/lib/x86_64-linux-gnu/libc.so.6”, O_RDONLY|O_CLOEXEC) = 3
write(1, “/etc/hostname\n”, 14/etc/hostname)
+++ exited with 0 +++

カンマ区切りで複数のシステムコールを同時に追えます。ファイルを開いて・読んで・書く、という一連の流れが見えます。

集計モード(-c オプション)

-c を付けると、プロセスが終了した後にシステムコールの種類・回数・所要時間を一覧で表示します。「どのシステムコールが多いか」を把握するのに便利です。




ubuntu@linuxlab: ~
$ strace -c echo hello
hello
% time seconds usecs/call calls errors syscall
—— ———– ———– ——— ——— —————-
0.00 0.000000 0 8 mmap
0.00 0.000000 0 2 openat
0.00 0.000000 0 1 write
0.00 0.000000 0 1 execve
0.00 0.000000 0 1 1 access
—— ———– ———– ——— ——— —————-
100.00 0.000000 0 36 1 total

Ubuntu 24.04 で実測した結果、echo hello は合計 36 回のシステムコール を呼んでいました。そのうち mmap(メモリマッピング)が 8 回と最多で、動的リンカの処理が大半を占めています。実際の文字出力(write)は 1 回だけです。

strace -c echo hello のシステムコール統計(実測)
strace -c echo hello のシステムコール統計(実測)

時間計測(-T オプション)

-T を付けると、各システムコールの末尾にかかった時間(秒)が表示されます。パフォーマンス問題の特定に役立ちます。




ubuntu@linuxlab: ~
$ strace -T -e trace=openat ls /etc/hostname
openat(AT_FDCWD, “/etc/ld.so.cache”, O_RDONLY|O_CLOEXEC) = 3 <0.000004>
openat(AT_FDCWD, “/lib/x86_64-linux-gnu/libselinux.so.1”, O_RDONLY|O_CLOEXEC) = 3 <0.000004>
openat(AT_FDCWD, “/lib/x86_64-linux-gnu/libc.so.6”, O_RDONLY|O_CLOEXEC) = 3 <0.000003>
openat(AT_FDCWD, “/lib/x86_64-linux-gnu/libpcre2-8.so.0”, O_RDONLY|O_CLOEXEC) = 3 <0.000005>
openat(AT_FDCWD, “/proc/filesystems”, O_RDONLY|O_CLOEXEC) = 3 <0.000004>
/etc/hostname
+++ exited with 0 +++

行末の <0.000004> が所要時間(秒)です。いずれも数マイクロ秒と高速ですが、NFS マウントされたファイルシステムや低速ディスクでは readwrite が数ミリ秒〜数十ミリ秒かかることもあり、そういった場合に -T が有効です。

strace フィルタ・オプション使用例(実測)
strace フィルタ・オプション使用例(実測)

出力をファイルに保存する(-o オプション)

strace の出力は stderr に出るため、通常は画面が出力で埋まります。-o でファイルに書き出すと後から読みやすくなります。




ubuntu@linuxlab: ~
$ strace -o /tmp/trace.log ls /etc/hostname
/etc/hostname
$ wc -l /tmp/trace.log
57 /tmp/trace.log
$ grep openat /tmp/trace.log
openat(AT_FDCWD, “/etc/ld.so.cache”, O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, “/lib/x86_64-linux-gnu/libselinux.so.1”, O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, “/lib/x86_64-linux-gnu/libc.so.6”, O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, “/lib/x86_64-linux-gnu/libpcre2-8.so.0”, O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, “/proc/filesystems”, O_RDONLY|O_CLOEXEC) = 3

保存したログに grep を組み合わせると、「この設定ファイルを本当に読んでいるか」「どのパスで検索しているか」を素早く確認できます。

実行中のプロセスにアタッチする(-p オプション)

すでに動いているプロセスを調べるには -p <PID> を使います。デーモンやサービスのトレースに便利です。




ubuntu@linuxlab: ~
$ sleep 100 &
[1] 4823
$ sudo strace -p 4823
strace: Process 4823 attached
restart_syscall(<… resuming interrupted syscall …>) = 0
nanosleep({tv_sec=97, tv_nsec=0}, NULL) = ? ERESTART_RESTARTBLOCK (Interrupted by signal)
^C (Ctrl+C で終了)
strace: Process 4823 detached

注意:sudo が必要

strace -p で他のユーザーのプロセスにアタッチするには sudo が必要です。自分が起動したプロセスなら sudo なしでアタッチできる場合もありますが、システムサービスは必ず sudo が要ります。

実用シナリオ

①「設定ファイルが読まれているか」を確認する

コマンドが期待通りに設定を読み込んでいるか疑問なとき、-e trace=openat で確認できます。




ubuntu@linuxlab: ~
$ strace -e trace=openat -o /tmp/ssh_trace.log ssh -V
OpenSSH_9.6p1 Ubuntu-3ubuntu13.11, OpenSSL 3.0.13 30 Jan 2024
$ grep “/etc/ssh” /tmp/ssh_trace.log
openat(AT_FDCWD, “/etc/ssh/ssh_config”, O_RDONLY) = 3

/etc/ssh/ssh_config を実際に開いているのが確認できます。「設定を変えたのに反映されない」という場合、別のパスを読んでいる可能性があり、こうして確認できます。

②ネットワーク接続を追う




ubuntu@linuxlab: ~
$ strace -e trace=network ping -c 1 8.8.8.8 2>&1 | head -15
socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP) = 3
socket(AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6) = 4
setsockopt(3, SOL_SOCKET, SO_MARK, [0], 4) = 0
connect(3, {sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr(“8.8.8.8”)}, 16) = 0
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.

trace=network はソケット・接続・送受信に関するシステムコールを一括でフィルタします。どこへ接続しているかを素早く確認できます。

③エラーの原因を探る

コマンドが「Permission denied」などのエラーで失敗するとき、strace でどのファイルやリソースへのアクセスが失敗したか特定できます。




ubuntu@linuxlab: ~
$ strace -e trace=openat some-failing-command 2>&1 | grep “ENOENT\|EACCES\|EPERM”
openat(AT_FDCWD, “/etc/myapp/config.toml”, O_RDONLY) = -1 ENOENT (No such file or directory)

ENOENT(ファイルが見つからない)・EACCES(権限なし)・EPERM(操作が許可されていない)でgrepすると、エラーの箇所だけ取り出せます。

よくあるエラーと対処

strace: attach: ptrace(PTRACE_SEIZE): Operation not permitted

他のユーザーのプロセスにアタッチしようとすると出ます。sudo strace -p <PID> で実行してください。

strace: exec: Exec format error

スクリプトファイルに直接 strace をかけようとしたときに出ます。strace bash script.sh のように、インタープリタを指定して実行してください。

出力が多すぎて読めない

まず -e trace=openat-e trace=network で絞り込み、-o /tmp/trace.log でファイルに書き出してから grep で検索するのがやりやすいです。

まとめ

よく使うオプションをまとめます。

オプション 意味 使いどころ
strace コマンド すべてのシステムコールを表示 とりあえず全体を把握したいとき
-e trace=openat ファイルアクセスだけ表示 設定ファイルのパスを確認したいとき
-e trace=network ネットワーク関連だけ表示 接続先やソケット操作を追うとき
-c システムコールの統計を表示 どのシステムコールが多いか把握したいとき
-T 各コールの実行時間を表示 パフォーマンス問題の箇所を絞るとき
-o ファイル トレース出力をファイルに保存 後から grep で絞り込みたいとき
-p PID 実行中のプロセスにアタッチ デーモンやサービスを調査するとき
-s N 文字列の表示長を N バイトに変更 デフォルト(32バイト)では内容が切れるとき
  • straceapt install strace で入ります(Ubuntu 24.04 では 6.8 がインストールされます)
  • -e trace=openat でファイルアクセスだけ絞ると、設定ファイルの読み込みパスがすぐわかります
  • -c の統計は「どのシステムコールが何回呼ばれているか」の全体像を掴むのに便利です
  • 出力が多いときは -o でファイル保存 → grep で絞り込む流れが実用的です

トラブルシュートに困ったとき、ログに何も出ない場合や設定が反映されているか疑わしいとき、ぜひ strace を試してみてください。意外と早く原因が見つかります。

著者アイコン
著者アイコン

VPS でサーバーを運用し始めると、なぜかサービスが起動しないとか、設定ファイルを変えたのに反映されないといったトラブルが必ず出てきます。そういうとき strace を使うと、ログに何も残っていなくても「このファイルを開こうとして失敗していた」というのが一目でわかります。入れておいて損はないツールです。

Linux をより深く活用したい方は、VPS を借りて実際にサーバーを動かしてみるのが近道です。

コメント

タイトルとURLをコピーしました