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

11

89

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.

Haem answered 6/10, 2008 at 8:57 Comment(0)
G
20

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.

Galoot answered 6/10, 2008 at 9:5 Comment(0)
F
69

A method that is portable to most POSIX systems is:

raise(SIGTRAP);
Fye answered 6/4, 2011 at 3:7 Comment(6)
raise(SIGTRAP) worked perfectly for me on gcc/Linux. __builtin_trap() caused a SIGILL signal to be raised.Ednaedny
Can this be used on OSX? I tried this in Xcode 6.1 I it said SIGTRAP was an undeclared identifier.Deductive
@thomthom: Did you #include <signal.h> ?Fye
No - I had missed that. Figured that one out later. Forgot to delete my comment.Deductive
Works for me: for iOS, macOS, tvOS and Android.Ferrand
It stops and resets the target on ARM with newlib-nano.Juice
L
45

I just added a module to portable-snippets (a collection of public domain snippets of portable code) to do this. It's not 100% portable, but it should be pretty robust:

  • __builtin_debugtrap for some versions of clang (identified with __has_builtin(__builtin_debugtrap))
  • On MSVC and Intel C/C++ Compiler: __debugbreak
  • For ARM C/C++ Compiler: __breakpoint(42)
  • For x86/x86_64, assembly: int3
  • For ARM Thumb, assembly: .inst 0xde01
  • For ARM AArch64, assembly: .inst 0xd4200000
  • For other ARM, assembly: .inst 0xe7f001f0
  • For Alpha, assembly: bpt
  • For non-hosted C with GCC (or something which masquerades as it), __builtin_trap
  • Otherwise, include signal.h and
    • If defined(SIGTRAP) (i.e., POSIX), raise(SIGTRAP)
    • Otherwise, raise(SIGABRT)

In the future the module in portable-snippets may expand to include other logic and I'll probably forget to update this answer, so you should look there for updates. It's public domain (CC0), so feel free to steal the code.

Latticework answered 2/3, 2018 at 23:47 Comment(7)
For x86 (including x86-64) GAS syntax, it's better to write int3 to make it explicit that you want the special case debug-break instruction, one byte CC not CD 03, for the rare cases where that matter (code size, and v8086 mode). (felixcloutier.com/x86/intn:into:int3:int1). With NASM they actually assemble differently, GAS optimizes both to int3.Wenda
__builtin_trap typically compiles to ud2 (x86) or other illegal instruction, not a debug breakpoint, and is also treated noreturn you can't continue after it even with a debugger. It does not belong in this list. e.g. there's no ret instruction after the ud2 in a simple function that uses it before a C return x statement.Wenda
Thanks @PeterCordes, I have updated both this answer and my code to use int3. FWIW, both GCC and clang generate int3, too (at least with -O3), which is what really matters here since it's about C++ not assembly. It sounds like int3 is more correct, though, so no reason not to "fix" it :)Latticework
For __debug_trap, I'm not sure there is really anything that can be done here. In both the comment and the linked code it is deep in fallback territory, only called if everything else has failed and it's a non-hosted environment (in which case signal.h won't be available). AFAICT alternative is either nothing or a compile-time error. If you have a suggestion about other possible alternatives I'd certainly be open; I agree that it's sub-optimal (hence its position as the last resort).Latticework
-O3 should be irrelevant, even for clang's built-in assembler. That's the optimization level in translating C++ to asm. Asm to machine code (including for asm coming from an asm("") template string) is a truly separate process for gcc, and logically separate for clang. But yes, int3 is a good idea; that's how 0xCC disassembles, and it's a more accurate representation of what you want.Wenda
Clang __debug_trap should be fine. The 2nd comment was about GNU C __builtin_trap. Depending on the use case (whether you want to be able to continue after), a compile time error like #error "FIXME: implement psnip_trap with inline asm for your ISA" would be more appropriate than an illegal instruction that compilers treat as noreturn. Maybe you'd want fallback to GNUC __builtin_trap to be optional, controlled by a macro that can be defined, perhaps on by default if the intended use-case is for something like assert.Wenda
Did you have a look at __asm__("bkpt #0"); / __asm__("brk #0"); / __asm__("BKPT 0")? If my quick reading on those are correct they are reasonable for ARM and some "embedded" chips.Langur
G
20

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.

Galoot answered 6/10, 2008 at 9:5 Comment(0)
L
15

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.

Liken answered 6/10, 2008 at 9:19 Comment(1)
__builtin_trap typically compiles to ud2 (x86) or other illegal instruction, not a debug breakpoint, and is also treated noreturn you can't continue after it even with a debugger.Wenda
E
15

This looks like an appropriate compat library https://github.com/scottt/debugbreak

Exenterate answered 3/10, 2013 at 8:26 Comment(0)
E
6

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.

Espalier answered 2/8, 2019 at 17:17 Comment(0)
A
3

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

Agincourt answered 6/10, 2008 at 11:18 Comment(2)
Good in most cases but not so helpful in release code. Yeah, sometimes I have to debug release code...Lucilelucilia
assert is not a suitable solution at all since it typically does not allow program to continue executing.Pepsin
C
0

If you are trying to debug a crash-related condition, good old fashioned abort() will give you a call stack on most platforms. Downside is that you can't continue from the current PC, which you probably don't want to do anyway.

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

Caffeine answered 26/12, 2013 at 22:33 Comment(0)
A
0

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.

Affer answered 12/5, 2021 at 22:17 Comment(0)
P
-7

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.

Poem answered 6/10, 2008 at 9:21 Comment(4)
I actually have a board here that will happily do a NULL pointer dereference. divide by zero may be safer.Liken
Interesting. How would continue from such exception when it hits? With int 3 the VS debugger knows how to continue, all I need is to press Go (F5), or if I want to disable the assert on that location, I can use the trick stackoverflow.com/questions/115237 - anything similar here?Agincourt
Dereferencing NULL (== 0) is not actually an error on most embedded systems, since address 0 is usually a real memory location. On an ARM core, it's the vector table.Altruism
DO AVOID THIS SOLUTION METHOD. It's an incredible security risk, it leaves the stack in an inconsistent state and depending on the application it can be used for exploits!Getty
S
-8
#define __debugbreak() \
do \
{       static bool b; \
        while (!b) \
                sleep(1); \
        b = false; \
} while (false)

When the process is sleeping, you can attach a debugger to the process, change the variable b to break the loop and do your thing. This code might not work in an optimized build!

Springtime answered 3/12, 2015 at 19:54 Comment(1)
this is the only solution which allows one to attach a debugger to the process blocked in the debugbreak() -- the rest of the solutions all cause the program to abort.Villanueva

© 2022 - 2024 — McMap. All rights reserved.