
eBPF上に構築されたモダンなシステムコールトレーサー。straceを思い浮かべてください。ただし、実際のTUI、スマートフィルター、TLS復号化、そして実際に読みやすい出力を備えています。
Linux用のシステムコールトレーサーで、eBPF上に構築されています。straceに似ていますが、ライブTUI、スマートフィルター、実際に読める引数デコード機能を備えています。

$ sudo snoop curl https://example.com
[ 0.001] curl(1234/1234) openat(AT_FDCWD, "/etc/ssl/certs/ca-certificates.crt", O_RDONLY) = 4 <0.031ms>
[ 0.002] curl(1234/1234) read(4, 0x7f3a1c000b20, 4096) = 4096 <0.012ms>
[ 0.003] curl(1234/1234) socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) = 5 <0.008ms>
[ 0.004] curl(1234/1234) connect(5, 93.184.216.34:443) = 0 <42.187ms>
[ 0.046] curl(1234/1234) sendto(5, 0x55a3bc001b40, 78, MSG_NOSIGNAL) = 78 <0.011ms>
あるいは --raw なしで実行すると、フルスクリーンのTUIが表示されます。
snoop pid:1234 comm:curl events:142 elapsed:0.341s
┌── syscall stream ─────────────────────────────────────┐┌── top syscalls ─────────┐
│ [ 0.001] curl openat("/etc/ssl/…") = 4 <0.031ms> ││ syscall count pct│
│ [ 0.002] curl read(4, …) = 4096 ││ read 38 26.8%│
│ [ 0.003] curl socket(AF_INET, …) = 5 ││ write 21 14.8%│
│ [ 0.004] curl connect(5, 93.184.216.34:443) = 0 ││ openat 18 12.7%│
│ [ 0.046] curl sendto(5, …) = 78 ││ mmap 14 9.9%│
└───────────────────────────────────────────────────────┘└─────────────────────────┘
[q]uit [Space]pause [/]search [f]iles [n]et [c]lear [↑↓]scroll [G]bottom
straceはptraceを使用するため、システムコールごとにプロセスを停止します。snoopは代わりにeBPFトレースポイントを使用するため、プロセスはフルスピードで動作し続け、トレースはカーネル内で行われます。パフォーマンスに加えて、snoopは引数を実際に読める形にデコードし、リアルタイムTUIを備え、トレースの保存・再生・差分表示が可能です。
--raw(strace互換)、--json(JSON Lines、jq用)、--explain(システムコールを高レベルなアクティビティサマリーにグループ化)--files、--net、--slow 10、--syscall openat-p PID)または起動(snoop <cmd>)--follow でフォークされた子プロセスもトレース--docker <name> と --pod <name> でコンテナ内のすべてをトレースカーネルモジュールもCツールチェーンも不要。eBPFプログラムは純粋なRust(aya)で書かれ、バイナリに埋め込まれています。
CAP_BPF + CAP_PERFMONcurl -L https://github.com/pandaadir05/snoop/releases/latest/download/snoop-x86_64-linux.tar.gz \
| tar -xz
sudo install -m755 snoop /usr/local/bin/snoop
aarch64の場合は、x86_64 を aarch64 に置き換えてください。
cargo install --git https://github.com/pandaadir05/snoop snoop
これによりeBPFプログラムが自動的にビルドされます。Cツールチェーンやカーネルヘッダーは不要で、Rustのみ必要です。
# コマンドをトレース
sudo snoop ls /etc
sudo snoop -- nginx -g 'daemon off;'
# 実行中のプロセスにアタッチ
sudo snoop -p $(pidof postgres)
sudo snoop -p 1234 --follow # フォークされた子プロセスもトレース
# コンテナ
sudo snoop --docker my-nginx
sudo snoop --pod my-app-pod --namespace production
# フィルタリング
sudo snoop -p 1234 --files # ファイルシステムコールのみ
sudo snoop -p 1234 --net # ネットワークコールのみ
sudo snoop -p 1234 --slow 5 # 5msより遅いコールのみ
sudo snoop -p 1234 --syscall openat --syscall read
--explain は生のシステムコールを個別に表示する代わりに、高レベルのサマリーにグループ化します。
sudo snoop -p 1234 --explain
READ /etc/passwd ↓1.2 KB (2 calls, 0.80ms)
NET 127.0.0.1:5432 ↑512 B ↓4.0 KB (18.20ms)
EXEC /usr/bin/python3
OpenSSLの SSL_write/SSL_read をフックして平文をキャプチャします。
sudo snoop -p 1234 --tls
sudo snoop -p 1234 --ltrace
トレースを記録し、後でrootなしで再生します。
sudo snoop record -p 1234 -o trace.snoop
snoop view trace.snoop
snoop view trace.snoop --files --slow 5 --json | jq 'select(.name=="read")'
デプロイ後のリグレッション発見に便利です。
sudo snoop record -p 1234 -o before.snoop
# ... 変更をデプロイ ...
sudo snoop record -p 1234 -o after.snoop
snoop diff before.snoop after.snoop
SYSCALL COUNTS
read 1200 → 1800 (+50.0%) ▲
openat 340 → 210 (-38.2%) ▼
DURATION REGRESSIONS (median)
read 0.02ms → 0.08ms (+300%)
ONLY IN after
statx (4x)
sudo snoop -p 1234 --json | jq 'select(.name == "connect")'
sudo snoop -p 1234 --flamegraph syscalls.svg
-p, --pid <PID> Running process にアタッチ
--follow フォークされた子プロセスのトレース(--pid 必須)
--docker <NAME|ID> Docker コンテナ内の全プロセスをトレース
--pod <POD> Kubernetes ポッド内の全プロセスをトレース
-n, --namespace <NS> Kubernetes 名前空間(デフォルト: default)
--raw 1行の strace 互換出力
--json JSON Lines — システムコールごとに 1 オブジェクト
--explain セマンティックアクティビティサマリー
--files ファイルシステムコールのみ
--net ネットワークコールのみ
--slow <MILLIS> 閾値より遅いコールのみ
--syscall <NAME> このシステムコールのみ(繰り返し可)
--no-decode 生の16進数引数、デコードなし
--tls SSL_write/SSL_read uprobe 経由で TLS 平文をキャプチャ
--ltrace malloc/free/calloc/realloc のトレース
--flamegraph <PATH> 終了時に Flamegraph SVG を書き込み
--ebpf-obj <PATH> 埋め込み eBPF オブジェクトを上書き [$SNOOP_EBPF_OBJ]
2つのeBPFプログラムが raw_syscalls/sys_enter と sys_exit にアタッチします。エントリ時にシステムコール番号と引数がスレッドごとのスクラッチマップに格納されます。終了時に戻り値がエントリデータとペアにされ、リングバッファにプッシュされます。ユーザー空間は非同期fd経由でリングバッファを消費し、すべてをフィルター/デコードパイプラインに通します。
kernel userspace
────── ─────────
raw_syscalls/sys_enter ──► SYSCALL_ENTER map (per-tid scratch)
raw_syscalls/sys_exit ──► EVENTS ring buffer (4 MiB)
│
poll consumer (tokio::spawn)
│
┌──────────┴──────────┐
RawOutput TuiApp
(one line/syscall) (ratatui TUI)
eBPFプログラムは aya を使ってRustで記述され、ビルド時にBPFバイトコードにコンパイルされ、バイナリに埋め込まれます。ランタイム依存関係やカーネルヘッダーは不要です。
git clone https://github.com/pandaadir05/snoop
cd snoop
# Nightly は BPF ターゲットにのみ必要
rustup toolchain install nightly
rustup component add rust-src --toolchain nightly
# bpf-linker は eBPF オブジェクトをリンク(システムLLVM不要)
cargo install bpf-linker --no-default-features
cargo build --release
sudo ./target/release/snoop -p $$
ビルドスクリプトがeBPFプログラムを自動的にコンパイルします。別途手順は不要です。
開発中は xtask も使用できます。
cargo xtask run -- -p $$
MIT または Apache-2.0、お好きな方を選択してください。
--tlsSSL_writeSSL_read--ltrace で malloc/free/calloc/realloc を追跡snoop record でディスクに保存、snoop view でオフライン再生(root不要)snoop diff before.snoop after.snoop で2回の実行間の変更を表示--flamegraph out.svg| キー | 動作 |
|---|
q | 終了 |
Space | 一時停止 / 再開 |
/ | インクリメンタル検索 |
f | ファイルシステムフィルターの切り替え |
n | ネットワークフィルターの切り替え |
c | イベントリストのクリア |
Enter | 選択したイベントの詳細ポップアップ表示 |
↑ / k | 上にスクロール |
↓ / j | 下にスクロール |
G / End | 最新イベントにジャンプ |
g / Home | 最古イベントにジャンプ |
| 層 | クレート |
|---|
| eBPFプログラム | aya-ebpf |
| eBPFローダー | aya |
| TUI | ratatui + crossterm |
| CLI | clap (derive) |
| 非同期ランタイム | tokio |
| フレームグラフ | inferno |