Is there a portable equivalent to DebugBreak()/__debugbreak?

Viewed 52596

In MSVC, DebugBreak() or __debugbreak cause a debugger to break. On x86 it is equivalent to writing "_asm int 3", on x64 it is something different. When compiling with gcc (or any other standard compiler) I want to do a break into debugger, too. Is there a platform independent function or intrinsic? I saw the XCode question about that, but it doesn't seem portable enough.

Sidenote: I mainly want to implement ASSERT with that, and I understand I can use assert() for that, but I also want to write DEBUG_BREAK or something into the code.

11 Answers

What about defining a conditional macro based on #ifdef that expands to different constructs based on the current architecture or platform.

Something like:

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

This would be expanded by the preprocessor the correct debugger break instruction based on the platform where the code is compiled. This way you always use DEBUG_BREAK in your code.

GCC has a builtin function named __builtin_trap which you can see here, however it is assumed that code execution halts once this is reached.

you should ensure that the __builtin_trap() call is conditional, otherwise no code will be emitted after it.

this post fueled by all of 5 minutes of testing, YMMV.

This seems to be a very good, portable solution to this question: https://github.com/scottt/debugbreak

The header provided in the repository cited (debugbreak.h) encapsulates MSVC's

    __debugbreak, 

and

    __asm__ volatile("int $0x03");

on i386 and x86_64, and on ARM it implements

    __asm__ volatile(".inst 0xe7f001f0");

as well as documenting some workarounds for problems noted in the header for single-stepping past the breakpoint in GDB plus a Python script for extending GDB on those platforms where stepi or cont get stuck. The script adds debugbreak-step and debugbreak-continue to GDB.

If you consider assert(x) portable enough, assert(false) seems to be the obvious portable solution to your problem.

FWIW, none of these solutions worked on a nRF9160 using the NRF Connect SDK. That's a SEGGER Embedded Studio for ARM (Nordic Edition) environment, using the arm-none-eabi-gcc compiler.

The debug-trap.h, debugbreak.h and __builtin_trap() mentioned in other answers all resulted in "undefined opcode" and a hard-fault (or a debug monitor fault, but the result is the same) and there no useful program counter, stack frame or other debuggable information.

In the end, this alternate did work. I derived it from some other mysterious Nordic library, where it is referred to as NRF_BREAKPOINT:

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

At build time, it is the __GNUC__ path which gets included, so __asm__("BKPT 0") is all that is required.

Instead of using 'normal' debug breaks, why not use one of the following, like a divide by zero:

int iCrash = 13 / 0;

or dereference a NULL pointer:

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

At least this is portable accross many platforms/architectures.

In many debuggers you can specify what action you want to perform on what exceptions so you can act accordingly when one of the above is hit (like pause execution, ala an "int 3" instruction) and an exception is generated.

Related