1 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */ 2 3 #ifndef SYNC_DEBUGGER_H 4 #define SYNC_DEBUGGER_H 5 6 #ifdef SYNCDEBUG 7 8 #include <assert.h> 9 #include <deque> 10 #include <vector> 11 #include <boost/cstdint.hpp> 12 13 /** 14 * @brief sync debugger class 15 * 16 * The sync debugger keeps track of the results of the last assignments of 17 * synced variables, and, if compiled with HAVE_BACKTRACE, it keeps a backtrace 18 * for every assignment. This allows communication between client and server 19 * to figure out which exact assignment (including backtrace) was the first 20 * assignment to differ between client and server. 21 */ 22 class CSyncDebugger { 23 24 public: 25 26 /** 27 * @brief get sync debugger instance 28 * 29 * The sync debugger is a singleton (for now). 30 * @return the instance 31 */ 32 static CSyncDebugger* GetInstance(); 33 34 private: 35 36 enum { 37 MAX_STACK = 5, ///< Maximum number of stackframes per HistItemWithBacktrace. 38 BLOCK_SIZE = 2048, ///< Number of \p HistItem per block history. 39 HISTORY_SIZE = 2048, ///< Number of blocks of the entire history. 40 }; 41 42 /** 43 * @brief a clientside history item representing one assignment 44 * 45 * One clientside item in the history (without backtrace) is 46 * represented by this structure. 47 */ 48 struct HistItem { 49 /// Checksum (XOR of 32 bit dwords of the data), 50 /// or the data itself if it is 32 bits or less. 51 unsigned data; 52 }; 53 54 /** 55 * @brief a serverside history item representing one assignment 56 * 57 * One serverside item in the history (with backtrace) is represented 58 * by this structure. 59 */ 60 struct HistItemWithBacktrace: public HistItem { 61 const char* op; ///< Pointer to short static string giving operator type (e.g. "+="). 62 unsigned frameNum; ///< gs->frameNum at the time this entry was committed. 63 unsigned bt_size; ///< Number of entries in the stacktrace. 64 void* bt[MAX_STACK]; ///< The stacktrace (frame pointers). 65 }; 66 67 private: 68 69 // client thread 70 71 /** 72 * @brief the history on clients 73 * 74 * This points to the assignment history that doesn't have backtraces. 75 * It is used on platforms which don't have a backtrace() function 76 * (Windows) and on game clients. The game host uses historybt instead. 77 * 78 * Results of the last HISTORY_SIZE * BLOCK_SIZE = 2048 * 2048 = 4194304 79 * assignments to synced variables are stored in it. 80 */ 81 HistItem* history; 82 83 /** 84 * @brief the history on the host 85 * 86 * This points to the assignment history that does have backtraces. 87 * It is used when running as server on platforms that have a 88 * backtrace() function. Game clients use the history array instead. 89 * 90 * Results of the last 4194304 assignments to synced variables are 91 * stored in it, with a backtrace attached to each of these results. 92 */ 93 HistItemWithBacktrace* historybt; 94 95 unsigned historyIndex; ///< Where are we in the history buffer? 96 volatile bool disable_history; ///< Volatile because it is read by server thread and written by client thread. 97 bool may_enable_history; ///< Is it safe already to set disable_history = false? 98 boost::uint64_t flop; ///< Current (local) operation number. 99 100 // server thread 101 102 struct PlayerStruct 103 { 104 std::vector<unsigned> checksumResponses; 105 boost::uint64_t remoteFlop; 106 std::vector<unsigned> remoteHistory; PlayerStructPlayerStruct107 PlayerStruct() : remoteFlop(0) {} 108 }; 109 typedef std::vector<PlayerStruct> PlayerVec; 110 PlayerVec players; 111 std::deque<unsigned> requestedBlocks; ///< We are processing these blocks. 112 std::deque<unsigned> pendingBlocksToRequest; ///< We still need to receive these blocks (slowly emptied). 113 bool waitingForBlockResponse; ///< Are we still waiting for a block response? 114 115 private: 116 117 // don't construct or copy 118 CSyncDebugger(); 119 CSyncDebugger(const CSyncDebugger&); 120 CSyncDebugger& operator=(const CSyncDebugger&); 121 ~CSyncDebugger(); 122 123 /** 124 * @brief output a backtrace to the log 125 * 126 * Writes the backtrace attached to history item # index to the log. 127 * The backtrace is prefixed with prefix. 128 */ 129 void Backtrace(int index, const char* prefix) const; 130 /** 131 * @brief get a checksum for a backtrace in the history 132 * 133 * @return a checksum for backtrace # index in the history. 134 */ 135 unsigned GetBacktraceChecksum(int index) const; 136 /** 137 * @brief second step after desync 138 * 139 * Called by client to send a response to a checksum request. 140 */ 141 void ClientSendChecksumResponse(); 142 /** 143 * @brief third step after desync 144 * 145 * Called by server after all checksum responses have been received. 146 * Compares the checksumResponses and figures out which blocks are out 147 * of sync (have different checksum). For these blocks requests are 148 * queued which will be send next frames (one request at a time, see 149 * CSyncDebugger::ServerHandlePendingBlockRequests()). 150 */ 151 void ServerQueueBlockRequests(); 152 /** 153 * @brief fourth step after desync 154 * 155 * Called by client to send a response to a block request. 156 */ 157 void ClientSendBlockResponse(int block); 158 /** 159 * @brief fifth step after desync 160 * 161 * Called each time a set of blockResponses (one for every client) is 162 * received. 163 * If there are no more pendingBlocksToRequest, 164 * it triggers the sixth step, ServerDumpStack(). 165 */ 166 void ServerReceivedBlockResponses(); 167 /** 168 * @brief sixth step after desync 169 * 170 * Called by server once all blockResponses are received. It dumps a 171 * backtrace to the logger for every checksum mismatch in the block 172 * which was out of sync. The backtraces are passed to the logger in a 173 * fairly simple form consisting basically only of hexadecimal 174 * addresses. The logger class resolves those to function, file-name 175 * & line number. 176 */ 177 void ServerDumpStack(); 178 179 public: 180 181 /** 182 * @brief the backbone of the sync debugger 183 * 184 * This function adds an item to the history and appends a backtrace and 185 * an operator (op) to it. p must point to the data to checksum and size 186 * must be the size of that data. 187 */ 188 void Sync(const void* p, unsigned size, const char* op); 189 190 /** 191 * @brief initialize 192 * 193 * Initialize the sync debugger. 194 * @param useBacktrace true for a server (requires approx. 160 MegaBytes 195 * on 32 bit systems and 256 MegaBytes on 64 bit systems) 196 * and false for a client (requires only 32 MegaBytes extra) 197 */ 198 void Initialize(bool useBacktrace, unsigned numPlayers); 199 /** 200 * @brief first step after desync 201 * 202 * Does nothing 203 */ 204 void ServerTriggerSyncErrorHandling(int serverframenum); 205 /** 206 * @brief serverside network receiver 207 * 208 * Plugin for the CGameServer network code in GameServer.cpp. 209 * @return the number of bytes read from the network stream 210 */ 211 bool ServerReceived(const unsigned char* inbuf); 212 /** 213 * @brief helper for the third step 214 * 215 * Must be called by the server in GameServer.cpp once every frame to 216 * handle queued block requests. 217 * @see #ServerQueueBlockRequests() 218 */ 219 void ServerHandlePendingBlockRequests(); 220 /** 221 * @brief clientside network receiver 222 * 223 * Plugin for the CGame network code in Game.cpp. 224 * @return the number of bytes read from the network stream 225 */ 226 bool ClientReceived(const unsigned char* inbuf); 227 /** 228 * @brief re-enable the history 229 * 230 * Restart the sync debugger lifecycle, so it can be used again (if the 231 * sync errors are resolved somehow or you were just testing it using 232 * /fakedesync). 233 * 234 * Called after typing '/reset' in chat area. 235 */ 236 void Reset(); 237 238 friend class CSyncedPrimitiveBase; 239 }; 240 241 #endif // SYNCDEBUG 242 243 #endif // SYNC_DEBUGGER_H 244