| 1 | #include "assertion_logger.h" |
| 2 | |
| 3 | #include <base/lock.h> |
| 4 | #include <base/logger.h> |
| 5 | #include <base/system.h> |
| 6 | |
| 7 | #include <engine/shared/ringbuffer.h> |
| 8 | #include <engine/storage.h> |
| 9 | |
| 10 | class CAssertionLogger : public ILogger |
| 11 | { |
| 12 | struct SDebugMessageItem |
| 13 | { |
| 14 | char m_aMessage[1024]; |
| 15 | }; |
| 16 | |
| 17 | CLock m_DbgMessageMutex; |
| 18 | CStaticRingBuffer<SDebugMessageItem, sizeof(SDebugMessageItem) * 64, CRingBufferBase::FLAG_RECYCLE> m_DbgMessages; |
| 19 | |
| 20 | char m_aAssertLogPath[IO_MAX_PATH_LENGTH]; |
| 21 | char m_aGameName[256]; |
| 22 | |
| 23 | void Dump() REQUIRES(!m_DbgMessageMutex); |
| 24 | |
| 25 | public: |
| 26 | CAssertionLogger(const char *pAssertLogPath, const char *pGameName); |
| 27 | void Log(const CLogMessage *pMessage) override REQUIRES(!m_DbgMessageMutex); |
| 28 | void GlobalFinish() override REQUIRES(!m_DbgMessageMutex); |
| 29 | }; |
| 30 | |
| 31 | void CAssertionLogger::Log(const CLogMessage *pMessage) |
| 32 | { |
| 33 | if(m_Filter.Filters(pMessage)) |
| 34 | { |
| 35 | return; |
| 36 | } |
| 37 | const CLockScope LockScope(m_DbgMessageMutex); |
| 38 | SDebugMessageItem *pMsgItem = (SDebugMessageItem *)m_DbgMessages.Allocate(Size: sizeof(SDebugMessageItem)); |
| 39 | str_copy(dst&: pMsgItem->m_aMessage, src: pMessage->m_aLine); |
| 40 | } |
| 41 | |
| 42 | void CAssertionLogger::GlobalFinish() |
| 43 | { |
| 44 | if(dbg_assert_has_failed()) |
| 45 | { |
| 46 | Dump(); |
| 47 | } |
| 48 | } |
| 49 | |
| 50 | void CAssertionLogger::Dump() |
| 51 | { |
| 52 | char aAssertLogFile[IO_MAX_PATH_LENGTH]; |
| 53 | char aDate[64]; |
| 54 | str_timestamp(buffer: aDate, buffer_size: sizeof(aDate)); |
| 55 | str_format(buffer: aAssertLogFile, buffer_size: std::size(aAssertLogFile), format: "%s%s_assert_log_%s_%d.txt" , m_aAssertLogPath, m_aGameName, aDate, pid()); |
| 56 | const CLockScope LockScope(m_DbgMessageMutex); |
| 57 | IOHANDLE FileHandle = io_open(filename: aAssertLogFile, flags: IOFLAG_WRITE); |
| 58 | if(FileHandle) |
| 59 | { |
| 60 | auto *pIt = m_DbgMessages.First(); |
| 61 | while(pIt) |
| 62 | { |
| 63 | io_write(io: FileHandle, buffer: pIt->m_aMessage, size: str_length(str: pIt->m_aMessage)); |
| 64 | io_write(io: FileHandle, buffer: "\n" , size: 1); |
| 65 | |
| 66 | pIt = m_DbgMessages.Next(pCurrent: pIt); |
| 67 | } |
| 68 | |
| 69 | io_sync(io: FileHandle); |
| 70 | io_close(io: FileHandle); |
| 71 | } |
| 72 | } |
| 73 | |
| 74 | CAssertionLogger::CAssertionLogger(const char *pAssertLogPath, const char *pGameName) |
| 75 | { |
| 76 | str_copy(dst&: m_aAssertLogPath, src: pAssertLogPath); |
| 77 | str_copy(dst&: m_aGameName, src: pGameName); |
| 78 | } |
| 79 | |
| 80 | std::unique_ptr<ILogger> CreateAssertionLogger(IStorage *pStorage, const char *pGameName) |
| 81 | { |
| 82 | char aAssertLogPath[IO_MAX_PATH_LENGTH]; |
| 83 | pStorage->GetCompletePath(Type: IStorage::TYPE_SAVE, pDir: "dumps/" , pBuffer: aAssertLogPath, BufferSize: sizeof(aAssertLogPath)); |
| 84 | return std::make_unique<CAssertionLogger>(args&: aAssertLogPath, args&: pGameName); |
| 85 | } |
| 86 | |