diff --git a/src/gpgmm/common/BUILD.gn b/src/gpgmm/common/BUILD.gn index 8e439ce5b..1fc6d0b7f 100644 --- a/src/gpgmm/common/BUILD.gn +++ b/src/gpgmm/common/BUILD.gn @@ -157,6 +157,7 @@ if (is_win || is_linux || is_chromeos || is_mac || is_fuchsia || is_android) { "Math.cpp", "Math.h", "Platform.h", + "PlatformDebug.h", "PlatformTime.cpp", "PlatformTime.h", "PlatformUtils.cpp", @@ -169,6 +170,7 @@ if (is_win || is_linux || is_chromeos || is_mac || is_fuchsia || is_android) { if (is_win) { sources += [ + "WindowsPlatformDebug.cpp", "WindowsTime.cpp", "WindowsUtils.cpp", "WindowsUtils.h", diff --git a/src/gpgmm/common/CMakeLists.txt b/src/gpgmm/common/CMakeLists.txt index 442189a80..b04d02d3f 100644 --- a/src/gpgmm/common/CMakeLists.txt +++ b/src/gpgmm/common/CMakeLists.txt @@ -28,6 +28,7 @@ target_sources(gpgmm_common PRIVATE "Math.cpp" "Math.h" "Platform.h" + "PlatformDebug.h" "PlatformTime.cpp" "PlatformTime.h" "PlatformUtils.cpp" @@ -40,6 +41,7 @@ target_sources(gpgmm_common PRIVATE if (WIN32) target_sources(gpgmm_common PRIVATE + "WindowsPlatformDebug.cpp" "WindowsTime.cpp" "WindowsUtils.cpp" "WindowsUtils.h" diff --git a/src/gpgmm/common/PlatformDebug.h b/src/gpgmm/common/PlatformDebug.h new file mode 100644 index 000000000..1bd5e300c --- /dev/null +++ b/src/gpgmm/common/PlatformDebug.h @@ -0,0 +1,42 @@ +// Copyright 2021 The GPGMM Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef GPGMM_COMMON_PLATFORMDEBUG_H_ +#define GPGMM_COMMON_PLATFORMDEBUG_H_ + +namespace gpgmm { + + class DebugPlatform { + public: + // Starts memory leak checking, if supported. + virtual void StartMemoryCheck() { + } + + // End memory leak checking and return true if a memory leak was detected. + virtual bool EndMemoryCheck() { + return false; + } + + // Output or dump leak detection to console. + virtual void ReportMemoryLeaks() { + } + + virtual ~DebugPlatform() = default; + }; + + DebugPlatform* CreateDebugPlatform(); + +} // namespace gpgmm + +#endif // GPGMM_COMMON_PLATFORMDEBUG_H_ diff --git a/src/gpgmm/common/WindowsPlatformDebug.cpp b/src/gpgmm/common/WindowsPlatformDebug.cpp new file mode 100644 index 000000000..63e417c8f --- /dev/null +++ b/src/gpgmm/common/WindowsPlatformDebug.cpp @@ -0,0 +1,78 @@ +// Copyright 2021 The GPGMM Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "PlatformDebug.h" + +#include "Assert.h" +#include "Platform.h" + +#include + +#if defined(GPGMM_PLATFORM_WIN32) +# include +#endif // defined(GPGMM_PLATFORM_WIN32) + +namespace gpgmm { + + class WindowsDebugPlatform : public DebugPlatform { + public: + WindowsDebugPlatform() : DebugPlatform() { + // Explicitly initialize so they are "used" when compiling non-debug builds. + mStart = {}; + mEnd = {}; + mDiff = {}; + } + + void StartMemoryCheck() override { + _CrtMemCheckpoint(&mStart); + } + + bool EndMemoryCheck() override { + _CrtMemCheckpoint(&mEnd); + if (_CrtMemDifference(&mDiff, &mStart, &mEnd)) { + _CrtMemDumpStatistics(&mDiff); + return true; + } + return false; + } + + void ReportMemoryLeaks() override { + // Send all reports to STDOUT. + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); + _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDOUT); + _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE); + _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDOUT); + _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE); + _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDOUT); + // Perform automatic leak checking at program exit through a call to _CrtDumpMemoryLeaks + // and generate an error report if the application failed to free all the memory it + // allocated. + _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); + } + + private: + _CrtMemState mStart; + _CrtMemState mEnd; + _CrtMemState mDiff; + }; + + DebugPlatform* CreateDebugPlatform() { +#if GPGMM_PLATFORM_WIN32 + return new WindowsDebugPlatform(); +#else + return nullptr; +#endif + } + +} // namespace gpgmm diff --git a/src/tests/GPGMMTest.cpp b/src/tests/GPGMMTest.cpp index b7305e062..aa8f7977b 100644 --- a/src/tests/GPGMMTest.cpp +++ b/src/tests/GPGMMTest.cpp @@ -13,42 +13,29 @@ // limitations under the License. #include "src/tests/GPGMMTest.h" -#include "gpgmm/common/Platform.h" #include static GPGMMTestEnvironment* gTestEnv = nullptr; -#if defined(GPGMM_PLATFORM_WIN32) -# include -#endif - -void ReportMemoryLeaks() { -#if GPGMM_PLATFORM_WIN32 - // Send all reports to STDOUT. - _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); - _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDOUT); - _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE); - _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDOUT); - _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE); - _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDOUT); - // Perform automatic leak checking at program exit through a call to _CrtDumpMemoryLeaks - // and generate an error report if the application failed to free all the memory it - // allocated. - _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); -#endif -} +static std::unique_ptr mDebugPlatform(gpgmm::CreateDebugPlatform()); GPGMMTestBase::~GPGMMTestBase() { } void GPGMMTestBase::SetUp() { - ReportMemoryLeaks(); + if (mDebugPlatform != nullptr) { + mDebugPlatform->ReportMemoryLeaks(); + } } void GPGMMTestBase::TearDown() { } +gpgmm::DebugPlatform* GPGMMTestBase::GetDebugPlatform() { + return mDebugPlatform.get(); +} + // GPGMMTestEnvironment void InitGPGMMEnd2EndTestEnvironment(int argc, char** argv) { diff --git a/src/tests/GPGMMTest.h b/src/tests/GPGMMTest.h index ff54e5566..393322520 100644 --- a/src/tests/GPGMMTest.h +++ b/src/tests/GPGMMTest.h @@ -18,6 +18,7 @@ #include #include "gpgmm/common/Log.h" +#include "gpgmm/common/PlatformDebug.h" #define GPGMM_SKIP_TEST_IF(expr) \ do { \ @@ -28,12 +29,28 @@ } \ } while (0) +#define GPGMM_TEST_MEMORY_LEAK_START() \ + do { \ + GetDebugPlatform()->StartMemoryCheck(); \ + } while (0) + +#define GPGMM_TEST_MEMORY_LEAK_END() \ + do { \ + EXPECT_FALSE(GetDebugPlatform()->EndMemoryCheck()); \ + } while (0) + +namespace gpgmm { + class DebugPlatform; +} // namespace gpgmm + class GPGMMTestBase { protected: virtual ~GPGMMTestBase(); void SetUp(); void TearDown(); + + gpgmm::DebugPlatform* GetDebugPlatform(); }; void InitGPGMMEnd2EndTestEnvironment(int argc, char** argv); diff --git a/src/tests/end2end/D3D12ResourceAllocatorTests.cpp b/src/tests/end2end/D3D12ResourceAllocatorTests.cpp index ca6f726bb..6a30fd154 100644 --- a/src/tests/end2end/D3D12ResourceAllocatorTests.cpp +++ b/src/tests/end2end/D3D12ResourceAllocatorTests.cpp @@ -85,6 +85,15 @@ TEST_F(D3D12ResourceAllocatorTests, CreateAllocator) { } } +TEST_F(D3D12ResourceAllocatorTests, CreateAllocatorNoLeak) { + GPGMM_TEST_MEMORY_LEAK_START(); + { + ComPtr resourceAllocator; + ResourceAllocator::CreateAllocator(CreateBasicAllocatorDesc(), &resourceAllocator); + } + GPGMM_TEST_MEMORY_LEAK_END(); +} + // Exceeding the max resource heap size should always fail. TEST_F(D3D12ResourceAllocatorTests, CreateBufferOversized) { ComPtr resourceAllocator;