diff --git a/apps/openmw/mwgui/debugwindow.cpp b/apps/openmw/mwgui/debugwindow.cpp index 79375849c7..23c8dbfe2e 100644 --- a/apps/openmw/mwgui/debugwindow.cpp +++ b/apps/openmw/mwgui/debugwindow.cpp @@ -8,6 +8,8 @@ #include #include +#include + #ifndef BT_NO_PROFILE namespace @@ -103,9 +105,10 @@ namespace MWGui #endif } - std::vector DebugWindow::sLogCircularBuffer; - int64_t DebugWindow::sLogStartIndex = 0; - int64_t DebugWindow::sLogEndIndex = 0; + static std::vector sLogCircularBuffer; + static std::mutex sBufferMutex; + static int64_t sLogStartIndex; + static int64_t sLogEndIndex; void DebugWindow::startLogRecording() { @@ -125,6 +128,7 @@ namespace MWGui default: color = "#FFFFFF"; } bool bufferOverflow = false; + std::lock_guard lock(sBufferMutex); const int64_t bufSize = sLogCircularBuffer.size(); auto addChar = [&](char c) { @@ -153,6 +157,8 @@ namespace MWGui void DebugWindow::updateLogView() { + std::lock_guard lock(sBufferMutex); + if (!mLogView || sLogCircularBuffer.empty() || sLogStartIndex == sLogEndIndex) return; if (mLogView->isTextSelection()) @@ -161,7 +167,6 @@ namespace MWGui std::string addition; const int64_t bufSize = sLogCircularBuffer.size(); { - std::unique_lock lock = Log::lock(); if (sLogStartIndex < sLogEndIndex) addition = std::string(sLogCircularBuffer.data() + sLogStartIndex, sLogEndIndex - sLogStartIndex); else @@ -212,4 +217,4 @@ namespace MWGui else updateBulletProfile(); } -} \ No newline at end of file +} diff --git a/apps/openmw/mwgui/debugwindow.hpp b/apps/openmw/mwgui/debugwindow.hpp index dba7436bc3..9b8711137a 100644 --- a/apps/openmw/mwgui/debugwindow.hpp +++ b/apps/openmw/mwgui/debugwindow.hpp @@ -20,13 +20,7 @@ namespace MWGui void updateBulletProfile(); MyGUI::TabControl* mTabControl; - MyGUI::EditBox* mLogView; - - static std::vector sLogCircularBuffer; - static int64_t sLogStartIndex; - static int64_t sLogEndIndex; - MyGUI::EditBox* mBulletProfilerEdit; }; diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index d620882dde..f641f0ee4b 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include diff --git a/components/crashcatcher/windows_crashmonitor.cpp b/components/crashcatcher/windows_crashmonitor.cpp index 5688d0eeb8..2693f7681d 100644 --- a/components/crashcatcher/windows_crashmonitor.cpp +++ b/components/crashcatcher/windows_crashmonitor.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include diff --git a/components/debug/debugging.cpp b/components/debug/debugging.cpp index c46971d651..61de1ef4c1 100644 --- a/components/debug/debugging.cpp +++ b/components/debug/debugging.cpp @@ -7,12 +7,14 @@ #include #include - +#include #ifdef _WIN32 #include #include #endif +#include + namespace Debug { #ifdef _WIN32 @@ -66,79 +68,177 @@ namespace Debug static LogListener logListener; void setLogListener(LogListener listener) { logListener = std::move(listener); } - std::streamsize DebugOutputBase::write(const char *str, std::streamsize size) + class DebugOutputBase : public boost::iostreams::sink { - if (size <= 0) + public: + DebugOutputBase() + { + if (CurrentDebugLevel == NoLevel) + fillCurrentDebugLevel(); + } + + virtual std::streamsize write(const char* str, std::streamsize size) + { + if (size <= 0) + return size; + std::string_view msg{ str, size_t(size) }; + + // Skip debug level marker + Level level = getLevelMarker(str); + if (level != NoLevel) + msg = msg.substr(1); + + char prefix[32]; + int prefixSize; + { + prefix[0] = '['; + const auto now = std::chrono::system_clock::now(); + const auto time = std::chrono::system_clock::to_time_t(now); + prefixSize = std::strftime(prefix + 1, sizeof(prefix) - 1, "%T", std::localtime(&time)) + 1; + char levelLetter = " EWIVD*"[int(level)]; + const auto ms = std::chrono::duration_cast(now.time_since_epoch()).count(); + prefixSize += snprintf(prefix + prefixSize, sizeof(prefix) - prefixSize, + ".%03u %c] ", static_cast(ms % 1000), levelLetter); + } + + while (!msg.empty()) + { + if (msg[0] == 0) + break; + size_t lineSize = 1; + while (lineSize < msg.size() && msg[lineSize - 1] != '\n') + lineSize++; + writeImpl(prefix, prefixSize, level); + writeImpl(msg.data(), lineSize, level); + if (logListener) + logListener(level, std::string_view(prefix, prefixSize), std::string_view(msg.data(), lineSize)); + msg = msg.substr(lineSize); + } + return size; - std::string_view msg{str, size_t(size)}; - - // Skip debug level marker - Level level = getLevelMarker(str); - if (level != NoLevel) - msg = msg.substr(1); - - char prefix[32]; - int prefixSize; - { - prefix[0] = '['; - const auto now = std::chrono::system_clock::now(); - const auto time = std::chrono::system_clock::to_time_t(now); - prefixSize = std::strftime(prefix + 1, sizeof(prefix) - 1, "%T", std::localtime(&time)) + 1; - char levelLetter = " EWIVD*"[int(level)]; - const auto ms = std::chrono::duration_cast(now.time_since_epoch()).count(); - prefixSize += snprintf(prefix + prefixSize, sizeof(prefix) - prefixSize, - ".%03u %c] ", static_cast(ms % 1000), levelLetter); } - while (!msg.empty()) + virtual ~DebugOutputBase() = default; + + protected: + static Level getLevelMarker(const char* str) { - if (msg[0] == 0) - break; - size_t lineSize = 1; - while (lineSize < msg.size() && msg[lineSize - 1] != '\n') - lineSize++; - writeImpl(prefix, prefixSize, level); - writeImpl(msg.data(), lineSize, level); - if (logListener) - logListener(level, std::string_view(prefix, prefixSize), std::string_view(msg.data(), lineSize)); - msg = msg.substr(lineSize); + if (unsigned(*str) <= unsigned(Marker)) + { + return Level(*str); + } + + return NoLevel; } - return size; - } + static void fillCurrentDebugLevel() + { + const char* env = getenv("OPENMW_DEBUG_LEVEL"); + if (env) + { + std::string value(env); + if (value == "ERROR") + CurrentDebugLevel = Error; + else if (value == "WARNING") + CurrentDebugLevel = Warning; + else if (value == "INFO") + CurrentDebugLevel = Info; + else if (value == "VERBOSE") + CurrentDebugLevel = Verbose; + else if (value == "DEBUG") + CurrentDebugLevel = Debug; - Level DebugOutputBase::getLevelMarker(const char *str) + return; + } + + CurrentDebugLevel = Verbose; + } + + virtual std::streamsize writeImpl(const char* str, std::streamsize size, Level debugLevel) + { + return size; + } + }; + +#if defined _WIN32 && defined _DEBUG + class DebugOutput : public DebugOutputBase { - if (unsigned(*str) <= unsigned(Marker)) + public: + std::streamsize writeImpl(const char* str, std::streamsize size, Level debugLevel) { - return Level(*str); + // Make a copy for null termination + std::string tmp(str, static_cast(size)); + // Write string to Visual Studio Debug output + OutputDebugString(tmp.c_str()); + return size; } - return NoLevel; - } + virtual ~DebugOutput() = default; + }; +#else - void DebugOutputBase::fillCurrentDebugLevel() + class Tee : public DebugOutputBase { - const char* env = getenv("OPENMW_DEBUG_LEVEL"); - if (env) + public: + Tee(std::ostream& stream, std::ostream& stream2) + : out(stream), out2(stream2) { - std::string value(env); - if (value == "ERROR") - CurrentDebugLevel = Error; - else if (value == "WARNING") - CurrentDebugLevel = Warning; - else if (value == "INFO") - CurrentDebugLevel = Info; - else if (value == "VERBOSE") - CurrentDebugLevel = Verbose; - else if (value == "DEBUG") - CurrentDebugLevel = Debug; + // TODO: check which stream is stderr? + mUseColor = useColoredOutput(); - return; + mColors[Error] = Red; + mColors[Warning] = Yellow; + mColors[Info] = Reset; + mColors[Verbose] = DarkGray; + mColors[Debug] = DarkGray; + mColors[NoLevel] = Reset; } - CurrentDebugLevel = Verbose; - } + std::streamsize writeImpl(const char* str, std::streamsize size, Level debugLevel) override + { + out.write(str, size); + out.flush(); + + if (mUseColor) + { + out2 << "\033[0;" << mColors[debugLevel] << "m"; + out2.write(str, size); + out2 << "\033[0;" << Reset << "m"; + } + else + { + out2.write(str, size); + } + out2.flush(); + + return size; + } + + virtual ~Tee() = default; + + private: + + static bool useColoredOutput() + { + // Note: cmd.exe in Win10 should support ANSI colors, but in its own way. +#if defined(_WIN32) + return 0; +#else + char* term = getenv("TERM"); + bool useColor = term && !getenv("NO_COLOR") && isatty(fileno(stderr)); + + return useColor; +#endif + } + + std::ostream& out; + std::ostream& out2; + bool mUseColor; + + std::map mColors; + }; +#endif + } static std::unique_ptr rawStdout = nullptr; diff --git a/components/debug/debugging.hpp b/components/debug/debugging.hpp index 63c44ae8b4..8ac4bcd8ef 100644 --- a/components/debug/debugging.hpp +++ b/components/debug/debugging.hpp @@ -4,17 +4,10 @@ #include #include -#include #include -#include - #include "debuglog.hpp" -#if defined _WIN32 && defined _DEBUG -#include -#endif - namespace Debug { // ANSI colors for terminal @@ -26,116 +19,12 @@ namespace Debug Yellow = 93 }; - class DebugOutputBase : public boost::iostreams::sink - { - public: - DebugOutputBase() - { - if (CurrentDebugLevel == NoLevel) - fillCurrentDebugLevel(); - } - - virtual std::streamsize write(const char *str, std::streamsize size); - - virtual ~DebugOutputBase() = default; - - protected: - static Level getLevelMarker(const char *str); - - static void fillCurrentDebugLevel(); - - virtual std::streamsize writeImpl(const char *str, std::streamsize size, Level debugLevel) - { - return size; - } - }; - #ifdef _WIN32 bool attachParentConsole(); #endif -#if defined _WIN32 && defined _DEBUG - class DebugOutput : public DebugOutputBase - { - public: - std::streamsize writeImpl(const char *str, std::streamsize size, Level debugLevel) - { - // Make a copy for null termination - std::string tmp (str, static_cast(size)); - // Write string to Visual Studio Debug output - OutputDebugString (tmp.c_str ()); - return size; - } - - virtual ~DebugOutput() {} - }; -#else - - class Tee : public DebugOutputBase - { - public: - Tee(std::ostream &stream, std::ostream &stream2) - : out(stream), out2(stream2) - { - // TODO: check which stream is stderr? - mUseColor = useColoredOutput(); - - mColors[Error] = Red; - mColors[Warning] = Yellow; - mColors[Info] = Reset; - mColors[Verbose] = DarkGray; - mColors[Debug] = DarkGray; - mColors[NoLevel] = Reset; - } - - std::streamsize writeImpl(const char *str, std::streamsize size, Level debugLevel) override - { - out.write (str, size); - out.flush(); - - if(mUseColor) - { - out2 << "\033[0;" << mColors[debugLevel] << "m"; - out2.write (str, size); - out2 << "\033[0;" << Reset << "m"; - } - else - { - out2.write(str, size); - } - out2.flush(); - - return size; - } - - virtual ~Tee() {} - - private: - - static bool useColoredOutput() - { - // Note: cmd.exe in Win10 should support ANSI colors, but in its own way. -#if defined(_WIN32) - return 0; -#else - char *term = getenv("TERM"); - bool useColor = term && !getenv("NO_COLOR") && isatty(fileno(stderr)); - - return useColor; -#endif - } - - std::ostream &out; - std::ostream &out2; - bool mUseColor; - - std::map mColors; - }; -#endif - using LogListener = std::function; void setLogListener(LogListener); - } // Can be used to print messages without timestamps diff --git a/components/debug/debuglog.cpp b/components/debug/debuglog.cpp index 510c638614..f4f0fdffa6 100644 --- a/components/debug/debuglog.cpp +++ b/components/debug/debuglog.cpp @@ -1,8 +1,36 @@ #include "debuglog.hpp" +#include namespace Debug { Level CurrentDebugLevel = Level::NoLevel; } -std::mutex Log::sLock; +static std::mutex sLock; + +Log::Log(Debug::Level level) + : mShouldLog(level <= Debug::CurrentDebugLevel) +{ + // No need to hold the lock if there will be no logging anyway + if (!mShouldLog) + return; + + // Locks a global lock while the object is alive + sLock.lock(); + + // If the app has no logging system enabled, log level is not specified. + // Show all messages without marker - we just use the plain cout in this case. + if (Debug::CurrentDebugLevel == Debug::NoLevel) + return; + + std::cout << static_cast(level); +} + +Log::~Log() +{ + if (!mShouldLog) + return; + + std::cout << std::endl; + sLock.unlock(); +} diff --git a/components/debug/debuglog.hpp b/components/debug/debuglog.hpp index bcb282ab3a..aa8156e119 100644 --- a/components/debug/debuglog.hpp +++ b/components/debug/debuglog.hpp @@ -1,7 +1,6 @@ #ifndef DEBUG_LOG_H #define DEBUG_LOG_H -#include #include namespace Debug @@ -23,27 +22,9 @@ namespace Debug class Log { - static std::mutex sLock; - - std::unique_lock mLock; public: - explicit Log(Debug::Level level) - : mShouldLog(level <= Debug::CurrentDebugLevel) - { - // No need to hold the lock if there will be no logging anyway - if (!mShouldLog) - return; - - // Locks a global lock while the object is alive - mLock = lock(); - - // If the app has no logging system enabled, log level is not specified. - // Show all messages without marker - we just use the plain cout in this case. - if (Debug::CurrentDebugLevel == Debug::NoLevel) - return; - - std::cout << static_cast(level); - } + explicit Log(Debug::Level level); + ~Log(); // Perfect forwarding wrappers to give the chain of objects to cout template @@ -55,14 +36,6 @@ public: return *this; } - ~Log() - { - if (mShouldLog) - std::cout << std::endl; - } - - static std::unique_lock lock() { return std::unique_lock(sLock); } - private: const bool mShouldLog; };