Linux下我們編譯好的代碼,無法直接調試
gcc/g++默認的工作模式是realse模式
程序要調試的話,必須是debug模式,也就是說編譯的時候要加-g選項
gdb攜帶調試信息的exe
我們現在在文件夾里面創建一個文件lesson11
里面創建一個累加的代碼,從1到100
1 #include <stdio.h>2 int Sum(int s,int e)3 {4 int result=0;5 int i=s;6 for(;i<=e;i++) 7 {8 result+=i;9 10 }11 return result;12 }13 int main()14 {15 int start=1;16 int end=100;17 printf("I will begin\n");18 int n=Sum(start,end);19 printf("running done,resu;t is:[%d-%d]=%d\n",start,end,n);20 return 0;21 }
還有一個Makefile文件,主要是生成可調試的文件,使用-g選項進行操作
?? buffers 1 mycode:mycode.c2 gcc -o $@ $^ -g3 .PHONY:clean4 clean:5 rm -f mycode
~
如果我們想讓文件是debug模式可以進行調試的話,那么我們可以在后面加上-g的選項
那么我們就可以對這個文件進行gdb調試操作了
如果我們要退出的話我們輸入這個quit
就行了
如果我們想顯示我們的源代碼的話我們可以直接輸入list
就行了,簡寫成l
就行了
但是我們想顯示我們的文件里面的內容的話
我們直接輸入命令l 1
就行了,直接從第一行開始進行查看的操作
然后直接輸入回車的話就直接將所有的代碼都顯示出來的
如果我們想在這個19行打上斷點的話,我們直接輸入b 19就行了
如果我們想讓程序直接跑完的話,我們直接輸入命令c就行了,程序就可以直接跑完了
最后不想使用gdb了,我們直接輸入quit
就能直接退出了
推薦一個cgdb,這個可以動態呈現我們的代碼
我們默認是沒有安裝的
我們可以輸入命令sudo yum install -y cgdb
就行了
我們進入到cgdb模式,上面是代碼,下面是我們的debug調試的操作
我們可以輸入l mycode.c :1查看我們的mycode.c文件從我們的第一行開始進行查看操作
我們的這里代碼片段左邊有個箭頭指向我們的16行
我們直接輸入run
,簡化就是r
然后就能直接運行結束
所以我們運行r之前我們是需要提前將斷點打上去
斷點就是程序運行到某處停下來
我們使用b filename :line
b后面主要接的是行號
這個就是打斷點的操作了
我們這里打完斷點后,對應的行左邊的行號字體顏色都變成紅色了
如果我們想要查看我們打的斷點的話
我們輸入命令info b
就能進行斷點的查看操作了
Num就是斷點的編號、
Type就是斷點的類型
Address就是我們將斷點達到哪個地址處
what就是描述我們打的是什么斷點,斷點的相關信息
我們只要打了斷點的話,我們運行run的話我們就直接在斷點處停下來了
如果我們要刪除斷點的話,我們是使用d進行斷點刪除的操作的
但是d后面不能是行號
只能是斷點的行號來進行刪除操作的
我們這里將編號為2的斷點進行刪除的操作,輸入d 2
那么此時我們對應的2號斷點就刪除了
總結:b 行號 創建斷點
d 斷點編號 刪除對應的斷點info b 查看所有的斷點的信息r 運行程序
我們如果不退出我們的cgdb的話,我們的斷點編號是依次進行線性遞增的
我們之前在vs中的f10是逐過程,f11是逐語句
假設現在我們運行到了斷點的地方了,現在我們想直接跑完Sum函數
我們直接輸入next
,簡單點就是n
,我們可以逐過程進行操作
這個時候我們一直輸入命令n
直到我們到了return 0那里,沒運行return 0
但是現在我們想進行重新進行調試,我們直接輸入r
就行了
系統會詢問我們是否要重新進行調試
那么我們又回到了我們一開始打斷點的地方了
我們現在想要運行我們下一行里面的函數了
我們可以輸入命令step
簡化就是s
了
我們輸入s
就能進入到函數內部了
這個就是逐語句了,那么我們這里19行直接進入到了Sum里面了
我們如果想在gdb中逐語句的話,我們輸入了一個s
,我們進到了函數內部
但是我們不想一直輸入s
了
我們可以輸入回車就行了
因為在gdb中我們的回車會記錄最近的一條指令
這個時候我們如果不想玩了,我們直接輸入r
然后y
就重新進入到了我們一開始的調試位置了
我們輸入命令cgdb mycode
進行可執行程序的調整,而不是這個源文件
下面我們就進入到了我們調試的頁面了
我們現在在第20行打斷點,輸入命令b 20
那么我們輸入info b
可以查看我們剛剛打的斷點
斷點的本質其實是幫助我們在特定的位置處停下來,將代碼進行切塊,進行局部性追蹤
那么我們這里輸入了r
之后我們的代碼就能在20行的位置停下來
那么下面我們就可以逐語句(s)和逐過程(n)了
如果我們想進入到函數內的話逐語句,那么我們輸入s就行了
我們可以輸入命令bt
進行函數棧幀的查看操作,可以查看調用棧
現在的話我們是在函數內部了
但是我們想直接將函數結束掉,不想單步的移動了,我想讓這個函數跳轉到運行結束處
我們直接輸入命令finish
就能跳出函數了
那么我們結束了Sum函數,我們又回到了20行
那么就是說明現在系統進行的是將我們的Sum的返回值復制給n了
我們使用命令p n
進行變量n打印的操作
我們發現呈現在我們面前的是一個隨機值,因為我們的n僅僅是開辟出來了
我們必須再往下面接著走一步
我們的n就讓寄存器放到內存里,那么我們的n就拿到了對應的結果了
我們給函數名打斷點就是給函數入口處打斷點
在我們的vs中斷點是可以刪除和禁用的
那么就說明我們的斷點是可以進行打開和關閉的,我們的斷點是可以禁用的
現在我們不想刪除斷點,我們想將斷點使能掉給禁用
默認我們的斷點的Enb=y
那么這一列就表示的是所有斷點是否被使能
我們現在要對17行的斷點進行使呢能的操作
那么我們輸入命令disable 4
后面必須接的是我們的斷點的需要不是行號
然后我們就可以發現我們的4號斷點的Enb就變成了n了
那么我們輸入命令r
我們就知道到第5個斷點了
4號斷點雖然是存在的,但是已經被我們給忽略了,相當于是禁用的
如果我們想我們禁用掉的斷點重新起作用的話
我們可以輸入命令enable 4
我們就可以讓我們禁用的4號斷點重新起作用了
調試的本質是什么 ?
1.找到問題
2.查看代碼上下文
我們這里有幾個斷點,一開始的話我們是從20號斷點開始的,我們輸入c
,就是continue
的意思
直接讓我們的程序從這個斷點運行到下一個斷點了
那么我們這里因為我們的20-25這一塊的代碼沒出問題
所以我們的代碼并沒有出任何的問題
所以斷點的本質就是對我們的代碼進行塊級別劃分,以我們塊為單位快速定位出現問題的區域
finish
可以確定問題是否在函數內,直接運行完函數
假如我們現在的調試過程一直在循環之后,我們想跳出這個for循環
那么我們可以輸入命令until 12
我們直接跳到我們的12行代碼處
until
局部區域快速執行
就是直接將我們的循環跑完了,然后就跳轉到我們指定的行
我們利用display
獲取我們的變量的數據,依次進行獲取
然后我們然后使用n
命令,后面一直回車n
進行調試
我們發現我們可以查看我們當時每一個display
的數據
所以我們的display
就是查看我們的上下文的數據
我們發現我們每次display
的話他會多出一個編號的
如果我們不想看到哪一個數據的話我們可以輸入命令undisplay 對應的編號
然后我們輸入命令n
的時候我們被刪除的常顯示的數據就不會顯示出來了
我們可以使用info locals
查看我們當前函數內所有的臨時變量
以下是整理后的 GDB 命令分類及示例,以表格形式展示:
類別 | 命令 | 作用 | 示例 |
---|---|---|---|
代碼查看與導航 | list/l | 顯示源代碼(每次10行) | list/l 10 |
list/l 函數名 | 列出指定函數的源代碼 | list/l main | |
list/l 文件名:行號 | 列出指定文件的某行代碼 | list/l mycmd.c:1 | |
程序運行與調試 | run/r | 從程序開始連續執行 | run |
next/n | 單步執行,不進入函數內部 | next | |
step/s | 單步執行,進入函數內部 | step | |
finish | 執行到當前函數返回,然后停止 | finish | |
continue/c | 從當前位置開始連續執行程序 | continue | |
until 行號 | 執行到指定行號 | until 20 | |
斷點管理 | break/b [文件名:]行號 | 在指定行號設置斷點 | break 10 或 break test.c:10 |
break/b 函數名 | 在函數開頭設置斷點 | break main | |
info break/b | 查看當前所有斷點的信息 | info break | |
delete/d breakpoints | 刪除所有斷點 | delete breakpoints | |
delete/d breakpoints n | 刪除編號為 n 的斷點 | delete breakpoints 1 | |
disable breakpoints | 禁用所有斷點 | disable breakpoints | |
enable breakpoints | 啟用所有斷點 | enable breakpoints | |
變量與表達式 | print/p 表達式 | 打印表達式的值 | print start+end |
p 變量名 | 打印指定變量的值 | p x | |
set var 變量=值 | 修改變量的值 | set var i=10 | |
display 變量名 | 跟蹤顯示指定變量值(每次停止時) | display x | |
undisplay 編號 | 取消指定編號的變量顯示 | undisplay 1 | |
調試信息 | backtrace/bt | 查看當前執行棧的各級函數調用及其參數 | backtrace |
info/i breakpoints | 查看當前設置的斷點列表 | info breakpoints | |
info/i locals | 查看當前幀的局部變量值 | info locals | |
退出調試 | quit | 退出 GDB 調試器 | quit |
此表格分類明確,便于快速查詢和理解。如需補充或調整,請隨時告知!
常見的技巧
watch 監視某一變量
執行監視一個表達式(如變量)的值,如果監視的表達式在程序運行期間的值發生變化,GDB會暫停程序的執行,并通知使用者
我們現在想看某個變量是否變化,變化的話就告訴我
我們使用命令watch result
給我們的result打一個硬件斷點,當我們的result發生變化的時候我們可以知道
而且我們使用info b
可以發現我們多了一個類型為hw watchpoint
的斷點,就是給result監控的
只要我們的這個result發生變換了我們都會第一時間被系統通知到的
新的值和舊的值
set var確定問題原因
下面我們確定問題是出在flag上面的
那么我們使用set var flag=1
在不修改源代碼的情況下對我們的flag進行重新賦值的操作
便于我們這里的檢驗
然后發現確實是flag的問題
改完我們的flag我們的結果就是符合預期的
條件斷點
現在我們想在循環中直接查看當我們的i是14的時候我們的result的結果是多少
那么我們可以使用我們的條件斷點進行判斷操作
輸入命令b 13 if i == 10
我們對13行進行打斷點,當我們的i=10的時候
此時info b
我們可以發現多了一個條件斷點
我們直接一個c
回車,我們可以發現我們當前的i就是等于10了
c
就是直接跳轉到下一個斷點
這種斷點我們照樣是可以使用我們的d 2
進行條件斷點的刪除的操作
除此之外,我們還可以使用condition
直接給我們已經設置好了的斷點添加條件
下面我們使用命令condition 4 i=10
給4號斷點設置一個條件 ,條件是i=10
直接利用condition
給我們已經存在的斷點設置條件
Cgdb是分屏操作的
上屏是代碼,下屏是調試命令窗口
我們默認是在調試命令窗口屏的
我們可以按下ESC回到我們的代碼屏
輸入i回到我們的調試窗口屏