2023-03-08 23:15:36 +01:00
/*
* Copyright ( C ) 2023 Intel Corporation
*
* SPDX - License - Identifier : MIT
*
*/
2023-08-22 10:28:03 +00:00
# include "shared/source/built_ins/built_ins.h"
2023-03-08 23:15:36 +01:00
# include "shared/source/compiler_interface/compiler_cache.h"
2023-08-22 10:28:03 +00:00
# include "shared/source/debug_settings/debug_settings_manager.h"
2023-03-08 23:15:36 +01:00
# include "shared/source/helpers/file_io.h"
2023-08-22 10:28:03 +00:00
# include "shared/source/helpers/path.h"
# include "shared/source/os_interface/windows/sys_calls.h"
# include "shared/source/utilities/directory.h"
2023-03-08 23:15:36 +01:00
# include "shared/source/utilities/io_functions.h"
# include "os_inc.h"
2023-08-22 10:28:03 +00:00
# include <algorithm>
2023-03-08 23:15:36 +01:00
namespace NEO {
2023-08-22 10:28:03 +00:00
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 ( HandleType handle ) {
OVERLAPPED overlapped = { 0 } ;
auto result = NEO : : SysCalls : : unlockFileEx ( 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 ( handle ) ;
}
2023-03-08 23:15:36 +01:00
bool CompilerCache : : evictCache ( ) {
2023-08-22 10:28:03 +00:00
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 ;
}
}
2023-03-08 23:15:36 +01:00
return true ;
}
2023-08-22 10:28:03 +00:00
void CompilerCache : : lockConfigFileAndReadSize ( const std : : string & configFilePath , HandleType & handle , size_t & directorySize ) {
bool countDirectorySize = false ;
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 ( handle = = INVALID_HANDLE_VALUE ) {
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 ( handle = = INVALID_HANDLE_VALUE ) {
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 ( handle ,
LOCKFILE_EXCLUSIVE_LOCK ,
0 ,
MAXDWORD ,
MAXDWORD ,
& overlapped ) ;
if ( result = = FALSE & & SysCalls : : getLastError ( ) = = ERROR_IO_PENDING ) { // if file is already locked by somebody else
DWORD numberOfBytesTransmitted = 0 ;
result = NEO : : SysCalls : : getOverlappedResult ( handle , & overlapped , & numberOfBytesTransmitted , TRUE ) ;
}
if ( ! result ) {
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 {
memset ( & overlapped , 0 , sizeof ( overlapped ) ) ;
result = NEO : : SysCalls : : readFileEx ( handle , & directorySize , sizeof ( directorySize ) , & overlapped , NULL ) ;
if ( ! result ) {
directorySize = 0 ;
unlockFileAndClose ( handle ) ;
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 ( ) ) ;
}
}
}
2023-03-08 23:15:36 +01:00
bool CompilerCache : : createUniqueTempFileAndWriteData ( char * tmpFilePath , const char * pBinary , size_t binarySize ) {
2023-08-22 10:28:03 +00:00
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 ) ;
2023-03-08 23:15:36 +01:00
return true ;
}
2023-08-22 10:28:03 +00:00
2023-03-08 23:15:36 +01:00
bool CompilerCache : : renameTempFileBinaryToProperName ( const std : : string & oldName , const std : : string & kernelFileHash ) {
2023-08-22 10:28:03 +00:00
return NEO : : SysCalls : : moveFileExA ( oldName . c_str ( ) , kernelFileHash . c_str ( ) , MOVEFILE_REPLACE_EXISTING ) ;
2023-03-08 23:15:36 +01:00
}
2023-08-22 10:28:03 +00:00
class HandleGuard {
public :
HandleGuard ( ) = delete ;
explicit HandleGuard ( HandleType & h ) : handle ( h ) { }
~ HandleGuard ( ) {
if ( handle ) {
unlockFileAndClose ( handle ) ;
}
}
private :
HandleType handle = nullptr ;
} ;
2023-03-08 23:15:36 +01:00
bool CompilerCache : : cacheBinary ( const std : : string & kernelFileHash , const char * pBinary , size_t binarySize ) {
if ( pBinary = = nullptr | | binarySize = = 0 ) {
return false ;
}
2023-08-22 10:28:03 +00:00
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 ) ;
HandleType hConfigFile = INVALID_HANDLE_VALUE ;
size_t directorySize = 0u ;
lockConfigFileAndReadSize ( configFilePath , hConfigFile , directorySize ) ;
if ( hConfigFile = = INVALID_HANDLE_VALUE ) {
return false ;
}
HandleGuard configGuard ( 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 ;
auto result = NEO : : SysCalls : : writeFile ( hConfigFile ,
& directorySize ,
( DWORD ) sizeof ( directorySize ) ,
& sizeWritten ,
NULL ) ;
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 ;
2023-03-08 23:15:36 +01:00
}
std : : unique_ptr < char [ ] > CompilerCache : : loadCachedBinary ( const std : : string & kernelFileHash , size_t & cachedBinarySize ) {
2023-08-22 10:28:03 +00:00
std : : string filePath = joinPath ( config . cacheDir , kernelFileHash + config . cacheFileExtension ) ;
2023-03-08 23:15:36 +01:00
return loadDataFromFile ( filePath . c_str ( ) , cachedBinarySize ) ;
}
} // namespace NEO