是否有类似于DebugBreak()/__DebugBreak的可移植等价物



在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中单步通过断点的标头中指出的问题的方法,以及在stepicont陷入困境的平台上扩展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.hdebugbreak.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以中断循环并执行您的操作。此代码可能无法在优化的构建中工作!

相关内容

  • 没有找到相关文章

最新更新