gdb調試用法

@linux

1、gdb功能簡介

GDB主要幫忙你完成下面四個方面的功能:

  • 1.啓動你的程序,能夠按照你的定製要求爲所欲爲的運行程序。
  • 2.可以讓被調試的程序在你所指定的調置的斷點處停住。
  • 3.當程序被停住時,能夠檢查此時你的程序中所發生的事,以及內存狀態等。
  • 4.動態的改變你程序的執行環境。

2、gdb使用前置條件:編譯時加入debug信息。

online doc of Compiling for Debugging正則表達式

gcc/g++是在編譯時加入-g(注意 -g 參數要在 -o 以前,不然可能沒有調試信息)
-g分4個等級:shell

  • -g0等於不加-g。即不包含任何信息
  • -g1只包含最小信息,通常來講只有你不須要debug,只須要backtrace信息,而且真的很在乎程序大小,或者有其餘保密/特殊需求時纔會使用-g1。
  • –g2爲gdb默認等級,包含絕大多數你須要的信息。
  • –g3包含一些額外信息,例如包含宏定義信息。當你須要調試宏定義時,請使用-g3

3、gdb最多見的幾個用法:

1.gdb的啓動,加載程序:

1) gdb ${你的程序} 進入gdb後,輸入run(簡寫r) ${arg1} ${arg2} … ${argN}
2) gdb --args ${你的程序} ${arg1} ${arg2} … ${argN} 進入gdb後,運行run。
3) gdb進入gdb後,輸入file ${你的程序}。而後使用set args ${arg1} ${arg2} … ${argN} 設定好你的程序參數,再運行run。數組

2.調試正在運行的程序:

  • gdb ${你的程序} ${程序pid}

3. 查core:

  • gdb ${你的程序} ${core文件}

4、gdb命令分類:

info 命令

info 簡寫爲 i
 info locals :查看局部變量,能夠簡寫: i lo.
 info args :查看函數入參 :i ar
 info break : 查看斷點: i b
 info registers :查看寄存器的狀況 
 info threads :當前已知線程

print

allows overriding the output format used by the command
/o - octal
/x - hexadecimal
/d - decimal
/u - unsigned decimal
/t - binary
/f - floating point
/a - address
/c - char
/s - string安全

打印動態數組

p arr[0]@lenbash

設置打印數組不限長度

set max-value-size unlimited多線程

ptype/pty 後跟結構體名,或結構體變量

以自動換行的方式打印結構體變量: set print pretty

backtrace

backtrace :顯示棧信息(調用鏈)。簡寫爲bt。
frame x  切換到第x幀。其中x會在bt命令中顯示,從0開始。0表示棧頂。簡寫爲f。
up/down x 往棧頂/棧底移動x幀。當不輸入x時,默認爲1。

layout

(gdb) layout src:顯示源碼窗口: la  sr
(gdb) layout asm:顯示彙編窗口
(gdb) layout regs:顯示寄存器窗口
(gdb) layout split:顯示源碼和彙編窗口
(gdb) layout next:顯示下一個layout窗口
(gdb) layout prev:顯示上一個layout窗口
Ctrl + L:刷新窗口
Ctrl + x,再按1:單窗口模式
Ctrl + x,再按2:雙窗口模式
Ctrl + x,再按a:退出layout,回到執行layout以前的調試窗口。

源碼路徑設置

1, set substitute-path from_path to_path,替換源碼文件路徑。當編譯機與運行程序的機器代碼路徑不一樣時,須要使用該指令替換代碼路徑,不然你沒法在gdb中看到源碼。
2, 爲靜態庫設置源碼路徑: dir pathapp

commands

簡寫爲: comm
先查看現有的斷點,而後 comm 後跟上斷點序號,寫入這個斷點要作的事,end結束。

調試跳轉命令

  1. step 單步調試,步入當前函數。可簡寫爲s
  2. next 單步調試,步過當前函數。可簡寫爲n
  3. until 執行到當前循環完成。可簡寫爲u
  4. finish 執行到當前函數返回
  5. continue 繼續運行程序到下一個斷點,或結束。可簡寫爲c
  6. return: 強制函數返回。能夠指定返回值
  7. jump使當前執行的程序跳轉到某一行,或者跳轉到某個地址。因爲只會使程序跳轉而不會改變棧值,所以若跳出函數到另外的地方 會致使return出錯。另外,熟悉彙編的人都知道,程序運行時,有一個寄存器用於保存當前代碼所在的內存地址。因此,jump命令也就是改變了這個寄存器中的值。因而,你可使用「set $pc」來更改跳轉執行的地址。如: set $pc = 0x485
  8. call 調用函數。
  9. command m 設置程序執行到斷點m時要看的內容,例如:若是command後面沒有參數n,則命令被賦給最後一個breakpoint,這實際上是說break和command連在一塊兒用,在腳本里用就很是方便了。
