7

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

编辑:为此建议我一些真正的工具,手动放置 printfs 或手动计算内存分配不会削减它。还转储 init 核心并四处寻找并不是一个真正的选择。

UPD1: valgrind不起作用。用适当的 valgrind + init 魔法替换内核命令行上的 /sbin/init 似乎不是一个选项,因为它尝试访问 /proc for self 以获取 smap,但在运行 init 之前这些不可用。

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

4

5 回答 5

8

穷人的解决方案是只记录每个呼叫mallocfree然后梳理日志并寻找模式。

ld提供了一个可以在这里提供帮助的惊人功能。

--wrap=symbol

对符号使用包装函数。任何未定义的符号引用都将解析为“__wrap_symbol”。任何对“__real_symbol”的未定义引用都将被解析为符号。

This can be used to provide a wrapper for a system function. The wrapper function should be called "__wrap_symbol". If it wishes to call the system function, it should call "__real_symbol".

Here is a trivial example:

void *
__wrap_malloc (size_t c)
{
   printf ("malloc called with %zu\n", c);
   return __real_malloc (c);
}

If you link other code with this file using --wrap malloc, then all calls to "malloc" will call the function "__wrap_malloc" instead. The call to "__real_malloc" in "__wrap_malloc" will call the real "malloc" function.

You may wish to provide a "__real_malloc" function as well, so that links without the --wrap option will succeed. If you do this, you should not put the definition of "__real_malloc" in the same file as "__wrap_malloc"; if you do, the assembler may resolve the call before the linker has a chance to wrap it to "malloc".


Update

Just to be clear on how this is useful.

  • Add a custom file to Upstart's build.

Like this:

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);
}
  • Recompile upstart with debug symbols (-g) so you can get some nice backtraces. You can still optimize (-O2/-O3) the code if you wish.

  • Link Upstart with the extra LD_FLAGS --wrap=malloc, --wrap=free.
    Now anywhere Upstart calls malloc the symbol will be magically resolved to your new symbol __wrap_malloc. Beautifully this is all transparent to the compiled code as it happens at link time.
    It's like shimming or instrumenting with out any of the mess.

  • Run the recompiled Upstart as usual until you're sure the leak has occured.

  • Look through the logs for mismatch malloceds and addrs.

A couple of notes:

  • The --wrap=symbol feature does not work with function names that are actually macros. So watch out for #define malloc nih_malloc. The this is what libnih does you'd need to use --wrap=nih_malloc and __wrap_nih_malloc instead.
  • Use gcc's builtin backtracing features.
  • All of these changes only affect the recompiled Upstart executable.
  • You could dump the logs to an sqlite DB instead with may make it easier to find mismatch mallocs and frees.
  • you can make you log format an SQL insert statement then just insert them into a database post-mortem for further analysis.
于 2010-10-11T16:51:14.890 回答
2

您可以通过挂钩 malloc/free 调用来自己检测内存分配,并计算每次分配和释放的字节数。

于 2010-10-11T10:32:37.420 回答
2

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

一种变体是稍微更改 init 源代码,以便在开始使用 malloc 之前尽早设置该环境变量本身。

您也可以按照 AmineK 的建议将调试代码添加到初始化源代码本身。

于 2010-10-11T10:52:32.260 回答
0

您可以尝试将您的新贵版本与Google 的 TCMalloc 链接。它带有一个内置的堆检查器

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

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

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

于 2010-10-11T16:28:17.130 回答
0

How about running pmap on the process and examining what memory segments are growing. That may give you some idea of what is eating memory. A little scripting could make this process almost automatic**.

** In a past life, I actually wrote a script that would take n pmap snapshots of a set of running processes spaced t seconds apart. The output of that was fed into a perl script that identified segments that changed their size. I used it to locate several memory leaks in some commercial code. [I would share the scripts, but they are covered under IP (copyright) of a previous employer.]

  • John
于 2010-10-14T18:29:00.020 回答