Geliştirici Araçları
TEKNİK REHBER GELİŞTİRİCİ ARAÇLARI PROFİLİNG 2026

valgrind —
memory & CPU profiler.

Shadow memory ile memory leak, use-after-free ve uninitialized read tespiti. Callgrind CPU profiling, Massif heap analizi, Helgrind thread race condition dedektörü. Cross-ARM build notu.

00 Valgrind çerçevesi — shadow memory nasıl çalışır

Valgrind, programınızı kendi sanal makine (JIT çevirici) üzerinde çalıştırır. Her bellek hücresini bir "shadow byte" ile izler. Bu yaklaşım son derece güçlüdür ama ciddi bir yavaşlamaya yol açar.

Valgrind'in çekirdek mekanizması shadow memory'dir: her gerçek bellek baytı için bir ya da iki "gölge" bayt tutulur. Bu gölge baytlar, ilgili belleğin durumunu kodlar — tahsis edilmiş mi? başlatılmış mı? hâlâ sahiplenilmiş mi? Program her bellek okuma/yazma işleminde valgrind bu gölge değeri kontrol eder.

bash — kurulum
# Debian/Ubuntu
sudo apt install valgrind

# ARM native (hedef üzerinde)
sudo apt install valgrind   # ARM paket deposundan

# Sürüm kontrolü
valgrind --version
# valgrind-3.19.0

# Debug sembolleriyle derle (ZORUNLU — daha anlamlı çıktı için)
gcc -g -O0 -o myapp sensor.c main.c

Performans etkisi

Memcheck
~10-30x yavaşlama. Her bellek erişiminde shadow kontrolü yapılır.
Callgrind
~10-100x yavaşlama. Her talimat sayılır.
Massif
~10-20x yavaşlama. Heap snapshot'lar periyodik alınır.
Helgrind
~20-50x yavaşlama. Thread senkronizasyon geçmişi tutulur.

01 Memcheck — memory leak ve use-after-free

Memcheck, Valgrind'in varsayılan aracıdır. Dört ana hata türünü tespit eder: memory leak, use-after-free, buffer overflow ve uninitialized memory read.

bash — temel memcheck
# Varsayılan araç memcheck'tir
valgrind ./myapp

# Açıkça belirt
valgrind --tool=memcheck ./myapp

# Argüman geçir
valgrind ./myapp --port /dev/ttyS0 --baud 115200

Use-after-free tespiti

C kodu — use-after-free hatası
/* sensor.c */
SensorHandle *s = sensor_create();
sensor_init(s);
sensor_destroy(s);      /* s free edildi */
int val = sensor_read(s);  /* BUG: freed pointer kullanımı */
valgrind çıktısı — use-after-free
==1234== Invalid read of size 4
==1234==    at 0x10498: sensor_read (sensor.c:45)
==1234==    at 0x104e8: main (main.c:23)
==1234==  Address 0x5204e80 is 0 bytes inside a block of size 48 free'd
==1234==    at 0x4848899: free (in /usr/lib/valgrind/vgpreload_memcheck.so)
==1234==    at 0x104d2: sensor_destroy (sensor.c:60)
==1234==    at 0x104e0: main (main.c:22)
==1234==  Block was alloc'd at
==1234==    at 0x4848A28: malloc (in /usr/lib/valgrind/vgpreload_memcheck.so)
==1234==    at 0x10454: sensor_create (sensor.c:15)

Buffer overflow tespiti

valgrind çıktısı — heap buffer overflow
==1234== Invalid write of size 1
==1234==    at 0x109A4: fill_buffer (sensor.c:88)
==1234==    by 0x10A10: main (main.c:45)
==1234==  Address 0x5204eb0 is 0 bytes after a block of size 256 alloc'd
==1234==    at 0x4848A28: malloc (...)
==1234==    by 0x10980: fill_buffer (sensor.c:82)
# 256 baytlık buffer'a 257 bayt yazılmaya çalışıldı