command n
          >printf "x is %d\n",x
          >c
          >end
  1. set var x=10 改變當前變量x的值。也能夠這樣用:set {int}0x83040 = 10把內存地址0x83040的值強制轉換爲int並賦值爲10

5、程序中斷機制: Breakpoints, Watchpoints, and Catchpoints:

online doc

1. 斷點

是指當執行到程序某一步時,程序交出控制權進入調試器。值得注意的是,break會有一些變體:tbreak,hbreak,thbreak與rbreak。tbreak與break功能相同,只是所設置的斷點在觸發一次後自動刪除。hbreak是一個硬件斷點。thbreak則既是一個臨時的硬件斷點。注意硬件斷點須要硬件支持,某些硬件可能不支持這種類型的斷點。rbreak稍微特殊一些,它會在匹配正則表達式的所有位置加上斷點,後面會有詳細講解。除去rbreak,其餘break家族的使用方法以下:函數

查看斷點: info break , 簡寫i b

添加斷點

break 能夠縮寫爲 b
1) break xxx.cpp:y。在文件 xxx.cpp 的第 y 行加入斷點。
不指定文件時候,則會以當前執行的文件做爲斷點文件。
若程序未執行,則以包含main函數的源代碼文件做爲斷點文件。
若x.cpp和y都不指定,則以當前debugger的點做爲斷點處。
2) break x.cpp:func。在x.cpp的func函數入口處加入斷點。x.cpp能夠不提供直接使用break func。注意因爲重載(overload)的存在,所以gdb可能會詢問你但願在哪一個函數加上斷點。
3) break 0xN。在地址N處加入斷點。N必須爲一個有效的代碼段(code segment)地址。
4) 刪除多個斷點 del 1-10

保存加載斷點

1, save break file.bp 保存斷點到 file.bp
2, source file.bp 加載斷點(so file.bp)

2. 監視點。

監視點是監視內存中某個地址,當該地址的數據被改變(或者被讀取)時,程序交出控制權進入調試器。注意監視點分爲軟件模式和硬件模式:GDB 使用軟件監視點的方式是在單步執行你的程序的同時測試變量的值,因此執行程序的速度會變慢。同時,軟件監視點僅在當前線程有效。幸運的是,32 位的 Intel x86 處理器提供了 4 個特殊的調試寄存器用來方便調試程序,GDB 可使用這些寄存器創建硬件監視點。GDB 老是會優先使用硬件監視點,由於這樣不會減慢程序的執行速度。然而,可用的(enable的)硬件監視點的個數是有限的。若是你設置了過多的硬件監視點,當程序從中斷的狀態變爲執行的狀態(例如continue,until或者finish)時,GDB 可能沒法把它們所有激活。另外,活動的硬件監視點的數量只有在試圖繼續執行程序時才能知道,也就是說,即便你設置了過多的硬件監視點,gdb在你運行程序以前也不會警告你。

設置監視點的命令有3個,watch(寫監視),rwatch(讀監視)以及awatch(讀寫監視)。他們的使用方法同樣,皆爲如下幾種:

1) (r/a)watch var。var是一個變量名。當x的值改變/被讀取時,程序交出控制權進入調試器。

2) (r/a)watch *0xdeadbeef。0xdeadbeef爲一個有效地址。當該地址的內容變化/被讀取時,程序交出控制權進入調試器。

3) (r/a)watch *(int *)0xdeadbeef。0xdeadbeef爲一個有效地址。當該地址的中的int指針指向的內容變化/被讀取時,程序交出控制權進入調試器。

4) (r/a)watch -l *(int *)0xdeadbeef。0xdeadbeef爲一個有效地址。當該地址的中的int指針指向的內容變化/被讀取,或者該地址的內容變化/被讀取時,程序交出控制權進入調試器。

注意3)和4)的區別在於,當加入-l選項後,會同時監視表達式自己以及表達式指向的內容。

