Files
UnrealEngineUWP/Engine/Source/Editor/VirtualizationEditor/Private/ValidateVirtualizedContentCommandlet.cpp
paul chipchase c6b707f171 Add a new cmdline option '-ValidateContent' to the 'ValidateVirtualizedContent commandlet
#rb none
#jira UE-182196
#rnx
#preflight 642d2b1389339023eb66b663

- The new cmdline option will download each payload and make sure that the data is correct in addition to checking that the payload exists.
- Note: There is no way to control which backend returns the payload so the VA graph, or cmdline parameters would need to be changed be the caller to make sure they are checking the backend that they are interested in.
-- This new option is fairly slow and is intended to be used as part of an automated checking system rather than by users themselves, so needing to change the graph and running the commandlet once per backend that needs testing shouldn't be too hard to do.

[CL 24924696 by paul chipchase in ue5-main branch]
2023-04-05 04:22:43 -04:00

209 lines
6.4 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "ValidateVirtualizedContentCommandlet.h"
#include "CommandletUtils.h"
#include "UObject/PackageTrailer.h"
#include "Virtualization/VirtualizationSystem.h"
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
UValidateVirtualizedContentCommandlet::UValidateVirtualizedContentCommandlet(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
int32 UValidateVirtualizedContentCommandlet::Main(const FString& Params)
{
using namespace UE::Virtualization;
TRACE_CPUPROFILER_EVENT_SCOPE(UValidateVirtualizedContentCommandlet);
TArray<FString> Tokens;
TArray<FString> Switches;
ParseCommandLine(*Params, Tokens, Switches);
const bool bValidateContent = Switches.Contains(TEXT("ValidateContent"));
UE_LOG(LogVirtualization, Display, TEXT("Finding packages in the project..."));
TArray<FString> PackagePaths = FindPackages(EFindPackageFlags::ExcludeEngineContent);
UE_LOG(LogVirtualization, Display, TEXT("Found %d package(s)"), PackagePaths.Num());
TMap<FString, UE::FPackageTrailer> Packages;
TSet<FIoHash> Payloads;
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());
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.."));
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)
{
UE_LOG(LogVirtualization, Error, TEXT("Failed to query the statuses of the payload(s)"));
return 1;
}
int32 ErrorCount = 0;
{
TRACE_CPUPROFILER_EVENT_SCOPE(ValidatePackages);
UE_LOG(LogVirtualization, Display, TEXT("Checking for missing payloads..."));
for (const TPair<FString, UE::FPackageTrailer>& Package : Packages)
{
bool bFoundErrors = false;
const UE::FPackageTrailer& Trailer = Package.Value;
const TArray<FIoHash> VirtualizedPayloads = Trailer.GetPayloads(UE::EPayloadStorageType::Virtualized);
for (const FIoHash& PayloadId : VirtualizedPayloads)
{
const int32 Index = Payloads.FindId(PayloadId).AsInteger();
if (PayloadStatuses[Index] == EPayloadStatus::FoundPartial)
{
// 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;
}
else if (PayloadStatuses[Index] != EPayloadStatus::FoundAll)
{
UE_LOG(LogVirtualization, Error, TEXT("%s: Payload '%s' could not be found in any persistent backend"), *Package.Key, *LexToString(PayloadId));
bFoundErrors = true;
}
}
if (bFoundErrors)
{
ErrorCount++;
}
}
}
return ErrorCount;
}
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;
}