İçeriğe geç
academia.sh

Ders 14 / 15

Hata Ayıklayıcı Kullanımı

Bir kesme noktasının adımlar arasındaki ara değeri neden hiç görmediğini, gözetleme noktasının aynı değeri neden gördüğünü C içinde modelleyerek ölçer.

Yayın Güncelleme

Bu derste

  • Kesme noktası tabanlı gözlemin adımlar arasındaki bir değeri neden atladığını ölçer
  • Gözetleme noktasının her yazmada durarak aynı değeri neden gördüğünü gösterir
  • Çekirdek dökümünün ne için kullanıldığını, koşturmadan ölçüm çerçevesine yerleştirir
İçindekiler

Bir test kaldığında hangi girdinin hangi adımda beklenenden saptığı bilinmez — yalnız sonuç bilinir. Bir hata ayıklayıcı, çalışan bir programın ara değerlerini durdurarak ya da izleyerek görünür kılar. Ama “görünür kılmak” tek bir anlam taşımıyor: bir hata ayıklayıcının neyi gördüğü, ona nasıl durması söylendiğine bağlı.

Bir önceki ders, bir testin başarısız olduğunu gösteriyordu ama neden başarısız olduğunu göstermiyordu. Onaylama kalırsa bilinen tek şey, koşulun yanlış olduğu; koşula giden değerin hangi adımda o hale geldiği testin kendi çıktısında görünmez. Bir hata ayıklayıcı bu boşluğu, programı istenen bir noktada durdurarak ya da bir değişkenin her değişimini izleyerek doldurur. İki yaklaşım da aynı çalışan süreci gözlemliyor olsa bile, hangi adımların görüldüğü stratejiye göre değişir — ve bu fark, bir hatayı ararken hangi değerin görülüp hangisinin kaçırıldığını doğrudan belirler.

Bu ders üç durma stratejisini karşılaştırıyor: konuma göre duran (kesme noktası), her değişimde duran (gözetleme noktası) ve bir duruma göre duran (koşullu kesme noktası). Üçü de aynı çalışan süreci gözlemliyor, ama her biri farklı bir soruya cevap vermek için kuruldu — “şu adıma gelince ne oluyor”, “bu değer ne zaman değişiyor” ve “bu durum ne zaman gerçekleşiyor”. Bir dördüncü araç, çekirdek dökümü, bambaşka bir zamanda devreye girer: süreç artık çalışmıyorken, geride bıraktığı durumu okur. Dört aracın ortak paydası, hepsinin aynı temel soruyu — programın bir andaki iç durumu neydi — farklı koşullar altında yanıtlamasıdır.

Kesme Noktası ve Gözetleme Noktası

Aşağıdaki model bir değişkene sırayla üç adımda üç değer yazan bir işlevi tanımlıyor. Bu işlev iki farklı gözlem biçimiyle çalıştırılabiliyor: biri yalnız seçilmiş adımlarda durup gözlemciyi çağırıyor, diğeri her adımda duruyor.

// adim.h — uc adimlik yazma dizisi ve iki gozlem bicimi
#ifndef ADIM_H
#define ADIM_H

typedef void (*GozlemIslevi)(int adim, int deger);

/* Uc adimda bir degiskene sirayla 1, 2, 3 yazar. kesme_noktalari dizisinde
   olmayan bir adimda gozlemci hic cagrilmaz -- program o adimda durmadan
   ilerler. */
void kesme_noktasiyla_calistir(GozlemIslevi gozle, const int *kesme_noktalari, int sayisi);

/* Ayni uc adimlik yazma dizisi, ama gozlemci HER adimda cagrilir -- kesme
   noktasi listesi yok, cunku her yazma bir durma nedenidir. */
void gozetleme_noktasiyla_calistir(GozlemIslevi gozle);

/* Adim listesi yerine deger uzerinde bir kosul tanimlanir: gozlemci yalnizca
   deger esik'e ulastiginda cagrilir. Hangi adimda durulacagi onceden bilinmez,
   yalnizca hangi kosulda durulacagi bilinir. */
