天天看點

gdb list多行_GDB基本指令

指令

描述

backtrace(或bt)

檢視各級函數調用及參數

finish

連續運作到目前函數傳回為止,然後停下來等待指令

frame(或f)幀編号

選擇棧幀

info(或i)

locals

檢視目前棧幀局部變量的值

list(或l)

列出源代碼,接着上次的位置往下列,每次列10行

list 行号

列出從第幾行開始的源代碼

list 函數名

列出某個函數的源代碼

next(或n)

執行下一行語句

print(或p)

列印表達式的值,通過表達式可以修改變量的值或者調用函數

quit(或q)

退出gdb調試環境

set var

修改變量的值

start

開始執行程式,停在main函數第一行語句前面等待指令

step(或s)

執行下一行語句,如果有函數調用則進入到函數中

break(或b) 行号

在某一行設定斷點

break

函數名

在某個函數開頭設定斷點

break ... if

...

設定條件斷點

continue(或c)

從目前位置開始連續運作程式

delete

breakpoints

删除斷點

display

變量名

跟蹤檢視某個變量,每次停下來都顯示它的值

disable

breakpoints

禁用斷點

enable

斷點号

啟用斷點

info(或i)breakpoints

檢視目前設定了哪些斷點

run(或r)

從頭開始連續運作程式

undisplay

跟蹤顯示号

取消跟蹤顯示

watch

設定觀察點

info(或i)

watchpoints

檢視目前設定了哪些觀察點

x

從某個位置開始列印存儲單元的内容,全部當成位元組來看,

而不區分哪個位元組屬于哪個變量

backtrace(bt) 顯示程式中的目前位置和目前位置的棧跟蹤(同where)

breakpoint(b) 在程式中設定一個斷點

cd 改變目前工作目錄

clear 删除剛才停止處的斷點

commands 命中斷點時,列出将要執行的指令

continue(c) 從斷點開始繼續執行

delete(d) 删除一個斷點或監測點;也可與其他指令一起使用

display 程式停止時顯示變量和表達時

down 下移棧幀,使得另一個函數成為目前函數

frame 選擇下一條continue指令的幀

info(i) 顯示與該程式有關的各種資訊

jump 在源程式中的另一點開始運作

kill(k) 異常終止在gdb 控制下運作的程式

list(l) 列出相應于正在執行的程式的原檔案内容

next(n) 執行下一個源程式行,進而執行其整體中的一個函數

print(p) 顯示變量或表達式的值

pwd 顯示目前工作目錄

ptype 顯示一個資料結構(如一個結構或C++類)的内容

quit(q) 退出gdb

reverse-search 在源檔案中反向搜尋正規表達式

run(r) 執行該程式

search 在源檔案中搜尋正規表達式

set var=x 給變量指派x

signal 将一個信号發送到正在運作的程序

step(s) 執行下一個源程式行,必要時進入下一個函數

thread(t) 切換到指定線程,t tid

undisplay display指令的反指令,不要顯示表達式

until 結束目前循環

up 上移棧幀,使另一函數成為目前函數

watch(w) 在程式中設定一個監測點(即資料斷點)

whatis 顯示變量或函數類型

source 定位源代碼路徑,用法source src-path

help(h)顯示目前幫助

具體可參考《GDB QUICK

REFERENCE》,非常棒!

另外gcore

pid 對正在運作的程序産生一個core檔案

llist(l)

(gdb)list line1,line2

要想運作準備調試的程式,可使用run(r)指令,在它後面可以跟随發給該程式的任何參數,包括标準輸入和标準輸出說明符()和外殼通配符

(*、?、[、])在内。

如果你使用不帶參數的run指令,gdb就再次使用你給予前一條run指令的參數,這是很有用的。

利用set args指令就可以修改發送給程式的參數,而使用show

args指令就可以檢視其預設參數的清單。

(gdb)set args -b -x

(gdb) show args

backtrace(bt)指令為堆棧提供向後跟蹤功能。

