İçeriğe geç
academia.sh

Ders 10 / 15

Bellek Hata Ayıklama Araçları

Bir sızıntıyı elle sayaç tutarak bulmak gerçek bir programda mümkün değildir — bunu yapan bir araç çalıştırılır. Ders, kursun baştan beri kullandığı sayaçlı sarmalayıcının, böyle bir aracın yaptığı tek şeyi zaten modellediğini gösteriyor: her ayırmanın karşılığında bir bırakma var mı, yok mu.

Yayın Güncelleme

Bu derste

  • Bir denetim noktasında ayrılan ile bırakılan sayısının karşılaştırılmasının bir aracın yaptığını nasıl modellediğini gösterir
  • Dengeli bir ayırma dizisiyle dengesiz bir ayırma dizisinin aynı denetimden farklı sonuç aldığını ölçer
  • Bir aracın adını, çıktısını ya da bayrağını anmadan yalnız yakalandi/yakalanmadi ikiliğiyle konuşur
İçindekiler

Önceki ders bir sızıntının hiçbir hata mesajı ya da çökme üretmediğini, yalnız sayaç farkıyla göründüğünü gösterdi. Gerçek bir programda kimse elle ayrilan_sayisi() ile birakilan_sayisi()yi karşılaştırmaz — bunu yapan bir araç çalıştırılır. Bu dersin konusu böyle bir aracın kendisi değil; konusu, o aracın yaptığı tek şeyin ne olduğu. Kursun baştan beri kullandığı sayac.c, aslında bunu birinci dersten beri modelliyordu.

Bu, önceki dersin doğal devamı. Bellek Sızıntıları dersi sahipsiz sınıfın iki türünü gösterdi ve ikisini de elle tutulan bir sayaç farkıyla ölçtü. Ama gerçek bir programda kimse main içine ayrilan_sayisi() - birakilan_sayisi() satırı yazıp sonucu elle okumaz; program büyüdükçe, yüzlerce ayırma çağrısı arasında hangi birinin karşılıksız kaldığını elle izlemek pratik değildir. Bu yüzden bu işi otomatikleştiren bir katman devreye girer — ve bu dersin tek iddiası, o katmanın yaptığı şeyin, kursun baştan beri kullandığı sayaçlı sarmalayıcıdan daha karmaşık bir fikre dayanmadığıdır.

Bir Denetim Noktası Neyi Karşılaştırır

Bir aracın yaptığı, özünde tek bir işlemdir: programın belirli bir noktasında (çoğunlukla çıkışında) o ana kadar yapılan her ayırma çağrısının karşılığında bir bırakma çağrısı olup olmadığını sayar. Karşılık varsa denetim temiz çıkar; yoksa, karşılığı bulunamayan her blok için bir bulgu bildirilir. Bu kursta bu karşılaştırma, sayac.cnin tuttuğu iki sayının farkıdır — ayrilan_sayisi() eksi birakilan_sayisi(). Fark sıfırsa yakalanmadi, sıfır değilse yakalandi.

// sayac.h — her ayirmayi ve her birakmayi sayan sarmalayici
#ifndef SAYAC_H
#define SAYAC_H

#include <stddef.h>

void *say_ayir(size_t boyut);
void *say_buyut(void *eski, size_t boyut);
void say_birak(void *blok);
int ayrilan_sayisi(void);
int birakilan_sayisi(void);

#endif
// sayac.c — sayaclar yalniz bu birimde tutulur
#include <stdlib.h>
#include "sayac.h"

static int ayrilan = 0;
static int birakilan = 0;

void *say_ayir(size_t boyut) {
    void *blok = malloc(boyut);
    if (blok != NULL) {
        ayrilan++;
    }
    return blok;
}

void *say_buyut(void *eski, size_t boyut) {
    int yeni_blok = (eski == NULL);
    void *yeni = realloc(eski, boyut);
    if (yeni != NULL && yeni_blok) {
        ayrilan++;
    }
    return yeni;
}

void say_birak(void *blok) {
    if (blok != NULL) {
        birakilan++;
    }
    free(blok);
}

int ayrilan_sayisi(void) {
    return ayrilan;
}

int birakilan_sayisi(void) {
    return birakilan;
}

BE12. sayac.c bu kursun ilk dersinden beri değişmeden duruyor ve hiçbir yerde “araç” sözcüğünü taşımıyor — yalnız iki sayaç ve dört işlev. Ama yaptığı şey, bir denetim noktasında ayrılanla bırakılanı karşılaştırmaktan ibaret; bu, gerçek bir aracın yaptığı işin küçültülmüş ve elle kurulmuş hâlidir. Fark, gerçek bir araç bu karşılaştırmayı programın kaynak koduna hiç dokunmadan yapar; burada aynı karşılaştırma sayac.cnin içine elle yazıldı.

