青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

posts - 319, comments - 22, trackbacks - 0, articles - 11
  C++博客 :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

Linux on Power 上的調(diào)試工具和技術(shù)

Calvin Sze (calvins@us.ibm.com), Linux 顧問 , EMC

簡介: 調(diào)試是一項(xiàng)主要的軟件開發(fā)活動,作為應(yīng)用程序開發(fā)人員,您無法避免對程序進(jìn)行調(diào)試。有效的調(diào)試不僅能縮短軟件開發(fā)周期,而且可以節(jié)省成本。本文簡要介紹了在用戶空間的 C/C++ 和 Java? 應(yīng)用程序中查找 bug 的技術(shù),并介紹了一些可以在 Linux? for POWER? 架構(gòu)上使用的調(diào)試技術(shù)。

本文的標(biāo)簽:  硬件平臺調(diào)試

發(fā)布日期: 2005 年 8 月 29 日 
級別: 中級 
訪問情況 3500 次瀏覽 
建議: 0 (添加評論)

1 star2 stars3 stars4 stars5 stars 平均分 (共 6 個(gè)評分 )

簡介

調(diào)試程序有很多方法,例如向屏幕上打印消息,使用調(diào)試器,或者只需仔細(xì)考慮程序如何運(yùn)行,并對問題進(jìn)行有根有據(jù)的猜測。

在修復(fù) bug 之前,首先要確定在源程序中的位置。例如,當(dāng)一個(gè)程序產(chǎn)生崩潰或生成核心轉(zhuǎn)儲(core dump)時(shí),您就需要了解是哪行代碼發(fā)生了崩潰。在找到有問題的代碼行之后,就可以確定這個(gè)函數(shù)中變量的值,函數(shù)是如何調(diào)用的,更具體點(diǎn)說,為什么會發(fā)生這種錯誤。使用調(diào)試器查找這些信息非常簡單。

本文將簡要介紹幾種用于修復(fù)一些很難通過可視化地檢查代碼而發(fā)現(xiàn)的 bug 的技術(shù),并闡述了如何使用在 Linux on Power 架構(gòu)上可用的工具。

調(diào)試內(nèi)存問題的工具和技術(shù)

動態(tài)內(nèi)存分配看起來似乎非常簡單:您可以根據(jù)需要分配內(nèi)存 —— 使用 malloc() 或其變種 —— 并在不需要時(shí)釋放這些內(nèi)存。實(shí)際上,內(nèi)存管理的問題是軟件中最為常見的 bug,因?yàn)橥ǔT诔绦騿訒r(shí)這些問題并不明顯。例如,程序中的內(nèi)存泄漏可能開始并不為人注意,直到經(jīng)過多天甚至幾個(gè)月的運(yùn)行才會被發(fā)現(xiàn)。接下來的幾節(jié)將簡要介紹如何使用流行的調(diào)試器 Valgrind 來發(fā)現(xiàn)并調(diào)試這些最常見的內(nèi)存 bug。

在開始使用任何調(diào)試工具之前,請考慮這個(gè)工具是否對重新編譯應(yīng)用程序有益,是否可以支持具有調(diào)試信息的庫(-g 選項(xiàng))。如果沒有啟用調(diào)試信息,調(diào)試工具可以做的最好的事情也不過是猜測一段特定的代碼是屬于哪個(gè)函數(shù)的。這使得錯誤消息和概要分析輸出幾乎沒有什么用處。使用 -g 選項(xiàng),您就有可能獲得一些信息來直接指出相關(guān)的代碼行。

Valgrind

Valgrind 已經(jīng)在 Linux 應(yīng)用程序開發(fā)社區(qū)中廣泛用來調(diào)試應(yīng)用程序。它尤其擅長發(fā)現(xiàn)內(nèi)存管理的問題。它可以檢查程序運(yùn)行時(shí)的內(nèi)存泄漏問題。這個(gè)工具目前正由 Julian Seward 進(jìn)行開發(fā),并由 Paul Mackerras 移植到了 Power 架構(gòu)上。

要安裝 Valgrind,請從 Valgrind 的 Web 站點(diǎn)上下載源代碼(參閱 參考資料)。切換到 Valgrind 目錄,并執(zhí)行下面的命令:

# make
# make check
# make install

Valgrind 的錯誤報(bào)告

Valgrind 的輸出格式如下:


清單 1. Valgrind 的輸出消息
                
# valgrind du –x –s
.
.
==29404==  Address 0x1189AD84 is 0 bytes after a block of size 12 alloc'd
==29404==    at 0xFFB9964: malloc (vg_replace_malloc.c:130)
==29404==    by 0xFEE1AD0: strdup (in /lib/tls/libc.so.6)
==29404==    by 0xFE94D30: setlocale (in /lib/tls/libc.so.6)
==29404==    by 0x10001414: main (in /usr/bin/du)

==29404== 是進(jìn)程的 ID。消息 Address 0x1189AD84 is 0 bytes after a block of size 12 alloc'd 說明在這個(gè) 12 字節(jié)的數(shù)組后面沒有存儲空間了。第二行以及后續(xù)幾行說明內(nèi)存是在 130 行(vg_replace_malloc.c)的 strdup() 程序中進(jìn)行分配的。strdup() 是在 libc.so.6 庫的 setlocale() 中調(diào)用的;main() 調(diào)用了 setlocale()

未初始化的內(nèi)存

最為常見的一個(gè) bug 是程序使用了未初始化的內(nèi)存。未初始化的數(shù)據(jù)可能來源于:

  • 未經(jīng)初始化的變量
  • malloc 函數(shù)所分配的數(shù)據(jù),在寫入值之前使用了

下面這個(gè)例子使用了一個(gè)未初始化的數(shù)組:


清單 2. 使用未初始化的內(nèi)存
                
      2 {
      3         int i[5];
      4 
      5         if (i[0] == 0)
      6                 i[1]=1;
      7         return 0;
      8 }

在這個(gè)例子中,整數(shù)數(shù)組 i[5] 沒有進(jìn)行初始化;因此,i[0] 包含的是一個(gè)隨機(jī)數(shù)。因此使用 i[0] 的值來判斷一個(gè)條件分支就會導(dǎo)致不可預(yù)期的問題。Valgrind 可以很容易捕獲這種錯誤條件。當(dāng)您使用 Valgrind 運(yùn)行這個(gè)程序時(shí),就會接收到下面的消息:


清單 3. Valgrind 的輸出消息
                
# gcc –g –o test1 test1.c
# valgrind ./test1
.
.
==31363== 
==31363== Conditional jump or move depends on uninitialised value(s)
==31363==    at 0x1000041C: main (test1.c:5)
==31363== 
==31363== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 7 from 1)
==31363== malloc/free: in use at exit: 0 bytes in 0 blocks.
==31363== malloc/free: 0 allocs, 0 frees, 0 bytes allocated.
==31363== For counts of detected errors, rerun with: -v
==31363== No malloc'd blocks -- no leaks are possible.

Valgrind 的輸出說明,有一個(gè)條件分支依賴于文件 test1.c 中第 5 行中的一個(gè)未初始化的變量。

內(nèi)存泄漏

內(nèi)存泄漏是另外一個(gè)常見的問題,也是很多程序中最難判斷的問題。內(nèi)存泄漏的主要表現(xiàn)為:當(dāng)程序連續(xù)運(yùn)行時(shí),與程序相關(guān)的內(nèi)存(或堆)變得越來越大。結(jié)果是,當(dāng)這個(gè)程序所消耗的內(nèi)存達(dá)到系統(tǒng)的上限時(shí),就會自己崩潰;或者會出現(xiàn)更嚴(yán)重的情況:掛起或?qū)е孪到y(tǒng)崩潰。下面是一個(gè)有內(nèi)存泄漏 bug 的示例程序:


清單 4. 內(nèi)存泄漏示例
                
      1 int main(void)
      2 {
      3         char *p1;
      4         char *p2;
      5 
      6         p1 = (char *) malloc(512);
      7         p2 = (char *) malloc(512);
      8 
      9         p1=p2;
     10 
     11         free(p1);
     12         free(p2);
     13 }

