plugify 1.2.8
Loading...
Searching...
No Matches
debugging.hpp
1#pragma once
2
3#include "plg/config.hpp"
4
5#if __has_include(<debugging>)
6#include <debugging>
7#if defined(__cpp_lib_debugging) && __cpp_lib_debugging >= 202403L
8#define PLUGIFY_HAS_STD_DEBUGGING 1
9#else
10#define PLUGIFY_HAS_STD_DEBUGGING 0
11#endif
12#else
13#define PLUGIFY_HAS_STD_DEBUGGING 0
14#endif
15
16#if !PLUGIFY_HAS_STD_DEBUGGING
17#if PLUGIFY_PLATFORM_WINDOWS
18#include <windows.h>
19#include <intrin.h>
20#elif PLUGIFY_PLATFORM_SWITCH
21#include <nn/diag/diag_Debugger.h>
22#elif PLUGIFY_PLATFORM_ORBIS || PLUGIFY_PLATFORM_PROSPERO
23#include <libdbg.h>
24#elif PLUGIFY_PLATFORM_APPLE
25#include <mach/mach_init.h>
26#include <mach/task.h>
27#elif PLUGIFY_PLATFORM_LINUX
28#include <cerrno>
29#include <cstring>
30#include <fcntl.h>
31#include <unistd.h>
32#endif
33
34namespace plg {
35
36#if PLUGIFY_PLATFORM_WINDOWS
37
38 inline bool is_debugger_present() noexcept {
39 return (IsDebuggerPresent() != FALSE);
40 }
41
42#elif PLUGIFY_PLATFORM_SWITCH
43
44 inline bool is_debugger_present() noexcept {
45 return nn::diag::IsDebuggerAttached();
46 }
47
48#elif PLUGIFY_PLATFORM_ORBIS || PLUGIFY_PLATFORM_PROSPERO
49
50 inline bool is_debugger_present() noexcept {
51 return sceDbgIsDebuggerAttached() != 0;
52 }
53
54#elif PLUGIFY_PLATFORM_APPLE
55
56 inline bool is_debugger_present() noexcept {
57 mach_msg_type_number_t count = 0;
58 exception_mask_t masks[EXC_TYPES_COUNT];
59 mach_port_t ports[EXC_TYPES_COUNT];
60 exception_behavior_t behaviors[EXC_TYPES_COUNT];
61 thread_state_flavor_t flavors[EXC_TYPES_COUNT];
62 exception_mask_t mask = EXC_MASK_ALL & ~(EXC_MASK_RESOURCE | EXC_MASK_GUARD);
63
64 kern_return_t result = task_get_exception_ports(mach_task_self(), mask, masks, &count, ports, behaviors, flavors);
65 if (result != KERN_SUCCESS)
66 return false;
67
68 for (mach_msg_type_number_t i = 0; i < count; ++i)
69 if (MACH_PORT_VALID(ports[i]))
70 return true;
71
72 return false;
73 }
74
75#elif PLUGIFY_PLATFORM_LINUX
76
77 namespace detail {
78 namespace debugging {
79 inline bool parse_proc_status_line(char* buf, size_t size) noexcept {
80 if (std::strncmp(buf, "TracerPid:\t", 11) != 0) {
81 return false;
82 }
83 unsigned long long pid = 0;
84 size_t pos = 11;
85 while (pos < size) {
86 auto byte = static_cast<unsigned char>(buf[pos]);
87 if (!(static_cast<unsigned char>('0') <= byte && byte <= static_cast<unsigned char>('9'))) {
88 return false;
89 }
90 uint8_t digit = static_cast<uint8_t>(byte - static_cast<unsigned char>('0'));
91 pid = (pid * 10) + digit;
92 pos++;
93 }
94 return (pid != 0);
95 }
96
97 inline bool parse_proc_status() noexcept {
98 int olderrno = errno;
99 bool detected_debugger = false;
100 int error = 0;
101 int fd = -1;
102 bool open_done = false;
103 while (!open_done) {
104 int res = open("/proc/self/status", O_RDONLY);
105 if (res >= 0) {
106 fd = res;
107 open_done = true;
108 } else if (errno != EINTR) {
109 error = errno;
110 open_done = true;
111 }
112 }
113 if (error != 0) {
114 errno = olderrno;
115 return false;
116 }
117 if (fd < 0) {
118 errno = olderrno;
119 return false;
120 }
121 bool eof = false;
122 uint8_t iobuf[1024];
123 size_t iobuf_size = 0;
124 char linebuf[128];
125 size_t linebuf_size = 0;
126 while (!eof) {
127 ssize_t bytes_read = read(fd, iobuf, sizeof(iobuf));
128 if (bytes_read == -1) {
129 if (errno == EINTR) {
130 continue;
131 }
132 } else if (bytes_read == 0) {
133 eof = true;
134 }
135 iobuf_size = static_cast<size_t>(bytes_read);
136 for (size_t i = 0; i < iobuf_size; ++i) {
137 if (iobuf[i] == static_cast<unsigned char>('\n')) {
138 if (parse_proc_status_line(linebuf, linebuf_size)) {
139 detected_debugger = true;
140 }
141 linebuf_size = 0;
142 } else {
143 if (linebuf_size < sizeof(linebuf)) {
144 linebuf[linebuf_size] = static_cast<char>(iobuf[i]);
145 linebuf_size++;
146 }
147 }
148 }
149 if (linebuf_size > 0) {
150 if (parse_proc_status_line(linebuf, linebuf_size)) {
151 detected_debugger = true;
152 }
153 linebuf_size = 0;
154 }
155 }
156 bool close_done = false;
157 while (!close_done) {
158 int res = close(fd);
159 if (res == 0) {
160 fd = -1;
161 close_done = true;
162 } else if (errno != EINTR) {
163 error = errno;
164 close_done = true;
165 }
166 }
167 if (error != 0) {
168 errno = olderrno;
169 return false;
170 }
171 errno = olderrno;
172 return detected_debugger;
173 }
174
175 } // namespace debugging
176 } // namespace detail
177
178 inline bool is_debugger_present() noexcept {
179 return plg::detail::debugging::parse_proc_status();
180 }
181
182#else
183
184 inline bool is_debugger_present() noexcept {
185 return false;
186 }
187
188#endif
189
190 inline void breakpoint() noexcept {
191#if PLUGIFY_COMPILER_MSVC
192 __debugbreak();
193#elif PLUGIFY_COMPILER_CLANG
194 __builtin_debugtrap();
195#elif PLUGIFY_COMPILER_GCC
196 __builtin_trap();
197#else
198# error "Unsupported platform for breakpoint"
199#endif
200 }
201
202 inline void breakpoint_if_debugging() noexcept {
203 if (plg::is_debugger_present()) {
204 plg::breakpoint();
205 }
206 }
207} // namespace plg
208
209namespace std {
210 using plg::breakpoint;
211 using plg::breakpoint_if_debugging;
212 using plg::is_debugger_present;
213} // namespace std
214
215#endif // !PLUGIFY_HAS_STD_DEBUGGING