diff --git a/engine/shared/library/CMakeLists.txt b/engine/shared/library/CMakeLists.txt index b5ab4cdb..d7f56c12 100644 --- a/engine/shared/library/CMakeLists.txt +++ b/engine/shared/library/CMakeLists.txt @@ -1,3 +1,6 @@ +add_subdirectory(sharedDebug) add_subdirectory(sharedFoundation) +add_subdirectory(sharedFoundationTypes) +add_subdirectory(sharedMemoryManager) add_subdirectory(sharedNetworkMessages) diff --git a/engine/shared/library/sharedDebug/CMakeLists.txt b/engine/shared/library/sharedDebug/CMakeLists.txt new file mode 100644 index 00000000..6f7a94cb --- /dev/null +++ b/engine/shared/library/sharedDebug/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 2.8) + +project(sharedDebug) + +if(WIN32) + add_definitions(/D_CRT_SECURE_NO_WARNINGS) +endif() + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include/public) + +add_subdirectory(src) diff --git a/engine/shared/library/sharedDebug/include/public/sharedDebug/CallStack.h b/engine/shared/library/sharedDebug/include/public/sharedDebug/CallStack.h new file mode 100644 index 00000000..3b4bf739 --- /dev/null +++ b/engine/shared/library/sharedDebug/include/public/sharedDebug/CallStack.h @@ -0,0 +1 @@ +#include "../../src/shared/CallStack.h" diff --git a/engine/shared/library/sharedDebug/include/public/sharedDebug/CallStackCollector.h b/engine/shared/library/sharedDebug/include/public/sharedDebug/CallStackCollector.h new file mode 100644 index 00000000..ac117006 --- /dev/null +++ b/engine/shared/library/sharedDebug/include/public/sharedDebug/CallStackCollector.h @@ -0,0 +1 @@ +#include "../../src/shared/CallStackCollector.h" diff --git a/engine/shared/library/sharedDebug/include/public/sharedDebug/ConfigSharedDebugLinux.h b/engine/shared/library/sharedDebug/include/public/sharedDebug/ConfigSharedDebugLinux.h new file mode 100644 index 00000000..bccdab14 --- /dev/null +++ b/engine/shared/library/sharedDebug/include/public/sharedDebug/ConfigSharedDebugLinux.h @@ -0,0 +1 @@ +#include "../../src/linux/ConfigSharedDebugLinux.h" diff --git a/engine/shared/library/sharedDebug/include/public/sharedDebug/DataLint.h b/engine/shared/library/sharedDebug/include/public/sharedDebug/DataLint.h new file mode 100644 index 00000000..7be3d57d --- /dev/null +++ b/engine/shared/library/sharedDebug/include/public/sharedDebug/DataLint.h @@ -0,0 +1 @@ +#include "../../src/shared/DataLint.h" diff --git a/engine/shared/library/sharedDebug/include/public/sharedDebug/DebugFlags.h b/engine/shared/library/sharedDebug/include/public/sharedDebug/DebugFlags.h new file mode 100644 index 00000000..e5a737be --- /dev/null +++ b/engine/shared/library/sharedDebug/include/public/sharedDebug/DebugFlags.h @@ -0,0 +1 @@ +#include "../../src/shared/DebugFlags.h" diff --git a/engine/shared/library/sharedDebug/include/public/sharedDebug/DebugHelp.h b/engine/shared/library/sharedDebug/include/public/sharedDebug/DebugHelp.h new file mode 100644 index 00000000..a0d0f9f4 --- /dev/null +++ b/engine/shared/library/sharedDebug/include/public/sharedDebug/DebugHelp.h @@ -0,0 +1,7 @@ +#if defined(PLATFORM_WIN32) +#include "../../src/win32/DebugHelp.h" +#elif defined(PLATFORM_LINUX) +#include "../../src/linux/DebugHelp.h" +#else +#error unsupported platform +#endif diff --git a/engine/shared/library/sharedDebug/include/public/sharedDebug/DebugKey.h b/engine/shared/library/sharedDebug/include/public/sharedDebug/DebugKey.h new file mode 100644 index 00000000..210af2eb --- /dev/null +++ b/engine/shared/library/sharedDebug/include/public/sharedDebug/DebugKey.h @@ -0,0 +1 @@ +#include "../../src/shared/DebugKey.h" diff --git a/engine/shared/library/sharedDebug/include/public/sharedDebug/DebugMonitor.h b/engine/shared/library/sharedDebug/include/public/sharedDebug/DebugMonitor.h new file mode 100644 index 00000000..2692bdd6 --- /dev/null +++ b/engine/shared/library/sharedDebug/include/public/sharedDebug/DebugMonitor.h @@ -0,0 +1,7 @@ +#if defined(PLATFORM_WIN32) +#include "../../src/win32/DebugMonitor.h" +#elif defined(PLATFORM_LINUX) +#include "../../src/linux/DebugMonitor.h" +#else +#error unsupported platform +#endif diff --git a/engine/shared/library/sharedDebug/include/public/sharedDebug/FirstSharedDebug.h b/engine/shared/library/sharedDebug/include/public/sharedDebug/FirstSharedDebug.h new file mode 100644 index 00000000..45be57b1 --- /dev/null +++ b/engine/shared/library/sharedDebug/include/public/sharedDebug/FirstSharedDebug.h @@ -0,0 +1 @@ +#include "../../src/shared/FirstSharedDebug.h" diff --git a/engine/shared/library/sharedDebug/include/public/sharedDebug/InstallTimer.h b/engine/shared/library/sharedDebug/include/public/sharedDebug/InstallTimer.h new file mode 100644 index 00000000..33d6b905 --- /dev/null +++ b/engine/shared/library/sharedDebug/include/public/sharedDebug/InstallTimer.h @@ -0,0 +1 @@ +#include "../../src/shared/InstallTimer.h" diff --git a/engine/shared/library/sharedDebug/include/public/sharedDebug/LeakFinder.h b/engine/shared/library/sharedDebug/include/public/sharedDebug/LeakFinder.h new file mode 100644 index 00000000..55f56279 --- /dev/null +++ b/engine/shared/library/sharedDebug/include/public/sharedDebug/LeakFinder.h @@ -0,0 +1 @@ +#include "../../src/shared/LeakFinder.h" diff --git a/engine/shared/library/sharedDebug/include/public/sharedDebug/PerformanceTimer.h b/engine/shared/library/sharedDebug/include/public/sharedDebug/PerformanceTimer.h new file mode 100644 index 00000000..36a68c7d --- /dev/null +++ b/engine/shared/library/sharedDebug/include/public/sharedDebug/PerformanceTimer.h @@ -0,0 +1,7 @@ +#if defined(PLATFORM_WIN32) +#include "../../src/win32/PerformanceTimer.h" +#elif defined(PLATFORM_LINUX) +#include "../../src/linux/PerformanceTimer.h" +#else +#error unsupported platform +#endif diff --git a/engine/shared/library/sharedDebug/include/public/sharedDebug/PixCounter.h b/engine/shared/library/sharedDebug/include/public/sharedDebug/PixCounter.h new file mode 100644 index 00000000..0431585f --- /dev/null +++ b/engine/shared/library/sharedDebug/include/public/sharedDebug/PixCounter.h @@ -0,0 +1 @@ +#include "../../src/shared/PixCounter.h" diff --git a/engine/shared/library/sharedDebug/include/public/sharedDebug/Profiler.h b/engine/shared/library/sharedDebug/include/public/sharedDebug/Profiler.h new file mode 100644 index 00000000..33e6fe0f --- /dev/null +++ b/engine/shared/library/sharedDebug/include/public/sharedDebug/Profiler.h @@ -0,0 +1 @@ +#include "../../src/shared/Profiler.h" diff --git a/engine/shared/library/sharedDebug/include/public/sharedDebug/ProfilerTimer.h b/engine/shared/library/sharedDebug/include/public/sharedDebug/ProfilerTimer.h new file mode 100644 index 00000000..bfd4cfce --- /dev/null +++ b/engine/shared/library/sharedDebug/include/public/sharedDebug/ProfilerTimer.h @@ -0,0 +1,7 @@ +#if defined(PLATFORM_WIN32) +#include "../../src/win32/ProfilerTimer.h" +#elif defined(PLATFORM_LINUX) +#include "../../src/linux/ProfilerTimer.h" +#else +#error unsupported platform +#endif diff --git a/engine/shared/library/sharedDebug/include/public/sharedDebug/RemoteDebug.h b/engine/shared/library/sharedDebug/include/public/sharedDebug/RemoteDebug.h new file mode 100644 index 00000000..fec418b3 --- /dev/null +++ b/engine/shared/library/sharedDebug/include/public/sharedDebug/RemoteDebug.h @@ -0,0 +1 @@ +#include "../../src/shared/RemoteDebug.h" diff --git a/engine/shared/library/sharedDebug/include/public/sharedDebug/RemoteDebug_inner.h b/engine/shared/library/sharedDebug/include/public/sharedDebug/RemoteDebug_inner.h new file mode 100644 index 00000000..f3158212 --- /dev/null +++ b/engine/shared/library/sharedDebug/include/public/sharedDebug/RemoteDebug_inner.h @@ -0,0 +1 @@ +#include "../../src/shared/RemoteDebug_inner.h" diff --git a/engine/shared/library/sharedDebug/include/public/sharedDebug/Report.h b/engine/shared/library/sharedDebug/include/public/sharedDebug/Report.h new file mode 100644 index 00000000..5aa8c121 --- /dev/null +++ b/engine/shared/library/sharedDebug/include/public/sharedDebug/Report.h @@ -0,0 +1 @@ +#include "../../src/shared/Report.h" diff --git a/engine/shared/library/sharedDebug/include/public/sharedDebug/SetupSharedDebug.h b/engine/shared/library/sharedDebug/include/public/sharedDebug/SetupSharedDebug.h new file mode 100644 index 00000000..46b3aaf7 --- /dev/null +++ b/engine/shared/library/sharedDebug/include/public/sharedDebug/SetupSharedDebug.h @@ -0,0 +1 @@ +#include "../../src/shared/SetupSharedDebug.h" diff --git a/engine/shared/library/sharedDebug/include/public/sharedDebug/VTune.h b/engine/shared/library/sharedDebug/include/public/sharedDebug/VTune.h new file mode 100644 index 00000000..e4af3ab2 --- /dev/null +++ b/engine/shared/library/sharedDebug/include/public/sharedDebug/VTune.h @@ -0,0 +1,7 @@ +#if defined(PLATFORM_WIN32) +#include "../../src/win32/VTune.h" +#elif defined(PLATFORM_LINUX) +#include "../../src/linux/VTune.h" +#else +#error unsupported platform +#endif diff --git a/engine/shared/library/sharedDebug/src/CMakeLists.txt b/engine/shared/library/sharedDebug/src/CMakeLists.txt new file mode 100644 index 00000000..aa4f09d3 --- /dev/null +++ b/engine/shared/library/sharedDebug/src/CMakeLists.txt @@ -0,0 +1,74 @@ + +set(SHARED_SOURCES + shared/CallStack.cpp + shared/CallStack.h + shared/CallStackCollector.cpp + shared/CallStackCollector.h + shared/DataLint.cpp + shared/DataLint.h + shared/DebugFlags.cpp + shared/DebugFlags.h + shared/DebugKey.cpp + shared/DebugKey.h + shared/FirstSharedDebug.h + shared/InstallTimer.cpp + shared/InstallTimer.h + shared/PixCounter.cpp + shared/PixCounter.h + shared/Profiler.cpp + shared/Profiler.h + shared/RemoteDebug.cpp + shared/RemoteDebug.h + shared/RemoteDebug_inner.cpp + shared/RemoteDebug_inner.h + shared/Report.cpp + shared/Report.h + shared/SetupSharedDebug.cpp + shared/SetupSharedDebug.h +) + +if(WIN32) + set(PLATFORM_SOURCES + win32/DebugHelp.cpp + win32/DebugHelp.h + win32/DebugMonitor.cpp + win32/DebugMonitor.h + win32/FirstSharedDebug.cpp + win32/PerformanceTimer.cpp + win32/PerformanceTimer.h + win32/ProfilerTimer.cpp + win32/ProfilerTimer.h + win32/VTune.cpp + win32/VTune.h + ) + + include_directories(${CMAKE_CURRENT_SOURCE_DIR}/win32) +else() + set(PLATFORM_SOURCES + linux/ConfigSharedDebugLinux.cpp + linux/ConfigSharedDebugLinux.h + linux/DebugHelp.cpp + linux/DebugHelp.h + linux/DebugMonitor.cpp + linux/DebugMonitor.h + linux/PerformanceTimer.cpp + linux/PerformanceTimer.h + linux/ProfilerTimer.cpp + linux/ProfilerTimer.h + ) + + include_directories(${CMAKE_CURRENT_SOURCE_DIR}/linux) +endif() + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/shared + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedFoundation/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedFoundationTypes/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedMemoryManager/include/public + ${SWG_ROOT_SOURCE_DIR}/external/3rd/library/vtune +) + +add_library(sharedDebug STATIC + ${SHARED_SOURCES} + ${PLATFORM_SOURCES} +) diff --git a/engine/shared/library/sharedDebug/src/linux/ConfigSharedDebugLinux.cpp b/engine/shared/library/sharedDebug/src/linux/ConfigSharedDebugLinux.cpp new file mode 100644 index 00000000..e50ad47c --- /dev/null +++ b/engine/shared/library/sharedDebug/src/linux/ConfigSharedDebugLinux.cpp @@ -0,0 +1,70 @@ +// ====================================================================== +// +// ConfigSharedDebugLinux.cpp +// Copyright 2002 Sony Online Entertainment, Inc. +// All Rights Reserved. +// +// ====================================================================== + +#include "sharedDebug/FirstSharedDebug.h" +#include "sharedDebug/ConfigSharedDebugLinux.h" + +#include "sharedDebug/Profiler.h" +#include "sharedFoundation/ConfigFile.h" + +//=================================================================== + +namespace ConfigSharedDebugLinuxNamespace +{ + bool s_useTty; + const char *s_debugMonitorOutputFilename; + bool s_logTtySetup; +} + +using namespace ConfigSharedDebugLinuxNamespace; + +//=================================================================== + +#define KEY_BOOL(a,b) (s_ ## a = ConfigFile::getKeyBool("SharedDebugLinux", #a, b)) +#define KEY_STRING(a,b) (s_ ## a = ConfigFile::getKeyString("SharedDebugLinux", #a, b)) +// #define KEY_INT(a,b) (s_ ## a = ConfigFile::getKeyInt("ClientAnimation", #a, b)) +// #define KEY_FLOAT(a,b) (s_ ## a = ConfigFile::getKeyFloat("ClientAnimation", #a, b)) + +//=================================================================== + +void ConfigSharedDebugLinux::install(void) +{ + KEY_BOOL(useTty, false); + KEY_STRING(debugMonitorOutputFilename, "ttySpecifier.txt"); + KEY_BOOL(logTtySetup, false); + + //-- Handle startup profiler handling here. + // @todo this should move into ConfigSharedDebug or be loaded + // by Profiler. Profiler can't load this config file option + // because the Profiler is installed prior to the config file + // system. + Profiler::enableProfilerOutput(ConfigFile::getKeyBool("SharedDebugLinux", "reportProfiler", false)); +} + +// ------------------------------------------------------------------ + +bool ConfigSharedDebugLinux::getUseTty() +{ + return s_useTty; +} + +// ------------------------------------------------------------------ + +char const *ConfigSharedDebugLinux::getDebugMonitorOutputFilename() +{ + return s_debugMonitorOutputFilename; +} + +// ------------------------------------------------------------------ + +bool ConfigSharedDebugLinux::getLogTtySetup() +{ + return s_logTtySetup; +} + +//=================================================================== diff --git a/engine/shared/library/sharedDebug/src/linux/ConfigSharedDebugLinux.h b/engine/shared/library/sharedDebug/src/linux/ConfigSharedDebugLinux.h new file mode 100644 index 00000000..9d23544d --- /dev/null +++ b/engine/shared/library/sharedDebug/src/linux/ConfigSharedDebugLinux.h @@ -0,0 +1,29 @@ +// ====================================================================== +// +// ConfigSharedDebugLinux.h +// Copyright 2002 Sony Online Entertainment, Inc. +// All Rights Reserved. +// +// ====================================================================== + +#ifndef INCLUDED_ConfigSharedDebugLinux_H +#define INCLUDED_ConfigSharedDebugLinux_H + +//=================================================================== + +class ConfigSharedDebugLinux +{ +public: + + static void install(); + + static bool getUseTty(); + static char const *getDebugMonitorOutputFilename(); + static bool getLogTtySetup(); + +}; + +//=================================================================== + +#endif + diff --git a/engine/shared/library/sharedDebug/src/linux/DebugHelp.cpp b/engine/shared/library/sharedDebug/src/linux/DebugHelp.cpp new file mode 100644 index 00000000..47d451b5 --- /dev/null +++ b/engine/shared/library/sharedDebug/src/linux/DebugHelp.cpp @@ -0,0 +1,677 @@ +// ====================================================================== +// +// DebugHelp.cpp +// Copyright 2001-2003 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedDebug/FirstSharedDebug.h" +#include "sharedDebug/DebugHelp.h" +#include "sharedSynchronization/Mutex.h" +#include +#include +#include +#include +#include +#include +#include +#include + +// ====================================================================== + +class SymbolCache +{ +public: + + struct SymbolInfo + { + char const *srcLib; + char const *srcFile; + int srcLine; + bool found; + }; + + static void clear(); + static SymbolInfo const &lookup(void const *addr); + static char const *uniqueString(char const *s); + static void *memPoolAllocate(size_t size); + +private: + static const size_t cms_memPoolMaxBytes = 8*1024*1024; + static size_t ms_memPoolUsed; + static char ms_memPool[cms_memPoolMaxBytes]; + static char *ms_memPoolFreeList; + static Mutex ms_memPoolMutex; + static SymbolInfo ms_nullSym; +}; + +// ---------------------------------------------------------------------- + +template +class SymbolCacheAllocator +{ +public: + typedef size_t size_type; + typedef ptrdiff_t difference_type; + typedef T * pointer; + typedef T const * const_pointer; + typedef T & reference; + typedef T const & const_reference; + typedef T value_type; + +public: + SymbolCacheAllocator() {} + template SymbolCacheAllocator(SymbolCacheAllocator const &) {} + template struct rebind { typedef SymbolCacheAllocator other; }; + + pointer allocate(size_type n, const_pointer = 0) { return reinterpret_cast(SymbolCache::memPoolAllocate(n*sizeof(value_type))); } + void deallocate(const_pointer p, size_type n) {} + pointer address(reference x) const { return &x; } + const_pointer address(const_reference x) const { return &x; } + size_type max_size() const { return SymbolCache::cms_memPoolMaxBytes; } + void construct(pointer p, value_type const & x) { new(p) value_type(x); } + void destroy(pointer p) { p->~value_type(); } +}; + +// ---------------------------------------------------------------------- + +size_t SymbolCache::ms_memPoolUsed; +char SymbolCache::ms_memPool[SymbolCache::cms_memPoolMaxBytes]; +char *SymbolCache::ms_memPoolFreeList; +Mutex SymbolCache::ms_memPoolMutex; +SymbolCache::SymbolInfo SymbolCache::ms_nullSym; +typedef std::map, SymbolCacheAllocator > > SymbolMap; +typedef std::vector > UniqueStringVector; +static SymbolMap ms_cacheMap; +static UniqueStringVector ms_uniqueStringVector; + +// ---------------------------------------------------------------------- + +static Elf32_Shdr const *elfGetObjSectionHeader(void const *objBaseAddr, int sectionIndex) +{ + Elf32_Ehdr const *eh = reinterpret_cast(objBaseAddr); + if (sectionIndex >= 0 && sectionIndex < eh->e_shnum) + return reinterpret_cast(static_cast(objBaseAddr)+eh->e_shoff+sectionIndex*eh->e_shentsize); + return 0; +} + +// ---------------------------------------------------------------------- + +static char const *elfGetObjSectionData(void const *objBaseAddr, int sectionIndex) +{ + Elf32_Shdr const *sh = elfGetObjSectionHeader(objBaseAddr, sectionIndex); + if (sh) + return reinterpret_cast(objBaseAddr)+sh->sh_offset; + return 0; +} + +// ---------------------------------------------------------------------- + +static unsigned int elfGetObjSectionSize(void const *objBaseAddr, int sectionIndex) +{ + Elf32_Shdr const *sh = elfGetObjSectionHeader(objBaseAddr, sectionIndex); + if (sh) + return sh->sh_size; + return 0; +} + +// ---------------------------------------------------------------------- + +static char const *elfGetObjSectionName(void const *objBaseAddr, int sectionIndex) +{ + Elf32_Ehdr const *eh = reinterpret_cast(objBaseAddr); + char const *sectionStr = elfGetObjSectionData(objBaseAddr, eh->e_shstrndx); + if (sectionStr) + { + Elf32_Shdr const *sh = elfGetObjSectionHeader(objBaseAddr, sectionIndex); + if (sh) + return sectionStr+sh->sh_name; + } + return 0; +} + +// ---------------------------------------------------------------------- + +static int elfGetObjSectionByName(void const *objBaseAddr, char const *sectionName) +{ + int n = reinterpret_cast(objBaseAddr)->e_shnum; + for (int i = 0; i < n; ++i) + if (!strcmp(elfGetObjSectionName(objBaseAddr, i), sectionName)) + return i; + return -1; +} + +// ---------------------------------------------------------------------- + +inline unsigned int dwarfGet(char const *src, u_int8_t &dest) +{ + memcpy(&dest, src, sizeof(u_int8_t)); + return sizeof(u_int8_t); +} + +// ---------------------------------------------------------------------- + +inline unsigned int dwarfGet(char const *src, u_int16_t &dest) +{ + memcpy(&dest, src, sizeof(u_int16_t)); + return sizeof(u_int16_t); +} + +// ---------------------------------------------------------------------- + +inline unsigned int dwarfGet(char const *src, u_int32_t &dest) +{ + memcpy(&dest, src, sizeof(u_int32_t)); + return sizeof(u_int32_t); +} + +// ---------------------------------------------------------------------- + +class LEB128 +{ +public: + operator int() const { return value; } + int value; +}; + +// ---------------------------------------------------------------------- + +inline unsigned int dwarfGet(char const *src, LEB128 &dest) +{ + unsigned int pos = 0; + int shift = 7; + int byte = ((u_int8_t*)src)[pos++]; + dest.value = byte; + while (byte >= 0x80) + { + byte = ((u_int8_t *)src)[pos++] ^ 1; + dest.value ^= byte << shift; + shift += 7; + } + if (shift < 32 && (byte & 0x40)) + dest.value |= -(1L<= 0x80) + { + byte = ((u_int8_t *)src)[pos++] ^ 1; + dest.value ^= byte << shift; + shift += 7; + } + return pos; +} + +// ---------------------------------------------------------------------- + +static bool dwarfSearch(char const *dwarfLines, unsigned int linesLength, void const *addr, Dl_info const &info, char const *&retSrcFile, int &retSrcLine) +{ + enum + { + DW_LNE_end_sequence = 1, + DW_LNE_set_address = 2, + + DW_LNS_copy = 1, + DW_LNS_advance_pc = 2, + DW_LNS_advance_line = 3, + DW_LNS_set_file = 4, + DW_LNS_set_column = 5, + DW_LNS_negate_stmt = 6, + DW_LNS_set_basic_block = 7, + DW_LNS_const_add_pc = 8, + DW_LNS_fixed_advance_pc = 9, + }; +//// + void const *bestOverAddr = reinterpret_cast(0xffffffff); + void const *bestUnderAddr = 0; + char const *bestUnderSrcFileTable = 0; + int bestUnderSrcFileNum = 0; + int bestUnderSrcLine = 0; + + unsigned int stmtProgMaxLen = linesLength; + u_int32_t stmtProgLen = 0; + + for (unsigned int progBeginOffset = 0; progBeginOffset < linesLength; progBeginOffset += stmtProgLen+4, stmtProgMaxLen -= stmtProgLen+4) + { + char const *stmtProg = dwarfLines+progBeginOffset; + // get program length + stmtProg += dwarfGet(stmtProg, stmtProgLen); + if (stmtProgLen < 12 || stmtProgLen+4 > stmtProgMaxLen) + continue; + + char const *stmtProgEnd = stmtProg+stmtProgLen; + stmtProg += 2; // skip version + // get prologue length + u_int32_t stmtProgPrologueLen; stmtProg += dwarfGet(stmtProg, stmtProgPrologueLen); + if (stmtProgPrologueLen+10 > stmtProgMaxLen) + continue; + + char const *stmtProgStart = stmtProg; + u_int8_t stmtProgMinInstructionLen; stmtProg += dwarfGet(stmtProg, stmtProgMinInstructionLen); + if (stmtProgMinInstructionLen == 0) + continue; + ++stmtProg; // skip default_is_stmt + int8_t stmtProgLineBase; stmtProg += dwarfGet(stmtProg, *(u_int8_t*)&stmtProgLineBase); + u_int8_t stmtProgLineRange; stmtProg += dwarfGet(stmtProg, stmtProgLineRange); + if (stmtProgLineRange == 0) + continue; + u_int8_t stmtProgOpcodeBase; stmtProg += dwarfGet(stmtProg, stmtProgOpcodeBase); + u_int8_t const *stmtProgOpcodeLengths = reinterpret_cast(stmtProg); + stmtProg += stmtProgOpcodeBase-1; + // include dirs here + while (*stmtProg) + while (*stmtProg++); + char const *stmtProgFilenames = stmtProg; + stmtProg = stmtProgStart+stmtProgPrologueLen; + + // run program + + while (stmtProg < stmtProgEnd) + { + int progFile = 0; + int progLine = 1; + u_int32_t progAddr = 0; + bool done = false; + bool valid = false; + + while (!done) + { + u_int8_t opcode = *stmtProg++; + if (opcode < stmtProgOpcodeBase) + { + switch (opcode) + { + case 0: // extended + { + u_int8_t size, extendedOpcode; + stmtProg += dwarfGet(stmtProg, size); + stmtProg += dwarfGet(stmtProg, extendedOpcode); + switch (extendedOpcode) + { + case DW_LNE_end_sequence: + valid = true; + done = true; + break; + case DW_LNE_set_address: + stmtProg += dwarfGet(stmtProg, progAddr); + break; + default: // unimplemented extended opcode, skip parms + stmtProg += size-1; + break; + } + } + break; + case DW_LNS_advance_pc: + { + LEB128u incr; stmtProg += dwarfGet(stmtProg, incr); + progAddr += incr*stmtProgMinInstructionLen; + } + break; + case DW_LNS_const_add_pc: + progAddr += (255-stmtProgOpcodeBase)/stmtProgLineRange*stmtProgMinInstructionLen; + break; + case DW_LNS_fixed_advance_pc: + { + u_int16_t incr; stmtProg += dwarfGet(stmtProg, incr); + progAddr += incr; + } + break; + case DW_LNS_advance_line: + { + LEB128 incr; stmtProg += dwarfGet(stmtProg, incr); + progLine += incr; + } + break; + case DW_LNS_set_file: + { + LEB128u fileNum; stmtProg += dwarfGet(stmtProg, fileNum); + progFile = fileNum-1; + } + break; + case DW_LNS_copy: + valid = true; + break; + // ignored + case DW_LNS_set_column: + { + LEB128u col; stmtProg += dwarfGet(stmtProg, col); + } + break; + case DW_LNS_negate_stmt: + case DW_LNS_set_basic_block: + break; + default: + { + // unimplemented standard opcode + // look up standard opcode length and skip that many LEB128u's + LEB128u temp; + for (int i = 0; i < stmtProgOpcodeLengths[opcode-1]; ++i) + stmtProg += dwarfGet(stmtProg, temp); + } + break; + } + } + else // special opcode + { + progLine += stmtProgLineBase+(opcode-stmtProgOpcodeBase)%stmtProgLineRange; + progAddr += (opcode-stmtProgOpcodeBase)/stmtProgLineRange*stmtProgMinInstructionLen; + valid = true; + } + + if (valid) + { + unsigned int addrOffset = 0; + if (progAddr < reinterpret_cast(info.dli_fbase)) + addrOffset = reinterpret_cast(info.dli_fbase); + const void *testAddr = reinterpret_cast(progAddr+addrOffset); + if (testAddr >= addr) + { + if (testAddr < bestOverAddr) + bestOverAddr = testAddr; + } + else if (testAddr > bestUnderAddr) + { + bestUnderAddr = testAddr; + bestUnderSrcFileTable = stmtProgFilenames; + bestUnderSrcFileNum = progFile; + bestUnderSrcLine = progLine; + } + } + } + } + } + + if (bestUnderAddr && bestOverAddr != reinterpret_cast(0xffffffff)) + { + char const *srcFile = bestUnderSrcFileTable+1; + for (int i = 0; i < bestUnderSrcFileNum; ++i) + { + while (*srcFile++); + srcFile += 3; + } + retSrcFile = SymbolCache::uniqueString(srcFile); + retSrcLine = bestUnderSrcLine; + return true; + } + return false; +} + +// ---------------------------------------------------------------------- + +static bool dwarfFind(void const *addr, Dl_info const &info, char const *& retSrcFile, int &retSrcLine) +{ + bool found = false; + int fd = open(info.dli_fname, O_RDONLY); + if (fd != -1) + { + int fileSize = lseek(fd, 0, SEEK_END); + lseek(fd, 0, SEEK_SET); + void const *mappedAddr = reinterpret_cast(mmap(0, fileSize, PROT_READ, MAP_PRIVATE, fd, 0)); + close(fd); + if (mappedAddr != MAP_FAILED) + { + int dwarfLinesIndex = elfGetObjSectionByName(mappedAddr, ".debug_line"); + if (dwarfLinesIndex != -1) + { + try + { + found = dwarfSearch( + elfGetObjSectionData(mappedAddr, dwarfLinesIndex), + elfGetObjSectionSize(mappedAddr, dwarfLinesIndex), + addr, + info, + retSrcFile, + retSrcLine); + } + catch (std::bad_alloc &) + { + munmap(const_cast(mappedAddr), fileSize); + throw std::bad_alloc(); + } + } + munmap(const_cast(mappedAddr), fileSize); + } + } + return found; +} + +// ---------------------------------------------------------------------- + +struct Stab +{ + unsigned int n_strx; // index into string table + unsigned char n_type; // type of stab entry + char n_other; + unsigned short n_desc; // for N_SLINE entries, line number + unsigned int n_value; // value of symbol +}; + +// ---------------------------------------------------------------------- + +static bool stabSearch(Stab const *stab, unsigned int stabSize, char const *stabStr, void const *addr, Dl_info const &info, char const *&retSrcFile, int &retSrcLine) +{ + enum + { + N_UNDF = 0, // undefined + N_FUN = 0x24, // function + N_SLINE = 0x44, // source line + N_SO = 0x64, // source file name + N_SOL = 0x84 // local source file name + }; + + unsigned int stabCount = stabSize/sizeof(Stab); + char const *srcFile = ""; + void const *funcBase = 0; + int foundSrcLine = -1; + + for (unsigned int i = 0; i < stabCount; ++i, ++stab) + { + if (stab->n_type == N_UNDF) // new stabs section, do a recursive search of it + { + if (stabSearch(stab+1, stab->n_desc*sizeof(Stab), stabStr, addr, info, retSrcFile, retSrcLine)) + return true; + i += stab->n_desc; + stab += stab->n_desc; + srcFile = ""; + continue; + } + else if (stab->n_type == N_SO || stab->n_type == N_SOL) // source or header file specification + srcFile = stabStr+stab->n_strx; + else if (stab->n_type == N_FUN) // function specification + { + // This may not be terribly accurate - it's attempting to differentiate between already relocated symbols (main program) + // and shared libs, which need to be offset by their file base + if (reinterpret_cast(stab->n_value) > info.dli_fbase) + funcBase = reinterpret_cast(stab->n_value); + else + funcBase = reinterpret_cast(reinterpret_cast(info.dli_fbase)+stab->n_value); + foundSrcLine = -1; + } + else if (stab->n_type == N_SLINE && addr >= funcBase) // source line + { + if (stab->n_value < reinterpret_cast(addr)-reinterpret_cast(funcBase)) + foundSrcLine = stab->n_desc; + else + { + retSrcLine = foundSrcLine == -1 ? stab->n_desc : foundSrcLine; + retSrcFile = SymbolCache::uniqueString(srcFile); + return true; + } + } + } + + return false; +} + +// ---------------------------------------------------------------------- + +static bool stabsFind(void const *addr, Dl_info const &info, char const *& retSrcFile, int &retSrcLine) +{ + bool found = false; + int fd = open(info.dli_fname, O_RDONLY); + if (fd != -1) + { + int fileSize = lseek(fd, 0, SEEK_END); + lseek(fd, 0, SEEK_SET); + void const *mappedAddr = reinterpret_cast(mmap(0, fileSize, PROT_READ, MAP_PRIVATE, fd, 0)); + close(fd); + if (mappedAddr != MAP_FAILED) + { + int stabIndex = elfGetObjSectionByName(mappedAddr, ".stab"); + int stabStrIndex = elfGetObjSectionByName(mappedAddr, ".stabstr"); + if (stabIndex != -1 && stabStrIndex != -1) + { + try + { + found = stabSearch( + reinterpret_cast(elfGetObjSectionData(mappedAddr, stabIndex)), + elfGetObjSectionSize(mappedAddr, stabIndex), + elfGetObjSectionData(mappedAddr, stabStrIndex), + addr, + info, + retSrcFile, + retSrcLine); + } + catch (std::bad_alloc &) + { + munmap(const_cast(mappedAddr), fileSize); + throw std::bad_alloc(); + } + } + munmap(const_cast(mappedAddr), fileSize); + } + } + return found; +} + +// ---------------------------------------------------------------------- + +SymbolCache::SymbolInfo const &SymbolCache::lookup(void const *addr) +{ + SymbolMap::const_iterator i = ms_cacheMap.find(addr); + if (i != ms_cacheMap.end()) + return (*i).second; + + // allow for running out of the fixed memory pool and recover gracefully + for (int tries = 0; tries < 2; ++tries) + { + try + { + SymbolInfo &symInfo = ms_cacheMap[addr]; + Dl_info info; + if (dladdr(addr, &info)) + { + symInfo.srcLib = uniqueString(info.dli_fname); + if ( stabsFind(addr, info, symInfo.srcFile, symInfo.srcLine) + || dwarfFind(addr, info, symInfo.srcFile, symInfo.srcLine)) + symInfo.found = true; + } + return symInfo; + } + catch (std::bad_alloc &) + { + ms_uniqueStringVector.clear(); + ms_cacheMap.clear(); + ms_memPoolUsed = 0; + } + } + + return ms_nullSym; +} + +// ---------------------------------------------------------------------- + +char const *SymbolCache::uniqueString(char const *s) +{ + for (UniqueStringVector::const_iterator i = ms_uniqueStringVector.begin(); i != ms_uniqueStringVector.end(); ++i) + if (!strcmp(s, *i)) + return *i; + char *newString = static_cast(memPoolAllocate((strlen(s)+8)&(~7))); + strcpy(newString, s); + ms_uniqueStringVector.push_back(newString); + return newString; +} + +// ---------------------------------------------------------------------- + +void *SymbolCache::memPoolAllocate(size_t size) +{ + ms_memPoolMutex.enter(); + if (ms_memPoolUsed+size > cms_memPoolMaxBytes) + { + ms_memPoolMutex.leave(); + throw std::bad_alloc(); + } + void *ret = ms_memPool+ms_memPoolUsed; + ms_memPoolUsed += size; + ms_memPoolMutex.leave(); + return ret; +} + +// ---------------------------------------------------------------------- + +bool lookupAddressInfo(void const *addr, char *retSrcLib, char *retSrcFile, int &retSrcLine, int stringBufLengths) +{ + SymbolCache::SymbolInfo const &symInfo = SymbolCache::lookup(addr); + if (retSrcLib && symInfo.srcLib) + { + strncpy(retSrcLib, symInfo.srcLib, stringBufLengths); + retSrcLib[stringBufLengths-1] = '\0'; + } + if (symInfo.found && retSrcFile) + { + strncpy(retSrcFile, symInfo.srcFile, stringBufLengths); + retSrcFile[stringBufLengths-1] = '\0'; + } + retSrcLine = symInfo.srcLine; + return symInfo.found; +} + +//// +// ====================================================================== + +void DebugHelp::install() +{ +} + +// ---------------------------------------------------------------------- + +void DebugHelp::remove() +{ +} + +// ---------------------------------------------------------------------- + +bool DebugHelp::lookupAddress(uint32 address, char *libName, char *fileName, int fileNameLength, int &line) +{ + return lookupAddressInfo(reinterpret_cast(address), libName, fileName, line, fileNameLength); +} + +// ---------------------------------------------------------------------- + +void DebugHelp::getCallStack(uint32 *callStack, int sizeOfCallStack) +{ + for (int i = 0; i < sizeOfCallStack; ++i) + callStack[i] = 0; + IGNORE_RETURN(backtrace(reinterpret_cast(callStack), sizeOfCallStack)); +} + +// ====================================================================== + diff --git a/engine/shared/library/sharedDebug/src/linux/DebugHelp.h b/engine/shared/library/sharedDebug/src/linux/DebugHelp.h new file mode 100644 index 00000000..5068ea1d --- /dev/null +++ b/engine/shared/library/sharedDebug/src/linux/DebugHelp.h @@ -0,0 +1,33 @@ +// ====================================================================== +// +// DebugHelp.h +// copyright 2000 Verant Interactive +// +// ====================================================================== + +#ifndef DEBUG_HELP_H +#define DEBUG_HELP_H + +#include "sharedDebug/FirstSharedDebug.h" + +// ====================================================================== + +typedef unsigned long uint32; + +// ====================================================================== + +class DebugHelp +{ +public: + + static void install(); + static void remove(); + + static void getCallStack(uint32 *callStack, int sizeOfCallStack); + static bool lookupAddress(uint32 address, char *libName, char *fileName, int fileNameLength, int &line); +}; + +// ====================================================================== + +#endif + diff --git a/engine/shared/library/sharedDebug/src/linux/DebugMonitor.cpp b/engine/shared/library/sharedDebug/src/linux/DebugMonitor.cpp new file mode 100644 index 00000000..59cd24bc --- /dev/null +++ b/engine/shared/library/sharedDebug/src/linux/DebugMonitor.cpp @@ -0,0 +1,445 @@ +// ====================================================================== +// +// DebugMonitor.cpp +// Copyright 2002, Sony Online Entertainment.h +// All rights reserved. +// +// ====================================================================== + +#include "sharedDebug/FirstSharedDebug.h" +#include "sharedDebug/DebugMonitor.h" + +#ifdef _DEBUG + +#include "sharedDebug/ConfigSharedDebugLinux.h" +#include "sharedDebug/Profiler.h" +#include "sharedFoundation/ConfigSharedFoundation.h" + +#include +#include +#include +#include +#include + +// ====================================================================== + +namespace DebugMonitorNamespace +{ + bool s_installed; + SCREEN *s_initialScreen; + WINDOW *s_initialWindow; + + FILE *s_ttyOutputFile; + FILE *s_ttyInputFile; + SCREEN *s_outputScreen; + WINDOW *s_outputWindow; +} + +using namespace DebugMonitorNamespace; + +// ====================================================================== +// Install the debug monitor subsystem +// +// Remarks: +// +// This routine will first attempt to install the selected debug monitor. + +void DebugMonitor::install(void) +{ + DEBUG_FATAL(s_installed, ("DebugMonitor already installed.")); + + //-- Check if user wants to use a TTY for the DebugMonitor. + if (!ConfigSharedDebugLinux::getUseTty()) + { + // Do not use the DebugMonitor. + DEBUG_REPORT_LOG(ConfigSharedDebugLinux::getLogTtySetup(), ("DebugMonitor: skipping install, ConfigSharedDebugLinux/useTty is false.\n")); + return; + } + + //-- Open and retrieve the name of the TTY file for output from the TTY Specification file. + char const *const configFilename = ConfigSharedDebugLinux::getDebugMonitorOutputFilename(); + + if (!configFilename || (*configFilename == '\0')) + { + DEBUG_WARNING(true, ("DebugMonitor(): skipping installation, no debugMonitorOutputFilename specified.")); + return; + } + + char ttyFilename[1024]; + + if (configFilename[0] != '@') + { + // filename is the real output filename (usually a device) for DebugMonitor output. + strncpy(ttyFilename, configFilename, sizeof(ttyFilename) - 1); + ttyFilename[sizeof(ttyFilename)-1] = 0; + } + else + { + // filename is actually an indirection filename, the contents of this file contains the real filename. + FILE *ttySpecifierFile = fopen(configFilename + 1, "r"); + if (!ttySpecifierFile) + { + DEBUG_WARNING(true, ("DebugMonitor: failed to open tty specifier file [%s]: [%s].", configFilename + 1, strerror(errno))); + return; + } + + // Retrieve the TTY file name. + fgets(ttyFilename, sizeof(ttyFilename), ttySpecifierFile); + fclose(ttySpecifierFile); + } + + // Strip trailing newline from TTY filename. + const int filenameLength = strlen(ttyFilename); + if (filenameLength > 0) + { + // chop off trailing newline if present. + if (ttyFilename[filenameLength - 1] == '\n') + ttyFilename[filenameLength - 1] = '\0'; + } + + //-- Open the TTY file for output. + DEBUG_REPORT_LOG(ConfigSharedDebugLinux::getLogTtySetup(), ("DebugMonitor: opening TTY file [%s] for reading and writing.\n", ttyFilename)); + FILE *s_ttyOutputFile = fopen(ttyFilename, "w"); + if (!s_ttyOutputFile) + { + DEBUG_WARNING(true, ("DebugMonitor: failed to open tty file for output [%s]: [%s].", ttyFilename, strerror(errno))); + return; + } + + //-- Open the TTY file for input. + FILE *s_ttyInputFile = fopen(ttyFilename, "r"); + if (!s_ttyInputFile) + { + DEBUG_WARNING(true, ("DebugMonitor: failed to open tty file for input [%s]: [%s].", ttyFilename, strerror(errno))); + fclose(s_ttyOutputFile); + return; + } + +#if 1 + // NOTE: -TRF- I would not expect this chunk of termios setup + // code to be necessary. My expectation is that it should + // be handled by the curs_inopts (see man page) options I have + // selected below. Alas, these are needed. + + //-- Setup profiler window input mode. + const int inputFd = STDIN_FILENO; + + // Get terminal attributes. + termios terminalAttributes; + if (tcgetattr(inputFd, &terminalAttributes) != 0) + { + DEBUG_WARNING(true, ("DebugMonitor: tcgetattr failed [%s].", strerror(errno))); + return; + } + + // Turn off line-processing mode. We want a character at a time. + terminalAttributes.c_lflag &= ~ICANON; + + // Turn off echo. + terminalAttributes.c_lflag &= ~ECHO; + + // Specify that read should return immediately (non-blocking). + terminalAttributes.c_cc[VMIN] = 0; + terminalAttributes.c_cc[VTIME] = 0; + + // Set terminal attributes. + if (tcsetattr(inputFd, TCSAFLUSH, &terminalAttributes) != 0) + { + DEBUG_WARNING(true, ("DebugMonitor: tcsetattr failed [%s].", strerror(errno))); + return; + } +#endif + + //-- Create a curses screen to represent the DebugMonitor output tty. + // NOTE: for now the curses input is hooked up to the application's + // standard input. Later we may want to change that to + // handle input from the output terminal, particularly to + // handle profiler modifications when the output window + // is active. + s_outputScreen = newterm(NULL, s_ttyOutputFile, stdin); + if (!s_outputScreen) + { + DEBUG_WARNING(true, ("DebugMonitor: newterm() failed [%s].", strerror(errno))); + fclose(s_ttyOutputFile); + fclose(s_ttyInputFile); + return; + } + + //-- Initialize the curses library. + s_initialWindow = initscr(); + if (!s_initialWindow) + { + DEBUG_WARNING(true, ("DebugMonitor: initscr(): failed [%s].", strerror(errno))); + fclose(s_ttyOutputFile); + fclose(s_ttyInputFile); + return; + } + + //-- Restore the stdout window to its previous state. We won't use stdout. + endwin(); + + //-- Set the default terminal to be the output TTY's terminal screen. + s_initialScreen = set_term(s_outputScreen); + if (!s_initialScreen) + { + DEBUG_WARNING(true, ("DebugMonitor: set_term(): failed [%s].", strerror(errno))); + delscreen(s_outputScreen); + fclose(s_ttyOutputFile); + fclose(s_ttyInputFile); + return; + } + + //-- Create the curses WINDOW at the full size of the TTY. + s_outputWindow = newwin(0, 0, 0, 0); + if (!s_outputWindow) + { + DEBUG_WARNING(true, ("DebugMonitor: newwin(): failed [%s].", strerror(errno))); + delscreen(s_outputScreen); + fclose(s_ttyOutputFile); + fclose(s_ttyInputFile); + return; + } + + //-- Destroy curses data structures associated with the stdout TTY + delwin(s_initialWindow); + delscreen(s_initialScreen); + + //-- Additional profiler input setup. + // Allow translation of arrow keys. + if (keypad(s_outputWindow, true) == ERR) + { + DEBUG_WARNING(true, ("DebugMonitor: keypad() failed [%s].", strerror(errno))); + return; + } + + //-- Add the removal routine to the ExitChain. + s_installed = true; + + // NOTE: this can't go on the exit chain because the MemoryManager + // will write to it way late in processing. + //ExitChain::add(remove, "DebugMonitor"); +} + +// ---------------------------------------------------------------------- + +void DebugMonitor::remove(void) +{ + if (!s_installed) + { + // Exit silently rather than FATAL. There's any number of reasons why + // we might not be installed, and the ExitChain does not call remove, + // so this should be valid. + return; + } + + s_installed = false; + + //-- Restore the TTY's state. + endwin(); + + //-- Delete curses data structures associated with the output terminal. + delwin(s_outputWindow); + s_outputWindow = 0; + + delscreen(s_outputScreen); + s_outputScreen = 0; + + //-- Close file handle to output terminal. + fclose(s_ttyOutputFile); + s_ttyOutputFile = 0; + + fclose(s_ttyInputFile); + s_ttyInputFile = 0; +} + +// ---------------------------------------------------------------------- +/** + * Set the debug window's z-order. + */ + +void DebugMonitor::setBehindWindow(HWND window) +{ + UNREF(window); +} + +// ---------------------------------------------------------------------- +/** + * Clear the debug monitor and home the cursor. + * + * If the mono monitor is not installed, this routine does nothing. + * + * This routine will clear the contents of the debug monitor, reset the screen + * offset to 0, and move the cursor to the upper left corner of the screen. + * + * @see DebugMonitor::home(), DebugMonitor::clearToCursor() + */ + +void DebugMonitor::clearScreen(void) +{ + //-- Ignore request if DebugMonitor isn't installed. + if (!s_installed) + return; + + //-- Tell curses to clear the virtual screen. The actual + // clear will not take effect until the next showOutput() call + // is made. + IGNORE_RETURN(wclear(s_outputWindow)); + + //-- Goto upper left corner. + gotoXY(0, 0); +} + +// ---------------------------------------------------------------------- +/** + * Clear the debug monitor to the current cursor position and home the cursor. + * + * If the debug monitor is not installed, this routine does nothing. + * + * This routine will clear the contents of the debug monitor only up to the + * cursor position. If the cursor is not very far down on the screen, + * this routine may be significantly more efficient clearing the screen. + * + * It will also move the cursor to the upper left corner of the screen. + * + * @see DebugMonitor::clearScreen(), DebugMonitor::home() + */ + +void DebugMonitor::clearToCursor(void) +{ + //-- Ignore request if DebugMonitor isn't installed. + if (!s_installed) + return; + + //-- I don't see this functionality in curses. Just clear + // the entire screen. + clearScreen(); +} + +// ---------------------------------------------------------------------- +/** + * Position the cursor on the debug monitor screen. + * + * If the debug monitor is not installed, this routine does nothing. + * + * All printing happens at the cursor position. + * + * @param x New X position for the cursor + * @param y New Y position for the cursor + */ + +void DebugMonitor::gotoXY(int x, int y) +{ + //-- Ignore request if DebugMonitor isn't installed. + if (!s_installed) + return; + + //-- Move the cursor position to the specified location. + const int result = wmove(s_outputWindow, y, x); + DEBUG_REPORT_LOG(result == ERR, ("DebugMonitor: wmove(%d, %d) failed [%s].\n", y, x, strerror(errno))); + UNREF(result); +} + +// ---------------------------------------------------------------------- +/** + * Display a string on the debug monitor. + * * If the debug monitor is not installed, this routine does nothing. + * + * Printing occurs from the cursor position. + * + * Newline characters '\n' will cause the cursor position to advance to the + * beginning of the next line. If the cursor is already on the last line of + * the screen, the screen will scroll up one line and the cursor will move to + * the beginning of the last line. + * + * The backspace character '\b' will cause the cursor to move one character + * backwards. If at the beginning of the line, the cursor will move to the + * end of the previous line. If already on the first line of the screen, the + * cursor position and screen contents will be unchanged. + * + * All other characters are placed directly into the text frame buffer. + * After each character, the cursor will be logically advanced one + * character forward. If the cursor was on the last column, it will advance + * to the next line. If the cursor was already on the last line, the screen + * will be scrolled up one line and the cursor will move to the beginning of + * the last line. + * + * @param string String to display on the debug monitor + */ + +void DebugMonitor::print(const char *string) +{ + //-- Ignore request if DebugMonitor isn't installed. + if (!s_installed) + return; + + const int result = wprintw(s_outputWindow, const_cast(string)); + DEBUG_REPORT_LOG(result == ERR, ("WARNING: DebugMonitor: wprintw failed [%s].\n", strerror(errno))); + UNREF(result); +} + +// ---------------------------------------------------------------------- +/** + * Ensure all changes to the DebugMonitor have taken effect by the time + * this function returns. + * + * Note: some platforms may do nothing here. The Win32 platform does not + * require flushing. The Linux platform does. Call it assuming + * that it is needed. It will be a no-op when not required. + */ + +void DebugMonitor::flushOutput() +{ + //-- Ignore request if DebugMonitor isn't installed. + if (!s_installed) + return; + + //-- Tell curses to refresh the output window so that + // the virtual curses display is rendered to the real + // terminal display. + const int result = wrefresh(s_outputWindow); + DEBUG_REPORT_LOG(result == ERR, ("WARNING: DebugMonitor: wrefresh failed [%s].\n", strerror(errno))); + UNREF(result); + + //-- Handle tty input here. This is somewhat hacky. Unix tty input + // and output currently are intimately tied to the profiler. Later + // this can be separated out so we can handle multiple TTYs for + // multiple purposes, such as for handling a TTY-based popup debug + // menu for on-the-fly options changing. + const int input = wgetch(s_outputWindow); + if (input != ERR) + { +#if 0 + DEBUG_REPORT_LOG(true, ("DebugMonitor: received key [index=%d].\n", input)); +#endif + + //-- Handle input. + switch (input) + { + case '8': + case KEY_UP: + Profiler::selectionMoveUp(); + break; + + case '9': + case KEY_DOWN: + Profiler::selectionMoveDown(); + break; + + case '0': + case 10: + case KEY_LEFT: + case KEY_RIGHT: + Profiler::selectionToggleExpanded(); + break; + + default: + // Nothing to do. + break; + } + } +} + +// ====================================================================== + +#endif + diff --git a/engine/shared/library/sharedDebug/src/linux/DebugMonitor.h b/engine/shared/library/sharedDebug/src/linux/DebugMonitor.h new file mode 100644 index 00000000..092f01c6 --- /dev/null +++ b/engine/shared/library/sharedDebug/src/linux/DebugMonitor.h @@ -0,0 +1,67 @@ +// ====================================================================== +// +// DebugMonitor.h +// Portions Copyright 1998, Bootprint Entertainment, Inc. +// Portions Copyright 2002, Sony Online Entertainment, Inc. +// +// ====================================================================== + +#ifndef INCLUDED_DebugMonitor_H +#define INCLUDED_DebugMonitor_H + +// ====================================================================== + +#ifdef _DEBUG + +class DebugMonitor +{ +private: + +public: + + static void install(void); + static void remove(void); + + typedef void* HWND; + + static void setBehindWindow(HWND window); + + static void clearScreen(void); + static void clearToCursor(void); + + static void home(void); + static void gotoXY(int x, int y); + static void print(const char *string); + + static void flushOutput(); +}; + +#endif + +// ====================================================================== +// Move the cursor to the upper left hand corner of the mono monitor screen +// +// Remarks: +// +// If the debug monitor is not installed, this routine does nothing. +// +// All printing happens at the cursor position. +// +// This routine is identical to calling gotoXY(0,0); +// +// See Also: +// +// DebugMonitor::gotoXY() + +#ifdef _DEBUG + +inline void DebugMonitor::home(void) +{ + gotoXY(0,0); +} + +#endif + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedDebug/src/linux/PerformanceTimer.cpp b/engine/shared/library/sharedDebug/src/linux/PerformanceTimer.cpp new file mode 100644 index 00000000..68c36d8c --- /dev/null +++ b/engine/shared/library/sharedDebug/src/linux/PerformanceTimer.cpp @@ -0,0 +1,125 @@ +//=================================================================== // +// +// PerformanceTimer.cpp +// copyright 2000-2004 Sony Online Entertainment +// All Rights Reserved +// +//=================================================================== + +#include "sharedFoundation/FirstSharedFoundation.h" +#include "sharedDebug/PerformanceTimer.h" + +//=================================================================== + +#include + +//=================================================================== + +void PerformanceTimer::install() +{ +} + +//------------------------------------------------------------------- + +PerformanceTimer::PerformanceTimer() +{ + startTime.tv_sec = 0; + startTime.tv_usec = 0; + stopTime.tv_sec = 0; + stopTime.tv_usec = 0; +} + +//------------------------------------------------------------------- + +PerformanceTimer::~PerformanceTimer() +{ +} + +//------------------------------------------------------------------- + +void PerformanceTimer::start() +{ + int const result = gettimeofday(&startTime, NULL); + FATAL(result != 0,("PerformanceTimer::start failed")); +} + +//------------------------------------------------------------------- + +void PerformanceTimer::resume() +{ + long sec = stopTime.tv_sec - startTime.tv_sec; + long usec = stopTime.tv_usec - startTime.tv_usec; + if(usec < 0) + { + --sec; + usec += 1000000; + } + + int const result = gettimeofday(&startTime, NULL); + FATAL(result != 0,("PerformanceTimer::resume failed")); + + startTime.tv_sec -= sec; + startTime.tv_usec -= usec; + if(startTime.tv_usec < 0) + { + --startTime.tv_sec; + startTime.tv_usec += 1000000; + } +} + +//------------------------------------------------------------------- + +void PerformanceTimer::stop() +{ + int result = gettimeofday(&stopTime, NULL); + FATAL(result != 0,("PerformanceTimer::start failed")); +} + +//------------------------------------------------------------------- + +float PerformanceTimer::getElapsedTime() const +{ + long sec = stopTime.tv_sec - startTime.tv_sec; + long usec = stopTime.tv_usec - startTime.tv_usec; + if(usec < 0) + { + --sec; + usec += 1000000; + } + return static_cast(sec) +(static_cast(usec) / 1000000.0f); +} + +//------------------------------------------------------------------- + +float PerformanceTimer::getSplitTime() const +{ + timeval currentTime; + + int const result = gettimeofday(¤tTime, NULL); + FATAL(result != 0,("PerformanceTimer::getSplitTime failed")); + + long sec = currentTime.tv_sec - startTime.tv_sec; + long usec = currentTime.tv_usec - startTime.tv_usec; + if(usec < 0) + { + --sec; + usec += 1000000; + } + + return static_cast(sec) +(static_cast(usec) / 1000000.0f); +} + +//------------------------------------------------------------------- + +void PerformanceTimer::logElapsedTime(const char* string) const +{ + UNREF(string); + +#ifdef _DEBUG + static char buffer [1000]; + sprintf(buffer, "%s : %1.5f seconds\n", string ? string : "null", getElapsedTime()); + DEBUG_REPORT_LOG_PRINT(true, ("%s", buffer)); +#endif +} + +//------------------------------------------------------------------- diff --git a/engine/shared/library/sharedDebug/src/linux/PerformanceTimer.h b/engine/shared/library/sharedDebug/src/linux/PerformanceTimer.h new file mode 100644 index 00000000..4e933ae1 --- /dev/null +++ b/engine/shared/library/sharedDebug/src/linux/PerformanceTimer.h @@ -0,0 +1,51 @@ +//=================================================================== // +// +// PerformanceTimer.h +// copyright 2000-2004 Sony Online Entertainment +// All Rights Reserved +// +//=================================================================== + +#ifndef INCLUDED_PerformaceTimer_H +#define INCLUDED_PerformaceTimer_H + +//=================================================================== + +#include +#include + +//=================================================================== + +class PerformanceTimer +{ +public: + + static void install(); + +public: + + PerformanceTimer(); + ~PerformanceTimer(); + + void start(); + void resume(); + void stop(); + + float getElapsedTime() const; + float getSplitTime() const; + void logElapsedTime(const char* string) const; + +private: + + timeval startTime; + timeval stopTime; + +private: + + PerformanceTimer(PerformanceTimer const &); + PerformanceTimer & operator=(PerformanceTimer const &); +}; + +//=================================================================== + +#endif diff --git a/engine/shared/library/sharedDebug/src/linux/ProfilerTimer.cpp b/engine/shared/library/sharedDebug/src/linux/ProfilerTimer.cpp new file mode 100644 index 00000000..73aa4150 --- /dev/null +++ b/engine/shared/library/sharedDebug/src/linux/ProfilerTimer.cpp @@ -0,0 +1,39 @@ +// ====================================================================== +// +// ProfilerTimer.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedDebug/FirstSharedDebug.h" +#include "sharedDebug/ProfilerTimer.h" + +// ====================================================================== + +void ProfilerTimer::install() +{ +} + +// ---------------------------------------------------------------------- + +void ProfilerTimer::getTime(Type &time) +{ + timeval tv; + gettimeofday(&tv, 0); + time = static_cast(tv.tv_sec)*static_cast(1000000)+static_cast(tv.tv_usec); +} + +// ---------------------------------------------------------------------- + +void ProfilerTimer::getCalibratedTime(Type &time, Type &frequency) +{ + getTime(time); + frequency = 1000000; +} + +void ProfilerTimer::getFrequency(Type &frequency) +{ + frequency = 1000000; +} + +// ====================================================================== diff --git a/engine/shared/library/sharedDebug/src/linux/ProfilerTimer.h b/engine/shared/library/sharedDebug/src/linux/ProfilerTimer.h new file mode 100644 index 00000000..c5a5ae75 --- /dev/null +++ b/engine/shared/library/sharedDebug/src/linux/ProfilerTimer.h @@ -0,0 +1,29 @@ +// ====================================================================== +// +// ProfilerTimer.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_ProfilerTimer_H +#define INCLUDED_ProfilerTimer_H + +// ====================================================================== + +class ProfilerTimer +{ +public: + + typedef uint64 Type; + +public: + + static void install(); + static void getTime(Type &type); + static void getCalibratedTime(Type &time, Type &frequency); + static void getFrequency(Type &frequency); +}; + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedDebug/src/shared/CallStack.cpp b/engine/shared/library/sharedDebug/src/shared/CallStack.cpp new file mode 100644 index 00000000..1df90532 --- /dev/null +++ b/engine/shared/library/sharedDebug/src/shared/CallStack.cpp @@ -0,0 +1,85 @@ +// ====================================================================== +// +// CallStack.cpp +// asommers +// +// Copyright 2004, Sony Online Entertainment +// All Rights Reserved +// +// ====================================================================== + +#include "sharedDebug/FirstSharedDebug.h" +#include "sharedDebug/CallStack.h" + +#include "sharedDebug/DebugHelp.h" + +#include + +// ====================================================================== + +#define DESIRED_CALLSTACK_DEPTH 24 + +#define CALLSTACK_DEPTH std::min(static_cast(CallStack::S_callStack), static_cast(DESIRED_CALLSTACK_DEPTH + 2)) + +// ====================================================================== + +CallStack::CallStack() +{ + Zero(m_callStack); +} + +// ---------------------------------------------------------------------- + +CallStack::~CallStack() +{ +} + +// ---------------------------------------------------------------------- + +bool CallStack::operator<(const CallStack &o) const +{ + return memcmp(m_callStack, o.m_callStack, sizeof(m_callStack))<0; +} + +// ---------------------------------------------------------------------- + +void CallStack::sample() +{ + DebugHelp::getCallStack(m_callStack, CALLSTACK_DEPTH); +} + +// ---------------------------------------------------------------------- + +void CallStack::debugPrint() const +{ + char libName[256]; + char fileName[256]; + int line = 0; + + for (size_t i = 2; i < CALLSTACK_DEPTH; ++i) + { + if (DebugHelp::lookupAddress(m_callStack[i], libName, fileName, sizeof(fileName), line)) + DEBUG_REPORT_PRINT(true, (" (0x%08X) %s(%d) : caller %d\n", static_cast(m_callStack[i]), fileName, line, i - 1)); + else + DEBUG_REPORT_PRINT(true, (" unknown(0x%08X) : caller %d\n", static_cast(m_callStack[i]), i - 1)); + } +} + +// ---------------------------------------------------------------------- + +void CallStack::debugLog() const +{ + char libName[256]; + char fileName[256]; + int line = 0; + + for (size_t i = 2; i < CALLSTACK_DEPTH && m_callStack[i]!=0; ++i) + { + if (DebugHelp::lookupAddress(m_callStack[i], libName, fileName, sizeof(fileName), line)) + DEBUG_REPORT_LOG(true, (" (0x%08X) %s(%d) : caller %d\n", static_cast(m_callStack[i]), fileName, line, i - 1)); + else + DEBUG_REPORT_LOG(true, (" unknown(0x%08X) : caller %d\n", static_cast(m_callStack[i]), i - 1)); + } +} + +// ====================================================================== diff --git a/engine/shared/library/sharedDebug/src/shared/CallStack.h b/engine/shared/library/sharedDebug/src/shared/CallStack.h new file mode 100644 index 00000000..376f0ed3 --- /dev/null +++ b/engine/shared/library/sharedDebug/src/shared/CallStack.h @@ -0,0 +1,48 @@ +// ====================================================================== +// +// CallStack.h +// asommers +// +// Copyright 2004, Sony Online Entertainment +// All Rights Reserved +// +// ====================================================================== + +#ifndef INCLUDED_CallStack_H +#define INCLUDED_CallStack_H + +// ====================================================================== + +class CallStack +{ +public: + + +public: + + CallStack(); + ~CallStack(); + + bool operator<(const CallStack &o) const; + + void sample(); + + void debugPrint() const; + void debugLog() const; + +private: + + enum + { + //-- This is to avoid memory allocations and needing to free the memory + S_callStack = 32 + }; + +private: + + uint32 m_callStack[S_callStack]; +}; + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedDebug/src/shared/CallStackCollector.cpp b/engine/shared/library/sharedDebug/src/shared/CallStackCollector.cpp new file mode 100644 index 00000000..8e401038 --- /dev/null +++ b/engine/shared/library/sharedDebug/src/shared/CallStackCollector.cpp @@ -0,0 +1,262 @@ +// ====================================================================== +// +// CallStackCollector.cpp +// asommers +// +// Copyright 2004, Sony Online Entertainment +// All Rights Reserved +// +// ====================================================================== + +#include "sharedDebug/FirstSharedDebug.h" +#include "sharedDebug/CallStackCollector.h" + +#include "sharedDebug/DebugFlags.h" +#include "sharedDebug/DebugHelp.h" +#include "sharedFoundation/ConstCharCrcString.h" +#include "sharedFoundation/Crc.h" +#include "sharedFoundation/ExitChain.h" +#include "sharedFoundation/LessPointerComparator.h" +#include "sharedFoundation/PersistentCrcString.h" + +#include +#include +#include + +// ====================================================================== + +#define DESIRED_CALLSTACK_DEPTH 24 + +#define CALLSTACK_DEPTH DESIRED_CALLSTACK_DEPTH + 2 + +// ====================================================================== + +namespace CallStackCollectorNamespace +{ + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + void remove(); + void clear(); + void debugReport(); + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + class Node + { + public: + + explicit Node(char const * name); + ~Node(); + + CrcString const & getName() const; + void addCallStack(uint32 * callStack); + + void debugReport() const; + + private: + + struct CallStackEntry + { + public: + + static bool compare(CallStackEntry const * a, CallStackEntry const * b); + + public: + + uint32 * m_callStack; + int m_calls; + }; + + private: + + Node(Node const &); + Node & operator=(Node const &); + + private: + + PersistentCrcString const m_name; + typedef std::map CallStackEntryMap; + CallStackEntryMap m_callStackEntryMap; + }; + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + typedef std::map NodeMap; + NodeMap ms_nodeMap; + + bool ms_debugReport; + bool ms_enabled; + bool ms_clear; + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +} + +using namespace CallStackCollectorNamespace; + +// ====================================================================== + +bool CallStackCollectorNamespace::Node::CallStackEntry::compare(CallStackEntry const * const a, CallStackEntry const * const b) +{ + return b->m_calls < a->m_calls; +} + +// ====================================================================== + +CallStackCollectorNamespace::Node::Node(char const * const name) : + m_name(name, false), + m_callStackEntryMap() +{ +} + +// ---------------------------------------------------------------------- + +CallStackCollectorNamespace::Node::~Node() +{ + for (CallStackEntryMap::iterator iter = m_callStackEntryMap.begin(); iter != m_callStackEntryMap.end(); ++iter) + delete [] iter->second.m_callStack; +} + +// ---------------------------------------------------------------------- + +CrcString const & CallStackCollectorNamespace::Node::getName() const +{ + return m_name; +} + +// ---------------------------------------------------------------------- + +void CallStackCollectorNamespace::Node::addCallStack(uint32 * const callStack) +{ + //-- Compute crc of memory + uint32 const crc = Crc::calculate(callStack, sizeof(uint32) * CALLSTACK_DEPTH); + + //-- Find callstack in list + CallStackEntryMap::iterator iter = m_callStackEntryMap.find(crc); + if (iter != m_callStackEntryMap.end()) + { + //-- Increment count of existing callstack + ++iter->second.m_calls; + } + else + { + //-- Create new callstack + uint32 * const newCallStack = new uint32[CALLSTACK_DEPTH]; + memcpy(newCallStack, callStack, sizeof(uint32) * CALLSTACK_DEPTH); + + CallStackEntry callStackEntry; + callStackEntry.m_callStack = newCallStack; + callStackEntry.m_calls = 1; + m_callStackEntryMap.insert(std::make_pair(crc, callStackEntry)); + } +} + +// ---------------------------------------------------------------------- + +void CallStackCollectorNamespace::Node::debugReport() const +{ + //-- Find top 3 entries per node + typedef std::vector CallStackEntryList; + CallStackEntryList callStackEntryList; + + for (CallStackEntryMap::const_iterator iter = m_callStackEntryMap.begin(); iter != m_callStackEntryMap.end(); ++iter) + callStackEntryList.push_back(&iter->second); + + std::stable_sort(callStackEntryList.begin(), callStackEntryList.end(), CallStackEntry::compare); + + //-- Print out entries + size_t const c_maximumNumberOfPrintedEntries = 3; + for (size_t i = 0; i < std::min(c_maximumNumberOfPrintedEntries, callStackEntryList.size()); ++i) + { + CallStackEntry const * const callStackEntry = callStackEntryList[i]; + + char libName[256]; + char fileName[256]; + int line = 0; + + REPORT_LOG(true, ("CallStackCollector(%s:%i) callstack called %d times\n", m_name.getString(), i, callStackEntry->m_calls)); + + for (size_t j = 2; j < CALLSTACK_DEPTH; ++j) + { + if (DebugHelp::lookupAddress(callStackEntry->m_callStack[j], libName, fileName, sizeof(fileName), line)) + REPORT_LOG(true, (" %s(%d) : caller %d\n", fileName, line, j - 1)); + else + REPORT_LOG(true, (" unknown(0x%08X) : caller %d\n", static_cast(callStackEntry->m_callStack[j]), j - 1)); + } + } +} + +// ====================================================================== + +void CallStackCollector::install() +{ + DebugFlags::registerFlag(ms_enabled, "SharedDebug/CallStackCollector", "enabled"); + DebugFlags::registerFlag(ms_debugReport, "SharedDebug/CallStackCollector", "debugReport", debugReport); + DebugFlags::registerFlag(ms_clear, "SharedDebug/CallStackCollector", "clear"); + ExitChain::add(CallStackCollectorNamespace::remove, "CallStackCollectorNamespace::remove"); +} + +// ---------------------------------------------------------------------- + +void CallStackCollector::sample(char const * const name) +{ + if (ms_clear) + clear(); + + if (!ms_enabled) + return; + + //-- Get the desired node + Node * node = 0; + ConstCharCrcString const crcName(name); + NodeMap::iterator iter = ms_nodeMap.find((const CrcString*)&crcName); + if (iter != ms_nodeMap.end()) + node = iter->second; + else + { + node = new Node(name); + ms_nodeMap.insert(std::make_pair(&node->getName(), node)); + } + + //-- Sample the callstack + uint32 callStack[CALLSTACK_DEPTH]; + DebugHelp::getCallStack(&callStack[0], CALLSTACK_DEPTH); + + //-- Add to the node + node->addCallStack(&callStack[0]); +} + +// ---------------------------------------------------------------------- + +void CallStackCollectorNamespace::remove() +{ + DebugFlags::unregisterFlag(ms_debugReport); + + clear(); +} + +// ---------------------------------------------------------------------- + +void CallStackCollectorNamespace::clear() +{ + for (NodeMap::iterator iter = ms_nodeMap.begin(); iter != ms_nodeMap.end(); ++iter) + delete iter->second; + + ms_nodeMap.clear(); + + ms_clear = false; +} + +// ---------------------------------------------------------------------- + +void CallStackCollectorNamespace::debugReport() +{ + if (ms_debugReport) + { + for (NodeMap::iterator iter = ms_nodeMap.begin(); iter != ms_nodeMap.end(); ++iter) + iter->second->debugReport(); + + ms_debugReport = false; + } +} + +// ====================================================================== diff --git a/engine/shared/library/sharedDebug/src/shared/CallStackCollector.h b/engine/shared/library/sharedDebug/src/shared/CallStackCollector.h new file mode 100644 index 00000000..5040b0c4 --- /dev/null +++ b/engine/shared/library/sharedDebug/src/shared/CallStackCollector.h @@ -0,0 +1,27 @@ +// ====================================================================== +// +// CallStackCollector.h +// asommers +// +// Copyright 2004, Sony Online Entertainment +// All Rights Reserved +// +// ====================================================================== + +#ifndef INCLUDED_CallStackCollector_H +#define INCLUDED_CallStackCollector_H + +// ====================================================================== + +class CallStackCollector +{ +public: + + static void install(); + + static void sample(char const * name); +}; + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedDebug/src/shared/DataLint.cpp b/engine/shared/library/sharedDebug/src/shared/DataLint.cpp new file mode 100644 index 00000000..a826923d --- /dev/null +++ b/engine/shared/library/sharedDebug/src/shared/DataLint.cpp @@ -0,0 +1,907 @@ +// ============================================================================ +// +// DataLint.cpp +// Copyright Sony Online Entertainment +// +// ============================================================================ + +#include "sharedDebug/FirstSharedDebug.h" +#include "sharedDebug/DataLint.h" + +#include "sharedFoundation/ConfigSharedFoundation.h" +#include "sharedFoundation/ExitChain.h" + +#include +#include +#include +#include +#include + +// ============================================================================ + +namespace DataLintNamespace +{ + enum Mode + { + M_client, + M_server + }; + + typedef stdvector::fwd StringList; + typedef std::pair > WarningPair; + typedef stdvector::fwd WarningList; + typedef bool (*AssetFunction)(char const *); + + bool ms_installed = false; + DataLint::AssetType m_currentAssetType = DataLint::AT_invalid; + bool m_enabled = false; + WarningList * m_warningList = NULL; + StringList * m_assetStack = NULL; + DataLint::StringPairList * m_assetList = NULL; + std::string m_dataLintCategorizedAssetsFile("DataLint_CategorizedAssets.txt"); + std::string m_dataLintUnSupportedAssetsFile("DataLint_UnSupportedAssets.txt"); + Mode m_mode = M_client; + + bool isAnAppearanceAsset(char const *text); + bool isAnArrangementDescriptorAsset(char const *text); + bool isALocalizedStringAsset(char const *text); + bool isAnObjectTemplateAsset(char const *text); + bool isAPortalProprtyAsset(char const *text); + bool isAShaderTemplateAsset(char const *text); + bool isASkyBoxAsset(char const *text); + bool isASlotDescriptorAsset(char const *text); + bool isASoundAsset(char const *text); + bool isATerrainAsset(char const *text); + bool isATextureAsset(char const *text); + bool isATextureRendererTemplateAsset(char const *text); + bool isAnUnSupportedAsset(char const *text); + + void fixUpSlashes(char *text); + DataLint::StringPairList getList(int const reserveCount, AssetFunction listTypeFunction); + void writeToCategorizedFile(FILE *fp, char const *title, DataLint::AssetType const listType); + std::string formatErrorMessage(WarningPair &warningPair, int ¤tErrorCount); + void writeError(FILE *fp, WarningPair &warningPair, int ¤tErrorCount); +} + +using namespace DataLintNamespace; + +// ============================================================================ +// +// DataLint +// +// ============================================================================ + +void DataLint::install() +{ + DEBUG_FATAL(ms_installed, ("DataLint::install: already installed")); + ms_installed = true; + m_enabled = true; + + m_warningList = new WarningList(); + m_warningList->reserve(4096); + + m_assetStack = new StringList (); + + m_assetList = new StringPairList(); + m_assetList->reserve(4096); + + FatalSetThrowExceptions(true); + + ExitChain::add(&remove, "DataLint::remove"); +} + +//----------------------------------------------------------------------------- + +bool DataLint::isInstalled() +{ + return ms_installed; +} + +//----------------------------------------------------------------------------- + +void DataLint::report() +{ + if (m_enabled) + { + // Error Assets + + DEBUG_REPORT_LOG_PRINT(true, ("Writing error files\n")); + + // Setup filenames for client versus server mode + + std::string dataLintErrorsAll((m_mode == M_client) ? "DataLint_Errors_All.txt" : "DataLintServer_Errors_All.txt"); + std::string dataLintErrorsAllFatal((m_mode == M_client) ? "DataLint_Errors_All_Fatal.txt" : "DataLintServer_Errors_All_Fatal.txt"); + std::string dataLintErrorsAllWarning((m_mode == M_client) ? "DataLint_Errors_All_Warning.txt" : "DataLintServer_Errors_All_Warning.txt"); + std::string dataLintErrorsAllObjectTemplate((m_mode == M_client) ? "DataLint_Errors_ObjectTemplate.txt" : "DataLintServer_Errors_ObjectTemplate.txt"); + + // Server/client + + FILE *assetErrorFileAll = fopen(dataLintErrorsAll.c_str(), "wt"); + FILE *assetErrorFileAllFatal = fopen(dataLintErrorsAllFatal.c_str(), "wt"); + FILE *assetErrorFileAllWarning = fopen(dataLintErrorsAllWarning.c_str(), "wt"); + FILE *assetErrorFileObjectTemplate = fopen(dataLintErrorsAllObjectTemplate.c_str(), "wt"); + + // Client only + + FILE *assetErrorFileAppearance = NULL; + FILE *assetErrorFileArrangementDescriptor = NULL; + FILE *assetErrorFileLocalizedStringTable = NULL; + FILE *assetErrorFilePortalProperty = NULL; + FILE *assetErrorFileShaderTemplate = NULL; + FILE *assetErrorFileSkyBox = NULL; + FILE *assetErrorFileSlotDescriptor = NULL; + FILE *assetErrorFileSoundTemplate = NULL; + FILE *assetErrorFileTerrain = NULL; + FILE *assetErrorFileTexture = NULL; + FILE *assetErrorFileTextureRenderer = NULL; + + if (m_mode == M_client) + { + assetErrorFileAppearance = fopen("DataLint_Errors_Appearance.txt", "wt"); + assetErrorFileArrangementDescriptor = fopen("DataLint_Errors_ArrangementDescriptor.txt", "wt"); + assetErrorFileLocalizedStringTable = fopen("DataLint_Errors_LocalizedStringTable.txt", "wt"); + assetErrorFilePortalProperty = fopen("DataLint_Errors_PortalProperty.txt", "wt"); + assetErrorFileShaderTemplate = fopen("DataLint_Errors_ShaderTemplate.txt", "wt"); + assetErrorFileSkyBox = fopen("DataLint_Errors_SkyBox.txt", "wt"); + assetErrorFileSlotDescriptor = fopen("DataLint_Errors_SlotDescriptor.txt", "wt"); + assetErrorFileSoundTemplate = fopen("DataLint_Errors_SoundTemplate.txt", "wt"); + assetErrorFileTerrain = fopen("DataLint_Errors_Terrain.txt", "wt"); + assetErrorFileTexture = fopen("DataLint_Errors_Texture.txt", "wt"); + assetErrorFileTextureRenderer = fopen("DataLint_Errors_TextureRenderer.txt", "wt"); + } + + // Might want to think of a better way to keep track of the number of errors for each specific asset type. + + int totalErrorCountAll = 1; + int totalErrorCountAllFatal = 1; + int totalErrorCountAllWarning = 1; + int totalErrorCountAppearance = 1; + int totalErrorCountArrangementDescriptor = 1; + int totalErrorCountLocalizedStringTable = 1; + int totalErrorCountObjectTemplate = 1; + int totalErrorCountPortalProperty = 1; + int totalErrorCountShaderTemplate = 1; + int totalErrorCountSkyBox = 1; + int totalErrorCountSlotDescriptor = 1; + int totalErrorCountSoundTemplate = 1; + int totalErrorCountTerrain = 1; + int totalErrorCountTexture = 1; + int totalErrorCountTextureRenderer = 1; + + typedef std::set StringSet; + StringSet duplicateStringSet; + + if (assetErrorFileAll) + { + WarningList::iterator warningListIter = m_warningList->begin(); + + for (; warningListIter != m_warningList->end(); ++warningListIter) + { + // Build a duplicate string + + std::string duplicateString(warningListIter->first); + + StringList::const_iterator stringStackIter = warningListIter->second.second.begin(); + + for (; stringStackIter != warningListIter->second.second.end(); ++stringStackIter) + { + duplicateString += *stringStackIter; + } + + if (duplicateStringSet.find(duplicateString) == duplicateStringSet.end()) + { + // This is the first instance of this error, so log it + + duplicateStringSet.insert(duplicateString); + } + else + { + // We found a duplicate warning, so don't show it twice + + continue; + } + + // Write the error to the appropriate error file + + switch (warningListIter->second.first) + { + case AT_appearance: + { + if (m_mode == M_client) + { + writeError(assetErrorFileAppearance, *warningListIter, totalErrorCountAppearance); + } + } + break; + case AT_arrangementDescriptor: + { + if (m_mode == M_client) + { + writeError(assetErrorFileArrangementDescriptor, *warningListIter, totalErrorCountArrangementDescriptor); + } + } + break; + case AT_localizedStringTable: + { + if (m_mode == M_client) + { + writeError(assetErrorFileLocalizedStringTable, *warningListIter, totalErrorCountLocalizedStringTable); + } + } + break; + case AT_objectTemplate: + { + writeError(assetErrorFileObjectTemplate, *warningListIter, totalErrorCountObjectTemplate); + } + break; + case AT_portalProperty: + { + if (m_mode == M_client) + { + writeError(assetErrorFilePortalProperty, *warningListIter, totalErrorCountPortalProperty); + } + } + break; + case AT_shaderTemplate: + { + if (m_mode == M_client) + { + writeError(assetErrorFileShaderTemplate, *warningListIter, totalErrorCountShaderTemplate); + } + } + break; + case AT_skyBox: + { + if (m_mode == M_client) + { + writeError(assetErrorFileSkyBox, *warningListIter, totalErrorCountSkyBox); + } + } + break; + case AT_slotDescriptor: + { + if (m_mode == M_client) + { + writeError(assetErrorFileSlotDescriptor, *warningListIter, totalErrorCountSlotDescriptor); + } + } + break; + case AT_soundTemplate: + { + if (m_mode == M_client) + { + writeError(assetErrorFileSoundTemplate, *warningListIter, totalErrorCountSoundTemplate); + } + } + break; + case AT_terrain: + { + if (m_mode == M_client) + { + writeError(assetErrorFileTerrain, *warningListIter, totalErrorCountTerrain); + } + } + break; + case AT_texture: + { + if (m_mode == M_client) + { + writeError(assetErrorFileTexture, *warningListIter, totalErrorCountTexture); + } + } + break; + case AT_textureRendererTemplate: + { + if (m_mode == M_client) + { + writeError(assetErrorFileTextureRenderer, *warningListIter, totalErrorCountTextureRenderer); + } + } + break; + default: + { + DEBUG_REPORT_LOG_PRINT(true, ("DataLint::remove() - Unsupported asset type: %s", warningListIter->first.c_str())); + } + break; + } + + // Write the error to the master error file + + writeError(assetErrorFileAll, *warningListIter, totalErrorCountAll); + + // If this is a fatal, write to the fatal list + + if (strstr(warningListIter->first.c_str(), "FATAL:")) + { + writeError(assetErrorFileAllFatal, *warningListIter, totalErrorCountAllFatal); + } + + // If a warning, write to the warning list + + if (strstr(warningListIter->first.c_str(), "WARNING:")) + { + writeError(assetErrorFileAllWarning, *warningListIter, totalErrorCountAllWarning); + } + } + + if (assetErrorFileAll) { fclose(assetErrorFileAll); } + if (assetErrorFileAllFatal) { fclose(assetErrorFileAllFatal); } + if (assetErrorFileAllWarning) { fclose(assetErrorFileAllWarning); } + if (assetErrorFileObjectTemplate) { fclose(assetErrorFileObjectTemplate); } + + if (m_mode == M_client) + { + if (assetErrorFileAppearance) { fclose(assetErrorFileAppearance); } + if (assetErrorFileArrangementDescriptor) { fclose(assetErrorFileArrangementDescriptor); } + if (assetErrorFileLocalizedStringTable) { fclose(assetErrorFileLocalizedStringTable); } + if (assetErrorFilePortalProperty) { fclose(assetErrorFilePortalProperty); } + if (assetErrorFileShaderTemplate) { fclose(assetErrorFileShaderTemplate); } + if (assetErrorFileSkyBox) { fclose(assetErrorFileSkyBox); } + if (assetErrorFileSlotDescriptor) { fclose(assetErrorFileSlotDescriptor); } + if (assetErrorFileSoundTemplate) { fclose(assetErrorFileSoundTemplate); } + if (assetErrorFileTerrain) { fclose(assetErrorFileTerrain); } + if (assetErrorFileTexture) { fclose(assetErrorFileTexture); } + if (assetErrorFileTextureRenderer) { fclose(assetErrorFileTextureRenderer); } + } + } + + // UnSupported Assets + + DEBUG_REPORT_LOG_PRINT(true, ("Writing unsupported assets list: %s\n", m_dataLintUnSupportedAssetsFile.c_str())); + + FILE *fp = fopen(m_dataLintUnSupportedAssetsFile.c_str(), "wt"); + + if (fp) + { + StringPairList unSupportedStringPairList(getList(AT_unSupported)); + StringPairList::iterator stringPairListIter = unSupportedStringPairList.begin(); + int count = 1; + + for (; stringPairListIter != unSupportedStringPairList.end(); ++stringPairListIter) + { + fprintf(fp, "%3d %s @ %s\n", count, stringPairListIter->first.c_str(), stringPairListIter->second.c_str()); + ++count; + } + + fclose(fp); + } + else + { + DEBUG_REPORT_LOG_PRINT(true, ("Error writing unsupported assets list: %s\n", m_dataLintUnSupportedAssetsFile.c_str())); + } + + // Categorized Assets + + DEBUG_REPORT_LOG_PRINT(true, ("Writing categorized files list: %s\n", m_dataLintCategorizedAssetsFile.c_str())); + + fp = fopen(m_dataLintCategorizedAssetsFile.c_str(), "wt"); + + if (fp) + { + fprintf(fp, "This file contains a categorized listing of the linted assets in the game. The categories are as follows:\n"); + fprintf(fp, "\n"); + fprintf(fp, "1. Appearances (%5d)\n", getList(AT_appearance).size()); + fprintf(fp, "2. Arrangement Descriptors (%5d)\n", getList(AT_arrangementDescriptor).size()); + fprintf(fp, "3. Localized String Tables (%5d)\n", getList(AT_localizedStringTable).size()); + fprintf(fp, "4. Object Templates (%5d)\n", getList(AT_objectTemplate).size()); + fprintf(fp, "5. Portal Properties (%5d)\n", getList(AT_portalProperty).size()); + fprintf(fp, "6. Shader Templates (%5d)\n", getList(AT_shaderTemplate).size()); + fprintf(fp, "7. SkyBoxes (%5d)\n", getList(AT_skyBox).size()); + fprintf(fp, "8. Slot Descriptors (%5d)\n", getList(AT_slotDescriptor).size()); + fprintf(fp, "9. Sound Templates (%5d)\n", getList(AT_soundTemplate).size()); + fprintf(fp, "10. Terrain (%5d)\n", getList(AT_terrain).size()); + fprintf(fp, "11. Textures (%5d)\n", getList(AT_texture).size()); + fprintf(fp, "12. Texture Renderer Templates (%5d)\n", getList(AT_textureRendererTemplate).size()); + fprintf(fp, "--------------------------------------\n"); + fprintf(fp, "13. Total Linted Assets (%5d)\n", m_assetList->size() - getList(AT_unSupported).size()); + fprintf(fp, "\n"); + + writeToCategorizedFile(fp, "Appearances", AT_appearance); + writeToCategorizedFile(fp, "Arrangement Descriptors", AT_arrangementDescriptor); + writeToCategorizedFile(fp, "Localized String Tables", AT_localizedStringTable); + writeToCategorizedFile(fp, "Object Templates", AT_objectTemplate); + writeToCategorizedFile(fp, "Portal Properties", AT_portalProperty); + writeToCategorizedFile(fp, "Shader Templates", AT_shaderTemplate); + writeToCategorizedFile(fp, "SkyBoxes", AT_skyBox); + writeToCategorizedFile(fp, "Slot Descriptors", AT_slotDescriptor); + writeToCategorizedFile(fp, "Sound Templates", AT_soundTemplate); + writeToCategorizedFile(fp, "Terrain", AT_terrain); + writeToCategorizedFile(fp, "Textures", AT_texture); + writeToCategorizedFile(fp, "Texture Renderer Templates", AT_textureRendererTemplate); + + fclose(fp); + } + } +} + +//----------------------------------------------------------------------------- +void DataLint::remove() +{ + DEBUG_FATAL(!ms_installed, ("DataLint::remove: not installed")); + ms_installed = false; + m_enabled = false; + + delete m_warningList; + delete m_assetStack; + delete m_assetList; + + FatalSetThrowExceptions(false); +} + +//----------------------------------------------------------------------------- +void DataLintNamespace::writeError(FILE *fp, WarningPair &warningPair, int ¤tErrorCount) +{ + std::string formatedErrorMessage(formatErrorMessage(warningPair, currentErrorCount)); + + if (fp) + { + fprintf(fp, "%s", formatedErrorMessage.c_str()); + } +} + +//----------------------------------------------------------------------------- +std::string DataLintNamespace::formatErrorMessage(WarningPair &warningPair, int ¤tErrorCount) +{ + std::string result; + char formatedError[4096]; + char text[4096]; + sprintf(text, "%s", warningPair.first.c_str()); + fixUpSlashes(text); + + char *assetError = strstr(text, " : "); + + if (assetError != NULL) + { + ++assetError; + ++assetError; + ++assetError; + } + + char *lineOfCode = &text[0]; + char *seperator = strstr(text, " : "); + + if (seperator) + { + *seperator = '\0'; + } + + if (assetError) + { + // Remove any trailing carriage returns + + int assetErrorLength = strlen(assetError); + + if (assetErrorLength > 0) + { + char *walkPtr = &assetError[0] + (assetErrorLength - 1); + + while ((assetErrorLength > 0) && (*walkPtr == '\n')) + { + --assetErrorLength; + *walkPtr = '\0'; + --walkPtr; + } + } + + sprintf(formatedError, "%3d Error: %s\n", currentErrorCount, assetError); + result += formatedError; + } + + if (lineOfCode) + { + sprintf(formatedError, " Line of Code: %s\n", lineOfCode); + result += formatedError; + } + + StringList::reverse_iterator stringListIter = warningPair.second.second.rbegin(); + + for (; stringListIter != warningPair.second.second.rend(); ++stringListIter) + { + std::string stackString(*stringListIter); + + sprintf(text, " Loaded From: %s\n", stackString.c_str()); + fixUpSlashes(text); + + sprintf(formatedError, "%s", text); + result += formatedError; + } + + result += "\n"; + ++currentErrorCount; + + return result; +} + +//----------------------------------------------------------------------------- +void DataLint::setEnabled(bool const enabled) +{ + m_enabled = enabled; +} + +//----------------------------------------------------------------------------- +bool DataLint::isEnabled() +{ + return m_enabled || ConfigSharedFoundation::getVerboseWarnings(); +} + +//----------------------------------------------------------------------------- +void DataLint::pushAsset(char const *assetPath) +{ + if (m_assetStack) + { + int const size = m_assetStack->size(); + UNREF(size); + m_assetStack->push_back(assetPath); + } +} + +//----------------------------------------------------------------------------- +void DataLint::popAsset() +{ + if (m_assetStack) + { + int const size = m_assetStack->size(); + UNREF(size); + DEBUG_FATAL(m_assetStack->empty(), ("DataLint::popAsset() - Trying to pop the asset stack when it is empty.")); + m_assetStack->pop_back(); + } +} + +//----------------------------------------------------------------------------- + +void DataLint::logWarning(std::string const &warning) +{ + if (m_enabled) + { + m_warningList->push_back(std::make_pair(warning, std::make_pair(m_currentAssetType, *m_assetStack))); + } +} + +//----------------------------------------------------------------------------- +void DataLint::clearAssetStack() +{ + if (m_assetStack) + m_assetStack->clear(); +} + +//----------------------------------------------------------------------------- +void DataLint::addFilePath(char const *filePath) +{ + if (filePath == NULL || (strlen(filePath) <= 0)) + { + return; + } + + if (m_assetList != NULL) + { + char text[4096]; + sprintf(text, "%s", filePath); + + char *seperator = strstr(text, " @ "); + char *longPath = seperator; + ++longPath; + ++longPath; + ++longPath; + char *shortPath = &text[0]; + *seperator = '\0'; + + m_assetList->push_back(std::make_pair(shortPath, longPath)); + } +} + +//----------------------------------------------------------------------------- +void DataLint::removeFilePath(char const *filePath) +{ + if (m_assetList != NULL) + { + StringPairList::iterator stringPairListIter = m_assetList->begin(); + + for (; stringPairListIter != m_assetList->end(); ++stringPairListIter) + { + char const *first = stringPairListIter->first.c_str(); + + if (strcmp(first, filePath) == 0) + { + m_assetList->erase(stringPairListIter); + } + } + } +} + +//----------------------------------------------------------------------------- +bool DataLintNamespace::isAnAppearanceAsset(char const *text) +{ + bool const appearanceAsset = (strstr(text, "appearance/") != NULL); + bool const portalPropertyAsset = (strstr(text, ".pob") != NULL); + + return (appearanceAsset && !portalPropertyAsset); +} + +//----------------------------------------------------------------------------- +bool DataLintNamespace::isAnArrangementDescriptorAsset(char const *text) +{ + bool const arrangementDescriptorAsset = (strstr(text, "slot/arrangement/") != NULL); + + return arrangementDescriptorAsset; +} + +//----------------------------------------------------------------------------- +bool DataLintNamespace::isALocalizedStringAsset(char const *text) +{ + bool const localizedStringAsset = (strstr(text, ".stf") != NULL); + + return localizedStringAsset; +} + +//----------------------------------------------------------------------------- +bool DataLintNamespace::isAnObjectTemplateAsset(char const *text) +{ + bool result = false; + UNREF(text); + bool const isInTheObjectDirectory = (strstr(text, "object/") != NULL); + bool const isInTheAbstractDirectory = (strstr(text, "abstract/") != NULL); + + if (isInTheObjectDirectory || isInTheAbstractDirectory) + { + bool const arrangementDescriptorAsset = isAnArrangementDescriptorAsset(text); + bool const slotDescriptorAsset = isASlotDescriptorAsset(text); + + result = (!arrangementDescriptorAsset) && (!slotDescriptorAsset); + } + + return result; +} + +//----------------------------------------------------------------------------- +bool DataLintNamespace::isAPortalProprtyAsset(char const *text) +{ + bool const portalPropertyAsset = (strstr(text, ".pob") != NULL); + + return portalPropertyAsset; +} + +//----------------------------------------------------------------------------- +bool DataLintNamespace::isAShaderTemplateAsset(char const *text) +{ + bool const shaderTemplateAsset = (strstr(text, "shader/") != NULL); + + return shaderTemplateAsset; +} + +//----------------------------------------------------------------------------- +bool DataLintNamespace::isASkyBoxAsset(char const *text) +{ + bool const skyBoxAsset = (strstr(text, "skybox/") != NULL); + + return skyBoxAsset; +} + +//----------------------------------------------------------------------------- +bool DataLintNamespace::isASlotDescriptorAsset(char const *text) +{ + bool const slotDescriptorAsset = (strstr(text, "slot/descriptor/") != NULL); + + return slotDescriptorAsset; +} + +//----------------------------------------------------------------------------- +bool DataLintNamespace::isASoundAsset(char const *text) +{ + bool const soundAsset = (strstr(text, "sound/") != NULL); + + return soundAsset; +} + +//----------------------------------------------------------------------------- +bool DataLintNamespace::isATerrainAsset(char const *text) +{ + return strstr(text, ".trn") != 0; +} + +//----------------------------------------------------------------------------- +bool DataLintNamespace::isATextureAsset(char const *text) +{ + bool const textureAsset = (strstr(text, ".dds") != NULL); + + return textureAsset; +} + +//----------------------------------------------------------------------------- +bool DataLintNamespace::isATextureRendererTemplateAsset(char const *text) +{ + bool const textureRendererAsset = (strstr(text, ".trt") != NULL); + + return textureRendererAsset; +} + +//----------------------------------------------------------------------------- +bool DataLintNamespace::isAnUnSupportedAsset(char const *text) +{ + bool const appearanceAsset = isAnAppearanceAsset(text); + bool const arrangementDescriptorAsset = isAnArrangementDescriptorAsset(text); + bool const localizedStringAsset = isALocalizedStringAsset(text); + bool const objectTemplateAsset = isAnObjectTemplateAsset(text); + bool const portalPropertyAsset = isAPortalProprtyAsset(text); + bool const shaderTemplateAsset = isAShaderTemplateAsset(text); + bool const skyBoxAsset = isASkyBoxAsset(text); + bool const slotDescriptorAsset = isASlotDescriptorAsset(text); + bool const soundAsset = isASoundAsset(text); + bool const terrainAsset = isATerrainAsset(text); + bool const textureAsset = isATextureAsset(text); + bool const textureRendererTemplateAsset = isATextureRendererTemplateAsset(text); + + bool const result = !(appearanceAsset || + arrangementDescriptorAsset || + localizedStringAsset || + objectTemplateAsset || + portalPropertyAsset || + shaderTemplateAsset || + skyBoxAsset || + slotDescriptorAsset || + soundAsset || + terrainAsset || + textureAsset || + textureRendererTemplateAsset); + + return result; +} + +//----------------------------------------------------------------------------- +DataLint::StringPairList DataLintNamespace::getList(int const reserveCount, AssetFunction assetTypeFunction) +{ + DEBUG_FATAL(!m_assetList, ("DataLint::getList() - m_assetList is NULL")); + + // Create the list of terrains + + DataLint::StringPairList stringPairList; + stringPairList.reserve(reserveCount); + + DataLint::StringPairList::iterator stringPairListIter = m_assetList->begin(); + + for (; stringPairListIter != m_assetList->end(); ++stringPairListIter) + { + char const *first = stringPairListIter->first.c_str(); + char const *second = stringPairListIter->second.c_str(); + + if (assetTypeFunction(first)) + { + stringPairList.push_back(std::make_pair(first, second)); + } + } + + return stringPairList; +} + +//----------------------------------------------------------------------------- +DataLint::StringPairList DataLint::getList(AssetType const assetType) +{ + switch (assetType) + { + case AT_appearance: + { + return DataLintNamespace::getList(8192, isAnAppearanceAsset); + } + break; + case AT_arrangementDescriptor: + { + return DataLintNamespace::getList(1024, isAnArrangementDescriptorAsset); + } + break; + case AT_localizedStringTable: + { + return DataLintNamespace::getList(1024, isALocalizedStringAsset); + } + break; + case AT_objectTemplate: + { + return DataLintNamespace::getList(8192, isAnObjectTemplateAsset); + } + break; + case AT_portalProperty: + { + return DataLintNamespace::getList(512, isAPortalProprtyAsset); + } + break; + case AT_shaderTemplate: + { + return DataLintNamespace::getList(4096, isAShaderTemplateAsset); + } + break; + case AT_skyBox: + { + return DataLintNamespace::getList(256, isASkyBoxAsset); + } + break; + case AT_slotDescriptor: + { + return DataLintNamespace::getList(1024, isASlotDescriptorAsset); + } + break; + case AT_soundTemplate: + { + return DataLintNamespace::getList(4096, isASoundAsset); + } + break; + case AT_terrain: + { + return DataLintNamespace::getList(32, isATerrainAsset); + } + break; + case AT_texture: + { + return DataLintNamespace::getList(8192, isATextureAsset); + } + break; + case AT_textureRendererTemplate: + { + return DataLintNamespace::getList(512, isATextureRendererTemplateAsset); + } + break; + case AT_unSupported: + { + return DataLintNamespace::getList(4096, isAnUnSupportedAsset); + } + break; + default: + { + DEBUG_FATAL(true, ("DataLint::getList() - Unknown list type.")); + } + } + + return StringPairList(); +} + +//----------------------------------------------------------------------------- +void DataLintNamespace::fixUpSlashes(char *text) +{ + char *slashEater = &text[0]; + + while (*slashEater != '\0') + { + if (*slashEater == '\\') + { + *slashEater = '/'; + } + + ++slashEater; + } +} + +//----------------------------------------------------------------------------- +void DataLintNamespace::writeToCategorizedFile(FILE *fp, char const *title, DataLint::AssetType const assetType) +{ + DataLint::StringPairList stringPairlist(DataLint::getList(assetType)); + + fprintf(fp, "-----------------%s (%d assets)-----------------\n", title, static_cast(stringPairlist.size())); + fprintf(fp, "\n"); + DataLint::StringPairList::const_iterator stringPairListIter = stringPairlist.begin(); + int count = 1; + + for (; stringPairListIter != stringPairlist.end(); ++stringPairListIter) + { + fprintf(fp, "%4d %s @ %s\n", count, stringPairListIter->first.c_str(), stringPairListIter->second.c_str()); + ++count; + } + + fprintf(fp, "\n"); +} + +//----------------------------------------------------------------------------- +void DataLint::setCurrentAssetType(AssetType const assetType) +{ + m_currentAssetType = assetType; +} + +//----------------------------------------------------------------------------- +void DataLint::setClientMode() +{ + m_mode = M_client; +} + +//----------------------------------------------------------------------------- +void DataLint::setServerMode() +{ + m_mode = M_server; +} + +// ============================================================================ diff --git a/engine/shared/library/sharedDebug/src/shared/DataLint.h b/engine/shared/library/sharedDebug/src/shared/DataLint.h new file mode 100644 index 00000000..7a732df2 --- /dev/null +++ b/engine/shared/library/sharedDebug/src/shared/DataLint.h @@ -0,0 +1,73 @@ +// ============================================================================ +// +// DataLint.h +// Copyright Sony Online Entertainment +// +// ============================================================================ + +#ifndef DATALINT_H +#define DATALINT_H + +// ============================================================================ + +class DataLint +{ +public: + + typedef stdvector >::fwd StringPairList; + +public: + + static void install(); + static void remove(); + + static bool isInstalled(); + + static void setClientMode(); + static void setServerMode(); + + static void addFilePath(char const *filePath); + static void removeFilePath(char const *filePath); + + DLLEXPORT static bool isEnabled(); + static void setEnabled(bool const enabled); + + static void clearAssetStack(); + static void logWarning(std::string const &warning); + static void logNote(std::string const &warning); + + static void pushAsset(char const *assetPath); + static void popAsset(); + + // Dump the report files + + static void report(); + + // If you add a new asset type, it needs a matching isA function below + + enum AssetType + { + AT_appearance, + AT_arrangementDescriptor, + AT_localizedStringTable, + AT_objectTemplate, + AT_portalProperty, + AT_shaderTemplate, + AT_skyBox, + AT_slotDescriptor, + AT_soundTemplate, + AT_terrain, + AT_texture, + AT_textureRendererTemplate, + AT_unSupported, + AT_count, + AT_invalid = -1 + }; + + static StringPairList getList(AssetType const listType); + static void setCurrentAssetType(AssetType const assetType); +}; + +// ============================================================================ + +#endif diff --git a/engine/shared/library/sharedDebug/src/shared/DebugFlags.cpp b/engine/shared/library/sharedDebug/src/shared/DebugFlags.cpp new file mode 100644 index 00000000..c676ddeb --- /dev/null +++ b/engine/shared/library/sharedDebug/src/shared/DebugFlags.cpp @@ -0,0 +1,259 @@ +// ====================================================================== +// +// DebugFlags.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedDebug/FirstSharedDebug.h" +#include "sharedDebug/DebugFlags.h" + +#include "sharedFoundation/ConfigFile.h" + +#include +#include + +// ====================================================================== + +std::vector DebugFlags::ms_flagsSortedByName; +std::vector DebugFlags::ms_flagsSortedByReportPriority; + +// ====================================================================== +/** + * @internal + */ + +void DebugFlags::config(bool &variable, const char *configSection, const char *configName) +{ + DEBUG_FATAL(strchr(configSection, ' ') != NULL, ("no spaces are allowed in debug flag section names: %s", configSection)); + DEBUG_FATAL(strchr(configName, ' ') != NULL, ("no spaces are allowed in debug flag names: %s", configName)); + + if (configSection && configName && ConfigFile::isInstalled()) + { +#ifdef _DEBUG + Flag const * const flag = getFlag(configSection, configName); + if (flag) + { + DEBUG_WARNING(true, ("DebugFlags::registerFlag: %s/%s is already registered", configSection, configName)); + flag->m_callStack.debugLog(); + } +#endif + + variable = ConfigFile::getKeyBool(configSection, configName, variable); + } +} + +// ---------------------------------------------------------------------- +/** + * @internal + */ + +static bool sortByNameLess(const DebugFlags::Flag &lhs, const DebugFlags::Flag &rhs) +{ + const int s = strcmp(lhs.section, rhs.section); + if (s < 0) + return true; + if (s > 0) + return false; + + return strcmp(lhs.name, rhs.name) < 0; +} + +// ---------------------------------------------------------------------- +/** + * @internal + */ + +static bool sortByReportPriorityLess(const DebugFlags::Flag &lhs, const DebugFlags::Flag &rhs) +{ + return (lhs.reportPriority < rhs.reportPriority); +} + +// ---------------------------------------------------------------------- +/** + * @internal + */ + +void DebugFlags::insert(const Flag &flag) +{ + ms_flagsSortedByName.insert(std::lower_bound(ms_flagsSortedByName.begin(), ms_flagsSortedByName.end(), flag, sortByNameLess), flag); + if (flag.reportRoutine1 || flag.reportRoutine2) + ms_flagsSortedByReportPriority.insert(std::lower_bound(ms_flagsSortedByReportPriority.begin(), ms_flagsSortedByReportPriority.end(), flag, sortByReportPriorityLess), flag); +} + +// ---------------------------------------------------------------------- +/** + * Register a debug flag. + * The parameters passed to this function must never go out of scope. + * @param variable The bool variable to determine whether the flag is on or not. + * @param section The section for the variable. + * @param name The name of the variable. + */ + +void DebugFlags::registerFlag(bool &variable, const char *section, const char *name) +{ + NOT_NULL(section); + NOT_NULL(name); + + config(variable, section, name); + + Flag f; + f.variable = &variable; + f.section = section; + f.name = name; + f.reportPriority = 0; + f.reportRoutine1 = NULL; + f.reportRoutine2 = NULL; + f.context = NULL; +#ifdef _DEBUG + f.m_callStack.sample(); +#endif + + insert(f); +} + +// ---------------------------------------------------------------------- + +void DebugFlags::registerFlag(bool &variable, const char *section, const char *name, ReportRoutine1 reportRoutine, int reportPriority) +{ + NOT_NULL(section); + NOT_NULL(name); + + config(variable, section, name); + + Flag f; + f.variable = &variable; + f.section = section; + f.name = name; + f.reportPriority = reportPriority; + f.reportRoutine1 = reportRoutine; + f.reportRoutine2 = NULL; + f.context = NULL; +#ifdef _DEBUG + f.m_callStack.sample(); +#endif + + insert(f); +} + +// ---------------------------------------------------------------------- + +void DebugFlags::registerFlag(bool &variable, const char *section, const char *name, ReportRoutine2 reportRoutine, void *context, int reportPriority) +{ + NOT_NULL(section); + NOT_NULL(name); + + config(variable, section, name); + + Flag f; + f.variable = &variable; + f.section = section; + f.name = name; + f.reportPriority = reportPriority; + f.reportRoutine1 = NULL; + f.reportRoutine2 = reportRoutine; + f.context = context; +#ifdef _DEBUG + f.m_callStack.sample(); +#endif + + insert(f); +} + +// ---------------------------------------------------------------------- +/** + * Unregister a debug flag. + */ + +void DebugFlags::unregisterFlag(bool &variable) +{ + { + FlagVector::iterator end = ms_flagsSortedByName.end(); + for (FlagVector::iterator i = ms_flagsSortedByName.begin(); i != end; ++i) + if (i->variable == &variable) + { + ms_flagsSortedByName.erase(i); + break; + } + } + + { + FlagVector::iterator end = ms_flagsSortedByReportPriority.end(); + for (FlagVector::iterator i = ms_flagsSortedByReportPriority.begin(); i != end; ++i) + if (i->variable == &variable) + { + ms_flagsSortedByReportPriority.erase(i); + break; + } + } +} + +// ---------------------------------------------------------------------- + +void DebugFlags::callReportRoutines() +{ + FlagVector::const_iterator end = ms_flagsSortedByReportPriority.end(); + for (FlagVector::const_iterator i = ms_flagsSortedByReportPriority.begin(); i != end; ++i) + { + const Flag &f = *i; + if (*f.variable) + { + if (f.reportRoutine1) + f.reportRoutine1(); + else + if (f.reportRoutine2) + f.reportRoutine2(f.context); + } + } +} + +// ---------------------------------------------------------------------- + +bool * DebugFlags::findFlag(char const * const section, char const * const name) +{ + Flag const * const flag = getFlag(section, name); + if (flag) + return flag->variable; + + return 0; +} + +// ---------------------------------------------------------------------- + +DebugFlags::Flag const * DebugFlags::getFlag(char const * const section, char const * name) +{ + FlagVector::const_iterator iEnd = ms_flagsSortedByName.end(); + for (FlagVector::const_iterator i = ms_flagsSortedByName.begin(); i != iEnd; ++i) + { + Flag const & f = *i; + if (strcmp(f.section, section) == 0 && strcmp(f.name, name) == 0) + return &f; + } + + return NULL; +} + +// ---------------------------------------------------------------------- + +int DebugFlags::getNumberOfFlags() +{ + return static_cast(ms_flagsSortedByName.size()); +} + +// ---------------------------------------------------------------------- + +char const * DebugFlags::getFlagSection(int const index) +{ + VALIDATE_RANGE_INCLUSIVE_EXCLUSIVE(0, index, getNumberOfFlags()); + return ms_flagsSortedByName[index].section; +} + +// ---------------------------------------------------------------------- + +char const * DebugFlags::getFlagName(int const index) +{ + VALIDATE_RANGE_INCLUSIVE_EXCLUSIVE(0, index, getNumberOfFlags()); + return ms_flagsSortedByName[index].name; +} + +// ====================================================================== diff --git a/engine/shared/library/sharedDebug/src/shared/DebugFlags.h b/engine/shared/library/sharedDebug/src/shared/DebugFlags.h new file mode 100644 index 00000000..fcab1e0b --- /dev/null +++ b/engine/shared/library/sharedDebug/src/shared/DebugFlags.h @@ -0,0 +1,73 @@ +// ====================================================================== +// +// DebugFlags.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_DebugFlags_H +#define INCLUDED_DebugFlags_H + +// ====================================================================== + +#include "CallStack.h" + +// ====================================================================== + +class DebugFlags +{ + friend class Os; + +public: + + typedef void (*ReportRoutine1)(); + typedef void (*ReportRoutine2)(void *context); + +public: + + static DLLEXPORT void registerFlag(bool &variable, const char *section, const char *name); + static DLLEXPORT void registerFlag(bool &variable, const char *section, const char *name, ReportRoutine1 reportRoutine, int reportPriority=0); + static void registerFlag(bool &variable, const char *section, const char *name, ReportRoutine2 reportRoutine, void *context, int reportPriority=0); + static DLLEXPORT void unregisterFlag(bool &variable); + + static void callReportRoutines(); + + static bool *findFlag(const char *section, const char *name); + + static int getNumberOfFlags(); + static char const * getFlagSection(int index); + static char const * getFlagName(int index); + +public: + + struct Flag + { + bool *variable; + const char *section; + const char *name; + int reportPriority; + ReportRoutine1 reportRoutine1; + ReportRoutine2 reportRoutine2; + void *context; +#ifdef _DEBUG + CallStack m_callStack; +#endif + }; + +private: + + static void config(bool &variable, const char *configSection, const char *configName); + static void insert(const Flag &flag); + static Flag const * getFlag(char const * const section, char const * name); + +private: + + typedef stdvector::fwd FlagVector; + + static FlagVector ms_flagsSortedByName; + static FlagVector ms_flagsSortedByReportPriority; +}; + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedDebug/src/shared/DebugKey.cpp b/engine/shared/library/sharedDebug/src/shared/DebugKey.cpp new file mode 100644 index 00000000..1fb1eefe --- /dev/null +++ b/engine/shared/library/sharedDebug/src/shared/DebugKey.cpp @@ -0,0 +1,164 @@ +// ====================================================================== +// +// DebugKey.cpp +// copyright 1998 Bootprint Entertainment +// copyright 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedDebug/FirstSharedDebug.h" +#include "sharedDebug/DebugKey.h" + +#include "sharedFoundation/ConfigFile.h" +#include "sharedFoundation/Production.h" + +#include +#include + +// ====================================================================== + +#if PRODUCTION == 0 +bool *DebugKey::ms_currentFlag; +bool DebugKey::ms_pressed[10]; +bool DebugKey::ms_down[10]; +DebugKey::FlagVector DebugKey::ms_flags; +#endif + +// ====================================================================== +/** + * @internal + */ + +bool DebugKey::Flag::operator <(const Flag &rhs) const +{ + return strcmp(name, rhs.name) < 0; +} + +// ---------------------------------------------------------------------- + +void DebugKey::newFrame() +{ +#if PRODUCTION == 0 + memset(ms_pressed, 0, sizeof(ms_pressed)); +#endif +} + +// ---------------------------------------------------------------------- + +void DebugKey::lostFocus() +{ +#if PRODUCTION == 0 + memset(ms_down, 0, sizeof(ms_down)); + memset(ms_pressed, 0, sizeof(ms_pressed)); +#endif +} + +// ---------------------------------------------------------------------- +/** + * Register a debug key flag. + * The parameters passed to this function must never go out of scope. + * @param variable The bool variable to determine whether the flag is on or not. + * @param name The name of the variable. + */ + +void DebugKey::registerFlag(bool &variable, const char *name) +{ +#if PRODUCTION == 0 + if (ConfigFile::isInstalled()) + variable = ConfigFile::getKeyBool("SharedDebug/DebugKey", name, variable); + + Flag f; + f.variable = &variable; + f.name = name; + ms_flags.insert(std::lower_bound(ms_flags.begin(), ms_flags.end(), f), f); +#else + UNREF(variable); + UNREF(name); +#endif +} + +// ---------------------------------------------------------------------- + +bool DebugKey::isPressed(int i) +{ +#if PRODUCTION == 0 + DEBUG_FATAL(i < 0 || i > 9, ("DebugKey::isPressed out of range 0/%d/9", i)); + return ms_pressed[i]; +#else + UNREF(i); + return false; +#endif +} + +// ---------------------------------------------------------------------- + +bool DebugKey::isDown(int i) +{ +#if PRODUCTION == 0 + DEBUG_FATAL(i < 0 || i > 9, ("DebugKey::isDown out of range 0/%d/9", i)); + return (ms_pressed[i] || ms_down[i]); +#else + UNREF(i); + return false; +#endif +} + +// ---------------------------------------------------------------------- + +bool DebugKey::isActive() +{ +#if PRODUCTION == 0 + return ms_currentFlag != NULL; +#else + return false; +#endif +} + +// ---------------------------------------------------------------------- + +void DebugKey::setCurrentFlag(const bool *newFlag) +{ +#if PRODUCTION == 0 + lostFocus(); + + // make sure the old flag gets set false + if (ms_currentFlag) + *ms_currentFlag = false; + + // update the flag pointer + ms_currentFlag = const_cast(newFlag); + + // make sure the new flag gets set true + if (ms_currentFlag) + *ms_currentFlag = true; +#else + UNREF(newFlag); +#endif +} + +// ---------------------------------------------------------------------- + +void DebugKey::pressKey(int i) +{ +#if PRODUCTION == 0 + DEBUG_FATAL(i < 0 || i > 9, ("DebugKey::pressKey out of range 0/%d/9", i)); + ms_pressed[i] = true; + ms_down[i] = true; +#else + UNREF(i); +#endif +} + +// ---------------------------------------------------------------------- + +void DebugKey::releaseKey(int i) +{ +#if PRODUCTION == 0 + DEBUG_FATAL(i < 0 || i > 9, ("DebugKey::releastKey out of range 0/%d/9", i)); + ms_down[i] = false; +#else + UNREF(i); +#endif +} + +// ====================================================================== diff --git a/engine/shared/library/sharedDebug/src/shared/DebugKey.h b/engine/shared/library/sharedDebug/src/shared/DebugKey.h new file mode 100644 index 00000000..ba53ed1e --- /dev/null +++ b/engine/shared/library/sharedDebug/src/shared/DebugKey.h @@ -0,0 +1,56 @@ +// ====================================================================== +// +// DebugKey.h +// copyright 1998 Bootprint Entertainment +// copyright 2001 Sony Online Entertainment +// All Rights Reserved. +// +// ====================================================================== + +#ifndef INCLUDED_DebugKey_H +#define INCLUDED_DebugKey_H + +// ====================================================================== + +class DebugKey +{ + friend class Os; + +public: + + static DLLEXPORT void registerFlag(bool &variable, const char *name); + + static void newFrame(); + static void lostFocus(); + + static bool isActive(); + static void setCurrentFlag(const bool *flag); + + static void pressKey(int i); + static void releaseKey(int i); + + static DLLEXPORT bool isPressed(int i); + static DLLEXPORT bool isDown(int i); + +private: + + struct Flag + { + bool *variable; + const char *name; + bool operator <(const Flag &rhs) const; + }; + + typedef stdvector::fwd FlagVector; + +private: + + static bool *ms_currentFlag; + static bool ms_pressed[10]; + static bool ms_down[10]; + static FlagVector ms_flags; +}; + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedDebug/src/shared/FirstSharedDebug.h b/engine/shared/library/sharedDebug/src/shared/FirstSharedDebug.h new file mode 100644 index 00000000..bb086267 --- /dev/null +++ b/engine/shared/library/sharedDebug/src/shared/FirstSharedDebug.h @@ -0,0 +1,17 @@ +// ====================================================================== +// +// FirstDebug.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_FirstDebug_H +#define INCLUDED_FirstDebug_H + +// ====================================================================== + +#include "sharedFoundation/FirstSharedFoundation.h" + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedDebug/src/shared/InstallTimer.cpp b/engine/shared/library/sharedDebug/src/shared/InstallTimer.cpp new file mode 100644 index 00000000..5fe48326 --- /dev/null +++ b/engine/shared/library/sharedDebug/src/shared/InstallTimer.cpp @@ -0,0 +1,71 @@ +// ====================================================================== +// +// InstallTimer.cpp +// Copyright 2004 Sony Online Entertainment Inc +// All Rights Reserved +// +// ====================================================================== + +#include "sharedDebug/FirstSharedDebug.h" +#include "sharedDebug/InstallTimer.h" + +#include "sharedFoundation/ConfigFile.h" + +// ====================================================================== + +namespace InstallTimerNamespace +{ + bool ms_enabled; + int ms_indent = 1; +} +using namespace InstallTimerNamespace; + +// ====================================================================== + +void InstallTimer::enable() +{ + ms_enabled = true; +} + +// ---------------------------------------------------------------------- + +void InstallTimer::checkConfigFile() +{ + ms_enabled = ConfigFile::getKeyBool("SharedDebug/InstallTimer", "enabled", false); +} + +// ====================================================================== + +InstallTimer::InstallTimer(char const * description) +: + m_description(description), + m_performanceTimer(), + m_startingNumberOfBytesAllocated(MemoryManager::getCurrentNumberOfBytesAllocated()) +{ + NOT_NULL(m_description); + m_performanceTimer.start(); + ++ms_indent; +} + +// ---------------------------------------------------------------------- + +InstallTimer::~InstallTimer() +{ + manualExit(); +} + +// ---------------------------------------------------------------------- + +void InstallTimer::manualExit() +{ + if (m_description) + { + m_performanceTimer.stop(); + unsigned long const endingNumberOfBytesAllocated = MemoryManager::getCurrentNumberOfBytesAllocated(); + --ms_indent; + REPORT_LOG_PRINT(ms_enabled, ("InstallTimer:%*c%6.4f %d %s\n", ms_indent * 2, ' ', m_performanceTimer.getElapsedTime(), static_cast(endingNumberOfBytesAllocated - m_startingNumberOfBytesAllocated), m_description)); + m_description = NULL; + } +} + +// ====================================================================== diff --git a/engine/shared/library/sharedDebug/src/shared/InstallTimer.h b/engine/shared/library/sharedDebug/src/shared/InstallTimer.h new file mode 100644 index 00000000..122444e0 --- /dev/null +++ b/engine/shared/library/sharedDebug/src/shared/InstallTimer.h @@ -0,0 +1,46 @@ +// ====================================================================== +// +// InstallTimer.h +// Copyright 2004 Sony Online Entertainment Inc +// All Rights Reserved +// +// ====================================================================== + +#ifndef INCLUDED_InstallTimer_H +#define INCLUDED_InstallTimer_H + +// ====================================================================== + +#include "sharedDebug/PerformanceTimer.h" + +// ====================================================================== + +class InstallTimer +{ +public: + + static void enable(); + static void checkConfigFile(); + +public: + + InstallTimer(char const * description); + ~InstallTimer(); + + void manualExit(); + +private: + + InstallTimer(InstallTimer const &); + InstallTimer & operator=(InstallTimer const &); + +private: + + char const * m_description; + PerformanceTimer m_performanceTimer; + unsigned long const m_startingNumberOfBytesAllocated; +}; + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedDebug/src/shared/LeakFinder.cpp b/engine/shared/library/sharedDebug/src/shared/LeakFinder.cpp new file mode 100644 index 00000000..988c4936 --- /dev/null +++ b/engine/shared/library/sharedDebug/src/shared/LeakFinder.cpp @@ -0,0 +1,197 @@ +// ====================================================================== +// +// LeakFinder.cpp +// bearhart +// +// Copyright 2005, Sony Online Entertainment +// All Rights Reserved +// +// ====================================================================== + +#include "sharedDebug/FirstSharedDebug.h" +#include "sharedDebug/LeakFinder.h" + +#include + +// ====================================================================== + +LeakFinder::LeakFinder() +{ +} + +// ---------------------------------------------------------------------- + +LeakFinder::~LeakFinder() +{ +} + +// ---------------------------------------------------------------------- + +void LeakFinder::onAllocate(void *object) +{ + ObjectData od; + od.callStack.sample(); + liveObjects[object]=od; +} + +// ---------------------------------------------------------------------- + +void LeakFinder::onFree(void *object) +{ + liveObjects.erase(object); +} + +// ---------------------------------------------------------------------- + +void LeakFinder::onReference(void *object) +{ + ObjectMap::iterator oi = liveObjects.find(object); + if (oi!=liveObjects.end()) + { + ObjectData &od = oi->second; + if (!od.referenceData) + { + od.referenceData = new ReferenceCountingData; + } + + + ReferenceCountChange rc; + rc.dir=ReferenceCountChange::up; + rc.callStack.sample(); + od.referenceData->push_back(rc); + } +} + +// ---------------------------------------------------------------------- + +void LeakFinder::onDereference(void *object) +{ + ObjectMap::iterator oi = liveObjects.find(object); + if (oi!=liveObjects.end()) + { + ObjectData &od = oi->second; + if (!od.referenceData) + { + od.referenceData = new ReferenceCountingData; + } + + ReferenceCountChange rc; + rc.dir=ReferenceCountChange::down; + rc.callStack.sample(); + od.referenceData->push_back(rc); + } +} + +// ---------------------------------------------------------------------- + +void LeakFinder::getCurrentObjects(LiveObjectList &o_objects) const +{ + o_objects.reserve(o_objects.size() + liveObjects.size()); + for (ObjectMap::const_iterator oi=liveObjects.begin();oi!=liveObjects.end();++oi) + { + LiveObject lo; + + lo.object=oi->first; + lo.callStack=oi->second.callStack; + lo.referenceData = oi->second.referenceData; + o_objects.push_back(lo); + } +} + +// ---------------------------------------------------------------------- + +void LeakFinder::debugPrint() const +{ + LeakFinder::LiveObjectList leaks; + getCurrentObjects(leaks); + + if (!leaks.empty()) + { + std::map > uniques; + std::map >::iterator ui; + + LeakFinder::LiveObjectList::iterator li; + + for (li=leaks.begin();li!=leaks.end();++li) + { + uniques[li->callStack].push_back(&(*li)); + } + + for (ui=uniques.begin();ui!=uniques.end();++ui) + { + bool printedRefs=false; + REPORT_PRINT(true, ("------------------------------------------------------------------------------\n")); + REPORT_PRINT(true, ("leaked objects:\n")); + + size_t i; + for (i=0;isecond.size();++i) + { + const ReferenceCountingData *referenceData = ui->second[i]->referenceData; + + const bool printRefs = referenceData && !printedRefs; + + if (i>0 && (i%10 == 0) || printRefs) + { + REPORT_PRINT(true, ("\n")); + } + REPORT_PRINT(true, ("(0x%08X) ", reinterpret_cast(ui->second[i]->object))); + if (printRefs) + { + printedRefs=true; + REPORT_PRINT(true, ("\n")); + _printReferenceCountingData(*referenceData); + } + } + if (i%10!=0) + { + REPORT_PRINT(true, ("\n")); + } + + REPORT_PRINT(true, ("from:\n")); + ui->first.debugPrint(); + REPORT_PRINT(true, ("------------------------------------------------------------------------------\n")); + } + } +} + +// ---------------------------------------------------------------------- + +void LeakFinder::_printReferenceCountingData(const ReferenceCountingData &history) const +{ + if (history.empty()) + { + REPORT_PRINT(true, ("No reference counting history.\n")); + return; + } + + std::map refs; + std::map derefs; + + std::list::const_iterator hi; + for (hi=history.begin();hi!=history.end();++hi) + { + const ReferenceCountChange &rc = *hi; + if (rc.dir==ReferenceCountChange::up) + { + refs[rc.callStack]++; + } + else + { + derefs[rc.callStack]++; + } + } + + std::map::iterator ri; + for (ri=refs.begin();ri!=refs.end();++ri) + { + REPORT_PRINT(true, ("%d Refs:\n", ri->second)); + ri->first.debugPrint(); + } + for (ri=derefs.begin();ri!=derefs.end();++ri) + { + REPORT_PRINT(true, ("%d De-Refs:\n", ri->second)); + ri->first.debugPrint(); + } +} + +// ====================================================================== diff --git a/engine/shared/library/sharedDebug/src/shared/LeakFinder.h b/engine/shared/library/sharedDebug/src/shared/LeakFinder.h new file mode 100644 index 00000000..e3a44d84 --- /dev/null +++ b/engine/shared/library/sharedDebug/src/shared/LeakFinder.h @@ -0,0 +1,99 @@ +// ====================================================================== +// +// LeakFinder.h +// bearhart +// +// Copyright 2005, Sony Online Entertainment +// All Rights Reserved +// +// ====================================================================== + +#ifndef INCLUDED_LeakFinder_H +#define INCLUDED_LeakFinder_H + +// ====================================================================== + +#include "sharedDebug/CallStack.h" + +#include +#include +#include + +// ====================================================================== + +class LeakFinder +{ +public: + + // -------------------------------------------------------- + + LeakFinder(); + ~LeakFinder(); + + // -------------------------------------------------------- + + void clear() { liveObjects.clear(); } + + void onAllocate(void *object); + void onFree(void *object); + + void onReference(void *object); + void onDereference(void *object); + + // -------------------------------------------------------- + + struct ReferenceCountChange + { + enum ChangeDirection { up, down }; + + ReferenceCountChange() : dir(up) {} + + ChangeDirection dir; + CallStack callStack; + }; + + typedef std::list ReferenceCountingData; + + struct LiveObject + { + void *object; + CallStack callStack; + const ReferenceCountingData *referenceData; + }; + typedef std::vector LiveObjectList; + + // -------------------------------------------------------- + + bool empty() const { return liveObjects.empty(); } + int size() const { return liveObjects.size(); } + void getCurrentObjects(LiveObjectList &o_objects) const; + + // -------------------------------------------------------- + + void debugPrint() const; + +protected: + + void _printReferenceCountingData(const ReferenceCountingData &) const; + + struct ptr_hash { + size_t operator()(void *p) const { return std::hash()((unsigned long)p); } + }; + + struct ObjectData + { + ObjectData() : referenceData(0) { } + ~ObjectData() { if (referenceData) { delete referenceData; } } + + CallStack callStack; + ReferenceCountingData *referenceData; + }; + + typedef std::hash_map ObjectMap; + + ObjectMap liveObjects; +}; + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedDebug/src/shared/PixCounter.cpp b/engine/shared/library/sharedDebug/src/shared/PixCounter.cpp new file mode 100644 index 00000000..5c67b80a --- /dev/null +++ b/engine/shared/library/sharedDebug/src/shared/PixCounter.cpp @@ -0,0 +1,505 @@ +// ====================================================================== +// +// PixCounter.cpp +// Copyright 2004 Sony Online Entertainment +// All Rights Reserved +// +// ====================================================================== + +#include "sharedDebug/FirstSharedDebug.h" +#include "sharedDebug/PixCounter.h" + +#if PRODUCTION == 0 + +#include "sharedFoundation/ExitChain.h" + +#include +#include + +// ====================================================================== + +#ifdef _WIN32 +#define STDCALL __stdcall +#else +#define STDCALL +#endif + +namespace PixCounterNamespace +{ + typedef std::vector Counters; + typedef BOOL (STDCALL *SetCounterFloat)(char const * name, float * value); + typedef BOOL (STDCALL *SetCounterInt)(char const * name, int * value); + typedef BOOL (STDCALL *SetCounterInt64)(char const * name, __int64 * value); + typedef BOOL (STDCALL *SetCounterString)(char const * name, char const * const * value); + typedef void (STDCALL *PollRoutine)(); + typedef void (STDCALL *ClientInstall)(bool * active, PollRoutine pollRoutine); + + void remove(); + void STDCALL poll(); + +#ifdef _WIN32 + HINSTANCE ms_pixDll; +#endif + SetCounterFloat ms_setCounterFloat; + SetCounterInt ms_setCounterInt; + SetCounterInt64 ms_setCounterInt64; + SetCounterString ms_setCounterString; + Counters ms_counters; + bool ms_enabled; + bool ms_connectedToPixProfiler; +} +using namespace PixCounterNamespace; + +// ====================================================================== + +void PixCounter::install() +{ +#ifdef _WIN32 + ms_pixDll = LoadLibrary("C:\\Program Files\\Microsoft DirectX 9.0 SDK (October 2004)\\Utilities\\PIX\\SoePix.PIXPlugin"); + if (ms_pixDll) + { + ms_setCounterFloat = reinterpret_cast (GetProcAddress(ms_pixDll, "SetCounterFloat")); + ms_setCounterInt = reinterpret_cast (GetProcAddress(ms_pixDll, "SetCounterInt")); + ms_setCounterInt64 = reinterpret_cast (GetProcAddress(ms_pixDll, "SetCounterInt64")); + ms_setCounterString = reinterpret_cast(GetProcAddress(ms_pixDll, "SetCounterString")); + + NOT_NULL(ms_setCounterFloat); + NOT_NULL(ms_setCounterInt); + NOT_NULL(ms_setCounterInt64); + NOT_NULL(ms_setCounterString); + + ClientInstall clientInstall = reinterpret_cast(GetProcAddress(ms_pixDll, "ClientInstall")); + NOT_NULL(clientInstall); + (*clientInstall)(&ms_connectedToPixProfiler, &PixCounterNamespace::poll); + + ExitChain::add(PixCounterNamespace::remove, "PixCounter"); + } +#endif +} + +// ---------------------------------------------------------------------- + +void PixCounterNamespace::remove() +{ +#ifdef _WIN32 + FreeLibrary(ms_pixDll); + ms_pixDll = NULL; +#endif + + ms_setCounterFloat = NULL; + ms_setCounterInt = NULL; + ms_setCounterInt64 = NULL; + ms_setCounterString = NULL; + + // Make sure the counter memory gets freed at this point + Counters().swap(ms_counters); +} + +// ---------------------------------------------------------------------- + +void PixCounter::enable() +{ + if (!ms_enabled) + { + resetAllCounters(); + ms_enabled = true; + } +} + +// ---------------------------------------------------------------------- + +void PixCounter::disable() +{ + ms_enabled = false; + resetAllCounters(); +} + +// ---------------------------------------------------------------------- + +bool PixCounter::isEnabled() +{ + return ms_enabled; +} + +// ---------------------------------------------------------------------- + +bool PixCounter::connectedToPixProfiler() +{ + return ms_connectedToPixProfiler; +} + +// ---------------------------------------------------------------------- + +void PixCounter::bindToCounter(char const * name, int * value) +{ + if (ms_setCounterInt && (*ms_setCounterInt)(name, value) == false) + DEBUG_WARNING(true, ("Could not bind int pix counter %s", name)); +} + +// ---------------------------------------------------------------------- + +void PixCounter::bindToCounter(char const * name, float * value) +{ + if (ms_setCounterFloat && (*ms_setCounterFloat)(name, value) == false) + DEBUG_WARNING(true, ("Could not bind float pix counter %s", name)); +} + +// ---------------------------------------------------------------------- + +void PixCounter::bindToCounter(char const * name, __int64 * value) +{ + if (ms_setCounterInt64 && (*ms_setCounterInt64)(name, value) == false) + DEBUG_WARNING(true, ("Could not bind int64 pix counter %s", name)); +} + +// ---------------------------------------------------------------------- + +void PixCounter::bindToCounter(char const * name, char const * const * value) +{ + if (ms_setCounterString && (*ms_setCounterString)(name, value) == false) + DEBUG_WARNING(true, ("Could not bind string pix counter %s", name)); +} + +// ---------------------------------------------------------------------- + +void PixCounter::resetAllCounters() +{ + Counters::iterator const iEnd = ms_counters.end(); + for (Counters::iterator i = ms_counters.begin(); i != iEnd; ++i) + (*i)->reset(); +} + +// ---------------------------------------------------------------------- + +void PixCounter::update() +{ + if (!ms_connectedToPixProfiler) + poll(); +} + +// ---------------------------------------------------------------------- + +void STDCALL PixCounterNamespace::poll() +{ + if (ms_enabled) + { + Counters::iterator const iEnd = ms_counters.end(); + for (Counters::iterator i = ms_counters.begin(); i != iEnd; ++i) + (*i)->poll(); + } +} + +// ====================================================================== + +PixCounter::Counter::Counter() +{ +} + +// ---------------------------------------------------------------------- + +PixCounter::Counter::~Counter() +{ + disable(); +} + +// ---------------------------------------------------------------------- + +void PixCounter::Counter::enable() +{ + ms_counters.push_back(this); +} + +// ---------------------------------------------------------------------- + +void PixCounter::Counter::disable() +{ + Counters::iterator i = std::find(ms_counters.begin(), ms_counters.end(), this); + if (i != ms_counters.end()) + ms_counters.erase(i); +} + +// ====================================================================== + +PixCounter::Integer::Integer() +: Counter(), + m_lastFrameValue(0), + m_currentValue(0) +{ +} + +// ---------------------------------------------------------------------- + +void PixCounter::Integer::bindToCounter(char const * name) +{ + PixCounter::bindToCounter(name, &m_lastFrameValue); + enable(); +} + +// ---------------------------------------------------------------------- + +void PixCounter::Integer::poll() +{ + m_lastFrameValue = m_currentValue; +} + +// ---------------------------------------------------------------------- + +void PixCounter::Integer::reset() +{ + m_currentValue = 0; +} + +// ---------------------------------------------------------------------- + +void PixCounter::Integer::operator =(int value) +{ + m_currentValue = value; +} + +// ---------------------------------------------------------------------- + +void PixCounter::Integer::operator +=(int value) +{ + m_currentValue += value; +} + +// ---------------------------------------------------------------------- + +void PixCounter::Integer::operator ++() +{ + ++m_currentValue; +} + +// ---------------------------------------------------------------------- + +int PixCounter::Integer::getCurrentValue() const +{ + return m_currentValue; +} + +// ---------------------------------------------------------------------- + +int PixCounter::Integer::getLastFrameValue() const +{ + return m_lastFrameValue; +} + +// ====================================================================== + +PixCounter::ResetInteger::ResetInteger() +: Integer() +{ +} + +// ---------------------------------------------------------------------- + +void PixCounter::ResetInteger::poll() +{ + Integer::poll(); + reset(); +} + +// ====================================================================== + +PixCounter::Float::Float() +: Counter(), + m_lastFrameValue(0.0f), + m_currentValue(0.0f) +{ +} + +// ---------------------------------------------------------------------- + +void PixCounter::Float::bindToCounter(char const * name) +{ + PixCounter::bindToCounter(name, &m_lastFrameValue); + enable(); +} + +// ---------------------------------------------------------------------- + +void PixCounter::Float::poll() +{ + m_lastFrameValue = m_currentValue; +} + +// ---------------------------------------------------------------------- + +void PixCounter::Float::reset() +{ + m_currentValue = 0.0f; +} + +// ---------------------------------------------------------------------- + +void PixCounter::Float::operator =(float value) +{ + m_currentValue = value; +} + +// ---------------------------------------------------------------------- + +void PixCounter::Float::operator +=(float value) +{ + m_currentValue += value; +} + +// ---------------------------------------------------------------------- + +float PixCounter::Float::getCurrentValue() const +{ + return m_currentValue; +} + +// ---------------------------------------------------------------------- + +float PixCounter::Float::getLastFrameValue() const +{ + return m_lastFrameValue; +} + +// ====================================================================== + +PixCounter::ResetFloat::ResetFloat() +: Float() +{ +} + +// ---------------------------------------------------------------------- + +void PixCounter::ResetFloat::poll() +{ + Float::poll(); + reset(); +} + +// ====================================================================== + +PixCounter::String::String() +: Counter(), + m_enabled(false), + m_lastFrameValue(), + m_lastFrameValuePointer(NULL), + m_currentValue() +{ +} + +// ---------------------------------------------------------------------- + +void PixCounter::String::bindToCounter(char const * name) +{ + PixCounter::bindToCounter(name, &m_lastFrameValuePointer); + enable(); +} + +// ---------------------------------------------------------------------- + +void PixCounter::String::poll() +{ + m_lastFrameValue = m_currentValue; + m_lastFrameValuePointer = &(m_lastFrameValue.c_str()[0]); +} + +// ---------------------------------------------------------------------- + +void PixCounter::String::pollAndReset() +{ + m_lastFrameValue.swap(m_currentValue); + m_currentValue.clear(); + m_lastFrameValuePointer = &(m_lastFrameValue.c_str()[0]); +} + +// ---------------------------------------------------------------------- + +void PixCounter::String::reset() +{ + m_currentValue.clear(); +} + +// ---------------------------------------------------------------------- + +void PixCounter::String::enable() +{ + Counter::enable(); + m_enabled = true; +} + +// ---------------------------------------------------------------------- + +void PixCounter::String::disable() +{ + Counter::disable(); + m_enabled = false; + m_lastFrameValue.clear(); + m_currentValue.clear(); +} + +// ---------------------------------------------------------------------- + +void PixCounter::String::operator =(const char * value) +{ + if (ms_enabled && m_enabled) + m_currentValue = value; +} + +// ---------------------------------------------------------------------- + +void PixCounter::String::operator +=(const char * value) +{ + if (ms_enabled && m_enabled) + m_currentValue += value; +} + +// ---------------------------------------------------------------------- + +void PixCounter::String::set(const char * format, ...) +{ + if (ms_enabled && m_enabled) + { + va_list va; + va_start(va, format); + + char buffer[512]; + vsnprintf(buffer, sizeof(buffer), format, va); + buffer[sizeof(buffer-1)] = '\0'; + operator =(buffer); + + va_end(va); + } +} + +// ---------------------------------------------------------------------- + +void PixCounter::String::append(const char * format, ...) +{ + if (ms_enabled && m_enabled) + { + va_list va; + va_start(va, format); + + char buffer[512]; + vsnprintf(buffer, sizeof(buffer), format, va); + buffer[sizeof(buffer)-1] = '\0'; + operator +=(buffer); + + va_end(va); + } +} + +// ====================================================================== + +PixCounter::ResetString::ResetString() +: String() +{ +} + +// ---------------------------------------------------------------------- + +void PixCounter::ResetString::poll() +{ + String::pollAndReset(); +} + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedDebug/src/shared/PixCounter.h b/engine/shared/library/sharedDebug/src/shared/PixCounter.h new file mode 100644 index 00000000..c7e58085 --- /dev/null +++ b/engine/shared/library/sharedDebug/src/shared/PixCounter.h @@ -0,0 +1,184 @@ +// ====================================================================== +// +// PixCounter.h +// Copyright 2004, Sony Online Entertainment +// All Rights Reserved +// +// ====================================================================== + +#ifndef INCLUDED_PixCounter_H +#define INCLUDED_PixCounter_H + +// ====================================================================== + +#include "sharedFoundation/Production.h" +#include + +// ====================================================================== + +#if PRODUCTION == 0 + +class PixCounter +{ +public: + + /// Base class for all other counters + class Counter + { + public: + Counter(); + virtual ~Counter(); + + virtual void poll() = 0; + virtual void reset() = 0; + virtual void enable(); + virtual void disable(); + + private: + Counter(Counter const &); + Counter & operator = (Counter const &); + }; + + /// General integer counter + class Integer : public Counter + { + public: + Integer(); + + void bindToCounter(char const * name); + + virtual void poll(); + virtual void reset(); + + void operator =(int value); + void operator +=(int value); + void operator ++(); + + int getCurrentValue() const; + int getLastFrameValue() const; + + private: + Integer(Integer const &); + Integer & operator = (Integer const &); + + private: + int m_lastFrameValue; + int m_currentValue; + }; + + /// Integer counter whose value gets reset to 0 immediately after being polled + class ResetInteger : public Integer + { + public: + ResetInteger(); + virtual void poll(); + private: + ResetInteger(ResetInteger const &); + ResetInteger & operator = (ResetInteger const &); + }; + + /// General float counter + class Float : public Counter + { + public: + Float(); + + void bindToCounter(char const * name); + + virtual void poll(); + virtual void reset(); + + void operator =(float value); + void operator +=(float value); + + float getCurrentValue() const; + float getLastFrameValue() const; + + private: + Float(Float const &); + Float & operator = (Float const &); + + private: + float m_lastFrameValue; + float m_currentValue; + }; + + /// Float counter whose value gets reset to 0 immediately after being polled + class ResetFloat : public Float + { + public: + ResetFloat(); + virtual void poll(); + private: + ResetFloat(ResetFloat const &); + ResetFloat & operator = (ResetFloat const &); + }; + + /// General string counter + class String : public Counter + { + public: + String(); + + void bindToCounter(char const * name); + + virtual void poll(); + virtual void reset(); + virtual void enable(); + virtual void disable(); + + void operator =(const char * value); + void operator +=(const char * value); + + void set(const char * format, ...); + void append(const char * format, ...); + + private: + String(String const &); + String & operator = (String const &); + + protected: + void pollAndReset(); + + private: + bool m_enabled; + std::string m_lastFrameValue; + char const * m_lastFrameValuePointer; + std::string m_currentValue; + }; + + /// String counter whose value gets reset to "" immediately after being polled + class ResetString : public String + { + public: + ResetString(); + virtual void poll(); + private: + ResetString(ResetString const &); + ResetString & operator = (ResetString const &); + }; + +public: + + static void install(); + + static void update(); + + static void enable(); + static void disable(); + static bool isEnabled(); + static bool connectedToPixProfiler(); + + static void resetAllCounters(); + + static void bindToCounter(char const * name, int * value); + static void bindToCounter(char const * name, float * value); + static void bindToCounter(char const * name, __int64 * value); + static void bindToCounter(char const * name, char const * const * value); +}; + +#endif + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedDebug/src/shared/Profiler.cpp b/engine/shared/library/sharedDebug/src/shared/Profiler.cpp new file mode 100644 index 00000000..a0bd3089 --- /dev/null +++ b/engine/shared/library/sharedDebug/src/shared/Profiler.cpp @@ -0,0 +1,874 @@ +// ====================================================================== +// +// Profiler.cpp +// +// Copyright 2003 Sony Online Entertainment +// All Rights Reserved +// +// ====================================================================== + +#include "sharedDebug/FirstSharedDebug.h" +#include "sharedDebug/Profiler.h" + +#include "sharedDebug/DebugFlags.h" +#include "sharedDebug/DebugKey.h" +#include "sharedDebug/ProfilerTimer.h" +#include "sharedFoundation/ConfigFile.h" +#include "sharedFoundation/ExitChain.h" +#include "sharedFoundation/MemoryBlockManager.h" +#include "sharedFoundation/MemoryBlockManagerMacros.h" + +#include +#include + +// ====================================================================== + +namespace ProfilerNamespace +{ + struct ProfilerEntry + { + typedef std::vector Children; + + const char *name; + int parent; + Children children; + int totalCalls; + ProfilerTimer::Type totalTime; + }; + + class VisibleExpandableEntry + { + public: + + VisibleExpandableEntry(char const * name); + ~VisibleExpandableEntry(); + + char const * getName() const; + + bool isExpanded() const; + void setExpanded(bool opened); + void toggleExpanded(); + + void markAllChildrenInvisible(); + VisibleExpandableEntry * findOrAddExpandableChild(char const *name); + bool pruneInvisibleChildren(); + + private: + + typedef std::vector Children; + + private: + + static bool ms_destroyedSelected; + + private: + + char const * m_name; + Children m_children; + bool m_expanded; + bool m_visible; + }; + + typedef std::vector ProfilerEntries; + typedef std::vector SortedProfilerEntries; + + struct SortMultisiteEntries + { + bool operator()(ProfilerEntry const * lhs, ProfilerEntry const * rhs) const; + }; + + void formatInteger(int length, int value); + void formatString(const char *string); + bool formatEntry(int indent, ProfilerTimer::Type totalTime, int totalCalls, bool expandable, bool expanded, bool selected, char const *name); + void processEntry(char const *rootName, int indent, ProfilerEntry const & entry, VisibleExpandableEntry * opened); + void enterWithTime(char const *name, ProfilerTimer::Type time); + void leaveWithTime(char const *name, ProfilerTimer::Type time); + void generateReport(); + bool myCompare(const char *a, const char *b); + + + std::vector ms_profilerEntryStack; + ProfilerEntries * ms_profilerEntriesCurrent; + ProfilerEntries * ms_profilerEntriesLast; + ProfilerEntries ms_profilerEntries1; + ProfilerEntries ms_profilerEntries2; + + char const * ms_rootProfilerName; + + VisibleExpandableEntry * ms_rootVisibleExpandableEntry; + VisibleExpandableEntry * ms_beforeSelectedVisibleExpandableEntry; + VisibleExpandableEntry * ms_selectedVisibleExpandableEntry; + VisibleExpandableEntry * ms_afterSelectedVisibleExpandableEntry; + VisibleExpandableEntry * ms_previousVisibleExpandableEntry; + + std::vector ms_text; + ProfilerTimer::Type ms_timeDivisor; + ProfilerTimer::Type ms_percentageDivisor; + ProfilerTimer::Type ms_frequency; + bool ms_enabled = true; + bool ms_desiredEnabled = true; + bool ms_debugKeyContext; + bool ms_debugReportFlag; + bool ms_debugReportLogFlag; + bool ms_temporaryExpandAll; + bool ms_multisiteCallerSummary; + bool ms_setRoot; + bool ms_selectedUp; + bool ms_selectedDown; + bool ms_selectedExpand; + bool ms_selectedCollapse; + bool ms_selectedToggle; + int ms_displayPercentageMinimum; + int ms_stackDepth; + SortedProfilerEntries ms_sortedProfilerEntries; + +} +using namespace ProfilerNamespace; + +bool VisibleExpandableEntry::ms_destroyedSelected; + +// ====================================================================== + +VisibleExpandableEntry::VisibleExpandableEntry(char const * name) +: + m_name(name), + m_children(), + m_expanded(false), + m_visible(true) +{ +} + +// ---------------------------------------------------------------------- + +VisibleExpandableEntry::~VisibleExpandableEntry() +{ + // handle case where the selected item gets deleted + if (this == ms_selectedVisibleExpandableEntry) + ms_destroyedSelected = true; + + while (!m_children.empty()) + { + VisibleExpandableEntry *back = m_children.back(); + m_children.pop_back(); + delete back; + } +} + +// ---------------------------------------------------------------------- + +inline char const * VisibleExpandableEntry::getName() const +{ + return m_name; +} + +// ---------------------------------------------------------------------- + +inline bool VisibleExpandableEntry::isExpanded() const +{ + return m_expanded || ms_temporaryExpandAll; +} + +// ---------------------------------------------------------------------- + +inline void VisibleExpandableEntry::setExpanded(bool expanded) +{ + m_expanded = expanded; +} + +// ---------------------------------------------------------------------- + +inline void VisibleExpandableEntry::toggleExpanded() +{ + m_expanded = !m_expanded; +} + +// ---------------------------------------------------------------------- + +void VisibleExpandableEntry::markAllChildrenInvisible() +{ + Children::iterator const iEnd = m_children.end(); + for (Children::iterator i = m_children.begin(); i != iEnd; ++i) + (*i)->m_visible = false; +} + +// ---------------------------------------------------------------------- + +VisibleExpandableEntry * VisibleExpandableEntry::findOrAddExpandableChild(char const *name) +{ + Children::iterator const iEnd = m_children.end(); + for (Children::iterator i = m_children.begin(); i != iEnd; ++i) + { + VisibleExpandableEntry * child = *i; + if (child->m_name == name) + { + child->m_visible = true; + return child; + } + } + + m_children.push_back(new VisibleExpandableEntry(name)); + return m_children.back(); +} + +// ---------------------------------------------------------------------- + +bool VisibleExpandableEntry::pruneInvisibleChildren() +{ + for (Children::iterator i = m_children.begin(); i != m_children.end(); ) + if ((*i)->m_visible == false) + { + delete *i; + m_children.erase(i); + } + else + ++i; + + bool const result = ms_destroyedSelected; + ms_destroyedSelected = false; + return result; +} + +// ====================================================================== + +void Profiler::install() +{ + ExitChain::add(Profiler::remove, "Profiler::remove"); + + ms_profilerEntriesCurrent = &ms_profilerEntries1; + ms_profilerEntriesLast = &ms_profilerEntries2; + ms_rootVisibleExpandableEntry = new VisibleExpandableEntry(NULL); + ms_rootVisibleExpandableEntry->setExpanded(true); + ms_selectedVisibleExpandableEntry = ms_rootVisibleExpandableEntry; +} + +// ---------------------------------------------------------------------- + +void Profiler::remove() +{ + delete ms_rootVisibleExpandableEntry; + ms_rootVisibleExpandableEntry = NULL; + ms_beforeSelectedVisibleExpandableEntry = NULL; + ms_selectedVisibleExpandableEntry = NULL; + ms_afterSelectedVisibleExpandableEntry = NULL; + ms_previousVisibleExpandableEntry = NULL; + ms_profilerEntriesCurrent = NULL; + ms_profilerEntriesLast = NULL; +} + +// ---------------------------------------------------------------------- + +void Profiler::registerDebugFlags() +{ + DebugKey::registerFlag(ms_debugKeyContext, "profiler"); + DebugFlags::registerFlag(ms_debugReportFlag, "SharedDebug/Profiler", "report", printLastFrameData); + DebugFlags::registerFlag(ms_desiredEnabled, "SharedDebug/Profiler", "enabled"); + DebugFlags::registerFlag(ms_debugReportLogFlag, "SharedDebug/Profiler", "logNextReport"); + DebugFlags::registerFlag(ms_temporaryExpandAll, "SharedDebug/Profiler", "temporaryExpandAll"); + ms_displayPercentageMinimum = ConfigFile::getKeyInt("SharedDebug/Profiler", "displayPercentageMinimum", 0); +} + +// ---------------------------------------------------------------------- + +void Profiler::enable(bool enable) +{ + ms_desiredEnabled = enable; +} + +// ---------------------------------------------------------------------- + +void Profiler::enableProfilerOutput(bool enableOutput) +{ + ms_debugReportFlag = enableOutput; +} + +// ---------------------------------------------------------------------- + +void ProfilerNamespace::formatInteger(int length, int value) +{ + // pad with spaces + for (int i = 0; i < length; ++i) + ms_text.push_back(' '); + + // get a pointer to the last character + char *buffer = &ms_text[ms_text.size()-1]; + + // write the value right to left + do + { + *buffer = static_cast('0' + (value % 10)); + --length; + --buffer; + value /= 10; + } while (length && value); +} + +// ---------------------------------------------------------------------- + +void ProfilerNamespace::formatString(char const *string) +{ + for ( ; *string; ++string) + ms_text.push_back(*string); +} + +// ---------------------------------------------------------------------- + +bool ProfilerNamespace::formatEntry(int indent, ProfilerTimer::Type totalTime, int totalCalls, bool expandable, bool expanded, bool selected, char const * name) +{ + int const percentage = static_cast(totalTime / ms_percentageDivisor); + + if (percentage < ms_displayPercentageMinimum) + return false; + + int const ticks = static_cast(totalTime / ms_timeDivisor); + + // indent + { + for (int i = 0; i < indent; ++i) + ms_text.push_back(' '); + } + + // print number of ticks + formatInteger(7, ticks); + ms_text.push_back(' '); + + // pring % of frame time + formatInteger(3, percentage); + ms_text.push_back('%'); + ms_text.push_back(' '); + + // print the number of calls + formatInteger(8, totalCalls); + ms_text.push_back(' '); + + if (expandable) + { + // print the expanded state + if (expanded) + ms_text.push_back('-'); + else + ms_text.push_back('+'); + + // print the selection marker + if (selected) + ms_text.push_back('>'); + else + ms_text.push_back(' '); + } + else + { + ms_text.push_back(' '); + ms_text.push_back(' '); + } + + // print the entry name + formatString(name); + ms_text.push_back('\n'); + + return true; +} + +// ---------------------------------------------------------------------- + +void ProfilerNamespace::processEntry(char const *rootName, int indent, ProfilerEntry const & entry, VisibleExpandableEntry * visibleExpandableEntry) +{ + if (rootName == entry.name) + rootName = NULL; + + if (!rootName) + { + if (!formatEntry(indent, entry.totalTime, entry.totalCalls, visibleExpandableEntry != NULL, visibleExpandableEntry && visibleExpandableEntry->isExpanded(), visibleExpandableEntry == ms_selectedVisibleExpandableEntry, entry.name)) + return; + } + + if (visibleExpandableEntry) + { + // keep track of before and after the selected entry + if (ms_selectedVisibleExpandableEntry == visibleExpandableEntry) + ms_beforeSelectedVisibleExpandableEntry = ms_previousVisibleExpandableEntry; + if (!entry.children.empty()) + { + if (ms_previousVisibleExpandableEntry == ms_selectedVisibleExpandableEntry) + ms_afterSelectedVisibleExpandableEntry = visibleExpandableEntry; + ms_previousVisibleExpandableEntry = visibleExpandableEntry; + } + + // process the children + visibleExpandableEntry->markAllChildrenInvisible(); + + // indent if we have already seen the root + if (!rootName) + indent += 2; + + if (visibleExpandableEntry->isExpanded()) + { + ProfilerEntry::Children::const_iterator iEnd = entry.children.end(); + for (ProfilerEntry::Children::const_iterator i = entry.children.begin(); i != iEnd; ++i) + { + ProfilerEntry const & child = (*ms_profilerEntriesLast)[*i]; + + if (child.children.empty()) + { + processEntry(rootName, indent, child, NULL); + } + else + { + VisibleExpandableEntry * childVisibleExpandableEntry = visibleExpandableEntry->findOrAddExpandableChild(child.name); + processEntry(rootName, indent, child, childVisibleExpandableEntry); + } + } + } + + if (visibleExpandableEntry->pruneInvisibleChildren()) + ms_selectedVisibleExpandableEntry = visibleExpandableEntry; + } +} + +// ---------------------------------------------------------------------- + +inline bool SortMultisiteEntries::operator()(ProfilerEntry const * lhs, ProfilerEntry const * rhs) const +{ + return lhs->name < rhs->name; +} + +// ---------------------------------------------------------------------- + +void ProfilerNamespace::generateReport() +{ + PROFILER_AUTO_BLOCK_DEFINE("Profiler::generateReport"); + + if (!ms_profilerEntriesLast->empty()) + { + if (ms_debugKeyContext) + { + if (DebugKey::isPressed(1)) + ms_desiredEnabled = !ms_desiredEnabled; + if (DebugKey::isPressed(2)) + ms_debugReportLogFlag = true; + if (DebugKey::isPressed(3)) + ms_temporaryExpandAll = !ms_temporaryExpandAll; + if (DebugKey::isPressed(4)) + ms_setRoot = true; + if (DebugKey::isPressed(5)) + ms_multisiteCallerSummary = !ms_multisiteCallerSummary; + if (DebugKey::isPressed(6)) + ms_displayPercentageMinimum = clamp(0, ms_displayPercentageMinimum - 1, 100); + if (DebugKey::isPressed(7)) + ms_displayPercentageMinimum = clamp(0, ms_displayPercentageMinimum + 1, 100); + if (DebugKey::isPressed(8)) + Profiler::selectionMoveUp(); + if (DebugKey::isPressed(9)) + Profiler::selectionMoveDown(); + if (DebugKey::isPressed(0)) + Profiler::selectionToggleExpanded(); + } + + // try to get the ticks to approximately 1 million per second + ms_timeDivisor = ms_frequency / static_cast(1000000); + if (ms_timeDivisor == 0) + ms_timeDivisor = 1; + + // figure out what to divide by to get the value in percent of total frame time + ms_percentageDivisor = (*ms_profilerEntriesLast)[0].totalTime / static_cast(100); + if (ms_percentageDivisor == 0) + ms_percentageDivisor = 1; + + formatString("Profiler (min "); + formatInteger(3, ms_displayPercentageMinimum); + formatString("%):\n"); + + ProfilerEntry const & rootEntry = (*ms_profilerEntriesLast)[0]; + + ms_rootVisibleExpandableEntry->markAllChildrenInvisible(); + ms_rootVisibleExpandableEntry->findOrAddExpandableChild(rootEntry.name); + + ms_previousVisibleExpandableEntry = NULL; + ms_beforeSelectedVisibleExpandableEntry = NULL; + ms_afterSelectedVisibleExpandableEntry = NULL; + processEntry(ms_rootProfilerName, 0, rootEntry, ms_rootVisibleExpandableEntry); + + if (ms_rootVisibleExpandableEntry->pruneInvisibleChildren()) + ms_selectedVisibleExpandableEntry = ms_rootVisibleExpandableEntry; + + if (ms_setRoot) + { + if (ms_rootProfilerName) + ms_rootProfilerName = NULL; + else + ms_rootProfilerName = ms_selectedVisibleExpandableEntry->getName(); + ms_setRoot = false; + } + + if (ms_selectedUp) + { + if (ms_beforeSelectedVisibleExpandableEntry) + ms_selectedVisibleExpandableEntry = ms_beforeSelectedVisibleExpandableEntry; + ms_selectedUp = false; + } + + if (ms_selectedDown) + { + if (ms_afterSelectedVisibleExpandableEntry) + ms_selectedVisibleExpandableEntry = ms_afterSelectedVisibleExpandableEntry; + ms_selectedDown = false; + } + + if (ms_selectedExpand) + { + ms_selectedVisibleExpandableEntry->setExpanded(true); + ms_selectedExpand= false; + } + + if (ms_selectedCollapse) + { + ms_selectedVisibleExpandableEntry->setExpanded(false); + ms_selectedCollapse = false; + } + + if (ms_selectedToggle) + { + ms_selectedVisibleExpandableEntry->toggleExpanded(); + ms_selectedToggle = false; + } + } + + if (ms_multisiteCallerSummary) + { + // generate a vector of all entries + { + ms_sortedProfilerEntries.clear(); + ProfilerEntries::iterator const iEnd = ms_profilerEntriesLast->end(); + for (ProfilerEntries::iterator i = ms_profilerEntriesLast->begin(); i != iEnd; ++i) + ms_sortedProfilerEntries.push_back(&(*i)); + } + + // sort them by name + std::sort(ms_sortedProfilerEntries.begin(), ms_sortedProfilerEntries.end(), SortMultisiteEntries()); + + // find multiple entries with the same name + { + formatString("Multisite entries:\n"); + + SortedProfilerEntries::iterator i = ms_sortedProfilerEntries.begin(); + SortedProfilerEntries::iterator const iEnd = ms_sortedProfilerEntries.end(); + do + { + char const * const iName = (*i)->name; + SortedProfilerEntries::iterator j = i + 1; + bool display = false; + int totalCalls = (*i)->totalCalls; + ProfilerTimer::Type totalTime = (*i)->totalTime; + + // find the span of entries with the same name + while (j != iEnd && iName == (*j)->name) + { + display = true; + totalCalls += (*j)->totalCalls; + totalTime += (*j)->totalTime; + ++j; + } + + // if the name had multiple entries, report so here + if (display) + formatEntry(2, totalTime, totalCalls, false, false, false, iName); + + i = j; + + } while (i != iEnd); + } + + } + + ms_text.push_back('\0'); +} + +// ---------------------------------------------------------------------- + +char const *Profiler::getLastFrameData() +{ + if (ms_text.empty()) + generateReport(); + + return &(ms_text[0]); +} + +// ---------------------------------------------------------------------- + +void Profiler::printLastFrameData() +{ + if (ms_text.empty()) + generateReport(); + + Report::setFlags(Report::RF_print | (ms_debugReportLogFlag ? Report::RF_log : 0)); + Report::puts(&ms_text[0]); + ms_debugReportLogFlag = false; +} + +// ---------------------------------------------------------------------- + +void Profiler::selectionMoveUp() +{ + ms_selectedUp = true; +} + +// ---------------------------------------------------------------------- + +void Profiler::selectionMoveDown() +{ + ms_selectedDown = true; +} + +// ---------------------------------------------------------------------- + +void Profiler::selectionExpand() +{ + ms_selectedExpand = true; +} + +// ---------------------------------------------------------------------- + +void Profiler::selectionCollapse() +{ + ms_selectedCollapse = true; +} + +// ---------------------------------------------------------------------- + +void Profiler::selectionToggleExpanded() +{ + ms_selectedToggle = true; +} + +// ---------------------------------------------------------------------- + +void Profiler::setTemporaryExpandAll(bool temporaryExpandAll) +{ + ms_temporaryExpandAll = temporaryExpandAll; +} + +// ---------------------------------------------------------------------- + +void Profiler::setDisplayPercentageMinimum(int percentage) +{ + ms_displayPercentageMinimum = percentage; +} + +// ---------------------------------------------------------------------- + +bool ProfilerNamespace::myCompare(const char *a, const char *b) +{ + while (*a && *b && tolower(*a) == tolower(*b)) + { + ++a; + ++b; + } + + return *a == '\0'; +} + +// ---------------------------------------------------------------------- + +void Profiler::handleOperation(char const *operation) +{ + if (_stricmp(operation, "up") == 0) + selectionMoveUp(); + else if (_stricmp(operation, "down") == 0) + selectionMoveDown(); + else if (_stricmp(operation, "toggle") == 0) + selectionToggleExpanded(); + else if (_stricmp(operation, "expand") == 0) + selectionExpand(); + else if (_stricmp(operation, "collapse") == 0) + selectionCollapse(); + else if (_stricmp(operation, "enable") == 0) + enable(true); + else if (_stricmp(operation, "disable") == 0) + enable(false); + else if (_stricmp(operation, "enableOutput") == 0) + enableProfilerOutput(true); + else if (_stricmp(operation, "disableOutput") == 0) + enableProfilerOutput(false); + else if (_stricmp(operation, "showAll") == 0) + setTemporaryExpandAll(true); + else if (_stricmp(operation, "showNormal") == 0) + setTemporaryExpandAll(false); + else if (myCompare("displayMinimum", operation)) + { + const char *result = strchr(operation, ' '); + if (result) + ms_displayPercentageMinimum = atoi(result+1); + } +} + +// ---------------------------------------------------------------------- + +void ProfilerNamespace::enterWithTime(char const *name, ProfilerTimer::Type time) +{ + if (ms_stackDepth == 0) + { + ms_enabled = ms_desiredEnabled; + if (!ms_enabled) + { + ms_text.clear(); + ms_text.push_back('\0'); + } + } + ++ms_stackDepth; + if (!ms_enabled) + return; + + // search for another call to this block from the parent + int entryIndex = -1; + ProfilerEntry *entry = NULL; + if (!ms_profilerEntryStack.empty()) + { + ProfilerEntry &parent = (*ms_profilerEntriesCurrent)[ms_profilerEntryStack.back()]; + ProfilerEntry::Children::iterator iEnd = parent.children.end(); + for (ProfilerEntry::Children::iterator i = parent.children.begin(); i != iEnd; ++i) + { + entryIndex = *i; + ProfilerEntry &check = (*ms_profilerEntriesCurrent)[entryIndex]; + if (check.name == name) + { + entry = ✓ + break; + } + } + } + + // no previous call to this block, so create a new entry + if (!entry) + { + entryIndex = ms_profilerEntriesCurrent->size(); + ms_profilerEntriesCurrent->push_back(ProfilerEntry()); + entry = &ms_profilerEntriesCurrent->back(); + + entry->name = name; + if (ms_profilerEntryStack.empty()) + { + entry->parent = -1; + } + else + { + entry->parent = ms_profilerEntryStack.back(); + (*ms_profilerEntriesCurrent)[entry->parent].children.push_back(entryIndex); + } + entry->totalCalls = 0; + entry->totalTime = 0; + + } + + // record the profiling time + entry->totalCalls += 1; + entry->totalTime -= time; + ms_profilerEntryStack.push_back(entryIndex); +} + +// ---------------------------------------------------------------------- + +void ProfilerNamespace::leaveWithTime(char const *name, ProfilerTimer::Type time) +{ + if (!ms_enabled) + return; + + UNREF(name); + if (ms_profilerEntryStack.empty()) + { + WARNING(true, ("Profiler::leave stack underflow leaving %s", name)); + return; + } + --ms_stackDepth; + + // pop the current entry off the stack + ProfilerEntry &entry= (*ms_profilerEntriesCurrent)[ms_profilerEntryStack.back()]; + ms_profilerEntryStack.pop_back(); + + DEBUG_FATAL(entry.name != name, ("Profiler::leave exiting '%s' but expected '%s'", name, entry.name)); + + // record the time in the entry + if (ms_profilerEntryStack.empty()) + { + // get the calibrated loop end time + ProfilerTimer::getCalibratedTime(time, ms_frequency); + entry.totalTime += time; + + // clear the text so we know the profile data must be regenerated if it is desired + ms_text.clear(); + + // swap the pointers + std::vector * temp = ms_profilerEntriesCurrent; + ms_profilerEntriesCurrent = ms_profilerEntriesLast; + ms_profilerEntriesLast = temp; + + // clear the entries for next frame + ms_profilerEntriesCurrent->clear(); + } + else + { + entry.totalTime += time; + } +} + +// ---------------------------------------------------------------------- + +void Profiler::enter(char const *name) +{ + ProfilerTimer::Type time; + ProfilerTimer::getTime(time); + enterWithTime(name, time); +} + +// ---------------------------------------------------------------------- + +void Profiler::leave(char const *name) +{ + ProfilerTimer::Type time; + ProfilerTimer::getTime(time); + leaveWithTime(name, time); +} + +// ---------------------------------------------------------------------- + +void Profiler::transfer(char const *leaveName, char const *enterName) +{ + ProfilerTimer::Type time; + ProfilerTimer::getTime(time); + leaveWithTime(leaveName, time); + enterWithTime(enterName, time); +} + +// ---------------------------------------------------------------------- + +void Profiler::adjustForLostBlocks(char const *expectingName) +{ + // This deals with cases where we've either missed closing a block or closed a block that wasn't open. + // We prefer to guess that we've failed to close a block, since that is the more common mistake, before + // trying to deal with it as though a block was closed but not opened. + + if (!ms_profilerEntryStack.empty()) + { + // if everything is correct, the back entry will be the expected one, so we don't have to do anything + if ((*ms_profilerEntriesCurrent)[ms_profilerEntryStack.back()].name == expectingName) + return; + for (int i = ms_profilerEntryStack.size()-2; i >= 0; --i) + { + if ((*ms_profilerEntriesCurrent)[ms_profilerEntryStack[i]].name == expectingName) + { + // we've got unclosed blocks, so close them + for (int j = ms_profilerEntryStack.size()-1; j > i; --j) + { + DEBUG_WARNING(true, ("Profiler adjusting for unclosed block '%s'.", (*ms_profilerEntriesCurrent)[ms_profilerEntryStack[j]].name)); + PROFILER_BLOCK_DEFINE(p, (*ms_profilerEntriesCurrent)[ms_profilerEntryStack[j]].name); + PROFILER_BLOCK_LEAVE(p); + } + return; + } + } + } + + DEBUG_WARNING(true, ("Profiler adjusting for unopened block '%s'.", expectingName)); + // we're about to close a block that was not open, so open it + PROFILER_BLOCK_DEFINE(p, expectingName); + PROFILER_BLOCK_ENTER(p); +} + +// ====================================================================== diff --git a/engine/shared/library/sharedDebug/src/shared/Profiler.h b/engine/shared/library/sharedDebug/src/shared/Profiler.h new file mode 100644 index 00000000..aaa11d16 --- /dev/null +++ b/engine/shared/library/sharedDebug/src/shared/Profiler.h @@ -0,0 +1,287 @@ +// ====================================================================== +// +// Profiler.h +// +// Copyright 2003 Sony Online Entertainment +// All Rights Reserved +// +// ====================================================================== + +#ifndef INCLUDED_Profiler_H +#define INCLUDED_Profiler_H + +// ====================================================================== + +#include "sharedFoundation/Production.h" + +// ====================================================================== + +class ProfilerBlock +{ +public: + + ProfilerBlock(char const *name); + + void enter(); + void leave(); + void transfer(char const *newName); + void adjustForLostBlocks(); + +private: + + ProfilerBlock(); + ProfilerBlock(ProfilerBlock const &); + ProfilerBlock & operator =(ProfilerBlock const &); + +private: + + char const * m_name; +}; + +// ---------------------------------------------------------------------- + +class ProfilerAutoBlock +{ +public: + + ProfilerAutoBlock(char const *name); + ~ProfilerAutoBlock(); + + void transfer(char const *newName); + +private: + + ProfilerAutoBlock(); + ProfilerAutoBlock(ProfilerAutoBlock const &); + ProfilerAutoBlock & operator =(ProfilerAutoBlock const &); + +private: + + ProfilerBlock m_profilerBlock; +}; + +// ---------------------------------------------------------------------- + +class ProfilerAutoBlockCheck +{ +public: + + ProfilerAutoBlockCheck(char const *name); + ~ProfilerAutoBlockCheck(); + + void transfer(char const *newName); + +private: + + ProfilerAutoBlockCheck(); + ProfilerAutoBlockCheck(ProfilerAutoBlockCheck const &); + ProfilerAutoBlockCheck & operator =(ProfilerAutoBlockCheck const &); + +private: + + ProfilerBlock m_profilerBlock; +}; + +//---------------------------------------------------------------------- + +class CuiManager; + +// ---------------------------------------------------------------------- + +class Profiler +{ + friend class ProfilerBlock; + friend class CuiManager; + +public: + + static void install(); + static void remove(); + static void registerDebugFlags(); + + static void enable(bool enabled); + static void enableProfilerOutput(bool enabled); + static char const *getLastFrameData(); + static void printLastFrameData(); + + static void selectionMoveUp(); + static void selectionMoveDown(); + static void selectionExpand(); + static void selectionCollapse(); + static void selectionToggleExpanded(); + static void setTemporaryExpandAll(bool temporaryExpandAll); + static void setDisplayPercentageMinimum(int percentage); + static void handleOperation(char const *operation); + +private: + + static void DLLEXPORT enter(char const *name); + static void DLLEXPORT leave(char const *name); + static void DLLEXPORT transfer(char const *leaveName, char const *enterName); + static void adjustForLostBlocks(char const *nextName); +}; + +// ====================================================================== + +inline ProfilerBlock::ProfilerBlock(const char *name) +: + m_name(name) +{ +} + +// ---------------------------------------------------------------------- + +inline void ProfilerBlock::enter() +{ + Profiler::enter(m_name); +} + +// ---------------------------------------------------------------------- + +inline void ProfilerBlock::leave() +{ + Profiler::leave(m_name); +} + +// ---------------------------------------------------------------------- + +inline void ProfilerBlock::transfer(char const *name) +{ + Profiler::transfer(m_name, name); + m_name = name; +} + +// ---------------------------------------------------------------------- + +inline void ProfilerBlock::adjustForLostBlocks() +{ + Profiler::adjustForLostBlocks(m_name); +} + +// ====================================================================== + +inline ProfilerAutoBlock::ProfilerAutoBlock(char const *name) +: + m_profilerBlock(name) +{ + m_profilerBlock.enter(); +} + +// ---------------------------------------------------------------------- + +inline ProfilerAutoBlock::~ProfilerAutoBlock() +{ + m_profilerBlock.leave(); +} + +// ---------------------------------------------------------------------- + +inline void ProfilerAutoBlock::transfer(char const *name) +{ + m_profilerBlock.transfer(name); +} + +// ====================================================================== + +inline ProfilerAutoBlockCheck::ProfilerAutoBlockCheck(char const *name) +: + m_profilerBlock(name) +{ + m_profilerBlock.enter(); +} + +// ---------------------------------------------------------------------- + +inline ProfilerAutoBlockCheck::~ProfilerAutoBlockCheck() +{ + m_profilerBlock.adjustForLostBlocks(); + m_profilerBlock.leave(); +} + +// ---------------------------------------------------------------------- + +inline void ProfilerAutoBlockCheck::transfer(char const *name) +{ + m_profilerBlock.transfer(name); +} + +// ====================================================================== + +#if defined(_DEBUG) || defined(PLATFORM_LINUX) + + #define PROFILER_BLOCK_DEFINE(a, b) ProfilerBlock a ( b ) + #define PROFILER_BLOCK_ENTER(a) a.enter() + #define PROFILER_BLOCK_LEAVE(a) a.leave() + #define PROFILER_BLOCK_TRANSFER(a,b) a.transfer(b) + #define PROFILER_BLOCK_LOST_CHECK(a) a.adjustForLostBlocks() + + #define PABD_PASTE(a,b) a##b + #define PABD_XPASTE(a,b) PABD_PASTE(a,b) + #define PROFILER_AUTO_BLOCK_DEFINE(a) ProfilerAutoBlock PABD_XPASTE(profilerAutoBlock, __LINE__) ( a ) + #define PROFILER_AUTO_BLOCK_CHECK_DEFINE(a) ProfilerAutoBlockCheck PABD_XPASTE(profilerAutoBlockCheck, __LINE__) ( a ) + #define PROFILER_NAMED_AUTO_BLOCK_DEFINE(a,b) ProfilerAutoBlock a ( b ) + #define PROFILER_NAMED_AUTO_BLOCK_TRANSFER(a,b) a.transfer(b) + + #define PROFILER_GET_LAST_FRAME_DATA() Profiler::getLastFrameData() + +#else + + #define PROFILER_BLOCK_DEFINE(a, b) + #define PROFILER_BLOCK_CHECK_DEFINE(a, b) + #define PROFILER_BLOCK_ENTER(a) NOP + #define PROFILER_BLOCK_LEAVE(a) NOP + #define PROFILER_BLOCK_TRANSFER(a,b) NOP + #define PROFILER_BLOCK_LOST_CHECK(a) NOP + + #define PROFILER_AUTO_BLOCK_DEFINE(A) NOP + #define PROFILER_AUTO_BLOCK_CHECK_DEFINE(a) NOP + #define PROFILER_NAMED_AUTO_BLOCK_DEFINE(a,b) + #define PROFILER_NAMED_AUTO_BLOCK_TRANSFER(a,b) NOP + + #define PROFILER_GET_LAST_FRAME_DATA() "" + +#endif + +// ====================================================================== +// Macro functions that stick around for all non-production builds. +// [NP = no/non-production] +// ====================================================================== + +#if (PRODUCTION == 0) + + #define NP_PROFILER_BLOCK_DEFINE(a, b) ProfilerBlock a ( b ) + #define NP_PROFILER_BLOCK_ENTER(a) a.enter() + #define NP_PROFILER_BLOCK_LEAVE(a) a.leave() + #define NP_PROFILER_BLOCK_TRANSFER(a,b) a.transfer(b) + #define NP_PROFILER_BLOCK_LOST_CHECK(a) a.adjustForLostBlocks() + + #define NP_PABD_PASTE(a,b) a##b + #define NP_PABD_XPASTE(a,b) NP_PABD_PASTE(a,b) + #define NP_PROFILER_AUTO_BLOCK_DEFINE(a) ProfilerAutoBlock NP_PABD_XPASTE(profilerAutoBlock, __LINE__) ( a ) + #define NP_PROFILER_AUTO_BLOCK_CHECK_DEFINE(a) ProfilerAutoBlockCheck NP_PABD_XPASTE(profilerAutoBlockCheck, __LINE__) ( a ) + #define NP_PROFILER_NAMED_AUTO_BLOCK_DEFINE(a,b) ProfilerAutoBlock a ( b ) + #define NP_PROFILER_NAMED_AUTO_BLOCK_TRANSFER(a,b) a.transfer(b) + + #define NP_PROFILER_GET_LAST_FRAME_DATA() Profiler::getLastFrameData() + +#else + + #define NP_PROFILER_BLOCK_DEFINE(a, b) + #define NP_PROFILER_BLOCK_CHECK_DEFINE(a, b) + #define NP_PROFILER_BLOCK_ENTER(a) NOP + #define NP_PROFILER_BLOCK_LEAVE(a) NOP + #define NP_PROFILER_BLOCK_TRANSFER(a,b) NOP + #define NP_PROFILER_BLOCK_LOST_CHECK(a) NOP + + #define NP_PROFILER_AUTO_BLOCK_DEFINE(A) NOP + #define NP_PROFILER_AUTO_BLOCK_CHECK_DEFINE(a) NOP + #define NP_PROFILER_NAMED_AUTO_BLOCK_DEFINE(a,b) + #define NP_PROFILER_NAMED_AUTO_BLOCK_TRANSFER(a,b) NOP + + #define NP_PROFILER_GET_LAST_FRAME_DATA() "" + +#endif + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedDebug/src/shared/RemoteDebug.cpp b/engine/shared/library/sharedDebug/src/shared/RemoteDebug.cpp new file mode 100644 index 00000000..84163deb --- /dev/null +++ b/engine/shared/library/sharedDebug/src/shared/RemoteDebug.cpp @@ -0,0 +1,783 @@ +// ====================================================================== +// +// RemoteDebug.cpp +// copyright 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedDebug/FirstSharedDebug.h" +#include "sharedDebug/RemoteDebug.h" +#include "sharedDebug/RemoteDebug_inner.h" + +#include "sharedFoundation/ExitChain.h" + +#include +#include +#include +#include +#include + +// ---------------------------------------------------------------------- + +//static definitions +RemoteDebug::RemoveFunction RemoteDebug::ms_removeFunction; +RemoteDebug::OpenFunction RemoteDebug::ms_openFunction; +RemoteDebug::CloseFunction RemoteDebug::ms_closeFunction; +RemoteDebug::SendFunction RemoteDebug::ms_sendFunction; +RemoteDebug::IsReadyFunction RemoteDebug::ms_isReadyFunction; + +RemoteDebug::StreamMap *RemoteDebug::ms_streams; +RemoteDebug::StaticViewMap *RemoteDebug::ms_staticViews; +RemoteDebug::VariableMap *RemoteDebug::ms_variables; +std::map *RemoteDebug::ms_squelchedStream; +std::map *RemoteDebug::ms_squelchedStatic; +RemoteDebug::VariableValueMap *RemoteDebug::ms_variableValues; +RemoteDebug::MovementFunctionMap *RemoteDebug::ms_upFunctionMap; +RemoteDebug::MovementFunctionMap *RemoteDebug::ms_downFunctionMap; +RemoteDebug::MovementFunctionMap *RemoteDebug::ms_leftFunctionMap; +RemoteDebug::MovementFunctionMap *RemoteDebug::ms_rightFunctionMap; +RemoteDebug::MovementFunctionMap *RemoteDebug::ms_enterFunctionMap; + +bool RemoteDebug::ms_opened; +bool RemoteDebug::ms_installed; +char RemoteDebug::ms_buffer[MAX_BUFFER_SIZE]; +char RemoteDebug::ms_varArgs_buffer[MAX_BUFFER_SIZE]; +uint32 RemoteDebug::ms_nextStream; +uint32 RemoteDebug::ms_nextVariable; +uint32 RemoteDebug::ms_nextStaticView; + +//----------------------------------------------------------------- + +RemoteDebug::~RemoteDebug () +{ +} + +// ====================================================================== + +void RemoteDebug::install(RemoveFunction rm, OpenFunction o, CloseFunction c, SendFunction s, IsReadyFunction irf)//, ReceiveFunction rc) +{ + ms_streams = new StreamMap; + ms_staticViews = new StaticViewMap; + ms_variables = new VariableMap; + ms_squelchedStream = new std::map; + ms_squelchedStatic = new std::map; + ms_variableValues = new VariableValueMap; + + ms_upFunctionMap = new MovementFunctionMap; + ms_downFunctionMap = new MovementFunctionMap; + ms_leftFunctionMap = new MovementFunctionMap; + ms_rightFunctionMap = new MovementFunctionMap; + ms_enterFunctionMap = new MovementFunctionMap; + + ms_nextStream = 0; + ms_nextStaticView = 0; + ms_nextVariable = 0; + + ms_removeFunction = rm; + ms_openFunction = o; + ms_closeFunction = c; + ms_sendFunction = s; + ms_isReadyFunction = irf; + ms_opened = false; + ms_installed = true; +} + +// ---------------------------------------------------------------------- + +void RemoteDebug::remove(void) +{ + if(!ms_installed) + { + DEBUG_FATAL(!ms_installed, ("not installed")); + return; + } + + ms_removeFunction = NULL; + ms_openFunction = NULL; + ms_closeFunction = NULL; + ms_sendFunction = NULL; + + //empty out session-level data + for (StreamMap::iterator it = ms_streams->begin(); it != ms_streams->end(); ++it) + delete it->second; + delete ms_streams; + + for (StaticViewMap::iterator itr = ms_staticViews->begin(); itr != ms_staticViews->end(); ++itr) + delete itr->second; + delete ms_staticViews; + + for (VariableMap::iterator iter = ms_variables->begin(); iter != ms_variables->end(); ++iter) + delete iter->second; + delete ms_variables; + + for (VariableValueMap::iterator iterr = ms_variableValues->begin(); iterr != ms_variableValues->end(); ++iterr) + delete iterr->second; + delete ms_variableValues; + + ms_nextStream = 0; + ms_nextStaticView = 0; + ms_nextVariable = 0; + + delete ms_squelchedStream; + delete ms_squelchedStatic; + delete ms_upFunctionMap; + delete ms_downFunctionMap; + delete ms_leftFunctionMap; + delete ms_rightFunctionMap; + delete ms_enterFunctionMap; + +// delete ms_sessions; + ms_installed = false; +} + +// ---------------------------------------------------------------------- + +void RemoteDebug::open(const char* server, uint16 port) +{ + DEBUG_FATAL(!ms_installed, ("remoteDebug not installed")); + DEBUG_FATAL(ms_opened, ("remoteDebug session already open")); + + ms_openFunction(server, port); +} + +// ---------------------------------------------------------------------- + +uint32 RemoteDebug::registerStream(const std::string& streamName) +{ + DEBUG_FATAL(!ms_installed, ("remoteDebug not installed")); + + uint32 streamNum = 0; + Channel *parent = NULL; + std::string base; + std::string rest = streamName; + //look for subchannels with a double backslash + uint32 baseIndex = 0; + uint32 restIndex = streamName.find('\\'); + while(restIndex != std::string::npos) + { + //pass the '\' + ++restIndex; + //break channel into the base and the rest + base = streamName.substr(baseIndex, restIndex-baseIndex-1); + rest = streamName.substr(restIndex, streamName.size()); + //build the channel + std::string newBase = streamName.substr(0, restIndex-1); + int32 streamNumber = findStream(newBase); + //only add the channel if we don't have it yet + if (streamNumber == -1) + { + Channel *child = new Channel(newBase, parent); + if (parent) + parent->addChild(child); + parent = child; + (*ms_streams)[ms_nextStream] = child; + streamNum = ms_nextStream++; + //every new channel is not squelched by default + (*ms_squelchedStream)[streamNum] = false; + //make sure we send the new base channel to the clients + send(NEW_STREAM, newBase.c_str()); + } + //look for any other subChannel + uint32 oldRestIndex = restIndex; + baseIndex = restIndex; + restIndex = rest.find('\\'); + if (restIndex != std::string::npos) + restIndex += oldRestIndex; + } + //no more sub channels, add this channel + Channel *child = new Channel(streamName, parent); + if (parent) + parent->addChild(child); + (*ms_streams)[ms_nextStream] = child; + streamNum = ms_nextStream++; + //every new channel is not squelched by default + (*ms_squelchedStream)[streamNum] = false; + + //return the new channelID + return streamNum; +} + +// ---------------------------------------------------------------------- + +uint32 RemoteDebug::registerStaticView(const std::string& channelName) +{ + DEBUG_FATAL(!ms_installed, ("remoteDebug not installed")); + uint32 staticViewNum = 0; + Channel *parent = NULL; + std::string base; + std::string rest = channelName; + //look for subchannels with a double backslash + uint32 baseIndex = 0; + uint32 restIndex = channelName.find('\\'); + while(restIndex != std::string::npos) + { + //pass the '\' + ++restIndex; + //break channel into the base and the rest + base = channelName.substr(baseIndex, restIndex-baseIndex-1); + rest = channelName.substr(restIndex, channelName.size()); + //build the channel + std::string newBase = channelName.substr(0, restIndex-1); + int32 channelNumber = findStaticView(newBase); + //only add the channel if we don't have it yet + if (channelNumber == -1) + { + Channel *child = new Channel(newBase, parent); + if (parent) + parent->addChild(child); + parent = child; + (*ms_staticViews)[ms_nextStaticView] = child; + staticViewNum = ms_nextStaticView++; + //every new channel is not squelched by default + (*ms_squelchedStatic)[staticViewNum] = false; + //make sure we send the new base channel to the clients + send(NEW_STATIC, newBase.c_str()); + } + //look for any other subChannel + uint32 oldRestIndex = restIndex; + baseIndex = restIndex; + restIndex = rest.find('\\'); + if (restIndex != std::string::npos) + restIndex += oldRestIndex; + } + //no more sub channels, add this channel + Channel *child = new Channel(channelName, parent); + if (parent) + parent->addChild(child); + (*ms_staticViews)[ms_nextStaticView] = child; + staticViewNum = ms_nextStaticView++; + //every new static view is not by default + (*ms_squelchedStatic)[staticViewNum] = true; + + //return the new channelID + return staticViewNum; +} + +// ---------------------------------------------------------------------- + +/** This function is called via a globally-accessible macro, so we can't assume that + * the system is installed. + */ +bool RemoteDebug::registerUpFunction(const char* staticViewName, UpFunction func) +{ + if (!ms_installed) + return false; + + int32 chan = findStaticView(staticViewName); + if (chan == -1) + return false; + + (*ms_upFunctionMap)[chan] = func; + return true; +} + +// ---------------------------------------------------------------------- + +/** This function is called via a globally-accessible macro, so we can't assume that + * the system is installed. + */ +bool RemoteDebug::registerDownFunction(const char* staticViewName, UpFunction func) +{ + if (!ms_installed) + return false; + + int32 chan = findStaticView(staticViewName); + if (chan == -1) + return false; + + (*ms_downFunctionMap)[chan] = func; + return true; +} + +// ---------------------------------------------------------------------- + +/** This function is called via a globally-accessible macro, so we can't assume that + * the system is installed. + */ +bool RemoteDebug::registerLeftFunction(const char* staticViewName, UpFunction func) +{ + if (!ms_installed) + return false; + + int32 chan = findStaticView(staticViewName); + if (chan == -1) + return false; + + (*ms_leftFunctionMap)[chan] = func; + return true; +} + +// ---------------------------------------------------------------------- + +/** This function is called via a globally-accessible macro, so we can't assume that + * the system is installed. + */ +bool RemoteDebug::registerRightFunction(const char* staticViewName, UpFunction func) +{ + if (!ms_installed) + return false; + + int32 chan = findStaticView(staticViewName); + if (chan == -1) + return false; + + (*ms_rightFunctionMap)[chan] = func; + return true; +} + +// ---------------------------------------------------------------------- + +/** This function is called via a globally-accessible macro, so we can't assume that + * the system is installed. + */ +bool RemoteDebug::registerEnterFunction(const char* staticViewName, UpFunction func) +{ + if (!ms_installed) + return false; + + int32 chan = findStaticView(staticViewName); + if (chan == -1) + return false; + + (*ms_enterFunctionMap)[static_cast(chan)] = func; + return true; +} + +// ---------------------------------------------------------------------- + +/** This function is called via a globally-accessible macro, so we can't assume that + * the system is installed. + */ +uint32 RemoteDebug::registerVariable(const char* variableName, void *memLoc, VARIABLE_TYPES type, bool sendToClients) +{ + if (!ms_installed) + return 0; + + std::string variable = variableName; + + uint32 variableNum = 0; + Channel *parent = NULL; + std::string base; + std::string rest = variable; + //look for subchannels with a double backslash + uint32 baseIndex = 0; + uint32 restIndex = variable.find('\\'); + while(restIndex != std::string::npos) + { + //pass the '\' + ++restIndex; + //break channel into the base and the rest + base = variable.substr(baseIndex, restIndex-baseIndex-1); + rest = variable.substr(restIndex, variable.size()); + //build the channel + std::string newBase = variable.substr(0, restIndex-1); + int32 variableNumber = findVariable(newBase); + //only add the variable if we don't have it yet + if (variableNumber == -1) + { + registerVariable(newBase.c_str(), NULL, BOOL, sendToClients); + } + //look for any other subChannel + uint32 oldRestIndex = restIndex; + baseIndex = restIndex; + restIndex = rest.find('\\'); + if (restIndex != std::string::npos) + restIndex += oldRestIndex; + } + //don't register duplicates + if(findVariable(variable) != -1) + return 0; + + //no more sub channels, add this channel + Channel *child = new Channel(variable, parent); + if (parent) + parent->addChild(child); + (*ms_variables)[ms_nextVariable] = child; + variableNum = ms_nextVariable++; + + Variable *v = new Variable(variable, memLoc, type); + (*ms_variableValues)[variableNum] = v; + + if(sendToClients) + { + send(NEW_VARIABLE, variable.c_str()); + send(VARIABLE_TYPE, variable.c_str()); + send(VARIABLE_VALUE, variable.c_str()); + } + + setVariableValue(variable.c_str(), memLoc, sendToClients); + + //return the new channelID + return variableNum; +} + +// ---------------------------------------------------------------------- + +void RemoteDebug::updateVariable(const char* variableName) +{ + send(VARIABLE_VALUE, variableName); +} + +// ---------------------------------------------------------------------- + +/** This function is called via a globally-accessible macro, so we can't assume that + * the system is installed. + */ +void RemoteDebug::setVariableValue(const char* variableName, void *newValue, bool sendToClients) +{ + if(!ms_installed) + return; + + int32 variableNum = findVariable(variableName); + Variable *v = (*ms_variableValues)[static_cast(variableNum)]; + + if(!v) + { + DEBUG_FATAL(true, ("message on undefined variable")); + } + v->setValue(newValue); + + if(sendToClients) + send(VARIABLE_VALUE, variableName); +} + +// ---------------------------------------------------------------------- + +/** This function builds a remoteDebug packet and sends it to the registered sendFunction. + * For some of the messages, it assumes that the varArgs static buffer has been filled + * in with the appropriate data (done with translateVarArgs, and taken care of in the + * macros defined in the header file). + */ +void RemoteDebug::send(MESSAGE_TYPE type, const char* theName) +{ + if (!ms_installed) + { + return; + } + if (!ms_opened) + { + return; + } + + if(ExitChain::isFataling()) + return; + + const std::string name = theName; + + //build a payload based on messagetype + char messageType = 0; + uint32 channelNumber = 0; + //use signed value to allow a negative value for failure in the search routines + int32 chan = -1; +// StreamMap::iterator it = ms_streams->begin(); + //pack in the message type + messageType = static_cast(type); + char scratchString[10]; + Variable *v; + //this variable is used when we want to send 4 bytes of 0's etc, without doing a strlen on the buffer + int32 explicitMessageLength = 0; + Variable::VARIABLEVALUE val; + switch(type) + { + case STREAM: + chan = findStream(name); + if (chan == -1) + { + //add the channel, sending that packet first + channelNumber = registerStream(name); + send(NEW_STREAM, name.c_str()); + } + else + { + channelNumber = static_cast(chan); + } + //bail out if this channel is squelched + if ((*ms_squelchedStream)[static_cast(chan)] == true) + return; + break; + + case NEW_STREAM: + //get the registered int for the new channel + chan = findStream(name); + if (chan != -1) + channelNumber = static_cast(chan); + else + { + channelNumber = registerStream(name); + } + strcpy(ms_varArgs_buffer, name.c_str()); + break; + + case STREAM_SQUELCH: + case STREAM_UNSQUELCH: + chan = findStream(name); + if (chan != -1) + channelNumber = static_cast(chan); + else + { + DEBUG_FATAL(true, ("message on unregistered channel")); + } + break; + + //end stream channel cases + ///// + //begin variable cases + + case NEW_VARIABLE: + //get the registered int for the new variable + chan = findVariable(name); + if (chan != -1) + channelNumber = static_cast(chan); + else + { + DEBUG_FATAL(true, ("message on unregistered channel")); + channelNumber = 0; //lint !e527 unreachable code + } + strcpy(ms_varArgs_buffer, name.c_str()); + break; + + + case VARIABLE_TYPE: + chan = findVariable(name); + if (chan != -1) + channelNumber = static_cast(chan); + v = (*ms_variableValues)[channelNumber]; + _itoa(v->type(), scratchString, 10); + strcpy(ms_varArgs_buffer, scratchString); + break; + + case VARIABLE_VALUE: + chan = findVariable(name); + if (chan != -1) + channelNumber = static_cast(chan); + else + { + DEBUG_FATAL(true, ("message on unregistered channel")); + channelNumber = 0; //lint !e527 unreachable code + } + v = (*ms_variableValues)[channelNumber]; + if(!v) + { + DEBUG_FATAL(true, ("message on undefined variable")); + return; //lint !e527 unreachable code + } + val = v->value(); + switch(v->type()) + { + case INT: + memcpy(ms_varArgs_buffer, &val.intValue, sizeof(int32)); + explicitMessageLength = sizeof(int32); + break; + + case FLOAT: + memcpy(ms_varArgs_buffer, &val.floatValue, sizeof(float)); + explicitMessageLength = sizeof(float); + break; + + case CSTRING: + strcpy(ms_varArgs_buffer, val.stringValue); + explicitMessageLength = strlen(val.stringValue)+1; + break; + + case BOOL: + memcpy(ms_varArgs_buffer, &val.boolValue, sizeof(int32)); + explicitMessageLength = sizeof(int32); + break; + } + break; + + //end variable cases + ///// + //begin static view cases + + case NEW_STATIC: + //get the registered int for the new channel + chan = findStaticView(name); + if (chan != -1) + channelNumber = static_cast(chan); + else + { + DEBUG_FATAL(true, ("message on unregistered channel")); + channelNumber = 0; //lint !e527 unreachable code + } + strcpy(ms_varArgs_buffer, name.c_str()); + break; + + case STATIC_LINE: + chan = findStaticView(name); + if (chan == -1) + { + //add the channel, sending that packet first + channelNumber = registerStaticView(name); + send(NEW_STATIC, name.c_str()); + send(STATIC_SQUELCH, name.c_str()); + } + else + { + channelNumber = static_cast(chan); + } + //bail out if this channel is squelched + if ((*ms_squelchedStatic)[static_cast(chan)] == true) + return; + break; + + case STATIC_SQUELCH: + case STATIC_UNSQUELCH: + chan = findStaticView(name); + if (chan != -1) + channelNumber = static_cast(chan); + else + { + channelNumber = registerStaticView(name); + send(NEW_STATIC, name.c_str()); + } + break; + + case STATIC_INPUT_TARGET: + chan = findStaticView(name); + if (chan == -1) + return; + else + channelNumber = static_cast(chan); + break; + + case STATIC_BEGIN_FRAME: + case STATIC_END_FRAME: + chan = findStaticView(name); + if (chan != -1) + channelNumber = static_cast(chan); + else + { + channelNumber = registerStaticView(name); + send(NEW_STATIC, name.c_str()); + send(STATIC_SQUELCH, name.c_str()); + } + //bail our if this channel is squelched + if ((*ms_squelchedStatic)[static_cast(chan)] == true) + return; + break; + + case RemoteDebug::STATIC_UP: + case RemoteDebug::STATIC_DOWN: + case RemoteDebug::STATIC_LEFT: + case RemoteDebug::STATIC_RIGHT: + case RemoteDebug::STATIC_ENTER: + case RemoteDebug::REQUEST_ALL_CHANNELS: + default: + break; + } + //get sizes for building the packet + int messageTypeLength = sizeof(messageType); + int channelNumberLength = sizeof(channelNumber); + uint32 messageLength = 0; + int messageLengthLength = 0; + + if (explicitMessageLength != 0) + messageLength = explicitMessageLength; + else if (ms_varArgs_buffer) + { + //only grab buffer sizes if needed + messageLength = strlen(ms_varArgs_buffer)+1; + } + else + { + messageLength = strlen(ms_buffer)+1; + } + messageLengthLength = sizeof(messageLength); + //allocate the mem for the packet + + uint32 packetLength = static_cast(messageTypeLength + channelNumberLength + messageLengthLength + static_cast(messageLength)); + + if (ms_varArgs_buffer) + { + //copy data into the packet + memcpy(ms_buffer, &messageType, static_cast(messageTypeLength)); + memcpy(ms_buffer + messageTypeLength, &channelNumber, static_cast(channelNumberLength)); + //only copy the buffer data if needed + memcpy(ms_buffer + messageTypeLength + channelNumberLength, &messageLength, static_cast(messageLengthLength)); + memcpy(ms_buffer + messageTypeLength + channelNumberLength + messageLengthLength, ms_varArgs_buffer, messageLength); + } + + //send and clean up + ms_sendFunction(ms_buffer, packetLength); + //clear out varArgs buffer + ms_varArgs_buffer[0] = '\0'; +} + +// ---------------------------------------------------------------------- + +int32 RemoteDebug::findStream(const std::string& name) +{ + for(StreamMap::iterator it = ms_streams->begin(); it != ms_streams->end(); ++it) + { + if (it->second->name().compare(name) == 0) + { + return static_cast(it->first); + } + } + return -1; +} + +// ---------------------------------------------------------------------- + +int32 RemoteDebug::findStaticView(const std::string& name) +{ + for(StaticViewMap::iterator it = ms_staticViews->begin(); it != ms_staticViews->end(); ++it) + { + if (it->second->name().compare(name) == 0) + { + return static_cast(it->first); + } + } + return -1; +} + +// ---------------------------------------------------------------------- + +int32 RemoteDebug::findVariable(const std::string& name) +{ + for(VariableMap::iterator it = ms_variables->begin(); it != ms_variables->end(); ++it) + { + if (it->second->name().compare(name) == 0) + { + return static_cast(it->first); + } + } + return -1; +} + +// ---------------------------------------------------------------------- + +/** Format and print a debugging message. + * + * This function copies the va_formatted into plain text and stores it into a static buffer + */ +void RemoteDebug::vprintf(const char *format, va_list va) +{ + // format the string + if (_vsnprintf(ms_varArgs_buffer, sizeof(ms_varArgs_buffer), format, va) < 0) + { + // handle overflow reasonably nicely + ms_varArgs_buffer[sizeof(ms_varArgs_buffer)-2] = '+'; + ms_varArgs_buffer[sizeof(ms_varArgs_buffer)-1] = '\0'; + } +} + +// ---------------------------------------------------------------------- + +/** + * Format and print a debugging message. + * + * This function ends up filling a static buffer with the formatted message + */ +void RemoteDebug::translateVarArgs(const char *format, ...) +{ + va_list va; + va_start(va, format); + //copy the data into a static buffer + vprintf(format, va); + va_end(va); +} + +// ====================================================================== + diff --git a/engine/shared/library/sharedDebug/src/shared/RemoteDebug.h b/engine/shared/library/sharedDebug/src/shared/RemoteDebug.h new file mode 100644 index 00000000..8f24d6af --- /dev/null +++ b/engine/shared/library/sharedDebug/src/shared/RemoteDebug.h @@ -0,0 +1,254 @@ +// ====================================================================== +// +// RemoteDebug.h +// copyright 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef REMOTE_DEBUG_H +#define REMOTE_DEBUG_H + +// ====================================================================== + +/** This class is both a platform and network-protocol independent way of + * sending output and input to a remote application. It does not depend + * on any specific network layer, and output may even be sent to a file or + * other output. Since it cannot rely on a network layer, it must build + * its own packets. Below is the packet structure: + * + * 1 byte for message type (current six types, so values 0-5. + * 4 bytes for the channel or variable number (since ints are registered + * and used for the network. + * 4 bytes for the messagelength (if necessary, not all messages have a + * payload to send). + * messageLength bytes for the payload (if necessary, could be a channel + * message or a variable value). + */ +class RemoteDebug +{ +protected: + //forward declare inner classes (defined in RemoteDebug_inner.h) + class Channel; + class Variable; + +public: + ///an enum of all the possible message types + enum MESSAGE_TYPE + { + STREAM, + NEW_STREAM, + STREAM_SQUELCH, + STREAM_UNSQUELCH, + NEW_VARIABLE, + VARIABLE_TYPE, + VARIABLE_VALUE, + NEW_STATIC, + STATIC_SQUELCH, + STATIC_UNSQUELCH, + STATIC_BEGIN_FRAME, + STATIC_END_FRAME, + STATIC_LINE, + STATIC_INPUT_TARGET, + STATIC_UP, + STATIC_DOWN, + STATIC_LEFT, + STATIC_RIGHT, + STATIC_ENTER, + REQUEST_ALL_CHANNELS + }; + + ///an enum of all the possible variable types + enum VARIABLE_TYPES + { + INT, //32 bit signed int + FLOAT, //32 bit signed float + CSTRING, //null terminated char[] + BOOL //0 or 1 (could use an int, but useful for tools) + }; + +protected: + //define typdefs of function types + typedef void (*RemoveFunction)(); + typedef void (*OpenFunction)(const char *server, uint16 port); + typedef void (*CloseFunction)(); + typedef void (*SendFunction)(void *buffer, uint32 bufferLen); + typedef void (*IsReadyFunction)(); + + typedef void (*UpFunction)(); + typedef UpFunction DownFunction; + typedef UpFunction LeftFunction; + typedef UpFunction RightFunction; + typedef UpFunction EnterFunction; + +//end inner classes, enums, and typedefs needed for public interface +// ---------------------------------------------------------------------- +//begin public interface + +public: + virtual ~RemoteDebug () = 0; + +public: + ///install the module, using the client defined functions + static void install(RemoveFunction, OpenFunction, CloseFunction, SendFunction, IsReadyFunction); + + ///open a session + static void open(const char *server = NULL, uint16 port = 0); + + ///packs the data into a packet, and sends it using the client-defined SendFunction + static void send(MESSAGE_TYPE type, const char* name = ""); + + ///emulate printf functionality specific to this class + static void translateVarArgs(const char *format, ...); + + ///register a variable with the system + static uint32 registerVariable(const char* variableName, void *memLoc, VARIABLE_TYPES type, bool sendToClients); + + ///send the variable value to the clients + static void updateVariable(const char* variableName); + + ///set the value of a variable (previously registered, so we know how to cast the void*) + static void setVariableValue(const char* variableName, void *newValue, bool sendToClients); + + ///register a function that the up key should call on the given static view + static bool registerUpFunction(const char* staticViewName, UpFunction); + + ///register a function that the down key should call on the given static view + static bool registerDownFunction(const char* staticViewName, DownFunction); + + ///register a function that the left key should call on the given static view + static bool registerLeftFunction(const char* staticViewName, LeftFunction); + + ///register a function that the right key should call on the given static view + static bool registerRightFunction(const char* staticViewName, RightFunction); + + ///register a function that the enter key should call on the given static view + static bool registerEnterFunction(const char* staticViewName, EnterFunction); + +//end public interface +// ---------------------------------------------------------------------- +//begin protected functions and data members + +protected: + ///can't use a const int because we want this value compiled into static arrays as their size + #define MAX_BUFFER_SIZE 8192 + + static void remove(); + + ///store the stream in a map, use an int for network communication + static uint32 registerStream(const std::string& streamName); + + ///store the static view in a map, use an int for network communication + static uint32 registerStaticView(const std::string& staticViewName); + + ///find and return the stream number for a given stream name, -1 if not found + static int32 findStream(const std::string& name); + + ///find and return the static view number for a given static view name, -1 if not found + static int32 findStaticView(const std::string& name); + + ///find and return the variable number for a given variable name, -1 if not found + static int32 findVariable(const std::string& name); + + ///emulate printf functionality specific to this class + static void vprintf(const char *format, va_list va); + + //bound functions used for the actual transfer of data (i.e. network calls) + static RemoveFunction ms_removeFunction; + static OpenFunction ms_openFunction; + static CloseFunction ms_closeFunction; + static SendFunction ms_sendFunction; + static IsReadyFunction ms_isReadyFunction; + + typedef stdmap::fwd StreamMap; + ///stream names + static StreamMap *ms_streams; + + typedef stdmap::fwd VariableMap; + ///What variables are registered + static VariableMap *ms_variables; + + typedef stdmap::fwd StaticViewMap; + ///StaticView names + static StaticViewMap *ms_staticViews; + + ///a static packet buffer to prevent per-message new and delete + static char ms_buffer[MAX_BUFFER_SIZE]; + + ///a static buffer used to build a string from the translateVarArgs data + static char ms_varArgs_buffer[MAX_BUFFER_SIZE]; + + ///true if system has been installed, false otherwise + static bool ms_installed; + + ///Whether the connection is open or not + static bool ms_opened; + + ///What streaming channels are squelched (i.e. don't send to client at all) + static stdmap::fwd *ms_squelchedStream; + + ///What static channels are squelched (i.e. don't send to client at all) + static stdmap::fwd *ms_squelchedStatic; + + ///the next stream number + static uint32 ms_nextStream; + + ///the next variable number + static uint32 ms_nextVariable; + + ///the next staticView number + static uint32 ms_nextStaticView; + + typedef stdmap::fwd VariableValueMap; + static VariableValueMap *ms_variableValues; + + typedef stdmap::fwd MovementFunctionMap; + static MovementFunctionMap *ms_upFunctionMap; + static MovementFunctionMap *ms_downFunctionMap; + static MovementFunctionMap *ms_leftFunctionMap; + static MovementFunctionMap *ms_rightFunctionMap; + static MovementFunctionMap *ms_enterFunctionMap; +}; + +// ====================================================================== + +#define OUTPUT_CHANNEL(streamName, varArgs) (RemoteDebug::translateVarArgs varArgs, RemoteDebug::send(RemoteDebug::STREAM, streamName)) +#define OUTPUT_REGISTER_VARIABLE(variableName, memLoc, type) RemoteDebug::registerVariable(variableName, memLoc, type, true) +#define OUTPUT_UPDATE_VARIABLE(variableName) RemoteDebug::updateVariable(variableName) +#define OUTPUT_STATIC_VIEW(staticViewName, varArgs) (RemoteDebug::translateVarArgs varArgs, RemoteDebug::send(RemoteDebug::STATIC_LINE, staticViewName)) +#define OUTPUT_STATIC_VIEW_BEGINFRAME(staticViewName) RemoteDebug::send(RemoteDebug::STATIC_BEGIN_FRAME, staticViewName) +#define OUTPUT_STATIC_VIEW_ENDFRAME(staticViewName) RemoteDebug::send(RemoteDebug::STATIC_END_FRAME, staticViewName) +#define OUTPUT_STATIC_VIEW_UP(staticViewName, upFunction) RemoteDebug::registerUpFunction(staticViewName, upFunction) +#define OUTPUT_STATIC_VIEW_DOWN(staticViewName, downFunction) RemoteDebug::registerDownFunction(staticViewName, downFunction) +#define OUTPUT_STATIC_VIEW_LEFT(staticViewName, leftFunction) RemoteDebug::registerLeftFunction(staticViewName, leftFunction) +#define OUTPUT_STATIC_VIEW_RIGHT(staticViewName, rightFunction) RemoteDebug::registerRightFunction(staticViewName, rightFunction) +#define OUTPUT_STATIC_VIEW_ENTER(staticViewName, enterFunction) RemoteDebug::registerEnterFunction(staticViewName, enterFunction) + +#ifdef _DEBUG +#define DEBUG_OUTPUT_CHANNEL(streamName, varArgs) OUTPUT_CHANNEL(streamName, varArgs) +#define DEBUG_OUTPUT_REGISTER_VARIABLE(variableName, memLoc, type) OUTPUT_REGISTER_VARIABLE(variableName, memLoc, type) +#define DEBUG_OUTPUT_UPDATE_VARIABLE(variableName) OUTPUT_UPDATE_VARIABLE(variableName) +#define DEBUG_OUTPUT_STATIC_VIEW(staticViewName, varArgs) OUTPUT_STATIC_VIEW(staticViewName, varArgs) +#define DEBUG_OUTPUT_STATIC_VIEW_BEGINFRAME(staticViewName) OUTPUT_STATIC_VIEW_BEGINFRAME(staticViewName) +#define DEBUG_OUTPUT_STATIC_VIEW_ENDFRAME(staticViewName) OUTPUT_STATIC_VIEW_ENDFRAME(staticViewName) +#define DEBUG_OUTPUT_STATIC_VIEW_UP(staticViewName, upFunction) OUTPUT_STATIC_VIEW_UP(staticViewName, upFunction) +#define DEBUG_OUTPUT_STATIC_VIEW_DOWN(staticViewName, downFunction) OUTPUT_STATIC_VIEW_DOWN(staticViewName, downFunction) +#define DEBUG_OUTPUT_STATIC_VIEW_LEFT(staticViewName, leftFunction) OUTPUT_STATIC_VIEW_LEFT(staticViewName, leftFunction) +#define DEBUG_OUTPUT_STATIC_VIEW_RIGHT(staticViewName, rightFunction) OUTPUT_STATIC_VIEW_RIGHT(staticViewName, rightFunction) +#define DEBUG_OUTPUT_STATIC_VIEW_ENTER(staticViewName, enterFunction) OUTPUT_STATIC_VIEW_ENTER(staticViewName, enterFunction) + +#else //!_DEBUG +#define DEBUG_OUTPUT_CHANNEL(streamName, varArgs) NOP +#define DEBUG_OUTPUT_REGISTER_VARIABLE(variableName, memLoc, type) NOP +#define DEBUG_OUTPUT_VARIABLE(variableName, newValue) NOP +#define DEBUG_OUTPUT_STATIC_VIEW(staticViewName, varArgs) NOP +#define DEBUG_OUTPUT_STATIC_VIEW_BEGINFRAME(staticViewName) NOP +#define DEBUG_OUTPUT_STATIC_VIEW_ENDFRAME(staticViewName) NOP +#define DEBUG_OUTPUT_STATIC_VIEW_UP(staticViewName, upFunction) NOP +#define DEBUG_OUTPUT_STATIC_VIEW_DOWN(staticViewName, downFunction) NOP +#define DEBUG_OUTPUT_STATIC_VIEW_LEFT(staticViewName, leftFunction) NOP +#define DEBUG_OUTPUT_STATIC_VIEW_RIGHT(staticViewName, rightFunction) NOP +#define DEBUG_OUTPUT_STATIC_VIEW_ENTER(staticViewName, enterFunction) NOP +#endif // _DEBUG + + +#endif // REMOTE_DEBUG_H diff --git a/engine/shared/library/sharedDebug/src/shared/RemoteDebug_inner.cpp b/engine/shared/library/sharedDebug/src/shared/RemoteDebug_inner.cpp new file mode 100644 index 00000000..30685720 --- /dev/null +++ b/engine/shared/library/sharedDebug/src/shared/RemoteDebug_inner.cpp @@ -0,0 +1,593 @@ +// ====================================================================== +// +// RemoteDebug_inner.cpp +// copyright 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedDebug/FirstSharedDebug.h" +#include "sharedDebug/RemoteDebug.h" +#include "sharedDebug/RemoteDebug_inner.h" + +#include +#include +#include + +// ---------------------------------------------------------------------- + +RemoteDebugClient::NewStreamFunction RemoteDebugClient::ms_newStreamFunction; +RemoteDebugClient::StreamMessageFunction RemoteDebugClient::ms_streamMessageFunction; +RemoteDebugClient::NewVariableFunction RemoteDebugClient::ms_newVariableFunction; +RemoteDebugClient::VariableValueFunction RemoteDebugClient::ms_variableValueFunction; +RemoteDebugClient::VariableTypeFunction RemoteDebugClient::ms_variableTypeFunction; +RemoteDebugClient::BeginFrameFunction RemoteDebugClient::ms_beginFrameFunction; +RemoteDebugClient::EndFrameFunction RemoteDebugClient::ms_endFrameFunction; +RemoteDebugClient::NewStaticViewFunction RemoteDebugClient::ms_newStaticViewFunction; +RemoteDebugClient::LineFunction RemoteDebugClient::ms_lineFunction; + +uint32 RemoteDebugServer::ms_inputTarget; + +// ====================================================================== + +RemoteDebug::Channel::Channel(const std::string& name, Channel *parent) +: m_name(NULL), + m_parent(parent) +{ + m_name = new std::string(name); + m_children = new NodeList; +} + +// ---------------------------------------------------------------------- + +RemoteDebug::Channel::~Channel() +{ + m_parent = NULL; + if(m_name) + { + delete m_name; + m_name = NULL; + } + if (m_children) + { + delete m_children; + m_children = NULL; + } +} + +// ---------------------------------------------------------------------- + +void RemoteDebug::Channel::addChild(Channel *child) +{ + m_children->push_back(child); +} + +// ---------------------------------------------------------------------- + +const std::string& RemoteDebug::Channel::name() +{ + return *m_name; +} + +// ====================================================================== + +RemoteDebug::Variable::Variable(const std::string& name, void *memLoc, VARIABLE_TYPES type) +: m_memLoc(memLoc), + m_name(NULL), + m_type(type) +{ + m_name = new std::string(name); + + int32 i = 0; + float f = 0.0; + char s = '\0'; + int32 b = 0; + switch(m_type) + { + case INT: + m_value.intValue = i; + break; + + case FLOAT: + m_value.floatValue = f; + break; + + case CSTRING: + m_value.stringValue = &s; + break; + + case BOOL: + m_value.boolValue = b; + break; + } +} + +// ---------------------------------------------------------------------- + +RemoteDebug::Variable::~Variable() +{ + if(m_type == CSTRING) + if (m_value.stringValue != NULL) + delete m_value.stringValue; + + if(m_name) + { + delete m_name; + m_name = NULL; + } +} + +// ---------------------------------------------------------------------- + +void RemoteDebug::Variable::setValue(VARIABLEVALUE v) +{ + switch(m_type) + { + case INT: + m_value.intValue = v.intValue; + break; + + case FLOAT: + m_value.floatValue = v.floatValue; + break; + + case CSTRING: + m_value.stringValue = v.stringValue; + break; + + case BOOL: + m_value.boolValue = v.boolValue; + break; + } +} + +// ---------------------------------------------------------------------- + +void RemoteDebug::Variable::setValue(void *memLoc) +{ + if(memLoc) + { + switch(m_type) + { + case INT: + memcpy(&m_value.intValue, memLoc, sizeof(int32)); + break; + + case FLOAT: + memcpy(&m_value.floatValue, memLoc, sizeof(float)); + break; + + case CSTRING: + if (m_value.stringValue) + delete[] m_value.stringValue; + m_value.stringValue = new char[strlen(static_cast(memLoc))]; + strcpy(m_value.stringValue, const_cast(static_cast(memLoc))); + break; + + case BOOL: + memcpy(&m_value.boolValue, memLoc, sizeof(int32)); + break; + } + } +} + +// ---------------------------------------------------------------------- + +RemoteDebug::Variable::VARIABLEVALUE RemoteDebug::Variable::value() +{ + return m_value; +} +// ---------------------------------------------------------------------- + +const std::string& RemoteDebug::Variable::name() +{ + return *m_name; +} + +// ---------------------------------------------------------------------- + +void RemoteDebug::Variable::setType(VARIABLE_TYPES type) +{ + m_type = type; +} + +// ---------------------------------------------------------------------- + +void RemoteDebug::Variable::pushValue() +{ + if (m_memLoc) + switch (m_type) + { + case BOOL: + case INT: + memcpy(m_memLoc, &m_value, sizeof(int32)); + break; + + case CSTRING: + strcpy(static_cast(m_memLoc), m_value.stringValue); + break; + + case FLOAT: + memcpy(m_memLoc, &m_value, sizeof(float)); + break; + } +} + +// ---------------------------------------------------------------------- + +RemoteDebug::VARIABLE_TYPES RemoteDebug::Variable::type() +{ + return m_type; +} + +// ====================================================================== + +void RemoteDebugClient::install(RemoveFunction rf, OpenFunction of, + CloseFunction cf, SendFunction sf, + IsReadyFunction irf, NewStreamFunction ncf, + StreamMessageFunction cmf, NewVariableFunction nvf, + VariableValueFunction vvf, VariableTypeFunction vtf, + BeginFrameFunction bff, EndFrameFunction eff, + NewStaticViewFunction nsvf, LineFunction lf) +{ + RemoteDebug::install(rf, of, cf, sf, irf); + ms_newStreamFunction = ncf; + ms_streamMessageFunction = cmf; + ms_newVariableFunction = nvf; + ms_variableValueFunction = vvf; + ms_variableTypeFunction = vtf; + ms_beginFrameFunction = bff; + ms_endFrameFunction = eff; + ms_newStaticViewFunction = nsvf; + ms_lineFunction = lf; +} + +// ---------------------------------------------------------------------- + +void RemoteDebugClient::remove() +{ + if (ms_opened) + close(); + RemoteDebug::remove(); +} + +// ---------------------------------------------------------------------- + +void RemoteDebugClient::receive(const unsigned char * const message, const uint32 bufferLen) +{ + UNREF(bufferLen); + //convert to a const char[] for conveinence + const char * const charMessage = reinterpret_cast(message); + //these values are known based on the packet protocol (see RemoteDebug.h for more info + const int sizeOfMessageType = 1; + const int sizeOfChannelNumber = 4; + const int sizeOfSizeofPayload = 4; + //this value is filled in from the packet + uint32 channelNumber = 0; + //this value if filled in from the packet + uint32 sizeOfPayload = 0; + + //get the message type + RemoteDebug::MESSAGE_TYPE type = static_cast(charMessage[0]); + + //grab the channelNumber + memcpy(&channelNumber, charMessage + sizeOfMessageType, sizeOfChannelNumber); + + //build the payload if needed + if (type == RemoteDebug::STREAM || + type == RemoteDebug::STATIC_LINE || + type == RemoteDebug::NEW_STATIC || + type == RemoteDebug::NEW_STREAM || + type == RemoteDebug::VARIABLE_TYPE || + type == RemoteDebug::VARIABLE_VALUE || + type == RemoteDebug::NEW_VARIABLE) + { + memcpy(&sizeOfPayload, charMessage + sizeOfMessageType + sizeOfChannelNumber, sizeOfSizeofPayload); + memcpy(ms_buffer, charMessage + sizeOfMessageType + sizeOfChannelNumber + sizeOfSizeofPayload, sizeOfPayload); + } + + //can't have local variables defined within switch statements, so place them out here. + int32 t; + VARIABLE_TYPES varType; + std::string variableName; + + switch(type) + { + case RemoteDebug::STREAM: + //see if we're redefining an already existing stream + if(ms_streamMessageFunction) + ms_streamMessageFunction(channelNumber, const_cast(ms_buffer)); + break; + + case RemoteDebug::NEW_STREAM: + if (channelNumber < ms_nextStream) + return; + registerStream(ms_buffer); + if (ms_newStreamFunction) + ms_newStreamFunction(channelNumber, const_cast(ms_buffer)); + break; + + case RemoteDebug::NEW_VARIABLE: + if (channelNumber < ms_nextVariable) + return; + //do not send value back to server (hence the "false") + registerVariable(ms_buffer, NULL, BOOL, false); + if (ms_newVariableFunction) + ms_newVariableFunction(channelNumber, const_cast(ms_buffer)); + break; + + case RemoteDebug::VARIABLE_TYPE: + t = atoi(ms_buffer); + varType = static_cast(t); + (*ms_variableValues)[channelNumber]->setType(varType); + if(ms_variableTypeFunction) + ms_variableTypeFunction(channelNumber, varType); + break; + + case RemoteDebug::VARIABLE_VALUE: + (*ms_variableValues)[channelNumber]->setValue(ms_buffer); + if(ms_variableValueFunction) + ms_variableValueFunction(channelNumber, ms_buffer); + break; + + case RemoteDebug::NEW_STATIC: + if (channelNumber < ms_nextStaticView) + return; + registerStaticView(ms_buffer); + if (ms_newStaticViewFunction) + ms_newStaticViewFunction(channelNumber, const_cast(ms_buffer)); + break; + + case RemoteDebug::STATIC_LINE: + if (ms_lineFunction) + ms_lineFunction(channelNumber, const_cast(ms_buffer)); + break; + + case RemoteDebug::STATIC_BEGIN_FRAME: + if (ms_beginFrameFunction) + ms_beginFrameFunction(channelNumber); + break; + + case RemoteDebug::STATIC_END_FRAME: + if (ms_endFrameFunction) + ms_endFrameFunction(channelNumber); + break; + + case RemoteDebug::STREAM_SQUELCH: + case RemoteDebug::STREAM_UNSQUELCH: + case RemoteDebug::STATIC_SQUELCH: + case RemoteDebug::STATIC_UNSQUELCH: + case RemoteDebug::STATIC_INPUT_TARGET: + case RemoteDebug::STATIC_UP: + case RemoteDebug::STATIC_DOWN: + case RemoteDebug::STATIC_LEFT: + case RemoteDebug::STATIC_RIGHT: + case RemoteDebug::STATIC_ENTER: + case RemoteDebug::REQUEST_ALL_CHANNELS: + default: + break; + } +} + +// ---------------------------------------------------------------------- + +/** Called when the clinet closes a session. Since we don't want to maintain + * data between sessions, clear out all data. + */ +void RemoteDebugClient::close() +{ + DEBUG_FATAL(!ms_installed, ("remoteDebug not installed")); + + //don't need to do anything if we're already closed + if(!ms_opened) + return; + + if (ms_closeFunction) + ms_closeFunction(); + ms_opened = false; + + //empty out session-level data + for (StreamMap::iterator it = ms_streams->begin(); it != ms_streams->end(); ++it) + delete it->second; + ms_streams->clear(); + + for (StaticViewMap::iterator itr = ms_staticViews->begin(); itr != ms_staticViews->end(); ++itr) + delete itr->second; + ms_staticViews->clear(); + + for (VariableMap::iterator iter = ms_variables->begin(); iter != ms_variables->end(); ++iter) + delete iter->second; + ms_variables->clear(); + + for (VariableValueMap::iterator iterr = ms_variableValues->begin(); iterr != ms_variableValues->end(); ++iterr) + delete iterr->second; + ms_variableValues->clear(); + + ms_squelchedStream->clear(); + ms_squelchedStatic->clear(); + + ms_nextStream = 0; + ms_nextStaticView = 0; + ms_nextVariable = 0; +} + +// ---------------------------------------------------------------------- + +void RemoteDebugClient::isReady() +{ + ms_opened = true; + if (ms_isReadyFunction) + ms_isReadyFunction(); + send(REQUEST_ALL_CHANNELS, ""); +} + +// ====================================================================== + +void RemoteDebugServer::install(RemoveFunction rmf, OpenFunction of , + CloseFunction cf , SendFunction sf, + IsReadyFunction irf) +{ + RemoteDebug::install(rmf, of, cf, sf, irf); + ms_inputTarget = 0; +} + +// ---------------------------------------------------------------------- + +void RemoteDebugServer::remove() +{ + if (ms_opened) + close(); + RemoteDebug::remove(); +} + +// ---------------------------------------------------------------------- + +void RemoteDebugServer::isReady() +{ + ms_opened = true; + if (ms_isReadyFunction) + ms_isReadyFunction(); +} +// ---------------------------------------------------------------------- + +void RemoteDebugServer::receive(const unsigned char * const message, const uint32 bufferLen) +{ + UNREF(bufferLen); + //convert to a const char[] for conveinence + const char * const charMessage = reinterpret_cast(message); + //these values are known based on the packet protocol (see RemoteDebug.h for more info + const int sizeOfMessageType = 1; + const int sizeOfChannelNumber = 4; + const int sizeOfSizeofPayload = 4; + //this value is filled in from the packet + uint32 channelNumber = 0; + //this value if filled in from the packet + uint32 sizeOfPayload = 0; + + //get the message type + RemoteDebug::MESSAGE_TYPE type = static_cast(charMessage[0]); + + //grab the channelNumber + memcpy(&channelNumber, charMessage + sizeOfMessageType, sizeOfChannelNumber); + + //build the payload if needed + if (type == RemoteDebug::STREAM || + type == RemoteDebug::NEW_STREAM || + type == RemoteDebug::VARIABLE_TYPE || + type == RemoteDebug::VARIABLE_VALUE || + type == RemoteDebug::NEW_VARIABLE) + { + memcpy(&sizeOfPayload, charMessage + sizeOfMessageType + sizeOfChannelNumber, sizeOfSizeofPayload); + memcpy(ms_buffer, charMessage + sizeOfMessageType + sizeOfChannelNumber + sizeOfSizeofPayload, sizeOfPayload); + } + + Variable* v = NULL; + switch(type) + { + case STREAM: + case NEW_STREAM: + case VARIABLE_TYPE: + case NEW_VARIABLE: + break; + + case STREAM_SQUELCH: + (*ms_squelchedStream)[channelNumber] = true; + break; + + case STREAM_UNSQUELCH: + (*ms_squelchedStream)[channelNumber] = false; + break; + + case VARIABLE_VALUE: + //set the new value into the variable on the server side + v = (*ms_variableValues)[channelNumber]; + if(!v) + { + DEBUG_FATAL(true, ("message on undefined variable")); + return; //lint !e527 unreachable code + } + v->setValue(ms_buffer); + //put that new value back into the game object + v->pushValue(); + break; + + case STATIC_SQUELCH: + (*ms_squelchedStatic)[channelNumber] = true; + break; + + case STATIC_UNSQUELCH: + (*ms_squelchedStatic)[channelNumber] = false; + break; + + case REQUEST_ALL_CHANNELS: + sendAllChannels(); + break; + + case STATIC_INPUT_TARGET: + ms_inputTarget = channelNumber; + break; + + case STATIC_UP: + if ((*ms_upFunctionMap)[ms_inputTarget] != NULL) + (*ms_upFunctionMap)[ms_inputTarget](); //lint !e10, !e522 ("expecting a function" and "expected void assignment) + break; + + case STATIC_DOWN: + if ((*ms_downFunctionMap)[ms_inputTarget] != NULL) + (*ms_downFunctionMap)[ms_inputTarget](); //lint !e10, !e522 ("expecting a function" and "expected void assignment) + break; + + case STATIC_LEFT: + if ((*ms_leftFunctionMap)[ms_inputTarget] != NULL) + (*ms_leftFunctionMap)[ms_inputTarget](); //lint !e10, !e522 ("expecting a function" and "expected void assignment) + break; + + case STATIC_RIGHT: + if ((*ms_rightFunctionMap)[ms_inputTarget] != NULL) + (*ms_rightFunctionMap)[ms_inputTarget](); //lint !e10, !e522 ("expecting a function" and "expected void assignment) + break; + + case STATIC_ENTER: + if ((*ms_enterFunctionMap)[ms_inputTarget] != NULL) + (*ms_enterFunctionMap)[ms_inputTarget](); //lint !e10, !e522 ("expecting a function" and "expected void assignment) + break; + + case NEW_STATIC: + case STATIC_BEGIN_FRAME: + case STATIC_END_FRAME: + case STATIC_LINE: + default: + break; + } +} + +// ---------------------------------------------------------------------- + +/** Called when a session is closed. Since this is the server side, and must + * maintain data between sessions, data is NOT cleared out. + */ +void RemoteDebugServer::close() +{ + DEBUG_FATAL(!ms_installed, ("remoteDebug not installed")); + + if (ms_closeFunction) + ms_closeFunction(); + ms_opened = false; +} + +// ---------------------------------------------------------------------- + +void RemoteDebugServer::sendAllChannels() +{ + for(StreamMap::iterator it = ms_streams->begin(); it != ms_streams->end(); ++it) + send(NEW_STREAM, it->second->name().c_str()); + for(VariableMap::iterator itr = ms_variables->begin(); itr != ms_variables->end(); ++itr) + { + send(NEW_VARIABLE, itr->second->name().c_str()); + send(VARIABLE_TYPE, itr->second->name().c_str()); + send(VARIABLE_VALUE, itr->second->name().c_str()); + } + for(StaticViewMap::iterator iter = ms_staticViews->begin(); iter != ms_staticViews->end(); ++iter) + send(NEW_STATIC, iter->second->name().c_str()); +} + +// ====================================================================== diff --git a/engine/shared/library/sharedDebug/src/shared/RemoteDebug_inner.h b/engine/shared/library/sharedDebug/src/shared/RemoteDebug_inner.h new file mode 100644 index 00000000..d3dd088a --- /dev/null +++ b/engine/shared/library/sharedDebug/src/shared/RemoteDebug_inner.h @@ -0,0 +1,155 @@ +// ====================================================================== +// +// RemoteDebug_inner.h +// copyright 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef REMOTE_DEBUG_INNER_H +#define REMOTE_DEBUG_INNER_H + +#include "sharedDebug/RemoteDebug.h" + +// ====================================================================== + + + /** This inner class is used to maintain relationships between channels. It stores both its parent + * and child nodes, as well as it's fully qualified name (i.e. "Foundation\\MemoryManager\\MemoryMap") + */ +class RemoteDebug::Channel +{ + public: + Channel(const std::string& name, Channel *parent); + ~Channel(); + void addChild(Channel *child); + const std::string& name(); + private: + typedef stdlist::fwd NodeList; + ///its children + NodeList* m_children; + ///the fully qualified name of the channel + std::string* m_name; + ///its parent (which is NULL for top-level nodes + Channel *m_parent; +}; + +/** This class stores data about a variable channel, both on the server (where the variable actually + * resides), and the client (which just displays it). The client simply creates Variable's with + * NULL'd out memLoc's, since it doesn't have direct access to the memory. + */ +class RemoteDebug::Variable +{ + public: + union VARIABLEVALUE + { + int32 intValue; + float floatValue; + int32 boolValue; + char* stringValue; + }; + + Variable(const std::string& name, void *memLoc, VARIABLE_TYPES type); + ~Variable(); + void setValue(VARIABLEVALUE v); + void setValue(void *memLoc); + VARIABLEVALUE value(); + const std::string& name(); + void setType(VARIABLE_TYPES type); + VARIABLE_TYPES type(); + void pushValue(); + private: + ///a data structure used to hold the current variable data + VARIABLEVALUE m_value; + void *m_memLoc; + std::string* m_name; + VARIABLE_TYPES m_type; +}; + +// ====================================================================== + + +/** This derived class represents a client application that uses the RemoteDebug module. + * It receives messages and can do things like send messages to the app to clear the screen. + * It can also send squelch and unsquelch messages. This module would be used by a text-based, + * MFC, or Qt app for instance. + */ +class RemoteDebugClient : public RemoteDebug +{ +protected: + typedef void (*NewStreamFunction)(uint32 streamNumber, const char *streamName); + typedef void (*StreamMessageFunction)(uint32 streamNumber, const char *message); + typedef void (*NewVariableFunction)(uint32 variableNumber, const char *variableName); + typedef void (*VariableTypeFunction)(uint32 variableNumber, VARIABLE_TYPES type); + typedef void (*VariableValueFunction)(uint32 variableNumber, const char *message); + typedef void (*BeginFrameFunction)(uint32 staticViewNumber); + typedef void (*EndFrameFunction)(uint32 staticViewNumber); + typedef void (*NewStaticViewFunction)(uint32 variableNumber, const char *variableName); + typedef void (*LineFunction)(uint32 staticViewNumber, const char *message); + + static NewStreamFunction ms_newStreamFunction; + static StreamMessageFunction ms_streamMessageFunction; + static NewVariableFunction ms_newVariableFunction; + static VariableValueFunction ms_variableValueFunction; + static VariableTypeFunction ms_variableTypeFunction; + static BeginFrameFunction ms_beginFrameFunction; + static EndFrameFunction ms_endFrameFunction; + static NewStaticViewFunction ms_newStaticViewFunction; + static LineFunction ms_lineFunction; + +public: + ///used to initialize the client application + static void install(RemoveFunction rf, OpenFunction of, CloseFunction cf, SendFunction sf, + IsReadyFunction irf, NewStreamFunction ncf, StreamMessageFunction cmf, + NewVariableFunction nvf , VariableValueFunction vvf, VariableTypeFunction vtf, + BeginFrameFunction bff, EndFrameFunction eff, NewStaticViewFunction nsvf, + LineFunction lf); + + ///uninstall the module + static void remove(); + + static void close(); + + ///receives and translates a packet, takes appropriate action + static void receive(const unsigned char * const buffer, const uint32 bufferLen); + + ///called when actually ready to transfer data (commonly called from an open ack) + static void isReady(); +}; + +// ====================================================================== + +/** This derived class represents a server application that uses the RemoteDebug module. + * It sends messages and can do things like receive squelch and unsquelch messages. + * This module would be used by applications like gameserver, loginservers, and the game client. + */ +class RemoteDebugServer : public RemoteDebug +{ +public: + + ///install the module, using the client defined functions + static void install(RemoveFunction rmf, OpenFunction of, + CloseFunction cf, SendFunction sf, + IsReadyFunction irf); + + ///uninstall the module + static void remove(); + + ///receives and translates a packet, takes appropriate action + static void receive(const unsigned char * const message, const uint32 bufferLen); + + static void close(); + + ///called when actually ready to transfer data (commonly called from an open ack) + static void isReady(); + +protected: + ///send all the channels to the client + static void sendAllChannels(); + + ///stores the channel number of the static view that currently receives keyboard input + static uint32 ms_inputTarget; +}; + +// ====================================================================== + +#endif // REMOTE_DEBUG_INNER_H diff --git a/engine/shared/library/sharedDebug/src/shared/Report.cpp b/engine/shared/library/sharedDebug/src/shared/Report.cpp new file mode 100644 index 00000000..7946e830 --- /dev/null +++ b/engine/shared/library/sharedDebug/src/shared/Report.cpp @@ -0,0 +1,243 @@ +// ====================================================================== +// +// Report.cpp +// copyright 1999 Bootprint Entertainment +// copyright 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedDebug/FirstSharedDebug.h" +#include "sharedDebug/Report.h" + +#include "sharedDebug/DebugFlags.h" +#include "sharedDebug/DebugMonitor.h" +#include "sharedFoundation/FloatingPointUnit.h" +#include "sharedFoundation/Os.h" +#include "sharedFoundation/PerThreadData.h" +#include "sharedFoundation/Production.h" + +#include +#include + +// ====================================================================== + +namespace ReportNamespace +{ + Report::Callback ms_logCallback; + Report::Callback ms_warningCallback; + Report::Callback ms_fatalCallback; + bool ms_logAllReports; + int ms_flags; +}; +using namespace ReportNamespace; + +// ====================================================================== + +void Report::install() +{ + DebugFlags::registerFlag(ms_logAllReports, "SharedDebug", "logAllReports"); +} + +// ---------------------------------------------------------------------- + +void Report::bindLogCallback(Callback callback) +{ + ms_logCallback = callback; +} + +// ---------------------------------------------------------------------- + +void Report::bindWarningCallback(Callback callback) +{ + ms_warningCallback = callback; +} + +// ---------------------------------------------------------------------- + +void Report::bindFatalCallback(Callback callback) +{ + ms_fatalCallback = callback; +} + +// ====================================================================== +/** + * Setup the debug print flags for the next debug print from this thread + * + * This routine should never be called directly, but only through the DEBUG_PRINT macros. + * + * @internal + */ + +void Report::setFlags(int flags) +{ + if (ms_logAllReports) + flags |= RF_log; + + if (!PerThreadData::isThreadInstalled()) + { + // if the per-thread-data isn't installed, then we know we're single-threaded and can use the static flags + ms_flags = flags; + } + else + PerThreadData::setDebugPrintFlags(flags); +} + +// ---------------------------------------------------------------------- + +void Report::puts(const char *buffer) +{ + int flags; + if (!PerThreadData::isThreadInstalled()) + { + // if the per-thread-data isn't installed, then we know we're single-threaded and can use the static flags + flags = ms_flags; + } + else + flags = PerThreadData::getDebugPrintFlags(); + + // handle logging callback functions + if (flags & RF_fatal) + { + if (ms_fatalCallback) + (*ms_fatalCallback)(buffer); + } + else + if (flags & RF_warning) + { + if (ms_warningCallback) + (*ms_warningCallback)(buffer); + } + else + if (flags & RF_log) + { + if (ms_logCallback) + (*ms_logCallback)(buffer); + } + + if (flags & RF_print) + { +#ifdef WIN32 + HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); + if (hStdOut) + { + DWORD bytesWritten; + WriteFile(hStdOut, buffer, strlen(buffer), &bytesWritten, 0); + } +#else + fputs(buffer, stdout); +#endif + } + +#if PRODUCTION == 0 + if (flags & RF_print) + { + DebugMonitor::print(buffer); + if (flags & (RF_fatal | RF_dialog)) + DebugMonitor::print("\n"); + } +#endif + + if (flags & RF_log) + { + const WORD fp1 = FloatingPointUnit::getControlWord(); + +#ifdef WIN32 + OutputDebugString(buffer); + if (flags & RF_console) + fputs(buffer, stderr); +#else + fputs(buffer, stderr); +#endif + + // fatal strings and dialog messages do not have newlines on the end of them, but we want them in the logs + if (flags & (RF_fatal | RF_dialog)) + { +#ifdef WIN32 + OutputDebugString("\n"); + if (flags & RF_console) + fputs("\n", stderr); +#else + fputs("\n", stderr); +#endif + } + + const WORD fp2 = FloatingPointUnit::getControlWord(); + + // -qq- HACK this is an attempt to work around OutputDebugString resetting the FPU precision to 53 bits when running under the debugger + if (fp1 != fp2) + FloatingPointUnit::setControlWord(fp1); + } + + // fatal strings should be made very obvious, so pop up a message box + if ((flags & RF_dialog) && Os::isMainThread()) + { + const char *title = "Report"; + if (flags & RF_fatal) + title = "Fatal Report"; + + MessageBox(NULL, buffer, title, MB_OK | MB_ICONEXCLAMATION); + } +} + +// ---------------------------------------------------------------------- +/** + * Format and print a debugging message. + * + * This routine should never be called directly, but only through the REPORT + * macros. + * + * This routine will send the specified string to the DebugMonitor. It will + * also be logged it to the debugger if the RF_log enum was specified. If + * the RF_fatal flag was specified, the routine will display a message box + * with the string in it as well. + * + * @internal + */ + +void Report::vprintf(const char *format, va_list va) +{ + char buffer[8 * 1024]; + + // make sure the buffer is always NULL terminated + buffer[sizeof(buffer)-1] = '\0'; + + // format the string + IGNORE_RETURN(vsnprintf(buffer, sizeof(buffer)-1, format, va)); + + // handle overflow reasonably nicely + if (strlen(buffer) == sizeof(buffer)-1) + { + buffer[sizeof(buffer)-3] = '+'; + buffer[sizeof(buffer)-2] = '\n'; + } + + puts(buffer); +} + +// ---------------------------------------------------------------------- +/** + * Format and print a debugging message. + * + * This routine should never be called directly, but only through the REPORT + * macros. + * + * This routine will send the specified string to the DebugMonitor. It will + * also be logged it to the debugger if the RF_log enum was specified. If + * the RF_fatal flag was specified, the routine will display a message box + * with the string in it as well. + * + * @internal + */ + +void Report::printf(const char *format, ...) +{ + va_list va; + + va_start(va, format); + + vprintf(format, va); + + va_end(va); +} + +// ====================================================================== diff --git a/engine/shared/library/sharedDebug/src/shared/Report.h b/engine/shared/library/sharedDebug/src/shared/Report.h new file mode 100644 index 00000000..aee973f2 --- /dev/null +++ b/engine/shared/library/sharedDebug/src/shared/Report.h @@ -0,0 +1,71 @@ +// ====================================================================== +// +// Report.cpp +// copyright 1999 Bootprint Entertainment +// copyright 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_Report_H +#define INCLUDED_Report_H + +// ====================================================================== + +class Report +{ +public: + + enum + { + RF_print = BINARY2(0000,0001), + RF_log = BINARY2(0000,0010), + RF_warning = BINARY2(0000,0100), + RF_fatal = BINARY2(0000,1000), + RF_console = BINARY2(0001,0000), + RF_dialog = BINARY2(0010,0000) + }; + +public: + + typedef void (*Callback)(char const *string); + +public: + + static void install(); + + static void bindLogCallback(Callback callback); + static void bindWarningCallback(Callback callback); + static void bindFatalCallback(Callback callback); + + static DLLEXPORT void setFlags(int flags); + static void puts(const char *string); + static void vprintf(const char *format, va_list va); + static DLLEXPORT void printf(const char *format, ...); +}; + +// ====================================================================== + +#define REPORT(expr, flags, printf2) ((expr) ? Report::setFlags(flags), Report::printf printf2 : NOP) +#define REPORT_LOG(expr, printf) REPORT(expr, Report::RF_log, printf) +#define REPORT_PRINT(expr, printf) REPORT(expr, Report::RF_print, printf) +#define REPORT_LOG_PRINT(expr, printf) REPORT(expr, Report::RF_log | Report::RF_print, printf) + +#ifdef _DEBUG + + #define DEBUG_REPORT(expr, flags, printf) REPORT(expr, flags, printf) + #define DEBUG_REPORT_LOG(expr, printf) REPORT_LOG(expr, printf) + #define DEBUG_REPORT_PRINT(expr, printf) REPORT_PRINT(expr, printf) + #define DEBUG_REPORT_LOG_PRINT(expr,printf) REPORT_LOG_PRINT(expr, printf) + +#else + + #define DEBUG_REPORT(expr, flags, printf2) NOP + #define DEBUG_REPORT_LOG(expr, printf) NOP + #define DEBUG_REPORT_PRINT(expr, printf) NOP + #define DEBUG_REPORT_LOG_PRINT(expr, printf) NOP + +#endif + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedDebug/src/shared/SetupSharedDebug.cpp b/engine/shared/library/sharedDebug/src/shared/SetupSharedDebug.cpp new file mode 100644 index 00000000..801c43b9 --- /dev/null +++ b/engine/shared/library/sharedDebug/src/shared/SetupSharedDebug.cpp @@ -0,0 +1,43 @@ +// ====================================================================== +// +// SetupSharedDebug.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedDebug/FirstSharedDebug.h" +#include "sharedDebug/SetupSharedDebug.h" + +#include "sharedDebug/CallStackCollector.h" +#include "sharedDebug/DebugMonitor.h" +#include "sharedDebug/PerformanceTimer.h" +#include "sharedDebug/PixCounter.h" +#include "sharedDebug/ProfilerTimer.h" +#include "sharedDebug/Profiler.h" + +#include "sharedFoundation/Production.h" + +#ifdef _WIN32 +#include "sharedDebug/VTune.h" +#endif + +// ====================================================================== + +void SetupSharedDebug::install(const int maxProfilerEntries) +{ + Profiler::install(); + UNREF(maxProfilerEntries); + + ProfilerTimer::install(); + PerformanceTimer::install(); + CallStackCollector::install(); + +#if PRODUCTION == 0 + PixCounter::install(); +#ifdef _WIN32 + VTune::install(); +#endif +#endif +} + +// ====================================================================== diff --git a/engine/shared/library/sharedDebug/src/shared/SetupSharedDebug.h b/engine/shared/library/sharedDebug/src/shared/SetupSharedDebug.h new file mode 100644 index 00000000..dd132b98 --- /dev/null +++ b/engine/shared/library/sharedDebug/src/shared/SetupSharedDebug.h @@ -0,0 +1,27 @@ +// ====================================================================== +// +// SetupSharedDebug.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_SetupSharedDebug_H +#define INCLUDED_SetupSharedDebug_H + +// ====================================================================== + +class SetupSharedDebug +{ +public: + + static void install(int maxProfilerEntries); + +private: + SetupSharedDebug(); + SetupSharedDebug(const SetupSharedDebug &); + SetupSharedDebug &operator =(const SetupSharedDebug &); +}; + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedDebug/src/win32/DebugHelp.cpp b/engine/shared/library/sharedDebug/src/win32/DebugHelp.cpp new file mode 100644 index 00000000..4cef16a7 --- /dev/null +++ b/engine/shared/library/sharedDebug/src/win32/DebugHelp.cpp @@ -0,0 +1,670 @@ +// ====================================================================== +// +// DebugHelp.cpp +// copyright 2000 Verant Interactive +// +// ====================================================================== + +#include "sharedDebug/FirstSharedDebug.h" +#include "sharedDebug/DebugHelp.h" + +#include "sharedFoundation/WindowsWrapper.h" + +#include +#include +#include + +// ====================================================================== + + +// ====================================================================== +// This was done to keep the header file from having to include or + +namespace DebugHelpNamespace +{ + static HINSTANCE library; + static HANDLE process; + + struct CallbackData + { + const char *name; + bool loaded; + }; + + typedef DWORD (__stdcall *SymSetOptionsFP)(IN DWORD SymOptions); + typedef BOOL (__stdcall *SymInitializeFP)(IN HANDLE hProcess, IN PSTR UserSearchPath, IN BOOL fInvadeProcess); + typedef BOOL (__stdcall *SymCleanupFP)(IN HANDLE hProcess); + + typedef BOOL (__stdcall *StackWalk64FP)(DWORD MachineType, HANDLE hProcess, HANDLE hThread, LPSTACKFRAME64 StackFrame, PVOID ContextRecord, PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine, PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine, PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine, PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress); + typedef BOOL (__stdcall *SymGetModuleInfo64FP)(IN HANDLE hProcess, IN DWORD64 dwAddr, OUT PIMAGEHLP_MODULE64 ModuleInfo); + typedef DWORD64 (__stdcall *SymLoadModule64FP)(IN HANDLE hProcess, IN HANDLE hFile, IN PSTR ImageName, IN PSTR ModuleName, IN DWORD64 BaseOfDll, IN DWORD SizeOfDll); + typedef BOOL (__stdcall *SymGetSymFromAddr64FP)(IN HANDLE hProcess, IN DWORD64 dwAddr, OUT PDWORD64 pdwDisplacement, OUT PIMAGEHLP_SYMBOL64 Symbol); + typedef BOOL (__stdcall *SymGetLineFromAddr64FP)(IN HANDLE hProcess, IN DWORD64 dwAddr, OUT PDWORD pdwDisplacement, OUT PIMAGEHLP_LINE64 Line); + typedef PVOID (__stdcall *SymFunctionTableAccess64FP)(HANDLE hProcess, DWORD64 AddrBase); + typedef DWORD64 (__stdcall *SymGetModuleBase64FP)(IN HANDLE hProcess, IN DWORD64 dwAddr); + typedef BOOL (__stdcall *SymEnumerateModules64FP)(HANDLE hProcess, PSYM_ENUMMODULES_CALLBACK64 EnumModulesCallback, PVOID UserContext); + typedef BOOL (__stdcall *EnumerateLoadedModules64FP)(IN HANDLE hProcess, IN PENUMLOADED_MODULES_CALLBACK64 EnumLoadedModulesCallback, IN PVOID UserContext); + typedef BOOL (__stdcall *MiniDumpWriteDumpFP)(HANDLE hProcess, DWORD ProcessId, HANDLE hFile, MINIDUMP_TYPE DumpType, PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, PMINIDUMP_CALLBACK_INFORMATION CallbackParam); + + static SymSetOptionsFP symSetOptions; + static SymInitializeFP symInitialize; + static SymCleanupFP symCleanup; + static StackWalk64FP stackWalk64; + static SymGetModuleInfo64FP symGetModuleInfo64; + static SymLoadModule64FP symLoadModule64; + static SymGetSymFromAddr64FP symGetSymFromAddr64; + static SymGetLineFromAddr64FP symGetLineFromAddr64; + static SymFunctionTableAccess64FP symFunctionTableAccess64; + static SymGetModuleBase64FP symGetModuleBase64; + static SymEnumerateModules64FP symEnumerateModules64; + static EnumerateLoadedModules64FP enumerateLoadedModules64; + static MiniDumpWriteDumpFP miniDumpWriteDump; + static CRITICAL_SECTION criticalSection; + + // ---------------------------------------------------------------------- + + static BOOL CALLBACK loadSymbolsForDllCallback(PTSTR ModuleName, DWORD64 ModuleBase, ULONG ModuleSize, PVOID UserContext); + + // ---------------------------------------------------------------------- + + + typedef unsigned long int ub4; /* unsigned 4-byte quantities */ + typedef unsigned char ub1; /* unsigned 1-byte quantities */ + + #define hashsize(n) ((ub4)1<<(n)) + #define hashmask(n) (hashsize(n)-1) + + /* + -------------------------------------------------------------------- + mix -- mix 3 32-bit values reversibly. + For every delta with one or two bits set, and the deltas of all three + high bits or all three low bits, whether the original value of a,b,c + is almost all zero or is uniformly distributed, + * If mix() is run forward or backward, at least 32 bits in a,b,c + have at least 1/4 probability of changing. + * If mix() is run forward, every bit of c will change between 1/3 and + 2/3 of the time. (Well, 22/100 and 78/100 for some 2-bit deltas.) + mix() was built out of 36 single-cycle latency instructions in a + structure that could supported 2x parallelism, like so: + a -= b; + a -= c; x = (c>>13); + b -= c; a ^= x; + b -= a; x = (a<<8); + c -= a; b ^= x; + c -= b; x = (b>>13); + ... + Unfortunately, superscalar Pentiums and Sparcs can't take advantage + of that parallelism. They've also turned some of those single-cycle + latency instructions into multi-cycle latency instructions. Still, + this is the fastest good hash I could find. There were about 2^^68 + to choose from. I only looked at a billion or so. + -------------------------------------------------------------------- + */ + #define mix(a,b,c) \ + { \ + a -= b; a -= c; a ^= (c>>13); \ + b -= c; b -= a; b ^= (a<<8); \ + c -= a; c -= b; c ^= (b>>13); \ + a -= b; a -= c; a ^= (c>>12); \ + b -= c; b -= a; b ^= (a<<16); \ + c -= a; c -= b; c ^= (b>>5); \ + a -= b; a -= c; a ^= (c>>3); \ + b -= c; b -= a; b ^= (a<<10); \ + c -= a; c -= b; c ^= (b>>15); \ + } + + /* + -------------------------------------------------------------------- + hash() -- hash a variable-length key into a 32-bit value + k : the key (the unaligned variable-length array of bytes) + len : the length of the key, counting by bytes + initval : can be any 4-byte value + Returns a 32-bit value. Every bit of the key affects every bit of + the return value. Every 1-bit and 2-bit delta achieves avalanche. + About 6*len+35 instructions. + + The best hash table sizes are powers of 2. There is no need to do + mod a prime (mod is sooo slow!). If you need less than 32 bits, + use a bitmask. For example, if you need only 10 bits, do + h = (h & hashmask(10)); + In which case, the hash table should have hashsize(10) elements. + + If you are hashing n strings (ub1 **)k, do it like this: + for (i=0, h=0; i= 12) + { + a += (k[0] +((ub4)k[1]<<8) +((ub4)k[2]<<16) +((ub4)k[3]<<24)); + b += (k[4] +((ub4)k[5]<<8) +((ub4)k[6]<<16) +((ub4)k[7]<<24)); + c += (k[8] +((ub4)k[9]<<8) +((ub4)k[10]<<16)+((ub4)k[11]<<24)); + mix(a,b,c); + k += 12; len -= 12; + } + + /*------------------------------------- handle the last 11 bytes */ + c += length; + switch(len) /* all the case statements fall through */ + { + case 11: c+=((ub4)k[10]<<24); + case 10: c+=((ub4)k[9]<<16); + case 9 : c+=((ub4)k[8]<<8); + /* the first byte of c is reserved for the length */ + case 8 : b+=((ub4)k[7]<<24); + case 7 : b+=((ub4)k[6]<<16); + case 6 : b+=((ub4)k[5]<<8); + case 5 : b+=k[4]; + case 4 : a+=((ub4)k[3]<<24); + case 3 : a+=((ub4)k[2]<<16); + case 2 : a+=((ub4)k[1]<<8); + case 1 : a+=k[0]; + /* case 0: nothing left to add */ + } + mix(a,b,c); + /*-------------------------------------------- report the result */ + return c; + } +#endif + + // version optimized for 4 byte input. + static ub4 hash_DWORD(const DWORD in, const ub4 initval) + { + ub1 *const k = (ub1 *)∈ + ub4 a, b, c, len; + + /* Set up the internal state */ + len = 4; + a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */ + c = initval; /* the previous hash value */ + + /*------------------------------------- handle the last 11 bytes */ + c += 4; + a+=((ub4)k[3]<<24); + a+=((ub4)k[2]<<16); + a+=((ub4)k[1]<<8); + a+=k[0]; + + mix(a,b,c); + + /*-------------------------------------------- report the result */ + return c; + } + + + // ---------------------------------------------------------------------- + + struct BaseAddressLookup + { + DWORD64 keyAddress; // key + DWORD64 baseAddress; // value + }; + + static BaseAddressLookup * s_baseAddressCache; + static inline unsigned _baseAddressCachePageBits() { return 8; } + static inline unsigned _baseAddressCacheBits() { return _baseAddressCachePageBits() + 12; } + static inline unsigned _baseAddressCacheSize() { return 1 << (_baseAddressCacheBits()); } + static inline unsigned _baseAddressCacheElements() { return _baseAddressCacheSize() / sizeof(*s_baseAddressCache); } + static inline unsigned _baseAddressCacheMask() { return _baseAddressCacheElements() - 1; } + + static int s_baseAddressCacheMisses; + static int s_baseAddressCacheHits; + + static int s_baseAddressElements; + static int s_baseAddressUsed; + + /* + static void _baseAddressCacheAnalyze() + { + s_baseAddressElements=0; + s_baseAddressUsed=0; + + unsigned i; + const unsigned count = _baseAddressCacheElements(); + for (i=0;ibaseAddress) + { + s_baseAddressUsed++; + } + } + } + */ + + static DWORD64 _baseAddressLookup(DWORD64 addr) + { + BaseAddressLookup *lookup; + + unsigned long bits = _baseAddressCacheBits(); + + DWORD *addr32 = (DWORD *)&addr; + unsigned long hash32 = hash_DWORD(addr32[0], addr32[1]); + unsigned long hash = (hash32>>(32-bits)) ^ hash32; + + unsigned long mask = _baseAddressCacheMask(); + + unsigned long index = hash & mask; + lookup = s_baseAddressCache + index; + if (lookup->keyAddress==addr) + { + s_baseAddressCacheHits++; + //DEBUG_FATAL(symGetModuleBase64(process, addr) != lookup->baseAddress, ("Cache failure.\n")); + return lookup->baseAddress; + } + else + { + s_baseAddressCacheMisses++; + + DWORD64 baseAddress = symGetModuleBase64(process, addr); + + lookup->keyAddress=addr; + lookup->baseAddress=baseAddress; + + return baseAddress; + } + } + + static DWORD64 __stdcall getModuleBase(HANDLE hProcess, DWORD64 dwAddr) + { + UNREF(hProcess); + DEBUG_FATAL(hProcess!=process, ("Wrong process handle for module base lookup.\n")); + return _baseAddressLookup(dwAddr); + } + + // ---------------------------------------------------------------------- + struct FunctionTableLookup + { + DWORD64 keyAddress; // key + PVOID functionTable; // value + }; + + static FunctionTableLookup * s_functionTableCache; + static inline unsigned _functionTableCachePageBits() { return 4; } + static inline unsigned _functionTableCacheBits() { return _functionTableCachePageBits() + 12; } + static inline unsigned _functionTableCacheSize() { return 1 << (_functionTableCacheBits()); } + static inline unsigned _functionTableCacheElements() { return _functionTableCacheSize() / sizeof(*s_functionTableCache); } + static inline unsigned _functionTableCacheMask() { return _functionTableCacheElements() - 1; } + + static int s_functionTableCacheMisses; + static int s_functionTableCacheHits; + + + static PVOID _functionTableLookup(DWORD64 addr) + { + FunctionTableLookup *lookup; + + unsigned long bits = _functionTableCacheBits(); + + DWORD *addr32 = (DWORD *)&addr; + unsigned long hash32 = hash_DWORD(addr32[0], addr32[1]); + unsigned long hash = (hash32>>(32-bits)) ^ hash32; + + unsigned long mask = _functionTableCacheMask(); + + unsigned long index = hash & mask; + lookup = s_functionTableCache + index; + if (lookup->keyAddress==addr) + { + s_functionTableCacheHits++; + //DEBUG_FATAL(symFunctionTableAccess64(process, addr) != lookup->functionTable, ("Cache failure.\n")); + return lookup->functionTable; + } + else + { + s_functionTableCacheMisses++; + PVOID functionTable = symFunctionTableAccess64(process, addr); + lookup->keyAddress=addr; + lookup->functionTable=functionTable; + + return functionTable; + } + } + + static PVOID __stdcall functionTableAccess(HANDLE hProcess, DWORD64 dwAddr) + { + UNREF(hProcess); + DEBUG_FATAL(hProcess!=process, ("Wrong process handle for module base lookup.\n")); + return _functionTableLookup(DWORD(dwAddr)); + } + + // ---------------------------------------------------------------------- +} +using namespace DebugHelpNamespace; + +// ---------------------------------------------------------------------- + +BOOL CALLBACK DebugHelpNamespace::loadSymbolsForDllCallback(PTSTR ModuleName, DWORD64 ModuleBase, ULONG ModuleSize, PVOID UserContext) +{ + if (!library) + return false; + + CallbackData *callbackData = reinterpret_cast(UserContext); + + // see if this is the right file module and if we can load its symbol information + if (_stricmp(ModuleName, callbackData->name) == 0 && symLoadModule64(process, NULL, ModuleName, 0, ModuleBase, ModuleSize) != 0) + { + callbackData->loaded = true; + return FALSE; + } + + return TRUE; +} + +// ====================================================================== + +void DebugHelp::install() +{ + DEBUG_FATAL(library, ("DebugHelp already installed")); + + library = LoadLibrary("dbghelp_6.3.17.0.dll"); + if (library) + { + process = GetCurrentProcess(); + +#define GPA(a, b) a = reinterpret_cast(GetProcAddress(library, #b)); DEBUG_FATAL(!a, ("GetProcAddress failed for " #b)) + GPA(symSetOptions, SymSetOptions); + GPA(symInitialize, SymInitialize); + GPA(symCleanup, SymCleanup); + GPA(stackWalk64, StackWalk64); + GPA(symGetModuleInfo64, SymGetModuleInfo64); + GPA(symLoadModule64, SymLoadModule64); + GPA(symGetSymFromAddr64, SymGetSymFromAddr64); + GPA(symGetLineFromAddr64, SymGetLineFromAddr64); + GPA(symFunctionTableAccess64, SymFunctionTableAccess64); + GPA(symGetModuleBase64, SymGetModuleBase64); + GPA(symEnumerateModules64, SymEnumerateModules64); + GPA(enumerateLoadedModules64, EnumerateLoadedModules64); + GPA(miniDumpWriteDump, MiniDumpWriteDump); +#undef GPA + + IGNORE_RETURN(symSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES)); + + // get the path to the executable + char executableDirectory[MAX_PATH * 2]; + const DWORD result = GetModuleFileName(NULL, executableDirectory, sizeof(executableDirectory)); + FATAL(result == 0, ("GetModuleFileName failed")); + char * const slash = strrchr(executableDirectory, '\\'); + DEBUG_FATAL(!slash, ("Executable path does not contain a slash")); + *slash = '\0'; + + const BOOL result1 = symInitialize(process, executableDirectory, TRUE); + UNREF(result1); + DEBUG_FATAL(!result1, ("SymInitialize failed")); + + // ----------------------------------------------------------------------- + + s_baseAddressCache = (BaseAddressLookup *)VirtualAlloc(0, _baseAddressCacheSize(), MEM_COMMIT, PAGE_READWRITE); + + s_functionTableCache = (FunctionTableLookup *)VirtualAlloc(0, _functionTableCacheSize(), MEM_COMMIT, PAGE_READWRITE); + + // ----------------------------------------------------------------------- + } + + // Initialize the critical section one time only. + InitializeCriticalSection(&criticalSection); +} + +// ---------------------------------------------------------------------- + +void DebugHelp::remove() +{ + if (s_baseAddressCache) + { + VirtualFree(s_baseAddressCache, 0, MEM_RELEASE); + s_baseAddressCache=0; + } + + if (s_functionTableCache) + { + VirtualFree(s_functionTableCache, 0, MEM_RELEASE); + s_functionTableCache=0; + } + + if (library) + { + IGNORE_RETURN(symCleanup(process)); + IGNORE_RETURN(FreeLibrary(library)); + library = NULL; + process = NULL; + symSetOptions = NULL; + symInitialize = NULL; + symCleanup = NULL; + stackWalk64 = NULL; + symGetModuleInfo64 = NULL; + symLoadModule64 = NULL; + symGetSymFromAddr64 = NULL; + symGetLineFromAddr64 = NULL; + symFunctionTableAccess64 = NULL; + symGetModuleBase64 = NULL; + symEnumerateModules64 = NULL; + enumerateLoadedModules64 = NULL; + } + + // Release resources used by the critical section object. + DeleteCriticalSection(&criticalSection); +} + +// ---------------------------------------------------------------------- + +bool DebugHelp::loadSymbolsForDll(const char *name) +{ + if (!library) + return false; + + CallbackData callbackData = { name, false }; + enumerateLoadedModules64(process, loadSymbolsForDllCallback, reinterpret_cast(&callbackData)); + return callbackData.loaded; +} + +// ---------------------------------------------------------------------- +#pragma warning (disable: 4740 4748) +void DebugHelp::getCallStack(uint32 *callStack, int sizeOfCallStack) +{ + { + for (int i = 0; i < sizeOfCallStack; ++i) + callStack[i] = 0; + } + + if (!library) + return; + + CONTEXT context; + Zero(context); + context.ContextFlags = CONTEXT_FULL; + + // GetThreadContext returns invalid data when called from within the same thread + //if (!GetThreadContext(GetCurrentThread(), &context)) + // return; + + EnterCriticalSection(&criticalSection); + __asm + { + call GetEIP + GetEIP: + pop eax + mov context.Eip, eax + mov context.Esp, esp + mov context.Ebp, ebp + } + LeaveCriticalSection(&criticalSection); + + STACKFRAME64 stackFrame; + Zero(stackFrame); + stackFrame.AddrPC.Mode = AddrModeFlat; + stackFrame.AddrPC.Offset = context.Eip; + stackFrame.AddrStack.Offset = context.Esp; + stackFrame.AddrStack.Mode = AddrModeFlat; + stackFrame.AddrFrame.Offset = context.Ebp; + stackFrame.AddrFrame.Mode = AddrModeFlat; + + for (int i = 0; i < sizeOfCallStack; ++i, ++callStack) + { + if (stackWalk64(IMAGE_FILE_MACHINE_I386, process, process, &stackFrame, &context, NULL, functionTableAccess, getModuleBase, NULL)) + { + const DWORD64 Offset = stackFrame.AddrPC.Offset; + *callStack = DWORD(Offset); + } + } +} + +// ---------------------------------------------------------------------- + +void DebugHelp::reportCallStack(int const maxStackDepth) +{ + // look up the call stack information + int const callStackOffset = 2; + int const callStackSize = callStackOffset + maxStackDepth; + uint32 * callStack = static_cast(_alloca((callStackOffset + maxStackDepth) * sizeof(uint32))); + getCallStack(callStack, callStackOffset + maxStackDepth); + + // look up the caller's file and line + if (callStack[callStackOffset]) + { + char lib[4 * 1024] = { '\0' }; + char file[4 * 1024] = { '\0' }; + int line = 0; + for (int i = callStackOffset; i < callStackSize; ++i) + { + if (callStack[i]) + { + if (lookupAddress(callStack[i], lib, file, sizeof(file), line)) + REPORT_LOG(true, ("\t%s(%d) : caller %d\n", file, line, i-callStackOffset)); + else + REPORT_LOG(true, ("\tunknown(0x%08X) : caller %d\n", static_cast(callStack[i]), i-callStackOffset)); + } + } + } +} + +// ---------------------------------------------------------------------- + +bool DebugHelp::lookupAddress(uint32 address, char *libName, char *fileName, int fileNameLength, int &line) +{ + UNREF(libName); + + if (!library) + return false; + + // make sure the image is loaded + IMAGEHLP_MODULE64 imageHelpModule; + Zero(imageHelpModule); + imageHelpModule.SizeOfStruct = sizeof(imageHelpModule); + if (!symGetModuleInfo64(process, address, &imageHelpModule)) + return false; + + // look up the symbol + const int MaxNameLength = 256; + char buffer[sizeof(IMAGEHLP_SYMBOL64) + MaxNameLength]; + memset(buffer, 0, sizeof(buffer)); + IMAGEHLP_SYMBOL64 *imageHelpSymbol = reinterpret_cast(buffer); + imageHelpSymbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64); + imageHelpSymbol->Address = address; + imageHelpSymbol->MaxNameLength = MaxNameLength; + { + DWORD64 displacement = 0; + if (!symGetSymFromAddr64(process, address, &displacement, imageHelpSymbol)) + { + return false; + } + } + + // look up the source file name and line number + IMAGEHLP_LINE64 imageHelpLine; + Zero(imageHelpLine); + imageHelpLine.SizeOfStruct = sizeof(imageHelpLine); + { + DWORD displacement = 0; + if (!symGetLineFromAddr64(process, address, &displacement, &imageHelpLine)) + { + return false; + } + } + + // return the results + strncpy(fileName, imageHelpLine.FileName, static_cast(fileNameLength)); + line = static_cast(imageHelpLine.LineNumber); + return true; +} + +// ---------------------------------------------------------------------- + +bool DebugHelp::writeMiniDump(char const *miniDumpFileName, PEXCEPTION_POINTERS exceptionPointers) +{ + if (!miniDumpWriteDump) + return false; + + char buffer[256]; + if (!miniDumpFileName) + { + // get the program name + char programName[512]; + DWORD result = GetModuleFileName(NULL, programName, sizeof(programName)); + if (result == 0) + return false; + + // get the file name without the path + const char *shortProgramName = strrchr(programName, '\\'); + if (shortProgramName) + ++shortProgramName; + else + shortProgramName = programName; + + // lop off the extension + char *dot = const_cast(strchr(shortProgramName, '.')); + if (dot) + *dot = '\0'; + + // create a reasonable minidump filename + snprintf(buffer, sizeof(buffer), "%s_%d.mdmp", shortProgramName, static_cast(GetCurrentProcessId())); + miniDumpFileName = buffer; + } + + // create the file + HANDLE const file = CreateFile(miniDumpFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_ARCHIVE, NULL); + if (file == INVALID_HANDLE_VALUE) + return false; + + // create the exception information + MINIDUMP_EXCEPTION_INFORMATION exceptionInformationData; + MINIDUMP_EXCEPTION_INFORMATION *exceptionInformation = 0; + if (exceptionPointers) + { + exceptionInformationData.ThreadId = GetCurrentThreadId(); + exceptionInformationData.ExceptionPointers = exceptionPointers; + exceptionInformationData.ClientPointers = true; + exceptionInformation = &exceptionInformationData; + } + + // @todo make the minidump style modifiable + BOOL const result = miniDumpWriteDump(process, GetCurrentProcessId(), file, MiniDumpNormal, exceptionInformation, NULL, NULL); + + // close the file + CloseHandle(file); + + return result ? true : false; +} + +// ====================================================================== diff --git a/engine/shared/library/sharedDebug/src/win32/DebugHelp.h b/engine/shared/library/sharedDebug/src/win32/DebugHelp.h new file mode 100644 index 00000000..ead64d75 --- /dev/null +++ b/engine/shared/library/sharedDebug/src/win32/DebugHelp.h @@ -0,0 +1,35 @@ +// ====================================================================== +// +// DebugHelp.h +// copyright 2000 Verant Interactive +// +// ====================================================================== + +#ifndef DEBUG_HELP_H +#define DEBUG_HELP_H + +// ====================================================================== + +typedef unsigned long uint32; + +// ====================================================================== + +class DebugHelp +{ +public: + + static void install(); + static void remove(); + + static bool loadSymbolsForDll(const char *name); + + static void getCallStack(uint32 *callStack, int sizeOfCallStack); + static void reportCallStack(int const maxStackDepth = 4); + static bool lookupAddress(uint32 address, char *libName, char *fileName, int fileNameLength, int &line); + + static bool writeMiniDump(char const *miniDumpFileName=0, PEXCEPTION_POINTERS exceptionPointers=0); +}; + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedDebug/src/win32/DebugMonitor.cpp b/engine/shared/library/sharedDebug/src/win32/DebugMonitor.cpp new file mode 100644 index 00000000..775afc4f --- /dev/null +++ b/engine/shared/library/sharedDebug/src/win32/DebugMonitor.cpp @@ -0,0 +1,321 @@ +// ====================================================================== +// +// DebugMonitor.cpp +// copyright 1998 Bootprint Entertainment +// copyright 2001-2004 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedDebug/FirstSharedDebug.h" +#include "sharedDebug/DebugMonitor.h" + +#if PRODUCTION == 0 + +#include "sharedDebug/DebugFlags.h" +#include "sharedFoundation/ConfigFile.h" + +// ====================================================================== + +namespace DebugMonitorNamespace +{ + typedef void (*ChangedWindowCallback)(int x, int y, int width, int height); + + typedef bool (*InstallFunction)(int x, int y, int width, int height); + typedef void (*RemoveFunction)(); + typedef void (*ShowFunction)(); + typedef void (*HideFunction)(); + typedef void (*SetChangedWindowCallback)(ChangedWindowCallback); + typedef void (*SetBehindWindowFunction)(HWND window); + typedef void (*ClearScreenFunction)(); + typedef void (*ClearToCursorFunction)(); + typedef void (*GotoXYFunction)(int x, int y); + typedef void (*PrintFunction)(const char *string); + + void changedWindowCallback(int x, int y, int width, int height); + + HINSTANCE dll; + HKEY registryKey = HKEY_CLASSES_ROOT; + RemoveFunction removeFunction; + ShowFunction showFunction; + HideFunction hideFunction; + SetBehindWindowFunction setBehindWindowFunction; + ClearScreenFunction clearScreenFunction; + ClearToCursorFunction clearToCursorFunction; + GotoXYFunction gotoXYFunction; + PrintFunction printFunction; + + bool noClear; + + int GetRegistryValue(char const * name, int defaultValue) + { + int value; + DWORD type = 0; + DWORD size = sizeof(DWORD); + LONG result = RegQueryValueEx(registryKey, name, NULL, &type, reinterpret_cast(&value), &size); + if (result != ERROR_SUCCESS || type != REG_DWORD && size != sizeof(int)) + value = defaultValue; + return value; + } + + void SetRegistryValue(char const * name, int value) + { + RegSetValueEx(registryKey, name, NULL, REG_DWORD, reinterpret_cast(&value), sizeof(int)); + } +} +using namespace DebugMonitorNamespace; + +// ====================================================================== +// Install the debug monitor subsystem +// +// Remarks: +// +// This routine will first attempt to install the selected debug monitor. + +void DebugMonitor::install() +{ + dll = LoadLibrary("debugWindow.dll"); + if (dll) + { + InstallFunction installFunction = reinterpret_cast(GetProcAddress(dll, "install")); + + RegCreateKeyEx(HKEY_CURRENT_USER, "Software\\Sony Online Entertainment\\DebugWindow", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, ®istryKey, NULL); + + int const x = ConfigFile::getKeyInt("SharedDebug", "debugWindowX", GetRegistryValue("x", 0)); + int const y = ConfigFile::getKeyInt("SharedDebug", "debugWindowY", GetRegistryValue("y", 0)); + int const width = ConfigFile::getKeyInt("SharedDebug", "debugWindowWidth", GetRegistryValue("width", 80)); + int const height = ConfigFile::getKeyInt("SharedDebug", "debugWindowHeight", GetRegistryValue("height", 50)); + + if (installFunction && installFunction(x, y, width, height)) + { + showFunction = reinterpret_cast(GetProcAddress(dll, "showWindow")); + hideFunction = reinterpret_cast(GetProcAddress(dll, "hideWindow")); + removeFunction = reinterpret_cast(GetProcAddress(dll, "remove")); + setBehindWindowFunction = reinterpret_cast(GetProcAddress(dll, "setBehindWindow")); + clearScreenFunction = reinterpret_cast(GetProcAddress(dll, "clearScreen")); + clearToCursorFunction = reinterpret_cast(GetProcAddress(dll, "clearToCursor")); + gotoXYFunction = reinterpret_cast(GetProcAddress(dll, "gotoXY")); + printFunction = reinterpret_cast(GetProcAddress(dll, "print")); + + SetChangedWindowCallback setChangedWindowCallback = reinterpret_cast(GetProcAddress(dll, "setChangedWindowCallback")); + if (setChangedWindowCallback) + (*setChangedWindowCallback)(changedWindowCallback); + + DebugFlags::registerFlag(noClear, "SharedDebug", "noDebugMonitorClear"); + + if (ConfigFile::getKeyBool("SharedDebug", "debugWindow", false)) + show(); + } + else + { + installFunction = NULL; + const BOOL result = FreeLibrary(dll); + dll = NULL; + UNREF(result); + DEBUG_FATAL(!result, ("FreeLibrary failed")); + } + } +} + +// ---------------------------------------------------------------------- +/** + * Remove the debug monitor subsystem. + */ + +void DebugMonitor::remove() +{ + if (removeFunction) + removeFunction(); + + removeFunction = NULL; + setBehindWindowFunction = NULL; + clearScreenFunction = NULL; + clearToCursorFunction = NULL; + gotoXYFunction = NULL; + printFunction = NULL; + + if (dll) + { + const BOOL result = FreeLibrary(dll); + UNREF(result); + DEBUG_FATAL(!result, ("FreeLibrary failed")); + dll = NULL; + + if (registryKey != HKEY_CLASSES_ROOT) + { + RegCloseKey(registryKey); + registryKey = HKEY_CLASSES_ROOT; + } + } +} + +// ---------------------------------------------------------------------- + +void DebugMonitorNamespace::changedWindowCallback(int const x, int const y, int const width, int const height) +{ + SetRegistryValue("x", x); + SetRegistryValue("y", y); + SetRegistryValue("width", width); + SetRegistryValue("height", height); +} + +// ---------------------------------------------------------------------- + +void DebugMonitor::show() +{ + if (showFunction) + (*showFunction)(); +} + +// ---------------------------------------------------------------------- + +void DebugMonitor::hide() +{ + if (hideFunction) + (*hideFunction)(); +} + +// ---------------------------------------------------------------------- +/** + * Set the debug window's z-order. + */ + +void DebugMonitor::setBehindWindow(HWND window) +{ + if (setBehindWindowFunction) + setBehindWindowFunction(window); +} + +// ---------------------------------------------------------------------- +/** + * Clear the debug monitor and home the cursor. + * + * If the mono monitor is not installed, this routine does nothing. + * + * This routine will clear the contents of the debug monitor, reset the screen + * offset to 0, and move the cursor to the upper left corner of the screen. + * + * @see DebugMonitor::home(), DebugMonitor::clearToCursor() + */ + +void DebugMonitor::clearScreen() +{ + if (noClear) + return; + + if (clearScreenFunction) + clearScreenFunction(); +} + +// ---------------------------------------------------------------------- +/** + * Clear the debug monitor to the current cursor position and home the cursor. + * + * If the debug monitor is not installed, this routine does nothing. + * + * This routine will clear the contents of the debug monitor only up to the + * cursor position. If the cursor is not very far down on the screen, + * this routine may be significantly more efficient clearing the screen. + * + * It will also move the cursor to the upper left corner of the screen. + * + * @see DebugMonitor::clearScreen(), DebugMonitor::home() + */ + +void DebugMonitor::clearToCursor() +{ + if (clearToCursorFunction) + clearToCursorFunction(); + else + clearScreen(); +} + +// ====================================================================== +// Move the cursor to the upper left hand corner of the mono monitor screen +// +// Remarks: +// +// If the debug monitor is not installed, this routine does nothing. +// +// All printing happens at the cursor position. +// +// This routine is identical to calling gotoXY(0,0); +// +// See Also: +// +// DebugMonitor::gotoXY() + +void DebugMonitor::home() +{ + gotoXY(0,0); +} + +// ---------------------------------------------------------------------- +/** + * Position the cursor on the debug monitor screen. + * + * If the debug monitor is not installed, this routine does nothing. + * + * All printing happens at the cursor position. + * + * @param x New X position for the cursor + * @param y New Y position for the cursor + */ + +void DebugMonitor::gotoXY(int x, int y) +{ + if (gotoXYFunction) + gotoXYFunction(x, y); +} + +// ---------------------------------------------------------------------- +/** + * Display a string on the debug monitor. + * + * If the debug monitor is not installed, this routine does nothing. + * + * Printing occurs from the cursor position. + * + * Newline characters '\n' will cause the cursor position to advance to the + * beginning of the next line. If the cursor is already on the last line of + * the screen, the screen will scroll up one line and the cursor will move to + * the beginning of the last line. + * + * The backspace character '\b' will cause the cursor to move one character + * backwards. If at the beginning of the line, the cursor will move to the + * end of the previous line. If already on the first line of the screen, the + * cursor position and screen contents will be unchanged. + * + * All other characters are placed directly into the text frame buffer. + * After each character, the cursor will be logically advanced one + * character forward. If the cursor was on the last column, it will advance + * to the next line. If the cursor was already on the last line, the screen + * will be scrolled up one line and the cursor will move to the beginning of + * the last line. + * + * @param string String to display on the debug monitor + */ + +void DebugMonitor::print(const char *string) +{ + if (printFunction) + printFunction(string); +} + +// ---------------------------------------------------------------------- +/** + * Ensure all changes to the DebugMonitor have taken effect by the time + * this function returns. + * + * Note: some platforms may do nothing here. The Win32 platform does not + * require flushing. The Linux platform does. Call it assuming + * that it is needed. It will be a no-op when not required. + */ + +void DebugMonitor::flushOutput() +{ + // Win32 debug monitors don't need to do anything here. +} + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedDebug/src/win32/DebugMonitor.h b/engine/shared/library/sharedDebug/src/win32/DebugMonitor.h new file mode 100644 index 00000000..c992ed11 --- /dev/null +++ b/engine/shared/library/sharedDebug/src/win32/DebugMonitor.h @@ -0,0 +1,48 @@ +// ====================================================================== +// +// DebugMonitor.h +// +// Portions copyright 1998 Bootprint Entertainment +// Portions copyright 2002-2004 Sony Online Entertainment +// All Rights Reserved. +// +// ====================================================================== + +#ifndef INCLUDED_DebugMonitor_H +#define INCLUDED_DebugMonitor_H + +// ====================================================================== + +#include "sharedFoundation/Production.h" + +// ====================================================================== + +#if PRODUCTION == 0 + +class DebugMonitor +{ +public: + + static void install(); + static void remove(); + + static void setBehindWindow(HWND window); + + static void show(); + static void hide(); + + static void clearScreen(); + static void clearToCursor(); + + static void home(); + static void gotoXY(int x, int y); + static void print(const char *string); + + static void flushOutput(); +}; + +#endif + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedDebug/src/win32/FirstSharedDebug.cpp b/engine/shared/library/sharedDebug/src/win32/FirstSharedDebug.cpp new file mode 100644 index 00000000..ee53a32e --- /dev/null +++ b/engine/shared/library/sharedDebug/src/win32/FirstSharedDebug.cpp @@ -0,0 +1,8 @@ +// ====================================================================== +// +// FirstDebug.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "shareddebug/FirstSharedDebug.h" diff --git a/engine/shared/library/sharedDebug/src/win32/PerformanceTimer.cpp b/engine/shared/library/sharedDebug/src/win32/PerformanceTimer.cpp new file mode 100644 index 00000000..be770b7f --- /dev/null +++ b/engine/shared/library/sharedDebug/src/win32/PerformanceTimer.cpp @@ -0,0 +1,105 @@ +// +// PerformanceTimer.cpp +// Copyright 2000-2004 Sony Online Entertainment +// + +//------------------------------------------------------------------- + +#include "shareddebug/FirstSharedDebug.h" +#include "shareddebug/PerformanceTimer.h" + +//------------------------------------------------------------------- + +#include + +//------------------------------------------------------------------- + +__int64 PerformanceTimer::ms_frequency; + +//------------------------------------------------------------------- + +void PerformanceTimer::install() +{ + BOOL result = QueryPerformanceFrequency(reinterpret_cast(&ms_frequency)); + FATAL(!result, ("PerformanceTimer::install QPF failed")); +} + +//------------------------------------------------------------------- + +PerformanceTimer::PerformanceTimer() : + m_startTime (0), + m_stopTime (0) +{ + DEBUG_FATAL (ms_frequency == 0.f, ("PerformanceTimer not installed")); +} + +//------------------------------------------------------------------- + +PerformanceTimer::~PerformanceTimer() +{ +} + +//------------------------------------------------------------------- + +void PerformanceTimer::start() +{ + //-- get the current time + BOOL result = QueryPerformanceCounter(reinterpret_cast(&m_startTime)); + DEBUG_FATAL(!result, ("PerformanceTimer::start QPC failed")); + UNREF (result); +} + +//------------------------------------------------------------------- + +void PerformanceTimer::resume() +{ + __int64 delta = m_stopTime - m_startTime; + start(); + m_startTime -= delta; +} + +//------------------------------------------------------------------- + +void PerformanceTimer::stop () +{ + //-- get the current time + BOOL result = QueryPerformanceCounter(reinterpret_cast(&m_stopTime)); + DEBUG_FATAL(!result, ("PerformanceTimer::stop QPC failed")); + UNREF (result); +} + +//------------------------------------------------------------------- + +float PerformanceTimer::getElapsedTime() const +{ + return static_cast (m_stopTime - m_startTime) / static_cast (ms_frequency); +} + +// ---------------------------------------------------------------------- + +float PerformanceTimer::getSplitTime() const +{ + __int64 currentTime; + BOOL result = QueryPerformanceCounter(reinterpret_cast(¤tTime)); + UNREF(result); + DEBUG_FATAL(!result, ("PerformanceTimer::getSplitTime QPC failed")); + + return static_cast (currentTime - m_startTime) / static_cast (ms_frequency); +} + +//------------------------------------------------------------------- + +void PerformanceTimer::logElapsedTime(const char* string) const +{ + UNREF (string); + +#ifdef _DEBUG + static char buffer [1000]; + sprintf (buffer, "%s : %1.5f seconds\n", string ? string : "null", getElapsedTime()); + DEBUG_REPORT_LOG_PRINT (true, ("%s", buffer)); + DEBUG_OUTPUT_CHANNEL("Foundation\\PerformanceTimer", ("%s", buffer)); +#endif +} + +//------------------------------------------------------------------- + diff --git a/engine/shared/library/sharedDebug/src/win32/PerformanceTimer.h b/engine/shared/library/sharedDebug/src/win32/PerformanceTimer.h new file mode 100644 index 00000000..0fda7c1a --- /dev/null +++ b/engine/shared/library/sharedDebug/src/win32/PerformanceTimer.h @@ -0,0 +1,49 @@ +// +// PerformanceTimer.h +// Copyright 2000-2004 Sony Online Entertainment +// + +//------------------------------------------------------------------- + +#ifndef INCLUDED_PerformanceTimer_H +#define INCLUDED_PerformanceTimer_H + +//------------------------------------------------------------------- + +class PerformanceTimer +{ +public: + + static void install(); + +public: + + DLLEXPORT PerformanceTimer(); + DLLEXPORT ~PerformanceTimer(); + + void DLLEXPORT start(); + void DLLEXPORT resume(); + void DLLEXPORT stop(); + + float DLLEXPORT getElapsedTime() const; + float getSplitTime() const; // Get the time since the timer was started without stopping the timer. + void logElapsedTime(const char* string) const; + +private: + + PerformanceTimer(PerformanceTimer const &); + PerformanceTimer & operator=(PerformanceTimer const &); + +private: + + static __int64 ms_frequency; + +private: + + __int64 m_startTime; + __int64 m_stopTime; +}; + +//------------------------------------------------------------------- + +#endif \ No newline at end of file diff --git a/engine/shared/library/sharedDebug/src/win32/ProfilerTimer.cpp b/engine/shared/library/sharedDebug/src/win32/ProfilerTimer.cpp new file mode 100644 index 00000000..774765a6 --- /dev/null +++ b/engine/shared/library/sharedDebug/src/win32/ProfilerTimer.cpp @@ -0,0 +1,90 @@ +// ====================================================================== +// +// ProfilerTimer.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "shareddebug/FirstSharedDebug.h" +#include "shareddebug/ProfilerTimer.h" + +#include "shareddebug/DebugFlags.h" +#include "sharedFoundation/WindowsWrapper.h" + +// ====================================================================== + +namespace ProfilerTimerNamespace +{ + ProfilerTimer::Type ms_qpcFrequency; + float ms_floatQpcFrequency; + __int64 ms_rdtsc; + __int64 ms_qpc; + bool ms_useRdtsc; + +} +using namespace ProfilerTimerNamespace; + +// ====================================================================== + +static __int64 __declspec(naked) __stdcall readTimeStampCounter() +{ + __asm + { + rdtsc; + ret; + } +} + +// ====================================================================== + +void ProfilerTimer::install() +{ + IGNORE_RETURN(QueryPerformanceCounter(reinterpret_cast(&ms_qpc))); + ms_rdtsc = readTimeStampCounter(); + + IGNORE_RETURN(QueryPerformanceFrequency(reinterpret_cast(&ms_qpcFrequency))); + ms_floatQpcFrequency = static_cast(ms_qpcFrequency); + + DebugFlags::registerFlag(ms_useRdtsc, "SharedDebug/Profiler", "useRdtsc"); +} + +// ---------------------------------------------------------------------- + +void ProfilerTimer::getTime(Type &time) +{ + if (ms_useRdtsc) + time = readTimeStampCounter(); + else + IGNORE_RETURN(QueryPerformanceCounter(reinterpret_cast(&time))); +} + +// ---------------------------------------------------------------------- + +void ProfilerTimer::getCalibratedTime(Type &time, Type &frequency) +{ + if (ms_useRdtsc) + { + __int64 qpc; + IGNORE_RETURN(QueryPerformanceCounter(reinterpret_cast(&qpc))); + __int64 rdtsc = readTimeStampCounter(); + + float const t = static_cast(qpc - ms_qpc) / ms_floatQpcFrequency; + frequency = static_cast<__int64>(static_cast(rdtsc - ms_rdtsc) / t); + + time = rdtsc; + ms_qpc = qpc; + ms_rdtsc = time; + } + else + { + IGNORE_RETURN(QueryPerformanceCounter(reinterpret_cast(&time))); + frequency = ms_qpcFrequency; + } +} + +void ProfilerTimer::getFrequency(Type &frequency) +{ + frequency = ms_qpcFrequency; +} + +// ====================================================================== diff --git a/engine/shared/library/sharedDebug/src/win32/ProfilerTimer.h b/engine/shared/library/sharedDebug/src/win32/ProfilerTimer.h new file mode 100644 index 00000000..fe1b8f9a --- /dev/null +++ b/engine/shared/library/sharedDebug/src/win32/ProfilerTimer.h @@ -0,0 +1,29 @@ +// ====================================================================== +// +// ProfilerTimer.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_ProfilerTimer_H +#define INCLUDED_ProfilerTimer_H + +// ====================================================================== + +class ProfilerTimer +{ +public: + + typedef __int64 Type; + +public: + + static void install(); + static void getTime(Type &time); + static void getCalibratedTime(Type &time, Type &frequency); + static void getFrequency(Type &frequency); +}; + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedDebug/src/win32/VTune.cpp b/engine/shared/library/sharedDebug/src/win32/VTune.cpp new file mode 100644 index 00000000..6b876fff --- /dev/null +++ b/engine/shared/library/sharedDebug/src/win32/VTune.cpp @@ -0,0 +1,142 @@ +// ====================================================================== +// +// VTune.cpp +// Copyright 2000-01, Sony Online Entertainment Inc. +// All Rights Reserved. +// +// ====================================================================== + +#include "sharedDebug/FirstSharedDebug.h" +#include "sharedDebug/VTune.h" + +#if PRODUCTION == 0 + +#include "sharedDebug/DebugFlags.h" + +#include + +// ====================================================================== + +HMODULE VTune::ms_module; +VTune::PauseFunction VTune::ms_pauseFunction; +VTune::ResumeFunction VTune::ms_resumeFunction; +VTune::State VTune::ms_state; +bool VTune::ms_resumeNextFrame; +bool VTune::ms_pauseNextFrame; +bool VTune::ms_debugReport; + +// ====================================================================== + +void VTune::install() +{ + DEBUG_FATAL(ms_module, ("vtune already installed")); + + ms_module = LoadLibrary("VTuneAPI"); + if (!ms_module) + return; + + ms_pauseFunction = reinterpret_cast(GetProcAddress(ms_module, "VTPause")); + ms_resumeFunction = reinterpret_cast(GetProcAddress(ms_module, "VTResume")); + ms_state = S_default; + +#if PRODUCTION == 0 + DebugFlags::registerFlag(ms_debugReport, "SharedDebug", "vtuneState", debugReport); +#endif +} + +// ---------------------------------------------------------------------- + +void VTune::remove() +{ + if (ms_module) + { + FreeLibrary(ms_module); + ms_pauseFunction = 0; + ms_resumeFunction = 0; + } +} + +// ---------------------------------------------------------------------- + +void VTune::debugReport() +{ + switch (ms_state) + { + case S_default: + REPORT_PRINT(true, ("Vtune state unknown\n")); + break; + + case S_sampling: + REPORT_PRINT(true, ("Vtune is sampling\n")); + break; + + case S_paused: + REPORT_PRINT(true, ("Vtune is NOT sampling\n")); + break; + + default: + DEBUG_FATAL(true, ("bad case")); + + } +} + +// ---------------------------------------------------------------------- + +void VTune::resume() +{ + if (ms_module && ms_resumeFunction) + { + MessageBeep(MB_OK); + (*ms_resumeFunction)(); + ms_state = S_sampling; + } +} + +// ---------------------------------------------------------------------- + +void VTune::pause() +{ + if (ms_module && ms_pauseFunction) + { + (*ms_pauseFunction)(); + MessageBeep(MB_ICONEXCLAMATION); + ms_state = S_paused; + } +} + +// ---------------------------------------------------------------------- + +void VTune::pauseNextFrame() +{ + ms_pauseNextFrame = true; + ms_resumeNextFrame = false; +} + +// ---------------------------------------------------------------------- + +void VTune::resumeNextFrame() +{ + ms_pauseNextFrame = false; + ms_resumeNextFrame = true; +} + +// ---------------------------------------------------------------------- + +void VTune::beginFrame() +{ + if (ms_pauseNextFrame) + { + pause(); + ms_pauseNextFrame = false; + } + + if (ms_resumeNextFrame) + { + resume(); + ms_resumeNextFrame = false; + } +} + +// ====================================================================== + +#endif // PRODUCTION == 0 diff --git a/engine/shared/library/sharedDebug/src/win32/VTune.h b/engine/shared/library/sharedDebug/src/win32/VTune.h new file mode 100644 index 00000000..31125646 --- /dev/null +++ b/engine/shared/library/sharedDebug/src/win32/VTune.h @@ -0,0 +1,65 @@ +// ====================================================================== +// +// VTune.h +// Copyright 2000-01, Sony Online Entertainment Inc. +// All Rights Reserved. +// +// ====================================================================== + +#ifndef INCLUDED_VTune_H +#define INCLUDED_VTune_H + +// ====================================================================== + +#include "sharedFoundation/Production.h" + +// ====================================================================== + +#if PRODUCTION == 0 + +class VTune +{ +public: + + static void install(); + + static void resume(); + static void pause(); + + static void resumeNextFrame(); + static void pauseNextFrame(); + static void beginFrame(); + +private: + + static void remove(); + static void debugReport(); + +private: + + enum State + { + S_default, + S_sampling, + S_paused + }; + + typedef void (__cdecl *PauseFunction)(void); + typedef void (__cdecl *ResumeFunction)(void); + +private: + + static HMODULE ms_module; + static PauseFunction ms_pauseFunction; + static ResumeFunction ms_resumeFunction; + static State ms_state; + static bool ms_resumeNextFrame; + static bool ms_pauseNextFrame; + static bool ms_debugReport; +}; + +#endif // PRODUCTION == 0 + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedFoundationTypes/CMakeLists.txt b/engine/shared/library/sharedFoundationTypes/CMakeLists.txt new file mode 100644 index 00000000..03e12574 --- /dev/null +++ b/engine/shared/library/sharedFoundationTypes/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 2.8) + +project(sharedFoundationTypes) + +if(WIN32) + add_definitions(/D_CRT_SECURE_NO_WARNINGS) +endif() + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include/public) + +add_subdirectory(src) diff --git a/engine/shared/library/sharedFoundationTypes/src/CMakeLists.txt b/engine/shared/library/sharedFoundationTypes/src/CMakeLists.txt new file mode 100644 index 00000000..fb6430cf --- /dev/null +++ b/engine/shared/library/sharedFoundationTypes/src/CMakeLists.txt @@ -0,0 +1,27 @@ + +set(SHARED_SOURCES + shared/FoundationTypes.h +) + +if(WIN32) + set(PLATFORM_SOURCES + win32/FoundationTypesWin32.h + ) + + set(EXCLUDE_PROJECT "") +else() + set(PLATFORM_SOURCES + linux/FoundationTypesLinux.h + ) + + set(EXCLUDE_PROJECT "EXCLUDE_FROM_ALL") +endif() + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/shared) + +add_library(sharedFoundationTypes STATIC ${EXCLUDE_PROJECT} + ${SHARED_SOURCES} + ${PLATFORM_SOURCES} +) + +set_target_properties(sharedFoundationTypes PROPERTIES LINKER_LANGUAGE CXX) diff --git a/engine/shared/library/sharedMemoryManager/CMakeLists.txt b/engine/shared/library/sharedMemoryManager/CMakeLists.txt new file mode 100644 index 00000000..9de56309 --- /dev/null +++ b/engine/shared/library/sharedMemoryManager/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 2.8) + +project(sharedMemoryManager) + +if(WIN32) + add_definitions(/D_CRT_SECURE_NO_WARNINGS) +endif() + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include/public) + +add_subdirectory(src) diff --git a/engine/shared/library/sharedMemoryManager/include/private/sharedMemoryManager/OsMemory.h b/engine/shared/library/sharedMemoryManager/include/private/sharedMemoryManager/OsMemory.h new file mode 100644 index 00000000..eee3c819 --- /dev/null +++ b/engine/shared/library/sharedMemoryManager/include/private/sharedMemoryManager/OsMemory.h @@ -0,0 +1,7 @@ +#if defined(PLATFORM_WIN32) +#include "../../src/win32/OsMemory.h" +#elif defined(PLATFORM_LINUX) +#include "../../src/linux/OsMemory.h" +#else +#error unsupported platform +#endif diff --git a/engine/shared/library/sharedMemoryManager/include/public/sharedMemoryManager/FirstSharedMemoryManager.h b/engine/shared/library/sharedMemoryManager/include/public/sharedMemoryManager/FirstSharedMemoryManager.h new file mode 100644 index 00000000..44494b87 --- /dev/null +++ b/engine/shared/library/sharedMemoryManager/include/public/sharedMemoryManager/FirstSharedMemoryManager.h @@ -0,0 +1 @@ +#include "../../src/shared/FirstSharedMemoryManager.h" diff --git a/engine/shared/library/sharedMemoryManager/include/public/sharedMemoryManager/MemoryManager.h b/engine/shared/library/sharedMemoryManager/include/public/sharedMemoryManager/MemoryManager.h new file mode 100644 index 00000000..d99f899b --- /dev/null +++ b/engine/shared/library/sharedMemoryManager/include/public/sharedMemoryManager/MemoryManager.h @@ -0,0 +1 @@ +#include "../../src/shared/MemoryManager.h" diff --git a/engine/shared/library/sharedMemoryManager/include/public/sharedMemoryManager/OsNewDel.h b/engine/shared/library/sharedMemoryManager/include/public/sharedMemoryManager/OsNewDel.h new file mode 100644 index 00000000..6ddbc618 --- /dev/null +++ b/engine/shared/library/sharedMemoryManager/include/public/sharedMemoryManager/OsNewDel.h @@ -0,0 +1,7 @@ +#if defined(PLATFORM_WIN32) +#include "../../src/win32/OsNewDel.h" +#elif defined(PLATFORM_LINUX) +#include "../../src/linux/OsNewDel.h" +#else +#error unsupported platform +#endif diff --git a/engine/shared/library/sharedMemoryManager/src/CMakeLists.txt b/engine/shared/library/sharedMemoryManager/src/CMakeLists.txt new file mode 100644 index 00000000..d69b5815 --- /dev/null +++ b/engine/shared/library/sharedMemoryManager/src/CMakeLists.txt @@ -0,0 +1,37 @@ + +set(SHARED_SOURCES + shared/FirstSharedMemoryManager.cpp + shared/FirstSharedMemoryManager.h + shared/MemoryManager.cpp + shared/MemoryManager.h +) + +if(WIN32) + set(PLATFORM_SOURCES + win32/OsMemory.cpp + win32/OsMemory.h + win32/OsNewDel.cpp + win32/OsNewDel.h + ) + + include_directories(${CMAKE_CURRENT_SOURCE_DIR}/win32) +else() + set(PLATFORM_SOURCES + linux/OsMemory.cpp + linux/OsMemory.h + linux/OsNewDel.cpp + linux/OsNewDel.h + ) + + include_directories(${CMAKE_CURRENT_SOURCE_DIR}/linux) +endif() + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/shared + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedFoundationTypes/include/public +) + +add_library(sharedMemoryManager STATIC + ${SHARED_SOURCES} + ${PLATFORM_SOURCES} +) diff --git a/engine/shared/library/sharedMemoryManager/src/linux/OsMemory.cpp b/engine/shared/library/sharedMemoryManager/src/linux/OsMemory.cpp new file mode 100644 index 00000000..6a63523b --- /dev/null +++ b/engine/shared/library/sharedMemoryManager/src/linux/OsMemory.cpp @@ -0,0 +1,40 @@ +// ====================================================================== +// +// OsMemory.cpp +// +// Copyright 2002 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedMemoryManager/FirstSharedMemoryManager.h" +#include "sharedMemoryManager/OsMemory.h" + +// ====================================================================== + +void OsMemory::install() +{ +} + +// ---------------------------------------------------------------------- + +void OsMemory::remove() +{ +} + +// ---------------------------------------------------------------------- + +void *OsMemory::commit(void *, size_t bytes) +{ + return ::malloc(bytes); +} + +// ---------------------------------------------------------------------- + +bool OsMemory::free(void *addr, size_t) +{ + ::free(addr); + + return true; +} + +// ====================================================================== diff --git a/engine/shared/library/sharedMemoryManager/src/linux/OsMemory.h b/engine/shared/library/sharedMemoryManager/src/linux/OsMemory.h new file mode 100644 index 00000000..8bc52ae5 --- /dev/null +++ b/engine/shared/library/sharedMemoryManager/src/linux/OsMemory.h @@ -0,0 +1,29 @@ +// ====================================================================== +// +// OsMemory.h +// +// Copyright 2002 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_OsMemory_H +#define INCLUDED_OsMemory_H + +// ====================================================================== + +class OsMemory +{ +public: + static void install(); + static void remove(); + + static void * reserve(size_t bytes); + static void * commit(void *addr, size_t bytes); + static bool free(void *addr, size_t bytes); + static bool protect(void *addr, size_t bytes, bool allowAccess); +}; + +// ====================================================================== + +#endif // INCLUDED_OsMemory_H + diff --git a/engine/shared/library/sharedMemoryManager/src/linux/OsNewDel.cpp b/engine/shared/library/sharedMemoryManager/src/linux/OsNewDel.cpp new file mode 100644 index 00000000..553adc93 --- /dev/null +++ b/engine/shared/library/sharedMemoryManager/src/linux/OsNewDel.cpp @@ -0,0 +1,134 @@ +// ====================================================================== +// +// OsNewDel.cpp +// +// Copyright 2002 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedMemoryManager/FirstSharedMemoryManager.h" +#include "sharedMemoryManager/MemoryManager.h" +#include "sharedMemoryManager/OsNewDel.h" + +#include +#include +#include + +static MemoryManager memoryManager __attribute__ ((init_priority (101))); + +#define USE_LIBC_MALLOC_HOOKS 0 + +#if USE_LIBC_MALLOC_HOOKS +extern "C" +{ + void memoryManagerFreeHook(__malloc_ptr_t __ptr, __const __malloc_ptr_t) + { + delete[] (char *)__ptr; + } + + __malloc_ptr_t memoryManagerMallocHook(size_t __size, const __malloc_ptr_t) + { + return new char[__size]; + } + + __malloc_ptr_t memoryManagerReallocHook(__malloc_ptr_t __ptr, size_t size, __const __malloc_ptr_t) + { + if(! __ptr) + return new char[size]; + + return MemoryManager::reallocate(__ptr, size); + } + + __malloc_ptr_t memoryManagerMemAlignHook(size_t alignment, size_t size, __const __malloc_ptr_t) + { + DEBUG_FATAL(true, ("memalign not implemented!")); + return new char[size]; + } + + static void memoryManagerMallocInitializeHook(void) + { + __free_hook = memoryManagerFreeHook; + __malloc_hook = memoryManagerMallocHook; + __realloc_hook = memoryManagerReallocHook; + __memalign_hook = memoryManagerMemAlignHook; + } + void (*__malloc_initialize_hook) (void) = memoryManagerMallocInitializeHook; +} + +#endif//USE_LIBC_MALLOC_HOOKS + +// ====================================================================== + +void *operator new(size_t size, MemoryManagerNotALeak) throw (std::bad_alloc) +{ + return MemoryManager::allocate(size, reinterpret_cast(__builtin_return_address(0)), false, false); +} + +// ---------------------------------------------------------------------- + +void *operator new(size_t size) throw (std::bad_alloc) +{ + return MemoryManager::allocate(size, reinterpret_cast(__builtin_return_address(0)), false, true); +} + +// ---------------------------------------------------------------------- + +void *operator new[](size_t size) throw (std::bad_alloc) +{ + return MemoryManager::allocate(size, reinterpret_cast(__builtin_return_address(0)), true, true); +} + +// ---------------------------------------------------------------------- + +void *operator new(size_t size, const char *file, int line) throw (std::bad_alloc) +{ + return MemoryManager::allocate(size, reinterpret_cast(__builtin_return_address(0)), false, true); +} + +// ---------------------------------------------------------------------- + +void *operator new[](size_t size, const char *file, int line) throw (std::bad_alloc) +{ + return MemoryManager::allocate(size, reinterpret_cast(__builtin_return_address(0)), true, true); +} + +// ---------------------------------------------------------------------- + +void operator delete(void *pointer) throw() +{ + if (pointer) + MemoryManager::free(pointer, false); +} + +// ---------------------------------------------------------------------- + +void operator delete[](void *pointer) throw() +{ + if (pointer) + MemoryManager::free(pointer, true); +} + +// ---------------------------------------------------------------------- + +void operator delete(void *pointer, const char *file, int line) throw() +{ + UNREF(file); + UNREF(line); + + if (pointer) + MemoryManager::free(pointer, false); +} + +// ---------------------------------------------------------------------- + +void operator delete[](void *pointer, const char *file, int line) throw() +{ + UNREF(file); + UNREF(line); + + if (pointer) + MemoryManager::free(pointer, true); +} + +// ====================================================================== + diff --git a/engine/shared/library/sharedMemoryManager/src/linux/OsNewDel.h b/engine/shared/library/sharedMemoryManager/src/linux/OsNewDel.h new file mode 100644 index 00000000..ad3d4df8 --- /dev/null +++ b/engine/shared/library/sharedMemoryManager/src/linux/OsNewDel.h @@ -0,0 +1,33 @@ +// ====================================================================== +// +// OsNewDel.h +// +// Copyright 2002 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_OsNewDel_H +#define INCLUDED_OsNewDel_H + +// ====================================================================== + +enum MemoryManagerNotALeak +{ + MM_notALeak +}; + +void *operator new(size_t size, MemoryManagerNotALeak) throw(std::bad_alloc); +void *operator new(size_t size) throw(std::bad_alloc); +void *operator new[](size_t size) throw(std::bad_alloc); +void *operator new(size_t size, char const *file, int line) throw(std::bad_alloc); +void *operator new[](size_t size, char const *file, int line) throw(std::bad_alloc); + +void operator delete(void *pointer) throw(); +void operator delete[](void *pointer) throw(); +void operator delete(void *pointer, char const *file, int line) throw(); +void operator delete[](void *pointer, char const *file, int line) throw(); + +// ====================================================================== + +#endif // INCLUDED_OsNewDel_H + diff --git a/engine/shared/library/sharedMemoryManager/src/shared/FirstSharedMemoryManager.cpp b/engine/shared/library/sharedMemoryManager/src/shared/FirstSharedMemoryManager.cpp new file mode 100644 index 00000000..d1d100b2 --- /dev/null +++ b/engine/shared/library/sharedMemoryManager/src/shared/FirstSharedMemoryManager.cpp @@ -0,0 +1,8 @@ +// ====================================================================== +// +// FirstMemoryManager.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedMemoryManager/FirstSharedMemoryManager.h" diff --git a/engine/shared/library/sharedMemoryManager/src/shared/FirstSharedMemoryManager.h b/engine/shared/library/sharedMemoryManager/src/shared/FirstSharedMemoryManager.h new file mode 100644 index 00000000..0beb0fc5 --- /dev/null +++ b/engine/shared/library/sharedMemoryManager/src/shared/FirstSharedMemoryManager.h @@ -0,0 +1,19 @@ +// ====================================================================== +// +// FirstMemoryManager.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_FirstMemoryManager_H +#define INCLUDED_FirstMemoryManager_H + +// ====================================================================== + +#include "sharedFoundationTypes/FoundationTypes.h" +#include "sharedDebug/FirstSharedDebug.h" +#include "sharedFoundation/FirstSharedFoundation.h" + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedMemoryManager/src/shared/MemoryManager.cpp b/engine/shared/library/sharedMemoryManager/src/shared/MemoryManager.cpp new file mode 100644 index 00000000..69a79c00 --- /dev/null +++ b/engine/shared/library/sharedMemoryManager/src/shared/MemoryManager.cpp @@ -0,0 +1,2239 @@ +// ====================================================================== +// +// MemoryManager.cpp +// Portions copyright 1998 Bootprint Entertainment +// Portions copyright 2002 Sony Online Entertainment +// All Rights Reserved +// +// ====================================================================== + +#include "sharedMemoryManager/FirstSharedMemoryManager.h" +#include "sharedMemoryManager/MemoryManager.h" +#include "sharedMemoryManager/OsMemory.h" + +#include "sharedFoundation/ConfigSharedFoundation.h" +#include "sharedFoundation/Production.h" +#include "sharedDebug/DebugFlags.h" +#include "sharedDebug/DebugMonitor.h" +#include "sharedDebug/PixCounter.h" +#include "sharedDebug/Profiler.h" +#include "sharedSynchronization/RecursiveMutex.h" +#include "sharedDebug/RemoteDebug.h" + +#include + +#ifdef _WIN32 +#include +#include +#else +#include +#endif + +#include +#include +#include +#include + +#ifdef _WIN32 +#define DISABLE_MEMORY_MANAGER 0 +#else +#define DISABLE_MEMORY_MANAGER 0 +#endif + +//lint -e826 // Suspicious pointer-to-pointer conversion (area too small) + +#if !DISABLE_MEMORY_MANAGER + +// ====================================================================== +// +#define DISABLED 0 + +#if PRODUCTION + + #define DO_TRACK 0 + #define DO_SCALAR 0 + #define DO_GUARDS 0 + #define DO_INITIALIZE_FILLS 0 + #define DO_FREE_FILLS 0 + +#else + + #if DEBUG_LEVEL == DEBUG_LEVEL_DEBUG + + #define DO_TRACK 1 + #define DO_SCALAR 1 + #define DO_GUARDS 1 + #define DO_INITIALIZE_FILLS 1 + #define DO_FREE_FILLS 1 + + #elif DEBUG_LEVEL == DEBUG_LEVEL_OPTIMIZED + + #define DO_TRACK 1 + #define DO_SCALAR 1 + #define DO_GUARDS 1 + #define DO_INITIALIZE_FILLS 1 + #define DO_FREE_FILLS 1 + + #elif DEBUG_LEVEL == DEBUG_LEVEL_RELEASE + + #define DO_TRACK 0 + #define DO_SCALAR 0 + #define DO_GUARDS 0 + #define DO_INITIALIZE_FILLS 0 + #define DO_FREE_FILLS 0 + + #else + + #error unknown DEBUG_LEVEL + + #endif + +#endif + +#ifdef PLATFORM_LINUX + #undef DO_TRACK + #define DO_TRACK 5 +#endif + +// ====================================================================== + +namespace MemoryManagerNamespace +{ + class Block; + + class SystemAllocation + { + public: + + SystemAllocation(int size); + ~SystemAllocation(); + + int getSize(); + + SystemAllocation * getNext(); + void setNext(SystemAllocation *); + + Block * getFirstMemoryBlock(); + Block * getLastMemoryBlock(); + + private: + + int m_size; + SystemAllocation * m_next; + int m_pad1; + int m_pad2; + }; + + + class Block + { + public: + + enum { cms_requestedSizeBits = 30 }; + + public: + + Block * getPrevious(); + Block const * getPrevious() const; + void setPrevious(Block *previous); + + Block * getNext(); + Block const * getNext() const; + void setNext(Block *previous); + + bool isFree() const; + void setFree(bool free); + + int getSize() const; + + private: + + Block * m_previous; + Block * m_next; + bool m_free:1; + + protected: + + // these are only in allocated blocks, but we can't expect a compiler to merge bitfields across multiple classes +#if DO_SCALAR + bool m_array:1; +#endif +#if DO_TRACK + bool m_leakTest:1; +#endif +#if DO_TRACK || DO_GUARDS + unsigned int m_requestedSize:cms_requestedSizeBits; +#endif + }; + + class FreeBlock; + + void addToFreeList(Block * block); + void removeFromFreeList(FreeBlock * block); + FreeBlock *searchFreeList(int blockSize); + + class FreeBlock : public Block + { + private: + + friend void addToFreeList(Block *block); + friend void removeFromFreeList(FreeBlock *block); + friend FreeBlock *searchFreeList(int blockSize); + + private: + FreeBlock * m_smallerFreeBlock; + FreeBlock * m_sameFreeBlock; + FreeBlock * m_largerFreeBlock; + FreeBlock * m_parentFreeBlock; + }; + + class AllocatedBlock : public Block + { + public: + +#if DO_SCALAR + bool isAllocatedAsArray() const; + void setAllocatedAsArray(bool array); +#endif + +#if DO_TRACK + bool checkForLeaks() const; + void setCheckForLeaks(bool checkForLeaks); + + uint32 getOwner(int index) const; + void setOwner(int index, uint32 owner); + void fillOwnerWithFreePattern(); + +#endif + +#if DO_TRACK || DO_GUARDS + int getRequestedSize() const; + void setRequestedSize(int requestedSize); +#endif + + private: + +#if DO_TRACK + uint32 m_owner[DO_TRACK]; +#endif + }; + +#ifdef _DEBUG + void logAllocationsNextFrame(); +#endif + + int convertBytesToMegabytesForSystemAllocation(int bytes); + void allocateSystemMemory(int megabytes); + + int min(int a, int b); + int max(int a, int b); + byte const * min(byte const * a, byte const * b); + byte const * max(byte const * a, byte const * b); + + int report(bool leak); + void report(AllocatedBlock const * block, bool leak); + + int quickGuessIsValidPointerFromHeap(void const *); + int authoritativeIsValidPointerFromHeap(void const *); + + void emitCharacters(char * & buffer, int blockSize, int & carryOverFree, int & carryOverUsed, int newFree, int newUsed, char const * const bufferOverrunAddress); + + bool reportToFile(char const *fileName, bool leak); + void outputDebugStringWrapper(char const * message); + void logMessageToFd(char const * message); + + int const cms_blockSize = (sizeof(Block) + 15) & (~15); + int const cms_freeBlockSize = (sizeof(FreeBlock) + 15) & (~15); + extern int const cms_allocatedBlockSize = (sizeof(AllocatedBlock) + 15) & (~15); + byte const cms_guardFillPattern = 0xAB; + byte const cms_initializeFillPattern = 0xCD; + byte const cms_freeFillPattern = 0xEF; + int const cms_guardBandSize = DO_GUARDS ? 16 : 0; + + int const cms_systemAllocationRoundSize = 4 * 1024 * 1024; + int const cms_systemAllocationMinimumSize = 4 * 1024 * 1024; + + bool ms_installed; + bool ms_limitSet; + bool ms_hardLimit; + int ms_limitMegabytes = 2047; + SystemAllocation * ms_firstSystemAllocation; + int ms_numberOfSystemAllocations; + int ms_systemMemoryAllocatedMegabytes; + bool ms_reportAllocations; +// bool ms_logEachAlloc; + +#if PRODUCTION == 0 + PixCounter::ResetInteger ms_allocationsPerFrame; + PixCounter::ResetInteger ms_bytesAllocatedPerFrame; + PixCounter::ResetInteger ms_freesPerFrame; + PixCounter::ResetInteger ms_bytesFreedPerFrame; +#endif + +#ifdef _DEBUG + bool ms_debugReportFlag; + bool ms_debugReportMapFlag; + bool ms_debugReportAllocations; + bool ms_debugReportLogMemoryAllocFreePointers; + bool ms_debugLogAllocations; + bool ms_debugLogAllocationsNextFrame; + bool ms_debugVerifyGuardPatterns; + bool ms_debugVerifyFreePatterns; + bool ms_debugProfileAllocate; + bool ms_debugLogAllocationsNextFrameStarted; +#endif + + RecursiveMutex * ms_criticalSection; + + char ms_memoryManagerBuffer[sizeof(MemoryManager)]; + + FreeBlock * ms_firstFreeBlock; + + int ms_allocateCalls; + unsigned long ms_allocateBytesTotal; +#ifndef _WIN32 + int ms_processVmSizeKBytes; +#endif + int ms_freeCalls; + + int ms_allocations; + int ms_maxAllocations; + + int ms_freeBlocks; + int ms_maxFreeBlocks; +#ifdef _DEBUG + int ms_maxTreeHeight; +#endif + +#if DO_TRACK || DO_GUARDS + unsigned long ms_currentBytesRequested; +#endif + unsigned long ms_currentBytesAllocated; +#if DO_TRACK + unsigned long ms_currentBytesAllocatedNoLeakTest; +#endif + unsigned long ms_maxBytesAllocated; + + bool ms_allowNameLookup = true; + int ms_logMessageFd = -1; + void (*LogMessage)(char const * message) = &outputDebugStringWrapper; + + PROFILER_BLOCK_DEFINE(ms_allocateProfilerBlock, "MemoryManager::allocate"); +} +using namespace MemoryManagerNamespace; + +// ====================================================================== + +SystemAllocation::SystemAllocation(int size) +: + m_size(size), + m_next(NULL), + m_pad1(0), + m_pad2(0) +{ + DEBUG_FATAL(sizeof(*this) != cms_blockSize, ("SystemAllocation size is not %d bytes", cms_blockSize)); + +#if DO_FREE_FILLS + // fill the newly allocated user memory with the free pattern + imemset(reinterpret_cast(this) + cms_blockSize, cms_freeFillPattern, m_size - cms_blockSize); +#endif + + Block * firstMemoryBlock = getFirstMemoryBlock(); + Block * firstFreeBlock = reinterpret_cast(reinterpret_cast(firstMemoryBlock) + cms_blockSize); + Block * lastMemoryBlock = getLastMemoryBlock(); + + // set up the prefix sentinel block + firstMemoryBlock->setPrevious(NULL); + firstMemoryBlock->setNext(firstFreeBlock); + firstMemoryBlock->setFree(false); + + firstFreeBlock->setPrevious(firstMemoryBlock); + firstFreeBlock->setNext(lastMemoryBlock); + firstFreeBlock->setFree(true); + + // set up the suffix sentinel block + lastMemoryBlock->setPrevious(firstFreeBlock); + lastMemoryBlock->setNext(NULL); + lastMemoryBlock->setFree(false); + + // put the first block on the free list + addToFreeList(firstFreeBlock); +} + +// ---------------------------------------------------------------------- + +SystemAllocation::~SystemAllocation() +{ + OsMemory::free(this, m_size); +} + +// ---------------------------------------------------------------------- + +int SystemAllocation::getSize() +{ + return m_size; +} + +// ---------------------------------------------------------------------- + +SystemAllocation * SystemAllocation::getNext() +{ + return m_next; +} + +// ---------------------------------------------------------------------- + +void SystemAllocation::setNext(SystemAllocation * next) +{ + m_next = next; +} + +// ---------------------------------------------------------------------- + +Block * SystemAllocation ::getFirstMemoryBlock() +{ + return reinterpret_cast(reinterpret_cast(this) + cms_blockSize); +} + +// ---------------------------------------------------------------------- + +Block * SystemAllocation ::getLastMemoryBlock() +{ + return reinterpret_cast(reinterpret_cast(this) + m_size - cms_blockSize); +} + +// ====================================================================== + +inline Block * Block::getPrevious() +{ + return m_previous; +} + +// ---------------------------------------------------------------------- + +inline Block const * Block::getPrevious() const +{ + return m_previous; +} + +// ---------------------------------------------------------------------- + +inline void Block::setPrevious(Block *previous) +{ + m_previous = previous; +} + +// ---------------------------------------------------------------------- + +inline Block * Block::getNext() +{ + return m_next; +} + +// ---------------------------------------------------------------------- + +inline Block const * Block::getNext() const +{ + return m_next; +} + +// ---------------------------------------------------------------------- + +inline void Block::setNext(Block *next) +{ + DEBUG_FATAL(next && reinterpret_cast(next) - reinterpret_cast(this) < cms_blockSize, ("too small")); + m_next = next; +} + +// ---------------------------------------------------------------------- + +inline bool Block::isFree() const +{ + return m_free; +} + +// ---------------------------------------------------------------------- + +inline void Block::setFree(bool free) +{ + m_free = free; +} + +// ---------------------------------------------------------------------- + +inline int Block::getSize() const +{ + return reinterpret_cast(m_next) - reinterpret_cast(this); +} + +// ====================================================================== + +#if DO_SCALAR + +inline bool AllocatedBlock::isAllocatedAsArray() const +{ + return m_array; +} + +// ---------------------------------------------------------------------- + +inline void AllocatedBlock::setAllocatedAsArray(bool array) +{ + m_array = array; +} + +#endif + +// ---------------------------------------------------------------------- + +#if DO_TRACK + +inline bool AllocatedBlock::checkForLeaks() const +{ + return m_leakTest; +} + +// ---------------------------------------------------------------------- + +inline void AllocatedBlock::setCheckForLeaks(bool checkForLeaks) +{ + m_leakTest = checkForLeaks; +} + +// ---------------------------------------------------------------------- + +inline uint32 AllocatedBlock::getOwner(int index) const +{ + return m_owner[index]; +} + +// ---------------------------------------------------------------------- + +inline void AllocatedBlock::setOwner(int index, uint32 owner) +{ + m_owner[index] = owner; +} + +// ---------------------------------------------------------------------- + +inline void AllocatedBlock::fillOwnerWithFreePattern() +{ + memset(m_owner, cms_freeFillPattern, sizeof(m_owner)); +} + +#endif + +// ---------------------------------------------------------------------- + +#if DO_TRACK || DO_GUARDS + +inline int AllocatedBlock::getRequestedSize() const +{ + return m_requestedSize; +} + +// ---------------------------------------------------------------------- + +inline void AllocatedBlock::setRequestedSize(int requestedSize) +{ + m_requestedSize = requestedSize; +} + +#endif + +// ====================================================================== + +inline int MemoryManagerNamespace::min(int a, int b) +{ + return (a < b) ? a : b; +} + +// ---------------------------------------------------------------------- + +inline int MemoryManagerNamespace::max(int a, int b) +{ + return (a > b) ? a : b; +} + +// ---------------------------------------------------------------------- + +inline byte const * MemoryManagerNamespace::min(byte const * a, byte const * b) +{ + return (a < b) ? a : b; +} + +// ---------------------------------------------------------------------- + +inline byte const * MemoryManagerNamespace::max(byte const * a, byte const * b) +{ + return (a > b) ? a : b; +} + +// ---------------------------------------------------------------------- +// this is here to get around linkage problems on win32 + +void MemoryManagerNamespace::outputDebugStringWrapper(char const * message) +{ + OutputDebugString(message); +} + +// ---------------------------------------------------------------------- + +void MemoryManager::setLimit(int megabytes, bool hardLimit, bool preallocate) +{ + DEBUG_FATAL(ms_limitSet, ("MemoryManager::setLimit may only be called once")); + ms_limitSet = true; + ms_limitMegabytes = megabytes; + ms_hardLimit = hardLimit; + DEBUG_FATAL(ms_hardLimit && ms_systemMemoryAllocatedMegabytes > ms_limitMegabytes, ("hard limit broken before being set %d/%d", ms_systemMemoryAllocatedMegabytes, ms_limitMegabytes)); + if (preallocate && ms_systemMemoryAllocatedMegabytes < ms_limitMegabytes) + allocateSystemMemory(ms_limitMegabytes - ms_systemMemoryAllocatedMegabytes); +} + +// ---------------------------------------------------------------------- + +int MemoryManager::getLimit() +{ + return ms_limitMegabytes; +} + +// ---------------------------------------------------------------------- + +bool MemoryManager::isHardLimit() +{ + return ms_hardLimit; +} + +// ---------------------------------------------------------------------- +/** + * Install the MemoryManager. + * @param newMaxBytes Maximum number of bytes in the memory pool + * @param newMaxBlocks Number of blocks (free or allocated) + * @param commitAll Commit all the specified ram to the application + */ + +MemoryManager::MemoryManager() +{ + // any allocation will install the memory manager if it has yet to be, so we want to bail safely here + if (ms_installed) + return; + +#if defined(PLATFORM_WIN32) && defined(_DEBUG) + /* + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG); + _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_WNDW); + _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_WNDW); + int crtHeapFlags = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); + crtHeapFlags &= ~_CRTDBG_ALLOC_MEM_DF; + crtHeapFlags &= ~_CRTDBG_CHECK_ALWAYS_DF; + crtHeapFlags &= ~_CRTDBG_CHECK_CRT_DF; + crtHeapFlags &= ~_CRTDBG_DELAY_FREE_MEM_DF; + crtHeapFlags &= ~_CRTDBG_LEAK_CHECK_DF; + _CrtSetDbgFlag(crtHeapFlags); + */ +#endif + + OsMemory::install(); + DebugHelp::install(); + +#if !DISABLED + + // this buffer must remain local to this function, otherwise it will get clobbered. + // the problem is this function gets called before the library gets properly initialized, + // and then when the library gets initialized, it will clear the global memory, thus + // wiping out our mutex. + static char s_criticalSectionBuffer[sizeof(RecursiveMutex)]; + ms_criticalSection = new(s_criticalSectionBuffer) RecursiveMutex; + + ms_reportAllocations = true; + ms_installed = true; +// ms_logEachAlloc = false; + +#endif +} + +// ---------------------------------------------------------------------- +/** + * Remove the MemoryManager. + */ + +MemoryManager::~MemoryManager() +{ +#if DISABLED + return; +#else + +#if PRODUCTION == 0 + ms_allocationsPerFrame.disable(); + ms_bytesAllocatedPerFrame.disable(); + ms_freesPerFrame.disable(); + ms_bytesFreedPerFrame.disable(); +#endif + + DEBUG_FATAL(!ms_installed, ("not installed")); + + ms_criticalSection->enter(); + + DEBUG_REPORT_LOG_PRINT(true, ("MM::remove %lu/%lu=bytes %d/%d=allocs\n", getCurrentNumberOfBytesAllocated(), getMaximumNumberOfBytesAllocated(), getCurrentNumberOfAllocations(), getMaximumNumberOfAllocations())); + DEBUG_OUTPUT_CHANNEL("Foundation\\MemoryManager", ("MM::remove %lu/%lu=bytes %d/%d=allocs\n", getCurrentNumberOfBytesAllocated(), getMaximumNumberOfBytesAllocated(), getCurrentNumberOfAllocations(), getMaximumNumberOfAllocations())); + +#if DO_TRACK + if (!ConfigSharedFoundation::getDemoMode() && ms_allocations && ms_reportAllocations) + { + ms_criticalSection->leave(); + int const leaks = MemoryManagerNamespace::report(true); + ms_criticalSection->enter(); + if (leaks) + REPORT(true, Report::RF_log | Report::RF_dialog, ("%d memory leak%s", leaks, leaks > 1 ? "s" : "")); + } +#endif + + // free all of the allocations from the OS + while (ms_firstSystemAllocation) + { + SystemAllocation * systemAllocation = ms_firstSystemAllocation; + ms_firstSystemAllocation = ms_firstSystemAllocation->getNext(); + systemAllocation->~SystemAllocation(); + } + + ms_criticalSection->leave(); + ms_installed = false; + +#ifdef _DEBUG + DebugMonitor::remove(); +#endif + + ms_criticalSection->~RecursiveMutex(); + +#endif + + DebugHelp::remove(); + OsMemory::remove(); +} + +// ---------------------------------------------------------------------- + +int MemoryManagerNamespace::convertBytesToMegabytesForSystemAllocation(int systemAllocationSize) +{ + if (systemAllocationSize < cms_systemAllocationMinimumSize) + systemAllocationSize = cms_systemAllocationMinimumSize; + + systemAllocationSize = (systemAllocationSize + (cms_systemAllocationRoundSize - 1)) & (~(cms_systemAllocationRoundSize - 1)); + + return systemAllocationSize / (1024 * 1024); +} + +// ---------------------------------------------------------------------- + +void MemoryManagerNamespace::allocateSystemMemory(int megabytes) +{ + if (ms_hardLimit && ms_systemMemoryAllocatedMegabytes + megabytes > ms_limitMegabytes) + { + megabytes = ms_limitMegabytes - ms_systemMemoryAllocatedMegabytes; + if (megabytes <= 0) + return; + } + + // allocate the system memory + size_t systemAllocationSize = static_cast(megabytes) * 1024 * 1024; + void * data = OsMemory::commit(0, systemAllocationSize); + + // failed to allocate virtual memory. there is insufficient virtual memory or address space to satisfy systemAllocationSize. + if (!data) + return; + + // Construct our tracking information + SystemAllocation * systemAllocation = new(data) SystemAllocation(systemAllocationSize); + ++ms_numberOfSystemAllocations; + ms_systemMemoryAllocatedMegabytes += megabytes; + + // insert the memory into the sorted linked list of system allocations + SystemAllocation * back = NULL; + SystemAllocation * front = ms_firstSystemAllocation; + for ( ; front && front->getFirstMemoryBlock() < systemAllocation->getFirstMemoryBlock(); back = front, front = front->getNext()) + {} + if (back) + back->setNext(systemAllocation); + else + ms_firstSystemAllocation = systemAllocation; + systemAllocation->setNext(front); +} + +// ---------------------------------------------------------------------- + +unsigned long MemoryManager::getCurrentNumberOfBytesAllocated(const int processId) +{ + UNREF(processId); + return ms_currentBytesAllocated; +} + +// ---------------------------------------------------------------------- + +unsigned long MemoryManager::getCurrentNumberOfBytesAllocatedNoLeakTest() +{ +#if DO_TRACK + return ms_currentBytesAllocatedNoLeakTest; +#else + return 0; +#endif +} + +// ---------------------------------------------------------------------- + +unsigned long MemoryManager::getMaximumNumberOfBytesAllocated() +{ + return ms_maxBytesAllocated; +} + +// ---------------------------------------------------------------------- + +int MemoryManager::getSystemMemoryAllocatedMegabytes() +{ + return ms_systemMemoryAllocatedMegabytes; +} + +// ---------------------------------------------------------------------- + +int MemoryManager::getCurrentNumberOfAllocations() +{ + return ms_allocations; +} + +// ---------------------------------------------------------------------- + +int MemoryManager::getMaximumNumberOfAllocations() +{ + return ms_maxAllocations; +} + +// ---------------------------------------------------------------------- + +void MemoryManager::setReportAllocations(bool reportAllocations) +{ + ms_reportAllocations = reportAllocations; +} + +// ---------------------------------------------------------------------- + +void MemoryManager::registerDebugFlags() +{ +#if PRODUCTION == 0 + ms_allocationsPerFrame.bindToCounter("MemoryManagerAllocateCount"); + ms_bytesAllocatedPerFrame.bindToCounter("MemoryManagerAllocateBytes"); + ms_freesPerFrame.bindToCounter("MemoryManagerFreeCount"); + ms_bytesFreedPerFrame.bindToCounter("MemoryManagerFreeBytes"); +#endif + +#ifdef _DEBUG + DebugFlags::registerFlag(ms_debugReportFlag, "SharedMemoryManager", "reportMemory", debugReport); + DebugFlags::registerFlag(ms_debugReportMapFlag, "SharedMemoryManager", "reportMemoryMap", debugReportMap); + DebugFlags::registerFlag(ms_debugReportAllocations, "SharedMemoryManager", "reportMemoryAllocations"); + DebugFlags::registerFlag(ms_debugReportLogMemoryAllocFreePointers, "SharedMemoryManager", "reportLogMemoryAllocFreePointers"); + DebugFlags::registerFlag(ms_debugLogAllocations, "SharedMemoryManager", "reportLogAllocations"); + DebugFlags::registerFlag(ms_debugLogAllocationsNextFrame, "SharedMemoryManager", "reportLogAllocationsNextFrame", logAllocationsNextFrame); + DebugFlags::registerFlag(ms_debugVerifyGuardPatterns, "SharedMemoryManager", "verifyGuardPatterns"); + DebugFlags::registerFlag(ms_debugVerifyFreePatterns, "SharedMemoryManager", "verifyFreePatterns"); + DebugFlags::registerFlag(ms_debugProfileAllocate, "SharedMemoryManager", "profileAllocate"); +#endif +} + +// ---------------------------------------------------------------------- +/** + * Dump debugging information about the MemoryManager. + */ + +void MemoryManager::debugReport() +{ +#if !DISABLED + DEBUG_FATAL(!ms_installed, ("not installed")); + DEBUG_REPORT_PRINT(ms_limitSet, ("MM: %9dmb (%s limit)\n", ms_limitMegabytes, ms_hardLimit ? "hard" : "soft")); + DEBUG_REPORT_PRINT(true, ("MM: %9d/%9d/%9d cur/max/tot allocs\n", ms_allocations, ms_maxAllocations, ms_allocateCalls)); + DEBUG_REPORT_PRINT(true, ("MM: %9lu/%9lu/%9lu cur/max/tot bytes\n", ms_currentBytesAllocated, ms_maxBytesAllocated, ms_allocateBytesTotal)); +#endif +} + +// ---------------------------------------------------------------------- + +#ifdef _DEBUG +void MemoryManagerNamespace::logAllocationsNextFrame() +{ + if (!ms_debugLogAllocationsNextFrameStarted) + { + REPORT_LOG_PRINT(true, ("MemoryManager::logAllocationsNextFrame start\n")); + ms_debugLogAllocations = true; + ms_debugLogAllocationsNextFrameStarted = true; + } + else + { + REPORT_LOG_PRINT(true, ("MemoryManager::logAllocationsNextFrame end\n")); + ms_debugLogAllocations = false; + ms_debugLogAllocationsNextFrameStarted = false; + ms_debugLogAllocationsNextFrame = false; + } +} +#endif + +// ---------------------------------------------------------------------- + +void MemoryManagerNamespace::emitCharacters(char * & buffer, int blockSize, int & carryOverFree, int & carryOverUsed, int newFree, int newUsed, char const * const bufferOverrunAddress) +{ + while (carryOverFree + carryOverUsed + newFree + newUsed >= blockSize) + { + int const absorbNew = blockSize - (carryOverUsed + carryOverFree); + if (newFree) + { + carryOverFree += absorbNew; + newFree -= absorbNew; + } + else + { + carryOverUsed += absorbNew; + newUsed -= absorbNew; + } + + if (carryOverFree == blockSize) + *buffer = '_'; + else + if (carryOverUsed == blockSize) + *buffer = '*'; + else + { + int const letter = (carryOverUsed * 26) / blockSize; + *buffer = static_cast('A' + letter); + } + ++buffer; + + FATAL(buffer == bufferOverrunAddress, ("Buffer overrun! 0x%0X", buffer)); + + carryOverFree = 0; + carryOverUsed = 0; + } + + carryOverFree += newFree; + carryOverUsed += newUsed; +} + +// ---------------------------------------------------------------------- +/** + * Dump an image of the memory state to the report system. + */ + +void MemoryManager::debugReportMap() +{ + for (SystemAllocation * systemAllocation = ms_firstSystemAllocation; systemAllocation; systemAllocation = systemAllocation->getNext()) + { + char buffer[128]; + int const numberOfCharactersPerRow = 64; + + snprintf(buffer, sizeof(buffer)-1, "%8p %3dmb : ", systemAllocation, systemAllocation->getSize() / (1024 * 1024)); + buffer[sizeof(buffer)-1] = '\0'; + + int const headerLength = strlen(buffer); + int const characterSize = systemAllocation->getSize() / numberOfCharactersPerRow; + + int carryOverUsed = 0; + int carryOverFree = 0; + char * b = buffer + headerLength; + char const * const bufferOverrunAddress = buffer + sizeof(buffer); + for (Block * block = systemAllocation->getFirstMemoryBlock()->getNext(); block != systemAllocation->getLastMemoryBlock(); block = block->getNext()) + { + if (block->isFree()) + emitCharacters(b, characterSize, carryOverFree, carryOverUsed, block->getSize(), 0, bufferOverrunAddress); + else + emitCharacters(b, characterSize, carryOverFree, carryOverUsed, 0, block->getSize(), bufferOverrunAddress); + } + + if (carryOverFree || carryOverUsed) + emitCharacters(b, carryOverFree + carryOverUsed, carryOverFree, carryOverUsed, 0, 0, bufferOverrunAddress); + + b[0] = '\0'; + + DEBUG_REPORT_PRINT(true, ("%s\n", buffer)); + } +} + +// ---------------------------------------------------------------------- +/** + * Add a block to the free memory chain list. + * + * @param block Block of memory that is being + */ + +void MemoryManagerNamespace::addToFreeList(Block * block) +{ + NOT_NULL(block); + + // @todo consider making the ternary search tree balanced + + FreeBlock * freeBlock = static_cast(block); + int const freeBlockSize = freeBlock->getSize(); + FreeBlock * parent = NULL; + FreeBlock * * next = &ms_firstFreeBlock; + FreeBlock * same = 0; + + for (FreeBlock * current = ms_firstFreeBlock; current; parent = current, current = *next) + { + int const currentBlockSize = current->getSize(); + if (freeBlockSize < currentBlockSize) + next = ¤t->m_smallerFreeBlock; + else + if (freeBlockSize > currentBlockSize) + next = ¤t->m_largerFreeBlock; + else + { + // quick out for same size blocks + same = current; + break; + } + } + + if (same) + { + + freeBlock->m_smallerFreeBlock = same->m_smallerFreeBlock; + same->m_smallerFreeBlock = NULL; + if (freeBlock->m_smallerFreeBlock) + freeBlock->m_smallerFreeBlock->m_parentFreeBlock = freeBlock; + + freeBlock->m_sameFreeBlock = same; + same->m_parentFreeBlock = freeBlock; + + freeBlock->m_largerFreeBlock = same->m_largerFreeBlock; + same->m_largerFreeBlock = NULL; + if (freeBlock->m_largerFreeBlock) + freeBlock->m_largerFreeBlock->m_parentFreeBlock = freeBlock; + + *next = freeBlock; + freeBlock->m_parentFreeBlock = parent; + } + else + { + *next = freeBlock; + freeBlock->m_smallerFreeBlock = NULL; + freeBlock->m_sameFreeBlock = NULL; + freeBlock->m_largerFreeBlock = NULL; + freeBlock->m_parentFreeBlock = parent; + } + + if (++ms_freeBlocks > ms_maxFreeBlocks) + ms_maxFreeBlocks = ms_freeBlocks; + +#ifdef _DEBUG + int treeHeight = 0; + while (freeBlock) + { + ++treeHeight; + freeBlock = freeBlock->m_parentFreeBlock; + } + if (treeHeight > ms_maxTreeHeight) + ms_maxTreeHeight = treeHeight; +#endif +} + +// ---------------------------------------------------------------------- +/** + * Remove a block from the free memory chain list. + * + * @param block Block of memory that is being allocated + */ + +void MemoryManagerNamespace::removeFromFreeList(FreeBlock * block) +{ + NOT_NULL(block); + + // find the pointer that points to block + FreeBlock * * parentPointer = NULL; + FreeBlock * parent = block->m_parentFreeBlock; + if (parent) + { + if (parent->m_smallerFreeBlock == block) + parentPointer = &parent->m_smallerFreeBlock; + else + if (parent->m_sameFreeBlock == block) + parentPointer = &parent->m_sameFreeBlock; + else + { + DEBUG_FATAL(parent->m_largerFreeBlock != block, ("parent doesn't know about child")); + parentPointer = &parent->m_largerFreeBlock; + } + } + else + { + DEBUG_FATAL(ms_firstFreeBlock != block, ("no parent but not first")); + parentPointer = &ms_firstFreeBlock; + } + + // now we need to replace our node with one from underneath us + FreeBlock * same = block->m_sameFreeBlock; + if (same) + { + // making use of the same size block is pretty easy and doesn't disturb tree balance so its our prefered choice + same->m_smallerFreeBlock = block->m_smallerFreeBlock; + if (same->m_smallerFreeBlock) + same->m_smallerFreeBlock->m_parentFreeBlock = same; + + same->m_largerFreeBlock = block->m_largerFreeBlock; + if (same->m_largerFreeBlock) + same->m_largerFreeBlock->m_parentFreeBlock = same; + + same->m_parentFreeBlock = parent; + + *parentPointer = same; + } + else + { + if (block->m_smallerFreeBlock) + { + if (block->m_largerFreeBlock) + { + // this is the worst case. this free block has smaller and larger children, but not any same sized children + // we're going to take the smallest block off the larger list and use that to replace the current node + FreeBlock * back = NULL; + FreeBlock * replacement = block->m_largerFreeBlock; + while (replacement->m_smallerFreeBlock) + { + back = replacement; + replacement = replacement->m_smallerFreeBlock; + } + + // disconnect the link between its parent and out replacement block + if (back) + { + back->m_smallerFreeBlock = replacement->m_largerFreeBlock; + if (back->m_smallerFreeBlock) + back->m_smallerFreeBlock->m_parentFreeBlock = back; + + replacement->m_largerFreeBlock = block->m_largerFreeBlock; + replacement->m_largerFreeBlock->m_parentFreeBlock = replacement; + } + + // put the replacement block in its new place + replacement->m_smallerFreeBlock = block->m_smallerFreeBlock; + replacement->m_smallerFreeBlock->m_parentFreeBlock = replacement; + + replacement->m_parentFreeBlock = parent; + + *parentPointer = replacement; + } + else + { + // this block only has one child, which is smaller. an easy case. + block->m_smallerFreeBlock->m_parentFreeBlock = parent; + *parentPointer = block->m_smallerFreeBlock; + } + } + else + { + if (block->m_largerFreeBlock) + { + // this block only has one child, which is larger. an easy case. + block->m_largerFreeBlock->m_parentFreeBlock = parent; + *parentPointer = block->m_largerFreeBlock; + } + else + { + // this block has no children + *parentPointer = NULL; + } + } + } + + // remove all the pointers the block may have had + block->m_smallerFreeBlock = NULL; + block->m_sameFreeBlock = NULL; + block->m_largerFreeBlock = NULL; + block->m_parentFreeBlock = NULL; + + --ms_freeBlocks; +} + +// ---------------------------------------------------------------------- + +FreeBlock *MemoryManagerNamespace::searchFreeList(int blockSize) +{ + FreeBlock * result = NULL; + FreeBlock * current = ms_firstFreeBlock; + while (current) + { + int const currentBlockSize = current->getSize(); + + if (currentBlockSize < blockSize) + { + // this block is too small so check blocks larger than this + current = current->m_largerFreeBlock; + } + else + if (currentBlockSize > blockSize) + { + // this block is too large but will be the best fit so far. continue checking smaller blocks. + result = current; + current = current->m_smallerFreeBlock; + } + else + { + // this block is a perfect fit, return it immediately. + return current; + } + } + + return result; +} + +// ---------------------------------------------------------------------- +/** + * Dynamically allocate memory. + * + * Users should not call this routine directly. It should only be called + * by operator new. + * + * @param size Number of bytes to allocate + * @param owner Line number on which the allocation was made + * @param array True if the array form of operator new was used, false if the scalar form was used + */ + +void * MemoryManager::allocate(size_t size, uint32 owner, bool array, bool leakTest) +{ + if (!ms_installed) + new(ms_memoryManagerBuffer) MemoryManager; + + UNREF(owner); + UNREF(array); + UNREF(leakTest); + +#if DISABLED + return operator new(size); +#else + + DEBUG_FATAL(!ms_installed, ("not installed")); + +#if PRODUCTION == 0 + ++ms_allocationsPerFrame; + ms_bytesAllocatedPerFrame += size; +#endif + +#ifdef _DEBUG + if (ms_debugProfileAllocate) + PROFILER_BLOCK_ENTER(ms_allocateProfilerBlock); + + if (ms_debugVerifyGuardPatterns || ms_debugVerifyFreePatterns) + verify(ms_debugVerifyGuardPatterns, ms_debugVerifyFreePatterns); + + if (ms_debugReportAllocations || ms_debugLogAllocations) + { + char libName[256]; + char fileName[256]; + int line; + if (ms_allowNameLookup && DebugHelp::lookupAddress(owner, libName, fileName, sizeof(fileName), line)) + { + DEBUG_REPORT(true, (ms_debugReportAllocations ? Report::RF_print : 0) | (ms_debugLogAllocations ? Report::RF_log : 0), ("%s(%d): alloc %d=bytes %d=array\n", fileName, line, size, static_cast(array))); + } + else + { + DEBUG_REPORT(true, (ms_debugReportAllocations ? Report::RF_print : 0) | (ms_debugLogAllocations ? Report::RF_log : 0), ("%08x: alloc %d=bytes %d=array\n", static_cast(owner), size, static_cast(array))); + } + } +#endif + + ms_criticalSection->enter(); + + // get the size of the allocation + int allocSize = (cms_allocatedBlockSize + cms_guardBandSize + (size ? static_cast(size) : 1) + cms_guardBandSize + 15) & ~15; + + FreeBlock * bestFreeBlock = NULL; + for (int tries = 0; !bestFreeBlock && tries < 2; ++tries) + { + bestFreeBlock = searchFreeList(allocSize); + + // if the memory allocation failed, try to get some more memory + if (!bestFreeBlock) + allocateSystemMemory(convertBytesToMegabytesForSystemAllocation(cms_blockSize + cms_blockSize + allocSize + cms_blockSize)); + } + + // make sure memory was available + if (!bestFreeBlock) + { + if (ConfigSharedFoundation::getMemoryManagerReportOnOutOfMemory()) + { + // avoid deadlock from FATAL calling free + (*LogMessage)("Out of memory, dumping current allocations:\n"); + IGNORE_RETURN(MemoryManagerNamespace::report(false)); + } + + ms_criticalSection->leave(); + FATAL(true, ("failed allocation attempt for %d (%d actual)", allocSize, size)); + } + + removeFromFreeList(bestFreeBlock); + + // setup the allocation record + bestFreeBlock->setFree(false); + + // check to see if we should subdivide this block + if (bestFreeBlock->getSize() > (allocSize + cms_allocatedBlockSize + cms_guardBandSize + 1 + cms_guardBandSize)) + { + Block *block = reinterpret_cast(reinterpret_cast(bestFreeBlock) + allocSize); + block->setPrevious(bestFreeBlock); + block->setNext(bestFreeBlock->getNext()); + block->setFree(true); + + bestFreeBlock->getNext()->setPrevious(block); + bestFreeBlock->setNext(block); + + addToFreeList(block); + } + + AllocatedBlock * best = reinterpret_cast(bestFreeBlock); + +#if DO_SCALAR + best->setAllocatedAsArray(array); +#endif + +#if DO_TRACK + best->setCheckForLeaks(leakTest); + best->setOwner(0, owner); +#endif +#if DO_TRACK > 1 + { + enum { OFFSET = 3 }; + uint32 owners[DO_TRACK + OFFSET]; + DebugHelp::getCallStack(owners, DO_TRACK + OFFSET); + + for (int i = 1; i < DO_TRACK; ++i) + { + best->setOwner(i, owners[i + OFFSET]); + +#ifdef _DEBUG + if (ms_debugReportAllocations || ms_debugLogAllocations) + { + char libName[256]; + char fileName[256]; + int line; + if (ms_allowNameLookup && DebugHelp::lookupAddress(owners[i + OFFSET], libName, fileName, sizeof(fileName), line)) + { + DEBUG_REPORT(true, (ms_debugReportAllocations ? Report::RF_print : 0) | (ms_debugLogAllocations ? Report::RF_log : 0), (" %s(%d): caller %d\n", fileName, line, i)); + } + else + { + DEBUG_REPORT(true, (ms_debugReportAllocations ? Report::RF_print : 0) | (ms_debugLogAllocations ? Report::RF_log : 0), (" %08x: caller %d\n", static_cast(owners[i + OFFSET]), i)); + } + } +#endif + } + } +#endif +#if DO_TRACK || DO_GUARDS + best->setRequestedSize(static_cast(size)); + ms_currentBytesRequested += size; + DEBUG_FATAL(best->getRequestedSize() != static_cast(size), ("allocated more memory at once than the memory manager supports (%d)", (1 << Block::cms_requestedSizeBits) - 1)); +#endif + + // update the size of the allocation because our block may not have been large enough to subdivide + allocSize = best->getSize(); + + // update the number of bytes allocated + ++ms_allocateCalls; + ms_allocateBytesTotal += allocSize; + ms_currentBytesAllocated += allocSize; + +#if DO_TRACK + if (!leakTest) + ms_currentBytesAllocatedNoLeakTest += allocSize; +#endif + + if (++ms_allocations > ms_maxAllocations) + ms_maxAllocations = ms_allocations; + if (ms_currentBytesAllocated > ms_maxBytesAllocated) + ms_maxBytesAllocated = ms_currentBytesAllocated; + + // get another pointer to the memory we allocated so we can tinker with it + byte * memory = reinterpret_cast(best) + cms_allocatedBlockSize + cms_guardBandSize; + +#if DO_GUARDS + // fill the prefix guard band + memset(memory - cms_guardBandSize, cms_guardFillPattern, cms_guardBandSize); +#endif + + // fill the user memory with the initialize pattern +#if DO_INITIALIZE_FILLS + memset(memory, cms_initializeFillPattern, size); +#endif + +#if DO_GUARDS + // fill the suffix guard band + memset(memory+size, cms_guardFillPattern, cms_guardBandSize); +#endif + + ms_criticalSection->leave(); + + DEBUG_REPORT_LOG_PRINT(ms_debugReportLogMemoryAllocFreePointers, ("MM::alloc %08x\n", reinterpret_cast(memory))); + +#ifdef _DEBUG + if (ms_debugProfileAllocate) + PROFILER_BLOCK_LEAVE(ms_allocateProfilerBlock); +#endif + +// DEBUG_REPORT_LOG(ms_logEachAlloc, ("MemoryManager::allocate() requested_size=%d, alloc_size=%d, ptr=%p\n", size, allocSize, memory)); + + return memory; +#endif +} + +// ---------------------------------------------------------------------- + +void *MemoryManager::reallocate(void *userPointer, size_t newSize) +{ + AllocatedBlock *allocatedBlock = 0; + bool array = false; + int oldSize = 0; + + if (userPointer) + { + allocatedBlock = reinterpret_cast(reinterpret_cast(userPointer) - (cms_allocatedBlockSize + cms_guardBandSize)); + #if DO_SCALAR + array = allocatedBlock->isAllocatedAsArray(); + #endif + if (!newSize) + { + MemoryManager::free(userPointer, array); + +// DEBUG_REPORT_LOG(ms_logEachAlloc, ("MemoryManager::reallocate() new_requested_size=%d, org ptr=%p, new ptr=NULL\n", newSize, userPointer)); + + return 0; + } + #if DO_TRACK || DO_GUARDS + oldSize = allocatedBlock->getRequestedSize(); + #else + oldSize = allocatedBlock->getSize()-cms_allocatedBlockSize; + #endif + } + + if (newSize <= static_cast(oldSize)) + { +// DEBUG_REPORT_LOG(ms_logEachAlloc, ("MemoryManager::reallocate() new_requested_size=%d, org ptr=%p, new ptr=%p\n", newSize, userPointer, userPointer)); + + return userPointer; + } + +#if DO_TRACK + uint32 owner = allocatedBlock->getOwner(0); + bool leakTest = allocatedBlock->checkForLeaks(); +#else + uint32 owner = 0; + bool leakTest = false; +#endif + + void *newPointer = allocate(newSize, owner, array, leakTest); + if (oldSize) + memcpy(newPointer, userPointer, oldSize); + if (userPointer) + MemoryManager::free(userPointer, array); + +// DEBUG_REPORT_LOG(ms_logEachAlloc, ("MemoryManager::reallocate() new_requested_size=%d, org ptr=%p, new ptr=%p\n", newSize, userPointer, newPointer)); + + return newPointer; +} + +// ---------------------------------------------------------------------- +/** + * Free dynamically allocated memory. + * + * Users should not call this routine directly. It should only be called + * by operator delete. + * + * This routine should not be called with the NULL pointer. + * + * @param userPointer Pointer to the memory + * @param array True if the array form of operator new was used, false if the scalar form was used + */ + +void MemoryManager::free(void * userPointer, bool array) +{ +#if DISABLED + UNREF(array); + operator delete(userPointer); + return; +#else + + DEBUG_FATAL(!ms_installed, ("not installed")); + NOT_NULL(userPointer); + +#ifdef _DEBUG + if (ms_debugVerifyGuardPatterns || ms_debugVerifyFreePatterns) + verify(ms_debugVerifyGuardPatterns, ms_debugVerifyFreePatterns); +#endif + + DEBUG_REPORT_LOG_PRINT(ms_debugReportLogMemoryAllocFreePointers, ("MM::free %08x\n", reinterpret_cast(userPointer))); + + UNREF(array); + + ms_criticalSection->enter(); + + AllocatedBlock * allocatedBlock = reinterpret_cast(reinterpret_cast(userPointer) - (cms_allocatedBlockSize + cms_guardBandSize)); + +#if DEBUG_LEVEL == DEBUG_LEVEL_DEBUG + DEBUG_FATAL(allocatedBlock->getNext()->getPrevious() != allocatedBlock, ("Bad free (1) %p", userPointer)); + DEBUG_FATAL(allocatedBlock->getPrevious()->getNext() != allocatedBlock, ("Bad free (2) %p", userPointer)); + DEBUG_FATAL(allocatedBlock->isFree(), ("Freeing already free block %p", userPointer)); +#endif + +#if PRODUCTION == 0 + ++ms_freesPerFrame; +#if DO_TRACK + ms_bytesFreedPerFrame += allocatedBlock->getRequestedSize(); +#endif +#endif + +#if DO_GUARDS + { + // verify the guard bands + byte * guard = reinterpret_cast(allocatedBlock) + cms_allocatedBlockSize; + bool corrupt = false; + + // check the prefix guard band + for (int i = 0; i < cms_guardBandSize; ++i, ++guard) + if (*guard != cms_guardFillPattern) + { + corrupt = true; + DEBUG_REPORT_LOG_PRINT(true, ("MemoryManager::free corrupted guard prefix at position %3d = %02x\n", i - cms_guardBandSize, static_cast(*guard))); + DEBUG_OUTPUT_CHANNEL("Foundation\\MemoryManager", ("MemoryManager::free corrupted guard prefix at position %3d = %02x\n", i - cms_guardBandSize, static_cast(*guard))); + } + + // advance past the user memory + guard += allocatedBlock->getRequestedSize(); + + // check the suffix guard band + for (int j = 0; j < cms_guardBandSize; ++j, ++guard) + if (*guard != cms_guardFillPattern) + { + corrupt = true; + DEBUG_REPORT_LOG_PRINT(true, ("MemoryManager::free corrupted guard suffix at position %3d = %02x\n", j, static_cast(*guard))); + DEBUG_OUTPUT_CHANNEL("Foundation\\MemoryManager", ("MemoryManager::free corrupted guard suffix at position %3d = %02x\n", j, static_cast(*guard))); + } + + if (corrupt) + { + MemoryManagerNamespace::report(allocatedBlock, false); + ms_criticalSection->leave(); + DEBUG_FATAL(true, ("corrupted guard pattern")); + return; //lint !e527 // Unreachable + } + } +#endif + +#if DO_SCALAR + if (allocatedBlock->isAllocatedAsArray() != array) //lint !e731 // Info -- Boolean argument to equal/not equal + { + MemoryManagerNamespace::report(allocatedBlock, false); + #ifdef _DEBUG + bool const blockArray = allocatedBlock->isAllocatedAsArray(); + ms_criticalSection->leave(); + FATAL(true, ("allocated %s deleted %s", blockArray ? "array" : "scalar", array ? "array" : "scalar")); //lint !e731 // Info -- Boolean argument to equal/not equal + #endif + } +#endif + + int const memorySize = allocatedBlock->getSize(); + + // wipe the user memory +#if DO_FREE_FILLS + imemset(reinterpret_cast(allocatedBlock) + cms_allocatedBlockSize, cms_freeFillPattern, memorySize - cms_allocatedBlockSize); +#endif + + allocatedBlock->setFree(true); +#if DO_SCALAR + allocatedBlock->setAllocatedAsArray(false); +#endif + + // clear out the block records +#if DO_TRACK + allocatedBlock->fillOwnerWithFreePattern(); +#endif + +#if DO_TRACK || DO_GUARDS + ms_currentBytesRequested -= allocatedBlock->getRequestedSize(); + allocatedBlock->setRequestedSize(0); + DEBUG_FATAL(allocatedBlock->getRequestedSize() != 0, ("bad size")); +#endif + + // update the number of bytes allocated + ++ms_freeCalls; + --ms_allocations; + + DEBUG_FATAL((ms_currentBytesAllocated < static_cast(memorySize)), ("currentBytesAllocated underflow")); + ms_currentBytesAllocated -= memorySize; +#if DO_TRACK + if (!allocatedBlock->checkForLeaks()) + ms_currentBytesAllocatedNoLeakTest -= memorySize; +#endif + DEBUG_FATAL(ms_allocations < 0, ("allocations underflow")); + +// DEBUG_REPORT_LOG(ms_logEachAlloc, ("MemoryManager::free() requested_size=%d, alloc_size=%d, userPointer=%p, allocatedBlock=%p\n", requestedSize, memorySize, userPointer, allocatedBlock)); + + Block *block = allocatedBlock; + + // recombine with the previous block + if (block->getPrevious()->isFree()) + { + FreeBlock * const previous = static_cast(block->getPrevious()); + removeFromFreeList(previous); + previous->setNext(block->getNext()); + block->getNext()->setPrevious(previous); + +#if DO_FREE_FILLS + memset(block, cms_freeFillPattern, cms_freeBlockSize); +#endif + block = previous; + } + + // recombine with the following block + if (block->getNext()->isFree()) + { + FreeBlock * const next = static_cast(block->getNext()); + removeFromFreeList(next); + block->setNext(next->getNext()); + next->getNext()->setPrevious(block); + +#if DO_FREE_FILLS + memset(next, cms_freeFillPattern, cms_freeBlockSize); +#endif + } + + addToFreeList(block); + + ms_criticalSection->leave(); +#endif +} + +// ---------------------------------------------------------------------- + +int MemoryManagerNamespace::quickGuessIsValidPointerFromHeap(void const * userPointer) +{ + int result = 0; + AllocatedBlock const * allocatedBlock = reinterpret_cast(reinterpret_cast(userPointer) - (cms_allocatedBlockSize + cms_guardBandSize)); + + ms_criticalSection->enter(); + if (!result && allocatedBlock->getNext()->getPrevious() != allocatedBlock) result = 1; + if (!result && allocatedBlock->getPrevious()->getNext() != allocatedBlock) result = 2; + if (!result && allocatedBlock->isFree()) result = 3; + ms_criticalSection->leave(); + + return result; +} + +// ---------------------------------------------------------------------- + +int MemoryManagerNamespace::authoritativeIsValidPointerFromHeap(void const * userPointer) +{ + ms_criticalSection->enter(); + + int result = quickGuessIsValidPointerFromHeap(userPointer); + if (result != 0) + return result; + + result = 16; + for (SystemAllocation * systemAllocation = ms_firstSystemAllocation; systemAllocation; systemAllocation = systemAllocation->getNext()) + for (Block * block = systemAllocation->getFirstMemoryBlock()->getNext(); result != 0 && block != systemAllocation->getLastMemoryBlock(); block = block->getNext()) + if (!block->isFree()) + { + byte const * memory = reinterpret_cast(block) + cms_allocatedBlockSize + cms_guardBandSize; + if (memory == userPointer) + result = 0; + } + + ms_criticalSection->leave(); + + return result; +} + +// ---------------------------------------------------------------------- +/** + * Take ownership for a block of memory. + * + * @param userPointer Memory to take ownership of + */ + +void MemoryManager::own(void * userPointer) +{ + UNREF(userPointer); + +#if DISABLED || !DO_TRACK + return; +#else + + DEBUG_FATAL(!ms_installed, ("not installed")); + + if (!userPointer) + return; + + ms_criticalSection->enter(); + + // fetch the block pointer + AllocatedBlock * block = reinterpret_cast(reinterpret_cast(userPointer) - (cms_allocatedBlockSize + cms_guardBandSize)); + DEBUG_FATAL(block->isFree(), ("cannot own a free block")); + + // update the owners + { + enum { OFFSET = 2 }; + uint32 owners[DO_TRACK + OFFSET]; + DebugHelp::getCallStack(owners, DO_TRACK + OFFSET); + + for (int i = 0; i < DO_TRACK; ++i) + block->setOwner(i, owners[i + OFFSET]); + } + + ms_criticalSection->leave(); +#endif +} + +// ---------------------------------------------------------------------- + +void MemoryManager::verify(bool guardPatterns, bool freePatterns) +{ + UNREF(guardPatterns); + UNREF(freePatterns); + +#if DO_FREE_FILLS || DO_GUARDS + + ms_criticalSection->enter(); + + // search for the memory pointer + for (SystemAllocation * systemAllocation = ms_firstSystemAllocation; systemAllocation; systemAllocation = systemAllocation->getNext()) + for (Block * block = systemAllocation->getFirstMemoryBlock()->getNext(); block != systemAllocation->getLastMemoryBlock(); block = block->getNext()) + if (block->isFree()) + { +#if DO_FREE_FILLS + if (freePatterns) + { + bool corrupt = false; + byte * memory = reinterpret_cast(block) + cms_freeBlockSize; + int freeSize = block->getSize() - cms_freeBlockSize; + + for (int i = 0; i < freeSize; ++i, ++memory) + if (*memory != cms_freeFillPattern) + { + corrupt = true; + DEBUG_REPORT_LOG_PRINT(true, ("corrupted free pattern at position %3d [membase=0x%x, memaddr=0x%x] = %02x\n", i, reinterpret_cast(reinterpret_cast(block) + cms_freeBlockSize), reinterpret_cast(reinterpret_cast(block) + cms_freeBlockSize + i), static_cast(*memory))); + DEBUG_OUTPUT_CHANNEL("Foundation\\MemoryManager", ("corrupted free pattern at position %3d = %02x\n", i, static_cast(*memory))); + } + + if (corrupt) + { + ms_criticalSection->leave(); + DEBUG_FATAL(true, ("corrupted free pattern")); + } + } +#endif + } + else + if (guardPatterns) + { +#if DO_GUARDS + // verify the guard bands + byte * guard = reinterpret_cast(block) + cms_allocatedBlockSize; + bool corrupt = false; + + // check the prefix guard band + for (int i = 0; i < cms_guardBandSize; ++i, ++guard) + if (*guard != cms_guardFillPattern) + { + corrupt = true; + DEBUG_REPORT_LOG_PRINT(true, ("corrupted guard prefix at position %3d = %02x\n", i - cms_guardBandSize, static_cast(*guard))); + DEBUG_OUTPUT_CHANNEL("Foundation\\MemoryManager", ("corrupted guard prefix at position %3d = %02x\n", i - cms_guardBandSize, static_cast(*guard))); + } + + // advance past the user memory + guard += static_cast(block)->getRequestedSize(); + + // check the suffix guard band + for (int j = 0; j < cms_guardBandSize; ++j, ++guard) + if (*guard != cms_guardFillPattern) + { + corrupt = true; + DEBUG_REPORT_LOG_PRINT(true, ("corrupted guard suffix at position %3d = %02x\n", j, static_cast(*guard))); + DEBUG_OUTPUT_CHANNEL("Foundation\\MemoryManager", ("corrupted guard suffix at position %3d = %02x\n", j, static_cast(*guard))); + } + + if (corrupt) + { + MemoryManagerNamespace::report(static_cast(block), false); + ms_criticalSection->leave(); + DEBUG_FATAL(true, ("corrupted guard pattern")); + } +#endif + } + + ms_criticalSection->leave(); + +#endif +} + +// ---------------------------------------------------------------------- +/** + * @internal + */ + +void MemoryManagerNamespace::report(AllocatedBlock const * block, bool leak) +{ +#if DO_TRACK + uint32 const owner = block->getOwner(0); +#else + uint32 const owner = 0; +#endif +#if DO_TRACK || DO_GUARDS + int const requestedSize = block->getRequestedSize();; +#else + int const requestedSize = 0; +#endif + + char buffer[256]; + char libName[256]; + char fileName[256]; + int line = 0; + int const memory = reinterpret_cast(reinterpret_cast(block) + cms_allocatedBlockSize + cms_guardBandSize); + + if (ms_allowNameLookup && DebugHelp::lookupAddress(owner, libName, fileName, sizeof(fileName), line)) + { + sprintf(buffer, "%s(%d) : %08X memory %s, %d bytes\n", fileName, line, memory, leak ? "leak" : "allocation", static_cast(requestedSize)); + } + else + { + sprintf(buffer, "unknown(0x%08X) : %08X memory %s, %d bytes\n", static_cast(owner), memory, leak ? "leak" : "allocation", static_cast(requestedSize)); + } + + (*LogMessage)(buffer); + +#if DO_TRACK > 1 + { + for (int i = 1; i < DO_TRACK; ++i) + if (block->getOwner(i)) + { + if (ms_allowNameLookup && DebugHelp::lookupAddress(block->getOwner(i), libName, fileName, sizeof(fileName), line)) + sprintf(buffer, " %s(%d) : caller %d\n", fileName, line, i); + else + sprintf(buffer, " 0x%08X : caller %d\n", static_cast(block->getOwner(i)), i); + (*LogMessage)(buffer); + } + } +#endif +} + +// ---------------------------------------------------------------------- +/** + * Dump information about all outstanding allocations. + */ + +int MemoryManagerNamespace::report(bool leak) +{ + DEBUG_FATAL(!ms_installed, ("not installed")); + + int count = 0; + ms_criticalSection->enter(); + + // search for the memory pointer + for (SystemAllocation * systemAllocation = ms_firstSystemAllocation; systemAllocation; systemAllocation = systemAllocation->getNext()) + for (Block * block = systemAllocation->getFirstMemoryBlock()->getNext(); block != systemAllocation->getLastMemoryBlock(); block = block->getNext()) + if (!block->isFree()) + { +#if DO_TRACK + if (!leak || static_cast(block)->checkForLeaks()) +#else + if (!leak) +#endif + { + report(static_cast(block), leak); + ++count; + } + } + + ms_criticalSection->leave(); + + return count; +} + +// ---------------------------------------------------------------------- +/** + * Dump information about all outstanding allocations. + */ + +void MemoryManager::report() +{ + IGNORE_RETURN(MemoryManagerNamespace::report(false)); +} + +//----------------------------------------------------------------------- + +void MemoryManagerNamespace::logMessageToFd(char const *message) +{ + if (ms_logMessageFd >= 0) + _write(ms_logMessageFd, message, strlen(message)); +} + +//----------------------------------------------------------------------- + +bool MemoryManagerNamespace::reportToFile(char const *fileName, bool leak) +{ + if (ms_logMessageFd >= 0) + return false; + + ms_logMessageFd = _open(fileName, O_CREAT | O_TRUNC | O_WRONLY, S_IREAD | S_IWRITE); + if (ms_logMessageFd < 0) + return false; + + void (*oldLogMessage)(char const * message) = LogMessage; + LogMessage = &logMessageToFd; + + report(leak); + + LogMessage = oldLogMessage; + + _close(ms_logMessageFd); + ms_logMessageFd = -1; + + return true; +} + +//----------------------------------------------------------------------- + +bool MemoryManager::reportToFile(char const *fileName, bool leak) +{ + return MemoryManagerNamespace::reportToFile(fileName, leak); +} + +// ====================================================================== +// DISABLE_MEMORY_MANAGER +#else +// ====================================================================== +namespace MemoryManagerNamespace +{ + // Memory tracking variables avialble when memory manger is running + // in disabled mode for linux + int ms_allocateCalls; + int ms_freeCalls; + unsigned long ms_allocateBytesTotal; +#ifndef _WIN32 + int ms_processVmSizeKBytes; +#endif + unsigned long ms_maxBytesAllocated; + int ms_allocations; + int ms_maxAllocations; + bool ms_installed; + + struct Statm //cache for /proc/statm data + { + int tpsr; //total program size resident + int rss; //resident set size share + }; + + Statm s_statm; + int s_pagesize; + + RecursiveMutex * ms_criticalSection; +} + +using namespace MemoryManagerNamespace; + +// ---------------------------------------------------------------------- + +MemoryManager::MemoryManager() +{ + // Initialize all memory tracking vars + MemoryManagerNamespace::ms_allocateCalls = 0; + MemoryManagerNamespace::ms_freeCalls = 0; + MemoryManagerNamespace::ms_allocateBytesTotal = 0; +#ifndef _WIN32 + MemoryManagerNamespace::ms_processVmSizeKBytes = 0; +#endif + MemoryManagerNamespace::ms_maxBytesAllocated = 0; + MemoryManagerNamespace::ms_allocations = 0; + MemoryManagerNamespace::ms_maxAllocations= 0; + + DEBUG_FATAL(MemoryManagerNamespace::ms_installed, ("already installed")); + + ms_criticalSection = new RecursiveMutex; + + MemoryManagerNamespace::ms_installed = true; + +#if defined(PLATFORM_WIN32) && defined(_DEBUG) + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG); + _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_WNDW); + _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_WNDW); + + int crtHeapFlags = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); + crtHeapFlags |= _CRTDBG_ALLOC_MEM_DF; + //crtHeapFlags |= _CRTDBG_CHECK_ALWAYS_DF; + crtHeapFlags |= _CRTDBG_LEAK_CHECK_DF; + _CrtSetDbgFlag(crtHeapFlags); +#endif +} + +// ---------------------------------------------------------------------- + +MemoryManager::~MemoryManager() +{ + delete ms_criticalSection; +} + +// ---------------------------------------------------------------------- + +void MemoryManager::verify(bool, bool) +{ +#if defined(PLATFORM_WIN32) && defined(_DEBUG) + const int result = _CrtCheckMemory(); + FATAL(result==FALSE, ("Heap corruption detected!\n")); +#endif +} + +// ---------------------------------------------------------------------- + +unsigned long MemoryManager::getCurrentNumberOfBytesAllocated(const int processId) +{ + +#ifdef _WIN32 + UNREF(processId); + return 0; +#else + + static char filename[30]; + static FILE *inStatmFile; + static time_t seconds = 0; + + unsigned long retVal = 0; + + ms_criticalSection->enter(); + + if (processId) + { + if ((time (0) - seconds) > 60 ) + { + if (seconds == 0) + { + snprintf(filename, sizeof(filename)-1, "/proc/%d/statm", processId); + filename[sizeof(filename)-1] = '\0'; + s_pagesize = getpagesize(); + } + + seconds = time (0); + + inStatmFile = fopen(filename,"rt"); + + if (inStatmFile) + { + if (fscanf (inStatmFile, "%d %d", &s_statm.tpsr, &s_statm.rss) == 2) + { + retVal = static_cast(s_statm.tpsr) * static_cast(s_pagesize); + } + else + { + retVal = 0; + } + fclose(inStatmFile); + } + else + { + retVal = 0; + } + } + else + { + retVal = static_cast(s_statm.tpsr) * static_cast(s_pagesize); + } + } + + ms_criticalSection->leave(); + + return retVal; +#endif +} + +// ---------------------------------------------------------------------- + +unsigned long MemoryManager::getCurrentNumberOfBytesAllocatedNoLeakTest() +{ + return 0; +} + +// ---------------------------------------------------------------------- + +unsigned long MemoryManager::getMaximumNumberOfBytesAllocated() +{ +#ifdef _WIN32 + return 0; +#else + return MemoryManagerNamespace::ms_maxBytesAllocated; +#endif +} + +// ---------------------------------------------------------------------- + +int MemoryManager::getSystemMemoryAllocatedMegabytes() +{ + return 0; +} + +// ---------------------------------------------------------------------- + +int MemoryManager::getCurrentNumberOfAllocations() +{ +#ifdef _WIN32 + return 0; +#else + return MemoryManagerNamespace::ms_allocations; +#endif +} + +// ---------------------------------------------------------------------- + +int MemoryManager::getMaximumNumberOfAllocations() +{ +#ifdef _WIN32 + return 0; +#else + return MemoryManagerNamespace::ms_maxAllocations; +#endif +} + +// ---------------------------------------------------------------------- + +void MemoryManager::setLimit(int, bool, bool) +{ +} + +// ---------------------------------------------------------------------- + +int MemoryManager::getLimit() +{ + return 768; +} + +// ---------------------------------------------------------------------- + +void MemoryManager::own(void * userPointer) +{ + UNREF(userPointer); +// __asm int 3; +} + +// ---------------------------------------------------------------------- + +void MemoryManager::setReportAllocations(bool) +{ +} + +// ---------------------------------------------------------------------- + +void MemoryManager::registerDebugFlags() +{ +} + +// ---------------------------------------------------------------------- + +void MemoryManager::report() +{ +} + +// ---------------------------------------------------------------------- + +void * MemoryManager::allocate(size_t size, uint32, bool, bool) +{ +#ifdef _WIN32 + return _malloc_dbg(size, _NORMAL_BLOCK, __FILE__, __LINE__); + //return (void *)LocalAlloc(LMEM_FIXED, size); +#else + return ::malloc(size); +#endif +} + +// ---------------------------------------------------------------------- + +void MemoryManager::free(void * userPointer, bool) +{ +#ifdef _WIN32 + _free_dbg(userPointer, _NORMAL_BLOCK); + //LocalFree((HLOCAL)userPointer); +#else + ::free(userPointer); + +#endif +} + +// ---------------------------------------------------------------------- + +void *MemoryManager::reallocate(void *userPointer, size_t newSize) +{ +#ifdef _WIN32 + return _realloc_dbg(userPointer, newSize, _NORMAL_BLOCK, __FILE__, __LINE__); + //return (void *)LocalReAlloc((HLOCAL)userPointer, newSize, 0); +#else + return ::realloc(userPointer, newSize); +#endif +} + +// ---------------------------------------------------------------------- + +void MemoryManager::debugReport(void) +{ + +} + +// ---------------------------------------------------------------------- + +void MemoryManager::debugReportMap(void) +{ + +} +#endif + +// ---------------------------------------------------------------------- +// this function is available regardless of the DISABLE_MEMORY_MANAGER setting + +#ifndef _WIN32 +int MemoryManager::getProcessVmSizeKBytes(const int processId) +{ + static char filename[30]; + static FILE *inStatusFile; + static char buffer[128]; + static time_t seconds = 0; + + ms_criticalSection->enter(); + + if (processId) + { + if ((time (0) - seconds) > 60) + { + MemoryManagerNamespace::ms_processVmSizeKBytes = 0; + + if (seconds == 0) + { + snprintf(filename, sizeof(filename)-1, "/proc/%d/status", processId); + filename[sizeof(filename)-1] = '\0'; + } + + seconds = time(0); + + inStatusFile = fopen(filename,"rt"); + + if (inStatusFile) + { + int temp; + + while (fgets(buffer, sizeof(buffer), inStatusFile)) + { + if (1 == sscanf(buffer, "VmSize: %d", &temp)) + { + MemoryManagerNamespace::ms_processVmSizeKBytes = temp; + break; + } + } + + fclose(inStatusFile); + } + } + } + + ms_criticalSection->leave(); + + return MemoryManagerNamespace::ms_processVmSizeKBytes; +} +#endif + +// ====================================================================== diff --git a/engine/shared/library/sharedMemoryManager/src/shared/MemoryManager.h b/engine/shared/library/sharedMemoryManager/src/shared/MemoryManager.h new file mode 100644 index 00000000..0f170c55 --- /dev/null +++ b/engine/shared/library/sharedMemoryManager/src/shared/MemoryManager.h @@ -0,0 +1,84 @@ +// ====================================================================== +// +// MemoryManager.h +// Portions copyright 1998 Bootprint Entertainment +// Portions copyright 2002 Sony Online Entertainment +// All Rights Reserved +// +// ====================================================================== + +#ifndef INCLUDED_MemoryManager_H +#define INCLUDED_MemoryManager_H + +// ====================================================================== + +#include "sharedDebug/DebugHelp.h" + +// ====================================================================== + +#include +#include "sharedMemoryManager/OsNewDel.h" + +// ====================================================================== +// Memory manager class. +// +// This class API is multi-thread safe. +// +// This class provides extensive debugging features for applications, including +// overwrite guard bands, initialize pattern fills, free pattern fills, and +// memory tracking. + +class MemoryManager +{ +public: + + MemoryManager(); + ~MemoryManager(); + + static void setLimit(int megabytes, bool hardLimit, bool preallocate); + static int getLimit(); + static bool isHardLimit(); + + static void registerDebugFlags(); + static void debugReport(); + static void debugReportMap(); + static bool reportToFile(const char * fileName, bool leak); + + static int getCurrentNumberOfAllocations(); + static unsigned long getCurrentNumberOfBytesAllocated(const int processId = 0); + static unsigned long getCurrentNumberOfBytesAllocatedNoLeakTest(); + static int getMaximumNumberOfAllocations(); + static unsigned long getMaximumNumberOfBytesAllocated(); + static int getSystemMemoryAllocatedMegabytes(); + +#ifndef _WIN32 + static int getProcessVmSizeKBytes(const int processId = 0); +#endif + + static DLLEXPORT void *allocate(size_t size, uint32 owner, bool array, bool leakTest); + static DLLEXPORT void free(void *pointer, bool array); + static DLLEXPORT void own(void *pointer); + static void * reallocate(void *userPointer, size_t newSize); + + static void verify(bool guardPatterns, bool freePatterns); + static void setReportAllocations(bool reportAllocations); + static void report(); + +private: + + // disabled + MemoryManager(MemoryManager const &); + MemoryManager &operator =(MemoryManager const &); +}; + +// ====================================================================== + +#ifdef _DEBUG + #define MEM_OWN(a) MemoryManager::own(a) +#else + #define MEM_OWN(a) UNREF(a) +#endif + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedMemoryManager/src/win32/OsMemory.cpp b/engine/shared/library/sharedMemoryManager/src/win32/OsMemory.cpp new file mode 100644 index 00000000..a6ad6331 --- /dev/null +++ b/engine/shared/library/sharedMemoryManager/src/win32/OsMemory.cpp @@ -0,0 +1,56 @@ +// ====================================================================== +// +// OsMemory.cpp +// +// Copyright 2002 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedMemoryManager/FirstSharedMemoryManager.h" +#include "sharedMemoryManager/OsMemory.h" + +// ====================================================================== + +void OsMemory::install() +{ +} + +// ---------------------------------------------------------------------- + +void OsMemory::remove() +{ +} + +// ---------------------------------------------------------------------- + +void *OsMemory::reserve(size_t bytes) +{ + return VirtualAlloc(0, bytes, MEM_RESERVE, PAGE_READWRITE); +} + +// ---------------------------------------------------------------------- + +void *OsMemory::commit(void *addr, size_t bytes) +{ + return VirtualAlloc(addr, bytes, MEM_COMMIT, PAGE_READWRITE); +} + +// ---------------------------------------------------------------------- + +bool OsMemory::free(void *addr, size_t bytes) +{ + UNREF(bytes); + return VirtualFree(addr, 0, MEM_RELEASE) ? true : false; +} + +// ---------------------------------------------------------------------- + +bool OsMemory::protect(void *addr, size_t bytes, bool allowAccess) +{ + DWORD old; + BOOL result = VirtualProtect(addr, bytes, allowAccess ? PAGE_READWRITE : PAGE_NOACCESS, &old); + return result ? true : false; +} + +// ====================================================================== + diff --git a/engine/shared/library/sharedMemoryManager/src/win32/OsMemory.h b/engine/shared/library/sharedMemoryManager/src/win32/OsMemory.h new file mode 100644 index 00000000..07ff6918 --- /dev/null +++ b/engine/shared/library/sharedMemoryManager/src/win32/OsMemory.h @@ -0,0 +1,29 @@ +// ====================================================================== +// +// OsMemory.h +// +// Copyright 2002 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_OsMemory_H +#define INCLUDED_OsMemory_H + +// ====================================================================== + +class OsMemory +{ +public: + static void install(); + static void remove(); + + static void * reserve(size_t bytes); + static void * commit(void *addr, size_t bytes); + static bool free(void *addr, size_t bytes); + static bool protect(void *addr, size_t bytes, bool allowAccess); +}; + +// ====================================================================== + +#endif INCLUDED_OsMemory_H + diff --git a/engine/shared/library/sharedMemoryManager/src/win32/OsNewDel.cpp b/engine/shared/library/sharedMemoryManager/src/win32/OsNewDel.cpp new file mode 100644 index 00000000..c6569707 --- /dev/null +++ b/engine/shared/library/sharedMemoryManager/src/win32/OsNewDel.cpp @@ -0,0 +1,211 @@ +// ====================================================================== +// +// OsNewDel.cpp +// +// Copyright 2002 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedMemoryManager/FirstSharedMemoryManager.h" +#include "sharedMemoryManager/OsNewDel.h" + +// ====================================================================== + +// this is here because MSVC won't call MemoryManager::allocate() from asm directly +static void * __cdecl localAllocate(size_t size, uint32 owner, bool array, bool leakTest) +{ + return MemoryManager::allocate(size, owner, array, leakTest); +} + +// ---------------------------------------------------------------------- + +// We are using the arguments (except for file and line), but MSVC can't tell that. +#pragma warning(disable: 4100) + +// ---------------------------------------------------------------------- + +__declspec(naked) void *operator new(size_t size, MemoryManagerNotALeak) +{ + _asm + { + // setup local call stack + push ebp + mov ebp, esp + + // MemoryManager::alloc(size, [return address], false, false) + push 0 + push 0 + mov eax, dword ptr [ebp+4] + push eax + mov eax, dword ptr [ebp+8] + push eax + call localAllocate + add esp, 12 + + mov esp, ebp + pop ebp + ret + } +} + +// ---------------------------------------------------------------------- + +__declspec(naked) void *operator new(size_t size) +{ + _asm + { + // setup local call stack + push ebp + mov ebp, esp + + // MemoryManager::alloc(size, [return address], false, true) + push 1 + push 0 + mov eax, dword ptr [ebp+4] + push eax + mov eax, dword ptr [ebp+8] + push eax + call localAllocate + add esp, 12 + + mov esp, ebp + pop ebp + ret + } +} + +// ---------------------------------------------------------------------- + +__declspec(naked) void *operator new[](size_t size) +{ + _asm + { + // setup local call stack + push ebp + mov ebp, esp + + // MemoryManager::alloc(size, [return address], true, true) + push 1 + push 1 + mov eax, dword ptr [ebp+4] + push eax + mov eax, dword ptr [ebp+8] + push eax + call localAllocate + add esp, 12 + + mov esp, ebp + pop ebp + ret + } +} + +// ---------------------------------------------------------------------- + +__declspec(naked) void *operator new(size_t size, const char *file, int line) +{ + _asm + { + // setup local call stack + push ebp + mov ebp, esp + + // MemoryManager::alloc(size, [return address], false, true) + push 1 + push 0 + mov eax, dword ptr [ebp+4] + push eax + mov eax, dword ptr [ebp+8] + push eax + call localAllocate + add esp, 12 + + mov esp, ebp + pop ebp + ret + } +} + +// ---------------------------------------------------------------------- + +__declspec(naked) void *operator new[](size_t size, const char *file, int line) +{ + _asm + { + // setup local call stack + push ebp + mov ebp, esp + + // MemoryManager::alloc(size, [return address], true, true) + push 1 + push 1 + mov eax, dword ptr [ebp+4] + push eax + mov eax, dword ptr [ebp+8] + push eax + call localAllocate + add esp, 12 + + mov esp, ebp + pop ebp + ret + } +} + +// ---------------------------------------------------------------------- + +#pragma warning(default: 4100) + +// ---------------------------------------------------------------------- + +void operator delete(void *pointer) +{ + if (pointer) + MemoryManager::free(pointer, false); +} + +// ---------------------------------------------------------------------- + +void operator delete[](void *pointer) +{ + if (pointer) + MemoryManager::free(pointer, true); +} + +// ---------------------------------------------------------------------- + +void operator delete(void *pointer, const char *file, int line) +{ + UNREF(file); + UNREF(line); + + if (pointer) + MemoryManager::free(pointer, false); +} + +// ---------------------------------------------------------------------- + +void operator delete[](void *pointer, const char *file, int line) +{ + UNREF(file); + UNREF(line); + + if (pointer) + MemoryManager::free(pointer, true); +} + +// ====================================================================== +// WARNING!!!!!!! +// +// The init_seg pragma command is used to create certain static objects first, before other static objects are created. +// However, multiple static variables that use the same init_seg category(i.e. compiler) are not guaranteed to destroy in any +// particular order. It is completely random based on how all the linking of static objects occurs. Since this command is being +// used on our memory manager(to overwrite new/delete) - NO OTHER STATIC SHOULD EVER USE INIT_SEG(COMPILER)!!!! This static object +// *MUST* be the final static object that is destroyed. +// +#pragma warning(disable: 4074) +#pragma init_seg(compiler) // ^-Read warning above.-^ +static MemoryManager memoryManager; + +// ====================================================================== + diff --git a/engine/shared/library/sharedMemoryManager/src/win32/OsNewDel.h b/engine/shared/library/sharedMemoryManager/src/win32/OsNewDel.h new file mode 100644 index 00000000..477a6674 --- /dev/null +++ b/engine/shared/library/sharedMemoryManager/src/win32/OsNewDel.h @@ -0,0 +1,52 @@ +// ====================================================================== +// +// OsNewDel.h +// +// Copyright 2002 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_OsNewDel_H +#define INCLUDED_OsNewDel_H + +// ====================================================================== + +enum MemoryManagerNotALeak +{ + MM_notALeak +}; + +void * __cdecl operator new(size_t size, MemoryManagerNotALeak); +void * __cdecl operator new(size_t size); +void * __cdecl operator new[](size_t size); +void * __cdecl operator new(size_t size, char const *file, int line); +void * __cdecl operator new[](size_t size, char const *file, int line); +void * __cdecl operator new(size_t size, void *placement); + +void operator delete(void *pointer); +void operator delete[](void *pointer); +void operator delete(void *pointer, char const *file, int line); +void operator delete[](void *pointer, char const *file, int line); +void operator delete(void *pointer, void *placement); + +#ifndef __PLACEMENT_NEW_INLINE +#define __PLACEMENT_NEW_INLINE + +inline void *operator new(size_t size, void *placement) +{ + static_cast(size); + return placement; +} + +inline void operator delete(void *pointer, void *placement) +{ + static_cast(pointer); + static_cast(placement); +} + +#endif // __PLACEMENT_NEW_INLINE + +// ====================================================================== + +#endif INCLUDED_OsNewDel_H +