Uygulama Performansını Profilleme: strace ve ltrace Kullanım Rehberi

Bir uygulama aniden yavaşladığında, log dosyaları sana hiçbir şey söylemediğinde ve “neden bu kadar uzun sürüyor” sorusunun cevabını bulamadığında, işte o an strace ve ltrace’in gerçek değerini anlıyorsun. Bu iki araç, Linux’ta uygulama davranışını mikroskop altına almanın en doğrudan yollarından biri. Sistem çağrılarını, kütüphane fonksiyonlarını ve süreçlerin işletim sistemiyle nasıl konuştuğunu canlı olarak izlemeni sağlıyorlar.

strace ve ltrace Nedir, Ne İşe Yarar?

strace, bir sürecin yaptığı sistem çağrılarını (system calls) takip eder. Süreç her dosya açtığında, ağ bağlantısı kurduğunda, bellek tahsis ettiğinde, bir sinyal aldığında strace bunların hepsini kaydeder. Kernel ile uygulama arasındaki köprüyü izliyorsun aslında.

ltrace ise bir adım üste çalışır. Sistem çağrıları yerine kütüphane fonksiyonu çağrılarını takip eder. malloc, fopen, printf, strlen gibi libc fonksiyonları veya başka shared library çağrıları ltrace’in gözlem alanına girer.

İkisi birbirini tamamlar. Bir uygulamanın neden yavaş olduğunu, neden crash verdiğini, hangi dosyalara eriştiğini veya hangi network bağlantılarını açtığını araştırırken bu ikili inanılmaz derecede güçlü bir kombinasyon oluşturur.

Kurulum

Çoğu sistemde hazır gelir ama gelmiyorsa:

# Debian/Ubuntu
sudo apt install strace ltrace

# RHEL/CentOS/Rocky Linux
sudo dnf install strace ltrace

# Arch Linux
sudo pacman -S strace ltrace

strace ile Başlamak

En basit kullanım şekli bir komutu strace ile çalıştırmak:

strace ls /tmp

Bu komutu çalıştırdığında ekrana seller gibi çıktı gelir. Her satır bir sistem çağrısını temsil eder. Format şu şekildedir:

syscall_adı(argümanlar) = dönüş_değeri

Örneğin:

openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
read(3, "177ELF2113>1P"..., 832) = 832
close(3) = 0

Burada openat ile /etc/ld.so.cache dosyası açılıyor, read ile okunuyor, close ile kapatılıyor. Bu kadar net.

Temel strace Parametreleri

-p PID: Çalışan bir sürece bağlan. Yeni süreç başlatmak yerine mevcut süreçi izle.

-f: Fork edilen child süreçleri de takip et. Multi-process uygulamalarda kritik.

-ff: Her child süreç için ayrı çıktı dosyası oluştur (-o ile birlikte).

-e trace=: Belirli sistem çağrılarını filtrele. Sadece ilgilendiğin çağrıları gör.

-e trace=file: Sadece dosya işlemlerini izle.

-e trace=network: Sadece ağ işlemlerini izle.

-e trace=process: Sadece süreç yönetimi çağrılarını izle.

-t: Her satıra zaman damgası ekle (saat:dakika:saniye).

-tt: Mikrosaniye hassasiyetiyle zaman damgası.

-T: Her sistem çağrısının ne kadar sürdüğünü göster.

-o dosya: Çıktıyı dosyaya yaz.

-c: Özet istatistik göster. Hangi çağrı kaç kez yapılmış, toplam süre nedir.

-s 1024: String boyutunu artır (varsayılan 32 byte, genellikle yetmez).

-x: String olmayan verileri hex olarak göster.

Gerçek Dünya Senaryosu 1: Yavaş Başlayan Uygulama

Bir Java uygulamasının start olması 45 saniye sürüyor ama neden bilmiyorsun. Önce -c ile özet bakalım:

strace -c -f java -jar myapp.jar 2>&1 | tail -30
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 67.23    4.234521         234     18097           stat
 18.45    1.162340          89     13054           openat
  8.12    0.511234        5112       100      98   connect
  4.21    0.265123          45      5891           read
...

stat çağrısı zamanın yüzde 67’sini alıyor. Bu ciddi bir işaret. Uygulama muhtemelen var olmayan dosyaları veya sınıfları arıyor. Şimdi daha detaylı inceleyelim:

strace -f -e trace=stat,openat -o /tmp/java_trace.log java -jar myapp.jar
grep "ENOENT" /tmp/java_trace.log | head -20

