ZeroErr
debugbreak.h
Go to the documentation of this file.
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) { __asm__ volatile("int $0x03"); }
46 #elif defined(__thumb__)
47 #define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION
48 /* FIXME: handle __THUMB_INTERWORK__ */
49 __attribute__((always_inline)) __inline__ static void trap_instruction(void) {
50  /* See 'arm-linux-tdep.c' in GDB source.
51  * Both instruction sequences below work. */
52 #if 1
53  /* 'eabi_linux_thumb_le_breakpoint' */
54  __asm__ volatile(".inst 0xde01");
55 #else
56  /* 'eabi_linux_thumb2_le_breakpoint' */
57  __asm__ volatile(".inst.w 0xf7f0a000");
58 #endif
59 
60  /* Known problem:
61  * After a breakpoint hit, can't 'stepi', 'step', or 'continue' in GDB.
62  * 'step' would keep getting stuck on the same instruction.
63  *
64  * Workaround: use the new GDB commands 'debugbreak-step' and
65  * 'debugbreak-continue' that become available
66  * after you source the script from GDB:
67  *
68  * $ gdb -x debugbreak-gdb.py <... USUAL ARGUMENTS ...>
69  *
70  * 'debugbreak-step' would jump over the breakpoint instruction with
71  * roughly equivalent of:
72  * (gdb) set $instruction_len = 2
73  * (gdb) tbreak *($pc + $instruction_len)
74  * (gdb) jump *($pc + $instruction_len)
75  */
76 }
77 #elif defined(__arm__) && !defined(__thumb__)
78 #define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION
79 __attribute__((always_inline)) __inline__ static void trap_instruction(void) {
80  /* See 'arm-linux-tdep.c' in GDB source,
81  * 'eabi_linux_arm_le_breakpoint' */
82  __asm__ volatile(".inst 0xe7f001f0");
83  /* Known problem:
84  * Same problem and workaround as Thumb mode */
85 }
86 #elif defined(__aarch64__) && defined(__APPLE__)
87 #define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_BULTIN_DEBUGTRAP
88 #elif defined(__aarch64__)
89 #define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION
90 __attribute__((always_inline)) __inline__ static void trap_instruction(void) {
91  /* See 'aarch64-tdep.c' in GDB source,
92  * 'aarch64_default_breakpoint' */
93  __asm__ volatile(".inst 0xd4200000");
94 }
95 #elif defined(__powerpc__)
96 /* PPC 32 or 64-bit, big or little endian */
97 #define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION
98 __attribute__((always_inline)) __inline__ static void trap_instruction(void) {
99  /* See 'rs6000-tdep.c' in GDB source,
100  * 'rs6000_breakpoint' */
101  __asm__ volatile(".4byte 0x7d821008");
102 
103  /* Known problem:
104  * After a breakpoint hit, can't 'stepi', 'step', or 'continue' in GDB.
105  * 'step' stuck on the same instruction ("twge r2,r2").
106  *
107  * The workaround is the same as ARM Thumb mode: use debugbreak-gdb.py
108  * or manually jump over the instruction. */
109 }
110 #elif defined(__riscv)
111 /* RISC-V 32 or 64-bit, whether the "C" extension
112  * for compressed, 16-bit instructions are supported or not */
113 #define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION
114 __attribute__((always_inline)) __inline__ static void trap_instruction(void) {
115  /* See 'riscv-tdep.c' in GDB source,
116  * 'riscv_sw_breakpoint_from_kind' */
117  __asm__ volatile(".4byte 0x00100073");
118 }
119 #else
120 #define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_SIGTRAP
121 #endif
122 
123 
124 #ifndef DEBUG_BREAK_IMPL
125 #error "debugbreak.h is not supported on this target"
126 #elif DEBUG_BREAK_IMPL == DEBUG_BREAK_USE_TRAP_INSTRUCTION
127 __attribute__((always_inline)) __inline__ static void debug_break(void) { trap_instruction(); }
128 #elif DEBUG_BREAK_IMPL == DEBUG_BREAK_USE_BULTIN_DEBUGTRAP
129 __attribute__((always_inline)) __inline__ static void debug_break(void) { __builtin_debugtrap(); }
130 #elif DEBUG_BREAK_IMPL == DEBUG_BREAK_USE_BULTIN_TRAP
131 __attribute__((always_inline)) __inline__ static void debug_break(void) { __builtin_trap(); }
132 #elif DEBUG_BREAK_IMPL == DEBUG_BREAK_USE_SIGTRAP
133 #include <signal.h>
134 __attribute__((always_inline)) __inline__ static void debug_break(void) { raise(SIGTRAP); }
135 #else
136 #error "invalid DEBUG_BREAK_IMPL value"
137 #endif
138 
139 #ifdef __cplusplus
140 }
141 #endif
142 
143 #endif /* ifdef _MSC_VER */
144 
145 
146 // Here is for checking the debugger is running
147 
148 #include <fstream>
149 
150 #ifdef IS_DEBUGGER_ACTIVE
151 __attribute__((always_inline)) __inline__ static bool isDebuggerActive() {
152  return IS_DEBUGGER_ACTIVE();
153 }
154 #else // IS_DEBUGGER_ACTIVE
155 #ifdef __linux__
156 class ErrnoGuard {
157 public:
158  ErrnoGuard() : m_oldErrno(errno) {}
159  ~ErrnoGuard() { errno = m_oldErrno; }
160 
161 private:
162  int m_oldErrno;
163 };
164 // See the comments in Catch2 for the reasoning behind this implementation:
165 // https://github.com/catchorg/Catch2/blob/v2.13.1/include/internal/catch_debugger.cpp#L79-L102
166 __attribute__((always_inline)) __inline__ static bool isDebuggerActive() {
167  ErrnoGuard guard;
168  std::ifstream in("/proc/self/status");
169  for (std::string line; std::getline(in, line);) {
170  static const int PREFIX_LEN = 11;
171  if (line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0) {
172  return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0';
173  }
174  }
175  return false;
176 }
177 #elif defined(__APPLE__)
178 // The following function is taken directly from the following technical note:
179 // https://developer.apple.com/library/archive/qa/qa1361/_index.html
180 // Returns true if the current process is being debugged (either
181 // running under the debugger or has a debugger attached post facto).
182 __attribute__((always_inline)) __inline__ static bool isDebuggerActive() {
183  int mib[4];
184  kinfo_proc info;
185  size_t size;
186  // Initialize the flags so that, if sysctl fails for some bizarre
187  // reason, we get a predictable result.
188  info.kp_proc.p_flag = 0;
189  // Initialize mib, which tells sysctl the info we want, in this case
190  // we're looking for information about a specific process ID.
191  mib[0] = CTL_KERN;
192  mib[1] = KERN_PROC;
193  mib[2] = KERN_PROC_PID;
194  mib[3] = getpid();
195  // Call sysctl.
196  size = sizeof(info);
197  if (sysctl(mib, (sizeof(mib) / sizeof(*mib)), &info, &size, 0, 0) != 0) {
198  std::cerr << "\nCall to sysctl failed - unable to determine if debugger is active **\n";
199  return false;
200  }
201  // We're being debugged if the P_TRACED flag is set.
202  return ((info.kp_proc.p_flag & P_TRACED) != 0);
203 }
204 #elif defined(__MINGW32__) || defined(__MINGW64__)
205 extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();
206 __attribute__((always_inline)) __inline__ static bool isDebuggerActive() {
207  return ::IsDebuggerPresent() != 0;
208 }
209 #elif defined(_MSC_VER)
210 extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();
211 inline __forceinline static bool isDebuggerActive() {
212  return ::IsDebuggerPresent() != 0;
213 }
214 #else
215 
216 __attribute__((always_inline)) __inline__ static bool isDebuggerActive() { return false; }
217 #endif
218 #endif // IS_DEBUGGER_ACTIVE
219 
220 
221 #endif /* ifndef DEBUG_BREAK_H */
__attribute__((always_inline)) __inline__ static void debug_break(void)
Definition: debugbreak.h:134