backtrace(bt)指令産生一張清單,包含着從最近的過程開始的是以有效過程和調用這些過程的參數。

1. 利用print(p)指令可以檢查各個變量的值

(gdb) print var (var為變量名)

whatis指令可以顯示某個變量的類型

(gdb) whatis var

type = int *

print

是gdb的一個功能很強的指令,利用它可以顯示被調試的語言中任何有效的表達式。當你使用print指令時,可以用一個參數/F來選擇輸出的列印格式。F可以是以下的一些值:

'x' 16進制整數格式

'd' 有符号十進制整數格式

'u' 無符号十進制整數格式

'f' 浮點數格式表達式

通常在gdb調試時要列印出一些字元串的内容,通過p

str列印字元串時,通常有長度的限制,我測試linux機器上預設為200個,但實際輸出的長度str_len可能大于該值,結果不能夠完全輸出,而進行了省略,通過指令set

print element 0就可以了。

print除了顯示變量外,還可以顯示以下内容:

l 對程式中函數的調用

(gdb) print find_entry(1,0)

l 資料結構和其他複雜對象

(gdb) print *table_start

$8={e=reference=’\000’,location=0x0,next=0x0}

l 值的曆史成分

(gdb)print $1 ($1為曆史記錄變量,在以後可以直接引用$1的值)

l 人為數組

人為數組提供了一種去顯示存儲器塊(數組節或動态配置設定的存儲區)内容的方法。早期的調試程式沒有很好的方法将任意的指針換成一個數組。就像對待參數一樣,讓我們檢視記憶體中在變量h後面的10個整數,一個動态數組的文法如下所示:

[email protected]

是以,要想顯示在h後面的10個元素,可以使用[email protected]:

(gdb)print [email protected]

$13=(-1,345,23,-234,0,0,0,98,345,10)

2. 使用info檢視資訊

(1)檢視目前源程式:info source

(2)看堆棧資訊:info stack

(3)檢視目前的參數:info args

(4)檢視目前所有變量:frame n ; info locale;

break指令(可以簡寫為b)可以用來在調試的程式中設定斷點,該指令有如下四種形式:

lbreak

line-number使程式恰好在執行給定行之前停止。

lbreak

function-name使程式恰好在進入指定的函數之前停止。

lbreak

line-or-function if

condition如果condition(條件)是真,程式到達指定行或函數時停止。

lbreak

routine-name在指定例程的入口處設定斷點

如果該程式是由很多原檔案構成的,你可以在各個原檔案中設定斷點,而不是在目前的原檔案中設定斷點,其方法如下:

(gdb) break filename:line-number

(gdb) break filename:function-name

要想設定一個條件斷點,可以利用break if指令,如下所示:

(gdb) break line-or-function if expr

例:

(gdb) break 46 if testsize==100

從斷點繼續運作:countinue(縮寫c)指令

1.Info(i)顯示目前gdb的斷點資訊:

(gdb) info break

他會以如下的形式顯示所有的斷點資訊:

Num Type Disp Enb Address What

1 breakpoint keep y 0x000028bc in init_random at

qsort2.c:155

2 breakpoint keep y 0x0000291c in init_organ at

qsort2.c:168

2. delete(d)删除指定的某個斷點:

(gdb) delete breakpoint 1

該指令将會删除編号為1的斷點,如果不帶編号參數,将删除所有的斷點

(gdb) delete breakpoint

3.禁止使用某個斷點

(gdb) disable breakpoint 1

該指令将禁止斷點 1,同時斷點資訊的 (Enb)域将變為 n

4.允許使用某個斷點

(gdb) enable breakpoint 1

該指令将允許斷點 1,同時斷點資訊的 (Enb)域将變為 y

5.清除原檔案中某一代碼行上的所有斷點

(gdb)clean number

注:number為原檔案的某個代碼行的行号

lwhatis:識别數組或變量的類型

lptype:比whatis的功能更強,他可以提供一個結構的定義

lset

variable:将值賦予變量

lprint:除了顯示一個變量的值外,還可以用來指派

當你調試一個很大的程式,并且在跟蹤一個關鍵的變量時,發現這個變量不知在哪兒被改動過,如何才能找到改動它的地方。這時你可以使用watch指令。簡單地說,監視點可以讓你監視某個表達式或變量,當它被讀或被寫時讓程式斷下。watch指令的用法如下:watch

EXPRESSION

watch指令是監視被寫的,當你想監視某個表達式或變量被讀的話,需要使用rwatch指令,具體用法是一樣的。要注意的是,監視點有硬體和軟體兩種方式,如果可能Linux盡可能用硬體方式,因為硬體方式在速度上要大大快于軟體方式。軟體方式由于要在每次執行一條指令後都要檢查所要監視的值是否被改變,是以它的執行速度會大大降低。同時它也無法設定成被讀時讓程式斷下,因為讀操作不會改變值,是以GDB無法檢測到讀操作。幸運的是,目前的PC機基本都支援硬體方式。如果你想确認一下你的機器是否支援硬體,你可以在調試程式時用watch設定一個監視點,如果GDB向你顯示:Hardware

watchpoint NUM: EXPR,那麼你可以放心了,你的機器支援硬體方式。

檢查記憶體值的指令是x,x是examine的意思。用法:x /NFU

ADDR,其中N代表重複數,F代表輸出格式,U代表每個資料機關的大小。U可以去如下值:

b :位元組(byte)

h :雙位元組數值

w :四位元組數值

g :八位元組數值

是以,上面的指令可以這樣解釋:從ADDR位址開始,以F格式顯示N個U數值。例如:x/4ub

0x4000,會以無符号十進制整數格式(u)顯示四個位元組(b),0x4000,0x4001,0x4002,0x4003。

next不進入的單步執行

step進入的單步執行

finish如果已經進入了某函數,而想退出該函數傳回到它的調用函數中,可使用指令finish

Øcall

name調用和執行一個函數

(gdb) call gen_and_sork( 1234,1,0 )

(gdb) call printf(“abcd”)

$1=4

Øfinish結束執行目前函數,顯示其傳回值(如果有的話)

有一組專用的gdb變量可以用來檢查和修改計算機的通用寄存器,gdb提供了目前每一台計算機中實際使用的4個寄存器的标準名字:

$pc 程式計數器

$fp 幀指針(目前堆棧幀)

$sp 棧指針

$ps 處理器狀态

gdb通常可以捕捉到發送給它的大多數信号,通過捕捉信号,它就可決定對于正在運作的程序要做些什麼工作。

例如,按CTRL-C将中斷信号發送給gdb,

通常就會終止gdb。但是你或許不想中斷gdb,真正的目的是要中斷gdb正在運作的程式。是以,gdb要抓住該信号并停止它正在運作的程式,這樣就可以執行某些調試操作。

handle指令可控制信号的處理,他有兩個參數,一個是信号名,另一個是接受到信号時該作什麼。幾種可能的參數是:

nostop接收到信号時,不要将它發送給程式,也不要停止程式。

stop接受到信号時停止程式的執行,進而允許程式調試;顯示一條表示已接受到信号的消息(禁止使用消息除外)

print接受到信号時顯示一條消息

noprint接受到信号時不要顯示消息(而且隐含着不停止程式運作)

pass将信号發送給程式,進而允許你的程式去處理它、停止運作或采取别的動作。

nopass停止程式運作,但不要将信号發送給程式。

例如,假定你截獲SIGPIPE信号,以防止正在調試的程式接受到該信号,而且隻要該信号一到達,就要求該程式停止,并通知你。要完成這一任務,可利用如下指令:

(gdb) handle SIGPIPE stop print

請注意,UNIX的信号名總是采用大寫字母!你可以用信号編号替代信号名。

如果你的程式要執行任何信号處理操作,就需要能夠測試其信号處理程式,為此,就需要一種能将信号發送給程式的簡便方法,這就是signal指令的任務。該指令的參數是一個數字或者一個名字,如SIGINT。

假定你的程式已将一個專用的SIGINT(鍵盤輸入,或CTRL-C;信号2)信号處理程式設定成采取某個清理動作,要想測試該信号處理程式,你可以設定一個斷點并使用如下指令:

(gdb) signal 2

continuing with signal SIGINT(2)

該程式繼續執行,但是立即傳輸該信号,而且處理程式開始運作.

searchtext:

該指令可顯示在目前檔案中包含text串的下一行。

reverse-searchtext:

該指令可以顯示包含text的前一行。

shell指令可啟動UNIX外殼,CTRL-D或exit退出外殼,傳回到

gdb.

為了允許使用曆史指令,可使用 set history expansion on 指令

(gdb)set

history expansion on

在 gdb 提示符處鍵入help,将列出指令的分類,主要的分類有:

* aliases:指令别名

* breakpoints:斷點定義;

* data:資料檢視;

* files:指定并檢視檔案;

* internals:維護指令;

* running:程式執行;

* stack:調用棧檢視;

* statu:狀态檢視;

* tracepoints:跟蹤程式執行。

鍵入help後跟指令的分類名,可獲得該類指令的詳細清單。

先介紹一下GDB多線程調試的基本指令。

Øinfo

threads

顯示目前可調試的所有線程,每個線程會有一個GDB為其配置設定的ID,後面操作線程的時候會用到這個ID。

前面有*的是目前調試的線程。

Øthread

ID

切換目前調試的線程為指定ID的線程。

Øbreak

thread_test.c:123 thread all

在所有線程中相應的行上設定斷點

Øthread

apply ID1 ID2 command

讓一個或者多個線程執行GDB指令command。

Øthread

apply all command

讓所有被調試線程執行GDB指令command。

Øset

scheduler-locking off|on|step

實際使用過多線程調試的人都可以發現,在使用step或者continue指令調試目前被調試線程的時候,其他線程也是同時執行的,怎麼隻讓被調試程式執行呢?

通過這個指令就可以實作這個需求:

off 不鎖定任何線程,也就是所有線程都執行,這是預設值。

on 隻有目前被調試程式會執行。

step

在單步的時候,除了next過一個函數的情況(熟悉情況的人可能知道,這其實是一個設定斷點然後continue的行為)以外,隻有目前線程會執行。

在介紹完基本的多線程調試指令後,大概介紹一下GDB多線程調試的實作思路。

比較主要的代碼是thread.c,前面介紹的幾個指令等都是在其中實作。

Ø thread_list這個表存儲了目前可調試的所有線程的資訊。

Ø

函數add_thread_silent或者add_thread(不同版本GDB不同)用來向thread_list清單增加一個線程的資訊。

Ø 函數delete_thread用來向thread_list清單删除一個線程的資訊。

上面提到的這2個函數會被有線程支援的target調用,用來增加和删除線程,不同的OS對線程的實作差異很大,這麼實作比較好的保證了GDB多線程調試支援的擴充性。

Ø 函數info_threads_command是被指令info

threads調用的,就是顯示thread_list清單的資訊。

Ø

函數thread_command是被指令thread調用,切換目前線程最終調用的函數是switch_to_thread,這個函數會先将目前調試線程變量inferior_ptid,然後對寄存器和frame緩沖進行重新整理。

Ø 函數thread_apply_command被指令thread

apply調用,這個函數的實際實作其實很簡單,就是先切換目前線為指定線程,然後調用函數execute_command調用指定函數。

比較特别的是set

scheduler-locking沒有實作在thread.c中,而是實作在控制被調試程式執行的檔案infrun.c中。

對其的設定會儲存到變量scheduler_mode中,而實際使用這個變量的函數隻有用來令被調試程式執行的函數resume。

在預設情況下,

