使用 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
。)假设你可以让它工作,那可能表现得更像您期望的那样。
我正在尝试检测 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
。)假设你可以让它工作,那可能表现得更像您期望的那样。