You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
We still have some duplicate inotify watches, but this first pass will spew a lot more information when we hit inotify limits.
Adds a "DumpINotifyStats" command in non-release builds
Spews global inotify & UE stats, along with physical count of directories, etc.
Canonicalize directory path in FDirectoryWatcherLinux::RegisterDirectoryChangedCallback_Handle
Shootergame was adding 141 duplicate watches for Samples/Games/ShooterGame/Content w/o this. Was 1136, is now 995.
Change PathsToWatchDescriptors tmap to PathNameHashSet
Don't need to store full paths for each watch directory twice
Fix bugs in TestPAL in addition to adding DumpStats() command, which looks ~ like this:
LogDirectoryWatcher: Warning: inotify limits
LogDirectoryWatcher: Warning: max_queued_events: 16384
LogDirectoryWatcher: Warning: max_user_instances: 128
LogDirectoryWatcher: Warning: max_user_watches: 65536
LogDirectoryWatcher: Warning: inotify per-process stats
LogDirectoryWatcher: Warning: systemd (pid 2239) watches:23 instances:3
...
LogDirectoryWatcher: Warning: plugin_host-3.3 (pid 395041) watches:62 instances:1
LogDirectoryWatcher: Warning: plugin_host-3.8 (pid 395044) watches:62 instances:1
LogDirectoryWatcher: Warning: TestPAL (pid 396852) watches:2 instances:1
LogDirectoryWatcher: Warning: Total inotify Watches:392 Instances:28
LogDirectoryWatcher: Warning: Current watch requests
LogDirectoryWatcher: Warning: /var/tmp/DirectoryWatcherTest396852: 2 watches
LogDirectoryWatcher: Warning: Total count:2
The above is also dumped (once) when we fail to init or add a inotify watch.
Need to create better documentation and add a pointer to it, similar to what VSCode does: (hat tip Brandon)
https://code.visualstudio.com/docs/setup/linux#_visual-studio-code-is-unable-to-watch-for-file-changes-in-this-large-workspace-error-enospc
Related bugs:
; FPS BP Cooking Content - errno = 28, Out of inotify watches
https://jira.it.epicgames.com/browse/UE-125210
; inotify Warnings when Cooking Content for Linux
https://jira.it.epicgames.com/browse/UE-119696
; Time Niagara Sequencer failed to play | Error: Couldn't find file for package
https://jira.it.epicgames.com/browse/UE-89750
; inotify warnings from Linux command line builds
https://jira.it.epicgames.com/browse/UE-76562
#review-17483609 @Brandon.Schaefer, @James.Singer
#jira UE-76562, UE-89750, UE-119696, UE-125210
[CL 17498916 by Michael Sartain in ue5-main branch]
210 lines
7.1 KiB
C++
210 lines
7.1 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "TestDirectoryWatcher.h"
|
|
|
|
#if USE_DIRECTORY_WATCHER
|
|
|
|
#include "IDirectoryWatcher.h"
|
|
#include "DirectoryWatcherModule.h"
|
|
#include "LaunchEngineLoop.h" // GEngineLoop
|
|
#include "Modules/ModuleManager.h"
|
|
#include "TestPALLog.h"
|
|
#include "HAL/PlatformFile.h"
|
|
#include "HAL/PlatformFileManager.h"
|
|
#include "HAL/PlatformProcess.h"
|
|
|
|
struct FChangeDetector
|
|
{
|
|
void OnDirectoryChanged(const TArray<FFileChangeData>& FileChanges)
|
|
{
|
|
UE_LOG(LogTestPAL, Display, TEXT(" -- %d change(s) detected"), static_cast<int32>(FileChanges.Num()));
|
|
|
|
int ChangeIdx = 0;
|
|
for (const auto& ThisEntry : FileChanges)
|
|
{
|
|
UE_LOG(LogTestPAL, Display, TEXT(" Change %d: %s was %s"),
|
|
++ChangeIdx,
|
|
*ThisEntry.Filename,
|
|
ThisEntry.Action == FFileChangeData::FCA_Added ? TEXT("added") :
|
|
(ThisEntry.Action == FFileChangeData::FCA_Removed ? TEXT("removed") :
|
|
(ThisEntry.Action == FFileChangeData::FCA_Modified ? TEXT("modified") : TEXT("??? (unknown)")
|
|
)
|
|
)
|
|
);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Kicks directory watcher test/
|
|
*/
|
|
int32 DirectoryWatcherTest(const TCHAR* CommandLine)
|
|
{
|
|
FPlatformMisc::SetCrashHandler(NULL);
|
|
FPlatformMisc::SetGracefulTerminationHandler();
|
|
|
|
GEngineLoop.PreInit(CommandLine);
|
|
UE_LOG(LogTestPAL, Display, TEXT("Running directory watcher test."));
|
|
|
|
IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
|
|
FString TestDir = FString::Printf(TEXT("%s/DirectoryWatcherTest%d"), FPlatformProcess::UserTempDir(), FPlatformProcess::GetCurrentProcessId());
|
|
|
|
if (PlatformFile.CreateDirectory(*TestDir) && PlatformFile.CreateDirectory(*(TestDir + TEXT("/subtest"))))
|
|
{
|
|
FChangeDetector Detector;
|
|
FDelegateHandle DirectoryChangedHandle;
|
|
|
|
IDirectoryWatcher* DirectoryWatcher = FModuleManager::Get().LoadModuleChecked<FDirectoryWatcherModule>(TEXT("DirectoryWatcher")).Get();
|
|
if (DirectoryWatcher)
|
|
{
|
|
/** Whether to include notifications for changes to actual directories (such as directories being created or removed). */
|
|
// IDirectoryWatcher::WatchOptions::IgnoreChangesInSubtree
|
|
/** Whether changes in subdirectories need to be reported. */
|
|
// IDirectoryWatcher::WatchOptions::IncludeDirectoryChanges
|
|
uint32 Flags = 0;
|
|
|
|
auto Callback = IDirectoryWatcher::FDirectoryChanged::CreateRaw(&Detector, &FChangeDetector::OnDirectoryChanged);
|
|
DirectoryWatcher->RegisterDirectoryChangedCallback_Handle(TestDir, Callback, DirectoryChangedHandle, Flags);
|
|
UE_LOG(LogTestPAL, Display, TEXT("Registered callback for changes in '%s'"), *TestDir);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogTestPAL, Fatal, TEXT("Could not get DirectoryWatcher module"));
|
|
}
|
|
|
|
FPlatformProcess::Sleep(1.0f);
|
|
DirectoryWatcher->Tick(1.0f);
|
|
|
|
// create and remove directory
|
|
UE_LOG(LogTestPAL, Display, TEXT("Creating DIRECTORY '%s'"), *(TestDir + TEXT("/test")));
|
|
verify(PlatformFile.CreateDirectory(*(TestDir + TEXT("/test"))));
|
|
DirectoryWatcher->Tick(1.0f);
|
|
FPlatformProcess::Sleep(1.0f);
|
|
DirectoryWatcher->Tick(1.0f);
|
|
|
|
UE_LOG(LogTestPAL, Display, TEXT("Deleting DIRECTORY '%s'"), *(TestDir + TEXT("/test")));
|
|
verify(PlatformFile.DeleteDirectory(*(TestDir + TEXT("/test"))));
|
|
DirectoryWatcher->Tick(1.0f);
|
|
FPlatformProcess::Sleep(1.0f);
|
|
DirectoryWatcher->Tick(1.0f);
|
|
|
|
// create and remove in a sub directory
|
|
UE_LOG(LogTestPAL, Display, TEXT("Creating DIRECTORY '%s'"), *(TestDir + TEXT("/subtest/blah")));
|
|
verify(PlatformFile.CreateDirectory(*(TestDir + TEXT("/subtest/blah"))));
|
|
DirectoryWatcher->Tick(1.0f);
|
|
FPlatformProcess::Sleep(1.0f);
|
|
DirectoryWatcher->Tick(1.0f);
|
|
|
|
UE_LOG(LogTestPAL, Display, TEXT("Deleting DIRECTORY '%s'"), *(TestDir + TEXT("/subtest/blah")));
|
|
verify(PlatformFile.DeleteDirectory(*(TestDir + TEXT("/subtest/blah"))));
|
|
DirectoryWatcher->Tick(1.0f);
|
|
FPlatformProcess::Sleep(1.0f);
|
|
DirectoryWatcher->Tick(1.0f);
|
|
|
|
{
|
|
// create file
|
|
FString DummyFileName = TestDir + TEXT("/test file.bin");
|
|
UE_LOG(LogTestPAL, Display, TEXT("Creating FILE '%s'"), *DummyFileName);
|
|
IFileHandle* DummyFile = PlatformFile.OpenWrite(*DummyFileName);
|
|
check(DummyFile);
|
|
DirectoryWatcher->Tick(1.0f);
|
|
FPlatformProcess::Sleep(1.0f);
|
|
DirectoryWatcher->Tick(1.0f);
|
|
|
|
// modify file
|
|
UE_LOG(LogTestPAL, Display, TEXT("Modifying FILE '%s'"), *DummyFileName);
|
|
uint8 Contents = 0;
|
|
DummyFile->Write(&Contents, sizeof(Contents));
|
|
DirectoryWatcher->Tick(1.0f);
|
|
FPlatformProcess::Sleep(1.0f);
|
|
DirectoryWatcher->Tick(1.0f);
|
|
|
|
// close the file
|
|
UE_LOG(LogTestPAL, Display, TEXT("Closing FILE '%s'"), *DummyFileName);
|
|
delete DummyFile;
|
|
DummyFile = nullptr;
|
|
DirectoryWatcher->Tick(1.0f);
|
|
FPlatformProcess::Sleep(1.0f);
|
|
DirectoryWatcher->Tick(1.0f);
|
|
|
|
// delete file
|
|
UE_LOG(LogTestPAL, Display, TEXT("Deleting FILE '%s'"), *DummyFileName);
|
|
verify(PlatformFile.DeleteFile(*DummyFileName));
|
|
DirectoryWatcher->Tick(1.0f);
|
|
FPlatformProcess::Sleep(1.0f);
|
|
DirectoryWatcher->Tick(1.0f);
|
|
}
|
|
|
|
// now the same in a grandchild directory
|
|
{
|
|
FString GrandChildDir = TestDir + TEXT("/subtest/grandchild");
|
|
|
|
UE_LOG(LogTestPAL, Display, TEXT("Creating DIRECTORY '%s'"), *GrandChildDir);
|
|
verify(PlatformFile.CreateDirectory(*GrandChildDir));
|
|
DirectoryWatcher->Tick(1.0f);
|
|
FPlatformProcess::Sleep(1.0f);
|
|
DirectoryWatcher->Tick(1.0f);
|
|
|
|
{
|
|
// create file
|
|
FString DummyFileName = GrandChildDir + TEXT("/test file.bin");
|
|
UE_LOG(LogTestPAL, Display, TEXT("Creating FILE '%s'"), *DummyFileName);
|
|
IFileHandle* DummyFile = PlatformFile.OpenWrite(*DummyFileName);
|
|
check(DummyFile);
|
|
DirectoryWatcher->Tick(1.0f);
|
|
FPlatformProcess::Sleep(1.0f);
|
|
DirectoryWatcher->Tick(1.0f);
|
|
|
|
// modify file
|
|
UE_LOG(LogTestPAL, Display, TEXT("Modifying FILE '%s'"), *DummyFileName);
|
|
uint8 Contents = 0;
|
|
DummyFile->Write(&Contents, sizeof(Contents));
|
|
DirectoryWatcher->Tick(1.0f);
|
|
FPlatformProcess::Sleep(1.0f);
|
|
DirectoryWatcher->Tick(1.0f);
|
|
|
|
// close the file
|
|
UE_LOG(LogTestPAL, Display, TEXT("Closing FILE '%s'"), *DummyFileName);
|
|
delete DummyFile;
|
|
DummyFile = nullptr;
|
|
DirectoryWatcher->Tick(1.0f);
|
|
FPlatformProcess::Sleep(1.0f);
|
|
DirectoryWatcher->Tick(1.0f);
|
|
|
|
// delete file
|
|
UE_LOG(LogTestPAL, Display, TEXT("Deleting FILE '%s'"), *DummyFileName);
|
|
PlatformFile.DeleteFile(*DummyFileName);
|
|
DirectoryWatcher->Tick(1.0f);
|
|
FPlatformProcess::Sleep(1.0f);
|
|
DirectoryWatcher->Tick(1.0f);
|
|
}
|
|
|
|
UE_LOG(LogTestPAL, Display, TEXT("Deleting DIRECTORY '%s'"), *GrandChildDir);
|
|
verify(PlatformFile.DeleteDirectory(*GrandChildDir));
|
|
DirectoryWatcher->Tick(1.0f);
|
|
FPlatformProcess::Sleep(1.0f);
|
|
DirectoryWatcher->Tick(1.0f);
|
|
}
|
|
|
|
DirectoryWatcher->DumpStats();
|
|
|
|
// clean up
|
|
verify(DirectoryWatcher->UnregisterDirectoryChangedCallback_Handle(TestDir, DirectoryChangedHandle));
|
|
// remove dirs as well
|
|
verify(PlatformFile.DeleteDirectory(*(TestDir + TEXT("/subtest"))));
|
|
verify(PlatformFile.DeleteDirectory(*TestDir));
|
|
|
|
UE_LOG(LogTestPAL, Display, TEXT("End of test"));
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogTestPAL, Fatal, TEXT("Could not create test directory %s."), *TestDir);
|
|
}
|
|
|
|
FEngineLoop::AppPreExit();
|
|
FEngineLoop::AppExit();
|
|
return 0;
|
|
}
|
|
|
|
#endif /* USE_DIRECTORY_WATCHER */
|