gdb各種調試命令和技巧


陳皓:用GDB調試程序

GDB概述
————

GDB是GNU開源組織發布的一個強大的UNIX下的程序調試工具。或許,各位比較喜歡那種圖形界面方式的,像VC、BCB等IDE的調試,但如果你是在UNIX平台下做軟件,你會發現GDB這個調試工具有比VC、BCB的圖形化調試器更強大的功能。所謂“寸有所長,尺有所短”就是這個道理。

一般來說,GDB主要幫忙你完成下面四個方面的功能:

    1、啟動你的程序,可以按照你的自定義的要求隨心所欲的運行程序。
    2、可讓被調試的程序在你所指定的調置的斷點處停住。(斷點可以是條件表達式)
    3、當程序被停住時,可以檢查此時你的程序中所發生的事。
    4、動態的改變你程序的執行環境。

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

一個調試示例
——————

 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    <---------- 在斷點處停住。
17               long result = 0;
(gdb) n          <--------------------- 單條語句執行,next命令簡寫。
18               for(i=1; i<=100; i++)
(gdb) n
20                       result += i;
(gdb) n
18               for(i=1; i<=100; i++)
(gdb) n
20                       result += i;
(gdb) c          <--------------------- 繼續運行程序,continue命令簡寫。
Continuing.
result[1-100] = 5050       <----------程序輸出。

 

Breakpoint 2, func (n=250) at tst.c:5
5                int sum=0,i;
(gdb) n
6                for(i=1; i<=n; i++)
(gdb) p i        <-------  打印變量i的值,print命令簡寫。從這里可以看到,n是顯示下一條要運行的語句,但還沒運行
$1 = 134513808
(gdb) n
8                        sum+=i;
(gdb) n
6                for(i=1; i<=n; i++)
(gdb) p sum
$2 = 1
(gdb) n
8                        sum+=i;
(gdb) p i
$3 = 2
(gdb) n
6                for(i=1; i<=n; i++)
(gdb) p sum
$4 = 3
(gdb) bt        <--------------------- 查看函數堆棧。backtrace 打印當前的函數調用棧的所有信息。
#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     <--------------------- 繼續運行。
Continuing.
result[1-250] = 31375    <----------程序輸出。

 

Program exited with code 027. <--------程序退出,調試結束。
(gdb) q     <--------------------- 退出gdb。
hchen/test>

 

好了,有了以上的感性認識,還是讓我們來系統地認識一下gdb吧。

 

 

 


使用GDB
————

 

一般來說GDB主要調試的是C/C++的程序。要調試C/C++的程序,首先在編譯時,我們必須要把調試信息加到可執行文件中。使用編譯器(cc/gcc/g++)的 -g 參數可以做到這一點。如:

 

    > cc -g hello.c -o hello
    > g++ -g hello.cpp -o hello

 

如果沒有-g,你將看不見程序的函數名、變量名,所代替的全是運行時的內存地址。當你用-g把調試信息加入之后,並成功編譯目標代碼以后,讓我們來看看如何用gdb來調試他。

 

啟動GDB的方法有以下幾種:

 

    1、gdb <program> 
       program也就是你的執行文件,一般在當然目錄下。

 

    2、gdb <program> core
       用gdb同時調試一個運行程序和core文件,core是程序非法執行后core dump后產生的文件。

 

    3、gdb <program> <PID>
       如果你的程序是一個服務程序,那么你可以指定這個服務程序運行時的進程ID。gdb會自動attach上去,並調試他。program應該在PATH環境變量中搜索得到。

 

 

 

GDB啟動時,可以加上一些GDB的啟動開關,詳細的開關可以用gdb -help查看。我在下面只例舉一些比較常用的參數:

 

    -symbols <file> 
    -s <file> 
    從指定文件中讀取符號表。

 

    -se file 
    從指定文件中讀取符號表信息,並把他用在可執行文件中。

 

    -core <file>
    -c <file> 
    調試時core dump的core文件。

 

    -directory <directory>
    -d <directory>
    加入一個源文件的搜索路徑。默認搜索路徑是環境變量中PATH所定義的路徑。

轉自:http://blog.csdn.net/haoel/article/details/2879

 技巧匯總:

輸出格式

一般來說,GDB會根據變量的類型輸出變量的值。但你也可以自定義GDB的輸出的格
式。例如,你想輸出一個整數的十六進制,或是二進制來查看這個整型變量的中的
位的情況。要做到這樣,你可以使用GDB的數據顯示格式:

x 按十六進制格式顯示變量。
d 按十進制格式顯示變量。
u 按十六進制格式顯示無符號整型。
o 按八進制格式顯示變量。
t 按二進制格式顯示變量。
a 按十六進制格式顯示變量。
c 按字符格式顯示變量。
f 按浮點數格式顯示變量。

(gdb) p i
$21 = 101

(gdb) p/a i
$22 = 0x65

(gdb) p/c i
$23 = 101 'e'

(gdb) p/f i
$24 = 1.41531145e-43

(gdb) p/x i
$25 = 0x65

(gdb) p/t i
$26 = 1100101

程序變量

查看文件中某變量的值:
file::variable
function::variable
可以通過這種形式指定你所想查看的變量,是哪個文件中的或是哪個函數中的。例如,查看文件f2.c中的全局變量x的值:
gdb) p 'f2.c'::x

查看數組的值

有時候,你需要查看一段連續的內存空間的值。比如數組的一段,或是動態分配的數據的大小。你可以使用GDB的“@”操

作符,“@”的左邊是第一個內存的地址的值,“@”的右邊則你你想查看內存的長度。例如,你的程序中有這樣的語句:

int *array = (int *) malloc (len * sizeof (int));
於是,在GDB調試過程中,你可以以如下命令顯示出這個動態數組的取值:

p *array@len

二維數組打印

p **array@len

如果是靜態數組的話,可以直接用print數組名,就可以顯示數組中所有數據的內容了。

http://www.cppblog.com/chaosuper85/archive/2009/08/04/92123.html

 

*啟動gdb調試指定程序app: 

$gdb app 

這樣就在啟動gdb之后直接載入了app可執行程序,需要注意的是,載入的app程序必須在編譯的時候有gdb調試選項,例如'gcc -g app app.c',注意,如果修改了程序的源代碼,但是沒有編譯,那么在gdb中顯示的會是改動后的源代碼,但是運行的是改動前的程序,這樣會導致跟蹤錯亂的。 

 

*啟動程序之后,再用gdb調試: 

$gdb <program> <PID> 

這里,<program>是程序的可執行文件名,<PID>是要調試程序的PID.如果你的程序是一個服務程序,那么你可以指定這個服務程序運行時的進程ID。gdb會自動attach上去,並調試他。program應該在PATH環境變量中搜索得到。 

 

*啟動程序之后,再啟動gdb調試: 

$gdb <PID> 

這里,程序是一個服務程序,那么你可以指定這個服務程序運行時的進程ID,<PID>是要調試程序的PID.這樣gdb就附加到程序上了,但是現在還沒法查看源代碼,用file命令指明可執行文件就可以顯示源代碼了。 

 

 

gdb斷點設置

http://sourceware.org/gdb/current/onlinedocs/gdb

二、斷點設置

gdb斷點分類:

以設置斷點的命令分類:

breakpoint

可以根據行號、函數、條件生成斷點。

watchpoint

監測變量或者表達式的值發生變化時產生斷點。

catchpoint

監測信號的產生。例如c++的throw,或者加載庫的時候。

gdb中的變量從1開始標號,不同的斷點采用變量標號同一管理,可以 用enable、disable等命令管理,同時支持斷點范圍的操作,比如有些命令接受斷點范圍作為參數。

例如:disable 5-8

 

1、break及break變種詳解:

相關命令有break,tbreak,rbreak,hbreak,thbreak,后兩種是基於硬件的,先不介紹。

>>break 與 tbreak

break,tbreak可以根據行號、函數、條件生成斷點。tbreak設置方法與break相同,只不過tbreak只在斷點停一次,過后會自動將斷點刪除,break需要手動控制斷點的刪除和使能。

break 可帶如下參數:

linenum                 本地行號,即list命令可見的行

filename:linenum  制定個文件的行號

function                函數,可以是自定義函數也可是庫函數,如open

filename:function  制定文件中的函

condtion                條件

 (

5.2 多文件設置斷點

在進入指定函數時停住:

C++中可以使用class::function或function(type,type)格式來指定函數名。如果有名稱空間,可以使用namespace::class::function或者function(type,type)格式來指定函數名。

break filename:linenum 
在源文件filename的linenum行處停住 
break filename:function 
在源文件filename的function函數的入口處停住

break class::function或function(type,type)  (個人感覺這個比較方便,b 類名::函數名,執行后會提示如:

 

>>b GamePerson::update
Breakpoint 1 at 0x46b89e: file GamePerson.cpp, line 14.

 

 

在類class的function函數的入口處停住

break namespace::class::function

在名稱空間為namespace的類class的function函數的入口處停住

)

 

*address      地址,可是函數,變量的地址,此地址可以通過info add命令得到。

例如:

break 10    

break test.c:10

break main

break test.c:main

break system

break open

如果想在指定的地址設置斷點,比如在main函數的地址出設斷點。

可用info add main 獲得main的地址如0x80484624,然后用break *0x80484624.

條件斷點就是在如上述指定斷點的同時指定進入斷點的條件。

例如:(假如有int 類型變量 index)

break 10 if index == 3

tbreak 12 if index == 5

 

8)單步運行  (gdb) n

