diff --git a/src/crashlog.cpp b/src/crashlog.cpp index 3681aa23c6..e6c2d7032e 100644 --- a/src/crashlog.cpp +++ b/src/crashlog.cpp @@ -78,6 +78,7 @@ /* static */ const char *CrashLog::message = NULL; /* static */ char *CrashLog::gamelog_buffer = NULL; /* static */ const char *CrashLog::gamelog_last = NULL; +/* static */ const CrashLog *CrashLog::main_thread_pending_crashlog = NULL; char *CrashLog::LogCompiler(char *buffer, const char *last) const { @@ -398,6 +399,12 @@ char *CrashLog::FillCrashLog(char *buffer, const char *last) const } #endif + if (IsNonMainThread()) { + buffer += seprintf(buffer, last, "Non-main thread ("); + buffer += GetThreadName(buffer, last); + buffer += seprintf(buffer, last, ")\n\n"); + } + buffer = this->LogOpenTTDVersion(buffer, last); buffer = this->LogStacktrace(buffer, last); buffer = this->LogRegisters(buffer, last); @@ -528,8 +535,33 @@ bool CrashLog::MakeCrashLog() const printf("Crash dump written to %s. Please add this file to any bug reports.\n\n", filename); } + if (IsNonMainThread()) { + printf("Asking main thread to write crash savegame and screenshot...\n\n"); + CrashLog::main_thread_pending_crashlog = this; + _exit_game = true; + CSleep(60000); + if (!CrashLog::main_thread_pending_crashlog) return ret; + printf("Main thread did not write crash savegame and screenshot within 60s, trying it from this thread...\n\n"); + } + CrashLog::main_thread_pending_crashlog = nullptr; + bret = CrashLog::MakeCrashSavegameAndScreenshot(); + if (!bret) ret = false; + + return ret; +} + +/** + * Makes a crash dump and crash savegame. It uses DEBUG to write + * information like paths to the console. + * @return true when everything is made successfully. + */ +bool CrashLog::MakeCrashSavegameAndScreenshot() const +{ + char filename[MAX_PATH]; + bool ret = true; + printf("Writing crash savegame...\n"); - bret = this->WriteSavegame(filename, lastof(filename)); + bool bret = this->WriteSavegame(filename, lastof(filename)); if (bret) { printf("Crash savegame written to %s. Please add this file and the last (auto)save to any bug reports.\n\n", filename); } else { @@ -549,6 +581,18 @@ bool CrashLog::MakeCrashLog() const return ret; } +/* static */ void CrashLog::MainThreadExitCheckPendingCrashlog() +{ + const CrashLog *cl = CrashLog::main_thread_pending_crashlog; + if (cl) { + CrashLog::main_thread_pending_crashlog = nullptr; + cl->MakeCrashSavegameAndScreenshot(); + + CrashLog::AfterCrashLogCleanup(); + abort(); + } +} + /** * Sets a message for the error message handler. * @param message The error message of the error. diff --git a/src/crashlog.h b/src/crashlog.h index 8948a8acb4..72077d2bbf 100644 --- a/src/crashlog.h +++ b/src/crashlog.h @@ -127,6 +127,7 @@ public: bool WriteScreenshot(char *filename, const char *filename_last) const; bool MakeCrashLog() const; + bool MakeCrashSavegameAndScreenshot() const; /** * Initialiser for crash logs; do the appropriate things so crashes are @@ -141,6 +142,10 @@ public: inline const char *GetMessage() const { return this->message; } static const char *GetAbortCrashlogReason(); + + static const CrashLog *main_thread_pending_crashlog; + + static void MainThreadExitCheckPendingCrashlog(); }; #endif /* CRASHLOG_H */ diff --git a/src/openttd.cpp b/src/openttd.cpp index 656e70f4cd..80d740932a 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -922,6 +922,8 @@ int openttd_main(int argc, char *argv[]) VideoDriver::GetInstance()->MainLoop(); + CrashLog::MainThreadExitCheckPendingCrashlog(); + WaitTillSaved(); /* only save config if we have to */ diff --git a/src/scope.h b/src/scope.h index d341b50b1f..b546813ac3 100644 --- a/src/scope.h +++ b/src/scope.h @@ -12,6 +12,8 @@ #ifndef SCOPE_H #define SCOPE_H +#include + template class scope_exit_obj { T f; diff --git a/src/thread/thread.h b/src/thread/thread.h index dddd26f63f..ecfd655fb6 100644 --- a/src/thread/thread.h +++ b/src/thread/thread.h @@ -133,4 +133,17 @@ void SetSelfAsMainThread(); */ bool IsMainThread(); +/** + * @return true if the current thread definitely a "non-main" thread. If in doubt returns false. + */ +bool IsNonMainThread(); + +/** + * Get the name of the current thread, if any. + * @param str The start of the buffer. + * @param last The last char of the buffer. + * @return Number of chars written to str. + */ +int GetThreadName(char *str, const char *last); + #endif /* THREAD_H */ diff --git a/src/thread/thread_morphos.cpp b/src/thread/thread_morphos.cpp index 9b2485bffe..259b0a56b1 100644 --- a/src/thread/thread_morphos.cpp +++ b/src/thread/thread_morphos.cpp @@ -199,3 +199,6 @@ private: void SetSelfAsMainThread() { } bool IsMainThread() { return false; } +bool IsNonMainThread() { return false; } + +int GetThreadName(char *str, const char *last) { return 0; } diff --git a/src/thread/thread_none.cpp b/src/thread/thread_none.cpp index b68b372f77..175cf32015 100644 --- a/src/thread/thread_none.cpp +++ b/src/thread/thread_none.cpp @@ -37,3 +37,6 @@ public: void SetSelfAsMainThread() { } bool IsMainThread() { return true; } +bool IsNonMainThread() { return false; } + +int GetThreadName(char *str, const char *last) { return 0; } diff --git a/src/thread/thread_os2.cpp b/src/thread/thread_os2.cpp index 6cb182bae7..41ae19ab38 100644 --- a/src/thread/thread_os2.cpp +++ b/src/thread/thread_os2.cpp @@ -149,3 +149,7 @@ public: void SetSelfAsMainThread() { } bool IsMainThread() { return false; } + +bool IsNonMainThread() { return false; } + +int GetThreadName(char *str, const char *last) { return 0; } diff --git a/src/thread/thread_pthread.cpp b/src/thread/thread_pthread.cpp index 103465c0a7..a6aeb521f4 100644 --- a/src/thread/thread_pthread.cpp +++ b/src/thread/thread_pthread.cpp @@ -11,6 +11,7 @@ #include "../stdafx.h" #include "thread.h" +#include "../string_func.h" #include #include @@ -201,3 +202,22 @@ bool IsMainThread() { return main_thread == pthread_self(); } + +bool IsNonMainThread() +{ + return main_thread != pthread_self(); +} + +int GetThreadName(char *str, const char *last) +{ +#if defined(__GLIBC__) +#if __GLIBC_PREREQ(2, 12) + char buffer[16]; + int result = pthread_getname_np(pthread_self(), buffer, sizeof(buffer)); + if (result == 0) { + return seprintf(str, last, "%s", buffer); + } +#endif +#endif + return 0; +} diff --git a/src/thread/thread_win32.cpp b/src/thread/thread_win32.cpp index 446c1bafc7..c7831040b0 100644 --- a/src/thread/thread_win32.cpp +++ b/src/thread/thread_win32.cpp @@ -13,13 +13,19 @@ #include "thread.h" #include "../debug.h" #include "../core/alloc_func.hpp" +#include "../scope.h" +#include "../string_func.h" #include #include #include #include "../os/windows/win32.h" +#include +#include #include "../safeguards.h" +static void Win32SetThreadName(uint id, const char *name); + /** * Win32 thread version for ThreadObject. */ @@ -46,6 +52,7 @@ public: { this->thread = (HANDLE)_beginthreadex(NULL, 0, &stThreadProc, this, CREATE_SUSPENDED, &this->id); if (this->thread == NULL) return; + Win32SetThreadName(this->id, name); ResumeThread(this->thread); } @@ -177,3 +184,31 @@ bool IsMainThread() { return main_thread_id == GetCurrentThreadId(); } + +bool IsNonMainThread() +{ + return main_thread_id != GetCurrentThreadId(); +} + +static std::map _thread_name_map; +static ThreadMutex_Win32 _thread_name_map_mutex; + +static void Win32SetThreadName(uint id, const char *name) +{ + _thread_name_map_mutex.BeginCritical(); + _thread_name_map[id] = name; + _thread_name_map_mutex.EndCritical(); +} + +int GetThreadName(char *str, const char *last) +{ + _thread_name_map_mutex.BeginCritical(); + auto guard = scope_guard([&]() { + _thread_name_map_mutex.EndCritical(); + }); + auto iter = _thread_name_map.find(GetCurrentThreadId()); + if (iter != _thread_name_map.end()) { + return seprintf(str, last, "%s", iter->second.c_str()); + } + return 0; +}