傳遞給target_resume的變量是resume_ptid,預設情況下其的值為RESUME_ALL,也就是告訴target程式執行的時候所有被調試線程都要被執行。而當scheduler_mode設定為隻讓目前線程執行的時候,resume_ptid将被設定為inferior_ptid,

這就告訴target隻有inferior_ptid的線程會被執行。

最後特别介紹一下Linux下多線程的支援,基本的調試功能在linux-nat.c中,這裡有對Linux輕量級别程序本地調試的支援。但是其在調試多線程程式的時候,還需要對pthread調試的支援,這個功能實作在linux-thread-db.c中,對pthread的調試要通過調用

libthread_db庫來支援。

這裡有一個單獨的target"multi-thread",這個target有2點很特别:

第一,一般target的裝載是在調用相關to_open函數的時候調用push_target進行裝載。而這個target則不同,在其初始化的時候,就注冊了函數thread_db_new_objfile到庫檔案attach事件中。這樣當GDB為調試程

序的動态加載庫時候attach庫檔案的時候,就會調用這個函數thread_db_new_objfile。這樣當GDB裝載libpthread庫的時候,最終會裝載

target"multi-thread"。

第二,這個target并沒有像大部分target那樣自己實作了全部調試功能,其配合linux-nat.c的代碼的功能,這裡有一個target多層

結構的設計,要介紹的比較多,就不詳細介紹了。

最後介紹一下我最近遇見的一個多線程調試和解決:

基本問題是在一個Linux環境中,調試多線程程式不正常,info threads看不到多線程的資訊。

我先用指令maintenance print

target-stack看了一下target的裝載情況,發現target"multi-thread"沒有被裝載,用GDB對GDB進行調試,發現在函數check_for_thread_db在調用libthread_db中的函數td_ta_new的時候,傳回了TD_NOLIBTHREAD,是以沒有裝載target"multi-thread"。

在這時候我就懷疑是不是libpthread有問題,于是檢查了一下發現了問題,這個環境中的libpthread是被strip過的,我想可能就是以為這個影響了td_ta_new對libpthread符号資訊的擷取。當我換了一個沒有strip過的libpthread的時候,問題果然解決

了。

最終我的解決辦法是拷貝了一個.debug版本的libpthread到lib目錄中,問題解決了。

多線程如果dump,多為段錯誤,一般都涉及記憶體非法讀寫。可以這樣處理,使用下面的指令打開系統開關,讓其可以在死掉的時候生成core檔案。

ulimit -c unlimited

這樣的話死掉的時候就可以在目前目錄看到core.pid(pid為程序号)的檔案。接着使用gdb:

gdb ./bin ./core.pid

進去後,使用bt檢視死掉時棧的情況,再使用frame指令。

還有就是裡面某個線程停住,也沒死,這種情況一般就是死鎖或者涉及消息接受的逾時問題(聽人說的,沒有遇到過)。遇到這種情況,可以使用:

gcore pid (調試程序的pid号)

手動生成core檔案,在使用pstack(linux下好像不好使)檢視堆棧的情況。如果都看不出來,就仔細檢視代碼,看看是不是在

if,return,break,continue這種語句操作是忘記解鎖,還有嵌套鎖的問題,都需要分析清楚了。

清單 一個有錯誤的C源程式 bugging.c

代碼:

-----------------

1 #include

2

3 static char buff [256];

4 static char* string;

5 int main ()

6 {

7 printf ("Please input a string: ");

8 gets (string);

9 printf ("\nYour string is: %s\n", string);

10 }

-----------------

上面這個程式非常簡單,其目的是接受使用者的輸入,然後将使用者的輸入列印出來。該程式使用了一個未經過初始化的字元串位址string,是以,編譯并運作之後,将出現Segment

Fault錯誤:

$ gcc -o bugging -g bugging.c

$ ./bugging

Please input a string: asfd

Segmentation fault (core dumped)

為了查找該程式中出現的問題,我們利用 gdb,并按如下的步驟進行:

1.運作 gdb bugging 指令,裝入 bugging 可執行檔案;

2.執行裝入的 bugging 指令 run;