void kosullu_kesme_noktasiyla_calistir(GozlemIslevi gozle, int esik);

#endif
// adim.c — kesme noktasi secici durur, gozetleme noktasi her yazmada durur
#include "adim.h"

static int kesme_noktasinda_mi(int adim, const int *noktalar, int sayisi) {
    for (int i = 0; i < sayisi; i++) {
        if (noktalar[i] == adim) return 1;
    }
    return 0;
}

void kesme_noktasiyla_calistir(GozlemIslevi gozle, const int *kesme_noktalari, int sayisi) {
    for (int adim = 0; adim < 3; adim++) {
        int deger = adim + 1;   /* adim 0 -> 1, adim 1 -> 2, adim 2 -> 3 */
        if (kesme_noktasinda_mi(adim, kesme_noktalari, sayisi)) {
            gozle(adim, deger);
        }
    }
}

void gozetleme_noktasiyla_calistir(GozlemIslevi gozle) {
    for (int adim = 0; adim < 3; adim++) {
        int deger = adim + 1;
        gozle(adim, deger);   /* her yazmada duruyor, secim yok */
    }
}

void kosullu_kesme_noktasiyla_calistir(GozlemIslevi gozle, int esik) {
    for (int adim = 0; adim < 3; adim++) {
        int deger = adim + 1;
        if (deger >= esik) {   /* adim numarasina degil, degerin kendisine bakiyor */
            gozle(adim, deger);
        }
    }
}

PA8. Adımlama burada tek bir döngünün ilerleyişi olarak modelleniyor: her döngü turu bir adımı, gözlemcinin çağrılıp çağrılmaması ise o adımda durulup durulmadığını temsil ediyor. Gerçek bir hata ayıklayıcı adım adım ilerlemeyi kullanıcının komutuyla tetikler; burada zamanlamayı görünür kılmak için tek bir çalışma akışında yürütülüyor.

PA9. Koşul burada tek bir tamsayı karşılaştırmasına (deger >= esik) indirgeniyor; gerçek bir hata ayıklayıcıda koşul rastgele bir boole ifadesi olabilir, ama mekanizma aynıdır — her adımda koşul değerlendirilir, yalnız sağlandığında durulur.

Kesme noktası ile koşullu kesme noktası arasındaki fark, neyin önceden bilindiğidir. Sıradan bir kesme noktası belirli adım numaralarında durur — hangi adımların ilginç olduğu önceden bilinmelidir. Koşullu bir kesme noktası ise adım numarasına hiç bakmaz; yalnız değerin kendisine bakar ve koşul her sağlandığında durur, kaçıncı adımda olduğuna bakılmaksızın. Bu, “üçüncü adımda dur” ile “değer 2’ye ulaştığında dur” arasındaki farktır — birincisi konuma, ikincisi duruma bakar. Konum önceden bilinmediğinde ama durumun ne olması gerektiği biliniyorsa (örneğin bir sayacın belirli bir eşiği geçtiği an), koşullu kesme noktası sıradan bir kesme noktasının yapamayacağı bir şeyi yapar: adım sayısı yüzlerce ya da binlerce olsa bile, aranan durum yalnız bir kez gerçekleştiğinde durur.

// olcum.c — kesme noktasi 2'yi atliyor, gozetleme noktasi 2'yi goruyor
#include <stdio.h>
#include "adim.h"

static int gorulen_sayisi = 0;
static int gorulen_degerler[3];

static void kaydet(int adim, int deger) {
    (void)adim;
    gorulen_degerler[gorulen_sayisi] = deger;
    gorulen_sayisi++;
}

