Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Core/GameEngine/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ set(GAMEENGINE_SRC
# Include/Common/MapObject.h
# Include/Common/MapReaderWriterInfo.h
# Include/Common/MessageStream.h
Include/Common/MiniDumper.h
Include/Common/MiniDumper_compat.h
# Include/Common/MiniLog.h
Include/Common/MiscAudio.h
# Include/Common/MissionStats.h
Expand Down Expand Up @@ -657,6 +659,7 @@ set(GAMEENGINE_SRC
# Source/Common/System/List.cpp
Source/Common/System/LocalFile.cpp
Source/Common/System/LocalFileSystem.cpp
Source/Common/System/MiniDumper.cpp
# Source/Common/System/ObjectStatusTypes.cpp
# Source/Common/System/QuotedPrintable.cpp
# Source/Common/System/Radar.cpp
Expand Down Expand Up @@ -1170,6 +1173,7 @@ target_include_directories(corei_gameengine_private INTERFACE

target_link_libraries(corei_gameengine_private INTERFACE
corei_always
$<$<AND:$<BOOL:${IS_VS6_BUILD}>,$<BOOL:${RTS_CRASHDUMP_ENABLE}>>:shlwapi.lib>
)

target_compile_definitions(corei_gameengine_private INTERFACE
Expand Down
96 changes: 96 additions & 0 deletions Core/GameEngine/Include/Common/GameMemory.h
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,9 @@ class MemoryPool;
class MemoryPoolFactory;
class DynamicMemoryAllocator;
class BlockCheckpointInfo;
#ifdef RTS_ENABLE_CRASHDUMP
class AllocationRangeIterator;
#endif

// TYPE DEFINES ///////////////////////////////////////////////////////////////

Expand Down Expand Up @@ -282,6 +285,14 @@ class Checkpointable
};
#endif

#ifdef RTS_ENABLE_CRASHDUMP
struct MemoryPoolAllocatedRange
{
char* allocationAddr;
size_t allocationSize;
};
#endif

// ----------------------------------------------------------------------------
/**
A MemoryPool provides a way to efficiently allocate objects of the same (or similar)
Expand Down Expand Up @@ -387,6 +398,9 @@ class MemoryPool
/// return true iff this block was allocated by this pool.
Bool debugIsBlockInPool(void *pBlock);
#endif
#ifdef RTS_ENABLE_CRASHDUMP
friend class AllocationRangeIterator;
#endif
};

// ----------------------------------------------------------------------------
Expand Down Expand Up @@ -477,13 +491,80 @@ class DynamicMemoryAllocator
Bool debugIsPoolInDma(MemoryPool *pool);

#endif // MEMORYPOOL_DEBUG
#ifdef RTS_ENABLE_CRASHDUMP
Int getRawBlockCount() const;
void fillAllocationRangeForRawBlockN(const Int n, MemoryPoolAllocatedRange& allocationRange) const;
#endif
};

// ----------------------------------------------------------------------------
#ifdef MEMORYPOOL_DEBUG
enum { MAX_SPECIAL_USED = 256 };
#endif

#ifdef RTS_ENABLE_CRASHDUMP
class AllocationRangeIterator
{
typedef const MemoryPoolAllocatedRange value_type;
typedef const MemoryPoolAllocatedRange* pointer;
typedef const MemoryPoolAllocatedRange& reference;

public:

AllocationRangeIterator(const MemoryPoolFactory* factory);
AllocationRangeIterator(MemoryPool& pool, MemoryPoolBlob& blob)
{
m_currentPool = &pool;
m_currentBlobInPool = &blob;
m_factory = NULL;
m_range = MemoryPoolAllocatedRange();
};

AllocationRangeIterator(MemoryPool* pool, MemoryPoolBlob* blob)
{
m_currentPool = pool;
m_currentBlobInPool = blob;
m_factory = NULL;
m_range = MemoryPoolAllocatedRange();
};

AllocationRangeIterator()
{
m_currentPool = NULL;
m_currentBlobInPool = NULL;
m_factory = NULL;
m_range = MemoryPoolAllocatedRange();
};

reference operator*() { UpdateRange(); return m_range; }
pointer operator->() { UpdateRange(); return &m_range; }

// Prefix increment
AllocationRangeIterator& operator++() { MoveToNextBlob(); return *this; }

// Postfix increment
AllocationRangeIterator operator++(int) { AllocationRangeIterator tmp = *this; ++(*this); return tmp; }

friend const bool operator== (const AllocationRangeIterator& a, const AllocationRangeIterator& b)
{
return a.m_currentBlobInPool == b.m_currentBlobInPool;
};

friend const bool operator!= (const AllocationRangeIterator& a, const AllocationRangeIterator& b)
{
return a.m_currentBlobInPool != b.m_currentBlobInPool;
};

private:
const MemoryPoolFactory* m_factory;
MemoryPool* m_currentPool;
MemoryPoolBlob* m_currentBlobInPool;
MemoryPoolAllocatedRange m_range;
void UpdateRange();
void MoveToNextBlob();
};
#endif

// ----------------------------------------------------------------------------
/**
The class that manages all the MemoryPools and DynamicMemoryAllocators.
Expand Down Expand Up @@ -576,6 +657,21 @@ class MemoryPoolFactory
void debugResetCheckpoints();

#endif
#ifdef RTS_ENABLE_CRASHDUMP
AllocationRangeIterator cbegin() const
{
return AllocationRangeIterator(this);
}

AllocationRangeIterator cend() const
{
return AllocationRangeIterator(NULL, NULL);
}

Int getMemoryPoolCount() const;
MemoryPool* getMemoryPoolN(const Int n) const;
friend class AllocationRangeIterator;
#endif
};

// how many bytes are we allowed to 'waste' per pool allocation before the debug code starts yelling at us...
Expand Down
129 changes: 129 additions & 0 deletions Core/GameEngine/Include/Common/MiniDumper.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 TheSuperHackers
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#pragma once

#ifdef RTS_ENABLE_CRASHDUMP
#include <imagehlp.h>
#include "Common/MiniDumper_compat.h"

enum DumpType CPP_11(: Int)
{
// Smallest dump type with call stacks and some supporting variables
DUMP_TYPE_MINIMAL,
// Large dump including all memory regions allocated by the GameMemory implementaion
DUMP_TYPE_GAMEMEMORY,
// Largest dump size including complete memory contents of the process
DUMP_TYPE_FULL,
};

enum MiniDumperExitCode CPP_11(: Int)
{
DUMPER_EXIT_SUCCESS = 0x0,
DUMPER_EXIT_FAILURE_WAIT = 0x37DA1040,
DUMPER_EXIT_FAILURE_PARAM = 0x4EA527BB,
DUMPER_EXIT_FORCED_TERMINATE = 0x158B1154,
};

class MiniDumper
{
public:
MiniDumper();
Bool IsInitialized() const;
void TriggerMiniDump(DumpType dumpType);
void TriggerMiniDumpForException(struct _EXCEPTION_POINTERS* e_info, DumpType dumpType);
static void initMiniDumper(const AsciiString& userDirPath);
static void shutdownMiniDumper();
static LONG WINAPI DumpingExceptionFilter(struct _EXCEPTION_POINTERS* e_info);

private:
void Initialize(const AsciiString& userDirPath);
void ShutDown();
void CreateMiniDump(DumpType dumpType);
BOOL DumpMemoryObjects(ULONG64& memoryBase, ULONG& memorySize);
void CleanupResources();
Bool IsDumpThreadStillRunning() const;

// Callbacks from dbghelp
static BOOL CALLBACK MiniDumpCallback(PVOID CallbackParam, PMINIDUMP_CALLBACK_INPUT CallbackInput, PMINIDUMP_CALLBACK_OUTPUT CallbackOutput);
BOOL CallbackInternal(const MINIDUMP_CALLBACK_INPUT& input, MINIDUMP_CALLBACK_OUTPUT& output);

// Thread procs
static DWORD WINAPI MiniDumpThreadProc(LPVOID lpParam);
DWORD ThreadProcInternal();

// Dump file directory bookkeeping
Bool InitializeDumpDirectory(const AsciiString& userDirPath);
static void KeepNewestFiles(const std::string& directory, const std::string& fileWildcard, const Int keepCount);

// Struct to hold file information
struct FileInfo
{
std::string name;
FILETIME lastWriteTime;
};

static bool CompareByLastWriteTime(const FileInfo& a, const FileInfo& b);

private:
Bool m_miniDumpInitialized;
DumpType m_requestedDumpType;

// Path buffers
Char m_dumpDir[MAX_PATH];
Char m_dumpFile[MAX_PATH];
Char m_sysDbgHelpPath[MAX_PATH];
WideChar m_executablePath[MAX_PATH];

// Module handles
HMODULE m_dbgHlp;

// Event handles
HANDLE m_dumpRequested;
HANDLE m_dumpComplete;
HANDLE m_quitting;

// Thread handles
HANDLE m_dumpThread;
DWORD m_dumpThreadId;

#ifndef DISABLE_GAMEMEMORY
// Internal memory dumping progress state
int m_dumpObjectsState;
int m_dumpObjectsSubState;
int m_dmaRawBlockIndex;

AllocationRangeIterator m_rangeIter;
#endif

// Function pointer to MiniDumpWriteDump in dbghelp.dll
typedef BOOL(WINAPI* MiniDumpWriteDump_t)(
HANDLE hProcess,
DWORD ProcessId,
HANDLE hFile,
MINIDUMP_TYPE DumpType,
PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
PMINIDUMP_CALLBACK_INFORMATION CallbackParam
);

MiniDumpWriteDump_t m_pMiniDumpWriteDump;
};

extern MiniDumper* TheMiniDumper;
#endif
Loading
Loading