Eylül 27 2024

Linux Sistem Performans Analizi: perf, strace ve eBPF Araçları

Bir sabah Slack’ten gelen o korkunç alarm sesiyle uyandınız: “Production veritabanı sunucusunda latency uçtu, CPU %99!” Hemen sunucuya SSH ile bağlandınız, top komutunu çalıştırdınız ve evet, CPU gerçekten can çekişiyor. Ancak top veya htop gibi geleneksel araçlar size sadece “yangın olduğunu” söyler; yangının hangi odada, hangi kibritle başladığını göstermez. İşte bu noktada modern bir SRE (Site Reliability Engineer) gibi düşünmeli ve linux çekirdeğinin (kernel) derinliklerine inmeliyiz. Bu yazıda, modern altyapılarda performance sorunlarını iğne deliğinden geçirir gibi analiz etmenizi sağlayacak üç silahşörü inceleyeceğiz: perf, strace ve devrim niteliğindeki ebpf tabanlı bpftrace.

1. Donanımın Nabzını Tutmak: perf (CPU & Hardware Profiling)

perf, Linux çekirdeği ile doğrudan entegre çalışan inanılmaz güçlü bir profil çıkarma (profiling) aracıdır. CPU döngülerini (cycles), cache miss (önbellek kaçırma) oranlarını ve CPU talimatlarını (instructions) donanımsal sayaçları (PMU – Performance Monitoring Unit) kullanarak analiz eder.

Neden Sadece CPU Yüzdesine Bakmak Yanıltıcıdır?

Çoğu mühendis CPU %100 olduğunda uygulamanın çok yoğun hesaplama yaptığını düşünür. Ancak durum her zaman bu değildir. CPU, RAM’den veri beklerken de (stall cycles) %100 meşgul görünebilir. Biz buna Memory-Bound (bellek sınırlandırılmış) deriz. Eğer CPU gerçekten matematiksel işlemler yapıyorsa buna da Compute-Bound denir.

Bu ayrımı görmek için ilk yapmamız gereken şey perf stat komutunu çalıştırmaktır:

# Belirli bir PID'yi 5 saniye boyunca analiz edelim
perf stat -p 1234 sleep 5

Karşımıza şöyle bir çıktı gelecektir:

 Performance counter stats for process id '1234':

       2001.45 msec task-clock                #    0.400 CPUs utilized          
              1234  context-switches          #    0.617 K/sec                  
                45  cpu-migrations            #    0.022 K/sec                  
               105  page-faults               #    0.052 K/sec                  
        4002938102  cycles                    #    2.000 GHz                    
        2001467291  instructions              #    0.50  insn per cycle         
         120495839  branches                  #   60.204 M/sec                  
           4958102  branch-misses             #    4.11% of all branches        

       5.002345123 seconds time elapsed

Burada odaklanmamız gereken en kritik metrik insn per cycle (IPC) değeridir. IPC (Instructions Per Cycle), CPU’nun her döngüde kaç talimat çalıştırdığını gösterir.

  • IPC < 1.0 ise: Uygulamanız muhtemelen I/O veya memory erişimi bekliyor (Memory-Bound). CPU boşta (stall) bekliyor demektir.
  • IPC > 1.5 ise: CPU gerçekten yoğun şekilde kodunuzu çalıştırıyor demektir (Compute-Bound).

Sıcak Noktaları Bulmak: perf record ve perf report

Uygulamanın tam olarak hangi fonksiyonunun CPU’yu sömürdüğünü bulmak için örnekleme (sampling) yapmamız gerekir. Bu işlem production ortamlarında genellikle %1 ila %5 arasında çok düşük bir overhead (ek yük) ile yapılabilir.

# Saniyede 99 frekansla (overkill olmaması için idealdir) 10 saniye boyunca kayıt alalım
# -g parametresi call-graph (çağrı ağacı) kaydetmesini sağlar
perf record -F 99 -p 1234 -g -- sleep 10

Bu komut geçerli dizinde perf.data adında bir dosya oluşturur. Bu dosyayı analiz etmek için terminalden şu komutu veririz:

perf report -n --stdio

Karşınıza çıkan interaktif arayüzde hangi fonksiyonun (sembolün) CPU döngülerinin yüzde kaçını harcadığını hiyerarşik bir şekilde görebilirsiniz. Eğer fonksiyon isimleri yerine [hexadecimal] adresler görüyorsanız, uygulamanızın “debug symbols” (hata ayıklama sembolleri) eksiktir demektir. Go kullanıyorsanız binary’nizi strip etmeyin, C++/Rust kullanıyorsanız -g flag’i ile derlediğinizden emin olun.

2. Karanlıkta Kalan Sistem Çağrıları: strace

Uygulamanız çalışıyor ama hiçbir şey yapmıyor gibi mi görünüyor? Dosya okumaya çalışırken kilitlenmiş olabilir mi? Yoksa DNS çözümlemesi yaparken timeout mu yaşıyor? Bu gibi durumlarda, uygulamanın kernel ile olan iletişimini yani System Calls (syscalls) trafiğini izlememiz gerekir.

İşte strace bu işin mutfağıdır.

Production Uyarısı: strace’i Dikkatli Kullanın!

Geliştirme ortamında strace my_app yazıp geçmek harikadır ancak bunu canlı production ortamında sakın yapmayın! strace, izlediği process’in her syscall yaptığında durdurulmasını (ptrace ile) sağlar. Bu durum, uygulamanın performansını %100 ila %1000 oranında düşürebilir.

