| // |
| // Copyright 2019 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_win32.cpp: Implementation of OS-specific functions for Windows. |
| |
| #include "common/FastVector.h" |
| #include "system_utils.h" |
| |
| #include <array> |
| |
| // Must be included in this order. |
| // clang-format off |
| #include <windows.h> |
| #include <psapi.h> |
| // clang-format on |
| |
| namespace angle |
| { |
| bool UnsetEnvironmentVar(const char *variableName) |
| { |
| return (SetEnvironmentVariableW(Widen(variableName).c_str(), nullptr) == TRUE); |
| } |
| |
| bool SetEnvironmentVar(const char *variableName, const char *value) |
| { |
| return (SetEnvironmentVariableW(Widen(variableName).c_str(), Widen(value).c_str()) == TRUE); |
| } |
| |
| std::string GetEnvironmentVar(const char *variableName) |
| { |
| std::wstring variableNameUtf16 = Widen(variableName); |
| FastVector<wchar_t, MAX_PATH> value; |
| |
| DWORD result; |
| |
| // First get the length of the variable, including the null terminator |
| result = GetEnvironmentVariableW(variableNameUtf16.c_str(), nullptr, 0); |
| |
| // Zero means the variable was not found, so return now. |
| if (result == 0) |
| { |
| return std::string(); |
| } |
| |
| // Now size the vector to fit the data, and read the environment variable. |
| value.resize(result, 0); |
| result = GetEnvironmentVariableW(variableNameUtf16.c_str(), value.data(), result); |
| |
| return Narrow(value.data()); |
| } |
| |
| void *OpenSystemLibraryWithExtensionAndGetError(const char *libraryName, |
| SearchType searchType, |
| std::string *errorOut) |
| { |
| char buffer[MAX_PATH]; |
| int ret = snprintf(buffer, MAX_PATH, "%s.%s", libraryName, GetSharedLibraryExtension()); |
| if (ret <= 0 || ret >= MAX_PATH) |
| { |
| fprintf(stderr, "Error generating library path: 0x%x", ret); |
| return nullptr; |
| } |
| |
| HMODULE libraryModule = nullptr; |
| |
| switch (searchType) |
| { |
| case SearchType::ModuleDir: |
| { |
| std::string moduleRelativePath = ConcatenatePath(GetModuleDirectory(), libraryName); |
| libraryModule = LoadLibraryW(Widen(moduleRelativePath).c_str()); |
| if (libraryModule == nullptr && errorOut) |
| { |
| *errorOut = std::string("failed to load library (SearchType::ModuleDir) ") + |
| moduleRelativePath; |
| } |
| break; |
| } |
| |
| case SearchType::SystemDir: |
| { |
| libraryModule = |
| LoadLibraryExW(Widen(libraryName).c_str(), nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32); |
| if (libraryModule == nullptr && errorOut) |
| { |
| *errorOut = |
| std::string("failed to load library (SearchType::SystemDir) ") + libraryName; |
| } |
| break; |
| } |
| |
| case SearchType::AlreadyLoaded: |
| { |
| libraryModule = GetModuleHandleW(Widen(libraryName).c_str()); |
| if (libraryModule == nullptr && errorOut) |
| { |
| *errorOut = std::string("failed to load library (SearchType::AlreadyLoaded) ") + |
| libraryName; |
| } |
| break; |
| } |
| } |
| |
| return reinterpret_cast<void *>(libraryModule); |
| } |
| |
| namespace |
| { |
| class Win32PageFaultHandler : public PageFaultHandler |
| { |
| public: |
| Win32PageFaultHandler(PageFaultCallback callback) : PageFaultHandler(callback) {} |
| ~Win32PageFaultHandler() override {} |
| |
| bool enable() override; |
| bool disable() override; |
| |
| LONG handle(PEXCEPTION_POINTERS pExceptionInfo); |
| |
| private: |
| void *mVectoredExceptionHandler = nullptr; |
| }; |
| |
| Win32PageFaultHandler *gWin32PageFaultHandler = nullptr; |
| static LONG CALLBACK VectoredExceptionHandler(PEXCEPTION_POINTERS info) |
| { |
| return gWin32PageFaultHandler->handle(info); |
| } |
| |
| bool SetMemoryProtection(uintptr_t start, size_t size, DWORD protections) |
| { |
| DWORD oldProtect; |
| BOOL res = VirtualProtect(reinterpret_cast<LPVOID>(start), size, protections, &oldProtect); |
| if (!res) |
| { |
| DWORD lastError = GetLastError(); |
| fprintf(stderr, "VirtualProtect failed: 0x%lx\n", lastError); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| LONG Win32PageFaultHandler::handle(PEXCEPTION_POINTERS info) |
| { |
| bool found = false; |
| |
| if (info->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION && |
| info->ExceptionRecord->NumberParameters >= 2 && |
| info->ExceptionRecord->ExceptionInformation[0] == 1) |
| { |
| found = mCallback(static_cast<uintptr_t>(info->ExceptionRecord->ExceptionInformation[1])) == |
| PageFaultHandlerRangeType::InRange; |
| } |
| |
| if (found) |
| { |
| return EXCEPTION_CONTINUE_EXECUTION; |
| } |
| else |
| { |
| return EXCEPTION_CONTINUE_SEARCH; |
| } |
| } |
| |
| bool Win32PageFaultHandler::disable() |
| { |
| if (mVectoredExceptionHandler) |
| { |
| ULONG res = RemoveVectoredExceptionHandler(mVectoredExceptionHandler); |
| mVectoredExceptionHandler = nullptr; |
| if (res == 0) |
| { |
| DWORD lastError = GetLastError(); |
| fprintf(stderr, "RemoveVectoredExceptionHandler failed: 0x%lx\n", lastError); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| bool Win32PageFaultHandler::enable() |
| { |
| if (mVectoredExceptionHandler) |
| { |
| return true; |
| } |
| |
| PVECTORED_EXCEPTION_HANDLER handler = |
| reinterpret_cast<PVECTORED_EXCEPTION_HANDLER>(&VectoredExceptionHandler); |
| |
| mVectoredExceptionHandler = AddVectoredExceptionHandler(1, handler); |
| |
| if (!mVectoredExceptionHandler) |
| { |
| DWORD lastError = GetLastError(); |
| fprintf(stderr, "AddVectoredExceptionHandler failed: 0x%lx\n", lastError); |
| return false; |
| } |
| return true; |
| } |
| } // namespace |
| |
| // Set write protection |
| bool ProtectMemory(uintptr_t start, size_t size) |
| { |
| return SetMemoryProtection(start, size, PAGE_READONLY); |
| } |
| |
| // Allow reading and writing |
| bool UnprotectMemory(uintptr_t start, size_t size) |
| { |
| return SetMemoryProtection(start, size, PAGE_READWRITE); |
| } |
| |
| size_t GetPageSize() |
| { |
| SYSTEM_INFO info; |
| GetSystemInfo(&info); |
| return static_cast<size_t>(info.dwPageSize); |
| } |
| |
| PageFaultHandler *CreatePageFaultHandler(PageFaultCallback callback) |
| { |
| gWin32PageFaultHandler = new Win32PageFaultHandler(callback); |
| return gWin32PageFaultHandler; |
| } |
| |
| uint64_t GetProcessMemoryUsageKB() |
| { |
| PROCESS_MEMORY_COUNTERS_EX pmc; |
| ::GetProcessMemoryInfo(::GetCurrentProcess(), reinterpret_cast<PROCESS_MEMORY_COUNTERS *>(&pmc), |
| sizeof(pmc)); |
| return static_cast<uint64_t>(pmc.PrivateUsage) / 1024ull; |
| } |
| } // namespace angle |