perf 서브시스템
리눅스 커널의 성능 프로파일링(Profiling) 프레임워크 perf를 PMU 하드웨어와 perf_event_open() 내부부터 다룹니다. 이 문서는 perf 도구 자체의 구조, 샘플링 모델, PEBS/IBS/SPE, c2c·mem·lock·sched 서브커맨드, TMA Top-Down, 커널 심볼(Kernel Symbol) 해석을 중심으로 설명하고, 전체 최적화 워크플로는 별도 문서로 분리합니다.
핵심 요약
perf 서브시스템을 이해하기 위한 6가지 핵심 개념입니다.
-
perf_event_open() 시스템 콜(System Call)이 핵심 진입점(Entry Point)
perf의 모든 기능은perf_event_open()시스템 콜을 통해 커널에 이벤트를 등록하는 것에서 시작합니다. 유저스페이스의 perf 도구(record, stat, top 등)는 모두 이 시스템 콜의 래퍼입니다. 반환된 파일 디스크립터(File Descriptor)로 이벤트를 제어하고 mmap으로 데이터를 수신합니다. -
PMU 하드웨어 카운터로 사이클/캐시(Cache)미스/분기예측 측정
CPU 내장 PMU(Performance Monitoring Unit)는 사이클, 명령어 수, 캐시 미스, 분기 예측(Branch Prediction) 실패 등을 하드웨어 수준에서 카운트합니다. Intel은 4-8개, ARM은 6-31개의 범용 카운터를 제공하며, 카운터 수를 초과하면 시분할 멀티플렉싱으로 보정합니다. -
샘플링(record) vs 카운팅(stat) 두 가지 모드
perf stat은 이벤트를 카운팅하여 총 수치를 제공합니다 (오버헤드(Overhead) 거의 0%).perf record는 주기적으로 샘플을 수집하여 어떤 함수/명령어에서 이벤트가 발생했는지 위치 정보를 제공합니다. stat으로 전체 현황을 파악하고, record로 핫스팟을 찾는 것이 일반적 워크플로입니다. -
Flamegraph로 콜스택 병목 시각화
perf record -g로 콜스택을 포함하여 수집한 뒤,perf script→stackcollapse-perf.pl→flamegraph.pl파이프라인(Pipeline)으로 SVG Flamegraph를 생성합니다. 넓은 프레임이 성능 병목이며, x축은 알파벳 순서(시간 아님), y축은 콜스택 깊이입니다. -
PEBS/IBS/SPE로 정밀 샘플링 (스키드 제거)
일반 PMI 인터럽트(Interrupt)는 이벤트 발생 후 수~수십 명령 뒤에 처리됩니다(스키드). Intel PEBS, AMD IBS, ARM SPE는 하드웨어가 직접 정확한 IP와 메모리 주소를 기록하여 스키드를 제거합니다.:pp수정자로 활성화합니다. -
BPF 연동으로 커널 내 필터링/집계
perf와 eBPF를 결합하면 커널 내에서 이벤트를 필터링하고 집계할 수 있습니다.perf lock contention --use-bpf처럼 BPF 프로그램이 커널에서 데이터를 처리하므로 유저스페이스 전송 오버헤드가 극적으로 줄어듭니다.
단계별 이해
perf를 처음 사용하는 사용자를 위한 6단계 실습 가이드입니다.
1단계: perf 설치 및 환경 확인
perf는 커널 소스에 포함된 도구이므로, 배포판에서 제공하는 패키지를 설치합니다. 권한 설정과 디버그 심볼도 확인합니다.
# 설치 (배포판별)
sudo apt install linux-tools-common linux-tools-$(uname -r) # Debian/Ubuntu
sudo dnf install perf # Fedora/RHEL
sudo pacman -S perf # Arch Linux
# 권한 확인 (paranoid 수준)
cat /proc/sys/kernel/perf_event_paranoid
# 2 이상이면 커널 이벤트 접근 불가 → -1로 변경 (개발 환경)
sudo sysctl -w kernel.perf_event_paranoid=-1
# 커널 디버그 심볼 설치 (perf report에서 커널 함수명 표시)
sudo apt install linux-image-$(uname -r)-dbgsym # Debian/Ubuntu
2단계: perf stat으로 첫 번째 성능 측정
가장 간단한 perf stat으로 프로그램의 전체 성능 지표를 측정합니다.
# 간단한 명령어로 시작
perf stat ls -la /tmp
# IPC(Instructions Per Cycle) 해석:
# IPC > 2.0: CPU 효율 좋음 (compute bound 최적화 효과 제한)
# IPC 1.0~2.0: 보통 (캐시/분기 개선 여지)
# IPC < 1.0: 메모리 바운드 의심 (캐시 미스, TLB 미스)
3단계: perf record + perf report로 핫스팟 찾기
# 콜스택 포함 프로파일링 (99Hz = 초당 99회 샘플)
perf record -F 99 -g -- ./my_program
# 결과 분석
perf report --stdio --sort sym
# → overhead가 높은 함수 = 핫스팟 (최적화 대상)
4단계: Flamegraph 생성으로 시각적 분석
# Flamegraph 도구 설치
git clone https://github.com/brendangregg/FlameGraph.git
# 파이프라인: record → script → collapse → flamegraph
perf record -F 99 -g -- ./my_program
perf script | FlameGraph/stackcollapse-perf.pl | \
FlameGraph/flamegraph.pl > profile.svg
# → 브라우저에서 profile.svg 열기
5단계: perf probe/trace로 특정 함수 추적
# 커널 함수 호출 추적
perf probe --add tcp_sendmsg
perf record -e probe:tcp_sendmsg -a -- sleep 5
perf script # 호출 시점, PID, 콜스택 확인
perf probe --del tcp_sendmsg
6단계: CI/CD 자동화 통합
# JSON 출력으로 자동화 파이프라인 구축
perf stat --json -e cycles,instructions -- ./benchmark 2> result.json
# → CI에서 baseline과 비교하여 성능 회귀 자동 감지
perf 입문 가이드
perf를 처음 실행할 때 자주 겪는 상황과 해결법을 안내합니다.
첫 perf 명령 실행
# 가장 간단한 perf 사용 (ls 명령의 성능 통계)
perf stat ls
# 출력 예:
# Performance counter stats for 'ls':
#
# 1.23 msec task-clock # 0.654 CPUs utilized
# 3 context-switches # 2.439 K/sec
# 0 cpu-migrations # 0.000 /sec
# 112 page-faults # 91.057 K/sec
# 3,248,521 cycles # 2.641 GHz
# 5,123,987 instructions # 1.58 insn per cycle
# 1,024,567 branches # 833.063 M/sec
# 42,567 branch-misses # 4.15% of all branches
출력 필드 해석
| 필드 | 의미 | 주의 사항 |
|---|---|---|
task-clock | 프로그램이 CPU에서 실행된 시간 | wall time과의 비율로 CPU 활용률 확인 |
context-switches | 컨텍스트 스위치 횟수 | 높으면 스케줄링 오버헤드 의심 |
cpu-migrations | CPU 간 이동 횟수 | 높으면 캐시 효율 저하, taskset 고려 |
page-faults | 페이지 폴트(Page Fault) 횟수 | minor(정상) vs major(디스크 I/O) 구분 필요 |
cycles | CPU 사이클 수 | 주파수 스케일링(Frequency Scaling) 영향 받음, GHz로 환산 표시 |
instructions | 실행된 명령어 수 | IPC = instructions / cycles |
branches | 분기 명령어 수 | 전체 명령어 중 분기 비율 확인 |
branch-misses | 분기 예측 실패 수 | 5% 이상이면 분기 최적화 고려 |
흔한 실수와 해결
"Error: Access to performance monitoring and observability operations is limited." 에러가 발생하면 perf_event_paranoid 값이 너무 높은 것입니다. sudo sysctl -w kernel.perf_event_paranoid=-1로 임시 해결하거나, setcap cap_perfmon+ep /usr/bin/perf로 perf에 권한을 부여하세요.
perf report에서 [unknown]이 많으면 디버그 심볼이 없는 것입니다. 1) 커널: linux-image-dbgsym 패키지 설치, 2) 유저 앱: -g 플래그로 컴파일 또는 debuginfo 패키지 설치, 3) -g 없이 perf record 실행 시 콜그래프가 불완전합니다 — 반드시 -g 또는 --call-graph 옵션을 사용하세요.
# 사용 가능한 이벤트 검색
perf list # 전체 이벤트 목록
perf list | grep -i cache # 캐시 관련 이벤트 검색
perf list | grep -i 'mem\|load' # 메모리 관련 이벤트 검색
# PMU 디바이스 확인
ls /sys/bus/event_source/devices/
# → cpu, software, tracepoint, breakpoint, power, uncore_*
# 특정 PMU의 이벤트
ls /sys/bus/event_source/devices/cpu/events/
# → instructions, cache-misses, branch-misses 등
perf 서브시스템 개요
perf는 리눅스 커널 2.6.31에서 도입된 성능 분석 프레임워크입니다. 하드웨어 PMU(Performance Monitoring Unit)와 소프트웨어 이벤트를 통합 인터페이스로 제공하며, perf_event_open() 시스템 콜이 핵심 진입점입니다.
perf 서브시스템 아키텍처 심층
perf 서브시스템은 유저스페이스 도구, 커널 이벤트 프레임워크, PMU 드라이버, 하드웨어의 4계층으로 구성됩니다. 각 계층의 역할과 데이터 흐름을 상세히 살펴봅니다.
perf_event_open() 시스템 콜 상세
perf_event_open()은 perf 서브시스템의 유일한 진입점으로, 5개의 인자를 받아 이벤트를 커널에 등록합니다. 반환된 파일 디스크립터(fd)로 이벤트를 제어(ioctl), 데이터를 읽기(read/mmap), 그룹 관리를 수행합니다.
/* perf_event_open() 시스템 콜 프로토타입 */
int perf_event_open(
struct perf_event_attr *attr, /* 이벤트 속성 구조체 */
pid_t pid, /* 대상 프로세스 (-1=모든 프로세스) */
int cpu, /* 대상 CPU (-1=모든 CPU) */
int group_fd, /* 그룹 리더 fd (-1=새 그룹) */
unsigned long flags /* PERF_FLAG_* 플래그 */
);
/* pid/cpu 조합에 따른 동작 차이 */
/* pid==-1, cpu>=0 : 특정 CPU의 모든 프로세스 모니터링 */
/* pid>=0, cpu==-1: 특정 프로세스의 모든 CPU 모니터링 */
/* pid>=0, cpu>=0 : 특정 프로세스 + 특정 CPU */
/* pid==-1, cpu==-1: 불가 (EINVAL) */
/* 반환된 fd로 수행할 수 있는 작업 */
ioctl(fd, PERF_EVENT_IOC_ENABLE, 0); /* 이벤트 활성화 */
ioctl(fd, PERF_EVENT_IOC_DISABLE, 0); /* 이벤트 비활성화 */
ioctl(fd, PERF_EVENT_IOC_RESET, 0); /* 카운터 리셋 */
ioctl(fd, PERF_EVENT_IOC_SET_BPF, bpf_fd); /* BPF 프로그램 연결 */
read(fd, &count, sizeof(count)); /* 카운터 값 읽기 */
/* mmap으로 ring buffer 매핑 (샘플링 모드) */
void *rb = mmap(NULL, (1 + 2/*pages*/) * PAGE_SIZE,
PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
struct perf_event_mmap_page *header = rb;
/* header→data_head: 커널 쓰기 위치 (원자적 읽기) */
/* header→data_tail: 유저 읽기 위치 (유저가 업데이트) */
perf_event_attr 주요 필드
| 필드 | 크기 | 설명 |
|---|---|---|
type | __u32 | 이벤트 타입 (PERF_TYPE_HARDWARE, SOFTWARE, TRACEPOINT 등) |
config | __u64 | 이벤트 ID (type에 따라 해석이 다름) |
sample_period / sample_freq | __u64 (union) | 샘플링 주기(이벤트 수) 또는 주파수(Hz) |
sample_type | __u64 | 샘플에 포함할 정보 비트마스크 (IP, TID, TIME, CALLCHAIN 등) |
read_format | __u64 | read() 시 반환할 데이터 형식 |
disabled | 1 bit | 1이면 비활성 상태로 생성 (ioctl ENABLE로 시작) |
inherit | 1 bit | 1이면 자식 프로세스도 카운팅 |
pinned | 1 bit | 1이면 항상 카운터 점유 (멀티플렉싱 제외) |
exclusive | 1 bit | 1이면 카운터를 독점 (다른 이벤트 배제) |
exclude_user | 1 bit | 유저스페이스 이벤트 제외 |
exclude_kernel | 1 bit | 커널 이벤트 제외 |
exclude_hv | 1 bit | 하이퍼바이저 이벤트 제외 |
precise_ip | 2 bits | 정밀도 수준 (0=best effort, 1-3=PEBS/IBS/SPE) |
use_clockid | 1 bit | 타임스탬프에 사용할 clock 선택 |
aux_output | 1 bit | AUX 버퍼로 출력 (Intel PT용) |
커널 내부 이벤트 처리 흐름
kernel/events/core.c (약 13,000줄)에 집중되어 있습니다. PMU 등록은 perf_pmu_register(), 이벤트 생성은 perf_event_alloc(), 샘플 출력은 perf_output_sample()에서 시작합니다. x86 PMU 드라이버는 arch/x86/events/, ARM은 drivers/perf/ 디렉토리를 참고하세요.
/* perf_event_open() 시스템 콜 */
struct perf_event_attr attr = {
.type = PERF_TYPE_HARDWARE,
.config = PERF_COUNT_HW_CPU_CYCLES,
.size = sizeof(attr),
.sample_period = 100000, /* 10만 사이클마다 샘플 */
.sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_CALLCHAIN,
.exclude_kernel = 0,
.exclude_hv = 1,
};
int fd = syscall(__NR_perf_event_open, &attr,
pid, cpu, group_fd, flags);
커널 심볼 디버깅(Debugging)
perf 분석 결과의 품질은 심볼(함수명, 소스 위치) 정보에 크게 의존합니다. 심볼이 없으면 [unknown]이나 16진수 주소만 표시되어 분석이 불가능합니다.
커널 디버그 정보 설정
# 커널 컴파일 옵션 확인
zcat /proc/config.gz | grep DEBUG_INFO
# CONFIG_DEBUG_INFO=y (기본 디버그 정보)
# CONFIG_DEBUG_INFO_DWARF5=y (DWARF v5 형식, 최신 커널 기본)
# CONFIG_DEBUG_INFO_BTF=y (BPF Type Format)
# CONFIG_DEBUG_INFO_REDUCED=n (축소하면 perf annotate 불가)
vmlinux vs /proc/kallsyms
# /proc/kallsyms: 실행 중 커널의 심볼 테이블 (항상 사용 가능)
head /proc/kallsyms
# ffffffff81000000 T _text
# → 주소 + 타입(T=text, D=data) + 심볼명
# vmlinux: ELF 바이너리 (DWARF 디버그 정보 포함)
# 위치: /usr/lib/debug/lib/modules/$(uname -r)/vmlinux
# 또는: /boot/vmlinux-$(uname -r)
# perf annotate는 vmlinux 필요 (어셈블리 + 소스 매핑)
perf annotate --vmlinux=/usr/lib/debug/lib/modules/$(uname -r)/vmlinux \
--symbol=tcp_sendmsg
debuginfod 서버 설정
# debuginfod: Build ID 기반 원격 디버그 정보 서버
# 환경변수 설정으로 자동 다운로드 활성화
export DEBUGINFOD_URLS="https://debuginfod.elfutils.org/"
# 배포판별 debuginfod 서버
# Fedora: https://debuginfod.fedoraproject.org/
# Ubuntu: https://debuginfod.ubuntu.com/
# Arch: https://debuginfod.archlinux.org/
# 캐시 위치
ls ~/.cache/debuginfod_client/
# Build ID 기반 캐시 구조
# 영구 설정
echo 'export DEBUGINFOD_URLS="https://debuginfod.elfutils.org/"' >> ~/.bashrc
Build ID 캐시 관리
# perf.data에 포함된 Build ID 확인
perf buildid-list -i perf.data
# Build ID 캐시에 바이너리 추가
perf buildid-cache --add /usr/lib/debug/lib/modules/$(uname -r)/vmlinux
# 캐시 경로
ls ~/.debug/.build-id/
# → Build ID의 처음 2자리를 디렉토리로 사용하는 구조
# 캐시 정리
perf buildid-cache --purge
| 심볼 소스 | 제공 정보 | 필요한 설정 |
|---|---|---|
/proc/kallsyms | 함수명, 주소 (커널) | 항상 사용 가능 (kptr_restrict=0 필요) |
vmlinux | 함수명 + 소스 위치 + 인라인 | CONFIG_DEBUG_INFO=y + debuginfo 패키지 |
debuginfod | 원격 디버그 정보 자동 다운로드 | DEBUGINFOD_URLS 환경변수 |
Build ID 캐시 | 바이너리 버전 매칭 | ~/.debug/.build-id/ 자동 관리 |
debuginfod 환경변수 설정 (자동 다운로드), 2) perf_event_paranoid=-1 (커널 심볼 접근), 3) 유저 앱은 -fno-omit-frame-pointer로 컴파일 (정확한 콜스택), 4) perf buildid-cache로 오프라인 분석용 캐시 관리.
/proc/sys/kernel/kptr_restrict가 1 이상이면 /proc/kallsyms의 주소가 모두 0으로 표시됩니다. perf가 커널 심볼을 해석하려면 sysctl -w kernel.kptr_restrict=0 또는 root 권한이 필요합니다.
커널 컴파일 옵션 가이드
perf의 기능을 최대한 활용하려면 적절한 커널 컴파일 옵션이 필요합니다. 아래 표는 각 기능별 필수/권장 커널 옵션을 정리합니다.
| 기능 | 커널 옵션 | 필수/권장 | 설명 |
|---|---|---|---|
| 기본 perf | CONFIG_PERF_EVENTS=y | 필수 | perf_event 서브시스템 활성화 |
| 트레이스포인트 | CONFIG_TRACEPOINTS=y | 필수 | sched:*, block:* 등 이벤트 |
| kprobe | CONFIG_KPROBES=y | 필수 | perf probe 동적 트레이스포인트 |
| uprobe | CONFIG_UPROBES=y | 필수 | 유저스페이스 동적 프로브 |
| BPF 연동 | CONFIG_BPF_SYSCALL=y | 권장 | perf lock contention --use-bpf |
| 락 분석 | CONFIG_LOCK_STAT=y | 선택 | perf lock record (BPF 없을 때) |
| 스케줄 분석 | CONFIG_SCHED_DEBUG=y | 권장 | 추가 스케줄러 트레이스포인트 |
| 디버그 정보 | CONFIG_DEBUG_INFO_DWARF5=y | 권장 | perf annotate 소스 매핑 |
| BTF | CONFIG_DEBUG_INFO_BTF=y | 권장 | BPF 타입 정보, CO-RE |
| ORC unwinder | CONFIG_UNWINDER_ORC=y | 권장 | 커널 콜스택 정확도 향상 |
| ftrace 통합 | CONFIG_FTRACE=y | 권장 | perf ftrace 서브커맨드 |
| cgroup | CONFIG_CGROUP_PERF=y | 선택 | 컨테이너별 프로파일링 |
| PEBS/LBR | 하드웨어 지원 필요 | - | Intel Core 2+, :pp precise |
| Intel PT | CONFIG_INTEL_TH=y | 선택 | intel_pt// 이벤트 |
# 현재 커널의 perf 관련 설정 확인
zcat /proc/config.gz 2>/dev/null | grep -E 'PERF|KPROBE|UPROBE|BPF|LOCK_STAT|DEBUG_INFO|FTRACE|TRACEPOINT' || \
grep -E 'PERF|KPROBE|UPROBE|BPF|LOCK_STAT|DEBUG_INFO|FTRACE|TRACEPOINT' /boot/config-$(uname -r)
# perf 빌드 옵션 확인
perf version --build-options
# → python, perl, libelf, libunwind, libbpf, libcrypto 등 확인
# perf가 지원하는 PMU 확인
ls /sys/bus/event_source/devices/
# → cpu, software, tracepoint, breakpoint, intel_pt, uncore_* 등
# PMU 기능 확인 (Intel)
cat /sys/bus/event_source/devices/cpu/caps/max_precise
# → 3 (PEBS 최대 정밀도 레벨)
cat /sys/bus/event_source/devices/cpu/nr_addr_filters
# → Intel PT 주소 필터 수
CONFIG_PERF_EVENTS=y, CONFIG_TRACEPOINTS=y, CONFIG_KPROBES=y, CONFIG_UPROBES=y가 기본 활성화되어 있습니다. 다만 CONFIG_LOCK_STAT과 CONFIG_DEBUG_INFO는 비활성화된 경우가 많으므로, 고급 분석이 필요하면 확인이 필요합니다.
PMU (Performance Monitoring Unit) 아키텍처
PMU는 CPU 내장 하드웨어로, 특정 이벤트(사이클, 캐시 미스, 분기 예측 실패 등)를 카운터로 집계합니다.
| 아키텍처 | PMU | 범용 카운터 | 고정 카운터 | 정밀 샘플링 |
|---|---|---|---|---|
| x86 Intel | IA32_PMC0-7 | 4-8개 | 3개 (cycles, instructions, ref-cycles) | PEBS |
| x86 AMD | MSR C001_0200-020B | 6개 | 없음 (Zen4+: 3개) | IBS |
| ARM | PMU v3 (PMCR_EL0) | 6-31개 | 1개 (cycle counter) | SPE |
| RISC-V | HPM CSR (mhpmcounter) | 29개 (3-31) | 3개 (cycle, instret, time) | 없음 |
/* include/linux/perf_event.h — PMU 드라이버 인터페이스 */
struct pmu {
const char *name;
int type; /* PERF_TYPE_* */
int capabilities;
/* 이벤트 라이프사이클 */
int (*event_init)(struct perf_event *event);
void (*add)(struct perf_event *event, int flags);
void (*del)(struct perf_event *event, int flags);
void (*start)(struct perf_event *event, int flags);
void (*stop)(struct perf_event *event, int flags);
void (*read)(struct perf_event *event);
/* 스케줄링 (멀티플렉싱) */
int (*schedule_events)(struct cpu_hw_events *cpuc, int n, int *assign);
void (*sched_task)(struct perf_event_pmu_context *pmu_ctx, bool sched_in);
};
이벤트 유형 상세
perf는 6가지 이벤트 유형을 지원합니다. 각 유형은 perf_event_attr.type과 config 필드의 조합으로 식별됩니다.
| type | 설명 | 예시 |
|---|---|---|
PERF_TYPE_HARDWARE | CPU 하드웨어 카운터 | cycles, instructions, cache-misses, branch-misses |
PERF_TYPE_SOFTWARE | 커널 소프트웨어 이벤트 | context-switches, page-faults, cpu-migrations |
PERF_TYPE_TRACEPOINT | 커널 트레이스포인트 | sched:sched_switch, block:block_rq_issue |
PERF_TYPE_HW_CACHE | 캐시 이벤트 (3차원 인코딩) | L1-dcache-load-misses, LLC-store-misses |
PERF_TYPE_RAW | PMU 전용 raw 이벤트 | r04c8 (Intel: DTLB_LOAD_MISSES.WALK_COMPLETED) |
PERF_TYPE_BREAKPOINT | 하드웨어 브레이크포인트 | 메모리 주소 접근 감시 |
Raw 이벤트 코딩
perf가 제공하는 일반 이벤트 이름으로 표현할 수 없는 PMU 전용 이벤트는 raw 코드로 직접 지정합니다. Intel/AMD의 아키텍처별 이벤트 번호를 조합하여 사용합니다.
# === Raw 이벤트 지정 방식 ===
# Intel: rUUEE 형식 (UU=umask, EE=event_select)
# 예: DTLB_LOAD_MISSES.WALK_COMPLETED = Event 0x08, Umask 0x0E
perf stat -e r0E08 -- ./workload
# 또는 가독성 좋은 sysfs 이벤트명
perf stat -e cpu/event=0x08,umask=0x0E/ -- ./workload
# AMD: 유사한 형식
# LsBadStatus2.StliOther = Event 0x24, Umask 0x02
perf stat -e cpu/event=0x24,umask=0x02/ -- ./workload
# sysfs에 정의된 이벤트 사용 (가독성 우수)
ls /sys/bus/event_source/devices/cpu/events/
# → instructions, cache-misses, mem-loads, mem-stores 등
# 이벤트 정의 파일 내용 확인
cat /sys/bus/event_source/devices/cpu/events/cache-misses
# → event=0x2e,umask=0x41
# PMU 포맷 확인 (비트 필드 레이아웃)
ls /sys/bus/event_source/devices/cpu/format/
# → event, umask, edge, inv, cmask, any, ...
cat /sys/bus/event_source/devices/cpu/format/event
# → config:0-7 (config 비트 0~7이 event_select)
HW_CACHE 3차원 인코딩
PERF_TYPE_HW_CACHE 이벤트는 캐시 ID, 연산 유형, 결과 3가지 차원의 조합으로 인코딩됩니다.
| 차원 | 값 | 설명 |
|---|---|---|
| Cache ID | L1-dcache | L1 데이터 캐시 |
| L1-icache | L1 명령어 캐시 | |
| LLC | Last Level Cache (L3) | |
| dTLB | 데이터 TLB | |
| iTLB | 명령어 TLB | |
| branch | 분기 예측 | |
| node | NUMA 노드 메모리 | |
| 연산 | loads | 읽기 |
| stores | 쓰기 | |
| prefetches | 프리페치 | |
| 결과 | (접근) | 전체 접근 횟수 (이름에 -misses 없음) |
| -misses | 미스 횟수 |
# HW_CACHE 이벤트 조합 예시
perf stat -e L1-dcache-loads,L1-dcache-load-misses \
-e LLC-loads,LLC-load-misses \
-e dTLB-loads,dTLB-load-misses \
-e iTLB-loads,iTLB-load-misses \
-- ./workload
# 미스율 계산
# L1D miss rate = L1-dcache-load-misses / L1-dcache-loads
# LLC miss rate = LLC-load-misses / LLC-loads
# DTLB miss rate = dTLB-load-misses / dTLB-loads
하드웨어 브레이크포인트
# 특정 메모리 주소 접근 감시 (하드웨어 워치포인트)
# 변수 주소 확인
nm ./my_program | grep my_global_var
# → 0x0000000000404020 B my_global_var
# 해당 주소에 쓰기 접근 시 샘플링
perf record -e mem:0x404020:w -g -- ./my_program
# :w = 쓰기, :r = 읽기, :rw = 읽기+쓰기, :x = 실행
# 커널 변수 감시
perf record -e mem:0xffffffff81a00000:w -a -- sleep 5
# → 해당 커널 주소에 쓰기 시 콜스택 수집
# 하드웨어 브레이크포인트 수 확인
# x86: 최대 4개 (DR0-DR3)
# ARM: 최대 16개 (구현에 따라 다름)
# 사용 가능한 이벤트 목록
perf list # 전체
perf list hw # 하드웨어만
perf list sw # 소프트웨어만
perf list tracepoint # 트레이스포인트만
perf list --raw-dump # 기계 파싱용
# PMU별 이벤트
ls /sys/bus/event_source/devices/
# → cpu, software, tracepoint, breakpoint, power, uncore_*
# 특정 이벤트 검색
perf list | grep -i cache # 캐시 관련
perf list | grep -i 'mem\|load' # 메모리 관련
perf list | grep -i tlb # TLB 관련
perf record
perf record는 샘플링 모드로 이벤트를 수집하여 perf.data 파일에 저장합니다. 핵심 메커니즘은 PMU 카운터 오버플로 → NMI/PMI 인터럽트 → 커널 ring buffer 기록 → 유저스페이스 mmap 읽기의 4단계입니다.
# 기본 사이클 프로파일링 (99Hz 샘플링)
perf record -F 99 -g -- ./workload
# 콜스택 방식 선택
perf record --call-graph dwarf # DWARF unwind (정확, 오버헤드 큼)
perf record --call-graph fp # Frame Pointer (빠르지만 -fno-omit-frame-pointer 필요)
perf record --call-graph lbr # Intel LBR (빠르고 정확, Intel 전용)
# 특정 이벤트 + 필터링
perf record -e cache-misses -e instructions -c 10000 \
--filter 'comm == "mysql"' -- sleep 10
# 시스템 전체 프로파일링
perf record -a -g -F 99 -- sleep 30
# 출력 파일 지정
perf record -o profile.data -g -- ./workload
mmap 기반 링 버퍼 구조
perf record는 커널과 유저스페이스 간 데이터 전송에 mmap 기반 공유 링 버퍼를 사용합니다. 커널은 data_head를 증가시키며 샘플을 기록하고, 유저스페이스(perf record)는 data_tail을 업데이트하며 데이터를 읽습니다. 이 설계로 시스템 콜 없이 데이터를 전송하여 오버헤드를 최소화합니다.
PERF_SAMPLE_* 플래그 상세
perf_event_attr.sample_type 비트마스크로 샘플에 포함할 정보를 선택합니다. 플래그가 많을수록 샘플 크기가 커져 ring buffer 소비가 빨라지므로, 필요한 정보만 선택해야 합니다.
| 플래그 | 비트 | 샘플에 추가되는 데이터 | 용도 |
|---|---|---|---|
PERF_SAMPLE_IP | 0x001 | u64 ip (명령어 포인터) | 핫스팟 함수/명령어 식별 |
PERF_SAMPLE_TID | 0x002 | u32 pid, u32 tid | 프로세스/스레드별 분류 |
PERF_SAMPLE_TIME | 0x004 | u64 timestamp | 시간 기반 분석, 타임라인 |
PERF_SAMPLE_ADDR | 0x008 | u64 data_addr | 메모리 접근 주소 (perf mem/c2c) |
PERF_SAMPLE_READ | 0x010 | 그룹 카운터 값들 | 샘플 시점의 모든 카운터 스냅샷 |
PERF_SAMPLE_CALLCHAIN | 0x020 | u64 nr + u64[] ips | 콜스택 (Flamegraph) |
PERF_SAMPLE_ID | 0x040 | u64 id (이벤트 식별자) | 멀티 이벤트 구분 |
PERF_SAMPLE_CPU | 0x080 | u32 cpu, u32 res | CPU별 분석 |
PERF_SAMPLE_PERIOD | 0x100 | u64 period | 가변 주기 보정 |
PERF_SAMPLE_WEIGHT | 0x400 | u64 weight (지연 사이클) | 메모리 레이턴시 분석 |
PERF_SAMPLE_DATA_SRC | 0x8000 | u64 data_src 인코딩 | L1/L2/L3/DRAM 히트 구분 |
PERF_SAMPLE_PHYS_ADDR | 0x80000 | u64 phys_addr | NUMA 노드 분석 |
PERF_SAMPLE_CGROUP | 0x200000 | u64 cgroup_id | 컨테이너별 분석 |
/* ring buffer에서 샘플 읽기 (유저스페이스) */
struct perf_event_mmap_page *header = mmap_base;
while (1) {
/* data_head를 원자적으로 읽기 (커널이 비동기 업데이트) */
__u64 head = __atomic_load_n(&header->data_head, __ATOMIC_ACQUIRE);
__u64 tail = header->data_tail;
if (head == tail) {
/* 새 데이터 없음 — poll() 또는 sleep */
poll(&pfd, 1, -1);
continue;
}
/* 데이터 읽기 (원형 버퍼이므로 data_size로 wrap) */
while (tail < head) {
__u64 offset = tail % data_size;
struct perf_event_header *ehdr = data_base + offset;
switch (ehdr->type) {
case PERF_RECORD_SAMPLE:
process_sample(ehdr);
break;
case PERF_RECORD_MMAP2:
process_mmap(ehdr); /* DSO 매핑 추적 */
break;
case PERF_RECORD_LOST:
lost_count += ((struct lost_event*)ehdr)->lost;
break;
}
tail += ehdr->size;
}
/* data_tail 업데이트 (커널에게 읽기 완료 알림) */
__atomic_store_n(&header->data_tail, tail, __ATOMIC_RELEASE);
}
PERF_RECORD_LOST를 기록합니다. perf report에서 "X.XX% lost events"가 보이면: 1) --mmap-pages로 버퍼 크기 증가 (기본 512KB, perf record -m 8M으로 8MB), 2) 샘플링 주파수 감소 (-F 99 → -F 49), 3) sample_type 플래그 줄이기, 4) --switch-output=1G로 파일 분할.
콜그래프(Call Graph) 방식 비교
| 방식 | 옵션 | 정확도 | 오버헤드 | 요구사항 |
|---|---|---|---|---|
| Frame Pointer | --call-graph fp | 높음 (FP 있을 때) | 매우 낮음 | -fno-omit-frame-pointer 컴파일 |
| DWARF | --call-graph dwarf | 매우 높음 | 높음 (스택 복사) | debuginfo, --call-graph dwarf,8192 |
| LBR | --call-graph lbr | 높음 (32 프레임) | 매우 낮음 | Intel Haswell+, 깊이 제한 |
| ORC | 자동 (커널) | 높음 | 낮음 | CONFIG_UNWINDER_ORC=y |
# DWARF 콜그래프 — 스택 덤프 크기 조절로 오버헤드 제어
perf record --call-graph dwarf,4096 -F 99 -- ./workload
# 기본 8192 바이트, 줄이면 오버헤드 감소 but 깊은 스택 손실
# LBR 콜그래프 — Intel 전용, 하드웨어 지원
perf record --call-graph lbr -F 99 -- ./workload
# 장점: 오버헤드 거의 없음, FP 불필요
# 단점: 최대 32 프레임, Intel 전용
# 커널은 ORC unwinder 사용 (CONFIG_UNWINDER_ORC=y)
# 유저스페이스는 FP 또는 DWARF 선택
# 실전 권장 조합
perf record -F 99 --call-graph fp -g \
-e cycles:pp -a -- sleep 30
# :pp = PEBS precise, fp = 빠른 콜그래프, -a = 시스템 전체
perf stat
perf stat는 카운팅 모드로 이벤트를 집계합니다. 샘플링 오버헤드가 거의 없어 정밀한 숫자를 얻을 수 있습니다.
# 기본 통계 (cycles, instructions, IPC, branches, cache-misses)
perf stat ./workload
# 상세 통계 (-d: L1/LLC 캐시, TLB)
perf stat -d -d ./workload
# 특정 이벤트 그룹 (동시 측정 보장)
perf stat -e '{cycles,instructions,cache-misses}' ./workload
# 반복 실행 + 통계 (평균/표준편차)
perf stat -r 5 ./workload
# CPU별, 1초 간격 출력
perf stat -a -A -I 1000 -e cycles,instructions -- sleep 10
# 결과 예:
# 3,248,521,387 cycles # 3.2 GHz
# 5,123,987,654 instructions # 1.58 insn per cycle (IPC)
# 42,567,321 cache-misses # 12.3% of cache-references
이벤트 그룹핑과 메트릭 계산
정확한 비율 계산(캐시 미스율, 분기 미스율 등)을 위해서는 분자와 분모 이벤트가 정확히 같은 시간에 측정되어야 합니다. {}로 그룹을 묶으면 커널이 동시 측정을 보장합니다.
# 이벤트 그룹핑 — {} 안의 이벤트는 동시 측정 보장
perf stat -e '{cycles,instructions}' \
-e '{cache-references,cache-misses}' \
-e '{branches,branch-misses}' \
./workload
# 메트릭 그룹 (-M 옵션, 사전 정의된 이벤트 조합)
perf stat -M CPI ./workload # Cycles Per Instruction
perf stat -M MPKI ./workload # Misses Per Kilo Instructions
perf stat -M L1MPKI ./workload # L1 Cache MPKI
perf stat -M TMA_Frontend_Bound ./workload
# 사용 가능한 메트릭 목록 확인
perf list metric
perf list metricgroup
# 메트릭 기반 분석 실전 예제
perf stat -M TopdownL1,IPC,MPKI -- ./workload
# 출력: Frontend Bound 15.3%, Backend Bound 34.4%,
# Bad Spec 12.1%, Retiring 38.2%
# IPC: 1.58, MPKI: 4.2
TopDown 분석 방법론 (TMA Level 1)
TopDown Microarchitecture Analysis(TMA)는 CPU 파이프라인 슬롯을 4개 카테고리로 분류하여 병목 원인을 체계적으로 찾는 방법론입니다. perf stat --topdown으로 Level 1을 즉시 확인할 수 있습니다.
# TopDown Level 1 분석 (커널 v5.13+)
perf stat --topdown -a -- ./workload
# 출력:
# retiring bad spec frontend backend
# 38.2% 12.1% 15.3% 34.4%
# → Backend Bound가 34.4%로 주 병목
# TopDown Level 2-3 (pmu-tools 필요)
# pip install pmu-tools 또는 git clone
toplev --single-thread -l2 -- ./workload
# → Backend_Bound.Memory_Bound: 28.1%
# → Backend_Bound.Core_Bound: 6.3%
toplev --single-thread -l3 -- ./workload
# → Memory_Bound.L3_Bound: 18.5% ← LLC 미스가 주범
# → Memory_Bound.DRAM_Bound: 9.6%
# Level 3 결과 기반 구체적 이벤트 수집
perf stat -e '{cycles,mem_load_retired.l3_miss,mem_load_retired.l3_hit}' \
-- ./workload
# → L3 미스율 계산: l3_miss / (l3_miss + l3_hit)
# 메모리 병목이 확인되면 perf record로 핫스팟 찾기
perf record -e mem_load_retired.l3_miss:pp -c 1000 -g -- ./workload
perf report --sort sym
perf stat --topdown으로 Level 1 확인 → 2) 가장 높은 카테고리 방향으로 toplev -l3 드릴다운 → 3) Level 3에서 구체적 이벤트 식별 → 4) 해당 이벤트로 perf record -e ... -g 프로파일링 → 5) perf annotate로 핫 명령어 확인 → 6) 코드 최적화 적용.
perf report / perf annotate
perf report는 perf.data를 분석하여 핫스팟 함수를 보여줍니다. perf annotate는 어셈블리(Assembly) 수준 분석을 제공합니다.
# 기본 보고서 (TUI 인터페이스)
perf report
# stdio 출력 (스크립팅/파이프용)
perf report --stdio --sort comm,dso,sym
# 콜그래프 출력
perf report --call-graph graph,0.5,caller
# 특정 함수 어셈블리 어노테이션
perf annotate --symbol=tcp_sendmsg
# DSO(공유 라이브러리) 기준 정렬
perf report --sort dso
# 차이 비교 (before/after 최적화)
perf diff before.data after.data
# 필터링 옵션
perf report --comm nginx # 특정 프로세스만
perf report --dso libc.so.6 # 특정 라이브러리만
perf report --symbol-filter tcp_ # 심볼 이름 필터
perf report --percent-limit 1.0 # 1% 이상만 표시
# children vs self 오버헤드
perf report --no-children # self 오버헤드만 (직접 실행)
perf report --children # children 포함 (호출 포함, 기본값)
# 계층적 보고서
perf report --hierarchy --stdio
# → 트리 형태로 overhead 표시
# 가중치 기반 정렬 (메모리 분석 시)
perf report --sort sym --field-separator , --weight
# → PEBS weight 기반 정렬 (캐시 미스 지연 반영)
perf report 출력 해석
| 컬럼 | 의미 | 해석 |
|---|---|---|
Overhead | 전체 샘플 중 비율 | 높을수록 CPU 시간 많이 사용 |
Children | 하위 호출 포함 비율 | 해당 함수 + 호출한 모든 하위 함수 |
Self | 함수 자체 비율 | 함수 직접 실행 시간만 |
Command | 프로세스 이름 | comm 필드 |
Shared Object | DSO (라이브러리/바이너리) | [kernel.kallsyms], libc.so.6 등 |
Symbol | 함수 이름 | [k] = 커널, [.] = 유저 |
perf diff로 최적화 전후 비교
# 최적화 전 프로파일
perf record -F 99 -g -o before.data -- ./workload_v1
# 최적화 후 프로파일
perf record -F 99 -g -o after.data -- ./workload_v2
# 차이 보고서
perf diff before.data after.data
# 출력:
# Baseline Delta Abs Symbol
# 42.30% -15.20% matrix_multiply ← 개선됨!
# 8.50% +3.10% new_function ← 새 오버헤드
# 28.10% -0.30% sort_elements ← 변화 없음
# 차이 Flamegraph (시각적 비교)
perf script -i before.data | stackcollapse-perf.pl > before.folded
perf script -i after.data | stackcollapse-perf.pl > after.folded
difffolded.pl before.folded after.folded | \
flamegraph.pl --negate > optimization_diff.svg
# → 빨간색: 증가, 파란색: 감소
perf report가 느리면: 1) --percent-limit 0.5로 0.5% 미만 항목 제거, 2) --no-children으로 children 계산 생략, 3) --sort sym으로 정렬 기준 단순화, 4) -n으로 샘플 수 표시. perf report --stdio가 TUI보다 빠릅니다.
perf 빠른 참조 카드
가장 자주 사용하는 perf 명령어를 한눈에 볼 수 있는 참조표입니다.
| 목적 | 명령어 |
|---|---|
| 기본 성능 통계 | perf stat ./app |
| 상세 캐시/TLB 통계 | perf stat -d -d ./app |
| TopDown 분석 | perf stat --topdown ./app |
| 반복 측정 (평균/표준편차) | perf stat -r 5 ./app |
| CPU 프로파일링 | perf record -F 99 -g -- ./app |
| 시스템 전체 프로파일링 | perf record -a -g -F 99 -- sleep 30 |
| PEBS 정밀 샘플링 | perf record -e cycles:pp -g -- ./app |
| 보고서 확인 | perf report --stdio |
| 어셈블리 분석 | perf annotate --symbol=func |
| Flamegraph 생성 | perf script | stackcollapse-perf.pl | flamegraph.pl > f.svg |
| 실시간 모니터링 | perf top -g |
| 메모리 프로파일링 | perf mem record -- ./app |
| False sharing 탐지 | perf c2c record -- ./app |
| 락 경합 분석 | perf lock contention --use-bpf -a -- sleep 5 |
| 스케줄링 분석 | perf sched record -- sleep 10 |
| 시스템 콜 추적 | perf trace -s -- ./app |
| 동적 프로브 추가 | perf probe --add 'func arg' |
| 최적화 전후 비교 | perf diff before.data after.data |
| JSON 출력 (CI용) | perf stat --json -e cycles,instructions -- ./app 2> r.json |
| Intel PT 수집 | perf record -e intel_pt// -- ./app |
| 벤치마크 스위트 | perf bench all |
유용한 환경변수
| 환경변수 | 설명 | 예시 |
|---|---|---|
DEBUGINFOD_URLS | 원격 debuginfo 서버 | https://debuginfod.ubuntu.com/ |
PERF_EXEC_PATH | perf 실행 경로 | /usr/lib/linux-tools/$(uname -r) |
PERF_BUILDID_DIR | Build ID 캐시 디렉토리 | ~/.debug (기본값) |
PERF_PAGER | perf 출력 페이저 | less (기본값) |
perf top
perf top은 실시간 프로파일링 도구로, top 명령처럼 라이브로 핫스팟을 보여줍니다. 프로덕션 시스템에서 현재 CPU를 가장 많이 사용하는 함수를 즉시 식별할 수 있으며, 대화형 TUI에서 실시간으로 콜그래프를 탐색할 수 있습니다.
# 시스템 전체 실시간 프로파일링
perf top -g
# 특정 프로세스
perf top -p $(pidof nginx)
# 커널 심볼만
perf top --sort sym --dso '[kernel.kallsyms]'
# 특정 이벤트
perf top -e cache-misses
# 콜그래프 포함 (핫 콜패스 확인)
perf top -g --call-graph fp
# 특정 CPU만 모니터링
perf top -C 0,1,2,3
# 샘플링 주파수 조절
perf top -F 999 -g # 높은 해상도
perf top -F 49 -g # 낮은 오버헤드
# 정렬 기준 변경
perf top --sort comm,dso,sym # 프로세스 + DSO + 심볼
perf top --sort dso # 라이브러리별
perf top --sort cpu,sym # CPU별 + 심볼
# stdio 모드 (TUI 없이 텍스트 출력)
perf top --stdio --sort sym | head -30
perf top TUI 단축키
| 키 | 동작 |
|---|---|
Enter | 선택한 심볼의 annotate 뷰 (어셈블리 수준) |
a | 어노테이트 모드 토글 |
d | DSO 필터 설정 |
H | 스레드별 표시 토글 |
K | 커널 심볼 표시/숨김 |
U | 유저 심볼 표시/숨김 |
z | 제로 카운트 항목 숨김 |
E | 이벤트 선택 |
q | 종료 |
perf top -g로 즉시 핫스팟 확인, 2) 커널 CPU 사용률 높을 때 perf top --sort sym --dso '[kernel.kallsyms]'로 커널 함수 확인, 3) 특정 프로세스 분석 시 perf top -p PID -g로 콜패스 포함 실시간 모니터링. perf top은 perf.data를 생성하지 않으므로 디스크 사용 없이 즉시 사용 가능합니다.
Flamegraph 생성
Brendan Gregg의 Flamegraph는 콜스택 프로파일링 결과를 시각화하는 표준 방법입니다.
# 1. 프로파일 수집
perf record -F 99 -a -g -- sleep 30
# 2. Flamegraph 생성 (Brendan Gregg 도구)
perf script | stackcollapse-perf.pl | flamegraph.pl > flame.svg
# 또는 perf 내장 flamegraph (v5.18+)
perf script report flamegraph
# 역방향 (icicle) 그래프: 호출자 기준 상단 정렬
perf script | stackcollapse-perf.pl | flamegraph.pl --reverse > icicle.svg
# 차이 flamegraph (before/after)
difffolded.pl before.folded after.folded | flamegraph.pl > diff.svg
Flame Graph 해석 방법
| 해석 요소 | 의미 | 조치 |
|---|---|---|
| 넓은 프레임 | 해당 함수(및 하위 호출)가 전체 CPU 시간의 큰 비율 차지 | 가장 넓은 프레임부터 최적화 우선순위 결정 |
| 탑 프레임 (맨 위) | 실제 CPU 사이클을 소비하는 함수 (self time) | 알고리즘/데이터 구조 최적화, SIMD 적용 |
| 높은 스택 | 깊은 콜체인 (많은 중간 함수 호출) | 인라인화, 콜체인 단축 |
| "[unknown]" 프레임 | 심볼 정보 없음 | debuginfo 설치, -fno-omit-frame-pointer 재컴파일 |
| X축 순서 | 알파벳 순서 (시간 순서가 아님) | 좌→우 방향은 의미 없음, 오직 너비만 의미 있음 |
# Flame Graph 고급 옵션
# 커널/유저 색상 분리
perf script | stackcollapse-perf.pl | \
flamegraph.pl --color=java --title="My App Profile" \
--subtitle="perf record -F 99 -g" > flame.svg
# Off-CPU Flame Graph (I/O/락 대기 시각화)
offcputime-bpfcc -df -p $(pidof myapp) 30 > offcpu.folded
flamegraph.pl --color=io --title="Off-CPU" \
--countname=us < offcpu.folded > offcpu.svg
# 차이 Flame Graph (최적화 전후 비교)
# 빨간색 = 증가, 파란색 = 감소
perf script -i before.data | stackcollapse-perf.pl > before.folded
perf script -i after.data | stackcollapse-perf.pl > after.folded
difffolded.pl before.folded after.folded | \
flamegraph.pl --negate > diff.svg
# 메모리 Flame Graph (캐시 미스 위치)
perf record -e cache-misses -g -- ./workload
perf script | stackcollapse-perf.pl | \
flamegraph.pl --color=mem --title="Cache Miss" > cachemiss.svg
# d3-flame-graph (인터랙티브 HTML)
perf script report flamegraph # 커널 5.18+
# → flamegraph.html 생성 (검색, 줌, 필터 가능)
[unknown]이 많으면 심볼 문제 해결이 먼저.
perf probe / 동적 트레이스포인트
perf probe는 커널/유저 함수에 동적 트레이스포인트를 추가하여 kprobe/uprobe를 perf 이벤트로 사용합니다. DWARF 디버그 정보를 활용하여 함수 인자, 지역 변수, 구조체 필드까지 캡처할 수 있으며, SDT(Statically Defined Tracing) 프로브도 지원합니다.
# 커널 함수에 kprobe 추가
perf probe --add tcp_sendmsg
perf record -e probe:tcp_sendmsg -a -- sleep 5
perf probe --del tcp_sendmsg
# 함수 인자 캡처
perf probe --add 'tcp_sendmsg size'
perf probe --add 'vfs_read file->f_path.dentry->d_iname:string'
# 유저스페이스 uprobe
perf probe -x /usr/lib/libc.so.6 --add malloc
perf record -e probe_libc:malloc -g -- ./app
# 사용 가능한 변수/필드 확인
perf probe --vars tcp_sendmsg
perf probe --line tcp_sendmsg
# 특정 라인에 프로브 (함수 진입이 아닌 특정 위치)
perf probe --add 'tcp_sendmsg:25 skb->len'
# → tcp_sendmsg 함수의 25번째 줄에서 skb->len 값 캡처
# 리턴 프로브 (반환 값 캡처)
perf probe --add 'tcp_sendmsg%return $retval'
# → 반환 시 리턴값 기록
# 문자열 필드 접근
perf probe --add 'vfs_read file->f_path.dentry->d_iname:string'
# → 파일 이름을 문자열로 캡처
SDT (Statically Defined Tracing) 프로브
SDT는 바이너리에 미리 정의된 트레이스포인트로, DTrace의 USDT(User-level Statically Defined Tracing)와 호환됩니다. 라이브러리나 애플리케이션이 ELF .note.stapsdt 섹션에 프로브 메타데이터를 포함하면, 런타임에 활성화/비활성화가 가능합니다. 비활성 시 NOP 명령이므로 오버헤드가 없습니다.
# 바이너리에 정의된 SDT 프로브 확인
perf buildid-cache --add /usr/lib/libc.so.6
perf probe --cache --list
# → sdt_libc:malloc, sdt_libc:free, sdt_libc:longjmp 등
# SDT 프로브 활성화
perf probe --add sdt_libc:malloc
perf record -e probe_libc:malloc -g -- ./my_app
perf probe --del probe_libc:malloc
# Python SDT 프로브 (python3-dbg 패키지 필요)
perf probe --add sdt_python3:function__entry
perf record -e probe_python3:function__entry -g -- python3 ./script.py
perf script
# MySQL SDT 프로브
perf probe --add sdt_mysqld:query__start
perf record -e probe_mysqld:query__start -a -- sleep 10
# SDT 프로브 메타데이터 직접 확인 (ELF 섹션)
readelf -n /usr/lib/libc.so.6 | grep -A4 stapsdt
readelf -n binary | grep stapsdt로 지원 여부를 확인하세요. SDT는 kprobe/uprobe와 달리 바이너리 개발자가 의도적으로 배치한 포인트이므로 안정성이 높고 ABI가 유지됩니다.
perf sched 상세
스케줄러(Scheduler) 성능 분석 도구입니다. sched:sched_switch, sched:sched_wakeup 등의 트레이스포인트를 수집하여 컨텍스트 스위치 빈도, wakeup-to-running 레이턴시, CPU 활용 패턴을 분석합니다.
# 스케줄러 이벤트 기록 (모든 sched:* 트레이스포인트)
perf sched record -a -- sleep 10
# === latency: wakeup → running 지연 분석 ===
perf sched latency --sort max
# 출력 예:
# Task | Runtime ms | Switches | Avg delay ms | Max delay ms
# nginx:1234 | 345.678 | 12345 | 0.023 | 2.456
# mysql:5678 | 234.567 | 8901 | 0.156 | 15.789
# → Max delay가 높으면 해당 태스크가 오래 runqueue에서 대기한 것
# === timehist: 시간순 이벤트 타임라인 ===
perf sched timehist
# 출력 예:
# time cpu task wait time sch delay run time
# 1000.100 [0] nginx:1234 0.000 ms 0.023 ms 1.234 ms
# 1001.345 [0] ksoftirqd/0:7 0.000 ms 0.005 ms 0.089 ms
# 1001.445 [0] nginx:1234 12.345 ms 0.034 ms 0.567 ms
# wait time: Off-CPU 시간, sch delay: runqueue 대기, run time: On-CPU 시간
# wakeup chain 추적 (누가 깨웠는지)
perf sched timehist -w
# → waker: ksoftirqd/0 → wakee: nginx:1234
# → I/O 완료 인터럽트가 nginx를 깨움
# 유휴(idle) 시간 포함
perf sched timehist -I
# → CPU가 idle인 시간도 표시 (CPU 활용률 분석)
# === map: CPU별 태스크 점유 맵 ===
perf sched map
# 출력 예 (ASCII art):
# *A0 B0 *C0 *D0
# [0001] nginx mysql . ksoftirqd
# [0002] . nginx mysql .
# *는 새로 스케줄된 태스크, .는 idle
# === replay: 스케줄링 시뮬레이션 ===
perf sched replay
# 기록된 스케줄링을 재현하여 스케줄러 변경 효과 테스트
# → "target context switch rate: 12345/sec, achieved: 11234/sec"
스케줄러 레이턴시 병목 진단
| 증상 | perf sched 확인 방법 | 원인 | 해결 |
|---|---|---|---|
| 높은 Max delay | latency --sort max | 다른 태스크가 CPU 독점 | 우선순위 조정, CPU 격리 |
| 높은 wait time | timehist (wait 컬럼) | I/O, 락, sleep 대기 | Off-CPU 분석, 락 최적화 |
| 잦은 migration | timehist (CPU 변경) | CPU 간 이동 → 캐시 효율 저하 | taskset, CPU affinity |
| 높은 context-switches | latency (Switches) | 과도한 스레드/프로세스 | 스레드 풀 크기 조정 |
| 불균형 CPU 사용 | map | 특정 CPU에 편중 | NUMA 밸런싱, IRQ 분산 |
perf sched record는 모든 sched:sched_switch 이벤트를 기록하므로 고부하 시스템에서 초당 수만~수십만 이벤트가 발생합니다. 프로덕션 환경에서는 짧은 시간(-- sleep 5) 동안만 수집하거나, 특정 태스크만 대상(-p PID)으로 하세요. 장시간 분석은 BPF 기반 offcputime이 적합합니다.
perf mem / perf c2c
메모리 접근 프로파일링과 false sharing 탐지 도구입니다. perf mem은 PEBS/IBS/SPE를 활용하여 메모리 로드/스토어의 지연 시간과 데이터 소스(L1/L2/LLC/DRAM)를 분석하고, perf c2c는 HITM(Hit In Modified) 이벤트를 추적하여 캐시 일관성(Cache Coherency) 프로토콜에 의한 성능 저하를 탐지합니다.
# 메모리 로드/스토어 프로파일링
perf mem record -- ./workload
perf mem report --sort mem,sym
# 메모리 레이턴시 필터 (30 사이클 이상 지연만)
perf mem record -e ldlat-loads --ldlat=30 -- ./workload
perf mem report --sort mem,sym,dso
# 데이터 소스 분포 확인
perf mem report --sort mem
# 출력 예:
# Samples Overhead Memory access
# 12345 62.30% L1 hit
# 3456 17.40% L2 hit
# 2345 11.80% LLC hit
# 1234 6.20% Local RAM hit
# 456 2.30% Remote RAM (1 hop)
# === false sharing 탐지 (perf c2c) ===
perf c2c record -a -- ./workload
perf c2c report --stdio
# HITM 상위 캐시라인 상세 분석
perf c2c report --stats
# → Shared Data Cache Line Table에서 가장 HITM 많은 라인 확인
# 소스 코드 레벨 분석 (디버그 심볼 필요)
perf c2c report --call-graph=fp --source
# 결과 해석 핵심:
# Rmt HITM: 원격 NUMA 노드의 Modified 캐시라인 히트 (가장 비쌈)
# Lcl HITM: 로컬 소켓 내 다른 코어의 Modified 히트
# Shared Data CL: 어떤 변수가 같은 캐시라인에 있는지 확인
perf c2c 보고서 해석 가이드
=================================================
Shared Data Cache Line Table (Top 5)
=================================================
# Rmt Lcl Stores Loads LLC Load Hit Symbol / Data Offset
1 823 156 4567 8901 345 [.] worker_thread / 0x20
2 412 89 2345 5678 234 [.] process_request / 0x40
3 201 45 1234 3456 123 [k] tcp_sendmsg / 0x18
→ #1: worker_thread 의 오프셋 0x20에서 Remote HITM 823회 발생
→ 같은 캐시라인의 다른 변수들이 다른 코어에서 동시 접근
→ pahole로 해당 구조체 레이아웃 확인 후 분리 필요
/* false sharing 해결 예제 */
/* 문제: counter와 flag가 같은 64B 캐시라인에 위치 */
struct bad_layout {
unsigned long counter; /* CPU 0이 자주 쓰기 */
unsigned long flag; /* CPU 1이 자주 쓰기 */
};
/* 해결 1: __cacheline_aligned_in_smp 패딩 */
struct good_layout {
unsigned long counter;
unsigned long flag __cacheline_aligned_in_smp;
/* flag가 다음 캐시라인 경계(64B)에서 시작 */
};
/* 해결 2: per-CPU 데이터 (가장 확실한 해결) */
static DEFINE_PER_CPU(unsigned long, per_cpu_counter);
/* 각 CPU가 자신만의 counter를 가짐 → 공유 없음 */
/* 해결 3: 구조체 전체를 캐시라인 정렬 */
struct aligned_data {
unsigned long hot_field_1;
unsigned long hot_field_2;
} ____cacheline_aligned_in_smp;
/* ____ 4개: 구조체 자체 정렬 */
/* __ 2개: 멤버 정렬 */
/* pahole로 구조체 레이아웃 확인 */
/* pahole -C bad_layout vmlinux */
/* → offset 0: counter (8B), offset 8: flag (8B) = 같은 캐시라인 */
Rmt HITM 비율이 높으면 심각한 false sharing입니다. HITM이 많은 캐시라인의 Shared Data CL 테이블에서 어떤 변수가 같은 라인에 있는지 확인하고, pahole로 구조체 레이아웃을 분석한 뒤 __cacheline_aligned_in_smp 또는 per-CPU 데이터로 분리하세요. 커널 struct net_device, struct sock 등은 이 기법이 이미 적용되어 있습니다.
perf lock
락 경합(contention) 분석 도구입니다. 커널 v5.19+에서는 BPF 기반 contention 서브커맨드로 CONFIG_LOCK_STAT=y 없이도 락 분석이 가능합니다.
# === 전통적 모드 (CONFIG_LOCK_STAT=y 필요) ===
perf lock record -- sleep 10
perf lock report
# → Contention stats by lock instance
# === BPF 기반 contention 분석 (v5.19+, 권장) ===
perf lock contention --use-bpf -a -- sleep 5
# 출력:
# contended total wait max wait avg wait type caller
# 431221 3.58 s 0.28 ms 8.30 us mutex ext4_mb_new_blocks+0x42
# 123456 1.23 s 0.15 ms 9.97 us spin _raw_spin_lock+0x1e
# 콜스택 포함
perf lock contention --use-bpf -a --call-graph fp -- sleep 5
# 락 종류별 필터
perf lock contention --use-bpf -a --type-filter mutex -- sleep 5
perf lock contention --use-bpf -a --type-filter spinlock -- sleep 5
# 특정 프로세스만
perf lock contention --use-bpf -p $(pidof mysql) -- sleep 10
# 상위 N개만 표시
perf lock contention --use-bpf -a --top 10 -- sleep 5
락 경합 해결 전략
| 경합 패턴 | perf lock 출력 | 해결 방법 |
|---|---|---|
| 단일 mutex에 집중 | 하나의 mutex에 contended 90%+ | 락 분할, 락프리 자료구조, per-CPU 데이터 |
| spinlock 장시간 보유 | max wait > 1ms (spinlock) | 크리티컬 섹션 축소, mutex 변환 고려 |
| rwsem 쓰기 경합 | rwsem total wait 높음 | 읽기/쓰기 비율 확인, RCU 적용 검토 |
| futex 경합 | futex wait/wake 빈번 | 사용자 락 그래뉼래리티 조정 |
CONFIG_BPF_SYSCALL=y, CONFIG_BPF_EVENTS=y, CAP_BPF 또는 root 권한이 필요합니다. BPF 없이 perf lock record를 사용하면 CONFIG_LOCK_STAT=y로 커널을 재컴파일해야 하며 오버헤드도 큽니다.
perf trace
perf trace는 strace의 고성능 대안으로, raw_syscalls 트레이스포인트를 사용하여 시스템 콜을 추적합니다. strace의 ptrace 기반 방식보다 오버헤드가 현저히 낮으며, perf 인프라와 통합되어 PMU 이벤트와 시스템 콜을 동시에 분석할 수 있습니다.
# 기본 사용 (strace 유사 출력)
perf trace ./my_program
# 특정 시스템 콜만 추적
perf trace -e read,write,open,close -- ./my_program
# 실행 중 프로세스 추적
perf trace -p $(pidof nginx) -- sleep 10
# 시스템 전체 추적
perf trace -a -- sleep 5
# syscall 통계 요약 (-s 옵션)
perf trace -s -- ./my_program 2>&1 | tail -30
# 출력 예:
# syscall calls errors total min avg max stddev
# read 5678 0 3.21s 12us 565us 45ms 23.4%
# write 3456 0 1.05s 8us 304us 12ms 18.7%
# futex 2345 123 0.89s 1us 379us 8.5ms 45.2%
# epoll_wait 1234 0 0.45s 50us 365us 2.1ms 12.3%
# 페이지 폴트 추적 (major/minor 구분)
perf trace -e major-faults,minor-faults -p $(pidof app) -- sleep 10
# 네트워크 관련 syscall만
perf trace -e 'sendto,recvfrom,connect,accept*,socket,bind,listen' \
-p $(pidof nginx) -- sleep 10
# I/O 관련 syscall + 지연 시간
perf trace --duration 1.0 -e read,write,pread64,pwrite64 \
-p $(pidof mysql) -- sleep 30
# --duration 1.0: 1ms 이상 걸린 syscall만 표시
perf trace vs strace 비교
| 특성 | perf trace | strace |
|---|---|---|
| 메커니즘 | 트레이스포인트 (커널 내) | ptrace 시스템 콜 |
| 오버헤드 | 낮음 (~2-5%) | 높음 (~20-100%) |
| 시스템 전체 | -a로 가능 | 불가 (프로세스별만) |
| 통계 요약 | -s 옵션 | -c 옵션 |
| PMU 통합 | 가능 (이벤트 동시 수집) | 불가 |
| 컨테이너 | cgroup 필터 가능 | nsenter 필요 |
| 프로덕션 | 안전 (저오버헤드) | 위험 (성능 저하 심각) |
perf trace를 사용하세요. --duration 옵션으로 느린 syscall만 필터링하면 출력량도 줄이고 오버헤드도 최소화할 수 있습니다. -s 옵션은 syscall별 통계를 한눈에 보여주어 I/O 병목 진단에 매우 유용합니다.
perf ftrace
perf ftrace는 ftrace의 function tracer를 perf 인터페이스로 사용합니다.
# 함수 트레이싱
perf ftrace -T tcp_sendmsg -- sleep 5
# 함수 그래프 (호출 + 리턴 시간)
perf ftrace --graph -T tcp_sendmsg -- sleep 5
# 특정 PID만
perf ftrace -p $(pidof nginx) -- sleep 10
perf kvm
KVM 가상화(Virtualization) 환경에서 VM Exit 원인과 게스트 성능을 분석합니다.
# VM Exit 분석
perf kvm stat live # 실시간
perf kvm stat record # 기록
perf kvm stat report # 보고
# 게스트 프로파일링 (호스트에서)
perf kvm --guest --host record -a -- sleep 10
perf kvm --guest report
커널 내부 구조
perf 서브시스템의 핵심 커널 자료구조입니다.
/* include/linux/perf_event.h */
struct perf_event {
struct list_head sibling_list;
struct perf_event_context *ctx;
struct pmu *pmu;
struct perf_event_attr attr;
u64 count; /* 현재 카운트 */
u64 total_time_enabled;
u64 total_time_running;
struct perf_buffer *rb; /* ring buffer */
perf_overflow_handler_t overflow_handler;
void *overflow_handler_context;
};
perf.data 파일 구조
perf.data는 perf record가 생성하는 바이너리 파일로, 샘플 데이터와 메타데이터를 함께 포함합니다. 파일 구조를 이해하면 커스텀 분석 도구 개발이나 데이터 변환에 유용합니다.
# perf.data 파일 정보 확인
perf report --header-only
# → 수집 환경, 커널 버전, CPU, 이벤트, 옵션 등
# perf.data 크기 관리
perf record -F 99 -g --switch-output=1G -- sleep 300
# → 1GB마다 파일 분할 (perf.data.2025030112, ...)
# 파이프 모드 (파일 없이 스트리밍)
perf record -o - -F 99 -g -- ./workload | perf report -i -
# → perf.data 생성 없이 바로 분석
# 압축
perf record -z --compression-level 3 -g -- ./workload
# → zstd 압축으로 perf.data 크기 50-80% 감소
# 오프라인 분석을 위한 아카이빙
perf archive perf.data
# → perf.data.tar.bz2 생성 (Build ID 매칭 바이너리 포함)
# → 다른 머신에서 perf report 가능
sideband 이벤트 레코드 유형
| 레코드 타입 | 설명 | 용도 |
|---|---|---|
PERF_RECORD_MMAP2 | 메모리 매핑 정보 | DSO(공유 라이브러리) 매핑 추적, 심볼 해석 |
PERF_RECORD_COMM | 프로세스 이름 변경 | exec() 후 새 프로세스명 추적 |
PERF_RECORD_FORK | 프로세스 fork | 자식 프로세스 추적 |
PERF_RECORD_EXIT | 프로세스 종료 | 프로세스 라이프사이클 |
PERF_RECORD_LOST | 이벤트 손실 알림 | ring buffer 오버플로 감지 |
PERF_RECORD_SAMPLE | 프로파일 샘플 | 핵심 데이터 (IP, callchain 등) |
PERF_RECORD_SWITCH | 컨텍스트 스위치 | on/off-CPU 추적 (v4.3+) |
PERF_RECORD_NAMESPACES | 네임스페이스 정보 | 컨테이너 식별 (v4.11+) |
PERF_RECORD_CGROUP | cgroup 이벤트 | cgroup 기반 필터링 (v5.7+) |
PERF_RECORD_AUX | AUX 버퍼 데이터 | Intel PT, ARM SPE 페이로드 |
--switch-output으로 파일 분할, 2) -z로 zstd 압축, 3) --mmap-pages를 충분히 설정하여 LOST 방지, 4) perf archive로 오프라인 분석용 아카이브 생성. 디스크 공간이 부족하면 perf record -o /dev/null로 pipe 모드 사용을 고려하세요.
샘플링 메커니즘
perf 샘플링은 PMU 카운터 오버플로 시 CPU가 PMI(Performance Monitor Interrupt)를 발생시켜 동작합니다. x86에서는 NMI로 전달되어 정확도가 높습니다.
| 기술 | 아키텍처 | 정밀도 | 설명 |
|---|---|---|---|
| PEBS | Intel | IP 정확 | 하드웨어가 샘플을 PEBS 버퍼(Buffer)에 기록, 스키드(skid) 최소화 |
| IBS | AMD | IP + 주소 정확 | 명령어/연산 기반 정밀 샘플링, 메모리 주소 포함 |
| SPE | ARM | IP + 주소 + 레이턴시 | Statistical Profiling Extension, 하드웨어 필터링 |
| 일반 PMI | 모든 | 스키드 있음 | 인터럽트 시점의 IP ≠ 이벤트 발생 IP (수~수십 명령 차이) |
# PEBS 사용 (Intel, :pp 접미사 = precise)
perf record -e cycles:pp -c 100000 -- ./workload
# IBS 사용 (AMD)
perf record -e ibs_op// -c 100000 -- ./workload
# SPE 사용 (ARM)
perf record -e arm_spe_0/ts_enable=1,pa_enable=1/ -- ./workload
PEBS 페이로드(Payload) 구조 분석
Intel PEBS(Processor Event-Based Sampling)는 PMI 발생 시 하드웨어가 직접 샘플 레코드를 PEBS 버퍼에 기록하여, 인터럽트 핸들러(Handler)의 지연(스키드)을 제거합니다. Ice Lake 이후의 Adaptive PEBS는 필요한 필드만 선택적으로 기록하여 오버헤드를 더욱 줄입니다.
PEBS 레코드 필드
| 필드 | 오프셋(Offset) | 크기 | 설명 |
|---|---|---|---|
| GP Registers | 0x00 | 128B | RAX-R15, RFLAGS — 이벤트 시점 레지스터(Register) 상태 |
| Eventing IP | 0x80 | 8B | 이벤트 발생 정확한 명령어 주소 (스키드 없음) |
| Data Linear Address | 0x98 | 8B | 메모리 로드/스토어 대상 가상 주소(Virtual Address) |
| Data Source | 0xA0 | 8B | 데이터가 어디서 왔는지 (L1/L2/L3/DRAM/Remote) |
| Latency Value | 0xA8 | 4B | 메모리 접근 지연 시간 (코어 사이클 단위) |
| TSC | 0xB0 | 8B | 타임스탬프 카운터 (Adaptive PEBS) |
# PEBS로 메모리 레이턴시 프로파일링
# ldlat-loads: 지정한 지연(사이클) 이상의 로드만 샘플링
perf mem record -e ldlat-loads --ldlat=30 -- ./workload
perf mem report --sort mem,sym,dso --stdio
# 데이터 소스 분포 확인
perf mem report --sort mem
# → L1 hit: 85%, L2 hit: 8%, L3 hit: 5%, DRAM: 2%
# PEBS precise 레벨 확인
perf record -e cycles:ppp -c 100000 -- ./workload
# :p = precise level 1 (skid 감소)
# :pp = precise level 2 (PEBS 사용, zero skid)
# :ppp = precise level 3 (최대 정밀도, Adaptive PEBS)
/* Adaptive PEBS 설정 (커널 내부, arch/x86/events/intel/ds.c) */
/* PEBS_DATA_CFG MSR 비트 필드 */
#define PEBS_DATACFG_GP BIT_ULL(0) /* GP 레지스터 기록 */
#define PEBS_DATACFG_MEMINFO BIT_ULL(1) /* Data LA + Latency + Source */
#define PEBS_DATACFG_GP2 BIT_ULL(2) /* XMM 레지스터 */
#define PEBS_DATACFG_LBRS BIT_ULL(3) /* LBR 엔트리 포함 */
/* 필요한 필드만 선택 → PEBS 레코드 크기 최소화 */
static void adaptive_pebs_record_size_update(void)
{
u64 pebs_data_cfg = 0;
if (needs_gp_regs) pebs_data_cfg |= PEBS_DATACFG_GP;
if (needs_mem_info) pebs_data_cfg |= PEBS_DATACFG_MEMINFO;
wrmsrl(MSR_PEBS_DATA_CFG, pebs_data_cfg);
}
AMD IBS
AMD IBS(Instruction-Based Sampling)는 PEBS와 달리 명령어 파이프라인의 특정 단계에서 샘플을 수집합니다. IBS Fetch와 IBS Op 두 가지 모드가 있으며, 각각 명령어 페치 단계와 실행(Op) 단계에서 서로 다른 정보를 제공합니다.
IBS Fetch vs IBS Op
| 속성 | IBS Fetch | IBS Op |
|---|---|---|
| 샘플 지점 | 명령어 페치 단계 | 명령어 실행 완료 단계 |
| MSR | IBS_FETCH_CTL (0xC0011030) | IBS_OP_CTL (0xC0011033) |
| 주요 정보 | I-cache 미스, ITLB 미스, 페치 지연 | Data cache, DTLB, 분기 예측, 지연 |
| 메모리 주소 | 명령어 주소만 | 데이터 가상/물리 주소(Physical Address) 포함 |
| Data Fabric | 해당 없음 | NorthBridge 이벤트, 원격 접근 정보 |
| perf 이벤트 | ibs_fetch// | ibs_op// |
# IBS Fetch: 명령어 페치 프로파일링
perf record -e ibs_fetch// -c 100000 -- ./workload
perf report --sort sym
# IBS Op: 실행 단계 프로파일링 (데이터 접근 포함)
perf record -e ibs_op// -c 100000 -- ./workload
perf report --sort sym
# IBS로 메모리 프로파일링 (perf mem 통합)
perf mem record -- ./workload # AMD에서 자동으로 IBS 사용
perf mem report --sort mem,sym
# IBS 이벤트 상세 (raw dump)
perf record -e ibs_op// -c 50000 -- ./workload
perf script -F ip,sym,dso,addr,event
/* IBS MSR 레이아웃 (arch/x86/events/amd/ibs.c) */
/* IBS Fetch Control (MSR 0xC0011030) */
/* [15:0] IbsFetchMaxCnt — 샘플링 주기 */
/* [16] IbsFetchCntEn — 카운터 활성화 */
/* [17] IbsFetchVal — 유효 샘플 표시 */
/* [49:32] IbsFetchLat — 페치 지연(사이클) */
/* [50] IbsFetchPhyAddrValid */
/* [51] IbsIcMiss — I-cache 미스 */
/* [52] IbsL1TlbMiss — L1 ITLB 미스 */
/* [53] IbsL2TlbMiss — L2 ITLB 미스 */
/* IBS Op Data (MSR 0xC0011035) */
/* [0] IbsLdOp — 로드 연산 */
/* [1] IbsStOp — 스토어 연산 */
/* [2] IbsDcMiss — D-cache 미스 */
/* [3] IbsDcMissAcc — 미스 접근 타입 */
/* [4] IbsDcL2Tlb — L2 DTLB 미스 */
/* [5] IbsNbIbsSrcBit — NorthBridge/Data Fabric 소스 */
/* [15:6] Reserved */
/* [31:16] IbsDcMissLat — D-cache 미스 지연(사이클) */
# AMD uProf vs perf IBS 비교
# uProf: GUI 기반, IBS 전용 분석 뷰, 소스 어노테이션
# perf: CLI 기반, 범용 프레임워크, Flamegraph/스크립트 통합
# 둘 다 동일한 IBS 하드웨어를 사용하므로 정밀도는 동일
# Zen4+: IBS 확장 — 향상된 Data Fabric 이벤트, 더 많은 필드
ARM64 SPE
ARM SPE(Statistical Profiling Extension)는 ARMv8.2에서 도입된 하드웨어 기반 정밀 프로파일링 기술입니다. 파이프라인을 통과하는 연산을 하드웨어가 직접 필터링하고 기록하므로, PMI 기반 샘플링과 달리 스키드가 없고 메모리 접근 주소, 지연 시간, 데이터 소스까지 정확하게 제공합니다.
SPE 필터 설정
| 필터 | perf 파라미터 | 설명 |
|---|---|---|
| 로드 필터 | load_filter=1 | 메모리 로드 연산만 기록 |
| 스토어 필터 | store_filter=1 | 메모리 스토어 연산만 기록 |
| 분기 필터 | branch_filter=1 | 분기 명령어만 기록 |
| 최소 지연 | min_latency=N | N 사이클 이상 지연된 이벤트만 기록 |
| 타임스탬프 | ts_enable=1 | 타임스탬프 포함 (시간 기반 분석) |
| 물리 주소 | pa_enable=1 | 물리 주소 포함 (NUMA 분석) |
# SPE 메모리 로드 프로파일링 (30 사이클 이상 지연)
perf record -e arm_spe_0/load_filter=1,min_latency=30,ts_enable=1,pa_enable=1/ \
-c 1000 -- ./workload
perf report --sort sym,dso --stdio
# SPE 데이터 소스 분석 (perf mem 통합)
perf mem record -e arm_spe_0// -- ./workload
perf mem report --sort mem,sym
# SPE 브랜치 프로파일링
perf record -e arm_spe_0/branch_filter=1/ -c 5000 -- ./workload
perf report --sort sym --stdio
# SPE vs PMI 정밀도 비교
# PMI 방식 (기존): IP ≠ 이벤트 발생 위치 (스키드 ~수십 명령)
perf record -e cycles -c 100000 -- ./workload
perf annotate --symbol=hot_function # 샘플이 실제 핫 명령어에서 벗어남
# SPE 방식: IP = 정확한 이벤트 발생 위치 (zero skid)
perf record -e arm_spe_0// -c 1000 -- ./workload
perf annotate --symbol=hot_function # 샘플이 정확한 명령어에 위치
# SPE 하드웨어 지원 확인
ls /sys/bus/event_source/devices/arm_spe_0/
# → 존재하면 SPE 지원, ARMv8.2+ 필요
# Cortex-A77, Cortex-A78, Neoverse N1/V1/V2 등 지원
min_latency 필터를 활용하면 캐시 미스 이벤트만 선택적으로 수집할 수 있습니다.
이벤트 멀티플렉싱
PMU 카운터 수는 제한적(보통 4-8개)이므로, 요청 이벤트가 카운터 수를 초과하면 커널이 시분할 멀티플렉싱으로 각 이벤트를 번갈아 측정합니다.
# 많은 이벤트 동시 요청 시 멀티플렉싱 발생
perf stat -e cycles,instructions,cache-misses,branch-misses,\
L1-dcache-load-misses,L1-icache-load-misses,\
dTLB-load-misses,iTLB-load-misses,\
LLC-load-misses,LLC-store-misses \
-- ./workload
# 출력에서 (xx.xx%) 표시 = 멀티플렉싱된 비율
# 3,248,521,387 cycles (66.67%) ← 측정 시간의 66%만 활성
# 커널이 스케일링 보정: 실제값 = 측정값 × (enabled / running)
perf stat -e '{cycles,instructions}'처럼 {}로 묶으면 같은 시간에 동시 측정됩니다. 그룹 이벤트 수가 카운터 수를 초과하면 전체 그룹이 함께 시분할됩니다.
cgroup 기반 프로파일링
perf는 cgroup 단위로 이벤트를 필터링할 수 있습니다. 컨테이너(Container) 환경에서 특정 서비스만 프로파일링할 때 유용합니다.
# cgroup으로 필터링 (Docker 컨테이너)
CGROUP_PATH=$(cat /proc/$(docker inspect --format '{{.State.Pid}}' mycontainer)/cgroup | grep perf_event | cut -d: -f3)
perf stat -a -G $CGROUP_PATH -e cycles,instructions -- sleep 10
# systemd 서비스 기반
perf record -a -G system.slice/nginx.service -g -- sleep 30
# perf_event cgroup 컨트롤러 필요
mount -t cgroup -o perf_event perf_event /sys/fs/cgroup/perf_event
보안 설정
perf는 권한이 없으면 커널 이벤트에 접근할 수 없습니다. perf_event_paranoid sysctl이 접근 수준을 제어합니다.
| 값 | 의미 |
|---|---|
| -1 | 모든 사용자 전체 접근 (개발 환경) |
| 0 | 커널 프로파일링 허용 (CAP_PERFMON 불필요) |
| 1 | 커널 프로파일링 제한 (기본값, 일부 배포판) |
| 2 | 커널 접근 불가, 유저스페이스만 (기본값, 일부 배포판) |
| 3 | perf_event_open() 완전 차단 (보안 강화) |
# 현재 설정 확인
cat /proc/sys/kernel/perf_event_paranoid
# 임시 완화 (재부팅 시 초기화)
echo -1 > /proc/sys/kernel/perf_event_paranoid
# 영구 설정
echo 'kernel.perf_event_paranoid = -1' >> /etc/sysctl.d/99-perf.conf
# CAP_PERFMON 으로 특정 사용자에게만 허용 (v5.8+)
setcap cap_perfmon+ep /usr/bin/perf
perf_event_paranoid=2 이상을 기본값으로 설정합니다.
실전 튜닝 가이드
워크로드 유형별 perf 분석 전략입니다.
# TMA Top-Down 분석 (Intel, perf v5.13+)
perf stat --topdown -- ./workload
# pmu-tools (Andi Kleen)
toplev --single-thread -l3 -- ./workload
# 워크로드별 분석 전략:
# CPU 바운드: perf stat (IPC), perf record -e cycles, flamegraph
# 메모리 바운드: perf mem, perf c2c, perf stat -e LLC-load-misses
# I/O 바운드: perf trace (syscall), perf record -e block:*
# 락 바운드: perf lock contention, perf record -e sched:*
Intel TMA
TMA(Top-Down Microarchitecture Analysis)는 Intel이 제안한 체계적 성능 병목 분석 방법론입니다. CPU 파이프라인 슬롯을 4가지 카테고리로 분류하고, Level 1에서 Level 4까지 점진적으로 병목 원인을 좁혀갑니다.
TMA Level 분해
| Level | 카테고리 | 의미 | 대표 원인 |
|---|---|---|---|
| L1 | Frontend Bound | 명령어 공급 부족 | I-cache 미스, 분기 예측기 워밍업 |
| Backend Bound | 실행 유닛/메모리 정체 | 캐시 미스, 실행 포트 포화 | |
| L1 | Bad Speculation | 잘못된 추측 실행 | 분기 예측 실패, 머신 클리어 |
| Retiring | 유용한 작업 (목표: 높을수록 좋음) | 정상 실행, 마이크로코드 시퀀스 | |
| L2 | Fetch Latency / Bandwidth | 프론트엔드 세분화 | ITLB 미스, DSB/MITE 전환 |
| Memory / Core Bound | 백엔드 세분화 | L1/L2/L3/DRAM 미스, 포트 경합 | |
| L3 | L1/L2/L3/DRAM/Store Bound | 메모리 계층별 병목 | 각 캐시 레벨의 미스율 |
# perf stat --topdown (Level 1, 커널 v5.13+)
perf stat --topdown -a -- ./workload
# 출력 예:
# retiring bad spec frontend backend
# 38.2% 12.1% 15.3% 34.4% → Backend Bound가 주 병목
# toplev.py (pmu-tools, Level 3까지)
# 설치: pip install pmu-tools 또는 git clone
toplev --single-thread -l3 -- ./workload
# → Level 3에서 "Memory_Bound.L3_Bound: 22.1%" 등 세부 원인 식별
# toplev Level 4 (가장 세부적, 많은 카운터 필요)
toplev -l4 --no-multiplex --core C0 -- taskset -c 0 ./workload
# TMA 메트릭 기반 튜닝 전략
# Backend Bound > Memory Bound > DRAM Bound가 높으면:
perf stat -e '{cycles,mem_load_retired.l3_miss,mem_load_retired.fb_hit}' \
-- ./workload
# → 데이터 프리페칭, 캐시 친화 자료구조, NUMA 최적화 고려
# Frontend Bound > Fetch Latency > ITLB_Misses가 높으면:
perf stat -e '{cycles,frontend_retired.itlb_miss,frontend_retired.l1i_miss}' \
-- ./workload
# → 코드 크기 축소, -Os 최적화, PGO(Profile-Guided Optimization)
# Bad Speculation > Branch_Mispredicts가 높으면:
perf stat -e '{cycles,br_misp_retired.all_branches,br_inst_retired.all_branches}' \
-- ./workload
# → likely/unlikely 힌트, 분기 제거(branchless), 프로파일 기반 최적화
perf stat --topdown으로 Level 1 확인, 2) 가장 높은 카테고리 방향으로 toplev -l3 드릴다운, 3) Level 3에서 특정 원인 식별, 4) 해당 이벤트로 perf record 프로파일링, 5) perf annotate로 핫 명령어 확인.
AMD uProf의 Pipeline Utilization 뷰를 사용하세요. ARM에서는 ARM Topdown 메트릭 그룹(perf stat -M TopdownL1, ARMv9+)이 제한적으로 지원됩니다.
Off-CPU 분석
Off-CPU 분석은 프로세스(Process)가 CPU를 사용하지 않는 시간(블로킹, 슬립(Sleep), I/O 대기 등)을 프로파일링합니다. On-CPU 분석만으로는 I/O 바운드나 락 대기 같은 병목을 발견할 수 없으므로, 두 분석을 결합하면 전체 실행 시간의 병목을 빠짐없이 파악할 수 있습니다.
perf sched를 이용한 Off-CPU 분석
# 스케줄러 이벤트 기록 (Off-CPU 포함)
perf sched record -a -- sleep 30
# 타임히스트: 각 스케줄링 이벤트의 타임라인
perf sched timehist
# 출력: 타임스탬프, task, runtime(on-cpu), 대기시간(off-cpu)
# time task wait time sch delay run time
# 1000.100 nginx:1234 12.345 ms 0.023 ms 1.234 ms
# wakeup chain: 누가 깨웠는지 추적
perf sched timehist -w
# → waker: ksoftirqd/0 → wakee: nginx:1234
# I/O 완료 인터럽트가 nginx를 깨운 것을 확인
eBPF 기반 Off-CPU 프로파일링
# bcc offcputime: 블로킹 스택 + 대기 시간 집계
# (bcc-tools 또는 bpfcc-tools 패키지 설치 필요)
offcputime-bpfcc -df -p $(pidof myapp) 30 > offcpu.folded
# Off-CPU Flamegraph 생성
flamegraph.pl --color=io --title="Off-CPU" \
--countname=us < offcpu.folded > offcpu_flame.svg
# bpftrace로 직접 Off-CPU 수집
bpftrace -e '
kprobe:finish_task_switch {
@start[tid] = nsecs;
}
kretprobe:finish_task_switch /@start[tid]/ {
@offcpu[kstack, comm] = sum(nsecs - @start[tid]);
delete(@start[tid]);
}
END { print(@offcpu); }
' -c "./workload"
# On-CPU + Off-CPU 결합 Flamegraph
perf script | stackcollapse-perf.pl > oncpu.folded
cat oncpu.folded offcpu.folded | flamegraph.pl \
--title="On+Off CPU" > combined.svg
| 분석 방법 | 장점 | 단점 |
|---|---|---|
perf sched timehist | 커널 빌트인, 추가 도구 불필요 | 오버헤드 큼, 스택 트레이스 제한 |
offcputime (bcc) | 커널 내 집계, 낮은 오버헤드 | bcc/bpf 필요, 커널 v4.6+ |
bpftrace 스크립트 | 유연한 커스터마이징 | 스크립팅 지식 필요 |
perf sched record는 모든 스케줄링 이벤트를 기록하므로 오버헤드가 큽니다. 프로덕션 환경에서는 BPF 기반 (offcputime)을 사용하세요. BPF는 커널 내에서 스택을 집계하므로 데이터 전송량이 훨씬 적습니다.
NUMA 인지 프로파일링
NUMA(Non-Uniform Memory Access) 시스템에서는 메모리 접근이 로컬 노드인지 원격 노드인지에 따라 지연 시간이 크게 달라집니다. perf는 NUMA 관련 이벤트와 분석 도구를 제공하여 메모리 배치 최적화를 지원합니다.
NUMA 관련 이벤트 측정
# NUMA 관련 하드웨어 이벤트 측정
perf stat -e node-loads,node-stores,\
node-load-misses,node-store-misses \
-- ./workload
# 출력 해석:
# 1,234,567 node-loads (로컬 노드 로드)
# 567,890 node-load-misses (원격 노드 로드 = NUMA miss)
# → miss 비율 = 567890 / (1234567+567890) = 31.5% (높으면 문제)
# Intel uncore IMC (메모리 컨트롤러) 대역폭
perf stat -e uncore_imc/cas_count_read/,\
uncore_imc/cas_count_write/ \
-a -- sleep 10
# NUMA 노드별 메모리 대역폭 확인
numastat -p $(pidof myapp)
perf c2c로 NUMA false sharing 분석
# cache-to-cache 전송 분석 (NUMA 노드 포함)
perf c2c record -a -- ./workload
perf c2c report --node --stdio
# 출력에서 주의할 지표:
# Rmt HITM: 원격 NUMA 노드 간 캐시라인 전송 (가장 느림)
# Lcl HITM: 로컬 소켓 내 코어 간 전송 (상대적으로 빠름)
# Rmt HITM 비율이 높으면 NUMA-aware 데이터 배치 필요
# numactl + perf 결합: NUMA 정책 변경 후 비교
# 인터리브 모드 (NUMA 밸런싱)
numactl --interleave=all perf stat -e node-load-misses -- ./workload
# 특정 노드에 바인딩
numactl --cpunodebind=0 --membind=0 \
perf stat -e node-load-misses -- ./workload
# perf bench numa: NUMA 밸런싱 벤치마크
perf bench numa mem -p 4 -t 4 -G 0 -P 1024 -T 0 -l 3 -c
| 지표 | 의미 | 개선 방법 |
|---|---|---|
node-load-misses 높음 | 원격 NUMA 메모리 접근 빈번 | numactl --membind로 메모리 로컬화 |
Rmt HITM 높음 | 원격 노드 간 캐시라인 핑퐁 | 데이터 분리, per-node 할당 |
Lcl HITM 높음 | 같은 소켓(Socket) 내 false sharing | __cacheline_aligned 패딩 |
| 비대칭 메모리 사용 | 특정 노드만 메모리 과다 사용 | numactl --interleave 또는 mbind() |
numactl --hardware로 노드 수, CPU-노드 매핑(Mapping), 노드 간 거리를 확인하세요. lstopo(hwloc)로 시각적 토폴로지 다이어그램도 유용합니다. 2-소켓 서버에서 원격 노드 접근은 로컬 대비 1.5-3배 느릴 수 있습니다.
ARM64 PMU 특성
ARM64는 PMUv3 아키텍처를 사용하며, ARMv8.2에서 추가된 SPE(Statistical Profiling Extension)가 PEBS와 유사한 정밀 샘플링을 제공합니다.
# ARM PMU 이벤트 (raw 코드 사용)
perf stat -e armv8_pmuv3/event=0x04/ # L1D_CACHE (L1 데이터 캐시 접근)
perf stat -e armv8_pmuv3/event=0x03/ # L1D_CACHE_REFILL (L1D 미스)
# SPE (Statistical Profiling Extension)
perf record -e arm_spe_0/ts_enable=1,pa_enable=1,load_filter=1/ \
-c 1000 -- ./workload
perf report --sort symbol
# TRBE (Trace Buffer Extension) — CoreSight 연동
perf record -e cs_etm/@tmc_etr0/ -- ./workload
perf와 eBPF 연동
perf와 eBPF는 모두 perf_event 인프라를 공유하며, 결합하면 커널 내에서 이벤트를 필터링/집계할 수 있습니다. BPF 프로그램이 커널에서 데이터를 처리하므로 유저스페이스 전송 오버헤드가 극적으로 줄어들어, 프로덕션 환경에서도 안전하게 사용 가능합니다.
# === BPF 프로그램 프로파일링 ===
perf record -e cycles -g -- bpftool prog run id 42
# === BPF 출력 이벤트와 perf 통합 ===
perf record -e bpf-output/no-inherit/ -a -- sleep 10
# === BPF 기반 perf lock contention (CONFIG_LOCK_STAT 불필요) ===
perf lock contention --use-bpf -a -- sleep 5
# → BPF가 커널에서 lock_acquire/lock_release를 추적하고 집계
# → CONFIG_LOCK_STAT=y 없이도 락 경합 분석 가능
# === bpf_perf_event_output() 활용 ===
# BPF 프로그램이 perf 이벤트를 유저스페이스로 전달하는 핵심 함수
# 커널 v4.4+에서 사용 가능
/* BPF 프로그램에서 perf 이벤트 출력 예제 */
struct {
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
__uint(key_size, sizeof(int));
__uint(value_size, sizeof(int));
} events SEC(".maps");
struct event_data {
u32 pid;
u64 timestamp;
char comm[16];
u64 latency_ns;
};
SEC("perf_event")
int on_perf_event(struct bpf_perf_event_data *ctx)
{
struct event_data data = {};
data.pid = bpf_get_current_pid_tgid() >> 32;
data.timestamp = bpf_ktime_get_ns();
bpf_get_current_comm(&data.comm, sizeof(data.comm));
/* 필터링: 특정 조건만 유저스페이스로 전달 */
if (data.latency_ns > 1000000) { /* 1ms 이상만 */
bpf_perf_event_output(ctx, &events,
BPF_F_CURRENT_CPU, &data, sizeof(data));
}
return 0;
}
/* perf_event 기반 BPF 프로그램 연결 (유저스페이스) */
int perf_fd = perf_event_open(&attr, -1, cpu, -1, 0);
ioctl(perf_fd, PERF_EVENT_IOC_SET_BPF, bpf_prog_fd);
ioctl(perf_fd, PERF_EVENT_IOC_ENABLE, 0);
perf vs bpftrace vs bcc 비교
| 특성 | perf | bpftrace | bcc (Python) |
|---|---|---|---|
| 데이터 처리 | 수집 후 오프라인 분석 | 실시간 커널 내 집계 | 실시간 커널 내 집계 |
| 인터페이스 | CLI 도구 + perf.data | 스크립팅 언어 (awk 유사) | Python + C BPF |
| 프로덕션 안전성 | 높음 (샘플링 오버헤드만) | 높음 (verifier 검증) | 높음 (verifier 검증) |
| 오프라인 분석 | 강력 (report/annotate/diff) | 제한적 | 제한적 |
| 커스텀 집계 | perf script Python 스크립트 | @맵 문법으로 간결 | BPF 맵 직접 조작 |
| 장점 | 풍부한 보고서, Flamegraph | 원라이너 분석 | 복잡한 로직 구현 |
| 커널 요구 | v2.6.31+ | v4.9+ (BTF: v5.2+) | v4.1+ |
perf stat으로 전체 지표 파악 → 2) perf record -g로 핫스팟 식별 → 3) bpftrace로 특정 경로 실시간 추적 → 4) perf lock contention --use-bpf로 락 분석 → 5) 커스텀 BPF 프로그램으로 프로덕션 모니터링. 세 도구는 모두 perf_event 인프라를 공유하므로 상호 보완적으로 활용하세요.
고급 이벤트 필터링
perf는 다양한 수준의 이벤트 필터링을 지원합니다. 트레이스포인트 필터, BPF 필터, 주소 필터, 그리고 이벤트 수정자를 조합하여 필요한 데이터만 정밀하게 수집할 수 있습니다.
트레이스포인트 필터 (--filter)
트레이스포인트 이벤트에 커널 측 필터를 적용하여, 조건을 만족하는 이벤트만 ring buffer에 기록합니다. 필터는 커널 내에서 평가되므로 유저스페이스 전송 오버헤드가 줄어듭니다.
# 트레이스포인트 필터: field op value 문법
# 사용 가능 필드 확인
cat /sys/kernel/tracing/events/sched/sched_switch/format
# 특정 프로세스의 스케줄링 이벤트만 기록
perf record -e sched:sched_switch \
--filter 'next_comm == "nginx" || prev_comm == "nginx"' \
-a -- sleep 10
# 블록 I/O: 4KB 이상 요청만
perf record -e block:block_rq_issue \
--filter 'bytes >= 4096' -a -- sleep 5
# 복합 조건: AND/OR 조합
perf record -e syscalls:sys_enter_write \
--filter 'fd > 2 && count > 1024' \
-p $(pidof myapp) -- sleep 10
# 비교 연산자: ==, !=, >, <, >=, <=
# 문자열: ==, != (정확 일치), ~ (glob 패턴)
# 논리: &&, ||, !
주소 필터 (--addr-filter)
Intel PT나 ARM CoreSight 같은 하드웨어 트레이서에서 특정 주소 범위만 추적하도록 필터를 설정합니다. 바이너리의 특정 함수만 트레이싱하면 버퍼 사용량과 디코딩 시간을 크게 줄일 수 있습니다.
# Intel PT: 특정 바이너리의 주소 범위만 트레이싱
perf record -e intel_pt// \
--addr-filter 'filter 0x400000/0x10000@/usr/bin/myapp' \
-- ./myapp
# 함수 이름으로 필터 (심볼 필요)
perf record -e intel_pt// \
--addr-filter 'filter hot_function@/usr/bin/myapp' \
-- ./myapp
# 여러 범위 조합
perf record -e intel_pt// \
--addr-filter 'filter func_a@/usr/bin/app,filter func_b@/usr/bin/app' \
-- ./app
이벤트 수정자 (:u :k :H :G :p)
이벤트 이름 뒤에 수정자(modifier)를 붙여 카운팅/샘플링 범위를 제한합니다.
| 수정자 | 의미 | 예시 |
|---|---|---|
:u | 유저스페이스만 | cycles:u |
:k | 커널만 | cycles:k |
:H | 하이퍼바이저(Hypervisor) | cycles:H |
:G | 게스트 VM | cycles:G |
:p | 정밀(precise) level 1 | cycles:p (IP 스키드 감소) |
:pp | 정밀 level 2 (PEBS/IBS) | cycles:pp (제로 스키드) |
:ppp | 정밀 level 3 (최대 정밀) | cycles:ppp |
:P | 부분(partial) — 그룹의 일부만 스케줄 가능 | {cycles:P,instructions} |
:S | 샘플 읽기 (perf stat에서) | instructions:S |
:D | 핀 고정(pinned) 해제 | cycles:D |
cycles:uk는 유저+커널 모두(하이퍼바이저 제외), cache-misses:kpp는 커널 전용 + PEBS 정밀 샘플링입니다. 수정자를 생략하면 기본적으로 :ukH (유저+커널+하이퍼바이저)입니다.
perf inject / buildid
perf inject는 perf.data 파일을 후처리하여 추가 정보(JIT 심볼, Build ID, Intel PT 디코딩 등)를 삽입합니다. 특히 JIT 컴파일러를 사용하는 런타임(Java, Node.js, V8)에서 심볼 해석에 필수적입니다.
JIT 심볼 주입
# Java/Node.js JIT 심볼 주입 파이프라인
# 1. JIT dump 파일 생성 (Java: -XX:+PreserveFramePointer)
perf record -g -k 1 -- java -XX:+PreserveFramePointer -jar app.jar
# 2. jitdump → perf 심볼 변환
perf inject -j -i perf.data -o perf.jit.data
# 3. JIT 심볼이 포함된 보고서
perf report -i perf.jit.data
# → [JIT] app.jar::com.example.HotMethod.process()
# Node.js: --perf-basic-prof 또는 --perf-prof 플래그
perf record -g -k 1 -- node --perf-prof app.js
perf inject -j -i perf.data -o perf.jit.data
Build ID 관리
# perf.data에 포함된 Build ID 목록
perf buildid-list -i perf.data
# Build ID 캐시에 바이너리 추가
perf buildid-cache --add /usr/lib/debug/lib/modules/$(uname -r)/vmlinux
# Build ID 캐시 위치
ls ~/.debug/.build-id/
# perf inject로 Build ID 삽입
perf inject -b -i perf.data -o perf.buildid.data
# debuginfod 서버 연동 (fedora, ubuntu 등)
export DEBUGINFOD_URLS="https://debuginfod.elfutils.org/"
perf report # 자동으로 debuginfo 다운로드
DEBUGINFOD_URLS 환경변수를 설정하면 perf가 Build ID 기반으로 원격 서버에서 debuginfo를 자동 다운로드합니다. Fedora, Ubuntu, Arch Linux 등 주요 배포판이 공식 debuginfod 서버를 운영합니다. ~/.cache/debuginfod_client/에 캐시됩니다.
멀티플렉싱 최적화
PMU 하드웨어 카운터 수는 제한적이므로, 동시에 측정할 이벤트 수가 카운터를 초과하면 커널이 시분할 멀티플렉싱을 수행합니다. 적절한 그룹 전략으로 측정 정확도를 극대화할 수 있습니다.
pinned vs flexible 그룹
커널은 이벤트를 pinned 그룹과 flexible 그룹으로 구분합니다. pinned 그룹은 항상 카운터를 점유하며, flexible 그룹은 남은 카운터를 공유합니다.
| 속성 | pinned 그룹 | flexible 그룹 |
|---|---|---|
| 스케줄링 | 항상 활성 (카운터 부족 시 에러) | 시분할 멀티플렉싱 |
| 용도 | 핵심 지표 (cycles, instructions) | 보조 지표 |
| 설정 | attr.pinned = 1 | 기본값 |
| 정확도 | 100% 시간 측정 | 스케일링 보정 필요 |
# 핵심 이벤트를 pinned 그룹으로 (항상 동시 측정)
perf stat -e '{cycles,instructions}' \
-e cache-misses,branch-misses,L1-dcache-load-misses \
./workload
# → {cycles,instructions}는 항상 동시 측정
# → 나머지는 flexible로 멀티플렉싱
# 카운터 가용성 확인 (-v: verbose)
perf stat -v -e cycles,instructions ./workload 2>&1 | grep -i "counter"
# → 실제 할당된 카운터와 멀티플렉싱 여부 확인
# :P 수정자: 그룹의 일부만 스케줄 허용
# 일반 그룹은 전체가 함께 on/off되지만, :P는 부분 스케줄 허용
perf stat -e '{cycles:P,instructions,cache-misses,branch-misses}' \
./workload
{cycles,instructions}를 항상 첫 번째 pinned 그룹으로 묶으세요. 나머지 이벤트는 연관성 기준으로 그룹화하여 비율 계산의 정확도를 높입니다. 예: {L1-dcache-loads,L1-dcache-load-misses}를 묶으면 미스율 계산이 동시 측정으로 정확해집니다.
(xx.xx%) 표시가 50% 미만이면 스케일링 오차가 커집니다. 측정 시간을 늘리거나 (-- sleep 30), 이벤트 수를 줄이거나, 여러 번 나누어 측정하세요. 특히 짧은 워크로드(<1초)에서 멀티플렉싱은 심각한 오차를 유발합니다.
CI/CD 자동화
perf의 JSON 출력과 스크립팅 기능을 활용하면 CI/CD 파이프라인에서 성능 회귀를 자동으로 감지할 수 있습니다. 커밋별 성능 변화를 추적하고 임계값 초과 시 알림을 보내는 시스템을 구축합니다.
perf stat JSON 출력 (v6.0+)
# JSON 포맷 출력
perf stat --json -e cycles,instructions,cache-misses \
-- ./workload 2> perf_result.json
# JSON 출력 예시
# {"counter-value": "3248521387", "unit": "", "event": "cycles",
# "event-runtime": 1000234567, "pcnt-running": 100.00,
# "metric-value": "3.25", "metric-unit": "GHz"}
# CSV 포맷 (이전 커널 호환)
perf stat -x, -e cycles,instructions -- ./workload 2> perf.csv
성능 회귀 감지 스크립트
#!/bin/bash
# perf-regression-check.sh: baseline 대비 성능 회귀 감지
THRESHOLD=5 # 5% 이상 변화 시 경고
BASELINE="baseline_perf.json"
CURRENT="current_perf.json"
# 현재 성능 측정 (3회 반복 평균)
perf stat -r 3 --json \
-e cycles,instructions,cache-misses,branch-misses \
-- ./benchmark 2> "$CURRENT"
# IPC 비교 (jq로 파싱)
BASE_IPC=$(jq -r 'select(.event=="instructions") | .["counter-value"]' "$BASELINE")
CURR_IPC=$(jq -r 'select(.event=="instructions") | .["counter-value"]' "$CURRENT")
CHANGE=$(echo "scale=2; ($CURR_IPC - $BASE_IPC) * 100 / $BASE_IPC" | bc)
if (( $(echo "$CHANGE < -$THRESHOLD" | bc -l) )); then
echo "REGRESSION: IPC dropped ${CHANGE}%"
exit 1
fi
echo "PASS: IPC change ${CHANGE}%"
GitHub Actions 통합 예제
# .github/workflows/perf-check.yml
name: Performance Regression Check
on: [push, pull_request]
jobs:
perf-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install perf
run: |
sudo apt-get update
sudo apt-get install -y linux-tools-common linux-tools-generic
sudo sysctl -w kernel.perf_event_paranoid=-1
- name: Build
run: make -j$(nproc) benchmark
- name: Run perf benchmark
run: |
perf stat -r 3 --json \
-e cycles,instructions,cache-misses \
-- ./benchmark 2> current_perf.json
- name: Compare with baseline
run: ./scripts/perf-regression-check.sh
- name: Archive results
uses: actions/upload-artifact@v4
with:
name: perf-results
path: current_perf.json
perf bench 벤치마크 스위트
# perf 내장 벤치마크 스위트
perf bench sched messaging # 스케줄러 메시징 (pipe/socket)
perf bench sched pipe # 파이프 컨텍스트 스위치
perf bench mem memcpy # 메모리 복사 대역폭
perf bench mem memset # 메모리 설정 대역폭
perf bench futex hash # futex 해시 테이블
perf bench numa mem # NUMA 메모리 접근
# 전체 벤치마크 실행 + 결과 저장
perf bench all 2>&1 | tee bench_$(date +%Y%m%d).txt
# CI에서 반복 실행하여 추세 모니터링
for i in $(seq 1 10); do
perf bench sched pipe 2>&1 | grep ops/sec
done | awk '{sum+=$1; n++} END {printf "avg: %.0f ops/sec\n", sum/n}'
| 벤치마크 | 측정 항목 | 용도 |
|---|---|---|
sched messaging | 메시지 전달 처리량(Throughput) | 스케줄러/IPC 성능 기준선 |
sched pipe | pipe 왕복 지연 | 컨텍스트 스위치 오버헤드 |
mem memcpy | 메모리 복사 대역폭(Bandwidth) | 메모리 서브시스템 성능 |
futex hash | futex 해시(Hash) 충돌 | 동기화 프리미티브 성능 |
numa mem | NUMA 노드간 대역폭 | NUMA 토폴로지 영향 확인 |
cpupower frequency-set -g performance), 3) 최소 3회 반복 측정, 4) 통계적 유의성 검증 (표준편차 확인), 5) 커밋별 결과를 시계열 DB에 저장하여 추세 모니터링.
perf_event_paranoid 조정이 불가능하거나 PMU 접근이 제한될 수 있습니다. 하드웨어 카운터 대신 perf stat -e task-clock,context-switches,page-faults 같은 소프트웨어 이벤트를 사용하거나, self-hosted runner를 활용하세요.
perf c2c NUMA 분석
perf c2c는 Cache-to-Cache transfer, 특히 false sharing을 탐지하는 데 특화된 도구입니다. 멀티코어 환경에서 서로 다른 CPU가 같은 캐시라인의 다른 변수를 동시에 접근하면, 실제로 데이터 공유가 없음에도 캐시 일관성(coherency) 프로토콜에 의해 성능이 심각하게 저하됩니다. perf c2c는 이런 HITM(Hit In Modified) 이벤트를 추적하여 문제가 되는 캐시라인과 그 라인을 접근하는 함수/변수를 식별합니다.
HITM은 하나의 코어가 Modified 상태인 캐시라인을 다른 코어가 읽을 때 발생합니다. 이때 수정된 데이터를 먼저 LLC(Last Level Cache)나 메모리에 쓰고 나서 다른 코어에 전달하므로 지연이 크게 증가합니다. NUMA 환경에서는 원격 노드의 HITM(Remote HITM)이 로컬 HITM보다 수십 배 느려 성능 영향이 더 심각합니다.
perf c2c record/report 사용법
# 전체 시스템의 HITM 이벤트 수집 (권장: 충분한 시간 동안)
perf c2c record -a -- sleep 10
# 특정 프로세스에 대해 수집
perf c2c record -p $(pidof my_server) -- sleep 30
# 보고서 생성 (HITM 기준 정렬)
perf c2c report --stdio
# Shared Data Cache Line 상세 보기
perf c2c report --stats
# 소스 코드 연동 (디버그 심볼 필요)
perf c2c report --call-graph=fp --source
HITM 보고서 해석
| 필드 | 의미 | 판단 기준 |
|---|---|---|
Rmt HITM | 원격 NUMA 노드에서 Modified 캐시라인 히트 | 가장 심각, 최우선 분석 대상 |
Lcl HITM | 로컬 소켓 내 다른 코어에서 Modified 히트 | Remote보다 영향 작으나 여전히 비용 높음 |
Stores | 해당 캐시라인에 대한 쓰기 접근 수 | 읽기만 있으면 HITM 발생 안 함 |
Shared Data CL | 공유 캐시라인의 변수 오프셋 정보 | 같은 라인에 다른 변수 → false sharing 가능 |
HITM% /Total | 전체 HITM 중 해당 캐시라인 비율 | 상위 라인부터 최적화 우선순위(Priority) |
cacheline 정렬로 false sharing 해결
/* false sharing 발생 구조 — counter와 flag가 같은 캐시라인에 위치 */
struct bad_layout {
unsigned long counter; /* CPU 0이 자주 쓰기 */
unsigned long flag; /* CPU 1이 자주 쓰기 — 같은 64B 라인! */
};
/* 해결: __cacheline_aligned_in_smp로 별도 캐시라인에 배치 */
struct good_layout {
unsigned long counter;
unsigned long flag __cacheline_aligned_in_smp;
/* flag가 다음 캐시라인 경계에서 시작 */
};
/* per-CPU 데이터로 완전 분리 (가장 확실한 해결) */
static DEFINE_PER_CPU(unsigned long, per_cpu_counter);
/* 구조체 전체를 캐시라인 정렬 */
struct aligned_data {
unsigned long value;
} ____cacheline_aligned_in_smp;
____cacheline_aligned_in_smp (밑줄 4개)는 구조체(Struct) 자체를 정렬하고, __cacheline_aligned_in_smp (밑줄 2개)는 멤버를 정렬합니다. 특히 struct net_device, struct sock 같은 네트워크 핫패스 구조체에서 이 기법이 광범위하게 사용됩니다. pahole 도구로 구조체 레이아웃과 패딩 홀을 확인하세요.
mem_load_retired.l3_miss 등 메모리 이벤트를 통해 데이터 주소를 수집합니다. perf_event_paranoid 값이 0 이하여야 합니다.
ARM SPE
SPE(Statistical Profiling Extension)는 ARMv8.2-A부터 도입된 하드웨어 프로파일링 확장으로, CPU가 자체적으로 명령어를 샘플링하여 전용 버퍼에 기록합니다. 기존 PMU 기반 인터럽트 샘플링과 달리, SPE는 파이프라인 내부에서 직접 데이터를 수집하므로 skid(실제 이벤트와 기록 사이의 오차)가 거의 없어 정확한 마이크로아키텍처 분석이 가능합니다.
SPE는 각 샘플링된 명령어에 대해 실행 지연(latency), 데이터 소스(L1/L2/LLC/DRAM), 가상/물리 주소, 분기 결과 등 풍부한 메타데이터를 제공합니다. 이 정보를 활용하면 캐시 미스가 발생하는 정확한 명령어 위치, DRAM 접근 패턴, 분기 예측 실패 위치를 정밀하게 파악할 수 있습니다.
SPE 기본 사용법
# SPE 디바이스 확인
ls /sys/bus/event_source/devices/arm_spe_0/
# → format/ events/ cpumask type
# 기본 SPE 프로파일링
perf record -e arm_spe_0// -- ./workload
# 샘플링 주기 설정 (최소 간격 = 더 많은 샘플)
perf record -e arm_spe_0/min_latency=50/ -- ./workload
# → 50사이클 이상 지연된 이벤트만 수집 (노이즈 감소)
# load 이벤트만 필터링
perf record -e arm_spe_0/load_filter=1,min_latency=40/ -- ./workload
# store 이벤트만 필터링
perf record -e arm_spe_0/store_filter=1/ -- ./workload
# 분기 이벤트 필터링
perf record -e arm_spe_0/branch_filter=1/ -- ./workload
# 보고서: data source 기반 정렬
perf report --mem-mode --sort=mem,sym,dso
SPE 필터링 옵션
| 필터 | 설명 | 사용 예 |
|---|---|---|
load_filter=1 | 로드(읽기) 명령어만 수집 | 캐시 미스 분석 |
store_filter=1 | 스토어(쓰기) 명령어만 수집 | 쓰기 패턴 분석 |
branch_filter=1 | 분기 명령어만 수집 | 분기 예측 분석 |
min_latency=N | N 사이클 이상 지연만 수집 | 노이즈 제거, 고지연 집중 |
ts_enable=1 | 타임스탬프 포함 | 시간별 패턴 분석 |
pa_enable=1 | 물리 주소 기록 (root 필요) | NUMA 노드별 접근 분석 |
SPE Data Source 해석
# data source별 분석 (mem-mode)
perf mem report --sort=mem
# 출력 예시:
# Samples Overhead Memory access Symbol
# 1532 42.30% L1 hit [k] copy_page
# 891 24.60% L2 hit [k] __memcpy
# 623 17.21% LLC hit [k] tcp_recvmsg
# 402 11.10% Local RAM [k] page_fault
# 174 4.81% Remote RAM (1 hop) [k] numa_migrate
# 특정 함수의 메모리 접근 패턴 상세
perf annotate --data-type --symbol=copy_page
SPE vs PMU 인터럽트 샘플링 비교
| 특성 | SPE | PMU 인터럽트 샘플링 |
|---|---|---|
| Skid(오차) | 거의 없음 (0~2 명령어) | 수십~수백 명령어 |
| 오버헤드 | 매우 낮음 (하드웨어 버퍼) | 인터럽트마다 핸들러 실행 |
| 메타데이터 | 지연, 데이터 소스, 주소 등 풍부 | 제한적 (PEBS/IBS 없으면 부족) |
| 필터링 | 하드웨어 레벨 사전 필터 | 소프트웨어 후처리 |
| CPU 지원 | ARMv8.2+ (Cortex-A77 이상) | 모든 아키텍처 |
| 버퍼 관리 | 전용 SPE 버퍼 (커널 주기적 수거) | perf ring buffer |
FEAT_SPEv1p3으로 추가 필터링 옵션과 확장된 데이터 소스 정보가 제공됩니다. Neoverse V2(Grace)에서는 서버 워크로드에 최적화된 SPE 버퍼 크기가 제공되어 장시간 프로파일링이 가능합니다. SPE 지원 여부는 cat /proc/cpuinfo | grep -i spe 또는 ID_AA64DFR0_EL1 레지스터에서 확인합니다.
perf mem record -e arm_spe_0//를 사용하면 메모리 접근 유형별 자동 분류가 가능합니다. perf mem report --sort=mem,sym으로 L1/L2/LLC/DRAM 접근 비율을 함수별로 확인하면, 캐시 최적화 대상을 빠르게 파악할 수 있습니다. 자세한 최적화 기법은 성능 최적화를 참고하세요.
AMD IBS
IBS(Instruction Based Sampling)는 AMD 프로세서(Family 10h 이후)에서 제공하는 정밀 프로파일링 기능입니다. Intel의 PEBS와 유사한 역할을 하지만, IBS는 Fetch와 Op(실행) 두 가지 독립적인 샘플링 채널을 제공한다는 점에서 차별화됩니다. Zen4/Zen5 아키텍처에서는 IBS의 정밀도와 수집 가능 정보가 크게 확장되었습니다.
IBS Fetch vs IBS Op
IBS Fetch는 명령어 페치(가져오기) 단계를 샘플링합니다. N번째 페치 완료 후 다음 페치를 태깅하여, I-캐시 미스, ITLB 미스, 페치 지연 등 프런트엔드 병목을 분석합니다. IBS Op는 실행(execute) 단계를 샘플링하여, 캐시 미스, 분기 예측, 데이터 주소, 실행 지연 등 백엔드 파이프라인 상태를 수집합니다.
# IBS 지원 확인
ls /sys/bus/event_source/devices/ibs_*
# → ibs_fetch, ibs_op
# IBS Fetch: 프런트엔드(I-캐시, ITLB) 분석
perf record -e ibs_fetch// -- ./workload
perf report --sort=sym,dso
# IBS Op: 백엔드(D-캐시, 분기, 실행 지연) 분석
perf record -e ibs_op// -- ./workload
# IBS Op + 메모리 분석 (데이터 주소 포함)
perf record -e ibs_op//p -- ./workload
perf mem report --sort=mem,sym
# 샘플링 주기 조정 (기본값은 하드웨어 결정)
perf record -e ibs_op/cnt=100000/ -- ./workload
# → 약 100K ops마다 1개 샘플
# Fetch + Op 동시 수집
perf record -e ibs_fetch//,ibs_op// -- ./workload
IBS 데이터 필드 해석
| IBS 채널 | 주요 필드 | 분석 대상 |
|---|---|---|
| IBS Fetch | IbsFetchCtl.IbsFetchCnt | 페치 샘플링 주기 카운터 |
IbsFetchCtl.IbsIcMiss | I-캐시 미스 여부 | |
IbsFetchCtl.IbsL1TlbMiss | L1 ITLB 미스 여부 | |
IbsFetchCtl.IbsFetchLat | 페치 완료까지 사이클 수 | |
| IBS Op | IbsOpData.IbsCompToRetCtr | 완료-퇴장 지연 (사이클) |
IbsOpData3.IbsDcMiss | D-캐시 미스 여부 | |
IbsOpData3.IbsL2Miss | L2 캐시 미스 여부 | |
IbsOpData3.IbsSwPf | 소프트웨어 프리페치 성공 여부 | |
IbsDcLinAddr | 데이터 접근 가상 주소 |
Zen4/Zen5 IBS 확장
Zen4(EPYC 9004)에서는 IBS Op에 L3 미스 지연, DRAM 접근 지연 필드가 추가되어 메모리 계층별 병목을 더 정밀하게 분석할 수 있습니다. Zen5에서는 IBS Op Data 4 레지스터가 확장되어 명령어 타입(정수/부동소수점/벡터)별 분류가 가능합니다.
PEBS vs IBS 비교
| 특성 | Intel PEBS | AMD IBS |
|---|---|---|
| 아키텍처 | Intel Core 2 이상 | AMD Family 10h 이상 |
| 샘플링 방식 | 이벤트 카운터 오버플로 기반 | N번째 명령어 태깅 기반 |
| Skid | 매우 낮음 (1~2 명령어) | 없음 (정확한 IP) |
| 프런트엔드 분석 | 제한적 (frontend_retired 이벤트) | IBS Fetch 전용 채널 |
| 데이터 주소 | PEBS + mem_inst_retired | IBS Op 기본 포함 |
| 지연 정보 | PEBS Latency (Sapphire Rapids+) | IBS Completion-to-Retire |
| 멀티 이벤트 | 카운터당 1개 이벤트 | Fetch/Op 각 1개 (동시 가능) |
perf c2c는 IBS Op의 데이터 주소 정보를 활용하여 false sharing을 탐지합니다. perf c2c record 실행 시 자동으로 IBS가 선택되며, perf c2c report에서 캐시라인별 HITM 분석이 가능합니다. Zen3 이전에서는 perf c2c 지원이 불완전할 수 있으므로 커널 5.19+ 권장됩니다.
data type profiling (6.x)
커널 6.3부터 도입된 data type profiling은 메모리 접근 프로파일링의 패러다임을 바꾸는 기능입니다. 기존에는 캐시 미스가 발생하는 명령어 주소(IP)만 알 수 있었지만, 이제 어떤 구조체의 어떤 필드에서 미스가 발생하는지 DWARF 디버그 정보를 활용하여 구조체/변수 수준으로 분석할 수 있습니다.
이 기능은 perf annotate --data-type 또는 perf report --sort=type으로 사용하며, 데이터 레이아웃 최적화의 핵심 도구입니다. 커널 6.8+에서는 타입 추론이 개선되어 지역 변수와 인라인 함수(Inline Function)의 접근도 분석할 수 있습니다.
data type profiling 사용법
# 1단계: 메모리 접근 이벤트 수집 (PEBS/IBS/SPE 필요)
perf mem record -a -- sleep 10
# 또는 특정 이벤트로 수집
perf record -e cpu/mem-loads,ldlat=30/P \
-e cpu/mem-stores/P -- ./workload
# 2단계: 데이터 타입별 보고서
perf report --sort=type
# 출력 예시:
# Overhead Data Type
# 34.20% struct sk_buff
# 18.50% struct page
# 12.30% struct sock
# 8.70% struct inode
# 3단계: 특정 구조체의 필드별 분석
perf annotate --data-type="struct sk_buff"
# → 구조체 필드별 캐시 미스 비율, 접근 횟수, 평균 지연
# 4단계: 소스 코드와 함께 보기
perf annotate --data-type --symbol=tcp_sendmsg --source
구조체별 캐시 미스 분석 예시
Annotate type: 'struct sk_buff' in vmlinux (143 samples):
=====================================================
Offset Size Samples Percent Field
0 8 12 8.39% .next
8 8 3 2.10% .prev
16 8 28 19.58% .sk ← 핫 필드! (다른 캐시라인)
24 8 5 3.50% .dev
112 4 41 28.67% .len ← 핫 필드!
116 4 18 12.59% .data_len
128 4 22 15.38% .hash ← 핫 필드!
192 2 8 5.59% .protocol
--- --- --- ------
143 100.0% Total
.sk, .len, .hash가 핫 필드임을 알 수 있습니다. 이 필드들을 같은 캐시라인(64B)에 배치하면 미스율을 줄일 수 있습니다. pahole --reorganize로 최적 레이아웃 제안을 받을 수 있으며, 실제 커널에서는 struct sk_buff의 핫 필드가 첫 번째 캐시라인에 모여 있도록 신중하게 배치되어 있습니다.
CONFIG_DEBUG_INFO_DWARF5=y 필수 (DWARF4에서는 타입 매칭 실패 가능), 2) 최적화 수준이 높으면 (-O2/-O3) 변수가 레지스터에만 존재하여 타입 매핑 불가, 3) 커널 6.3~6.7은 글로벌 변수만 지원, 6.8+에서 지역 변수/인라인 확장, 4) vmlinux 없이는 사용 불가 — 커널 심볼 설정 참고.
perf annotate
perf annotate는 프로파일 데이터를 소스 코드 및 디스어셈블리와 연결하여 명령어 수준에서 핫스팟을 분석하는 핵심 도구입니다. 각 명령어가 전체 샘플 중 차지하는 비율을 표시하여, 병목이 되는 정확한 코드 위치를 파악할 수 있습니다.
출력 모드 비교
# --stdio: 터미널 텍스트 출력 (스크립트 연동 용이)
perf annotate --stdio --symbol=tcp_sendmsg
# --tui: ncurses 대화형 UI (기본값)
perf annotate --symbol=tcp_sendmsg
# → 't' 키로 소스/어셈블리 토글, 'h' 키로 도움말
# --gtk: GTK GUI (X11 필요)
perf annotate --gtk --symbol=tcp_sendmsg
# 소스 코드만 표시 (어셈블리 숨김)
perf annotate --stdio --symbol=tcp_sendmsg --no-asm
# 어셈블리만 표시 (소스 숨김)
perf annotate --stdio --symbol=tcp_sendmsg --no-source
이벤트별 명령어 비율 분석
# 여러 이벤트로 수집 후 이벤트별 annotate
perf record -e cycles,cache-misses,branch-misses -g -- ./workload
# 사이클 기준 annotate
perf annotate --event=cycles --symbol=hot_function
# 캐시 미스 기준 annotate
perf annotate --event=cache-misses --symbol=hot_function
# 분기 미스 기준 annotate
perf annotate --event=branch-misses --symbol=hot_function
# → 같은 함수라도 이벤트마다 핫 명령어가 다름!
# cycles: 루프 본체, cache-misses: 메모리 접근, branch-misses: 조건분기
인라인 함수와 DWARF 처리
최적화된 커널에서는 많은 함수가 인라인되어, 인라인된 함수의 샘플이 호출자 함수에 합산됩니다. perf annotate는 DWARF 디버그 정보의 DW_TAG_inlined_subroutine을 활용하여 인라인된 함수 호출을 역추적(Backtrace)합니다.
# 인라인 함수 분해 (6.2+)
perf annotate --stdio --symbol=__netif_receive_skb_core
# 출력 예시:
# 15.30% │ mov 0x70(%rbx),%rax
# │ → [inlined] skb_vlan_tag_present()
# 8.20% │ test $0x4,%al
# │ → [inlined] eth_type_trans()
# DWARF 정보 확인 (인라인 함수 목록)
readelf --debug-dump=info vmlinux | grep -A2 DW_TAG_inlined_subroutine | head -30
debuginfod 연동으로 원격 디버그 정보 활용
# debuginfod 서버 설정 (환경변수)
export DEBUGINFOD_URLS="https://debuginfod.ubuntu.com/"
# perf annotate가 자동으로 debuginfod에서 소스/디버그 정보 다운로드
perf annotate --symbol=tcp_sendmsg
# → Build ID 기반으로 vmlinux, 소스 코드를 자동 다운로드
# 캐시 확인
ls ~/.cache/debuginfod_client/
# → buildid/ source/ debuginfo/
# 타임아웃 설정 (초 단위)
export DEBUGINFOD_TIMEOUT=30
perf annotate --percent-type=local-period로 함수 내부 비율(함수 자체를 100%로), --percent-type=global-period로 전체 프로파일 대비 비율을 선택할 수 있습니다. 함수 내 명령어 최적화에는 local, 전체 병목 파악에는 global이 적합합니다.
PREEMPT_RT 환경 제약
PREEMPT_RT(실시간 커널)에서 perf를 사용할 때는 일반 커널과 다른 중요한 제약 사항이 있습니다. RT 커널의 목표는 결정적(deterministic) 지연을 보장하는 것이므로, perf의 인터럽트 기반 샘플링이 실시간 태스크(Task)에 간섭을 줄 수 있습니다. 이 문제를 이해하고 올바른 설정으로 사용해야 합니다.
NMI 안전성 문제
일반 x86 커널에서 perf는 PMU 카운터 오버플로 시 NMI(Non-Maskable Interrupt)로 샘플을 수집합니다. NMI는 어떤 컨텍스트에서도 발생할 수 있어 정확한 IP를 캡처하지만, PREEMPT_RT에서는 NMI 핸들러 내에서 일반 스핀락(Spinlock)을 사용할 수 없습니다(RT에서는 스핀락이 sleeping lock으로 변환되므로). 이 때문에 RT 커널에서는 NMI 기반 샘플링 대신 hrtimer 기반으로 전환됩니다.
# RT 커널 확인
uname -v | grep -i rt
# → #1 SMP PREEMPT_RT ...
# perf_event_paranoid 설정 (RT 환경 권장값)
sysctl kernel.perf_event_paranoid
# RT에서는 1 이상 유지 권장 (불필요한 인터럽트 최소화)
# RT 안전한 perf record (소프트웨어 이벤트)
perf record -e cpu-clock -- ./rt_application
# → cpu-clock은 hrtimer 기반, NMI 회피
# 하드웨어 카운터 사용 시 (주의 필요)
perf record -e cycles -F 1000 -- ./rt_application
# → 샘플링 빈도를 낮춰 간섭 최소화
# RT 태스크에 대한 perf 격리
taskset -c 0-3 perf record -e cycles -a -C 4-7 -- sleep 10
# → RT 태스크(CPU 0-3)와 perf 수집(CPU 4-7)을 분리
RT 커널에서 perf 사용 모범 사례
| 방법 | 설명 | RT 간섭 |
|---|---|---|
perf stat (카운팅) | 인터럽트 없이 카운터 읽기 | 거의 없음 (context switch 시점만) |
cpu-clock 이벤트 | hrtimer 기반 소프트웨어 샘플링 | 낮음 (스레드(Thread) 컨텍스트 전환 시점) |
-F 100 (저빈도) | 초당 100회 샘플링 | 낮음 (인터럽트 빈도 감소) |
-C CPU 격리(Isolation) | 비-RT CPU에서만 수집 | RT CPU에 영향 없음 |
| tracepoint 이벤트 | 특정 커널 경로에서만 기록 | 해당 경로 통과 시에만 |
perf record -S | 스냅샷 모드 (트리거 시 저장) | 평소 거의 없음 |
RTLA와의 역할 분담
PREEMPT_RT 환경에서 지연 분석은 perf보다 RTLA(Real-Time Linux Analysis)가 더 적합합니다. RTLA의 timerlat은 IRQ/스레드 지연을 마이크로초 단위로 측정하고, osnoise는 CPU에서 발생하는 모든 간섭원을 추적합니다. perf는 간섭의 원인을 깊이 분석하는 데, RTLA는 간섭의 크기와 빈도를 측정하는 데 사용하세요.
# RTLA로 지연 측정 → perf로 원인 분석 (조합 워크플로)
# 1단계: RTLA timerlat으로 지연 측정
rtla timerlat top -c 4 -p 95 -d 60s
# → CPU 4에서 FIFO:95 우선순위로 60초 측정
# 2단계: 지연 원인 트레이싱 (자동 트리거)
rtla timerlat hist -c 4 -p 95 -T 50 -t
# → 50μs 초과 시 자동으로 trace 저장
# 3단계: perf로 해당 시점 상세 분석
perf record -e sched:sched_switch,irq:irq_handler_entry -C 4 -- sleep 60
perf script --sort=time,event | head -100
-F 10000 이상)로 RT CPU에서 프로파일링, 2) perf record -a로 모든 CPU 동시 프로파일링 (RT CPU 포함), 3) perf_event_paranoid=-1을 프로덕션 RT 시스템에 설정, 4) perf trace를 RT 태스크에 직접 연결 (syscall마다 오버헤드). RTLA / timerlat / osnoise를 먼저 고려하세요.
CONFIG_PERF_EVENTS_NMI=n이면 NMI 기반 샘플링이 비활성화되고 hrtimer로 대체됩니다. RT 커널에서는 자동 설정되나, 일반 커널에서도 명시적으로 비활성화 가능합니다. /proc/sys/kernel/nmi_watchdog도 RT 환경에서는 0으로 설정하여 NMI 소스를 줄이는 것이 좋습니다.
perf script 커스텀 분석
perf script는 perf.data 파일의 이벤트를 텍스트로 출력하는 도구이지만, -s 옵션으로 Python/Perl 스크립트를 연결하면 강력한 커스텀 분석이 가능합니다. 이벤트 핸들러를 직접 작성하여 특정 패턴 탐지, 통계 집계, 커스텀 보고서 생성 등을 자동화할 수 있습니다.
perf script 기본 사용법
# 모든 이벤트를 텍스트로 출력
perf script
# 출력 필드 선택
perf script -F comm,pid,tid,cpu,time,event,ip,sym,dso
# 특정 이벤트만 출력
perf script --event=sched:sched_switch
# 시간 범위 필터
perf script --time "10%/1" # 처음 10%만
perf script --time "1000,2000" # 1~2초 구간
# 콜스택 포함 출력
perf script --show-callchain
# Flame Graph 변환 (Brendan Gregg 스크립트)
perf script | stackcollapse-perf.pl | flamegraph.pl > flame.svg
Python 스크립트 API
#!/usr/bin/env python3
# perf-script-latency.py: 스케줄링 지연 분석 스크립트
# 사용법: perf script -s perf-script-latency.py
import os, sys
sys.path.append(os.environ['PERF_EXEC_PATH'] +
'/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
from perf_trace_context import *
from Core import *
wakeup_time = {}
latencies = []
def sched__sched_wakeup(event_name, context, common_cpu,
common_secs, common_nsecs, common_pid, common_comm,
comm, pid, prio, success, target_cpu):
"""태스크 깨우기 이벤트 — 타임스탬프 기록"""
wakeup_time[pid] = common_secs * 1000000000 + common_nsecs
def sched__sched_switch(event_name, context, common_cpu,
common_secs, common_nsecs, common_pid, common_comm,
prev_comm, prev_pid, prev_prio, prev_state,
next_comm, next_pid, next_prio):
"""스케줄 전환 이벤트 — wakeup → switch 지연 계산"""
if next_pid in wakeup_time:
now = common_secs * 1000000000 + common_nsecs
latency_us = (now - wakeup_time[next_pid]) / 1000.0
latencies.append((next_comm, next_pid, latency_us))
del wakeup_time[next_pid]
def trace_end():
"""스크립트 종료 시 통계 출력"""
if not latencies:
return
latencies.sort(key=lambda x: x[2], reverse=True)
print(f"\n{'Comm':>20s} {'PID':>8s} {'Latency(μs)':>15s}")
print("-" * 48)
for comm, pid, lat in latencies[:20]:
print(f"{comm:>20s} {pid:>8d} {lat:>15.2f}")
vals = [x[2] for x in latencies]
print(f"\nTotal: {len(vals)} events")
print(f"Avg: {sum(vals)/len(vals):.2f} μs")
print(f"Max: {max(vals):.2f} μs")
내장 스크립트 활용
# 사용 가능한 내장 스크립트 목록
perf script -l
# → syscall-counts, sched-migration, failed-syscalls 등
# syscall 통계
perf record -e raw_syscalls:sys_enter -a -- sleep 5
perf script -s syscall-counts
# 스케줄링 마이그레이션 분석
perf record -e sched:sched_migrate_task -a -- sleep 10
perf script -s sched-migration
# 커스텀 스크립트 생성 템플릿
perf script -g python
# → perf-script.py 템플릿 파일 생성 (이벤트 핸들러 골격)
perf script -g perl
# → perf-script.pl 템플릿 파일 생성
perf script | stackcollapse-perf.pl | flamegraph.pl 파이프라인은 가장 널리 사용되는 perf script 활용 사례입니다. FlameGraph 레포지토리에서 다운로드할 수 있습니다. --colors=java, --title="My Profile" 등 옵션으로 커스터마이징이 가능합니다. 성능 최적화 문서에서 Flame Graph 해석 방법을 자세히 다룹니다.
perf script -s를 사용하려면 perf가 Python 지원으로 빌드되어야 합니다. perf version --build-options | grep python으로 확인하세요. Ubuntu/Debian에서는 linux-tools-common 패키지에 기본 포함되나, 소스 빌드 시 make -C tools/perf PYTHON=python3으로 명시해야 합니다.
Intel PT 연동
Intel PT(Processor Trace)는 CPU가 실행하는 모든 분기(branch)를 하드웨어 레벨에서 기록하여, 프로그램의 완전한 제어 흐름(control flow)을 재구성할 수 있는 기능입니다. 일반 샘플링과 달리, Intel PT는 통계적 추정이 아닌 정확한 실행 경로를 제공하여 버그 재현, 코드 커버리지 분석, AutoFDO(자동 피드백 최적화)에 활용됩니다.
Intel PT는 5세대(Broadwell) 이후 프로세서에서 지원되며, 사이클 정확도(cycle-accurate) 타이밍, 함수 호출/반환 추적, 간접 분기 타겟 기록 등을 제공합니다. 패킷(Packet) 형태로 전용 버퍼에 기록되어 오버헤드가 매우 낮습니다(보통 5% 미만).
Intel PT 수집 및 디코딩
# Intel PT 지원 확인
ls /sys/bus/event_source/devices/intel_pt/
# → format/ type caps/
# 기본 PT 수집
perf record -e intel_pt// -- ./workload
# 사이클 정확도 타이밍 포함
perf record -e intel_pt/cyc=1,cyc_thresh=1/ -- ./workload
# 커널 + 유저 모두 수집
perf record -e intel_pt//u -- ./user_app # 유저 모드만
perf record -e intel_pt//k -- sleep 5 # 커널 모드만
perf record -e intel_pt// -- ./workload # 양쪽 모두
# AUX 버퍼 크기 설정 (기본 4MB, 장시간 수집 시 증가)
perf record -e intel_pt// --aux-size=64M -- ./workload
# 스냅샷 모드 (트리거 시점만 저장)
perf record -e intel_pt// -S -- ./long_running_app
# → Ctrl+C 또는 SIGUSR2로 스냅샷 트리거
PT 디코딩과 실행 경로 분석
# --itrace 옵션으로 PT 패킷 디코딩
perf script --itrace=i0ns --ns
# i: instruction 이벤트 합성
# 0: 모든 명령어 (N: N번째만)
# ns: 나노초 타임스탬프
# 분기 이벤트만 디코딩
perf script --itrace=b0ns
# b: branch 이벤트
# 함수 호출/반환만 추출
perf script --itrace=cr
# c: call, r: return
# 디코딩 후 Flame Graph 생성
perf script --itrace=i10000ns --ns -F comm,pid,tid,time,ip,sym,dso,callchain | \
stackcollapse-perf.pl | flamegraph.pl > pt_flame.svg
# 특정 함수의 실행 경로 추출
perf script --itrace=b0ns --ns | grep tcp_sendmsg | head -50
Intel PT 활용 사례
| 활용 사례 | 방법 | 장점 |
|---|---|---|
| 코드 커버리지 | --itrace=i0 + 실행 IP 집합 | 실제 실행된 코드 경로 100% 파악 |
| AutoFDO | create_llvm_prof로 프로파일 변환 | 실제 분기 빈도 기반 컴파일러 최적화(Compiler Optimization) |
| 버그 재현 | 스냅샷 모드 + 크래시 시점 복원 | 크래시 직전 수천 명령어 재구성 |
| 레이턴시 분석 | cyc=1 + 구간 타이밍 | 명령어별 사이클 정확도 측정 |
| 보안 분석 | 제어 흐름 무결성(Integrity) 검증 | ROP/JOP 공격 탐지 |
AutoFDO 연동
# 1단계: Intel PT로 프로파일 수집
perf record -e intel_pt//u -b -- ./my_application
# 2단계: perf.data → AutoFDO 프로파일 변환
perf script --itrace=b0 --ns -F pid,ip,brstack | \
create_llvm_prof --binary=./my_application \
--out=profile.afdo
# 3단계: 프로파일 기반 재컴파일 (PGO)
clang -O2 -fprofile-sample-use=profile.afdo \
-o my_application_optimized my_application.c
# GCC 사용 시
create_gcov --binary=./my_application \
--profile=perf.data --gcov=profile.gcov
gcc -O2 -fauto-profile=profile.gcov \
-o my_application_optimized my_application.c
--aux-size를 적절히 설정하고, 필요시 mtc_period, psb_period를 조정하여 패킷 밀도를 줄이세요. 스냅샷 모드(-S)를 사용하면 관심 시점 전후만 저장하여 크기를 크게 줄일 수 있습니다.
perf record -g는 통계적 샘플링으로 호출 빈도를 추정하지만, Intel PT는 모든 분기를 기록하여 정확한 실행 경로를 제공합니다. 대신 데이터 크기가 훨씬 크고 디코딩에 시간이 걸립니다. ftrace의 function_graph 트레이서와 결합하면 커널 함수 호출 경로와 PT 데이터를 교차 분석할 수 있습니다.
perf 트러블슈팅 가이드
perf 사용 시 자주 발생하는 문제와 해결 방법을 정리합니다.
| 증상 | 원인 | 해결 방법 |
|---|---|---|
[unknown]이 대부분 | 디버그 심볼 없음 | debuginfo 패키지 설치, DEBUGINFOD_URLS 설정, -fno-omit-frame-pointer 재컴파일 |
| "Access limited" 에러 | perf_event_paranoid 높음 | sysctl -w kernel.perf_event_paranoid=-1 또는 setcap cap_perfmon+ep /usr/bin/perf |
| 커널 주소가 0 | kptr_restrict 활성 | sysctl -w kernel.kptr_restrict=0 또는 root 실행 |
| "XX% lost events" | ring buffer 오버플로 | perf record -m 8M 버퍼 증가, -F 주파수 감소 |
| 콜스택이 1-2 프레임만 | Frame Pointer 없음 | --call-graph dwarf 또는 --call-graph lbr(Intel) 사용 |
| 멀티플렉싱 (xx.xx%) | 이벤트 > 카운터 수 | 이벤트 수 줄이기, {} 그룹핑, 여러 번 나누어 측정 |
| "perf not found" | 패키지 미설치 | apt install linux-tools-$(uname -r) 또는 dnf install perf |
| "cycles:pp not supported" | PEBS 미지원 CPU | :p (단일)로 변경하거나 cycles만 사용 |
| perf report 느림 | 대용량 perf.data | perf report --no-children, --percent-limit 1, -n |
| JIT 심볼 없음 | JIT dump 미생성 | Java: -XX:+PreserveFramePointer, perf inject -j |
| "no samples found" | 워크로드 너무 짧음 | 실행 시간 늘리기 또는 -c(period) 줄이기 |
| ARM64 이벤트 없음 | PMU 드라이버 미로드 | ls /sys/bus/event_source/devices/ 확인, CONFIG_ARM_PMU=y |
# perf 환경 진단 스크립트
echo "=== perf 환경 진단 ==="
echo "perf version:"
perf version 2>/dev/null || echo "perf not installed"
echo "perf_event_paranoid:"
cat /proc/sys/kernel/perf_event_paranoid
echo "kptr_restrict:"
cat /proc/sys/kernel/kptr_restrict
echo "PMU devices:"
ls /sys/bus/event_source/devices/ 2>/dev/null
echo "DEBUGINFOD:"
echo "$DEBUGINFOD_URLS"
echo "perf build options:"
perf version --build-options 2>/dev/null
echo "Kernel debug info:"
ls /usr/lib/debug/lib/modules/$(uname -r)/vmlinux 2>/dev/null || \
echo "vmlinux not found (install debuginfo package)"
성능 측정 정확도 향상 팁
# CPU 주파수 고정 (터보부스트/절전 비활성화)
cpupower frequency-set -g performance
# 또는
echo performance | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
# ASLR 비활성화 (반복 측정 일관성)
echo 0 > /proc/sys/kernel/randomize_va_space
# NMI watchdog 비활성화 (PMU 카운터 1개 추가 확보)
sysctl -w kernel.nmi_watchdog=0
# 반복 측정으로 통계적 유의성 확보
perf stat -r 10 -- ./benchmark
# → 10회 반복, 평균 + 표준편차 자동 계산
# → 표준편차가 5% 이상이면 노이즈 의심
# CPU 고정 실행 (캐시 효과 일관성)
taskset -c 4 perf stat -- ./benchmark
# 시스템 노이즈 최소화
# → 불필요한 서비스 중지, cron 비활성화
# → numactl --physcpubind=4 --membind=0 ./benchmark
performance 거버너 필수, 2) 짧은 워크로드(<1초)는 멀티플렉싱 오차가 심합니다 — 충분히 긴 실행 시간 확보, 3) 첫 실행은 캐시 콜드 상태이므로 워밍업 실행 후 측정하세요, 4) 가상 머신(VM)에서는 PMU 가상화 제한으로 일부 이벤트가 부정확할 수 있습니다.
실전 사례별 분석 워크플로
성능 문제의 유형에 따라 perf 분석 전략이 달라집니다. 각 병목 유형(CPU bound, I/O bound, Lock contention, 메모리 병목)에 대한 체계적 분석 워크플로를 제시합니다.
사례 A: CPU Bound 분석
# 1단계: 초기 진단
perf stat ./cpu_intensive_app
# → IPC: 2.3 (CPU 효율 좋음, compute bound)
# → task-clock: 4000ms (wall time과 동일 → 100% CPU 사용)
# 2단계: 핫스팟 식별
perf record -F 99 -g -- ./cpu_intensive_app
perf report --sort sym --stdio | head -20
# → 42% matrix_multiply
# → 28% sort_elements
# 3단계: Flamegraph로 시각화
perf script | stackcollapse-perf.pl | flamegraph.pl > cpu_flame.svg
# 4단계: 명령어 수준 분석
perf annotate --symbol=matrix_multiply --stdio
# → 특정 루프에서 FP 연산에 샘플 집중
# → SIMD 벡터화 적용 검토
# 5단계: TMA로 마이크로아키텍처 분석
perf stat --topdown -- ./cpu_intensive_app
# → Retiring: 65% (좋음, 유용한 작업 비율 높음)
# → 추가 최적화 여지 제한적 → 알고리즘 개선 필요
사례 B: 메모리 병목 분석
# 1단계: 초기 진단
perf stat -d ./memory_heavy_app
# → IPC: 0.45 (낮음 → 메모리 대기 의심)
# → LLC-load-misses: 8.2% (높음)
# → dTLB-load-misses: 2.1%
# 2단계: TMA 확인
perf stat --topdown -- ./memory_heavy_app
# → Backend Bound: 58% → Memory Bound 확인
# 3단계: 메모리 접근 프로파일링
perf mem record -- ./memory_heavy_app
perf mem report --sort mem,sym
# → copy_buffer: 34% LLC miss, 22% Remote DRAM
# → 데이터 지역성 문제 확인
# 4단계: data type profiling (커널 6.3+)
perf report --sort=type
# → struct buffer_t: 45% of all memory samples
perf annotate --data-type="struct buffer_t"
# → offset 128 (next_ptr): 60% of misses → 포인터 체이싱
# 5단계: false sharing 확인
perf c2c record -- ./memory_heavy_app
perf c2c report --stdio
# → Rmt HITM이 높으면 __cacheline_aligned 패딩 적용
사례 C: I/O Bound 분석
# 1단계: 초기 진단
perf stat ./io_heavy_app
# → task-clock: 200ms, wall time: 5000ms → CPU 4% 사용
# → context-switches: 50,000 → 잦은 블로킹
# 2단계: syscall 추적
perf trace -s -- ./io_heavy_app 2>&1 | head -30
# → read: 12345 calls, 3.2s total, avg 260us
# → write: 5678 calls, 1.1s total, avg 194us
# → I/O syscall이 대부분의 시간 소비
# 3단계: Off-CPU 분석
offcputime-bpfcc -df -p $(pidof io_heavy_app) 10 > offcpu.folded
flamegraph.pl --color=io --title="Off-CPU" \
--countname=us < offcpu.folded > io_offcpu.svg
# → 어떤 함수에서 I/O 대기하는지 시각화
# 4단계: 블록 I/O 이벤트 분석
perf record -e block:block_rq_issue,block:block_rq_complete \
-a -- sleep 10
perf script | grep -c block_rq_issue
# → I/O 요청 수, 크기, 지연 시간 확인
# 5단계: 네트워크 I/O (웹서버 등)
perf trace -e 'sendto,recvfrom,accept4,epoll_wait' \
-p $(pidof nginx) -- sleep 5
사례 D: Lock Contention 분석
# 1단계: 초기 진단
perf stat ./multithreaded_app
# → task-clock: 16000ms (8 스레드 × 2초 wall time)
# → context-switches: 500,000 → 매우 높음
# → IPC: 0.3 → 대부분 대기 상태
# 2단계: 락 경합 분석 (BPF 기반, 가장 권장)
perf lock contention --use-bpf -a -- sleep 5
# 출력:
# contended total wait max wait avg wait type caller
# 45123 2.34 s 0.45 ms 51.8 us mutex worker_process+0x42
# 12345 0.89 s 0.12 ms 72.1 us spin hash_insert+0x18
# → worker_process의 mutex가 총 2.34초 대기
# 3단계: 콜스택과 함께 락 분석
perf lock contention --use-bpf -a --call-graph fp -- sleep 5
# → 어떤 호출 경로에서 락 경합이 발생하는지 확인
# 4단계: 특정 락의 contention 히스토그램
perf lock contention --use-bpf -a --lock-addr=0xffff... -- sleep 5
# 5단계: futex 기반 상세 분석
perf trace -e futex -p $(pidof multithreaded_app) -- sleep 5
# → FUTEX_WAIT/FUTEX_WAKE 패턴 확인
# → 특정 futex 주소에서의 경합 빈도 확인
종합 체크리스트
| 단계 | 명령 | 확인 사항 |
|---|---|---|
| 1. 전체 현황 | perf stat -d ./app | IPC, cache-miss, context-switch, task-clock vs wall time |
| 2. 병목 유형 | perf stat --topdown | Frontend/Backend/BadSpec/Retiring 비율 |
| 3. 핫스팟 | perf record -g → report | 가장 많은 샘플의 함수/모듈 |
| 4. 시각화 | Flamegraph | 콜스택 기반 핫스팟 시각화 |
| 5. 명령어 수준 | perf annotate | 핫 명령어, 캐시 미스 위치 |
| 6. Off-CPU | offcputime / perf sched | I/O/락 대기 시간 |
| 7. 검증 | perf diff / perf stat -r 5 | 최적화 전후 정량 비교 |
perf stat으로 시작하세요. 전체 현황 없이 바로 perf record에 뛰어들면 잘못된 방향으로 분석할 수 있습니다. IPC, task-clock, context-switches 3가지 지표만으로도 CPU/Memory/I/O/Lock 중 어떤 병목인지 빠르게 판별할 수 있습니다.
관련 문서
perf 서브시스템과 관련된 다른 주제를 더 깊이 이해하고 싶다면 다음 문서를 참고하세요.
참고 자료
커널 공식 문서
- perf-security — 커널 공식 문서 — perf_event_open 접근 제어, CAP_PERFMON, perf_event_paranoid sysctl 설정을 설명하는 공식 문서입니다
- Performance monitor support — 커널 공식 문서 — 아키텍처별 PMU 드라이버, 하드웨어 이벤트 지원 현황을 정리한 공식 문서입니다
- ARM CMN PMU — 커널 공식 문서 — ARM Coherent Mesh Network 인터커넥트 PMU 드라이버 사용법을 설명합니다
- ARM SPE — 커널 공식 문서 — ARM Statistical Profiling Extension(SPE)의 커널 지원과 perf 연동 방법을 설명합니다
- Event Tracing — 커널 공식 문서 — perf의 트레이스포인트 이벤트 백엔드인 커널 이벤트 트레이싱 인프라를 설명합니다
perf 위키 및 튜토리얼
- perf wiki — 공식 위키 — perf 도구의 공식 위키 페이지로, 기본 사용법부터 고급 기능까지 포괄적으로 다룹니다
- perf Tutorial — 공식 위키 — perf stat, record, report, annotate 등 핵심 명령어의 단계별 사용법을 설명하는 공식 튜토리얼입니다
- perf Features Tracker — 공식 위키 — perf 도구의 신규 기능 개발 현황과 로드맵을 추적하는 페이지입니다
Brendan Gregg의 perf 리소스
- perf Examples — Brendan Gregg — perf의 다양한 실전 사용 예제를 정리한 페이지로, CPU 프로파일링부터 메모리/네트워크 분석까지 포괄합니다
- CPU Flame Graphs — Brendan Gregg — perf record 데이터를 Flame Graph로 시각화하는 방법을 상세히 설명합니다
- Memory Flame Graphs — Brendan Gregg — perf mem과 PEBS를 활용한 메모리 접근 패턴 Flame Graph 생성 방법을 다룹니다
- Off-CPU Flame Graphs — Brendan Gregg — Off-CPU 분석과 perf sched를 결합한 대기 시간 시각화 기법을 설명합니다
- perf Counting — Brendan Gregg — perf stat의 카운팅 모드 동작 원리와 PMU 멀티플렉싱을 설명하는 블로그 글입니다
LWN.net 기사
- LWN: Perfcounters added to the mainline — perf_events(당시 perfcounters)가 Linux 2.6.31에 머지된 배경과 초기 설계를 다루는 기사입니다
- LWN: Secrets of the Ftrace function tracer — perf와 ftrace의 통합 트레이싱 아키텍처와 상호 보완적 활용 방법을 설명합니다
- LWN: perf events and tool design — perf 이벤트 시스템의 사용자 공간 인터페이스 설계 철학과 구현을 분석합니다
- LWN: Performance Counters for Linux — Ingo Molnar의 perf_event 서브시스템 제안 원문과 기술적 논의를 다룹니다
- LWN: The first satisfsatisfying satisfying perf tool release — perf 도구의 주요 기능 진화와 새로운 서브커맨드 추가를 정리합니다
- LWN: User-space Software Events with perf — USDT(User Statically Defined Tracing)와 perf의 사용자 공간 이벤트 지원을 설명합니다
컨퍼런스 발표 및 기술 자료
- perf: Performance Analysis Tools for Linux — OLS 2010 — Arnaldo Carvalho de Melo의 perf 도구 설계와 PMU 추상화 계층을 다룬 발표 자료입니다
- Linux Performance Tools — Brendan Gregg, USENIX/LISA 2019 — perf를 포함한 리눅스 성능 분석 도구 전반을 다룬 강연입니다
- Linux Systems Performance — Brendan Gregg, QCon 2019 — perf stat/record 기반 시스템 성능 분석 방법론을 체계적으로 설명하는 발표입니다
- Linux perf_event Features and Overhead — CERN — perf_event의 오버헤드 측정과 CERN 환경에서의 대규모 프로파일링 경험을 공유하는 자료입니다
커널 소스 참조
- kernel/events/core.c — Bootlin Elixir — perf 서브시스템의 핵심 코어 파일로, perf_event_open 시스템 콜과 이벤트 스케줄링 로직이 구현되어 있습니다
- include/uapi/linux/perf_event.h — Bootlin Elixir — perf_event_attr 구조체, 이벤트 타입 상수 등 사용자 공간 API 정의를 포함하는 UAPI 헤더입니다
- arch/x86/events/ — Bootlin Elixir — Intel/AMD x86 PMU 드라이버 코드로, PEBS, LBR, TopDown 메트릭 등의 구현을 확인할 수 있습니다
- tools/perf/ — Bootlin Elixir — perf 사용자 공간 도구의 전체 소스 코드로, 서브커맨드별 구현을 확인할 수 있습니다