4

我有一组与共享内存 (ipc) 一起工作的程序 ~ 48GB。

在 Linux 3.6.0-rc5 中运行的程序,用纯 C 编写,在主计算机上编译的 gcc 负载平均为 6.0,每 10 秒跳到 16.0(24 核)

一个代理通过 0mq (3.2.3, ~1000 msgs/s 来自同一网络中的 12 台机器) 从其他机器接收数据,写入共享内存许多 (<50) 工作人员读取这些数据并进行一些计算。

使用大约 20% cpu 的代理每个使用 1% CPU 的工作人员周期性地跳跃 10%。

当所有分配在 init() 中完成时,所有程序都以这种方式编写 - 在程序启动时调用,在 destroy() 中完成所有空闲 - 在退出前调用

完全不使用任何 malloc/calloc/free 的重复代码。

但是这两个程序仍然泄漏。大约每分钟 120-240 字节。这并不多 - 内存在 7-8 天内耗尽,我只是启动/停止进程,但是每次监控应用程序报告我有关此重启的情况时,那些泄露的字节都会让我大吃一惊:)

坏事 - 由于使用共享内存,我无法运行 valgrind - 它只是停止分配/附加共享内存,然后一切都开始崩溃。

试图找到这个泄漏我制作了代理的剥离版本 - 没有泄漏,但我不能用相同数量的数据提供它。

在 gdb 下运行时仍然没有泄漏,但速度下降了 2/3 左右 - 所以重现这个错误可能没有那么快。

所以可能的泄漏在:

  • 我的代码。但是没有malloc/calloc。只是指针 +-,memcpy,memcmp
  • 一些标准库。glibc?系统日志?
  • 0mq 处理许多来源(不要认为每秒 1k/msgs 的流量太多)

是否存在任何其他工具/库/黑客可以在这种情况下提供帮助?

编辑:Shivan Raptor 询问了代码。重复部分是 5k 行数学。没有我提到的任何分配。

但是在这里开始、停止和重复输入:

int main(int argc, char **argv)
{
    ida_init(argc, argv, PROXY);
    ex_pollponies(); // repetive
    ida_destroy();
    return(0);
}


// with some cuttings

int ex_pollponies(void)
{
  int i, rc;
  unsigned char buf[90];
  uint64_t fos[ROLLINGBUFFERSIZE];
  uint64_t bhs[ROLLINGBUFFERSIZE];
  int bfcnt = 0;

  uint64_t *fo;
  uint64_t *bh;

  while(1) {
    rc = zmq_poll(ex_in->poll_items, ex_in->count, EX_POLL_TIMEOUT);
    for (i=0; i < ex_in->count; i++) {
      if (ex_in->poll_items[i].revents & ZMQ_POLLIN) {

        if (zmq_recv(ex_in->poll_items[i].socket, &buf, max_size, 0) == 0)
          continue;
        fo = &fos[bfcnt];
        bh = &bhs[bfcnt];
        bfcnt++;
        if (bfcnt >= ROLLINGBUFFERSIZE)
          bfcnt = 0;

        memcpy(fo, (void *)&buf[1], sizeof(FRAMEOBJECT));
        memcpy(bh, &buf[sizeof(FRAMEOBJECT)+1], sizeof(FRAMEHASH));

        // then store fo, bh into shared memory, with some adjusting and checkings
        // storing every second around 1000 msgs 16 bytes each. But leaking is only 200 bytes per minute.

      }
     }

  }
}

编辑2:

我终于让 valgrind 工作了——只使用部分数据(6GB),它终于通过了。并且没有发现任何泄漏。但是,在工作过程中它需要 100% 的 cpu,而且我的程序肯定没有处理所有传入的数据——它不能在满负荷下工作。这一半证实了我最后的猜测 - 泄漏是在数据交换块上。我找到有关 mtrace(libc 的一部分)的信息它帮助我跟踪泄漏的地址 - 它在我的代码之外,在一个线程中。我的代码中唯一的线程是由 zeromq 创建的。然后我开始使用套接字选项(增加hwm,缓冲区)并且泄漏速度降低,但即使在荒谬的大值上也没有完全消失:(

所以,现在我 95% 确定它的 zeromq 泄漏了。尝试在他们的邮件列表中找到答案。

4

1 回答 1

3

如果 valgrind 没有解决它 - 您可以尝试自己跟踪内存分配。

有两种方法 - 用您自己的 malloc 和 free 版本的调用替换您对 malloc 的调用,并将某种标识符传递给这些函数,例如FILELINE,或者您可以传入系统的名称,即分配。

在非内存泄漏检测模式下,您直接通过 malloc 和 free,而在内存泄漏检测模式下,您首先记录 alloc 和 free 调用,然后调用 malloc 和 free。当程序完成时,你匹配分配和释放,你会看到你在哪里泄漏内存。

您可以使用宏来执行此操作,因此您的常规构建不会减慢。

您不会从无法自己重新编译的客户端库中发现泄漏。

另一种方法是使用 gcc 的 -wrap 标志在链接时让 gcc 调用您的 malloc/free 版本而不是 glibc 版本。看到这个帖子:

在 C 中为 malloc 和 free 创建一个包装函数

这样做的好处是您也可以在客户端库中记录分配。缺点是您受限于相同的函数签名,因此您将无法在泄漏检查器中获取FILELINE

如果这是 C++,你可以重载全局操作符 new。

于 2013-06-04T14:52:57.203 回答