GDB调试使用详解
參考:C語言gdb調(diào)試之精髓(常用命令、多進(jìn)程、多線程、程序日志)
作者:C語言技術(shù)網(wǎng)
發(fā)布時(shí)間: 2020-07-15 17:13:30
網(wǎng)址:https://www.bilibili.com/video/BV1ei4y1V758?from=search&seid=4037367131025904962
以及:http://www.freecplus.net/b72113dda88a43b48728e0552fd8a74c.html
參考:Linux的調(diào)試器–GDB等
作者:一只青木呀
發(fā)布時(shí)間: 2020-07-14 15:31:01
網(wǎng)址:https://blog.csdn.net/weixin_45309916/article/details/107338210
目錄
- Linux的調(diào)試器
- 概念
- 安裝GDB增強(qiáng)工具 (gef)
- 簡單的安裝方法(我的Linux是Ubuntu18.04)
- 嘗試使用gdb進(jìn)行小程序的調(diào)試
- GDB的命令
- GDB的基本命令
- GDB的簡單使用
- 0.編譯hello.c
- 1.啟動GDB
- 2.下斷點(diǎn)
- 3.讓程序運(yùn)行起來
- 4.此時(shí)需要往下運(yùn)行
- 5.查看運(yùn)行中程序的變量
- 6.運(yùn)行時(shí)程序的打印會在最上面顯示
- 7.退出gdb
- 8.補(bǔ)充一點(diǎn)(設(shè)置參數(shù))
- GDB多進(jìn)程調(diào)試
- 1.編譯
- 2.啟動gdb
- 3.下斷點(diǎn)(我們以man函數(shù)為例)
- 4.運(yùn)行
- 5.選擇是走子進(jìn)程還是父進(jìn)程
- GDB多線程調(diào)試
- 1.編譯
- 2.開始調(diào)試
- 3.選擇線程
Linux的調(diào)試器
概念
程序員寫在編寫程序的時(shí)候不可能是一帆風(fēng)順的,gcc編譯器可以發(fā)現(xiàn)程序代碼的語法錯(cuò)誤,但不能發(fā)現(xiàn)程序的業(yè)務(wù)邏輯錯(cuò)誤,調(diào)試程序是軟件開發(fā)的內(nèi)容之一。調(diào)試程序的方法有很多種,例如可以用printf語句跟蹤程序的運(yùn)行步驟和顯示變量的值,本章節(jié)介紹一個(gè)功能強(qiáng)大的調(diào)試工具gdb。
- 基于命令行的調(diào)試方法
- 所有的調(diào)試都是可以進(jìn)行腳本編寫的
- 能夠調(diào)試所有架構(gòu)的代碼
- 有三種調(diào)試方法供大家選擇
- GDB支持遠(yuǎn)程調(diào)試,支持與IDA進(jìn)行聯(lián)調(diào)
安裝GDB增強(qiáng)工具 (gef)
一般來說,gdb啟動起來就是這樣的樣子
- gdb的一直都非常強(qiáng)大,但是每一步調(diào)試,可能有一些要查看的信息,如果每一步都要手動輸入命令,未免有點(diǎn)麻煩,所以就出現(xiàn)了插件,把某一些經(jīng)常要查看的信息每一步都自動幫你顯示出來,方便調(diào)試
簡單的安裝方法(我的Linux是Ubuntu18.04)
1、切換到用戶權(quán)限
2、切換到用戶家目錄 比如/home/qingmu
3、保持網(wǎng)絡(luò)通暢,然后只要輸入下面的命令 就行
克隆完之后,會在你當(dāng)前目錄下面有GdbPlugins 這個(gè)文件,并且里面會有這幾個(gè)文件
- 這樣就算ok了,里面三個(gè)插件,當(dāng)你想要用某一個(gè)插件的時(shí)候,只要輸入對應(yīng)命令就行
- 這里我們主要使用gef 我們執(zhí)行
然后我們啟動gdb就行了
至此gef插件安裝完畢
嘗試使用gdb進(jìn)行小程序的調(diào)試
gcc -g的選項(xiàng) gdb gdb-test 一些GDB的命令GDB的命令
啟動方法
本地普通啟動 gdb 本地段錯(cuò)誤文件啟動 gdb core attch方式啟動 gdb 遠(yuǎn)程啟動 gdbserver 0.0.0.0:1234 /path/to/file啟動選項(xiàng)
–symbols < file > -s < file > 從指定文件中讀取符號表 -se < file > 從指定文件中讀取符號表信息,并把它們用在可執(zhí)行文件中 –core < file > 調(diào)試時(shí)core dump的core文件 –directory < directory> -d < directory> 加入一個(gè)源文件的搜索路徑。默認(rèn)搜索路徑是環(huán)境變量中PATH所定義的路徑 詳細(xì)的開關(guān)可以使用gdb --help基本命令
set listsize 設(shè)置調(diào)試中可以查看的行數(shù) set args 10 20 30 40 50 設(shè)置程序所需要的參數(shù) path show paths save breakpoint name.bp gdb elf -x name.bp print § 查看運(yùn)行數(shù)據(jù) print *array@10 查看數(shù)組 print file::variable 查看file文件下的variable x/n、f、u 查看內(nèi)存 n 是一個(gè)正整數(shù),表示顯示內(nèi)存的長度,也就是說從當(dāng)前地址向后顯示幾個(gè)地址的內(nèi)容。 f 表示顯示的格式,跟print 的格式參數(shù)相同 u 表示從當(dāng)前地址往后請求的字節(jié)數(shù),如果不指定的話,GDB默認(rèn)是4個(gè)bytes。u參數(shù)可以用下面的字符來代替,b表示單字節(jié),h表示雙字節(jié),w表示四字節(jié),g表示八字節(jié)。當(dāng)我們指定了字節(jié)長度后,GDB會從指內(nèi)存定的內(nèi)存地址開始,讀寫指定字節(jié),并把其當(dāng)作一個(gè)值取出來。GDB的基本命令
Linux程序發(fā)布流程
確定程序是否存在符號表
readelf -s test-1
生成符號表
objcopy --only-keep-debug test-1 test-1.symbol
生成發(fā)布程序
objcopy --strip-debug test-1 test-release
使用符號表進(jìn)行程序debug
gdb -q --symbol=test-1.symbol --exec=test-release
GDB中暫停/恢復(fù)程序運(yùn)行
斷點(diǎn)
條件斷點(diǎn)
break if
info breakpoints
delete
disable
enable
觀察點(diǎn)
watch 地址
info watchpoints
rwatch
捕捉點(diǎn)
catch event
throw 拋出一個(gè)C++的異常 catch throw
catch 捕捉一個(gè)C++的異常 catch catch
exec 調(diào)用系統(tǒng)調(diào)用exev時(shí)停止 catch exec
fork 調(diào)用系統(tǒng)調(diào)用fork時(shí)停止 catch fork
load/load libname 載入動態(tài)鏈接庫時(shí) catch load / catch load libname
暫停命令
commands bnum
.
.
.
.
end
GDB的簡單使用
0.編譯hello.c
gcc -g hello.c -o hello注意:編譯的時(shí)候一定要加上 -g 選項(xiàng),使之加入符號表,否賊調(diào)試的時(shí)候看不到程序的源代碼。為了更方便的使用gdb 一定要安裝gdb插件gef
以hello.c為例
1.啟動GDB
gdb hello //hello 是hello.c的可執(zhí)行文件2.下斷點(diǎn)
也就是程序運(yùn)行到哪,我們以man函數(shù)為例
也可以指定在哪一行下斷點(diǎn)。
b
info breakpoints 可查看下的斷點(diǎn)
3.讓程序運(yùn)行起來
r
此時(shí)程序就運(yùn)行起來了
4.此時(shí)需要往下運(yùn)行
" n " :執(zhí)行一條語句,碰到函數(shù)會直接運(yùn)行函數(shù)
" s ":執(zhí)行下一條語句,碰到函數(shù)會進(jìn)入到函數(shù)中
5.查看運(yùn)行中程序的變量
" p "
6.運(yùn)行時(shí)程序的打印會在最上面顯示
7.退出gdb
q8.補(bǔ)充一點(diǎn)(設(shè)置參數(shù))
如果程序需要傳入?yún)?shù)那么 “set args” 在啟動gdb之后就可以設(shè)置參數(shù)
例如我需要傳遞兩個(gè)參數(shù):
中間用空格隔開就可以了
GDB多進(jìn)程調(diào)試
下面以process.c為例
#include<stdio.h> #include<stdio.h> #include<unistd.h> #include<sys/types.h> #include<sys/wait.h>int main() {pid_t pid = fork();//創(chuàng)建子進(jìn)程if(pid == -1){perror("fork error");return -1;}else if(pid == 0)//child{printf("i am a child:my pid is %d,my father is %d\n",getpid(),getppid());}else//father{printf("i am a father:my pid is %d\n",getpid());wait(NULL);//等待子進(jìn)程}return 0;}1.編譯
gcc -g process.c -o process //一定要加-g 把符號表載入代碼2.啟動gdb
gdb process3.下斷點(diǎn)(我們以man函數(shù)為例)
b main4.運(yùn)行
r5.選擇是走子進(jìn)程還是父進(jìn)程
fork()函數(shù)
返回值為0:子進(jìn)程
返回值大于0:父進(jìn)程
返回值小于0:創(chuàng)建失敗
函數(shù)運(yùn)行是他自己是走父進(jìn)程還是子進(jìn)程是不確定的,所以我們需要讓他按照自己的想法運(yùn)行,這是我們就按自己的需要是走父進(jìn)程還是子進(jìn)程
跟著父進(jìn)程去運(yùn)行
set follow-fork-mode parent跟著子進(jìn)程運(yùn)行
set follow-fork-mode child設(shè)置之后我們就n往下運(yùn)行就可以進(jìn)入子進(jìn)程
GDB多線程調(diào)試
下面以thread.c為例
#include<stdio.h> #include<pthread.h>void* thread1(void* arg) {printf("i am thread1,my tid is %u\n",pthread_self());int i=0;int result=0;for(;i<100;i++){result += i;}printf("thread1 result = %d\n",result);return NULL; }void* thread2(void* arg) {printf("i am thread2,my tid is %u\n",pthread_self());int i=0;int result=0;for(;i<100;i++){result ^= i;}printf("thread2 result = %d\n",result);return NULL; }int main() {pthread_t tid1,tid2;pthread_create(&tid1,NULL,thread1,NULL);//創(chuàng)建線程1pthread_create(&tid2,NULL,thread2,NULL);//創(chuàng)建線程2pthread_join(tid1,NULL);//等待線程1pthread_join(tid2,NULL);//等待線程2 return 0; }1.編譯
gcc -g thread.c -o thread -lpthread //線程庫不是內(nèi)核自帶的 要自己加上2.開始調(diào)試
gdb thread3.選擇線程
線程比較簡單 想看哪個(gè)線程就把斷點(diǎn)下到哪個(gè)線程的函數(shù)上
跟線程1運(yùn)行
b thread1跟線程2運(yùn)行
b thread2這里我們跟線程1運(yùn)行
這里我們就跟進(jìn)了線程1了
可以通過:"info threads"命令來查看正在運(yùn)行程序中線程信息
其余的就是基本操作了
p 打印想看的數(shù)據(jù)
x查看對應(yīng)的內(nèi)存等等
總結(jié)
- 上一篇: android146 360 病毒查杀
- 下一篇: cupp字典生成工具(同类工具还有cru