Hata ayıklamada kullandığımız program debugger olarak adlandırılır.FreeBSD ile beraber gelen debugger gdb(GNU debugger) dir. Bir programın hatalarını ayıklamaya başlamak için:
% gdb programismi
Eğer Emacs altında bu işlemi yapmak istiyorsanız :
M-x gdb RET programismi RET
Hata ayıklama programı (debugger) kullanmanız programınızı daha kontrollü bir ortamda çalıştırabilmenizi sağlar.Tipik olarak programınızı adım adım işletebilir , değişkenlerin içeriğini görebilir değiştirebilir, programınızın belli bir yere kadar çalışıp sonra durmasını sağlayabilirsiniz.Programınızı çalışan bir programa ekleyebilirsiniz.Ayrıca core dosyalarını inceleyerek programınızın neden göçtüğünü anlamanız da mümkün.Hatta küçük bir hile ile çekirdeği (kernel) bile inceleyebilirsiniz.
gdb güzel on-line yardımının yanında bilgi sayfaları ile de gerekli dökümantasyonu sağlıyor.Bu bölümde bazı basit komutlar üzerinde duracağız.
Son olarak eğer yazı tabanlı komut satırı içermesi hoşunuza gitmedi ise , grafik tabanlı xxgdb yi port ağacından kullanabilirsiniz.
(http://www.FreeBSD.org/ports/devel.html)
Bu bölüm gdb programının kullanımına ilişkindir çekirdek hata ayıklama (kernel debug) gibi özel konulara girilmeyecek.
Öncelikle programın hatalarını ayıklayabilmek için (debug) programı -g seçeneği ile derlemiş olmanız gerekmektedir.Aksi taktirde programınız çalışacaktır fakat programınızın içinde bulunan fonksiyonlar hata ayıklama programı (debugger) tarafından tanınmayacaktır.
... (no debugging symbols found) ...
şeklinde bir uyarı alabilirsiniz.
Gdb komut satırında 'break main' yazdığınızda bu main çağrıldığında dur manasına gelir. 'run' yazdığınızda programınız çalışmaya başlar ama siz main'e kesme(break) koyduğunuz için programın başlangıcında durur.(Main fonksiyonunun nerede çağrıldığını merak ediyorsanız artık öğrenmişsinizdir.)
Artık programda adım adım ilerlemek için 'n' harfini kullanabilirsiniz.Eğer bir fonksiyon çağrısı varsa içine girmek için 's' tuşunu kullanabilirsiniz.Eğer fonksiyonun içinde iseniz geri dönmek için 'f' tuşunu kullanabilirsiniz. Aşağı yukarı tuşları ile fonksiyon çağrımları içerisinde (caller) gezinebilirsiniz.
Burada basit bir gdb kullanım örneği var. Hususi olarak hatalı yazılmış programımız.
#include <stdio.h>
int bazz(int anint);
main() {
	int i;
	printf("This is my program\n");
	bazz(i);
	return 0;
}
int bazz(int anint) {
	printf("You gave me %d\n", anint);
	return anint;
}İstiyoruz ki programımız i değişkenini 5 yapsın ve bazz fonksiyonuna göndersin. Derlediğimiz zaman:
% cc -g -o temp temp.c % ./temp This is my program anint = 4231
Bu bizim beklemediğimiz bir sonuç peki burada ne oldu ? Bunu anlamak için gdb ile hatamızın nerede olduğunu bulalım :
% gdb temp GDB is free software and you are welcome to distribute copies of it under certain conditions; type "show copying" to see the conditions. There is absolutely no warranty for GDB; type "show warranty" for details. GDB 4.13 (i386-unknown-freebsd), Copyright 1994 Free Software Foundation, Inc. (gdb) break main Skip the set-up programlisting Breakpoint 1 at 0x160f: file temp.c, line 9. gdb puts breakpoint atmain()(gdb) run Run as far asmain()Starting program: /home/james/tmp/temp Program starts running Breakpoint 1, main () at temp.c:9 gdb stops atmain()(gdb) n Go to next line This is my program Program prints out (gdb) s step intobazz()bazz (anint=4231) at temp.c:17 gdb displays stack frame (gdb)
Hang on a minute! How did anint get to be
	4231? Did we not we set it to be
	5 in main()? Let's
	move up to main() and have a look.
(gdb) up					Move up call stack
#1  0x1625 in main () at temp.c:11		gdb displays stack frame
(gdb) p i					Show us the value of i
$1 = 4231					gdb displays 4231(gdb) run//burada main() çalışrırılır.
Breakpoint 1, main () at temp.c:9 gdb stops at main()
//ilk durma noktasında main fonksiyonunun içinde durur.
(gdb) n//Sonraki satıra geçer
This is my program//Program ekrana çıktı verir
(gdb) s
//bazz() fonksiyonuna girer
bazz (anint=4231) at temp.c:17
//giriş işlemi
(gdb)
Burada gördüğünüz gibi bir yanlışlık var niçin program bazz()'ı 4321 ile çağırıyor.
yukarı çıkıp i nin değerini görmek için :
(gdb) up #1 0x1625 in main () at temp.c:11 (gdb) p i $1 = 4231 gdb displays 4231
Baktığımızda i değişkeninin değerinin 5 olmadığını görüyoruz.Demek ki i değişkenine başta değer atamayı unutmuşuz.
Programın düzeltilmiş hali:
. . . main() { int i; i = 5; printf("This is my program\n"); . . .Programızda i değişkenine değer atamazsak hangi değerin geleceği belli olmaz.Bu örnekte 4231 kullanıldı.
Not: gdb her fonksiyon çağrısında ve çıkışında yığın düzenini gösterir, yukarı aşağı tuşları ile bu yığında dolaşabiliriz.Bu fonksiyonun ismini ve gönderilen argümanları gösterir böylece hangi fonksiyonun nasıl çalışdığını anlayabiliriz.Böylece parametreleri doğru gönderilip gönderilmediğini anlamak daha kolay olacaktır.(Yığın fonksiyonların hangi argümanlarla çağırışıp ne döndüğünü tutan bir hafıza birimidir call stack olarak da adlandırılır.)
Core programın göçtüğündeki durumlarını tutan basit bir dosyadır.Eskiden programcılar programın onaltılık tabanda dökümümünü alıp makinanın kod referansına göre hata tesbit etmeye çalışıyorlardı. Şimdilerde hayat biraz daha kolaylaştı.4.4 BSD ve sonrası sistemlerde core dosyası sadece core isminde değil programadı.core şeklinde ifade ediliyor ki hangi programın core dosyası olduğu belli olsun.
Core dosyasını gdb altında açmak için aşağıdaki gibi komut göndermeniz yeterli olacaktır.
(gdb)
core programadı.core
eğer core dosyası ile aynı dizinde değilseniz öncelikle dir komutu ile dizininizi değiştirin.
% gdb a.out GDB is free software and you are welcome to distribute
        copies of it under certain conditions;type "show copying" to see the
        conditions. There is absolutely no warranty for GDB;type "show
        warranty" for details. GDB 4.13 (i386-unknown-freebsd), Copyright 1994
        Free Software Foundation, Inc. (gdb) core a.out.core Core was generated by ‘a.out’. Program terminated with
        signal 11, Segmentation fault. Cannot access memory at address
        0x7020796d. #0 0x164a in bazz (anint=0x5) at temp.c:17(gdb)
Bu durumda programımız a.out ise core dosyası a.out.core olur. Yukarıdan anlaşılıyor ki programımız bazz fonksiyonunda uygun olmayan bir adrese ulaşmaya çalışmış.
Bazen fonksiyonun nasıl çağrıldığını görmek faydalı olabilir,belki problem çok daha önceden kaynaklanıyor olabilir .
btkomutu ile fonksiyon çağrıları içerisinde geriye doğru gidebilirsiniz.
(gdb) bt #0 0x164a in bazz (anint=0x5) at temp.c:17 #1 0xefbfd888 in end () #2 0x162c in main () at temp.c:11 (gdb)
program göçmeden önce end() fonkiyonu çağırılmış, bazz() fonksiyonu main() altından çağırılmış.
Gdb nin güzel özelliklerinden bir tanesi hatasını ayıkladığınız programı çalışmakta olan programa bağlayabilmesidir.Elbette bunun için gerekli izninizin olduğunu varsayıyoruz.Ortak bir problem olarak Eğer bir program fork() fonksiyonu ile ikiye ayrılıyorsa çocuğu (child) izlemek istediğimiz zaman hata ayıklama programı sadece ebeveyni (parent) izlemeye izin verir. Bunu çözmenin yolu başka bir gdb açtıktan sonra pid yerine çocuk (child) sürecinin pid numarasını girmektir. Bu numarayı ps komutu ile elde edebilirsiniz.
(gdb) attach pid
Sonra normal şekilde hatalarını ayıklayabilirsiniz.
"Tamam çok güzel ama ben bunları yapana kadar çocuk(child) kim bilir hangi dağları aşmış olur ?" diyorsanız korkmayın bunun da çözümü var :
. . . if ((pid = fork()) <0) /*Hep kontrol edilir
        */ error(); error();else if (pid == 0) { /* child */ int PauseMode = 1; while (PauseMode) sleep(10);/* biri attach yapana kadar devam eder
        */ ... } else { /* parent */...
Sonra cocuk sürece (child'da) devam edecekseniz PauseMode değişkenini 0 yapın yeterli.