Add ggp os specific functionality

This commit is contained in:
Aliya Pazylbekova
2019-02-13 16:10:10 -05:00
committed by baldurk
parent 64437157d0
commit bb77c53e15
7 changed files with 1011 additions and 0 deletions
+17
View File
@@ -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
+317
View File
@@ -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);
}
};
+60
View File
@@ -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)
{
}
+39
View File
@@ -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);
}
};
+198
View File
@@ -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;
}
+340
View File
@@ -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;
}
};
+40
View File
@@ -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);
}