2010-05-25 346 views
55

當我運行我的C++程序時,它會因此錯誤而崩潰。如何使用gdb追蹤C++中的double free或corruption錯誤

* glibc的檢測* ./load:雙重釋放或腐敗(上一個!): 0x0000000000c6ed50 ***

我試圖跟蹤它使用的cout語句,但我發現很難。 gdb可以讓這更容易嗎?它是如何完成的?

+3

我不知道爲什麼每個人都建議'NULL'指針(它掩蓋了被捕獲的錯誤,正如這個問題很好地顯示的那樣),但是沒有人建議完全不做手動內存管理,這在C++中是非常好的。多年以來我沒有寫過'刪除'。 (而且,是的,我的代碼對性能至關重要,否則它不會用C++編寫)。 – sbi 2010-05-25 08:32:15

+2

@sbi:堆腐敗等很少被捕獲,至少不是它們發生的地方。 NULL指針可能會讓你的程序更早崩潰。 – Hasturkun 2010-05-25 08:45:14

+0

@Hasturkun:我非常不同意。 「NULL」指針的一個主要動機是防止第二個'delete ptr;'被炸燬 - 這掩蓋了一個錯誤,因爲第二個'delete'不應該發生。 (它也用來檢查一個指針是否仍然指向一個有效的對象,但這只是提出了一個問題,爲什麼你有一個沒有指向對象的指針) – sbi 2010-05-25 08:55:56

回答

15

您可以使用gdb,但我會先嚐試Valgrind。請參閱quick start guide

+1

雖然這個鏈接可能回答這個問題,最好在這裏包括答案的重要部分,並提供參考鏈接。如果鏈接頁面更改,則僅鏈接答案可能會失效。 – SMR 2015-03-05 07:32:15

+0

@SMR,在這種情況下,答案的重要部分是整個大型鏈接頁面。所以只包括答案中的鏈接是完全正確的。關於爲什麼作者偏好Valgrind而不是gdb以及他如何解決_specific_問題的幾句話是恕我直言,答案中真正缺少的是什麼。 – ndemou 2016-05-17 07:16:50

22

至少有兩種可能的情況:

  1. 要刪除的同一個實體兩次
  2. 要刪除,這不是分配

對於第一個我強烈建議的東西將所有刪除的指針置空。

你有三個選擇:

  1. 重載new和delete和跟蹤分配
  2. 是,使用gdb的 - 那麼你會從你的崩潰得到一個回溯,這很可能會很樂於助人
  3. 的建議 - 使用Valgrind的 - 這是不容易進入,而且可以節省您的時間千倍在未來...
+1

gdb是如何完成的? – neuromancer 2010-05-25 05:28:45

+0

2.會導致腐敗,但我認爲這個消息通常不會出現,因爲理智檢查只在堆上完成。但是,我認爲3.堆緩衝區溢出是可能的。 – 2010-05-25 05:31:47

+0

好的。真的,我錯過了讓指針爲NULL並面對這個錯誤。得到教訓! – hrushi 2017-02-27 03:05:11

11

三條基本規律:

  1. 免費後將指針設置爲NULL
  2. 釋放前檢查NULL。
  3. 在開始時初始化指向NULL的指針。

這三個作品的組合很好。

+0

我不是C專家,但我通常可以保持頭腦清醒。爲什麼#1?當你試圖訪問一個free'd指針,而不僅僅是一個無聲的錯誤時,你的程序是否會崩潰? – 2010-05-25 06:48:42

+0

@Precision:是的,就是這一點。這是一個很好的做法:有一個指向刪除內存的指針是一個風險。 – ereOn 2010-05-25 08:15:32

+0

@ereOn:謝謝。我剛剛在C上做了幾個類,並且從來不必與其他人編寫代碼,所以從來沒有真正遇到過這個問題。看起來像一個非常好的做法,但。 – 2010-05-25 08:31:08

41

如果您使用的glibc,你可以設置MALLOC_CHECK_環境變量2,這將導致的glibc使用的malloc的容錯版本,這將導致你的程序在該點中止在雙重釋放完成。

您可以在運行程序之前使用set environment MALLOC_CHECK_ 2命令從gdb中設置此項;程序應該中止,在回溯中可以看到free()的調用。

看到man page for malloc()以獲取更多信息

4

是否使用智能指針如boost shared_ptr?如果是這樣,請通過調用get()來檢查是否在任何地方直接使用原始指針。我發現這是一個相當普遍的問題。

例如,假設一個原始指針傳遞給代碼的場景(可能是回調處理程序)。您可能會決定將其分配給智能指針以應對引用計數等。大錯誤:除非您進行深度複製,否則您的代碼不擁有此指針。當你的代碼與智能指針完成後,它會銷燬它並試圖銷燬它指向的內存,因爲它認爲沒有其他人需要它,然後調用代碼將嘗試刪除它,會有一個雙重的免費問題。

當然,這可能不是你的問題。這是最簡單的例子,它顯示了它會如何發生。第一次刪除是好的,但編譯器意識到它已經刪除了內存並導致了問題。這就是爲什麼在刪除後立即將0指向一個指針是個好主意。

int main(int argc, char* argv[]) 
{ 
    char* ptr = new char[20]; 

    delete[] ptr; 
    ptr = 0; // Comment me out and watch me crash and burn. 
    delete[] ptr; 
} 

編輯:改變deletedelete[],如PTR是字符數組。

+0

我的程序中沒有使用任何刪除命令。這仍然是問題嗎? – neuromancer 2010-05-26 01:57:19

+1

@Phenom:你爲什麼不使用刪除?是因爲你使用智能指針嗎?想必你在你的代碼中使用new來在堆上創建對象?如果這兩個答案都是肯定的,那麼你在智能指針上使用get/set來複制原始指針嗎?如果是這樣,不要!你會打破引用計數。或者,您可以將您正在調用的庫代碼的指針分配給智能指針。如果你不'擁有'指向的內存,那麼不要這樣做,因爲庫和智能指針都會嘗試刪除它。 – 2010-05-27 11:23:29

11

您可以使用valgrind進行調試。

#include<stdio.h> 
#include<stdlib.h> 

int main() 
{ 
char *x = malloc(100); 
free(x); 
free(x); 
return 0; 
} 

[[email protected] testbox]$ vim t1.c 
[[email protected] testbox]$ cc -g t1.c -o t1 
[[email protected] testbox]$ ./t1 
*** glibc detected *** ./t1: double free or corruption (top): 0x00000000058f7010 *** 
======= Backtrace: ========= 
/lib64/libc.so.6[0x3a3127245f] 
/lib64/libc.so.6(cfree+0x4b)[0x3a312728bb] 
./t1[0x400500] 
/lib64/libc.so.6(__libc_start_main+0xf4)[0x3a3121d994] 
./t1[0x400429] 
======= Memory map: ======== 
00400000-00401000 r-xp 00000000 68:02 30246184       /home/sand/testbox/t1 
00600000-00601000 rw-p 00000000 68:02 30246184       /home/sand/testbox/t1 
058f7000-05918000 rw-p 058f7000 00:00 0         [heap] 
3a30e00000-3a30e1c000 r-xp 00000000 68:03 5308733      /lib64/ld-2.5.so 
3a3101b000-3a3101c000 r--p 0001b000 68:03 5308733      /lib64/ld-2.5.so 
3a3101c000-3a3101d000 rw-p 0001c000 68:03 5308733      /lib64/ld-2.5.so 
3a31200000-3a3134e000 r-xp 00000000 68:03 5310248      /lib64/libc-2.5.so 
3a3134e000-3a3154e000 ---p 0014e000 68:03 5310248      /lib64/libc-2.5.so 
3a3154e000-3a31552000 r--p 0014e000 68:03 5310248      /lib64/libc-2.5.so 
3a31552000-3a31553000 rw-p 00152000 68:03 5310248      /lib64/libc-2.5.so 
3a31553000-3a31558000 rw-p 3a31553000 00:00 0 
3a41c00000-3a41c0d000 r-xp 00000000 68:03 5310264      /lib64/libgcc_s-4.1.2-20080825.so.1 
3a41c0d000-3a41e0d000 ---p 0000d000 68:03 5310264      /lib64/libgcc_s-4.1.2-20080825.so.1 
3a41e0d000-3a41e0e000 rw-p 0000d000 68:03 5310264      /lib64/libgcc_s-4.1.2-20080825.so.1 
2b1912300000-2b1912302000 rw-p 2b1912300000 00:00 0 
2b191231c000-2b191231d000 rw-p 2b191231c000 00:00 0 
7ffffe214000-7ffffe229000 rw-p 7ffffffe9000 00:00 0      [stack] 
7ffffe2b0000-7ffffe2b4000 r-xp 7ffffe2b0000 00:00 0      [vdso] 
ffffffffff600000-ffffffffffe00000 ---p 00000000 00:00 0     [vsyscall] 
Aborted 
[[email protected] testbox]$ 


[[email protected] testbox]$ vim t1.c 
[[email protected] testbox]$ cc -g t1.c -o t1 
[[email protected] testbox]$ valgrind --tool=memcheck ./t1 
==20859== Memcheck, a memory error detector 
==20859== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al. 
==20859== Using Valgrind-3.5.0 and LibVEX; rerun with -h for copyright info 
==20859== Command: ./t1 
==20859== 
==20859== Invalid free()/delete/delete[] 
==20859== at 0x4A05A31: free (vg_replace_malloc.c:325) 
==20859== by 0x4004FF: main (t1.c:8) 
==20859== Address 0x4c26040 is 0 bytes inside a block of size 100 free'd 
==20859== at 0x4A05A31: free (vg_replace_malloc.c:325) 
==20859== by 0x4004F6: main (t1.c:7) 
==20859== 
==20859== 
==20859== HEAP SUMMARY: 
==20859==  in use at exit: 0 bytes in 0 blocks 
==20859== total heap usage: 1 allocs, 2 frees, 100 bytes allocated 
==20859== 
==20859== All heap blocks were freed -- no leaks are possible 
==20859== 
==20859== For counts of detected and suppressed errors, rerun with: -v 
==20859== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4) 
[[email protected] testbox]$ 


[[email protected] testbox]$ valgrind --tool=memcheck --leak-check=full ./t1 
==20899== Memcheck, a memory error detector 
==20899== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al. 
==20899== Using Valgrind-3.5.0 and LibVEX; rerun with -h for copyright info 
==20899== Command: ./t1 
==20899== 
==20899== Invalid free()/delete/delete[] 
==20899== at 0x4A05A31: free (vg_replace_malloc.c:325) 
==20899== by 0x4004FF: main (t1.c:8) 
==20899== Address 0x4c26040 is 0 bytes inside a block of size 100 free'd 
==20899== at 0x4A05A31: free (vg_replace_malloc.c:325) 
==20899== by 0x4004F6: main (t1.c:7) 
==20899== 
==20899== 
==20899== HEAP SUMMARY: 
==20899==  in use at exit: 0 bytes in 0 blocks 
==20899== total heap usage: 1 allocs, 2 frees, 100 bytes allocated 
==20899== 
==20899== All heap blocks were freed -- no leaks are possible 
==20899== 
==20899== For counts of detected and suppressed errors, rerun with: -v 
==20899== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4) 
[[email protected] testbox]$ 

一個可能的解決辦法:

#include<stdio.h> 
#include<stdlib.h> 

int main() 
{ 
char *x = malloc(100); 
free(x); 
x=NULL; 
free(x); 
return 0; 
} 

[[email protected] testbox]$ vim t1.c 
[[email protected] testbox]$ cc -g t1.c -o t1 
[[email protected] testbox]$ ./t1 
[[email protected] testbox]$ 

[[email protected] testbox]$ valgrind --tool=memcheck --leak-check=full ./t1 
==20958== Memcheck, a memory error detector 
==20958== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al. 
==20958== Using Valgrind-3.5.0 and LibVEX; rerun with -h for copyright info 
==20958== Command: ./t1 
==20958== 
==20958== 
==20958== HEAP SUMMARY: 
==20958==  in use at exit: 0 bytes in 0 blocks 
==20958== total heap usage: 1 allocs, 1 frees, 100 bytes allocated 
==20958== 
==20958== All heap blocks were freed -- no leaks are possible 
==20958== 
==20958== For counts of detected and suppressed errors, rerun with: -v 
==20958== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 4 from 4) 
[[email protected] testbox]$ 

檢查出使用Valgrind的Link

0

我知道這是一個非常古老的線程,但它是這個錯誤的頂級的谷歌搜索的博客,沒有一個答覆提到了這個錯誤的常見原因。

哪個關閉已關閉的文件。

如果你不注意並且有兩個不同的函數關閉同一個文件,那麼第二個會產生這個錯誤。

2

使用現代C++編譯器,您可以使用消毒劑進行跟蹤。

樣品例如:

我的程序:

$cat d_free.cxx 
#include<iostream> 

using namespace std; 

int main() 

{ 
    int * i = new int(); 
    delete i; 
    //i = NULL; 
    delete i; 
} 

編譯地址消毒劑:

# g++-7.1 d_free.cxx -Wall -Werror -fsanitize=address -g 

執行:

# ./a.out 
================================================================= 
==4836==ERROR: AddressSanitizer: attempting double-free on 0x602000000010 in thread T0: 
    #0 0x7f35b2d7b3c8 in operator delete(void*, unsigned long) /media/sf_shared/gcc-7.1.0/libsanitizer/asan/asan_new_delete.cc:140 
    #1 0x400b2c in main /media/sf_shared/jkr/cpp/d_free/d_free.cxx:11 
    #2 0x7f35b2050c04 in __libc_start_main (/lib64/libc.so.6+0x21c04) 
    #3 0x400a08 (/media/sf_shared/jkr/cpp/d_free/a.out+0x400a08) 

0x602000000010 is located 0 bytes inside of 4-byte region [0x602000000010,0x602000000014) 
freed by thread T0 here: 
    #0 0x7f35b2d7b3c8 in operator delete(void*, unsigned long) /media/sf_shared/gcc-7.1.0/libsanitizer/asan/asan_new_delete.cc:140 
    #1 0x400b1b in main /media/sf_shared/jkr/cpp/d_free/d_free.cxx:9 
    #2 0x7f35b2050c04 in __libc_start_main (/lib64/libc.so.6+0x21c04) 

previously allocated by thread T0 here: 
    #0 0x7f35b2d7a040 in operator new(unsigned long) /media/sf_shared/gcc-7.1.0/libsanitizer/asan/asan_new_delete.cc:80 
    #1 0x400ac9 in main /media/sf_shared/jkr/cpp/d_free/d_free.cxx:8 
    #2 0x7f35b2050c04 in __libc_start_main (/lib64/libc.so.6+0x21c04) 

SUMMARY: AddressSanitizer: double-free /media/sf_shared/gcc-7.1.0/libsanitizer/asan/asan_new_delete.cc:140 in operator delete(void*, unsigned long) 
==4836==ABORTING 

要了解更多有關你可以檢查消毒劑thisthis或任何現代的C++編譯器(例如, gcc,clang等)文件。

0

任何解決方案me.So沒有工作,我開始亂搞找到一種方法,這爲我工作的: 錯誤是顯示每次我:

MyClass b(5); 
MyClass a = b; 

我改變了代碼:

MyClass b(5); 
MyClass a; 
a = b; 

它只是爲我工作,希望它也適用於你。