diff --git a/CMakeLists.txt b/CMakeLists.txt index e5a7ebe7c..eddf5bb56 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1579,6 +1579,8 @@ set_src(ENGINE_INTERFACE GLOB src/engine warning.h ) set_src(ENGINE_SHARED GLOB src/engine/shared + assertion_logger.cpp + assertion_logger.h compression.cpp compression.h config.cpp diff --git a/src/base/system.cpp b/src/base/system.cpp index e0b7651fb..56e7fe41b 100644 --- a/src/base/system.cpp +++ b/src/base/system.cpp @@ -132,12 +132,13 @@ IOHANDLE io_current_exe() #endif } -typedef struct +struct DBG_LOGGER_DATA { DBG_LOGGER logger; DBG_LOGGER_FINISH finish; + DBG_LOGGER_ASSERTION on_assert = nullptr; void *user; -} DBG_LOGGER_DATA; +}; static DBG_LOGGER_DATA loggers[16]; static int has_stdout_logger = 0; @@ -181,11 +182,21 @@ static NETSOCKET_INTERNAL invalid_socket = {NETTYPE_INVALID, -1, -1, -1}; #define AF_WEBSOCKET_INET (0xee) +static void dbg_assert_notify_loggers() +{ + for(int i = 0; i < num_loggers; i++) + { + if(loggers[i].on_assert) + loggers[i].on_assert(loggers[i].user); + } +} + void dbg_assert_imp(const char *filename, int line, int test, const char *msg) { if(!test) { dbg_msg("assert", "%s(%d): %s", filename, line, msg); + dbg_assert_notify_loggers(); dbg_break(); } } @@ -337,6 +348,13 @@ void dbg_logger(DBG_LOGGER logger, DBG_LOGGER_FINISH finish, void *user) num_loggers++; } +void dbg_logger_assertion(DBG_LOGGER logger, DBG_LOGGER_FINISH finish, DBG_LOGGER_ASSERTION on_assert, void *user) +{ + dbg_logger(logger, finish, user); + + loggers[num_loggers - 1].on_assert = on_assert; +} + void dbg_logger_stdout() { #if defined(CONF_FAMILY_WINDOWS) diff --git a/src/base/system.h b/src/base/system.h index af2ed2f66..9ff16fb52 100644 --- a/src/base/system.h +++ b/src/base/system.h @@ -1849,6 +1849,9 @@ typedef void (*DBG_LOGGER)(const char *line, void *user); typedef void (*DBG_LOGGER_FINISH)(void *user); void dbg_logger(DBG_LOGGER logger, DBG_LOGGER_FINISH finish, void *user); +typedef void (*DBG_LOGGER_ASSERTION)(void *user); +void dbg_logger_assertion(DBG_LOGGER logger, DBG_LOGGER_FINISH finish, DBG_LOGGER_ASSERTION on_assert, void *user); + void dbg_logger_stdout(); void dbg_logger_debugger(); void dbg_logger_file(const char *filename); diff --git a/src/engine/shared/assertion_logger.cpp b/src/engine/shared/assertion_logger.cpp new file mode 100644 index 000000000..7077bbcc3 --- /dev/null +++ b/src/engine/shared/assertion_logger.cpp @@ -0,0 +1,54 @@ +#include "assertion_logger.h" + +#include +#include + +void CAssertionLogger::DbgLogger(const char *pLine, void *pUser) +{ + ((CAssertionLogger *)pUser)->DbgLogger(pLine); +} + +void CAssertionLogger::DbgLoggerAssertion(void *pUser) +{ + ((CAssertionLogger *)pUser)->DbgLoggerAssertion(); +} + +void CAssertionLogger::DbgLogger(const char *pLine) +{ + std::unique_lock Lock(m_DbgMessageMutex); + + SDebugMessageItem *pMsgItem = (SDebugMessageItem *)m_DbgMessages.Allocate(sizeof(SDebugMessageItem)); + str_copy(pMsgItem->m_aMessage, pLine, std::size(pMsgItem->m_aMessage)); +} + +void CAssertionLogger::DbgLoggerAssertion() +{ + char aAssertLogFile[IO_MAX_PATH_LENGTH]; + char aDate[64]; + str_timestamp(aDate, sizeof(aDate)); + str_format(aAssertLogFile, std::size(aAssertLogFile), "%s%s_assert_log_%d_%s.txt", m_aAssertLogPath, m_aGameName, pid(), aDate); + std::unique_lock Lock(m_DbgMessageMutex); + IOHANDLE FileHandle = io_open(aAssertLogFile, IOFLAG_WRITE); + if(FileHandle) + { + auto *pIt = m_DbgMessages.First(); + while(pIt) + { + io_write(FileHandle, pIt->m_aMessage, str_length(pIt->m_aMessage)); + io_write(FileHandle, "\n", 1); + + pIt = m_DbgMessages.Next(pIt); + } + + io_sync(FileHandle); + io_close(FileHandle); + } +} + +void CAssertionLogger::Init(const char *pAssertLogPath, const char *pGameName) +{ + str_copy(m_aAssertLogPath, pAssertLogPath, std::size(m_aAssertLogPath)); + str_copy(m_aGameName, pGameName, std::size(m_aGameName)); + + dbg_logger_assertion(DbgLogger, nullptr, DbgLoggerAssertion, this); +} diff --git a/src/engine/shared/assertion_logger.h b/src/engine/shared/assertion_logger.h new file mode 100644 index 000000000..72c1f68dd --- /dev/null +++ b/src/engine/shared/assertion_logger.h @@ -0,0 +1,36 @@ +#ifndef ENGINE_SHARED_ASSERTION_LOGGER_H +#define ENGINE_SHARED_ASSERTION_LOGGER_H + +#include + +#include +#include + +#include + +class CAssertionLogger +{ + static void DbgLogger(const char *pLine, void *pUser); + static void DbgLoggerAssertion(void *pUser); + + static constexpr size_t s_MaxDbgMessageCount = 64; + + void DbgLogger(const char *pLine); + void DbgLoggerAssertion(); + + struct SDebugMessageItem + { + char m_aMessage[1024]; + }; + + std::mutex m_DbgMessageMutex; + CStaticRingBuffer m_DbgMessages; + + char m_aAssertLogPath[IO_MAX_PATH_LENGTH]; + char m_aGameName[256]; + +public: + void Init(const char *pAssertLogPath, const char *pGameName); +}; + +#endif diff --git a/src/engine/shared/engine.cpp b/src/engine/shared/engine.cpp index 0bfeed0d3..17f768d59 100644 --- a/src/engine/shared/engine.cpp +++ b/src/engine/shared/engine.cpp @@ -9,6 +9,8 @@ #include #include +#include + CHostLookup::CHostLookup() = default; CHostLookup::CHostLookup(const char *pHostname, int Nettype) @@ -29,6 +31,10 @@ public: IStorage *m_pStorage; bool m_Logging; + CAssertionLogger m_AssertionLogger; + + char m_aAppName[256]; + static void Con_DbgLognetwork(IConsole::IResult *pResult, void *pUserData) { CEngine *pEngine = static_cast(pUserData); @@ -53,6 +59,7 @@ public: CEngine(bool Test, const char *pAppname, bool Silent, int Jobs) { + str_copy(m_aAppName, pAppname, std::size(m_aAppName)); if(!Test) { if(!Silent) @@ -87,6 +94,10 @@ public: if(!m_pConsole || !m_pStorage) return; + char aFullPath[IO_MAX_PATH_LENGTH]; + m_pStorage->GetCompletePath(IStorage::TYPE_SAVE, "dumps/", aFullPath, sizeof(aFullPath)); + m_AssertionLogger.Init(aFullPath, m_aAppName); + m_pConsole->Register("dbg_lognetwork", "", CFGFLAG_SERVER | CFGFLAG_CLIENT, Con_DbgLognetwork, this, "Log the network"); }