02 Memcheck — --leak-check=full, --track-origins, --suppressions

Gelişmiş Memcheck seçenekleri: tam leak raporu, başlatılmamış değerlerin kaynağını izleme ve bilinen false positive'leri bastırma.

bash
# Kapsamlı leak analizi
valgrind \
  --leak-check=full \
  --show-leak-kinds=all \
  --track-origins=yes \
  --verbose \
  ./myapp 2>&1 | tee valgrind_report.txt
valgrind --leak-check=full çıktısı
==1234== LEAK SUMMARY:
==1234==    definitely lost: 48 bytes in 1 blocks
==1234==    indirectly lost: 256 bytes in 3 blocks
==1234==      possibly lost: 0 bytes in 0 blocks
==1234==    still reachable: 1,024 bytes in 4 blocks
==1234==         suppressed: 0 bytes in 0 blocks
==1234==
==1234== 48 bytes in 1 blocks are definitely lost in loss record 1 of 2
==1234==    at 0x4848A28: malloc (vgpreload_memcheck.so)
==1234==    at 0x10454: sensor_create (sensor.c:15)
==1234==    at 0x104d0: main (main.c:18)
# "definitely lost": pointer hiçbir değişkende yok → gerçek leak
# "still reachable": program sonunda pointer hâlâ erişilebilir

--track-origins ile başlatılmamış okuma

valgrind --track-origins=yes çıktısı
==1234== Conditional jump or move depends on uninitialised value(s)
==1234==    at 0x10510: process_data (data.c:34)
==1234==    by 0x104e8: main (main.c:27)
==1234==  Uninitialised value was created by a heap allocation
==1234==    at 0x4848A28: malloc (vgpreload_memcheck.so)
==1234==    at 0x10454: sensor_create (sensor.c:15)
# malloc ile tahsis edilmiş ama memset/başlatma yapılmamış

Suppressions — false positive bastırma

bash
# Suppression dosyası oluştur (genellikle glibc/openssl hataları için)
valgrind --gen-suppressions=all ./myapp 2>&1 | \
  grep -A 20 "^{" > myapp.supp

# Suppression dosyasını kullan
valgrind --suppressions=myapp.supp ./myapp

# Valgrind çıkış kodu: hata varsa 0 dışı döner
valgrind --error-exitcode=1 ./myapp
echo "Valgrind çıkış kodu: $?"
# CI/CD pipeline'da kullanışlı: hata varsa build başarısız olur

03 Callgrind — CPU profiling

Callgrind, her fonksiyonun kaç talimat çalıştırdığını ve çağrı ağacını kaydeder. gerçek zaman ölçümü değil talimat sayısıdır — bu nedenle deterministtir.

bash
# Callgrind ile çalıştır
valgrind --tool=callgrind ./myapp
# callgrind.out.1234 dosyası oluşur

# callgrind_annotate ile konsola rapor
callgrind_annotate callgrind.out.1234

# Belirli bir fonksiyon
callgrind_annotate --auto=yes callgrind.out.1234 | grep -A 20 "sensor_read"

# Özet — en pahalı fonksiyonlar
callgrind_annotate callgrind.out.1234 | head -50
callgrind_annotate çıktısı
--------------------------------------------------------------------------------
Profile data file 'callgrind.out.1234' (creator: callgrind-3.19.0)
--------------------------------------------------------------------------------
I1 cache:
D1 cache:
LL cache:
--------------------------------------------------------------------------------
Ir
--------------------------------------------------------------------------------
1,234,567  PROGRAM TOTALS

--------------------------------------------------------------------------------
        Ir  file:function
--------------------------------------------------------------------------------
   876,543  sensor.c:json_parse
   234,567  libc.so.6:(below main)
   123,456  sensor.c:sensor_read
    45,678  data.c:process_data
# Ir = Instruction References (talimat sayısı)
# json_parse: toplam talimatların %71'i — optimize edilmeli!

