转载linux下内存泄露查找、BUG调试

先收藏着,抽空好好看看:

简介

调试程序有很多方法,例如向屏幕上打印消息,使用调试器,或者只需仔细考虑程序如何运行,并对问题进行有根有据的猜测。

在修复 bug 之前,首先要确定在源程序中的位置。例如,当一个程序产生崩溃或生成核心转储(core dump)时,您就需要了解是哪行代码发生了崩溃。在找到有问题的代码行之后,就可以确定这个函数中变量的值,函数是如何调用的,更具体点说,为什么会发生这种错误。使用调试器查找这些信息非常简单。

本文将简要介绍几种用于修复一些很难通过可视化地检查代码而发现的 bug 的技术,并阐述了如何使用在 Linux on Power 架构上可用的工具。

调试内存问题的工具和技术

动态内存分配看起来似乎非常简单:您可以根据需要分配内存 —— 使用 malloc() 或其变种 —— 并在不需要时释放这些内存。实际上,内存管理的问题是软件中最为常见的 bug,因为通常在程序启动时这些问题并不明显。例如,程序中的内存泄漏可能开始并不为人注意,直到经过多天甚至几个月的运行才会被发现。接下来的几节将简要介绍如何使用流行的调试器 Valgrind 来发现并调试这些最常见的内存 bug。

在开始使用任何调试工具之前,请考虑这个工具是否对重新编译应用程序有益,是否可以支持具有调试信息的库(-g选项)。如果没有启用调试信息,调试工具可以做的最好的事情也不过是猜测一段特定的代码是属于哪个函数的。这使得错误消息和概要分析输出几乎没有什么用处。使用-g选项,您就有可能获得一些信息来直接指出相关的代码行。

Valgrind

Valgrind 已经在 Linux 应用程序开发社区中广泛用来调试应用程序。它尤其擅长发现内存管理的问题。它可以检查程序运行时的内存泄漏问题。这个工具目前正由 Julian Seward 进行开发,并由 Paul Mackerras 移植到了 Power 架构上。

要安装 Valgrind,请从 Valgrind 的 Web 站点上下载源代码(参阅)。切换到 Valgrind 目录,并执行下面的命令:

# make# make check# make install

Valgrind 的错误报告

Valgrind 的输出格式如下:

清单 1. Valgrind 的输出消息

# valgrind du –x –s..==29404== Address 0x1189AD84 is 0 bytes after a block of size 12 alloc’d==29404== at 0xFFB9964: malloc (vg_replace_malloc.c:130)==29404== by 0xFEE1AD0: strdup (in /lib/tls/libc.so.6)==29404== by 0xFE94D30: setlocale (in /lib/tls/libc.so.6)==29404== by 0x10001414: main (in /usr/bin/du)

==29404==是进程的 ID。消息Address 0x1189AD84 is 0 bytes after a block of size 12 alloc’d说明在这个 12 字节的数组后面没有存储空间了。第二行以及后续几行说明内存是在 130 行(vg_replace_malloc.c)的strdup()程序中进行分配的。strdup()是在 libc.so.6 库的setlocale()中调用的;main()调用了setlocale()。

未初始化的内存

最为常见的一个 bug 是程序使用了未初始化的内存。未初始化的数据可能来源于:

未经初始化的变量malloc 函数所分配的数据,在写入值之前使用了

下面这个例子使用了一个未初始化的数组:

清单 2. 使用未初始化的内存

2 {3int i[5];45if (i[0] == 0)6i[1]=1;7return 0;8 }

在这个例子中,整数数组 i[5] 没有进行初始化;因此,i[0] 包含的是一个随机数。因此使用 i[0] 的值来判断一个条件分支就会导致不可预期的问题。Valgrind 可以很容易捕获这种错误条件。当您使用 Valgrind 运行这个程序时,就会接收到下面的消息:

清单 3. Valgrind 的输出消息

# gcc –g –o test1 test1.c# valgrind ./test1..==31363== ==31363== Conditional jump or move depends on uninitialised value(s)==31363== at 0x1000041C: main (test1.c:5)==31363== ==31363== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 7 from 1)==31363== malloc/free: in use at exit: 0 bytes in 0 blocks.==31363== malloc/free: 0 allocs, 0 frees, 0 bytes allocated.==31363== For counts of detected errors, rerun with: -v==31363== No malloc’d blocks — no leaks are possible.

Valgrind 的输出说明,有一个条件分支依赖于文件 test1.c 中第 5 行中的一个未初始化的变量。

内存泄漏

内存泄漏是另外一个常见的问题,也是很多程序中最难判断的问题。内存泄漏的主要表现为:当程序连续运行时,与程序相关的内存(或堆)变得越来越大。结果是,当这个程序所消耗的内存达到系统的上限时,就会自己崩溃;或者会出现更严重的情况:挂起或导致系统崩溃。下面是一个有内存泄漏 bug 的示例程序:

清单 4. 内存泄漏示例

1 int main(void)2 {3char *p1;4char *p2;56p1 = (char *) malloc(512);7p2 = (char *) malloc(512);89p1=p2;1011free(p1);12free(p2);13 }

上面的代码分别给字符指针 p1 和 p2 分配了两个 512 字节的内存块,然后将指向第一个内存块的指针设置为指向第二个内存块。结果是,第二个内存块的地址丢失了,并导致内存泄漏。在使用 Valgrind 运行这个程序时,会返回如下的消息:

清单 5. Valgrind 的输出消息

# gcc –g –o test2 test2.c# valgrind ./test2..==31468== Invalid free() / delete / delete[]==31468== at 0xFFB9FF0: free (vg_replace_malloc.c:152)==31468== by 0x100004B0: main (test2.c:12)==31468== Address 0x11899258 is 0 bytes inside a block of size 512 free’d==31468== at 0xFFB9FF0: free (vg_replace_malloc.c:152)==31468== by 0x100004A4: main (test2.c:11)==31468== ==31468== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 7 from 1)==31468== malloc/free: in use at exit: 512 bytes in 1 blocks.==31468== malloc/free: 2 allocs, 2 frees, 1024 bytes allocated.==31468== For counts of detected errors, rerun with: -v==31468== searching for pointers to 1 not-freed blocks.==31468== checked 167936 bytes.==31468== ==31468== LEAK SUMMARY:==31468== definitely lost: 512 bytes in 1 blocks.==31468==possibly lost: 0 bytes in 0 blocks.==31468== still reachable: 0 bytes in 0 blocks.==31468==suppressed: 0 bytes in 0 blocks.==31468== Use –leak-check=full to see details of leaked memory.

正如您可以看到的一样,Valgrind 报告说这个程序中有 512 字节的内存丢失了。

非法写/读

我想,这就是旅行的真义吧。

转载linux下内存泄露查找、BUG调试

相关文章:

你感兴趣的文章:

标签云: