1 // Copyright 2020 The Dawn Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include "utils/PlatformDebugLogger.h" 16 17 #include "common/Assert.h" 18 #include "common/windows_with_undefs.h" 19 20 #include <array> 21 #include <thread> 22 23 namespace utils { 24 25 class WindowsDebugLogger : public PlatformDebugLogger { 26 public: WindowsDebugLogger()27 WindowsDebugLogger() : PlatformDebugLogger() { 28 if (IsDebuggerPresent()) { 29 // This condition is true when running inside Visual Studio or some other debugger. 30 // Messages are already printed there so we don't need to do anything. 31 return; 32 } 33 34 mShouldExitHandle = CreateEventA(nullptr, TRUE, FALSE, nullptr); 35 ASSERT(mShouldExitHandle != nullptr); 36 37 mThread = std::thread( 38 [](HANDLE shouldExit) { 39 // https://blogs.msdn.microsoft.com/reiley/2011/07/29/a-debugging-approach-to-outputdebugstring/ 40 // for the layout of this struct. 41 struct { 42 DWORD process_id; 43 char data[4096 - sizeof(DWORD)]; 44 }* dbWinBuffer = nullptr; 45 46 HANDLE file = CreateFileMappingA(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 47 0, sizeof(*dbWinBuffer), "DBWIN_BUFFER"); 48 ASSERT(file != nullptr); 49 ASSERT(file != INVALID_HANDLE_VALUE); 50 51 dbWinBuffer = static_cast<decltype(dbWinBuffer)>( 52 MapViewOfFile(file, SECTION_MAP_READ, 0, 0, 0)); 53 ASSERT(dbWinBuffer != nullptr); 54 55 HANDLE dbWinBufferReady = 56 CreateEventA(nullptr, FALSE, FALSE, "DBWIN_BUFFER_READY"); 57 ASSERT(dbWinBufferReady != nullptr); 58 59 HANDLE dbWinDataReady = CreateEventA(nullptr, FALSE, FALSE, "DBWIN_DATA_READY"); 60 ASSERT(dbWinDataReady != nullptr); 61 62 std::array<HANDLE, 2> waitHandles = {shouldExit, dbWinDataReady}; 63 while (true) { 64 SetEvent(dbWinBufferReady); 65 DWORD wait = WaitForMultipleObjects(waitHandles.size(), waitHandles.data(), 66 FALSE, INFINITE); 67 if (wait == WAIT_OBJECT_0) { 68 break; 69 } 70 ASSERT(wait == WAIT_OBJECT_0 + 1); 71 fprintf(stderr, "%.*s\n", static_cast<int>(sizeof(dbWinBuffer->data)), 72 dbWinBuffer->data); 73 fflush(stderr); 74 } 75 76 CloseHandle(dbWinDataReady); 77 CloseHandle(dbWinBufferReady); 78 UnmapViewOfFile(dbWinBuffer); 79 CloseHandle(file); 80 }, 81 mShouldExitHandle); 82 } 83 ~WindowsDebugLogger()84 ~WindowsDebugLogger() override { 85 if (IsDebuggerPresent()) { 86 // This condition is true when running inside Visual Studio or some other debugger. 87 // Messages are already printed there so we don't need to do anything. 88 return; 89 } 90 91 if (mShouldExitHandle != nullptr) { 92 BOOL result = SetEvent(mShouldExitHandle); 93 ASSERT(result != 0); 94 CloseHandle(mShouldExitHandle); 95 } 96 97 if (mThread.joinable()) { 98 mThread.join(); 99 } 100 } 101 102 private: 103 std::thread mThread; 104 HANDLE mShouldExitHandle = INVALID_HANDLE_VALUE; 105 }; 106 CreatePlatformDebugLogger()107 PlatformDebugLogger* CreatePlatformDebugLogger() { 108 return new WindowsDebugLogger(); 109 } 110 111 } // namespace utils 112