EXC_BAD_ACCESS 但 NSZombies 从未触发,如何调试?

EXC_BAD_ACCESS but NSZombies never triggered, how to debug?

我们有一些非常随机的错误发生并抛出 EXC_BAD_ACCESS,或 malloc_error_break,或 abort。异常没有一致性,启用 NSZombies 不会导致任何僵尸被触发。

事实上,运行 僵尸启用导致崩溃永远不会发生。我相信这个代码库中存在一个微妙的内存错误,在花了很多时间清理可能是小问题之后,我们仍然没有解决这个问题。

错误的指针可能会覆盖一段内存,然后取消引用并导致应用程序崩溃,这是有道理的。但是还有什么其他方法可以隔离根本问题呢?

我们已经使用了所有诊断内存工具,这些工具也将 运行 连接到设备(此应用程序使用外围设备,因此无法在模拟器中完全调试)。

NSZombie 只是一种毒化对象使用的 space 而不是释放它们的机制,类似于 Address Sanitizer's Memory Poisoning。通过使用各种工具,例如 NSZombie 或上述 ASAN,您的堆栈和堆分配将以不同方式布局,导致在崩溃可能是最佳情况的情况下出现未定义的行为。

EXC_BAD_ACCESS 表示您试图访问无效地址或试图读取或写入您没有此类权限的内存区域。您遇到的不一致 运行 很可能是堆栈或堆损坏的严重后果,例如有时会覆盖仅保存数据的变量,有时会覆盖程序正在使用的指针。

数据布局对于发生的事情很重要,堆布局在 non-debug 构建中通常是随机的,这为不一致的崩溃增加了更多空间。此外,对程序源代码或构建设置的任何更改 may/will 都不可避免地会导致数据布局发生变化。

我会推荐:

  1. 在调试模式下构建(-g 编译器标志)并 运行 附加调试器。当你遇到崩溃时,gdblldb(后者是 Xcode 工具的默认设置)将停止执行并让你做一些事情,从那里使用 [=16 获得堆栈跟踪=] 这可能会让您找出问题的更深层原因。
  2. 使用 ASAN,this page explains about its usage within Xcode tooling。它通常是处理内存问题的绝佳工具。请注意,将它与不支持它的共享库一起使用可能会导致异常,但它通常会告诉您这些异常,并且通常会尽可能多地握住您的手。
  3. "printf debugging" 可以提供帮助,例如 #define TRACE printf("%s %s:%d\n", __func__, __FILE__, __LINE__); 并将它们分散在可能的问题点上实际上会有所帮助。

一般来说,我建议先使用调试器,不使用 NSZombie 或其他任何东西,只需执行一堆 运行s 直到崩溃点并获取堆栈跟踪、寄存器状态等. 在多次崩溃中获取这些示例可以帮助您缩小问题范围。