📄 syncdebugger.cpp
字号:
ClientSendBlockResponse(*(unsigned short*)&inbuf[1]);
logger.AddLine("Client: block response sent for block %d", *(unsigned short*)&inbuf[1]);
// simple progress indication
logOutput.Print("[SD] Client: %d / %d", *(unsigned short*)&inbuf[3], *(unsigned short*)&inbuf[5]);
}
length = 7;
break;
case NETMSG_SD_RESET:
logger.CloseSession();
logOutput.Print("[SD] Client: Done!");
// disable_history = false;
may_enable_history = true;
if (gu->autoQuit) {
logOutput.Print("[SD] Client: Automatical quit enforced from commandline");
globalQuit = true;
}
length = 1;
break;
default:
logOutput.Print("[SD] Client: invalid msg");
length = 0;
break;
}
return length;
}
/**
* @brief first step after desync
*
* Called by server to trigger sync error handling.
* It pauses the game and sends a checksum request to all clients.
*/
void CSyncDebugger::ServerTriggerSyncErrorHandling(int serverframenum)
{
if (!disable_history) {
//this will set disable_history = true once received so only one sync errors is handled at a time.
serverNet->SendPause(gu->myPlayerNum, true);
serverNet->SendData< int >(NETMSG_SD_CHKREQUEST, serverframenum);
}
}
/**
* @brief second step after desync
*
* Called by client to send a response to a checksum request.
*/
void CSyncDebugger::ClientSendChecksumResponse()
{
std::vector<unsigned> checksums;
for (unsigned i = 0; i < HISTORY_SIZE; ++i) {
unsigned checksum = 0;
for (unsigned j = 0; j < BLOCK_SIZE; ++j) {
if (historybt)
checksum = 33 * checksum + historybt[BLOCK_SIZE * i + j].chk;
else checksum = 33 * checksum + history[BLOCK_SIZE * i + j].chk;
}
checksums.push_back(checksum);
}
net->SendSTLData< unsigned char, Uint64, std::vector<unsigned> >(NETMSG_SD_CHKRESPONSE, gu->myPlayerNum, flop, checksums);
}
/**
* @brief third step after desync
*
* Called by server after all checksum responses have been received.
* Compares the checksumResponses and figures out which blocks are out of sync
* (have different checksum). For these blocks requests are queued which will
* be send next frames (one request at a time, see
* CSyncDebugger::ServerHandlePendingBlockRequests()).
*/
void CSyncDebugger::ServerQueueBlockRequests()
{
Uint64 correctFlop = 0;
for (int j = 0; j < gs->activeTeams; ++j) {
if (correctFlop) {
if (remoteFlop[j] != correctFlop)
logger.AddLine("Server: bad flop# %llu instead of %llu for player %d", remoteFlop[j], correctFlop, j);
} else {
correctFlop = remoteFlop[j];
}
}
unsigned i = ((unsigned)(correctFlop % (HISTORY_SIZE * BLOCK_SIZE)) / BLOCK_SIZE) + 1, c = 0;
for (; c < HISTORY_SIZE; ++i, ++c) {
unsigned correctChecksum = 0;
if (i == HISTORY_SIZE) i = 0;
for (int j = 0; j < gs->activeTeams; ++j) {
if (correctChecksum && checksumResponses[j][i] != correctChecksum) {
pendingBlocksToRequest.push_back(i);
break;
}
correctChecksum = checksumResponses[j][i];
}
}
if (!pendingBlocksToRequest.empty()) {
logger.AddLine("Server: blocks: %u equal, %u not equal", HISTORY_SIZE - pendingBlocksToRequest.size(), pendingBlocksToRequest.size());
requestedBlocks = pendingBlocksToRequest;
// we know the first FPU bug occured in block # ii, so we send out a block request for it.
// serverNet->SendData<unsigned> (NETMSG_SD_BLKREQUEST, ii);
} else {
logger.AddLine("Server: huh, all blocks equal?!?");
serverNet->SendData(NETMSG_SD_RESET);
}
//cleanup
for (int j = 0; j < MAX_PLAYERS; ++j)
checksumResponses[j].clear();
}
/**
* @brief helper for the third step
*
* Must be called by the server in GameServer.cpp once every frame to handle
* queued block requests (see CSyncDebugger::ServerQueueBlockRequests()).
*/
void CSyncDebugger::ServerHandlePendingBlockRequests()
{
if (!pendingBlocksToRequest.empty() && !waitingForBlockResponse) {
// last two shorts are for progress indication
serverNet->SendData<unsigned short, unsigned short, unsigned short> (NETMSG_SD_BLKREQUEST, pendingBlocksToRequest.front(), requestedBlocks.size() - pendingBlocksToRequest.size() + 1, requestedBlocks.size());
waitingForBlockResponse = true;
}
}
/**
* @brief fourth step after desync
*
* Called by client to send a response to a block request.
*/
void CSyncDebugger::ClientSendBlockResponse(int block)
{
std::vector<unsigned> checksums;
for (unsigned i = 0; i < BLOCK_SIZE; ++i) {
if (historybt)
checksums.push_back(historybt[BLOCK_SIZE * block + i].chk);
else checksums.push_back(history[BLOCK_SIZE * block + i].chk);
}
net->SendSTLData< unsigned char, std::vector<unsigned> >(NETMSG_SD_BLKRESPONSE, gu->myPlayerNum, checksums);
}
/**
* @brief fifth step after desync
*
* Called each time a set of blockResponses (one for every client) is received.
* If there are no more pendingBlocksToRequest, it triggers the sixth step,
* ServerDumpStack().
*/
void CSyncDebugger::ServerReceivedBlockResponses()
{
pendingBlocksToRequest.pop_front();
waitingForBlockResponse = false;
// analyse data and reset if this was the last block response
if (pendingBlocksToRequest.empty())
ServerDumpStack();
}
/**
* @brief sixth step after desync
*
* Called by server once all blockResponses are received. It dumps a backtrace
* to the logger for every checksum mismatch in the block which was out of
* sync. The backtraces are passed to the logger in a fairly simple form
* consisting basically only of hexadecimal addresses. The logger class
* resolves those to function, filename & line number.
*/
void CSyncDebugger::ServerDumpStack()
{
// first calculate start iterator...
unsigned posInHistory = (unsigned)(remoteFlop[0] % (HISTORY_SIZE * BLOCK_SIZE));
logger.AddLine("Server: position in history: %u", posInHistory);
unsigned blockNr = posInHistory / BLOCK_SIZE;
unsigned virtualBlockNr = 0; // block nr in remoteHistory (which skips unchanged blocks)
for (; virtualBlockNr < requestedBlocks.size() && requestedBlocks[virtualBlockNr] != blockNr; ++virtualBlockNr) {}
unsigned virtualPosInHistory = (virtualBlockNr * BLOCK_SIZE) + (posInHistory % BLOCK_SIZE) + 1;
unsigned virtualHistorySize = remoteHistory[0].size();
if (virtualBlockNr >= requestedBlocks.size())
virtualPosInHistory = virtualHistorySize;
unsigned ndif = 0; // number of differences
assert(virtualPosInHistory <= virtualHistorySize);
// we make a pool of backtraces (to merge identical ones)
unsigned curBacktrace = 0;
std::map<unsigned, unsigned> checksumToIndex;
std::map<unsigned, unsigned> indexToHistPos;
// then loop from virtualPosInHistory to virtualHistorySize and from 0 to virtualPosInHistory.
for (unsigned i = virtualPosInHistory, c = 0; c < virtualHistorySize; ++i, ++c) {
unsigned correctChecksum = 0;
if (i == virtualHistorySize) i = 0;
bool err = false;
for (int j = 0; j < gs->activeTeams; ++j) {
if (correctChecksum && remoteHistory[j][i] != correctChecksum) {
if (historybt) {
virtualBlockNr = i / BLOCK_SIZE;
blockNr = requestedBlocks[virtualBlockNr];
unsigned histPos = blockNr * BLOCK_SIZE + i % BLOCK_SIZE;
unsigned checksum = GetBacktraceChecksum(histPos);
std::map<unsigned, unsigned>::iterator it = checksumToIndex.find(checksum);
if (it == checksumToIndex.end()) {
++curBacktrace;
checksumToIndex[checksum] = curBacktrace;
indexToHistPos[curBacktrace] = histPos;
}
logger.AddLine("Server: chk %08X, %15.8e instead of %08X, %15.8e, frame %06u, backtrace %u in \"%s\"", remoteHistory[j][i], *(float*)&remoteHistory[j][i], correctChecksum, *(float*)&correctChecksum, historybt[histPos].frameNum, checksumToIndex[checksum], historybt[histPos].op);
} else {
logger.AddLine("Server: chk %08X, %15.8e instead of %08X, %15.8e", remoteHistory[j][i], *(float*)&remoteHistory[j][i], correctChecksum, *(float*)&correctChecksum);
}
err = true;
} else {
correctChecksum = remoteHistory[j][i];
}
}
if (err) {
++ndif;
}
}
if (ndif)
logger.AddLine("Server: chks: %d equal, %d not equal", virtualHistorySize - ndif, ndif);
else
// This is impossible (internal error).
// Server first finds there are differing blocks, then all checksums equal??
logger.AddLine("Server: huh, all checksums equal?!? (INTERNAL ERROR)");
//cleanup
for (int j = 0; j < MAX_PLAYERS; ++j)
remoteHistory[j].clear();
if (historybt) {
// output backtraces we collected earlier this function
for (std::map<unsigned, unsigned>::iterator it = indexToHistPos.begin(); it != indexToHistPos.end(); ++it) {
logger.AddLine("Server: === Backtrace %u ===", it->first);
Backtrace(it->second, "Server: ");
}
}
// and reset
serverNet->SendData(NETMSG_SD_RESET);
logger.AddLine("Server: Done!");
logger.CloseSession();
logOutput.Print("[SD] Server: Done!");
}
/**
* @brief helper network plugin function
*
* @return the size of the sync debugger related net message in inbuf
* at index 0 (if any), zero otherwise.
*/
int CSyncDebugger::GetMessageLength(const unsigned char* inbuf) const
{
switch (inbuf[0]) {
case NETMSG_SD_CHKREQUEST:
return 5;
case NETMSG_SD_BLKREQUEST:
return 7;
case NETMSG_SD_CHKRESPONSE:
case NETMSG_SD_BLKRESPONSE:
return *(short*)&inbuf[1];
case NETMSG_SD_RESET:
return 1;
default:
return 0;
}
}
/**
* @brief re-enable the history
*
* Restart the sync debugger lifecycle, so it can be used again (if the sync
* errors are resolved somehow or you were just testing it using .fakedesync).
*
* Called after typing '.reset' in chat area.
*/
void CSyncDebugger::Reset()
{
if (may_enable_history)
disable_history = false;
}
#endif // SYNCDEBUG
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -