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

Detect C/C++ command line memory leaks using 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;
}

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

然而,在使用 Instruments 之后:

如您所见,没有泄漏报告。 在网上搜索后,我遇到了,其中作者使用了sleep,所以我想也许Instruments实际上并没有像Valgrind那样覆盖malloc,而是使用了采样技术,并且它不会在这么短的时间内对其进行采样,所以我将程序更改为:

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

现在,我得到:

这完全没有意义,因为这是明显的内存泄漏。 我会说它必须做一些优化,但后来我又明确地禁用了它。此外,如果我 malloc 更多字节,它确实会检测到它。或者它可能是 Instruments 中的错误?

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

你有什么建议吗?

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

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

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

在你的情况下,分配的地址可能仍在寄存器或堆栈内存中。在 "real" 程序中,堆栈和寄存器最终将被重用,这些陈旧数据将被消除。

Allocations 工具确实跟踪所有分配和解除分配(假设该过程由 Instruments 启动)。您泄漏的分配在分配列表中,并且仍列为 "live"(a.k.a。已创建并持久存在)。问题是 Allocations 工具没有明确指出这种分配是泄漏。

此外,系统库还进行了分配,这些分配旨在在进程退出之前一直存在,并且不会明确清除。因此,您的泄漏有点隐藏在不相关的信息中。您可以过滤和排序分配列表以发现漏洞,但这需要一些工作。

在更真实的程序的上下文中,Instruments 非常擅长发现漏洞。

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