9)程序繼續運行  (gdb) c

  使程序繼續往下運行,直到再次遇到斷點或程序結束;

break + 設置斷點的行號  break n      在n行處設置斷點

tbreak + 行號或函數名  tbreak n/func    設置臨時斷點,到達后被自動刪除

 

until   

until line-number  繼續運行直到到達指定行號,或者函數,地址等。 (這個很有用)

until line-number if   condition

 

bt:  顯示當前堆棧的追蹤,當前所在的函數

 

 

(1)如何打印變量的值?(print var) 

(2)如何打印變量的地址?(print &var) 

(3)如何打印地址的數據值?(print *address) 

(4)如何查看當前運行的文件和行?(backtrace) 

(5)如何查看指定文件的代碼?(list file:N) 

(6)如何立即執行完當前的函數,但是並不是執行完整個應用程序?(finish) 

(7)如果程序是多文件的,怎樣定位到指定文件的指定行或者函數?(list file:N) 

(8)如果循環次數很多,如何執行完當前的循環?(until) 

 

 

 

4.調試運行環境相關命令

set args  set args arg1 arg2  設置運行參數

show args  show args  參看運行參數

set width + 數目  set width 70  設置GDB的行寬

cd + 工作目錄  cd ../  切換工作目錄

run  r/run  程序開始執行

step(s)  s  進入式(會進入到所調用的子函數中)單步執行,進入函數的前提是,此函數被編譯有debug信息

next(n)  n  非進入式(不會進入到所調用的子函數中)單步執行

finish  finish  一直運行到函數返回並打印函數返回時的堆棧地址和返回值及參數值等信息

until + 行數  u 3  運行到函數某一行 

continue(c)  c  執行到下一個斷點或程序結束 

return <返回值>  return 5  改變程序流程,直接結束當前函數,並將指定值返回

call + 函數  call func  在當前位置執行所要運行的函數

 

查看堆棧信息

info stack

用這條指令你可以看清楚程序的調用層次關系,這個挺有用

3. 執行一行程序. 若呼叫函數, 則將該包含該函數程序代碼視為一行程序 (next 指令可簡寫為 n)。

(gdb) next

4. 執行一行程序. 若呼叫函數, 則進入函數逐行執行 (step 指令可簡寫為 s)。
(gdb) step

5. 執行一行程序,若此時程序是在 for/while/do loop 循環的最后一行,則一直執行到循環結束后的第一行程序后停止 (until 指令可簡寫為 u)。
(gdb) until

6. 執行現行程序到回到上一層程序為止。
(gdb) finish

 

*執行下一步: 

(gdb) next 

這樣,執行一行代碼,如果是函數也會跳過函數。這個命令可以簡化為n. 

 

*執行N次下一步: 

(gdb) next N 

 

*執行上次執行的命令: 

(gdb) [Enter] 

這里,直接輸入回車就會執行上次的命令了。 

 

*單步進入: 

(gdb) step 

這樣,也會執行一行代碼,不過如果遇到函數的話就會進入函數的內部,再一行一行的執行。 

 

*執行完當前函數返回到調用它的函數: 

(gdb) finish 

這里,運行程序,直到當前函數運行完畢返回再停止。例如進入的單步執行如果已經進入了某函數,而想退出該函數返回到它的調用函數中,可使用命令finish. 

 

*指定程序直到退出當前循環體: 

(gdb) until 

或(gdb) u 

這里,發現需要把光標停止在循環的頭部,然后輸入u這樣就自動執行全部的循環了。

 

 

 

*列出指定區域(n1到n2之間)的代碼: 

(gdb) list n1 n2 

這樣,list可以簡寫為l,將會顯示n1行和n2行之間的代碼,如果使用-tui啟動gdb,將會在相應的位置顯示。如果沒有n1和n2參數,那么就會默認顯示當前行和之后的10行,再執行又下滾10行。另外,list還可以接函數名。 

一般來說在list后面可以跟以下這們的參數: 

<linenum>   行號。 

<+offset>   當前行號的正偏移量。 

<-offset>   當前行號的負偏移量。 

<filename:linenum>  哪個文件的哪一行。 

<function>  函數名。 

<filename:function> 哪個文件中的哪個函數。 

<*address>  程序運行時的語句在內存中的地址

 

 有時候n輸不出代碼信息,上傳缺失的那個文件,然后directory . 表示指定當前目錄。 然后就可以看到了。

 

>>rbreak

rbreak 可以跟一個規則表達式。rbreak + 表達式的用法與grep + 表達式相似。即在所有與表達式匹配的函數入口都設置斷點。

 

rbreak list_* 即在所有以 list_ 為開頭字符的函數地方都設置斷點。

rbreak ^list_ 功能與上同。

>>查看斷點信息

info break [break num ]

info break 可列出所有斷點信息,info break 后也可設置要查看的break num如:

info break 1 列出斷點號是1的斷點信

 

 

Linux編程基礎——GDB(設置斷點)

http://www.cnblogs.com/ggjucheng/archive/2011/12/14/2288004.html

 

GDB 找不到源代碼

有時候在用gdb調試程序的時候,發現gdb找不到源碼。用list命令無效。

記住: gdb的調試信息中並不包含源碼,只是包含了怎樣去尋找源碼,但是因為某種原因,比如你的源碼轉移了位置或者別的原因。你需要告訴gdb到哪里去尋找源碼。這個通過directory命令來實現。

 

要查看當前gdb尋找源碼的路徑:

show directories

添加一個新的路徑到查找路徑:

dir  dirname

添加多個時,個dirname用: 分開。

詳細見 : http://ftp.gnu.org/old-gnu/Manuals/gdb-5.1.1/html_node/gdb_48.html

三、源文件
GDB時,提示找不到源文件。
需要做下面的檢查:
編譯程序員是否加上了 -g參數 以包含debug信息。
路徑是否設置正確了。
使用GDB的directory命令來設置源文件的目錄。

下面給一個調試/bin/ls的示例(ubuntu下)
$ apt-get source coreutils
$ sudo apt-get install coreutils-dbgsym
$ gdb /bin/ls
GNU gdb (GDB) 7.1-ubuntu
(gdb) list main
1192    ls.c: No such file or directory.
in ls.c
(gdb) directory ~/src/coreutils-7.4/src/
Source directories searched: /home/hchen/src/coreutils-7.4:$cdir:$cwd
(gdb) list main
1192        }
1193    }
1194

http://blog.csdn.net/liujiayu2/article/details/50008131

http://blog.chinaunix.net/uid-9525959-id-2001805.html

https://wenku.baidu.com/view/504eaffa02768e9951e738c8.html

 

gdb打印STL容器

GDB中print方法並不能直接打印STL容器中保存的變量,

(gdb) p vec
$1 = std::vector of length 1, capacity 1 = {2}

 

(

其實只要http://www.yolinux.com/TUTORIALS/src/dbinit_stl_views-1.03.txt這個文件保存為~/.gdbinit  就可以使用它提供的方法方便調試容器

現在我使用這個腳本提供的plist方法打印

 

下載 http://www.yolinux.com/TUTORIALS/src/dbinit_stl_views-1.03.txt   2. #cat dbinit_stl_views-1.03.txt >> ~/.gdbinit   3. 若正處於gdb中,運行命令:    (gdb) source ~/.gdbinit

 

 

[c-sharp]  view plain copy
 
 
  1. (gdb) plist lst  
  2. List size = 5   
  3. List type = std::list<int, std::allocator<int> >  
  4. Use plist <variable_name> <element_type> to see the elements in the list.  
  5. (gdb)  

 

 

詳細查看list中的元素信息

 

[c-sharp]  view plain copy
 
 
  1. (gdb) plist lst int  
  2. elem[0]: $5 = 7  
  3. elem[1]: $6 = 1  
  4. elem[2]: $7 = 5  
  5. elem[3]: $8 = 9  
  6. elem[4]: $9 = 2  
  7. List size = 5   
  8. (gdb)   

腳本文件如下:把下列內容拷貝到一份txt文件里,然后重命名    .gdbinit(有點號,隱藏文件),然后拷貝到用戶主目錄下,~/.gdbinit,調用gdb就可以查看容器內容了。

 

# include < vector > 
using namespace std ; 

int main( ) 

        vector < int > vec; 
        vec. push_back( 2) ; 
        vec. push_back( 3) ; 
        vec. push_back( 4) ; 
        return 0; 
}

編譯:#g+ + - o bugging - g bugging. cpp

 

Breakpoint 1, main ( ) at bugging. cpp: 6 
6        vector < int > vec; 
( gdb) n 
7        vec. push_back( 2) ; 
( gdb) 
8        vec. push_back( 3) ; 
( gdb) pvector 
Prints std : : vector < T> information. 
Syntax: pvector < vector > < idx1> < idx2> 
Note: idx, idx1 and idx2 must be in acceptable range [ 0. . < vector > . size( ) - 1] . 
Examples: 
pvector v - Prints vector content, size, capacity and T typedef 
pvector v 0 - Prints element[ idx] from vector 
pvector v 1 2 - Prints elements in range [ idx1. . idx2] from vector 
( gdb) pvector vec 
elem[ 0] : $1 = 2 
Vector size = 1 
Vector capacity = 1 
Element type = int * 
( gdb) n 
9        vec. push_back( 4) ; 
( gdb) 
10        return 0; 
( gdb) pvector vec 
elem[ 0] : $2 = 2 
elem[ 1] : $3 = 3 
elem[ 2] : $4 = 4 
Vector size = 3 
Vector capacity = 4 
Element type = int * 
( gdb)