Dengeli ve Dengesiz Dizi

// denetim.c — bir denetim noktasinin dengeli ve dengesiz diziyi ayirt etmesi
#include <stdio.h>
#include "sayac.h"

static const char *denetle(int fark) {
    return fark != 0 ? "yakalandi" : "yakalanmadi";
}

static void gozle(const char *ad, const char *kanit) {
    printf("%-30s%s\n", ad, kanit);
}

int main(void) {
    char tampon[64];
    int once, fark_sonra;

    printf("%-30s%s\n", "gozlem", "kanit");

    /* --- dengeli dizi: her ayirmanin karsiligi var --- */
    once = ayrilan_sayisi() - birakilan_sayisi();
    int *birinci = say_ayir(2 * sizeof *birinci);
    int *ikinci = say_ayir(3 * sizeof *ikinci);
    say_birak(birinci);
    say_birak(ikinci);
    fark_sonra = (ayrilan_sayisi() - birakilan_sayisi()) - once;
    snprintf(tampon, sizeof tampon, "denetim noktasi: %s", denetle(fark_sonra));
    gozle("dengeli dizi", tampon);

    /* --- dengesiz dizi: bir ayirmanin karsiligi yok --- */
    once = ayrilan_sayisi() - birakilan_sayisi();
    int *ucuncu = say_ayir(2 * sizeof *ucuncu);
    int *dorduncu = say_ayir(3 * sizeof *dorduncu);
    say_birak(ucuncu);
    /* dorduncu bilerek birakilmiyor: dengesiz dizi burada kuruluyor */
    fark_sonra = (ayrilan_sayisi() - birakilan_sayisi()) - once;
    snprintf(tampon, sizeof tampon, "denetim noktasi: %s", denetle(fark_sonra));
    gozle("dengesiz dizi", tampon);

    say_birak(dorduncu);
    return 0;
}
gozlem                        kanit
dengeli dizi                  denetim noktasi: yakalanmadi
dengesiz dizi                 denetim noktasi: yakalandi

BE13. dengeli dizi gözleminde iki ayırmanın ikisi de kendi bloğu içinde bırakıldı; denetim noktasındaki fark 0 ve sonuç yakalanmadi. dengesiz dizi gözleminde ise dorduncu o denetim noktasına gelene kadar bırakılmadı — kod bunu kendisi, yorum satırıyla işaretleyerek yaptı, çünkü bir aracın gerçekte ne bulduğunu göstermenin tek tanımlı yolu bu. Fark 1e çıktı ve sonuç yakalandi. Denetim noktasından sonra dorduncu yine de bırakıldı — ders, tanımlı olmayan bir durumu (programı sızıntılı bitirmeyi) arkasında bırakmıyor.

Üç Ardışık Denetim Noktası

Önceki bölümdeki iki gözlem birbirinden bağımsız iki dizi kurdu — her biri kendi once değerinden başladı. Gerçek bir programda denetim noktaları böyle yalıtılmış durmaz; art arda gelir ve her biri yalnız kendisinden önceki noktadan bu yana olan aralığı görür. Bu, bir sızıntının hangi denetim noktasında yakalandığının, sızıntının nerede oluştuğuyla aynı yer olduğunu gösterir — ne bir önceki noktada, ne bir sonrakinde.

// ardisik.c — sizintinin tam olustugu denetim noktasinda yakalanmasi
#include <stdio.h>
#include "sayac.h"

static const char *denetle(int fark) {
    return fark != 0 ? "yakalandi" : "yakalanmadi";
}

