| // | |
| // 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.cpp: Implementation of common functions | |
| #include "common/system_utils.h" | |
| #include "common/debug.h" | |
| #include <stdlib.h> | |
| #include <atomic> | |
| #if defined(ANGLE_PLATFORM_ANDROID) | |
| # include <sys/system_properties.h> | |
| #endif | |
| #if defined(ANGLE_PLATFORM_APPLE) | |
| # include <dispatch/dispatch.h> | |
| # include <pthread.h> | |
| #endif | |
| namespace angle | |
| { | |
| std::string GetExecutableName() | |
| { | |
| #if defined(ANGLE_PLATFORM_ANDROID) && __ANDROID_API__ >= 21 | |
| // Support for "getprogname" function in bionic was introduced in L (API level 21) | |
| const char *executableName = getprogname(); | |
| return (executableName) ? std::string(executableName) : "ANGLE"; | |
| #else | |
| std::string executableName = GetExecutablePath(); | |
| size_t lastPathSepLoc = executableName.find_last_of(GetPathSeparator()); | |
| return (lastPathSepLoc > 0 ? executableName.substr(lastPathSepLoc + 1, executableName.length()) | |
| : "ANGLE"); | |
| #endif // ANGLE_PLATFORM_ANDROID | |
| } | |
| // On Android return value cached in the process environment, if none, call | |
| // GetEnvironmentVarOrUnCachedAndroidProperty if not in environment. | |
| std::string GetEnvironmentVarOrAndroidProperty(const char *variableName, const char *propertyName) | |
| { | |
| #if defined(ANGLE_PLATFORM_ANDROID) && __ANDROID_API__ >= 21 | |
| // Can't use GetEnvironmentVar here because that won't allow us to distinguish between the | |
| // environment being set to an empty string vs. not set at all. | |
| const char *variableValue = getenv(variableName); | |
| if (variableValue != nullptr) | |
| { | |
| std::string value(variableValue); | |
| return value; | |
| } | |
| #endif | |
| return GetEnvironmentVarOrUnCachedAndroidProperty(variableName, propertyName); | |
| } | |
| // On Android call out to 'getprop' on a shell to get an Android property. On desktop, return | |
| // the value of the environment variable. | |
| std::string GetEnvironmentVarOrUnCachedAndroidProperty(const char *variableName, | |
| const char *propertyName) | |
| { | |
| #if defined(ANGLE_PLATFORM_ANDROID) && __ANDROID_API__ >= 26 | |
| std::string propertyValue; | |
| const prop_info *propertyInfo = __system_property_find(propertyName); | |
| if (propertyInfo != nullptr) | |
| { | |
| __system_property_read_callback( | |
| propertyInfo, | |
| [](void *cookie, const char *, const char *value, unsigned) { | |
| auto propertyValue = reinterpret_cast<std::string *>(cookie); | |
| *propertyValue = value; | |
| }, | |
| &propertyValue); | |
| } | |
| return propertyValue; | |
| #else | |
| // Return the environment variable's value. | |
| return GetEnvironmentVar(variableName); | |
| #endif // ANGLE_PLATFORM_ANDROID | |
| } | |
| // Look up a property and add it to the application's environment. | |
| // Adding to the env is a performance optimization, as getting properties is expensive. | |
| // This should only be used in non-Release paths, i.e. when using FrameCapture or DebugUtils. | |
| // It can cause race conditions in stress testing. See http://anglebug.com/6822 | |
| std::string GetAndSetEnvironmentVarOrUnCachedAndroidProperty(const char *variableName, | |
| const char *propertyName) | |
| { | |
| std::string value = GetEnvironmentVarOrUnCachedAndroidProperty(variableName, propertyName); | |
| #if defined(ANGLE_PLATFORM_ANDROID) | |
| if (!value.empty()) | |
| { | |
| // Set the environment variable with the value to improve future lookups (avoids | |
| SetEnvironmentVar(variableName, value.c_str()); | |
| } | |
| #endif | |
| return value; | |
| } | |
| bool GetBoolEnvironmentVar(const char *variableName) | |
| { | |
| std::string envVarString = GetEnvironmentVar(variableName); | |
| return (!envVarString.empty() && envVarString == "1"); | |
| } | |
| bool PrependPathToEnvironmentVar(const char *variableName, const char *path) | |
| { | |
| std::string oldValue = GetEnvironmentVar(variableName); | |
| const char *newValue = nullptr; | |
| std::string buf; | |
| if (oldValue.empty()) | |
| { | |
| newValue = path; | |
| } | |
| else | |
| { | |
| buf = path; | |
| buf += GetPathSeparatorForEnvironmentVar(); | |
| buf += oldValue; | |
| newValue = buf.c_str(); | |
| } | |
| return SetEnvironmentVar(variableName, newValue); | |
| } | |
| bool IsFullPath(std::string dirName) | |
| { | |
| if (dirName.find(GetRootDirectory()) == 0) | |
| { | |
| return true; | |
| } | |
| return false; | |
| } | |
| std::string ConcatenatePath(std::string first, std::string second) | |
| { | |
| if (first.empty()) | |
| { | |
| return second; | |
| } | |
| if (second.empty()) | |
| { | |
| return first; | |
| } | |
| if (IsFullPath(second)) | |
| { | |
| return second; | |
| } | |
| bool firstRedundantPathSeparator = first.find_last_of(GetPathSeparator()) == first.length() - 1; | |
| bool secondRedundantPathSeparator = second.find(GetPathSeparator()) == 0; | |
| if (firstRedundantPathSeparator && secondRedundantPathSeparator) | |
| { | |
| return first + second.substr(1); | |
| } | |
| else if (firstRedundantPathSeparator || secondRedundantPathSeparator) | |
| { | |
| return first + second; | |
| } | |
| return first + GetPathSeparator() + second; | |
| } | |
| Optional<std::string> CreateTemporaryFile() | |
| { | |
| const Optional<std::string> tempDir = GetTempDirectory(); | |
| if (!tempDir.valid()) | |
| return Optional<std::string>::Invalid(); | |
| return CreateTemporaryFileInDirectory(tempDir.value()); | |
| } | |
| PageFaultHandler::PageFaultHandler(PageFaultCallback callback) : mCallback(callback) {} | |
| PageFaultHandler::~PageFaultHandler() {} | |
| Library *OpenSharedLibrary(const char *libraryName, SearchType searchType) | |
| { | |
| void *libraryHandle = OpenSystemLibraryAndGetError(libraryName, searchType, nullptr); | |
| return new Library(libraryHandle); | |
| } | |
| Library *OpenSharedLibraryWithExtension(const char *libraryName, SearchType searchType) | |
| { | |
| void *libraryHandle = | |
| OpenSystemLibraryWithExtensionAndGetError(libraryName, searchType, nullptr); | |
| return new Library(libraryHandle); | |
| } | |
| Library *OpenSharedLibraryAndGetError(const char *libraryName, | |
| SearchType searchType, | |
| std::string *errorOut) | |
| { | |
| void *libraryHandle = OpenSystemLibraryAndGetError(libraryName, searchType, errorOut); | |
| return new Library(libraryHandle); | |
| } | |
| Library *OpenSharedLibraryWithExtensionAndGetError(const char *libraryName, | |
| SearchType searchType, | |
| std::string *errorOut) | |
| { | |
| void *libraryHandle = | |
| OpenSystemLibraryWithExtensionAndGetError(libraryName, searchType, errorOut); | |
| return new Library(libraryHandle); | |
| } | |
| void *OpenSystemLibrary(const char *libraryName, SearchType searchType) | |
| { | |
| return OpenSystemLibraryAndGetError(libraryName, searchType, nullptr); | |
| } | |
| void *OpenSystemLibraryWithExtension(const char *libraryName, SearchType searchType) | |
| { | |
| return OpenSystemLibraryWithExtensionAndGetError(libraryName, searchType, nullptr); | |
| } | |
| void *OpenSystemLibraryAndGetError(const char *libraryName, | |
| SearchType searchType, | |
| std::string *errorOut) | |
| { | |
| std::string libraryWithExtension = std::string(libraryName); | |
| std::string dotExtension = std::string(".") + GetSharedLibraryExtension(); | |
| // Only append the extension if it's not already present. This enables building libEGL.so.1 | |
| // and libGLESv2.so.2 by setting these as ANGLE_EGL_LIBRARY_NAME and ANGLE_GLESV2_LIBRARY_NAME. | |
| if (libraryWithExtension.find(dotExtension) == std::string::npos) | |
| { | |
| libraryWithExtension += dotExtension; | |
| } | |
| #if ANGLE_PLATFORM_IOS | |
| // On iOS, libraryWithExtension is a directory in which the library resides. | |
| // The actual library name doesn't have an extension at all. | |
| // E.g. "libEGL.framework/libEGL" | |
| libraryWithExtension = libraryWithExtension + "/" + libraryName; | |
| #endif | |
| return OpenSystemLibraryWithExtensionAndGetError(libraryWithExtension.c_str(), searchType, | |
| errorOut); | |
| } | |
| std::string StripFilenameFromPath(const std::string &path) | |
| { | |
| size_t lastPathSepLoc = path.find_last_of("\\/"); | |
| return (lastPathSepLoc != std::string::npos) ? path.substr(0, lastPathSepLoc) : ""; | |
| } | |
| static std::atomic<uint64_t> globalThreadSerial(1); | |
| #if defined(ANGLE_PLATFORM_APPLE) | |
| // https://anglebug.com/6479, similar to egl::GetCurrentThread() in libGLESv2/global_state.cpp | |
| uint64_t GetCurrentThreadUniqueId() | |
| { | |
| static pthread_key_t tlsIndex; | |
| static dispatch_once_t once; | |
| dispatch_once(&once, ^{ | |
| ASSERT(pthread_key_create(&tlsIndex, nullptr) == 0); | |
| }); | |
| void *tlsValue = pthread_getspecific(tlsIndex); | |
| if (tlsValue == nullptr) | |
| { | |
| uint64_t threadId = globalThreadSerial++; | |
| ASSERT(pthread_setspecific(tlsIndex, reinterpret_cast<void *>(threadId)) == 0); | |
| return threadId; | |
| } | |
| return reinterpret_cast<uint64_t>(tlsValue); | |
| } | |
| #else | |
| uint64_t GetCurrentThreadUniqueId() | |
| { | |
| thread_local uint64_t threadId(globalThreadSerial++); | |
| return threadId; | |
| } | |
| #endif | |
| } // namespace angle |