2023-03-08 23:15:36 +01:00
/*
2025-08-14 08:53:15 +00:00
* Copyright ( C ) 2023 - 2025 Intel Corporation
2023-03-08 23:15:36 +01:00
*
* 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"
2025-08-14 08:53:15 +00:00
# include "shared/source/helpers/non_copyable_or_moveable.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 ;
2023-10-03 09:39:59 +00:00
WIN32_FIND_DATAA ffd { 0 } ;
2023-08-22 10:28:03 +00:00
HANDLE hFind = INVALID_HANDLE_VALUE ;
2024-09-09 11:44:08 +00:00
std : : string newPath = joinPath ( path , " * " ) ;
2023-08-22 10:28:03 +00:00
hFind = NEO : : SysCalls : : findFirstFileA ( newPath . c_str ( ) , & ffd ) ;
if ( hFind = = INVALID_HANDLE_VALUE ) {
2023-11-30 08:32:25 +00:00
NEO : : printDebugString ( NEO : : debugManager . flags . PrintDebugMessages . get ( ) , stderr , " PID %d [Cache failure]: File search failed! error code: %lu \n " , NEO : : SysCalls : : getProcessId ( ) , SysCalls : : getLastError ( ) ) ;
2023-08-22 10:28:03 +00:00
return files ;
}
do {
2024-09-09 11:44:08 +00:00
if ( ffd . dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) {
continue ;
}
2023-08-22 10:28:03 +00:00
auto fileName = joinPath ( path , ffd . cFileName ) ;
if ( fileName . find ( " .cl_cache " ) ! = fileName . npos | |
2023-10-03 09:39:59 +00:00
fileName . find ( " .l0_cache " ) ! = fileName . npos ) {
2023-08-22 10:28:03 +00:00
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 ;
}
2023-09-15 14:48:06 +00:00
void unlockFileAndClose ( UnifiedHandle handle ) {
2023-08-22 10:28:03 +00:00
OVERLAPPED overlapped = { 0 } ;
2023-09-15 14:48:06 +00:00
auto result = NEO : : SysCalls : : unlockFileEx ( std : : get < void * > ( handle ) , 0 , MAXDWORD , MAXDWORD , & overlapped ) ;
2023-08-22 10:28:03 +00:00
if ( ! result ) {
2023-11-30 08:32:25 +00:00
NEO : : printDebugString ( NEO : : debugManager . flags . PrintDebugMessages . get ( ) , stderr , " PID %d [Cache failure]: Unlock file failed! error code: %lu \n " , NEO : : SysCalls : : getProcessId ( ) , SysCalls : : getLastError ( ) ) ;
2023-08-22 10:28:03 +00:00
}
2023-09-15 14:48:06 +00:00
NEO : : SysCalls : : closeHandle ( std : : get < void * > ( handle ) ) ;
2023-08-22 10:28:03 +00:00
}
2023-10-03 09:39:59 +00:00
bool CompilerCache : : evictCache ( uint64_t & bytesEvicted ) {
bytesEvicted = 0 ;
const auto cacheFiles = getFiles ( config . cacheDir ) ;
const auto evictionLimit = config . cacheSize / 3 ;
2023-08-22 10:28:03 +00:00
2023-10-03 09:39:59 +00:00
for ( const auto & file : cacheFiles ) {
auto res = NEO : : SysCalls : : deleteFileA ( file . path . c_str ( ) ) ;
if ( ! res ) {
continue ;
}
2023-08-22 10:28:03 +00:00
2023-10-03 09:39:59 +00:00
bytesEvicted + = file . fileSize ;
if ( bytesEvicted > evictionLimit ) {
2023-08-22 10:28:03 +00:00
return true ;
}
}
2023-03-08 23:15:36 +01:00
return true ;
}
2023-09-15 14:48:06 +00:00
void CompilerCache : : lockConfigFileAndReadSize ( const std : : string & configFilePath , UnifiedHandle & handle , size_t & directorySize ) {
2023-08-22 10:28:03 +00:00
bool countDirectorySize = false ;
2023-09-15 14:48:06 +00:00
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 ) {
2023-10-02 09:35:55 +00:00
if ( SysCalls : : getLastError ( ) ! = ERROR_FILE_NOT_FOUND ) {
2023-11-30 08:32:25 +00:00
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 ( ) ) ;
2023-10-02 09:35:55 +00:00
return ;
}
2023-09-15 14:48:06 +00:00
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 ) {
2024-09-09 11:44:08 +00:00
NEO : : printDebugString ( NEO : : debugManager . flags . PrintDebugMessages . get ( ) , stderr , " PID %d [Cache failure]: Create config file failed! error code: %lu \n " , GetCurrentProcessId ( ) , GetLastError ( ) ) ;
2023-09-15 14:48:06 +00:00
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 ) ;
2023-08-22 10:28:03 +00:00
} else {
countDirectorySize = true ;
}
}
OVERLAPPED overlapped = { 0 } ;
2023-09-15 14:48:06 +00:00
auto result = NEO : : SysCalls : : lockFileEx ( std : : get < void * > ( handle ) ,
2023-08-22 10:28:03 +00:00
LOCKFILE_EXCLUSIVE_LOCK ,
0 ,
MAXDWORD ,
MAXDWORD ,
& overlapped ) ;
if ( ! result ) {
2023-09-15 14:48:06 +00:00
std : : get < void * > ( handle ) = INVALID_HANDLE_VALUE ;
2023-11-30 08:32:25 +00:00
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 ( ) ) ;
2023-08-22 10:28:03 +00:00
return ;
}
if ( countDirectorySize ) {
2023-10-03 09:39:59 +00:00
const auto cacheFiles = getFiles ( config . cacheDir ) ;
2023-08-22 10:28:03 +00:00
2023-10-03 09:39:59 +00:00
for ( const auto & file : cacheFiles ) {
2023-08-22 10:28:03 +00:00
directorySize + = static_cast < size_t > ( file . fileSize ) ;
}
} else {
2023-10-02 09:35:55 +00:00
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 ;
2023-11-30 08:32:25 +00:00
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 ( ) ) ;
2023-10-02 09:35:55 +00:00
return ;
}
DWORD numOfBytesRead = 0 ;
result = NEO : : SysCalls : : readFile ( std : : get < void * > ( handle ) ,
& directorySize ,
sizeof ( directorySize ) ,
& numOfBytesRead ,
NULL ) ;
2023-08-22 10:28:03 +00:00
if ( ! result ) {
directorySize = 0 ;
2023-09-15 14:48:06 +00:00
unlockFileAndClose ( std : : get < void * > ( handle ) ) ;
std : : get < void * > ( handle ) = INVALID_HANDLE_VALUE ;
2023-11-30 08:32:25 +00:00
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-08-22 10:28:03 +00:00
}
}
}
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 ) {
2023-11-30 08:32:25 +00:00
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 ( ) ) ;
2023-08-22 10:28:03 +00:00
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 ) {
2023-11-30 08:32:25 +00:00
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 ( ) ) ;
2023-08-22 10:28:03 +00:00
return false ;
}
DWORD dwBytesWritten = 0 ;
auto result2 = NEO : : SysCalls : : writeFile ( hTempFile ,
pBinary ,
( DWORD ) binarySize ,
& dwBytesWritten ,
NULL ) ;
if ( ! result2 ) {
2023-11-30 08:32:25 +00:00
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 ( ) ) ;
2023-08-22 10:28:03 +00:00
} else if ( dwBytesWritten ! = ( DWORD ) binarySize ) {
2023-11-30 08:32:25 +00:00
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 ) ;
2023-08-22 10:28:03 +00:00
}
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
}
2025-08-14 08:53:15 +00:00
class HandleGuard : NonCopyableAndNonMovableClass {
2023-08-22 10:28:03 +00:00
public :
HandleGuard ( ) = delete ;
2023-09-15 14:48:06 +00:00
explicit HandleGuard ( void * & h ) : handle ( h ) { }
2023-08-22 10:28:03 +00:00
~ HandleGuard ( ) {
if ( handle ) {
unlockFileAndClose ( handle ) ;
}
}
private :
2023-09-15 14:48:06 +00:00
void * handle = nullptr ;
2023-08-22 10:28:03 +00:00
} ;
2025-08-14 08:53:15 +00:00
static_assert ( NonCopyableAndNonMovable < HandleGuard > ) ;
2023-10-03 09:39:59 +00:00
void writeDirSizeToConfigFile ( void * hConfigFile , size_t directorySize ) {
DWORD sizeWritten = 0 ;
OVERLAPPED overlapped = { 0 } ;
auto result = NEO : : SysCalls : : writeFile ( hConfigFile ,
& directorySize ,
( DWORD ) sizeof ( directorySize ) ,
& sizeWritten ,
& overlapped ) ;
if ( ! result ) {
2023-11-30 08:32:25 +00:00
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 ( ) ) ;
2023-10-03 09:39:59 +00:00
} else if ( sizeWritten ! = ( DWORD ) sizeof ( directorySize ) ) {
2023-11-30 08:32:25 +00:00
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 ) ) ;
2023-10-03 09:39:59 +00:00
}
}
2023-03-08 23:15:36 +01:00
bool CompilerCache : : cacheBinary ( const std : : string & kernelFileHash , const char * pBinary , size_t binarySize ) {
2023-10-03 09:39:59 +00:00
if ( pBinary = = nullptr | | binarySize = = 0 | | binarySize > config . cacheSize ) {
2023-03-08 23:15:36 +01:00
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 ) ;
2023-09-15 14:48:06 +00:00
UnifiedHandle hConfigFile { INVALID_HANDLE_VALUE } ;
2023-08-22 10:28:03 +00:00
size_t directorySize = 0u ;
lockConfigFileAndReadSize ( configFilePath , hConfigFile , directorySize ) ;
2023-09-15 14:48:06 +00:00
if ( std : : get < void * > ( hConfigFile ) = = INVALID_HANDLE_VALUE ) {
2023-08-22 10:28:03 +00:00
return false ;
}
2023-09-15 14:48:06 +00:00
HandleGuard configGuard ( std : : get < void * > ( hConfigFile ) ) ;
2023-08-22 10:28:03 +00:00
DWORD cacheFileAttr = 0 ;
cacheFileAttr = NEO : : SysCalls : : getFileAttributesA ( cacheFilePath . c_str ( ) ) ;
if ( ( cacheFileAttr ! = INVALID_FILE_ATTRIBUTES ) & &
( SysCalls : : getLastError ( ) ! = ERROR_FILE_NOT_FOUND ) ) {
return true ;
}
2023-10-03 09:39:59 +00:00
const size_t maxSize = config . cacheSize ;
2023-08-22 10:28:03 +00:00
if ( maxSize < ( directorySize + binarySize ) ) {
2023-10-03 09:39:59 +00:00
uint64_t bytesEvicted { 0u } ;
const auto evictSuccess = evictCache ( bytesEvicted ) ;
const auto availableSpace = maxSize - directorySize + bytesEvicted ;
directorySize = std : : max ( static_cast < size_t > ( 0 ) , directorySize - static_cast < size_t > ( bytesEvicted ) ) ;
if ( ! evictSuccess | | binarySize > availableSpace ) {
if ( bytesEvicted > 0 ) {
writeDirSizeToConfigFile ( std : : get < void * > ( hConfigFile ) , directorySize ) ;
}
2023-08-22 10:28:03 +00:00
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 ;
2023-10-03 09:39:59 +00:00
writeDirSizeToConfigFile ( std : : get < void * > ( hConfigFile ) , directorySize ) ;
2023-08-22 10:28:03 +00:00
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 ) ;
}
2023-09-15 14:48:06 +00:00
} // namespace NEO