Linux 성능 병목을 추적하는 perf·vmstat·iostat·strace·ltrace

perf, vmstat, iostat, strace, ltrace로 CPU·메모리·디스크 I/O·시스템 콜 병목을 진단하는 Linux 성능 분석 방법

2026-08-14 · 최초 발행 2026-01-19

병목이 보이지 않을 때 먼저 나눠 볼 계층

성능 저하는 CPU, 메모리, 디스크, 네트워크처럼 서로 다른 계층에서 생긴다. 한 지표만 보고 원인을 단정하면 엉뚱한 곳을 최적화하기 쉽다. Linux의 perf, vmstat, iostat, strace, ltrace는 각각 다른 관측 지점을 제공하며, 함께 사용하면 시스템 상태부터 코드와 호출 경로까지 추적할 수 있다.

perf로 CPU 이벤트와 실행 경로 읽기

perf는 perf_event_open() 시스템 콜을 통해 커널 perf 코어와 연결되고, PMU·소프트웨어 이벤트·트레이스포인트에서 데이터를 수집한다.

perf User Space Toolperf_event_open()System CallKernel perf CorePMU(Performance MonitoringUnit)Software EventsTracepointsCPU CyclesCache MissesBranch MispredictionsContext SwitchesPage FaultsScheduler EventsSystem Calls

perf stat은 기본 성능 통계를 모으고, perf record는 샘플을 저장한다. 저장한 결과는 perf report로 분석할 수 있다. 실시간 상태는 perf top, 시스템 콜 추적은 perf trace, 어셈블리 수준 확인은 perf annotate가 담당한다.

실행 단위의 통계 수집

# 기본 통계 수집
perf stat ./my_program

# 상세 통계 (더 많은 이벤트)
perf stat -d ./my_program

# 특정 이벤트만 측정
perf stat -e cycles,instructions,cache-misses,branch-misses ./my_program

# 실행 중인 프로세스 모니터링 (10초간)
perf stat -p <PID> -d sleep 10

# CPU별 통계
perf stat -a -A sleep 5

샘플을 남기고 호출 그래프 확인하기

# CPU 샘플링 (기본 주파수: 1000Hz)
perf record -g ./my_program

# 높은 주파수로 샘플링
perf record -F 4000 -g ./my_program

# 특정 CPU에서만 샘플링
perf record -C 0,1 -g ./my_program

# 커널 함수 포함
perf record -g --all-kernel ./my_program

# 분석 결과 출력
perf report --stdio
perf report -g graph,0.5,caller

# Call-graph 생성 (flame graph)
perf script | stackcollapse-perf.pl | flamegraph.pl > flame.svg

PMU 이벤트를 직접 지정하면 캐시, 분기 예측, TLB처럼 관심 있는 하드웨어 동작을 좁혀 볼 수 있다.

# 사용 가능한 이벤트 목록
perf list

# 캐시 관련 상세 분석
perf stat -e L1-dcache-loads,L1-dcache-load-misses,LLC-loads,LLC-load-misses ./my_program

# 분기 예측 분석
perf stat -e branches,branch-misses ./my_program

# TLB 미스 분석
perf stat -e dTLB-loads,dTLB-load-misses,iTLB-loads,iTLB-load-misses ./my_program

vmstat에서 CPU 대기와 메모리 압박 가르기

vmstat은 프로세스 상태, 메모리, 스왑, I/O, 시스템, CPU 지표를 한 화면에서 확인하게 해 준다. 전반적인 상태를 먼저 훑을 때 유용하다.

# 기본 사용법 (1초 간격, 10회)
vmstat 1 10

# 출력 예시:
# procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
#  r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
#  2  0      0 2048576 204800 4096000  0    0     5    10  500 1000 25 10 60  5  0
vmstat OutputProcess(r: running, b: blocked)Memory(swpd, free, buff, cache)Swap(si: swap in, so: swap out)I/O(bi: blocks in, bo: blocksout)System(in: interrupts, cs: contextswitches)CPU(us, sy, id, wa, st)

r 값이 높으면 CPU 경합과 더 많은 CPU 코어의 필요성을 검토할 수 있다. b 값이 높다면 I/O 대기와 디스크 성능을 살핀다. si/so > 0은 스왑 발생과 메모리 부족 상태를 뜻하며, wa 증가는 I/O 대기와 디스크 병목의 신호다. cs가 높을 때는 과도한 컨텍스트 스위칭과 스레드 수를 점검한다.

# 메모리 상세 정보
vmstat -s

# 디스크 통계
vmstat -d

# 파티션별 통계
vmstat -p /dev/sda1

# 슬랩 캐시 정보
vmstat -m

# 이벤트 카운터 및 메모리 통계
vmstat -a 1 5

iostat으로 디스크 대기 시간을 확인하기

디스크 I/O가 의심되면 iostat의 확장 통계로 디바이스별 요청량, 대기 시간, 사용률을 확인한다.

