如何使用valgrind来查找内存泄漏?

如何使用valgrind来查找程序中的内存泄漏?

有人能帮我描述一下手术的步骤吗?

我使用的是Ubuntu 10.04,我有一个程序a.c,请帮助我。

598105 次浏览

试试这个:

valgrind --leak-check=full -v ./your_program

只要安装了valgrind,它就会检查你的程序并告诉你哪里出了问题。它可以给你指针和可能发现漏洞的大致位置。如果你是段错误,试着通过gdb运行它。

你可以运行:

valgrind --leak-check=full --log-file="logfile.out" -v [your_program(and its arguments)]

如何运行Valgrind

不是为了侮辱OP,而是为了那些遇到这个问题并且还不熟悉Linux -你可能需要安装Valgrind的人。

sudo apt install valgrind  # Ubuntu, Debian, etc.
sudo yum install valgrind  # RHEL, CentOS, Fedora, etc.
sudo pacman -Syu valgrind  # Arch, Manjaro, Garuda, etc
Valgrind很容易用于C/ c++代码,但甚至可以用于其他代码 当正确配置时(参见 for 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:显示所有的“明确的,间接的,可能的,可达到的”;泄漏类型在&;full"报告。
  • --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个字节。这是怎么发生的?错误报告只是说 mainmalloc。在一个较大的程序中,这将是非常麻烦的 追捕。这是因为可执行文件是如何编译的。我们可以 实际上是逐行详细了解出了什么问题。重新编译程序 带调试标志(我在这里使用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指向准确的代码行 分配泄漏的内存!(措辞很重要:可能不会 确切地说,你的泄漏是,但什么被泄漏。痕迹可以帮助你找到 其中< / em >)。

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)

内存泄漏调试技术错误

  • 使用www.cplusplus.com!它有大量关于C/ c++函数的文档。

  • 内存泄漏的一般建议:

  • 确保动态分配的内存确实被释放。

  • 不要分配内存,忘记分配指针。

  • 不要用一个新的指针覆盖一个指针,除非旧的内存被释放。

  • 对于内存错误的一般建议:

  • 访问并写入你确定属于你的地址和索引。内存 错误不同于泄漏;它们通常只是IndexOutOfBoundsException 类型的问题。< / p >

  • 释放内存后不访问或写入内存。

  • 有时候你的泄漏/错误可能是相互关联的,就像IDE发现你还没有输入右括号一样。解决一个问题可以解决其他问题,所以寻找一个看起来是罪魁祸首的问题,并应用下面的一些想法:

  • 列出代码中依赖/依赖于的函数 “offending"有内存错误的代码。跟踪程序的执行 (甚至可能在gdb中),并寻找前置条件/后置条件错误。其思想是跟踪程序的执行,同时关注已分配内存的生命周期

  • 试着注释掉“冒犯的”;代码块(在合理范围内,所以您的代码 仍然编译)。如果Valgrind错误消失了,你就找到了它的位置

  • 如果其他方法都失败了,试着查一下。Valgrind也有文档 !


查看常见的泄漏和错误

注意你的指针

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的*(alphabet + 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)是 再次出界,类似于无效写入。无效读取也是 离一错误的常见结果。看作业的右边 操作。< / p >


开源(U/Dys)的乌托邦

我怎么知道什么时候漏水是我的?我在使用时如何找到漏洞 别人的代码?我发现了一个不属于我的漏洞;我该做点什么吗?所有 都是合理的问题。首先,两个真实世界的例子,展示了两个类 共同的遭遇。< / p >

简颂: JSON库

#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字符串并解析它。在制作过程中, 我们使用库调用来为我们进行解析。杨松做了必要的事 动态分配,因为JSON可以包含自己的嵌套结构。 然而,这并不意味着我们decref或"free"记忆给了我们从 每一个函数。事实上,我上面写的这段代码抛出了一个“无效读取”; 以及“无效书写”。当你去掉decref行,这些错误就消失了 value。< / p > < p >为什么?变量value被认为是一个“借来的引用”;在杨松 API。Jansson为你跟踪它的内存,你只需要decref JSON结构相互独立。这里的教训是: 阅读文档。真的。有时候很难理解,但是 他们会告诉你为什么会发生这些事。相反,我们有

. 现有的问题关于内存错误

SDL:一个图形和游戏库

#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;
}

这段代码有什么问题?它始终为我泄漏~ 212kib的内存。花点时间思考一下。我们打开SDL,然后关闭。答案?没什么问题。

乍听起来可能很奇怪。说实话,图形是混乱的,有时你不得不接受一些泄漏作为标准库的一部分。这里的教训:您不需要消除每个内存泄漏。有时候你只需要抑制泄密 因为它们是已知的问题,你无能为力。(这不是我允许你忽略自己泄露的信息!)

对虚空的回答

我怎么知道什么时候漏水是我的?< br > 它是。(99%确定)

当我使用别人的代码时,我如何找到我的漏洞?< br > 很有可能已经有人找到了。试试谷歌!如果失败了,使用我上面给你的技巧。如果失败了,你看到的大多是API调用,而你自己的堆栈跟踪很少,请参阅下一个问题 我发现了一个不属于我的漏洞;我该做点什么吗?< br > 是的!大多数api都有报告错误和问题的方法。使用它们!帮助回馈你在项目中使用的工具!< / p >


进一步的阅读

谢谢你陪了我这么久。我希望你已经学到了一些东西,因为我试图了解得到这个答案的广泛人群。我希望您在学习过程中已经问过一些问题:C的内存分配器是如何工作的?内存泄漏和内存错误究竟是什么?它们与段错误有什么不同?Valgrind是如何运作的?如果你有这些,请满足你的好奇心:

您可以在.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日志文件。