Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
Debugger.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#include "Stroika/Foundation/StroikaPreComp.h"
5
6#if __cpp_lib_debugging >= 202403L
7#include <debugging>
8#endif
9
10#if qStroika_Foundation_Common_Platform_POSIX
11#include <fcntl.h>
12#include <string.h>
13#include <string>
14#include <unistd.h>
15#if defined(__APPLE__) && defined(__MACH__)
16#include <sys/sysctl.h>
17#include <sys/types.h>
18#endif
19#include <cstdio>
20#include <signal.h>
21#include <sys/ptrace.h>
22#elif qStroika_Foundation_Common_Platform_Windows
23#include <Windows.h>
24#endif
25#include "Stroika/Foundation/Math/Common.h"
26
27#include "Debugger.h"
28
29using namespace Stroika::Foundation;
30
31#ifndef __has_builtin
32#define __has_builtin(x) 0 // Compatibility with non-clang compilers.
33#endif
34
35#if qStroika_Foundation_Common_Platform_Linux
36namespace {
37 // From https://stackoverflow.com/questions/3596781/how-to-detect-if-the-current-process-is-being-run-by-gdb
38 bool DebuggerIsAttached_ ()
39 {
40 char buf[4096];
41 ssize_t num_read;
42 {
43 const int status_fd = ::open ("/proc/self/status", O_RDONLY);
44 if (status_fd == -1)
45 return false;
46 num_read = ::read (status_fd, buf, sizeof (buf) - 1);
47 ::close (status_fd);
48 if (num_read <= 0)
49 return false;
50 buf[num_read] = '\0';
51 }
52 constexpr char kTacerPidString_[] = "TracerPid:";
53 const auto tracer_pid_ptr = ::strstr (buf, kTacerPidString_);
54 if (tracer_pid_ptr == nullptr)
55 return false;
56 for (const char* characterPtr = tracer_pid_ptr + sizeof (kTacerPidString_) - 1; characterPtr <= buf + num_read; ++characterPtr) {
57 if (::isspace (*characterPtr))
58 continue;
59 else
60 return ::isdigit (*characterPtr) != 0 && *characterPtr != '0';
61 }
62 return false;
63 }
64}
65#endif
66
67#if qStroika_Foundation_Common_Platform_MacOS
68namespace {
69 // https://stackoverflow.com/questions/2200277/detecting-debugger-on-mac-os-x
70
71 // Returns true if the current process is being debugged (either
72 // running under the debugger or has a debugger attached post facto).
73 bool DebuggerIsAttached_ ()
74 {
75 // Initialize the flags so that, if sysctl fails for some bizarre
76 // reason, we get a predictable result.
77 kinfo_proc info{};
78 // Initialize mib, which tells sysctl the info we want, in this case
79 // we're looking for information about a specific process ID.
80 int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid ()};
81 size_t size = sizeof (info);
82 [[maybe_unused]] int junk = sysctl (mib, sizeof (mib) / sizeof (*mib), &info, &size, NULL, 0);
83 Assert (junk == 0);
84 return (info.kp_proc.p_flag & P_TRACED) != 0;
85 }
86}
87#endif
88
89/*
90 ********************************************************************************
91 ************************ Debug::IsThisProcessBeingDebugged *********************
92 ********************************************************************************
93 */
94optional<bool> Debug::IsThisProcessBeingDebugged ()
95{
96#if __cpp_lib_debugging >= 202403L
97 return std::is_debugger_present ();
98#endif
99#if qStroika_Foundation_Common_Platform_Linux
100 return DebuggerIsAttached_ ();
101#endif
102#if qStroika_Foundation_Common_Platform_MacOS
103 return DebuggerIsAttached_ ();
104#endif
105#if qStroika_Foundation_Common_Platform_POSIX
106#if 0
107 // Not a good approach - see https://stackoverflow.com/questions/3596781/how-to-detect-if-the-current-process-is-being-run-by-gdb
108 return ptrace (PTRACE_TRACEME, 0, NULL, 0) == -1;
109#endif
110#endif
111#if qStroika_Foundation_Common_Platform_Windows
112 return ::IsDebuggerPresent ();
113#endif
114 return nullopt;
115}
116
117/*
118 ********************************************************************************
119 ************************ Debug::DropIntoDebuggerIfPresent **********************
120 ********************************************************************************
121 */
122void Debug::DropIntoDebuggerIfPresent ()
123{
124 if (IsThisProcessBeingDebugged () == true) {
125#if __has_builtin(__builtin_trap) || defined(__GNUC__)
126 __builtin_trap ();
127#elif qStroika_Foundation_Common_Platform_POSIX
128 raise (SIGTRAP);
129#elif qStroika_Foundation_Common_Platform_Windows
130 ::DebugBreak ();
131#else
132 abort ();
133#endif
134 }
135}