--instr-atstart=no ile seçici profilleme

bash
# Başlangıçta enstrümantasyonu kapat
valgrind --tool=callgrind --instr-atstart=no ./myapp
# Sadece istenen bölümde enstrümantasyonu aç

# C kodunda seçici enstrümantasyon:
# #include <valgrind/callgrind.h>
# CALLGRIND_START_INSTRUMENTATION;
# sensor_read(...);
# CALLGRIND_STOP_INSTRUMENTATION;
# CALLGRIND_DUMP_STATS;

# Çalışan prosese sinyal gönder (interactive)
callgrind_control -i on   # enstrümantasyonu aç
callgrind_control -i off  # enstrümantasyonu kapat
callgrind_control -d      # stats dump et

04 kcachegrind — Callgrind görselleştirme

kcachegrind, callgrind.out dosyasını görsel çağrı ağacı ve kaynak annotasyonu olarak sunar. Profiling iş akışının en etkili parçasıdır.

bash
# kcachegrind kurulumu
sudo apt install kcachegrind

# Callgrind çıktısını aç
kcachegrind callgrind.out.1234 &

# Terminal tabanlı alternatif: callgrind_annotate
callgrind_annotate --tree=both callgrind.out.1234 | less

# Pyprof2calltree: Python profiler çıktısını callgrind formatına çevir
# (Python uygulamaları için)
python3 -m cProfile -o output.pstats myapp.py
pyprof2calltree -i output.pstats -o callgrind.python
kcachegrind callgrind.python

kcachegrind üç ana görünüm sunar: Flat Profile (fonksiyon başına maliyet), Call Graph (çağrı ilişkileri ağacı) ve Source Annotated (satır bazında maliyet). Embedded geliştirmede en sık kullanılan, hotspot fonksiyonu bulmak için Flat Profile'dır.

05 Massif — heap profiler

Massif, heap bellek kullanımını zaman içinde izler. Peak kullanımı ve en fazla bellek tahsis eden kod yollarını tespit eder. Uzun süre çalışan daemon'larda bellek büyümesini izlemek için idealdir.

bash
# Massif ile çalıştır
valgrind --tool=massif ./myapp
# massif.out.1234 dosyası oluşur

# ms_print ile metin raporu
ms_print massif.out.1234 | less
ms_print çıktısı — zaman/bellek grafiği
    MB
4.543^                                                               #
     |                                                           @@@##
     |                                                       @@@@@####
     |                                                   @@@@@@@######
     |                                               :@@@@@@@@@########
     |                                         @@@:::@@@@@@@@@##########
     |                                     @@@@@@::::@@@@@@@@@@##########
     |                                 @@@@@@@@@@::::@@@@@@@@@@@##########
     |                             @@@@@@@@@@@@@@::::@@@@@@@@@@@@##########
     |         @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@:::@@@@@@@@@@@@@##########
     |      @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@:::@@@@@@@@@@@@@@##########
     |   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@:::@@@@@@@@@@@@@@@#########
   0 +----------------------------------------------------------------------->Gi
     0                                                                   4.21

# # = heap         @ = stack        : = diğer
# Peak: 4.543 MB — bu noktadaki stack trace:

--------------------------------------------------------------------------------
  n        time(i)         total(B)   useful-heap(B) extra-heap(B)    stacks(B)
--------------------------------------------------------------------------------
 10      4,234,567        4,766,208        4,763,904         2,304            0

 98.95% (4,763,904B) (heap allocation functions) ...
   99.91% (4,759,040B) 0x10490: sensor_buffer_alloc (sensor.c:34)
     99.91% (4,759,040B) 0x104d0: sensor_loop (main.c:67)
bash — gelişmiş massif seçenekleri
# Stack belleği de izle
valgrind --tool=massif --stacks=yes ./myapp

# Snapshot sıklığı
valgrind --tool=massif --detailed-freq=10 ./myapp