int main(void) {
    char tampon[64];
    int nokta_once = ayrilan_sayisi() - birakilan_sayisi();

    /* --- birinci nokta: temiz bir ayirma-birakma cifti --- */
    int *a = say_ayir(sizeof *a);
    say_birak(a);
    int fark1 = (ayrilan_sayisi() - birakilan_sayisi()) - nokta_once;
    snprintf(tampon, sizeof tampon, "nokta 1: %s", denetle(fark1));
    printf("%-30s%s\n", "ardisik denetim", tampon);
    nokta_once = ayrilan_sayisi() - birakilan_sayisi();

    /* --- ikinci nokta: bilerek birakilmayan bir blok --- */
    int *b = say_ayir(sizeof *b);
    int fark2 = (ayrilan_sayisi() - birakilan_sayisi()) - nokta_once;
    snprintf(tampon, sizeof tampon, "nokta 2: %s", denetle(fark2));
    printf("%-30s%s\n", "ardisik denetim", tampon);
    nokta_once = ayrilan_sayisi() - birakilan_sayisi();

    /* --- ucuncu nokta: b'den bagimsiz, kendi icinde dengeli yeni bir cift --- */
    int *c = say_ayir(sizeof *c);
    say_birak(c);
    int fark3 = (ayrilan_sayisi() - birakilan_sayisi()) - nokta_once;
    snprintf(tampon, sizeof tampon, "nokta 3: %s", denetle(fark3));
    printf("%-30s%s\n", "ardisik denetim", tampon);

    say_birak(b);
    return 0;
}
ardisik denetim               nokta 1: yakalanmadi
ardisik denetim               nokta 2: yakalandi
ardisik denetim               nokta 3: yakalanmadi

BE14. b ikinci noktada ayrılıp o noktaya kadar hiç bırakılmadı, bu yüzden nokta 2 sonucu yakalandi. Üçüncü nokta ise b’den tamamen bağımsız, kendi içinde dengeli yeni bir çift (c) kuruyor — kendi aralığında bir ayırmanın karşılığında bir bırakma var, bu yüzden nokta 3: yakalanmadi. b’nin sızıntısı ikinci noktadan sonra hiçbir yerde yeniden raporlanmıyor; üçüncü nokta onu bir daha saymıyor, çünkü her nokta yalnızca kendi aralığında karşılıksız kalan ayırmaları görür, daha önce zaten raporlanmış bir aralığı tekrar taramaz. Bu üç sonuç birlikte önemli bir şeyi gösteriyor: bir sızıntı gerçek zamanda tam hangi aralıkta oluştuysa, denetim de tam o aralıkta yakalar — ne bir önceki noktada (henüz ayrılmamıştı), ne bir sonraki noktada (o aralığın konusu değil). b sonunda yine say_birak ile bırakılıyor, ama bu bırakma hiçbir noktanın ölçtüğü aralığın içinde değil — kod yalnız programı sızıntılı bitirmemek için bunu yapıyor, denetim anlatısının bir parçası olarak değil.

Gecikmiş Bir Bırakma Yeni Bir Sızıntıyı Maskeleyebilir

Üç ardışık noktanın gösterdiği düzenek bir varsayıma dayanıyordu: her aralıktaki ayırma ve bırakma çağrıları o aralığın kendi olayları. Ama gerçek bir programda bir blok, bir denetim noktasında ayrılıp başka bir noktada bırakılabilir — ve bu, sayaç farkının aynı aralıkta oluşan başka bir sızıntıyı sayısal olarak dengeleyip gizlemesine yol açabilir. Bu, sayaç tabanlı denetimin bir kusuru değil, sınırıdır: denetim yalnız net farkı görür, hangi bloğun hangi olaya ait olduğunu görmez.

// gecikmis.c — onceki araliktan tasinan bir birakmanin yeni bir sizintiyi maskelemesi
#include <stdio.h>
#include "sayac.h"

static const char *denetle(int fark) {
    return fark != 0 ? "yakalandi" : "yakalanmadi";
}

int main(void) {
    char tampon[64];
    int nokta_once = ayrilan_sayisi() - birakilan_sayisi();

    /* --- A noktasi: d ayriliyor, bu araliktan hic birakilmiyor --- */
    int *d = say_ayir(sizeof *d);
    int farkA = (ayrilan_sayisi() - birakilan_sayisi()) - nokta_once;
    snprintf(tampon, sizeof tampon, "nokta A: %s", denetle(farkA));
    printf("%-30s%s\n", "gecikmis birakma", tampon);
    nokta_once = ayrilan_sayisi() - birakilan_sayisi();

    /* --- B noktasi: yeni bir sizinti (e) olusuyor, ama d de burada birakiliyor --- */
    int *e = say_ayir(sizeof *e);
    say_birak(d);
    int farkB = (ayrilan_sayisi() - birakilan_sayisi()) - nokta_once;
    snprintf(tampon, sizeof tampon, "nokta B: %s", denetle(farkB));
    printf("%-30s%s\n", "gecikmis birakma", tampon);

    say_birak(e);
    return 0;
}
gecikmis birakma              nokta A: yakalandi
gecikmis birakma              nokta B: yakalanmadi