ENOENT (No such file or directory) hatası alan stat çağrıları sana classpath sorununu veya eksik konfigürasyon dosyalarını gösterecektir.

Gerçek Dünya Senaryosu 2: Çalışan Servise Bağlanma

Production’da bir nginx worker süreci beklenenden fazla CPU kullanıyor. Yeni süreç başlatmadan izleme yapalım:

# Önce PID'i bul
ps aux | grep nginx | grep worker
# Çıktı: www-data  12345  89.2  0.1  12345  nginx: worker process

# Bağlan
sudo strace -p 12345 -T -tt 2>&1 | head -50
14:23:45.123456 epoll_wait(9, [{EPOLLIN, {u32=15, u64=15}}], 512, 60000) = 1 <0.000123>
14:23:45.123789 accept4(6, {sa_family=AF_INET, ...}, [16], SOCK_NONBLOCK) = 15 <0.000234>
14:23:45.124023 read(15, "GET /heavy-endpoint HTTP/1.1rn", 16384) = 387 <0.000089>

-T ile her çağrının süresi parantez içinde görünüyor. Hangi çağrının ne kadar sürdüğünü net görebiliyorsun.

strace ile Dosya İzleme

Bir uygulama hangi config dosyalarını okuyor, hangi geçici dosyalar oluşturuyor, hangilerini kilitliyor? Bunu bilmek bazen deployment sorunlarını çözmek için kritik:

strace -e trace=openat,read,write,close -f -o /tmp/fileops.log ./myapp
# Sadece başarılı açılan dosyaları listele
grep "openat" /tmp/fileops.log | grep -v "ENOENT" | grep -v "EPERM" | awk -F'"' '{print $2}' | sort -u

Bu komut sana uygulamanın gerçekte eriştiği dosyaların temiz bir listesini verir. Container veya chroot ortamlarında hangi dosyaların gerekli olduğunu anlamak için mükemmel.

Ağ Bağlantılarını İzlemek

Uygulama hangi sunuculara bağlanıyor, hangi portlara istek atıyor, DNS sorgularını nasıl yapıyor?

strace -e trace=network -f -s 1024 ./myapp 2>&1 | grep -E "connect|bind|listen"

DNS resolution sorunlarını görmek için:

strace -e trace=network,read,write -f -s 4096 ./myapp 2>&1 | grep -A2 "connect"

Bir connect çağrısının uzun sürdüğünü görürsen (timeout bekleniyor olabilir), -T ile süreleri ekleyip hangi IP’e bağlanmaya çalışırken takıldığını görebilirsin:

strace -e trace=network -T -f ./myapp 2>&1 | grep "connect.*= -1"

ltrace ile Kütüphane Çağrılarını İzlemek

strace kernel seviyesinde çalışırken ltrace user space kütüphane çağrılarını yakalar. Özellikle uygulama mantığındaki sorunları ve kütüphane kullanımını anlamak için değerli:

ltrace ./myapp 2>&1 | head -30

Çıktı strace’e benzer ama kütüphane fonksiyonlarını görürsün:

malloc(1024)                                     = 0x55a3b2c1d4e0
fopen("/etc/myapp/config.ini", "r")              = 0x55a3b2c1d560
fgets(0x7ffd3a2b1234, 512, 0x55a3b2c1d560)      = 0x7ffd3a2b1234
strcmp("debug_mode", "debug_mode")               = 0
atoi("1")                                        = 1

ltrace Parametreleri

-p PID: Çalışan sürece bağlan.

-f: Child süreçleri de izle.

-l kütüphane: Sadece belirli kütüphane çağrılarını izle. Örneğin -l libssl.so.

-e fonksiyon: Belirli fonksiyonları filtrele.

-T: Her çağrının süresini göster.

-c: Özet istatistik.

-o dosya: Çıktıyı dosyaya yaz.

-n sayı: Her çağrıyı N space ile indent et (nested çağrılarda okunabilirlik için).

-S: Kütüphane çağrılarına ek olarak sistem çağrılarını da göster.

Gerçek Dünya Senaryosu 3: Memory Leak Araştırması

Bir servis zamanla bellek tüketiyor. ltrace ile malloc/free dengesi bakıyoruz:

ltrace -e malloc,free,realloc,calloc -f -c -p $(pgrep myservice)

