1 //
2 // Copyright 2019 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 // system_utils_unittest.cpp: Unit tests for ANGLE's system utility functions
7
8 #include "gmock/gmock.h"
9 #include "gtest/gtest.h"
10
11 #include "common/mathutil.h"
12 #include "common/system_utils.h"
13 #include "util/test_utils.h"
14
15 #include <vector>
16
17 #if defined(ANGLE_PLATFORM_POSIX)
18 # include <signal.h>
19 #endif
20
21 using namespace angle;
22
23 namespace
24 {
25 // Test getting the executable path
TEST(SystemUtils,ExecutablePath)26 TEST(SystemUtils, ExecutablePath)
27 {
28 // TODO: fuchsia support. http://anglebug.com/3161
29 #if !defined(ANGLE_PLATFORM_FUCHSIA)
30 std::string executablePath = GetExecutablePath();
31 EXPECT_NE("", executablePath);
32 #endif
33 }
34
35 // Test getting the executable directory
TEST(SystemUtils,ExecutableDir)36 TEST(SystemUtils, ExecutableDir)
37 {
38 // TODO: fuchsia support. http://anglebug.com/3161
39 #if !defined(ANGLE_PLATFORM_FUCHSIA)
40 std::string executableDir = GetExecutableDirectory();
41 EXPECT_NE("", executableDir);
42
43 std::string executablePath = GetExecutablePath();
44 EXPECT_LT(executableDir.size(), executablePath.size());
45 EXPECT_EQ(0, strncmp(executableDir.c_str(), executablePath.c_str(), executableDir.size()));
46 #endif
47 }
48
49 // Test setting environment variables
TEST(SystemUtils,Environment)50 TEST(SystemUtils, Environment)
51 {
52 constexpr char kEnvVarName[] = "UNITTEST_ENV_VARIABLE";
53 constexpr char kEnvVarValue[] = "The quick brown fox jumps over the lazy dog";
54
55 bool setEnvDone = SetEnvironmentVar(kEnvVarName, kEnvVarValue);
56 EXPECT_TRUE(setEnvDone);
57
58 std::string readback = GetEnvironmentVar(kEnvVarName);
59 EXPECT_EQ(kEnvVarValue, readback);
60
61 bool unsetEnvDone = UnsetEnvironmentVar(kEnvVarName);
62 EXPECT_TRUE(unsetEnvDone);
63
64 readback = GetEnvironmentVar(kEnvVarName);
65 EXPECT_EQ("", readback);
66 }
67
68 // Test CPU time measurement with a small operation
69 // (pretty much the measurement itself)
TEST(SystemUtils,CpuTimeSmallOp)70 TEST(SystemUtils, CpuTimeSmallOp)
71 {
72 double cpuTimeStart = GetCurrentProcessCpuTime();
73 double cpuTimeEnd = GetCurrentProcessCpuTime();
74 EXPECT_GE(cpuTimeEnd, cpuTimeStart);
75 }
76
77 // Test CPU time measurement with a sleepy operation
TEST(SystemUtils,CpuTimeSleepy)78 TEST(SystemUtils, CpuTimeSleepy)
79 {
80 double cpuTimeStart = GetCurrentProcessCpuTime();
81 angle::Sleep(1);
82 double cpuTimeEnd = GetCurrentProcessCpuTime();
83 EXPECT_GE(cpuTimeEnd, cpuTimeStart);
84 }
85
86 // Test CPU time measurement with a heavy operation
TEST(SystemUtils,CpuTimeHeavyOp)87 TEST(SystemUtils, CpuTimeHeavyOp)
88 {
89 constexpr size_t bufferSize = 1048576;
90 std::vector<uint8_t> buffer(bufferSize, 1);
91 double cpuTimeStart = GetCurrentProcessCpuTime();
92 memset(buffer.data(), 0, bufferSize);
93 double cpuTimeEnd = GetCurrentProcessCpuTime();
94 EXPECT_GE(cpuTimeEnd, cpuTimeStart);
95 }
96
97 #if defined(ANGLE_PLATFORM_POSIX)
TEST(SystemUtils,ConcatenatePathSimple)98 TEST(SystemUtils, ConcatenatePathSimple)
99 {
100 std::string path1 = "/this/is/path1";
101 std::string path2 = "this/is/path2";
102 std::string expected = "/this/is/path1/this/is/path2";
103 EXPECT_EQ(ConcatenatePath(path1, path2), expected);
104 }
105
TEST(SystemUtils,ConcatenatePath1Empty)106 TEST(SystemUtils, ConcatenatePath1Empty)
107 {
108 std::string path1 = "";
109 std::string path2 = "this/is/path2";
110 std::string expected = "this/is/path2";
111 EXPECT_EQ(ConcatenatePath(path1, path2), expected);
112 }
113
TEST(SystemUtils,ConcatenatePath2Empty)114 TEST(SystemUtils, ConcatenatePath2Empty)
115 {
116 std::string path1 = "/this/is/path1";
117 std::string path2 = "";
118 std::string expected = "/this/is/path1";
119 EXPECT_EQ(ConcatenatePath(path1, path2), expected);
120 }
121
TEST(SystemUtils,ConcatenatePath2FullPath)122 TEST(SystemUtils, ConcatenatePath2FullPath)
123 {
124 std::string path1 = "/this/is/path1";
125 std::string path2 = "/this/is/path2";
126 std::string expected = "/this/is/path2";
127 EXPECT_EQ(ConcatenatePath(path1, path2), expected);
128 }
129
TEST(SystemUtils,ConcatenatePathRedundantSeparators)130 TEST(SystemUtils, ConcatenatePathRedundantSeparators)
131 {
132 std::string path1 = "/this/is/path1/";
133 std::string path2 = "this/is/path2";
134 std::string expected = "/this/is/path1/this/is/path2";
135 EXPECT_EQ(ConcatenatePath(path1, path2), expected);
136 }
137
TEST(SystemUtils,IsFullPath)138 TEST(SystemUtils, IsFullPath)
139 {
140 std::string path1 = "/this/is/path1/";
141 std::string path2 = "this/is/path2";
142 EXPECT_TRUE(IsFullPath(path1));
143 EXPECT_FALSE(IsFullPath(path2));
144 }
145 #elif defined(ANGLE_PLATFORM_WINDOWS)
TEST(SystemUtils,ConcatenatePathSimple)146 TEST(SystemUtils, ConcatenatePathSimple)
147 {
148 std::string path1 = "C:\\this\\is\\path1";
149 std::string path2 = "this\\is\\path2";
150 std::string expected = "C:\\this\\is\\path1\\this\\is\\path2";
151 EXPECT_EQ(ConcatenatePath(path1, path2), expected);
152 }
153
TEST(SystemUtils,ConcatenatePath1Empty)154 TEST(SystemUtils, ConcatenatePath1Empty)
155 {
156 std::string path1 = "";
157 std::string path2 = "this\\is\\path2";
158 std::string expected = "this\\is\\path2";
159 EXPECT_EQ(ConcatenatePath(path1, path2), expected);
160 }
161
TEST(SystemUtils,ConcatenatePath2Empty)162 TEST(SystemUtils, ConcatenatePath2Empty)
163 {
164 std::string path1 = "C:\\this\\is\\path1";
165 std::string path2 = "";
166 std::string expected = "C:\\this\\is\\path1";
167 EXPECT_EQ(ConcatenatePath(path1, path2), expected);
168 }
169
TEST(SystemUtils,ConcatenatePath2FullPath)170 TEST(SystemUtils, ConcatenatePath2FullPath)
171 {
172 std::string path1 = "C:\\this\\is\\path1";
173 std::string path2 = "C:\\this\\is\\path2";
174 std::string expected = "C:\\this\\is\\path2";
175 EXPECT_EQ(ConcatenatePath(path1, path2), expected);
176 }
177
TEST(SystemUtils,ConcatenatePathRedundantSeparators)178 TEST(SystemUtils, ConcatenatePathRedundantSeparators)
179 {
180 std::string path1 = "C:\\this\\is\\path1\\";
181 std::string path2 = "this\\is\\path2";
182 std::string expected = "C:\\this\\is\\path1\\this\\is\\path2";
183 EXPECT_EQ(ConcatenatePath(path1, path2), expected);
184 }
185
TEST(SystemUtils,ConcatenatePathRedundantSeparators2)186 TEST(SystemUtils, ConcatenatePathRedundantSeparators2)
187 {
188 std::string path1 = "C:\\this\\is\\path1\\";
189 std::string path2 = "\\this\\is\\path2";
190 std::string expected = "C:\\this\\is\\path1\\this\\is\\path2";
191 EXPECT_EQ(ConcatenatePath(path1, path2), expected);
192 }
193
TEST(SystemUtils,ConcatenatePathRedundantSeparators3)194 TEST(SystemUtils, ConcatenatePathRedundantSeparators3)
195 {
196 std::string path1 = "C:\\this\\is\\path1";
197 std::string path2 = "\\this\\is\\path2";
198 std::string expected = "C:\\this\\is\\path1\\this\\is\\path2";
199 EXPECT_EQ(ConcatenatePath(path1, path2), expected);
200 }
201
TEST(SystemUtils,IsFullPath)202 TEST(SystemUtils, IsFullPath)
203 {
204 std::string path1 = "C:\\this\\is\\path1\\";
205 std::string path2 = "this\\is\\path2";
206 EXPECT_TRUE(IsFullPath(path1));
207 EXPECT_FALSE(IsFullPath(path2));
208 }
209 #endif
210
211 // Test retrieving page size
TEST(SystemUtils,PageSize)212 TEST(SystemUtils, PageSize)
213 {
214 size_t pageSize = GetPageSize();
215 EXPECT_TRUE(pageSize > 0);
216 }
217
218 // mprotect is not supported on Fuchsia right now.
219 #if defined(ANGLE_PLATFORM_FUCHSIA)
220 # define MAYBE_PageFaultHandlerInit DISABLED_PageFaultHandlerInit
221 # define MAYBE_PageFaultHandlerProtect DISABLED_PageFaultHandlerProtect
222 # define MAYBE_PageFaultHandlerDefaultHandler DISABLED_PageFaultHandlerDefaultHandler
223 // mprotect tests hang on macOS M1.
224 #elif defined(ANGLE_PLATFORM_MACOS)
225 # define MAYBE_PageFaultHandlerInit PageFaultHandlerInit
226 # define MAYBE_PageFaultHandlerProtect DISABLED_PageFaultHandlerProtect
227 # define MAYBE_PageFaultHandlerDefaultHandler DISABLED_PageFaultHandlerDefaultHandler
228 #else
229 # define MAYBE_PageFaultHandlerInit PageFaultHandlerInit
230 # define MAYBE_PageFaultHandlerProtect PageFaultHandlerProtect
231 # define MAYBE_PageFaultHandlerDefaultHandler PageFaultHandlerDefaultHandler
232 #endif // defined(ANGLE_PLATFORM_FUCHSIA)
233
234 // Test initializing and cleaning up page fault handler.
TEST(SystemUtils,MAYBE_PageFaultHandlerInit)235 TEST(SystemUtils, MAYBE_PageFaultHandlerInit)
236 {
237 PageFaultCallback callback = [](uintptr_t address) {
238 return PageFaultHandlerRangeType::InRange;
239 };
240
241 std::unique_ptr<PageFaultHandler> handler(CreatePageFaultHandler(callback));
242
243 EXPECT_TRUE(handler->enable());
244 EXPECT_TRUE(handler->disable());
245 }
246
247 // Test write protecting and unprotecting memory
TEST(SystemUtils,MAYBE_PageFaultHandlerProtect)248 TEST(SystemUtils, MAYBE_PageFaultHandlerProtect)
249 {
250 size_t pageSize = GetPageSize();
251 EXPECT_TRUE(pageSize > 0);
252
253 std::vector<float> data = std::vector<float>(100);
254 size_t dataSize = sizeof(float) * data.size();
255 uintptr_t dataStart = reinterpret_cast<uintptr_t>(data.data());
256 uintptr_t protectionStart = rx::roundDownPow2(dataStart, pageSize);
257 uintptr_t protectionEnd = rx::roundUpPow2(dataStart + dataSize, pageSize);
258
259 std::mutex mutex;
260 bool handlerCalled = false;
261
262 PageFaultCallback callback = [&mutex, pageSize, dataStart, dataSize,
263 &handlerCalled](uintptr_t address) {
264 if (address >= dataStart && address < dataStart + dataSize)
265 {
266 std::lock_guard<std::mutex> lock(mutex);
267 uintptr_t pageStart = rx::roundDownPow2(address, pageSize);
268 EXPECT_TRUE(UnprotectMemory(pageStart, pageSize));
269 handlerCalled = true;
270 return PageFaultHandlerRangeType::InRange;
271 }
272 else
273 {
274 return PageFaultHandlerRangeType::OutOfRange;
275 }
276 };
277
278 std::unique_ptr<PageFaultHandler> handler(CreatePageFaultHandler(callback));
279 handler->enable();
280
281 size_t protectionSize = protectionEnd - protectionStart;
282
283 // Test Protect
284 EXPECT_TRUE(ProtectMemory(protectionStart, protectionSize));
285
286 data[0] = 0.0;
287
288 {
289 std::lock_guard<std::mutex> lock(mutex);
290 EXPECT_TRUE(handlerCalled);
291 }
292
293 // Test Protect and unprotect
294 EXPECT_TRUE(ProtectMemory(protectionStart, protectionSize));
295 EXPECT_TRUE(UnprotectMemory(protectionStart, protectionSize));
296
297 handlerCalled = false;
298 data[0] = 0.0;
299 EXPECT_FALSE(handlerCalled);
300
301 // Clean up
302 EXPECT_TRUE(handler->disable());
303 }
304
305 #if defined(ANGLE_PLATFORM_POSIX)
306 std::mutex gCustomHandlerMutex;
307 bool gCustomHandlerCalled = false;
308
CustomSegfaultHandlerFunction(int sig,siginfo_t * info,void * unused)309 void CustomSegfaultHandlerFunction(int sig, siginfo_t *info, void *unused)
310 {
311 std::lock_guard<std::mutex> lock(gCustomHandlerMutex);
312 gCustomHandlerCalled = true;
313 }
314
315 // Test if the default page fault handler is called on OutOfRange.
TEST(SystemUtils,MAYBE_PageFaultHandlerDefaultHandler)316 TEST(SystemUtils, MAYBE_PageFaultHandlerDefaultHandler)
317 {
318 size_t pageSize = GetPageSize();
319 EXPECT_TRUE(pageSize > 0);
320
321 std::vector<float> data = std::vector<float>(100);
322 size_t dataSize = sizeof(float) * data.size();
323 uintptr_t dataStart = reinterpret_cast<uintptr_t>(data.data());
324 uintptr_t protectionStart = rx::roundDownPow2(dataStart, pageSize);
325 uintptr_t protectionEnd = rx::roundUpPow2(dataStart + dataSize, pageSize);
326 size_t protectionSize = protectionEnd - protectionStart;
327
328 // Create custom handler
329 struct sigaction sigAction = {};
330 sigAction.sa_flags = SA_SIGINFO;
331 sigAction.sa_sigaction = &CustomSegfaultHandlerFunction;
332 sigemptyset(&sigAction.sa_mask);
333
334 struct sigaction oldSigSegv = {};
335 ASSERT_TRUE(sigaction(SIGSEGV, &sigAction, &oldSigSegv) == 0);
336 struct sigaction oldSigBus = {};
337 ASSERT_TRUE(sigaction(SIGBUS, &sigAction, &oldSigBus) == 0);
338
339 PageFaultCallback callback = [protectionStart, protectionSize](uintptr_t address) {
340 EXPECT_TRUE(UnprotectMemory(protectionStart, protectionSize));
341 return PageFaultHandlerRangeType::OutOfRange;
342 };
343
344 std::unique_ptr<PageFaultHandler> handler(CreatePageFaultHandler(callback));
345 handler->enable();
346
347 // Test Protect
348 EXPECT_TRUE(ProtectMemory(protectionStart, protectionSize));
349
350 data[0] = 0.0;
351
352 {
353 std::lock_guard<std::mutex> lock(gCustomHandlerMutex);
354 EXPECT_TRUE(gCustomHandlerCalled);
355 }
356
357 // Clean up
358 EXPECT_TRUE(handler->disable());
359
360 ASSERT_TRUE(sigaction(SIGSEGV, &oldSigSegv, nullptr) == 0);
361 ASSERT_TRUE(sigaction(SIGBUS, &oldSigBus, nullptr) == 0);
362 }
363 #else
TEST(SystemUtils,MAYBE_PageFaultHandlerDefaultHandler)364 TEST(SystemUtils, MAYBE_PageFaultHandlerDefaultHandler) {}
365 #endif
366
367 } // anonymous namespace
368