# 기본 출력
iostat

# 확장 통계 (1초 간격, 5회)
iostat -x 1 5

# 특정 디바이스만
iostat -x sda 1

# MB 단위 출력
iostat -x -m 1 5

# CPU와 디스크 통계 모두 출력
iostat -xc 1 5
# 출력 예시 해석:
# Device    r/s   w/s   rkB/s   wkB/s  rrqm/s  wrqm/s  %util  await  svctm
# sda     100.0  50.0  4096.0  2048.0     5.0    10.0   85.0   12.5    8.5

r/s, w/s는 초당 읽기와 쓰기 요청 수다. rkB/s, wkB/s는 초당 읽기와 쓰기 데이터량(KB)을 나타낸다. await는 평균 I/O 대기 시간(ms), svctm은 평균 서비스 시간(ms)이며, %util은 디바이스 사용률이다. %util이 100%에 가까우면 포화 상태를 의심한다.

YesNoYesNoYesNoHigh %util?Check awaitI/O is not bottleneckawait 20ms?Disk is slow(HDD), consider SSDCheck rrqm/s, wrqm/sLow merge rate?Random I/O patternConsider increasing queuedepthSequential I/OCheck disk throughput
# 파티션별 통계
iostat -p sda 1

# NFS 통계 포함
iostat -n 1

# 디바이스 그룹별 출력
iostat -g total sda sdb sdc 1

# JSON 형식 출력 (스크립트 처리용)
iostat -o JSON 1 5

strace로 시스템 콜 비용을 추적하기

strace는 프로그램의 시스템 콜을 추적한다. 실행 중인 프로세스에 연결하거나, 호출 유형을 필터링하고, 통계 및 타임스탬프를 기록할 수 있다.

# 프로그램 실행 추적
strace ./my_program

# 실행 중인 프로세스에 연결
strace -p <PID>

# 시스템 콜 통계
strace -c ./my_program

# 타임스탬프 포함
strace -t ./my_program
strace -tt ./my_program  # 마이크로초 단위
strace -ttt ./my_program  # epoch 시간

# 출력을 파일로 저장
strace -o trace.log ./my_program

파일, 네트워크, 시그널처럼 관심 있는 시스템 콜 영역만 남기면 추적 결과를 줄일 수 있다.

# 파일 관련 시스템 콜만
strace -e trace=file ./my_program

# 네트워크 관련 시스템 콜만
strace -e trace=network ./my_program

# open 계열 시스템 콜만
strace -e trace=open,openat ./my_program

# read/write 추적
strace -e trace=read,write -e read=0,1,2 -e write=0,1,2 ./my_program

# 시그널 추적
strace -e trace=signal ./my_program

프로세스가 자식 프로세스나 여러 스레드를 만들면 추적 범위도 함께 넓혀야 한다.

# 자식 프로세스 추적
strace -f ./my_program

# 스레드 ID 출력
strace -f -ff -o trace ./my_program

# 각 스레드의 출력을 별도 파일로 (trace.PID 형식)
strace -ff -o trace ./my_program

호출 횟수와 시간으로 정렬하거나, 지연된 호출만 걸러 파일 접근 패턴을 확인할 수 있다.

# 시스템 콜별 시간 측정
strace -c -S calls ./my_program

# 느린 시스템 콜 찾기 (100ms 이상만)
strace -T -e trace=all -w ./my_program 2>&1 | awk '/<[0-9]+\.[1-9]/{print}'

# 파일 접근 패턴 분석
strace -e trace=open,read,write,close -o file_access.log ./my_program
grep -E 'open|read|write|close' file_access.log

ltrace로 라이브러리 호출을 따라가기

ltrace는 라이브러리 함수 호출을 관찰한다. 시스템 콜보다 상위의 호출 경로를 확인하거나, 특정 라이브러리와 함수에 관심을 둘 때 사용할 수 있다.

# 라이브러리 함수 호출 추적
ltrace ./my_program

# 실행 중인 프로세스에 연결
ltrace -p <PID>

# 함수 호출 통계
ltrace -c ./my_program

# 시스템 콜도 함께 추적
ltrace -S ./my_program

# 출력을 파일로 저장
ltrace -o ltrace.log ./my_program
# 특정 라이브러리만 추적
ltrace -l libpthread.so.0 ./my_program

# 특정 함수만 추적
ltrace -e malloc+free ./my_program
ltrace -e 'malloc*' ./my_program  # malloc, malloc_usable_size 등

# 함수 인자 상세 출력
ltrace -A 100 ./my_program  # 배열 인자 100개까지 출력

메모리 할당과 해제 호출을 추적하면 누수 여부를 조사하는 단서가 된다.

# malloc/free 추적
ltrace -e malloc+free+realloc ./my_program

# 통계로 확인
ltrace -c -e malloc+free ./my_program

# 각 호출의 스택 트레이스 (GDB와 함께)
ltrace -w 3 -e malloc ./my_program

