程式扎記: [ Nachos 4.0 ] Debugging Nachos

標籤

2013年3月14日 星期四

[ Nachos 4.0 ] Debugging Nachos

來源自 這裡 
Preface: 
基本上有兩種方法來 debug Nachos: external & internal. external debugger 透過外部載入 Nachos 的程式, 並使用 interactive 方法與使用者互動, 你可以一行行 debug 程式並檢查相關變數的設定, 常見的有 GDB. internal debugger 就是在你的程式加入 debug message, 透過這些訊息來判斷問題出在哪裡, 最常用也最沒有效率, 因為你無法一步步除錯, 且無法知道執行當下的變數(除非你把他們都印出來 orz)與執行環境. 

Internal Debugging in Nachos: 
在 Nachos 的原代碼中你會發現一些代碼使用 DEBUG 巨集來列印訊息, 範例如下: 
  1. DEBUG(dbgSys, "Received Exception " << which << " type: " << type << "\n");  
這些訊息在正常使用參數 -x 來執行 Nachos 時並不會顯示在 Console, 但是你可以使用參數 -d  指示需要顯示的 debug 訊息的種類, 這些種類定義在 code/lib/debug.h : 
  1. const char dbgAll = '+';        // turn on all debug messages  
  2. const char dbgThread = 't';     // threads  
  3. const char dbgSynch = 's';      // locks, semaphores, condition vars  
  4. const char dbgInt = 'i';        // interrupt emulation  
  5. const char dbgMach = 'm';       // machine emulation  
  6. const char dbgDisk = 'd';       // disk emulation  
  7. const char dbgFile = 'f';       // file system  
  8. const char dbgAddr = 'a';       // address spaces  
  9. const char dbgNet = 'n';        // network emulation  
  10. const char dbgSys = 'u';        // systemcall  
  11. const char dbJohn = 'j';        // Debug by John  
上面我有自定義一個類型 "dbJohn", 如果你執行時希望看到類型 "dbJohn" 的訊息時, 可以在執行 nachos 下參數 "-d j" 來告訴 nachos 打開 debug 類型為 "dbJohn" 的訊息. 底下為一個執行範例, nachos 會 load 進執行檔 "project1": 
> nachos -d j -x project1
Received Exception 1 type: 52
Add 15 + 10
Add returning with 25
Received Exception 1 type: 60
...

底下為 internal debugging 相關的內容: 
- The Trace Facility 
Each debugging message printed by the trace facility has a type. You can control which types of messages are actually printed using command line arguments to Nachos. The -d flag controls this. For example: 
> nachos -d td ...

will print debugging messages related to threads ("t") and the disk ("d") emulation. If you do not use -d, the trace facility will be silent. Using -d + will cause trace messages of all types to be printed. Remember, however, that one of the nice features of this facility is that you can focus on only those messages that you are interested in. Having to wade through reams of irrelevant trace output is almost as bad as not having any trace output at all. 

- Single Stepping 
The single-step facility is enabled by the -s command-line argument. It causes the machine simulation to single-step through instruction execution. After each instruction is executed, the machine will print the values of all of its registers, as well as the current simulated time. It will also print a prompt. Your options are to execute one more instruction, to execute instructions until a specified time is reached, or to exit single-step mode and run without further interruption. Most of the time, you will find that you do not need this facility. However, those interested in the details of the machine emulation may wish to play with it. 

- Assertions 
The assertion facility allows you to state explicitly things that you believe will be true at a certain point in your program's execution. The assertion is tested and if it is found to be false, the program is terminated and a message is printed indicating which assertion failed. For example, the following piece of code can be found the Nachos main function: 
  1. myId = kernel->procTable->GetNewProcId(-1);  
  2.         ASSERT(myId >= 0);  
Here, the assertion is that a valid process identifier was, in fact, returned by the call to GetNewProcId. Note that ASSERT should be used to describe conditions that you expect to be true, and for which no graceful recovery exists for your program if they are false. 

External Debugger: 
在 external debugger 的部分, 接下來要介紹工具 GDB 並透過範例說明如何使用該工具進行 Nachos 的除錯. 

- Using GDB with Nachos 
執行 gdb -h 後你可以知道 gdb 更多的使用訊息, 而兩個下命令的語法如下: 
gdb [options] [executable-file [core-file or process-id]]
gdb [options] --args executable-file [inferior-arguments ...]

上面的 executable-file 就是你要進行偵錯的程式, 這邊就是 nachos 執行檔. 執行上面另一命令後會進行 interactive mode. 首先執行命令如下: 
> gdb nachos
GNU gdb (GDB) 7.4.1-debian
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
For bug reporting instructions, please see:
...
Reading symbols from /nfs/master/00/p00922002/OSC/nachos/NachOS-4.0/code/test/nachos...done.
(gdb)
 <等待輸入命令>

接著假設你要在 Nachos 上執行 halt 的 user program, 且你想要知道執行後 register 上面的值, 你可以如下面給定命令: 
(gdb) break AddrSpace::InitRegisters # 設定斷點
Breakpoint 1 at 0x8057b83: file ../userprog/addrspace.cc, line 215.
(gdb) run -x ../test/halt # 執行並給定 nachos 參數 "-x ../test/halt"
Starting program: /nfs/master/00/p00922002/OSC/nachos/NachOS-4.0/code/test/nachos -x ../test/halt