int main(void) {
    int noktalar[2] = { 0, 2 };   /* kesme noktalari 0. ve 2. adimda */

    kesme_noktasiyla_calistir(kaydet, noktalar, 2);
    printf("kesme noktasiyla gorulen sayisi: %d\n", gorulen_sayisi);
    printf("kesme noktasiyla ilk gorulen: %d\n", gorulen_degerler[0]);
    printf("kesme noktasiyla ikinci gorulen: %d\n", gorulen_degerler[1]);

    gorulen_sayisi = 0;
    gozetleme_noktasiyla_calistir(kaydet);
    printf("gozetleme noktasiyla gorulen sayisi: %d\n", gorulen_sayisi);
    printf("gozetleme noktasiyla ikinci gorulen: %d\n", gorulen_degerler[1]);

    gorulen_sayisi = 0;
    kosullu_kesme_noktasiyla_calistir(kaydet, 2);   /* deger 2'ye ulasinca dur */
    printf("kosullu kesme noktasiyla gorulen sayisi: %d\n", gorulen_sayisi);
    printf("kosullu kesme noktasiyla ilk gorulen: %d\n", gorulen_degerler[0]);
    printf("kosullu kesme noktasiyla ikinci gorulen: %d\n", gorulen_degerler[1]);
    return 0;
}
kesme noktasiyla gorulen sayisi: 2
kesme noktasiyla ilk gorulen: 1
kesme noktasiyla ikinci gorulen: 3
gozetleme noktasiyla gorulen sayisi: 3
gozetleme noktasiyla ikinci gorulen: 2
kosullu kesme noktasiyla gorulen sayisi: 2
kosullu kesme noktasiyla ilk gorulen: 2
kosullu kesme noktasiyla ikinci gorulen: 3

Kesme noktaları 0. ve 2. adımda kuruldu; program bu iki adımda durdu ve sırasıyla 1 ile 3 değerlerini gördü. Aradaki 1. adımda değişken 2 değerini taşıdı, ama orada kesme noktası olmadığı için gözlemci hiç çağrılmadı — 2 değeri hiç görülmedi. Aynı üç adımlık dizi gözetleme noktasıyla çalıştırıldığında gözlemci her adımda çağrıldı: üç değer de kaydedildi ve ikinci gözlem tam olarak kesme noktasının kaçırdığı 2 değeriydi.

Son üç satır koşullu kesme noktasının davranışını gösteriyor. Eşik 2 olarak verildiğinde, 0. adımdaki değer (1) eşiğin altında kaldığı için gözlemci orada hiç çağrılmadı; 1. ve 2. adımlardaki değerler (2 ve 3) eşiğe ulaştığı ya da geçtiği için ikisinde de durdu. Görülen sayı 2, ilk görülen 2, ikinci görülen 3 — sıradan kesme noktasının 0. ve 2. adımda durup 1 ile 3’ü görmesinden farklı bir küme. İki mekanizma da aynı üç adımlık diziyi tarıyor, ama biri konuma göre seçim yapıyor, diğeri duruma göre; bu yüzden aynı diziden farklı gözlem kümeleri çıkarıyorlar. Bu fark, hangi mekanizmanın seçileceğini önceden bilinen bilginin türü belirliyor: aranan adım biliniyorsa kesme noktası yeterli ve daha ucuzdur, aranan yalnız bir durum (bir değerin belirli bir eşiği geçmesi gibi) ise ve hangi adımda gerçekleşeceği bilinmiyorsa koşullu kesme noktası gerekir.

Gözetleme noktasıyla koşullu kesme noktası birbirine benzer görünse de aynı şey değildir. Gözetleme noktası her yazmada durur, seçim yapmaz — üç değerin üçünü de görmesinin nedeni budur. Koşullu kesme noktası ise yalnız koşul sağlandığında durur; eşik 2 yerine 4 verilseydi, hiçbir adımda durmazdı, çünkü dizideki en büyük değer 3’tür ve hiçbiri 4’e ulaşmaz. Gözetleme noktası “hiç kaçırma” garantisi verirken bunun bedelini her adımda durarak öder; koşullu kesme noktası bu garantiyi vermez — yalnız koşulu tanımlanan durumu kaçırmaz, koşulun dışında kalan her şeyi sıradan kesme noktası gibi atlar. Üçü arasındaki seçim, aranan şeyin bir konum mu, her değişim mi, yoksa belirli bir durum mu olduğuna bağlıdır.

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

