陳皓:用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) step5. 執行一行程序,若此時程序是在 for/while/do loop 循環的最後一行,則一直執行到循環結束後的第一行程序後中止 (until 指令可簡寫爲 u)。
(gdb) until6. 執行現行程序到回到上一層程序爲止。
(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找不到源碼。用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
詳細查看list中的元素信息
腳本文件以下:把下列內容拷貝到一份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恢復全部保存的斷點,以下:
該命令能夠改變一個變量的值。
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 顯示當前的所在目錄。
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; }
碼就不分析了,稍微花點時間應該就能明白。你能發現幾個錯誤?
使用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。
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
第二種方法:爲程序傳遞參數5
(gdb) run 5
第三種方法:爲程序傳遞參數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
查看指定給程序的參數經過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)
set print object 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 就能夠打印出整個數組。若是想要打印出整個數組,能夠經過建立一我的工數組來解決這個問題。其通常形式爲:
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 命令多是我最喜好的命令。它告訴你一個 C 語言表達式的類型。
(gdb) ptype i
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.htmlGDB內存斷點(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 的使用