5. 默認情況下gdb不能用[]查看stl容器的數據元素,提示如下錯誤:

( gdb) print vec[ 0] 
One of the arguments you tried to pass to operator [ ] could not be converted to what the function wants.

 

 

Gdb保存斷點:

 

 1. 保存斷點

    先用info b 查看一下目前設置的斷點,使用save breakpoint命令保存到指定的文件,這里我使用了和進程名字后面加bp后綴,你可以按你的喜好取名字。

    我使用的是save breakpoint fig8.3.bp ,在當前目錄下,會生成一個fig8.3.bp的文件,里面存儲的就是斷點的命令。

 

    2. 讀取斷點

    注意,在gdb已經加載進程的過程中,是不能夠讀取斷點文件的,必須在gdb加載文件的命令中指定斷點文件,具體就是使用-x參數。例如,我需要調試fig8.3這個文件,指定剛才保存的斷點文件fig8.3.bp。

    我使用的是gdb fig8.3 -x fig8.3.bp

 

我又仔細看了一下上面的對於“save breakpoints”的注釋,在另一個gdb session中可以使用“source”命令restore(恢復,還原)它們(斷點)。測試一下:先刪除所有斷點,然后使用source恢復所有保存的斷點,如下:

  1. (gdb) D                                                #刪除所有斷點  
  2. Delete all breakpoints? (y or n) y  
  3. (gdb) info b                                           #查看斷點  
  4. No breakpoints or watchpoints.  
  5. (gdb) source gdb.cfg                                   #加載斷點  
  6. Breakpoint 9 at 0x40336d: file RecvMain.cpp, line 290.  
  7. Breakpoint 10 at 0x2aaab049c7ef: file CRealCreditEventAb.cpp, line 80.  
  8. ……  
  9.   
  10.   
  11. (gdb) info b                                           #查看斷點  
  12. Num     Type           Disp Enb Address            What  
  13. 9       breakpoint     keep y   0x000000000040336d in main(int, char**) at RecvMain.cpp:290  
  14. 10      breakpoint     keep y   0x00002aaab049c7ef in CRealCreditEventAb::LoopEventCreditCtrl(int)  
  15.                                                at CRealCreditEventAb.cpp:80  
  16. ……  

 

set命令

再見吐舌頭該命令可以改變一個變量的值。

再見吐舌頭set variable varname = value

再見吐舌頭varname是變量名稱,value是變量的新值。

(1)修改變量值:

a. printv=value: 修改變量值的同時,把修改后的值顯示出來

b. set [var]v=value: 修改變量值,需要注意如果變量名與GDB中某個set命令中的關鍵字一樣的話,前面加上var關鍵字

 

 

條件斷點

 

設置一個條件斷點,條件由cond指定;在gdb每次執行到此
斷點時,cond都被計算。當cond的值為非零時,程序在斷點處停止。

 

用法:

break [break-args] if (condition)

 

例如:

break main if argc > 1
break 180 if (string == NULL && i < 0)
break test.c:34 if (x & y) == 1
break myfunc if i % (j+3) != 0
break 44 if strlen(mystring) == 0
b 10 if ((int)$gdb_strcmp(a,"chinaunix") == 0)
b 10 if ((int)aa.find("dd",0) == 0)

 

condition

可以在我們設置的條件成立時,自動停止當前的程序,先使用break(或者watch也可以)設置斷點,
然后用condition來修改這個斷點的停止(就是斷)的條件。

 

用法:
condition <break_list> (conditon)

 

例如:
cond 3 i == 3

condition 2 ((int)strstr($r0,".plist") != 0)

 

ignore

如果我們不是想根據某一條件表達式來停止,而是想斷點自動忽略前面多少次的停止,從某一次開始
才停止,這時ignore就很有用了。

 

用法:

ignore <break_list> count

上面的命令行表示break_list所指定的斷點號將被忽略count次。

 

例如:

ignore 1 100,表示忽略斷點1的前100次停止

 

  Watchpoints相關命令:(Watchpoint的作用是讓程序在某個表達式的值發生變化的時候停止運行,達到‘監視’該表達式的目的)

(1)設置watchpoints:

a. watch expr: 設置寫watchpoint,當應用程序寫expr,修改其值時,程序停止運行

b. rwatch expr: 設置讀watchpoint,當應用程序讀表達式expr時,程序停止運行

c. awatch expr: 設置讀寫watchpoint, 當應用程序讀或者寫表達式expr時,程序都會停止運行

(2)info watchpoints:查看當前調試的程序中設置的watchpoints相關信息

(3)watchpoints和breakpoints很相像,都有enable/disabe/delete等操作,使用方法也與breakpoints的類似

 

 

 

對斷點的控制除了建立和刪除外,還可以通過使能和禁止來控制,后一種方法更靈活。

斷點的四種使能操作:

enable [breakpoints] [range...] 完全使能
enable                //激活所有斷點
enable 4            //激活4斷點
enable 5-6            //激活5~6斷點
disable [breakpoints] [range...] 禁止
用法舉例:
diable                //禁止所有斷點
disble 2            //禁止第二個斷點
disable 1-5            //禁止第1到第5個斷點

enable once [breakpoints] [range...] 使能一次,觸發后禁止
enable delete [breakpoints] [range...]使能一次,觸發后刪除

 

 

1、程序運行參數。
set args 可指定運行時參數。(如:set args -f 20 -t 40)
show args 命令可以查看設置好的運行參數。

2、運行環境。
path 可設定程序的運行路徑。
show paths 查看程序的運行路徑。
set environment varname [=value] 設置環境變量。如:set env USER=user
show environment [varname] 查看環境變量。

3、工作目錄。
cd 相當於shell的cd命令。
pwd 顯示當前的所在目錄。

 

 

從一個實例來認識GDB與高效調試

http://blog.csdn.net/tonywearme/article/details/41447007

http://blog.csdn.net/lxl584685501/article/details/45575717

GDB的全稱是GNU project debugger,是類Unix系統上一個十分強大的調試器。這里通過一個簡單的例子(插入算法)來介紹如何使用gdb進行調試,特別是如何通過中斷來高效地找出死循環;我們還可以看到,在修正了程序錯誤並重新編譯后,我們仍然可以通過原先的GDB session進行調試(而不需要重開一個GDB),這避免了一些重復的設置工作;同時,在某些受限環境中(比如某些實時或嵌入式系統),往往只有一個Linux字符界面可供調試。這種情況下,可以使用job在代碼編輯器、編譯器(編譯環境)、調試器之間做到無縫切換。這也是高效調試的一個方法。

先來看看這段插入排序算法(a.cpp),里面有一些錯誤。

// a.cpp
#include <stdio.h>
#include <stdlib.h>

int x[10];
int y[10];
int num_inputs;
int num_y = 0;

void get_args(int ac, char **av)
{ 
   num_inputs = ac - 1;
   for (int i = 0; i < num_inputs; i++)
      x[i] = atoi(av[i+1]);
}

void scoot_over(int jj)
{ 
   for (int k = num_y-1; k > jj; k++)
      y[k] = y[k-1];
}

void insert(int new_y)
{ 
   if (num_y = 0)
   {
      y[0] = new_y;
      return;
   }

   for (int j = 0; j < num_y; j++)
   {
      if (new_y < y[j])
      {
         scoot_over(j);
         y[j] = new_y;
         return;
      }
   }
}

void process_data()
{
   for (num_y = 0; num_y < num_inputs; num_y++)
      insert(x[num_y]);
}

void print_results()
{ 
   for (int i = 0; i < num_inputs; i++)
      printf("%d\n",y[i]);
}

int main(int argc, char ** argv)
{ 
   get_args(argc,argv);
   process_data();
   print_results();
   return 0;
}
View Code

碼就不分析了,稍微花點時間應該就能明白。你能發現幾個錯誤?

 

使用gcc編譯:

gcc -g -Wall -o insert_sort a.cpp

"-g"告訴gcc在二進制文件中加入調試信息,如符號表信息,這樣gdb在調試時就可以把地址和函數、變量名對應起來。在調試的時候你就可以根據變量名查看它的值、在源代碼的某一行加一個斷點等,這是調試的先決條件。“-Wall”是把所有的警告開關打開,這樣編譯時如果遇到warning就會打印出來。一般情況下建議打開所有的警告開關。

運行編譯后的程序(./insert_sort),才發現程序根本停不下來。上調試器!(有些bug可能一眼就能看出來,這里使用GDB只是為了介紹相關的基本功能)

TUI模式

現在版本的GDB都支持所謂的終端用戶接口模式(Terminal User Interface),就是在顯示GDB命令行的同時可以顯示源代碼。好處是你可以隨時看到當前執行到哪條語句。之所以叫TUI,應該是從GUI抄過來的。注意,可以通過ctrl + x + a來打開或關閉TUI模式。

gdb -tui ./insert_sort


死循環

進入GDB后運行run命令,傳入命令行參數,也就是要排序的數組。當然,程序也是停不下來:

