我在Upstart init进程(pid 1)中有内存泄漏,我在调试它时有哪些选项?
编辑:建议我一些真正的工具,手动输入printfs或手动计算内存分配不会削减它。同时转储init核心并探索它并不是一个真正的选择。
UPD1: valgrind 不起作用。使用正确的valgrind + init magic替换内核命令行上的/ sbin / init似乎不是一个选项,因为它尝试访问/ proc for self for smaps,但是在init运行之前它们不可用。
UPD2: dmalloc 也不起作用(不在ARM上编译)。
答案 0 :(得分:8)
穷人的解决方案是只记录每个电话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”的定义放在同一个文件中 as“__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
)重新编译upstart,这样您就可以获得一些不错的回溯。如果愿意,您仍然可以优化(-O2/-O3
)代码。
将Upstart与额外的LD_FLAGS
--wrap=malloc
,--wrap=free
相关联。
现在任何暴风雨调用malloc
的任何地方,该符号都会神奇地解析为您的新符号__wrap_malloc
。很好地,这对于编译代码来说是透明的,因为它发生在链接时
就像shimming或instrumenting一样,没有任何混乱。
像往常一样运行重新编译的Upstart,直到您确定已发生泄漏。
查看日志中是否存在不匹配malloced
和addr
s。
几点说明:
--wrap=symbol
功能不适用于实际为宏的函数名称。所以请注意#define malloc nih_malloc
。这就是libnih你需要使用--wrap=nih_malloc
和__wrap_nih_malloc
来代替的。答案 1 :(得分:2)
您可以通过挂钩malloc / free调用来计算内存分配,并计算您分配的字节数,每次都可以释放。
答案 2 :(得分:2)
您也可以使用init不变,但创建一个包装器,将MALLOC_CHECK环境变量设置为1 or higher。这将让您看到一些内存分配诊断。
一种变化是稍微更改init源代码,以便在开始使用malloc之前尽早设置该环境变量。
您也可以像AmineK建议的那样将调试代码添加到init源代码本身。
答案 3 :(得分:0)
您可以尝试将您的新手版本与Google's TCMalloc相关联。它带有内置heap checker。
可以通过两种方式启用堆检查器:
HEAPCHECK
设置为{normal |严格| draconian}。HEAPCHECK
设置为local
并手动检查HeapProfileLeakChecker
个对象的代码。我不知道如何为init设置环境变量。
答案 4 :(得分:0)
如何在流程上运行pmap并检查正在增长的内存段。这可能会让你知道什么是吃内存。一个小脚本可以使这个过程几乎自动**。
**在过去的生活中,我实际上编写了一个脚本,该脚本将 n pmap快照占用 t 秒间隔的一组正在运行的进程。它的输出被输入到perl脚本中,该脚本识别出改变其大小的段。我用它在一些商业代码中找到了几个内存泄漏。 [我会分享这些剧本,但它们受前一雇主的知识产权(版权)保护。]