學習筆記 --- LINUX應用調試之使用GDB

一、調試原理
這裏的gdb調試是在pc機上對在開發板上運行的程序進行調試。具體來說的話,在pc機上要運行gdb,在開發板上運行dbserver。gdb調試的時候,pc機上的gdb向開發板上的gdbserver發出命令,而開發板上的gdbserver就會嚮應用程序發出信號,使應用程序停下來或者完成其他一些工作!由此我們知道,pc機上要運行gdb,開發板上要運行gdbserver!

二、安裝gdb與gdbserver:需要 gdb-7.4.tar.bz2 
gdb:
1、下載: http://ftp.gnu.org/gnu/gdb/ 
2、解壓:tar xvf gdb-7.4.tar.bz2
3、配置:cd gdb-7.4/
                ./configure --target=arm-linux
4、編譯:make
5、安裝:mkdir tmp 
                make install prefix=$PWD/tmp
這裏是安裝到了我們當前目錄的tmp裏面
6、查看PC機上以前安裝好的gdb版本:arm-linux-gdb -v
發現是7.4版本的,我們編譯的正好也是7.4版本的哦!
7、拷貝:cp tmp/bin/arm-linux-gdb /bin/
以後我們如果想使用我們自己編譯的gdb的話可以使用絕對路徑:/bin/arm-linux-gdb

gdbserver
1、cd gdb/gdbserver/
2、配置: ./configure --target=arm-linux --host=arm-linux
3、編譯: make CC=/usr/local/arm/3.4.5/bin/arm-linux-gcc
出現錯誤:
linux-arm-low.c: In function `arm_stopped_by_watchpoint':
linux-arm-low.c:642: error: `PTRACE_GETSIGINFO' undeclared (first use in this function)
linux-arm-low.c:642: error: (Each undeclared identifier is reported only once
linux-arm-low.c:642: error: for each function it appears in.)

解決方法:這裏提示沒有PTRACE_GETSIGINFO這個東西,這裏搜索PTRACE_GETSIGINFO的路徑爲-I指定的頭文件以及交叉 編譯工具鏈,我們不妨到交叉編譯工具鏈裏面去查找一下:
cd /usr/local/arm/3.4.5/
grep "PTRACE_GETSIGINFO" * -nR
找到如下信息:
arm-linux/sys-include/linux/ptrace.h:27:#define PTRACE_GETSIGINFO       0x4202
arm-linux/include/linux/ptrace.h:27:#define PTRACE_GETSIGINFO   0x4202
distributed/arm-linux/sys-include/linux/ptrace.h:27:#define PTRACE_GETSIGINFO   0x4202
distributed/arm-linux/include/linux/ptrace.h:27:#define PTRACE_GETSIGINFO       0x4202
說明PTRACE_GETSIGINFO是在交叉編譯工具鏈:linux/ptrace.h文件裏定義的,那麼可能是頭文件沒有包含好吧!
我們到gdbserver下的linux-arm-low.c裏面一看,可不是嘛,只有:#include <sys/ptrace.h>而沒有:#include <linux/ptrace.h>,於是加上:#include <linux/ptrace.h>,再次編譯:make CC=/usr/local/arm/3.4.5/bin/arm-linux-gcc,成功!

4、拷貝:將gdbserver拷貝到開發板的bin目錄下

三、調試
1、編譯要調試的應用程序:必須要加-g選項
測試程序如下(名字是:test_debug.c):
#include <stdio.h>
void C(int *p)
{
*p = 0x12;
}
void B(int *p)
{
C(p);
}
void A(int *p)
{
B(p);
}
void A2(int *p)
{
C(p);
}
int main(int argc, char **argv)
{
int a;
int *p = NULL;
A2(&a);  // A2 > C
printf("a = 0x%x\n", a);

A(p);    // A > B > C
return 0;
}
按如下編譯它:arm-linux-gcc -g -o test_debug test_debug.c
這裏加了 -g 表示添加調試信息

2、運行時出現錯誤:
/mnt/code/28th_app_debug # ./test_debug
a = 0x12
Segmentation fault
下面就開始進行調試

3、在開發板上:gdbserver 192.168.183.127:2345 ./test_debug
 
打印出如下信息:
Process ./test_debug created; pid = 751
Listening on port 2345
 
其中192.168.183.127:本開發板的ip
       123:端口號,自己隨便寫的
       ./test_debug:要調試的程序
 
4、在PC上:/bin/arm-linux-gdb ./test-debug
                     target remote 192.168.183.127:2345
 
5、下面就可以正式調試了!我們先來說一下幾個常用的命令
(1)l:列出所有源代碼
(2)break main:在main處打斷點
         break test_debug.c:11:在test_debug.c的11行打斷點
(3)c:運行到斷點處
(4)step:單步執行
(5)next:單步執行,但是step會進入函數裏面,但是next不會
(6)print a:打印a這個變量的值
(6)quit:退出,輸入此命令則開發板上的gdbserver也退出
更詳細的命令,我們在下一節裏面會進一步來講講的!
 
6、另一種調試方法
讓程序在開發板上直接運行,當它發生錯誤時,令它產生core dump文件
然後使用gdb根據core dump文件找到發生錯誤的地方
 
在ARM板上:
1. ulimit -c unlimited
2. 執行應用程序 : 程序出錯時會在當前目錄下生成名爲core的文件
 
在PC上:
3、首先將core文件拷貝到pc機上
     然後:/bin/arm-linux-gdb ./test_debug ./core
 
打印出如下信息:
 