了讓程序停下來,我們可以發送一個中斷信號(ctrl + c),GDB捕捉到該信號后會掛起被調試進程。注意,什么時候發送這個中斷有點技巧,完全取決於我們的經驗和程序的特點。像這個簡單的程序,正常情況下幾乎立刻就會執行完畢。如果感覺到延遲就說明已經發生了死循環(或其他什么),這時候發出中斷肯定落在死循環的循環體中。這樣我們才能通過檢查上下文來找到有用信息。大型程序如果正常情況下就需要跑個幾秒鍾甚至幾分鍾,那么你至少需要等到它超時后再去中斷。

此時,程序暫停在第44行(第44行還未執行),TUI模式下第44行會被高亮顯示。我們知道,這一行是某個死循環體中的一部分。

因為暫停的代碼有一定的隨機性,可以多運行幾次,看看每次停留的語句有什么不同。后面執行run命令的時候可以不用再輸入命令行參數(“12 5”),GDB會記住。還有,再執行run的時候GDB會問是否重頭開始執行程序,當然我們要從頭開始執行。

基本確定位置后(如上面的44行),因為這個程序很小,可以單步(step)一條條語句查看。不難發現問題出在第24行,具體的步驟就省略了。

無縫切換

在編碼、調試的時候,除非你有集成開發環境,一般你會需要打開三個窗口:代碼編輯器(比如很多人用的VIM)、編譯器(新開的窗口運行gcc或者make命令、執行程序等)、調試器。集成開發環境當然好,但某些倒閉的場合下你無法使用任何GUI工具,比如一些僅提供字符界面的嵌入式設備——你只有一個Linux命令行可以使用。顯然,如果在VIM中修改好代碼后需要先關閉VIM才能敲入gcc的編譯命令,或者調試過程中發現問題需要先關閉調試器才能重新打開VIM修改代碼、編譯、再重新打開調試器,那么不言而喻,這個過程太痛苦了!

好在可以通過Linux的作業管理機制,通過ctrl + z把當前任務掛起,返回終端做其他事情。通過jobs命令可以查看當前shell有哪些任務。比如,當我暫停GDB時,jobs顯示我的VIM編輯器進程與GDB目前都處於掛起狀態。

以下是些相關的命令,比較常用

fg %1         // 打開VIM,1是VIM對應的作業號
fg %2         // 打開GDB
bg %1         // 讓VIM到后台運行
kill %1 && fg // 徹底殺死VIM進程

 

GDB的“在線刷新”

