From bb77c53e15738c37b48ca60e1f7d4b46544004df Mon Sep 17 00:00:00 2001 From: Aliya Pazylbekova Date: Wed, 13 Feb 2019 16:10:10 -0500 Subject: [PATCH] Add ggp os specific functionality --- renderdoc/CMakeLists.txt | 17 ++ renderdoc/os/posix/ggp/ggp_callstack.cpp | 317 +++++++++++++++++++++ renderdoc/os/posix/ggp/ggp_hook.cpp | 60 ++++ renderdoc/os/posix/ggp/ggp_network.cpp | 39 +++ renderdoc/os/posix/ggp/ggp_process.cpp | 198 +++++++++++++ renderdoc/os/posix/ggp/ggp_stringio.cpp | 340 +++++++++++++++++++++++ renderdoc/os/posix/ggp/ggp_threading.cpp | 40 +++ 7 files changed, 1011 insertions(+) create mode 100644 renderdoc/os/posix/ggp/ggp_callstack.cpp create mode 100644 renderdoc/os/posix/ggp/ggp_hook.cpp create mode 100644 renderdoc/os/posix/ggp/ggp_network.cpp create mode 100644 renderdoc/os/posix/ggp/ggp_process.cpp create mode 100644 renderdoc/os/posix/ggp/ggp_stringio.cpp create mode 100644 renderdoc/os/posix/ggp/ggp_threading.cpp diff --git a/renderdoc/CMakeLists.txt b/renderdoc/CMakeLists.txt index 2b50003d2..f59dd888e 100644 --- a/renderdoc/CMakeLists.txt +++ b/renderdoc/CMakeLists.txt @@ -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 diff --git a/renderdoc/os/posix/ggp/ggp_callstack.cpp b/renderdoc/os/posix/ggp/ggp_callstack.cpp new file mode 100644 index 000000000..4b070f083 --- /dev/null +++ b/renderdoc/os/posix/ggp/ggp_callstack.cpp @@ -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 +#include +#include +#include +#include +#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 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(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 m_Modules; + std::map 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 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); +} +}; diff --git a/renderdoc/os/posix/ggp/ggp_hook.cpp b/renderdoc/os/posix/ggp/ggp_hook.cpp new file mode 100644 index 000000000..525fcccca --- /dev/null +++ b/renderdoc/os/posix/ggp/ggp_hook.cpp @@ -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 +#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) +{ +} diff --git a/renderdoc/os/posix/ggp/ggp_network.cpp b/renderdoc/os/posix/ggp/ggp_network.cpp new file mode 100644 index 000000000..8e6f8a8ca --- /dev/null +++ b/renderdoc/os/posix/ggp/ggp_network.cpp @@ -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); +} +}; diff --git a/renderdoc/os/posix/ggp/ggp_process.cpp b/renderdoc/os/posix/ggp/ggp_process.cpp new file mode 100644 index 000000000..2a36d3574 --- /dev/null +++ b/renderdoc/os/posix/ggp/ggp_process.cpp @@ -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 +#include +#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 getSockets(pid_t childPid) +{ + std::vector sockets; + string dirPath = StringFormat::Fmt("/proc/%d/fd", (int)childPid); + std::vector 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 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; +} diff --git a/renderdoc/os/posix/ggp/ggp_stringio.cpp b/renderdoc/os/posix/ggp/ggp_stringio.cpp new file mode 100644 index 000000000..3008687a5 --- /dev/null +++ b/renderdoc/os/posix/ggp/ggp_stringio.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 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 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; +} +}; diff --git a/renderdoc/os/posix/ggp/ggp_threading.cpp b/renderdoc/os/posix/ggp/ggp_threading.cpp new file mode 100644 index 000000000..c9cf07234 --- /dev/null +++ b/renderdoc/os/posix/ggp/ggp_threading.cpp @@ -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 +#include + +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); +}