上面的代碼分別給字符指針 p1 和 p2 分配了兩個(gè) 512 字節(jié)的內(nèi)存塊,然后將指向第一個(gè)內(nèi)存塊的指針設(shè)置為指向第二個(gè)內(nèi)存塊。結(jié)果是,第二個(gè)內(nèi)存塊的地址丟失了,并導(dǎo)致內(nèi)存泄漏。在使用 Valgrind 運(yùn)行這個(gè)程序時(shí),會返回如下的消息:


清單 5. Valgrind 的輸出消息
                
# gcc –g –o test2 test2.c
# valgrind ./test2
.
.
==31468== Invalid free() / delete / delete[]
==31468==    at 0xFFB9FF0: free (vg_replace_malloc.c:152)
==31468==    by 0x100004B0: main (test2.c:12)
==31468== Address 0x11899258 is 0 bytes inside a block of size 512 free'd
==31468==    at 0xFFB9FF0: free (vg_replace_malloc.c:152)
==31468==    by 0x100004A4: main (test2.c:11)
==31468== 
==31468== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 7 from 1)
==31468== malloc/free: in use at exit: 512 bytes in 1 blocks.
==31468== malloc/free: 2 allocs, 2 frees, 1024 bytes allocated.
==31468== For counts of detected errors, rerun with: -v
==31468== searching for pointers to 1 not-freed blocks.
==31468== checked 167936 bytes.
==31468== 
==31468== LEAK SUMMARY:
==31468==    definitely lost: 512 bytes in 1 blocks.
==31468==      possibly lost: 0 bytes in 0 blocks.
==31468==    still reachable: 0 bytes in 0 blocks.
==31468==         suppressed: 0 bytes in 0 blocks.
==31468== Use --leak-check=full to see details of leaked memory.

正如您可以看到的一樣,Valgrind 報(bào)告說這個(gè)程序中有 512 字節(jié)的內(nèi)存丟失了。

非法寫/讀

這種情況發(fā)生在程序試圖對一個(gè)不屬于程序本身的內(nèi)存地址進(jìn)行讀寫時(shí)。在有些系統(tǒng)上,在發(fā)生這種錯誤時(shí),程序會異常結(jié)束,并產(chǎn)生一個(gè)段錯誤。下面這個(gè)例子就是一個(gè)常見的 bug,它試圖讀寫一個(gè)超出數(shù)組邊界的元素。


清單 6. 非法讀寫
                
      1 int main() {
      2         int i, *iw, *ir;
      3 
      4         iw = (int *)malloc(10*sizeof(int));
      5         ir = (int *)malloc(10*sizeof(int));
      6 
      7 
      8         for (i=0; i<11; i++)
      9                 iw[i] = i;
     10 
     11         for (i=0; i<11; i++)
     12                 ir[i] = iw[i];
     13 
     14         free(iw);
     15         free(ir);
     16 } 

從這個(gè)程序中我們可以看出,對于 iw[10] 和 ir[10] 的訪問都是非法的,因?yàn)?nbsp;iw 和 ir 都只有 10 個(gè)元素,分別是從 0 到 9。請注意int iw[10 ] 和 iw = (int *)malloc(10*sizeof(int)) 是等效的 —— 它們都是用來給一個(gè)整數(shù)數(shù)組 iw 分配 10 個(gè)元素。

當(dāng)您使用 Valgrind 運(yùn)行這個(gè)程序時(shí),會返回如下的消息:


清單 7. Valgrind 的輸出消息
                
# gcc –g –o test3 test3.c
# valgrind ./test3
.
.
==31522== Invalid write of size 4
==31522==    at 0x100004C0: main (test3.c:9)
==31522==  Address 0x11899050 is 0 bytes after a block of size 40 alloc'd
==31522==    at 0xFFB9964: malloc (vg_replace_malloc.c:130)
==31522==    by 0x10000474: main (test10.c:4)
==31522== 
==31522== Invalid read of size 4
==31522==    at 0x1000050C: main (test3.c:12)
==31522==  Address 0x11899050 is 0 bytes after a block of size 40 alloc'd
==31522==    at 0xFFB9964: malloc (vg_replace_malloc.c:130)
==31522==    by 0x10000474: main (test10.c:4)
==31522== 
==31522== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 7 from 1)
==31522== malloc/free: in use at exit: 0 bytes in 0 blocks.
==31522== malloc/free: 2 allocs, 2 frees, 84 bytes allocated.
==31522== For counts of detected errors, rerun with: -v
==31522== No malloc'd blocks -- no leaks are possible.

在 test3.c 的第 9 行發(fā)現(xiàn)一個(gè)非法的 4 字節(jié)寫操作,在第 12 行發(fā)現(xiàn)一個(gè)非法的 4 字節(jié)讀操作。

Valgrind 也可以幫助判斷內(nèi)存誤用的問題,例如:

  • 讀/寫已經(jīng)釋放的內(nèi)存
  • C++ 環(huán)境中錯誤地使用 malloc/new 與 free/delete 的配對

下面這個(gè)列表介紹了 POWER 架構(gòu)上 Valgrind 的狀態(tài):

  • memcheck 和 addrcheck 工具都可以很好地工作。然而,其他工具還沒有進(jìn)行大量的測試。另外,Helgrind (一個(gè)數(shù)據(jù)競爭的檢測程序)在 POWER 上尚不能使用。
  • 所有的 32 位 PowerPC? 用戶模式的指令都可以支持,除了兩條非常少用的指令:lswx 和 stswx。具體來說,所有的浮點(diǎn)和 Altivec(VMX)指令都可以支持。
  • Valgrind 可以在 32 位或 64 位 PowerPC/Linux 內(nèi)核上工作,但是只能用于 32 位的可執(zhí)行程序。

有關(guān) Valgrind 內(nèi)存調(diào)試的更多信息,請?jiān)L問 Valgrind HOW TO 站點(diǎn)。還可以參閱 Steve Best 的“Debugging Memory Problems”(Linux Magazine,2003 年 5 月)。參考資料 中有它們的鏈接

除了 Valgrind 之外,還可以使用其他幾個(gè)內(nèi)存調(diào)試工具;例如,Memwatch 和 Electric Fence。

調(diào)試其他程序問題的工具和技術(shù)

除了內(nèi)存 bug 之外,開發(fā)人員通常還會碰到程序雖然能夠成功編譯,但是在運(yùn)行時(shí)卻會產(chǎn)生內(nèi)核轉(zhuǎn)儲或段錯誤的問題。有時(shí)在程序完成之后,程序的輸出可能與所期望或設(shè)計(jì)的不同。在這兩種情況中,可能代碼中存在您認(rèn)為正確而實(shí)際上錯誤的情況。接下來的幾節(jié)中介紹的調(diào)試器將幫助您找到這些情況的原因。

GNU 項(xiàng)目調(diào)試器

GDB(GNU 項(xiàng)目調(diào)試器)可以讓您了解程序在執(zhí)行時(shí)“內(nèi)部” 究竟在干些什么,以及在程序發(fā)生崩潰的瞬間正在做什么。

GDB 做以下 4 件主要的事情來幫助您捕獲程序中的 bug:

  • 在程序啟動之前指定一些可以影響程序行為的變量或條件
  • 在某個(gè)指定的地方或條件下暫停程序
  • 在程序停止時(shí)檢查已經(jīng)發(fā)生了什么
  • 在程序執(zhí)行過程中修改程序中的變量或條件,這樣就可以體驗(yàn)修復(fù)一個(gè) bug 的成果,并繼續(xù)了解其他 bug

要調(diào)試的程序可以是使用 C、C++、Pascal、Objective-C 以及其他很多語言編寫的。GDB 的二進(jìn)制文件名是 gdb。

gdb 中有很多命令。使用 help 命令可以列出所有的命令,以及關(guān)于如何使用這些命令的介紹。下表給出了最常用的 GDB 命令。


