1.啟動調(diào)試
前置條件:編譯生成執(zhí)行碼時帶上 -g,如果使用Makefile,通過給CFLAGS指定-g選項(xiàng),否則調(diào)試時沒有符號信息。
gdb program //最常用的用gdb啟動程序,開始調(diào)試的方式
gdb program core //用gdb查看core dump文件,跟蹤程序core的原因
gdb program pid //用gdb調(diào)試已經(jīng)開始運(yùn)行的程序,指定pid即可
gdb attach pid //用gdb調(diào)試已經(jīng)開始運(yùn)行的程序,指定pid即可
2.調(diào)試命令
(1)執(zhí)行命令模式
-batch選項(xiàng)。
比如:打印$pid進(jìn)程所有線程的堆棧并退出。
gdb -ex "set pagination 0" -ex "thread apply all bt" -batch -p $pid
(2).交互模式
run ? ? ? ? //運(yùn)行程序
continue ?//中斷后繼續(xù)運(yùn)行到下一個斷點(diǎn)
step ? ? ? ?//單步執(zhí)行,進(jìn)入函數(shù)
next ? ? ? //單步執(zhí)行
return ? ?//函數(shù)未執(zhí)行完,忽略未執(zhí)行的語句,返回。
finish ? ? //函數(shù)執(zhí)行完畢返回。
call ? ? ? ?//調(diào)用某一個函數(shù) fun("1234")
(backtrace)bt //顯示棧楨
bt N ? ? ? ? ? ? ?//顯示開頭N個棧楨
bt -N ? ? ? ? ? ?//顯示最后N個棧楨
(frame)f N ? ? //顯示第N層棧楨
list //顯示源碼
set directory ?//設(shè)置gdb的工作目錄?
pwd ? ? ? ? ? ? ?//當(dāng)前的工作目錄
(3)反復(fù)執(zhí)行
continue N ? ?//連續(xù)執(zhí)行cointiue N次,一般用于避免頻繁斷點(diǎn)
step N
next N
3.斷點(diǎn)
break 函數(shù)名 ? ? ? ?//設(shè)置斷在某個函數(shù)
break 文件名:行號 //設(shè)置斷在某一行
info break ? ? ? ? ? ?//查看設(shè)置的斷點(diǎn)信息
break if condition //條件斷點(diǎn)
break 函數(shù)名 thread 線程號 //設(shè)置斷點(diǎn)只斷某個線程,通過info threads 查看線程號
delete 斷點(diǎn)號 斷點(diǎn)號... //刪除一個或多個斷點(diǎn)
diable 斷點(diǎn)號 斷點(diǎn)號... //禁止一個或多個斷點(diǎn)
enable 斷點(diǎn)號 斷點(diǎn)號... //打開一個或多個斷點(diǎn)
command 斷點(diǎn)號 //斷點(diǎn)觸發(fā)時,執(zhí)行命令,一般用于打印變量
(gdb) command 1
Type commands for breakpoint(s) 1, one per line.
End with a line saying just "end".
>print x
>end
(gdb)
4.檢測點(diǎn)
watch //為表達(dá)式(變量)expr設(shè)置一個觀察點(diǎn)。一量表達(dá)式值有變化時,馬上停住程序。
rwatch //當(dāng)表達(dá)式(變量)expr被讀時,停住程序。
awatch //當(dāng)表達(dá)式(變量)的值被讀或被寫時,停住程序。
info watchpoints //列出當(dāng)前所設(shè)置了的所有觀察點(diǎn)。
經(jīng)驗(yàn):觀察某個變量是否變化,被讀或者被寫,由于變量只在某一個作用域,可以獲取變量的地址,然后觀察。
比如:觀察examined_rows變量神馬時候被修改
(1).p &examined_rows,得到地址
(2).watch *(ha_rows *) 0x7ffec8005e28,則可以觀察這個變量的變化情況。
5.查看變量
(1)設(shè)置
set print elements N //指定打印的長度,對長字符串特別有用。
set print element 0 //輸出完整的字符串
set print pretty //設(shè)置GDB打印結(jié)構(gòu)的時候,每行一個成員,并且有相應(yīng)的縮進(jìn),缺省是關(guān)閉的
print {type} variable?
比如:
(gdb) p {ABC} 0x7fffffffe710
$2 = {val = 1.5, val2 = 10}
print xxx //打印變量
p /x xxx //16進(jìn)制顯示
p str@str_len //打印字符串
info locals //打印出當(dāng)前函數(shù)中所有局部變量及其值。
info args //打印出當(dāng)前函數(shù)的參數(shù)名及其值。
display 變量 //自動打印變量
undisplay //取消自動打印
注意:默認(rèn)編譯的時候,調(diào)試過程是看不見宏的值的,編譯時候需要給選項(xiàng)。-g3
6.內(nèi)存查看
格式: x /nfu
a.n表示要顯示的內(nèi)存單元的個數(shù)?
b.f表示顯示方式, 可取如下值
(1).x 按十六進(jìn)制格式顯示變量。
(2).d 按十進(jìn)制格式顯示變量。
(3).u 按十進(jìn)制格式顯示無符號整型。
(4).o 按八進(jìn)制格式顯示變量。
(5).t 按二進(jìn)制格式顯示變量。
(6).a 按十六進(jìn)制格式顯示變量。
(7).i 指令地址格式
(8).c 按字符格式顯示變量。
(9).f 按浮點(diǎn)數(shù)格式顯示變量。
c.u表示一個地址單元的長度
(1).b表示單字節(jié),
(2).h表示雙字節(jié),
(3).w表示四字節(jié),
(4).g表示八字節(jié)
比如:x/3xh buf?
表示從內(nèi)存地址buf讀取內(nèi)容,3表示三個單位,x表示按十六進(jìn)制顯示,h表示以雙字節(jié)為一個單位。
7.多線程調(diào)試
info threads //查看線程
thread thread_no //切換到線程號
thread apply all command //所有線程都執(zhí)行命令打印棧楨
比如:thread apply all bt //所有線程都打印棧楨
(1)線程鎖
show scheduler-locking?
set scheduler-locking on
set scheduler-locking off
默認(rèn)是off,當(dāng)程序繼續(xù)運(yùn)行的時候如果有斷點(diǎn),那么就把所有的線程都停下來,直到你指定某個線程繼續(xù)執(zhí)行(thread thread_no apply continue).
但是如果直接在當(dāng)前線程執(zhí)行continue的話,默認(rèn)是會啟動所有線程。這種模式有一種副作用,如果多個線程都斷在同一個函數(shù),這時候調(diào)試會出問題。
這個時候需要打開線程鎖,但打開線程鎖,意味著其它線程不能運(yùn)行了。
(2)non-stop模式(7.0以后的版本支持)
set target-async 1
set pagination off
set non-stop on
gdb啟動了不停模式,除了斷點(diǎn)有關(guān)的線程會被停下來,其他線程會執(zhí)行。
8.信號量
(1).singal 發(fā)送信號
假定你的程序已將一個專用的 SIGINT(鍵盤輸入,或CTRL-C;信號2)信號處理程序設(shè)置成采取某個清理動作,
要想測試該信號處理程序,你可以設(shè)置一個斷點(diǎn)并使用如下命令:
(gdb) signal 2
(2).handle 攔截信號
Handle命令可控制信號的處理,他有兩個參數(shù),一個是信號名,另一個是接受到信號時該作什么。幾種可能的參數(shù)是:
* nostop 接收到信號時,不要將它發(fā)送給程序,也不要停止程序。
* stop 接受到信號時停止程序的執(zhí)行,從而允許程序調(diào)試;顯示一條表示已接受到信號的消息(禁止使用消息除外)
* print 接受到信號時顯示一條消息
* noprint 接受到信號時不要顯示消息(??且隱含著不停止程序運(yùn)行)
* pass 將信號發(fā)送給程序,從而允許你的程序去處理它、停止運(yùn)行或采取別的動作。
* nopass 停止程序運(yùn)行,但不要將信號發(fā)送給程序。?
比如:
handle SIGPIPE stop print //截獲SIGPIPE信號,程序停止并打印信息
handle SIGUSR1 nostop noprint //忽略SIGUSR1信號
9.生成環(huán)境使用GDB場景
內(nèi)核轉(zhuǎn)儲(coredump)
(1).配置產(chǎn)生core文件
前置條件:確保系統(tǒng)配置的core file size足夠,一般設(shè)置成unlimited
ulimit -c unlimited
配置corefile的參數(shù):
echo 2 > /proc/sys/fs/suid_dumpable [程序中切換用戶,也要產(chǎn)生corefile]
mkdir /tmp/corefiles?
chmod 777 /tmp/corefiles
echo "/tmp/corefiles/core">/proc/sys/kernel/core_pattern //配置core文件產(chǎn)生的目錄為/tmp/corefiles
echo "1" > /proc/sys/kernel/core_uses_pid
注意:
a.確保配置的目錄有足夠的磁盤空間,否則產(chǎn)生core文件可能不完整。
b.對于mysqld而言,要保證正確產(chǎn)生core-file,需要加上--core-file,默認(rèn)這個參數(shù)是不打開的。
c.kill -9 pid 是不能產(chǎn)生core文件的,因?yàn)镾IGKILL信號不能被捕獲。
(2).使用core文件
gdb /usr/mysql/bin/mysqld core.24556
(3).dump已經(jīng)運(yùn)行進(jìn)程的狀態(tài)信息
gdb attach pid
(gdb) generate-core-file
調(diào)試完畢后,通過detach命令退出。
另外,通過gcore pid 命令也可以dump core文件,生成在當(dāng)前目錄下。
(4).打印線程信息
pstack pid
pt-pmp -p pid
pstack和pt-pmp都可以打印線程的信息,但是pt-pmp會對同類堆棧的線程做聚合匯總,相對于pstack功能更強(qiáng)大,顯示也更友好。
(5).altert日志
這里主要針對mysqld問題排查,mysqld異常crash后,有時候在alter日志中可以看到最后crash線程的堆棧,但是一般只有函數(shù)名或一串二進(jìn)制地址,無法定位到具體是crash到哪一行,通過addr2line可以解這個問題。
比如:alter日志中記錄crash時的地址是0x64bd60,通過如下命令,可以定位到具體是哪一行
addr2line -e /usr/mysql/bin/mysqld 0x64bd60
/home/admin/131_20160715135613566_11155487_code/rpm_workspace/sql/sql_parse.cc:3067
?
評論
查看更多