1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
|
/* Author: Tobi Vollebregt */
#ifndef SYNCDEBUGGER_H
#define SYNCDEBUGGER_H
#ifdef SYNCDEBUG
#include <assert.h>
#include <deque>
#include <vector>
/**
* @brief sync debugger class
*
* The sync debugger keeps track of the results of the last assignments of
* synced variables, and, if compiled with HAVE_BACKTRACE, it keeps a backtrace
* for every assignment. This allows communication between client and server
* to figure out which exact assignment (including backtrace) was the first
* assignment to differ between client and server.
*/
class CSyncDebugger {
public:
static CSyncDebugger* GetInstance();
private:
enum {
MAX_STACK = 5, ///< Maximum number of stackframes per HistItemWithBacktrace.
BLOCK_SIZE = 2048, ///< Number of \p HistItem per block history.
HISTORY_SIZE = 2048, ///< Number of blocks of the entire history.
};
/**
* @brief a clientside history item representing one assignment
*
* One clientside item in the history (without backtrace) is
* represented by this structure.
*/
struct HistItem {
unsigned chk; ///< Checksum (XOR of 32 bit dwords of the data).
unsigned data; ///< First four bytes of data
};
/**
* @brief a serverside history item representing one assignment
*
* One serverside item in the history (with backtrace) is represented
* by this structure.
*/
struct HistItemWithBacktrace: public HistItem {
const char* op; ///< Pointer to short static string giving operator type (e.g. "+=").
unsigned frameNum; ///< gs->frameNum at the time this entry was committed.
unsigned bt_size; ///< Number of entries in the stacktrace.
void* bt[MAX_STACK]; ///< The stacktrace (frame pointers).
};
private:
// client thread
/**
* @brief the history on clients
*
* This points to the assignment history that doesn't have backtraces.
* It is used on platforms which don't have a backtrace() function
* (Windows) and on game clients. The game host uses historybt instead.
*
* Results of the last HISTORY_SIZE * BLOCK_SIZE = 2048 * 2048 = 4194304
* assignments to synced variables are stored in it.
*
* The size of the array is HISTORY_SIZE * BLOCK_SIZE * sizeof(HistItem),
* that is 2048*2048*4 = 16 megabytes.
*/
HistItem* history;
/**
* @brief the history on the host
*
* This points to the assignment history that does have backtraces.
* It is used when running as server on platforms that have a
* backtrace() function. Game clients use the history array instead.
*
* Results of the last 4194304 assignments to synced variables are
* stored in it, with a backtrace attached to each of these results.
*
* This makes the size of the array HISTORY_SIZE * BLOCK_SIZE *
* sizeof(HistItemWithBacktrace), that is
* 2048*2048*(8+(MAX_STACK+1)*sizeof(void*)) = 128 megabytes on 32 bit
* systems and 224 megabytes on 64 bit systems.
*/
HistItemWithBacktrace* historybt;
unsigned historyIndex; ///< Where are we in the history buffer?
volatile bool disable_history; ///< Volatile because it is read by server thread and written by client thread.
bool may_enable_history; ///< Is it safe already to set disable_history = false?
boost::uint64_t flop; ///< Current (local) operation number.
// server thread
struct PlayerStruct
{
std::vector<unsigned> checksumResponses;
boost::uint64_t remoteFlop;
std::vector<unsigned> remoteHistory;
};
typedef std::vector<PlayerStruct> playerVec;
playerVec players;
std::deque<unsigned> requestedBlocks; ///< We are processing these blocks.
std::deque<unsigned> pendingBlocksToRequest; ///< We still need to receive these blocks (slowly emptied).
bool waitingForBlockResponse; ///< Are we still waiting for a block response?
private:
// don't construct or copy
CSyncDebugger();
CSyncDebugger(const CSyncDebugger&);
CSyncDebugger& operator=(const CSyncDebugger&);
~CSyncDebugger();
void Backtrace(int index, const char* prefix) const;
unsigned GetBacktraceChecksum(int index) const;
void ClientSendChecksumResponse();
void ServerQueueBlockRequests();
void ClientSendBlockResponse(int block);
void ServerReceivedBlockResponses();
void ServerDumpStack();
void Sync(void* p, unsigned size, const char* op);
public:
void Initialize(bool useBacktrace, unsigned numPlayers);
void ServerTriggerSyncErrorHandling(int serverframenum);
bool ServerReceived(const unsigned char* inbuf);
void ServerHandlePendingBlockRequests();
bool ClientReceived(const unsigned char* inbuf);
void Reset();
friend class CSyncedPrimitiveBase;
};
#endif // SYNCDEBUG
#endif // SYNCDEBUGGER_H
|