compute-runtime/shared/source/compiler_interface/windows/compiler_cache_windows.cpp

309 lines
13 KiB
C++

/*
* Copyright (C) 2023 Intel Corporation
*
* SPDX-License-Identifier: MIT
*
*/
#include "shared/source/built_ins/built_ins.h"
#include "shared/source/compiler_interface/compiler_cache.h"
#include "shared/source/debug_settings/debug_settings_manager.h"
#include "shared/source/helpers/file_io.h"
#include "shared/source/helpers/path.h"
#include "shared/source/os_interface/windows/sys_calls.h"
#include "shared/source/utilities/directory.h"
#include "shared/source/utilities/io_functions.h"
#include "os_inc.h"
#include <algorithm>
namespace NEO {
struct ElementsStruct {
std::string path;
FILETIME lastAccessTime;
uint64_t fileSize;
};
std::vector<ElementsStruct> getFiles(const std::string &path) {
std::vector<ElementsStruct> files;
std::string newPath;
WIN32_FIND_DATAA ffd;
HANDLE hFind = INVALID_HANDLE_VALUE;
if (path.c_str()[path.size() - 1] == '\\') {
return files;
} else {
newPath = path + "/*";
}
hFind = NEO::SysCalls::findFirstFileA(newPath.c_str(), &ffd);
if (hFind == INVALID_HANDLE_VALUE) {
NEO::printDebugString(NEO::DebugManager.flags.PrintDebugMessages.get(), stderr, "PID %d [Cache failure]: File search failed! error code: %lu\n", NEO::SysCalls::getProcessId(), SysCalls::getLastError());
return files;
}
do {
auto fileName = joinPath(path, ffd.cFileName);
if (fileName.find(".cl_cache") != fileName.npos ||
fileName.find(".l0_cache") != fileName.npos ||
fileName.find(".ocloc_cache") != fileName.npos) {
uint64_t fileSize = (ffd.nFileSizeHigh * (MAXDWORD + 1)) + ffd.nFileSizeLow;
files.push_back({fileName, ffd.ftLastAccessTime, fileSize});
}
} while (NEO::SysCalls::findNextFileA(hFind, &ffd) != 0);
NEO::SysCalls::findClose(hFind);
std::sort(files.begin(), files.end(), [](const ElementsStruct &a, const ElementsStruct &b) { return CompareFileTime(&a.lastAccessTime, &b.lastAccessTime) < 0; });
return files;
}
void unlockFileAndClose(UnifiedHandle handle) {
OVERLAPPED overlapped = {0};
auto result = NEO::SysCalls::unlockFileEx(std::get<void *>(handle), 0, MAXDWORD, MAXDWORD, &overlapped);
if (!result) {
NEO::printDebugString(NEO::DebugManager.flags.PrintDebugMessages.get(), stderr, "PID %d [Cache failure]: Unlock file failed! error code: %lu\n", NEO::SysCalls::getProcessId(), SysCalls::getLastError());
}
NEO::SysCalls::closeHandle(std::get<void *>(handle));
}
bool CompilerCache::evictCache() {
auto files = getFiles(config.cacheDir);
auto evictionLimit = config.cacheSize / 3;
uint64_t evictionSizeCount = 0;
for (const auto &file : files) {
NEO::SysCalls::deleteFileA(file.path.c_str());
evictionSizeCount += file.fileSize;
if (evictionSizeCount > evictionLimit) {
return true;
}
}
return true;
}
void CompilerCache::lockConfigFileAndReadSize(const std::string &configFilePath, UnifiedHandle &handle, size_t &directorySize) {
bool countDirectorySize = false;
std::get<void *>(handle) = NEO::SysCalls::createFileA(configFilePath.c_str(),
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (std::get<void *>(handle) == INVALID_HANDLE_VALUE) {
if (SysCalls::getLastError() != ERROR_FILE_NOT_FOUND) {
NEO::printDebugString(NEO::DebugManager.flags.PrintDebugMessages.get(), stderr, "PID %d [Cache failure]: Open config file failed! error code: %lu\n", NEO::SysCalls::getProcessId(), SysCalls::getLastError());
return;
}
std::get<void *>(handle) = NEO::SysCalls::createFileA(configFilePath.c_str(),
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
CREATE_NEW,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (std::get<void *>(handle) == INVALID_HANDLE_VALUE) {
std::get<void *>(handle) = NEO::SysCalls::createFileA(configFilePath.c_str(),
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
} else {
countDirectorySize = true;
}
}
OVERLAPPED overlapped = {0};
auto result = NEO::SysCalls::lockFileEx(std::get<void *>(handle),
LOCKFILE_EXCLUSIVE_LOCK,
0,
MAXDWORD,
MAXDWORD,
&overlapped);
if (!result) {
std::get<void *>(handle) = INVALID_HANDLE_VALUE;
NEO::printDebugString(NEO::DebugManager.flags.PrintDebugMessages.get(), stderr, "PID %d [Cache failure]: Lock config file failed! error code: %lu\n", NEO::SysCalls::getProcessId(), SysCalls::getLastError());
return;
}
if (countDirectorySize) {
auto files = getFiles(config.cacheDir);
for (const auto &file : files) {
directorySize += static_cast<size_t>(file.fileSize);
}
} else {
DWORD fPointer = NEO::SysCalls::setFilePointer(std::get<void *>(handle),
0,
NULL,
FILE_BEGIN);
if (fPointer != 0) {
directorySize = 0;
unlockFileAndClose(std::get<void *>(handle));
std::get<void *>(handle) = INVALID_HANDLE_VALUE;
NEO::printDebugString(NEO::DebugManager.flags.PrintDebugMessages.get(), stderr, "PID %d [Cache failure]: File pointer move failed! error code: %lu\n", NEO::SysCalls::getProcessId(), SysCalls::getLastError());
return;
}
DWORD numOfBytesRead = 0;
result = NEO::SysCalls::readFile(std::get<void *>(handle),
&directorySize,
sizeof(directorySize),
&numOfBytesRead,
NULL);
if (!result) {
directorySize = 0;
unlockFileAndClose(std::get<void *>(handle));
std::get<void *>(handle) = INVALID_HANDLE_VALUE;
NEO::printDebugString(NEO::DebugManager.flags.PrintDebugMessages.get(), stderr, "PID %d [Cache failure]: Read config failed! error code: %lu\n", NEO::SysCalls::getProcessId(), SysCalls::getLastError());
}
}
}
bool CompilerCache::createUniqueTempFileAndWriteData(char *tmpFilePath, const char *pBinary, size_t binarySize) {
auto result = NEO::SysCalls::getTempFileNameA(config.cacheDir.c_str(), "TMP", 0, tmpFilePath);
if (result == 0) {
NEO::printDebugString(NEO::DebugManager.flags.PrintDebugMessages.get(), stderr, "PID %d [Cache failure]: Creating temporary file name failed! error code: %lu\n", NEO::SysCalls::getProcessId(), SysCalls::getLastError());
return false;
}
auto hTempFile = NEO::SysCalls::createFileA((LPCSTR)tmpFilePath, // file name
GENERIC_WRITE, // open for write
0, // do not share
NULL, // default security
CREATE_ALWAYS, // overwrite existing
FILE_ATTRIBUTE_NORMAL, // normal file
NULL); // no template
if (hTempFile == INVALID_HANDLE_VALUE) {
NEO::printDebugString(NEO::DebugManager.flags.PrintDebugMessages.get(), stderr, "PID %d [Cache failure]: Creating temporary file failed! error code: %lu\n", NEO::SysCalls::getProcessId(), SysCalls::getLastError());
return false;
}
DWORD dwBytesWritten = 0;
auto result2 = NEO::SysCalls::writeFile(hTempFile,
pBinary,
(DWORD)binarySize,
&dwBytesWritten,
NULL);
if (!result2) {
NEO::printDebugString(NEO::DebugManager.flags.PrintDebugMessages.get(), stderr, "PID %d [Cache failure]: Writing to temporary file failed! error code: %lu\n", NEO::SysCalls::getProcessId(), SysCalls::getLastError());
} else if (dwBytesWritten != (DWORD)binarySize) {
NEO::printDebugString(NEO::DebugManager.flags.PrintDebugMessages.get(), stderr, "PID %d [Cache failure]: Writing to temporary file failed! Incorrect number of bytes written: %lu vs %lu\n", NEO::SysCalls::getProcessId(), dwBytesWritten, (DWORD)binarySize);
}
NEO::SysCalls::closeHandle(hTempFile);
return true;
}
bool CompilerCache::renameTempFileBinaryToProperName(const std::string &oldName, const std::string &kernelFileHash) {
return NEO::SysCalls::moveFileExA(oldName.c_str(), kernelFileHash.c_str(), MOVEFILE_REPLACE_EXISTING);
}
class HandleGuard {
public:
HandleGuard() = delete;
explicit HandleGuard(void *&h) : handle(h) {}
~HandleGuard() {
if (handle) {
unlockFileAndClose(handle);
}
}
private:
void *handle = nullptr;
};
bool CompilerCache::cacheBinary(const std::string &kernelFileHash, const char *pBinary, size_t binarySize) {
if (pBinary == nullptr || binarySize == 0) {
return false;
}
std::unique_lock<std::mutex> lock(cacheAccessMtx);
constexpr std::string_view configFileName = "config.file";
std::string configFilePath = joinPath(config.cacheDir, configFileName.data());
std::string cacheFilePath = joinPath(config.cacheDir, kernelFileHash + config.cacheFileExtension);
UnifiedHandle hConfigFile{INVALID_HANDLE_VALUE};
size_t directorySize = 0u;
lockConfigFileAndReadSize(configFilePath, hConfigFile, directorySize);
if (std::get<void *>(hConfigFile) == INVALID_HANDLE_VALUE) {
return false;
}
HandleGuard configGuard(std::get<void *>(hConfigFile));
DWORD cacheFileAttr = 0;
cacheFileAttr = NEO::SysCalls::getFileAttributesA(cacheFilePath.c_str());
if ((cacheFileAttr != INVALID_FILE_ATTRIBUTES) &&
(SysCalls::getLastError() != ERROR_FILE_NOT_FOUND)) {
return true;
}
size_t maxSize = config.cacheSize;
if (maxSize < (directorySize + binarySize)) {
if (!evictCache()) {
return false;
}
}
std::string tmpFileName = "cl_cache.XXXXXX";
std::string tmpFilePath = joinPath(config.cacheDir, tmpFileName);
if (!createUniqueTempFileAndWriteData(tmpFilePath.data(), pBinary, binarySize)) {
return false;
}
if (!renameTempFileBinaryToProperName(tmpFilePath, cacheFilePath)) {
NEO::SysCalls::deleteFileA(tmpFilePath.c_str());
return false;
}
directorySize += binarySize;
DWORD sizeWritten = 0;
OVERLAPPED overlapped = {0};
auto result = NEO::SysCalls::writeFile(std::get<void *>(hConfigFile),
&directorySize,
(DWORD)sizeof(directorySize),
&sizeWritten,
&overlapped);
if (!result) {
NEO::printDebugString(NEO::DebugManager.flags.PrintDebugMessages.get(), stderr, "PID %d [Cache failure]: Writing to config file failed! error code: %lu\n", NEO::SysCalls::getProcessId(), SysCalls::getLastError());
} else if (sizeWritten != (DWORD)sizeof(directorySize)) {
NEO::printDebugString(NEO::DebugManager.flags.PrintDebugMessages.get(), stderr, "PID %d [Cache failure]: Writing to config file failed! Incorrect number of bytes written: %lu vs %lu\n", NEO::SysCalls::getProcessId(), sizeWritten, (DWORD)sizeof(directorySize));
}
return true;
}
std::unique_ptr<char[]> CompilerCache::loadCachedBinary(const std::string &kernelFileHash, size_t &cachedBinarySize) {
std::string filePath = joinPath(config.cacheDir, kernelFileHash + config.cacheFileExtension);
return loadDataFromFile(filePath.c_str(), cachedBinarySize);
}
} // namespace NEO