如何使用valgrind查找程序中的内存泄漏?
请有人帮助我并描述执行该程序的步骤吗?
我正在使用Ubuntu 10.04并且我有一个程序a.c
,请帮帮我。
答案 0 :(得分:152)
我想为如何使用Valgrind建立一个更详细的解释 有效地以及如何解决内存泄漏问题。不是为了侮辱OP,而是为了那些 谁来解决这个问题并且仍然是Linux的新手 - 你可能不得不这样做 在您的系统上安装Valgrind 。
sudo apt install valgrind # Ubuntu, Debian, etc.
sudo yum install valgrind # RHEL, CentOS, Fedora, etc.
Valgrind可以很容易地用于C / C ++代码,但甚至可以用于其他代码 正确配置语言(请参阅Python的this)。
要运行valgrind ,请将可执行文件作为参数传递(以及任何参数 程序的参数)。
valgrind --leak-check=full \
--show-leak-kinds=all \
--track-origins=yes \
--verbose \
--log-file=valgrind-out.txt \
./executable exampleParam1
这将在执行程序结束时生成一份报告(希望如此) 看起来像这样:
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
类型问题。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
或&#34;免费&#34;给予我们的记忆
每个功能。实际上,我上面写的这段代码会引发一个&#34;无效的读取&#34;
和#34;无效的写&#34;。当您取出decref
行时,这些错误就会消失
value
。
为什么呢?变量value
被认为是&#34;借来的参考&#34;在Jansson
API。 Jansson为您记录其记忆,您只需decref
JSON结构彼此独立。这里的教训:
阅读文档。真。它有时很难理解,但是
他们告诉你为什么会发生这些事情。相反,我们有
existing questions关于此内存错误。
#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;
}
this code有什么问题?它一直为我泄漏~212 KiB的内存。花点时间考虑一下。我们打开SDL然后关闭。回答?没有错。
That might sound bizarre at first。说实话,图形很乱,有时你必须接受一些泄漏作为标准库的一部分。这里的教训是:你不必平息每次内存泄漏。有时您只需要suppress the leaks ,因为他们已知的问题您无法对做任何事情。 (我不允许忽略你自己的泄密!)
我如何知道泄漏何时是我的?
它是。 (99%肯定,无论如何)
当我使用别人的代码时,如何找到泄密点?
有可能是其他人已经找到了它。试试谷歌!如果失败了,请使用我上面给你的技能。如果失败并且您主要看到API调用并且几乎没有自己的堆栈跟踪,请参阅下一个问题。
我发现了一个不是我的泄漏;我该怎么办?
是!大多数API都有报告错误和问题的方法。使用它们!帮助回复您在项目中使用的工具!
感谢你和我在一起这么久。我希望你能学到一些东西,因为我试图倾向于广泛的人们得出这个答案。我希望你在路上问过一些事情:C&#39的内存分配器是如何工作的?实际上是内存泄漏和内存错误?它们与段错误有什么不同? Valgrind如何运作?如果您有任何这些,请满足您的好奇心:
答案 1 :(得分:137)
试试这个:
valgrind --leak-check=full -v ./your_program
只要安装了valgrind,它就会通过你的程序告诉你出了什么问题。它可以为您提供指针和可能发现泄漏的大致位置。如果你是段错误,请尝试通过gdb
运行它。
答案 2 :(得分:25)
你可以运行:
valgrind --leak-check=full --log-file="logfile.out" -v [your_program(and its arguments)]
答案 3 :(得分:3)
您可以按照以下步骤在.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日志文件。