diff --git a/src/company_cmd.cpp b/src/company_cmd.cpp index 965816ec3e..900ccb739b 100644 --- a/src/company_cmd.cpp +++ b/src/company_cmd.cpp @@ -938,7 +938,7 @@ CommandCost CmdCompanyCtrl(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 InvalidateWindowData(WC_CLIENT_LIST, 0); InvalidateWindowClassesData(WC_DEPARTURES_BOARD, 0); - CheckCaches(true, nullptr); + CheckCaches(true, nullptr, CHECK_CACHE_ALL | CHECK_CACHE_EMIT_LOG); break; } diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index 4cec71eae2..6d831ca5af 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -2688,7 +2688,7 @@ DEF_CONSOLE_CMD(ConCheckCaches) if (broadcast) { DoCommandP(0, 0, 0, CMD_DESYNC_CHECK); } else { - CheckCaches(true, nullptr); + CheckCaches(true, nullptr, CHECK_CACHE_ALL | CHECK_CACHE_EMIT_LOG); } return true; diff --git a/src/crashlog.cpp b/src/crashlog.cpp index 0fbd23ea91..db68d5dd7e 100644 --- a/src/crashlog.cpp +++ b/src/crashlog.cpp @@ -535,6 +535,62 @@ char *CrashLog::FillDesyncCrashLog(char *buffer, const char *last, const DesyncE return buffer; } +/** + * Fill the crash log buffer with all data of an inconsistency event. + * @param buffer The begin where to write at. + * @param last The last position in the buffer to write to. + * @return the position of the \c '\0' character after the buffer. + */ +char *CrashLog::FillInconsistencyLog(char *buffer, const char *last, const InconsistencyExtraInfo &info) const +{ + time_t cur_time = time(nullptr); + buffer += seprintf(buffer, last, "*** OpenTTD Inconsistency Report ***\n\n"); + + buffer += seprintf(buffer, last, "Inconsistency at: %s", asctime(gmtime(&cur_time))); + +#ifdef USE_SCOPE_INFO + buffer += WriteScopeLog(buffer, last); +#endif + + buffer += seprintf(buffer, last, "In game date: %i-%02i-%02i (%i, %i) (DL: %u)\n", _cur_date_ymd.year, _cur_date_ymd.month + 1, _cur_date_ymd.day, _date_fract, _tick_skip_counter, _settings_game.economy.day_length_factor); + if (_game_load_time != 0) { + buffer += seprintf(buffer, last, "Game loaded at: %i-%02i-%02i (%i, %i), %s", + _game_load_cur_date_ymd.year, _game_load_cur_date_ymd.month + 1, _game_load_cur_date_ymd.day, _game_load_date_fract, _game_load_tick_skip_counter, asctime(gmtime(&_game_load_time))); + } + if (_networking && !_network_server) { + extern Date _last_sync_date; + extern DateFract _last_sync_date_fract; + extern uint8 _last_sync_tick_skip_counter; + + YearMonthDay ymd; + ConvertDateToYMD(_last_sync_date, &ymd); + buffer += seprintf(buffer, last, "Last sync at: %i-%02i-%02i (%i, %i)", + ymd.year, ymd.month + 1, ymd.day, _last_sync_date_fract, _last_sync_tick_skip_counter); + } + buffer += seprintf(buffer, last, "\n"); + + buffer = this->LogOpenTTDVersion(buffer, last); + buffer = this->LogOSVersion(buffer, last); + buffer = this->LogCompiler(buffer, last); + buffer = this->LogOSVersionDetail(buffer, last); + buffer = this->LogConfiguration(buffer, last); + buffer = this->LogLibraries(buffer, last); + buffer = this->LogGamelog(buffer, last); + buffer = this->LogRecentNews(buffer, last); + buffer = this->LogCommandLog(buffer, last); + buffer = DumpDesyncMsgLog(buffer, last); + + if (!info.check_caches_result.empty()) { + buffer += seprintf(buffer, last, "CheckCaches:\n"); + for (const std::string &str : info.check_caches_result) { + buffer += seprintf(buffer, last, " %s\n", str.c_str()); + } + } + + buffer += seprintf(buffer, last, "*** End of OpenTTD Inconsistency Report ***\n"); + return buffer; +} + /** * Fill the version info log buffer. * @param buffer The begin where to write at. @@ -758,6 +814,61 @@ bool CrashLog::MakeDesyncCrashLog(const std::string *log_in, std::string *log_ou return ret; } +/** + * Makes an inconsistency log, writes it to a file and then subsequently tries + * to make a crash savegame. It uses DEBUG to write + * information like paths to the console. + * @return true when everything is made successfully. + */ +bool CrashLog::MakeInconsistencyLog(const InconsistencyExtraInfo &info) const +{ + char filename[MAX_PATH]; + char buffer[65536 * 2]; + bool ret = true; + + char name_buffer[64]; + char *name_buffer_date = name_buffer + seprintf(name_buffer, lastof(name_buffer), "inconsistency-"); + time_t cur_time = time(nullptr); + strftime(name_buffer_date, lastof(name_buffer) - name_buffer_date, "%Y%m%dT%H%M%SZ", gmtime(&cur_time)); + + printf("Inconsistency encountered, generating diagnostics log...\n"); + this->FillInconsistencyLog(buffer, lastof(buffer), info); + + bool bret = this->WriteCrashLog(buffer, filename, lastof(filename), name_buffer); + if (bret) { + printf("Inconsistency log written to %s. Please add this file to any bug reports.\n\n", filename); + } else { + printf("Writing inconsistency log failed.\n\n"); + ret = false; + } + + _savegame_DBGL_data = buffer; + _save_DBGC_data = true; + bret = this->WriteSavegame(filename, lastof(filename), name_buffer); + if (bret) { + printf("info savegame written to %s. Please add this file and the last (auto)save to any bug reports.\n\n", filename); + } else { + ret = false; + printf("Writing inconsistency savegame failed. Please attach the last (auto)save to any bug reports.\n\n"); + } + _savegame_DBGL_data = nullptr; + _save_DBGC_data = false; + + if (!(_screen.width < 1 || _screen.height < 1 || _screen.dst_ptr == nullptr)) { + SetScreenshotAuxiliaryText("Inconsistency Log", buffer); + bret = this->WriteScreenshot(filename, lastof(filename), name_buffer); + if (bret) { + printf("Inconsistency screenshot written to %s. Please add this file to any bug reports.\n\n", filename); + } else { + ret = false; + printf("Writing inconsistency screenshot failed.\n\n"); + } + ClearScreenshotAuxiliaryText(); + } + + return ret; +} + /** * Makes a version info log, writes it to a file. It uses DEBUG to write * information like paths to the console. diff --git a/src/crashlog.h b/src/crashlog.h index ee1ed05564..a7c4a99d8d 100644 --- a/src/crashlog.h +++ b/src/crashlog.h @@ -27,6 +27,10 @@ struct DesyncExtraInfo { }; DECLARE_ENUM_AS_BIT_SET(DesyncExtraInfo::Flags) +struct InconsistencyExtraInfo { + std::vector check_caches_result; +}; + /** * Helper class for creating crash logs. */ @@ -130,6 +134,7 @@ public: char *FillCrashLog(char *buffer, const char *last) const; char *FillDesyncCrashLog(char *buffer, const char *last, const DesyncExtraInfo &info) const; + char *FillInconsistencyLog(char *buffer, const char *last, const InconsistencyExtraInfo &info) const; char *FillVersionInfoLog(char *buffer, const char *last) const; bool WriteCrashLog(const char *buffer, char *filename, const char *filename_last, const char *name = "crash", FILE **crashlog_file = nullptr) const; @@ -148,6 +153,7 @@ public: bool MakeCrashLog(); bool MakeDesyncCrashLog(const std::string *log_in, std::string *log_out, const DesyncExtraInfo &info) const; + bool MakeInconsistencyLog(const InconsistencyExtraInfo &info) const; bool MakeVersionInfoLog() const; bool MakeCrashSavegameAndScreenshot() const; @@ -165,6 +171,7 @@ public: static void InitThread(); static void DesyncCrashLog(const std::string *log_in, std::string *log_out, const DesyncExtraInfo &info); + static void InconsistencyLog(const InconsistencyExtraInfo &info); static void VersionInfoLog(); static void SetErrorMessage(const char *message); diff --git a/src/debug_desync.h b/src/debug_desync.h index 98b8760f00..121b831dc0 100644 --- a/src/debug_desync.h +++ b/src/debug_desync.h @@ -16,7 +16,8 @@ enum CheckCachesFlags : uint32 { CHECK_CACHE_NONE = 0, CHECK_CACHE_GENERAL = 1 << 0, CHECK_CACHE_INFRA_TOTALS = 1 << 1, - CHECK_CACHE_ALL = UINT32_MAX, + CHECK_CACHE_ALL = UINT16_MAX, + CHECK_CACHE_EMIT_LOG = 1 << 16, }; DECLARE_ENUM_AS_BIT_SET(CheckCachesFlags) diff --git a/src/economy.cpp b/src/economy.cpp index 6098f835ae..522927041d 100644 --- a/src/economy.cpp +++ b/src/economy.cpp @@ -2288,7 +2288,7 @@ static void DoAcquireCompany(Company *c) delete c; - CheckCaches(true, nullptr); + CheckCaches(true, nullptr, CHECK_CACHE_ALL | CHECK_CACHE_EMIT_LOG); } extern int GetAmountOwnedBy(const Company *c, Owner owner); diff --git a/src/openttd.cpp b/src/openttd.cpp index 0cb3d7221f..c8739b5fde 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -1373,6 +1373,13 @@ void CheckCaches(bool force_check, std::function log, CheckC if (desync_level == 1 && _scaled_date_ticks % 500 != 0) return; } + std::vector saved_messages; + if (flags & CHECK_CACHE_EMIT_LOG) { + log = [&saved_messages](const char *str) { + saved_messages.emplace_back(str); + }; + } + char cclog_buffer[1024]; #define CCLOG(...) { \ seprintf(cclog_buffer, lastof(cclog_buffer), __VA_ARGS__); \ @@ -1763,6 +1770,15 @@ void CheckCaches(bool force_check, std::function log, CheckC } } + if ((flags & CHECK_CACHE_EMIT_LOG) && !saved_messages.empty()) { + InconsistencyExtraInfo info; + info.check_caches_result = std::move(saved_messages); + CrashLog::InconsistencyLog(info); + for (std::string &str : info.check_caches_result) { + LogDesyncMsg(std::move(str)); + } + } + #undef CCLOGV #undef CCLOG } @@ -1779,7 +1795,7 @@ void CheckCaches(bool force_check, std::function log, CheckC CommandCost CmdDesyncCheck(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { if (flags & DC_EXEC) { - CheckCaches(true, nullptr); + CheckCaches(true, nullptr, CHECK_CACHE_ALL | CHECK_CACHE_EMIT_LOG); } return CommandCost(); @@ -1836,7 +1852,7 @@ void StateGameLoop() SaveOrLoad(name, SLO_SAVE, DFT_GAME_FILE, AUTOSAVE_DIR, false); } - CheckCaches(false, nullptr); + CheckCaches(false, nullptr, CHECK_CACHE_ALL | CHECK_CACHE_EMIT_LOG); /* All these actions has to be done from OWNER_NONE * for multiplayer compatibility */ diff --git a/src/os/macosx/crashlog_osx.cpp b/src/os/macosx/crashlog_osx.cpp index 1811aa5c41..5d7c9993b1 100644 --- a/src/os/macosx/crashlog_osx.cpp +++ b/src/os/macosx/crashlog_osx.cpp @@ -492,6 +492,13 @@ void CDECL HandleCrash(int signum, siginfo_t *si, void *context) log.MakeDesyncCrashLog(log_in, log_out, info); } +/* static */ void CrashLog::InconsistencyLog(const InconsistencyExtraInfo &info) +{ + CrashLogOSX log(CrashLogOSX::DesyncTag{}); + log.MakeInconsistencyLog(info); +} + + /* static */ void CrashLog::VersionInfoLog() { CrashLogOSX log(CrashLogOSX::DesyncTag{}); diff --git a/src/os/unix/crashlog_unix.cpp b/src/os/unix/crashlog_unix.cpp index 94ce535a9d..bfaee844d0 100644 --- a/src/os/unix/crashlog_unix.cpp +++ b/src/os/unix/crashlog_unix.cpp @@ -628,6 +628,12 @@ static void CDECL HandleCrash(int signum) log.MakeDesyncCrashLog(log_in, log_out, info); } +/* static */ void CrashLog::InconsistencyLog(const InconsistencyExtraInfo &info) +{ + CrashLogUnix log(CrashLogUnix::DesyncTag{}); + log.MakeInconsistencyLog(info); +} + /* static */ void CrashLog::VersionInfoLog() { CrashLogUnix log(CrashLogUnix::DesyncTag{}); diff --git a/src/os/windows/crashlog_win.cpp b/src/os/windows/crashlog_win.cpp index 50e6c9596d..2f728573b2 100644 --- a/src/os/windows/crashlog_win.cpp +++ b/src/os/windows/crashlog_win.cpp @@ -758,6 +758,12 @@ static void CDECL CustomAbort(int signal) log.MakeDesyncCrashLog(log_in, log_out, info); } +/* static */ void CrashLog::InconsistencyLog(const InconsistencyExtraInfo &info) +{ + CrashLogWindows log(nullptr); + log.MakeInconsistencyLog(info); +} + /* static */ void CrashLog::VersionInfoLog() { CrashLogWindows log(nullptr);