2017-04-05 58 views
3

如何調試一個C程序,該程序在與gdb中的gdbrun連接時不會崩潰?C malloc「不能分配區域」錯誤,但不能用GDB重新生成?

獨立運行時會一直崩潰 - 即使是相同的調試版本!


我們幾個都收到此錯誤與BSD/Linux編寫的C程序,我們正在編制上與OpenSSL的Mac系統。

app(37457,0x7000017c7000) malloc: *** mach_vm_map(size=13835058055282167808) failed (error code=3) 
*** error: can't allocate region 
*** set a breakpoint in malloc_error_break to debug 
ERROR: malloc(buf->length + 1) failed! 

我知道,沒有幫助。

重新編譯應用程序-g -rdynamic給出了相同的錯誤。好吧,現在我們知道這不是因爲發佈版本,因爲它繼續失敗。

雖然在gdb調試會話中運行,但它工作!

$ sudo gdb app 
(gdb) b malloc_error_break 
Function "malloc_error_break" not defined. 
Make breakpoint pending on future shared library load? (y or [n]) y 
Breakpoint 1 (malloc_error_break) pending. 
(gdb) run -threads 8 
Starting program: ~/code/app/app -threads 8 
[New Thread 0x1903 of process 45436] 
warning: unhandled dyld version (15) 

它運行幾個小時。 CTRL-C,然後運行./app -threads 8,並在第二次或兩次(幾百萬次迭代)後崩潰。

很明顯,其中一個線程存在問題。但這些線程的工作人員非常大(幾百行代碼)。沒有什麼突出的。

請注意,線程迭代每秒約2000萬的循環。

  • MACOS 10.12.3
  • 自制瓦特/ GNU GCC和OpenSSL(鏈接到加密)

PS,不熟悉C太多 - 尤其是任何類型的調試。善良,表達/詳細的答案。 :)

+1

這肯定看起來很可疑:'mach_vm_map(size = 13835058055282167808)'對我來說,'buf-> length'未初始化或損壞。考慮到它是一個多線程的過程,我會先仔細查看線程之間的所有共享變量/內存並仔細檢查,確保正確的鎖定/同步。總的來說,這聽起來像一個典型的競爭條件在這種情況下,外部工具往往沒有幫助,因爲它們放慢了程序足以隱藏問題。 –

+1

我會嘗試valgrind,在做其他事情之前...... – m8mble

回答

3

有時被忽視的一種調試技術是在代碼中包含調試打印,當然這有它的缺點,但也有其優點。儘管面對異常終止,但您必須記住的一件事是確保打印輸出實際得到打印。通常,打印到stderr就足夠了(但是如果沒有明確說明,則可能需要fflush該流)。

另一個竅門是在錯誤發生之前停止程序。這要求您知道程序即將崩潰的時間,最好儘可能接近。你通過使用raise來做到這一點:

raise(SIGSTOP); 

這並不終止程序,它只是暫停執行。現在您可以使用命令gdb <program-name> <pid>附加gdb(使用ps查找進程的PID)。現在gdb你必須告訴它忽略SIGSTOP

> handle SIGSTOP ignore 

然後,您可以設置斷點。您也可以使用finish命令(可能必須多次發出才能返回到您的代碼)步出raise函數。

此技巧使得程序在您決定停止時具有正常行爲,希望在gdb下運行時的最後部分不會改變行爲enuogh。第三個選項是使用valgrind。通常當你看到這些錯誤時,會出現錯誤valgrind。這些訪問超出範圍和未初始化的變量。

0

許多內存管理器初始化內存已知的壞值揭露這樣的問題(例如微軟的CRT將使用範圍值(0XCD指未初始化的,0xDD已經意味着自由等)。

每個使用malloc後,嘗試將內存memset到0xCD(或其他常量值),這將允許您更容易地使用調試器識別未初始化的內存。不要使用0x00,因爲這是一個「正常」值,並且很難找到如果這是錯的(它也將可能「修復」你的問題)

喜歡的東西:

void *memory = malloc(sizeof(my_object)); 
memset(memory, 0xCD, sizeof(my_object)); 

如果你知道塊的大小,你可以做免費之前類似的東西(這有時很難,除非你知道你的對象的大小,或跟蹤它以某種方式):

memset(memory, 0xDD, sizeof(my_object)); 
free(memory);