Bunun yerine, production dostu parametrelerle nokta atışı yapmalıyız:

# Uygulamanın en çok hangi sistem çağrısında ne kadar zaman harcadığını özetleyelim
# Bu işlem ham log akıtmaya göre çok daha az overhead yaratır
strace -c -p 1234

Çıktı bize muhteşem bir özet sunacaktır:

% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 89.45    1.204958         120     10041           read
 10.12    0.136120          13     10200      1050 openat
  0.43    0.005789          11       500           write
------ ----------- ----------- --------- --------- ----------------
100.00    1.346867                 20741      1050 total

Yukarıdaki tabloda net bir şekilde görüyoruz ki, uygulama zamanının %89’unu read sistem çağrısında harcıyor ve ciddi miktarda openat (dosya açma hatası) alıyor. Hangi dosyayı açamadığını bulmak için sadece hatalı çağrıları filtreleyebiliriz:

# Sadece başarısız olan (errors) openat çağrılarını göster
strace -e trace=openat -Z -p 1234

3. Modern Çağın Sihirli Değneği: eBPF ve bpftrace

Geleneksel araçların kısıtlamalarından sıkıldıysanız, sahneye eBPF (Extended Berkeley Packet Filter) çıkıyor. eBPF, Linux çekirdeğinin kodunu değiştirmeden veya kernel modülü yüklemeden, güvenli sandbox’lar içinde doğrudan çekirdekte kod çalıştırmamızı sağlar.

bpftrace ise eBPF dünyasının “awk” dilidir. Son derece hafif, pratik ve neredeyse sıfır overhead ile çalışan tek satırlık (one-liners) script’ler yazmamıza olanak tanır.

Örnek 1: Disk I/O Latency Analizi (Biolatency)

Diskinizin yavaş olduğunu düşünüyorsunuz ama hangi process’in ne kadarlık bir gecikmeye sebep olduğunu bulamıyorsunuz. Geleneksel iostat size sadece ortalama değerler verir. bpftrace ile gerçek zamanlı bir histogram çizelim:

# Disk I/O tamamlanma sürelerini mikrosaniye cinsinden histogram olarak gösterir
bpftrace -e '
kprobe:vfs_read { @start[tid] = nsecs; } 
kretprobe:vfs_read /@start[tid]/ { 
    @latency = hist((nsecs - @start[tid]) / 1000); 
    delete(@start[tid]); 
}'

Bu script, her vfs_read (sanal dosya sistemi okuma) başladığında bir zaman damgası alır ve bittiğinde aradaki farkı hesaplayıp logaritmik bir grafik çizer:

@latency: 
[2, 4)                12 |@@@@                                        |
[4, 8)                85 |@@@@@@@@@@@@@@@@@@@@@@@@                    |
[8, 16)              142 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|
[16, 32)              45 |@@@@@@@@@@@@                                |
[32, 64)               8 |@@                                          |

Bu grafiğe bakarak, okuma işlemlerinin büyük çoğunluğunun 8-16 mikrosaniye arasında tamamlandığını, yani diskimizin sağlıklı çalıştığını saniyeler içinde anlayabiliriz.

Örnek 2: Bellek Sızıntısı (Memory Leak) Avı

Sisteminizde gizemli bir bellek şişmesi var. Hangi uygulamanın sürekli malloc çağırıp free etmediğini bulmak istiyorsunuz. eBPF ile kullanıcı alanındaki (user-space) bellek allocation çağrılarını takip edebiliriz:

# libc içindeki malloc çağrılarının byte boyutlarına göre dağılımını izleyelim
bpftrace -e 'usdt:/lib/x86_64-linux-gnu/libc.so.6:libc:memory_malloc_retry { @[arg0] = count(); }'

Bu sayede production ortamında çalışan uygulamanıza hiçbir kütüphane enjekte etmeden veya onu durdurmadan bellek tüketim kalıplarını yakalayabilirsiniz.

Özet ve Doğru Aracı Seçme Rehberi

Sistem tıkandığında hangi aracı ne zaman kullanacağınıza karar vermek, tecrübeli bir mühendisi amatörden ayıran en önemli özelliktir. İşte size pratik bir başucu tablosu:

Senaryo / Belirti Kullanılacak Araç Neden?
Yüksek CPU kullanımı var, hangi kod satırının yavaş olduğunu bulmak istiyorum. perf record / report Düşük overhead ile CPU çağrı ağacını (call graph) çıkarır.
Uygulama kilitlendi (stuck), log üretmiyor, ne yaptığını göremiyorum. strace -p <PID> Çekirdeğe gönderdiği sistem çağrılarını (network, dosya erişimi) anlık listeler.
Disk performansından şüpheleniyorum ama genel istatistikler yetersiz kalıyor. bpftrace (eBPF) Kernel seviyesinde I/O kuyruk gecikmesini histogram olarak gösterir.
Sistemin donanımsal düzeyde (L1/L2 Cache, CPU Cycles) analizine ihtiyacım var. perf stat İşlemcinin donanımsal sayaçlarına doğrudan erişim sağlar.

Performans analizi yaparken her zaman en az invaziv (sisteme en az müdahale eden) yöntemden başlayın. Önce perf stat ve bpftrace ile genel resmi görün, gerekirse ve güvenliyse strace ile derinlemesine inceleme yapın. Unutmayın, iyi bir SRE sadece sorunu çözen değil, sorunu çözerken production sistemini ayakta tutabilen kişidir!

Category: Genel | LEAVE A COMMENT