perf ile Linux Performans Profilleme

Sistem yöneticisi olarak en sık karşılaştığım durumlardan biri şu: bir servis yavaş çalışıyor, CPU kullanımı yüksek, ama tam olarak neden yüksek olduğunu bilmiyorsun. top ve htop sana hangi prosesin CPU yediğini söylüyor ama o prosesin içinde ne olduğunu söylemiyor. İşte tam bu noktada perf devreye giriyor.

perf, Linux çekirdeğine gömülü performans sayaçlarını (hardware performance counters) ve çeşitli izleme altyapılarını kullanan güçlü bir profilleme aracıdır. Intel ve AMD işlemcilerdeki donanım seviyesindeki PMU (Performance Monitoring Unit) olaylarını, yazılım olaylarını ve çekirdek tracepoint’lerini tek bir araçtan yönetebilirsin. Sadece uygulama geliştiricilerin değil, sistem yöneticilerinin de günlük hayatında çok işe yarayan bir araçtır.

perf Kurulumu ve Ön Hazırlık

Çoğu dağıtımda perf varsayılan olarak gelmez, ayrıca kurman gerekir. Önemli bir nokta: perf versiyonu çekirdek versiyonuyla eşleşmeli.

# Debian/Ubuntu
sudo apt install linux-tools-common linux-tools-$(uname -r)

# RHEL/CentOS/AlmaLinux
sudo yum install perf

# Arch Linux
sudo pacman -S perf

# Kurulumu doğrula
perf --version

Kurulum sonrası birkaç kernel parametresini kontrol etmek iyi olur:

# Paranoid seviyesini kontrol et (düşük = daha fazla izin)
cat /proc/sys/kernel/perf_event_paranoid

# Geçici olarak kısıtlamayı kaldır (root olmayan kullanıcılar için)
sudo sysctl -w kernel.perf_event_paranoid=1

# Kalıcı yapmak için
echo 'kernel.perf_event_paranoid = 1' | sudo tee -a /etc/sysctl.conf

perf_event_paranoid değerleri şöyle çalışır:

  • -1: Tüm kullanıcılar her şeye erişebilir
  • 0: Kernel profiling’e izin verilir
  • 1: CPU olayları kullanıcı alanında kullanılabilir (varsayılan)
  • 2: Sadece root kullanabilir

Kalsa sembollerini (symbol) düzgün görebilmek için debuginfo paketlerini de kurmanı öneririm. Aksi takdirde fonksiyon adları yerine hex adresler görürsün:

# Ubuntu için
sudo apt install linux-image-$(uname -r)-dbgsym

# RHEL/CentOS için
sudo debuginfo-install glibc kernel

perf stat ile Temel İstatistikler

perf stat, bir komutun veya prosesin temel performans metriklerini toplar. En hızlı başlangıç noktasıdır.

# Basit bir komut için istatistik topla
perf stat ls -la /usr/bin

# Belirli bir PID için 10 saniye izle
perf stat -p 1234 sleep 10

# Tüm CPU'lar için sistem geneli istatistik
perf stat -a sleep 5

# Detaylı istatistik (daha fazla event)
perf stat -d -d -d ls -la /usr/bin

Tipik bir çıktı şöyle görünür:

Performance counter stats for 'ls -la /usr/bin':

          2,345,678      instructions              #    1.23  insns per cycle
          1,901,234      cycles
            456,789      cache-misses              #    5.12% of all cache refs
          8,912,345      cache-references
                123      page-faults

       0.001234567 seconds time elapsed

instructions per cycle (IPC) değeri çok önemlidir. 1’in altındaysa CPU, memory veya I/O bekliyor demektir. 2-3 civarındaysa uygulama CPU’yu verimli kullanıyor.

Cache-miss oranı yüksekse (yüzde 10’un üzerinde), uygulamanın bellek erişim paterni kötü demektir. Bu bilgi, hangi yönde optimizasyon yapman gerektiğini söyler.

perf record ile Profil Toplama

perf stat sana genel istatistik verir ama hangi fonksiyonun ne kadar CPU harcadığını söylemez. Bunun için perf record kullanırsın:

# Bir uygulamayı baştan sona profilile
perf record ./my_application

# Çalışan bir proses için profil al (30 saniye)
perf record -p $(pgrep nginx) -g sleep 30

# Tüm sistem için profil al
perf record -a -g sleep 10

# Örnekleme frekansını ayarla (varsayılan 1000 Hz)
perf record -F 99 -p 1234 sleep 10

# Belirli bir event üzerinden örnekleme yap
perf record -e cache-misses -p 1234 sleep 5

