在實際開發中,當遇見程序出現bug,無法一眼看出問題點時,需要使用調試的方法來找出原因。調試的方法有以下兩種:
- 在程序中插入打印語句,優點是能夠顯示程序的動態過程,比較容易檢查源程序的有關信息;缺點是效率低、可能輸入大量無關的數據,發現問題具有偶然性。
- 藉助調試工具。
下面我們主要來熟悉下各種調試工具的使用,方便更高效的進行調試
1. strace 工具
strace 就是一個通過跟蹤系統調用來讓開發者知道一個程序在後臺所做事情的工具。
每一行都是一次系統調用,等號的左邊是系統調用的函數名及其參數,右邊是該調用的返回值。
- strace 不光能追蹤系統調用,通過使用參數-c,它還能將進程所有的系統調用做一個統計分析並返回。
- 參數-o用在將strace的結果輸出到文件中
- 參數-T將每個系統調用所花費的時間打印出來,每個調用的時間花銷都體現在調用行最右邊的尖括號裏面
- 參數-t、-tt、-ttt則是記錄每次系統調用發生的時間,分別精確到秒、微秒和UNIX時間戳的微秒
- strace不光能自己初始化一個進程進行strace,還能追蹤現有進程,參數-p就是取這個作用,格式:strace -p pid (pid是指進程id)
2. gdb工具
- 啓動gdb的方法
(1)gdb program
(2)gdb program core(用gdb同時調試一個運行程序和core文件,core是程序非法執行後core dump後產生的文件)
(3)gdb program 1234(如果程序是一個服務程序,那麼可以指定這個服務程序運行時的進程ID,gdb會自動進行attach操作,並調試這個程序。並且program應該在PATH環境變量中搜索得到) - gdb的命令
(1)l:列出函數代碼及其行數
(2)b 16:在代碼16行處設置斷點
(3)b func:在函數func處設置斷點
(4)r:運行程序
(5)n:單條執行語句
(6)p i:打印i變量的值
(7)bt:查看函數的堆棧
(8)finish:退出函數
(9)q:結束調試 - coredump文件的存儲路徑
(1)在/proc/sys/kernel/core_pattern文件中可以看到core文件的存儲位置,默認值是core,也就是當前目錄,如果不是core,則是在指定的目錄下
(2)通過修改kernel的參數,可以指定內核所生成的coredump文件的文件名。eg:使用echo “/data/coredump/core.%e.%p” > /proc/sys/kernel/core_pattern可以修改core文件名的格式爲core.filename.pid。
(3)當/proc/sys/kernel/core_uses_pid文件中的內容被置爲1是,core_pattern中沒有設置%p,最後生成的core dump文件名仍會加上進程ID - 產生coredump文件的條件
(1)首先確認當前會話的能生成的coredump文件大小(ulimit -c:查看coredump文件大小的最大值,爲0時不會產生對應的coredump文件;ulimit -c unlimited:設置coredump文件的大小爲不受限制;ulimit -c [size]:設置對應的字符大小,size的單位是blocks,一般1 blocks = 512Bytes),當前設置的ulimit只對當前會話有效,若想系統均有效則在/etc/profile中加入對應的設置eg:ulimit -c unlimited
(2)當前用戶具有寫入core目錄的寫權限以及有足夠的空間
(3)產生coredump文件的原因
–>1.內存訪問越界(I.數組訪問越界;II。搜索字符串時,依靠字符串結束符來判斷字符串是否結束,但是字符串沒有正常的使用結束符;III.使用strcpy,strcat,sprintf,strcasecmp等字符串操作函數時,容易出現將目標字符串讀、寫越界的情況,應改用strncpy,strlcpy,strncat,strlcat,snprintf,strncmp,strcasecmp等函數防止讀寫越界)
–>2.多線程使用線程不安全的函數
–>3.多線程讀寫的數據未加鎖保護
–>4.非法指針,包括使用空指針或隨意使用指針轉換或野指針
–>5.堆棧溢出
3. top 工具
top命令是Linux系統下常用的性能分析工具,能夠實時顯示系統中各個進程的資源佔用情況,類似於windows的任務管理器。
- 各列所代表的意思,請自行查閱相關資料
- buffers指的是塊設備的讀寫緩衝區;cached指的是文件系統本身的頁面緩存。
4. ps 工具
Linux中的ps(process status)命令列出的是當前在運行的進程的快照
- linx上進程的5中狀態
(1)運行(正在運行或在運行隊列中等待)
(2)中斷(休眠中,受阻,在等待某個條件的形成或接受到信號)
(3)不可中斷(收到信號不喚醒和不可運行,進程必須等待直到有中斷髮生)
(4)僵死(進程已終止,但進程描述符存在,直到父進程調用wait4()系統調用後釋放)
(5)停止(進程收到SIGSTOP,SIGSTP,SIGTIN,SIGTOU信號後停止運行) - ps工具標識進程的5種狀態碼:
(1)D不可中斷:uninterruptible sleep(usually IO)
(2)R 運行:runnable(on run queue)
(3)S 中斷:sleeping
(4)T 停止:traced or stopped
(5)Z 僵死:a defunct(“zombie”)process - 顯示指定用戶信息 (eg:ps -u root)
- 顯示所有進程信息 (eg:ps -ef)
- 查找特定進程 (eg:ps -ef | grep test)
- 將目前登錄的PID與相關信息列出來 (ps -l)
- 列出目前所有的正在內存當中的程序(tty爲 pts/0等,標識由網絡連接進主機的程序)
5. 內存分析工具—Valgrind
-
Valgrind包括如下工具:
(1)Memcheck:內存檢查器
(2)Callgrind:和gprof類似的分析工具,但它對程序的運行觀察更是入微,能夠提供更多的信息
(3)Cachegrind:檢查程序中緩存使用出現的問題
(4)Helgrind:檢查多線程程序中出現的競爭問題
(5)Massif:堆棧分析器,它能測量程序在堆棧中使用了多少內存,告訴我們堆塊、堆管理塊和棧的大小
(6)Extension:可以利用core提供的功能,自己編寫特定的內存調試工具 -
C程序內存空間的組成
(1)代碼段(.text segment):存放程序執行代碼
(2)初始化數據段(.data segment):存放程序中已初始化的全局變量
(3)未初始化數據段(.bss segment):存放程序中未初始化的全局變量(bss的全稱:Block Started by Symbol)
(4)堆(heap):進程運行中被動態分配的內存段
(5)棧(stack):存放程序的局部變量,不包括static變量,static存放在數據段 -
Valgrind的官網地址:http://www.valgrind.org
-
Valgrind的使用
Valgrind默認的工具是Memcheck,也可以通過“–tool=tool name”之路指定其他的工具 -
常見的內存錯誤情況
(1)使用未初始化的內存
(2)內存讀寫越界
(3)內存覆蓋
(4)動態內存管理錯誤(I.申請和釋放不一致,使用函數不匹配;II.申請和釋放不匹配,多釋放或少釋放;III.釋放後仍然讀寫)
(5)內存泄露
memcheck將內存泄露分爲兩種,一種是可能的內存泄露(possibly lost),一種是確定的內存泄露(definitely lost)。可能的內存泄露是指仍然存在某個指針能夠訪問某塊內存,但該指針指向的已經不是該內存首地址。確定的內存泄露是指已經不能訪問這塊內存。而確定的內存泄露又分爲兩種:直接的(direct)和間接地(indirect).直接和間接地區別就是,直接是沒有任何指針指向該內存;間接是指指向該內存的指針都位於內存泄露處。