GDB和GDBServer

GDB是linux下的調試利器,是使用命令行來調試的。有必要專門學習一下。

bserver是配合gdb使用的另一個工具,通過配合可以實現遠程調試。目標機和調試機可以不在一臺機器上。先來學習一下gdb的常用命令:

    (1)l:列出所有源代碼
  (2)break main:在main處打斷點
          break test_debug.c:11:在test_debug.c的11行打斷點
  (3)c:運行到斷點處
  (4)s:(step)單步執行 (step in)
  (5)n:(next)單步執行,(step over) s(tep)會進入函數裏面,但是next不會
  (6)print a:打印a這個變量的值
  (7)quit:退出,輸入此命令則開發板上的gdbserver也退出

要使用gdb,必須在編譯時增加-g選項。

將gdbserver安裝在目標機上,並運行要調試的程序,監聽。然後在運行gdb的機器上可以向gdbserver發命令,完成調試。

例如現有程序代碼a.cpp,在調試機上運行g++ -o a -g a.cpp可以生成帶調試信息的a程序

將a拷貝到目標機,在目標機上運行

#gdbserver :1234 ./a

其中1234是端口號,可以改成其他的也可以。如果要指定調試機的ip可以在:1234前面加上ip地址

例如:#gdbserver 192.168.1.100:1234 ./a  (其中192.168.1.100爲調試機的ip地址)

然後再調試機上運行

#gdb ./a

然後進入gdb調試命令行

再輸入

target remote 192.168.1.1:1234  

其中192.168.1.1:1234是目標機的ip和端口號。

然後就可以遠程調試了。

參考資料1:linux應用調試技術之GDB和GDBServer

轉自:https://www.cnblogs.com/veryStrong/p/6240769.html

linux應用調試技術之GDB和GDBServer

1、調試原理

  GDB調試是應用程序在開發板上運行,然後在PC機上對開發板上得應用程序進行調試,PC機運行GDB,開發板上運行GDBServer。在應用程序調試的時候,pc機上的gdb向開發板上的GDBServer發出命令,而開發板上的gdbserver就會嚮應用程序發出信號,使應用程序停下來或者完成其他一些工作。

 

2、安裝GDB和GDBServer(gdb-7.4.tar.bz2 )

  2.1、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

    6、拷貝:cp tmp/bin/arm-linux-gdb  /bin/

    7、查看版本 /bin/arm-linux-gdb -v (使用絕對路徑使用gdb)

  2.2、GDBServer

    1、cd gdb/gdbserver/

    2、配置: ./configure --target=arm-linux --host=arm-linux

    3、編譯: make CC=arm-linux-gcc

 

  2.3、編譯GDBServer的時候會出現以下錯誤

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宏,導致編譯錯誤。我們到交叉編譯鏈去搜索一下,我們交叉編譯地址爲 /work/tools/gcc-3.4.5-glibc-2.3.6

# cd  /work/tools/gcc-3.4.5-glibc-2.3.6
# 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宏爲0x4202,頭文件爲include<linux/ptrace.h>中。

  有兩種解決辦法,可任選其一:

  ① 在linux-arm-low.c中直接添加宏 #define PTRACE_GETSIGINFO 0x4202

  ② 在linux-arm-low.c中將#include <sys/ptrace.h> 更改爲 #include <linux/ptrace.h>

  再次編譯,編譯通過。

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

#cp gdbserver /work/nfs_root/first_fs/bin

 

 3、調試

  3.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

3.2、在開發板上:

  打印出如下信息:

#gdbserver 192.168.1.10:123 ./test_debug
Process ./test_debug created; pid = 751
Listening on port 2345

 

  註釋:192.168.1.10:本開發板的ip

          123:端口號,自己隨便寫的

          ./test_debug:要調試的程序

3.3、在PC上輸入:

    /bin/arm-linux-gdb ./test-debug
           target remote 192.168.183.127:2345

