2023-11-29 18:47:11 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "UbaSessionClient.h"
2024-03-05 12:41:52 -05:00
# include "UbaApplicationRules.h"
2023-11-29 18:47:11 -05:00
# include "UbaNetworkClient.h"
# include "UbaNetworkMessage.h"
# include "UbaProcess.h"
2024-01-11 15:42:50 -05:00
# include "UbaProcessStartInfoHolder.h"
2023-11-29 18:47:11 -05:00
# include "UbaProtocol.h"
# include "UbaStorage.h"
2024-05-08 12:53:11 -04:00
# if PLATFORM_WINDOWS
# include <winerror.h>
# endif
2023-11-29 18:47:11 -05:00
namespace uba
{
2024-06-15 01:56:18 -04:00
struct SessionClient : : ModuleInfo
{
ModuleInfo ( const tchar * n , const CasKey & c , u32 a ) : name ( n ) , casKey ( c ) , attributes ( a ) , done ( true ) { }
TString name ;
CasKey casKey ;
u32 attributes ;
Event done ;
} ;
2023-11-29 18:47:11 -05:00
SessionClient : : SessionClient ( const SessionClientCreateInfo & info )
2024-02-26 01:50:10 -05:00
: Session ( info , TC ( " UbaSessionClient " ) , true , & info . client )
2023-11-29 18:47:11 -05:00
, m_client ( info . client )
, m_name ( info . name . data )
2024-02-21 16:49:26 -05:00
, m_terminationTime ( ~ 0ull )
2023-11-29 18:47:11 -05:00
, m_waitToSendEvent ( false )
, m_loop ( true )
2024-08-21 19:56:16 -04:00
, m_allowSpawn ( true )
2023-11-29 18:47:11 -05:00
{
m_maxProcessCount = info . maxProcessCount ;
m_dedicated = info . dedicated ;
m_useStorage = info . useStorage ;
m_defaultPriorityClass = info . defaultPriorityClass ;
m_outputStatsThresholdMs = info . outputStatsThresholdMs ;
m_maxIdleSeconds = info . maxIdleSeconds ;
m_disableCustomAllocator = info . disableCustomAllocator ;
m_useBinariesAsVersion = info . useBinariesAsVersion ;
m_memWaitLoadPercent = info . memWaitLoadPercent ;
m_memKillLoadPercent = info . memKillLoadPercent ;
2024-02-21 01:43:26 -05:00
m_processFinished = info . processFinished ;
2023-11-29 18:47:11 -05:00
2024-04-18 01:10:16 -04:00
m_processIdCounter = ~ 0u / 2 ; // We set this value to a very high value.. because it will be used by child processes and we don't want id from server and child process id to collide
2023-11-29 18:47:11 -05:00
if ( m_name . IsEmpty ( ) )
{
tchar buf [ 256 ] ;
if ( GetComputerNameW ( buf , sizeof_array ( buf ) ) )
m_name . Appendf ( TC ( " %s " ) , buf ) ;
}
m_processWorkingDir . Append ( m_rootDir ) . Append ( TC ( " empty " ) ) ; // + TC("empty\\");
m_storage . CreateDirectory ( m_processWorkingDir . data ) ;
m_processWorkingDir . EnsureEndsWithSlash ( ) ;
if ( info . killRandom )
{
Guid g ;
CreateGuid ( g ) ;
m_killRandomIndex = 10 + g . data1 % 30 ;
}
2024-02-21 01:43:26 -05:00
m_nameToHashTableMem . Init ( NameToHashMemSize ) ;
2024-03-08 18:31:48 -05:00
Create ( info ) ;
2024-03-03 23:33:30 -05:00
}
2023-11-29 18:47:11 -05:00
2024-03-03 23:33:30 -05:00
SessionClient : : ~ SessionClient ( )
{
Stop ( ) ;
}
bool SessionClient : : Start ( )
{
2023-11-29 18:47:11 -05:00
m_client . RegisterOnDisconnected ( [ this ] ( )
{
m_loop = false ;
} ) ;
2024-02-21 01:43:26 -05:00
m_client . RegisterOnConnected ( [ this ] ( )
{
Connect ( ) ;
} ) ;
2024-03-03 23:33:30 -05:00
return true ;
2024-02-09 16:12:12 -05:00
}
void SessionClient : : Stop ( )
2023-11-29 18:47:11 -05:00
{
m_loop = false ;
2024-06-15 00:37:17 -04:00
CancelAllProcessesAndWait ( ) ;
2023-11-29 18:47:11 -05:00
m_waitToSendEvent . Set ( ) ;
m_loopThread . Wait ( ) ;
}
bool SessionClient : : Wait ( u32 milliseconds , Event * wakeupEvent )
{
return m_loopThread . Wait ( milliseconds , wakeupEvent ) ;
}
void SessionClient : : SetIsTerminating ( const tchar * reason , u64 delayMs )
{
m_terminationTime = GetTime ( ) + MsToTime ( delayMs ) ;
m_terminationReason = reason ;
2024-03-05 12:41:52 -05:00
StackBinaryWriter < 1024 > writer ;
NetworkMessage msg ( m_client , ServiceId , SessionMessageType_Notification , writer ) ;
writer . WriteU32 ( m_sessionId ) ;
writer . WriteString ( reason ) ;
msg . Send ( ) ;
2023-11-29 18:47:11 -05:00
}
2024-03-03 21:18:10 -05:00
void SessionClient : : SetMaxProcessCount ( u32 count )
{
m_maxProcessCount = count ;
}
2024-08-21 19:56:16 -04:00
void SessionClient : : SetAllowSpawn ( bool allow )
{
m_allowSpawn = allow ;
}
2023-11-29 18:47:11 -05:00
u64 SessionClient : : GetBestPing ( )
{
return m_bestPing ;
}
2023-12-19 01:53:01 -05:00
bool SessionClient : : RetrieveCasFile ( CasKey & outNewKey , u64 & outSize , const CasKey & casKey , const tchar * hint , bool storeUncompressed , bool allowProxy )
2023-11-29 18:47:11 -05:00
{
2024-03-03 21:18:10 -05:00
//StringBuffer<> workName;
//u32 len = TStrlen(hint);
//workName.Append(TC("RC:")).Append(len > 30 ? hint + (len - 30) : hint);
//TrackWorkScope tws(m_client, workName.data);
2023-11-29 18:47:11 -05:00
TimerScope s ( m_stats . storageRetrieve ) ;
CasKey tempKey = casKey ;
# if UBA_USE_SPARSEFILE
2023-12-19 01:53:01 -05:00
storeUncompressed = false ;
2023-11-29 18:47:11 -05:00
# endif
2023-12-19 01:53:01 -05:00
if ( storeUncompressed )
2023-11-29 18:47:11 -05:00
tempKey = AsCompressed ( casKey , false ) ;
//else
2023-12-19 01:53:01 -05:00
// storeUncompressed = !IsCompressed(casKey);
2023-11-29 18:47:11 -05:00
Storage : : RetrieveResult result ;
bool res = m_storage . RetrieveCasFile ( result , tempKey , hint , nullptr , 1 , allowProxy ) ;
outNewKey = result . casKey ;
outSize = result . size ;
return res ;
}
bool SessionClient : : GetCasKeyForFile ( CasKey & out , u32 processId , const StringBufferBase & fileName , const StringKey & fileNameKey )
{
TimerScope waitTimer ( Stats ( ) . waitGetFileMsg ) ;
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( m_nameToHashLookupLock , lock ) ;
2023-11-29 18:47:11 -05:00
auto insres = m_nameToHashLookup . try_emplace ( fileNameKey ) ;
HashRec & rec = insres . first - > second ;
lock . Leave ( ) ;
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( rec . lock , lock2 ) ;
2023-11-29 18:47:11 -05:00
if ( rec . key = = CasKeyZero ) //!rec.serverTime)
{
waitTimer . Cancel ( ) ;
// These will never succeed
if ( fileName . StartsWith ( m_sessionBinDir . data ) | | fileName . StartsWith ( TC ( " c: \\ noenvironment " ) ) | | fileName . StartsWith ( m_processWorkingDir . data ) )
{
out = CasKeyZero ;
return true ;
}
StackBinaryWriter < 1024 > writer ;
NetworkMessage msg ( m_client , ServiceId , SessionMessageType_GetFileFromServer , writer ) ;
writer . WriteU32 ( processId ) ;
writer . WriteString ( fileName ) ;
writer . WriteStringKey ( fileNameKey ) ;
StackBinaryReader < 128 > reader ;
if ( ! msg . Send ( reader , Stats ( ) . getFileMsg ) )
return false ;
rec . key = reader . ReadCasKey ( ) ;
2024-05-08 12:53:11 -04:00
if ( rec . key ! = CasKeyZero )
rec . serverTime = reader . ReadU64 ( ) ;
2023-11-29 18:47:11 -05:00
}
out = rec . key ;
return true ;
}
2024-08-29 19:28:38 -04:00
bool SessionClient : : EnsureBinaryFile ( StringBufferBase & out , StringBufferBase & outVirtual , u32 processId , const StringBufferBase & fileName , const StringKey & fileNameKey , const tchar * applicationDir , const u8 * loaderPaths , u32 loaderPathsSize )
2023-11-29 18:47:11 -05:00
{
CasKey casKey ;
u32 fileAttributes = DefaultAttributes ( ) ; // TODO: This is wrong.. need to retrieve from server if this is executable or not
2024-01-03 16:23:58 -05:00
2024-06-15 00:37:17 -04:00
bool isAbsolute = IsAbsolutePath ( fileName . data ) ;
2024-01-03 16:23:58 -05:00
if ( isAbsolute )
2023-11-29 18:47:11 -05:00
{
2024-01-03 16:23:58 -05:00
UBA_ASSERT ( fileNameKey ! = StringKeyZero ) ;
2023-11-29 18:47:11 -05:00
if ( ! GetCasKeyForFile ( casKey , processId , fileName , fileNameKey ) )
return false ;
2023-12-19 19:53:32 -05:00
// This needs to be absolute virtual path (the path on the host).. don't remember why this was written like this. Changed to just use fileName (needed for shadercompilerworker)
//const tchar* lastSlash = TStrrchr(fileName.data, PathSeparator);
//outVirtual.Append(lastSlash + 1);
outVirtual . Append ( fileName ) ;
2023-11-29 18:47:11 -05:00
}
else
{
2024-01-03 16:23:58 -05:00
UBA_ASSERT ( fileNameKey = = StringKeyZero ) ;
2023-11-29 18:47:11 -05:00
StackBinaryWriter < 1024 > writer ;
NetworkMessage msg ( m_client , ServiceId , SessionMessageType_EnsureBinaryFile , writer ) ;
writer . WriteU32 ( processId ) ;
writer . WriteString ( fileName ) ;
writer . WriteStringKey ( fileNameKey ) ;
writer . WriteString ( applicationDir ) ;
2024-08-29 19:28:38 -04:00
if ( loaderPathsSize )
writer . WriteBytes ( loaderPaths , loaderPathsSize ) ;
2023-11-29 18:47:11 -05:00
StackBinaryReader < 1024 > reader ;
if ( ! msg . Send ( reader , Stats ( ) . getBinaryMsg ) )
return false ;
casKey = reader . ReadCasKey ( ) ;
reader . ReadString ( outVirtual ) ;
}
if ( casKey = = CasKeyZero )
{
out . Append ( fileName ) ;
return true ;
}
2023-12-19 01:53:01 -05:00
bool storeUncompressed = true ;
2023-11-29 18:47:11 -05:00
CasKey newKey ;
u64 fileSize ;
2023-12-19 01:53:01 -05:00
if ( ! RetrieveCasFile ( newKey , fileSize , casKey , outVirtual . data , storeUncompressed ) )
2023-11-29 18:47:11 -05:00
UBA_ASSERTF ( false , TC ( " Casfile not found for %s using %s " ) , outVirtual . data , CasKeyString ( casKey ) . str ) ;
StringBuffer < > destFile ;
2024-01-03 16:23:58 -05:00
if ( isAbsolute | | fileName . Contains ( TC ( " .. " ) ) ) // This is not beautiful, but we need to keep some dlls in the sub folder (for cl.exe etc)
2023-11-29 18:47:11 -05:00
destFile . AppendFileName ( fileName . data ) ;
else
destFile . Append ( fileName ) ;
StringBuffer < > applicationDirLower ;
applicationDirLower . Append ( applicationDir ) . MakeLower ( ) ;
KeyToString keyStr ( ToStringKey ( applicationDirLower ) ) ;
return WriteBinFile ( out , destFile . data , newKey , keyStr , fileAttributes ) ;
}
2024-06-15 01:56:18 -04:00
bool SessionClient : : PrepareProcess ( ProcessStartInfoHolder & startInfo , bool isChild , StringBufferBase & outRealApplication , const tchar * & outRealWorkingDir )
2023-11-29 18:47:11 -05:00
{
outRealWorkingDir = m_processWorkingDir . data ;
2024-06-15 01:56:18 -04:00
if ( StartsWith ( startInfo . application , TC ( " ubacopy " ) ) )
return true ;
outRealApplication . Clear ( ) ;
2023-11-29 18:47:11 -05:00
2024-06-15 01:56:18 -04:00
const tchar * application = startInfo . application ;
UBA_ASSERT ( application & & * application ) ;
bool isAbsolute = IsAbsolutePath ( startInfo . application ) ;
SCOPED_WRITE_LOCK ( m_handledApplicationEnvironmentsLock , environmentslock ) ;
auto insres = m_handledApplicationEnvironments . try_emplace ( application ) ;
environmentslock . Leave ( ) ;
ApplicationEnvironment & appEnv = insres . first - > second ;
SCOPED_WRITE_LOCK ( appEnv . lock , lock ) ;
if ( ! appEnv . realApplication . empty ( ) )
{
outRealApplication . Append ( appEnv . realApplication ) ;
if ( ! isAbsolute )
{
startInfo . applicationStr = appEnv . virtualApplication ;
startInfo . application = startInfo . applicationStr . c_str ( ) ;
}
return true ;
}
List < ModuleInfo > modules ;
if ( ! ReadModules ( modules , 0 , application ) )
return false ;
StringBuffer < MaxPath > applicationDir ;
applicationDir . AppendDir ( application ) ;
KeyToString keyStr ( ToStringKeyLower ( applicationDir ) ) ;
Atomic < bool > success = true ;
Atomic < u32 > handledCount ;
for ( auto & m : modules )
{
m_client . AddWork ( [ & handledCount , & m , this , & success , & keyStr ] ( )
{
+ + handledCount ;
auto g = MakeGuard ( [ & ] ( ) { m . done . Set ( ) ; } ) ;
CasKey newCasKey ;
bool storeUncompressed = true ;
u64 fileSize ;
const tchar * moduleName = m . name . c_str ( ) ;
if ( ! RetrieveCasFile ( newCasKey , fileSize , m . casKey , moduleName , storeUncompressed ) )
{
m_logger . Error ( TC ( " Casfile not found for %s (%s) " ) , moduleName , CasKeyString ( m . casKey ) . str ) ;
success = false ;
return ;
}
if ( const tchar * lastSeparator = TStrrchr ( moduleName , PathSeparator ) )
moduleName = lastSeparator + 1 ;
StringBuffer < MaxPath > temp ;
if ( ! WriteBinFile ( temp , moduleName , newCasKey , keyStr , m . attributes ) )
success = false ;
} , 1 , TC ( " EnsureApp " ) ) ;
}
while ( handledCount < modules . size ( ) )
m_client . DoWork ( ) ;
// Wait for all to be done
for ( auto & m : modules )
if ( ! m . done . IsSet ( 10 * 60 * 1000 ) ) // 10 minutes is a very long time
return m_logger . Error ( TC ( " Timed out while waiting for application cas files to be downloaded " ) ) ;
if ( ! success )
return false ;
outRealApplication . Append ( m_sessionBinDir ) . Append ( keyStr ) . Append ( PathSeparator ) . AppendFileName ( application ) ;
appEnv . realApplication = outRealApplication . data ;
if ( ! isAbsolute )
{
appEnv . virtualApplication = modules . front ( ) . name ;
startInfo . applicationStr = appEnv . virtualApplication ;
startInfo . application = startInfo . applicationStr . c_str ( ) ;
}
return true ;
}
2024-03-12 18:16:36 -04:00
bool SessionClient : : ReadModules ( List < ModuleInfo > & outModules , u32 processId , const tchar * application )
{
StackBinaryReader < SendMaxSize > reader ;
{
StackBinaryWriter < 1024 > writer ;
NetworkMessage msg ( m_client , ServiceId , SessionMessageType_GetApplication , writer ) ;
writer . WriteU32 ( processId ) ;
writer . WriteString ( application ) ;
if ( ! msg . Send ( reader , m_stats . getApplicationMsg ) )
return false ;
}
u32 serverSystemPathLen = reader . ReadU32 ( ) ;
u32 moduleCount = reader . ReadU32 ( ) ;
if ( moduleCount = = 0 )
return m_logger . Error ( TC ( " Application %s not found " ) , application ) ;
while ( moduleCount - - )
{
StringBuffer < > moduleFile ;
reader . ReadString ( moduleFile ) ;
u32 fileAttributes = reader . ReadU32 ( ) ;
bool isSystem = reader . ReadBool ( ) ;
CasKey casKey = reader . ReadCasKey ( ) ;
if ( casKey = = CasKeyZero )
return m_logger . Error ( TC ( " Bad CasKey for %s (%s) " ) , moduleFile . data , CasKeyString ( casKey ) . str ) ;
if ( isSystem )
{
StringBuffer < > localSystemModule ;
localSystemModule . Append ( m_systemPath ) . Append ( moduleFile . data + serverSystemPathLen ) ;
2024-06-15 01:56:18 -04:00
if ( FileExists ( m_logger , localSystemModule . data ) & & ! localSystemModule . EndsWith ( TC ( " .exe " ) ) )
2024-03-12 18:16:36 -04:00
continue ;
moduleFile . Clear ( ) . Append ( localSystemModule ) ;
}
outModules . emplace_back ( moduleFile . data , casKey , fileAttributes ) ;
}
return true ;
}
2023-11-29 18:47:11 -05:00
void * SessionClient : : GetProcessEnvironmentVariables ( )
{
UBA_ASSERT ( ! m_environmentVariables . empty ( ) ) ;
return m_environmentVariables . data ( ) ;
}
bool SessionClient : : WriteBinFile ( StringBufferBase & out , const tchar * binaryName , const CasKey & casKey , const KeyToString & applicationDir , u32 fileAttributes )
{
UBA_ASSERT ( fileAttributes ) ;
out . Append ( m_sessionBinDir ) ;
out . Append ( applicationDir ) . Append ( PathSeparator ) ;
StringBuffer < > lower ;
lower . Append ( applicationDir ) . Append ( PathSeparator ) . Append ( binaryName ) ;
lower . MakeLower ( ) ;
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( m_binFileLock , lock ) ;
2023-11-29 18:47:11 -05:00
auto insres = m_writtenBinFiles . try_emplace ( lower . data , casKey ) ;
if ( ! insres . second )
{
out . Append ( binaryName ) ;
if ( insres . first - > second ! = casKey )
return m_logger . Error ( TC ( " Writing same binary file %s multiple times but with different data! (Current: %s Previous: %s) " ) , out . data , CasKeyString ( casKey ) . str , CasKeyString ( insres . first - > second ) . str ) ;
return true ;
}
m_storage . CreateDirectory ( out . data ) ;
out . Append ( binaryName ) ;
//if (GetFileAttributesW(out.data) != INVALID_FILE_ATTRIBUTES)
// return true;
if ( TStrchr ( binaryName , PathSeparator ) )
{
StringBuffer < > binaryDir ;
binaryDir . AppendDir ( out ) ;
if ( ! m_storage . CreateDirectory ( binaryDir . data ) )
return false ;
}
2024-01-09 14:26:32 -05:00
2023-11-29 18:47:11 -05:00
return m_storage . CopyOrLink ( casKey , out . data , fileAttributes ) ;
}
2023-12-12 21:28:16 -05:00
bool SessionClient : : CreateFile ( CreateFileResponse & out , const CreateFileMessage & msg )
2023-11-29 18:47:11 -05:00
{
const StringBufferBase & fileName = msg . fileName ;
StringKey fileNameKey = msg . fileNameKey ;
if ( ( msg . access & FileAccess_Write ) ! = 0 )
2023-12-12 21:28:16 -05:00
return Session : : CreateFile ( out , msg ) ;
2023-11-29 18:47:11 -05:00
CasKey casKey ;
if ( ! GetCasKeyForFile ( casKey , msg . process . m_id , fileName , fileNameKey ) )
return false ;
// Not finding a file is a valid path. Some applications try with a path and if fails try another path
if ( casKey = = CasKeyZero )
{
//m_logger.Warning(TC("No casfile found for %s"), fileName.data);
out . directoryTableSize = GetDirectoryTableSize ( ) ;
out . mappedFileTableSize = GetFileMappingSize ( ) ;
out . fileName . Append ( fileName ) ;
return true ;
}
2024-05-02 00:46:58 -04:00
// Code for doing retry if failing to decompress casfile. We've seen cases of corrupt cas files on clients
bool shouldRetry = true ;
FileMappingEntry * retryEntry = nullptr ;
auto retryEntryGuard = MakeGuard ( [ & ] ( ) { if ( retryEntry ) retryEntry - > lock . LeaveWrite ( ) ; } ) ;
2023-12-19 01:53:01 -05:00
2024-05-02 00:46:58 -04:00
while ( true )
2023-12-19 01:53:01 -05:00
{
2024-05-02 00:46:58 -04:00
StringBuffer < > newName ;
bool isDir = casKey = = CasKeyIsDirectory ;
u64 fileSize = InvalidValue ;
CasKey newCasKey ;
2023-11-29 18:47:11 -05:00
2024-05-02 00:46:58 -04:00
u32 memoryMapAlignment = 0 ;
if ( m_allowMemoryMaps )
2023-11-29 18:47:11 -05:00
{
2024-05-27 23:22:26 -04:00
memoryMapAlignment = GetMemoryMapAlignment ( fileName ) ;
2024-05-02 00:46:58 -04:00
if ( ! memoryMapAlignment & & ! m_useStorage )
memoryMapAlignment = 64 * 1024 ;
2023-11-29 18:47:11 -05:00
}
2024-05-02 00:46:58 -04:00
if ( isDir )
2023-11-29 18:47:11 -05:00
{
2024-05-02 00:46:58 -04:00
newName . Append ( TC ( " $d " ) ) ;
}
else if ( casKey ! = CasKeyZero )
{
if ( m_useStorage | | memoryMapAlignment = = 0 )
2023-11-29 18:47:11 -05:00
{
2024-05-02 00:46:58 -04:00
bool storeUncompressed = memoryMapAlignment = = 0 ;
2024-04-05 19:10:53 -04:00
bool allowProxy = msg . process . m_startInfo . rules - > AllowStorageProxy ( fileName ) ;
2024-05-02 00:46:58 -04:00
if ( ! RetrieveCasFile ( newCasKey , fileSize , casKey , fileName . data , storeUncompressed , allowProxy ) )
2023-11-29 18:47:11 -05:00
return m_logger . Error ( TC ( " Error retrieving cas entry %s (%s) " ) , CasKeyString ( casKey ) . str , fileName . data ) ;
2024-05-02 00:46:58 -04:00
# if !UBA_USE_SPARSEFILE
if ( ! m_storage . GetCasFileName ( newName , newCasKey ) )
return false ;
# else
if ( ! memoryMapAlignment )
memoryMapAlignment = 4096 ;
MemoryMap map ;
if ( ! CreateMemoryMapFromView ( map , fileNameKey , fileName . data , newCasKey , memoryMapAlignment ) )
return false ;
newName . Append ( map . name ) ;
fileSize = map . size ;
# endif
2023-11-29 18:47:11 -05:00
}
else
2024-05-02 00:46:58 -04:00
{
StorageStats & stats = m_storage . Stats ( ) ;
TimerScope ts ( stats . ensureCas ) ;
SCOPED_WRITE_LOCK ( m_fileMappingTableLookupLock , lookupLock ) ;
auto insres = m_fileMappingTableLookup . try_emplace ( fileNameKey ) ;
FileMappingEntry & entry = insres . first - > second ;
lookupLock . Leave ( ) ;
SCOPED_WRITE_LOCK ( entry . lock , entryCs ) ;
ts . Leave ( ) ;
if ( entry . handled )
{
if ( ! entry . success )
return false ;
}
else
{
TimerScope s ( m_stats . storageRetrieve ) ;
casKey = AsCompressed ( casKey , false ) ;
entry . handled = true ;
Storage : : RetrieveResult result ;
bool allowProxy = msg . process . m_startInfo . rules - > AllowStorageProxy ( fileName ) ;
if ( ! m_storage . RetrieveCasFile ( result , casKey , fileName . data , & m_fileMappingBuffer , memoryMapAlignment , allowProxy ) )
return m_logger . Error ( TC ( " Error retrieving cas entry %s (%s) " ) , CasKeyString ( casKey ) . str , fileName . data ) ;
entry . success = true ;
entry . size = result . size ;
entry . mapping = result . view . handle ;
entry . mappingOffset = result . view . offset ;
}
fileSize = entry . size ;
if ( entry . mapping . IsValid ( ) )
Storage : : GetMappingString ( newName , entry . mapping , entry . mappingOffset ) ;
else
newName . Append ( entry . isDir ? TC ( " $d " ) : TC ( " $f " ) ) ;
}
2023-11-29 18:47:11 -05:00
}
2024-05-02 00:46:58 -04:00
UBA_ASSERTF ( ! newName . IsEmpty ( ) , TC ( " No casfile available for %s using %s " ) , fileName . data , CasKeyString ( casKey ) . str ) ;
2023-11-29 18:47:11 -05:00
2024-05-02 00:46:58 -04:00
if ( newName [ 0 ] ! = ' ^ ' )
2023-11-29 18:47:11 -05:00
{
2024-05-02 00:46:58 -04:00
if ( ! isDir & & memoryMapAlignment )
{
if ( retryEntry )
retryEntryGuard . Execute ( ) ;
2023-11-29 18:47:11 -05:00
2024-05-02 00:46:58 -04:00
MemoryMap map ;
if ( ! CreateMemoryMapFromFile ( map , fileNameKey , newName . data , IsCompressed ( newCasKey ) , memoryMapAlignment ) )
{
if ( ! shouldRetry )
return false ;
shouldRetry = false ;
// We need to take a lock around the file map entry since there might be another thread also wanting to map this
{
SCOPED_WRITE_LOCK ( m_fileMappingTableLookupLock , lookupLock ) ;
retryEntry = & m_fileMappingTableLookup . try_emplace ( fileNameKey ) . first - > second ;
lookupLock . Leave ( ) ;
retryEntry - > lock . EnterWrite ( ) ;
retryEntry - > handled = false ;
}
if ( ! m_storage . ReportBadCasFile ( newCasKey ) )
return false ;
continue ;
}
fileSize = map . size ;
newName . Clear ( ) . Append ( map . name ) ;
}
else if ( ! IsRarelyRead ( msg . process , fileName ) )
{
AddFileMapping ( fileNameKey , fileName . data , newName . data , fileSize ) ;
}
2023-11-29 18:47:11 -05:00
}
2024-05-02 00:46:58 -04:00
out . directoryTableSize = GetDirectoryTableSize ( ) ;
out . mappedFileTableSize = GetFileMappingSize ( ) ;
out . fileName . Append ( newName ) ;
out . size = fileSize ;
return true ;
2023-11-29 18:47:11 -05:00
}
}
bool SessionClient : : SendFiles ( ProcessImpl & process , Timer & sendFiles )
{
StorageStatsScope storageStatsScope ( process . m_storageStats ) ;
for ( auto & pair : process . m_writtenFiles )
{
TimerScope timer ( sendFiles ) ;
# ifdef _DEBUG
if ( ! pair . second . mappingHandle . IsValid ( ) )
m_logger . Warning ( TC ( " %s is not using file mapping " ) , pair . first . c_str ( ) ) ;
# endif
2024-05-27 23:22:26 -04:00
bool keepMappingInMemory = IsWindows & & ! IsRarelyReadAfterWritten ( process , pair . first ) ;
2024-03-03 21:18:10 -05:00
if ( ! SendFile ( pair . second , pair . first . c_str ( ) , process . GetId ( ) , keepMappingInMemory ) )
2023-11-29 18:47:11 -05:00
return false ;
}
return true ;
}
2024-03-03 21:18:10 -05:00
bool SessionClient : : SendFile ( WrittenFile & source , const tchar * destination , u32 processId , bool keepMappingInMemory )
2023-11-29 18:47:11 -05:00
{
CasKey casKey ;
{
TimerScope ts ( m_stats . storageSend ) ;
bool deferCreation = false ;
if ( ! m_storage . StoreCasFile ( casKey , source . key , source . name . c_str ( ) , source . mappingHandle , 0 , source . mappingWritten , destination , deferCreation , keepMappingInMemory ) )
return false ;
}
UBA_ASSERTF ( casKey ! = CasKeyZero , TC ( " Failed to store cas file for %s (destination %s) " ) , source . name . c_str ( ) , destination ) ;
CloseFileMapping ( source . mappingHandle ) ;
source . mappingHandle = { } ;
StackBinaryReader < 128 > reader ;
{
StackBinaryWriter < 1024 > writer ;
NetworkMessage msg ( m_client , ServiceId , SessionMessageType_SendFileToServer , writer ) ;
2024-03-03 21:18:10 -05:00
writer . WriteU32 ( processId ) ;
2023-11-29 18:47:11 -05:00
writer . WriteString ( destination ) ;
writer . WriteStringKey ( source . key ) ;
writer . WriteU32 ( source . attributes ) ;
writer . WriteCasKey ( casKey ) ;
if ( ! msg . Send ( reader , Stats ( ) . sendFileMsg ) )
return m_logger . Error ( TC ( " Failed to send file %s to server " ) , source . name . c_str ( ) ) ;
}
if ( ! reader . ReadBool ( ) )
2024-05-14 20:26:45 -04:00
return m_logger . Error ( TC ( " Server failed to receive file %s (%s) " ) , source . name . c_str ( ) , destination ) ;
2023-11-29 18:47:11 -05:00
return true ;
}
void RemoveWrittenFile ( ProcessImpl & process , const TString & name ) ;
bool SessionClient : : DeleteFile ( DeleteFileResponse & out , const DeleteFileMessage & msg )
{
// TODO: Deleting output files should also delete them on disk (for now they will leak until process shutdown)
RemoveWrittenFile ( msg . process , msg . fileName . data ) ;
bool sendDelete = true ;
if ( msg . closeId ! = 0 )
{
UBA_ASSERTF ( false , TC ( " This has not been tested properly " ) ) ;
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( m_activeFilesLock , lock ) ;
2023-11-29 18:47:11 -05:00
sendDelete = m_activeFiles . erase ( msg . closeId ) = = 0 ;
}
{
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( m_outputFilesLock , lock ) ;
2023-11-29 18:47:11 -05:00
sendDelete = m_outputFiles . erase ( msg . fileName . data ) = = 0 & & sendDelete ;
}
bool isTemp = StartsWith ( msg . fileName . data , m_tempPath . data ) ;
if ( isTemp )
sendDelete = false ;
if ( ! sendDelete )
{
if ( ! m_allowMemoryMaps & & isTemp )
{
out . result = uba : : DeleteFileW ( msg . fileName . data ) ;
out . errorCode = GetLastError ( ) ;
return true ;
}
out . result = true ;
out . errorCode = ERROR_SUCCESS ;
return true ;
}
// TODO: Cache this if it becomes noisy
StackBinaryWriter < 1024 > writer ;
NetworkMessage networkMsg ( m_client , ServiceId , SessionMessageType_DeleteFile , writer ) ;
writer . WriteStringKey ( msg . fileNameKey ) ;
writer . WriteString ( msg . fileName ) ;
2023-12-15 15:38:35 -05:00
StackBinaryReader < SendMaxSize > reader ;
2023-11-29 18:47:11 -05:00
if ( ! networkMsg . Send ( reader , Stats ( ) . deleteFileMsg ) )
return false ;
out . result = reader . ReadBool ( ) ;
out . errorCode = reader . ReadU32 ( ) ;
if ( out . result )
2024-08-28 00:48:26 -04:00
if ( ! SendUpdateDirectoryTable ( reader . Reset ( ) ) )
2023-11-29 18:47:11 -05:00
return false ;
out . directoryTableSize = GetDirectoryTableSize ( ) ;
return true ;
}
bool SessionClient : : CopyFile ( CopyFileResponse & out , const CopyFileMessage & msg )
{
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( m_outputFilesLock , lock ) ;
2023-11-29 18:47:11 -05:00
auto findIt = m_outputFiles . find ( msg . fromName . data ) ;
if ( findIt = = m_outputFiles . end ( ) )
{
lock . Leave ( ) ;
StackBinaryWriter < 1024 > writer ;
NetworkMessage networkMsg ( m_client , ServiceId , SessionMessageType_CopyFile , writer ) ;
writer . WriteStringKey ( msg . fromKey ) ;
writer . WriteString ( msg . fromName ) ;
writer . WriteStringKey ( msg . toKey ) ;
writer . WriteString ( msg . toName ) ;
2023-12-15 15:38:35 -05:00
StackBinaryReader < SendMaxSize > reader ;
2023-11-29 18:47:11 -05:00
if ( ! networkMsg . Send ( reader , Stats ( ) . copyFileMsg ) )
return false ;
out . fromName . Append ( msg . fromName ) ;
out . toName . Append ( msg . toName ) ;
out . closeId = ~ 0u ;
out . errorCode = reader . ReadU32 ( ) ;
if ( ! out . errorCode )
2024-08-28 00:48:26 -04:00
if ( ! SendUpdateDirectoryTable ( reader . Reset ( ) ) )
2023-11-29 18:47:11 -05:00
return false ;
out . directoryTableSize = GetDirectoryTableSize ( ) ;
return true ;
}
lock . Leave ( ) ;
out . fromName . Append ( findIt - > second ) ;
CreateFileMessage writeMsg { msg . process } ;
writeMsg . fileName . Append ( msg . toName . data ) ;
writeMsg . fileNameKey = msg . toKey ;
writeMsg . access = FileAccess_Write ;
CreateFileResponse writeOut ;
2023-12-12 21:28:16 -05:00
if ( ! CreateFile ( writeOut , writeMsg ) )
2023-11-29 18:47:11 -05:00
return false ;
out . toName . Append ( writeOut . fileName ) ;
out . closeId = writeOut . closeId ;
return true ;
}
bool SessionClient : : MoveFile ( MoveFileResponse & out , const MoveFileMessage & msg )
{
const tchar * fromName = msg . fromName . data ;
2024-01-09 12:04:16 -05:00
const tchar * toName = msg . toName . data ;
2024-09-03 17:11:48 -04:00
auto & process = msg . process ;
2023-11-29 18:47:11 -05:00
{
2024-09-03 17:11:48 -04:00
SCOPED_WRITE_LOCK ( process . m_writtenFilesLock , lock ) ;
auto & writtenFiles = process . m_writtenFiles ;
2023-11-29 18:47:11 -05:00
auto findIt = writtenFiles . find ( fromName ) ;
if ( findIt ! = writtenFiles . end ( ) )
{
2024-01-09 12:04:16 -05:00
auto insres = writtenFiles . try_emplace ( toName ) ;
2024-09-03 17:11:48 -04:00
UBA_ASSERTF ( insres . second , TC ( " Moving written file %s to other written file %s. (%s) " ) , fromName , toName , process . m_startInfo . description ) ;
2023-11-29 18:47:11 -05:00
insres . first - > second = findIt - > second ;
2024-09-03 17:11:48 -04:00
insres . first - > second . key = msg . toKey ;
insres . first - > second . owner = & process ;
2023-11-29 18:47:11 -05:00
writtenFiles . erase ( findIt ) ;
}
2024-09-03 17:11:48 -04:00
else
{
// TODO: Need to tell server
}
2023-11-29 18:47:11 -05:00
}
bool sendMove = true ;
{
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( m_outputFilesLock , lock ) ;
2024-01-09 12:04:16 -05:00
auto findIt = m_outputFiles . find ( fromName ) ;
2023-11-29 18:47:11 -05:00
if ( findIt ! = m_outputFiles . end ( ) )
{
2024-01-09 12:04:16 -05:00
auto insres = m_outputFiles . try_emplace ( toName ) ;
2024-05-08 12:53:11 -04:00
UBA_ASSERTF ( insres . second , TC ( " Failed to add move destination file %s as output file because it is already added. (Moved from %s) " ) , toName , fromName ) ;
2023-11-29 18:47:11 -05:00
insres . first - > second = findIt - > second ;
m_outputFiles . erase ( findIt ) ;
sendMove = false ;
}
}
if ( ! sendMove )
{
out . result = true ;
out . errorCode = ERROR_SUCCESS ;
return true ;
}
2024-09-03 17:11:48 -04:00
// TODO: This should be done by server?
2024-01-09 12:04:16 -05:00
out . result = uba : : MoveFileExW ( fromName , toName , 0 ) ;
2023-11-29 18:47:11 -05:00
out . errorCode = GetLastError ( ) ;
return true ;
}
bool SessionClient : : Chmod ( ChmodResponse & out , const ChmodMessage & msg )
{
const tchar * fromName = msg . fileName . data ;
{
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( msg . process . m_writtenFilesLock , lock ) ;
2023-11-29 18:47:11 -05:00
auto & writtenFiles = msg . process . m_writtenFiles ;
auto findIt = writtenFiles . find ( fromName ) ;
if ( findIt ! = writtenFiles . end ( ) )
{
bool executable = false ;
# if !PLATFORM_WINDOWS
if ( msg . fileMode & S_IXUSR )
executable = true ;
# endif
findIt - > second . attributes = DefaultAttributes ( executable ) ;
out . errorCode = 0 ;
return true ;
}
}
UBA_ASSERTF ( false , TC ( " Code path not implemented.. should likely send message to server " ) ) ;
return true ;
}
bool SessionClient : : CreateDirectory ( CreateDirectoryResponse & out , const CreateDirectoryMessage & msg )
{
// TODO: Cache this if it becomes noisy
StackBinaryWriter < 1024 > writer ;
NetworkMessage networkMsg ( m_client , ServiceId , SessionMessageType_CreateDirectory , writer ) ;
writer . WriteString ( msg . name ) ;
2024-08-28 00:48:26 -04:00
StackBinaryReader < SendMaxSize > reader ;
2023-11-29 18:47:11 -05:00
if ( ! networkMsg . Send ( reader , Stats ( ) . createDirMsg ) )
return false ;
out . result = reader . ReadBool ( ) ;
out . errorCode = reader . ReadU32 ( ) ;
2024-08-28 00:48:26 -04:00
if ( out . result )
if ( ! SendUpdateDirectoryTable ( reader . Reset ( ) ) )
return false ;
out . directoryTableSize = GetDirectoryTableSize ( ) ;
2023-11-29 18:47:11 -05:00
return true ;
}
2024-06-15 00:37:17 -04:00
bool SessionClient : : RemoveDirectory ( RemoveDirectoryResponse & out , const RemoveDirectoryMessage & msg )
{
StackBinaryWriter < 1024 > writer ;
NetworkMessage networkMsg ( m_client , ServiceId , SessionMessageType_RemoveDirectory , writer ) ;
writer . WriteString ( msg . name ) ;
2024-08-28 00:48:26 -04:00
StackBinaryReader < SendMaxSize > reader ;
2024-06-15 00:37:17 -04:00
if ( ! networkMsg . Send ( reader , Stats ( ) . deleteFileMsg ) ) // Wrong message
return false ;
out . result = reader . ReadBool ( ) ;
out . errorCode = reader . ReadU32 ( ) ;
2024-08-28 00:48:26 -04:00
if ( out . result )
if ( ! SendUpdateDirectoryTable ( reader . Reset ( ) ) )
return false ;
out . directoryTableSize = GetDirectoryTableSize ( ) ;
2024-06-15 00:37:17 -04:00
return true ;
}
2023-12-12 21:28:16 -05:00
bool SessionClient : : GetFullFileName ( GetFullFileNameResponse & out , const GetFullFileNameMessage & msg )
2023-11-29 18:47:11 -05:00
{
2023-12-22 02:41:43 -05:00
// TODO: There is a potential risk here where two different applications asks for the full name of a file
// and they have different bin/working dir.. and there are two versions of this file.
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( m_nameToNameLookupLock , lock ) ;
2023-11-29 18:47:11 -05:00
auto insres = m_nameToNameLookup . try_emplace ( msg . fileName . data ) ;
NameRec & rec = insres . first - > second ;
lock . Leave ( ) ;
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( rec . lock , lock2 ) ;
2023-11-29 18:47:11 -05:00
if ( rec . handled )
{
out . fileName . Append ( rec . name . c_str ( ) ) ;
out . virtualFileName . Append ( rec . virtualName . c_str ( ) ) ;
return true ;
}
rec . handled = true ;
2024-06-15 01:56:18 -04:00
StringBuffer < > dir ;
dir . AppendDir ( msg . process . m_startInfo . application ) ;
2024-08-29 19:28:38 -04:00
if ( ! EnsureBinaryFile ( out . fileName , out . virtualFileName , msg . process . m_id , msg . fileName , msg . fileNameKey , dir . data , msg . loaderPaths , msg . loaderPathsSize ) )
2023-11-29 18:47:11 -05:00
return false ;
2024-01-03 16:23:58 -05:00
StringKey fileNameKey = msg . fileNameKey ;
if ( fileNameKey = = StringKeyZero )
fileNameKey = CaseInsensitiveFs ? ToStringKeyLower ( out . virtualFileName ) : ToStringKey ( out . virtualFileName ) ;
2023-11-29 18:47:11 -05:00
rec . name = out . fileName . data ;
rec . virtualName = out . virtualFileName . data ;
2024-01-03 16:23:58 -05:00
out . mappedFileTableSize = AddFileMapping ( fileNameKey , msg . fileName . data , out . fileName . data ) ;
2023-11-29 18:47:11 -05:00
return true ;
}
2024-06-15 00:37:17 -04:00
bool SessionClient : : GetLongPathName ( GetLongPathNameResponse & out , const GetLongPathNameMessage & msg )
{
StackBinaryWriter < 1024 > writer ;
NetworkMessage networkMsg ( m_client , ServiceId , SessionMessageType_GetLongPathName , writer ) ;
writer . WriteString ( msg . fileName ) ;
StackBinaryReader < 1024 > reader ;
if ( ! networkMsg . Send ( reader , Stats ( ) . getLongNameMsg ) )
return false ;
out . errorCode = reader . ReadU32 ( ) ;
reader . ReadString ( out . fileName ) ;
return true ;
}
2023-11-29 18:47:11 -05:00
bool SessionClient : : GetListDirectoryInfo ( ListDirectoryResponse & out , tchar * dirName , const StringKey & dirKey )
{
StackBinaryWriter < 1024 > writer ;
NetworkMessage msg ( m_client , ServiceId , SessionMessageType_ListDirectory , writer ) ;
writer . WriteU32 ( m_sessionId ) ;
writer . WriteString ( dirName ) ;
writer . WriteStringKey ( dirKey ) ;
StackBinaryReader < SendMaxSize > reader ;
if ( ! msg . Send ( reader , Stats ( ) . listDirMsg ) )
return false ;
u32 tableOffset = reader . ReadU32 ( ) ;
u32 oldTableSize = GetDirectoryTableSize ( ) ;
if ( ! UpdateDirectoryTableFromServer ( reader ) )
return false ;
u32 newTableSize = GetDirectoryTableSize ( ) ;
// Ask for a refresh of hashes straight away since they will likely be asked for by the process doing this query
if ( oldTableSize ! = newTableSize )
m_waitToSendEvent . Set ( ) ;
out . tableOffset = tableOffset ;
out . tableSize = newTableSize ;
return true ;
}
2024-03-08 18:31:48 -05:00
bool SessionClient : : WriteFilesToDisk ( ProcessImpl & process , WrittenFile * * files , u32 fileCount )
2023-11-29 18:47:11 -05:00
{
// Do nothing, we will send the data to the host when process is finished
return true ;
}
struct SessionClient : : ActiveUpdateDirectoryEntry
{
Event done ;
u32 readPos = 0 ;
ActiveUpdateDirectoryEntry * prev = nullptr ;
ActiveUpdateDirectoryEntry * next = nullptr ;
2024-08-09 15:08:14 -04:00
bool success = true ;
2023-11-29 18:47:11 -05:00
2024-08-09 15:08:14 -04:00
static bool Wait ( SessionClient & client , ActiveUpdateDirectoryEntry * & first , ScopedWriteLock & lock , u32 readPos , const tchar * hint )
2023-11-29 18:47:11 -05:00
{
ActiveUpdateDirectoryEntry item ;
item . next = first ;
if ( item . next )
item . next - > prev = & item ;
item . readPos = readPos ;
first = & item ;
item . done . Create ( true ) ;
2024-08-09 15:08:14 -04:00
2023-11-29 18:47:11 -05:00
lock . Leave ( ) ;
2024-08-28 00:48:26 -04:00
bool res = item . done . IsSet ( 5 * 60 * 1000 ) ;
2023-11-29 18:47:11 -05:00
lock . Enter ( ) ;
2024-08-09 15:08:14 -04:00
2023-11-29 18:47:11 -05:00
if ( item . prev )
item . prev - > next = item . next ;
else
first = item . next ;
if ( item . next )
item . next - > prev = item . prev ;
2024-08-09 15:08:14 -04:00
if ( res )
return item . success ;
u32 activeCount = 0 ;
for ( auto i = first ; i ; i = i - > next )
+ + activeCount ;
return client . m_logger . Error ( TC ( " Timed out after 5 minutes waiting for update directory message to reach read position %u (%u active in %s wait) " ) , readPos , activeCount , hint ) ;
2023-11-29 18:47:11 -05:00
}
static void UpdateReadPosMatching ( ActiveUpdateDirectoryEntry * & first , u32 readPos )
{
for ( auto i = first ; i ; i = i - > next )
{
if ( i - > readPos ! = readPos )
continue ;
i - > done . Set ( ) ;
break ;
}
}
2024-08-09 15:08:14 -04:00
static void UpdateReadPosLessOrEqual ( ActiveUpdateDirectoryEntry * & first , u32 readPos )
2023-11-29 18:47:11 -05:00
{
for ( auto i = first ; i ; i = i - > next )
if ( i - > readPos < = readPos )
i - > done . Set ( ) ;
}
2024-08-09 15:08:14 -04:00
static void UpdateError ( ActiveUpdateDirectoryEntry * & first )
{
for ( auto i = first ; i ; i = i - > next )
{
i - > success = false ;
i - > done . Set ( ) ;
}
}
2023-11-29 18:47:11 -05:00
} ;
2023-12-15 15:38:35 -05:00
bool SessionClient : : UpdateDirectoryTableFromServer ( StackBinaryReader < SendMaxSize > & reader )
2023-11-29 18:47:11 -05:00
{
auto & dirTable = m_directoryTable ;
2024-08-09 15:08:14 -04:00
auto updateMemorySizeAndSignal = [ & ]
{
SCOPED_WRITE_LOCK ( dirTable . m_memoryLock , lock ) ;
dirTable . m_memorySize = m_directoryTableMemPos ;
lock . Leave ( ) ;
ActiveUpdateDirectoryEntry : : UpdateReadPosLessOrEqual ( m_firstEmptyWait , m_directoryTableMemPos ) ;
return true ;
} ;
u32 lastWriteEnd = ~ 0u ;
2023-11-29 18:47:11 -05:00
while ( true )
{
u32 readPos = reader . ReadU32 ( ) ;
u8 * pos = dirTable . m_memory + readPos ;
u32 toRead = u32 ( reader . GetLeft ( ) ) ;
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( m_directoryTableLock , lock ) ;
2023-11-29 18:47:11 -05:00
2024-08-09 15:08:14 -04:00
if ( m_directoryTableError )
return false ;
2023-11-29 18:47:11 -05:00
if ( toRead = = 0 )
{
2024-08-09 15:08:14 -04:00
// We wrote to lastWriteEnd and now we got an empty message where readPos is the same..
// This means that it was a good cut-off and we can increase m_memorySize
// If m_directoryTableMemPos is different it means that we have another thread going on that will update things a little bit later.
if ( lastWriteEnd = = readPos & & lastWriteEnd = = m_directoryTableMemPos )
return updateMemorySizeAndSignal ( ) ;
2023-11-29 18:47:11 -05:00
// We might share this position with others
2024-08-09 15:08:14 -04:00
if ( dirTable . m_memorySize < readPos )
if ( ! ActiveUpdateDirectoryEntry : : Wait ( * this , m_firstEmptyWait , lock , readPos , TC ( " empty " ) ) )
2023-11-29 18:47:11 -05:00
return false ;
return true ;
}
reader . ReadBytes ( pos , toRead ) ;
2024-08-09 15:08:14 -04:00
// Wait until all data before readPos has been read
2023-11-29 18:47:11 -05:00
if ( readPos ! = m_directoryTableMemPos )
2024-08-09 15:08:14 -04:00
if ( ! ActiveUpdateDirectoryEntry : : Wait ( * this , m_firstReadWait , lock , readPos , TC ( " read " ) ) )
2023-11-29 18:47:11 -05:00
return false ;
m_directoryTableMemPos + = toRead ;
2024-08-09 15:08:14 -04:00
// Find potential waiter waiting for this exact size and wake it up
2023-11-29 18:47:11 -05:00
ActiveUpdateDirectoryEntry : : UpdateReadPosMatching ( m_firstReadWait , m_directoryTableMemPos ) ;
2024-08-09 15:08:14 -04:00
// If there is space left in the message it means that we caught up with the directory table server side..
// And we will stop asking for more data.
// Note, we can only set m_memorySize when getting messages that reads less than capacity since we don't know if we reached a good position in the directory table
if ( reader . GetPosition ( ) < m_client . GetMessageMaxSize ( ) - m_client . GetMessageReceiveHeaderSize ( ) )
return updateMemorySizeAndSignal ( ) ;
lastWriteEnd = m_directoryTableMemPos ;
StackBinaryWriter < 1024 > writer ;
NetworkMessage msg ( m_client , ServiceId , SessionMessageType_GetDirectoriesFromServer , writer ) ;
writer . WriteU32 ( m_sessionId ) ;
2024-08-28 00:48:26 -04:00
if ( msg . Send ( reader . Reset ( ) , Stats ( ) . getDirsMsg ) )
2024-08-09 15:08:14 -04:00
continue ;
// Let's signal waiters to exit faster since we will not get out of this situation (most likely a disconnect)
m_directoryTableError = true ;
ActiveUpdateDirectoryEntry : : UpdateError ( m_firstReadWait ) ;
ActiveUpdateDirectoryEntry : : UpdateError ( m_firstEmptyWait ) ;
return false ;
}
2023-11-29 18:47:11 -05:00
}
2023-12-15 15:38:35 -05:00
bool SessionClient : : UpdateNameToHashTableFromServer ( StackBinaryReader < SendMaxSize > & reader )
2023-11-29 18:47:11 -05:00
{
u32 serverTableSize = 0 ;
bool isFirst = true ;
u32 readStartPos = u32 ( m_nameToHashTableMem . writtenSize ) ;
u32 localTableSize = readStartPos ;
u64 serverTime = 0 ;
while ( true )
{
if ( isFirst )
{
serverTableSize = reader . ReadU32 ( ) ;
isFirst = false ;
}
else
{
StackBinaryWriter < 1024 > writer ;
NetworkMessage msg ( m_client , ServiceId , SessionMessageType_GetNameToHashFromServer , writer ) ;
writer . WriteU32 ( serverTableSize ) ;
writer . WriteU32 ( localTableSize ) ;
2024-08-28 00:48:26 -04:00
if ( ! msg . Send ( reader . Reset ( ) , Stats ( ) . getHashesMsg ) )
2023-11-29 18:47:11 -05:00
return false ;
}
serverTime = reader . ReadU64 ( ) ;
u8 * pos = m_nameToHashTableMem . memory + localTableSize ;
u32 left = u32 ( reader . GetLeft ( ) ) ;
u32 toRead = serverTableSize - localTableSize ;
bool needMore = left < toRead ;
if ( needMore )
toRead = left ;
2023-12-05 19:50:46 -05:00
m_nameToHashTableMem . AllocateNoLock ( toRead , 1 , TC ( " NameToHashTable " ) ) ;
2023-11-29 18:47:11 -05:00
reader . ReadBytes ( pos , toRead ) ;
localTableSize + = toRead ;
if ( ! needMore )
break ;
}
u32 addCount = 0 ;
BinaryReader r ( m_nameToHashTableMem . memory , readStartPos , NameToHashMemSize ) ;
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( m_nameToHashLookupLock , lock ) ;
2023-11-29 18:47:11 -05:00
while ( r . GetPosition ( ) < localTableSize )
{
StringKey name = r . ReadStringKey ( ) ;
CasKey hash = r . ReadCasKey ( ) ;
HashRec & rec = m_nameToHashLookup [ name ] ;
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( rec . lock , lock2 ) ;
2023-11-29 18:47:11 -05:00
if ( serverTime < rec . serverTime )
continue ;
rec . key = hash ;
rec . serverTime = serverTime ;
+ + addCount ;
}
return true ;
}
void SessionClient : : Connect ( )
{
StackBinaryWriter < 1024 > writer ;
NetworkMessage msg ( m_client , ServiceId , SessionMessageType_Connect , writer ) ;
writer . WriteString ( m_name . data ) ;
writer . WriteU32 ( SessionNetworkVersion ) ;
CasKey keys [ 2 ] ;
if ( m_useBinariesAsVersion )
{
StringBuffer < > dir ;
GetDirectoryOfCurrentModule ( m_logger , dir ) ;
u64 dirCount = dir . count ;
dir . Append ( PathSeparator ) . Append ( UBA_AGENT_EXECUTABLE ) ;
m_storage . CalculateCasKey ( keys [ 0 ] , dir . data ) ;
dir . Resize ( dirCount ) . Append ( PathSeparator ) . Append ( UBA_DETOURS_LIBRARY ) ;
m_storage . CalculateCasKey ( keys [ 1 ] , dir . data ) ;
}
writer . WriteCasKey ( keys [ 0 ] ) ;
writer . WriteCasKey ( keys [ 1 ] ) ;
writer . WriteU32 ( m_maxProcessCount ) ;
writer . WriteBool ( m_dedicated ) ;
StringBuffer < > info ;
GetSystemInfo ( info ) ;
writer . WriteString ( info ) ;
StackBinaryReader < SendMaxSize > reader ;
if ( ! msg . Send ( reader , m_stats . connectMsg ) )
return ;
m_connected = reader . ReadBool ( ) ;
if ( ! m_connected )
{
StringBuffer < > str ;
reader . ReadString ( str ) ;
m_logger . Error ( str . data ) ;
CasKey exeKey = reader . ReadCasKey ( ) ;
CasKey dllKey = reader . ReadCasKey ( ) ;
m_client . InvokeVersionMismatch ( exeKey , dllKey ) ;
return ;
}
{
CasKey detoursBinaryKey = reader . ReadCasKey ( ) ;
2023-12-15 16:57:54 -05:00
{
TimerScope s ( m_stats . storageRetrieve ) ;
Storage : : RetrieveResult result ;
if ( ! m_storage . RetrieveCasFile ( result , AsCompressed ( detoursBinaryKey , false ) , UBA_DETOURS_LIBRARY ) )
return ;
}
2023-11-29 18:47:11 -05:00
KeyToString dir ( StringKeyZero ) ;
StringBuffer < > detoursFile ;
if ( ! WriteBinFile ( detoursFile , UBA_DETOURS_LIBRARY , detoursBinaryKey , dir , DefaultAttributes ( ) ) )
return ;
# if PLATFORM_WINDOWS
char dll [ 1024 ] ;
sprintf_s ( dll , sizeof ( dll ) , " %ls " , detoursFile . data ) ;
m_detoursLibrary = dll ;
# else
m_detoursLibrary = detoursFile . data ;
# endif
}
bool resetCas = reader . ReadBool ( ) ;
if ( resetCas )
m_storage . Reset ( ) ;
m_sessionId = reader . ReadU32 ( ) ;
m_uiLanguage = reader . ReadU32 ( ) ;
2024-03-29 17:49:27 -04:00
m_storeObjFilesCompressed = reader . ReadBool ( ) ;
2023-11-29 18:47:11 -05:00
m_detailedTrace = reader . ReadBool ( ) ;
2023-12-15 02:18:13 -05:00
m_shouldSendLogToServer = reader . ReadBool ( ) ;
2024-03-03 21:18:10 -05:00
m_shouldSendTraceToServer = reader . ReadBool ( ) ;
2023-12-15 02:18:13 -05:00
if ( m_shouldSendLogToServer )
m_logToFile = true ;
2024-03-03 21:18:10 -05:00
if ( m_shouldSendTraceToServer )
{
//if (m_detailedTrace)
{
m_client . SetWorkTracker ( & m_trace ) ;
}
StartTrace ( nullptr ) ;
}
2023-11-29 18:47:11 -05:00
BuildEnvironmentVariables ( reader ) ;
m_loop = true ;
m_loopThread . Start ( [ this ] ( ) { ThreadCreateProcessLoop ( ) ; return 0 ; } ) ;
}
void SessionClient : : BuildEnvironmentVariables ( BinaryReader & reader )
{
TString tempStr ;
while ( true )
{
tempStr = reader . ReadString ( ) ;
if ( tempStr . empty ( ) )
break ;
m_environmentVariables . insert ( m_environmentVariables . end ( ) , tempStr . begin ( ) , tempStr . end ( ) ) ;
m_environmentVariables . push_back ( 0 ) ;
}
# if PLATFORM_WINDOWS
AddEnvironmentVariableNoLock ( TC ( " TEMP " ) , m_tempPath . data ) ;
AddEnvironmentVariableNoLock ( TC ( " TMP " ) , m_tempPath . data ) ;
2023-12-06 12:42:50 -05:00
# else
AddEnvironmentVariableNoLock ( TC ( " TMPDIR " ) , m_tempPath . data ) ;
2023-11-29 18:47:11 -05:00
# endif
StringBuffer < > v ;
for ( auto & var : m_localEnvironmentVariables )
if ( GetEnvironmentVariableW ( var , v . data , v . capacity ) )
AddEnvironmentVariableNoLock ( var , v . data ) ;
m_environmentVariables . push_back ( 0 ) ;
}
2024-01-11 15:42:50 -05:00
struct SessionClient : : InternalProcessStartInfo : ProcessStartInfoHolder
2023-11-29 18:47:11 -05:00
{
u32 processId = 0 ;
} ;
2024-01-11 21:01:56 -05:00
bool SessionClient : : SendProcessAvailable ( Vector < InternalProcessStartInfo > & out , float availableWeight )
2023-11-29 18:47:11 -05:00
{
StackBinaryWriter < 32 > writer ;
NetworkMessage msg ( m_client , ServiceId , SessionMessageType_ProcessAvailable , writer ) ;
writer . WriteU32 ( m_sessionId ) ;
writer . WriteU32 ( * ( u32 * ) & availableWeight ) ;
StackBinaryReader < SendMaxSize > reader ;
if ( ! msg . Send ( reader , m_stats . procAvailableMsg ) )
{
if ( m_loop )
m_logger . Error ( TC ( " Failed to send ProcessAvailable message " ) ) ;
return false ;
}
while ( true )
{
u32 processId = reader . ReadU32 ( ) ;
if ( processId = = 0 )
break ;
if ( processId = = SessionProcessAvailableResponse_Disconnect )
{
m_logger . Info ( TC ( " Got disconnect request from host " ) ) ;
return false ;
}
if ( processId = = SessionProcessAvailableResponse_RemoteExecutionDisabled )
{
2024-01-11 21:01:56 -05:00
m_remoteExecutionEnabled = false ;
2023-11-29 18:47:11 -05:00
break ;
}
out . push_back ( { } ) ;
InternalProcessStartInfo & info = out . back ( ) ;
info . processId = processId ;
2024-01-11 15:42:50 -05:00
info . Read ( reader ) ;
2023-11-29 18:47:11 -05:00
}
2023-12-15 15:38:35 -05:00
u32 neededDirectoryTableSize = reader . ReadU32 ( ) ;
u32 neededHashTableSize = reader . ReadU32 ( ) ;
2023-12-19 01:53:01 -05:00
if ( u32 knownInputsCount = reader . ReadU32 ( ) )
{
while ( knownInputsCount - - )
{
CasKey knownInputKey = reader . ReadCasKey ( ) ;
u32 mappingAlignment = reader . ReadU32 ( ) ;
bool storeUncompressed = ! m_allowMemoryMaps | | mappingAlignment = = 0 ;
if ( storeUncompressed )
knownInputKey = AsCompressed ( knownInputKey , false ) ;
m_client . AddWork ( [ knownInputKey , this ] ( )
{
Storage : : RetrieveResult result ;
bool allowProxy = true ;
2023-12-22 02:41:43 -05:00
bool res = m_storage . RetrieveCasFile ( result , knownInputKey , TC ( " KnownInput " ) , nullptr , 1 , allowProxy ) ;
2023-12-19 01:53:01 -05:00
( void ) res ;
} , 1 , TC ( " KnownInput " ) ) ;
}
}
2023-11-29 18:47:11 -05:00
if ( ! out . empty ( ) )
2023-12-15 15:38:35 -05:00
if ( neededDirectoryTableSize > GetDirectoryTableSize ( ) )
2024-08-28 00:48:26 -04:00
if ( ! SendUpdateDirectoryTable ( reader . Reset ( ) ) )
2023-12-15 15:38:35 -05:00
return false ;
// Always nice to update name-to-hash table since it can reduce number of messages while building.
2024-02-26 01:14:42 -05:00
u32 hashTableMemSize ;
{
SCOPED_READ_LOCK ( m_nameToHashMemLock , l ) ;
hashTableMemSize = u32 ( m_nameToHashTableMem . writtenSize ) ;
}
2023-12-15 15:38:35 -05:00
if ( neededHashTableSize > hashTableMemSize )
2024-08-28 00:48:26 -04:00
if ( ! SendUpdateNameToHashTable ( reader . Reset ( ) ) )
2023-11-29 18:47:11 -05:00
return false ;
return true ;
}
void SessionClient : : SendReturnProcess ( u32 processId , const tchar * reason )
{
StackBinaryWriter < 1024 > writer ;
NetworkMessage msg ( m_client , ServiceId , SessionMessageType_ProcessReturned , writer ) ;
writer . WriteU32 ( processId ) ;
writer . WriteString ( reason ) ;
StackBinaryReader < 32 > reader ;
if ( ! msg . Send ( reader , m_stats . procReturnedMsg ) )
return ;
}
2024-04-05 19:10:53 -04:00
bool SessionClient : : SendProcessInputs ( ProcessImpl & process )
{
auto inputs = process . GetTrackedInputs ( ) ;
2024-04-10 20:21:25 -04:00
u32 left = u32 ( inputs . size ( ) ) ;
u32 capacityToAdd = left ;
u8 * readPos = inputs . data ( ) ;
while ( left )
{
StackBinaryWriter < SendMaxSize > writer ;
NetworkMessage msg ( m_client , ServiceId , SessionMessageType_ProcessInputs , writer ) ;
writer . Write7BitEncoded ( process . m_id ) ;
writer . Write7BitEncoded ( capacityToAdd ) ;
capacityToAdd = 0 ;
u32 toWrite = Min ( left , u32 ( writer . GetCapacityLeft ( ) ) ) ;
writer . WriteBytes ( readPos , toWrite ) ;
StackBinaryReader < 32 > reader ;
if ( ! msg . Send ( reader ) )
return false ;
readPos + = toWrite ;
left - = toWrite ;
}
return true ;
2024-04-05 19:10:53 -04:00
}
bool SessionClient : : SendProcessFinished ( ProcessImpl & process , u32 exitCode )
{
StackBinaryWriter < SendMaxSize > writer ;
NetworkMessage msg ( m_client , ServiceId , SessionMessageType_ProcessFinished , writer ) ;
writer . WriteU32 ( process . m_id ) ;
writer . WriteU32 ( exitCode ) ;
writer . WriteU32 ( CountLogLines ( process ) ) ;
WriteLogLines ( writer , process ) ;
2024-08-22 01:31:17 -04:00
// This is normally set after callback so we need to calculate it here
auto & exitTime = process . m_processStats . exitTime ;
auto oldExitTime = exitTime . load ( ) ;
if ( exitTime )
exitTime = GetTime ( ) - exitTime ;
2024-04-05 19:10:53 -04:00
// Must be written last
process . m_processStats . Write ( writer ) ;
process . m_sessionStats . Write ( writer ) ;
process . m_storageStats . Write ( writer ) ;
2024-06-02 18:14:50 -04:00
process . m_kernelStats . Write ( writer ) ;
2024-04-05 19:10:53 -04:00
2024-08-22 01:31:17 -04:00
exitTime = oldExitTime ;
2024-04-05 19:10:53 -04:00
StackBinaryReader < 16 > reader ;
if ( ! msg . Send ( reader , m_stats . procFinishedMsg ) & & m_loop )
return m_logger . Error ( TC ( " Failed to send ProcessFinished message! " ) ) ;
return true ;
}
2023-12-15 15:38:35 -05:00
bool SessionClient : : SendUpdateDirectoryTable ( StackBinaryReader < SendMaxSize > & reader )
2023-11-29 18:47:11 -05:00
{
2023-12-15 15:38:35 -05:00
UBA_ASSERT ( reader . GetPosition ( ) = = 0 ) ;
2023-11-29 18:47:11 -05:00
StackBinaryWriter < 32 > writer ;
NetworkMessage msg ( m_client , ServiceId , SessionMessageType_GetDirectoriesFromServer , writer ) ;
writer . WriteU32 ( m_sessionId ) ;
if ( ! msg . Send ( reader , Stats ( ) . getDirsMsg ) )
return false ;
return UpdateDirectoryTableFromServer ( reader ) ;
}
2023-12-15 15:38:35 -05:00
bool SessionClient : : SendUpdateNameToHashTable ( StackBinaryReader < SendMaxSize > & reader )
2023-11-29 18:47:11 -05:00
{
StackBinaryWriter < 32 > writer ;
NetworkMessage msg ( m_client , ServiceId , SessionMessageType_GetNameToHashFromServer , writer ) ;
writer . WriteU32 ( ~ u32 ( 0 ) ) ;
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( m_nameToHashMemLock , lock ) ;
2023-11-29 18:47:11 -05:00
writer . WriteU32 ( u32 ( m_nameToHashTableMem . writtenSize ) ) ;
if ( ! msg . Send ( reader , Stats ( ) . getHashesMsg ) )
return false ;
return UpdateNameToHashTableFromServer ( reader ) ;
}
void SessionClient : : SendPing ( u64 memAvail , u64 memTotal )
{
u64 time = GetTime ( ) ;
if ( TimeToMs ( time - m_lastPingSendTime ) < 2000 ) // Ping every ~2 seconds... this is so server can disconnect a client quickly if no ping is coming
return ;
float cpuLoad = UpdateCpuLoad ( ) ;
StackBinaryWriter < 128 > writer ;
NetworkMessage msg ( m_client , ServiceId , SessionMessageType_Ping , writer ) ;
writer . WriteU32 ( m_sessionId ) ;
writer . WriteU64 ( m_lastPing ) ;
writer . WriteU64 ( memAvail ) ;
writer . WriteU64 ( memTotal ) ;
writer . WriteU32 ( * ( u32 * ) & cpuLoad ) ;
StackBinaryReader < 32 > reader ;
time = GetTime ( ) ;
if ( ! msg . Send ( reader , m_stats . pingMsg ) )
m_loop = false ;
u64 newTime = GetTime ( ) ;
m_lastPing = newTime - time ;
m_lastPingSendTime = newTime ;
if ( m_lastPing < m_bestPing | | m_bestPing = = 0 )
m_bestPing = m_lastPing ;
m_storage . Ping ( ) ;
2024-03-08 18:31:48 -05:00
if ( reader . ReadBool ( ) ) // abort
abort ( ) ;
2023-11-29 18:47:11 -05:00
}
2024-03-03 23:33:30 -05:00
void SessionClient : : SendSummary ( const Function < void ( Logger & ) > & extraInfo )
2023-11-29 18:47:11 -05:00
{
StackBinaryWriter < SendMaxSize > writer ;
NetworkMessage msg ( m_client , ServiceId , SessionMessageType_Summary , writer ) ;
writer . WriteU32 ( m_sessionId ) ;
WriteSummary ( writer , [ & ] ( Logger & logger )
{
PrintSummary ( logger ) ;
m_storage . PrintSummary ( logger ) ;
m_client . PrintSummary ( logger ) ;
2024-06-02 18:14:50 -04:00
KernelStats : : GetGlobal ( ) . Print ( logger , true ) ;
2024-03-03 23:33:30 -05:00
if ( extraInfo )
extraInfo ( logger ) ;
2023-11-29 18:47:11 -05:00
} ) ;
msg . Send ( ) ;
}
2024-01-16 13:24:28 -05:00
void SessionClient : : SendLogFileToServer ( ProcessImpl & pi )
{
auto logFile = pi . m_startInfo . logFile ;
if ( ! logFile | | ! * logFile )
return ;
WrittenFile f ;
f . name = logFile ;
f . attributes = DefaultAttributes ( ) ;
StringBuffer < > dest ;
if ( const tchar * lastSlash = TStrrchr ( logFile , PathSeparator ) )
logFile = lastSlash + 1 ;
dest . Append ( TC ( " <log> " ) ) . Append ( logFile ) ;
f . key = ToStringKeyLower ( dest ) ;
2024-03-03 21:18:10 -05:00
SendFile ( f , dest . data , pi . GetId ( ) , false ) ;
2024-05-08 12:53:11 -04:00
for ( auto & child : pi . m_childProcesses )
SendLogFileToServer ( * ( ProcessImpl * ) child . m_process ) ;
2024-01-16 13:24:28 -05:00
}
2024-07-07 15:08:15 -04:00
void SessionClient : : GetLogFileName ( StringBufferBase & out , const tchar * logFile , const tchar * arguments , u32 processId )
2024-01-16 13:24:28 -05:00
{
out . Append ( m_sessionLogDir . data ) ;
if ( logFile & & * logFile )
{
if ( const tchar * lastSeparator = TStrrchr ( logFile , PathSeparator ) )
logFile = lastSeparator + 1 ;
out . Append ( logFile ) ;
}
else
{
2024-07-07 15:08:15 -04:00
GenerateNameForProcess ( out , arguments , processId ) ;
2024-01-16 13:24:28 -05:00
out . Append ( TC ( " .log " ) ) ;
}
}
2023-11-29 18:47:11 -05:00
void SessionClient : : ThreadCreateProcessLoop ( )
{
struct ProcessRec
{
ProcessRec ( ProcessImpl * impl ) : handle ( impl ) { }
ProcessHandle handle ;
ReaderWriterLock lock ;
2024-02-26 01:50:10 -05:00
Atomic < bool > isKilled ;
Atomic < bool > isDone ;
2023-11-29 18:47:11 -05:00
float weight = 1.0f ;
} ;
List < ProcessRec > activeProcesses ;
u64 lastWaitTime = 0 ;
u64 waitForMemoryPressureStartTime = 0 ;
constexpr u64 waitTimeToSpawnAfterKillMs = 5 * 1000 ;
u64 memAvail ;
u64 memTotal ;
GetMemoryInfo ( memAvail , memTotal ) ;
SendPing ( memAvail , memTotal ) ;
u64 memRequiredToSpawn = u64 ( double ( memTotal ) * double ( 100 - m_memWaitLoadPercent ) / 100.0 ) ;
u64 memRequiredFree = u64 ( double ( memTotal ) * double ( 100 - m_memKillLoadPercent ) / 100.0 ) ;
float activeWeight = 0 ;
ReaderWriterLock activeWeightLock ;
u64 idleStartTime = GetTime ( ) ;
u32 processRequestCount = 0 ;
auto RemoveInactiveProcesses = [ & ] ( )
{
for ( auto it = activeProcesses . begin ( ) ; it ! = activeProcesses . end ( ) ; )
{
ProcessRec & r = * it ;
if ( ! r . isDone )
{
+ + it ;
continue ;
}
2024-02-26 01:50:10 -05:00
r . lock . EnterWrite ( ) ;
r . lock . LeaveWrite ( ) ;
2023-11-29 18:47:11 -05:00
it = activeProcesses . erase ( it ) ;
}
2024-01-11 21:01:56 -05:00
if ( m_remoteExecutionEnabled & & m_terminationReason )
2023-11-29 18:47:11 -05:00
{
2024-01-11 21:01:56 -05:00
m_remoteExecutionEnabled = false ;
2024-02-21 16:49:26 -05:00
m_logger . Info ( TC ( " %s. Will stop scheduling processes and send failing processes back for retry " ) , m_terminationReason . load ( ) ) ;
2023-11-29 18:47:11 -05:00
}
2024-08-21 19:56:16 -04:00
if ( ! activeProcesses . empty ( ) | | ! m_allowSpawn )
2023-11-29 18:47:11 -05:00
{
idleStartTime = GetTime ( ) ;
processRequestCount = 0 ;
}
2024-01-11 21:01:56 -05:00
else if ( m_remoteExecutionEnabled )
2023-11-29 18:47:11 -05:00
{
u32 idleTime = u32 ( TimeToS ( GetTime ( ) - idleStartTime ) ) ;
if ( idleTime > m_maxIdleSeconds )
{
m_logger . Info ( TC ( " Session has been idle longer than max idle time (%u seconds). Disconnecting (Did %u process requests during idle) " ) , m_maxIdleSeconds , processRequestCount ) ;
m_waitToSendEvent . Set ( ) ;
2024-01-11 21:01:56 -05:00
m_remoteExecutionEnabled = false ;
2023-11-29 18:47:11 -05:00
}
}
} ;
while ( m_loop )
{
2024-03-03 21:18:10 -05:00
float maxWeight = float ( m_maxProcessCount ) ;
2023-11-29 18:47:11 -05:00
u32 waitTimeoutMs = 3000 ;
2023-12-05 01:40:20 -05:00
FlushDeadProcesses ( ) ;
2023-11-29 18:47:11 -05:00
GetMemoryInfo ( memAvail , memTotal ) ;
if ( memAvail < memRequiredFree )
{
for ( auto it = activeProcesses . rbegin ( ) ; it ! = activeProcesses . rend ( ) ; + + it )
{
ProcessRec & rec = * it ;
if ( rec . isKilled | | rec . isDone )
continue ;
2024-02-26 01:50:10 -05:00
SCOPED_WRITE_LOCK ( rec . lock , lock ) ;
2024-08-01 16:00:15 -04:00
if ( rec . isDone )
continue ;
2023-11-29 18:47:11 -05:00
rec . handle . Cancel ( true ) ;
rec . isKilled = true ;
SendReturnProcess ( rec . handle . GetId ( ) , TC ( " Running out of memory " ) ) ;
+ + m_stats . killCount ;
m_logger . Warning ( TC ( " Killed process due to memory pressure (Available: %s Total: %s) " ) , BytesToText ( memAvail ) . str , BytesToText ( memTotal ) . str ) ;
break ;
}
lastWaitTime = GetTime ( ) ;
}
2024-08-21 19:56:16 -04:00
bool canSpawn = TimeToMs ( GetTime ( ) - lastWaitTime ) > waitTimeToSpawnAfterKillMs & & m_allowSpawn ;
2023-11-29 18:47:11 -05:00
if ( ! canSpawn )
waitTimeoutMs = 500 ;
bool firstCall = true ;
2024-01-11 21:01:56 -05:00
while ( m_remoteExecutionEnabled & & canSpawn & & m_loop )
2023-11-29 18:47:11 -05:00
{
float availableWeight ;
{
2024-02-26 01:14:42 -05:00
SCOPED_READ_LOCK ( activeWeightLock , lock ) ;
2023-11-29 18:47:11 -05:00
if ( activeWeight > = maxWeight )
break ;
availableWeight = maxWeight - activeWeight ;
}
if ( ! firstCall )
GetMemoryInfo ( memAvail , memTotal ) ;
if ( memAvail < memRequiredToSpawn )
{
if ( waitForMemoryPressureStartTime = = 0 )
{
m_logger . Info ( TC ( " Delaying spawn due to memory pressure (Available: %s Total: %s) " ) , BytesToText ( memAvail ) . str , BytesToText ( memTotal ) . str ) ;
waitForMemoryPressureStartTime = GetTime ( ) ;
}
break ;
}
if ( waitForMemoryPressureStartTime )
{
u64 waitTime = GetTime ( ) - waitForMemoryPressureStartTime ;
m_logger . Info ( TC ( " Waited %s for memory pressure to go down (Available: %s Total: %s) " ) , TimeToText ( waitTime ) . str , BytesToText ( memAvail ) . str , BytesToText ( memTotal ) . str ) ;
m_stats . waitMemPressure + = waitTime ;
waitForMemoryPressureStartTime = 0 ;
lastWaitTime = GetTime ( ) ;
waitTimeoutMs = 200 ;
availableWeight = Min ( availableWeight , 1.0f ) ;
}
Vector < InternalProcessStartInfo > startInfos ;
2024-01-11 21:01:56 -05:00
if ( ! SendProcessAvailable ( startInfos , availableWeight ) )
2023-11-29 18:47:11 -05:00
{
m_loop = false ;
break ;
}
+ + processRequestCount ;
2024-01-11 21:01:56 -05:00
if ( ! m_remoteExecutionEnabled )
2023-11-29 18:47:11 -05:00
{
m_logger . Info ( TC ( " Got remote execution disabled response from host (will finish %llu active processes) " ) , startInfos . size ( ) + activeProcesses . size ( ) ) ;
}
if ( startInfos . empty ( ) )
{
canSpawn = false ;
waitTimeoutMs = 200 ;
}
2024-06-15 01:56:18 -04:00
for ( InternalProcessStartInfo & startInfo : startInfos )
2023-11-29 18:47:11 -05:00
{
startInfo . uiLanguage = int ( m_uiLanguage ) ;
startInfo . priorityClass = m_defaultPriorityClass ;
startInfo . useCustomAllocator = ! m_disableCustomAllocator ;
startInfo . outputStatsThresholdMs = m_outputStatsThresholdMs ! = 0 ? m_outputStatsThresholdMs : startInfo . outputStatsThresholdMs ;
2024-04-05 19:10:53 -04:00
startInfo . rules = GetRules ( startInfo ) ;
2023-11-29 18:47:11 -05:00
StringBuffer < > logFile ;
if ( m_logToFile )
{
2024-07-07 15:08:15 -04:00
GetLogFileName ( logFile , startInfo . logFile , startInfo . arguments , startInfo . processId ) ;
2023-11-29 18:47:11 -05:00
startInfo . logFile = logFile . data ;
}
void * env = GetProcessEnvironmentVariables ( ) ;
2024-06-15 01:56:18 -04:00
auto process = new ProcessImpl ( * this , startInfo . processId , nullptr ) ;
2023-11-29 18:47:11 -05:00
activeProcesses . emplace_back ( process ) ;
ProcessRec * rec = & activeProcesses . back ( ) ;
2024-06-15 01:56:18 -04:00
rec - > weight = startInfo . weight ;
2023-11-29 18:47:11 -05:00
{
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( activeWeightLock , lock ) ;
2023-11-29 18:47:11 -05:00
activeWeight + = rec - > weight ;
}
struct ExitedRec
{
ExitedRec ( SessionClient & s , ReaderWriterLock & l , float & w , ProcessRec * r ) : session ( s ) , activeWeightLock ( l ) , activeWeight ( w ) , rec ( r ) { }
SessionClient & session ;
ReaderWriterLock & activeWeightLock ;
float & activeWeight ;
ProcessRec * rec ;
} ;
ExitedRec * exitedRec = new ExitedRec ( * this , activeWeightLock , activeWeight , rec ) ;
2024-01-02 17:42:42 -05:00
startInfo . userData = exitedRec ;
2023-11-29 18:47:11 -05:00
startInfo . exitedFunc = [ ] ( void * userData , const ProcessHandle & h )
{
auto er = ( ExitedRec * ) userData ;
SessionClient & session = er - > session ;
ReaderWriterLock & activeWeightLock = er - > activeWeightLock ;
float & activeWeight = er - > activeWeight ;
ProcessRec * rec = er - > rec ;
delete er ;
2024-01-11 15:42:50 -05:00
auto & startInfo = h . GetStartInfo ( ) ;
2023-12-15 02:18:13 -05:00
if ( session . m_shouldSendLogToServer )
2024-01-16 13:24:28 -05:00
session . SendLogFileToServer ( * ( ProcessImpl * ) h . m_process ) ;
2023-11-29 18:47:11 -05:00
2024-02-20 20:12:09 -05:00
float weight = rec - > weight ;
2023-11-29 18:47:11 -05:00
auto decreaseWeight = MakeGuard ( [ & ] ( )
{
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( activeWeightLock , weightLock ) ;
2024-02-20 20:12:09 -05:00
activeWeight - = weight ;
2023-11-29 18:47:11 -05:00
session . m_waitToSendEvent . Set ( ) ;
} ) ;
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( rec - > lock , lock ) ;
2024-02-20 20:12:09 -05:00
auto doneGuard = MakeGuard ( [ & ] ( ) { rec - > isDone = true ; session . m_waitToSendEvent . Set ( ) ; } ) ;
2023-11-29 18:47:11 -05:00
if ( rec - > isKilled )
return ;
2024-02-19 18:41:16 -05:00
2023-11-29 18:47:11 -05:00
auto & process = * ( ProcessImpl * ) h . m_process ;
if ( session . m_killRandomIndex ! = ~ 0u & & session . m_killRandomCounter + + = = session . m_killRandomIndex )
{
session . m_loop = false ;
2024-06-15 01:29:18 -04:00
session . m_logger . Info ( TC ( " Killed random process (%s) " ) , process . m_startInfo . GetDescription ( ) ) ;
2023-11-29 18:47:11 -05:00
return ;
}
u32 exitCode = process . m_exitCode ;
if ( exitCode ! = 0 )
{
if ( GetTime ( ) > = session . m_terminationTime )
{
if ( session . m_loop )
session . SendReturnProcess ( rec - > handle . GetId ( ) , session . m_terminationReason ) ;
return ;
}
2024-08-06 15:16:58 -04:00
if ( process . HasFailedMessage ( ) ) // If there are failure caused by failed messages we send back for retry
{
if ( session . m_loop )
session . SendReturnProcess ( rec - > handle . GetId ( ) , TC ( " Failed message " ) ) ;
return ;
}
2023-11-29 18:47:11 -05:00
}
2024-01-11 15:42:50 -05:00
if ( exitCode = = 0 | | startInfo . writeOutputFilesOnFail )
2023-11-29 18:47:11 -05:00
{
// Should we decrease weight before or after sending files?
//decreaseWeight.Execute();
if ( ! session . SendFiles ( process , process . m_processStats . sendFiles ) )
{
const tchar * desc = TC ( " Failed to send output files to host " ) ;
session . m_logger . Error ( desc ) ;
if ( session . m_loop )
session . SendReturnProcess ( rec - > handle . GetId ( ) , desc ) ;
return ;
}
}
decreaseWeight . Execute ( ) ;
if ( process . IsCancelled ( ) )
{
if ( session . m_loop )
session . SendReturnProcess ( rec - > handle . GetId ( ) , TC ( " Cancelled " ) ) ;
return ;
}
2024-04-05 19:10:53 -04:00
if ( startInfo . trackInputs )
session . SendProcessInputs ( process ) ;
2023-11-29 18:47:11 -05:00
2024-04-05 19:10:53 -04:00
session . SendProcessFinished ( process , exitCode ) ;
2023-11-29 18:47:11 -05:00
// TODO: These should be removed and instead added in TraceReader (so it will update over time)
session . m_stats . stats . Add ( process . m_sessionStats ) ;
session . m_storage . AddStats ( process . m_storageStats ) ;
if ( session . m_processFinished )
session . m_processFinished ( & process ) ;
} ;
2024-06-15 01:56:18 -04:00
process - > Start ( startInfo , true , env , true , true ) ;
2023-11-29 18:47:11 -05:00
}
RemoveInactiveProcesses ( ) ;
firstCall = false ;
}
SendPing ( memAvail , memTotal ) ;
2023-12-15 15:38:35 -05:00
//SendUpdateNameToHashTable(); // It is always nice to populate this at certain cadence since it might speed up running processes queries
2023-11-29 18:47:11 -05:00
m_waitToSendEvent . IsSet ( waitTimeoutMs ) ;
RemoveInactiveProcesses ( ) ;
2024-01-11 21:01:56 -05:00
if ( activeProcesses . empty ( ) & & ! m_remoteExecutionEnabled )
2024-07-22 18:00:44 -04:00
{
// There can be processes that are done (isDone is true) but are still in m_processes list (since they are removed from that after). give them some time
u64 counter = 300 ;
while ( true )
{
if ( ! counter - - )
{
m_logger . Warning ( TC ( " Took a long time for processes to be removed after being finished " ) ) ;
break ;
}
SCOPED_READ_LOCK ( m_processesLock , processesLock ) ;
if ( m_processes . empty ( ) )
break ;
processesLock . Leave ( ) ;
Sleep ( 10 ) ;
}
2023-11-29 18:47:11 -05:00
break ;
2024-07-22 18:00:44 -04:00
}
2023-11-29 18:47:11 -05:00
}
CancelAllProcessesAndWait ( ) ; // If we got the exit from server there is no point sending anything more back.. cancel everything
u32 retry = 0 ;
while ( true )
{
if ( retry + + = = 100 )
{
m_logger . Error ( TC ( " This should never happen! " ) ) ;
break ;
}
RemoveInactiveProcesses ( ) ;
if ( activeProcesses . empty ( ) )
break ;
m_waitToSendEvent . IsSet ( 100 ) ;
} ;
2023-12-19 01:53:01 -05:00
m_client . FlushWork ( ) ;
2024-03-03 21:18:10 -05:00
if ( m_shouldSendTraceToServer )
{
m_client . SetWorkTracker ( nullptr ) ;
StopTraceThread ( ) ;
StackBinaryWriter < SendMaxSize > writer ;
WriteSummary ( writer , [ & ] ( Logger & logger )
{
PrintSummary ( logger ) ;
m_storage . PrintSummary ( logger ) ;
m_client . PrintSummary ( logger ) ;
2024-06-02 18:14:50 -04:00
KernelStats : : GetGlobal ( ) . Print ( logger , true ) ;
2024-03-03 21:18:10 -05:00
} ) ;
m_trace . SessionSummary ( 0 , writer . GetData ( ) , writer . GetPosition ( ) ) ;
StringBuffer < > ubaFile ( m_sessionLogDir ) ;
ubaFile . Append ( TC ( " Trace.uba " ) ) ;
if ( StopTrace ( ubaFile . data ) )
{
WrittenFile f ;
f . name = ubaFile . data ;
f . attributes = DefaultAttributes ( ) ;
StringBuffer < > dest ( TC ( " <uba> " ) ) ;
f . key = ToStringKeyLower ( dest ) ;
SendFile ( f , dest . data , 0 , false ) ;
}
}
2023-11-29 18:47:11 -05:00
}
u32 SessionClient : : CountLogLines ( ProcessImpl & process )
{
u32 count = u32 ( process . m_logLines . size ( ) ) ;
for ( auto & child : process . m_childProcesses )
count + = CountLogLines ( * ( ProcessImpl * ) child . m_process ) ;
return count ;
}
void SessionClient : : WriteLogLines ( BinaryWriter & writer , ProcessImpl & process )
{
for ( auto & child : process . m_childProcesses )
WriteLogLines ( writer , * ( ProcessImpl * ) child . m_process ) ;
for ( auto & line : process . m_logLines )
{
writer . WriteString ( line . text ) ;
writer . WriteByte ( line . type ) ;
}
}
bool SessionClient : : AllocFailed ( Process & process , const tchar * allocType , u32 error )
{
//StackBinaryWriter<32> writer;
//NetworkMessage msg(m_client, ServiceId, SessionMessageType_VirtualAllocFailed, writer);
//if (!msg.Send())
// m_logger.Error(TC("Failed to send VirtualAllocFailed message!"));
return Session : : AllocFailed ( process , allocType , error ) ;
}
void SessionClient : : PrintSessionStats ( Logger & logger )
{
Session : : PrintSessionStats ( logger ) ;
}
2024-01-12 01:51:57 -05:00
bool SessionClient : : GetNextProcess ( Process & process , bool & outNewProcess , NextProcessInfo & outNextProcess , u32 prevExitCode , BinaryReader & statsReader )
2024-01-11 21:01:56 -05:00
{
outNewProcess = false ;
if ( ! m_remoteExecutionEnabled )
return true ;
auto & pi = ( ProcessImpl & ) process ;
if ( ! FlushWrittenFiles ( pi ) )
return false ;
2024-01-12 02:35:20 -05:00
ProcessStats processStats ;
processStats . Read ( statsReader , TraceVersion ) ;
processStats . sendFiles = pi . m_processStats . sendFiles ;
2024-01-11 21:01:56 -05:00
StackBinaryReader < SendMaxSize > reader ;
StackBinaryWriter < 16 * 1024 > writer ;
NetworkMessage msg ( m_client , ServiceId , SessionMessageType_GetNextProcess , writer ) ;
writer . WriteU32 ( pi . m_id ) ;
writer . WriteU32 ( prevExitCode ) ;
2024-01-12 02:35:20 -05:00
processStats . Write ( writer ) ;
2024-01-12 01:51:57 -05:00
writer . WriteBytes ( statsReader . GetPositionData ( ) , statsReader . GetLeft ( ) ) ;
2024-01-11 21:01:56 -05:00
if ( ! msg . Send ( reader , m_stats . customMsg ) )
return false ;
outNewProcess = reader . ReadBool ( ) ;
if ( outNewProcess )
{
2024-01-16 13:24:28 -05:00
if ( m_shouldSendLogToServer )
SendLogFileToServer ( pi ) ;
2024-01-11 21:01:56 -05:00
pi . m_exitCode = prevExitCode ;
if ( m_processFinished )
m_processFinished ( & process ) ;
outNextProcess . arguments = reader . ReadString ( ) ;
outNextProcess . workingDir = reader . ReadString ( ) ;
outNextProcess . description = reader . ReadString ( ) ;
2024-01-16 13:24:28 -05:00
outNextProcess . logFile = reader . ReadString ( ) ;
if ( m_logToFile )
{
StringBuffer < 512 > logFile ;
2024-07-07 15:08:15 -04:00
GetLogFileName ( logFile , outNextProcess . logFile . c_str ( ) , outNextProcess . arguments . c_str ( ) , process . GetId ( ) ) ;
2024-01-16 13:24:28 -05:00
outNextProcess . logFile = logFile . data ;
}
2024-01-11 21:01:56 -05:00
}
2024-08-28 00:48:26 -04:00
return SendUpdateDirectoryTable ( reader . Reset ( ) ) ;
2024-01-11 21:01:56 -05:00
}
2024-01-02 17:42:42 -05:00
bool SessionClient : : CustomMessage ( Process & process , BinaryReader & reader , BinaryWriter & writer )
2023-11-29 18:47:11 -05:00
{
StackBinaryWriter < SendMaxSize > msgWriter ;
NetworkMessage msg ( m_client , ServiceId , SessionMessageType_Custom , msgWriter ) ;
u32 recvSize = reader . ReadU32 ( ) ;
2024-01-02 17:42:42 -05:00
msgWriter . WriteU32 ( process . GetId ( ) ) ;
2023-11-29 18:47:11 -05:00
msgWriter . WriteU32 ( recvSize ) ;
msgWriter . WriteBytes ( reader . GetPositionData ( ) , recvSize ) ;
BinaryReader msgReader ( writer . GetData ( ) , 0 ) ;
if ( ! msg . Send ( msgReader , m_stats . customMsg ) )
return false ;
u32 responseSize = msgReader . ReadU32 ( ) ;
writer . AllocWrite ( 4ull + responseSize ) ;
return true ;
}
2024-05-08 12:53:11 -04:00
bool SessionClient : : SHGetKnownFolderPath ( Process & process , BinaryReader & reader , BinaryWriter & writer )
{
# if PLATFORM_WINDOWS
StackBinaryWriter < SendMaxSize > msgWriter ;
NetworkMessage msg ( m_client , ServiceId , SessionMessageType_SHGetKnownFolderPath , msgWriter ) ;
msgWriter . WriteBytes ( reader . GetPositionData ( ) , reader . GetLeft ( ) ) ;
BinaryReader msgReader ( writer . GetData ( ) , 0 ) ;
if ( ! msg . Send ( msgReader , m_stats . customMsg ) )
{
writer . WriteU32 ( u32 ( E_FAIL ) ) ;
return false ;
}
writer . AllocWrite ( msgReader . GetPosition ( ) ) ;
# endif
return true ;
}
2024-08-30 22:52:40 -04:00
bool SessionClient : : HostRun ( BinaryReader & reader , BinaryWriter & writer )
{
const void * data = reader . GetPositionData ( ) ;
u64 size = reader . GetLeft ( ) ;
CasKey key = ToCasKey ( CasKeyHasher ( ) . Update ( data , size ) , false ) ;
SCOPED_WRITE_LOCK ( m_hostRunCacheLock , l ) ;
auto insres = m_hostRunCache . try_emplace ( key ) ;
auto & buffer = insres . first - > second ;
if ( ! insres . second )
{
writer . WriteBytes ( buffer . data ( ) , buffer . size ( ) ) ;
return true ;
}
StackBinaryWriter < SendMaxSize > msgWriter ;
NetworkMessage msg ( m_client , ServiceId , SessionMessageType_HostRun , msgWriter ) ;
msgWriter . WriteBytes ( data , size ) ;
BinaryReader msgReader ( writer . GetData ( ) , 0 ) ;
if ( ! msg . Send ( msgReader , m_stats . customMsg ) )
return false ;
writer . AllocWrite ( msgReader . GetLeft ( ) ) ;
buffer . resize ( msgReader . GetLeft ( ) ) ;
memcpy ( buffer . data ( ) , msgReader . GetPositionData ( ) , buffer . size ( ) ) ;
return true ;
}
2023-11-29 18:47:11 -05:00
bool SessionClient : : FlushWrittenFiles ( ProcessImpl & process )
{
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( process . m_writtenFilesLock , lock ) ;
2024-05-08 12:53:11 -04:00
bool success = SendFiles ( process , process . m_processStats . sendFiles ) ;
2024-01-09 12:04:16 -05:00
{
2024-02-26 01:14:42 -05:00
SCOPED_WRITE_LOCK ( m_outputFilesLock , lock2 ) ;
2024-01-09 12:04:16 -05:00
for ( auto & kv : process . m_writtenFiles )
m_outputFiles . erase ( kv . first ) ;
}
2023-11-29 18:47:11 -05:00
process . m_writtenFiles . clear ( ) ;
2024-05-08 12:53:11 -04:00
return success ;
2023-11-29 18:47:11 -05:00
}
2024-01-09 12:04:16 -05:00
bool SessionClient : : UpdateEnvironment ( ProcessImpl & process , const tchar * reason , bool resetStats )
2023-11-29 18:47:11 -05:00
{
2023-12-15 15:38:35 -05:00
StackBinaryReader < SendMaxSize > reader ;
2024-01-09 12:04:16 -05:00
if ( resetStats )
{
StackBinaryWriter < 16 * 1024 > writer ;
NetworkMessage msg ( m_client , ServiceId , SessionMessageType_UpdateEnvironment , writer ) ;
writer . WriteU32 ( process . m_id ) ;
writer . WriteString ( reason ) ;
process . m_processStats . Write ( writer ) ;
process . m_sessionStats . Write ( writer ) ;
process . m_storageStats . Write ( writer ) ;
2024-06-02 18:14:50 -04:00
process . m_kernelStats . Write ( writer ) ;
2024-01-09 12:04:16 -05:00
process . m_processStats = { } ;
process . m_sessionStats = { } ;
process . m_storageStats = { } ;
2024-06-02 18:14:50 -04:00
process . m_kernelStats = { } ;
2024-01-09 12:04:16 -05:00
if ( ! msg . Send ( reader , m_stats . customMsg ) )
return false ;
reader . Reset ( ) ;
}
2023-12-15 15:38:35 -05:00
return SendUpdateDirectoryTable ( reader ) ;
2023-11-29 18:47:11 -05:00
}
2024-03-03 21:18:10 -05:00
2024-05-01 02:26:42 -04:00
bool SessionClient : : LogLine ( ProcessImpl & process , const tchar * line , LogEntryType logType )
{
2024-08-06 15:16:58 -04:00
// Remove this once we have figured out a bug that seems to exist for remote execution
// Update: Bug has been found for macos... for windows we believe the bug is related to uninformed shutdown and having multiple tcp connections..
// ... one tcp connection is disconnected, causing file not found while another connection manages to send "process finished"
#if 0 // PLATFORM_WINDOWS
2024-06-17 02:02:04 -04:00
2024-05-01 02:26:42 -04:00
auto rules = process . m_startInfo . rules ;
if ( ! rules )
return true ;
const tchar * errorPos = nullptr ;
if ( rules - > index = = 1 )
{
if ( ! Contains ( line , TC ( " C1083 " ) , false , & errorPos ) )
return true ;
}
else
{
if ( ! Contains ( line , TC ( " ' file not found " ) ) )
return true ;
if ( ! Contains ( line , TC ( " fatal error: ' " ) , false , & errorPos ) )
return true ;
}
const tchar * fileBegin = TStrchr ( errorPos , ' \' ' ) ;
if ( ! fileBegin )
return true ;
+ + fileBegin ;
const tchar * fileEnd = TStrchr ( fileBegin , ' \' ' ) ;
if ( ! fileEnd )
return true ;
MemoryBlock memoryBlock ;
DirectoryTable dirTable ( & memoryBlock ) ;
{
SCOPED_WRITE_LOCK ( m_directoryTable . m_memoryLock , lock2 ) ;
m_directoryTable . m_memorySize = m_directoryTableMemPos ;
dirTable . Init ( m_directoryTable . m_memory , 0 , m_directoryTable . m_memorySize ) ;
}
2024-06-17 02:02:04 -04:00
StringBuffer < > errorPath ;
errorPath . Append ( fileBegin , fileEnd - fileBegin ) . Replace ( ' / ' , PathSeparator ) ;
{
StackBinaryWriter < 1024 > writer ;
NetworkMessage msg ( m_client , ServiceId , SessionMessageType_DebugFileNotFoundError , writer ) ;
writer . WriteString ( errorPath ) ;
writer . WriteString ( process . m_startInfo . workingDir ) ;
msg . Send ( ) ;
}
StringView searchString = errorPath ;
if ( searchString . data [ 0 ] = = ' . ' & & searchString . data [ 1 ] = = ' . ' )
{
searchString . data + = 3 ;
searchString . count - = 3 ;
}
2024-05-01 02:26:42 -04:00
u32 foundCount = 0 ;
2024-06-17 02:02:04 -04:00
dirTable . TraverseAllFilesNoLock ( [ & ] ( const DirectoryTable : : EntryInformation & info , const StringBufferBase & path , u32 dirOffset )
2024-05-01 02:26:42 -04:00
{
2024-05-10 12:56:21 -04:00
if ( ! path . EndsWith ( searchString . data ) )
2024-05-01 02:26:42 -04:00
return ;
2024-05-10 12:56:21 -04:00
if ( path [ path . count - searchString . count - 1 ] ! = PathSeparator )
return ;
2024-05-01 13:58:08 -04:00
auto ToString = [ ] ( bool b ) { return b ? TC ( " true " ) : TC ( " false " ) ; } ;
2024-05-01 02:26:42 -04:00
+ + foundCount ;
StringBuffer < > logStr ;
2024-06-17 02:02:04 -04:00
logStr . Appendf ( TC ( " File %s found in directory table at offset %u of %u while searching for matches for %s (File size %llu attr %u) " ) , path . data , dirOffset , dirTable . m_memorySize , searchString . data , info . size , info . attributes ) ;
2024-05-01 02:26:42 -04:00
process . LogLine ( false , logStr . data , logType ) ;
StringKey fileNameKey = ToStringKey ( path ) ;
SCOPED_READ_LOCK ( m_fileMappingTableLookupLock , mlock ) ;
auto findIt = m_fileMappingTableLookup . find ( fileNameKey ) ;
if ( findIt ! = m_fileMappingTableLookup . end ( ) )
{
auto & entry = findIt - > second ;
SCOPED_READ_LOCK ( entry . lock , entryCs ) ;
logStr . Clear ( ) . Appendf ( TC ( " File %s found in mapping table table. " ) , path . data ) ;
2024-05-01 13:58:08 -04:00
if ( entry . handled )
2024-05-01 02:26:42 -04:00
{
StringBuffer < 128 > mappingName ;
2024-05-01 13:58:08 -04:00
if ( entry . mapping . IsValid ( ) )
Storage : : GetMappingString ( mappingName , entry . mapping , entry . mappingOffset ) ;
else
mappingName . Append ( TC ( " Not valid " ) ) ;
logStr . Appendf ( TC ( " Success: %s Size: %u IsDir: %s Mapping name: %s Mapping offset: %u " ) , ToString ( entry . success ) , entry . size , ToString ( entry . isDir ) , mappingName . data , entry . mappingOffset ) ;
}
else
{
logStr . Appendf ( TC ( " Entry not handled " ) ) ;
2024-05-01 02:26:42 -04:00
}
}
else
logStr . Clear ( ) . Appendf ( TC ( " File %s not found in mapping table table. " ) , path . data ) ;
process . LogLine ( false , logStr . data , logType ) ;
CasKey key ;
if ( GetCasKeyForFile ( key , process . m_id , path , fileNameKey ) )
2024-05-01 13:58:08 -04:00
{
2024-06-17 02:02:04 -04:00
logStr . Clear ( ) . Appendf ( TC ( " File %s caskey is %s. " ) , path . data , CasKeyString ( key ) . str ) ;
StringBuffer < 512 > casKeyFile ;
if ( m_storage . GetCasFileName ( casKeyFile , key ) )
2024-05-01 13:58:08 -04:00
{
2024-06-17 02:02:04 -04:00
logStr . Appendf ( TC ( " CasKeyFile: %s " ) , casKeyFile . data ) ;
u64 size = 0 ;
u32 attributes = 0 ;
bool exists = FileExists ( m_logger , casKeyFile . data , & size , & attributes ) ;
logStr . Appendf ( TC ( " Exists: %s " ) , ToString ( exists ) ) ;
if ( exists )
2024-06-15 01:57:17 -04:00
{
2024-06-17 02:02:04 -04:00
logStr . Appendf ( TC ( " Size: %llu Attr: %u " ) , size , attributes ) ;
2024-05-01 13:58:08 -04:00
2024-06-17 02:02:04 -04:00
FileHandle fileHandle = uba : : CreateFileW ( casKeyFile . data , GENERIC_READ , FILE_SHARE_READ , OPEN_EXISTING , DefaultAttributes ( ) ) ;
if ( fileHandle = = InvalidFileHandle )
2024-06-15 01:57:17 -04:00
{
2024-06-17 02:02:04 -04:00
logStr . Appendf ( TC ( " Failed to open file %s (%s) " ) , casKeyFile . data , LastErrorToText ( ) . data ) ;
}
else
{
logStr . Appendf ( TC ( " CreateFile for read successful " ) ) ;
uba : : CloseFile ( casKeyFile . data , fileHandle ) ;
2024-06-15 01:57:17 -04:00
}
}
}
2024-06-17 02:02:04 -04:00
else
logStr . Appendf ( TC ( " Failed to get cas filename for cas key " ) ) ;
2024-05-01 13:58:08 -04:00
}
2024-05-01 02:26:42 -04:00
else
logStr . Clear ( ) . Appendf ( TC ( " File %s caskey not found " ) , path . data ) ;
process . LogLine ( false , logStr . data , logType ) ;
} ) ;
2024-06-17 02:02:04 -04:00
2024-05-01 02:26:42 -04:00
if ( ! foundCount )
{
StringBuffer < > logStr ;
logStr . Appendf ( TC ( " No matching entry found in directory table while searching for matches for %s. DirTable size: %u " ) , searchString . data , GetDirectoryTableSize ( ) ) ;
process . LogLine ( false , logStr . data , logType ) ;
2024-06-17 02:02:04 -04:00
if ( errorPath . StartsWith ( TC ( " .. \\ Intermediate " ) ) )
{
auto workDir = process . m_startInfo . workingDir ;
StringBuffer < > fullPath ;
FixPath ( errorPath . data , workDir , TStrlen ( workDir ) , fullPath ) ;
}
2024-05-01 02:26:42 -04:00
}
2024-06-17 02:02:04 -04:00
# endif
2024-05-01 02:26:42 -04:00
return true ;
}
2024-03-03 21:18:10 -05:00
void SessionClient : : TraceSessionUpdate ( )
{
float cpuLoad = 0.0f ; //UpdateCpuLoad();
u64 send ;
u64 recv ;
if ( auto backend = m_client . GetFirstConnectionBackend ( ) )
{
backend - > GetTotalSendAndRecv ( send , recv ) ;
}
else
{
recv = m_client . GetTotalRecvBytes ( ) ;
send = m_client . GetTotalSentBytes ( ) ;
}
u64 memAvail = 0 ; //m_memAvail;
u64 memTotal = 0 ; //m_memTotal;
// send and recv are swapped on purpose because that is how visualizer is visualizing
m_trace . SessionUpdate ( 0 , 0 , send , recv , 0 , memAvail , memTotal , cpuLoad ) ;
}
2023-11-29 18:47:11 -05:00
}