| // 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 (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; | |
| } | |
| if (mShouldExitHandle != nullptr) { | |
| BOOL result = SetEvent(mShouldExitHandle); | |
| ASSERT(result != 0); | |
| CloseHandle(mShouldExitHandle); | |
| } | |
| if (mThread.joinable()) { | |
| mThread.join(); | |
| } | |
| } | |
| private: | |
| std::thread mThread; | |
| HANDLE mShouldExitHandle = INVALID_HANDLE_VALUE; | |
| }; | |
| PlatformDebugLogger* CreatePlatformDebugLogger() { | |
| return new WindowsDebugLogger(); | |
| } | |
| } // namespace utils |