Bir program beklenmedik şekilde sonlandığında, o andaki bellek durumunun bir dökümü sonradan incelenebilir — programı yeniden çalıştırmadan, çökme anındaki yığıt ve değişken değerlerine bakmak için. Bu döküm burada üretilmiyor: bir sürecin gerçekten çökertilmesi bu dersin kapsamı dışında, yalnız ne için kullanıldığı anlatılıyor. Kesme noktası ve gözetleme noktası çalışan bir süreci durdurup gözlemler; döküm ise durmuş bir sürecin geride bıraktığı durumu, süreç artık yokken okur — ikisi aynı görünürlük sorusuna iki farklı zamanda cevap verir.

Çekirdek dökümünün asıl değeri, gözlemcinin hazır olmadığı anlar içindir. Kesme noktası, gözetleme noktası ve koşullu kesme noktasının üçü de, hatayı arayan kişinin programı önceden bir hata ayıklayıcının altında başlatmasını ya da sonradan ona bağlanmasını gerektirir — programın ne zaman ve nerede duracağı bir ölçüde önceden tahmin edilmelidir. Üretimde çalışan, kimsenin izlemediği bir süreç beklenmedik bir anda çökerse, o anı yeniden yaratmak çoğu zaman mümkün olmaz; girdi, zamanlama ya da sistem durumu bir daha aynı şekilde tekrarlanmayabilir. İşletim sistemi böyle bir çökme anında, sürecin o anki bellek görüntüsünü — yığıt, öbek ve yazmaç değerlerini — bir dosyaya yazabilir. Bu dosya sonradan, süreç artık çalışmıyorken bir hata ayıklayıcıya verilir ve çökme anındaki değişken değerleri, çağrı zinciri ve durum, sanki program hâlâ o anda duruyormuş gibi incelenebilir — yalnız artık ileri adımlanamaz, çünkü döküm tek bir donmuş kareden ibarettir, çalışan bir süreç değildir.

Bu, dökümü kesme ve gözetleme noktalarından ayıran temel farkı ortaya koyuyor: kesme ve gözetleme noktaları önceden kurulmuş bir gözlem gerektirir, döküm ise gözlem hiç kurulmamış olsa bile geriye bir kayıt bırakır. Bir hata yeniden üretilebiliyorsa ve nerede durulacağı biliniyorsa kesme ya da gözetleme noktası tercih edilir, çünkü canlı bir süreç üzerinde adım adım ilerlemeye izin verir. Hata yalnız bir kez, öngörülemeyen bir anda ve gözlemci hazır değilken oluyorsa, geriye kalan tek araç dökümdür — çökme anının kendisini yeniden yaratmaya gerek kalmadan, o anın donmuş bir kopyasını sonradan okumaktır.

Bu modelin kendi sınırı da var: burada gözlemlenen değer, kaynak kodun kendisinde yazan değerle birebir aynı kalacak şekilde kuruldu; eniyilenmiş bir programda hata ayıklayıcının gördüğü değer kaynaktakiyle aynı olmayabilir — bir değişken yazmaçta tutulmuş ya da tamamen silinmiş olabilir.

Kesme noktası ile gözetleme noktası arasındaki fark, bu dersin modelinde bir dizi uzunluğuyla sınırlı görünse de, gerçek bir programda çok daha büyük bir aralıkta gizli kalabilir. Üç adımlık bir dizide aradaki tek değer gözden kaçtığında bunu fark etmek görece kolaydır; yüzlerce adımdan geçen bir döngüde, kesme noktası yalnızca girişte ve çıkışta kurulmuşsa, aradaki hatalı bir ara değer koşum boyunca hiç görülmeden kalabilir. Gözetleme noktasının her yazmada durması bu riski ortadan kaldırır, ama bunun bir bedeli vardır: bir değişken sık sık yazılıyorsa, program her yazmada durduğu için çok daha yavaş ilerler. Kesme noktası ile gözetleme noktası arasındaki seçim bu yüzden yalnız “hangisi daha çok görür” sorusu değil, “hangi adımı görmek gerektiği önceden ne kadar biliniyor” sorusudur — hiç bilinmiyorsa gözetleme noktasının yavaşlığı, kesme noktasının kaçırma riskine tercih edilir.