# Sayfalara göre (mmap dahil)
valgrind --tool=massif --pages-as-heap=yes ./myapp

# massif-visualizer (GUI alternatif)
massif-visualizer massif.out.1234

06 Helgrind — thread race condition dedektörü

Helgrind, çok iş parçacıklı programlarda veri yarışı (data race), mutex deadlock ve POSIX iş parçacığı hatalarını tespit eder. Embedded gerçek zamanlı sistemlerde kritik bir güvenlik aracıdır.

bash
# Helgrind ile çalıştır
valgrind --tool=helgrind ./myapp_threaded 2>&1 | tee helgrind_report.txt
C kodu — race condition örneği
/* İki thread, mutex olmadan g_sensor_value'ya erişiyor */
int g_sensor_value = 0;   /* global, korumasız */

void *reader_thread(void *arg) {
    while (1) {
        printf("Value: %d\n", g_sensor_value);  /* RACE! */
        usleep(100000);
    }
}

void *writer_thread(void *arg) {
    while (1) {
        g_sensor_value = adc_read();            /* RACE! */
        usleep(50000);
    }
}
helgrind çıktısı
==1234== Possible data race during read of size 4 at 0x60104C
==1234==    at 0x10510: reader_thread (threaded.c:12)
==1234==    by 0x4C38A85: mythread_wrapper (hg_intercepts.c:406)
==1234==  This conflicts with a previous write of size 4 by thread #2
==1234==    at 0x10590: writer_thread (threaded.c:22)
==1234==    by 0x4C38A85: mythread_wrapper (hg_intercepts.c:406)
==1234==  Lock at 0x60102C was first observed by thread #1, not held
bash — DRD (Data Race Detector) alternatif
# DRD: Helgrind'e alternatif, daha az bellek kullanır
valgrind --tool=drd ./myapp_threaded

# DRD + mutex kontrolü devre dışı
valgrind --tool=drd --check-stack-var=yes ./myapp_threaded

07 Pratik: C embedded programda leak bul

Gerçek bir embedded C uygulamasında Valgrind ile memory leak bulma ve düzeltme senaryosu.

Örnek program — sensor_daemon.c

C — hatalı kod
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
    int  fd;
    int  channel;
    char *config_path;    /* heap tahsis edilecek */
} SensorHandle;

SensorHandle *sensor_create(const char *config) {
    SensorHandle *s = malloc(sizeof(SensorHandle));
    s->config_path = strdup(config);  /* heap tahsis */
    s->fd = -1;
    s->channel = 0;
    return s;
}

void sensor_destroy(SensorHandle *s) {
    /* BUG: config_path free edilmiyor! */
    free(s);
}

int main(void) {
    for (int i = 0; i < 100; i++) {
        SensorHandle *s = sensor_create("/etc/sensor.conf");
        /* ... kullan ... */
        sensor_destroy(s);
    }
    return 0;
}
bash — valgrind ile analiz
# Derle (-g zorunlu, -O0 önerilir)
gcc -g -O0 -o sensor_daemon sensor_daemon.c

# Valgrind ile çalıştır
valgrind --leak-check=full --show-leak-kinds=all \
  --track-origins=yes ./sensor_daemon
valgrind çıktısı
==1234== HEAP SUMMARY:
==1234==     in use at exit: 1,900 bytes in 100 blocks
==1234==   total heap usage: 200 allocs, 100 frees, 6,900 bytes allocated

==1234== 1,900 bytes in 100 blocks are definitely lost in loss record 1 of 1
==1234==    at 0x4848899: malloc (vgpreload_memcheck.so)
==1234==    at 0x401260: strdup (strdup.c:42)
==1234==    at 0x10490: sensor_create (sensor_daemon.c:13)
==1234==    at 0x104e8: main (sensor_daemon.c:25)

