// // 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 #include #if defined(ANGLE_PLATFORM_ANDROID) # include #endif #if defined(ANGLE_PLATFORM_APPLE) # include # include #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(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 CreateTemporaryFile() { const Optional tempDir = GetTempDirectory(); if (!tempDir.valid()) return Optional::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) + "." + GetSharedLibraryExtension(); #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 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(threadId)) == 0); return threadId; } return reinterpret_cast(tlsValue); } #else uint64_t GetCurrentThreadUniqueId() { thread_local uint64_t threadId(globalThreadSerial++); return threadId; } #endif } // namespace angle