mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-05 01:20:42 +00:00
Add ggp os specific functionality
This commit is contained in:
committed by
baldurk
parent
64437157d0
commit
bb77c53e15
@@ -271,6 +271,23 @@ elseif(APPLE)
|
||||
os/posix/posix_stringio.cpp
|
||||
os/posix/posix_threading.cpp
|
||||
os/posix/posix_specific.h)
|
||||
elseif(ENABLE_GGP)
|
||||
list(APPEND sources
|
||||
data/embedded_files.h
|
||||
os/posix/ggp/ggp_stringio.cpp
|
||||
os/posix/ggp/ggp_callstack.cpp
|
||||
os/posix/ggp/ggp_process.cpp
|
||||
os/posix/ggp/ggp_threading.cpp
|
||||
os/posix/ggp/ggp_hook.cpp
|
||||
os/posix/ggp/ggp_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
|
||||
|
||||
@@ -0,0 +1,317 @@
|
||||
/******************************************************************************
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 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 <execinfo.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include "os/os_specific.h"
|
||||
|
||||
void *renderdocBase = NULL;
|
||||
void *renderdocEnd = NULL;
|
||||
|
||||
class GgpCallstack : public Callstack::Stackwalk
|
||||
{
|
||||
public:
|
||||
GgpCallstack()
|
||||
{
|
||||
RDCEraseEl(addrs);
|
||||
numLevels = 0;
|
||||
Collect();
|
||||
}
|
||||
GgpCallstack(uint64_t *calls, size_t num) { Set(calls, num); }
|
||||
~GgpCallstack() {}
|
||||
void Set(uint64_t *calls, size_t num)
|
||||
{
|
||||
numLevels = num;
|
||||
for(int i = 0; i < numLevels; i++)
|
||||
addrs[i] = calls[i];
|
||||
}
|
||||
|
||||
size_t NumLevels() const { return size_t(numLevels); }
|
||||
const uint64_t *GetAddrs() const { return addrs; }
|
||||
private:
|
||||
GgpCallstack(const Callstack::Stackwalk &other);
|
||||
|
||||
void Collect()
|
||||
{
|
||||
void *addrs_ptr[ARRAY_COUNT(addrs)];
|
||||
|
||||
numLevels = backtrace(addrs_ptr, ARRAY_COUNT(addrs));
|
||||
|
||||
int offs = 0;
|
||||
// if we want to trim levels of the stack, we can do that here
|
||||
// by incrementing offs and decrementing numLevels
|
||||
while(numLevels > 0 && addrs_ptr[offs] >= renderdocBase && addrs_ptr[offs] < renderdocEnd)
|
||||
{
|
||||
offs++;
|
||||
numLevels--;
|
||||
}
|
||||
|
||||
for(int i = 0; i < numLevels; i++)
|
||||
addrs[i] = (uint64_t)addrs_ptr[i + offs];
|
||||
}
|
||||
|
||||
uint64_t addrs[128];
|
||||
int numLevels;
|
||||
};
|
||||
|
||||
namespace Callstack
|
||||
{
|
||||
void Init()
|
||||
{
|
||||
// look for our own line
|
||||
FILE *f = FileIO::fopen("/proc/self/maps", "r");
|
||||
|
||||
if(f)
|
||||
{
|
||||
while(!feof(f))
|
||||
{
|
||||
char line[512] = {0};
|
||||
if(fgets(line, 511, f))
|
||||
{
|
||||
if(strstr(line, "librenderdoc") && strstr(line, "r-xp"))
|
||||
{
|
||||
sscanf(line, "%p-%p", &renderdocBase, &renderdocEnd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FileIO::fclose(f);
|
||||
}
|
||||
}
|
||||
|
||||
Stackwalk *Collect()
|
||||
{
|
||||
return new GgpCallstack();
|
||||
}
|
||||
|
||||
Stackwalk *Create()
|
||||
{
|
||||
return new GgpCallstack(NULL, 0);
|
||||
}
|
||||
|
||||
bool GetLoadedModules(byte *buf, size_t &size)
|
||||
{
|
||||
// we just dump the whole file rather than pre-parsing, that way we can improve
|
||||
// parsing without needing to recapture
|
||||
FILE *f = FileIO::fopen("/proc/self/maps", "r");
|
||||
|
||||
size = 0;
|
||||
|
||||
if(buf)
|
||||
memcpy(buf, "LNUXCALL", 8);
|
||||
|
||||
size += 8;
|
||||
|
||||
byte dummy[512];
|
||||
|
||||
while(!feof(f))
|
||||
{
|
||||
byte *readbuf = buf ? buf + size : dummy;
|
||||
size += FileIO::fread(readbuf, 1, 512, f);
|
||||
}
|
||||
|
||||
FileIO::fclose(f);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct LookupModule
|
||||
{
|
||||
uint64_t base;
|
||||
uint64_t end;
|
||||
uint64_t offset;
|
||||
char path[2048];
|
||||
};
|
||||
|
||||
class GgpResolver : public Callstack::StackResolver
|
||||
{
|
||||
public:
|
||||
GgpResolver(vector<LookupModule> modules) { m_Modules = modules; }
|
||||
Callstack::AddressDetails GetAddr(uint64_t addr)
|
||||
{
|
||||
EnsureCached(addr);
|
||||
|
||||
return m_Cache[addr];
|
||||
}
|
||||
|
||||
private:
|
||||
void EnsureCached(uint64_t addr)
|
||||
{
|
||||
auto it = m_Cache.insert(
|
||||
std::pair<uint64_t, Callstack::AddressDetails>(addr, Callstack::AddressDetails()));
|
||||
if(!it.second)
|
||||
return;
|
||||
|
||||
Callstack::AddressDetails &ret = it.first->second;
|
||||
|
||||
ret.filename = "Unknown";
|
||||
ret.line = 0;
|
||||
ret.function = StringFormat::Fmt("0x%08llx", addr);
|
||||
|
||||
for(size_t i = 0; i < m_Modules.size(); i++)
|
||||
{
|
||||
if(addr >= m_Modules[i].base && addr < m_Modules[i].end)
|
||||
{
|
||||
uint64_t relative = addr - m_Modules[i].base + m_Modules[i].offset;
|
||||
string cmd = StringFormat::Fmt("addr2line -fCe \"%s\" 0x%llx", m_Modules[i].path, relative);
|
||||
|
||||
FILE *f = ::popen(cmd.c_str(), "r");
|
||||
|
||||
char result[2048] = {0};
|
||||
fread(result, 1, 2047, f);
|
||||
|
||||
fclose(f);
|
||||
|
||||
char *line2 = strchr(result, '\n');
|
||||
if(line2)
|
||||
{
|
||||
*line2 = 0;
|
||||
line2++;
|
||||
}
|
||||
|
||||
ret.function = result;
|
||||
|
||||
if(line2)
|
||||
{
|
||||
char *linenum = line2 + strlen(line2) - 1;
|
||||
while(linenum > line2 && *linenum != ':')
|
||||
linenum--;
|
||||
|
||||
ret.line = 0;
|
||||
|
||||
if(*linenum == ':')
|
||||
{
|
||||
*linenum = 0;
|
||||
linenum++;
|
||||
|
||||
while(*linenum >= '0' && *linenum <= '9')
|
||||
{
|
||||
ret.line *= 10;
|
||||
ret.line += (uint32_t(*linenum) - uint32_t('0'));
|
||||
linenum++;
|
||||
}
|
||||
}
|
||||
|
||||
ret.filename = line2;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<LookupModule> m_Modules;
|
||||
std::map<uint64_t, Callstack::AddressDetails> m_Cache;
|
||||
};
|
||||
|
||||
StackResolver *MakeResolver(byte *moduleDB, size_t DBSize, RENDERDOC_ProgressCallback progress)
|
||||
{
|
||||
// we look in the original locations for the files, we don't prompt if we can't
|
||||
// find the file, or the file doesn't have symbols (and we don't validate that
|
||||
// the file is the right version). A good option for doing this would be
|
||||
// http://github.com/mlabbe/nativefiledialog
|
||||
|
||||
if(DBSize < 8 || memcmp(moduleDB, "LNUXCALL", 8))
|
||||
{
|
||||
RDCWARN("Can't load callstack resolve for this log. Possibly from another platform?");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *start = (char *)(moduleDB + 8);
|
||||
char *search = start;
|
||||
char *dbend = (char *)(moduleDB + DBSize);
|
||||
|
||||
vector<LookupModule> modules;
|
||||
|
||||
while(search && search < dbend)
|
||||
{
|
||||
if(progress)
|
||||
progress(float(search - start) / float(DBSize));
|
||||
|
||||
// find .text segments
|
||||
{
|
||||
long unsigned int base = 0, end = 0, offset = 0;
|
||||
|
||||
int inode = 0;
|
||||
int offs = 0;
|
||||
// base-end perms offset devid inode offs
|
||||
int num = sscanf(search, "%lx-%lx r-xp %lx %*x:%*x %d %n", &base, &end, &offset,
|
||||
&inode, &offs);
|
||||
|
||||
// we don't care about inode actually, we ust use it to verify that
|
||||
// we read all 4 params (and so perms == r-xp)
|
||||
if(num == 4 && offs > 0)
|
||||
{
|
||||
LookupModule mod = {0};
|
||||
|
||||
mod.base = (uint64_t)base;
|
||||
mod.end = (uint64_t)end;
|
||||
mod.offset = (uint64_t)offset;
|
||||
|
||||
search += offs;
|
||||
while(search < dbend && (*search == ' ' || *search == '\t'))
|
||||
search++;
|
||||
|
||||
if(search < dbend && *search != '[' && *search != 0 && *search != '\n')
|
||||
{
|
||||
size_t n = ARRAY_COUNT(mod.path) - 1;
|
||||
mod.path[n] = 0;
|
||||
for(size_t i = 0; i < n; i++)
|
||||
{
|
||||
if(search[i] == 0 || search[i] == '\n')
|
||||
{
|
||||
mod.path[i] = 0;
|
||||
break;
|
||||
}
|
||||
if(search + i >= dbend)
|
||||
{
|
||||
mod.path[i] = 0;
|
||||
break;
|
||||
}
|
||||
mod.path[i] = search[i];
|
||||
}
|
||||
|
||||
modules.push_back(mod);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(progress)
|
||||
progress(RDCMIN(1.0f, float(search - start) / float(DBSize)));
|
||||
|
||||
if(search >= dbend)
|
||||
break;
|
||||
|
||||
search = strchr(search, '\n');
|
||||
if(search)
|
||||
search++;
|
||||
}
|
||||
|
||||
return new GgpResolver(modules);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,60 @@
|
||||
/******************************************************************************
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 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 "hooks/hooks.h"
|
||||
|
||||
// No hooks needed for GGP.
|
||||
void LibraryHooks::BeginHookRegistration()
|
||||
{
|
||||
}
|
||||
|
||||
bool LibraryHooks::Detect(const char *identifier)
|
||||
{
|
||||
return dlsym(RTLD_DEFAULT, identifier) != NULL;
|
||||
}
|
||||
|
||||
void LibraryHooks::EndHookRegistration()
|
||||
{
|
||||
}
|
||||
|
||||
void LibraryHooks::RemoveHooks()
|
||||
{
|
||||
}
|
||||
|
||||
void LibraryHooks::Refresh()
|
||||
{
|
||||
}
|
||||
|
||||
void LibraryHooks::IgnoreLibrary(const char *libraryName)
|
||||
{
|
||||
}
|
||||
|
||||
void LibraryHooks::RegisterLibraryHook(char const *name, FunctionLoadCallback cb)
|
||||
{
|
||||
}
|
||||
|
||||
void LibraryHooks::RegisterFunctionHook(const char *libraryName, const FunctionHook &hook)
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/******************************************************************************
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2017-2019 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
|
||||
{
|
||||
uint32_t Socket::GetRemoteIP() const
|
||||
{
|
||||
return GetIPFromTCPSocket((int)socket);
|
||||
}
|
||||
|
||||
Socket *CreateServerSocket(const char *bindaddr, uint16_t port, int queuesize)
|
||||
{
|
||||
return CreateTCPServerSocket(bindaddr, port, queuesize);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,198 @@
|
||||
/******************************************************************************
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 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 <unistd.h>
|
||||
#include <algorithm>
|
||||
#include "os/os_specific.h"
|
||||
|
||||
extern char **environ;
|
||||
|
||||
// we wait 1ns, then 2ns, then 4ns, etc so our total is 0xfff etc
|
||||
// 0xfffff == ~1s
|
||||
#define INITIAL_WAIT_TIME 1
|
||||
#define MAX_WAIT_TIME 0xfffff
|
||||
|
||||
char **GetCurrentEnvironment()
|
||||
{
|
||||
return environ;
|
||||
}
|
||||
|
||||
std::vector<int> getSockets(pid_t childPid)
|
||||
{
|
||||
std::vector<int> sockets;
|
||||
string dirPath = StringFormat::Fmt("/proc/%d/fd", (int)childPid);
|
||||
std::vector<PathEntry> files = FileIO::GetFilesInDirectory(dirPath.c_str());
|
||||
if(files.empty())
|
||||
return sockets;
|
||||
|
||||
for(const PathEntry &file : files)
|
||||
{
|
||||
string target = StringFormat::Fmt("%s/%s", dirPath.c_str(), file.filename.c_str());
|
||||
char linkname[1024];
|
||||
ssize_t length = readlink(target.c_str(), linkname, 1023);
|
||||
if(length == -1)
|
||||
continue;
|
||||
|
||||
linkname[length] = '\0';
|
||||
uint32_t inode = 0;
|
||||
int num = sscanf(linkname, "socket:[%u]", &inode);
|
||||
if(num == 1)
|
||||
sockets.push_back(inode);
|
||||
}
|
||||
return sockets;
|
||||
}
|
||||
|
||||
int GetIdentPort(pid_t childPid)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
string procfile = StringFormat::Fmt("/proc/%d/net/tcp", (int)childPid);
|
||||
|
||||
int waitTime = INITIAL_WAIT_TIME;
|
||||
|
||||
// try for a little while for the /proc entry to appear
|
||||
while(ret == 0 && waitTime <= MAX_WAIT_TIME)
|
||||
{
|
||||
// back-off for each retry
|
||||
usleep(waitTime);
|
||||
|
||||
waitTime *= 2;
|
||||
|
||||
FILE *f = FileIO::fopen(procfile.c_str(), "r");
|
||||
|
||||
if(f == NULL)
|
||||
{
|
||||
// try again in a bit
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<int> sockets = getSockets(childPid);
|
||||
|
||||
// read through the proc file to check for an open listen socket
|
||||
while(ret == 0 && !feof(f))
|
||||
{
|
||||
const size_t sz = 512;
|
||||
char line[sz];
|
||||
line[sz - 1] = 0;
|
||||
fgets(line, sz - 1, f);
|
||||
|
||||
// an example for a line:
|
||||
// sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
|
||||
// 0: 00000000:9808 00000000:0000 0A 00000000:00000000 00:00000000 00000000 1000 0 109747
|
||||
|
||||
int socketnum = 0, hexip = 0, hexport = 0, inode = 0;
|
||||
int num = sscanf(line, " %d: %x:%x %*x:%*x %*x %*x:%*x %*x:%*x %*x %*d %*d %d", &socketnum,
|
||||
&hexip, &hexport, &inode);
|
||||
|
||||
// find open listen socket on 0.0.0.0:port
|
||||
if(num == 4 && hexip == 0 && hexport >= RenderDoc_FirstTargetControlPort &&
|
||||
hexport <= RenderDoc_LastTargetControlPort &&
|
||||
std::find(sockets.begin(), sockets.end(), inode) != sockets.end())
|
||||
{
|
||||
ret = hexport;
|
||||
}
|
||||
}
|
||||
|
||||
FileIO::fclose(f);
|
||||
}
|
||||
|
||||
if(ret == 0)
|
||||
{
|
||||
RDCWARN("Couldn't locate renderdoc target control listening port between %u and %u in %s",
|
||||
(uint32_t)RenderDoc_FirstTargetControlPort, (uint32_t)RenderDoc_LastTargetControlPort,
|
||||
procfile.c_str());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// because OSUtility::DebuggerPresent is called often we want it to be
|
||||
// cheap. Opening and parsing a file would cause high overhead on each
|
||||
// call, so instead we just cache it at startup. This fails in the case
|
||||
// of attaching to processes
|
||||
bool debuggerPresent = false;
|
||||
|
||||
void CacheDebuggerPresent()
|
||||
{
|
||||
FILE *f = FileIO::fopen("/proc/self/status", "r");
|
||||
|
||||
if(f == NULL)
|
||||
{
|
||||
RDCWARN("Couldn't open /proc/self/status");
|
||||
return;
|
||||
}
|
||||
|
||||
// read through the proc file to check for TracerPid
|
||||
while(!feof(f))
|
||||
{
|
||||
const size_t sz = 512;
|
||||
char line[sz];
|
||||
line[sz - 1] = 0;
|
||||
fgets(line, sz - 1, f);
|
||||
|
||||
int tracerpid = 0;
|
||||
int num = sscanf(line, "TracerPid: %d", &tracerpid);
|
||||
|
||||
// found TracerPid line
|
||||
if(num == 1)
|
||||
{
|
||||
debuggerPresent = (tracerpid != 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
FileIO::fclose(f);
|
||||
}
|
||||
|
||||
bool OSUtility::DebuggerPresent()
|
||||
{
|
||||
return debuggerPresent;
|
||||
}
|
||||
|
||||
const char *Process::GetEnvVariable(const char *name)
|
||||
{
|
||||
return getenv(name);
|
||||
}
|
||||
|
||||
uint64_t Process::GetMemoryUsage()
|
||||
{
|
||||
FILE *f = FileIO::fopen("/proc/self/statm", "r");
|
||||
|
||||
if(f == NULL)
|
||||
{
|
||||
RDCWARN("Couldn't open /proc/self/statm");
|
||||
return 0;
|
||||
}
|
||||
|
||||
char line[512] = {};
|
||||
fgets(line, 511, f);
|
||||
|
||||
uint32_t vmPages = 0;
|
||||
int num = sscanf(line, "%u", &vmPages);
|
||||
|
||||
if(num == 1 && vmPages > 0)
|
||||
return vmPages * (uint64_t)sysconf(_SC_PAGESIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,340 @@
|
||||
/******************************************************************************
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 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 <errno.h>
|
||||
#include <iconv.h>
|
||||
#include <pwd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include "api/app/renderdoc_app.h"
|
||||
#include "common/threading.h"
|
||||
#include "os/os_specific.h"
|
||||
#include "strings/string_utils.h"
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace Keyboard
|
||||
{
|
||||
void Init()
|
||||
{
|
||||
}
|
||||
|
||||
bool PlatformHasKeyInput()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void AddInputWindow(void *wnd)
|
||||
{
|
||||
}
|
||||
|
||||
void RemoveInputWindow(void *wnd)
|
||||
{
|
||||
}
|
||||
|
||||
bool GetKeyState(int key)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
namespace FileIO
|
||||
{
|
||||
string GetTempRootPath()
|
||||
{
|
||||
return "/tmp";
|
||||
}
|
||||
|
||||
string GetAppFolderFilename(const string &filename)
|
||||
{
|
||||
passwd *pw = getpwuid(getuid());
|
||||
const char *homedir = pw->pw_dir;
|
||||
|
||||
string ret = string(homedir) + "/.renderdoc/";
|
||||
|
||||
mkdir(ret.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
|
||||
|
||||
return ret + filename;
|
||||
}
|
||||
|
||||
void GetExecutableFilename(string &selfName)
|
||||
{
|
||||
char path[512] = {0};
|
||||
readlink("/proc/self/exe", path, 511);
|
||||
|
||||
selfName = string(path);
|
||||
}
|
||||
|
||||
int LibraryLocator = 42;
|
||||
|
||||
void GetLibraryFilename(string &selfName)
|
||||
{
|
||||
// this is a hack, but the only reliable way to find the absolute path to the library.
|
||||
// dladdr would be fine but it returns the wrong result for symbols in the library
|
||||
|
||||
string librenderdoc_path;
|
||||
|
||||
FILE *f = fopen("/proc/self/maps", "r");
|
||||
|
||||
if(f)
|
||||
{
|
||||
// read the whole thing in one go. There's no need to try and be tight with
|
||||
// this allocation, so just make sure we can read everything.
|
||||
char *map_string = new char[1024 * 1024];
|
||||
memset(map_string, 0, 1024 * 1024);
|
||||
|
||||
::fread(map_string, 1, 1024 * 1024, f);
|
||||
|
||||
::fclose(f);
|
||||
|
||||
char *c = strstr(map_string, "/librenderdoc.so");
|
||||
|
||||
if(c)
|
||||
{
|
||||
// walk backwards until we hit the start of the line
|
||||
while(c > map_string)
|
||||
{
|
||||
c--;
|
||||
|
||||
if(c[0] == '\n')
|
||||
{
|
||||
c++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// walk forwards across the address range (00400000-0040c000)
|
||||
while(isalnum(c[0]) || c[0] == '-')
|
||||
c++;
|
||||
|
||||
// whitespace
|
||||
while(c[0] == ' ')
|
||||
c++;
|
||||
|
||||
// permissions (r-xp)
|
||||
while(isalpha(c[0]) || c[0] == '-')
|
||||
c++;
|
||||
|
||||
// whitespace
|
||||
while(c[0] == ' ')
|
||||
c++;
|
||||
|
||||
// offset (0000b000)
|
||||
while(isalnum(c[0]) || c[0] == '-')
|
||||
c++;
|
||||
|
||||
// whitespace
|
||||
while(c[0] == ' ')
|
||||
c++;
|
||||
|
||||
// dev
|
||||
while(isalnum(c[0]) || c[0] == ':')
|
||||
c++;
|
||||
|
||||
// whitespace
|
||||
while(c[0] == ' ')
|
||||
c++;
|
||||
|
||||
// inode
|
||||
while(isdigit(c[0]))
|
||||
c++;
|
||||
|
||||
// whitespace
|
||||
while(c[0] == ' ')
|
||||
c++;
|
||||
|
||||
// FINALLY we are at the start of the actual path
|
||||
char *end = strchr(c, '\n');
|
||||
|
||||
if(end)
|
||||
librenderdoc_path = string(c, end - c);
|
||||
}
|
||||
|
||||
delete[] map_string;
|
||||
}
|
||||
|
||||
if(librenderdoc_path.empty())
|
||||
{
|
||||
RDCWARN("Couldn't get librenderdoc.so path from /proc/self/maps, falling back to dladdr");
|
||||
|
||||
Dl_info info;
|
||||
if(dladdr(&LibraryLocator, &info))
|
||||
librenderdoc_path = info.dli_fname;
|
||||
}
|
||||
|
||||
selfName = librenderdoc_path;
|
||||
}
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
std::string Wide2UTF8(const std::wstring &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;
|
||||
|
||||
std::vector<char> charBuffer(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 std::string(&charBuffer[0]);
|
||||
}
|
||||
|
||||
std::wstring UTF82Wide(const std::string &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;
|
||||
|
||||
std::vector<wchar_t> wcharBuffer(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 std::wstring(&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,40 @@
|
||||
/******************************************************************************
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 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);
|
||||
}
|
||||
Reference in New Issue
Block a user