gdb各類調試命令和技巧

陳皓:用GDB調試程序css

GDB概述
————
html

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

通常來講,GDB主要幫忙你完成下面四個方面的功能:mysql

    一、啓動你的程序,能夠按照你的自定義的要求爲所欲爲的運行程序。
    二、可以讓被調試的程序在你所指定的調置的斷點處停住。(斷點能夠是條件表達式)
    三、當程序被停住時,能夠檢查此時你的程序中所發生的事。
    四、動態的改變你程序的執行環境。
linux

從上面看來,GDB和通常的調試工具沒有什麼兩樣,基本上也是完成這些功能,不過在細節上,你會發現GDB這個調試工具的強大,你們可能比較習慣了圖形化的調試工具,但有時候,命令行的調試工具卻有着圖形化工具所不能完成的功能。讓咱們一一看來。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 }
redis

 

編譯生成執行文件:(Linux下)
    hchen/test> cc -g tst.c -o tst
算法

 

使用GDB調試:sql

 

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的方法有如下幾種:

 

    一、gdb <program> 
       program也就是你的執行文件,通常在固然目錄下。

 

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

 

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

2、斷點設置

gdb斷點分類:

以設置斷點的命令分類:

breakpoint

能夠根據行號、函數、條件生成斷點。

watchpoint

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

catchpoint

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

gdb中的變量從1開始標號,不一樣的斷點採用變量標號同一管理,能夠 用enable、disable等命令管理,同時支持斷點範圍的操做,好比有些命令接受斷點範圍做爲參數。

例如:disable 5-8

 

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

3、源文件
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...]使能一次,觸發後刪除

 

 

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

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

三、工做目錄。
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





由於暫停的代碼有必定的隨機性,能夠多運行幾回,看看每次停留的語句有什麼不一樣。後面執行run命令的時候能夠不用再輸入命令行參數(「12 5」),GDB會記住。還有,再執行run的時候GDB會問是否重頭開始執行程序,固然咱們要從頭開始執行。



    在使用gdb調試時,常常要用到查看堆棧信息,特別是在內核調試時,這(gdb) show args Argument list to give program being debugged when it is started is "5". (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

二、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 , 再次打印出全部線程的當前的調用棧, 而後經過比較工具比較這兩個調用棧, 若是某個線程的調用棧時刻在改變而且在循環中,那麼這個線程可能處於死循環狀態。

相關文章
相關標籤/搜索