📄 syncdebugger.cpp
字号:
/* Author: Tobi Vollebregt */
#include "StdAfx.h"
#ifdef SYNCDEBUG
#include "LogOutput.h"
#include "System/GlobalStuff.h"
#include "System/Net.h"
#include "SyncDebugger.h"
#include "Logger.h"
#ifndef WIN32
/* for backtrace() function */
# include <execinfo.h>
# define HAVE_BACKTRACE
#elif defined __MINGW32__
/* from backtrace.c: */
extern "C" int backtrace (void **array, int size);
# define HAVE_BACKTRACE
#else
# undef HAVE_BACKTRACE
#endif
#define LOGFILE_SERVER "syncdebug-server.log"
#define LOGFILE_CLIENT "syncdebug-client.log"
// externals
extern bool globalQuit;
/**
* @brief logging instance
*/
CLogger logger;
/**
* @brief get sync debugger instance
*
* The sync debugger is a singleton (for now).
* @return the instance
*/
CSyncDebugger* CSyncDebugger::GetInstance() {
static CSyncDebugger instance;
return &instance;
}
/**
* @brief default-noarg-constructor
*/
CSyncDebugger::CSyncDebugger():
history(NULL), historybt(NULL), historyIndex(0),
disable_history(false), may_enable_history(false),
flop(0), waitingForBlockResponse(false)
{
// Need to allocate those here instead of having them inlined in the class,
// because of #include dependency: MAX_PLAYERS is in GlobalStuff.h, which
// needs creg.h. But creg.h needs SyncedPrimitive.h, which needs
// SyncDebugger.h. Hence we can't use MAX_PLAYERS in SyncDebugger.h. :-)
checksumResponses = new std::vector<unsigned> [MAX_PLAYERS];
remoteHistory = new std::vector<unsigned> [MAX_PLAYERS];
remoteFlop = new Uint64 [MAX_PLAYERS];
}
/**
* @brief destructor
*/
CSyncDebugger::~CSyncDebugger()
{
delete[] history;
delete[] historybt;
delete[] checksumResponses;
delete[] remoteHistory;
delete[] remoteFlop;
}
/**
* @brief initialize
*
* Initialize the sync debugger. Pass true for a server (this requires approx.
* 144 megabytes on 32 bit systems and 240 megabytes on 64 bit systems) and
* false for a client (requires only 16 megabytes extra).
*/
void CSyncDebugger::Initialize(bool useBacktrace)
{
delete[] history;
history = 0;
delete[] historybt;
historybt = 0;
#ifdef HAVE_BACKTRACE
if (useBacktrace) {
historybt = new HistItemWithBacktrace[HISTORY_SIZE * BLOCK_SIZE];
memset(historybt, 0, HISTORY_SIZE * BLOCK_SIZE * sizeof(HistItemWithBacktrace));
} else
#endif
{
history = new HistItem[HISTORY_SIZE * BLOCK_SIZE];
memset(history, 0, HISTORY_SIZE * BLOCK_SIZE * sizeof(HistItem));
}
//cleanup
historyIndex = 0;
disable_history = false;
may_enable_history = false;
flop = 0;
for (int j = 0; j < MAX_PLAYERS; ++j) {
checksumResponses[j].clear();
remoteHistory[j].clear();
remoteFlop[j] = 0;
}
pendingBlocksToRequest.clear();
waitingForBlockResponse = false;
// init logger
logger.SetFilename(useBacktrace ? LOGFILE_SERVER : LOGFILE_CLIENT);
}
/**
* @brief the backbone of the sync debugger
*
* This function adds an item to the history and appends a backtrace and an
* operator (op) to it. p must point to the data to checksum and size must be
* the size of that data.
*/
void CSyncDebugger::Sync(void* p, unsigned size, const char* op)
{
if (!history && !historybt)
return;
HistItem* h = &history[historyIndex];
#ifdef HAVE_BACKTRACE
if (historybt) {
// dirty hack to skip the uppermost 2 or 4 (32 resp. 64 bit) frames without memcpy'ing the whole backtrace...
const int frameskip = (12 + sizeof(void*)) / sizeof(void*);
historybt[historyIndex].bt_size = backtrace(historybt[historyIndex].bt - frameskip, MAX_STACK + frameskip) - frameskip;
historybt[historyIndex].op = op;
historybt[historyIndex].frameNum = gs->frameNum;
h = &historybt[historyIndex];
}
#endif
unsigned i = 0;
h->chk = 0;
for (; i < (size & ~3); i += 4)
h->chk ^= *(unsigned*) ((unsigned char*) p + i);
for (; i < size; ++i)
h->chk ^= *((unsigned char*) p + i);
if (++historyIndex == HISTORY_SIZE * BLOCK_SIZE)
historyIndex = 0; // wrap around
++flop;
}
/**
* @brief output a backtrace to the log
*
* Writes the backtrace attached to history item # index to the log.
* The backtrace is prefixed with prefix.
*/
void CSyncDebugger::Backtrace(int index, const char* prefix) const
{
if (historybt) {
for (unsigned i = 0; i < historybt[index].bt_size; ++i) {
// the "{%p}" part is resolved to "functionname [filename:lineno]"
// by the CLogger class.
logger.AddLine("%s#%u {%p}", prefix, i, historybt[index].bt[i]);
}
}
}
/**
* @brief get a checksum for a backtrace in the history
*
* @return a checksum for backtrace # index in the history.
*/
unsigned CSyncDebugger::GetBacktraceChecksum(int index) const
{
unsigned checksum = 0;
const unsigned* p = (const unsigned*) historybt[index].bt;
for (unsigned i = 0; i < (sizeof(void*)/sizeof(unsigned)) * historybt[index].bt_size; ++i, ++p)
checksum = 33 * checksum + *p;
return checksum;
}
/**
* @brief serverside network receiver
*
* Plugin for the CGameServer network code in GameServer.cpp.
* @return the number of bytes read from the network stream
*/
int CSyncDebugger::ServerReceived(const unsigned char* inbuf)
{
int length = 0;
switch (inbuf[0]) {
case NETMSG_SD_CHKRESPONSE:
if (*(short*)&inbuf[1] != HISTORY_SIZE * sizeof(unsigned) + 12) {
logger.AddLine("Server: received checksum response of %d instead of %d bytes", *(short*)&inbuf[1], HISTORY_SIZE * 4 + 12);
} else {
int player = inbuf[3];
if(player >= gs->activeTeams || player < 0) {
logger.AddLine("Server: got invalid playernum %d in checksum response", player);
} else {
const unsigned* begin = (unsigned*)&inbuf[12];
const unsigned* end = begin + HISTORY_SIZE;
checksumResponses[player].resize(HISTORY_SIZE);
std::copy(begin, end, checksumResponses[player].begin());
remoteFlop[player] = *(Uint64*)&inbuf[4];
int i = 0;
while (i < gs->activeTeams && !checksumResponses[i].empty()) ++i;
if (i == gs->activeTeams) {
ServerQueueBlockRequests();
logger.AddLine("Server: checksum responses received; %d block requests queued", pendingBlocksToRequest.size());
}
}
}
length = *(short*)&inbuf[1];
break;
case NETMSG_SD_BLKRESPONSE:
if (*(short*)&inbuf[1] != BLOCK_SIZE * sizeof(unsigned) + 4) {
logger.AddLine("Server: received block response of %d instead of %d bytes", *(short*)&inbuf[1], BLOCK_SIZE * 4 + 4);
} else {
int player = inbuf[3];
if(player >= gs->activeTeams || player < 0) {
logger.AddLine("Server: got invalid playernum %d in block response", player);
} else {
const unsigned* begin = (unsigned*)&inbuf[4];
const unsigned* end = begin + BLOCK_SIZE;
unsigned size = remoteHistory[player].size();
remoteHistory[player].resize(size + BLOCK_SIZE);
std::copy(begin, end, remoteHistory[player].begin() + size);
int i = 0;
size += BLOCK_SIZE;
while (i < gs->activeTeams && size == remoteHistory[i].size()) ++i;
if (i == gs->activeTeams) {
logger.AddLine("Server: block responses received");
ServerReceivedBlockResponses();
}
}
}
length = *(short*)&inbuf[1];
break;
default:
logger.AddLine("Server: invalid msg");
length = 0;
break;
}
return length;
}
/**
* @brief clientside network receiver
*
* Plugin for the CGame network code in Game.cpp.
* @return the number of bytes read from the network stream
*/
int CSyncDebugger::ClientReceived(const unsigned char* inbuf)
{
int length = 0;
switch (inbuf[0]) {
case NETMSG_SD_CHKREQUEST:
if (gs->frameNum != *(int*)&inbuf[1]) {
logger.AddLine("Client: received checksum request for frame %d instead of %d", *(int*)&inbuf[1], gs->frameNum);
} else {
disable_history = true; // no more additions to the history until we're done
may_enable_history = false;
ClientSendChecksumResponse();
logger.AddLine("Client: checksum response sent");
}
length = 5;
break;
case NETMSG_SD_BLKREQUEST:
if (*(unsigned short*)&inbuf[1] >= HISTORY_SIZE) {
logger.AddLine("Client: invalid block number %d in block request", *(unsigned short*)&inbuf[1]);
} else {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -