Linux下C/C++語言的調試 gdb工具 筆記

參考資料


學習地址收錄

http://blog.csdn.net/tenfyguo/article/details/8159176/



1. Gdb進行調試的是可執行文件, 而不是”.c”源文件, 因此, 需要先通過Gcc編譯生成可執行文件才能用Gdb進行調試.

一定要加上選項”-g,這樣編譯出的可執行代碼中才包含調試信息, 否則Gdb無法載入該可執行文件.

在Gdb中鍵入l(list的縮寫)可以查看所載入的文件


2.

在Gdb中設置斷點非常簡單, 只需在”b”後加入對應的行號即可(這是最常用的方式).

(gdb) b 9

注意: 該斷點的作用是當程序運行到第 9 行時暫停(第 8 行執行完畢, 第 9 行未執行)

查看斷點信息

(gdb) info b

接下來就可運行代碼了, Gdb默認從首行開始運行代碼, 可鍵入”r”(run的縮寫)即可. 若想從程序中指定的行開始運行,可在r後面加上行號.

(gdb) r


3.單步運行可以使用n(next的縮寫)或者s(step的縮寫), 它們之間的區別在於: 若有函數調用的時候,s會進入該函數而n不會.因此, s就類似於VC等工具中的”step in”, n就類似於VC等工具中的”step over”.

查看變量值

鍵入p(print的縮寫)+變量名即可查看該變量在此時的值

 

4.從指定行開始運行

 r 行號


5.帶有命令行參數的gdb調試


================================================================================================

詳解coredump  摘錄

地址來源:http://blog.csdn.net/tenfyguo/article/details/8159176/


1.設置corefile文件大小 格式和路徑

(1)設置core文件大小

[root@localhost gdbtest]# ulimit -c unlimited

[root@localhost gdbtest]# ulimit -a
core file size          (blocks, -c) unlimited

但當前設置的ulimit只對當前會話有效,若想系統均有效,則需要進行如下設置:
在/etc/profile中加入以下一行,這將允許生成coredump文件
ulimit-c unlimited

 (2)設置core文件路徑和格式

使程序崩潰時生成的coredump文件位於/data/coredump/目錄下

使用下面的命令使kernel生成名字爲core.filename.pid格式的core dump文件:
[root@localhost gdbtest]# echo "/home/shentan/backend_dev/gdbtest/core.%e.%p" >/proc/sys/kernel/core_pattern
[root@localhost gdbtest]# cat /proc/sys/kernel/core_pattern
/home/shentan/backend_dev/gdbtest/core.%e.%p

2.gdb定位coredump文件

(1)運行程序  生成core文件

[root@localhost gdbtest]# ./test 
0
Floating point exception (core dumped)
[root@localhost gdbtest]# ll
total 152
-rw-------. 1 root    root    290816 Mar 14 08:16 core.test.2635
-rwxrwxr-x. 1 shentan shentan  15592 Mar 14 07:32 test
-rw-rw-r--. 1 shentan shentan    213 Mar 14 07:32 test.cpp

(2)gdb定位core文件

[root@localhost gdbtest]# gdb ./test core.test.2635
GNU gdb (GDB) Fedora (7.3.50.20110722-9.fc16)
Copyright (C) 2011 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/shentan/backend_dev/gdbtest/test...done.
[New LWP 2635]
Missing separate debuginfo for 
Try: yum --disablerepo='*' --enablerepo='*-debuginfo' install /usr/lib/debug/.build-id/db/8dfab1cb00f1914573d3aa11e942b2d2a9b946
Core was generated by `./test'.
Program terminated with signal 8, Arithmetic exception.

#0  0x0804867f in main () at test.cpp:12
12 cout << 5/0 << endl;
Missing separate debuginfos, use: debuginfo-install glibc-2.14.90-14.i686 libgcc-4.6.2-1.fc16.i686 libstdc++-4.6.2-1.fc16.i686
(gdb) 

3.coredump產生的幾種可能情況

造成程序coredump的原因有很多,這裏總結一些比較常用的經驗吧:

 1,內存訪問越界

  a) 由於使用錯誤的下標,導致數組訪問越界。

  b) 搜索字符串時,依靠字符串結束符來判斷字符串是否結束,但是字符串沒有正常的使用結束符。

  c) 使用strcpy, strcat, sprintf, strcmp,strcasecmp等字符串操作函數,將目標字符串讀/寫爆。應該使用strncpy, strlcpy, strncat, strlcat, snprintf, strncmp, strncasecmp等函數防止讀寫越界。

 2,多線程程序使用了線程不安全的函數。

應該使用下面這些可重入的函數,它們很容易被用錯:

_r函數

asctime_r(3c) gethostbyname_r(3n) getservbyname_r(3n)ctermid_r(3s) gethostent_r(3n) getservbyport_r(3n) ctime_r(3c) getlogin_r(3c)getservent_r(3n) fgetgrent_r(3c) getnetbyaddr_r(3n) getspent_r(3c)fgetpwent_r(3c) getnetbyname_r(3n) getspnam_r(3c) fgetspent_r(3c)getnetent_r(3n) gmtime_r(3c) gamma_r(3m) getnetgrent_r(3n) lgamma_r(3m) getauclassent_r(3)getprotobyname_r(3n) localtime_r(3c) getauclassnam_r(3) etprotobynumber_r(3n)nis_sperror_r(3n) getauevent_r(3) getprotoent_r(3n) rand_r(3c) getauevnam_r(3)getpwent_r(3c) readdir_r(3c) getauevnum_r(3) getpwnam_r(3c) strtok_r(3c) getgrent_r(3c)getpwuid_r(3c) tmpnam_r(3s) getgrgid_r(3c) getrpcbyname_r(3n) ttyname_r(3c)getgrnam_r(3c) getrpcbynumber_r(3n) gethostbyaddr_r(3n) getrpcent_r(3n)

 3,多線程讀寫的數據未加鎖保護。

對於會被多個線程同時訪問的全局數據,應該注意加鎖保護,否則很容易造成coredump

 4,非法指針

  a) 使用空指針

  b) 隨意使用指針轉換。一個指向一段內存的指針,除非確定這段內存原先就分配爲某種結構或類型,或者這種結構或類型的數組,否則不要將它轉換爲這種結構或類型的指針,而應該將這段內存拷貝到一個這種結構或類型中,再訪問這個結構或類型。這是因爲如果這段內存的開始地址不是按照這種結構或類型對齊的,那麼訪問它時就很容易因爲bus error而core dump。

 5,堆棧溢出

不要使用大的局部變量(因爲局部變量都分配在棧上),這樣容易造成堆棧溢出,破壞系統的棧和堆結構,導致出現莫名其妙的錯誤。


4.gdb命令摘錄

l 列出函數代碼及其行數

b 16 在代碼16行處設置斷點

b func 在函數func處設置斷點

r 運行程序  --到第一個斷點處停止,使用n繼續執行

n 單條執行語句

p i 打印i變量的值

bt  查看函數的堆棧

finish 退出函數

q 結束調試


5.可重入函數和線程安全函數

參考:http://blog.csdn.net/lmh12506/article/details/7169361

(1)可重入函數

可重入函數,描述的是函數被多次調用但是結果具有可再現性
如果fun(),中,使用了static變量、返回全局變量、調用非可重入函數等等,帶有全局性的操作,
都將會導致2次以上調用fun()的結果的不可再現性(當然,有些時候使用了static、全局變量等等,
不一定導致調用結果不可再現性)。只要使調用結果具有可再現性,那麼該函數就是可重入的。
爲了保證函數是可重入的,需要做到一下幾點:
1,不在函數內部使用靜態或者全局數據
2,不返回靜態或者全局數據,所有的數據都由函數調用者提供
3,使用本地數據,或者通過製作全局數據的本地拷貝來保護全局數據
4,如果必須訪問全局數據,使用互斥鎖來保護 <---
5,不調用不可重入函數


可重入函數也可以這樣理解,重入即表示重複進入,首先它意味着這個函數可以被中斷,其次意味着它除了使用自己棧上的變量以外不依賴於任何環境(包括static),

這樣的函數就是purecode(純代碼)可重入,可以允許有多個該函數的副本在運行,由於它們使用的是分離的棧,所以不會互相干擾。

如果確實需要訪問全局變量(包括static),一定要注意實施互斥手段。可重入函數在並行運行環境中非常重要,但是一般要爲訪問全局變量付出一些性能代價。

編寫可重入函數時,若使用全局變量,則應通過關中斷、信號量(即P、V操作)等手段對其加以保護。
若對所使用的全局變量不加以保護,則此函數就不具有可重入性,即當多個進程調用此函數時,很有可能使有關全局變量變爲不可知狀態。

(2)線程安全函數

如果一個函數能夠安全的同時被多個線程調用而得到正確的結果,那麼,我們說這個函數是線程安全的
所謂安全,一切可能導致結果不正確的因素都是不安全的調用。

線程安全,是針對多線程而言的。那麼和可重入聯繫起來,我們可以斷定,可重入函數必定是線程安全的,但是線程安全的,不一定是可重入的。
不可重入函數,函數調用結果不具有可再現性,可以通過互斥鎖等機制,使之能安全的同時被多個線程調用,那麼,這個不可重入函數就是轉換成了線程安全。

(3)二者關係

可重入函數必定是線程安全的,但是線程安全的,不一定是可重入的。


發佈了63 篇原創文章 · 獲贊 9 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章