Üç durma stratejisi tek bir hata avında art arda da kullanılabilir. Bir toplam değişkeninin beklenmedik bir noktada negatife düştüğü varsayılsın. İlk adım genellikle gözetleme noktasıdır: değişkenin her yazıldığı an durulur, birkaç koşumdan sonra hangi aralıkta işaretin döndüğü görülür. Aralık daraldıkça gözetleme noktasının bedeli — her yazmada durmanın getirdiği yavaşlık — artık gerekli değildir; o aralığın başına sabit bir kesme noktası konur ve program yalnız orada durdurulup adım adım ilerlenir. Değer negatife dönüşü tek bir koşulda değil, girdiye bağlı birkaç farklı koşulda tekrarlıyorsa, kesme noktası yerini koşullu kesme noktasına bırakır: durma artık “şu satıra gelince” değil “toplam sıfırın altına düşünce” olarak tanımlanır, hangi girdinin bunu tetiklediğinden bağımsız olarak. Bu üç aracın sırayla kullanılması rastgele değildir — her adım bir öncekinin bulduğu bilgiyi (yaklaşık konum, sonra kesin konum, sonra durumun kendisi) daraltarak bir sonrakine devreder. Hata üretimde ve yalnız bir kez, kimsenin izlemediği bir anda ortaya çıkmışsa bu sıralı avlanma hiç başlamaz; elde kalan tek şey çekirdek dökümüdür, ve o zaman soru “nerede durulmalı” değil “geride ne kayıt kaldı”dır.

Özet

  • Kesme noktası, yalnız seçilmiş adımlarda durur; iki kesme noktası arasında geçen bir ara değer hiç gözlemlenmez — bu dersin ölçtüğü sınıf gözlenmeyen.
  • Gözetleme noktası her yazmada durur; kesme noktasının kaçırdığı ara değeri de görür, bu bir karşıtlık modelidir.
  • Koşullu kesme noktası adım numarasına değil değerin kendi durumuna bakar; konum bilinmese de aranan durum tanımlanabiliyorsa kesme noktasının yapamadığını yapar.
  • Çekirdek dökümü çalışan bir süreci durdurmaz, sonlanmış bir sürecin durumunu sonradan okur; önceden bir gözlem kurulmamış olsa bile geriye bir kayıt bırakır.
  • Bu modelde gözlemlenen değer kaynaktaki değerle eşleşecek şekilde kuruldu; eniyilenmiş bir programda bu eşleşme garanti değildir.
  • Hangi durma stratejisinin seçileceği aranan şeyin bir konum mu, her değişim mi, yoksa belirli bir durum mu olduğuna bağlıdır; gerçek bir avda üçü art arda da kullanılır — gözetleme noktasıyla kabaca daraltılan aralık kesme noktasıyla kesinleştirilir, koşula bağlı tekrar eden bir durum ise koşullu kesme noktasıyla yakalanır.

Kesme noktası ile gözetleme noktasının ikisi de programı durdurarak çalışır: program duruncaya kadar normal hızında ilerler, durduğu anda bellek durumu dondurulmuş gibi okunabilir hale gelir. Bu, iki yöntemi çekirdek dökümünden ayıran ikinci bir eksendir — döküm sürecin kendisi artık çalışmıyorken alınan tek bir kareyken, kesme ya da gözetleme noktası çalışan sürecin üzerinde birden çok kareyi art arda gösterebilir. Bir hatayı ararken hangisinin daha uygun olduğu, hatanın tek bir anda mı yoksa bir dizi adım boyunca mı ortaya çıktığına bağlıdır: tek bir çökme anı dökümle, adımlar arasında biriken bir sapma ise durma noktalarıyla incelenir.

Sonraki Adım

Bir değişkenin kaynakta yazdığı değerle çalışma zamanında taşıdığı değer her zaman aynı kalmaz — bir sonraki ders bu farkın nereden geldiğini ele alıyor.

İ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