Initial commits of the sharedDebug and sharedMemoryManager projects

This commit is contained in:
Anonymous
2014-01-13 09:07:22 -07:00
parent 5940c017b7
commit b632e73284
91 changed files with 12704 additions and 0 deletions
@@ -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(&currentTime, 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