表 1. gdb 中最常用的命令
命令說明例子
help顯示命令類別help - 顯示命令類別
help breakpoints - 顯示屬于 breakpoints 類別的命令
help break - 顯示 break 命令的解釋
run啟動所調(diào)試的程序?
kill終止正在調(diào)試的程序的執(zhí)行通常這會在要執(zhí)行的代碼行已經(jīng)超過了您想要調(diào)試的代碼時(shí)使用。執(zhí)行 kill 會重置斷點(diǎn),并從頭再次運(yùn)行這個(gè)程序
cont所調(diào)試的程序運(yùn)行到一個(gè)斷點(diǎn)、異常或單步之后,繼續(xù)執(zhí)行?
info break顯示當(dāng)前的斷點(diǎn)或觀察點(diǎn)?
break在指定的行或函數(shù)處設(shè)置斷點(diǎn)break 93 if i=8 - 當(dāng)變量 i 等于 8 時(shí),在第 93 行停止程序執(zhí)行
Step單步執(zhí)行程序,直到它到達(dá)一個(gè)不同的源代碼行。您可以使用 s 來代表 step 命令?
Next與 step 命令類似,只是它不會“單步跟蹤到”子例程中?
print打印一個(gè)變量或表達(dá)式的值print pointer - 打印變量指針的內(nèi)容
print *pointer - 打印指針?biāo)赶虻臄?shù)據(jù)結(jié)構(gòu)的內(nèi)容
delete刪除某些斷點(diǎn)或自動顯示表達(dá)式delete 1 - 刪除斷點(diǎn) 1。斷點(diǎn)可以通過 info break 來顯示
watch為一個(gè)表達(dá)式設(shè)置一個(gè)觀察點(diǎn)。當(dāng)表達(dá)式的值發(fā)生變化時(shí),這個(gè)觀察點(diǎn)就會暫停程序的執(zhí)行?
where打印所有堆棧幀的棧信息where - 不使用參數(shù),輸出當(dāng)前線程的堆棧信息
where all - 輸出當(dāng)前線程組中所有線程的堆棧信息
where threadindex - 輸出指定線程的堆棧信息
attach開始查看一個(gè)已經(jīng)運(yùn)行的進(jìn)程attach <process_id> - 附加到進(jìn)程 process_id 上。process_id 可以使用 ps 命令找到
info thread顯示當(dāng)前正在運(yùn)行的線程?
thread apply threadno command對一個(gè)線程運(yùn)行 gdb 命令thread apply 3 where - 對線程 3 運(yùn)行 where 命令
Thread threadno選擇一個(gè)線程作為當(dāng)前線程?

如果一個(gè)程序崩潰了,并生成了一個(gè) core 文件,您可以查看 core 文件來判斷進(jìn)程結(jié)束時(shí)的狀態(tài)。使用下面的命令啟動 gdb:

# gdb programname corefilename 

要調(diào)試一個(gè) core 文件,您需要可執(zhí)行程序、源代碼文件以及 core 文件。要對一個(gè) core 文件啟動 gdb,請使用 -c 選項(xiàng):

# gdb -c core programname 

gdb 會顯示是哪行代碼導(dǎo)致這個(gè)程序產(chǎn)生了核心轉(zhuǎn)儲。

默認(rèn)情況下,核心轉(zhuǎn)儲在 Novell 的 SUSE LINUX Enterprise Server 9(SLES 9)和 Red Hat? Enterprise Linux Advanced Server(RHEL AS 4)上都是禁用的。要啟用核心轉(zhuǎn)儲,請以 root 用戶的身份在命令行中執(zhí)行 ulimit –c unlimited

清單 8 中的例子闡述了如何使用 gdb 來定位程序中的 bug。清單 8 是一段包含 bug 的 C++ 代碼。

清單 8 中的 C++ 程序試圖構(gòu)建 10 個(gè)鏈接在一起的數(shù)字框(number box),例如:


圖 1. 一個(gè)包含 10 個(gè)鏈接在一起的數(shù)字框的列表
 

然后試圖從這個(gè)列表中逐個(gè)刪除數(shù)字框。

編譯并運(yùn)行這個(gè)程序,如下所示:


清單 9. 編譯并運(yùn)行這個(gè)程序
                
# g++ -g -o gdbtest1 gdbtest1.cpp
# ./gdbtest1
Number Box "0" created
Number Box "1" created
Number Box "2" created
Number Box "3" created
Number Box "4" created
Number Box "5" created
Number Box "6" created
Number Box "7" created
Number Box "8" created
Number Box "9" created
list created
Number Box "9" deleted
Segmentation fault

正如您可以看到的一樣,這個(gè)程序會導(dǎo)致段錯誤。調(diào)用 gdb 來看一下這個(gè)問題,如下所示:


清單 10. 調(diào)用 gdb
                
# gdb ./gdbtest1
GNU gdb 6.2.1
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you 
are welcome to change it and/or distribute copies of it under certain 
conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for 
details.
This GDB was configured as "ppc-suse-linux"...Using host libthread_db 
library "/lib/tls/libthread_db.so.1".
(gdb)

您知道段錯誤是在數(shù)字框 "9" 被刪除之后發(fā)生的。執(zhí)行 run 和 where 命令來精確定位段錯誤發(fā)生在程序中的什么位置。


清單 11. 執(zhí)行 run 和 where 命令
                
(gdb) run
Starting program: /root/test/gdbtest1 
Number Box "0" created
Number Box "1" created
Number Box "2" created
Number Box "3" created
Number Box "4" created
Number Box "5" created
Number Box "6" created
Number Box "7" created
Number Box "8" created
Number Box "9" created
list created
Number Box "9" deleted
Program received signal SIGSEGV, Segmentation fault.
0x10000f74 in NumBox<int>::GetNext (this=0x0) at gdbtest1.cpp:14
14              NumBox<T>*GetNext() const { return Next; }
(gdb) where
#0  0x10000f74 in NumBox<int>::GetNext (this=0x0) at gdbtest1.cpp:14
#1  0x10000d10 in NumChain<int>::RemoveBox (this=0x10012008, 

item_to_remove=@0xffffe200) at gdbtest1.cpp:63 #2 0x10000978 in main (argc=1, argv=0xffffe554) at gdbtest1.cpp:94 (gdb)

跟蹤信息顯示這個(gè)程序在第 14 行 NumBox<int>::GetNext (this=0x0) 接收到一個(gè)段錯誤。這個(gè)數(shù)字框上 Next 指針的地址是 0x0,這對于一個(gè)數(shù)字框來說是一個(gè)無效的地址。從上面的跟蹤信息可以看出,GetNext 函數(shù)是由 63 行調(diào)用的。看一下在 gdbtest1.cpp 的 63 行附近發(fā)生了什么:


清單 12. gdbtest1.cpp
                
     54                       } else {
     55                               temp->SetNext (current->GetNext());
     56                               delete temp;
     57                               temp = 0;
     58                               return 0;
     59                       }
     60               }
     61               current = 0;
     62               temp = current;
     63               current = current->GetNext();
     64       }
     65 
     66       return -1;

第 61 行 current=0 將這個(gè)指針設(shè)置為一個(gè)無效的地址,這正是產(chǎn)生段錯誤的根源。注釋掉第 61 行,將其保存為 gdbtest2.cpp,然后編譯并重新運(yùn)行。


清單 13. 再次運(yùn)行程序(gdbtest2.cpp)
                
# g++ -g -o gdbtest2 gdbtest2.cpp
# ./gdbtest2
Number Box "0" created
Number Box "1" created
Number Box "2" created
Number Box "3" created
Number Box "4" created
Number Box "5" created
Number Box "6" created
Number Box "7" created
Number Box "8" created
Number Box "9" created
list created
Number Box "9" deleted
Number Box "0" deleted

這個(gè)程序現(xiàn)在可以成功完成而不會出現(xiàn)段錯誤了。然而,結(jié)果并不像我們預(yù)期的一樣:程序在刪除 Number Box "9"之后刪除了 Number Box "0",而不像我們期望的一樣刪除 Number Box "8,"。使用 gdb 再次來看一下。


