perf 서브시스템

리눅스 커널의 성능 프로파일링(Profiling) 프레임워크 perf를 PMU 하드웨어와 perf_event_open() 내부부터 다룹니다. 이 문서는 perf 도구 자체의 구조, 샘플링 모델, PEBS/IBS/SPE, c2c·mem·lock·sched 서브커맨드, TMA Top-Down, 커널 심볼(Kernel Symbol) 해석을 중심으로 설명하고, 전체 최적화 워크플로는 별도 문서로 분리합니다.

전제 조건: 성능 최적화 문서에서 계측 기반 워크플로를 먼저 읽으세요. 이 문서는 "어떤 병목(Bottleneck)을 찾을 것인가"보다 "perf가 내부적으로 무엇을 측정하고 어떤 옵션으로 해석하는가"에 더 집중합니다.
일상 비유: perf는 엔진 분해 계측기에 가깝습니다. 차량이 느린 이유를 찾을 때 시승 감각만 보는 것이 아니라 실린더 압력, 점화 타이밍, 연료 분사량을 계측하듯이, perf도 함수·명령어·카운터 수준에서 병목을 해부합니다.
관련 문서: 전체 튜닝 절차는 성능 최적화, 함수 추적은 ftrace / Tracepoints, Intel 전용 언코어/전력 계측은 Intel PCM, 실시간(Real-time) 지연(Latency) 분석은 RTLA / timerlat / osnoise에서 이어집니다.

핵심 요약

perf 서브시스템을 이해하기 위한 6가지 핵심 개념입니다.

  1. perf_event_open() 시스템 콜(System Call)이 핵심 진입점(Entry Point)
    perf의 모든 기능은 perf_event_open() 시스템 콜을 통해 커널에 이벤트를 등록하는 것에서 시작합니다. 유저스페이스의 perf 도구(record, stat, top 등)는 모두 이 시스템 콜의 래퍼입니다. 반환된 파일 디스크립터(File Descriptor)로 이벤트를 제어하고 mmap으로 데이터를 수신합니다.
  2. PMU 하드웨어 카운터로 사이클/캐시(Cache)미스/분기예측 측정
    CPU 내장 PMU(Performance Monitoring Unit)는 사이클, 명령어 수, 캐시 미스, 분기 예측(Branch Prediction) 실패 등을 하드웨어 수준에서 카운트합니다. Intel은 4-8개, ARM은 6-31개의 범용 카운터를 제공하며, 카운터 수를 초과하면 시분할 멀티플렉싱으로 보정합니다.
  3. 샘플링(record) vs 카운팅(stat) 두 가지 모드
    perf stat은 이벤트를 카운팅하여 총 수치를 제공합니다 (오버헤드(Overhead) 거의 0%). perf record는 주기적으로 샘플을 수집하여 어떤 함수/명령어에서 이벤트가 발생했는지 위치 정보를 제공합니다. stat으로 전체 현황을 파악하고, record로 핫스팟을 찾는 것이 일반적 워크플로입니다.
  4. Flamegraph로 콜스택 병목 시각화
    perf record -g로 콜스택을 포함하여 수집한 뒤, perf scriptstackcollapse-perf.plflamegraph.pl 파이프라인(Pipeline)으로 SVG Flamegraph를 생성합니다. 넓은 프레임이 성능 병목이며, x축은 알파벳 순서(시간 아님), y축은 콜스택 깊이입니다.
  5. PEBS/IBS/SPE로 정밀 샘플링 (스키드 제거)
    일반 PMI 인터럽트(Interrupt)는 이벤트 발생 후 수~수십 명령 뒤에 처리됩니다(스키드). Intel PEBS, AMD IBS, ARM SPE는 하드웨어가 직접 정확한 IP와 메모리 주소를 기록하여 스키드를 제거합니다. :pp 수정자로 활성화합니다.
  6. 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-migrationsCPU 간 이동 횟수높으면 캐시 효율 저하, taskset 고려
page-faults페이지 폴트(Page Fault) 횟수minor(정상) vs major(디스크 I/O) 구분 필요
cyclesCPU 사이클 수주파수 스케일링(Frequency Scaling) 영향 받음, GHz로 환산 표시
instructions실행된 명령어 수IPC = instructions / cycles
branches분기 명령어 수전체 명령어 중 분기 비율 확인
branch-misses분기 예측 실패 수5% 이상이면 분기 최적화 고려

흔한 실수와 해결

권한 부족 (paranoid): "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에 권한을 부여하세요.
심볼 없음 ([unknown]): 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계층으로 구성됩니다. 각 계층의 역할과 데이터 흐름을 상세히 살펴봅니다.

Layer 1: 유저스페이스 (User Space) perf record perf stat perf top perf report perf probe perf script perf sched/mem libperf API (libbpf 연동) mmap() 링 버퍼 접근 perf.data 파일 I/O perf_event_open() / ioctl() / mmap() / read() 시스템 콜 Layer 2: 커널 perf_event 서브시스템 perf_event_context per-task / per-cpu pinned + flexible RB-tree perf_event attr, pmu, count overflow_handler perf_buffer ring buffer (mmap) data_head / data_tail 이벤트 스케줄러 멀티플렉싱 enabled/running 보정 PMI 핸들러 NMI / hrtimer 샘플 레코드 생성 tracepoint / kprobe / uprobe 등록 BPF 프로그램 연결 (perf_event) cgroup / namespace 기반 필터링 Layer 3: PMU 드라이버 (아키텍처별) x86_pmu (Intel/AMD) arch/x86/events/ arm_pmu drivers/perf/ riscv_pmu drivers/perf/ sw_perf_event kernel/events/ uncore_pmu (Intel) arch/x86/events/intel/ Layer 4: 하드웨어 PMU / CPU Intel Core PMU IA32_PERFEVTSELx IA32_PMCx (4-8 GP) PEBS + PT + LBR AMD PMU PerfEvtSel / PerfCtr 6 GP (Zen4: +3 fixed) IBS Fetch + IBS Op ARM PMUv3 PMCR_EL0 6-31 GP + 1 fixed SPE + TRBE RISC-V HPM mhpmcounter3-31 29 GP + 3 fixed Sscofpmf (v1.0) Uncore IMC, CHA QPI/UPI PCIe 카운터

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__u64read() 시 반환할 데이터 형식
disabled1 bit1이면 비활성 상태로 생성 (ioctl ENABLE로 시작)
inherit1 bit1이면 자식 프로세스도 카운팅
pinned1 bit1이면 항상 카운터 점유 (멀티플렉싱 제외)
exclusive1 bit1이면 카운터를 독점 (다른 이벤트 배제)
exclude_user1 bit유저스페이스 이벤트 제외
exclude_kernel1 bit커널 이벤트 제외
exclude_hv1 bit하이퍼바이저 이벤트 제외
precise_ip2 bits정밀도 수준 (0=best effort, 1-3=PEBS/IBS/SPE)
use_clockid1 bit타임스탬프에 사용할 clock 선택
aux_output1 bitAUX 버퍼로 출력 (Intel PT용)

