Standart C Programlama Dili


B.2.1. GDB Kullanım Örnekleri

#include <stdio.h>

void bol (int a, int b)
{
  printf("%d / %d = %d\n", a, b, a/b);
} /* bol */

int main (void)
{
  int x, y;
  for (x=170, y=6; x>-170; x-=30, y-=2)
    bol(x, y);
} /* main */

Yukarıda değişik tamsayıları birbirine bölüp sonucu yazan basit bir program verilmiştir. Ancak program çalıştırıldığında hata alıp sonlanmaktadır. Problemi gdb hata düzelticisi ile araştırmak için iki seçeneğimiz bulunmaktadır: (1) Programı canlı olarak gdb altında çalıştırmak. (2) Programı çalıştırıp, hatayı alarak core dump oluşturmak ve program öldükten oluşan bu dosyayı gdb ile incelemek.

Canlı İnceleme

İlk yöntem ile ilgili örnek bir oturumun ekran dökümü aşağıda verilmiştir:

$ gcc -std=c17 -pedantic -Wall -g prog.c  ## Programı "-g" ile derle.
$ gdb -silent ./a.out  ## Programı "gdb" altında yükle.
Reading symbols from ./a.out...
(gdb) r  ## Programı çalıştır.
Starting program: …/a.out
170 / 6 = 28
140 / 4 = 35
110 / 2 = 55

Program received signal SIGFPE, Arithmetic exception.
0x000055555555515f in bol (a=80, b=0) at prog.c:5
5	  printf("%d / %d = %d\n", a, b, a/b);
(gdb) bt
#0  0x000055555555515f in bol (a=80, b=0) at prog.c:5
#1  0x00005555555551ab in main () at prog.c:12
(gdb) p main::x
$1 = 80
(gdb) p main::y
$2 = 0
(gdb) c
Continuing.

Program terminated with signal SIGFPE, Arithmetic exception.
The program no longer exists.
(gdb) q

Yukarıdaki ekran dökümünün ilk satırında, programın -g seçeneği ile derlendiğine dikkat edin. Eğer gdb kullanarak herhangi bir programda hata düzeltmesi yapılacaksa, derleme sonucunda oluşacak yürütülebilir dosyaya (örneğin a.out) uygun bilgileri dahil etmek ve incelemeyi mümkün kılmak için program derleme komutunda mutlaka -g seçeneği kullanılmalıdır. İkinci satırda, derleme sonucunda bulunduğumuz dizinde oluşan a.out dosyasını gdb komutuna argüman olarak veriyoruz. Bu şekilde program belleğe yüklenir. Çalıştırmak için dördüncü satırda run (veya r) komutunu giriyoruz. Program (kısmen) çalışarak bir miktar çıktı üretiyor; ancak normal sonlanamadan, hata alıp duruyor. Alınan hata mesajı 10. satırda gdb tarafından görüntülenmektedir. Hatanın alındığı bellek adresi programın 5. kaynak satırına denk gelmektedir ve bu satır bol fonksiyonunun içindedir. Bu esnada bol fonksiyonunun parametrelerinin sırası ile a=80 ve b=0 olduğunu görüyoruz. gdb program kaynak kodu bilgilerine de sahip olduğu için, bu satırın kaynak kodu da ekrana dökülmektedir.

Hata alındığı sırada, programda nerede olduğumuzu daha detaylı görebilmek için (çok daha büyük ve karmaşık programlarda bu daha da önemli hale gelir), geçerli fonksiyon yığınını geriye doğru döken backtrace (veya bt) komutunu giriyoruz ve bol fonksiyonunun main fonksiyonu tarafından programın 12. kaynak kodu satırında çağrıldığını görüyoruz. Bu esnada main fonksiyonunda tanımlı olan x ve y değişkenlerini de kontrol etmek için print (veya p) komutlarını giriyoruz. Aktif bir fonksiyona ait değişkenleri kullanmak için fonksiyon_adı::değişken_adı sözdizimini kullanırız. Eğer sadece değişken adı kullanılmış olsaydı, sadece içinde bulunulan fonsiyon (geçerli çerçeve) olan bol tarafından bilinen değişkenler (a ve b) hakkında bilgi alabilecektik.

Ölüm Sonrası İnceleme

İkinci yöntem için, programı komut satırında normal bir şekilde çalıştırıp, hatayı alırız. Yürütme sırasında alınan hataların detaylı bir incelemesi yapılabilsin diye, sistem hata alındığı anki programın bellek durumunu bir dosyaya döker. Buna core dump (çekirdek dökümü) adı verilir. Ancak, bu tip dosyaların zamanla sistemde birikmesi disk alanını doldurabileceği ve genelde sıradan kullanıcıların işine yaramayacağı düşünüldüğünden, birçok sistemde (örneğin, güncel Ubuntu versiyonlarında) bu özellik kapalı tutulmaktadır. Core dump oluşumunu mevcut oturum için, yani geçici olarak etkin hale getirmek için, program çalıştırılmadan önce, ulimit -c unlimited işletim sistemi komutu girilmelidir. (Bazı sistemlerde, ayrıca kernel.core_pattern isimli işletim sistemi çekirdek parametresinin sysctl komutu ile değiştirilmesi gerekebilir.) Aşağıda örnek bir oturumun ekran dökümü verilmiştir:

$ gcc -std=c17 -pedantic -Wall -g prog.c  ## Programı "-g" ile derle.
$ ulimit -c unlimited  ## Bu oturum için "core" dosyasına izin ver.
$ rm core  ## Daha önceden oluşmuş bir "core" dosyası varsa, sil.
rm: cannot remove 'core': No such file or directory
$ ./a.out  ## Programı çalıştır.
170 / 6 = 28
140 / 4 = 35
110 / 2 = 55
Floating point exception (core dumped)
$ ls -sh core  ## "core" dosyası oluştu mu?
412K core
$ gdb -silent ./a.out core  ## Programı ve core dosyasını yükle.
Reading symbols from ./a.out...
[New LWP 31322]
Core was generated by `./a.out'.
Program terminated with signal SIGFPE, Arithmetic exception.
#0  0x00005565d614215f in bol (a=80, b=0) at prog.c:5
5	  printf("%d / %d = %d\n", a, b, a/b);
(gdb) bt
#0  0x00005565d614215f in bol (a=80, b=0) at prog.c:5
#1  0x00005565d61421ab in main () at prog.c:12
(gdb) f 1
#1  0x00005565d61421ab in main () at prog.c:12
12	    bol(x, y);
(gdb) p x
$1 = 80
(gdb) p y
$2 = 0
(gdb) q

Beşinci satırda görüldüğü gibi, a.out programını normal bir şekilde çalıştırıp, hatayı ve core dosyasını oluşturuyoruz. Program kısmen çıktı üretip hatalı bir şekilde sonlanıyor. Onuncu satırda core dosyasının oluştuğunu doğruluyoruz. Onikinci satırda, ilk argüman olarak yine programın yürütülebilir dosya adını, ikinci argüman olarak ise core dump dosya adını vererek gdb komutunu çalıştırıyoruz. Bu sefer programı gdb üzerinde tekrar çalıştırmayıp, doğrudan durum çözümlemesine geçiyoruz. Benzer komutlarla (backtrace, frame 1, print) çalışan fonksiyonlara ve değişken değerlerine bakıp, doğrudan quit (veya q) komutu ile gdb oturumunu sonlandırıyoruz.