GDB ile Hata Ay─▒klama (Debug ­čÉŤ)┬Â

┼×imdi, GDB ile kendi program─▒m─▒z debug etmeye ├žal─▒┼čal─▒m, olacak m─▒ bilmiyorum.

├çekirdek, Hardware Thread, hart Say─▒s─▒ Ayarlama┬Â

├ľncesinde bir konuya de─činmek istiyorum. make qemu dedi─čimiz zaman asl─▒nda ├žok ├žekirdekli bir sanal bilgisayar olu┼čuyor, elbette RISC-V. Default olarak QEMU -smp 3 diye bir arg├╝manla ├ža─čr─▒l─▒yor, 3 ├žekirdekli bir makine say─▒ biraz ilgin├ž. QEMU ├žal─▒┼č─▒rken top -c dersek QEMUÔÇÖnun %300 CPU kulland─▒─č─▒n─▒ g├Ârd├╝m. Yani muhtemelen her bir sanal RISC-V ├žekirde─či i├žin bilgisayar─▒mdaki bir coreÔÇÖu harc─▒yor. Normalde QEMU %100 CPU kullanan bir uygulama de─čil. Fakat emule etti─či sistem hi├ž uyumuyorsa mesela infinite loopta d├Ân├╝yorsa her core b├Âyle olabilir belki. Her ne kadar bu kadar CPU kullan─▒m─▒ garip gelse de ileriye d├Ân├╝k bu konuyu b─▒rak─▒yorum.

xv6 kernel is booting

hart 2 starting
hart 1 starting
init: starting sh

Buradaki hart x core say─▒s─▒ ile ilgili. Koda bir bakal─▒m [1]:

kernel/main.c┬Â
13if(cpuid() == 0){
14  consoleinit();
15  printfinit();
16  printf("\n");
17  printf("xv6 kernel is booting\n");
18  printf("\n");
19  kinit();         // physical page allocator
20  kvminit();       // create kernel page table
21  kvminithart();   // turn on paging
22  procinit();      // process table
23  trapinit();      // trap vectors
24  trapinithart();  // install kernel trap vector
25  plicinit();      // set up interrupt controller
26  plicinithart();  // ask PLIC for device interrupts
27  binit();         // buffer cache
28  iinit();         // inode table
29  fileinit();      // file table
30  virtio_disk_init(); // emulated hard disk
31  userinit();      // first user process
32  __sync_synchronize();
33  started = 1;
34} else {
35  while(started == 0)
36    ;
37  __sync_synchronize();
38  printf("hart %d starting\n", cpuid());
39  kvminithart();    // turn on paging
40  trapinithart();   // install kernel trap vector
41  plicinithart();   // ask PLIC for device interrupts
42}

Kodu takip ederseniz cpuid() nin ilgili ├žekirde─čin hart yani hardware thread IDÔÇÖsi oldu─čunu g├Ârebiliyoruz [2]. RISC-V mimarisi detaylar─▒na ┼čimdilik bakm─▒yoruz ama hartÔÇÖlar─▒ birer core gibi d├╝┼č├╝nebiliriz diye anl─▒yorum [3]

Burada ID 0 olan─▒ niye bast─▒rmam─▒┼člar bilmiyorum, ├ž├╝nk├╝ 1 ve 2 g├Âr├╝nce 2 ├žekirdek d├╝┼č├╝nebiliriz asl─▒nda 3 ├žal─▒┼č─▒yor.

─░stersek ├žekirdek say─▒s─▒n─▒ de─či┼čtirebiliriz. Makefile i├žerisinde CPUS diye bir ├ževresel de─či┼čken, environment variable, okunuyor, default 3. Bunu ge├žersek core say─▒s─▒n─▒ de─či┼čtirebiliriz.

ay@dsklin:~/ws/xv6-riscv$ CPUS=1 make qemu

xv6 kernel is booting

init: starting sh
$ QEMU: Terminated

ay@dsklin:~/ws/xv6-riscv$ CPUS=8 make qemu

xv6 kernel is booting

hart 6 starting
hart 7 starting
hart 3 starting
hart 2 starting
hart 1 starting
hart 4 starting
hart 5 starting
init: starting sh

Tek core ya da 8 core a├žabiliriz. Benim sistemde her core %100 CPU kullan─▒yor, onu tekrar belirteyim. make CPUS=4 qemu diyebiliriz alternatif olarak, CPUS u ortaya alma ┼čeklinde.

GDB┬Â

┼×imdi gelelim esas konuya. QEMU ├╝zerinde ├žal─▒┼čt─▒rd─▒─č─▒ uygulamay─▒, sanal makinede ├žal─▒┼čan uygulamay─▒ sanki JTAG gibi do─črudan donan─▒ma ba─čl─▒ym─▒┼č─▒z gibi GDB ile debug etmeye imkan sa─čl─▒yor [4]:

QEMU supports working with gdb via gdbÔÇÖs remote-connection facility (the ÔÇťgdbstubÔÇŁ). This allows you to debug guest code in the same way that you might with a low-level debug facility like JTAG on real hardware. You can stop and start the virtual machine, examine state like registers and memory, and set breakpoints and watchpoints.

E─čer xv6ÔÇÖy─▒ make qemu-gdb ile ├žal─▒┼čt─▒r─▒rsak QEMU bu modda ├žal─▒┼č─▒yor.

ay@dsklin:~/ws/xv6-riscv$ make qemu-gdb
sed "s/:1234/:26000/" < .gdbinit.tmpl-riscv > .gdbinit
*** Now run 'gdb' in another window.
qemu-system-riscv64 ... -S -gdb tcp::26000

