diff --git a/CMakeLists.txt b/CMakeLists.txt index 94646f228..4e9b69387 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,10 @@ if(APPLE) SET(CMAKE_XCODE_SCHEME_ENABLE_GPU_FRAME_CAPTURE_MODE "Disabled") endif() +if("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "FreeBSD") + set(FreeBSD YES) +endif() + # Configure some stuff that needs to be set really early if(BUILD_ANDROID) if(NOT DEFINED ENV{JAVA_HOME}) diff --git a/qrenderdoc/3rdparty/catch/official/catch.hpp b/qrenderdoc/3rdparty/catch/official/catch.hpp index 8ef0132d5..e505b6152 100644 --- a/qrenderdoc/3rdparty/catch/official/catch.hpp +++ b/qrenderdoc/3rdparty/catch/official/catch.hpp @@ -20,6 +20,7 @@ actual site failure. So the debugger stops on the CHECK() or REQUIRE() call instead of inside AssertionHandler::complete() * https://github.com/baldurk/renderdoc/commit/4232736fc21fc6a13a4de6997a5ae106598b225f +* Add FreeBSD support with correct debugger handling */ #ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED #define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED @@ -2724,7 +2725,7 @@ namespace Catch { #define CATCH_TRAP() __asm__(".inst 0xde01") #endif -#elif defined(CATCH_PLATFORM_LINUX) +#elif defined(CATCH_PLATFORM_LINUX) || defined(__FreeBSD__) // If we can use inline assembler, do it because this allows us to break // directly at the location of the failing check instead of breaking inside // raise() called from it, i.e. one stack frame below. @@ -10428,7 +10429,7 @@ namespace Catch { // end catch_debug_console.cpp // start catch_debugger.cpp -#if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE) +#if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE) || defined(__FreeBSD__) # include # include @@ -10440,10 +10441,13 @@ namespace Catch { // These headers will only compile with AppleClang (XCode) // For other compilers (Clang, GCC, ... ) we need to exclude them # include +#elif defined(__FreeBSD__) +# include +# include #endif namespace Catch { - #ifdef __apple_build_version__ + #if defined(__apple_build_version__) || defined(__FreeBSD__) // The following function is taken directly from the following technical note: // https://developer.apple.com/library/archive/qa/qa1361/_index.html @@ -10454,10 +10458,12 @@ namespace Catch { struct kinfo_proc info; std::size_t size; + #ifndef __FreeBSD__ // Initialize the flags so that, if sysctl fails for some bizarre // reason, we get a predictable result. info.kp_proc.p_flag = 0; + #endif // Initialize mib, which tells sysctl the info we want, in this case // we're looking for information about a specific process ID. @@ -10477,7 +10483,11 @@ namespace Catch { // We're being debugged if the P_TRACED flag is set. + #ifdef __FreeBSD__ + return ( (info.ki_flag & P_TRACED) != 0 ); + #else return ( (info.kp_proc.p_flag & P_TRACED) != 0 ); + #endif } #else bool isDebuggerActive() { diff --git a/qrenderdoc/Windows/BufferViewer.cpp b/qrenderdoc/Windows/BufferViewer.cpp index 965e485c2..c8db1a46e 100644 --- a/qrenderdoc/Windows/BufferViewer.cpp +++ b/qrenderdoc/Windows/BufferViewer.cpp @@ -79,7 +79,7 @@ enum Key_F = 33, Key_W = 17, Key_R = 19, -#elif defined(Q_OS_LINUX) +#elif defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) Key_A = 30 + 8, Key_S = 31 + 8, Key_D = 32 + 8, @@ -111,7 +111,7 @@ enum Key_F = quint32('F'), Key_W = quint32('W'), Key_R = quint32('R'), -#elif defined(Q_OS_LINUX) +#elif defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) Key_A = quint32('a'), Key_S = quint32('s'), Key_D = quint32('d'), diff --git a/qrenderdoc/Windows/Dialogs/CaptureDialog.cpp b/qrenderdoc/Windows/Dialogs/CaptureDialog.cpp index b56f1b4a0..b9f2337f0 100644 --- a/qrenderdoc/Windows/Dialogs/CaptureDialog.cpp +++ b/qrenderdoc/Windows/Dialogs/CaptureDialog.cpp @@ -471,7 +471,7 @@ void CaptureDialog::vulkanLayerWarn_mouseClick() bool needReg = RENDERDOC_NeedVulkanLayerRegistration(NULL); ui->vulkanLayerWarn->setVisible(needReg); -#if !defined(Q_OS_LINUX) +#if !defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) // can't alert the user on linux because the command might still be running - there's // seemingly no portable way to wait for the command to finish. if(needReg) @@ -490,7 +490,7 @@ void CaptureDialog::vulkanLayerWarn_mouseClick() { // linux sometimes can't run GUI apps as root, so we have to run renderdoccmd. Check that it's // installed, error if not, then invoke it. -#if defined(Q_OS_LINUX) +#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) QDir binDir = QFileInfo(qApp->applicationFilePath()).absoluteDir(); QString cmd = lit("renderdoccmd"); diff --git a/renderdoc/3rdparty/catch/official/catch.hpp b/renderdoc/3rdparty/catch/official/catch.hpp index 8ef0132d5..e505b6152 100644 --- a/renderdoc/3rdparty/catch/official/catch.hpp +++ b/renderdoc/3rdparty/catch/official/catch.hpp @@ -20,6 +20,7 @@ actual site failure. So the debugger stops on the CHECK() or REQUIRE() call instead of inside AssertionHandler::complete() * https://github.com/baldurk/renderdoc/commit/4232736fc21fc6a13a4de6997a5ae106598b225f +* Add FreeBSD support with correct debugger handling */ #ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED #define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED @@ -2724,7 +2725,7 @@ namespace Catch { #define CATCH_TRAP() __asm__(".inst 0xde01") #endif -#elif defined(CATCH_PLATFORM_LINUX) +#elif defined(CATCH_PLATFORM_LINUX) || defined(__FreeBSD__) // If we can use inline assembler, do it because this allows us to break // directly at the location of the failing check instead of breaking inside // raise() called from it, i.e. one stack frame below. @@ -10428,7 +10429,7 @@ namespace Catch { // end catch_debug_console.cpp // start catch_debugger.cpp -#if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE) +#if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE) || defined(__FreeBSD__) # include # include @@ -10440,10 +10441,13 @@ namespace Catch { // These headers will only compile with AppleClang (XCode) // For other compilers (Clang, GCC, ... ) we need to exclude them # include +#elif defined(__FreeBSD__) +# include +# include #endif namespace Catch { - #ifdef __apple_build_version__ + #if defined(__apple_build_version__) || defined(__FreeBSD__) // The following function is taken directly from the following technical note: // https://developer.apple.com/library/archive/qa/qa1361/_index.html @@ -10454,10 +10458,12 @@ namespace Catch { struct kinfo_proc info; std::size_t size; + #ifndef __FreeBSD__ // Initialize the flags so that, if sysctl fails for some bizarre // reason, we get a predictable result. info.kp_proc.p_flag = 0; + #endif // Initialize mib, which tells sysctl the info we want, in this case // we're looking for information about a specific process ID. @@ -10477,7 +10483,11 @@ namespace Catch { // We're being debugged if the P_TRACED flag is set. + #ifdef __FreeBSD__ + return ( (info.ki_flag & P_TRACED) != 0 ); + #else return ( (info.kp_proc.p_flag & P_TRACED) != 0 ); + #endif } #else bool isDebuggerActive() { diff --git a/renderdoc/CMakeLists.txt b/renderdoc/CMakeLists.txt index cff654c00..fdb659b48 100644 --- a/renderdoc/CMakeLists.txt +++ b/renderdoc/CMakeLists.txt @@ -59,6 +59,15 @@ elseif(UNIX) PRIVATE -ldl PRIVATE -lrt) + if(FreeBSD) + list(APPEND RDOC_LIBRARIES + PRIVATE -L/usr/local/lib + PRIVATE -liconv + PRIVATE -lexecinfo + PRIVATE -lutil + ) + endif() + if(ENABLE_XLIB) find_package(X11 REQUIRED) @@ -329,6 +338,23 @@ elseif(ENABLE_GGP) os/posix/posix_stringio.cpp os/posix/posix_threading.cpp os/posix/posix_specific.h) +elseif(FreeBSD) + list(APPEND sources + data/embedded_files.h + os/posix/linux/linux_stringio.cpp + os/posix/linux/linux_callstack.cpp + os/posix/linux/linux_threading.cpp + os/posix/freebsd/freebsd_process.cpp + os/posix/linux/linux_hook.cpp + os/posix/linux/linux_network.cpp + 3rdparty/plthook/plthook.h + 3rdparty/plthook/plthook_elf.c + os/posix/posix_network.h + os/posix/posix_network.cpp + os/posix/posix_process.cpp + os/posix/posix_stringio.cpp + os/posix/posix_threading.cpp + os/posix/posix_specific.h) elseif(UNIX) list(APPEND sources data/embedded_files.h diff --git a/renderdoc/api/app/renderdoc_app.h b/renderdoc/api/app/renderdoc_app.h index 9cf0bf489..cd02dd97f 100644 --- a/renderdoc/api/app/renderdoc_app.h +++ b/renderdoc/api/app/renderdoc_app.h @@ -35,7 +35,7 @@ #if defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) #define RENDERDOC_CC __cdecl -#elif defined(__linux__) +#elif defined(__linux__) || defined(__FreeBSD__) #define RENDERDOC_CC #elif defined(__APPLE__) #define RENDERDOC_CC diff --git a/renderdoc/driver/gl/gl_manager.cpp b/renderdoc/driver/gl/gl_manager.cpp index cc2517114..b89758e8c 100644 --- a/renderdoc/driver/gl/gl_manager.cpp +++ b/renderdoc/driver/gl/gl_manager.cpp @@ -84,6 +84,9 @@ void GLResourceManager::MarkFBOAttachmentsReferenced(ResourceId fboid, GLResourc { FBOCache *cache = m_FBOAttachmentsCache[fboid]; + if(!record) + return; + if(!cache) { cache = m_FBOAttachmentsCache[fboid] = new FBOCache; diff --git a/renderdoc/os/posix/freebsd/freebsd_process.cpp b/renderdoc/os/posix/freebsd/freebsd_process.cpp new file mode 100644 index 000000000..c048bcc3b --- /dev/null +++ b/renderdoc/os/posix/freebsd/freebsd_process.cpp @@ -0,0 +1,211 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2019-2023 Baldur Karlsson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#include // for dlsym +#include +#include +#include +#include +#include +#include "common/common.h" +#include "common/formatting.h" +#include "core/core.h" +#include "os/os_specific.h" + +// extern char **environ; + +char **GetCurrentEnvironment() +{ + // environ is broken: https://reviews.freebsd.org/D30842 + return *(char ***)dlsym(RTLD_DEFAULT, "environ"); + // return environ; +} + +rdcstr execcmd(const char *cmd) +{ + FILE *pipe = popen(cmd, "r"); + + if(!pipe) + return "ERROR"; + + char buffer[128]; + + rdcstr result = ""; + + while(!feof(pipe)) + { + if(fgets(buffer, 128, pipe) != NULL) + result += buffer; + } + + pclose(pipe); + + return result; +} + +bool isNewline(char c) +{ + return c == '\n' || c == '\r'; +} + +// FIXME: lsof is in ports but not in base system +int GetIdentPort(pid_t childPid) +{ + rdcstr lsof = StringFormat::Fmt("lsof -p %d -a -i 4 -F n", (int)childPid); + rdcstr result; + uint32_t wait = 1; + // Wait for a maximum of ~16 seconds + for(int i = 0; i < 14; ++i) + { + result = execcmd(lsof.c_str()); + if(!result.empty()) + break; + usleep(wait * 1000); + wait *= 2; + } + if(result.empty()) + { + RDCERR("No output from lsof command: '%s'", lsof.c_str()); + return 0; + } + + // Parse the result expecting: + // p + // + // n*: + + rdcstr parseResult(result); + const int len = parseResult.count(); + if(parseResult[0] == 'p') + { + int tokenStart = 1; + int i = tokenStart; + for(; i < len; i++) + { + if(parseResult[i] < '0' || parseResult[i] > '9') + break; + } + parseResult[i++] = 0; + + if(isNewline(parseResult[i])) + i++; + + const int pid = atoi(&result[tokenStart]); + if(pid == (int)childPid) + { + const char *netString("n*:"); + while(i < len) + { + const int netStart = parseResult.find(netString, i); + if(netStart >= 0) + { + tokenStart = netStart + (int)strlen(netString); + i = tokenStart; + for(; i < len; i++) + { + if(parseResult[i] < '0' || parseResult[i] > '9') + break; + } + parseResult[i++] = 0; + + if(isNewline(parseResult[i])) + i++; + + const int port = atoi(&result[tokenStart]); + if(port >= RenderDoc_FirstTargetControlPort && port <= RenderDoc_LastTargetControlPort) + { + return port; + } + // continue on to next port + } + else + { + RDCERR("Malformed line - expected 'n*':\n%s", &result[i]); + return 0; + } + } + } + else + { + RDCERR("pid from lsof output doesn't match childPid"); + return 0; + } + } + RDCERR("Failed to parse output from lsof:\n%s", result.c_str()); + return 0; +} + +void StopAtMainInChild() +{ +} + +bool StopChildAtMain(pid_t childPid, bool *exitWithNoExec) +{ + return false; +} + +void ResumeProcess(pid_t childPid, uint32_t delay) +{ +} + +// OSUtility::DebuggerPresent is called a lot +// cache the value at startup as an optimisation +static bool s_debuggerPresent = false; +static bool s_debuggerCached = false; + +void CacheDebuggerPresent() +{ + int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()}; + struct kinfo_proc info = {}; + size_t size = sizeof(info); + if(!sysctl(mib, ARRAY_COUNT(mib), &info, &size, NULL, 0)) + { + s_debuggerPresent = (info.ki_flag & P_TRACED); + s_debuggerCached = true; + } +} + +bool OSUtility::DebuggerPresent() +{ + if(!s_debuggerCached) + CacheDebuggerPresent(); + return s_debuggerPresent; +} + +rdcstr Process::GetEnvVariable(const rdcstr &name) +{ + const char *val = getenv(name.c_str()); + return val ? val : rdcstr(); +} + +uint64_t Process::GetMemoryUsage() +{ + int mib[4] = {CTL_VM, KERN_PROC, KERN_PROC_PID, getpid()}; + struct kinfo_proc info = {}; + size_t size = sizeof(info); + if(sysctl(mib, ARRAY_COUNT(mib), &info, &size, NULL, 0) != 0) + return 0; + // from usr.bin/top/machine.c macro PROCSIZE + return info.ki_size / 1024; +} diff --git a/renderdoc/os/posix/linux/linux_callstack.cpp b/renderdoc/os/posix/linux/linux_callstack.cpp index e97d235b9..c266f126b 100644 --- a/renderdoc/os/posix/linux/linux_callstack.cpp +++ b/renderdoc/os/posix/linux/linux_callstack.cpp @@ -66,11 +66,11 @@ private: { void *addrs_ptr[ARRAY_COUNT(addrs)]; - int ret = backtrace(addrs_ptr, ARRAY_COUNT(addrs)); + size_t ret = backtrace(addrs_ptr, ARRAY_COUNT(addrs)); numLevels = 0; if(ret > 0) - numLevels = (size_t)ret; + numLevels = ret; int offs = 0; // if we want to trim levels of the stack, we can do that here diff --git a/renderdoc/os/posix/linux/linux_hook.cpp b/renderdoc/os/posix/linux/linux_hook.cpp index 7138904d8..3148d364c 100644 --- a/renderdoc/os/posix/linux/linux_hook.cpp +++ b/renderdoc/os/posix/linux/linux_hook.cpp @@ -38,7 +38,11 @@ Threading::CriticalSection libLock; +#ifdef __linux__ RDOC_EXTERN_CONFIG(bool, Linux_Debug_PtraceLogging); +#else +#define Linux_Debug_PtraceLogging() false +#endif static std::map> libraryCallbacks; static rdcarray libraryHooks; @@ -116,6 +120,7 @@ int direct_setenv(const char *name, const char *value, int overwrite); // The other variants all forward to one of those - the 'l' cases unroll the va_args first before // calling onwards +#ifndef __FreeBSD__ // TODO: environ bug #define GET_EXECL_PARAMS(has_e) \ va_list args; \ va_start(args, arg); \ @@ -194,6 +199,7 @@ __attribute__((visibility("default"))) int execvp(const char *pathname, char *co return execvpe(pathname, argv, environ); } +#endif // __FreeBSD__ __attribute__((visibility("default"))) int execve(const char *pathname, char *const argv[], char *const envp[]) diff --git a/renderdoc/os/posix/linux/linux_threading.cpp b/renderdoc/os/posix/linux/linux_threading.cpp index ebd57602f..a47f68f51 100644 --- a/renderdoc/os/posix/linux/linux_threading.cpp +++ b/renderdoc/os/posix/linux/linux_threading.cpp @@ -24,7 +24,9 @@ #include "os/os_specific.h" +#ifdef __linux__ #include +#endif #include #include @@ -42,5 +44,7 @@ uint64_t Timing::GetTick() void Threading::SetCurrentThreadName(const rdcstr &name) { +#ifdef __linux__ prctl(PR_SET_NAME, (unsigned long)name.c_str(), 0, 0, 0); +#endif } diff --git a/renderdoccmd/CMakeLists.txt b/renderdoccmd/CMakeLists.txt index 1ccc82a1e..3cac9e71f 100644 --- a/renderdoccmd/CMakeLists.txt +++ b/renderdoccmd/CMakeLists.txt @@ -50,9 +50,14 @@ elseif(ENABLE_GGP) elseif(UNIX) list(APPEND sources renderdoccmd_linux.cpp) -if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set_property(SOURCE renderdoccmd_linux.cpp APPEND_STRING PROPERTY COMPILE_FLAGS " -Wno-shadow") -endif() + if(FreeBSD) + list(APPEND libraries PRIVATE -L/usr/local/lib) + list(APPEND includes /usr/local/include) + endif() + + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + set_property(SOURCE renderdoccmd_linux.cpp APPEND_STRING PROPERTY COMPILE_FLAGS " -Wno-shadow") + endif() if(ENABLE_XLIB) list(APPEND libraries PRIVATE -lX11)