Skip to content

Commit 7e85db6

Browse files
committed
feat(crashdump): Add crash dump functionality
1 parent 38a4202 commit 7e85db6

File tree

10 files changed

+1286
-3
lines changed

10 files changed

+1286
-3
lines changed

Core/GameEngine/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ set(GAMEENGINE_SRC
7171
# Include/Common/MapObject.h
7272
# Include/Common/MapReaderWriterInfo.h
7373
# Include/Common/MessageStream.h
74+
Include/Common/MiniDumper.h
75+
Include/Common/MiniDumper_compat.h
7476
# Include/Common/MiniLog.h
7577
Include/Common/MiscAudio.h
7678
# Include/Common/MissionStats.h
@@ -658,6 +660,7 @@ set(GAMEENGINE_SRC
658660
# Source/Common/System/List.cpp
659661
Source/Common/System/LocalFile.cpp
660662
Source/Common/System/LocalFileSystem.cpp
663+
Source/Common/System/MiniDumper.cpp
661664
# Source/Common/System/ObjectStatusTypes.cpp
662665
# Source/Common/System/QuotedPrintable.cpp
663666
# Source/Common/System/Radar.cpp

Core/GameEngine/Include/Common/GameMemory.h

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,9 @@ class MemoryPool;
223223
class MemoryPoolFactory;
224224
class DynamicMemoryAllocator;
225225
class BlockCheckpointInfo;
226+
#ifdef RTS_ENABLE_CRASHDUMP
227+
class AllocationRangeIterator;
228+
#endif
226229

227230
// TYPE DEFINES ///////////////////////////////////////////////////////////////
228231

@@ -279,6 +282,14 @@ class Checkpointable
279282
};
280283
#endif
281284

285+
#ifdef RTS_ENABLE_CRASHDUMP
286+
struct MemoryPoolAllocatedRange
287+
{
288+
char* allocationAddr;
289+
size_t allocationSize;
290+
};
291+
#endif
292+
282293
// ----------------------------------------------------------------------------
283294
/**
284295
A MemoryPool provides a way to efficiently allocate objects of the same (or similar)
@@ -384,6 +395,9 @@ class MemoryPool
384395
/// return true iff this block was allocated by this pool.
385396
Bool debugIsBlockInPool(void *pBlock);
386397
#endif
398+
#ifdef RTS_ENABLE_CRASHDUMP
399+
friend class AllocationRangeIterator;
400+
#endif
387401
};
388402

389403
// ----------------------------------------------------------------------------
@@ -474,13 +488,70 @@ class DynamicMemoryAllocator
474488
Bool debugIsPoolInDma(MemoryPool *pool);
475489

476490
#endif // MEMORYPOOL_DEBUG
491+
#ifdef RTS_ENABLE_CRASHDUMP
492+
Int getRawBlockCount() const;
493+
void fillAllocationRangeForRawBlockN(const Int n, MemoryPoolAllocatedRange& allocationRange) const;
494+
#endif
477495
};
478496

479497
// ----------------------------------------------------------------------------
480498
#ifdef MEMORYPOOL_DEBUG
481499
enum { MAX_SPECIAL_USED = 256 };
482500
#endif
483501

502+
#ifdef RTS_ENABLE_CRASHDUMP
503+
class AllocationRangeIterator {
504+
typedef const MemoryPoolAllocatedRange value_type;
505+
typedef const MemoryPoolAllocatedRange* pointer;
506+
typedef const MemoryPoolAllocatedRange& reference;
507+
public:
508+
509+
AllocationRangeIterator(const MemoryPoolFactory* factory);
510+
AllocationRangeIterator(MemoryPool& pool, MemoryPoolBlob& blob) {
511+
m_currentPool = &pool;
512+
m_currentBlobInPool = &blob;
513+
m_factory = NULL;
514+
m_range = MemoryPoolAllocatedRange();
515+
};
516+
517+
AllocationRangeIterator(MemoryPool* pool, MemoryPoolBlob* blob)
518+
{
519+
m_currentPool = pool;
520+
m_currentBlobInPool = blob;
521+
m_factory = NULL;
522+
m_range = MemoryPoolAllocatedRange();
523+
};
524+
525+
AllocationRangeIterator()
526+
{
527+
m_currentPool = NULL;
528+
m_currentBlobInPool = NULL;
529+
m_factory = NULL;
530+
m_range = MemoryPoolAllocatedRange();
531+
};
532+
533+
reference operator*() { UpdateRange(); return m_range; }
534+
pointer operator->() { UpdateRange(); return &m_range; }
535+
536+
// Prefix increment
537+
AllocationRangeIterator& operator++() { MoveToNextBlob(); return *this; }
538+
539+
// Postfix increment
540+
AllocationRangeIterator operator++(int) { AllocationRangeIterator tmp = *this; ++(*this); return tmp; }
541+
542+
friend bool operator== (const AllocationRangeIterator& a, const AllocationRangeIterator& b) { return a.m_currentBlobInPool == b.m_currentBlobInPool; };
543+
friend bool operator!= (const AllocationRangeIterator& a, const AllocationRangeIterator& b) { return a.m_currentBlobInPool != b.m_currentBlobInPool; };
544+
545+
private:
546+
const MemoryPoolFactory* m_factory;
547+
MemoryPool* m_currentPool;
548+
MemoryPoolBlob* m_currentBlobInPool;
549+
MemoryPoolAllocatedRange m_range;
550+
void UpdateRange();
551+
void MoveToNextBlob();
552+
};
553+
#endif
554+
484555
// ----------------------------------------------------------------------------
485556
/**
486557
The class that manages all the MemoryPools and DynamicMemoryAllocators.
@@ -573,6 +644,19 @@ class MemoryPoolFactory
573644
void debugResetCheckpoints();
574645

575646
#endif
647+
#ifdef RTS_ENABLE_CRASHDUMP
648+
AllocationRangeIterator cbegin() const {
649+
return AllocationRangeIterator(this);
650+
}
651+
652+
AllocationRangeIterator cend() const {
653+
return AllocationRangeIterator(NULL, NULL);
654+
}
655+
656+
Int getMemoryPoolCount() const;
657+
MemoryPool* getMemoryPoolN(const Int n) const;
658+
friend class AllocationRangeIterator;
659+
#endif
576660
};
577661

578662
// how many bytes are we allowed to 'waste' per pool allocation before the debug code starts yelling at us...
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/*
2+
** Command & Conquer Generals Zero Hour(tm)
3+
** Copyright 2025 TheSuperHackers
4+
**
5+
** This program is free software: you can redistribute it and/or modify
6+
** it under the terms of the GNU General Public License as published by
7+
** the Free Software Foundation, either version 3 of the License, or
8+
** (at your option) any later version.
9+
**
10+
** This program is distributed in the hope that it will be useful,
11+
** but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
** GNU General Public License for more details.
14+
**
15+
** You should have received a copy of the GNU General Public License
16+
** along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
19+
#pragma once
20+
21+
#ifdef RTS_ENABLE_CRASHDUMP
22+
#include <imagehlp.h>
23+
#include "Common/MiniDumper_compat.h"
24+
25+
class MiniDumper
26+
{
27+
public:
28+
MiniDumper()
29+
{
30+
m_miniDumpInitialized = false;
31+
m_extendedInfoRequested = false;
32+
m_dbgHlp = NULL;
33+
m_pMiniDumpWriteDump = NULL;
34+
m_dumpRequested = NULL;
35+
m_dumpComplete = NULL;
36+
m_quitting = NULL;
37+
m_dumpThread = NULL;
38+
m_dumpThreadId = 0;
39+
m_dumpObjectsState = 0;
40+
m_dumpObjectsSubState = 0;
41+
m_dmaRawBlockIndex = 0;
42+
memset(m_dumpDir, 0, ARRAY_SIZE(m_dumpDir));
43+
memset(m_dumpFile, 0, ARRAY_SIZE(m_dumpFile));
44+
memset(m_sysDbgHelpPath, 0, ARRAY_SIZE(m_sysDbgHelpPath));
45+
};
46+
47+
void Initialize(const AsciiString& userDirPath);
48+
Bool IsInitialized() const;
49+
void TriggerMiniDump(Bool extendedInfo = false);
50+
void TriggerMiniDumpForException(struct _EXCEPTION_POINTERS* e_info, Bool extendedInfo = false);
51+
void ShutDown();
52+
static LONG WINAPI DumpingExceptionFilter(struct _EXCEPTION_POINTERS* e_info);
53+
private:
54+
void CreateMiniDump(Bool extendedInfo);
55+
BOOL DumpMemoryObjects(ULONG64& memoryBase, ULONG& memorySize);
56+
void CleanupResources();
57+
58+
// Callbacks from dbghelp
59+
static BOOL CALLBACK MiniDumpCallback(PVOID CallbackParam, PMINIDUMP_CALLBACK_INPUT CallbackInput, PMINIDUMP_CALLBACK_OUTPUT CallbackOutput);
60+
BOOL CallbackInternal(const MINIDUMP_CALLBACK_INPUT& input, MINIDUMP_CALLBACK_OUTPUT& output);
61+
62+
// Thread procs
63+
static DWORD WINAPI MiniDumpThreadProc(LPVOID lpParam);
64+
DWORD ThreadProcInternal();
65+
66+
// Dump file directory bookkeeping
67+
Bool InitializeDumpDirectory(const AsciiString& userDirPath);
68+
static void KeepNewestFiles(const std::string& directory, const std::string& fileWildcard, const Int keepCount);
69+
70+
// Struct to hold file information
71+
struct FileInfo {
72+
std::string name;
73+
FILETIME lastWriteTime;
74+
};
75+
76+
static bool CompareByLastWriteTime(const FileInfo& a, const FileInfo& b);
77+
78+
private:
79+
Bool m_miniDumpInitialized;
80+
Bool m_extendedInfoRequested;
81+
82+
// Path buffers
83+
Char m_dumpDir[MAX_PATH];
84+
Char m_dumpFile[MAX_PATH];
85+
Char m_sysDbgHelpPath[MAX_PATH];
86+
87+
// Module handles
88+
HMODULE m_dbgHlp;
89+
90+
// Event handles
91+
HANDLE m_dumpRequested;
92+
HANDLE m_dumpComplete;
93+
HANDLE m_quitting;
94+
95+
// Thread handles
96+
HANDLE m_dumpThread;
97+
DWORD m_dumpThreadId;
98+
99+
// Internal memory dumping progress state
100+
int m_dumpObjectsState;
101+
int m_dumpObjectsSubState;
102+
int m_dmaRawBlockIndex;
103+
104+
AllocationRangeIterator m_RangeIter;
105+
AllocationRangeIterator m_endRangeIter;
106+
107+
// Function pointer to MiniDumpWriteDump in dbghelp.dll
108+
typedef BOOL(WINAPI* MiniDumpWriteDump_t)(
109+
HANDLE hProcess,
110+
DWORD ProcessId,
111+
HANDLE hFile,
112+
MINIDUMP_TYPE DumpType,
113+
PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
114+
PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
115+
PMINIDUMP_CALLBACK_INFORMATION CallbackParam
116+
);
117+
118+
MiniDumpWriteDump_t m_pMiniDumpWriteDump;
119+
};
120+
#endif

0 commit comments

Comments
 (0)