遠程調試
有時候需要調試的程序並不在本機上,GDB是支持遠程調試的,具體命令如下:
(gdb) target remote IP:PORT
這時,本機的GDB客戶端可以遠程連接到被調試機器上,對程序進行調試。當然,前提是在遠端的機器上,必須要啓動好GDB服務端程序。
運行程序
如果是用GDB把程序加載起來,而不是attach到一個已經運行的進程上的話,默認情況下程序是不會運行的,可以通過以下命令將程序執行起來:
(gdb) run [ARGS]
在命令的後面可以跟隨發送給程序的參數,參數可以包含Shell通配符(*,[…]等),和輸入輸出重定向符(<,>,>>)。如果你使用不帶任何參數的run命令,GDB就再次使用你上次執行run命令時給的參數。
如果想查看當前默認的參數是什麼,可以使用這條命令:
(gdb) show args
如果想修改當前的默認參數,可以使用以下的命令:
(gdb) set args ARGS
修改過後,再次不帶參數執行run命令時,就會使用新的默認參數。當然,如果接着執行的run命令帶了參數,則新帶的參數會覆蓋當前的默認參數。
run命令會使程序一直運行,直到碰到第一個斷點纔會停止下來。而很多情況下,調試的時候會想執行到 main函數先暫停一下,這種情況下,我們可以使用start 命令:
(gdb) start [ARGS]
使用方法和run命令完全相同。
程序一旦運行起來以後,可以通過下面的命令查看當前程序運行的狀態:
(gdb)info program
這條命令將告訴你,現在程序的狀態是什麼,停在什麼位置,因爲什麼原因停下來的(斷點、單步等),是不是已經異常退出了,退出的原因是什麼。
添加斷點
斷點有兩種,一種是永久斷點,就是設置了之後,除非顯式的將其刪除,否則一直存在;還有一種是臨時斷點,就是一旦斷在那個斷點之後,該斷點會自動刪除,說白了就是隻能斷一次的斷點。可以通過break命令設置永久斷點,而tbreak命令設置臨時斷點。
break和tbreak的命令格式完全一樣,這裏只介紹break命令,具體使用方法如下:
(gdb) break LOCATION [CONDITION]
斷點的位置可以是本地代碼的行號(LineNumber):
(gdb) break 10 #在本地代碼的第10行設置斷點
也可以是指定代碼源文件中代碼的行號(FileName:LineNumber):
(gdb) break main.c:20 #在main.c源文件中的第20行代碼設置斷點
也可以是本地代碼中的某一個函數(FunctionName):
(gdb) break pcap_parse #在本地代碼的pcap_parse函數處設置斷點
也可以是指定代碼源文件中的一個函數(FileName:FunctionName):
(gdb) break tcpdump.c:main #在tcpdump.c源文件中的 main函數處設置斷點
也可以是某一個指令的地址
(gdb) break *0x12345678 #在地址爲0x12345678的指令處設置斷點
注意,在對某一個地址的指令下斷點時前面要加“*”。
另外,break命令是可以指定觸發條件的,即只有當執行到斷點指定指令,且條件表達式滿足時斷點才生效。例如:
(gdb) break 50 if size>0 #只有在size變量大於0的情況下斷點才起效
而如果要刪除指定編號斷點的觸發條件可以使用condition命令:
(gdb) condition NUMBER
例如:
(gdb) condition 1 #刪除斷點編號爲1上的觸發條件。
注意,這條命令只會刪除斷點上的觸發條件,並不會刪除斷點本身。
有時候希望給一批函數加斷點,這些函數名都有一些共同的特點,這時候可以使用rbreak命令:
(gdb) rbreak REGEXP
這個命令的參數是一個正則表達式,作用是給代碼中所有函數名匹配這個正則表達式的函數添加斷點,例如:
(gdb) rbreak pcap_* #給所有函數名以pcap_打頭的函數添加斷點
添加監視點
監視點(Watch Point),顧名思義,其用途一般是觀察一個變量、某個內存地址處存放的數值或者是一個表達式的值,是否被程序讀取或修改了。可以使用如下命令設置程序中的監視點:
(gdb) watch [EXPR]
表示設置一個監視點,當所指定的表達式EXPR的值被修改了,則程序會停止。
(gdb) rwatch [EXPR]
表示設置一個監視點,當所指定的表達式EXPR的值被讀取了,則程序會停止。
(gdb) awatch [EXPR]
表示設置一個監視點,當所指定的表達式EXPR的值被讀取或修改了,都會讓程序停止。
所謂表達式,含義非常豐富,可以是一個具體的變量:
(gdb) watch i #當變量i被修改了停止程序
也可以是一個內存地址處存放的值:
(gdb) watch *(int *)0x12345678 #當存放在內存地址處的int值被修改了停止程序
還有可能是一個複雜的表達式:
(gdb) watch x+ y #當表達式的值改變時停止程序
注意,如果當前跑的代碼已經超出了要監視變量的作用域範圍,則那個對應的監視點會被自動刪除掉。
添加捕捉點:
GDB除了可以根據代碼的位置以及某個變量的改變爲條件使得代碼執行中斷外,還有一個非常強大的功能,就是當某一個特別的事件發生的時候,也可以捕捉到,並暫停程序。具體來說,可以使用如下的命令:
(gdb) catch [EVENT]
上面這條命令的意思是,當對應的EVENT事件發生時,停住程序。EVENT可以是下面的內容:
1)throw:當程序拋出一個異常時;
2)catch:當程序捕獲到一個異常時;
3)exec:當程序調用exec時;
4)fork:當程序調用fork時;
5)vfork:當程序調用vfork時;
6)syscall:當程序調用系統調用(system calls)時。
查看斷點/監視點/捕捉點
想查看目前在程序中已經設置了哪些斷點(這裏的斷點是廣義的概念,包含普通意義上的斷點、監視點或捕捉點),可以通過下面的命令:
(gdb) info breakpoints
輸出大致如下:
共有六個域:
1) Num:表示這個斷點的編號,可以用這個編號號對某個斷點做單獨的操作,如刪除、禁用等;
2) Type:可以是普通意義上的斷點(breakpoint)、監視點(watchpoint)或捕捉點(catchpoint);
3) Disp:表示該斷點在斷下來一次後怎麼處理,是繼續保持(keep),還是刪除(del);
4) Enb:表示該斷點是否是打開的,如果打開的就是“y”,如果被關閉了就是“n”;
5) Address:斷點在內存中的位置,也就是要斷在的那條指令的地址;
6) What:如果是普通意義上的斷點的話,則這裏顯示斷點所在代碼的位置,如果調試的程序不是debug版本的,則這項顯示不出來;如果是監視點的話,則顯示要監視的表達式;如果是捕捉點的話,則顯示要捕捉的事件。
前面的命令會顯示當前設置的所有斷點、監視點和捕捉點。如果只想看監視點的話,可以用下面的命令:
(gdb)info watchpoints
刪除斷點/監視點/捕捉點
可以刪除已經添加到程序中的斷點(這裏的斷點是廣義的概念,包含普通意義上的斷點、監視點或捕捉點),命令如下:
(gdb) delete [NUMBER]
如果不接任何參數,則表示刪除所有程序中的斷點。如果想刪除某一個特定的斷點,只需要加上相應的編號。例如:
(gdb) delete #刪除所有的斷點
(gdb) delete 2 #刪除斷點號爲2的斷點
除了按斷點號來刪除斷點之外,還可以刪除指定位置上的斷點,命令如下:
(gdb) clear [LOCATION]
如果不加任何參數的話,表示刪除當前執行代碼位置處的所有斷點。當然,還可以帶上一個位置作爲參數,這樣的話,就會清除在指定位置上存在的所有斷點。代碼位置可以是行號、函數名、內存地址等(和在break命令中參數中所謂的代碼位置定義相同)。
禁用/打開斷點/監視點/捕捉點
在調試的時候,有時想禁用某些斷點(同樣這裏的斷點是廣義的概念,包含普通意義上的斷點、監視點或捕捉點),但又不想刪除,因爲以後可能這些斷點還要用,如果刪除了以後還得再加上,比較麻煩。這時候可以使用GDB中禁用斷點的命令:
(gdb) disable [NUMBER]
如果不加任何參數,則表示禁用程序中所有設置的斷點。如果只是想禁用某個斷點,只需要加上斷點號。例如:
(gdb) disable #禁用所有的斷點
(gdb) disable 3 #禁用斷點號爲3的斷點
如果想打開前面被禁用的斷點,可以用下面的命令:
(gdb) enable [NUMBER]
用法和禁用斷點相同。
查看寄存器的值
GDB支持查看被調試進程當前常用寄存器的值,命令如下:
(gdb) info registers[REGISTER]
如果不加任何參數,則顯示所有寄存器的值。如果只想看某個寄存器的值,可以在參數中指定。例如:
(gdb) info registers#查看所有寄存器當前的值
(gdb) info registers pc #只查看PC寄存器當前的值
前面的命令只會顯示常用的寄存器值,而如果想查看所有寄存器的值(包括浮點寄存器),可以使用如下的命令:
(gdb) info all-registers
同樣可以使用print命令來訪問寄存器的情況,只需要在寄存器名字前加一個$符號就可以了:
(gdb) print $REGISTER
例如:
(gdb) print $pc #同樣查看PC寄存器當前的值
print是一個非常強大的命令,有很多種用法,後面還會提到。
修改寄存器的值
在調試的過程中,有時候會修改被調試程序的寄存器的值,從而達到改變程序流程的目的。這時候可以使用如下命令:
(gdb) set $REGISTER=VALUE
注意,在要修改的寄存器前面,要加上“$”。例如:
(gdb) set $r0 =1 #將r0寄存器的值修改成1
查看內存的值
GDB支持查看指定地址處內存中存放的值,具體的命令如下:
(gdb) x[/FMT] ADDRESS
FMT是可選的參數,主要用來告訴GDB以什麼格式解析內存中的值以及顯示多少數據,其共由三部分組成:
1) 要顯示的數據量,具體顯示多少位要看後面指定的長度數據
2) 顯示的格式,共有以下幾種情況:
a) o(octal):8進制數
b) x(hex):16進制數
c) d(decimal):10進制數
d) t(binary):2進制數
e) f(float):浮點數
f) i(instruction):指令
g) c(char):單個字符
h) s(string):字符串
3) 數據單元的長度,共有以下幾種情況:
a) b(byte):以1個字節爲長度顯示數據
b) h(halfword):以半字,也就是2個字節爲長度顯示數據
c) w(word):以字,也就是4個字節爲長度顯示數據
d) g(giant):以8個字節爲長度顯示數據
下面舉一些例子:
(gdb) x /xw 0x80040000 #以16進制顯示指定地址處的數據
(gdb) x /8s 0x86468700 #顯示指定地址處開始的8個字符串
(gdb) x /50i main #顯示main函數開頭的50條指令
修改內存的值
除了查看某個內存地址所存的值外,GDB還支持直接修改內存的值,命令格式如下:
(gdb) set *ADDRESS=VALUE
關於地址部分,可以輔助指定一下其存值的類型,從而方便賦值,例如:
(gdb) set *0xb6d2a908=0 #將該地址指向的字節設置爲0
(gdb) set *(int *)0xb6daaaec=15 #將該地址指向的整數設置爲15
(gdb) set *(int**)0x8048a548=0x55aa55aa #將該地址指向的地址設置爲新值
查看調用堆棧
如果想在程序暫停時,顯示當前函數調用的棧幀信息,可以使用backtrace命令:
(gdb) backtrace [full] [NUMBER]
如果命令不帶任何參數的話,則默認顯示所有棧幀的層編號和位置等信息。如果帶有參數full的話,則還將顯示棧幀內存放的所有局部變量以及函數返回值信息。如果帶有大於0的數字NUMBER的話,則表示只顯示開頭的NUMBER個棧幀;而如果這個數字小於0的話,則表示只顯示最後NUMBER個棧幀。注意,這裏的NUMBER指的是顯示幾個棧幀,並不是指棧幀的層編號(所以不可能是0)。例如:
(gdb) backtrace 1 #只顯示最上面的1個棧幀的概要信息
(gdb) backtrace full -2 #只顯示最後的2個棧幀的詳細信息
此外,與backtrace等效的命令還有where和info stack:
(gdb) where [full] [NUMBER]
(gdb) info stack [full] [NUMBER]
它們的用法與backtrace完全一樣,顯示結果也一樣。
backtrace命令只會顯示棧幀的信息,並不會改變當前選中的棧幀。不切換的情況下,默認當前棧幀指的是當前執行函數使用的棧幀。如果要切換當前棧幀,可以使用下面的命令:
(gdb) frame [NUMBER]
如果不加任何參數的話,會顯示當前選中棧幀的信息。如果帶一個數字參數NUMBER的話(從0開始),則表示選中那個層編號爲NUMBER的棧幀(修改當前棧幀爲這個棧幀)。
同時,也可以相對當前棧幀向上移動:
(gdb) up [NUMBER]
如果不接參數,表示相對當前棧幀,向上移動一層。如果帶了NUMBER參數,則表示相對當前棧幀,向上移動NUMBER層。
還可以向下移動:
(gdb) down [NUMBER]
例如,假設當前棧幀處於第1層,則:
(gdb) up #當前棧幀處於第2層
(gdb) down 2 #當前棧幀處於第0層,即最上層
簽名的命令,在每次切換棧幀的時候都會打印出移動到的棧幀的概要信息。如果你不想讓其打出信息,可以使用以下三個對應的命令:
(gdb) select-frame [NUMBER] #對應於 frame 命令
(gdb) up-silently [NUMBER] #對應於 up 命令
(gdb) down-silently [NUMBER] #對應於 down 命令
還有一點,前面提到使用backtrace full命令可以查看棧幀的詳細信息,但前提是必須要有被調試程序的符號表信息。如果沒有符號表信息(比如要調試一個release版的程序),肯定是沒法知道棧中保存的當前函數用到的局部變量到底有哪些,但是理論上任然可以得到比如局部變量、入參、函數返回地址等在棧中存放的地址。爲此,GDB提供瞭如下的命令:
(gdb) info frame [NUMBER]
這條命令在沒有符號表信息的情況下,任然可以儘可能多的獲得棧中的信息。如果不加參數,就顯示當前選中棧的信息,加上一個NUMBER參數就顯示那個層編號爲NUMBER的棧幀的信息。
另外,還有一個命令,如果想查看當前棧幀中的異常處理器,也就是顯示當前函數中的異常處理信息,可以使用下面的命令:
(gdb) info catch
不要搞錯了,這個命令不是用來顯示捕捉點信息的。
查看變量的值
在GDB中,可以隨時查看程序中以下三種變量的值:
1)全局變量(所有文件可見的);
2)靜態全局變量(當前文件可見的);
3)局部變量(當前函數作用域中可見的,存放在當前函數的棧幀中)。
可以通過print命令查看某個具體變量的值:
(gdb) print VAR
參數就是你要顯示的變量名。如果你需要查看的變量是一個數組,存儲在一段連續的內存空間中,則可以使用GDB的“@”操作符,其左邊是第一個內存的地址的值,右邊是想查看內存的長度。具體命令格式如下:
(gdb) print *VAR@LEN
這條命令將顯示數組變量VAR中前LEN個元素的值。
如果想顯示當前棧幀中包含的所有局部變量的值,可以使用如下命令:
(gdb) info locals
可以通過前面介紹的frame命令查看當前棧幀的信息。info locals命令只能顯示局部變量的值,如果想看全局變量的值,只能使用print命令。
而如果想查看函數入參變量的值,可以使用下面的命令:
(gdb) info args
這條命令會查找存放於當前棧幀中的入口參數,並顯示出來。注意,不要和前面的show args命令混淆了。
如果想看某個變量在代碼中被定義的類型,可以用如下命令:
(gdb) whatis VAR
自動顯示
調試的時候進場碰到這種情況,每次單步執行完成之後都想查看某一個變量或內存位置當前存放的值,每次再print太麻煩了。這時候,可以使用display命令,從而當程序停住或在單步跟蹤時自動顯示指定變量的值。命令格式如下:
(gdb) display[/FMT] EXPR
FMT是可選參數,主要用來告訴GDB以什麼格式解析內存中的值以及顯示多少數據,其定義和前面介紹過的x命令中的相同,這裏不再贅述。
EXPR是一個表達式,
當你用display設定好了一個或多個表達式後,只要你的程序被停下來,GDB就會自動幫你顯示所設置的這些表達式當前的值。
如果想看當前設置了哪些要自動顯示的表達式,可以用下面的命令:
(gdb) info display
該命令將顯示三部分信息:
1) Num:對應該自動顯示錶達式的編號(後面可以用這個編號來禁用或者刪除這個自動顯示錶達式);
2) Enb:表示這個自動顯示錶達式當前是有效的(y),還是禁用的(n);
3) Expression:具體要自動顯示的表達式。
如果想刪除自動顯示錶達式,可以用下面的命令:
(gdb) undisplay [NUMBER]
(gdb) delete display [NUMBER]
上面的兩條命令是完全等效的。如果不帶任何參數的話,默認會刪除所有的自動顯示錶達式。而如果帶一個NUMBER作爲參數,表示只刪除編號爲NUMBER的那個自動顯示錶達式。
如果不想刪除自動錶達式,只是想先將其禁用,可以用下面的命令:
(gdb) disable display [NUMBER]
如果不帶任何參數的話,將默認禁用所有的自動顯示錶達式。如果帶一個NUMBER作爲參數的話,表示只禁用編號爲NUMBER的那個自動顯示錶達式。
將自動顯示錶達式禁用後,其就暫時失效了,但是任然可以通過info display命令查到它,如果將其刪除了就查不到了。暫時禁用後,後面還可以將其恢復,命令如下:
(gdb) enable display [NUMBER]
同樣,如果不帶任何參數的話,將默認恢復所有的自動顯示錶達式。如果帶一個NUMBER作爲參數的話,表示只恢復編號爲NUMBER的那個自動顯示錶達式。
調試控制
程序一旦斷到了你設置的斷點位置,除了查看程序當前的狀態之外,最想做的應該還是按照你想要的順序執行程序,邊執行邊分析。
首先,我們來看看如何單步執行,GDB中提供了四個相關的命令,先來看其中兩個:
(gdb) next
(gdb) step
這兩條命令都是單步執行源代碼中的一行代碼。它們的區別是,如果下一個單步執行的代碼是一個函數調用的話,next命令不會執行到該被調用函數的函數內部,會把函數調用當成一條普通的語句來處理(相當於Step Over);而step命令則相反,會進入到被調用函數的內部繼續執行(相當於Step Into)。所以,如果下一個單步執行的代碼不是函數調用的話,這兩個命令就沒有任何差別。
前面的兩個命令都是執行源代碼中的一行。而如果要逐條執行彙編指令(在沒有被調試程序源碼的情況下尤爲有用),可以使用下面兩條命令:
(gdb) nexti
(gdb) stepi
這兩條命令只會執行下一條彙編/機器指令。i代表instruction,即彙編/機器指令,同一條源碼可能被編譯成多條機器指令。它們兩的區別同樣在對函數調用指令的處理上,nexti不會進入被調用函數,而stepi會進入被調用函數。
除了前面的單步執行外,也可以指定代碼執行到某個位置就停止。
如果當前代碼停在某個函數內部,同時想讓程序一直執行到當前函數結束返回,即跳出當前函數回到調用該函數的代碼位置,可以使用如下命令:
(gdb) finish
也可以讓代碼從當前停止的位置一直繼續運行,直到到達指定的位置結束,命令如下:
(gdb) until [LOCATION]
如果不加任何參數的話,則表示當程序執行的代碼位置超過了當前代碼位置(也就是敲until命令時的代碼位置)的時候,代碼將停止運行。這個命令有一個特別有用處的地方,如果現在你的代碼正處於一個循環當中,這時你想讓程序一直運行直到這個循環結束。當然可以一直不停的敲next命令直到循環結束,但要是循環1萬次呢,那不累死了。這時,可以用next命令將當前的代碼執行位置移動到循環的最末端,然後敲入until命令,無論要循環執行多少遍都沒有問題。
until命令也可以跟一個代碼位置作爲參數,代碼位置可以是行號、函數名、內存地址等(和在break命令中參數中所謂的代碼位置定義相同)。程序將一直運行,直到指定的那個位置。
使用until命令時,還有一點需要特別注意,如果程序退出當前調用棧時,程序也會停止。比如,如果在執行完until命令後,但是還沒到指定的位置,這時函數返回了(return),則GDB也會在返回的代碼處停下來。但這隻針對退出當前調用棧的情況,對於調用棧增加的情況(如調用了另一個函數,或者是遞歸調用自己),並不會停止,而是一直執行。
調試時還可以使用 continue 命令繼續運行程序。程序會在遇到斷點後再次暫停運行。如果沒有遇到斷點,就會一直運行到結束,命令如下:
(gdb) continue [NUMBER]
如果沒有任何參數,則表示遇到下一個斷點就停止程序。而如果在命令中指定了參數NUMBER,則表示可以忽略NUMBER次斷點。例如:
(gdb) continue 5 #遇到5個斷點不停止,直到第6次遇到斷點
如果你的調試斷點在某個函數中,並還有語句沒有執行完。你可以使用以下命令強制函數忽略還沒有執行的語句並返回:
(gdb) return [EXPR]
使用return命令取消當前函數的執行,並立即返回,如果指定了表達式EXPR,那麼該表達式的值會被當做函數的返回值。例如:
(gdb) return 0 #退出當前函數並返回0
執行完return命令後,會退出當前執行函數的調用棧,回到上一級調用棧。
一般來說,被調試程序會按照程序代碼的運行順序依次執行。GDB提供了打亂執行順序的功能,也就是說GDB可以讓程序執行隨意跳躍。這個功能由GDB的jump命令來完:
(gdb) jump LOCATION
該命令會帶一個參數,即要跳轉到的代碼位置,可以是源代碼的行號:
(gdb) jump 555 #跳轉到源代碼的第555行的位置
可以是相對當前代碼位置的偏移量:
(gdb) jump +10 #跳轉到距當前代碼下10行的位置
也可以是代碼所處的內存地址:
(gdb) jump *0x12345678 #跳轉到位於該地址的代碼處
注意,在內存地址前面要加“*”。還有,jump命令不會改變當前程序調用棧的內容,所以當你從一個函數跳到另一個函數時,當函數運行完返回進行退棧操作時就會發生錯誤,因此最好還是在同一個函數中進行跳轉。
當然,GDB也可以強制調用一個指定的函數:
(gdb) call FUNCTION(PARAMTERS…)
參數是要調用的函數名以及調用該函數所需要帶的參數。運行完命令後,該指定函數會被執行,並顯示函數的返回值。如果函數返回值是void,那麼就不顯示。注意,即使被調用的函數不需要任何參數,也要加上括號(“()”)。
同樣,print命令也可以用來強制調用一個指定函數:
(gdb) print FUNCTION(PARAMTERS…)
兩者的差別非常小,如果函數返回void,call命令不顯示,但是print命令會顯示,並把該值存入歷史數據中。
查看源代碼
GDB可以打印出所調試程序的源代碼。當然,在程序編譯時一定要加上–g的參數,把源程序信息編譯到執行文件中,不然就看不到源程序了。當程序停下來以後,GDB會報告程序停在了哪個源文件的第幾行代碼上。
你可以用list命令來打印程序的源代碼,其有很多種格式,下面一一解釋:
(gdb) list
如果不帶任何參數的話,該命令會接着打印上次list命令打印出代碼後面的代碼。如果是第一次執行list命令則會顯示當前正在執行代碼位置附近的代碼。
(gdb) list -
如果參數是一個減號的話,則和前面剛好相反,會打印上次list命令打印出代碼前面的代碼。
(gdb) list LOATION
list命令還可以帶一個代碼位置作爲參數,顧名思義,這樣的話就會打印出該代碼位置附近的代碼。這個代碼位置的定義和在break命令中定義的相同,可以是一個行號:
(gdb) list 100 #列出當前代碼文件中第100行附近代碼
(gdb) list tcpdump.c:450 #列出tcpdump.c文件中第450行附近代碼
也可以是一個函數名:
(gdb) list main #列出當前代碼文件中main函數附近代碼
(gdb) list inet.c:pcap_lookupdev #列出inet.c代碼文件中指定函數附近代碼
list命令還可以指定要顯示代碼的具體範圍:
(gdb) list FIRST,LAST
這裏FIRST和LAST都是具體的代碼位置,此時該命令將顯示FIRST到LAST之間的代碼。可以不指定FIRST或者LAST參數,這樣的話就將顯示LAST之前或者FIRST之後的代碼。注意,即使只指定一個參數也要帶逗號,否則就編程前面的命令,顯示代碼位置附近的代碼了。
list命令默認只會打印出10行源代碼,如果覺得不夠,可以使用如下命令修改:
(gdb) set listsize COUNT
這樣的話,下次list命令就會顯示COUNT行源代碼了。如果想查看這個參數當前被設置成多少,可以使用如下命令:
(gdb) show listsize
還有一個非常有用的命令,如果你想看程序中一共定義了哪些函數,可以使用下面的命令:
(gdb) info functions
這個命令會顯示程序中所有函數的名詞,參數格式,返回值類型以及函數處於哪個代碼文件中。
查看彙編指令
如果被調試程序沒有包含源碼的信息,那麼也可以查看當前程序的彙編代碼。其命令如下:
(gdb) disassemble START,END
(gdb) disassemble START,+LENGTH
有兩種格式,第一種指定要查看代碼的地址範圍,從START開始到END結束;第二種是指定從START開始的LENGTH條彙編代碼。
另外,如果想源代碼和彙編代碼一起看的話,可以在命令後跟一個/m參數。
如果想單步執行之後,看到下一步要執行的彙編指令,可以用下面的命令:
(gdb) set disassemble-next-line on
關於ARM,還有一點值得注意的。大家知道,ARM處理器支持兩種指令集,一種就是ARM,還有一種是Thumb,那麼在顯示彙編代碼時,GDB將怎麼解釋二進制代碼呢?到底解釋成ARM還是Thumb呢?如果有符號表,那麼GDB就使用符號表來決定指令是 ARM指令還是Thumb指令。如果被調試程序沒有符號表的話,可以使用如下命令指定:
(gdb) set arm fallback-mode MODE
作爲參數的模式MODE有三種:
1) arm:將二進制代碼用ARM指令解釋;
2) thumb:將二進制代碼用Thumb指令解釋;
3) auto:讓GDB使用當前的執行模式(從CPSR寄存器的T位得到)。
而如果想看當前使用的是那種解釋模式,可以用下面的命令:
(gdb) show arm fallback-mode
退出
如果想退出GDB調試,只需要鍵入以下命令:
(gdb) quit
命令簡寫
前面大致把GDB常用的命令介紹了一下,其本身的功能還是非常強大的,覆蓋了調試需要的所有的功能。但是,和GUI的調試工具相比,每操作一步都需要敲入命令。如果每條命令都全部敲入的話,調試起來會非常麻煩。因此GDB支持命令的簡寫形式。
簡寫的規則非常的簡單,只使用足以區別其它命令的前幾個字符來執行命令就行了。如果你鍵入的命令不足以唯一定位一條命令,也就是有歧義,則GDB會給出提示。例如:
(gdb) info breakpoints
(gdb) i b #不會衝突
上面兩條命令是完全等價的。但也有衝突的情況:
(gdb) info watchpoints
(gdb) info warranty
(gdb) i w #會衝突
(gdb) i wat #不會衝突
可以看出來,單敲入命令i w,GDB是無法知道你到底是想執行info watchpoints命令還是info warranty命令的。如果再打幾個字母,消除歧義就沒問題了。
下面的表格列出了常用命令的簡寫形式:
全命令 |
簡寫命令 |
break |
b |
list |
l |
info |
i |
backtrace |
bt |
frame |
f |
continue |
c |
next |
n |
step |
s |
nexti |
ni |
stepi |
si |
delete |
d |
|
p |