使用 Instruments 检测 C/C++ 命令行内存泄漏

OzB*_*OzB 5 c xcode valgrind memory-leaks xcode-instruments

我正在尝试检测 macOS 上 C(和 C++)程序中的内存泄漏。在 Linux 和 Windows 中,我可以使用 轻松完成此操作valgrind,但不幸的是,它在 macOS 上不可用。

由于我有 ObjC 和 iOS 开发的背景经验,我想使用 Instruments 来进行内存泄漏检查。乍一看,这听起来非常适合这项工作。

我写了这个非常简单的泄露程序:

#include <stdlib.h>
#include <stdio.h>

int* allocSomething() {
  return malloc(sizeof(int));
}

int main(int argc, const char * argv[]) {
  int* p = allocSomething();
  *p = 5;
  printf("*p = %d\n", *p);
  p = NULL;
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

我通过 Clang Static Analyzer 运行了它,它完成了这项工作,但我希望它也能被 Instruments 捕获,因为我正在寻找合适的 Valgrind 替代品。因此:

  • 我更改了配置文件架构以使用调试而不是发布。
  • 我确保没有优化。

然而,使用 Instruments 后: 仪器未检测到泄漏

正如您所看到的,没有泄漏报告。在网上搜索后,我遇到了Can't detector C Leaks in xcode 9 Instruments,其中作者使用了sleep,所以我想也许 Instruments 实际上并没有malloc像 Valgrind 一样覆盖,而是使用了采样技术,并且它没有采样它在这么短的时间内,所以我将程序更改为:

int main(int argc, const char * argv[]) {
  int* p = allocSomething();
  p = NULL;
  sleep(600000);
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

现在,我得到: 完全没有泄漏

这完全没有意义,因为这是明显的内存泄漏。我想说它必须进行一些优化,但我再次明确禁用了它。此外,如果我malloc有更多字节,它确实会检测到它。或者也许这是仪器中的一个错误?

所以我想知道这是否是仪器无法检测小分配的问题?我必须指出,Valgrind 可以很好地处理它,所以我很惊讶。

你有什么建议吗?

Ken*_*ses 8

Leaks 工具(Leaks 模板中的工具之一)的工作方式与您的预期不同。

首先,为什么它没有检测到任何短期进程:它是基于计时器的。它经常停止该过程并检查是否有泄漏。对于短期进程,在进程退出之前不会进行检查。

其次,为什么它会错过一些泄漏:它检查所有线程的堆栈、所有线程的寄存器以及查找分配地址的全局变量。如果在任何这些地方找到分配的地址,则该分配不被视为已泄漏。

在您的情况下,分配的地址可能仍在寄存器或堆栈内存中。在“真实”程序中,堆栈和寄存器最终将被重用,并且此类陈旧数据将被消除。

分配工具确实跟踪所有分配和解除分配(假设该过程是由工具启动的)。您泄漏的分配位于分配列表中,并且仍列为“活动”(又名“创建和持久”)。问题在于分配工具没有明确将此类分配称为泄漏。

此外,系统库还进行了分配,这些分配旨在在进程退出之前一直存在,并且不会显式清除。所以,你的泄漏有点被不相关的信息所掩盖。您可以对分配列表进行过滤和排序以发现泄漏,但这需要一些工作。

在更真实的程序中,Instruments 非常擅长查找泄漏。

显然,Clang 支持LeakSanitizer,无论是作为 AddressSanitizer 的一部分还是独立的。这可能需要比 Apple 作为 Xcode 和/或 Catalina 的一部分提供的版本更新的 Clang 版本。(我在 Mojave 上使用 Xcode 11.3.1 进行了测试。它不支持该-fsanitize=leak选项,也ASAN_OPTIONS=detect_leaks=1不受支持。)假设您可以让它工作,那么它的行为可能更像您所期望的。