Breakpoint 1, AddrSpace::InitRegisters (this=0x8070f30) at ../userprog/addrspace.cc:215
215 Machine *machine = kernel->machine;

(gdb) <等待輸入命令>

透過 break 命令可以設定 Nachos 執行時設定斷點在某個特殊的行數或是函數, 更多的用法可以參考 這裡; 在這裡我們設定程式執行到 AddrSpace::InitRegisters (初始化 Registers) 時中斷. 接著run 命令告訴 gdb 開始跑 Nachos, 任何跟在 run 命令後面的參數都會被 pass 到要 debug 的程式. 

在這邊當 Nachos 執行到 AddrSpace::InitRegisters 時會中斷, 並顯示訊息說明該中斷點在 ../userprog/addrspace.cc:215. 接著停在 Console 等待下一個命令的輸入. 這個時候你可以檢視目前程式使用變數的值與 call stack 的狀況; 除此之外你可以從當前中斷點一步步執行程式 (透過命令 step/next) 或是繼續執行程式 (透過命令 continue); 甚至你可以動態更改程式變數的值或檢視當前執行位置的代碼 (list). 

接下來底下是執行的過程: 
(gdb) list 210, 230 # 列出當前執行位置第 210~230 行
  1. 210     //----------------------------------------------------------------------  
  2. 211  
  3. 212     void  
  4. 213     AddrSpace::InitRegisters()  
  5. 214     {  
  6. 215         Machine *machine = kernel->machine;  
  7. 216         int i;  
  8. 217  
  9. 218         for (i = 0; i < NumTotalRegs; i++)  
  10. 219             machine->WriteRegister(i, 0);  
  11. 220  
  12. 221         // Initial program counter -- must be location of "Start", which  
  13. 222         //  is assumed to be virtual address zero  
  14. 223         machine->WriteRegister(PCReg, 0);  
  15. 224  
  16. 225         // Need to also tell MIPS where next instruction is, because  
  17. 226         // of branch delay possibility  
  18. 227         // Since instructions occupy four bytes each, the next instruction  
  19. 228         // after start will be at virtual address four.  
  20. 229         machine->WriteRegister(NextPCReg, 4);  
  21. 230  
(gdb) next
218 for (i = 0; i < NumTotalRegs; i++)
(gdb) next # 執行直到下一行程式碼. (當前執行如果是呼叫其它函數, 則會執行完畢該函數到返回. 如果是 step, 則會進入到該函數!)
219 machine->WriteRegister(i, 0);
(gdb) print i # 列印出當前變數 i 的值.
$1 = 0
(gdb) next
218 for (i = 0; i < NumTotalRegs; i++)
(gdb) next
219 machine->WriteRegister(i, 0);
(gdb) next
218 for (i = 0; i < NumTotalRegs; i++)
(gdb) print i
$2 = 1
(gdb) break 229 # 設定斷點到當前位置 229 行代碼
Breakpoint 2 at 0x8057bdb: file ../userprog/addrspace.cc, line 229.
(gdb) continue # 繼續執行程式
Continuing.

Breakpoint 2, AddrSpace::InitRegisters (this=0x8070f30) at ../userprog/addrspace.cc:229
229 machine->WriteRegister(NextPCReg, 4);

(gdb) <等待輸入命令>

另外一個非常有用的命令是 bt: 它會秀出目前 call stack 的狀態, 這樣你可以知道之前你是執行那些函數後到目前執行點: 
(gdb) bt
#0 AddrSpace::InitRegisters (this=0x8070f30) at ../userprog/addrspace.cc:229
#1 0x08057b05 in AddrSpace::Execute (this=0x8070f30) at ../userprog/addrspace.cc:191
#2 0x08054b87 in main (argc=3, argv=0xffffdb24) at ../threads/main.cc:291

這樣的好處是當你的程式發生問題時 (segmentation fault), gdb 會停在 Console prompt mode, 此時你可以使用 bt 來 trace 到底程式時執行到那些函數時發生問題. 最後執行 continue 繼續執行:
(gdb) continue
Continuing.
Machine halting!

Ticks: total 22, idle 0, system 10, user 12
Disk I/O: reads 0, writes 0
Console I/O: reads 0, writes 0
Paging: faults 0
Network I/O: packets received 0, sent 0
[Inferior 1 (process 16569) exited normally]

(gdb) <等待輸入命令>

上面由 "Inferior 1 (process 16569) exited normally" 知道 debug 的程式正常結束執行. 在 gdb Console prompt mode 可以執行命令 quit 離開並回到原先 shell 的 console. 最後任何你要用 gdb 進行 debug 的程式都必須使用參數 -g 編譯, 如此 gdb 才能夠正常執行, 在 Nachos 的 Makefile 預設已經使用該參數: 
CFLAGS = -g -Wall $(INCPATH) $(DEFINES) $(HOSTCFLAGS) -DCHANGED -m32
...


Supplement: 
GDB 入門 : 使用 GDB 來除錯 
gdb是FSF下的一個子計畫,目的是提供一個除錯器的實作。只要使用GNU計畫出產的編譯器,就可以用它來進行除錯。它是一個文字介面的除錯器,然而也有人寫出GUI的介面。 在這裡將會介紹基本的除錯使用...

Debugging with GDB - Seventh Edition, for GDB version 4.17

沒有留言:

張貼留言

網誌存檔

關於我自己

我的相片
Where there is a will, there is a way!