summaryrefslogtreecommitdiffstats
path: root/gfx/angle/checkout/src/common/system_utils_posix.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/angle/checkout/src/common/system_utils_posix.cpp')
-rw-r--r--gfx/angle/checkout/src/common/system_utils_posix.cpp470
1 files changed, 470 insertions, 0 deletions
diff --git a/gfx/angle/checkout/src/common/system_utils_posix.cpp b/gfx/angle/checkout/src/common/system_utils_posix.cpp
new file mode 100644
index 0000000000..ab0faee0bc
--- /dev/null
+++ b/gfx/angle/checkout/src/common/system_utils_posix.cpp
@@ -0,0 +1,470 @@
+//
+// Copyright 2018 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// system_utils_posix.cpp: Implementation of POSIX OS-specific functions.
+
+#include "common/debug.h"
+#include "system_utils.h"
+
+#include <array>
+#include <iostream>
+
+#include <dlfcn.h>
+#include <grp.h>
+#include <inttypes.h>
+#include <pwd.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "common/string_utils.h"
+
+#ifdef ANGLE_PLATFORM_FUCHSIA
+# include <zircon/process.h>
+# include <zircon/syscalls.h>
+#else
+# include <sys/resource.h>
+#endif
+
+namespace angle
+{
+
+namespace
+{
+std::string GetModulePath(void *moduleOrSymbol)
+{
+ Dl_info dlInfo;
+ if (dladdr(moduleOrSymbol, &dlInfo) == 0)
+ {
+ return "";
+ }
+
+ return dlInfo.dli_fname;
+}
+
+void *OpenPosixLibrary(const std::string &fullPath, int extraFlags, std::string *errorOut)
+{
+ void *module = dlopen(fullPath.c_str(), RTLD_NOW | extraFlags);
+ if (module)
+ {
+ if (errorOut)
+ {
+ *errorOut = fullPath;
+ }
+ }
+ else if (errorOut)
+ {
+ *errorOut = "dlopen(";
+ *errorOut += fullPath;
+ *errorOut += ") failed with error: ";
+ *errorOut += dlerror();
+ struct stat sfile;
+ if (-1 == stat(fullPath.c_str(), &sfile))
+ {
+ *errorOut += ", stat() call failed.";
+ }
+ else
+ {
+ *errorOut += ", stat() info: ";
+ struct passwd *pwuser = getpwuid(sfile.st_uid);
+ if (pwuser)
+ {
+ *errorOut += "owner: ";
+ *errorOut += pwuser->pw_name;
+ *errorOut += ", ";
+ }
+ struct group *grpnam = getgrgid(sfile.st_gid);
+ if (grpnam)
+ {
+ *errorOut += "group: ";
+ *errorOut += grpnam->gr_name;
+ *errorOut += ", ";
+ }
+ *errorOut += "perms: ";
+ *errorOut += std::to_string(sfile.st_mode);
+ *errorOut += ", links: ";
+ *errorOut += std::to_string(sfile.st_nlink);
+ *errorOut += ", size: ";
+ *errorOut += std::to_string(sfile.st_size);
+ }
+ }
+ return module;
+}
+} // namespace
+
+Optional<std::string> GetCWD()
+{
+ std::array<char, 4096> pathBuf;
+ char *result = getcwd(pathBuf.data(), pathBuf.size());
+ if (result == nullptr)
+ {
+ return Optional<std::string>::Invalid();
+ }
+ return std::string(pathBuf.data());
+}
+
+bool SetCWD(const char *dirName)
+{
+ return (chdir(dirName) == 0);
+}
+
+bool UnsetEnvironmentVar(const char *variableName)
+{
+ return (unsetenv(variableName) == 0);
+}
+
+bool SetEnvironmentVar(const char *variableName, const char *value)
+{
+ return (setenv(variableName, value, 1) == 0);
+}
+
+std::string GetEnvironmentVar(const char *variableName)
+{
+ const char *value = getenv(variableName);
+ return (value == nullptr ? std::string() : std::string(value));
+}
+
+const char *GetPathSeparatorForEnvironmentVar()
+{
+ return ":";
+}
+
+std::string GetModuleDirectoryAndGetError(std::string *errorOut)
+{
+ std::string directory;
+ static int placeholderSymbol = 0;
+ std::string moduleName = GetModulePath(&placeholderSymbol);
+ if (!moduleName.empty())
+ {
+ directory = moduleName.substr(0, moduleName.find_last_of('/') + 1);
+ }
+
+ // Ensure we return the full path to the module, not the relative path
+ if (!IsFullPath(directory))
+ {
+ if (errorOut)
+ {
+ *errorOut += "Directory: '";
+ *errorOut += directory;
+ *errorOut += "' is not full path";
+ }
+ Optional<std::string> cwd = GetCWD();
+ if (cwd.valid())
+ {
+ directory = ConcatenatePath(cwd.value(), directory);
+ if (errorOut)
+ {
+ *errorOut += ", so it has been modified to: '";
+ *errorOut += directory;
+ *errorOut += "'. ";
+ }
+ }
+ else if (errorOut)
+ {
+ *errorOut += " and getcwd was invalid. ";
+ }
+ }
+ return directory;
+}
+
+std::string GetModuleDirectory()
+{
+ return GetModuleDirectoryAndGetError(nullptr);
+}
+
+void *OpenSystemLibraryWithExtensionAndGetError(const char *libraryName,
+ SearchType searchType,
+ std::string *errorOut)
+{
+ std::string directory;
+ if (searchType == SearchType::ModuleDir)
+ {
+#if ANGLE_PLATFORM_IOS
+ // On iOS, shared libraries must be loaded from within the app bundle.
+ directory = GetExecutableDirectory() + "/Frameworks/";
+#elif ANGLE_PLATFORM_FUCHSIA
+ // On Fuchsia the dynamic loader always looks up libraries in /pkg/lib
+ // and disallows loading of libraries via absolute paths.
+ directory = "";
+#else
+ directory = GetModuleDirectoryAndGetError(errorOut);
+#endif
+ }
+
+ int extraFlags = 0;
+ if (searchType == SearchType::AlreadyLoaded)
+ {
+ extraFlags = RTLD_NOLOAD;
+ }
+
+ std::string fullPath = directory + libraryName;
+ return OpenPosixLibrary(fullPath, extraFlags, errorOut);
+}
+
+void *GetLibrarySymbol(void *libraryHandle, const char *symbolName)
+{
+ if (!libraryHandle)
+ {
+ return nullptr;
+ }
+
+ return dlsym(libraryHandle, symbolName);
+}
+
+std::string GetLibraryPath(void *libraryHandle)
+{
+ if (!libraryHandle)
+ {
+ return "";
+ }
+
+ return GetModulePath(libraryHandle);
+}
+
+void CloseSystemLibrary(void *libraryHandle)
+{
+ if (libraryHandle)
+ {
+ dlclose(libraryHandle);
+ }
+}
+
+bool IsDirectory(const char *filename)
+{
+ struct stat st;
+ int result = stat(filename, &st);
+ return result == 0 && ((st.st_mode & S_IFDIR) == S_IFDIR);
+}
+
+bool IsDebuggerAttached()
+{
+ // This could have a fuller implementation.
+ // See https://cs.chromium.org/chromium/src/base/debug/debugger_posix.cc
+ return false;
+}
+
+void BreakDebugger()
+{
+ // This could have a fuller implementation.
+ // See https://cs.chromium.org/chromium/src/base/debug/debugger_posix.cc
+ abort();
+}
+
+const char *GetExecutableExtension()
+{
+ return "";
+}
+
+char GetPathSeparator()
+{
+ return '/';
+}
+
+std::string GetRootDirectory()
+{
+ return "/";
+}
+
+Optional<std::string> GetTempDirectory()
+{
+ const char *tmp = getenv("TMPDIR");
+ if (tmp != nullptr)
+ {
+ return std::string(tmp);
+ }
+
+#if defined(ANGLE_PLATFORM_ANDROID)
+ // Not used right now in the ANGLE test runner.
+ // return PathService::Get(DIR_CACHE, path);
+ return Optional<std::string>::Invalid();
+#else
+ return std::string("/tmp");
+#endif
+}
+
+Optional<std::string> CreateTemporaryFileInDirectory(const std::string &directory)
+{
+ std::string tempFileTemplate = directory + "/.angle.XXXXXX";
+
+ char tempFile[1000];
+ strcpy(tempFile, tempFileTemplate.c_str());
+
+ int fd = mkstemp(tempFile);
+ close(fd);
+
+ if (fd != -1)
+ {
+ return std::string(tempFile);
+ }
+
+ return Optional<std::string>::Invalid();
+}
+
+double GetCurrentProcessCpuTime()
+{
+#ifdef ANGLE_PLATFORM_FUCHSIA
+ static zx_handle_t me = zx_process_self();
+ zx_info_task_runtime_t task_runtime;
+ zx_object_get_info(me, ZX_INFO_TASK_RUNTIME, &task_runtime, sizeof(task_runtime), nullptr,
+ nullptr);
+ return static_cast<double>(task_runtime.cpu_time) * 1e-9;
+#else
+ // We could also have used /proc/stat, but that requires us to read the
+ // filesystem and convert from jiffies. /proc/stat also relies on jiffies
+ // (lower resolution) while getrusage can potentially use a sched_clock()
+ // underneath that has higher resolution.
+ struct rusage usage;
+ getrusage(RUSAGE_SELF, &usage);
+ double userTime = usage.ru_utime.tv_sec + usage.ru_utime.tv_usec * 1e-6;
+ double systemTime = usage.ru_stime.tv_sec + usage.ru_stime.tv_usec * 1e-6;
+ return userTime + systemTime;
+#endif
+}
+
+namespace
+{
+bool SetMemoryProtection(uintptr_t start, size_t size, int protections)
+{
+ int ret = mprotect(reinterpret_cast<void *>(start), size, protections);
+ if (ret < 0)
+ {
+ perror("mprotect failed");
+ }
+ return ret == 0;
+}
+
+class PosixPageFaultHandler : public PageFaultHandler
+{
+ public:
+ PosixPageFaultHandler(PageFaultCallback callback) : PageFaultHandler(callback) {}
+ ~PosixPageFaultHandler() override {}
+
+ bool enable() override;
+ bool disable() override;
+ void handle(int sig, siginfo_t *info, void *unused);
+
+ private:
+ struct sigaction mDefaultBusAction = {};
+ struct sigaction mDefaultSegvAction = {};
+};
+
+PosixPageFaultHandler *gPosixPageFaultHandler = nullptr;
+void SegfaultHandlerFunction(int sig, siginfo_t *info, void *unused)
+{
+ gPosixPageFaultHandler->handle(sig, info, unused);
+}
+
+void PosixPageFaultHandler::handle(int sig, siginfo_t *info, void *unused)
+{
+ bool found = false;
+ if ((sig == SIGSEGV || sig == SIGBUS) &&
+ (info->si_code == SEGV_ACCERR || info->si_code == SEGV_MAPERR))
+ {
+ found = mCallback(reinterpret_cast<uintptr_t>(info->si_addr)) ==
+ PageFaultHandlerRangeType::InRange;
+ }
+
+ // Fall back to default signal handler
+ if (!found)
+ {
+ if (sig == SIGSEGV)
+ {
+ mDefaultSegvAction.sa_sigaction(sig, info, unused);
+ }
+ else if (sig == SIGBUS)
+ {
+ mDefaultBusAction.sa_sigaction(sig, info, unused);
+ }
+ else
+ {
+ UNREACHABLE();
+ }
+ }
+}
+
+bool PosixPageFaultHandler::disable()
+{
+ return sigaction(SIGSEGV, &mDefaultSegvAction, nullptr) == 0 &&
+ sigaction(SIGBUS, &mDefaultBusAction, nullptr) == 0;
+}
+
+bool PosixPageFaultHandler::enable()
+{
+ struct sigaction sigAction = {};
+ sigAction.sa_flags = SA_SIGINFO;
+ sigAction.sa_sigaction = &SegfaultHandlerFunction;
+ sigemptyset(&sigAction.sa_mask);
+
+ // Some POSIX implementations use SIGBUS for mprotect faults
+ return sigaction(SIGSEGV, &sigAction, &mDefaultSegvAction) == 0 &&
+ sigaction(SIGBUS, &sigAction, &mDefaultBusAction) == 0;
+}
+} // namespace
+
+// Set write protection
+bool ProtectMemory(uintptr_t start, size_t size)
+{
+ return SetMemoryProtection(start, size, PROT_READ);
+}
+
+// Allow reading and writing
+bool UnprotectMemory(uintptr_t start, size_t size)
+{
+ return SetMemoryProtection(start, size, PROT_READ | PROT_WRITE);
+}
+
+size_t GetPageSize()
+{
+ long pageSize = sysconf(_SC_PAGE_SIZE);
+ if (pageSize < 0)
+ {
+ perror("Could not get sysconf page size");
+ return 0;
+ }
+ return static_cast<size_t>(pageSize);
+}
+
+PageFaultHandler *CreatePageFaultHandler(PageFaultCallback callback)
+{
+ gPosixPageFaultHandler = new PosixPageFaultHandler(callback);
+ return gPosixPageFaultHandler;
+}
+
+uint64_t GetProcessMemoryUsageKB()
+{
+ FILE *file = fopen("/proc/self/status", "r");
+
+ if (!file)
+ {
+ return 0;
+ }
+
+ const char *kSearchString = "VmRSS:";
+ constexpr size_t kMaxLineSize = 100;
+ std::array<char, kMaxLineSize> line = {};
+
+ uint64_t kb = 0;
+
+ while (fgets(line.data(), line.size(), file) != nullptr)
+ {
+ if (strncmp(line.data(), kSearchString, strlen(kSearchString)) == 0)
+ {
+ std::vector<std::string> strings;
+ SplitStringAlongWhitespace(line.data(), &strings);
+
+ sscanf(strings[1].c_str(), "%" SCNu64, &kb);
+ break;
+ }
+ }
+ fclose(file);
+
+ return kb;
+}
+} // namespace angle