mirror of
https://bitbucket.org/theswgsource/src-1.2.git
synced 2026-07-31 01:15:48 -04:00
Initial commits of the sharedDebug and sharedMemoryManager projects
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user