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

Tum*_*oid 7 linux embedded memory-leaks init upstart

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

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

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

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

def*_*ode 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);
}
Run Code Online (Sandbox Code Playgroud)

如果使用--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);
}
Run Code Online (Sandbox Code Playgroud)
  • 用调试符号(-g)重新编译upstart,这样你就可以获得一些不错的回溯.-O2/-O3如果您愿意,您仍然可以优化()代码.

  • Link Upstart与额外的LD_FLAGS --wrap=malloc,--wrap=free.
    现在任何Upstart调用malloc符号都将神奇地解析为您的新符号__wrap_malloc.精美的这对于编译代码来说是透明的,因为它发生在链接时.
    这就像填补检测任何混乱.

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

  • 查看日志中的不匹配malloceds和addrs.

几个笔记:

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

  • 注意,如果你不想在这里使用printf,你可以分配一个缓冲区并将日志信息放入其中,然后在init完成后将其转储出来(或者只是在调试器中插入它).我希望理想的策略是确认结果在运行之间是一致的,弄清楚日志的哪一行包含没有匹配的free的malloc,然后将__wrap_malloc设置为陷阱在该行 - 此时,你可以在调试器中查看调用堆栈并找到有问题的调用. (2认同)