1 //
2 // Copyright 2018 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6
7 // system_utils.cpp: Implementation of common functions
8
9 #include "common/system_utils.h"
10 #include "common/debug.h"
11
12 #include <stdlib.h>
13 #include <atomic>
14
15 #if defined(ANGLE_PLATFORM_ANDROID)
16 # include <sys/system_properties.h>
17 #endif
18
19 #if defined(ANGLE_PLATFORM_APPLE)
20 # include <dispatch/dispatch.h>
21 # include <pthread.h>
22 #endif
23
24 namespace angle
25 {
GetExecutableName()26 std::string GetExecutableName()
27 {
28 #if defined(ANGLE_PLATFORM_ANDROID) && __ANDROID_API__ >= 21
29 // Support for "getprogname" function in bionic was introduced in L (API level 21)
30 const char *executableName = getprogname();
31 return (executableName) ? std::string(executableName) : "ANGLE";
32 #else
33 std::string executableName = GetExecutablePath();
34 size_t lastPathSepLoc = executableName.find_last_of(GetPathSeparator());
35 return (lastPathSepLoc > 0 ? executableName.substr(lastPathSepLoc + 1, executableName.length())
36 : "ANGLE");
37 #endif // ANGLE_PLATFORM_ANDROID
38 }
39
40 // On Android return value cached in the process environment, if none, call
41 // GetEnvironmentVarOrUnCachedAndroidProperty if not in environment.
GetEnvironmentVarOrAndroidProperty(const char * variableName,const char * propertyName)42 std::string GetEnvironmentVarOrAndroidProperty(const char *variableName, const char *propertyName)
43 {
44 #if defined(ANGLE_PLATFORM_ANDROID) && __ANDROID_API__ >= 21
45 // Can't use GetEnvironmentVar here because that won't allow us to distinguish between the
46 // environment being set to an empty string vs. not set at all.
47 const char *variableValue = getenv(variableName);
48 if (variableValue != nullptr)
49 {
50 std::string value(variableValue);
51 return value;
52 }
53 #endif
54 return GetEnvironmentVarOrUnCachedAndroidProperty(variableName, propertyName);
55 }
56
57 // On Android call out to 'getprop' on a shell to get an Android property. On desktop, return
58 // the value of the environment variable.
GetEnvironmentVarOrUnCachedAndroidProperty(const char * variableName,const char * propertyName)59 std::string GetEnvironmentVarOrUnCachedAndroidProperty(const char *variableName,
60 const char *propertyName)
61 {
62 #if defined(ANGLE_PLATFORM_ANDROID) && __ANDROID_API__ >= 26
63 std::string propertyValue;
64
65 const prop_info *propertyInfo = __system_property_find(propertyName);
66 if (propertyInfo != nullptr)
67 {
68 __system_property_read_callback(
69 propertyInfo,
70 [](void *cookie, const char *, const char *value, unsigned) {
71 auto propertyValue = reinterpret_cast<std::string *>(cookie);
72 *propertyValue = value;
73 },
74 &propertyValue);
75 }
76
77 return propertyValue;
78 #else
79 // Return the environment variable's value.
80 return GetEnvironmentVar(variableName);
81 #endif // ANGLE_PLATFORM_ANDROID
82 }
83
84 // Look up a property and add it to the application's environment.
85 // Adding to the env is a performance optimization, as getting properties is expensive.
86 // This should only be used in non-Release paths, i.e. when using FrameCapture or DebugUtils.
87 // It can cause race conditions in stress testing. See http://anglebug.com/6822
GetAndSetEnvironmentVarOrUnCachedAndroidProperty(const char * variableName,const char * propertyName)88 std::string GetAndSetEnvironmentVarOrUnCachedAndroidProperty(const char *variableName,
89 const char *propertyName)
90 {
91 std::string value = GetEnvironmentVarOrUnCachedAndroidProperty(variableName, propertyName);
92
93 #if defined(ANGLE_PLATFORM_ANDROID)
94 if (!value.empty())
95 {
96 // Set the environment variable with the value to improve future lookups (avoids
97 SetEnvironmentVar(variableName, value.c_str());
98 }
99 #endif
100
101 return value;
102 }
103
GetBoolEnvironmentVar(const char * variableName)104 bool GetBoolEnvironmentVar(const char *variableName)
105 {
106 std::string envVarString = GetEnvironmentVar(variableName);
107 return (!envVarString.empty() && envVarString == "1");
108 }
109
PrependPathToEnvironmentVar(const char * variableName,const char * path)110 bool PrependPathToEnvironmentVar(const char *variableName, const char *path)
111 {
112 std::string oldValue = GetEnvironmentVar(variableName);
113 const char *newValue = nullptr;
114 std::string buf;
115 if (oldValue.empty())
116 {
117 newValue = path;
118 }
119 else
120 {
121 buf = path;
122 buf += GetPathSeparatorForEnvironmentVar();
123 buf += oldValue;
124 newValue = buf.c_str();
125 }
126 return SetEnvironmentVar(variableName, newValue);
127 }
128
IsFullPath(std::string dirName)129 bool IsFullPath(std::string dirName)
130 {
131 if (dirName.find(GetRootDirectory()) == 0)
132 {
133 return true;
134 }
135 return false;
136 }
137
ConcatenatePath(std::string first,std::string second)138 std::string ConcatenatePath(std::string first, std::string second)
139 {
140 if (first.empty())
141 {
142 return second;
143 }
144 if (second.empty())
145 {
146 return first;
147 }
148 if (IsFullPath(second))
149 {
150 return second;
151 }
152 bool firstRedundantPathSeparator = first.find_last_of(GetPathSeparator()) == first.length() - 1;
153 bool secondRedundantPathSeparator = second.find(GetPathSeparator()) == 0;
154 if (firstRedundantPathSeparator && secondRedundantPathSeparator)
155 {
156 return first + second.substr(1);
157 }
158 else if (firstRedundantPathSeparator || secondRedundantPathSeparator)
159 {
160 return first + second;
161 }
162 return first + GetPathSeparator() + second;
163 }
164
CreateTemporaryFile()165 Optional<std::string> CreateTemporaryFile()
166 {
167 const Optional<std::string> tempDir = GetTempDirectory();
168 if (!tempDir.valid())
169 return Optional<std::string>::Invalid();
170
171 return CreateTemporaryFileInDirectory(tempDir.value());
172 }
173
PageFaultHandler(PageFaultCallback callback)174 PageFaultHandler::PageFaultHandler(PageFaultCallback callback) : mCallback(callback) {}
~PageFaultHandler()175 PageFaultHandler::~PageFaultHandler() {}
176
OpenSharedLibrary(const char * libraryName,SearchType searchType)177 Library *OpenSharedLibrary(const char *libraryName, SearchType searchType)
178 {
179 void *libraryHandle = OpenSystemLibraryAndGetError(libraryName, searchType, nullptr);
180 return new Library(libraryHandle);
181 }
182
OpenSharedLibraryWithExtension(const char * libraryName,SearchType searchType)183 Library *OpenSharedLibraryWithExtension(const char *libraryName, SearchType searchType)
184 {
185 void *libraryHandle =
186 OpenSystemLibraryWithExtensionAndGetError(libraryName, searchType, nullptr);
187 return new Library(libraryHandle);
188 }
189
OpenSharedLibraryAndGetError(const char * libraryName,SearchType searchType,std::string * errorOut)190 Library *OpenSharedLibraryAndGetError(const char *libraryName,
191 SearchType searchType,
192 std::string *errorOut)
193 {
194 void *libraryHandle = OpenSystemLibraryAndGetError(libraryName, searchType, errorOut);
195 return new Library(libraryHandle);
196 }
197
OpenSharedLibraryWithExtensionAndGetError(const char * libraryName,SearchType searchType,std::string * errorOut)198 Library *OpenSharedLibraryWithExtensionAndGetError(const char *libraryName,
199 SearchType searchType,
200 std::string *errorOut)
201 {
202 void *libraryHandle =
203 OpenSystemLibraryWithExtensionAndGetError(libraryName, searchType, errorOut);
204 return new Library(libraryHandle);
205 }
206
OpenSystemLibrary(const char * libraryName,SearchType searchType)207 void *OpenSystemLibrary(const char *libraryName, SearchType searchType)
208 {
209 return OpenSystemLibraryAndGetError(libraryName, searchType, nullptr);
210 }
211
OpenSystemLibraryWithExtension(const char * libraryName,SearchType searchType)212 void *OpenSystemLibraryWithExtension(const char *libraryName, SearchType searchType)
213 {
214 return OpenSystemLibraryWithExtensionAndGetError(libraryName, searchType, nullptr);
215 }
216
OpenSystemLibraryAndGetError(const char * libraryName,SearchType searchType,std::string * errorOut)217 void *OpenSystemLibraryAndGetError(const char *libraryName,
218 SearchType searchType,
219 std::string *errorOut)
220 {
221 std::string libraryWithExtension = std::string(libraryName);
222 std::string dotExtension = std::string(".") + GetSharedLibraryExtension();
223 // Only append the extension if it's not already present. This enables building libEGL.so.1
224 // and libGLESv2.so.2 by setting these as ANGLE_EGL_LIBRARY_NAME and ANGLE_GLESV2_LIBRARY_NAME.
225 if (libraryWithExtension.find(dotExtension) == std::string::npos)
226 {
227 libraryWithExtension += dotExtension;
228 }
229 #if ANGLE_PLATFORM_IOS_FAMILY
230 // On iOS, libraryWithExtension is a directory in which the library resides.
231 // The actual library name doesn't have an extension at all.
232 // E.g. "libEGL.framework/libEGL"
233 libraryWithExtension = libraryWithExtension + "/" + libraryName;
234 #endif
235 return OpenSystemLibraryWithExtensionAndGetError(libraryWithExtension.c_str(), searchType,
236 errorOut);
237 }
238
StripFilenameFromPath(const std::string & path)239 std::string StripFilenameFromPath(const std::string &path)
240 {
241 size_t lastPathSepLoc = path.find_last_of("\\/");
242 return (lastPathSepLoc != std::string::npos) ? path.substr(0, lastPathSepLoc) : "";
243 }
244
245 #if defined(ANGLE_PLATFORM_APPLE)
246 // https://anglebug.com/6479, similar to egl::GetCurrentThread() in libGLESv2/global_state.cpp
GetCurrentThreadUniqueId()247 uint64_t GetCurrentThreadUniqueId()
248 {
249 static std::atomic<uint64_t> globalThreadSerial;
250 static pthread_key_t tlsIndex;
251 static dispatch_once_t once;
252 dispatch_once(&once, ^{
253 auto result = pthread_key_create(&tlsIndex, nullptr);
254 ASSERT(result == 0);
255 });
256 void *tlsValue = pthread_getspecific(tlsIndex);
257 if (ANGLE_UNLIKELY(tlsValue == nullptr))
258 {
259 uint64_t threadId = ++globalThreadSerial;
260 auto result = pthread_setspecific(tlsIndex, reinterpret_cast<void *>(threadId));
261 ASSERT(result == 0);
262 return threadId;
263 }
264 return reinterpret_cast<uint64_t>(tlsValue);
265 }
266 #else
GetCurrentThreadUniqueId()267 uint64_t GetCurrentThreadUniqueId()
268 {
269 static std::atomic<uint64_t> globalThreadSerial;
270 thread_local uint64_t threadId(++globalThreadSerial);
271 return threadId;
272 }
273 #endif
274
275 } // namespace angle
276