Upstart init正在泄漏内存,你怎么debugging它?

我在Upstart init进程(pid 1)中有内存泄漏,我有什么选项来debugging它?

编辑:build议我一些真正的工具,手动将printfs或手动计算内存分配不会削减它。 也倾销初始核心和捅周围是不是一个真正的select。

UPD1: valgrind不起作用。 在内核命令行上用适当的valgrind + init魔术replace/ sbin / init似乎不是一个选项,因为它试图访问smaps的/ proc,但是在init运行之前,这些不可用。

UPD2: dmalloc也不能正常工作(不能在ARM上编译)。

一个穷人的解决办法是只记录每个呼叫mallocfree ,然后梳理日志和寻找模式。

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”之前解析调用。


更新

只是要清楚这是如何有用的。

  • 将一个自定义文件添加到Upstart的构建。

喜欢这个:

 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
  • 使用gcc的内置追溯功能。
  • 所有这些更改只影响重新编译的Upstart可执行文件。
  • 你可以转储日志到一个sqlite数据库,而可能会更容易找到不匹配的malloc和释放。
  • 你可以使你的日志格式成为一个SQL插入语句,然后将它们插入到数据库中进行进一步的分析。

你也可以不改变地使用init,但是创建一个包装器,将MALLOC_CHECK环境变量设置为1或者更高 。 这会让你看到一些内存分配的诊断。

一个变化是稍微改变init源代码,以便在开始使用malloc之前尽早设置这个环境变量。

您也可以像AmineK建议的那样,将调试代码添加到init源代码本身。

你可以通过hook malloc / free调用你自己的内存分配,并计算你分配的字节数,每次释放。

你可以试试把你的新贵与Google的TCMalloc联系起来 。 它配有一个内置堆检查器 。

堆检查器可以通过两种方式启用:

  • 将环境变量HEAPCHECK设置为{normal |中的一个 严格| 严酷的}。
  • HEAPCHECK设置为local并使用HeapProfileLeakChecker对象手动检查代码。

但是我不知道如何为init设置一个环境变量。

如何在进程中运行pmap并检查哪些内存段正在增长。 这可能会给你一些什么饮食记忆的想法。 一个小脚本可以使这个过程几乎自动**。

**在过去的一段时间里,我实际上写了一个脚本,这个脚本会把一组正在运行的进程的pmap快照分隔开来。 这个输出被输入到一个perl脚本中,用来识别改变它们大小的段。 我用它来定位一些商业代码中的一些内存泄漏。 [我会分享这些脚本,但是这些脚本属于以前雇主的知识产权(版权)。]

  • 约翰