使用和了解Valgrind核心:高级主题
目錄
3.1。客戶端請求機(jī)制3.2。使用Valgrind gdbserver和GDB調(diào)試程序本章介紹了Valgrind核心服務(wù)的高級方面,主要是為希望以某些有用的方式自定義和修改Valgrind的默認(rèn)行為的用戶感興趣。涵蓋的主題是:
-
“客戶請求”機(jī)制
-
使用Valgrind的gdbserver和GDB調(diào)試程序
-
功能包裝
3.1。客戶端請求機(jī)制
Valgrind具有一個陷門機(jī)制,通過該機(jī)制,客戶端程序可以將所有方式的請求和查詢傳遞給Valgrind和當(dāng)前工具。在內(nèi)部,這廣泛用于使各種事情發(fā)揮作用,盡管從外部看不到。
為了方便起見,提供了這些所謂的客戶端請求的一部分,以便您可以告訴Valgrind有關(guān)程序行為的事實,也可以進(jìn)行查詢。特別是,您的程序可以告訴Valgrind有關(guān)其他方面不會知道的事情,從而獲得更好的結(jié)果。
客戶端需要包含頭文件才能使其工作。哪個頭文件取決于您使用的客戶端請求。一些客戶端請求由內(nèi)核處理,并在頭文件中定義valgrind/valgrind.h。工具特定的頭文件以工具命名,例如?valgrind/memcheck.h。每個特定于工具的頭文件都包含在內(nèi)valgrind/valgrind.h,因此如果您包含特定于工具的頭,則不需要將其包含在客戶端中。所有頭文件可以在安裝include/valgrindValgrind的目錄中找到。
這些頭文件中的宏具有魔術(shù)屬性,它們可以生成Valgrind可以發(fā)現(xiàn)的代碼。但是,代碼在Valgrind上運(yùn)行時不執(zhí)行任何操作,因此您不會因為使用此文件中的宏而被迫在Valgrind下運(yùn)行程序。此外,您不需要將程序與任何額外的支持庫鏈接。
添加到您的二進(jìn)制代碼的代碼可以忽略不計的性能影響:在x86,amd64,ppc32,ppc64和ARM上,開銷是6個簡單的整數(shù)指令,除非是緊密循環(huán),否則可能無法檢測。但是,如果您真的希望編譯客戶端請求,可以使用-DNVALGRIND(類似于?-DNDEBUG“效果”?assert)進(jìn)行編譯。
我們鼓勵您將標(biāo)題復(fù)制valgrind/*.h到項目的包含目錄中,這樣您的程序就沒有編譯時依賴于正在安裝的Valgrind。Valgrind標(biāo)題與其他代碼大部分不同,是BSD風(fēng)格的許可證,所以您可以不必?fù)?dān)心許可證不兼容。
以下是對可用宏的簡要說明?valgrind.h,可以使用多種工具(請參閱特定于工具的文檔以了解工具特定的宏)。
RUNNING_ON_VALGRIND:如果在Valgrind上運(yùn)行,則返回1,如果在實際CPU上運(yùn)行則返回0。如果您運(yùn)行Valgrind本身,則返回您運(yùn)行的Valgrind仿真層數(shù)。
丟棄指定地址范圍內(nèi)的代碼翻譯。如果您正在調(diào)試JIT編譯器或其他動態(tài)代碼生成系統(tǒng),則很有用。在此調(diào)用之后,嘗試在無效地址范圍內(nèi)執(zhí)行代碼將導(dǎo)致Valgrind對該代碼進(jìn)行新的翻譯,這可能是您想要的語義。請注意,代碼無效是昂貴的,因為快速找到所有相關(guān)的翻譯是非常困難的,所以盡量不要經(jīng)常打電話。請注意,您可以很聰明:只有當(dāng)以前包含代碼的區(qū)域被新的代碼覆蓋時,才需要調(diào)用它。您可以選擇將代碼寫入新鮮的內(nèi)存,并且偶爾調(diào)用這些代碼一次丟棄大量的舊代碼。
或者,對于ppc32?--smc-check=all/ Linux,ppc64 / Linux或ARM / Linux?的透明自修改代碼支持,使用或運(yùn)行。
返回Valgrind到目前為止發(fā)現(xiàn)的錯誤數(shù)。與--log-fd=-1選項結(jié)合使用時,可用于測試工具代碼?;?這個默認(rèn)運(yùn)行Valgrind,但客戶端程序可以檢測何時發(fā)生錯誤。僅對報告錯誤的工具有用,例如它對Memcheck有用,但對于Cachegrind,它總是返回零,因為Cachegrind不報告錯誤。
如果您的程序管理自己的內(nèi)存,而不是使用標(biāo)準(zhǔn)的malloc/?new/?new[],則跟蹤有關(guān)堆塊的信息的工具不會做得很好。例如,Memcheck將不會檢測到幾乎相同的錯誤,并且錯誤消息將不會提供信息。為了改善這種情況,在您的自定義分配器分配一些新內(nèi)存之后使用此宏。有關(guān)valgrind.h如何使用它的信息,請參閱注釋?。
這應(yīng)該與之配合使用?VALGRIND_MALLOCLIKE_BLOCK。再次看看valgrind.h有關(guān)如何使用它的信息。
通知Valgrind工具,分配的塊的大小已被修改,但不是其地址。查看valgrind.h有關(guān)如何使用它的更多信息。
這些類似于?VALGRIND_MALLOCLIKE_BLOCK和?VALGRIND_FREELIKE_BLOCK?,但偏向于使用內(nèi)存池的代碼定制。有關(guān)詳細(xì)說明,請參閱?內(nèi)存池。
在客戶端程序中執(zhí)行實際?CPU?上的功能?,而不是Valgrind正常運(yùn)行代碼的虛擬CPU。該函數(shù)必須采用一個整數(shù)(保持線程ID)作為第一個參數(shù),然后將0,1,2或3個參數(shù)(取決于使用哪個客戶端請求)。這些在Valgrind內(nèi)部以各種方式使用。它們可能對客戶端程序有用。
警告:只有使用這些,如果你?真的知道你在做什么。它們并不完全可靠,可能導(dǎo)致Valgrind崩潰。查看?valgrind.h更多詳情。
將Printf樣式的消息打印到Valgrind日志文件。該消息以一對**標(biāo)記之間的PID為前綴?。(與所有客戶端請求一樣,如果客戶端程序未在Valgrind下運(yùn)行,則不會輸出任何內(nèi)容。)直到遇到換行符或后續(xù)的Valgrind輸出打印時才會產(chǎn)生輸出;?這允許您通過多個呼叫建立單行輸出。返回輸出的字符數(shù),不包括PID前綴。
喜歡VALGRIND_PRINTF(特別是返回值是相同的),但是之后立即打印一個堆棧回溯。
執(zhí)行給定的monitor命令(一個字符串)。如果識別到命令,則返回0。如果無法識別命令,則返回1。請注意,某些監(jiān)視器命令提供對通過特定客戶端請求可訪問的功能的訪問。例如,可以使用VALGRIND_DO_LEAK_CHECK或通過監(jiān)視器命令“l(fā)eak_search”從客戶端程序請求memcheck泄漏搜索。請注意,命令字符串的語法僅在運(yùn)行時驗證。因此,如果存在,最好使用特定的客戶端請求對參數(shù)進(jìn)行更好的編譯時驗證。
注冊一個新的堆棧。通知Valgrind,start和end之間的內(nèi)存范圍是唯一的堆棧。返回可與其他VALGRIND_STACK_*調(diào)用一起使用的堆棧標(biāo)識符?。
Valgrind將使用此信息來確定堆棧指針的更改是否是推送到堆棧上的項目或更改為新堆棧。如果您正在使用用戶級線程包,并注意到堆棧跟蹤記錄中的崩潰或Valgrind關(guān)于未初始化內(nèi)存讀取的虛假錯誤,請使用此方法。
警告:不幸的是,這個客戶端請求是不可靠的,最好避免。
取消注冊以前注冊的堆棧。通知Valgrind以前注冊的堆棧ID的內(nèi)存范圍?id不再是堆棧。
警告:不幸的是,這個客戶端請求是不可靠的,最好避免。
更改以前注冊的堆棧。通知Valgrind,堆棧id的先前注冊的堆棧?id已更改其起始值和結(jié)束值。如果您的用戶級線程包實現(xiàn)堆棧增長,請使用此選項。
警告:不幸的是,這個客戶端請求是不可靠的,最好避免。
3.2。使用Valgrind gdbserver和GDB調(diào)試程序
在Valgrind下運(yùn)行的程序不會直接由CPU執(zhí)行。而是運(yùn)行在Valgrind提供的合成CPU上。這就是為什么調(diào)試器在Valgrind上運(yùn)行時無法調(diào)試程序的原因。
本節(jié)介紹GDB如何與Valgrind gdbserver進(jìn)行交互,以在Valgrind下提供完全可調(diào)試的程序。以這種方式使用,GDB還提供了Valgrind核心或工具功能的交互式使用,包括Memcheck和按需Massif快照生產(chǎn)的增量泄漏搜索。
3.2.1。快速入門:3步調(diào)試
開始使用最簡單的方法是使用標(biāo)志運(yùn)行Valgrind?--vgdb-error=0。然后按照屏幕上的方向進(jìn)行操作,這樣您就可以獲得啟動GDB并將其連接到程序所需的精確命令。
否則,這里有一個更詳細(xì)的概述。
如果要在使用Memcheck工具時使用GDB調(diào)試程序,請啟動Valgrind,如下所示:
valgrind --vgdb = yes --vgdb-error = 0 prog在另一個shell中,啟動GDB:
gdb程序然后給GDB發(fā)送以下命令:
(gdb)target remote | vgdb您現(xiàn)在可以調(diào)試程序,例如插入斷點,然后使用GDB?continue?命令。
這個快速啟動信息足以基本使用Valgrind gdbserver。以下部分描述了Valgrind和GDB組合提供的更高級的功能。請注意,--vgdb=yes可以省略命令行標(biāo)志,因為這是默認(rèn)值。
3.2.2。Valgrind gdbserver整體組織
GNU GDB調(diào)試器通常用于調(diào)試在同一臺機(jī)器上運(yùn)行的進(jìn)程。在這種模式下,GDB使用系統(tǒng)調(diào)用來控制和查詢被調(diào)試的程序。這樣做很好,但只允許GDB調(diào)試在同一臺計算機(jī)上運(yùn)行的程序。
GDB還可以調(diào)試在不同計算機(jī)上運(yùn)行的進(jìn)程。為了實現(xiàn)這一點,GDB定義了一個協(xié)議(即一組查詢和回復(fù)數(shù)據(jù)包),有助于獲取內(nèi)存或寄存器的值,設(shè)置斷點等。gdbserver是“GDB遠(yuǎn)程調(diào)試”協(xié)議的實現(xiàn)。要調(diào)試在遠(yuǎn)程計算機(jī)上運(yùn)行的進(jìn)程,gdbserver(有時稱為GDB存根)必須在遠(yuǎn)程計算機(jī)端運(yùn)行。
Valgrind內(nèi)核提供了一個內(nèi)置的gdbserver實現(xiàn),它使用--vgdb=yes?或激活--vgdb=full。該gdbserver允許在Valgrind的合成CPU上運(yùn)行的進(jìn)程遠(yuǎn)程調(diào)試。GDB向Valgrind嵌入式gdbserver發(fā)送協(xié)議查詢數(shù)據(jù)包(如“獲取寄存器內(nèi)容”)。gdbserver執(zhí)行查詢(例如,它將獲得合成CPU的寄存器值),并將結(jié)果返回給GDB。
GDB可以使用各種通道(TCP / IP,串行線等)與gdbserver進(jìn)行通信。在Valgrind的gdbserver的情況下,通過一個管道和一個名為vgdb的小幫助程序完成通信,該程序充當(dāng)中間人。如果沒有使用GDB,vgdb也可以用于從shell命令行將監(jiān)控命令發(fā)送到Valgrind gdbserver。
3.2.3。將GDB連接到Valgrind gdbserver
要調(diào)試程序“?prog” Valgrind的下運(yùn)行,你必須確保Valgrind的gdbserver的是通過指定激活--vgdb=yes?或--vgdb=full。輔助命令行選項,?--vgdb-error=number可以用于告知gdbserver在顯示指定數(shù)量的錯誤后才會變?yōu)榛顒訝顟B(tài)。因此,值為零將導(dǎo)致gdbserver在啟動時變?yōu)榛顒訝顟B(tài),這允許您在開始運(yùn)行之前插入斷點。例如:
valgrind --tool = memcheck --vgdb = yes --vgdb-error = 0 ./progValgrind gdbserver在啟動時被調(diào)用,并指示它正在等待來自GDB的連接:
== 2418 == Memcheck,一個內(nèi)存錯誤檢測器 == 2418 ==版權(quán)所有(C)2002-2010和GNU GPL'd,由Julian Seward等人 == 2418 ==使用Valgrind-3.7.0.SVN和LibVEX; 用-h重新運(yùn)行版權(quán)信息 == 2418 ==命令:./prog == == 2418 == 2418 ==(啟動時的動作)vgdb me ...然后可以將GDB(在另一個shell中)連接到Valgrind gdbserver。為此,必須在程序上啟動GDB?prog:
gdb ./prog然后,您向GDB指出您要調(diào)試遠(yuǎn)程目標(biāo):
(gdb)target remote | vgdbGDB然后啟動一個vgdb中繼應(yīng)用程序與Valgrind嵌入式gdbserver進(jìn)行通信:
(gdb)target remote | vgdb 遠(yuǎn)程調(diào)試使用| vgdb 在gdb和進(jìn)程2418之間中繼數(shù)據(jù) 從/lib/ld-linux.so.2...done讀取符號。 從/usr/lib/debug/lib/ld-2.11.2.so.debug...done讀取符號。 /lib/ld-linux.so.2的加載符號 [切換到線程2418] 來自/lib/ld-linux.so.2的_start()中的0x001f2850 (GDB)請注意,vgdb是作為Valgrind分發(fā)的一部分提供的。您不需要單獨(dú)安裝。
如果vgdb檢測到可以連接多個Valgrind gdbservers,它將列出所有這些服務(wù)器及其PID,然后退出。然后,您可以重新發(fā)行GDB“目標(biāo)”命令,但指定要調(diào)試的進(jìn)程的PID:
(gdb)target remote | vgdb 遠(yuǎn)程調(diào)試使用| vgdb 沒有--pid = arg給定和多個valgrind pids找到: 使用--pid = 2479 for valgrind --tool = memcheck --vgdb = yes --vgdb-error = 0 ./prog 使用--pid = 2481 for valgrind --tool = memcheck --vgdb = yes --vgdb-error = 0 ./prog 使用--pid = 2483 for valgrind --vgdb = yes --vgdb-error = 0 ./another_prog 遠(yuǎn)程通信錯誤:資源暫時不可用。 (gdb)target remote | vgdb --pid = 2479 遠(yuǎn)程調(diào)試使用| vgdb --pid = 2479 在gdb和進(jìn)程2479之間中繼數(shù)據(jù) 從/lib/ld-linux.so.2...done讀取符號。 從/usr/lib/debug/lib/ld-2.11.2.so.debug...done讀取符號。 /lib/ld-linux.so.2的加載符號 [切換到線程2479] 來自/lib/ld-linux.so.2的_start()中的0x001f2850 (GDB)一旦GDB連接到Valgrind gdbserver,它就可以像你在本機(jī)調(diào)試程序一樣使用:
-
斷點可插入或刪除。
-
可以檢查或修改變量和寄存器值。
-
可以配置信號處理(打印,忽略)。
-
執(zhí)行可以被控制(繼續(xù),步驟,下一步,stepi等)。
-
程序執(zhí)行可以使用Control-C中斷。
等等。有關(guān)GDB功能的完整說明,請參閱GDB用戶手冊。
3.2.4。連接到Android gdbserver
開發(fā)Android應(yīng)用程序時,通常會使用開發(fā)系統(tǒng)(安裝了Android NDK)來編譯應(yīng)用程序。將使用Android目標(biāo)系統(tǒng)或仿真器來運(yùn)行應(yīng)用程序。在此設(shè)置中,Valgrind和vgdb將在Android系統(tǒng)上運(yùn)行,而GDB將在開發(fā)系統(tǒng)上運(yùn)行。GDB將使用Android NDK'adb forward'應(yīng)用程序連接到在Android系統(tǒng)上運(yùn)行的vgdb。
示例:在Android系統(tǒng)上,執(zhí)行以下操作:
valgrind --vgdb-error = 0 --vgdb = yes prog 然后在另一個shell中運(yùn)行: vgdb --port = 1234在開發(fā)系統(tǒng)中,執(zhí)行以下命令:
adb forward tcp:1234 tcp:1234 gdb程序 (gdb)target remote:1234GDB將使用本地tcp / ip連接來連接到Android adb轉(zhuǎn)發(fā)器。Adb將在主機(jī)系統(tǒng)和Android目標(biāo)系統(tǒng)之間建立中繼連接。確保使用Android NDK系統(tǒng)中提供的GDB(通常是arm-linux-androideabi-gdb),因為主機(jī)GDB可能無法調(diào)試Android手臂應(yīng)用程序。請注意,本地端口nr(由GDB使用)不一定等于vgdb使用的端口號:adb可以在不同端口號之間轉(zhuǎn)發(fā)tcp / ip。
在當(dāng)前版本中,默認(rèn)情況下,由于建立一個合適的目錄,因為Valgrind可以創(chuàng)建必要的FIFO(命名管道)用于通信目的,GDB服務(wù)器未啟用。您可以嘗試使用GDB服務(wù)器,但是您需要使用該標(biāo)志--vgdb=yes或?顯式啟用它?--vgdb=full。
此外,您將需要選擇一個臨時目錄(a)可由Valgrind寫入,(b)支持FIFO。這是難點。通常/sdcard滿足要求(a),但是(b)因為是VFAT文件系統(tǒng)而VFAT不支持管道而失敗。可能你可以嘗試是?/data/local,?/data/local/Inst(如果你安裝了Valgrind的存在),或者?/data/data/name.of.my.app,如果你正在運(yùn)行特定的應(yīng)用程序,它有它自己的這種形式的目錄。這最后的可能性可能是最高的成功概率。
您可以通過--with-tmpdir=配置時間標(biāo)志指定臨時目錄或在運(yùn)行Valgrind時(在Android設(shè)備上,而不是Android NDK開發(fā)主機(jī)上)設(shè)置環(huán)境變量TMPDIR。另一個選擇是使用--vgdb-prefix=Valgrind命令行選項來指定FIFO的目錄。
我們希望將來有一個更好的故事,用于Android上的臨時目錄處理。困難在于,與標(biāo)準(zhǔn)Unixes不同,沒有單個臨時文件目錄可靠地在所有設(shè)備和方案中工作。
3.2.5。監(jiān)視Valgrind gdbserver的命令處理
Valgrind gdbserver通過“monitor命令”提供了額外的Valgrind特定功能。這樣的監(jiān)視命令可以從GDB命令行或從shell命令行發(fā)送,或者由客戶端程序使用VALGRIND_MONITOR_COMMAND客戶端請求發(fā)送。有關(guān)Valgrind核心監(jiān)視器命令的列表,請參閱?Valgrind監(jiān)視器命令,無論選擇了Valgrind工具。
以下工具提供特定于工具的監(jiān)視器命令:
-
Memcheck監(jiān)視器命令
-
Callgrind監(jiān)視器命令
-
Massif監(jiān)視器命令
-
Helgrind監(jiān)視器命令
特定于工具的監(jiān)視器命令的示例是Memcheck監(jiān)視器命令leak_check full reachable any。這要求對所分配的內(nèi)存塊進(jìn)行全面報告。要執(zhí)行此泄漏檢查,請使用GDB命令:
(gdb)monitor leak_check完全可達(dá)任何GDB將發(fā)送leak_check?命令到Valgrind gdbserver。如果Valgrind gdbserver將其識別為Valgrind core monitor命令,則會自動執(zhí)行monitor命令。如果它不被認(rèn)可,則假設(shè)它是具體的工具,并被交給該工具執(zhí)行。例如:
(gdb)monitor leak_check完全可達(dá)任何 == 2418 == 1塊中的100字節(jié)仍然可以在損失記錄1中1 == 2418 == at 0x4006E9E:malloc(vg_replace_malloc.c:236) == 2418 == by 0x804884F:main(prog.c:88) == == 2418 == 2418 == LEAK SUMMARY: == 2418 ==絕對丟失:0塊0塊 == 2418 ==間接丟失:0個字節(jié),0個塊 == 2418 ==可能丟失:0個字節(jié)的0個塊 == 2418 ==仍然可達(dá):100個字節(jié)的1個塊 == 2418 == suppress:0個字節(jié),0個塊 == == 2418 (GDB)與其他GDB命令一樣,Valgrind gdbserver將接受縮寫的監(jiān)視器命令名稱和參數(shù),只要給定的縮寫是明確的。例如,上述?leak_check?命令也可以鍵入:
(gdb)mo lfra這些字母mo被GDB識別為縮寫monitor。所以GDB將字符串發(fā)送l f r a到Valgrind gdbserver。這個字符串中提供的字母對Valgrind gdbserver來說是明確的。因此,這將給出與未縮寫的命令和參數(shù)相同的輸出。如果提供的縮寫不明確,則Valgrind gdbserver會報告可匹配的命令列表(或參數(shù)值):
(gdb)mo v。n v。可以匹配v.set v.info v.wait v.kill v.translate v.do (gdb)mo vi n n_errs_found 0 n_errs_shown 0(vgdb-error 0) (GDB)而不是從GDB發(fā)送監(jiān)視器命令,您也可以從shell命令行發(fā)送這些命令。例如,以下命令行在shell中給出時,將導(dǎo)致進(jìn)程3145執(zhí)行相同的泄漏搜索:
vgdb --pid = 3145 leak_check完全可達(dá)任何 vgdb --pid = 3145 lfra請注意,在單獨(dú)調(diào)用vgdb之后,Valgrind gdbserver會自動繼續(xù)執(zhí)行該程序。從GDB發(fā)送的監(jiān)視命令不會導(dǎo)致程序繼續(xù):使用GDB命令(如“繼續(xù)”或“下一個”)顯式控制程序執(zhí)行。
3.2.6。Valgrind gdbserver線程信息
Valgrind的gdbserver?info threads使用Valgrind特定的信息豐富了GDB?命令的輸出。操作系統(tǒng)的線程號后跟Valgrind的該線程的內(nèi)部索引(“tid”)和Valgrind調(diào)度程序線程狀態(tài):
(gdb)信息線程4 Thread 6239(tid 4 VgTs_Yielding)0x11f2832 in _ll_sysinfo_int80()from /lib/ld-linux.so.2 * 3線程6238(tid 3 VgTs_Runnable)make_error(s = 0x8048b76“從倫敦調(diào)用”)在prog.c:202 Thread 6237(tid 2 VgTs_WaitSys)0x001f2832 in _ll_sysinfo_int80()from /lib/ld-linux.so.21 prog 6234(tid 1 VgTs_Yielding)main(argc = 1,argv = 0xbedcc274)在prog.c:105 (GDB)3.2.7。檢查和修改Valgrind影子寄存器
當(dāng)給出選項--vgdb-shadow-registers=yes時,Valgrind gdbserver將允許GDB檢查和/或修改Valgrind的影子寄存器。需要GDB 7.1或更高版本才能工作。對于x86和amd64,需要GDB版本7.2或更高版本。
對于每個CPU寄存器,Valgrind內(nèi)核保留兩個影子寄存器集。這些影子寄存器可以通過給出一個后綴s1?或s2分別為第一個和第二個影子寄存器從GDB訪問。例如,eax可以使用以下命令檢查x86寄存器?及其兩個陰影:
(gdb)p $ eax $ 1 = 0 (gdb)p $ eaxs1 $ 2 = 0 (gdb)p $ eaxs2 $ 3 = 0 (GDB)浮動影子寄存器由GDB顯示為無符號整數(shù)值,而不是浮點值,因為預(yù)期這些陰影值主要用于memcheck有效位。
Intel / amd64 AVX寄存器ymm0?也ymm15有它們的影子寄存器。然而,GDB使用兩個“半”寄存器呈現(xiàn)陰影值。例如,半影子寄存器?ymm9是?xmm9s1(組1的下半部分),ymm9hs1(組1的?上半部分),?xmm9s2(組2的下半部分),?ymm9hs2(組2的上半部分)。請注意半登記名稱的不一致表示法:下半部分以a開頭x,上半部分以a開頭,y?并且h在陰影后綴之前。
AVX影子寄存器的特殊表現(xiàn)是由于GDB獨(dú)立地檢索寄存器的下半部分和上半部分ymm。然而,GDB不知道陰影半記錄必須被顯示組合。
3.2.8。Valgrind gdbserver的限制
使用Valgrind gdbserver進(jìn)行調(diào)試與本機(jī)調(diào)試非常相似。Valgrind的gdbserver實現(xiàn)是相當(dāng)完整的,因此提供了大部分GDB調(diào)試功能。但是有一些限制和特點:
-
“停止”命令的精度。
諸如“step”,“next”,“stepi”,斷點和觀察點等GDB命令將停止執(zhí)行進(jìn)程。使用該選項--vgdb=yes,該過程可能不會在精確請求的指令中停止。相反,它可能繼續(xù)執(zhí)行當(dāng)前的基本塊,并停止在以下基本塊之一。這與Valgrind gdbserver必須調(diào)整塊以允許停止在所請求的精確指令的事實有關(guān)。目前,不支持重新檢測當(dāng)前正在執(zhí)行的塊。因此,如果GDB請求的動作(例如單步或插入斷點)意味著重新檢測當(dāng)前塊,則GDB操作可能無法精確執(zhí)行。
當(dāng)正在執(zhí)行的基本塊尚未被調(diào)試時,此限制適用。當(dāng)gdbserver由于工具報告錯誤或觀察點而被激活時,通常會發(fā)生這種情況。如果在斷點之后激活了gdbserver塊,或者在執(zhí)行斷點之前已經(jīng)插入了斷點,則該塊已經(jīng)被調(diào)試了。
如果使用該選項--vgdb=full,則GDB“stop-at”命令將被精確地遵守。不利之處在于,這需要對每個指令進(jìn)行附加調(diào)用,調(diào)用gdbserver幫助函數(shù),這相當(dāng)于開銷(對于memcheck為+ 500%)?--vgdb=no。--vgdb=yes與之相比,選項具有可忽視的開銷--vgdb=no。
-
處理器寄存器和標(biāo)志值。
當(dāng)Valgrind gdbserver停止錯誤時,在斷點上,或者由于Valgrind內(nèi)核進(jìn)行了優(yōu)化,單步執(zhí)行,寄存器和標(biāo)志值可能并不總是最新的。默認(rèn)值?--vex-iropt-register-updates=unwindregs-at-mem-access?可確保在每次存儲器訪問(即內(nèi)存異常點)時進(jìn)行堆棧跟蹤(通常為PC / SP / FP)所需的寄存器是最新的。使用以下值禁用某些優(yōu)化將增加寄存器和標(biāo)志值的精度(為每個選項給出對于memcheck的典型性能影響)。
- --vex-iropt-register-updates=allregs-at-mem-access?(+ 10%)確保所有寄存器和標(biāo)志在每次存儲器訪問時都是最新的。
- --vex-iropt-register-updates=allregs-at-each-insn?(+ 25%)確保所有寄存器和標(biāo)志在每個指令時都是最新的。
請注意,--vgdb=full(+ 500%,見上文“停止”命令的精度)自動激活--vex-iropt-register-updates=allregs-at-each-insn。
-
Valgrind gdbserver支持硬件觀察點。
如果選定的工具提供支持,Valgrind gdbserver可以模擬硬件觀察點。目前,只有Memcheck提供硬件觀察點模擬。由Memcheck提供的硬件觀察點模擬比GDB軟件觀察點快得多,GDB軟件觀察點由GDB在每個指令之后檢查被監(jiān)視區(qū)域的值。硬件觀察點模擬還提供讀取觀察點。Memcheck的硬件觀察點模擬與實際硬件觀察點相比有一些限制。但是,模擬觀察點的數(shù)量和長度不受限制。
通常,(實際)硬件觀察點的數(shù)量有限。例如,x86架構(gòu)最多支持4個硬件觀察點,每個觀察點觀察1,2,4或8個字節(jié)。Valgrind gdbserver對模擬硬件觀察點的數(shù)量沒有任何限制。正在觀看的內(nèi)存區(qū)域的長度也沒有限制。使用GDB版本7.4或更高版本可以充分利用Valgrind gdbserver的模擬硬件觀察點的靈活性。以前的GDB版本不明白Valgrind gdbserver觀察點沒有長度限制。
Memcheck通過將觀看的地址范圍標(biāo)記為不可尋址來實現(xiàn)硬件觀察點模擬。當(dāng)硬件觀察點被刪除時,該范圍被標(biāo)記為可尋址并定義。可尋址但未定義的內(nèi)存區(qū)域的硬件觀察點模擬工作正常,但是在刪除觀察點時定義的區(qū)域具有不良的副作用。
寫入監(jiān)視區(qū)域的準(zhǔn)確指令可能不會報告寫入點,除非--vgdb=full給出了選項。讀取觀察點將始終按照讀取監(jiān)視內(nèi)存的準(zhǔn)確說明進(jìn)行報告。
最好避免使用不可尋址(還))內(nèi)存的硬件觀察點:在這種情況下,GDB將會回退到非常慢的軟件觀察點。另外,如果不在兩個調(diào)試會話之間退出GDB,如果在程序啟動時監(jiān)視的內(nèi)存區(qū)域不可尋址,則前一個會話的硬件觀察點將作為軟件觀察點重新插入。
-
在ARM內(nèi)部進(jìn)行共享庫。
由于未知的原因,在ARM上進(jìn)入共享庫可能會失敗。解決方法是使用?ldd命令查找共享庫列表及其加載地址,并使用GDB命令“add-symbol-file”通知GDB加載地址。例:
(gdb)shell ldd ./proglibc.so.6 => /lib/libc.so.6(0x4002c000)/lib/ld-linux.so。(0x40000000) (gdb)add-symbol-file /lib/libc.so.6 0x4002c000 從文件“/lib/libc.so.6”中添加符號表.text_addr = 0x4002c000 (y或n)y 從/lib/libc.so.6...(找到調(diào)試符號)讀取符號...完成。 (GDB) -
ARM和PPC32 / 64需要GDB版本。
您必須使用能夠讀取gdbserver發(fā)送的XML目標(biāo)描述的GDB版本。如果使用“expat”庫配置和構(gòu)建GDB,則這是標(biāo)準(zhǔn)設(shè)置。如果您的GDB未配置XML支持,則在使用“target”命令時會報告錯誤消息。調(diào)試將不起作用,因為GDB將無法從Valgrind gdbserver獲取寄存器。對于使用Thumb指令集的ARM程序,必須使用7.1或更高版本的GDB版本,因為早期版本在Thumb代碼中具有下一個/步驟/斷點的問題。
-
堆放在PPC32 / PPC64上。
在PPC32 / PPC64,棧展開葉功能(即不調(diào)用任何其他函數(shù)的函數(shù))正常工作,只有當(dāng)你給的選項?--vex-iropt-register-updates=allregs-at-mem-access?或--vex-iropt-register-updates=allregs-at-each-insn。您還必須通過此選項,以便在信號被GDB捕獲時獲得精確的堆棧。
-
斷點遇到多次。
一些指令(例如x86“rep movsb”)由Valgrind使用循環(huán)來翻譯。如果在這樣的指令上放置了一個斷點,斷點將被多次遇到 - 一次執(zhí)行指令的“隱含”循環(huán)的每個步驟。
-
執(zhí)行Valgrind gdbserver的下層函數(shù)調(diào)用。
GDB允許用戶在被調(diào)試的進(jìn)程內(nèi)“調(diào)用”函數(shù)。這種呼叫在GDB術(shù)語中被稱為“次要呼叫”。劣質(zhì)呼叫的典型用途是執(zhí)行打印復(fù)雜數(shù)據(jù)結(jié)構(gòu)的人類可讀版本的功能。要進(jìn)行一個較低的調(diào)用,請使用GDB“print”命令,后跟該函數(shù)調(diào)用及其參數(shù)。作為示例,以下GDB命令會導(dǎo)致對被調(diào)試進(jìn)程執(zhí)行的libc“printf”函數(shù)的較低調(diào)用:
(gdb)p printf(“正在調(diào)試的進(jìn)程具有pid%d \ n”,getpid()) $ 5 = 36 (GDB)Valgrind gdbserver支持較差的函數(shù)調(diào)用。當(dāng)一個劣質(zhì)的電話正在運(yùn)行時,Valgrind工具會像往常一樣報告錯誤。如果您不希望有這樣的錯誤停止執(zhí)行劣質(zhì)呼叫,您可以使用v.set vgdb-error在呼叫之前設(shè)置一個較大的值,然后在呼叫完成時手動將其重置為其原始值。
為了執(zhí)行劣質(zhì)調(diào)用,GDB會更改寄存器,如程序計數(shù)器,然后繼續(xù)執(zhí)行程序。在多線程程序中,所有的線程都會繼續(xù)進(jìn)行,而不僅僅是線程指示進(jìn)行較低的調(diào)用。如果另一個線程報告錯誤或遇到斷點,則劣質(zhì)評估的評估將被放棄。
請注意,劣質(zhì)函數(shù)調(diào)用是強(qiáng)大的GDB功能,但應(yīng)謹(jǐn)慎使用。例如,如果正在調(diào)試的程序在函數(shù)“printf”中被停止,則強(qiáng)制通過劣質(zhì)調(diào)用對printf的遞歸調(diào)用將非常可能產(chǎn)生問題。Valgrind工具還可能為劣質(zhì)調(diào)用增加了另一個級別的復(fù)雜性,例如在Inferior調(diào)用期間報告工具錯誤或由于完成了儀器設(shè)置。
-
連接到或中斷Valgrind進(jìn)程在系統(tǒng)調(diào)用中被阻止。
連接或中斷系統(tǒng)調(diào)用阻塞的Valgrind進(jìn)程需要“ptrace”系統(tǒng)調(diào)用才能使用。出于安全考慮,這可能會在您的內(nèi)核中被禁用。
運(yùn)行程序時,Valgrind的調(diào)度程序會定期檢查是否有任何可以由gdbserver處理的工作。不幸的是,只有當(dāng)進(jìn)程的至少一個線程可運(yùn)行時,才會進(jìn)行該檢查。如果進(jìn)程的所有線程在系統(tǒng)調(diào)用中被阻止,則不會發(fā)生檢查,Valgrind調(diào)度程序?qū)⒉粫{(diào)用gdbserver。在這種情況下,vgdb中繼應(yīng)用程序?qū)ⅰ皬?qiáng)制”調(diào)用gdbserver,而不需要Valgrind調(diào)度程序的干預(yù)。
這種強(qiáng)制調(diào)用Valgrind gdbserver是由vgdb使用ptrace系統(tǒng)調(diào)用實現(xiàn)的。在正確實現(xiàn)的內(nèi)核上,由vgdb完成的ptrace調(diào)用不會影響在Valgrind下運(yùn)行的程序的行為。然而,如果他們這樣做,給--max-invoke-ms=0vgdb中繼應(yīng)用程序的選項將禁用ptrace調(diào)用的使用。禁用vgdb中的ptrace使用的結(jié)果是,在系統(tǒng)調(diào)用中阻塞的Valgrind進(jìn)程不能從GDB喚醒或中斷,直到它執(zhí)行足夠的基本塊,以使Valgrind調(diào)度程序的正常檢查生效。
當(dāng)在vgdb中禁用ptrace時,可以通過為該選項賦予較低的值來增加Valgrind gdbserver對命令或中斷的響應(yīng)--vgdb-poll。如果您的應(yīng)用程序在大多數(shù)情況下在系統(tǒng)調(diào)用中被阻止,則使用非常低的值--vgdb-poll會導(dǎo)致更快地調(diào)用gdbserver。Valgrind的調(diào)度程序執(zhí)行的gdbserver輪詢非常有效,因此增加的查詢頻率不應(yīng)導(dǎo)致嚴(yán)重的性能下降。
當(dāng)在vgdb中禁用ptrace時,由GDB發(fā)送的查詢數(shù)據(jù)包可能需要大量時間才能由Valgrind gdbserver處理。在這種情況下,GDB可能會遇到協(xié)議超時。為避免這種情況,您可以使用GDB命令“set remotetimeout”來增加超時值。
Ubuntu 10.10及更高版本可能會將ptrace的范圍限制為調(diào)用ptrace的進(jìn)程的子進(jìn)程。由于Valgrind進(jìn)程不是vgdb的子進(jìn)程,因此這種受限制的范圍會導(dǎo)致ptrace調(diào)用失敗。為避免這種情況,Valgrind將自動允許屬于同一用戶標(biāo)識符的所有進(jìn)程通過使用PR_SET_PTRACER“追蹤”Valgrind進(jìn)程。
系統(tǒng)調(diào)用中阻止的取消阻止進(jìn)程目前在Mac OS X和Android上尚未實施。因此,您無法連接或中斷Mac OS X或Android系統(tǒng)調(diào)用中阻止的進(jìn)程。
-
更改寄存器值。
當(dāng)線程處于狀態(tài)Runnable或Yielding時,Valgrind gdbserver將僅修改線程的寄存器的值。在其他狀態(tài)(通常為WaitSys)中,嘗試更改寄存器值將失敗。除此之外,這意味著對系統(tǒng)調(diào)用中的線程不執(zhí)行劣質(zhì)調(diào)用,因為Valgrind gdbserver不會執(zhí)行系統(tǒng)調(diào)用重啟。
-
不支持的GDB功能。
GDB提供了大量的調(diào)試功能,并沒有全部支持。具體來說,不支持以下內(nèi)容:可逆調(diào)試和跟蹤點。
-
未知的限制或問題。
GDB,Valgrind和Valgrind gdbserver的組合可能具有未知的其他限制和問題。如果遇到奇怪或意想不到的行為,請隨時報告錯誤。但首先請確認(rèn)GDB或GDB遠(yuǎn)程協(xié)議不是固有的限制或問題。您可以通過檢查使用GDB包的標(biāo)準(zhǔn)gdbserver部分時的行為來執(zhí)行此操作。
3.2.9。vgdb命令行選項
用法:?vgdb [OPTION]... [[-c] COMMAND]...
vgdb(“Valgrind to GDB”)是一個小程序,用作Valgrind和GDB或shell之間的中介。因此,它有兩種使用模式:
作為獨(dú)立實用程序,它從shell命令行使用,將監(jiān)視器命令發(fā)送到在Valgrind下運(yùn)行的進(jìn)程。對于此用法,vgdb OPTION(s)必須跟隨monitor命令發(fā)送。要發(fā)送多個命令,請使用該-c選項分隔。
結(jié)合GDB“target remote |”?命令,它用作GDB和Valgrind gdbserver之間的中繼應(yīng)用程序。對于這種用法,只能給出OPTION,但不能給出COMMAND。
vgdb?接受以下選項:
--pid=<number>指定vgdb必須連接到的進(jìn)程的PID。如果可以連接多個Valgrind gdbserver,則此選項很有用。如果--pid沒有給出參數(shù)并且多個Valgrind gdbserver進(jìn)程正在運(yùn)行,vgdb將報告這些進(jìn)程的列表,然后退出。
如果要更改用于Valgrind gdbserver和vgdb之間的通信的FIFO(命名管道)的默認(rèn)前綴,則必須將其賦予Valgrind和vgdb。
指示vgdb以指定的秒數(shù)搜索可用的Valgrind gdbservers。這樣可以啟動一個vgdb進(jìn)程,然后再啟動與目標(biāo)vgdb進(jìn)行通信的Valgrind gdbserver。當(dāng)與--vgdb-prefix您要等待的進(jìn)程唯一的情況一起使用時,此選項很有用。另外,如果--wait在GDB“target remote”命令中使用參數(shù),則必須將GDB remotetimeout設(shè)置為大于-wait參數(shù)值的值。有關(guān)--max-invoke-ms設(shè)置remotetimeout值的示例,請參閱選項?(如下)。
提供毫秒數(shù),之后vgdb將強(qiáng)制調(diào)用嵌入在Valgrind中的gdbserver。默認(rèn)值為100毫秒。值為0將禁用強(qiáng)制調(diào)用。當(dāng)vgdb連接到Valgrind gdbserver時,使用強(qiáng)制調(diào)用,Valgrind進(jìn)程在系統(tǒng)調(diào)用中阻止其所有線程。
如果指定了較大的值,則可能需要將GDB“remotetimeout”的值從其默認(rèn)值2秒增加。您應(yīng)該確保超時(以秒為單位)大于該--max-invoke-ms值。例如,對于--max-invoke-ms=5000以下GDB命令是合適的:
(gdb)set remotetimeout 6指示獨(dú)立的vgdb退出,如果連接到其中的Valgrind gdbserver不以指定的秒數(shù)處理命令。默認(rèn)值是永遠(yuǎn)不會超時。
指示vgdb使用tcp / ip并在指定的端口nr上監(jiān)聽GDB,而不是使用管道與GDB進(jìn)行通信。使用tcp / ip允許在一臺計算機(jī)上運(yùn)行GDB并調(diào)試在另一臺目標(biāo)計算機(jī)上運(yùn)行的Valgrind進(jìn)程。例:
#在目標(biāo)計算機(jī)上,使用valgrind啟動您的程序 valgrind --vgdb-error = 0 prog 然后在另一個shell中運(yùn)行: vgdb --port = 1234在承載GDB的計算機(jī)上,執(zhí)行以下命令:
gdb程序 (gdb)target remote targetip:1234其中targetip是目標(biāo)計算機(jī)的IP地址或主機(jī)名。
要向獨(dú)立的vgdb提供多個命令,請通過選項分隔命令-c。例:
vgdb v.set log_output -c leak_check any指示獨(dú)立的vgdb報告運(yùn)行的Valgrind gdbserver進(jìn)程的列表,然后退出。
指示獨(dú)立的vgdb顯示Valgrind gdbserver使用的共享內(nèi)存的狀態(tài)。顯示Valgrind gdbserver共享內(nèi)存狀態(tài)后,vgdb將退出。
指示vgdb產(chǎn)生調(diào)試輸出。提供多個-d參數(shù)以增加詳細(xì)程度。當(dāng)給予-d中繼vgdb時,您最好將vgdb的標(biāo)準(zhǔn)錯誤(stderr)重定向到一個文件,以避免GDB和vgdb調(diào)試輸出之間的交互。
3.2.10。Valgrind監(jiān)視器命令
本節(jié)介紹Valgrind監(jiān)視器命令,無論選擇了Valgrind工具,都可以使用。有關(guān)特定于工具的命令,請參閱Memcheck Monitor命令,?Helgrind Monitor命令,?Callgrind Monitor命令和?Massif Monitor命令。
監(jiān)視器命令可以通過使用獨(dú)立的vgdb或從GDB通過使用GDB的“監(jiān)視器”命令(請參閱由Valgrind gdbserver監(jiān)視命令處理)從shell命令行發(fā)送。客戶端程序也可以使用VALGRIND_MONITOR_COMMAND客戶端請求啟動它們。
-
help [debug]指示Valgrind的gdbserver給出Valgrind內(nèi)核和工具的所有監(jiān)視器命令的列表。可選的“debug”參數(shù)還指出了針對Valgrind內(nèi)部調(diào)試的監(jiān)視器命令的幫助。
-
v.info all_errors?顯示到目前為止發(fā)現(xiàn)的所有錯誤。
-
v.info last_error?顯示最后發(fā)現(xiàn)的錯誤。
-
v.info location <addr>輸出位置信息<addr>。可能的描述如下:全局變量,本地(堆棧)變量,分配或釋放的塊,...生成的信息取決于工具和給予valgrind的選項。一些工具(例如memcheck和helgrind)可以為客戶端堆塊生成更詳細(xì)的信息。例如,這些工具顯示堆棧跟蹤分配堆塊的位置。如果一個工具不能替代malloc / free / ...函數(shù),則不會描述客戶機(jī)堆塊。使用該選項--read-var-info=yes獲取有關(guān)全局或本地(堆棧)變量的更詳細(xì)信息。
(gdb)monitor v.info位置0x8050b20位置0x8050b20是全局var“mx”內(nèi)的0個字節(jié)在tc19_shadowmem.c上聲明:19(gdb)mo v.in loc 0x582f33c位置0x582f33c是0字節(jié)的局部變量“info”在tc19_shadowmem.c:282,在線程3的框架#1中聲明 (GDB) -
v.info n_errs_found [msg]顯示到目前為止發(fā)現(xiàn)的錯誤的數(shù)量,到目前為止顯示的錯誤的nr和--vgdb-error參數(shù)的當(dāng)前值。附加可選?msg(一個或多個單詞)。通常,這可以用于在進(jìn)程輸出文件之間的幾個測試之間插入標(biāo)記,序列中只有一個進(jìn)程啟動。這允許將Valgrind報告的錯誤與產(chǎn)生這些錯誤的特定測試相關(guān)聯(lián)。
-
v.info open_fds顯示打開的文件描述符和與文件描述符相關(guān)的詳細(xì)信息的列表。這只有--track-fds=yes?在Valgrind啟動時才能使用。
-
v.set {gdb_output | log_output | mixed_output}允許重定向Valgrind輸出(例如工具檢測到的錯誤)。默認(rèn)設(shè)置為?mixed_output。
使用mixed_outputValgrind輸出到Valgrind日志(通常是stderr),而GDB顯示交互式GDB監(jiān)視器命令(eg?v.info last_error)的輸出。
由于gdb_outputGDB顯示了Valgrind輸出和交互式GDB監(jiān)視器命令輸出。
使用log_outputValgrind輸出和交互式GDB監(jiān)視器命令輸出將轉(zhuǎn)到Valgrind日志。
-
v.wait [ms (default 0)]指示Valgrind gdbserver以毫秒為單位睡眠“ms”,然后繼續(xù)。當(dāng)從獨(dú)立的vgdb發(fā)送時,如果這是最后一個命令,則Valgrind進(jìn)程將繼續(xù)執(zhí)行客戶機(jī)進(jìn)程。典型的用法是使用vgdb向Valgrind gdbserver發(fā)送一個“no-op”命令,以便繼續(xù)執(zhí)行g(shù)uest虛擬機(jī)進(jìn)程。
-
v.kill請求gdbserver終止進(jìn)程。這可以從獨(dú)立的vgdb使用,以正確地殺死當(dāng)前正在期待vgdb連接的Valgrind進(jìn)程。
-
v.set vgdb-error <errornr>?動態(tài)地更改--vgdb-error參數(shù)的值?。典型的用法是--vgdb-error=0從命令行開始?,然后設(shè)置幾個斷點,將vgdb-error值設(shè)置為一個巨大的值,并繼續(xù)執(zhí)行。
以下Valgrind監(jiān)視器命令可用于調(diào)查Valgrind或其gdbserver在發(fā)生問題或錯誤時的行為。
-
v.do expensive_sanity_check_general?執(zhí)行各種理智檢查。特別是Valgrind堆的理智得到驗證。如果您懷疑您的程序和/或Valgrind有一個錯誤的Valgrind數(shù)據(jù)結(jié)構(gòu)錯誤,這將非常有用。當(dāng)Valgrind工具向連接的GDB報告客戶端錯誤時,也可以使用它,以便在繼續(xù)執(zhí)行之前驗證Valgrind的完整性。
-
v.info gdbserver_status顯示gdbserver狀態(tài)。在出現(xiàn)問題(例如通信)的情況下,這將顯示一些相關(guān)Valgrind gdbserver內(nèi)部變量的值。請注意,與斷點和觀察點相關(guān)的變量(例如斷點地址數(shù)和觀察點數(shù))將為零,因為默認(rèn)情況下,GDB將在執(zhí)行停止時刪除所有觀察點和斷點,并在恢復(fù)執(zhí)行時重新插入調(diào)試過程。您可以使用GDB命令更改此GDB行為?set breakpoint always-inserted on。
-
v.info memory [aspacemgr]顯示了Valgrind內(nèi)部堆管理的統(tǒng)計信息。如果提供了選項--profile-heap=yes,將會輸出詳細(xì)的統(tǒng)計數(shù)據(jù)。使用可選參數(shù)?aspacemgr。將輸出由valgrind地址空間管理器維護(hù)的段列表。請注意,此列表的段始終在Valgrind日志中輸出。
-
v.info exectxt顯示有關(guān)Valgrind記錄的“可執(zhí)行上下文”(即堆棧跟蹤)的信息。對于一些程序,Valgrind可以記錄非常多的這種堆棧跟蹤,導(dǎo)致高內(nèi)存使用。此監(jiān)視器命令顯示所有記錄的堆棧跟蹤,然后顯示一些統(tǒng)計信息。這可以用來分析大量堆棧跟蹤的原因。通常,如果v.info memory“exectxt”競技場顯示出顯著的內(nèi)存使用,您將使用此命令。
-
v.info scheduler顯示有關(guān)線程的各種信息。首先,它輸出主機(jī)棧跟蹤,即正在執(zhí)行的Valgrind代碼。然后,對于每個線程,它將輸出線程狀態(tài)。對于未終止的線程,狀態(tài)后面是guest(客戶端)堆棧跟蹤。最后,對于每個活動線程或?qū)τ谏形粗匦率褂玫拿總€終止的線程槽,它顯示valgrind堆棧的最大使用。
顯示客戶端堆棧跟蹤允許將由Valgrind展開器生成的堆棧跟蹤與由GDB + Valgrind gdbserver生成的堆棧跟蹤進(jìn)行比較。注意GDB和Valgrind調(diào)度器狀態(tài)有自己的線程編號方案。要使GDB線程號和對應(yīng)的Valgrind調(diào)度程序線程號之間的鏈接,請使用GDB命令info threads。此命令的輸出顯示GDB線程號和valgrind'tid'。'tid'是輸出的線程號v.info scheduler。當(dāng)使用callgrind工具時,callgrind monitor命令?status輸出內(nèi)部callgrind信息,關(guān)于它保存的堆棧/調(diào)用圖。
-
v.info stats顯示各種valgrind核心和工具統(tǒng)計。有了這個,Valgrind和工具統(tǒng)計可以在運(yùn)行時檢查,即使沒有選項--stats=yes。
-
v.info unwind <addr> [<len>]顯示地址范圍[addr,addr + len-1]的CFI展開調(diào)試信息。默認(rèn)值<len>為1,給出了<addr>中的指令的展開信息。
-
v.set debuglog <intvalue>將Valgrind調(diào)試日志級別設(shè)置為<intvalue>。這允許動態(tài)地更改Valgrind的日志級別,例如當(dāng)檢測到問題時。
-
v.set hostvisibility [yes*|no]值“yes”表示gdbserver指示GDB可以查看Valgrind的“主機(jī)”(內(nèi)部)狀態(tài)/內(nèi)存。“否”禁用此訪問權(quán)限。當(dāng)啟動主機(jī)時,GDB可以查看Valgrind全局變量。例如,要在x86上檢查memcheck工具的Valgrind全局變量,請執(zhí)行以下設(shè)置:
(gdb)monitor v.set hostvisibility yes (gdb)add-symbol-file / path / to / tool / executable / file / memcheck-x86-linux 0x38000000 從文件“/ path / to / tool / executable / file / memcheck-x86-linux”中添加符號表.text_addr = 0x38000000 (y或n)y 從/path/to/tool/executable/file/memcheck-x86-linux...done讀取符號。 (GDB)之后,可以訪問memcheck-x86-linux中定義的變量,例如
(gdb)p / x vgPlain_threads [1] .os_state $ 3 = {lwpid = 0x4688,threadgroup = 0x4688,parent = 0x0, valgrind_stack_base = 0x62e78000,valgrind_stack_init_SP = 0x62f79fe0, exitcode = 0x0,fatalsig = 0x0} (gdb)p vex_control $ 5 = {iropt_verbosity = 0,iropt_level = 2, iropt_register_updates = VexRegUpdUnwindregsAtMemAccess, iropt_unroll_thresh = 120,guest_max_insns = 60,guest_chase_thresh = 10, guest_chase_cond = 0'\ 000'} (GDB) -
v.translate <address> [<traceflags>]顯示address包含給定跟蹤標(biāo)志的塊的翻譯。該traceflags值位模式也有類似的含義Valgrind的的?--trace-flags選項。它可以十六進(jìn)制(例如0x20)或十進(jìn)制(例如32)或二進(jìn)制1s和0s位(例如0b00100000)給出。traceflags的默認(rèn)值為0b00100000,對應(yīng)于“儀表后顯示”。該命令的輸出總是進(jìn)入Valgrind日志。
附加位標(biāo)志0b100000000(位8)在--trace-flags選項中沒有等效項。它可以跟蹤gdbserver特定的儀器。請注意,該位8只能在跟蹤中添加gdbserver儀器。如果gdbserver工具由于某些其他原因而處于活動狀態(tài),則將其設(shè)置為0將不會禁用跟蹤,例如因為此地址處有斷點或因為gdbserver處于單步執(zhí)行模式。
3.3。功能包裝
Valgrind允許調(diào)用某些指定的函數(shù)被攔截并重新路由到不同的用戶提供的函數(shù)。這可以做任何它喜歡的,通常檢查的論據(jù),向原來的,并可能檢查結(jié)果。可以包裝任何數(shù)量的功能。
函數(shù)換行對于以某種方式對API進(jìn)行測試非常有用。例如,Helgrind在POSIX pthreads API中包裝函數(shù),因此可以了解線程狀態(tài)更改,核心能夠?qū)⒐δ馨贛PI(消息傳遞)API中,以便知道與消息到達(dá)/離開。這些信息通常通過在包裝函數(shù)中使用客戶端請求傳遞給Valgrind,盡管確切的機(jī)制可能會有所不同。
3.3.1。一個簡單的例子
假設(shè)我們要包裝一些功能
int foo(int x,int y){return x + y; }包裝器是相同類型的函數(shù),但是具有將其標(biāo)識為包裝的特殊名稱foo。包裝器需要包括支持宏valgrind.h。這是一個簡單的包裝器,它打印參數(shù)和返回值:
#include <stdio.h> #include“valgrind.h” int I_WRAP_SONAME_FNNAME_ZU(NONE,foo)(int x,int y) {int結(jié)果;OrigFn fn;VALGRIND_GET_ORIG_FN(FN);printf(“foo's wrapper:args%d%d \ n”,x,y);CALL_FN_W_WW(result,fn,x,y);printf(“foo's wrapper:result%d \ n”,result);返回結(jié)果; }為了變得活躍,包裝器只需要存在于與其包裝的函數(shù)相同的進(jìn)程'地址空間中的文本部分中,并且其ELF符號名稱對于Valgrind可見。在實踐中,這意味著要么編譯到一個?.o和它連接,或編譯的.so和?LD_PRELOAD在荷蘭國際集團(tuán)它。后者是更方便的,因為它不要求重新鏈接。
所有包裝紙都有上述形式。有三個關(guān)鍵的宏:
I_WRAP_SONAME_FNNAME_ZU:這將生成包裝器的真實名稱。這是Valgrind讀取符號表信息時注意到的編碼名稱。它說的是:我是一個命名的函數(shù)的包裝器,foo它在ELF共享對象中找到一個空(“?NONE”)soname字段。規(guī)范機(jī)制是強(qiáng)大的,因為通配符可以用于聲名和函數(shù)名。細(xì)節(jié)將在下面討論。
VALGRIND_GET_ORIG_FN:一旦在包裝器中,首先要抓住原始地址(以及需要的任何其他支持信息)。它存儲在不透明類型的值中OrigFn。使用信息獲取?VALGRIND_GET_ORIG_FN。在調(diào)用同一個線程中的任何其他包裝函數(shù)之前,使這個宏調(diào)用非常重要。
CALL_FN_W_WW:最終我們想要調(diào)用被包裝的函數(shù)。直接調(diào)用它不起作用,因為這只是讓我們回到包裝器并導(dǎo)致無限循環(huán)。相反,結(jié)果lvalue?OrigFn和參數(shù)被交給一個形式的宏的家庭之一?CALL_FN_*。這些導(dǎo)致Valgrind調(diào)用原來的,并避免遞歸回包裝。
3.3.2。包裝規(guī)格
這個方案具有獨(dú)立的優(yōu)點。可以以正常方式編譯包裝器的對象代碼,并且不依賴外部腳本告訴Valgrind哪個包裝器涉及哪些原件。
每個包裝器都有一個名字,在最常見的情況下說:我是名稱匹配FNPATT,其ELF“soname”匹配SOPATT的任何函數(shù)的包裝器。FNPATT和SOPATT都可以包含通配符(星號)和其他通常不被視為有效的C標(biāo)識符名稱的字符(空格,點,@等)。
需要這種靈活性才能為POSIX pthread函數(shù)編寫強(qiáng)大的包裝器,通常我們不完全確定函數(shù)名稱或soname,或者我們想要一次包裝一整套函數(shù)。
例如,pthread_create?在GNU libpthread中通常是一個版本化的符號 - 一個名字以...結(jié)尾的符號?@GLIBC_2.3。因此,我們不確定它的真實姓名。我們也想覆蓋任何形式的聲納libpthread.so*。所以包裝的標(biāo)題將是
int I_WRAP_SONAME_FNNAME_ZZ(libpthreadZdsoZd0,pthreadZucreateZAZa)(...正式...){ ... 身體 ... }為了將不尋常的字符寫為有效的C函數(shù)名稱,使用Z編碼方案。名稱是字面上的,除了一個首都Z作為轉(zhuǎn)義字符,具有以下編碼:
Za編碼*Zp +Zc:Zd。祖_Zh - Zs(空格)ZA @ZZ ZZL(#僅在valgrind 3.3.0及更高版本ZR)#只在valgrind 3.3.0及更高版本因此libpthreadZdsoZd0是的soname的編碼libpthread.so.0?和pthreadZucreateZAZa是函數(shù)名的編碼pthread_create@*。
宏I_WRAP_SONAME_FNNAME_ZZ?構(gòu)造了一個包裝器名稱,其中soname(第一個組件)和函數(shù)名稱(第二個組件)都是Z編碼的。編碼函數(shù)名稱可能是令人厭煩的,并且通常是不必要的,因此I_WRAP_SONAME_FNNAME_ZU可以使用第二個宏?。該_ZU變體對于為C ++函數(shù)編寫包裝也是有用的,其中函數(shù)名通常使用一些其他約定在Z中發(fā)揮重要作用而被破壞。不得不第二次編碼很快就會變得混亂。
由于函數(shù)名字段可能包含通配符,它??可以是任何東西,包括只是*。soname也是如此。但是,一些ELF對象(特別是主要的可執(zhí)行文件)沒有聲名。任何缺少soname的對象都被視為它的soname?NONE,這就是為什么上面的原始例子有一個名字?I_WRAP_SONAME_FNNAME_ZU(NONE,foo)。
請注意,ELF對象的soname與其文件名不同,盡管它通常是相似的。您可以libfoo.so使用該命令?找到對象的soname?readelf -a libfoo.so | grep soname。
3.3.3。包裝語義
包裝器替代無限功能系列的能力是強(qiáng)大的,但是在ELF對象出現(xiàn)和消失的情況下(dlopen'd和dlclose'd)在飛行中會帶來復(fù)雜性。Valgrind試圖在這種情況下保持明智的行為。
例如,假設(shè)一個進(jìn)程已經(jīng)削減(帶有soname的ELF對象)object1.so,其中包含?function1。它function1立即開始使用?。
過了一會兒,它變得渾濁wrappers.so,里面包含一個包裝function1(soname)?object1.so。所有后續(xù)調(diào)用?function1都將重新路由到包裝器。
如果wrappers.so是以后dlclose'd,呼叫function1自然地路由到原來的。
或者,如果object1.so?是dlclose'd但wrappers.so仍然保留,則導(dǎo)出的包裝器將wrappers.so?變?yōu)椴换顒拥?#xff0c;因為無法獲取 - 沒有原始文件可以再調(diào)用。然而,Valgrind記得包裝仍然存在。如果?object1.so最終再次出現(xiàn),包裝將再次活躍。
簡而言之,valgrind會檢查所有代碼加載/卸載事件,以確保當(dāng)前活動的包裝器集合保持一致。
第二個可能的問題是沖突的包裝器。很容易加載兩個或更多的包裝紙,這兩個包裝紙都稱為包裝紙,用于第三個功能。在這種情況下,Valgrind會在第二次出現(xiàn)時抱怨有沖突的包裝者,并且只會榮獲第一名。
3.3.4。調(diào)試
弄清楚發(fā)生了什么事情,因為包裝的動態(tài)性質(zhì)可能很困難。該?--trace-redir=yes選項可以通過在每個mmap/?munmap?事件影響代碼(文本)后顯示重定向子系統(tǒng)的完整狀態(tài)?。
有兩個中心概念:
-
“重定向規(guī)范”是一個(soname pattern,fnname pattern)對與一個代碼地址的綁定。這些綁定是通過使用I_WRAP_SONAME_FNNAME_{ZZ,_ZU}?宏創(chuàng)建的名稱編寫函數(shù)創(chuàng)建的?。
-
“活動重定向”是當(dāng)前有效的代碼地址綁定的代碼地址。
包裝和重定向子系統(tǒng)的狀態(tài)包括一組規(guī)范和一組主動綁定。通過觀看?代碼(文本)部分的所有mmap/?munmap事件來獲取/丟棄規(guī)格?。從規(guī)范中重新計算主動綁定集,以及對規(guī)范集進(jìn)行任何更改后,所有已知的符號名稱。
--trace-redir=yes?在任何此類事件之后顯示兩個集合的內(nèi)容。
-v?每次首次使用活動規(guī)范時,都會打印一行文本。
因此,為了最大化調(diào)試效率,您將需要使用這兩個選項。
一個最后的評論。功能包裝設(shè)備與Valgrind的替換(重定向)指定功能的能力密切相關(guān),例如將調(diào)用重定向?malloc到其自身的實現(xiàn)。實際上,替換功能可以被認(rèn)為是不稱為原始的包裝功能。然而,為了使實現(xiàn)更加健壯,兩種截取(包裝與替換)的處理方式不同。
--trace-redir=yes顯示替換和包裝功能的規(guī)范和綁定。為了區(qū)分兩者,使用打印R->包裝打印替換綁定?W->。
3.3.5。限制 - 控制流程
在大多數(shù)情況下,函數(shù)包裝實現(xiàn)是強(qiáng)大的。唯一重要的注意事項是:在包裝器中,在調(diào)用任何其他包裝功能之前,請OrigFn使用該信息?VALGRIND_GET_ORIG_FN。一旦你有了?OrigFn,任意調(diào)用之間的遞歸和longjumps之間的包裝應(yīng)該正常工作。包裝函數(shù)之間從來沒有任何交互,只是替換函數(shù)(例如malloc),所以你可以malloc從包裝器中安全地調(diào)用?etc。
上述評論對于{x86,amd64,ppc32,arm,mips32,s390} -linux是正確的。由于ppc64-linux ABI(可能設(shè)計不佳)ppc64-linux功能的包裝更加脆弱。這要求使用一個影子堆棧來跟蹤包裝和替換功能的條目/出口。這給出了兩個限制:首先,包裝器的長時間跳過會迅速導(dǎo)致災(zāi)難,因為陰影堆棧將無法正確清除。其次,由于陰影棧具有有限的大小,因此包裝/替換功能之間的遞歸僅可能達(dá)到有限的深度,Valgrind不得不中止運(yùn)行。這個深度目前是16個電話。
對于所有平臺({x86,amd64,ppc32,ppc64,arm,mips32,s390} -linux),所有上述注釋都適用于每個線程。換句話說,包裝是線程安全的:每個線程必須單獨(dú)遵守上述限制,但不需要進(jìn)行任何類型的跨線程合作。
3.3.6。限制 - 原始功能簽名
如上面的例子所示,要調(diào)用原來你必須使用宏的形式CALL_FN_*。由于技術(shù)原因,無法創(chuàng)建單個宏來處理所有參數(shù)類型和數(shù)字,因此提供了覆蓋最常見情況的一系列宏。在下文中,'W'表示機(jī)器字型的值(指針或C?long),'v'表示C的void類型。目前可用的宏是:
CALL_FN_v_v - 調(diào)用類型為void fn(void)的原始文件 CALL_FN_W_v - 調(diào)用long fn(void)類型的原始文件CALL_FN_v_W - 調(diào)用類型為void fn(long)的原始文件 CALL_FN_W_W - 調(diào)用long fn(long)類型的原始文件CALL_FN_v_WW - 調(diào)用類型為void fn(long,long)的原始文件 CALL_FN_W_WW - 調(diào)用long fn(long,long)類型的原始文件CALL_FN_v_WWW - 調(diào)用類型為void fn(long,long,long)的原始文件 CALL_FN_W_WWW - 調(diào)用long fn(long,long,long)CALL_FN_W_WWWW - 調(diào)用long fn(long,long,long,long)類型的原始文件 CALL_FN_W_5W - 調(diào)用long fn(long,long,long,long,long) CALL_FN_W_6W - 調(diào)用long fn(long,long,long,long,long,long) 等等,直到 CALL_FN_W_12W可以根據(jù)需要擴(kuò)展支持的類型集。令人遺憾的是,這種限制存在。功能包裝已被證明難以實施,具有一定顯然不可避免的水平。經(jīng)過幾次實施嘗試,目前的安排似乎是最不利的權(quán)衡。至少它在動態(tài)鏈接和動態(tài)代碼加載/卸載的情況下可靠地工作。
您不應(yīng)嘗試用不同類型簽名的包裝器包裝一個類型簽名的功能。這樣的詭計一定會導(dǎo)致崩潰或奇怪的行為。這不是函數(shù)包裝實現(xiàn)的限制,只是反映了如果你不小心,它給你掃射的力量來射擊自己。想象一下,您可以通過編寫一個與任何soname中的任何函數(shù)名稱相匹配的包裝器,實際上就是一個聲稱是進(jìn)程中所有函數(shù)的包裝器的破壞。
3.3.7。例子
在源代碼樹中,?memcheck/tests/wrap[1-8].c提供了一系列的例子,從很簡單到相當(dāng)先進(jìn)。
mpi/libmpiwrap.c是一個包裝大而復(fù)雜的API(MPI-2接口)的例子。這個文件定義了近300個不同的包裝器。
總結(jié)
以上是生活随笔為你收集整理的使用和了解Valgrind核心:高级主题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Burnside引理与Pólya定理
- 下一篇: 嵌入式软件设计第12次实验报告-1402