好了,剛才介紹了無縫切換,那我們可以在不關閉GDB的情況下(注意,ctrl + z不是關閉GDB這個進程,只是掛起)切換到VIM中去修改代碼來消除死循環(把第24行的“if (num_y = 0)" 改成"if (num_y == 0)")。動作序列可以是:

ctrl + z // 掛起GDB
jobs     // 查看VIM對應的作業號,假設為1
fg %1    // 進入VIM,修改代碼..
ctrl + z // 修改完后掛起VIM
gcc -g -Wall -o insert_sort a.cpp // 重新編譯程序
fg %2    // 進入GDB,假設GDB的作業號為2

現在,我們又返回GDB調試界面了!但在調試前還有一步,如何讓GDB識別新的程序(因為程序已經重新編譯)?只要再次運行run就可以了。因為GDB沒有關閉,所以之前設置的斷點、運行run時傳入的命令行參數等還保留着,不需要重新輸入。很好用吧!

 

 

gdb Tui窗口:

TUI模式

現在版本的GDB都支持所謂的終端用戶接口模式(Terminal User Interface),就是在顯示GDB命令行的同時可以顯示源代碼。好處是你可以隨時看到當前執行到哪條語句。之所以叫TUI,應該是從GUI抄過來的。注意,可以通過ctrl + x + a來打開或關閉TUI模式。

gdb -tui ./insert_sort

方法1:使用gdb -tui

方法二: 直接使用gdb調試代碼,在需要的時候使用切換鍵 ctrl+x a調出gdbtui。

 

調試代碼的時候,只能看到下一行,每次使用list非常煩,不知道當前代碼的context 

http://beej.us/guide/bggdb/#compiling

 

簡單來說就是在以往的gdb開始的時候添加一個-tui選項.有的版本已經有gdbtui這個程序了

在linux自帶的終端里是正常顯示的,但是在securecrt里面,可能由於編碼的問題,邊緣會有些亂碼,不過不影響使用(如果你的程序有錯誤輸出,會擾亂整個界面,所以在調試的時候,建議添加2>/dev/null,這樣的話基本可用) 

啟動gdb之后,上面是src窗口,下面是cmd窗口,默認focus在src窗口的,這樣的話上下鍵以及pagedown,pageup都是在移動顯示代碼,並不顯示上下的調試命令.這個時候要切換focus,具體可簡單參見

(gdb) info win 查看當前focus
        SRC     (36 lines)  <has focus>
        CMD     (18 lines)
(gdb) fs next 切換focus Focus set to CMD window. (gdb) info win SRC (36 lines) CMD (18 lines) <has focus> (gdb) fs SRC 切換指定focus Focus set to SRC window. (gdb) 

(Window names are case in-sensitive.)

  

 

 

To start in neato and highly-recommended GUI mode, start the debugger with gdb -tui. (For many of the examples, below, I show the output of gdb's dumb terminal mode, but in real life I use TUI mode exclusively.)

And here is a screenshot of what you'll see, approximately:

 

 

gdb設置程序參數:

有三種方法可以指定程序運行的參數,第一種方法是在命令行上直接指定;第二種方法是通過run命令提供程序運行時的參數;第三種方法是通過set args命令指定程序的參數

第一種方法:為程序傳遞參數5

root@guo-virtual-machine:~/debug# gdb --args factorial 5
  • 1

第二種方法:為程序傳遞參數5

(gdb) run 5 
  • 1
  • 2
  • 3

第三種方法:為程序傳遞參數5

(gdb) set args 5 (gdb) run Starting program: /root/debug/factorial 5 warning: no loadable sections found in added symbol-file system-supplied DSO at 0x7ffff7ffa000 Factorial of 5 is 120 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

查看指定給程序的參數通過show args

(gdb) show args Argument list to give program being debugged when it is started is "5". (gdb) 



因為暫停的代碼有一定的隨機性,可以多運行幾次,看看每次停留的語句有什么不同。后面執行run命令的時候可以不用再輸入命令行參數(“12 5”),GDB會記住。還有,再執行run的時候GDB會問是否重頭開始執行程序,當然我們要從頭開始執行。



    在使用gdb調試時,經常要用到查看堆棧信息,特別是在內核調試時,這
顯得尤其重要。通過gdb的堆棧跟蹤,可以看到所有已調用的函數列表,以及
每個函數在棧中的信息。
---------------------------------------------------------------------------------
一,簡單實例。
  1. #include <stdio.h>
  2. int sum(int m,int n)
  3. {
  4.     int i = 3;
  5.     int j = 4;
  6.    return m+n;    
  7. }
  8. int main(void)
  9. {
  10.     int m = 10;
  11.     int n = 9;
  12.     int ret = 0;
  13.     ret = sum(m,n);
  14.     printf("ret = %d\n",ret);
  15.     return 0;    
  16. }
  1. (gdb) bt
  2. #0 sum (m=10, n=9) at sum.c:5
  3. #1 0x08048418 in main () at sum.c:16
每次有函數調用,在棧上就會生成一個棧框(stack frame),也就是一個數據
單元用來描述該函數,描述函數的地址,參數,還有函數的局部變量的值等信息。
使用bt命令就可以把這個棧的調用信息全部顯示出來。

由上面的顯示結果可以看出,棧上有兩個棧框(stack frame),分別用來描述函數
main和函數sum.前面的#0表示sum函數棧框的標號。#1表示main函數棧框的標號。
最新的棧框標號為0.main函數棧框標號最大。
  1. (gdb) frame 1
  2. #1 0x08048418 in main () at sum.c:16
  3. 16 ret = sum(m,n);
frame 1 表示選擇棧框1,也就是選擇了main函數的棧框,因為我這時候想查看
main函數的信息。

  1. (gdb) info locals
  2. m = 10
  3. n = 9
  4. ret = 0
這時候可以通過info locals查看main函數棧框里面局部變量的值。
-----------------------------------------------------------------------------------
二,使用gdb堆棧跟蹤很方面調試遞歸程序。

  1. #include <stdio.h>
  2. long long func(int n)
  3. {
  4.     int i = 0;
  5.     if (n > 20) {
  6.         printf("n too large!\n");
  7.         return -1;
  8.     }
  9.     if (n == 0) 
  10.         return 1;
  11.     else {
  12.         i = n * func(n-1);
  13.         return i;
  14.     }
  15. }
  16. int main(void)
  17. {
  18.     long long ret;
  19.     ret = func(10);
  20.     printf("ret = %lld\n",ret);
  21.     return 0;
  22. }
  1. (gdb) bt
  2. #0 func (n=7) at test.c:7
  3. #1 0x0804843f in func (n=8) at test.c:14
  4. #2 0x0804843f in func (n=9) at test.c:14
  5. #3 0x0804843f in func (n=10) at test.c:14
  6. #4 0x08048469 in main () at test.c:22
如上所示,可以很清楚地看到遞歸深入到了第幾層,以及該層局部變量值的情況。
---------------------------------------------------------------------------------
三,gdb使用手冊上有一塊專門說如何查看堆棧,翻譯后的文檔如下:
 gdb查看堆棧.rar   
----------------------------------------------------------------------------------
 

 

打印一個類的成員

ptype obj/class/struct
查看obj/class/struct的成員,但是會把基類指針指向的派生類識別為基類
 

set print object on

這個選項可以看到派生對象的真實類名,雖然ptype也可以打印出對象
 
set print pretty on
以樹形打印對象的成員,可以清晰展示繼承關系,設置為off時對象較大時會顯示“一坨”
 
如調試mysql Item類的派生類對象時會這樣顯示:
 
 
set print vtbl on
用比較規整的格式來顯示虛函數表
 
推薦設置這兩個:
set print object on
set print pretty on

 

 gdb中查看字符串,地址的操作,數據類型
比始有一個int型的變量i,相要知道他的相關信息,可以
(gdb) print i
打印出變量i的當前值
(gdb)x &i
與上面的命令等價。

如果有x命令看時,需要看一片內存區域,(如果某個地方的值為0,用x時會自動截斷了)
(gdb) x/16bx address
單字節16進制打印address地址處的長度為16的空間的內存,16表示空間長度,不是16進制,x表示16進制,b表示byte單字節

gdb看變量是哪個數據類型 
(gdb) whatis i (whatis和ptype類似)
即可知道i是什么類型的變量


gdb調試中,對於動態創建的數組,比如 int * x;  這個時候不能像靜態數組那樣通過p x 就可以打印出整個數組。如果想要打印出整個數組,可以通過創建一個人工數組來解決這個問題。其一般形式為:

 

    *pointer@number_of_elements

 

gdb還允許在適當的時候使用類型強制轉換,比如:

 

    (gdb) p (int[25]*x

 

gdb的ptype命令可以很方便的快速瀏覽類或結構體的結構。

 

    (gdb) info locals命令得到當前棧中所有局部變量的值列表。

 

print和display的高級選項

 

    p /x y 會以十六進制的格式顯示變量,而不是十進制的形式。其它常用的格式為c表示字符,s表示字符串,f表示浮點。

 

可以臨時禁用某個顯示項。例如

 

    (gdb) dis disp 1

 

查看條目好,命令是

 

    (gdb) info disp

 

重新啟用條目,命令是

 

    (gdb) enable disp 1

 

完全刪除顯示的條目,命令是

 

    (gdb) undisp 1

 

在gdb中設置變量

 

在單步調試程序的中間使用調試器設置變量的值是非常有用的。命令是

 

    (gdb) set x = 12           ( 前提是x必須在程序中已經聲明了)

 

set var a=3  

 

 

可以通過gdb的set args 命令設置程序的命令行參數。

 

設置“方便變量”,命令是

 

    (gdb) set $q = p  

 

 

 

用來記錄特定節點的歷史,這個變量q不會去改變自己的地址。

9.GDB環境變量

    你可以在GDB的調試環境中定義自己的變量,用來保存一些調試程序中的運行數據。要定義一個GDB的變量很簡單只需。使用GDB的set命令。GDB的環境變量和UNIX一樣,也是以$起頭。如:
    
    set $foo = *object_ptr
    
    使用環境變量時,GDB會在你第一次使用時創建這個變量,而在以后的使用中,則直接對其賦值。環境變量沒有類型,你可以給環境變量定義任一的類型。包括結構體和數組。
    
    show convenience 
        該命令查看當前所設置的所有的環境變量。
        
    這是一個比較強大的功能,環境變量和程序變量的交互使用,將使得程序調試更為靈活便捷。例如:
    
        set $i = 0
        print bar[$i++]->contents
    
    於是,當你就不必,print bar[0]->contents, print bar[1]->contents地輸入命令了。輸入這樣的命令后,只用敲回車,重復執行上一條語句,環境變量會自動累加,從而完成逐個輸出的功能。

 

在調用gdb時,可以指定“啟動文件”。例如:

 

    gdb –command=z x

 

表示要在可執行文件x上運行gdb,首先要從文件z中讀取命令。   

 

    (gdb) jump 34

 

程序直接跳到34行。

 

使用strace命令,可以跟蹤系統做過的所有系統調用。

 

 

 

進程和線程的主要區別是:與進程一樣,雖然每個線程有自己的局部變量,但是多線程環境中父程序的全局變量被所有線程共享,並作為在線程之間通信的主要方法。

 

可以通過命令 ps axH 來查看系統上當前的所有進程和線程。

 

 

使用 ptype 檢查類型

ptype 命令可能是我最喜愛的命令。它告訴你一個 C 語言表達式的類型。

(gdb) ptype i

type = int
(gdb) ptype &i
type = int *
(gdb) ptype main
type = int (void)

C 語言中的類型可以變得很復雜,但是好在 ptype 允許你交互式地查看他們。

 

 

調試已運行的程序

兩種方法: 
  (1)在UNIX下用ps查看正在運行的程序的PID(進程ID),然后用gdb PID格式掛接正在運行的程序。 
  (2)先用gdb 關聯上源代碼,並進行gdb,在gdb中用attach命令來掛接進程的PID。並用detach來取消掛接的進程。

  detach:

   當你調試結束之后,可以使用該命令斷開進程與gdb的連接(結束gdb對進程的控制),在這個命令執行之后,你所調試的那個進程將繼續運行;

 

 

內存查看命令

可以使用examine命令(簡寫是x)來查看內存地址中的值。x命令的語法如下所示:

x/<n/f/u> <addr>

其中,n是一個正整數,表示需要顯示的內存單元的個數。

           f 表示顯示的格式:x 按十六進制格式顯示變量

                                 d 按十進制格式顯示變量

                                 u 按十六進制格式顯示無符號整型

                                 o 按八進制格式顯示變量

                                 t 按二進制格式顯示變量

                                 c 按字符格式顯示變量

                                 f 按浮點數格式顯示變量

u 表示從當前地址往后請求的字節數,如果不指定的話,GDB默認是4個bytes。u參數可以用下面的字符來代替,b表示單字節,h表示雙字節,w表示四字 節,g表示八字節。當我們指定了字節長度后,GDB會從指內存定的內存地址開始,讀寫指定字節,並把其當作一個值取出來。

命令:x/3uh 0x54320 表示,從內存地址0x54320讀取內容,h表示以雙字節為一個單位,3表示輸出三個單位,u表示按十六進制顯示。

 

 http://blog.jobbole.com/87482/

 https://www.cnblogs.com/muhe221/articles/4846680.html

 

 

watch

watch [-l|-location] expr [thread threadnum] [mask maskvalue]

     -l 與 mask沒有仔細研究,thread threadnum 是在多線程的程序中限定只有被線程號是threadnum的線程修改值后進入斷點。

經常用到的如下命令:

     watch <expr>

     為表達式(變量)expr設置一個觀察點。變量量表達式值有變化時,馬上停住程序。

 

     表達式可以是一個變量

     例如:watch value_a

 

     表達式可以是一個地址:

     例如:watch *(int *)0x12345678 可以檢測4個字節的內存是否變化。

 

     表達式可以是一個復雜的語句表達式:

     例如:watch a*b + c/d

 

watch 在有些操作系統支持硬件觀測點,硬件觀測點的運行速度比軟件觀測點的快。如果系統支持硬件觀測的話,當設置觀測點是會打印如下信息:

     Hardware watchpoint num: expr

watch兩個變種 rwatch,awatch,這兩個命令只支持硬件觀測點如果系統不支持硬件觀測點會答應出不支持這兩個命令的信息

 

rwatch <expr>

    當表達式(變量)expr被讀時,停住程序。

       

    awatch <expr>

    當表達式(變量)的值被讀或被寫時,停住程序。

   

    info watchpoints

    列出當前所設置了的所有觀察點。

 

     watch 所設置的斷點也可以用控制斷點的命令來控制。如 disable、enable、delete等。 

 

1:定位某變量/內存地址 何時被修改
a為待觀察的變量
gdb> watch *(long*)a
gdb> watch *(long*)(a+4)
gdb> watch *(long*)(a+8)
2:查看數組的值。
編程時:array[i]
用GDB查看時,用 p array+i即可。
3:善於使用$
 
http://blog.chinaunix.net/uid-20801390-id-3236840.html
 

GDB內存斷點(Memory break)的使用舉例

 

本文是一篇使用GDB設置內存斷點的例子。

 

1. 源程序

 

文件名:testMemBreak.c

#include <stdio.h>

#include <string.h>

 

int main()

{

    int i,j;

    char buf[256]={0};

    char* pp = buf;

 

    printf("buf addr= 0x%x/r/n",buf);

    for(i=0;i<16;i++)

    {

        printf("addr = 0x%x ~ 0x%x/r/n",pp+i*16,pp+i*16+15);

        for(j=0;j<16;j++)

            *(pp+i*16+j)=i*16+j;

    }

 

    printf("ASCII table:/n");

    for(i=0;i<16;i++)

    {

        for(j=0;j<16;j++)

            printf("%c  ", *(pp+i*16+j));

        printf("/n");

    }

   

    return 0;

 

}

 

即完成ASCII碼的初始化並打印出來。

 

要使用的GDB命令,如下。

(gdb) help watch

Set a watchpoint for an expression.

A watchpoint stops execution of your program whenever the value of

an expression changes.

(gdb) help rwatch

Set a read watchpoint for an expression.

A watchpoint stops execution of your program whenever the value of

an expression is read.

(gdb) help awatch

Set a watchpoint for an expression.

A watchpoint stops execution of your program whenever the value of

an expression is either read or written.

(gdb)

 

2. 調試過程

 

2.1 設置斷點並啟動程序完成初始化

 

啟動程序對其進行初始化,並使用display自動顯示buf的地址。

 

$ gcc -g -o membreak testMemBreak.c

 

$ gdb ./membreak.exe

GNU gdb 6.3.50_2004-12-28-cvs (cygwin-special)

Copyright 2004 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 "i686-pc-cygwin"...

(gdb) l

1       #include <stdio.h>

2       #include <string.h>

3

4       int main()

5       {

6           int i,j;

7           char buf[256]={0};

8           char* pp = buf;

9

10          printf("buf addr= 0x%x/r/n",buf);

(gdb)

11          for(i=0;i<16;i++)

12          {

13              printf("addr = 0x%x ~ 0x%x/r/n",pp+i*16,pp+i*16+15);

14              for(j=0;j<16;j++)

15                  *(pp+i*16+j)=i*16+j;

16          }

17

18          printf("ASCII table:/n");

19          for(i=0;i<16;i++)

20          {

(gdb)

21              for(j=0;j<16;j++)

22                  printf("%c  ", *(pp+i*16+j));

23              printf("/n");

24          }

25

26          return 0;

27      }

(gdb) b 10

Breakpoint 1 at 0x4010ae: file testMemBreak.c, line 10.

(gdb) r

Starting program: /cygdrive/e/study/programming/linux/2009-12-28 testMemBreak/membreak.exe

 

Breakpoint 1, main () at testMemBreak.c:10

10          printf("buf addr= 0x%x/r/n",buf);

(gdb) step

buf addr= 0x22cb70

11          for(i=0;i<16;i++)

(gdb) p buf

$1 = '/0' <repeats 255 times>

(gdb) p &buf

$2 = (char (*)[256]) 0x22cb70

(gdb) display &buf

1: &buf = (char (*)[256]) 0x22cb70

(gdb) p/x *0x22cb70@64

$3 = {0x0 <repeats 64 times>}

(gdb) x/64w 0x22cb70

0x22cb70:       0x00000000      0x00000000      0x00000000      0x00000000

0x22cb80:       0x00000000      0x00000000      0x00000000      0x00000000

0x22cb90:       0x00000000      0x00000000      0x00000000      0x00000000

0x22cba0:       0x00000000      0x00000000      0x00000000      0x00000000

0x22cbb0:       0x00000000      0x00000000      0x00000000      0x00000000

0x22cbc0:       0x00000000      0x00000000      0x00000000      0x00000000

0x22cbd0:       0x00000000      0x00000000      0x00000000      0x00000000

0x22cbe0:       0x00000000      0x00000000      0x00000000      0x00000000

0x22cbf0:       0x00000000      0x00000000      0x00000000      0x00000000

0x22cc00:       0x00000000      0x00000000      0x00000000      0x00000000

0x22cc10:       0x00000000      0x00000000      0x00000000      0x00000000

0x22cc20:       0x00000000      0x00000000      0x00000000      0x00000000

0x22cc30:       0x00000000      0x00000000      0x00000000      0x00000000

0x22cc40:       0x00000000      0x00000000      0x00000000      0x00000000

0x22cc50:       0x00000000      0x00000000      0x00000000      0x00000000

0x22cc60:       0x00000000      0x00000000      0x00000000      0x00000000

 

p/x *0x22cb70@64:以16進制方式顯示0x22cb70開始的64個雙字組成的數組,實際上就是256個字節的數組,只是默認以雙字顯示。

可以看見,buf內存塊中的所有數據被初始化為0

 

2.2 buf+80處設置內存斷點

 

設置斷點后,運行程序,使之停在對該內存寫的動作上。

(gdb) watch *(int*)0x22cbc0

Hardware watchpoint 2: *(int *) 2280384

(gdb) c

Continuing.

addr = 0x22cb70 ~ 0x22cb7f

addr = 0x22cb80 ~ 0x22cb8f

addr = 0x22cb90 ~ 0x22cb9f

addr = 0x22cba0 ~ 0x22cbaf

addr = 0x22cbb0 ~ 0x22cbbf

addr = 0x22cbc0 ~ 0x22cbcf

Hardware watchpoint 2: *(int *) 2280384

 

Old value = 0

New value = 80

main () at testMemBreak.c:14

14              for(j=0;j<16;j++)

1: &buf = (char (*)[256]) 0x22cb70

(gdb) p i

$4 = 5

(gdb) p j

$5 = 0

(gdb) info breakpoints

Num Type           Disp Enb Address    What

1   breakpoint     keep y   0x004010ae in main at testMemBreak.c:10

        breakpoint already hit 1 time

2   hw watchpoint  keep y              *(int *) 2280384

        breakpoint already hit 1 time

(gdb) delete 1

(gdb) delete 2

(gdb) b 18

Breakpoint 3 at 0x40113b: file testMemBreak.c, line 18.

(gdb) info breakpoints

Num Type           Disp Enb Address    What

3   breakpoint     keep y   0x0040113b in main at testMemBreak.c:18

(gdb) p/x *0x22cb70@64

$6 = {0x3020100, 0x7060504, 0xb0a0908, 0xf0e0d0c, 0x13121110, 0x17161514, 0x1b1a1918, 0x1f1e1d1c, 0x23222120,

  0x27262524, 0x2b2a2928, 0x2f2e2d2c, 0x33323130, 0x37363534, 0x3b3a3938, 0x3f3e3d3c, 0x43424140, 0x47464544,

  0x4b4a4948, 0x4f4e4d4c, 0x50, 0x0 <repeats 43 times>}

(gdb) x/64w 0x22cb70

0x22cb70:       0x03020100      0x07060504      0x0b0a0908      0x0f0e0d0c

0x22cb80:       0x13121110      0x17161514      0x1b1a1918      0x1f1e1d1c

0x22cb90:       0x23222120      0x27262524      0x2b2a2928      0x2f2e2d2c

0x22cba0:       0x33323130      0x37363534      0x3b3a3938      0x3f3e3d3c

0x22cbb0:       0x43424140      0x47464544      0x4b4a4948      0x4f4e4d4c

0x22cbc0:       0x00000050      0x00000000      0x00000000      0x00000000

0x22cbd0:       0x00000000      0x00000000      0x00000000      0x00000000

0x22cbe0:       0x00000000      0x00000000      0x00000000      0x00000000

0x22cbf0:       0x00000000      0x00000000      0x00000000      0x00000000

0x22cc00:       0x00000000      0x00000000      0x00000000      0x00000000

0x22cc10:       0x00000000      0x00000000      0x00000000      0x00000000

0x22cc20:       0x00000000      0x00000000      0x00000000      0x00000000

0x22cc30:       0x00000000      0x00000000      0x00000000      0x00000000

0x22cc40:       0x00000000      0x00000000      0x00000000      0x00000000

0x22cc50:       0x00000000      0x00000000      0x00000000      0x00000000

0x22cc60:       0x00000000      0x00000000      0x00000000      0x00000000

 

查看buf內存塊,可以看見,程序按我們希望的在運行,並在buf+80處停住。此時,程序正試圖向該單元即0x22cbc0寫入80

 

2.3 使用continue執行程序直至結束

 

(gdb) c

Continuing.

addr = 0x22cbd0 ~ 0x22cbdf

addr = 0x22cbe0 ~ 0x22cbef

addr = 0x22cbf0 ~ 0x22cbff

addr = 0x22cc00 ~ 0x22cc0f

addr = 0x22cc10 ~ 0x22cc1f

addr = 0x22cc20 ~ 0x22cc2f

addr = 0x22cc30 ~ 0x22cc3f

addr = 0x22cc40 ~ 0x22cc4f

addr = 0x22cc50 ~ 0x22cc5f

addr = 0x22cc60 ~ 0x22cc6f

 

Breakpoint 3, main () at testMemBreak.c:18

18          printf("ASCII table:/n");

1: &buf = (char (*)[256]) 0x22cb70

(gdb) p/x *0x22cb70@64

$7 = {0x3020100, 0x7060504, 0xb0a0908, 0xf0e0d0c, 0x13121110, 0x17161514, 0x1b1a1918, 0x1f1e1d1c, 0x23222120,

  0x27262524, 0x2b2a2928, 0x2f2e2d2c, 0x33323130, 0x37363534, 0x3b3a3938, 0x3f3e3d3c, 0x43424140, 0x47464544,

  0x4b4a4948, 0x4f4e4d4c, 0x53525150, 0x57565554, 0x5b5a5958, 0x5f5e5d5c, 0x63626160, 0x67666564, 0x6b6a6968,

  0x6f6e6d6c, 0x73727170, 0x77767574, 0x7b7a7978, 0x7f7e7d7c, 0x83828180, 0x87868584, 0x8b8a8988, 0x8f8e8d8c,

  0x93929190, 0x97969594, 0x9b9a9998, 0x9f9e9d9c, 0xa3a2a1a0, 0xa7a6a5a4, 0xabaaa9a8, 0xafaeadac, 0xb3b2b1b0,

  0xb7b6b5b4, 0xbbbab9b8, 0xbfbebdbc, 0xc3c2c1c0, 0xc7c6c5c4, 0xcbcac9c8, 0xcfcecdcc, 0xd3d2d1d0, 0xd7d6d5d4,

  0xdbdad9d8, 0xdfdedddc, 0xe3e2e1e0, 0xe7e6e5e4, 0xebeae9e8, 0xefeeedec, 0xf3f2f1f0, 0xf7f6f5f4, 0xfbfaf9f8,

  0xfffefdfc}

(gdb) x/64w 0x22cb70

0x22cb70:       0x03020100      0x07060504      0x0b0a0908      0x0f0e0d0c

0x22cb80:       0x13121110      0x17161514      0x1b1a1918      0x1f1e1d1c

0x22cb90:       0x23222120      0x27262524      0x2b2a2928      0x2f2e2d2c

0x22cba0:       0x33323130      0x37363534      0x3b3a3938      0x3f3e3d3c

0x22cbb0:       0x43424140      0x47464544      0x4b4a4948      0x4f4e4d4c

0x22cbc0:       0x53525150      0x57565554      0x5b5a5958      0x5f5e5d5c

0x22cbd0:       0x63626160      0x67666564      0x6b6a6968      0x6f6e6d6c

0x22cbe0:       0x73727170      0x77767574      0x7b7a7978      0x7f7e7d7c

0x22cbf0:       0x83828180      0x87868584      0x8b8a8988      0x8f8e8d8c

0x22cc00:       0x93929190      0x97969594      0x9b9a9998      0x9f9e9d9c

0x22cc10:       0xa3a2a1a0      0xa7a6a5a4      0xabaaa9a8      0xafaeadac

0x22cc20:       0xb3b2b1b0      0xb7b6b5b4      0xbbbab9b8      0xbfbebdbc

0x22cc30:       0xc3c2c1c0      0xc7c6c5c4      0xcbcac9c8      0xcfcecdcc

0x22cc40:       0xd3d2d1d0      0xd7d6d5d4      0xdbdad9d8      0xdfdedddc

0x22cc50:       0xe3e2e1e0      0xe7e6e5e4      0xebeae9e8      0xefeeedec

0x22cc60:       0xf3f2f1f0      0xf7f6f5f4      0xfbfaf9f8      0xfffefdfc

(gdb) c

Continuing.

ASCII table:

            

   

                            

   !  "  #  $  %  &  '  (  )  *  +  ,  -  .  /

0  1  2  3  4  5  6  7  8  9  :  ;  <  =  >  ?

@  A  B  C  D  E  F  G  H  I  J  K  L  M  N  O

P  Q  R  S  T  U  V  W  X  Y  Z  [  /  ]  ^  _

`  a  b  c  d  e  f  g  h  i  j  k  l  m  n  o

p  q  r  s  t  u  v  w  x  y  z  {  |  }  ~ 

€  ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 

 

Program exited normally.

(gdb)

 

至此,我們已經看到watch查看內存的作用了,只要被查看的單元會被修改(讀/寫),均會斷在此處,以方便我們調試程序

https://blog.csdn.net/livelylittlefish/article/details/5110234

 

 

gdb 內存斷點watch 的使用

 1.  watch 變量的類型
    a. 整形變量: int i; watch i;
    b. 指針類型:  char *p; watch p, watch *p;
    它們是有區別的.
      watch p 是查看 *(&p), 是p 變量本身。

      watch (*p) 是 p 所指的內存的內容, 查看地址,一般是我們所需要的。

      我們就是要看莫地址上的數據是怎樣變化的,雖然這個地址具體位置只有編譯器知道。

    c. watch 一個數組或內存區間
    char buf[128], watch buf,  
    是對buf 的128個數據進行了監視. 此時不是采用硬件斷點,而是軟中斷實現的。
    軟中斷方式去檢查內存變量是比較耗費cpu資源的。
    精確的指明地址是硬件中斷。

2. 當你設置的觀察點是一個局部變量時。局部變量無效后,觀察點無效
 Watchpoint 2 deleted because the program has left the block in 
  which its expression is valid. 
               
3. 附上一個簡單程序方便你利用內存斷點觀察,調試.

[cpp]  view plain  copy
 
  1. $ cat test.cpp  
  2. #include <stdio.h>  
  3. #include <string.h>  
  4. void initBuf(char *buf);  
  5. void prtBuf(char *buf);  
  6. char mem[8];  
  7. char buf[128];  
  8. int main()  
  9. {  
  10.     initBuf(buf);  
  11.     prtBuf(buf);  
  12.     return 0;  
  13. }  
  14.   
  15. void initBuf(char *pBuf)  
  16. {  
  17.     int i, j;  
  18.     mem[0]='0';  
  19.     mem[1]='1';  
  20.     mem[2]='2';  
  21.     mem[3]='3';  
  22.     mem[4]='4';  
  23.     mem[5]='5';  
  24.     mem[6]='6';  
  25.     mem[7]='7';  
  26.     //ascii table first 32 is not printable  
  27.     for(i=2;i<8;i++)  
  28.     {  
  29.         for(j=0;j<16;j++)  
  30.             pBuf[i*16+j]=i*16+j;  
  31.     }  
  32. }  
  33.   
  34. void prtBuf(char *pBuf)  
  35. {  
  36.     int i, j;  
  37.     for(i=2;i<8;i++)  
  38.     {  
  39.         for(j=0;j<16;j++)  
  40.             printf("%c  ", pBuf[i*16+j]);  
  41.         printf("\n");  
  42.     }  
  43. }  

玩弄內存調試於股掌之中。
(由於效率問題你需要適當控制內存斷點設置,當然,對這個小程序無所謂.)
----------------------------------------
看一下mem 數組, 內存數據是怎樣被寫入的。
----------------------------------------
gdb test
b main
watch mem
run
Breakpoint 1, main () at test.cpp:9
gdb) continue
  Continuing.
  Hardware watchpoint 2: mem
  Old value = "\000\000\000\000\000\000\000"
  New value = "0\000\000\000\000\000\000"
  initBuf (pBuf=0x6010a0 <buf> "") at test.cpp:18
(gdb) continue
  Continuing.
  Hardware watchpoint 2: mem
  Old value = "0\000\000\000\000\000\000"
  New value = "01\000\000\000\000\000"
  initBuf (pBuf=0x6010a0 <buf> "") at test.cpp:19
(gdb) continue
  Continuing.
  Hardware watchpoint 2: mem
  Old value = "01\000\000\000\000\000"
  New value = "012\000\000\000\000"
  initBuf (pBuf=0x6010a0 <buf> "") at test.cpp:20
(gdb) 
......

(gdb) continue
  Continuing.
  Hardware watchpoint 2: mem
  Old value = "0123456"
  New value = "01234567" 
  initBuf (pBuf=0x6010a0 <buf> "") at test.cpp:26
 
 
 

使用gdb watch調試代碼

 
 
 

本篇文章將使用一個簡單的例子說明如何使用gdb watch調試代碼。

  首先來看以下一段簡單的代碼

 

       

       

  顯然,第7行代碼是有問題,那么這個錯誤的memset會造成什么后果呢?

  我們運行以下兩個指令看下程序的輸出:

 

   g++ -g main.c -o main.o

   ./main.o

  程序的輸出是0 0 3,變量a的值因為memset被錯誤地修改了。

  因為這是個很短的程序,所以我們能很輕松地看出第7行的代碼是有問題的。這里的memset會越界,錯誤地修改了a的值。在實際情況下,當我們發現某個變量的值不符合預期時,一般的做法是先查下這個變量的引用,找到對該變量有寫操作的地方(在本例中,對變量a的寫操作只有一處,即第6行)。當我們發現所有的寫操作和邏輯都不會產生該非法值時,可以認定程序中有越界的情況。越界的情況在實際項目中是非常令人頭痛的。一是問題的根源難以定位:在本例中異常的數據是a,但其根本原因是對b操作不當造成的。二是越界之后程序的行為是未定義的,而除了回檔之外也找不到到更好的方法來還原數據。

  在開發環境下,我們可以使用gdb來輔助定位越界這一問題,這里用的是watch命令。

  我們將斷點設在第7行,並運行程序。可以分別看下a,b,c的值,可以看到這個時候三個變量的值都是正常的(實際這個時候變量c的值也是未定義的,看編譯器怎么處理)。我們再分別打印變量a和變量b的地址,可以看到a的地址比b大4 。(回憶下,一個int型變量占4個字節,而棧上的內存是從大到小分配的)。

      

 

  這個我們使用watch監控變量a值得變量,一種做法是直接watch a,我的習慣是watch地址,watch地址的方法更加具有普適性一些。

具體的指令是 watch *(int *)0x7fff84b67b08。然后我們繼續執行程序看會在哪一步停下來,

      

  程序在執行到第8行時發現watch的一段內存有變化,舊的值是1,新的值是0。這個時候我們回去看程序,就能發現是第7行這個memset錯誤地清空了a的內存空間。

 

watchpoint只能在程序啟動后設置,先在main那下個斷點,讓程序啟動后暫停在main函數處

 

gdb硬件斷點-----watch使用方法

硬件斷點使用watch監測,可以監測棧變量和堆變量值的變化,當被監測變量值發生變化時,程序被停住。

1.    棧變量
測試代碼(文件名為1.c,比較low,哈哈):

點擊(此處)折疊或打開

  1. #include <string.h>
  2. void test(void)
  3. {
  4.         int iA, iB, iC; 
  5.         iA = 1;
  6.         iB = 2;
  7.         iC = 3;
  8.         iB += iA + iC; 
  9.         printf("iB = %d\n", iB);
  10.         iB = 0;
  11.         printf("iB = %d\n", iB);
  12.         iB *= iC; 
  13.         printf("iB = %d\n", iB);
  14.         return;
  15. }
  16. void main(void)
  17. {
  18.         test();
  19.         return;
  20. }

測試過程(監測變量iB值在代碼中發生變化的位置):

點擊(此處)折疊或打開

  1. [root@ceph181 test]# vim 1.c
  2. [root@ceph181 test]# gcc -g 1.c 
  3. [root@ceph181 test]# ls
  4. 1.c a.out debug.c log.c mmap.c sscanf.c sync_fetch.c time.c unlikely.c
  5. [root@ceph181 test]# gdb a.out 
  6. GNU gdb (GDB) Red Hat Enterprise Linux (7.2-60.el6_4.1)
  7. Copyright (C) 2010 Free Software Foundation, Inc.
  8. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
  9. This is free software: you are free to change and redistribute it.
  10. There is NO WARRANTY, to the extent permitted by law. Type "show copying"
  11. and "show warranty" for details.
  12. This GDB was configured as "x86_64-redhat-linux-gnu".
  13. For bug reporting instructions, please see:
  14. <http://www.gnu.org/software/gdb/bugs/>...
  15. Reading symbols from /home/work/test/a.out...done.
  16. (gdb) b test 
  17. Breakpoint 1 at 0x4004cc: file 1.c, line 8.
  18. (gdb) r
  19. Starting program: /home/work/test/a.out 
  20. Breakpoint 1, test () at 1.c:8
  21. 8        iA = 1;
  22. Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.132.el6.x86_64
  23. (gdb) watch iB
  24. Hardware watchpoint 2: iB
  25. (gdb) c
  26. Continuing.
  27. Hardware watchpoint 2: iB
  28. Old value = 4195296
  29. New value = 2
  30. test () at 1.c:10
  31. 10        iC = 3;
  32. (gdb) c
  33. Continuing.
  34. Hardware watchpoint 2: iB
  35. Old value = 2
  36. New value = 6
  37. test () at 1.c:13
  38. 13        printf("iB = %d\n", iB);
  39. (gdb) c
  40. Continuing.
  41. iB = 6
  42. Hardware watchpoint 2: iB
  43. Old value = 6
  44. New value = 0
  45. test () at 1.c:15
  46. 15        printf("iB = %d\n", iB);
  47. (gdb) c
  48. Continuing.
  49. iB = 0
  50. iB = 0
  51. Watchpoint 2 deleted because the program has left the block in
  52. which its expression is valid.
  53. main () at 1.c:26
  54. 26        return;
  55. (gdb) c
  56. Continuing.
  57. Program exited with code 07.
  58. (gdb)

注意: 棧變量的生命周期有限,因此,使用watch監測其硬件斷點時,要注意生命周期

2.    堆變量
int *piA = malloc(sizeof(int));
監測堆變量piA值的變化時,
    1)    在gdb中打印出變量piA的地址: print  &piA,記為piB;
    2)    watch *piB
    3)    continue
如下例所示,要監測指針connection->session的值在什么時候被修改:

點擊(此處)折疊或打開

  1. Breakpoint 1, xio_connection_destroy (connection=0x7ffff0009240) at ../common/xio_connection.c:2364
  2. 2364        int            retval = 0;
  3. Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.132.el6.x86_64 libibverbs-1.1.8mlnx1-OFED.3.1.1.0.0.x86_64 libmlx4-1.0.6mlnx1-OFED.3.1.1.0.0.x86_64 libmlx5-1.0.2mlnx1-OFED.3.1.1.0.3.x86_64 libnl-1.1.4-2.el6.x86_64 librdmacm-1.0.21mlnx-OFED.3.0.1.5.2.x86_64 numactl-2.0.7-8.el6.x86_64
  4. (gdb) p connection->session
  5. $1 = (struct xio_session *) 0x6120b0
  6. (gdb) p &connection->session
  7. $2 = (struct xio_session **) 0x7ffff0009298
  8. (gdb) watch *($2)
  9. Hardware watchpoint 2: *($2)
  10. (gdb) disable 1
  11. (gdb) c
  12. Continuing.
  13. Hardware watchpoint 2: *($2)
  14. Old value = (struct xio_session *) 0x6120b0
  15. New value = (struct xio_session *) 0x0
  16. 0x0000003917805e94 in rdma_get_cm_event () from /usr/lib64/librdmacm.so.1
  17. (gdb)

 

 gdb 里設置臨時變量

使用 set 命令。

(gdb) set $i="hello"
(gdb) ptype $i 
type = char [6]
(gdb) set $i=1
(gdb) ptype $i
type = int
(gdb) set $i=(char)1
(gdb) ptype $i
type = char
(gdb) set $i=(short)1
(gdb) ptype $i
type = short

 

set設置臨時變量加$符號。

(1)修改變量值:

a. printv=value: 修改變量值的同時,把修改后的值顯示出來

b. set [var]v=value: 修改變量值,需要注意如果變量名與GDB中某個set命令中的關鍵字一樣的話,前面加上var關鍵字

 不加$表示修改原有的變量

 

打印指針內容:

p *poin

 

 

gdb 斷點設置(二)watch

2、watch

     watch [-l|-location] expr [thread threadnum] [mask maskvalue]

     -l 與 mask沒有仔細研究,thread threadnum 是在多線程的程序中限定只有被線程號是threadnum的線程修改值后進入斷點。

     經常用到的如下命令:

     watch <expr>

     為表達式(變量)expr設置一個觀察點。變量量表達式值有變化時,馬上停住程序。

 

     表達式可以是一個變量

     例如:watch value_a

 

     表達式可以是一個地址:

     例如:watch *(int *)0x12345678 可以檢測4個字節的內存是否變化。

 

     表達式可以是一個復雜的語句表達式:

     例如:watch a*b + c/d

 

     watch 在有些操作系統支持硬件觀測點,硬件觀測點的運行速度比軟件觀測點的快。如果系統支持硬件觀測的話,當設置觀測點是會打印如下信息:

     Hardware watchpoint num: expr

    如果不想用硬件觀測點的話可如下設置:

    set can-use-hw-watchpoints

 

    watch兩個變種 rwatch,awatch,這兩個命令只支持硬件觀測點如果系統不支持硬件觀測點會答應出不支持這兩個命令的信息:,

 

    rwatch <expr>

    當表達式(變量)expr被讀時,停住程序。

       

    awatch <expr>

    當表達式(變量)的值被讀或被寫時,停住程序。

   

    info watchpoints

    列出當前所設置了的所有觀察點。

 

     watch 所設置的斷點也可以用控制斷點的命令來控制。如 disable、enable、delete等。 

 

     可以為停止點設定運行命令

 

     commands [bnum]

    ... command-list ...

    end

    為斷點號bnum指寫一個命令列表。當程序被該斷點停住時,gdb會依次運行命令列表中的命令。

    例如:

 

        break foo if x>0

        commands

        printf "x is %d/n",x

        continue

        end

    斷點設置在函數foo中,斷點條件是x>0,如果程序被斷住后,也就是,一旦x的值在foo函數中大於0,GDB會自動打印出x的值,並繼續運行程序。 

   注意:watch 設置也是斷點,如果調試的時候設置的斷點(任何種類的斷點)過多的時候,watch斷點會被忽略,有時候沒有任何提示,

            這是我在測試的時候發現的,只有把多余的斷點刪除后才可用。

 
 

display +表達式  display a  用於顯示表達式的值,每當程序運行到斷點處都會顯示表達式的值 

 

 

 

1.Segment Fault

Segment Fault一般是由於訪問非法指針或者訪問空指針造成的,而且發生了這類錯誤之后,程序將不能再繼續執行,必須重新啟動整個系統才可以解決問題,因此此類問題后果十分嚴重,如何定位這樣的問題也一直是一個難題。如果使用debug版本,可以使用gdb巧妙的定位出這樣的問題。具體的定位方法如下述:

如果使用gdb啟動的情況下,一般發生segment fault,程序便停在出錯的地方,

 

這是可以使用bt將調用棧打印出來,

 

接着可以使用info local 命令將當前所有的局部變量的值打印出來

 

通過變量的值可以看出xx的指針的值已經不正確,由此可以斷定是由於這個非法訪問這個指針造成的。如果這個指針是由上層調用棧傳入的,可以使用f n  n代表使用bt打印出的調用棧的第幾層,可以在其他的調用層看看哪層開始指針不正確。

2. 死循環問題

當程序處於死循環狀態時, 可以按下ctrl+c, 是程序停下,然后運行命令 thread applly all bt ,這樣可以打印出所有線程的當前的調用棧, 再按c, 讓程序繼續運行,過幾秒鍾, 再運用thread applly all bt , 再次打印出所有線程的當前的調用棧, 然后通過比較工具比較這兩個調用棧, 如果某個線程的調用棧時刻在改變並且在循環中,那么這個線程可能處於死循環狀態。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM