[英]How do I use valgrind to find memory leaks?
如何使用 valgrind 查找程序中的內存泄漏?
請有人幫助我並描述執行該程序的步驟?
我正在使用 Ubuntu 10.04 並且我有一個程序ac
,請幫幫我。
不是侮辱 OP,而是對於那些提出這個問題並且仍然是 Linux 新手的人 -您可能必須在您的系統上安裝 Valgrind 。
sudo apt install valgrind # Ubuntu, Debian, etc.
sudo yum install valgrind # RHEL, CentOS, Fedora, etc.
Valgrind 很容易用於 C/C++ 代碼,但如果配置正確,甚至可以用於其他語言(請參閱 Python 的此內容)。
要運行 Valgrind ,請將可執行文件作為參數(以及任何參數)傳遞給程序。
valgrind --leak-check=full \
--show-leak-kinds=all \
--track-origins=yes \
--verbose \
--log-file=valgrind-out.txt \
./executable exampleParam1
簡而言之,這些標志是:
--leak-check=full
:“將詳細顯示每個單獨的泄漏”--show-leak-kinds=all
:在“完整”報告中顯示所有“確定的、間接的、可能的、可達的”泄漏類型。--track-origins=yes
: --track-origins=yes
有用的輸出而不是速度。 這會跟蹤未初始化值的來源,這對於內存錯誤非常有用。 如果 Valgrind 慢得令人無法接受,請考慮關閉。--verbose
:可以告訴您程序的異常行為。 重復更多細節。--log-file
:寫入文件。 當輸出超過終端空間時很有用。最后,您希望看到如下所示的 Valgrind 報告:
HEAP SUMMARY:
in use at exit: 0 bytes in 0 blocks
total heap usage: 636 allocs, 636 frees, 25,393 bytes allocated
All heap blocks were freed -- no leaks are possible
ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
所以,你有一個內存泄漏,Valgrind 沒有說任何有意義的事情。 也許,像這樣:
5 bytes in 1 blocks are definitely lost in loss record 1 of 1
at 0x4C29BE3: malloc (vg_replace_malloc.c:299)
by 0x40053E: main (in /home/Peri461/Documents/executable)
我們也來看看我寫的C代碼:
#include <stdlib.h>
int main() {
char* string = malloc(5 * sizeof(char)); //LEAK: not freed!
return 0;
}
好吧,丟失了 5 個字節。 它怎么發生的? 錯誤報告只是說main
和malloc
。 在一個更大的程序中,追捕這將是非常麻煩的。 這是因為可執行文件是如何編譯的。 我們實際上可以逐行獲取有關出錯的詳細信息。 使用調試標志重新編譯您的程序(我在這里使用gcc
):
gcc -o executable -std=c11 -Wall main.c # suppose it was this at first
gcc -o executable -std=c11 -Wall -ggdb3 main.c # add -ggdb3 to it
現在有了這個調試版本, Valgrind 指向分配泄漏內存的確切代碼行! (用詞是很重要的:它可能不是正是你的泄漏,但什么得到了泄露的跟蹤可以幫助您找到在哪里。)
5 bytes in 1 blocks are definitely lost in loss record 1 of 1
at 0x4C29BE3: malloc (vg_replace_malloc.c:299)
by 0x40053E: main (main.c:4)
IndexOutOfBoundsException
類型的問題。有時,您的泄漏/錯誤可以相互關聯,就像 IDE 發現您尚未鍵入結束括號一樣。 解決一個問題可以解決其他問題,所以尋找一個看起來很好的罪魁禍首並應用以下一些想法:
gdb
),並查找前置條件/后置條件錯誤。 這個想法是在關注分配內存的生命周期的同時跟蹤程序的執行。60 bytes in 1 blocks are definitely lost in loss record 1 of 1
at 0x4C2BB78: realloc (vg_replace_malloc.c:785)
by 0x4005E4: resizeArray (main.c:12)
by 0x40062E: main (main.c:19)
和代碼:
#include <stdlib.h>
#include <stdint.h>
struct _List {
int32_t* data;
int32_t length;
};
typedef struct _List List;
List* resizeArray(List* array) {
int32_t* dPtr = array->data;
dPtr = realloc(dPtr, 15 * sizeof(int32_t)); //doesn't update array->data
return array;
}
int main() {
List* array = calloc(1, sizeof(List));
array->data = calloc(10, sizeof(int32_t));
array = resizeArray(array);
free(array->data);
free(array);
return 0;
}
作為助教,我經常看到這個錯誤。 學生使用局部變量而忘記更新原始指針。 這里的錯誤是注意到realloc
實際上可以將分配的內存移動到其他地方並更改指針的位置。 然后我們離開resizeArray
而不告訴array->data
數組移動到哪里。
1 errors in context 1 of 1:
Invalid write of size 1
at 0x4005CA: main (main.c:10)
Address 0x51f905a is 0 bytes after a block of size 26 alloc'd
at 0x4C2B975: calloc (vg_replace_malloc.c:711)
by 0x400593: main (main.c:5)
和代碼:
#include <stdlib.h>
#include <stdint.h>
int main() {
char* alphabet = calloc(26, sizeof(char));
for(uint8_t i = 0; i < 26; i++) {
*(alphabet + i) = 'A' + i;
}
*(alphabet + 26) = '\0'; //null-terminate the string?
free(alphabet);
return 0;
}
請注意,Valgrind 將我們指向上面注釋的代碼行。 大小為 26 的數組的索引為 [0,25],這就是為什么*(alphabet + 26)
是無效寫入的原因——它越界了。 無效寫入是一對一錯誤的常見結果。 查看賦值操作的左側。
1 errors in context 1 of 1:
Invalid read of size 1
at 0x400602: main (main.c:9)
Address 0x51f90ba is 0 bytes after a block of size 26 alloc'd
at 0x4C29BE3: malloc (vg_replace_malloc.c:299)
by 0x4005E1: main (main.c:6)
和代碼:
#include <stdlib.h>
#include <stdint.h>
int main() {
char* destination = calloc(27, sizeof(char));
char* source = malloc(26 * sizeof(char));
for(uint8_t i = 0; i < 27; i++) {
*(destination + i) = *(source + i); //Look at the last iteration.
}
free(destination);
free(source);
return 0;
}
Valgrind 將我們指向上面的注釋行。 看看這里的最后一次迭代,這是*(destination + 26) = *(source + 26);
. 但是, *(source + 26)
再次越界,類似於無效寫入。 無效讀取也是一對一錯誤的常見結果。 查看賦值操作的右側。
我怎么知道什么時候泄漏是我的? 當我使用別人的代碼時,如何找到我的漏洞? 我發現了一個不屬於我的泄漏; 我應該做些什么嗎? 都是合理的問題。 首先,2 個真實世界的例子展示了 2 類常見的遭遇。
#include <jansson.h>
#include <stdio.h>
int main() {
char* string = "{ \"key\": \"value\" }";
json_error_t error;
json_t* root = json_loads(string, 0, &error); //obtaining a pointer
json_t* value = json_object_get(root, "key"); //obtaining a pointer
printf("\"%s\" is the value field.\n", json_string_value(value)); //use value
json_decref(value); //Do I free this pointer?
json_decref(root); //What about this one? Does the order matter?
return 0;
}
這是一個簡單的程序:它讀取一個 JSON 字符串並解析它。 在制作中,我們使用庫調用來為我們進行解析。 Jansson 動態地進行必要的分配,因為 JSON 可以包含自身的嵌套結構。 然而,這並不意味着我們要decref
或“釋放”每個函數給我們的內存。 事實上,我上面寫的這段代碼同時拋出了“無效讀取”和“無效寫入”。 當您取出value
的decref
行時,這些錯誤就會消失。
為什么? 變量value
在 Jansson API 中被視為“借用引用”。 Jansson 會為您跟蹤其內存,您只需對彼此獨立的 JSON 結構進行decref
。 這里的教訓:閱讀文檔。 真的。 有時很難理解,但他們會告訴您為什么會發生這些事情。 相反,我們有關於此內存錯誤的現有問題。
#include "SDL2/SDL.h"
int main(int argc, char* argv[]) {
if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO) != 0) {
SDL_Log("Unable to initialize SDL: %s", SDL_GetError());
return 1;
}
SDL_Quit();
return 0;
}
這段代碼有什么問題? 它始終為我泄漏約 212 KiB 的內存。 花點時間考慮一下。 我們打開然后關閉 SDL。 回答? 沒有任何錯誤。
這乍一聽可能很奇怪。 說實話,圖形很亂,有時你不得不接受一些泄漏作為標准庫的一部分。 這里的教訓是:您無需消除所有內存泄漏。 有時您只需要抑制泄漏,因為它們是您無能為力的已知問題。 (這不是我允許忽略你自己的泄漏!)
我怎么知道什么時候泄漏是我的?
這是。 (無論如何,99% 肯定)
當我使用別人的代碼時,如何找到我的漏洞?
很有可能其他人已經找到了。 試試谷歌! 如果失敗,請使用我上面給你的技能。 如果失敗並且您主要看到 API 調用而很少看到您自己的堆棧跟蹤,請參閱下一個問題。
我發現了一個不屬於我的泄漏; 我應該做些什么嗎?
是的! 大多數 API 都有報告錯誤和問題的方法。 使用它們! 幫助回饋您在項目中使用的工具!
謝謝你陪我這么久。 我希望你已經學到了一些東西,因為我試圖傾向於得到這個答案的廣泛人群。 我希望你一路上問過一些事情:C 的內存分配器是如何工作的? 什么是內存泄漏和內存錯誤? 它們與段錯誤有何不同? Valgrind 是如何工作的? 如果你有這些,請一定要滿足你的好奇心:
嘗試這個:
valgrind --leak-check=full -v ./your_program
只要安裝了 valgrind,它就會通過您的程序並告訴您出了什么問題。 它可以為您提供可能發現泄漏的指針和大致位置。 如果您遇到了段錯誤,請嘗試通過gdb
運行它。
你可以運行:
valgrind --leak-check=full --log-file="logfile.out" -v [your_program(and its arguments)]
您可以在 .bashrc 文件中創建別名,如下所示
alias vg='valgrind --leak-check=full -v --track-origins=yes --log-file=vg_logfile.out'
所以每當你想檢查內存泄漏時,只需簡單地做
vg ./<name of your executable> <command line parameters to your executable>
這將在當前目錄中生成一個 Valgrind 日志文件。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.