-g parametresi call graph (çağrı grafiği) toplamasını sağlar. Bu olmadan fonksiyon adlarını görürsün ama kim çağırdı bilmezsin. Gerçek dünya senaryolarında her zaman -g ekle.

-F 99 seçeneği saniyede 99 örnek alır. 100 veya 1000 gibi tam sayılar yerine 99 gibi asal sayılar kullanılır, çünkü bazı uygulamalar 100 Hz’de çalışan timer’larla örnekleme senkronize olabilir ve yanlış sonuç verebilir.

perf record çalıştırıldığında bulunduğun dizinde perf.data dosyası oluşturur.

perf report ile Sonuçları Analiz Etme

# Interaktif TUI arayüzü
perf report

# Belirli bir perf.data dosyasını analiz et
perf report -i /tmp/nginx-profile.data

# Düz metin çıktı (script veya grep için)
perf report --stdio

# Call graph ile detaylı rapor
perf report -g graph --stdio | head -100

# Belirli bir binary için filtrele
perf report --dsos nginx

perf report TUI arayüzünde gezinmek için:

  • Yukarı/Aşağı ok: Fonksiyonlar arasında gezin
  • Enter: Fonksiyonun detayına in (annotate)
  • a: Assembly ve kaynak kodu görüntüle
  • e: Expand/collapse call tree
  • q: Çık

Gerçek Dünya Senaryosu 1: Yavaş PHP-FPM

Bir gün PHP-FPM worker’larının CPU’yu aşırı tükettiğine dair alarm aldım. top gösteriyordu ama neden göstermiyordu:

# PHP-FPM worker PID'lerini bul
pgrep -f "php-fpm: worker"

# 60 saniye boyunca tüm php-fpm worker'larını profilile
perf record -F 99 -g -p $(pgrep -d',' -f "php-fpm: worker") sleep 60

# Sonuçları incele
perf report --stdio --no-children | head -50

Bu analizde json_decode ve preg_match fonksiyonlarının CPU’nun yüzde 60’ını tükettiğini gördüm. Uygulama her request’te büyük JSON’ları parse ediyordu ve bu cache’lenmiyordu. Sonuç: Redis cache ekleyince CPU kullanımı yüzde 80’den yüzde 15’e düştü.

perf top ile Gerçek Zamanlı İzleme

htop‘un profilleme versiyonu gibi düşün. Sistem genelinde hangi fonksiyonların en çok CPU harcadığını canlı olarak gösterir:

# Sistem geneli gerçek zamanlı profil
sudo perf top

# Belirli bir PID için
perf top -p 1234

# Yüksek frekanslı örnekleme
perf top -F 999

# Belirli bir CPU'yu izle
perf top -C 0,1,2

# Call graph ile
perf top -g -p 1234

perf top özellikle anlık sorunları araştırırken çok işe yarar. Bir servis aniden CPU’ya yüklendiğinde ilk koşacağın komutlardan biri bu olmalı.

perf flamegraph: Görsel Analiz

Flamegraph, profil verilerini görsel olarak analiz etmenin en güzel yollarından biri. Brendan Gregg’in geliştirdiği bu yöntem, call stack’leri görsel olarak gösterir:

# Brendan Gregg'in FlameGraph scriptlerini indir
git clone https://github.com/brendangregg/FlameGraph.git
export PATH=$PATH:$(pwd)/FlameGraph

# Profil topla
perf record -F 99 -g -p $(pgrep mysql) sleep 30

# perf script ile işlenebilir formata çevir
perf script > out.perf

# FlameGraph oluştur
stackcollapse-perf.pl out.perf | flamegraph.pl > mysql-flame.svg

# Browserde aç
firefox mysql-flame.svg

Flamegraph’ta x ekseni CPU zamanını, y ekseni call stack derinliğini gösterir. Geniş bloklara sahip fonksiyonlar en çok zaman harcayan fonksiyonlardır. Tepesi düz olan (platolu) bloklar genellikle optimizasyon fırsatı işaret eder.

Gerçek Dünya Senaryosu 2: MySQL Sorgu Yavaşlığı

Bir üretim sunucusunda MySQL’in yüksek CPU kullandığı bir durumla karşılaştım. Slow query log’a bakmak ilk adım olsa da, bazen sorun sorgu planında değil MySQL’in internal işlemlerindedir:

# MySQL process'ini bul
MYSQL_PID=$(pgrep -n mysqld)

# 30 saniyelik profil topla
sudo perf record -F 99 -g --pid=$MYSQL_PID sleep 30

# Flamegraph oluştur
sudo perf script | stackcollapse-perf.pl | flamegraph.pl > mysql-$(date +%Y%m%d).svg

# Metin bazlı analiz
sudo perf report --stdio --no-children 2>/dev/null | grep -v "^#" | head -30