清單 14. 再次使用 gdb 進(jìn)行查看
                
# gdb ./gdbtest2
GNU gdb 6.2.1
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you 
are welcome to change it and/or distribute copies of it under certain 
conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for 
details.
This GDB was configured as "ppc-suse-linux"...Using host libthread_db 
library "/lib/tls/libthread_db.so.1".
(gdb) break 94 if i==8
Breakpoint 1 at 0x10000968: file gdbtest2.cpp, line 94.
(gdb) run
Starting program: /root/test/gdbtest2 
Number Box "0" created
Number Box "1" created
Number Box "2" created
Number Box "3" created
Number Box "4" created
Number Box "5" created
Number Box "6" created
Number Box "7" created
Number Box "8" created
Number Box "9" created
list created
Number Box "9" deleted
Breakpoint 1, main (argc=1, argv=0xffffe554) at gdbtest2.cpp:94
94                      list ->RemoveBox(i);

您可能希望找出為什么這個(gè)程序刪除的是 Number Box 0,而不是 Number Box 8,因此需要在您認(rèn)為程序會刪除 Number Box 8 的地方停止程序。設(shè)置這個(gè)斷點(diǎn):break 94 if i==8,可以在 i 等于 8 時(shí)在第 94 行處停止程序。然后單步跟蹤到 RemoveBox() 函數(shù)中。


清單 15. 單步跟蹤到 RemoveBox() 函數(shù)中
                
(gdb) s
38                      NumBox<T> *temp = 0;  
(gdb) s
40                      while (current != 0) {
(gdb) print pointer
$1 = (NumBox<int> *) 0x100120a8
 (gdb) print *pointer
$2 = {Num = 0, Next = 0x0}
(gdb)

指針早已指向了 Number Box "0",因此這個(gè) bug 可能就存在于程序刪除 Number Box "9" 的地方。要在 gdb 中重新啟動這個(gè)程序,請使用 kill 刪除原來的斷點(diǎn),然后添加一個(gè) i 等于 9 時(shí)的新斷點(diǎn),然后再次運(yùn)行這個(gè)程序。


清單 16. 在 gdb 中重新啟動程序
                
(gdb) kill
Kill the program being debugged? (y or n) y
(gdb) info break
Num Type           Disp Enb Address    What
1   breakpoint     keep y   0x10000968 in main at gdbtest2.cpp:94
        stop only if i == 8
        breakpoint already hit 1 time
(gdb) delete 1
(gdb) break 94 if i==9
Breakpoint 2 at 0x10000968: file gdbtest2.cpp, line 94.
(gdb) run
Starting program: /root/test/gdbtest2 
Number Box "0" created
Number Box "1" created
Number Box "2" created
Number Box "3" created
Number Box "4" created
Number Box "5" created
Number Box "6" created
Number Box "7" created
Number Box "8" created
Number Box "9" created
list created
Breakpoint 2, main (argc=1, argv=0xffffe554) at gdbtest2.cpp:94
94                      list ->RemoveBox(i);
(gdb)

當(dāng)這一次單步跟蹤 RemoveBox() 函數(shù)時(shí),要特別注意 list->pointer 正在指向哪一個(gè)數(shù)字框,因?yàn)?bug 可能就在于 list->pointer 開始指向 Number Box "0" 的地方。請使用 display *pointer 命令來查看,這會自動顯示這個(gè)函數(shù)。


清單 17. 使用 display *pointer 命令進(jìn)行監(jiān)視
                
