共计 3160 个字符,预计需要花费 8 分钟才能阅读完成。
1. 内存泄露介绍
在工作中,动态内存调配是常有的事,而随同动态内存调配而来的最大的问题就是“内存泄露”。所谓“内存泄露”的意思就是申请了内存,但遗记归还给零碎,长此以往,零碎的可分配内存越来越少,这种问题一旦呈现必然很难查找。起因很简略,程序是人写的,写的人都遗记本人已经在哪里调配了而没有开释,那零碎就更不能轻易帮忙回收内存了。一旦“内存泄露”产生,特地是放生在一些生命周期较长的程序中,从零碎的角度来说,可用内存莫名其妙地越来越少,形象地就比喻零碎上如同真的呈现了一个洞,内存从这个洞里被“漏掉”不见了。
2.mtrace 应用介绍
一旦发现零碎有这个“苗头”,事不宜迟就是要找到代码里哪里遗记偿还了动态分配的内存。而“内存调配跟踪(malloctracing)”机制则是帮忙查看“内存泄露”的好帮手,本文就来给大家介绍一下这个工具的应用,习惯上这个工具简称为 mtrace,下文也间接用 mtrace 指称这个工具。
mtrace 工具的次要思路是在的调用内存调配和开释的函数中装载“钩子(hook)”函数,通过钩子函数打印的日志来帮忙剖析对内存的应用是否存在问题。对该工具的应用包含两局部内容,一个是要批改源码,装载 hook 函数,另一个是通过运行批改后的程序,生成非凡的 log 文件,而后利用 mtrace 工具剖析日志,判断是否存在内存泄露以及定位可能产生内存泄露的代码地位。
mtrace 是 Glibc 的一部分,毋庸非凡装置。mtrace 钩子函数定义如下:
#include <mcheck.h>
void mtrace(void);
void muntrace(void);
其中 mtrace()用于开启内存调配跟踪,muntrace()用于勾销内存调配跟踪。
具体的做法是 mtrace()函数中会为那些和动态内存调配无关的函数(如 malloc、realloc、memalign 以及 free)装置“钩子(hook)”函数,这些 hook 函数会为记录所有无关内存调配和开释的跟踪信息,而 muntrace()则会卸载相应的 hook 函数。基于这些 hook 函数生成的调试跟踪信息,就能够剖析是否存在“内存泄露”这类问题了。
2.1. 示例代码
示例代码如下:
demo_mtrace_memleak.c
/* gcc demo_mtrace_memleak.c -o demo_mtrace_memleak -g -Wall */
#include<stdio.h>
#include<stdlib.h>
#include<mcheck.h>
int main(int argc, char **argv)
{mtrace();
char *p = malloc(16);
free(p);
p = malloc(32);
muntrace();
return 0;
}
如上,进行了两次 malloc 申请内存的操作,但只开释一次,因而会导致“内存透露”。看看 mtrace 是否能的到雷同的论断。
2.2. mtrace 命令行应用
mtrace 机制须要理论运行一下程序,而后能力生成跟踪的日志,但在运行程序前还须要定义并导出一个环境变量 MALLOC_TRACE 用于记录剖析日志,如下所示。
export MALLOC_TRACE=./memleak.log
上述的后果就是通知 mtrace 在生成日志信息时,在以后门路下创立一个名为 memleak.log 的文件,并将日志输入到这个文件中去。
运行程序后,查看 memleak.log 内容如下:
= Start
@ ./demo_mtrace_memleak:[0x7f6ca1400738] + 0x7fffc93796a0 0x10
@ ./demo_mtrace_memleak:[0x7f6ca1400748] - 0x7fffc93796a0
@ ./demo_mtrace_memleak:[0x7f6ca1400752] + 0x7fffc93796c0 0x20
= End
这个文件的内容有三行“无效”记录(除去第一行 =Start 和最初一行 =End),别离对应源码的 malloc->free->malloc 操作。
具体格局以第一行 @ ./demo_mtrace_memleak:[0x7f6ca1400738] + 0x7fffc93796a0 0x10
为例。./demo_mtrace_memleak
显然指的是运行的可执行程序的名字。[0x7f6ca1400738]
这里的数值是对应代码中第一次调用地址,但留神这是机器码的地址,恰好在编译可执行程序的时候利用 - g 带上了调试信息,所以齐全能够利用 addr2line 这个工具,反推出源文件的行数。具体做法如下:
# addr2line -f -e ./demo_mtrace_memleak 0x00748
main
/mnt/d/MingruiZhou/tinylab/demo_mtrace_memleak.c:11
确实在第 11 行。
接着前面的是一个符号 +,表明这一行对应的是分配内存,反之 - 示意是开释。再往后是一个数值 0x7fffc93796a0,是 malloc()函数调配的内存的首地址。最初是 0x10,换算成十进制就是 16,正是调配的内存的大小。
理解了具体格局后从三行无效日志中能够得出,因为第一行是调配,其调配的内存首地址是 0x7fffc93796a0,而第二行开释的内存的首地址也是 0x7fffc93796a0,天然阐明是一对,互相对消,不存在内存泄露。第三行调配的内存首地址是 0x7fffc93796c0,前面没有匹配的开释日志,则阐明这里呈现了“内存泄露”。
在理论工作中的场景代码相对不会就这么几行的,零碎提供了一个叫做 mtrace 的命令行工具能够帮忙实现对日志的剖析。
连忙来试一下。输出如下命令:
# mtrace ./demo_mtrace_memleak $MALLOC_TRACE
Memory not freed:
-----------------
Address Size Caller
0x00007fffc93796c0 0x20 at 0x7f6ca1400752
输入的后果曾经通知了所有。mtrace 这个工具须要至多两个参数,一个是生成的可执行程序文件的门路,还有一个是日志文件的门路。
mtrace 还能帮忙查找“反复开释”问题。示例如下:
/* gcc demo_mtrace_double_free.c -o demo_mtrace_double_free -g -Wall */
#include<stdio.h>
#include<malloc.h>
#include<mcheck.h>
int main(int argc,char **argv)
{
char *s = NULL;
mtrace();
s = malloc(32);
free(s);
free(s); //<--double free
muntrace();
return 0;
}
后果如下:
# gcc demo_mtrace_double_free.c -o demo_mtrace_double_free -g -Wall
#
# export MALLOC_TRACE=./double_free.log
# ./demo_mtrace_double_free
# mtrace ./demo_mtrace_double_free $MALLOC_TRACE
- 0x00007fffd74056a0 Free 4 was never alloc'd 0x7f07ff60075c
No memory leaks.
# addr2line -f -e ./demo_mtrace_double_free 0x75c
main
/mnt/d/MingruiZhou/tinylab/demo_mtrace_double_free.c:19
尽管最终并没有精确找到出错行,但也靠近谬误行。
email: MingruiZhou@outlook.com