2023-01-30 04:56:40 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "ValidateVirtualizedContentCommandlet.h"
# include "CommandletUtils.h"
# include "UObject/PackageTrailer.h"
2023-02-28 10:48:04 -05:00
# include "Virtualization/VirtualizationSystem.h"
2023-01-30 04:56:40 -05:00
2023-04-05 04:22:43 -04:00
namespace UE
{
/** Utility class so we can find all of the packages that use a given payload id */
class FIoHashToPackagesLookup
{
public :
FIoHashToPackagesLookup ( const TMap < FString , UE : : FPackageTrailer > & InPackages )
{
PackagePaths . SetNum ( InPackages . Num ( ) ) ;
for ( const TPair < FString , UE : : FPackageTrailer > & Package : InPackages )
{
const int32 Index = PackagePaths . Num ( ) ;
PackagePaths . Add ( Package . Key ) ;
const TArray < FIoHash > VirtualizedPayloads = Package . Value . GetPayloads ( UE : : EPayloadStorageType : : Virtualized ) ;
for ( const FIoHash & Id : VirtualizedPayloads )
{
PackageLookup . FindOrAdd ( Id ) . Add ( Index ) ;
}
}
}
void PrintError ( const FIoHash & Id , const TCHAR * ErrorMessage ) const
{
TStringBuilder < 1028 > FinalMessage ;
FinalMessage < < TEXT ( " Payload " ) < < Id < < TEXT ( " - " ) < < ErrorMessage ;
for ( int32 PackageIndex : PackageLookup [ Id ] )
{
FinalMessage < < LINE_TERMINATOR < < TEXT ( " \t " ) < < PackagePaths [ PackageIndex ] ;
}
UE_LOG ( LogVirtualization , Error , TEXT ( " %s " ) , FinalMessage . ToString ( ) ) ;
}
private :
TArray < FString > PackagePaths ;
TMap < FIoHash , TArray < uint32 , TInlineAllocator < 4 > > > PackageLookup ;
} ;
} //namespace UE
2023-01-30 04:56:40 -05:00
UValidateVirtualizedContentCommandlet : : UValidateVirtualizedContentCommandlet ( const FObjectInitializer & ObjectInitializer )
: Super ( ObjectInitializer )
{
}
2023-04-05 04:22:43 -04:00
2023-01-30 04:56:40 -05:00
int32 UValidateVirtualizedContentCommandlet : : Main ( const FString & Params )
{
2023-02-28 10:48:04 -05:00
using namespace UE : : Virtualization ;
2023-01-30 04:56:40 -05:00
2023-02-28 10:48:04 -05:00
TRACE_CPUPROFILER_EVENT_SCOPE ( UValidateVirtualizedContentCommandlet ) ;
2023-01-30 04:56:40 -05:00
2023-04-05 04:22:43 -04:00
TArray < FString > Tokens ;
TArray < FString > Switches ;
ParseCommandLine ( * Params , Tokens , Switches ) ;
const bool bValidateContent = Switches . Contains ( TEXT ( " ValidateContent " ) ) ;
2023-02-28 10:48:04 -05:00
UE_LOG ( LogVirtualization , Display , TEXT ( " Finding packages in the project... " ) ) ;
2023-03-01 03:47:24 -05:00
TArray < FString > PackagePaths = FindPackages ( EFindPackageFlags : : ExcludeEngineContent ) ;
2023-02-28 10:48:04 -05:00
UE_LOG ( LogVirtualization , Display , TEXT ( " Found %d package(s) " ) , PackagePaths . Num ( ) ) ;
2023-01-30 04:56:40 -05:00
2023-02-28 10:48:04 -05:00
TMap < FString , UE : : FPackageTrailer > Packages ;
TSet < FIoHash > Payloads ;
2023-01-30 04:56:40 -05:00
2023-02-28 10:48:04 -05:00
UE_LOG ( LogVirtualization , Display , TEXT ( " Scanning package(s) for virtualized payloads... " ) , PackagePaths . Num ( ) ) ;
FindVirtualizedPayloadsAndTrailers ( PackagePaths , Packages , Payloads ) ;
UE_LOG ( LogVirtualization , Display , TEXT ( " Found %d virtualized package(s) with %d unique payload(s) " ) , Packages . Num ( ) , Payloads . Num ( ) ) ;
2023-04-05 04:22:43 -04:00
UE : : FIoHashToPackagesLookup PkgLookupTable ( Packages ) ;
int32 ErrorCount = 0 ;
if ( bValidateContent )
{
ErrorCount = ValidatePayloadContent ( Packages , Payloads , PkgLookupTable ) ;
}
else
{
ErrorCount = ValidatePayloadsExists ( Packages , Payloads ) ;
}
if ( ErrorCount = = 0 )
{
UE_LOG ( LogVirtualization , Display , TEXT ( " All virtualized payloads could be found in persistent storage " ) ) ;
return 0 ;
}
else
{
UE_LOG ( LogVirtualization , Error , TEXT ( " %d/%d package(s) had at least one virtualized payload missing from persistent storage " ) , ErrorCount , Packages . Num ( ) ) ;
return 0 ;
}
}
int32 UValidateVirtualizedContentCommandlet : : ValidatePayloadsExists ( const TMap < FString , UE : : FPackageTrailer > & Packages , const TSet < FIoHash > & Payloads )
{
using namespace UE : : Virtualization ;
UE_LOG ( LogVirtualization , Display , TEXT ( " Validating payloads existence.. " ) ) ;
2023-02-28 10:48:04 -05:00
IVirtualizationSystem & System = IVirtualizationSystem : : Get ( ) ;
UE_LOG ( LogVirtualization , Display , TEXT ( " Querying the state of the virtualized payload(s) in persistent storage... " ) ) ;
TArray < EPayloadStatus > PayloadStatuses ;
if ( System . QueryPayloadStatuses ( Payloads . Array ( ) , EStorageType : : Persistent , PayloadStatuses ) ! = EQueryResult : : Success )
2023-01-30 04:56:40 -05:00
{
2023-02-28 10:48:04 -05:00
UE_LOG ( LogVirtualization , Error , TEXT ( " Failed to query the statuses of the payload(s) " ) ) ;
return 1 ;
2023-01-30 04:56:40 -05:00
}
2023-02-28 10:48:04 -05:00
int32 ErrorCount = 0 ;
2023-01-30 04:56:40 -05:00
{
2023-02-28 10:48:04 -05:00
TRACE_CPUPROFILER_EVENT_SCOPE ( ValidatePackages ) ;
2023-01-30 04:56:40 -05:00
2023-02-28 10:48:04 -05:00
UE_LOG ( LogVirtualization , Display , TEXT ( " Checking for missing payloads... " ) ) ;
2023-01-30 04:56:40 -05:00
2023-02-28 10:48:04 -05:00
for ( const TPair < FString , UE : : FPackageTrailer > & Package : Packages )
2023-01-30 04:56:40 -05:00
{
2023-02-28 10:48:04 -05:00
bool bFoundErrors = false ;
2023-04-05 04:22:43 -04:00
const UE : : FPackageTrailer & Trailer = Package . Value ;
const TArray < FIoHash > VirtualizedPayloads = Trailer . GetPayloads ( UE : : EPayloadStorageType : : Virtualized ) ;
2023-02-28 10:48:04 -05:00
for ( const FIoHash & PayloadId : VirtualizedPayloads )
2023-01-30 04:56:40 -05:00
{
2023-02-28 10:48:04 -05:00
const int32 Index = Payloads . FindId ( PayloadId ) . AsInteger ( ) ;
if ( PayloadStatuses [ Index ] = = EPayloadStatus : : FoundPartial )
2023-01-30 04:56:40 -05:00
{
2023-02-28 10:48:04 -05:00
// TODO: We currently don't have a way to inform the user which persistent backend the payload is not in!
UE_LOG ( LogVirtualization , Error , TEXT ( " %s: Payload '%s' could not be found in all persistent backends " ) , * Package . Key , * LexToString ( PayloadId ) ) ;
bFoundErrors = true ;
2023-01-30 04:56:40 -05:00
}
2023-02-28 10:48:04 -05:00
else if ( PayloadStatuses [ Index ] ! = EPayloadStatus : : FoundAll )
2023-01-30 04:56:40 -05:00
{
2023-02-28 10:48:04 -05:00
UE_LOG ( LogVirtualization , Error , TEXT ( " %s: Payload '%s' could not be found in any persistent backend " ) , * Package . Key , * LexToString ( PayloadId ) ) ;
bFoundErrors = true ;
2023-01-30 04:56:40 -05:00
}
2023-02-28 10:48:04 -05:00
}
if ( bFoundErrors )
{
ErrorCount + + ;
2023-01-30 04:56:40 -05:00
}
}
}
2023-04-05 04:22:43 -04:00
return ErrorCount ;
2023-01-30 04:56:40 -05:00
}
2023-04-05 04:22:43 -04:00
int32 UValidateVirtualizedContentCommandlet : : ValidatePayloadContent ( const TMap < FString , UE : : FPackageTrailer > & Packages , const TSet < FIoHash > & Payloads , const UE : : FIoHashToPackagesLookup & PkgLookupTable )
{
UE_LOG ( LogVirtualization , Display , TEXT ( " Validating payloads existence and content... " ) ) ;
std : : atomic < int32 > ErrorCount = 0 ;
const int32 BatchSize = 64 ;
UE : : Virtualization : : PullPayloadsThreaded ( Payloads . Array ( ) , BatchSize , TEXT ( " Validated " ) , [ & PkgLookupTable , & ErrorCount ] ( const UE : : Virtualization : : FPullRequest & Request )
{
if ( ! Request . IsSuccess ( ) | | Request . GetPayload ( ) . IsNull ( ) )
{
PkgLookupTable . PrintError ( Request . GetIdentifier ( ) , TEXT ( " could not be pulled " ) ) ;
ErrorCount + + ;
return ;
}
if ( Request . GetPayload ( ) . GetRawHash ( ) ! = Request . GetIdentifier ( ) )
{
PkgLookupTable . PrintError ( Request . GetIdentifier ( ) , TEXT ( " pulled a payload with the wrong hash " ) ) ;
ErrorCount + + ;
return ;
}
FSharedBuffer UncompressedPayload = Request . GetPayload ( ) . Decompress ( ) ;
if ( UncompressedPayload . IsNull ( ) )
{
PkgLookupTable . PrintError ( Request . GetIdentifier ( ) , TEXT ( " could not be decompressed " ) ) ;
ErrorCount + + ;
return ;
}
if ( FIoHash : : HashBuffer ( UncompressedPayload ) ! = Request . GetIdentifier ( ) )
{
PkgLookupTable . PrintError ( Request . GetIdentifier ( ) , TEXT ( " is corrupted " ) ) ;
ErrorCount + + ;
return ;
}
} ) ;
return ErrorCount ;
}