3.使用 where 指令檢視程式出錯的地方;

4.利用 list 指令檢視調用 gets 函數附近的代碼;

5.唯一能夠導緻 gets 函數出錯的因素就是變量 string。用print指令檢視 string

的值;

6.在 gdb 中,我們可以直接修改變量的值,隻要将 string

取一個合法的指針值就可以了,為此,我們在第8行處設定斷點 break 8;

7.程式重新運作到第8行處停止,這時,我們可以用set variable指令修改

string的取值;

8.然後繼續運作,将看到正确的程式運作結果。

指令

縮寫

用法

作用

help

h

h command

顯示指令的幫助

run

r

r [args]

運作要調試的程式args為要運作程式的參數

step

s

s [n]

步進,n為步進次數。如果調用了某個函數,會跳入函數内部。

next

n

n [n]

下一步,n為下一步的次數

continue

c

c

繼續執行程式

list

l

l / l+ / l-

列出源碼

break

b

b address

在位址address上設定斷點

b function

此指令用來在某個函數上設定斷點。

b linenum

在行号為linenum的行上設定斷點。程式在運作到此行之前停止

b +offset

b -offset

在目前程式運作到的前幾行或後幾行設定斷點。offset為行号

b filename:linenum

在檔案名為filename的原檔案的第linenum行設定斷點

b filename:function

在檔案名為filename的原檔案的名為function的函數上設定斷點。當你的多個檔案中可能含有相同的函數名時必須給出檔案名

watch

w

w exp

監視表達式的值

kill

k

k

結束目前調試的程式

print

p

p exp

列印表達式的值

p/fmt exp

fmt為以下值x十六進制d十進制

u無符号數o八進制t二進制a十六進制列印

c字元格式f浮點數

output

o

o exp

同print,但是不輸出下一行的語句

ptype

ptype struct

輸出一個struct結構的定義

whatis

whatis var

指令可以顯示某個變量的類型

x

x/(length)(format)(size)

addr

x/6(o/d/x/u/c/t)(b/h/w)例如:x/4xw

按一定格式顯示記憶體位址或變量的值

pwd

pwd

顯示目前路徑

delete

d

d num

删除編号為num的斷點和監視

disable

disable n

編号為n的斷點暫時無效

enable

enable n

與disable相反

display

display expr

暫停,步進時自動顯示表達式的值

finish

執行直到函數傳回執行直到目前stack傳回

return

強制從目前函數傳回

where

指令用來檢視執行的代碼在什麼地方中止

backtrace

bt

顯示函數調用得所有棧架構(stack

frames)的蹤迹和目前函數的參數的值。

quit

q

退出調試程式

frame

shell

shell ls

執行shell指令

make

不退出gdb而重新編譯生成可執行檔案

disassemble

顯示反彙編代碼

thread

thread thread_no

用來線上程之間的切換

set

set width 70

就是把标準螢幕設為70列

set var=54

設定變量的值。

forward/search

search string

從目前行向後查找比對某個字元串的程式行

reverse-search

forward/search相反,向前查找字元串。使用格式同上

up/down

上移/下移棧幀,使另一函數成為目前函數

info

i

i breakpoint

顯示目前斷點清單

i reg[ister]

顯示寄存器資訊

i threads

顯示線程資訊

i func

顯示所有的函數名

i

local

顯示目前函數的所有局部變量的資訊

i prog

顯示調試程式的執行狀态

i watch exp

為表達式(變量)exp設定一個觀察點。一但表達式值有變化時,馬上停住程式。

i proc

顯示程序的概要資訊

info proc mappings

報告你程序所能通路的位址範圍。

info proc times

你程序和子程序的開始時間,使用者時間(user CPU

time),和系統CPU時間。

info proc id

報告有關程序id的資訊

info proc status

報告你程序的一般狀态資訊。如果程序停止了。這個報告還包括停止的原因和收到的信号

info proc all

顯示上面proc指令這些指令傳回的所有資訊