注意:watch 地址的時候,要加解引用符號 *

If you watch for a change in a numerically entered address you need to dereference it, as the address itself is just a constant number which will never change. GDB refuses to create a watchpoint that watches a never-changing value
(gdb) watch 0x600850
Cannot watch constant value 0x600850.
(gdb) watch (int ) 0x600850
Watchpoint 1: (int ) 6293584

三、跟蹤點(tracepoint)doc

跟蹤點與上面三個斷點不一樣之處在於,它只是跟蹤記錄信息而不會中斷程序的運行。當你的程序是realtime程序,或者與其餘的程序有交互時,你可能會但願使用跟蹤點達到監視程序而又不破壞程序自身行爲的目的。與斷點相同的是,跟蹤點會保存下在跟蹤點時的一些內存信息供使用者查閱,例如數組或者對象。

另外,tracepoints能夠經過save命令保存,以方便使用者下次再次進入程序調試時不須要重設這些跟蹤點。

四、檢查點(checkpoint) doc

gdb能夠經過fork當前進程的映像,而且稍後又能夠返回到這個狀態。這個稱之爲checkpoint。

每一個檢查點是進程的一個拷貝。這樣當一個bug很難重現,而又擔憂調試過頭了又要從頭開始重現時,能夠在估計要重現這個bug以前,作一個checkpoint,這樣即便debug過頭了,也能夠從這個checkpoint開始,而不用重啓整個程序而且期待它重現這個bug。
用法:
在當前位置設置檢查點
(gdb) checkpoint

查看檢查點
(gdb) info checkpoints

回到設置的檢查點,
(gdb) restart checkpoint-id

六,調試core文件

core dump又叫核心轉儲, 當程序運行過程當中發生異常, 程序異常退出時, 由操做系統把程序當前的內存情況存儲在一個core文件中, 叫core dump. (linux中若是內存越界會收到SIGSEGV信號,而後就會core dump)

1, 形成segment fault,產生core dump的可能緣由

1.內存訪問越界

a) 因爲使用錯誤的下標,致使數組訪問越界

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

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

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

3 多線程讀寫的數據未加鎖保護。對於會被多個線程同時訪問的全局數據,應該注意加鎖保護,不然很容易形成core dump

4 非法指針

a) 使用空指針

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

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

2, 配置操做系統使其產生core文件

首先經過ulimit命 令查看一下系統是否配置支持了dump core的功能。經過ulimit -c或ulimit -a,能夠查看core file大小的配置狀況,若是爲0,則表示系統關閉了dump core。能夠經過ulimit -c unlimited來打開。若發生了段錯誤,但沒有core dump,是因爲系統禁止core文件的生成。
解決方法:
$ulimit -c unlimited  (只對當前shell進程有效)
或在~/.bashrc 的最後加入: ulimit -c unlimited (一勞永逸)

3,調試core

gdb [exec file] [core file]

如: gdb ./test test.core
能夠產生core的c語言語句: *(char *)0 = 0;

4, 控制core文件保存位置和文件名格式

修改文件命令:
把core文件與執行程序相同路徑
echo "core-%e-%p" > /proc/sys/kernel/core_pattern
或者:
sysctl -w kernel.core_pattern=/corefile/core-%e-%p-%t kernel.core_pattern = /corefile/core-%e-%p-%t
能夠將core文件統一輩子成到/corefile目錄下,產生的文件名爲core-命令名-pid-時間戳
如下是參數列表:

%e - insert coredumping executable name into filename 添加致使產生core的命令名
%p - insert pid into filename 添加pid(進程id)
%u - insert current uid into filename 添加當前uid(用戶id)
%g - insert current gid into filename 添加當前gid(用戶組id)
%s - insert signal that caused the coredump into the filename 添加致使產生core的信號
%t - insert UNIX time that the coredump occurred into filename 添加core文件生成時的unix時間
%h - insert hostname where the coredump happened into filename 添加主機名

在內核中還有一個與coredump相關的設置,就是/proc/sys/kernel/core_uses_pid。若是這個文件的內容被配置成1,
那麼即便core_pattern中沒有設置%p,最後生成的core dump文件名仍會加上進程ID。

emacs :https://www.cnblogs.com/gaowengang/p/5799292.html
https://www.cnblogs.com/xsln/p/gdb_instructions1.html

彙編:https://www.cnblogs.com/zhangyachen/p/9227037.html

相關文章
相關標籤/搜索