linux 靜態(tài)鏈接庫和動(dòng)態(tài)連接庫
一:起因
(1)也許我們非常熟悉Windows下的VC6.0 和 CodeBlocks的調(diào)試工具 —— 界面化的調(diào)試,但是你是否想過你的每一個(gè)按鍵或者快捷鍵的背后指令是什么,讓我們一起走進(jìn)Linux的gcc動(dòng)態(tài)調(diào)試工具GDB
(2)程序調(diào)試無非就是:debug(gcc -g -o target source);設(shè)置斷點(diǎn)(b n);觀察變臉(info locals);觀察特定值(PRint expr);執(zhí)行(start);執(zhí)行下一行(next);進(jìn)入子函數(shù)(step);執(zhí)行到當(dāng)前函數(shù)結(jié)尾(finish);查看源代碼(list)等等操作
(3)gcc 的過程:C程序的編輯 用到vim; 程序的編譯和運(yùn)行用到gcc; 程序的調(diào)試用到gdb (kdbg是可視化的調(diào)試工具)
二:實(shí)例講解
(1)調(diào)試步驟分為兩步
1)編譯時(shí)一定到 加上 -g 產(chǎn)生調(diào)試信息,讓調(diào)試信息包含在可執(zhí)行文件中exe —— 命令行下:
[plain] view plain copy
%20 2)把可執(zhí)行文件exe加載到調(diào)試環(huán)境中: %20
[plain] view%20plain copy
%20 3)%20gdb為調(diào)試命令提示符,即可鍵入調(diào)試命令
(2)調(diào)試常用命令
%20 1)基本調(diào)試命令(重要)
%20 %20 %20 %20list%20行號:列出產(chǎn)品從第幾行開始的源代碼;%20 %20list%20函數(shù)名:列出某個(gè)函數(shù)的源代碼
start:開始執(zhí)行程序,停在main函數(shù)第一行語句前面等待命令;%20 next(n):執(zhí)行下一列語句
step(s):執(zhí)行下一行語句,如果有函數(shù)調(diào)用則進(jìn)入到函數(shù)中%20 ;breaktrace(或bt):查看各級函數(shù)調(diào)用及參數(shù)
frame(f)%20幀編號:選擇棧幀%20;%20 info(i)%20locals:查看當(dāng)前棧幀局部變量的值
finish:執(zhí)行到當(dāng)前函數(shù)返回,然后挺下來等待命令%20 ;%20print(p):打印表達(dá)式的值,通過表達(dá)式可以修改變量的值或者調(diào)用函數(shù)
set%20var:修改變量的值%20 ;quit:退出gdb
%20 2)高級調(diào)試命令
%20 %20 break(b)%20行號:在某一行設(shè)置斷點(diǎn)%20;break%20函數(shù)名:在某個(gè)函數(shù)開頭設(shè)置斷點(diǎn)%20; break...if...:設(shè)置條件斷點(diǎn)
continue(或c):從當(dāng)前位置開始連續(xù)而非單步執(zhí)行程序%20 ;%20 delete%20breakpoints:刪除所有斷點(diǎn)%20 ;delete%20breakpoints%20n:刪除序號為n的斷點(diǎn)
disable%20breakpoints:禁用斷點(diǎn)%20;%20 enable%20breakpoints:啟用斷點(diǎn)
info(或i)%20breakpoints:參看當(dāng)前設(shè)置了哪些斷點(diǎn);%20;%20 %20run(或r):從開始連續(xù)而非單步執(zhí)行程序
display%20變量名:跟蹤查看一個(gè)變量,每次停下來都顯示它的值%20 ;undisplay:取消對先前設(shè)置的那些變量的跟蹤
%203)格式化輸入查看變量%20print(p)
輸出格式:一般來說,GDB會(huì)根據(jù)變量的類型輸出變量的值。但你也可以自定義GDB的輸出的格式。例如,你想輸出一個(gè)整數(shù)的十六進(jìn)制,或是二進(jìn)制來查看這個(gè)整型變量的中的位的情況。要做到這樣,你可以使用GDB的數(shù)據(jù)顯示格式:
x%20按十六進(jìn)制格式顯示變量。 %20 %20 %20 %20 %20 %20 %20 %20 %20 %20 d%20按十進(jìn)制格式顯示變量。
u%20按十六進(jìn)制格式顯示無符號整型。%20 %20 %20 %20 o%20按八進(jìn)制格式顯示變量。
t%20按二進(jìn)制格式顯示變量。%20 %20 %20 %20 %20 %20 %20 %20 %20 %20 %20 %20 %20 a%20按十六進(jìn)制格式顯示變量。
c%20按字符格式顯示變量。%20 %20 %20 %20 %20 %20 %20 %20 %20 %20 %20 %20 %20 %20 %20 f%20按浮點(diǎn)數(shù)格式顯示變量。
啟動(dòng)GDB后,首先就是要設(shè)置斷點(diǎn),程序中斷后才能調(diào)試。在gdb中,斷點(diǎn)通常有三種形式:
斷點(diǎn)(BreakPoint):
在代碼的指定位置中斷,這個(gè)是我們用得最多的一種。設(shè)置斷點(diǎn)的命令是break,它通常有如下方式:
break%20<function> 在進(jìn)入指定函數(shù)時(shí)停住break%20<linenum> 在指定行號停住。break%20+/-offset 在當(dāng)前行號的前面或后面的offset行停住。offiset為自然數(shù)。break%20filename:linenum 在源文件filename的linenum行處停住。break%20...%20if%20<condition> ...可以是上述的參數(shù),condition表示條件,在條件成立時(shí)停住。比如在循環(huán)境體中,可以設(shè)置break%20if%20i=100,表示當(dāng)i為100時(shí)停住程序。可以通過info%20breakpoints%20[n]命令查看當(dāng)前斷點(diǎn)信息。此外,還有如下幾個(gè)配套的常用命令:
delete 刪除所有斷點(diǎn)delete%20breakpoint%20[n] 刪除某個(gè)斷點(diǎn)disable%20breakpoint%20[n] 禁用某個(gè)斷點(diǎn)enable%20breakpoint%20[n] 使能某個(gè)斷點(diǎn)觀察點(diǎn)(WatchPoint):
在變量讀、寫或變化時(shí)中斷,這類方式常用來定位bug。
watch%20<expr> 變量發(fā)生變化時(shí)中斷rwatch%20<expr> 變量被讀時(shí)中斷awatch%20<expr> %20變量值被讀或被寫時(shí)中斷可以通過info watchpoints [n]命令查看當(dāng)前觀察點(diǎn)信息
捕捉點(diǎn)(CatchPoint):
捕捉點(diǎn)用來補(bǔ)捉程序運(yùn)行時(shí)的一些事件。如:載入共享庫(動(dòng)態(tài)鏈接庫)、C++的異常等。通常也是用來定位bug。
捕捉點(diǎn)的命令格式是:catch <event>,event可以是下面的內(nèi)容
throw %20C++拋出的異常時(shí)中斷catch %20C++捕捉到的異常時(shí)中斷exec 調(diào)用系統(tǒng)調(diào)用exec時(shí)(只在某些操作系統(tǒng)下有用)fork 調(diào)用系統(tǒng)調(diào)用fork時(shí)(只在某些操作系統(tǒng)下有用)vfork 調(diào)用系統(tǒng)調(diào)用vfork時(shí)(只在某些操作系統(tǒng)下有用)load%20或%20load%20<libname>%20 載入共享庫時(shí)(只在某些操作系統(tǒng)下有用)unload%20或%20unload%20<libname> 卸載共享庫時(shí)(只在某些操作系統(tǒng)下有用)另外,還有一個(gè)tcatch%20<event>,功能類似,不過他只設(shè)置一次捕捉點(diǎn),當(dāng)程序停住以后,應(yīng)點(diǎn)被自動(dòng)刪除。
捕捉點(diǎn)信息的查看方式和代碼斷點(diǎn)的命令是一樣的,這里就不多介紹了。
在特定線程中中斷
你可以定義你的斷點(diǎn)是否在所有的線程上,或是在某個(gè)特定的線程。GDB很容易幫你完成這一工作。
break%20<linespec>%20thread%20<threadno>break%20<linespec>%20thread%20<threadno>%20if%20...linespec指定了斷點(diǎn)設(shè)置在的源程序的行號。threadno指定了線程的ID,注意,這個(gè)ID是GDB分配的,你可以通過"info%20threads"命令來查看正在運(yùn)行程序中的線程信息。如果你不指定thread%20<threadno>則表示你的斷點(diǎn)設(shè)在所有線程上面。你還可以為某線程指定斷點(diǎn)條件。如:
%20(gdb)%20break%20frik.c:13%20thread%2028%20if%20bartab%20>%20lim
當(dāng)你的程序被GDB停住時(shí),所有的運(yùn)行線程都會(huì)被停住。這方便你你查看運(yùn)行程序的總體情況。而在你恢復(fù)程序運(yùn)行時(shí),所有的線程也會(huì)被恢復(fù)運(yùn)行。那怕是主進(jìn)程在被單步調(diào)試時(shí)。
恢復(fù)程序運(yùn)行和單步調(diào)試
在gdb中,和調(diào)試步進(jìn)相關(guān)的命令主要有如下幾條:
continue 繼續(xù)運(yùn)行程序直到下一個(gè)斷點(diǎn)(類似于VS里的F5)next 逐過程步進(jìn),不會(huì)進(jìn)入子函數(shù)(類似VS里的F10)setp 逐語句步進(jìn),會(huì)進(jìn)入子函數(shù)(類似VS里的F11)until 運(yùn)行至當(dāng)前語句塊結(jié)束finish 運(yùn)行至函數(shù)結(jié)束并跳出,并打印函數(shù)的返回值(類似VS的Shift+F11)PS:這些命令大部分可以簡寫為第一個(gè)字母,在日常使用過程中,往往只會(huì)輸入第一個(gè)字符即可執(zhí)行該命令,我標(biāo)紅的即是通常的使用方式。這幾條命令使用非常頻繁,并且可以帶一些附加參數(shù)以實(shí)現(xiàn)高級功能,需要熟練掌握。
大概三步走~~~(%20#%20./core_dump_run運(yùn)行時(shí)產(chǎn)生段錯(cuò)誤,但是編譯階段gcc%20–o%20[]不報(bào)錯(cuò))
第一步:設(shè)置core文件大小為無限(默認(rèn)為0,即不產(chǎn)生)如果想讓系統(tǒng)在信號中斷造成的錯(cuò)誤時(shí)產(chǎn)生core文件,我們需要在shell中按如下設(shè)置:#設(shè)置core文件大小為無限ulimit%20-c%20unlimited#設(shè)置core文件大小為1000kbulimit%20–c%201000
#查看設(shè)置的大小
ulimit%20–c
[@sjs_37_33%20t_core_gdb]#%20ulimit-c
100000
第二步:#gcc(g++)-g%20-o%20core_dump_run%20core_dump_test.c,并運(yùn)行#./core_dump_run產(chǎn)生core.pid文件
第三步:#gdb%20core_dump_runcore.pid 即可調(diào)試 (gdb)start%20-à nextà bk直接到錯(cuò)誤之處
附加:對于第三步中提示信息太少的,但是提供了錯(cuò)誤地址的可以反編譯:objdump
#objdump%20–d%20 core_dump_run
代碼:
[cpp] view%20plain copy(3)gdb多線程調(diào)試
先介紹一下GDB多線程調(diào)試的基本命令。
info threads 顯示當(dāng)前可調(diào)試的所有線程,每個(gè)線程會(huì)有一個(gè)GDB為其分配的ID,后面操作線程的時(shí)候會(huì)用到這個(gè)ID。前面有*的是當(dāng)前調(diào)試的線程。
thread ID 切換當(dāng)前調(diào)試的線程為指定ID的線程。
break thread_test.c:123 thread all 在所有線程中相應(yīng)的行上設(shè)置斷點(diǎn)
thread apply ID1 ID2 command 讓一個(gè)或者多個(gè)線程執(zhí)行GDB命令command。
thread apply all command 讓所有被調(diào)試線程執(zhí)行GDB命令command。
set scheduler-locking off|on|step 估計(jì)是實(shí)際使用過多線程調(diào)試的人都可以發(fā)現(xiàn),在使用step或者continue命令調(diào)試當(dāng)前被調(diào)試線程的時(shí)候,其他線程也是同時(shí)執(zhí)行的,怎么只讓被調(diào)試程序執(zhí)行呢?通過這個(gè)命令就可以實(shí)現(xiàn)這個(gè)需求。off不鎖定任何線程,也就是所有線程都執(zhí)行,這是默認(rèn)值。 on只有當(dāng)前被調(diào)試程序會(huì)執(zhí)行。 step在單步的時(shí)候,除了next過一個(gè)函數(shù)的情況(熟悉情況的人可能知道,這其實(shí)是一個(gè)設(shè)置斷點(diǎn)然后continue的行為)以外,只有當(dāng)前線程會(huì)執(zhí)行。
gdb對于多線程程序的調(diào)試有如下的支持:
· 線程產(chǎn)生通知:在產(chǎn)生新的線程時(shí), gdb會(huì)給出提示信息
(gdb) rStarting program: /root/thread [New Thread 1073951360 (LWP 12900)] [New Thread 1082342592 (LWP 12907)]---以下三個(gè)為新產(chǎn)生的線程[New Thread 1090731072 (LWP 12908)][New Thread 1099119552 (LWP 12909)]
· 查看線程:使用info threads可以查看運(yùn)行的線程。
(gdb) info threads 4 Thread 1099119552 (LWP 12940) 0xffffe002 in ?? () 3 Thread 1090731072 (LWP 12939) 0xffffe002 in ?? () 2 Thread 1082342592 (LWP 12938) 0xffffe002 in ?? ()* 1 Thread 1073951360 (LWP 12931) main (argc=1,argv=0xbfffda04) at thread.c:21(gdb)
注意,行首的藍(lán)色文字為gdb分配的線程號,對線程進(jìn)行切換時(shí),使用該該號碼,而不是上文標(biāo)出的綠色數(shù)字。
另外,行首的紅色星號標(biāo)識了當(dāng)前活動(dòng)的線程
· 切換線程:使用 threadTHREADNUMBER 進(jìn)行切換,THREADNUMBER 為上文提到的線程號。下例顯示將活動(dòng)線程從 1 切換至 4。
(gdb) info threads 4 Thread 1099119552 (LWP 12940) 0xffffe002 in ?? () 3 Thread 1090731072 (LWP 12939) 0xffffe002 in ?? () 2 Thread 1082342592 (LWP 12938) 0xffffe002 in ?? ()* 1 Thread 1073951360 (LWP12931) main (argc=1, argv=0xbfffda04) at thread.c:21(gdb) thread 4[Switching to thread 4 (Thread 1099119552 (LWP 12940))]#0 0xffffe002 in ?? ()(gdb) info threads* 4 Thread 1099119552 (LWP12940) 0xffffe002 in ?? () 3 Thread 1090731072 (LWP 12939) 0xffffe002 in ?? () 2 Thread 1082342592 (LWP 12938) 0xffffe002 in ?? () 1 Thread 1073951360 (LWP 12931) main (argc=1,argv=0xbfffda04) at thread.c:21(gdb)
后面就是直接在你的線程函數(shù)里面設(shè)置斷點(diǎn),然后continue到那個(gè)斷點(diǎn),一般情況下多線程的時(shí)候,由于是同時(shí)運(yùn)行的,最好設(shè)置 setscheduler-locking on
這樣的話,只調(diào)試當(dāng)前線程
三:個(gè)人心得
(0)記錄個(gè)人成長中的收獲的點(diǎn)點(diǎn)滴滴,哪怕跌倒再次爬起來,也是一種難得的經(jīng)驗(yàn) —— 我一直為自己前幾年沒有留下成長記憶而苦惱,如今時(shí)時(shí)刻刻的督促自己寫一寫自己的聲音。
(1)哪怕一周花一到兩個(gè)小時(shí)的時(shí)間,整理一下自己當(dāng)下的心得和學(xué)習(xí)所得;從短期看:這會(huì)加深自己對知識的理解;從長遠(yuǎn)來看:雖然花費(fèi)了一兩個(gè)小時(shí),但是最終你是賺了的。
(2)也許我們都會(huì)有類似的經(jīng)歷 —— 我們今天遇到的難題,前幾天剛剛遇到過,處理過;但是,遺憾的是,自己忘記如何處理的了,更悲催的是,我們都忘了是哪一個(gè)模塊里的;僅僅記得剛剛處理過這個(gè)問題;糾結(jié)糾結(jié) ~~~浪費(fèi)同樣的時(shí)間處理一個(gè)相似的問題。
新聞熱點(diǎn)
疑難解答
圖片精選