커널 내부 이벤트 처리 흐름

perf_event_open() 커널 처리 경로 1. copy_from_user perf_event_attr 복사 2. perf_event_alloc 이벤트 구조체 할당 3. pmu→event_init PMU 드라이버 검증 4. 권한 검사 paranoid / CAP_PERFMON 5. context 연결 per-task 또는 per-cpu 6. 그룹 관리 leader/sibling 설정 7. fd 할당 anon_inode_getfd 8. return fd 유저에게 fd 반환 샘플링 시 데이터 흐름 (perf record) PMU 오버플로 counter == 0 NMI / PMI 인터럽트 발생 perf_event_overflow 샘플 구성 ring_buffer perf_output_sample 유저 poll() mmap 읽기 카운팅 시 데이터 흐름 (perf stat) PMU 카운터 누적 (HW) context_switch 시 save/restore read(fd) → 최종 값 반환
커널 소스 탐색 포인터: perf 서브시스템 코어는 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 record perf stat perf report perf top perf script perf_event_open() / mmap() 커널 perf_event 서브시스템 perf_event_context per-task / per-cpu perf_event 이벤트 인스턴스 ring_buffer mmap 공유 버퍼 pmu→{add,del,read} PMU 드라이버 콜백 하드웨어 PMU x86: IA32_PERFEVTSELx ARM: PMCR_EL0 RISC-V: HPM CSR Software: context-switch, page-fault
/* 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
성능 문제 발생? CPU 바운드? 메모리? I/O? 락 경합? perf stat perf record perf mem perf c2c perf trace sched perf lock → Flamegraph 시각화 → TMA --topdown 분석
심볼 소스제공 정보필요한 설정
/proc/kallsyms함수명, 주소 (커널)항상 사용 가능 (kptr_restrict=0 필요)
vmlinux함수명 + 소스 위치 + 인라인CONFIG_DEBUG_INFO=y + debuginfo 패키지
debuginfod원격 디버그 정보 자동 다운로드DEBUGINFOD_URLS 환경변수
Build ID 캐시바이너리 버전 매칭~/.debug/.build-id/ 자동 관리
최적 심볼 설정: 1) debuginfod 환경변수 설정 (자동 다운로드), 2) perf_event_paranoid=-1 (커널 심볼 접근), 3) 유저 앱은 -fno-omit-frame-pointer로 컴파일 (정확한 콜스택), 4) perf buildid-cache로 오프라인 분석용 캐시 관리.
kptr_restrict 주의: /proc/sys/kernel/kptr_restrict가 1 이상이면 /proc/kallsyms의 주소가 모두 0으로 표시됩니다. perf가 커널 심볼을 해석하려면 sysctl -w kernel.kptr_restrict=0 또는 root 권한이 필요합니다.

커널 컴파일 옵션 가이드

perf의 기능을 최대한 활용하려면 적절한 커널 컴파일 옵션이 필요합니다. 아래 표는 각 기능별 필수/권장 커널 옵션을 정리합니다.

기능커널 옵션필수/권장설명
기본 perfCONFIG_PERF_EVENTS=y필수perf_event 서브시스템 활성화
트레이스포인트CONFIG_TRACEPOINTS=y필수sched:*, block:* 등 이벤트
kprobeCONFIG_KPROBES=y필수perf probe 동적 트레이스포인트
uprobeCONFIG_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 소스 매핑
BTFCONFIG_DEBUG_INFO_BTF=y권장BPF 타입 정보, CO-RE
ORC unwinderCONFIG_UNWINDER_ORC=y권장커널 콜스택 정확도 향상
ftrace 통합CONFIG_FTRACE=y권장perf ftrace 서브커맨드
cgroupCONFIG_CGROUP_PERF=y선택컨테이너별 프로파일링
PEBS/LBR하드웨어 지원 필요-Intel Core 2+, :pp precise
Intel PTCONFIG_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 주소 필터 수
배포판 기본 설정: Ubuntu/Fedora 등 주요 배포판의 기본 커널은 CONFIG_PERF_EVENTS=y, CONFIG_TRACEPOINTS=y, CONFIG_KPROBES=y, CONFIG_UPROBES=y가 기본 활성화되어 있습니다. 다만 CONFIG_LOCK_STATCONFIG_DEBUG_INFO는 비활성화된 경우가 많으므로, 고급 분석이 필요하면 확인이 필요합니다.

PMU (Performance Monitoring Unit) 아키텍처

PMU는 CPU 내장 하드웨어로, 특정 이벤트(사이클, 캐시 미스, 분기 예측 실패 등)를 카운터로 집계합니다.

아키텍처PMU범용 카운터고정 카운터정밀 샘플링
x86 IntelIA32_PMC0-74-8개3개 (cycles, instructions, ref-cycles)PEBS
x86 AMDMSR C001_0200-020B6개없음 (Zen4+: 3개)IBS
ARMPMU v3 (PMCR_EL0)6-31개1개 (cycle counter)SPE
RISC-VHPM 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.typeconfig 필드의 조합으로 식별됩니다.

type설명예시
PERF_TYPE_HARDWARECPU 하드웨어 카운터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_RAWPMU 전용 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 IDL1-dcacheL1 데이터 캐시
L1-icacheL1 명령어 캐시
LLCLast Level Cache (L3)
dTLB데이터 TLB
iTLB명령어 TLB
branch분기 예측
nodeNUMA 노드 메모리
연산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_event_mmap_page + Ring Buffer 구조 페이지 0: 메타데이터 perf_event_mmap_page version, compat_version data_head (커널 쓰기 위치) data_tail (유저 읽기 위치) data_offset, data_size time_enabled, time_running 페이지 1~N: 링 버퍼 데이터 영역 샘플 #1 header(type,size) IP,TID,TIME,... 샘플 #2 header(type,size) IP,TID,TIME,... (빈 공간) MMAP 이벤트 (sideband) LOST 이벤트 (오버플로 시) data_head ↓ (커널이 원자적 증가) data_tail ↓ (유저가 업데이트) 샘플 레코드 내부 구조 (PERF_RECORD_SAMPLE) perf_event_header type=9, misc, size SAMPLE_IP SAMPLE_TID SAMPLE_TIME SAMPLE_ADDR CALLCHAIN BRANCH_STACK PERF_SAMPLE_* 플래그 (sample_type 비트마스크) PERF_SAMPLE_IP (0x001) — 명령어 주소 PERF_SAMPLE_TID (0x002) — PID/TID PERF_SAMPLE_TIME (0x004) — 타임스탬프 PERF_SAMPLE_ADDR (0x008) — 데이터 주소 PERF_SAMPLE_CALLCHAIN (0x020) — 콜스택 PERF_SAMPLE_CPU (0x080) — CPU 번호 PERF_SAMPLE_PERIOD (0x100) — 샘플 주기 PERF_SAMPLE_BRANCH_STACK (0x800) — LBR PERF_SAMPLE_WEIGHT (0x400) — 가중치 PERF_SAMPLE_DATA_SRC (0x8000) — 데이터 소스 PERF_SAMPLE_PHYS_ADDR (0x80000) — 물리 주소 PERF_SAMPLE_CGROUP (0x200000) — cgroup

PERF_SAMPLE_* 플래그 상세

perf_event_attr.sample_type 비트마스크로 샘플에 포함할 정보를 선택합니다. 플래그가 많을수록 샘플 크기가 커져 ring buffer 소비가 빨라지므로, 필요한 정보만 선택해야 합니다.

플래그비트샘플에 추가되는 데이터용도
PERF_SAMPLE_IP0x001u64 ip (명령어 포인터)핫스팟 함수/명령어 식별
PERF_SAMPLE_TID0x002u32 pid, u32 tid프로세스/스레드별 분류
PERF_SAMPLE_TIME0x004u64 timestamp시간 기반 분석, 타임라인
PERF_SAMPLE_ADDR0x008u64 data_addr메모리 접근 주소 (perf mem/c2c)
PERF_SAMPLE_READ0x010그룹 카운터 값들샘플 시점의 모든 카운터 스냅샷
PERF_SAMPLE_CALLCHAIN0x020u64 nr + u64[] ips콜스택 (Flamegraph)
PERF_SAMPLE_ID0x040u64 id (이벤트 식별자)멀티 이벤트 구분
PERF_SAMPLE_CPU0x080u32 cpu, u32 resCPU별 분석
PERF_SAMPLE_PERIOD0x100u64 period가변 주기 보정
PERF_SAMPLE_WEIGHT0x400u64 weight (지연 사이클)메모리 레이턴시 분석
PERF_SAMPLE_DATA_SRC0x8000u64 data_src 인코딩L1/L2/L3/DRAM 히트 구분
PERF_SAMPLE_PHYS_ADDR0x80000u64 phys_addrNUMA 노드 분석
PERF_SAMPLE_CGROUP0x200000u64 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);
}
LOST 이벤트 방지: ring buffer가 가득 차면 커널이 샘플을 버리고 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 = 시스템 전체
PMU 오버플로 PMI / NMI perf_event_overflow 샘플 레코드 생성 ring_buffer mmap 공유 메모리 perf record poll() + read perf.data

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
IPC 해석: IPC(Instructions Per Cycle)가 1.0 이하면 메모리 바운드 또는 분기 예측 실패 의심, 2.0 이상이면 CPU 효율이 좋은 코드입니다. 최신 x86은 이론적 최대 4-6 IPC입니다.

이벤트 그룹핑과 메트릭 계산

정확한 비율 계산(캐시 미스율, 분기 미스율 등)을 위해서는 분자와 분모 이벤트가 정확히 같은 시간에 측정되어야 합니다. {}로 그룹을 묶으면 커널이 동시 측정을 보장합니다.

# 이벤트 그룹핑 — {} 안의 이벤트는 동시 측정 보장
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을 즉시 확인할 수 있습니다.

TMA (Top-Down Microarchitecture Analysis) Level 1 전체 파이프라인 슬롯 (100%) = Issue Width x Cycles Retiring (38%) 유용한 작업 완료 Bad Spec 12% Frontend 15% Backend Bound (35%) 주 병목 목표: 가능한 높게 실제 유용한 명령어 비율 100%에 가까울수록 이상적 분기 예측 실패 잘못된 추측 실행 후 폐기 → likely/unlikely, branchless 명령어 공급 부족 I-cache 미스, ITLB 미스 → PGO, 코드 크기 축소 실행 유닛/메모리 정체 D-cache/LLC 미스, 포트 포화 → 데이터 지역성, 프리페치 Level 2 드릴다운 (toplev -l2) Branch Mispred Machine Clears Fetch Latency Memory Bound Core Bound Level 3 드릴다운 (toplev -l3) L1 Bound L2 Bound L3 Bound perf stat --topdown (Level 1) → toplev -l3 (Level 3) → perf record (핫스팟) → perf annotate (명령어)
# 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
TopDown 워크플로 요약: 1) 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 reportperf.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 ObjectDSO (라이브러리/바이너리)[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 성능 팁: 대용량 perf.data (수 GB)에서 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_PATHperf 실행 경로/usr/lib/linux-tools/$(uname -r)
PERF_BUILDID_DIRBuild ID 캐시 디렉토리~/.debug (기본값)
PERF_PAGERperf 출력 페이저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어노테이트 모드 토글
dDSO 필터 설정
H스레드별 표시 토글
K커널 심볼 표시/숨김
U유저 심볼 표시/숨김
z제로 카운트 항목 숨김
E이벤트 선택
q종료
perf top 활용 시나리오: 1) 서버 부하 급증 시 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는 콜스택 프로파일링 결과를 시각화하는 표준 방법입니다.

perf record -g → perf.data perf script → 텍스트 스택 stackcollapse-perf.pl → 접힌 스택 flamegraph.pl → SVG
# 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 해석 방법

Flame Graph 구조와 해석 가이드 all (100%) main (58%) worker_thread (39%) process_data (36%) io_submit (20%) compute_hash (29%) parse_input (26%) sha256_block (26%) tokenize (17%) _sha256_transform 콜스택 깊이 (아래→위) X축 = 알파벳 순서 (시간 순서 아님!) | 프레임 너비 = 샘플 비율 핫스팟! 넓은 프레임 = 높은 CPU 사용 최적화 대상 후보 넓은 프레임 → CPU 시간 많이 사용 | 탑 프레임 → 직접 실행 (self time) | 중간 프레임 → 호출 경로 (inclusive time)
해석 요소의미조치
넓은 프레임해당 함수(및 하위 호출)가 전체 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 생성 (검색, 줌, 필터 가능)
Flame Graph 해석 핵심 규칙: 1) 가장 넓은 프레임이 최우선 최적화 대상, 2) X축은 시간이 아니므로 좌우 위치에 의미를 부여하지 말 것, 3) "plateau"(넓고 평평한 프레임)가 있으면 해당 함수가 직접 CPU를 소비하는 것, 4) 커널과 유저 프레임 경계에서 시스템 콜 오버헤드 확인, 5) [unknown]이 많으면 심볼 문제 해결이 먼저.

perf probe / 동적 트레이스포인트

perf probe는 커널/유저 함수에 동적 트레이스포인트를 추가하여 kprobe/uprobe를 perf 이벤트로 사용합니다. DWARF 디버그 정보를 활용하여 함수 인자, 지역 변수, 구조체 필드까지 캡처할 수 있으며, SDT(Statically Defined Tracing) 프로브도 지원합니다.

perf probe 유형별 동작 원리 kprobe (커널 함수) 함수 진입점에 int3 삽입 trap → 핸들러 실행 → 원래 명령 실행 vmlinux 디버그 정보로 변수 접근 perf probe --add tcp_sendmsg uprobe (유저 함수) ELF 바이너리에 int3 삽입 COW 메모리 매핑으로 프로세스별 독립 debuginfo 또는 DWARF로 변수 접근 perf probe -x /usr/bin/app --add func SDT (정적 프로브) 바이너리에 미리 정의된 프로브 포인트 USDT (DTrace 호환) + SystemTap 프로브 ELF .note.stapsdt 섹션에 메타데이터 perf probe --add sdt_libc:malloc kretprobe (함수 반환) 리턴 값, 실행 시간 측정 가능 uretprobe (유저 함수 반환) malloc/free 반환값 추적 등 변수/필드 접근 DWARF로 인자, 지역변수, 구조체 접근 perf probe 실전 워크플로 1. --vars 변수 확인 2. --add 프로브 등록 3. record 이벤트 수집 4. script 분석/출력 5. --del 프로브 제거
# 커널 함수에 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
SDT 활용 범위: glibc (malloc/free), Python (function entry/return), Ruby (method entry), MySQL (query start/done), PostgreSQL (query execute), JVM (GC, thread start), Node.js 등 주요 런타임이 SDT 프로브를 제공합니다. readelf -n binary | grep stapsdt로 지원 여부를 확인하세요. SDT는 kprobe/uprobe와 달리 바이너리 개발자가 의도적으로 배치한 포인트이므로 안정성이 높고 ABI가 유지됩니다.

perf sched 상세

스케줄러(Scheduler) 성능 분석 도구입니다. sched:sched_switch, sched:sched_wakeup 등의 트레이스포인트를 수집하여 컨텍스트 스위치 빈도, wakeup-to-running 레이턴시, CPU 활용 패턴을 분석합니다.

perf sched 서브커맨드와 분석 대상 perf sched record sched:* 트레이스포인트 수집 sched latency wakeup→running 지연 sched timehist 시간순 이벤트 타임라인 sched map CPU별 태스크 맵핑 sched:sched_switch 이벤트 흐름 Task A (running) sched_switch A: sleep/blocked Task B (running) sched_wakeup(A) A: runqueue (대기 지연) sched_switch Task A (running) Off-CPU 시간 (blocked) 스케줄링 지연 (runqueue) 수집되는 트레이스포인트 sched:sched_switch sched:sched_wakeup sched:sched_migrate_task sched:sched_stat_runtime/wait/sleep sched latency: wakeup→switch 지연 | timehist: 이벤트별 타임라인 | map: CPU↔task 매핑 perf sched record는 모든 sched:* 이벤트를 수집 → 각 서브커맨드가 다른 관점으로 분석
# 스케줄러 이벤트 기록 (모든 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 delaylatency --sort max다른 태스크가 CPU 독점우선순위 조정, CPU 격리
높은 wait timetimehist (wait 컬럼)I/O, 락, sleep 대기Off-CPU 분석, 락 최적화
잦은 migrationtimehist (CPU 변경)CPU 간 이동 → 캐시 효율 저하taskset, CPU affinity
높은 context-switcheslatency (Switches)과도한 스레드/프로세스스레드 풀 크기 조정
불균형 CPU 사용map특정 CPU에 편중NUMA 밸런싱, IRQ 분산
perf sched 오버헤드: 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) 프로토콜에 의한 성능 저하를 탐지합니다.

False Sharing 발생 메커니즘 (MESI 프로토콜) 캐시라인 64 바이트 counter (8B) flag (8B) padding (48B) CPU 0 (Core 0) counter++ 반복 수행 캐시라인 → Modified invalidate 전송 → CPU 1 (Core 1) flag = true 반복 수행 캐시라인 → Modified ← invalidate 전송 HITM 핑퐁 False Sharing 비용 Local HITM: ~40 사이클 지연 Remote HITM (NUMA): ~200 사이클 지연 해결: 캐시라인 분리 counter → 캐시라인 0 | flag → 캐시라인 1 __cacheline_aligned_in_smp 해결: per-CPU 데이터 DEFINE_PER_CPU(unsigned long, counter) 각 CPU 전용 메모리 → 공유 없음
# 메모리 로드/스토어 프로파일링
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) = 같은 캐시라인 */
HITM 지표: 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 없이도 락 분석이 가능합니다.