Bu analizde buf_pool_resize fonksiyonunun sürekli çağrıldığını gördüm. Buffer pool’un sürekli resize edilmesi ciddi performans problemi yaratıyordu. innodb_buffer_pool_size parametresini sabit değere çekince sorun çözüldü.

perf trace ile Sistem Çağrılarını İzleme

strace‘e benzer ama çok daha düşük overhead ile çalışır. Özellikle I/O yoğun uygulamalarda sistem çağrısı profillemesi yapmak için idealdir:

# Bir komutun tüm syscall'larını izle
perf trace ls -la /tmp

# Belirli bir PID için
perf trace -p 1234

# Sadece belirli syscall'ları izle
perf trace -e open,read,write -p 1234

# İstatistik modu (hangi syscall kaç kez çağrıldı)
perf trace --summary -p 1234 sleep 10

# Dosya I/O'ya odaklan
perf trace -e 'read,write,openat,close' -p $(pgrep nginx) sleep 5

perf trace --summary çıktısı şöyle görünür:

 Summary of events:

 nginx (1234), 500 events, 100.0%

   syscall            calls    total       min       avg       max
   --------------- -------- --------- --------- --------- ---------
   epoll_wait           120   10.234s   0.001ms  85.283ms 500.000ms
   writev                89    1.234ms   0.005ms   0.013ms   0.234ms
   read                  45    0.456ms   0.002ms   0.010ms   0.089ms

perf Events: Özel Olaylar

perf sadece CPU sampling yapmaz. Donanım ve yazılım olaylarının geniş bir yelpazesini izleyebilir:

# Mevcut tüm eventleri listele
perf list

# Sadece hardware eventleri
perf list hardware

# Cache miss olaylarını izle
perf stat -e cache-misses,cache-references,instructions,cycles -p 1234 sleep 10

# Branch misprediction analizi
perf stat -e branch-misses,branch-instructions -p 1234 sleep 5

# TLB miss analizi
perf stat -e dTLB-load-misses,iTLB-load-misses -p 1234 sleep 5

# Context switch izleme
perf stat -e context-switches,cpu-migrations -a sleep 10

Sık kullandığım event kombinasyonları:

  • cache-misses,cache-references: Bellek erişim verimliliği için
  • branch-misses,branch-instructions: Kod akış optimizasyonu için
  • cpu-migrations: İş yükünün CPU’lar arası ne sıklıkla taşındığı için
  • page-faults: Bellek yönetimi sorunları için

perf diff ile Karşılaştırmalı Analiz

İki farklı profil dosyasını karşılaştırmak, bir değişikliğin etkisini ölçmek için harikadır:

# İlk profili topla (eski kod veya config)
perf record -F 99 -g -o before.data ./app --config old.conf sleep 30

# Değişikliği uygula (config değişikliği, kod deploy, vb.)

# İkinci profili topla
perf record -F 99 -g -o after.data ./app --config new.conf sleep 30

# Karşılaştır
perf diff before.data after.data

# Daha detaylı karşılaştırma
perf diff -c delta before.data after.data --stdio

Bu yöntem özellikle kernel güncelleme, uygulama deploy veya configuration değişikliği sonrası performans etkisini ölçmek için çok değerlidir. CI/CD pipeline’larına entegre ederek performans regresyonlarını otomatik olarak yakalayabilirsin.

perf probe ile Dinamik Tracing

Kernel veya kullanıcı alanı fonksiyonlarına dinamik olarak probe ekleyebilirsin. Bu özellik, kaynak kodunu değiştirmeden ölçüm almanı sağlar:

# Bir kernel fonksiyonuna probe ekle
sudo perf probe --add tcp_sendmsg

# Probe'u listele
sudo perf probe --list

# Probe'u kullanarak veri topla
sudo perf record -e probe:tcp_sendmsg -ag sleep 10

# Raporu gör
sudo perf report

# Probe'u kaldır
sudo perf probe --del tcp_sendmsg

# Kullanıcı alanı fonksiyonu için probe
sudo perf probe -x /usr/sbin/nginx --add ngx_http_process_request

Gerçek Dünya Senaryosu 3: Network Latency Problemi

Bir microservice mimarisinde iki servis arasındaki gecikme aniden arttı. Network ekibi altyapıda sorun olmadığını söylüyordu. Kernel TCP stack’ini incelemeye karar verdim:

# TCP ilgili kernel probe'ları ekle
sudo perf probe --add tcp_sendmsg
sudo perf probe --add tcp_recvmsg
sudo perf probe --add tcp_v4_connect

# 20 saniye veri topla
sudo perf record -e probe:tcp_sendmsg,probe:tcp_recvmsg -ag sleep 20

