在MSVC中,DebugBreak()或__DebugBreak会导致调试器中断。在x86上,它相当于写"_asm int 3",在x64上则有所不同。当使用gcc(或任何其他标准编译器)进行编译时,我也想进入调试器。是否存在独立于平台的功能或内在功能?我看到了关于XCode的问题,但它似乎不够便携。
旁注:我主要想用它来实现ASSERT,我知道我可以使用ASSERT()来实现它,但我也想在代码中写入DEBUG_BREAK或其他东西。
可移植到大多数POSIX系统的方法是:
raise(SIGTRAP);
我刚刚向可移植代码段(可移植代码的公共域代码段的集合)添加了一个模块来实现这一点。它不是100%可移植的,但它应该非常健壮:
- 某些clang版本的
__builtin_debugtrap
(用__has_builtin(__builtin_debugtrap)
标识) - 关于MSVC与英特尔C/C++编译器
__debugbreak
- 适用于ARM C/C++编译器:
__breakpoint(42)
- 对于x86/x8_64,程序集:
int3
- 对于ARM Thumb,程序集:
.inst 0xde01
- 对于ARM AArch64,程序集:
.inst 0xd4200000
- 对于其他ARM,装配:
.inst 0xe7f001f0
- 对于Alpha,装配:
bpt
- 对于带有GCC(或伪装成GCC的东西)的非托管C,
__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推动的。
这看起来像是一个合适的compat库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步骤和debugbreak continue添加到GDB中。
如果您认为assert(x)
足够便携,那么assert(false)
似乎是您问题的明显便携解决方案。
如果您试图调试与崩溃相关的条件,那么在大多数平台上,老式的abort()会为您提供一个调用堆栈。不利的一面是你无法从当前的电脑继续,这可能是你无论如何都不想做的。
http://www.cplusplus.com/reference/cstdlib/abort/
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")
。
与其使用"正常"调试中断,不如使用以下方法之一,比如除以零:
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以中断循环并执行您的操作。此代码可能无法在优化的构建中工作!