在 MSVC 中,DebugBreak()或__debugbreak会导致调试器中断。在 x86 上它相当于写“_asm int 3”,在 x64 上它是不同的。使用 gcc(或任何其他标准编译器)编译时,我也想中断调试器。是否有独立于平台的功能或内在功能?我看到了关于这个的XCode 问题,但它似乎不够便携。
旁注:我主要想用它来实现 ASSERT,我知道我可以为此使用 assert(),但我也想在代码中写入 DEBUG_BREAK 或其他东西。
在 MSVC 中,DebugBreak()或__debugbreak会导致调试器中断。在 x86 上它相当于写“_asm int 3”,在 x64 上它是不同的。使用 gcc(或任何其他标准编译器)编译时,我也想中断调试器。是否有独立于平台的功能或内在功能?我看到了关于这个的XCode 问题,但它似乎不够便携。
旁注:我主要想用它来实现 ASSERT,我知道我可以为此使用 assert(),但我也想在代码中写入 DEBUG_BREAK 或其他东西。
一种可移植到大多数 POSIX 系统的方法是:
raise(SIGTRAP);
我刚刚向可移植代码片段(可移植代码的公共域片段集合)添加了一个模块来执行此操作。它不是 100% 可移植的,但它应该非常健壮:
__builtin_debugtrap
对于某些版本的 clang(用 标识__has_builtin(__builtin_debugtrap)
)__debugbreak
__breakpoint(42)
int3
.inst 0xde01
.inst 0xd4200000
.inst 0xe7f001f0
bpt
__builtin_trap
signal.h
和
defined(SIGTRAP)
(即 POSIX),raise(SIGTRAP)
raise(SIGABRT)
将来可移植片段中的模块可能会扩展到包含其他逻辑,我可能会忘记更新这个答案,所以你应该在那里寻找更新。它是公共领域(CC0),所以请随意窃取代码。
如何定义一个基于#ifdef 的条件宏,该宏扩展为基于当前架构或平台的不同结构。
就像是:
#ifdef _MSC_VER
#define DEBUG_BREAK __debugbreak()
#else
...
#endif
这将由预处理器根据编译代码的平台扩展正确的调试器中断指令。这种方式你总是DEBUG_BREAK
在你的代码中使用。
GCC 有一个名为的内置函数__builtin_trap
,您可以在此处看到,但是假定一旦达到此代码执行就会停止。
你应该确保__builtin_trap()
调用是有条件的,否则在它之后不会发出任何代码。
这篇文章由所有 5 分钟的测试推动,YMMV。
这看起来像一个合适的兼容库https://github.com/scottt/debugbreak
对于这个问题,这似乎是一个非常好的、可移植的解决方案: https ://github.com/scottt/debugbreak
引用的存储库 (debugbreak.h) 中提供的标头封装了 MSVC 的
__debugbreak,
和
__asm__ volatile("int $0x03");
在 i386 和 x86_64 上,在 ARM 上实现
__asm__ volatile(".inst 0xe7f001f0");
以及记录标题中指出的问题的一些解决方法,用于单步越过 GDB 中的断点,以及用于在stepi或cont卡住的平台上扩展 GDB 的 Python 脚本。该脚本将debugbreak-step和debugbreak-continue添加到 GDB。
如果您认为assert(x)
便携性足够,assert(false)
似乎是解决您问题的明显便携解决方案。
FWIW,这些解决方案都不适用于使用 NRF Connect SDK 的 nRF9160。那是一个 SEGGER Embedded Studio for ARM (Nordic Edition) 环境,使用arm-none-eabi-gcc
编译器。
,和其他答案debug-trap.h
中提到的都导致“未定义的操作码”和硬故障(或调试监视器故障,但结果相同),并且没有有用的程序计数器、堆栈帧或其他可调试信息。debugbreak.h
__builtin_trap()
最后,这个替代方案确实奏效了。我从其他一些神秘的北欧图书馆派生出来,它被称为NRF_BREAKPOINT
:
#if defined(__GNUC__)
__asm__("BKPT 0");
#else
__BKPT(0)
#endif
在构建时,它是__GNUC__
包含在内的路径,因此__asm__("BKPT 0")
也是必需的。
如果您尝试调试与崩溃相关的情况,老式的 abort() 将在大多数平台上为您提供调用堆栈。缺点是您无法从当前的 PC 继续,您可能无论如何都不想这样做。
与其使用“正常”调试中断,不如使用以下方法之一,例如除以零:
int iCrash = 13 / 0;
或取消引用 NULL 指针:
BYTE bCrash = *(BYTE *)(NULL);
至少这在许多平台/架构上是可移植的。
在许多调试器中,您可以指定要对哪些异常执行什么操作,以便在遇到上述任何一项(如暂停执行,ala“int 3”指令)并生成异常时采取相应措施。
#define __debugbreak() \
do \
{ static bool b; \
while (!b) \
sleep(1); \
b = false; \
} while (false)
当进程处于睡眠状态时,您可以将调试器附加到进程,更改变量 b 以中断循环并执行您的操作。此代码可能无法在优化的构建中运行!