AetherEchoesEngineering
Engineering#020311 min2,2499 view

witr で「なぜこのプロセスが動いているのか」を辿る — 起点を運用者目線で追う

Go 製 CLI/TUI の witr は、プロセス・ポート・コンテナ・ファイルを起動した親まで一発で辿れます。占有ポートの犯人特定から warnings・exit code・TUI・JSON 出力まで、運用者目線で読みます。

SoSoraEndo2026年8月10日 09:0711 min2,249

動画で読む

witr は「なぜこのプロセスが動いているのか」を系統樹で答える

witr(Go 製の CLI/TUI ツール)は、プロセス・ポート・コンテナ・ファイルを入力に取り、それを起動した親までの連鎖を一発で表示します。答える問いは「何が動いているか」ではなく「なぜ動いているか」です。

運用の現場で厄介なのは、pslsof で「動いている実体」までは分かるのに、「誰がそれを起こしたのか」がすぐ出てこないことです。PID は分かった、でもその親は、さらにその親は。手で ps -o ppid= -p <pid> を何度も叩いて init まで遡った経験は、たぶん運用に関わる人なら一度はあります。witr はこの遡りを最初から目的に据えていて、systemd (pid 1) → PM2 v5.3.1: God (pid 1481580) → python (pid 1482060) のような因果の鎖を一行で見せてくれます。

作者は pranshuparmar 氏で、ライセンスは Apache-2.0。Linux では /proc を、macOS では ps / lsof / sysctl を、Windows では Win32 API を、FreeBSD では procstat を使い、プラットフォームごとにネイティブな手段で系統樹を組み立てます。PowerShell や WMI に依存しない設計なので、Windows でも余計な前提が要りません。

占有ポートの犯人を、親まで一撃で辿る

一番刺さる使い方は、占有ポートの原因特定です。witr --port 5432 --short と打つと、そのポートを LISTEN しているプロセスから init までの鎖が一行で出ます。

Address already in use は運用で最も出会う定番のエラーです。従来なら lsof -i :5432 で PID を出し、その PID を ps に食わせて親を辿る、という二段構えでした。witr はこの二段を畳んでいて、ポート番号から一気に「supervisor(PM2 なのか systemd なのか)まで含めた出自」を返します。私が最初にありがたいと思ったのはここでした。ポートを塞いでいたのが「昨日デバッグで上げっぱなしにした python」なのか「systemd が監視して再起動し続けているサービス」なのかで、対処がまったく変わるからです。

# ポート 5432 を握っている犯人を、親まで一行で
witr --port 5432 --short
# systemd (pid 1) → postgresql@16-main (pid 812) → postgres (pid 851)

# 木構造で子プロセスまで見たいとき
witr --pid 143895 --tree

--short は系統だけ、--tree は子まで含めた木を出す、という出し分けです。ポート・PID・名前を混ぜて witr nginx --port 5432 --pid 1234 のように複数ターゲットを一度に渡すこともでき、それぞれ区切り線付きで結果が並びます。

プロセス・ファイル・コンテナを、同じ問いに畳み込む

witr の設計上の芯は、「すべてをプロセスの問いに変換する」ことです。ポートもファイルもコンテナも、いったん PID に写像してから同じ系統樹エンジンに通します。

入力の受け口は繰り返し可能なフラグで統一されています。--pid は PID、--port は LISTEN ポート、--file は開かれているファイル、--container は Docker / Podman / nerdctl / K8s(crictl) / Incus / LXC / LXD / FreeBSD jail を横断して受けます。たとえば witr --file /var/lib/dpkg/lock と打てば、その lock ファイルを握っているのが誰かを起点から辿れます。apt がハングしたときの「誰が dpkg のロックを離さないのか」を、prefix なしで直接問える形です。

コンテナ横断が一つのインターフェースにまとまっているのは実務で効きます。ランタイムごとに docker inspectpodman inspectcrictl を使い分ける代わりに、witr --container redis で出自を問える。ランタイム非依存という設計判断が、そのまま運用者の記憶容量の節約になっています。ローカルで破壊的操作を止める dcg を実運用で評価した話 と同じで、「一つの語彙で複数の現実を覆う」道具は運用の認知コストを確実に下げます。

warnings と exit code で、異常を機械的に拾う

witr は人が読む narrative だけでなく、機械が判定できる信号も返します。終了コードと warnings セクションがそれで、監視やスクリプトに組み込めます。

終了コードは 6 段階に分かれていて、0 はクリーン、1 は warnings あり、2 は not found、3 は権限拒否、4 は入力が曖昧、5 は内部エラーです。これがあると、シェルスクリプトで「見つからなかった」と「権限が足りない」を分岐できます。witr --port 8080 >/dev/null; echo $? の戻り値だけで、ポートが空いているのか、それとも root 権限が要るのかを判別できるわけです。

