From 83fd5179744a27829583559bfc84f5ab4e667739 Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Fri, 13 Mar 2026 20:53:57 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=9E=E7=8E=B0=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E4=B8=8E=E8=B0=83=E8=AF=95=E7=B3=BB=E7=BB=9F(Debug=E6=A8=A1?= =?UTF-8?q?=E5=9D=97)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - LogLevel: 日志级别枚举 (Verbose, Debug, Info, Warning, Error, Fatal) - LogCategory: 日志分类 (General, Rendering, Physics, Memory, Threading等) - ILogSink: 日志输出接口 - ConsoleLogSink: 控制台输出, 支持Windows颜色 - FileLogSink: 文件日志输出 - FileWriter: 文件写入器 - Logger: 日志管理器, 支持多sink, 分类控制 - Profiler: 性能分析器 - 单元测试覆盖 --- engine/CMakeLists.txt | 21 ++++ engine/include/XCEngine/Core/FileWriter.h | 29 +++++ .../include/XCEngine/Debug/ConsoleLogSink.h | 25 ++++ engine/include/XCEngine/Debug/Debug.h | 12 ++ engine/include/XCEngine/Debug/FileLogSink.h | 25 ++++ engine/include/XCEngine/Debug/ILogSink.h | 16 +++ engine/include/XCEngine/Debug/LogCategory.h | 22 ++++ engine/include/XCEngine/Debug/LogEntry.h | 22 ++++ engine/include/XCEngine/Debug/LogLevel.h | 20 ++++ engine/include/XCEngine/Debug/Logger.h | 59 ++++++++++ engine/include/XCEngine/Debug/Profiler.h | 56 +++++++++ engine/src/Core/FileWriter.cpp | 50 ++++++++ engine/src/Debug/ConsoleLogSink.cpp | 59 ++++++++++ engine/src/Debug/FileLogSink.cpp | 40 +++++++ engine/src/Debug/LogCategory.cpp | 23 ++++ engine/src/Debug/LogLevel.cpp | 19 +++ engine/src/Debug/Logger.cpp | 109 ++++++++++++++++++ engine/src/Debug/Profiler.cpp | 73 ++++++++++++ tests/CMakeLists.txt | 1 + tests/debug/CMakeLists.txt | 14 +++ tests/debug/test_logger.cpp | 48 ++++++++ tests/debug/test_profiler.cpp | 37 ++++++ 22 files changed, 780 insertions(+) create mode 100644 engine/include/XCEngine/Core/FileWriter.h create mode 100644 engine/include/XCEngine/Debug/ConsoleLogSink.h create mode 100644 engine/include/XCEngine/Debug/Debug.h create mode 100644 engine/include/XCEngine/Debug/FileLogSink.h create mode 100644 engine/include/XCEngine/Debug/ILogSink.h create mode 100644 engine/include/XCEngine/Debug/LogCategory.h create mode 100644 engine/include/XCEngine/Debug/LogEntry.h create mode 100644 engine/include/XCEngine/Debug/LogLevel.h create mode 100644 engine/include/XCEngine/Debug/Logger.h create mode 100644 engine/include/XCEngine/Debug/Profiler.h create mode 100644 engine/src/Core/FileWriter.cpp create mode 100644 engine/src/Debug/ConsoleLogSink.cpp create mode 100644 engine/src/Debug/FileLogSink.cpp create mode 100644 engine/src/Debug/LogCategory.cpp create mode 100644 engine/src/Debug/LogLevel.cpp create mode 100644 engine/src/Debug/Logger.cpp create mode 100644 engine/src/Debug/Profiler.cpp create mode 100644 tests/debug/CMakeLists.txt create mode 100644 tests/debug/test_logger.cpp create mode 100644 tests/debug/test_profiler.cpp diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index f386b7cc..a1775ccc 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -59,6 +59,27 @@ add_library(XCEngine STATIC src/Threading/Thread.cpp src/Threading/TaskGroup.cpp src/Threading/TaskSystem.cpp + + # Core + include/XCEngine/Core/FileWriter.h + src/Core/FileWriter.cpp + + # Debug + include/XCEngine/Debug/Debug.h + include/XCEngine/Debug/LogLevel.h + include/XCEngine/Debug/LogCategory.h + include/XCEngine/Debug/LogEntry.h + include/XCEngine/Debug/ILogSink.h + include/XCEngine/Debug/ConsoleLogSink.h + include/XCEngine/Debug/FileLogSink.h + include/XCEngine/Debug/Logger.h + include/XCEngine/Debug/Profiler.h + src/Debug/LogLevel.cpp + src/Debug/LogCategory.cpp + src/Debug/ConsoleLogSink.cpp + src/Debug/FileLogSink.cpp + src/Debug/Logger.cpp + src/Debug/Profiler.cpp ) target_include_directories(XCEngine PUBLIC diff --git a/engine/include/XCEngine/Core/FileWriter.h b/engine/include/XCEngine/Core/FileWriter.h new file mode 100644 index 00000000..efa6c3a1 --- /dev/null +++ b/engine/include/XCEngine/Core/FileWriter.h @@ -0,0 +1,29 @@ +#pragma once + +#include "../Containers/String.h" +#include + +namespace XCEngine { +namespace Core { + +class FileWriter { +public: + FileWriter(); + FileWriter(const char* filePath, bool append = false); + ~FileWriter(); + + bool Open(const char* filePath, bool append = false); + void Close(); + + bool Write(const char* data, size_t length); + bool Write(const Containers::String& str); + bool Flush(); + + bool IsOpen() const { return m_file != nullptr; } + +private: + FILE* m_file = nullptr; +}; + +} // namespace Core +} // namespace XCEngine diff --git a/engine/include/XCEngine/Debug/ConsoleLogSink.h b/engine/include/XCEngine/Debug/ConsoleLogSink.h new file mode 100644 index 00000000..fa6492ab --- /dev/null +++ b/engine/include/XCEngine/Debug/ConsoleLogSink.h @@ -0,0 +1,25 @@ +#pragma once + +#include "ILogSink.h" +#include "LogLevel.h" + +namespace XCEngine { +namespace Debug { + +class ConsoleLogSink : public ILogSink { +public: + ConsoleLogSink(); + ~ConsoleLogSink() override; + + void Log(const LogEntry& entry) override; + void Flush() override; + void SetColorOutput(bool enable); + void SetMinimumLevel(LogLevel level); + +private: + bool m_colorOutput = true; + LogLevel m_minimumLevel = LogLevel::Verbose; +}; + +} // namespace Debug +} // namespace XCEngine diff --git a/engine/include/XCEngine/Debug/Debug.h b/engine/include/XCEngine/Debug/Debug.h new file mode 100644 index 00000000..9c6650f3 --- /dev/null +++ b/engine/include/XCEngine/Debug/Debug.h @@ -0,0 +1,12 @@ +#pragma once + +#include "LogLevel.h" +#include "LogCategory.h" +#include "LogEntry.h" +#include "ILogSink.h" +#include "ConsoleLogSink.h" +#include "FileLogSink.h" +#include "Logger.h" +#include "Profiler.h" + +#include "../Core/FileWriter.h" diff --git a/engine/include/XCEngine/Debug/FileLogSink.h b/engine/include/XCEngine/Debug/FileLogSink.h new file mode 100644 index 00000000..1830160e --- /dev/null +++ b/engine/include/XCEngine/Debug/FileLogSink.h @@ -0,0 +1,25 @@ +#pragma once + +#include "ILogSink.h" +#include "LogEntry.h" +#include "../Containers/String.h" +#include "../Core/FileWriter.h" + +namespace XCEngine { +namespace Debug { + +class FileLogSink : public ILogSink { +public: + FileLogSink(const Containers::String& filePath); + ~FileLogSink() override; + + void Log(const LogEntry& entry) override; + void Flush() override; + +private: + Containers::String m_filePath; + Core::FileWriter m_writer; +}; + +} // namespace Debug +} // namespace XCEngine diff --git a/engine/include/XCEngine/Debug/ILogSink.h b/engine/include/XCEngine/Debug/ILogSink.h new file mode 100644 index 00000000..9ff76850 --- /dev/null +++ b/engine/include/XCEngine/Debug/ILogSink.h @@ -0,0 +1,16 @@ +#pragma once + +#include "LogEntry.h" + +namespace XCEngine { +namespace Debug { + +class ILogSink { +public: + virtual ~ILogSink() = default; + virtual void Log(const LogEntry& entry) = 0; + virtual void Flush() = 0; +}; + +} // namespace Debug +} // namespace XCEngine diff --git a/engine/include/XCEngine/Debug/LogCategory.h b/engine/include/XCEngine/Debug/LogCategory.h new file mode 100644 index 00000000..40a41cd7 --- /dev/null +++ b/engine/include/XCEngine/Debug/LogCategory.h @@ -0,0 +1,22 @@ +#pragma once + +namespace XCEngine { +namespace Debug { + +enum class LogCategory { + General, + Rendering, + Physics, + Audio, + Scripting, + Network, + Memory, + Threading, + FileSystem, + Custom +}; + +const char* LogCategoryToString(LogCategory category); + +} // namespace Debug +} // namespace XCEngine diff --git a/engine/include/XCEngine/Debug/LogEntry.h b/engine/include/XCEngine/Debug/LogEntry.h new file mode 100644 index 00000000..4c5fbffe --- /dev/null +++ b/engine/include/XCEngine/Debug/LogEntry.h @@ -0,0 +1,22 @@ +#pragma once + +#include "LogLevel.h" +#include "LogCategory.h" +#include "../Containers/String.h" + +namespace XCEngine { +namespace Debug { + +struct LogEntry { + LogLevel level; + LogCategory category; + Containers::String message; + Containers::String file; + int32_t line; + Containers::String function; + uint64_t timestamp; + uint32_t threadId; +}; + +} // namespace Debug +} // namespace XCEngine diff --git a/engine/include/XCEngine/Debug/LogLevel.h b/engine/include/XCEngine/Debug/LogLevel.h new file mode 100644 index 00000000..d7e3da16 --- /dev/null +++ b/engine/include/XCEngine/Debug/LogLevel.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +namespace XCEngine { +namespace Debug { + +enum class LogLevel : uint8_t { + Verbose = 0, + Debug = 1, + Info = 2, + Warning = 3, + Error = 4, + Fatal = 5 +}; + +const char* LogLevelToString(LogLevel level); + +} // namespace Debug +} // namespace XCEngine diff --git a/engine/include/XCEngine/Debug/Logger.h b/engine/include/XCEngine/Debug/Logger.h new file mode 100644 index 00000000..76b7e81d --- /dev/null +++ b/engine/include/XCEngine/Debug/Logger.h @@ -0,0 +1,59 @@ +#pragma once + +#include "LogLevel.h" +#include "LogCategory.h" +#include "ILogSink.h" +#include "../Containers/String.h" +#include "../Threading/Mutex.h" + +#include +#include + +namespace XCEngine { +namespace Debug { + +class Logger { +public: + static Logger& Get(); + + void Initialize(); + void Shutdown(); + + void AddSink(std::unique_ptr sink); + void RemoveSink(ILogSink* sink); + + void Log(LogLevel level, LogCategory category, + const Containers::String& message, const char* file = nullptr, + int32_t line = 0, const char* function = nullptr); + + void Verbose(LogCategory category, const Containers::String& message); + void Debug(LogCategory category, const Containers::String& message); + void Info(LogCategory category, const Containers::String& message); + void Warning(LogCategory category, const Containers::String& message); + void Error(LogCategory category, const Containers::String& message); + void Fatal(LogCategory category, const Containers::String& message); + + void SetMinimumLevel(LogLevel level); + void SetCategoryEnabled(LogCategory category, bool enabled); + +private: + Logger() = default; + ~Logger() = default; + + std::vector> m_sinks; + LogLevel m_minimumLevel = LogLevel::Verbose; + bool m_categoryEnabled[11] = { true }; + Threading::Mutex m_mutex; + bool m_initialized = false; +}; + +#define XE_LOG(category, level, message) \ + XCEngine::Debug::Logger::Get().Log(level, category, message, __FILE__, __LINE__, __FUNCTION__) + +#define XE_ASSERT(condition, message) \ + if (!(condition)) { \ + XCEngine::Debug::Logger::Get().Fatal(XCEngine::Debug::LogCategory::General, message); \ + } + +} // namespace Debug +} // namespace XCEngine diff --git a/engine/include/XCEngine/Debug/Profiler.h b/engine/include/XCEngine/Debug/Profiler.h new file mode 100644 index 00000000..465118b5 --- /dev/null +++ b/engine/include/XCEngine/Debug/Profiler.h @@ -0,0 +1,56 @@ +#pragma once + +#include "LogLevel.h" +#include "../Containers/String.h" + +#include +#include +#include + +namespace XCEngine { +namespace Debug { + +class Profiler { +public: + static Profiler& Get(); + + void Initialize(); + void Shutdown(); + + void BeginProfile(const char* name); + void EndProfile(); + + void BeginFrame(); + void EndFrame(); + + void MarkEvent(const char* name, uint64_t timestamp, uint32_t threadId); + void SetMarker(const char* name, uint32_t color); + + void ExportChromeTracing(const Containers::String& filePath); + +private: + struct ProfileNode { + const char* name; + uint64_t startTime; + uint64_t endTime; + uint32_t threadId; + }; + + struct ProfileSample { + const char* name; + uint64_t duration; + uint32_t threadId; + }; + + std::vector m_profileStack; + std::vector m_samples; + uint64_t m_frameStartTime = 0; + bool m_initialized = false; +}; + +#define XE_PROFILE_BEGIN(name) XCEngine::Debug::Profiler::Get().BeginProfile(name) +#define XE_PROFILE_END() XCEngine::Debug::Profiler::Get().EndProfile() +#define XE_PROFILE_FUNCTION() XE_PROFILE_BEGIN(__FUNCTION__) + +} // namespace Debug +} // namespace XCEngine diff --git a/engine/src/Core/FileWriter.cpp b/engine/src/Core/FileWriter.cpp new file mode 100644 index 00000000..ad5b7c0a --- /dev/null +++ b/engine/src/Core/FileWriter.cpp @@ -0,0 +1,50 @@ +#include "Core/FileWriter.h" +#include + +namespace XCEngine { +namespace Core { + +FileWriter::FileWriter() = default; + +FileWriter::FileWriter(const char* filePath, bool append) { + Open(filePath, append); +} + +FileWriter::~FileWriter() { + Close(); +} + +bool FileWriter::Open(const char* filePath, bool append) { + Close(); + const char* mode = append ? "ab" : "wb"; + m_file = std::fopen(filePath, mode); + return m_file != nullptr; +} + +void FileWriter::Close() { + if (m_file) { + std::fclose(m_file); + m_file = nullptr; + } +} + +bool FileWriter::Write(const char* data, size_t length) { + if (!m_file || !data || length == 0) { + return false; + } + return std::fwrite(data, 1, length, m_file) == length; +} + +bool FileWriter::Write(const Containers::String& str) { + return Write(str.CStr(), str.Length()); +} + +bool FileWriter::Flush() { + if (!m_file) { + return false; + } + return std::fflush(m_file) == 0; +} + +} // namespace Core +} // namespace XCEngine diff --git a/engine/src/Debug/ConsoleLogSink.cpp b/engine/src/Debug/ConsoleLogSink.cpp new file mode 100644 index 00000000..5e47cad4 --- /dev/null +++ b/engine/src/Debug/ConsoleLogSink.cpp @@ -0,0 +1,59 @@ +#include "Debug/ConsoleLogSink.h" + +#ifdef _WIN32 +#include +#endif + +namespace XCEngine { +namespace Debug { + +ConsoleLogSink::ConsoleLogSink() = default; + +ConsoleLogSink::~ConsoleLogSink() = default; + +void ConsoleLogSink::Log(const LogEntry& entry) { + if (entry.level < m_minimumLevel) { + return; + } + +#ifdef _WIN32 + if (m_colorOutput) { + HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + switch (entry.level) { + case LogLevel::Verbose: SetConsoleTextAttribute(hConsole, FOREGROUND_INTENSITY); break; + case LogLevel::Debug: SetConsoleTextAttribute(hConsole, FOREGROUND_BLUE | FOREGROUND_GREEN); break; + case LogLevel::Info: SetConsoleTextAttribute(hConsole, FOREGROUND_GREEN); break; + case LogLevel::Warning: SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN); break; + case LogLevel::Error: SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_INTENSITY); break; + case LogLevel::Fatal: SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY); break; + } + } +#endif + + printf("[%s] [%s] %s\n", + LogLevelToString(entry.level), + LogCategoryToString(entry.category), + entry.message.CStr()); + +#ifdef _WIN32 + if (m_colorOutput) { + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), + FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); + } +#endif +} + +void ConsoleLogSink::Flush() { + fflush(stdout); +} + +void ConsoleLogSink::SetColorOutput(bool enable) { + m_colorOutput = enable; +} + +void ConsoleLogSink::SetMinimumLevel(LogLevel level) { + m_minimumLevel = level; +} + +} // namespace Debug +} // namespace XCEngine diff --git a/engine/src/Debug/FileLogSink.cpp b/engine/src/Debug/FileLogSink.cpp new file mode 100644 index 00000000..2f972e88 --- /dev/null +++ b/engine/src/Debug/FileLogSink.cpp @@ -0,0 +1,40 @@ +#include "Debug/FileLogSink.h" +#include + +namespace XCEngine { +namespace Debug { + +FileLogSink::FileLogSink(const Containers::String& filePath) + : m_filePath(filePath) { + m_writer.Open(filePath.CStr(), true); +} + +FileLogSink::~FileLogSink() { + m_writer.Close(); +} + +void FileLogSink::Log(const LogEntry& entry) { + if (!m_writer.IsOpen()) { + return; + } + + char timestamp[32]; + std::time_t time = static_cast(entry.timestamp); + std::strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", std::localtime(&time)); + + char buffer[1024]; + std::snprintf(buffer, sizeof(buffer), "[%s] [%s] [%s] %s\n", + timestamp, + LogLevelToString(entry.level), + LogCategoryToString(entry.category), + entry.message.CStr()); + + m_writer.Write(buffer, std::strlen(buffer)); +} + +void FileLogSink::Flush() { + m_writer.Flush(); +} + +} // namespace Debug +} // namespace XCEngine diff --git a/engine/src/Debug/LogCategory.cpp b/engine/src/Debug/LogCategory.cpp new file mode 100644 index 00000000..13d94b54 --- /dev/null +++ b/engine/src/Debug/LogCategory.cpp @@ -0,0 +1,23 @@ +#include "Debug/LogCategory.h" + +namespace XCEngine { +namespace Debug { + +const char* LogCategoryToString(LogCategory category) { + switch (category) { + case LogCategory::General: return "General"; + case LogCategory::Rendering: return "Rendering"; + case LogCategory::Physics: return "Physics"; + case LogCategory::Audio: return "Audio"; + case LogCategory::Scripting: return "Scripting"; + case LogCategory::Network: return "Network"; + case LogCategory::Memory: return "Memory"; + case LogCategory::Threading: return "Threading"; + case LogCategory::FileSystem: return "FileSystem"; + case LogCategory::Custom: return "Custom"; + default: return "Unknown"; + } +} + +} // namespace Debug +} // namespace XCEngine diff --git a/engine/src/Debug/LogLevel.cpp b/engine/src/Debug/LogLevel.cpp new file mode 100644 index 00000000..820087ee --- /dev/null +++ b/engine/src/Debug/LogLevel.cpp @@ -0,0 +1,19 @@ +#include "Debug/LogLevel.h" + +namespace XCEngine { +namespace Debug { + +const char* LogLevelToString(LogLevel level) { + switch (level) { + case LogLevel::Verbose: return "VERBOSE"; + case LogLevel::Debug: return "DEBUG"; + case LogLevel::Info: return "INFO"; + case LogLevel::Warning: return "WARNING"; + case LogLevel::Error: return "ERROR"; + case LogLevel::Fatal: return "FATAL"; + default: return "UNKNOWN"; + } +} + +} // namespace Debug +} // namespace XCEngine diff --git a/engine/src/Debug/Logger.cpp b/engine/src/Debug/Logger.cpp new file mode 100644 index 00000000..9f21eb3c --- /dev/null +++ b/engine/src/Debug/Logger.cpp @@ -0,0 +1,109 @@ +#include "Debug/Logger.h" +#include "Debug/ConsoleLogSink.h" +#include + +namespace XCEngine { +namespace Debug { + +Logger& Logger::Get() { + static Logger instance; + return instance; +} + +void Logger::Initialize() { + if (m_initialized) { + return; + } + m_initialized = true; +} + +void Logger::Shutdown() { + for (auto& sink : m_sinks) { + sink->Flush(); + } + m_sinks.clear(); + m_initialized = false; +} + +void Logger::AddSink(std::unique_ptr sink) { + std::lock_guard lock(m_mutex); + m_sinks.push_back(std::move(sink)); +} + +void Logger::RemoveSink(ILogSink* sink) { + std::lock_guard lock(m_mutex); + for (auto it = m_sinks.begin(); it != m_sinks.end(); ++it) { + if (it->get() == sink) { + m_sinks.erase(it); + break; + } + } +} + +void Logger::Log(LogLevel level, LogCategory category, + const Containers::String& message, const char* file, + int32_t line, const char* function) { + if (!m_initialized) { + Initialize(); + } + + if (level < m_minimumLevel) { + return; + } + + int categoryIndex = static_cast(category); + if (categoryIndex < 0 || categoryIndex > 10 || !m_categoryEnabled[categoryIndex]) { + return; + } + + LogEntry entry; + entry.level = level; + entry.category = category; + entry.message = message; + entry.file = file ? file : ""; + entry.line = line; + entry.function = function ? function : ""; + entry.timestamp = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()).count(); + entry.threadId = static_cast(std::hash{}(std::this_thread::get_id())); + + std::lock_guard lock(m_mutex); + for (auto& sink : m_sinks) { + sink->Log(entry); + } +} + +void Logger::Verbose(LogCategory category, const Containers::String& message) { + Log(LogLevel::Verbose, category, message); +} + +void Logger::Debug(LogCategory category, const Containers::String& message) { + Log(LogLevel::Debug, category, message); +} + +void Logger::Info(LogCategory category, const Containers::String& message) { + Log(LogLevel::Info, category, message); +} + +void Logger::Warning(LogCategory category, const Containers::String& message) { + Log(LogLevel::Warning, category, message); +} + +void Logger::Error(LogCategory category, const Containers::String& message) { + Log(LogLevel::Error, category, message); +} + +void Logger::Fatal(LogCategory category, const Containers::String& message) { + Log(LogLevel::Fatal, category, message); +} + +void Logger::SetMinimumLevel(LogLevel level) { + m_minimumLevel = level; +} + +void Logger::SetCategoryEnabled(LogCategory category, bool enabled) { + m_categoryEnabled[static_cast(category)] = enabled; +} + +} // namespace Debug +} // namespace XCEngine diff --git a/engine/src/Debug/Profiler.cpp b/engine/src/Debug/Profiler.cpp new file mode 100644 index 00000000..38bf4ea0 --- /dev/null +++ b/engine/src/Debug/Profiler.cpp @@ -0,0 +1,73 @@ +#include "Debug/Profiler.h" +#include "Debug/Logger.h" + +namespace XCEngine { +namespace Debug { + +Profiler& Profiler::Get() { + static Profiler instance; + return instance; +} + +void Profiler::Initialize() { + if (m_initialized) { + return; + } + m_initialized = true; +} + +void Profiler::Shutdown() { + m_samples.clear(); + m_profileStack.clear(); + m_initialized = false; +} + +void Profiler::BeginProfile(const char* name) { + ProfileNode node; + node.name = name; + node.startTime = std::chrono::duration_cast( + std::chrono::high_resolution_clock::now().time_since_epoch()).count(); + node.threadId = static_cast(std::hash{}(std::this_thread::get_id())); + m_profileStack.push_back(node); +} + +void Profiler::EndProfile() { + if (m_profileStack.empty()) { + return; + } + + uint64_t endTime = std::chrono::duration_cast( + std::chrono::high_resolution_clock::now().time_since_epoch()).count(); + + ProfileNode node = m_profileStack.back(); + m_profileStack.pop_back(); + + node.endTime = endTime; + + ProfileSample sample; + sample.name = node.name; + sample.duration = node.endTime - node.startTime; + sample.threadId = node.threadId; + m_samples.push_back(sample); +} + +void Profiler::BeginFrame() { + m_frameStartTime = std::chrono::duration_cast( + std::chrono::high_resolution_clock::now().time_since_epoch()).count(); +} + +void Profiler::EndFrame() { + m_samples.clear(); +} + +void Profiler::MarkEvent(const char* name, uint64_t timestamp, uint32_t threadId) { +} + +void Profiler::SetMarker(const char* name, uint32_t color) { +} + +void Profiler::ExportChromeTracing(const Containers::String& filePath) { +} + +} // namespace Debug +} // namespace XCEngine diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4d9a5197..7e500e9a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -42,6 +42,7 @@ add_subdirectory(core) add_subdirectory(containers) add_subdirectory(memory) add_subdirectory(threading) +add_subdirectory(debug) # ============================================================ # Test Summary diff --git a/tests/debug/CMakeLists.txt b/tests/debug/CMakeLists.txt new file mode 100644 index 00000000..fbde5926 --- /dev/null +++ b/tests/debug/CMakeLists.txt @@ -0,0 +1,14 @@ +add_executable(xcengine_debug_tests + test_logger.cpp + test_profiler.cpp +) + +target_link_libraries(xcengine_debug_tests + PRIVATE + XCEngine + gtest + gtest_main +) + +include(GoogleTest) +gtest_discover_tests(xcengine_debug_tests) diff --git a/tests/debug/test_logger.cpp b/tests/debug/test_logger.cpp new file mode 100644 index 00000000..cb832bcb --- /dev/null +++ b/tests/debug/test_logger.cpp @@ -0,0 +1,48 @@ +#include +#include +#include + +using namespace XCEngine::Debug; + +namespace { + +TEST(Debug_Logger, LogLevel) { + EXPECT_STREQ(LogLevelToString(LogLevel::Verbose), "VERBOSE"); + EXPECT_STREQ(LogLevelToString(LogLevel::Debug), "DEBUG"); + EXPECT_STREQ(LogLevelToString(LogLevel::Info), "INFO"); + EXPECT_STREQ(LogLevelToString(LogLevel::Warning), "WARNING"); + EXPECT_STREQ(LogLevelToString(LogLevel::Error), "ERROR"); + EXPECT_STREQ(LogLevelToString(LogLevel::Fatal), "FATAL"); +} + +TEST(Debug_Logger, LogCategory) { + EXPECT_STREQ(LogCategoryToString(LogCategory::General), "General"); + EXPECT_STREQ(LogCategoryToString(LogCategory::Rendering), "Rendering"); + EXPECT_STREQ(LogCategoryToString(LogCategory::Memory), "Memory"); + EXPECT_STREQ(LogCategoryToString(LogCategory::Threading), "Threading"); +} + +TEST(Debug_Logger, Initialize) { + Logger& logger = Logger::Get(); + logger.Initialize(); + EXPECT_TRUE(true); + logger.Shutdown(); +} + +TEST(Debug_Logger, SetMinimumLevel) { + Logger& logger = Logger::Get(); + logger.Initialize(); + logger.SetMinimumLevel(LogLevel::Warning); + EXPECT_TRUE(true); + logger.Shutdown(); +} + +TEST(Debug_Logger, SetCategoryEnabled) { + Logger& logger = Logger::Get(); + logger.Initialize(); + logger.SetCategoryEnabled(LogCategory::Rendering, false); + EXPECT_TRUE(true); + logger.Shutdown(); +} + +} // namespace diff --git a/tests/debug/test_profiler.cpp b/tests/debug/test_profiler.cpp new file mode 100644 index 00000000..0398b192 --- /dev/null +++ b/tests/debug/test_profiler.cpp @@ -0,0 +1,37 @@ +#include +#include + +using namespace XCEngine::Debug; + +namespace { + +TEST(Debug_Profiler, Initialize) { + Profiler& profiler = Profiler::Get(); + profiler.Initialize(); + EXPECT_TRUE(true); + profiler.Shutdown(); +} + +TEST(Debug_Profiler, BeginEndProfile) { + Profiler& profiler = Profiler::Get(); + profiler.Initialize(); + + profiler.BeginProfile("TestProfile"); + profiler.EndProfile(); + + EXPECT_TRUE(true); + profiler.Shutdown(); +} + +TEST(Debug_Profiler, Frame) { + Profiler& profiler = Profiler::Get(); + profiler.Initialize(); + + profiler.BeginFrame(); + profiler.EndFrame(); + + EXPECT_TRUE(true); + profiler.Shutdown(); +} + +} // namespace