天天看點

gdbServer + gdb 調試

内容摘要 遠端調試環境由主控端GDB和目标機調試stub共同構成,兩者通過序列槽或TCP連接配接。使用 GDB标準程串行協定協同工作,實作對目标機上的系統核心和上層應用的監控和調試功能。調試stub是嵌入式系統中的一段代碼,作為主控端GDB和目标機調試程式間的一個媒介而存在。 就目前而言,嵌入式Linux系統中,主要有三種遠端調試方法,分别适用于不同場合的調試工作:用ROM Monitor調試目标機程式、用KGDB調試系統核心和用gdbserver調試使用者空間程式。這三種調試方法的差別主要在于,目标機遠端調試stub 的存在形式的不同,而其設計思路和實作方法則是大緻相同的。 而我們最常用的是調試應用程式。就是采用gdb+gdbserver的方式進行調試。在很多情況下,使用者需要對一個應用程式進行反複調試,特别是複雜的程式。采用GDB方法調試,由于嵌入式系統資源有限性,一般不能直接在目标系統上進行調試,通常采用gdb+gdbserver的方式進行調試。

gdb的簡單使用

GDB是GNU開源組織釋出的一個強大的UNIX下的程式調試工具。或許,各位比較喜歡那種圖形界面方式的,像VC、BCB等IDE的調試,但如果你是在 UNIX平台下做軟體,你會發現GDB這個調試工具有比VC、BCB的圖形化調試器更強大的功能。所謂“寸有所長,尺有所短”就是這個道理。一般來說,GDB主要幫忙你完成下面四個方面的功能:      1、啟動你的程式,可以按照你的自定義的要求随心所欲的運作程式。

     2、可讓被調試的程式在你所指定的調置的斷點處停住。(斷點可以是條件表達式)

     3、當程式被停住時,可以檢查此時你的程式中所發生的事。

     4、動态的改變你程式的執行環境。從上面看來,GDB和一般的調試工具沒有什麼兩樣,基本上也是完成這些功能,不過在細節上,你會發現GDB這個調試工具的強大,大家可能比較習慣了圖形化的調試工具,但有時候,指令行的調試工具卻有着圖形化工具所不能完成的功能。讓我們一一看來。

一個調試示例

—————— 源程式:tst.c       1 #include <stdio.h>

      2

      3 int func(int n)

      4 {

      5          int sum=0,i;

      6          for(i=0; i<n; i++)

      7          {

      8                  sum+=i;

      9          }

     10          return sum;

     11 }

     12

     13

     14 main()

     15 {

     16          int i;

     17          long result = 0;

     18          for(i=1; i<=100; i++)

     19          {

     20                  result += i;

     21          }

     22

     23         printf("result[1-100] = %d \\n", result );

     24         printf("result[1-250] = %d \\n", func(250) );

     25 } 編譯生成執行檔案:(Linux下)

     hchen/test> cc -g tst.c -o tst 使用GDB調試: hchen/test> gdb tst   <---------- 啟動GDB

GNU gdb 5.1.1

Copyright 2002 Free Software Foundation, Inc.

GDB is free software, covered by the GNU General Public License, and you are

welcome to change it and/or 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.

This GDB was configured as "i386-suse-linux"...

(gdb) l      <-------------------- l指令相當于list,從第一行開始例出原碼。

1         #include <stdio.h>

2

3         int func(int n)

4         {

5                 int sum=0,i;

6                 for(i=0; i<n; i++)

7                 {

8                         sum+=i;

9                 }

10                return sum;

(gdb)        <-------------------- 直接回車表示,重複上一次指令

11        }

12

13

14        main()