관측 범위를 좁혀 가는 진단 흐름

전반적인 상태를 vmstat으로 확인한 뒤 CPU, I/O, 메모리 부족 여부에 따라 다음 도구를 고른다. CPU 병목은 perf top 또는 perf record로 핫스팟을 찾고, 이후 perf annotate로 코드 최적화 대상을 확인한다. I/O 병목은 iostat과 I/O 패턴·캐싱 개선으로 이어지며, 메모리 부족은 vmstat -s, free -h, 메모리 누수 확인으로 이어진다. 어느 쪽에도 명확히 속하지 않으면 straceltrace로 호출을 추적한다.

YesNoYesNoYesNo(1) 성능 문제 인지(2) vmstat으로 전반적 상태확인CPU 병목?perf top/record로 핫스팟분석I/O 병목?iostat으로 디스크 분석메모리 부족?vmstat -s, free -h 확인strace로 시스템 분석perf annotate로 코드 최적화I/O 패턴 개선, 캐싱메모리 증설, 메모리 누수 확인ltrace로 라이브러리 호출 분석

다음 스크립트는 시스템 상태, CPU 프로파일, I/O 통계, 메모리 정보, 상위 프로세스를 한 번에 수집하고 perf 보고서를 생성한다.

#!/bin/bash
# 시스템 성능 종합 분석 스크립트

OUTPUT_DIR="perf_analysis_$(date +%Y%m%d_%H%M%S)"
mkdir -p "$OUTPUT_DIR"

echo "Starting comprehensive performance analysis..."

# 1. 시스템 전반적 상태
echo "(1) Collecting system overview..."
vmstat 1 60 > "$OUTPUT_DIR/vmstat.log" &
VMSTAT_PID=$!

# 2. CPU 프로파일링
echo "(2) CPU profiling with perf..."
perf record -g -a -o "$OUTPUT_DIR/perf.data" sleep 30 &
PERF_PID=$!

# 3. I/O 통계
echo "(3) Disk I/O statistics..."
iostat -x 1 60 > "$OUTPUT_DIR/iostat.log" &
IOSTAT_PID=$!

# 4. 메모리 정보
echo "(4) Memory information..."
cat /proc/meminfo > "$OUTPUT_DIR/meminfo.log"
vmstat -s > "$OUTPUT_DIR/vmstat_summary.log"

# 5. 프로세스별 리소스 사용
echo "(5) Top processes..."
top -b -n 10 -d 1 > "$OUTPUT_DIR/top.log" &
TOP_PID=$!

# 대기
wait $VMSTAT_PID $PERF_PID $IOSTAT_PID $TOP_PID

# perf 리포트 생성
perf report -i "$OUTPUT_DIR/perf.data" --stdio > "$OUTPUT_DIR/perf_report.txt"

echo "Analysis complete. Results in $OUTPUT_DIR/"

웹 서버와 데이터베이스에서의 추적 예시

웹 서버의 지연을 조사할 때는 먼저 시스템 전반의 CPU와 I/O 상태를 확인하고, CPU 핫스팟과 시스템 콜 경합을 차례로 추적할 수 있다.

# 1. 전반적 시스템 상태 확인
vmstat 1 10
# 관찰: CPU us/sy 높음, I/O 대기 낮음 → CPU 병목

# 2. CPU 핫스팟 분석
perf top -p $(pgrep nginx)
# 발견: SSL 핸드셰이크가 CPU의 40% 사용

# 3. 시스템 콜 분석
strace -c -p $(pgrep nginx) -f
# 발견: futex 시스템 콜이 과도하게 발생 → 잠금 경합

# 해결: 워커 프로세스 수 증가, SSL 세션 캐싱 활성화

데이터베이스 쿼리 지연에서는 디스크 대기, 파일 관련 시스템 콜, 캐시 미스를 이어서 살필 수 있다.

# 1. I/O 패턴 확인
iostat -x 1 10
# 관찰: await 높음, r/s 많음 → 랜덤 읽기 과다

# 2. 시스템 콜 추적
strace -p $(pgrep postgres) -e trace=file -c
# 발견: pread64가 대부분의 시간 소비

# 3. 캐시 미스 분석
perf stat -e LLC-loads,LLC-load-misses -p $(pgrep postgres) sleep 10
# 발견: LLC 미스율 25% → 메모리 캐시 부족

# 해결: 데이터베이스 버퍼 풀 증가, 인덱스 최적화

perf는 CPU 수준의 상세 프로파일링을, vmstat과 iostat은 시스템 자원 상태를, strace와 ltrace는 애플리케이션의 호출 동작을 보여 준다. 이 도구들을 함께 사용하면 복잡한 성능 문제에서도 병목을 분리하고 최적화 방향을 정할 수 있으며, 지속적인 모니터링으로 사전 대응의 근거를 마련할 수 있다.

Linux성능 분석perf시스템 모니터링프로파일링