Belirli bir süre sonra Ctrl+C ile durdur ve özet istatistiğe bak. malloc sayısı free sayısından çok fazlaysa leak’in varlığına dair güçlü bir işaret var demektir. Bu kesin tanı koymaz ama valgrind veya AddressSanitizer ile devam etmen gerektiğini söyler.

Daha ayrıntılı görmek için:

ltrace -e malloc,free -f -T ./myapp 2>&1 | awk '
/malloc/ { allocs++ }
/free/ { frees++ }
END { print "Allocs:", allocs, "Frees:", frees, "Diff:", allocs-frees }
'

Gerçek Dünya Senaryosu 4: Sertifika veya SSL Sorunu

Uygulaman HTTPS bağlantısında timeout oluyor. SSL handshake mi takılıyor, sertifika doğrulama mı başarısız?

ltrace -l libssl.so -l libcrypto.so -f ./myapp 2>&1 | grep -E "SSL|verify|cert"

Ya da strace ile aynı anda sistem çağrılarına bakarak:

strace -e trace=network,read,write -s 4096 -f -T ./myapp 2>&1 | grep -E "connect|ECONNREFUSED|ETIMEDOUT"

ECONNREFUSED görürsen hedef port kapalı, ETIMEDOUT görürsen firewall veya routing sorunu var demektir.

strace ile Process Fork Analizi

Bir CGI scripti veya her istek için yeni süreç başlatan bir uygulama var. Her fork ne kadar sürüyor, child süreçler nerede zaman harcıyor?

strace -f -ff -o /tmp/traces/app_trace -T ./myapp &
# Biraz bekle
ls -la /tmp/traces/
# Her PID için ayrı dosya: app_trace.12345, app_trace.12346 vs.
grep -l "execve" /tmp/traces/app_trace.*

Her child sürecin trace’i ayrı dosyaya gider. Uzun süren child süreçleri tespit etmek için:

# Her trace dosyasındaki toplam zamanı hesapla
for f in /tmp/traces/app_trace.*; do
    echo -n "$f: "
    grep -oP '<K[0-9.]+' "$f" | awk '{sum+=$1} END {printf "%.3f secondsn", sum}'
done | sort -t: -k2 -rn | head -10

Çıktıyı Anlamlandırmak: Yaygın Sistem Çağrıları

strace çıktısını okurken sık karşılaşacağın sistem çağrıları:

openat: Dosya veya dizin açma. Dönüş değeri pozitifse file descriptor, -1 ise hata var.

read / write: Dosya okuma/yazma. Üçüncü argüman istek edilen byte, dönüş değeri gerçekte işlenen byte.

mmap / munmap: Bellek haritalama. Kütüphane yüklemeleri ve büyük bellek tahsisleri burada görünür.

epoll_wait / poll / select: I/O multiplexing. Event-driven uygulamaların kalbi. Uzun wait süreleri idle demektir, kısa süreler yoğun trafik.

futex: Mutex ve senkronizasyon primitifleri. Çok sayıda futex çağrısı lock contention işaretidir.

clone: Thread veya process oluşturma.

execve: Yeni program çalıştırma.

brk / sbrk: Heap genişletme. Sık sık görünüyorsa bellek tahsisi yoğun demektir.

sendto / recvfrom: UDP soket işlemleri.

sendmsg / recvmsg: Gelişmiş soket işlemleri.

Performans Etkisi ve Dikkat Edilecekler

strace ve ltrace kullanımı izlenen süreci ciddi ölçüde yavaşlatır. Bu araçlar ptrace sistem çağrısını kullanır ve her izlenen çağrıda kernel-user space geçişi yaşanır. Genel etki 10x ile 100x arası yavaşlama olabilir.

Production’da strace kullanırken şunlara dikkat et:

  • -c ile sadece istatistik topla, her çağrıyı loglamaktan kaçın. Bu çok daha az yavaşlatır.
  • Kısa süreli kullanım yap. Birkaç saniye yeter genellikle.
  • Mümkünse yük dengeleyiciden bir backend’i devre dışı bırakıp sadece onu izle.
  • -e trace= ile sadece ilgilendiğin çağrıları filtrele.
  • Bazı güvenlik sistemleri (SELinux, seccomp) ptrace kullanımını kısıtlayabilir.

Container ortamlarında ptrace genellikle kısıtlıdır. Docker’da strace kullanmak istiyorsan:

docker run --cap-add=SYS_PTRACE --security-opt seccomp=unconfined myimage

Pratik Hızlı Başvuru Komutları

