├── .gitignore ├── COPYING ├── GNUmakefile ├── HOW-TO-USE-DEBUGBREAK-GDB-PY.md ├── README.md ├── debugbreak-gdb.py ├── debugbreak.h └── test ├── break-c++.cc ├── break.c ├── break.gdb ├── fib.c ├── fib.gdb ├── test-debugbreak.gdb ├── trap.c └── trap.gdb /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | fib 3 | core 4 | core.* 5 | cscope.out 6 | tags 7 | .gdb_history 8 | test/trap 9 | test/break 10 | test/break-c++ 11 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011-2016, Scott Tsai 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 21 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /GNUmakefile: -------------------------------------------------------------------------------- 1 | CFLAGS := -Os -Wall -g 2 | CXXFLAGS := $(CFLAGS) 3 | 4 | PROGRAMS := $(basename $(wildcard *.c test/*.c test/*.cc *.S)) 5 | 6 | .PHONY: all clean 7 | all: $(PROGRAMS) 8 | clean: 9 | rm -f $(PROGRAMS) cscope.out tags 10 | 11 | %: %.S 12 | $(CC) $(CFLAGS) -nostdlib $< -o $@ 13 | 14 | # Not using builtin rules due to debugbreak.h dependency 15 | %: %.c 16 | $(CC) $(CFLAGS) $(LDFLAGS) $< -o $@ 17 | 18 | %: %.cc 19 | $(CXX) $(CXXFLAGS) $(LDFLAGS) $< -o $@ 20 | 21 | test/%: CFLAGS +=-I. 22 | test/%: CXXFLAGS +=-I. 23 | $(PROGRAMS): debugbreak.h 24 | 25 | GDB ?= gdb 26 | .PHONY: gdb 27 | gdb: 28 | $(GDB) -q -x debugbreak-gdb.py 29 | -------------------------------------------------------------------------------- /HOW-TO-USE-DEBUGBREAK-GDB-PY.md: -------------------------------------------------------------------------------- 1 | # How to use `debugbreak-gdb.py` 2 | 3 | Just add `-x debugbreak-gdb.py` to your usual GDB invocation or add `source debugbreak-gdb.py` to your `$HOME/.gdbinit`. 4 | 5 | Here's a sample session: 6 | 7 | ``` 8 | $ cd debugbreak 9 | $ make 10 | $ gdb -q -x debugbreak-gdb.py test/break 11 | Reading symbols from test/break...done. 12 | 13 | (gdb) set disassemble-next-line 1 14 | (gdb) run 15 | Starting program: /home/fedora/debugbreak/test/break 16 | 17 | Program received signal SIGTRAP, Trace/breakpoint trap. 18 | main () at test/break.c:6 19 | 6 debug_break(); 20 | 21 | Program received signal SIGTRAP, Trace/breakpoint trap. 22 | main () at test/break.c:6 23 | 6 debug_break(); 24 | (gdb) debugbreak-step 25 | 7 printf("hello world\n"); 26 | (gdb) debugbreak-continue 27 | hello world 28 | [Inferior 1 (process 12533) exited normally] 29 | (gdb) 30 | 31 | ``` 32 | 33 | On ARM and POWER, trying to use `step` or `stepi` in place of `debugbreak-step` in the sesion above wouldn't have worked as execution would be stock on the breakpoint instruction. 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Debug Break 2 | 3 | [debugbreak.h](https://github.com/scottt/debugbreak/blob/master/debugbreak.h) allows you to put breakpoints in your C/C++ code with a call to **debug_break()**: 4 | ```C 5 | #include 6 | #include "debugbreak.h" 7 | 8 | int main() 9 | { 10 | debug_break(); /* will break into debugger */ 11 | printf("hello world\n"); 12 | return 0; 13 | } 14 | ``` 15 | * Include one header file and insert calls to `debug_break()` in the code where you wish to break into the debugger. 16 | * Supports GCC, Clang and MSVC. 17 | * Works well on ARM, AArch64, i686, x86-64, POWER and has a fallback code path for other architectures. 18 | * Works like the **DebugBreak()** fuction provided by [Windows](http://msdn.microsoft.com/en-us/library/ea9yy3ey.aspx) and [QNX](http://www.qnx.com/developers/docs/6.3.0SP3/neutrino/lib_ref/d/debugbreak.html). 19 | 20 | **License**: the very permissive [2-Clause BSD](https://github.com/scottt/debugbreak/blob/master/COPYING). 21 | 22 | Known Problem: if continuing execution after a debugbreak breakpoint hit doesn't work (e.g. on ARM or POWER), see [HOW-TO-USE-DEBUGBREAK-GDB-PY.md](HOW-TO-USE-DEBUGBREAK-GDB-PY.md) for a workaround. 23 | 24 | Implementation Notes 25 | ================================ 26 | 27 | The requirements for the **debug_break()** function are: 28 | * Act as a compiler code motion barrier 29 | * Don't cause the compiler optimizers to think the code following it can be removed 30 | * Trigger a software breakpoint hit when executed (e.g. **SIGTRAP** on Linux) 31 | * GDB commands like **continue**, **next**, **step**, **stepi** must work after a **debug_break()** hit 32 | 33 | Ideally, both GCC and Clang would provide a **__builtin_debugtrap()** that satisfies the above on all architectures and operating systems. Unfortunately, that is not the case (yet). 34 | GCC's [__builtin_trap()](http://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html#index-g_t_005f_005fbuiltin_005ftrap-3278) causes the optimizers to think the code follwing can be removed ([test/trap.c](https://github.com/scottt/debugbreak/blob/master/test/trap.c)): 35 | ```C 36 | #include 37 | 38 | int main() 39 | { 40 | __builtin_trap(); 41 | printf("hello world\n"); 42 | return 0; 43 | } 44 | ``` 45 | compiles to: 46 | ``` 47 | main 48 | 0x0000000000400390 <+0>: 0f 0b ud2 49 | ``` 50 | Notice how the call to `printf()` is not present in the assembly output. 51 | 52 | Further, on i386 / x86-64 **__builtin_trap()** generates an **ud2** instruction which triggers **SIGILL** instead of **SIGTRAP**. This makes it necessary to change GDB's default behavior on **SIGILL** to not terminate the process being debugged: 53 | ``` 54 | (gdb) handle SIGILL stop nopass 55 | ``` 56 | Even after this, continuing execution in GDB doesn't work well on some GCC, GDB combinations. See [GCC Bugzilla 84595](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84595). 57 | 58 | On ARM, **__builtin_trap()** generates a call to **abort()**, making it even less suitable. 59 | 60 | **debug_break()** generates an **int3** instruction on i386 / x86-64 ([test/break.c](https://github.com/scottt/debugbreak/blob/master/test/break.c)): 61 | ```C 62 | #include 63 | #include "debugbreak.h" 64 | 65 | int main() 66 | { 67 | debug_break(); 68 | printf("hello world\n"); 69 | return 0; 70 | } 71 | ``` 72 | compiles to: 73 | ``` 74 | main 75 | 0x00000000004003d0 <+0>: 50 push %rax 76 | 0x00000000004003d1 <+1>: cc int3 77 | 0x00000000004003d2 <+2>: bf a0 05 40 00 mov $0x4005a0,%edi 78 | 0x00000000004003d7 <+7>: e8 d4 ff ff ff callq 0x4003b0 79 | 0x00000000004003dc <+12>: 31 c0 xor %eax,%eax 80 | 0x00000000004003de <+14>: 5a pop %rdx 81 | 0x00000000004003df <+15>: c3 retq 82 | ``` 83 | which correctly trigges **SIGTRAP** and single-stepping in GDB after a **debug_break()** hit works well. 84 | 85 | Clang / LLVM also has a **__builtin_trap()** that generates **ud2** but further provides **__builtin_debugtrap()** that generates **int3** on i386 / x86-64 ([original LLVM intrinsic](http://lists.llvm.org/pipermail/llvm-commits/Week-of-Mon-20120507/142621.html), [further fixes](https://reviews.llvm.org/rL166300#96cef7d3), [Clang builtin support](https://reviews.llvm.org/rL166298)). 86 | 87 | On ARM, **debug_break()** generates **.inst 0xe7f001f0** in ARM mode and **.inst 0xde01** in Thumb mode which correctly triggers **SIGTRAP** on Linux. Unfortunately, stepping in GDB after a **debug_break()** hit doesn't work and requires a workaround like: 88 | ``` 89 | (gdb) set $l = 2 90 | (gdb) tbreak *($pc + $l) 91 | (gdb) jump *($pc + $l) 92 | (gdb) # Change $l from 2 to 4 for ARM mode 93 | ``` 94 | to jump over the instruction. 95 | A new GDB command, **debugbreak-step**, is defined in [debugbreak-gdb.py](https://github.com/scottt/debugbreak/blob/master/debugbreak-gdb.py) to automate the above. See [HOW-TO-USE-DEBUGBREAK-GDB-PY.md](HOW-TO-USE-DEBUGBREAK-GDB-PY.md) for sample usage. 96 | ``` 97 | $ arm-none-linux-gnueabi-gdb -x debugbreak-gdb.py test/break-c++ 98 | <...> 99 | (gdb) run 100 | Program received signal SIGTRAP, Trace/breakpoint trap. 101 | main () at test/break-c++.cc:6 102 | 6 debug_break(); 103 | 104 | (gdb) debugbreak-step 105 | 106 | 7 std::cout << "hello, world\n"; 107 | ``` 108 | 109 | On AArch64, **debug_break()** generates **.inst 0xd4200000**. 110 | 111 | See table below for the behavior of **debug_break()** on other architecturs. 112 | 113 | Behavior on Different Architectures 114 | ---------------- 115 | 116 | | Architecture | debug_break() | 117 | | ------------- | ------------- | 118 | | x86/x86-64 | `int3` | 119 | | ARM mode, 32-bit | `.inst 0xe7f001f0` | 120 | | Thumb mode, 32-bit | `.inst 0xde01` | 121 | | AArch64, ARMv8 | `.inst 0xd4200000` | 122 | | POWER | `.4byte 0x7d821008` | 123 | | RISC-V | `.4byte 0x00100073` | 124 | | MSVC compiler | `__debugbreak` | 125 | | Apple compiler on AArch64 | `__builtin_trap()` | 126 | | Otherwise | `raise(SIGTRAP)` | 127 | 128 | -------------------------------------------------------------------------------- /debugbreak-gdb.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2013, Scott Tsai 2 | # 3 | # All rights reserved. 4 | # 5 | # Redistribution and use in source and binary forms, with or without 6 | # modification, are permitted provided that the following conditions are met: 7 | # 8 | # 1. Redistributions of source code must retain the above copyright notice, 9 | # this list of conditions and the following disclaimer. 10 | # 2. Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | # 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 18 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | # POSSIBILITY OF SUCH DAMAGE. 25 | 26 | 27 | # Usage: gdb -x debugbreak-gdb.py 28 | # (gdb) debugbreak-step 29 | # (gdb) debugbreak-continue 30 | # 31 | # To debug: 32 | # (gdb) set python print-stack full 33 | 34 | import gdb 35 | import re 36 | 37 | def _gdb_show_version_parse(version_str): 38 | ''' 39 | >>> s0 = 'This GDB was configured as "x86_64-redhat-linux-gnu".' 40 | >>> s1 = 'This GDB was configured as "--host=i686-build_pc-linux-gnu --target=arm-linux-gnueabihf".' 41 | >>> s2 = 'This GDB was configured as "x86_64-unknown-linux-gnu".' 42 | >>> _gdb_show_version_parse(s0) == dict(target='x86_64-redhat-linux-gnu') 43 | True 44 | >>> _gdb_show_version_parse(s1) == dict(host='i686-build_pc-linux-gnu', target='arm-linux-gnueabihf') 45 | True 46 | >>> _gdb_show_version_parse(s2) == dict(target='x86_64-unknown-linux-gnu') 47 | True 48 | ''' 49 | 50 | t = version_str 51 | msg = 'This GDB was configured as "' 52 | s = t.find(msg) 53 | if s == -1: 54 | raise ValueError 55 | s += len(msg) 56 | e = t.find('".', s) 57 | if e == -1: 58 | raise ValueError 59 | 60 | config = t[s:e] 61 | d = {} 62 | for i in config.split(): 63 | i = i.strip() 64 | if i.startswith('--'): 65 | (k, v) = i[2:].split('=') 66 | d[k] = v 67 | else: 68 | if not i: 69 | continue 70 | d['target'] = i 71 | return d 72 | 73 | def _target_triplet(): 74 | ''' 75 | -> 'arm-linux-gnueabihf' or 'x86_64-redhat-linux-gnu' or ... 76 | 77 | >>> import re 78 | >>> not not re.match(r'\w*-\w*-\w*', target_triplet()) 79 | True 80 | ''' 81 | t = gdb.execute('show version', to_string=True) 82 | return _gdb_show_version_parse(t)['target'] 83 | 84 | temp_breakpoint_num = None 85 | 86 | def on_stop_event(e): 87 | global temp_breakpoint_num 88 | if not isinstance(e, gdb.BreakpointEvent): 89 | return 90 | for bp in e.breakpoints: 91 | if bp.number == temp_breakpoint_num: 92 | bp.delete() 93 | gdb.events.stop.disconnect(on_stop_event) 94 | l = gdb.find_pc_line(int(gdb.parse_and_eval('$pc'))).line 95 | gdb.execute('list %d, %d' % (l, l)) 96 | break 97 | 98 | def _next_instn_jump_len(gdb_frame): 99 | '-> None means don\'t jump' 100 | try: 101 | arch_name = gdb_frame.architecture().name() 102 | except AttributeError: 103 | arch_name = None 104 | 105 | if arch_name.startswith('powerpc:'): 106 | # 'powerpc:common64' on ppc64 big endian 107 | i = bytes(gdb.selected_inferior().read_memory(gdb.parse_and_eval('$pc'), 4)) 108 | if (i == b'\x7d\x82\x10\x08') or (i == b'\x08\x10\x82\x7d'): 109 | return 4 110 | else: # not stopped on a breakpoint instruction 111 | return None 112 | 113 | triplet = _target_triplet() 114 | if re.match(r'^arm-', triplet): 115 | i = bytes(gdb.selected_inferior().read_memory(gdb.parse_and_eval('$pc'), 4)) 116 | if i == b'\xf0\x01\xf0\xe7': 117 | return 4 118 | elif i.startswith(b'\x01\xde'): 119 | return 2 120 | elif i == b'\xf0\xf7\x00\xa0 ': 121 | # 'arm_linux_thumb2_le_breakpoint' from arm-linux-tdep.c in GDB 122 | return 4 123 | else: # not stopped on a breakpoint instruction 124 | return None 125 | return None 126 | 127 | def _debugbreak_step(): 128 | global temp_breakpoint_num 129 | try: 130 | frame = gdb.selected_frame() 131 | except gdb.error as e: 132 | # 'No frame is currently selected.' 133 | gdb.write(e.args[0] + '\n', gdb.STDERR) 134 | return 135 | instn_len = _next_instn_jump_len(frame) 136 | 137 | if instn_len is None: 138 | gdb.execute('stepi') 139 | else: 140 | loc = '*($pc + %d)' % (instn_len,) 141 | bp = gdb.Breakpoint(loc, gdb.BP_BREAKPOINT, internal=True) 142 | bp.silent = True 143 | temp_breakpoint_num = bp.number 144 | gdb.events.stop.connect(on_stop_event) 145 | gdb.execute('jump ' + loc) 146 | 147 | def _debugbreak_continue(): 148 | try: 149 | frame = gdb.selected_frame() 150 | except gdb.error as e: 151 | # 'No frame is currently selected.' 152 | gdb.write(e.args[0] + '\n', gdb.STDERR) 153 | return 154 | instn_len = _next_instn_jump_len(frame) 155 | 156 | if instn_len is None: 157 | gdb.execute('continue') 158 | else: 159 | loc = '*($pc + %d)' % (instn_len,) 160 | gdb.execute('jump ' + loc) 161 | 162 | class _DebugBreakStep(gdb.Command): 163 | '''Usage: debugbreak-step 164 | Step one instruction after a debug_break() breakpoint hit''' 165 | 166 | def __init__(self): 167 | gdb.Command.__init__(self, 'debugbreak-step', gdb.COMMAND_BREAKPOINTS, gdb.COMPLETE_NONE) 168 | 169 | def invoke(self, arg, from_tty): 170 | _debugbreak_step() 171 | 172 | class _DebugBreakContinue(gdb.Command): 173 | '''Usage: debugbreak-continue 174 | Continue execution after a debug_break() breakpoint hit''' 175 | 176 | def __init__(self): 177 | gdb.Command.__init__(self, 'debugbreak-continue', gdb.COMMAND_BREAKPOINTS, gdb.COMPLETE_NONE) 178 | 179 | def invoke(self, arg, from_tty): 180 | _debugbreak_continue() 181 | 182 | _DebugBreakStep() 183 | _DebugBreakContinue() 184 | -------------------------------------------------------------------------------- /debugbreak.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2011-2021, Scott Tsai 2 | * 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright notice, 11 | * this list of conditions and the following disclaimer in the documentation 12 | * and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 18 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | * POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | #ifndef DEBUG_BREAK_H 27 | #define DEBUG_BREAK_H 28 | 29 | #ifdef _MSC_VER 30 | 31 | #define debug_break __debugbreak 32 | 33 | #else 34 | 35 | #ifdef __cplusplus 36 | extern "C" { 37 | #endif 38 | 39 | #define DEBUG_BREAK_USE_TRAP_INSTRUCTION 1 40 | #define DEBUG_BREAK_USE_BULTIN_TRAP 2 41 | #define DEBUG_BREAK_USE_SIGTRAP 3 42 | 43 | #if defined(__i386__) || defined(__x86_64__) 44 | #define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION 45 | __inline__ static void trap_instruction(void) 46 | { 47 | __asm__ volatile("int $0x03"); 48 | } 49 | #elif defined(__thumb__) 50 | #define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION 51 | /* FIXME: handle __THUMB_INTERWORK__ */ 52 | __attribute__((always_inline)) 53 | __inline__ static void trap_instruction(void) 54 | { 55 | /* See 'arm-linux-tdep.c' in GDB source. 56 | * Both instruction sequences below work. */ 57 | #if 1 58 | /* 'eabi_linux_thumb_le_breakpoint' */ 59 | __asm__ volatile(".inst 0xde01"); 60 | #else 61 | /* 'eabi_linux_thumb2_le_breakpoint' */ 62 | __asm__ volatile(".inst.w 0xf7f0a000"); 63 | #endif 64 | 65 | /* Known problem: 66 | * After a breakpoint hit, can't 'stepi', 'step', or 'continue' in GDB. 67 | * 'step' would keep getting stuck on the same instruction. 68 | * 69 | * Workaround: use the new GDB commands 'debugbreak-step' and 70 | * 'debugbreak-continue' that become available 71 | * after you source the script from GDB: 72 | * 73 | * $ gdb -x debugbreak-gdb.py <... USUAL ARGUMENTS ...> 74 | * 75 | * 'debugbreak-step' would jump over the breakpoint instruction with 76 | * roughly equivalent of: 77 | * (gdb) set $instruction_len = 2 78 | * (gdb) tbreak *($pc + $instruction_len) 79 | * (gdb) jump *($pc + $instruction_len) 80 | */ 81 | } 82 | #elif defined(__arm__) && !defined(__thumb__) 83 | #define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION 84 | __attribute__((always_inline)) 85 | __inline__ static void trap_instruction(void) 86 | { 87 | /* See 'arm-linux-tdep.c' in GDB source, 88 | * 'eabi_linux_arm_le_breakpoint' */ 89 | __asm__ volatile(".inst 0xe7f001f0"); 90 | /* Known problem: 91 | * Same problem and workaround as Thumb mode */ 92 | } 93 | #elif defined(__aarch64__) && defined(__APPLE__) 94 | #define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_BULTIN_DEBUGTRAP 95 | #elif defined(__aarch64__) 96 | #define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION 97 | __attribute__((always_inline)) 98 | __inline__ static void trap_instruction(void) 99 | { 100 | /* See 'aarch64-tdep.c' in GDB source, 101 | * 'aarch64_default_breakpoint' */ 102 | __asm__ volatile(".inst 0xd4200000"); 103 | } 104 | #elif defined(__powerpc__) 105 | /* PPC 32 or 64-bit, big or little endian */ 106 | #define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION 107 | __attribute__((always_inline)) 108 | __inline__ static void trap_instruction(void) 109 | { 110 | /* See 'rs6000-tdep.c' in GDB source, 111 | * 'rs6000_breakpoint' */ 112 | __asm__ volatile(".4byte 0x7d821008"); 113 | 114 | /* Known problem: 115 | * After a breakpoint hit, can't 'stepi', 'step', or 'continue' in GDB. 116 | * 'step' stuck on the same instruction ("twge r2,r2"). 117 | * 118 | * The workaround is the same as ARM Thumb mode: use debugbreak-gdb.py 119 | * or manually jump over the instruction. */ 120 | } 121 | #elif defined(__riscv) 122 | /* RISC-V 32 or 64-bit, whether the "C" extension 123 | * for compressed, 16-bit instructions are supported or not */ 124 | #define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION 125 | __attribute__((always_inline)) 126 | __inline__ static void trap_instruction(void) 127 | { 128 | /* See 'riscv-tdep.c' in GDB source, 129 | * 'riscv_sw_breakpoint_from_kind' */ 130 | __asm__ volatile(".4byte 0x00100073"); 131 | } 132 | #else 133 | #define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_SIGTRAP 134 | #endif 135 | 136 | 137 | #ifndef DEBUG_BREAK_IMPL 138 | #error "debugbreak.h is not supported on this target" 139 | #elif DEBUG_BREAK_IMPL == DEBUG_BREAK_USE_TRAP_INSTRUCTION 140 | __attribute__((always_inline)) 141 | __inline__ static void debug_break(void) 142 | { 143 | trap_instruction(); 144 | } 145 | #elif DEBUG_BREAK_IMPL == DEBUG_BREAK_USE_BULTIN_DEBUGTRAP 146 | __attribute__((always_inline)) 147 | __inline__ static void debug_break(void) 148 | { 149 | __builtin_debugtrap(); 150 | } 151 | #elif DEBUG_BREAK_IMPL == DEBUG_BREAK_USE_BULTIN_TRAP 152 | __attribute__((always_inline)) 153 | __inline__ static void debug_break(void) 154 | { 155 | __builtin_trap(); 156 | } 157 | #elif DEBUG_BREAK_IMPL == DEBUG_BREAK_USE_SIGTRAP 158 | #include 159 | __attribute__((always_inline)) 160 | __inline__ static void debug_break(void) 161 | { 162 | raise(SIGTRAP); 163 | } 164 | #else 165 | #error "invalid DEBUG_BREAK_IMPL value" 166 | #endif 167 | 168 | #ifdef __cplusplus 169 | } 170 | #endif 171 | 172 | #endif /* ifdef _MSC_VER */ 173 | 174 | #endif /* ifndef DEBUG_BREAK_H */ 175 | -------------------------------------------------------------------------------- /test/break-c++.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "debugbreak.h" 3 | 4 | int main() 5 | { 6 | debug_break(); 7 | std::cout << "hello, world\n"; 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /test/break.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "debugbreak.h" 3 | 4 | int main() 5 | { 6 | debug_break(); 7 | printf("hello world\n"); 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /test/break.gdb: -------------------------------------------------------------------------------- 1 | file break 2 | run 3 | -------------------------------------------------------------------------------- /test/fib.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "debugbreak.h" 4 | 5 | int fib(int n) 6 | { 7 | int r; 8 | if (n == 0 || n == 1) 9 | return 1; 10 | r = fib(n-1) + fib(n-2); 11 | if (r == 89) { 12 | debug_break(); 13 | } 14 | return r; 15 | } 16 | 17 | int main() 18 | { 19 | printf("%d\n", fib(15)); 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /test/fib.gdb: -------------------------------------------------------------------------------- 1 | file fib 2 | run 3 | -------------------------------------------------------------------------------- /test/test-debugbreak.gdb: -------------------------------------------------------------------------------- 1 | source ../debugbreak-gdb.py 2 | 3 | file break-c++ 4 | # set remote exec-file break-c++ 5 | run 6 | #debugbreak-step 7 | -------------------------------------------------------------------------------- /test/trap.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() 4 | { 5 | __builtin_trap(); 6 | printf("hello world\n"); 7 | return 0; 8 | } 9 | -------------------------------------------------------------------------------- /test/trap.gdb: -------------------------------------------------------------------------------- 1 | file trap 2 | handle SIGILL stop nopass 3 | run 4 | --------------------------------------------------------------------------------