warnings セクションはさらに運用寄りで、root 権限で動いている、危険な capability を持っている、再起動を繰り返している、メモリが高い、プロセスが妙に古い、ライブラリがインジェクトされている、といった「ブロックはしないが気に留めるべき兆候」を拾って出します。--warnings を付ければ warnings だけを表示できます。best-effort detection を掲げていて、確信が持てない箇所は「確信が持てない」と明示する姿勢なので、出力を鵜呑みにして事故る危険が少ない。ここは好感が持てる設計です。

TUI と JSON — 人が見る画面と、機械が食う出力

出力は二方向に分かれています。人が探索するための TUI(-i または引数なしで起動)と、機械が処理するための --json です。同じエンジンの結果を、用途に応じて別の皮で出します。

TUI には Processes / Ports / Containers / Locks の 4 タブがあり、プロセス一覧は並べ替えとフィルタができ、右側に系統樹パネルが出ます。ポートタブは LISTEN ポートと所有プロセス、コンテナタブは全ランタイム統合、ロックタブは POSIX/FLOCK のファイルロックです。a キーで「全 open file 表示」に切り替えたり、/ で検索したりと、TUI ファイルマネージャに近い操作感で、superfile に学ぶ TUI 設計の話 で見たキーバインドや状態管理の作法がここでも生きています。Kill / Terminate / Pause / Resume のシグナル操作までタブ内から打てます。

一方 --json は CI や監視に組み込む側の口です。narrative をパースする必要がなく、決定的な順序で構造化データが返るので、「このポートの supervisor が systemd 以外になったら通知」のような自動判定が書けます。同じ問いに対して、人には物語を、機械には構造を返す。この二枚看板は、道具としての寿命を長くする作りだと思います。

導入と、私が実際に手元で試した所感

導入は驚くほど選択肢が多く、Homebrew / APT / Arch AUR / Winget / Nix / go install など主要どころはほぼ網羅しています。困るのは「入れ方が無いこと」ではなく「入れ方が多すぎて選ぶこと」くらいです。

私は 2026 年 8 月の金曜の夜に、手元の MacBook Air(M2)で go install github.com/pranshuparmar/witr/cmd/witr@latest を打って入れました。まず witr node で開発中の dev server を辿ってみたら、PID・ユーザ・コマンド・起動時刻に加えて「なぜ存在するか」の系統、supervisor、working dir、Git リポジトリ、ソケットまで一画面に収まって出ました。single-screen default という設計方針どおり、慌てている時でも一目で読める分量にまとまっているのが良かったです。

ただし macOS には制約があります。環境変数の取得は SIP(System Integrity Protection、システムの保護機構)で制限され、--env は空振りすることがある。一部のシステムプロセスは sudo を付けても詳細が見えません。Windows ではロックタブとプロセス操作が使えず、file locks の検出は macOS/FreeBSD では lsof 由来で限定的です。要するに「Linux が本命、他はできる範囲で」という正直な作りで、/proc を持つ Linux で最も真価を発揮します。それでも、占有ポートを親まで一行で辿れるだけで、私の手元の lsof | grep の回数は目に見えて減りました。運用の道具はだいたい、動かない時にだけ価値が分かります。witr は逆で、動いている理由を先に教えてくれる、珍しい方向の道具でした。

よくある質問

witr は ps や lsof と何が違うのですか?
ps や lsof が「今動いている実体」を見せるのに対し、witr は「なぜ動いているのか」を起点まで辿って系統樹で見せます。ポートや PID から init/systemd までの因果の鎖を一行で返す点が実務では大きな差になります。
占有ポートの原因はどう調べますか?
witr --port 5432 --short と打つと、そのポートを LISTEN しているプロセスから supervisor(PM2 や systemd)を含む親までの鎖が一行で出ます。lsof で PID を出して ps で親を辿る二段作業を一発に畳み込めます。
macOS でも使えますか?制約はありますか?
使えます。ただし環境変数の取得は SIP で制限され --env が空振りすることがあり、一部のシステムプロセスは sudo でも詳細が見えません。/proc を持つ Linux で最も真価を発揮します。
スクリプトや監視に組み込めますか?
組み込めます。exit code が 6 段階(0=クリーン / 1=warnings / 2=not found / 3=権限拒否 / 4=曖昧 / 5=内部エラー)に分かれ、--json で決定的な順序の構造化データが返るため、自動判定を書けます。

参考文献

  1. witr — pranshuparmar/witr (GitHub)
  2. proc(5) — Linux manual page (man7.org)

Reaction

Share

X (Twitter)