mirror of
https://github.com/SWG-Source/src.git
synced 2026-07-28 22:15:49 -04:00
Initial commits of the sharedDebug and sharedMemoryManager projects
This commit is contained in:
@@ -1,3 +1,6 @@
|
||||
|
||||
add_subdirectory(sharedDebug)
|
||||
add_subdirectory(sharedFoundation)
|
||||
add_subdirectory(sharedFoundationTypes)
|
||||
add_subdirectory(sharedMemoryManager)
|
||||
add_subdirectory(sharedNetworkMessages)
|
||||
|
||||
@@ -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)
|
||||
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/CallStack.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/CallStackCollector.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../../src/linux/ConfigSharedDebugLinux.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/DataLint.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/DebugFlags.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
|
||||
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/DebugKey.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
|
||||
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/FirstSharedDebug.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/InstallTimer.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/LeakFinder.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
|
||||
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/PixCounter.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/Profiler.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
|
||||
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/RemoteDebug.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/RemoteDebug_inner.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/Report.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/SetupSharedDebug.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
|
||||
@@ -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}
|
||||
)
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
@@ -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
|
||||
|
||||
@@ -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 <execinfo.h>
|
||||
#include <elf.h>
|
||||
#include <sys/mman.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <dlfcn.h>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
// ======================================================================
|
||||
|
||||
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 T>
|
||||
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 <class U> SymbolCacheAllocator(SymbolCacheAllocator<U> const &) {}
|
||||
template <class U> struct rebind { typedef SymbolCacheAllocator<U> other; };
|
||||
|
||||
pointer allocate(size_type n, const_pointer = 0) { return reinterpret_cast<pointer>(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<void const *, SymbolCache::SymbolInfo, std::less<void const *>, SymbolCacheAllocator<std::pair<void const *, SymbolCache::SymbolInfo> > > SymbolMap;
|
||||
typedef std::vector<char const *, SymbolCacheAllocator<char const *> > 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<Elf32_Ehdr const *>(objBaseAddr);
|
||||
if (sectionIndex >= 0 && sectionIndex < eh->e_shnum)
|
||||
return reinterpret_cast<Elf32_Shdr const *>(static_cast<char const *>(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<char const *>(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<Elf32_Ehdr const *>(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<Elf32_Ehdr const *>(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<<shift);
|
||||
return pos;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
class LEB128u
|
||||
{
|
||||
public:
|
||||
operator unsigned int() const { return value; }
|
||||
unsigned int value;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
inline unsigned int dwarfGet(char const *src, LEB128u &dest)
|
||||
{
|
||||
unsigned int pos = 0;
|
||||
int shift = 7;
|
||||
unsigned 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;
|
||||
}
|
||||
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<void const *>(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<u_int8_t const *>(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<unsigned int>(info.dli_fbase))
|
||||
addrOffset = reinterpret_cast<unsigned int>(info.dli_fbase);
|
||||
const void *testAddr = reinterpret_cast<const void *>(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<void const *>(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<void const *>(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<void *>(mappedAddr), fileSize);
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
}
|
||||
munmap(const_cast<void *>(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<void const *>(stab->n_value) > info.dli_fbase)
|
||||
funcBase = reinterpret_cast<void const *>(stab->n_value);
|
||||
else
|
||||
funcBase = reinterpret_cast<void const *>(reinterpret_cast<unsigned int>(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<unsigned int>(addr)-reinterpret_cast<unsigned int>(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<void const *>(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<Stab const *>(elfGetObjSectionData(mappedAddr, stabIndex)),
|
||||
elfGetObjSectionSize(mappedAddr, stabIndex),
|
||||
elfGetObjSectionData(mappedAddr, stabStrIndex),
|
||||
addr,
|
||||
info,
|
||||
retSrcFile,
|
||||
retSrcLine);
|
||||
}
|
||||
catch (std::bad_alloc &)
|
||||
{
|
||||
munmap(const_cast<void *>(mappedAddr), fileSize);
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
}
|
||||
munmap(const_cast<void *>(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<char *>(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<void const *>(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<void **>(callStack), sizeOfCallStack));
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 <curses.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <termios.h>
|
||||
|
||||
// ======================================================================
|
||||
|
||||
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<char*>(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
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,125 @@
|
||||
//=================================================================== //
|
||||
//
|
||||
// PerformanceTimer.cpp
|
||||
// copyright 2000-2004 Sony Online Entertainment
|
||||
// All Rights Reserved
|
||||
//
|
||||
//===================================================================
|
||||
|
||||
#include "sharedFoundation/FirstSharedFoundation.h"
|
||||
#include "sharedDebug/PerformanceTimer.h"
|
||||
|
||||
//===================================================================
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
//===================================================================
|
||||
|
||||
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<float>(sec) +(static_cast<float>(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<float>(sec) +(static_cast<float>(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
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
@@ -0,0 +1,51 @@
|
||||
//=================================================================== //
|
||||
//
|
||||
// PerformanceTimer.h
|
||||
// copyright 2000-2004 Sony Online Entertainment
|
||||
// All Rights Reserved
|
||||
//
|
||||
//===================================================================
|
||||
|
||||
#ifndef INCLUDED_PerformaceTimer_H
|
||||
#define INCLUDED_PerformaceTimer_H
|
||||
|
||||
//===================================================================
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
//===================================================================
|
||||
|
||||
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
|
||||
@@ -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<Type>(tv.tv_sec)*static_cast<Type>(1000000)+static_cast<Type>(tv.tv_usec);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void ProfilerTimer::getCalibratedTime(Type &time, Type &frequency)
|
||||
{
|
||||
getTime(time);
|
||||
frequency = 1000000;
|
||||
}
|
||||
|
||||
void ProfilerTimer::getFrequency(Type &frequency)
|
||||
{
|
||||
frequency = 1000000;
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
@@ -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
|
||||
@@ -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 <algorithm>
|
||||
|
||||
// ======================================================================
|
||||
|
||||
#define DESIRED_CALLSTACK_DEPTH 24
|
||||
|
||||
#define CALLSTACK_DEPTH std::min(static_cast<size_t>(CallStack::S_callStack), static_cast<size_t>(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<int>(m_callStack[i]), fileName, line, i - 1));
|
||||
else
|
||||
DEBUG_REPORT_PRINT(true, (" unknown(0x%08X) : caller %d\n", static_cast<int>(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<int>(m_callStack[i]), fileName, line, i - 1));
|
||||
else
|
||||
DEBUG_REPORT_LOG(true, (" unknown(0x%08X) : caller %d\n", static_cast<int>(m_callStack[i]), i - 1));
|
||||
}
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
@@ -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
|
||||
@@ -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 <algorithm>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
// ======================================================================
|
||||
|
||||
#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<uint32, CallStackEntry> CallStackEntryMap;
|
||||
CallStackEntryMap m_callStackEntryMap;
|
||||
};
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
typedef std::map<CrcString const *, Node *, LessPointerComparator> 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<CallStackEntry const *> 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<int>(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;
|
||||
}
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
@@ -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
|
||||
@@ -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 <cstdio>
|
||||
#include <set>
|
||||
#include <stack>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// ============================================================================
|
||||
|
||||
namespace DataLintNamespace
|
||||
{
|
||||
enum Mode
|
||||
{
|
||||
M_client,
|
||||
M_server
|
||||
};
|
||||
|
||||
typedef stdvector<std::string>::fwd StringList;
|
||||
typedef std::pair<std::string, std::pair<DataLint::AssetType, StringList> > WarningPair;
|
||||
typedef stdvector<WarningPair>::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<std::string> 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<int>(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;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
@@ -0,0 +1,73 @@
|
||||
// ============================================================================
|
||||
//
|
||||
// DataLint.h
|
||||
// Copyright Sony Online Entertainment
|
||||
//
|
||||
// ============================================================================
|
||||
|
||||
#ifndef DATALINT_H
|
||||
#define DATALINT_H
|
||||
|
||||
// ============================================================================
|
||||
|
||||
class DataLint
|
||||
{
|
||||
public:
|
||||
|
||||
typedef stdvector<std::pair<std::string, std::string> >::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<AssetType> 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
|
||||
@@ -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 <algorithm>
|
||||
#include <vector>
|
||||
|
||||
// ======================================================================
|
||||
|
||||
std::vector<DebugFlags::Flag> DebugFlags::ms_flagsSortedByName;
|
||||
std::vector<DebugFlags::Flag> 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<int>(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;
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
@@ -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<Flag>::fwd FlagVector;
|
||||
|
||||
static FlagVector ms_flagsSortedByName;
|
||||
static FlagVector ms_flagsSortedByReportPriority;
|
||||
};
|
||||
|
||||
// ======================================================================
|
||||
|
||||
#endif
|
||||
@@ -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 <algorithm>
|
||||
#include <vector>
|
||||
|
||||
// ======================================================================
|
||||
|
||||
#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<bool *>(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
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
@@ -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<Flag>::fwd FlagVector;
|
||||
|
||||
private:
|
||||
|
||||
static bool *ms_currentFlag;
|
||||
static bool ms_pressed[10];
|
||||
static bool ms_down[10];
|
||||
static FlagVector ms_flags;
|
||||
};
|
||||
|
||||
// ======================================================================
|
||||
|
||||
#endif
|
||||
@@ -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
|
||||
@@ -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<int>(endingNumberOfBytesAllocated - m_startingNumberOfBytesAllocated), m_description));
|
||||
m_description = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
@@ -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
|
||||
@@ -0,0 +1,197 @@
|
||||
// ======================================================================
|
||||
//
|
||||
// LeakFinder.cpp
|
||||
// bearhart
|
||||
//
|
||||
// Copyright 2005, Sony Online Entertainment
|
||||
// All Rights Reserved
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#include "sharedDebug/FirstSharedDebug.h"
|
||||
#include "sharedDebug/LeakFinder.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
// ======================================================================
|
||||
|
||||
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<CallStack, std::vector<const LiveObject *> > uniques;
|
||||
std::map<CallStack, std::vector<const LiveObject *> >::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;i<ui->second.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<int>(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<CallStack, int> refs;
|
||||
std::map<CallStack, int> derefs;
|
||||
|
||||
std::list<ReferenceCountChange>::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<CallStack, int>::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();
|
||||
}
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
@@ -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 <vector>
|
||||
#include <list>
|
||||
#include <hash_map>
|
||||
|
||||
// ======================================================================
|
||||
|
||||
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<ReferenceCountChange> ReferenceCountingData;
|
||||
|
||||
struct LiveObject
|
||||
{
|
||||
void *object;
|
||||
CallStack callStack;
|
||||
const ReferenceCountingData *referenceData;
|
||||
};
|
||||
typedef std::vector<LiveObject> 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>()((unsigned long)p); }
|
||||
};
|
||||
|
||||
struct ObjectData
|
||||
{
|
||||
ObjectData() : referenceData(0) { }
|
||||
~ObjectData() { if (referenceData) { delete referenceData; } }
|
||||
|
||||
CallStack callStack;
|
||||
ReferenceCountingData *referenceData;
|
||||
};
|
||||
|
||||
typedef std::hash_map<void *, ObjectData, ptr_hash> ObjectMap;
|
||||
|
||||
ObjectMap liveObjects;
|
||||
};
|
||||
|
||||
// ======================================================================
|
||||
|
||||
#endif
|
||||
@@ -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 <algorithm>
|
||||
#include <vector>
|
||||
|
||||
// ======================================================================
|
||||
|
||||
#ifdef _WIN32
|
||||
#define STDCALL __stdcall
|
||||
#else
|
||||
#define STDCALL
|
||||
#endif
|
||||
|
||||
namespace PixCounterNamespace
|
||||
{
|
||||
typedef std::vector<PixCounter::Counter *> 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<SetCounterFloat> (GetProcAddress(ms_pixDll, "SetCounterFloat"));
|
||||
ms_setCounterInt = reinterpret_cast<SetCounterInt> (GetProcAddress(ms_pixDll, "SetCounterInt"));
|
||||
ms_setCounterInt64 = reinterpret_cast<SetCounterInt64> (GetProcAddress(ms_pixDll, "SetCounterInt64"));
|
||||
ms_setCounterString = reinterpret_cast<SetCounterString>(GetProcAddress(ms_pixDll, "SetCounterString"));
|
||||
|
||||
NOT_NULL(ms_setCounterFloat);
|
||||
NOT_NULL(ms_setCounterInt);
|
||||
NOT_NULL(ms_setCounterInt64);
|
||||
NOT_NULL(ms_setCounterString);
|
||||
|
||||
ClientInstall clientInstall = reinterpret_cast<ClientInstall>(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
|
||||
@@ -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 <string>
|
||||
|
||||
// ======================================================================
|
||||
|
||||
#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
|
||||
@@ -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 <vector>
|
||||
#include <algorithm>
|
||||
|
||||
// ======================================================================
|
||||
|
||||
namespace ProfilerNamespace
|
||||
{
|
||||
struct ProfilerEntry
|
||||
{
|
||||
typedef std::vector<int> 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<VisibleExpandableEntry *> Children;
|
||||
|
||||
private:
|
||||
|
||||
static bool ms_destroyedSelected;
|
||||
|
||||
private:
|
||||
|
||||
char const * m_name;
|
||||
Children m_children;
|
||||
bool m_expanded;
|
||||
bool m_visible;
|
||||
};
|
||||
|
||||
typedef std::vector<ProfilerEntry> ProfilerEntries;
|
||||
typedef std::vector<ProfilerEntry *> 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<int> 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<char> 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<char>('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<int>(totalTime / ms_percentageDivisor);
|
||||
|
||||
if (percentage < ms_displayPercentageMinimum)
|
||||
return false;
|
||||
|
||||
int const ticks = static_cast<int>(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<ProfilerTimer::Type>(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<ProfilerTimer::Type>(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<ProfilerEntry> * 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);
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
@@ -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
|
||||
@@ -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 <string>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
//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<uint32, bool> *RemoteDebug::ms_squelchedStream;
|
||||
std::map<uint32, bool> *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<uint32, bool>;
|
||||
ms_squelchedStatic = new std::map<uint32, bool>;
|
||||
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<uint32>(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<uint32>(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<char>(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<uint32>(chan);
|
||||
}
|
||||
//bail out if this channel is squelched
|
||||
if ((*ms_squelchedStream)[static_cast<uint32>(chan)] == true)
|
||||
return;
|
||||
break;
|
||||
|
||||
case NEW_STREAM:
|
||||
//get the registered int for the new channel
|
||||
chan = findStream(name);
|
||||
if (chan != -1)
|
||||
channelNumber = static_cast<uint32>(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<uint32>(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<uint32>(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<uint32>(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<uint32>(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<uint32>(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<uint32>(chan);
|
||||
}
|
||||
//bail out if this channel is squelched
|
||||
if ((*ms_squelchedStatic)[static_cast<uint32>(chan)] == true)
|
||||
return;
|
||||
break;
|
||||
|
||||
case STATIC_SQUELCH:
|
||||
case STATIC_UNSQUELCH:
|
||||
chan = findStaticView(name);
|
||||
if (chan != -1)
|
||||
channelNumber = static_cast<uint32>(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<uint32>(chan);
|
||||
break;
|
||||
|
||||
case STATIC_BEGIN_FRAME:
|
||||
case STATIC_END_FRAME:
|
||||
chan = findStaticView(name);
|
||||
if (chan != -1)
|
||||
channelNumber = static_cast<uint32>(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<uint32>(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<uint32>(messageTypeLength + channelNumberLength + messageLengthLength + static_cast<int>(messageLength));
|
||||
|
||||
if (ms_varArgs_buffer)
|
||||
{
|
||||
//copy data into the packet
|
||||
memcpy(ms_buffer, &messageType, static_cast<uint32>(messageTypeLength));
|
||||
memcpy(ms_buffer + messageTypeLength, &channelNumber, static_cast<uint32>(channelNumberLength));
|
||||
//only copy the buffer data if needed
|
||||
memcpy(ms_buffer + messageTypeLength + channelNumberLength, &messageLength, static_cast<uint32>(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<int32>(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<int32>(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<int32>(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);
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
|
||||
@@ -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<uint32, Channel *>::fwd StreamMap;
|
||||
///stream names
|
||||
static StreamMap *ms_streams;
|
||||
|
||||
typedef stdmap<uint32, Channel *>::fwd VariableMap;
|
||||
///What variables are registered
|
||||
static VariableMap *ms_variables;
|
||||
|
||||
typedef stdmap<uint32, Channel *>::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<uint32, bool>::fwd *ms_squelchedStream;
|
||||
|
||||
///What static channels are squelched (i.e. don't send to client at all)
|
||||
static stdmap<uint32, bool>::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<uint32, Variable *>::fwd VariableValueMap;
|
||||
static VariableValueMap *ms_variableValues;
|
||||
|
||||
typedef stdmap<uint32, UpFunction>::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
|
||||
@@ -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 <string>
|
||||
#include <list>
|
||||
#include <map>
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
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<char *>(memLoc))];
|
||||
strcpy(m_value.stringValue, const_cast<const char *>(static_cast<char *>(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<char *>(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<const char * const>(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<RemoteDebug::MESSAGE_TYPE>(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<const char*>(ms_buffer));
|
||||
break;
|
||||
|
||||
case RemoteDebug::NEW_STREAM:
|
||||
if (channelNumber < ms_nextStream)
|
||||
return;
|
||||
registerStream(ms_buffer);
|
||||
if (ms_newStreamFunction)
|
||||
ms_newStreamFunction(channelNumber, const_cast<const char*>(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<const char*>(ms_buffer));
|
||||
break;
|
||||
|
||||
case RemoteDebug::VARIABLE_TYPE:
|
||||
t = atoi(ms_buffer);
|
||||
varType = static_cast<VARIABLE_TYPES>(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<const char*>(ms_buffer));
|
||||
break;
|
||||
|
||||
case RemoteDebug::STATIC_LINE:
|
||||
if (ms_lineFunction)
|
||||
ms_lineFunction(channelNumber, const_cast<const char*>(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<const char * const>(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<RemoteDebug::MESSAGE_TYPE>(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());
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
@@ -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<Channel *>::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
|
||||
@@ -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 <cstdio>
|
||||
#include <cstdarg>
|
||||
|
||||
// ======================================================================
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
@@ -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
|
||||
@@ -0,0 +1,670 @@
|
||||
// ======================================================================
|
||||
//
|
||||
// DebugHelp.cpp
|
||||
// copyright 2000 Verant Interactive
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#include "sharedDebug/FirstSharedDebug.h"
|
||||
#include "sharedDebug/DebugHelp.h"
|
||||
|
||||
#include "sharedFoundation/WindowsWrapper.h"
|
||||
|
||||
#include <dbghelp.h>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
// ======================================================================
|
||||
|
||||
|
||||
// ======================================================================
|
||||
// This was done to keep the header file from having to include <windows.h> or <dbghelp.h>
|
||||
|
||||
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<n; ++i) h = hash( k[i], len[i], h);
|
||||
|
||||
By Bob Jenkins, 1996. [email protected]. You may use this
|
||||
code any way you wish, private, educational, or commercial. It's free.
|
||||
|
||||
See http://burtleburtle.net/bob/hash/evahash.html
|
||||
Use for hash table lookup, or anything where one collision in 2^^32 is
|
||||
acceptable. Do NOT use for cryptographic purposes.
|
||||
--------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
// k; /* the key */
|
||||
// length; /* the length of the key */
|
||||
// initval; /* the previous hash, or an arbitrary value */
|
||||
#if 0
|
||||
static ub4 hash(ub1 *k, const ub4 length, const ub4 initval)
|
||||
{
|
||||
ub4 a, b, c, len;
|
||||
|
||||
/* Set up the internal state */
|
||||
len = length;
|
||||
a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */
|
||||
c = initval; /* the previous hash value */
|
||||
|
||||
/*---------------------------------------- handle most of the key */
|
||||
while (len >= 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;i<count;i++)
|
||||
{
|
||||
const BaseAddressLookup *lookup = s_baseAddressCache + i;
|
||||
s_baseAddressElements++;
|
||||
if (lookup->baseAddress)
|
||||
{
|
||||
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<CallbackData *>(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<b##FP>(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<void *>(&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<uint32 *>(_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<int>(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<IMAGEHLP_SYMBOL64*>(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<uint>(fileNameLength));
|
||||
line = static_cast<int>(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<char *>(strchr(shortProgramName, '.'));
|
||||
if (dot)
|
||||
*dot = '\0';
|
||||
|
||||
// create a reasonable minidump filename
|
||||
snprintf(buffer, sizeof(buffer), "%s_%d.mdmp", shortProgramName, static_cast<int>(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;
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
@@ -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
|
||||
@@ -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<LPBYTE>(&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<const LPBYTE>(&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<InstallFunction>(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<ShowFunction>(GetProcAddress(dll, "showWindow"));
|
||||
hideFunction = reinterpret_cast<ShowFunction>(GetProcAddress(dll, "hideWindow"));
|
||||
removeFunction = reinterpret_cast<RemoveFunction>(GetProcAddress(dll, "remove"));
|
||||
setBehindWindowFunction = reinterpret_cast<SetBehindWindowFunction>(GetProcAddress(dll, "setBehindWindow"));
|
||||
clearScreenFunction = reinterpret_cast<ClearScreenFunction>(GetProcAddress(dll, "clearScreen"));
|
||||
clearToCursorFunction = reinterpret_cast<ClearToCursorFunction>(GetProcAddress(dll, "clearToCursor"));
|
||||
gotoXYFunction = reinterpret_cast<GotoXYFunction>(GetProcAddress(dll, "gotoXY"));
|
||||
printFunction = reinterpret_cast<PrintFunction>(GetProcAddress(dll, "print"));
|
||||
|
||||
SetChangedWindowCallback setChangedWindowCallback = reinterpret_cast<SetChangedWindowCallback>(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
|
||||
@@ -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
|
||||
@@ -0,0 +1,8 @@
|
||||
// ======================================================================
|
||||
//
|
||||
// FirstDebug.cpp
|
||||
// copyright (c) 2001 Sony Online Entertainment
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#include "shareddebug/FirstSharedDebug.h"
|
||||
@@ -0,0 +1,105 @@
|
||||
//
|
||||
// PerformanceTimer.cpp
|
||||
// Copyright 2000-2004 Sony Online Entertainment
|
||||
//
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
|
||||
#include "shareddebug/FirstSharedDebug.h"
|
||||
#include "shareddebug/PerformanceTimer.h"
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
|
||||
__int64 PerformanceTimer::ms_frequency;
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
|
||||
void PerformanceTimer::install()
|
||||
{
|
||||
BOOL result = QueryPerformanceFrequency(reinterpret_cast<LARGE_INTEGER *>(&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<LARGE_INTEGER *>(&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<LARGE_INTEGER *>(&m_stopTime));
|
||||
DEBUG_FATAL(!result, ("PerformanceTimer::stop QPC failed"));
|
||||
UNREF (result);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
|
||||
float PerformanceTimer::getElapsedTime() const
|
||||
{
|
||||
return static_cast<float> (m_stopTime - m_startTime) / static_cast<float> (ms_frequency);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
float PerformanceTimer::getSplitTime() const
|
||||
{
|
||||
__int64 currentTime;
|
||||
BOOL result = QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER *>(¤tTime));
|
||||
UNREF(result);
|
||||
DEBUG_FATAL(!result, ("PerformanceTimer::getSplitTime QPC failed"));
|
||||
|
||||
return static_cast<float> (currentTime - m_startTime) / static_cast<float> (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
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
|
||||
@@ -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
|
||||
@@ -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<LARGE_INTEGER *>(&ms_qpc)));
|
||||
ms_rdtsc = readTimeStampCounter();
|
||||
|
||||
IGNORE_RETURN(QueryPerformanceFrequency(reinterpret_cast<LARGE_INTEGER *>(&ms_qpcFrequency)));
|
||||
ms_floatQpcFrequency = static_cast<float>(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<LARGE_INTEGER *>(&time)));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void ProfilerTimer::getCalibratedTime(Type &time, Type &frequency)
|
||||
{
|
||||
if (ms_useRdtsc)
|
||||
{
|
||||
__int64 qpc;
|
||||
IGNORE_RETURN(QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER *>(&qpc)));
|
||||
__int64 rdtsc = readTimeStampCounter();
|
||||
|
||||
float const t = static_cast<float>(qpc - ms_qpc) / ms_floatQpcFrequency;
|
||||
frequency = static_cast<__int64>(static_cast<float>(rdtsc - ms_rdtsc) / t);
|
||||
|
||||
time = rdtsc;
|
||||
ms_qpc = qpc;
|
||||
ms_rdtsc = time;
|
||||
}
|
||||
else
|
||||
{
|
||||
IGNORE_RETURN(QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER *>(&time)));
|
||||
frequency = ms_qpcFrequency;
|
||||
}
|
||||
}
|
||||
|
||||
void ProfilerTimer::getFrequency(Type &frequency)
|
||||
{
|
||||
frequency = ms_qpcFrequency;
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
@@ -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
|
||||
@@ -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 <vtune/vtuneapi.h>
|
||||
|
||||
// ======================================================================
|
||||
|
||||
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<PauseFunction>(GetProcAddress(ms_module, "VTPause"));
|
||||
ms_resumeFunction = reinterpret_cast<PauseFunction>(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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
+7
@@ -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
|
||||
+1
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/FirstSharedMemoryManager.h"
|
||||
+1
@@ -0,0 +1 @@
|
||||
#include "../../src/shared/MemoryManager.h"
|
||||
+7
@@ -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
|
||||
@@ -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}
|
||||
)
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
// ======================================================================
|
||||
//
|
||||
// OsNewDel.cpp
|
||||
//
|
||||
// Copyright 2002 Sony Online Entertainment
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#include "sharedMemoryManager/FirstSharedMemoryManager.h"
|
||||
#include "sharedMemoryManager/MemoryManager.h"
|
||||
#include "sharedMemoryManager/OsNewDel.h"
|
||||
|
||||
#include <malloc.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
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<uint32>(__builtin_return_address(0)), false, false);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void *operator new(size_t size) throw (std::bad_alloc)
|
||||
{
|
||||
return MemoryManager::allocate(size, reinterpret_cast<uint32>(__builtin_return_address(0)), false, true);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void *operator new[](size_t size) throw (std::bad_alloc)
|
||||
{
|
||||
return MemoryManager::allocate(size, reinterpret_cast<uint32>(__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<uint32>(__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<uint32>(__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);
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
// ======================================================================
|
||||
//
|
||||
// FirstMemoryManager.cpp
|
||||
// copyright (c) 2001 Sony Online Entertainment
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#include "sharedMemoryManager/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
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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 <new>
|
||||
#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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
// ======================================================================
|
||||
|
||||
@@ -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<void>(size);
|
||||
return placement;
|
||||
}
|
||||
|
||||
inline void operator delete(void *pointer, void *placement)
|
||||
{
|
||||
static_cast<void>(pointer);
|
||||
static_cast<void>(placement);
|
||||
}
|
||||
|
||||
#endif // __PLACEMENT_NEW_INLINE
|
||||
|
||||
// ======================================================================
|
||||
|
||||
#endif INCLUDED_OsNewDel_H
|
||||
|
||||
Reference in New Issue
Block a user