# 四、
來源:[JOS學習筆記(四)](http://blog.csdn.net/roger__wong/article/details/8623941)
Lab1還差最后一部分,就是給出具體的調試信息,如下面所示:
```
K> backtrace
Stack backtrace:
ebp f010ff78 eip f01008ae args 00000001 f010ff8c 00000000 f0110580 00000000
kern/monitor.c:143: monitor+106
ebp f010ffd8 eip f0100193 args 00000000 00001aac 00000660 00000000 00000000
kern/init.c:49: i386_init+59
ebp f010fff8 eip f010003d args 00000000 00000000 0000ffff 10cf9a00 0000ffff
kern/entry.S:70: <unknown>+0
K>
```
和上次的montacktrace相比,增加了eip所在的源文件、對應的源文件行數、函數名以及在相對于該函數地址后的匯編代碼的地址偏移。
首先明確,這個功能既然能實現,那么肯定在某個地方以某種方式地方記錄著eip和源文件之間的相互對應的信息。
而這個存儲的地方就是stab表。
首先使用objdump -G 查看一下kernel生成的obj(obj/kern/kernel)中stab表中的內容,如下:

前5列是stab,第六列不知道含義,第七列對應stabstr,換句話說這個表會原封不動的讀入內存,stab部分的地址就是kdebug.c里的__stab_begin__,后面字符串地址就是__stabstr_begin__。
這個表很直觀,根據類型不同在某些行(用index表示)會給出一個地址與源文件的對應關系(SO),緊接著給出這個源文件中的函數地址和函數名的對應關系(FUN),之后是這個函數里的每一行(SLINE)和地址偏移的對應關系。
而我們所要做的就是讀取這些關系并顯示出來。
## 問題1 這個表何時以何種方式加入的內存。
很遺憾kdebug.c里面stab表的地址符號是extern進來的,我并沒有找到這個符號第一次出現的位置,不過估計應該是和內核一起,通過elf頭的文件信息加載進來的,但具體加載到了哪里我沒有深究。
## 問題2 如何對這個表進行檢索。
慶幸的是,Jos很“友好”的給我們提供了一個檢索函數:stab_binsearch。函數接受4個參數,第一是stab總表(這個說法并不是很恰當,雖然函數的含義是傳入一個總表,但其實只要傳入表中的一項當做起始位置函數也能正常工作),第二第三是表的下標的范圍,代表在這個下標范圍內尋找,若找到了,則第二個參數賦值為相應的表的下標,若沒有找到,則right>left,第四個參數傳入要尋找的表項的類型,也就是上面圖的第二列。第五個參數代表要尋找的值,也就是上面圖中的第五列,根據這個值尋找表項。而且為了比較高效,這個函數還是個二分搜索的。更加詳細的說明函數注釋都解釋的很清楚。
## 問題3 我們需要做什么
需要做的是完成debuginfo_eip,該函數傳入一個eip值,通過這個eip查找所需信息,并放入Eipdebuginfo這個結構體里。
JOS比較“友好”,已經替我們完成了很多功能了,我們只需要使用stab_binsearch找到函數地址和源文件行數對應的關系就行了。
代碼如下:

之前的代碼已經找到了函數地址,并且已經將addr減去了這個函數地址得到了函數內偏移,查找結果會放在lline里,根據這個index訪問stab表即可。
之后修改backtrace函數,按要求顯示相應信息即可,然后運行結果大概如下:

值得注意的是,對應源文件,發現“行數”指向的并不是很正確,有時候會多向下指幾行。
這是因為在查找stab時候,我們是根據eip進行查找,而eip的內容就是指向下一條將要執行的指令的地址,所以“行數”的不準是正常的合乎邏輯的。
當然要修正這個“bug”使之更符合我們的調試習慣也并不難,只要把查找到的lline改成lline-1,即尋找上一行源代碼即可。
但因為實驗貌似要求就是根據eip找到相應的位置,所以我也并沒有改。
至此lab1就結束了,本來想一兩周就寫完的,沒想到斷斷續續拖了好幾個月。
5年前剛接觸編程的時候編的第一個程序就是printf("hello,world!");,然后學習斷點調試,可直到今天我才大概明白了這看上去如此“簡單”而不值得一提的功能實現起來需要多少的技術水平。任何看起來“理所當然”的東西實現或者證明起來都是相當難的,數學和計算機技術都佐證了這一點。
所謂返璞歸真的境界,大概就是如此吧。