Upstart init正在泄漏内存,你如何调试呢?

时间:2010-10-08 04:34:59

标签: linux memory-leaks embedded init upstart

我在Upstart init进程(pid 1)中有内存泄漏,我在调试它时有哪些选项?

编辑:建议我一些真正的工具,手动输入printfs或手动计算内存分配不会削减它。同时转储init核心并探索它并不是一个真正的选择。

UPD1: valgrind 不起作用。使用正确的valgrind + init magic替换内核命令行上的/ sbin / init似乎不是一个选项,因为它尝试访问/ proc for self for smaps,但是在init运行之前它们不可用。

UPD2: dmalloc 也不起作用(不在ARM上编译)。

5 个答案:

答案 0 :(得分:8)

穷人的解决方案是只记录每个电话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”的定义放在同一个文件中     as“__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)重新编译upstart,这样您就可以获得一些不错的回溯。如果愿意,您仍然可以优化(-O2/-O3)代码。

  • 将Upstart与额外的LD_FLAGS --wrap=malloc--wrap=free相关联。
    现在任何暴风雨调用malloc的任何地方,该符号都会神奇地解析为您的新符号__wrap_malloc。很好地,这对于编译代码来说是透明的,因为它发生在链接时 就像shimminginstrumenting一样,没有任何混乱。

  • 像往常一样运行重新编译的Upstart,直到您确定已发生泄漏。

  • 查看日志中是否存在不匹配mallocedaddr s。

几点说明:

  • --wrap=symbol功能不适用于实际为宏的函数名称。所以请注意#define malloc nih_malloc。这就是libnih你需要使用--wrap=nih_malloc__wrap_nih_malloc来代替的。
  • 使用gcc的builtin backtracing功能。
  • 所有这些更改仅影响重新编译的Upstart可执行文件。
  • 您可以将日志转储到sqlite数据库,而不是更容易找到不匹配的mallocs和frees。
  • 您可以将日志格式化为SQL插入语句,然后将它们插入到数据库中进行进一步分析。

答案 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脚本中,该脚本识别出改变其大小的段。我用它在一些商业代码中找到了几个内存泄漏。 [我会分享这些剧本,但它们受前一雇主的知识产权(版权)保护。]

  • 约翰