Sık kullandığım komut kombinasyonlarından bazıları:

# Uygulama hangi config dosyalarını okuyor?
strace -e trace=openat -f ./myapp 2>&1 | grep "O_RDONLY"

# Uygulama nerede zaman harcıyor? (sadece yavaş çağrılar)
strace -T -f ./myapp 2>&1 | awk -F'<' '{if(NF>1 && $2+0 > 0.01) print}'

# Çalışan servise bağlan ve ağ trafiğini izle
sudo strace -p $(pgrep -x nginx) -e trace=network -s 1024 -f

# Memory allocation istatistikleri
ltrace -e malloc,free,realloc -c ./myapp

# Hangi shared library fonksiyonları en çok çağrılıyor?
ltrace -c -f ./myapp 2>&1 | sort -k1 -rn | head -15

# Uygulama hangi ortam değişkenlerine bakıyor?
strace -e trace=openat,read -f ./myapp 2>&1 | grep "environ"

strace ile Güvenlik Analizi

Güvenlik açısından da strace çok değerli. Bir binary’nin şüpheli davranışını araştırıyorsan:

# Hangi dosyaları silmeye çalışıyor?
strace -e trace=unlink,unlinkat,rename ./suspiciousbinary 2>&1

# Hangi komutları execute etmeye çalışıyor?
strace -e trace=execve,clone -f ./suspiciousbinary 2>&1

# Hangi network adreslerine bağlanıyor?
strace -e trace=connect -s 256 ./suspiciousbinary 2>&1

Bu çalışmayı sandbox veya izole bir ortamda yap. Zararlı bir binary izleme sırasında da zarar verebilir.

ltrace ile Kriptografi Kütüphanesi Debugging

Bir uygulamanın şifreleme işlemlerini neden yanlış yaptığını veya neden yavaş olduğunu görmek için:

ltrace -l 'libcrypto*' -f -T ./myapp 2>&1 | grep -E "EVP|AES|RSA|SHA"

OpenSSL veya benzer kütüphanelerin hangi algoritmaları kullandığını, her şifreleme işleminin ne kadar sürdüğünü görebilirsin. Yanlış veya zayıf algoritma kullanımını bu şekilde tespit etmek mümkün.

Araçların Sınırlılıkları

strace ve ltrace güçlü araçlar ama her şeyi çözmez:

  • JVM, .NET, Python gibi yorumlayıcı tabanlı uygulamalarda ltrace pek işe yaramaz. Kütüphane çağrıları interpreter üzerinden geçer, doğrudan shared library çağrısı olmayabilir.
  • strace kernel boundary’sini izler; uygulama içi fonksiyon çağrıları görünmez. Bunun için perf, gdb veya uygulama spesifik profiler’lar gerekir.
  • Çok yüksek frekanslı çağrılarda çıktı o kadar büyük olur ki analiz zorlaşır. Filtreleme kritik.
  • Thread’lerin birbirini etkilediği race condition’ları strace ile yakalamak neredeyse imkansız; izlemenin yaptığı yavaşlama timing’i bozar.

Java için jstack, async-profiler veya JFR (Java Flight Recorder), Python için py-spy, Node.js için 0x gibi platform spesifik araçları tercih et.

Sonuç

strace ve ltrace, bir sysadmin’in araç kutusunda her zaman hazır bulunması gereken iki güçlü tanılama aracı. Log dosyaları susmaya başladığında, uygulama açıklanamaz biçimde yavaşladığında ya da “bu nasıl çalışıyor ki” diye sorduğunda bu iki araç sana gerçeği gösterir.

strace ile sistem çağrısı seviyesinde, ltrace ile kütüphane seviyesinde olmak üzere iki farklı perspektiften uygulamanın davranışını inceleyebilirsin. Dosya erişimleri, ağ bağlantıları, bellek yönetimi, süreç yönetimi – bunların hepsi bu araçların gözlem alanına girer.

Production kullanımında temkinli ol, -c ile başla, filtreleme kullan ve gözlem süresini kısa tut. Ama bir debug ortamında veya test sisteminde çekinmeden her parametreyi dene. Bu araçları ne kadar çok kullanırsan, çıktıları okuma ve yorumlama becerin o kadar gelişir.

Bir sonraki kez bir uygulama sana neden yavaş olduğunu söylemediğinde, strace’i çalıştır. Cevap zaten orada, sistem çağrılarının akışında gizli.

Yorum yapın