是否存在与 DebugBreak()/__DebugBreak 等效的可移植性?

在 MSVC 中,DebugBreak ()调试破解会导致调试器中断。在 x86上它等于写“ _ asmint3”,在 x64上它是不同的。在使用 gcc (或任何其他标准编译器)进行编译时,我也想插入调试器。是否有独立于平台的功能或内在的功能?我看到了 XCode 问题,但它似乎不够便携。

旁注: 我主要想用它来实现 ASSERT,我知道我可以使用断言() ,但是我也想把 DEBUG _ BREAK 或其他东西写到代码中。

54118 次浏览

如何定义基于 # ifdef 的条件宏,该宏根据当前架构或平台扩展到不同的构造。

比如:

#ifdef _MSC_VER
#define DEBUG_BREAK __debugbreak()
#else
...
#endif

这将由预处理器根据编译代码的平台扩展正确的调试器中断指令。这样,您总是在代码中使用 DEBUG_BREAK

GCC 有一个名为 __builtin_trap的内置函数,您可以看到 给你,但是假设一旦到达这个值,代码执行就会停止。

应该确保 __builtin_trap()调用是有条件的,否则在它之后不会发出任何代码。

这个帖子由5分钟的测试,YMMV。

与其使用“正常”调试中断,为什么不使用以下方法之一,比如除以零:

int iCrash = 13 / 0;

或取消引用 NULL 指针:

BYTE bCrash = *(BYTE *)(NULL);

至少这在许多平台/架构上是可移植的。

在许多调试器中,您可以指定要对哪些异常执行哪些操作,以便在命中上述异常之一(如暂停执行,ala 一个“ int 3”指令)并生成异常时相应地执行操作。

如果你认为 assert(x)的便携性足够,assert(false)似乎是明显的便携式解决方案,你的问题。

对大多数 POSIX 系统可移植的一种方法是:

raise(SIGTRAP);

这看起来像是一个合适的 compat 库 https://github.com/scottt/debugbreak

如果您试图调试与崩溃相关的条件,那么在大多数平台上,传统的 abort ()会为您提供一个调用堆栈。缺点是你不能继续从当前的 PC,你可能不想这样做无论如何。

Http://www.cplusplus.com/reference/cstdlib/abort/

#define __debugbreak() \
do \
{       static bool b; \
while (!b) \
sleep(1); \
b = false; \
} while (false)

当进程处于睡眠状态时,您可以将调试器附加到进程,更改变量 b 以中断循环并执行您的操作。此代码可能无法在优化的构建中工作!

为此,我刚刚将 一个模块添加到 便携式代码片段(一组可移植代码的公共域片段)。它不是100% 便携,但应该相当强大:

  • 对于某些版本的 clang (用 __has_builtin(__builtin_debugtrap)标识) ,使用 __builtin_debugtrap
  • MSVC 与 Intel C/C + + 编译器: __debugbreak
  • 对于 ARM C/C + + 编译器: __breakpoint(42)
  • 对于 x86/x86 _ 64,装配: int3
  • 对于 ARM 拇指,组装: .inst 0xde01
  • 对于 ARM AArch64,装配: .inst 0xd4200000
  • 对于其他 ARM,组装: .inst 0xe7f001f0
  • 阿尔法,装配: bpt
  • 对于带有 GCC 的非宿主 C (或伪装成它的东西) ,__builtin_trap
  • 否则,包括 signal.h
    • 如果 defined(SIGTRAP)(即 POSIX) ,则为 raise(SIGTRAP)
    • 否则,raise(SIGABRT)

将来,可移植代码片段中的模块可能会扩展到包含其他逻辑,我可能会忘记更新这个答案,所以您应该在那里寻找更新。它是公共领域(CC0) ,所以你可以随意窃取代码。

对于这个问题,这似乎是一个非常好的便携式解决方案: Https://github.com/scottt/debugbreak

所引用的存储库中提供的头(debugbreak.h)封装了 MSVC 的

    __debugbreak,

还有

    __asm__ volatile("int $0x03");

在 i386和 x86 _ 64上实现,在 ARM 上实现

    __asm__ volatile(".inst 0xe7f001f0");

同时还记录了一些解决方案,解决了头部提到的单步执行 GDB 中的断点的问题,以及在 Stepi继续卡住的平台上扩展 GDB 的 Python 脚本。该脚本将 调试破解步骤调试中断-继续添加到 GDB。

对于使用 NRF Connect SDK 的 nRF9160,这些解决方案都不能正常工作。这是一个使用 arm-none-eabi-gcc编译器的 SEGGER 嵌入式 ARM 工作室(北欧版)环境。

其他答案中提到的 debug-trap.hdebugbreak.h__builtin_trap()都导致了“未定义的操作码”和硬故障(或调试监视器故障,但结果是相同的) ,并且没有有用的程序计数器、堆栈帧或其他可调试信息。

最后,这个替代品起作用了。我从其他一些神秘的北欧图书馆得到了它,在那里它被称为 NRF_BREAKPOINT:

#if defined(__GNUC__)
__asm__("BKPT 0");
#else
__BKPT(0)
#endif

在构建时,包含的是 __GNUC__路径,因此只需要 __asm__("BKPT 0")