• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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