我在Upstart init进程(pid 1)中有内存泄漏,我有什么选项来debugging它?
编辑:build议我一些真正的工具,手动将printfs或手动计算内存分配不会削减它。 也倾销初始核心和捅周围是不是一个真正的select。
UPD1: valgrind不起作用。 在内核命令行上用适当的valgrind + init魔术replace/ sbin / init似乎不是一个选项,因为它试图访问smaps的/ proc,但是在init运行之前,这些不可用。
UPD2: dmalloc也不能正常工作(不能在ARM上编译)。
一个穷人的解决办法是只记录每个呼叫malloc
和free
,然后梳理日志和寻找模式。
ld
提供了一个惊人的功能,可以帮助这里。
--wrap=symbol
使用符号的包装函数。 任何未定义的符号引用将被解析为“__wrap_symbol”。 对“__real_symbol”的任何未定义的引用将被解析为符号。
这可以用来为系统功能提供包装。 包装函数应该被称为“__wrap_symbol”。 如果它希望调用系统函数,它应该调用“__real_symbol”。
这是一个微不足道的例子:
void * __wrap_malloc (size_t c) { printf ("malloc called with %zu\n", c); return __real_malloc (c); }
如果使用–wrap malloc链接其他代码,则所有对“malloc”的调用将调用函数“__wrap_malloc”。 在“__wrap_malloc”中对“__real_malloc”的调用将调用真正的“malloc”函数。
你也可以提供一个“__real_malloc”函数,这样没有–wrap选项的链接将会成功。 如果你这样做,你不应该把“__real_malloc”的定义放在与“__wrap_malloc”相同的文件中; 如果这样做,汇编程序可能会在链接器有机会将其包装到“malloc”之前解析调用。
只是要清楚这是如何有用的。
喜欢这个:
void*__wrap_malloc( size_t c ) { void *malloced = __real_malloc(c); /* log malloced with its associated backtrace*/ /* something like: <malloced>: <bt-symbol-1>, <bt-symbol-2>, .. */ return malloced } void __wrap_free( void* addr ) { /* log addr with its associated backtrace*/ /* something like: <addr>: <bt-symbol-1>, <bt-symbol-2>, .. */ __real_free(addr); }
用调试符号( -g
)重新编译新手,这样你可以得到一些不错的回溯。 如果您愿意,您仍然可以优化( -O2/-O3
)代码。
用额外的LD_FLAGS
链接Upstart --wrap=malloc
,– --wrap=free
。
现在任何地方Upstart调用malloc
的符号将神奇地解决你的新符号__wrap_malloc
。 精美地说,这对编译后的代码来说是透明的,因为它发生在链接时。
这就像填补或摆脱任何混乱。
像往常一样运行重新编译的Upstart,直到确定发生了泄漏。
通过日志查找不匹配的malloced
s和addr
s。
几个注意事项:
--wrap=symbol
功能不适用于实际为宏的函数名称。 所以请注意#define malloc nih_malloc
。 这就是你需要使用的--wrap=nih_malloc
和__wrap_nih_malloc
。 你也可以不改变地使用init,但是创建一个包装器,将MALLOC_CHECK环境变量设置为1或者更高 。 这会让你看到一些内存分配的诊断。
一个变化是稍微改变init源代码,以便在开始使用malloc之前尽早设置这个环境变量。
您也可以像AmineK建议的那样,将调试代码添加到init源代码本身。
你可以通过hook malloc / free调用你自己的内存分配,并计算你分配的字节数,每次释放。
你可以试试把你的新贵与Google的TCMalloc联系起来 。 它配有一个内置堆检查器 。
堆检查器可以通过两种方式启用:
HEAPCHECK
设置为{normal |中的一个 严格| 严酷的}。 HEAPCHECK
设置为local
并使用HeapProfileLeakChecker
对象手动检查代码。 但是我不知道如何为init设置一个环境变量。
如何在进程中运行pmap并检查哪些内存段正在增长。 这可能会给你一些什么饮食记忆的想法。 一个小脚本可以使这个过程几乎自动**。
**在过去的一段时间里,我实际上写了一个脚本,这个脚本会把一组正在运行的进程的pmap快照分隔开来。 这个输出被输入到一个perl脚本中,用来识别改变它们大小的段。 我用它来定位一些商业代码中的一些内存泄漏。 [我会分享这些脚本,但是这些脚本属于以前雇主的知识产权(版权)。]