catch_debugger.cpp
1 2 // Copyright Catch2 Authors 3 // Distributed under the Boost Software License, Version 1.0. 4 // (See accompanying file LICENSE.txt or copy at 5 // https://www.boost.org/LICENSE_1_0.txt) 6 7 // SPDX-License-Identifier: BSL-1.0 8 #include <catch2/internal/catch_debugger.hpp> 9 #include <catch2/internal/catch_errno_guard.hpp> 10 #include <catch2/internal/catch_platform.hpp> 11 #include <catch2/internal/catch_stdstreams.hpp> 12 13 #if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE) 14 15 # include <cassert> 16 # include <sys/types.h> 17 # include <unistd.h> 18 # include <cstddef> 19 # include <ostream> 20 21 #ifdef __apple_build_version__ 22 // These headers will only compile with AppleClang (XCode) 23 // For other compilers (Clang, GCC, ... ) we need to exclude them 24 # include <sys/sysctl.h> 25 #endif 26 27 namespace Catch { 28 #ifdef __apple_build_version__ 29 // The following function is taken directly from the following technical note: 30 // https://developer.apple.com/library/archive/qa/qa1361/_index.html 31 32 // Returns true if the current process is being debugged (either 33 // running under the debugger or has a debugger attached post facto). 34 bool isDebuggerActive(){ 35 int mib[4]; 36 struct kinfo_proc info; 37 std::size_t size; 38 39 // Initialize the flags so that, if sysctl fails for some bizarre 40 // reason, we get a predictable result. 41 42 info.kp_proc.p_flag = 0; 43 44 // Initialize mib, which tells sysctl the info we want, in this case 45 // we're looking for information about a specific process ID. 46 47 mib[0] = CTL_KERN; 48 mib[1] = KERN_PROC; 49 mib[2] = KERN_PROC_PID; 50 mib[3] = getpid(); 51 52 // Call sysctl. 53 54 size = sizeof(info); 55 if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, nullptr, 0) != 0 ) { 56 Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n\n" << std::flush; 57 return false; 58 } 59 60 // We're being debugged if the P_TRACED flag is set. 61 62 return ( (info.kp_proc.p_flag & P_TRACED) != 0 ); 63 } 64 #else 65 bool isDebuggerActive() { 66 // We need to find another way to determine this for non-appleclang compilers on macOS 67 return false; 68 } 69 #endif 70 } // namespace Catch 71 72 #elif defined(CATCH_PLATFORM_LINUX) 73 #include <fstream> 74 #include <string> 75 76 namespace Catch{ 77 // The standard POSIX way of detecting a debugger is to attempt to 78 // ptrace() the process, but this needs to be done from a child and not 79 // this process itself to still allow attaching to this process later 80 // if wanted, so is rather heavy. Under Linux we have the PID of the 81 // "debugger" (which doesn't need to be gdb, of course, it could also 82 // be strace, for example) in /proc/$PID/status, so just get it from 83 // there instead. 84 bool isDebuggerActive(){ 85 // Libstdc++ has a bug, where std::ifstream sets errno to 0 86 // This way our users can properly assert over errno values 87 ErrnoGuard guard; 88 std::ifstream in("/proc/self/status"); 89 for( std::string line; std::getline(in, line); ) { 90 static const int PREFIX_LEN = 11; 91 if( line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0 ) { 92 // We're traced if the PID is not 0 and no other PID starts 93 // with 0 digit, so it's enough to check for just a single 94 // character. 95 return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0'; 96 } 97 } 98 99 return false; 100 } 101 } // namespace Catch 102 #elif defined(_MSC_VER) 103 extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); 104 namespace Catch { 105 bool isDebuggerActive() { 106 return IsDebuggerPresent() != 0; 107 } 108 } 109 #elif defined(__MINGW32__) 110 extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); 111 namespace Catch { 112 bool isDebuggerActive() { 113 return IsDebuggerPresent() != 0; 114 } 115 } 116 #else 117 namespace Catch { 118 bool isDebuggerActive() { return false; } 119 } 120 #endif // Platform