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 <rafael.cintron@microsoft.com>
Reviewed-by: Bryan Bernhart <bryan.bernhart@intel.com>
Commit-Queue: Austin Eng <enga@chromium.org>
This commit is contained in:
Austin Eng 2020-08-14 21:02:12 +00:00 committed by Commit Bot service account
parent 24b5971b84
commit f580096468
7 changed files with 190 additions and 3 deletions

View File

@ -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 <sstream>
#include <unordered_map>
#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::PlatformDebugLogger>(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) {

View File

@ -155,6 +155,7 @@ BackendTestConfig VulkanBackend(std::initializer_list<const char*> forceEnabledW
std::initializer_list<const char*> 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<dawn_native::DeviceType> mDevicePreferences;
std::vector<TestAdapterProperties> mAdapterProperties;
std::unique_ptr<utils::PlatformDebugLogger> mPlatformDebugLogger;
};
class DawnTestBase {

View File

@ -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) {

View File

@ -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)

View File

@ -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

View File

@ -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_

View File

@ -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 <array>
#include <thread>
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<decltype(dbWinBuffer)>(
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<HANDLE, 2> 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<int>(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