지금 이 프로세스, 파일과 네트워크에 뭘 하고 있을까요.
macOS와 Linux에서 프로세스의 파일·네트워크 I/O를 추적합니다. 어디에 얼마나 읽고 쓰는지, 얼마나 걸리는지 실시간으로 확인하세요.
iotap은 macOS와 Linux에서 쓰는 명령줄 도구입니다. 프로세스 ID나 이름을 넘기면 그 프로세스가 읽고 쓰는 모든 순간을 실시간으로 보여줍니다. 어느 파일이나 원격 주소인지, 몇 바이트를 요청해서 몇 바이트가 실제로 오갔는지, 호출에 얼마나 걸렸는지, 실패했는지까지. 추적을 멈추면 파일별, 엔드포인트별로 합계를 내줍니다.
위에서 아래로 읽으면 curl 한 번의 처음부터 끝까지가 그대로 보입니다:
- 93.184.216.34의 443 포트로 517바이트짜리 요청을 보냅니다.
- 16 KiB를 요청하고 4 KiB를 받습니다.
- 다시 요청하지만 아직 도착한 게 없습니다:
EAGAIN. - 그 4 KiB를
page.html에 씁니다. - 진행 표시 20바이트를 터미널에 쓰고 종료합니다.
익숙한 도구들은 저마다 한 조각씩만 보여줍니다
컴퓨터가 뭔가로 바쁜데, 뭐 때문인지 알고 싶을 때가 있습니다. 어떤 프로세스가 디스크를 긁어대거나, 방금 설치한 앱이 시킨 적 없는 어딘가와 통신하거나, 빌드가 느린데 아무래도 I/O 탓 같거나, 프로그램이 자꾸 죽는데 어느 파일이나 연결에서 넘어지는지 보고 싶을 때요.
iotap은 그 조각을 한데 모읍니다. 오간 바이트, 그 바이트가 속한 프로세스, 그리고 목적지였던 파일이나 엔드포인트까지.
| 도구 | 보여주는 것 | 놓치는 것 |
|---|---|---|
| lsof, netstat | 지금 무엇이 열려 있는지 | 그 안으로 무엇이 오가는지 |
| 활성 상태 보기, top | 몇 바이트인지 | 어디로 갔는지 |
| 패킷 캡처 | 컴퓨터 전체의 트래픽 | 어느 프로세스의 것인지 |
| strace, fs_usage | 프로세스가 하는 호출 하나하나 | 파일별, 엔드포인트별 합계 |
| iotap | 지정한 프로세스의 읽기와 쓰기 하나하나, 바이트 수, 지연 시간, 대상까지, 그리고 파일별·엔드포인트별 합계 | 메모리 맵 I/O, 그리고 Linux에서는 io_uring과 libaio |
무엇을 지켜볼지 고르세요
대상은 프로세스 ID나 이름입니다. 이름을 주면 프로세스 이름, 실행 파일 이름, 첫 번째 인자 중 하나가 대소문자를 가리지 않고 같은 프로세스를 모두 잡고, 나중에 그 이름으로 뜨는 프로세스도 이어서 추적합니다.
-f를 붙이면 추적 중인 프로세스가 띄우는 프로세스까지 따라갑니다. 시작할 때 이미 떠 있던 것도, 나중에 뜨는 것도요.
sudo iotap 1234 # 프로세스 하나
sudo iotap Safari # 모든 Safari 프로세스, 새로 뜨는 것까지
sudo iotap -f make # make와 make가 시작하는 모든 프로세스
sudo iotap -i wlan0 firefox # wlan0을 지나는 네트워크 I/O만
sudo iotap -q -d 10 Finder # 10초 뒤 요약만
추적 하나, 읽는 방법은 넷
이벤트 스트림은 위에서 본 그대로입니다. 추적이 끝나면 iotap이 합계를 냅니다. 오래 도는 프로세스라면 터미널 UI로 같은 표를 실시간으로 볼 수 있고, JSON Lines로 뽑아 jq에 넘기거나 로그로 남길 수도 있습니다. 레코딩해 두면 나중에 root 없이도 이 중 어떤 형태로든 다시 볼 수 있습니다.
요약
Ctrl-C를 누르거나 프로세스가 끝나면 iotap이 파일별, 엔드포인트별로 합계를 냅니다. 이 예에서 네트워크 트래픽은 192.168.1.20 주소를 가진 인터페이스 en0을 거쳤습니다.
터미널 UI
--tui는 직전 1초의 처리량과 누적 처리량을 맨 위에 보여주고, 그 아래에 파일, 네트워크 엔드포인트, 최근 이벤트 10,000개의 세 탭을 둡니다. 아래는 다운로드 레코딩을 재생한 뒤의 Files 탭입니다.
- 1 2 3 탭 전환
- s 바이트, 읽기, 쓰기, 호출 수, 최근 순으로 정렬
- Enter 선택한 행의 상세 정보
- p 화면만 멈춤, 추적은 계속
- y 선택한 경로나 주소 복사
- i 네트워크 인터페이스별 트래픽
- q 종료하고 요약 출력
JSON Lines
--json은 이벤트 하나를 한 줄씩 씁니다. 시각은 Unix epoch 기준 나노초, 대상은 작은 객체입니다. jq로 파이프하거나 그대로 로그로 남기면 됩니다. 아래는 이벤트 하나를 여러 줄로 펼친 모습입니다.
sudo iotap --json curl | jq -c 'select(.type == "event")'
레코딩
--record FILE은 추적을 파일로 저장하고, --replay FILE은 그 파일을 같은 처리 과정에 다시 넣습니다. 그래서 어떤 출력 모드로 재생하든 실시간으로 실행했을 때와 똑같은 출력이 나옵니다. 레코딩에는 경로와 주소만 담기고 전송된 데이터는 담기지 않으며, 레코딩한 운영체제에서만 재생됩니다.
sudo iotap --net-only --record t.iotaprec 1234
iotap --replay t.iotaprec # 같은 출력을 다시, root 없이
커널이 이미 기록하는 것을 읽을 뿐입니다
프로그램이 하는 read, write, open, connect는 전부 시스템 콜이고, 커널은 요청하면 그 하나하나를 일어나는 대로 기록해 줍니다. 어느 프로세스가 호출했는지, 인자와 결과가 무엇인지까지요. iotap은 그 기록을 읽습니다.
디버거를 붙이지도, 코드를 주입하지도, 지켜보는 프로그램을 건드리지도 않습니다.
- macOS에서는
- 그 기록이 커널 트레이스 기능인 kdebug입니다. Apple의
fs_usage가 읽는 바로 그 출처로, 커널에 내장되어 있어 따로 설치할 것이 없습니다. - Linux에서는
- iotap이 직접 만든 작은 eBPF 프로그램을 커널의 시스템 콜 트레이스포인트에 붙입니다. 커널이 실행 전에 검증하고, iotap이 끝나면 떼어냅니다.
- sudo가 필요한 이유
- 둘 다 컴퓨터의 모든 프로세스를 들여다보는 통로라서 커널은 root에게만 열어 줍니다. root가 아니면 iotap은 그렇다고 알리고 아무것도 건드리기 전에 멈춥니다. 레코딩 재생에는 root가 필요 없습니다.
- 메타데이터만
- iotap이 기록하는 것은 호출, 디스크립터, 바이트 수, 지연 시간, 경로, 소켓 엔드포인트뿐입니다. 오가는 데이터 자체는 읽지도 저장하지도 않습니다. 어떤 프로세스가 SSH 키를 읽으면 그 파일을 몇 바이트 읽었는지만 보이지, 내용은 절대 보이지 않습니다.
- 지정한 것만
- 커널에 기록을 요청하는 건 지정한 프로세스, 그리고
-f를 붙였다면 그 프로세스가 띄운 프로세스의 호출뿐입니다. 다른 프로세스는 건드리지 않습니다. - 아무것도 남기지 않음
- macOS에서 kdebug는 한 번에 한 프로그램만 쓸 수 있어서, iotap은 오류, 시그널, 패닉을 포함해 어떻게 끝나든 반드시 반납합니다. Linux에서는 iotap이 끝나는 순간 커널이 eBPF 프로그램을 떼어냅니다. 터미널 UI도 마찬가지로 어떤 경우든 터미널을 원래대로 돌려놓습니다.
- 바이너리 하나, 데몬 없음
- iotap은 실행하는 동안만 돌고, 스스로 네트워크 연결을 만들지 않습니다. 옵션인 호스트 이름 조회(
--resolve)도 시스템 리졸버를 거칩니다. - 보지 못하는 것
- 메모리 맵 파일 I/O, 그리고 페이지 캐시 writeback처럼 커널이 프로세스 대신 하는 I/O는 보지 못합니다. Linux에서는 io_uring이나 libaio로 넣은 I/O도 마찬가지입니다. 나머지는 README의 Limitations 절에 있습니다.
Homebrew로 설치하고, 바로 추적하세요
Apple Silicon Mac과 ARM64·x86-64 Linux에서는 Homebrew로 빌드된 바이너리를 설치할 수 있습니다. Rust를 따로 설치할 필요는 없습니다.
Homebrew가 없다면 먼저 설치하세요. 실행에 필요한 라이브러리는 Homebrew가 관리하며, Homebrew 자체의 OS 요구 사항도 적용됩니다.
추적할 때는 sudo가 필요합니다. Linux는 BPF와 시스템 콜 트레이스포인트를 지원하는 커널 5.8 이상이어야 합니다. 저장된 기록을 재생할 때는 root 권한이 필요 없습니다.
iotap을 설치합니다:
brew install rath/tap/iotap
설치된 버전을 확인합니다:
iotap --version
실행 중인 프로세스를 추적합니다. 1234를 실제 PID로 바꿔 주세요:
sudo iotap --tui 1234
릴리스 바이너리를 직접 받아서 SHA256SUMS로 검증할 수도 있습니다. Linux 다운로드에는 glibc 2.28 이상, libelf, zlib가 필요합니다. 필요한 패키지를 확인하세요.
소스에서 빌드하기 — Intel Mac 포함
소스 빌드는 macOS와 64비트 Arm·x86-64 Linux를 지원합니다.
빌드에는 Rust 1.98.1이 필요하고, rust-toolchain.toml이 그 버전을 골라 줍니다. macOS에서는 Xcode Command Line Tools가, Linux에서는 clang, make, pkg-config, C 컴파일러, libelf와 zlib 개발 패키지가 추가로 필요합니다.
Debian이나 Ubuntu라면 먼저 빌드에 필요한 패키지를 설치합니다:
sudo apt install build-essential clang pkg-config \
libelf-dev zlib1g-dev
그다음 빌드하고 실행 중인 프로세스를 추적합니다:
git clone https://github.com/rath/iotap.git
cd iotap
cargo build --release --locked
sudo ./target/release/iotap 1234