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