Tüm eğitimler
Rehber Linux / Sistem strace perf

strace · perf · valgrind —
Linux Debugging Araçları.

program neden yavaş, neden çöküyor, neden memory şişiyor — cevap bu araçlarda.

00 Debugging araçları taksonomisi

Her debugging problemi üç kategoriden birine girer: davranış hatası, performans sorunu veya memory bozulması — doğru aracı seçmek sorunun yarısını çözer.

Üç problem kategorisi

Bir Linux uygulamasında bir şeyler yanlış gittiğinde sorunun kaynağı genellikle şu üç kategoriden birinde yatar: programın syscall seviyesinde yanlış davranması (dosya açamıyor, bağlanamıyor, izin hatası), performans sorunu (CPU, bellek veya I/O darboğazı), ya da memory management hatası (leak, corruption, double free). Her kategori için farklı bir araç takımı kullanılır.

Araç Kategori Ne Gösterir Overhead
straceDavranışSyscall'lar: argüman, dönüş değeri, hata koduYüksek (~10x)
ltraceDavranışDinamik kütüphane çağrıları (libc, libssl vb.)Yüksek
perfPerformansCPU cycle, cache miss, branch mispredict, flamegraphÇok düşük (<1%)
valgrindMemoryLeak, invalid read/write, double free, use-after-freeÇok yüksek (2–10x)
ASanMemoryBuffer overflow, use-after-free, stack corruptionOrta (~2x)
TSanMemory / ConcurrencyRace condition, lock-order violationYüksek (5–15x)

Karar rehberi: hangi araç ne zaman?

  Problem nedir?
       │
       ├─ "Dosya açılmıyor / bağlanamıyor / izin hatası"
       │        └──→  strace -e trace=file,network
       │
       ├─ "Uygulama çok yavaş, CPU çok yüksek"
       │        └──→  perf top  →  perf record -g  →  flamegraph
       │
       ├─ "Bellek sürekli artıyor"
       │        └──→  valgrind --leak-check=full
       │              ya da  /proc/PID/smaps + heaptrack
       │
       ├─ "Segfault / crash"
       │        └──→  core dump + gdb  →  ASan  →  valgrind
       │
       └─ "Race condition / deadlock"
                └──→  ThreadSanitizer (-fsanitize=thread)
    

Kurulum

kurulum.sh
# Debian / Ubuntu
sudo apt install strace ltrace linux-perf valgrind

# RHEL / CentOS / Fedora
sudo yum install strace ltrace perf valgrind

# perf kernel sürümü doğrula
perf --version
uname -r

# perf paranoia seviyesini düşür (root olmadan kullanmak için)
sudo sysctl kernel.perf_event_paranoid=1

Bu bölümde öğrendikleriniz

  • Debugging problemleri davranış, performans ve memory olmak üzere üç kategoriye ayrılır
  • strace/ltrace syscall ve library çağrılarını; perf CPU metriklerini; valgrind/ASan memory hatalarını yakalar
  • Doğru araç seçimi debug süresini ciddi ölçüde kısaltır

01 strace temelleri

strace, ptrace() syscall'ını kullanarak hedef process'in her kernel çağrısını yakalar ve terminale basar — program ile kernel arasında bir cam duvar gibi davranır.

strace nasıl çalışır?

strace, Linux kernel'in ptrace() mekanizmasını kullanır. PTRACE_SYSCALL isteğiyle hedef process her syscall'a girerken ve çıkarken durdurulur; argümanlar ve dönüş değeri okunarak terminale yazdırılır. Bu nedenle overhead yüksektir — production'da dikkatli kullanılmalıdır.

  user space             kernel space
       │                      │
   myapp ──── syscall ───────→│
       │      (giriş)         │← strace buraya hook eder
       │                      │  argümanları okur
       │                 kernel işler
       │                      │← strace çıkışa hook eder
   myapp ←── dönüş ───────────│  dönüş değerini okur
       │                      │
    

İlk çalıştırma: strace ls

terminal
strace ls /tmp 2>&1 | head -20

Tipik çıktı (kısaltılmış):

