Skip to content

Commit 7187333

Browse files
committed
feat(crashdump): Add crash dump functionality
1 parent dd2bb78 commit 7187333

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
@@ -70,6 +70,8 @@ set(GAMEENGINE_SRC
7070
# Include/Common/MapObject.h
7171
# Include/Common/MapReaderWriterInfo.h
7272
# Include/Common/MessageStream.h
73+
Include/Common/MiniDumper.h
74+
Include/Common/MiniDumper_compat.h
7375
# Include/Common/MiniLog.h
7476
Include/Common/MiscAudio.h
7577
# Include/Common/MissionStats.h
@@ -657,6 +659,7 @@ set(GAMEENGINE_SRC
657659
# Source/Common/System/List.cpp
658660
Source/Common/System/LocalFile.cpp
659661
Source/Common/System/LocalFileSystem.cpp
662+
Source/Common/System/MiniDumper.cpp
660663
# Source/Common/System/ObjectStatusTypes.cpp
661664
# Source/Common/System/QuotedPrintable.cpp
662665
# 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
@@ -226,6 +226,9 @@ class MemoryPool;
226226
class MemoryPoolFactory;
227227
class DynamicMemoryAllocator;
228228
class BlockCheckpointInfo;
229+
#ifdef RTS_ENABLE_CRASHDUMP
230+
class AllocationRangeIterator;
231+
#endif
229232

230233
// TYPE DEFINES ///////////////////////////////////////////////////////////////
231234

@@ -282,6 +285,14 @@ class Checkpointable
282285
};
283286
#endif
284287

288+
#ifdef RTS_ENABLE_CRASHDUMP
289+
struct MemoryPoolAllocatedRange
290+
{
291+
char* allocationAddr;
292+
size_t allocationSize;
293+
};
294+
#endif
295+
285296
// ----------------------------------------------------------------------------
286297
/**
287298
A MemoryPool provides a way to efficiently allocate objects of the same (or similar)
@@ -387,6 +398,9 @@ class MemoryPool
387398
/// return true iff this block was allocated by this pool.
388399
Bool debugIsBlockInPool(void *pBlock);
389400
#endif
401+
#ifdef RTS_ENABLE_CRASHDUMP
402+
friend class AllocationRangeIterator;
403+
#endif
390404
};
391405

392406
// ----------------------------------------------------------------------------
@@ -477,13 +491,70 @@ class DynamicMemoryAllocator
477491
Bool debugIsPoolInDma(MemoryPool *pool);
478492

479493
#endif // MEMORYPOOL_DEBUG
494+
#ifdef RTS_ENABLE_CRASHDUMP
495+
Int getRawBlockCount() const;
496+
void fillAllocationRangeForRawBlockN(const Int n, MemoryPoolAllocatedRange& allocationRange) const;
497+
#endif
480498
};
481499

482500
// ----------------------------------------------------------------------------
483501
#ifdef MEMORYPOOL_DEBUG
484502
enum { MAX_SPECIAL_USED = 256 };
485503
#endif
486504

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

578649
#endif
650+
#ifdef RTS_ENABLE_CRASHDUMP
651+
AllocationRangeIterator cbegin() const {
652+
return AllocationRangeIterator(this);
653+
}
654+
655+
AllocationRangeIterator cend() const {
656+
return AllocationRangeIterator(NULL, NULL);
657+
}
658+
659+
Int getMemoryPoolCount() const;
660+
MemoryPool* getMemoryPoolN(const Int n) const;
661+
friend class AllocationRangeIterator;
662+
#endif
579663
};
580664

581665
// 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)