1. 程式人生 > >Linux除錯工具strace和gdb常用命令小結-轉

Linux除錯工具strace和gdb常用命令小結-轉

最近在Linux環境下做C語言專案,由於是在一個原有專案基礎之上進行二次開發,而且專案工程龐大複雜,出現了不少問題,其中遇到最多、花費時間最長的問題就是著名的“段錯誤”(Segmentation Fault)。藉此機會系統學習了一下,這裡對Linux環境下的段錯誤做個小結,方便以後同類問題的排查與解決。

1. 段錯誤是什麼

一句話來說,段錯誤是指訪問的記憶體超出了系統給這個程式所設定的記憶體空間,例如訪問了不存在的記憶體地址、訪問了系統保護的記憶體地址、訪問了只讀的記憶體地址等等情況。這裡貼一個對於“段錯誤”的準確定義(參考Answers.com):

複製程式碼 A segmentation fault (often shortened to segfault) is a particular error condition that can occur during the operation of computer software. In short, a segmentation fault occurs when
a program attempts to access a memory location that it is notallowed to access, or attempts to access a memory location in a way that is notallowed (e.g., attempts to write to a read-only location, or to overwrite part of the operating system). Systems based onprocessors like the Motorola 68000 tend to refer to these events as Address or Bus errors.

Segmentation is one approach to memory management and protection in the operating system. It has been superseded by paging for
most purposes, but much of the terminology of segmentation is still used, "segmentation fault" being an example. Some operating systems still have segmentation at some logical level although paging is used as the main memory management policy.

OnUnix-like operating systems, a process that accesses invalid memory receives the SIGSEGV signal. On
Microsoft Windows, a process that accesses invalid memory receives the STATUS_ACCESS_VIOLATION exception. 複製程式碼 2. 段錯誤產生的原因2.1 訪問不存在的記憶體地址 複製程式碼 #include<stdio.h>
#include<stdlib.h>
voidmain()
{
int*ptr = NULL;
*ptr = 0;
} 複製程式碼 2.2 訪問系統保護的記憶體地址 複製程式碼 #include<stdio.h>
#include<stdlib.h>
voidmain()
{
int*ptr = (int*)0;
*ptr = 100;
} 複製程式碼 2.3 訪問只讀的記憶體地址 複製程式碼 #include<stdio.h>
#include<stdlib.h>
#include<string.h>
voidmain()
{
char*ptr = "test";
strcpy(ptr, "TEST");
} 複製程式碼 2.4 棧溢位 複製程式碼 #include<stdio.h>
#include<stdlib.h>
voidmain()
{
main();
} 複製程式碼

等等其他原因。

3. 段錯誤資訊的獲取

程式發生段錯誤時,提示資訊很少,下面有幾種檢視段錯誤的發生資訊的途徑。

3.1 dmesg

dmesg可以在應用程式crash掉時,顯示核心中儲存的相關資訊。如下所示,通過dmesg命令可以檢視發生段錯誤的程式名稱、引起段錯誤發生的記憶體地址、指令指標地址、堆疊指標地址、錯誤程式碼、錯誤原因等。以程式2.3為例:

[email protected]:~/segfault$ dmesg
[ 2329.479037] segfault3[2700]: segfault at 80484e0 ip 00d2906a sp bfbbec3c error 7 in libc-2.10.1.so[cb4000+13e000] 3.2 -g

使用gcc編譯程式的原始碼時,加上-g引數,這樣可以使得生成的二進位制檔案中加入可以用於gdb除錯的有用資訊。以程式2.3為例:

[email protected]:~/segfault$ gcc -g -o segfault3 segfault3.c 3.3 nm

使用nm命令列出二進位制檔案中的符號表,包括符號地址、符號型別、符號名等,這樣可以幫助定位在哪裡發生了段錯誤。以程式2.3為例:

複製程式碼 [email protected]:~/segfault$ nm segfault3
08049f20 d _DYNAMIC
08049ff4 d _GLOBAL_OFFSET_TABLE_
080484dc R _IO_stdin_used
w _Jv_RegisterClasses
08049f10 d __CTOR_END__
08049f0c d __CTOR_LIST__
08049f18 D __DTOR_END__
08049f14 d __DTOR_LIST__
080484ec r __FRAME_END__
08049f1c d __JCR_END__
08049f1c d __JCR_LIST__
0804a014 A __bss_start
0804a00c D __data_start
08048490 t __do_global_ctors_aux
08048360 t __do_global_dtors_aux
0804a010 D __dso_handle
w __gmon_start__
0804848a T __i686.get_pc_thunk.bx
08049f0c d __init_array_end
08049f0c d __init_array_start
08048420 T __libc_csu_fini
08048430 T __libc_csu_init
[email protected]@GLIBC_2.0
0804a014 A _edata
0804a01c A _end
080484bc T _fini
080484d8 R _fp_hw
080482bc T _init
08048330 T _start
0804a014 b completed.6990
0804a00c W data_start
0804a018 b dtor_idx.6992
080483c0 t frame_dummy
080483e4 T main
[email protected]@GLIBC_2.0 複製程式碼 3.4 ldd

使用ldd命令檢視二進位制程式的共享連結庫依賴,包括庫的名稱、起始地址,這樣可以確定段錯誤到底是發生在了自己的程式中還是依賴的共享庫中。以程式2.3為例:

[email protected]:~/segfault$ ldd ./segfault3
linux-gate.so.1 =>  (0x00e08000)
libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0x00675000)
/lib/ld-linux.so.2 (0x00482000) 4. 段錯誤的除錯方法4.1 使用printf輸出資訊

這個是看似最簡單但往往很多情況下十分有效的除錯方式,也許可以說是程式設計師用的最多的除錯方式。簡單來說,就是在程式的重要程式碼附近加上像printf這類輸出資訊,這樣可以跟蹤並打印出段錯誤在程式碼中可能出現的位置。

為了方便使用這種方法,可以使用條件編譯指令#ifdef DEBUG和#endif把printf函式包起來。這樣在程式編譯時,如果加上-DDEBUG引數就能檢視除錯資訊;否則不加該引數就不會顯示除錯資訊。

4.2 使用gcc和gdb4.2.1 除錯步驟

 1、為了能夠使用gdb除錯程式,在編譯階段加上-g引數,以程式2.3為例:

[email protected]:~/segfault$ gcc -g -o segfault3 segfault3.c

2、使用gdb命令除錯程式:

複製程式碼 [email protected]:~/segfault$ gdb ./segfault3 
GNU gdb (GDB) 7.0-ubuntu
Copyright (C) 2009 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" fordetails.
This GDB was configured as "i486-linux-gnu".
Forbug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/panfeng/segfault/segfault3...done.
(gdb)  複製程式碼

3、進入gdb後,執行程式:

複製程式碼 (gdb) run
Starting program: /home/panfeng/segfault/segfault3 

Program received signal SIGSEGV, Segmentation fault.
0x001a306a in memcpy () from /lib/tls/i686/cmov/libc.so.6
(gdb)  複製程式碼

從輸出看出,程式2.3收到SIGSEGV訊號,觸發段錯誤,並提示地址0x001a306a、呼叫memcpy報的錯,位於/lib/tls/i686/cmov/libc.so.6庫中。

4、完成除錯後,輸入quit命令退出gdb:

複製程式碼 (gdb) quit
A debugging session is active.

Inferior 1 [process 3207] will be killed.

Quit anyway? (y or n) y 複製程式碼 4.2.2 適用場景

1、僅當能確定程式一定會發生段錯誤的情況下使用。

2、當程式的原始碼可以獲得的情況下,使用-g引數編譯程式。

3、一般用於測試階段,生產環境下gdb會有副作用:使程式執行減慢,執行不夠穩定,等等。

4、即使在測試階段,如果程式過於複雜,gdb也不能處理。

4.3 使用core檔案和gdb

在4.2節中提到段錯誤會觸發SIGSEGV訊號,通過man 7 signal,可以看到SIGSEGV預設的handler會列印段錯誤出錯資訊,併產生core檔案,由此我們可以藉助於程式異常退出時生成的core檔案中的除錯資訊,使用gdb工具來除錯程式中的段錯誤。

4.3.1 除錯步驟

1、在一些Linux版本下,預設是不產生core檔案的,首先可以檢視一下系統core檔案的大小限制:

[email protected]:~/segfault$ ulimit -c
0

2、可以看到預設設定情況下,本機Linux環境下發生段錯誤時不會自動生成core檔案,下面設定下core檔案的大小限制(單位為KB):

[email protected]:~/segfault$ ulimit -c 1024
[email protected]:~/segfault$ ulimit -c
1024

3、執行程式2.3,發生段錯誤生成core檔案:

[email protected]:~/segfault$ ./segfault3
段錯誤 (core dumped)

4、載入core檔案,使用gdb工具進行除錯:

複製程式碼 [email protected]:~/segfault$ gdb ./segfault3 ./core 
GNU gdb (GDB) 7.0-ubuntu
Copyright (C) 2009 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" fordetails.
This GDB was configured as "i486-linux-gnu".
Forbug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/panfeng/segfault/segfault3...done.

warning: Can't read pathname forload map: 輸入/輸出錯誤.
Reading symbols from /lib/tls/i686/cmov/libc.so.6...(no debugging symbols found)...done.
Loaded symbols for/lib/tls/i686/cmov/libc.so.6
Reading symbols from /lib/ld-linux.so.2...(no debugging symbols found)...done.
Loaded symbols for/lib/ld-linux.so.2
Core was generated by `./segfault3'.
Program terminated with signal 11, Segmentation fault.
#0  0x0018506a in memcpy () from /lib/tls/i686/cmov/libc.6 複製程式碼

從輸出看出,同4.2.1中一樣的段錯誤資訊。

5、完成除錯後,輸入quit命令退出gdb:

(gdb) quit 4.3.2 適用場景

1、適合於在實際生成環境下除錯程式的段錯誤(即在不用重新發生段錯誤的情況下重現段錯誤)。

2、當程式很複雜,core檔案相當大時,該方法不可用。

4.4 使用objdump4.4.1 除錯步驟

1、使用dmesg命令,找到最近發生的段錯誤輸出資訊:

[email protected]:~/segfault$ dmesg
... ...
[17257.502808] segfault3[3320]: segfault at 80484e0 ip 0018506a sp bfc1cd6c error 7 in libc-2.10.1.so[110000+13e000]

其中,對我們接下來的除錯過程有用的是發生段錯誤的地址:80484e0和指令指標地址:0018506a。

2、使用objdump生成二進位制的相關資訊,重定向到檔案中:

[email protected]:~/segfault$ objdump -d ./segfault3 > segfault3Dump

其中,生成的segfault3Dump檔案中包含了二進位制檔案的segfault3的彙編程式碼。

3、在segfault3Dump檔案中查詢發生段錯誤的地址:

複製程式碼 [email protected]:~/segfault$ grep -n -A 10 -B 10 "80484e0" ./segfault3Dump 
121- 80483df:    ff d0                    call*%eax
122- 80483e1:    c9                       leave  
123- 80483e2:    c3                       ret    
124- 80483e3:    90                       nop
125-
126-080483e4 <main>:
127- 80483e4:    55                       push   %ebp
128- 80483e5:    89 e5                    mov    %esp,%ebp
129- 80483e7:    83 e4 f0                 and    $0xfffffff0,%esp
130- 80483ea:    83 ec 20                 sub    $0x20,%esp
131: 80483ed:    c7 44 24 1c e0 84 04     movl   $0x80484e0,0x1c(%esp)
132- 80483f4:    08 
133- 80483f5:    b8 e5 84 04 08           mov    $0x80484e5,%eax
134- 80483fa:    c7 44 24 08 05 00 00     movl   $0x5,0x8(%esp)
135- 8048401:    00 
136- 8048402:    89 44 24 04              mov    %eax,0x4(%esp)
137- 8048406:    8b 44 24 1c              mov    0x1c(%esp),%eax
138- 804840a:    89 04 24                 mov    %eax,(%esp)
139- 804840d:    e8 0a ff ff ff           call804831c <[email protected]>
140- 8048412:    c9                       leave  
141- 8048413:    c3                       ret     複製程式碼

通過對以上彙編程式碼分析,得知段錯誤發生main函式,對應的彙編指令是movl $0x80484e0,0x1c(%esp),接下來開啟程式的原始碼,找到彙編指令對應的原始碼,也就定位到段錯誤了。

4.4.2 適用場景

1、不需要-g引數編譯,不需要藉助於core檔案,但需要有一定的組合語言基礎。

2、如果使用了gcc編譯優化引數(-O1,-O2,-O3)的話,生成的彙編指令將會被優化,使得除錯過程有些難度。

4.5 使用catchsegv

catchsegv命令專門用來撲獲段錯誤,它通過動態載入器(ld-linux.so)的預載入機制(PRELOAD)把一個事先寫好的庫(/lib/libSegFault.so)載入上,用於捕捉斷錯誤的出錯資訊。

複製程式碼 [email protected]:~/segfault$ catchsegv ./segfault3
Segmentation fault (core dumped)
*** Segmentation fault
Register dump:

EAX: 00000000   EBX: 00fb3ff4   ECX: 00000002   EDX: 00000000
ESI: 080484e5   EDI: 080484e0   EBP: bfb7ad38   ESP: bfb7ad0c

EIP: 00ee806a   EFLAGS: 00010203

CS: 0073   DS: 007b   ES: 007b   FS: 0000   GS: 0033   SS: 007b

Trap: 0000000e   Error: 00000007   OldMask: 00000000
ESP/signal: bfb7ad0c   CR2: 080484e0

Backtrace:
/lib/libSegFault.so[0x3b606f]
??:0(??)[0xc76400]
/lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe6)[0xe89b56]
/build/buildd/eglibc-2.10.1/csu/../sysdeps/i386/elf/start.S:122(_start)[0x8048351]

Memory map:

00258000-00273000 r-xp 00000000 08:01 157 /lib/ld-2.10.1.so
00273000-00274000 r--p 0001a000 08:01 157 /lib/ld-2.10.1.so
00274000-00275000 rw-p 0001b000 08:01 157 /lib/ld-2.10.1.so
003b4000-003b7000 r-xp 00000000 08:01 13105 /lib/libSegFault.so
003b7000-003b8000 r--p 00002000 08:01 13105 /lib/libSegFault.so
003b8000-003b9000 rw-p 00003000 08:01 13105 /lib/libSegFault.so
00c76000-00c77000 r-xp 00000000 00:00 0 [vdso]
00e0d000-00e29000 r-xp 00000000 08:01 4817 /lib/libgcc_s.so.1
00e29000-00e2a000 r--p 0001b000 08:01 4817 /lib/libgcc_s.so.1
00e2a000-00e2b000 rw-p 0001c000 08:01 4817 /lib/libgcc_s.so.1
00e73000-00fb1000 r-xp 00000000 08:01 1800 /lib/tls/i686/cmov/libc-2.10.1.so
00fb1000-00fb2000 ---p 0013e000 08:01 1800 /lib/tls/i686/cmov/libc-2.10.1.so
00fb2000-00fb4000 r--p 0013e000 08:01 1800 /lib/tls/i686/cmov/libc-2.10.1.so
00fb4000-00fb5000 rw-p 00140000 08:01 1800 /lib/tls/i686/cmov/libc-2.10.1.so
00fb5000-00fb8000 rw-p 00000000 00:00 0
08048000-08049000 r-xp 00000000 08:01 303895 /home/panfeng/segfault/segfault3
08049000-0804a000 r--p 00000000 08:01 303895 /home/panfeng/segfault/segfault3
0804a000-0804b000 rw-p 00001000 08:01 303895 /home/panfeng/segfault/segfault3
09432000-09457000 rw-p 00000000 00:00 0 [heap]
b78cf000-b78d1000 rw-p 00000000 00:00 0
b78df000-b78e1000 rw-p 00000000 00:00 0
bfb67000-bfb7c000 rw-p 00000000 00:00 0 [stack] 複製程式碼 5. 一些注意事項

1、出現段錯誤時,首先應該想到段錯誤的定義,從它出發考慮引發錯誤的原因。

2、在使用指標時,定義了指標後記得初始化指標,在使用的時候記得判斷是否為NULL。

3、在使用陣列時,注意陣列是否被初始化,陣列下標是否越界,陣列元素是否存在等。

4、在訪問變數時,注意變數所佔地址空間是否已經被程式釋放掉。

5、在處理變數時,注意變數的格式控制是否合理等。

6. 參考資料列表

1、http://www.docin.com/p-105923877.html

2、http://blog.chinaunix.net/space.php?uid=317451&do=blog&id=92412

相關推薦

Linux除錯工具stracegdb常用命令小結-

最近在Linux環境下做C語言專案,由於是在一個原有專案基礎之上進行二次開發,而且專案工程龐大複雜,出現了不少問題,其中遇到最多、花費時間最長的問題就是著名的“段錯誤”(Segmentation Fault)。藉此機會系統學習了一下,這裡對Linux環境下的段錯誤做個小結,方便以後同類問題的排查與解決。 1

linux檔案壓縮壓縮常用命令小結

檔案壓縮和常用命令 zip是目前使用最多的文件壓縮格式。可以跨平臺使用,在linux,windows和mac os上均可使用,但是支援的壓縮率不是很高。 rar雖然普及率和速度上比zip略差一些,但是rar有更好的壓縮率,支援多卷壓縮檔案,比起zip的“跨磁碟”壓縮檔案更加

Linux編譯動態庫gdb除錯命令

Linux編譯動態庫和gdb除錯命令TOC IPC命令: 拷貝到當前目錄: cp …/day01/cleanipc . 清除ipc命令: cleanipc zhidao101 all 檢視網路連線: netstat -an | grep 8001 檢視使用者程序: ps -u

】gcc/g++常用編譯選項gdb常用除錯命令

  gcc/g++編譯器是我們寫編譯C/C++程式時離不開的編譯工具,而gdb又是除錯C/C++程式的利器,這一篇文章我們記錄一下它們的慣常用法。 gcc/g++常用編譯選項 選項 作

Linux系統管理維護常用命令

linuxLinux系統管理和維護常用命令Linux系統管理和維護常用命令ls 命令功能說明ls 命令顯示指定工作目錄下的內容,列出工作目錄所包含的文件及子目錄。語法結構:ls [選項] [路徑或文件]ls 選項及說明-a 顯示指定目錄下的所有文件以及子目錄,包含隱藏文件-d 只顯示目錄列表,不顯示文件-

Linux系統下python學習筆記——Linux中檔案目錄常用命令詳解

一、檢視目錄內容 ls命令說明: 英文單詞list的簡寫,功能為列出目錄的內容,是使用者最常用的命令字義    Linux下檔案和目錄的特點: Linux檔案或目錄名稱最長可以有256個字元 以 . 開頭的檔案為隱藏檔案,需要用-a引數才能顯示(all

linux定位應用問題的一些常用命令,特別針對記憶體執行緒分析的dump命令

1.jps找出程序號,找到對應的程序號後面才好繼續操作 2.linux檢視程序詳細資訊     ps -ef | grep 程序ID   3. dump記憶體資訊     Jmap -dump:format=b,file=YYMMddhhmm

Linux GDB常用命令

Linux GDB 常用命令如下: 1.啟動和退出gdb (1)啟動:gdb ***:顯示一段版權說明; (*** 表示可執行程式名)(2)退出:quit。有的時候輸入quit後會出現相關提示:類似於“(y/n)”,輸入y 2.顯示和查詢程式原始碼 (1)list :顯示

linuxgdb常用命令簡介

    file <檔名>   載入被除錯的可執行程式檔案。因為一般都在被除錯程式所在目錄下執行GDB,因而文字名不需要帶路徑。示例:(gdb) file gdb-sample     rRun的簡寫,執行被除錯的程式。如果此前沒有下過斷點,則執行完整個程式;如果有斷點,則程式暫停在第一個可用斷點

Linux GDB常用命令整理

設定斷點:break *0x7c00break mainbreak main.c:19列出所有斷點info br 刪除斷點delete 5delete 1-10檢視暫存器i rp $pcp $eip檢視區域性變數info locals檢視結構體display (struct

linux下redismongo常用命令

redis啟動 redis-server /etc/redis.conf redis管理員登陸  redis-cli -a 153539414 -p 40500 redis檢視基本資訊 redis-cli -a 153539414 -p 40500 info redi

linux除錯工具gdb用法

1 .編譯選項 1.1  gcc -g ソースコード名 「-g」オプションを付けてビルドを行うことで、「gdb」でデバッグ可能となります。 デバッグの為の情報が増えますので生成された実行ファイルのサイ

Linux常用命令http://www.weixuehao.com/archives/25)

close who set 更新 adduser tar halt mod user 常用指令 ls 顯示文件或目錄 -l 列出文件詳細信息l(list) -a 列出當前目錄下所有文件及目錄,包括隱藏的a(all) mkdir 創建目錄 -p 創建目錄,若無父目

linux操作Mysql數據庫常用命令

復制代碼 roo 目錄 update too scribe not fault primary 1、顯示數據庫 show databases; 2、選擇數據庫 use 數據庫名; 3、顯示數據庫中的表 show tables; 4、顯示數據表的結構 des

GDB常用命令簡介

進入 常用 機器 fse 參數 代碼 next 匯編 不同 1、啟動調試程序   gdb 調試對象,例如gdb app 2、運行程序   run 或這簡寫為r 3、設置斷點   有幾種不同的方式   1)break line   2) break file:line

linux常用命令

結構 remove groupadd pda time mkfs ack current ubunt Linux常用命令大全 最近都在和Linux打交道,這方面基礎比較薄弱的我只好買了本鳥哥的書看看,感覺還不錯。我覺得Linux相比windows比較麻煩的就是很多東西都要用

Linux常用命令小結

解壓縮 gzip 遞歸 bsp ln -s scp mdi -i iso sudo passwd :設置root下的密碼 mkdir:創建文件夾 clear:清除終端的屏幕 cd . : 當前目錄 cd .. :上級目錄 rm:刪除文件(rmdir:刪除目錄) touch

Linux查詢用戶組的命令

awk ado shadow tap cront cat ren users back root@PC-RENGUOQIANG:~# cat /etc/passwd root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemo

GDB常用命令使用

sig end 機制 機器 方式 行程 nbsp 運行時 step GDB(GNU Debugger)是在Unix以及類Unix系統下的調試工具。功能極其強大,幾乎涵蓋了你所需要的全部功能。 GDB主要幫忙你完成下面四個方面的功能: 1.啟動你的程序,可以按照你的定制要求隨

總結Linux 、Redis 操作常用命令

tab ESS 哈希 大對象 由於 edi always wan 情況下 Redis的配置 1. Redis默認不是以守護進程的方式運行,可以通過該配置項修改,使用yes啟用守護進程 daemonize no 2. 當Redis以守護進程方式運行時,Redis默認會把