==1234== LEAK SUMMARY:
==1234==    definitely lost: 1,900 bytes in 100 blocks
==1234==    total heap usage: 200 allocs, 100 frees

Düzeltme

C — düzeltilmiş kod
void sensor_destroy(SensorHandle *s) {
    if (!s) return;
    free(s->config_path);   /* önce iç heap'i serbest bırak */
    s->config_path = NULL;  /* dangling pointer güvenliği */
    free(s);
}

/* Valgrind çalıştır — temiz çıktı:
   LEAK SUMMARY: definitely lost: 0 bytes in 0 blocks
   ERROR SUMMARY: 0 errors from 0 contexts */

08 Cross-ARM Valgrind build notu

Valgrind ARM hedefte native olarak çalışır — cross-derleme yapıp hedef üzerinde çalıştırmak gerekir. x86 geliştirici makinesinde ARM binary'yi doğrudan analiz etmek mümkün değildir.

bash — QEMU + Valgrind yaklaşımı
# Seçenek 1: QEMU user-mode emulation ile x86 üzerinde ARM çalıştır
# (Valgrind QEMU içinde çalışmaz doğrudan, ama test için faydalı)
qemu-arm-static -L /opt/sysroot ./sensor_daemon_arm

# Seçenek 2: ARM board/VM üzerinde native valgrind
# Buildroot: BR2_PACKAGE_VALGRIND=y  (ARM destekli)
# Yocto: IMAGE_INSTALL:append = " valgrind"

# Seçenek 3: Docker ARM emulation (çok yavaş ama erişilebilir)
docker run --rm --platform linux/arm/v7 \
  -v $(pwd):/work ubuntu:22.04 \
  bash -c "apt-get install -y valgrind gcc && \
           cd /work && gcc -g -o sensor sensor.c && \
           valgrind --leak-check=full ./sensor"

ARM'da Valgrind kısıtlamaları

bash
# Valgrind ARM desteği: ARMv7 ve AArch64 desteklenir
# ARMv5 ve öncesi: desteklenmez

# ARM hard-float ile derleme
arm-linux-gnueabihf-gcc -g -O0 \
  -mfloat-abi=hard -mfpu=neon-vfpv4 \
  -o sensor_armhf sensor.c

# Valgrind ARM özel sorunları:
# - Thumb-2 talimatlarının tamamı desteklenmeyebilir
# - NEON intrinsic'ler bazen yanlış analiz edilir
# → Sorun çıkarsa: -O0 ve -mfpu=vfpv3 ile dene

# AArch64 (64-bit ARM — daha iyi destek)
aarch64-linux-gnu-gcc -g -O0 -o sensor_a64 sensor.c
# AArch64'te Valgrind desteği ARMv7'den çok daha iyidir

AddressSanitizer — Valgrind alternatifi

bash — ASan (daha az yavaş)
# Valgrind çalışmazsa: ASan derleme zamanı çözümü
arm-linux-gnueabihf-gcc -g -O1 \
  -fsanitize=address \
  -fno-omit-frame-pointer \
  -o sensor_asan sensor.c

# Çalıştır (hedef üzerinde)
./sensor_asan

# Yalnızca bellek sızıntısı (LeakSanitizer)
arm-linux-gnueabihf-gcc -g -O1 -fsanitize=leak -o sensor_lsan sensor.c

# ASan avantajları: ~2x yavaşlama (Valgrind'in 10x'ine karşı)
# ASan dezavantajı: derleme zamanı enstrümantasyon gerekir
#                   binary boyutu büyür (~2x)
İPUCU — hangi aracı seç?

Geliştirme aşamasında AddressSanitizer tercih et — çok daha hızlı ve cross-compile ile doğrudan çalışır. Valgrind Memcheck'i, ASan'ın bulamadığı karmaşık heap hatalarında veya üçüncü parti kütüphane analizinde kullan. Callgrind'i performans profilleme için, Massif'i ise daemon'larda bellek büyümesi için kullan.