From f580096468450bfa697ea9e118e891962ff6f796 Mon Sep 17 00:00:00 2001 From: Austin Eng Date: Fri, 14 Aug 2020 21:02:12 +0000 Subject: [PATCH] end2end_tests: Forward Windows debug messages to stderr When validation layers are enabled, D3D12 warnings and error messages are logged to the shared DBWIN_BUFFER segment of memory. This CL has makes the test environment watch for new events and logs them to stderr so they show up in the test bot logs. This helps debug problems in the D3D12 backend which previously just crashed with a general Device Lost message. Bug: none Change-Id: I0eaddf9e16303bd65579e85fe6693bd8cdfbd8da Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/26640 Reviewed-by: Rafael Cintron Reviewed-by: Bryan Bernhart Commit-Queue: Austin Eng --- src/tests/DawnTest.cpp | 12 +++- src/tests/DawnTest.h | 5 +- src/utils/BUILD.gn | 7 +++ src/utils/CMakeLists.txt | 7 +++ src/utils/EmptyDebugLogger.cpp | 29 +++++++++ src/utils/PlatformDebugLogger.h | 29 +++++++++ src/utils/WindowsDebugLogger.cpp | 104 +++++++++++++++++++++++++++++++ 7 files changed, 190 insertions(+), 3 deletions(-) create mode 100644 src/utils/EmptyDebugLogger.cpp create mode 100644 src/utils/PlatformDebugLogger.h create mode 100644 src/utils/WindowsDebugLogger.cpp diff --git a/src/tests/DawnTest.cpp b/src/tests/DawnTest.cpp index 80d2b9940f..33b3701afb 100644 --- a/src/tests/DawnTest.cpp +++ b/src/tests/DawnTest.cpp @@ -24,6 +24,7 @@ #include "dawn_native/DawnNative.h" #include "dawn_wire/WireClient.h" #include "dawn_wire/WireServer.h" +#include "utils/PlatformDebugLogger.h" #include "utils/SystemUtils.h" #include "utils/TerribleCommandBuffer.h" #include "utils/WGPUHelpers.h" @@ -35,7 +36,7 @@ #include #include -#ifdef DAWN_ENABLE_BACKEND_OPENGL +#if defined(DAWN_ENABLE_BACKEND_OPENGL) # include "GLFW/glfw3.h" # include "dawn_native/OpenGLBackend.h" #endif // DAWN_ENABLE_BACKEND_OPENGL @@ -81,7 +82,7 @@ namespace { DawnTestEnvironment* gTestEnv = nullptr; -} // namespace +} // anonymous namespace const RGBA8 RGBA8::kZero = RGBA8(0, 0, 0, 0); const RGBA8 RGBA8::kBlack = RGBA8(0, 0, 0, 255); @@ -186,6 +187,11 @@ void DawnTestEnvironment::SetEnvironment(DawnTestEnvironment* env) { DawnTestEnvironment::DawnTestEnvironment(int argc, char** argv) { ParseArgs(argc, argv); + if (mEnableBackendValidation) { + mPlatformDebugLogger = + std::unique_ptr(utils::CreatePlatformDebugLogger()); + } + // Create a temporary instance to select available and preferred adapters. This is done before // test instantiation so GetAvailableAdapterTestParamsForBackends can generate test // parameterizations all selected adapters. We drop the instance at the end of this function @@ -199,6 +205,8 @@ DawnTestEnvironment::DawnTestEnvironment(int argc, char** argv) { PrintTestConfigurationAndAdapterInfo(); } +DawnTestEnvironment::~DawnTestEnvironment() = default; + void DawnTestEnvironment::ParseArgs(int argc, char** argv) { size_t argLen = 0; // Set when parsing --arg=X arguments for (int i = 1; i < argc; ++i) { diff --git a/src/tests/DawnTest.h b/src/tests/DawnTest.h index 903ab60d81..1116b5685b 100644 --- a/src/tests/DawnTest.h +++ b/src/tests/DawnTest.h @@ -155,6 +155,7 @@ BackendTestConfig VulkanBackend(std::initializer_list forceEnabledW std::initializer_list forceDisabledWorkarounds = {}); namespace utils { + class PlatformDebugLogger; class TerribleCommandBuffer; } // namespace utils @@ -176,7 +177,7 @@ void InitDawnEnd2EndTestEnvironment(int argc, char** argv); class DawnTestEnvironment : public testing::Environment { public: DawnTestEnvironment(int argc, char** argv); - ~DawnTestEnvironment() override = default; + ~DawnTestEnvironment() override; static void SetEnvironment(DawnTestEnvironment* env); @@ -219,6 +220,8 @@ class DawnTestEnvironment : public testing::Environment { std::string mWireTraceDir; std::vector mDevicePreferences; std::vector mAdapterProperties; + + std::unique_ptr mPlatformDebugLogger; }; class DawnTestBase { diff --git a/src/utils/BUILD.gn b/src/utils/BUILD.gn index a2c57c3e6e..eff41835b1 100644 --- a/src/utils/BUILD.gn +++ b/src/utils/BUILD.gn @@ -70,6 +70,7 @@ static_library("dawn_utils") { "ComboRenderBundleEncoderDescriptor.h", "ComboRenderPipelineDescriptor.cpp", "ComboRenderPipelineDescriptor.h", + "PlatformDebugLogger.h", "SystemUtils.cpp", "SystemUtils.h", "TerribleCommandBuffer.cpp", @@ -89,6 +90,12 @@ static_library("dawn_utils") { libs = [] frameworks = [] + if (is_win) { + sources += [ "WindowsDebugLogger.cpp" ] + } else { + sources += [ "EmptyDebugLogger.cpp" ] + } + if (is_win) { sources += [ "WindowsTimer.cpp" ] } else if (is_mac) { diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt index 3a959401a2..080c2d5603 100644 --- a/src/utils/CMakeLists.txt +++ b/src/utils/CMakeLists.txt @@ -22,6 +22,7 @@ target_sources(dawn_utils PRIVATE "ComboRenderPipelineDescriptor.h" "GLFWUtils.cpp" "GLFWUtils.h" + "PlatformDebugLogger.h" "SystemUtils.cpp" "SystemUtils.h" "TerribleCommandBuffer.cpp" @@ -42,6 +43,12 @@ target_link_libraries(dawn_utils glfw ) +if(WIN32) + target_sources(dawn_utils PRIVATE "WindowsDebugLogger.cpp") +else() + target_sources(dawn_utils PRIVATE "EmptyDebugLogger.cpp") +endif() + if(WIN32) target_sources(dawn_utils PRIVATE "WindowsTimer.cpp") elseif(APPLE) diff --git a/src/utils/EmptyDebugLogger.cpp b/src/utils/EmptyDebugLogger.cpp new file mode 100644 index 0000000000..ed0ad7ff53 --- /dev/null +++ b/src/utils/EmptyDebugLogger.cpp @@ -0,0 +1,29 @@ +// Copyright 2020 The Dawn 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 "utils/PlatformDebugLogger.h" + +namespace utils { + + class EmptyDebugLogger : public PlatformDebugLogger { + public: + EmptyDebugLogger() = default; + ~EmptyDebugLogger() override = default; + }; + + PlatformDebugLogger* CreatePlatformDebugLogger() { + return new EmptyDebugLogger(); + } + +} // namespace utils diff --git a/src/utils/PlatformDebugLogger.h b/src/utils/PlatformDebugLogger.h new file mode 100644 index 0000000000..33c46dec44 --- /dev/null +++ b/src/utils/PlatformDebugLogger.h @@ -0,0 +1,29 @@ +// Copyright 2020 The Dawn 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 UTILS_PLATFORMDEBUGLOGGER_H_ +#define UTILS_PLATFORMDEBUGLOGGER_H_ + +namespace utils { + + class PlatformDebugLogger { + public: + virtual ~PlatformDebugLogger() = default; + }; + + PlatformDebugLogger* CreatePlatformDebugLogger(); + +} // namespace utils + +#endif // UTILS_PLATFORMDEBUGLOGGER_H_ diff --git a/src/utils/WindowsDebugLogger.cpp b/src/utils/WindowsDebugLogger.cpp new file mode 100644 index 0000000000..0383e0a0d0 --- /dev/null +++ b/src/utils/WindowsDebugLogger.cpp @@ -0,0 +1,104 @@ +// Copyright 2020 The Dawn 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 "utils/PlatformDebugLogger.h" + +#include "common/Assert.h" +#include "common/windows_with_undefs.h" + +#include +#include + +namespace utils { + + class WindowsDebugLogger : public PlatformDebugLogger { + public: + WindowsDebugLogger() : PlatformDebugLogger() { + if (IsDebuggerPresent()) { + // This condition is true when running inside Visual Studio or some other debugger. + // Messages are already printed there so we don't need to do anything. + return; + } + + mShouldExitHandle = CreateEventA(nullptr, TRUE, FALSE, nullptr); + ASSERT(mShouldExitHandle != nullptr); + + mThread = std::thread( + [](HANDLE shouldExit) { + // https://blogs.msdn.microsoft.com/reiley/2011/07/29/a-debugging-approach-to-outputdebugstring/ + // for the layout of this struct. + struct { + DWORD process_id; + char data[4096 - sizeof(DWORD)]; + }* dbWinBuffer = nullptr; + + HANDLE file = CreateFileMappingA(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, + 0, sizeof(*dbWinBuffer), "DBWIN_BUFFER"); + ASSERT(file != nullptr); + ASSERT(file != INVALID_HANDLE_VALUE); + + dbWinBuffer = static_cast( + MapViewOfFile(file, SECTION_MAP_READ, 0, 0, 0)); + ASSERT(dbWinBuffer != nullptr); + + HANDLE dbWinBufferReady = + CreateEventA(nullptr, FALSE, FALSE, "DBWIN_BUFFER_READY"); + ASSERT(dbWinBufferReady != nullptr); + + HANDLE dbWinDataReady = CreateEventA(nullptr, FALSE, FALSE, "DBWIN_DATA_READY"); + ASSERT(dbWinDataReady != nullptr); + + std::array waitHandles = {shouldExit, dbWinDataReady}; + while (true) { + SetEvent(dbWinBufferReady); + DWORD wait = WaitForMultipleObjects(waitHandles.size(), waitHandles.data(), + FALSE, INFINITE); + if (wait == WAIT_OBJECT_0) { + break; + } + ASSERT(wait == WAIT_OBJECT_0 + 1); + fprintf(stderr, "%.*s\n", static_cast(sizeof(dbWinBuffer->data)), + dbWinBuffer->data); + fflush(stderr); + } + + CloseHandle(dbWinDataReady); + CloseHandle(dbWinBufferReady); + UnmapViewOfFile(dbWinBuffer); + CloseHandle(file); + }, + mShouldExitHandle); + } + + ~WindowsDebugLogger() override { + if (mShouldExitHandle != nullptr) { + ASSERT(SetEvent(mShouldExitHandle)); + CloseHandle(mShouldExitHandle); + } + + if (mThread.joinable()) { + mThread.join(); + } + } + + private: + std::thread mThread; + HANDLE mShouldExitHandle = INVALID_HANDLE_VALUE; + }; + + PlatformDebugLogger* CreatePlatformDebugLogger() { + return new WindowsDebugLogger(); + } + +} // namespace utils