GNU gdb (GDB) 7.4
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
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 "--host=i686-pc-linux-gnu --target=arm-linux".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/share/jz2440/test_debug...done.
[New LWP 748]
warning: `/lib/libc.so.6': Shared library architecture unknown is not compatible with target architecture arm.
warning: `/lib/ld-linux.so.2': Shared library architecture unknown is not compatible with target architecture arm.
Core was generated by `./test_debug'.
Program terminated with signal 11, Segmentation fault.
#0  0x000084ac in C (p=0x0) at test_debug.c:6
6               *p = 0x12;
 
4、bt:可以顯示調用關係
 
#0  0x000084ac in C (p=0x0) at test_debug.c:6
#1  0x000084d0 in B (p=0x0) at test_debug.c:12
#2  0x000084f0 in A (p=0x0) at test_debug.c:17

#3  0x00008554 in main (argc=1, argv=0xbeb32eb4) at test_debug.c:34

______________________________________________________________________________________________________________________________-

DBG工具的使用補充:

1、查看源碼:
list [函數名][行數]

2、暫停程序
(1)設置斷點:
a、break + [源代碼行號][源代碼函數名][內存地址]
b、break ... if condition   ...可以是上述任一參數,condition是條件。例如在循環體中可以設置break ... if i = 100 來設置循環次數
(2)觀察斷點:
a、watch + [變量][表達式]  當變量或表達式值改變時即停住程序。
b、rwatch + [變量][表達式] 當變量或表達式被讀時,停住程序。
c、awatch + [變量][表達式] 當變量或表達式被讀或被寫時,停住程序。

(3)設置捕捉點:
catch + event  當event發生時,停住程序。
event可以是下面的內容:
a、throw 一個C++拋出的異常。(throw爲關鍵字)
b、catch 一個C++捕捉到的異常。(catch爲關鍵字)
c、exec 調用系統調用exec時。(exec爲關鍵字,目前此功能只在HP-UX下有用)
d、fork 調用系統調用fork時。(fork爲關鍵字,目前此功能只在HP-UX下有用)
e、vfork 調用系統調用vfork時。(vfork爲關鍵字,目前此功能只在HP-UX下有用)
f、load 或 load 載入共享庫(動態鏈接庫)時。(load爲關鍵字,目前此功能只在HP-UX下有用)
g、unload 或 unload 卸載共享庫(動態鏈接庫)時。(unload爲關鍵字,目前此功能只在HP-UX下有用)


(4)捕獲信號:
handle + [argu] + signals
signals:
是Linux/Unix定義的信號,SIGINT表示中斷字符信號,也就是Ctrl+C的信號,SIGBUS表示硬件故障的信號;SIGCHLD表示子進程狀態改變信號; SIGKILL表示終止程序運行的信號,等等。
argu:
nostop   當被調試的程序收到信號時,GDB不會停住程序的運行,但會打出消息告訴你收到這種信號。
stop     當被調試的程序收到信號時,GDB會停住你的程序。
print    當被調試的程序收到信號時,GDB會顯示出一條信息。
noprint  當被調試的程序收到信號時,GDB不會告訴你收到信號的信息。
pass or noignore    當被調試的程序收到信號時,GDB不處理信號。這表示,GDB會把這個信號交給被調試程序會處理。
nopass or ignore     當被調試的程序收到信號時,GDB不會讓被調試程序來處理這個信號。


(5)線程中斷:

break [linespec] thread [threadno] [if ...]
linespec 斷點設置所在的源代碼的行號。如: test.c:12表示文件爲test.c中的第12行設置一個斷點。
threadno 線程的ID。是GDB分配的,通過輸入info threads來查看正在運行中程序的線程信息。 
 if ...   設置中斷條件。

3、查看信息:
(1)查看數據:
print  variable        查看變量
print  *array@len      查看數組(array是數組指針,len是需要數據長度)
可以通過添加參數來設置輸出格式:
            /x 按十六進制格式顯示變量。
            /d 按十進制格式顯示變量。
            /u 按十六進制格式顯示無符號整型。
            /o 按八進制格式顯示變量。
            /t 按二進制格式顯示變量。 
            /a 按十六進制格式顯示變量。
            /c 按字符格式顯示變量。
            /f 按浮點數格式顯示變量。

(2)查看內存:
         examine /n f u + 內存地址(指針變量)
         n 表示顯示內存長度
         f 表示輸出格式(見上)
         u 表示字節數制定(b 單字節;h 雙字節;w 四字節;g 八字節;默認爲四字節)
  如:
             x /10cw pFilePath  (pFilePath爲一個字符串指針,指針佔4字節)
             x 爲examine命令的簡寫。

(3)查看棧信息:
  backtrace [-n][n]
         n  表示只打印棧頂上n層的棧信息。
        -n 表示只打印棧底上n層的棧信息。
         不加參數,表示打印所有棧信息。

基本gdb命令: 
---------------------------------------------------------------------
命令          簡寫         功能
---------------------------------------------------------------------
file                             裝入想要調試的可執行文件. 
kill             k              終止正在調試的程序. 
list             l               列出產生執行文件的源代碼的一部分. 
next           n              執行一行源代碼但不進入函數內部. 
step          s              執行一行源代碼而且進入函數內部. 
continue  c               繼續執行程序,直至下一中斷或者程序結束。
run            r               執行當前被調試的程序.
quit           q               終止 gdb.
watch                        使你能監視一個變量的值而不管它何時被改變. 
catch                         設置捕捉點.
thread       t               查看當前運行程序的線程信息.
break        b              在代碼裏設置斷點, 這將使程序執行到這裏時被掛起. 
make                        使你能不退出 gdb 就可以重新產生可執行文件. 
shell                         使你能不離開 gdb 就執行 UNIX shell 命令.  
print          p              打印數據內容。
examine  x               打印內存內容。
backtrace bt             查看函數調用棧的所有信息。 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章