15        {

16                int i;

17                long result = 0;

18                for(i=1; i<=100; i++)

19                {

20                        result += i;    

(gdb) break 16     <-------------------- 設定斷點,在源程式第16行處。

Breakpoint 1 at 0x8048496: file tst.c, line 16.

(gdb) break func   <-------------------- 設定斷點,在函數func()入口處。

Breakpoint 2 at 0x8048456: file tst.c, line 5.

(gdb) info break   <-------------------- 檢視斷點資訊。

Num Type            Disp Enb Address     What

1    breakpoint      keep y    0x08048496 in main at tst.c:16

2    breakpoint      keep y    0x08048456 in func at tst.c:5

(gdb) r            <--------------------- 運作程式,run指令簡寫

Starting program: /home/hchen/test/tst Breakpoint 1, main () at tst.c:17     <---------- 在斷點處停住。

(gdb) n           <--------------------- 單條語句執行,next指令簡寫。

(gdb) n

20                        result += i;

(gdb) c           <--------------------- 繼續運作程式,continue指令簡寫。

Continuing.

result[1-100] = 5050        <----------程式輸出。 Breakpoint 2, func (n=250) at tst.c:5

6                 for(i=1; i<=n; i++)

(gdb) p i         <--------------------- 列印變量i的值,print指令簡寫。

$1 = 134513808

(gdb) p sum

$2 = 1

(gdb) p i

$3 = 2

$4 = 3

(gdb) bt         <--------------------- 檢視函數堆棧。

#0   func (n=250) at tst.c:5

#1   0x080484e4 in main () at tst.c:24

#2   0x400409ed in __libc_start_main () from /lib/libc.so.6

(gdb) finish     <--------------------- 退出函數。

Run till exit from #0   func (n=250) at tst.c:5

0x080484e4 in main () at tst.c:24

24               printf("result[1-250] = %d \n", func(250) );

Value returned is $6 = 31375

(gdb) c      <--------------------- 繼續運作。

result[1-250] = 31375     <----------程式輸出。 Program exited with code 027. <--------程式退出,調試結束。

(gdb) q      <--------------------- 退出gdb。

hchen/test>

gdb+gdbserver方式進行ARM程式調試

【摘要】:本文首先介紹了gdb+gdbserver相關的概念,然後介紹了其下載下傳、編譯、安裝等過程;接着介紹了利用gdb+gdbserver調試應用程式的流程及執行個體等;最後分析了下gdb+gdbserver安裝過程中的常見問題。

【關鍵詞】:gdb,gdbserver,遠端調試

目錄

一、gdb+gdbserver總體介紹... 1

二、源代碼下載下傳... 1

三、配置編譯及安裝下載下傳... 1

四、gdb+gdbserver nfs調試流程... 2

五、如何利用序列槽調試... 3

六、實戰調試... 3

七、linux下安裝gdbserver問題... 5

一、gdb+gdbserver總體介紹

遠端調試環境由主控端GDB和目标機調試stub共同構成,兩者通過序列槽或TCP連接配接。使用 GDB标準程串行協定協同工作,實作對目标機上的系統核心和上層應用的監控和調試功能。調試stub是嵌入式系統中的一段代碼,作為主控端GDB和目标機調試程式間的一個媒介而存在。

就目前而言,嵌入式Linux系統中,主要有三種遠端調試方法,分别适用于不同場合的調試工作:用ROM Monitor調試目标機程式、用KGDB調試系統核心和用gdbserver調試使用者空間程式。這三種調試方法的差別主要在于,目标機遠端調試stub 的存在形式的不同,而其設計思路和實作方法則是大緻相同的。

而我們最常用的是調試應用程式。就是采用gdb+gdbserver的方式進行調試。在很多情況下,使用者需要對一個應用程式進行反複調試,特别是複雜的程式。采用GDB方法調試,由于嵌入式系統資源有限性,一般不能直接在目标系統上進行調試,通常采用gdb+gdbserver的方式進行調試。

二、源代碼下載下傳

嵌入式Linux的GDB調試環境由Host和Target兩部分組成,Host端使用arm-linux-gdb,Target Board端使用gdbserver。這樣,應用程式在嵌入式目标系統上運作,而gdb調試在Host端,是以要采用遠端調試(remote)的方法。進行GDB調試,目标系統必須包括gdbserver程式(在主機上正對硬體平台編譯成功後下載下傳到目标機上),主控端也必須安裝GDB 程式。一般Linux發行版中都有一個可以運作的GDB,但開發人員不能直接使用該發行版中的GDB來做遠端調試,而要擷取GDB的源代碼包,針對arm 平台作一個簡單配置,重新編譯得到相應GDB。GDB的源代碼包可以從

http://www.gnu.org/software/gdb/download/

http://ftp.gnu.org/gnu/gdb/   211.95.105.202:3128可以上去的,所有的版本都有啊

http: //ftp.cs.pu.edu.tw/linux/sourceware/gdb/releases/下載下傳

ftp://ftp.gnu.org/gnu/gdb

外網的ftp我經常上不去,國内常見的開源社群的下載下傳頻道通常都有下載下傳的http://download.chinaunix.net/download/0004000/3482.shtml,最新版本為gdb-6.5.tar.bz2。下載下傳到某個目錄,筆者下載下傳到/opt/。但要注意,gdb的版本需要和croostool 相比對。

三、配置編譯及安裝下載下傳

下載下傳完後,進入/opt/目錄,配置編譯步驟如下:

#tar jxvf gdb-6.5-tar-bz2

#cd gdb-6.5

#./configure --target=arm-linux --prefix=/usr/local/arm-gdb –v

(--target配置gdb的目标平台,--prefix配置安裝路徑,當然其他路徑也可以, .跟下面配置一緻即可,須在環境變量中聲明,啟動arm-linux-gdb需要,可更改/etc/profile或~/.bash_profile或~/.bashrc,添加export PATH=$PATH:/usr/local/arm-gdb/bin,這樣可以找到路徑)

#make

#make install

(生成arm-linux-gdb,并存入/usr/local/arm-gdb /bin/,查詢确認下)

也可以啟動arm-linux-gdb,若成功,則證明安裝無誤

進入gdb/gdbserver目錄:

[root@dding gdbserver]# pwd

/opt/gdb-6.5/gdb/gdbserver

[root@dding gdbserver]# 必須在gdbserver目錄下運作配置指令,此時才能用相對路徑

#./configure --target=arm-linux --host=arm-linux

(--target=arm-linux表示目标平台,--host表示主機端運作的是arm-linux-gdb,不需要配置—prefix,因為gdbserver不在主機端安裝運作)

#make CC=/usr/local/arm/2.95.3/bin/arm-linux-gcc

(這一步要指定你自己的arm-linux-gcc的絕對位置,我試過相對的不行,提示make: arm-linux-gcc: Command not found,可好多人都用的相對路徑,即直接指派arm-linux-gcc,可采取make時傳遞參數,也可以直接修改gdbserver目錄下的Makefile檔案中的環境變量CC)

沒有錯誤的話就在gdbserver目錄下生成gdbserver可執行檔案,注意此時要更改其屬性,否則可能會出現無法通路的情況,chmod 777 gdbserver将其更改為任何人都可以讀寫執行;使用arm-linux-strip指令處理一下gdbserver,将多餘的符号資訊删除,可讓elf檔案更精簡,通常在應用程式的最後釋出時使用;然後把它燒寫到flash的根檔案系統分區的/usr/bin(在此目錄下,系統可以自動找到應用程式,否則必須到gdbserver所在目錄下運作之),或通過nfs mount的方式都可以。隻要保證gdbserver能在開發闆上運作就行。

四、gdb+gdbserver nfs調試流程

下面就可以用gdb+gdbserver調試我們開發闆上的程式了。在目标闆上運作 gdbserver,其實就是在主控端的minicom下。我是在minicom下#mount 192.168.2.100:/ /tmp後做的(這裡參數-o nolock可以不加,不加這一步執行得反而更快些),hello和gdbserver都是位于Linux根目錄下,把主機根目錄挂在到開發闆的/tmp 目錄下。

要進行gdb調試,首先要在目标系統上啟動gdbserver服務。在gdbserver所在目錄下輸入指令:

(minicom下)

#cd /tmp

#./gdbserver 192.168.2.100:2345 hello

192.168.2.100為主控端IP,在目标系統的2345端口(你也可以設其他可用的值,當然必須跟主機的gdb一緻)開啟了一個調試程序,hello為要調試的程式(必須-g加入調試資訊)。

出現提示:

Process /tmp/hello created: pid=80

Listening on port 2345

(另一個終端下)

#cd /

#export PATH=$PATH:/usr/local/arm-gdb/bin

#arm-linux-gdb hello

最後一行顯示:This GDB was configured as “--host=i686-pc-linux-gnu,--target=arm-linux”...,如果不一緻說明arm-linux-gdb有問題

說明此gdb在X86的Host上運作,但是調試目标是ARM代碼。

(gdb) target remote 192.168.2.223:2345

(192.168.2.223為開發闆IP)

Remote debugging using 192.168.2.223:2345

[New thread 80]

[Switching to thread 80]

0x40002a90 in ??()

同時在minicom下提示:

Remote debugging from host 192.168.2.100

(gdb)

注意:你的端口号必須與gdbserver開啟的端口号一緻,這樣才能進行通信。建立連結後,就可以進行調試了。調試在Host端,跟gdb調試方法相同。注意的是要用“c”來執行指令,不能用“r”。因為程式已經在Target Board上面由gdbserver啟動了。結果輸出是在Target Board端,用超級終端檢視。連接配接成功,這時候就可以輸入各種GDB指令如list、run、next、step、break等進行程式調試了。

以上針對通過nfs mount和tftp的方式,隻能在主機上調試好後下載下傳到開發闆上運作,如果有錯誤要反複這個過程,繁瑣不說,有些程式隻能在開發闆上調試。是以筆者采用了gdbserver的遠端調試方式。希望對大家調試程式有用!

五、如何利用序列槽調試

如果你用序列槽1調試hello的話,你就要現在闆子上運作指令:

gdbserver hello /dev/ttyS0 (詳情可以參考gdbserver目錄下的readme檔案)

這時gdbserver就在等待gdb的應答信号了。

然後在pc機上運作指令:

xxx-linux-gdb hello

在xxx-linux-gdb裡敲入入下指令:

set remotedevice /dev/ttyS0(這裡設定序列槽1)

set remote baud 9600 (這裡設定序列槽波特率)

set debug remote 1(可選)

target remote /dev/ttyS0

操作到這兒,gdb就應該和gdbserver聯系上了。

六、實戰調試

1.編輯檔案

# vi gdbtest.c

1 #include <stdio.h>

3 int

4 func(int n){

5     int   sum=0, i;

6     for (i=0; i<n; i++){

7         sum += i;

8     }

9     return sum;

10 }

11

12 int

13 main(void)

14 {

15    int   i;

16    long result = 0;

17    for (i=0; i<=100; i++){

18        result += i;

19    }

20 

21    printf("result[1-100] = %d \n", result);

22    printf("resutl[1-225] = %d \n", func(255));

23

24    return 0;

25 }

# arm-linux-gcc -g gdbtest.c -o gdbtest         // 交叉編譯

2.下載下傳檔案到目标闆: gdbtest和gdbserver

假設 host pc ip:192.168.1.45

     board   ip:192.168.1.180   

将檔案拷貝到目标闆上:

先将gdbtest和gdbserver兩個檔案拷貝到主機的/tftpboot目錄下,此時系統主機和目标機都必須能夠支援nfs

在目标闆的Linux中運作:

#mount 192.168.1.108:/tftpboot /mnt/nfs

#cd /mnt/nfs

#ls

看是否有gdbtest和gdbserver兩個檔案。

3.運作調試

client board:

#./gdbserver 192.168.1.45:1234 gdbtest  // 目标闆上運作gdbtest 監聽端口1234

[root@AT91RM9200DK arm]$./gdbserver 192.168.0.12:2345 mainparacarm

./gdbserver: error in loading shared libraries: libthread_db.so.1: cannot open [root@AT91RM9200DK arm]$

host pc:

#cd /usr/local/arm-gdb/bin/ 以便能夠運作arm-linux-gdb,但是無此必要,可在環境變量中設定此路徑即可。

#copy gdbtest /usr/local/arm-gdb/bin/   // 将前面編譯的檔案gdbtest拷貝到此目錄

#./arm-linux-gdb gdbtest

(gdb)target remote 192.168.1.180:1234   // 連接配接到開發闆成功後就可以

進行調試

(gdb)list   or l

(gdb)break func

(gdb)break 22

(gdb)info br   

(gdb)continue   or c    // 這裡不能用 run

(gdb)next   or n

(gdb)print or p    result 

(gdb) finish        // 跳出func函數

(gdb) next

(gdb) quit

建立連接配接後進行gdb遠端調試和gdb本地調試方法相同

七、 linux下安裝gdbserver問題

toolchain version:   gdb的版本可能和交叉編譯器有很大的關系

gcc-3.3.2

glibc-2.2.5

binutils-2.15 此為croostool 3.3.2

安裝步驟:

下載下傳解壓gdb-6.6

#cd gdb-6.6

#make & make install

OK,然後:

#export PATH=$PATH:/usr/local/arm-gdb

進入gdbserver目錄:

#make CC=/usr/local/armv5l/3.3.2/bin/armv5l-linux-gcc

出錯:

/usr/local/armv5l/3.3.2/bin/armv5l-linux-gcc -c -Wall -g -O2 -I. -I. -I./../regformats -I./../../include -I../../bfd -I./../../bfd linux-arm-low.c

linux-arm-low.c:35:21: sys/reg.h: 沒有那個檔案或目錄

make: *** [linux-arm-low.o] 錯誤 1

然後把/usr/include/sys/reg.h copy到/usr/local/armv5l-2.6.x/3.3.2/armv5l-linux/include/sys/reg.h,即将該檔案拷貝到交叉編譯器的include目錄下,再make,顯示錯誤:

/usr/local/armv5l/3.3.2/bin/armv5l-linux-gcc -c -Wall -g -O2 -I. -I. -I./../regformats -I./../../include -I../../bfd -I./../../bfd thread-db.c

thread-db.c: In function `thread_db_err_str':

thread-db.c:95: error: `TD_VERSION' undeclared (first use in this function)

thread-db.c:95: error: (Each undeclared identifier is reported only once

thread-db.c:95: error: for each function it appears in.)

thread-db.c: In function `thread_db_get_tls_address':

thread-db.c:336: warning: implicit declaration of function `td_thr_tls_get_addr'

thread-db.c:336: warning: cast to pointer from integer of different size

thread-db.c:340: warning: cast from pointer to integer of different size

make: *** [thread-db.o] 錯誤 1

本想繼續fix error,但是感覺不太對,請問各位,是什麼原因呢?

是不是CC的target寫錯了?應該是arm-linux還是armv5l-linux?

1.

make: *** [linux-arm-low.o] Error 1

[root@dding gdbserver]#

[root@dding gdbserver]# gedit config.h

/* Define to 1 if you have the <sys/reg.h> header file. */

/*define HAVE_SYS_REG_H 1  */

/*have no  <sys/reg.h> header file. so undefine 20070402 dding  */

thread-db.c: In function `thread_db_err_str': gdb6.5

thread-db.c:95: `TD_VERSION' undeclared (first use in this function)

     94 #ifdef HAVE_TD_VERSION

     95     case TD_VERSION:

     96       return "version mismatch between libthread_db and libpthread";

     97 #endif

/* Define if TD_VERSION is available. */

/*#define HAVE_TD_VERSION 1  */

/*have no  TD_VERSION. so undefine 20070402 dding  */

gdb6.1 沒有此問題

3.

[root@AT91RM9200DK arm]$./gdbserver 192.168.0.12:2345 mainparacarm  gdb6.5

./gdbserver: error in loading shared libraries: libthread_db.so.1: cannot open

[root@AT91RM9200DK arm]$./gdbserver 192.168.0.14:2345 mainparacarm  gdb6.1

./gdbserver: error in loading shared libraries: libthread_db.so.1: cannot open shared object file: No such file or directory

我已經加了libthread_db.so.1共享庫為什麼還打不開呢????共享庫和cpu類型有關嗎? 

gdbserver: error while loading shared libraries: libthread_db.so.1: cannot open 

shared object file: No such file or director

****編譯GDB的時候搞成靜态的就好了.我想編譯選項裡應該有. 要不你就在Makefile裡加上CFLAGS += -static

LDFLAGS += -static

這兩個的其中一個應該就可以了,不過還是兩個都加上吧.

***/lib there is no  libthread_db.so.1 Can i use nfs to copy  libthread_db.so.1 to /lib? But now i cannot find this file, and is there any for cross 3.3.2?

libpthread-0.8.so

libpthread.so          libpthread.so.0        libresolv-2.1.3.so

libresolv.so.2         libstdc++.a.2.10.0     libtermcap.so.2

[root@AT91RM9200DK arm]$cp libthread_db-1.0.so libthread_db.so.1

[root@AT91RM9200DK arm]$cp libthread_db.so.1 /lib/

./gdbserver: /lib/libc.so.6: version `GLIBC_2.2' not found (required by /lib/li)

難道目前的gdb 6.5 版本太高,需要核心版本和交叉編譯器與之比對?實在不行,就試試低版本的gdb

參考文檔

http://blog.chinaunix.net/u/27802/showart_211833.html

http://litttlebylittle.bokee.com/5803108.html

http://www.blogcn.com/u/93/99/litcatfish/index.html

上一篇: GDB調試總結
下一篇: gdb core調試

繼續閱讀