BE15. A noktası, d’nin o aralıkta bırakılmadığını doğru biçimde yakalandi olarak bildiriyor — tıpkı önceki bölümdeki nokta 2 gibi. B noktasında ise iki şey aynı anda oluyor: e yeni ayrılıyor ve hiç bırakılmıyor bu aralıkta, üstelik d de (önceki aralıktan kalan borç) tam bu aralıkta bırakılıyor. Sayaç farkı bu iki olayı ayrı ayrı görmüyor, yalnız toplamlarını görüyor: +1 (yeni ayrılan e) ile -1 (geç bırakılan d) birbirini götürüyor ve farkB 0 çıkıyor — sonuç yakalanmadi, oysa bu aralıkta gerçekten yeni bir blok (e) sızdırılmış durumda. e yalnızca main’in sonunda, hiçbir denetim noktasının ölçmediği bir yerde bırakılıyor. Bu, bir sayaç farkının sıfır olmasının, o aralıkta hiçbir sorun olmadığı anlamına gelmediğini gösteriyor — yalnız ayrılan ile bırakılanın sayıca eşitlendiği anlamına gelir. Gerçek araçların çoğu bu sınırı, yalnız sayıyı değil her bloğun kendi kimliğini (hangi satırda ayrıldığını) izleyerek aşar; bu kurs böyle bir kimlik izlemiyor, çünkü konusu aracın kendisi değil, aracın dayandığı temel fikir.

Sınırlayıcı Ölçüm

Bir denetim noktası yalnız sayar — hangi bloğun hangi satırda ayrıldığını, o bloğun programın hangi bölümüne ait olduğunu, ya da bloğun içeriğinin ne olduğunu bilmez. Bildiği tek şey, karşılığı bulunamayan ayırma çağrısının sayısıdır. Bu yüzden bu kursta bir aracın verdiği yanıt yalnız iki değerden biri olarak yazılıyor: yakalandi ya da yakalanmadi. Bu ikilik dışında hiçbir çıktı biçimi, hiçbir bayrak, hiçbir araç adı bu derste geçmiyor — çünkü hangi aracın hangi çıktıyı ürettiği ortama bağlıdır, ölçülen düzenek değildir.

Bir aracın bulamadığı şey de önemlidir: bir işaretçinin üzerine yazılmadan önce eski değerinin bir yere kaydedilip kaydedilmediğini, ya da bir bloğun sahiplik sözleşmesinin sahip.hdeki yorumla uyuşup uyuşmadığını bir denetim noktası bilemez — bunlar kaynak kodun okunmasını gerektirir. Denetim noktası yalnız sayının kendisini doğrular; sayının neden o değere ulaştığını açıklamaz.

Araç adının ve çıktı biçiminin bu derste hiç geçmemesinin tek nedeni özen değil; bir gerekliliktir. Aynı denetim fikri, farklı ortamlarda farklı araçlarla, farklı çıktı biçimleriyle uygulanabilir — bazısı programı ayrı bir süreç içinde çalıştırır, bazısı ayırma işlevlerinin kendisini değiştirir, bazısı yalnız çıkıştaki özeti yazar. Bu kurs hangi ortamda koşacağını bilemeyeceği için, ölçtüğü şeyi ortamdan bağımsız tek bir düzeneğe indirgiyor: bir sayı ve o sayının sıfır olup olmadığı. yakalandi ile yakalanmadi bu yüzden bir basitleştirme değil, dersin gerçek konusunun kendisidir.

Özet

  • Bir bellek hata ayıklama aracının yaptığı tek şey, bir denetim noktasında ayrılan ile bırakılan sayısını karşılaştırmaktır; bu dersin ölçtüğü bellek sınıfı yine sahipsizdır.
  • sayac.c, kursun ilk dersinden beri bu karşılaştırmayı elle yapıyordu; bu ders bunu ilk kez adlandırdı.
  • Dengeli bir dizide denetim noktası yakalanmadi, dengesiz bir dizide yakalandi sonucunu verdi; ikisi dışında üçüncü bir sonuç yok.
  • Hiçbir araç adı, çıktı biçimi ya da bayrak bu derste geçmedi — bunlar ortama bağlıdır, ölçülen düzenek değildir.

Sonraki Adım

Bu ders belleği modüller arasında dolaşırken izledi — ayrılan bir bloğun kimin elinde olduğu, denetim noktasına kadar nasıl değiştiği. Dinamik Bellek konusunun son dersi ise geri, en başa dönüyor: dört dil gözlemi ile ayir ve birak gözlemini yan yana koyup, bir bloğun hangi bölgede durduğunun — otomatik, statik ya da dinamik — sahiplik sorusuyla nasıl örtüştüğünü gösteriyor.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ ↵ Esc gezin · aç · kapat