perf lock 분석 모드 비교 전통적 모드: perf lock record/report CONFIG_LOCK_STAT=y 필요 lock:lock_acquire / lock:lock_release 트레이스포인트 높은 오버헤드 (모든 락 이벤트 기록) 오프라인 분석 (perf.data) BPF 모드: perf lock contention --use-bpf CONFIG_LOCK_STAT 불필요 (v5.19+) BPF 프로그램이 커널에서 직접 집계 낮은 오버헤드 (커널 내 집계) 실시간 분석 (집계 결과만 전송) 추적 가능한 락 유형 mutex sleeping lock spinlock busy-wait lock rwlock 읽기/쓰기 분리 rwsem sleeping rw lock percpu-rwsem per-CPU rw semaphore 주요 분석 지표 contended (경합 횟수) | total wait (총 대기 시간) | max wait (최대 대기) | avg wait (평균 대기) | type | caller (호출자)
# === 전통적 모드 (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 빈번사용자 락 그래뉼래리티 조정
perf lock contention --use-bpf 전제 조건: 커널 v5.19+, 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 tracestrace
메커니즘트레이스포인트 (커널 내)ptrace 시스템 콜
오버헤드낮음 (~2-5%)높음 (~20-100%)
시스템 전체-a로 가능불가 (프로세스별만)
통계 요약-s 옵션-c 옵션
PMU 통합가능 (이벤트 동시 수집)불가
컨테이너cgroup 필터 가능nsenter 필요
프로덕션안전 (저오버헤드)위험 (성능 저하 심각)
perf trace 활용 팁: 프로덕션 환경에서 시스템 콜 추적이 필요하면 strace 대신 반드시 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 서브시스템의 핵심 커널 자료구조입니다.

