1、printf段錯誤(core dump): 一個格式化輸出引起的問題
貼一個簡單的例子:
#include <stdio.h>
int main(){
int len = sizeof(int);
printf("%s\n",len);
return 0;
}
root@ubuntu:test#gcc test.c test.c: In function ‘main’: test.c:5:2: warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘int’ [-Wformat=] printf("%s\n",len); ^
root@ubuntu:test# ./a.out Segmentation fault (core dumped)
由於項目文件比較大,用Makefile編譯也沒有注意一些重要的編譯警告,而且還升級了版本,找了兩三天才發現用gdb調試后才發現還真是printf導致自己的程序段錯誤。
貼一下項目gdb運行錯誤:線程名為CoreThread接收到信號段錯誤出現
(gdb) bt
Thread 12 "CoreThread" received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7fffed7fa700 (LWP 21396)]
0x00007ffff6d95cd0 in _IO_vfprintf_internal (s=0x7ffff710c620 <_IO_2_1_stdout_>,
format=<optimized out>, ap=ap@entry=0x7fffed7f9cf8) at vfprintf.c:1632
1632 vfprintf.c: No such file or directory.
2、用gdb的 “bt”命令顯示當前調用堆棧
(gdb) bt
#0 0x00007ffff6d95cd0 in _IO_vfprintf_internal (s=0x7ffff710c620 <_IO_2_1_stdout_>,
format=<optimized out>, ap=ap@entry=0x7fffed7f9cf8) at vfprintf.c:1632
#1 0x00007ffff6e5daef in ___printf_chk (flag=1, format=<optimized out>)
at printf_chk.c:35
#2 0x000000000041f10c in printf (
__fmt=0x5b3bc0 "%s, startPushStream success, channel: %d, session_id:%s;lv_stream_type_e:%d, bastime: %d, offset: %d, worktype: %d\n")
at /usr/include/x86_64-linux-gnu/bits/stdio2.h:104
#3 StartPushStream (channelId=channelId@entry=0, session=session@entry=0x7fffed7f9ea0,
type=type@entry=SDK_LV_STREAM_CMD_LIVE) at nvr_demo/nvrCallback.cpp:235
#4 0x000000000042004c in NvrStartPushStreamingCb (channelId=0,
session=0x7fffed7f9ea0, param=0x7fffd400be20) at nvr_demo/nvrCallback.cpp:584
#5 0x0000000000417140 in StartNvrPushStreamingCb (auth=0x1407f80, param=0x7fffd400be20)
at ../src/core_cb/cb_to_cloud.c:885
#6 0x0000000000462619 in linkvisual::JobPool::jobProcess(void*) ()
#7 0x00007ffff71186ba in start_thread (arg=0x7fffed7fa700) at pthread_create.c:333
#8 0x00007ffff6e4e51d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:109
(gdb)
3、查找錯誤原因
根據gdb的當前調用堆棧信息我們定位到了#2這行找到startPushStream這個函數里面的printf,發現新版本的session_id的類型為int,而這里打印卻用%s,導致了段錯誤,由於舊版本的session_id類型為char *所以沒有錯誤。
當自己的程序出現段錯誤的時候靈活運用gdb可以加快定位錯誤的速度節省時間,而不是添加打印這種比較繁瑣的調試定位方法。
4、gdb的一些常用是用方法
1、運行命令
run:簡記為 r ,其作用是運行程序,當遇到斷點后,程序會在斷點處停止運行,等待用戶輸入下一步的命令。
continue (簡寫c ):繼續執行,到下一個斷點處(或運行結束)
next:(簡寫 n),單步跟蹤程序,當遇到函數調用時,也不進入此函數體;此命令同 step 的主要區別是,step 遇到用戶自定義的函數,將步進到函數中去運行,而 next 則直接調用函數,不會進入到函數體內。
step (簡寫s):單步調試如果有函數調用,則進入函數;與命令n不同,n是不進入調用的函數的
until:當你厭倦了在一個循環體內單步跟蹤時,這個命令可以運行程序直到退出循環體。
until+行號: 運行至某行,不僅僅用來跳出循環
finish: 運行程序,直到當前函數完成返回,並打印函數返回時的堆棧地址和返回值及參數值等信息。
call 函數(參數):調用程序中可見的函數,並傳遞“參數”,如:call gdb_test(55)
quit:簡記為 q ,退出gdb
2、設置斷點
break n (簡寫b n):在第n行處設置斷點
(可以帶上代碼路徑和代碼名稱: b OAGUPDATE.cpp:578)
b fn1 if a>b:條件斷點設置
break func(break縮寫為b):在函數func()的入口處設置斷點,如:break cb_button
delete 斷點號n:刪除第n個斷點
disable 斷點號n:暫停第n個斷點
enable 斷點號n:開啟第n個斷點
clear 行號n:清除第n行的斷點
info b (info breakpoints) :顯示當前程序的斷點設置情況
delete breakpoints:清除所有斷點:
3、查看源碼
list :簡記為 l ,其作用就是列出程序的源代碼,默認每次顯示10行。
list 行號:將顯示當前文件以“行號”為中心的前后10行代碼,如:list 12
list 函數名:將顯示“函數名”所在函數的源代碼,如:list main
list :不帶參數,將接着上一次 list 命令的,輸出下邊的內容。
4、打印表達式
print 表達式:簡記為 p ,其中“表達式”可以是任何當前正在被測試程序的有效表達式,比如當前正在調試C語言的程序,那么“表達式”可以是任何C語言的有效表達式,包括數字,變量甚至是函數調用。
print a:將顯示整數 a 的值
print ++a:將把 a 中的值加1,並顯示出來
print name:將顯示字符串 name 的值
print gdb_test(22):將以整數22作為參數調用 gdb_test() 函數
print gdb_test(a):將以變量 a 作為參數調用 gdb_test() 函數
display 表達式:在單步運行時將非常有用,使用display命令設置一個表達式后,它將在每次單步進行指令后,緊接着輸出被設置的表達式及值。如: display a
watch 表達式:設置一個監視點,一旦被監視的“表達式”的值改變,gdb將強行終止正在被調試的程序。如: watch a
whatis :查詢變量或函數
info function: 查詢函數
擴展info locals: 顯示當前堆棧頁的所有變量
5、查看運行信息
where/bt :當前運行的堆棧列表;
bt backtrace 顯示當前調用堆棧
up/down 改變堆棧顯示的深度
set args 參數:指定運行時的參數
show args:查看設置好的參數
info program: 來查看程序的是否在運行,進程號,被暫停的原因。
6、分割窗口
layout:用於分割窗口,可以一邊查看代碼,一邊測試:
layout src:顯示源代碼窗口
layout asm:顯示反匯編窗口
layout regs:顯示源代碼/反匯編和CPU寄存器窗口
layout split:顯示源代碼和反匯編窗口
Ctrl + L:刷新窗口