3.4、正式調試!介紹幾個常用的命令

  (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這個變量的值

  (7)quit:退出,輸入此命令則開發板上的gdbserver也退出

   (8)  詳細的GDB調試命令

4、另外一種調試方法

  讓程序在開發板上直接運行,當它發生錯誤時,令它產生core dump文件,然後使用gdb根據core dump文件找到發生錯誤的地方

 

  在ARM板上:
  4.1、 ulimit -c unlimited
  4.2、 執行應用程序 : 程序出錯時會在當前目錄下生成名爲core的文件

 

  在PC上:

  4.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.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

參考資料2:gdb命令

轉自https://man.linuxde.net/gdb

gdb命令

編程開發

gdb命令包含在GNU的gcc開發套件中,是功能強大的程序調試器。GDB中的命令固然很多,但我們只需掌握其中十個左右的命令,就大致可以完成日常的基本的程序調試工作。

命令 解釋 示例
file <文件名> 加載被調試的可執行程序文件。
因爲一般都在被調試程序所在目錄下執行GDB,因而文本名不需要帶路徑。
(gdb) file gdb-sample
r Run的簡寫,運行被調試的程序。
如果此前沒有下過斷點,則執行完整個程序;如果有斷點,則程序暫停在第一個可用斷點處。
(gdb) r
c Continue的簡寫,繼續執行被調試程序,直至下一個斷點或程序結束。 (gdb) c
b <行號>
b <函數名稱>
b *<函數名稱>
b *<代碼地址> d [編號]
b: Breakpoint的簡寫,設置斷點。兩可以使用“行號”“函數名稱”“執行地址”等方式指定斷點位置。
其中在函數名稱前面加“*”符號表示將斷點設置在“由編譯器生成的prolog代碼處”。如果不瞭解彙編,可以不予理會此用法。 d: Delete breakpoint的簡寫,刪除指定編號的某個斷點,或刪除所有斷點。斷點編號從1開始遞增。
(gdb) b 8
(gdb) b main
(gdb) b *main
(gdb) b *0x804835c (gdb) d
s, n s: 執行一行源程序代碼,如果此行代碼中有函數調用,則進入該函數;
n: 執行一行源程序代碼,此行代碼中的函數調用也一併執行。 s 相當於其它調試器中的“Step Into (單步跟蹤進入)”;
n 相當於其它調試器中的“Step Over (單步跟蹤)”。 這兩個命令必須在有源代碼調試信息的情況下才可以使用(GCC編譯時使用“-g”參數)。
(gdb) s
(gdb) n
si, ni si命令類似於s命令,ni命令類似於n命令。所不同的是,這兩個命令(si/ni)所針對的是彙編指令,而s/n針對的是源代碼。 (gdb) si
(gdb) ni
p <變量名稱> Print的簡寫,顯示指定變量(臨時變量或全局變量)的值。 (gdb) p i
(gdb) p nGlobalVar
display ... undisplay <編號> display,設置程序中斷後欲顯示的數據及其格式。
例如,如果希望每次程序中斷後可以看到即將被執行的下一條彙編指令,可以使用命令
“display /i $pc”
其中 $pc 代表當前彙編指令,/i 表示以十六進行顯示。當需要關心彙編代碼時,此命令相當有用。 undispaly,取消先前的display設置,編號從1開始遞增。
(gdb) display /i $pc (gdb) undisplay 1
i info的簡寫,用於顯示各類信息,詳情請查閱“help i”。 (gdb) i r
q Quit的簡寫,退出GDB調試環境。 (gdb) q
help [命令名稱] GDB幫助命令,提供對GDB名種命令的解釋說明。
如果指定了“命令名稱”參數,則顯示該命令的詳細說明;如果沒有指定參數,則分類顯示所有GDB命令,供用戶進一步瀏覽和查詢。
(gdb) help

語法

gdb(選項)(參數)

選項

-cd:設置工作目錄;
-q:安靜模式,不打印介紹信息和版本信息;
-d:添加文件查找路徑;
-x:從指定文件中執行GDB指令;
-s:設置讀取的符號表文件。

參數

文件:二進制可執行程序。

實例

以下是linux下dgb調試的一個實例,先給出一個示例用的小程序,C語言代碼:

#include <stdio.h>
int nGlobalVar = 0;

int tempFunction(int a, int b)
{
    printf("tempFunction is called, a = %d, b = %d /n", a, b);
    return (a + b);
}

int main()
{
    int n;
        n = 1;
        n++;
        n--;

        nGlobalVar += 100;
        nGlobalVar -= 12;

    printf("n = %d, nGlobalVar = %d /n", n, nGlobalVar);

        n = tempFunction(1, 2);
    printf("n = %d", n);

    return 0;
}

請將此代碼複製出來並保存到文件 gdb-sample.c 中,然後切換到此文件所在目錄,用GCC編譯之:

gcc gdb-sample.c -o gdb-sample -g

在上面的命令行中,使用 -o 參數指定了編譯生成的可執行文件名爲 gdb-sample,使用參數 -g 表示將源代碼信息編譯到可執行文件中。如果不使用參數 -g,會給後面的GDB調試造成不便。當然,如果我們沒有程序的源代碼,自然也無從使用 -g 參數,調試/跟蹤時也只能是彙編代碼級別的調試/跟蹤。

下面“gdb”命令啓動GDB,將首先顯示GDB說明,不管它:

GNU gdb Red Hat Linux (5.3post-0.20021129.18rh)
Copyright 2003 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-redhat-linux-gnu".
(gdb)

上面最後一行“(gdb)”爲GDB內部命令引導符,等待用戶輸入GDB命令。

下面使用“file”命令載入被調試程序 gdb-sample(這裏的 gdb-sample 即前面 GCC 編譯輸出的可執行文件):

(gdb) file gdb-sample
Reading symbols from gdb-sample...done.

上面最後一行提示已經加載成功。

下面使用“r”命令執行(Run)被調試文件,因爲尚未設置任何斷點,將直接執行到程序結束:

(gdb) r
Starting program: /home/liigo/temp/test_jmp/test_jmp/gdb-sample
n = 1, nGlobalVar = 88
tempFunction is called, a = 1, b = 2
n = 3
Program exited normally.

下面使用“b”命令在 main 函數開頭設置一個斷點(Breakpoint):

(gdb) b main
Breakpoint 1 at 0x804835c: file gdb-sample.c, line 19.

上面最後一行提示已經成功設置斷點,並給出了該斷點信息:在源文件 gdb-sample.c 第19行處設置斷點;這是本程序的第一個斷點(序號爲1);斷點處的代碼地址爲 0x804835c(此值可能僅在本次調試過程中有效)。回過頭去看源代碼,第19行中的代碼爲“n = 1”,恰好是 main 函數中的第一個可執行語句(前面的“int n;”爲變量定義語句,並非可執行語句)。

再次使用“r”命令執行(Run)被調試程序:

(gdb) r
Starting program: /home/liigo/temp/gdb-sample

Breakpoint 1, main () at gdb-sample.c:19
19 n = 1;

程序中斷在gdb-sample.c第19行處,即main函數是第一個可執行語句處。

上面最後一行信息爲:下一條將要執行的源代碼爲“n = 1;”,它是源代碼文件gdb-sample.c中的第19行。

下面使用“s”命令(Step)執行下一行代碼(即第19行“n = 1;”):

(gdb) s
20 n++;

上面的信息表示已經執行完“n = 1;”,並顯示下一條要執行的代碼爲第20行的“n++;”。

既然已經執行了“n = 1;”,即給變量 n 賦值爲 1,那我們用“p”命令(Print)看一下變量 n 的值是不是 1 :

(gdb) p n
$1 = 1

果然是 1。($1大致是表示這是第一次使用“p”命令——再次執行“p n”將顯示“$2 = 1”——此信息應該沒有什麼用處。)

下面我們分別在第26行、tempFunction 函數開頭各設置一個斷點(分別使用命令“b 26”“b tempFunction”):

(gdb) b 26
Breakpoint 2 at 0x804837b: file gdb-sample.c, line 26.
(gdb) b tempFunction
Breakpoint 3 at 0x804832e: file gdb-sample.c, line 12.

使用“c”命令繼續(Continue)執行被調試程序,程序將中斷在第二 個斷點(26行),此時全局變量 nGlobalVar 的值應該是 88;再一次執行“c”命令,程序將中斷於第三個斷點(12行,tempFunction 函數開頭處),此時tempFunction 函數的兩個參數 a、b 的值應分別是 1 和 2:

(gdb) c
Continuing.

Breakpoint 2, main () at gdb-sample.c:26
26 printf("n = %d, nGlobalVar = %d /n", n, nGlobalVar);
(gdb) p nGlobalVar
$2 = 88
(gdb) c
Continuing.
n = 1, nGlobalVar = 88

Breakpoint 3, tempFunction (a=1, b=2) at gdb-sample.c:12
12 printf("tempFunction is called, a = %d, b = %d /n", a, b);
(gdb) p a
$3 = 1
(gdb) p b
$4 = 2

上面反饋的信息一切都在我們預料之中~~

再一次執行“c”命令(Continue),因爲後面再也沒有其它斷點,程序將一直執行到結束:

(gdb) c
Continuing.
tempFunction is called, a = 1, b = 2
n = 3
Program exited normally.

有時候需要看到編譯器生成的彙編代碼,以進行彙編級的調試或跟蹤,又該如何操作呢?

這就要用到display命令“display /i $pc”了(此命令前面已有詳細解釋):

(gdb) display /i $pc
(gdb)

此後程序再中斷時,就可以顯示出彙編代碼了:

(gdb) r
Starting program: /home/liigo/temp/test_jmp/test_jmp/gdb-sample

Breakpoint 1, main () at gdb-sample.c:19
19 n = 1;
1: x/i $pc 0x804835c <main+16>: movl $0x1,0xfffffffc(%ebp)

看到了彙編代碼,“n = 1;”對應的彙編代碼是“movl $0x1,0xfffffffc(%ebp)”。

並且以後程序每次中斷都將顯示下一條彙編指定(“si”命令用於執行一條彙編代碼——區別於“s”執行一行C代碼):

(gdb) si
20 n++;
1: x/i $pc 0x8048363 <main+23>: lea 0xfffffffc(%ebp),%eax
(gdb) si
0x08048366 20 n++;
1: x/i $pc 0x8048366 <main+26>: incl (%eax)
(gdb) si
21 n--;
1: x/i $pc 0x8048368 <main+28>: lea 0xfffffffc(%ebp),%eax
(gdb) si
0x0804836b 21 n--;
1: x/i $pc 0x804836b <main+31>: decl (%eax)
(gdb) si
23 nGlobalVar += 100;
1: x/i $pc 0x804836d <main+33>: addl $0x64,0x80494fc

接下來我們試一下命令“b *<函數名稱>”。

爲了更簡明,有必要先刪除目前所有斷點(使用“d”命令——Delete breakpoint):

(gdb) d
Delete all breakpoints? (y or n) y
(gdb)

當被詢問是否刪除所有斷點時,輸入“y”並按回車鍵即可。

下面使用命令“b *main”在 main 函數的 prolog 代碼處設置斷點(prolog、epilog,分別表示編譯器在每個函數的開頭和結尾自行插入的代碼):

(gdb) b *main
Breakpoint 4 at 0x804834c: file gdb-sample.c, line 17.
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/liigo/temp/test_jmp/test_jmp/gdb-sample

Breakpoint 4, main () at gdb-sample.c:17
17 {
1: x/i $pc 0x804834c <main>: push %ebp
(gdb) si
0x0804834d 17 {
1: x/i $pc 0x804834d <main+1>: mov %esp,%ebp
(gdb) si
0x0804834f in main () at gdb-sample.c:17
17 {
1: x/i $pc 0x804834f <main+3>: sub $0x8,%esp
(gdb) si
0x08048352 17 {
1: x/i $pc 0x8048352 <main+6>: and $0xfffffff0,%esp
(gdb) si
0x08048355 17 {
1: x/i $pc 0x8048355 <main+9>: mov $0x0,%eax
(gdb) si
0x0804835a 17 {
1: x/i $pc 0x804835a <main+14>: sub %eax,%esp
(gdb) si
19 n = 1;
1: x/i $pc 0x804835c <main+16>: movl $0x1,0xfffffffc(%ebp)

此時可以使用“i r”命令顯示寄存器中的當前值———“i r”即“Infomation Register”:

(gdb) i r
eax 0xbffff6a4 -1073744220
ecx 0x42015554 1107383636
edx 0x40016bc8 1073834952
ebx 0x42130a14 1108544020
esp 0xbffff6a0 0xbffff6a0
ebp 0xbffff6a8 0xbffff6a8
esi 0x40015360 1073828704
edi 0x80483f0 134513648
eip 0x8048366 0x8048366
eflags 0x386 902
cs 0x23 35
ss 0x2b 43
ds 0x2b 43
es 0x2b 43
fs 0x0 0
gs 0x33 51

當然也可以顯示任意一個指定的寄存器值:

(gdb) i r eax
eax 0xbffff6a4 -1073744220

最後一個要介紹的命令是“q”,退出(Quit)GDB調試環境:

(gdb) q
The program is running. exit anyway? (y or n)

 

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