task_struct → perf_event_ctxp[] perf_event_context pinned_groups (RB-tree) flexible_groups (RB-tree) perf_event attr (이벤트 설정) pmu (PMU 드라이버) count (카운트 값) overflow_handler perf_buffer mmap 공유 ring buffer per-CPU context cpu_pmu_context
/* 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_file_header) magic: "PERFFILE2" header size attr section offset data section offset event types adds_features pipe mode 속성 섹션 (perf_event_attr[] + event IDs) 이벤트별 타입, config, sample_type, read_format 등 데이터 섹션 (이벤트 레코드 스트림) RECORD_MMAP2 RECORD_COMM RECORD_SAMPLE RECORD_SAMPLE RECORD_LOST RECORD_SAMPLE RECORD_EXIT 시간순 이벤트 스트림: SAMPLE(프로파일) + sideband(MMAP, COMM, FORK, EXIT, LOST) 피처 섹션 (Feature Headers) BUILD_ID HOSTNAME OS_RELEASE CPUDESC TOTAL_MEM CMDLINE CPU_TOPOLOGY NUMA_TOPOLOGY
# 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_CGROUPcgroup 이벤트cgroup 기반 필터링 (v5.7+)
PERF_RECORD_AUXAUX 버퍼 데이터Intel PT, ARM SPE 페이로드
대용량 perf.data 관리: 장시간 프로파일링이나 높은 주파수 샘플링은 수 GB의 perf.data를 생성합니다. 1) --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로 전달되어 정확도가 높습니다.

기술아키텍처정밀도설명
PEBSIntelIP 정확하드웨어가 샘플을 PEBS 버퍼(Buffer)에 기록, 스키드(skid) 최소화
IBSAMDIP + 주소 정확명령어/연산 기반 정밀 샘플링, 메모리 주소 포함
SPEARMIP + 주소 + 레이턴시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 Record Layout (Enhanced PEBS) GP Registers RAX, RBX, RCX, RDX RSI, RDI, RBP, RSP R8-R15 RFLAGS offset: 0x00-0x90 Eventing IP 정확한 이벤트 발생 주소 Data Linear Address 메모리 접근 가상 주소 Data Source Encoding L1/L2/L3/DRAM/Remote Latency Value 메모리 접근 지연(사이클) Adaptive PEBS (Ice Lake+): 선택적 필드 기록 PEBS_DATA_CFG MSR로 GP Regs / Memory Info / Counters / LBR 선택 → 오버헤드 최소화 PMU 오버플로 HW → PEBS 버퍼 기록 PMI (버퍼 full) 커널 ring buffer

PEBS 레코드 필드

필드오프셋(Offset)크기설명
GP Registers0x00128BRAX-R15, RFLAGS — 이벤트 시점 레지스터(Register) 상태
Eventing IP0x808B이벤트 발생 정확한 명령어 주소 (스키드 없음)
Data Linear Address0x988B메모리 로드/스토어 대상 가상 주소(Virtual Address)
Data Source0xA08B데이터가 어디서 왔는지 (L1/L2/L3/DRAM/Remote)
Latency Value0xA84B메모리 접근 지연 시간 (코어 사이클 단위)
TSC0xB08B타임스탬프 카운터 (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);
}
PEBS 세대별 발전: Nehalem에서 기본 PEBS 도입, Haswell에서 Data LA/Source 추가 (정밀 메모리 프로파일링), Ice Lake에서 Adaptive PEBS로 선택적 기록 + 멀티 카운터 PEBS (여러 이벤트를 동시에 PEBS로 수집). Sapphire Rapids에서는 PEBS-via-PT (Intel PT를 통한 PEBS 전달)가 추가되었습니다.

AMD IBS

AMD IBS(Instruction-Based Sampling)는 PEBS와 달리 명령어 파이프라인의 특정 단계에서 샘플을 수집합니다. IBS FetchIBS Op 두 가지 모드가 있으며, 각각 명령어 페치 단계와 실행(Op) 단계에서 서로 다른 정보를 제공합니다.

IBS Fetch vs IBS Op

속성IBS FetchIBS Op
샘플 지점명령어 페치 단계명령어 실행 완료 단계
MSRIBS_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 이벤트, 더 많은 필드
IBS vs PEBS 핵심 차이: PEBS는 이벤트 카운터 오버플로 시점에 샘플하지만, IBS는 파이프라인의 특정 단계(Fetch/Op)에서 무작위로 선택된 명령어를 추적합니다. 따라서 IBS는 이벤트 종류에 관계없이 항상 정확한 IP와 메모리 주소를 제공하며, PEBS는 특정 이벤트에 대해서만 동작합니다.

ARM64 SPE

ARM SPE(Statistical Profiling Extension)는 ARMv8.2에서 도입된 하드웨어 기반 정밀 프로파일링 기술입니다. 파이프라인을 통과하는 연산을 하드웨어가 직접 필터링하고 기록하므로, PMI 기반 샘플링과 달리 스키드가 없고 메모리 접근 주소, 지연 시간, 데이터 소스까지 정확하게 제공합니다.

SPE Sample Record (패킷 형식) Address PC + Data VA Counter Total Latency Type Load/Store/Br Data Source L1/L2/Peer/DRAM Events TLB miss, etc. Context EL, NS, TID SPE 하드웨어 필터링 파이프라인 연산 선택 (1/N) 타입 필터 load/store/br 이벤트 필터 min_latency SPE 버퍼 기록 PMI → 커널 하드웨어 필터링으로 관심 있는 연산만 기록 → 오버헤드 최소화

SPE 필터 설정

필터perf 파라미터설명
로드 필터load_filter=1메모리 로드 연산만 기록
스토어 필터store_filter=1메모리 스토어 연산만 기록
분기 필터branch_filter=1분기 명령어만 기록
최소 지연min_latency=NN 사이클 이상 지연된 이벤트만 기록
타임스탬프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 등 지원
SPE의 핵심 장점: SPE는 PEBS/IBS와 달리 하드웨어가 파이프라인 내에서 직접 필터링하므로, 관심 없는 연산은 아예 기록하지 않습니다. 이로 인해 프로파일링 오버헤드가 극도로 낮으며, 프로덕션 환경에서도 상시 모니터링이 가능합니다. 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커널 접근 불가, 유저스페이스만 (기본값, 일부 배포판)
3perf_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
PLATYPUS 공격: RAPL/perf 에너지 카운터를 이용한 사이드 채널 공격(CVE-2020-8694)으로, 많은 배포판이 perf_event_paranoid=2 이상을 기본값으로 설정합니다.

실전 튜닝 가이드

워크로드 유형별 perf 분석 전략입니다.

TMA (Top-Down Microarchitecture Analysis) 전체 파이프라인 슬롯 Frontend Bound Backend Bound Bad Speculation Retiring (유용) Memory Bound Core Bound toplev.py (pmu-tools) 또는 perf stat --topdown 으로 TMA Level 1-3 분석
# 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까지 점진적으로 병목 원인을 좁혀갑니다.

Pipeline Slots (100%) Frontend Bound Backend Bound Bad Speculation Retiring Fetch Latency Fetch Bandwidth Memory Bound Core Bound L1 Bound L2 Bound L3 Bound DRAM Bound Store Bound ICache Miss ITLB Miss Level 1 → Level 2 → Level 3 순으로 병목 원인을 좁혀감 perf stat --topdown (Level 1) / toplev.py -l3 (Level 3까지)

TMA Level 분해

Level카테고리의미대표 원인
L1Frontend Bound명령어 공급 부족I-cache 미스, 분기 예측기 워밍업
Backend Bound실행 유닛/메모리 정체캐시 미스, 실행 포트 포화
L1Bad Speculation잘못된 추측 실행분기 예측 실패, 머신 클리어
Retiring유용한 작업 (목표: 높을수록 좋음)정상 실행, 마이크로코드 시퀀스
L2Fetch Latency / Bandwidth프론트엔드 세분화ITLB 미스, DSB/MITE 전환
Memory / Core Bound백엔드 세분화L1/L2/L3/DRAM 미스, 포트 경합
L3L1/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), 프로파일 기반 최적화
TMA 실전 워크플로: 1) perf stat --topdown으로 Level 1 확인, 2) 가장 높은 카테고리 방향으로 toplev -l3 드릴다운, 3) Level 3에서 특정 원인 식별, 4) 해당 이벤트로 perf record 프로파일링, 5) perf annotate로 핫 명령어 확인.
TMA 제한 사항: TMA는 Intel Core/Xeon 전용 방법론입니다. AMD는 유사한 분석을 위해 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 바운드나 락 대기 같은 병목을 발견할 수 없으므로, 두 분석을 결합하면 전체 실행 시간의 병목을 빠짐없이 파악할 수 있습니다.

On-CPU Off-CPU (blocked) I/O wait, lock, sleep Runqueue On-CPU Off-CPU sched_switch wakeup sched_switch 시간 → perf record로 분석 Off-CPU 분석 필요

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 스크립트유연한 커스터마이징스크립팅 지식 필요
Off-CPU 분석 활용 시나리오: 1) 웹서버가 CPU 사용률 낮은데 응답 느림 → I/O 대기 확인, 2) 데이터베이스 쿼리 지연 → 락 대기 확인, 3) 마이크로서비스 P99 지연 → 네트워크 대기/runqueue 대기 확인. On-CPU Flamegraph에서 병목이 보이지 않으면 반드시 Off-CPU 분석을 병행하세요.
오버헤드 주의: 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()
NUMA 토폴로지(Topology) 확인: 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 프로그램이 커널에서 데이터를 처리하므로 유저스페이스 전송 오버헤드가 극적으로 줄어들어, 프로덕션 환경에서도 안전하게 사용 가능합니다.

perf + BPF 연동 아키텍처 유저스페이스 perf record --filter BPF bpftrace perf_event 사용 libbpf app 커스텀 BPF BPF 출력 채널 perf_buffer 이벤트 전달 ring_buffer (5.8+ 권장) BPF 맵 집계 데이터 커널 perf_event + BPF 서브시스템 perf_event PMU 이벤트 / tracepoint BPF 프로그램 필터링 / 집계 / 변환 BPF 맵 (커널) hash / array / ringbuf bpf_perf_event_output 유저로 이벤트 전송 트리거 커널 내 필터링 → 저오버헤드 커널 내 집계 → 적은 데이터 전송 프로덕션 안전 (verifier 검증) perf vs BPF 도구 역할 분담 perf: 수집 + 오프라인 분석 (perf.data) + report/annotate 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 비교

특성perfbpftracebcc (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+
실전 조합 전략: 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게스트 VMcycles:G
:p정밀(precise) level 1cycles: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
include/exclude 결합: 수정자는 결합 가능합니다. cycles:uk는 유저+커널 모두(하이퍼바이저 제외), cache-misses:kpp는 커널 전용 + PEBS 정밀 샘플링입니다. 수정자를 생략하면 기본적으로 :ukH (유저+커널+하이퍼바이저)입니다.

perf inject / buildid

perf injectperf.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 팁: 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
최적 그룹 구성 전략: IPC 계산에 필요한 {cycles,instructions}를 항상 첫 번째 pinned 그룹으로 묶으세요. 나머지 이벤트는 연관성 기준으로 그룹화하여 비율 계산의 정확도를 높입니다. 예: {L1-dcache-loads,L1-dcache-load-misses}를 묶으면 미스율 계산이 동시 측정으로 정확해집니다.
멀티플렉싱 함정: (xx.xx%) 표시가 50% 미만이면 스케일링 오차가 커집니다. 측정 시간을 늘리거나 (-- sleep 30), 이벤트 수를 줄이거나, 여러 번 나누어 측정하세요. 특히 짧은 워크로드(<1초)에서 멀티플렉싱은 심각한 오차를 유발합니다.

CI/CD 자동화

perf의 JSON 출력과 스크립팅 기능을 활용하면 CI/CD 파이프라인에서 성능 회귀를 자동으로 감지할 수 있습니다. 커밋별 성능 변화를 추적하고 임계값 초과 시 알림을 보내는 시스템을 구축합니다.

git push commit Build compile + test perf stat --json 출력 Compare baseline 비교 PASS FAIL Alert IPC / cache-miss 변화율이 임계값(예: 5%) 초과 시 FAIL → Slack/Email 알림

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 pipepipe 왕복 지연컨텍스트 스위치 오버헤드
mem memcpy메모리 복사 대역폭(Bandwidth)메모리 서브시스템 성능
futex hashfutex 해시(Hash) 충돌동기화 프리미티브 성능
numa memNUMA 노드간 대역폭NUMA 토폴로지 영향 확인
CI 성능 테스트 모범 사례: 1) 전용 베어메탈 머신 사용 (VM은 노이즈가 큼), 2) CPU 주파수 고정 (cpupower frequency-set -g performance), 3) 최소 3회 반복 측정, 4) 통계적 유의성 검증 (표준편차 확인), 5) 커밋별 결과를 시계열 DB에 저장하여 추세 모니터링.
클라우드 CI 환경 한계: GitHub Actions/GitLab CI 공유 러너에서는 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 HITM 이벤트 수집 perf c2c report 캐시라인 랭킹 Shared Data CL 변수/오프셋 식별 False Sharing True Sharing __cacheline _aligned 분리 데이터 구조 재설계 Remote HITM이 많은 캐시라인 → false sharing 후보 → __cacheline_aligned_in_smp로 분리 NUMA Remote HITM 지연: ~200 사이클 | Local HITM 지연: ~40 사이클 | LLC Hit: ~10 사이클

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;
커널의 false sharing 예방: 커널 코드에서 ____cacheline_aligned_in_smp (밑줄 4개)는 구조체(Struct) 자체를 정렬하고, __cacheline_aligned_in_smp (밑줄 2개)는 멤버를 정렬합니다. 특히 struct net_device, struct sock 같은 네트워크 핫패스 구조체에서 이 기법이 광범위하게 사용됩니다. pahole 도구로 구조체 레이아웃과 패딩 홀을 확인하세요.
perf c2c 전제 조건: Intel에서는 PEBS(Precise Event Based Sampling), AMD에서는 IBS(Instruction Based Sampling) 지원이 필요합니다. 커널 4.2+ 이상에서 사용 가능하며, 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=NN 사이클 이상 지연만 수집노이즈 제거, 고지연 집중
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 인터럽트 샘플링 비교

특성SPEPMU 인터럽트 샘플링
Skid(오차)거의 없음 (0~2 명령어)수십~수백 명령어
오버헤드매우 낮음 (하드웨어 버퍼)인터럽트마다 핸들러 실행
메타데이터지연, 데이터 소스, 주소 등 풍부제한적 (PEBS/IBS 없으면 부족)
필터링하드웨어 레벨 사전 필터소프트웨어 후처리
CPU 지원ARMv8.2+ (Cortex-A77 이상)모든 아키텍처
버퍼 관리전용 SPE 버퍼 (커널 주기적 수거)perf ring buffer
프로세서별 SPE 차이: Cortex-A710은 SPE v1.2, Cortex-X3/V2는 SPE v1.3을 지원합니다. v1.3에서는 FEAT_SPEv1p3으로 추가 필터링 옵션과 확장된 데이터 소스 정보가 제공됩니다. Neoverse V2(Grace)에서는 서버 워크로드에 최적화된 SPE 버퍼 크기가 제공되어 장시간 프로파일링이 가능합니다. SPE 지원 여부는 cat /proc/cpuinfo | grep -i spe 또는 ID_AA64DFR0_EL1 레지스터에서 확인합니다.
SPE + perf mem 활용: 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는 FetchOp(실행) 두 가지 독립적인 샘플링 채널을 제공한다는 점에서 차별화됩니다. 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 FetchIbsFetchCtl.IbsFetchCnt페치 샘플링 주기 카운터
IbsFetchCtl.IbsIcMissI-캐시 미스 여부
IbsFetchCtl.IbsL1TlbMissL1 ITLB 미스 여부
IbsFetchCtl.IbsFetchLat페치 완료까지 사이클 수
IBS OpIbsOpData.IbsCompToRetCtr완료-퇴장 지연 (사이클)
IbsOpData3.IbsDcMissD-캐시 미스 여부
IbsOpData3.IbsL2MissL2 캐시 미스 여부
IbsOpData3.IbsSwPf소프트웨어 프리페치 성공 여부
IbsDcLinAddr데이터 접근 가상 주소

Zen4/Zen5 IBS 확장

Zen4(EPYC 9004)에서는 IBS Op에 L3 미스 지연, DRAM 접근 지연 필드가 추가되어 메모리 계층별 병목을 더 정밀하게 분석할 수 있습니다. Zen5에서는 IBS Op Data 4 레지스터가 확장되어 명령어 타입(정수/부동소수점/벡터)별 분류가 가능합니다.

PEBS vs IBS 비교

특성Intel PEBSAMD IBS
아키텍처Intel Core 2 이상AMD Family 10h 이상
샘플링 방식이벤트 카운터 오버플로 기반N번째 명령어 태깅 기반
Skid매우 낮음 (1~2 명령어)없음 (정확한 IP)
프런트엔드 분석제한적 (frontend_retired 이벤트)IBS Fetch 전용 채널
데이터 주소PEBS + mem_inst_retiredIBS Op 기본 포함
지연 정보PEBS Latency (Sapphire Rapids+)IBS Completion-to-Retire
멀티 이벤트카운터당 1개 이벤트Fetch/Op 각 1개 (동시 가능)
IBS + perf c2c 조합: AMD 환경에서 perf c2c는 IBS Op의 데이터 주소 정보를 활용하여 false sharing을 탐지합니다. perf c2c record 실행 시 자동으로 IBS가 선택되며, perf c2c report에서 캐시라인별 HITM 분석이 가능합니다. Zen3 이전에서는 perf c2c 지원이 불완전할 수 있으므로 커널 5.19+ 권장됩니다.
AMD uProf 연동: AMD에서 제공하는 AMD uProf 도구는 IBS 데이터를 GUI로 시각화하며, perf로 수집한 데이터를 임포트할 수도 있습니다. 마이크로아키텍처 이벤트 매핑은 AMD PPR(Processor Programming Reference)에서 확인할 수 있습니다.

data type profiling (6.x)

커널 6.3부터 도입된 data type profiling은 메모리 접근 프로파일링의 패러다임을 바꾸는 기능입니다. 기존에는 캐시 미스가 발생하는 명령어 주소(IP)만 알 수 있었지만, 이제 어떤 구조체의 어떤 필드에서 미스가 발생하는지 DWARF 디버그 정보를 활용하여 구조체/변수 수준으로 분석할 수 있습니다.

이 기능은 perf annotate --data-type 또는 perf report --sort=type으로 사용하며, 데이터 레이아웃 최적화의 핵심 도구입니다. 커널 6.8+에서는 타입 추론이 개선되어 지역 변수와 인라인 함수(Inline Function)의 접근도 분석할 수 있습니다.

perf mem record PEBS/IBS/SPE 데이터 주소 수집 DWARF 매칭 IP → 변수 → 타입 구조체.필드 해석 타입별 집계 struct sk_buff: 42% struct page: 28% 필드 오프셋 분석 레이아웃 최적화 DWARF 디버그 정보로 메모리 접근 → 구조체.필드 수준으로 역매핑 필요: CONFIG_DEBUG_INFO_DWARF5=y + vmlinux (또는 debuginfod)

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의 핫 필드가 첫 번째 캐시라인에 모여 있도록 신중하게 배치되어 있습니다.
data type profiling 제약: 1) 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
annotate 고급 기법: perf annotate --percent-type=local-period로 함수 내부 비율(함수 자체를 100%로), --percent-type=global-period로 전체 프로파일 대비 비율을 선택할 수 있습니다. 함수 내 명령어 최적화에는 local, 전체 병목 파악에는 global이 적합합니다.

PREEMPT_RT 환경 제약

PREEMPT_RT(실시간 커널)에서 perf를 사용할 때는 일반 커널과 다른 중요한 제약 사항이 있습니다. RT 커널의 목표는 결정적(deterministic) 지연을 보장하는 것이므로, perf의 인터럽트 기반 샘플링이 실시간 태스크(Task)에 간섭을 줄 수 있습니다. 이 문제를 이해하고 올바른 설정으로 사용해야 합니다.

일반 커널 (PREEMPT_DYNAMIC) PMU 오버플로 하드웨어 인터럽트 NMI 마스크 불가 정확한 IP skid 최소 RT 커널 (PREEMPT_RT) PMU 오버플로 하드웨어 인터럽트 hrtimer 스레드 컨텍스트 skid 증가 간섭 최소 장점: 높은 정확도 NMI는 어떤 컨텍스트에서도 동작 PEBS/IBS와 결합 시 최고 정밀도 장점: RT 간섭 최소 실시간 태스크 지연에 영향 적음 NMI 비활성화로 결정성 보장

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
RT 커널에서 절대 하지 말 것: 1) 높은 샘플링 빈도(-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 템플릿 파일 생성
Flame Graph 자동화: perf script | stackcollapse-perf.pl | flamegraph.pl 파이프라인은 가장 널리 사용되는 perf script 활용 사례입니다. FlameGraph 레포지토리에서 다운로드할 수 있습니다. --colors=java, --title="My Profile" 등 옵션으로 커스터마이징이 가능합니다. 성능 최적화 문서에서 Flame Graph 해석 방법을 자세히 다룹니다.
Python 스크립트 요구사항: 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% 미만).

CPU Pipeline 분기 실행 감지 TNT/TIP/PSB 생성 AUX 버퍼 PT 패킷 저장 (DMA, 저오버헤드) perf.data PT 패킷 + 메타 sideband 이벤트 PT Decoder 패킷 + 바이너리 → 실행 흐름 재구성 코드 경로 재구성 커버리지 분석 AutoFDO 프로파일 PT 패킷: TNT(조건분기 결과) | TIP(간접분기 타겟) | PSB(동기화) | TSC(타임스탬프) | CBR(코어 비율) 디코더는 바이너리(vmlinux/ELF)와 PT 패킷을 결합하여 정확한 실행 경로를 복원 사이클 정확도 타이밍 포함 시 명령어별 실행 시간 측정 가능

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% 파악
AutoFDOcreate_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
PT 데이터 크기 관리: Intel PT는 초당 수백 MB의 데이터를 생성할 수 있습니다. --aux-size를 적절히 설정하고, 필요시 mtc_period, psb_period를 조정하여 패킷 밀도를 줄이세요. 스냅샷 모드(-S)를 사용하면 관심 시점 전후만 저장하여 크기를 크게 줄일 수 있습니다.
Intel PT vs perf record 비교: 일반 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
커널 주소가 0kptr_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.dataperf 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
벤치마크 함정: 1) CPU 주파수 스케일링이 활성화되면 같은 코드라도 측정마다 cycles 수가 달라집니다 — performance 거버너 필수, 2) 짧은 워크로드(<1초)는 멀티플렉싱 오차가 심합니다 — 충분히 긴 실행 시간 확보, 3) 첫 실행은 캐시 콜드 상태이므로 워밍업 실행 후 측정하세요, 4) 가상 머신(VM)에서는 PMU 가상화 제한으로 일부 이벤트가 부정확할 수 있습니다.

실전 사례별 분석 워크플로

성능 문제의 유형에 따라 perf 분석 전략이 달라집니다. 각 병목 유형(CPU bound, I/O bound, Lock contention, 메모리 병목)에 대한 체계적 분석 워크플로를 제시합니다.

성능 병목 유형별 perf 분석 워크플로 1단계: perf stat 초기 진단 A. CPU Bound 판별 기준 CPU 사용률 > 90% IPC > 1.0 (compute) IPC < 1.0 (stall) task-clock == wall time B. Memory Bound 판별 기준 IPC < 1.0 + 높은 cache-misses LLC-load-misses > 5% TMA Backend > Memory dTLB-load-misses 높음 C. I/O Bound 판별 기준 CPU 사용률 << wall time Off-CPU 시간 > On-CPU block:* 이벤트 빈번 syscall read/write 지배적 D. Lock Contention 판별 기준 CPU 사용률 낮은데 느림 futex / mutex 대기 빈번 context-switches 매우 높음 스레드 수 > CPU 코어 수 2단계: 유형별 심층 분석 도구 CPU Bound 도구 perf record -e cycles -g perf stat --topdown Flamegraph → perf annotate Memory Bound 도구 perf mem record (PEBS/IBS) perf c2c (false sharing) perf report --sort=type (6.3+) I/O Bound 도구 perf trace (syscall) perf record -e block:* Off-CPU Flamegraph Lock Contention 도구 perf lock contention --use-bpf perf record -e sched:* perf sched latency 3단계: 최적화 적용 후 검증 perf stat -r 5 (before/after 비교) → perf diff (프로파일 비교) → 차이 Flamegraph IPC 변화, cache-miss 변화, 실행 시간 변화를 정량적으로 확인

사례 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 ./appIPC, cache-miss, context-switch, task-clock vs wall time
2. 병목 유형perf stat --topdownFrontend/Backend/BadSpec/Retiring 비율
3. 핫스팟perf record -greport가장 많은 샘플의 함수/모듈
4. 시각화Flamegraph콜스택 기반 핫스팟 시각화
5. 명령어 수준perf annotate핫 명령어, 캐시 미스 위치
6. Off-CPUoffcputime / perf schedI/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 위키 및 튜토리얼

Brendan Gregg의 perf 리소스

LWN.net 기사

컨퍼런스 발표 및 기술 자료

커널 소스 참조