Files
ben clayton 5ed44a865d [Core/Containers] Remove MALLOCLEAK_IGNORE_SCOPE() for TLS allocations that are never freed
This rolls back CL 35966216.

Due to way some platforms destruct TLS before terminating threads, `FMallocLeakDetection::SetDisabledForThisThread(false)` can incorrectly fail `check(Count >= 0)`, despite a paired `SetDisabledForThisThread(true)` call. This can happen when freeing memory as part of thread destruction.

#rb john.stiles, neil.henning

[CL 36905886 by ben clayton in 5.5 branch]
2024-10-07 13:40:24 -04:00

144 lines
3.8 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "CoreMinimal.h"
#include "Misc/EventPool.h"
#include "Misc/LazySingleton.h"
#include "RequiredProgramMainCPPInclude.h" // required for ue programs
#include "HAL/MallocLeakDetection.h"
IMPLEMENT_APPLICATION(AutoRTFMTests, "AutoRTFMTests");
#define CATCH_AMALGAMATED_CUSTOM_MAIN
#include "catch_amalgamated.cpp"
class FListener final : public Catch::EventListenerBase
{
public:
using Catch::EventListenerBase::EventListenerBase;
void testCaseStarting(const Catch::TestCaseInfo&) override
{
FMallocLeakDetection::Get().SetAllocationCollection(true);
}
void testCaseEnded(const Catch::TestCaseStats&) override
{
FMallocLeakDetection::Get().SetAllocationCollection(false);
}
void testRunEnded(const Catch::TestRunStats&) override
{
}
};
CATCH_REGISTER_LISTENER(FListener)
// Checks for memory leaks. Returns true if no leaks were found, otherwise all
// leaks are printed to stderr and false is returned.
bool CheckNoMemoryLeaks();
int RunTests(int ArgC, const char* ArgV[])
{
{
// measure_environment lazily allocates an Environment pointer on first
// call, and holds this in a static variable. Pre-allocate this with the
// leak detector disabled to prevent this being reported as a leak.
MALLOCLEAK_IGNORE_SCOPE();
Catch::Benchmark::Detail::measure_environment<Catch::Benchmark::default_clock>();
}
Catch::Session Session;
bool NoRetry = false;
bool RetryNestedToo = false;
Session.cli(Session.cli()
| Catch::Clara::Opt(NoRetry)["--no-retry"]
| Catch::Clara::Opt(RetryNestedToo)["--retry-nested-too"]);
{
const int Result = Session.applyCommandLine(ArgC, ArgV);
if (0 != Result)
{
return Result;
}
}
if (RetryNestedToo)
{
AutoRTFM::ForTheRuntime::SetRetryTransaction(AutoRTFM::ForTheRuntime::EAutoRTFMRetryTransactionState::RetryNestedToo);
}
else if (NoRetry)
{
AutoRTFM::ForTheRuntime::SetRetryTransaction(AutoRTFM::ForTheRuntime::EAutoRTFMRetryTransactionState::NoRetry);
}
else
{
// Otherwise default to just retrying the parent transaction.
AutoRTFM::ForTheRuntime::SetRetryTransaction(AutoRTFM::ForTheRuntime::EAutoRTFMRetryTransactionState::RetryNonNested);
}
const TCHAR* CommandLine = TEXT("-Multiprocess");
GEngineLoop.PreInit(CommandLine);
FModuleManager::Get().StartProcessingNewlyLoadedObjects();
// Enable AutoRTFM.
AutoRTFM::ForTheRuntime::SetAutoRTFMRuntime(AutoRTFM::ForTheRuntime::EAutoRTFMEnabledState::AutoRTFM_Enabled);
// We don't want to trigger ensure's on abort because we are going to test that.
AutoRTFM::ForTheRuntime::SetEnsureOnAbortByLanguage(false);
int Result = Session.run();
FPlatformMisc::RequestExit(false);
CollectGarbage(GARBAGE_COLLECTION_KEEPFLAGS, /* bPerformFullPurge */ true);
FEngineLoop::AppPreExit();
FModuleManager::Get().UnloadModulesAtShutdown();
FEngineLoop::AppExit();
TLazySingleton<TEventPool<EEventMode::AutoReset>>::Get().EmptyPool();
TLazySingleton<TEventPool<EEventMode::ManualReset>>::Get().EmptyPool();
return Result;
}
int main(int ArgC, const char* ArgV[])
{
int Result = RunTests(ArgC, ArgV);
#if 0 // Memory leak detection disabled - see FORT-794390
if (0 == Result && !CheckNoMemoryLeaks() )
{
Result = -1;
}
#endif
return Result;
}
bool CheckNoMemoryLeaks()
{
class FOutputDeviceStderr final : public FOutputDevice
{
public:
void Serialize(const TCHAR* V, ELogVerbosity::Type Verbosity, const class FName& Category) override
{
std::cerr << reinterpret_cast<const char*>(StringCast<UTF8CHAR>(V).Get()) << std::endl;
}
};
FOutputDeviceStderr OutputDevice;
FMallocLeakReportOptions Options;
Options.OutputDevice = &OutputDevice;
int32 NumLeaks = FMallocLeakDetection::Get().DumpOpenCallstacks(TEXT("AutoRTFMTests"), Options);
if (NumLeaks > 0)
{
std::cerr << NumLeaks << " memory leaks detected" << std::endl;
return false;
}
return true;
}