# Analiz et
sudo perf report --stdio | head -40

# Probe'ları temizle
sudo perf probe --del '*'

Analizde tcp_sendmsg fonksiyonunun beklenmedik şekilde uzun sürdüğü görüldü. Daha derine inince Nagle algoritmasının devre dışı bırakılmamış olduğu ortaya çıktı. TCP_NODELAY socket opsiyonu eksikti. Bu değişiklikle gecikme yüzde 40 azaldı.

Script Haline Getirilmiş Profilleme Araçları

Sık kullandığım bir profilleme scripti:

#!/bin/bash
# quick-profile.sh - Hızlı profilleme scripti

PROCESS=$1
DURATION=${2:-30}
OUTPUT_DIR="/tmp/perf-$(date +%Y%m%d-%H%M%S)"

mkdir -p $OUTPUT_DIR

PID=$(pgrep -n "$PROCESS")
if [ -z "$PID" ]; then
    echo "Process bulunamadı: $PROCESS"
    exit 1
fi

echo "PID $PID ($PROCESS) için $DURATION saniyelik profil toplanıyor..."

# Profil topla
perf record -F 99 -g --pid=$PID 
    -o $OUTPUT_DIR/perf.data sleep $DURATION

# Metin raporu oluştur
perf report --stdio -i $OUTPUT_DIR/perf.data 
    --no-children 2>/dev/null > $OUTPUT_DIR/report.txt

# FlameGraph varsa oluştur
if command -v stackcollapse-perf.pl &>/dev/null; then
    perf script -i $OUTPUT_DIR/perf.data 2>/dev/null | 
        stackcollapse-perf.pl | 
        flamegraph.pl > $OUTPUT_DIR/flamegraph.svg
    echo "FlameGraph: $OUTPUT_DIR/flamegraph.svg"
fi

echo "Rapor: $OUTPUT_DIR/report.txt"
echo "Top 10 fonksiyon:"
grep -v "^#" $OUTPUT_DIR/report.txt | head -15

Dikkat Edilmesi Gereken Noktalar

Overhead konusu: perf record düşük overhead ile çalışır ama yüksek frekanslı örnekleme (1000+ Hz) üretim sistemlerinde yüzde 1-5 CPU overhead yaratabilir. Kritik sistemlerde -F 99 veya -F 49 kullan.

Kernel sembolleri: /proc/kallsyms okunabilir olmalı. Aksi halde çekirdek fonksiyon adları görünmez:

sudo sysctl -w kernel.kptr_restrict=0

JIT compiled diller: Java, Node.js, Python gibi diller için ek adımlar gerekir. Java için -XX:+PreserveFramePointer JVM parametresi eklenmelidir. Node.js için --perf-basic-prof ile başlatılmalıdır.

Container ortamları: Container içinde çalışırken perf kullanmak için container’ın SYS_ADMIN veya PERFMON capability’sine ihtiyacı vardır.

# Docker container'ı perf yetkisiyle başlat
docker run --cap-add SYS_ADMIN --cap-add PERFMON my-image

Disk alanı: Uzun süreli profilleme seanslarında perf.data dosyası büyük olabilir. -m parametresiyle ring buffer boyutunu sınırlayabilirsin:

perf record -m 256 -F 99 -g -p 1234 sleep 60

Sonuç

perf, Linux’ta performans sorunlarını derinlemesine anlamanın en güçlü yollarından biri. Sadece “hangi process CPU harcıyor” sorusundan çok daha fazlasını cevaplıyor: hangi fonksiyon, hangi kod satırı, hangi sistem çağrısı, hangi donanım olayı sorununa yol açıyor?

Günlük iş akışımda perf top ile başlıyorum, sorun gördüğümde perf record -g ile detaylı profil topluyorum, ardından perf report veya flamegraph ile analiz yapıyorum. Bu üçlü kombinasyon vakaların büyük çoğunluğunu çözmeye yetiyor.

Başlangıç olarak üretim dışı bir ortamda perf stat ve perf top ile oynamanı öneririm. Araç ilk başta biraz karmaşık görünse de, birkaç gerçek sorun üzerinde çalıştıktan sonra vazgeçilmez hale geliyor. CPU affinity problemlerinden cache thrashing’e, gereksiz sistem çağrılarından kernel lock contention’a kadar pek çok problemi perf olmadan bulmak çok daha uzun sürer.

Son olarak: Brendan Gregg’in “Systems Performance” kitabını ve kendi blog’unu (brendangregg.com) şiddetle tavsiye ederim. perf konusunda internetteki en kapsamlı Türkçe kaynaklardan biri olmak istedim bu yazıyla, umarım işe yarar.

Yorum yapın