嵌入式開發中使用DDD進行調試

    1.認識DDD

    GNU DDD是命令行調試程序,如GDB、DBX、WDB、Ladebug、JDB、XDB、Perl Debugger或Python Debugger的可視化圖形前端。它特有的圖形數據顯示功能(Graphical Data Display)可以把數據結構按照圖形的方式顯示出來。DDD最初源於1990年Andreas Zeller編寫的VSL結構化語言,後來經過一些程序員的努力,演化成今天的模樣。DDD的功能非常強大,可以調試用C/C++、Ada、 Fortran、Pascal、Modula-2和Modula-3編寫的程序;可以超文本方式瀏覽源代碼;能夠進行斷點設置、回溯調試和歷史紀錄編輯; 具有程序在終端運行的仿真窗口,並在遠程主機上進行調試的能力;圖形數據顯示功能(Graphical Data Display)是創建該調試器的初衷之一,能夠顯示各種數據結構之間的關係,並將數據結構以圖形化形式顯示;具有GDB/DBX/XDB的命令行界面, 包括完全的文本編輯、歷史紀錄、搜尋引擎。

    DDD是開源軟件,用戶可以去http://www.cs.tubs.de/softech/ddd/下載.rpm格式的DDD源碼文件。

     圖1顯示的是DDD的主窗口。它主要由選單欄、工具條、數據窗口、源文件窗口、機器碼窗口、控制檯和命令工具窗口等幾部分組成。其中,數據窗口用於觀察復 雜的數據結構,在刪除數據之後,顯示仍然有效;源文件窗口顯示源代碼、斷點和當前執行到達的位置,選擇該窗口中的“Display”項,可以顯示任意表達 式的值;機器碼窗口顯示當前所選函數的機器代碼,但僅對於GDB來說是可用的;在Debugger控制檯裏,用戶可以與DDD內置調試器的命令行接口進行 交互,等同於執行命令工具欄中的命令。


圖1 DDD的主窗口


     2.DDD運行機理

    在設計DDD的時候,主創人員決定把它與GDB之間的耦合度儘可能降小。因爲像GDB這樣的開源軟件,更新要比商業軟件快。所以爲了使GDB的變化不會影響到DDD,在DDD中,GDB是作爲獨立的進程運行的,通過命令行接口與DDD進行交互。

    DDD 的運行機理如圖2所示。它顯示了用戶、DDD、GDB和被調試進程之間的關係。爲了使響應時間變小,DDD和GDB之間的所有通信都是異步進行的。在 DDD中發出的GDB命令都會與一個回調例程相連,放入命令隊列中。這個回調例程在合適的時間會處理GDB的輸出。例如,如果用戶手動輸入一條GDB的命 令,DDD就會把這條命令與顯示GDB輸出的一個回調例程連起來。一旦GDB命令完成,就會觸發回調例程,GDB的輸出就會顯示在DDD的命令窗口中。


圖2 DDD的運行機理


    DDD 在事件循環時等待用戶輸入和GDB輸出,同時等着GDB進入等待輸入狀態。當GDB可用時,下一條命令就會從命令隊列中取出,送給GDB。GDB到達的輸 出由上次命令的回調過程來處理。這種異步機制避免了DDD在等待GDB輸出時發生阻塞現象,到達的事件可以在任何時間得到處理。

DDD和GDB的分離使得DDD運行速度變慢,但這種方法還有很多好處。例如,用戶可以把GDB調試器換成其它調試器,如DBX等。另外,還可以在不同的機器上運行GDB和DDD。

   
3. DDD調試示例

    現在就用DDD來實際調試下面sample.c這段程序,爲了節省空間,去掉了所有的註釋。

#include <stdio.h>
#include <stdlib.h>

#define MAXINPUTSTRINGSIZE 5
int n;
int factn;
char resultstring[100];

int getInt()
{
char * inputString;
int inputInt;
inputString = (char *) malloc (MAXINPUTSTRINGSIZE * sizeof(char));
printf("Enter the value:");
fgets(inputString, MAXINPUTSTRINGSIZE, stdin);
printf("You entered %s/n", inputString);
inputInt = atoi(inputString);
return inputInt;
}

int computeFact(int n)
{
int accum=0;
while(n>1) {
accum *= n;
n--;
}
return accum;
}

char * buildResultString(int x, int factx)
{
char * resultString = (char *) malloc(100 * sizeof(char));
sprintf(resultString, "The factorial of %d is %d/n", x, factx);
return resultString;
}

void main(int argc, char * argv[])
{
char * outString; // The string we will print out
n = getInt();
factn = computeFact(n);
outString = buildResultString(n, factn);
printf("%s/n",outString);
}


    首先,使用下面的命令編譯sample.c,切記要使用“-g”選項生成調試信息:
    #gcc -g -o sample sample.c

    接着運行sample程序,輸入數值“5”後,可以看到如下結果:
    You entered 5
    The factorial of 5 is 0

    可以看出,上面程序中是有錯誤的,需要進行調試。輸入下面的命令啓動DDD調試器,調試這個可執行程序:
    #ddd sample

    一段時間之後,DDD的主窗口就會出現。找到懷疑出錯的地方,在相應的代碼上設置斷點(在有懷疑的行上單擊鼠標左鍵,然後單擊工具欄中的“Break”按鈕)。然後單擊命令工具欄上的運行按鈕或在選單欄“Commands”裏選擇運行相關命令,如圖3所示。


圖3 使用DDD進行調試


    在控制檯中提示符下輸入數字“5”後按回車鍵,就會運行到圖3中箭頭指示的位置。這時候檢查可疑變量accum的值,在控制檯提示後輸入下面的命令:
    (gdb) display accum

    接着往下單步運行,多次點擊工具欄中的“Step”按鈕,觀察變量accum的結果。具體參考如下:

(gdb) step
43 while(n>1) {
1: accum = 0
(gdb)
44 accum *= n;
1: accum = 0
(gdb)
45 n--;
1: accum = 0
(gdb)
43 while(n>1) {
1: accum = 0
(gdb)
44 accum *= n;
1: accum = 0
(gdb)
45 n--;
1: accum = 0


    可以看出問題出在accum上。這時點擊命令工具欄上的“Kill”按鈕將程序斷掉,把初始化accum的那一句改爲“int accum = 1;”。重新運行之後,發現結果正確。至此,調試過程完畢。

   4. 特殊功能

    上面只是粗略地介紹了DDD調試的方法。實際上,DDD還有一些與衆不同的功能,例如可視化顯示數據結構(單個結構體、二叉樹、鏈表等)和繪製數據集等。

     圖4就是按點集繪製的數組sval中保存的數據(首先在源碼窗口選中要顯示的數組,然後點擊工具欄中的“Plot”按鈕,即會出現繪製窗口)。用戶也可以 不按數據點集顯示,在彈出窗口選單“Plot”下選擇“Lines”,就可以顯示成連線段。這個功能非常直觀,對於程序員調試程序來說是有很大幫助作用 的。


圖4 繪製數據集


    DDD包含的內容不止這些,由於篇幅的限制,這裏就不多說了。希望能起到拋磚引玉的作用。如果用戶想進一步學習,可以參考DDD的用戶手冊。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章