Breakpoint 2, main (argc=1, argv=0xffffe554) at gdbtest2.cpp:94
94            list ->RemoveBox(i);
(gdb) s
NumChain<int>::RemoveBox (this=0x10012008, item_to_remove=@0xffffe200) 
at gdbtest2.cpp:37 37 NumBox<T> *current = pointer; (gdb) display *pointer 1: *this->pointer = {Num = 9, Next = 0x10012098} (gdb) s 38 NumBox<T> *temp = 0; 1: *this->pointer = {Num = 9, Next = 0x10012098} (gdb) s 40 while (current != 0) { 1: *this->pointer = {Num = 9, Next = 0x10012098} (gdb) s 41 if (current->GetValue() == item_to_remove) { 1: *this->pointer = {Num = 9, Next = 0x10012098} (gdb) s NumBox<int>::GetValue (this=0x100120a8) at gdbtest2.cpp:16 16 const T& GetValue () const { return Num; } (gdb) s NumChain<int>::RemoveBox (this=0x10012008, item_to_remove=@0xffffe200)
at gdbtest2.cpp:42 42 if (temp == 0) { 1: *this->pointer = {Num = 9, Next = 0x10012098} (gdb) s 44 if (current->GetNext() == 0) { 1: *this->pointer = {Num = 9, Next = 0x10012098} (gdb) s NumBox<int>::GetNext (this=0x100120a8) at gdbtest2.cpp:14 14 NumBox<T>*GetNext() const { return Next; } (gdb) s NumChain<int>::RemoveBox (this=0x10012008, item_to_remove=@0xffffe200)
at gdbtest2.cpp:50 50 delete current; 1: *this->pointer = {Num = 9, Next = 0x10012098} (gdb) s ~NumBox (this=0x100120a8) at gdbtest2.cpp:10 10 std::cout << "Number Box " <<"\"" << GetValue()
<<"\"" <<" deleted" << std::endl; (gdb) s NumBox<int>::GetValue (this=0x100120a8) at gdbtest2.cpp:16 16 const T& GetValue () const { return Num; } (gdb) s Number Box "9" deleted ~NumBox (this=0x100120a8) at gdbtest2.cpp:11 11 Next = 0; (gdb) s NumChain<int>::RemoveBox (this=0x10012008, item_to_remove=@0xffffe200)
at gdbtest2.cpp:51 51 current = 0; 1: *this->pointer = {Num = 0, Next = 0x0} (gdb) s 53 return 0; 1: *this->pointer = {Num = 0, Next = 0x0} (gdb) s 0x10000d1c 66 return -1; 1: *this->pointer = {Num = 0, Next = 0x0}

從上面的跟蹤過程中,您可以看到 list->pointer 在刪除 Number Box "9" 之后指向了 Number Box "0"。這個(gè)邏輯并不正確,因?yàn)樵趧h除 Number Box "9" 之后,list->pointer 應(yīng)該指向的是 Number Box "8"。現(xiàn)在非常顯然我們應(yīng)該在第 50 行之前添加一條語句pointer = pointer->GetNext();,如下所示:


清單 18. 在第 50 行之前添加一條 pointer = pointer->GetNext(); 語句
                
     49                     } else {
     50                             pointer = pointer->GetNext();
     51                             delete current;
     52                             current = 0;
     53                      }
     54                      return 0;

將新修改之后的程序保存為 gdbtest3.cpp,然后編譯并再次運(yùn)行。


清單 19. 再次運(yùn)行程序(gdbtest3.cpp)
                
# g++ -g -o gdbtest3 gdbtest3.cpp
# ./gdbtest3
Number Box "0" created
Number Box "1" created
Number Box "2" created
Number Box "3" created
Number Box "4" created
Number Box "5" created
Number Box "6" created
Number Box "7" created
Number Box "8" created
Number Box "9" created
list created
Number Box "9" deleted
Number Box "8" deleted
Number Box "7" deleted
Number Box "6" deleted
Number Box "5" deleted
Number Box "4" deleted
Number Box "3" deleted
Number Box "2" deleted
Number Box "1" deleted
Number Box "0" deleted

這才是我們期望的結(jié)果。

多線程環(huán)境

在 GDB 中有一些特殊的命令可以用于多線程應(yīng)用程序的調(diào)試。下面這個(gè)例子給出了一個(gè)死鎖情況,并介紹了如何使用這些命令來檢查多線程應(yīng)用程序中的問題:


清單 20. 多線程的例子
                
#include <stdio.h>
#include "pthread.h>
pthread_mutex_t AccountA_mutex;
pthread_mutex_t AccountB_mutex;
struct BankAccount {
     char account_name[1];
     int balance;
};
struct BankAccount  accountA = {"A", 10000 };
struct BankAccount  accountB = {"B", 20000 };
void * transferAB (void* amount_ptr) {
     int amount = *((int*)amount_ptr);
     pthread_mutex_lock(&AccountA_mutex);
     if (accountA.balance < amount)   {
             printf("There is not enough memory in Account A!\n");
             pthread_mutex_unlock(&AccountA_mutex);
             pthread_exit((void *)1);
     }
     accountA.balance -=amount;
     sleep(1);
     pthread_mutex_lock(&AccountB_mutex);
     accountB.balance +=amount;
     pthread_mutex_unlock(&AccountA_mutex); 
     pthread_mutex_unlock(&AccountB_mutex);
}
void * transferBA (void* amount_ptr) {
     int amount = *((int*)amount_ptr);
     pthread_mutex_lock(&AccountB_mutex);
     if (accountB.balance < amount)   {
             printf("There is not enough memory in Account B!\n");
             pthread_mutex_unlock(&AccountB_mutex);
             pthread_exit((void *)1);
     }
     accountB.balance -=amount;
     sleep(1);
     pthread_mutex_lock(&AccountA_mutex);
     accountA.balance +=amount;
     pthread_mutex_unlock(&AccountB_mutex);
     pthread_mutex_unlock(&AccountA_mutex);
}
int main(int argc, char* argv[]) {
     int             threadid[4];
     pthread_t       pthread[4];
     int             transfer_amount[4] = {100, 200, 300, 400};
     int             final_balanceA, final_balanceB;
     final_balanceA=accountA.balance-transfer_amount[0]-
transfer_amount[1]+transfer_amount[2]+transfer_amount[3]; final_balanceB=accountB.balance+transfer_amount[0]
+transfer_amount[1]-transfer_amount[2]-transfer_amount[3]; if (threadid[0] = pthread_create(&pthread[0], NULL, transferAB, (void*)&transfer_amount[0]) " 0) { perror("Thread #0 creation failed."); exit (1); } if (threadid[1] = pthread_create(&pthread[1], NULL, transferAB, (void*)&transfer_amount[1]) " 0) { perror("Thread #1 creation failed."); exit (1); } if (threadid[2] = pthread_create(&pthread[2], NULL, transferBA, (void*)&transfer_amount[2]) < 0) { perror("Thread #2 creation failed."); exit (1); } if (threadid[3] = pthread_create(&pthread[3], NULL, transferBA, (void*)&transfer_amount[3]) < 0) { perror("Thread #3 creation failed."); exit (1); } printf("Transitions are in progress.."); while ((accountA.balance != final_balanceA) && (accountB.balance != final_balanceB)) { printf(".."); } printf("\nAll the money is transferred !!\n"); }

使用 gcc 來編譯這個(gè)程序,如下所示:

# gcc -g -o gdbtest2 gdbtest2.c -L/lib/tls -lpthread

程序 gdbtest2 會掛起,不會返回一條 All the money is transferred !! 消息。

將 gdb 附加到正在運(yùn)行的進(jìn)程上,從而了解這個(gè)進(jìn)程內(nèi)部正在發(fā)生什么。


清單 21. 將 gdb 附加到正在運(yùn)行的進(jìn)程上
                
# ps -ef |grep gdbtest2
root      9510  8065  1 06:30 pts/1    00:00:00 ./gdbtest2
root      9516  9400  0 06:30 pts/4    00:00:00 grep gdbtest2
# gdb -pid 9510
GNU gdb 6.2.1
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you 
are welcome to change it and/or distribute copies of it under certain 
conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for 
details.
This GDB was configured as "ppc-suse-linux".
Attaching to process 9510
Reading symbols from /root/test/gdbtest2...done.
Using host libthread_db library "/lib/tls/libthread_db.so.1".
Reading symbols from /lib/tls/libpthread.so.0...done.
[Thread debugging using libthread_db enabled]
[New Thread 1073991712 (LWP 9510)]
[New Thread 1090771744 (LWP 9514)]
[New Thread 1086577440 (LWP 9513)]
[New Thread 1082383136 (LWP 9512)]
[New Thread 1078188832 (LWP 9511)]
Loaded symbols for /lib/tls/libpthread.so.0
Reading symbols from /lib/tls/libc.so.6...done.
Loaded symbols for /lib/tls/libc.so.6
Reading symbols from /lib/ld.so.1...done.
Loaded symbols for /lib/ld.so.1
0x0ff4ac40 in __write_nocancel () from /lib/tls/libc.so.6
(gdb) info thread
  5 Thread 1078188832 (LWP 9511)  0x0ffe94ec in __lll_lock_wait () 
   from /lib/tls/libpthread.so.0
  4 Thread 1082383136 (LWP 9512)  0x0ffe94ec in __lll_lock_wait () 
   from /lib/tls/libpthread.so.0
  3 Thread 1086577440 (LWP 9513)  0x0ffe94ec in __lll_lock_wait () 
   from /lib/tls/libpthread.so.0
  2 Thread 1090771744 (LWP 9514)  0x0ffe94ec in __lll_lock_wait () 
        from /lib/tls/libpthread.so.0
  1 Thread 1073991712 (LWP 9510)  0x0ff4ac40 in __write_nocancel () 
     from /lib/tls/libc.so.6
(gdb)

從 info thread 命令中,我們可以了解到除了主線程(thread #1)之外的所有線程都在等待函數(shù) __lll_lock_wait () 完成。

使用 thread apply threadno where 命令來查看每個(gè)線程到底運(yùn)行到了什么地方:


清單 22. 查看每個(gè)線程運(yùn)行到了什么地方
                
(gdb) thread apply 1 where
Thread 1 (Thread 1073991712 (LWP 9510)):
#0  0x0ff4ac40 in __write_nocancel () from /lib/tls/libc.so.6
#1  0x0ff4ac28 in __write_nocancel () from /lib/tls/libc.so.6
Previous frame identical to this frame (corrupt stack?)
#0  0x0ff4ac40 in __write_nocancel () from /lib/tls/libc.so.6
(gdb) thread apply 2 where
Thread 2 (Thread 1090771744 (LWP 9514)):
#0  0x0ffe94ec in __lll_lock_wait () from /lib/tls/libpthread.so.0
#1  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
#2  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
#3  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
#4  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
Previous frame inner to this frame (corrupt stack?)
#0  0x0ff4ac40 in __write_nocancel () from /lib/tls/libc.so.6
(gdb) thread apply 3 where
Thread 3 (Thread 1086577440 (LWP 9513)):
#0  0x0ffe94ec in __lll_lock_wait () from /lib/tls/libpthread.so.0
#1  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
#2  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
#3  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
#4  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
Previous frame inner to this frame (corrupt stack?)
#0  0x0ff4ac40 in __write_nocancel () from /lib/tls/libc.so.6
(gdb) thread apply 4 where
Thread 4 (Thread 1082383136 (LWP 9512)):
#0  0x0ffe94ec in __lll_lock_wait () from /lib/tls/libpthread.so.0
#1  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
#2  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
#3  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
#4  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
Previous frame inner to this frame (corrupt stack?)
#0  0x0ff4ac40 in __write_nocancel () from /lib/tls/libc.so.6
 (gdb) thread apply 5 where
Thread 5 (Thread 1078188832 (LWP 9511)):
#0  0x0ffe94ec in __lll_lock_wait () from /lib/tls/libpthread.so.0
#1  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
#2  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
#3  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
#4  0x0ffe466c in pthread_mutex_lock () from /lib/tls/libpthread.so.0
Previous frame inner to this frame (corrupt stack?)
#0  0x0ff4ac40 in __write_nocancel () from /lib/tls/libc.so.6

每個(gè)線程都試圖對一個(gè)互斥體進(jìn)行加鎖,但是這個(gè)互斥體卻是不可用的,可能是因?yàn)橛辛硗庖粋€(gè)線程已經(jīng)對其進(jìn)行加鎖了。從上面的證據(jù)我們可以判斷程序中一定存在死鎖。您還可以看到哪個(gè)線程現(xiàn)在擁有這個(gè)互斥體。


清單 23. 查看哪個(gè)線程擁有互斥體
                
(gdb) print AccountA_mutex
$1 = {__m_reserved = 2, __m_count = 0, __m_owner = 0x2527, 
__m_kind = 0, __m_lock = {__status = 1, __spinlock = 0}} (gdb) print 0x2527 $2 = 9511 (gdb) print AccountB_mutex $3 = {__m_reserved = 2, __m_count = 0, __m_owner = 0x2529,
__m_kind = 0, __m_lock = {__status = 1, __spinlock = 0}} (gdb) print 0x2529 $4 = 9513 (gdb)

從上面的命令中,我們可以看出 AccontA_mutex 是被線程 5(LWP 9511)加鎖(擁有)的,而 AccontB_mutex 是被線程 3(LWP 9513)加鎖(擁有)的。

為了解決上面的死鎖情況,可以按照相同的順序?qū)コ怏w進(jìn)行加鎖,如下所示:


清單 24. 按照相同的順序?qū)コ怏w進(jìn)行加鎖
                    
.
.
void * transferAB (void* amount_ptr) {
        int amount = *((int*)amount_ptr);
        pthread_mutex_lock(&AccountA_mutex);
        pthread_mutex_lock(&AccountB_mutex);
        if (accountA.balance < amount)   {
                printf("There is not enough memory in Account A!\n");
                pthread_mutex_unlock(&AccountA_mutex);
                pthread_exit((void *)1);
        }
        accountA.balance -=amount;
        sleep(1);
        accountB.balance +=amount;
        pthread_mutex_unlock(&AccountA_mutex);
        pthread_mutex_unlock(&AccountB_mutex);
}
void * transferBA (void* amount_ptr) {
        int amount = *((int*)amount_ptr);
        pthread_mutex_lock(&AccountA_mutex);
        pthread_mutex_lock(&AccountB_mutex);
        if (accountB.balance < amount)   {
                printf("There is not enough memory in Account B!\n");
                pthread_mutex_unlock(&AccountB_mutex);
                pthread_exit((void *)1);
        }
        accountB.balance -=amount;
        sleep(1);
        accountA.balance +=amount;
        pthread_mutex_unlock(&AccountA_mutex);
        pthread_mutex_unlock(&AccountB_mutex);
}
.
.

或者對每個(gè)帳號單獨(dú)進(jìn)行加鎖,如下所示:


清單 25. 對每個(gè)帳號單獨(dú)進(jìn)行加鎖
                    
.
.
void * transferAB (void* amount_ptr) {
        int amount = *((int*)amount_ptr);
        pthread_mutex_lock(&AccountA_mutex);
        if (accountA.balance < amount)   {
                printf("There is not enough memory in Account A!\n");
                pthread_mutex_unlock(&AccountA_mutex);
                pthread_exit((void *)1);
        }
        accountA.balance -=amount;
        sleep(1);
        pthread_mutex_unlock(&AccountA_mutex);
        pthread_mutex_lock(&AccountB_mutex); 
        accountB.balance +=amount;
        pthread_mutex_unlock(&AccountB_mutex);
}
void * transferBA (void* amount_ptr) {
        int amount = *((int*)amount_ptr);
        pthread_mutex_lock(&AccountB_mutex);
        if (accountB.balance < amount)   {
                printf("There is not enough memory in Account B!\n");
                pthread_mutex_unlock(&AccountB_mutex);
                pthread_exit((void *)1);
        }
        accountB.balance -=amount;
        sleep(1);
        pthread_mutex_unlock(&AccountB_mutex);
        pthread_mutex_lock(&AccountA_mutex); 
        accountA.balance +=amount;
        pthread_mutex_unlock(&AccountA_mutex);
}
.
.
.

要調(diào)試 64 位的應(yīng)用程序(使用 GCC 的 –m64 選項(xiàng)或 IBM XL C/C++ 編譯器的 –q64 選項(xiàng)編譯),應(yīng)該使用一個(gè)特別的 gdb 版本 gdb64。

Java 調(diào)試器

Java? 調(diào)試器 JDB 是一個(gè)用來調(diào)試 Java 類的命令行調(diào)試器。它提供了對本地或遠(yuǎn)程 Java 虛擬機(jī)(JVM)的檢查和調(diào)試功能。其二進(jìn)制文件是 jdb。

JDB 是與 java 編譯器 javac 一起打包的,在 java2 rpm 包中。

有很多方法都可以開始一個(gè) jdb 會話。最常見的方法是讓 jdb 啟動一個(gè)新的 Java 虛擬機(jī),其中運(yùn)行要調(diào)試的應(yīng)用程序的主類。這可以通過在命令行中使用命令 jdb 替換 java 來實(shí)現(xiàn)。例如,如果您的應(yīng)用程序的主類是 appClass,那么就使用下面的命令在 JDB 中調(diào)試這個(gè)程序:

# jdb appClass

另外一種使用 jdb 的方法是將其附加到一個(gè)早已在運(yùn)行的 JVM 上。要使用 jdb 進(jìn)行調(diào)試的 VM 必須是使用下面的選項(xiàng)啟動的:

# java -Xdebug -Xnoagent - 
Xrunjdwp:transport=dt_socket,server=y,suspend=n,
address=8888 -Djava.compiler=NONEappClass

然后您就可以使用下面的命令將 jdb 附加到這個(gè) VM 上:

# jdb -attach 8888

jdb 最常用的命令與 gdb 的類似。詳細(xì)信息請參考 表 1

圖形化調(diào)試器

使用圖形模式的調(diào)試器相對于命令行調(diào)試器的一個(gè)優(yōu)點(diǎn)是,在調(diào)試器中單步執(zhí)行程序的同時(shí)可以看到對應(yīng)的每行源代碼。

GNU DDD(Data Display Debugger)就是一個(gè)調(diào)試器(例如 GDB 和 JDB)的圖形化前端。除了常見的前端特性(例如查看源代碼)之外,DDD 還通過將要顯示的數(shù)據(jù)結(jié)構(gòu)以交互式的圖形化方式進(jìn)行顯示而聞名。

對于 SLES 9 來說,用于 PowerPC 的 DDD 二進(jìn)制文件是在 SUSE Linux SDK CD 中單獨(dú)提供的,也可以從 Novell 公司的網(wǎng)站上進(jìn)行下載(參閱 參考資料)。RedHat 在 RHEL AS 4 CD 中提供了 DDD 的 rpm 包。

圖 2 是在使用 DDD 來調(diào)試 清單 19 (gdbtest3.cpp)中的例子時(shí)的截圖。


圖 2. DDD 截屏
 

默認(rèn)情況下,DDD 使用 gdb 作為后端調(diào)試器;要切換到 jdb,請使用 ddd -jdb 來啟動 DDD。

有關(guān) DDD 的更多信息,請參考 GNU 項(xiàng)目 Web 站點(diǎn)上的 DDD 部分(參閱 參考資料)。

strace

strace 命令是可以在 Linux on POWER 架構(gòu)上使用的一個(gè)功能非常強(qiáng)大的工具。它可以顯示用戶空間的應(yīng)用程序所執(zhí)行的全部系統(tǒng)調(diào)用。strace 可以以符號表的形式顯示這些調(diào)用的參數(shù)和返回值。strace 從內(nèi)核接收信息,并不需要采用任何特殊的方式來構(gòu)建內(nèi)核。要跟蹤的應(yīng)用程序也不需要為 strace 重新進(jìn)行編譯,當(dāng)我們無法訪問應(yīng)用程序的源代碼時(shí),這是非常方便的一種方法。

下面的例子使用 strace 來跟蹤一個(gè)普通用戶執(zhí)行 cat /etc/shadow 的過程,然后將跟蹤到的結(jié)果打印到 strace.cat.out 中。這個(gè)程序依然正常運(yùn)行,但是在使用 strace 運(yùn)行時(shí)速度稍微有些慢;最后,我們得到一個(gè)跟蹤文件。

$ strace -o strace.cat.out cat /etc/shadow
cat: /etc/shadow: Permission denied
$

跟蹤文件通常都很大。即使對這個(gè)簡單的例子來說,strace.cat.out 也有 111 行長。這個(gè)文件的最后包含下面的行:


清單 26. strace.cat.out 文件中的部分行
                
     88 open("/usr/lib/locale/en_US.UTF-8/LC_NUMERIC", 
          O_RDONLY) = -1 ENOENT (No such file or directory)
     89 open("/usr/lib/locale/en_US.utf8/LC_NUMERIC", O_RDONLY) = 3
     90 fstat64(3, {st_mode=S_IFREG|0644, st_size=54, ...}) = 0
     91 mmap(NULL, 54, PROT_READ, MAP_PRIVATE, 3, 0) = 0x4010f000
     92 close(3)                                = 0
     93 open("/usr/lib/locale/en_US.UTF-8/LC_CTYPE", 
            O_RDONLY) = -1 ENOENT (No such file or directory)
     94 open("/usr/lib/locale/en_US.utf8/LC_CTYPE", O_RDONLY) = 3
     95 fstat64(3, {st_mode=S_IFREG|0644, st_size=208464, ...}) = 0
     96 mmap(NULL, 208464, PROT_READ, MAP_PRIVATE, 3, 0) = 0x40110000
     97 close(3)                                = 0
     98 fstat64(1, {st_mode=S_IFCHR|0620, 
          st_rdev=makedev(136, 4), ...}) = 0
     99 open("/etc/shadow", O_RDONLY|O_LARGEFILE) = -1 EACCES 
           (Permission denied)
    100 write(2, "cat: ", 5)                    = 5
    101 write(2, "/etc/shadow", 11)             = 11
    102 open("/usr/share/locale/en_US.UTF-8/LC_MESSAGES/libc.mo", 
           O_RDONLY) = -1 ENOENT (No such file or directory)
    103 open("/usr/share/locale/en_US.utf8/LC_MESSAGES/libc.mo", 
            O_RDONLY) = -1 ENOENT (No such file or directory)
    104 open("/usr/share/locale/en_US/LC_MESSAGES/libc.mo", 
           O_RDONLY) = -1 ENOENT (No such file or directory)
    105 open("/usr/share/locale/en.UTF-8/LC_MESSAGES/libc.mo", 
           O_RDONLY) = -1 ENOENT (No such file or directory)
    106 open("/usr/share/locale/en.utf8/LC_MESSAGES/libc.mo", 
          O_RDONLY) = -1 ENOENT (No such file or directory)
    107 open("/usr/share/locale/en/LC_MESSAGES/libc.mo", 
          O_RDONLY) = -1 ENOENT (No such file or directory)
    108 write(2, ": Permission denied", 19)     = 19
    109 write(2, "\n", 1)                       = 1
    110 close(1)                                = 0
    111 exit_group(1)                 

注意在第 99 行處,命令會失效,因?yàn)橄到y(tǒng)調(diào)用 open("/etc/shadow", O_RDONLY|O_LARGEFILE) 失效了,返回了一個(gè) EACCESS 錯誤代碼,這說明權(quán)限不符合。

在有些情況中,應(yīng)用程序可能會掛起,并且不能響應(yīng)諸如 ctrl+c(SIGINT)之類的信號。這說明應(yīng)用程序正在調(diào)用的系統(tǒng)調(diào)用在其內(nèi)核模式下掛起了,一直不會返回用戶模式。strace 可以非常有用地用來判斷是哪一個(gè)系統(tǒng)調(diào)用,以及傳給這個(gè)系統(tǒng)調(diào)用的參數(shù)是什么。可能性最大的情況是參數(shù)的位置不對而導(dǎo)致了問題。

與 gdb64 是用于 64 位應(yīng)用程序的 gdb 類似,strace64 也用來跟蹤 64 位應(yīng)用程序所請求的系統(tǒng)調(diào)用。

結(jié)束語

在 Linux on POWER 平臺上可以使用很多工具來幫助調(diào)試應(yīng)用程序。本文中介紹的工具可以幫助您解決很多編碼的問題。諸如 Valgrind 之類的工具可以顯示內(nèi)存泄漏的位置、非法讀/寫以及類似的內(nèi)容,這可以解決內(nèi)存管理的問題。

使用 gdb 和 jdb 有助于解決那些導(dǎo)致應(yīng)用程序異常結(jié)束的問題,以及導(dǎo)致非預(yù)期或不想要的結(jié)果的 bug。DDD 工具可以幫助簡化調(diào)試任務(wù),方法是通過將代碼的執(zhí)行與源代碼行聯(lián)系在一起,并在數(shù)據(jù)顯示窗口中可視化地顯示數(shù)據(jù)結(jié)構(gòu)。另外,strace 是一個(gè)功能非常強(qiáng)大的工具,它可以用來跟蹤應(yīng)用程序的所有系統(tǒng)調(diào)用。因此,下一次當(dāng)您要在 Linux 上修復(fù) bug 時(shí),可以試用一下這些工具。

致謝

我要感謝 Linda Kinnunen 為我提供文檔模板,并幫我審閱本文;感謝 John Engel 和 Chakarat Skawratananond 所提供的技術(shù)幫助以及對本文的審閱。

特別說明

本文所介紹的信息僅僅限于本文的環(huán)境,并不提供任何擔(dān)保。

本文中介紹的所有客戶例子都是用來解釋這些客戶如何使用 IBM 產(chǎn)品的,以及他們達(dá)到了什么結(jié)果。針對每個(gè)客戶的實(shí)際環(huán)境,成本和性能可能會有很大的差異。

非 IBM 產(chǎn)品中的信息來源于這些產(chǎn)品的供應(yīng)商所公開發(fā)表的資料,或可以公開使用的源代碼,這并不說明 IBM 產(chǎn)品對它們的認(rèn)可。非 IBM 產(chǎn)品的價(jià)格和性能都來源于可公開使用的信息,包括供應(yīng)商的公告和供應(yīng)商的全球主頁。IBM 沒有對這些產(chǎn)品進(jìn)行測試,也不保證性能、能力數(shù)據(jù)的正確性,以及其他有關(guān)非 IBM 產(chǎn)品的聲明的正確性。有關(guān)非 IBM 產(chǎn)品的功能的問題應(yīng)該由這些產(chǎn)品的供應(yīng)商來解決。

所有有關(guān) IBM 將來方向和興趣的聲明都可能會不加任何通知而發(fā)生變化或撤銷,這只是為了表示我們的目標(biāo)。有關(guān)具體的方向聲明的全文,請與您本地的 IBM 辦公室或 IBM 認(rèn)證的分銷商聯(lián)系。

有些信息是為了解決預(yù)期將來的功能。這種信息不會作為對將來任何產(chǎn)品所能實(shí)現(xiàn)特定級別的性能、功能或上市周期的承諾的最終聲明。這些承諾只在 IBM 產(chǎn)品聲明中做出。本文中提到這些信息是為了說明 IBM 目前的投資和開發(fā)活動是真正為了幫助客戶實(shí)現(xiàn)將來的計(jì)劃。

性能是在一個(gè)受控的環(huán)境中使用標(biāo)準(zhǔn)的 IBM 基準(zhǔn)測試結(jié)果而得出的。用戶自己環(huán)境中的實(shí)際吞吐量或性能可能會有很大的不同,這取決于很多方面,例如用戶作業(yè)流中的程序數(shù)量,I/O 配置情況,存儲配置,以及所處理的任務(wù)負(fù)載。因此,我們并不保證每個(gè)用戶能獲得的吞吐量和性能方面的提高都與本文中介紹的相同。


參考資料

  • 參與論壇討論

  • 請參考以下資料:
    • Debugging Memory Problems”(Linux Magazine,2003 年 5 月)介紹了更多有關(guān) Valgrind 內(nèi)存調(diào)試的內(nèi)容。
    • Valgrind HOW TO 也介紹了更多有關(guān) Valgrind 內(nèi)存調(diào)試的內(nèi)容。
    • Linux on Power Architecture Developer's Corner:介紹了更多有關(guān) Linux on Power 的內(nèi)容。您可以在這里找到很多技術(shù)文檔、培訓(xùn)、下載和產(chǎn)品信息。
    • developerWorks Linux 專區(qū):可以找到更多為 Linux 開發(fā)人員準(zhǔn)備的資料
    • Linux at IBM:可以找到硬件、軟件和服務(wù),以及為新手和專家提供的建議。
    • 從 Solaris 向 Linux on POWER 遷移指南”(developerWorks,2005 年 2 月)幫助您加速移植。學(xué)習(xí) Solaris 和 Linux on POWER 上的區(qū)別是在移植過程中經(jīng)常碰到的一個(gè)問題。

  • 獲得產(chǎn)品和技術(shù)
    • Valgrind 工具包用來調(diào)試和配置 x86-Linux 程序。
    • GDB: GNU 項(xiàng)目調(diào)試器。
    • DDD:命令行調(diào)試器(例如 GDB)的 GNU 圖形化前端。
    • DDD 二進(jìn)制文件 可以從 Novell 公司的網(wǎng)站上下載。

關(guān)于作者

Calvin Sze 是 IBM 的 IBM eServer Solutions Enablement 組織的一名 Linux 顧問。他住在德州奧斯汀。Calvin 的主要角色是幫助解決方案開發(fā)人員將自己的應(yīng)用程序移植到 Linux on POWER 上。Calvin 參與 Linux 和 AIX 平臺上的軟件開發(fā)和系統(tǒng)集成已超過 10 年的時(shí)間了。您可以通過 calvins@us.ibm.com 與 Calvin 聯(lián)系。

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            久久久av毛片精品| 久久久亚洲高清| 欧美日韩18| 在线成人免费观看| 欧美一区二区三区在线观看视频 | 老司机成人在线视频| 国产区亚洲区欧美区| 亚洲一级片在线观看| 欧美激情亚洲一区| 久久久久久久一区二区三区| 国产精品视频不卡| 亚洲综合精品一区二区| 亚洲欧洲一区二区在线播放| 久久综合色8888| 一区二区视频免费完整版观看| 久久激情五月丁香伊人| 亚洲一区在线看| 国产精品av免费在线观看| 一区二区精品国产| 亚洲欧洲另类| 欧美成人一区二区三区| 亚洲国产另类久久久精品极度| 美女精品在线观看| 久久精品久久综合| 国产一区91精品张津瑜| 欧美在线观看网站| 午夜影院日韩| 国产日韩成人精品| 久久av在线| 午夜在线a亚洲v天堂网2018| 国产精品一区二区你懂得| 亚洲欧美日韩在线不卡| 亚洲私人影吧| 国产美女诱惑一区二区| 欧美一级二级三级蜜桃| 亚洲欧美区自拍先锋| 国产欧美日韩精品a在线观看| 午夜国产精品影院在线观看| 亚洲一区免费视频| 国产精品丝袜xxxxxxx| 欧美一激情一区二区三区| 先锋影音国产一区| 国产一区亚洲| 美女精品自拍一二三四| 美国十次成人| 日韩网站免费观看| 日韩视频精品| 国产精品亚洲аv天堂网| 欧美诱惑福利视频| 久久久www| 亚洲精品日韩久久| 亚洲精品中文字幕在线| 国产精品家庭影院| 久久久国产午夜精品| 久久中文字幕一区二区三区| 亚洲精品国偷自产在线99热| 亚洲乱码久久| 国产精品亚洲综合天堂夜夜| 久久久99精品免费观看不卡| 六十路精品视频| 一区二区三区久久精品| 亚洲色无码播放| 国内一区二区三区| 亚洲高清在线| 国产精品大片免费观看| 久久久久久久激情视频| 欧美第一黄色网| 午夜精品www| 久久久久久香蕉网| 一区二区三区三区在线| 亚洲欧美高清| 亚洲欧洲一级| 中文精品在线| 在线观看中文字幕不卡| 日韩午夜电影在线观看| 国产日韩一区二区三区在线播放| 免费日韩av电影| 欧美日韩一区二区三区四区五区| 欧美在线观看一二区| 开心色5月久久精品| 亚洲图片在区色| 久久精品国产欧美激情| 野花国产精品入口| 欧美一区二区福利在线| 亚洲裸体在线观看| 午夜性色一区二区三区免费视频| 亚洲精品一二区| 亚洲欧美中日韩| 亚洲精品一区二区三区福利| 午夜精品久久| 夜夜嗨av一区二区三区中文字幕 | 亚洲风情在线资源站| 国产精品久久久久久久久| 老司机精品视频网站| 欧美日韩一区成人| 免费久久99精品国产自在现线| 欧美日韩国产综合网| 久久综合中文色婷婷| 欧美亚州一区二区三区| 欧美成人综合| 国产欧美日韩亚州综合| 亚洲乱码国产乱码精品精可以看| 激情欧美一区二区三区| 亚洲性视频网站| 亚洲免费激情| 久久精品中文| 欧美一级久久| 欧美日韩精品中文字幕| 欧美**人妖| 国产一区二区三区久久 | 国产麻豆日韩| 亚洲美女av黄| 最近看过的日韩成人| 欧美一区二区三区啪啪| 亚洲香蕉伊综合在人在线视看| 久久综合伊人77777蜜臀| 久久aⅴ乱码一区二区三区| 欧美日韩精品在线观看| 亚洲第一在线综合网站| 国内精品视频666| 亚洲欧美日韩一区在线| 亚洲一区区二区| 欧美精品一区二区三区很污很色的| 久热re这里精品视频在线6| 国产女人精品视频| 亚洲网在线观看| 在线一区欧美| 欧美激情一二三区| 亚洲第一页中文字幕| 在线成人中文字幕| 久久精品亚洲精品| 久久久国产精品亚洲一区 | 久久精品视频一| 国产精品男女猛烈高潮激情 | 欧美片网站免费| 亚洲国产日韩美| 亚洲高清视频中文字幕| 久久久xxx| 久久久久综合网| 国产亚洲欧洲997久久综合| 亚洲综合色激情五月| 亚洲摸下面视频| 欧美午夜精品久久久久久人妖| 亚洲三级免费观看| 99精品久久免费看蜜臀剧情介绍| 欧美jizzhd精品欧美喷水| 欧美激情小视频| 亚洲精品欧美一区二区三区| 女女同性女同一区二区三区91| 欧美成人黄色小视频| 亚洲国产精品一区二区www在线| 久久久人成影片一区二区三区 | 欧美诱惑福利视频| 久久久久久久一区| 黑人巨大精品欧美一区二区小视频| 欧美专区在线观看| 久久这里只精品最新地址| 在线观看成人一级片| 免费不卡视频| 亚洲精品123区| 亚洲一区二区高清| 国产精品午夜久久| 久久不射网站| 欧美成人免费全部| 亚洲人成网站色ww在线| 欧美另类在线播放| 亚洲视频在线观看视频| 久久精品91| 亚洲第一区在线观看| 欧美高清免费| 一本色道久久综合亚洲精品高清| 亚洲欧美区自拍先锋| 国产亚洲一级高清| 久久综合九色| 亚洲伦理网站| 欧美一区二区成人6969| 伊人成年综合电影网| 免费观看成人鲁鲁鲁鲁鲁视频| 亚洲欧洲日产国码二区| 亚洲午夜电影网| 国产日韩欧美一区在线 | 亚洲欧美区自拍先锋| 久久手机精品视频| 亚洲伦理中文字幕| 国产精品久久久久秋霞鲁丝 | 亚洲视频在线播放| 久久午夜精品一区二区| 亚洲美女区一区| 国产精品久久久久免费a∨| 久久精品国产精品亚洲精品| 亚洲国产精品视频| 小辣椒精品导航| 亚洲高清免费在线| 欧美图区在线视频| 久久久久女教师免费一区| 99视频超级精品| 久久久久久一区| 一本一本a久久| 狠狠综合久久| 欧美日韩中文字幕在线视频|