strace ile Dosya Sistemi Çağrılarını İzleme: Hangi Dosyalar Neden Açılıyor?
Bir uygulamanın neden yavaş açıldığını, hangi konfigürasyon dosyasını okuduğunu ya da neden “permission denied” hatası aldığını bulmaya çalışırken saatlerce log dosyalarına baktığınız oldu mu? Ben bu tür durumlarda artık doğrudan strace‘e gidiyorum. Özellikle dosya sistemi çağrılarını izlemek söz konusu olduğunda, strace bir x-ray cihazı gibi çalışıyor; uygulamanın içinde ne olduğunu, hangi dosyaları açmaya çalıştığını, hangi dizinlere baktığını bütün şeffaflığıyla önünüze seriyor.
Bu yazıda strace’i sadece teorik olarak değil, gerçekten karşılaştığım sorun senaryoları üzerinden anlatacağım. Temel kullanımdan başlayıp production ortamında makul şekilde kullanabileceğiniz filtreleme tekniklerine kadar gideceğiz.
strace Nedir ve Nasıl Çalışır?
strace, Linux çekirdeğinin ptrace sistem çağrısını kullanarak bir sürecin yaptığı sistem çağrılarını (syscall) izler. Yani uygulamanızın doğrudan çekirdeğe “ben şu dosyayı aç”, “bu sokete bağlan”, “şu belleği ayır” gibi isteklerini gerçek zamanlı olarak görebilirsiniz.
Dosya sistemi söz konusu olduğunda en çok işimize yarayan sistem çağrıları şunlar:
- openat: Dosya veya dizin açma (modern Linux’ta open() yerini büyük ölçüde aldı)
- read / write: Dosyadan okuma ve yazma
- stat / fstat / lstat: Dosya metadata sorgulama
- close: Dosya descriptor kapatma
- access: Dosyaya erişim izni kontrolü
- unlink / unlinkat: Dosya silme
- rename / renameat: Dosya yeniden adlandırma
- mkdir / rmdir: Dizin oluşturma ve silme
Kurulum için çoğu dağıtımda hazır geliyor ama gelmiyorsa:
# Debian/Ubuntu
apt install strace
# RHEL/CentOS/AlmaLinux
dnf install strace
# Arch
pacman -S strace
Temel Kullanım: Bir Programın Açtığı Tüm Dosyaları Görmek
En basit kullanım şekliyle bir komutun önüne strace yazıp çalıştırabilirsiniz:
strace ls /tmp
Bu çıktı oldukça gürültülü olacaktır. Binlerce satır gelir ve çoğu işinize yaramaz. Dosya sistemi çağrılarına odaklanmak için -e trace=file filtresini kullanmak çok daha mantıklı:
strace -e trace=file ls /tmp
Ama ben genellikle daha spesifik filtreleri tercih ediyorum. Sadece dosya açma çağrılarını görmek istiyorsanız:
strace -e trace=openat,open ls /tmp
Çıktıda şöyle satırlar göreceksiniz:
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libselinux.so.1", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/tmp", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 3
Her satırda gösterilen sayı (= 3 gibi) döndürülen file descriptor numarasıdır. -1 dönüyorsa çağrı başarısız olmuş demektir ve hemen yanında hata kodu görürsünüz.
Çalışmakta Olan Bir Sürece Bağlanma
Yeni bir süreç başlatmak yerine zaten çalışmakta olan bir süreci izlemek için -p parametresini kullanıyoruz. Bu production’da çok işe yarayan bir özellik:
# Önce PID'i bulalım
pgrep nginx
# ya da
pidof nginx
# Sürece bağlanalım
strace -p 1234 -e trace=openat,read,write
Burada dikkat edilmesi gereken nokta: strace bağlandığında süreç kısa bir süre duraklar (attach anı), bu normal. Ama uzun süreli izlemede performans etkisi olabilir, buna aşağıda döneceğim.
Nginx gibi birden fazla worker process’i olan uygulamalarda tüm child process’leri de izlemek için -f parametresi kullanılır:
strace -f -p $(pgrep -o nginx) -e trace=openat 2>&1 | head -50
Gerçek Senaryo 1: “Hangi Config Dosyasını Okuyor Bu Uygulama?”
Bunu defalarca yaşadım. Bir uygulama var, birden fazla yerde config dosyası olabilir ve hangisini kullandığını bilmiyorsunuz. Dokümantasyon ya yok ya da eskimiş. Klasik durum.
Diyelim ki PostgreSQL’in hangi postgresql.conf dosyasını okuduğunu bulmak istiyorsunuz:
strace -e trace=openat -p $(pgrep -o postgres) 2>&1 | grep -i "conf|config|.ini|.yaml"
Ya da yeni başlayan bir servisi izlemek daha temiz sonuç verir:
strace -e trace=openat systemctl start myapp 2>&1 | grep ".conf|.cfg|.yaml|.json"
Bir keresinde bir Python uygulaması prod’da farklı, staging’de farklı davranıyordu. strace ile baktığımızda /etc/myapp/config.yaml yerine /home/deploy/.config/myapp/config.yaml dosyasını okuduğunu gördük. Deploy kullanıcısının home dizininde eski bir config kalmıştı ve uygulama sistem geneli config’den önce onu buluyor, onu okuyordu. Bulmak 2 dakika sürdü.
Gerçek Senaryo 2: “Permission Denied” Hatasının Kökünü Bulmak
Bu belki de strace’in dosya sistemi izleme konusunda en değerli kullanım alanı. Bir uygulama hata veriyor ama hangi dosyaya erişemediğini söylemiyor. Ya da söylüyor ama o dosyayı düzeltiyorsunuz, aynı hata başka bir dosya için tekrar geliyor.
-e trace=file ile birlikte -y parametresi file descriptor’ları resolve eder, yani sayı yerine gerçek dosya yolunu gösterir. -v ise bazı çağrıların detaylarını genişletir. En çok kullandığım kombinasyon şu:
strace -e trace=file -e fault=none 2>&1 myapp | grep "EACCES|EPERM|ENOENT"
Daha temiz bir yaklaşım: sadece başarısız olan çağrıları görmek için -z parametresinin tersini kullanıyoruz, yani sadece hatalı sonuçları filtreliyoruz:
strace -e trace=openat myapp 2>&1 | grep " = -1"
Çıktı şöyle görünür:
openat(AT_FDCWD, "/var/run/myapp/myapp.pid", O_WRONLY|O_CREAT|O_TRUNC, 0644) = -1 EACCES (Permission denied)
openat(AT_FDCWD, "/etc/ssl/private/myapp.key", O_RDONLY) = -1 EACCES (Permission denied)
Artık tam olarak hangi dosyaların sorun çıkardığını görüyorsunuz. İkinci satırdaki SSL private key meselesi çok yaygın bir durum; uygulamayı yanlış kullanıcıyla çalıştırıyorsunuzdur ya da dosya izinleri bozulmuştur.
Çıktıyı Dosyaya Kaydetme ve Zaman Damgaları
Production sistemlerde strace çıktısını doğrudan terminalde okumak yerine dosyaya kaydetmek daha sağlıklı. -o parametresi bu iş için:
strace -e trace=file -o /tmp/strace_output.txt myapp
Birden fazla process izlerken her process’in çıktısını ayrı dosyaya kaydetmek için:
strace -f -e trace=openat -o /tmp/strace_%p.txt myapp
%p otomatik olarak PID ile değiştirilir. Yoğun bir ortamda bu çok işe yarıyor.
Zaman damgası için iki seçenek var:
- -t: Saati gösterir (HH:MM:SS)
- -tt: Mikrosaniye hassasiyetiyle gösterir
- -ttt: Unix timestamp olarak gösterir
- -T: Her sistem çağrısının süresini gösterir (performans analizi için kritik)
strace -tt -T -e trace=openat,read -o /tmp/strace_detailed.txt myapp
Çıktı şöyle görünür:
14:23:01.445221 openat(AT_FDCWD, "/etc/myapp/config.yaml", O_RDONLY) = 4 <0.000234>
14:23:01.446891 read(4, "---nserver:n port: 8080n", 4096) = 24 <0.000018>
Sondaki o sistem çağrısının ne kadar sürdüğünü gösteriyor. Hangi dosya işlemlerinin uygulamayı yavaşlattığını bulmak için bu parametre altın değerinde.
Uygulama Başlangıcını İzleme: execve’den İlk İsteğe
Bir uygulamanın başlangıç sürecini tam olarak anlamak istiyorsanız, execve’den itibaren izleyebilirsiniz. Bu özellikle “uygulama neden bu kadar yavaş başlıyor” sorusunu cevaplamak için harika:
strace -f -e trace=execve,openat -tt -T -o /tmp/startup_trace.txt /usr/bin/python3 myapp.py
Sonra dosyayı analiz edelim, en uzun süren dosya açma işlemlerini bulalım:
# Açılan tüm dosyaları listele
grep "openat" /tmp/startup_trace.txt | grep -v " = -1" | awk '{print $NF, $(NF-1)}' | sort -t'<' -k2 -rn | head -20
Bunu bir Python microservice’te uyguladığımda, başlangıçta 340’tan fazla .py dosyasının açıldığını gördüm. Bunların büyük çoğunluğu site-packages altındaki modüller içindi ve toplamda 1.2 saniye harcıyordu. Bu analiz olmadan başlangıç yavaşlığının sebebini bulmak çok daha uzun sürerdi.
strace ile stat Çağrılarını İzleme: Hangi Dosyanın Varlığı Sorgulanıyor?
Sadece dosya açma değil, dosyanın var olup olmadığını kontrol eden stat çağrıları da çok bilgi verir. Uygulamalar bazen onlarca yerde aynı dosyayı arayabilir:
strace -e trace=stat,stat64,newstat,statx myapp 2>&1 | grep "ENOENT"
Bu çıktı size uygulamanın var olmayan dosyaları nerede aradığını gösterir. Bir uygulama bazen eski lokasyonlardaki config dosyalarını aramaya devam eder. Bunu görmek, “neden bu kadar uzun süre filesystem’e vuruyor” sorusunu cevaplar.
Daha kapsamlı bir izleme için:
strace -e trace=%file myapp 2>&1 | awk '
/ENOENT/ { print "NOT FOUND:", $0 }
/EACCES/ { print "PERMISSION DENIED:", $0 }
/= [0-9]+$/ { print "SUCCESS:", $0 }
' | head -100
Child Process ve Thread İzleme: -f ve -ff
Modern uygulamaların büyük çoğunluğu fork veya thread kullanır. Sadece ana süreci izlerseniz önemli bilgileri kaçırırsınız. -f parametresi fork edilen tüm child process’leri de izler:
strace -f -e trace=openat -o /tmp/full_trace.txt nginx -t
Her satırın başında [pid XXXX] şeklinde process ID görürsünüz, hangi child’ın hangi dosyayı açtığını ayırt edebilirsiniz:
[pid 12345] openat(AT_FDCWD, "/etc/nginx/nginx.conf", O_RDONLY) = 4
[pid 12346] openat(AT_FDCWD, "/etc/nginx/sites-enabled/default", O_RDONLY) = 5
[pid 12347] openat(AT_FDCWD, "/var/log/nginx/access.log", O_WRONLY|O_CREAT|O_APPEND, 0644) = 6
Performance Etkisi ve Production’da Dikkat Edilmesi Gerekenler
Bu konuda açık olmak gerekiyor: strace production sistemlerde ciddi performans etkisi yaratabilir. ptrace mekanizması her sistem çağrısında süreci duraklatır, bilgiyi kullanıcı alanına kopyalar ve devam ettirir. Yoğun I/O yapan bir uygulamada bu etki %50-100’e kadar çıkabilir.
Production’da güvenli strace kullanımı için önerilerim:
- Kısa süreli izleme yapın, uzun süre bağlı kalmayın
- Mümkünse sadece ihtiyaç duyduğunuz sistem çağrılarını filtreleyin (
-e trace=openatgibi) -cparametresiyle sadece istatistik toplayın, her çağrıyı değil:
strace -c -e trace=file myapp
Bu çıktı her sistem çağrısının kaç kez çağrıldığını ve toplam süresini özet olarak gösterir, çok daha az overhead’e sahiptir.
Çıktı şöyle görünür:
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
45.23 0.023451 23 1021 12 openat
31.14 0.016158 16 987 read
15.67 0.008125 8 1045 close
7.96 0.004130 41 101 98 stat
------ ----------- ----------- --------- --------- ----------------
Bir diğer güvenli alternatif: trafiğin düşük olduğu bir replica’da ya da staging’de izleme yapıp sonuçları uygulamak.
Gerçek Senaryo 3: Library Loading Sorunları
Bir C uygulaması “cannot open shared library” hatası verdiğinde, hangi dizinlere baktığını görmek için:
strace -e trace=openat /usr/bin/myapp 2>&1 | grep ".so"
Çıktı şöyle görünür:
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libssl.so.3", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/libssl.so.3", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/local/lib/libssl.so.3", O_RDONLY|O_CLOEXEC) = 4
Uygulamanın önce standart sistem dizinlerinde aradığını, bulamayınca /usr/local/lib‘de bulduğunu görüyorsunuz. Eğer orası da başarısız olsaydı, ldconfig çalıştırmanız ya da LD_LIBRARY_PATH ayarlamanız gerektiğini doğrudan anlardınız.
Strace Çıktısını Analiz Etmek İçin Pratik grep Kalıpları
Uzun strace çıktılarıyla çalışırken şu filtreleri sık kullanıyorum:
# Sadece başarılı dosya açmalarını göster
grep "openat" trace.txt | grep -v " = -1"
# Sadece hataları göster
grep " = -1 E" trace.txt
# Belirli bir dizindeki dosya erişimlerini göster
grep "openat" trace.txt | grep "/etc/"
# En çok hangi dosyalar açılmış
grep "openat" trace.txt | grep -v " = -1" | grep -oP '"[^"]*"' | sort | uniq -c | sort -rn | head -20
# Yazma işlemi yapılan dosyalar
grep "openat" trace.txt | grep "O_WRONLY|O_RDWR"
Son komut özellikle ilginç: hangi dosyaların sadece okunduğunu değil, hangilerinin yazıldığını da görebilirsiniz. Bir uygulamanın beklenmedik yerlere yazma yaptığını keşfetmek için güçlü bir yöntem.
ltrace ile Karşılaştırma: Ne Zaman Hangisi?
Bazı durumlarda strace yerine ltrace (library trace) daha uygun olabilir. ltrace, sistem çağrıları yerine kütüphane fonksiyon çağrılarını izler. Ama dosya sistemi konusunda strace genellikle daha net bilgi verir çünkü kütüphane çağrıları da sonunda sistem çağrılarına dönüşür ve strace en alttaki gerçek I/O’yu gösterir.
İkisini birlikte kullanmak bazen anlam ifade eder: ltrace ile uygulamanın hangi fopen() çağrısı yaptığını, strace ile bunun gerçekte hangi kernel syscall’a dönüştüğünü görebilirsiniz.
Sonuç
strace, bir Linux sistem yöneticisinin ve DevOps mühendisinin araç kutusundaki en değerli aletlerden biri. Özellikle dosya sistemi çağrılarını izlemek söz konusu olduğunda, saatlerce sürebilecek hata ayıklama süreçlerini dakikalara indirebilir.
Özetlemek gerekirse:
-e trace=openatile dosya açma çağrılarına odaklanın-oile çıktıyı dosyaya yazın, terminalde kaybetmeyin-Tile zaman ölçümü yaparak performans sorunlarını tespit edin-file child process’leri de yakalayın- Production’da
-cile özet istatistik toplayın, full trace kullanmaktan kaçının grep " = -1"ile hatalı çağrıları hızlıca filtreleyin
“Neden bu uygulama bu dosyayı açıyor?” ya da “Hangi config dosyasını okuyor?” gibi sorularla bir daha karşılaştığınızda, log dosyalarına değil doğrudan strace’e gidin. Cevabı genellikle 2 dakika içinde alırsınız.
