mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-08-22 14:36:35 +00:00
Add some fixes.
This commit is contained in:
+1
-1
@@ -10,7 +10,7 @@ if(APPLE)
|
||||
endif()
|
||||
|
||||
if("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "FreeBSD")
|
||||
set(FreeBSD YES)
|
||||
set(FREEBSD YES)
|
||||
endif()
|
||||
|
||||
# Configure some stuff that needs to be set really early
|
||||
|
||||
+3
-13
@@ -20,7 +20,6 @@
|
||||
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
|
||||
@@ -2725,7 +2724,7 @@ namespace Catch {
|
||||
#define CATCH_TRAP() __asm__(".inst 0xde01")
|
||||
#endif
|
||||
|
||||
#elif defined(CATCH_PLATFORM_LINUX) || defined(__FreeBSD__)
|
||||
#elif defined(CATCH_PLATFORM_LINUX)
|
||||
// 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.
|
||||
@@ -10429,7 +10428,7 @@ namespace Catch {
|
||||
// end catch_debug_console.cpp
|
||||
// start catch_debugger.cpp
|
||||
|
||||
#if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE) || defined(__FreeBSD__)
|
||||
#if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE)
|
||||
|
||||
# include <cassert>
|
||||
# include <sys/types.h>
|
||||
@@ -10441,13 +10440,10 @@ namespace Catch {
|
||||
// These headers will only compile with AppleClang (XCode)
|
||||
// For other compilers (Clang, GCC, ... ) we need to exclude them
|
||||
# include <sys/sysctl.h>
|
||||
#elif defined(__FreeBSD__)
|
||||
# include <sys/user.h>
|
||||
# include <sys/sysctl.h>
|
||||
#endif
|
||||
|
||||
namespace Catch {
|
||||
#if defined(__apple_build_version__) || defined(__FreeBSD__)
|
||||
#ifdef __apple_build_version__
|
||||
// The following function is taken directly from the following technical note:
|
||||
// https://developer.apple.com/library/archive/qa/qa1361/_index.html
|
||||
|
||||
@@ -10458,12 +10454,10 @@ 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.
|
||||
@@ -10483,11 +10477,7 @@ 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() {
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
#include <QStyledItemDelegate>
|
||||
#include "Code/Interface/QRDInterface.h"
|
||||
|
||||
#if !defined(RELEASE)
|
||||
#if !defined(RELEASE) && !defined(__FreeBSD__)
|
||||
#define ENABLE_UNIT_TESTS 1
|
||||
#else
|
||||
#define ENABLE_UNIT_TESTS 0
|
||||
|
||||
+3
-13
@@ -20,7 +20,6 @@
|
||||
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
|
||||
@@ -2725,7 +2724,7 @@ namespace Catch {
|
||||
#define CATCH_TRAP() __asm__(".inst 0xde01")
|
||||
#endif
|
||||
|
||||
#elif defined(CATCH_PLATFORM_LINUX) || defined(__FreeBSD__)
|
||||
#elif defined(CATCH_PLATFORM_LINUX)
|
||||
// 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.
|
||||
@@ -10429,7 +10428,7 @@ namespace Catch {
|
||||
// end catch_debug_console.cpp
|
||||
// start catch_debugger.cpp
|
||||
|
||||
#if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE) || defined(__FreeBSD__)
|
||||
#if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE)
|
||||
|
||||
# include <cassert>
|
||||
# include <sys/types.h>
|
||||
@@ -10441,13 +10440,10 @@ namespace Catch {
|
||||
// These headers will only compile with AppleClang (XCode)
|
||||
// For other compilers (Clang, GCC, ... ) we need to exclude them
|
||||
# include <sys/sysctl.h>
|
||||
#elif defined(__FreeBSD__)
|
||||
# include <sys/user.h>
|
||||
# include <sys/sysctl.h>
|
||||
#endif
|
||||
|
||||
namespace Catch {
|
||||
#if defined(__apple_build_version__) || defined(__FreeBSD__)
|
||||
#ifdef __apple_build_version__
|
||||
// The following function is taken directly from the following technical note:
|
||||
// https://developer.apple.com/library/archive/qa/qa1361/_index.html
|
||||
|
||||
@@ -10458,12 +10454,10 @@ 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.
|
||||
@@ -10483,11 +10477,7 @@ 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() {
|
||||
|
||||
@@ -59,7 +59,7 @@ elseif(UNIX)
|
||||
PRIVATE -ldl
|
||||
PRIVATE -lrt)
|
||||
|
||||
if(FreeBSD)
|
||||
if(FREEBSD)
|
||||
list(APPEND RDOC_LIBRARIES
|
||||
PRIVATE -L/usr/local/lib
|
||||
PRIVATE -liconv
|
||||
@@ -338,15 +338,15 @@ elseif(ENABLE_GGP)
|
||||
os/posix/posix_stringio.cpp
|
||||
os/posix/posix_threading.cpp
|
||||
os/posix/posix_specific.h)
|
||||
elseif(FreeBSD)
|
||||
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
|
||||
os/posix/bsd/bsd_stringio.cpp
|
||||
os/posix/bsd/bsd_callstack.cpp
|
||||
os/posix/bsd/bsd_threading.cpp
|
||||
os/posix/bsd/bsd_process.cpp
|
||||
os/posix/bsd/bsd_hook.cpp
|
||||
os/posix/bsd/bsd_network.cpp
|
||||
3rdparty/plthook/plthook.h
|
||||
3rdparty/plthook/plthook_elf.c
|
||||
os/posix/posix_network.h
|
||||
|
||||
@@ -226,8 +226,8 @@ enum
|
||||
// this strips them completely
|
||||
#define STRIP_DEBUG_LOGS OPTION_OFF
|
||||
|
||||
// disable unit tests on android
|
||||
#if ENABLED(RDOC_ANDROID)
|
||||
// disable unit tests on android and *BSD
|
||||
#if ENABLED(RDOC_ANDROID) || defined(__FreeBSD__)
|
||||
|
||||
#define ENABLE_UNIT_TESTS OPTION_OFF
|
||||
|
||||
|
||||
@@ -84,9 +84,6 @@ void GLResourceManager::MarkFBOAttachmentsReferenced(ResourceId fboid, GLResourc
|
||||
{
|
||||
FBOCache *cache = m_FBOAttachmentsCache[fboid];
|
||||
|
||||
if(!record)
|
||||
return;
|
||||
|
||||
if(!cache)
|
||||
{
|
||||
cache = m_FBOAttachmentsCache[fboid] = new FBOCache;
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
/******************************************************************************
|
||||
* 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 "common/common.h"
|
||||
#include "os/os_specific.h"
|
||||
|
||||
class BSDCallstack : public Callstack::Stackwalk
|
||||
{
|
||||
public:
|
||||
BSDCallstack()
|
||||
{
|
||||
RDCEraseEl(addrs);
|
||||
numLevels = 0;
|
||||
}
|
||||
BSDCallstack(uint64_t *calls, size_t num) { Set(calls, num); }
|
||||
~BSDCallstack() {}
|
||||
void Set(uint64_t *calls, size_t num)
|
||||
{
|
||||
numLevels = num;
|
||||
for(size_t i = 0; i < numLevels; i++)
|
||||
addrs[i] = calls[i];
|
||||
}
|
||||
|
||||
size_t NumLevels() const { return 0; }
|
||||
const uint64_t *GetAddrs() const { return addrs; }
|
||||
private:
|
||||
BSDCallstack(const Callstack::Stackwalk &other);
|
||||
|
||||
uint64_t addrs[128];
|
||||
size_t numLevels;
|
||||
};
|
||||
|
||||
namespace Callstack
|
||||
{
|
||||
void Init()
|
||||
{
|
||||
}
|
||||
|
||||
Stackwalk *Collect()
|
||||
{
|
||||
return new BSDCallstack();
|
||||
}
|
||||
|
||||
Stackwalk *Create()
|
||||
{
|
||||
return new BSDCallstack(NULL, 0);
|
||||
}
|
||||
|
||||
bool GetLoadedModules(byte *buf, size_t &size)
|
||||
{
|
||||
size = 0;
|
||||
|
||||
if(buf)
|
||||
memcpy(buf, "BSD_CALL", 8);
|
||||
|
||||
size += 8;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
StackResolver *MakeResolver(bool interactive, byte *moduleDB, size_t DBSize,
|
||||
RENDERDOC_ProgressCallback progress)
|
||||
{
|
||||
RDCERR("Callstack resolving not supported on *BSD.");
|
||||
return NULL;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,556 @@
|
||||
/******************************************************************************
|
||||
* 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 <dlfcn.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include "common/threading.h"
|
||||
#include "core/core.h"
|
||||
#include "core/settings.h"
|
||||
#include "hooks/hooks.h"
|
||||
#include "os/os_specific.h"
|
||||
#include "plthook/plthook.h"
|
||||
#include "strings/string_utils.h"
|
||||
|
||||
Threading::CriticalSection libLock;
|
||||
|
||||
static std::map<rdcstr, rdcarray<FunctionLoadCallback>> libraryCallbacks;
|
||||
static rdcarray<rdcstr> libraryHooks;
|
||||
static rdcarray<FunctionHook> functionHooks;
|
||||
|
||||
void *intercept_dlopen(const char *filename, int flag, void *ret);
|
||||
void plthook_lib(void *handle);
|
||||
|
||||
typedef pid_t (*FORKPROC)();
|
||||
typedef int (*EXECLEPROC)(const char *pathname, const char *arg, ...);
|
||||
typedef int (*EXECVEPROC)(const char *pathname, char *const argv[], char *const envp[]);
|
||||
typedef int (*EXECVPEPROC)(const char *pathname, char *const argv[], char *const envp[]);
|
||||
typedef void *(*DLOPENPROC)(const char *, int);
|
||||
typedef void *(*DLSYMPROC)(void *, const char *);
|
||||
DLOPENPROC realdlopen = NULL;
|
||||
EXECLEPROC realexecle = NULL;
|
||||
EXECVEPROC realexecve = NULL;
|
||||
EXECVPEPROC realexecvpe = NULL;
|
||||
FORKPROC realfork = NULL;
|
||||
DLSYMPROC realdlsym = NULL;
|
||||
|
||||
static int32_t tlsbusyflag = 0;
|
||||
|
||||
__attribute__((visibility("default"))) void *dlopen(const char *filename, int flag)
|
||||
{
|
||||
if(!realdlopen)
|
||||
{
|
||||
DLOPENPROC passthru = (DLOPENPROC)dlsym(RTLD_NEXT, "dlopen");
|
||||
|
||||
void *ret = passthru(filename, flag);
|
||||
|
||||
if(filename && ret && (flag & RTLD_DEEPBIND))
|
||||
plthook_lib(ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
if(RenderDoc::Inst().IsReplayApp())
|
||||
return realdlopen(filename, flag);
|
||||
|
||||
// don't do any hook processing inside here even if we call dlopen again
|
||||
Atomic::Inc32(&tlsbusyflag);
|
||||
void *ret = realdlopen(filename, flag);
|
||||
Atomic::Dec32(&tlsbusyflag);
|
||||
|
||||
if(filename && ret)
|
||||
{
|
||||
SCOPED_LOCK(libLock);
|
||||
ret = intercept_dlopen(filename, flag, ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int GetIdentPort(pid_t childPid);
|
||||
|
||||
void PreForkConfigureHooks();
|
||||
void GetUnhookedEnvp(char *const *envp, rdcstr &envpStr, rdcarray<char *> &modifiedEnv);
|
||||
void GetHookedEnvp(char *const *envp, rdcstr &envpStr, rdcarray<char *> &modifiedEnv);
|
||||
void ResetHookingEnvVars();
|
||||
void StopAtMainInChild();
|
||||
bool StopChildAtMain(pid_t childPid, bool *exitWithNoExec);
|
||||
void ResumeProcess(pid_t childPid, uint32_t delay = 0);
|
||||
int direct_setenv(const char *name, const char *value, int overwrite);
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
// exec hooks - we have to hook each variant since if the application calls the 'real' one of a
|
||||
// variant, even if it ultimately goes to execve it will be resolved to the real libc one as libc
|
||||
// doesn't get LD_PRELOAD hooked.
|
||||
//
|
||||
// There are two 'real' implementations we have, execve and execvpe that forward to the real
|
||||
// function after patching the environment. That's to allow the real function to handle the path
|
||||
// handling in the vpe case, otherwise they're identical.
|
||||
//
|
||||
// The other variants all forward to one of those - the 'l' cases unroll the va_args first before
|
||||
// calling onwards
|
||||
|
||||
#if 1 // TODO: environ bug
|
||||
char **GetCurrentEnvironment();
|
||||
#define environ GetCurrentEnvironment()
|
||||
|
||||
#define GET_EXECL_PARAMS(has_e) \
|
||||
va_list args; \
|
||||
va_start(args, arg); \
|
||||
\
|
||||
rdcarray<char *> arglist; \
|
||||
arglist.push_back((char *)arg); \
|
||||
\
|
||||
while(true) \
|
||||
{ \
|
||||
char *nextArg = va_arg(args, char *); \
|
||||
arglist.push_back(nextArg); \
|
||||
\
|
||||
/* list is terminated with a NULL */ \
|
||||
if(!nextArg) \
|
||||
break; \
|
||||
} \
|
||||
\
|
||||
char **envp = NULL; \
|
||||
if(has_e) \
|
||||
envp = va_arg(args, char **); \
|
||||
\
|
||||
va_end(args);
|
||||
|
||||
__attribute__((visibility("default"))) int execl(const char *pathname, const char *arg, ...)
|
||||
{
|
||||
GET_EXECL_PARAMS(false);
|
||||
|
||||
return execve(pathname, arglist.data(), environ);
|
||||
}
|
||||
|
||||
__attribute__((visibility("default"))) int execle(const char *pathname, const char *arg, ...)
|
||||
{
|
||||
GET_EXECL_PARAMS(true);
|
||||
|
||||
return execve(pathname, arglist.data(), envp);
|
||||
}
|
||||
|
||||
__attribute__((visibility("default"))) int execv(const char *pathname, char *const argv[])
|
||||
{
|
||||
return execve(pathname, argv, environ);
|
||||
}
|
||||
#endif
|
||||
|
||||
__attribute__((visibility("default"))) int execve(const char *pathname, char *const argv[],
|
||||
char *const envp[])
|
||||
{
|
||||
if(!realexecve)
|
||||
{
|
||||
EXECVEPROC passthru = (EXECVEPROC)dlsym(RTLD_NEXT, "execve");
|
||||
return passthru(pathname, argv, envp);
|
||||
}
|
||||
|
||||
if(RenderDoc::Inst().IsReplayApp())
|
||||
return realexecve(pathname, argv, envp);
|
||||
|
||||
rdcarray<char *> modifiedEnv;
|
||||
rdcstr envpStr;
|
||||
|
||||
// if we're not hooking children just call to the real one, but ensure we remove any hooking env
|
||||
// vars that were kept around after initialisation
|
||||
if(!RenderDoc::Inst().GetCaptureOptions().hookIntoChildren)
|
||||
{
|
||||
GetUnhookedEnvp(envp, envpStr, modifiedEnv);
|
||||
return realexecve(pathname, argv, modifiedEnv.data());
|
||||
}
|
||||
|
||||
GetHookedEnvp(envp, envpStr, modifiedEnv);
|
||||
return realexecve(pathname, argv, modifiedEnv.data());
|
||||
}
|
||||
|
||||
__attribute__((visibility("default"))) int execvpe(const char *pathname, char *const argv[],
|
||||
char *const envp[])
|
||||
{
|
||||
if(!realexecvpe)
|
||||
{
|
||||
EXECVPEPROC passthru = (EXECVPEPROC)dlsym(RTLD_NEXT, "execvpe");
|
||||
return passthru(pathname, argv, envp);
|
||||
}
|
||||
|
||||
if(RenderDoc::Inst().IsReplayApp())
|
||||
return realexecvpe(pathname, argv, envp);
|
||||
|
||||
rdcarray<char *> modifiedEnv;
|
||||
rdcstr envpStr;
|
||||
|
||||
// if we're not hooking children just call to the real one, but ensure we remove any hooking env
|
||||
// vars that were kept around after initialisation
|
||||
if(!RenderDoc::Inst().GetCaptureOptions().hookIntoChildren)
|
||||
{
|
||||
GetUnhookedEnvp(envp, envpStr, modifiedEnv);
|
||||
return realexecvpe(pathname, argv, modifiedEnv.data());
|
||||
}
|
||||
|
||||
GetHookedEnvp(envp, envpStr, modifiedEnv);
|
||||
return realexecvpe(pathname, argv, modifiedEnv.data());
|
||||
}
|
||||
|
||||
__attribute__((visibility("default"))) pid_t fork()
|
||||
{
|
||||
if(!realfork)
|
||||
{
|
||||
FORKPROC passthru = (FORKPROC)dlsym(RTLD_NEXT, "fork");
|
||||
return passthru();
|
||||
}
|
||||
|
||||
if(RenderDoc::Inst().IsReplayApp())
|
||||
return realfork();
|
||||
|
||||
// if we're not hooking children just call to the real one, we don't have to do anything
|
||||
if(!RenderDoc::Inst().GetCaptureOptions().hookIntoChildren)
|
||||
{
|
||||
// this is a nasty hack. We set this env var when we inject into a process, but because we don't
|
||||
// know when vulkan may be initialised we need to leave it on indefinitely. If we're not
|
||||
// injecting into children we need to unset this variable so it doesn't get inherited.
|
||||
//
|
||||
// Note this does nothing if the application is doing fork + execve or other variant that passes
|
||||
// envp because it was probably fetched before the fork - see the exec hooks for where we patch
|
||||
// that part.
|
||||
|
||||
pid_t ret = realfork();
|
||||
if(ret == 0)
|
||||
direct_setenv(RENDERDOC_VULKAN_LAYER_VAR, "", true);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// fork in a captured application. Need to get the child ident and register it
|
||||
|
||||
// set up environment variables for hooking now
|
||||
PreForkConfigureHooks();
|
||||
|
||||
pid_t ret = realfork();
|
||||
|
||||
if(ret == 0)
|
||||
{
|
||||
StopAtMainInChild();
|
||||
}
|
||||
else if(ret > 0)
|
||||
{
|
||||
// restore environment variables
|
||||
ResetHookingEnvVars();
|
||||
|
||||
bool exitWithNoExec = false;
|
||||
bool stopped = StopChildAtMain(ret, &exitWithNoExec);
|
||||
|
||||
if(!exitWithNoExec && stopped)
|
||||
{
|
||||
int ident = GetIdentPort(ret);
|
||||
|
||||
ResumeProcess(ret);
|
||||
|
||||
if(ident)
|
||||
{
|
||||
RDCLOG("Identified child process %u with ident %u", ret, ident);
|
||||
RenderDoc::Inst().AddChildProcess((uint32_t)ret, (uint32_t)ident);
|
||||
}
|
||||
else
|
||||
{
|
||||
RDCERR("Couldn't get ident for PID %u after stopping at main", ret);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// resume the process just in case something went wrong. This should be harmless if we're not
|
||||
// actually tracing
|
||||
ResumeProcess(ret);
|
||||
|
||||
// ptrace_scope isn't amenable, or we hit an error. We'll have to spin up a thread to check
|
||||
// the ident on the child process and add it as soon as it's available
|
||||
Threading::ThreadHandle handle = Threading::CreateThread([ret]() {
|
||||
RDCLOG("Starting thread to get ident for PID %u", ret);
|
||||
|
||||
// don't accept a return value of our own ident, that means we've checked too early and exec
|
||||
// hasn't run yet
|
||||
const uint32_t ownIdent = RenderDoc::Inst().GetTargetControlIdent();
|
||||
uint32_t ident = ownIdent;
|
||||
for(uint32_t i = 0; i < 10 && ident == ownIdent; i++)
|
||||
{
|
||||
ident = (uint32_t)GetIdentPort(ret);
|
||||
if(ident == ownIdent)
|
||||
usleep(1000);
|
||||
}
|
||||
|
||||
if(ident == ownIdent)
|
||||
ident = 0;
|
||||
|
||||
RDCLOG("PID %u has ident %u", ret, ident);
|
||||
|
||||
RenderDoc::Inst().AddChildProcess((uint32_t)ret, (uint32_t)ident);
|
||||
RenderDoc::Inst().CompleteChildThread((uint32_t)ret);
|
||||
});
|
||||
RenderDoc::Inst().AddChildThread((uint32_t)ret, handle);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if defined(RENDERDOC_HOOK_DLSYM)
|
||||
|
||||
#pragma message("ALERT: dlsym() hooking enabled! This is unreliable & relies on glibc internals.")
|
||||
|
||||
extern "C" {
|
||||
|
||||
__attribute__((visibility("default"))) void *dlsym(void *handle, const char *name);
|
||||
|
||||
extern void *_dl_sym(void *, const char *, void *);
|
||||
|
||||
void bootstrap_dlsym()
|
||||
{
|
||||
realdlsym = (DLSYMPROC)_dl_sym(RTLD_NEXT, "dlsym", (void *)&dlsym);
|
||||
}
|
||||
|
||||
__attribute__((visibility("default"))) void *dlsym(void *handle, const char *name)
|
||||
{
|
||||
if(!strcmp(name, "dlsym"))
|
||||
return (void *)&dlsym;
|
||||
|
||||
if(!strcmp(name, "dlopen") && realdlopen)
|
||||
return (void *)&dlopen;
|
||||
|
||||
if(realdlsym == NULL)
|
||||
bootstrap_dlsym();
|
||||
|
||||
if(realdlsym == NULL)
|
||||
{
|
||||
fprintf(stderr, "Couldn't get onwards dlsym in hooked dlsym\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
return realdlsym(handle, name);
|
||||
}
|
||||
|
||||
}; // extern "C"
|
||||
|
||||
#endif
|
||||
|
||||
void plthook_lib(void *handle)
|
||||
{
|
||||
plthook_t *plthook = NULL;
|
||||
|
||||
// minimal error handling as we can't do much more than log the error, and since this is
|
||||
// 'best-effort' attempt to hook the unhookable, we just try and allow it to fail.
|
||||
if(plthook_open_by_handle(&plthook, handle))
|
||||
return;
|
||||
|
||||
plthook_replace(plthook, "dlopen", (void *)dlopen, NULL);
|
||||
|
||||
for(FunctionHook &hook : functionHooks)
|
||||
{
|
||||
void *orig = NULL;
|
||||
plthook_replace(plthook, hook.function.c_str(), hook.hook, &orig);
|
||||
if(hook.orig && *hook.orig == NULL && orig)
|
||||
*hook.orig = orig;
|
||||
}
|
||||
|
||||
plthook_close(plthook);
|
||||
}
|
||||
|
||||
// multiple libraries names pointing at the same file are declared as hooks
|
||||
// in this case, if the second version gets loaded or when CheckLoadedLibraries is run,
|
||||
// hooks are run another time. Avoid this by clearing callbacks of hooks pointing at the same
|
||||
// library
|
||||
static void PreventDoubleHook(const void *loadedHandle)
|
||||
{
|
||||
for(auto it = libraryHooks.begin(); it != libraryHooks.end(); ++it)
|
||||
{
|
||||
rdcstr libName = *it;
|
||||
// if callbacks are empty, there is no risk of executing anything
|
||||
if(libraryCallbacks[libName].empty())
|
||||
continue;
|
||||
|
||||
void *handle = realdlopen(libName.c_str(), RTLD_NOW | RTLD_NOLOAD | RTLD_GLOBAL);
|
||||
if(handle == loadedHandle)
|
||||
{
|
||||
// we have been loaded by a different name, don't run hooks again
|
||||
libraryCallbacks[libName].clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void CheckLoadedLibraries()
|
||||
{
|
||||
// don't process anything if the busy flag was set, otherwise set it ourselves
|
||||
if(Atomic::CmpExch32(&tlsbusyflag, 0, 1) != 0)
|
||||
return;
|
||||
|
||||
// iterate over the libraries and see which ones are already loaded, process function hooks for
|
||||
// them and call callbacks.
|
||||
for(auto it = libraryHooks.begin(); it != libraryHooks.end(); ++it)
|
||||
{
|
||||
rdcstr libName = *it;
|
||||
void *handle = realdlopen(libName.c_str(), RTLD_NOW | RTLD_NOLOAD | RTLD_GLOBAL);
|
||||
|
||||
if(handle)
|
||||
{
|
||||
for(FunctionHook &hook : functionHooks)
|
||||
{
|
||||
if(hook.orig && *hook.orig == NULL)
|
||||
*hook.orig = dlsym(handle, hook.function.c_str());
|
||||
}
|
||||
|
||||
rdcarray<FunctionLoadCallback> callbacks;
|
||||
|
||||
// don't call callbacks again if the library is dlopen'd again
|
||||
libraryCallbacks[libName].swap(callbacks);
|
||||
PreventDoubleHook(handle);
|
||||
|
||||
for(FunctionLoadCallback cb : callbacks)
|
||||
if(cb)
|
||||
cb(handle);
|
||||
}
|
||||
}
|
||||
|
||||
// clear any dl errors in case twitchy applications get set off by false positive errors.
|
||||
dlerror();
|
||||
|
||||
// decrement the flag counter
|
||||
Atomic::Dec32(&tlsbusyflag);
|
||||
}
|
||||
|
||||
void *intercept_dlopen(const char *filename, int flag, void *ret)
|
||||
{
|
||||
if(filename == NULL)
|
||||
return ret;
|
||||
|
||||
if(flag & RTLD_DEEPBIND)
|
||||
plthook_lib(ret);
|
||||
|
||||
rdcstr base = get_basename(filename);
|
||||
|
||||
for(auto it = libraryHooks.begin(); it != libraryHooks.end(); ++it)
|
||||
{
|
||||
const rdcstr &libName = *it;
|
||||
if(*it == base)
|
||||
{
|
||||
RDCDEBUG("Redirecting dlopen to ourselves for %s", filename);
|
||||
|
||||
for(FunctionHook &hook : functionHooks)
|
||||
{
|
||||
if(hook.orig && *hook.orig == NULL)
|
||||
*hook.orig = dlsym(ret, hook.function.c_str());
|
||||
}
|
||||
|
||||
rdcarray<FunctionLoadCallback> callbacks;
|
||||
|
||||
// don't call callbacks again if the library is dlopen'd again
|
||||
libraryCallbacks[libName].swap(callbacks);
|
||||
PreventDoubleHook(ret);
|
||||
|
||||
for(FunctionLoadCallback cb : callbacks)
|
||||
if(cb)
|
||||
cb(ret);
|
||||
|
||||
ret = realdlopen("lib" STRINGIZE(RDOC_BASE_NAME) ".so", flag);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// this library might depend on one we care about, so check again as we
|
||||
// did in EndHookRegistration to see if any library has been loaded.
|
||||
CheckLoadedLibraries();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void LibraryHooks::ReplayInitialise()
|
||||
{
|
||||
realdlopen = (DLOPENPROC)dlsym(RTLD_NEXT, "dlopen");
|
||||
realfork = (FORKPROC)dlsym(RTLD_NEXT, "fork");
|
||||
realexecle = (EXECLEPROC)dlsym(RTLD_NEXT, "execle");
|
||||
realexecve = (EXECVEPROC)dlsym(RTLD_NEXT, "execve");
|
||||
realexecvpe = (EXECVPEPROC)dlsym(RTLD_NEXT, "execvpe");
|
||||
}
|
||||
|
||||
void LibraryHooks::BeginHookRegistration()
|
||||
{
|
||||
realdlopen = (DLOPENPROC)dlsym(RTLD_NEXT, "dlopen");
|
||||
realfork = (FORKPROC)dlsym(RTLD_NEXT, "fork");
|
||||
realexecle = (EXECLEPROC)dlsym(RTLD_NEXT, "execle");
|
||||
realexecve = (EXECVEPROC)dlsym(RTLD_NEXT, "execve");
|
||||
realexecvpe = (EXECVPEPROC)dlsym(RTLD_NEXT, "execvpe");
|
||||
}
|
||||
|
||||
bool LibraryHooks::Detect(const char *identifier)
|
||||
{
|
||||
return dlsym(RTLD_DEFAULT, identifier) != NULL;
|
||||
}
|
||||
|
||||
void LibraryHooks::RemoveHooks()
|
||||
{
|
||||
RDCERR("Removing hooks is not possible on this platform");
|
||||
}
|
||||
|
||||
void LibraryHooks::EndHookRegistration()
|
||||
{
|
||||
CheckLoadedLibraries();
|
||||
}
|
||||
|
||||
void LibraryHooks::Refresh()
|
||||
{
|
||||
// don't need to refresh on linux
|
||||
}
|
||||
|
||||
void LibraryHooks::RegisterFunctionHook(const char *libraryName, const FunctionHook &hook)
|
||||
{
|
||||
// we don't use the library name
|
||||
(void)libraryName;
|
||||
|
||||
SCOPED_LOCK(libLock);
|
||||
functionHooks.push_back(hook);
|
||||
}
|
||||
|
||||
void LibraryHooks::RegisterLibraryHook(char const *name, FunctionLoadCallback cb)
|
||||
{
|
||||
SCOPED_LOCK(libLock);
|
||||
|
||||
if(!libraryHooks.contains(name))
|
||||
libraryHooks.push_back(name);
|
||||
|
||||
if(cb)
|
||||
libraryCallbacks[name].push_back(cb);
|
||||
}
|
||||
|
||||
void LibraryHooks::IgnoreLibrary(const char *libraryName)
|
||||
{
|
||||
}
|
||||
|
||||
// android only hooking functions, not used on linux
|
||||
ScopedSuppressHooking::ScopedSuppressHooking()
|
||||
{
|
||||
}
|
||||
|
||||
ScopedSuppressHooking::~ScopedSuppressHooking()
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/******************************************************************************
|
||||
* 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 "os/os_specific.h"
|
||||
#include "os/posix/posix_network.h"
|
||||
|
||||
namespace Network
|
||||
{
|
||||
void SocketPostSend()
|
||||
{
|
||||
// only needed for awful hack on Android
|
||||
}
|
||||
|
||||
uint32_t Socket::GetRemoteIP() const
|
||||
{
|
||||
return GetIPFromTCPSocket((int)socket);
|
||||
}
|
||||
|
||||
Socket *CreateServerSocket(const rdcstr &bindaddr, uint16_t port, int queuesize)
|
||||
{
|
||||
return CreateTCPServerSocket(bindaddr, port, queuesize);
|
||||
}
|
||||
};
|
||||
+4
-1
@@ -24,8 +24,11 @@
|
||||
|
||||
#include <dlfcn.h> // for dlsym
|
||||
#include <stdlib.h>
|
||||
#include <sys/sysctl.h>
|
||||
// clang-format off
|
||||
// sys/types.h needs to be included before sys/sysctl.h
|
||||
#include <sys/types.h>
|
||||
#include <sys/sysctl.h>
|
||||
// clang-format on
|
||||
#include <sys/user.h>
|
||||
#include <unistd.h>
|
||||
#include "common/common.h"
|
||||
@@ -0,0 +1,809 @@
|
||||
/******************************************************************************
|
||||
* 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 <ctype.h>
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <iconv.h>
|
||||
#include <pwd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <set>
|
||||
#include "api/app/renderdoc_app.h"
|
||||
#include "api/replay/replay_enums.h"
|
||||
#include "common/common.h"
|
||||
#include "common/threading.h"
|
||||
#include "os/os_specific.h"
|
||||
#include "strings/string_utils.h"
|
||||
|
||||
#if ENABLED(RDOC_XLIB)
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/keysym.h>
|
||||
#else
|
||||
typedef struct _XDisplay Display;
|
||||
#endif
|
||||
|
||||
#if ENABLED(RDOC_XCB)
|
||||
#include <X11/keysym.h>
|
||||
#include <xcb/xcb_keysyms.h>
|
||||
#else
|
||||
struct xcb_connection_t;
|
||||
#endif
|
||||
|
||||
#if ENABLED(RDOC_WAYLAND)
|
||||
#include <linux/input.h>
|
||||
#include <wayland-client.h>
|
||||
#include <map>
|
||||
#else
|
||||
struct wl_display;
|
||||
struct wl_surface;
|
||||
#endif
|
||||
|
||||
namespace Keyboard
|
||||
{
|
||||
void Init()
|
||||
{
|
||||
}
|
||||
|
||||
#if ENABLED(RDOC_XLIB)
|
||||
|
||||
Display *CurrentXDisplay = NULL;
|
||||
|
||||
void UseXlibDisplay(Display *dpy)
|
||||
{
|
||||
if(CurrentXDisplay || dpy == NULL)
|
||||
return;
|
||||
|
||||
CurrentXDisplay = XOpenDisplay(XDisplayString(dpy));
|
||||
}
|
||||
|
||||
bool HasXlibInput()
|
||||
{
|
||||
return CurrentXDisplay != NULL;
|
||||
}
|
||||
|
||||
bool GetXlibKeyState(int key)
|
||||
{
|
||||
if(CurrentXDisplay == NULL)
|
||||
return false;
|
||||
|
||||
KeySym ks = 0;
|
||||
|
||||
if(key >= eRENDERDOC_Key_A && key <= eRENDERDOC_Key_Z)
|
||||
ks = key;
|
||||
if(key >= eRENDERDOC_Key_0 && key <= eRENDERDOC_Key_9)
|
||||
ks = key;
|
||||
|
||||
switch(key)
|
||||
{
|
||||
case eRENDERDOC_Key_Divide: ks = XK_KP_Divide; break;
|
||||
case eRENDERDOC_Key_Multiply: ks = XK_KP_Multiply; break;
|
||||
case eRENDERDOC_Key_Subtract: ks = XK_KP_Subtract; break;
|
||||
case eRENDERDOC_Key_Plus: ks = XK_KP_Add; break;
|
||||
case eRENDERDOC_Key_F1: ks = XK_F1; break;
|
||||
case eRENDERDOC_Key_F2: ks = XK_F2; break;
|
||||
case eRENDERDOC_Key_F3: ks = XK_F3; break;
|
||||
case eRENDERDOC_Key_F4: ks = XK_F4; break;
|
||||
case eRENDERDOC_Key_F5: ks = XK_F5; break;
|
||||
case eRENDERDOC_Key_F6: ks = XK_F6; break;
|
||||
case eRENDERDOC_Key_F7: ks = XK_F7; break;
|
||||
case eRENDERDOC_Key_F8: ks = XK_F8; break;
|
||||
case eRENDERDOC_Key_F9: ks = XK_F9; break;
|
||||
case eRENDERDOC_Key_F10: ks = XK_F10; break;
|
||||
case eRENDERDOC_Key_F11: ks = XK_F11; break;
|
||||
case eRENDERDOC_Key_F12: ks = XK_F12; break;
|
||||
case eRENDERDOC_Key_Home: ks = XK_Home; break;
|
||||
case eRENDERDOC_Key_End: ks = XK_End; break;
|
||||
case eRENDERDOC_Key_Insert: ks = XK_Insert; break;
|
||||
case eRENDERDOC_Key_Delete: ks = XK_Delete; break;
|
||||
case eRENDERDOC_Key_PageUp: ks = XK_Prior; break;
|
||||
case eRENDERDOC_Key_PageDn: ks = XK_Next; break;
|
||||
case eRENDERDOC_Key_Backspace: ks = XK_BackSpace; break;
|
||||
case eRENDERDOC_Key_Tab: ks = XK_Tab; break;
|
||||
case eRENDERDOC_Key_PrtScrn: ks = XK_Print; break;
|
||||
case eRENDERDOC_Key_Pause: ks = XK_Pause; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
if(ks == 0)
|
||||
return false;
|
||||
|
||||
KeyCode kc = XKeysymToKeycode(CurrentXDisplay, ks);
|
||||
|
||||
char keyState[32];
|
||||
XQueryKeymap(CurrentXDisplay, keyState);
|
||||
|
||||
int byteIdx = (kc / 8);
|
||||
int bitMask = 1 << (kc % 8);
|
||||
|
||||
uint8_t keyByte = (uint8_t)keyState[byteIdx];
|
||||
|
||||
return (keyByte & bitMask) != 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// if RENDERDOC_WINDOWING_XLIB is not enabled
|
||||
|
||||
void UseXlibDisplay(Display *dpy)
|
||||
{
|
||||
}
|
||||
|
||||
bool HasXlibInput()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GetXlibKeyState(int key)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if ENABLED(RDOC_XCB)
|
||||
|
||||
xcb_connection_t *connection;
|
||||
xcb_key_symbols_t *symbols;
|
||||
|
||||
void UseXcbConnection(xcb_connection_t *conn)
|
||||
{
|
||||
connection = conn;
|
||||
symbols = xcb_key_symbols_alloc(conn);
|
||||
}
|
||||
|
||||
bool HasXCBInput()
|
||||
{
|
||||
return symbols != NULL;
|
||||
}
|
||||
|
||||
bool GetXCBKeyState(int key)
|
||||
{
|
||||
if(symbols == NULL)
|
||||
return false;
|
||||
|
||||
xcb_keysym_t ks = 0;
|
||||
|
||||
if(key >= eRENDERDOC_Key_A && key <= eRENDERDOC_Key_Z)
|
||||
ks = key;
|
||||
if(key >= eRENDERDOC_Key_0 && key <= eRENDERDOC_Key_9)
|
||||
ks = key;
|
||||
|
||||
switch(key)
|
||||
{
|
||||
case eRENDERDOC_Key_Divide: ks = XK_KP_Divide; break;
|
||||
case eRENDERDOC_Key_Multiply: ks = XK_KP_Multiply; break;
|
||||
case eRENDERDOC_Key_Subtract: ks = XK_KP_Subtract; break;
|
||||
case eRENDERDOC_Key_Plus: ks = XK_KP_Add; break;
|
||||
case eRENDERDOC_Key_F1: ks = XK_F1; break;
|
||||
case eRENDERDOC_Key_F2: ks = XK_F2; break;
|
||||
case eRENDERDOC_Key_F3: ks = XK_F3; break;
|
||||
case eRENDERDOC_Key_F4: ks = XK_F4; break;
|
||||
case eRENDERDOC_Key_F5: ks = XK_F5; break;
|
||||
case eRENDERDOC_Key_F6: ks = XK_F6; break;
|
||||
case eRENDERDOC_Key_F7: ks = XK_F7; break;
|
||||
case eRENDERDOC_Key_F8: ks = XK_F8; break;
|
||||
case eRENDERDOC_Key_F9: ks = XK_F9; break;
|
||||
case eRENDERDOC_Key_F10: ks = XK_F10; break;
|
||||
case eRENDERDOC_Key_F11: ks = XK_F11; break;
|
||||
case eRENDERDOC_Key_F12: ks = XK_F12; break;
|
||||
case eRENDERDOC_Key_Home: ks = XK_Home; break;
|
||||
case eRENDERDOC_Key_End: ks = XK_End; break;
|
||||
case eRENDERDOC_Key_Insert: ks = XK_Insert; break;
|
||||
case eRENDERDOC_Key_Delete: ks = XK_Delete; break;
|
||||
case eRENDERDOC_Key_PageUp: ks = XK_Prior; break;
|
||||
case eRENDERDOC_Key_PageDn: ks = XK_Next; break;
|
||||
case eRENDERDOC_Key_Backspace: ks = XK_BackSpace; break;
|
||||
case eRENDERDOC_Key_Tab: ks = XK_Tab; break;
|
||||
case eRENDERDOC_Key_PrtScrn: ks = XK_Print; break;
|
||||
case eRENDERDOC_Key_Pause: ks = XK_Pause; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
if(ks == 0)
|
||||
return false;
|
||||
|
||||
xcb_keycode_t *keyCodes = xcb_key_symbols_get_keycode(symbols, ks);
|
||||
|
||||
if(!keyCodes)
|
||||
return false;
|
||||
|
||||
xcb_query_keymap_cookie_t keymapcookie = xcb_query_keymap(connection);
|
||||
xcb_query_keymap_reply_t *keys = xcb_query_keymap_reply(connection, keymapcookie, NULL);
|
||||
|
||||
bool ret = false;
|
||||
|
||||
if(keys && keyCodes[0] != XCB_NO_SYMBOL)
|
||||
{
|
||||
int byteIdx = (keyCodes[0] / 8);
|
||||
int bitMask = 1 << (keyCodes[0] % 8);
|
||||
|
||||
ret = (keys->keys[byteIdx] & bitMask) != 0;
|
||||
}
|
||||
|
||||
free(keyCodes);
|
||||
free(keys);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// if RENDERDOC_WINDOWING_XCB is not enabled
|
||||
|
||||
void UseXcbConnection(xcb_connection_t *conn)
|
||||
{
|
||||
}
|
||||
|
||||
bool GetXCBKeyState(int key)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HasXCBInput()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if ENABLED(RDOC_WAYLAND)
|
||||
|
||||
#include <wayland-client.h>
|
||||
|
||||
std::set<wl_display *> displays;
|
||||
std::set<wl_surface *> surfaces;
|
||||
std::map<rdcpair<wl_registry *, uint32_t>, wl_seat *> seatNames;
|
||||
std::map<wl_seat *, wl_keyboard *> seatKeyboard;
|
||||
bool inFocus = false;
|
||||
Threading::CriticalSection waylandLock;
|
||||
|
||||
bool keyState[eRENDERDOC_Key_Max] = {};
|
||||
|
||||
void WaylandKeymapDummy(void *data, wl_keyboard *keyboard, uint32_t format, int fd, uint32_t size)
|
||||
{
|
||||
}
|
||||
|
||||
void WaylandModifiersDummy(void *data, wl_keyboard *keyboard, uint32_t serial,
|
||||
uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked,
|
||||
uint32_t group)
|
||||
{
|
||||
}
|
||||
|
||||
void WaylandRepeatInfoDummy(void *data, wl_keyboard *keyboard, int32_t rate, int32_t delay)
|
||||
{
|
||||
}
|
||||
|
||||
void WaylandEnter(void *data, wl_keyboard *keyboard, uint32_t serial, wl_surface *surf, wl_array *keys)
|
||||
{
|
||||
SCOPED_LOCK(waylandLock);
|
||||
inFocus = surfaces.find(surf) != surfaces.end();
|
||||
RDCEraseEl(keyState);
|
||||
}
|
||||
|
||||
void WaylandLeave(void *data, wl_keyboard *keyboard, uint32_t serial, wl_surface *surf)
|
||||
{
|
||||
SCOPED_LOCK(waylandLock);
|
||||
inFocus = false;
|
||||
RDCEraseEl(keyState);
|
||||
}
|
||||
|
||||
void WaylandKeypress(void *data, wl_keyboard *keyboard, uint32_t serial, uint32_t time,
|
||||
uint32_t key, uint32_t state)
|
||||
{
|
||||
int keyIdx = -1;
|
||||
switch(key)
|
||||
{
|
||||
case KEY_0: keyIdx = eRENDERDOC_Key_0; break;
|
||||
case KEY_1: keyIdx = eRENDERDOC_Key_1; break;
|
||||
case KEY_2: keyIdx = eRENDERDOC_Key_2; break;
|
||||
case KEY_3: keyIdx = eRENDERDOC_Key_3; break;
|
||||
case KEY_4: keyIdx = eRENDERDOC_Key_4; break;
|
||||
case KEY_5: keyIdx = eRENDERDOC_Key_5; break;
|
||||
case KEY_6: keyIdx = eRENDERDOC_Key_6; break;
|
||||
case KEY_7: keyIdx = eRENDERDOC_Key_7; break;
|
||||
case KEY_8: keyIdx = eRENDERDOC_Key_8; break;
|
||||
case KEY_9: keyIdx = eRENDERDOC_Key_9; break;
|
||||
case KEY_A: keyIdx = eRENDERDOC_Key_A; break;
|
||||
case KEY_B: keyIdx = eRENDERDOC_Key_B; break;
|
||||
case KEY_C: keyIdx = eRENDERDOC_Key_C; break;
|
||||
case KEY_D: keyIdx = eRENDERDOC_Key_D; break;
|
||||
case KEY_E: keyIdx = eRENDERDOC_Key_E; break;
|
||||
case KEY_F: keyIdx = eRENDERDOC_Key_F; break;
|
||||
case KEY_G: keyIdx = eRENDERDOC_Key_G; break;
|
||||
case KEY_H: keyIdx = eRENDERDOC_Key_H; break;
|
||||
case KEY_I: keyIdx = eRENDERDOC_Key_I; break;
|
||||
case KEY_J: keyIdx = eRENDERDOC_Key_J; break;
|
||||
case KEY_K: keyIdx = eRENDERDOC_Key_K; break;
|
||||
case KEY_L: keyIdx = eRENDERDOC_Key_L; break;
|
||||
case KEY_M: keyIdx = eRENDERDOC_Key_M; break;
|
||||
case KEY_N: keyIdx = eRENDERDOC_Key_N; break;
|
||||
case KEY_O: keyIdx = eRENDERDOC_Key_O; break;
|
||||
case KEY_P: keyIdx = eRENDERDOC_Key_P; break;
|
||||
case KEY_Q: keyIdx = eRENDERDOC_Key_Q; break;
|
||||
case KEY_R: keyIdx = eRENDERDOC_Key_R; break;
|
||||
case KEY_S: keyIdx = eRENDERDOC_Key_S; break;
|
||||
case KEY_T: keyIdx = eRENDERDOC_Key_T; break;
|
||||
case KEY_U: keyIdx = eRENDERDOC_Key_U; break;
|
||||
case KEY_V: keyIdx = eRENDERDOC_Key_V; break;
|
||||
case KEY_W: keyIdx = eRENDERDOC_Key_W; break;
|
||||
case KEY_X: keyIdx = eRENDERDOC_Key_X; break;
|
||||
case KEY_Y: keyIdx = eRENDERDOC_Key_Y; break;
|
||||
case KEY_Z: keyIdx = eRENDERDOC_Key_Z; break;
|
||||
|
||||
case KEY_KPSLASH: keyIdx = eRENDERDOC_Key_Divide; break;
|
||||
case KEY_KPASTERISK: keyIdx = eRENDERDOC_Key_Multiply; break;
|
||||
case KEY_KPMINUS: keyIdx = eRENDERDOC_Key_Subtract; break;
|
||||
case KEY_KPPLUS: keyIdx = eRENDERDOC_Key_Plus; break;
|
||||
|
||||
case KEY_F1: keyIdx = eRENDERDOC_Key_F1; break;
|
||||
case KEY_F2: keyIdx = eRENDERDOC_Key_F2; break;
|
||||
case KEY_F3: keyIdx = eRENDERDOC_Key_F3; break;
|
||||
case KEY_F4: keyIdx = eRENDERDOC_Key_F4; break;
|
||||
case KEY_F5: keyIdx = eRENDERDOC_Key_F5; break;
|
||||
case KEY_F6: keyIdx = eRENDERDOC_Key_F6; break;
|
||||
case KEY_F7: keyIdx = eRENDERDOC_Key_F7; break;
|
||||
case KEY_F8: keyIdx = eRENDERDOC_Key_F8; break;
|
||||
case KEY_F9: keyIdx = eRENDERDOC_Key_F9; break;
|
||||
case KEY_F10: keyIdx = eRENDERDOC_Key_F10; break;
|
||||
case KEY_F11: keyIdx = eRENDERDOC_Key_F11; break;
|
||||
case KEY_F12: keyIdx = eRENDERDOC_Key_F12; break;
|
||||
|
||||
case KEY_HOME: keyIdx = eRENDERDOC_Key_Home; break;
|
||||
case KEY_END: keyIdx = eRENDERDOC_Key_End; break;
|
||||
case KEY_INSERT: keyIdx = eRENDERDOC_Key_Insert; break;
|
||||
case KEY_DELETE: keyIdx = eRENDERDOC_Key_Delete; break;
|
||||
case KEY_PAGEUP: keyIdx = eRENDERDOC_Key_PageUp; break;
|
||||
case KEY_PAGEDOWN: keyIdx = eRENDERDOC_Key_PageDn; break;
|
||||
|
||||
case KEY_BACKSPACE: keyIdx = eRENDERDOC_Key_Backspace; break;
|
||||
case KEY_TAB: keyIdx = eRENDERDOC_Key_Tab; break;
|
||||
case KEY_SYSRQ: keyIdx = eRENDERDOC_Key_PrtScrn; break;
|
||||
case KEY_PAUSE: keyIdx = eRENDERDOC_Key_Pause; break;
|
||||
}
|
||||
|
||||
if(keyIdx < 0)
|
||||
return;
|
||||
|
||||
{
|
||||
SCOPED_LOCK(waylandLock);
|
||||
keyState[keyIdx] = (state == WL_KEYBOARD_KEY_STATE_PRESSED);
|
||||
}
|
||||
}
|
||||
|
||||
void WaylandSeatCaps(void *data, wl_seat *seat, uint32_t capabilities)
|
||||
{
|
||||
if(capabilities & WL_SEAT_CAPABILITY_KEYBOARD)
|
||||
{
|
||||
{
|
||||
SCOPED_LOCK(waylandLock);
|
||||
if(seatKeyboard[seat])
|
||||
return;
|
||||
}
|
||||
|
||||
wl_keyboard *keyboard = wl_seat_get_keyboard(seat);
|
||||
static const wl_keyboard_listener listener = {
|
||||
WaylandKeymapDummy, WaylandEnter, WaylandLeave,
|
||||
WaylandKeypress, WaylandModifiersDummy, WaylandRepeatInfoDummy,
|
||||
};
|
||||
wl_keyboard_add_listener(keyboard, &listener, NULL);
|
||||
|
||||
{
|
||||
SCOPED_LOCK(waylandLock);
|
||||
seatKeyboard[seat] = keyboard;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
wl_keyboard *keyboard = NULL;
|
||||
|
||||
{
|
||||
SCOPED_LOCK(waylandLock);
|
||||
keyboard = seatKeyboard[seat];
|
||||
|
||||
if(!keyboard)
|
||||
return;
|
||||
|
||||
seatKeyboard[seat] = NULL;
|
||||
}
|
||||
|
||||
wl_keyboard_destroy(keyboard);
|
||||
}
|
||||
}
|
||||
|
||||
void WaylandRegistryAdd(void *data, wl_registry *reg, uint32_t name, const char *iface,
|
||||
uint32_t version)
|
||||
{
|
||||
if(!strcmp(iface, "wl_seat"))
|
||||
{
|
||||
wl_seat *seat = (wl_seat *)wl_registry_bind(reg, name, &wl_seat_interface, 1);
|
||||
static const wl_seat_listener listener = {&WaylandSeatCaps};
|
||||
wl_seat_add_listener(seat, &listener, NULL);
|
||||
|
||||
{
|
||||
SCOPED_LOCK(waylandLock);
|
||||
seatNames[{reg, name}] = seat;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WaylandRegistryRemove(void *data, wl_registry *reg, uint32_t name)
|
||||
{
|
||||
SCOPED_LOCK(waylandLock);
|
||||
auto it = seatNames.find({reg, name});
|
||||
if(it != seatNames.end())
|
||||
{
|
||||
wl_seat_destroy(it->second);
|
||||
seatNames.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void UseWaylandDisplay(wl_display *disp)
|
||||
{
|
||||
// only listen to each display once at most
|
||||
{
|
||||
SCOPED_LOCK(waylandLock);
|
||||
if(displays.find(disp) != displays.end())
|
||||
return;
|
||||
displays.insert(disp);
|
||||
}
|
||||
|
||||
static const wl_registry_listener listener = {&WaylandRegistryAdd, &WaylandRegistryRemove};
|
||||
|
||||
// get the registry and listen to it. This will then let us find seats
|
||||
wl_registry_add_listener(wl_display_get_registry(disp), &listener, NULL);
|
||||
}
|
||||
|
||||
void AddWaylandInputWindow(wl_surface *wnd)
|
||||
{
|
||||
SCOPED_LOCK(waylandLock);
|
||||
surfaces.insert(wnd);
|
||||
}
|
||||
|
||||
void RemoveWaylandInputWindow(wl_surface *wnd)
|
||||
{
|
||||
SCOPED_LOCK(waylandLock);
|
||||
surfaces.erase(wnd);
|
||||
}
|
||||
|
||||
bool HasWaylandInput()
|
||||
{
|
||||
SCOPED_LOCK(waylandLock);
|
||||
return !displays.empty();
|
||||
}
|
||||
|
||||
bool GetWaylandKeyState(int key)
|
||||
{
|
||||
SCOPED_LOCK(waylandLock);
|
||||
return keyState[key];
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void UseWaylandDisplay(wl_display *disp)
|
||||
{
|
||||
}
|
||||
|
||||
void AddWaylandInputWindow(wl_surface *wnd)
|
||||
{
|
||||
}
|
||||
|
||||
void RemoveWaylandInputWindow(wl_surface *wnd)
|
||||
{
|
||||
}
|
||||
|
||||
bool HasWaylandInput()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GetWaylandKeyState(int key)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
WindowingSystem UseUnknownDisplay(void *disp)
|
||||
{
|
||||
if(disp == NULL)
|
||||
return WindowingSystem::Unknown;
|
||||
|
||||
// could be wayland or xlib, try to detect.
|
||||
// both Display* and wl_display* are valid pointers, so dereference and read the first pointer
|
||||
// sized bytes
|
||||
void *firstPointer = NULL;
|
||||
memcpy(&firstPointer, disp, sizeof(void *));
|
||||
|
||||
// in a Display* we don't know what this contains, but in a wl_display it should point to the
|
||||
// wl_display_interface exported symbol. Check with dladdr
|
||||
Dl_info info;
|
||||
if(dladdr(firstPointer, &info) && !strcmp(info.dli_sname, "wl_display_interface"))
|
||||
{
|
||||
UseWaylandDisplay((wl_display *)disp);
|
||||
return WindowingSystem::Wayland;
|
||||
}
|
||||
else
|
||||
{
|
||||
UseXlibDisplay((Display *)disp);
|
||||
return WindowingSystem::Xlib;
|
||||
}
|
||||
}
|
||||
|
||||
void AddInputWindow(WindowingSystem windowSystem, void *wnd)
|
||||
{
|
||||
if(windowSystem == WindowingSystem::Wayland)
|
||||
{
|
||||
AddWaylandInputWindow((wl_surface *)wnd);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO check against this drawable & parent window being focused in GetKeyState
|
||||
}
|
||||
}
|
||||
|
||||
void RemoveInputWindow(WindowingSystem windowSystem, void *wnd)
|
||||
{
|
||||
if(windowSystem == WindowingSystem::Wayland)
|
||||
{
|
||||
RemoveWaylandInputWindow((wl_surface *)wnd);
|
||||
}
|
||||
}
|
||||
|
||||
bool PlatformHasKeyInput()
|
||||
{
|
||||
return HasXCBInput() || HasXlibInput() || HasWaylandInput();
|
||||
}
|
||||
|
||||
bool GetKeyState(int key)
|
||||
{
|
||||
return GetXCBKeyState(key) || GetXlibKeyState(key) || GetWaylandKeyState(key);
|
||||
}
|
||||
}
|
||||
|
||||
namespace FileIO
|
||||
{
|
||||
rdcstr GetTempRootPath()
|
||||
{
|
||||
return "/tmp";
|
||||
}
|
||||
|
||||
rdcstr GetAppFolderFilename(const rdcstr &filename)
|
||||
{
|
||||
passwd *pw = getpwuid(getuid());
|
||||
rdcstr homedir = pw ? pw->pw_dir : "";
|
||||
|
||||
if(homedir.empty())
|
||||
homedir = Process::GetEnvVariable("HOME");
|
||||
|
||||
if(homedir.empty())
|
||||
{
|
||||
RDCERR("Can't get HOME directory, defaulting to '/' instead");
|
||||
homedir = "";
|
||||
}
|
||||
|
||||
rdcstr ret = homedir + "/.renderdoc/";
|
||||
|
||||
mkdir(ret.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
|
||||
|
||||
return ret + filename;
|
||||
}
|
||||
|
||||
rdcstr DefaultFindFileInPath(const rdcstr &fileName);
|
||||
rdcstr FindFileInPath(const rdcstr &fileName)
|
||||
{
|
||||
return DefaultFindFileInPath(fileName);
|
||||
}
|
||||
|
||||
void GetExecutableFilename(rdcstr &selfName)
|
||||
{
|
||||
int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, getpid()};
|
||||
|
||||
char path[512] = {0};
|
||||
size_t size = sizeof(path);
|
||||
|
||||
if(sysctl(mib, ARRAY_COUNT(mib), path, &size, NULL, 0) != 0)
|
||||
{
|
||||
selfName = "/unknown/unknown";
|
||||
RDCERR("Can't get executable name");
|
||||
return; // don't try and readlink this
|
||||
}
|
||||
selfName = rdcstr(path);
|
||||
|
||||
memset(path, 0, sizeof(path));
|
||||
readlink(selfName.c_str(), path, 511);
|
||||
|
||||
if(path[0] != 0)
|
||||
selfName = rdcstr(path);
|
||||
}
|
||||
|
||||
int LibraryLocator = 42;
|
||||
|
||||
void GetLibraryFilename(rdcstr &selfName)
|
||||
{
|
||||
Dl_info info;
|
||||
if(dladdr(&LibraryLocator, &info))
|
||||
{
|
||||
selfName = info.dli_fname;
|
||||
}
|
||||
else
|
||||
{
|
||||
RDCERR("dladdr failed to get library path");
|
||||
selfName = "";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
namespace StringFormat
|
||||
{
|
||||
// cache iconv_t descriptor to save on iconv_open/iconv_close each time
|
||||
iconv_t iconvWide2UTF8 = (iconv_t)-1;
|
||||
iconv_t iconvUTF82Wide = (iconv_t)-1;
|
||||
|
||||
// iconv is not thread safe when sharing an iconv_t descriptor
|
||||
// I don't expect much contention but if it happens we could TryLock
|
||||
// before creating a temporary iconv_t, or hold two iconv_ts, or something.
|
||||
Threading::CriticalSection iconvLock;
|
||||
|
||||
void Shutdown()
|
||||
{
|
||||
SCOPED_LOCK(iconvLock);
|
||||
|
||||
if(iconvWide2UTF8 != (iconv_t)-1)
|
||||
iconv_close(iconvWide2UTF8);
|
||||
iconvWide2UTF8 = (iconv_t)-1;
|
||||
|
||||
if(iconvUTF82Wide != (iconv_t)-1)
|
||||
iconv_close(iconvUTF82Wide);
|
||||
iconvUTF82Wide = (iconv_t)-1;
|
||||
}
|
||||
|
||||
rdcstr Wide2UTF8(const rdcwstr &s)
|
||||
{
|
||||
// include room for null terminator, assuming unicode input (not ucs)
|
||||
// utf-8 characters can be max 4 bytes.
|
||||
size_t len = (s.length() + 1) * 4;
|
||||
|
||||
rdcarray<char> charBuffer;
|
||||
charBuffer.resize(len);
|
||||
|
||||
size_t ret;
|
||||
|
||||
{
|
||||
SCOPED_LOCK(iconvLock);
|
||||
|
||||
if(iconvWide2UTF8 == (iconv_t)-1)
|
||||
iconvWide2UTF8 = iconv_open("UTF-8", "WCHAR_T");
|
||||
|
||||
if(iconvWide2UTF8 == (iconv_t)-1)
|
||||
{
|
||||
RDCERR("Couldn't open iconv for WCHAR_T to UTF-8: %d", errno);
|
||||
return "";
|
||||
}
|
||||
|
||||
char *inbuf = (char *)s.c_str();
|
||||
size_t insize = (s.length() + 1) * sizeof(wchar_t); // include null terminator
|
||||
char *outbuf = &charBuffer[0];
|
||||
size_t outsize = len;
|
||||
|
||||
ret = iconv(iconvWide2UTF8, &inbuf, &insize, &outbuf, &outsize);
|
||||
}
|
||||
|
||||
if(ret == (size_t)-1)
|
||||
{
|
||||
#if ENABLED(RDOC_DEVEL)
|
||||
RDCWARN("Failed to convert wstring");
|
||||
#endif
|
||||
return "";
|
||||
}
|
||||
|
||||
// convert to string from null-terminated string - utf-8 never contains
|
||||
// 0 bytes before the null terminator, and this way we don't care if
|
||||
// charBuffer is larger than the string
|
||||
return rdcstr(&charBuffer[0]);
|
||||
}
|
||||
|
||||
rdcwstr UTF82Wide(const rdcstr &s)
|
||||
{
|
||||
// include room for null terminator, for ascii input we need at least as many output chars as
|
||||
// input.
|
||||
size_t len = s.length() + 1;
|
||||
|
||||
rdcarray<wchar_t> wcharBuffer;
|
||||
wcharBuffer.resize(len);
|
||||
|
||||
size_t ret;
|
||||
|
||||
{
|
||||
SCOPED_LOCK(iconvLock);
|
||||
|
||||
if(iconvUTF82Wide == (iconv_t)-1)
|
||||
iconvUTF82Wide = iconv_open("WCHAR_T", "UTF-8");
|
||||
|
||||
if(iconvUTF82Wide == (iconv_t)-1)
|
||||
{
|
||||
RDCERR("Couldn't open iconv for UTF-8 to WCHAR_T: %d", errno);
|
||||
return L"";
|
||||
}
|
||||
|
||||
char *inbuf = (char *)s.c_str();
|
||||
size_t insize = s.length() + 1; // include null terminator
|
||||
char *outbuf = (char *)&wcharBuffer[0];
|
||||
size_t outsize = len * sizeof(wchar_t);
|
||||
|
||||
ret = iconv(iconvUTF82Wide, &inbuf, &insize, &outbuf, &outsize);
|
||||
}
|
||||
|
||||
if(ret == (size_t)-1)
|
||||
{
|
||||
#if ENABLED(RDOC_DEVEL)
|
||||
RDCWARN("Failed to convert wstring");
|
||||
#endif
|
||||
return L"";
|
||||
}
|
||||
|
||||
// convert to string from null-terminated string
|
||||
return rdcwstr(&wcharBuffer[0]);
|
||||
}
|
||||
};
|
||||
|
||||
namespace OSUtility
|
||||
{
|
||||
void WriteOutput(int channel, const char *str)
|
||||
{
|
||||
if(channel == OSUtility::Output_StdOut)
|
||||
{
|
||||
fprintf(stdout, "%s", str);
|
||||
fflush(stdout);
|
||||
}
|
||||
else if(channel == OSUtility::Output_StdErr)
|
||||
{
|
||||
fprintf(stderr, "%s", str);
|
||||
fflush(stderr);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t GetMachineIdent()
|
||||
{
|
||||
uint64_t ret = MachineIdent_Linux;
|
||||
|
||||
#if defined(_M_ARM) || defined(__arm__)
|
||||
ret |= MachineIdent_Arch_ARM;
|
||||
#else
|
||||
ret |= MachineIdent_Arch_x86;
|
||||
#endif
|
||||
|
||||
#if ENABLED(RDOC_X64)
|
||||
ret |= MachineIdent_64bit;
|
||||
#else
|
||||
ret |= MachineIdent_32bit;
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
/******************************************************************************
|
||||
* 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 "os/os_specific.h"
|
||||
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
double Timing::GetTickFrequency()
|
||||
{
|
||||
return 1000000.0;
|
||||
}
|
||||
|
||||
uint64_t Timing::GetTick()
|
||||
{
|
||||
timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return uint64_t(ts.tv_sec) * 1000000000ULL + uint32_t(ts.tv_nsec & 0xffffffff);
|
||||
}
|
||||
|
||||
void Threading::SetCurrentThreadName(const rdcstr &name)
|
||||
{
|
||||
}
|
||||
@@ -66,11 +66,11 @@ private:
|
||||
{
|
||||
void *addrs_ptr[ARRAY_COUNT(addrs)];
|
||||
|
||||
size_t ret = backtrace(addrs_ptr, ARRAY_COUNT(addrs));
|
||||
int ret = backtrace(addrs_ptr, ARRAY_COUNT(addrs));
|
||||
|
||||
numLevels = 0;
|
||||
if(ret > 0)
|
||||
numLevels = ret;
|
||||
numLevels = (size_t)ret;
|
||||
|
||||
int offs = 0;
|
||||
// if we want to trim levels of the stack, we can do that here
|
||||
|
||||
@@ -38,11 +38,7 @@
|
||||
|
||||
Threading::CriticalSection libLock;
|
||||
|
||||
#ifdef __linux__
|
||||
RDOC_EXTERN_CONFIG(bool, Linux_Debug_PtraceLogging);
|
||||
#else
|
||||
#define Linux_Debug_PtraceLogging() false
|
||||
#endif
|
||||
|
||||
static std::map<rdcstr, rdcarray<FunctionLoadCallback>> libraryCallbacks;
|
||||
static rdcarray<rdcstr> libraryHooks;
|
||||
@@ -120,7 +116,6 @@ 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); \
|
||||
@@ -199,7 +194,6 @@ __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[])
|
||||
|
||||
@@ -24,9 +24,7 @@
|
||||
|
||||
#include "os/os_specific.h"
|
||||
|
||||
#ifdef __linux__
|
||||
#include <sys/prctl.h>
|
||||
#endif
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@@ -44,7 +42,5 @@ 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
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ elseif(ENABLE_GGP)
|
||||
elseif(UNIX)
|
||||
list(APPEND sources renderdoccmd_linux.cpp)
|
||||
|
||||
if(FreeBSD)
|
||||
if(FREEBSD)
|
||||
list(APPEND libraries PRIVATE -L/usr/local/lib)
|
||||
list(APPEND includes /usr/local/include)
|
||||
endif()
|
||||
|
||||
Reference in New Issue
Block a user