Burada bizler i├žin .gdbinit dosyas─▒ olu┼čuyor ve QEMU ├žal─▒┼č─▒rken S -gdb tcp::26000 parametresi ge├žiliyor. ┼×imdi ba┼čka bir pence a├ž─▒p ayn─▒ dizine gidip gdb diyoruz. gdb, buradaki .gdbinit dosyas─▒n─▒ okuyacak.

┼×├Âyle bir hata ald─▒k:

warning: File "/home/ay/ws/xv6-riscv/.gdbinit" auto-loading has been declined by
your `auto-load safe-path' set to "$debugdir:$datadir/auto-load".

├ľnce bunu d├╝zeltelim [5].

$HOME/.config/gdb/gdbinit i a├ž─▒p add-auto-load-safe-path /home/ay/ws/xv6-riscv/.gdbinit ekliyoruz. Bende bu dosya yoktu, yaratt─▒m.

Daha sonra gdb dedi─čimiz zaman bu sefer bu hata geliyor fakat

This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word".
.gdbinit:2: Error in sourced command file:
Undefined item: "riscv:rv64".

gibi bir hata ald─▒k. Ben gdb yazd─▒─č─▒m i├žin RISC-V destekleyen cross GDB ? de─čil, klasik gdb ├žal─▒┼čt─▒. riscv64-linux-gnu-gdb ├žal─▒┼čt─▒racakt─▒m ama b├Âyle bir dosya san─▒r─▒m yok [6]. gdb-multiarch ile yapal─▒m, bu sefer oldu. ┼×├Âyle bir uyar─▒ ald─▒k:

warning: No executable has been specified and target does not support
determining executable automatically.  Try using the "file" command.
0x0000000000001000 in ?? ()

┼×imdilik g├Âz ard─▒ ediyorum. gdb de c yani continue dedi─čimizde xv6 ├žal─▒┼čmaya ba┼čl─▒yor. Ctrl-C ile durdurabiliyoruz. Bunu durdurunca CPU kullan─▒m─▒ da 0ÔÇÖa d├╝┼č├╝yor, durdu─ču nokta bende schedular oldu. Daha sonra c ile devam edebiliriz.

loop.c Debug┬Â

┼×imdi bir ├Ânceki yaz─▒da yazd─▒─č─▒m─▒z loop.c kodunu debug etmeye ├žal─▒┼čal─▒m. Mesela Ctrl-d ile programdan ├ž─▒kabiliyorduk. Bizim kodun ├ž─▒kmas─▒ i├žin read() geri d├Ân├╝┼č de─čerinin 0 veya daha k├╝├ž├╝k olmas─▒ gerekiyor. Bakal─▒m hangi de─čeri d├Ân├╝yormu┼č.

make qemu-gdb ile bir pencerede ba┼člatal─▒m xv6ÔÇÖy─▒, di─čer pencerede gdb-multiarch ├žal─▒┼čt─▒ral─▒m. c diyelim ve xv6ÔÇÖy─▒ salal─▒m ├žal─▒┼čs─▒n.

xv6ÔÇÖda loop diyerek yaz─▒l─▒m─▒ ├žal─▒┼čt─▒r─▒yoruz. Bekledi─čimiz gibi ├žal─▒┼č─▒yor. ┼×imdi gdb de Ctrl-C diyelim ve targetÔÇÖ─▒ durdural─▒m. gdb de file user/_loop diyerek gdb ye ├žal─▒┼čan komutu tan─▒tal─▒m. Sonra list main diyerek kaynak kodunu listeleyelim:

(gdb) file user/_loop
Reading symbols from user/_loop...
(gdb) list main
5 static const char msg1[] = "read yapildi\n";
6 static const char msg2[] = "write yapildi\n";
7 static const char msg3[] = "read cikti!!!\n";
8
9 int main()
10 {
11   for (;;){
12     int n = read(0 , buf, sizeof(buf));
13     write(1, msg1, sizeof(msg1) - 1);
14     if (n <= 0) {
(gdb)

n nin de─čerini g├Ârmek i├žin mesela sat─▒r 13ÔÇÖe breakpoint koyal─▒m, b 13 diyerek.

(gdb) b 13
Breakpoint 1 at 0x56: file user/loop.c, line 13.

┼×imdi c diyerek program─▒m─▒z─▒ devam ettirelim. Mesela test yaz─▒p Enter diyelim. Program─▒m─▒z breakpointÔÇÖte duracakt─▒r. ┼×imdi p n yaz─▒p gdbÔÇÖde n nin de─čerine bakal─▒m. 5 mi┼č. Neden? ├ç├╝nk├╝ test + Enter 5 karakter. c deyip devam edelim. ┼×imdi loop program─▒na Ctrl-d verelim. Yine breakpointte durduk. p n diyoruz ve 0 de─čerini g├Ârd├╝k. Demek ki read() bize 0 d├Ân├╝yormu┼č bu durumda. c dersek program─▒m─▒z─▒n ├ž─▒k─▒─č─▒n─▒ g├Ârece─čiz. QEMUÔÇÖdan yine Ctrl-a x ,le ├ž─▒kabiliriz. gdb den de quit deyip ├ž─▒kabiliriz. B├Âylece program─▒m─▒z debug edebildik, ne ho┼č

Bu altyap─▒ kernelin kendsinin de debug i┼člemi i├žin kullan─▒labilir. Onun i├žin file kernel/kernel dememiz gerekecek gdbÔÇÖye. Demesek olur mu tam emin de─čilim, ileride bakar─▒z.