strace çıktısı
execve("/usr/bin/ls", ["ls", "/tmp"], 0x... /* env vars */) = 0
brk(NULL)                               = 0x55f8c2a01000
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=89432, ...}) = 0
mmap(NULL, 89432, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f3a2c1d0000
close(3)                                = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3...", 832)       = 832
mmap(NULL, 2037344, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f3a...
close(3)                                = 0
getdents64(3, /* 12 entries */, 32768) = 336
write(1, "file1.txt  file2.log\n", 21)   = 21
close(1)                                = 0
exit_group(0)                           = ?

Syscall satırı formatı

BileşenÖrnekAçıklama
Syscall adıopenatÇağrılan kernel fonksiyonu
ArgümanlarAT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXECSyscall parametreleri, string olarak decode edilmiş
Dönüş değeri= 3Başarıda fd numarası, hata durumunda -1
Hata kodu= -1 ENOENT (No such file or directory)Sadece hata durumunda görünür

-c özet çıktısı

terminal
strace -c ls /tmp
-c çıktısı
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 38.21    0.000412          41        10           mmap
 22.14    0.000239          29         8           openat
 14.87    0.000160          22         7           read
  8.92    0.000096          13         7           fstat
  5.43    0.000058          14         4           close
  4.11    0.000044          11         4           mprotect
  3.72    0.000040          13         3           brk
  2.60    0.000028          28         1           getdents64
------ ----------- ----------- --------- --------- ----------------
100.00    0.001077                    44         0 total

Filtreleme ve zamanlama seçenekleri

-e trace=networkSadece network syscall'larını göster (socket, connect, bind, recv, send…)
-e trace=fileSadece dosya sistemi syscall'larını göster (open, read, write, stat…)
-e trace=processProcess yönetimi (fork, execve, exit, wait…)
-THer syscall'ın süresini mikrosaniye cinsinden göster: openat(...) = 3 <0.000042>
-cTüm çıktı yerine istatistik özeti göster
terminal
# Sadece network syscall'ları, süre ile
strace -T -e trace=network curl https://example.com 2>&1 | head -15

Bu bölümde öğrendikleriniz

  • strace, ptrace() ile her syscall'ı girişte ve çıkışta yakalar
  • Çıktı formatı: syscall(argümanlar) = dönüş
  • -c ile istatistiksel özet, -e trace= ile filtreleme, -T ile süre ölçümü yapılır

02 strace gelişmiş kullanım

Child process takibi, çalışan process'e attach, çıktıyı dosyaya yönlendirme ve fd çözümleme — strace'in gerçek gücü bu seçeneklerde.

-f: child process'leri takip et

Fork veya exec yapan uygulamalarda -f olmadan sadece parent process görülür. Shell script'leri, daemon'lar veya system() çağrısı yapan programlarda -f şarttır.

terminal
# bash + fork + exec zincirini takip et
strace -f bash -c "ls /tmp && cat /etc/hostname"

# Her satırın başına PID ekle (çok process varsa şart)
strace -f -e trace=execve -o /tmp/trace.txt ./myapp

-p PID: çalışan process'e attach

terminal
# PID bul
pidof nginx

# Çalışan process'e attach
strace -p 1234

# Tüm nginx worker'larını izle (-f ile birlikte)
strace -f -p 1234 -e trace=network 2>&1 | head -50
NOT

Process'e attach etmek için root yetkisi veya CAP_SYS_PTRACE capability gerekir. Modern Linux sistemlerinde /proc/sys/kernel/yama/ptrace_scope = 1 ise sadece child process'lere attach edilebilir; bunu sudo sysctl kernel.yama.ptrace_scope=0 ile geçici olarak değiştirebilirsiniz.

-o: çıktıyı dosyaya yönlendir

terminal
# Çıktıyı dosyaya yaz (stderr yerine)
strace -o /tmp/strace.log ./myapp

# -f ile çok process: her process ayrı dosyaya (PID eklenir)
strace -f -o /tmp/trace ./myapp
# → /tmp/trace.1234, /tmp/trace.1235, ...

-s: string uzunluğunu artır

strace, varsayılan olarak string argümanları 32 karakterde keser. Config dosya yolları, HTTP başlıkları veya uzun path'leri görmek için bu değeri artırın.

terminal
# Default: 32 karakter (path'ler kesilir)
strace ./myapp
# openat(AT_FDCWD, "/etc/myapp/config/application.co"..., O_RDONLY) = -1

# -s 256 ile tam path
strace -s 256 ./myapp
# openat(AT_FDCWD, "/etc/myapp/config/application.conf", O_RDONLY) = -1 ENOENT

-y ve -yy: fd'leri çözümle

terminal
# -y: fd numarasını dosya adına çevir
strace -y ./myapp
# read(3</etc/passwd>, ...)  — fd 3'ün /etc/passwd olduğu görünür

# -yy: socket fd'lerini adres:port olarak göster
strace -yy ./myapp
# sendto(5<TCP:127.0.0.1:8080->10.0.0.1:443>, ...)

Gerçek senaryo: "config dosyası neden yüklenmiyor?"

Uygulama başlıyor, hata mesajı veriyor ama hangi dosyayı aradığını söylemiyor. strace ile 30 saniyede çözülür:

debug-config.sh
# 1. Sadece dosya açma hataları filtrele
strace -e trace=openat,open -s 256 ./myapp 2>&1 \
  | grep ENOENT

# Çıktı:
# openat(AT_FDCWD, "/etc/myapp.conf", O_RDONLY) = -1 ENOENT
# openat(AT_FDCWD, "/home/user/.config/myapp.conf", O_RDONLY) = -1 ENOENT
# → Program /etc/myapp.conf'u arıyor, dosya mevcut değil

# 2. Çözüm: doğru path'e semlink veya dosya oluştur
sudo cp myapp.conf.example /etc/myapp.conf

Bu bölümde öğrendikleriniz

  • -f ile fork/exec zincirinin tamamı takip edilir
  • -p PID ile çalışan sürece attach edilir
  • -s 256 -y -yy kombinasyonu ile tam dosya yolları ve socket adresleri görülür
  • Config yükleme sorunları strace ile dakikalar içinde tespit edilir

03 ltrace: kütüphane çağrıları

ltrace, syscall'ların bir seviye üstünde çalışır: dinamik olarak bağlanan kütüphane fonksiyonlarını (libc, libssl, libcurl…) yakalar.

ltrace nedir?

strace kernel-user boundary'yi izlerken ltrace, shared library'lerin PLT (Procedure Linkage Table) girdilerini hook eder. Bu sayede malloc(), fopen(), SSL_connect() gibi libc ve üst seviye kütüphane çağrıları görülür — bunlar doğrudan syscall değildir.

terminal
ltrace ls /tmp 2>&1 | head -20
ltrace çıktısı
__libc_start_main(0x401b20, 2, 0x7ffd..., ...)              = 0
setlocale(LC_ALL, "")                                      = "en_US.UTF-8"
bindtextdomain("coreutils", "/usr/share/locale")           = "/usr/share/locale"
textdomain("coreutils")                                    = "coreutils"
__cxa_atexit(0x40a480, 0, 0x603260)                         = 0
isatty(1)                                                   = 1
malloc(40)                                                  = 0x604670
opendir("/tmp")                                             = 0x6046b0
readdir(0x6046b0)                                           = 0x6046f0
free(0x604670)                                              = <void>

strace vs ltrace karşılaştırması

Özellikstraceltrace
Hook noktasıKernel syscall arayüzüShared library PLT
Ne yakalar?read, write, openat, socket…malloc, fopen, strcmp, SSL_connect…
Static binaryÇalışırÇalışmaz
Performans overheadYüksekYüksek
Kullanım amacıOS etkileşimi, hata kodlarıKütüphane çağrısı profilleme

Filtreleme seçenekleri

terminal
# Özet istatistik
ltrace -c ./myapp

# Sadece malloc ve free çağrıları
ltrace -e malloc+free ./myapp

# Belirli bir kütüphaneyi izle
ltrace -l libssl.so.3 openssl s_client -connect example.com:443

# Child process'leri de takip et
ltrace -f ./myapp
DİKKAT

ltrace yalnızca dinamik olarak bağlanmış binary'lerde çalışır. gcc -static ile derlenen veya Go gibi statik binary üreten derleyicilerle oluşturulan programlarda ltrace hiçbir çıktı üretmez. Bunu file ./myapp ile kontrol edin: "dynamically linked" yazıyorsa ltrace çalışır.

Bu bölümde öğrendikleriniz

  • ltrace, shared library PLT'yi hook ederek kütüphane çağrılarını yakalar
  • strace kernel sınırını, ltrace kütüphane sınırını görür — ikisi birbirini tamamlar
  • Static binary'lerde ltrace çalışmaz; file komutu ile binary türü kontrol edilmelidir

04 perf stat: CPU counter'ları

perf, CPU'nun içindeki donanım sayaçlarını (PMU) okuyarak instructions, cycles, cache miss ve branch misprediction sayılarını gerçek zamanlı olarak raporlar.

PMU nedir?

Modern CPU'lar, Performance Monitoring Unit (PMU) adı verilen donanım devrelerine sahiptir. Bu devreler her clock cycle'da belirli olayları sayar: kaç instruction çalıştırıldı, kaç L1/L2/LLC cache miss yaşandı, kaç branch prediction yanlış gitti. perf, bu sayaçları root yetkisi olmadan ve çok düşük overhead ile okur.

perf stat: temel kullanım

terminal
perf stat ./myapp
perf stat çıktısı
 Performance counter stats for './myapp':

          1,842.53 msec task-clock         #    0.998 CPUs utilized
                 4      context-switches   #    2.171 /sec
                 0      cpu-migrations     #    0.000 /sec
               512      page-faults        #  277.857 /sec

     4,921,034,128      cycles             #    2.671 GHz
     6,284,019,442      instructions       #    1.28  insn per cycle  ← IPC
       847,234,891      branches           #  459.788 M/sec
         3,421,023      branch-misses      #    0.40% of all branches

       1.845761234 seconds time elapsed
       1.840123000 seconds user
       0.004534000 seconds sys

IPC: Instructions Per Cycle

IPC değeri uygulamanın CPU'yu ne kadar verimli kullandığını gösterir:

IPC DeğeriYorumMuhtemel Neden
< 0.5Çok kötüMemory-bound: L2/L3/DRAM bekleniyor
0.5 – 1.0ZayıfCache miss veya branch mispredict
1.0 – 2.0NormalTipik uygulama aralığı
> 3.0MükemmelVektörizasyon, SIMD, pipeline doldu

Özel event'lerle detay ölçümü

terminal
# Cache miss detayı
perf stat -e cache-misses,cache-references,LLC-loads,LLC-load-misses ./myapp

# Mevcut event listesi
perf list | grep cache

# 5 kez çalıştır, ortalama ve standart sapma hesapla
perf stat -r 5 ./myapp
perf stat -r 5 çıktısı (istatistik kısmı)
       1.847213534 seconds time elapsed   ( +-  0.23% )
       1.841890200 seconds user            ( +-  0.24% )
       0.005230600 seconds sys             ( +-  1.12% )

perf top: canlı CPU profil

terminal
# Tüm sistem genelinde CPU yiyen fonksiyonlar (htop gibi ama fonksiyon bazlı)
sudo perf top

# Belirli bir process
sudo perf top -p $(pidof myapp)

# Call-graph ile (hangi fonksiyon kimi çağırıyor)
sudo perf top -g

Bu bölümde öğrendikleriniz

  • PMU, donanım seviyesinde düşük overhead ile CPU metriklerini sayar
  • IPC değeri CPU verimliliğinin temel göstergesidir; düşük IPC → memory-bound veya cache miss sorunu
  • perf stat -r 5 ile istatistiksel doğruluk sağlanır; perf top ile canlı hotspot görülür

05 perf record/report ve flamegraph

perf record, örnekleme tabanlı profil kaydeder; perf report ve FlameGraph ile zaman harcayan fonksiyonlar call-graph'ıyla görselleştirilir.

perf record: profil kayıt

terminal
# -g: call-graph (backtrace) kaydet — flamegraph için şart
perf record -g ./myapp

# Çalışan process'e attach
sudo perf record -g -p $(pidof myapp) -- sleep 10

# Frekans ayarla (Hz): default 4000, daha az overhead için düşür
perf record -F 99 -g ./myapp

# → perf.data dosyası oluşturulur
ls -lh perf.data

perf report: interaktif TUI

terminal
# İnteraktif TUI (ok tuşlarıyla gezin, Enter ile aç)
perf report

# Text çıktı (pipe veya CI için)
perf report --stdio 2>&1 | head -40

# Belirli bir sembol için assembly annotation
perf annotate main --stdio
perf report --stdio çıktısı
# Overhead  Command   Shared Object     Symbol
# ........  ........  ................  ......................
#
    42.31%  myapp     myapp             [.] process_buffer
    18.74%  myapp     libc.so.6         [.] __memcpy_avx_unaligned
     9.12%  myapp     myapp             [.] parse_header
     7.03%  myapp     myapp             [.] hash_lookup
     4.21%  myapp     [kernel]          [k] __get_user_pages

FlameGraph üretme

flamegraph.sh
# Brendan Gregg'in FlameGraph araçlarını indir
git clone https://github.com/brendangregg/FlameGraph /opt/flamegraph

# Profil kaydet
perf record -F 99 -g -p $(pidof myapp) -- sleep 30

# SVG üret
perf script \
  | /opt/flamegraph/stackcollapse-perf.pl \
  | /opt/flamegraph/flamegraph.pl > /tmp/flame.svg

# Tarayıcıda aç
xdg-open /tmp/flame.svg

FlameGraph okuma rehberi

Boyut / RenkAnlamı
Yatay genişlikO fonksiyonun toplam CPU zamanındaki oranı — en geniş bar en fazla CPU alan fonksiyon
Dikey yükseklikCall stack derinliği — en altta çağıran, en üstte çağrılan
RenkAnlamsız — sadece okunabilirlik için rastgele seçilir
Düz plato (geniş + yatay tavan)O fonksiyon çok zaman harcıyor ve çocuklarına dağıtmıyor → hotspot
NOT

FlameGraph anlamlı sonuç vermesi için binary'nin sembol tablosunu içermesi gerekir: -g flag'iyle derleme yapın (gcc -O2 -g myapp.c). strip edilmiş binary'lerde fonksiyon adları yerine hex adres görülür.

Bu bölümde öğrendikleriniz

  • perf record -g call-graph bilgisiyle örnekleme profili kaydeder
  • perf report --stdio text çıktı verir; interaktif TUI ile call stack gezilir
  • FlameGraph'ta en geniş plateau → CPU hotspot; renk anlamsızdır

06 valgrind memcheck

valgrind, uygulamayı sentetik bir CPU üzerinde çalıştırarak her bellek erişimini izler — leak, invalid access ve double free hatalarını stack trace ile raporlar.

Çalışma modeli

valgrind, hedef binary'yi JIT ile kendi sanal CPU'suna çevirir. Her malloc()/free() ve her bellek okuma/yazma işlemi izlenir. Bu yüzden overhead yüksektir (2–10x yavaş), ancak başka hiçbir araç onun yakaladığı hataları bu kesinlikte yakalayamaz.

Temel kullanım

terminal
# Tam leak analizi
valgrind --leak-check=full --show-leak-kinds=all \
         --track-origins=yes -v ./myapp 2>&1 | tee valgrind.log

Leak türleri

TürAçıklamaÖncelik
definitely lostHiçbir pointer bu bloğa işaret etmiyor — kesin leakKritik
indirectly lost"Definitely lost" bir bloktan ulaşılan başka bloklarYüksek
possibly lostPointer var ama bloğun başına değil ortasına işaret ediyorOrta
still reachableProgram sonunda hâlâ erişilebilir pointer var (program sonunda free edilmemiş)Düşük

Gerçek bug: heap-use-after-free

uaf-bug.c
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

int main(void) {
    char *buf = malloc(64);
    strcpy(buf, "merhaba");

    free(buf);          /* buf serbest bırakıldı */

    /* HATA: free sonrası erişim — use-after-free */
    printf("%s\n", buf);

    return 0;
}
valgrind çıktısı
==12345== Invalid read of size 1
==12345==    at 0x4C33A22: strlen (vg_replace_strmem.c:459)
==12345==    at 0x1091C5: main (uaf-bug.c:12)
==12345==  Address 0x5204040 is 0 bytes inside a block of size 64 free'd
==12345==    at 0x4C30D3B: free (vg_replace_malloc.c:530)
==12345==    at 0x1091BE: main (uaf-bug.c:9)
==12345==  Block was alloc'd at
==12345==    at 0x4C2FB0F: malloc (vg_replace_malloc.c:309)
==12345==    at 0x10918B: main (uaf-bug.c:6)
==12345==
==12345== LEAK SUMMARY:
==12345==    definitely lost: 0 bytes in 0 blocks
==12345==    indirectly lost: 0 bytes in 0 blocks
==12345==    possibly lost:   0 bytes in 0 blocks
==12345==    still reachable: 0 bytes in 0 blocks

Suppression dosyası

my.supp
{
   openssl_false_positive
   Memcheck:Leak
   match-leak-kinds: reachable
   fun:malloc
   obj:*libcrypto*
}
terminal
valgrind --suppressions=my.supp --leak-check=full ./myapp

Bu bölümde öğrendikleriniz

  • valgrind, sentetik CPU üzerinde her bellek erişimini izler — overhead yüksektir ama hata tespiti çok doğrudur
  • "Definitely lost" kesin leak; "still reachable" genellikle düşük öncelikli
  • Stack trace ile hatanın tam satırı ve allocation noktası gösterilir
  • Suppression dosyası ile kütüphane kaynaklı false positive'ler bastırılır

07 AddressSanitizer (ASan)

ASan, compiler instrumentation ile çalışır — valgrind'den yaklaşık 2x daha hızlı, ancak derleme zamanında aktif edilmesi gerekir.

ASan nedir?

AddressSanitizer, LLVM/Clang ekibinin geliştirdiği ve GCC'nin de desteklediği bir compiler instrumentation aracıdır. Derleme sırasında her bellek erişiminin yanına kontrol kodu eklenir (shadow memory yöntemi). Valgrind gibi sanal bir CPU gerektirmediğinden overhead çok daha düşüktür.

Derleme ve çalıştırma

terminal
# ASan + sembol için -g şart
gcc -fsanitize=address -g -O1 uaf-bug.c -o myapp-asan

# Derleme sonrası normal gibi çalıştır
./myapp-asan
ASan çıktısı
==12346==ERROR: AddressSanitizer: heap-use-after-free on address 0x602000000010
READ of size 1 at 0x602000000010 thread T0
    #0 0x401234 in strlen /usr/include/string.h:395
    #1 0x401456 in main uaf-bug.c:12
    #2 0x7f3a2b main in libc-start.c

0x602000000010 is located 0 bytes inside of 64-byte region
FREED by thread T0 here:
    #0 0x7f3a2b in __interceptor_free sanitizer_common_interceptors.inc:752
    #1 0x4013e2 in main uaf-bug.c:9

ALLOCATED by thread T0 here:
    #0 0x7f3a2b in __interceptor_malloc sanitizer_common_interceptors.inc:823
    #1 0x401378 in main uaf-bug.c:6

Shadow bytes around the buggy address:
  0x0c047fff7fb0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff7fc0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x0c047fff7fd0:[fd]fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd

Sanitizer ailesi

-fsanitize=addressHeap/stack/global buffer overflow, use-after-free, use-after-return, use-after-scope
-fsanitize=undefinedUndefinedBehaviorSanitizer: integer overflow, null pointer deref, misaligned access, divide-by-zero
-fsanitize=threadThreadSanitizer: data race, lock-order inversion (ASan ile aynı anda kullanılamaz)
-fsanitize=memoryMemorySanitizer: uninitialized memory read (clang-only)
-fsanitize=leakLeakSanitizer: memory leak (ASan ile zaten dahil)
terminal
# ASan + UBSan kombinasyonu (yaygın CI konfigürasyonu)
gcc -fsanitize=address,undefined -g -O1 myapp.c -o myapp-san

# TSan ayrı derlenmeli
gcc -fsanitize=thread -g -O1 myapp.c -o myapp-tsan
UYARI

ASan ve diğer sanitizer'larla derlenmiş binary'leri asla production ortamında çalıştırmayın. Shadow memory için ~2x fazla RAM kullanırlar, overhead 1.5–2x düzeyindedir ve bazı sanitizer'lar (TSan) 5–15x overhead getirir. Bu araçlar yalnızca test ve CI pipeline'ları içindir.

Bu bölümde öğrendikleriniz

  • ASan, compiler instrumentation ile çalışır — valgrind'den 2x hızlı, derleme zamanında aktif edilir
  • ASan + UBSan kombinasyonu çoğu bug türünü tek derlemede yakalar
  • TSan, ASan ile aynı anda kullanılamaz; ayrı derleme gerekir
  • Sanitizer'lar sadece test/CI ortamında kullanılmalıdır

08 Pratik debug senaryoları

Gerçek dünya sorunları ve bu araçları sistematik olarak nasıl zincirleyeceğiniz — dört klasik senaryo.

Senaryo 1: "Uygulama açılmıyor"

Binary çalışıyor ama hemen çıkıyor, hata mesajı belirsiz. Önce shared library eksikliğini kontrol et, ardından config dosyası sorununu araştır.

senaryo1.sh
# 1. Shared library bağımlılıkları kontrol
ldd ./myapp
# → "libmylib.so.2 => not found"  varsa sorun bu

# 2. strace ile tam başlangıç izle
strace -e trace=openat,open,read -s 256 ./myapp 2>&1 | grep -E "ENOENT|EACCES"

# Tipik çıktı:
# openat(AT_FDCWD, "/etc/myapp/myapp.conf", O_RDONLY) = -1 ENOENT
# → Config dosyası eksik

# 3. execve başarısız oluyorsa
strace -e trace=execve ./myapp 2>&1
# execve("/usr/bin/myapp", ...) = -1 EACCES  → execute izni yok
chmod +x ./myapp

Senaryo 2: "Memory yavaş yavaş artıyor"

senaryo2.sh
# 1. Hızlı kontrol: RSS artışını izle
while true; do
    cat /proc/$(pidof myapp)/status | grep VmRSS
    sleep 5
done

# 2. smaps ile hangi mapping büyüyor?
cat /proc/$(pidof myapp)/smaps | grep -A 1 heap

# 3. valgrind ile leak tespiti (test ortamında)
valgrind --leak-check=full --track-origins=yes \
         --log-file=leak.log ./myapp &
sleep 60
kill %1
grep "definitely lost" leak.log

Senaryo 3: "Yüksek CPU"

senaryo3.sh
# 1. Hangi fonksiyon CPU yiyor? (canlı)
sudo perf top -p $(pidof myapp)

# 2. 30 saniye profil al
sudo perf record -F 99 -g -p $(pidof myapp) -- sleep 30

# 3. Flamegraph üret
perf script | /opt/flamegraph/stackcollapse-perf.pl \
             | /opt/flamegraph/flamegraph.pl > /tmp/cpu-flame.svg

# 4. Text rapor (en pahalı 10 fonksiyon)
perf report --stdio --sort overhead | head -20

Senaryo 4: "Segfault"

senaryo4.sh
# 1. Core dump aktif et
ulimit -c unlimited
echo "/tmp/core.%p" | sudo tee /proc/sys/kernel/core_pattern

# 2. Uygulamayı çalıştır (crash olsun)
./myapp
# Segmentation fault (core dumped)

# 3. gdb ile core analiz
gdb ./myapp /tmp/core.$(pidof myapp 2>/dev/null || echo "*")
# gdb prompt:
# (gdb) bt            ← backtrace
# (gdb) bt full       ← tüm yerel değişkenler
# (gdb) info locals   ← mevcut frame değişkenleri
# (gdb) frame 2       ← call stack'te 2. frame'e git

# 4. Alternatif: ASan ile yeniden derle (stack trace daha açık)
gcc -fsanitize=address -g -O1 myapp.c -o myapp-asan && ./myapp-asan

Debug araçları zinciri

  Sorun bildir
       │
       ├─ "Açılmıyor / hata"
       │    └─ ldd → strace -e file,network -s256 → grep ENOENT/EACCES
       │
       ├─ "Segfault / crash"
       │    └─ ulimit -c unlimited → gdb + core → bt full
       │         veya gcc -fsanitize=address → ASan output
       │
       ├─ "Yüksek CPU"
       │    └─ perf top → perf record -g → flamegraph → hotspot
       │
       └─ "Memory artıyor"
            └─ /proc/PID/status VmRSS → smaps
                 valgrind --leak-check=full → definitely lost
                  ya da gcc -fsanitize=address,leak
    

Bu bölümde öğrendikleriniz

  • "Uygulama açılmıyor" → ldd + strace -e file -s256 | grep ENOENT
  • "Memory artıyor" → /proc/PID/status + valgrind --leak-check=full
  • "Yüksek CPU" → perf top + perf record -g + flamegraph
  • "Segfault" → core dump + gdb backtrace veya ASan ile yeniden derleme