文章原地址:https://blog.csdn.net/qq_29350001/article/details/53780697
一、什麼是段錯誤?
一旦一個程序發生了越界訪問,cpu 就會產生相應的保護,於是 segmentation fault 就出現了,通過上面的解釋,段錯誤應該就是訪問了不可訪問的內存,這個內存區要麼是不存在的,要麼是受到系統保護的,還有可能是缺少文件或者文件損壞。
二、段錯誤產生的原因
下面是一些典型的段錯誤的原因:
非關聯化空指針——這是特殊情況由內存管理硬件
試圖訪問一個不存在的內存地址(在進程的地址空間)
試圖訪問內存的程序沒有權利(如內核結構流程上下文)
試圖寫入只讀存儲器(如代碼段)
1、訪問不存在的內存地址
在C代碼,分割錯誤通常發生由於指針的錯誤使用,特別是在C動態內存分配。非關聯化一個空指針總是導致段錯誤,但野指針和懸空指針指向的內存,可能會或可能不會存在,而且可能或不可能是可讀的還是可寫的,因此會導致瞬態錯誤。
- #include <stdio.h>
- int main (void)
- {
- int *ptr = NULL;
- *ptr = 0;
- return 0;
- }
- 輸出結果:
- 段錯誤(核心已轉儲)
現在,非關聯化這些變量可能導致段錯誤:非關聯化空指針通常會導致段錯誤,閱讀時從野指針可能導致隨機數據但沒有段錯誤,和閱讀從懸空指針可能導致有效數據,然後隨機數據覆蓋。
2、訪問系統保護的內存地址
- #include <stdio.h>
- int main (void)
- {
- int *ptr = (int *)0;
- *ptr = 100;
- return 0;
- }
- 輸出結果:
- 段錯誤(核心已轉儲)
3、訪問只讀的內存地址
寫入只讀存儲器提出了一個 segmentation fault,這個發生在程序寫入自己的一部分代碼段或者是隻讀的數據段,這些都是由操作系統加載到只讀存儲器。
- #include <stdio.h>
- #include <string.h>
- int main (void)
- {
- char *ptr = "test";
- strcpy (ptr, "TEST");
- return 0;
- }
- 輸出結果:
- 段錯誤(核心已轉儲)
- #include <stdio.h>
- int main (void)
- {
- char *ptr = "hello";
- *ptr = 'H';
- return 0;
- }
- 輸出結果:
- 段錯誤(核心已轉儲)
包含這個代碼被編譯程序時,字符串“hello”位於rodata部分程序的可執行文件的只讀部分數據段。當加載時,操作系統與其他字符串和地方常數只讀段的內存中的數據。當執行時,一個變量 ptr 設置爲指向字符串的位置,並試圖編寫一個H字符通過變量進入內存,導致段錯誤。編譯程序的編譯器不檢查作業的只讀的位置在編譯時,和運行類unix操作系統產生以下運行時發生 segmentation fault。
可以糾正這個代碼使用一個數組而不是一個字符指針,這個棧上分配內存並初始化字符串的值:
- #include <stdio.h>
- int main (void)
- {
- char ptr[] = "hello";
- ptr[0] = 'H';
- return 0;
- }
即使不能修改字符串(相反,這在C標準未定義行爲),在C char *類型,所以沒有隱式轉換原始代碼,在c++的 const char *類型,因此有一個隱式轉換,所以編譯器通常會抓住這個特定的錯誤。
4、空指針廢棄
因爲是一個很常見的程序錯誤空指針廢棄(讀或寫在一個空指針,用於C的意思是“沒有對象指針”作爲一個錯誤指示器),大多數操作系統內存訪問空指針的地址,這樣它會導致段錯誤。
- #include <stdio.h>
- int main (void)
- {
- int *ptr = NULL;
- printf ("%d\n", *ptr);
- return 0;
- }
- 輸出結果:
- 段錯誤(核心已轉儲)
非關聯化一個空指針,然後分配(寫一個值到一個不存在的目標)也通常會導致段錯誤。
- #include <stdio.h>
- int main (void)
- {
- int *ptr = NULL;
- *ptr = 1;
- return 0;
- }
- 輸出結果:
- 段錯誤(核心已轉儲)
- #include <stdio.h>
- int main (void)
- {
- int *ptr = NULL;
- *ptr;
- return 0;
- }
還有,比如malloc 動態分配內存,釋放、置空完成後,不可再使用該指針。
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- int main()
- {
- char* str=(char* )malloc(100);
- if(*str)
- {
- return;
- }
- strcpy(str,"hello");
- printf("%s\n",str);
- free(str);
- str=NULL;
- strcpy(str,"abcdef");
- return 0;
- }
- 輸出結果:
- hello
- 段錯誤 (核心已轉儲)
5、堆棧溢出
- #include <stdio.h>
- #include <string.h>
- int main (void)
- {
- main ();
- return 0;
- }
- 輸出結果:
- 段錯誤(核心已轉儲)
上述例子的無限遞歸,導致的堆棧溢出會導致段錯誤,但無線遞歸未必導致堆棧溢出,優化執行的編譯器和代碼的確切結構。在這種情況下,遙不可及的代碼(返回語句)行爲是未定義的。因此,編譯器可以消除它,使用尾部調用優化,可能導致沒有堆棧使用。其他優化可能包括將遞歸轉換成迭代,給出例子的結構功能永遠會導致程序運行,雖然可能不是其他堆棧溢出。
6、內存越界(數組越界,變量類型不一致等)
- #include <stdio.h>
- int main (void)
- {
- char test[10];
- printf ("%c\n", test[100000]);
- return 0;
- }
- 輸出結果:
- 段錯誤(核心已轉儲)
三、段錯誤信息的獲取
程序發生段錯誤時,提示信息很少,下面有幾種查看段錯誤的發生信息的途徑。
1、dmesg
dmesg 可以在應用程序崩潰時,顯示內存中保存的相關信息。如下所示,通過 dmesg 命令可以查看發生段錯誤的程序名稱、引起段錯誤發生的內存地址、指令指針地址、堆棧指針地址、錯誤代碼、錯誤原因等。
- root@#dmesg
- [ 6357.422282] a.out[3044]: segfault at 806851c ip b75cd668 sp bf8b2100 error 4 in libc-2.15.so[b7559000+19f000]
2、-g
使用gcc編譯程序的源碼時,加上 -g 參數,這樣可以使得生成的二進制文件中加入可以用於 gdb 調試的有用信息。
可產生供gdb調試用的可執行文件,大小明顯比只用-o選項編譯彙編連接後的文件大。
- root@ubuntu:/home/tarena/project/c_test# gcc hello.c
- root@ubuntu:/home/tarena/project/c_test# ls -la a.out
- -rwxr-xr-x 1 root root 7159 Nov 26 23:32 a.out
- root@ubuntu:/home/tarena/project/c_test# gcc -g hello.c
- root@ubuntu:/home/tarena/project/c_test# ls -la a.out
- -rwxr-xr-x 1 root root 8051 Nov 26 23:32 a.out
gdb的簡單使用:
(gdb)l 列表(list)
(gdb)r 執行(run)
(gdb)n 下一個(next)
(gdb)q 退出(quit)
(gdb)p 輸出(print)
(gdb)c 繼續(continue)
(gdb)b 4 設置斷點(break)
(gdb)d 刪除斷點(delete)
- root@ubuntu:/home/tarena/project/c_test# gdb a.out
- GNU gdb (Ubuntu/Linaro 7.4-2012.02-0ubuntu2) 7.4-2012.02
- Copyright (C) 2012 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" for details.
- This GDB was configured as "i686-linux-gnu".
- For bug reporting instructions, please see:
- <http://bugs.launchpad.net/gdb-linaro/>...
- Reading symbols from /home/tarena/project/c_test/a.out...done.
- (gdb) l
- 1 #include <stdio.h>
- 2 int main (void)
- 3 {
- 4 printf ("hello world!\n");
- 5 return 0;
- 6 }
- (gdb) r
- Starting program: /home/tarena/project/c_test/a.out
- hello world!
- [Inferior 1 (process 6906) exited normally]
- (gdb) q
- root@ubuntu:/home/tarena/project/c_test#
3、nm
使用 nm 命令列出二進制文件中符號表,包括符號地址、符號類型、符號名等。這樣可以幫助定位在哪裏發生了段錯誤。
- root@# nm a.out
- 08049f28 d _DYNAMIC
- 08049ff4 d _GLOBAL_OFFSET_TABLE_
- 080484ac R _IO_stdin_used
- w _Jv_RegisterClasses
- 08049f18 d __CTOR_END__
- 08049f14 d __CTOR_LIST__
- 08049f20 D __DTOR_END__
- 08049f1c d __DTOR_LIST__
- 080485a4 r __FRAME_END__
- 08049f24 d __JCR_END__
- 08049f24 d __JCR_LIST__
- 0804a010 A __bss_start
- 0804a008 D __data_start
- 08048460 t __do_global_ctors_aux
- 08048330 t __do_global_dtors_aux
- 0804a00c D __dso_handle
- w __gmon_start__
- 08048452 T __i686.get_pc_thunk.bx
- 08049f14 d __init_array_end
- 08049f14 d __init_array_start
- 08048450 T __libc_csu_fini
- 080483e0 T __libc_csu_init
- U __libc_start_main@@GLIBC_2.0
- 0804a010 A _edata
- 0804a018 A _end
- 0804848c T _fini
- 080484a8 R _fp_hw
- 08048294 T _init
- 08048300 T _start
- 0804a010 b completed.6159
- 0804a008 W data_start
- 0804a014 b dtor_idx.6161
- 08048390 t frame_dummy
- 080483b4 T main
4、ldd
使用 ldd 命令查看二進制程序的共享鏈接庫依賴,包括庫的名稱、起始地址,這樣可以確定段錯誤到底是發生在了自己的程序中還是依賴的共享庫中。
- root@t# ldd a.out
- linux-gate.so.1 => (0xb7765000)
- libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75ac000)
- /lib/ld-linux.so.2 (0xb7766000)
四、段錯誤的調試方法
接下來的講解是圍繞下面的代碼進行的:
- #include <stdio.h>
- int main (void)
- {
- int *ptr = NULL;
- *ptr = 10;
- return 0;
- }
- 輸出結果:
- 段錯誤(核心已轉儲)
1、使用 printf 輸出信息
這個是看似最簡單,但往往很多情況下十分有效的調試方式,也許可以說是程序員用的最多的調試方式。簡單來說,就是在程序的重要代碼附近加上像 printf 這類輸出信息,這樣可以跟蹤並打印出段錯誤在代碼中可能出現的位置。
爲了方便使用這種方法,可以使用條件編譯指令 #define DEBUG 和 #endif 把 printf 函數包起來。這樣在程序編譯時,如果加上 -DDEBUG 參數就可以查看調試信息;否則不加上參數就不會顯示調試信息。
2、使用 gcc 和 gdb
1)調試步驟
A、爲了能夠使用 gdb 調試程序,在編譯階段加上 -g 參數。
- root@# gcc -g test.c
- root@# gdb a.out
- GNU gdb (Ubuntu/Linaro 7.4-2012.02-0ubuntu2) 7.4-2012.02
- Copyright (C) 2012 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" for details.
- This GDB was configured as "i686-linux-gnu".
- For bug reporting instructions, please see:
- <http://bugs.launchpad.net/gdb-linaro/>...
- Reading symbols from /home/tarena/project/c_test/a.out...done.
- (gdb)
- (gdb) r
- Starting program: /home/tarena/project/c_test/a.out
- Program received signal SIGSEGV, Segmentation fault.
- 0x080483c4 in main () at test.c:6
- 6 *ptr = 10;
- (gdb)
從輸出看出,程序收到 SIGSEGV 信號,觸發段錯誤,並提示地址 0x080483c4、創建了一個空指針,然後試圖訪問它的值(讀值)。
可以通過man 7 signal查看SIGSEGV的信息
- Signal Value Action Comment
- ──────────────────────────────────────────────────────────────────────
- SIGHUP 1 Term Hangup detected on controlling terminal
- or death of controlling process
- SIGINT 2 Term Interrupt from keyboard
- SIGQUIT 3 Core Quit from keyboard
- SIGILL 4 Core Illegal Instruction
- SIGABRT 6 Core Abort signal from abort(3)
- SIGFPE 8 Core Floating point exception
- SIGKILL 9 Term Kill signal
- SIGSEGV 11 Core Invalid memory reference
- SIGPIPE 13 Term Broken pipe: write to pipe with no
- readers
- SIGALRM 14 Term Timer signal from alarm(2)
- SIGTERM 15 Term Termination signal
- SIGUSR1 30,10,16 Term User-defined signal 1
- SIGUSR2 31,12,17 Term User-defined signal 2
- SIGCHLD 20,17,18 Ign Child stopped or terminated
- SIGCONT 19,18,25 Cont Continue if stopped
- SIGSTOP 17,19,23 Stop Stop process
- SIGTSTP 18,20,24 Stop Stop typed at tty
- SIGTTIN 21,21,26 Stop tty input for background process
- SIGTTOU 22,22,27 Stop tty output for background process
- The signals SIGKILL and SIGSTOP cannot be caught, blocked, or ignored.
- (gdb) q
- A debugging session is active.
- Inferior 1 [process 3483] will be killed.
- Quit anyway? (y or n) y
2)適用場景
A、僅當能確定程序一定會發生段錯誤的情況下適用。
B、當程序的源碼可以獲得的情況下,使用 -g 參數編譯程序
C、一般用於測試階段,生產環境下 gdb 會有副作用:使程序運行減慢,運行不夠穩定,等等。
D、即使在測試階段,如果程序過於複雜,gdb 也不能處理。
3、使用 core 文件和 gdb
上面有提到段錯誤觸發SIGSEGV信號,通過man 7 signal,可以看到SIGSEGV默認的處理程序(handler)會打印段錯誤信息,併產生 core 文件,由此我們可以藉助於程序異常退出生成的 core 文件中的調試信息,使用 gdb 工具來調試程序中的段錯誤。
1)調試步驟
A、在一些Linux版本下,默認是不產生 core 文件的,首先可以查看一下系統 core 文件的大小限制:
- root@# ulimit -c
- 0
- root@# ulimit -c 1024
- root@# ulimit -c
- 1024
- root@# ./a.out
- 段錯誤 (核心已轉儲)
- root@ubuntu:/home/tarena/project/c_test# gdb a.out core
- GNU gdb (Ubuntu/Linaro 7.4-2012.02-0ubuntu2) 7.4-2012.02
- Copyright (C) 2012 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" for details.
- This GDB was configured as "i686-linux-gnu".
- For bug reporting instructions, please see:
- <http://bugs.launchpad.net/gdb-linaro/>...
- Reading symbols from /home/tarena/project/c_test/a.out...done.
- [New LWP 3491]
- warning: Can't read pathname for load map: 輸入/輸出錯誤.
- Core was generated by `./a.out'.
- Program terminated with signal 11, Segmentation fault.
- #0 0x080483c4 in main () at test.c:6
- 6 *ptr = 10;
- (gdb)
E、完成調試後,輸入 q 命令退出 gdb
- (gdb) q
2)適用場景
A、適合於在實際生成環境下調試程度的段錯誤(即在不用重新發生段錯誤的情況下重現段錯誤)
B、當程序很複雜,core 文件相當大時,該方法不可用
4、使用 objdump
1)調試步驟
A、使用 dmesg 命令,找到最近發生的段錯誤輸入信息
- root@# dmesg
- [ 372.350652] a.out[2712]: segfault at 0 ip 080483c4 sp bfd1f7b8 error 6 in a.out[8048000+1000]
其中,對我們接下來的調試過程有用的是發生段錯誤的地址 0 和指令指針地址 080483c4。
有時候,“地址引起的錯”可以告訴你問題的根源。看到上面的例子,我們可以說,int *ptr = NULL; *ptr = 10;,創建了一個空指針,然後試圖訪問它的值(讀值)。
B、使用 objdump 生成二進制的相關信息,重定向到文件中
- root@# objdump -d a.out > a.outDump
- root@# ls
- a.out a.outDump core test.c
C、在 a.outDump 文件中查找發生段錯誤的地址
- root@ubuntu:/home/tarena/project/c_test# grep -n -A 10 -B 10 "0" a.outDump
- 1-
- 2-a.out: file format elf32-i386
- 118:080483b4 <main>:
- 119: 80483b4: 55 push %ebp
- 120: 80483b5: 89 e5 mov %esp,%ebp
- 121: 80483b7: 83 ec 10 sub $0x10,%esp
- 122: 80483ba: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%ebp)
- 123: 80483c1: 8b 45 fc mov -0x4(%ebp),%eax
- 124: 80483c4: c7 00 0a 00 00 00 movl $0xa,(%eax)
- 125: 80483ca: b8 00 00 00 00 mov $0x0,%eax
- 126: 80483cf: c9 leave
- 127: 80483d0: c3 ret
- 128: 80483d1: 90 nop
- 129: 80483d2: 90 nop
- 130: 80483d3: 90 nop
- 131: 80483d4: 90 nop
- 132: 80483d5: 90 nop
- 133: 80483d6: 90 nop
- 134: 80483d7: 90 nop
- 135: 80483d8: 90 nop
- 136: 80483d9: 90 nop
- 137: 80483da: 90 nop
- 138: 80483db: 90 nop
- 139: 80483dc: 90 nop
- 140: 80483dd: 90 nop
- 141: 80483de: 90 nop
- 142: 80483df: 90 nop
2)適用場景
A、不需要 -g 參數編譯,不需要藉助於core文件,但需要有一定的彙編語言基礎。
B、如果使用 gcc 編譯優化參數(-O1,-O2,-O3)的話,生成的彙編指令將會被優化,使得調試過程有些難度。
5、使用catchsegv
catchsegv 命令專門用來補貨段錯誤,它通過動態加載器(ld-linux.so)的預加載機制(PRELOAD)把一個事先寫好的 庫(/lib/libSegFault.so)加載上,用於捕捉段錯誤的出錯信息。
- root@t# catchsegv ./a.out
- Segmentation fault (core dumped)
- *** Segmentation fault
- Register dump:
- EAX: 00000000 EBX: b77a1ff4 ECX: bfd8a0e4 EDX: bfd8a074
- ESI: 00000000 EDI: 00000000 EBP: bfd8a048 ESP: bfd8a038
- EIP: 080483c4 EFLAGS: 00010282
- CS: 0073 DS: 007b ES: 007b FS: 0000 GS: 0033 SS: 007b
- Trap: 0000000e Error: 00000006 OldMask: 00000000
- ESP/signal: bfd8a038 CR2: 00000000
- Backtrace:
- ??:0(main)[0x80483c4]
- /lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3)[0xb761a4d3]
- ??:0(_start)[0x8048321]
- Memory map:
- 08048000-08049000 r-xp 00000000 08:01 2102158 /home/tarena/project/c_test/a.out
- 08049000-0804a000 r--p 00000000 08:01 2102158 /home/tarena/project/c_test/a.out
- 0804a000-0804b000 rw-p 00001000 08:01 2102158 /home/tarena/project/c_test/a.out
- 09467000-0948c000 rw-p 00000000 00:00 0 [heap]
- b75e1000-b75fd000 r-xp 00000000 08:01 1704884 /lib/i386-linux-gnu/libgcc_s.so.1
- b75fd000-b75fe000 r--p 0001b000 08:01 1704884 /lib/i386-linux-gnu/libgcc_s.so.1
- b75fe000-b75ff000 rw-p 0001c000 08:01 1704884 /lib/i386-linux-gnu/libgcc_s.so.1
- b75ff000-b7601000 rw-p 00000000 00:00 0
- b7601000-b77a0000 r-xp 00000000 08:01 1704863 /lib/i386-linux-gnu/libc-2.15.so
- b77a0000-b77a2000 r--p 0019f000 08:01 1704863 /lib/i386-linux-gnu/libc-2.15.so
- b77a2000-b77a3000 rw-p 001a1000 08:01 1704863 /lib/i386-linux-gnu/libc-2.15.so
- b77a3000-b77a6000 rw-p 00000000 00:00 0
- b77b8000-b77bb000 r-xp 00000000 08:01 1704847 /lib/i386-linux-gnu/libSegFault.so
- b77bb000-b77bc000 r--p 00002000 08:01 1704847 /lib/i386-linux-gnu/libSegFault.so
- b77bc000-b77bd000 rw-p 00003000 08:01 1704847 /lib/i386-linux-gnu/libSegFault.so
- b77bd000-b77bf000 rw-p 00000000 00:00 0
- b77bf000-b77c0000 r-xp 00000000 00:00 0 [vdso]
- b77c0000-b77e0000 r-xp 00000000 08:01 1704843 /lib/i386-linux-gnu/ld-2.15.so
- b77e0000-b77e1000 r--p 0001f000 08:01 1704843 /lib/i386-linux-gnu/ld-2.15.so
- b77e1000-b77e2000 rw-p 00020000 08:01 1704843 /lib/i386-linux-gnu/ld-2.15.so
- bfd6b000-bfd8c000 rw-p 00000000 00:00 0 [stack]
五、內存泄漏檢測工具Valgrind
參看:Unix下C程序內存泄漏檢測工具Valgrind安裝與使用
Valgrind是一款用於內存調試、內存泄漏檢測以及性能分析的軟件開發工具。
官網:valgrind
(1)下載安裝
下載:https://sourceware.org/pub/valgrind/valgrind-3.13.0.tar.bz2
安裝:
- #tar xvf valgrind-3.13.0.tar.bz2
- #cd valgrind-3.13.0
- #./configure --prefix=/usr/local/webserver/valgrind
- #make
- #make install
- 添加環境變量:
- #vi /etc/profile
- PATH=${PATH}:/usr/local/webserver/valgrind/bin
- #source /etc/profile
- # valgrind ls -l
- ==16243== Memcheck, a memory error detector
- ==16243== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
- ==16243== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
- ==16243== Command: ls -l
- ==16243==
- valgrind: Fatal error at startup: a function redirection
- valgrind: which is mandatory for this platform-tool combination
- valgrind: cannot be set up. Details of the redirection are:
- valgrind:
- valgrind: A must-be-redirected function
- valgrind: whose name matches the pattern: strlen
- valgrind: in an object with soname matching: ld-linux.so.2
- valgrind: was not found whilst processing
- valgrind: symbols from the object with soname: ld-linux.so.2
- valgrind:
- valgrind: Possible fixes: (1, short term): install glibc's debuginfo
- valgrind: package on this machine. (2, longer term): ask the packagers
- valgrind: for your Linux distribution to please in future ship a non-
- valgrind: stripped ld.so (or whatever the dynamic linker .so is called)
- valgrind: that exports the above-named function using the standard
- valgrind: calling conventions for this platform. The package you need
- valgrind: to install for fix (1) is called
- valgrind:
- valgrind: On Debian, Ubuntu: libc6-dbg
- valgrind: On SuSE, openSuSE, Fedora, RHEL: glibc-debuginfo
- valgrind:
- valgrind: Note that if you are debugging a 32 bit process on a
- valgrind: 64 bit system, you will need a corresponding 32 bit debuginfo
- valgrind: package (e.g. libc6-dbg:i386).
- valgrind:
- valgrind: Cannot continue -- exiting now. Sorry.
運行:sudo apt-get install libc6-dbg 以安裝libc6-dbg
然後再次運行: valgrind ls -l
- $ valgrind ls -l
- ==14297== Memcheck, a memory error detector
- ==14297== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
- ==14297== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
- ==14297== Command: ls -l
- ==14297==
- 總用量 4
- -rw-rw-r-- 1 tarena tarena 105 1月 15 10:04 test.c
- ==14297==
- ==14297== HEAP SUMMARY:
- ==14297== in use at exit: 13,265 bytes in 29 blocks
- ==14297== total heap usage: 218 allocs, 189 frees, 73,974 bytes allocated
- ==14297==
- ==14297== LEAK SUMMARY:
- ==14297== definitely lost: 80 bytes in 2 blocks
- ==14297== indirectly lost: 240 bytes in 20 blocks
- ==14297== possibly lost: 0 bytes in 0 blocks
- ==14297== still reachable: 12,945 bytes in 7 blocks
- ==14297== suppressed: 0 bytes in 0 blocks
- ==14297== Rerun with --leak-check=full to see details of leaked memory
- ==14297==
- ==14297== For counts of detected and suppressed errors, rerun with: -v
- ==14297== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
(2)選項:
- <span style="color:#000000;">#valgrind -h
- usage: valgrind [options] prog-and-args
- tool-selection option, with default in [ ]:
- --tool=<name> use the Valgrind tool named <name> [memcheck]
- basic user options for all Valgrind tools, with defaults in [ ]:
- -h --help show this message
- --help-debug show this message, plus debugging options
- --version show version
- -q --quiet run silently; only print error msgs
- -v --verbose be more verbose -- show misc extra info
- --trace-children=no|yes Valgrind-ise child processes (follow execve)? [no]
- --trace-children-skip=patt1,patt2,... specifies a list of executables
- that --trace-children=yes should not trace into
- --trace-children-skip-by-arg=patt1,patt2,... same as --trace-children-skip=
- but check the argv[] entries for children, rather
- than the exe name, to make a follow/no-follow decision
- --child-silent-after-fork=no|yes omit child output between fork & exec? [no]
- --vgdb=no|yes|full activate gdbserver? [yes]
- full is slower but provides precise watchpoint/step
- --vgdb-error=<number> invoke gdbserver after <number> errors [999999999]
- to get started quickly, use --vgdb-error=0
- and follow the on-screen directions
- --vgdb-stop-at=event1,event2,... invoke gdbserver for given events [none]
- where event is one of:
- startup exit valgrindabexit all none
- --track-fds=no|yes track open file descriptors? [no]
- --time-stamp=no|yes add timestamps to log messages? [no]
- --log-fd=<number> log messages to file descriptor [2=stderr]
- --log-file=<file> log messages to <file>
- --log-socket=ipaddr:port log messages to socket ipaddr:port
- user options for Valgrind tools that report errors:
- --xml=yes emit error output in XML (some tools only)
- --xml-fd=<number> XML output to file descriptor
- --xml-file=<file> XML output to <file>
- --xml-socket=ipaddr:port XML output to socket ipaddr:port
- --xml-user-comment=STR copy STR verbatim into XML output
- --demangle=no|yes automatically demangle C++ names? [yes]
- --num-callers=<number> show <number> callers in stack traces [12]
- --error-limit=no|yes stop showing new errors if too many? [yes]
- --error-exitcode=<number> exit code to return if errors found [0=disable]
- --error-markers=<begin>,<end> add lines with begin/end markers before/after
- each error output in plain text mode [none]
- --show-below-main=no|yes continue stack traces below main() [no]
- --default-suppressions=yes|no
- load default suppressions [yes]
- --suppressions=<filename> suppress errors described in <filename>
- --gen-suppressions=no|yes|all print suppressions for errors? [no]
- --input-fd=<number> file descriptor for input [0=stdin]
- --dsymutil=no|yes run dsymutil on Mac OS X when helpful? [yes]
- --max-stackframe=<number> assume stack switch for SP changes larger
- than <number> bytes [2000000]
- --main-stacksize=<number> set size of main thread's stack (in bytes)
- [min(max(current 'ulimit' value,1MB),16MB)]
- user options for Valgrind tools that replace malloc:
- --alignment=<number> set minimum alignment of heap allocations [8]
- --redzone-size=<number> set minimum size of redzones added before/after
- heap blocks (in bytes). [16]
- --xtree-memory=none|allocs|full profile heap memory in an xtree [none]
- and produces a report at the end of the execution
- none: no profiling, allocs: current allocated
- size/blocks, full: profile current and cumulative
- allocated size/blocks and freed size/blocks.
- --xtree-memory-file=<file> xtree memory report file [xtmemory.kcg.%p]
- uncommon user options for all Valgrind tools:
- --fullpath-after= (with nothing after the '=')
- show full source paths in call stacks
- --fullpath-after=string like --fullpath-after=, but only show the
- part of the path after 'string'. Allows removal
- of path prefixes. Use this flag multiple times
- to specify a set of prefixes to remove.
- --extra-debuginfo-path=path absolute path to search for additional
- debug symbols, in addition to existing default
- well known search paths.
- --debuginfo-server=ipaddr:port also query this server
- (valgrind-di-server) for debug symbols
- --allow-mismatched-debuginfo=no|yes [no]
- for the above two flags only, accept debuginfo
- objects that don't "match" the main object
- --smc-check=none|stack|all|all-non-file [all-non-file]
- checks for self-modifying code: none, only for
- code found in stacks, for all code, or for all
- code except that from file-backed mappings
- --read-inline-info=yes|no read debug info about inlined function calls
- and use it to do better stack traces. [yes]
- on Linux/Android/Solaris for Memcheck/Helgrind/DRD
- only. [no] for all other tools and platforms.
- --read-var-info=yes|no read debug info on stack and global variables
- and use it to print better error messages in
- tools that make use of it (Memcheck, Helgrind,
- DRD) [no]
- --vgdb-poll=<number> gdbserver poll max every <number> basic blocks [5000]
- --vgdb-shadow-registers=no|yes let gdb see the shadow registers [no]
- --vgdb-prefix=<prefix> prefix for vgdb FIFOs [/tmp/vgdb-pipe]
- --run-libc-freeres=no|yes free up glibc memory at exit on Linux? [yes]
- --run-cxx-freeres=no|yes free up libstdc++ memory at exit on Linux
- and Solaris? [yes]
- --sim-hints=hint1,hint2,... activate unusual sim behaviours [none]
- where hint is one of:
- lax-ioctls lax-doors fuse-compatible enable-outer
- no-inner-prefix no-nptl-pthread-stackcache fallback-llsc none
- --fair-sched=no|yes|try schedule threads fairly on multicore systems [no]
- --kernel-variant=variant1,variant2,...
- handle non-standard kernel variants [none]
- where variant is one of:
- bproc android-no-hw-tls
- android-gpu-sgx5xx android-gpu-adreno3xx none
- --merge-recursive-frames=<number> merge frames between identical
- program counters in max <number> frames) [0]
- --num-transtab-sectors=<number> size of translated code cache [32]
- more sectors may increase performance, but use more memory.
- --avg-transtab-entry-size=<number> avg size in bytes of a translated
- basic block [0, meaning use tool provided default]
- --aspace-minaddr=0xPP avoid mapping memory below 0xPP [guessed]
- --valgrind-stacksize=<number> size of valgrind (host) thread's stack
- (in bytes) [1048576]
- --show-emwarns=no|yes show warnings about emulation limits? [no]
- --require-text-symbol=:sonamepattern:symbolpattern abort run if the
- stated shared object doesn't have the stated
- text symbol. Patterns can contain ? and *.
- --soname-synonyms=syn1=pattern1,syn2=pattern2,... synonym soname
- specify patterns for function wrapping or replacement.
- To use a non-libc malloc library that is
- in the main exe: --soname-synonyms=somalloc=NONE
- in libxyzzy.so: --soname-synonyms=somalloc=libxyzzy.so
- --sigill-diagnostics=yes|no warn about illegal instructions? [yes]
- --unw-stack-scan-thresh=<number> Enable stack-scan unwind if fewer
- than <number> good frames found [0, meaning "disabled"]
- NOTE: stack scanning is only available on arm-linux.
- --unw-stack-scan-frames=<number> Max number of frames that can be
- recovered by stack scanning [5]
- --resync-filter=no|yes|verbose [yes on MacOS, no on other OSes]
- attempt to avoid expensive address-space-resync operations
- --max-threads=<number> maximum number of threads that valgrind can
- handle [500]
- user options for Memcheck:
- --leak-check=no|summary|full search for memory leaks at exit? [summary]
- --leak-resolution=low|med|high differentiation of leak stack traces [high]
- --show-leak-kinds=kind1,kind2,.. which leak kinds to show?
- [definite,possible]
- --errors-for-leak-kinds=kind1,kind2,.. which leak kinds are errors?
- [definite,possible]
- where kind is one of:
- definite indirect possible reachable all none
- --leak-check-heuristics=heur1,heur2,... which heuristics to use for
- improving leak search false positive [all]
- where heur is one of:
- stdstring length64 newarray multipleinheritance all none
- --show-reachable=yes same as --show-leak-kinds=all
- --show-reachable=no --show-possibly-lost=yes
- same as --show-leak-kinds=definite,possible
- --show-reachable=no --show-possibly-lost=no
- same as --show-leak-kinds=definite
- --xtree-leak=no|yes output leak result in xtree format? [no]
- --xtree-leak-file=<file> xtree leak report file [xtleak.kcg.%p]
- --undef-value-errors=no|yes check for undefined value errors [yes]
- --track-origins=no|yes show origins of undefined values? [no]
- --partial-loads-ok=no|yes too hard to explain here; see manual [yes]
- --expensive-definedness-checks=no|yes
- Use extra-precise definedness tracking [no]
- --freelist-vol=<number> volume of freed blocks queue [20000000]
- --freelist-big-blocks=<number> releases first blocks with size>= [1000000]
- --workaround-gcc296-bugs=no|yes self explanatory [no]. Deprecated.
- Use --ignore-range-below-sp instead.
- --ignore-ranges=0xPP-0xQQ[,0xRR-0xSS] assume given addresses are OK
- --ignore-range-below-sp=<number>-<number> do not report errors for
- accesses at the given offsets below SP
- --malloc-fill=<hexnumber> fill malloc'd areas with given value
- --free-fill=<hexnumber> fill free'd areas with given value
- --keep-stacktraces=alloc|free|alloc-and-free|alloc-then-free|none
- stack trace(s) to keep for malloc'd/free'd areas [alloc-and-free]
- --show-mismatched-frees=no|yes show frees that don't match the allocator? [yes]
- Extra options read from ~/.valgrindrc, $VALGRIND_OPTS, ./.valgrindrc
- Memcheck is Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
- Valgrind is Copyright (C) 2000-2017, and GNU GPL'd, by Julian Seward et al.
- LibVEX is Copyright (C) 2004-2017, and GNU GPL'd, by OpenWorks LLP et al.
- Bug reports, feedback, admiration, abuse, etc, to: www.valgrind.org.</span><span style="color:#993399;">
- </span>
(3)使用
1、使用未初始化內存問題
- #include<stdio.h>
- int main(void)
- {
- int a[5];
- int i,s;
- a[0]=a[1]=a[2]=a[3]=a[4]=0;
- s=0;
- for (i =0;i<5;i++)
- s+=a[i];
- if(s==377)
- printf("sum is %d\n",s);
- return 0;
- }
- 執行:
- # gcc -Wall test.c -g -o test
- # valgrind --tool=memcheck --leak-check=yes ./test
- $ valgrind --tool=memcheck --leak-check=yes ./test
- ==14496== Memcheck, a memory error detector
- ==14496== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
- ==14496== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
- ==14496== Command: ./test
- ==14496==
- ==14496==
- ==14496== HEAP SUMMARY:
- ==14496== in use at exit: 0 bytes in 0 blocks
- ==14496== total heap usage: 0 allocs, 0 frees, 0 bytes allocated
- ==14496==
- ==14496== All heap blocks were freed -- no leaks are possible
- ==14496==
- ==14496== For counts of detected and suppressed errors, rerun with: -v
- ==14496== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
2、內存讀寫越界
- #include<stdlib.h>
- #include<stdio.h>
- int main(int argc,char *argv[])
- {
- int len =4;
- int* pt =(int*)malloc(len*sizeof(int));
- int* p=pt;
- int i;
- for(i=0;i<len;i++)
- {
- p++;
- }
- *p=5;
- printf("the value of p equal:%d\n",*p);
- return 0;
- }
- 執行:
- # gcc -Wall test.c -g -o test
- # valgrind --tool=memcheck --leak-check=yes ./test
- $ valgrind --tool=memcheck --leak-check=yes ./test
- ==14514== Memcheck, a memory error detector
- ==14514== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
- ==14514== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
- ==14514== Command: ./test
- ==14514==
- ==14514== Invalid write of size 4
- ==14514== at 0x8048462: main (test.c:14)
- ==14514== Address 0x41f4038 is 0 bytes after a block of size 16 alloc'd
- ==14514== at 0x402BD74: malloc (vg_replace_malloc.c:270)
- ==14514== by 0x8048433: main (test.c:7)
- ==14514==
- ==14514== Invalid read of size 4
- ==14514== at 0x804846C: main (test.c:15)
- ==14514== Address 0x41f4038 is 0 bytes after a block of size 16 alloc'd
- ==14514== at 0x402BD74: malloc (vg_replace_malloc.c:270)
- ==14514== by 0x8048433: main (test.c:7)
- ==14514==
- the value of p equal:5
- ==14514==
- ==14514== HEAP SUMMARY:
- ==14514== in use at exit: 16 bytes in 1 blocks
- ==14514== total heap usage: 1 allocs, 0 frees, 16 bytes allocated
- ==14514==
- ==14514== 16 bytes in 1 blocks are definitely lost in loss record 1 of 1
- ==14514== at 0x402BD74: malloc (vg_replace_malloc.c:270)
- ==14514== by 0x8048433: main (test.c:7)
- ==14514==
- ==14514== LEAK SUMMARY:
- ==14514== definitely lost: 16 bytes in 1 blocks
- ==14514== indirectly lost: 0 bytes in 0 blocks
- ==14514== possibly lost: 0 bytes in 0 blocks
- ==14514== still reachable: 0 bytes in 0 blocks
- ==14514== suppressed: 0 bytes in 0 blocks
- ==14514==
- ==14514== For counts of detected and suppressed errors, rerun with: -v
- ==14514== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)
3、內存覆蓋
- #include<stdio.h>
- #include<stdlib.h>
- #include<string.h>
- int main(int argc, char* argv[])
- {
- char x[50];
- int i;
- for(i=0;i<50;i++)
- {
- x[i]=i+1;
- }
- strncpy(x+20,x,20);
- strncpy(x+20,x,21);
- strncpy(x,x+20,20);
- strncpy(x,x+20,21);
- x[39]='\0';
- strcpy(x,x+20);
- x[39]=39;
- x[50]='\0';
- strcpy(x,x+20);
- return 0;
- }
- 執行:
- # gcc -Wall test.c -g -o test
- # valgrind --tool=memcheck --leak-check=yes ./test
- $ valgrind --tool=memcheck --leak-check=yes ./test
- ==14551== Memcheck, a memory error detector
- ==14551== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
- ==14551== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
- ==14551== Command: ./test
- ==14551==
- ==14551== Source and destination overlap in strncpy(0xbec6a0c3, 0xbec6a0af, 21)
- ==14551== at 0x402C860: strncpy (mc_replace_strmem.c:472)
- ==14551== by 0x80484E6: main (test.c:14)
- ==14551==
- ==14551== Source and destination overlap in strncpy(0xbec6a0af, 0xbec6a0c3, 21)
- ==14551== at 0x402C860: strncpy (mc_replace_strmem.c:472)
- ==14551== by 0x8048524: main (test.c:16)
- ==14551==
- ==14551== Source and destination overlap in strcpy(0xbec6a09a, 0xbec6a0ae)
- ==14551== at 0x402C6AB: strcpy (mc_replace_strmem.c:438)
- ==14551== by 0x8048561: main (test.c:23)
- ==14551==
- ==14551==
- ==14551== HEAP SUMMARY:
- ==14551== in use at exit: 0 bytes in 0 blocks
- ==14551== total heap usage: 0 allocs, 0 frees, 0 bytes allocated
- ==14551==
- ==14551== All heap blocks were freed -- no leaks are possible
- ==14551==
- ==14551== For counts of detected and suppressed errors, rerun with: -v
- ==14551== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)
4、動態內存管理錯誤
- #include<stdlib.h>
- #include<stdio.h>
- int main(int argc,char *argv[])
- {
- int i;
- char* p=(char*)malloc(10);
- char* pt=p;
- for(i =0;i<10;i++)
- {
- p[i]='z';
- }
- delete p;
- pt[1]='x';
- free(pt);
- return 0;
- }
- 執行:
- # g++ -Wall test.cpp -g -o test
- # valgrind --tool=memcheck --leak-check=yes ./test
- $ valgrind --tool=memcheck --leak-check=yes ./test
- ==14607== Memcheck, a memory error detector
- ==14607== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
- ==14607== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
- ==14607== Command: ./test
- ==14607==
- ==14607== Mismatched free() / delete / delete []
- ==14607== at 0x402A92A: operator delete(void*) (vg_replace_malloc.c:480)
- ==14607== by 0x80484F6: main (test.cpp:13)
- ==14607== Address 0x4324028 is 0 bytes inside a block of size 10 alloc'd
- ==14607== at 0x402BD74: malloc (vg_replace_malloc.c:270)
- ==14607== by 0x80484B8: main (test.cpp:7)
- ==14607==
- ==14607== Invalid write of size 1
- ==14607== at 0x80484FE: main (test.cpp:15)
- ==14607== Address 0x4324029 is 1 bytes inside a block of size 10 free'd
- ==14607== at 0x402A92A: operator delete(void*) (vg_replace_malloc.c:480)
- ==14607== by 0x80484F6: main (test.cpp:13)
- ==14607==
- ==14607== Invalid free() / delete / delete[] / realloc()
- ==14607== at 0x402AD3D: free (vg_replace_malloc.c:446)
- ==14607== by 0x804850C: main (test.cpp:16)
- ==14607== Address 0x4324028 is 0 bytes inside a block of size 10 free'd
- ==14607== at 0x402A92A: operator delete(void*) (vg_replace_malloc.c:480)
- ==14607== by 0x80484F6: main (test.cpp:13)
- ==14607==
- ==14607==
- ==14607== HEAP SUMMARY:
- ==14607== in use at exit: 0 bytes in 0 blocks
- ==14607== total heap usage: 1 allocs, 2 frees, 10 bytes allocated
- ==14607==
- ==14607== All heap blocks were freed -- no leaks are possible
- ==14607==
- ==14607== For counts of detected and suppressed errors, rerun with: -v
- ==14607== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)
5、內存泄漏
- //tree.h
- #ifndef _BADLEAK_
- #define _BAD_LEAK_
- typedef struct _node{
- struct _node *l;
- struct _node * r;
- char v;
- }node;
- node *mk( node *l, node *r, char val);
- void nodefr(node *n);
- #endif
- //tree.cpp
- #include <stdio.h>
- #include "tree.h"
- #include <stdlib.h>
- node *mk(node *l, node * r, char val)
- {
- node *f=(node* )malloc(sizeof(node));
- f->l=l;
- f->r=r;
- f->v=val;
- return f;
- }
- void nodefr(node* n)
- {
- if(n)
- {
- nodefr(n->l);
- nodefr(n->r);
- free(n);
- }
- }
- //badleak.cpp
- #include <stdio.h>
- #include <stdlib.h>
- #include "tree.h"
- int main()
- {
- node* tree1, *tree2, * tree3;
- tree1=mk(mk(mk(0,0,'3'),0,'2'),0,'1');
- tree2=mk(0,mk(0,mk(0,0,'6'),'5'),'4');
- tree3=mk(mk(tree1,tree2,'8'),0,'7');
- //nodefr(tree3);
- return 0;
- }
- 執行:
- # g++ -Wall tree.cpp badleak.cpp -g -o badleak
- #valgrind --tool=memcheck --leak-check=yes ./badleak
- $ valgrind --tool=memcheck --leak-check=yes ./badleak
- ==14676== Memcheck, a memory error detector
- ==14676== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
- ==14676== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
- ==14676== Command: ./badleak
- ==14676==
- ==14676==
- ==14676== HEAP SUMMARY:
- ==14676== in use at exit: 96 bytes in 8 blocks
- ==14676== total heap usage: 8 allocs, 0 frees, 96 bytes allocated
- ==14676==
- ==14676== 96 (12 direct, 84 indirect) bytes in 1 blocks are definitely lost in loss record 8 of 8
- ==14676== at 0x402BD74: malloc (vg_replace_malloc.c:270)
- ==14676== by 0x804842B: mk(_node*, _node*, char) (tree.cpp:7)
- ==14676== by 0x8048560: main (badleak.cpp:10)
- ==14676==
- ==14676== LEAK SUMMARY:
- ==14676== definitely lost: 12 bytes in 1 blocks
- ==14676== indirectly lost: 84 bytes in 7 blocks
- ==14676== possibly lost: 0 bytes in 0 blocks
- ==14676== still reachable: 0 bytes in 0 blocks
- ==14676== suppressed: 0 bytes in 0 blocks
- ==14676==
- ==14676== For counts of detected and suppressed errors, rerun with: -v
- ==14676== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
六、一些注意事項
1)出現段錯誤時,首先應該想到段錯誤的定義,從它出發考慮引發錯誤的原因。
2)在使用指針時,定義了指針後記得初始化指針,在使用的時候記得判斷是否爲 NULL
3)在使用數組時,注意數組是否被初始化,數組下標是否越界,數組元素是否存在等
4)在訪問變量,注意變量所佔地址空間是否已經被程序釋放掉
5)在處理變量時,注意變量的格式控制是否合理等